{
  "shell.native_background": {
    "score": 1,
    "evidence": "src/agents/bash-tools.exec-run.ts:117-124 default 10s yield; :676-692 promotes to a registered process session; :715-731 returns session id + pid and the process follow-up"
  },
  "shell.timeout_cap": {
    "score": 0.5,
    "evidence": "src/agents/bash-tools.exec-run.ts:125-126 default 1800s; src/agents/bash-tools.schemas.ts:43-47 model-settable timeoutSeconds; no explicit maximum, only the Node timer clamp to 2^31-1 ms at packages/gateway-client/src/timeouts.ts:28,55-63"
  },
  "shell.persistent": {
    "score": 0.5,
    "evidence": "src/agents/shell-snapshot.ts:84-104,427-440 caches and restores login-shell env/aliases per call, but each exec spawns a fresh shell (src/agents/bash-tools.exec-runtime.ts:921-940), so cwd is not persistent"
  },
  "shell.powershell": {
    "score": 1,
    "evidence": "src/agents/shell-utils.ts:181-196 selects PowerShell on win32; pwsh/powershell resolution at src/agents/shell-utils.ts:27-61"
  },
  "shell.middle_out": {
    "score": 0,
    "evidence": "searched middleOut/middle-out; default exec keeps only the tail: src/agents/bash-process-registry.ts:468-470 trimWithCap is tail(text,max), called at :231"
  },
  "shell.full_log": {
    "score": 0,
    "evidence": "searched fullOutputPath/spill in exec; only the non-default session bash writes one (src/agents/sessions/tools/output-accumulator.ts:165-173). Exec is in-memory capped at src/agents/bash-tools.exec-runtime.ts:107-113 and explicitly says loss is unrecoverable at src/agents/bash-tools.exec-output.ts:12-14"
  },
  "shell.omitted_counts": {
    "score": 0.5,
    "evidence": "foreground exec only says output was discarded and unrecoverable (src/agents/bash-tools.exec-output.ts:12-14); background process log does return total + paging ('showing last 200 of N lines; pass offset/limit') at src/agents/bash-tools.process.ts:99-104,527-560"
  },
  "shell.signal_honesty": {
    "score": 0.5,
    "evidence": "signal is named rather than faked: src/agents/bash-tools.exec-runtime.ts:542-543 ('Command aborted by signal ...') and src/agents/bash-tools.exec-output.ts:21-30 ('signal SIGTERM'); no 128+N/137/143 mapping found"
  },
  "shell.benign_exit": {
    "score": 0,
    "evidence": "searched benign/grep/diff/test exit handling; exec only appends the numeric code at src/agents/bash-tools.exec-runtime.ts:563-575, with no benign-exit annotation"
  },
  "shell.untrusted_fencing": {
    "score": 0,
    "evidence": "searched untrusted/fence for exec results; only the approval-review payload is fenced (src/agents/bash-tools.exec-approval-output.ts:88-127). Main exec output is returned as plain text at src/agents/bash-tools.exec-support.ts:27-69"
  },
  "shell.bg_output_reader": {
    "score": 1,
    "evidence": "src/agents/bash-tools.process.ts:445-517 poll returns unread output; :519-562 log returns a window of retained output"
  },
  "shell.wait_modes": {
    "score": 1,
    "evidence": "src/agents/bash-tools.process.ts:457-473 poll timeout blocks until new output or exit; :142-153 timeout=0 returns immediately"
  },
  "shell.cursor_reads": {
    "score": 1,
    "evidence": "src/agents/bash-tools.process.ts:484-516 drains and acknowledges only new output; src/agents/bash-process-registry.ts:238-320 stages each poll delivery and acknowledges it so bytes are not repeated"
  },
  "shell.list_tasks": {
    "score": 1,
    "evidence": "src/agents/bash-tools.process.ts:347-391 process action=list lists running and finished sessions"
  },
  "shell.kill_id": {
    "score": 1,
    "evidence": "src/agents/bash-tools.process.ts:624-654 process action=kill cancels the background session by id"
  },
  "shell.kill_pid_port": {
    "score": 0,
    "evidence": "searched pid/port controls; the process schema exposes sessionId only (src/agents/bash-tools.schemas.ts:95-100) and kill uses that id (src/agents/bash-tools.process.ts:624-654). No model-facing pid or listening-port kill"
  },
  "shell.sigterm_escalation": {
    "score": 1,
    "evidence": "src/process/supervisor/supervisor.ts:412-418 sends SIGTERM then SIGKILL after GRACEFUL_CANCEL_TIMEOUT_MS; 5s window at src/process/supervisor/cancellation-policy.ts:2"
  },
  "shell.group_kill": {
    "score": 1,
    "evidence": "packages/agent-core/src/harness/env/kill-tree.ts:22-29,46-47,81-83 only group-kills when detached or the pid is its own group leader; src/process/supervisor/adapters/child.ts:395-402 passes detached=false after a no-detach fallback"
  },
  "shell.monitor": {
    "score": 0.5,
    "evidence": "a background completion enqueues a system event and wakes via requestHeartbeat (src/agents/bash-tools.exec-runtime.ts:356-427; advertised at src/agents/bash-tools.descriptions.ts:33). No output-pattern or scheduled command-monitor tool found."
  },
  "shell.sleep": {
    "score": 0,
    "evidence": "searched for a sleep tool; none. The tool prompt says avoid sleep loops and use automations (src/agents/bash-tools.descriptions.ts:37); sessions_yield is limited to pending child completion (src/agents/tools/sessions-yield-tool.ts:10-24)"
  },
  "shell.cron": {
    "score": 1,
    "evidence": "durable automations tool at src/agents/tools/cron-tool.ts:214-227; jobs are stored in SQLite (src/cron/service/ops-read.ts:42-49)"
  },
  "shell.bg_subagents": {
    "score": 0.5,
    "evidence": "sessions_spawn spawns a background subagent at src/agents/tools/sessions-spawn-tool.ts:349,586, but the explicit agents_wait collector is gated by tools.swarm.enabled=false (src/agents/openclaw-tools.swarm.ts:39-48; src/agents/subagents/swarm/swarm-config.ts:15-16). Default collection is push completion + sessions_yield"
  },
  "shell.safe_classification": {
    "score": 1,
    "evidence": "authorization builds command segments from a web-tree-sitter bash AST (src/infra/exec-authorization-plan.ts:768-799; src/infra/exec-approvals-allowlist.ts:98-110), not only a token allowlist; safeBins remains a secondary auto-approval path"
  },
  "shell.workspace_boundary": {
    "score": 0,
    "evidence": "exec accepts any existing local directory as workdir (src/agents/bash-tools.exec-workdir.ts:430-436); tools.fs.workspaceOnly defaults false (src/config/schema.help.runtime.ts:541-542) and does not gate shell paths"
  },
  "read.line_window": {
    "score": 1,
    "evidence": "packages/agent-core/src/harness/utils/truncate.ts:2 DEFAULT_MAX_LINES=2000; applied by src/agents/sessions/tools/read-page.ts:56,100"
  },
  "read.second_ceiling": {
    "score": 1,
    "evidence": "packages/agent-core/src/harness/utils/truncate.ts:3 DEFAULT_MAX_BYTES=50KB; page ceiling DEFAULT_READ_PAGE_MAX_BYTES=32KB at src/agents/agent-tools.read.ts:77 and clamped at :134-146"
  },
  "read.per_line_clamp": {
    "score": 1,
    "evidence": "src/agents/sessions/tools/read-page.ts:107-113 truncates an over-budget first line to the byte budget and returns a cursor continuation"
  },
  "read.line_prefixes": {
    "score": 0,
    "evidence": "searched line-prefix/cat -n rendering; model output is raw selectedLines.join('\\n') at src/agents/sessions/tools/read.ts:573, with no line numbers"
  },
  "read.resume_offset": {
    "score": 1,
    "evidence": "src/agents/sessions/tools/read-page.ts:13-30 emits '[Showing ... Use offset=<next> to continue]'"
  },
  "read.empty_file_note": {
    "score": 1,
    "evidence": "src/agents/sessions/tools/read.ts:527-531 returns 'File is empty (0 bytes).' or 'File contains no readable text'"
  },
  "read.eof_note": {
    "score": 1,
    "evidence": "src/agents/sessions/tools/read.ts:532-533 returns a recovery note ('Offset N is beyond end of file ... Retry with offset <= N') instead of throwing"
  },
  "read.streaming": {
    "score": 0,
    "evidence": "searched streaming reader; ops.readFile returns the whole Buffer (src/agents/sessions/tools/read.ts:116-131,449) and the full text is split at :513-518, so memory is not capped by streaming"
  },
  "read.deferred_chunk": {
    "score": 1,
    "evidence": "src/agents/sessions/tools/read-page.ts:100-129 derives the truncation and next offset from the known totals before emitting the 'more remains' notice"
  },
  "read.dedup": {
    "score": 0,
    "evidence": "searched unchanged-read/same-window/read-cache; every call re-reads the file at src/agents/sessions/tools/read.ts:439-456 and no per-session consumed record exists"
  },
  "read.ledger": {
    "score": 0,
    "evidence": "searched read-ledger/seen-state; write reads the file from disk only for diff/idempotency (src/agents/sessions/tools/write.ts:326-371), not to consult what the model previously read"
  },
  "read.partial_ledger": {
    "score": 0,
    "evidence": "searched partial-view ledger; no record of prior partial reads in src/agents/sessions/tools/write.ts or edit.ts, so destructive overwrites are not refused on that basis"
  },
  "read.did_you_mean": {
    "score": 1,
    "evidence": "src/agents/sessions/tools/read.ts:75-95 builds Levenshtein suggestions; :295-299 appends 'Did you mean: ...?'"
  },
  "read.unicode_retry": {
    "score": 1,
    "evidence": "src/agents/sessions/tools/path-utils.ts:58-84 generates NFC/NFD, curly-quote, and Unicode-space filename variants; retried in src/agents/sessions/tools/read.ts:269-293"
  },
  "read.confusables": {
    "score": 0,
    "evidence": "searched zero-width/bidi disclosure in the read path; the only related code strips these characters for prompt safety (src/agents/sanitize-for-prompt.ts:8-19) without disclosing them in file content"
  },
  "read.image_vision": {
    "score": 1,
    "evidence": "src/agents/sessions/tools/read.ts:479-511 detects image bytes and attaches image content blocks"
  },
  "read.coord_scale": {
    "score": 1,
    "evidence": "src/agents/utils/image-resize.ts:178-185 emits the original/displayed dimensions and coordinate scale factor; added to the read result at src/agents/sessions/tools/read.ts:500-503"
  },
  "read.magic_bytes": {
    "score": 1,
    "evidence": "src/agents/sessions/tools/read.ts:479 calls MIME detection on the buffer; signatures are checked from bytes in src/agents/utils/mime.ts:10-30, ignoring the file extension"
  },
  "read.notebooks": {
    "score": 0,
    "evidence": "searched ipynb/notebook handling across src, packages, and extensions; no notebook parser or cell renderer found"
  },
  "read.docx": {
    "score": 0,
    "evidence": "searched docx; no docx extractor. The only document-extractor contract is PDF (extensions/document-extract/openclaw.plugin.json:9-11)"
  },
  "read.pptx": {
    "score": 0,
    "evidence": "searched pptx/speaker notes; no pptx parser or extractor found"
  },
  "read.xlsx": {
    "score": 0,
    "evidence": "searched xlsx/spreadsheet; no xlsx parser or table renderer found"
  },
  "read.odf": {
    "score": 0,
    "evidence": "searched odt/ods/odp/OpenDocument; no ODF extractor found"
  },
  "read.rtf_epub": {
    "score": 0,
    "evidence": "searched rtf/epub; no RTF or EPUB extractor found"
  },
  "read.pdf_text": {
    "score": 0.5,
    "evidence": "PDF text extraction ships by default for attachments (extensions/document-extract/openclaw.plugin.json:3-11; text mode at extensions/document-extract/document-extractor.ts:78-87; media path at src/media/input-files.ts:448-468) and a separate pdf tool exists (src/agents/tools/pdf-tool.ts:433-436), but the read tool itself does not route PDFs"
  },
  "read.scanned_pdf": {
    "score": 0.5,
    "evidence": "when extracted text is thin the default PDF extractor renders page images (extensions/document-extract/document-extractor.ts:85-119), but no page-name note or rendering recovery command is emitted"
  },
  "read.docs_windowed": {
    "score": 0,
    "evidence": "searched offset/limit for documents; the pdf tool only supports page ranges (src/agents/tools/pdf-tool.ts:81-86), not the read line window"
  },
  "read.docs_bulk": {
    "score": 0.5,
    "evidence": "the pdf tool accepts up to 10 PDFs in one call (src/agents/tools/pdf-tool.ts:76-79), but general read is single-path (src/agents/sessions/tools/read-tool-contract.ts:5-17)"
  },
  "read.converter_on_demand": {
    "score": 0.5,
    "evidence": "document-extract is not activated at startup (extensions/document-extract/openclaw.plugin.json:3-6) and clawpdf is dynamically imported on first use (extensions/document-extract/document-extractor.ts:18-27), but clawpdf is an installed dependency rather than fetched on demand"
  },
  "read.many_files": {
    "score": 0,
    "evidence": "read accepts a single path only (src/agents/sessions/tools/read-tool-contract.ts:5-17); no paths array or batch loop"
  },
  "read.globs": {
    "score": 0,
    "evidence": "searched glob expansion in the read tool; path is a plain string (src/agents/sessions/tools/read-tool-contract.ts:5-17), no glob expansion"
  },
  "read.per_file_range": {
    "score": 0,
    "evidence": "no multi-file read; offset/limit apply to the single file at src/agents/sessions/tools/read.ts:523-558"
  },
  "read.exclusions_gitignore": {
    "score": 0,
    "evidence": "searched gitignore/exclude handling in read; no ignore-file or exclusion processing in src/agents/sessions/tools/read.ts or src/agents/agent-tools.read.ts"
  },
  "read.aggregate_cap": {
    "score": 0,
    "evidence": "no multi-file read to share one budget; the cap is per call/page at src/agents/agent-tools.read.ts:77,134-146"
  },
  "read.aliased_input": {
    "score": 0,
    "evidence": "searched file_path/filePath aliasing; the read schema accepts only 'path' (src/agents/sessions/tools/read-tool-contract.ts:5-17) and normalization only touches the ['path'] key (src/agents/agent-tools.read.ts:1061)"
  },
  "read.negative_offset": {
    "score": 0,
    "evidence": "schema sets minimum 1 (src/agents/sessions/tools/read-tool-contract.ts:7) and execute rejects offset < 1 at src/agents/sessions/tools/read.ts:400-401; no tail mode"
  },
  "read.device_blocklist": {
    "score": 1,
    "evidence": "src/agents/sessions/tools/read.ts:60-73 stats and rejects any non-regular file (directories, character/block devices) before I/O; used as the default access op at :126-131"
  }
}
