Remove the profile limits, runtime counters, quota errors, and extra-round resume flow. Keep tool authorization stateless and give protocol drift recovery its own 20-attempt limit, reset on resume.
Migrate profiles to schema 5 and discard retired fields at the existing profile, draft, and checkpoint decoding boundaries. Route shared Session profile migration and persistence through AgentProfileService.
Validation: pnpm run check; focused profile and runtime tests; macOS profile migration, JSON save, export dialog, and old checkpoint reads.
Store incoming images and rejection notes through one entry point, then project MCP results without duplicating structured text or describing image-only results as empty. Centralize stored image formats and file paths, reuse workspace text reads, and remove superseded identity checks.
Preserve Claude tool images and original text with or without current tool definitions. Route OpenAI-compatible tool media once and reject unsupported Gemini tool audio/video. Document capability, normalization and retention at their owning topics.
Validation: payload, gateway, runtime, MCP, image and Shell focused tests; Rust crate boundaries; pnpm run check.
Normalize extension tool images once into workspace files, keep only references in history, and hydrate a request copy before model retries. Carry the admitted image capability through invocation binding and Session compaction, count retained images, and encode tool images in each provider format. Consolidate tool result construction and redact inline media by structure.
Validation: extension runtime contracts 4/4; agent tools 18/18; tool results 5/5; provider image encoding 4/4; HTTP classification and log redaction focused tests; extension JS contracts 3/3; TypeScript, frontend guards, lint and crate boundaries passed.
Tool errors and run failures named task.return, workspace.finish and
workspace.commit by ID. An error about another tool now names it by
its alias; one about the tool just called says "this tool", so it has
no name to keep in step. ToolSystem.md states the rule.
Register WebView tools through a dedicated provider with shared tool selection, naming, and result handling. Keep extension availability independent from saved profile selections.
Clarify model-facing feedback and consolidate behavioral tests and architecture documentation.
Document skill mounts, prompt catalogs and migration boundaries. Retain the JavaScript capability and kit references while removing obsolete executor details.
Unify Bashkit and QuickJS behind WorkspaceFs, retain the existing skill entry point during migration, and clarify Agent tool descriptions and errors.
Validation: pnpm run check.
Run shell commands and jq against the shared, scoped workspace. Integrate per-round automatic commits and cancellation that finishes active file writes before returning.
Accept null and empty arguments as an empty object, and validate argument shape once before dispatch. Malformed calls consume their budget and return recoverable tool errors.
Normalize provider replay while preserving rejected input in audit storage. Include runtime and protocol regression coverage and the tool argument contract.
Validation: pnpm run check.
Start a new foreground invocation with fresh budgets while retaining the run context, tool snapshot, commit history, and workspace. Preserve the current output as a revision source and reuse unchanged persistent versions.
Background
----------
Issue surfaced in production: model uses tools correctly through the
full pipeline (write_file → commit), then on the round expected to call
workspace_finish, replies with the full content as plain assistant text
instead. The committed message survives but the run fails with
`model.tool_call_required`, leaving an orphaned chat message and the UI
locked. PR #62 (issue #55) added rollback signalling so the host can
unwind cleanly, but the model's work is still discarded.
Two complementary fixes:
1. Strengthen `workspace.commit`'s tool result string with an explicit
"REQUIRED NEXT STEP: call workspace_finish" reminder + a
`nextRequiredTool` field in structured payload. This is the highest
attention real-estate in the prompt (last tool result the model sees
before the next turn) and costs essentially nothing.
2. Soft drift recovery: when the model returns 0 tool calls, give it
ONE corrective nudge instead of failing immediately. We push the
model's drifted text into history (so it owns what it said) and
append a synthetic `user` reminder that distinguishes the post-commit
case (commit will be rolled back) from the no-commit case (every
turn must use a tool). If the model recovers (any tool call) the
run continues normally; if it drifts again, we fall through to the
existing #55 path (rollback + `userRetryable=true`).
Hard cap of 1 attempt per run via `DRIFT_RECOVERY_MAX_ATTEMPTS`
prevents token-bombing on stubborn drifters. Multi-turn pattern
verified against Anthropic + OpenAI docs as API-compatible (no
role-alternation constraint after no-tool-use turn).
Backward compatibility
----------------------
- Failed recovery falls through to existing #55 code path: same
`model.tool_call_required` error, same `run_rollback_targets` event,
same `userRetryable=true`. No frontend changes required for the
failure case.
- New `drift_recovery_attempted` event is additive; old hosts ignore.
- Existing #55 drift test extended to include the recovery attempt
before falling through to rollback.
Tests
-----
- `foreground_run_emits_rollback_targets_on_tool_call_required_drift`:
updated to assert exactly one `drift_recovery_attempted` precedes the
rollback when the model drifts twice in a row.
- `foreground_run_recovers_from_post_commit_drift_with_nudge`: new —
model drifts after commit, gets nudged, complies with finish; run
completes, no rollback, commit preserved. Also asserts the nudge text
reaches the model verbatim.
- `foreground_run_recovers_from_no_commit_drift_with_nudge`: new —
model drifts on turn 1, gets nudged, completes the full pipeline.
601 lib tests passing.
Docs
----
- `RunEventJournal.md`: documents `drift_recovery_attempted` event +
recovery flow.
- `ToolSystem.md`: spells out the soft-recovery contract and which
drift codes do/don't get the second chance.
Co-authored-by: Cursor <cursoragent@cursor.com>
When the model deviated from the tool contract after committing chat
artifacts (e.g. answered with plain text instead of calling
`workspace_finish`), the run failed with `model.tool_call_required` /
`agent.tool_after_finish` / `agent.max_tool_rounds_exceeded`, but the
already-committed messages were left orphaned in chat and the UI had no
signal to offer a Retry path.
Loop runner now tracks each `AgentToolEffect::ChatCommitted` during a
run. When the run later enters a drift state, it emits a
`run_rollback_targets` warn event BEFORE `run_failed`, listing the
`{ path, mode, messageId, round }` of every commit the host UI needs to
roll back.
`run_failed` payload gains a `userRetryable: bool` flag:
- `retryable=true` (transient / rate-limited) implies `userRetryable=true`.
- Otherwise drift codes (`model.tool_call_required`,
`agent.tool_after_finish`, `agent.max_tool_rounds_exceeded`) are
`userRetryable=true` while remaining auto-non-retryable, so the UI can
surface a manual Retry button after performing the rollback. Policy /
validation errors stay `userRetryable=false`.
`workspace_commit` description tightened to spell out the
"commit -> finish in the same or next turn, never plain text" contract.
Docs:
- `docs/Agent/RunEventJournal.md`: new `run_rollback_targets` event,
expanded `run_failed` payload schema with `userRetryable` semantics.
- `docs/Agent/ToolSystem.md`: documents the
`workspace.path_is_directory` business error and the commit/finish
drift contract so the tool-system docs stay in sync.
Tests: integration test
`foreground_run_emits_rollback_targets_on_tool_call_required_drift`
drives a mock gateway to commit then drift, asserting that the
rollback event lists the committed message and that the run fails with
`userRetryable=true`. Extended `error_payload` unit tests cover all
three drift codes.
Co-authored-by: Cursor <cursoragent@cursor.com>
Change worldinfo.read_activated to list activated entry refs by default
and require explicit ref-based reads for entry content.
- replace bulk World Info content output with index/content modes
- add per-entry character range reads and recoverable ref/range errors
- update Agent prompt guidance, tool schema, docs, and runtime tests
- Add workspace.search_files and skill.search so agents can discover relevant
content in visible workspace files and installed Skills before reading.
- Extend workspace.read_file and skill.read with line and character range reads
for large files, and wire the new capabilities through tool specs, dispatcher,
profile defaults, prompt hints, host Skill APIs, docs, and tests.
Introduce workspace.commit as the explicit chat publication step while
keeping workspace.finish scoped to run completion.
- add foreground/background run presentation semantics
- route chat commits through a host bridge backed by saveReply()
- support default replace commits and append commits on one message floor
- require foreground runs to commit before finish
- remove the old prepare/finalize commit API path
- update Agent docs, types, and runtime tests
- add Agent Profile domain models, file repository, resolver, and management commands
- resolve profiles before run creation and persist resolved_profile.json in run inputs
- drive tools, skills, workspace roots, output artifacts, rounds, and budgets from profiles
- support agentSystemPrompt replacement and model-facing tool description overrides
- enforce profile tool policy and skill read budgets at runtime
- Add file-backed Skill storage with import preview, install, export, source refs, and safe file reads
- Expose the Skill Host API and embedded Preset/Character Skill import prompts
- Register skill.list and skill.read in the Agent tool loop
- Cover archive roundtrips, source cleanup, symlink safety, and API contracts
- Refresh Agent docs around the current Skill capability boundary