- add discovery of `TITLE_SYSTEM.md` and pass it through interactive startup context
- route custom title prompts to online and local tiny title generators via protocol
- update session-title docs and changelog with override behavior
- add tests for prompt discovery, forwarding, and fallback to bundled title prompts
The schema default for tools.approvalMode is already "yolo", so checking the
resolved setting alone disabled the ACP permission gate for every
default-config session (17 existing tests in
agent-session-acp-permission.test.ts fail). The skip now requires an
explicitly configured approval mode — the --yolo/--auto-approve runtime
override or a user-set tools.approvalMode — via the new
Settings.isConfigured(), keeping default ACP sessions gated.
Addresses review feedback on #2097.
- Type test registry mocks as CanonicalModelVariant[] (main's catalog split
added canonicalId/selector/source to the variant shape; tsgo failed on the
PR's loose { model } mocks)
- Move the CHANGELOG entry back under [Unreleased] (rebase auto-merge dropped
it into the released 15.10.2 section)
Addresses review feedback on #2044.
When a pattern like "qwen/qwen3-coder:exacto" contains "/" but no
recognised provider prefix, findExactModelReferenceMatch fails (it
treats "qwen" as the provider). Fall through to scan available models
by id so the pattern still matches the openrouter-hosted model.
Adds a regression test covering this case.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use parseThinkingLevel to validate :suffix before stripping, so
colon-bearing OpenRouter ids (openrouter/qwen/qwen3-coder:exacto) are
preserved and matched correctly
- Skip glob patterns instead of early-returning, so mixed glob + exact
patterns still apply the exact ones; only-globs falls back to all
- Return [] on no-match (consistent with resolveAllowedModels contract)
instead of the full list, so a typo in enabledModels gives consistent
signals (session fails to start + picker is hidden)
- Remove double blank line (nit)
- Add CHANGELOG entry
- Add OpenRouter regression test and update no-match expectation
getAvailableModels() was calling modelRegistry.getAvailable() directly,
which skips the enabledModels setting. The setting was only applied
during session init to pick the starting model, not to the list
advertised to ACP clients (Zed, etc.).
Add filterAvailableModelsByEnabledPatterns() to model-resolver.ts - a
synchronous subset of resolveAllowedModels() that handles the patterns
used in real configs (exact provider/modelId, canonical ids, bare model
ids, thinking-level suffixes). Glob patterns fall back to showing all
models rather than accidentally emptying the picker.
Update getAvailableModels() to call it, so the ACP model dropdown in
Zed (and any other ACP client) respects the users enabledModels config.
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.
Extension commands (e.g. /sonnet) and TypeScript custom commands that
consume the input without calling the LLM return early from
session.prompt() with no agent turn. In ACP mode this left the pending
prompt promise unresolved, hanging the client forever.
Change session.prompt() to return Promise<boolean>: true when the LLM
was invoked, false when the command was fully handled locally.
#runPromptOrCommand calls #finishPrompt immediately on a false return so
the ACP turn completes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ACP_BUILTIN_SLASH_COMMANDS only carries primary names; the reserved set
passed to getRegisteredCommands was therefore missing aliases like
"models" (/model) and "force:" (/force). An extension registering one
of these aliases would appear in the palette but the builtin would win
at dispatch time (lookupBuiltinSlashCommand searches aliases too).
Export ACP_BUILTIN_RESERVED_NAMES from acp-builtins — the union of all
primary names and aliases for ACP-surfaced builtins — and use it as the
reserved set. Widen getRegisteredCommands parameter to ReadonlySet since
it only calls .has().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
`#buildAvailableCommands` was omitting commands registered by extensions
via `extensionRunner.getRegisteredCommands()`. ACP clients (e.g. Zed)
never saw these in the `available_commands_update` notification, so they
couldn't forward the corresponding slash commands to the agent.
Mirrors the interactive-mode pattern: pass ACP builtin names as the
reserved set so extensions cannot shadow core commands.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The native arboard backend cannot retain X11 / Wayland selection ownership
after the calling process exits, and for short-lived napi calls it can drop
ownership before any consumer sees the selection — leaving the clipboard
empty even though set_text returned success. tmux + QTerminal made this
visible because OSC 52 is also dropped by libqtermwidget, so both backends
fail and the UI's 'Copied to clipboard' status reads as a lie.
utils/clipboard.ts now:
- Tries wl-copy / xclip / xsel before arboard on Linux. These CLIs fork
after reading stdin and serve the selection until another app claims it,
so the payload survives our process exit.
- Honors OMP_CLIPBOARD_COMMAND as a shell-string escape hatch (e.g.
`xclip -selection clipboard -in -silent`).
- Logs a single warning when every backend fails, so the silent-success
regression from #2075 cannot recur unnoticed.
Tests assert dispatch order: Linux+X11 prefers xclip, Wayland prefers
wl-copy, xclip→xsel fallback works, all-fail falls back to native,
macOS still goes straight to native, and OMP_CLIPBOARD_COMMAND wins
over the CLI chain.
Fixes#2075