@borgee/agents-host 0.2.74 → 0.2.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +11 -24
  2. package/dist/agents-host.d.ts +6 -4
  3. package/dist/agents-host.js +127 -16
  4. package/dist/background-runs.d.ts +24 -0
  5. package/dist/background-runs.js +184 -0
  6. package/dist/chat/chat-control-plane.d.ts +7 -1
  7. package/dist/chat/sdk-chat-control-plane.d.ts +7 -2
  8. package/dist/chat/sdk-chat-control-plane.js +22 -0
  9. package/dist/context/claude-file-brief.js +3 -3
  10. package/dist/context/injection.d.ts +18 -12
  11. package/dist/context/injection.js +53 -53
  12. package/dist/context/prompt.js +10 -10
  13. package/dist/context/resolved-working-folder.d.ts +3 -0
  14. package/dist/context/resolved-working-folder.js +106 -0
  15. package/dist/context/turn-preparation.js +1 -1
  16. package/dist/managed-daemon.js +30 -30
  17. package/dist/plugin-sdk.js +315 -29
  18. package/dist/plugin-sdk.js.map +3 -3
  19. package/dist/policy/authorization-audit.d.ts +1 -1
  20. package/dist/policy/copilot-permission.d.ts +9 -0
  21. package/dist/policy/copilot-permission.js +120 -1
  22. package/dist/progress-to-activity.d.ts +1 -1
  23. package/dist/progress-to-activity.js +1 -0
  24. package/dist/providers/claude/adapter.d.ts +2 -1
  25. package/dist/providers/claude/adapter.js +36 -1
  26. package/dist/providers/claude/background-run-observer.d.ts +32 -0
  27. package/dist/providers/claude/background-run-observer.js +381 -0
  28. package/dist/providers/claude/cli-client.d.ts +12 -0
  29. package/dist/providers/claude/cli-client.js +453 -72
  30. package/dist/providers/claude/foreground-handoff.d.ts +4 -0
  31. package/dist/providers/claude/foreground-handoff.js +45 -0
  32. package/dist/providers/claude/task-cancellation-protocol.d.ts +14 -0
  33. package/dist/providers/claude/task-cancellation-protocol.js +21 -0
  34. package/dist/providers/codex/adapter.d.ts +1 -1
  35. package/dist/providers/codex/adapter.js +7 -0
  36. package/dist/providers/codex/cli-client.js +8 -7
  37. package/dist/providers/codex/project-doc.js +6 -6
  38. package/dist/providers/copilot/activity-metadata.d.ts +3 -0
  39. package/dist/providers/copilot/activity-metadata.js +19 -0
  40. package/dist/providers/copilot/adapter.d.ts +6 -2
  41. package/dist/providers/copilot/adapter.js +27 -1
  42. package/dist/providers/copilot/cli-client.d.ts +37 -10
  43. package/dist/providers/copilot/cli-client.js +756 -122
  44. package/dist/providers/copilot/sdk-session.d.ts +149 -0
  45. package/dist/providers/copilot/sdk-session.js +981 -0
  46. package/dist/providers/create-provider.js +33 -0
  47. package/dist/providers/provider-adapter.d.ts +24 -1
  48. package/dist/providers/provider-adapter.js +17 -0
  49. package/dist/types.d.ts +40 -11
  50. package/dist/vendor/claude-agent-acp/LICENSE +191 -0
  51. package/dist/vendor/claude-agent-acp/NOTICE +8 -0
  52. package/dist/vendor/claude-agent-acp/dist/acp-agent.d.ts +1017 -0
  53. package/dist/vendor/claude-agent-acp/dist/acp-agent.d.ts.map +1 -0
  54. package/dist/vendor/claude-agent-acp/dist/acp-agent.js +6305 -0
  55. package/dist/vendor/claude-agent-acp/dist/borgee-foreground-handoff-bridge.js +322 -0
  56. package/dist/vendor/claude-agent-acp/dist/borgee-task-cancellation-bridge.js +101 -0
  57. package/dist/vendor/claude-agent-acp/dist/borgee-task-lifecycle-bridge.js +58 -0
  58. package/dist/vendor/claude-agent-acp/dist/elicitation.d.ts +130 -0
  59. package/dist/vendor/claude-agent-acp/dist/elicitation.d.ts.map +1 -0
  60. package/dist/vendor/claude-agent-acp/dist/elicitation.js +304 -0
  61. package/dist/vendor/claude-agent-acp/dist/index.d.ts +3 -0
  62. package/dist/vendor/claude-agent-acp/dist/index.d.ts.map +1 -0
  63. package/dist/vendor/claude-agent-acp/dist/index.js +75 -0
  64. package/dist/vendor/claude-agent-acp/dist/lib.d.ts +6 -0
  65. package/dist/vendor/claude-agent-acp/dist/lib.d.ts.map +1 -0
  66. package/dist/vendor/claude-agent-acp/dist/lib.js +5 -0
  67. package/dist/vendor/claude-agent-acp/dist/settings.d.ts +68 -0
  68. package/dist/vendor/claude-agent-acp/dist/settings.d.ts.map +1 -0
  69. package/dist/vendor/claude-agent-acp/dist/settings.js +185 -0
  70. package/dist/vendor/claude-agent-acp/dist/tools.d.ts +102 -0
  71. package/dist/vendor/claude-agent-acp/dist/tools.d.ts.map +1 -0
  72. package/dist/vendor/claude-agent-acp/dist/tools.js +1000 -0
  73. package/dist/vendor/claude-agent-acp/dist/utils.d.ts +16 -0
  74. package/dist/vendor/claude-agent-acp/dist/utils.d.ts.map +1 -0
  75. package/dist/vendor/claude-agent-acp/dist/utils.js +81 -0
  76. package/dist/vendor/claude-agent-acp/package.json +85 -0
  77. package/package.json +15 -10
  78. package/skills/borgee-agent/references/task-properties.md +3 -3
  79. package/skills/borgee-agent/scripts/borgee-agent.mjs +1 -1
  80. package/skills/borgee-agent/scripts/borgee-agent.py +1 -1
  81. package/dist/context/resolved-workspace.d.ts +0 -3
  82. package/dist/context/resolved-workspace.js +0 -106
@@ -0,0 +1,1017 @@
1
+ import { AuthenticateRequest, CancelNotification, ClientCapabilities, CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse, DisableProviderRequest, DisableProviderResponse, ForkSessionRequest, ForkSessionResponse, InitializeRequest, InitializeResponse, ListProvidersRequest, ListProvidersResponse, LlmProtocol, ListSessionsRequest, ListSessionsResponse, LoadSessionRequest, LoadSessionResponse, LogoutRequest, NewSessionRequest, NewSessionResponse, PromptRequest, PromptResponse, ReadTextFileRequest, ReadTextFileResponse, SetProviderRequest, SetProviderResponse, RequestPermissionRequest, RequestPermissionResponse, ResumeSessionRequest, ResumeSessionResponse, SessionConfigOption, SessionModeState, SessionNotification, SetSessionConfigOptionRequest, SetSessionConfigOptionResponse, SetSessionModeRequest, SetSessionModeResponse, CloseSessionRequest, CloseSessionResponse, DeleteSessionRequest, DeleteSessionResponse, WriteTextFileRequest, WriteTextFileResponse } from "@agentclientprotocol/sdk";
2
+ import { AgentInfo, CanUseTool, FastModeDisabledReason, FastModeState, ModelInfo, Options, PermissionMode, PermissionUpdate, Query, SDKMessageOrigin, SDKPartialAssistantMessage, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
3
+ import { ContentBlockParam } from "@anthropic-ai/sdk/resources";
4
+ import { BetaContentBlock, BetaRawContentBlockDelta } from "@anthropic-ai/sdk/resources/beta.mjs";
5
+ import { SettingsManager } from "./settings.js";
6
+ import { TaskState } from "./tools.js";
7
+ import { Pushable } from "./utils.js";
8
+ export declare const CLAUDE_CONFIG_DIR: string;
9
+ /**
10
+ * Logger interface for customizing logging output
11
+ */
12
+ export interface Logger {
13
+ log: (...args: any[]) => void;
14
+ error: (...args: any[]) => void;
15
+ }
16
+ type AccumulatedUsage = {
17
+ inputTokens: number;
18
+ outputTokens: number;
19
+ cachedReadTokens: number;
20
+ cachedWriteTokens: number;
21
+ };
22
+ /** Request-level steering options. `promptRequired` is opt-in so existing Hosts
23
+ * keep the established idle fallback behavior. */
24
+ type SteerMeta = {
25
+ [key: string]: unknown;
26
+ steering?: {
27
+ idleBehavior?: "promptRequired";
28
+ };
29
+ };
30
+ /** Params of a {@link STEER_METHOD} request. Shaped like the relevant subset of
31
+ * a `PromptRequest` so the same `promptToClaude` conversion applies. Delivery
32
+ * priority is deliberately NOT exposed here — it's an internal detail the agent
33
+ * chooses (see {@link STEER_PRIORITY}). */
34
+ export type SteerRequest = {
35
+ sessionId: string;
36
+ prompt: PromptRequest["prompt"];
37
+ _meta?: SteerMeta | null;
38
+ };
39
+ /** Result of a {@link STEER_METHOD} request. The legacy `startedNewTurn` result
40
+ * remains the default idle behavior; `promptRequired` is returned only when the
41
+ * Host explicitly opts into the host-owned fallback in request `_meta`. */
42
+ export type SteerResponse = {
43
+ outcome: "injected";
44
+ } | {
45
+ outcome: "startedNewTurn";
46
+ } | {
47
+ outcome: "promptRequired";
48
+ reason: "noRunningTurn";
49
+ };
50
+ /** Internal model-selection state. Mirrors the shape the ACP SDK exposed as
51
+ * `SessionModelState` before model selection moved entirely into
52
+ * `SessionConfigOption` (category "model"). Retained internally to track the
53
+ * current model and build the "model" config option. */
54
+ type SessionModelState = {
55
+ availableModels: Array<{
56
+ modelId: string;
57
+ name: string;
58
+ description?: string;
59
+ }>;
60
+ currentModelId: string;
61
+ };
62
+ /** One in-flight `prompt()` call. A persistent per-session consumer (see
63
+ * `runConsumer`) drains the SDK query stream for the whole session and settles
64
+ * each Turn's deferred when that turn's outcome is known, so `prompt()` itself
65
+ * holds no loop. Turns are processed FIFO: the SDK echoes queued user messages
66
+ * back in submission order, so `turnQueue[0]` is the turn currently running. */
67
+ type Turn = {
68
+ /** uuid stamped on the pushed `SDKUserMessage`; the SDK echoes it back so the
69
+ * consumer can match the replayed user message to this turn. */
70
+ promptUuid: string;
71
+ /** Local-only slash commands (e.g. `/clear`) return a result without an echo,
72
+ * so the consumer can't promote them via the replay; it falls back to
73
+ * promoting the queue head when the result arrives. */
74
+ isLocalOnlyCommand: boolean;
75
+ /** Set once the deferred has been resolved/rejected, so the consumer never
76
+ * settles a turn twice (idle + handoff + stream-end can all race). */
77
+ settled: boolean;
78
+ /** Set when a `command_lifecycle` "started" frame arrives for this turn's
79
+ * uuid (msg_lifecycle_v1 CLIs): the SDK dispatched the command into a turn.
80
+ * Read by cancel() to seed the orphan's state — a started orphan's turn may
81
+ * still emit a result, an undispatched one may be dropped without one. */
82
+ commandStarted?: boolean;
83
+ /** Set when a terminal `command_lifecycle` frame arrives for this turn's
84
+ * uuid while the turn is still queued (msg_lifecycle_v1 CLIs). The command
85
+ * is already finished SDK-side, so a later cancel() must not seed an
86
+ * orphan entry for it — no terminal frame will ever come to drain it.
87
+ * "completed"/"discarded" leave nothing outstanding; "cancelled" after a
88
+ * dispatch means the dead turn's result may still arrive (seeded as a
89
+ * zombie) unless it already passed (`commandResultSeen`), and without a
90
+ * dispatch means dropped (nothing coming). */
91
+ commandFinished?: "completed" | "discarded" | "cancelled";
92
+ /** Set when a user-turn result arrives while this command is known
93
+ * dispatched (`commandStarted`) with no terminal frame yet. Turns run
94
+ * sequentially and frames arrive in stream order, so the turn this command
95
+ * was dispatched into IS the turn that emitted that result — including
96
+ * when the command was FOLDED into another turn (their shared result).
97
+ * Read by cancel() and the force-cancel wedge path so neither seeds an
98
+ * orphan entry for a result that has already passed: such an entry could
99
+ * never be drained by its result and would swallow an unrelated later
100
+ * echo-less one instead. */
101
+ commandResultSeen?: boolean;
102
+ /** Task ids of the background subagents launched while this turn was the
103
+ * active one — including during its held-open drain window, so an agent
104
+ * chain (a followup that launches another subagent) extends the hold.
105
+ * A turn only waits on its OWN spawned subagents: a long-running agent
106
+ * from an earlier turn must not stall every later prompt's settlement.
107
+ * Known residual: task_started carries no lineage, so a spawn made by a
108
+ * PREVIOUS turn's followup chain while a later turn happens to be held
109
+ * is attributed to the holder — extending that hold behind a foreign
110
+ * chain. Bounded: the hold still ends at drain, hand-off, or cancel. */
111
+ spawnedTaskIds?: Set<string>;
112
+ /** Set instead of settling when the turn's terminal result arrives while
113
+ * subagents it spawned are still live (`spawnedTaskIds` ∩
114
+ * `session.liveBackgroundTasks`). The turn is held open — its
115
+ * `session/prompt` stays pending — so the subagents' streamed output,
116
+ * their permission requests (which would otherwise block on an RPC a
117
+ * client that stops consuming at the prompt response never answers —
118
+ * issue #866), and the model's task-notification followup summary all
119
+ * land inside the turn.
120
+ *
121
+ * The CLI does NOT hold its trailing idle for background agents (observed
122
+ * on 2.1.206: `idle` follows the result immediately while the subagent
123
+ * still runs), so the hold spans multiple idle cycles: user result →
124
+ * idle → (subagent works) → task_notification → followup turn → idle.
125
+ * The stored outcome (the result's stop reason and usage snapshot) is
126
+ * what the turn settles with once its spawned subagents have settled —
127
+ * at the followup's terminal result (the summary has streamed by then),
128
+ * or at an idle with none of its subagents left (no followup came). A
129
+ * cancel or the next turn's echo hand-off settles it earlier, so a
130
+ * long-running subagent never holds the prompt hostage.
131
+ *
132
+ * Accepted residuals. (1) A subagent that ends WITHOUT waking the model —
133
+ * its task_notification lost or skipped (only the terminal task_updated
134
+ * patch is guaranteed per transition) — leaves no followup result and no
135
+ * further idle, so the held turn parks until `session/cancel` or the next
136
+ * prompt (either settles it: the echo hand-off or ensureActiveTurn's
137
+ * held-turn hand-off). Settling at the prune sites instead would preempt
138
+ * the followup summary in the normal ordering (prunes precede the
139
+ * notification), and a grace timer was judged not worth the machinery —
140
+ * the same rescue contract as the adapter's other wedge classes (issue
141
+ * #825's out-of-scope notes). (2) Drained-ness is judged by live-task
142
+ * membership only: with parallel subagents, a notification that prunes
143
+ * the last task during an earlier task's still-streaming followup lets
144
+ * that followup's result settle the turn before the LAST task's summary
145
+ * streams — degrading to post-turn delivery for it, never worse than the
146
+ * pre-hold behavior (pending wakes are not countable: notifications can
147
+ * batch into one followup). */
148
+ deferredSettle?: PromptResponse;
149
+ resolve: (response: PromptResponse) => void;
150
+ reject: (error: unknown) => void;
151
+ };
152
+ type Session = {
153
+ query: Query;
154
+ input: Pushable<SDKUserMessage>;
155
+ cancelled: boolean;
156
+ /** FIFO of in-flight prompts. The head is the turn the SDK is currently
157
+ * processing; later entries are queued and will be echoed in order. */
158
+ turnQueue?: Turn[];
159
+ /** The turn whose messages the consumer is currently attributing output to
160
+ * (the head of `turnQueue` once its user message has been echoed). */
161
+ activeTurn?: Turn | null;
162
+ /** Count of result messages the consumer should treat as orphans and skip
163
+ * (not promote/attribute to the current head). When cancel() settles+removes
164
+ * a queued turn, that turn's user message was already pushed to the SDK, so
165
+ * the SDK still runs it and emits a result with no uuid we can match. Because
166
+ * the SDK processes input FIFO, those orphan results arrive (in submission
167
+ * order) before the next live turn's, so skipping exactly this many leaves
168
+ * the genuine head untouched. On CLIs with the interrupt receipt, orphans
169
+ * the interrupt dropped (absent from `still_queued`) are uncounted as soon
170
+ * as the receipt arrives (see cancel()). Reset to 0 on every activation as
171
+ * a backstop against a dropped queued input this can't see (older CLIs, a
172
+ * receipt lost to a failed control round-trip). Only used when the CLI does
173
+ * NOT emit lifecycle frames (see `orphanCommands` for the msg_lifecycle_v1
174
+ * lane); a count can't express command coalescing — N queued commands can
175
+ * fold into ONE turn emitting one result, leaving a stale skip of N-1. */
176
+ pendingOrphanResults?: number;
177
+ /** msg_lifecycle_v1 lane of the orphan accounting (see
178
+ * `pendingOrphanResults` for the count lane): the uuids of cancelled queued
179
+ * turns whose SDK-side command may still produce an unaccounted result,
180
+ * keyed to what we know of its fate. "pending" = not seen dispatched; if
181
+ * the SDK drops it (interrupt, `cancelled` before "started") no result
182
+ * ever comes. "started" = dispatched into a turn whose result is still
183
+ * coming; exactly one terminal lifecycle frame will follow. "zombie" = its
184
+ * turn was aborted/failed after dispatch with no result seen since
185
+ * (`cancelled` after "started"); no more lifecycle frames come, but the
186
+ * dead turn's error result may still arrive. Entries are removed the
187
+ * moment their result is covered: EVERY user-turn result covers ALL
188
+ * started and zombie entries at once (turns run sequentially and frames
189
+ * arrive in stream order, so at any result the started entries were
190
+ * dispatched into — possibly folded into — the emitting turn, and any
191
+ * zombie's late result has already passed or never existed), whether that
192
+ * result was attributed to the active turn or skipped echo-less (see
193
+ * recordResultForOrphanCommands / ensureActiveTurn). A command's own
194
+ * terminal frame also drains its entry ("completed" is emitted after any
195
+ * result its turn produced; a bare `cancelled` deletes a pending entry —
196
+ * dropped without running — and zombifies a started one). An echo-less
197
+ * result is an orphan's iff this map is non-empty (FIFO: orphan turns run
198
+ * before any live turn's). Cleared on every activation, same self-heal as
199
+ * the count (covers a lost frame, which can leak an entry — each state
200
+ * bounds the damage to one wrong skip). */
201
+ orphanCommands?: Map<string, "pending" | "started" | "zombie">;
202
+ /** True once a `system`/init advertised the msg_lifecycle_v1 capability, so
203
+ * cancel() routes orphan accounting to `orphanCommands` (exact, per-uuid)
204
+ * instead of `pendingOrphanResults` (count, coalescing-blind). */
205
+ msgLifecycleV1?: boolean;
206
+ /** The long-lived consumer task. Lazily started on the first `prompt()` and
207
+ * kept alive for the session so between-turn/background messages are still
208
+ * drained and forwarded. */
209
+ consumer?: Promise<void>;
210
+ /** Set once the SDK query stream has terminated (it ran to `done` or threw a
211
+ * non-process error). The query iterator is not reusable afterward, so a
212
+ * later `prompt()` rejects instead of enqueueing onto a dead stream and
213
+ * hanging (or silently restarting a consumer that resolves `end_turn`
214
+ * without ever reaching the model). */
215
+ queryClosed?: boolean;
216
+ cwd: string;
217
+ /** Serialized snapshot of session-defining params (cwd, mcpServers) used to
218
+ * detect when loadSession/resumeSession is called with changed values. */
219
+ sessionFingerprint: string;
220
+ settingsManager: SettingsManager;
221
+ accumulatedUsage: AccumulatedUsage;
222
+ modes: SessionModeState;
223
+ models: SessionModelState;
224
+ modelInfos: ModelInfo[];
225
+ configOptions: SessionConfigOption[];
226
+ /** Custom main-thread agent personas the user (or a plugin/project) has
227
+ * configured, discovered via `supportedAgents()` with Claude Code's built-in
228
+ * subagents filtered out. Empty when none are configured, in which case the
229
+ * "agent" config option is omitted entirely. */
230
+ agents: AgentInfo[];
231
+ /** The currently selected main-thread agent name, or "default" for the
232
+ * standard Claude Code agent (no `agent` flag applied). */
233
+ currentAgent: string;
234
+ /** Whether Fast mode is currently enabled for this session. Tracked as the
235
+ * user's intent so it persists across model switches; the Fast mode config
236
+ * option is only surfaced while the selected model supports it. */
237
+ fastModeEnabled: boolean;
238
+ /** Why the SDK currently can't serve Fast mode, when the reason is one worth
239
+ * telling the user about (see {@link FAST_MODE_UNAVAILABLE_EXPLANATIONS} —
240
+ * routine states like the SDK's own opt-in requirement normalize to
241
+ * `undefined`). Refreshed from every `fast_mode_disabled_reason` the SDK
242
+ * reports on `system`/init and user-turn `result`s; surfaced in the Fast mode
243
+ * option's description so a toggle that snaps back off explains itself. */
244
+ fastModeDisabledReason?: FastModeDisabledReason;
245
+ abortController: AbortController;
246
+ /** Signal the consumer races `query.next()` against. Aborted by cancel()
247
+ * (after a grace period) to force the active turn to settle "cancelled" when
248
+ * the SDK is wedged and `query.next()` never yields again (issue #680).
249
+ * Distinct from `abortController`: this only wakes the consumer; it does NOT
250
+ * touch the SDK query/subprocess. The consumer re-arms it after each fire.
251
+ * Undefined until the consumer is started by the first prompt. */
252
+ cancelController?: AbortController;
253
+ /** Pending grace-period timer that aborts `cancelController`. Cleared when the
254
+ * active turn settles normally so the backstop never fires after a clean
255
+ * cancel. */
256
+ forceCancelTimer?: ReturnType<typeof setTimeout>;
257
+ emitRawSDKMessages: boolean | SDKMessageFilter[];
258
+ /** Whether nested subagent text/thinking is forwarded to the ACP client.
259
+ * Enabled by either the ACP capability or the pre-existing SDK option. */
260
+ forwardSubagentText: boolean;
261
+ /** Context window size of the session's current model, carried across
262
+ * prompts so mid-stream usage_update notifications report a correct `size`
263
+ * before the turn's first result message arrives. Seeded synchronously at
264
+ * session creation and on model switches from the per-model cache or the
265
+ * text heuristic (DEFAULT_CONTEXT_WINDOW when both miss; on session/load the
266
+ * resumed session's own `getContextUsage` report wins, see
267
+ * `readResumedLiveModel`), then confirmed — and the cache populated — by each
268
+ * result's modelUsage. No extra `getContextUsage` IPC is on these paths: on a
269
+ * fresh session it stalls until the first turn runs (see the seeding call
270
+ * sites and `contextWindowCache`). */
271
+ contextWindowSize: number;
272
+ /** Whether `contextWindowSize` came from an authoritative source (the
273
+ * cross-session cache, a resumed session's `getContextUsage` report, or a
274
+ * `result.modelUsage`) rather than the text heuristic / default. Guards the
275
+ * mid-stream `message_start` heuristic upgrade: an authoritative window that
276
+ * happens to equal DEFAULT_CONTEXT_WINDOW must not be mistaken for "unseeded"
277
+ * and clobbered by a "1m" text match. */
278
+ contextWindowAuthoritative: boolean;
279
+ /** Stable identifier of the LLM backend this session's query was created
280
+ * against, derived from the routing-relevant vars of the exact `env` handed
281
+ * to the SDK at query creation (see {@link providerCacheKeyFor}). The context
282
+ * window is a property of (model id, backend) — the same resolved model id
283
+ * can name different windows behind different base URLs, routing headers, or
284
+ * credentials — so this scopes the module-global `contextWindowCache` per
285
+ * backend. Captured from the query's own env (not re-resolved later) because
286
+ * the process-wide provider config can change while a session is being
287
+ * created, while the query stays baked to the env it was created with. */
288
+ providerCacheKey: string;
289
+ /** Accumulated task list for the session, keyed by task ID. Task IDs are
290
+ * per-session, so this state must not be shared across sessions. */
291
+ taskState: TaskState;
292
+ /** Last session title we pushed to the client via `session_info_update`.
293
+ * The SDK auto-generates a title in a background task and persists it to the
294
+ * session file; we poll it on each turn-end (`session_state_changed: idle`)
295
+ * and only notify the client when it actually changes. Undefined until the
296
+ * first title is observed. */
297
+ lastTitle?: string;
298
+ /** Caches `tool_use` blocks by id so the matching `tool_result` can recover
299
+ * the tool name/input when mapping it to a `tool_call_update`. Per-session
300
+ * (tool_use ids are only unique within a session) and pruned at
301
+ * `tool_result` time so a long-running session doesn't accumulate every
302
+ * tool call for its whole lifetime. */
303
+ toolUseCache: ToolUseCache;
304
+ /** Tracks which tool_use ids we've already emitted a `tool_call` for, so the
305
+ * second source to encounter a tool call sends a `tool_call_update` instead
306
+ * of a duplicate `tool_call`. The SDK can invoke `canUseTool` (→ a permission
307
+ * request, which emits the tool_call eagerly so the client has it before
308
+ * being asked to approve it) either before or after the assistant message's
309
+ * tool_use block streams; this set makes the two paths converge regardless of
310
+ * order. Pruned at `tool_result` time alongside `toolUseCache`. */
311
+ emittedToolCalls: Set<string>;
312
+ /** Registry of live background tasks, keyed by task id: populated at
313
+ * `task_started`, pruned when the task settles (a `task_notification` or
314
+ * a terminal `task_updated` patch), and reconciled against
315
+ * `background_tasks_changed`'s replace-semantics payload so a lost
316
+ * bookend can't leak an entry. One structure for both of its concerns so
317
+ * a future terminal path can't prune one and not the other:
318
+ *
319
+ * `parentToolUseId` — the tool_use id of the Agent/Task call that spawned
320
+ * the task. For subagent tasks the SDK keys its registry by agent id, so
321
+ * `task_started.task_id` IS the `agentID` that `canUseTool` later
322
+ * receives. Lets the permission flow attribute a subagent's
323
+ * eagerly-emitted `tool_call` (and the permission request itself) to its
324
+ * parent tool call via `_meta.claudeCode.parentToolUseId`, matching the
325
+ * streamed subagent path. Best-effort: a `canUseTool` that races ahead of
326
+ * the consumer processing `task_started` omits the attribution from the
327
+ * eager tool_call, and the streamed tool_use chunk's refining
328
+ * `tool_call_update` — which carries the message-level
329
+ * `parent_tool_use_id` — restores it for merging clients; that recovery
330
+ * is what makes best-effort acceptable here.
331
+ *
332
+ * `isSubagent` — whether the task is a Task/Agent-tool subagent
333
+ * (`task_started` carried a `subagent_type`). Read by
334
+ * `turnAwaitingSubagents` (with `spawnedTaskIds`) to decide whether a
335
+ * turn's settlement is deferred (see `Turn.deferredSettle`), so the
336
+ * subagents' post-result output and permission requests stay inside the
337
+ * turn (issues #864/#866). Deliberately false for non-subagent background
338
+ * tasks (e.g. a `run_in_background` dev server): those can outlive every
339
+ * turn, and the model's contract with them is a wake-on-exit
340
+ * notification, not a turn-scoped drain — a hold must NEVER wait on a
341
+ * shell.
342
+ *
343
+ * `endedPerLevel` — a `background_tasks_changed` payload did not include
344
+ * this subagent entry. The level's universe is BACKGROUND tasks only, so
345
+ * a live sync (foreground) subagent is legitimately absent — its entry is
346
+ * kept for permission attribution — but a hold must stop waiting on the
347
+ * id: an absent id can equally be a leaked async entry whose settle
348
+ * bookends were lost, and waiting on it would park the hold forever.
349
+ * Non-subagent entries are simply deleted instead (shells are always in
350
+ * the level's universe). */
351
+ liveBackgroundTasks: Map<string, {
352
+ parentToolUseId?: string;
353
+ isSubagent: boolean;
354
+ /** Absent-from-level lifecycle, one field so the illegal
355
+ * armed-but-not-ended state is unrepresentable: undefined = live per
356
+ * the level signal; "ended" = a level omitted the task (holds stop
357
+ * waiting on it; attribution is kept); "sweep-armed" = a turn
358
+ * activation saw it ended — the NEXT activation deletes it. The
359
+ * one-activation grace exists for the absent-mark race (a level
360
+ * payload built before a live async agent's registration): a
361
+ * corrective inclusive level resets the field to undefined — one
362
+ * assignment, disarming any in-flight sweep — if it arrives within a
363
+ * full turn, keeping the agent's attribution; eager deletion would
364
+ * be irreversible, since levels never ADD entries. A re-mark
365
+ * preserves an in-flight arm (`??=`), keeping a continuously absent
366
+ * entry on its two-activation clock. */
367
+ endedPerLevel?: "ended" | "sweep-armed";
368
+ }>;
369
+ /** Whether any top-level assistant text reached the client since the last
370
+ * stretch boundary. Set as a side effect of sending in the consumer's
371
+ * `sendUpdate`, never at an emission site; read at the terminal `result`
372
+ * to tell a turn whose answer was already delivered from one that only
373
+ * ever carried it on `result` (issue #453). Session-level (not
374
+ * consumer-scoped) so cancel()'s inline settle can clear it.
375
+ *
376
+ * The CURRENT boundary set — a new clear site must be added here: the
377
+ * result case's `finally` (user-turn results), settleActive's wasHeld
378
+ * clear (every held-turn settle lane: drain settle, both hand-offs,
379
+ * stream-done), failActive, the force-cancel backstop, the idle
380
+ * cancelled-settle, the autonomous-result close (only with no turn
381
+ * active OR queued — see its queued-turn guard), and cancel()'s inline
382
+ * mirror.
383
+ *
384
+ * Deliberately NOT reset on turn activation: activation can fire
385
+ * mid-message (see the echo hand-off), so a flag cleared there would
386
+ * forget text that already streamed and the result text would be emitted
387
+ * a second time. Neither the consolidated `assistant` message nor a
388
+ * `stream_event` carries `origin`, so an autonomous cycle's prose is
389
+ * indistinguishable from a user turn's here and sets the flag too; the
390
+ * autonomous-result close normally ends that stretch so a replayed
391
+ * prompt behind it still delivers, and only in the racing window (a
392
+ * turn already active or queued when the autonomous result lands) does
393
+ * the replayed turn stay silent rather than risk a duplicate. */
394
+ emittedAssistantText: boolean;
395
+ /** The most recent `session_state_changed` state the consumer processed.
396
+ * Read by cancel() to decide whether the interrupt will produce a
397
+ * trailing idle worth pre-counting: interrupting a RUNNING cycle yields
398
+ * one; interrupting an already-idle session (the common held-turn shape)
399
+ * yields none, and a pre-counted debt that never drains would mask one
400
+ * future issue-#825 detection. */
401
+ lastSessionState?: "idle" | "running" | "requires_action";
402
+ /** How many trailing `session_state_changed: idle` messages are already
403
+ * accounted for: every result is followed by one (user-turn results that
404
+ * terminate a turn — settle, reject, or orphan skip — and autonomous
405
+ * cycles alike), as is a cancelled turn settled by the next turn's echo
406
+ * hand-off or by cancel()'s inline settle of a held turn whose interrupt
407
+ * pre-empts a running cycle — the reason this lives on the Session:
408
+ * cancel() must be able to record the debt. The idle handler absorbs
409
+ * owed idles; an idle that arrives when NONE is owed while the active
410
+ * turn is still unsettled means the SDK ended the turn without ever
411
+ * emitting its result, so the turn will never settle on its own (issue
412
+ * #825). Stream-level debt, deliberately NOT reset per turn: a lagged
413
+ * idle can arrive after the next turn has already activated (issue
414
+ * #773), and the debt is what attributes it to the turn that owed it.
415
+ * Over-counting (an idle the SDK never emits) is benign: the counter
416
+ * just absorbs one future idle, and detection degrades to the status quo
417
+ * rather than misfiring. */
418
+ owedTrailingIdles: number;
419
+ /** Maps the ACP `messageId` we expose to clients (see `messageIdForGrouping`)
420
+ * to the SDK message uuid that the Agent SDK's rewind/resume APIs key on
421
+ * (`Query.rewindFiles` takes a user-message uuid; `resumeSessionAt` takes an
422
+ * `SDKAssistantMessage.uuid`). For assistant turns the two differ — the ACP
423
+ * id is the Anthropic API message id (`msg_…`), available at `message_start`
424
+ * so streamed chunks can carry it, while the uuid only arrives on the
425
+ * consolidated message — so a client can only ask to rewind/fork by the id it
426
+ * was given, and we need this table to translate it back.
427
+ *
428
+ * Populated as a byproduct of the message loop (the consolidated message
429
+ * carries both ids) and of `replaySessionHistory` on load, so no extra
430
+ * `getSessionMessages` read is needed at rewind time. Last-write-wins
431
+ * naturally yields the turn-boundary uuid when one `msg_…` spans several
432
+ * content-block messages.
433
+ *
434
+ * NOT READ YET — recorded now so the mapping exists if/when we wire up
435
+ * fork/rewind. */
436
+ messageIdToUuid: Map<string, string>;
437
+ };
438
+ export type SDKMessageFilter = {
439
+ type: string;
440
+ subtype?: string;
441
+ origin?: SDKMessageOrigin["kind"];
442
+ };
443
+ /**
444
+ * Extra metadata that can be given when creating a new session.
445
+ */
446
+ export type NewSessionMeta = {
447
+ claudeCode?: {
448
+ /**
449
+ * Options forwarded to Claude Code when starting a new session.
450
+ * Those parameters will be ignored and managed by ACP:
451
+ * - cwd
452
+ * - includePartialMessages
453
+ * - allowDangerouslySkipPermissions
454
+ * - permissionMode
455
+ * - canUseTool
456
+ * - executable
457
+ * Those parameters will be used and updated to work with ACP:
458
+ * - hooks (merged with ACP's hooks)
459
+ * - mcpServers (merged with ACP's mcpServers)
460
+ * - disallowedTools (merged with ACP's disallowedTools)
461
+ * - tools (passed through; defaults to claude_code preset if not provided)
462
+ */
463
+ options?: Options;
464
+ /**
465
+ * When set, raw SDK messages are emitted as extNotification("_claude/sdkMessage", message)
466
+ * in addition to normal processing.
467
+ * - true: emit all messages
468
+ * - false/undefined: emit nothing (default)
469
+ * - SDKMessageFilter[]: emit only messages matching at least one filter
470
+ */
471
+ emitRawSDKMessages?: boolean | SDKMessageFilter[];
472
+ };
473
+ additionalRoots?: string[];
474
+ };
475
+ /**
476
+ * Extra metadata for 'gateway' authentication requests.
477
+ */
478
+ type GatewayAuthMeta = {
479
+ /**
480
+ * These parameters are mapped to environment variables to:
481
+ * - Redirect API calls via baseUrl
482
+ * - Inject custom headers
483
+ * - Bypass the default Claude login requirement
484
+ */
485
+ gateway: {
486
+ baseUrl: string;
487
+ headers: Record<string, string>;
488
+ };
489
+ };
490
+ type GatewayAuthRequest = AuthenticateRequest & {
491
+ _meta?: GatewayAuthMeta;
492
+ };
493
+ /**
494
+ * Resolved, non-secret + secret routing config for the `main` provider. This is
495
+ * the shared shape produced by both `providers/set` and the legacy gateway auth
496
+ * path, and consumed by {@link createEnvForProvider}. `null` means the provider
497
+ * is unconfigured (no client-managed routing in effect).
498
+ */
499
+ type ProviderConfig = {
500
+ apiType: LlmProtocol;
501
+ baseUrl: string;
502
+ headers: Record<string, string>;
503
+ /** Present only for `apiType === "vertex"`. */
504
+ vertex?: {
505
+ projectId: string;
506
+ region: string;
507
+ };
508
+ };
509
+ /**
510
+ * Extra metadata that the agent provides for each tool_call / tool_update update.
511
+ */
512
+ export type ToolUpdateMeta = {
513
+ claudeCode?: {
514
+ toolName: string;
515
+ title?: string;
516
+ toolResponse?: unknown;
517
+ parentToolUseId?: string;
518
+ nonExecutionKind?: string;
519
+ userFeedback?: string;
520
+ subagent?: true;
521
+ };
522
+ terminal_info?: {
523
+ terminal_id: string;
524
+ };
525
+ terminal_output?: {
526
+ terminal_id: string;
527
+ data: string;
528
+ };
529
+ terminal_exit?: {
530
+ terminal_id: string;
531
+ exit_code: number;
532
+ signal: string | null;
533
+ };
534
+ };
535
+ export type ToolUseCache = {
536
+ [key: string]: {
537
+ type: "tool_use" | "server_tool_use" | "mcp_tool_use";
538
+ id: string;
539
+ name: string;
540
+ input: unknown;
541
+ };
542
+ };
543
+ type StreamedToolInput = {
544
+ id: string;
545
+ name: string;
546
+ partialJson: string;
547
+ /** Offset into `partialJson` the scanner has consumed; each delta only scans
548
+ * the newly appended fragment, so total scan work stays linear. */
549
+ scannedTo: number;
550
+ inString: boolean;
551
+ escaped: boolean;
552
+ objectDepth: number;
553
+ arrayDepth: number;
554
+ /** Offset of the most recent comma at the top level of the input object
555
+ * (-1 before the first). Everything before it is a complete field. */
556
+ lastTopLevelComma: number;
557
+ /** The comma offset the last emitted refinement was sliced at (-1 before the
558
+ * first), so a field boundary only triggers one recovery attempt. */
559
+ emittedThroughComma: number;
560
+ };
561
+ export type StreamedToolInputCache = Map<string, Map<number, StreamedToolInput>>;
562
+ export declare function claudeCliPath(): Promise<string>;
563
+ /**
564
+ * Return user-message content with local-command marker tags removed, or
565
+ * `null` if nothing meaningful remains (caller should skip the message).
566
+ * Preserves real prose that's mixed in alongside the markers — e.g. a
567
+ * message like `<command-name>…</command-name>hi` becomes `hi`.
568
+ */
569
+ export declare function stripLocalCommandMetadata(content: unknown): unknown | null;
570
+ export declare function isLocalCommandMetadata(content: unknown): boolean;
571
+ /**
572
+ * True for the synthetic assistant message the CLI injects into the transcript
573
+ * when a turn fails authentication (e.g. "Not logged in · Please run /login",
574
+ * "Session expired. Please run /login to sign in again."). The `/login`
575
+ * instruction is Claude Code TUI-specific and meaningless to ACP clients
576
+ * (issue #863). The live prompt loop suppresses the text and fails the turn
577
+ * with `authRequired` so the client can run its own auth flow; replay must
578
+ * skip it too — both for parity with what the client saw live and because the
579
+ * message stays in the transcript forever, so it would resurface on every
580
+ * session/load even after the user has logged back in.
581
+ *
582
+ * Takes the API message (`message.message`), which replay only knows as
583
+ * `unknown`. The persisted record's structured `error: "authentication_failed"`
584
+ * marker is stripped by `getSessionMessages`, so the synthetic model + text is
585
+ * all both paths have to match on.
586
+ */
587
+ export declare function isSyntheticLoginMessage(apiMessage: unknown): boolean;
588
+ export declare function resolvePermissionMode(defaultMode?: unknown, logger?: Logger): PermissionMode;
589
+ /**
590
+ * Builds the label for the "Always Allow" permission option so the user can see
591
+ * the exact scope they are committing to. Uses the SDK-provided suggestions
592
+ * when available (e.g. `Bash(npm test:*)`) and falls back to naming the whole
593
+ * tool so "Always Allow" is never a blank check without disclosure.
594
+ */
595
+ export declare function describeAlwaysAllow(suggestions: PermissionUpdate[] | undefined, toolName: string): string;
596
+ /**
597
+ * Client-facing surface the agent calls back into. This is the subset of ACP
598
+ * client methods the agent actually uses, expressed as a narrow interface so
599
+ * tests can supply lightweight mocks. In production it is backed by
600
+ * {@link ClientConnection} over the SDK's typed `AgentContext`.
601
+ */
602
+ export interface AcpClient {
603
+ sessionUpdate(params: SessionNotification): Promise<void>;
604
+ /** `signal`, when aborted, sends `$/cancel_request` for the in-flight
605
+ * permission request so the client can dismiss its prompt (and settle our
606
+ * await) instead of leaving the dialog open after the turn was cancelled. */
607
+ requestPermission(params: RequestPermissionRequest, signal?: AbortSignal): Promise<RequestPermissionResponse>;
608
+ readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse>;
609
+ writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
610
+ /** `signal`, when aborted, sends `$/cancel_request` for the in-flight
611
+ * elicitation so the client can dismiss its prompt and settle our await. */
612
+ unstable_createElicitation(params: CreateElicitationRequest, signal?: AbortSignal): Promise<CreateElicitationResponse>;
613
+ unstable_completeElicitation(params: CompleteElicitationNotification): Promise<void>;
614
+ /** Send a custom (extension) notification, e.g. `_claude/sdkMessage`. */
615
+ extNotification(method: string, params: Record<string, unknown>): Promise<void>;
616
+ }
617
+ export declare class ClaudeAcpAgent {
618
+ sessions: {
619
+ [key: string]: Session;
620
+ };
621
+ client: AcpClient;
622
+ clientCapabilities?: ClientCapabilities;
623
+ logger: Logger;
624
+ gatewayAuthRequest?: GatewayAuthRequest;
625
+ /** Client-managed LLM routing set via `providers/set`. Process-scoped and
626
+ * never persisted to disk (see the Configurable LLM Providers RFD). When
627
+ * set, it takes precedence over {@link gatewayAuthRequest}. */
628
+ providerConfig?: ProviderConfig;
629
+ /** Grace period before a `session/cancel` forces a wedged prompt loop to
630
+ * return "cancelled". See {@link DEFAULT_FORCE_CANCEL_GRACE_MS}. Mutable so
631
+ * tests can shrink it. */
632
+ forceCancelGraceMs: number;
633
+ constructor(client: AcpClient, logger?: Logger);
634
+ initialize(request: InitializeRequest): Promise<InitializeResponse>;
635
+ newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
636
+ unstable_forkSession(params: ForkSessionRequest): Promise<ForkSessionResponse>;
637
+ resumeSession(params: ResumeSessionRequest): Promise<ResumeSessionResponse>;
638
+ loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse>;
639
+ listSessions(params: ListSessionsRequest): Promise<ListSessionsResponse>;
640
+ /** Read the SDK-maintained title for a session and, if it changed since the
641
+ * last time we looked, notify the client with a `session_info_update`. The
642
+ * SDK has no push event for the title it auto-generates in the background, so
643
+ * we pull it at turn-end. A missing session file or read error is non-fatal:
644
+ * the title is best-effort and another turn will retry. */
645
+ private maybeUpdateSessionTitle;
646
+ authenticate(_params: AuthenticateRequest): Promise<void>;
647
+ /**
648
+ * `providers/list` — returns the single client-configurable custom gateway
649
+ * provider (`main`). `current` carries only non-secret routing (never headers,
650
+ * which may hold secrets); only `apiType`/`baseUrl` are surfaced for UI
651
+ * display, and is `null` when the provider is not configured/disabled. The
652
+ * provider is optional (`required: false`): while disabled/unconfigured the
653
+ * agent falls back to its own default routing (normal Claude login).
654
+ */
655
+ unstable_listProviders(_params: ListProvidersRequest): Promise<ListProvidersResponse>;
656
+ /**
657
+ * `providers/set` — replace the full configuration for the `main` provider.
658
+ * Rejects unknown IDs, unsupported protocols, and empty/invalid base URLs with
659
+ * `invalid_params`. Config is process-scoped and applies to sessions created or
660
+ * loaded after this call.
661
+ */
662
+ unstable_setProvider(params: SetProviderRequest): Promise<SetProviderResponse>;
663
+ /**
664
+ * `providers/disable` — disabling the `main` provider clears any client-managed
665
+ * routing (both a `providers/set` config and the legacy gateway auth request),
666
+ * so the agent reverts to its own default routing and `providers/list` reports
667
+ * `current: null`. Disabling any other (unknown) ID is treated as a successful
668
+ * no-op per the RFD's idempotency rule.
669
+ */
670
+ unstable_disableProvider(params: DisableProviderRequest): Promise<DisableProviderResponse>;
671
+ /**
672
+ * Resolve the effective client-managed routing config. `providers/set` takes
673
+ * precedence; otherwise fall back to the legacy gateway auth request. Returns
674
+ * `null` when neither is configured.
675
+ */
676
+ resolveProviderConfig(): ProviderConfig | null;
677
+ logout(_params: LogoutRequest): Promise<void>;
678
+ prompt(params: PromptRequest): Promise<PromptResponse>;
679
+ /** Steer the session per the ACP steering wire protocol: inject a follow-up
680
+ * message into the turn that is currently running. If that turn already
681
+ * settled, the established default starts a new detached turn; Hosts may opt
682
+ * into the host-owned `promptRequired` fallback through request `_meta`.
683
+ *
684
+ * When a turn is in flight this injects (returns `injected`): unlike
685
+ * `prompt()`, it does NOT create a Turn or enqueue on `turnQueue`; it pushes
686
+ * an `SDKUserMessage` onto the same streaming input, which the SDK routes
687
+ * into the in-flight turn. The injected message's echo carries a uuid that
688
+ * matches no queued turn, so the consumer drops it as an unrelated replay
689
+ * without promoting/settling anything. It is delivered at {@link
690
+ * STEER_PRIORITY} (`now`) so it pre-empts the current generation (interrupting
691
+ * a single-shot response, or slotting in between a multi-step turn's tool
692
+ * calls). The steered message's own output streams via `session/update`, not
693
+ * this response.
694
+ *
695
+ * When the session is idle, the opt-in path returns `promptRequired` WITHOUT
696
+ * calling `prompt()`, pushing SDK input, or mutating `turnQueue`: the content
697
+ * stays Host-owned so the Host can submit it through a standard
698
+ * `session/prompt`. Without the opt-in, the existing detached `prompt()` and
699
+ * `startedNewTurn` result are preserved for compatibility. */
700
+ steer(params: SteerRequest): Promise<SteerResponse>;
701
+ /** Lazily start the per-session consumer that drains the SDK query stream for
702
+ * the session's whole life. Idempotent: only the first `prompt()` starts it. */
703
+ private ensureConsumer;
704
+ /** The single, long-lived consumer of the SDK query stream for a session. It
705
+ * forwards every message as ACP `sessionUpdate`s (so background/between-turn
706
+ * output streams live, not just while a prompt is awaiting) and settles each
707
+ * Turn's deferred when that turn ends. Replaces the per-prompt message loop;
708
+ * `params` only carries the (session-invariant) `sessionId`. */
709
+ private runConsumer;
710
+ /** Route one orphaned command into the session's orphan-accounting lane:
711
+ * the per-uuid map on msg_lifecycle_v1 CLIs (drained by the command's own
712
+ * terminal lifecycle frame and the echo-less-result skip), the plain count
713
+ * elsewhere (the count lane can't express per-command states, so `state`
714
+ * only matters on the map lane). Both orphan-producing paths — cancel()'s
715
+ * queued-turn sweep and the consumer's force-cancel wedge path — must seed
716
+ * through here so the lane split stays a single mechanism.
717
+ *
718
+ * Known window: `msgLifecycleV1` is only learnable from the stream's first
719
+ * `system`/init (the control-channel initialize carries no capabilities),
720
+ * so a cancel that beats that drain seeds the COUNT lane on a
721
+ * lifecycle-capable CLI — where command coalescing can leave the count
722
+ * stale by N-1 (the pre-map bug, confined to this sub-second window and
723
+ * still healed by the next activation's reset). Structural until the SDK
724
+ * exposes capabilities before the stream starts. */
725
+ private trackOrphanCommand;
726
+ cancel(params: CancelNotification): Promise<void>;
727
+ /** Mark a session's SDK query stream as permanently ended and release the
728
+ * resources tied to it: drop the consumer handle, dispose the settings
729
+ * watchers, end the input stream, and close the query (which terminates the
730
+ * subprocess). The query iterator is not revivable, so `prompt()`/`cancel()`
731
+ * consult `queryClosed` and fail/short-circuit instead of acting on a dead
732
+ * stream. Idempotent (guarded by `queryClosed`), so the consumer's done/error
733
+ * paths and a later `teardownSession` can all call it without double-releasing.
734
+ *
735
+ * Deliberately does NOT abort `session.abortController`: that controller may be
736
+ * CLIENT-supplied (`_meta.claudeCode.options.abortController`) and reused, so
737
+ * aborting it on a spontaneous stream end would cancel the client's own work
738
+ * or make a sibling session born aborted. `query.close()` already terminates
739
+ * the subprocess; aborting the signal belongs in `teardownSession` (explicit
740
+ * destroy), not here. Also does NOT remove the session from the map — that is
741
+ * `teardownSession`'s job — so prompt() can still answer with a clear "session
742
+ * ended" error after an unexpected stream close. The leftover session object
743
+ * is a lightweight husk (its heavy resources are released here) and is evicted
744
+ * on the next closeSession/deleteSession or when the connection's `dispose()`
745
+ * runs. */
746
+ private closeQueryStream;
747
+ /** Cleanly tear down a session: cancel in-flight work, release stream
748
+ * resources, and remove it from the session map. */
749
+ private teardownSession;
750
+ /** Tear down all active sessions. Called when the ACP connection closes. */
751
+ dispose(): Promise<void>;
752
+ closeSession(params: CloseSessionRequest): Promise<CloseSessionResponse>;
753
+ deleteSession(params: DeleteSessionRequest): Promise<DeleteSessionResponse>;
754
+ setSessionMode(params: SetSessionModeRequest): Promise<SetSessionModeResponse>;
755
+ setSessionConfigOption(params: SetSessionConfigOptionRequest): Promise<SetSessionConfigOptionResponse>;
756
+ private applySessionMode;
757
+ private replaySessionHistory;
758
+ readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse>;
759
+ writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
760
+ /** Forward a permission request to the client, wiring the tool call's
761
+ * `signal` through as a `cancellationSignal`. When the turn is cancelled
762
+ * while the client's prompt is still open the signal aborts, the SDK sends
763
+ * `$/cancel_request`, and the client settles the request (a `cancelled`
764
+ * outcome or a `requestCancelled` rejection). Either way we surface the same
765
+ * "Tool use aborted" the callers already expect, so a cancelled dialog no
766
+ * longer leaves the `await` hanging. */
767
+ private requestPermissionFromClient;
768
+ /** Emit the `tool_call` a permission request references if it hasn't been sent
769
+ * yet, so the client has the tool call before being asked to approve it. The
770
+ * matching streamed tool_use chunk later refines it with a `tool_call_update`
771
+ * instead of emitting a duplicate (see `emittedToolCalls`). Built via the same
772
+ * `toolCallNotification` helper as the streamed path so the two are identical.
773
+ * Tools the stream renders as a plan (TodoWrite) or suppresses (Task*) are
774
+ * emitted too: a permission request referencing a tool call the client has
775
+ * never seen can trip strict clients (issue #851), so the reference must
776
+ * always resolve. Since the streamed path never completes those calls, they
777
+ * are resolved at tool_result time instead (see `toAcpNotifications`).
778
+ * `parentToolUseId` attributes a subagent's tool call to the Agent/Task call
779
+ * that spawned it, matching the streamed path's `_meta`. */
780
+ private ensureToolCallEmitted;
781
+ canUseTool(sessionId: string): CanUseTool;
782
+ /**
783
+ * Handle elicitation requests that originate from MCP servers by forwarding
784
+ * them to the client over ACP. Modes the client did not advertise (or
785
+ * requests we can't represent) are declined.
786
+ */
787
+ private handleMcpElicitation;
788
+ /**
789
+ * Present the built-in AskUserQuestion tool's questions as an ACP form
790
+ * elicitation and return the answers as the tool's `updatedInput`. Called from
791
+ * `canUseTool` since that is where the SDK routes the tool's permission check.
792
+ */
793
+ private handleAskUserQuestion;
794
+ /**
795
+ * Handle `request_user_dialog` control requests — blocking dialogs the CLI
796
+ * asks the host to render. Only kinds declared in `supportedDialogKinds`
797
+ * are ever emitted; everything unexpected is answered `cancelled` (the
798
+ * required answer for unrecognized kinds), which applies the dialog's
799
+ * default behavior CLI-side. Today the only declared kind is the
800
+ * refusal-fallback consent prompt, rendered as an ACP form elicitation.
801
+ */
802
+ private handleUserDialog;
803
+ private sendAvailableCommandsUpdate;
804
+ private updateConfigOption;
805
+ private applyConfigOptionValue;
806
+ /** Reconcile adapter model state after the SDK persistently swapped the
807
+ * session's model out from under us (refusal fallback). The SDK already
808
+ * made the switch, so this must NOT call `query.setModel` — it only
809
+ * updates our bookkeeping (currentModelId, context window, mode clamping,
810
+ * effort/Fast-mode options) via the same `applyConfigOptionValue` path a
811
+ * user-driven model change takes, then notifies the client. */
812
+ private syncModelAfterRefusalFallback;
813
+ /** Replace the Fast mode option in `session.configOptions` so it reflects
814
+ * `enabled` (and the client's current boolean-capability). A no-op when the
815
+ * option isn't present, so callers must confirm the current model surfaces
816
+ * it first. */
817
+ private refreshFastModeOption;
818
+ /** Toggle Fast mode for a session: push the SDK flag, record the user's
819
+ * intent, and refresh the Fast mode config option in place. Only reached
820
+ * once the option exists (i.e. the current model supports fast mode), so the
821
+ * option is guaranteed to be present in `configOptions`. */
822
+ private applyFastMode;
823
+ /** Reconcile the session's Fast mode toggle with an SDK-reported
824
+ * `fast_mode_state` (delivered on `system`/init and on user-turn `result`s).
825
+ * The SDK can flip fast mode independently of the user — e.g. back to `on`
826
+ * once a rate-limit `cooldown` clears — so we mirror definitive on/off
827
+ * changes into the config option and notify the client.
828
+ *
829
+ * Guards, in order:
830
+ * - absent state: nothing to reconcile.
831
+ * - no Fast mode option: the current model doesn't support fast mode, so the
832
+ * reported state reflects capability, not the user's intent. Leave the
833
+ * retained setting untouched so it's correct when a supporting model is
834
+ * reselected (the source of the earlier intent-clobber bug was mutating it
835
+ * here).
836
+ * - `cooldown`: a transient suspension of an already-enabled fast mode.
837
+ * Leave the toggle as-is rather than flapping it — and never let a stray
838
+ * cooldown spuriously enable a toggle the user has off.
839
+ *
840
+ * `reason` is the SDK's `fast_mode_disabled_reason`, reported alongside the
841
+ * state. Only explainable reasons are retained (see
842
+ * {@link normalizeFastModeDisabledReason}), so the comparison below tracks
843
+ * exactly what the user can see: a routine `sdk_opt_in_required` report on
844
+ * every turn's result can't churn the option, while a real blocker updates
845
+ * the description even when the toggle's own value is unchanged. */
846
+ private syncFastModeState;
847
+ private getOrCreateSession;
848
+ /**
849
+ * Ensures the requested `cwd` is an absolute path that points at an existing
850
+ * directory before we create a session. Throws an `invalidParams` error with
851
+ * an actionable message so clients (e.g. Zed) can surface it to the user
852
+ * instead of failing later with an opaque SDK error.
853
+ */
854
+ private validateCwd;
855
+ private createSession;
856
+ }
857
+ export declare const BUILTIN_AGENT_NAMES: Set<string>;
858
+ export declare const DEFAULT_AGENT_ID = "default";
859
+ /** Discover user/plugin/project-configured main-thread agents, excluding the
860
+ * built-in subagents and the reserved "default" sentinel. Returns an empty
861
+ * list if discovery fails so a flaky control request never blocks session
862
+ * creation. */
863
+ export declare function discoverCustomAgents(q: Query): Promise<AgentInfo[]>;
864
+ /** Stable ids for the session config options surfaced via `configOptions`.
865
+ * Centralized so the option declarations in `buildConfigOptions` and the
866
+ * handlers in `setSessionConfigOption`/`applyConfigOptionValue` reference the
867
+ * same identifiers and can't drift apart. */
868
+ export declare const MODE_CONFIG_ID = "mode";
869
+ export declare const MODEL_CONFIG_ID = "model";
870
+ export declare const EFFORT_CONFIG_ID = "effort";
871
+ export declare const AGENT_CONFIG_ID = "agent";
872
+ export declare const FAST_MODE_CONFIG_ID = "fast";
873
+ /** Select-fallback values used when the client has not opted into boolean
874
+ * config options (see {@link createFastModeConfigOption}). */
875
+ export declare const FAST_MODE_ON = "on";
876
+ export declare const FAST_MODE_OFF = "off";
877
+ /** Map the SDK's tri-state `fast_mode_state` onto the boolean config toggle.
878
+ * `cooldown` (fast mode temporarily suspended after a rate limit, per the SDK
879
+ * docs) keeps the toggle on so it reflects the user's intent — only an
880
+ * explicit `off` clears it. */
881
+ export declare function fastModeStateEnabled(state: FastModeState): boolean;
882
+ /** Normalize an SDK-reported `fast_mode_disabled_reason` to the one we retain:
883
+ * a reason we have an explanation for, else `undefined`. Keeping only
884
+ * explainable reasons means state comparisons (see `syncFastModeState`) track
885
+ * exactly what the user can see, so routine reports like
886
+ * `sdk_opt_in_required` never churn the config option. */
887
+ export declare function normalizeFastModeDisabledReason(reason: FastModeDisabledReason | undefined): FastModeDisabledReason | undefined;
888
+ /** Whether the Client advertised support for boolean session config options
889
+ * (`session.configOptions.boolean`). Agents MUST only send `type: "boolean"`
890
+ * config options to Clients that opt in; otherwise we fall back to a `select`.
891
+ * See https://agentclientprotocol.com/rfds/boolean-config-option. */
892
+ export declare function clientSupportsBooleanConfigOptions(clientCapabilities?: ClientCapabilities | null): boolean;
893
+ /** Build the Fast mode config option. When the Client supports boolean config
894
+ * options we expose a native `type: "boolean"` toggle; otherwise we degrade to
895
+ * a two-value `select` ("on"/"off") so older Clients still get a usable
896
+ * control.
897
+ *
898
+ * `disabledReason` (the SDK's `fast_mode_disabled_reason`) is folded into the
899
+ * description while the toggle reads off, so a user whose account or provider
900
+ * can't serve Fast mode sees why instead of a switch that silently refuses to
901
+ * stay on. Ignored while enabled: a reason reported alongside an `on`/`cooldown`
902
+ * state isn't blocking anything right now. */
903
+ export declare function createFastModeConfigOption(enabled: boolean, useBooleanOption: boolean, disabledReason?: FastModeDisabledReason): SessionConfigOption;
904
+ /** Resolve the requested Fast mode value from a `session/set_config_option`
905
+ * request. Accepts a native boolean (boolean-capable Clients) or the
906
+ * "on"/"off" select-fallback strings. */
907
+ export declare function resolveFastModeEnabled(params: SetSessionConfigOptionRequest): boolean;
908
+ /** Per-model Fast mode state threaded into {@link buildConfigOptions}. The
909
+ * option is only surfaced when the current model `supported`s fast mode. */
910
+ export type FastModeOptionState = {
911
+ supported: boolean;
912
+ enabled: boolean;
913
+ /** Whether the Client opted into boolean config options. */
914
+ useBooleanOption: boolean;
915
+ /** Latest explainable `fast_mode_disabled_reason`, folded into the option's
916
+ * description while the toggle reads off. */
917
+ disabledReason?: FastModeDisabledReason;
918
+ };
919
+ export declare function buildConfigOptions(modes: SessionModeState, models: SessionModelState, modelInfos: ModelInfo[], currentEffortLevel?: string, agents?: AgentInfo[], currentAgent?: string, fastMode?: FastModeOptionState): SessionConfigOption[];
920
+ export declare function resolveModelPreference(models: ModelInfo[], preference: string): ModelInfo | null;
921
+ /** Map the live model reported by a resumed session onto the picker's model
922
+ * list. The CLI restores a resumed session's model from the transcript's
923
+ * last assistant message, which records the concrete API id (e.g.
924
+ * "claude-opus-4-6") with any "[1m]" context hint dropped. Tiers, in order:
925
+ * 1. Exact match with the Default entry's resolution — when a named alias
926
+ * shares Default's resolvedModel verbatim, the live id can't tell the
927
+ * two apart, and a never-customized session should stay on Default.
928
+ * 2. Exact resolvedModel match on a named row. Checked before the
929
+ * hint-stripped Default comparison so a live "claude-sonnet-5[1m]" lands
930
+ * on the "sonnet[1m]" row rather than a Default that resolves to the
931
+ * bare "claude-sonnet-5" — the two rows differ in context window, which
932
+ * drives `contextWindowSize` and capability gating downstream.
933
+ * 3. Hint-stripped match with Default's resolution — a session that never
934
+ * left the default resumes as the bare transcript id, and shouldn't show
935
+ * a concrete picker entry.
936
+ * 4. `resolveModelPreference` over the picker entries.
937
+ * 5. A model with no picker counterpart (e.g. excluded by an
938
+ * `availableModels` allowlist) is tracked verbatim, mirroring
939
+ * `syncModelAfterRefusalFallback`: the picker shows no selection, but the
940
+ * model-dependent bookkeeping stays truthful to what the SDK is running. */
941
+ export declare function matchResumedModel(models: ModelInfo[], liveModel: string): ModelInfo;
942
+ /**
943
+ * Restrict the SDK's model list to the user's `availableModels` allowlist
944
+ * (already merged-and-deduped across settings sources by `SettingsManager`).
945
+ * The user's exact entries become the model IDs surfaced via configOptions
946
+ * and passed to `setModel`, which prevents Claude Code from silently
947
+ * substituting a date-pinned variant (e.g. `haiku` →
948
+ * `claude-haiku-4-5-20251001`) that the user may not have access to.
949
+ *
950
+ * Display info and capability flags are copied from the closest SDK match so
951
+ * the UI still renders sensible names and effort levels.
952
+ *
953
+ * Semantics from https://code.claude.com/docs/en/model-config#restrict-model-selection:
954
+ * - `undefined` is handled by the caller (no allowlist applied).
955
+ * - The Default option is unaffected by `availableModels` — it always remains
956
+ * available, even when the allowlist is `[]`.
957
+ */
958
+ export declare function applyAvailableModelsAllowlist(sdkModels: ModelInfo[], allowlist: string[], settingsModelOverrides?: Record<string, string>): ModelInfo[];
959
+ export declare function promptToClaude(prompt: PromptRequest): SDKUserMessage;
960
+ /**
961
+ * Resolves the ACP `messageId` for a Claude SDK message (live) or a persisted
962
+ * transcript message (replay) so chunk grouping is identical in both views.
963
+ *
964
+ * Assistant turns are keyed by the Anthropic API message id (`message.id`),
965
+ * which is identical at `message_start`, on the consolidated assistant message,
966
+ * and in the persisted transcript — unlike the per-`stream_event` uuid, which is
967
+ * unique per event and never persisted. User messages have no API id, but they
968
+ * are never streamed, so their (stable) SDK uuid is used instead. ACP message
969
+ * ids are opaque strings, so no particular format is required.
970
+ */
971
+ export declare function messageIdForGrouping(message: {
972
+ type?: string;
973
+ uuid?: string | null;
974
+ message?: unknown;
975
+ }): string | undefined;
976
+ /**
977
+ * Convert an SDKAssistantMessage (Claude) to a SessionNotification (ACP).
978
+ * Only handles text, image, and thinking chunks for now.
979
+ */
980
+ export declare function toAcpNotifications(content: string | ContentBlockParam[] | BetaContentBlock[] | BetaRawContentBlockDelta[], role: "assistant" | "user", sessionId: string, toolUseCache: ToolUseCache, client: AcpClient, logger: Logger, options?: {
981
+ registerHooks?: boolean;
982
+ clientCapabilities?: ClientCapabilities;
983
+ parentToolUseId?: string | null;
984
+ cwd?: string;
985
+ taskState?: TaskState;
986
+ emittedToolCalls?: Set<string>;
987
+ messageId?: string;
988
+ toolUseResult?: unknown;
989
+ toolResultMeta?: unknown;
990
+ }): SessionNotification[];
991
+ export declare function streamEventToAcpNotifications(message: SDKPartialAssistantMessage, sessionId: string, toolUseCache: ToolUseCache, client: AcpClient, logger: Logger, options?: {
992
+ clientCapabilities?: ClientCapabilities;
993
+ cwd?: string;
994
+ taskState?: TaskState;
995
+ emittedToolCalls?: Set<string>;
996
+ messageId?: string;
997
+ streamedToolInputs?: StreamedToolInputCache;
998
+ }): SessionNotification[];
999
+ /** Run a `session/prompt` while honoring `$/cancel_request` for it. ACP clients
1000
+ * normally stop a turn with the `session/cancel` notification, but `signal`
1001
+ * (the prompt request's abort signal) also fires when the client sends the
1002
+ * generic `$/cancel_request` for this prompt — the protocol's complementary
1003
+ * cancellation fallback. Route that to the same `agent.cancel` path so a client
1004
+ * using only the generic mechanism still stops the turn (and the prompt
1005
+ * resolves "cancelled" instead of running to completion).
1006
+ *
1007
+ * The listener is scoped to this call: once the prompt settles it is removed,
1008
+ * so a later teardown-time abort of the (per-request) signal can't cancel a
1009
+ * subsequent turn. `signal` also aborts on connection close, in which case
1010
+ * cancelling the in-flight turn is the desired behavior anyway. */
1011
+ export declare function runPromptWithCancellation(agent: Pick<ClaudeAcpAgent, "prompt" | "cancel" | "logger">, params: PromptRequest, signal: AbortSignal): Promise<PromptResponse>;
1012
+ export declare function runAcp(): {
1013
+ connection: import("@agentclientprotocol/sdk").AgentConnection;
1014
+ agent: ClaudeAcpAgent;
1015
+ };
1016
+ export {};
1017
+ //# sourceMappingURL=acp-agent.d.ts.map