@ai-sdk/harness 0.0.0 → 1.0.0-canary.3
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 +24 -0
- package/LICENSE +13 -0
- package/README.md +142 -0
- package/agent/index.ts +17 -0
- package/bridge/index.ts +10 -0
- package/dist/agent/index.d.ts +1480 -0
- package/dist/agent/index.js +2554 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/bridge/index.d.ts +111 -0
- package/dist/bridge/index.js +414 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/index.d.ts +1510 -0
- package/dist/index.js +15834 -0
- package/dist/index.js.map +1 -0
- package/dist/observability/index.d.ts +97 -0
- package/dist/observability/index.js +225 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/utils/index.d.ts +196 -0
- package/dist/utils/index.js +327 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +104 -1
- package/src/agent/harness-agent-session.ts +352 -0
- package/src/agent/harness-agent-settings.ts +131 -0
- package/src/agent/harness-agent-tool-approval-continuation.ts +94 -0
- package/src/agent/harness-agent.ts +750 -0
- package/src/agent/harness-diagnostics.ts +88 -0
- package/src/agent/internal/bootstrap-recipe.ts +124 -0
- package/src/agent/internal/bridge-port-registry.ts +52 -0
- package/src/agent/internal/harness-stream-text-result.ts +720 -0
- package/src/agent/internal/permission-mode.ts +50 -0
- package/src/agent/internal/resolve-observability.ts +128 -0
- package/src/agent/internal/resume-state-validation.ts +51 -0
- package/src/agent/internal/run-prompt.ts +811 -0
- package/src/agent/internal/strip-work-dir.ts +68 -0
- package/src/agent/internal/to-harness-stream.ts +75 -0
- package/src/agent/internal/translate-stream-part.ts +221 -0
- package/src/agent/internal/turn-telemetry.ts +359 -0
- package/src/agent/prewarm.ts +46 -0
- package/src/bridge/index.ts +700 -0
- package/src/errors/harness-capability-unsupported-error.ts +41 -0
- package/src/errors/harness-error.ts +22 -0
- package/src/index.ts +3 -0
- package/src/observability/file-reporter.ts +209 -0
- package/src/observability/index.ts +13 -0
- package/src/observability/trace-tree-reporter.ts +122 -0
- package/src/utils/classify-disk-log.ts +43 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/sandbox-channel.ts +453 -0
- package/src/v1/harness-v1-bootstrap.ts +46 -0
- package/src/v1/harness-v1-bridge-protocol.ts +310 -0
- package/src/v1/harness-v1-builtin-tool.ts +138 -0
- package/src/v1/harness-v1-call-warning.ts +22 -0
- package/src/v1/harness-v1-diagnostic.ts +66 -0
- package/src/v1/harness-v1-metadata.ts +13 -0
- package/src/v1/harness-v1-network-sandbox-session.ts +123 -0
- package/src/v1/harness-v1-observability.ts +20 -0
- package/src/v1/harness-v1-permission-mode.ts +11 -0
- package/src/v1/harness-v1-prompt-control.ts +41 -0
- package/src/v1/harness-v1-prompt.ts +11 -0
- package/src/v1/harness-v1-resume-state.ts +46 -0
- package/src/v1/harness-v1-sandbox-provider.ts +76 -0
- package/src/v1/harness-v1-session.ts +268 -0
- package/src/v1/harness-v1-skill.ts +22 -0
- package/src/v1/harness-v1-stream-part.ts +363 -0
- package/src/v1/harness-v1-tool-spec.ts +31 -0
- package/src/v1/harness-v1.ts +83 -0
- package/src/v1/index.ts +93 -0
- package/utils/index.ts +1 -0
|
@@ -0,0 +1,1480 @@
|
|
|
1
|
+
import { Experimental_SandboxSession, UserModelMessage, ToolSet, FlexibleSchema, ToolApprovalResponse, ModelMessage, Context, AssistantModelMessage, ToolModelMessage } from '@ai-sdk/provider-utils';
|
|
2
|
+
import { z } from 'zod/v4';
|
|
3
|
+
import * as _ai_sdk_provider from '@ai-sdk/provider';
|
|
4
|
+
import { JSONValue, LanguageModelV4ToolCall, LanguageModelV4ToolApprovalRequest, LanguageModelV4ToolResult, LanguageModelV4FinishReason, LanguageModelV4Usage, JSONSchema7 } from '@ai-sdk/provider';
|
|
5
|
+
import * as ai from 'ai';
|
|
6
|
+
import { ToolApprovalStatus, TelemetryOptions, StreamTextResult, TextStreamPart, ProviderMetadata, CallWarning, ContentPart, FinishReason, LanguageModelUsage, StepResult, UIMessage, UIMessageStreamOptions, InferUIMessageChunk, Agent, AgentCallParameters, GenerateTextResult, AgentStreamParameters } from 'ai';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* One file to write into the sandbox as part of an adapter's bootstrap recipe.
|
|
10
|
+
* Paths should live under {@link HarnessV1Bootstrap.bootstrapDir}.
|
|
11
|
+
*/
|
|
12
|
+
interface HarnessV1BootstrapFile {
|
|
13
|
+
readonly path: string;
|
|
14
|
+
readonly content: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* One command to run in the sandbox as part of an adapter's bootstrap recipe.
|
|
18
|
+
* Commands run sequentially after all files have been written; a non-zero exit
|
|
19
|
+
* aborts the bootstrap.
|
|
20
|
+
*/
|
|
21
|
+
interface HarnessV1BootstrapCommand {
|
|
22
|
+
readonly command: string;
|
|
23
|
+
readonly workingDirectory?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Adapter-owned bootstrap recipe. The adapter declares the files and commands
|
|
27
|
+
* needed to set up its bridge inside any sandbox. The harness framework hashes
|
|
28
|
+
* the recipe into an identity used by sandbox providers for snapshot-based
|
|
29
|
+
* reuse, and applies the recipe idempotently before the bridge spawns.
|
|
30
|
+
*/
|
|
31
|
+
interface HarnessV1Bootstrap {
|
|
32
|
+
/**
|
|
33
|
+
* Stable id of the adapter that owns this recipe. Conventionally matches
|
|
34
|
+
* {@link HarnessV1.harnessId}. Contributes to the recipe hash.
|
|
35
|
+
*/
|
|
36
|
+
readonly harnessId: string;
|
|
37
|
+
/**
|
|
38
|
+
* Absolute path inside the sandbox where this recipe writes its state.
|
|
39
|
+
* The marker file lives directly under it. Files declared in {@link files}
|
|
40
|
+
* should also use this prefix so an adapter upgrade can sweep stale state
|
|
41
|
+
* by clearing the directory.
|
|
42
|
+
*/
|
|
43
|
+
readonly bootstrapDir: string;
|
|
44
|
+
/** Files to write into the sandbox before any command runs. */
|
|
45
|
+
readonly files: ReadonlyArray<HarnessV1BootstrapFile>;
|
|
46
|
+
/** Commands to run after files are written, in order. */
|
|
47
|
+
readonly commands: ReadonlyArray<HarnessV1BootstrapCommand>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Network sandbox session returned by `HarnessV1SandboxProvider.createSession()`. The
|
|
52
|
+
* harness keeps this for the lifetime of a session. It is itself a
|
|
53
|
+
* {@link SandboxSession} (file I/O, exec, spawn) and adds the infra surface on
|
|
54
|
+
* top: port resolution, lifecycle, and network-policy mutation.
|
|
55
|
+
*
|
|
56
|
+
* Code that should only touch the filesystem and spawn processes receives the
|
|
57
|
+
* reduced view from {@link HarnessV1NetworkSandboxSession.restricted}, never the
|
|
58
|
+
* network sandbox session itself — so it cannot stop the sandbox or change its
|
|
59
|
+
* network policy.
|
|
60
|
+
*/
|
|
61
|
+
interface HarnessV1NetworkSandboxSession extends Experimental_SandboxSession {
|
|
62
|
+
/**
|
|
63
|
+
* Stable identifier for the underlying sandbox resource. Used by the
|
|
64
|
+
* harness session manager as the durable lookup key for cross-process
|
|
65
|
+
* resume — the framework persists this on the resume payload so a future
|
|
66
|
+
* process can call `HarnessV1SandboxProvider.resume?({ sessionId })` and
|
|
67
|
+
* reach the same resource. Providers populate it from their native
|
|
68
|
+
* identifier (Vercel: the sandbox name; just-bash: a UUID minted at
|
|
69
|
+
* create time).
|
|
70
|
+
*/
|
|
71
|
+
readonly id: string;
|
|
72
|
+
/**
|
|
73
|
+
* The sandbox's default working directory — the absolute path that
|
|
74
|
+
* `run`/`spawn` resolve relative commands against when no `workingDirectory`
|
|
75
|
+
* is given. Read from the live sandbox (it is provider-specific and
|
|
76
|
+
* configurable at create time: Vercel defaults to `/vercel/sandbox`,
|
|
77
|
+
* just-bash to `/home/user`), never hardcoded.
|
|
78
|
+
*
|
|
79
|
+
* The framework composes each session's working directory underneath this
|
|
80
|
+
* path (`<defaultWorkingDirectory>/<harnessId>-<sessionId>`) so adapters do
|
|
81
|
+
* not bake a provider-specific base into their own paths.
|
|
82
|
+
*/
|
|
83
|
+
readonly defaultWorkingDirectory: string;
|
|
84
|
+
/** Ports the sandbox exposes; resolvable to public URLs via `getPortUrl`. */
|
|
85
|
+
readonly ports: ReadonlyArray<number>;
|
|
86
|
+
/**
|
|
87
|
+
* Resolve a publicly-reachable URL for a sandbox-exposed port. Bridge-backed
|
|
88
|
+
* adapters call this to open their WebSocket to the in-sandbox bridge.
|
|
89
|
+
*/
|
|
90
|
+
readonly getPortUrl: (options: {
|
|
91
|
+
port: number;
|
|
92
|
+
protocol?: 'http' | 'https' | 'ws';
|
|
93
|
+
}) => PromiseLike<string>;
|
|
94
|
+
/** Stop the sandbox. Idempotent. */
|
|
95
|
+
readonly stop: () => PromiseLike<void>;
|
|
96
|
+
/**
|
|
97
|
+
* Destroy/delete the sandbox resource when supported. Optional because some
|
|
98
|
+
* providers only have a stop/dispose concept. Implementations must handle
|
|
99
|
+
* both a still-running sandbox and a previously stopped sandbox.
|
|
100
|
+
*/
|
|
101
|
+
readonly destroy?: () => PromiseLike<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Update the sandbox's outbound network policy. Optional — implementations
|
|
104
|
+
* without a local enforcement primitive (e.g. just-bash) omit this. Callers
|
|
105
|
+
* use optional-call (`sandboxSession.setNetworkPolicy?.(policy)`); a
|
|
106
|
+
* missing implementation is a no-op.
|
|
107
|
+
*/
|
|
108
|
+
readonly setNetworkPolicy?: (policy: HarnessV1NetworkPolicy) => PromiseLike<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Replace the set of ports exposed by the sandbox. Full-replacement
|
|
111
|
+
* semantics: ports omitted from the array are deregistered. Optional —
|
|
112
|
+
* implementations that cannot expose ports (e.g. just-bash) omit this.
|
|
113
|
+
*/
|
|
114
|
+
readonly setPorts?: (ports: ReadonlyArray<number>, options?: {
|
|
115
|
+
abortSignal?: AbortSignal;
|
|
116
|
+
}) => PromiseLike<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Reduced view of this session, typed as the bare {@link SandboxSession}
|
|
119
|
+
* (file I/O, exec, spawn) — nothing that could stop the sandbox or change
|
|
120
|
+
* its network policy. Pass this to user-tool `execute()` calls and other
|
|
121
|
+
* code that must not reach the infra surface.
|
|
122
|
+
*
|
|
123
|
+
* The returned object points at exactly the same underlying sandbox
|
|
124
|
+
* resource as the network sandbox session it was produced from; it is only a
|
|
125
|
+
* narrower surface over the same resource, not a separate sandbox.
|
|
126
|
+
*/
|
|
127
|
+
readonly restricted: () => Experimental_SandboxSession;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Outbound network policy applied by the sandbox runtime.
|
|
131
|
+
*
|
|
132
|
+
* `'allow-all'` and `'deny-all'` are convenience presets. `'custom'` is an
|
|
133
|
+
* allow-list with an optional CIDR deny-list that takes precedence:
|
|
134
|
+
*
|
|
135
|
+
* - Reachable hosts are the union of `allowedHosts` and `allowedCIDRs`.
|
|
136
|
+
* - `deniedCIDRs` wins over both, useful for blocking cloud-metadata IPs while
|
|
137
|
+
* otherwise allowing broad access.
|
|
138
|
+
*
|
|
139
|
+
* The two `'custom'` branches share the same discriminator but each requires
|
|
140
|
+
* a different allow field. Specifying `'custom'` with only `deniedCIDRs`
|
|
141
|
+
* (deny-only) is rejected at compile time — functionally it would be
|
|
142
|
+
* equivalent to `'deny-all'`.
|
|
143
|
+
*/
|
|
144
|
+
type HarnessV1NetworkPolicy = {
|
|
145
|
+
mode: 'allow-all';
|
|
146
|
+
} | {
|
|
147
|
+
mode: 'deny-all';
|
|
148
|
+
} | {
|
|
149
|
+
mode: 'custom';
|
|
150
|
+
allowedHosts: ReadonlyArray<string>;
|
|
151
|
+
allowedCIDRs?: ReadonlyArray<string>;
|
|
152
|
+
deniedCIDRs?: ReadonlyArray<string>;
|
|
153
|
+
} | {
|
|
154
|
+
mode: 'custom';
|
|
155
|
+
allowedHosts?: ReadonlyArray<string>;
|
|
156
|
+
allowedCIDRs: ReadonlyArray<string>;
|
|
157
|
+
deniedCIDRs?: ReadonlyArray<string>;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/** Severity of a diagnostic, ordered most → least severe. */
|
|
161
|
+
declare const harnessV1DebugLevelSchema: z.ZodEnum<{
|
|
162
|
+
error: "error";
|
|
163
|
+
warn: "warn";
|
|
164
|
+
info: "info";
|
|
165
|
+
debug: "debug";
|
|
166
|
+
trace: "trace";
|
|
167
|
+
}>;
|
|
168
|
+
type HarnessV1DebugLevel = z.infer<typeof harnessV1DebugLevelSchema>;
|
|
169
|
+
/**
|
|
170
|
+
* Per-session diagnostics configuration the framework hands an adapter (and the
|
|
171
|
+
* host sends on `start.debug`). When absent or `enabled` is false the adapter
|
|
172
|
+
* captures and emits nothing. `subsystems` filters structured events by dotted
|
|
173
|
+
* prefix; console capture is independent of the subsystem filter.
|
|
174
|
+
*/
|
|
175
|
+
declare const harnessV1DebugConfigSchema: z.ZodObject<{
|
|
176
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
177
|
+
level: z.ZodOptional<z.ZodEnum<{
|
|
178
|
+
error: "error";
|
|
179
|
+
warn: "warn";
|
|
180
|
+
info: "info";
|
|
181
|
+
debug: "debug";
|
|
182
|
+
trace: "trace";
|
|
183
|
+
}>>;
|
|
184
|
+
subsystems: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
185
|
+
}, z.core.$strip>;
|
|
186
|
+
type HarnessV1DebugConfig = z.infer<typeof harnessV1DebugConfigSchema>;
|
|
187
|
+
/**
|
|
188
|
+
* A diagnostic as emitted by a harness adapter. Structurally identical to the
|
|
189
|
+
* host-facing `HarnessDiagnostic` today, but kept separate: this is the spec's
|
|
190
|
+
* emission shape, that is the external consumption shape.
|
|
191
|
+
*/
|
|
192
|
+
type HarnessV1Diagnostic = {
|
|
193
|
+
/** Severity. */
|
|
194
|
+
readonly level: HarnessV1DebugLevel;
|
|
195
|
+
/** Human-readable line (console capture) or message (structured event). */
|
|
196
|
+
readonly message: string;
|
|
197
|
+
/** Dotted subsystem (`sandbox.log.<source>` for console capture). */
|
|
198
|
+
readonly subsystem: string;
|
|
199
|
+
/** `'log'` = captured console line; `'event'` = structured emission. */
|
|
200
|
+
readonly kind: 'log' | 'event';
|
|
201
|
+
/** Originating source label (console capture). */
|
|
202
|
+
readonly source?: string;
|
|
203
|
+
/** Which standard stream the line came from (console capture). */
|
|
204
|
+
readonly stream?: 'stdout' | 'stderr';
|
|
205
|
+
/** Structured attributes (structured events only). */
|
|
206
|
+
readonly attrs?: Record<string, unknown>;
|
|
207
|
+
/** Error payload (structured events only). */
|
|
208
|
+
readonly error?: {
|
|
209
|
+
name?: string;
|
|
210
|
+
message: string;
|
|
211
|
+
stack?: string;
|
|
212
|
+
};
|
|
213
|
+
/** The harness session this diagnostic originated from. */
|
|
214
|
+
readonly sessionId?: string;
|
|
215
|
+
/** Emission time (epoch ms). */
|
|
216
|
+
readonly timestamp: number;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Diagnostics wiring the framework hands to an adapter's `doStart`. `report` is
|
|
221
|
+
* the general emission sink: a bridge adapter normalizes each wire frame into a
|
|
222
|
+
* `HarnessV1Diagnostic` (via `harnessV1DiagnosticFromBridgeFrame`) and calls it;
|
|
223
|
+
* a non-bridge adapter constructs a `HarnessV1Diagnostic` from its host-side
|
|
224
|
+
* logs/errors and calls it directly. `debug` gates what the adapter emits.
|
|
225
|
+
* Absent when the consumer has not enabled diagnostics.
|
|
226
|
+
*/
|
|
227
|
+
type HarnessV1Observability = {
|
|
228
|
+
/** Per-session debug config gating what the adapter captures/emits. */
|
|
229
|
+
readonly debug?: HarnessV1DebugConfig;
|
|
230
|
+
/** General emission sink — any adapter reports a `HarnessV1Diagnostic` here. */
|
|
231
|
+
readonly report?: (diagnostic: HarnessV1Diagnostic) => void;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Baseline permission mode for adapter-native built-in tools.
|
|
236
|
+
*
|
|
237
|
+
* Custom host-executed tools are not controlled by this setting. They use
|
|
238
|
+
* `HarnessAgentSettings.toolApproval`, similar to AI SDK's per-tool approval
|
|
239
|
+
* status map.
|
|
240
|
+
*/
|
|
241
|
+
type HarnessV1PermissionMode = 'allow-reads' | 'allow-edits' | 'allow-all';
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Prompt shape passed to `HarnessV1Session.doPromptTurn`.
|
|
245
|
+
*
|
|
246
|
+
* A harness session represents an ongoing third-party agent runtime that
|
|
247
|
+
* owns its own conversation history. Each prompt turn carries only the
|
|
248
|
+
* fresh user input for that turn, either as a plain string or as a single
|
|
249
|
+
* `UserModelMessage`.
|
|
250
|
+
*/
|
|
251
|
+
type HarnessV1Prompt = string | UserModelMessage;
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Bidirectional control surface returned by `doPromptTurn`.
|
|
255
|
+
*
|
|
256
|
+
* The host uses these methods to feed asynchronous responses back to the
|
|
257
|
+
* adapter while a turn is running. All methods are optional except those
|
|
258
|
+
* the adapter actively supports (host-executed tools require
|
|
259
|
+
* `submitToolResult`; approvals require `submitToolApproval`; mid-turn
|
|
260
|
+
* messages require `submitUserMessage`).
|
|
261
|
+
*/
|
|
262
|
+
type HarnessV1PromptControl = {
|
|
263
|
+
/**
|
|
264
|
+
* Provide a result for a `tool-call` the adapter emitted. The adapter
|
|
265
|
+
* forwards the result to the underlying runtime so the model can continue.
|
|
266
|
+
*/
|
|
267
|
+
submitToolResult(input: {
|
|
268
|
+
toolCallId: string;
|
|
269
|
+
output: unknown;
|
|
270
|
+
isError?: boolean;
|
|
271
|
+
}): PromiseLike<void>;
|
|
272
|
+
/**
|
|
273
|
+
* Respond to a `tool-approval-request` the adapter emitted.
|
|
274
|
+
*/
|
|
275
|
+
submitToolApproval?(input: {
|
|
276
|
+
approvalId: string;
|
|
277
|
+
approved: boolean;
|
|
278
|
+
reason?: string;
|
|
279
|
+
}): PromiseLike<void>;
|
|
280
|
+
/**
|
|
281
|
+
* Inject a fresh user message into a turn that is still in flight.
|
|
282
|
+
* Supported only by runtimes that accept interactive input.
|
|
283
|
+
*/
|
|
284
|
+
submitUserMessage?(text: string): PromiseLike<void>;
|
|
285
|
+
/**
|
|
286
|
+
* Resolves when the adapter has finished the turn (success or failure).
|
|
287
|
+
* Rejects with the underlying error when the turn fails.
|
|
288
|
+
*/
|
|
289
|
+
readonly done: PromiseLike<void>;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
type HarnessV1PendingToolApproval = {
|
|
293
|
+
readonly approvalId: string;
|
|
294
|
+
readonly toolCallId: string;
|
|
295
|
+
readonly toolName: string;
|
|
296
|
+
readonly input: string;
|
|
297
|
+
readonly kind: 'builtin' | 'custom';
|
|
298
|
+
readonly providerExecuted?: boolean;
|
|
299
|
+
readonly nativeName?: string;
|
|
300
|
+
};
|
|
301
|
+
/**
|
|
302
|
+
* Opaque payload returned by resumable session lifecycle methods and accepted
|
|
303
|
+
* by a future `HarnessV1.doStart({ resumeFrom })` to resume the same
|
|
304
|
+
* underlying session.
|
|
305
|
+
*
|
|
306
|
+
* The contents are entirely adapter-defined. Consumers (including
|
|
307
|
+
* `HarnessAgent`) treat the value as opaque; adapters describe and validate
|
|
308
|
+
* their own schemas via `HarnessV1.resumeStateSchema`.
|
|
309
|
+
*/
|
|
310
|
+
type HarnessV1ResumeState = {
|
|
311
|
+
/**
|
|
312
|
+
* Identifier of the harness that produced this state. Used by adapters to
|
|
313
|
+
* refuse mismatched resume payloads.
|
|
314
|
+
*/
|
|
315
|
+
readonly harnessId: string;
|
|
316
|
+
/**
|
|
317
|
+
* Spec version of the harness that produced this state.
|
|
318
|
+
*/
|
|
319
|
+
readonly specificationVersion: 'harness-v1';
|
|
320
|
+
/**
|
|
321
|
+
* Adapter-defined payload. May be persisted as JSON; the adapter is
|
|
322
|
+
* responsible for any necessary encoding.
|
|
323
|
+
*/
|
|
324
|
+
readonly data: JSONValue;
|
|
325
|
+
/**
|
|
326
|
+
* Framework-owned pending approval records. These are intentionally outside
|
|
327
|
+
* adapter-defined `data` so callers can persist the entire resume payload
|
|
328
|
+
* without the harness framework owning storage.
|
|
329
|
+
*/
|
|
330
|
+
readonly pendingToolApprovals?: readonly HarnessV1PendingToolApproval[];
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* A self-contained instruction bundle the underlying runtime can load into
|
|
335
|
+
* its context. Adapters decide how to surface skills to the runtime — the
|
|
336
|
+
* `claude` CLI auto-discovers skills materialised as Markdown files in
|
|
337
|
+
* `.claude/skills`, while the `codex` CLI has no skill mechanism and the
|
|
338
|
+
* adapter inlines them into every user message.
|
|
339
|
+
*/
|
|
340
|
+
type HarnessV1Skill = {
|
|
341
|
+
/** Stable identifier for the skill (kebab-case slug). */
|
|
342
|
+
readonly name: string;
|
|
343
|
+
/**
|
|
344
|
+
* Short, model-facing description. For runtimes that auto-select skills
|
|
345
|
+
* (Claude Code), this is what the runtime sees to decide whether the
|
|
346
|
+
* skill is relevant; for runtimes that load every skill on every turn
|
|
347
|
+
* (Codex), it appears alongside the content.
|
|
348
|
+
*/
|
|
349
|
+
readonly description: string;
|
|
350
|
+
/** Full skill content the model loads when the skill is active. */
|
|
351
|
+
readonly content: string;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Warning emitted by a harness adapter during a call.
|
|
356
|
+
*
|
|
357
|
+
* Surfaces non-fatal issues such as unsupported options or quirks of the
|
|
358
|
+
* underlying agent runtime. Mirrors the shape of `SharedV4Warning` but lives
|
|
359
|
+
* in the harness namespace.
|
|
360
|
+
*/
|
|
361
|
+
type HarnessV1CallWarning = {
|
|
362
|
+
type: 'unsupported-setting';
|
|
363
|
+
setting: string;
|
|
364
|
+
details?: string;
|
|
365
|
+
} | {
|
|
366
|
+
type: 'unsupported-tool';
|
|
367
|
+
tool: string;
|
|
368
|
+
details?: string;
|
|
369
|
+
} | {
|
|
370
|
+
type: 'other';
|
|
371
|
+
message: string;
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Adapter-namespaced opaque data attached to harness events.
|
|
376
|
+
*
|
|
377
|
+
* Mirrors the `providerMetadata` pattern from `@ai-sdk/provider`, but lives
|
|
378
|
+
* in the harness namespace because a harness is a peer concept to a
|
|
379
|
+
* provider, not a kind of provider.
|
|
380
|
+
*
|
|
381
|
+
* Keys are harness ids (e.g. `'claude-code'`). Inner values are arbitrary
|
|
382
|
+
* JSON-serializable data the adapter chooses to surface to callers.
|
|
383
|
+
*/
|
|
384
|
+
type HarnessV1Metadata = Record<string, Record<string, JSONValue>>;
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* One event emitted by a harness adapter during a prompt turn.
|
|
388
|
+
*
|
|
389
|
+
* Mirrors `LanguageModelV4StreamPart` on the variants it shares so a
|
|
390
|
+
* `HarnessAgent` can pipe events through to AI SDK consumers with minimal
|
|
391
|
+
* translation. Primitive types from the V4 spec (`LanguageModelV4ToolCall`,
|
|
392
|
+
* `LanguageModelV4ToolResult`, `LanguageModelV4ToolApprovalRequest`,
|
|
393
|
+
* `LanguageModelV4Usage`, `LanguageModelV4FinishReason`) are reused
|
|
394
|
+
* verbatim — type-compat tests assert this stays the case.
|
|
395
|
+
*
|
|
396
|
+
* The metadata field is named `harnessMetadata` (not `providerMetadata`)
|
|
397
|
+
* because a harness is a peer to a provider, not a kind of provider. The
|
|
398
|
+
* agent rebinds it when forwarding to AI SDK consumers.
|
|
399
|
+
*/
|
|
400
|
+
type HarnessV1StreamPart = {
|
|
401
|
+
type: 'stream-start';
|
|
402
|
+
warnings?: ReadonlyArray<HarnessV1CallWarning>;
|
|
403
|
+
/**
|
|
404
|
+
* The model the runtime actually resolved to for this turn, when the
|
|
405
|
+
* adapter learns it at stream start (e.g. Claude Code's `init` message
|
|
406
|
+
* reports the resolved/default model). Surfaced into telemetry as
|
|
407
|
+
* `gen_ai.request.model`. Omitted when the adapter doesn't know it here.
|
|
408
|
+
*/
|
|
409
|
+
modelId?: string;
|
|
410
|
+
} | {
|
|
411
|
+
type: 'text-start';
|
|
412
|
+
id: string;
|
|
413
|
+
harnessMetadata?: HarnessV1Metadata;
|
|
414
|
+
} | {
|
|
415
|
+
type: 'text-delta';
|
|
416
|
+
id: string;
|
|
417
|
+
delta: string;
|
|
418
|
+
harnessMetadata?: HarnessV1Metadata;
|
|
419
|
+
} | {
|
|
420
|
+
type: 'text-end';
|
|
421
|
+
id: string;
|
|
422
|
+
harnessMetadata?: HarnessV1Metadata;
|
|
423
|
+
} | {
|
|
424
|
+
type: 'reasoning-start';
|
|
425
|
+
id: string;
|
|
426
|
+
harnessMetadata?: HarnessV1Metadata;
|
|
427
|
+
} | {
|
|
428
|
+
type: 'reasoning-delta';
|
|
429
|
+
id: string;
|
|
430
|
+
delta: string;
|
|
431
|
+
harnessMetadata?: HarnessV1Metadata;
|
|
432
|
+
} | {
|
|
433
|
+
type: 'reasoning-end';
|
|
434
|
+
id: string;
|
|
435
|
+
harnessMetadata?: HarnessV1Metadata;
|
|
436
|
+
} | (LanguageModelV4ToolCall & {
|
|
437
|
+
nativeName?: string;
|
|
438
|
+
}) | LanguageModelV4ToolApprovalRequest | LanguageModelV4ToolResult | {
|
|
439
|
+
type: 'finish-step';
|
|
440
|
+
finishReason: LanguageModelV4FinishReason;
|
|
441
|
+
usage: LanguageModelV4Usage;
|
|
442
|
+
harnessMetadata?: HarnessV1Metadata;
|
|
443
|
+
} | {
|
|
444
|
+
type: 'finish';
|
|
445
|
+
finishReason: LanguageModelV4FinishReason;
|
|
446
|
+
totalUsage: LanguageModelV4Usage;
|
|
447
|
+
harnessMetadata?: HarnessV1Metadata;
|
|
448
|
+
} | {
|
|
449
|
+
type: 'file-change';
|
|
450
|
+
event: 'create' | 'modify' | 'delete';
|
|
451
|
+
path: string;
|
|
452
|
+
harnessMetadata?: HarnessV1Metadata;
|
|
453
|
+
} | {
|
|
454
|
+
type: 'compaction';
|
|
455
|
+
trigger: 'manual' | 'auto';
|
|
456
|
+
summary: string;
|
|
457
|
+
tokensBefore?: number;
|
|
458
|
+
tokensAfter?: number;
|
|
459
|
+
harnessMetadata?: HarnessV1Metadata;
|
|
460
|
+
} | {
|
|
461
|
+
type: 'error';
|
|
462
|
+
error: unknown;
|
|
463
|
+
} | {
|
|
464
|
+
type: 'raw';
|
|
465
|
+
rawValue: unknown;
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Description of a host-defined tool that the harness should make available
|
|
470
|
+
* to the underlying agent runtime.
|
|
471
|
+
*
|
|
472
|
+
* Adapters translate this into whatever shape their runtime expects (e.g.
|
|
473
|
+
* Claude Code's tool definitions, Codex CLI's tool config, an MCP server
|
|
474
|
+
* exposed to the runtime, …). The adapter does not execute the tool; when
|
|
475
|
+
* the runtime calls it, the adapter emits a `tool-call` event and waits for
|
|
476
|
+
* `submitToolResult` from the caller.
|
|
477
|
+
*/
|
|
478
|
+
type HarnessV1ToolSpec = {
|
|
479
|
+
/**
|
|
480
|
+
* Tool name the agent runtime sees. Must match the name on incoming
|
|
481
|
+
* `tool-call` events.
|
|
482
|
+
*/
|
|
483
|
+
readonly name: string;
|
|
484
|
+
/**
|
|
485
|
+
* Human-readable description handed to the runtime, used to help the model
|
|
486
|
+
* decide when to call the tool.
|
|
487
|
+
*/
|
|
488
|
+
readonly description?: string;
|
|
489
|
+
/**
|
|
490
|
+
* JSON Schema describing the expected input for the tool. Optional because
|
|
491
|
+
* some runtimes accept tools without schemas (free-form arguments).
|
|
492
|
+
*/
|
|
493
|
+
readonly inputSchema?: JSONSchema7;
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Options passed to `HarnessV1.doStart`.
|
|
498
|
+
*
|
|
499
|
+
* `sandboxSession` and `sessionWorkDir` are coupled and always present. The
|
|
500
|
+
* framework creates the sandbox and per-session working directory before
|
|
501
|
+
* calling the adapter, so adapters never need to derive provider-specific paths.
|
|
502
|
+
*/
|
|
503
|
+
type HarnessV1StartOptions = {
|
|
504
|
+
/**
|
|
505
|
+
* Stable identifier for this harness session. Used as the underlying
|
|
506
|
+
* resource name where the adapter has a notion of a named session
|
|
507
|
+
* (sandbox name, native session id, …).
|
|
508
|
+
*/
|
|
509
|
+
readonly sessionId: string;
|
|
510
|
+
/**
|
|
511
|
+
* Skills made available to the underlying runtime for the lifetime of
|
|
512
|
+
* the session. Adapters decide how to surface them — the `claude` CLI
|
|
513
|
+
* picks them up from `.claude/skills/*.md`, while the `codex` adapter
|
|
514
|
+
* inlines them into every user message.
|
|
515
|
+
*/
|
|
516
|
+
readonly skills?: ReadonlyArray<HarnessV1Skill>;
|
|
517
|
+
/**
|
|
518
|
+
* Optional resume payload returned by a prior session lifecycle method. When
|
|
519
|
+
* provided, the adapter should resume the existing session rather than create
|
|
520
|
+
* a fresh one.
|
|
521
|
+
*/
|
|
522
|
+
readonly resumeFrom?: HarnessV1ResumeState;
|
|
523
|
+
/**
|
|
524
|
+
* Approval policy for built-in adapter-native tool use. Custom host-executed
|
|
525
|
+
* tools are approved by the framework before results are submitted back to
|
|
526
|
+
* the adapter.
|
|
527
|
+
*/
|
|
528
|
+
readonly permissionMode?: HarnessV1PermissionMode;
|
|
529
|
+
/**
|
|
530
|
+
* Signal that aborts startup. The adapter must propagate cancellation to
|
|
531
|
+
* any spawned processes or network calls.
|
|
532
|
+
*/
|
|
533
|
+
readonly abortSignal?: AbortSignal;
|
|
534
|
+
/**
|
|
535
|
+
* Diagnostics wiring. The framework populates this; the adapter only
|
|
536
|
+
* forwards `observability.onDiagnostic` into its `SandboxChannel` and
|
|
537
|
+
* `observability.debug` into the bridge `start` message. Absent when the
|
|
538
|
+
* consumer has not enabled diagnostics.
|
|
539
|
+
*/
|
|
540
|
+
readonly observability?: HarnessV1Observability;
|
|
541
|
+
/**
|
|
542
|
+
* Network sandbox session the adapter operates against. It is owned and
|
|
543
|
+
* lifecycled by `HarnessAgent`. Adapters call `restricted()` for the
|
|
544
|
+
* tool-safe filesystem/exec/spawn surface, and use the infra methods
|
|
545
|
+
* (`getPortUrl`, `ports`, `setNetworkPolicy`) for bridge wiring. Adapters
|
|
546
|
+
* must not call `stop()` themselves; the agent does that during cleanup.
|
|
547
|
+
*/
|
|
548
|
+
readonly sandboxSession: HarnessV1NetworkSandboxSession;
|
|
549
|
+
/**
|
|
550
|
+
* Absolute path the adapter runs the agent in for this session. Composed by
|
|
551
|
+
* the framework as `<sandboxSession.defaultWorkingDirectory>/<harnessId>-<sessionId>`
|
|
552
|
+
* and created before `doStart`, so the adapter uses it directly instead of
|
|
553
|
+
* deriving its own provider-specific path.
|
|
554
|
+
*/
|
|
555
|
+
readonly sessionWorkDir: string;
|
|
556
|
+
};
|
|
557
|
+
/**
|
|
558
|
+
* Options passed to `HarnessV1Session.doPromptTurn`.
|
|
559
|
+
*/
|
|
560
|
+
type HarnessV1PromptTurnOptions = {
|
|
561
|
+
/**
|
|
562
|
+
* Fresh input for this turn — either a plain string or a single
|
|
563
|
+
* `ModelMessage`. The harness session owns its own conversation history,
|
|
564
|
+
* so prior turns are never replayed across the contract.
|
|
565
|
+
*/
|
|
566
|
+
readonly prompt: HarnessV1Prompt;
|
|
567
|
+
/**
|
|
568
|
+
* Host-defined tools to make available to the underlying runtime for this
|
|
569
|
+
* turn. The harness emits `tool-call` events when the runtime calls one
|
|
570
|
+
* and waits for `submitToolResult`.
|
|
571
|
+
*/
|
|
572
|
+
readonly tools?: ReadonlyArray<HarnessV1ToolSpec>;
|
|
573
|
+
/**
|
|
574
|
+
* Free-form instructions for the session. The framework supplies the same
|
|
575
|
+
* value on every turn; the adapter is responsible for applying it once, by
|
|
576
|
+
* prepending it to the first user message of a fresh (non-resumed) session.
|
|
577
|
+
* On a resumed session the adapter must not re-apply it — the original first
|
|
578
|
+
* message already carried it and lives in the runtime's persisted history.
|
|
579
|
+
*/
|
|
580
|
+
readonly instructions?: string;
|
|
581
|
+
/**
|
|
582
|
+
* Signal that aborts the in-flight turn. The adapter must cancel any
|
|
583
|
+
* underlying work and resolve `done` (with an error if appropriate).
|
|
584
|
+
*/
|
|
585
|
+
readonly abortSignal?: AbortSignal;
|
|
586
|
+
/**
|
|
587
|
+
* Callback invoked once for each event the adapter produces during the
|
|
588
|
+
* turn. The adapter is responsible for the ordering and completeness of
|
|
589
|
+
* events. `done` resolves once the adapter has emitted all events for the
|
|
590
|
+
* turn (success or failure).
|
|
591
|
+
*/
|
|
592
|
+
readonly emit: (event: HarnessV1StreamPart) => void;
|
|
593
|
+
};
|
|
594
|
+
/**
|
|
595
|
+
* Options passed to `HarnessV1Session.doContinueTurn`.
|
|
596
|
+
*
|
|
597
|
+
* Unlike `doPromptTurn`, there is no `prompt`: `doContinueTurn` continues the
|
|
598
|
+
* in-flight turn rather than starting a new one. It is used to continue a turn
|
|
599
|
+
* that was previously suspended temporarily, e.g. by the workflow slice loop.
|
|
600
|
+
*/
|
|
601
|
+
type HarnessV1ContinueTurnOptions = {
|
|
602
|
+
/**
|
|
603
|
+
* Host-defined tools to make available for the continued turn. Same shape
|
|
604
|
+
* as `doPromptTurn`'s `tools`. An adapter that purely attaches to a live turn
|
|
605
|
+
* may ignore them; an adapter that re-drives the turn (rerun) needs them.
|
|
606
|
+
*/
|
|
607
|
+
readonly tools?: ReadonlyArray<HarnessV1ToolSpec>;
|
|
608
|
+
/**
|
|
609
|
+
* Signal that aborts the continued turn. The adapter must cancel any
|
|
610
|
+
* underlying work and resolve `done` (with an error if appropriate).
|
|
611
|
+
*/
|
|
612
|
+
readonly abortSignal?: AbortSignal;
|
|
613
|
+
/**
|
|
614
|
+
* Callback invoked once for each event the adapter produces while the
|
|
615
|
+
* continued turn runs. Same contract as `doPromptTurn`'s `emit`.
|
|
616
|
+
*/
|
|
617
|
+
readonly emit: (event: HarnessV1StreamPart) => void;
|
|
618
|
+
};
|
|
619
|
+
/**
|
|
620
|
+
* Active harness session, returned by `HarnessV1.doStart`.
|
|
621
|
+
*
|
|
622
|
+
* A session is the unit of state continuity across multiple prompts (one
|
|
623
|
+
* sandbox, one conversation history, one running agent runtime). The host
|
|
624
|
+
* holds onto the session across `doPromptTurn` calls and ends the local
|
|
625
|
+
* instance via `doDetach`, `doStop`, or `doDestroy`.
|
|
626
|
+
*/
|
|
627
|
+
type HarnessV1Session = {
|
|
628
|
+
/**
|
|
629
|
+
* Stable identifier for this session. Same value the host passed in via
|
|
630
|
+
* `HarnessV1StartOptions.sessionId`.
|
|
631
|
+
*/
|
|
632
|
+
readonly sessionId: string;
|
|
633
|
+
/**
|
|
634
|
+
* Whether this session was created from a resume payload. Fresh sessions
|
|
635
|
+
* report `false`; resumed sessions report `true`.
|
|
636
|
+
*/
|
|
637
|
+
readonly isResume: boolean;
|
|
638
|
+
/**
|
|
639
|
+
* The model id the underlying runtime is configured to use, if the adapter
|
|
640
|
+
* knows it (e.g. from its settings). Surfaced into telemetry as
|
|
641
|
+
* `gen_ai.request.model` and the trace span labels. Omitted when the adapter
|
|
642
|
+
* defers to the runtime's own default and has no concrete id.
|
|
643
|
+
*/
|
|
644
|
+
readonly modelId?: string;
|
|
645
|
+
/**
|
|
646
|
+
* Run one prompt turn. Returns a control handle the host uses to feed
|
|
647
|
+
* tool results, approvals, and user messages back into the turn while it
|
|
648
|
+
* is in flight. The handle's `done` promise resolves when the turn ends.
|
|
649
|
+
*/
|
|
650
|
+
doPromptTurn(options: HarnessV1PromptTurnOptions): PromiseLike<HarnessV1PromptControl>;
|
|
651
|
+
/**
|
|
652
|
+
* Request that the underlying runtime compact its context. The runtime owns
|
|
653
|
+
* the compaction — the harness neither implements nor schedules it; this is
|
|
654
|
+
* only the trigger. When compaction completes, the adapter surfaces a
|
|
655
|
+
* `compaction` stream part on the next/active turn.
|
|
656
|
+
*
|
|
657
|
+
* Required, but not every runtime can honour it: adapters whose transport
|
|
658
|
+
* exposes no manual compaction (e.g. Codex over `codex exec`, which still
|
|
659
|
+
* auto-compacts on its own) throw `HarnessCapabilityUnsupportedError`.
|
|
660
|
+
* `customInstructions`, when supported, steer the compaction summary.
|
|
661
|
+
*/
|
|
662
|
+
doCompact(customInstructions?: string): PromiseLike<void>;
|
|
663
|
+
/**
|
|
664
|
+
* Continue the in-flight turn **without a new user prompt**, returning the
|
|
665
|
+
* same control surface as `doPromptTurn`. Used to keep consuming a turn that
|
|
666
|
+
* was interrupted at a process boundary (the workflow slice loop), after the
|
|
667
|
+
* session itself has been resumed via `doStart({ resumeFrom })`:
|
|
668
|
+
*
|
|
669
|
+
* - When the runtime's turn is still live and reachable (bridge `attach` /
|
|
670
|
+
* `replay`), the adapter subscribes to its events and resolves `done` on
|
|
671
|
+
* the turn's `finish` — **without** re-driving it. Lossless.
|
|
672
|
+
* - When the live turn is gone (bridge respawned `rerun`, or a host-resident
|
|
673
|
+
* runtime like Pi whose turn cannot survive its process), the adapter
|
|
674
|
+
* re-drives the runtime's own thread from its persisted state. Lossy: work
|
|
675
|
+
* in flight at the interruption is recomputed.
|
|
676
|
+
*
|
|
677
|
+
* Required on every adapter. The behaviour an adapter can guarantee follows
|
|
678
|
+
* from its architecture; the contract is uniform.
|
|
679
|
+
*/
|
|
680
|
+
doContinueTurn(options: HarnessV1ContinueTurnOptions): PromiseLike<HarnessV1PromptControl>;
|
|
681
|
+
/**
|
|
682
|
+
* Gracefully freeze the active turn **at a precise cursor while keeping the
|
|
683
|
+
* runtime alive**, returning the resume payload.
|
|
684
|
+
*
|
|
685
|
+
* This is the slice-boundary primitive. The adapter stops host-side
|
|
686
|
+
* consumption of the in-flight turn without telling the runtime to stop:
|
|
687
|
+
* for a bridge adapter it closes the host socket (the bridge keeps the turn
|
|
688
|
+
* running and accumulates events for replay) and resolves the active
|
|
689
|
+
* `doPromptTurn`/`doContinueTurn` `done` **cleanly** (not as an error) once buffered
|
|
690
|
+
* events have drained, so the cursor in the returned state equals the last
|
|
691
|
+
* event delivered to the host — guaranteeing the next slice's attach replays
|
|
692
|
+
* with no gap and no duplicate. A host-resident adapter (Pi) cannot keep its
|
|
693
|
+
* turn alive, so it persists what it can and the in-flight tail is recomputed
|
|
694
|
+
* on continue.
|
|
695
|
+
*
|
|
696
|
+
* Like `doDetach`, the sandbox/runtime is left running. Unlike `doDetach`,
|
|
697
|
+
* this is for an active turn at a slice boundary rather than a between-turn
|
|
698
|
+
* session handoff. Required on every adapter.
|
|
699
|
+
*/
|
|
700
|
+
doSuspendTurn(): PromiseLike<HarnessV1ResumeState>;
|
|
701
|
+
/**
|
|
702
|
+
* Detach from the underlying runtime without tearing it down, returning a
|
|
703
|
+
* payload the host can later pass to `HarnessV1.doStart({ resumeFrom })`
|
|
704
|
+
* to reconnect. After `doDetach`, no further methods on this session
|
|
705
|
+
* instance may be called.
|
|
706
|
+
*
|
|
707
|
+
* Required. Adapters that cannot keep a live runtime parked still return the
|
|
708
|
+
* best resume state they can while leaving the sandbox running.
|
|
709
|
+
*/
|
|
710
|
+
doDetach(): PromiseLike<HarnessV1ResumeState>;
|
|
711
|
+
/**
|
|
712
|
+
* Persist enough state to resume later, then stop the underlying runtime.
|
|
713
|
+
* After `doStop`, no further methods on this session instance may be called.
|
|
714
|
+
*/
|
|
715
|
+
doStop(): PromiseLike<HarnessV1ResumeState>;
|
|
716
|
+
/**
|
|
717
|
+
* Stop the underlying runtime without returning resume state. After
|
|
718
|
+
* `doDestroy`, no further methods on this session instance may be called.
|
|
719
|
+
*/
|
|
720
|
+
doDestroy(): PromiseLike<void>;
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Versioned specification for a harness adapter — the integration point for
|
|
725
|
+
* one third-party coding-agent runtime (Claude Code, Codex, …).
|
|
726
|
+
*
|
|
727
|
+
* Modelled after `LanguageModelV4`: a tagged spec version, a small set of
|
|
728
|
+
* descriptive fields, and one entry-point method (`doStart`) that yields a
|
|
729
|
+
* session. There is intentionally no static "capabilities" object —
|
|
730
|
+
* optional features are signalled by the presence or absence of optional
|
|
731
|
+
* methods on the prompt-control handle. Adapters that cannot satisfy a request
|
|
732
|
+
* (manual compaction not supported, required port exposure unavailable, …)
|
|
733
|
+
* throw `HarnessCapabilityUnsupportedError` from the method that needs the
|
|
734
|
+
* capability.
|
|
735
|
+
*/
|
|
736
|
+
type HarnessV1<TBuiltinTools extends ToolSet = ToolSet> = {
|
|
737
|
+
/**
|
|
738
|
+
* Spec version this adapter implements. Always the literal `'harness-v1'`.
|
|
739
|
+
*/
|
|
740
|
+
readonly specificationVersion: 'harness-v1';
|
|
741
|
+
/**
|
|
742
|
+
* Stable identifier for this harness, used as the key inside
|
|
743
|
+
* `HarnessV1Metadata` objects. Conventionally a kebab-case slug matching
|
|
744
|
+
* the package name (`'claude-code'`, `'codex'`).
|
|
745
|
+
*/
|
|
746
|
+
readonly harnessId: string;
|
|
747
|
+
/**
|
|
748
|
+
* Tools the adapter's underlying runtime exposes natively, as a `ToolSet`
|
|
749
|
+
* keyed by what the bridge emits on `tool-call` events
|
|
750
|
+
* (`commonName ?? nativeName`). Each entry is a `HarnessV1BuiltinTool`
|
|
751
|
+
* (a `Tool` plus harness-specific `nativeName` / `commonName` metadata).
|
|
752
|
+
*
|
|
753
|
+
* The agent merges this with consumer-supplied user tools when validating
|
|
754
|
+
* inbound tool calls and when typing the consumer-facing stream.
|
|
755
|
+
*/
|
|
756
|
+
readonly builtinTools: TBuiltinTools;
|
|
757
|
+
/**
|
|
758
|
+
* Whether the adapter can emit approval requests for built-in tools when
|
|
759
|
+
* `permissionMode` is not `'allow-all'`.
|
|
760
|
+
*
|
|
761
|
+
* Custom host-executed tool approvals are handled by `HarnessAgent`, so this
|
|
762
|
+
* only describes adapter-native tool approval support.
|
|
763
|
+
*/
|
|
764
|
+
readonly supportsBuiltinToolApprovals?: boolean;
|
|
765
|
+
/**
|
|
766
|
+
* Optional schema for resume payloads returned by the session lifecycle.
|
|
767
|
+
* When present, the adapter promises that exported state validated by this
|
|
768
|
+
* schema can be re-imported in a future `doStart({ resumeFrom })` call.
|
|
769
|
+
* Hosts use this to persist and re-hydrate resume payloads safely.
|
|
770
|
+
*/
|
|
771
|
+
readonly resumeStateSchema?: FlexibleSchema<unknown>;
|
|
772
|
+
/**
|
|
773
|
+
* Optional bootstrap recipe. When defined, the harness session manager
|
|
774
|
+
* computes a stable identity from the recipe, passes it (along with a
|
|
775
|
+
* one-time recipe-application hook) to the sandbox provider, and applies
|
|
776
|
+
* the recipe idempotently after the provider returns the handle.
|
|
777
|
+
*
|
|
778
|
+
* Adapters with no bootstrap needs omit this. Adapters that need to install
|
|
779
|
+
* deps or ship bridge files into the sandbox declare them here so the
|
|
780
|
+
* provider can cache the result across sessions via snapshots when
|
|
781
|
+
* supported.
|
|
782
|
+
*/
|
|
783
|
+
readonly getBootstrap?: (options?: {
|
|
784
|
+
abortSignal?: AbortSignal;
|
|
785
|
+
}) => PromiseLike<HarnessV1Bootstrap>;
|
|
786
|
+
/**
|
|
787
|
+
* Start a fresh session (or resume via `resumeFrom`). The host then issues
|
|
788
|
+
* prompts against the returned session, ending with `doDetach`, `doStop`, or
|
|
789
|
+
* `doDestroy`.
|
|
790
|
+
*/
|
|
791
|
+
doStart(options: HarnessV1StartOptions): PromiseLike<HarnessV1Session>;
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Provider that produces network sandbox sessions for harness sessions. Lives at
|
|
796
|
+
* module scope as a stable, synchronous object — analogous to
|
|
797
|
+
* `LanguageModelV4` providers, no I/O performed at construction. The actual
|
|
798
|
+
* sandbox is created (or wrapped) when `HarnessAgent` calls `createSession()`.
|
|
799
|
+
*/
|
|
800
|
+
interface HarnessV1SandboxProvider {
|
|
801
|
+
readonly specificationVersion: 'harness-sandbox-v1';
|
|
802
|
+
readonly providerId: string;
|
|
803
|
+
/**
|
|
804
|
+
* Pool of ports the consumer reserved on a caller-provided sandbox for
|
|
805
|
+
* concurrent harness sessions. The session manager leases one port per
|
|
806
|
+
* session and releases on stop or destroy.
|
|
807
|
+
*
|
|
808
|
+
* Only meaningful when the provider wraps a caller-provided sandbox
|
|
809
|
+
* (the caller pre-declared the ports). In create-new modes the provider
|
|
810
|
+
* mints a fresh sandbox per session, so no leasing is needed; providers
|
|
811
|
+
* leave this undefined.
|
|
812
|
+
*/
|
|
813
|
+
readonly bridgePorts?: ReadonlyArray<number>;
|
|
814
|
+
readonly createSession: (options?: {
|
|
815
|
+
/**
|
|
816
|
+
* Stable per-session identifier. When supplied, the provider names the
|
|
817
|
+
* underlying resource deterministically so a future call to `resume`
|
|
818
|
+
* (potentially from a different process) can find the same sandbox.
|
|
819
|
+
* Omitted from prewarm and other paths that don't need a resumable
|
|
820
|
+
* resource — in that case the provider falls back to its native
|
|
821
|
+
* auto-naming.
|
|
822
|
+
*/
|
|
823
|
+
sessionId?: string;
|
|
824
|
+
abortSignal?: AbortSignal;
|
|
825
|
+
/**
|
|
826
|
+
* Stable identity for snapshot-based reuse. Providers that support
|
|
827
|
+
* persistence/snapshots use this as part of the persistent sandbox
|
|
828
|
+
* name; subsequent calls with the same identity resume from snapshot.
|
|
829
|
+
*
|
|
830
|
+
* Ignored when the provider is wrapping a caller-provided sandbox.
|
|
831
|
+
*/
|
|
832
|
+
identity?: string;
|
|
833
|
+
/**
|
|
834
|
+
* Called exactly once per identity, on fresh creation. Snapshot-capable
|
|
835
|
+
* providers wire this into the platform's one-time-setup hook so the
|
|
836
|
+
* side effects are baked into the snapshot. Providers without snapshot
|
|
837
|
+
* support run it immediately after fresh create.
|
|
838
|
+
*
|
|
839
|
+
* Not called when the provider is wrapping a caller-provided sandbox
|
|
840
|
+
* (the caller owns the sandbox; the framework applies its own
|
|
841
|
+
* idempotent bootstrap post-create instead).
|
|
842
|
+
*/
|
|
843
|
+
onFirstCreate?: (session: Experimental_SandboxSession, opts: {
|
|
844
|
+
abortSignal?: AbortSignal;
|
|
845
|
+
}) => Promise<void>;
|
|
846
|
+
}) => PromiseLike<HarnessV1NetworkSandboxSession>;
|
|
847
|
+
/**
|
|
848
|
+
* Reattach to an existing sandbox previously created with the same
|
|
849
|
+
* `sessionId`. Optional — providers that cannot rehydrate by id (e.g.
|
|
850
|
+
* just-bash) omit this; the harness throws
|
|
851
|
+
* `HarnessCapabilityUnsupportedError` when resume is attempted against
|
|
852
|
+
* them.
|
|
853
|
+
*
|
|
854
|
+
* The provider derives the sandbox identifier from `sessionId` using the
|
|
855
|
+
* same deterministic naming scheme it used in `createSession`. Returns a
|
|
856
|
+
* network sandbox session bound to the existing resource.
|
|
857
|
+
*/
|
|
858
|
+
readonly resumeSession?: (options: {
|
|
859
|
+
sessionId: string;
|
|
860
|
+
abortSignal?: AbortSignal;
|
|
861
|
+
}) => PromiseLike<HarnessV1NetworkSandboxSession>;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/** Severity of a diagnostic. */
|
|
865
|
+
type HarnessDebugLevel = 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
|
866
|
+
/**
|
|
867
|
+
* Consumer-facing diagnostics configuration. Set on `HarnessAgentSettings` to
|
|
868
|
+
* enable bridge log forwarding and the `HARNESS_DEBUG` stderr default in code.
|
|
869
|
+
* `HARNESS_DEBUG` / `HARNESS_DEBUG_LEVEL` / `HARNESS_DEBUG_SUBSYSTEMS` env vars
|
|
870
|
+
* fill any unset field — a convenience default, never the only path.
|
|
871
|
+
*/
|
|
872
|
+
type HarnessDebugConfig = {
|
|
873
|
+
/** Master switch. Nothing is captured or forwarded when false/unset. */
|
|
874
|
+
readonly enabled?: boolean;
|
|
875
|
+
/** Threshold; events at or above this severity are emitted. Default `debug`. */
|
|
876
|
+
readonly level?: HarnessDebugLevel;
|
|
877
|
+
/** Dotted-prefix subsystem filter for structured events. */
|
|
878
|
+
readonly subsystems?: ReadonlyArray<string>;
|
|
879
|
+
};
|
|
880
|
+
/**
|
|
881
|
+
* A forwarded bridge diagnostic, normalized for host consumers.
|
|
882
|
+
*
|
|
883
|
+
* The bridge emits two raw frame kinds — captured console lines (`sandbox-log`)
|
|
884
|
+
* and structured events (`debug-event`). The framework normalizes both into
|
|
885
|
+
* this single shape before handing them to a consumer's `onLog` callback, the
|
|
886
|
+
* `HARNESS_DEBUG` stderr default, and observability reporters. Diagnostics are
|
|
887
|
+
* kept first-class and per-line — they are never folded into telemetry spans.
|
|
888
|
+
*/
|
|
889
|
+
type HarnessDiagnostic = {
|
|
890
|
+
/**
|
|
891
|
+
* Severity. Structured events carry their own level; captured console lines
|
|
892
|
+
* map `stderr` → `'warn'` and `stdout` → `'info'`.
|
|
893
|
+
*/
|
|
894
|
+
readonly level: HarnessDebugLevel;
|
|
895
|
+
/** Human-readable line (console capture) or message (structured event). */
|
|
896
|
+
readonly message: string;
|
|
897
|
+
/**
|
|
898
|
+
* Dotted subsystem. For captured console output this is
|
|
899
|
+
* `sandbox.log.<source>`; for structured events it is the adapter-supplied
|
|
900
|
+
* subsystem (e.g. `bridge.turn`).
|
|
901
|
+
*/
|
|
902
|
+
readonly subsystem: string;
|
|
903
|
+
/** `'log'` = captured console line; `'event'` = structured `bridgeLog`. */
|
|
904
|
+
readonly kind: 'log' | 'event';
|
|
905
|
+
/** Originating sandbox source label (console capture). */
|
|
906
|
+
readonly source?: string;
|
|
907
|
+
/** Which standard stream the line came from (console capture). */
|
|
908
|
+
readonly stream?: 'stdout' | 'stderr';
|
|
909
|
+
/** Structured attributes (structured events only). */
|
|
910
|
+
readonly attrs?: Record<string, unknown>;
|
|
911
|
+
/** Error payload (structured events only). */
|
|
912
|
+
readonly error?: {
|
|
913
|
+
name?: string;
|
|
914
|
+
message: string;
|
|
915
|
+
stack?: string;
|
|
916
|
+
};
|
|
917
|
+
/** The harness session this diagnostic originated from. */
|
|
918
|
+
readonly sessionId?: string;
|
|
919
|
+
/** Host receipt time (epoch ms). */
|
|
920
|
+
readonly timestamp: number;
|
|
921
|
+
};
|
|
922
|
+
/**
|
|
923
|
+
* A telemetry integration that also wants the per-line diagnostics stream. The
|
|
924
|
+
* framework calls `ingestDiagnostic` for every forwarded bridge diagnostic in
|
|
925
|
+
* addition to driving the standard `Telemetry` span lifecycle, so a single
|
|
926
|
+
* reporter object (e.g. `createFileReporter`) registered in
|
|
927
|
+
* `telemetry.integrations` receives both spans and logs.
|
|
928
|
+
*/
|
|
929
|
+
interface HarnessDiagnosticConsumer {
|
|
930
|
+
ingestDiagnostic?(diagnostic: HarnessDiagnostic): void;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
type HarnessAgentToolApprovalConfiguration = Readonly<Record<string, ToolApprovalStatus>>;
|
|
934
|
+
/**
|
|
935
|
+
* Construction-time settings for a `HarnessAgent`.
|
|
936
|
+
*
|
|
937
|
+
* Per-call settings (prompt, abortSignal, callbacks) belong on the
|
|
938
|
+
* `AgentCallParameters` / `AgentStreamParameters` passed to `generate` /
|
|
939
|
+
* `stream` and are not duplicated here.
|
|
940
|
+
*/
|
|
941
|
+
type HarnessAgentSettings<THarness extends HarnessV1<any> = HarnessV1, TUserTools extends ToolSet = {}> = {
|
|
942
|
+
/**
|
|
943
|
+
* The harness adapter driving the underlying agent runtime. Its
|
|
944
|
+
* `builtinTools` are merged with the user-defined `tools` and exposed to
|
|
945
|
+
* AI SDK consumers in the typed `tool-call` stream.
|
|
946
|
+
*/
|
|
947
|
+
readonly harness: THarness;
|
|
948
|
+
/**
|
|
949
|
+
* Stable identifier for this agent instance. Exposed via `agent.id`.
|
|
950
|
+
* If omitted, `agent.id` is `undefined`.
|
|
951
|
+
*/
|
|
952
|
+
readonly id?: string;
|
|
953
|
+
/**
|
|
954
|
+
* Tools available to the underlying runtime in addition to the harness's
|
|
955
|
+
* own builtins. The agent forwards each tool to the harness as a
|
|
956
|
+
* `HarnessV1ToolSpec`; when the runtime calls one, the agent executes
|
|
957
|
+
* `tool.execute()` on the host and submits the result back to the harness.
|
|
958
|
+
*
|
|
959
|
+
* User tools take precedence over harness builtins on key collision —
|
|
960
|
+
* declare a tool with the same name as a builtin to override.
|
|
961
|
+
*/
|
|
962
|
+
readonly tools?: TUserTools;
|
|
963
|
+
/**
|
|
964
|
+
* Skills made available to the underlying runtime for the lifetime of
|
|
965
|
+
* the session. Each adapter decides how to surface skills (file in the
|
|
966
|
+
* working tree, prompt prefix, …).
|
|
967
|
+
*/
|
|
968
|
+
readonly skills?: ReadonlyArray<HarnessV1Skill>;
|
|
969
|
+
/**
|
|
970
|
+
* Instructions for the underlying agent runtime. Adapters prepend this to
|
|
971
|
+
* the first user message of a fresh session, once — it is not re-applied on
|
|
972
|
+
* later turns or when resuming a previously ended session.
|
|
973
|
+
*/
|
|
974
|
+
readonly instructions?: string;
|
|
975
|
+
/**
|
|
976
|
+
* Built-in tool permission mode. Defaults to `'allow-all'`, preserving the
|
|
977
|
+
* existing bypass-permissions behavior unless users opt in.
|
|
978
|
+
*/
|
|
979
|
+
readonly permissionMode?: HarnessV1PermissionMode;
|
|
980
|
+
/**
|
|
981
|
+
* Per custom-tool approval statuses. This mirrors AI SDK `toolApproval`
|
|
982
|
+
* object configuration for host-executed tools, without callback support.
|
|
983
|
+
*
|
|
984
|
+
* `not-applicable` and `approved` run the tool, `user-approval` pauses the
|
|
985
|
+
* turn for a user decision, and `denied` immediately submits an
|
|
986
|
+
* `execution-denied` result.
|
|
987
|
+
*/
|
|
988
|
+
readonly toolApproval?: HarnessAgentToolApprovalConfiguration;
|
|
989
|
+
/**
|
|
990
|
+
* Sandbox provider whose `create()` produces the network sandbox session the
|
|
991
|
+
* harness runs against. Its `restricted()` view is also propagated to user
|
|
992
|
+
* tool `execute()` calls (as the `experimental_sandbox` field), typed as
|
|
993
|
+
* `Experimental_SandboxSession` so tools cannot reach the infra surface.
|
|
994
|
+
*/
|
|
995
|
+
readonly sandbox: HarnessV1SandboxProvider;
|
|
996
|
+
/**
|
|
997
|
+
* Called after each sandbox session is acquired and the session work
|
|
998
|
+
* directory exists, before the harness adapter starts. Runs for fresh and
|
|
999
|
+
* resumed sessions.
|
|
1000
|
+
*
|
|
1001
|
+
* Use this to write per-session config, install lightweight tools, activate
|
|
1002
|
+
* licenses, or prepare files in `sessionWorkDir`. Keep it idempotent if the
|
|
1003
|
+
* agent may resume sessions.
|
|
1004
|
+
*/
|
|
1005
|
+
readonly onSandboxSession?: (opts: {
|
|
1006
|
+
readonly session: Experimental_SandboxSession;
|
|
1007
|
+
readonly sessionWorkDir: string;
|
|
1008
|
+
readonly abortSignal?: AbortSignal;
|
|
1009
|
+
}) => Promise<void>;
|
|
1010
|
+
/**
|
|
1011
|
+
* Telemetry configuration. The harness drives AI SDK's pluggable
|
|
1012
|
+
* `Telemetry` integration contract from the turn lifecycle, so a harness turn
|
|
1013
|
+
* appears in a consumer's traces with the same span shape as `streamText`.
|
|
1014
|
+
* Register an integration here (e.g. `@ai-sdk/otel`) or globally via
|
|
1015
|
+
* `registerTelemetry`. The harness itself stays OpenTelemetry-agnostic.
|
|
1016
|
+
*/
|
|
1017
|
+
readonly telemetry?: TelemetryOptions;
|
|
1018
|
+
/**
|
|
1019
|
+
* Diagnostics configuration. Enables bridge log forwarding (sandbox
|
|
1020
|
+
* console + structured `debug-event`s) and the `HARNESS_DEBUG` stderr default.
|
|
1021
|
+
* Set `{ enabled: true }` to turn it on in code; env vars fill unset fields.
|
|
1022
|
+
*/
|
|
1023
|
+
readonly debug?: HarnessDebugConfig;
|
|
1024
|
+
/**
|
|
1025
|
+
* Programmatic sink for forwarded bridge diagnostics. Receives every
|
|
1026
|
+
* captured console line and structured event, normalized. Independent of the
|
|
1027
|
+
* stderr default — wire this to capture diagnostics in code.
|
|
1028
|
+
*/
|
|
1029
|
+
readonly onLog?: (event: HarnessDiagnostic) => void;
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
type HarnessAgentToolApprovalContinuation = {
|
|
1033
|
+
readonly approvalResponse: ToolApprovalResponse;
|
|
1034
|
+
readonly toolCall: {
|
|
1035
|
+
readonly type: 'tool-call';
|
|
1036
|
+
readonly toolCallId: string;
|
|
1037
|
+
readonly toolName: string;
|
|
1038
|
+
readonly input: unknown;
|
|
1039
|
+
readonly providerExecuted?: boolean;
|
|
1040
|
+
};
|
|
1041
|
+
};
|
|
1042
|
+
/**
|
|
1043
|
+
* Extract approval decisions that should continue a suspended harness turn.
|
|
1044
|
+
*
|
|
1045
|
+
* AI SDK clients send approval decisions as a trailing `role: "tool"` message
|
|
1046
|
+
* containing `tool-approval-response` parts. The response only carries the
|
|
1047
|
+
* approval id, so the harness has to recover the matching approval request
|
|
1048
|
+
* locally to find the original tool call before it can resume the paused turn.
|
|
1049
|
+
* Responses that already have a tool result are ignored, because those
|
|
1050
|
+
* approvals were already consumed by a prior continuation.
|
|
1051
|
+
*/
|
|
1052
|
+
declare function collectHarnessAgentToolApprovalContinuations(input: {
|
|
1053
|
+
messages: readonly ModelMessage[];
|
|
1054
|
+
}): readonly HarnessAgentToolApprovalContinuation[];
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Concrete `StreamTextResult` implementation backed by a single
|
|
1058
|
+
* harness prompt turn.
|
|
1059
|
+
*
|
|
1060
|
+
* Wraps a `ReadableStream<TextStreamPart<TOOLS>>` that the calling
|
|
1061
|
+
* driver pushes events into. Every `PromiseLike` accessor is backed by a
|
|
1062
|
+
* `DelayedPromise` so the AI SDK consumer surface stays identical to
|
|
1063
|
+
* `streamText`'s — consumers can `await result.text` or iterate
|
|
1064
|
+
* `result.fullStream`, in either order.
|
|
1065
|
+
*
|
|
1066
|
+
* Each `finish-step` boundary in the driver translates to a `StepResult`
|
|
1067
|
+
* built via `DefaultStepResult`. Step content is accumulated as
|
|
1068
|
+
* `ContentPart[]` and fed straight to `DefaultStepResult`, which derives
|
|
1069
|
+
* `text`, `toolCalls`, `toolResults`, `reasoning`, etc. via its getters.
|
|
1070
|
+
*
|
|
1071
|
+
* The Node.js response helpers (`pipeUIMessageStreamToResponse`,
|
|
1072
|
+
* `pipeTextStreamToResponse`, `toTextStreamResponse`) and the
|
|
1073
|
+
* output-specification surfaces (`partialOutputStream`/`elementStream`) are not
|
|
1074
|
+
* implemented yet — they throw a clear error.
|
|
1075
|
+
*/
|
|
1076
|
+
declare class HarnessStreamTextResult<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context> implements StreamTextResult<TOOLS, RUNTIME_CONTEXT, never> {
|
|
1077
|
+
private readonly _content;
|
|
1078
|
+
private readonly _text;
|
|
1079
|
+
private readonly _reasoning;
|
|
1080
|
+
private readonly _reasoningText;
|
|
1081
|
+
private readonly _files;
|
|
1082
|
+
private readonly _sources;
|
|
1083
|
+
private readonly _toolCalls;
|
|
1084
|
+
private readonly _staticToolCalls;
|
|
1085
|
+
private readonly _dynamicToolCalls;
|
|
1086
|
+
private readonly _toolResults;
|
|
1087
|
+
private readonly _staticToolResults;
|
|
1088
|
+
private readonly _dynamicToolResults;
|
|
1089
|
+
private readonly _finishReason;
|
|
1090
|
+
private readonly _rawFinishReason;
|
|
1091
|
+
private readonly _usage;
|
|
1092
|
+
private readonly _warnings;
|
|
1093
|
+
private readonly _steps;
|
|
1094
|
+
private readonly _finalStep;
|
|
1095
|
+
private readonly _request;
|
|
1096
|
+
private readonly _response;
|
|
1097
|
+
private readonly _responseMessages;
|
|
1098
|
+
private readonly _providerMetadata;
|
|
1099
|
+
private readonly fullStreamController;
|
|
1100
|
+
readonly stream: AsyncIterableStream<TextStreamPart<TOOLS>>;
|
|
1101
|
+
readonly fullStream: AsyncIterableStream<TextStreamPart<TOOLS>>;
|
|
1102
|
+
readonly textStream: AsyncIterableStream<string>;
|
|
1103
|
+
private readonly stepsBuffer;
|
|
1104
|
+
private currentStepContent;
|
|
1105
|
+
private currentStepWarnings;
|
|
1106
|
+
private stepNumber;
|
|
1107
|
+
private readonly tools;
|
|
1108
|
+
private readonly runtimeContext;
|
|
1109
|
+
private readonly toolsContext;
|
|
1110
|
+
private readonly providerName;
|
|
1111
|
+
private readonly modelId;
|
|
1112
|
+
private accumulatedUsage;
|
|
1113
|
+
private finalProviderMetadata;
|
|
1114
|
+
private finalFinishReason;
|
|
1115
|
+
private finalRawFinishReason;
|
|
1116
|
+
private aggregateWarnings;
|
|
1117
|
+
private settled;
|
|
1118
|
+
constructor(options: {
|
|
1119
|
+
tools: TOOLS;
|
|
1120
|
+
runtimeContext: RUNTIME_CONTEXT;
|
|
1121
|
+
toolsContext: never;
|
|
1122
|
+
harnessId: string;
|
|
1123
|
+
sessionId: string;
|
|
1124
|
+
});
|
|
1125
|
+
/**
|
|
1126
|
+
* Push a translated `TextStreamPart` into `fullStream` and accumulate it
|
|
1127
|
+
* into the current step's content array where applicable.
|
|
1128
|
+
*/
|
|
1129
|
+
enqueue(part: TextStreamPart<TOOLS>): void;
|
|
1130
|
+
/**
|
|
1131
|
+
* Mark the end of a step. Builds a `StepResult` from the accumulated
|
|
1132
|
+
* content and records it in the steps array. Accepts the V4-shaped
|
|
1133
|
+
* finish reason / usage the harness emits and normalizes to AI SDK's
|
|
1134
|
+
* flat shape internally.
|
|
1135
|
+
*/
|
|
1136
|
+
finishStep(input: {
|
|
1137
|
+
finishReason: LanguageModelV4FinishReason;
|
|
1138
|
+
usage: LanguageModelV4Usage;
|
|
1139
|
+
providerMetadata: ProviderMetadata | undefined;
|
|
1140
|
+
warnings: CallWarning[];
|
|
1141
|
+
}): void;
|
|
1142
|
+
/**
|
|
1143
|
+
* Resolve every delayed promise and close `fullStream`. Idempotent.
|
|
1144
|
+
*/
|
|
1145
|
+
finish(): Promise<void>;
|
|
1146
|
+
/**
|
|
1147
|
+
* Surface a fatal error as a stream `error` part + reject every delayed
|
|
1148
|
+
* promise so awaiting consumers stop hanging. Idempotent.
|
|
1149
|
+
*/
|
|
1150
|
+
fail(error: unknown): void;
|
|
1151
|
+
get content(): Promise<ContentPart<TOOLS>[]>;
|
|
1152
|
+
get text(): Promise<string>;
|
|
1153
|
+
get reasoning(): Promise<(ai.ReasoningOutput | ai.ReasoningFileOutput)[]>;
|
|
1154
|
+
get reasoningText(): Promise<string | undefined>;
|
|
1155
|
+
get files(): Promise<ai.Experimental_GeneratedImage[]>;
|
|
1156
|
+
get sources(): Promise<_ai_sdk_provider.LanguageModelV4Source[]>;
|
|
1157
|
+
get toolCalls(): Promise<ai.TypedToolCall<TOOLS>[]>;
|
|
1158
|
+
get staticToolCalls(): Promise<ai.StaticToolCall<TOOLS>[]>;
|
|
1159
|
+
get dynamicToolCalls(): Promise<ai.DynamicToolCall[]>;
|
|
1160
|
+
get toolResults(): Promise<ai.TypedToolResult<TOOLS>[]>;
|
|
1161
|
+
get staticToolResults(): Promise<ai.StaticToolResult<TOOLS>[]>;
|
|
1162
|
+
get dynamicToolResults(): Promise<ai.DynamicToolResult[]>;
|
|
1163
|
+
get finishReason(): Promise<FinishReason>;
|
|
1164
|
+
get rawFinishReason(): Promise<string | undefined>;
|
|
1165
|
+
get usage(): Promise<LanguageModelUsage>;
|
|
1166
|
+
get totalUsage(): Promise<LanguageModelUsage>;
|
|
1167
|
+
get warnings(): Promise<_ai_sdk_provider.SharedV4Warning[] | undefined>;
|
|
1168
|
+
get steps(): Promise<StepResult<TOOLS, RUNTIME_CONTEXT>[]>;
|
|
1169
|
+
get finalStep(): Promise<StepResult<TOOLS, RUNTIME_CONTEXT>>;
|
|
1170
|
+
get request(): Promise<ai.LanguageModelRequestMetadata>;
|
|
1171
|
+
get response(): Promise<ai.LanguageModelResponseMetadata>;
|
|
1172
|
+
get responseMessages(): Promise<(AssistantModelMessage | ToolModelMessage)[]>;
|
|
1173
|
+
get providerMetadata(): Promise<_ai_sdk_provider.SharedV4ProviderMetadata | undefined>;
|
|
1174
|
+
get experimental_partialOutputStream(): never;
|
|
1175
|
+
get partialOutputStream(): never;
|
|
1176
|
+
get elementStream(): never;
|
|
1177
|
+
get output(): never;
|
|
1178
|
+
consumeStream(): Promise<void>;
|
|
1179
|
+
toUIMessageStream<UI_MESSAGE extends UIMessage>({ originalMessages, generateMessageId, onFinish, messageMetadata, sendReasoning, sendSources, sendStart, sendFinish, onError, }?: UIMessageStreamOptions<UI_MESSAGE>): AsyncIterableStream<InferUIMessageChunk<UI_MESSAGE>>;
|
|
1180
|
+
pipeUIMessageStreamToResponse(): never;
|
|
1181
|
+
pipeTextStreamToResponse(): never;
|
|
1182
|
+
toUIMessageStreamResponse<UI_MESSAGE extends UIMessage>({ originalMessages, generateMessageId, onFinish, messageMetadata, sendReasoning, sendSources, sendStart, sendFinish, onError, ...init }?: ResponseInit & {
|
|
1183
|
+
consumeSseStream?: (options: {
|
|
1184
|
+
stream: ReadableStream<string>;
|
|
1185
|
+
}) => PromiseLike<void> | void;
|
|
1186
|
+
} & UIMessageStreamOptions<UI_MESSAGE>): Response;
|
|
1187
|
+
toTextStreamResponse(): never;
|
|
1188
|
+
private appendToCurrentStepContent;
|
|
1189
|
+
}
|
|
1190
|
+
type AsyncIterableStream<T> = ReadableStream<T> & AsyncIterable<T>;
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* Drive one prompt turn end-to-end:
|
|
1194
|
+
* - call `session.doPromptTurn` via `toHarnessStream`
|
|
1195
|
+
* - translate harness events to AI SDK `TextStreamPart`s and push into the
|
|
1196
|
+
* result object
|
|
1197
|
+
* - execute host-side user tools when their `tool-call` events arrive and
|
|
1198
|
+
* submit results back to the harness
|
|
1199
|
+
* - close the result when the harness signals `finish` (or on error)
|
|
1200
|
+
*
|
|
1201
|
+
* Returns the result synchronously after the stream is wired up; callers
|
|
1202
|
+
* await its `PromiseLike` accessors to observe completion.
|
|
1203
|
+
*/
|
|
1204
|
+
declare function runPrompt<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context>(input: {
|
|
1205
|
+
harness: HarnessV1;
|
|
1206
|
+
session: HarnessV1Session;
|
|
1207
|
+
/**
|
|
1208
|
+
* Turn entry point. `'prompt'` (default) starts a new turn from `prompt`;
|
|
1209
|
+
* `'continue'` continues the in-flight turn via `doContinueTurn` and ignores
|
|
1210
|
+
* `prompt`/`instructions`.
|
|
1211
|
+
*/
|
|
1212
|
+
mode?: 'prompt' | 'continue';
|
|
1213
|
+
/** Required for `mode: 'prompt'`; absent for `mode: 'continue'`. */
|
|
1214
|
+
prompt?: HarnessV1Prompt;
|
|
1215
|
+
instructions: string | undefined;
|
|
1216
|
+
tools: TOOLS;
|
|
1217
|
+
toolSpecs: HarnessV1ToolSpec[];
|
|
1218
|
+
sandboxSession: Experimental_SandboxSession;
|
|
1219
|
+
sessionWorkDir: string;
|
|
1220
|
+
runtimeContext: RUNTIME_CONTEXT;
|
|
1221
|
+
abortSignal: AbortSignal | undefined;
|
|
1222
|
+
telemetry?: TelemetryOptions | undefined;
|
|
1223
|
+
toolApproval?: HarnessAgentToolApprovalConfiguration | undefined;
|
|
1224
|
+
pendingToolApprovals?: readonly HarnessV1PendingToolApproval[];
|
|
1225
|
+
toolApprovalContinuations?: readonly HarnessAgentToolApprovalContinuation[] | undefined;
|
|
1226
|
+
onPendingToolApproval?: (approval: HarnessV1PendingToolApproval) => void;
|
|
1227
|
+
onToolApprovalSettled?: (approvalId: string) => void;
|
|
1228
|
+
}): {
|
|
1229
|
+
result: HarnessStreamTextResult<TOOLS, RUNTIME_CONTEXT>;
|
|
1230
|
+
done: Promise<void>;
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1233
|
+
/**
|
|
1234
|
+
* Live harness session held by the caller.
|
|
1235
|
+
*
|
|
1236
|
+
* Created by {@link import('./harness-agent').HarnessAgent.createSession}.
|
|
1237
|
+
* Owns the underlying `HarnessV1Session`, the network sandbox session, and the
|
|
1238
|
+
* bridge-port lease (when the provider wraps a caller-provided sandbox with a
|
|
1239
|
+
* port pool).
|
|
1240
|
+
*
|
|
1241
|
+
* Pass the instance back to `agent.generate` / `agent.stream` on every
|
|
1242
|
+
* call; end the local handle with `detach()`, `stop()`, or `destroy()`.
|
|
1243
|
+
*
|
|
1244
|
+
* After any lifecycle method has resolved, the session is unusable — any
|
|
1245
|
+
* subsequent `generate`/`stream` call against it throws.
|
|
1246
|
+
*/
|
|
1247
|
+
declare class HarnessAgentSession {
|
|
1248
|
+
/**
|
|
1249
|
+
* Stable identifier the harness adapter saw in `doStart`. The same
|
|
1250
|
+
* string callers persist when they intend to resume the session in a
|
|
1251
|
+
* future process.
|
|
1252
|
+
*/
|
|
1253
|
+
readonly sessionId: string;
|
|
1254
|
+
private readonly harness;
|
|
1255
|
+
private readonly sandboxProvider;
|
|
1256
|
+
private readonly sessionWorkDir;
|
|
1257
|
+
private underlyingSession;
|
|
1258
|
+
private sandboxSession;
|
|
1259
|
+
private leasedBridgePort;
|
|
1260
|
+
private readonly toolApproval;
|
|
1261
|
+
private readonly pendingToolApprovals;
|
|
1262
|
+
private stopped;
|
|
1263
|
+
/**
|
|
1264
|
+
* Whether this session was created from a resume payload. Captured at
|
|
1265
|
+
* construction so it survives lifecycle cleanup.
|
|
1266
|
+
*/
|
|
1267
|
+
readonly isResume: boolean;
|
|
1268
|
+
constructor(options: {
|
|
1269
|
+
sessionId: string;
|
|
1270
|
+
harness: HarnessV1;
|
|
1271
|
+
underlyingSession: HarnessV1Session;
|
|
1272
|
+
sandboxSession: HarnessV1NetworkSandboxSession;
|
|
1273
|
+
sandboxProvider: HarnessV1SandboxProvider;
|
|
1274
|
+
leasedBridgePort?: number;
|
|
1275
|
+
sessionWorkDir: string;
|
|
1276
|
+
toolApproval: HarnessAgentToolApprovalConfiguration | undefined;
|
|
1277
|
+
pendingToolApprovals?: readonly HarnessV1PendingToolApproval[];
|
|
1278
|
+
});
|
|
1279
|
+
promptTurn<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context>(options: {
|
|
1280
|
+
prompt: HarnessV1Prompt;
|
|
1281
|
+
instructions: string | undefined;
|
|
1282
|
+
tools: TOOLS;
|
|
1283
|
+
toolSpecs: HarnessV1ToolSpec[];
|
|
1284
|
+
runtimeContext: RUNTIME_CONTEXT;
|
|
1285
|
+
abortSignal: AbortSignal | undefined;
|
|
1286
|
+
telemetry: TelemetryOptions | undefined;
|
|
1287
|
+
}): ReturnType<typeof runPrompt<TOOLS, RUNTIME_CONTEXT>>;
|
|
1288
|
+
continueTurn<TOOLS extends ToolSet, RUNTIME_CONTEXT extends Context>(options: {
|
|
1289
|
+
instructions: string | undefined;
|
|
1290
|
+
tools: TOOLS;
|
|
1291
|
+
toolSpecs: HarnessV1ToolSpec[];
|
|
1292
|
+
runtimeContext: RUNTIME_CONTEXT;
|
|
1293
|
+
abortSignal: AbortSignal | undefined;
|
|
1294
|
+
telemetry: TelemetryOptions | undefined;
|
|
1295
|
+
toolApprovalContinuations?: readonly HarnessAgentToolApprovalContinuation[] | undefined;
|
|
1296
|
+
}): ReturnType<typeof runPrompt<TOOLS, RUNTIME_CONTEXT>>;
|
|
1297
|
+
/**
|
|
1298
|
+
* Ask the underlying runtime to compact its context. The runtime performs
|
|
1299
|
+
* the compaction itself; when it completes, a `compaction` part appears on
|
|
1300
|
+
* the active (or next) turn's stream. Safe to call between turns for
|
|
1301
|
+
* runtimes whose compaction is session-scoped (e.g. Pi).
|
|
1302
|
+
*
|
|
1303
|
+
* Throws `HarnessCapabilityUnsupportedError` for harnesses that cannot
|
|
1304
|
+
* trigger compaction manually (e.g. Codex, which still auto-compacts under
|
|
1305
|
+
* the hood). Throws if the session has ended.
|
|
1306
|
+
*/
|
|
1307
|
+
compact(customInstructions?: string): Promise<void>;
|
|
1308
|
+
/**
|
|
1309
|
+
* Park the session, returning a payload the caller can persist and later
|
|
1310
|
+
* pass to `agent.createSession({ sessionId, resumeFrom })` to reconnect.
|
|
1311
|
+
* The runtime and sandbox keep running; this local session handle becomes
|
|
1312
|
+
* unusable.
|
|
1313
|
+
*/
|
|
1314
|
+
detach(): Promise<HarnessV1ResumeState>;
|
|
1315
|
+
/**
|
|
1316
|
+
* Persist enough state to resume later, then stop the runtime and sandbox.
|
|
1317
|
+
* Returns the resume state for a future
|
|
1318
|
+
* `agent.createSession({ sessionId, resumeFrom })` call.
|
|
1319
|
+
*/
|
|
1320
|
+
stop(): Promise<HarnessV1ResumeState>;
|
|
1321
|
+
/**
|
|
1322
|
+
* Stop the runtime and discard resumability. The sandbox is destroyed when
|
|
1323
|
+
* the provider supports destruction; otherwise it is stopped.
|
|
1324
|
+
*/
|
|
1325
|
+
destroy(): Promise<void>;
|
|
1326
|
+
/**
|
|
1327
|
+
* Gracefully freeze the active turn at the slice boundary and return the
|
|
1328
|
+
* resume payload, **leaving the sandbox/runtime running** so the next process
|
|
1329
|
+
* can resume. Resolves once the in-flight `stream()`/`continueTurn()`
|
|
1330
|
+
* has cleanly wound down at a precise cursor (see
|
|
1331
|
+
* {@link HarnessV1Session.doSuspendTurn}).
|
|
1332
|
+
*
|
|
1333
|
+
* After this call the session is marked stopped. This in-process handle no
|
|
1334
|
+
* longer drives turns; a future slice creates a fresh session from the
|
|
1335
|
+
* returned state. The sandbox is **not** stopped and no port lease is
|
|
1336
|
+
* released, because bridge-backed adapters may still have a live bridge on
|
|
1337
|
+
* that port.
|
|
1338
|
+
*/
|
|
1339
|
+
suspendTurn(): Promise<HarnessV1ResumeState>;
|
|
1340
|
+
private getPendingToolApprovals;
|
|
1341
|
+
private withPendingToolApprovals;
|
|
1342
|
+
private endLocalHandle;
|
|
1343
|
+
private releasePortLease;
|
|
1344
|
+
private requireReusableSession;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
/** Extract the builtin tool set type from a `HarnessV1<...>` parameter. */
|
|
1348
|
+
type BuiltinToolsOf<H> = H extends HarnessV1<infer T> ? T : never;
|
|
1349
|
+
/**
|
|
1350
|
+
* Type-level merge of a harness's builtin tools with user-defined tools.
|
|
1351
|
+
* User tools override builtins on key collision.
|
|
1352
|
+
*/
|
|
1353
|
+
type HarnessAllTools<THarness extends HarnessV1<any>, TUserTools extends ToolSet> = Omit<BuiltinToolsOf<THarness>, keyof TUserTools> & TUserTools;
|
|
1354
|
+
/**
|
|
1355
|
+
* Required `session` extension on every `HarnessAgent.generate` /
|
|
1356
|
+
* `HarnessAgent.stream` call. The agent operates exclusively on the
|
|
1357
|
+
* `HarnessAgentSession` the caller passes in — it owns no session
|
|
1358
|
+
* state of its own.
|
|
1359
|
+
*/
|
|
1360
|
+
interface HarnessAgentCallExtensions {
|
|
1361
|
+
/**
|
|
1362
|
+
* Active session returned by `agent.createSession(...)`. Drives the
|
|
1363
|
+
* underlying harness adapter for this turn.
|
|
1364
|
+
*/
|
|
1365
|
+
session: HarnessAgentSession;
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* AI SDK `Agent` implementation that drives a third-party agent runtime
|
|
1369
|
+
* through a `HarnessV1` adapter (Claude Code, Codex, …).
|
|
1370
|
+
*
|
|
1371
|
+
* Behaviour summary:
|
|
1372
|
+
* - **Stateless definition.** Construct once at module scope. The agent
|
|
1373
|
+
* holds the harness adapter, the merged tool surface, the sandbox
|
|
1374
|
+
* provider and other config — never a live session. Per-call data
|
|
1375
|
+
* (prompt, abort signal, the `HarnessAgentSession`) lives on
|
|
1376
|
+
* `generate()` / `stream()`.
|
|
1377
|
+
* - **Explicit sessions.** Callers spawn sessions with
|
|
1378
|
+
* `agent.createSession(...)`, pass the returned
|
|
1379
|
+
* `HarnessAgentSession` on every `generate` / `stream`, and end it via
|
|
1380
|
+
* `session.detach()`, `session.stop()`, or `session.destroy()`.
|
|
1381
|
+
* - **Cross-process resume.** `createSession({ sessionId, resumeFrom })`
|
|
1382
|
+
* resumes from state previously returned by `session.detach()` or
|
|
1383
|
+
* `session.stop()`. The framework validates `resumeFrom` against the
|
|
1384
|
+
* harness's `resumeStateSchema` before handing it to the adapter.
|
|
1385
|
+
* - **Host tool execution.** User tools passed in `settings.tools` are
|
|
1386
|
+
* executed on the host whenever the underlying runtime calls them;
|
|
1387
|
+
* the result is fed back to the harness via `submitToolResult`.
|
|
1388
|
+
* Adapter builtin tools (e.g. Claude Code's `Bash`) pass through
|
|
1389
|
+
* untouched.
|
|
1390
|
+
* - **Sandbox propagation.** `settings.sandbox` is a sandbox provider.
|
|
1391
|
+
* On `createSession`, the agent calls `provider.createSession()` (or
|
|
1392
|
+
* `resumeSession()`) and passes the resulting network sandbox session into
|
|
1393
|
+
* `doStart`. Its `restricted()` view (a tool-safe
|
|
1394
|
+
* `Experimental_SandboxSession`) is handed to user-tool `execute()` calls
|
|
1395
|
+
* via `experimental_sandbox`.
|
|
1396
|
+
*/
|
|
1397
|
+
declare class HarnessAgent<THarness extends HarnessV1<any> = HarnessV1, TUserTools extends ToolSet = {}, RUNTIME_CONTEXT extends Context = Context> implements Agent<never, HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, never> {
|
|
1398
|
+
readonly version: "agent-v1";
|
|
1399
|
+
readonly id: string | undefined;
|
|
1400
|
+
/**
|
|
1401
|
+
* Merged tool set exposed to AI SDK consumers: harness builtins +
|
|
1402
|
+
* user-defined tools, with user tools overriding builtins on key
|
|
1403
|
+
* collision. Built once at construction time so the typed surface is
|
|
1404
|
+
* stable across calls.
|
|
1405
|
+
*/
|
|
1406
|
+
readonly tools: HarnessAllTools<THarness, TUserTools>;
|
|
1407
|
+
private readonly settings;
|
|
1408
|
+
private readonly userTools;
|
|
1409
|
+
private readonly permissionMode;
|
|
1410
|
+
constructor(settings: HarnessAgentSettings<THarness, TUserTools>);
|
|
1411
|
+
/** Identifier of the harness backing this agent. */
|
|
1412
|
+
get harnessId(): string;
|
|
1413
|
+
/**
|
|
1414
|
+
* Start a fresh session, or resume from state previously returned by
|
|
1415
|
+
* `session.detach()` or `session.stop()`. The returned
|
|
1416
|
+
* `HarnessAgentSession` must be passed to subsequent `generate` / `stream`
|
|
1417
|
+
* calls; end it with `session.detach()`, `session.stop()`, or
|
|
1418
|
+
* `session.destroy()`.
|
|
1419
|
+
*/
|
|
1420
|
+
createSession(options?: {
|
|
1421
|
+
/**
|
|
1422
|
+
* Optional stable identifier for the underlying sandbox/session.
|
|
1423
|
+
* When omitted the agent generates one. Supply the original
|
|
1424
|
+
* `session.sessionId` together with `resumeFrom` to reattach a
|
|
1425
|
+
* previously ended session across processes.
|
|
1426
|
+
*/
|
|
1427
|
+
sessionId?: string;
|
|
1428
|
+
/**
|
|
1429
|
+
* Resume payload returned by a prior `session.detach()` or
|
|
1430
|
+
* `session.stop()`. Must be
|
|
1431
|
+
* accompanied by the original `sessionId`; the framework
|
|
1432
|
+
* validates it against `harness.resumeStateSchema` before handing
|
|
1433
|
+
* it to the adapter.
|
|
1434
|
+
*/
|
|
1435
|
+
resumeFrom?: HarnessV1ResumeState;
|
|
1436
|
+
abortSignal?: AbortSignal;
|
|
1437
|
+
}): Promise<HarnessAgentSession>;
|
|
1438
|
+
generate(options: AgentCallParameters<never, HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT> & HarnessAgentCallExtensions): Promise<GenerateTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, never>>;
|
|
1439
|
+
stream(options: AgentStreamParameters<never, HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT> & HarnessAgentCallExtensions): Promise<StreamTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, never>>;
|
|
1440
|
+
/**
|
|
1441
|
+
* Continue the in-flight turn **without a new prompt**, streaming its events
|
|
1442
|
+
* like {@link stream}. Used to keep consuming a turn that is still running
|
|
1443
|
+
* (or finished) in the runtime after a process boundary — the workflow slice
|
|
1444
|
+
* loop calls this on every slice after the first. Routes through the adapter's
|
|
1445
|
+
* `doContinueTurn`; what it can guarantee (lossless attach vs. lossy rerun)
|
|
1446
|
+
* follows from how the adapter resumed the session.
|
|
1447
|
+
*/
|
|
1448
|
+
continueTurn(options: {
|
|
1449
|
+
session: HarnessAgentSession;
|
|
1450
|
+
toolApprovalContinuations?: readonly HarnessAgentToolApprovalContinuation[];
|
|
1451
|
+
abortSignal?: AbortSignal;
|
|
1452
|
+
}): Promise<StreamTextResult<HarnessAllTools<THarness, TUserTools>, RUNTIME_CONTEXT, never>>;
|
|
1453
|
+
private _startTurn;
|
|
1454
|
+
private _acquireSandbox;
|
|
1455
|
+
private _resolveTurnInput;
|
|
1456
|
+
private _toToolSpecs;
|
|
1457
|
+
private _toGenerateResult;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
/**
|
|
1461
|
+
* Pre-build a harness's sandbox template without running an agent. Idempotent:
|
|
1462
|
+
* if the template already exists (snapshot present, or marker on a non-snapshot
|
|
1463
|
+
* provider), this resolves quickly.
|
|
1464
|
+
*
|
|
1465
|
+
* Use from a CI/deploy script to amortize the first-session cost so production
|
|
1466
|
+
* sessions always resume from snapshot. For adapters without a bootstrap
|
|
1467
|
+
* recipe (no `getBootstrap`) this is a no-op.
|
|
1468
|
+
*
|
|
1469
|
+
* The temporary network sandbox session created during pre-warm is stopped
|
|
1470
|
+
* before the function resolves; the snapshot/template state persists in the
|
|
1471
|
+
* provider's native storage (for Vercel: as the `currentSnapshotId` of the
|
|
1472
|
+
* named template sandbox).
|
|
1473
|
+
*/
|
|
1474
|
+
declare function prewarmHarness(options: {
|
|
1475
|
+
readonly harness: HarnessV1;
|
|
1476
|
+
readonly sandboxProvider: HarnessV1SandboxProvider;
|
|
1477
|
+
readonly abortSignal?: AbortSignal;
|
|
1478
|
+
}): Promise<void>;
|
|
1479
|
+
|
|
1480
|
+
export { HarnessAgent, HarnessAgentSession, type HarnessAgentSettings, type HarnessAgentToolApprovalConfiguration, type HarnessAgentToolApprovalContinuation, type HarnessDebugConfig, type HarnessDebugLevel, type HarnessDiagnostic, type HarnessDiagnosticConsumer, collectHarnessAgentToolApprovalContinuations, prewarmHarness };
|