@intentic/sandbox-contract 1.166.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/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/gate.contract.d.ts +1 -1
- 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 +29 -3
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +22 -4
- 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/events.d.ts +4 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +82 -79
- package/dist/index.d.ts.map +1 -1
- package/dist/schemas.d.ts +102 -22
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +56 -13
- package/dist/schemas.js.map +1 -1
- package/package.json +2 -2
- package/src/agent-catalog.ts +6 -11
- package/src/contracts/agents.contract.ts +8 -6
- package/src/contracts/gate.contract.ts +2 -2
- 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 +2 -1
- package/src/model-order.ts +1 -1
- package/src/quick-model.test.ts +1 -1
- package/src/schemas.test.ts +10 -0
- package/src/schemas.ts +173 -67
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,7 +774,7 @@ 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),
|
|
729
|
-
/* THE LANDING GATE — the check command run over the COMPOSITE of landed work
|
|
777
|
+
/* THE LANDING GATE — the check command run over the COMPOSITE of landed work after a landing debounce.
|
|
730
778
|
* Empty ⇒ no gate at all, which is the default: only the owner knows what verifies this workspace, and a
|
|
731
779
|
* guessed command that fails on a fresh clone would read as the gate finding a bug on its first run.
|
|
732
780
|
*
|
|
@@ -821,13 +869,21 @@ export const InputSavingsSchema = z.object({
|
|
|
821
869
|
});
|
|
822
870
|
export type InputSavings = z.infer<typeof InputSavingsSchema>;
|
|
823
871
|
|
|
824
|
-
// One arm of
|
|
825
|
-
// without it.
|
|
826
|
-
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() });
|
|
827
875
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
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"]),
|
|
831
887
|
on: SavingsArmSchema,
|
|
832
888
|
off: SavingsArmSchema,
|
|
833
889
|
// Turns per arm before a delta is reported at all. Carried on the wire so the screen's "measuring…" state
|
|
@@ -835,18 +891,24 @@ export const OutputSavingsSchema = z.object({
|
|
|
835
891
|
minTurns: z.number(),
|
|
836
892
|
/* The three below are present TOGETHER, and only once both arms clear `minTurns` — a schema that can't
|
|
837
893
|
* express a half-measured experiment is how a 34%-that-becomes-8%-tomorrow never reaches the screen.
|
|
838
|
-
* deltaPct
|
|
839
|
-
* marginPct
|
|
840
|
-
*
|
|
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). */
|
|
841
898
|
deltaPct: z.number().optional(),
|
|
842
899
|
marginPct: z.number().optional(),
|
|
843
|
-
|
|
900
|
+
saved: z.number().optional(),
|
|
844
901
|
});
|
|
845
|
-
export type
|
|
902
|
+
export type TurnExperiment = z.infer<typeof TurnExperimentSchema>;
|
|
846
903
|
|
|
847
|
-
// `output`
|
|
848
|
-
// isn't there reads as "not measured", which is the truth, while zeros would read as "measured,
|
|
849
|
-
|
|
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
|
+
});
|
|
850
912
|
export type SavingsReport = z.infer<typeof SavingsReportSchema>;
|
|
851
913
|
|
|
852
914
|
// ---- intentic CLI ----
|
|
@@ -999,9 +1061,9 @@ export const RepoChangesSchema = z.object({
|
|
|
999
1061
|
remote: GitRemoteStateSchema.optional(),
|
|
1000
1062
|
// WHICH AGENT PUT IT THERE: repo-relative path → the agent ids that landed it, newest land first. Keyed by
|
|
1001
1063
|
// PATH rather than carried on each GitChange because a path can be listed on two sides at once (staged and
|
|
1002
|
-
// edited again) and its origin is the same fact for both. Only agents
|
|
1003
|
-
//
|
|
1004
|
-
// 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.
|
|
1005
1067
|
// Ids, not titles: the identity for every id named here rides the response once, in `originAgents`.
|
|
1006
1068
|
origins: z.record(z.string(), z.array(z.string())).optional(),
|
|
1007
1069
|
// Why the repo could not be scanned at all, condensed to git's own one-line reason ("fatal: bad object HEAD").
|
|
@@ -2052,9 +2114,9 @@ export const AutomationRunSchema = z.object({
|
|
|
2052
2114
|
// as "it never fired" — the one reading a 3 a.m. automation must not be given.
|
|
2053
2115
|
outcome: z.enum(["completed", "skipped", "error", "interrupted"]),
|
|
2054
2116
|
detail: z.string().optional(),
|
|
2055
|
-
// The
|
|
2056
|
-
//
|
|
2057
|
-
|
|
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(),
|
|
2058
2120
|
});
|
|
2059
2121
|
export type AutomationRun = z.infer<typeof AutomationRunSchema>;
|
|
2060
2122
|
|
|
@@ -2227,13 +2289,12 @@ export const GateAgentSchema = z.object({
|
|
|
2227
2289
|
});
|
|
2228
2290
|
export type GateAgent = z.infer<typeof GateAgentSchema>;
|
|
2229
2291
|
|
|
2230
|
-
// The fix turn one red verdict got.
|
|
2231
|
-
//
|
|
2232
|
-
//
|
|
2233
|
-
// the same thing an automation's run record carries for the same reason.
|
|
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.
|
|
2234
2295
|
export const GateFixSchema = z.object({
|
|
2235
2296
|
startedAt: z.number(),
|
|
2236
|
-
|
|
2297
|
+
conversationId: z.string(),
|
|
2237
2298
|
// `running` while the turn streams; `done` when it ended cleanly, whatever the re-check then said;
|
|
2238
2299
|
// `error` when the turn itself failed (a provider outage, no credential), which is worth distinguishing
|
|
2239
2300
|
// because it is the one case where re-running the fix could still help.
|
|
@@ -2406,11 +2467,9 @@ export type PortForwardResult = z.infer<typeof PortForwardResultSchema>;
|
|
|
2406
2467
|
// process (a lingering shell after a crash reads false). A process row that maps to an installed extension's
|
|
2407
2468
|
// declared process carries extensionId+processName, the address for its /extensions start/stop routes. The
|
|
2408
2469
|
// `{name}` kill-route param is a bare string validated in the handler (a bad name is a BAD_REQUEST) since the
|
|
2409
|
-
// same charset gates a `tmux kill-session -t` shell-out.
|
|
2410
|
-
//
|
|
2411
|
-
// /system/
|
|
2412
|
-
// the same rule as `agent`: it is a record of work, not a place. Its `label` is the page's own title and
|
|
2413
|
-
// `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).
|
|
2414
2473
|
//
|
|
2415
2474
|
// `activityAt` (epoch ms of the session's last output) and `exitCode` (the LAST window's exit status, absent
|
|
2416
2475
|
// while that pane still lives) are what let a finished session be READ rather than merely listed: the panel's
|
|
@@ -2420,19 +2479,56 @@ export type PortForwardResult = z.infer<typeof PortForwardResultSchema>;
|
|
|
2420
2479
|
export const TerminalSessionSchema = z.object({
|
|
2421
2480
|
name: z.string(),
|
|
2422
2481
|
label: z.string().optional(),
|
|
2423
|
-
kind: z.enum(["shell", "panel", "agent", "job", "process"
|
|
2482
|
+
kind: z.enum(["shell", "panel", "agent", "job", "process"]),
|
|
2424
2483
|
running: z.boolean(),
|
|
2425
2484
|
activityAt: z.number(),
|
|
2426
2485
|
exitCode: z.number().optional(),
|
|
2427
2486
|
extensionId: z.string().optional(),
|
|
2428
2487
|
processName: z.string().optional(),
|
|
2429
|
-
// Browser sessions only: the page the agent is on right now.
|
|
2430
|
-
url: z.string().optional(),
|
|
2431
2488
|
});
|
|
2432
2489
|
export const TerminalsListSchema = z.object({ sessions: z.array(TerminalSessionSchema) });
|
|
2433
2490
|
export type TerminalsList = z.infer<typeof TerminalsListSchema>;
|
|
2434
2491
|
export const TerminalNameParamSchema = z.object({ name: z.string() });
|
|
2435
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
|
+
|
|
2436
2532
|
// ---- environment: the overlay Dockerfile extending the sandbox image ----
|
|
2437
2533
|
// The approved file is DAEMON-COMPOSED: pinned FROM + capability fragments + the owner-approved custom section.
|
|
2438
2534
|
// The agent writes the proposal file (.intentic/environment.Dockerfile — custom-section content only, no FROM)
|
|
@@ -2598,7 +2694,8 @@ export const UsageTurnSchema = z.object({
|
|
|
2598
2694
|
// when the provider's own subscription default served it without the daemon naming one.
|
|
2599
2695
|
model: z.string().optional(),
|
|
2600
2696
|
harness: z.string(),
|
|
2601
|
-
// 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.
|
|
2602
2699
|
conversationId: z.string().optional(),
|
|
2603
2700
|
// The provider's own turn count for the request (a Claude "turn" can be several under the hood), so turns
|
|
2604
2701
|
// and cost stay comparable across providers. 1 when the provider reported none.
|
|
@@ -2617,6 +2714,15 @@ export const UsageTurnSchema = z.object({
|
|
|
2617
2714
|
* no control to be compared against. Pooling those into the off-arm would compare steered turns against a
|
|
2618
2715
|
* population selected by something other than the coin flip, which is not a control at all. */
|
|
2619
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(),
|
|
2620
2726
|
});
|
|
2621
2727
|
export type UsageTurn = z.infer<typeof UsageTurnSchema>;
|
|
2622
2728
|
|