Published per-cwd discovery snapshots to existing task tools and refreshed them from TUI, ACP, and Agent Control Center reload paths.
Added regressions for existing and future task tools across TUI and ACP reloads.
Fixes#7940
session/load and session/resume resolved a session only within the
directory re-derived from cwd (SessionManager.list(cwd)), so sessions
stored under the legacy/hashed project-directory scheme (17.2.5+,
reverted in #7656) were unreachable and threw "ACP session not found"
despite existing on disk.
#findStoredSession now falls back to a global by-id scan (listAll,
already used by the fork path) when the cwd-scoped lookup misses. The
session id is globally unique and #openStoredSession reopens the file
with the request cwd, so no directory-scheme knowledge is needed.
Fixes#7779
- Implemented in-house, zero-dependency utility modules in `pi-utils` covering DOM manipulation, markdown parsing, templating, browser automation helpers, and terminal buffers.
- Migrated packages across the repository to consume the new internal utilities and `omptype` schema validators instead of external dependencies.
- Removed multiple external runtime and development dependencies including Zod, Marked, LRU cache, Turndown, and Puppeteer browser packages.
editor's prefill is the actual document being edited, not a placeholder
like input's — the interactive (hook-editor.ts) and RPC implementations
set it verbatim on any truthy value. Trimming before the presence check
silently dropped whitespace/blank-line prefill content from the ACP
elicitation schema, so a client-side form opened empty and accepting it
could not reproduce the extension's supplied indentation.
Now only a genuinely empty string omits `default`; any other prefill,
including whitespace-only, passes through unchanged.
createAcpExtensionUiContext stubbed editor as a hardcoded no-op
(async () => undefined), so free-text elicitation requests from
/review's custom-instructions branch and the ask tool's custom-input
path never reached the client and silently resolved to undefined.
Wire editor through the existing elicitFromAcpClient bridge used by
select/confirm/input, using a {type: "string"} schema with default
set to the prefill text when non-blank.
Verified against a real stdio JSON-RPC transport via acp-probe: the
elicitation/create request now fires with the expected schema and
round-trips the accepted value back to the prompt without hanging.
Adds matching unit test coverage in acp-agent.test.ts mirroring the
existing select/confirm/input bridge tests (prefill->default mapping,
whitespace-prefill omits default, decline/cancel->undefined, no-form
fallback).
Zed (and any other ACP client) never learned about a model switch that
happened from inside the agent loop — prewalk hand-offs, retry-fallback,
model cycling — because #pushConfigOptionUpdate was only ever wired to
the client-initiated setSessionConfigOption/setSessionMode RPCs and to
the thinking_level_changed lifetime event. The model itself did switch
correctly (subsequent requests used the new model), but the client's
model picker/status bar kept showing the session's starting model.
#handleLifetimeEvent now also reacts to the model_changed event added
in the previous commit and re-pushes config_option_update. Extend the
existing thinking-only subscription dedupe in setSessionConfigOption to
cover the model config id too, so a client-initiated model change still
produces exactly one notification once the lifetime subscription is
installed.
Regression tests mirror the existing thinking-level coverage:
- 'pushes config_option_update when the model changes internally'
- 'emits a single config_option_update per setSessionConfigOption(model) call'
Verified: bun test test/acp-agent.test.ts (57/57), plus
agent-session-prewalk.test.ts, agent-session-retry-fallback.test.ts,
retry-fallback.test.ts, model-resolver.test.ts, and the other acp-*.test.ts
files all still pass; tsgo --noEmit and biome check clean.
(cherry picked from commit f5c5081088e8cbac2650a9de89049731de1b2777)
- Added the `xd://` virtual device protocol (`internal-urls/xd-protocol.ts`, `tools/xdev.ts`): tools declaring `loadMode: "discoverable"` are unmounted from the request tools array and driven via `read xd://` (list/docs+schema) and `write xd://<tool>` (execute), gated by the `tools.xdev` setting (default on) and inlined into the system prompt.
- Merged the `irc`, `job`, and `launch` tools into a single `hub` tool (`tools/hub/`, `async/job-manager.ts`): messaging keeps `send`/`inbox`/`list`, job control maps to `wait`/`cancel`/`jobs`, process supervision keeps `start`/`logs`/`stop`/`restart`/`describe` with `ps`, and the unified `wait` races background jobs against peer messages; SDK `IrcTool`/`JobTool`/`LaunchTool` are replaced by `HubTool`.
- Removed the hidden `resolve` tool in favor of the `xd://resolve`/`xd://reject`/`xd://propose` resolution devices, auto-including `write` whenever a deferrable tool or plan mode is present.
- Removed the BM25 tool-discovery system: the `search_tool_bm25` tool, the `tool-discovery` module, the `tools.discoveryMode`/`mcp.discoveryMode`/`mcp.discoveryDefaultServers`/`tools.essentialOverride` settings, per-tool MCP selection, and the `mcp_tool_selection` message type.
- Unified tool presentation on `ToolLoadMode` (`essential`|`discoverable`), replacing the custom-tool `xdev?: boolean` opt-out; custom, extension, MCP, RPC host, image-generation, and TTS tools now default to `discoverable`, and added a `satisfies` predicate to `SoftToolRequirement`.
- Removed the standalone `ssh` command tool and `ssh/ssh-executor` (the `ssh://` read/write/search protocol stays), and made `--tools` address hidden built-ins.
- Updated collab-web to render `xd://` dispatches and `hub` op families, dropped the `search_tool_bm25`/`ssh`/`report-finding` renderers, refreshed tool docs and prompts, and migrated the affected tests and changelogs.
- Added `isAcceptedElicitation` in the ACP agent to narrow accepted elicitations before accessing response content.
- Added `isFormElicitation` in ACP tests and used it to narrow form-mode requests before assertions.
- Added a fallback to emit error messages during `agent_end` if no error was previously streamed during the turn.
- Added tracking to prevent duplicate error messages when a provider error is successfully delivered during streaming.
- Added a stderr hint for interactive users launching the ACP server directly from a terminal.
The assistant message_end fan-out is fire-and-forget in the session layer
and can be parked on extension delivery while agent_end is flushed through
#endInFlight, so agent_end can overtake it. #finishPrompt then unsubscribes
the prompt turn and the mapAssistantMessageEnd fallback never runs: an ACP
client that only received agent_thought_chunk updates (thinking streamed,
text arrived only on the trailing message) stays stuck on the thinking
block with no visible answer. On agent_end, emit the last assistant
message's text before resolving the prompt when live-message progress shows
no text was ever delivered, and defer the live-state reset past that flush
so a late message_end cannot resurrect fresh progress and double-emit.
Fixes#4902
Extension sendUserMessage() without deliverAs fell through to prompt(),
which throws AgentBusyError during an active stream; the message was
dropped and surfaced as 'Extension sendUserMessage failed'. Route the
omitted-deliverAs path through prompt() with streamingBehavior 'steer'
so streaming queues a steer with normal prompt-flow side effects
(keyword notices, advisor auto-resume reset) and idle still starts a
turn.
ACP skill-command prompts now pass streamingBehavior 'steer'; the RPC
skill fast-path honors the prompt command's streamingBehavior field
(default steer) like the plain-prompt path already did. Documented the
extension-facing delivery semantics.
Synthesized from PR #4942 (prompt-flow steer routing, docs, tests) and
PR #4922 (RPC streamingBehavior threading, steer regression test);
dropped PR #4942's unrelated workflow-notice.md ellipsis churn.
Fixes#4923
Co-authored-by: roboomp <omp@can.ac>
Co-authored-by: metaphorics <metaphorics@users.noreply.github.com>
- Classified OpenAI-compatible custom relays serving OpenAI model ids into the OpenAI service-tier family.
- Passed the model into OpenAI service-tier wire gating so custom relays emit service_tier when eligible.
- Reported /fast on as unavailable when the active model has no service-tier family.
Fixes#4386
User-invoked skills (typed /skill:, steered, follow-up, interrupted/
resumed via compaction, ACP, RPC) only appended a bare "Skill: <path>"
line, so the model neither learned the user had invoked that specific
skill nor where the skill directory was. Relative paths in skill bodies
(scripts/, templates/) could not be resolved.
Route all user-invoked paths through a self-identifying, baseDir-aware
prompt template; keep hidden autoload skills on the minimal non-user
format. Interactive skillCommands now carries the loaded Skill object
instead of a bare path so baseDir flows through without reconstruction.
The invocation kind defaults to "user" to keep buildSkillPromptMessage
source-compatible.
Op: correct
Restores: spec:user-invoked-skill-prompt-self-identifies-and-exposes-skill-directory
Fix all Windows-specific test failures caused by path handling problems
and EBUSY errors from unclosed SQLite database handles.
Root causes fixed:
1. POSIX path assumptions: replaced hard-coded file:///tmp, /repo, etc.
with pathToFileURL/path.resolve/path.join computed expectations
2. shortenPath() now normalizes backslashes to forward slashes after ~
and respects home directory boundaries
3. HistoryStorage.resetInstance() leaked its Database — added #close()
that finalizes all prepared statements and closes the DB
4. AgentStorage gained the same resetInstance()/#close() pattern
5. SqliteAuthCredentialStore.close() leaked one-off prepared statements
from inline this.#db.prepare() calls — wrapped each in try/finally
6. model-cache.ts used a process-global DB even for custom dbPath —
now opens/closes per-call via withModelCacheDb
7. createAgentSession leaked AuthStorage on construction failure —
added ownsAuthStorage cleanup in catch block
8. MnemopiBackend.removeDbFiles() now truly best-effort (catches errors)
9. TempDir retry window expanded from 4x10ms to 40x25ms
10. TempDir prefix convention: non-@ prefixes created dirs relative to
cwd instead of os.tmpdir() — all test temp dirs now use @ prefix
11. Shell-escaped interpolated paths in bash tool tests
12. git core.autocrlf false in autoresearch test repo init
All 522 previously-failing Windows tests now pass.
Added the ACP mobile speech.models.list method so voice settings can fetch static local STT, TTS, and voice catalog options without invoking setup/download paths.
Fixes#3011
- Migrated all wire protocol, schema definitions, and tools validation from Zod to ArkType across multiple packages.
- Updated extension runtimes, custom tools loader, and TypeBox compatibility shim to expose and use ArkType instances.
- Added a comprehensive ArkType migration guide, validation parity tests, and helper utilities.
- Removed redundant PDF asset routing and parsing implementations from the read tool.
- Removed `userMessageId` from `AcpAgent` prompt state and response payloads.
- Removed `models` from new/load/resume/fork session responses and deleted model-state building.
- Removed `unstable_setSessionModel` and routed model changes through `setSessionConfigOption`.
- Replaced the ACP agent test's fixed bootstrap wait with a deterministic `/reload-plugins` flow and asserted against the latest available-commands advertisement.
- Added a TUI probe test that verifies native committed scrollback rows are passed to children before render.
parseSlashCommand treats ':' as a name/args separator, so an extension
command like 'model:foo' was advertised in available_commands_update but
dispatched to the '/model' builtin. Filter such names via
isAcpBuiltinShadowedName, and fix the FakeAgentSession prompt stubs in
acp-agent.test.ts to return true now that AgentSession.prompt() reports
whether the agent was invoked (6 tests were failing against the new
early-finish path).
Addresses review feedback on #2052.
Dispatch in AgentSession runs #tryExecuteExtensionCommand before
#tryExecuteCustomCommand, so the palette must reflect the same order.
Moving the extension-runner block before session.customCommands ensures
that on a name collision the advertised command matches what will
actually execute.
Update the test to assert the extension description wins over the
colliding custom TS description.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update ordering comment in #buildAvailableCommands to document the
extension tier and explain why skills/custom TS commands intentionally
shadow extension commands (unlike interactive mode)
- Add CHANGELOG entry under [Unreleased]
- Add regression test: verifies extension commands surface in
available_commands_update and that a builtin-colliding extension
command is excluded via the reserved-set, with no duplicates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The implicit-cancel overlap path dropped the cleanup promise, so an
abort() that hung past the cleanup timeout left the managed session
registered with a still-streaming AgentSession — the explicit cancel()
path closes it in that case. Mirror that handling and cover it with a
regression test.
Addresses review feedback on #2186.
When the user presses Stop in Zed and immediately types a new message,
the new session/prompt RPC can arrive before (or without) a preceding
session/cancel notification. The previous guard threw an error in that
case, leaving the session stuck and blocking further interaction.
Replace the throw with an implicit cancel: call #beginCancelCleanup on
the unsettled turn so it resolves with stopReason:"cancelled", then let
#queuePrompt serialize the new prompt behind the abort cleanup as it
already does when session/cancel is called explicitly. #beginCancelCleanup
is idempotent so a concurrent explicit cancel notification is a no-op.
Updated the test to assert the new contract: overlapping prompt
auto-cancels the first turn and the second is processed normally.
- Centralized catalog and registry handling on `ModelSpec` and `buildModel`, resolving compatibility at model build time.
- Removed runtime compatibility detectors and switched provider request flows to direct `model.compat` reads.
- Added compat fields (`supportsReasoningParams`, `alwaysSendMaxTokens`, `strictResponsesPairing`, `whenThinking`).
- Persisted explicit compatibility overrides through `compatConfig` in discovery and cache merge paths.
- Replaced approved-plan renaming with `resolveApprovedPlan` resolution and state/slug lookup.
- Updated ACP and interactive apply flows to propagate canonical `planFilePath` instead of renamed paths.
- Added local plan fallback lookup by mtime for unresolved slugs after plan approval.
- Restricted plan-mode writes to `local://` plan artifacts and simplified path handling.
#requestAcpPlanApprovalChoice returned 'value !== REFINE_OPTION' which
treats any non-refine outcome — explicit dismissal, transport failure,
abort, or timeout — as approval. That meant closing the elicitation
dialog (or the request failing) granted the agent write access without
explicit user consent.
Tighten to 'value === APPROVE_OPTION' so only the explicit approve
selection passes the gate. Refine, dismissal, and every other outcome
fall through to refine semantics: the caller keeps plan mode active and
returns guidance text instead of renaming the plan and exiting.
Adds a regression test that drives a form-capable harness with a
cancelled elicitation and asserts the plan file is preserved, plan
mode/handler stay active, plan reference stays unset, and no mode-exit
notifications are emitted.
ACP plan approval exited plan mode internally and emitted the current-mode
notification, but it did not push the matching mode config-option update.
Clients that cache or render the config selector could therefore keep
showing mode=plan after approval switched the session back to default.
The approval path now emits the same config_option_update used by other
mode transitions, and the regression test asserts that the approval-driven
exit updates both current_mode_update and the mode config option.
ACP plan mode set the plan-mode state but never installed the standing
resolve handler that interactive mode wires in #enterPlanMode. The agent
in plan mode dutifully called resolve { action: 'apply', extra.title }
to submit its plan; ResolveTool.execute then consulted
peekQueueInvoker() ?? peekStandingResolveHandler(), found neither, and
threw 'No pending action to resolve. Nothing to apply or discard.' —
stranding the agent with no path out of plan mode in Zed (and any other
ACP client).
#applyModeChange now registers a standing handler when entering
mode: 'plan' and clears it when leaving. The handler:
- validates the plan file exists at the configured '/data/workspaces/can1357__oh-my-pi__1869/.omp-session/2026-06-04T15-12-15-302Z_019e9331-31c6-7000-9bce-6be119505a1d/local' path,
- normalizes the agent-supplied title to a safe filename stem,
- asks the ACP client to confirm via unstable_createElicitation when
the client advertises elicitation.form (auto-approves otherwise so
the agent never gets stuck on a client without the surface),
- renames the plan to '/data/workspaces/can1357__oh-my-pi__1869/.omp-session/2026-06-04T15-12-15-302Z_019e9331-31c6-7000-9bce-6be119505a1d/local/<title>.md,'
- sets the plan reference path so the next turn injects the plan as
context, then clears the standing handler + plan-mode state and
emits current_mode_update so the client UI reflects the exit.
Refinement (user picks 'Refine plan') returns guidance text and leaves
plan mode active so the agent can iterate.
Fixes#1869
- Renamed `TodoWriteTool` to `TodoTool` and its source/prompt files.
- Updated tool registration, schema, renderers, and gating to `todo`.
- Adjusted cursor provider native tool names and tests to match.
- Renamed strike-animation constants and `todo-error-reminder` type.
- Tracked ACP tool-call inputs per session and replayed them via `toolArgsById`/`getToolArgs` plumbing.
- Merged ACP tool execution end content from start and result events so command output replay preserves original args.
- Scoped ACP async-job draining by session `ownerId` and `agentId` with in-flight tracking and permission-gated deferred turns.
- Refactored compaction telemetry and async tests with per-test telemetry setup and asynchronous teardown resets.