@ai-sdk/harness 0.0.0-6b196531-20260710185421

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