@camox/api-contract 0.65.1 → 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 +14 -0
- package/dist/drafts.js +14 -1
- package/dist/patrol.d.ts +10 -14
- package/dist/patrol.js +9 -5
- package/dist/router.d.ts +204 -8
- package/dist/threads.d.ts +80 -9
- package/dist/threads.js +136 -38
- package/package.json +1 -1
- package/src/drafts.ts +15 -0
- package/src/patrol.ts +15 -15
- package/src/threads.ts +139 -52
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"
|
|
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"
|
|
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
|
@@ -2298,6 +2298,25 @@ declare const schema: {
|
|
|
2298
2298
|
}, {}, {
|
|
2299
2299
|
length: number | undefined;
|
|
2300
2300
|
}>;
|
|
2301
|
+
overviewName: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
2302
|
+
name: "overview_name";
|
|
2303
|
+
tableName: "drafts";
|
|
2304
|
+
dataType: "string";
|
|
2305
|
+
columnType: "SQLiteText";
|
|
2306
|
+
data: string;
|
|
2307
|
+
driverParam: string;
|
|
2308
|
+
notNull: false;
|
|
2309
|
+
hasDefault: false;
|
|
2310
|
+
isPrimaryKey: false;
|
|
2311
|
+
isAutoincrement: false;
|
|
2312
|
+
hasRuntimeDefault: false;
|
|
2313
|
+
enumValues: [string, ...string[]];
|
|
2314
|
+
baseColumn: never;
|
|
2315
|
+
identity: undefined;
|
|
2316
|
+
generated: undefined;
|
|
2317
|
+
}, {}, {
|
|
2318
|
+
length: number | undefined;
|
|
2319
|
+
}>;
|
|
2301
2320
|
overviewBullets: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
2302
2321
|
name: "overview_bullets";
|
|
2303
2322
|
tableName: "drafts";
|
|
@@ -9948,9 +9967,9 @@ type CheckJob = {
|
|
|
9948
9967
|
};
|
|
9949
9968
|
/**
|
|
9950
9969
|
* A run of patrollers' patrols of a project's Live content (ADR 0034), keyed by project: the cron
|
|
9951
|
-
* trigger requests one of every patroller patrolling Live for every project every 6 hours
|
|
9952
|
-
*
|
|
9953
|
-
*
|
|
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.
|
|
9954
9973
|
*/
|
|
9955
9974
|
type PatrolJob = {
|
|
9956
9975
|
kind: "patrol";
|
|
@@ -10357,10 +10376,12 @@ type Overview =
|
|
|
10357
10376
|
} |
|
|
10358
10377
|
/**
|
|
10359
10378
|
* Written. A `stale` one describes an older version of the draft's changes: it's still shown
|
|
10360
|
-
* while a new one is written.
|
|
10379
|
+
* while a new one is written. `name` is the short name AI suggests keeping the draft under,
|
|
10380
|
+
* null for an overview written before overviews had one.
|
|
10361
10381
|
*/
|
|
10362
10382
|
{
|
|
10363
10383
|
status: "ready";
|
|
10384
|
+
name: string | null;
|
|
10364
10385
|
headline: string;
|
|
10365
10386
|
bullets: string[];
|
|
10366
10387
|
stale: boolean;
|
|
@@ -14493,6 +14514,177 @@ declare const router: {
|
|
|
14493
14514
|
structuredData: BlockStructuredData;
|
|
14494
14515
|
recordStructuredData: import("@camox/api-contract").RecordStructuredData;
|
|
14495
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>>;
|
|
14496
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>>, {
|
|
14497
14689
|
draft: DraftRequest | null;
|
|
14498
14690
|
locale: string | undefined;
|
|
@@ -18103,16 +18295,17 @@ declare const router: {
|
|
|
18103
18295
|
subject: import("@camox/api-contract/patrol").ReportSubject;
|
|
18104
18296
|
locale: string;
|
|
18105
18297
|
field: string;
|
|
18298
|
+
fieldKind: string | null;
|
|
18106
18299
|
raisedAt: number;
|
|
18107
18300
|
live: boolean;
|
|
18108
18301
|
place: import("@camox/api-contract/patrol").ReportPlace | null;
|
|
18109
18302
|
patroller: {
|
|
18110
|
-
id: "linkChecker" | "
|
|
18303
|
+
id: "linkChecker" | "seoManager" | "translator";
|
|
18111
18304
|
role: string;
|
|
18112
18305
|
};
|
|
18113
18306
|
}[];
|
|
18114
18307
|
checking: {
|
|
18115
|
-
id: "linkChecker" | "
|
|
18308
|
+
id: "linkChecker" | "seoManager" | "translator";
|
|
18116
18309
|
role: string;
|
|
18117
18310
|
}[];
|
|
18118
18311
|
defaultLocale: string | null;
|
|
@@ -18121,6 +18314,7 @@ declare const router: {
|
|
|
18121
18314
|
status: "pending";
|
|
18122
18315
|
} | {
|
|
18123
18316
|
status: "ready";
|
|
18317
|
+
name: string | null;
|
|
18124
18318
|
headline: string;
|
|
18125
18319
|
bullets: string[];
|
|
18126
18320
|
stale: boolean;
|
|
@@ -18144,16 +18338,17 @@ declare const router: {
|
|
|
18144
18338
|
subject: import("@camox/api-contract/patrol").ReportSubject;
|
|
18145
18339
|
locale: string;
|
|
18146
18340
|
field: string;
|
|
18341
|
+
fieldKind: string | null;
|
|
18147
18342
|
raisedAt: number;
|
|
18148
18343
|
live: boolean;
|
|
18149
18344
|
place: import("@camox/api-contract/patrol").ReportPlace | null;
|
|
18150
18345
|
patroller: {
|
|
18151
|
-
id: "linkChecker" | "
|
|
18346
|
+
id: "linkChecker" | "seoManager" | "translator";
|
|
18152
18347
|
role: string;
|
|
18153
18348
|
};
|
|
18154
18349
|
}[];
|
|
18155
18350
|
checking: {
|
|
18156
|
-
id: "linkChecker" | "
|
|
18351
|
+
id: "linkChecker" | "seoManager" | "translator";
|
|
18157
18352
|
role: string;
|
|
18158
18353
|
}[];
|
|
18159
18354
|
defaultLocale: string | null;
|
|
@@ -18162,6 +18357,7 @@ declare const router: {
|
|
|
18162
18357
|
status: "pending";
|
|
18163
18358
|
} | {
|
|
18164
18359
|
status: "ready";
|
|
18360
|
+
name: string | null;
|
|
18165
18361
|
headline: string;
|
|
18166
18362
|
bullets: string[];
|
|
18167
18363
|
stale: boolean;
|
package/dist/threads.d.ts
CHANGED
|
@@ -45,6 +45,13 @@ export declare const BRIEF_ENTRY = "camox.brief";
|
|
|
45
45
|
* it at the end of the conversation; editors never see it, since the draft's card says so.
|
|
46
46
|
*/
|
|
47
47
|
export declare const DRAFT_ENDED_ENTRY = "camox.draft-ended";
|
|
48
|
+
/**
|
|
49
|
+
* The kind of the transcript entry a Live thread starts afresh from once its agent handed a
|
|
50
|
+
* request off to a draft (ADR 0036): a reset of Pi's context, with a `startedDraftCardSchema` as
|
|
51
|
+
* data. Editors see it as the draft's card, alone, and the model reads a note naming the draft.
|
|
52
|
+
* Everything before it is left out of both.
|
|
53
|
+
*/
|
|
54
|
+
export declare const DRAFT_CARD_ENTRY = "camox.draft-card";
|
|
48
55
|
/**
|
|
49
56
|
* The kind of the transcript entry recording a member's push to the draft's code from their own
|
|
50
57
|
* checkout (ADR 0026), with a `pushDataSchema` as data. The model reads it as context, never
|
|
@@ -67,6 +74,43 @@ export declare const START_DRAFT_TOOL = "startDraft";
|
|
|
67
74
|
export declare const BUILD_TOOL = "build";
|
|
68
75
|
/** The agent's tool rendering the draft in a browser and returning screenshots to the model. */
|
|
69
76
|
export declare const LOOK_TOOL = "look";
|
|
77
|
+
/**
|
|
78
|
+
* The agent's tool handing an artifact's source to its Artifact Agent, which writes and changes
|
|
79
|
+
* it in a conversation of its own and answers the agent (ADR 0035).
|
|
80
|
+
*/
|
|
81
|
+
export declare const ARTIFACT_TOOL = "artifact";
|
|
82
|
+
/**
|
|
83
|
+
* The kind of the entry recording a request the agent sent an Artifact Agent, in the Artifact
|
|
84
|
+
* Agent's own conversation, which editors never see: who the run acts for and the operation ids
|
|
85
|
+
* of its messages, as data (`artifactRequestDataSchema`).
|
|
86
|
+
*/
|
|
87
|
+
export declare const ARTIFACT_REQUEST_ENTRY = "camox.artifact-request";
|
|
88
|
+
/** What an Artifact Agent's request records: the member the run acts for, and its messages. */
|
|
89
|
+
export declare const artifactRequestDataSchema: z.ZodObject<{
|
|
90
|
+
member: z.ZodString;
|
|
91
|
+
operationIds: z.ZodArray<z.ZodString>;
|
|
92
|
+
}, z.core.$strip>;
|
|
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;
|
|
70
114
|
/**
|
|
71
115
|
* The tools whose calls change a draft: the content tools that write, and the code tools that
|
|
72
116
|
* write files. The model calls the code tools, and code mode scripts call both. A test in the API
|
|
@@ -155,16 +199,12 @@ export declare const pushDataSchema: z.ZodObject<{
|
|
|
155
199
|
}>;
|
|
156
200
|
}, z.core.$strip>;
|
|
157
201
|
export type PushData = z.infer<typeof pushDataSchema>;
|
|
158
|
-
/**
|
|
159
|
-
* What an editor's message entry records: who sent it, the operation id it was sent with, and for
|
|
160
|
-
* a message sent on Live as the agent handed the request off, the draft it went to with it.
|
|
161
|
-
*/
|
|
202
|
+
/** What an editor's message entry records: who sent it, and the operation id it was sent with. */
|
|
162
203
|
export declare const editorMessageDataSchema: z.ZodObject<{
|
|
163
204
|
id: z.ZodString;
|
|
164
205
|
name: z.ZodString;
|
|
165
206
|
image: z.ZodNullable<z.ZodString>;
|
|
166
207
|
operationId: z.ZodOptional<z.ZodString>;
|
|
167
|
-
handedOffTo: z.ZodOptional<z.ZodString>;
|
|
168
208
|
}, z.core.$strip>;
|
|
169
209
|
/**
|
|
170
210
|
* A draft the agent on Live started with `startDraft`, and the operation ids of the messages of
|
|
@@ -344,7 +384,35 @@ export type ThreadMessage = {
|
|
|
344
384
|
* - `fixing`: the end of the turn's build failed, and the agent fixes it.
|
|
345
385
|
* - `startingDraft`: on Live, its `startDraft` call runs.
|
|
346
386
|
*/
|
|
347
|
-
export
|
|
387
|
+
export declare const agentActivitySchema: z.ZodEnum<{
|
|
388
|
+
building: "building";
|
|
389
|
+
changing: "changing";
|
|
390
|
+
checking: "checking";
|
|
391
|
+
exploring: "exploring";
|
|
392
|
+
fixing: "fixing";
|
|
393
|
+
startingDraft: "startingDraft";
|
|
394
|
+
thinking: "thinking";
|
|
395
|
+
}>;
|
|
396
|
+
export type AgentActivity = z.infer<typeof agentActivitySchema>;
|
|
397
|
+
/**
|
|
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.
|
|
401
|
+
*/
|
|
402
|
+
export declare const handOffDetailsSchema: z.ZodObject<{
|
|
403
|
+
answer: z.ZodOptional<z.ZodString>;
|
|
404
|
+
final: z.ZodOptional<z.ZodBoolean>;
|
|
405
|
+
activity: z.ZodOptional<z.ZodEnum<{
|
|
406
|
+
building: "building";
|
|
407
|
+
changing: "changing";
|
|
408
|
+
checking: "checking";
|
|
409
|
+
exploring: "exploring";
|
|
410
|
+
fixing: "fixing";
|
|
411
|
+
startingDraft: "startingDraft";
|
|
412
|
+
thinking: "thinking";
|
|
413
|
+
}>>;
|
|
414
|
+
}, z.core.$strip>;
|
|
415
|
+
export type HandOffDetails = z.infer<typeof handOffDetailsSchema>;
|
|
348
416
|
/** Everything the studio shows for a thread. */
|
|
349
417
|
export type ThreadView = {
|
|
350
418
|
messages: readonly ThreadMessage[];
|
|
@@ -376,6 +444,11 @@ export type ThreadState = {
|
|
|
376
444
|
changing?: boolean;
|
|
377
445
|
/** Whether the model's latest round calls `look`: from writing the call until it's called again. */
|
|
378
446
|
checking?: boolean;
|
|
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;
|
|
379
452
|
/** The drafts the thread's cards name, as the thread last looked them up. */
|
|
380
453
|
drafts?: DraftCardStates;
|
|
381
454
|
};
|
|
@@ -387,8 +460,6 @@ type ThreadStep = {
|
|
|
387
460
|
type: "editor";
|
|
388
461
|
sender: ThreadSender;
|
|
389
462
|
text: string;
|
|
390
|
-
/** The draft the message went to, as the agent on Live handed the request off. */
|
|
391
|
-
handedOffTo?: string;
|
|
392
463
|
} | {
|
|
393
464
|
type: "connectedAgent";
|
|
394
465
|
member: ThreadSender;
|
|
@@ -566,7 +637,7 @@ export declare const editorTextOf: (content: Message["content"]) => string;
|
|
|
566
637
|
* run is over, or a later message started another run after its last model call came back
|
|
567
638
|
* unfinished. While Pi retries a failed call, the run goes on, so the turn shows as working.
|
|
568
639
|
*/
|
|
569
|
-
export declare function threadViewOf({ steps, running, toolCalls, callingTools, changing, checking, drafts }: ThreadState): ThreadView;
|
|
640
|
+
export declare function threadViewOf({ steps, running, toolCalls, callingTools, changing, checking, handedOffActivity, drafts }: ThreadState): ThreadView;
|
|
570
641
|
/** Folds one of Pi's agent events into the thread's state. */
|
|
571
642
|
export declare function reduceThreadState(state: ThreadState, event: AgentEvent): ThreadState;
|
|
572
643
|
/** Folds a batch of Pi's agent events into the thread's state. */
|
package/dist/threads.js
CHANGED
|
@@ -44,6 +44,13 @@ const BRIEF_ENTRY = "camox.brief";
|
|
|
44
44
|
*/
|
|
45
45
|
const DRAFT_ENDED_ENTRY = "camox.draft-ended";
|
|
46
46
|
/**
|
|
47
|
+
* The kind of the transcript entry a Live thread starts afresh from once its agent handed a
|
|
48
|
+
* request off to a draft (ADR 0036): a reset of Pi's context, with a `startedDraftCardSchema` as
|
|
49
|
+
* data. Editors see it as the draft's card, alone, and the model reads a note naming the draft.
|
|
50
|
+
* Everything before it is left out of both.
|
|
51
|
+
*/
|
|
52
|
+
const DRAFT_CARD_ENTRY = "camox.draft-card";
|
|
53
|
+
/**
|
|
47
54
|
* The kind of the transcript entry recording a member's push to the draft's code from their own
|
|
48
55
|
* checkout (ADR 0026), with a `pushDataSchema` as data. The model reads it as context, never
|
|
49
56
|
* answering it, and editors see who pushed, their commits, and whether they built.
|
|
@@ -65,6 +72,42 @@ const BUILD_TOOL = "build";
|
|
|
65
72
|
/** The agent's tool rendering the draft in a browser and returning screenshots to the model. */
|
|
66
73
|
const LOOK_TOOL = "look";
|
|
67
74
|
/**
|
|
75
|
+
* The agent's tool handing an artifact's source to its Artifact Agent, which writes and changes
|
|
76
|
+
* it in a conversation of its own and answers the agent (ADR 0035).
|
|
77
|
+
*/
|
|
78
|
+
const ARTIFACT_TOOL = "artifact";
|
|
79
|
+
/**
|
|
80
|
+
* The kind of the entry recording a request the agent sent an Artifact Agent, in the Artifact
|
|
81
|
+
* Agent's own conversation, which editors never see: who the run acts for and the operation ids
|
|
82
|
+
* of its messages, as data (`artifactRequestDataSchema`).
|
|
83
|
+
*/
|
|
84
|
+
const ARTIFACT_REQUEST_ENTRY = "camox.artifact-request";
|
|
85
|
+
/** What an Artifact Agent's request records: the member the run acts for, and its messages. */
|
|
86
|
+
const artifactRequestDataSchema = z.object({
|
|
87
|
+
member: z.string(),
|
|
88
|
+
operationIds: z.array(z.string())
|
|
89
|
+
});
|
|
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
|
+
/**
|
|
68
111
|
* The tools whose calls change a draft: the content tools that write, and the code tools that
|
|
69
112
|
* write files. The model calls the code tools, and code mode scripts call both. A test in the API
|
|
70
113
|
* keeps this in step with the tools themselves.
|
|
@@ -132,14 +175,8 @@ const pushDataSchema = z.object({
|
|
|
132
175
|
subjects: z.array(z.string()),
|
|
133
176
|
outcome: pushOutcomeSchema
|
|
134
177
|
});
|
|
135
|
-
/**
|
|
136
|
-
|
|
137
|
-
* a message sent on Live as the agent handed the request off, the draft it went to with it.
|
|
138
|
-
*/
|
|
139
|
-
const editorMessageDataSchema = threadSenderSchema.extend({
|
|
140
|
-
operationId: z.string().optional(),
|
|
141
|
-
handedOffTo: z.string().optional()
|
|
142
|
-
});
|
|
178
|
+
/** What an editor's message entry records: who sent it, and the operation id it was sent with. */
|
|
179
|
+
const editorMessageDataSchema = threadSenderSchema.extend({ operationId: z.string().optional() });
|
|
143
180
|
/**
|
|
144
181
|
* A draft the agent on Live started with `startDraft`, and the operation ids of the messages of
|
|
145
182
|
* the run that started it: the studio that sent one of them enters the draft, if it still shows
|
|
@@ -193,6 +230,37 @@ const touchedBatchSchema = z.object({
|
|
|
193
230
|
operationIds: z.array(z.string()),
|
|
194
231
|
touches: z.array(touchSchema)
|
|
195
232
|
});
|
|
233
|
+
/**
|
|
234
|
+
* What the agent is doing during a run, as editors see it, without the details of how it works:
|
|
235
|
+
* - `thinking`: the model is called, and hasn't started writing a tool call.
|
|
236
|
+
* - `exploring`: it uses tools that only read, from writing a call until the model is called again.
|
|
237
|
+
* - `changing`: a call of that round changes the draft (`CHANGING_TOOLS`), as soon as it's written
|
|
238
|
+
* or, in a code mode script, as soon as the script calls one.
|
|
239
|
+
* - `building`: its `build` call runs, or the end of the turn builds its code changes.
|
|
240
|
+
* - `checking`: it looks at what it designed, from writing a `look` call until the model is
|
|
241
|
+
* called again.
|
|
242
|
+
* - `fixing`: the end of the turn's build failed, and the agent fixes it.
|
|
243
|
+
* - `startingDraft`: on Live, its `startDraft` call runs.
|
|
244
|
+
*/
|
|
245
|
+
const agentActivitySchema = z.enum([
|
|
246
|
+
"thinking",
|
|
247
|
+
"exploring",
|
|
248
|
+
"changing",
|
|
249
|
+
"building",
|
|
250
|
+
"checking",
|
|
251
|
+
"fixing",
|
|
252
|
+
"startingDraft"
|
|
253
|
+
]);
|
|
254
|
+
/**
|
|
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.
|
|
258
|
+
*/
|
|
259
|
+
const handOffDetailsSchema = z.object({
|
|
260
|
+
answer: z.string().optional(),
|
|
261
|
+
final: z.boolean().optional(),
|
|
262
|
+
activity: agentActivitySchema.optional()
|
|
263
|
+
});
|
|
196
264
|
const EMPTY_THREAD_VIEW = {
|
|
197
265
|
messages: [],
|
|
198
266
|
running: false
|
|
@@ -307,19 +375,23 @@ function threadStepOf(entry) {
|
|
|
307
375
|
...push
|
|
308
376
|
};
|
|
309
377
|
}
|
|
378
|
+
if (entry.kind === "camox.draft-card") {
|
|
379
|
+
const card = startedDraftCardSchema.safeParse(entry.data).data;
|
|
380
|
+
return card && {
|
|
381
|
+
id,
|
|
382
|
+
type: "draft",
|
|
383
|
+
...card
|
|
384
|
+
};
|
|
385
|
+
}
|
|
310
386
|
const message = entry.model?.find((candidate) => candidate.role !== "system");
|
|
311
387
|
if (!message) return void 0;
|
|
312
388
|
if (message.role === "user") {
|
|
313
|
-
if (entry.kind === "camox.editor-message") {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
text: editorTextOf(message.content),
|
|
320
|
-
...handedOffTo && { handedOffTo }
|
|
321
|
-
};
|
|
322
|
-
}
|
|
389
|
+
if (entry.kind === "camox.editor-message") return sender && {
|
|
390
|
+
id,
|
|
391
|
+
type: "editor",
|
|
392
|
+
sender,
|
|
393
|
+
text: editorTextOf(message.content)
|
|
394
|
+
};
|
|
323
395
|
if (entry.kind === "camox.connected-agent-message") {
|
|
324
396
|
const data = connectedAgentMessageDataSchema.safeParse(entry.data).data;
|
|
325
397
|
return data && {
|
|
@@ -353,6 +425,14 @@ function threadStepOf(entry) {
|
|
|
353
425
|
}
|
|
354
426
|
if (message.role === "assistant") return assistantStep(id, message);
|
|
355
427
|
if (message.role === "toolResult") {
|
|
428
|
+
if (handsOff(message.toolName) && !message.isError) {
|
|
429
|
+
const details = handOffDetailsSchema.safeParse(message.details).data;
|
|
430
|
+
if (details?.final && details.answer) return {
|
|
431
|
+
id,
|
|
432
|
+
type: "answer",
|
|
433
|
+
text: details.answer
|
|
434
|
+
};
|
|
435
|
+
}
|
|
356
436
|
const card = message.toolName === "startDraft" && !message.isError ? startedDraftCardSchema.safeParse(message.details).data : void 0;
|
|
357
437
|
return card ? {
|
|
358
438
|
id,
|
|
@@ -373,7 +453,7 @@ function threadStepOf(entry) {
|
|
|
373
453
|
* run is over, or a later message started another run after its last model call came back
|
|
374
454
|
* unfinished. While Pi retries a failed call, the run goes on, so the turn shows as working.
|
|
375
455
|
*/
|
|
376
|
-
function threadViewOf({ steps, running, toolCalls = [], callingTools = false, changing = false, checking = false, drafts = {} }) {
|
|
456
|
+
function threadViewOf({ steps, running, toolCalls = [], callingTools = false, changing = false, checking = false, handedOffActivity, drafts = {} }) {
|
|
377
457
|
const messages = [];
|
|
378
458
|
let waiting;
|
|
379
459
|
let unfinished = false;
|
|
@@ -395,17 +475,7 @@ function threadViewOf({ steps, running, toolCalls = [], callingTools = false, ch
|
|
|
395
475
|
endTurn();
|
|
396
476
|
};
|
|
397
477
|
for (const step of steps) switch (step.type) {
|
|
398
|
-
case "editor":
|
|
399
|
-
const card = step.handedOffTo ? messages.findLastIndex((message) => message.from === "draft" && message.draftId === step.handedOffTo) : -1;
|
|
400
|
-
if (card !== -1) {
|
|
401
|
-
messages.splice(card, 0, {
|
|
402
|
-
id: step.id,
|
|
403
|
-
from: "editor",
|
|
404
|
-
sender: step.sender,
|
|
405
|
-
text: step.text
|
|
406
|
-
});
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
478
|
+
case "editor":
|
|
409
479
|
if (unfinished) fail();
|
|
410
480
|
messages.push({
|
|
411
481
|
id: step.id,
|
|
@@ -415,7 +485,6 @@ function threadViewOf({ steps, running, toolCalls = [], callingTools = false, ch
|
|
|
415
485
|
});
|
|
416
486
|
waiting = step.id;
|
|
417
487
|
break;
|
|
418
|
-
}
|
|
419
488
|
case "connectedAgent":
|
|
420
489
|
if (unfinished) fail();
|
|
421
490
|
messages.push({
|
|
@@ -496,6 +565,7 @@ function threadViewOf({ steps, running, toolCalls = [], callingTools = false, ch
|
|
|
496
565
|
break;
|
|
497
566
|
case "tools":
|
|
498
567
|
unfinished = false;
|
|
568
|
+
status = void 0;
|
|
499
569
|
break;
|
|
500
570
|
case "unfinished": unfinished = true;
|
|
501
571
|
}
|
|
@@ -512,16 +582,18 @@ function threadViewOf({ steps, running, toolCalls = [], callingTools = false, ch
|
|
|
512
582
|
toolCalls,
|
|
513
583
|
callingTools,
|
|
514
584
|
changing,
|
|
515
|
-
checking
|
|
585
|
+
checking,
|
|
586
|
+
handedOffActivity
|
|
516
587
|
})
|
|
517
588
|
};
|
|
518
589
|
}
|
|
519
590
|
/** What the agent is doing during a run, from where the turn's build stands and its tool calls. */
|
|
520
|
-
function activityOf({ status, toolCalls, callingTools, changing, checking }) {
|
|
591
|
+
function activityOf({ status, toolCalls, callingTools, changing, checking, handedOffActivity }) {
|
|
521
592
|
const calling = (name) => toolCalls.some((call) => call.name === name);
|
|
522
593
|
if (status === "building" || calling("build")) return "building";
|
|
523
|
-
if (checking) return "checking";
|
|
524
594
|
if (status === "fixing") return "fixing";
|
|
595
|
+
if (toolCalls.some((call) => handsOff(call.name))) return handedOffActivity ?? "changing";
|
|
596
|
+
if (checking) return "checking";
|
|
525
597
|
if (calling("startDraft")) return "startingDraft";
|
|
526
598
|
if (changing) return "changing";
|
|
527
599
|
if (callingTools || toolCalls.length > 0) return "exploring";
|
|
@@ -533,8 +605,22 @@ const changes = (name) => CHANGING_TOOLS.has(name);
|
|
|
533
605
|
const callsChangingTool = (block) => block.type === "toolCall" && changes(block.name);
|
|
534
606
|
/** Whether a block of the model's message is a call of `look`. */
|
|
535
607
|
const callsLook = (block) => block.type === "toolCall" && block.name === "look";
|
|
608
|
+
/**
|
|
609
|
+
* Adds an entry to the thread's state as Pi places it. An entry heading a new context, such as
|
|
610
|
+
* the card a Live thread starts afresh from, leaves out every step before its head, as Pi's
|
|
611
|
+
* snapshots do.
|
|
612
|
+
*/
|
|
536
613
|
const append = (state, entry) => {
|
|
537
|
-
|
|
614
|
+
if (!entry) return state;
|
|
615
|
+
const step = threadStepOf(entry);
|
|
616
|
+
if (entry.head !== void 0) {
|
|
617
|
+
const head = Number(entry.head);
|
|
618
|
+
const kept = state.steps.filter((known) => Number(known.id) >= head && known.id !== String(entry.id));
|
|
619
|
+
return {
|
|
620
|
+
...state,
|
|
621
|
+
steps: step ? [step, ...kept] : kept
|
|
622
|
+
};
|
|
623
|
+
}
|
|
538
624
|
if (!step || state.steps.some((known) => known.id === step.id)) return state;
|
|
539
625
|
return {
|
|
540
626
|
...state,
|
|
@@ -550,9 +636,11 @@ function reduceThreadState(state, event) {
|
|
|
550
636
|
name: tool.name
|
|
551
637
|
}]);
|
|
552
638
|
const generated = event.generation?.message?.content ?? [];
|
|
639
|
+
const handingOff = event.tools.find((tool) => handsOff(tool.name) && tool.status !== "done");
|
|
553
640
|
return {
|
|
554
641
|
...state,
|
|
555
642
|
steps: event.entries.flatMap((entry) => threadStepOf(entry) ?? []),
|
|
643
|
+
handedOffActivity: handOffDetailsSchema.safeParse(handingOff?.details).data?.activity,
|
|
556
644
|
running: event.run !== void 0,
|
|
557
645
|
toolCalls,
|
|
558
646
|
callingTools: generated.some((part) => part.type === "toolCall"),
|
|
@@ -570,7 +658,8 @@ function reduceThreadState(state, event) {
|
|
|
570
658
|
toolCalls: [],
|
|
571
659
|
callingTools: false,
|
|
572
660
|
changing: false,
|
|
573
|
-
checking: false
|
|
661
|
+
checking: false,
|
|
662
|
+
handedOffActivity: void 0
|
|
574
663
|
};
|
|
575
664
|
case "message_start": return event.message.role === "assistant" ? {
|
|
576
665
|
...state,
|
|
@@ -604,9 +693,18 @@ function reduceThreadState(state, event) {
|
|
|
604
693
|
}]
|
|
605
694
|
};
|
|
606
695
|
}
|
|
696
|
+
case "tool_execution_update": {
|
|
697
|
+
if (!handsOff(event.toolName) || event.parentToolCallId !== void 0) return state;
|
|
698
|
+
const activity = handOffDetailsSchema.safeParse(event.details).data?.activity;
|
|
699
|
+
return activity ? {
|
|
700
|
+
...state,
|
|
701
|
+
handedOffActivity: activity
|
|
702
|
+
} : state;
|
|
703
|
+
}
|
|
607
704
|
case "tool_execution_end": return append({
|
|
608
705
|
...state,
|
|
609
|
-
toolCalls: state.toolCalls?.filter((call) => call.callId !== event.toolCallId)
|
|
706
|
+
toolCalls: state.toolCalls?.filter((call) => call.callId !== event.toolCallId),
|
|
707
|
+
...handsOff(event.toolName) && event.parentToolCallId === void 0 && { handedOffActivity: void 0 }
|
|
610
708
|
}, event.entry);
|
|
611
709
|
case "message_end":
|
|
612
710
|
case "entry_appended": return append(state, event.entry);
|
|
@@ -628,4 +726,4 @@ function reduceThreadFrame(state, frame) {
|
|
|
628
726
|
}
|
|
629
727
|
|
|
630
728
|
//#endregion
|
|
631
|
-
export { BRIEF_ENTRY, BUILD_STATUS_ENTRY, BUILD_TOOL, CHANGING_TOOLS, CONNECTED_AGENT_MESSAGE_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, 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
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"
|
|
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
|
-
|
|
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
|
@@ -57,6 +57,14 @@ export const BRIEF_ENTRY = "camox.brief";
|
|
|
57
57
|
*/
|
|
58
58
|
export const DRAFT_ENDED_ENTRY = "camox.draft-ended";
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* The kind of the transcript entry a Live thread starts afresh from once its agent handed a
|
|
62
|
+
* request off to a draft (ADR 0036): a reset of Pi's context, with a `startedDraftCardSchema` as
|
|
63
|
+
* data. Editors see it as the draft's card, alone, and the model reads a note naming the draft.
|
|
64
|
+
* Everything before it is left out of both.
|
|
65
|
+
*/
|
|
66
|
+
export const DRAFT_CARD_ENTRY = "camox.draft-card";
|
|
67
|
+
|
|
60
68
|
/**
|
|
61
69
|
* The kind of the transcript entry recording a member's push to the draft's code from their own
|
|
62
70
|
* checkout (ADR 0026), with a `pushDataSchema` as data. The model reads it as context, never
|
|
@@ -80,6 +88,54 @@ export const BUILD_TOOL = "build";
|
|
|
80
88
|
/** The agent's tool rendering the draft in a browser and returning screenshots to the model. */
|
|
81
89
|
export const LOOK_TOOL = "look";
|
|
82
90
|
|
|
91
|
+
/**
|
|
92
|
+
* The agent's tool handing an artifact's source to its Artifact Agent, which writes and changes
|
|
93
|
+
* it in a conversation of its own and answers the agent (ADR 0035).
|
|
94
|
+
*/
|
|
95
|
+
export const ARTIFACT_TOOL = "artifact";
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The kind of the entry recording a request the agent sent an Artifact Agent, in the Artifact
|
|
99
|
+
* Agent's own conversation, which editors never see: who the run acts for and the operation ids
|
|
100
|
+
* of its messages, as data (`artifactRequestDataSchema`).
|
|
101
|
+
*/
|
|
102
|
+
export const ARTIFACT_REQUEST_ENTRY = "camox.artifact-request";
|
|
103
|
+
|
|
104
|
+
/** What an Artifact Agent's request records: the member the run acts for, and its messages. */
|
|
105
|
+
export const artifactRequestDataSchema = z.object({
|
|
106
|
+
member: z.string(),
|
|
107
|
+
operationIds: z.array(z.string()),
|
|
108
|
+
});
|
|
109
|
+
export type ArtifactRequestData = z.infer<typeof artifactRequestDataSchema>;
|
|
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
|
+
|
|
83
139
|
/**
|
|
84
140
|
* The tools whose calls change a draft: the content tools that write, and the code tools that
|
|
85
141
|
* write files. The model calls the code tools, and code mode scripts call both. A test in the API
|
|
@@ -172,13 +228,9 @@ export const pushDataSchema = z.object({
|
|
|
172
228
|
});
|
|
173
229
|
export type PushData = z.infer<typeof pushDataSchema>;
|
|
174
230
|
|
|
175
|
-
/**
|
|
176
|
-
* What an editor's message entry records: who sent it, the operation id it was sent with, and for
|
|
177
|
-
* a message sent on Live as the agent handed the request off, the draft it went to with it.
|
|
178
|
-
*/
|
|
231
|
+
/** What an editor's message entry records: who sent it, and the operation id it was sent with. */
|
|
179
232
|
export const editorMessageDataSchema = threadSenderSchema.extend({
|
|
180
233
|
operationId: z.string().optional(),
|
|
181
|
-
handedOffTo: z.string().optional(),
|
|
182
234
|
});
|
|
183
235
|
|
|
184
236
|
/**
|
|
@@ -284,14 +336,28 @@ export type ThreadMessage =
|
|
|
284
336
|
* - `fixing`: the end of the turn's build failed, and the agent fixes it.
|
|
285
337
|
* - `startingDraft`: on Live, its `startDraft` call runs.
|
|
286
338
|
*/
|
|
287
|
-
export
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
339
|
+
export const agentActivitySchema = z.enum([
|
|
340
|
+
"thinking",
|
|
341
|
+
"exploring",
|
|
342
|
+
"changing",
|
|
343
|
+
"building",
|
|
344
|
+
"checking",
|
|
345
|
+
"fixing",
|
|
346
|
+
"startingDraft",
|
|
347
|
+
]);
|
|
348
|
+
export type AgentActivity = z.infer<typeof agentActivitySchema>;
|
|
349
|
+
|
|
350
|
+
/**
|
|
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.
|
|
354
|
+
*/
|
|
355
|
+
export const handOffDetailsSchema = z.object({
|
|
356
|
+
answer: z.string().optional(),
|
|
357
|
+
final: z.boolean().optional(),
|
|
358
|
+
activity: agentActivitySchema.optional(),
|
|
359
|
+
});
|
|
360
|
+
export type HandOffDetails = z.infer<typeof handOffDetailsSchema>;
|
|
295
361
|
|
|
296
362
|
/** Everything the studio shows for a thread. */
|
|
297
363
|
export type ThreadView = {
|
|
@@ -323,6 +389,11 @@ export type ThreadState = {
|
|
|
323
389
|
changing?: boolean;
|
|
324
390
|
/** Whether the model's latest round calls `look`: from writing the call until it's called again. */
|
|
325
391
|
checking?: boolean;
|
|
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;
|
|
326
397
|
/** The drafts the thread's cards name, as the thread last looked them up. */
|
|
327
398
|
drafts?: DraftCardStates;
|
|
328
399
|
};
|
|
@@ -331,13 +402,7 @@ export const EMPTY_THREAD_STATE: ThreadState = { steps: [], running: false };
|
|
|
331
402
|
|
|
332
403
|
/** One transcript entry, as far as the studio's view is concerned. */
|
|
333
404
|
type ThreadStep = { id: string } & (
|
|
334
|
-
| {
|
|
335
|
-
type: "editor";
|
|
336
|
-
sender: ThreadSender;
|
|
337
|
-
text: string;
|
|
338
|
-
/** The draft the message went to, as the agent on Live handed the request off. */
|
|
339
|
-
handedOffTo?: string;
|
|
340
|
-
}
|
|
405
|
+
| { type: "editor"; sender: ThreadSender; text: string }
|
|
341
406
|
| { type: "connectedAgent"; member: ThreadSender; client: ConnectedAgentClient; text: string }
|
|
342
407
|
/** Camox starting a turn of its own, saying why. */
|
|
343
408
|
| { type: "camox"; text: string }
|
|
@@ -495,20 +560,16 @@ function threadStepOf(entry: EntryRecord): ThreadStep | undefined {
|
|
|
495
560
|
const push = pushDataSchema.safeParse(entry.data).data;
|
|
496
561
|
return push && { id, type: "push", ...push };
|
|
497
562
|
}
|
|
563
|
+
// The model reads a note: editors see the card it starts the Live thread afresh with.
|
|
564
|
+
if (entry.kind === DRAFT_CARD_ENTRY) {
|
|
565
|
+
const card = startedDraftCardSchema.safeParse(entry.data).data;
|
|
566
|
+
return card && { id, type: "draft", ...card };
|
|
567
|
+
}
|
|
498
568
|
const message = entry.model?.find((candidate) => candidate.role !== "system");
|
|
499
569
|
if (!message) return undefined;
|
|
500
570
|
if (message.role === "user") {
|
|
501
571
|
if (entry.kind === EDITOR_MESSAGE_ENTRY) {
|
|
502
|
-
|
|
503
|
-
return (
|
|
504
|
-
sender && {
|
|
505
|
-
id,
|
|
506
|
-
type: "editor",
|
|
507
|
-
sender,
|
|
508
|
-
text: editorTextOf(message.content),
|
|
509
|
-
...(handedOffTo && { handedOffTo }),
|
|
510
|
-
}
|
|
511
|
-
);
|
|
572
|
+
return sender && { id, type: "editor", sender, text: editorTextOf(message.content) };
|
|
512
573
|
}
|
|
513
574
|
if (entry.kind === CONNECTED_AGENT_MESSAGE_ENTRY) {
|
|
514
575
|
const data = connectedAgentMessageDataSchema.safeParse(entry.data).data;
|
|
@@ -533,6 +594,12 @@ function threadStepOf(entry: EntryRecord): ThreadStep | undefined {
|
|
|
533
594
|
}
|
|
534
595
|
if (message.role === "assistant") return assistantStep(id, message);
|
|
535
596
|
if (message.role === "toolResult") {
|
|
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;
|
|
601
|
+
if (details?.final && details.answer) return { id, type: "answer", text: details.answer };
|
|
602
|
+
}
|
|
536
603
|
const card =
|
|
537
604
|
message.toolName === START_DRAFT_TOOL && !message.isError
|
|
538
605
|
? startedDraftCardSchema.safeParse(message.details).data
|
|
@@ -558,6 +625,7 @@ export function threadViewOf({
|
|
|
558
625
|
callingTools = false,
|
|
559
626
|
changing = false,
|
|
560
627
|
checking = false,
|
|
628
|
+
handedOffActivity,
|
|
561
629
|
drafts = {},
|
|
562
630
|
}: ThreadState): ThreadView {
|
|
563
631
|
const messages: ThreadMessage[] = [];
|
|
@@ -583,24 +651,7 @@ export function threadViewOf({
|
|
|
583
651
|
|
|
584
652
|
for (const step of steps) {
|
|
585
653
|
switch (step.type) {
|
|
586
|
-
case "editor":
|
|
587
|
-
// A message sent as the agent on Live handed the request off went to the draft with it:
|
|
588
|
-
// it belongs to the turn ending on that draft's card, which it shows just before. Pi
|
|
589
|
-
// placed it after the card, which leaves the transcript the model read as it was.
|
|
590
|
-
const card = step.handedOffTo
|
|
591
|
-
? messages.findLastIndex(
|
|
592
|
-
(message) => message.from === "draft" && message.draftId === step.handedOffTo,
|
|
593
|
-
)
|
|
594
|
-
: -1;
|
|
595
|
-
if (card !== -1) {
|
|
596
|
-
messages.splice(card, 0, {
|
|
597
|
-
id: step.id,
|
|
598
|
-
from: "editor",
|
|
599
|
-
sender: step.sender,
|
|
600
|
-
text: step.text,
|
|
601
|
-
});
|
|
602
|
-
break;
|
|
603
|
-
}
|
|
654
|
+
case "editor":
|
|
604
655
|
// Pi places a steering message only once a model call is settled: after a round of tool
|
|
605
656
|
// calls, or with the answer ending the run. Never between a failed call and its retry, so
|
|
606
657
|
// an editor's message after an unfinished call means that call's turn is over.
|
|
@@ -608,7 +659,6 @@ export function threadViewOf({
|
|
|
608
659
|
messages.push({ id: step.id, from: "editor", sender: step.sender, text: step.text });
|
|
609
660
|
waiting = step.id;
|
|
610
661
|
break;
|
|
611
|
-
}
|
|
612
662
|
case "connectedAgent":
|
|
613
663
|
// Like an editor's message: it starts a turn, or steers the one going.
|
|
614
664
|
if (unfinished) fail();
|
|
@@ -680,6 +730,8 @@ export function threadViewOf({
|
|
|
680
730
|
break;
|
|
681
731
|
case "tools":
|
|
682
732
|
unfinished = false;
|
|
733
|
+
// The Code Agent's build status ends with its request, whose answer is a tool result.
|
|
734
|
+
status = undefined;
|
|
683
735
|
break;
|
|
684
736
|
case "unfinished":
|
|
685
737
|
unfinished = true;
|
|
@@ -691,7 +743,14 @@ export function threadViewOf({
|
|
|
691
743
|
return {
|
|
692
744
|
messages,
|
|
693
745
|
running,
|
|
694
|
-
activity: activityOf({
|
|
746
|
+
activity: activityOf({
|
|
747
|
+
status,
|
|
748
|
+
toolCalls,
|
|
749
|
+
callingTools,
|
|
750
|
+
changing,
|
|
751
|
+
checking,
|
|
752
|
+
handedOffActivity,
|
|
753
|
+
}),
|
|
695
754
|
};
|
|
696
755
|
}
|
|
697
756
|
|
|
@@ -702,19 +761,24 @@ function activityOf({
|
|
|
702
761
|
callingTools,
|
|
703
762
|
changing,
|
|
704
763
|
checking,
|
|
764
|
+
handedOffActivity,
|
|
705
765
|
}: {
|
|
706
766
|
status: BuildStatus | undefined;
|
|
707
767
|
toolCalls: NonNullable<ThreadState["toolCalls"]>;
|
|
708
768
|
callingTools: boolean;
|
|
709
769
|
changing: boolean;
|
|
710
770
|
checking: boolean;
|
|
771
|
+
handedOffActivity: AgentActivity | undefined;
|
|
711
772
|
}): AgentActivity {
|
|
712
773
|
const calling = (name: string) => toolCalls.some((call) => call.name === name);
|
|
713
774
|
// Fixing a build error takes thinking, reading and writing, which all read as fixing, but its
|
|
714
775
|
// builds show as building.
|
|
715
776
|
if (status === "building" || calling(BUILD_TOOL)) return "building";
|
|
716
|
-
if (checking) return "checking";
|
|
717
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";
|
|
718
782
|
if (calling(START_DRAFT_TOOL)) return "startingDraft";
|
|
719
783
|
if (changing) return "changing";
|
|
720
784
|
if (callingTools || toolCalls.length > 0) return "exploring";
|
|
@@ -732,8 +796,21 @@ const callsChangingTool = (block: AssistantMessage["content"][number]) =>
|
|
|
732
796
|
const callsLook = (block: AssistantMessage["content"][number]) =>
|
|
733
797
|
block.type === "toolCall" && block.name === LOOK_TOOL;
|
|
734
798
|
|
|
799
|
+
/**
|
|
800
|
+
* Adds an entry to the thread's state as Pi places it. An entry heading a new context, such as
|
|
801
|
+
* the card a Live thread starts afresh from, leaves out every step before its head, as Pi's
|
|
802
|
+
* snapshots do.
|
|
803
|
+
*/
|
|
735
804
|
const append = (state: ThreadState, entry: EntryRecord | undefined): ThreadState => {
|
|
736
|
-
|
|
805
|
+
if (!entry) return state;
|
|
806
|
+
const step = threadStepOf(entry);
|
|
807
|
+
if (entry.head !== undefined) {
|
|
808
|
+
const head = Number(entry.head);
|
|
809
|
+
const kept = state.steps.filter(
|
|
810
|
+
(known) => Number(known.id) >= head && known.id !== String(entry.id),
|
|
811
|
+
);
|
|
812
|
+
return { ...state, steps: step ? [step, ...kept] : kept };
|
|
813
|
+
}
|
|
737
814
|
if (!step || state.steps.some((known) => known.id === step.id)) return state;
|
|
738
815
|
return { ...state, steps: [...state.steps, step] };
|
|
739
816
|
};
|
|
@@ -746,9 +823,11 @@ export function reduceThreadState(state: ThreadState, event: AgentEvent): Thread
|
|
|
746
823
|
tool.status === "done" ? [] : [{ callId: tool.callId, name: tool.name }],
|
|
747
824
|
);
|
|
748
825
|
const generated = event.generation?.message?.content ?? [];
|
|
826
|
+
const handingOff = event.tools.find((tool) => handsOff(tool.name) && tool.status !== "done");
|
|
749
827
|
return {
|
|
750
828
|
...state,
|
|
751
829
|
steps: event.entries.flatMap((entry) => threadStepOf(entry) ?? []),
|
|
830
|
+
handedOffActivity: handOffDetailsSchema.safeParse(handingOff?.details).data?.activity,
|
|
752
831
|
running: event.run !== undefined,
|
|
753
832
|
toolCalls,
|
|
754
833
|
callingTools: generated.some((part) => part.type === "toolCall"),
|
|
@@ -778,6 +857,7 @@ export function reduceThreadState(state: ThreadState, event: AgentEvent): Thread
|
|
|
778
857
|
callingTools: false,
|
|
779
858
|
changing: false,
|
|
780
859
|
checking: false,
|
|
860
|
+
handedOffActivity: undefined,
|
|
781
861
|
};
|
|
782
862
|
case "message_start":
|
|
783
863
|
// The model is called again: it thinks until it writes a tool call.
|
|
@@ -811,11 +891,18 @@ export function reduceThreadState(state: ThreadState, event: AgentEvent): Thread
|
|
|
811
891
|
toolCalls: [...(state.toolCalls ?? []), { callId: event.toolCallId, name: event.toolName }],
|
|
812
892
|
};
|
|
813
893
|
}
|
|
894
|
+
case "tool_execution_update": {
|
|
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;
|
|
898
|
+
}
|
|
814
899
|
case "tool_execution_end":
|
|
815
900
|
return append(
|
|
816
901
|
{
|
|
817
902
|
...state,
|
|
818
903
|
toolCalls: state.toolCalls?.filter((call) => call.callId !== event.toolCallId),
|
|
904
|
+
...(handsOff(event.toolName) &&
|
|
905
|
+
event.parentToolCallId === undefined && { handedOffActivity: undefined }),
|
|
819
906
|
},
|
|
820
907
|
event.entry,
|
|
821
908
|
);
|