@intentic/sandbox-contract 1.240.0 → 1.242.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/README.md +52 -16
- package/dist/agent-catalog.d.ts +5 -28
- package/dist/agent-catalog.d.ts.map +1 -1
- package/dist/agent-catalog.js +21 -157
- package/dist/agent-catalog.js.map +1 -1
- package/dist/agent-run-model.d.ts +3 -2
- package/dist/agent-run-model.d.ts.map +1 -1
- package/dist/agent-run-model.js +9 -6
- package/dist/agent-run-model.js.map +1 -1
- package/dist/agent-runtimes.d.ts +27 -0
- package/dist/agent-runtimes.d.ts.map +1 -0
- package/dist/agent-runtimes.js +113 -0
- package/dist/agent-runtimes.js.map +1 -0
- package/dist/approvals-execution.d.ts +11 -0
- package/dist/approvals-execution.d.ts.map +1 -0
- package/dist/approvals-execution.js +44 -0
- package/dist/approvals-execution.js.map +1 -0
- package/dist/arrival.d.ts +151 -0
- package/dist/arrival.d.ts.map +1 -0
- package/dist/arrival.js +56 -0
- package/dist/arrival.js.map +1 -0
- package/dist/card-status.d.ts +5 -0
- package/dist/card-status.d.ts.map +1 -0
- package/dist/card-status.js +74 -0
- package/dist/card-status.js.map +1 -0
- package/dist/chores/chores.d.ts.map +1 -1
- package/dist/chores/chores.js +1 -1
- package/dist/chores/chores.js.map +1 -1
- package/dist/command-classes.d.ts +0 -1
- package/dist/command-classes.d.ts.map +1 -1
- package/dist/command-classes.js +9 -2
- package/dist/command-classes.js.map +1 -1
- package/dist/command-run.d.ts +10 -0
- package/dist/command-run.d.ts.map +1 -0
- package/dist/command-run.js +46 -0
- package/dist/command-run.js.map +1 -0
- package/dist/contracts/agent.contract.d.ts +991 -771
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.d.ts +345 -337
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +20 -2
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/approvals.contract.d.ts +99 -0
- package/dist/contracts/approvals.contract.d.ts.map +1 -0
- package/dist/contracts/approvals.contract.js +32 -0
- package/dist/contracts/approvals.contract.js.map +1 -0
- package/dist/contracts/ci.contract.d.ts +1 -4
- package/dist/contracts/ci.contract.d.ts.map +1 -1
- package/dist/contracts/ci.contract.js +1 -9
- package/dist/contracts/ci.contract.js.map +1 -1
- package/dist/contracts/claude.contract.d.ts +9 -0
- package/dist/contracts/claude.contract.d.ts.map +1 -1
- package/dist/contracts/cursor.contract.d.ts +6 -0
- package/dist/contracts/cursor.contract.d.ts.map +1 -1
- package/dist/contracts/git.contract.d.ts +63 -1
- package/dist/contracts/git.contract.d.ts.map +1 -1
- package/dist/contracts/git.contract.js +22 -4
- package/dist/contracts/git.contract.js.map +1 -1
- package/dist/contracts/grok.contract.d.ts +3 -0
- package/dist/contracts/grok.contract.d.ts.map +1 -1
- package/dist/contracts/keys.contract.d.ts +81 -0
- package/dist/contracts/keys.contract.d.ts.map +1 -0
- package/dist/contracts/keys.contract.js +51 -0
- package/dist/contracts/keys.contract.js.map +1 -0
- package/dist/contracts/panels.contract.d.ts +7 -0
- package/dist/contracts/panels.contract.d.ts.map +1 -1
- package/dist/contracts/prepush.contract.js +2 -2
- package/dist/contracts/providers.contract.d.ts +2 -0
- package/dist/contracts/providers.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.d.ts +84 -86
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/safety.contract.d.ts +35 -0
- package/dist/contracts/safety.contract.d.ts.map +1 -0
- package/dist/contracts/safety.contract.js +32 -0
- package/dist/contracts/safety.contract.js.map +1 -0
- package/dist/contracts/sessions.contract.d.ts +73 -337
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +30 -32
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +139 -337
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/translator.contract.d.ts +31 -3
- package/dist/contracts/translator.contract.d.ts.map +1 -1
- package/dist/contracts/usage.contract.d.ts +29 -20
- package/dist/contracts/usage.contract.d.ts.map +1 -1
- package/dist/contracts/usage.contract.js +13 -0
- package/dist/contracts/usage.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +8 -0
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/conversation-ids.d.ts +2 -0
- package/dist/conversation-ids.d.ts.map +1 -1
- package/dist/conversation-ids.js +8 -0
- package/dist/conversation-ids.js.map +1 -1
- package/dist/credential-material.d.ts +1 -0
- package/dist/credential-material.d.ts.map +1 -1
- package/dist/credential-material.js +24 -2
- package/dist/credential-material.js.map +1 -1
- package/dist/definition.d.ts +51 -93
- package/dist/definition.d.ts.map +1 -1
- package/dist/definition.js +6 -28
- package/dist/definition.js.map +1 -1
- package/dist/events.d.ts +1831 -2285
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +155 -55
- package/dist/events.js.map +1 -1
- package/dist/history-state.d.ts.map +1 -1
- package/dist/history-state.js +1 -0
- package/dist/history-state.js.map +1 -1
- package/dist/index.d.ts +1959 -1903
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -5
- package/dist/index.js.map +1 -1
- package/dist/ingress-protocol.d.ts +13 -1
- package/dist/ingress-protocol.d.ts.map +1 -1
- package/dist/ingress-protocol.js +19 -0
- package/dist/ingress-protocol.js.map +1 -1
- package/dist/mentions.d.ts +3 -0
- package/dist/mentions.d.ts.map +1 -0
- package/dist/mentions.js +13 -0
- package/dist/mentions.js.map +1 -0
- package/dist/needs-action.d.ts +7 -0
- package/dist/needs-action.d.ts.map +1 -0
- package/dist/needs-action.js +3 -0
- package/dist/needs-action.js.map +1 -0
- package/dist/overlay-lint.d.ts +7 -0
- package/dist/overlay-lint.d.ts.map +1 -0
- package/dist/overlay-lint.js +68 -0
- package/dist/overlay-lint.js.map +1 -0
- package/dist/owner-ticket.d.ts +18 -0
- package/dist/owner-ticket.d.ts.map +1 -0
- package/dist/owner-ticket.js +51 -0
- package/dist/owner-ticket.js.map +1 -0
- package/dist/plan-pools.d.ts +12 -0
- package/dist/plan-pools.d.ts.map +1 -0
- package/dist/plan-pools.js +30 -0
- package/dist/plan-pools.js.map +1 -0
- package/dist/provider-specs.d.ts +228 -0
- package/dist/provider-specs.d.ts.map +1 -0
- package/dist/provider-specs.js +124 -0
- package/dist/provider-specs.js.map +1 -0
- package/dist/quick-model.d.ts.map +1 -1
- package/dist/quick-model.js +2 -1
- package/dist/quick-model.js.map +1 -1
- package/dist/runner-protocol.js +2 -2
- package/dist/runner-protocol.js.map +1 -1
- package/dist/runtime-state.d.ts +2 -2
- package/dist/runtime-state.d.ts.map +1 -1
- package/dist/runtime-state.js +1 -1
- package/dist/runtime-state.js.map +1 -1
- package/dist/safety-policy.d.ts +49 -0
- package/dist/safety-policy.d.ts.map +1 -0
- package/dist/safety-policy.js +52 -0
- package/dist/safety-policy.js.map +1 -0
- package/dist/schemas/agent.d.ts +21 -3
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +18 -8
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/agents.d.ts +19 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +15 -0
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/approvals.d.ts +241 -0
- package/dist/schemas/approvals.d.ts.map +1 -0
- package/dist/schemas/approvals.js +63 -0
- package/dist/schemas/approvals.js.map +1 -0
- package/dist/schemas/automations.d.ts +5 -0
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/ci.d.ts +5 -9
- package/dist/schemas/ci.d.ts.map +1 -1
- package/dist/schemas/ci.js +3 -10
- package/dist/schemas/ci.js.map +1 -1
- package/dist/schemas/computers.d.ts +31 -5
- package/dist/schemas/computers.d.ts.map +1 -1
- package/dist/schemas/computers.js +7 -5
- package/dist/schemas/computers.js.map +1 -1
- package/dist/schemas/environment.d.ts +11 -131
- package/dist/schemas/environment.d.ts.map +1 -1
- package/dist/schemas/environment.js +5 -42
- package/dist/schemas/environment.js.map +1 -1
- package/dist/schemas/git-history.d.ts +4 -0
- package/dist/schemas/git-history.d.ts.map +1 -1
- package/dist/schemas/git.d.ts +179 -0
- package/dist/schemas/git.d.ts.map +1 -1
- package/dist/schemas/git.js +39 -1
- package/dist/schemas/git.js.map +1 -1
- package/dist/schemas/panels.d.ts +21 -0
- package/dist/schemas/panels.d.ts.map +1 -1
- package/dist/schemas/panels.js +5 -0
- package/dist/schemas/panels.js.map +1 -1
- package/dist/schemas/plan-limits.d.ts +50 -3
- package/dist/schemas/plan-limits.d.ts.map +1 -1
- package/dist/schemas/plan-limits.js +11 -6
- package/dist/schemas/plan-limits.js.map +1 -1
- package/dist/schemas/provider-oauth.d.ts +6 -0
- package/dist/schemas/provider-oauth.d.ts.map +1 -1
- package/dist/schemas/provider-subscriptions.d.ts +2 -1
- package/dist/schemas/provider-subscriptions.d.ts.map +1 -1
- package/dist/schemas/provider-subscriptions.js +2 -1
- package/dist/schemas/provider-subscriptions.js.map +1 -1
- package/dist/schemas/settings.d.ts +29 -16
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +13 -20
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/system.d.ts +3 -0
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +5 -1
- package/dist/schemas/system.js.map +1 -1
- package/dist/schemas/workspace-repos.d.ts +14 -0
- package/dist/schemas/workspace-repos.d.ts.map +1 -1
- package/dist/schemas/workspace-repos.js +5 -0
- package/dist/schemas/workspace-repos.js.map +1 -1
- package/dist/schemas/workspace-tree.d.ts +1 -0
- package/dist/schemas/workspace-tree.d.ts.map +1 -1
- package/dist/schemas/workspace-tree.js +3 -0
- package/dist/schemas/workspace-tree.js.map +1 -1
- package/dist/transcript-fold.d.ts +31 -0
- package/dist/transcript-fold.d.ts.map +1 -0
- package/dist/transcript-fold.js +457 -0
- package/dist/transcript-fold.js.map +1 -0
- package/dist/workspace-state.d.ts +20 -3
- package/dist/workspace-state.d.ts.map +1 -1
- package/dist/workspace-state.js +22 -2
- package/dist/workspace-state.js.map +1 -1
- package/package.json +27 -5
- package/src/agent-catalog.test.ts +1 -1
- package/src/agent-catalog.ts +76 -555
- package/src/agent-run-model.test.ts +31 -15
- package/src/agent-run-model.ts +26 -12
- package/src/agent-runtimes.ts +419 -0
- package/src/approvals-execution.ts +96 -0
- package/src/arrival.ts +160 -0
- package/src/capability-ledger.test.ts +6 -4
- package/src/card-status.ts +93 -0
- package/src/chores/chores.ts +6 -3
- package/src/command-classes.test.ts +25 -0
- package/src/command-classes.ts +47 -5
- package/src/command-run.ts +78 -0
- package/src/contract-lock.test.ts +1 -1
- package/src/contract-lock.ts +2 -2
- package/src/contracts/agents.contract.ts +35 -2
- package/src/contracts/approvals.contract.ts +37 -0
- package/src/contracts/ci.contract.ts +0 -13
- package/src/contracts/git.contract.ts +28 -3
- package/src/contracts/keys.contract.ts +79 -0
- package/src/contracts/prepush.contract.ts +3 -3
- package/src/contracts/safety.contract.ts +48 -0
- package/src/contracts/usage.contract.ts +22 -0
- package/src/conversation-ids.test.ts +31 -1
- package/src/conversation-ids.ts +37 -3
- package/src/credential-material.test.ts +69 -1
- package/src/credential-material.ts +86 -5
- package/src/definition.ts +25 -53
- package/src/events.ts +374 -173
- package/src/history-state.ts +6 -0
- package/src/index.ts +24 -5
- package/src/ingress-protocol.ts +52 -1
- package/src/mentions.test.ts +22 -0
- package/src/mentions.ts +25 -0
- package/src/needs-action.ts +14 -0
- package/src/overlay-lint.test.ts +89 -0
- package/src/overlay-lint.ts +116 -0
- package/src/owner-ticket.test.ts +54 -0
- package/src/owner-ticket.ts +102 -0
- package/src/plan-pools.test.ts +71 -0
- package/src/plan-pools.ts +82 -0
- package/src/provider-specs.test.ts +130 -0
- package/src/provider-specs.ts +352 -0
- package/src/quick-model.ts +9 -5
- package/src/runner-protocol.ts +2 -2
- package/src/runtime-state.ts +6 -6
- package/src/safety-policy.ts +165 -0
- package/src/schemas/agent.ts +62 -17
- package/src/schemas/agents.ts +71 -0
- package/src/schemas/approvals.ts +174 -0
- package/src/schemas/ci.ts +19 -26
- package/src/schemas/computers.ts +52 -11
- package/src/schemas/environment.ts +21 -100
- package/src/schemas/git.ts +121 -6
- package/src/schemas/issues.ts +1 -1
- package/src/schemas/panels.ts +20 -0
- package/src/schemas/plan-limits.ts +58 -11
- package/src/schemas/provider-subscriptions.ts +7 -2
- package/src/schemas/settings.ts +67 -93
- package/src/schemas/system.ts +12 -1
- package/src/schemas/workspace-repos.ts +9 -0
- package/src/schemas/workspace-tree.ts +11 -0
- package/src/transcript-fold.test.ts +493 -0
- package/src/transcript-fold.ts +655 -0
- package/src/workspace-state.test.ts +77 -29
- package/src/workspace-state.ts +99 -31
- package/dist/contracts/drafts.contract.d.ts +0 -57
- package/dist/contracts/drafts.contract.d.ts.map +0 -1
- package/dist/contracts/drafts.contract.js +0 -32
- package/dist/contracts/drafts.contract.js.map +0 -1
- package/dist/definition-action.d.ts +0 -7
- package/dist/definition-action.d.ts.map +0 -1
- package/dist/definition-action.js +0 -3
- package/dist/definition-action.js.map +0 -1
- package/dist/publish-drafts.d.ts +0 -7
- package/dist/publish-drafts.d.ts.map +0 -1
- package/dist/publish-drafts.js +0 -20
- package/dist/publish-drafts.js.map +0 -1
- package/dist/schemas/drafts.d.ts +0 -84
- package/dist/schemas/drafts.d.ts.map +0 -1
- package/dist/schemas/drafts.js +0 -44
- package/dist/schemas/drafts.js.map +0 -1
- package/src/contracts/drafts.contract.ts +0 -35
- package/src/definition-action.ts +0 -8
- package/src/publish-drafts.ts +0 -59
- package/src/schemas/drafts.ts +0 -108
package/src/events.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { AgentHarnessSchema, AgentProviderSchema, PermissionModeSchema } from ".
|
|
|
3
3
|
import { AgentSummarySchema, LandConflictSchema } from "./schemas/agents.js";
|
|
4
4
|
import { RateLimitInfoSchema } from "./schemas/claude-gate.js";
|
|
5
5
|
import { FastModeStateSchema } from "./schemas/fast-mode.js";
|
|
6
|
-
import { AgentReplySchema, UsageWindowSchema } from "./schemas/plan-limits.js";
|
|
6
|
+
import { AccountUsageSchema, AgentReplySchema, ProviderRefusalSchema, UsageWindowSchema } from "./schemas/plan-limits.js";
|
|
7
7
|
import { ShareDetailSchema } from "./schemas/share.js";
|
|
8
8
|
import { MemberRoleSchema } from "./schemas/shared.js";
|
|
9
9
|
import { SubagentKindSchema, SubagentStatusSchema, SubagentVerificationSchema } from "./schemas/terminal.js";
|
|
@@ -45,7 +45,11 @@ export type AskQuestion = z.infer<typeof AskQuestionSchema>;
|
|
|
45
45
|
export const ProgramAskSchema = z.object({
|
|
46
46
|
text: z.string().describe("What would run."),
|
|
47
47
|
language: z.enum(["bash", "javascript"]).describe("Which of the two backends it is written for, named as the grammar that colours it."),
|
|
48
|
-
truncated: z
|
|
48
|
+
truncated: z
|
|
49
|
+
.boolean()
|
|
50
|
+
.describe(
|
|
51
|
+
"Whether this is an excerpt of a longer program, so the card can say so instead of ending mid-word. An excerpt always carries the flagged fragment: the beginning, then a window around the fragment, with any skipped middle written into the text as a bracketed count.",
|
|
52
|
+
),
|
|
49
53
|
spans: z
|
|
50
54
|
.array(z.object({ start: z.number().int().nonnegative(), end: z.number().int().nonnegative() }))
|
|
51
55
|
.describe("Which fragments of the text put it in the class that held it. Offsets into text, in order, never overlapping."),
|
|
@@ -76,11 +80,16 @@ export const PermissionAskSchema = z.object({
|
|
|
76
80
|
program: ProgramAskSchema.optional().describe(
|
|
77
81
|
"The program this card is holding, when the card is about one. Present on a command gate's card and absent on every other permission ask.",
|
|
78
82
|
),
|
|
83
|
+
/* THE JUDGE'S OWN SENTENCE, and on a command card it is the reason the card exists rather than a note added
|
|
84
|
+
* to it: the judge read the owner's policy and the program and decided this needed asking, and this is what
|
|
85
|
+
* it decided. Written by the quick model from the program text and the policy, never by the agent being
|
|
86
|
+
* gated — a card whose persuasive half was authored by the thing it is stopping argues for its own approval,
|
|
87
|
+
* and the turns that raise cards are exactly the ones whose account of themselves may be a stranger's. */
|
|
79
88
|
explain: z
|
|
80
89
|
.string()
|
|
81
90
|
.optional()
|
|
82
91
|
.describe(
|
|
83
|
-
"One plain sentence saying what the program does and why
|
|
92
|
+
"One plain sentence saying what the program does and why it is being asked about. Written by the judge that read your safety policy, never by the agent being gated.",
|
|
84
93
|
),
|
|
85
94
|
});
|
|
86
95
|
export type PermissionAsk = z.infer<typeof PermissionAskSchema>;
|
|
@@ -312,7 +321,7 @@ export type CardDocument = z.infer<typeof CardDocumentSchema>;
|
|
|
312
321
|
|
|
313
322
|
/* ONE CARD'S OWN FIELDS, spelled once. Three readers carry the same card and must agree on what it is: the
|
|
314
323
|
* frame that raises it (AgentEventSchema below), the journal entry that keeps a parked one across a restart
|
|
315
|
-
* (ParkedCardSchema), and the record row that keeps it for good (
|
|
324
|
+
* (ParkedCardSchema), and the record row that keeps it for good (TranscriptRowSchema's card fields). A shape
|
|
316
325
|
* declared inline in each was three shapes with one name. */
|
|
317
326
|
const REQUEST_ID = z.string().describe("What to send back when you answer.");
|
|
318
327
|
const planCard = {
|
|
@@ -395,72 +404,83 @@ const PermissionCardSchema = PermissionAskSchema.extend({
|
|
|
395
404
|
export const ParkedCardSchema = z.discriminatedUnion("kind", [PlanCardSchema, QuestionCardSchema, PermissionCardSchema]);
|
|
396
405
|
export type ParkedCard = z.infer<typeof ParkedCardSchema>;
|
|
397
406
|
|
|
398
|
-
// ----
|
|
399
|
-
/* THE CARDS A TURN PARKED ON, as
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
* long as the turn's frame log lived (minutes) and in the browser's local mirror for as long as that survived
|
|
405
|
-
* (until the next web build), and then the record repainted the conversation without it: the card, the
|
|
406
|
-
* questions, and the user's own picks, gone from the only durable copy. The rule the record follows is that a
|
|
407
|
-
* reopened chat redraws what was on screen, and a decision the user made is the part of a conversation they
|
|
408
|
-
* come back to re-read.
|
|
407
|
+
// ---- transcript cards ----
|
|
408
|
+
/* THE CARDS A TURN PARKED ON, as a transcript row carries them: the card exactly as it was raised, how it was
|
|
409
|
+
* settled, and whatever landed on it afterwards (a permission's late explanation, an offer's stream and
|
|
410
|
+
* receipt). One shape for the live row and the recorded one, because they are the same row: the daemon folds
|
|
411
|
+
* the turn's frames into these rows as they stream (transcript-fold.ts) and writes the same rows down when the
|
|
412
|
+
* turn settles, so a chat reopened tomorrow is the chat that was on screen.
|
|
409
413
|
*
|
|
410
|
-
* The
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
export const
|
|
421
|
-
export type
|
|
422
|
-
export const
|
|
423
|
-
export type
|
|
424
|
-
//
|
|
425
|
-
|
|
426
|
-
export
|
|
427
|
-
export
|
|
428
|
-
|
|
429
|
-
export const
|
|
430
|
-
export type
|
|
431
|
-
export const
|
|
414
|
+
* The STATUS is settled by the fold, from the reply that released the card (card-status.ts), and rides the
|
|
415
|
+
* row rather than the reply it came from: every reader wants the verdict, and the one derivation lives beside
|
|
416
|
+
* the fold that applies it. `pending` is a card the turn is still parked on; `cancelled` is nobody answering,
|
|
417
|
+
* the turn stopped or died under the card, which is not a decision and does not read back as one. */
|
|
418
|
+
export const PlanStatusSchema = z.enum(["pending", "approved", "rejected", "cancelled"]);
|
|
419
|
+
export type PlanStatus = z.infer<typeof PlanStatusSchema>;
|
|
420
|
+
export const QuestionStatusSchema = z.enum(["pending", "answered", "cancelled"]);
|
|
421
|
+
export type QuestionStatus = z.infer<typeof QuestionStatusSchema>;
|
|
422
|
+
export const PermissionStatusSchema = z.enum(["pending", "allowed", "always", "denied", "cancelled"]);
|
|
423
|
+
export type PermissionStatus = z.infer<typeof PermissionStatusSchema>;
|
|
424
|
+
export const HelpStatusSchema = z.enum(["pending", "helped", "declined", "cancelled"]);
|
|
425
|
+
export type HelpStatus = z.infer<typeof HelpStatusSchema>;
|
|
426
|
+
export const OfferStatusSchema = z.enum(["pending", "approved", "skipped", "cancelled"]);
|
|
427
|
+
export type OfferStatus = z.infer<typeof OfferStatusSchema>;
|
|
428
|
+
// A yes settles the DECISION, not the ask: the owner is now setting the capability up, so the card moves to
|
|
429
|
+
// `connecting` and stays there until the capability_outcome frame says how the setup ended.
|
|
430
|
+
export const CapabilityOfferStatusSchema = z.enum(["pending", "connecting", "skipped", "cancelled"]);
|
|
431
|
+
export type CapabilityOfferStatus = z.infer<typeof CapabilityOfferStatusSchema>;
|
|
432
|
+
|
|
433
|
+
export const TranscriptPlanSchema = z.object({ ...planCard, status: PlanStatusSchema.describe("Where the decision stands.") });
|
|
434
|
+
export type TranscriptPlan = z.infer<typeof TranscriptPlanSchema>;
|
|
435
|
+
export const TranscriptQuestionSchema = z.object({
|
|
436
|
+
...questionCard,
|
|
437
|
+
status: QuestionStatusSchema.describe("Where the answer stands."),
|
|
438
|
+
answers: z
|
|
439
|
+
.record(z.string(), z.array(z.string()))
|
|
440
|
+
.optional()
|
|
441
|
+
.describe("What was chosen, keyed by the question, with the chosen labels or the user's own words."),
|
|
442
|
+
});
|
|
443
|
+
export type TranscriptQuestion = z.infer<typeof TranscriptQuestionSchema>;
|
|
444
|
+
// `explain`, the judge's sentence, lands here through PermissionAskSchema; it is on the card from the moment
|
|
445
|
+
// it is raised, so nothing patches it in afterwards.
|
|
446
|
+
export const TranscriptPermissionSchema = PermissionAskSchema.extend({ ...permissionCard, status: PermissionStatusSchema.describe("Where the decision stands.") });
|
|
447
|
+
export type TranscriptPermission = z.infer<typeof TranscriptPermissionSchema>;
|
|
448
|
+
export const TranscriptBrowserHelpSchema = z.object({ ...browserHelpCard, status: HelpStatusSchema.describe("How the hand-over ended.") });
|
|
449
|
+
export type TranscriptBrowserHelp = z.infer<typeof TranscriptBrowserHelpSchema>;
|
|
450
|
+
export const TranscriptTerminalHelpSchema = z.object({ ...terminalHelpCard, status: HelpStatusSchema.describe("How the hand-over ended.") });
|
|
451
|
+
export type TranscriptTerminalHelp = z.infer<typeof TranscriptTerminalHelpSchema>;
|
|
452
|
+
export const TranscriptServiceOfferSchema = z.object({
|
|
432
453
|
...serviceOfferCard,
|
|
433
|
-
|
|
454
|
+
status: OfferStatusSchema.describe("Where the decision stands."),
|
|
434
455
|
events: z.array(ServiceStreamEventSchema).optional().describe("The approved run's stream, in order (the service_event frames)."),
|
|
435
456
|
receipt: ServiceReceiptSchema.optional().describe("How the approved run ended (the service_receipt frame)."),
|
|
436
457
|
});
|
|
437
|
-
export type
|
|
438
|
-
export const
|
|
458
|
+
export type TranscriptServiceOffer = z.infer<typeof TranscriptServiceOfferSchema>;
|
|
459
|
+
export const TranscriptCapabilityOfferSchema = z.object({
|
|
439
460
|
...capabilityOfferCard,
|
|
440
|
-
|
|
461
|
+
status: CapabilityOfferStatusSchema.describe("Where the decision stands."),
|
|
441
462
|
outcome: CapabilityOutcomeSchema.optional().describe("How an accepted ask's setup ended (the capability_outcome frame)."),
|
|
442
463
|
});
|
|
443
|
-
export type
|
|
444
|
-
export const
|
|
464
|
+
export type TranscriptCapabilityOffer = z.infer<typeof TranscriptCapabilityOfferSchema>;
|
|
465
|
+
export const TranscriptPaymentOfferSchema = z.object({
|
|
445
466
|
...paymentOfferCard,
|
|
446
|
-
|
|
467
|
+
status: OfferStatusSchema.describe("Where the decision stands."),
|
|
447
468
|
receipt: PaymentReceiptSchema.optional().describe("How the approved payment ended (the payment_receipt frame)."),
|
|
448
469
|
});
|
|
449
|
-
export type
|
|
470
|
+
export type TranscriptPaymentOffer = z.infer<typeof TranscriptPaymentOfferSchema>;
|
|
450
471
|
|
|
451
|
-
// ----
|
|
452
|
-
// What
|
|
453
|
-
//
|
|
454
|
-
//
|
|
455
|
-
//
|
|
456
|
-
//
|
|
457
|
-
//
|
|
472
|
+
// ---- transcript rows ----
|
|
473
|
+
// What a conversation is made of, on every surface: the rows the daemon folds a turn's frames into as they
|
|
474
|
+
// stream (the live chat renders these, patched as they grow), the rows the record keeps once the turn settles,
|
|
475
|
+
// and the rows /agents/{id}/transcript replays into a reopened tab. One shape because it is one thing: a
|
|
476
|
+
// reopened chat REDRAWS the transcript the user was looking at rather than paraphrasing it, so a row keeps the
|
|
477
|
+
// assistant's thinking and the tool cards its block ran, which is also what lets a runtime handoff carry more
|
|
478
|
+
// than bare prose across to a replacement session (see runtime-history.ts).
|
|
458
479
|
//
|
|
459
|
-
// One
|
|
460
|
-
//
|
|
461
|
-
//
|
|
462
|
-
|
|
463
|
-
export const RestoredToolCallSchema: z.ZodType<RestoredToolCall> = z.lazy(() =>
|
|
480
|
+
// One tool card. A subagent's own calls and its thinking nest under the Agent card that spawned them, so a
|
|
481
|
+
// delegation reads as one unit instead of a flat run of siblings. z.lazy because the shape refers to itself: a
|
|
482
|
+
// subagent that delegates nests one level deeper.
|
|
483
|
+
export const TranscriptToolSchema: z.ZodType<TranscriptTool> = z.lazy(() =>
|
|
464
484
|
z.object({
|
|
465
485
|
id: z.string().describe("The call's id."),
|
|
466
486
|
name: z.string().describe("Which tool."),
|
|
@@ -472,17 +492,40 @@ export const RestoredToolCallSchema: z.ZodType<RestoredToolCall> = z.lazy(() =>
|
|
|
472
492
|
locations: z.array(ToolCallLocationSchema).optional().describe("The files it touched."),
|
|
473
493
|
content: z.array(ToolCallContentSchema).optional().describe("What it produced: text, a change to a file, or a picture."),
|
|
474
494
|
children: z
|
|
475
|
-
.array(
|
|
495
|
+
.array(TranscriptToolSchema)
|
|
476
496
|
.optional()
|
|
477
497
|
.describe(
|
|
478
498
|
"Calls a delegated helper made, nested under the call that started it, so a reopened conversation redraws the delegation rather than collapsing it into one result.",
|
|
479
499
|
),
|
|
480
500
|
thinking: z.string().optional().describe("What the agent was reasoning about around this call."),
|
|
501
|
+
subagent: TranscriptSubagentSchema.optional().describe(
|
|
502
|
+
"The helper this call started, as the daemon's registry sees it: what it is, how it is going, what it has spent. What a card can say about a backgrounded child whose result is minutes away.",
|
|
503
|
+
),
|
|
481
504
|
}),
|
|
482
505
|
);
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
506
|
+
/* THE CHILD A CALL STARTED, on the card whose id the `subagent`/`subagent_update` frames name (that call's own),
|
|
507
|
+
* so no correlation is needed: an Agent card wears its subagent's live state, and a Bash card that turned out
|
|
508
|
+
* to be a `codex exec` wears its delegate's. The identifying fields arrive once (the `subagent` frame), the
|
|
509
|
+
* moving ones (status, spend, what it is doing) replace as each update lands. */
|
|
510
|
+
export const TranscriptSubagentSchema = z.object({
|
|
511
|
+
kind: SubagentKindSchema,
|
|
512
|
+
agentType: z.string().optional(),
|
|
513
|
+
description: z.string().optional(),
|
|
514
|
+
model: z.string().optional(),
|
|
515
|
+
provider: z.string().optional(),
|
|
516
|
+
background: z.boolean().optional(),
|
|
517
|
+
status: SubagentStatusSchema,
|
|
518
|
+
tokens: z.number().optional(),
|
|
519
|
+
toolUses: z.number().optional(),
|
|
520
|
+
lastTool: z.string().optional(),
|
|
521
|
+
summary: z.string().optional(),
|
|
522
|
+
error: z.string().optional(),
|
|
523
|
+
verification: SubagentVerificationSchema.optional(),
|
|
524
|
+
});
|
|
525
|
+
export type TranscriptSubagent = z.infer<typeof TranscriptSubagentSchema>;
|
|
526
|
+
// Mutable, unlike most of this file: the fold settles a card IN PLACE when its result arrives turns later
|
|
527
|
+
// (transcript-fold.ts's `cards` map, readWorkspaceSession's `awaiting`), which is what saves it a second pass.
|
|
528
|
+
export interface TranscriptTool {
|
|
486
529
|
id: string;
|
|
487
530
|
name: string;
|
|
488
531
|
category: ToolKind;
|
|
@@ -490,8 +533,9 @@ export interface RestoredToolCall {
|
|
|
490
533
|
target?: string | undefined;
|
|
491
534
|
locations?: ToolCallLocation[] | undefined;
|
|
492
535
|
content?: ToolCallContent[] | undefined;
|
|
493
|
-
children?:
|
|
536
|
+
children?: TranscriptTool[] | undefined;
|
|
494
537
|
thinking?: string | undefined;
|
|
538
|
+
subagent?: TranscriptSubagent | undefined;
|
|
495
539
|
}
|
|
496
540
|
|
|
497
541
|
/* ONE NOTE THE DAEMON PUT IN FRONT OF A USER'S MESSAGE, as both audiences see it: the model reads `text`, and
|
|
@@ -503,15 +547,26 @@ export const TurnNoteSchema = z.object({
|
|
|
503
547
|
});
|
|
504
548
|
export type TurnNote = z.infer<typeof TurnNoteSchema>;
|
|
505
549
|
|
|
506
|
-
//
|
|
507
|
-
//
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
550
|
+
// End-of-turn accounting (assistant rows only, the last bubble of a turn): what the turn cost, attached where
|
|
551
|
+
// the answer ended so a reader can see what each exchange spent.
|
|
552
|
+
export const TranscriptUsageSchema = z.object({
|
|
553
|
+
costUsd: z.number().optional(),
|
|
554
|
+
inputTokens: z.number().optional(),
|
|
555
|
+
outputTokens: z.number().optional(),
|
|
556
|
+
durationMs: z.number().optional(),
|
|
557
|
+
numTurns: z.number().optional(),
|
|
558
|
+
});
|
|
559
|
+
export type TranscriptUsage = z.infer<typeof TranscriptUsageSchema>;
|
|
560
|
+
|
|
561
|
+
// One row. Each block of the agent's prose is its own, with the tool cards that block introduced, which is what
|
|
562
|
+
// reproduces the way a turn actually unfolded rather than collapsing its whole narration into one bubble with
|
|
563
|
+
// every tool hanging off the end.
|
|
564
|
+
export const TranscriptRowSchema = z.object({
|
|
565
|
+
/* `notice` is neither side of the conversation: it is something that HAPPENED to the turn, a refusal, a
|
|
566
|
+
* landed delta, a compaction, a stop, written down so a reopened conversation says it too. The one that
|
|
567
|
+
* matters most is a refused turn: a provider that answers "your organization has disabled Claude
|
|
568
|
+
* subscription access" produced no assistant text, so a transcript of the two speakers alone ends on the
|
|
569
|
+
* user's message and reads as broken. */
|
|
515
570
|
role: z
|
|
516
571
|
.enum(["user", "assistant", "notice"])
|
|
517
572
|
.describe(
|
|
@@ -533,28 +588,36 @@ export const RestoredMessageSchema = z.object({
|
|
|
533
588
|
.describe(
|
|
534
589
|
"When it was sent, in milliseconds. On the user's rows only, because that is the only moment actually known: a turn's own frames arrive with no clock, so stamping the agent's rows could only ever mean the whole turn's start or end.",
|
|
535
590
|
),
|
|
536
|
-
// Files the user attached to this turn (user
|
|
537
|
-
//
|
|
591
|
+
// Files the user attached to this turn (user rows only) as workspace-relative paths, the uploads alone:
|
|
592
|
+
// a path @-mentioned inline in the text is already visible there and is not drawn as a chip.
|
|
538
593
|
attachments: z.array(z.string()).optional().describe("Files attached to this message, as workspace paths."),
|
|
539
|
-
/* The checkpoint this message can be rewound to (user
|
|
540
|
-
*
|
|
541
|
-
*
|
|
594
|
+
/* The checkpoint this message can be rewound to (user rows only), and where this message sits in the
|
|
595
|
+
* conversation's record, which is what the rewind route addresses it by. Never stored: both are stamped
|
|
596
|
+
* onto the live row by the turn's own `checkpoint` frame and onto a replayed row by the read that serves
|
|
597
|
+
* it, looked up from the daemon's rewind points, which a rewind rewrites, so a reopened tab offers exactly
|
|
598
|
+
* the turns that are still there to go back to. */
|
|
542
599
|
checkpointId: z
|
|
543
600
|
.string()
|
|
544
601
|
.optional()
|
|
545
602
|
.describe(
|
|
546
603
|
"The saved point this message can be rewound to. Looked up on each read rather than stored, so what is offered is exactly what is still there to go back to.",
|
|
547
604
|
),
|
|
605
|
+
rewindIndex: z
|
|
606
|
+
.number()
|
|
607
|
+
.int()
|
|
608
|
+
.nonnegative()
|
|
609
|
+
.optional()
|
|
610
|
+
.describe("This message's position in the conversation's record, which is how a rewind names it. Present only beside a checkpoint."),
|
|
548
611
|
thinking: z.string().optional().describe("What the agent was reasoning about."),
|
|
549
|
-
tools: z.array(
|
|
612
|
+
tools: z.array(TranscriptToolSchema).optional().describe("The tool calls this part of the turn made."),
|
|
613
|
+
todos: z.array(TodoItemSchema).optional().describe("The agent's task checklist, as of this bubble."),
|
|
614
|
+
usage: TranscriptUsageSchema.optional().describe("What the turn cost, on the bubble its answer ended in."),
|
|
550
615
|
/* What the daemon added to this turn's message (user rows only), the same notes the live `preamble` frame
|
|
551
|
-
* carries
|
|
552
|
-
* (sessions/turn-transcript.ts); only a conversation adopted from a provider's session store recovers them
|
|
553
|
-
* by parsing the composed prompt kept there, the one store the daemon never wrote typed.
|
|
616
|
+
* carries, read off that frame by the fold.
|
|
554
617
|
*
|
|
555
618
|
* On the message rather than as a row of its own, and that matters twice: they ARE part of what was
|
|
556
619
|
* sent, and a record row per turn preamble would break the one-row-per-bubble correspondence a branch counts
|
|
557
|
-
* with
|
|
620
|
+
* with. */
|
|
558
621
|
notes: z
|
|
559
622
|
.array(TurnNoteSchema)
|
|
560
623
|
.optional()
|
|
@@ -576,62 +639,118 @@ export const RestoredMessageSchema = z.object({
|
|
|
576
639
|
.describe(
|
|
577
640
|
"A person wrote this in the agent's voice, with no turn behind it. Marked for the human re-reading the conversation months later, so their own words do not pass as the agent's. The agent itself never sees the mark.",
|
|
578
641
|
),
|
|
579
|
-
/* THE ONE-PRESS OFFER A
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
* chat's other one-press notices are drawn live and never recorded, so they never needed this; a routed turn
|
|
585
|
-
* is different precisely because the whole point of recording it is that somebody reads it LATER. A KIND, not
|
|
586
|
-
* a callback, exactly as the live ones are: the reader decides what the press does and whether the offer
|
|
587
|
-
* still stands (a chat already holding its pick shows a settled sentence, not a stale button). */
|
|
642
|
+
/* THE ONE-PRESS OFFER A NOTICE CARRIES (notice rows only), named rather than inferred from its words: the
|
|
643
|
+
* landed notice's "keep future work on the branch", the outage notice's "stop resuming these by itself",
|
|
644
|
+
* the terminal a dependency install the daemon just started is running in, and the routed turn's "keep
|
|
645
|
+
* this chat on my pick". A KIND, not a callback: the chat decides what the press does and whether the
|
|
646
|
+
* offer still stands (a chat already holding its pick shows a settled sentence, not a stale button). */
|
|
588
647
|
noticeAction: z
|
|
589
|
-
.enum(["tierHold"])
|
|
648
|
+
.enum(["landHold", "outageOptOut", "depsInstall", "tierHold"])
|
|
590
649
|
.optional()
|
|
591
|
-
.describe("A one-press follow-up this
|
|
650
|
+
.describe("A one-press follow-up this notice offers, by name. The chat decides what it does and whether it still applies."),
|
|
651
|
+
/* A WAIT THIS NOTICE DESCRIBES that had not finished when it was written (notice rows only): the chat draws
|
|
652
|
+
* a spinner over it while the wait is on, and the plain line once it is over. A KIND rather than a boolean
|
|
653
|
+
* because whether the wait is STILL running is a fact about the conversation now, not about a row in a
|
|
654
|
+
* record: the reader pairs the kind with the live state that answers it. */
|
|
655
|
+
noticeWait: z.enum(["credentialRenewal"]).optional().describe("The wait this notice describes, by name, so a reader can say whether it is still on."),
|
|
592
656
|
/* THE CARD THIS BUBBLE PARKED ON (assistant rows only), at most one: a card closes the bubble it lands in,
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
capabilityOffer: RestoredCapabilityOfferSchema.optional().describe("The capability setup this row asked for, the decision, and the outcome."),
|
|
604
|
-
paymentOffer: RestoredPaymentOfferSchema.optional().describe("The payment this row asked for, the decision, and the receipt."),
|
|
657
|
+
* so the next thing the agent says opens a fresh row beneath it. One field per kind rather than one union
|
|
658
|
+
* field, so a reader reaches the card it draws by name. */
|
|
659
|
+
plan: TranscriptPlanSchema.optional().describe("The plan this row asked approval for, and the answer."),
|
|
660
|
+
question: TranscriptQuestionSchema.optional().describe("The questions this row asked, and the picks that answered them."),
|
|
661
|
+
permission: TranscriptPermissionSchema.optional().describe("The tool this row asked permission for, and the decision."),
|
|
662
|
+
browserHelp: TranscriptBrowserHelpSchema.optional().describe("The browser hand-over this row asked for, and how it ended."),
|
|
663
|
+
terminalHelp: TranscriptTerminalHelpSchema.optional().describe("The terminal hand-over this row asked for, and how it ended."),
|
|
664
|
+
serviceOffer: TranscriptServiceOfferSchema.optional().describe("The priced service run this row offered, the decision, and the receipt."),
|
|
665
|
+
capabilityOffer: TranscriptCapabilityOfferSchema.optional().describe("The capability setup this row asked for, the decision, and the outcome."),
|
|
666
|
+
paymentOffer: TranscriptPaymentOfferSchema.optional().describe("The payment this row asked for, the decision, and the receipt."),
|
|
605
667
|
});
|
|
606
|
-
export type
|
|
668
|
+
export type TranscriptRow = z.infer<typeof TranscriptRowSchema>;
|
|
607
669
|
|
|
608
670
|
/* THE CARD FIELDS A ROW CAN CARRY, as one list, for every reader that has to ask "does this row hold a card":
|
|
609
|
-
* the fold that counts a card-only bubble as a row
|
|
610
|
-
*
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
export
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
671
|
+
* the fold that counts a card-only bubble as a row, the chat's row count (a branch is cut by it), and the
|
|
672
|
+
* surfaces that draw whichever card a bubble is waiting on. */
|
|
673
|
+
export const CARD_FIELDS = ["plan", "question", "permission", "browserHelp", "terminalHelp", "serviceOffer", "capabilityOffer", "paymentOffer"] as const;
|
|
674
|
+
export type CardField = (typeof CARD_FIELDS)[number];
|
|
675
|
+
export type TranscriptCards = Pick<TranscriptRow, CardField>;
|
|
676
|
+
// Whether a row holds a card at all, answered or not.
|
|
677
|
+
export const holdsCard = (row: TranscriptCards): boolean => CARD_FIELDS.some((field) => row[field] !== undefined);
|
|
678
|
+
// Whether a row is holding the turn open on a card nobody has answered.
|
|
679
|
+
export const isAwaitingDecision = (row: TranscriptCards): boolean => CARD_FIELDS.some((field) => row[field]?.status === "pending");
|
|
680
|
+
|
|
681
|
+
/* ONE CHANGE TO A RUN'S ROWS, what the attach stream carries while a turn runs. The daemon folds each frame
|
|
682
|
+
* into its rows (transcript-fold.ts) and says what moved, so a client keeps rows, never frames: it applies
|
|
683
|
+
* these to the list it holds and draws it. `index` counts from the run's first row, which the attach head
|
|
684
|
+
* places in the conversation.
|
|
685
|
+
*
|
|
686
|
+
* Prose and thinking arrive as APPENDS to a row rather than as the row again, so the chat can type them out at
|
|
687
|
+
* the pace they are written; a tool card arrives whole (`tool`, by id, replacing an earlier copy of the same
|
|
688
|
+
* id wherever it nests), because its updates are snapshots already; everything else replaces its row. `drop`
|
|
689
|
+
* is the one removal: an assistant row opened for a block that then wrote nothing. */
|
|
690
|
+
export const TranscriptPatchSchema = z.discriminatedUnion("op", [
|
|
691
|
+
z.object({ op: z.literal("append").describe("A new row at the end."), row: TranscriptRowSchema }),
|
|
692
|
+
z.object({ op: z.literal("replace").describe("This row, whole, in place of the one at that index."), index: z.number().int().nonnegative(), row: TranscriptRowSchema }),
|
|
693
|
+
z.object({ op: z.literal("drop").describe("The row at that index is gone: it was opened and never written into."), index: z.number().int().nonnegative() }),
|
|
694
|
+
z.object({ op: z.literal("text").describe("More of the agent's prose, onto that row's text."), index: z.number().int().nonnegative(), text: z.string() }),
|
|
695
|
+
z.object({ op: z.literal("thinking").describe("More of the agent's reasoning, onto that row's thinking."), index: z.number().int().nonnegative(), text: z.string() }),
|
|
696
|
+
z.object({
|
|
697
|
+
op: z.literal("tool").describe("A tool card, whole: new, or the latest state of one already there, matched by id wherever it nests."),
|
|
698
|
+
index: z.number().int().nonnegative(),
|
|
699
|
+
tool: TranscriptToolSchema,
|
|
700
|
+
parent: z.string().optional().describe("The card this one nests under, when it is a delegated helper's own call."),
|
|
701
|
+
}),
|
|
702
|
+
]);
|
|
703
|
+
export type TranscriptPatch = z.infer<typeof TranscriptPatchSchema>;
|
|
627
704
|
|
|
628
705
|
export const SessionTranscriptSchema = z.object({
|
|
629
706
|
messages: z
|
|
630
|
-
.array(
|
|
707
|
+
.array(TranscriptRowSchema)
|
|
631
708
|
.describe(
|
|
632
709
|
"The conversation, in order. Each block of the agent's prose is its own message with the tools that block introduced, which is what reproduces the way it actually unfolded.",
|
|
633
710
|
),
|
|
634
711
|
});
|
|
712
|
+
/* HOW A TURN THAT LEFT WORK BEHIND ENDED, as the daemon has it, for whoever asks and however long after.
|
|
713
|
+
*
|
|
714
|
+
* One shape for every ending that leaves finished work behind a live session, because they are one situation
|
|
715
|
+
* with one answer (a press) and they differ only in what can honestly be said about WHEN and what the press
|
|
716
|
+
* DOES. The client folds this straight into its pick-up state (chat/pickUp.ts), which is why the field names
|
|
717
|
+
* line up: a second vocabulary between the two halves is how they came to disagree in the first place.
|
|
718
|
+
*
|
|
719
|
+
* IT USED TO BE A BOOLEAN, and the boolean is what left the longest wait uncovered. One flag could only say
|
|
720
|
+
* "a Stop, or a daemon killed under it", so a spent allowance, the one ending that reliably OUTLIVES the
|
|
721
|
+
* window that hit it, reached a reopened tab as nothing at all: no strip, no countdown, no press, and the user
|
|
722
|
+
* typing the word by hand hours later. It could not say more without these three facts, and each of them
|
|
723
|
+
* changes what the surface may promise:
|
|
724
|
+
*
|
|
725
|
+
* · `resetsAt` is the only honest "not before this" any ending knows, and it is the whole of what a chat
|
|
726
|
+
* reopened the next morning wants to be told;
|
|
727
|
+
* · `held` is what makes the press cheap. The daemon keeps the refused turn whole, so continuing RE-RUNS it
|
|
728
|
+
* and adds nothing to the conversation; without this the same press appends a message reading "Continue",
|
|
729
|
+
* which is exactly the transcript pollution the press exists to prevent, and `ran` separates a turn that
|
|
730
|
+
* got somewhere from one the allowance refused at the door (two different sentences);
|
|
731
|
+
* · `scheduled` says somebody else is already bringing this turn back, so the surface REPORTS a wait instead
|
|
732
|
+
* of offering one, and no local automation races the daemon's own pass for it. */
|
|
733
|
+
export const TurnEndingSchema = z.object({
|
|
734
|
+
reason: z
|
|
735
|
+
.enum(["stopped", "limit", "outage"])
|
|
736
|
+
.describe(
|
|
737
|
+
"Which ending left the work here: a Stop or a daemon killed under the turn, a spent usage allowance, or a provider that refused it.",
|
|
738
|
+
),
|
|
739
|
+
resetsAt: z
|
|
740
|
+
.number()
|
|
741
|
+
.optional()
|
|
742
|
+
.describe("When the spent allowance reopens, in epoch seconds. Absent for every ending that names no instant, and for a provider that publishes none."),
|
|
743
|
+
held: z
|
|
744
|
+
.object({ ran: z.boolean().describe("Whether the held turn got anywhere before it was refused, which is a different sentence from one refused at the door.") })
|
|
745
|
+
.optional()
|
|
746
|
+
.describe("Present when the daemon still holds the refused turn whole, so a press re-runs it rather than appending a message after it."),
|
|
747
|
+
scheduled: z
|
|
748
|
+
.boolean()
|
|
749
|
+
.optional()
|
|
750
|
+
.describe("Whether something other than the user is already booked to send this turn again, so the surface reports the wait instead of offering a press."),
|
|
751
|
+
});
|
|
752
|
+
export type TurnEnding = z.infer<typeof TurnEndingSchema>;
|
|
753
|
+
|
|
635
754
|
/* THE RECORD A REOPENED TAB IS REBUILT FROM: the messages, plus what the session behind them is BOUND to.
|
|
636
755
|
*
|
|
637
756
|
* A provider session is minted on one runtime under one credential, and it resumes only there, so a client
|
|
@@ -641,7 +760,7 @@ export const SessionTranscriptSchema = z.object({
|
|
|
641
760
|
* is what made switching BACK to the account that minted it announce a fresh session and then retire a
|
|
642
761
|
* perfectly resumable one, spending the whole transcript again on a cold prompt cache.
|
|
643
762
|
*
|
|
644
|
-
*
|
|
763
|
+
* The session fields are all optional, and absent together on a conversation that has no session to resume. */
|
|
645
764
|
export const AgentTranscriptSchema = SessionTranscriptSchema.extend({
|
|
646
765
|
sessionId: z.string().optional().describe("The provider session behind the last turn, when there is one."),
|
|
647
766
|
provider: AgentProviderSchema.optional().describe("Which provider minted that session."),
|
|
@@ -650,6 +769,19 @@ export const AgentTranscriptSchema = SessionTranscriptSchema.extend({
|
|
|
650
769
|
.string()
|
|
651
770
|
.optional()
|
|
652
771
|
.describe("Which stored account it belongs to, as the daemon resolved it. Absent when no stored account paid for the turn."),
|
|
772
|
+
/* AND HOW THE LAST TURN ENDED, for the endings that leave the client something to OFFER rather than
|
|
773
|
+
* something to draw: work half done behind a session that is perfectly alive, where the only thing missing
|
|
774
|
+
* is somebody saying carry on.
|
|
775
|
+
*
|
|
776
|
+
* It rides the transcript because the offer used to ride the WINDOW. A chat armed the continue press from
|
|
777
|
+
* the stream it was watching when the turn stopped, so the press existed only where somebody had been
|
|
778
|
+
* looking: stop an agent from the board with its chat closed, or reopen the tab on another device, or after
|
|
779
|
+
* a reload that dropped the tab, and the same stopped session came back with no way on but typing the word
|
|
780
|
+
* by hand, which is precisely what the press exists to spare. The daemon is the one party that knows this
|
|
781
|
+
* about a conversation whoever asks and however long after, so it is the one that says it. */
|
|
782
|
+
ending: TurnEndingSchema.optional().describe(
|
|
783
|
+
"How the last turn ended, when it left work behind that one press finishes. Absent for a conversation whose last turn ended on its own, and for the failures that name something to repair first.",
|
|
784
|
+
),
|
|
653
785
|
});
|
|
654
786
|
|
|
655
787
|
/* WHAT A PUBLISHED CONVERSATION'S PAGE IS HANDED, the whole of it, baked into the page as one JSON block.
|
|
@@ -659,7 +791,7 @@ export const AgentTranscriptSchema = SessionTranscriptSchema.extend({
|
|
|
659
791
|
* it, which also settles the security question by construction, a page with nothing to ask has no way to ask
|
|
660
792
|
* for something it was not given.
|
|
661
793
|
*
|
|
662
|
-
* The messages are the SAME
|
|
794
|
+
* The messages are the SAME TranscriptRow rows the app replays a reopened tab from, already filtered to the
|
|
663
795
|
* chosen detail level and with every picture path rewritten to the copy published beside the page. That
|
|
664
796
|
* sameness is the point: the shared page renders them with the app's own components, so what a recipient sees
|
|
665
797
|
* is what the owner saw. */
|
|
@@ -668,7 +800,7 @@ export const SharePayloadSchema = z.object({
|
|
|
668
800
|
// When the snapshot was taken, not when the conversation happened, see SharedConversation.sharedAt.
|
|
669
801
|
sharedAt: z.number(),
|
|
670
802
|
detail: ShareDetailSchema,
|
|
671
|
-
messages: z.array(
|
|
803
|
+
messages: z.array(TranscriptRowSchema),
|
|
672
804
|
});
|
|
673
805
|
export type SharePayload = z.infer<typeof SharePayloadSchema>;
|
|
674
806
|
|
|
@@ -676,9 +808,8 @@ export type SharePayload = z.infer<typeof SharePayloadSchema>;
|
|
|
676
808
|
// ~40 SDKMessage types down to this union: high-value block types get a dedicated frame
|
|
677
809
|
// (delta/thinking/tool_call/tool_call_update/todos/usage/rate_limit_info/account_usage/context_usage/init/compact); any SDK message
|
|
678
810
|
// without a UI mapping is dropped. `plan`/`question`/`permission` pause the turn until the user answers on the
|
|
679
|
-
// `POST /agent/reply` side channel, and `resolved` releases the one it names; `
|
|
680
|
-
//
|
|
681
|
-
// agent changes it.
|
|
811
|
+
// `POST /agent/reply` side channel, and `resolved` releases the one it names; `mode` reports the live
|
|
812
|
+
// permission posture as the agent changes it.
|
|
682
813
|
// `parentToolUseId` tags frames produced inside a subagent (Task tool); `subagent`/`subagent_update` report the
|
|
683
814
|
// subagent itself, keyed by the same tool_use id those tagged frames carry.
|
|
684
815
|
export const AgentEventSchema = z.discriminatedUnion("kind", [
|
|
@@ -1048,19 +1179,12 @@ export const AgentEventSchema = z.discriminatedUnion("kind", [
|
|
|
1048
1179
|
// already moved on. It rides verbatim, exactly as the client POSTed it; absent, nobody answered (the turn
|
|
1049
1180
|
// was stopped, or died under the card), which is not a decision and must not replay as one.
|
|
1050
1181
|
z.object({ kind: z.literal("resolved"), requestId: z.string(), reply: AgentReplySchema.optional() }),
|
|
1051
|
-
/*
|
|
1052
|
-
*
|
|
1053
|
-
*
|
|
1054
|
-
*
|
|
1055
|
-
*
|
|
1056
|
-
*
|
|
1057
|
-
* like the agent freezing, which is the failure the explanation was meant to prevent rather than cause. So
|
|
1058
|
-
* the card goes out complete and unexplained, and this arrives if and when it does. Same shape and the same
|
|
1059
|
-
* reason as `service_event`, which streams a running service's status onto an offer already rendered.
|
|
1060
|
-
*
|
|
1061
|
-
* Never arrives at all when the setting is off, when nothing is connected to answer, or when the user
|
|
1062
|
-
* settles the card first, and none of those change what the card says. */
|
|
1063
|
-
z.object({ kind: z.literal("permission_note"), requestId: z.string(), explain: z.string() }),
|
|
1182
|
+
/* There was a `permission_note` frame here: a late sentence raced onto a command card that had already gone
|
|
1183
|
+
* out, because the explanation was optional and the card must not wait for a quick-model rung that might
|
|
1184
|
+
* take tens of seconds. It is gone with the setting that made it optional. The judge now decides the
|
|
1185
|
+
* verdict, so the sentence is not a decoration arriving afterwards — it is the REASON THE CARD EXISTS, and
|
|
1186
|
+
* a card cannot be raised before it is known. Nothing races, and `PermissionAsk.explain` is populated at
|
|
1187
|
+
* raise time (guard/command-gate.ts). */
|
|
1064
1188
|
// The turn's permission mode, whenever it changes, the user's pick at turn start, then every move the
|
|
1065
1189
|
// AGENT makes on its own (EnterPlanMode on a request that needs thinking through, ExitPlanMode once the
|
|
1066
1190
|
// user approves). The composer's mode selector follows this, so the UI never lies about the live posture.
|
|
@@ -1186,23 +1310,34 @@ export const AgentEventSchema = z.discriminatedUnion("kind", [
|
|
|
1186
1310
|
// rate_limit_event or the account's persisted usage windows). Absent when the reset instant is unknown
|
|
1187
1311
|
// (nothing to schedule against).
|
|
1188
1312
|
resetsAt: z.number().optional(),
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1313
|
+
/* Where the daemon's resume of THIS turn stands, for the three codes that have one (provider-outage,
|
|
1314
|
+
* claude-token-refused, rate_limit). "scheduled" = the resume is armed and this turn comes back by
|
|
1315
|
+
* itself; "available" = the daemon remembered the failed turn and arming THIS conversation
|
|
1316
|
+
* (AgentSummarySchema's resumeAfterOutage / resumeAfterLimit) picks up that same resume, which is what
|
|
1317
|
+
* the offer banner hangs off, gated codes only, since a credential renewal is never gated on a posture
|
|
1318
|
+
* at all. The two words are read against the effective posture (the conversation's override, else the
|
|
1319
|
+
* sandbox default), so a chat armed on its own says "scheduled" while the unarmed board around it says
|
|
1320
|
+
* "available". Absent means there is nothing automatic to resume: a limit whose reset instant nobody
|
|
1321
|
+
* published has nothing to schedule against, and a refused credential has none once re-minting it has
|
|
1322
|
+
* already been tried and failed.
|
|
1323
|
+
*
|
|
1324
|
+
* A SPENT ALLOWANCE USED TO BE ABSENT HERE BY RULE, and the rule was right about the default and wrong
|
|
1325
|
+
* about the ceiling. The budget is the user's, so nothing fires unless they said so, which is what the
|
|
1326
|
+
* posture is; what the old absence also cost was the case a press cannot reach, a 2am wall on a board
|
|
1327
|
+
* nobody is watching. Both words are now honest for it: unarmed says "available", which is an offer,
|
|
1328
|
+
* and armed says "scheduled", which the card counts down to. */
|
|
1198
1329
|
autoResume: z.enum(["scheduled", "available"]).optional(),
|
|
1199
1330
|
/* THE DAEMON IS STILL HOLDING THIS EXACT TURN, so the way on is to RE-RUN it rather than to send
|
|
1200
1331
|
* something after it. rate_limit only, and the counterpart to `autoResume` rather than a member of it:
|
|
1201
|
-
* that field answers "is a machine bringing this back",
|
|
1202
|
-
*
|
|
1332
|
+
* that field answers "is a machine bringing this back", which for a spent allowance is a posture the
|
|
1333
|
+
* user sets and defaults to no (the allowance is their own budget to spend, turn-resume.ts). This
|
|
1203
1334
|
* answers the question that was never asked, "and if the user says go, what happens", which had exactly
|
|
1204
1335
|
* one possible answer for as long as it went unasked: a new user message reading "Continue".
|
|
1205
1336
|
*
|
|
1337
|
+
* BOTH ANSWERS RUN THROUGH THIS FIELD, which is why it is not folded into the one above: an armed
|
|
1338
|
+
* conversation's scheduled fire and an unarmed one's press are the same held turn re-run the same way,
|
|
1339
|
+
* and the only difference is who says go.
|
|
1340
|
+
*
|
|
1206
1341
|
* That answer was wrong in a way the chat could not show. The press is not a new instruction, it is the
|
|
1207
1342
|
* same one again, and appending it said otherwise to the only reader that matters: the provider session
|
|
1208
1343
|
* grew one "Continue" per press, each with a synthetic "No response requested." above it, so a chat that
|
|
@@ -1229,34 +1364,73 @@ export const AgentEventSchema = z.discriminatedUnion("kind", [
|
|
|
1229
1364
|
]);
|
|
1230
1365
|
export type AgentEvent = z.infer<typeof AgentEventSchema>;
|
|
1231
1366
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1367
|
+
/* THE FRAMES THAT ARE FACTS ABOUT THE TURN rather than words in it: which session it runs, where it stands,
|
|
1368
|
+
* what it costs, how it failed. Everything else an AgentEvent can say is transcript, and reaches a client as
|
|
1369
|
+
* rows and patches (TranscriptPatchSchema) after the daemon has folded it; these reach it as themselves,
|
|
1370
|
+
* because there is nothing to fold, a client keeps them as state beside the transcript. A frame can be both,
|
|
1371
|
+
* a `worktree` that rebased writes a notice AND says where the branch is, so the two lists overlap, and the
|
|
1372
|
+
* fold and this list each take the half that is theirs. */
|
|
1373
|
+
export const TURN_FACT_KINDS = [
|
|
1374
|
+
"session",
|
|
1375
|
+
"worktree",
|
|
1376
|
+
"init",
|
|
1377
|
+
"terminal",
|
|
1378
|
+
"browser",
|
|
1379
|
+
"commands",
|
|
1380
|
+
"usage",
|
|
1381
|
+
"rate_limit_info",
|
|
1382
|
+
"fast_mode",
|
|
1383
|
+
"tier",
|
|
1384
|
+
"provider_retry",
|
|
1385
|
+
"account_usage",
|
|
1386
|
+
"context_usage",
|
|
1387
|
+
"mode",
|
|
1388
|
+
"error",
|
|
1389
|
+
] as const;
|
|
1390
|
+
export type TurnFact = Extract<AgentEvent, { kind: (typeof TURN_FACT_KINDS)[number] }>;
|
|
1391
|
+
export const isTurnFact = (event: AgentEvent): event is TurnFact => (TURN_FACT_KINDS as readonly string[]).includes(event.kind);
|
|
1392
|
+
// The same members AgentEventSchema declares, picked out rather than declared twice: a fact's shape is the
|
|
1393
|
+
// frame's shape, and a second spelling of it would be the drift the list above exists to prevent.
|
|
1394
|
+
type AgentEventMember = (typeof AgentEventSchema.options)[number];
|
|
1395
|
+
const factMembers = AgentEventSchema.options.filter((member) => (TURN_FACT_KINDS as readonly string[]).includes(member.shape.kind.value)) as unknown as [
|
|
1396
|
+
AgentEventMember,
|
|
1397
|
+
...AgentEventMember[],
|
|
1398
|
+
];
|
|
1399
|
+
export const TurnFactSchema = z.discriminatedUnion("kind", factMembers) as unknown as z.ZodType<TurnFact>;
|
|
1400
|
+
|
|
1401
|
+
/* The /agent/attach stream: a head carrying the run's rows so far, then every change to them and every fact
|
|
1402
|
+
* about the turn as each lands, then `end` when the run is over, nothing more coming. A stream that closes
|
|
1403
|
+
* WITHOUT `end` was dropped mid-run; the client re-attaches and takes the head's rows again, whole, which is
|
|
1404
|
+
* what makes attaching idempotent: a window never re-folds what it has already drawn, it replaces it.
|
|
1405
|
+
*
|
|
1406
|
+
* Facts REPLAY on every attach (their seq is at or below the head's), because a window joining late still has
|
|
1407
|
+
* to learn which session the turn runs and where its branch stands; patches are only ever live (their seq is
|
|
1408
|
+
* above the head's), because the head already holds their result. */
|
|
1238
1409
|
export const AttachFrameSchema = z.discriminatedUnion("kind", [
|
|
1239
1410
|
z.object({
|
|
1240
|
-
kind: z.literal("attached").describe("The first frame, identifying the run you have joined."),
|
|
1411
|
+
kind: z.literal("attached").describe("The first frame, identifying the run you have joined and handing you its transcript so far."),
|
|
1241
1412
|
run: z.string().describe("The run's id."),
|
|
1242
|
-
prompt: z.string().describe("What was said to start it, so a window that did not start the turn can still draw the message."),
|
|
1243
1413
|
startedAt: z.number().describe("When it started, in milliseconds, so a window joining late can show how long it has been going."),
|
|
1244
|
-
seq: z.number().describe("How many frames
|
|
1414
|
+
seq: z.number().describe("How many frames the run has produced so far. A fact at or below this number is being replayed; a patch is never."),
|
|
1415
|
+
rows: z
|
|
1416
|
+
.array(TranscriptRowSchema)
|
|
1417
|
+
.describe("The turn's rows as they stand: what was asked, and everything the agent has said and done since. Draw these, then apply the patches that follow."),
|
|
1245
1418
|
}),
|
|
1246
1419
|
z.object({
|
|
1247
|
-
kind: z.literal("
|
|
1248
|
-
seq: z
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
),
|
|
1420
|
+
kind: z.literal("patch").describe("One change to the run's rows."),
|
|
1421
|
+
seq: z.number().describe("Its position in the run, counting from one."),
|
|
1422
|
+
patch: TranscriptPatchSchema,
|
|
1423
|
+
}),
|
|
1424
|
+
z.object({
|
|
1425
|
+
kind: z.literal("fact").describe("One thing about the turn that is not a row: its session, its branch, its cost, a failure."),
|
|
1426
|
+
seq: z.number().describe("Its position in the run, counting from one. At or below the head's number, it is being replayed."),
|
|
1427
|
+
fact: TurnFactSchema,
|
|
1254
1428
|
}),
|
|
1255
1429
|
z.object({
|
|
1256
1430
|
kind: z
|
|
1257
1431
|
.literal("end")
|
|
1258
1432
|
.describe(
|
|
1259
|
-
"The run is over and every frame has been delivered. A stream that closes without this was dropped mid-run, so re-attach
|
|
1433
|
+
"The run is over and every frame has been delivered. A stream that closes without this was dropped mid-run, so re-attach rather than assuming the turn finished.",
|
|
1260
1434
|
),
|
|
1261
1435
|
}),
|
|
1262
1436
|
]);
|
|
@@ -1536,9 +1710,34 @@ export type Presence = z.infer<typeof PresenceSchema>;
|
|
|
1536
1710
|
export const AgentsSchema = z.object({ kind: z.literal("agents"), agents: z.array(AgentSummarySchema), rev: z.number() });
|
|
1537
1711
|
export type Agents = z.infer<typeof AgentsSchema>;
|
|
1538
1712
|
|
|
1713
|
+
/* AN ACCOUNT'S HEADROOM JUST MOVED, the reading itself, keyed the way the daemon's store keys it (a Claude
|
|
1714
|
+
* account id, or `${provider}:${authFile}` for a routed subscription).
|
|
1715
|
+
*
|
|
1716
|
+
* The fifth push, and the one that lets every ring, rail and picker row stop refetching on mount. A reading
|
|
1717
|
+
* lands on the daemon for one of four reasons, a turn settled, a plan refused, a screen asked, a provider
|
|
1718
|
+
* pushed, and until this frame existed only the window that caused it ever heard: every other window drew the
|
|
1719
|
+
* number it had loaded that morning until something in it happened to remount. Snapshot-not-diff per account,
|
|
1720
|
+
* last frame wins, and a browser that missed one simply holds the older reading, which is what `measuredAt`
|
|
1721
|
+
* is for. `usage` absent ⇒ the account's snapshot was cleared (it was disconnected). */
|
|
1722
|
+
export const AccountUsageChangedSchema = z.object({
|
|
1723
|
+
kind: z.literal("accountUsage"),
|
|
1724
|
+
// The provider whose row this account is, because the key alone does not say (a native id is bare).
|
|
1725
|
+
provider: z.string(),
|
|
1726
|
+
account: z.string(),
|
|
1727
|
+
usage: AccountUsageSchema.optional(),
|
|
1728
|
+
});
|
|
1729
|
+
export type AccountUsageChanged = z.infer<typeof AccountUsageChangedSchema>;
|
|
1730
|
+
|
|
1731
|
+
// A provider's last refusal was recorded or settled. The observed half of "can I run on this" (see
|
|
1732
|
+
// ProviderRefusalSchema), pushed for the same reason the reading above is: a refusal at 4am used to reach a
|
|
1733
|
+
// window only when it next reloaded its account rows. `refusal` absent ⇒ settled, nothing stands.
|
|
1734
|
+
export const ProviderRefusalChangedSchema = z.object({ kind: z.literal("providerRefusal"), provider: z.string(), refusal: ProviderRefusalSchema.optional() });
|
|
1735
|
+
export type ProviderRefusalChanged = z.infer<typeof ProviderRefusalChangedSchema>;
|
|
1736
|
+
|
|
1539
1737
|
// The /events stream union: the hello identity frame, then liveness heartbeats interleaved with boot progress,
|
|
1540
|
-
// workspace-change batches, repo-set snapshots, ref-move batches, runtime-domain nudges,
|
|
1541
|
-
// roster snapshots. oRPC validates every yielded frame against this,
|
|
1738
|
+
// workspace-change batches, repo-set snapshots, ref-move batches, runtime-domain nudges, presence + fleet
|
|
1739
|
+
// roster snapshots, and account headroom / refusal changes. oRPC validates every yielded frame against this,
|
|
1740
|
+
// so all kinds must live here.
|
|
1542
1741
|
export const SystemEventSchema = z.discriminatedUnion("kind", [
|
|
1543
1742
|
HelloSchema,
|
|
1544
1743
|
HeartbeatSchema,
|
|
@@ -1549,5 +1748,7 @@ export const SystemEventSchema = z.discriminatedUnion("kind", [
|
|
|
1549
1748
|
RuntimeChangedSchema,
|
|
1550
1749
|
PresenceSchema,
|
|
1551
1750
|
AgentsSchema,
|
|
1751
|
+
AccountUsageChangedSchema,
|
|
1752
|
+
ProviderRefusalChangedSchema,
|
|
1552
1753
|
]);
|
|
1553
1754
|
export type SystemEvent = z.infer<typeof SystemEventSchema>;
|