@camox/api-contract 0.58.1 → 0.60.2
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/dist/drafts.d.ts +1 -13
- package/dist/drafts.js +1 -11
- package/dist/projects.d.ts +8 -2
- package/dist/projects.js +5 -2
- package/dist/router.d.ts +664 -384
- package/dist/threads.d.ts +143 -17
- package/dist/threads.js +175 -27
- package/package.json +1 -1
- package/src/drafts.ts +3 -18
- package/src/projects.ts +6 -2
- package/src/threads.ts +244 -35
package/src/threads.ts
CHANGED
|
@@ -10,7 +10,9 @@ import type { DraftOutcome } from "./drafts.ts";
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* The kind of the transcript entry recording an editor's message, with its sender and the
|
|
13
|
-
* operation id the studio sent it with as data (`editorMessageDataSchema`).
|
|
13
|
+
* operation id the studio sent it with as data (`editorMessageDataSchema`). Its first text part
|
|
14
|
+
* is what the editor wrote; a second one, which only the model reads, says where they were in the
|
|
15
|
+
* studio as they sent it (`editorTextOf`).
|
|
14
16
|
*/
|
|
15
17
|
export const EDITOR_MESSAGE_ENTRY = "camox.editor-message";
|
|
16
18
|
|
|
@@ -45,12 +47,57 @@ export const BRIEF_ENTRY = "camox.brief";
|
|
|
45
47
|
*/
|
|
46
48
|
export const DRAFT_ENDED_ENTRY = "camox.draft-ended";
|
|
47
49
|
|
|
50
|
+
/**
|
|
51
|
+
* The kind of the transcript entry recording a member's push to the draft's code from their own
|
|
52
|
+
* checkout (ADR 0026), with a `pushDataSchema` as data. The model reads it as context, never
|
|
53
|
+
* answering it, and editors see who pushed, their commits, and whether they built.
|
|
54
|
+
*/
|
|
55
|
+
export const PUSH_ENTRY = "camox.push";
|
|
56
|
+
|
|
57
|
+
/** How a pushed head's build ended: built, built with type errors, or built but not previewed. */
|
|
58
|
+
export const pushOutcomeSchema = z.enum(["built", "typeErrors", "notPreviewed"]);
|
|
59
|
+
export type PushOutcome = z.infer<typeof pushOutcomeSchema>;
|
|
60
|
+
|
|
48
61
|
/**
|
|
49
62
|
* The agent's tool on Live handing a request off to a draft it starts (ADR 0025). A run whose
|
|
50
63
|
* call of it went through ends on the draft's card.
|
|
51
64
|
*/
|
|
52
65
|
export const START_DRAFT_TOOL = "startDraft";
|
|
53
66
|
|
|
67
|
+
/** The agent's tool building its code changes mid-turn, which the end of a turn also does. */
|
|
68
|
+
export const BUILD_TOOL = "build";
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The tools whose calls change a draft: the content tools that write, and the code tools that
|
|
72
|
+
* write files. The model calls the code tools, and code mode scripts call both. A test in the API
|
|
73
|
+
* keeps this in step with the tools themselves.
|
|
74
|
+
*/
|
|
75
|
+
export const CHANGING_TOOLS: ReadonlySet<string> = new Set([
|
|
76
|
+
// Content
|
|
77
|
+
"addBackToLocale",
|
|
78
|
+
"addLocale",
|
|
79
|
+
"createBlock",
|
|
80
|
+
"createCollectionRecord",
|
|
81
|
+
"createPage",
|
|
82
|
+
"deleteBlock",
|
|
83
|
+
"deleteCollectionRecord",
|
|
84
|
+
"deletePage",
|
|
85
|
+
"editBlock",
|
|
86
|
+
"editCollectionRecord",
|
|
87
|
+
"moveBlock",
|
|
88
|
+
"removeLocale",
|
|
89
|
+
"setPageLayout",
|
|
90
|
+
"setPageSeo",
|
|
91
|
+
"updateFile",
|
|
92
|
+
"updatePage",
|
|
93
|
+
"uploadFile",
|
|
94
|
+
// Code
|
|
95
|
+
"edit",
|
|
96
|
+
"mv",
|
|
97
|
+
"rm",
|
|
98
|
+
"write",
|
|
99
|
+
]);
|
|
100
|
+
|
|
54
101
|
/** The draft a `startDraft` call started, as its result's details name it for the card. */
|
|
55
102
|
export const startedDraftCardSchema = z.object({ draftId: z.string(), name: z.string() });
|
|
56
103
|
export type StartedDraftCard = z.infer<typeof startedDraftCardSchema>;
|
|
@@ -85,6 +132,14 @@ export type ThreadSender = z.infer<typeof threadSenderSchema>;
|
|
|
85
132
|
export const isSender = (data: unknown): data is ThreadSender =>
|
|
86
133
|
threadSenderSchema.safeParse(data).success;
|
|
87
134
|
|
|
135
|
+
/** What a push's entry records: who pushed, their commits' subjects, oldest first, and the build. */
|
|
136
|
+
export const pushDataSchema = z.object({
|
|
137
|
+
pusher: threadSenderSchema,
|
|
138
|
+
subjects: z.array(z.string()),
|
|
139
|
+
outcome: pushOutcomeSchema,
|
|
140
|
+
});
|
|
141
|
+
export type PushData = z.infer<typeof pushDataSchema>;
|
|
142
|
+
|
|
88
143
|
/**
|
|
89
144
|
* What an editor's message entry records: who sent it, the operation id it was sent with, and for
|
|
90
145
|
* a message sent on Live as the agent handed the request off, the draft it went to with it.
|
|
@@ -166,23 +221,39 @@ export type ThreadMessage =
|
|
|
166
221
|
* the draft's card, with its current name, which opens it. Once the draft ended, it has its
|
|
167
222
|
* last name and how it ended, and opens nothing. The turn is finished.
|
|
168
223
|
*/
|
|
169
|
-
| ({ id: string; from: "draft"; ended?: DraftOutcome } & StartedDraftCard)
|
|
224
|
+
| ({ id: string; from: "draft"; ended?: DraftOutcome } & StartedDraftCard)
|
|
225
|
+
/**
|
|
226
|
+
* A member's push to the draft's code from their own checkout: who pushed, their commits'
|
|
227
|
+
* subjects, oldest first, and how the pushed head built. It's no turn: the agent doesn't
|
|
228
|
+
* answer it.
|
|
229
|
+
*/
|
|
230
|
+
| { id: string; from: "push"; sender: ThreadSender; subjects: string[]; outcome: PushOutcome };
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* What the agent is doing during a run, as editors see it, without the details of how it works:
|
|
234
|
+
* - `thinking`: the model is called, and hasn't started writing a tool call.
|
|
235
|
+
* - `exploring`: it uses tools that only read, from writing a call until the model is called again.
|
|
236
|
+
* - `changing`: a call of that round changes the draft (`CHANGING_TOOLS`), as soon as it's written
|
|
237
|
+
* or, in a code mode script, as soon as the script calls one.
|
|
238
|
+
* - `building`: its `build` call runs, or the end of the turn builds its code changes.
|
|
239
|
+
* - `fixing`: the end of the turn's build failed, and the agent fixes it.
|
|
240
|
+
* - `startingDraft`: on Live, its `startDraft` call runs.
|
|
241
|
+
*/
|
|
242
|
+
export type AgentActivity =
|
|
243
|
+
| "thinking"
|
|
244
|
+
| "exploring"
|
|
245
|
+
| "changing"
|
|
246
|
+
| "building"
|
|
247
|
+
| "fixing"
|
|
248
|
+
| "startingDraft";
|
|
170
249
|
|
|
171
250
|
/** Everything the studio shows for a thread. */
|
|
172
251
|
export type ThreadView = {
|
|
173
252
|
messages: readonly ThreadMessage[];
|
|
174
253
|
/** Whether a run is going: the agent is working on a turn, and stopping would end it. */
|
|
175
254
|
running: boolean;
|
|
176
|
-
/**
|
|
177
|
-
|
|
178
|
-
* turn is building it, or it failed and the agent is fixing it.
|
|
179
|
-
*/
|
|
180
|
-
status?: BuildStatus;
|
|
181
|
-
/**
|
|
182
|
-
* While a run is going, whether the agent is using its tools, code mode included, rather than
|
|
183
|
-
* thinking: from writing a call until the model is called again.
|
|
184
|
-
*/
|
|
185
|
-
usingTools?: true;
|
|
255
|
+
/** While a run is going, what the agent is doing. */
|
|
256
|
+
activity?: AgentActivity;
|
|
186
257
|
};
|
|
187
258
|
|
|
188
259
|
export const EMPTY_THREAD_VIEW: ThreadView = { messages: [], running: false };
|
|
@@ -195,10 +266,15 @@ export const EMPTY_THREAD_VIEW: ThreadView = { messages: [], running: false };
|
|
|
195
266
|
export type ThreadState = {
|
|
196
267
|
steps: readonly ThreadStep[];
|
|
197
268
|
running: boolean;
|
|
198
|
-
/** The model's calls still running
|
|
199
|
-
toolCalls?: readonly string[];
|
|
269
|
+
/** The model's calls still running. A tool's own calls aren't the agent's. */
|
|
270
|
+
toolCalls?: readonly { callId: string; name: string }[];
|
|
200
271
|
/** Whether the model's latest message makes tool calls: it writes them, then they run. */
|
|
201
272
|
callingTools?: boolean;
|
|
273
|
+
/**
|
|
274
|
+
* Whether a call of the latest round changes the draft, the model's or a script's: from writing
|
|
275
|
+
* or making it until the model is called again.
|
|
276
|
+
*/
|
|
277
|
+
changing?: boolean;
|
|
202
278
|
/** The drafts the thread's cards name, as the thread last looked them up. */
|
|
203
279
|
drafts?: DraftCardStates;
|
|
204
280
|
};
|
|
@@ -226,6 +302,8 @@ type ThreadStep = { id: string } & (
|
|
|
226
302
|
| ({ type: "draft" } & StartedDraftCard)
|
|
227
303
|
/** Where the build of the turn's code stands, as the turn ends. */
|
|
228
304
|
| { type: "build"; status: BuildStatus }
|
|
305
|
+
/** A member's push, between turns. */
|
|
306
|
+
| ({ type: "push" } & PushData)
|
|
229
307
|
/**
|
|
230
308
|
* The end of the turn sent the agent back to work, after its answer: the answer wasn't the
|
|
231
309
|
* turn's last, and what the agent was told is never shown.
|
|
@@ -233,13 +311,54 @@ type ThreadStep = { id: string } & (
|
|
|
233
311
|
| { type: "continued" }
|
|
234
312
|
);
|
|
235
313
|
|
|
314
|
+
/**
|
|
315
|
+
* What an editor had selected on the page as they sent a message, unless they cleared it from
|
|
316
|
+
* the message: its path as the studio names it, from the block down to the selection, with the
|
|
317
|
+
* ids the agent's tools take. A record is the one the selection shows, a field the selection's
|
|
318
|
+
* own, of the record when a record is selected.
|
|
319
|
+
*/
|
|
320
|
+
export const editorSelectionSchema = z.object({
|
|
321
|
+
path: z.array(z.string().max(512)).min(1).max(16),
|
|
322
|
+
blockId: z.string().max(64).optional(),
|
|
323
|
+
itemId: z.string().max(64).optional(),
|
|
324
|
+
recordId: z.string().max(64).optional(),
|
|
325
|
+
fieldName: z.string().max(256).optional(),
|
|
326
|
+
});
|
|
327
|
+
export type EditorSelection = z.infer<typeof editorSelectionSchema>;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Where an editor was in the studio as they sent a message, in the Locale they viewed: a Page,
|
|
331
|
+
* by its path after the Locale's prefix and its title, with its id for a curated page and its
|
|
332
|
+
* Layout's for a layout page, and what they had selected on it, or the Canvas with no page
|
|
333
|
+
* selected. A hint for the agent, never what the message is about.
|
|
334
|
+
*/
|
|
335
|
+
export const editorWhereSchema = z.discriminatedUnion("on", [
|
|
336
|
+
z.object({
|
|
337
|
+
on: z.literal("page"),
|
|
338
|
+
locale: z.string().max(64),
|
|
339
|
+
path: z.string().max(2048),
|
|
340
|
+
title: z.string().max(512),
|
|
341
|
+
pageId: z.string().max(64).optional(),
|
|
342
|
+
layoutId: z.string().max(256).optional(),
|
|
343
|
+
selection: editorSelectionSchema.optional(),
|
|
344
|
+
}),
|
|
345
|
+
z.object({ on: z.literal("canvas"), locale: z.string().max(64) }),
|
|
346
|
+
]);
|
|
347
|
+
export type EditorWhere = z.infer<typeof editorWhereSchema>;
|
|
348
|
+
|
|
236
349
|
/** Studio → thread. */
|
|
237
350
|
export const threadClientMessageSchema = z.discriminatedUnion("type", [
|
|
238
351
|
/**
|
|
239
|
-
* An editor's message. The operation id is generated by
|
|
240
|
-
* a message resent after a dropped connection is one
|
|
352
|
+
* An editor's message, and where they were as they sent it. The operation id is generated by
|
|
353
|
+
* the studio and kept across resends, so a message resent after a dropped connection is one
|
|
354
|
+
* submission.
|
|
241
355
|
*/
|
|
242
|
-
z.object({
|
|
356
|
+
z.object({
|
|
357
|
+
type: z.literal("send"),
|
|
358
|
+
operationId: z.string().min(1),
|
|
359
|
+
text: z.string().min(1),
|
|
360
|
+
where: editorWhereSchema.optional(),
|
|
361
|
+
}),
|
|
243
362
|
/** Stops the run going, if any. What the agent changed before stays. */
|
|
244
363
|
z.object({ type: z.literal("stop") }),
|
|
245
364
|
/** Asks for a fresh snapshot of the thread. */
|
|
@@ -275,8 +394,7 @@ export type ThreadServerMessage =
|
|
|
275
394
|
|
|
276
395
|
/**
|
|
277
396
|
* The code a thread closes its sockets with once it ended with its draft, and for good: it's
|
|
278
|
-
* deleted, so reconnecting reaches nothing.
|
|
279
|
-
* thread the next time the studio connects.
|
|
397
|
+
* deleted, so reconnecting reaches nothing.
|
|
280
398
|
*/
|
|
281
399
|
export const THREAD_ENDED_CLOSE_CODE = 4000;
|
|
282
400
|
|
|
@@ -289,6 +407,15 @@ export const textOf = (content: Message["content"]) =>
|
|
|
289
407
|
.join("")
|
|
290
408
|
.trim();
|
|
291
409
|
|
|
410
|
+
/**
|
|
411
|
+
* What an editor wrote, of their message entry's content: its first text part. The rest, where
|
|
412
|
+
* they were in the studio, only the model reads.
|
|
413
|
+
*/
|
|
414
|
+
export const editorTextOf = (content: Message["content"]) =>
|
|
415
|
+
typeof content === "string"
|
|
416
|
+
? content
|
|
417
|
+
: (content.find((part) => part.type === "text")?.text ?? "").trim();
|
|
418
|
+
|
|
292
419
|
/** What an assistant message is to the thread: a final answer, a tool round, or neither. */
|
|
293
420
|
function assistantStep(id: string, message: AssistantMessage): ThreadStep {
|
|
294
421
|
if (message.content.some((part) => part.type === "toolCall")) return { id, type: "tools" };
|
|
@@ -315,6 +442,10 @@ function threadStepOf(entry: EntryRecord): ThreadStep | undefined {
|
|
|
315
442
|
}
|
|
316
443
|
// Only the model reads it: as a user message, it would otherwise read as Pi continuing a run.
|
|
317
444
|
if (entry.kind === DRAFT_ENDED_ENTRY) return undefined;
|
|
445
|
+
if (entry.kind === PUSH_ENTRY) {
|
|
446
|
+
const push = pushDataSchema.safeParse(entry.data).data;
|
|
447
|
+
return push && { id, type: "push", ...push };
|
|
448
|
+
}
|
|
318
449
|
const message = entry.model?.find((candidate) => candidate.role !== "system");
|
|
319
450
|
if (!message) return undefined;
|
|
320
451
|
if (message.role === "user") {
|
|
@@ -325,7 +456,7 @@ function threadStepOf(entry: EntryRecord): ThreadStep | undefined {
|
|
|
325
456
|
id,
|
|
326
457
|
type: "editor",
|
|
327
458
|
sender,
|
|
328
|
-
text:
|
|
459
|
+
text: editorTextOf(message.content),
|
|
329
460
|
...(handedOffTo && { handedOffTo }),
|
|
330
461
|
}
|
|
331
462
|
);
|
|
@@ -364,6 +495,7 @@ export function threadViewOf({
|
|
|
364
495
|
running,
|
|
365
496
|
toolCalls = [],
|
|
366
497
|
callingTools = false,
|
|
498
|
+
changing = false,
|
|
367
499
|
drafts = {},
|
|
368
500
|
}: ThreadState): ThreadView {
|
|
369
501
|
const messages: ThreadMessage[] = [];
|
|
@@ -462,6 +594,16 @@ export function threadViewOf({
|
|
|
462
594
|
case "build":
|
|
463
595
|
status = step.status;
|
|
464
596
|
break;
|
|
597
|
+
case "push":
|
|
598
|
+
// Pushes land between turns: one doesn't end a turn, nor start one.
|
|
599
|
+
messages.push({
|
|
600
|
+
id: step.id,
|
|
601
|
+
from: "push",
|
|
602
|
+
sender: step.pusher,
|
|
603
|
+
subjects: step.subjects,
|
|
604
|
+
outcome: step.outcome,
|
|
605
|
+
});
|
|
606
|
+
break;
|
|
465
607
|
case "tools":
|
|
466
608
|
unfinished = false;
|
|
467
609
|
break;
|
|
@@ -471,15 +613,44 @@ export function threadViewOf({
|
|
|
471
613
|
}
|
|
472
614
|
}
|
|
473
615
|
if (!running) fail();
|
|
474
|
-
|
|
616
|
+
if (!running) return { messages, running };
|
|
475
617
|
return {
|
|
476
618
|
messages,
|
|
477
619
|
running,
|
|
478
|
-
|
|
479
|
-
...(usingTools && { usingTools }),
|
|
620
|
+
activity: activityOf({ status, toolCalls, callingTools, changing }),
|
|
480
621
|
};
|
|
481
622
|
}
|
|
482
623
|
|
|
624
|
+
/** What the agent is doing during a run, from where the turn's build stands and its tool calls. */
|
|
625
|
+
function activityOf({
|
|
626
|
+
status,
|
|
627
|
+
toolCalls,
|
|
628
|
+
callingTools,
|
|
629
|
+
changing,
|
|
630
|
+
}: {
|
|
631
|
+
status: BuildStatus | undefined;
|
|
632
|
+
toolCalls: NonNullable<ThreadState["toolCalls"]>;
|
|
633
|
+
callingTools: boolean;
|
|
634
|
+
changing: boolean;
|
|
635
|
+
}): AgentActivity {
|
|
636
|
+
const calling = (name: string) => toolCalls.some((call) => call.name === name);
|
|
637
|
+
// Fixing a build error takes thinking, reading and writing, which all read as fixing, but its
|
|
638
|
+
// builds show as building.
|
|
639
|
+
if (status === "building" || calling(BUILD_TOOL)) return "building";
|
|
640
|
+
if (status === "fixing") return "fixing";
|
|
641
|
+
if (calling(START_DRAFT_TOOL)) return "startingDraft";
|
|
642
|
+
if (changing) return "changing";
|
|
643
|
+
if (callingTools || toolCalls.length > 0) return "exploring";
|
|
644
|
+
return "thinking";
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/** Whether a tool call changes the draft. */
|
|
648
|
+
const changes = (name: string) => CHANGING_TOOLS.has(name);
|
|
649
|
+
|
|
650
|
+
/** Whether a block of the model's message is a call of a tool that changes the draft. */
|
|
651
|
+
const callsChangingTool = (block: AssistantMessage["content"][number]) =>
|
|
652
|
+
block.type === "toolCall" && changes(block.name);
|
|
653
|
+
|
|
483
654
|
const append = (state: ThreadState, entry: EntryRecord | undefined): ThreadState => {
|
|
484
655
|
const step = entry && threadStepOf(entry);
|
|
485
656
|
if (!step || state.steps.some((known) => known.id === step.id)) return state;
|
|
@@ -489,33 +660,71 @@ const append = (state: ThreadState, entry: EntryRecord | undefined): ThreadState
|
|
|
489
660
|
/** Folds one of Pi's agent events into the thread's state. */
|
|
490
661
|
export function reduceThreadState(state: ThreadState, event: AgentEvent): ThreadState {
|
|
491
662
|
switch (event.type) {
|
|
492
|
-
case "snapshot":
|
|
663
|
+
case "snapshot": {
|
|
664
|
+
const toolCalls = event.tools.flatMap((tool) =>
|
|
665
|
+
tool.status === "done" ? [] : [{ callId: tool.callId, name: tool.name }],
|
|
666
|
+
);
|
|
667
|
+
const generated = event.generation?.message?.content ?? [];
|
|
493
668
|
return {
|
|
494
669
|
...state,
|
|
495
670
|
steps: event.entries.flatMap((entry) => threadStepOf(entry) ?? []),
|
|
496
671
|
running: event.run !== undefined,
|
|
497
|
-
toolCalls
|
|
498
|
-
callingTools:
|
|
672
|
+
toolCalls,
|
|
673
|
+
callingTools: generated.some((part) => part.type === "toolCall"),
|
|
674
|
+
// A script's calls count while the script runs, finished or not.
|
|
675
|
+
changing:
|
|
676
|
+
generated.some(callsChangingTool) ||
|
|
677
|
+
toolCalls.some((call) => changes(call.name)) ||
|
|
678
|
+
event.nestedTools.some(
|
|
679
|
+
(nested) =>
|
|
680
|
+
changes(nested.name) &&
|
|
681
|
+
toolCalls.some(
|
|
682
|
+
(call) =>
|
|
683
|
+
nested.parentCallId === call.callId ||
|
|
684
|
+
nested.parentCallId.startsWith(`${call.callId}/`),
|
|
685
|
+
),
|
|
686
|
+
),
|
|
499
687
|
};
|
|
688
|
+
}
|
|
500
689
|
case "run_start":
|
|
501
690
|
return { ...state, running: true };
|
|
502
691
|
case "run_end":
|
|
503
|
-
return { ...state, running: false, toolCalls: [], callingTools: false };
|
|
692
|
+
return { ...state, running: false, toolCalls: [], callingTools: false, changing: false };
|
|
504
693
|
case "message_start":
|
|
505
694
|
// The model is called again: it thinks until it writes a tool call.
|
|
506
|
-
return event.message.role === "assistant"
|
|
507
|
-
|
|
508
|
-
return event.changes.some((change) => change.type === "toolcall_start")
|
|
509
|
-
? { ...state, callingTools: true }
|
|
695
|
+
return event.message.role === "assistant"
|
|
696
|
+
? { ...state, callingTools: false, changing: false }
|
|
510
697
|
: state;
|
|
511
|
-
case "
|
|
512
|
-
|
|
513
|
-
|
|
698
|
+
case "message_update": {
|
|
699
|
+
// A call's name may come with its start, a later change to its block, or the whole message.
|
|
700
|
+
const calls = event.changes.flatMap((change) =>
|
|
701
|
+
change.type === "toolcall_start" || change.type === "block"
|
|
702
|
+
? [change.block]
|
|
703
|
+
: change.type === "message"
|
|
704
|
+
? change.message.content
|
|
705
|
+
: [],
|
|
706
|
+
);
|
|
707
|
+
if (calls.length === 0) return state;
|
|
708
|
+
return {
|
|
709
|
+
...state,
|
|
710
|
+
callingTools: state.callingTools || calls.some((block) => block.type === "toolCall"),
|
|
711
|
+
changing: state.changing || calls.some(callsChangingTool),
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
case "tool_execution_start": {
|
|
715
|
+
const changing = state.changing || changes(event.toolName);
|
|
716
|
+
if (event.parentToolCallId !== undefined) return { ...state, changing };
|
|
717
|
+
return {
|
|
718
|
+
...state,
|
|
719
|
+
changing,
|
|
720
|
+
toolCalls: [...(state.toolCalls ?? []), { callId: event.toolCallId, name: event.toolName }],
|
|
721
|
+
};
|
|
722
|
+
}
|
|
514
723
|
case "tool_execution_end":
|
|
515
724
|
return append(
|
|
516
725
|
{
|
|
517
726
|
...state,
|
|
518
|
-
toolCalls: state.toolCalls?.filter((
|
|
727
|
+
toolCalls: state.toolCalls?.filter((call) => call.callId !== event.toolCallId),
|
|
519
728
|
},
|
|
520
729
|
event.entry,
|
|
521
730
|
);
|