@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/dist/threads.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ import { Message } from "@earendil-works/pi-ai";
|
|
|
5
5
|
//#region src/threads.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* The kind of the transcript entry recording an editor's message, with its sender and the
|
|
8
|
-
* operation id the studio sent it with as data (`editorMessageDataSchema`).
|
|
8
|
+
* operation id the studio sent it with as data (`editorMessageDataSchema`). Its first text part
|
|
9
|
+
* is what the editor wrote; a second one, which only the model reads, says where they were in the
|
|
10
|
+
* studio as they sent it (`editorTextOf`).
|
|
9
11
|
*/
|
|
10
12
|
export declare const EDITOR_MESSAGE_ENTRY = "camox.editor-message";
|
|
11
13
|
/** The kind of the transcript entry recording that an editor stopped a run, with them as data. */
|
|
@@ -34,11 +36,32 @@ export declare const BRIEF_ENTRY = "camox.brief";
|
|
|
34
36
|
* it at the end of the conversation; editors never see it, since the draft's card says so.
|
|
35
37
|
*/
|
|
36
38
|
export declare const DRAFT_ENDED_ENTRY = "camox.draft-ended";
|
|
39
|
+
/**
|
|
40
|
+
* The kind of the transcript entry recording a member's push to the draft's code from their own
|
|
41
|
+
* checkout (ADR 0026), with a `pushDataSchema` as data. The model reads it as context, never
|
|
42
|
+
* answering it, and editors see who pushed, their commits, and whether they built.
|
|
43
|
+
*/
|
|
44
|
+
export declare const PUSH_ENTRY = "camox.push";
|
|
45
|
+
/** How a pushed head's build ended: built, built with type errors, or built but not previewed. */
|
|
46
|
+
export declare const pushOutcomeSchema: z.ZodEnum<{
|
|
47
|
+
built: "built";
|
|
48
|
+
typeErrors: "typeErrors";
|
|
49
|
+
notPreviewed: "notPreviewed";
|
|
50
|
+
}>;
|
|
51
|
+
export type PushOutcome = z.infer<typeof pushOutcomeSchema>;
|
|
37
52
|
/**
|
|
38
53
|
* The agent's tool on Live handing a request off to a draft it starts (ADR 0025). A run whose
|
|
39
54
|
* call of it went through ends on the draft's card.
|
|
40
55
|
*/
|
|
41
56
|
export declare const START_DRAFT_TOOL = "startDraft";
|
|
57
|
+
/** The agent's tool building its code changes mid-turn, which the end of a turn also does. */
|
|
58
|
+
export declare const BUILD_TOOL = "build";
|
|
59
|
+
/**
|
|
60
|
+
* The tools whose calls change a draft: the content tools that write, and the code tools that
|
|
61
|
+
* write files. The model calls the code tools, and code mode scripts call both. A test in the API
|
|
62
|
+
* keeps this in step with the tools themselves.
|
|
63
|
+
*/
|
|
64
|
+
export declare const CHANGING_TOOLS: ReadonlySet<string>;
|
|
42
65
|
/** The draft a `startDraft` call started, as its result's details name it for the card. */
|
|
43
66
|
export declare const startedDraftCardSchema: z.ZodObject<{
|
|
44
67
|
draftId: z.ZodString;
|
|
@@ -80,6 +103,21 @@ export declare const threadSenderSchema: z.ZodObject<{
|
|
|
80
103
|
export type ThreadSender = z.infer<typeof threadSenderSchema>;
|
|
81
104
|
/** Whether `data` names a sender. */
|
|
82
105
|
export declare const isSender: (data: unknown) => data is ThreadSender;
|
|
106
|
+
/** What a push's entry records: who pushed, their commits' subjects, oldest first, and the build. */
|
|
107
|
+
export declare const pushDataSchema: z.ZodObject<{
|
|
108
|
+
pusher: z.ZodObject<{
|
|
109
|
+
id: z.ZodString;
|
|
110
|
+
name: z.ZodString;
|
|
111
|
+
image: z.ZodNullable<z.ZodString>;
|
|
112
|
+
}, z.core.$strip>;
|
|
113
|
+
subjects: z.ZodArray<z.ZodString>;
|
|
114
|
+
outcome: z.ZodEnum<{
|
|
115
|
+
built: "built";
|
|
116
|
+
typeErrors: "typeErrors";
|
|
117
|
+
notPreviewed: "notPreviewed";
|
|
118
|
+
}>;
|
|
119
|
+
}, z.core.$strip>;
|
|
120
|
+
export type PushData = z.infer<typeof pushDataSchema>;
|
|
83
121
|
/**
|
|
84
122
|
* What an editor's message entry records: who sent it, the operation id it was sent with, and for
|
|
85
123
|
* a message sent on Live as the agent handed the request off, the draft it went to with it.
|
|
@@ -233,22 +271,37 @@ export type ThreadMessage = {
|
|
|
233
271
|
id: string;
|
|
234
272
|
from: "draft";
|
|
235
273
|
ended?: DraftOutcome;
|
|
236
|
-
} & StartedDraftCard)
|
|
274
|
+
} & StartedDraftCard) |
|
|
275
|
+
/**
|
|
276
|
+
* A member's push to the draft's code from their own checkout: who pushed, their commits'
|
|
277
|
+
* subjects, oldest first, and how the pushed head built. It's no turn: the agent doesn't
|
|
278
|
+
* answer it.
|
|
279
|
+
*/
|
|
280
|
+
{
|
|
281
|
+
id: string;
|
|
282
|
+
from: "push";
|
|
283
|
+
sender: ThreadSender;
|
|
284
|
+
subjects: string[];
|
|
285
|
+
outcome: PushOutcome;
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* What the agent is doing during a run, as editors see it, without the details of how it works:
|
|
289
|
+
* - `thinking`: the model is called, and hasn't started writing a tool call.
|
|
290
|
+
* - `exploring`: it uses tools that only read, from writing a call until the model is called again.
|
|
291
|
+
* - `changing`: a call of that round changes the draft (`CHANGING_TOOLS`), as soon as it's written
|
|
292
|
+
* or, in a code mode script, as soon as the script calls one.
|
|
293
|
+
* - `building`: its `build` call runs, or the end of the turn builds its code changes.
|
|
294
|
+
* - `fixing`: the end of the turn's build failed, and the agent fixes it.
|
|
295
|
+
* - `startingDraft`: on Live, its `startDraft` call runs.
|
|
296
|
+
*/
|
|
297
|
+
export type AgentActivity = "thinking" | "exploring" | "changing" | "building" | "fixing" | "startingDraft";
|
|
237
298
|
/** Everything the studio shows for a thread. */
|
|
238
299
|
export type ThreadView = {
|
|
239
300
|
messages: readonly ThreadMessage[];
|
|
240
301
|
/** Whether a run is going: the agent is working on a turn, and stopping would end it. */
|
|
241
302
|
running: boolean;
|
|
242
|
-
/**
|
|
243
|
-
|
|
244
|
-
* turn is building it, or it failed and the agent is fixing it.
|
|
245
|
-
*/
|
|
246
|
-
status?: BuildStatus;
|
|
247
|
-
/**
|
|
248
|
-
* While a run is going, whether the agent is using its tools, code mode included, rather than
|
|
249
|
-
* thinking: from writing a call until the model is called again.
|
|
250
|
-
*/
|
|
251
|
-
usingTools?: true;
|
|
303
|
+
/** While a run is going, what the agent is doing. */
|
|
304
|
+
activity?: AgentActivity;
|
|
252
305
|
};
|
|
253
306
|
export declare const EMPTY_THREAD_VIEW: ThreadView;
|
|
254
307
|
/**
|
|
@@ -259,10 +312,18 @@ export declare const EMPTY_THREAD_VIEW: ThreadView;
|
|
|
259
312
|
export type ThreadState = {
|
|
260
313
|
steps: readonly ThreadStep[];
|
|
261
314
|
running: boolean;
|
|
262
|
-
/** The model's calls still running
|
|
263
|
-
toolCalls?: readonly
|
|
315
|
+
/** The model's calls still running. A tool's own calls aren't the agent's. */
|
|
316
|
+
toolCalls?: readonly {
|
|
317
|
+
callId: string;
|
|
318
|
+
name: string;
|
|
319
|
+
}[];
|
|
264
320
|
/** Whether the model's latest message makes tool calls: it writes them, then they run. */
|
|
265
321
|
callingTools?: boolean;
|
|
322
|
+
/**
|
|
323
|
+
* Whether a call of the latest round changes the draft, the model's or a script's: from writing
|
|
324
|
+
* or making it until the model is called again.
|
|
325
|
+
*/
|
|
326
|
+
changing?: boolean;
|
|
266
327
|
/** The drafts the thread's cards name, as the thread last looked them up. */
|
|
267
328
|
drafts?: DraftCardStates;
|
|
268
329
|
};
|
|
@@ -305,6 +366,10 @@ type ThreadStep = {
|
|
|
305
366
|
type: "build";
|
|
306
367
|
status: BuildStatus;
|
|
307
368
|
} |
|
|
369
|
+
/** A member's push, between turns. */
|
|
370
|
+
({
|
|
371
|
+
type: "push";
|
|
372
|
+
} & PushData) |
|
|
308
373
|
/**
|
|
309
374
|
* The end of the turn sent the agent back to work, after its answer: the answer wasn't the
|
|
310
375
|
* turn's last, and what the agent was told is never shown.
|
|
@@ -312,11 +377,68 @@ type ThreadStep = {
|
|
|
312
377
|
{
|
|
313
378
|
type: "continued";
|
|
314
379
|
});
|
|
380
|
+
/**
|
|
381
|
+
* What an editor had selected on the page as they sent a message, unless they cleared it from
|
|
382
|
+
* the message: its path as the studio names it, from the block down to the selection, with the
|
|
383
|
+
* ids the agent's tools take. A record is the one the selection shows, a field the selection's
|
|
384
|
+
* own, of the record when a record is selected.
|
|
385
|
+
*/
|
|
386
|
+
export declare const editorSelectionSchema: z.ZodObject<{
|
|
387
|
+
path: z.ZodArray<z.ZodString>;
|
|
388
|
+
blockId: z.ZodOptional<z.ZodString>;
|
|
389
|
+
itemId: z.ZodOptional<z.ZodString>;
|
|
390
|
+
recordId: z.ZodOptional<z.ZodString>;
|
|
391
|
+
fieldName: z.ZodOptional<z.ZodString>;
|
|
392
|
+
}, z.core.$strip>;
|
|
393
|
+
export type EditorSelection = z.infer<typeof editorSelectionSchema>;
|
|
394
|
+
/**
|
|
395
|
+
* Where an editor was in the studio as they sent a message, in the Locale they viewed: a Page,
|
|
396
|
+
* by its path after the Locale's prefix and its title, with its id for a curated page and its
|
|
397
|
+
* Layout's for a layout page, and what they had selected on it, or the Canvas with no page
|
|
398
|
+
* selected. A hint for the agent, never what the message is about.
|
|
399
|
+
*/
|
|
400
|
+
export declare const editorWhereSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
401
|
+
on: z.ZodLiteral<"page">;
|
|
402
|
+
locale: z.ZodString;
|
|
403
|
+
path: z.ZodString;
|
|
404
|
+
title: z.ZodString;
|
|
405
|
+
pageId: z.ZodOptional<z.ZodString>;
|
|
406
|
+
layoutId: z.ZodOptional<z.ZodString>;
|
|
407
|
+
selection: z.ZodOptional<z.ZodObject<{
|
|
408
|
+
path: z.ZodArray<z.ZodString>;
|
|
409
|
+
blockId: z.ZodOptional<z.ZodString>;
|
|
410
|
+
itemId: z.ZodOptional<z.ZodString>;
|
|
411
|
+
recordId: z.ZodOptional<z.ZodString>;
|
|
412
|
+
fieldName: z.ZodOptional<z.ZodString>;
|
|
413
|
+
}, z.core.$strip>>;
|
|
414
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
415
|
+
on: z.ZodLiteral<"canvas">;
|
|
416
|
+
locale: z.ZodString;
|
|
417
|
+
}, z.core.$strip>], "on">;
|
|
418
|
+
export type EditorWhere = z.infer<typeof editorWhereSchema>;
|
|
315
419
|
/** Studio → thread. */
|
|
316
420
|
export declare const threadClientMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
317
421
|
type: z.ZodLiteral<"send">;
|
|
318
422
|
operationId: z.ZodString;
|
|
319
423
|
text: z.ZodString;
|
|
424
|
+
where: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
425
|
+
on: z.ZodLiteral<"page">;
|
|
426
|
+
locale: z.ZodString;
|
|
427
|
+
path: z.ZodString;
|
|
428
|
+
title: z.ZodString;
|
|
429
|
+
pageId: z.ZodOptional<z.ZodString>;
|
|
430
|
+
layoutId: z.ZodOptional<z.ZodString>;
|
|
431
|
+
selection: z.ZodOptional<z.ZodObject<{
|
|
432
|
+
path: z.ZodArray<z.ZodString>;
|
|
433
|
+
blockId: z.ZodOptional<z.ZodString>;
|
|
434
|
+
itemId: z.ZodOptional<z.ZodString>;
|
|
435
|
+
recordId: z.ZodOptional<z.ZodString>;
|
|
436
|
+
fieldName: z.ZodOptional<z.ZodString>;
|
|
437
|
+
}, z.core.$strip>>;
|
|
438
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
439
|
+
on: z.ZodLiteral<"canvas">;
|
|
440
|
+
locale: z.ZodString;
|
|
441
|
+
}, z.core.$strip>], "on">>;
|
|
320
442
|
}, z.core.$strip>, z.ZodObject<{
|
|
321
443
|
type: z.ZodLiteral<"stop">;
|
|
322
444
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -368,12 +490,16 @@ export type ThreadServerMessage =
|
|
|
368
490
|
};
|
|
369
491
|
/**
|
|
370
492
|
* The code a thread closes its sockets with once it ended with its draft, and for good: it's
|
|
371
|
-
* deleted, so reconnecting reaches nothing.
|
|
372
|
-
* thread the next time the studio connects.
|
|
493
|
+
* deleted, so reconnecting reaches nothing.
|
|
373
494
|
*/
|
|
374
495
|
export declare const THREAD_ENDED_CLOSE_CODE = 4000;
|
|
375
496
|
/** The text of a message's content: its text parts, joined. */
|
|
376
497
|
export declare const textOf: (content: Message["content"]) => string;
|
|
498
|
+
/**
|
|
499
|
+
* What an editor wrote, of their message entry's content: its first text part. The rest, where
|
|
500
|
+
* they were in the studio, only the model reads.
|
|
501
|
+
*/
|
|
502
|
+
export declare const editorTextOf: (content: Message["content"]) => string;
|
|
377
503
|
/**
|
|
378
504
|
* What the studio shows for a thread. A turn starts with an editor's message, or Camox's own
|
|
379
505
|
* when it began the turn itself, and messages sent
|
|
@@ -383,7 +509,7 @@ export declare const textOf: (content: Message["content"]) => string;
|
|
|
383
509
|
* run is over, or a later message started another run after its last model call came back
|
|
384
510
|
* unfinished. While Pi retries a failed call, the run goes on, so the turn shows as working.
|
|
385
511
|
*/
|
|
386
|
-
export declare function threadViewOf({ steps, running, toolCalls, callingTools, drafts }: ThreadState): ThreadView;
|
|
512
|
+
export declare function threadViewOf({ steps, running, toolCalls, callingTools, changing, drafts }: ThreadState): ThreadView;
|
|
387
513
|
/** Folds one of Pi's agent events into the thread's state. */
|
|
388
514
|
export declare function reduceThreadState(state: ThreadState, event: AgentEvent): ThreadState;
|
|
389
515
|
/** Folds a batch of Pi's agent events into the thread's state. */
|
package/dist/threads.js
CHANGED
|
@@ -3,7 +3,9 @@ import { z } from "zod";
|
|
|
3
3
|
//#region src/threads.ts
|
|
4
4
|
/**
|
|
5
5
|
* The kind of the transcript entry recording an editor's message, with its sender and the
|
|
6
|
-
* operation id the studio sent it with as data (`editorMessageDataSchema`).
|
|
6
|
+
* operation id the studio sent it with as data (`editorMessageDataSchema`). Its first text part
|
|
7
|
+
* is what the editor wrote; a second one, which only the model reads, says where they were in the
|
|
8
|
+
* studio as they sent it (`editorTextOf`).
|
|
7
9
|
*/
|
|
8
10
|
const EDITOR_MESSAGE_ENTRY = "camox.editor-message";
|
|
9
11
|
/** The kind of the transcript entry recording that an editor stopped a run, with them as data. */
|
|
@@ -33,10 +35,52 @@ const BRIEF_ENTRY = "camox.brief";
|
|
|
33
35
|
*/
|
|
34
36
|
const DRAFT_ENDED_ENTRY = "camox.draft-ended";
|
|
35
37
|
/**
|
|
38
|
+
* The kind of the transcript entry recording a member's push to the draft's code from their own
|
|
39
|
+
* checkout (ADR 0026), with a `pushDataSchema` as data. The model reads it as context, never
|
|
40
|
+
* answering it, and editors see who pushed, their commits, and whether they built.
|
|
41
|
+
*/
|
|
42
|
+
const PUSH_ENTRY = "camox.push";
|
|
43
|
+
/** How a pushed head's build ended: built, built with type errors, or built but not previewed. */
|
|
44
|
+
const pushOutcomeSchema = z.enum([
|
|
45
|
+
"built",
|
|
46
|
+
"typeErrors",
|
|
47
|
+
"notPreviewed"
|
|
48
|
+
]);
|
|
49
|
+
/**
|
|
36
50
|
* The agent's tool on Live handing a request off to a draft it starts (ADR 0025). A run whose
|
|
37
51
|
* call of it went through ends on the draft's card.
|
|
38
52
|
*/
|
|
39
53
|
const START_DRAFT_TOOL = "startDraft";
|
|
54
|
+
/** The agent's tool building its code changes mid-turn, which the end of a turn also does. */
|
|
55
|
+
const BUILD_TOOL = "build";
|
|
56
|
+
/**
|
|
57
|
+
* The tools whose calls change a draft: the content tools that write, and the code tools that
|
|
58
|
+
* write files. The model calls the code tools, and code mode scripts call both. A test in the API
|
|
59
|
+
* keeps this in step with the tools themselves.
|
|
60
|
+
*/
|
|
61
|
+
const CHANGING_TOOLS = /* @__PURE__ */ new Set([
|
|
62
|
+
"addBackToLocale",
|
|
63
|
+
"addLocale",
|
|
64
|
+
"createBlock",
|
|
65
|
+
"createCollectionRecord",
|
|
66
|
+
"createPage",
|
|
67
|
+
"deleteBlock",
|
|
68
|
+
"deleteCollectionRecord",
|
|
69
|
+
"deletePage",
|
|
70
|
+
"editBlock",
|
|
71
|
+
"editCollectionRecord",
|
|
72
|
+
"moveBlock",
|
|
73
|
+
"removeLocale",
|
|
74
|
+
"setPageLayout",
|
|
75
|
+
"setPageSeo",
|
|
76
|
+
"updateFile",
|
|
77
|
+
"updatePage",
|
|
78
|
+
"uploadFile",
|
|
79
|
+
"edit",
|
|
80
|
+
"mv",
|
|
81
|
+
"rm",
|
|
82
|
+
"write"
|
|
83
|
+
]);
|
|
40
84
|
/** The draft a `startDraft` call started, as its result's details name it for the card. */
|
|
41
85
|
const startedDraftCardSchema = z.object({
|
|
42
86
|
draftId: z.string(),
|
|
@@ -53,6 +97,12 @@ const threadSenderSchema = z.object({
|
|
|
53
97
|
});
|
|
54
98
|
/** Whether `data` names a sender. */
|
|
55
99
|
const isSender = (data) => threadSenderSchema.safeParse(data).success;
|
|
100
|
+
/** What a push's entry records: who pushed, their commits' subjects, oldest first, and the build. */
|
|
101
|
+
const pushDataSchema = z.object({
|
|
102
|
+
pusher: threadSenderSchema,
|
|
103
|
+
subjects: z.array(z.string()),
|
|
104
|
+
outcome: pushOutcomeSchema
|
|
105
|
+
});
|
|
56
106
|
/**
|
|
57
107
|
* What an editor's message entry records: who sent it, the operation id it was sent with, and for
|
|
58
108
|
* a message sent on Live as the agent handed the request off, the draft it went to with it.
|
|
@@ -122,24 +172,60 @@ const EMPTY_THREAD_STATE = {
|
|
|
122
172
|
steps: [],
|
|
123
173
|
running: false
|
|
124
174
|
};
|
|
175
|
+
/**
|
|
176
|
+
* What an editor had selected on the page as they sent a message, unless they cleared it from
|
|
177
|
+
* the message: its path as the studio names it, from the block down to the selection, with the
|
|
178
|
+
* ids the agent's tools take. A record is the one the selection shows, a field the selection's
|
|
179
|
+
* own, of the record when a record is selected.
|
|
180
|
+
*/
|
|
181
|
+
const editorSelectionSchema = z.object({
|
|
182
|
+
path: z.array(z.string().max(512)).min(1).max(16),
|
|
183
|
+
blockId: z.string().max(64).optional(),
|
|
184
|
+
itemId: z.string().max(64).optional(),
|
|
185
|
+
recordId: z.string().max(64).optional(),
|
|
186
|
+
fieldName: z.string().max(256).optional()
|
|
187
|
+
});
|
|
188
|
+
/**
|
|
189
|
+
* Where an editor was in the studio as they sent a message, in the Locale they viewed: a Page,
|
|
190
|
+
* by its path after the Locale's prefix and its title, with its id for a curated page and its
|
|
191
|
+
* Layout's for a layout page, and what they had selected on it, or the Canvas with no page
|
|
192
|
+
* selected. A hint for the agent, never what the message is about.
|
|
193
|
+
*/
|
|
194
|
+
const editorWhereSchema = z.discriminatedUnion("on", [z.object({
|
|
195
|
+
on: z.literal("page"),
|
|
196
|
+
locale: z.string().max(64),
|
|
197
|
+
path: z.string().max(2048),
|
|
198
|
+
title: z.string().max(512),
|
|
199
|
+
pageId: z.string().max(64).optional(),
|
|
200
|
+
layoutId: z.string().max(256).optional(),
|
|
201
|
+
selection: editorSelectionSchema.optional()
|
|
202
|
+
}), z.object({
|
|
203
|
+
on: z.literal("canvas"),
|
|
204
|
+
locale: z.string().max(64)
|
|
205
|
+
})]);
|
|
125
206
|
/** Studio → thread. */
|
|
126
207
|
const threadClientMessageSchema = z.discriminatedUnion("type", [
|
|
127
208
|
z.object({
|
|
128
209
|
type: z.literal("send"),
|
|
129
210
|
operationId: z.string().min(1),
|
|
130
|
-
text: z.string().min(1)
|
|
211
|
+
text: z.string().min(1),
|
|
212
|
+
where: editorWhereSchema.optional()
|
|
131
213
|
}),
|
|
132
214
|
z.object({ type: z.literal("stop") }),
|
|
133
215
|
z.object({ type: z.literal("resync") })
|
|
134
216
|
]);
|
|
135
217
|
/**
|
|
136
218
|
* The code a thread closes its sockets with once it ended with its draft, and for good: it's
|
|
137
|
-
* deleted, so reconnecting reaches nothing.
|
|
138
|
-
* thread the next time the studio connects.
|
|
219
|
+
* deleted, so reconnecting reaches nothing.
|
|
139
220
|
*/
|
|
140
221
|
const THREAD_ENDED_CLOSE_CODE = 4e3;
|
|
141
222
|
/** The text of a message's content: its text parts, joined. */
|
|
142
223
|
const textOf = (content) => typeof content === "string" ? content : content.flatMap((part) => part.type === "text" ? [part.text] : []).join("").trim();
|
|
224
|
+
/**
|
|
225
|
+
* What an editor wrote, of their message entry's content: its first text part. The rest, where
|
|
226
|
+
* they were in the studio, only the model reads.
|
|
227
|
+
*/
|
|
228
|
+
const editorTextOf = (content) => typeof content === "string" ? content : (content.find((part) => part.type === "text")?.text ?? "").trim();
|
|
143
229
|
/** What an assistant message is to the thread: a final answer, a tool round, or neither. */
|
|
144
230
|
function assistantStep(id, message) {
|
|
145
231
|
if (message.content.some((part) => part.type === "toolCall")) return {
|
|
@@ -184,6 +270,14 @@ function threadStepOf(entry) {
|
|
|
184
270
|
};
|
|
185
271
|
}
|
|
186
272
|
if (entry.kind === "camox.draft-ended") return void 0;
|
|
273
|
+
if (entry.kind === "camox.push") {
|
|
274
|
+
const push = pushDataSchema.safeParse(entry.data).data;
|
|
275
|
+
return push && {
|
|
276
|
+
id,
|
|
277
|
+
type: "push",
|
|
278
|
+
...push
|
|
279
|
+
};
|
|
280
|
+
}
|
|
187
281
|
const message = entry.model?.find((candidate) => candidate.role !== "system");
|
|
188
282
|
if (!message) return void 0;
|
|
189
283
|
if (message.role === "user") {
|
|
@@ -193,7 +287,7 @@ function threadStepOf(entry) {
|
|
|
193
287
|
id,
|
|
194
288
|
type: "editor",
|
|
195
289
|
sender,
|
|
196
|
-
text:
|
|
290
|
+
text: editorTextOf(message.content),
|
|
197
291
|
...handedOffTo && { handedOffTo }
|
|
198
292
|
};
|
|
199
293
|
}
|
|
@@ -237,7 +331,7 @@ function threadStepOf(entry) {
|
|
|
237
331
|
* run is over, or a later message started another run after its last model call came back
|
|
238
332
|
* unfinished. While Pi retries a failed call, the run goes on, so the turn shows as working.
|
|
239
333
|
*/
|
|
240
|
-
function threadViewOf({ steps, running, toolCalls = [], callingTools = false, drafts = {} }) {
|
|
334
|
+
function threadViewOf({ steps, running, toolCalls = [], callingTools = false, changing = false, drafts = {} }) {
|
|
241
335
|
const messages = [];
|
|
242
336
|
let waiting;
|
|
243
337
|
let unfinished = false;
|
|
@@ -338,20 +432,50 @@ function threadViewOf({ steps, running, toolCalls = [], callingTools = false, dr
|
|
|
338
432
|
case "build":
|
|
339
433
|
status = step.status;
|
|
340
434
|
break;
|
|
435
|
+
case "push":
|
|
436
|
+
messages.push({
|
|
437
|
+
id: step.id,
|
|
438
|
+
from: "push",
|
|
439
|
+
sender: step.pusher,
|
|
440
|
+
subjects: step.subjects,
|
|
441
|
+
outcome: step.outcome
|
|
442
|
+
});
|
|
443
|
+
break;
|
|
341
444
|
case "tools":
|
|
342
445
|
unfinished = false;
|
|
343
446
|
break;
|
|
344
447
|
case "unfinished": unfinished = true;
|
|
345
448
|
}
|
|
346
449
|
if (!running) fail();
|
|
347
|
-
|
|
450
|
+
if (!running) return {
|
|
451
|
+
messages,
|
|
452
|
+
running
|
|
453
|
+
};
|
|
348
454
|
return {
|
|
349
455
|
messages,
|
|
350
456
|
running,
|
|
351
|
-
|
|
352
|
-
|
|
457
|
+
activity: activityOf({
|
|
458
|
+
status,
|
|
459
|
+
toolCalls,
|
|
460
|
+
callingTools,
|
|
461
|
+
changing
|
|
462
|
+
})
|
|
353
463
|
};
|
|
354
464
|
}
|
|
465
|
+
/** What the agent is doing during a run, from where the turn's build stands and its tool calls. */
|
|
466
|
+
function activityOf({ status, toolCalls, callingTools, changing }) {
|
|
467
|
+
const calling = (name) => toolCalls.some((call) => call.name === name);
|
|
468
|
+
if (status === "building" || calling("build")) return "building";
|
|
469
|
+
if (status === "fixing") return "fixing";
|
|
470
|
+
if (calling("startDraft")) return "startingDraft";
|
|
471
|
+
if (changing) return "changing";
|
|
472
|
+
if (callingTools || toolCalls.length > 0) return "exploring";
|
|
473
|
+
return "thinking";
|
|
474
|
+
}
|
|
475
|
+
/** Whether a tool call changes the draft. */
|
|
476
|
+
const changes = (name) => CHANGING_TOOLS.has(name);
|
|
477
|
+
/** Whether a block of the model's message is a call of a tool that changes the draft. */
|
|
478
|
+
const callsChangingTool = (block) => block.type === "toolCall" && changes(block.name);
|
|
355
479
|
const append = (state, entry) => {
|
|
356
480
|
const step = entry && threadStepOf(entry);
|
|
357
481
|
if (!step || state.steps.some((known) => known.id === step.id)) return state;
|
|
@@ -363,13 +487,21 @@ const append = (state, entry) => {
|
|
|
363
487
|
/** Folds one of Pi's agent events into the thread's state. */
|
|
364
488
|
function reduceThreadState(state, event) {
|
|
365
489
|
switch (event.type) {
|
|
366
|
-
case "snapshot":
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
490
|
+
case "snapshot": {
|
|
491
|
+
const toolCalls = event.tools.flatMap((tool) => tool.status === "done" ? [] : [{
|
|
492
|
+
callId: tool.callId,
|
|
493
|
+
name: tool.name
|
|
494
|
+
}]);
|
|
495
|
+
const generated = event.generation?.message?.content ?? [];
|
|
496
|
+
return {
|
|
497
|
+
...state,
|
|
498
|
+
steps: event.entries.flatMap((entry) => threadStepOf(entry) ?? []),
|
|
499
|
+
running: event.run !== void 0,
|
|
500
|
+
toolCalls,
|
|
501
|
+
callingTools: generated.some((part) => part.type === "toolCall"),
|
|
502
|
+
changing: generated.some(callsChangingTool) || toolCalls.some((call) => changes(call.name)) || event.nestedTools.some((nested) => changes(nested.name) && toolCalls.some((call) => nested.parentCallId === call.callId || nested.parentCallId.startsWith(`${call.callId}/`)))
|
|
503
|
+
};
|
|
504
|
+
}
|
|
373
505
|
case "run_start": return {
|
|
374
506
|
...state,
|
|
375
507
|
running: true
|
|
@@ -378,25 +510,41 @@ function reduceThreadState(state, event) {
|
|
|
378
510
|
...state,
|
|
379
511
|
running: false,
|
|
380
512
|
toolCalls: [],
|
|
381
|
-
callingTools: false
|
|
513
|
+
callingTools: false,
|
|
514
|
+
changing: false
|
|
382
515
|
};
|
|
383
516
|
case "message_start": return event.message.role === "assistant" ? {
|
|
384
517
|
...state,
|
|
385
|
-
callingTools: false
|
|
518
|
+
callingTools: false,
|
|
519
|
+
changing: false
|
|
386
520
|
} : state;
|
|
387
|
-
case "message_update":
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
} : state;
|
|
391
|
-
case "tool_execution_start":
|
|
392
|
-
if (event.parentToolCallId !== void 0) return state;
|
|
521
|
+
case "message_update": {
|
|
522
|
+
const calls = event.changes.flatMap((change) => change.type === "toolcall_start" || change.type === "block" ? [change.block] : change.type === "message" ? change.message.content : []);
|
|
523
|
+
if (calls.length === 0) return state;
|
|
393
524
|
return {
|
|
394
525
|
...state,
|
|
395
|
-
|
|
526
|
+
callingTools: state.callingTools || calls.some((block) => block.type === "toolCall"),
|
|
527
|
+
changing: state.changing || calls.some(callsChangingTool)
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
case "tool_execution_start": {
|
|
531
|
+
const changing = state.changing || changes(event.toolName);
|
|
532
|
+
if (event.parentToolCallId !== void 0) return {
|
|
533
|
+
...state,
|
|
534
|
+
changing
|
|
396
535
|
};
|
|
536
|
+
return {
|
|
537
|
+
...state,
|
|
538
|
+
changing,
|
|
539
|
+
toolCalls: [...state.toolCalls ?? [], {
|
|
540
|
+
callId: event.toolCallId,
|
|
541
|
+
name: event.toolName
|
|
542
|
+
}]
|
|
543
|
+
};
|
|
544
|
+
}
|
|
397
545
|
case "tool_execution_end": return append({
|
|
398
546
|
...state,
|
|
399
|
-
toolCalls: state.toolCalls?.filter((
|
|
547
|
+
toolCalls: state.toolCalls?.filter((call) => call.callId !== event.toolCallId)
|
|
400
548
|
}, event.entry);
|
|
401
549
|
case "message_end":
|
|
402
550
|
case "entry_appended": return append(state, event.entry);
|
|
@@ -418,4 +566,4 @@ function reduceThreadFrame(state, frame) {
|
|
|
418
566
|
}
|
|
419
567
|
|
|
420
568
|
//#endregion
|
|
421
|
-
export { BRIEF_ENTRY, BUILD_STATUS_ENTRY, DRAFT_ENDED_ENTRY, EDITOR_MESSAGE_ENTRY, EMPTY_THREAD_STATE, EMPTY_THREAD_VIEW, RECONCILE_ENTRY, RUN_STOPPED_ENTRY, START_DRAFT_TOOL, THREAD_ENDED_CLOSE_CODE, buildStatusDataSchema, buildStatusSchema, editorMessageDataSchema, isSender, reconcileDataSchema, reduceThreadEvents, reduceThreadFrame, reduceThreadState, startedDraftCardSchema, startedDraftSchema, textOf, threadClientMessageSchema, threadSenderSchema, threadViewOf, touchKindSchema, touchSchema, touchedBatchSchema };
|
|
569
|
+
export { BRIEF_ENTRY, BUILD_STATUS_ENTRY, BUILD_TOOL, CHANGING_TOOLS, DRAFT_ENDED_ENTRY, EDITOR_MESSAGE_ENTRY, EMPTY_THREAD_STATE, EMPTY_THREAD_VIEW, PUSH_ENTRY, RECONCILE_ENTRY, RUN_STOPPED_ENTRY, START_DRAFT_TOOL, THREAD_ENDED_CLOSE_CODE, buildStatusDataSchema, buildStatusSchema, editorMessageDataSchema, editorSelectionSchema, editorTextOf, editorWhereSchema, isSender, pushDataSchema, pushOutcomeSchema, reconcileDataSchema, reduceThreadEvents, reduceThreadFrame, reduceThreadState, startedDraftCardSchema, startedDraftSchema, textOf, threadClientMessageSchema, threadSenderSchema, threadViewOf, touchKindSchema, touchSchema, touchedBatchSchema };
|
package/package.json
CHANGED
package/src/drafts.ts
CHANGED
|
@@ -31,25 +31,10 @@ export const PUBLISHING_DRAFT_VAR = "CAMOX_PUBLISHING_DRAFT";
|
|
|
31
31
|
*/
|
|
32
32
|
export const DRAFT_CREATE_HEADER = "x-camox-draft-create";
|
|
33
33
|
|
|
34
|
-
/**
|
|
35
|
-
* The `x-camox-draft` value naming the caller's own development draft in the project a request
|
|
36
|
-
* acts on (ADR 0006, ADR 0011). Dev mode and the CLI always send it.
|
|
37
|
-
*/
|
|
38
|
-
export const DEVELOPMENT_DRAFT = "development";
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* oRPC error code for a request naming the development draft before dev mode created it: the
|
|
42
|
-
* first dev-mode sync after login does, so the site has to run locally once first.
|
|
43
|
-
*/
|
|
44
|
-
export const NO_DEVELOPMENT_DRAFT_ERROR = "NO_DEVELOPMENT_DRAFT";
|
|
45
|
-
|
|
46
34
|
/** URL search param carrying the current draft's id in the studio and the rendered site. */
|
|
47
35
|
export const DRAFT_SEARCH_PARAM = "camox-draft";
|
|
48
36
|
|
|
49
|
-
/**
|
|
50
|
-
export type DraftKind = "regular" | "development";
|
|
51
|
-
|
|
52
|
-
/** How a regular draft ended. */
|
|
37
|
+
/** How a draft ended. */
|
|
53
38
|
export type DraftOutcome = "published" | "deleted";
|
|
54
39
|
|
|
55
40
|
/** Who ended a draft, how and when, from its ended-drafts record (ADR 0009, ADR 0011). */
|
|
@@ -140,7 +125,7 @@ export const LIVE_READ_ONLY_ERROR = "LIVE_READ_ONLY";
|
|
|
140
125
|
// for one on Live. Every procedure is listed, so a new one fails the type check until it's
|
|
141
126
|
// classified and can't be forgotten. Procedures that only fill a generated value (ADR 0012),
|
|
142
127
|
// like `generateSummary`, write no content. A synced block's shared content is written through
|
|
143
|
-
// `blocks` and `repeatableItems`. Releases
|
|
128
|
+
// `blocks` and `repeatableItems`. Releases aren't listed.
|
|
144
129
|
const BLOCK_WRITES: Record<keyof Router["blocks"], boolean> = {
|
|
145
130
|
get: false,
|
|
146
131
|
getPageMarkdown: false,
|
|
@@ -223,7 +208,7 @@ const LOCALE_WRITES: Record<keyof Router["locales"], boolean> = {
|
|
|
223
208
|
addBack: true,
|
|
224
209
|
};
|
|
225
210
|
|
|
226
|
-
// `definitions` is absent: its sync is a release
|
|
211
|
+
// `definitions` is absent: its sync is a release, never a studio content write.
|
|
227
212
|
const CONTENT_WRITES: Partial<Record<keyof Router, Record<string, boolean>>> = {
|
|
228
213
|
blocks: BLOCK_WRITES,
|
|
229
214
|
locales: LOCALE_WRITES,
|
package/src/projects.ts
CHANGED
|
@@ -42,8 +42,12 @@ export const projectSlugSchema = /* @__PURE__ */ z
|
|
|
42
42
|
"Use lowercase letters, digits and hyphens, without a hyphen at either end",
|
|
43
43
|
);
|
|
44
44
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
45
|
+
/**
|
|
46
|
+
* The address a project's website is served at: its Custom Domain when it has one, else its
|
|
47
|
+
* Project Slug's `camox.site` subdomain, which then redirects to it (ADR 0028).
|
|
48
|
+
*/
|
|
49
|
+
export const projectWebsiteUrl = (project: { slug: string; customDomain: string | null }) =>
|
|
50
|
+
`https://${project.customDomain ?? `${project.slug}.camox.site`}`;
|
|
47
51
|
|
|
48
52
|
/** Why a slug can't be given to a new project. */
|
|
49
53
|
export type UnavailableSlugReason = "invalid" | "reserved" | "taken";
|