convex-durable-agents 0.2.5 → 0.2.6

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 (37) hide show
  1. package/README.md +1 -0
  2. package/dist/client/handler.d.ts +2 -0
  3. package/dist/client/handler.d.ts.map +1 -1
  4. package/dist/client/handler.js +1 -0
  5. package/dist/client/handler.js.map +1 -1
  6. package/dist/client/streamer.d.ts +3 -1
  7. package/dist/client/streamer.d.ts.map +1 -1
  8. package/dist/client/streamer.js +9 -3
  9. package/dist/client/streamer.js.map +1 -1
  10. package/dist/react/test/happy-dom-setup.d.ts +2 -0
  11. package/dist/react/test/happy-dom-setup.d.ts.map +1 -0
  12. package/dist/react/test/happy-dom-setup.js +28 -0
  13. package/dist/react/test/happy-dom-setup.js.map +1 -0
  14. package/package.json +24 -21
  15. package/src/client/handler.ts +3 -0
  16. package/src/client/streamer.test.ts +187 -0
  17. package/src/client/streamer.ts +10 -3
  18. package/src/client/tools.test.ts +48 -0
  19. package/src/component/messages.test.ts +40 -0
  20. package/src/component/streams.test.ts +118 -0
  21. package/src/component/threads.test.ts +48 -0
  22. package/src/react/__fixtures__/01-early-streaming-start.json +35 -0
  23. package/src/react/__fixtures__/02-reasoning-complete-tool-call.json +85 -0
  24. package/src/react/__fixtures__/03-new-round-seq2.json +89 -0
  25. package/src/react/__fixtures__/04-tool-call-error-seq3.json +145 -0
  26. package/src/react/__fixtures__/05-later-round-seq5.json +117 -0
  27. package/src/react/__fixtures__/06-text-streaming-seq6.json +162 -0
  28. package/src/react/__fixtures__/07-text-streaming-more-seq6.json +212 -0
  29. package/src/react/__fixtures__/08-fully-committed-seq6.json +188 -0
  30. package/src/react/__snapshots__/apply-streaming-updates.test.ts.snap +1357 -0
  31. package/src/react/__snapshots__/use-thread-messages.test.tsx.snap +1429 -0
  32. package/src/react/agent-chat.test.tsx +155 -0
  33. package/src/react/apply-streaming-updates.test.ts +28 -0
  34. package/src/react/test/happy-dom-setup.ts +31 -0
  35. package/src/react/use-thread-messages.test.tsx +702 -0
  36. package/src/utils/msg.test.ts +34 -0
  37. package/src/utils/retry.test.ts +214 -0
@@ -0,0 +1,40 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { isToolInputAvailablePart } from "./messages";
3
+
4
+ describe("isToolInputAvailablePart", () => {
5
+ it("returns true for tool input available objects", () => {
6
+ expect(
7
+ isToolInputAvailablePart({
8
+ toolCallId: "tool-call-1",
9
+ state: "input-available",
10
+ }),
11
+ ).toBe(true);
12
+ });
13
+
14
+ it("returns false for non-object values", () => {
15
+ expect(isToolInputAvailablePart(null)).toBe(false);
16
+ expect(isToolInputAvailablePart("tool-call-1")).toBe(false);
17
+ expect(isToolInputAvailablePart(123)).toBe(false);
18
+ expect(isToolInputAvailablePart(true)).toBe(false);
19
+ });
20
+
21
+ it("returns false when required fields are missing or invalid", () => {
22
+ expect(
23
+ isToolInputAvailablePart({
24
+ state: "input-available",
25
+ }),
26
+ ).toBe(false);
27
+ expect(
28
+ isToolInputAvailablePart({
29
+ toolCallId: 123,
30
+ state: "input-available",
31
+ }),
32
+ ).toBe(false);
33
+ expect(
34
+ isToolInputAvailablePart({
35
+ toolCallId: "tool-call-1",
36
+ state: "output-available",
37
+ }),
38
+ ).toBe(false);
39
+ });
40
+ });
@@ -0,0 +1,118 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import type { UIMessageChunk } from "ai";
3
+ import { STREAM_HEARTBEAT_INTERVAL_MS, STREAM_LIVENESS_THRESHOLD_MS } from "../utils/streaming";
4
+ import { isAlive, replacePartIds } from "./streams";
5
+
6
+ describe("isAlive", () => {
7
+ it("uses a liveness threshold greater than the heartbeat interval", () => {
8
+ expect(STREAM_LIVENESS_THRESHOLD_MS).toBeGreaterThan(STREAM_HEARTBEAT_INTERVAL_MS);
9
+ });
10
+
11
+ it("returns true for a recent streaming heartbeat", () => {
12
+ const stream = {
13
+ state: {
14
+ kind: "streaming",
15
+ lockId: "lock-id",
16
+ lastHeartbeat: Date.now(),
17
+ },
18
+ } as Exclude<Parameters<typeof isAlive>[0], null>;
19
+ expect(isAlive(stream)).toBe(true);
20
+ });
21
+
22
+ it("returns false for an old streaming heartbeat", () => {
23
+ const stream = {
24
+ state: {
25
+ kind: "streaming",
26
+ lockId: "lock-id",
27
+ lastHeartbeat: Date.now() - STREAM_LIVENESS_THRESHOLD_MS - 1,
28
+ },
29
+ } as Exclude<Parameters<typeof isAlive>[0], null>;
30
+ expect(isAlive(stream)).toBe(false);
31
+ });
32
+ });
33
+
34
+ describe("replacePartIds", () => {
35
+ it("returns empty array for empty input", () => {
36
+ expect(replacePartIds([], new Map(), new Set())).toEqual([]);
37
+ });
38
+
39
+ it("assigns sequential ids starting from 0", () => {
40
+ const parts: UIMessageChunk[] = [
41
+ { type: "text-delta", id: "orig-a", delta: "Hello" },
42
+ { type: "text-delta", id: "orig-b", delta: " world" },
43
+ ];
44
+ const result = replacePartIds(parts, new Map(), new Set());
45
+ expect(result).toEqual([
46
+ { type: "text-delta", id: "0", delta: "Hello" },
47
+ { type: "text-delta", id: "1", delta: " world" },
48
+ ]);
49
+ });
50
+
51
+ it("reuses mapped id for parts with the same original id", () => {
52
+ const parts: UIMessageChunk[] = [
53
+ { type: "text-delta", id: "orig-a", delta: "Hello" },
54
+ { type: "text-delta", id: "orig-a", delta: " again" },
55
+ ];
56
+ const result = replacePartIds(parts, new Map(), new Set());
57
+ expect(result).toEqual([
58
+ { type: "text-delta", id: "0", delta: "Hello" },
59
+ { type: "text-delta", id: "0", delta: " again" },
60
+ ]);
61
+ });
62
+
63
+ it("uses pre-existing newIds mapping", () => {
64
+ const newIds = new Map([["orig-a", "42"]]);
65
+ const parts: UIMessageChunk[] = [{ type: "text-delta", id: "orig-a", delta: "Hello" }];
66
+ const result = replacePartIds(parts, newIds, new Set());
67
+ expect(result).toEqual([{ type: "text-delta", id: "42", delta: "Hello" }]);
68
+ });
69
+
70
+ it("skips ids that are already in prevIds", () => {
71
+ const prevIds = new Set(["0", "1"]);
72
+ const parts: UIMessageChunk[] = [{ type: "text-delta", id: "orig-a", delta: "Hello" }];
73
+ const result = replacePartIds(parts, new Map(), prevIds);
74
+ expect(result).toEqual([{ type: "text-delta", id: "2", delta: "Hello" }]);
75
+ });
76
+
77
+ it("adds generated ids to prevIds", () => {
78
+ const prevIds = new Set<string>();
79
+ const parts: UIMessageChunk[] = [{ type: "text-delta", id: "orig-a", delta: "Hello" }];
80
+ replacePartIds(parts, new Map(), prevIds);
81
+ expect(prevIds.has("0")).toBe(true);
82
+ });
83
+
84
+ it("records new mappings in newIds", () => {
85
+ const newIds = new Map<string, string>();
86
+ const parts: UIMessageChunk[] = [{ type: "text-delta", id: "orig-a", delta: "Hello" }];
87
+ replacePartIds(parts, newIds, new Set());
88
+ expect(newIds.get("orig-a")).toBe("0");
89
+ });
90
+
91
+ it("passes through parts without an id field unchanged", () => {
92
+ const parts: UIMessageChunk[] = [{ type: "tool-input-delta", toolCallId: "tc1", inputTextDelta: "some input" }];
93
+ const result = replacePartIds(parts, new Map(), new Set());
94
+ expect(result).toEqual([{ type: "tool-input-delta", toolCallId: "tc1", inputTextDelta: "some input" }]);
95
+ });
96
+
97
+ it("passes through parts with a falsy id unchanged", () => {
98
+ const parts: UIMessageChunk[] = [{ type: "text-delta", id: "", delta: "Hello" }];
99
+ const result = replacePartIds(parts, new Map(), new Set());
100
+ expect(result).toEqual([{ type: "text-delta", id: "", delta: "Hello" }]);
101
+ });
102
+
103
+ it("handles a mix of parts with and without ids", () => {
104
+ const parts: UIMessageChunk[] = [
105
+ { type: "text-delta", id: "a", delta: "Hello" },
106
+ { type: "tool-input-delta", toolCallId: "tc1", inputTextDelta: "input" },
107
+ { type: "reasoning-delta", id: "b", delta: "thinking" },
108
+ { type: "text-delta", id: "a", delta: " world" },
109
+ ];
110
+ const result = replacePartIds(parts, new Map(), new Set());
111
+ expect(result).toEqual([
112
+ { type: "text-delta", id: "0", delta: "Hello" },
113
+ { type: "tool-input-delta", toolCallId: "tc1", inputTextDelta: "input" },
114
+ { type: "reasoning-delta", id: "1", delta: "thinking" },
115
+ { type: "text-delta", id: "0", delta: " world" },
116
+ ]);
117
+ });
118
+ });
@@ -0,0 +1,48 @@
1
+ import { beforeEach, describe, expect, it } from "vitest";
2
+ import { resetFinalizerMismatchAlertState, trackFinalizerMismatchRate } from "./threads";
3
+
4
+ describe("trackFinalizerMismatchRate", () => {
5
+ beforeEach(() => {
6
+ resetFinalizerMismatchAlertState();
7
+ });
8
+
9
+ it("alerts when mismatch threshold is reached within the window", () => {
10
+ const now = 1_000_000;
11
+ expect(trackFinalizerMismatchRate("thread-a", now)).toEqual({
12
+ windowStartedAt: now,
13
+ count: 1,
14
+ shouldAlert: false,
15
+ });
16
+ expect(trackFinalizerMismatchRate("thread-a", now + 1_000)).toEqual({
17
+ windowStartedAt: now,
18
+ count: 2,
19
+ shouldAlert: false,
20
+ });
21
+ expect(trackFinalizerMismatchRate("thread-a", now + 2_000)).toEqual({
22
+ windowStartedAt: now,
23
+ count: 3,
24
+ shouldAlert: true,
25
+ });
26
+ expect(trackFinalizerMismatchRate("thread-a", now + 3_000)).toEqual({
27
+ windowStartedAt: now,
28
+ count: 4,
29
+ shouldAlert: false,
30
+ });
31
+ });
32
+
33
+ it("resets the counter outside the alert window and isolates by thread", () => {
34
+ const now = 2_000_000;
35
+ trackFinalizerMismatchRate("thread-a", now);
36
+ trackFinalizerMismatchRate("thread-a", now + 1_000);
37
+ expect(trackFinalizerMismatchRate("thread-b", now + 1_500)).toEqual({
38
+ windowStartedAt: now + 1_500,
39
+ count: 1,
40
+ shouldAlert: false,
41
+ });
42
+ expect(trackFinalizerMismatchRate("thread-a", now + 6 * 60 * 1_000)).toEqual({
43
+ windowStartedAt: now + 6 * 60 * 1_000,
44
+ count: 1,
45
+ shouldAlert: false,
46
+ });
47
+ });
48
+ });
@@ -0,0 +1,35 @@
1
+ {
2
+ "description": "01-early-streaming-start",
3
+ "logLine": 31988,
4
+ "messages": [
5
+ {
6
+ "id": "LT8PGDxTlpf0lVZs",
7
+ "role": "user",
8
+ "parts": [
9
+ {
10
+ "text": "get me a weather forecast for this weekend",
11
+ "type": "text"
12
+ }
13
+ ],
14
+ "metadata": {
15
+ "key": "jn7a2st357bqyzrcetv78j0fmx80yd8n-LT8PGDxTlpf0lVZs",
16
+ "status": "success",
17
+ "_creationTime": 1770783020905.086
18
+ }
19
+ }
20
+ ],
21
+ "streamingUpdates": {
22
+ "messages": [
23
+ {
24
+ "msgId": "vsOhda95lumMg4QZ",
25
+ "parts": [
26
+ {
27
+ "messageId": "vsOhda95lumMg4QZ",
28
+ "seq": 1,
29
+ "type": "start"
30
+ }
31
+ ]
32
+ }
33
+ ]
34
+ }
35
+ }
@@ -0,0 +1,85 @@
1
+ {
2
+ "description": "02-reasoning-complete-tool-call",
3
+ "logLine": 31998,
4
+ "messages": [
5
+ {
6
+ "id": "LT8PGDxTlpf0lVZs",
7
+ "role": "user",
8
+ "parts": [
9
+ {
10
+ "text": "get me a weather forecast for this weekend",
11
+ "type": "text"
12
+ }
13
+ ],
14
+ "metadata": {
15
+ "key": "jn7a2st357bqyzrcetv78j0fmx80yd8n-LT8PGDxTlpf0lVZs",
16
+ "status": "success",
17
+ "_creationTime": 1770783020905.086
18
+ }
19
+ }
20
+ ],
21
+ "streamingUpdates": {
22
+ "messages": [
23
+ {
24
+ "msgId": "vsOhda95lumMg4QZ",
25
+ "parts": [
26
+ {
27
+ "messageId": "vsOhda95lumMg4QZ",
28
+ "seq": 1,
29
+ "type": "start"
30
+ },
31
+ {
32
+ "seq": 1,
33
+ "type": "start-step"
34
+ },
35
+ {
36
+ "id": "0",
37
+ "seq": 1,
38
+ "type": "reasoning-start"
39
+ },
40
+ {
41
+ "delta": "The user wants a weather forecast for this weekend. I should use the Weather capability which provides \"",
42
+ "id": "0",
43
+ "seq": 1,
44
+ "type": "reasoning-delta"
45
+ },
46
+ {
47
+ "delta": "current conditions, hourly and multi-day forecasts for an",
48
+ "id": "0",
49
+ "seq": 1,
50
+ "type": "reasoning-delta"
51
+ },
52
+ {
53
+ "delta": "y location worldwide\". That should have what I need",
54
+ "id": "0",
55
+ "seq": 1,
56
+ "type": "reasoning-delta"
57
+ },
58
+ {
59
+ "delta": " for a weekend forecast.\n\nLet me read the Weather CAPABILITY.md to see how to query it",
60
+ "id": "0",
61
+ "seq": 1,
62
+ "type": "reasoning-delta"
63
+ },
64
+ {
65
+ "delta": ".",
66
+ "id": "0",
67
+ "seq": 1,
68
+ "type": "reasoning-delta"
69
+ },
70
+ {
71
+ "id": "0",
72
+ "seq": 1,
73
+ "type": "reasoning-end"
74
+ },
75
+ {
76
+ "seq": 1,
77
+ "toolCallId": "tooluse_4X05H1krEnm7rFppLQw1pi",
78
+ "toolName": "readFile",
79
+ "type": "tool-input-start"
80
+ }
81
+ ]
82
+ }
83
+ ]
84
+ }
85
+ }
@@ -0,0 +1,89 @@
1
+ {
2
+ "description": "03-new-round-seq2",
3
+ "logLine": 32006,
4
+ "messages": [
5
+ {
6
+ "id": "LT8PGDxTlpf0lVZs",
7
+ "role": "user",
8
+ "parts": [
9
+ {
10
+ "text": "get me a weather forecast for this weekend",
11
+ "type": "text"
12
+ }
13
+ ],
14
+ "metadata": {
15
+ "key": "jn7a2st357bqyzrcetv78j0fmx80yd8n-LT8PGDxTlpf0lVZs",
16
+ "status": "success",
17
+ "_creationTime": 1770783020905.086
18
+ }
19
+ },
20
+ {
21
+ "id": "vsOhda95lumMg4QZ",
22
+ "role": "assistant",
23
+ "parts": [
24
+ {
25
+ "type": "step-start"
26
+ },
27
+ {
28
+ "providerMetadata": {
29
+ "bedrock": {
30
+ "signature": "AxB2kT9pLmNcDfEgHiJkLmNoPqRsTuVwXyZ0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEF"
31
+ }
32
+ },
33
+ "state": "done",
34
+ "text": "The user wants a weather forecast for this weekend. I should use the Weather capability which provides \"current conditions, hourly and multi-day forecasts for any location worldwide\". That should have what I need for a weekend forecast.\n\nLet me read the Weather CAPABILITY.md to see how to query it.",
35
+ "type": "reasoning"
36
+ },
37
+ {
38
+ "input": {
39
+ "path": "capabilities/weather/CAPABILITY.md"
40
+ },
41
+ "output": "---\nname: Weather\ndescription: Weather provides current conditions, hourly and multi-day forecasts for any location worldwide\n---\n\n# Weather Service\n\nGlobal weather data provider with current observations and forecasts.\n\n## Data Overview\n\n- Current conditions: temperature, humidity, wind, visibility\n- Hourly forecasts: up to 48 hours ahead\n- Daily forecasts: up to 10 days ahead\n- Historical data: past 30 days\n\n## Examples\n\nGet a multi-day forecast:\n\n```typescript\nconst forecast = await getWeather(\"weatherprod\", {\n location: \"San Francisco, CA\",\n type: \"daily\",\n days: 7,\n});\nconsole.log(forecast);\n```\n\nGet current conditions:\n\n```typescript\nconst current = await getWeather(\"weatherprod\", {\n location: \"New York, NY\",\n type: \"current\",\n});\nconsole.log(current);\n```\n\n## Guidelines\n\n- Always specify a location string with city and state/country.\n- Use type \"daily\" for multi-day forecasts and \"current\" for present conditions.\n",
42
+ "state": "output-available",
43
+ "toolCallId": "tooluse_4X05H1krEnm7rFppLQw1pi",
44
+ "type": "tool-readFile"
45
+ }
46
+ ],
47
+ "metadata": {
48
+ "key": "jn7a2st357bqyzrcetv78j0fmx80yd8n-vsOhda95lumMg4QZ",
49
+ "status": "success",
50
+ "_creationTime": 1770783024158.487,
51
+ "committedSeq": 1
52
+ }
53
+ }
54
+ ],
55
+ "streamingUpdates": {
56
+ "messages": [
57
+ {
58
+ "msgId": "vsOhda95lumMg4QZ",
59
+ "parts": [
60
+ {
61
+ "messageId": "vsOhda95lumMg4QZ",
62
+ "seq": 2,
63
+ "type": "start"
64
+ },
65
+ {
66
+ "seq": 2,
67
+ "type": "start-step"
68
+ },
69
+ {
70
+ "id": "0",
71
+ "seq": 2,
72
+ "type": "text-start"
73
+ },
74
+ {
75
+ "delta": "Now let me fetch the weekend forecast for your area:",
76
+ "id": "0",
77
+ "seq": 2,
78
+ "type": "text-delta"
79
+ },
80
+ {
81
+ "id": "0",
82
+ "seq": 2,
83
+ "type": "text-end"
84
+ }
85
+ ]
86
+ }
87
+ ]
88
+ }
89
+ }
@@ -0,0 +1,145 @@
1
+ {
2
+ "description": "04-tool-call-error-seq3",
3
+ "logLine": 32020,
4
+ "messages": [
5
+ {
6
+ "id": "LT8PGDxTlpf0lVZs",
7
+ "role": "user",
8
+ "parts": [
9
+ {
10
+ "text": "get me a weather forecast for this weekend",
11
+ "type": "text"
12
+ }
13
+ ],
14
+ "metadata": {
15
+ "key": "jn7a2st357bqyzrcetv78j0fmx80yd8n-LT8PGDxTlpf0lVZs",
16
+ "status": "success",
17
+ "_creationTime": 1770783020905.086
18
+ }
19
+ },
20
+ {
21
+ "id": "vsOhda95lumMg4QZ",
22
+ "role": "assistant",
23
+ "parts": [
24
+ {
25
+ "type": "step-start"
26
+ },
27
+ {
28
+ "providerMetadata": {
29
+ "bedrock": {
30
+ "signature": "AxB2kT9pLmNcDfEgHiJkLmNoPqRsTuVwXyZ0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEFghijklmnopqrstuvwxyz0123456789abcdefABCDEF"
31
+ }
32
+ },
33
+ "state": "done",
34
+ "text": "The user wants a weather forecast for this weekend. I should use the Weather capability which provides \"current conditions, hourly and multi-day forecasts for any location worldwide\". That should have what I need for a weekend forecast.\n\nLet me read the Weather CAPABILITY.md to see how to query it.",
35
+ "type": "reasoning"
36
+ },
37
+ {
38
+ "input": {
39
+ "path": "capabilities/weather/CAPABILITY.md"
40
+ },
41
+ "output": "---\nname: Weather\ndescription: Weather provides current conditions, hourly and multi-day forecasts for any location worldwide\n---\n\n# Weather Service\n\nGlobal weather data provider with current observations and forecasts.\n\n## Data Overview\n\n- Current conditions: temperature, humidity, wind, visibility\n- Hourly forecasts: up to 48 hours ahead\n- Daily forecasts: up to 10 days ahead\n- Historical data: past 30 days\n\n## Examples\n\nGet a multi-day forecast:\n\n```typescript\nconst forecast = await getWeather(\"weatherprod\", {\n location: \"San Francisco, CA\",\n type: \"daily\",\n days: 7,\n});\nconsole.log(forecast);\n```\n\nGet current conditions:\n\n```typescript\nconst current = await getWeather(\"weatherprod\", {\n location: \"New York, NY\",\n type: \"current\",\n});\nconsole.log(current);\n```\n\n## Guidelines\n\n- Always specify a location string with city and state/country.\n- Use type \"daily\" for multi-day forecasts and \"current\" for present conditions.\n",
42
+ "state": "output-available",
43
+ "toolCallId": "tooluse_4X05H1krEnm7rFppLQw1pi",
44
+ "type": "tool-readFile"
45
+ },
46
+ {
47
+ "type": "step-start"
48
+ },
49
+ {
50
+ "state": "done",
51
+ "text": "Now let me fetch the weekend forecast for your area:",
52
+ "type": "text"
53
+ },
54
+ {
55
+ "errorText": "Code execution failed:\nWeather API request failed: Bad Request\nWeatherApiError: Weather API request failed: Bad Request\n at getWeather (/workspace/services/executor/.cache/revision-envs/k970rr9s6zxgkh8rg4fdbbhw3d80yzhs/bundle.mjs:2141:15)\n at async <anonymous> (script.ts:2:35)\n at async executeTypeScript (/workspace/services/executor/src/exec.ts:507:14)\n at processTicksAndRejections (native:7:39)\n{\"messages\":[{\"type\":\"FATAL\",\"text\":\"Invalid date range specified.\"}]}\n{\"headers\":{\"cache-control\":\"no-store, no-cache, must-revalidate, max-age=0\",\"content-length\":\"52\",\"content-type\":\"application/json; charset=UTF-8\",\"date\":\"Wed, 11 Feb 2026 04:10:27 GMT\"},\"status\":400}",
56
+ "input": {
57
+ "code": "\nconst forecast = await getWeather(\"weatherprod\", {\n location: \"San Francisco, CA\",\n type: \"daily\",\n startDate: \"2026-02-14\",\n endDate: \"2026-02-15\",\n});\nconsole.log(\"Weekend Forecast\");\nconsole.log(\"================\");\nconsole.log(JSON.stringify(forecast, null, 2));\n",
58
+ "description": "Get weekend weather forecast"
59
+ },
60
+ "state": "output-error",
61
+ "toolCallId": "tooluse_g3zTfupLSe8DI2Yla3gzG1",
62
+ "type": "tool-runCode"
63
+ },
64
+ {
65
+ "type": "step-start"
66
+ },
67
+ {
68
+ "type": "text",
69
+ "text": "Let me fix the query parameters:",
70
+ "state": "done"
71
+ },
72
+ {
73
+ "type": "tool-runCode",
74
+ "toolCallId": "tooluse_vGo3riHmTITFJfPvsE2y4k",
75
+ "state": "input-streaming"
76
+ }
77
+ ],
78
+ "metadata": {
79
+ "key": "jn7a2st357bqyzrcetv78j0fmx80yd8n-vsOhda95lumMg4QZ",
80
+ "status": "success",
81
+ "_creationTime": 1770783024158.487,
82
+ "committedSeq": 2
83
+ }
84
+ }
85
+ ],
86
+ "streamingUpdates": {
87
+ "messages": [
88
+ {
89
+ "msgId": "vsOhda95lumMg4QZ",
90
+ "parts": [
91
+ {
92
+ "messageId": "vsOhda95lumMg4QZ",
93
+ "seq": 3,
94
+ "type": "start"
95
+ },
96
+ {
97
+ "seq": 3,
98
+ "type": "start-step"
99
+ },
100
+ {
101
+ "id": "0",
102
+ "seq": 3,
103
+ "type": "text-start"
104
+ },
105
+ {
106
+ "delta": "Let me fix the query parameters:",
107
+ "id": "0",
108
+ "seq": 3,
109
+ "type": "text-delta"
110
+ },
111
+ {
112
+ "id": "0",
113
+ "seq": 3,
114
+ "type": "text-end"
115
+ },
116
+ {
117
+ "seq": 3,
118
+ "toolCallId": "tooluse_vGo3riHmTITFJfPvsE2y4k",
119
+ "toolName": "runCode",
120
+ "type": "tool-input-start"
121
+ },
122
+ {
123
+ "input": {
124
+ "code": "\nconst forecast = await getWeather(\"weatherprod\", {\n location: \"San Francisco, CA\",\n type: \"daily\",\n days: 3,\n});\nconsole.log(\"Weekend Weather Forecast\");\nconsole.log(\"========================\");\nforecast.days.forEach((day: any) => {\n console.log(`${day.date}: High ${day.high}°F / Low ${day.low}°F | ${day.condition} | Wind ${day.wind_mph} mph`);\n});\n",
125
+ "description": "Get weekend weather forecast"
126
+ },
127
+ "seq": 3,
128
+ "toolCallId": "tooluse_vGo3riHmTITFJfPvsE2y4k",
129
+ "toolName": "runCode",
130
+ "type": "tool-input-available"
131
+ },
132
+ {
133
+ "seq": 3,
134
+ "type": "finish-step"
135
+ },
136
+ {
137
+ "finishReason": "tool-calls",
138
+ "seq": 3,
139
+ "type": "finish"
140
+ }
141
+ ]
142
+ }
143
+ ]
144
+ }
145
+ }