@ai-sdk/workflow-harness 1.0.70 → 1.0.71
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 +8 -0
- package/dist/index.js +1 -12
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/run-harness-agent.ts +0 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @ai-sdk/workflow-harness
|
|
2
2
|
|
|
3
|
+
## 1.0.71
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f9d847d: Avoid replaying a pending tool input when its output arrives in a later time slice.
|
|
8
|
+
- Updated dependencies [8d717b3]
|
|
9
|
+
- @ai-sdk/harness@1.0.71
|
|
10
|
+
|
|
3
11
|
## 1.0.70
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -191,8 +191,7 @@ function createMutableStreamContext(context) {
|
|
|
191
191
|
function createExecutionPartState() {
|
|
192
192
|
return {
|
|
193
193
|
openedTextParts: /* @__PURE__ */ new Set(),
|
|
194
|
-
openedReasoningParts: /* @__PURE__ */ new Set()
|
|
195
|
-
writtenToolInputs: /* @__PURE__ */ new Set()
|
|
194
|
+
openedReasoningParts: /* @__PURE__ */ new Set()
|
|
196
195
|
};
|
|
197
196
|
}
|
|
198
197
|
async function writeWorkflowChunk(options) {
|
|
@@ -211,11 +210,6 @@ async function writeRequiredPrelude(options) {
|
|
|
211
210
|
await writer.write(streamContext.activeReasoningParts[id]);
|
|
212
211
|
executionPartState.openedReasoningParts.add(id);
|
|
213
212
|
}
|
|
214
|
-
const toolCallId = stringProperty({ chunk, key: "toolCallId" });
|
|
215
|
-
if (toolCallId != null && needsToolInputPrelude(chunk) && streamContext.pendingToolInputs[toolCallId] != null && !executionPartState.writtenToolInputs.has(toolCallId)) {
|
|
216
|
-
await writer.write(streamContext.pendingToolInputs[toolCallId]);
|
|
217
|
-
executionPartState.writtenToolInputs.add(toolCallId);
|
|
218
|
-
}
|
|
219
213
|
}
|
|
220
214
|
function recordWorkflowChunk(options) {
|
|
221
215
|
const { chunk, streamContext, executionPartState } = options;
|
|
@@ -243,12 +237,10 @@ function recordWorkflowChunk(options) {
|
|
|
243
237
|
}
|
|
244
238
|
if (chunk.type === "tool-input-available" && toolCallId != null) {
|
|
245
239
|
streamContext.pendingToolInputs[toolCallId] = cloneChunk(chunk);
|
|
246
|
-
executionPartState.writtenToolInputs.add(toolCallId);
|
|
247
240
|
return;
|
|
248
241
|
}
|
|
249
242
|
if (chunk.type === "tool-input-error" && toolCallId != null) {
|
|
250
243
|
delete streamContext.pendingToolInputs[toolCallId];
|
|
251
|
-
executionPartState.writtenToolInputs.add(toolCallId);
|
|
252
244
|
return;
|
|
253
245
|
}
|
|
254
246
|
if ((chunk.type === "tool-output-error" || chunk.type === "tool-output-denied" || chunk.type === "tool-output-available" && chunk.preliminary !== true) && toolCallId != null) {
|
|
@@ -278,9 +270,6 @@ function serializeStreamContextField(context) {
|
|
|
278
270
|
};
|
|
279
271
|
return Object.keys(streamContext).length > 0 ? { streamContext } : {};
|
|
280
272
|
}
|
|
281
|
-
function needsToolInputPrelude(chunk) {
|
|
282
|
-
return chunk.type === "tool-approval-request" || chunk.type === "tool-output-available" || chunk.type === "tool-output-error" || chunk.type === "tool-output-denied";
|
|
283
|
-
}
|
|
284
273
|
function hasPendingHostInput(state) {
|
|
285
274
|
return (state.pendingToolApprovals?.length ?? 0) > 0 || (state.pendingToolResults?.length ?? 0) > 0;
|
|
286
275
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/harness-workflow-state.ts","../src/run-harness-agent.ts","../src/run-harness-agent-step.ts","../src/run-harness-agent-time-slice.ts","../src/run-harness-agent-slice.ts"],"sourcesContent":["import type {\n HarnessV1ContinueTurnState,\n HarnessV1Prompt,\n HarnessV1ResumeSessionState,\n} from '@ai-sdk/harness';\n\nexport type HarnessWorkflowModelMessage =\n | { readonly role: 'system'; readonly content: any }\n | { readonly role: 'user'; readonly content: any }\n | { readonly role: 'assistant'; readonly content: any }\n | { readonly role: 'tool'; readonly content: any };\n\n/**\n * Where a workflow-driven harness run is in its execution loop.\n *\n * - `not_started` — fresh state, no workflow step has run yet.\n * - `ready_for_next_step` — the turn remains unfinished and `continueFrom`\n * carries the cursor for the next workflow step.\n * - `awaiting_tool_approval` — the turn emitted one or more tool approval\n * requests and `continueFrom` carries the suspended turn.\n * - `finished` — the agent turn completed on its own; `finalResult` is set.\n * - `failed` — the turn errored; `error` is set.\n */\nexport type HarnessWorkflowStatus =\n | 'not_started'\n | 'ready_for_next_step'\n | 'awaiting_tool_approval'\n | 'finished'\n | 'failed'\n /** @deprecated Use `ready_for_next_step` instead. */\n | 'timed_out';\n\nexport interface HarnessWorkflowUsageSummary {\n readonly inputTokens?: number;\n readonly outputTokens?: number;\n}\n\nexport interface HarnessWorkflowFinalResult {\n readonly sessionId: string;\n readonly finishReason: string;\n readonly usage?: HarnessWorkflowUsageSummary;\n}\n\nexport interface HarnessWorkflowSerializedChunk {\n readonly type: string;\n readonly [key: string]: unknown;\n}\n\nexport interface HarnessWorkflowStreamContext {\n readonly activeTextParts?: Record<string, HarnessWorkflowSerializedChunk>;\n readonly activeReasoningParts?: Record<\n string,\n HarnessWorkflowSerializedChunk\n >;\n readonly pendingToolInputs?: Record<string, HarnessWorkflowSerializedChunk>;\n}\n\n/**\n * Serializable state machine threaded between workflow steps. A `'use step'`\n * returns the next value of this object, and the Workflow DevKit persists that\n * return value — so this is the entire durable state of a harness run. Every\n * field must be JSON-serializable.\n *\n * Two independent lifecycle states drive the engine:\n *\n * - `resumeFrom` reattaches to a warm session before starting this run's new\n * user turn.\n * - `continueFrom` reattaches to a suspended turn from this same run and\n * continues it without sending `prompt` again.\n */\nexport interface HarnessWorkflowState {\n /**\n * Stable harness session id; doubles as the sandbox name across processes.\n * Reuse the chat/conversation id so every user turn resumes the same warm\n * session and the agent retains prior-turn context.\n */\n readonly sessionId: string;\n /**\n * The new user turn for this run — a plain string or a single\n * `UserModelMessage` (the harness's own {@link HarnessV1Prompt}), so\n * structured content survives instead of being flattened to text. Sent once,\n * on the execution that starts the turn.\n */\n readonly prompt: HarnessV1Prompt;\n /**\n * Full AI SDK model messages for continuing a suspended approval turn. When\n * present, the next execution sends these to `HarnessAgent.stream()` so\n * approval responses can resume the suspended turn.\n */\n readonly messages?: HarnessWorkflowModelMessage[];\n readonly status: HarnessWorkflowStatus;\n /**\n * Resume coordinates for the next user turn. Absent only on the first turn of\n * a brand-new conversation or when the sandbox was destroyed after finish.\n */\n readonly resumeFrom?: HarnessV1ResumeSessionState;\n /**\n * Continuation coordinates for this run's current suspended turn. When\n * present, the next execution continues the turn rather than sending `prompt`\n * again.\n */\n readonly continueFrom?: HarnessV1ContinueTurnState;\n readonly streamContext?: HarnessWorkflowStreamContext;\n readonly finalResult?: HarnessWorkflowFinalResult;\n readonly error?: string;\n}\n\n/**\n * Input for one user turn — the argument to {@link createHarnessWorkflowState}\n * and the natural shape for a workflow function's input. `sessionId` is required\n * (and must be caller-supplied, since the workflow runtime forbids\n * non-deterministic id generation inside a step) — reuse the conversation id so\n * the sandbox name is stable across turns. Pass `resumeFrom` (the handle\n * persisted after the previous turn) to resume the warm conversation; omit it\n * only for the first turn of a new conversation.\n */\nexport interface HarnessWorkflowInput {\n prompt?: HarnessV1Prompt;\n messages?: HarnessWorkflowModelMessage[];\n sessionId: string;\n resumeFrom?: HarnessV1ResumeSessionState;\n continueFrom?: HarnessV1ContinueTurnState;\n}\n\n/** Initial state for one user turn (see {@link HarnessWorkflowInput}). */\nexport function createHarnessWorkflowState(\n input: HarnessWorkflowInput,\n): HarnessWorkflowState {\n return {\n sessionId: input.sessionId,\n prompt: input.prompt ?? '',\n ...(input.messages != null ? { messages: input.messages } : {}),\n status: 'not_started',\n ...(input.resumeFrom != null ? { resumeFrom: input.resumeFrom } : {}),\n ...(input.continueFrom != null ? { continueFrom: input.continueFrom } : {}),\n };\n}\n\n/**\n * Collapse a terminal state into its result. Throws if the run failed; returns\n * the captured `finalResult` when finished, or a best-effort result otherwise.\n */\nexport function finalizeHarnessWorkflow(\n state: HarnessWorkflowState,\n): HarnessWorkflowFinalResult {\n if (state.status === 'failed') {\n throw new Error(state.error ?? 'harness workflow failed');\n }\n if (state.finalResult) {\n return state.finalResult;\n }\n return { sessionId: state.sessionId, finishReason: 'unknown' };\n}\n","import type {\n HarnessV1ContinueTurnState,\n HarnessV1Prompt,\n HarnessV1ResumeSessionState,\n} from '@ai-sdk/harness';\nimport type { HarnessAgentSession } from '@ai-sdk/harness/agent';\nimport type {\n HarnessWorkflowModelMessage,\n HarnessWorkflowSerializedChunk,\n HarnessWorkflowState,\n HarnessWorkflowStreamContext,\n HarnessWorkflowUsageSummary,\n} from './harness-workflow-state';\n\n/** The non-string arm of {@link HarnessV1Prompt} — a single `UserModelMessage`. */\ntype HarnessV1UserMessage = Exclude<HarnessV1Prompt, string>;\n\n/** A UI-message-stream chunk. Kept structural so this package need not depend on `ai`. */\nexport interface HarnessWorkflowChunk {\n readonly type: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * The subset of a harness `stream()` / `continueStream()` result the runner uses.\n * `StreamTextResult` satisfies it structurally.\n */\nexport interface HarnessWorkflowStreamResult {\n toUIMessageStream(): ReadableStream<HarnessWorkflowChunk>;\n readonly finishReason: PromiseLike<unknown>;\n readonly totalUsage: PromiseLike<unknown>;\n}\n\n/**\n * The subset of `HarnessAgent` the runner drives. Declared structurally so\n * the engine is decoupled from the concrete agent generics and easy to mock.\n */\nexport interface HarnessWorkflowAgent {\n createSession(options?: {\n sessionId?: string;\n resumeFrom?: HarnessV1ResumeSessionState;\n continueFrom?: HarnessV1ContinueTurnState;\n }): Promise<HarnessAgentSession>;\n stream(\n options:\n | {\n session: HarnessAgentSession;\n /**\n * The new user turn. A string or an array of user messages — the shape\n * `HarnessAgent.stream` accepts (it collapses an array to its last user\n * entry). The engine passes the run's single {@link HarnessV1Prompt}.\n */\n prompt: string | HarnessV1UserMessage[];\n messages?: undefined;\n }\n | {\n session: HarnessAgentSession;\n prompt?: undefined;\n messages: HarnessWorkflowModelMessage[];\n },\n ): Promise<HarnessWorkflowStreamResult>;\n continueStream(options: {\n session: HarnessAgentSession;\n }): Promise<HarnessWorkflowStreamResult>;\n}\n\nexport interface RunHarnessAgentOptions {\n readonly agent: HarnessWorkflowAgent;\n readonly state: HarnessWorkflowState;\n readonly timeSliceSeconds?: number;\n /**\n * When the turn finishes, whether to destroy the sandbox. Defaults to `false`:\n * the session is parked or stopped and a fresh resume state is returned in\n * `resumeFrom`, so the next user turn reattaches to the same conversation\n * (multi-turn chat). Set `true` for a one-shot run that should release the\n * sandbox when the turn completes.\n */\n readonly destroyOnFinish?: boolean;\n /**\n * Where to write the turn's UI-message chunks. Defaults to the workflow's\n * output stream (`getWritable()` from `workflow`). Inject a stream in tests\n * to run the engine without a workflow runtime.\n */\n readonly writable?: WritableStream<HarnessWorkflowChunk>;\n}\n\n/**\n * Run one durable execution of a harness agent turn.\n *\n * Intended to be the body of a consumer's `'use step'`: it resumes (or starts)\n * the session and streams the turn's chunks to the workflow output. When\n * `timeSliceSeconds` is present, it also races the turn against that wall-clock\n * budget and suspends the turn when the time slice completes.\n *\n * The returned {@link HarnessWorkflowState} is serializable and is meant to be\n * the step's return value — the Workflow DevKit persists it as the durable\n * checkpoint between workflow steps.\n */\nexport async function runHarnessAgent(\n options: RunHarnessAgentOptions,\n): Promise<HarnessWorkflowState> {\n const { agent, state } = options;\n const destroyOnFinish = options.destroyOnFinish ?? false;\n\n const session =\n state.continueFrom != null\n ? await agent.createSession({\n sessionId: state.sessionId,\n continueFrom: state.continueFrom,\n })\n : state.resumeFrom != null\n ? await agent.createSession({\n sessionId: state.sessionId,\n resumeFrom: state.resumeFrom,\n })\n : await agent.createSession({ sessionId: state.sessionId });\n\n let result: HarnessWorkflowStreamResult;\n try {\n result =\n state.messages != null\n ? await agent.stream({\n session,\n messages: state.messages,\n })\n : state.continueFrom != null\n ? await agent.continueStream({ session })\n : await agent.stream({\n session,\n prompt:\n typeof state.prompt === 'string'\n ? state.prompt\n : [state.prompt],\n });\n } catch (err) {\n await destroyQuietly(session);\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'failed',\n ...(state.resumeFrom != null ? { resumeFrom: state.resumeFrom } : {}),\n ...(state.continueFrom != null\n ? { continueFrom: state.continueFrom }\n : {}),\n error: errorMessage(err),\n };\n }\n\n const writable = options.writable ?? (await resolveWorkflowWritable());\n const writer = writable.getWriter();\n const streamContext = createMutableStreamContext(state.streamContext);\n const executionPartState = createExecutionPartState();\n\n let suspendPromise: Promise<HarnessV1ContinueTurnState> | undefined;\n const timer =\n options.timeSliceSeconds == null\n ? undefined\n : setTimeout(() => {\n suspendPromise = session.suspendTurn();\n }, options.timeSliceSeconds * 1000);\n (timer as { unref?: () => void } | undefined)?.unref?.();\n\n let sawError = false;\n // Tracks whether the writable was closed (only on a finished turn). The\n // `finally` then releases the lock only when we did NOT close, since closing\n // already releases it.\n let writerClosed = false;\n try {\n const reader = result.toUIMessageStream().getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n if (value == null) continue;\n /*\n * One continuous assistant message per user turn: the execution that\n * starts the turn keeps the opening `start`; executions that continue\n * an already suspended turn drop it. Intermediate `finish` chunks are\n * dropped and the loop writes a single terminal `finish` itself when\n * the run completes. A new user turn keeps its `start` so the UI\n * renders it as a fresh assistant message.\n */\n if (value.type === 'start' && state.continueFrom != null) continue;\n if (value.type === 'finish') continue;\n if (value.type === 'error') {\n const errorText = (value as { errorText?: unknown }).errorText;\n /*\n * When a suspend is in flight we tore the turn down at the execution\n * boundary, so an *abort* error is the expected consequence —\n * swallow it (surfacing it would replace the streamed content with\n * an error, and the next execution continues the turn). Any\n * non-abort error is unanticipated and must NOT be silenced: surface\n * it and fail the execution, even mid-suspend.\n */\n if (suspendPromise != null && isAbortError(errorText)) {\n continue;\n }\n sawError = true;\n }\n await writeWorkflowChunk({\n chunk: value,\n writer,\n streamContext,\n executionPartState,\n });\n }\n } finally {\n if (timer != null) clearTimeout(timer);\n reader.releaseLock();\n }\n\n /*\n * A non-abort error (anything `sawError` survived the abort filter for) is\n * a real failure and takes priority over a completed time slice. Abort\n * errors during suspension were already filtered above.\n */\n if (sawError) {\n if (suspendPromise != null) await suspendPromise.catch(() => {});\n await destroyQuietly(session);\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'failed',\n ...(state.resumeFrom != null ? { resumeFrom: state.resumeFrom } : {}),\n ...(state.continueFrom != null\n ? { continueFrom: state.continueFrom }\n : {}),\n error: 'harness turn emitted an error',\n };\n }\n\n /*\n * The time slice completed: the turn keeps running in the sandbox. Persist\n * the cursor so the next time slice attaches to the same in-flight turn.\n */\n if (suspendPromise != null) {\n const continueFrom = await suspendPromise;\n await closeOpenExecutionParts({\n writer,\n streamContext,\n executionPartState,\n });\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'ready_for_next_step',\n continueFrom,\n ...serializeStreamContextField(streamContext),\n };\n }\n\n const [finishReason, usage] = await Promise.all([\n Promise.resolve(result.finishReason).catch(() => undefined),\n Promise.resolve(result.totalUsage).catch(() => undefined),\n ]);\n const normalizedFinishReason = toFinishReasonString(finishReason);\n\n if (session.hasUnfinishedTurn()) {\n const continueFrom = await session.suspendTurn();\n\n if (!hasPendingHostInput(continueFrom)) {\n await closeOpenExecutionParts({\n writer,\n streamContext,\n executionPartState,\n });\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'ready_for_next_step',\n continueFrom,\n ...serializeStreamContextField(streamContext),\n };\n }\n\n await writer.write({\n type: 'finish',\n finishReason: normalizedFinishReason,\n });\n await writer.close();\n writerClosed = true;\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'awaiting_tool_approval',\n continueFrom,\n resumeFrom: toResumeState({ continueFrom }),\n finalResult: {\n sessionId: state.sessionId,\n finishReason: normalizedFinishReason,\n usage: toUsageSummary(usage),\n },\n };\n }\n\n // The turn finished on its own: write the single terminal `finish` for the\n // UI message, then CLOSE the writable. Closing matters: the workflow output\n // stream (`getWritable()`) is what the run's `readable` is fed from, and the\n // DevKit only marks that run stream \"done\" when the writable is closed — a\n // released-but-open writer leaves it open forever, so a consumer piping\n // `run.readable` into a UI-message-stream response never receives the\n // terminal close and the client stays \"streaming\" indefinitely.\n // `ready_for_next_step` and `failed` deliberately do NOT close — another\n // execution keeps writing, or the failure propagates.\n await writer.write({ type: 'finish' });\n await writer.close();\n writerClosed = true;\n\n /*\n * Capture resume state for the *next user turn* before ending this local\n * session handle. `detach()` parks the session without stopping the sandbox;\n * bridge-backed sessions usually resume by attach/replay, while\n * host-resident sessions may resume by rerun. A one-shot consumer opts into\n * `destroyOnFinish` to destroy the sandbox instead.\n */\n let resumeFrom = state.resumeFrom;\n if (destroyOnFinish) {\n await destroyQuietly(session);\n resumeFrom = undefined;\n } else {\n resumeFrom = await session.detach().catch(() => state.resumeFrom);\n }\n\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'finished',\n ...(resumeFrom != null ? { resumeFrom } : {}),\n finalResult: {\n sessionId: state.sessionId,\n finishReason: normalizedFinishReason,\n usage: toUsageSummary(usage),\n },\n };\n } finally {\n if (!writerClosed) writer.releaseLock();\n }\n}\n\ntype MutableStreamContext = {\n activeTextParts: Record<string, HarnessWorkflowSerializedChunk>;\n activeReasoningParts: Record<string, HarnessWorkflowSerializedChunk>;\n pendingToolInputs: Record<string, HarnessWorkflowSerializedChunk>;\n};\n\ntype ExecutionPartState = {\n openedTextParts: Set<string>;\n openedReasoningParts: Set<string>;\n writtenToolInputs: Set<string>;\n};\n\nfunction createMutableStreamContext(\n context: HarnessWorkflowStreamContext | undefined,\n): MutableStreamContext {\n return {\n activeTextParts: { ...(context?.activeTextParts ?? {}) },\n activeReasoningParts: { ...(context?.activeReasoningParts ?? {}) },\n pendingToolInputs: { ...(context?.pendingToolInputs ?? {}) },\n };\n}\n\nfunction createExecutionPartState(): ExecutionPartState {\n return {\n openedTextParts: new Set(),\n openedReasoningParts: new Set(),\n writtenToolInputs: new Set(),\n };\n}\n\nasync function writeWorkflowChunk(options: {\n chunk: HarnessWorkflowChunk;\n writer: WritableStreamDefaultWriter<HarnessWorkflowChunk>;\n streamContext: MutableStreamContext;\n executionPartState: ExecutionPartState;\n}): Promise<void> {\n await writeRequiredPrelude(options);\n await options.writer.write(options.chunk);\n recordWorkflowChunk(options);\n}\n\nasync function writeRequiredPrelude(options: {\n chunk: HarnessWorkflowChunk;\n writer: WritableStreamDefaultWriter<HarnessWorkflowChunk>;\n streamContext: MutableStreamContext;\n executionPartState: ExecutionPartState;\n}): Promise<void> {\n const { chunk, writer, streamContext, executionPartState } = options;\n const id = stringProperty({ chunk, key: 'id' });\n\n if (\n (chunk.type === 'text-delta' || chunk.type === 'text-end') &&\n id != null &&\n streamContext.activeTextParts[id] != null &&\n !executionPartState.openedTextParts.has(id)\n ) {\n await writer.write(streamContext.activeTextParts[id]);\n executionPartState.openedTextParts.add(id);\n }\n\n if (\n (chunk.type === 'reasoning-delta' || chunk.type === 'reasoning-end') &&\n id != null &&\n streamContext.activeReasoningParts[id] != null &&\n !executionPartState.openedReasoningParts.has(id)\n ) {\n await writer.write(streamContext.activeReasoningParts[id]);\n executionPartState.openedReasoningParts.add(id);\n }\n\n const toolCallId = stringProperty({ chunk, key: 'toolCallId' });\n if (\n toolCallId != null &&\n needsToolInputPrelude(chunk) &&\n streamContext.pendingToolInputs[toolCallId] != null &&\n !executionPartState.writtenToolInputs.has(toolCallId)\n ) {\n await writer.write(streamContext.pendingToolInputs[toolCallId]);\n executionPartState.writtenToolInputs.add(toolCallId);\n }\n}\n\nfunction recordWorkflowChunk(options: {\n chunk: HarnessWorkflowChunk;\n streamContext: MutableStreamContext;\n executionPartState: ExecutionPartState;\n}): void {\n const { chunk, streamContext, executionPartState } = options;\n const id = stringProperty({ chunk, key: 'id' });\n const toolCallId = stringProperty({ chunk, key: 'toolCallId' });\n\n if (chunk.type === 'text-start' && id != null) {\n streamContext.activeTextParts[id] = cloneChunk(chunk);\n executionPartState.openedTextParts.add(id);\n return;\n }\n\n if (chunk.type === 'text-end' && id != null) {\n delete streamContext.activeTextParts[id];\n executionPartState.openedTextParts.delete(id);\n return;\n }\n\n if (chunk.type === 'reasoning-start' && id != null) {\n streamContext.activeReasoningParts[id] = cloneChunk(chunk);\n executionPartState.openedReasoningParts.add(id);\n return;\n }\n\n if (chunk.type === 'reasoning-end' && id != null) {\n delete streamContext.activeReasoningParts[id];\n executionPartState.openedReasoningParts.delete(id);\n return;\n }\n\n if (chunk.type === 'tool-input-available' && toolCallId != null) {\n streamContext.pendingToolInputs[toolCallId] = cloneChunk(chunk);\n executionPartState.writtenToolInputs.add(toolCallId);\n return;\n }\n\n if (chunk.type === 'tool-input-error' && toolCallId != null) {\n delete streamContext.pendingToolInputs[toolCallId];\n executionPartState.writtenToolInputs.add(toolCallId);\n return;\n }\n\n if (\n (chunk.type === 'tool-output-error' ||\n chunk.type === 'tool-output-denied' ||\n (chunk.type === 'tool-output-available' &&\n (chunk as { preliminary?: unknown }).preliminary !== true)) &&\n toolCallId != null\n ) {\n delete streamContext.pendingToolInputs[toolCallId];\n }\n}\n\nasync function closeOpenExecutionParts(options: {\n writer: WritableStreamDefaultWriter<HarnessWorkflowChunk>;\n streamContext: MutableStreamContext;\n executionPartState: ExecutionPartState;\n}): Promise<void> {\n const { writer, streamContext, executionPartState } = options;\n\n for (const id of executionPartState.openedTextParts) {\n if (streamContext.activeTextParts[id] != null) {\n await writer.write({ type: 'text-end', id });\n }\n }\n executionPartState.openedTextParts.clear();\n\n for (const id of executionPartState.openedReasoningParts) {\n if (streamContext.activeReasoningParts[id] != null) {\n await writer.write({ type: 'reasoning-end', id });\n }\n }\n executionPartState.openedReasoningParts.clear();\n}\n\nfunction serializeStreamContextField(context: MutableStreamContext): {\n streamContext?: HarnessWorkflowStreamContext;\n} {\n const streamContext: HarnessWorkflowStreamContext = {\n ...(Object.keys(context.activeTextParts).length > 0\n ? { activeTextParts: context.activeTextParts }\n : {}),\n ...(Object.keys(context.activeReasoningParts).length > 0\n ? { activeReasoningParts: context.activeReasoningParts }\n : {}),\n ...(Object.keys(context.pendingToolInputs).length > 0\n ? { pendingToolInputs: context.pendingToolInputs }\n : {}),\n };\n return Object.keys(streamContext).length > 0 ? { streamContext } : {};\n}\n\nfunction needsToolInputPrelude(chunk: HarnessWorkflowChunk): boolean {\n return (\n chunk.type === 'tool-approval-request' ||\n chunk.type === 'tool-output-available' ||\n chunk.type === 'tool-output-error' ||\n chunk.type === 'tool-output-denied'\n );\n}\n\nfunction hasPendingHostInput(state: HarnessV1ContinueTurnState): boolean {\n return (\n (state.pendingToolApprovals?.length ?? 0) > 0 ||\n (state.pendingToolResults?.length ?? 0) > 0\n );\n}\n\nfunction toResumeState(options: {\n continueFrom: HarnessV1ContinueTurnState;\n}): HarnessV1ResumeSessionState {\n const { continueFrom } = options;\n return {\n type: 'resume-session',\n harnessId: continueFrom.harnessId,\n specificationVersion: continueFrom.specificationVersion,\n data: continueFrom.data,\n continueFrom,\n };\n}\n\nfunction stringProperty(options: {\n chunk: HarnessWorkflowChunk;\n key: string;\n}): string | undefined {\n const value = options.chunk[options.key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction cloneChunk(\n chunk: HarnessWorkflowChunk,\n): HarnessWorkflowSerializedChunk {\n return { ...chunk };\n}\n\n/**\n * Resolve the workflow's default output stream lazily, so importing this module\n * never requires the `workflow` runtime (tests inject their own `writable`).\n */\nasync function resolveWorkflowWritable(): Promise<\n WritableStream<HarnessWorkflowChunk>\n> {\n const { getWritable } = (await import('workflow')) as {\n getWritable: <T>() => WritableStream<T>;\n };\n return getWritable<HarnessWorkflowChunk>();\n}\n\nasync function destroyQuietly(session: HarnessAgentSession): Promise<void> {\n await session.destroy().catch(() => {});\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Whether an error (or UI `error` part's `errorText`) is an abort — the\n * expected consequence of `suspendTurn()` tearing the turn down at a slice\n * boundary. Only these are safe to swallow during a suspend; any other error is\n * unanticipated and must surface.\n */\nfunction isAbortError(value: unknown): boolean {\n if (value == null) return false;\n if (\n typeof value === 'object' &&\n (value as { name?: unknown }).name === 'AbortError'\n ) {\n return true;\n }\n const text =\n typeof value === 'string'\n ? value\n : value instanceof Error\n ? value.message\n : String(value);\n return /\\baborted\\b|AbortError|operation was aborted/i.test(text);\n}\n\nfunction toFinishReasonString(finishReason: unknown): string {\n if (typeof finishReason === 'string') return finishReason;\n if (\n finishReason != null &&\n typeof finishReason === 'object' &&\n typeof (finishReason as { unified?: unknown }).unified === 'string'\n ) {\n return (finishReason as { unified: string }).unified;\n }\n return 'stop';\n}\n\nfunction toUsageSummary(\n usage: unknown,\n): HarnessWorkflowUsageSummary | undefined {\n if (usage == null || typeof usage !== 'object') return undefined;\n const u = usage as {\n inputTokens?: number | { total?: number };\n outputTokens?: number | { total?: number };\n };\n const inputTokens =\n typeof u.inputTokens === 'number' ? u.inputTokens : u.inputTokens?.total;\n const outputTokens =\n typeof u.outputTokens === 'number' ? u.outputTokens : u.outputTokens?.total;\n if (inputTokens == null && outputTokens == null) return undefined;\n return {\n ...(inputTokens != null ? { inputTokens } : {}),\n ...(outputTokens != null ? { outputTokens } : {}),\n };\n}\n","import type { HarnessWorkflowState } from './harness-workflow-state';\nimport {\n runHarnessAgent,\n type RunHarnessAgentOptions,\n} from './run-harness-agent';\n\nexport type RunHarnessAgentStepOptions = Omit<\n RunHarnessAgentOptions,\n 'timeSliceSeconds'\n>;\n\n/**\n * Run a harness agent until its next semantic step boundary.\n *\n * Configure the agent with a `stopWhen` condition such as `isStepCount(1)`.\n * When that condition completes a result while the underlying turn remains\n * unfinished, the returned state has status `ready_for_next_step` and carries\n * the continuation state for the next workflow step.\n */\nexport async function runHarnessAgentStep(\n options: RunHarnessAgentStepOptions,\n): Promise<HarnessWorkflowState> {\n return runHarnessAgent(options);\n}\n","import type { HarnessWorkflowState } from './harness-workflow-state';\nimport {\n runHarnessAgent,\n type RunHarnessAgentOptions,\n} from './run-harness-agent';\n\n/*\n * Vercel Fluid Compute recycles a function instance at approximately 800\n * seconds. Completing a time slice at 750 seconds leaves a safety buffer so\n * the next invocation can reattach to the still-running sandbox.\n */\nconst DEFAULT_TIME_SLICE_SECONDS = 750;\n\nexport interface RunHarnessAgentTimeSliceOptions extends Omit<\n RunHarnessAgentOptions,\n 'timeSliceSeconds'\n> {\n /**\n * Wall-clock budget for one time slice. Defaults to 750 seconds.\n */\n readonly timeSliceSeconds?: number;\n}\n\n/**\n * Run one time-boxed slice of a durable harness agent turn.\n *\n * When the time slice completes before the turn, the returned state has status\n * `ready_for_next_step` and carries the continuation state for the next slice.\n */\nexport async function runHarnessAgentTimeSlice(\n options: RunHarnessAgentTimeSliceOptions,\n): Promise<HarnessWorkflowState> {\n return runHarnessAgent({\n ...options,\n timeSliceSeconds: options.timeSliceSeconds ?? DEFAULT_TIME_SLICE_SECONDS,\n });\n}\n","import type { HarnessWorkflowState } from './harness-workflow-state';\nimport {\n runHarnessAgentTimeSlice,\n type RunHarnessAgentTimeSliceOptions,\n} from './run-harness-agent-time-slice';\n\nexport interface RunHarnessAgentSliceOptions extends Omit<\n RunHarnessAgentTimeSliceOptions,\n 'timeSliceSeconds'\n> {\n readonly timeSliceSeconds?: number;\n /**\n * @deprecated Use `timeSliceSeconds` instead.\n */\n readonly sliceTimeoutSeconds?: number;\n}\n\n/**\n * @deprecated Use {@link runHarnessAgentTimeSlice} instead.\n */\nexport async function runHarnessAgentSlice(\n options: RunHarnessAgentSliceOptions,\n): Promise<HarnessWorkflowState> {\n const { sliceTimeoutSeconds, timeSliceSeconds, ...timeSliceOptions } =\n options;\n const state = await runHarnessAgentTimeSlice({\n ...timeSliceOptions,\n timeSliceSeconds: timeSliceSeconds ?? sliceTimeoutSeconds,\n });\n\n return state.status === 'ready_for_next_step'\n ? { ...state, status: 'timed_out' }\n : state;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/harness-workflow-state.ts","../src/run-harness-agent.ts","../src/run-harness-agent-step.ts","../src/run-harness-agent-time-slice.ts","../src/run-harness-agent-slice.ts"],"sourcesContent":["import type {\n HarnessV1ContinueTurnState,\n HarnessV1Prompt,\n HarnessV1ResumeSessionState,\n} from '@ai-sdk/harness';\n\nexport type HarnessWorkflowModelMessage =\n | { readonly role: 'system'; readonly content: any }\n | { readonly role: 'user'; readonly content: any }\n | { readonly role: 'assistant'; readonly content: any }\n | { readonly role: 'tool'; readonly content: any };\n\n/**\n * Where a workflow-driven harness run is in its execution loop.\n *\n * - `not_started` — fresh state, no workflow step has run yet.\n * - `ready_for_next_step` — the turn remains unfinished and `continueFrom`\n * carries the cursor for the next workflow step.\n * - `awaiting_tool_approval` — the turn emitted one or more tool approval\n * requests and `continueFrom` carries the suspended turn.\n * - `finished` — the agent turn completed on its own; `finalResult` is set.\n * - `failed` — the turn errored; `error` is set.\n */\nexport type HarnessWorkflowStatus =\n | 'not_started'\n | 'ready_for_next_step'\n | 'awaiting_tool_approval'\n | 'finished'\n | 'failed'\n /** @deprecated Use `ready_for_next_step` instead. */\n | 'timed_out';\n\nexport interface HarnessWorkflowUsageSummary {\n readonly inputTokens?: number;\n readonly outputTokens?: number;\n}\n\nexport interface HarnessWorkflowFinalResult {\n readonly sessionId: string;\n readonly finishReason: string;\n readonly usage?: HarnessWorkflowUsageSummary;\n}\n\nexport interface HarnessWorkflowSerializedChunk {\n readonly type: string;\n readonly [key: string]: unknown;\n}\n\nexport interface HarnessWorkflowStreamContext {\n readonly activeTextParts?: Record<string, HarnessWorkflowSerializedChunk>;\n readonly activeReasoningParts?: Record<\n string,\n HarnessWorkflowSerializedChunk\n >;\n readonly pendingToolInputs?: Record<string, HarnessWorkflowSerializedChunk>;\n}\n\n/**\n * Serializable state machine threaded between workflow steps. A `'use step'`\n * returns the next value of this object, and the Workflow DevKit persists that\n * return value — so this is the entire durable state of a harness run. Every\n * field must be JSON-serializable.\n *\n * Two independent lifecycle states drive the engine:\n *\n * - `resumeFrom` reattaches to a warm session before starting this run's new\n * user turn.\n * - `continueFrom` reattaches to a suspended turn from this same run and\n * continues it without sending `prompt` again.\n */\nexport interface HarnessWorkflowState {\n /**\n * Stable harness session id; doubles as the sandbox name across processes.\n * Reuse the chat/conversation id so every user turn resumes the same warm\n * session and the agent retains prior-turn context.\n */\n readonly sessionId: string;\n /**\n * The new user turn for this run — a plain string or a single\n * `UserModelMessage` (the harness's own {@link HarnessV1Prompt}), so\n * structured content survives instead of being flattened to text. Sent once,\n * on the execution that starts the turn.\n */\n readonly prompt: HarnessV1Prompt;\n /**\n * Full AI SDK model messages for continuing a suspended approval turn. When\n * present, the next execution sends these to `HarnessAgent.stream()` so\n * approval responses can resume the suspended turn.\n */\n readonly messages?: HarnessWorkflowModelMessage[];\n readonly status: HarnessWorkflowStatus;\n /**\n * Resume coordinates for the next user turn. Absent only on the first turn of\n * a brand-new conversation or when the sandbox was destroyed after finish.\n */\n readonly resumeFrom?: HarnessV1ResumeSessionState;\n /**\n * Continuation coordinates for this run's current suspended turn. When\n * present, the next execution continues the turn rather than sending `prompt`\n * again.\n */\n readonly continueFrom?: HarnessV1ContinueTurnState;\n readonly streamContext?: HarnessWorkflowStreamContext;\n readonly finalResult?: HarnessWorkflowFinalResult;\n readonly error?: string;\n}\n\n/**\n * Input for one user turn — the argument to {@link createHarnessWorkflowState}\n * and the natural shape for a workflow function's input. `sessionId` is required\n * (and must be caller-supplied, since the workflow runtime forbids\n * non-deterministic id generation inside a step) — reuse the conversation id so\n * the sandbox name is stable across turns. Pass `resumeFrom` (the handle\n * persisted after the previous turn) to resume the warm conversation; omit it\n * only for the first turn of a new conversation.\n */\nexport interface HarnessWorkflowInput {\n prompt?: HarnessV1Prompt;\n messages?: HarnessWorkflowModelMessage[];\n sessionId: string;\n resumeFrom?: HarnessV1ResumeSessionState;\n continueFrom?: HarnessV1ContinueTurnState;\n}\n\n/** Initial state for one user turn (see {@link HarnessWorkflowInput}). */\nexport function createHarnessWorkflowState(\n input: HarnessWorkflowInput,\n): HarnessWorkflowState {\n return {\n sessionId: input.sessionId,\n prompt: input.prompt ?? '',\n ...(input.messages != null ? { messages: input.messages } : {}),\n status: 'not_started',\n ...(input.resumeFrom != null ? { resumeFrom: input.resumeFrom } : {}),\n ...(input.continueFrom != null ? { continueFrom: input.continueFrom } : {}),\n };\n}\n\n/**\n * Collapse a terminal state into its result. Throws if the run failed; returns\n * the captured `finalResult` when finished, or a best-effort result otherwise.\n */\nexport function finalizeHarnessWorkflow(\n state: HarnessWorkflowState,\n): HarnessWorkflowFinalResult {\n if (state.status === 'failed') {\n throw new Error(state.error ?? 'harness workflow failed');\n }\n if (state.finalResult) {\n return state.finalResult;\n }\n return { sessionId: state.sessionId, finishReason: 'unknown' };\n}\n","import type {\n HarnessV1ContinueTurnState,\n HarnessV1Prompt,\n HarnessV1ResumeSessionState,\n} from '@ai-sdk/harness';\nimport type { HarnessAgentSession } from '@ai-sdk/harness/agent';\nimport type {\n HarnessWorkflowModelMessage,\n HarnessWorkflowSerializedChunk,\n HarnessWorkflowState,\n HarnessWorkflowStreamContext,\n HarnessWorkflowUsageSummary,\n} from './harness-workflow-state';\n\n/** The non-string arm of {@link HarnessV1Prompt} — a single `UserModelMessage`. */\ntype HarnessV1UserMessage = Exclude<HarnessV1Prompt, string>;\n\n/** A UI-message-stream chunk. Kept structural so this package need not depend on `ai`. */\nexport interface HarnessWorkflowChunk {\n readonly type: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * The subset of a harness `stream()` / `continueStream()` result the runner uses.\n * `StreamTextResult` satisfies it structurally.\n */\nexport interface HarnessWorkflowStreamResult {\n toUIMessageStream(): ReadableStream<HarnessWorkflowChunk>;\n readonly finishReason: PromiseLike<unknown>;\n readonly totalUsage: PromiseLike<unknown>;\n}\n\n/**\n * The subset of `HarnessAgent` the runner drives. Declared structurally so\n * the engine is decoupled from the concrete agent generics and easy to mock.\n */\nexport interface HarnessWorkflowAgent {\n createSession(options?: {\n sessionId?: string;\n resumeFrom?: HarnessV1ResumeSessionState;\n continueFrom?: HarnessV1ContinueTurnState;\n }): Promise<HarnessAgentSession>;\n stream(\n options:\n | {\n session: HarnessAgentSession;\n /**\n * The new user turn. A string or an array of user messages — the shape\n * `HarnessAgent.stream` accepts (it collapses an array to its last user\n * entry). The engine passes the run's single {@link HarnessV1Prompt}.\n */\n prompt: string | HarnessV1UserMessage[];\n messages?: undefined;\n }\n | {\n session: HarnessAgentSession;\n prompt?: undefined;\n messages: HarnessWorkflowModelMessage[];\n },\n ): Promise<HarnessWorkflowStreamResult>;\n continueStream(options: {\n session: HarnessAgentSession;\n }): Promise<HarnessWorkflowStreamResult>;\n}\n\nexport interface RunHarnessAgentOptions {\n readonly agent: HarnessWorkflowAgent;\n readonly state: HarnessWorkflowState;\n readonly timeSliceSeconds?: number;\n /**\n * When the turn finishes, whether to destroy the sandbox. Defaults to `false`:\n * the session is parked or stopped and a fresh resume state is returned in\n * `resumeFrom`, so the next user turn reattaches to the same conversation\n * (multi-turn chat). Set `true` for a one-shot run that should release the\n * sandbox when the turn completes.\n */\n readonly destroyOnFinish?: boolean;\n /**\n * Where to write the turn's UI-message chunks. Defaults to the workflow's\n * output stream (`getWritable()` from `workflow`). Inject a stream in tests\n * to run the engine without a workflow runtime.\n */\n readonly writable?: WritableStream<HarnessWorkflowChunk>;\n}\n\n/**\n * Run one durable execution of a harness agent turn.\n *\n * Intended to be the body of a consumer's `'use step'`: it resumes (or starts)\n * the session and streams the turn's chunks to the workflow output. When\n * `timeSliceSeconds` is present, it also races the turn against that wall-clock\n * budget and suspends the turn when the time slice completes.\n *\n * The returned {@link HarnessWorkflowState} is serializable and is meant to be\n * the step's return value — the Workflow DevKit persists it as the durable\n * checkpoint between workflow steps.\n */\nexport async function runHarnessAgent(\n options: RunHarnessAgentOptions,\n): Promise<HarnessWorkflowState> {\n const { agent, state } = options;\n const destroyOnFinish = options.destroyOnFinish ?? false;\n\n const session =\n state.continueFrom != null\n ? await agent.createSession({\n sessionId: state.sessionId,\n continueFrom: state.continueFrom,\n })\n : state.resumeFrom != null\n ? await agent.createSession({\n sessionId: state.sessionId,\n resumeFrom: state.resumeFrom,\n })\n : await agent.createSession({ sessionId: state.sessionId });\n\n let result: HarnessWorkflowStreamResult;\n try {\n result =\n state.messages != null\n ? await agent.stream({\n session,\n messages: state.messages,\n })\n : state.continueFrom != null\n ? await agent.continueStream({ session })\n : await agent.stream({\n session,\n prompt:\n typeof state.prompt === 'string'\n ? state.prompt\n : [state.prompt],\n });\n } catch (err) {\n await destroyQuietly(session);\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'failed',\n ...(state.resumeFrom != null ? { resumeFrom: state.resumeFrom } : {}),\n ...(state.continueFrom != null\n ? { continueFrom: state.continueFrom }\n : {}),\n error: errorMessage(err),\n };\n }\n\n const writable = options.writable ?? (await resolveWorkflowWritable());\n const writer = writable.getWriter();\n const streamContext = createMutableStreamContext(state.streamContext);\n const executionPartState = createExecutionPartState();\n\n let suspendPromise: Promise<HarnessV1ContinueTurnState> | undefined;\n const timer =\n options.timeSliceSeconds == null\n ? undefined\n : setTimeout(() => {\n suspendPromise = session.suspendTurn();\n }, options.timeSliceSeconds * 1000);\n (timer as { unref?: () => void } | undefined)?.unref?.();\n\n let sawError = false;\n // Tracks whether the writable was closed (only on a finished turn). The\n // `finally` then releases the lock only when we did NOT close, since closing\n // already releases it.\n let writerClosed = false;\n try {\n const reader = result.toUIMessageStream().getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n if (value == null) continue;\n /*\n * One continuous assistant message per user turn: the execution that\n * starts the turn keeps the opening `start`; executions that continue\n * an already suspended turn drop it. Intermediate `finish` chunks are\n * dropped and the loop writes a single terminal `finish` itself when\n * the run completes. A new user turn keeps its `start` so the UI\n * renders it as a fresh assistant message.\n */\n if (value.type === 'start' && state.continueFrom != null) continue;\n if (value.type === 'finish') continue;\n if (value.type === 'error') {\n const errorText = (value as { errorText?: unknown }).errorText;\n /*\n * When a suspend is in flight we tore the turn down at the execution\n * boundary, so an *abort* error is the expected consequence —\n * swallow it (surfacing it would replace the streamed content with\n * an error, and the next execution continues the turn). Any\n * non-abort error is unanticipated and must NOT be silenced: surface\n * it and fail the execution, even mid-suspend.\n */\n if (suspendPromise != null && isAbortError(errorText)) {\n continue;\n }\n sawError = true;\n }\n await writeWorkflowChunk({\n chunk: value,\n writer,\n streamContext,\n executionPartState,\n });\n }\n } finally {\n if (timer != null) clearTimeout(timer);\n reader.releaseLock();\n }\n\n /*\n * A non-abort error (anything `sawError` survived the abort filter for) is\n * a real failure and takes priority over a completed time slice. Abort\n * errors during suspension were already filtered above.\n */\n if (sawError) {\n if (suspendPromise != null) await suspendPromise.catch(() => {});\n await destroyQuietly(session);\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'failed',\n ...(state.resumeFrom != null ? { resumeFrom: state.resumeFrom } : {}),\n ...(state.continueFrom != null\n ? { continueFrom: state.continueFrom }\n : {}),\n error: 'harness turn emitted an error',\n };\n }\n\n /*\n * The time slice completed: the turn keeps running in the sandbox. Persist\n * the cursor so the next time slice attaches to the same in-flight turn.\n */\n if (suspendPromise != null) {\n const continueFrom = await suspendPromise;\n await closeOpenExecutionParts({\n writer,\n streamContext,\n executionPartState,\n });\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'ready_for_next_step',\n continueFrom,\n ...serializeStreamContextField(streamContext),\n };\n }\n\n const [finishReason, usage] = await Promise.all([\n Promise.resolve(result.finishReason).catch(() => undefined),\n Promise.resolve(result.totalUsage).catch(() => undefined),\n ]);\n const normalizedFinishReason = toFinishReasonString(finishReason);\n\n if (session.hasUnfinishedTurn()) {\n const continueFrom = await session.suspendTurn();\n\n if (!hasPendingHostInput(continueFrom)) {\n await closeOpenExecutionParts({\n writer,\n streamContext,\n executionPartState,\n });\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'ready_for_next_step',\n continueFrom,\n ...serializeStreamContextField(streamContext),\n };\n }\n\n await writer.write({\n type: 'finish',\n finishReason: normalizedFinishReason,\n });\n await writer.close();\n writerClosed = true;\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'awaiting_tool_approval',\n continueFrom,\n resumeFrom: toResumeState({ continueFrom }),\n finalResult: {\n sessionId: state.sessionId,\n finishReason: normalizedFinishReason,\n usage: toUsageSummary(usage),\n },\n };\n }\n\n // The turn finished on its own: write the single terminal `finish` for the\n // UI message, then CLOSE the writable. Closing matters: the workflow output\n // stream (`getWritable()`) is what the run's `readable` is fed from, and the\n // DevKit only marks that run stream \"done\" when the writable is closed — a\n // released-but-open writer leaves it open forever, so a consumer piping\n // `run.readable` into a UI-message-stream response never receives the\n // terminal close and the client stays \"streaming\" indefinitely.\n // `ready_for_next_step` and `failed` deliberately do NOT close — another\n // execution keeps writing, or the failure propagates.\n await writer.write({ type: 'finish' });\n await writer.close();\n writerClosed = true;\n\n /*\n * Capture resume state for the *next user turn* before ending this local\n * session handle. `detach()` parks the session without stopping the sandbox;\n * bridge-backed sessions usually resume by attach/replay, while\n * host-resident sessions may resume by rerun. A one-shot consumer opts into\n * `destroyOnFinish` to destroy the sandbox instead.\n */\n let resumeFrom = state.resumeFrom;\n if (destroyOnFinish) {\n await destroyQuietly(session);\n resumeFrom = undefined;\n } else {\n resumeFrom = await session.detach().catch(() => state.resumeFrom);\n }\n\n return {\n sessionId: state.sessionId,\n prompt: state.prompt,\n status: 'finished',\n ...(resumeFrom != null ? { resumeFrom } : {}),\n finalResult: {\n sessionId: state.sessionId,\n finishReason: normalizedFinishReason,\n usage: toUsageSummary(usage),\n },\n };\n } finally {\n if (!writerClosed) writer.releaseLock();\n }\n}\n\ntype MutableStreamContext = {\n activeTextParts: Record<string, HarnessWorkflowSerializedChunk>;\n activeReasoningParts: Record<string, HarnessWorkflowSerializedChunk>;\n pendingToolInputs: Record<string, HarnessWorkflowSerializedChunk>;\n};\n\ntype ExecutionPartState = {\n openedTextParts: Set<string>;\n openedReasoningParts: Set<string>;\n};\n\nfunction createMutableStreamContext(\n context: HarnessWorkflowStreamContext | undefined,\n): MutableStreamContext {\n return {\n activeTextParts: { ...(context?.activeTextParts ?? {}) },\n activeReasoningParts: { ...(context?.activeReasoningParts ?? {}) },\n pendingToolInputs: { ...(context?.pendingToolInputs ?? {}) },\n };\n}\n\nfunction createExecutionPartState(): ExecutionPartState {\n return {\n openedTextParts: new Set(),\n openedReasoningParts: new Set(),\n };\n}\n\nasync function writeWorkflowChunk(options: {\n chunk: HarnessWorkflowChunk;\n writer: WritableStreamDefaultWriter<HarnessWorkflowChunk>;\n streamContext: MutableStreamContext;\n executionPartState: ExecutionPartState;\n}): Promise<void> {\n await writeRequiredPrelude(options);\n await options.writer.write(options.chunk);\n recordWorkflowChunk(options);\n}\n\nasync function writeRequiredPrelude(options: {\n chunk: HarnessWorkflowChunk;\n writer: WritableStreamDefaultWriter<HarnessWorkflowChunk>;\n streamContext: MutableStreamContext;\n executionPartState: ExecutionPartState;\n}): Promise<void> {\n const { chunk, writer, streamContext, executionPartState } = options;\n const id = stringProperty({ chunk, key: 'id' });\n\n if (\n (chunk.type === 'text-delta' || chunk.type === 'text-end') &&\n id != null &&\n streamContext.activeTextParts[id] != null &&\n !executionPartState.openedTextParts.has(id)\n ) {\n await writer.write(streamContext.activeTextParts[id]);\n executionPartState.openedTextParts.add(id);\n }\n\n if (\n (chunk.type === 'reasoning-delta' || chunk.type === 'reasoning-end') &&\n id != null &&\n streamContext.activeReasoningParts[id] != null &&\n !executionPartState.openedReasoningParts.has(id)\n ) {\n await writer.write(streamContext.activeReasoningParts[id]);\n executionPartState.openedReasoningParts.add(id);\n }\n}\n\nfunction recordWorkflowChunk(options: {\n chunk: HarnessWorkflowChunk;\n streamContext: MutableStreamContext;\n executionPartState: ExecutionPartState;\n}): void {\n const { chunk, streamContext, executionPartState } = options;\n const id = stringProperty({ chunk, key: 'id' });\n const toolCallId = stringProperty({ chunk, key: 'toolCallId' });\n\n if (chunk.type === 'text-start' && id != null) {\n streamContext.activeTextParts[id] = cloneChunk(chunk);\n executionPartState.openedTextParts.add(id);\n return;\n }\n\n if (chunk.type === 'text-end' && id != null) {\n delete streamContext.activeTextParts[id];\n executionPartState.openedTextParts.delete(id);\n return;\n }\n\n if (chunk.type === 'reasoning-start' && id != null) {\n streamContext.activeReasoningParts[id] = cloneChunk(chunk);\n executionPartState.openedReasoningParts.add(id);\n return;\n }\n\n if (chunk.type === 'reasoning-end' && id != null) {\n delete streamContext.activeReasoningParts[id];\n executionPartState.openedReasoningParts.delete(id);\n return;\n }\n\n if (chunk.type === 'tool-input-available' && toolCallId != null) {\n streamContext.pendingToolInputs[toolCallId] = cloneChunk(chunk);\n return;\n }\n\n if (chunk.type === 'tool-input-error' && toolCallId != null) {\n delete streamContext.pendingToolInputs[toolCallId];\n return;\n }\n\n if (\n (chunk.type === 'tool-output-error' ||\n chunk.type === 'tool-output-denied' ||\n (chunk.type === 'tool-output-available' &&\n (chunk as { preliminary?: unknown }).preliminary !== true)) &&\n toolCallId != null\n ) {\n delete streamContext.pendingToolInputs[toolCallId];\n }\n}\n\nasync function closeOpenExecutionParts(options: {\n writer: WritableStreamDefaultWriter<HarnessWorkflowChunk>;\n streamContext: MutableStreamContext;\n executionPartState: ExecutionPartState;\n}): Promise<void> {\n const { writer, streamContext, executionPartState } = options;\n\n for (const id of executionPartState.openedTextParts) {\n if (streamContext.activeTextParts[id] != null) {\n await writer.write({ type: 'text-end', id });\n }\n }\n executionPartState.openedTextParts.clear();\n\n for (const id of executionPartState.openedReasoningParts) {\n if (streamContext.activeReasoningParts[id] != null) {\n await writer.write({ type: 'reasoning-end', id });\n }\n }\n executionPartState.openedReasoningParts.clear();\n}\n\nfunction serializeStreamContextField(context: MutableStreamContext): {\n streamContext?: HarnessWorkflowStreamContext;\n} {\n const streamContext: HarnessWorkflowStreamContext = {\n ...(Object.keys(context.activeTextParts).length > 0\n ? { activeTextParts: context.activeTextParts }\n : {}),\n ...(Object.keys(context.activeReasoningParts).length > 0\n ? { activeReasoningParts: context.activeReasoningParts }\n : {}),\n ...(Object.keys(context.pendingToolInputs).length > 0\n ? { pendingToolInputs: context.pendingToolInputs }\n : {}),\n };\n return Object.keys(streamContext).length > 0 ? { streamContext } : {};\n}\n\nfunction hasPendingHostInput(state: HarnessV1ContinueTurnState): boolean {\n return (\n (state.pendingToolApprovals?.length ?? 0) > 0 ||\n (state.pendingToolResults?.length ?? 0) > 0\n );\n}\n\nfunction toResumeState(options: {\n continueFrom: HarnessV1ContinueTurnState;\n}): HarnessV1ResumeSessionState {\n const { continueFrom } = options;\n return {\n type: 'resume-session',\n harnessId: continueFrom.harnessId,\n specificationVersion: continueFrom.specificationVersion,\n data: continueFrom.data,\n continueFrom,\n };\n}\n\nfunction stringProperty(options: {\n chunk: HarnessWorkflowChunk;\n key: string;\n}): string | undefined {\n const value = options.chunk[options.key];\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction cloneChunk(\n chunk: HarnessWorkflowChunk,\n): HarnessWorkflowSerializedChunk {\n return { ...chunk };\n}\n\n/**\n * Resolve the workflow's default output stream lazily, so importing this module\n * never requires the `workflow` runtime (tests inject their own `writable`).\n */\nasync function resolveWorkflowWritable(): Promise<\n WritableStream<HarnessWorkflowChunk>\n> {\n const { getWritable } = (await import('workflow')) as {\n getWritable: <T>() => WritableStream<T>;\n };\n return getWritable<HarnessWorkflowChunk>();\n}\n\nasync function destroyQuietly(session: HarnessAgentSession): Promise<void> {\n await session.destroy().catch(() => {});\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Whether an error (or UI `error` part's `errorText`) is an abort — the\n * expected consequence of `suspendTurn()` tearing the turn down at a slice\n * boundary. Only these are safe to swallow during a suspend; any other error is\n * unanticipated and must surface.\n */\nfunction isAbortError(value: unknown): boolean {\n if (value == null) return false;\n if (\n typeof value === 'object' &&\n (value as { name?: unknown }).name === 'AbortError'\n ) {\n return true;\n }\n const text =\n typeof value === 'string'\n ? value\n : value instanceof Error\n ? value.message\n : String(value);\n return /\\baborted\\b|AbortError|operation was aborted/i.test(text);\n}\n\nfunction toFinishReasonString(finishReason: unknown): string {\n if (typeof finishReason === 'string') return finishReason;\n if (\n finishReason != null &&\n typeof finishReason === 'object' &&\n typeof (finishReason as { unified?: unknown }).unified === 'string'\n ) {\n return (finishReason as { unified: string }).unified;\n }\n return 'stop';\n}\n\nfunction toUsageSummary(\n usage: unknown,\n): HarnessWorkflowUsageSummary | undefined {\n if (usage == null || typeof usage !== 'object') return undefined;\n const u = usage as {\n inputTokens?: number | { total?: number };\n outputTokens?: number | { total?: number };\n };\n const inputTokens =\n typeof u.inputTokens === 'number' ? u.inputTokens : u.inputTokens?.total;\n const outputTokens =\n typeof u.outputTokens === 'number' ? u.outputTokens : u.outputTokens?.total;\n if (inputTokens == null && outputTokens == null) return undefined;\n return {\n ...(inputTokens != null ? { inputTokens } : {}),\n ...(outputTokens != null ? { outputTokens } : {}),\n };\n}\n","import type { HarnessWorkflowState } from './harness-workflow-state';\nimport {\n runHarnessAgent,\n type RunHarnessAgentOptions,\n} from './run-harness-agent';\n\nexport type RunHarnessAgentStepOptions = Omit<\n RunHarnessAgentOptions,\n 'timeSliceSeconds'\n>;\n\n/**\n * Run a harness agent until its next semantic step boundary.\n *\n * Configure the agent with a `stopWhen` condition such as `isStepCount(1)`.\n * When that condition completes a result while the underlying turn remains\n * unfinished, the returned state has status `ready_for_next_step` and carries\n * the continuation state for the next workflow step.\n */\nexport async function runHarnessAgentStep(\n options: RunHarnessAgentStepOptions,\n): Promise<HarnessWorkflowState> {\n return runHarnessAgent(options);\n}\n","import type { HarnessWorkflowState } from './harness-workflow-state';\nimport {\n runHarnessAgent,\n type RunHarnessAgentOptions,\n} from './run-harness-agent';\n\n/*\n * Vercel Fluid Compute recycles a function instance at approximately 800\n * seconds. Completing a time slice at 750 seconds leaves a safety buffer so\n * the next invocation can reattach to the still-running sandbox.\n */\nconst DEFAULT_TIME_SLICE_SECONDS = 750;\n\nexport interface RunHarnessAgentTimeSliceOptions extends Omit<\n RunHarnessAgentOptions,\n 'timeSliceSeconds'\n> {\n /**\n * Wall-clock budget for one time slice. Defaults to 750 seconds.\n */\n readonly timeSliceSeconds?: number;\n}\n\n/**\n * Run one time-boxed slice of a durable harness agent turn.\n *\n * When the time slice completes before the turn, the returned state has status\n * `ready_for_next_step` and carries the continuation state for the next slice.\n */\nexport async function runHarnessAgentTimeSlice(\n options: RunHarnessAgentTimeSliceOptions,\n): Promise<HarnessWorkflowState> {\n return runHarnessAgent({\n ...options,\n timeSliceSeconds: options.timeSliceSeconds ?? DEFAULT_TIME_SLICE_SECONDS,\n });\n}\n","import type { HarnessWorkflowState } from './harness-workflow-state';\nimport {\n runHarnessAgentTimeSlice,\n type RunHarnessAgentTimeSliceOptions,\n} from './run-harness-agent-time-slice';\n\nexport interface RunHarnessAgentSliceOptions extends Omit<\n RunHarnessAgentTimeSliceOptions,\n 'timeSliceSeconds'\n> {\n readonly timeSliceSeconds?: number;\n /**\n * @deprecated Use `timeSliceSeconds` instead.\n */\n readonly sliceTimeoutSeconds?: number;\n}\n\n/**\n * @deprecated Use {@link runHarnessAgentTimeSlice} instead.\n */\nexport async function runHarnessAgentSlice(\n options: RunHarnessAgentSliceOptions,\n): Promise<HarnessWorkflowState> {\n const { sliceTimeoutSeconds, timeSliceSeconds, ...timeSliceOptions } =\n options;\n const state = await runHarnessAgentTimeSlice({\n ...timeSliceOptions,\n timeSliceSeconds: timeSliceSeconds ?? sliceTimeoutSeconds,\n });\n\n return state.status === 'ready_for_next_step'\n ? { ...state, status: 'timed_out' }\n : state;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/workflow-harness",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.71",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@ai-sdk/harness": "1.0.
|
|
29
|
+
"@ai-sdk/harness": "1.0.71"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"workflow": "^4.2.1"
|
package/src/run-harness-agent.ts
CHANGED
|
@@ -346,7 +346,6 @@ type MutableStreamContext = {
|
|
|
346
346
|
type ExecutionPartState = {
|
|
347
347
|
openedTextParts: Set<string>;
|
|
348
348
|
openedReasoningParts: Set<string>;
|
|
349
|
-
writtenToolInputs: Set<string>;
|
|
350
349
|
};
|
|
351
350
|
|
|
352
351
|
function createMutableStreamContext(
|
|
@@ -363,7 +362,6 @@ function createExecutionPartState(): ExecutionPartState {
|
|
|
363
362
|
return {
|
|
364
363
|
openedTextParts: new Set(),
|
|
365
364
|
openedReasoningParts: new Set(),
|
|
366
|
-
writtenToolInputs: new Set(),
|
|
367
365
|
};
|
|
368
366
|
}
|
|
369
367
|
|
|
@@ -406,17 +404,6 @@ async function writeRequiredPrelude(options: {
|
|
|
406
404
|
await writer.write(streamContext.activeReasoningParts[id]);
|
|
407
405
|
executionPartState.openedReasoningParts.add(id);
|
|
408
406
|
}
|
|
409
|
-
|
|
410
|
-
const toolCallId = stringProperty({ chunk, key: 'toolCallId' });
|
|
411
|
-
if (
|
|
412
|
-
toolCallId != null &&
|
|
413
|
-
needsToolInputPrelude(chunk) &&
|
|
414
|
-
streamContext.pendingToolInputs[toolCallId] != null &&
|
|
415
|
-
!executionPartState.writtenToolInputs.has(toolCallId)
|
|
416
|
-
) {
|
|
417
|
-
await writer.write(streamContext.pendingToolInputs[toolCallId]);
|
|
418
|
-
executionPartState.writtenToolInputs.add(toolCallId);
|
|
419
|
-
}
|
|
420
407
|
}
|
|
421
408
|
|
|
422
409
|
function recordWorkflowChunk(options: {
|
|
@@ -454,13 +441,11 @@ function recordWorkflowChunk(options: {
|
|
|
454
441
|
|
|
455
442
|
if (chunk.type === 'tool-input-available' && toolCallId != null) {
|
|
456
443
|
streamContext.pendingToolInputs[toolCallId] = cloneChunk(chunk);
|
|
457
|
-
executionPartState.writtenToolInputs.add(toolCallId);
|
|
458
444
|
return;
|
|
459
445
|
}
|
|
460
446
|
|
|
461
447
|
if (chunk.type === 'tool-input-error' && toolCallId != null) {
|
|
462
448
|
delete streamContext.pendingToolInputs[toolCallId];
|
|
463
|
-
executionPartState.writtenToolInputs.add(toolCallId);
|
|
464
449
|
return;
|
|
465
450
|
}
|
|
466
451
|
|
|
@@ -514,15 +499,6 @@ function serializeStreamContextField(context: MutableStreamContext): {
|
|
|
514
499
|
return Object.keys(streamContext).length > 0 ? { streamContext } : {};
|
|
515
500
|
}
|
|
516
501
|
|
|
517
|
-
function needsToolInputPrelude(chunk: HarnessWorkflowChunk): boolean {
|
|
518
|
-
return (
|
|
519
|
-
chunk.type === 'tool-approval-request' ||
|
|
520
|
-
chunk.type === 'tool-output-available' ||
|
|
521
|
-
chunk.type === 'tool-output-error' ||
|
|
522
|
-
chunk.type === 'tool-output-denied'
|
|
523
|
-
);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
502
|
function hasPendingHostInput(state: HarnessV1ContinueTurnState): boolean {
|
|
527
503
|
return (
|
|
528
504
|
(state.pendingToolApprovals?.length ?? 0) > 0 ||
|