@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.
- package/CHANGELOG.md +338 -0
- package/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/derived.d.ts +5 -3
- package/dist/derived.d.ts.map +1 -1
- package/dist/derived.js +11 -7
- package/dist/derived.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/acp/index.d.ts +1 -1
- package/dist/providers/acp/index.d.ts.map +1 -1
- package/dist/providers/acp/index.js +5 -97
- package/dist/providers/acp/index.js.map +1 -1
- package/dist/providers/acp/session.d.ts +8 -1
- package/dist/providers/acp/session.d.ts.map +1 -1
- package/dist/providers/acp/session.js +94 -0
- package/dist/providers/acp/session.js.map +1 -1
- package/dist/providers/claude/attach.d.ts +8 -0
- package/dist/providers/claude/attach.d.ts.map +1 -0
- package/dist/providers/claude/attach.js +113 -0
- package/dist/providers/claude/attach.js.map +1 -0
- package/dist/providers/claude/execute.d.ts.map +1 -1
- package/dist/providers/claude/execute.js +17 -2
- package/dist/providers/claude/execute.js.map +1 -1
- package/dist/providers/claude/goal-capability.d.ts +15 -0
- package/dist/providers/claude/goal-capability.d.ts.map +1 -0
- package/dist/providers/claude/goal-capability.js +20 -0
- package/dist/providers/claude/goal-capability.js.map +1 -0
- package/dist/providers/claude/index.d.ts.map +1 -1
- package/dist/providers/claude/index.js +8 -4
- package/dist/providers/claude/index.js.map +1 -1
- package/dist/providers/claude/session.d.ts +11 -9
- package/dist/providers/claude/session.d.ts.map +1 -1
- package/dist/providers/claude/session.js +36 -14
- package/dist/providers/claude/session.js.map +1 -1
- package/dist/providers/codex/attach.d.ts +9 -0
- package/dist/providers/codex/attach.d.ts.map +1 -0
- package/dist/providers/codex/attach.js +93 -0
- package/dist/providers/codex/attach.js.map +1 -0
- package/dist/providers/codex/execute.d.ts.map +1 -1
- package/dist/providers/codex/execute.js +17 -3
- package/dist/providers/codex/execute.js.map +1 -1
- package/dist/providers/codex/goal-capability.d.ts +13 -0
- package/dist/providers/codex/goal-capability.d.ts.map +1 -0
- package/dist/providers/codex/goal-capability.js +18 -0
- package/dist/providers/codex/goal-capability.js.map +1 -0
- package/dist/providers/codex/index.d.ts +1 -0
- package/dist/providers/codex/index.d.ts.map +1 -1
- package/dist/providers/codex/index.js +9 -6
- package/dist/providers/codex/index.js.map +1 -1
- package/dist/providers/codex/session.d.ts +11 -7
- package/dist/providers/codex/session.d.ts.map +1 -1
- package/dist/providers/codex/session.js +37 -12
- package/dist/providers/codex/session.js.map +1 -1
- package/dist/providers/codex/transcript-normalize.d.ts +28 -0
- package/dist/providers/codex/transcript-normalize.d.ts.map +1 -0
- package/dist/providers/codex/transcript-normalize.js +191 -0
- package/dist/providers/codex/transcript-normalize.js.map +1 -0
- package/dist/providers/cursor/index.d.ts.map +1 -1
- package/dist/providers/cursor/index.js +2 -2
- package/dist/providers/cursor/index.js.map +1 -1
- package/dist/providers/openclaw/index.d.ts.map +1 -1
- package/dist/providers/openclaw/index.js +2 -2
- package/dist/providers/openclaw/index.js.map +1 -1
- package/dist/providers/opencode/index.d.ts.map +1 -1
- package/dist/providers/opencode/index.js +3 -5
- package/dist/providers/opencode/index.js.map +1 -1
- package/dist/providers/pi/index.d.ts.map +1 -1
- package/dist/providers/pi/index.js +3 -5
- package/dist/providers/pi/index.js.map +1 -1
- package/dist/providers/process/index.d.ts.map +1 -1
- package/dist/providers/process/index.js +2 -2
- package/dist/providers/process/index.js.map +1 -1
- package/dist/registry.d.ts +0 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +0 -4
- package/dist/registry.js.map +1 -1
- package/dist/sessions/index.d.ts +3 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/sessions/index.js +2 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/sessions/record.d.ts +43 -0
- package/dist/sessions/record.d.ts.map +1 -0
- package/dist/sessions/record.js +85 -0
- package/dist/sessions/record.js.map +1 -0
- package/dist/types.d.ts +176 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/endpoint.d.ts +38 -0
- package/dist/utils/endpoint.d.ts.map +1 -0
- package/dist/utils/endpoint.js +151 -0
- package/dist/utils/endpoint.js.map +1 -0
- package/dist/utils/env.d.ts.map +1 -1
- package/dist/utils/env.js +5 -1
- package/dist/utils/env.js.map +1 -1
- package/dist/utils/uuid.d.ts +7 -1
- package/dist/utils/uuid.d.ts.map +1 -1
- package/dist/utils/uuid.js +21 -1
- package/dist/utils/uuid.js.map +1 -1
- package/package.json +64 -7
- package/src/derived.ts +311 -0
- package/src/goals/controller.ts +442 -0
- package/src/goals/index.ts +21 -0
- package/src/goals/normalize.ts +173 -0
- package/src/goals/sentinel.ts +90 -0
- package/src/index.ts +270 -0
- package/src/providers/_shared/http-agent.ts +304 -0
- package/src/providers/acp/index.ts +103 -0
- package/src/providers/acp/parse.ts +131 -0
- package/src/providers/acp/session.ts +744 -0
- package/src/providers/claude/attach.ts +147 -0
- package/src/providers/claude/codec.ts +43 -0
- package/src/providers/claude/execute.ts +300 -0
- package/src/providers/claude/goal-capability.ts +21 -0
- package/src/providers/claude/index.ts +72 -0
- package/src/providers/claude/mcp.ts +82 -0
- package/src/providers/claude/parse.ts +824 -0
- package/src/providers/claude/session.ts +1192 -0
- package/src/providers/claude/transcript.ts +555 -0
- package/src/providers/codex/attach.ts +123 -0
- package/src/providers/codex/codec.ts +50 -0
- package/src/providers/codex/execute.ts +337 -0
- package/src/providers/codex/goal-capability.ts +19 -0
- package/src/providers/codex/index.ts +57 -0
- package/src/providers/codex/modes.ts +159 -0
- package/src/providers/codex/parse.ts +691 -0
- package/src/providers/codex/plan-mode.ts +49 -0
- package/src/providers/codex/session.ts +1287 -0
- package/src/providers/codex/transcript-normalize.ts +197 -0
- package/src/providers/codex/transcript.ts +487 -0
- package/src/providers/codex/usage-scanner.ts +178 -0
- package/src/providers/copilot/index.ts +19 -0
- package/src/providers/cursor/codec.ts +44 -0
- package/src/providers/cursor/execute.ts +271 -0
- package/src/providers/cursor/index.ts +25 -0
- package/src/providers/cursor/parse.ts +288 -0
- package/src/providers/gemini/index.ts +21 -0
- package/src/providers/openclaw/codec.ts +40 -0
- package/src/providers/openclaw/execute.ts +19 -0
- package/src/providers/openclaw/index.ts +29 -0
- package/src/providers/opencode/codec.ts +50 -0
- package/src/providers/opencode/event-parse.ts +141 -0
- package/src/providers/opencode/execute.ts +251 -0
- package/src/providers/opencode/http-session.ts +427 -0
- package/src/providers/opencode/index.ts +30 -0
- package/src/providers/opencode/parse.ts +203 -0
- package/src/providers/opencode/server.ts +0 -0
- package/src/providers/pi/codec.ts +44 -0
- package/src/providers/pi/execute.ts +297 -0
- package/src/providers/pi/index.ts +30 -0
- package/src/providers/pi/parse.ts +231 -0
- package/src/providers/pi/session.ts +381 -0
- package/src/providers/process/execute.ts +148 -0
- package/src/providers/process/index.ts +52 -0
- package/src/registry.ts +40 -0
- package/src/sessions/index.ts +8 -0
- package/src/sessions/record.ts +108 -0
- package/src/types.ts +1638 -0
- package/src/utils/ask-user-question.ts +57 -0
- package/src/utils/auth.ts +661 -0
- package/src/utils/binary.ts +179 -0
- package/src/utils/endpoint.ts +172 -0
- package/src/utils/env.ts +63 -0
- package/src/utils/execute-all.ts +68 -0
- package/src/utils/exit-plan-mode.ts +40 -0
- package/src/utils/instructions.ts +427 -0
- package/src/utils/process.ts +223 -0
- package/src/utils/runtime-config.ts +100 -0
- package/src/utils/runtime-homes.ts +49 -0
- package/src/utils/skill-commands.ts +493 -0
- package/src/utils/skills.ts +500 -0
- package/src/utils/template.ts +16 -0
- package/src/utils/tool-names.ts +51 -0
- package/src/utils/uuid.ts +21 -0
- 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
|
+
}
|