@ai-sdk/workflow 2.0.7 → 2.0.9
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 +20 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/normalize-ui-message-stream.ts +16 -16
- package/src/workflow-agent.ts +10 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/workflow",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "WorkflowAgent for building AI agents with AI SDK",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -26,20 +26,20 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"@ai-sdk/provider": "
|
|
31
|
-
"
|
|
32
|
-
"
|
|
29
|
+
"@ai-sdk/provider": "4.0.8",
|
|
30
|
+
"@ai-sdk/provider-utils": "5.0.30",
|
|
31
|
+
"ai": "7.0.79",
|
|
32
|
+
"ajv": "^8.20.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "22.19.19",
|
|
36
|
+
"@vercel/ai-tsconfig": "0.0.0",
|
|
36
37
|
"@workflow/vitest": "5.0.0-beta.42",
|
|
37
38
|
"tsup": "^8.5.1",
|
|
38
39
|
"typescript": "5.8.3",
|
|
39
40
|
"vitest": "4.1.6",
|
|
40
41
|
"workflow": "5.0.0-beta.42",
|
|
41
|
-
"zod": "4.4.3"
|
|
42
|
-
"@vercel/ai-tsconfig": "0.0.0"
|
|
42
|
+
"zod": "4.4.3"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"workflow": "^5.0.0-beta.42",
|
|
@@ -2,12 +2,12 @@ import type { UIMessageChunk } from 'ai';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Tracks, for one part family (text or reasoning), which part ids are open or
|
|
5
|
-
* ended
|
|
5
|
+
* have ended since the latest step boundary.
|
|
6
6
|
*/
|
|
7
7
|
interface PartFrameState {
|
|
8
|
-
/** A `*-start` was seen and not yet
|
|
8
|
+
/** A `*-start` was seen and has not yet received its explicit `*-end`. */
|
|
9
9
|
open: Set<string>;
|
|
10
|
-
/** A part that
|
|
10
|
+
/** A part that ended since the latest step boundary. */
|
|
11
11
|
ended: Set<string>;
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -18,7 +18,7 @@ const newPartFrameState = (): PartFrameState => ({
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Repairs the framing for a single `*-start` / `*-delta` / `*-end` chunk
|
|
21
|
-
* against the running
|
|
21
|
+
* against the running framing state, yielding the chunks the consumer should
|
|
22
22
|
* see. Text and reasoning parts share this logic (`startType` differentiates
|
|
23
23
|
* the synthesized start chunk).
|
|
24
24
|
*
|
|
@@ -32,7 +32,8 @@ function* repairPart(
|
|
|
32
32
|
startType: 'text-start' | 'reasoning-start',
|
|
33
33
|
): Generator<UIMessageChunk> {
|
|
34
34
|
if (kind === 'start') {
|
|
35
|
-
// Drop a duplicate/replayed start for a part
|
|
35
|
+
// Drop a duplicate/replayed start for a part that is still open or has
|
|
36
|
+
// already ended since the latest step boundary.
|
|
36
37
|
if (state.open.has(id) || state.ended.has(id)) {
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
@@ -70,10 +71,9 @@ function* repairPart(
|
|
|
70
71
|
* whole turn. Two properties of the durable streaming model make that error
|
|
71
72
|
* reachable:
|
|
72
73
|
*
|
|
73
|
-
* - A workflow run owns a single shared stream
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* `*-start` across a step boundary orphans the rest of that step's content.
|
|
74
|
+
* - A workflow run owns a single shared stream. Multi-step turns can reuse the
|
|
75
|
+
* same part id (commonly `"0"`), so a dropped or duplicated `*-start` can
|
|
76
|
+
* orphan the rest of that part's content.
|
|
77
77
|
* - The same stream is read across reconnects, and a stream-producing step can
|
|
78
78
|
* run more than once (retry/redelivery, or the concurrent-worker duplication
|
|
79
79
|
* tracked in vercel/workflow#2331 and #2039). Either can interleave or
|
|
@@ -86,11 +86,12 @@ function* repairPart(
|
|
|
86
86
|
*
|
|
87
87
|
* ## What it does
|
|
88
88
|
*
|
|
89
|
-
* Mirrors the consumer's part-lifetime state machine
|
|
90
|
-
* -
|
|
89
|
+
* Mirrors the consumer's explicit-end part-lifetime state machine per part type:
|
|
90
|
+
* - keeps open parts active across `finish-step`, while allowing ended ids to
|
|
91
|
+
* be reused by a later step;
|
|
91
92
|
* - synthesizes a missing `*-start` when an orphaned `*-delta`/`*-end` arrives;
|
|
92
93
|
* - drops a re-delivered `*-start`/`*-delta`/`*-end` for a part already
|
|
93
|
-
* open or ended
|
|
94
|
+
* open or ended since the latest step boundary (reconnect/replay overlap).
|
|
94
95
|
*
|
|
95
96
|
* A well-formed stream passes through unchanged.
|
|
96
97
|
*
|
|
@@ -126,11 +127,10 @@ export async function* normalizeUIMessageStreamParts(
|
|
|
126
127
|
break;
|
|
127
128
|
|
|
128
129
|
case 'finish-step':
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
|
|
130
|
+
// Open parts are closed only by explicit end chunks. A finish-step can
|
|
131
|
+
// come from another interleaved execution while a part is still open.
|
|
132
|
+
// Ended ids may be reused by the next step.
|
|
132
133
|
text.ended.clear();
|
|
133
|
-
reasoning.open.clear();
|
|
134
134
|
reasoning.ended.clear();
|
|
135
135
|
yield chunk;
|
|
136
136
|
break;
|
package/src/workflow-agent.ts
CHANGED
|
@@ -1562,12 +1562,12 @@ export class WorkflowAgent<
|
|
|
1562
1562
|
}
|
|
1563
1563
|
|
|
1564
1564
|
// Re-validate through the shared core implementation: input schema,
|
|
1565
|
-
// HMAC signature (when configured), and approval policy.
|
|
1566
|
-
// invalid input
|
|
1567
|
-
// agent loop can continue gracefully.
|
|
1565
|
+
// HMAC signature (when configured), and approval policy. Convert
|
|
1566
|
+
// invalid input, denial, or signature errors to a tool error result
|
|
1567
|
+
// so the agent loop can continue gracefully.
|
|
1568
1568
|
let revalidationReason: string | undefined;
|
|
1569
1569
|
try {
|
|
1570
|
-
const { deniedToolApprovals: policyDenied } =
|
|
1570
|
+
const { deniedToolApprovals: policyDenied, invalidToolApprovals } =
|
|
1571
1571
|
await validateApprovedToolApprovals({
|
|
1572
1572
|
approvedToolApprovals: [approval.collected],
|
|
1573
1573
|
tools: this.tools as ToolSet,
|
|
@@ -1577,7 +1577,12 @@ export class WorkflowAgent<
|
|
|
1577
1577
|
effectiveToolsContext as InferToolSetContext<ToolSet>,
|
|
1578
1578
|
runtimeContext: effectiveRuntimeContext,
|
|
1579
1579
|
});
|
|
1580
|
-
|
|
1580
|
+
|
|
1581
|
+
if (invalidToolApprovals.length > 0) {
|
|
1582
|
+
revalidationReason = getErrorMessage(
|
|
1583
|
+
invalidToolApprovals[0].error,
|
|
1584
|
+
);
|
|
1585
|
+
} else if (policyDenied.length > 0) {
|
|
1581
1586
|
revalidationReason =
|
|
1582
1587
|
policyDenied[0].approvalResponse.reason ??
|
|
1583
1588
|
'Tool approval denied';
|