@ai-sdk/workflow-harness 0.0.0-07500555-20260730161446

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,306 @@
1
+ # @ai-sdk/workflow-harness
2
+
3
+ ## 0.0.0-07500555-20260730161446
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [5f65e61]
8
+ - @ai-sdk/harness@0.0.0-07500555-20260730161446
9
+
10
+ ## 1.0.39
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [86a84c9]
15
+ - @ai-sdk/harness@1.0.39
16
+
17
+ ## 1.0.38
18
+
19
+ ### Patch Changes
20
+
21
+ - f5cdb2d: chore(harness): update primary SDK dependencies
22
+ - @ai-sdk/harness@1.0.38
23
+
24
+ ## 1.0.37
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies [b460541]
29
+ - Updated dependencies [079591e]
30
+ - @ai-sdk/harness@1.0.37
31
+
32
+ ## 1.0.36
33
+
34
+ ### Patch Changes
35
+
36
+ - @ai-sdk/harness@1.0.36
37
+
38
+ ## 1.0.35
39
+
40
+ ### Patch Changes
41
+
42
+ - @ai-sdk/harness@1.0.35
43
+
44
+ ## 1.0.34
45
+
46
+ ### Patch Changes
47
+
48
+ - @ai-sdk/harness@1.0.34
49
+
50
+ ## 1.0.33
51
+
52
+ ### Patch Changes
53
+
54
+ - @ai-sdk/harness@1.0.33
55
+
56
+ ## 1.0.32
57
+
58
+ ### Patch Changes
59
+
60
+ - @ai-sdk/harness@1.0.32
61
+
62
+ ## 1.0.31
63
+
64
+ ### Patch Changes
65
+
66
+ - @ai-sdk/harness@1.0.31
67
+
68
+ ## 1.0.30
69
+
70
+ ### Patch Changes
71
+
72
+ - @ai-sdk/harness@1.0.30
73
+
74
+ ## 1.0.29
75
+
76
+ ### Patch Changes
77
+
78
+ - @ai-sdk/harness@1.0.29
79
+
80
+ ## 1.0.28
81
+
82
+ ### Patch Changes
83
+
84
+ - @ai-sdk/harness@1.0.28
85
+
86
+ ## 1.0.27
87
+
88
+ ### Patch Changes
89
+
90
+ - @ai-sdk/harness@1.0.27
91
+
92
+ ## 1.0.26
93
+
94
+ ### Patch Changes
95
+
96
+ - @ai-sdk/harness@1.0.26
97
+
98
+ ## 1.0.25
99
+
100
+ ### Patch Changes
101
+
102
+ - Updated dependencies [44e988a]
103
+ - @ai-sdk/harness@1.0.25
104
+
105
+ ## 1.0.24
106
+
107
+ ### Patch Changes
108
+
109
+ - @ai-sdk/harness@1.0.24
110
+
111
+ ## 1.0.23
112
+
113
+ ### Patch Changes
114
+
115
+ - Updated dependencies [39c8276]
116
+ - Updated dependencies [91fe6d8]
117
+ - Updated dependencies [0be5014]
118
+ - @ai-sdk/harness@1.0.23
119
+
120
+ ## 1.0.22
121
+
122
+ ### Patch Changes
123
+
124
+ - @ai-sdk/harness@1.0.22
125
+
126
+ ## 1.0.21
127
+
128
+ ### Patch Changes
129
+
130
+ - @ai-sdk/harness@1.0.21
131
+
132
+ ## 1.0.20
133
+
134
+ ### Patch Changes
135
+
136
+ - Updated dependencies [b7aa06a]
137
+ - @ai-sdk/harness@1.0.20
138
+
139
+ ## 1.0.19
140
+
141
+ ### Patch Changes
142
+
143
+ - @ai-sdk/harness@1.0.19
144
+
145
+ ## 1.0.18
146
+
147
+ ### Patch Changes
148
+
149
+ - @ai-sdk/harness@1.0.18
150
+
151
+ ## 1.0.17
152
+
153
+ ### Patch Changes
154
+
155
+ - Updated dependencies [5c5c0f5]
156
+ - @ai-sdk/harness@1.0.17
157
+
158
+ ## 1.0.16
159
+
160
+ ### Patch Changes
161
+
162
+ - @ai-sdk/harness@1.0.16
163
+
164
+ ## 1.0.15
165
+
166
+ ### Patch Changes
167
+
168
+ - @ai-sdk/harness@1.0.15
169
+
170
+ ## 1.0.14
171
+
172
+ ### Patch Changes
173
+
174
+ - @ai-sdk/harness@1.0.14
175
+
176
+ ## 1.0.13
177
+
178
+ ### Patch Changes
179
+
180
+ - @ai-sdk/harness@1.0.13
181
+
182
+ ## 1.0.12
183
+
184
+ ### Patch Changes
185
+
186
+ - Updated dependencies [7859cea]
187
+ - Updated dependencies [c857346]
188
+ - @ai-sdk/harness@1.0.12
189
+
190
+ ## 1.0.11
191
+
192
+ ### Patch Changes
193
+
194
+ - @ai-sdk/harness@1.0.11
195
+
196
+ ## 1.0.10
197
+
198
+ ### Patch Changes
199
+
200
+ - @ai-sdk/harness@1.0.10
201
+
202
+ ## 1.0.9
203
+
204
+ ### Patch Changes
205
+
206
+ - @ai-sdk/harness@1.0.9
207
+
208
+ ## 1.0.8
209
+
210
+ ### Patch Changes
211
+
212
+ - @ai-sdk/harness@1.0.8
213
+
214
+ ## 1.0.7
215
+
216
+ ### Patch Changes
217
+
218
+ - @ai-sdk/harness@1.0.7
219
+
220
+ ## 1.0.6
221
+
222
+ ### Patch Changes
223
+
224
+ - @ai-sdk/harness@1.0.6
225
+
226
+ ## 1.0.5
227
+
228
+ ### Patch Changes
229
+
230
+ - @ai-sdk/harness@1.0.5
231
+
232
+ ## 1.0.4
233
+
234
+ ### Patch Changes
235
+
236
+ - Updated dependencies [c493634]
237
+ - @ai-sdk/harness@1.0.4
238
+
239
+ ## 1.0.3
240
+
241
+ ### Patch Changes
242
+
243
+ - Updated dependencies [51d10a0]
244
+ - @ai-sdk/harness@1.0.3
245
+
246
+ ## 1.0.2
247
+
248
+ ### Patch Changes
249
+
250
+ - @ai-sdk/harness@1.0.2
251
+
252
+ ## 1.0.1
253
+
254
+ ### Patch Changes
255
+
256
+ - @ai-sdk/harness@1.0.1
257
+
258
+ ## 1.0.0
259
+
260
+ ### Major Changes
261
+
262
+ - 6e33eb6: feat(workflow-harness): introduce workflow utilities for durable harness agent execution
263
+
264
+ ### Patch Changes
265
+
266
+ ## 1.0.0-beta.5
267
+
268
+ ### Patch Changes
269
+
270
+ - @ai-sdk/harness@1.0.0-beta.27
271
+
272
+ ## 1.0.0-beta.4
273
+
274
+ ### Patch Changes
275
+
276
+ - Updated dependencies [a83a367]
277
+ - @ai-sdk/harness@1.0.0-beta.26
278
+
279
+ ## 1.0.0-beta.3
280
+
281
+ ### Patch Changes
282
+
283
+ - @ai-sdk/harness@1.0.0-beta.25
284
+
285
+ ## 1.0.0-beta.2
286
+
287
+ ### Patch Changes
288
+
289
+ - @ai-sdk/harness@1.0.0-beta.24
290
+
291
+ ## 1.0.0-beta.1
292
+
293
+ ### Patch Changes
294
+
295
+ - Updated dependencies [57e0a59]
296
+ - @ai-sdk/harness@1.0.0-beta.23
297
+
298
+ ## 1.0.0-beta.0
299
+
300
+ ### Major Changes
301
+
302
+ - 6e33eb6: feat(workflow-harness): introduce workflow utilities for durable harness agent execution
303
+
304
+ ### Patch Changes
305
+
306
+ - @ai-sdk/harness@1.0.0-beta.22
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2023 Vercel, Inc.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # @ai-sdk/workflow-harness
2
+
3
+ Run an AI SDK `HarnessAgent` (Claude Code, Codex, Pi) as a **durable workflow**
4
+ using the [Workflow DevKit](https://www.npmjs.com/package/workflow).
5
+
6
+ A long agent turn is sliced into short, time-boxed steps so it survives a Fluid
7
+ Compute function recycle (~800s). Between slices the agent is frozen
8
+ non-destructively — the sandbox keeps running and the next slice reattaches to
9
+ the in-flight turn (`attach`) — and a serializable state object is persisted as
10
+ the durable step return value.
11
+
12
+ This package ships plain helpers + a serializable state machine; you own the
13
+ thin `'use workflow'` / `'use step'` wrappers (the Workflow DevKit compiles
14
+ those directives in your app).
15
+
16
+ Keep the Workflow DevKit entrypoints separate from the agent definition. The
17
+ workflow module should import only workflow-safe code plus step modules. The
18
+ step module should dynamically import the agent inside the `'use step'` body so
19
+ the agent, sandbox provider, and other Node-heavy dependencies stay out of the
20
+ compiled workflow bundle.
21
+
22
+ `agent.ts`:
23
+
24
+ ```ts
25
+ import { HarnessAgent } from '@ai-sdk/harness/agent';
26
+ import { claudeCode } from '@ai-sdk/harness-claude-code';
27
+ import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
28
+
29
+ export const agent = new HarnessAgent({
30
+ harness: claudeCode,
31
+ sandbox: createVercelSandbox({ runtime: 'node24', ports: [4000] }),
32
+ });
33
+ ```
34
+
35
+ `run-slice-step.ts`:
36
+
37
+ ```ts
38
+ import {
39
+ runHarnessAgentSlice,
40
+ type HarnessWorkflowState,
41
+ } from '@ai-sdk/workflow-harness';
42
+
43
+ export async function runSlice(
44
+ state: HarnessWorkflowState,
45
+ ): Promise<HarnessWorkflowState> {
46
+ 'use step';
47
+
48
+ const { agent } = await import('./agent');
49
+ return runHarnessAgentSlice({ agent, state });
50
+ }
51
+ ```
52
+
53
+ `workflow.ts`:
54
+
55
+ ```ts
56
+ import {
57
+ createHarnessWorkflowState,
58
+ finalizeHarnessWorkflow,
59
+ type HarnessWorkflowInput,
60
+ } from '@ai-sdk/workflow-harness';
61
+ import { runSlice } from './run-slice-step';
62
+
63
+ export async function codingWorkflow(input: {
64
+ prompt: HarnessWorkflowInput['prompt'];
65
+ sessionId: string;
66
+ }) {
67
+ 'use workflow';
68
+
69
+ let state = createHarnessWorkflowState(input);
70
+ while (state.status === 'running' || state.status === 'timed_out') {
71
+ state = await runSlice(state);
72
+ }
73
+ return finalizeHarnessWorkflow(state);
74
+ }
75
+ ```
76
+
77
+ `route.ts` (Next.js example):
78
+
79
+ ```ts
80
+ import { start } from 'workflow/api';
81
+ import { codingWorkflow } from './workflow';
82
+
83
+ export async function POST(request: Request) {
84
+ const body = (await request.json()) as {
85
+ prompt: string;
86
+ sessionId: string;
87
+ };
88
+ const run = await start(codingWorkflow, [body]);
89
+
90
+ return new Response(run.readable);
91
+ }
92
+ ```
@@ -0,0 +1,200 @@
1
+ import { HarnessV1Prompt, HarnessV1ResumeSessionState, HarnessV1ContinueTurnState } from '@ai-sdk/harness';
2
+ import { HarnessAgentSession } from '@ai-sdk/harness/agent';
3
+
4
+ type HarnessWorkflowModelMessage = {
5
+ readonly role: 'system';
6
+ readonly content: any;
7
+ } | {
8
+ readonly role: 'user';
9
+ readonly content: any;
10
+ } | {
11
+ readonly role: 'assistant';
12
+ readonly content: any;
13
+ } | {
14
+ readonly role: 'tool';
15
+ readonly content: any;
16
+ };
17
+ /**
18
+ * Where a workflow-driven harness run is in its slice loop.
19
+ *
20
+ * - `running` — fresh state, no slice has run yet.
21
+ * - `timed_out` — a slice hit its wall-clock budget; `continueFrom` carries
22
+ * the cursor to continue the same turn.
23
+ * - `awaiting_tool_approval` — the turn emitted one or more tool approval
24
+ * requests and `continueFrom` carries the suspended turn.
25
+ * - `finished` — the agent turn completed on its own; `finalResult` is set.
26
+ * - `failed` — the turn errored; `error` is set.
27
+ */
28
+ type HarnessWorkflowStatus = 'running' | 'timed_out' | 'awaiting_tool_approval' | 'finished' | 'failed';
29
+ interface HarnessWorkflowUsageSummary {
30
+ readonly inputTokens?: number;
31
+ readonly outputTokens?: number;
32
+ }
33
+ interface HarnessWorkflowFinalResult {
34
+ readonly sessionId: string;
35
+ readonly finishReason: string;
36
+ readonly usage?: HarnessWorkflowUsageSummary;
37
+ }
38
+ interface HarnessWorkflowSerializedChunk {
39
+ readonly type: string;
40
+ readonly [key: string]: unknown;
41
+ }
42
+ interface HarnessWorkflowStreamContext {
43
+ readonly activeTextParts?: Record<string, HarnessWorkflowSerializedChunk>;
44
+ readonly activeReasoningParts?: Record<string, HarnessWorkflowSerializedChunk>;
45
+ readonly pendingToolInputs?: Record<string, HarnessWorkflowSerializedChunk>;
46
+ }
47
+ /**
48
+ * Serializable state machine threaded between workflow slices. A `'use step'`
49
+ * returns the next value of this object, and the Workflow DevKit persists that
50
+ * return value — so this is the entire durable state of a harness run. Every
51
+ * field must be JSON-serializable.
52
+ *
53
+ * Two independent lifecycle states drive the engine:
54
+ *
55
+ * - `resumeFrom` reattaches to a warm session before starting this run's new
56
+ * user turn.
57
+ * - `continueFrom` reattaches to an interrupted turn from this same run and
58
+ * continues it without sending `prompt` again.
59
+ */
60
+ interface HarnessWorkflowState {
61
+ /**
62
+ * Stable harness session id; doubles as the sandbox name across processes.
63
+ * Reuse the chat/conversation id so every user turn resumes the same warm
64
+ * session and the agent retains prior-turn context.
65
+ */
66
+ readonly sessionId: string;
67
+ /**
68
+ * The new user turn for this run — a plain string or a single
69
+ * `UserModelMessage` (the harness's own {@link HarnessV1Prompt}), so
70
+ * structured content survives instead of being flattened to text. Sent once,
71
+ * on the slice that starts the turn.
72
+ */
73
+ readonly prompt: HarnessV1Prompt;
74
+ /**
75
+ * Full AI SDK model messages for continuing a suspended approval turn. When
76
+ * present, the next slice sends these to `HarnessAgent.stream()` so approval
77
+ * responses can resume the interrupted turn.
78
+ */
79
+ readonly messages?: HarnessWorkflowModelMessage[];
80
+ readonly status: HarnessWorkflowStatus;
81
+ /**
82
+ * Resume coordinates for the next user turn. Absent only on the first turn of
83
+ * a brand-new conversation or when the sandbox was destroyed after finish.
84
+ */
85
+ readonly resumeFrom?: HarnessV1ResumeSessionState;
86
+ /**
87
+ * Continuation coordinates for this run's current suspended turn. When
88
+ * present, the next slice must call `continueTurn` rather than sending
89
+ * `prompt` again.
90
+ */
91
+ readonly continueFrom?: HarnessV1ContinueTurnState;
92
+ readonly streamContext?: HarnessWorkflowStreamContext;
93
+ readonly finalResult?: HarnessWorkflowFinalResult;
94
+ readonly error?: string;
95
+ }
96
+ /**
97
+ * Input for one user turn — the argument to {@link createHarnessWorkflowState}
98
+ * and the natural shape for a workflow function's input. `sessionId` is required
99
+ * (and must be caller-supplied, since the workflow runtime forbids
100
+ * non-deterministic id generation inside a step) — reuse the conversation id so
101
+ * the sandbox name is stable across turns. Pass `resumeFrom` (the handle
102
+ * persisted after the previous turn) to resume the warm conversation; omit it
103
+ * only for the first turn of a new conversation.
104
+ */
105
+ interface HarnessWorkflowInput {
106
+ prompt?: HarnessV1Prompt;
107
+ messages?: HarnessWorkflowModelMessage[];
108
+ sessionId: string;
109
+ resumeFrom?: HarnessV1ResumeSessionState;
110
+ continueFrom?: HarnessV1ContinueTurnState;
111
+ }
112
+ /** Initial state for one user turn (see {@link HarnessWorkflowInput}). */
113
+ declare function createHarnessWorkflowState(input: HarnessWorkflowInput): HarnessWorkflowState;
114
+ /**
115
+ * Collapse a terminal state into its result. Throws if the run failed; returns
116
+ * the captured `finalResult` when finished, or a best-effort result otherwise.
117
+ */
118
+ declare function finalizeHarnessWorkflow(state: HarnessWorkflowState): HarnessWorkflowFinalResult;
119
+
120
+ /** The non-string arm of {@link HarnessV1Prompt} — a single `UserModelMessage`. */
121
+ type HarnessV1UserMessage = Exclude<HarnessV1Prompt, string>;
122
+ /** A UI-message-stream chunk. Kept structural so this package need not depend on `ai`. */
123
+ interface HarnessWorkflowChunk {
124
+ readonly type: string;
125
+ readonly [key: string]: unknown;
126
+ }
127
+ /**
128
+ * The subset of a harness `stream()` / `continueStream()` result the slice loop uses.
129
+ * `StreamTextResult` satisfies it structurally.
130
+ */
131
+ interface HarnessWorkflowStreamResult {
132
+ toUIMessageStream(): ReadableStream<HarnessWorkflowChunk>;
133
+ readonly finishReason: PromiseLike<unknown>;
134
+ readonly totalUsage: PromiseLike<unknown>;
135
+ }
136
+ /**
137
+ * The subset of `HarnessAgent` the slice loop drives. Declared structurally so
138
+ * the engine is decoupled from the concrete agent generics and easy to mock.
139
+ */
140
+ interface HarnessWorkflowAgent {
141
+ createSession(options?: {
142
+ sessionId?: string;
143
+ resumeFrom?: HarnessV1ResumeSessionState;
144
+ continueFrom?: HarnessV1ContinueTurnState;
145
+ }): Promise<HarnessAgentSession>;
146
+ stream(options: {
147
+ session: HarnessAgentSession;
148
+ /**
149
+ * The new user turn. A string or an array of user messages — the shape
150
+ * `HarnessAgent.stream` accepts (it collapses an array to its last user
151
+ * entry). The engine passes the run's single {@link HarnessV1Prompt}.
152
+ */
153
+ prompt: string | HarnessV1UserMessage[];
154
+ messages?: undefined;
155
+ } | {
156
+ session: HarnessAgentSession;
157
+ prompt?: undefined;
158
+ messages: HarnessWorkflowModelMessage[];
159
+ }): Promise<HarnessWorkflowStreamResult>;
160
+ continueStream(options: {
161
+ session: HarnessAgentSession;
162
+ }): Promise<HarnessWorkflowStreamResult>;
163
+ }
164
+ interface RunHarnessAgentSliceOptions {
165
+ readonly agent: HarnessWorkflowAgent;
166
+ readonly state: HarnessWorkflowState;
167
+ /** Wall-clock budget for this slice. Defaults to {@link DEFAULT_SLICE_TIMEOUT_SECONDS}. */
168
+ readonly sliceTimeoutSeconds?: number;
169
+ /**
170
+ * When the turn finishes, whether to destroy the sandbox. Defaults to `false`:
171
+ * the session is parked or stopped and a fresh resume state is returned in
172
+ * `resumeFrom`, so the next user turn reattaches to the same conversation
173
+ * (multi-turn chat). Set `true` for a one-shot run that should release the
174
+ * sandbox when the turn completes.
175
+ */
176
+ readonly destroyOnFinish?: boolean;
177
+ /**
178
+ * Where to write the turn's UI-message chunks. Defaults to the workflow's
179
+ * output stream (`getWritable()` from `workflow`). Inject a stream in tests
180
+ * to run the engine without a workflow runtime.
181
+ */
182
+ readonly writable?: WritableStream<HarnessWorkflowChunk>;
183
+ }
184
+ /**
185
+ * Run one durable slice of a harness agent turn.
186
+ *
187
+ * Intended to be the body of a consumer's `'use step'`: it resumes (or starts)
188
+ * the session, streams the turn's chunks to the workflow output, and races the
189
+ * turn against a wall-clock budget. If the budget fires first it freezes the
190
+ * turn with `session.suspendTurn()` (the sandbox keeps running) and returns a
191
+ * `timed_out` state carrying continuation state for the next slice; if the
192
+ * turn finishes first it returns a `finished` state with the result.
193
+ *
194
+ * The returned {@link HarnessWorkflowState} is serializable and is meant to be
195
+ * the step's return value — the Workflow DevKit persists it as the durable
196
+ * checkpoint between slices.
197
+ */
198
+ declare function runHarnessAgentSlice(options: RunHarnessAgentSliceOptions): Promise<HarnessWorkflowState>;
199
+
200
+ export { type HarnessWorkflowAgent, type HarnessWorkflowChunk, type HarnessWorkflowFinalResult, type HarnessWorkflowInput, type HarnessWorkflowModelMessage, type HarnessWorkflowState, type HarnessWorkflowStatus, type HarnessWorkflowStreamResult, type HarnessWorkflowUsageSummary, type RunHarnessAgentSliceOptions, createHarnessWorkflowState, finalizeHarnessWorkflow, runHarnessAgentSlice };