@ai-sdk/harness 0.0.0 → 1.0.0-beta.14

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 (67) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/LICENSE +13 -0
  3. package/README.md +142 -0
  4. package/agent/index.ts +47 -0
  5. package/bridge/index.ts +10 -0
  6. package/dist/agent/index.d.ts +1521 -0
  7. package/dist/agent/index.js +2958 -0
  8. package/dist/agent/index.js.map +1 -0
  9. package/dist/bridge/index.d.ts +111 -0
  10. package/dist/bridge/index.js +415 -0
  11. package/dist/bridge/index.js.map +1 -0
  12. package/dist/index.d.ts +1536 -0
  13. package/dist/index.js +15834 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/utils/index.d.ts +225 -0
  16. package/dist/utils/index.js +12148 -0
  17. package/dist/utils/index.js.map +1 -0
  18. package/package.json +99 -1
  19. package/src/agent/harness-agent-session.ts +509 -0
  20. package/src/agent/harness-agent-settings.ts +131 -0
  21. package/src/agent/harness-agent-tool-approval-continuation.ts +94 -0
  22. package/src/agent/harness-agent-types.ts +50 -0
  23. package/src/agent/harness-agent.ts +819 -0
  24. package/src/agent/internal/bootstrap-recipe.ts +124 -0
  25. package/src/agent/internal/bridge-port-registry.ts +52 -0
  26. package/src/agent/internal/harness-stream-text-result.ts +720 -0
  27. package/src/agent/internal/lifecycle-state-validation.ts +95 -0
  28. package/src/agent/internal/permission-mode.ts +50 -0
  29. package/src/agent/internal/resolve-observability.ts +128 -0
  30. package/src/agent/internal/run-prompt.ts +813 -0
  31. package/src/agent/internal/strip-work-dir.ts +68 -0
  32. package/src/agent/internal/to-harness-stream.ts +75 -0
  33. package/src/agent/internal/translate-stream-part.ts +221 -0
  34. package/src/agent/internal/turn-telemetry.ts +359 -0
  35. package/src/agent/observability/file-reporter.ts +206 -0
  36. package/src/agent/observability/index.ts +15 -0
  37. package/src/agent/observability/trace-tree-reporter.ts +122 -0
  38. package/src/agent/observability/types.ts +86 -0
  39. package/src/agent/prewarm.ts +47 -0
  40. package/src/bridge/index.ts +702 -0
  41. package/src/errors/harness-capability-unsupported-error.ts +41 -0
  42. package/src/errors/harness-error.ts +22 -0
  43. package/src/index.ts +3 -0
  44. package/src/utils/bridge-ready.ts +277 -0
  45. package/src/utils/classify-disk-log.ts +43 -0
  46. package/src/utils/index.ts +15 -0
  47. package/src/utils/sandbox-channel.ts +453 -0
  48. package/src/v1/harness-v1-bootstrap.ts +46 -0
  49. package/src/v1/harness-v1-bridge-protocol.ts +310 -0
  50. package/src/v1/harness-v1-builtin-tool.ts +138 -0
  51. package/src/v1/harness-v1-call-warning.ts +22 -0
  52. package/src/v1/harness-v1-diagnostic.ts +66 -0
  53. package/src/v1/harness-v1-lifecycle-state.ts +65 -0
  54. package/src/v1/harness-v1-metadata.ts +13 -0
  55. package/src/v1/harness-v1-network-sandbox-session.ts +123 -0
  56. package/src/v1/harness-v1-observability.ts +20 -0
  57. package/src/v1/harness-v1-permission-mode.ts +11 -0
  58. package/src/v1/harness-v1-prompt-control.ts +41 -0
  59. package/src/v1/harness-v1-prompt.ts +11 -0
  60. package/src/v1/harness-v1-sandbox-provider.ts +76 -0
  61. package/src/v1/harness-v1-session.ts +272 -0
  62. package/src/v1/harness-v1-skill.ts +36 -0
  63. package/src/v1/harness-v1-stream-part.ts +363 -0
  64. package/src/v1/harness-v1-tool-spec.ts +31 -0
  65. package/src/v1/harness-v1.ts +83 -0
  66. package/src/v1/index.ts +93 -0
  67. package/utils/index.ts +1 -0
@@ -0,0 +1,123 @@
1
+ import type { Experimental_SandboxSession as SandboxSession } from '@ai-sdk/provider-utils';
2
+
3
+ /**
4
+ * Network sandbox session returned by `HarnessV1SandboxProvider.createSession()`. The
5
+ * harness keeps this for the lifetime of a session. It is itself a
6
+ * {@link SandboxSession} (file I/O, exec, spawn) and adds the infra surface on
7
+ * top: port resolution, lifecycle, and network-policy mutation.
8
+ *
9
+ * Code that should only touch the filesystem and spawn processes receives the
10
+ * reduced view from {@link HarnessV1NetworkSandboxSession.restricted}, never the
11
+ * network sandbox session itself — so it cannot stop the sandbox or change its
12
+ * network policy.
13
+ */
14
+ export interface HarnessV1NetworkSandboxSession extends SandboxSession {
15
+ /**
16
+ * Stable identifier for the underlying sandbox resource. Used by the
17
+ * harness session manager as the durable lookup key for cross-process
18
+ * resume — the framework persists this on lifecycle state so a future
19
+ * process can call `HarnessV1SandboxProvider.resume?({ sessionId })` and
20
+ * reach the same resource. Providers populate it from their native
21
+ * identifier (Vercel: the sandbox name; just-bash: a UUID minted at
22
+ * create time).
23
+ */
24
+ readonly id: string;
25
+
26
+ /**
27
+ * The sandbox's default working directory — the absolute path that
28
+ * `run`/`spawn` resolve relative commands against when no `workingDirectory`
29
+ * is given. Read from the live sandbox (it is provider-specific and
30
+ * configurable at create time: Vercel defaults to `/vercel/sandbox`,
31
+ * just-bash to `/home/user`), never hardcoded.
32
+ *
33
+ * The framework composes each session's working directory underneath this
34
+ * path (`<defaultWorkingDirectory>/<harnessId>-<sessionId>`) so adapters do
35
+ * not bake a provider-specific base into their own paths.
36
+ */
37
+ readonly defaultWorkingDirectory: string;
38
+
39
+ /** Ports the sandbox exposes; resolvable to public URLs via `getPortUrl`. */
40
+ readonly ports: ReadonlyArray<number>;
41
+
42
+ /**
43
+ * Resolve a publicly-reachable URL for a sandbox-exposed port. Bridge-backed
44
+ * adapters call this to open their WebSocket to the in-sandbox bridge.
45
+ */
46
+ readonly getPortUrl: (options: {
47
+ port: number;
48
+ protocol?: 'http' | 'https' | 'ws';
49
+ }) => PromiseLike<string>;
50
+
51
+ /** Stop the sandbox. Idempotent. */
52
+ readonly stop: () => PromiseLike<void>;
53
+
54
+ /**
55
+ * Destroy/delete the sandbox resource when supported. Optional because some
56
+ * providers only have a stop/dispose concept. Implementations must handle
57
+ * both a still-running sandbox and a previously stopped sandbox.
58
+ */
59
+ readonly destroy?: () => PromiseLike<void>;
60
+
61
+ /**
62
+ * Update the sandbox's outbound network policy. Optional — implementations
63
+ * without a local enforcement primitive (e.g. just-bash) omit this. Callers
64
+ * use optional-call (`sandboxSession.setNetworkPolicy?.(policy)`); a
65
+ * missing implementation is a no-op.
66
+ */
67
+ readonly setNetworkPolicy?: (
68
+ policy: HarnessV1NetworkPolicy,
69
+ ) => PromiseLike<void>;
70
+
71
+ /**
72
+ * Replace the set of ports exposed by the sandbox. Full-replacement
73
+ * semantics: ports omitted from the array are deregistered. Optional —
74
+ * implementations that cannot expose ports (e.g. just-bash) omit this.
75
+ */
76
+ readonly setPorts?: (
77
+ ports: ReadonlyArray<number>,
78
+ options?: { abortSignal?: AbortSignal },
79
+ ) => PromiseLike<void>;
80
+
81
+ /**
82
+ * Reduced view of this session, typed as the bare {@link SandboxSession}
83
+ * (file I/O, exec, spawn) — nothing that could stop the sandbox or change
84
+ * its network policy. Pass this to user-tool `execute()` calls and other
85
+ * code that must not reach the infra surface.
86
+ *
87
+ * The returned object points at exactly the same underlying sandbox
88
+ * resource as the network sandbox session it was produced from; it is only a
89
+ * narrower surface over the same resource, not a separate sandbox.
90
+ */
91
+ readonly restricted: () => SandboxSession;
92
+ }
93
+
94
+ /**
95
+ * Outbound network policy applied by the sandbox runtime.
96
+ *
97
+ * `'allow-all'` and `'deny-all'` are convenience presets. `'custom'` is an
98
+ * allow-list with an optional CIDR deny-list that takes precedence:
99
+ *
100
+ * - Reachable hosts are the union of `allowedHosts` and `allowedCIDRs`.
101
+ * - `deniedCIDRs` wins over both, useful for blocking cloud-metadata IPs while
102
+ * otherwise allowing broad access.
103
+ *
104
+ * The two `'custom'` branches share the same discriminator but each requires
105
+ * a different allow field. Specifying `'custom'` with only `deniedCIDRs`
106
+ * (deny-only) is rejected at compile time — functionally it would be
107
+ * equivalent to `'deny-all'`.
108
+ */
109
+ export type HarnessV1NetworkPolicy =
110
+ | { mode: 'allow-all' }
111
+ | { mode: 'deny-all' }
112
+ | {
113
+ mode: 'custom';
114
+ allowedHosts: ReadonlyArray<string>;
115
+ allowedCIDRs?: ReadonlyArray<string>;
116
+ deniedCIDRs?: ReadonlyArray<string>;
117
+ }
118
+ | {
119
+ mode: 'custom';
120
+ allowedHosts?: ReadonlyArray<string>;
121
+ allowedCIDRs: ReadonlyArray<string>;
122
+ deniedCIDRs?: ReadonlyArray<string>;
123
+ };
@@ -0,0 +1,20 @@
1
+ import type {
2
+ HarnessV1DebugConfig,
3
+ HarnessV1Diagnostic,
4
+ } from './harness-v1-diagnostic';
5
+
6
+ /**
7
+ * Diagnostics wiring the framework hands to an adapter's `doStart`. `report` is
8
+ * the general emission sink: a bridge adapter normalizes each wire frame into a
9
+ * `HarnessV1Diagnostic` (via `harnessV1DiagnosticFromBridgeFrame`) and calls it;
10
+ * a non-bridge adapter constructs a `HarnessV1Diagnostic` from its host-side
11
+ * logs/errors and calls it directly. `debug` gates what the adapter emits.
12
+ * Absent when the consumer has not enabled diagnostics.
13
+ */
14
+ export type HarnessV1Observability = {
15
+ /** Per-session debug config gating what the adapter captures/emits. */
16
+ readonly debug?: HarnessV1DebugConfig;
17
+
18
+ /** General emission sink — any adapter reports a `HarnessV1Diagnostic` here. */
19
+ readonly report?: (diagnostic: HarnessV1Diagnostic) => void;
20
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Baseline permission mode for adapter-native built-in tools.
3
+ *
4
+ * Custom host-executed tools are not controlled by this setting. They use
5
+ * `HarnessAgentSettings.toolApproval`, similar to AI SDK's per-tool approval
6
+ * status map.
7
+ */
8
+ export type HarnessV1PermissionMode =
9
+ | 'allow-reads'
10
+ | 'allow-edits'
11
+ | 'allow-all';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Bidirectional control surface returned by `doPromptTurn`.
3
+ *
4
+ * The host uses these methods to feed asynchronous responses back to the
5
+ * adapter while a turn is running. All methods are optional except those
6
+ * the adapter actively supports (host-executed tools require
7
+ * `submitToolResult`; approvals require `submitToolApproval`; mid-turn
8
+ * messages require `submitUserMessage`).
9
+ */
10
+ export type HarnessV1PromptControl = {
11
+ /**
12
+ * Provide a result for a `tool-call` the adapter emitted. The adapter
13
+ * forwards the result to the underlying runtime so the model can continue.
14
+ */
15
+ submitToolResult(input: {
16
+ toolCallId: string;
17
+ output: unknown;
18
+ isError?: boolean;
19
+ }): PromiseLike<void>;
20
+
21
+ /**
22
+ * Respond to a `tool-approval-request` the adapter emitted.
23
+ */
24
+ submitToolApproval?(input: {
25
+ approvalId: string;
26
+ approved: boolean;
27
+ reason?: string;
28
+ }): PromiseLike<void>;
29
+
30
+ /**
31
+ * Inject a fresh user message into a turn that is still in flight.
32
+ * Supported only by runtimes that accept interactive input.
33
+ */
34
+ submitUserMessage?(text: string): PromiseLike<void>;
35
+
36
+ /**
37
+ * Resolves when the adapter has finished the turn (success or failure).
38
+ * Rejects with the underlying error when the turn fails.
39
+ */
40
+ readonly done: PromiseLike<void>;
41
+ };
@@ -0,0 +1,11 @@
1
+ import type { UserModelMessage } from '@ai-sdk/provider-utils';
2
+
3
+ /**
4
+ * Prompt shape passed to `HarnessV1Session.doPromptTurn`.
5
+ *
6
+ * A harness session represents an ongoing third-party agent runtime that
7
+ * owns its own conversation history. Each prompt turn carries only the
8
+ * fresh user input for that turn, either as a plain string or as a single
9
+ * `UserModelMessage`.
10
+ */
11
+ export type HarnessV1Prompt = string | UserModelMessage;
@@ -0,0 +1,76 @@
1
+ import type { Experimental_SandboxSession as SandboxSession } from '@ai-sdk/provider-utils';
2
+ import type { HarnessV1NetworkSandboxSession } from './harness-v1-network-sandbox-session';
3
+
4
+ /**
5
+ * Provider that produces network sandbox sessions for harness sessions. Lives at
6
+ * module scope as a stable, synchronous object — analogous to
7
+ * `LanguageModelV4` providers, no I/O performed at construction. The actual
8
+ * sandbox is created (or wrapped) when `HarnessAgent` calls `createSession()`.
9
+ */
10
+ export interface HarnessV1SandboxProvider {
11
+ readonly specificationVersion: 'harness-sandbox-v1';
12
+ readonly providerId: string;
13
+
14
+ /**
15
+ * Pool of ports the consumer reserved on a caller-provided sandbox for
16
+ * concurrent harness sessions. The session manager leases one port per
17
+ * session and releases on stop or destroy.
18
+ *
19
+ * Only meaningful when the provider wraps a caller-provided sandbox
20
+ * (the caller pre-declared the ports). In create-new modes the provider
21
+ * mints a fresh sandbox per session, so no leasing is needed; providers
22
+ * leave this undefined.
23
+ */
24
+ readonly bridgePorts?: ReadonlyArray<number>;
25
+
26
+ readonly createSession: (options?: {
27
+ /**
28
+ * Stable per-session identifier. When supplied, the provider names the
29
+ * underlying resource deterministically so a future call to `resume`
30
+ * (potentially from a different process) can find the same sandbox.
31
+ * Omitted from prewarm and other paths that don't need a resumable
32
+ * resource — in that case the provider falls back to its native
33
+ * auto-naming.
34
+ */
35
+ sessionId?: string;
36
+ abortSignal?: AbortSignal;
37
+ /**
38
+ * Stable identity for snapshot-based reuse. Providers that support
39
+ * persistence/snapshots use this as part of the persistent sandbox
40
+ * name; subsequent calls with the same identity resume from snapshot.
41
+ *
42
+ * Ignored when the provider is wrapping a caller-provided sandbox.
43
+ */
44
+ identity?: string;
45
+ /**
46
+ * Called exactly once per identity, on fresh creation. Snapshot-capable
47
+ * providers wire this into the platform's one-time-setup hook so the
48
+ * side effects are baked into the snapshot. Providers without snapshot
49
+ * support run it immediately after fresh create.
50
+ *
51
+ * Not called when the provider is wrapping a caller-provided sandbox
52
+ * (the caller owns the sandbox; the framework applies its own
53
+ * idempotent bootstrap post-create instead).
54
+ */
55
+ onFirstCreate?: (
56
+ session: SandboxSession,
57
+ opts: { abortSignal?: AbortSignal },
58
+ ) => Promise<void>;
59
+ }) => PromiseLike<HarnessV1NetworkSandboxSession>;
60
+
61
+ /**
62
+ * Reattach to an existing sandbox previously created with the same
63
+ * `sessionId`. Optional — providers that cannot rehydrate by id (e.g.
64
+ * just-bash) omit this; the harness throws
65
+ * `HarnessCapabilityUnsupportedError` when resume is attempted against
66
+ * them.
67
+ *
68
+ * The provider derives the sandbox identifier from `sessionId` using the
69
+ * same deterministic naming scheme it used in `createSession`. Returns a
70
+ * network sandbox session bound to the existing resource.
71
+ */
72
+ readonly resumeSession?: (options: {
73
+ sessionId: string;
74
+ abortSignal?: AbortSignal;
75
+ }) => PromiseLike<HarnessV1NetworkSandboxSession>;
76
+ }
@@ -0,0 +1,272 @@
1
+ import type { HarnessV1NetworkSandboxSession } from './harness-v1-network-sandbox-session';
2
+ import type { HarnessV1Observability } from './harness-v1-observability';
3
+ import type { HarnessV1PermissionMode } from './harness-v1-permission-mode';
4
+ import type { HarnessV1Prompt } from './harness-v1-prompt';
5
+ import type { HarnessV1PromptControl } from './harness-v1-prompt-control';
6
+ import type {
7
+ HarnessV1ContinueTurnState,
8
+ HarnessV1ResumeSessionState,
9
+ } from './harness-v1-lifecycle-state';
10
+ import type { HarnessV1Skill } from './harness-v1-skill';
11
+ import type { HarnessV1StreamPart } from './harness-v1-stream-part';
12
+ import type { HarnessV1ToolSpec } from './harness-v1-tool-spec';
13
+
14
+ /**
15
+ * Options passed to `HarnessV1.doStart`.
16
+ *
17
+ * `sandboxSession` and `sessionWorkDir` are coupled and always present. The
18
+ * framework creates the sandbox and per-session working directory before
19
+ * calling the adapter, so adapters never need to derive provider-specific paths.
20
+ */
21
+ export type HarnessV1StartOptions = {
22
+ /**
23
+ * Stable identifier for this harness session. Used as the underlying
24
+ * resource name where the adapter has a notion of a named session
25
+ * (sandbox name, native session id, …).
26
+ */
27
+ readonly sessionId: string;
28
+
29
+ /**
30
+ * Skills made available to the underlying runtime for the lifetime of
31
+ * the session. Adapters decide how to surface them.
32
+ */
33
+ readonly skills?: ReadonlyArray<HarnessV1Skill>;
34
+
35
+ /**
36
+ * Optional resume payload returned by a prior session lifecycle method. When
37
+ * provided, the adapter should resume the existing session before accepting a
38
+ * new prompt or continuing a nested unfinished turn.
39
+ */
40
+ readonly resumeFrom?: HarnessV1ResumeSessionState;
41
+
42
+ /**
43
+ * Optional continuation payload returned by `doSuspendTurn`, or nested in
44
+ * `resumeFrom`. When provided, the adapter should resume the existing session
45
+ * in a shape ready for `doContinueTurn` rather than for a fresh prompt.
46
+ */
47
+ readonly continueFrom?: HarnessV1ContinueTurnState;
48
+
49
+ /**
50
+ * Approval policy for built-in adapter-native tool use. Custom host-executed
51
+ * tools are approved by the framework before results are submitted back to
52
+ * the adapter.
53
+ */
54
+ readonly permissionMode?: HarnessV1PermissionMode;
55
+
56
+ /**
57
+ * Signal that aborts startup. The adapter must propagate cancellation to
58
+ * any spawned processes or network calls.
59
+ */
60
+ readonly abortSignal?: AbortSignal;
61
+
62
+ /**
63
+ * Diagnostics wiring. The framework populates this; the adapter only
64
+ * forwards `observability.onDiagnostic` into its `SandboxChannel` and
65
+ * `observability.debug` into the bridge `start` message. Absent when the
66
+ * consumer has not enabled diagnostics.
67
+ */
68
+ readonly observability?: HarnessV1Observability;
69
+ /**
70
+ * Network sandbox session the adapter operates against. It is owned and
71
+ * lifecycled by `HarnessAgent`. Adapters call `restricted()` for the
72
+ * tool-safe filesystem/exec/spawn surface, and use the infra methods
73
+ * (`getPortUrl`, `ports`, `setNetworkPolicy`) for bridge wiring. Adapters
74
+ * must not call `stop()` themselves; the agent does that during cleanup.
75
+ */
76
+ readonly sandboxSession: HarnessV1NetworkSandboxSession;
77
+
78
+ /**
79
+ * Absolute path the adapter runs the agent in for this session. Composed by
80
+ * the framework as `<sandboxSession.defaultWorkingDirectory>/<harnessId>-<sessionId>`
81
+ * and created before `doStart`, so the adapter uses it directly instead of
82
+ * deriving its own provider-specific path.
83
+ */
84
+ readonly sessionWorkDir: string;
85
+ };
86
+
87
+ /**
88
+ * Options passed to `HarnessV1Session.doPromptTurn`.
89
+ */
90
+ export type HarnessV1PromptTurnOptions = {
91
+ /**
92
+ * Fresh input for this turn — either a plain string or a single
93
+ * `ModelMessage`. The harness session owns its own conversation history,
94
+ * so prior turns are never replayed across the contract.
95
+ */
96
+ readonly prompt: HarnessV1Prompt;
97
+
98
+ /**
99
+ * Host-defined tools to make available to the underlying runtime for this
100
+ * turn. The harness emits `tool-call` events when the runtime calls one
101
+ * and waits for `submitToolResult`.
102
+ */
103
+ readonly tools?: ReadonlyArray<HarnessV1ToolSpec>;
104
+
105
+ /**
106
+ * Free-form instructions for the session. The framework supplies the same
107
+ * value on every turn; the adapter is responsible for applying it once, by
108
+ * prepending it to the first user message of a fresh (non-resumed) session.
109
+ * On a resumed session the adapter must not re-apply it — the original first
110
+ * message already carried it and lives in the runtime's persisted history.
111
+ */
112
+ readonly instructions?: string;
113
+
114
+ /**
115
+ * Signal that aborts the in-flight turn. The adapter must cancel any
116
+ * underlying work and resolve `done` (with an error if appropriate).
117
+ */
118
+ readonly abortSignal?: AbortSignal;
119
+
120
+ /**
121
+ * Callback invoked once for each event the adapter produces during the
122
+ * turn. The adapter is responsible for the ordering and completeness of
123
+ * events. `done` resolves once the adapter has emitted all events for the
124
+ * turn (success or failure).
125
+ */
126
+ readonly emit: (event: HarnessV1StreamPart) => void;
127
+ };
128
+
129
+ /**
130
+ * Options passed to `HarnessV1Session.doContinueTurn`.
131
+ *
132
+ * Unlike `doPromptTurn`, there is no `prompt`: `doContinueTurn` continues the
133
+ * in-flight turn rather than starting a new one. It is used to continue a turn
134
+ * that was previously suspended temporarily, e.g. by the workflow slice loop.
135
+ */
136
+ export type HarnessV1ContinueTurnOptions = {
137
+ /**
138
+ * Host-defined tools to make available for the continued turn. Same shape
139
+ * as `doPromptTurn`'s `tools`. An adapter that purely attaches to a live turn
140
+ * may ignore them; an adapter that re-drives the turn (rerun) needs them.
141
+ */
142
+ readonly tools?: ReadonlyArray<HarnessV1ToolSpec>;
143
+
144
+ /**
145
+ * Signal that aborts the continued turn. The adapter must cancel any
146
+ * underlying work and resolve `done` (with an error if appropriate).
147
+ */
148
+ readonly abortSignal?: AbortSignal;
149
+
150
+ /**
151
+ * Callback invoked once for each event the adapter produces while the
152
+ * continued turn runs. Same contract as `doPromptTurn`'s `emit`.
153
+ */
154
+ readonly emit: (event: HarnessV1StreamPart) => void;
155
+ };
156
+
157
+ /**
158
+ * Active harness session, returned by `HarnessV1.doStart`.
159
+ *
160
+ * A session is the unit of state continuity across multiple prompts (one
161
+ * sandbox, one conversation history, one running agent runtime). The host
162
+ * holds onto the session across `doPromptTurn` calls and ends the local
163
+ * instance via `doDetach`, `doStop`, or `doDestroy`.
164
+ */
165
+ export type HarnessV1Session = {
166
+ /**
167
+ * Stable identifier for this session. Same value the host passed in via
168
+ * `HarnessV1StartOptions.sessionId`.
169
+ */
170
+ readonly sessionId: string;
171
+
172
+ /**
173
+ * Whether this session was created from `resumeFrom` or `continueFrom`. Fresh
174
+ * sessions report `false`; resumed sessions report `true`.
175
+ */
176
+ readonly isResume: boolean;
177
+
178
+ /**
179
+ * The model id the underlying runtime is configured to use, if the adapter
180
+ * knows it (e.g. from its settings). Surfaced into telemetry as
181
+ * `gen_ai.request.model` and the trace span labels. Omitted when the adapter
182
+ * defers to the runtime's own default and has no concrete id.
183
+ */
184
+ readonly modelId?: string;
185
+
186
+ /**
187
+ * Run one prompt turn. Returns a control handle the host uses to feed
188
+ * tool results, approvals, and user messages back into the turn while it
189
+ * is in flight. The handle's `done` promise resolves when the turn ends.
190
+ */
191
+ doPromptTurn(
192
+ options: HarnessV1PromptTurnOptions,
193
+ ): PromiseLike<HarnessV1PromptControl>;
194
+
195
+ /**
196
+ * Request that the underlying runtime compact its context. The runtime owns
197
+ * the compaction — the harness neither implements nor schedules it; this is
198
+ * only the trigger. When compaction completes, the adapter surfaces a
199
+ * `compaction` stream part on the next/active turn.
200
+ *
201
+ * Required, but not every runtime can honour it: adapters whose transport
202
+ * exposes no manual compaction (e.g. Codex over `codex exec`, which still
203
+ * auto-compacts on its own) throw `HarnessCapabilityUnsupportedError`.
204
+ * `customInstructions`, when supported, steer the compaction summary.
205
+ */
206
+ doCompact(customInstructions?: string): PromiseLike<void>;
207
+
208
+ /**
209
+ * Continue the in-flight turn **without a new user prompt**, returning the
210
+ * same control surface as `doPromptTurn`. Used to keep consuming a turn that
211
+ * was interrupted at a process boundary (the workflow slice loop), after the
212
+ * session itself has been resumed via `doStart({ continueFrom })`:
213
+ *
214
+ * - When the runtime's turn is still live and reachable (bridge `attach` /
215
+ * `replay`), the adapter subscribes to its events and resolves `done` on
216
+ * the turn's `finish` — **without** re-driving it. Lossless.
217
+ * - When the live turn is gone (bridge respawned `rerun`, or a host-resident
218
+ * runtime like Pi whose turn cannot survive its process), the adapter
219
+ * re-drives the runtime's own thread from its persisted state. Lossy: work
220
+ * in flight at the interruption is recomputed.
221
+ *
222
+ * Required on every adapter. The behaviour an adapter can guarantee follows
223
+ * from its architecture; the contract is uniform.
224
+ */
225
+ doContinueTurn(
226
+ options: HarnessV1ContinueTurnOptions,
227
+ ): PromiseLike<HarnessV1PromptControl>;
228
+
229
+ /**
230
+ * Gracefully freeze the active turn **at a precise cursor while keeping the
231
+ * runtime alive**, returning the continuation payload.
232
+ *
233
+ * This is the slice-boundary primitive. The adapter stops host-side
234
+ * consumption of the in-flight turn without telling the runtime to stop:
235
+ * for a bridge adapter it closes the host socket (the bridge keeps the turn
236
+ * running and accumulates events for replay) and resolves the active
237
+ * `doPromptTurn`/`doContinueTurn` `done` **cleanly** (not as an error) once buffered
238
+ * events have drained, so the cursor in the returned state equals the last
239
+ * event delivered to the host — guaranteeing the next slice's attach replays
240
+ * with no gap and no duplicate. A host-resident adapter (Pi) cannot keep its
241
+ * turn alive, so it persists what it can and the in-flight tail is recomputed
242
+ * on continue.
243
+ *
244
+ * Like `doDetach`, the sandbox/runtime is left running. Unlike `doDetach`,
245
+ * this is for an active turn at a slice boundary rather than a between-turn
246
+ * session handoff. Required on every adapter.
247
+ */
248
+ doSuspendTurn(): PromiseLike<HarnessV1ContinueTurnState>;
249
+
250
+ /**
251
+ * Detach from the underlying runtime without tearing it down, returning a
252
+ * payload the host can later pass to
253
+ * `HarnessV1.doStart({ resumeFrom })` to reconnect before a new turn. After
254
+ * `doDetach`, no further methods on this session instance may be called.
255
+ *
256
+ * Required. Adapters that cannot keep a live runtime parked still return the
257
+ * best resume session state they can while leaving the sandbox running.
258
+ */
259
+ doDetach(): PromiseLike<HarnessV1ResumeSessionState>;
260
+
261
+ /**
262
+ * Persist enough state to resume later, then stop the underlying runtime.
263
+ * After `doStop`, no further methods on this session instance may be called.
264
+ */
265
+ doStop(): PromiseLike<HarnessV1ResumeSessionState>;
266
+
267
+ /**
268
+ * Stop the underlying runtime without returning lifecycle state. After
269
+ * `doDestroy`, no further methods on this session instance may be called.
270
+ */
271
+ doDestroy(): PromiseLike<void>;
272
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * A self-contained instruction bundle the underlying runtime can load into
3
+ * its context. Adapters decide how to surface skills to the runtime.
4
+ */
5
+ export type HarnessV1Skill = {
6
+ /** Stable identifier for the skill (kebab-case slug). */
7
+ readonly name: string;
8
+
9
+ /**
10
+ * Short, model-facing description. This is what the runtime sees to
11
+ * decide whether the skill is relevant.
12
+ */
13
+ readonly description: string;
14
+
15
+ /** Full skill content the model loads when the skill is active. */
16
+ readonly content: string;
17
+
18
+ /**
19
+ * Additional files that belong to this skill. Adapters with native skill
20
+ * directories materialize these next to `SKILL.md`; adapters without native
21
+ * skill files include them with the skill content.
22
+ */
23
+ readonly files?: ReadonlyArray<HarnessV1SkillFile>;
24
+ };
25
+
26
+ export type HarnessV1SkillFile = {
27
+ /**
28
+ * Skill-relative POSIX path, for example `reference.md` or
29
+ * `references/codes.md`. Absolute paths and `..` segments are rejected by
30
+ * adapters before writing.
31
+ */
32
+ readonly path: string;
33
+
34
+ /** UTF-8 text content for the file. */
35
+ readonly content: string;
36
+ };