@intentic/sandbox-contract 1.147.0 → 1.149.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 (54) hide show
  1. package/dist/agent-catalog.d.ts.map +1 -1
  2. package/dist/agent-catalog.js +1 -0
  3. package/dist/agent-catalog.js.map +1 -1
  4. package/dist/contracts/agent.contract.d.ts +53 -8
  5. package/dist/contracts/agent.contract.d.ts.map +1 -1
  6. package/dist/contracts/agent.contract.js +2 -3
  7. package/dist/contracts/agent.contract.js.map +1 -1
  8. package/dist/contracts/agents.contract.d.ts +4 -0
  9. package/dist/contracts/agents.contract.d.ts.map +1 -1
  10. package/dist/contracts/claude.contract.d.ts +27 -0
  11. package/dist/contracts/claude.contract.d.ts.map +1 -1
  12. package/dist/contracts/codex.contract.d.ts +5 -0
  13. package/dist/contracts/codex.contract.d.ts.map +1 -1
  14. package/dist/contracts/gemini.contract.d.ts +16 -0
  15. package/dist/contracts/gemini.contract.d.ts.map +1 -0
  16. package/dist/contracts/gemini.contract.js +6 -0
  17. package/dist/contracts/gemini.contract.js.map +1 -0
  18. package/dist/contracts/git.contract.d.ts +1 -0
  19. package/dist/contracts/git.contract.d.ts.map +1 -1
  20. package/dist/contracts/grok.contract.d.ts +16 -0
  21. package/dist/contracts/grok.contract.d.ts.map +1 -1
  22. package/dist/contracts/kimi.contract.d.ts +27 -0
  23. package/dist/contracts/kimi.contract.d.ts.map +1 -1
  24. package/dist/contracts/system.contract.d.ts +2 -0
  25. package/dist/contracts/system.contract.d.ts.map +1 -1
  26. package/dist/contracts/translator.contract.d.ts +15 -0
  27. package/dist/contracts/translator.contract.d.ts.map +1 -1
  28. package/dist/contracts/translator.contract.js +3 -2
  29. package/dist/contracts/translator.contract.js.map +1 -1
  30. package/dist/events.d.ts +50 -13
  31. package/dist/events.d.ts.map +1 -1
  32. package/dist/events.js +13 -8
  33. package/dist/events.js.map +1 -1
  34. package/dist/index.d.ts +166 -9
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +3 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/schemas.d.ts +118 -7
  39. package/dist/schemas.d.ts.map +1 -1
  40. package/dist/schemas.js +55 -12
  41. package/dist/schemas.js.map +1 -1
  42. package/package.json +2 -2
  43. package/src/agent-catalog.ts +4 -2
  44. package/src/contracts/agent.contract.ts +4 -5
  45. package/src/contracts/gemini.contract.ts +11 -0
  46. package/src/contracts/translator.contract.ts +12 -6
  47. package/src/events.ts +29 -14
  48. package/src/index.ts +3 -1
  49. package/src/schemas.ts +121 -19
  50. package/dist/model-metadata.d.ts +0 -8
  51. package/dist/model-metadata.d.ts.map +0 -1
  52. package/dist/model-metadata.js +0 -24
  53. package/dist/model-metadata.js.map +0 -1
  54. package/src/model-metadata.ts +0 -44
package/src/schemas.ts CHANGED
@@ -23,11 +23,11 @@ export const SessionTranscriptMessageSchema = z.object({ role: z.enum(["user", "
23
23
  export type SessionTranscriptMessage = z.infer<typeof SessionTranscriptMessageSchema>;
24
24
 
25
25
  // The agent runtimes the daemon can serve — the vocabulary every surface that picks an agent shares (chat
26
- // turns, automations). The three NATIVE providers have dedicated adapters (and their ids are reserved); any
26
+ // turns, automations). The NATIVE providers have dedicated adapters (and their ids are reserved); any
27
27
  // other value is the id of an installed `agent`-kind capability served over ACP (Agent Client Protocol).
28
28
  // Kept as a bare string on the wire (not an enum) so an unknown id is a clean error frame from the agent
29
29
  // route — the same bet RepoParamSchema makes — and adding an ACP agent needs no contract change.
30
- export const NATIVE_PROVIDERS = ["claude", "codex", "grok", "kimi"] as const;
30
+ export const NATIVE_PROVIDERS = ["claude", "codex", "grok", "kimi", "gemini"] as const;
31
31
  export type NativeProvider = (typeof NATIVE_PROVIDERS)[number];
32
32
  export const AgentProviderSchema = z.string().min(1);
33
33
  export type AgentProvider = z.infer<typeof AgentProviderSchema>;
@@ -54,6 +54,12 @@ export type EditorContext = z.infer<typeof EditorContextSchema>;
54
54
  // and filesystem paths — the regex is the injection guard. Shared by the turn input and the attach input.
55
55
  const ConversationIdSchema = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/);
56
56
 
57
+ // How tool calls are gated — the Claude Agent SDK's PermissionMode, narrowed to the four the composer offers
58
+ // (the SDK also has 'dontAsk'/'auto', which have no UI here). The user picks one per turn AND the agent can
59
+ // move itself between them mid-turn, so this is both a turn input and the payload of the `mode` frame.
60
+ export const PermissionModeSchema = z.enum(["default", "acceptEdits", "plan", "bypassPermissions"]);
61
+ export type PermissionMode = z.infer<typeof PermissionModeSchema>;
62
+
57
63
  export const AgentTurnSchema = z
58
64
  .object({
59
65
  prompt: z.string(),
@@ -89,8 +95,11 @@ export const AgentTurnSchema = z
89
95
  history: z.array(SessionTranscriptMessageSchema).max(200).optional(),
90
96
  // The browser sends the chosen model per turn; the provider token is the sandbox's own stored credential.
91
97
  model: z.string().optional(),
92
- // When true, run the always-plan flow (propose approve → execute). Reasoning controls are optional.
93
- plan: z.boolean().optional(),
98
+ // How tool calls are gated for this turn (the SDK's permissionMode, verbatim). 'plan' runs the
99
+ // propose → approve → execute flow; 'default' prompts per tool on the permission side channel;
100
+ // 'acceptEdits' auto-accepts file edits; 'bypassPermissions' runs everything. The agent can move
101
+ // itself between modes mid-turn (EnterPlanMode/ExitPlanMode), which rides back as a `mode` frame.
102
+ permissionMode: PermissionModeSchema.optional(),
94
103
  effort: z.string().optional(),
95
104
  thinking: z.boolean().optional(),
96
105
  // The opt-in editor context chip: what the user is looking at, folded into the prompt daemon-side.
@@ -140,7 +149,12 @@ export const AgentActivitySchema = z.object({
140
149
  });
141
150
  export type AgentActivity = z.infer<typeof AgentActivitySchema>;
142
151
  // Which "needs you" flags are raised — the fleet badge aggregates these across all agents.
143
- export const AgentAttentionSchema = z.object({ plan: z.boolean(), question: z.boolean(), conflict: z.boolean() });
152
+ export const AgentAttentionSchema = z.object({
153
+ plan: z.boolean(),
154
+ question: z.boolean(),
155
+ permission: z.boolean(),
156
+ conflict: z.boolean(),
157
+ });
144
158
  export type AgentAttention = z.infer<typeof AgentAttentionSchema>;
145
159
  export const AgentSummarySchema = z.object({
146
160
  // The conversationId.
@@ -193,22 +207,49 @@ export type LandResult = z.infer<typeof LandResultSchema>;
193
207
 
194
208
  // The providers whose model can run UNDER the Claude Code harness through the bundled translator (CLIProxyAPI),
195
209
  // which holds their SUBSCRIPTION OAuth and re-serves it behind an Anthropic endpoint. The `claude` provider is
196
- // absent — native Anthropic OAuth serves it directly, without the translator.
197
- export const KeyedProviderSchema = z.enum(["codex", "grok"]);
210
+ // absent — native Anthropic OAuth serves it directly, without the translator. Codex and Grok also have a native
211
+ // runtime and so carry the harness axis; `gemini` is routed-only (Google publishes no Anthropic-protocol
212
+ // endpoint and this sandbox bakes no Gemini runtime), so a Gemini turn is always a Claude Code turn.
213
+ export const KeyedProviderSchema = z.enum(["codex", "grok", "gemini"]);
198
214
  export type KeyedProvider = z.infer<typeof KeyedProviderSchema>;
199
215
 
200
216
  // Which routed-provider subscriptions are connected in the translator (per provider). Drives the
201
217
  // "connected / connect subscription" state in Sandbox ▸ Agent.
202
- export const TranslatorAccountsSchema = z.object({ codex: z.boolean(), grok: z.boolean() });
218
+ export const TranslatorAccountsSchema = z.object({ codex: z.boolean(), grok: z.boolean(), gemini: z.boolean() });
203
219
  export type TranslatorAccounts = z.infer<typeof TranslatorAccountsSchema>;
204
220
 
205
- // Side-channel bodies: the UI posts these to resolve a turn paused on a plan approval / question.
206
- export const DecisionSchema = z.object({ decisionId: z.string().min(1), approve: z.boolean(), feedback: z.string().optional() });
207
- export const AnswerSchema = z.object({
208
- requestId: z.string().min(1),
209
- answers: z.record(z.string(), z.array(z.string())).optional(),
210
- cancelled: z.boolean().optional(),
211
- });
221
+ // The side-channel body that un-parks a turn waiting on the user. Every interactive card plan approval,
222
+ // clarifying questions, a per-tool permission prompt parks on the SAME registry keyed by `requestId`, so
223
+ // one route resolves all three; the `kind` says which card answered and carries its payload.
224
+ export const AgentReplySchema = z.discriminatedUnion("kind", [
225
+ // ExitPlanMode approval. `mode` is the posture to execute the approved plan in — Claude Code's "yes, and
226
+ // auto-accept edits" (acceptEdits) vs "yes, and manually approve edits" (default); it rides back to the SDK
227
+ // as a session setMode. Rejection feedback loops back into the model as the denial reason.
228
+ z.object({
229
+ kind: z.literal("plan"),
230
+ requestId: z.string().min(1),
231
+ approve: z.boolean(),
232
+ mode: PermissionModeSchema.optional(),
233
+ feedback: z.string().optional(),
234
+ }),
235
+ // AskUserQuestion picks: question text → chosen option label(s) (+ any free-text "Other"). `cancelled`
236
+ // is the dismissal, which tells the model to proceed on sensible defaults rather than leaving it parked.
237
+ z.object({
238
+ kind: z.literal("question"),
239
+ requestId: z.string().min(1),
240
+ answers: z.record(z.string(), z.array(z.string())).optional(),
241
+ cancelled: z.boolean().optional(),
242
+ }),
243
+ // A per-tool permission prompt. 'once' allows this call only; 'always' also persists the SDK's suggested
244
+ // rules so the same tool stops asking; 'deny' blocks it and feeds `feedback` back as the reason.
245
+ z.object({
246
+ kind: z.literal("permission"),
247
+ requestId: z.string().min(1),
248
+ decision: z.enum(["once", "always", "deny"]),
249
+ feedback: z.string().optional(),
250
+ }),
251
+ ]);
252
+ export type AgentReply = z.infer<typeof AgentReplySchema>;
212
253
  // Steering: a user message delivered INTO the running turn (injected between tool calls, Claude Code style),
213
254
  // keyed by the conversation whose turn is in flight. NOT_FOUND when no steerable turn is running — the client
214
255
  // then falls back to a fresh send.
@@ -217,6 +258,26 @@ export const SteerSchema = z.object({ conversationId: z.string().min(1), text: z
217
258
  // /agent fetch (which sends no cancel frame).
218
259
  export const StopTurnSchema = z.object({ conversationId: z.string().min(1) });
219
260
 
261
+ // ---- claude subscription usage ----
262
+ // Which usage window is active for a Claude account, how much of it is spent, and when it resets. Two readers,
263
+ // so it lives here rather than beside the stream frames: the SDK's rate_limit_event rides it out on the agent
264
+ // stream (see the `rate_limit_info` frame in events.ts), and the daemon persists the latest snapshot per
265
+ // account so `/claude/accounts` can answer "how much is left on each" without spending a turn to find out.
266
+ export const RateLimitInfoSchema = z.object({
267
+ status: z.enum(["allowed", "allowed_warning", "rejected"]),
268
+ resetsAt: z.number().optional(), // epoch seconds
269
+ rateLimitType: z.string().optional(), // 'five_hour' | 'seven_day' | 'seven_day_opus' | ...
270
+ utilization: z.number().optional(), // 0-100, how much of the window is used
271
+ });
272
+ export type RateLimitInfo = z.infer<typeof RateLimitInfoSchema>;
273
+
274
+ // The persisted view: a snapshot plus when it was taken. Within one window utilization only climbs, so a
275
+ // snapshot stays a valid FLOOR until `resetsAt` passes — after that the window has rolled over and the store
276
+ // drops it rather than reporting a stale number. `measuredAt` is epoch MS (matching connectedAt), while
277
+ // `resetsAt` stays epoch SECONDS (matching the SDK frame) — they are deliberately different units.
278
+ export const AccountUsageSchema = RateLimitInfoSchema.extend({ measuredAt: z.number() });
279
+ export type AccountUsage = z.infer<typeof AccountUsageSchema>;
280
+
220
281
  // ---- provider oauth ----
221
282
  // Claude uses the PKCE authorize-URL + paste-back handshake (start → exchange). Codex uses OpenAI's device-code
222
283
  // flow (start → poll): the browser signs in at verificationUri and enters userCode; the daemon polls until done.
@@ -234,6 +295,10 @@ export const OauthAccountSchema = z.object({
234
295
  // Provider-agnostic; only Codex probes it today (Claude refreshes on-demand, Grok's tokens are OpenCode's).
235
296
  needsReauth: z.boolean().optional(),
236
297
  detail: z.string().optional(),
298
+ // The account's last known subscription-usage snapshot, so the picker can show what's left on each account
299
+ // before the user commits a turn to one. Claude-only (it is the sole provider whose stream reports a usage
300
+ // window) and absent until that account has run a turn — an unmeasured account reads as unknown, never 0%.
301
+ usage: AccountUsageSchema.optional(),
237
302
  });
238
303
  export type OauthAccount = z.infer<typeof OauthAccountSchema>;
239
304
  export const OauthAccountListSchema = z.object({ accounts: z.array(OauthAccountSchema) });
@@ -259,16 +324,48 @@ export const KimiConnectSchema = z.object({ apiKey: z.string().min(1), label: z.
259
324
  // completion and the UI polls `/grok/accounts`.
260
325
  // ponytail: OpenCode holds one xAI auth per data dir, so Grok stays single-account — the list is 0 or 1. Per
261
326
  // account would need an OpenCode server per data dir; add when there's demand.
262
- // A device-code login start: the verification URL + the one-time code the user enters there. Shared by the
263
- // native Grok flow (via OpenCode) and the routed-provider subscription connect (codex/grok via CLIProxyAPI).
327
+ // A device-code login start: the verification URL + the one-time code the user enters there. The native Grok
328
+ // flow (via OpenCode) see TranslatorStartSchema for the routed-provider connect, which adds `state`.
264
329
  export const DeviceStartSchema = z.object({ url: z.string(), code: z.string() });
330
+ // A routed-provider subscription login start (codex/grok/gemini via CLIProxyAPI). Codex and Grok mint a
331
+ // one-time device `code` the user enters at the provider's site, and CLIProxyAPI polls to completion on its
332
+ // own — the card only waits. Google publishes no device flow: the user approves in a browser and is redirected
333
+ // to a loopback URL this sandbox never receives, so `code` is empty and the card asks them to paste that URL
334
+ // back (see TranslatorCompleteSchema). Which half a provider uses is READ from the response rather than
335
+ // hardcoded per provider, so the card needs no provider table.
336
+ export const TranslatorStartSchema = z.object({ url: z.string(), code: z.string(), state: z.string() });
337
+ // The paste-back half of a redirect login: the URL the provider sent the browser to, carrying the grant as
338
+ // ?code=&state=. `state` ties it to the handshake that issued it — the translator rejects a mismatch.
339
+ export const TranslatorCompleteSchema = z.object({
340
+ provider: KeyedProviderSchema,
341
+ redirectUrl: z.string().min(1),
342
+ state: z.string().min(1),
343
+ });
265
344
  // A provider's model catalog, resolved daemon-side from live discovery with a persisted last-known-good list and
266
345
  // a seed floor (Grok via opencode.ts xaiModels, Codex via codex-models.ts, Claude via the Agent SDK's
267
346
  // supportedModels) — never empty, so the picker is never blank. `label` is the humanized display name; `default`
268
347
  // is the model a fresh chat on that provider seeds (always present). Shared by /grok/models, /codex/models,
269
348
  // /claude/models. `efforts` is the reasoning-effort tiers the model accepts (Claude reports them per model);
270
349
  // empty ⇒ the client's default tiers.
271
- export const ModelSchema = z.object({ id: z.string(), label: z.string(), efforts: z.array(z.string()).optional() });
350
+ //
351
+ // EVERY field here is provider-reported — nothing about a model is curated in this repo, so a new release or a
352
+ // renamed family flows to the UI with no code change. Providers differ in how much they publish: the Claude
353
+ // Agent SDK reports a display name, a capability description, effort tiers, and capability flags, while the
354
+ // OpenAI-compatible /v1/models endpoints (codex/grok/kimi) report ids only — those rows render label-only, and
355
+ // that absence is the honest answer rather than something to paper over with a hand-written table.
356
+ //
357
+ // ORDER IS MEANINGFUL: `models` arrives in the provider's own preference order, which is what the picker sorts
358
+ // by, and `default` is the provider's own default. Neither is re-ranked locally.
359
+ export const ModelBadgeSchema = z.enum(["reasoning", "fast"]);
360
+ export type ModelBadge = z.infer<typeof ModelBadgeSchema>;
361
+ export const ModelSchema = z.object({
362
+ id: z.string(),
363
+ label: z.string(),
364
+ efforts: z.array(z.string()).optional(),
365
+ description: z.string().optional(),
366
+ badges: z.array(ModelBadgeSchema).optional(),
367
+ });
368
+ export type Model = z.infer<typeof ModelSchema>;
272
369
  export const ModelsSchema = z.object({ models: z.array(ModelSchema), default: z.string() });
273
370
 
274
371
  // ---- sessions ----
@@ -375,9 +472,14 @@ export const RepoChangesSchema = z.object({
375
472
  // Absent on an unborn HEAD (a repo initialized but never committed).
376
473
  branch: z.string().optional(),
377
474
  changes: z.array(GitChangeSchema),
475
+ // Why the repo could not be scanned at all, condensed to git's own one-line reason ("fatal: bad object HEAD").
476
+ // A repo left torn by a canceled or failed upload used to be dropped from the response entirely, so it just
477
+ // vanished from the panel with nothing to act on; it now arrives with empty `changes` and this set instead.
478
+ error: z.string().optional(),
378
479
  });
379
480
  export type RepoChanges = z.infer<typeof RepoChangesSchema>;
380
- // The aggregated review set across every repo (root + every discovered repo); only repos with changes appear.
481
+ // The aggregated review set across every repo (root + every discovered repo); a repo appears when it has changes
482
+ // or when it failed to scan.
381
483
  export const GitChangesSchema = z.object({ repos: z.array(RepoChangesSchema) });
382
484
  export type GitChanges = z.infer<typeof GitChangesSchema>;
383
485
 
@@ -1,8 +0,0 @@
1
- export type ModelBadge = "reasoning" | "vision" | "fast" | "agentic";
2
- export interface ModelMetadata {
3
- readonly description?: string;
4
- readonly badges?: readonly ModelBadge[];
5
- readonly rank?: number;
6
- }
7
- export declare const modelMetadataFor: (id: string) => ModelMetadata | undefined;
8
- //# sourceMappingURL=model-metadata.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"model-metadata.d.ts","sourceRoot":"","sources":["../src/model-metadata.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAErE,MAAM,WAAW,aAAa;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAIxC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CAC1B;AA0BD,eAAO,MAAM,gBAAgB,OAAQ,MAAM,KAAG,aAAa,GAAG,SAG7D,CAAC"}
@@ -1,24 +0,0 @@
1
- const FLAGSHIP = 90;
2
- const BALANCED = 80;
3
- const CODING = 65;
4
- const FAST = 50;
5
- const RULES = [
6
- { pattern: /opus/, metadata: { description: `Deepest reasoning, most capable Claude`, badges: [`reasoning`, `vision`], rank: FLAGSHIP } },
7
- { pattern: /sonnet/, metadata: { description: `Balanced Claude for everyday coding`, badges: [`reasoning`, `vision`], rank: BALANCED } },
8
- { pattern: /haiku/, metadata: { description: `Fastest Claude, light tasks`, badges: [`fast`, `vision`], rank: FAST } },
9
- { pattern: /codex.*(mini|nano)|(mini|nano).*codex/, metadata: { description: `Smaller, faster Codex`, badges: [`agentic`, `fast`], rank: FAST } },
10
- { pattern: /codex/, metadata: { description: `OpenAI's agentic coding model`, badges: [`agentic`, `reasoning`], rank: CODING } },
11
- { pattern: /gpt.*(mini|nano)/, metadata: { description: `Small, fast OpenAI model`, badges: [`fast`], rank: FAST } },
12
- { pattern: /gpt/, metadata: { description: `OpenAI flagship, general reasoning`, badges: [`reasoning`, `vision`], rank: FLAGSHIP } },
13
- { pattern: /grok.*(fast|mini)/, metadata: { description: `Speed-first Grok`, badges: [`fast`], rank: FAST } },
14
- { pattern: /grok.*code/, metadata: { description: `xAI's coding model`, badges: [`agentic`, `fast`], rank: CODING } },
15
- { pattern: /grok/, metadata: { description: `xAI frontier model`, badges: [`reasoning`], rank: FLAGSHIP } },
16
- { pattern: /kimi.*(turbo|flash|fast)|k2.*(turbo|flash|fast)/, metadata: { description: `Speed-first Kimi`, badges: [`fast`], rank: FAST } },
17
- { pattern: /kimi|k2/, metadata: { description: `Moonshot's frontier model`, badges: [`reasoning`, `agentic`], rank: FLAGSHIP } },
18
- { pattern: /fast|mini|nano|lite|flash/, metadata: { badges: [`fast`], rank: FAST } },
19
- ];
20
- export const modelMetadataFor = (id) => {
21
- const normalized = id.toLowerCase();
22
- return RULES.find((rule) => rule.pattern.test(normalized))?.metadata;
23
- };
24
- //# sourceMappingURL=model-metadata.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"model-metadata.js","sourceRoot":"","sources":["../src/model-metadata.ts"],"names":[],"mappings":"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"}
@@ -1,44 +0,0 @@
1
- /* Curated presentation metadata for the model picker. The daemons discover models live and report only
2
- * `{ id, label, efforts? }` (ModelSchema), so descriptions and capability badges are curated here by id
3
- * pattern — first match wins, ordered specific → generic. An unmatched id simply renders label-only, so a
4
- * brand-new release degrades gracefully until a rule is added. */
5
-
6
- export type ModelBadge = "reasoning" | "vision" | "fast" | "agentic";
7
-
8
- export interface ModelMetadata {
9
- readonly description?: string;
10
- readonly badges?: readonly ModelBadge[];
11
- // Relative capability, strongest-first — the picker sorts each provider's models by this (a version number
12
- // breaks ties, so within a tier the newest generation leads). Only ever compared WITHIN one provider, so the
13
- // scale is coarse: FLAGSHIP (deepest) > BALANCED (everyday) > CODING (specialized) > FAST (light/small).
14
- readonly rank?: number;
15
- }
16
-
17
- // The coarse capability tiers `rank` draws from. Compared only within a provider, so absolute values are
18
- // arbitrary — only the ordering FLAGSHIP > BALANCED > CODING > FAST matters. An id matching no rule below
19
- // renders rankless and the picker floors it just under BALANCED (a new flagship surfaces rather than sinks).
20
- const FLAGSHIP = 90;
21
- const BALANCED = 80;
22
- const CODING = 65;
23
- const FAST = 50;
24
-
25
- const RULES: readonly { pattern: RegExp; metadata: ModelMetadata }[] = [
26
- { pattern: /opus/, metadata: { description: `Deepest reasoning, most capable Claude`, badges: [`reasoning`, `vision`], rank: FLAGSHIP } },
27
- { pattern: /sonnet/, metadata: { description: `Balanced Claude for everyday coding`, badges: [`reasoning`, `vision`], rank: BALANCED } },
28
- { pattern: /haiku/, metadata: { description: `Fastest Claude, light tasks`, badges: [`fast`, `vision`], rank: FAST } },
29
- { pattern: /codex.*(mini|nano)|(mini|nano).*codex/, metadata: { description: `Smaller, faster Codex`, badges: [`agentic`, `fast`], rank: FAST } },
30
- { pattern: /codex/, metadata: { description: `OpenAI's agentic coding model`, badges: [`agentic`, `reasoning`], rank: CODING } },
31
- { pattern: /gpt.*(mini|nano)/, metadata: { description: `Small, fast OpenAI model`, badges: [`fast`], rank: FAST } },
32
- { pattern: /gpt/, metadata: { description: `OpenAI flagship, general reasoning`, badges: [`reasoning`, `vision`], rank: FLAGSHIP } },
33
- { pattern: /grok.*(fast|mini)/, metadata: { description: `Speed-first Grok`, badges: [`fast`], rank: FAST } },
34
- { pattern: /grok.*code/, metadata: { description: `xAI's coding model`, badges: [`agentic`, `fast`], rank: CODING } },
35
- { pattern: /grok/, metadata: { description: `xAI frontier model`, badges: [`reasoning`], rank: FLAGSHIP } },
36
- { pattern: /kimi.*(turbo|flash|fast)|k2.*(turbo|flash|fast)/, metadata: { description: `Speed-first Kimi`, badges: [`fast`], rank: FAST } },
37
- { pattern: /kimi|k2/, metadata: { description: `Moonshot's frontier model`, badges: [`reasoning`, `agentic`], rank: FLAGSHIP } },
38
- { pattern: /fast|mini|nano|lite|flash/, metadata: { badges: [`fast`], rank: FAST } },
39
- ];
40
-
41
- export const modelMetadataFor = (id: string): ModelMetadata | undefined => {
42
- const normalized = id.toLowerCase();
43
- return RULES.find((rule) => rule.pattern.test(normalized))?.metadata;
44
- };