@agentex/agent 0.0.23 → 0.0.26

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 (177) hide show
  1. package/CHANGELOG.md +338 -0
  2. package/LICENSE +21 -0
  3. package/README.md +110 -0
  4. package/dist/derived.d.ts +5 -3
  5. package/dist/derived.d.ts.map +1 -1
  6. package/dist/derived.js +11 -7
  7. package/dist/derived.js.map +1 -1
  8. package/dist/index.d.ts +7 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +4 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/providers/acp/index.d.ts +1 -1
  13. package/dist/providers/acp/index.d.ts.map +1 -1
  14. package/dist/providers/acp/index.js +5 -97
  15. package/dist/providers/acp/index.js.map +1 -1
  16. package/dist/providers/acp/session.d.ts +8 -1
  17. package/dist/providers/acp/session.d.ts.map +1 -1
  18. package/dist/providers/acp/session.js +94 -0
  19. package/dist/providers/acp/session.js.map +1 -1
  20. package/dist/providers/claude/attach.d.ts +8 -0
  21. package/dist/providers/claude/attach.d.ts.map +1 -0
  22. package/dist/providers/claude/attach.js +113 -0
  23. package/dist/providers/claude/attach.js.map +1 -0
  24. package/dist/providers/claude/execute.d.ts.map +1 -1
  25. package/dist/providers/claude/execute.js +17 -2
  26. package/dist/providers/claude/execute.js.map +1 -1
  27. package/dist/providers/claude/goal-capability.d.ts +15 -0
  28. package/dist/providers/claude/goal-capability.d.ts.map +1 -0
  29. package/dist/providers/claude/goal-capability.js +20 -0
  30. package/dist/providers/claude/goal-capability.js.map +1 -0
  31. package/dist/providers/claude/index.d.ts.map +1 -1
  32. package/dist/providers/claude/index.js +8 -4
  33. package/dist/providers/claude/index.js.map +1 -1
  34. package/dist/providers/claude/session.d.ts +11 -9
  35. package/dist/providers/claude/session.d.ts.map +1 -1
  36. package/dist/providers/claude/session.js +36 -14
  37. package/dist/providers/claude/session.js.map +1 -1
  38. package/dist/providers/codex/attach.d.ts +9 -0
  39. package/dist/providers/codex/attach.d.ts.map +1 -0
  40. package/dist/providers/codex/attach.js +93 -0
  41. package/dist/providers/codex/attach.js.map +1 -0
  42. package/dist/providers/codex/execute.d.ts.map +1 -1
  43. package/dist/providers/codex/execute.js +17 -3
  44. package/dist/providers/codex/execute.js.map +1 -1
  45. package/dist/providers/codex/goal-capability.d.ts +13 -0
  46. package/dist/providers/codex/goal-capability.d.ts.map +1 -0
  47. package/dist/providers/codex/goal-capability.js +18 -0
  48. package/dist/providers/codex/goal-capability.js.map +1 -0
  49. package/dist/providers/codex/index.d.ts +1 -0
  50. package/dist/providers/codex/index.d.ts.map +1 -1
  51. package/dist/providers/codex/index.js +9 -6
  52. package/dist/providers/codex/index.js.map +1 -1
  53. package/dist/providers/codex/session.d.ts +11 -7
  54. package/dist/providers/codex/session.d.ts.map +1 -1
  55. package/dist/providers/codex/session.js +37 -12
  56. package/dist/providers/codex/session.js.map +1 -1
  57. package/dist/providers/codex/transcript-normalize.d.ts +28 -0
  58. package/dist/providers/codex/transcript-normalize.d.ts.map +1 -0
  59. package/dist/providers/codex/transcript-normalize.js +191 -0
  60. package/dist/providers/codex/transcript-normalize.js.map +1 -0
  61. package/dist/providers/cursor/index.d.ts.map +1 -1
  62. package/dist/providers/cursor/index.js +2 -2
  63. package/dist/providers/cursor/index.js.map +1 -1
  64. package/dist/providers/openclaw/index.d.ts.map +1 -1
  65. package/dist/providers/openclaw/index.js +2 -2
  66. package/dist/providers/openclaw/index.js.map +1 -1
  67. package/dist/providers/opencode/index.d.ts.map +1 -1
  68. package/dist/providers/opencode/index.js +3 -5
  69. package/dist/providers/opencode/index.js.map +1 -1
  70. package/dist/providers/pi/index.d.ts.map +1 -1
  71. package/dist/providers/pi/index.js +3 -5
  72. package/dist/providers/pi/index.js.map +1 -1
  73. package/dist/providers/process/index.d.ts.map +1 -1
  74. package/dist/providers/process/index.js +2 -2
  75. package/dist/providers/process/index.js.map +1 -1
  76. package/dist/registry.d.ts +0 -1
  77. package/dist/registry.d.ts.map +1 -1
  78. package/dist/registry.js +0 -4
  79. package/dist/registry.js.map +1 -1
  80. package/dist/sessions/index.d.ts +3 -0
  81. package/dist/sessions/index.d.ts.map +1 -0
  82. package/dist/sessions/index.js +2 -0
  83. package/dist/sessions/index.js.map +1 -0
  84. package/dist/sessions/record.d.ts +43 -0
  85. package/dist/sessions/record.d.ts.map +1 -0
  86. package/dist/sessions/record.js +85 -0
  87. package/dist/sessions/record.js.map +1 -0
  88. package/dist/types.d.ts +176 -0
  89. package/dist/types.d.ts.map +1 -1
  90. package/dist/types.js.map +1 -1
  91. package/dist/utils/endpoint.d.ts +38 -0
  92. package/dist/utils/endpoint.d.ts.map +1 -0
  93. package/dist/utils/endpoint.js +151 -0
  94. package/dist/utils/endpoint.js.map +1 -0
  95. package/dist/utils/env.d.ts.map +1 -1
  96. package/dist/utils/env.js +5 -1
  97. package/dist/utils/env.js.map +1 -1
  98. package/dist/utils/uuid.d.ts +7 -1
  99. package/dist/utils/uuid.d.ts.map +1 -1
  100. package/dist/utils/uuid.js +21 -1
  101. package/dist/utils/uuid.js.map +1 -1
  102. package/package.json +64 -7
  103. package/src/derived.ts +311 -0
  104. package/src/goals/controller.ts +442 -0
  105. package/src/goals/index.ts +21 -0
  106. package/src/goals/normalize.ts +173 -0
  107. package/src/goals/sentinel.ts +90 -0
  108. package/src/index.ts +270 -0
  109. package/src/providers/_shared/http-agent.ts +304 -0
  110. package/src/providers/acp/index.ts +103 -0
  111. package/src/providers/acp/parse.ts +131 -0
  112. package/src/providers/acp/session.ts +744 -0
  113. package/src/providers/claude/attach.ts +147 -0
  114. package/src/providers/claude/codec.ts +43 -0
  115. package/src/providers/claude/execute.ts +300 -0
  116. package/src/providers/claude/goal-capability.ts +21 -0
  117. package/src/providers/claude/index.ts +72 -0
  118. package/src/providers/claude/mcp.ts +82 -0
  119. package/src/providers/claude/parse.ts +824 -0
  120. package/src/providers/claude/session.ts +1192 -0
  121. package/src/providers/claude/transcript.ts +555 -0
  122. package/src/providers/codex/attach.ts +123 -0
  123. package/src/providers/codex/codec.ts +50 -0
  124. package/src/providers/codex/execute.ts +337 -0
  125. package/src/providers/codex/goal-capability.ts +19 -0
  126. package/src/providers/codex/index.ts +57 -0
  127. package/src/providers/codex/modes.ts +159 -0
  128. package/src/providers/codex/parse.ts +691 -0
  129. package/src/providers/codex/plan-mode.ts +49 -0
  130. package/src/providers/codex/session.ts +1287 -0
  131. package/src/providers/codex/transcript-normalize.ts +197 -0
  132. package/src/providers/codex/transcript.ts +487 -0
  133. package/src/providers/codex/usage-scanner.ts +178 -0
  134. package/src/providers/copilot/index.ts +19 -0
  135. package/src/providers/cursor/codec.ts +44 -0
  136. package/src/providers/cursor/execute.ts +271 -0
  137. package/src/providers/cursor/index.ts +25 -0
  138. package/src/providers/cursor/parse.ts +288 -0
  139. package/src/providers/gemini/index.ts +21 -0
  140. package/src/providers/openclaw/codec.ts +40 -0
  141. package/src/providers/openclaw/execute.ts +19 -0
  142. package/src/providers/openclaw/index.ts +29 -0
  143. package/src/providers/opencode/codec.ts +50 -0
  144. package/src/providers/opencode/event-parse.ts +141 -0
  145. package/src/providers/opencode/execute.ts +251 -0
  146. package/src/providers/opencode/http-session.ts +427 -0
  147. package/src/providers/opencode/index.ts +30 -0
  148. package/src/providers/opencode/parse.ts +203 -0
  149. package/src/providers/opencode/server.ts +0 -0
  150. package/src/providers/pi/codec.ts +44 -0
  151. package/src/providers/pi/execute.ts +297 -0
  152. package/src/providers/pi/index.ts +30 -0
  153. package/src/providers/pi/parse.ts +231 -0
  154. package/src/providers/pi/session.ts +381 -0
  155. package/src/providers/process/execute.ts +148 -0
  156. package/src/providers/process/index.ts +52 -0
  157. package/src/registry.ts +40 -0
  158. package/src/sessions/index.ts +8 -0
  159. package/src/sessions/record.ts +108 -0
  160. package/src/types.ts +1638 -0
  161. package/src/utils/ask-user-question.ts +57 -0
  162. package/src/utils/auth.ts +661 -0
  163. package/src/utils/binary.ts +179 -0
  164. package/src/utils/endpoint.ts +172 -0
  165. package/src/utils/env.ts +63 -0
  166. package/src/utils/execute-all.ts +68 -0
  167. package/src/utils/exit-plan-mode.ts +40 -0
  168. package/src/utils/instructions.ts +427 -0
  169. package/src/utils/process.ts +223 -0
  170. package/src/utils/runtime-config.ts +100 -0
  171. package/src/utils/runtime-homes.ts +49 -0
  172. package/src/utils/skill-commands.ts +493 -0
  173. package/src/utils/skills.ts +500 -0
  174. package/src/utils/template.ts +16 -0
  175. package/src/utils/tool-names.ts +51 -0
  176. package/src/utils/uuid.ts +21 -0
  177. package/src/utils/workspace.ts +156 -0
package/src/types.ts ADDED
@@ -0,0 +1,1638 @@
1
+ /** Static declaration of what a provider supports. */
2
+ export interface ProviderCapabilities {
3
+ sessions: boolean;
4
+ modelDiscovery: boolean;
5
+ quotaProbing: boolean;
6
+ mcp: boolean;
7
+ skills: boolean;
8
+ skillInventory?: "provider-init" | "local-discovery" | "none";
9
+ skillInvocation?: "native-slash" | "expanded-prompt" | "configured-only" | "unsupported";
10
+ instructions: boolean;
11
+ workspace: boolean;
12
+ /**
13
+ * Read-only "plan" mode is honored by this provider. When `true`, the
14
+ * provider runs the agent so it can read and reason but cannot mutate.
15
+ *
16
+ * Mechanism differs per provider:
17
+ * - `claude`: `--permission-mode plan` — CLI-native plan UX. The agent
18
+ * emits its plan through the `ExitPlanMode` tool as a permission
19
+ * request; the host extracts it via `parseExitPlanMode(req)`.
20
+ * - `codex`: `--sandbox read-only` plus an injected planning system
21
+ * preamble. Codex *does* have a native plan mode (one of three
22
+ * collaboration modes — Plan, Pair, Execute — activated by `/plan` or
23
+ * Shift+Tab in the TUI), but `codex exec` exposes no flag to start in
24
+ * that mode and the JSON-RPC `collaboration_mode` parameter is per
25
+ * message, not startup. So the plan ends up in `ExecutionResult.summary`
26
+ * instead of streaming through `item/plan/delta` events. No in-protocol
27
+ * approval gate; the consumer drives the next step.
28
+ *
29
+ * Providers with this set to `false` ignore `config.planMode` entirely.
30
+ */
31
+ planMode: boolean;
32
+ /**
33
+ * Descriptive: the underlying CLI accepts user messages mid-turn. When
34
+ * `true`, callers may call `session.send()` while a previous turn is still
35
+ * in progress; the CLI's own queue handles ordering and either drains
36
+ * mid-turn (Claude injects as `<system-reminder>` attachments on the next
37
+ * tool-result batch) or coalesces queued items into the next turn.
38
+ *
39
+ * When `false`, calling `send()` while a turn is in progress throws.
40
+ *
41
+ * Apps may use this flag to gate "type while working" UI; it does not gate
42
+ * the API itself.
43
+ */
44
+ concurrentSend: boolean;
45
+ /**
46
+ * Descriptive: `session.cancel(uuid)` can remove queued (not-yet-processing)
47
+ * messages on this provider. When `false`, `cancel()` is still callable but
48
+ * always returns `{cancelled: false}`.
49
+ *
50
+ * Note that even when `true`, cancel is best-effort — once the CLI has
51
+ * dequeued a message for processing (mid-turn drain or new-turn dispatch),
52
+ * cancel returns `{cancelled: false}`.
53
+ */
54
+ cancelQueuedMessage: boolean;
55
+ /**
56
+ * Descriptive: `session.stopTask(taskId)` can stop a single in-flight
57
+ * background task (a backgrounded shell, a running async subagent) without
58
+ * disturbing the session or its other tasks. When `false`, `stopTask()` is
59
+ * still callable but always returns `{ stopped: false }`.
60
+ *
61
+ * Currently `true` only for the Claude provider, whose CLI exposes a
62
+ * `stop_task` control request the harness fulfills by killing the owning
63
+ * process — the model is not involved.
64
+ */
65
+ stopTask: boolean;
66
+ /**
67
+ * Provider exposes selectable operating modes via `listModes()` — e.g. Codex
68
+ * collaboration modes, Copilot's allow-all/agent/plan. When `false`,
69
+ * `config.modeId` is ignored and `listModes` is absent.
70
+ */
71
+ modes: boolean;
72
+ /**
73
+ * Goal support for this provider. Describes HOW a session-scoped goal
74
+ * (`AgentSession.setGoal`) is enforced, so hosts can branch on the
75
+ * enforcement model (an enforced goal can loop and burn budget; an advisory
76
+ * one can stall — different UI). Absent → the library's emulation engine is
77
+ * used whenever a host arms a goal. See `GoalState` / `GoalStatus`.
78
+ */
79
+ goals?: GoalCapability;
80
+ /**
81
+ * Capabilities are negotiated at runtime rather than statically known.
82
+ * `true` for ACP providers, whose real capability set comes from the agent's
83
+ * `initialize` handshake — the static flags here are a best-effort default
84
+ * until a session is created. Consumers needing exact capabilities for a
85
+ * dynamic provider should create a session and read its reported state.
86
+ */
87
+ dynamicCapabilities?: boolean;
88
+ /**
89
+ * Session identity survives the host process: `session.describe()` produces a
90
+ * `SessionRecord` and `provider.attachSession(record)` rebuilds read-only
91
+ * access to it (locate transcript, classify last turn, `catchUp`, `resume`).
92
+ * `true` for providers with a durable on-disk transcript contract (Claude,
93
+ * Codex); absent elsewhere. See internal-docs/spec-durable-sessions.md.
94
+ */
95
+ durableSessions?: boolean;
96
+ }
97
+
98
+ // ---------------------------------------------------------------------------
99
+ // Goals — session-scoped objectives normalized across providers.
100
+ //
101
+ // Two upstream mechanisms are reconciled here: Claude Code's Stop-hook +
102
+ // fast-model "sentinel" (harness-enforced, binary met/not-met) and Codex's
103
+ // durable thread-goal state mutated by model tools (advisory, multi-status).
104
+ // The library also EMULATES the enforced loop on providers with no native
105
+ // support, so `setGoal` works everywhere. See internal-docs/spec-goals.md.
106
+ // ---------------------------------------------------------------------------
107
+
108
+ /**
109
+ * Cross-provider goal status. Normalizes Claude's binary `met` flag and Codex's
110
+ * `active|paused|complete|budget-limited` thread status into one ladder.
111
+ *
112
+ * - "active" — armed, in progress. (Claude met:false; Codex active)
113
+ * - "paused" — retained, tracking suspended. (Codex paused; not native to
114
+ * Claude — reachable there only via the emulation engine.)
115
+ * - "met" — satisfied / complete. (Claude met:true; Codex complete)
116
+ * - "blocked" — cannot proceed. Absorbs Codex `budget-limited`, a user-input
117
+ * blocker, and the emulation engine's iteration cap. Carries a
118
+ * `blockedReason` so consumers can tell budget from stall.
119
+ * - "cleared" — aborted before completion. (Claude `/goal clear`; host clearGoal)
120
+ */
121
+ export type GoalStatus = "active" | "paused" | "met" | "blocked" | "cleared";
122
+
123
+ /** Why a `blocked` goal is blocked. */
124
+ export type GoalBlockedReason = "budget" | "needs_input" | "max_iterations";
125
+
126
+ /** Who last changed the goal state. */
127
+ export type GoalSource = "host" | "model" | "sentinel" | "agentex";
128
+
129
+ /**
130
+ * How a provider enforces goals. A structured descriptor (not a bare boolean)
131
+ * because hosts must branch on the enforcement model.
132
+ */
133
+ export interface GoalCapability {
134
+ /**
135
+ * - "sentinel" — native turn-end gate judged by a fast model (Claude).
136
+ * - "model-tools" — native durable state the model self-reports (Codex).
137
+ * - "emulated" — no native surface; the library drives the loop.
138
+ */
139
+ mechanism: "sentinel" | "model-tools" | "emulated";
140
+ /** Turn-end is gated until met. true for sentinel + emulated; false for model-tools. */
141
+ enforced: boolean;
142
+ /** Statuses this provider can actually report. */
143
+ statuses: GoalStatus[];
144
+ /** Clearing semantics: "self" (auto on met), "manual", or "both". */
145
+ clears: "self" | "manual" | "both";
146
+ /** Whether the provider reports tokensUsed/timeUsedSeconds on transitions. */
147
+ telemetry: boolean;
148
+ }
149
+
150
+ /** Live state of the session's active goal. */
151
+ export interface GoalState {
152
+ /** Normalized objective text (Claude `condition`; Codex `objective`). */
153
+ objective: string;
154
+ /** Normalized status. */
155
+ status: GoalStatus;
156
+ /** Convenience: status === "met". */
157
+ met: boolean;
158
+ /**
159
+ * How this goal is gated:
160
+ * - true — turn-end is gated until met (Claude sentinel, emulation engine).
161
+ * - false — advisory only; the model self-reports (Codex), or a record-only goal.
162
+ */
163
+ enforced: boolean;
164
+ /** Who last changed the state. */
165
+ source: GoalSource;
166
+ /** Why a `blocked` goal is blocked. Absent unless status === "blocked". */
167
+ blockedReason?: GoalBlockedReason;
168
+ /** Codex telemetry, when the provider reports it (reverse-engineered fields). */
169
+ tokensUsed?: number;
170
+ timeUsedSeconds?: number;
171
+ /** Codex soft budget when set via create_goal / `/goal --tokens`. */
172
+ tokenBudget?: number;
173
+ /** Continuation turns the emulation engine has driven. Absent for native goals. */
174
+ iterations?: number;
175
+ /** ISO timestamp of the last transition. */
176
+ updatedAt: string;
177
+ }
178
+
179
+ /** Per-call options for `AgentSession.setGoal`. */
180
+ export interface GoalOptions {
181
+ /**
182
+ * Enforcement strategy. Default: follow the provider's native mechanism.
183
+ * - "provider" — native if available, else emulate.
184
+ * - "emulate" — force the library engine even on Claude/Codex (uniform
185
+ * behavior across a heterogeneous fleet).
186
+ * - "advisory" — record the goal but never gate turn-end.
187
+ */
188
+ enforce?: "provider" | "emulate" | "advisory";
189
+ /**
190
+ * Sentinel for enforced/emulated goals — decides whether the objective is met
191
+ * after a turn ends. If omitted, the default sentinel is used. Providing a
192
+ * sentinel forces the emulation engine (the native Claude/Codex judges are
193
+ * not overridable). Return `true`/`{met:true}` to satisfy, or
194
+ * `{met:false, nudge}` to continue with an optional custom continuation.
195
+ */
196
+ sentinel?: GoalSentinel;
197
+ /**
198
+ * Max continuation turns the emulation engine drives before giving up and
199
+ * emitting status "blocked" (`blockedReason: "max_iterations"`). Guards
200
+ * against infinite loops. Default 12. Ignored for non-enforced goals.
201
+ */
202
+ maxIterations?: number;
203
+ }
204
+
205
+ /** Result of `AgentSession.setGoal`. */
206
+ export interface SetGoalResult {
207
+ /** True when the goal was armed. */
208
+ armed: boolean;
209
+ /** The mechanism actually used (may differ from the request after fallback). */
210
+ mechanism: "sentinel" | "model-tools" | "emulated";
211
+ }
212
+
213
+ /** Outcome of `AgentSession.clearGoal`. */
214
+ export interface ClearGoalResult {
215
+ /** True when an active goal was cleared; false when there was none. */
216
+ cleared: boolean;
217
+ }
218
+
219
+ /**
220
+ * A goal sentinel. Decides, after a turn settles, whether the objective is met.
221
+ * May call a model, run a command, inspect the transcript — anything. Return a
222
+ * bare boolean, or `{met, nudge?}` to supply a custom continuation message for
223
+ * the next turn when unmet.
224
+ */
225
+ export type GoalSentinel = (
226
+ ctx: GoalSentinelContext,
227
+ ) => boolean | GoalSentinelVerdict | Promise<boolean | GoalSentinelVerdict>;
228
+
229
+ export interface GoalSentinelVerdict {
230
+ met: boolean;
231
+ /** Custom continuation message when unmet. Falls back to a default nudge. */
232
+ nudge?: string;
233
+ }
234
+
235
+ export interface GoalSentinelContext {
236
+ objective: string;
237
+ /** The TurnResult that just settled. */
238
+ lastTurn: TurnResult;
239
+ /** Transcript path for the session, for sentinels that read history. Null when unknown. */
240
+ transcriptPath: string | null;
241
+ /** How many continuation turns have run so far against this goal. */
242
+ iterations: number;
243
+ }
244
+
245
+ /**
246
+ * A selectable operating mode a provider/session exposes. Discovered at
247
+ * runtime for dynamic providers (ACP), queried from the agent for codex.
248
+ */
249
+ export interface AgentMode {
250
+ /**
251
+ * Stable mode identifier. May be a full URI for ACP providers (e.g.
252
+ * "https://agentclientprotocol.com/protocol/session-modes#agent") — never
253
+ * assume it's a simple slug.
254
+ */
255
+ id: string;
256
+ /** Human-readable label for display. */
257
+ name: string;
258
+ /** Optional longer description of what the mode does. */
259
+ description?: string;
260
+ }
261
+
262
+ /** Options for `ProviderModule.listModes()`. */
263
+ export interface ListModesOptions {
264
+ cwd?: string;
265
+ env?: Record<string, string>;
266
+ config?: ProviderConfig;
267
+ }
268
+
269
+ // Core provider interface — every provider must implement this
270
+ export interface ProviderModule {
271
+ type: string;
272
+ capabilities: ProviderCapabilities;
273
+ execute(ctx: ExecutionContext): Promise<ExecutionResult>;
274
+ createSession?(ctx: SessionContext): Promise<AgentSession>;
275
+ /**
276
+ * Single source of truth for "is this provider usable?" Returns binary
277
+ * status, every supported auth path with present: boolean, and (when
278
+ * available) rich identity info (email, org, subscription tier).
279
+ *
280
+ * Prefers the CLI's own status subcommand (e.g. `claude auth status --json`,
281
+ * `codex login status`) for definitive truth, falling back to filesystem
282
+ * heuristics if the binary is missing or too old.
283
+ *
284
+ * Results are cached for 60s per provider+env; pass `{ fresh: true }` to
285
+ * bypass the cache.
286
+ */
287
+ resolveAuth(ctx?: AuthResolveContext): Promise<AuthReport>;
288
+ sessionCodec?: SessionCodec;
289
+ /** List available models. Pass cacheTtlMs to cache results (0 = no cache, default). */
290
+ listModels?(options?: { cacheTtlMs?: number }): Promise<ProviderModel[]>;
291
+ /**
292
+ * List the operating modes this provider exposes (see `AgentMode`). Present
293
+ * only on providers with `capabilities.modes === true`. May spawn the agent
294
+ * to query it (ACP, codex), so it's async and accepts cwd/env/config.
295
+ */
296
+ listModes?(options?: ListModesOptions): Promise<AgentMode[]>;
297
+ /** Check current quota/rate limit status. Not all providers support this. */
298
+ checkQuota?(ctx: QuotaContext): Promise<QuotaStatus>;
299
+ /**
300
+ * Polymorphic on-disk transcript access. Present only on providers that
301
+ * persist a durable JSONL transcript (currently Claude and Codex). Apps
302
+ * that know the provider at compile time can keep using the per-provider
303
+ * named helpers (e.g. `getClaudeTranscriptPath`); this field is for
304
+ * runtime-dispatched recovery flows.
305
+ */
306
+ transcript?: TranscriptOps<unknown>;
307
+ /**
308
+ * Rebuild read-only access to a durable session from a `SessionRecord` (as
309
+ * produced by `session.describe()` / `createSessionRecord`). Present only on
310
+ * providers with `capabilities.durableSessions === true` (Claude, Codex).
311
+ *
312
+ * Attach is read-only: it spawns nothing. It locates the on-disk transcript,
313
+ * classifies how the last turn ended (`lastTurn`), exposes `catchUp()` to
314
+ * replay normalized events with checkpointable offsets, and `resume()` to
315
+ * continue the session live — where `resume` is exactly
316
+ * `createSession({ ...ctx, sessionParams: record.params })` (one resume path,
317
+ * never auto-invoked). See internal-docs/spec-durable-sessions.md.
318
+ */
319
+ attachSession?(record: SessionRecord, opts?: AttachOptions): Promise<SessionAttachment>;
320
+ }
321
+
322
+ // Result of looking up a transcript for a given session.
323
+ export interface FoundTranscript {
324
+ /** Absolute path to the on-disk JSONL transcript. */
325
+ filePath: string;
326
+ /**
327
+ * The literal cwd recorded in the transcript, if recoverable. For Claude
328
+ * this comes from the on-disk envelope's `cwd` field; for Codex from the
329
+ * `session_meta` line or legacy `environment_context` user message.
330
+ * Null when the transcript carries no cwd metadata.
331
+ */
332
+ cwd: string | null;
333
+ }
334
+
335
+ // One unit pulled from a transcript by `read()`. The `event` type varies
336
+ // by provider (Claude: `StreamEvent`; Codex: `CodexTranscriptLine`); the
337
+ // envelope shape is identical so polymorphic callers can iterate uniformly.
338
+ export interface TranscriptYield<TEvent> {
339
+ event: TEvent;
340
+ /**
341
+ * Byte offset immediately after the trailing `\n` of the line this event
342
+ * came from. Pass back as `fromOffset` to resume from the next line.
343
+ */
344
+ offset: number;
345
+ }
346
+
347
+ // Result of `peek()`. Same shape across providers.
348
+ export interface TranscriptPeek<TEvent> {
349
+ lastEvent: TEvent | null;
350
+ size: number | null;
351
+ }
352
+
353
+ /**
354
+ * Polymorphic transcript access for a provider. Methods delegate to the
355
+ * provider's per-name helpers (e.g. `claudeProvider.transcript.find` calls
356
+ * `findClaudeTranscriptBySessionId` / `getClaudeTranscriptPath` under the
357
+ * hood). `TEvent` is the per-provider event shape and varies between
358
+ * implementations.
359
+ */
360
+ export interface TranscriptOps<TEvent> {
361
+ /**
362
+ * Locate the transcript file for a session.
363
+ *
364
+ * `cwd` is an optional hint: providers that key transcripts by cwd (Claude)
365
+ * use it for an O(1) direct lookup; providers that don't (Codex) ignore it.
366
+ * In all cases the returned `filePath` is verified to exist — a `null`
367
+ * return means no transcript was found for this session.
368
+ *
369
+ * The returned `cwd` is the literal cwd recorded inside the transcript,
370
+ * recovered when the file is opened. May be `null` if the transcript has
371
+ * no cwd metadata.
372
+ */
373
+ find(opts: { sessionId: string; cwd?: string }): Promise<FoundTranscript | null>;
374
+ /**
375
+ * Stream-read a transcript file, yielding parsed events with byte offsets.
376
+ * Behavior matches the underlying named function (skips wrapper lines,
377
+ * tolerates malformed JSON, resume-from-offset).
378
+ */
379
+ read(opts: {
380
+ filePath: string;
381
+ fromOffset?: number;
382
+ /** Defensive dedup for providers that expose stable per-event IDs (Claude). Ignored by others. */
383
+ sinceEventId?: string;
384
+ }): AsyncIterable<TranscriptYield<TEvent>>;
385
+ /** Cheap "what's the last event + total size?" probe — reads only the tail of the file. */
386
+ peek(filePath: string): Promise<TranscriptPeek<TEvent>>;
387
+ }
388
+
389
+ // ---------------------------------------------------------------------------
390
+ // Durable sessions — persist/reattach a session across a host restart.
391
+ //
392
+ // The agents underneath agentex are disk-durable and resumable (Claude:
393
+ // transcripts + `--resume`; Codex: SQLite threads + `thread/resume`). These
394
+ // types are the blessed composition of the existing primitives (`sessionCodec`,
395
+ // `transcript` ops, `ctx.sessionParams` resume) so a host persists ONE object
396
+ // (`SessionRecord`) and gets back to a session with `provider.attachSession`.
397
+ // See internal-docs/spec-durable-sessions.md.
398
+ // ---------------------------------------------------------------------------
399
+
400
+ /**
401
+ * Durable, JSON-serializable identity of a session — the one object a host
402
+ * persists to get back to a session after a restart. Produce with
403
+ * `session.describe()` or `createSessionRecord(...)`; consume with
404
+ * `provider.attachSession(record)`.
405
+ */
406
+ export interface SessionRecord {
407
+ version: 1;
408
+ /** Provider type (registry key) that owns this session. */
409
+ providerType: string;
410
+ /** Codec-serialized session params (e.g. `{sessionId, cwd?}`). */
411
+ params: Record<string, unknown>;
412
+ /** Working directory the session ran in — transcript-lookup hint. */
413
+ cwd: string | null;
414
+ /** Human-facing id (`sessionCodec.getDisplayId`), for UIs/logs. */
415
+ displayId: string | null;
416
+ /** ISO timestamp of when this record was produced/refreshed. */
417
+ updatedAt: string;
418
+ }
419
+
420
+ /** How the last *persisted* turn of an attached session ended. */
421
+ export type LastTurnStatus =
422
+ /** Terminal marker present (Claude `result`; Codex `task_complete`). */
423
+ | "completed"
424
+ /**
425
+ * Transcript ends without a terminal marker. Either the turn was cut off
426
+ * (host died mid-turn) OR another process is driving the session right
427
+ * now — attach cannot distinguish; hosts gate on their own running flag.
428
+ */
429
+ | "interrupted"
430
+ /** No transcript found, or it was empty/unreadable. */
431
+ | "unknown";
432
+
433
+ /** One replayed event from `SessionAttachment.catchUp()`. */
434
+ export interface CatchUpYield {
435
+ /** Normalized event (same vocabulary as live `onEvent`). */
436
+ event: StreamEvent;
437
+ /**
438
+ * Byte offset just past the transcript LINE this event came from — pass back
439
+ * as `fromOffset` to resume. NOTE: it is **line-granular**, not per-event: a
440
+ * single Claude line (e.g. an assistant message with a text block + a
441
+ * `tool_use` block) yields multiple events that all share this one offset. So
442
+ * an offset is a safe resume point only once EVERY event carrying it has been
443
+ * consumed — resuming from it re-opens the file at the *next* line. Checkpoint
444
+ * at line boundaries (persist an offset only after it advances vs the previous
445
+ * yield, or once the loop drains); persisting mid-line-group and crashing
446
+ * would skip that group's remaining events on resume. (Codex is currently 1:1
447
+ * here — each normalizer branch happens to return one event — so today this
448
+ * only bites Claude tool-call turns.)
449
+ */
450
+ offset: number;
451
+ /** Stable wire id for dedup vs live events (Claude); null when the provider has none (Codex). */
452
+ eventId: string | null;
453
+ }
454
+
455
+ export interface CatchUpOptions {
456
+ /** Resume reading from a previously checkpointed offset. */
457
+ fromOffset?: number;
458
+ /** Claude only: additionally skip events up to this wire id (defensive dedup). */
459
+ sinceEventId?: string;
460
+ }
461
+
462
+ export interface AttachOptions {
463
+ /** Env overlay for home-dir resolution (same vars the transcript helpers honor). */
464
+ env?: Record<string, string>;
465
+ }
466
+
467
+ /** Read-only reattachment to a session's durable state. See `attachSession`. */
468
+ export interface SessionAttachment {
469
+ /** The input record, normalized through the provider's `sessionCodec`. */
470
+ record: SessionRecord;
471
+ /** Located on-disk transcript, or null (then `lastTurn` is "unknown"). */
472
+ transcript: FoundTranscript | null;
473
+ lastTurn: LastTurnStatus;
474
+ /** Replay normalized events from the transcript. Never spawns anything. */
475
+ catchUp(opts?: CatchUpOptions): AsyncIterable<CatchUpYield>;
476
+ /**
477
+ * Continue the session live. Exactly equivalent to
478
+ * `provider.createSession({ ...ctx, sessionParams: record.params })` —
479
+ * same resume path, nothing new. Never called automatically.
480
+ * NOTE: pending user-input requests from before the restart are gone
481
+ * (the old process's stdio died); if `lastTurn === "interrupted"`,
482
+ * re-prompt or re-send as appropriate.
483
+ */
484
+ resume(ctx?: SessionContext): Promise<AgentSession>;
485
+ }
486
+
487
+ // Execution input
488
+ export interface ExecutionContext {
489
+ prompt: string;
490
+ model?: string;
491
+ runId?: string;
492
+ cwd?: string;
493
+ env?: Record<string, string>;
494
+ sessionParams?: Record<string, unknown> | null;
495
+ config?: ProviderConfig;
496
+ onOutput?: (stream: "stdout" | "stderr", chunk: string) => void | Promise<void>;
497
+ /**
498
+ * Called for every stream event. Handlers are awaited in event order — the
499
+ * next handler does not start until the previous one's returned promise
500
+ * settles. `execute()` resolves only after every handler for events up to
501
+ * and including the run's terminal `result` event has settled.
502
+ *
503
+ * A handler that throws is swallowed; the chain continues with the next
504
+ * event.
505
+ */
506
+ onEvent?: (event: StreamEvent) => void | Promise<void>;
507
+ onStart?: (pid: number) => void;
508
+ /** AbortSignal to cancel execution. When aborted, the process receives SIGTERM
509
+ * followed by SIGKILL after the grace period. */
510
+ signal?: AbortSignal;
511
+ /** Called at key execution lifecycle phases (preparing, spawning, running, etc.). */
512
+ onLifecycle?: (event: LifecycleEvent) => void;
513
+ }
514
+
515
+ /**
516
+ * Point a provider at a custom, Anthropic/OpenAI-compatible endpoint (BYOK,
517
+ * self-hosted gateway, or an alternative model). There is no shared wire
518
+ * format across CLIs, so this is TRANSLATED per provider at spawn time
519
+ * (`utils/endpoint.ts`):
520
+ * - claude: env vars — `ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`/
521
+ * `ANTHROPIC_API_KEY`, `ANTHROPIC_CUSTOM_HEADERS`, and `modelMap` →
522
+ * `ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU,FABLE}_MODEL`.
523
+ * - codex: a synthesized `[model_providers.custom]` block (base_url,
524
+ * `wire_api = "responses"`, env_key) via `-c` overrides, with the key injected
525
+ * into env. `modelMap` is ignored (Codex has no tier aliases — pass a concrete
526
+ * `model`). Codex removed the Chat Completions wire protocol in Feb 2026, so a
527
+ * custom endpoint must speak the OpenAI Responses API (directly or via a
528
+ * translating gateway such as LiteLLM).
529
+ * - other providers ignore it (documented per provider), like `allowedTools`.
530
+ *
531
+ * Credential hygiene (claude): when a custom `baseUrl` is set, only the auth
532
+ * declared here reaches it — an ambient `ANTHROPIC_API_KEY`/`ANTHROPIC_AUTH_TOKEN`
533
+ * from the host env is NOT forwarded to the third party (pass auth explicitly),
534
+ * and ambient alternate-routing config (Bedrock/Vertex/Foundry) is cleared so it
535
+ * can't steer Claude away from the endpoint. Codex header values are passed via
536
+ * env (`env_http_headers`), never argv, so secrets don't show up in `ps`.
537
+ *
538
+ * Applied once at spawn, so it is a per-session / per-`exec` property: change
539
+ * it by starting a fresh `createSession`/`exec` (resume re-applies it), never
540
+ * mid-session.
541
+ */
542
+ export interface ProviderEndpointConfig {
543
+ /** Base URL of the compatible endpoint. Required for codex. */
544
+ baseUrl?: string;
545
+ /** Bearer token — claude: `ANTHROPIC_AUTH_TOKEN` (`Authorization: Bearer`);
546
+ * codex: injected as the provider `env_key`. Wins over `apiKey` if both set. */
547
+ authToken?: string;
548
+ /** API key — claude: `ANTHROPIC_API_KEY` (`x-api-key`); codex: fallback
549
+ * provider `env_key` when `authToken` is absent. Set one of the two. */
550
+ apiKey?: string;
551
+ /** Extra headers on every request — claude: `ANTHROPIC_CUSTOM_HEADERS`;
552
+ * codex: `model_providers.custom.env_http_headers.*` (values passed via env,
553
+ * not argv, so secret headers don't leak to `ps`). */
554
+ headers?: Record<string, string>;
555
+ /** Tier alias → concrete endpoint model id (claude only:
556
+ * `ANTHROPIC_DEFAULT_*_MODEL`). Lets alias callers (`model: "sonnet"`)
557
+ * resolve on a non-Anthropic endpoint. Ignored by codex. */
558
+ modelMap?: {
559
+ opus?: string;
560
+ sonnet?: string;
561
+ haiku?: string;
562
+ fable?: string;
563
+ };
564
+ }
565
+
566
+ // Provider-specific configuration
567
+ export interface ProviderConfig {
568
+ command?: string;
569
+ model?: string;
570
+ effort?: string;
571
+ maxTurns?: number;
572
+ /**
573
+ * Hard runtime cap, in seconds.
574
+ * - `exec()`: kills the child process (SIGTERM → grace → SIGKILL) and reports
575
+ * `status: "timeout"`.
576
+ * - Sessions (`createSession`): acts as the per-session default timeout for
577
+ * `send()`. A per-call `SendOptions.timeoutSec` overrides it. On fire, the
578
+ * active turn is `interrupt()`ed and that send's `TurnResult` resolves with
579
+ * `status: "timeout"`. Unset means no timeout.
580
+ */
581
+ timeoutSec?: number;
582
+ /**
583
+ * Grace period, in seconds, between SIGTERM and SIGKILL when terminating the
584
+ * underlying process. Applies to both `exec()` and session `close()`/`drain()`.
585
+ * Defaults to 5. Bump it for workloads that legitimately need longer to clean
586
+ * up (long Bash, test suites) so they aren't hard-killed mid-flight.
587
+ */
588
+ graceSec?: number;
589
+ skipPermissions?: boolean;
590
+ skillDirs?: string[];
591
+ instructionsFile?: string;
592
+ mcpServers?: McpServerConfig[];
593
+ /**
594
+ * Only use MCP servers from `mcpServers` (claude: `--strict-mcp-config`),
595
+ * ignoring ambient configs (a stray `.mcp.json` in cwd, user-scope servers).
596
+ * Hosts embedding sessions should set this so the session's MCP surface is
597
+ * exactly what they attach. Works without `mcpServers` too — strict with no
598
+ * config blocks all ambient MCP.
599
+ */
600
+ strictMcpConfig?: boolean;
601
+ /**
602
+ * Tool names/patterns to pre-approve (claude: `--allowed-tools`). Patterns
603
+ * like `Bash(rm *)` and `mcp__server__*` pass through verbatim. Silently
604
+ * ignored by providers without argv tool filtering (codex — its mechanism is
605
+ * permission profiles).
606
+ */
607
+ allowedTools?: string[];
608
+ /**
609
+ * Tool names/patterns to deny (claude: `--disallowed-tools`). Deny wins over
610
+ * allow. Silently ignored by providers without argv tool filtering (codex).
611
+ */
612
+ disallowedTools?: string[];
613
+ /**
614
+ * Emit incremental assistant text as `assistant_delta` stream events
615
+ * (claude: `--include-partial-messages`). Purely additive — the consolidated
616
+ * `assistant` event still fires when the block completes, so consumers that
617
+ * ignore deltas see identical behavior. Off by default; providers without
618
+ * delta support ignore the flag.
619
+ */
620
+ includePartialMessages?: boolean;
621
+ extraArgs?: string[];
622
+ search?: boolean;
623
+ sandbox?: boolean;
624
+ thinking?: string;
625
+ /**
626
+ * Provider-specific mode pass-through. Currently used by `cursor` for its
627
+ * `--mode <mode>` flag. Don't use this for plan mode — set `planMode: true`
628
+ * instead, which is the cross-provider abstraction.
629
+ */
630
+ mode?: string;
631
+ /**
632
+ * Select a provider operating mode by id (one of `listModes()`). Honored by
633
+ * providers with `capabilities.modes === true` (codex collaboration modes,
634
+ * ACP session modes, copilot allow-all/agent/plan). Ignored otherwise.
635
+ * Distinct from `mode` (cursor's raw `--mode` passthrough) and `planMode`
636
+ * (the cross-provider read-only abstraction).
637
+ */
638
+ modeId?: string;
639
+ /**
640
+ * Run the agent in read-only "plan" mode: it can read, search, and reason,
641
+ * but cannot edit files or run mutating commands. Honored by providers
642
+ * with `capabilities.planMode === true` (claude, codex). Ignored by
643
+ * providers that don't support it — check `provider.capabilities.planMode`
644
+ * before relying on it.
645
+ *
646
+ * Mutually exclusive with `skipPermissions`. If both are set, `planMode`
647
+ * wins (more conservative intent) and `skipPermissions` is ignored.
648
+ *
649
+ * To resume a planned session in normal (executing) mode, pass the
650
+ * returned `sessionParams` on the next call with `planMode: false`.
651
+ */
652
+ planMode?: boolean;
653
+ /** Run the agent in an isolated workspace. The library creates a worktree
654
+ * before execution and uses it as the working directory. */
655
+ workspace?: {
656
+ strategy: "worktree";
657
+ baseBranch?: string;
658
+ branchName?: string;
659
+ };
660
+ /**
661
+ * Point the provider at a custom, Anthropic/OpenAI-compatible endpoint
662
+ * (BYOK / gateway / alternative model). Translated per provider at spawn —
663
+ * see {@link ProviderEndpointConfig}. Frozen for the process lifetime;
664
+ * providers without a custom-endpoint mechanism ignore it.
665
+ */
666
+ endpoint?: ProviderEndpointConfig;
667
+ }
668
+
669
+ // ---------------------------------------------------------------------------
670
+ // Quota probing
671
+ // ---------------------------------------------------------------------------
672
+
673
+ export interface QuotaStatus {
674
+ /** Whether the provider currently has available capacity */
675
+ available: boolean;
676
+ /** Remaining tokens in current window, if known */
677
+ remainingTokens?: number;
678
+ /** When the current rate limit window resets, if known */
679
+ resetAt?: string;
680
+ /** Billing type detected */
681
+ billingType: "api" | "subscription" | "metered_api";
682
+ /** Additional provider-specific info */
683
+ detail?: Record<string, unknown>;
684
+ }
685
+
686
+ export interface QuotaContext {
687
+ config?: ProviderConfig;
688
+ env?: Record<string, string>;
689
+ }
690
+
691
+ // ---------------------------------------------------------------------------
692
+ // Execution status & session state
693
+ // ---------------------------------------------------------------------------
694
+
695
+ /** Final outcome of a single-turn execution. */
696
+ export type ExecutionStatus =
697
+ | "completed" // success
698
+ | "failed" // agent or execution error
699
+ | "aborted" // cancelled via AbortSignal
700
+ | "timeout" // exceeded time limit
701
+ | "blocked"; // agent reported a blocker it can't resolve
702
+
703
+ /** Live state of an interactive session. */
704
+ export type SessionState =
705
+ | "idle" // session created, no turn in progress
706
+ | "thinking" // agent is generating/reasoning
707
+ | "tool_executing" // agent is running a tool
708
+ | "waiting_for_approval" // blocked on tool permission request
709
+ | "waiting_for_input" // blocked on user input (AskUserQuestion, elicitation)
710
+ | "closed"; // session ended
711
+
712
+ // ---------------------------------------------------------------------------
713
+ // Token usage
714
+ // ---------------------------------------------------------------------------
715
+
716
+ /**
717
+ * Token usage for a single model within a run.
718
+ *
719
+ * `cachedInputTokens` normalizes across providers:
720
+ * - Claude: `cache_read_input_tokens`
721
+ * - Codex: `cached_input_tokens`
722
+ *
723
+ * `cacheCreationInputTokens` is Claude-specific (`cache_creation_input_tokens`).
724
+ */
725
+ export interface TokenUsage {
726
+ inputTokens: number;
727
+ outputTokens: number;
728
+ cachedInputTokens?: number;
729
+ cacheCreationInputTokens?: number;
730
+ }
731
+
732
+ /**
733
+ * Per-model usage with optional cost + rate-limit-adjacent extras.
734
+ * Claude populates most fields via its `modelUsage` result payload;
735
+ * other providers populate only `TokenUsage` fields and leave the rest
736
+ * undefined.
737
+ */
738
+ export interface ModelUsage extends TokenUsage {
739
+ costUsd?: number;
740
+ webSearchRequests?: number;
741
+ contextWindow?: number;
742
+ maxOutputTokens?: number;
743
+ }
744
+
745
+ /**
746
+ * Get aggregate usage across all models. Convenience for when you don't
747
+ * care about per-model breakdown.
748
+ */
749
+ export function aggregateUsage(usage: Record<string, TokenUsage> | undefined): TokenUsage | null {
750
+ if (!usage) return null;
751
+ const entries = Object.values(usage);
752
+ if (entries.length === 0) return null;
753
+ const result: TokenUsage = { inputTokens: 0, outputTokens: 0 };
754
+ for (const u of entries) {
755
+ result.inputTokens += u.inputTokens;
756
+ result.outputTokens += u.outputTokens;
757
+ if (u.cachedInputTokens != null) {
758
+ result.cachedInputTokens = (result.cachedInputTokens ?? 0) + u.cachedInputTokens;
759
+ }
760
+ if (u.cacheCreationInputTokens != null) {
761
+ result.cacheCreationInputTokens = (result.cacheCreationInputTokens ?? 0) + u.cacheCreationInputTokens;
762
+ }
763
+ }
764
+ return result;
765
+ }
766
+
767
+ /**
768
+ * Rate-limit signal reported by a provider (currently Claude's
769
+ * `rate_limit_event`). Surfaced both as a StreamEvent and aggregated onto
770
+ * `ExecutionResult.rateLimits` for consumers that want quota state.
771
+ */
772
+ export interface RateLimitInfo {
773
+ /** Provider-reported status, e.g. "allowed" | "rejected". */
774
+ status: string;
775
+ /** Kind of limit, e.g. Claude's "five_hour" / "weekly". */
776
+ limitType: string | null;
777
+ /** ISO timestamp when the limit window resets, when known. */
778
+ resetAt: string | null;
779
+ /** Provider-reported overage state (Claude: "allowed" / null). */
780
+ overageStatus: string | null;
781
+ /** Whether the current run is consuming from overage capacity. */
782
+ isUsingOverage: boolean | null;
783
+ }
784
+
785
+ // Execution output
786
+ export interface ExecutionResult {
787
+ runId: string;
788
+ exitCode: number | null;
789
+ signal: string | null;
790
+ status: ExecutionStatus;
791
+ startedAt: string;
792
+ completedAt: string;
793
+ durationMs: number;
794
+ errorMessage: string | null;
795
+ errorCode: string | null;
796
+ usage?: Record<string, ModelUsage>;
797
+ costUsd: number | null;
798
+ model: string | null;
799
+ summary: string | null;
800
+ sessionParams: Record<string, unknown> | null;
801
+ sessionDisplayId: string | null;
802
+ clearSession: boolean;
803
+ billingType: "api" | "subscription" | "metered_api" | null;
804
+
805
+ // ---- Provider-reported run metadata (populated when the provider reports it) ----
806
+ /** Why the model stopped (e.g. "end_turn", "max_turns", "tool_use"). Claude only. */
807
+ stopReason?: string | null;
808
+ /** CLI's own terminal reason (Claude: "completed" | "error" | ...). Claude only. */
809
+ terminalReason?: string | null;
810
+ /** Total turns executed, when the provider reports it. Claude only. */
811
+ numTurns?: number | null;
812
+ /** Time spent in model API calls, separate from wall-clock `durationMs`. Claude only. */
813
+ durationApiMs?: number | null;
814
+ /** Claude's `permission_denials` array, verbatim. */
815
+ permissionDenials?: unknown[];
816
+ /** Every rate-limit signal observed during the run. Claude only. */
817
+ rateLimits?: RateLimitInfo[];
818
+
819
+ /**
820
+ * True escape hatch. Holds the final provider-native event object
821
+ * verbatim — Claude's `result` event, or Codex's `turn.completed` /
822
+ * `turn.failed` / `error`. Use for anything we haven't normalized.
823
+ */
824
+ raw?: Record<string, unknown> | null;
825
+ /** If the run used a workspace, this contains the workspace handle for diffing/cleanup */
826
+ workspace?: import("./utils/workspace.js").PreparedWorkspace;
827
+ }
828
+
829
+ /**
830
+ * Fields present on every `StreamEvent`. Populated per-provider:
831
+ *
832
+ * | Field | Claude | Codex |
833
+ * |-------------------|-----------------------------------|--------------------------------|
834
+ * | sessionId | `session_id` | `thread_id` |
835
+ * | eventId | top-level `uuid` | null (CLI doesn't emit) |
836
+ * | messageId | `message.id` (`msg_*`) | `item.id` — **turn-local**, resets per turn, not globally unique |
837
+ * | parentToolCallId | `parent_tool_use_id` | null |
838
+ * | providerType | "claude" | "codex" |
839
+ * | raw | original event object verbatim | original event object verbatim |
840
+ *
841
+ * Other providers (cursor, gemini, opencode, pi, openclaw) currently emit
842
+ * stubs (null IDs, partial raw). Enriching them is tracked in
843
+ * `internal-docs/stream-event-enrichment.md`.
844
+ */
845
+ export interface BaseStreamEventFields {
846
+ timestamp: string;
847
+ /** Which provider emitted this event. */
848
+ providerType: string;
849
+ /** Stable session/thread ID across turns; null when not yet known. */
850
+ sessionId: string | null;
851
+ /**
852
+ * Provider-native message ID.
853
+ * - Claude: Anthropic API message ID like `msg_01...`.
854
+ * - Codex: `item_N` where N resets per turn — NOT globally unique.
855
+ * Combine with (sessionId, turn index) if you need a stable key.
856
+ */
857
+ messageId: string | null;
858
+ /**
859
+ * Unique ID for this specific event line. Claude emits a top-level
860
+ * `uuid` on every line; Codex doesn't, so this is null for Codex.
861
+ */
862
+ eventId: string | null;
863
+ /**
864
+ * Native turn identifier for providers that emit one. Turn-scoped
865
+ * events (items, deltas, token usage, turn completion) share this value.
866
+ *
867
+ * - Codex v2 JSON-RPC app-server: native UUIDv7 from `params.turnId`
868
+ * (or `params.turn.id` on turn/started + turn/completed notifications).
869
+ * - Codex legacy NDJSON (`codex exec --json`): null. Legacy emits bare
870
+ * `{"type":"turn.started"}` with no turn id.
871
+ * - Claude: null. `messageId` (`msg_*`) is globally unique so turn
872
+ * scope isn't needed to disambiguate rows.
873
+ *
874
+ * For Codex v2, `(sessionId, turnId, messageId)` is a stable composite
875
+ * key. For legacy Codex, `messageId` (`item_N`) is turn-local and will
876
+ * collide across turns — scope by event insertion order instead.
877
+ */
878
+ turnId: string | null;
879
+ /**
880
+ * Lineage: the `toolCallId` of the ancestor Task tool_call that spawned
881
+ * this sub-agent. Same ID namespace as `tool_call.toolCallId`. Null
882
+ * when the event isn't inside a sub-agent. Claude only.
883
+ */
884
+ parentToolCallId: string | null;
885
+ /** Original provider event object verbatim, for fields we don't normalize. */
886
+ raw: Record<string, unknown>;
887
+ }
888
+
889
+ /**
890
+ * Categorical reason for an `auth_required` event. Derived from the
891
+ * provider's user-facing error text. Stable across providers — new
892
+ * provider integrations should map their auth strings into this set
893
+ * rather than introducing per-provider variants.
894
+ *
895
+ * Mappings for Claude (from https://code.claude.com/docs/en/errors):
896
+ * - `expired` — `OAuth token has expired · Please run /login`
897
+ * - `revoked` — `OAuth token revoked · Please run /login`
898
+ * - `missing` — `Not logged in · Please run /login`
899
+ * - `invalid` — `Invalid API key · Fix external API key`, `Failed to
900
+ * authenticate. API Error: 401 Invalid bearer token`, Bedrock 403
901
+ * "security token included in the request is invalid"
902
+ * - `scope` — `OAuth token does not meet scope requirement: <scope>`
903
+ * - `disabled_org` — `Your ANTHROPIC_API_KEY belongs to a disabled
904
+ * organization · ...`
905
+ * - `routines_disabled` — `Routines are disabled by your organization's
906
+ * policy.`
907
+ * - `unknown` — anything we couldn't classify (still emitted, but the
908
+ * consumer can't branch on a specific recovery path).
909
+ */
910
+ export type AuthRequiredReason =
911
+ | "expired"
912
+ | "revoked"
913
+ | "missing"
914
+ | "invalid"
915
+ | "scope"
916
+ | "disabled_org"
917
+ | "routines_disabled"
918
+ | "unknown";
919
+
920
+ /**
921
+ * Normalized streaming events, discriminated on `type`.
922
+ *
923
+ * Union-growth policy: this union grows in minor versions as new provider
924
+ * events are modeled (the `goal_status` variant is the precedent). Consumers
925
+ * MUST keep a `default` branch when switching on `type` — an unmodeled wire
926
+ * event surfaces as `type: "unknown"` today, and a future first-class variant
927
+ * you don't yet handle must not break your dispatch.
928
+ */
929
+ // Stream events — discriminated union
930
+ export type StreamEvent =
931
+ | ({
932
+ type: "system";
933
+ subtype: string;
934
+ model: string | null;
935
+ cwd: string | null;
936
+ tools: string[] | null;
937
+ permissionMode: string | null;
938
+ slashCommands?: string[];
939
+ skills?: string[];
940
+ } & BaseStreamEventFields)
941
+ | ({ type: "assistant"; text: string } & BaseStreamEventFields)
942
+ | ({
943
+ /**
944
+ * Incremental assistant text (typewriter). Only emitted when
945
+ * `config.includePartialMessages` is set, and purely additive: the
946
+ * consolidated `assistant` event still fires when the block completes,
947
+ * with `messageId` matching these deltas so hosts can reconcile
948
+ * optimistic delta text against the durable event.
949
+ */
950
+ type: "assistant_delta";
951
+ /** Incremental text chunk — append-only within (messageId, blockIndex). */
952
+ text: string;
953
+ /** Content block index within the message, for multi-block replies. */
954
+ blockIndex: number;
955
+ } & BaseStreamEventFields)
956
+ | ({
957
+ /**
958
+ * Incremental thinking text (best-effort, same `includePartialMessages`
959
+ * flag). NOTE: on recent Claude versions the consolidated `thinking`
960
+ * block is withheld (signature-only), so these deltas can be the ONLY
961
+ * place thinking prose appears. Don't depend on them being present or
962
+ * complete — treat as advisory UI sugar.
963
+ */
964
+ type: "thinking_delta";
965
+ text: string;
966
+ /** Content block index within the message. */
967
+ blockIndex: number;
968
+ } & BaseStreamEventFields)
969
+ | ({ type: "thinking"; text: string } & BaseStreamEventFields)
970
+ | ({
971
+ type: "tool_call";
972
+ /** This tool invocation's own ID. Matched later by tool_result.toolCallId. */
973
+ toolCallId: string | null;
974
+ name: string;
975
+ input: unknown;
976
+ } & BaseStreamEventFields)
977
+ | ({
978
+ type: "tool_result";
979
+ /** FK back to the tool_call.toolCallId this responds to. */
980
+ toolCallId: string | null;
981
+ /**
982
+ * Name of the tool whose result this is — mirrors the matching
983
+ * `tool_call.name`. Saves consumers from maintaining their own
984
+ * `toolCallId → name` cache to attribute a result to a named action.
985
+ * Null when the name couldn't be correlated (no preceding `tool_call`
986
+ * was observed on this stream — e.g. onEvent attached mid-turn, or a
987
+ * provider that emits a result with no paired call).
988
+ */
989
+ toolName: string | null;
990
+ content: string;
991
+ isError: boolean;
992
+ /** Exit code for command-execution tools (Codex); null otherwise. */
993
+ exitCode: number | null;
994
+ } & BaseStreamEventFields)
995
+ | ({
996
+ type: "rate_limit";
997
+ status: string;
998
+ limitType: string | null;
999
+ resetAt: string | null;
1000
+ overageStatus: string | null;
1001
+ isUsingOverage: boolean | null;
1002
+ } & BaseStreamEventFields)
1003
+ /**
1004
+ * Emitted when the provider's API rejected the request because the user
1005
+ * is not authenticated. Distinct from `rate_limit` and from generic
1006
+ * `result.isError` outcomes. Consumers should surface a login button or
1007
+ * banner; the running session is unrecoverable until the user re-auths
1008
+ * and (typically) the session handle is recycled.
1009
+ *
1010
+ * Driven by structured wire fields (Claude: `api_error_status` 401/403 on
1011
+ * the `result` event and `error: "authentication_failed"` on the
1012
+ * synthetic-assistant message). Falls back to text-match against the
1013
+ * documented user-facing strings (see https://code.claude.com/docs/en/errors)
1014
+ * for cases where the CLI short-circuits before any HTTP round-trip
1015
+ * (`Not logged in · Please run /login`).
1016
+ */
1017
+ | ({
1018
+ type: "auth_required";
1019
+ /**
1020
+ * Upstream HTTP status that triggered this. 401 or 403 when the CLI
1021
+ * actually reached the API; null when the CLI short-circuited (e.g.
1022
+ * `Not logged in` before any network call) or when derived from a
1023
+ * text-only signal.
1024
+ */
1025
+ httpStatus: number | null;
1026
+ /** Categorical reason. Stable across providers. */
1027
+ reason: AuthRequiredReason;
1028
+ /** Provider-recommended recovery command, e.g. `claude auth login`. */
1029
+ loginCommand: string;
1030
+ /** Provider's human-readable message for display. Not for branching. */
1031
+ message: string | null;
1032
+ } & BaseStreamEventFields)
1033
+ /**
1034
+ * Permission mode change. Claude emits a top-level `permission-mode` event
1035
+ * when the agent transitions between modes (e.g., user accepts a plan and
1036
+ * the session leaves `plan` mode). The `system.init` event also reports
1037
+ * the initial `permissionMode`; this variant reports subsequent transitions.
1038
+ *
1039
+ * Claude's known values: `"default"`, `"plan"`, `"acceptEdits"`,
1040
+ * `"bypassPermissions"`. Kept as `string` for forward compat.
1041
+ */
1042
+ | ({
1043
+ type: "permission_mode";
1044
+ permissionMode: string;
1045
+ } & BaseStreamEventFields)
1046
+ /**
1047
+ * Goal lifecycle transition — emitted when a session goal is set, judged,
1048
+ * blocked, or cleared. Normalized across providers; `raw` holds the
1049
+ * provider-native record (Claude `goal_status` attachment / Codex
1050
+ * `thread_goal_updated` payload / an emulation-engine synthetic).
1051
+ *
1052
+ * One emitter per mode, so there is no intra-stream double-emit: in native
1053
+ * mode the provider's parser is the sole emitter; in emulation mode the
1054
+ * library's `GoalController` is. Codex's goal *tool* calls
1055
+ * (`get_goal`/`create_goal`/`update_goal`) deliberately surface as ordinary
1056
+ * `tool_call`/`tool_result` events, NOT as `goal_status` (see
1057
+ * `CODEX_GOAL_TOOLS`). The library does not dedup the same transition seen on
1058
+ * two different transports (e.g. a live stream and the on-disk transcript) —
1059
+ * that's a host concern, keyed off `eventId`.
1060
+ */
1061
+ | ({
1062
+ type: "goal_status";
1063
+ objective: string;
1064
+ status: GoalStatus;
1065
+ met: boolean;
1066
+ enforced: boolean;
1067
+ source: GoalSource;
1068
+ blockedReason?: GoalBlockedReason;
1069
+ tokensUsed?: number;
1070
+ timeUsedSeconds?: number;
1071
+ tokenBudget?: number;
1072
+ iterations?: number;
1073
+ } & BaseStreamEventFields)
1074
+ | ({
1075
+ type: "result";
1076
+ text: string;
1077
+ costUsd: number | null;
1078
+ isError: boolean;
1079
+ stopReason: string | null;
1080
+ terminalReason: string | null;
1081
+ numTurns: number | null;
1082
+ durationMs: number | null;
1083
+ } & BaseStreamEventFields)
1084
+ /**
1085
+ * Emitted when the parser sees a wire event type it doesn't have a
1086
+ * first-class variant for. Gives consumers forward-compat access to
1087
+ * new provider events via `raw` without requiring a library update.
1088
+ * `subtype` carries the provider's outer `type` field value.
1089
+ *
1090
+ * Provider-native discriminators and payloads remain in `raw` — the
1091
+ * event shape is intentionally minimal here because we do not model
1092
+ * these events. Known locations:
1093
+ * - Claude: `raw.subtype` (inner discriminator, e.g. `away_summary`,
1094
+ * `compact_boundary`, `turn_duration`), `raw.content` (payload text
1095
+ * when present).
1096
+ * - Codex: `raw.method` (JSON-RPC method), or nested `raw.item.type`
1097
+ * for `item/completed` events whose item type is unrecognized.
1098
+ * - Cursor / Gemini / OpenCode / Pi: `raw.type` mirrors `subtype`;
1099
+ * payload fields vary per wire event.
1100
+ *
1101
+ * For Claude specifically, see `getClaudeUnknownDetails` in
1102
+ * `providers/claude/parse.ts` for an ergonomic accessor.
1103
+ */
1104
+ | ({
1105
+ type: "unknown";
1106
+ subtype: string;
1107
+ } & BaseStreamEventFields);
1108
+
1109
+ // Lifecycle events — execution phase tracking
1110
+ export type LifecycleEvent =
1111
+ | { phase: "preparing"; step: "workspace" | "skills" | "auth" | "instructions" | "binary" }
1112
+ | { phase: "spawning" }
1113
+ | { phase: "running"; pid: number }
1114
+ | { phase: "waiting_for_input"; request: UserInputRequest }
1115
+ | { phase: "completed" }
1116
+ | { phase: "cancelled" }
1117
+ | { phase: "error"; message: string };
1118
+
1119
+ // Session persistence
1120
+ export interface SessionCodec {
1121
+ deserialize(raw: unknown): Record<string, unknown> | null;
1122
+ serialize(params: Record<string, unknown> | null): Record<string, unknown> | null;
1123
+ getDisplayId?(params: Record<string, unknown> | null): string | null;
1124
+ }
1125
+
1126
+ // ---------------------------------------------------------------------------
1127
+ // Auth reporting
1128
+ // ---------------------------------------------------------------------------
1129
+
1130
+ /** How a provider is authenticated. Determines billing behavior at runtime. */
1131
+ export type AuthMethod = "api_key" | "bedrock" | "subscription";
1132
+
1133
+ /** Where an auth credential lives. */
1134
+ export type AuthSource =
1135
+ /** Single environment variable, e.g. OPENAI_API_KEY. */
1136
+ | { kind: "env"; var: string }
1137
+ /** Multiple env vars that together form one credential, e.g. AWS creds. */
1138
+ | { kind: "env_combo"; vars: string[] }
1139
+ /** A file on disk, e.g. ~/.codex/auth.json. Path is already resolved. */
1140
+ | { kind: "file"; path: string }
1141
+ /** macOS keychain entry. */
1142
+ | { kind: "keychain"; service: string; account?: string }
1143
+ /** Determined by spawning a CLI status command. */
1144
+ | { kind: "cli"; command: string };
1145
+
1146
+ /**
1147
+ * One auth path this provider supports, with its current presence state.
1148
+ *
1149
+ * `present` is boolean: true if the credential is confirmed present,
1150
+ * false otherwise. Previously `"unknown"` was a third state for macOS
1151
+ * keychain; the CLI-status approach replaces it with definitive truth.
1152
+ */
1153
+ export interface AuthOption {
1154
+ method: AuthMethod;
1155
+ source: AuthSource;
1156
+ present: boolean;
1157
+ }
1158
+
1159
+ /** Binary status for a provider's CLI. */
1160
+ export interface BinaryStatus {
1161
+ installed: boolean;
1162
+ /** Resolved absolute path to the binary, when installed. */
1163
+ resolvedPath?: string;
1164
+ /** Version string from `<cli> --version`, when we could parse one. */
1165
+ version?: string;
1166
+ /** Error message when installed=false (e.g. "command not found"). */
1167
+ error?: string;
1168
+ }
1169
+
1170
+ /**
1171
+ * Rich identity info reported by the CLI's own auth-status command.
1172
+ * Only populated for providers that expose this (currently Claude;
1173
+ * Codex exposes a limited version).
1174
+ */
1175
+ export interface AuthIdentity {
1176
+ /** Email address of the logged-in account, when known. */
1177
+ email?: string;
1178
+ /** Organization / team name, when known. */
1179
+ orgName?: string;
1180
+ /**
1181
+ * Subscription tier as reported by the CLI (e.g. "max", "pro", "team",
1182
+ * "enterprise"). Provider-specific free-form string.
1183
+ */
1184
+ subscriptionType?: string;
1185
+ /**
1186
+ * Active auth method as reported by the CLI (e.g. "claude.ai",
1187
+ * "chatgpt", "api_key", "bedrock"). Provider-specific free-form string.
1188
+ * Distinct from AuthMethod, which is the normalized billing-mode
1189
+ * category.
1190
+ */
1191
+ authMethod?: string;
1192
+ }
1193
+
1194
+ /** Full auth report for a provider. */
1195
+ export interface AuthReport {
1196
+ providerType: string;
1197
+ /** Whether the CLI binary is installed and what we know about it. */
1198
+ binary: BinaryStatus;
1199
+ /**
1200
+ * Every auth path this provider supports, with current presence state.
1201
+ * Empty when the binary is missing (there's nothing to report against).
1202
+ */
1203
+ options: AuthOption[];
1204
+ /** Rich identity info from the CLI's status output, when available. */
1205
+ identity?: AuthIdentity;
1206
+ /**
1207
+ * Where the presence data came from:
1208
+ * - "cli": parsed from a live `<cli> auth status` call (definitive)
1209
+ * - "filesystem": best-effort heuristic from env vars and files
1210
+ * (used when the binary is missing or its status subcommand failed)
1211
+ */
1212
+ source: "cli" | "filesystem";
1213
+ }
1214
+
1215
+ /** Optional context for auth resolution. */
1216
+ export interface AuthResolveContext {
1217
+ /** Additional env vars layered on top of process.env. */
1218
+ env?: Record<string, string>;
1219
+ /** Override the CLI binary path (passed through to findBinary). */
1220
+ command?: string;
1221
+ /** Bypass the 60s result cache and refresh from source. */
1222
+ fresh?: boolean;
1223
+ }
1224
+
1225
+ // Models
1226
+ export interface ProviderModel {
1227
+ id: string;
1228
+ name: string;
1229
+ provider?: string;
1230
+ }
1231
+
1232
+ // MCP server configuration
1233
+ /**
1234
+ * An MCP server to attach to the agent. Two transports:
1235
+ * - **stdio** (default when `type` is omitted): the agent spawns `command`.
1236
+ * - **http / sse**: the agent connects to `url`; `headers` may carry auth
1237
+ * tokens — agentex stages the config as a 0600 temp file and passes
1238
+ * `--mcp-config <path>`, never inline argv (argv is world-readable via `ps`).
1239
+ *
1240
+ * Honored by the claude provider. Codex has no MCP wiring yet
1241
+ * (`capabilities.mcp` is `false` there); the field is ignored.
1242
+ */
1243
+ export type McpServerConfig =
1244
+ | {
1245
+ name: string;
1246
+ /** stdio transport — the default when `type` is omitted. */
1247
+ type?: "stdio";
1248
+ command: string;
1249
+ args?: string[];
1250
+ env?: Record<string, string>;
1251
+ }
1252
+ | {
1253
+ name: string;
1254
+ type: "http" | "sse";
1255
+ url: string;
1256
+ headers?: Record<string, string>;
1257
+ };
1258
+
1259
+ // ---------------------------------------------------------------------------
1260
+ // Multi-turn session types
1261
+ // ---------------------------------------------------------------------------
1262
+
1263
+ /** Context for creating a persistent multi-turn session. */
1264
+ export interface SessionContext {
1265
+ cwd?: string;
1266
+ env?: Record<string, string>;
1267
+ config?: ProviderConfig;
1268
+ /** Resume an existing session. If omitted, starts fresh. */
1269
+ sessionParams?: Record<string, unknown> | null;
1270
+ /** AbortSignal to cancel the session. When aborted, the session is closed
1271
+ * and the underlying process is terminated. */
1272
+ signal?: AbortSignal;
1273
+
1274
+ /**
1275
+ * Called for every stream event across all turns. Handlers are awaited in
1276
+ * event order — the next handler does not start until the previous one's
1277
+ * returned promise settles. `send()` resolves only after every handler for
1278
+ * events up to and including the turn's terminal `result` event has
1279
+ * settled. Trailing events the provider may emit after the result event
1280
+ * (rare: late `system`/`rate_limit` lines) are still dispatched in order
1281
+ * but may run after `send()` resolves.
1282
+ *
1283
+ * A handler that throws is swallowed; the chain continues with the next
1284
+ * event.
1285
+ */
1286
+ onEvent?: (event: StreamEvent) => void | Promise<void>;
1287
+ /** Called for raw stdout/stderr output across all turns. */
1288
+ onOutput?: (stream: "stdout" | "stderr", chunk: string) => void | Promise<void>;
1289
+ /** Called at key execution lifecycle phases (preparing, spawning, running, etc.). */
1290
+ onLifecycle?: (event: LifecycleEvent) => void;
1291
+
1292
+ /**
1293
+ * Called when the agent needs confirmation or user input before proceeding
1294
+ * with a tool call. This covers both regular tool permissions (e.g. Bash,
1295
+ * Write) and interactive tools like AskUserQuestion.
1296
+ *
1297
+ * Use `parseAskUserQuestion(req)` to detect structured question prompts
1298
+ * and return answers via `updatedInput`.
1299
+ *
1300
+ * Return `{ allow: true }` to proceed, `{ allow: false }` to deny.
1301
+ * If not provided, all tool calls are auto-allowed.
1302
+ */
1303
+ onUserInputRequest?: (req: UserInputRequest) => Promise<UserInputResponse>;
1304
+
1305
+ /**
1306
+ * Called when an MCP server requests user input (form fields, multiple
1307
+ * choice, URL, etc.). Return `{ action: "accept", content: {...} }` to
1308
+ * provide the input, `{ action: "decline" }` to refuse, or
1309
+ * `{ action: "cancel" }` to abort the current turn.
1310
+ *
1311
+ * If not provided, all elicitations are declined.
1312
+ */
1313
+ onElicitation?: (req: ElicitationRequest) => Promise<ElicitationResponse>;
1314
+
1315
+ /**
1316
+ * Called when the CLI needs the host to run a hook callback.
1317
+ * If not provided, hook callbacks return an empty result.
1318
+ */
1319
+ onHookCallback?: (req: HookCallbackRequest) => Promise<HookCallbackResponse>;
1320
+ }
1321
+
1322
+ /**
1323
+ * Handle returned by `AgentSession.send()`. Carries the library-generated
1324
+ * UUID for the user message (use with `cancel(uuid)`) plus a Promise for the
1325
+ * TurnResult.
1326
+ *
1327
+ * When `concurrentSend` is true and multiple `send()` calls are coalesced into
1328
+ * one turn by the CLI, their `result` Promises resolve with the same
1329
+ * TurnResult object — callers cannot assume 1:1 correspondence between
1330
+ * `send()` calls and TurnResults.
1331
+ */
1332
+ export interface SendHandle {
1333
+ /** Library-generated UUID attached to the user message. Pass to `cancel()`. */
1334
+ uuid: string;
1335
+ /** Resolves with the next TurnResult after the message was written. */
1336
+ result: Promise<TurnResult>;
1337
+ }
1338
+
1339
+ /** Outcome of a `cancel(uuid)` call. */
1340
+ export interface CancelResult {
1341
+ /**
1342
+ * `true` only when the CLI confirmed the queued message was removed before
1343
+ * being processed. `false` when:
1344
+ * - the provider doesn't support per-message cancel (capabilities.cancelQueuedMessage === false)
1345
+ * - the message had already been dequeued (lost race to mid-turn drain or new-turn dispatch)
1346
+ * - the UUID is unknown to the CLI
1347
+ */
1348
+ cancelled: boolean;
1349
+ }
1350
+
1351
+ /** Outcome of a `stopTask(taskId)` call. */
1352
+ export interface StopTaskResult {
1353
+ /**
1354
+ * `true` only when the provider has a per-task stop control and the CLI
1355
+ * acknowledged the request without error. `false` when:
1356
+ * - the provider doesn't support per-task stop (`capabilities.stopTask === false`)
1357
+ * - the session is already closed
1358
+ * - the `taskId` is unknown to the CLI, or the task had already ended
1359
+ *
1360
+ * The terminal status the task settles into is intentionally NOT returned
1361
+ * here — the CLI's stop acknowledgement carries no payload. It arrives
1362
+ * asynchronously on the event stream as the task's next `task_updated` /
1363
+ * `task_notification`.
1364
+ */
1365
+ stopped: boolean;
1366
+ }
1367
+
1368
+ /** Per-call options for `AgentSession.send()`. */
1369
+ export interface SendOptions {
1370
+ /**
1371
+ * Hard cap on this turn's runtime, in seconds. On fire, the SDK
1372
+ * `interrupt()`s the active turn and resolves this send's `result` with
1373
+ * `status: "timeout"`. Overrides `ProviderConfig.timeoutSec` for this call.
1374
+ *
1375
+ * Note: a session runs a single underlying agent, so interrupting one
1376
+ * timed-out send also ends any other sends coalesced into the same turn —
1377
+ * the natural shape for the one-send-per-turn (scheduled-run) use case.
1378
+ */
1379
+ timeoutSec?: number;
1380
+ /**
1381
+ * Abort just this turn (not the whole session). On abort, the active turn is
1382
+ * `interrupt()`ed and this send's `result` resolves with `status: "aborted"`.
1383
+ * Distinct from `SessionContext.signal`, which closes the entire session.
1384
+ * Stacks with `timeoutSec`; whichever fires first wins.
1385
+ */
1386
+ signal?: AbortSignal;
1387
+ }
1388
+
1389
+ /** A persistent session handle for multi-turn conversations. */
1390
+ export interface AgentSession {
1391
+ readonly sessionId: string | null;
1392
+ /**
1393
+ * Reflects the most recent observed lifecycle event, not whether `send()`
1394
+ * is callable. For providers with `concurrentSend: true`, `send()` is
1395
+ * always callable while state is not `closed`.
1396
+ */
1397
+ readonly state: SessionState;
1398
+
1399
+ /**
1400
+ * Send a user message.
1401
+ *
1402
+ * Returns a `SendHandle` synchronously-then-asynchronously: `uuid` is
1403
+ * available as soon as the Promise resolves (which is on the next tick);
1404
+ * `result` resolves with the next `TurnResult` after the message was
1405
+ * written.
1406
+ *
1407
+ * For providers with `concurrentSend: true` (Claude, Codex), callable at
1408
+ * any time including while a turn is in progress — the CLI's own queue
1409
+ * handles ordering. For providers with `concurrentSend: false`, throws when
1410
+ * called while !idle.
1411
+ *
1412
+ * Multiple concurrent sends may resolve with the same shared `TurnResult`
1413
+ * if the CLI coalesces them. See `SendHandle` JSDoc.
1414
+ *
1415
+ * Pass `SendOptions` to bound this turn with a timeout and/or abort signal.
1416
+ * Throws if the session is closed or `drain()`ing.
1417
+ */
1418
+ send(message: string, options?: SendOptions): Promise<SendHandle>;
1419
+
1420
+ /**
1421
+ * Cancel a previously-sent message that is still queued in the CLI.
1422
+ *
1423
+ * Always callable. Returns `{cancelled: false}` when the provider doesn't
1424
+ * support per-message cancel, when the message has already been dequeued,
1425
+ * or when the UUID is unknown.
1426
+ */
1427
+ cancel(uuid: string): Promise<CancelResult>;
1428
+
1429
+ /**
1430
+ * Stop a single in-flight background task (a backgrounded shell, a running
1431
+ * async subagent) without disturbing the session or its other tasks.
1432
+ *
1433
+ * Always callable. Returns `{ stopped: false }` when the provider has no
1434
+ * per-task stop control (`capabilities.stopTask === false`), the session is
1435
+ * closed, or the `taskId` is unknown / already ended. The kill is performed
1436
+ * by the underlying CLI/harness (which owns the process); the model is not
1437
+ * involved and learns of the stop via the task's next lifecycle event.
1438
+ */
1439
+ stopTask(taskId: string): Promise<StopTaskResult>;
1440
+
1441
+ /**
1442
+ * Arm a session-scoped goal. The library uses native enforcement where the
1443
+ * provider supports it (Claude's Stop-hook sentinel, Codex's thread goal) and
1444
+ * the emulation engine otherwise. Resolves once the goal is armed — NOT when
1445
+ * it is met; watch for `goal_status` stream events for that.
1446
+ *
1447
+ * Initial turn: Claude native `/goal` and the emulation engine both kick off
1448
+ * a turn directed at the objective (mirroring native `/goal`, which starts
1449
+ * immediately). Codex native goal mode only seeds durable thread state and
1450
+ * starts NO turn — the model works toward it on your next `send()`. Advisory
1451
+ * goals never start a turn.
1452
+ *
1453
+ * Enforcement caveat: Claude's native arm is fire-and-forget — a CLI that
1454
+ * doesn't honor headless `/goal` will report `armed: true` while nothing
1455
+ * actually gates turn-end. If you need *guaranteed* enforcement on any
1456
+ * provider, pass `enforce: "emulate"` (the library drives the loop itself).
1457
+ *
1458
+ * Setting a goal while one is active replaces it (emits `cleared` then
1459
+ * `active`). `objective` is capped at 4,000 chars to match both native
1460
+ * providers; longer input throws `RangeError`.
1461
+ */
1462
+ setGoal(objective: string, options?: GoalOptions): Promise<SetGoalResult>;
1463
+
1464
+ /**
1465
+ * Abort the active goal early. Emits a `goal_status` with status "cleared"
1466
+ * (or "blocked" when `reason: "blocked"`). Resolves `{cleared:false}` when no
1467
+ * goal is active.
1468
+ */
1469
+ clearGoal(options?: { reason?: "cleared" | "blocked" }): Promise<ClearGoalResult>;
1470
+
1471
+ /** Current goal state, or null when none is armed. Reads live in-memory state. */
1472
+ getGoal(): GoalState | null;
1473
+
1474
+ /** Gracefully interrupt the current turn. */
1475
+ interrupt(): Promise<void>;
1476
+
1477
+ /**
1478
+ * Graceful stop: refuse new `send()` calls (they throw), await any in-flight
1479
+ * turn's `result` to settle, then `close()`. Use this — not `interrupt()`
1480
+ * (loses in-flight work) or `close()` (kills mid-tool) — when you want a
1481
+ * running turn to finish before shutting down (budget gate, SIGTERM, schedule
1482
+ * pause). Resolves once fully closed. Idempotent.
1483
+ */
1484
+ drain(): Promise<void>;
1485
+
1486
+ /** Terminate the session and kill the underlying process. */
1487
+ close(): Promise<void>;
1488
+
1489
+ /**
1490
+ * Produce a durable, JSON-serializable `SessionRecord` a host can persist to
1491
+ * reattach after a restart (via `provider.attachSession`). Returns null until
1492
+ * the provider has assigned a session id (watch for the first `system` /
1493
+ * session event). Present only on providers with
1494
+ * `capabilities.durableSessions === true`.
1495
+ */
1496
+ describe?(): SessionRecord | null;
1497
+ }
1498
+
1499
+ /** Result of a single turn within a session. */
1500
+ export interface TurnResult {
1501
+ summary: string | null;
1502
+ usage?: Record<string, TokenUsage>;
1503
+ costUsd: number | null;
1504
+ /**
1505
+ * `timeout` — the per-send timeout (`SendOptions.timeoutSec` or the session
1506
+ * default `ProviderConfig.timeoutSec`) fired and the turn was interrupted.
1507
+ * `aborted` — a `SendOptions.signal` aborted the turn (or the turn was
1508
+ * otherwise interrupted).
1509
+ */
1510
+ status: "completed" | "failed" | "max_turns" | "max_budget" | "aborted" | "timeout";
1511
+ errorCode: string | null;
1512
+ errorMessage: string | null;
1513
+ }
1514
+
1515
+ /**
1516
+ * Describes a tool the agent wants to use and needs confirmation or user
1517
+ * input before proceeding. This is the unified callback for both regular
1518
+ * tool permissions (Bash, Write, etc.) and interactive tools like
1519
+ * AskUserQuestion.
1520
+ *
1521
+ * For AskUserQuestion, use `parseAskUserQuestion(req)` to extract the
1522
+ * structured questions and return answers via `updatedInput`.
1523
+ */
1524
+ export interface UserInputRequest {
1525
+ toolName: string;
1526
+ input: Record<string, unknown>;
1527
+ toolUseId: string;
1528
+ /** Human-readable title for the tool action. */
1529
+ title?: string;
1530
+ /** Display name of the tool. */
1531
+ displayName?: string;
1532
+ /** Why the agent decided to use this tool. */
1533
+ description?: string;
1534
+ /** ID of the sub-agent making the request, if any. */
1535
+ agentId?: string;
1536
+ }
1537
+
1538
+ /** Host response to a tool request. */
1539
+ export interface UserInputResponse {
1540
+ allow: boolean;
1541
+ message?: string;
1542
+ /** Optionally modify the tool's input before execution (e.g. answers for AskUserQuestion). */
1543
+ updatedInput?: Record<string, unknown>;
1544
+ }
1545
+
1546
+ // ---------------------------------------------------------------------------
1547
+ // Elicitation — server-initiated user input requests (forms, choices, URLs)
1548
+ // ---------------------------------------------------------------------------
1549
+
1550
+ /**
1551
+ * Sent when a server (typically an MCP tool-server running inside the Claude
1552
+ * process) needs user input. The request can represent anything from a simple
1553
+ * yes/no confirmation to a rich multi-field form combining dropdowns,
1554
+ * checkboxes, text fields, and number inputs.
1555
+ *
1556
+ * The `requestedSchema` is a standard JSON Schema (type: "object") whose
1557
+ * `properties` define the form fields. Supported property types:
1558
+ *
1559
+ * | Schema pattern | Renders as |
1560
+ * |---|---|
1561
+ * | `{ "type": "string", "oneOf": [{ "const": "a", "title": "A" }, ...] }` | Single-select dropdown / radio |
1562
+ * | `{ "type": "string", "enum": ["x", "y"] }` | Single-select (legacy) |
1563
+ * | `{ "type": "array", "items": { "anyOf": [{ "const": "a" }, ...] } }` | Multi-select checkboxes |
1564
+ * | `{ "type": "string" }` | Freeform text input |
1565
+ * | `{ "type": "string", "format": "email" \| "uri" \| "date" }` | Validated text input |
1566
+ * | `{ "type": "integer", "minimum": 1, "maximum": 10 }` | Number input |
1567
+ * | `{ "type": "boolean" }` | Toggle / checkbox |
1568
+ *
1569
+ * A single form can mix all of these — e.g., a dropdown for language, checkboxes
1570
+ * for features, and a freeform "notes" field.
1571
+ *
1572
+ * **Example — multiple choice + freeform:**
1573
+ * ```json
1574
+ * { "type": "object", "properties": {
1575
+ * "framework": { "type": "string", "oneOf": [
1576
+ * { "const": "express", "title": "Express" },
1577
+ * { "const": "fastify", "title": "Fastify" },
1578
+ * { "const": "hono", "title": "Hono" }
1579
+ * ]},
1580
+ * "features": { "type": "array", "items": {
1581
+ * "anyOf": [
1582
+ * { "const": "auth", "title": "Authentication" },
1583
+ * { "const": "db", "title": "Database" },
1584
+ * { "const": "ws", "title": "WebSockets" }
1585
+ * ]
1586
+ * }},
1587
+ * "notes": { "type": "string" }
1588
+ * },
1589
+ * "required": ["framework"]
1590
+ * }
1591
+ * ```
1592
+ */
1593
+ export interface ElicitationRequest {
1594
+ /**
1595
+ * Name of the MCP server requesting input. Maps to the `mcp_server_name`
1596
+ * field in the Claude protocol. Display this so the user knows which
1597
+ * server is asking for input.
1598
+ */
1599
+ mcpServerName: string;
1600
+ /** Human-readable prompt describing what input is needed. */
1601
+ message: string;
1602
+ /** How to present the request: "form" for inline input, "url" to open a browser. */
1603
+ mode?: "form" | "url";
1604
+ /** URL to open when mode is "url". */
1605
+ url?: string;
1606
+ /** Unique ID for this elicitation, used for deduplication. */
1607
+ elicitationId?: string;
1608
+ /**
1609
+ * JSON Schema (type: "object") describing the expected input. Each property
1610
+ * in `properties` is a form field. See the type-level JSDoc for the full
1611
+ * list of supported property types and examples.
1612
+ */
1613
+ requestedSchema?: Record<string, unknown>;
1614
+ }
1615
+
1616
+ /** Host response to an elicitation request. */
1617
+ export interface ElicitationResponse {
1618
+ /** "accept" to provide content, "decline" to refuse, "cancel" to abort the turn. */
1619
+ action: "accept" | "decline" | "cancel";
1620
+ /** The user's input, matching the requestedSchema. Only required when action is "accept". */
1621
+ content?: Record<string, unknown>;
1622
+ }
1623
+
1624
+ // ---------------------------------------------------------------------------
1625
+ // Hook callbacks — CLI requesting the host to run a hook
1626
+ // ---------------------------------------------------------------------------
1627
+
1628
+ /** Sent when the CLI needs the host to execute a hook callback. */
1629
+ export interface HookCallbackRequest {
1630
+ callbackId: string;
1631
+ input: Record<string, unknown>;
1632
+ toolUseId?: string;
1633
+ }
1634
+
1635
+ /** Host response to a hook callback. */
1636
+ export interface HookCallbackResponse {
1637
+ result?: Record<string, unknown>;
1638
+ }