@intentic/sandbox-contract 1.246.1 → 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/batch-runs.d.ts +2 -0
- package/dist/batch-runs.d.ts.map +1 -1
- package/dist/batch-runs.js +1 -0
- package/dist/batch-runs.js.map +1 -1
- package/dist/contracts/agent.contract.d.ts +20 -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 +109 -12
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/usage.contract.d.ts +22 -0
- package/dist/contracts/usage.contract.d.ts.map +1 -1
- package/dist/contracts/usage.contract.js +19 -0
- package/dist/contracts/usage.contract.js.map +1 -1
- package/dist/definition.d.ts +20 -24
- package/dist/definition.d.ts.map +1 -1
- package/dist/fast-tier.js +1 -1
- package/dist/fast-tier.js.map +1 -1
- package/dist/index.d.ts +191 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/model-pins.d.ts +15 -0
- package/dist/model-pins.d.ts.map +1 -0
- package/dist/model-pins.js +22 -0
- package/dist/model-pins.js.map +1 -0
- package/dist/model-roles.d.ts +172 -0
- package/dist/model-roles.d.ts.map +1 -0
- package/dist/model-roles.js +167 -0
- package/dist/model-roles.js.map +1 -0
- package/dist/schemas/agent.d.ts +22 -2
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +4 -2
- package/dist/schemas/agent.js.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/plan-limits.d.ts +20 -0
- package/dist/schemas/plan-limits.d.ts.map +1 -1
- package/dist/schemas/plan-limits.js +21 -0
- package/dist/schemas/plan-limits.js.map +1 -1
- package/dist/schemas/settings.d.ts +88 -6
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +19 -23
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/usage.d.ts +5 -0
- package/dist/schemas/usage.d.ts.map +1 -1
- package/dist/schemas/usage.js +5 -0
- package/dist/schemas/usage.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/agent-catalog.ts +1 -1
- package/src/batch-runs.test.ts +10 -5
- package/src/batch-runs.ts +10 -3
- 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/contracts/usage.contract.ts +31 -0
- package/src/events.ts +1 -1
- package/src/fast-tier.test.ts +1 -1
- package/src/fast-tier.ts +5 -5
- package/src/index.ts +2 -3
- package/src/model-pins.test.ts +121 -0
- package/src/model-pins.ts +132 -0
- package/src/model-roles.test.ts +52 -0
- package/src/model-roles.ts +320 -0
- package/src/plan-pools.ts +1 -1
- package/src/prompt-complexity.test.ts +1 -1
- package/src/prompt-complexity.ts +2 -2
- package/src/provider-specs.test.ts +1 -1
- package/src/schemas/agent.ts +42 -17
- package/src/schemas/agents.ts +2 -2
- package/src/schemas/automations.ts +6 -2
- package/src/schemas/personas.ts +85 -18
- package/src/schemas/plan-limits.ts +50 -0
- package/src/schemas/settings.ts +92 -98
- package/src/schemas/usage.ts +59 -0
- package/src/workspace-state.test.ts +0 -1
- package/src/workspace-state.ts +0 -6
- package/dist/agent-run-model.d.ts +0 -4
- package/dist/agent-run-model.d.ts.map +0 -1
- package/dist/agent-run-model.js +0 -13
- package/dist/agent-run-model.js.map +0 -1
- package/dist/quick-model.d.ts +0 -15
- package/dist/quick-model.d.ts.map +0 -1
- package/dist/quick-model.js +0 -39
- package/dist/quick-model.js.map +0 -1
- 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/agent-run-model.test.ts +0 -76
- package/src/agent-run-model.ts +0 -65
- package/src/quick-model.test.ts +0 -158
- package/src/quick-model.ts +0 -162
- package/src/schemas/context.ts +0 -87
package/src/schemas/agent.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { ModelRoleSchema } from "../model-roles.js";
|
|
2
3
|
import { NATIVE_PROVIDERS } from "../provider-specs.js";
|
|
3
4
|
import { AgentPlacementSchema } from "../runner-protocol.js";
|
|
4
5
|
import { entryId } from "./internal.js";
|
|
@@ -267,6 +268,23 @@ export const AgentTurnSchema = z
|
|
|
267
268
|
.describe(
|
|
268
269
|
"Whether this turn's work merges into the workspace when it finishes. Overrides the conversation's own setting for this turn only.",
|
|
269
270
|
),
|
|
271
|
+
/* WHAT STARTED THIS TURN, when it was not a person at a composer, and therefore which of the owner's
|
|
272
|
+
* model lists answers for it (settings.modelRoles, model-roles.ts declares the roles).
|
|
273
|
+
*
|
|
274
|
+
* IT NAMES THE JOB, NOT THE TIER. Before this field there was one list for every unattended turn, so a
|
|
275
|
+
* production incident and a documentation sweep were configured together, and a surface added tomorrow
|
|
276
|
+
* inherited that tier by saying nothing. Now a starter says what it IS, and the owner gets to answer
|
|
277
|
+
* per job: cheap for the sweep, frontier for the incident.
|
|
278
|
+
*
|
|
279
|
+
* ONLY FILLS A SILENCE. The daemon applies the role's list to a turn that named no model AND no
|
|
280
|
+
* provider (turn-resume.ts): a caret pick, an acceptance run's own choice, a workflow step pinned to a
|
|
281
|
+
* provider — all of those already answered the question, and this must not overrule them.
|
|
282
|
+
*
|
|
283
|
+
* A `run` role, always. The `helper` roles never reach here: a one-shot is not a turn, it has no
|
|
284
|
+
* conversation and no worktree, and it asks its own role directly at the seam that spends it. */
|
|
285
|
+
runRole: ModelRoleSchema.optional().describe(
|
|
286
|
+
"What started this turn, when it was not a person typing: which of the sandbox's per-job model lists answers for it. Only used when the turn names no model of its own.",
|
|
287
|
+
),
|
|
270
288
|
// Set ONLY by the daemon's own automation dispatchers: this turn opens a conversation on behalf of an
|
|
271
289
|
// outside message rather than a user. Recorded on the registry entry so the fleet can say where the
|
|
272
290
|
// agent came from. Requires conversationId, there is nothing to record it on otherwise.
|
|
@@ -311,7 +329,7 @@ export const AgentTurnSchema = z
|
|
|
311
329
|
* opposite defaults, the chat wants the provider's own catalog default, an unattended run wants the
|
|
312
330
|
* tier its owner chose for work that spends money while they are not watching.
|
|
313
331
|
*
|
|
314
|
-
* The daemon fills `agent`/`model` and the pinned entry's own knobs from
|
|
332
|
+
* The daemon fills `agent`/`model` and the pinned entry's own knobs from the turn's own role list for any turn that says
|
|
315
333
|
* this and names none of them (startConversationTurn), walking that list until one can actually be
|
|
316
334
|
* started. Naming one still wins: every surface-started run now carries a caret that overrides the list
|
|
317
335
|
* for that run alone, and Acceptance picks per run because it fans a session out per story. Either way
|
|
@@ -416,14 +434,14 @@ export type AgentTurn = z.infer<typeof AgentTurnSchema>;
|
|
|
416
434
|
* Shared rather than re-declared per route because every surface that starts an agent for the user now carries
|
|
417
435
|
* that caret, and they must all mean the same thing by it: the pair rides onto the turn as `agent`/`model`, and
|
|
418
436
|
* the daemon's own fill step then leaves it alone (turn-resume.ts fills only what is absent). ABSENT is the
|
|
419
|
-
* ordinary case and the one to keep cheap, nobody touched the caret, so `
|
|
437
|
+
* ordinary case and the one to keep cheap, nobody touched the caret, so the turn's `runRole` list answers.
|
|
420
438
|
*
|
|
421
439
|
* Both halves or neither, because a model id is only meaningful to the provider that vends it: half a pick
|
|
422
440
|
* would send a Codex model id to Claude. Routes that accept this pass it through verbatim; a model this build
|
|
423
441
|
* has never heard of is a supported pick, since the picker offers a custom-id escape hatch.
|
|
424
442
|
*
|
|
425
443
|
* AND THE TIER IT RUNS AT, because naming a model is only half of what the standing setting says. A pinned
|
|
426
|
-
* entry carries its own effort (
|
|
444
|
+
* entry carries its own effort (ModelPinSchema), and the daemon applies the pin's knobs ONLY to a turn that
|
|
427
445
|
* named no model (turn-resume.ts): so a caret that could re-point the model but not the tier moved every
|
|
428
446
|
* override onto the provider's own default effort, and the one moment somebody reaches for the caret is the
|
|
429
447
|
* failure that just beat the standing order. Optional, and absent means absent, the turn goes out without an
|
|
@@ -442,29 +460,36 @@ export const AgentRunPickSchema = z
|
|
|
442
460
|
})
|
|
443
461
|
.optional();
|
|
444
462
|
export type AgentRunPick = z.infer<typeof AgentRunPickSchema>;
|
|
445
|
-
/*
|
|
446
|
-
*
|
|
463
|
+
/* ONE ENTRY OF ONE ROLE'S MODEL LIST (settings.modelRoles): the standing version of the pick above, and not
|
|
464
|
+
* merely which model but HOW it is to be run.
|
|
447
465
|
*
|
|
448
|
-
* THE KNOBS RIDE THE ENTRY RATHER THAN THE LIST, which is the whole reason this is an object
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
466
|
+
* THE KNOBS RIDE THE ENTRY RATHER THAN THE LIST, which is the whole reason this is an object rather than a
|
|
467
|
+
* `${provider}:${model}` string. The reasoning effort was once a single field beside a list, so one tier
|
|
468
|
+
* answered for every model in it — and the entries of such a list are deliberately NOT interchangeable: it is a
|
|
469
|
+
* frontier pin with the cheap account underneath that catches it when the first is spent. A tier scale is a
|
|
470
|
+
* property of the MODEL as well ('max' is off Kimi's scale entirely, and off Claude's own the moment thinking
|
|
453
471
|
* is switched off), so a shared effort was either off-scale for half the list or the lowest common rung for all
|
|
454
|
-
* of it. Each entry
|
|
472
|
+
* of it. Each entry carries what the composer's picker configures for the turn in front of you.
|
|
473
|
+
*
|
|
474
|
+
* THE SAME SHAPE FOR EVERY ROLE, one-shot helpers included, and that is a deliberate widening. A commit
|
|
475
|
+
* message or a session title used to be pinnable by model alone, on the argument that the daemon runs those
|
|
476
|
+
* with reasoning off and no effort, so a control for either would be a switch with nothing behind it. True of
|
|
477
|
+
* the machinery, and it made the machinery the argument: an owner who pins a reasoning model to their commit
|
|
478
|
+
* subjects was paying that model's price to have its distinguishing feature suppressed. The knobs now travel
|
|
479
|
+
* through the one-shot path too, so an entry means the same thing wherever it is written.
|
|
455
480
|
*
|
|
456
|
-
* EVERY FIELD BUT THE PAIR IS OPTIONAL, AND ABSENT MEANS ABSENT: the
|
|
481
|
+
* EVERY FIELD BUT THE PAIR IS OPTIONAL, AND ABSENT MEANS ABSENT: the work goes out without the field and the
|
|
457
482
|
* provider's own default answers, exactly as an unconfigured pin always did. Nothing here invents a "low".
|
|
458
483
|
*
|
|
459
484
|
* NO TIER HOLD, and its absence is the rule rather than an omission: automatic tier selection gates on
|
|
460
|
-
* `unattended` (prompt-complexity.ts), so a
|
|
461
|
-
*
|
|
485
|
+
* `unattended` (prompt-complexity.ts), so a role-started run is never downgraded in the first place and a veto
|
|
486
|
+
* over it would be a control whose state can make no difference to anything.
|
|
462
487
|
*
|
|
463
488
|
* The pair is BOTH HALVES for the reason the pick above is: a model id is only meaningful to the provider that
|
|
464
489
|
* vends it, so half a pin would send a Codex id to Claude. Taken verbatim, never validated against a catalog:
|
|
465
490
|
* the picker offers a custom-id escape hatch, so a model this build has never heard of is a supported pin. */
|
|
466
|
-
export const
|
|
467
|
-
provider: AgentProviderSchema.describe("Which provider serves
|
|
491
|
+
export const ModelPinSchema = z.object({
|
|
492
|
+
provider: AgentProviderSchema.describe("Which provider serves this work."),
|
|
468
493
|
model: z.string().min(1).describe("Which of its models. Both halves, because a model name only means anything to the provider that serves it."),
|
|
469
494
|
effort: z
|
|
470
495
|
.string()
|
|
@@ -474,7 +499,7 @@ export const AgentRunPinSchema = z.object({
|
|
|
474
499
|
fast: z.boolean().optional().describe("Ask for this model's work at a higher rate for a higher price. A request rather than a promise."),
|
|
475
500
|
harness: AgentHarnessSchema.optional().describe("Which agentic loop runs it. Leave it out to use the provider's own."),
|
|
476
501
|
});
|
|
477
|
-
export type
|
|
502
|
+
export type ModelPin = z.infer<typeof ModelPinSchema>;
|
|
478
503
|
// POST /agent's ack: the daemon-minted id of the detached turn run it started. The turn executes daemon-side
|
|
479
504
|
// regardless of any client connection; every window, the initiator included, renders it via /agent/attach.
|
|
480
505
|
export const StartedTurnSchema = z.object({
|
package/src/schemas/agents.ts
CHANGED
|
@@ -182,8 +182,8 @@ export const LandedMessageSchema = z.object({
|
|
|
182
182
|
.describe("What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions."),
|
|
183
183
|
});
|
|
184
184
|
export type LandedMessage = z.infer<typeof LandedMessageSchema>;
|
|
185
|
-
/* ONE MODEL'S TURN IN THE DRAFTING WALK, asked, and what became of the ask. The
|
|
186
|
-
* connected models in order (agent/
|
|
185
|
+
/* ONE MODEL'S TURN IN THE DRAFTING WALK, asked, and what became of the ask. The one-shot helper chain tries the
|
|
186
|
+
* connected models in order (agent/role-model.ts), and each rung ends one of four ways:
|
|
187
187
|
* asking , in flight right now; `ms` absent because it is still being spent.
|
|
188
188
|
* answered, it wrote the sentence, in `ms`.
|
|
189
189
|
* refused , it failed or declined, in `ms`, with its own words in `reason`.
|
|
@@ -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
|
|
@@ -58,6 +58,56 @@ export const AccountUsageSchema = z.object({
|
|
|
58
58
|
measuredAt: z.number(),
|
|
59
59
|
});
|
|
60
60
|
export type AccountUsage = z.infer<typeof AccountUsageSchema>;
|
|
61
|
+
/* THE ONE WAY PAST A SPENT SESSION WINDOW THAT IS NOT WAITING, which Anthropic grants once a week per account.
|
|
62
|
+
*
|
|
63
|
+
* Every other affordance around a refused turn is about WHEN: arm the appointment, count down to the reset,
|
|
64
|
+
* press when it opens. This one moves the clock. The provider reopens the five-hour window immediately and
|
|
65
|
+
* charges the account one of its weekly resets; the WEEKLY allowance is untouched and still binds, so this
|
|
66
|
+
* buys back the session pool and nothing else. Upstream's own CLI spells it `/limit-reset`.
|
|
67
|
+
*
|
|
68
|
+
* THE ANSWER IS THE PROVIDER'S, NEVER OURS. There is no rule here to re-derive: eligibility turns on the plan
|
|
69
|
+
* tier, how long the account has existed, whether it is actually at the wall, whether another experiment holds
|
|
70
|
+
* it, and whether the week's reset is already spent — all of it decided server-side and none of it visible from
|
|
71
|
+
* a usage reading. So this shape is a transcription of what the endpoint said, and the button exists only while
|
|
72
|
+
* it says `available`. A client must never infer availability from a 100% window.
|
|
73
|
+
*
|
|
74
|
+
* `reason` is the provider's own word for the refusal ("tier", "tenure", "not_at_wall", "weekly_limit",
|
|
75
|
+
* "already_used", …) and is carried rather than translated, because the set is the provider's to extend and a
|
|
76
|
+
* word we don't recognise is still worth showing to somebody asking why the button is not there. */
|
|
77
|
+
export const LimitResetStatusSchema = z.object({
|
|
78
|
+
available: z
|
|
79
|
+
.boolean()
|
|
80
|
+
.describe("Whether the provider will reopen this account's session window right now. The only thing a button may be drawn from."),
|
|
81
|
+
reason: z
|
|
82
|
+
.string()
|
|
83
|
+
.optional()
|
|
84
|
+
.describe("Why not, in the provider's own word, when it gave one. Absent when it is available, or when the provider said nothing."),
|
|
85
|
+
// Both epoch SECONDS, matching every other reset instant on the wire (UsageWindow.resetsAt, limitResetsAt).
|
|
86
|
+
nextAvailableAt: z
|
|
87
|
+
.number()
|
|
88
|
+
.optional()
|
|
89
|
+
.describe("When the next reset may be claimed, in epoch seconds, where the provider publishes it. Absent means unknown, never 'now'."),
|
|
90
|
+
weeklyResetsAt: z.number().optional().describe("When the weekly allowance itself reopens, in epoch seconds, where the provider publishes it."),
|
|
91
|
+
});
|
|
92
|
+
export type LimitResetStatus = z.infer<typeof LimitResetStatusSchema>;
|
|
93
|
+
/* WHAT CLAIMING IT DID, in the provider's own vocabulary plus the two failures that are ours.
|
|
94
|
+
*
|
|
95
|
+
* `reset` is the only outcome that changed anything, and the caller's cue to send the held turn again. The rest
|
|
96
|
+
* are all "nothing happened", and they are kept APART rather than folded into one failure because they are read
|
|
97
|
+
* by somebody who just pressed a button and is owed the difference: `already_used` means come back next week,
|
|
98
|
+
* `not_limited` means the window reopened while they were reading, `ineligible` means this account never had
|
|
99
|
+
* it, and `unavailable`/`error` mean try again. Collapsing them would make every one of those read as a fault.
|
|
100
|
+
*
|
|
101
|
+
* Never throws over the wire: a claim that fails leaves the account exactly as it was, and the honest answer to
|
|
102
|
+
* a press is a word, not a stack trace. */
|
|
103
|
+
export const LimitResetClaimSchema = z.object({
|
|
104
|
+
result: z
|
|
105
|
+
.enum(["reset", "already_used", "not_limited", "ineligible", "unavailable", "error"])
|
|
106
|
+
.describe("What the provider did. Only `reset` reopened the window; every other value means nothing changed."),
|
|
107
|
+
nextAvailableAt: z.number().optional().describe("When another reset may be claimed, in epoch seconds, where the provider published it."),
|
|
108
|
+
detail: z.string().optional().describe("What went wrong, in words, for the two outcomes that are this sandbox's fault rather than the plan's."),
|
|
109
|
+
});
|
|
110
|
+
export type LimitResetClaim = z.infer<typeof LimitResetClaimSchema>;
|
|
61
111
|
/* THE LAST TIME A PROVIDER ACTUALLY REFUSED A TURN, the other half of "can I run on this", and the half no
|
|
62
112
|
* meter can supply.
|
|
63
113
|
*
|