@camox/api-contract 0.62.0 → 0.65.1
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/analytics.d.ts +1 -1
- package/dist/drafts.d.ts +50 -2
- package/dist/drafts.js +57 -3
- package/dist/layout-routes.d.ts +20 -0
- package/dist/layout-routes.js +43 -0
- package/dist/locales.d.ts +2 -0
- package/dist/locales.js +7 -1
- package/dist/patrol.d.ts +116 -8
- package/dist/patrol.js +33 -1
- package/dist/router.d.ts +7166 -3317
- package/dist/threads.d.ts +69 -12
- package/dist/threads.js +70 -8
- package/package.json +8 -2
- package/src/drafts.ts +67 -2
- package/src/layout-routes.ts +51 -0
- package/src/locales.ts +7 -0
- package/src/patrol.ts +129 -9
- package/src/threads.ts +95 -4
package/src/patrol.ts
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
* Live, and names each report's subject, so the studio needs no entity loaded to show it.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
export
|
|
7
|
+
/** Every patroller's stable id. The registry in the API lists them all. */
|
|
8
|
+
export const PATROLLER_IDS = ["translator", "seoManager", "linkChecker", "proofreader"] as const;
|
|
9
|
+
|
|
10
|
+
/** A patroller's stable id. */
|
|
11
|
+
export type PatrollerId = (typeof PATROLLER_IDS)[number];
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* What a structured report says was done. Each has a failed form, by the report's outcome. A
|
|
@@ -16,7 +19,10 @@ export type ReportVerb =
|
|
|
16
19
|
| "mergedWithLive"
|
|
17
20
|
| "wroteSeo"
|
|
18
21
|
| "describedFile"
|
|
19
|
-
| "publishedCatchUp"
|
|
22
|
+
| "publishedCatchUp"
|
|
23
|
+
| "removedLink"
|
|
24
|
+
| "fixedLink"
|
|
25
|
+
| "fixedMistake";
|
|
20
26
|
|
|
21
27
|
/** What a report can be about. */
|
|
22
28
|
export type ReportEntity =
|
|
@@ -51,8 +57,11 @@ export const reportLocale = (
|
|
|
51
57
|
/** Whether the work was done, or failed. A failure shows in its report only. */
|
|
52
58
|
export type ReportOutcome = "done" | "failed";
|
|
53
59
|
|
|
54
|
-
/**
|
|
55
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Where a patroller is on duty, in drafts or on Live: it works as you go, checks when you
|
|
62
|
+
* publish, or is off there (ADR 0034).
|
|
63
|
+
*/
|
|
64
|
+
export type PatrollerDuty = "asYouGo" | "atPublish" | "off";
|
|
56
65
|
|
|
57
66
|
/**
|
|
58
67
|
* Where the studio shows a report's subject: a page on the Canvas, with the block and repeatable
|
|
@@ -68,7 +77,6 @@ export type ReportView = {
|
|
|
68
77
|
id: string;
|
|
69
78
|
summary: ReportSummary;
|
|
70
79
|
outcome: ReportOutcome;
|
|
71
|
-
attention: AttentionLevel;
|
|
72
80
|
/** When the work last settled, in milliseconds since the epoch. */
|
|
73
81
|
at: number;
|
|
74
82
|
/** Whether the report is Live's, seen from a draft or from Live. */
|
|
@@ -80,6 +88,98 @@ export type ReportView = {
|
|
|
80
88
|
place: ReportPlace | null;
|
|
81
89
|
};
|
|
82
90
|
|
|
91
|
+
/**
|
|
92
|
+
* What a finding is about (ADR 0034), by its kind:
|
|
93
|
+
* - `missingPage`: a link to a page that doesn't exist, the page's name as it was last known.
|
|
94
|
+
* - `brokenLink`: an external link that failed, and how.
|
|
95
|
+
* - `misspelling`, `grammar`: the flagged text, in the sentence it's in.
|
|
96
|
+
* A link's `url` is as content stores it: a page link's is `camox:page:<id>`, and `text` is what
|
|
97
|
+
* the link reads.
|
|
98
|
+
*/
|
|
99
|
+
export type FindingAbout =
|
|
100
|
+
| { kind: "missingPage"; url: string; text: string; page: string | null }
|
|
101
|
+
| { kind: "brokenLink"; url: string; text: string; failure: LinkFailure }
|
|
102
|
+
| { kind: "misspelling" | "grammar"; text: string; sentence: string };
|
|
103
|
+
|
|
104
|
+
/** How a link failed: an HTTP error status, no connection (DNS included), or no answer in time. */
|
|
105
|
+
export type LinkFailure =
|
|
106
|
+
| { kind: "status"; status: number }
|
|
107
|
+
| { kind: "unreachable" }
|
|
108
|
+
| { kind: "timeout" };
|
|
109
|
+
|
|
110
|
+
export type FindingKind = FindingAbout["kind"];
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The fix a finding suggests, which applying makes: removing the link, keeping its text, or
|
|
114
|
+
* replacing the flagged text with `replacement` in its sentence, keeping its formatting.
|
|
115
|
+
*/
|
|
116
|
+
export type FindingFix = { kind: "removeLink" } | { kind: "replaceText"; replacement: string };
|
|
117
|
+
|
|
118
|
+
/** A finding, open, in the scope viewed. */
|
|
119
|
+
export type FindingView = {
|
|
120
|
+
/** What applying or dismissing it names. */
|
|
121
|
+
id: number;
|
|
122
|
+
about: FindingAbout;
|
|
123
|
+
/** The fix it suggests, if any. */
|
|
124
|
+
fix: FindingFix | null;
|
|
125
|
+
/** The content it's in, named in its locale. */
|
|
126
|
+
subject: ReportSubject;
|
|
127
|
+
/** The locale of the content it's in, by its tag. */
|
|
128
|
+
locale: string;
|
|
129
|
+
/** The content field it's in, by name. */
|
|
130
|
+
field: string;
|
|
131
|
+
/** When it was first raised, in milliseconds since the epoch. */
|
|
132
|
+
raisedAt: number;
|
|
133
|
+
/** Whether it's Live's. Live's are fixed in the Patrol Draft. */
|
|
134
|
+
live: boolean;
|
|
135
|
+
/** Where its content shows, as for reports. */
|
|
136
|
+
place: ReportPlace | null;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/** What a finding is about, after where it is: "links to About, a page that no longer exists". */
|
|
140
|
+
function aboutSentence(about: FindingAbout): string {
|
|
141
|
+
switch (about.kind) {
|
|
142
|
+
case "missingPage":
|
|
143
|
+
return about.page === null
|
|
144
|
+
? "links to a page that no longer exists"
|
|
145
|
+
: `links to ${about.page}, a page that no longer exists`;
|
|
146
|
+
case "brokenLink": {
|
|
147
|
+
const { failure } = about;
|
|
148
|
+
const how =
|
|
149
|
+
failure.kind === "status"
|
|
150
|
+
? `answers with an error (${failure.status})`
|
|
151
|
+
: failure.kind === "timeout"
|
|
152
|
+
? "doesn't answer in time"
|
|
153
|
+
: "can't be reached";
|
|
154
|
+
return `links to ${about.url}, which ${how}`;
|
|
155
|
+
}
|
|
156
|
+
case "misspelling":
|
|
157
|
+
return "may be misspelled";
|
|
158
|
+
case "grammar":
|
|
159
|
+
return "may be a grammar mistake";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* A finding as one sentence, as the studio, emails, Briefs and refusals word it: "“About us” in
|
|
165
|
+
* Footer links to About, a page that no longer exists".
|
|
166
|
+
*/
|
|
167
|
+
export function findingSentence(finding: Pick<FindingView, "about" | "subject">): string {
|
|
168
|
+
return `“${finding.about.text}” in ${finding.subject.name} ${aboutSentence(finding.about)}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** A member's dismissal of what a finding was about: no finding like it is raised again. */
|
|
172
|
+
export type DismissalView = {
|
|
173
|
+
id: number;
|
|
174
|
+
about: FindingAbout;
|
|
175
|
+
by: { id: string; name: string };
|
|
176
|
+
/** When it was dismissed, in milliseconds since the epoch. */
|
|
177
|
+
at: number;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/** What applying a finding's fix did: applied it, or found its content changed and rechecked. */
|
|
181
|
+
export type ApplyResult = { applied: boolean };
|
|
182
|
+
|
|
83
183
|
/** How many subjects a patroller is on in one locale, a locale's tag. */
|
|
84
184
|
export type LocaleCount = { locale: string; count: number };
|
|
85
185
|
|
|
@@ -96,18 +196,38 @@ export type PatrollerView = {
|
|
|
96
196
|
id: PatrollerId;
|
|
97
197
|
/** Its role, as editors know it: "Translator", "SEO manager". */
|
|
98
198
|
role: string;
|
|
199
|
+
/** Its duty in the scope viewed. Off there, it still has its reports. */
|
|
200
|
+
duty: PatrollerDuty;
|
|
99
201
|
/** Null when it isn't working in the scope. */
|
|
100
202
|
working: PatrollerWorking | null;
|
|
101
|
-
/**
|
|
203
|
+
/**
|
|
204
|
+
* When it last finished a check in the scope, whether it found anything or not, or null when it
|
|
205
|
+
* never checked there.
|
|
206
|
+
*/
|
|
207
|
+
lastCheckedAt: number | null;
|
|
208
|
+
/**
|
|
209
|
+
* Its latest activity in the scope: its last check, its newest report or when its pending work
|
|
210
|
+
* started, whichever is later. Null when it never worked there.
|
|
211
|
+
*/
|
|
102
212
|
lastActivityAt: number | null;
|
|
103
213
|
/** Its newest reports, newest first. */
|
|
104
214
|
reports: ReportView[];
|
|
105
215
|
/** How many more reports it has than `reports` shows, per locale, the most first. */
|
|
106
216
|
more: { locale: string | null; count: number }[];
|
|
217
|
+
/** Its open findings in the scope viewed, the oldest first. Their count is the row's badge. */
|
|
218
|
+
findings: FindingView[];
|
|
219
|
+
/** The project's dismissals of its findings, the newest first. */
|
|
220
|
+
dismissals: DismissalView[];
|
|
107
221
|
};
|
|
108
222
|
|
|
109
|
-
/**
|
|
110
|
-
|
|
223
|
+
/**
|
|
224
|
+
* Every patroller of the registry, the latest activity first; never-worked ones last. With the
|
|
225
|
+
* project's open Patrol Draft, where Live's findings are fixed, if any.
|
|
226
|
+
*/
|
|
227
|
+
export type PatrolView = {
|
|
228
|
+
patrollers: PatrollerView[];
|
|
229
|
+
patrolDraft: { id: string; name: string | null } | null;
|
|
230
|
+
};
|
|
111
231
|
|
|
112
232
|
/** How many reports the Patrol shows of each patroller; the rest are counted. */
|
|
113
233
|
export const REPORTS_SHOWN = 5;
|
package/src/threads.ts
CHANGED
|
@@ -16,6 +16,15 @@ import type { DraftOutcome } from "./drafts.ts";
|
|
|
16
16
|
*/
|
|
17
17
|
export const EDITOR_MESSAGE_ENTRY = "camox.editor-message";
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* The kind of the transcript entry recording a Connected Agent's message (ADR 0030), with its
|
|
21
|
+
* member and client as data (`connectedAgentMessageDataSchema`). The model reads it as a user
|
|
22
|
+
* message, as it does an editor's: its first text part is what the Connected Agent wrote, and a
|
|
23
|
+
* second one, which only the model reads, says who sent it. Studios show it as the member's AI,
|
|
24
|
+
* never as the member.
|
|
25
|
+
*/
|
|
26
|
+
export const CONNECTED_AGENT_MESSAGE_ENTRY = "camox.connected-agent-message";
|
|
27
|
+
|
|
19
28
|
/** The kind of the transcript entry recording that an editor stopped a run, with them as data. */
|
|
20
29
|
export const RUN_STOPPED_ENTRY = "camox.run-stopped";
|
|
21
30
|
|
|
@@ -36,7 +45,8 @@ export const RECONCILE_ENTRY = "camox.reconcile";
|
|
|
36
45
|
/**
|
|
37
46
|
* The kind of the transcript entry opening a draft's thread a Live thread handed a request off
|
|
38
47
|
* to: the Brief, which the model reads and editors see as from Camox, ahead of the editor's
|
|
39
|
-
* messages.
|
|
48
|
+
* messages. In a draft Camox opened, such as its Patrol Draft, Camox's own messages are Briefs
|
|
49
|
+
* too, with their `operationId` as data, and start the agent's run, which acts for Camox.
|
|
40
50
|
*/
|
|
41
51
|
export const BRIEF_ENTRY = "camox.brief";
|
|
42
52
|
|
|
@@ -67,6 +77,9 @@ export const START_DRAFT_TOOL = "startDraft";
|
|
|
67
77
|
/** The agent's tool building its code changes mid-turn, which the end of a turn also does. */
|
|
68
78
|
export const BUILD_TOOL = "build";
|
|
69
79
|
|
|
80
|
+
/** The agent's tool rendering the draft in a browser and returning screenshots to the model. */
|
|
81
|
+
export const LOOK_TOOL = "look";
|
|
82
|
+
|
|
70
83
|
/**
|
|
71
84
|
* The tools whose calls change a draft: the content tools that write, and the code tools that
|
|
72
85
|
* write files. The model calls the code tools, and code mode scripts call both. A test in the API
|
|
@@ -76,6 +89,7 @@ export const CHANGING_TOOLS: ReadonlySet<string> = new Set([
|
|
|
76
89
|
// Content
|
|
77
90
|
"addBackToLocale",
|
|
78
91
|
"addLocale",
|
|
92
|
+
"applyFindingFix",
|
|
79
93
|
"createBlock",
|
|
80
94
|
"createCollectionRecord",
|
|
81
95
|
"createPage",
|
|
@@ -132,6 +146,24 @@ export type ThreadSender = z.infer<typeof threadSenderSchema>;
|
|
|
132
146
|
export const isSender = (data: unknown): data is ThreadSender =>
|
|
133
147
|
threadSenderSchema.safeParse(data).success;
|
|
134
148
|
|
|
149
|
+
/**
|
|
150
|
+
* The app a Connected Agent runs in, by the name it gave: verified when it comes from a Client ID
|
|
151
|
+
* Metadata Document, self-asserted otherwise.
|
|
152
|
+
*/
|
|
153
|
+
export const connectedAgentClientSchema = z.object({ name: z.string(), verified: z.boolean() });
|
|
154
|
+
export type ConnectedAgentClient = z.infer<typeof connectedAgentClientSchema>;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* What a Connected Agent's message entry records: the member it acts as, its client, and the
|
|
158
|
+
* operation id it was sent with.
|
|
159
|
+
*/
|
|
160
|
+
export const connectedAgentMessageDataSchema = z.object({
|
|
161
|
+
member: threadSenderSchema,
|
|
162
|
+
client: connectedAgentClientSchema,
|
|
163
|
+
operationId: z.string().optional(),
|
|
164
|
+
});
|
|
165
|
+
export type ConnectedAgentMessageData = z.infer<typeof connectedAgentMessageDataSchema>;
|
|
166
|
+
|
|
135
167
|
/** What a push's entry records: who pushed, their commits' subjects, oldest first, and the build. */
|
|
136
168
|
export const pushDataSchema = z.object({
|
|
137
169
|
pusher: threadSenderSchema,
|
|
@@ -205,6 +237,17 @@ export type TouchedBatch = z.infer<typeof touchedBatchSchema>;
|
|
|
205
237
|
/** A message the studio shows in a thread. */
|
|
206
238
|
export type ThreadMessage =
|
|
207
239
|
| { id: string; from: "editor"; sender: ThreadSender; text: string }
|
|
240
|
+
/**
|
|
241
|
+
* A member's Connected Agent's message: the member's own AI, in its client, writing as itself.
|
|
242
|
+
* Never shown as the member's own words.
|
|
243
|
+
*/
|
|
244
|
+
| {
|
|
245
|
+
id: string;
|
|
246
|
+
from: "connectedAgent";
|
|
247
|
+
member: ThreadSender;
|
|
248
|
+
client: ConnectedAgentClient;
|
|
249
|
+
text: string;
|
|
250
|
+
}
|
|
208
251
|
/** Camox's own message, starting a turn it began itself: why it did. */
|
|
209
252
|
| { id: string; from: "camox"; text: string }
|
|
210
253
|
/** The agent's final message of a turn, in Markdown. */
|
|
@@ -236,6 +279,8 @@ export type ThreadMessage =
|
|
|
236
279
|
* - `changing`: a call of that round changes the draft (`CHANGING_TOOLS`), as soon as it's written
|
|
237
280
|
* or, in a code mode script, as soon as the script calls one.
|
|
238
281
|
* - `building`: its `build` call runs, or the end of the turn builds its code changes.
|
|
282
|
+
* - `checking`: it looks at what it designed, from writing a `look` call until the model is
|
|
283
|
+
* called again.
|
|
239
284
|
* - `fixing`: the end of the turn's build failed, and the agent fixes it.
|
|
240
285
|
* - `startingDraft`: on Live, its `startDraft` call runs.
|
|
241
286
|
*/
|
|
@@ -244,6 +289,7 @@ export type AgentActivity =
|
|
|
244
289
|
| "exploring"
|
|
245
290
|
| "changing"
|
|
246
291
|
| "building"
|
|
292
|
+
| "checking"
|
|
247
293
|
| "fixing"
|
|
248
294
|
| "startingDraft";
|
|
249
295
|
|
|
@@ -275,6 +321,8 @@ export type ThreadState = {
|
|
|
275
321
|
* or making it until the model is called again.
|
|
276
322
|
*/
|
|
277
323
|
changing?: boolean;
|
|
324
|
+
/** Whether the model's latest round calls `look`: from writing the call until it's called again. */
|
|
325
|
+
checking?: boolean;
|
|
278
326
|
/** The drafts the thread's cards name, as the thread last looked them up. */
|
|
279
327
|
drafts?: DraftCardStates;
|
|
280
328
|
};
|
|
@@ -290,6 +338,7 @@ type ThreadStep = { id: string } & (
|
|
|
290
338
|
/** The draft the message went to, as the agent on Live handed the request off. */
|
|
291
339
|
handedOffTo?: string;
|
|
292
340
|
}
|
|
341
|
+
| { type: "connectedAgent"; member: ThreadSender; client: ConnectedAgentClient; text: string }
|
|
293
342
|
/** Camox starting a turn of its own, saying why. */
|
|
294
343
|
| { type: "camox"; text: string }
|
|
295
344
|
| { type: "answer"; text: string }
|
|
@@ -461,6 +510,18 @@ function threadStepOf(entry: EntryRecord): ThreadStep | undefined {
|
|
|
461
510
|
}
|
|
462
511
|
);
|
|
463
512
|
}
|
|
513
|
+
if (entry.kind === CONNECTED_AGENT_MESSAGE_ENTRY) {
|
|
514
|
+
const data = connectedAgentMessageDataSchema.safeParse(entry.data).data;
|
|
515
|
+
return (
|
|
516
|
+
data && {
|
|
517
|
+
id,
|
|
518
|
+
type: "connectedAgent",
|
|
519
|
+
member: senderOf(data.member)!,
|
|
520
|
+
client: { name: data.client.name, verified: data.client.verified },
|
|
521
|
+
text: editorTextOf(message.content),
|
|
522
|
+
}
|
|
523
|
+
);
|
|
524
|
+
}
|
|
464
525
|
if (entry.kind === BRIEF_ENTRY) return { id, type: "camox", text: textOf(message.content) };
|
|
465
526
|
if (entry.kind === RECONCILE_ENTRY) {
|
|
466
527
|
const reconcile = reconcileDataSchema.safeParse(entry.data).data;
|
|
@@ -496,6 +557,7 @@ export function threadViewOf({
|
|
|
496
557
|
toolCalls = [],
|
|
497
558
|
callingTools = false,
|
|
498
559
|
changing = false,
|
|
560
|
+
checking = false,
|
|
499
561
|
drafts = {},
|
|
500
562
|
}: ThreadState): ThreadView {
|
|
501
563
|
const messages: ThreadMessage[] = [];
|
|
@@ -547,6 +609,18 @@ export function threadViewOf({
|
|
|
547
609
|
waiting = step.id;
|
|
548
610
|
break;
|
|
549
611
|
}
|
|
612
|
+
case "connectedAgent":
|
|
613
|
+
// Like an editor's message: it starts a turn, or steers the one going.
|
|
614
|
+
if (unfinished) fail();
|
|
615
|
+
messages.push({
|
|
616
|
+
id: step.id,
|
|
617
|
+
from: "connectedAgent",
|
|
618
|
+
member: step.member,
|
|
619
|
+
client: step.client,
|
|
620
|
+
text: step.text,
|
|
621
|
+
});
|
|
622
|
+
waiting = step.id;
|
|
623
|
+
break;
|
|
550
624
|
case "camox":
|
|
551
625
|
if (unfinished) fail();
|
|
552
626
|
messages.push({ id: step.id, from: "camox", text: step.text });
|
|
@@ -617,7 +691,7 @@ export function threadViewOf({
|
|
|
617
691
|
return {
|
|
618
692
|
messages,
|
|
619
693
|
running,
|
|
620
|
-
activity: activityOf({ status, toolCalls, callingTools, changing }),
|
|
694
|
+
activity: activityOf({ status, toolCalls, callingTools, changing, checking }),
|
|
621
695
|
};
|
|
622
696
|
}
|
|
623
697
|
|
|
@@ -627,16 +701,19 @@ function activityOf({
|
|
|
627
701
|
toolCalls,
|
|
628
702
|
callingTools,
|
|
629
703
|
changing,
|
|
704
|
+
checking,
|
|
630
705
|
}: {
|
|
631
706
|
status: BuildStatus | undefined;
|
|
632
707
|
toolCalls: NonNullable<ThreadState["toolCalls"]>;
|
|
633
708
|
callingTools: boolean;
|
|
634
709
|
changing: boolean;
|
|
710
|
+
checking: boolean;
|
|
635
711
|
}): AgentActivity {
|
|
636
712
|
const calling = (name: string) => toolCalls.some((call) => call.name === name);
|
|
637
713
|
// Fixing a build error takes thinking, reading and writing, which all read as fixing, but its
|
|
638
714
|
// builds show as building.
|
|
639
715
|
if (status === "building" || calling(BUILD_TOOL)) return "building";
|
|
716
|
+
if (checking) return "checking";
|
|
640
717
|
if (status === "fixing") return "fixing";
|
|
641
718
|
if (calling(START_DRAFT_TOOL)) return "startingDraft";
|
|
642
719
|
if (changing) return "changing";
|
|
@@ -651,6 +728,10 @@ const changes = (name: string) => CHANGING_TOOLS.has(name);
|
|
|
651
728
|
const callsChangingTool = (block: AssistantMessage["content"][number]) =>
|
|
652
729
|
block.type === "toolCall" && changes(block.name);
|
|
653
730
|
|
|
731
|
+
/** Whether a block of the model's message is a call of `look`. */
|
|
732
|
+
const callsLook = (block: AssistantMessage["content"][number]) =>
|
|
733
|
+
block.type === "toolCall" && block.name === LOOK_TOOL;
|
|
734
|
+
|
|
654
735
|
const append = (state: ThreadState, entry: EntryRecord | undefined): ThreadState => {
|
|
655
736
|
const step = entry && threadStepOf(entry);
|
|
656
737
|
if (!step || state.steps.some((known) => known.id === step.id)) return state;
|
|
@@ -671,6 +752,7 @@ export function reduceThreadState(state: ThreadState, event: AgentEvent): Thread
|
|
|
671
752
|
running: event.run !== undefined,
|
|
672
753
|
toolCalls,
|
|
673
754
|
callingTools: generated.some((part) => part.type === "toolCall"),
|
|
755
|
+
checking: generated.some(callsLook) || toolCalls.some((call) => call.name === LOOK_TOOL),
|
|
674
756
|
// A script's calls count while the script runs, finished or not.
|
|
675
757
|
changing:
|
|
676
758
|
generated.some(callsChangingTool) ||
|
|
@@ -689,11 +771,18 @@ export function reduceThreadState(state: ThreadState, event: AgentEvent): Thread
|
|
|
689
771
|
case "run_start":
|
|
690
772
|
return { ...state, running: true };
|
|
691
773
|
case "run_end":
|
|
692
|
-
return {
|
|
774
|
+
return {
|
|
775
|
+
...state,
|
|
776
|
+
running: false,
|
|
777
|
+
toolCalls: [],
|
|
778
|
+
callingTools: false,
|
|
779
|
+
changing: false,
|
|
780
|
+
checking: false,
|
|
781
|
+
};
|
|
693
782
|
case "message_start":
|
|
694
783
|
// The model is called again: it thinks until it writes a tool call.
|
|
695
784
|
return event.message.role === "assistant"
|
|
696
|
-
? { ...state, callingTools: false, changing: false }
|
|
785
|
+
? { ...state, callingTools: false, changing: false, checking: false }
|
|
697
786
|
: state;
|
|
698
787
|
case "message_update": {
|
|
699
788
|
// A call's name may come with its start, a later change to its block, or the whole message.
|
|
@@ -709,6 +798,7 @@ export function reduceThreadState(state: ThreadState, event: AgentEvent): Thread
|
|
|
709
798
|
...state,
|
|
710
799
|
callingTools: state.callingTools || calls.some((block) => block.type === "toolCall"),
|
|
711
800
|
changing: state.changing || calls.some(callsChangingTool),
|
|
801
|
+
checking: state.checking || calls.some(callsLook),
|
|
712
802
|
};
|
|
713
803
|
}
|
|
714
804
|
case "tool_execution_start": {
|
|
@@ -717,6 +807,7 @@ export function reduceThreadState(state: ThreadState, event: AgentEvent): Thread
|
|
|
717
807
|
return {
|
|
718
808
|
...state,
|
|
719
809
|
changing,
|
|
810
|
+
checking: state.checking || event.toolName === LOOK_TOOL,
|
|
720
811
|
toolCalls: [...(state.toolCalls ?? []), { callId: event.toolCallId, name: event.toolName }],
|
|
721
812
|
};
|
|
722
813
|
}
|