@intentic/sandbox-contract 1.222.0 → 1.224.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.
Files changed (86) hide show
  1. package/dist/chores/chores.js +1 -1
  2. package/dist/chores/chores.js.map +1 -1
  3. package/dist/chores/prompt.d.ts.map +1 -1
  4. package/dist/chores/prompt.js +3 -3
  5. package/dist/chores/prompt.js.map +1 -1
  6. package/dist/contracts/ports.contract.d.ts +11 -0
  7. package/dist/contracts/ports.contract.d.ts.map +1 -1
  8. package/dist/index.d.ts +11 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/schemas.d.ts +22 -0
  11. package/dist/schemas.d.ts.map +1 -1
  12. package/dist/schemas.js +3 -0
  13. package/dist/schemas.js.map +1 -1
  14. package/package.json +4 -4
  15. package/src/agent-catalog.ts +78 -78
  16. package/src/agent-run-model.ts +8 -8
  17. package/src/capability-env.ts +3 -3
  18. package/src/capability-secrets.ts +5 -5
  19. package/src/chores/chores.ts +67 -67
  20. package/src/chores/digest.ts +3 -3
  21. package/src/chores/extension-update.ts +3 -3
  22. package/src/chores/fix-deps.ts +3 -3
  23. package/src/chores/probes.ts +16 -16
  24. package/src/chores/prompt.ts +9 -9
  25. package/src/chores/stack.ts +17 -17
  26. package/src/chores/verdict.ts +23 -23
  27. package/src/contract-lock.ts +7 -7
  28. package/src/contracts/activity.contract.ts +1 -1
  29. package/src/contracts/agent.contract.ts +2 -2
  30. package/src/contracts/agents.contract.ts +11 -11
  31. package/src/contracts/automations.contract.ts +5 -5
  32. package/src/contracts/capabilities.contract.ts +7 -7
  33. package/src/contracts/chores.contract.ts +3 -3
  34. package/src/contracts/ci.contract.ts +2 -2
  35. package/src/contracts/claude.contract.ts +3 -3
  36. package/src/contracts/drafts.contract.ts +1 -1
  37. package/src/contracts/endpoints.contract.ts +2 -2
  38. package/src/contracts/extensions.contract.ts +8 -8
  39. package/src/contracts/git.contract.ts +6 -6
  40. package/src/contracts/grok.contract.ts +4 -4
  41. package/src/contracts/host.contract.ts +5 -5
  42. package/src/contracts/intentic.contract.ts +2 -2
  43. package/src/contracts/logs.contract.ts +1 -1
  44. package/src/contracts/loops.contract.ts +8 -8
  45. package/src/contracts/personas.contract.ts +8 -8
  46. package/src/contracts/ports.contract.ts +1 -1
  47. package/src/contracts/prepush.contract.ts +2 -2
  48. package/src/contracts/providers.contract.ts +4 -4
  49. package/src/contracts/public.contract.ts +1 -1
  50. package/src/contracts/push.contract.ts +2 -2
  51. package/src/contracts/secrets.contract.ts +2 -2
  52. package/src/contracts/sessions.contract.ts +2 -2
  53. package/src/contracts/settings.contract.ts +3 -3
  54. package/src/contracts/share.contract.ts +1 -1
  55. package/src/contracts/skills.contract.ts +2 -2
  56. package/src/contracts/system.contract.ts +9 -9
  57. package/src/contracts/usage.contract.ts +1 -1
  58. package/src/contracts/vpn.contract.ts +5 -5
  59. package/src/contracts/workflows.contract.ts +12 -12
  60. package/src/contracts/workspace.contract.ts +13 -13
  61. package/src/conversation-ids.ts +7 -7
  62. package/src/events.ts +136 -136
  63. package/src/history-state.ts +12 -12
  64. package/src/host-protocol.ts +7 -7
  65. package/src/hostnames.ts +15 -15
  66. package/src/index.ts +8 -8
  67. package/src/listener-protocol.ts +12 -12
  68. package/src/model-order.ts +30 -30
  69. package/src/output-fields.ts +9 -9
  70. package/src/path-refs.ts +6 -6
  71. package/src/publish-drafts.ts +6 -6
  72. package/src/quick-model.ts +15 -15
  73. package/src/routes.ts +19 -19
  74. package/src/runtime-state.ts +22 -22
  75. package/src/schemas.ts +930 -918
  76. package/src/search-globs.ts +6 -6
  77. package/src/session-names.ts +5 -5
  78. package/src/share-paths.ts +7 -7
  79. package/src/sse.ts +1 -1
  80. package/src/state-portability.ts +8 -8
  81. package/src/terminal-protocol.ts +3 -3
  82. package/src/title.ts +20 -20
  83. package/src/tunnel-ids.ts +7 -7
  84. package/src/versions.ts +5 -5
  85. package/src/workflow-faults.ts +11 -11
  86. package/src/workspace-state.ts +119 -119
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentic/sandbox-contract",
3
- "version": "1.222.0",
3
+ "version": "1.224.0",
4
4
  "description": "oRPC wire contract for the intentic sandbox daemon — shared by the daemon and its browser client",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -89,9 +89,9 @@
89
89
  "@orpc/contract": "1.14.13",
90
90
  "tslib": "2.8.1",
91
91
  "zod": "4.4.3",
92
- "@intentic/constants": "1.222.0",
93
- "@intentic/extension-manifest": "1.222.0",
94
- "@intentic/registry": "1.222.0"
92
+ "@intentic/constants": "1.224.0",
93
+ "@intentic/registry": "1.224.0",
94
+ "@intentic/extension-manifest": "1.224.0"
95
95
  },
96
96
  "devDependencies": {
97
97
  "@types/node": "24.13.2",
@@ -8,7 +8,7 @@ import {
8
8
  type PermissionMode,
9
9
  } from "./schemas.js";
10
10
 
11
- /* The provider / harness / model catalog every picker shares (the chat menu, the automations dialog) pure
11
+ /* The provider / harness / model catalog every picker shares (the chat menu, the automations dialog), pure
12
12
  * data keyed by the wire vocabulary in schemas.ts, so the surfaces can't drift. Live state stays with the
13
13
  * consumer (native Grok's model list is the daemon's own catalog for it, layered on top of modelsFor by the
14
14
  * web; ACP providers are merged in from the installed `agent` capabilities). */
@@ -26,13 +26,13 @@ export const PROVIDERS: readonly { label: string; value: NativeProvider }[] = [
26
26
  { label: "Codex", value: "codex" },
27
27
  { label: "Grok", value: "grok" },
28
28
  { label: "Kimi Code", value: "kimi" },
29
- // Labelled for the ACCOUNT, not the model family: the `gemini` id names one channel Google's Antigravity
29
+ // Labelled for the ACCOUNT, not the model family: the `gemini` id names one channel. Google's Antigravity,
30
30
  // and that channel vends Claude and GPT-OSS models alongside Gemini's own (see gemini-models.ts). A section
31
31
  // headed "Gemini" holding Claude Opus would be a lie; "Google" is what the whole list has in common.
32
32
  { label: "Google", value: "gemini" },
33
33
  ];
34
34
 
35
- // What it COSTS to unlock a provider, and what the user connects to do it the axis the picker groups on, since
35
+ // What it COSTS to unlock a provider, and what the user connects to do it, the axis the picker groups on, since
36
36
  // "can this row actually run" is the first thing a model list has to answer. `free` is not a courtesy tier: the
37
37
  // Google channel serves its models on an ordinary Google sign-in, at no subscription, which is the single most
38
38
  // useful thing this catalog can tell a user who has connected nothing yet.
@@ -40,7 +40,7 @@ export type AccessKind = "free" | "subscription" | "key";
40
40
 
41
41
  export interface ProviderAccess {
42
42
  readonly kind: AccessKind;
43
- // What the user connects, named the way its vendor names it this is the noun every connect prompt uses.
43
+ // What the user connects, named the way its vendor names it, this is the noun every connect prompt uses.
44
44
  readonly requirement: string;
45
45
  // What connecting it lets them run, for the connect gate's one-line pitch.
46
46
  readonly runs: string;
@@ -54,7 +54,7 @@ export const PROVIDER_ACCESS: Record<NativeProvider, ProviderAccess> = {
54
54
  gemini: { kind: "free", requirement: "Google sign-in", runs: "Gemini, Claude and GPT-OSS under Claude Code" },
55
55
  };
56
56
 
57
- /* THE PROVIDERS THAT COST NOTHING derived from the table above rather than named a second time, and read by
57
+ /* THE PROVIDERS THAT COST NOTHING, derived from the table above rather than named a second time, and read by
58
58
  * every surface that LEADS with a free option instead of merely labelling one.
59
59
  *
60
60
  * The distinction is worth the export. `accessBadge` answers "what does this row cost" for a row the user is
@@ -65,7 +65,7 @@ export const PROVIDER_ACCESS: Record<NativeProvider, ProviderAccess> = {
65
65
  export const FREE_PROVIDERS: readonly NativeProvider[] = NATIVE_PROVIDERS.filter((provider) => PROVIDER_ACCESS[provider].kind === "free");
66
66
  export const isFreeProvider = (provider: AgentProvider): boolean => FREE_PROVIDERS.includes(provider as NativeProvider);
67
67
 
68
- /* WHOSE ALLOWANCE A TURN ON THIS PROVIDER SPENDS, as the subject of a sentence a third naming of the same
68
+ /* WHOSE ALLOWANCE A TURN ON THIS PROVIDER SPENDS, as the subject of a sentence, a third naming of the same
69
69
  * five ids, and the third is not redundancy. PROVIDERS names the RUNTIME the user picks ("Claude Code", "Kimi
70
70
  * Code") and PROVIDER_ACCESS.requirement names the thing they CONNECT ("Claude subscription", "Google sign-in");
71
71
  * neither reads as English in "… usage limit reached", and neither is what a spent quota belongs to.
@@ -73,7 +73,7 @@ export const isFreeProvider = (provider: AgentProvider): boolean => FREE_PROVIDE
73
73
  * The routed providers are why this can't be inferred from the harness: a `gemini` turn drives Claude Opus 4.6
74
74
  * through Google's Antigravity channel on a plain Google sign-in, so the quota that refuses it is Google's and
75
75
  * Anthropic has no part in it. Saying "Claude usage limit reached" there sends the user to check the wrong
76
- * account and to a reset that is days out on a pool they never touched. */
76
+ * account, and to a reset that is days out on a pool they never touched. */
77
77
  export const PROVIDER_VENDOR: Record<NativeProvider, string> = {
78
78
  claude: "Claude",
79
79
  codex: "ChatGPT",
@@ -85,50 +85,50 @@ export const PROVIDER_VENDOR: Record<NativeProvider, string> = {
85
85
  // What a turn on this provider costs at the MARGIN, ordering the same three kinds by the only question a
86
86
  // helper spending the user's money on their behalf has to answer: free is free; a subscription is already paid
87
87
  // but has a quota the user watches; a key is metered, so every call is real money. Deliberately not folded into
88
- // AccessKind's declaration order a union's order is not a runtime fact, and this one is load-bearing.
88
+ // AccessKind's declaration order, a union's order is not a runtime fact, and this one is relied on.
89
89
  export const ACCESS_COST: Record<AccessKind, number> = { free: 0, subscription: 1, key: 2 };
90
90
 
91
- /* THE PROVIDER ID OF AN `endpoint` CAPABILITY a model API the user pointed us at, native or not, near or far.
91
+ /* THE PROVIDER ID OF AN `endpoint` CAPABILITY, a model API the user pointed us at, native or not, near or far.
92
92
  *
93
93
  * Namespaced rather than bare (which is what ACP agents are) because the two kinds mint providers with OPPOSITE
94
94
  * ability records: an ACP agent brings its own loop and gets the documented ACP floor, while an endpoint is
95
95
  * driven BY the Claude Code loop and gets its full ceiling. capabilitiesOf answers that from the id alone, so
96
96
  * the prefix is what keeps it a pure function of (provider, harness) instead of a lookup against the installed
97
- * manifest which the contract cannot see and the browser would have to pass in everywhere.
97
+ * manifest, which the contract cannot see and the browser would have to pass in everywhere.
98
98
  *
99
99
  * A SLASH, never a colon: `${provider}:${model}` is the picker's own key shape, and quick-model.ts's parsePinned
100
100
  * splits a pinned selection on the FIRST colon. `endpoint:ollama:qwen3` would parse as provider "endpoint" with
101
- * model "ollama:qwen3" a pin that silently resolves to nothing. The capability id (entryId) excludes both
101
+ * model "ollama:qwen3", a pin that silently resolves to nothing. The capability id (entryId) excludes both
102
102
  * characters, so `endpoint/<id>` stays unambiguous in either direction. */
103
- /* THE FREE TRIAL'S ENDPOINT ID IS RESERVED, the way `pi` is an `endpoint`-kind capability like any model API
103
+ /* THE FREE TRIAL'S ENDPOINT ID IS RESERVED, the way `pi` is, an `endpoint`-kind capability like any model API
104
104
  * the user configured, except that this one is provisioned by the DAEMON rather than added by a person, and it
105
105
  * points at intentic's own pool (see the sandbox's trial/ and the platform's /trial routes).
106
106
  *
107
107
  * Riding the endpoint kind is the entire reason the trial needed no new turn path, no new provider and no new
108
108
  * adapter: the translator already re-serves an OpenAI-compatible upstream to the Claude Code loop, so a trial
109
- * turn is an endpoint turn and everything downstream catalog, picker, routing works unchanged.
109
+ * turn is an endpoint turn and everything downstream, catalog, picker, routing, works unchanged.
110
110
  *
111
111
  * What the reserved id buys is the part that must NOT look the same. A trial turn passes through intentic's
112
112
  * servers, which no other provider in this product does, and a user cannot consent to something they were not
113
- * told. So every surface that names a provider asks `isTrialProvider` and says so, and the id is here beside
114
- * the vocabulary those surfaces already read rather than spelled out in each of them. */
113
+ * told. So every surface that names a provider asks `isTrialProvider` and says so, and the id is here, beside
114
+ * the vocabulary those surfaces already read, rather than spelled out in each of them. */
115
115
  export const TRIAL_ENDPOINT_ID = "free-trial";
116
116
  export const TRIAL_PROVIDER = "endpoint/free-trial";
117
117
  export const isTrialProvider = (provider: AgentProvider): boolean => provider === TRIAL_PROVIDER;
118
118
 
119
- /* THE ONLY MODEL THE TRIAL PUBLISHES a synthetic id, not one of Google's, and that is the point.
119
+ /* THE ONLY MODEL THE TRIAL PUBLISHES, a synthetic id, not one of Google's, and that is the point.
120
120
  *
121
121
  * The trial used to publish whatever the upstream listed. Two things were wrong with that and neither could be
122
122
  * fixed by filtering harder. Google lists ~54 models on a fresh key and declares `generateContent` for many that
123
- * cannot serve an agent turn deep-research, antigravity, gemma, robotics and computer-use previews all pass a
124
- * capability check and then fail the first message so the picker was full of rows whose only outcome was an
123
+ * cannot serve an agent turn, deep-research, antigravity, gemma, robotics and computer-use previews all pass a
124
+ * capability check and then fail the first message, so the picker was full of rows whose only outcome was an
125
125
  * error. And the list MOVED: the translator's routing table is written at boot and on capability edits, while
126
126
  * the picker re-reads the catalog every minute, so a model discovered in between was pickable and unroutable,
127
127
  * refused with "unknown provider for model".
128
128
  *
129
129
  * One id, never changing, ends both. There is nothing to filter because nothing is discovered, and the routing
130
130
  * table cannot drift from a list of one constant. WHICH real model answers is decided per message by the
131
- * platform, which is the only party that can see which of its keys still has quota on which model the sandbox
131
+ * platform, which is the only party that can see which of its keys still has quota on which model, the sandbox
132
132
  * cannot, and a user choosing blind between rows they know nothing about was never a choice worth offering. */
133
133
  export const TRIAL_MODEL_ID = "auto";
134
134
  // What the picker calls it, and the sentence the surfaces put underneath. One wording, so the composer's notice
@@ -143,28 +143,28 @@ export const isEndpointProvider = (provider: AgentProvider): boolean => provider
143
143
  export const endpointIdOf = (provider: AgentProvider): string | undefined =>
144
144
  isEndpointProvider(provider) ? provider.slice(ENDPOINT_PROVIDER_PREFIX.length) : undefined;
145
145
 
146
- // An ACP provider carries its own credentials installed means runnable so it has no access requirement at
146
+ // An ACP provider carries its own credentials, installed means runnable, so it has no access requirement at
147
147
  // all; `undefined` is that state, and every surface reads it as "nothing to connect". An endpoint is the same
148
148
  // answer for a different reason: its credential (if it even needs one) was configured with the endpoint itself,
149
- // so there is likewise nothing left to connect. What a turn on it COSTS is deliberately not claimed here a
149
+ // so there is likewise nothing left to connect. What a turn on it COSTS is deliberately not claimed here, a
150
150
  // self-hosted model on the user's own GPU and a metered gateway key are the same shape to us, and inventing an
151
151
  // AccessKind for them would have the picker assert a price the daemon has no way to know.
152
152
  export const accessFor = (provider: AgentProvider): ProviderAccess | undefined => PROVIDER_ACCESS[provider as NativeProvider];
153
153
 
154
- // An ACP provider's label is its capability's display name, which the web layers on top the raw id is the
154
+ // An ACP provider's label is its capability's display name, which the web layers on top, the raw id is the
155
155
  // static fallback.
156
156
  export const providerLabel = (provider: AgentProvider): string => PROVIDERS.find((p) => p.value === provider)?.label ?? provider;
157
157
 
158
- /* Whether a plan-limit reading for this provider is OBTAINABLE at all one fact, on the wire, because both
158
+ /* Whether a plan-limit reading for this provider is OBTAINABLE at all, one fact, on the wire, because both
159
159
  * halves need it and they need the same answer. The daemon reads it to decide what to even ask upstream for
160
160
  * (usage/translator-usage.ts); the browser reads it to say WHY an account shows no meter, which is the
161
- * difference between "this plan publishes nothing" and "we haven't measured yet" two states that look
161
+ * difference between "this plan publishes nothing" and "we haven't measured yet", two states that look
162
162
  * identical as a blank row and mean opposite things.
163
163
  *
164
164
  * Four can be read, by two mechanisms that stop at the daemon's readers: Claude's rides its own turn (the
165
165
  * OAuth usage endpoint, agent.ts), ChatGPT's, Google's and Kimi's are pulled through the translator's
166
166
  * credential-scoped api-call. Kimi's endpoint is the platform's own `/coding/v1/usages`, which the Kimi Code
167
- * subscription's OAuth token reads directly the bundled translator does not route it, but it does not have
167
+ * subscription's OAuth token reads directly, the bundled translator does not route it, but it does not have
168
168
  * to: the api-call substitutes that token server-side like it does for the other two.
169
169
  *
170
170
  * Grok is the one absence, because xAI's usable billing data needs a subject id CLIProxyAPI keeps out of its
@@ -176,28 +176,28 @@ export const reportsPlanLimits = (provider: AgentProvider): boolean => PLAN_LIMI
176
176
  // The harness (agentic loop) a turn runs on, orthogonal to the provider. `native` = the provider's own runtime;
177
177
  // `claude-code` = the Claude Code loop for any provider (codex/grok then route through the translator).
178
178
  // Surfaced for codex/grok alone. Claude is always its own Claude Code loop; kimi has no native runtime to switch
179
- // to (it only exists under this harness); and GEMINI IS THE MIRROR OF KIMI it only exists under its native
179
+ // to (it only exists under this harness); and GEMINI IS THE MIRROR OF KIMI, it only exists under its native
180
180
  // one, because Google refuses Claude Code's traffic outright (capabilitiesOf says why). See AgentHarness in
181
181
  // schemas.ts.
182
182
  //
183
183
  // Gemini's `native` is OpenCode rather than a Google CLI: the image ships no Gemini binary, and OpenCode is
184
- // already here driving Grok. It spends the same translator accounts a routed turn would have what Google
184
+ // already here driving Grok. It spends the same translator accounts a routed turn would have, what Google
185
185
  // refuses is the loop, never the credential.
186
186
  export const HARNESSES: readonly { label: string; value: AgentHarness }[] = [
187
187
  { label: "Native", value: "native" },
188
188
  { label: "Claude Code", value: "claude-code" },
189
189
  ];
190
190
 
191
- /* WHAT A PROVIDER/HARNESS PAIR CAN ACTUALLY DO one declaration, read by both sides of the wire.
191
+ /* WHAT A PROVIDER/HARNESS PAIR CAN ACTUALLY DO, one declaration, read by both sides of the wire.
192
192
  *
193
193
  * Five runtimes serve turns behind one seam (AgentRequest in, AgentEvent frames out): the Claude Code Agent SDK
194
194
  * loop, Codex app-server, OpenCode, any ACP agent, and Pi's RPC surface. They do NOT do the same things, and for a long time
195
- * the only thing that said so was a comment inside each adapter "Ignores the Claude-only request fields"
195
+ * the only thing that said so was a comment inside each adapter, "Ignores the Claude-only request fields",
196
196
  * which no surface above it could read. So the composer offered "Ask before each file edit" on a runtime whose
197
197
  * every tool call is pre-approved, and offered a reasoning-effort scale to a runtime that drops the field.
198
198
  *
199
199
  * A capability is listed here only if something READS it: the daemon gates a seam on it, the composer hides or
200
- * clamps a control by it, or `limitationsOf` tells the user about it. That is the whole point an ability the
200
+ * clamps a control by it, or `limitationsOf` tells the user about it. That is the whole point, an ability the
201
201
  * matrix claims and nothing consults is how the drift started.
202
202
  *
203
203
  * Adding a provider is a row here, not a hunt for literals; agent-catalog.test.ts walks PROVIDERS × HARNESSES
@@ -205,11 +205,11 @@ export const HARNESSES: readonly { label: string; value: AgentHarness }[] = [
205
205
 
206
206
  // An execution backend: one way a turn runs work of its own, named for the AgentCapabilities.execution axis
207
207
  // and for the persona switch that grants it. Adding a language is a member here and a backend in the daemon's
208
- // execution/ module never a new one-off tool wired where nothing else can see it.
208
+ // execution/ module, never a new one-off tool wired where nothing else can see it.
209
209
  export type ExecutionBackend = "shell" | "js";
210
210
 
211
211
  export interface AgentCapabilities {
212
- // Which agentic loop actually serves the turn the question "is the harness `claude-code`" only looks like.
212
+ // Which agentic loop actually serves the turn, the question "is the harness `claude-code`" only looks like.
213
213
  // Claude is always its own Claude Code loop and Kimi has no native runtime, so both run it whatever harness
214
214
  // the client sent; codex/grok/gemini each have a native runtime to switch away from. Names the session store
215
215
  // a finished conversation's transcript is backfilled from, too.
@@ -222,7 +222,7 @@ export interface AgentCapabilities {
222
222
  readonly steering: boolean;
223
223
  // How much of the permission-mode axis the runtime honours. "modes" = every PermissionMode, with per-tool
224
224
  // permission cards and `mode` frames when the agent moves itself; "plan" = propose-then-approve or run, and
225
- // nothing in between the container is the isolation boundary and every tool call is pre-approved.
225
+ // nothing in between, the container is the isolation boundary and every tool call is pre-approved.
226
226
  readonly permissions: "modes" | "plan";
227
227
  // Can stop mid-turn and ask the user a multiple-choice question (`question` frames).
228
228
  readonly questions: boolean;
@@ -232,14 +232,14 @@ export interface AgentCapabilities {
232
232
  // them at all. Keeping the partial answers distinct matters: a runtime that can drive a connected account
233
233
  // must not be described as tool-less, and one that cannot host daemon-side SDK servers must not claim full.
234
234
  readonly mcp: "full" | "browser" | "http" | "none";
235
- /* WHICH EXECUTION BACKENDS THE RUNTIME HOSTS the ways a turn RUNS things, as opposed to the tools it is
235
+ /* WHICH EXECUTION BACKENDS THE RUNTIME HOSTS, the ways a turn RUNS things, as opposed to the tools it is
236
236
  * handed. "shell" is the runtime's own command tool (Bash on the Claude Code loop, each foreign loop's
237
237
  * equivalent); "js" is the sandbox's JavaScript backend (execution/ in the daemon): the model writes a
238
238
  * script instead of a command line, and the daemon runs it in a permission-fenced Node subprocess.
239
239
  *
240
240
  * A first-class axis rather than a corollary of `mcp`, because the two answer different questions: `mcp`
241
241
  * says which TOOLS reach the model's context, this says which ways of EXECUTING the daemon can stand
242
- * behind for this runtime with the same guard, secret and persona seams the shell gets. A runtime that
242
+ * behind for this runtime, with the same guard, secret and persona seams the shell gets. A runtime that
243
243
  * cannot host a backend simply never shows it, and the persona switch for it (PersonaPowersSchema.code)
244
244
  * then has nothing to grant there. */
245
245
  readonly execution: readonly ExecutionBackend[];
@@ -247,14 +247,14 @@ export interface AgentCapabilities {
247
247
  readonly effort: boolean;
248
248
  /* The runtime can serve a turn at fast speed when asked (AgentTurn.fast). A statement about the LOOP, not
249
249
  * about the route: the Claude Code loop knows how to ask for it, which is why every provider this record
250
- * hands the loop to reads true here including the ones served through the translator, whose turns the
250
+ * hands the loop to reads true here, including the ones served through the translator, whose turns the
251
251
  * harness will then refuse fast mode for because a translator endpoint is not first-party. That second
252
252
  * question is answered where the endpoint is decided (planHarnessTurn), because it is a fact about the
253
253
  * CREDENTIAL rather than about the runtime, and this record is a pure function of (provider, harness). */
254
254
  readonly fastMode: boolean;
255
255
  // How an isolated conversation's worktree is enforced. "namespace" = the worktree IS /work inside the turn's
256
256
  // mount namespace (with the tool-input rewrite as the fallback when the container can't build one); "cwd" =
257
- // the turn is merely cwd'd into the worktree, so an absolute /work path still reaches the shared checkout
257
+ // the turn is merely cwd'd into the worktree, so an absolute /work path still reaches the shared checkout,
258
258
  // which is why those turns are told where their tree is (turn-preamble.ts).
259
259
  readonly isolation: "namespace" | "cwd";
260
260
  // Publishes its slash commands (`commands` frames) for the composer's `/` popover.
@@ -264,31 +264,31 @@ export interface AgentCapabilities {
264
264
  // Fails with the coded frames the daemon's auto-resume keys off (rate_limit, provider-outage), so a turn the
265
265
  // provider killed is re-run once the breaker says the provider is back (turn-resume.ts).
266
266
  readonly recovery: boolean;
267
- /* HOW MUCH OF ITS STANDING INSTRUCTIONS THIS RUNTIME WILL TAKE FROM US the axis behind the sandbox's
267
+ /* HOW MUCH OF ITS STANDING INSTRUCTIONS THIS RUNTIME WILL TAKE FROM US, the axis behind the sandbox's
268
268
  * system-prompt setting (SandboxSettings.systemPromptMode) and the persona's own override.
269
269
  *
270
270
  * It exists because that setting was silently a Claude Code setting. The composer offers Codex, Grok and
271
271
  * Gemini on their own runtimes, and a turn on any of them ignored the prompt the owner had written without
272
- * saying so anywhere the one failure mode a settings page cannot recover from, because nothing on screen
272
+ * saying so anywhere, the one failure mode a settings page cannot recover from, because nothing on screen
273
273
  * is wrong. Naming it here means every surface reads the same answer and the daemon composes to it
274
274
  * (agent/system-prompt.ts), rather than each learning the exception separately.
275
275
  *
276
- * "replace" the whole base prompt can be swapped for the owner's text, and extra guidance appended on
276
+ * "replace", the whole base prompt can be swapped for the owner's text, and extra guidance appended on
277
277
  * top of whichever base is in force. The Claude Code loop (SDK `systemPrompt`) and native
278
278
  * Codex (`model_instructions_file` replaces its base; `developer_instructions` adds a
279
- * developer message both verified on the wire against codex-cli 0.147).
280
- * "append" extra system text only; the runtime's own base prompt stands. OpenCode takes one per
279
+ * developer message, both verified on the wire against codex-cli 0.147).
280
+ * "append" , extra system text only; the runtime's own base prompt stands. OpenCode takes one per
281
281
  * message (`system` on the prompt body), and there is no seam for replacing its base.
282
- * "none" no system seam at all. What must still reach the model (the persona note) rides the user
282
+ * "none" , no system seam at all. What must still reach the model (the persona note) rides the user
283
283
  * message instead, which is the door the delegation note already uses.
284
284
  *
285
- * The BASE CHOICE Intentic's prompt or Claude Code's is a "replace" runtime's question and, of those,
285
+ * The BASE CHOICE. Intentic's prompt or Claude Code's, is a "replace" runtime's question and, of those,
286
286
  * only the Claude Code loop's: Codex's own base describes Codex's own tools, so swapping it for a prompt
287
287
  * written about another harness is the owner's deliberate act (their custom text), never ours. */
288
288
  readonly instructions: "replace" | "append" | "none";
289
289
  }
290
290
 
291
- // The Claude Code Agent SDK loop the ceiling every other runtime is measured against, and the only one that
291
+ // The Claude Code Agent SDK loop, the ceiling every other runtime is measured against, and the only one that
292
292
  // owns the whole request: permission callbacks, the ask tool, plugins, hooks, and the spawn seam a mount
293
293
  // namespace needs.
294
294
  const CLAUDE_CODE: AgentCapabilities = {
@@ -297,7 +297,7 @@ const CLAUDE_CODE: AgentCapabilities = {
297
297
  permissions: "modes",
298
298
  questions: true,
299
299
  mcp: "full",
300
- // The one loop with a seam the daemon can put its own backend through so it hosts the JS backend beside
300
+ // The one loop with a seam the daemon can put its own backend through, so it hosts the JS backend beside
301
301
  // its Bash. Every other runtime below hosts only its own shell.
302
302
  execution: ["shell", "js"],
303
303
  effort: true,
@@ -310,13 +310,13 @@ const CLAUDE_CODE: AgentCapabilities = {
310
310
  };
311
311
 
312
312
  /* Codex app-server: item-level events, process-backed MCP servers, and the four interactive seams its protocol
313
- * actually publishes `turn/steer` for mid-turn injection, the experimental `item/tool/requestUserInput` server
313
+ * actually publishes, `turn/steer` for mid-turn injection, the experimental `item/tool/requestUserInput` server
314
314
  * request behind a question card, `skills/list` for the `/` popover (a picked command rides back as a structured
315
315
  * skill input), and the same mount namespace the Claude Code loop gets, because app-server is a child process
316
316
  * the adapter spawns and nsenter can put it in the turn's namespace like any other.
317
317
  *
318
318
  * Browser servers ride the per-thread config; daemon-side SDK servers, plugins and server-initiated APPROVALS
319
- * stay unwired the container is the isolation boundary, so approvals are declined by design rather than
319
+ * stay unwired, the container is the isolation boundary, so approvals are declined by design rather than
320
320
  * missing (codex-app-server.ts refuses every server request but the question one). */
321
321
  const CODEX: AgentCapabilities = {
322
322
  runtime: "codex",
@@ -334,12 +334,12 @@ const CODEX: AgentCapabilities = {
334
334
  /* Both halves, through the per-thread `config` block the adapter already sends: `model_instructions_file`
335
335
  * takes the place of Codex's own base prompt, `developer_instructions` arrives as an extra developer
336
336
  * message ahead of its skills and team blocks. Verified against codex-cli 0.147 by reading what actually
337
- * reached the wire the keys are undocumented, and a strings dump proves only that they parse. */
337
+ * reached the wire, the keys are undocumented, and a strings dump proves only that they parse. */
338
338
  instructions: "replace",
339
339
  };
340
340
 
341
341
  // OpenCode (the Grok runtime): its own agentic loop, its own tools, allow-all permissions. It takes a model id,
342
- // a prompt and one system message of ours no effort scale, no tools of ours, no command list.
342
+ // a prompt and one system message of ours, no effort scale, no tools of ours, no command list.
343
343
  const OPENCODE: AgentCapabilities = {
344
344
  runtime: "opencode",
345
345
  steering: false,
@@ -353,19 +353,19 @@ const OPENCODE: AgentCapabilities = {
353
353
  commands: false,
354
354
  terminals: false,
355
355
  recovery: false,
356
- // `system` on the prompt body, per message. It ADDS to OpenCode's own prompt there is no seam for
357
- // replacing that so a custom prompt lands here as extra instructions, and the settings page says so
356
+ // `system` on the prompt body, per message. It ADDS to OpenCode's own prompt, there is no seam for
357
+ // replacing that, so a custom prompt lands here as extra instructions, and the settings page says so
358
358
  // rather than letting "replaces everything" quietly mean something else on two providers.
359
359
  instructions: "append",
360
360
  };
361
361
 
362
- /* The same OpenCode loop, serving Gemini instead of xAI identical abilities, which is the point of giving it
362
+ /* The same OpenCode loop, serving Gemini instead of xAI, identical abilities, which is the point of giving it
363
363
  * its own row rather than its own record shape.
364
364
  *
365
365
  * It exists because the alternative was Gemini's ONLY route being the Claude Code loop, and that loop announces
366
366
  * itself to whatever it is pointed at: the CLI prepends its own "You are a Claude agent, built on Anthropic's
367
367
  * Claude Agent SDK." to every request, baked into the binary with no option to suppress it. Google's Antigravity
368
- * channel matches that exact sentence and refuses the request reported as a quota error, which sent the
368
+ * channel matches that exact sentence and refuses the request, reported as a quota error, which sent the
369
369
  * translator walking all 31 connected accounts looking for one with room, ~60s per attempt, none of which could
370
370
  * ever have answered. Under this runtime the request carries OpenCode's own prompt, so the turn is simply not
371
371
  * Claude Code traffic and the block has nothing to match.
@@ -379,7 +379,7 @@ const OPENCODE_GEMINI: AgentCapabilities = {
379
379
 
380
380
  // Any agent speaking the Agent Client Protocol: a documented floor rather than the native ceiling. It publishes
381
381
  // commands, runs its terminals in the conversation's tmux session, and takes our http MCP tools when it says it
382
- // can but it owns its own model, effort and permission posture.
382
+ // can, but it owns its own model, effort and permission posture.
383
383
  const ACP: AgentCapabilities = {
384
384
  runtime: "acp",
385
385
  steering: false,
@@ -399,7 +399,7 @@ const ACP: AgentCapabilities = {
399
399
  };
400
400
 
401
401
  /* THE PI CAPABILITY ID IS RESERVED, the same way the five native ids are: an `agent`-kind capability installed
402
- * under it is served over Pi's own RPC protocol rather than ACP Pi closed ACP support deliberately (its RPC
402
+ * under it is served over Pi's own RPC protocol rather than ACP. Pi closed ACP support deliberately (its RPC
403
403
  * mode is the embedding surface), and the two want different records. A bare id rather than a namespace like
404
404
  * `endpoint/`, because there is exactly one Pi runtime to name; capabilitiesOf still answers from the id alone,
405
405
  * which is what keeps it a pure function of (provider, harness). */
@@ -408,7 +408,7 @@ export const PI_PROVIDER = "pi";
408
408
  // Pi driven over its RPC mode (`pi --mode rpc`, strict-LF JSONL over stdio): above the ACP floor and below the
409
409
  // Claude Code ceiling. Its `steer` command is real mid-turn injection; `set_thinking_level` takes the effort
410
410
  // tiers; `get_commands` publishes its extension/skill commands. It has no MCP seam (Pi's own extensions are its
411
- // tool surface), no approval channel (plan is the shared two-phase emulation), and runs bash in-process no
411
+ // tool surface), no approval channel (plan is the shared two-phase emulation), and runs bash in-process, no
412
412
  // tmux session for the terminal panel to attach to.
413
413
  const PI: AgentCapabilities = {
414
414
  runtime: "pi",
@@ -429,9 +429,9 @@ const PI: AgentCapabilities = {
429
429
  };
430
430
 
431
431
  // The pair → its record. An `endpoint/<id>` provider is a model API the user configured, driven BY the Claude
432
- // Code loop on either harness so it gets that loop's full ceiling, which is the entire point of routing a
432
+ // Code loop on either harness, so it gets that loop's full ceiling, which is the entire point of routing a
433
433
  // model through it rather than adopting a second runtime. The reserved `pi` id is the Pi coding agent on its
434
- // own RPC runtime (harness doesn't apply Pi is its own loop, like ACP). Any other id that names no native
434
+ // own RPC runtime (harness doesn't apply. Pi is its own loop, like ACP). Any other id that names no native
435
435
  // provider is an installed `agent`-kind capability, served over ACP.
436
436
  export const capabilitiesOf = (provider: AgentProvider, harness: AgentHarness): AgentCapabilities => {
437
437
  if (provider === "codex") {
@@ -442,12 +442,12 @@ export const capabilitiesOf = (provider: AgentProvider, harness: AgentHarness):
442
442
  }
443
443
  /* GEMINI IGNORES THE HARNESS, and it is the only routed provider that does. The Claude Code loop announces
444
444
  * itself in every request it sends and Google refuses on that announcement (see OPENCODE_GEMINI), so
445
- * "Gemini under Claude Code" was never a slower or poorer option it was one that could not complete a
445
+ * "Gemini under Claude Code" was never a slower or poorer option, it was one that could not complete a
446
446
  * single turn, on any of the connected accounts, ever.
447
447
  *
448
448
  * Answering OPENCODE_GEMINI whatever the caller asked for is what makes that structural rather than a rule
449
- * each surface has to remember. Everything downstream reads the runtime off this record the adapter that
450
- * serves a turn, the transcript store, the quick helper's choice of loop so there is exactly one place
449
+ * each surface has to remember. Everything downstream reads the runtime off this record, the adapter that
450
+ * serves a turn, the transcript store, the quick helper's choice of loop, so there is exactly one place
451
451
  * where Gemini's loop is decided, and no way left to route Claude Code traffic at Google by asking for it. */
452
452
  if (provider === "gemini") {
453
453
  return OPENCODE_GEMINI;
@@ -466,18 +466,18 @@ export const capabilitiesOf = (provider: AgentProvider, harness: AgentHarness):
466
466
  export const modesFor = (capabilities: AgentCapabilities): readonly PermissionMode[] =>
467
467
  capabilities.permissions === "modes" ? ["default", "acceptEdits", "plan", "bypassPermissions"] : ["plan", "bypassPermissions"];
468
468
 
469
- // The mode a selection falls back to when the runtime can't hold it the same shape as clampEffort, and for the
469
+ // The mode a selection falls back to when the runtime can't hold it, the same shape as clampEffort, and for the
470
470
  // same reason: a provider switch must not leave the composer showing a posture nothing applies.
471
471
  export const clampMode = (mode: PermissionMode, capabilities: AgentCapabilities): PermissionMode =>
472
472
  modesFor(capabilities).includes(mode) ? mode : "bypassPermissions";
473
473
 
474
- /* What this pair does NOT do, phrased for the person about to send a message to it the honest half of the
474
+ /* What this pair does NOT do, phrased for the person about to send a message to it, the honest half of the
475
475
  * picker, and the reason the record carries axes the daemon itself never branches on. Empty ⇒ the full ceiling.
476
476
  *
477
477
  * `fastMode` is deliberately NOT disclosed here, and it is the one axis that can't be: every other axis is fully
478
478
  * determined by the record, while fast mode also depends on the route and the model. The record says true for
479
- * every provider the Claude Code loop serves including the ones routed through the translator, which can never
480
- * go fast so a sentence derived from it would stay silent for exactly the turns that most need to hear it.
479
+ * every provider the Claude Code loop serves, including the ones routed through the translator, which can never
480
+ * go fast, so a sentence derived from it would stay silent for exactly the turns that most need to hear it.
481
481
  * fastAllowed answers the real question, and the `fast_mode` frame reports what the turn actually got. */
482
482
  export const limitationsOf = (capabilities: AgentCapabilities): string[] => [
483
483
  ...(capabilities.permissions === "plan" ? ["no per-tool approvals"] : []),
@@ -503,10 +503,10 @@ export const limitationsOf = (capabilities: AgentCapabilities): string[] => [
503
503
  ...(capabilities.instructions === "none" ? ["your system prompt isn't applied"] : []),
504
504
  ];
505
505
 
506
- // Claude's compile-time model floor, shared by the daemon's catalog (claude-models.ts its last rung, reached
506
+ // Claude's compile-time model floor, shared by the daemon's catalog (claude-models.ts, its last rung, reached
507
507
  // only before either live source has ever answered) and by the web's pre-load list, so the two can't name
508
508
  // different models. VERSIONED ids only, never the tier aliases (`opus`, `sonnet`) that used to sit here: an
509
- // alias names no version, so a turn running on one leaves the user unable to say what answered them and it
509
+ // alias names no version, so a turn running on one leaves the user unable to say what answered them, and it
510
510
  // lags a release besides, resolving to the previous version for as long as the CLI keeps pointing it there.
511
511
  // Going stale costs nothing: every rung above replaces the whole list, and a selection the live catalog no
512
512
  // longer offers is repointed to its default (loadProviderModels web-side, routedModel daemon-side).
@@ -517,8 +517,8 @@ export const CLAUDE_SEED_MODELS: readonly Model[] = [
517
517
  ];
518
518
 
519
519
  // The STATIC floor of the model catalog, harness-independent: every provider's real list is the daemon's live
520
- // catalog (/providers/{provider}/models discovery with a persisted/seed floor, never empty),
521
- // which consumers layer on top. Codex/grok are empty here (nothing sensible to offer before the live load and
520
+ // catalog (/providers/{provider}/models, discovery with a persisted/seed floor, never empty),
521
+ // which consumers layer on top. Codex/grok are empty here (nothing sensible to offer before the live load, and
522
522
  // under the Claude Code harness they route through the translator, which serves the SAME subscription model ids
523
523
  // as the native catalog, so the harness no longer changes the list).
524
524
  export const modelsFor = (provider: AgentProvider): CatalogOption[] => {
@@ -532,9 +532,9 @@ export const modelsFor = (provider: AgentProvider): CatalogOption[] => {
532
532
  // Whether a reasoning-effort tier is actually sendable for this provider with this thinking setting. 'max' is
533
533
  // the only constrained tier and it fails two ways: no non-Claude scale HAS it, and Claude's API rejects it
534
534
  // outright when extended thinking is disabled ("effort 'max' is not supported when thinking is disabled on this
535
- // model" a 400 that kills the turn before the model sees it, surfacing only as the SDK's `unknown` error
536
- // category). It is the one rule a MODEL's published tier list can't express the daemon reports what a model
537
- // accepts without knowing this turn's thinking setting so the consumer that assembles the offered scale
535
+ // model", a 400 that kills the turn before the model sees it, surfacing only as the SDK's `unknown` error
536
+ // category). It is the one rule a MODEL's published tier list can't express, the daemon reports what a model
537
+ // accepts without knowing this turn's thinking setting, so the consumer that assembles the offered scale
538
538
  // (effortsFor, web-side) filters through here, and the clamp over that scale makes the pair unreachable.
539
539
  export const effortAllowed = (effort: string, provider: AgentProvider, thinking: boolean): boolean =>
540
540
  effort !== "max" || (provider === "claude" && thinking);
@@ -543,7 +543,7 @@ export const effortAllowed = (effort: string, provider: AgentProvider, thinking:
543
543
  *
544
544
  * effortAllowed makes the pair unreachable in the picker, and the picker is not the only way a turn is
545
545
  * assembled: a route, an extension, a restored tab or a settings-pinned model can all name an effort that no
546
- * live scale filtered. One did a session ran `max` with thinking off, and every server-side tool call in it
546
+ * live scale filtered. One did, a session ran `max` with thinking off, and every server-side tool call in it
547
547
  * came back `400 output_config.effort 'max' is not supported when thinking is disabled`, which reads to the
548
548
  * model as "web search is broken" and cost it the answer it was sent to find.
549
549
  *
@@ -554,18 +554,18 @@ export const effortAllowed = (effort: string, provider: AgentProvider, thinking:
554
554
  export const sendableEffort = (effort: string | undefined, thinking: boolean | undefined): string | undefined =>
555
555
  effort === "max" && thinking !== true ? "high" : effort;
556
556
 
557
- /* WHETHER FAST SPEED CAN BE OFFERED for a provider/harness/model triple the picker-side filter, the same
557
+ /* WHETHER FAST SPEED CAN BE OFFERED for a provider/harness/model triple, the picker-side filter, the same
558
558
  * shape and the same reason as effortAllowed: the composer must not show a control that does nothing.
559
559
  *
560
- * Three conditions, each answering a different question, and all three are load-bearing:
560
+ * Three conditions, each answering a different question, and all three are required:
561
561
  *
562
562
  * - the RUNTIME has to know how to ask (capabilities.fastMode). Only the Claude Code loop does.
563
563
  * - the ROUTE has to be first-party. Every non-Claude provider the Claude Code loop serves is served through
564
564
  * the sandbox's translator, and the harness refuses fast mode on a non-Anthropic endpoint ("not_first_party")
565
- * so a `grok` turn on the claude-code harness reads true on the capability and still cannot go fast.
565
+ * , so a `grok` turn on the claude-code harness reads true on the capability and still cannot go fast.
566
566
  * - the MODEL has to publish it, which is the `fast` badge Anthropic's own catalog reports per model
567
567
  * (claude-models.ts maps supportsFastMode onto it). Curating a list of ids here instead is what this repo
568
- * deliberately does not do a model that gains or loses fast mode moves the badge, and this follows.
568
+ * deliberately does not do, a model that gains or loses fast mode moves the badge, and this follows.
569
569
  *
570
570
  * `badges` absent ⇒ false. That is the honest reading: a catalog row that published no capabilities said
571
571
  * nothing about fast mode, and the seed floor a picker shows before its first live load is exactly that row. */
@@ -1,6 +1,6 @@
1
1
  import { parsePinned, type QuickModelChoice, quickModelKey, type QuickModelSource } from "./quick-model.js";
2
2
 
3
- /* WHAT A SURFACE-STARTED AGENT RUN OPENS ON the resolver for `agentRunModels`, sibling to resolveQuickModels
3
+ /* WHAT A SURFACE-STARTED AGENT RUN OPENS ON, the resolver for `agentRunModels`, sibling to resolveQuickModels
4
4
  * and deliberately not the same function.
5
5
  *
6
6
  * BOTH ARE ORDERED LISTS, FOR THE SAME REASON. One connected account whose allowance went on the chat this
@@ -13,33 +13,33 @@ import { parsePinned, type QuickModelChoice, quickModelKey, type QuickModelSourc
13
13
  * is connected" is a good answer and quickModel's empty list resolves to a derived Auto ladder. An agent run is
14
14
  * a full session with a worktree, billed whole: nothing here can judge whether a job is worth the frontier tier,
15
15
  * so an empty list resolves to NOTHING and the caller falls back to the model the user picked for their own
16
- * chat a choice they made, rather than one this file guessed for them. For the same reason there is no Auto
16
+ * chat, a choice they made, rather than one this file guessed for them. For the same reason there is no Auto
17
17
  * ladder underneath a list that has been emptied by disconnection: it would spend an account the user never
18
18
  * pointed at, on the most expensive kind of run this app starts.
19
19
  *
20
20
  * WHAT "STEPPED OVER" MEANS HERE IS NARROWER than the quick chain's, and worth being exact about. The quick
21
21
  * chain re-asks the next rung when a call comes back refused, because a one-shot that failed has cost nothing
22
- * and can simply be run again. An agent session cannot be replayed that way by the time a provider refuses
23
- * mid-turn the agent may have already edited files so this list is read ONCE, at the moment the turn is
22
+ * and can simply be run again. An agent session cannot be replayed that way, by the time a provider refuses
23
+ * mid-turn the agent may have already edited files, so this list is read ONCE, at the moment the turn is
24
24
  * composed, and steps over exactly one thing: an account that is not connected. A model that accepts the turn
25
25
  * and fails later is a failed run the user reads on the card, like any other. */
26
26
 
27
27
  /* The pins that could actually be started right now, in the user's own order.
28
28
  *
29
29
  * `sources` is the same readiness view resolveQuickModels takes, so both settings rows agree about which
30
- * accounts this sandbox can send to a pin greyed as "Not connected" in one row and silently spent by the
30
+ * accounts this sandbox can send to, a pin greyed as "Not connected" in one row and silently spent by the
31
31
  * other would be the worst of both.
32
32
  *
33
33
  * A pin whose provider is gone is DROPPED rather than held: it would otherwise sit at the head of the chain
34
- * failing every run, which is exactly what the list exists to prevent. It stays on SCREEN, greyed the settings
35
- * row renders the stored list, not this one because a setting that vanished from view would look like the app
34
+ * failing every run, which is exactly what the list exists to prevent. It stays on SCREEN, greyed, the settings
35
+ * row renders the stored list, not this one, because a setting that vanished from view would look like the app
36
36
  * had eaten it.
37
37
  *
38
38
  * Empty out means nobody has pinned anything this sandbox can reach, and the caller's floor takes over. */
39
39
  export const resolveAgentRunModels = (sources: readonly QuickModelSource[], pinned: readonly string[]): readonly QuickModelChoice[] => {
40
40
  const ready = new Set(sources.filter((source) => source.ready).map((source) => source.provider));
41
41
  const requested = pinned.flatMap((key) => {
42
- // Verbatim, unvalidated against the catalog the same call resolveQuickModels makes and for the same
42
+ // Verbatim, unvalidated against the catalog, the same call resolveQuickModels makes and for the same
43
43
  // reason: the picker offers a custom-id escape hatch for a model the static catalog has not caught up
44
44
  // with, and second-guessing the id here would run a different model than the settings row names.
45
45
  const choice = parsePinned(key);
@@ -1,10 +1,10 @@
1
- /* HOW A CLI CAPABILITY'S ENV VARS ARE NAMED the one rule both ends of that wire have to apply.
1
+ /* HOW A CLI CAPABILITY'S ENV VARS ARE NAMED, the one rule both ends of that wire have to apply.
2
2
  *
3
3
  * The daemon writes the agent's shell env by suffixing every var a cli connector declares with the instance id
4
4
  * (cli-env.ts), so two connections of the same provider coexist on one flat environment. A connector's own
5
- * TOOL then has to read them back, and an extension may not import daemon internals so without this the
5
+ * TOOL then has to read them back, and an extension may not import daemon internals, so without this the
6
6
  * rule would be spelled twice, and a change to it would silently split the writer from the reader. */
7
7
 
8
8
  // `analytics` → `POSTGRES_URL_ANALYTICS`; the default-named `github` → `GITHUB_TOKEN_GITHUB`.
9
- // ponytail: ids differing only by case or `-`/`_` (my-db vs my_db) map to the same suffix last wins.
9
+ // ponytail: ids differing only by case or `-`/`_` (my-db vs my_db) map to the same suffix, last wins.
10
10
  export const envSuffix = (id: string): string => id.toUpperCase().replaceAll("-", "_");
@@ -1,4 +1,4 @@
1
- /* THE ONE VALUE THAT MEANS "WHATEVER IS ALREADY STORED" the rule both ends of the capability wire apply.
1
+ /* THE ONE VALUE THAT MEANS "WHATEVER IS ALREADY STORED", the rule both ends of the capability wire apply.
2
2
  *
3
3
  * A capability's credentials never reach a browser: the list route echoes the shape of a connection and drops
4
4
  * every secret in it, and the manifest on disk holds this marker where each value used to be (the vault keeps
@@ -6,15 +6,15 @@
6
6
  * change a tunnel's routed networks sends this back in place of the pre-shared key it was never shown, and both
7
7
  * the write path and the storage layer read it as "leave that one alone".
8
8
  *
9
- * Deliberately not an empty string and not a dropped key. The entry still has to satisfy CapabilitySchema a
10
- * connector's required token, an ssh key and a reader that somehow bypasses rehydration must fail LOUDLY: a
9
+ * Deliberately not an empty string and not a dropped key. The entry still has to satisfy CapabilitySchema, a
10
+ * connector's required token, an ssh key, and a reader that somehow bypasses rehydration must fail LOUDLY: a
11
11
  * service refusing this string is a clear auth error, where an empty value reads as "not configured yet" and a
12
12
  * missing key as a shape change.
13
13
  *
14
14
  * It lives in the contract rather than in the daemon's store because it is now spoken on the wire. The browser
15
15
  * has to be able to say it, the add route has to resolve it before a handler ever sees it, and the store has to
16
- * refuse to write it over a real value three places, one spelling. */
16
+ * refuse to write it over a real value, three places, one spelling. */
17
17
  export const VAULTED = "__intentic_vaulted__";
18
18
 
19
- /** Whether a config value is the marker rather than a credential the check, so the string is typed once. */
19
+ /** Whether a config value is the marker rather than a credential, the check, so the string is typed once. */
20
20
  export const isVaulted = (value: unknown): boolean => value === VAULTED;