@camox/api-contract 0.66.0 → 0.67.0

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 CHANGED
@@ -49,6 +49,20 @@ export declare const LOOK_LOCALE_PARAM = "locale";
49
49
  export declare const LOOK_BLOCK_DEFINITION_PARAM = "blockDefinition";
50
50
  export declare const LOOK_CONTENT_PARAM = "content";
51
51
  export declare const LOOK_SETTINGS_PARAM = "settings";
52
+ /**
53
+ * The look document's Layout Definition mode: instead of a page, the curated Layout
54
+ * `LOOK_LAYOUT_DEFINITION_PARAM` names, alone in the site's shell, its blocks with their default
55
+ * content and Block Settings and the overrides `LOOK_BLOCKS_PARAM` gives as JSON, by Block
56
+ * Definition name (`{ navbar: { content, settings } }`), around a labelled placeholder where a
57
+ * page's content goes, as tall as `LOOK_PLACEHOLDER_PARAM` says: one of `LOOK_PLACEHOLDER_HEIGHTS`,
58
+ * the first without.
59
+ */
60
+ export declare const LOOK_LAYOUT_DEFINITION_PARAM = "layoutDefinition";
61
+ export declare const LOOK_BLOCKS_PARAM = "blocks";
62
+ export declare const LOOK_PLACEHOLDER_PARAM = "placeholder";
63
+ /** A short placeholder shows whether the footer rises up the screen; a tall one, scrolling. */
64
+ export declare const LOOK_PLACEHOLDER_HEIGHTS: readonly ["short", "tall"];
65
+ export type LookPlaceholderHeight = (typeof LOOK_PLACEHOLDER_HEIGHTS)[number];
52
66
  /**
53
67
  * The id of a block shown without being placed, as the look document's Block Definition mode
54
68
  * shows it: never a stored block's, which are UUIDs. Its element is
package/dist/drafts.js CHANGED
@@ -50,6 +50,19 @@ const LOOK_BLOCK_DEFINITION_PARAM = "blockDefinition";
50
50
  const LOOK_CONTENT_PARAM = "content";
51
51
  const LOOK_SETTINGS_PARAM = "settings";
52
52
  /**
53
+ * The look document's Layout Definition mode: instead of a page, the curated Layout
54
+ * `LOOK_LAYOUT_DEFINITION_PARAM` names, alone in the site's shell, its blocks with their default
55
+ * content and Block Settings and the overrides `LOOK_BLOCKS_PARAM` gives as JSON, by Block
56
+ * Definition name (`{ navbar: { content, settings } }`), around a labelled placeholder where a
57
+ * page's content goes, as tall as `LOOK_PLACEHOLDER_PARAM` says: one of `LOOK_PLACEHOLDER_HEIGHTS`,
58
+ * the first without.
59
+ */
60
+ const LOOK_LAYOUT_DEFINITION_PARAM = "layoutDefinition";
61
+ const LOOK_BLOCKS_PARAM = "blocks";
62
+ const LOOK_PLACEHOLDER_PARAM = "placeholder";
63
+ /** A short placeholder shows whether the footer rises up the screen; a tall one, scrolling. */
64
+ const LOOK_PLACEHOLDER_HEIGHTS = ["short", "tall"];
65
+ /**
53
66
  * The id of a block shown without being placed, as the look document's Block Definition mode
54
67
  * shows it: never a stored block's, which are UUIDs. Its element is
55
68
  * `[data-camox-viewport-block="unplaced"]`.
@@ -223,4 +236,4 @@ function isContentWrite(path) {
223
236
  }
224
237
 
225
238
  //#endregion
226
- export { CAMOX_ACTOR, CAMOX_NAME, CHECKS_RUNNING_ERROR, CONFLICTS_CHANGED_ERROR, DRAFT_CREATE_HEADER, DRAFT_ENDED_ERROR_CODE, DRAFT_HEADER, DRAFT_SEARCH_PARAM, FINDINGS_OPEN_ERROR, LIVE_READ_ONLY_ERROR, LOCALE_HEADER, LOOK_BLOCK_DEFINITION_PARAM, LOOK_CONTENT_PARAM, LOOK_DOCUMENT_PATH, LOOK_LOCALE_PARAM, LOOK_PATH_PARAM, LOOK_SETTINGS_PARAM, LOOK_TOKEN_COOKIE, LOOK_TOKEN_HEADER, NOT_PUBLISHED_CODES, PUBLISHING_DRAFT_HEADER, PUBLISHING_DRAFT_VAR, PUBLISH_ABORT_REASONS, PUBLISH_BLOCKED_ERROR, REVISION_CHANGED_ERROR, TRANSLATIONS_PENDING_ERROR, UNPLACED_BLOCK_ID, isContentWrite };
239
+ export { CAMOX_ACTOR, CAMOX_NAME, CHECKS_RUNNING_ERROR, CONFLICTS_CHANGED_ERROR, DRAFT_CREATE_HEADER, DRAFT_ENDED_ERROR_CODE, DRAFT_HEADER, DRAFT_SEARCH_PARAM, FINDINGS_OPEN_ERROR, LIVE_READ_ONLY_ERROR, LOCALE_HEADER, LOOK_BLOCKS_PARAM, LOOK_BLOCK_DEFINITION_PARAM, LOOK_CONTENT_PARAM, LOOK_DOCUMENT_PATH, LOOK_LAYOUT_DEFINITION_PARAM, LOOK_LOCALE_PARAM, LOOK_PATH_PARAM, LOOK_PLACEHOLDER_HEIGHTS, LOOK_PLACEHOLDER_PARAM, LOOK_SETTINGS_PARAM, LOOK_TOKEN_COOKIE, LOOK_TOKEN_HEADER, NOT_PUBLISHED_CODES, PUBLISHING_DRAFT_HEADER, PUBLISHING_DRAFT_VAR, PUBLISH_ABORT_REASONS, PUBLISH_BLOCKED_ERROR, REVISION_CHANGED_ERROR, TRANSLATIONS_PENDING_ERROR, UNPLACED_BLOCK_ID, isContentWrite };
package/dist/patrol.d.ts CHANGED
@@ -5,14 +5,14 @@
5
5
  * Live, and names each report's subject, so the studio needs no entity loaded to show it.
6
6
  */
7
7
  /** Every patroller's stable id. The registry in the API lists them all. */
8
- export declare const PATROLLER_IDS: readonly ["translator", "seoManager", "linkChecker", "proofreader"];
8
+ export declare const PATROLLER_IDS: readonly ["translator", "seoManager", "linkChecker"];
9
9
  /** A patroller's stable id. */
10
10
  export type PatrollerId = (typeof PATROLLER_IDS)[number];
11
11
  /**
12
12
  * What a structured report says was done. Each has a failed form, by the report's outcome. A
13
13
  * merge with Live's localization reads as a translation.
14
14
  */
15
- export type ReportVerb = "translated" | "mergedWithLive" | "wroteSeo" | "describedFile" | "publishedCatchUp" | "removedLink" | "fixedLink" | "fixedMistake";
15
+ export type ReportVerb = "translated" | "mergedWithLive" | "wroteSeo" | "describedFile" | "publishedCatchUp" | "removedLink" | "fixedLink";
16
16
  /** What a report can be about. */
17
17
  export type ReportEntity = "page" | "block" | "sharedContent" | "repeatableItem" | "record" | "file";
18
18
  /** A report's subject, named as the Canvas names it, read when the Patrol is. */
@@ -84,7 +84,6 @@ export type ReportView = {
84
84
  * What a finding is about (ADR 0034), by its kind:
85
85
  * - `missingPage`: a link to a page that doesn't exist, the page's name as it was last known.
86
86
  * - `brokenLink`: an external link that failed, and how.
87
- * - `misspelling`, `grammar`: the flagged text, in the sentence it's in.
88
87
  * A link's `url` is as content stores it: a page link's is `camox:page:<id>`, and `text` is what
89
88
  * the link reads.
90
89
  */
@@ -98,10 +97,6 @@ export type FindingAbout = {
98
97
  url: string;
99
98
  text: string;
100
99
  failure: LinkFailure;
101
- } | {
102
- kind: "misspelling" | "grammar";
103
- text: string;
104
- sentence: string;
105
100
  };
106
101
  /** How a link failed: an HTTP error status, no connection (DNS included), or no answer in time. */
107
102
  export type LinkFailure = {
@@ -113,15 +108,9 @@ export type LinkFailure = {
113
108
  kind: "timeout";
114
109
  };
115
110
  export type FindingKind = FindingAbout["kind"];
116
- /**
117
- * The fix a finding suggests, which applying makes: removing the link, keeping its text, or
118
- * replacing the flagged text with `replacement` in its sentence, keeping its formatting.
119
- */
111
+ /** The fix a finding suggests, which applying makes: removing the link, keeping its text. */
120
112
  export type FindingFix = {
121
113
  kind: "removeLink";
122
- } | {
123
- kind: "replaceText";
124
- replacement: string;
125
114
  };
126
115
  /** A finding, open, in the scope viewed. */
127
116
  export type FindingView = {
@@ -136,6 +125,8 @@ export type FindingView = {
136
125
  locale: string;
137
126
  /** The content field it's in, by name. */
138
127
  field: string;
128
+ /** That field's kind, such as "String" or "Link", or null when unknown. */
129
+ fieldKind: string | null;
139
130
  /** When it was first raised, in milliseconds since the epoch. */
140
131
  raisedAt: number;
141
132
  /** Whether it's Live's. Live's are fixed in the Patrol Draft. */
@@ -148,6 +139,11 @@ export type FindingView = {
148
139
  * Footer links to About, a page that no longer exists".
149
140
  */
150
141
  export declare function findingSentence(finding: Pick<FindingView, "about" | "subject">): string;
142
+ /**
143
+ * A finding as one sentence where its content is already shown, as the studio's sidebar shows it
144
+ * by its field: "“About us” links to About, a page that no longer exists".
145
+ */
146
+ export declare function findingSentenceInPlace(finding: Pick<FindingView, "about">): string;
151
147
  /** A member's dismissal of what a finding was about: no finding like it is raised again. */
152
148
  export type DismissalView = {
153
149
  id: number;
package/dist/patrol.js CHANGED
@@ -8,8 +8,7 @@
8
8
  const PATROLLER_IDS = [
9
9
  "translator",
10
10
  "seoManager",
11
- "linkChecker",
12
- "proofreader"
11
+ "linkChecker"
13
12
  ];
14
13
  /** The locale a summary's work was in: a structured summary's, if any. A text summary has none. */
15
14
  const reportLocale = (summary) => summary.kind === "structured" ? summary.locale : null;
@@ -22,8 +21,6 @@ function aboutSentence(about) {
22
21
  const how = failure.kind === "status" ? `answers with an error (${failure.status})` : failure.kind === "timeout" ? "doesn't answer in time" : "can't be reached";
23
22
  return `links to ${about.url}, which ${how}`;
24
23
  }
25
- case "misspelling": return "may be misspelled";
26
- case "grammar": return "may be a grammar mistake";
27
24
  }
28
25
  }
29
26
  /**
@@ -33,8 +30,15 @@ function aboutSentence(about) {
33
30
  function findingSentence(finding) {
34
31
  return `“${finding.about.text}” in ${finding.subject.name} ${aboutSentence(finding.about)}`;
35
32
  }
33
+ /**
34
+ * A finding as one sentence where its content is already shown, as the studio's sidebar shows it
35
+ * by its field: "“About us” links to About, a page that no longer exists".
36
+ */
37
+ function findingSentenceInPlace(finding) {
38
+ return `“${finding.about.text}” ${aboutSentence(finding.about)}`;
39
+ }
36
40
  /** How many reports the Patrol shows of each patroller; the rest are counted. */
37
41
  const REPORTS_SHOWN = 5;
38
42
 
39
43
  //#endregion
40
- export { PATROLLER_IDS, REPORTS_SHOWN, findingSentence, reportLocale };
44
+ export { PATROLLER_IDS, REPORTS_SHOWN, findingSentence, findingSentenceInPlace, reportLocale };
package/dist/router.d.ts CHANGED
@@ -9967,9 +9967,9 @@ type CheckJob = {
9967
9967
  };
9968
9968
  /**
9969
9969
  * A run of patrollers' patrols of a project's Live content (ADR 0034), keyed by project: the cron
9970
- * trigger requests one of every patroller patrolling Live for every project every 6 hours, and a
9971
- * release no draft published requests the Proofreader's. Requesting one while another is pending
9972
- * joins it: one run patrols for both, so its new findings are told of once.
9970
+ * trigger requests one of every patroller patrolling Live for every project every 6 hours.
9971
+ * Requesting one while another is pending joins it: one run patrols for both, so its new findings
9972
+ * are told of once.
9973
9973
  */
9974
9974
  type PatrolJob = {
9975
9975
  kind: "patrol";
@@ -14514,6 +14514,177 @@ declare const router: {
14514
14514
  structuredData: BlockStructuredData;
14515
14515
  recordStructuredData: import("@camox/api-contract").RecordStructuredData;
14516
14516
  }>, Record<never, never>, Record<never, never>>;
14517
+ getUnplaced: import("@orpc/server").DecoratedProcedure<import("@orpc/server").MergedInitialContext<import("@orpc/server").MergedInitialContext<BaseContext & Record<never, never>, BaseContext, BaseContext>, import("@orpc/server").MergedCurrentContext<BaseContext, Record<never, never>>, import("@orpc/server").MergedCurrentContext<BaseContext, Record<never, never>>>, import("@orpc/server").MergedCurrentContext<import("@orpc/server").MergedCurrentContext<BaseContext, Record<never, never>>, {
14518
+ draft: DraftRequest | null;
14519
+ locale: string | undefined;
14520
+ publishingDraft: string | undefined;
14521
+ look: LookGrant | undefined;
14522
+ }>, import("zod").ZodObject<{
14523
+ projectUuid: import("zod").ZodUUID;
14524
+ layoutId: import("zod").ZodString;
14525
+ blocks: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
14526
+ content: import("zod").ZodOptional<import("zod").ZodUnknown>;
14527
+ settings: import("zod").ZodOptional<import("zod").ZodUnknown>;
14528
+ }, import("zod/v4/core").$strip>>>;
14529
+ }, import("zod/v4/core").$strip>, import("@orpc/server").Schema<{
14530
+ projectName: string;
14531
+ project: {
14532
+ id: number;
14533
+ updatedAt: number;
14534
+ analytics: AnalyticsProviders;
14535
+ socialProfiles: string[];
14536
+ };
14537
+ layout: {
14538
+ layoutId: string;
14539
+ contentUpdatedAt: number;
14540
+ updatedAt: number;
14541
+ beforeBlockIds: string[];
14542
+ afterBlockIds: string[];
14543
+ };
14544
+ blocks: (Omit<{
14545
+ baseVersion: number | null;
14546
+ content: unknown;
14547
+ createdAt: number;
14548
+ deleted: boolean;
14549
+ draftId: string | null;
14550
+ firstPublishedAt: number | null;
14551
+ id: string;
14552
+ layoutId: string | null;
14553
+ locale: string;
14554
+ pageId: string | null;
14555
+ position: string;
14556
+ projectId: number;
14557
+ publishedAt: number | null;
14558
+ publishedBy: string | null;
14559
+ rowId: number;
14560
+ settings: unknown;
14561
+ slot: "after" | "before" | null;
14562
+ type: string;
14563
+ updatedAt: number;
14564
+ version: number;
14565
+ }, "content"> & {
14566
+ content: Record<string, unknown>;
14567
+ } & {
14568
+ summary: string;
14569
+ title: string;
14570
+ artifactHashes: Record<string, string>;
14571
+ } & {
14572
+ references: ResolvedReferences;
14573
+ })[];
14574
+ repeatableItems: (Omit<{
14575
+ baseVersion: number | null;
14576
+ blockId: string | null;
14577
+ content: unknown;
14578
+ createdAt: number;
14579
+ deleted: boolean;
14580
+ draftId: string | null;
14581
+ fieldName: string;
14582
+ firstPublishedAt: number | null;
14583
+ id: string;
14584
+ locale: string;
14585
+ parentItemId: string | null;
14586
+ position: string;
14587
+ projectId: number | null;
14588
+ publishedAt: number | null;
14589
+ publishedBy: string | null;
14590
+ rowId: number;
14591
+ settings: unknown;
14592
+ sharedContentType: string | null;
14593
+ updatedAt: number;
14594
+ version: number;
14595
+ }, "content"> & {
14596
+ content: Record<string, unknown>;
14597
+ } & {
14598
+ blockId: string;
14599
+ summary: string;
14600
+ title: string;
14601
+ artifactHashes: Record<string, string>;
14602
+ } & {
14603
+ references: ResolvedReferences;
14604
+ })[];
14605
+ files: FileView[];
14606
+ structuredData: BlockStructuredData;
14607
+ recordStructuredData: import("@camox/api-contract").RecordStructuredData;
14608
+ }, {
14609
+ projectName: string;
14610
+ project: {
14611
+ id: number;
14612
+ updatedAt: number;
14613
+ analytics: AnalyticsProviders;
14614
+ socialProfiles: string[];
14615
+ };
14616
+ layout: {
14617
+ layoutId: string;
14618
+ contentUpdatedAt: number;
14619
+ updatedAt: number;
14620
+ beforeBlockIds: string[];
14621
+ afterBlockIds: string[];
14622
+ };
14623
+ blocks: (Omit<{
14624
+ baseVersion: number | null;
14625
+ content: unknown;
14626
+ createdAt: number;
14627
+ deleted: boolean;
14628
+ draftId: string | null;
14629
+ firstPublishedAt: number | null;
14630
+ id: string;
14631
+ layoutId: string | null;
14632
+ locale: string;
14633
+ pageId: string | null;
14634
+ position: string;
14635
+ projectId: number;
14636
+ publishedAt: number | null;
14637
+ publishedBy: string | null;
14638
+ rowId: number;
14639
+ settings: unknown;
14640
+ slot: "after" | "before" | null;
14641
+ type: string;
14642
+ updatedAt: number;
14643
+ version: number;
14644
+ }, "content"> & {
14645
+ content: Record<string, unknown>;
14646
+ } & {
14647
+ summary: string;
14648
+ title: string;
14649
+ artifactHashes: Record<string, string>;
14650
+ } & {
14651
+ references: ResolvedReferences;
14652
+ })[];
14653
+ repeatableItems: (Omit<{
14654
+ baseVersion: number | null;
14655
+ blockId: string | null;
14656
+ content: unknown;
14657
+ createdAt: number;
14658
+ deleted: boolean;
14659
+ draftId: string | null;
14660
+ fieldName: string;
14661
+ firstPublishedAt: number | null;
14662
+ id: string;
14663
+ locale: string;
14664
+ parentItemId: string | null;
14665
+ position: string;
14666
+ projectId: number | null;
14667
+ publishedAt: number | null;
14668
+ publishedBy: string | null;
14669
+ rowId: number;
14670
+ settings: unknown;
14671
+ sharedContentType: string | null;
14672
+ updatedAt: number;
14673
+ version: number;
14674
+ }, "content"> & {
14675
+ content: Record<string, unknown>;
14676
+ } & {
14677
+ blockId: string;
14678
+ summary: string;
14679
+ title: string;
14680
+ artifactHashes: Record<string, string>;
14681
+ } & {
14682
+ references: ResolvedReferences;
14683
+ })[];
14684
+ files: FileView[];
14685
+ structuredData: BlockStructuredData;
14686
+ recordStructuredData: import("@camox/api-contract").RecordStructuredData;
14687
+ }>, Record<never, never>, Record<never, never>>;
14517
14688
  list: import("@orpc/server").DecoratedProcedure<import("@orpc/server").MergedInitialContext<import("@orpc/server").MergedInitialContext<BaseContext & Record<never, never>, BaseContext, BaseContext>, import("@orpc/server").MergedCurrentContext<BaseContext, Record<never, never>>, import("@orpc/server").MergedCurrentContext<BaseContext, Record<never, never>>>, import("@orpc/server").MergedCurrentContext<import("@orpc/server").MergedCurrentContext<BaseContext, Record<never, never>>, {
14518
14689
  draft: DraftRequest | null;
14519
14690
  locale: string | undefined;
@@ -18124,16 +18295,17 @@ declare const router: {
18124
18295
  subject: import("@camox/api-contract/patrol").ReportSubject;
18125
18296
  locale: string;
18126
18297
  field: string;
18298
+ fieldKind: string | null;
18127
18299
  raisedAt: number;
18128
18300
  live: boolean;
18129
18301
  place: import("@camox/api-contract/patrol").ReportPlace | null;
18130
18302
  patroller: {
18131
- id: "linkChecker" | "proofreader" | "seoManager" | "translator";
18303
+ id: "linkChecker" | "seoManager" | "translator";
18132
18304
  role: string;
18133
18305
  };
18134
18306
  }[];
18135
18307
  checking: {
18136
- id: "linkChecker" | "proofreader" | "seoManager" | "translator";
18308
+ id: "linkChecker" | "seoManager" | "translator";
18137
18309
  role: string;
18138
18310
  }[];
18139
18311
  defaultLocale: string | null;
@@ -18166,16 +18338,17 @@ declare const router: {
18166
18338
  subject: import("@camox/api-contract/patrol").ReportSubject;
18167
18339
  locale: string;
18168
18340
  field: string;
18341
+ fieldKind: string | null;
18169
18342
  raisedAt: number;
18170
18343
  live: boolean;
18171
18344
  place: import("@camox/api-contract/patrol").ReportPlace | null;
18172
18345
  patroller: {
18173
- id: "linkChecker" | "proofreader" | "seoManager" | "translator";
18346
+ id: "linkChecker" | "seoManager" | "translator";
18174
18347
  role: string;
18175
18348
  };
18176
18349
  }[];
18177
18350
  checking: {
18178
- id: "linkChecker" | "proofreader" | "seoManager" | "translator";
18351
+ id: "linkChecker" | "seoManager" | "translator";
18179
18352
  role: string;
18180
18353
  }[];
18181
18354
  defaultLocale: string | null;
package/dist/threads.d.ts CHANGED
@@ -91,6 +91,26 @@ export declare const artifactRequestDataSchema: z.ZodObject<{
91
91
  operationIds: z.ZodArray<z.ZodString>;
92
92
  }, z.core.$strip>;
93
93
  export type ArtifactRequestData = z.infer<typeof artifactRequestDataSchema>;
94
+ /**
95
+ * The agent's tool handing code work to the draft thread's Code Agent, which reads and writes the
96
+ * website's code in a conversation of its own and answers the agent.
97
+ */
98
+ export declare const CODE_TOOL = "code";
99
+ /**
100
+ * The kind of the entry recording a request the agent sent the Code Agent, in the Code Agent's
101
+ * own conversation, which editors never see: who the run acts for, the operation ids of its
102
+ * messages, and whether it's Camox's reconcile turn, as data (`codeRequestDataSchema`).
103
+ */
104
+ export declare const CODE_REQUEST_ENTRY = "camox.code-request";
105
+ /** What a Code Agent's request records: as an Artifact Agent's, and whether Camox reconciles. */
106
+ export declare const codeRequestDataSchema: z.ZodObject<{
107
+ member: z.ZodString;
108
+ operationIds: z.ZodArray<z.ZodString>;
109
+ reconciling: z.ZodOptional<z.ZodBoolean>;
110
+ }, z.core.$strip>;
111
+ export type CodeRequestData = z.infer<typeof codeRequestDataSchema>;
112
+ /** Whether `name` is a tool handing work to an agent of its own (`HANDING_OFF_TOOLS`). */
113
+ export declare const handsOff: (name: string) => boolean;
94
114
  /**
95
115
  * The tools whose calls change a draft: the content tools that write, and the code tools that
96
116
  * write files. The model calls the code tools, and code mode scripts call both. A test in the API
@@ -375,11 +395,11 @@ export declare const agentActivitySchema: z.ZodEnum<{
375
395
  }>;
376
396
  export type AgentActivity = z.infer<typeof agentActivitySchema>;
377
397
  /**
378
- * What an `artifact` call's result carries for the studio: the Artifact Agent's answer, whether
379
- * it's the agent's answer to the turn too, which then ends on it, and while it works, what the
380
- * Artifact Agent is doing.
398
+ * What the result of a call handing work off (`handsOff`), such as `artifact` or `code`, carries
399
+ * for the studio: the other agent's answer, whether it's the agent's answer to the turn too,
400
+ * which then ends on it, and while it works, what the other agent is doing.
381
401
  */
382
- export declare const artifactDetailsSchema: z.ZodObject<{
402
+ export declare const handOffDetailsSchema: z.ZodObject<{
383
403
  answer: z.ZodOptional<z.ZodString>;
384
404
  final: z.ZodOptional<z.ZodBoolean>;
385
405
  activity: z.ZodOptional<z.ZodEnum<{
@@ -392,7 +412,7 @@ export declare const artifactDetailsSchema: z.ZodObject<{
392
412
  thinking: "thinking";
393
413
  }>>;
394
414
  }, z.core.$strip>;
395
- export type ArtifactDetails = z.infer<typeof artifactDetailsSchema>;
415
+ export type HandOffDetails = z.infer<typeof handOffDetailsSchema>;
396
416
  /** Everything the studio shows for a thread. */
397
417
  export type ThreadView = {
398
418
  messages: readonly ThreadMessage[];
@@ -424,8 +444,11 @@ export type ThreadState = {
424
444
  changing?: boolean;
425
445
  /** Whether the model's latest round calls `look`: from writing the call until it's called again. */
426
446
  checking?: boolean;
427
- /** What the Artifact Agent the running `artifact` call waits for is doing, as it last said. */
428
- artifactActivity?: AgentActivity;
447
+ /**
448
+ * What the agent a running call handing work off (`handsOff`) waits for is doing, as it last
449
+ * said.
450
+ */
451
+ handedOffActivity?: AgentActivity;
429
452
  /** The drafts the thread's cards name, as the thread last looked them up. */
430
453
  drafts?: DraftCardStates;
431
454
  };
@@ -614,7 +637,7 @@ export declare const editorTextOf: (content: Message["content"]) => string;
614
637
  * run is over, or a later message started another run after its last model call came back
615
638
  * unfinished. While Pi retries a failed call, the run goes on, so the turn shows as working.
616
639
  */
617
- export declare function threadViewOf({ steps, running, toolCalls, callingTools, changing, checking, artifactActivity, drafts }: ThreadState): ThreadView;
640
+ export declare function threadViewOf({ steps, running, toolCalls, callingTools, changing, checking, handedOffActivity, drafts }: ThreadState): ThreadView;
618
641
  /** Folds one of Pi's agent events into the thread's state. */
619
642
  export declare function reduceThreadState(state: ThreadState, event: AgentEvent): ThreadState;
620
643
  /** Folds a batch of Pi's agent events into the thread's state. */
package/dist/threads.js CHANGED
@@ -88,6 +88,26 @@ const artifactRequestDataSchema = z.object({
88
88
  operationIds: z.array(z.string())
89
89
  });
90
90
  /**
91
+ * The agent's tool handing code work to the draft thread's Code Agent, which reads and writes the
92
+ * website's code in a conversation of its own and answers the agent.
93
+ */
94
+ const CODE_TOOL = "code";
95
+ /**
96
+ * The kind of the entry recording a request the agent sent the Code Agent, in the Code Agent's
97
+ * own conversation, which editors never see: who the run acts for, the operation ids of its
98
+ * messages, and whether it's Camox's reconcile turn, as data (`codeRequestDataSchema`).
99
+ */
100
+ const CODE_REQUEST_ENTRY = "camox.code-request";
101
+ /** What a Code Agent's request records: as an Artifact Agent's, and whether Camox reconciles. */
102
+ const codeRequestDataSchema = artifactRequestDataSchema.extend({ reconciling: z.boolean().optional() });
103
+ /**
104
+ * The agent's tools handing work to an agent of its own, in a conversation beside the thread's,
105
+ * whose answer they return: editors see what that agent does as the agent's.
106
+ */
107
+ const HANDING_OFF_TOOLS = /* @__PURE__ */ new Set([ARTIFACT_TOOL, CODE_TOOL]);
108
+ /** Whether `name` is a tool handing work to an agent of its own (`HANDING_OFF_TOOLS`). */
109
+ const handsOff = (name) => HANDING_OFF_TOOLS.has(name);
110
+ /**
91
111
  * The tools whose calls change a draft: the content tools that write, and the code tools that
92
112
  * write files. The model calls the code tools, and code mode scripts call both. A test in the API
93
113
  * keeps this in step with the tools themselves.
@@ -232,11 +252,11 @@ const agentActivitySchema = z.enum([
232
252
  "startingDraft"
233
253
  ]);
234
254
  /**
235
- * What an `artifact` call's result carries for the studio: the Artifact Agent's answer, whether
236
- * it's the agent's answer to the turn too, which then ends on it, and while it works, what the
237
- * Artifact Agent is doing.
255
+ * What the result of a call handing work off (`handsOff`), such as `artifact` or `code`, carries
256
+ * for the studio: the other agent's answer, whether it's the agent's answer to the turn too,
257
+ * which then ends on it, and while it works, what the other agent is doing.
238
258
  */
239
- const artifactDetailsSchema = z.object({
259
+ const handOffDetailsSchema = z.object({
240
260
  answer: z.string().optional(),
241
261
  final: z.boolean().optional(),
242
262
  activity: agentActivitySchema.optional()
@@ -405,8 +425,8 @@ function threadStepOf(entry) {
405
425
  }
406
426
  if (message.role === "assistant") return assistantStep(id, message);
407
427
  if (message.role === "toolResult") {
408
- if (message.toolName === "artifact" && !message.isError) {
409
- const details = artifactDetailsSchema.safeParse(message.details).data;
428
+ if (handsOff(message.toolName) && !message.isError) {
429
+ const details = handOffDetailsSchema.safeParse(message.details).data;
410
430
  if (details?.final && details.answer) return {
411
431
  id,
412
432
  type: "answer",
@@ -433,7 +453,7 @@ function threadStepOf(entry) {
433
453
  * run is over, or a later message started another run after its last model call came back
434
454
  * unfinished. While Pi retries a failed call, the run goes on, so the turn shows as working.
435
455
  */
436
- function threadViewOf({ steps, running, toolCalls = [], callingTools = false, changing = false, checking = false, artifactActivity, drafts = {} }) {
456
+ function threadViewOf({ steps, running, toolCalls = [], callingTools = false, changing = false, checking = false, handedOffActivity, drafts = {} }) {
437
457
  const messages = [];
438
458
  let waiting;
439
459
  let unfinished = false;
@@ -545,6 +565,7 @@ function threadViewOf({ steps, running, toolCalls = [], callingTools = false, ch
545
565
  break;
546
566
  case "tools":
547
567
  unfinished = false;
568
+ status = void 0;
548
569
  break;
549
570
  case "unfinished": unfinished = true;
550
571
  }
@@ -562,17 +583,17 @@ function threadViewOf({ steps, running, toolCalls = [], callingTools = false, ch
562
583
  callingTools,
563
584
  changing,
564
585
  checking,
565
- artifactActivity
586
+ handedOffActivity
566
587
  })
567
588
  };
568
589
  }
569
590
  /** What the agent is doing during a run, from where the turn's build stands and its tool calls. */
570
- function activityOf({ status, toolCalls, callingTools, changing, checking, artifactActivity }) {
591
+ function activityOf({ status, toolCalls, callingTools, changing, checking, handedOffActivity }) {
571
592
  const calling = (name) => toolCalls.some((call) => call.name === name);
572
593
  if (status === "building" || calling("build")) return "building";
573
- if (calling("artifact")) return artifactActivity ?? "changing";
574
- if (checking) return "checking";
575
594
  if (status === "fixing") return "fixing";
595
+ if (toolCalls.some((call) => handsOff(call.name))) return handedOffActivity ?? "changing";
596
+ if (checking) return "checking";
576
597
  if (calling("startDraft")) return "startingDraft";
577
598
  if (changing) return "changing";
578
599
  if (callingTools || toolCalls.length > 0) return "exploring";
@@ -615,11 +636,11 @@ function reduceThreadState(state, event) {
615
636
  name: tool.name
616
637
  }]);
617
638
  const generated = event.generation?.message?.content ?? [];
618
- const artifact = event.tools.find((tool) => tool.name === "artifact" && tool.status !== "done");
639
+ const handingOff = event.tools.find((tool) => handsOff(tool.name) && tool.status !== "done");
619
640
  return {
620
641
  ...state,
621
642
  steps: event.entries.flatMap((entry) => threadStepOf(entry) ?? []),
622
- artifactActivity: artifactDetailsSchema.safeParse(artifact?.details).data?.activity,
643
+ handedOffActivity: handOffDetailsSchema.safeParse(handingOff?.details).data?.activity,
623
644
  running: event.run !== void 0,
624
645
  toolCalls,
625
646
  callingTools: generated.some((part) => part.type === "toolCall"),
@@ -638,7 +659,7 @@ function reduceThreadState(state, event) {
638
659
  callingTools: false,
639
660
  changing: false,
640
661
  checking: false,
641
- artifactActivity: void 0
662
+ handedOffActivity: void 0
642
663
  };
643
664
  case "message_start": return event.message.role === "assistant" ? {
644
665
  ...state,
@@ -673,17 +694,17 @@ function reduceThreadState(state, event) {
673
694
  };
674
695
  }
675
696
  case "tool_execution_update": {
676
- if (event.toolName !== "artifact" || event.parentToolCallId !== void 0) return state;
677
- const activity = artifactDetailsSchema.safeParse(event.details).data?.activity;
697
+ if (!handsOff(event.toolName) || event.parentToolCallId !== void 0) return state;
698
+ const activity = handOffDetailsSchema.safeParse(event.details).data?.activity;
678
699
  return activity ? {
679
700
  ...state,
680
- artifactActivity: activity
701
+ handedOffActivity: activity
681
702
  } : state;
682
703
  }
683
704
  case "tool_execution_end": return append({
684
705
  ...state,
685
706
  toolCalls: state.toolCalls?.filter((call) => call.callId !== event.toolCallId),
686
- ...event.toolName === "artifact" && event.parentToolCallId === void 0 && { artifactActivity: void 0 }
707
+ ...handsOff(event.toolName) && event.parentToolCallId === void 0 && { handedOffActivity: void 0 }
687
708
  }, event.entry);
688
709
  case "message_end":
689
710
  case "entry_appended": return append(state, event.entry);
@@ -705,4 +726,4 @@ function reduceThreadFrame(state, frame) {
705
726
  }
706
727
 
707
728
  //#endregion
708
- export { ARTIFACT_REQUEST_ENTRY, ARTIFACT_TOOL, BRIEF_ENTRY, BUILD_STATUS_ENTRY, BUILD_TOOL, CHANGING_TOOLS, CONNECTED_AGENT_MESSAGE_ENTRY, DRAFT_CARD_ENTRY, DRAFT_ENDED_ENTRY, EDITOR_MESSAGE_ENTRY, EMPTY_THREAD_STATE, EMPTY_THREAD_VIEW, LOOK_TOOL, PUSH_ENTRY, RECONCILE_ENTRY, RUN_STOPPED_ENTRY, START_DRAFT_TOOL, THREAD_ENDED_CLOSE_CODE, agentActivitySchema, artifactDetailsSchema, artifactRequestDataSchema, buildStatusDataSchema, buildStatusSchema, connectedAgentClientSchema, connectedAgentMessageDataSchema, editorMessageDataSchema, editorSelectionSchema, editorTextOf, editorWhereSchema, isSender, pushDataSchema, pushOutcomeSchema, reconcileDataSchema, reduceThreadEvents, reduceThreadFrame, reduceThreadState, startedDraftCardSchema, startedDraftSchema, textOf, threadClientMessageSchema, threadSenderSchema, threadViewOf, touchKindSchema, touchSchema, touchedBatchSchema };
729
+ export { ARTIFACT_REQUEST_ENTRY, ARTIFACT_TOOL, BRIEF_ENTRY, BUILD_STATUS_ENTRY, BUILD_TOOL, CHANGING_TOOLS, CODE_REQUEST_ENTRY, CODE_TOOL, CONNECTED_AGENT_MESSAGE_ENTRY, DRAFT_CARD_ENTRY, DRAFT_ENDED_ENTRY, EDITOR_MESSAGE_ENTRY, EMPTY_THREAD_STATE, EMPTY_THREAD_VIEW, LOOK_TOOL, PUSH_ENTRY, RECONCILE_ENTRY, RUN_STOPPED_ENTRY, START_DRAFT_TOOL, THREAD_ENDED_CLOSE_CODE, agentActivitySchema, artifactRequestDataSchema, buildStatusDataSchema, buildStatusSchema, codeRequestDataSchema, connectedAgentClientSchema, connectedAgentMessageDataSchema, editorMessageDataSchema, editorSelectionSchema, editorTextOf, editorWhereSchema, handOffDetailsSchema, handsOff, isSender, pushDataSchema, pushOutcomeSchema, reconcileDataSchema, reduceThreadEvents, reduceThreadFrame, reduceThreadState, startedDraftCardSchema, startedDraftSchema, textOf, threadClientMessageSchema, threadSenderSchema, threadViewOf, touchKindSchema, touchSchema, touchedBatchSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camox/api-contract",
3
- "version": "0.66.0",
3
+ "version": "0.67.0",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "src",
package/src/drafts.ts CHANGED
@@ -58,6 +58,21 @@ export const LOOK_BLOCK_DEFINITION_PARAM = "blockDefinition";
58
58
  export const LOOK_CONTENT_PARAM = "content";
59
59
  export const LOOK_SETTINGS_PARAM = "settings";
60
60
 
61
+ /**
62
+ * The look document's Layout Definition mode: instead of a page, the curated Layout
63
+ * `LOOK_LAYOUT_DEFINITION_PARAM` names, alone in the site's shell, its blocks with their default
64
+ * content and Block Settings and the overrides `LOOK_BLOCKS_PARAM` gives as JSON, by Block
65
+ * Definition name (`{ navbar: { content, settings } }`), around a labelled placeholder where a
66
+ * page's content goes, as tall as `LOOK_PLACEHOLDER_PARAM` says: one of `LOOK_PLACEHOLDER_HEIGHTS`,
67
+ * the first without.
68
+ */
69
+ export const LOOK_LAYOUT_DEFINITION_PARAM = "layoutDefinition";
70
+ export const LOOK_BLOCKS_PARAM = "blocks";
71
+ export const LOOK_PLACEHOLDER_PARAM = "placeholder";
72
+ /** A short placeholder shows whether the footer rises up the screen; a tall one, scrolling. */
73
+ export const LOOK_PLACEHOLDER_HEIGHTS = ["short", "tall"] as const;
74
+ export type LookPlaceholderHeight = (typeof LOOK_PLACEHOLDER_HEIGHTS)[number];
75
+
61
76
  /**
62
77
  * The id of a block shown without being placed, as the look document's Block Definition mode
63
78
  * shows it: never a stored block's, which are UUIDs. Its element is
package/src/patrol.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
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;
8
+ export const PATROLLER_IDS = ["translator", "seoManager", "linkChecker"] as const;
9
9
 
10
10
  /** A patroller's stable id. */
11
11
  export type PatrollerId = (typeof PATROLLER_IDS)[number];
@@ -21,8 +21,7 @@ export type ReportVerb =
21
21
  | "describedFile"
22
22
  | "publishedCatchUp"
23
23
  | "removedLink"
24
- | "fixedLink"
25
- | "fixedMistake";
24
+ | "fixedLink";
26
25
 
27
26
  /** What a report can be about. */
28
27
  export type ReportEntity =
@@ -92,14 +91,12 @@ export type ReportView = {
92
91
  * What a finding is about (ADR 0034), by its kind:
93
92
  * - `missingPage`: a link to a page that doesn't exist, the page's name as it was last known.
94
93
  * - `brokenLink`: an external link that failed, and how.
95
- * - `misspelling`, `grammar`: the flagged text, in the sentence it's in.
96
94
  * A link's `url` is as content stores it: a page link's is `camox:page:<id>`, and `text` is what
97
95
  * the link reads.
98
96
  */
99
97
  export type FindingAbout =
100
98
  | { 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 };
99
+ | { kind: "brokenLink"; url: string; text: string; failure: LinkFailure };
103
100
 
104
101
  /** How a link failed: an HTTP error status, no connection (DNS included), or no answer in time. */
105
102
  export type LinkFailure =
@@ -109,11 +106,8 @@ export type LinkFailure =
109
106
 
110
107
  export type FindingKind = FindingAbout["kind"];
111
108
 
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 };
109
+ /** The fix a finding suggests, which applying makes: removing the link, keeping its text. */
110
+ export type FindingFix = { kind: "removeLink" };
117
111
 
118
112
  /** A finding, open, in the scope viewed. */
119
113
  export type FindingView = {
@@ -128,6 +122,8 @@ export type FindingView = {
128
122
  locale: string;
129
123
  /** The content field it's in, by name. */
130
124
  field: string;
125
+ /** That field's kind, such as "String" or "Link", or null when unknown. */
126
+ fieldKind: string | null;
131
127
  /** When it was first raised, in milliseconds since the epoch. */
132
128
  raisedAt: number;
133
129
  /** Whether it's Live's. Live's are fixed in the Patrol Draft. */
@@ -153,10 +149,6 @@ function aboutSentence(about: FindingAbout): string {
153
149
  : "can't be reached";
154
150
  return `links to ${about.url}, which ${how}`;
155
151
  }
156
- case "misspelling":
157
- return "may be misspelled";
158
- case "grammar":
159
- return "may be a grammar mistake";
160
152
  }
161
153
  }
162
154
 
@@ -168,6 +160,14 @@ export function findingSentence(finding: Pick<FindingView, "about" | "subject">)
168
160
  return `“${finding.about.text}” in ${finding.subject.name} ${aboutSentence(finding.about)}`;
169
161
  }
170
162
 
163
+ /**
164
+ * A finding as one sentence where its content is already shown, as the studio's sidebar shows it
165
+ * by its field: "“About us” links to About, a page that no longer exists".
166
+ */
167
+ export function findingSentenceInPlace(finding: Pick<FindingView, "about">): string {
168
+ return `“${finding.about.text}” ${aboutSentence(finding.about)}`;
169
+ }
170
+
171
171
  /** A member's dismissal of what a finding was about: no finding like it is raised again. */
172
172
  export type DismissalView = {
173
173
  id: number;
package/src/threads.ts CHANGED
@@ -108,6 +108,34 @@ export const artifactRequestDataSchema = z.object({
108
108
  });
109
109
  export type ArtifactRequestData = z.infer<typeof artifactRequestDataSchema>;
110
110
 
111
+ /**
112
+ * The agent's tool handing code work to the draft thread's Code Agent, which reads and writes the
113
+ * website's code in a conversation of its own and answers the agent.
114
+ */
115
+ export const CODE_TOOL = "code";
116
+
117
+ /**
118
+ * The kind of the entry recording a request the agent sent the Code Agent, in the Code Agent's
119
+ * own conversation, which editors never see: who the run acts for, the operation ids of its
120
+ * messages, and whether it's Camox's reconcile turn, as data (`codeRequestDataSchema`).
121
+ */
122
+ export const CODE_REQUEST_ENTRY = "camox.code-request";
123
+
124
+ /** What a Code Agent's request records: as an Artifact Agent's, and whether Camox reconciles. */
125
+ export const codeRequestDataSchema = artifactRequestDataSchema.extend({
126
+ reconciling: z.boolean().optional(),
127
+ });
128
+ export type CodeRequestData = z.infer<typeof codeRequestDataSchema>;
129
+
130
+ /**
131
+ * The agent's tools handing work to an agent of its own, in a conversation beside the thread's,
132
+ * whose answer they return: editors see what that agent does as the agent's.
133
+ */
134
+ const HANDING_OFF_TOOLS: ReadonlySet<string> = new Set([ARTIFACT_TOOL, CODE_TOOL]);
135
+
136
+ /** Whether `name` is a tool handing work to an agent of its own (`HANDING_OFF_TOOLS`). */
137
+ export const handsOff = (name: string) => HANDING_OFF_TOOLS.has(name);
138
+
111
139
  /**
112
140
  * The tools whose calls change a draft: the content tools that write, and the code tools that
113
141
  * write files. The model calls the code tools, and code mode scripts call both. A test in the API
@@ -320,16 +348,16 @@ export const agentActivitySchema = z.enum([
320
348
  export type AgentActivity = z.infer<typeof agentActivitySchema>;
321
349
 
322
350
  /**
323
- * What an `artifact` call's result carries for the studio: the Artifact Agent's answer, whether
324
- * it's the agent's answer to the turn too, which then ends on it, and while it works, what the
325
- * Artifact Agent is doing.
351
+ * What the result of a call handing work off (`handsOff`), such as `artifact` or `code`, carries
352
+ * for the studio: the other agent's answer, whether it's the agent's answer to the turn too,
353
+ * which then ends on it, and while it works, what the other agent is doing.
326
354
  */
327
- export const artifactDetailsSchema = z.object({
355
+ export const handOffDetailsSchema = z.object({
328
356
  answer: z.string().optional(),
329
357
  final: z.boolean().optional(),
330
358
  activity: agentActivitySchema.optional(),
331
359
  });
332
- export type ArtifactDetails = z.infer<typeof artifactDetailsSchema>;
360
+ export type HandOffDetails = z.infer<typeof handOffDetailsSchema>;
333
361
 
334
362
  /** Everything the studio shows for a thread. */
335
363
  export type ThreadView = {
@@ -361,8 +389,11 @@ export type ThreadState = {
361
389
  changing?: boolean;
362
390
  /** Whether the model's latest round calls `look`: from writing the call until it's called again. */
363
391
  checking?: boolean;
364
- /** What the Artifact Agent the running `artifact` call waits for is doing, as it last said. */
365
- artifactActivity?: AgentActivity;
392
+ /**
393
+ * What the agent a running call handing work off (`handsOff`) waits for is doing, as it last
394
+ * said.
395
+ */
396
+ handedOffActivity?: AgentActivity;
366
397
  /** The drafts the thread's cards name, as the thread last looked them up. */
367
398
  drafts?: DraftCardStates;
368
399
  };
@@ -563,9 +594,10 @@ function threadStepOf(entry: EntryRecord): ThreadStep | undefined {
563
594
  }
564
595
  if (message.role === "assistant") return assistantStep(id, message);
565
596
  if (message.role === "toolResult") {
566
- // An Artifact Agent's answer that ends the turn is the agent's: editors never see it apart.
567
- if (message.toolName === ARTIFACT_TOOL && !message.isError) {
568
- const details = artifactDetailsSchema.safeParse(message.details).data;
597
+ // An Artifact Agent's or the Code Agent's answer that ends the turn is the agent's: editors
598
+ // never see it apart.
599
+ if (handsOff(message.toolName) && !message.isError) {
600
+ const details = handOffDetailsSchema.safeParse(message.details).data;
569
601
  if (details?.final && details.answer) return { id, type: "answer", text: details.answer };
570
602
  }
571
603
  const card =
@@ -593,7 +625,7 @@ export function threadViewOf({
593
625
  callingTools = false,
594
626
  changing = false,
595
627
  checking = false,
596
- artifactActivity,
628
+ handedOffActivity,
597
629
  drafts = {},
598
630
  }: ThreadState): ThreadView {
599
631
  const messages: ThreadMessage[] = [];
@@ -698,6 +730,8 @@ export function threadViewOf({
698
730
  break;
699
731
  case "tools":
700
732
  unfinished = false;
733
+ // The Code Agent's build status ends with its request, whose answer is a tool result.
734
+ status = undefined;
701
735
  break;
702
736
  case "unfinished":
703
737
  unfinished = true;
@@ -709,7 +743,14 @@ export function threadViewOf({
709
743
  return {
710
744
  messages,
711
745
  running,
712
- activity: activityOf({ status, toolCalls, callingTools, changing, checking, artifactActivity }),
746
+ activity: activityOf({
747
+ status,
748
+ toolCalls,
749
+ callingTools,
750
+ changing,
751
+ checking,
752
+ handedOffActivity,
753
+ }),
713
754
  };
714
755
  }
715
756
 
@@ -720,23 +761,24 @@ function activityOf({
720
761
  callingTools,
721
762
  changing,
722
763
  checking,
723
- artifactActivity,
764
+ handedOffActivity,
724
765
  }: {
725
766
  status: BuildStatus | undefined;
726
767
  toolCalls: NonNullable<ThreadState["toolCalls"]>;
727
768
  callingTools: boolean;
728
769
  changing: boolean;
729
770
  checking: boolean;
730
- artifactActivity: AgentActivity | undefined;
771
+ handedOffActivity: AgentActivity | undefined;
731
772
  }): AgentActivity {
732
773
  const calling = (name: string) => toolCalls.some((call) => call.name === name);
733
774
  // Fixing a build error takes thinking, reading and writing, which all read as fixing, but its
734
775
  // builds show as building.
735
776
  if (status === "building" || calling(BUILD_TOOL)) return "building";
736
- // An Artifact Agent works on the agent's behalf: editors see what it does as the agent's.
737
- if (calling(ARTIFACT_TOOL)) return artifactActivity ?? "changing";
738
- if (checking) return "checking";
739
777
  if (status === "fixing") return "fixing";
778
+ // An Artifact Agent or the Code Agent works on the agent's behalf: editors see what it does as
779
+ // the agent's.
780
+ if (toolCalls.some((call) => handsOff(call.name))) return handedOffActivity ?? "changing";
781
+ if (checking) return "checking";
740
782
  if (calling(START_DRAFT_TOOL)) return "startingDraft";
741
783
  if (changing) return "changing";
742
784
  if (callingTools || toolCalls.length > 0) return "exploring";
@@ -781,13 +823,11 @@ export function reduceThreadState(state: ThreadState, event: AgentEvent): Thread
781
823
  tool.status === "done" ? [] : [{ callId: tool.callId, name: tool.name }],
782
824
  );
783
825
  const generated = event.generation?.message?.content ?? [];
784
- const artifact = event.tools.find(
785
- (tool) => tool.name === ARTIFACT_TOOL && tool.status !== "done",
786
- );
826
+ const handingOff = event.tools.find((tool) => handsOff(tool.name) && tool.status !== "done");
787
827
  return {
788
828
  ...state,
789
829
  steps: event.entries.flatMap((entry) => threadStepOf(entry) ?? []),
790
- artifactActivity: artifactDetailsSchema.safeParse(artifact?.details).data?.activity,
830
+ handedOffActivity: handOffDetailsSchema.safeParse(handingOff?.details).data?.activity,
791
831
  running: event.run !== undefined,
792
832
  toolCalls,
793
833
  callingTools: generated.some((part) => part.type === "toolCall"),
@@ -817,7 +857,7 @@ export function reduceThreadState(state: ThreadState, event: AgentEvent): Thread
817
857
  callingTools: false,
818
858
  changing: false,
819
859
  checking: false,
820
- artifactActivity: undefined,
860
+ handedOffActivity: undefined,
821
861
  };
822
862
  case "message_start":
823
863
  // The model is called again: it thinks until it writes a tool call.
@@ -852,17 +892,17 @@ export function reduceThreadState(state: ThreadState, event: AgentEvent): Thread
852
892
  };
853
893
  }
854
894
  case "tool_execution_update": {
855
- if (event.toolName !== ARTIFACT_TOOL || event.parentToolCallId !== undefined) return state;
856
- const activity = artifactDetailsSchema.safeParse(event.details).data?.activity;
857
- return activity ? { ...state, artifactActivity: activity } : state;
895
+ if (!handsOff(event.toolName) || event.parentToolCallId !== undefined) return state;
896
+ const activity = handOffDetailsSchema.safeParse(event.details).data?.activity;
897
+ return activity ? { ...state, handedOffActivity: activity } : state;
858
898
  }
859
899
  case "tool_execution_end":
860
900
  return append(
861
901
  {
862
902
  ...state,
863
903
  toolCalls: state.toolCalls?.filter((call) => call.callId !== event.toolCallId),
864
- ...(event.toolName === ARTIFACT_TOOL &&
865
- event.parentToolCallId === undefined && { artifactActivity: undefined }),
904
+ ...(handsOff(event.toolName) &&
905
+ event.parentToolCallId === undefined && { handedOffActivity: undefined }),
866
906
  },
867
907
  event.entry,
868
908
  );