@intentic/sandbox-contract 1.247.0 → 1.248.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/contracts/agent.contract.d.ts +1 -0
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/personas.contract.d.ts +38 -4
- package/dist/contracts/personas.contract.d.ts.map +1 -1
- package/dist/contracts/personas.contract.js +10 -1
- package/dist/contracts/personas.contract.js.map +1 -1
- package/dist/contracts/runner.contract.d.ts +93 -93
- package/dist/contracts/settings.contract.d.ts +12 -2
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/definition.d.ts +8 -8
- package/dist/index.d.ts +51 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/model-pins.d.ts +1 -3
- package/dist/model-pins.d.ts.map +1 -1
- package/dist/model-pins.js +3 -26
- package/dist/model-pins.js.map +1 -1
- package/dist/model-roles.d.ts +36 -8
- package/dist/model-roles.d.ts.map +1 -1
- package/dist/model-roles.js +48 -10
- package/dist/model-roles.js.map +1 -1
- package/dist/schemas/agent.d.ts +1 -0
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/automations.js.map +1 -1
- package/dist/schemas/personas.d.ts +48 -4
- package/dist/schemas/personas.d.ts.map +1 -1
- package/dist/schemas/personas.js +28 -5
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/settings.d.ts +6 -1
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +5 -6
- package/dist/schemas/settings.js.map +1 -1
- package/dist/workspace-state.d.ts +0 -5
- package/dist/workspace-state.d.ts.map +1 -1
- package/dist/workspace-state.js +0 -1
- package/dist/workspace-state.js.map +1 -1
- package/package.json +4 -4
- package/src/chores/chores.ts +1 -1
- package/src/chores/verdict.test.ts +2 -2
- package/src/chores/verdict.ts +3 -3
- package/src/contracts/personas.contract.ts +17 -0
- package/src/fast-tier.test.ts +1 -1
- package/src/fast-tier.ts +1 -1
- package/src/index.ts +0 -1
- package/src/model-pins.test.ts +32 -113
- package/src/model-pins.ts +44 -95
- package/src/model-roles.test.ts +52 -0
- package/src/model-roles.ts +119 -23
- package/src/schemas/automations.ts +6 -2
- package/src/schemas/personas.ts +85 -18
- package/src/schemas/settings.ts +22 -17
- package/src/workspace-state.test.ts +0 -1
- package/src/workspace-state.ts +0 -6
- package/dist/schemas/context.d.ts +0 -30
- package/dist/schemas/context.d.ts.map +0 -1
- package/dist/schemas/context.js +0 -34
- package/dist/schemas/context.js.map +0 -1
- package/src/schemas/context.ts +0 -87
package/src/model-roles.ts
CHANGED
|
@@ -23,19 +23,26 @@ import { z } from "zod";
|
|
|
23
23
|
* went on the chat this morning, and one spent provider takes the role down for hours while three others sit
|
|
24
24
|
* idle. Written in order, the next entry catches it.
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* AN EMPTY LIST IS THE JOB SWITCHED OFF, and NOTHING IS DERIVED FOR IT. A helper role used to fall to an
|
|
27
|
+
* "Auto ladder" worked out from whatever was connected — every provider's cheapest row, best-first — so a
|
|
28
|
+
* sandbox that had never been configured still spent somebody's account on commit messages and safety
|
|
29
|
+
* verdicts, on a recommendation this table invented, which re-ranked itself the day another account was
|
|
30
|
+
* connected. Not set now means not set: no auto-selection, no recommendation, and the owner names the models
|
|
31
|
+
* for a job or the job does not happen.
|
|
28
32
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
+
* TWO KINDS, and what separates them is what the CALLER does with that empty answer. It is declared per role
|
|
34
|
+
* because it is a property of the job:
|
|
35
|
+
*
|
|
36
|
+
* helper — a one-shot. One prompt, no tools, one string back, and it is over. With no list the job does not
|
|
37
|
+
* run at all: no commit subject is drafted, no session is renamed, no command is judged. Every one
|
|
38
|
+
* of them already had a road for "the model could not answer" (the derived title stands, the
|
|
39
|
+
* commit box stays empty, the gate falls to its standing rule), so an owner who wants none of them
|
|
40
|
+
* leaves the row empty and pays nothing.
|
|
33
41
|
*
|
|
34
42
|
* run — a whole session with tools and a worktree, started by a surface rather than by a person at a
|
|
35
|
-
* composer.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* choice they made rather than one this table invented.
|
|
43
|
+
* composer. With no list the caller's own floor answers: the model the owner picked for their chat.
|
|
44
|
+
* That is not this table recommending anything — it is a choice they already made, in front of
|
|
45
|
+
* them, on the composer they work in.
|
|
39
46
|
*
|
|
40
47
|
* EVERY ENTRY IS A FULL PIN (ModelPinSchema): which model, and how it runs — effort, thinking, speed, harness.
|
|
41
48
|
* The helper roles carry them too, which they did not use to: a one-shot ran with reasoning forcibly off, so
|
|
@@ -51,6 +58,18 @@ import { z } from "zod";
|
|
|
51
58
|
export const ModelRoleKindSchema = z.enum(["helper", "run"]);
|
|
52
59
|
export type ModelRoleKind = z.infer<typeof ModelRoleKindSchema>;
|
|
53
60
|
|
|
61
|
+
/* WHAT STARTS A RUN, declared for the run roles alone.
|
|
62
|
+
*
|
|
63
|
+
* It is not a wire value and never has been: it decides which BLOCK a job is read in, and the argument for
|
|
64
|
+
* reading them apart is the same one that separates a helper from a run — an owner holds a session nobody is
|
|
65
|
+
* watching to a different budget from one they are sitting in front of. The settings page used to make that
|
|
66
|
+
* argument in a comment over a thirteen-row list ("the ones somebody presses ahead of the ones that fire on
|
|
67
|
+
* their own"), which is a claim no reader can check and no row has to honour. Declared, it draws the page.
|
|
68
|
+
*
|
|
69
|
+
* A HELPER DECLARES NONE, and that is the honest answer rather than a gap: nobody presses "write me a commit
|
|
70
|
+
* subject". It happens because something else did. */
|
|
71
|
+
export type ModelRoleTrigger = "pressed" | "unprompted";
|
|
72
|
+
|
|
54
73
|
/* The shape of a row. `id` is a bare string HERE and narrowed on the exported type below, because the id union
|
|
55
74
|
* is derived from this very table: a self-referential `satisfies` would be a type that has to know its own
|
|
56
75
|
* answer before it can check it. */
|
|
@@ -62,6 +81,8 @@ interface ModelRoleRow {
|
|
|
62
81
|
// cannot rather than restating it.
|
|
63
82
|
readonly blurb: string;
|
|
64
83
|
readonly kind: ModelRoleKind;
|
|
84
|
+
/** Runs only, and required for every one of them: see `ModelRoleTrigger`. */
|
|
85
|
+
readonly trigger?: ModelRoleTrigger;
|
|
65
86
|
// The row's glyph, from the shared icon set. Here rather than in a web-side map because the whole value of
|
|
66
87
|
// this table is that a role is declared ONCE; a second table keyed by the same ids is the drift this
|
|
67
88
|
// replaced, moved one layer up.
|
|
@@ -71,7 +92,8 @@ interface ModelRoleRow {
|
|
|
71
92
|
/* THE TABLE. Ordered as the settings page draws it, and the order is an argument about reach: the one-shots
|
|
72
93
|
* first, because nobody chose a model for them and they run constantly; then the runs somebody's click starts;
|
|
73
94
|
* then the runs that start themselves, which are the ones an owner is least likely to be watching and most
|
|
74
|
-
* likely to want held to a budget.
|
|
95
|
+
* likely to want held to a budget. Those three are BLOCKS now (see MODEL_ROLE_BLOCKS) rather than an ordering
|
|
96
|
+
* this comment asserts and nothing holds to.
|
|
75
97
|
*
|
|
76
98
|
* The ids are the wire vocabulary: a turn carries one (AgentTurn.runRole), so they are kebab-case and stable,
|
|
77
99
|
* and renaming one is a breaking change to the setting rather than a cosmetic edit. */
|
|
@@ -108,11 +130,23 @@ export const MODEL_ROLES = [
|
|
|
108
130
|
kind: "helper",
|
|
109
131
|
icon: "check-square",
|
|
110
132
|
},
|
|
133
|
+
{
|
|
134
|
+
/* THE ONE HELPER THAT ANSWERS A CLASSIFICATION rather than writing prose: one card id, or none, from the
|
|
135
|
+
* owner's own short list (schemas/personas.ts `brief`). Cheap by construction, once per chat, and the
|
|
136
|
+
* job a small model does well, which is the whole argument for routing chats onto static cards rather
|
|
137
|
+
* than asking a model to compose a context per session. */
|
|
138
|
+
id: "persona-router",
|
|
139
|
+
label: "Persona routing",
|
|
140
|
+
blurb: "Which model reads a new chat's first message and picks the persona for it.",
|
|
141
|
+
kind: "helper",
|
|
142
|
+
icon: "users",
|
|
143
|
+
},
|
|
111
144
|
{
|
|
112
145
|
id: "pipeline-fix",
|
|
113
146
|
label: "Pipeline fixes",
|
|
114
147
|
blurb: "The agent started by Fix on a red pipeline.",
|
|
115
148
|
kind: "run",
|
|
149
|
+
trigger: "pressed",
|
|
116
150
|
icon: "wave-pulse",
|
|
117
151
|
},
|
|
118
152
|
{
|
|
@@ -120,6 +154,7 @@ export const MODEL_ROLES = [
|
|
|
120
154
|
label: "Deployment fixes",
|
|
121
155
|
blurb: "The agent started by Fix on a deployment that is down.",
|
|
122
156
|
kind: "run",
|
|
157
|
+
trigger: "pressed",
|
|
123
158
|
icon: "server",
|
|
124
159
|
},
|
|
125
160
|
{
|
|
@@ -127,6 +162,7 @@ export const MODEL_ROLES = [
|
|
|
127
162
|
label: "Maintenance chores",
|
|
128
163
|
blurb: "A chore run started from the Maintenance board.",
|
|
129
164
|
kind: "run",
|
|
165
|
+
trigger: "pressed",
|
|
130
166
|
icon: "wrench",
|
|
131
167
|
},
|
|
132
168
|
{
|
|
@@ -134,6 +170,7 @@ export const MODEL_ROLES = [
|
|
|
134
170
|
label: "Documentation runs",
|
|
135
171
|
blurb: "A pass over a repo's own documentation.",
|
|
136
172
|
kind: "run",
|
|
173
|
+
trigger: "pressed",
|
|
137
174
|
icon: "book",
|
|
138
175
|
},
|
|
139
176
|
{
|
|
@@ -141,6 +178,7 @@ export const MODEL_ROLES = [
|
|
|
141
178
|
label: "Acceptance runs",
|
|
142
179
|
blurb: "One session per story in an acceptance fan-out.",
|
|
143
180
|
kind: "run",
|
|
181
|
+
trigger: "pressed",
|
|
144
182
|
icon: "list-check",
|
|
145
183
|
},
|
|
146
184
|
{
|
|
@@ -148,27 +186,34 @@ export const MODEL_ROLES = [
|
|
|
148
186
|
label: "Pre-push fixes",
|
|
149
187
|
blurb: "The fix proposed when a check fails on the way to a push.",
|
|
150
188
|
kind: "run",
|
|
189
|
+
trigger: "pressed",
|
|
151
190
|
icon: "cloud-upload",
|
|
152
191
|
},
|
|
153
192
|
{
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
kind: "run",
|
|
158
|
-
icon: "clock",
|
|
159
|
-
},
|
|
160
|
-
{
|
|
193
|
+
/* PRESSED, ON THE STRENGTH OF THE APPROVAL. Nothing here runs until somebody reads the item and says
|
|
194
|
+
* yes, and that press is the start of this turn as much as Fix is the start of a pipeline run — the
|
|
195
|
+
* queue between the two is machinery, not a second decision. */
|
|
161
196
|
id: "approval-queue",
|
|
162
197
|
label: "Approvals queue",
|
|
163
198
|
blurb: "The turn that publishes or acts on what you approved.",
|
|
164
199
|
kind: "run",
|
|
200
|
+
trigger: "pressed",
|
|
165
201
|
icon: "check-circle",
|
|
166
202
|
},
|
|
203
|
+
{
|
|
204
|
+
id: "automation-wake",
|
|
205
|
+
label: "Automation wakes",
|
|
206
|
+
blurb: "A turn an automation fires: a schedule, a webhook, a message from outside.",
|
|
207
|
+
kind: "run",
|
|
208
|
+
trigger: "unprompted",
|
|
209
|
+
icon: "clock",
|
|
210
|
+
},
|
|
167
211
|
{
|
|
168
212
|
id: "extension-review",
|
|
169
213
|
label: "Extension update reviews",
|
|
170
214
|
blurb: "The agent that reads an extension update before it is applied.",
|
|
171
215
|
kind: "run",
|
|
216
|
+
trigger: "unprompted",
|
|
172
217
|
icon: "box",
|
|
173
218
|
},
|
|
174
219
|
{
|
|
@@ -176,6 +221,7 @@ export const MODEL_ROLES = [
|
|
|
176
221
|
label: "Loop iterations",
|
|
177
222
|
blurb: "Each round of a loop working towards its goal.",
|
|
178
223
|
kind: "run",
|
|
224
|
+
trigger: "unprompted",
|
|
179
225
|
icon: "repeat",
|
|
180
226
|
},
|
|
181
227
|
{
|
|
@@ -183,6 +229,7 @@ export const MODEL_ROLES = [
|
|
|
183
229
|
label: "Watch wakes",
|
|
184
230
|
blurb: "The turn a watch starts when the thing it was watching happens.",
|
|
185
231
|
kind: "run",
|
|
232
|
+
trigger: "unprompted",
|
|
186
233
|
icon: "eye",
|
|
187
234
|
},
|
|
188
235
|
{
|
|
@@ -190,6 +237,7 @@ export const MODEL_ROLES = [
|
|
|
190
237
|
label: "Verify nudges",
|
|
191
238
|
blurb: "The follow-up turn sent when work was left unverified.",
|
|
192
239
|
kind: "run",
|
|
240
|
+
trigger: "unprompted",
|
|
193
241
|
icon: "search",
|
|
194
242
|
},
|
|
195
243
|
{
|
|
@@ -200,6 +248,7 @@ export const MODEL_ROLES = [
|
|
|
200
248
|
label: "Child agents",
|
|
201
249
|
blurb: "What an agent's own subagents run on when it names no model for them.",
|
|
202
250
|
kind: "run",
|
|
251
|
+
trigger: "unprompted",
|
|
203
252
|
icon: "users",
|
|
204
253
|
},
|
|
205
254
|
] as const satisfies readonly ModelRoleRow[];
|
|
@@ -215,10 +264,57 @@ export const MODEL_ROLE_IDS = MODEL_ROLES.map((role) => role.id) as readonly Mod
|
|
|
215
264
|
* silently ignored. */
|
|
216
265
|
export const ModelRoleSchema = z.enum(MODEL_ROLE_IDS as [ModelRole, ...ModelRole[]]);
|
|
217
266
|
|
|
218
|
-
|
|
267
|
+
/* ═══ THE BLOCKS THE SETTINGS PAGE READS IN ═══
|
|
268
|
+
*
|
|
269
|
+
* EIGHTEEN JOBS IN ONE LIST IS A TABLE, NOT A PAGE. The unit being the role is right and is not in question —
|
|
270
|
+
* it is what lets an owner pin Opus to commit subjects without pinning it to every session title — but the cost
|
|
271
|
+
* lands on whoever opens the page: one unbroken run of eighteen rows, each with a name, a sentence, a control
|
|
272
|
+
* and a list under it, with no landmark to say where you are in it or which rows are like the one you came for.
|
|
273
|
+
*
|
|
274
|
+
* SO THE BLOCKS ARE DECLARED, AND THEY ARE THE DISTINCTIONS THE TABLE ALREADY MAKES. Nothing here is a fresh
|
|
275
|
+
* taxonomy invented for the layout: `kind` was always the difference between a one-shot and a whole session,
|
|
276
|
+
* and `trigger` is the sentence the old table wrote in a comment about its own ordering. A block is what those
|
|
277
|
+
* two answers already separate, given a name and a heading.
|
|
278
|
+
*
|
|
279
|
+
* THE LABELS LIVE HERE, beside the role labels, for the same reason those do: a heading kept in a web-side map
|
|
280
|
+
* keyed by the same ids is the drift a single table exists to stop. What is NOT here is anything about how the
|
|
281
|
+
* page draws them — that is the page's, and it changes on its own schedule.
|
|
282
|
+
*
|
|
283
|
+
* ORDER IS REACH, and it is the order of the table itself: jobs nobody picked a model for, then sessions a
|
|
284
|
+
* click of yours starts, then sessions that start without one. Every role belongs to exactly one block and
|
|
285
|
+
* every block keeps the table's order, which is what makes a role added upstairs appear on the page by
|
|
286
|
+
* existing. */
|
|
287
|
+
export type ModelRoleBlockId = "helper" | "pressed" | "unprompted";
|
|
219
288
|
|
|
220
|
-
|
|
221
|
-
|
|
289
|
+
export interface ModelRoleBlock {
|
|
290
|
+
readonly id: ModelRoleBlockId;
|
|
291
|
+
/** The group's heading on the settings page. */
|
|
292
|
+
readonly label: string;
|
|
293
|
+
/** One line beside it: what the jobs in this block have in common that the others do not. */
|
|
294
|
+
readonly caption: string;
|
|
295
|
+
/** Its roles, in table order. */
|
|
296
|
+
readonly roles: readonly ModelRoleSpec[];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const rolesWhere = (match: (role: ModelRoleSpec) => boolean): readonly ModelRoleSpec[] => MODEL_ROLES.filter((role) => match(role));
|
|
222
300
|
|
|
223
|
-
|
|
224
|
-
|
|
301
|
+
export const MODEL_ROLE_BLOCKS: readonly ModelRoleBlock[] = [
|
|
302
|
+
{
|
|
303
|
+
id: "helper",
|
|
304
|
+
label: "Automatic helpers",
|
|
305
|
+
caption: "One prompt, no tools, one answer back.",
|
|
306
|
+
roles: rolesWhere((role) => role.kind === "helper"),
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: "pressed",
|
|
310
|
+
label: "Runs you start",
|
|
311
|
+
caption: "Whole sessions, begun by a click of yours.",
|
|
312
|
+
roles: rolesWhere((role) => role.kind === "run" && role.trigger === "pressed"),
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
id: "unprompted",
|
|
316
|
+
label: "Runs that start themselves",
|
|
317
|
+
caption: "Whole sessions nobody pressed for.",
|
|
318
|
+
roles: rolesWhere((role) => role.kind === "run" && role.trigger === "unprompted"),
|
|
319
|
+
},
|
|
320
|
+
];
|
|
@@ -264,7 +264,9 @@ export type WebchatMessage = z.infer<typeof WebchatMessageSchema>;
|
|
|
264
264
|
export const AutomationSchema = z.object({
|
|
265
265
|
id: entryId.describe("The automation's id."),
|
|
266
266
|
trigger: TriggerSchema.describe("What sets it off: a schedule, an event in the workspace, a message arriving from outside, or a webhook."),
|
|
267
|
-
|
|
267
|
+
/* Shell command run in the workspace root before waking; exit 0 ⇒ wake, non-zero ⇒ the run is "skipped".
|
|
268
|
+
* Its environment carries `AUTOMATION_ID` (this automation's own id, so a guard can look up what its past
|
|
269
|
+
* fires did) and, for a trigger that arrived with one, `AUTOMATION_PAYLOAD`. */
|
|
268
270
|
guard: z
|
|
269
271
|
.string()
|
|
270
272
|
.min(1)
|
|
@@ -279,7 +281,9 @@ export const AutomationSchema = z.object({
|
|
|
279
281
|
// field rather than a shared "public endpoint" bag: the two sources answer different questions (a chat's
|
|
280
282
|
// greeting and access model, an intake's dedup ceiling and ingest key) and a union of both would be a
|
|
281
283
|
// schema where most fields are wrong for whichever source is reading it.
|
|
282
|
-
issues: IssuesConfigSchema.optional().describe(
|
|
284
|
+
issues: IssuesConfigSchema.optional().describe(
|
|
285
|
+
"Settings for the bug reporter, for an automation that takes crash reports from your own sites and apps.",
|
|
286
|
+
),
|
|
283
287
|
/* NARROW THIS ONE JOB FURTHER than the persona it runs as, raw tool names, and the escape hatch under the
|
|
284
288
|
* shelves rather than the way anyone is expected to answer this question.
|
|
285
289
|
*
|
package/src/schemas/personas.ts
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
// Personas: the named faces a sandbox shows the outside world — which accounts each speaks for, what a
|
|
2
2
|
// session wearing one may do, and where it works.
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { type ModelSource, readyChain } from "../model-pins.js";
|
|
5
|
+
import { type ModelPin, ModelPinSchema } from "./agent.js";
|
|
4
6
|
import { entryId } from "./internal.js";
|
|
5
7
|
import { SkillDraftSchema, SkillNameSchema, SystemPromptModeSchema } from "./settings.js";
|
|
6
8
|
/* A NAMED PERSONA THE SANDBOX SHOWS THE OUTSIDE WORLD, "work-reddit", "the studio account", and the layer
|
|
7
9
|
* that decides which connected accounts a given turn may act through.
|
|
8
10
|
*
|
|
9
|
-
* IT ANSWERS
|
|
10
|
-
*
|
|
11
|
-
* whole of what an owner is deciding,
|
|
11
|
+
* IT ANSWERS FIVE QUESTIONS AND NO MORE: who it speaks as, what it may do, where it works, what it is told, and
|
|
12
|
+
* what it runs on (the repositories its tree holds, the models it runs on). Making one is then a name, a few
|
|
13
|
+
* accounts, some switches and, only if you want one, a prompt. That is the whole of what an owner is deciding,
|
|
14
|
+
* and short enough that they finish.
|
|
15
|
+
*
|
|
16
|
+
* A CARD IS THE SANDBOX'S ONE STATIC DESCRIPTION OF A WORKING POSTURE, and static is the design. Everything a
|
|
17
|
+
* session sees before the user's first word is a function of the card it wears: the accounts, the tool set the
|
|
18
|
+
* powers leave, the kit's prompt and skills, and now the tree it opens on. Two sessions on one card open on
|
|
19
|
+
* the same prefix, which is what lets a provider's prompt cache serve the second. The only per-task decision
|
|
20
|
+
* left is WHICH card, and that is a classification over the owner's own short list (`brief`), once per chat,
|
|
21
|
+
* which a cheap model does reliably; asking a model to compose a context per session instead either picks
|
|
22
|
+
* badly or costs a second strong run and a hand-off of everything it decided.
|
|
12
23
|
*
|
|
13
24
|
* NO PUBLISH-OR-DRAFT SWITCH. It read as a lock and was a sentence: it asked the turn to route outward things
|
|
14
25
|
* through the approvals queue and could not stop it posting. The queue is the mechanism, and a control whose
|
|
@@ -122,6 +133,28 @@ export const PersonaWorkspaceSchema = z.object({
|
|
|
122
133
|
folders: z.array(z.string().min(1)).max(50).optional().describe("Which folders it may touch at all. Absent means the whole workspace."),
|
|
123
134
|
});
|
|
124
135
|
export type PersonaWorkspace = z.infer<typeof PersonaWorkspaceSchema>;
|
|
136
|
+
/* WHAT A CONVERSATION WEARING THIS CARD CARRIES: which repositories its checkout HOLDS, as opposed to
|
|
137
|
+
* `workspace.folders`, which fences what the file tools may TOUCH inside a tree that holds everything.
|
|
138
|
+
*
|
|
139
|
+
* `repos` names NESTED repositories by their workspace-relative dir (the ids repo-discovery reports). The root
|
|
140
|
+
* repository is the workspace itself and is always carried, never listed, so an empty list is the workspace
|
|
141
|
+
* alone. ABSENT `context` is every repository the workspace has, which is what every card meant before this
|
|
142
|
+
* existed and what a card that has never thought about it should get. The sandbox brings a conversation's
|
|
143
|
+
* checkout to this list on every turn (agents/worktrees.ts `selection`): a repository the list names joins,
|
|
144
|
+
* one it stops naming leaves with its work committed to the branch.
|
|
145
|
+
*
|
|
146
|
+
* An OBJECT rather than a bare list, so the next kinds of thing a session can carry (a directory inside a
|
|
147
|
+
* repository through a sparse cone, a documents folder) arrive as fields beside this one rather than as a
|
|
148
|
+
* grammar inside it. */
|
|
149
|
+
export const PersonaContextSchema = z.object({
|
|
150
|
+
repos: z
|
|
151
|
+
.array(z.string().min(1).max(200))
|
|
152
|
+
.max(50)
|
|
153
|
+
.describe(
|
|
154
|
+
"Which nested repositories a conversation wearing this card carries, by workspace-relative path. The workspace itself is always carried; empty means the workspace alone.",
|
|
155
|
+
),
|
|
156
|
+
});
|
|
157
|
+
export type PersonaContext = z.infer<typeof PersonaContextSchema>;
|
|
125
158
|
export const PersonaSchema = z.object({
|
|
126
159
|
id: entryId.describe("The persona's id."),
|
|
127
160
|
// What the owner calls it in the composer chip. Absent ⇒ surfaces read the id, which is already human-chosen.
|
|
@@ -138,28 +171,40 @@ export const PersonaSchema = z.object({
|
|
|
138
171
|
.describe(
|
|
139
172
|
"Which connected accounts are its hands. Named individually rather than by site, because two accounts on one site is the whole problem this solves. Naming one that is not connected yet is not an error: it is a card describing an account this sandbox has still to sign into.",
|
|
140
173
|
),
|
|
141
|
-
/*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
174
|
+
/* WHAT THIS PERSONA IS FOR, in one line: the sentence a new chat is routed on (the sandbox's
|
|
175
|
+
* agent/persona-router.ts reads one line per card and names one) and the row's subtitle on the Personas
|
|
176
|
+
* page. Written by the owner rather than derived from the card, because a classifier reading "backend work
|
|
177
|
+
* on the api and billing services, never the marketing site" routes better than one reading a list of
|
|
178
|
+
* account ids, and because it is the one sentence a reader scanning the list wants under each name. Absent
|
|
179
|
+
* is a card the router can still name from what else the card says, with less to go on. */
|
|
180
|
+
brief: z
|
|
181
|
+
.string()
|
|
182
|
+
.max(200)
|
|
148
183
|
.optional()
|
|
149
|
-
.describe(
|
|
150
|
-
"Which repositories prefer this persona, so a conversation opened on one starts with the right choice already made. A preference rather than a fence.",
|
|
151
|
-
),
|
|
184
|
+
.describe("What this persona is for, in one line. A new chat is routed onto a persona by this sentence, and the Personas page shows it under the name."),
|
|
152
185
|
// What a session wearing this card may do, and where it works. Both absent ⇒ the full toolbox and the whole
|
|
153
186
|
// workspace, so a card written before these existed keeps behaving exactly as it did.
|
|
154
187
|
powers: PersonaPowersSchema.optional().describe(
|
|
155
188
|
"What a conversation wearing it may do. Absent means the full toolbox, so a card written before this existed behaves exactly as it did.",
|
|
156
189
|
),
|
|
157
190
|
workspace: PersonaWorkspaceSchema.optional().describe("Where it works. Absent means the whole workspace."),
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
191
|
+
context: PersonaContextSchema.optional().describe(
|
|
192
|
+
"Which part of the workspace a conversation wearing it carries: the repositories its checkout holds. Absent means every repository.",
|
|
193
|
+
),
|
|
194
|
+
/* WHICH MODELS A CONVERSATION WEARING THIS CARD RUNS ON, in order: the ladder shape every role list has
|
|
195
|
+
* (settings.modelRoles, model-pins.ts), for the same reason, a single pin is a single point of failure and
|
|
196
|
+
* the next entry catches an account that is spent today. ABSENT is the caller's own answer: the composer's
|
|
197
|
+
* pick for a chat, the run role's list for a turn a surface started. Present, the composer moves its model
|
|
198
|
+
* pill to the ladder's head when the card is picked (so a chat routed onto a card runs on that card's
|
|
199
|
+
* model as well as in its context), and the sandbox fills an unattended turn's silence from it before the
|
|
200
|
+
* run role's list (agent/turn-resume.ts). A model named on the turn itself always wins. */
|
|
201
|
+
models: z
|
|
202
|
+
.array(ModelPinSchema)
|
|
203
|
+
.max(10)
|
|
204
|
+
.optional()
|
|
205
|
+
.describe(
|
|
206
|
+
"Which models a conversation wearing it runs on, tried in order. Absent means whatever the chat or the job would have run on anyway; a model chosen for the turn itself always wins.",
|
|
207
|
+
),
|
|
163
208
|
/* WHICH SYSTEM PROMPT A SESSION WEARING THIS CARD RUNS ON, the same three bases the sandbox chooses
|
|
164
209
|
* between, asked per card. ABSENT is the fourth answer and the default: follow the sandbox, which is what
|
|
165
210
|
* every card meant before this field existed and what almost every card will go on meaning.
|
|
@@ -179,6 +224,28 @@ export const PersonaSchema = z.object({
|
|
|
179
224
|
systemPromptMode: SystemPromptModeSchema.optional(),
|
|
180
225
|
});
|
|
181
226
|
export type Persona = z.infer<typeof PersonaSchema>;
|
|
227
|
+
/* WHICH OF A CARD'S MODELS THIS SANDBOX CAN RUN, IN ORDER: the ladder filtered to connected providers and
|
|
228
|
+
* deduplicated — exactly the walk a role list gets, and now the SAME CALL rather than a sibling of one
|
|
229
|
+
* (model-pins.ts readyChain). It used to be "that walk minus the role's floor"; there is no floor any more, so
|
|
230
|
+
* the two lists ask one question. An absent ladder, or one whose every provider is disconnected, is empty
|
|
231
|
+
* here, and empty means the caller's own answer: a card that says nothing about models has not asked for a
|
|
232
|
+
* cheap one, it has left the question to whoever opened the chat. */
|
|
233
|
+
export const personaModels = (card: Pick<Persona, "models">, sources: readonly ModelSource[]): readonly ModelPin[] => readyChain(sources, card.models ?? []);
|
|
234
|
+
/* WHAT A NEW CHAT IS ROUTED ON, and what comes back. The composer asks once per settled draft on a chat that
|
|
235
|
+
* has no turns and no persona pinned by hand (its personaRoute composable), and the sandbox answers with the
|
|
236
|
+
* one card the message belongs to, or none. `folder` and `paths` are the two facts a card's `context` and
|
|
237
|
+
* `workspace.startIn` can be matched against and the words alone cannot supply. */
|
|
238
|
+
export const PersonaRouteAskSchema = z.object({
|
|
239
|
+
prompt: z.string().min(1).max(20000).describe("The message a new chat is about to open with."),
|
|
240
|
+
folder: z.string().max(200).optional().describe("The workspace folder the chat was opened in, when it was opened in one."),
|
|
241
|
+
paths: z.array(z.string().min(1).max(500)).max(50).default([]).describe("Workspace paths the message names: uploads, @-mentions, the editor's own file."),
|
|
242
|
+
});
|
|
243
|
+
export type PersonaRouteAsk = z.infer<typeof PersonaRouteAskSchema>;
|
|
244
|
+
export const PersonaRouteSchema = z.object({
|
|
245
|
+
persona: entryId.optional().describe("The card this message belongs to, or absent when none does and the chat should stay open to everything."),
|
|
246
|
+
reason: z.string().describe("Why, in the one line a chip can show. Present whether or not a card was named."),
|
|
247
|
+
});
|
|
248
|
+
export type PersonaRoute = z.infer<typeof PersonaRouteSchema>;
|
|
182
249
|
/* THE ONE CARD ID THE PRODUCT NAMES ITSELF, the read-only persona a public web chat answers through.
|
|
183
250
|
*
|
|
184
251
|
* Nothing else is stock: a fresh workspace has no personas at all, and every card on the Personas page is one
|
package/src/schemas/settings.ts
CHANGED
|
@@ -307,17 +307,21 @@ export const SandboxSettingsSchema = z.object({
|
|
|
307
307
|
"Keep the instructions identical between turns so the provider can cache them, moving anything that varies into the message instead. Cheaper, at the cost of some flexibility.",
|
|
308
308
|
),
|
|
309
309
|
skills: z.array(z.string()).default(["lsp", "fileq"]).describe("Which skills are switched on."),
|
|
310
|
-
/*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
310
|
+
/* WHETHER A NEW CHAT IS ROUTED ONTO A PERSONA, and how far the answer goes without a press. The router is
|
|
311
|
+
* the `persona-router` helper role (model-roles.ts): it reads the first message and one line per card and
|
|
312
|
+
* names one card or none, once per chat, before the first turn (the sandbox's agent/persona-router.ts).
|
|
313
|
+
* off — never asked. A chat wears the persona the user picked, or none.
|
|
314
|
+
* suggest — asked, and the answer is a chip on the composer that the user presses to apply. The default:
|
|
315
|
+
* a persona takes accounts and repositories AWAY from a chat, and the first time that happens
|
|
316
|
+
* it should happen because somebody pressed it.
|
|
317
|
+
* auto — the answer is applied when the message is sent, unless the chip was dismissed first.
|
|
318
|
+
* Attended chats only, whatever this says: a wake nobody is watching names its persona on its own form,
|
|
319
|
+
* and routing one onto a card would GRANT it accounts the owner never named for it. */
|
|
320
|
+
personaRouting: z
|
|
321
|
+
.enum(["off", "suggest", "auto"])
|
|
322
|
+
.default("suggest")
|
|
319
323
|
.describe(
|
|
320
|
-
"
|
|
324
|
+
"Whether a new chat is matched to one of your personas from its first message. Suggest shows the match on the composer for you to press; auto applies it when you send unless you dismiss it first. Never applies to unwatched runs, which name their persona themselves.",
|
|
321
325
|
),
|
|
322
326
|
hashlineEdits: z
|
|
323
327
|
.boolean()
|
|
@@ -478,12 +482,13 @@ export const SandboxSettingsSchema = z.object({
|
|
|
478
482
|
* that is connected and will not answer today: the account's allowance went on the chat, and one spent
|
|
479
483
|
* provider takes that job down for hours while the others sit idle.
|
|
480
484
|
*
|
|
481
|
-
* AN ABSENT OR EMPTY LIST IS THE
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
485
|
+
* AN ABSENT OR EMPTY LIST IS THE JOB SWITCHED OFF, and nothing is derived to fill it. A one-shot helper
|
|
486
|
+
* used to fall to an "Auto ladder" worked out from whatever was connected, which meant a sandbox nobody
|
|
487
|
+
* had configured still spent an account on every commit subject, every session title and every safety
|
|
488
|
+
* verdict, on a ranking this repo invented and re-ranked whenever an account was added. Not set now means
|
|
489
|
+
* not set: no auto-selection and no recommendation. A one-shot with no list does not run; a whole session
|
|
490
|
+
* with no list opens on the model the owner picked for their own chat, which is a choice they made rather
|
|
491
|
+
* than one this schema guessed. */
|
|
487
492
|
// `partialRecord`, not `record`: an exhaustive one would make every role a required key, so a settings file
|
|
488
493
|
// that has never been touched would have to spell out seventeen empty arrays to be valid, and adding a role
|
|
489
494
|
// would invalidate every settings file in existence. An absent key IS the answer "this role has no list".
|
|
@@ -491,7 +496,7 @@ export const SandboxSettingsSchema = z.object({
|
|
|
491
496
|
.partialRecord(ModelRoleSchema, z.array(ModelPinSchema).max(10))
|
|
492
497
|
.default({})
|
|
493
498
|
.describe(
|
|
494
|
-
"Which models do which job, one ordered list per job: commit messages, session titles, the safety judge, pipeline fixes, and every other place this sandbox picks a model for you. Tried in order, so one spent account does not take a job down.
|
|
499
|
+
"Which models do which job, one ordered list per job: commit messages, session titles, the safety judge, pipeline fixes, and every other place this sandbox picks a model for you. Tried in order, so one spent account does not take a job down. Nothing is chosen for you: a one-shot job with no list does not run, and a whole session with no list opens on whatever your own chat is set to.",
|
|
495
500
|
),
|
|
496
501
|
/* WHICH REPOS KEEP A CHANGELOG, the repos whose commits carry a `Release-Note:` trailer, written by the
|
|
497
502
|
* same quick model that drafts the subject (git/commit-message.ts) and harvested at release time.
|
|
@@ -380,7 +380,6 @@ describe(`VERSIONED_STATE_PATHS`, () => {
|
|
|
380
380
|
`${STATE_DIR}/config/capability-dismissals.json`,
|
|
381
381
|
// The context shelves: which repositories a conversation opened on one carries. A list of names, and
|
|
382
382
|
// the decision about what a session may see, which is what a review is for.
|
|
383
|
-
`${STATE_DIR}/config/context/`,
|
|
384
383
|
/* The two entries the AGENT authors on its own initiative, and the reason `versioned` is not read as
|
|
385
384
|
* config-only. Both are the sandbox acting outward: a draft publishes words under the owner's name,
|
|
386
385
|
* a workspace extension is code that runs in the app and can serve HTTP with the workspace under
|
package/src/workspace-state.ts
CHANGED
|
@@ -738,12 +738,6 @@ const STATE_FILES = [
|
|
|
738
738
|
* behaves, it holds no credential, and it belongs in a pull request, which is also what makes it
|
|
739
739
|
* searchable, since every versioned entry already is. */
|
|
740
740
|
{ path: ".intentic/config/personas/", invalidates: ["personas"], portability: "carry", versioned: true },
|
|
741
|
-
|
|
742
|
-
/* THE CONTEXT SHELVES, one JSON file per shelf (schemas/context.ts): which part of the workspace a
|
|
743
|
-
* conversation opened on it carries. `versioned` and `carry` on the persona card's own argument, a shelf is
|
|
744
|
-
* a list of item ids and holds no credential, and which repositories a session can see is exactly the kind
|
|
745
|
-
* of decision that belongs in a pull request. */
|
|
746
|
-
{ path: ".intentic/config/context/", invalidates: ["context"], portability: "carry", versioned: true },
|
|
747
741
|
] as const satisfies readonly WorkspaceStateFile[];
|
|
748
742
|
|
|
749
743
|
export const WORKSPACE_STATE_FILES: readonly WorkspaceStateFile[] = STATE_FILES;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
export declare const CONTEXT_ITEM_KINDS: readonly ["repo"];
|
|
3
|
-
export type ContextItemKind = (typeof CONTEXT_ITEM_KINDS)[number];
|
|
4
|
-
export declare const ContextItemIdSchema: z.ZodString;
|
|
5
|
-
export type ContextItemId = z.infer<typeof ContextItemIdSchema>;
|
|
6
|
-
export declare const contextItem: (id: ContextItemId) => {
|
|
7
|
-
readonly kind: ContextItemKind;
|
|
8
|
-
readonly name: string;
|
|
9
|
-
};
|
|
10
|
-
export declare const ContextCapsSchema: z.ZodObject<{
|
|
11
|
-
repos: z.ZodOptional<z.ZodNumber>;
|
|
12
|
-
}, z.core.$strip>;
|
|
13
|
-
export type ContextCaps = z.infer<typeof ContextCapsSchema>;
|
|
14
|
-
export declare const ContextShelfSchema: z.ZodObject<{
|
|
15
|
-
id: z.ZodString;
|
|
16
|
-
label: z.ZodOptional<z.ZodString>;
|
|
17
|
-
pinned: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
18
|
-
allowed: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
19
|
-
denied: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
20
|
-
caps: z.ZodOptional<z.ZodObject<{
|
|
21
|
-
repos: z.ZodOptional<z.ZodNumber>;
|
|
22
|
-
}, z.core.$strip>>;
|
|
23
|
-
}, z.core.$strip>;
|
|
24
|
-
export type ContextShelf = z.infer<typeof ContextShelfSchema>;
|
|
25
|
-
export declare const ContextCompositionSchema: z.ZodObject<{
|
|
26
|
-
shelf: z.ZodOptional<z.ZodString>;
|
|
27
|
-
items: z.ZodArray<z.ZodString>;
|
|
28
|
-
}, z.core.$strip>;
|
|
29
|
-
export type ContextComposition = z.infer<typeof ContextCompositionSchema>;
|
|
30
|
-
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/schemas/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoBxB,eAAO,MAAM,kBAAkB,YAAI,MAAM,CAAU,CAAC;AACpD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAKlE,eAAO,MAAM,mBAAmB,aAK0I,CAAC;AAC3K,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAGhE,eAAO,MAAM,WAAW,OAAQ,aAAa,KAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAGtG,CAAC;AAMF,eAAO,MAAM,iBAAiB;;iBAE5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAa5D,eAAO,MAAM,kBAAkB;;;;;;;;;iBAW7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAS9D,eAAO,MAAM,wBAAwB;;;iBAGnC,CAAC;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
|
package/dist/schemas/context.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { entryId } from "./internal.js";
|
|
3
|
-
export const CONTEXT_ITEM_KINDS = ["repo"];
|
|
4
|
-
const ITEM = /^(repo):[a-zA-Z0-9][a-zA-Z0-9._-]*(\/[a-zA-Z0-9][a-zA-Z0-9._-]*)*$/;
|
|
5
|
-
export const ContextItemIdSchema = z
|
|
6
|
-
.string()
|
|
7
|
-
.min(1)
|
|
8
|
-
.max(200)
|
|
9
|
-
.regex(ITEM)
|
|
10
|
-
.describe("One thing in the workspace a conversation can carry, as `<kind>:<name>`. Today the kind is `repo` and the name is a repository's workspace-relative path.");
|
|
11
|
-
export const contextItem = (id) => {
|
|
12
|
-
const at = id.indexOf(":");
|
|
13
|
-
return { kind: id.slice(0, at), name: id.slice(at + 1) };
|
|
14
|
-
};
|
|
15
|
-
export const ContextCapsSchema = z.object({
|
|
16
|
-
repos: z.number().int().min(0).optional().describe("How many repositories a composition may hold. Absent means as many as the shelf allows."),
|
|
17
|
-
});
|
|
18
|
-
export const ContextShelfSchema = z.object({
|
|
19
|
-
id: entryId.describe("The shelf's id, which is also its file name."),
|
|
20
|
-
label: z.string().max(60).optional().describe("What to call it on screen. Absent falls back to the id."),
|
|
21
|
-
pinned: z.array(ContextItemIdSchema).max(200).default([]).describe("Items every composition from this shelf carries."),
|
|
22
|
-
allowed: z
|
|
23
|
-
.array(ContextItemIdSchema)
|
|
24
|
-
.max(500)
|
|
25
|
-
.default([])
|
|
26
|
-
.describe("Items a composition may carry, in the order they are loaded and shed. The order is the priority; nothing else is."),
|
|
27
|
-
denied: z.array(ContextItemIdSchema).max(200).default([]).describe("Items no composition from this shelf carries, whatever else says so."),
|
|
28
|
-
caps: ContextCapsSchema.optional().describe("How many of each kind a composition may hold."),
|
|
29
|
-
});
|
|
30
|
-
export const ContextCompositionSchema = z.object({
|
|
31
|
-
shelf: entryId.optional().describe("Which shelf this pick was made from. Absent when the items were set without one."),
|
|
32
|
-
items: z.array(ContextItemIdSchema).max(500).describe("What the conversation carries, in shelf order. The root repository is always carried and never listed."),
|
|
33
|
-
});
|
|
34
|
-
//# sourceMappingURL=context.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/schemas/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAmBxC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAAM,CAAU,CAAC;AAKpD,MAAM,IAAI,GAAG,oEAAoE,CAAC;AAClF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC;KAC/B,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,CAAC;KACN,GAAG,CAAC,GAAG,CAAC;KACR,KAAK,CAAC,IAAI,CAAC;KACX,QAAQ,CAAC,2JAA2J,CAAC,CAAC;AAI3K,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,EAAiB,EAA6D,EAAE;IACxG,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAoB,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;AAChF,CAAC,CAAC;AAMF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yFAAyF,CAAC;CAChJ,CAAC,CAAC;AAcH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,8CAA8C,CAAC;IACpE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yDAAyD,CAAC;IACxG,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,kDAAkD,CAAC;IACtH,OAAO,EAAE,CAAC;SACL,KAAK,CAAC,mBAAmB,CAAC;SAC1B,GAAG,CAAC,GAAG,CAAC;SACR,OAAO,CAAC,EAAE,CAAC;SACX,QAAQ,CAAC,mHAAmH,CAAC;IAClI,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,sEAAsE,CAAC;IAC1I,IAAI,EAAE,iBAAiB,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,+CAA+C,CAAC;CAC/F,CAAC,CAAC;AAUH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kFAAkF,CAAC;IACtH,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,wGAAwG,CAAC;CAClK,CAAC,CAAC"}
|