@intentic/sandbox-contract 1.165.0 → 1.167.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/agent-catalog.d.ts +0 -1
- package/dist/agent-catalog.d.ts.map +1 -1
- package/dist/agent-catalog.js +1 -2
- package/dist/agent-catalog.js.map +1 -1
- package/dist/contracts/agent.contract.d.ts +2 -2
- package/dist/contracts/agents.contract.d.ts +18 -0
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/automations.contract.d.ts +1 -1
- package/dist/contracts/ci.contract.d.ts +4 -0
- package/dist/contracts/ci.contract.d.ts.map +1 -1
- package/dist/contracts/ci.contract.js +2 -1
- package/dist/contracts/ci.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +1 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/gate.contract.d.ts +47 -0
- package/dist/contracts/gate.contract.d.ts.map +1 -0
- package/dist/contracts/gate.contract.js +9 -0
- package/dist/contracts/gate.contract.js.map +1 -0
- package/dist/contracts/kimi.contract.d.ts +0 -70
- package/dist/contracts/kimi.contract.d.ts.map +1 -1
- package/dist/contracts/kimi.contract.js +1 -5
- package/dist/contracts/kimi.contract.js.map +1 -1
- package/dist/contracts/settings.contract.d.ts +37 -3
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +68 -8
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +3 -1
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/translator.contract.d.ts +11 -0
- package/dist/contracts/translator.contract.d.ts.map +1 -1
- package/dist/contracts/translator.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +3 -1
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/events.d.ts +98 -2
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +21 -2
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +188 -81
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/model-order.d.ts.map +1 -1
- package/dist/model-order.js +25 -6
- package/dist/model-order.js.map +1 -1
- package/dist/schemas.d.ts +179 -21
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +90 -13
- package/dist/schemas.js.map +1 -1
- package/dist/session-names.d.ts +8 -0
- package/dist/session-names.d.ts.map +1 -0
- package/dist/session-names.js +17 -0
- package/dist/session-names.js.map +1 -0
- package/package.json +13 -2
- package/src/agent-catalog.ts +6 -11
- package/src/contracts/agents.contract.ts +8 -6
- package/src/contracts/ci.contract.ts +5 -1
- package/src/contracts/gate.contract.ts +19 -0
- package/src/contracts/kimi.contract.ts +4 -21
- package/src/contracts/system.contract.ts +9 -0
- package/src/contracts/translator.contract.ts +4 -6
- package/src/events.ts +56 -8
- package/src/index.ts +3 -0
- package/src/model-order.test.ts +29 -12
- package/src/model-order.ts +50 -18
- package/src/quick-model.test.ts +3 -3
- package/src/schemas.test.ts +20 -0
- package/src/schemas.ts +305 -64
- package/src/session-names.ts +44 -0
package/src/schemas.ts
CHANGED
|
@@ -50,8 +50,8 @@ export const EditorContextSchema = z.object({
|
|
|
50
50
|
});
|
|
51
51
|
export type EditorContext = z.infer<typeof EditorContextSchema>;
|
|
52
52
|
|
|
53
|
-
// The client-minted stable conversation identity. Constrained because
|
|
54
|
-
// and filesystem paths — the regex is the injection guard. Shared by
|
|
53
|
+
// The client-minted stable conversation identity. Constrained because isolated conversations also use it in
|
|
54
|
+
// branch names (agent/<id>) and filesystem paths — the regex is the injection guard. Shared by turn + attach.
|
|
55
55
|
const ConversationIdSchema = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/);
|
|
56
56
|
|
|
57
57
|
// Where a conversation came from when nobody typed it into the browser: an automation wake carrying a message
|
|
@@ -102,7 +102,7 @@ export const AgentTurnSchema = z
|
|
|
102
102
|
account: z.string().optional(),
|
|
103
103
|
sessionId: z.string().optional(),
|
|
104
104
|
// The client-minted stable conversation identity (survives provider/account/harness switches, which
|
|
105
|
-
// retire sessions). Keys the fleet registry entry
|
|
105
|
+
// retire sessions). Keys the fleet registry entry and turn run, plus the worktree when isolated.
|
|
106
106
|
conversationId: ConversationIdSchema.optional(),
|
|
107
107
|
// When true, the turn runs in the conversation's isolated git worktree (created lazily on first use)
|
|
108
108
|
// instead of the shared /work tree — the parallel-agents mode. Requires conversationId.
|
|
@@ -157,10 +157,10 @@ export const AttachTurnSchema = z.object({
|
|
|
157
157
|
});
|
|
158
158
|
export type AttachTurn = z.infer<typeof AttachTurnSchema>;
|
|
159
159
|
|
|
160
|
-
// ---- agents: the
|
|
161
|
-
// A "fleet agent" is
|
|
162
|
-
//
|
|
163
|
-
// surface shows
|
|
160
|
+
// ---- agents: the conversation fleet ----
|
|
161
|
+
// A "fleet agent" is any conversation with a registry entry, keyed by its conversationId. Isolated ones own a
|
|
162
|
+
// git worktree (branch agent/<id> in every workspace repo); workspace conversations have no branch. The fleet
|
|
163
|
+
// surface shows both through the same status/activity/cost lifecycle.
|
|
164
164
|
|
|
165
165
|
// idle/running/awaiting are the turn lifecycle (awaiting = paused on a plan approval or question); ready /
|
|
166
166
|
// landed / conflict are outcomes of the land flow — `ready` is a clean completion whose delta stayed on the
|
|
@@ -173,7 +173,31 @@ export type AttachTurn = z.infer<typeof AttachTurnSchema>;
|
|
|
173
173
|
// unlabelled: without it such a turn rehydrates as `idle`, which is the resting status of a turn that finished
|
|
174
174
|
// CLEANLY, so the board files a killed agent under Finished and the question it was holding disappears with the
|
|
175
175
|
// process that asked it. See agents-store.ts — this is the status a live turn leaves on disk.
|
|
176
|
-
|
|
176
|
+
//
|
|
177
|
+
/* `stopping` and `stopped` are the two halves of a user's Stop, and they exist because a hard-cancel is NOT
|
|
178
|
+
* instant: /agent/stop aborts the provider and then waits for the turn's generator to unwind (worktree and
|
|
179
|
+
* registry cleanup), which is seconds of real time. For that whole window the runtime half still said
|
|
180
|
+
* `running`, so every surface kept its spinner turning on a turn the user had already killed — and then the
|
|
181
|
+
* card jumped to a settled state out of nowhere. `stopping` is what the daemon knows the instant the abort
|
|
182
|
+
* lands, published immediately so the press has a visible result; `stopped` is where the turn comes to rest.
|
|
183
|
+
*
|
|
184
|
+
* `stopped` is deliberately its own value rather than `interrupted` or `error`. Not `error`, which is what a
|
|
185
|
+
* stopped turn used to report (every provider adapter surfaces the abort's unwind as an error frame) — a card
|
|
186
|
+
* accusing the user's own deliberate press of being a failure. Not `interrupted` either: that one means the
|
|
187
|
+
* daemon died under the turn, and a boot pass may re-run it, which is precisely what must never happen to a
|
|
188
|
+
* turn a person chose to end. */
|
|
189
|
+
export const AgentStatusSchema = z.enum([
|
|
190
|
+
"idle",
|
|
191
|
+
"running",
|
|
192
|
+
"awaiting",
|
|
193
|
+
"stopping",
|
|
194
|
+
"stopped",
|
|
195
|
+
"ready",
|
|
196
|
+
"landed",
|
|
197
|
+
"conflict",
|
|
198
|
+
"error",
|
|
199
|
+
"interrupted",
|
|
200
|
+
]);
|
|
177
201
|
export type AgentStatus = z.infer<typeof AgentStatusSchema>;
|
|
178
202
|
// The card's live activity snippet: the last tool the agent used (with its target) and the in-progress todo.
|
|
179
203
|
export const AgentActivitySchema = z.object({
|
|
@@ -355,9 +379,8 @@ export const AgentLandSchema = z.object({ id: z.string().min(1), mode: LandModeS
|
|
|
355
379
|
// The providers whose model can run UNDER the Claude Code harness through the bundled translator (CLIProxyAPI),
|
|
356
380
|
// which holds their SUBSCRIPTION OAuth and re-serves it behind an Anthropic endpoint. The `claude` provider is
|
|
357
381
|
// absent — native Anthropic OAuth serves it directly, without the translator. Codex and Grok also have a native
|
|
358
|
-
// runtime and so carry the harness axis;
|
|
359
|
-
|
|
360
|
-
export const KeyedProviderSchema = z.enum(["codex", "grok", "gemini"]);
|
|
382
|
+
// runtime and so carry the harness axis; Kimi and Gemini are routed-only, so their turns always use Claude Code.
|
|
383
|
+
export const KeyedProviderSchema = z.enum(["codex", "grok", "kimi", "gemini"]);
|
|
361
384
|
export type KeyedProvider = z.infer<typeof KeyedProviderSchema>;
|
|
362
385
|
|
|
363
386
|
// One connected subscription in the translator. `name` is CLIProxyAPI's auth-file name — the stable store key a
|
|
@@ -371,6 +394,7 @@ export type TranslatorAccount = z.infer<typeof TranslatorAccountSchema>;
|
|
|
371
394
|
export const TranslatorAccountsSchema = z.object({
|
|
372
395
|
codex: z.array(TranslatorAccountSchema),
|
|
373
396
|
grok: z.array(TranslatorAccountSchema),
|
|
397
|
+
kimi: z.array(TranslatorAccountSchema),
|
|
374
398
|
gemini: z.array(TranslatorAccountSchema),
|
|
375
399
|
});
|
|
376
400
|
export type TranslatorAccounts = z.infer<typeof TranslatorAccountsSchema>;
|
|
@@ -525,11 +549,6 @@ export const OauthExchangeSchema = z.object({
|
|
|
525
549
|
label: z.string().optional(),
|
|
526
550
|
});
|
|
527
551
|
export const AuthorizeChallengeSchema = z.object({ authorizeUrl: z.string(), verifier: z.string(), state: z.string() });
|
|
528
|
-
// Kimi (Moonshot) authenticates with an API key, not OAuth: the user pastes a key from their Moonshot account
|
|
529
|
-
// and the sandbox stores it as an account (one key per account, several accounts side by side). `label` is the
|
|
530
|
-
// user's display name (blank ⇒ the daemon derives a default). The key never rides back out — connection status
|
|
531
|
-
// is existence in `/kimi/accounts`.
|
|
532
|
-
export const KimiConnectSchema = z.object({ apiKey: z.string().min(1), label: z.string().optional() });
|
|
533
552
|
// xAI Grok (via OpenCode) uses subscription OAuth via the headless device-code method. `start` returns the
|
|
534
553
|
// `url` the user opens (xAI's verification_uri_complete, which pre-fills the code) and `code` — the same
|
|
535
554
|
// one-time code, surfaced so the card matches x.ai exactly. There is no paste-back: OpenCode polls to
|
|
@@ -539,13 +558,15 @@ export const KimiConnectSchema = z.object({ apiKey: z.string().min(1), label: z.
|
|
|
539
558
|
// A device-code login start: the verification URL + the one-time code the user enters there. The native Grok
|
|
540
559
|
// flow (via OpenCode) — see TranslatorStartSchema for the routed-provider connect, which adds `state`.
|
|
541
560
|
export const DeviceStartSchema = z.object({ url: z.string(), code: z.string() });
|
|
542
|
-
// A routed-provider subscription login start (codex/grok/gemini via CLIProxyAPI).
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
561
|
+
// A routed-provider subscription login start (codex/grok/kimi/gemini via CLIProxyAPI). Device flows poll to
|
|
562
|
+
// completion after the user approves upstream; redirect flows need the browser's landing URL pasted back. The
|
|
563
|
+
// explicit flow discriminator matters even when a provider's verification URL already embeds its optional code.
|
|
564
|
+
export const TranslatorStartSchema = z.object({
|
|
565
|
+
url: z.string(),
|
|
566
|
+
code: z.string(),
|
|
567
|
+
state: z.string(),
|
|
568
|
+
flow: z.enum(["device", "redirect"]),
|
|
569
|
+
});
|
|
549
570
|
// The paste-back half of a redirect login: the URL the provider sent the browser to, carrying the grant as
|
|
550
571
|
// ?code=&state=. `state` ties it to the handshake that issued it — the translator rejects a mismatch.
|
|
551
572
|
export const TranslatorCompleteSchema = z.object({
|
|
@@ -555,7 +576,7 @@ export const TranslatorCompleteSchema = z.object({
|
|
|
555
576
|
});
|
|
556
577
|
// A provider's model catalog, resolved daemon-side from live discovery with a persisted last-known-good list and
|
|
557
578
|
// a seed floor (Grok via opencode.ts xaiModels, Codex via codex-models.ts, Claude via the Agent SDK's
|
|
558
|
-
// supportedModels) — never empty, so the picker is never blank. `label` is the
|
|
579
|
+
// supportedModels) — never empty, so the picker is never blank. `label` is the provider's display name; `default`
|
|
559
580
|
// is the model a fresh chat on that provider seeds (always present). Shared by /grok/models, /codex/models,
|
|
560
581
|
// /claude/models. `efforts` is the reasoning-effort tiers the model accepts (Claude reports them per model);
|
|
561
582
|
// empty ⇒ the client's default tiers.
|
|
@@ -563,8 +584,8 @@ export const TranslatorCompleteSchema = z.object({
|
|
|
563
584
|
// EVERY field here is provider-reported — nothing about a model is curated in this repo, so a new release or a
|
|
564
585
|
// renamed family flows to the UI with no code change. Providers differ in how much they publish: the Claude
|
|
565
586
|
// Agent SDK reports a display name, a capability description, effort tiers, and capability flags, while the
|
|
566
|
-
// OpenAI-compatible /v1/models endpoints
|
|
567
|
-
//
|
|
587
|
+
// Some OpenAI-compatible /v1/models endpoints report ids only — those rows render label-only, and that absence
|
|
588
|
+
// is the honest answer rather than something to paper over with a hand-written table.
|
|
568
589
|
//
|
|
569
590
|
// ORDER IS MEANINGFUL: `models` arrives in the provider's own preference order, which is what the picker sorts
|
|
570
591
|
// by, and `default` is the provider's own default. Neither is re-ranked locally.
|
|
@@ -624,6 +645,11 @@ export const BuiltinPromptSchema = z.object({ base: z.enum(["intentic", "claude"
|
|
|
624
645
|
// iqSearch — loads the image-baked iq Claude Code plugin (skill + SessionStart nudge) so the agent
|
|
625
646
|
// prefers the iq CLI over grep/find/Glob; off ⇒ plugin not loaded, native search tools
|
|
626
647
|
// only. Opt-in (default off); the browser Search box uses iq regardless.
|
|
648
|
+
// iqContext — retrieves for the user's message BEFORE the turn starts and prepends the ranked answer
|
|
649
|
+
// to it, so the model opens with the anchors instead of paying a search round-trip to
|
|
650
|
+
// find them. Independent of iqSearch: that one teaches the agent to search, this one
|
|
651
|
+
// answers ahead of it.
|
|
652
|
+
// iqContextHoldout — measurement control for iqContext, same shape as terseHoldout (UsageTurn.iqContext).
|
|
627
653
|
// outputCleaners — the Bash output-cleaner spec (agent-output-filter): "off" = filter disabled (default),
|
|
628
654
|
// "" = all cleaners on, else an iq-style allow-list / default-minus
|
|
629
655
|
// spec ("git,pnpm" = only those; "-cap" = all except). Threaded to the filter via env.
|
|
@@ -643,6 +669,12 @@ export const BuiltinPromptSchema = z.object({ base: z.enum(["intentic", "claude"
|
|
|
643
669
|
// surface fail to parse the moment a toggle is added — which reaches the user as a page of switches that are
|
|
644
670
|
// silently dead, not as an error. It also means an older on-disk manifest keeps the owner's other picks rather
|
|
645
671
|
// than being discarded whole.
|
|
672
|
+
// Build-wide kill switch for usage-limit auto-resume. Keep the setting and implementation in place while the
|
|
673
|
+
// feature is disabled, but clamp every daemon response and settings write to OFF so a persisted `true` from an
|
|
674
|
+
// older build cannot keep spending a newly reset allowance. The web also reads this constant to render the
|
|
675
|
+
// control unavailable and to distrust a stale daemon that still reports a scheduled resume.
|
|
676
|
+
export const USAGE_LIMIT_AUTO_RESUME_ENABLED: boolean = false;
|
|
677
|
+
|
|
646
678
|
export const SandboxSettingsSchema = z.object({
|
|
647
679
|
stableSystemPrompt: z.boolean().default(false),
|
|
648
680
|
skills: z.array(z.string()).default([]),
|
|
@@ -678,6 +710,21 @@ export const SandboxSettingsSchema = z.object({
|
|
|
678
710
|
* Cap is roomy — the bases it stands in for are ~6.8k characters — but finite, because every turn pays it. */
|
|
679
711
|
systemPrompt: z.string().max(20000).default(""),
|
|
680
712
|
iqSearch: z.boolean().default(false),
|
|
713
|
+
/* RETRIEVE BEFORE THE TURN, don't wait to be asked. The daemon runs the user's message through the resident
|
|
714
|
+
* iq engine and prepends the ranked answer to it, so a turn that would have opened with two or three search
|
|
715
|
+
* calls opens with the anchors already in hand. Independent of `iqSearch`, which only teaches the agent to
|
|
716
|
+
* reach for the CLI once it decides to search — this one answers ahead of that decision, and the two
|
|
717
|
+
* compose: the injected capsule names the anchors, the CLI is there for what it missed.
|
|
718
|
+
*
|
|
719
|
+
* It rides the USER message (turn-context.ts), never the system prompt, for the same reason the setup
|
|
720
|
+
* notice does: it changes every turn, and the system prefix is kept byte-stable for the prompt cache.
|
|
721
|
+
* Off by default — it spends input tokens on every eligible turn, and whether that trade pays is exactly
|
|
722
|
+
* what the holdout below is for. */
|
|
723
|
+
iqContext: z.boolean().default(false),
|
|
724
|
+
// Measurement control for the pre-injection, identical in shape to `terseHoldout`: a fraction [0,1] of
|
|
725
|
+
// otherwise-eligible turns run WITHOUT the retrieved context and stamp their arm onto the ledger
|
|
726
|
+
// (UsageTurn.iqContext), so the report compares two real populations of turns instead of asserting a saving.
|
|
727
|
+
iqContextHoldout: z.number().min(0).max(1).default(0),
|
|
681
728
|
outputCleaners: z.string().default("off"),
|
|
682
729
|
outputHoldout: z.number().min(0).max(1).default(0),
|
|
683
730
|
filterBackend: z.enum(["native", "rtk"]).default("native"),
|
|
@@ -699,12 +746,13 @@ export const SandboxSettingsSchema = z.object({
|
|
|
699
746
|
* automation-opened agents (Discord, webhooks, email) finish turns with no browser in the room — a
|
|
700
747
|
* browser-held preference could not govern them. Per-agent override: AgentSummarySchema.autoLand. */
|
|
701
748
|
autoLand: z.boolean().default(true),
|
|
702
|
-
//
|
|
703
|
-
//
|
|
704
|
-
//
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
749
|
+
// Latent opt-in for re-running a turn after the Claude subscription's usage window resets. It defaults off
|
|
750
|
+
// because an unattended retry spends the fresh allowance without the user in the room, and the build-wide
|
|
751
|
+
// gate above currently clamps even an older saved opt-in off while the feature is unavailable.
|
|
752
|
+
autoResumeOnLimit: z
|
|
753
|
+
.boolean()
|
|
754
|
+
.default(false)
|
|
755
|
+
.overwrite((enabled) => USAGE_LIMIT_AUTO_RESUME_ENABLED && enabled),
|
|
708
756
|
/* When a turn dies because the MODEL PROVIDER was failing (500/502/503, a 529 at capacity, a dropped
|
|
709
757
|
* socket), re-run it on an escalating backoff until it goes through or the attempts are spent.
|
|
710
758
|
*
|
|
@@ -726,6 +774,34 @@ export const SandboxSettingsSchema = z.object({
|
|
|
726
774
|
* OFF still records the interruption: the fleet card reads `interrupted` (see AgentStatusSchema) and an
|
|
727
775
|
* automation's row shows an `interrupted` run — nothing is re-run, but nothing is silently lost either. */
|
|
728
776
|
autoResumeOnRestart: z.boolean().default(true),
|
|
777
|
+
/* THE LANDING GATE — the check command run over the COMPOSITE of landed work after a landing debounce.
|
|
778
|
+
* Empty ⇒ no gate at all, which is the default: only the owner knows what verifies this workspace, and a
|
|
779
|
+
* guessed command that fails on a fresh clone would read as the gate finding a bug on its first run.
|
|
780
|
+
*
|
|
781
|
+
* Configuring it is the opt-in, which is why there is no separate enable flag to disagree with it. The
|
|
782
|
+
* command runs in the workspace root through `sh -c`, exactly as a terminal would run it (see gate/gate.ts
|
|
783
|
+
* for why this is NOT an automation guard: a suite outlives GUARD_TIMEOUT_MS, and a timed-out guard reads
|
|
784
|
+
* as "skipped" — a silent green over a suite that never finished). */
|
|
785
|
+
gateCommand: z.string().max(500).default(""),
|
|
786
|
+
/* How long after a land the gate waits before running. A landing burst is the case this exists for: five
|
|
787
|
+
* agents finishing within a minute of each other are five lands, and a gate that ran per land would spend
|
|
788
|
+
* five suites to answer about four trees nobody will ever push. Every land re-arms the timer, so the run
|
|
789
|
+
* happens once, on the tree the user is about to review.
|
|
790
|
+
*
|
|
791
|
+
* It counts from the last LAND and nothing else — explicitly not "until the fleet is idle". Agents here run
|
|
792
|
+
* for hours, so a fleet of twenty with one long runner would never present a quiet moment, and a gate that
|
|
793
|
+
* waited for one would only ever fire when clicked (gate/gate.ts). */
|
|
794
|
+
gateQuietMs: z.number().min(0).max(600_000).default(20_000),
|
|
795
|
+
// Ceiling on one gate run, after which the child is killed and the verdict is `failed` with `timedOut`.
|
|
796
|
+
// Never a pass: a suite that did not finish has not said anything about the tree, and the one thing this
|
|
797
|
+
// gate exists to prevent is a green light nobody earned.
|
|
798
|
+
gateTimeoutMs: z.number().min(60_000).max(3_600_000).default(900_000),
|
|
799
|
+
/* Wake a fixer automatically when the gate goes red, instead of only lighting the badge. ON with a
|
|
800
|
+
* configured command, unlike the other unattended-spend toggles (autoResumeOnLimit), and the difference is
|
|
801
|
+
* that the spend here is the POINT: a red gate whose fix waits for the user to notice has moved the CI
|
|
802
|
+
* round-trip into the workspace without removing it from the user's day. One attempt per verdict, so a
|
|
803
|
+
* command that fails for a reason no agent can fix costs one turn, not a loop (gate/gate.ts). */
|
|
804
|
+
gateAutoFix: z.boolean().default(true),
|
|
729
805
|
});
|
|
730
806
|
export type SandboxSettings = z.infer<typeof SandboxSettingsSchema>;
|
|
731
807
|
|
|
@@ -793,13 +869,21 @@ export const InputSavingsSchema = z.object({
|
|
|
793
869
|
});
|
|
794
870
|
export type InputSavings = z.infer<typeof InputSavingsSchema>;
|
|
795
871
|
|
|
796
|
-
// One arm of
|
|
797
|
-
// without it.
|
|
798
|
-
export const SavingsArmSchema = z.object({ turns: z.number(),
|
|
872
|
+
// One arm of a turn-level experiment: the turns that ran with the mechanism, and the turns the holdout ran
|
|
873
|
+
// without it. A mean PER TURN, because the arms never hold the same number of turns.
|
|
874
|
+
export const SavingsArmSchema = z.object({ turns: z.number(), mean: z.number() });
|
|
799
875
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
876
|
+
/* A turn-level A/B — the one shape both of this sandbox's turn experiments report in, because they differ in
|
|
877
|
+
* nothing but which flag flips and what the turns are judged on. Only turns the mechanism was ELIGIBLE for are
|
|
878
|
+
* counted: a turn under a custom system prompt drops the terse steer along with everything else the daemon
|
|
879
|
+
* appends, so it belongs to neither arm.
|
|
880
|
+
*
|
|
881
|
+
* `metric` says what `mean` counts and what `deltaPct` is a delta in. The terse steer is judged on the model's
|
|
882
|
+
* OWN output tokens, which is the thing it steers. Pre-injection is judged on COST, because it spends input
|
|
883
|
+
* tokens deliberately to buy back search turns — scored on output tokens it would look like a pure expense,
|
|
884
|
+
* and scored on input tokens like a pure loss; the trade only nets out in money. */
|
|
885
|
+
export const TurnExperimentSchema = z.object({
|
|
886
|
+
metric: z.enum(["outputTokens", "costUsd"]),
|
|
803
887
|
on: SavingsArmSchema,
|
|
804
888
|
off: SavingsArmSchema,
|
|
805
889
|
// Turns per arm before a delta is reported at all. Carried on the wire so the screen's "measuring…" state
|
|
@@ -807,18 +891,24 @@ export const OutputSavingsSchema = z.object({
|
|
|
807
891
|
minTurns: z.number(),
|
|
808
892
|
/* The three below are present TOGETHER, and only once both arms clear `minTurns` — a schema that can't
|
|
809
893
|
* express a half-measured experiment is how a 34%-that-becomes-8%-tomorrow never reaches the screen.
|
|
810
|
-
* deltaPct
|
|
811
|
-
* marginPct
|
|
812
|
-
*
|
|
894
|
+
* deltaPct — change in the metric's mean per turn under the mechanism; negative is a saving.
|
|
895
|
+
* marginPct — ± percentage points, 95% (Welch, unequal variances and unequal arms).
|
|
896
|
+
* saved — what the delta is worth over the turns that actually ran with it, in this window, in the
|
|
897
|
+
* metric's own unit (tokens, or dollars). */
|
|
813
898
|
deltaPct: z.number().optional(),
|
|
814
899
|
marginPct: z.number().optional(),
|
|
815
|
-
|
|
900
|
+
saved: z.number().optional(),
|
|
816
901
|
});
|
|
817
|
-
export type
|
|
902
|
+
export type TurnExperiment = z.infer<typeof TurnExperimentSchema>;
|
|
818
903
|
|
|
819
|
-
// `output`
|
|
820
|
-
// isn't there reads as "not measured", which is the truth, while zeros would read as "measured,
|
|
821
|
-
|
|
904
|
+
// `output`/`context` are absent when that experiment isn't running at all (its flag off, or no holdout set) — a
|
|
905
|
+
// section that isn't there reads as "not measured", which is the truth, while zeros would read as "measured,
|
|
906
|
+
// worth nothing".
|
|
907
|
+
export const SavingsReportSchema = z.object({
|
|
908
|
+
input: InputSavingsSchema,
|
|
909
|
+
output: TurnExperimentSchema.optional(),
|
|
910
|
+
context: TurnExperimentSchema.optional(),
|
|
911
|
+
});
|
|
822
912
|
export type SavingsReport = z.infer<typeof SavingsReportSchema>;
|
|
823
913
|
|
|
824
914
|
// ---- intentic CLI ----
|
|
@@ -971,9 +1061,9 @@ export const RepoChangesSchema = z.object({
|
|
|
971
1061
|
remote: GitRemoteStateSchema.optional(),
|
|
972
1062
|
// WHICH AGENT PUT IT THERE: repo-relative path → the agent ids that landed it, newest land first. Keyed by
|
|
973
1063
|
// PATH rather than carried on each GitChange because a path can be listed on two sides at once (staged and
|
|
974
|
-
// edited again) and its origin is the same fact for both. Only agents
|
|
975
|
-
//
|
|
976
|
-
// agents/origins.ts),
|
|
1064
|
+
// edited again) and its origin is the same fact for both. Only branch-backed agents whose work passed
|
|
1065
|
+
// through land can appear here; workspace conversations, terminal edits and the user's typing are absent
|
|
1066
|
+
// (see agents/origins.ts), so the panel badges an attributable agent and says nothing for anyone else.
|
|
977
1067
|
// Ids, not titles: the identity for every id named here rides the response once, in `originAgents`.
|
|
978
1068
|
origins: z.record(z.string(), z.array(z.string())).optional(),
|
|
979
1069
|
// Why the repo could not be scanned at all, condensed to git's own one-line reason ("fatal: bad object HEAD").
|
|
@@ -1215,8 +1305,9 @@ export type WorkspaceClassification = z.infer<typeof WorkspaceClassificationSche
|
|
|
1215
1305
|
// within `text` so clients highlight without re-finding the needle.
|
|
1216
1306
|
export const WorkspaceSearchQuerySchema = z.object({
|
|
1217
1307
|
query: z.string().min(2).max(512),
|
|
1218
|
-
// Search verbs only — anchor/git verbs (outline, context, log, who, …) are CLI-only surface.
|
|
1219
|
-
|
|
1308
|
+
// Search verbs only — anchor/git verbs (outline, context, log, who, …) are CLI-only surface. Natural language
|
|
1309
|
+
// has no verb of its own: `q` classifies the query and answers it semantically when the words call for it.
|
|
1310
|
+
mode: z.enum(["q", "find", "files", "def", "refs", "sym", "ast"]).optional(),
|
|
1220
1311
|
includeIgnored: z.stringbool().optional(),
|
|
1221
1312
|
limit: z.coerce.number().int().positive().optional(),
|
|
1222
1313
|
after: z.string().optional(),
|
|
@@ -1244,6 +1335,9 @@ export const WorkspaceSearchFreshnessSchema = z.object({
|
|
|
1244
1335
|
state: z.enum(["fresh", "building", "stale"]),
|
|
1245
1336
|
ageMs: z.number().optional(),
|
|
1246
1337
|
progress: z.number().optional(),
|
|
1338
|
+
// How many files the index has not caught up with, when it is stale. A count is reportable; "stale" alone
|
|
1339
|
+
// reads as a warning about the answer, which it almost never is.
|
|
1340
|
+
behind: z.number().optional(),
|
|
1247
1341
|
});
|
|
1248
1342
|
export type WorkspaceSearchFreshness = z.infer<typeof WorkspaceSearchFreshnessSchema>;
|
|
1249
1343
|
export const WorkspaceSearchResultSchema = z.object({
|
|
@@ -1255,8 +1349,12 @@ export const WorkspaceSearchResultSchema = z.object({
|
|
|
1255
1349
|
truncated: z.boolean(),
|
|
1256
1350
|
cursor: z.string().optional(),
|
|
1257
1351
|
hint: z.string().optional(),
|
|
1258
|
-
// Code-graph neighbors of the top hits (definition anchors +
|
|
1352
|
+
// Code-graph neighbors of the top hits (definition anchors + the strongest caller of each).
|
|
1259
1353
|
related: z.array(z.string()).optional(),
|
|
1354
|
+
// Ranked `path:line` anchors that placed but were NOT shown, best first — the answer often sits at rank 5–13,
|
|
1355
|
+
// behind groups the budget spent itself on. The text surface has always printed this map; a JSON caller could
|
|
1356
|
+
// not see it, so it had to page through `cursor` to learn what the terminal was told up front.
|
|
1357
|
+
candidates: z.array(z.string()).optional(),
|
|
1260
1358
|
// Run provenance for benchmarking: retrieval stages DISABLED this invocation (absent = full pipeline).
|
|
1261
1359
|
features: z.array(z.string()).optional(),
|
|
1262
1360
|
});
|
|
@@ -2016,9 +2114,9 @@ export const AutomationRunSchema = z.object({
|
|
|
2016
2114
|
// as "it never fired" — the one reading a 3 a.m. automation must not be given.
|
|
2017
2115
|
outcome: z.enum(["completed", "skipped", "error", "interrupted"]),
|
|
2018
2116
|
detail: z.string().optional(),
|
|
2019
|
-
// The
|
|
2020
|
-
//
|
|
2021
|
-
|
|
2117
|
+
// The stable conversation opened by the wake, so the row can open the provider-neutral agent transcript.
|
|
2118
|
+
// Absent only for a run skipped before a conversation was needed.
|
|
2119
|
+
conversationId: z.string().optional(),
|
|
2022
2120
|
});
|
|
2023
2121
|
export type AutomationRun = z.infer<typeof AutomationRunSchema>;
|
|
2024
2122
|
|
|
@@ -2118,9 +2216,17 @@ export const CiRunsResponseSchema = z.object({
|
|
|
2118
2216
|
repos: z.array(CiRepoSchema),
|
|
2119
2217
|
// Newest first, across all mapped repos.
|
|
2120
2218
|
runs: z.array(PipelineRunSchema),
|
|
2219
|
+
// When the owner last opened the pipelines view. Rides the runs response so the rail can decide what is
|
|
2220
|
+
// NEW without a second call — a breakage older than this has already been seen and must not badge again.
|
|
2221
|
+
// Absent ⇒ never opened, so everything counts as unseen.
|
|
2222
|
+
seenAt: z.number().optional(),
|
|
2121
2223
|
});
|
|
2122
2224
|
export type CiRunsResponse = z.infer<typeof CiRunsResponseSchema>;
|
|
2123
2225
|
|
|
2226
|
+
// Stamping the view as read hands back the timestamp it wrote, so the client updates without a refetch.
|
|
2227
|
+
export const CiSeenResponseSchema = z.object({ seenAt: z.number() });
|
|
2228
|
+
export type CiSeenResponse = z.infer<typeof CiSeenResponseSchema>;
|
|
2229
|
+
|
|
2124
2230
|
// rerun/cancel/fix address a run by repo + vendor id; the daemon re-resolves repo → project + token per call,
|
|
2125
2231
|
// so a stale card can't act on a project the workspace no longer maps to.
|
|
2126
2232
|
export const CiRunParamSchema = z.object({ repo: z.string(), runId: z.number() });
|
|
@@ -2130,6 +2236,96 @@ export type CiRunParam = z.infer<typeof CiRunParamSchema>;
|
|
|
2130
2236
|
export const CiFixResponseSchema = z.object({ conversationId: z.string() });
|
|
2131
2237
|
export type CiFixResponse = z.infer<typeof CiFixResponseSchema>;
|
|
2132
2238
|
|
|
2239
|
+
/* ---- the landing gate: the workspace's own verdict on the composite of landed work ----
|
|
2240
|
+
*
|
|
2241
|
+
* WHERE THIS SITS, and why it is not one of the four other places it could:
|
|
2242
|
+
*
|
|
2243
|
+
* A fleet of 5-20 agents lands work into the main tree as UNCOMMITTED changes (agents/land.ts), the user
|
|
2244
|
+
* reviews and commits it by parts, pushes, and CI answers minutes later. This gate front-runs that answer by
|
|
2245
|
+
* asking the same question of the same artifact, before the push.
|
|
2246
|
+
*
|
|
2247
|
+
* NOT inside an agent's turn. An isolated worktree's `node_modules` reads as the MAIN checkout's, so a
|
|
2248
|
+
* monorepo's workspace links resolve cross-package imports to /work's sources rather than the worktree's edited
|
|
2249
|
+
* ones (agents/worktrees.ts). A suite run in a worktree therefore tests the agent's edits against everyone
|
|
2250
|
+
* else's UNEDITED siblings: it invents failures that don't exist and passes changes that break on the
|
|
2251
|
+
* composite, and two agents editing one contract each go green alone and red together. The composite is the
|
|
2252
|
+
* only honest artifact, and it exists in exactly one place — the main working tree.
|
|
2253
|
+
*
|
|
2254
|
+
* NOT at commit. The user commits BY PARTS, and a suite reads the worktree, not the index — so a verdict taken
|
|
2255
|
+
* at a partial commit describes a tree that never gets pushed as such. Commit is where a verdict is DISPLAYED
|
|
2256
|
+
* (ReviewPanel's badge), computed earlier.
|
|
2257
|
+
*
|
|
2258
|
+
* NOT at push. By then HEAD has moved, per-path attribution has expired (agents/origins.ts), and the agents may
|
|
2259
|
+
* be archived with their worktrees reclaimed — so the fix starts cold, in the same position `/ci/fix` is in.
|
|
2260
|
+
* That saves the CI round-trip and none of the context switch.
|
|
2261
|
+
*
|
|
2262
|
+
* So: after the land, before the staging — the one window where the artifact is what CI will see, attribution
|
|
2263
|
+
* is still live, and nobody is waiting on it. */
|
|
2264
|
+
|
|
2265
|
+
/* What the gate has to say about the tree right now.
|
|
2266
|
+
*
|
|
2267
|
+
* idle — no command configured, or nothing has run yet.
|
|
2268
|
+
* armed — work landed; the quiet period is counting down (see gateQuietMs).
|
|
2269
|
+
* running — the check is live. `output` grows as it streams.
|
|
2270
|
+
* passed — exited 0 over `fingerprint`.
|
|
2271
|
+
* failed — exited non-zero, or was killed by gateTimeoutMs (`timedOut`). The state a fix answers.
|
|
2272
|
+
* error — the gate itself could not run: the command was not spawnable. NOT a fix-able failure, because
|
|
2273
|
+
* there is nothing wrong with the code — the gate is misconfigured, and saying "tests failed"
|
|
2274
|
+
* would send an agent hunting a bug that isn't there.
|
|
2275
|
+
* cancelled — the user stopped the run, or the tree moved under it.
|
|
2276
|
+
*/
|
|
2277
|
+
export const GateStatusSchema = z.enum(["idle", "armed", "running", "passed", "failed", "error", "cancelled"]);
|
|
2278
|
+
export type GateStatus = z.infer<typeof GateStatusSchema>;
|
|
2279
|
+
|
|
2280
|
+
// An agent whose landed work the failure implicates. `paths` are its attributed files that the check's own
|
|
2281
|
+
// output NAMED — empty when the output named none of them, which is the honest shape for a failure that could
|
|
2282
|
+
// not be pinpointed (an integration break between two deltas, a suite that prints no paths at all): the agent
|
|
2283
|
+
// is listed because its work is in the tree under test, not because anything accused it.
|
|
2284
|
+
export const GateAgentSchema = z.object({
|
|
2285
|
+
agentId: z.string(),
|
|
2286
|
+
title: z.string().optional(),
|
|
2287
|
+
provider: AgentProviderSchema.optional(),
|
|
2288
|
+
paths: z.array(z.string()),
|
|
2289
|
+
});
|
|
2290
|
+
export type GateAgent = z.infer<typeof GateAgentSchema>;
|
|
2291
|
+
|
|
2292
|
+
// The fix turn one red verdict got. It is a workspace conversation because the composite it must reproduce
|
|
2293
|
+
// lives as uncommitted content in the main working tree; it still has the same stable registry identity as any
|
|
2294
|
+
// other conversation.
|
|
2295
|
+
export const GateFixSchema = z.object({
|
|
2296
|
+
startedAt: z.number(),
|
|
2297
|
+
conversationId: z.string(),
|
|
2298
|
+
// `running` while the turn streams; `done` when it ended cleanly, whatever the re-check then said;
|
|
2299
|
+
// `error` when the turn itself failed (a provider outage, no credential), which is worth distinguishing
|
|
2300
|
+
// because it is the one case where re-running the fix could still help.
|
|
2301
|
+
outcome: z.enum(["running", "done", "error"]),
|
|
2302
|
+
detail: z.string().optional(),
|
|
2303
|
+
});
|
|
2304
|
+
export type GateFix = z.infer<typeof GateFixSchema>;
|
|
2305
|
+
|
|
2306
|
+
export const GateVerdictSchema = z.object({
|
|
2307
|
+
status: GateStatusSchema,
|
|
2308
|
+
// The command this verdict ran, echoed rather than read back from settings: a verdict read after the
|
|
2309
|
+
// setting changed still has to say what produced it.
|
|
2310
|
+
command: z.string(),
|
|
2311
|
+
startedAt: z.number().optional(),
|
|
2312
|
+
finishedAt: z.number().optional(),
|
|
2313
|
+
exitCode: z.number().optional(),
|
|
2314
|
+
timedOut: z.boolean().optional(),
|
|
2315
|
+
// The check's own output, tail-capped (GATE_OUTPUT_BYTES). The tail, not the head: a suite's verdict and
|
|
2316
|
+
// its failure summary are at the end, and a head-capped buffer of a chatty build is all progress lines.
|
|
2317
|
+
output: z.string(),
|
|
2318
|
+
/* WHICH TREE this verdict is about — HEAD plus the shape of every repo's uncommitted content. Recomputed
|
|
2319
|
+
* on read: when it no longer matches, the verdict is `stale` and the badge says so instead of asserting a
|
|
2320
|
+
* green light over a tree that has since moved. This is what keeps a passed verdict from outliving its
|
|
2321
|
+
* subject when the user edits, discards, or commits half of it. */
|
|
2322
|
+
fingerprint: z.string(),
|
|
2323
|
+
stale: z.boolean(),
|
|
2324
|
+
implicated: z.array(GateAgentSchema),
|
|
2325
|
+
fix: GateFixSchema.optional(),
|
|
2326
|
+
});
|
|
2327
|
+
export type GateVerdict = z.infer<typeof GateVerdictSchema>;
|
|
2328
|
+
|
|
2133
2329
|
// ---- drafts: agent-proposed posts awaiting owner approval (.intentic/drafts/<id>.json) ----
|
|
2134
2330
|
// One JSON file per draft. The AGENT creates drafts with its normal file tools — it can't call daemon routes,
|
|
2135
2331
|
// the same split as the environment proposal — while the daemon edits/deletes them on the owner's behalf, so
|
|
@@ -2271,11 +2467,9 @@ export type PortForwardResult = z.infer<typeof PortForwardResultSchema>;
|
|
|
2271
2467
|
// process (a lingering shell after a crash reads false). A process row that maps to an installed extension's
|
|
2272
2468
|
// declared process carries extensionId+processName, the address for its /extensions start/stop routes. The
|
|
2273
2469
|
// `{name}` kill-route param is a bare string validated in the handler (a bad name is a BAD_REQUEST) since the
|
|
2274
|
-
// same charset gates a `tmux kill-session -t` shell-out.
|
|
2275
|
-
//
|
|
2276
|
-
// /system/
|
|
2277
|
-
// the same rule as `agent`: it is a record of work, not a place. Its `label` is the page's own title and
|
|
2278
|
-
// `url` the page it is on, which is the one thing a browser pill has to say that a terminal pill does not.
|
|
2470
|
+
// same charset gates a `tmux kill-session -t` shell-out. The agent's BROWSER is deliberately NOT one of these
|
|
2471
|
+
// kinds: a Chromium with its own tab strip is a surface in its own right, not a pane in the terminal panel, so
|
|
2472
|
+
// it lists from /system/browsers with the pages it has open (BrowserSessionSchema below).
|
|
2279
2473
|
//
|
|
2280
2474
|
// `activityAt` (epoch ms of the session's last output) and `exitCode` (the LAST window's exit status, absent
|
|
2281
2475
|
// while that pane still lives) are what let a finished session be READ rather than merely listed: the panel's
|
|
@@ -2285,19 +2479,56 @@ export type PortForwardResult = z.infer<typeof PortForwardResultSchema>;
|
|
|
2285
2479
|
export const TerminalSessionSchema = z.object({
|
|
2286
2480
|
name: z.string(),
|
|
2287
2481
|
label: z.string().optional(),
|
|
2288
|
-
kind: z.enum(["shell", "panel", "agent", "job", "process"
|
|
2482
|
+
kind: z.enum(["shell", "panel", "agent", "job", "process"]),
|
|
2289
2483
|
running: z.boolean(),
|
|
2290
2484
|
activityAt: z.number(),
|
|
2291
2485
|
exitCode: z.number().optional(),
|
|
2292
2486
|
extensionId: z.string().optional(),
|
|
2293
2487
|
processName: z.string().optional(),
|
|
2294
|
-
// Browser sessions only: the page the agent is on right now.
|
|
2295
|
-
url: z.string().optional(),
|
|
2296
2488
|
});
|
|
2297
2489
|
export const TerminalsListSchema = z.object({ sessions: z.array(TerminalSessionSchema) });
|
|
2298
2490
|
export type TerminalsList = z.infer<typeof TerminalsListSchema>;
|
|
2299
2491
|
export const TerminalNameParamSchema = z.object({ name: z.string() });
|
|
2300
2492
|
|
|
2493
|
+
/* ---- browsers: the Chromium the agent drives through its @playwright/mcp tools ----
|
|
2494
|
+
*
|
|
2495
|
+
* A `browser-<sdk session>` Chromium (browser/browser-sessions.ts), watchable live over the
|
|
2496
|
+
* /system/browser-view WebSocket. It lists apart from the terminals because it is shaped differently in the one
|
|
2497
|
+
* way that decides a UI: a terminal is ONE stream of bytes, while a browser holds SEVERAL pages at once and the
|
|
2498
|
+
* question "what is the agent looking at?" only has an answer if the wire carries all of them. So `pages` is the
|
|
2499
|
+
* point of this schema — the view renders them as a tab strip and binds the screencast to whichever the user
|
|
2500
|
+
* picks, and `active` is the one the agent itself last touched (what the view follows until the user says
|
|
2501
|
+
* otherwise).
|
|
2502
|
+
*
|
|
2503
|
+
* `id` is opaque and minted per session, and it is what makes a tab survive a relist: it is stable for the life
|
|
2504
|
+
* of the page, unlike its url (the agent navigates away) or its position (a closed tab renumbers the rest). */
|
|
2505
|
+
export const BrowserPageSchema = z.object({
|
|
2506
|
+
id: z.string(),
|
|
2507
|
+
// The page's own title. Absent mid-navigation, which is exactly when a tab still needs to render.
|
|
2508
|
+
title: z.string().optional(),
|
|
2509
|
+
url: z.string(),
|
|
2510
|
+
// The page the agent last drove. Exactly one page per running session has it.
|
|
2511
|
+
active: z.boolean(),
|
|
2512
|
+
});
|
|
2513
|
+
export const BrowserSessionSchema = z.object({
|
|
2514
|
+
name: z.string(),
|
|
2515
|
+
// The pill's text: the active page's title, else its host, else which browser this is.
|
|
2516
|
+
label: z.string(),
|
|
2517
|
+
// Which MCP server drives it: `web` (the credential-free browser) or a logged-in capability's id — the
|
|
2518
|
+
// difference between a throwaway page and one signed in as the user, which is worth saying out loud.
|
|
2519
|
+
server: z.string(),
|
|
2520
|
+
// False once that Chromium is gone (the turn ended, the agent closed it, it crashed). A finished session
|
|
2521
|
+
// still lists for a while, with the pages it had — the record of where the agent went.
|
|
2522
|
+
running: z.boolean(),
|
|
2523
|
+
activityAt: z.number(),
|
|
2524
|
+
pages: z.array(BrowserPageSchema),
|
|
2525
|
+
});
|
|
2526
|
+
export type BrowserPage = z.infer<typeof BrowserPageSchema>;
|
|
2527
|
+
export type BrowserSession = z.infer<typeof BrowserSessionSchema>;
|
|
2528
|
+
export const BrowsersListSchema = z.object({ sessions: z.array(BrowserSessionSchema) });
|
|
2529
|
+
export type BrowsersList = z.infer<typeof BrowsersListSchema>;
|
|
2530
|
+
export const BrowserNameParamSchema = z.object({ name: z.string() });
|
|
2531
|
+
|
|
2301
2532
|
// ---- environment: the overlay Dockerfile extending the sandbox image ----
|
|
2302
2533
|
// The approved file is DAEMON-COMPOSED: pinned FROM + capability fragments + the owner-approved custom section.
|
|
2303
2534
|
// The agent writes the proposal file (.intentic/environment.Dockerfile — custom-section content only, no FROM)
|
|
@@ -2463,7 +2694,8 @@ export const UsageTurnSchema = z.object({
|
|
|
2463
2694
|
// when the provider's own subscription default served it without the daemon naming one.
|
|
2464
2695
|
model: z.string().optional(),
|
|
2465
2696
|
harness: z.string(),
|
|
2466
|
-
// The conversation this turn belonged to, so spend can join to a fleet agent. Absent
|
|
2697
|
+
// The conversation this turn belonged to, so spend can join to a fleet agent. Absent only for an internal
|
|
2698
|
+
// one-shot turn that has no conversation identity.
|
|
2467
2699
|
conversationId: z.string().optional(),
|
|
2468
2700
|
// The provider's own turn count for the request (a Claude "turn" can be several under the hood), so turns
|
|
2469
2701
|
// and cost stay comparable across providers. 1 when the provider reported none.
|
|
@@ -2482,6 +2714,15 @@ export const UsageTurnSchema = z.object({
|
|
|
2482
2714
|
* no control to be compared against. Pooling those into the off-arm would compare steered turns against a
|
|
2483
2715
|
* population selected by something other than the coin flip, which is not a control at all. */
|
|
2484
2716
|
terse: z.boolean().optional(),
|
|
2717
|
+
/* Which arm of the pre-injection experiment this turn ran on (settings.iqContextHoldout), on the same terms
|
|
2718
|
+
* as `terse` above: absent ⇒ outside the experiment.
|
|
2719
|
+
*
|
|
2720
|
+
* TRUE means the turn was ASSIGNED the retrieved context, not that a note was necessarily prepended — a
|
|
2721
|
+
* treatment turn whose retrieval came back empty or unconfident injects nothing. That is deliberate: the
|
|
2722
|
+
* arms have to be the coin flip's populations, and re-labelling a turn by what retrieval happened to find
|
|
2723
|
+
* would sort turns by how searchable their question was, which is a property of the question. The control
|
|
2724
|
+
* arm contains the same unsearchable questions in the same proportion, so they cancel. */
|
|
2725
|
+
iqContext: z.boolean().optional(),
|
|
2485
2726
|
});
|
|
2486
2727
|
export type UsageTurn = z.infer<typeof UsageTurnSchema>;
|
|
2487
2728
|
|