@nexrall/code-core 1.4.71 → 1.4.73

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 (126) hide show
  1. package/dist/agent/agentRegistry.d.ts.map +1 -1
  2. package/dist/agent/agentRegistry.js +28 -10
  3. package/dist/agent/agentTypes.d.ts.map +1 -1
  4. package/dist/agent/agentTypes.js +159 -48
  5. package/dist/agent/askOnce.d.ts.map +1 -1
  6. package/dist/agent/askOnce.js +14 -5
  7. package/dist/agent/backgroundAgents.d.ts.map +1 -1
  8. package/dist/agent/backgroundAgents.js +22 -8
  9. package/dist/agent/claimEvidence.d.ts.map +1 -1
  10. package/dist/agent/claimEvidence.js +2 -1
  11. package/dist/agent/compaction.d.ts.map +1 -1
  12. package/dist/agent/compaction.js +31 -10
  13. package/dist/agent/crossProcessLock.d.ts.map +1 -1
  14. package/dist/agent/crossProcessLock.js +14 -6
  15. package/dist/agent/editCompleteness.d.ts.map +1 -1
  16. package/dist/agent/fileLocks.d.ts.map +1 -1
  17. package/dist/agent/fileLocks.js +14 -5
  18. package/dist/agent/flaky.d.ts.map +1 -1
  19. package/dist/agent/hooks.d.ts.map +1 -1
  20. package/dist/agent/hooks.js +284 -75
  21. package/dist/agent/iterationPolicy.d.ts.map +1 -1
  22. package/dist/agent/iterationPolicy.js +26 -23
  23. package/dist/agent/lifecycleHost.d.ts.map +1 -1
  24. package/dist/agent/lifecycleHost.js +35 -11
  25. package/dist/agent/loop.d.ts +22 -4
  26. package/dist/agent/loop.d.ts.map +1 -1
  27. package/dist/agent/loop.js +238 -51
  28. package/dist/agent/memory.d.ts.map +1 -1
  29. package/dist/agent/memory.js +13 -7
  30. package/dist/agent/modelCatalogue.d.ts.map +1 -1
  31. package/dist/agent/modelCatalogue.js +4 -1
  32. package/dist/agent/nestedInstructions.d.ts +122 -0
  33. package/dist/agent/nestedInstructions.d.ts.map +1 -0
  34. package/dist/agent/nestedInstructions.js +696 -0
  35. package/dist/agent/peerRegistry.d.ts.map +1 -1
  36. package/dist/agent/peerRegistry.js +44 -6
  37. package/dist/agent/peerTransport.d.ts.map +1 -1
  38. package/dist/agent/peerTransport.js +18 -6
  39. package/dist/agent/planMode.d.ts.map +1 -1
  40. package/dist/agent/planMode.js +194 -41
  41. package/dist/agent/readDedupe.d.ts.map +1 -1
  42. package/dist/agent/readDedupe.js +3 -1
  43. package/dist/agent/securityLint.d.ts.map +1 -1
  44. package/dist/agent/sharedTasks.d.ts.map +1 -1
  45. package/dist/agent/sharedTasks.js +20 -5
  46. package/dist/agent/skills.d.ts +79 -0
  47. package/dist/agent/skills.d.ts.map +1 -1
  48. package/dist/agent/skills.js +403 -14
  49. package/dist/agent/subAgentBudget.d.ts.map +1 -1
  50. package/dist/agent/subTask.d.ts.map +1 -1
  51. package/dist/agent/subTask.js +87 -37
  52. package/dist/agent/subTaskSupport.d.ts.map +1 -1
  53. package/dist/agent/subTaskSupport.js +43 -14
  54. package/dist/agent/testIntegrity.d.ts.map +1 -1
  55. package/dist/agent/testIntegrity.js +4 -2
  56. package/dist/agent/toolDescriptions.d.ts.map +1 -1
  57. package/dist/agent/toolPrefetch.d.ts.map +1 -1
  58. package/dist/agent/toolPrefetch.js +15 -11
  59. package/dist/agent/trust.d.ts.map +1 -1
  60. package/dist/agent/trust.js +6 -2
  61. package/dist/agent/worktree.d.ts.map +1 -1
  62. package/dist/agent/worktree.js +70 -16
  63. package/dist/agent/worktreeEnforcement.d.ts.map +1 -1
  64. package/dist/agent/worktreeEnforcement.js +9 -4
  65. package/dist/api/client.d.ts +6 -0
  66. package/dist/api/client.d.ts.map +1 -1
  67. package/dist/api/client.js +64 -31
  68. package/dist/auth/index.d.ts.map +1 -1
  69. package/dist/auth/index.js +3 -1
  70. package/dist/checkpoint/manager.d.ts.map +1 -1
  71. package/dist/checkpoint/manager.js +22 -7
  72. package/dist/commands/loader.d.ts.map +1 -1
  73. package/dist/commands/loader.js +6 -1
  74. package/dist/index.d.ts +2 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +2 -0
  77. package/dist/mcp/client.d.ts +42 -0
  78. package/dist/mcp/client.d.ts.map +1 -1
  79. package/dist/mcp/client.js +89 -24
  80. package/dist/mcp/httpClient.d.ts +24 -1
  81. package/dist/mcp/httpClient.d.ts.map +1 -1
  82. package/dist/mcp/httpClient.js +80 -14
  83. package/dist/mcp/instructions.d.ts +11 -0
  84. package/dist/mcp/instructions.d.ts.map +1 -0
  85. package/dist/mcp/instructions.js +42 -0
  86. package/dist/mcp/manager.d.ts +35 -0
  87. package/dist/mcp/manager.d.ts.map +1 -1
  88. package/dist/mcp/manager.js +135 -24
  89. package/dist/mcp/server.d.ts.map +1 -1
  90. package/dist/mcp/server.js +48 -14
  91. package/dist/mcp/sseClient.d.ts +9 -1
  92. package/dist/mcp/sseClient.d.ts.map +1 -1
  93. package/dist/mcp/sseClient.js +43 -10
  94. package/dist/mcp/stats.d.ts.map +1 -1
  95. package/dist/permissions/bashClassify.d.ts.map +1 -1
  96. package/dist/permissions/bashClassify.js +25 -3
  97. package/dist/permissions/destructive.d.ts.map +1 -1
  98. package/dist/permissions/destructive.js +7 -2
  99. package/dist/permissions/destructiveTokens.d.ts.map +1 -1
  100. package/dist/permissions/destructiveTokens.js +104 -24
  101. package/dist/permissions/modePolicy.d.ts.map +1 -1
  102. package/dist/permissions/modePolicy.js +40 -17
  103. package/dist/permissions/rules.d.ts.map +1 -1
  104. package/dist/permissions/rules.js +7 -5
  105. package/dist/plugins/data.d.ts.map +1 -1
  106. package/dist/plugins/data.js +6 -2
  107. package/dist/plugins/index.d.ts.map +1 -1
  108. package/dist/plugins/index.js +18 -7
  109. package/dist/plugins/installer.d.ts.map +1 -1
  110. package/dist/plugins/installer.js +17 -10
  111. package/dist/plugins/sources.d.ts.map +1 -1
  112. package/dist/plugins/sources.js +9 -3
  113. package/dist/tools/executor.d.ts.map +1 -1
  114. package/dist/tools/executor.js +454 -168
  115. package/dist/tools/sandbox.d.ts.map +1 -1
  116. package/dist/tools/sandbox.js +5 -1
  117. package/dist/tools/symbols.d.ts.map +1 -1
  118. package/dist/tools/symbols.js +30 -8
  119. package/dist/tools/tsLangService.d.ts.map +1 -1
  120. package/dist/tools/tsLangService.js +12 -4
  121. package/dist/types.d.ts +17 -0
  122. package/dist/types.d.ts.map +1 -1
  123. package/dist/util/frontmatter.d.ts.map +1 -1
  124. package/dist/util/miniYaml.d.ts.map +1 -1
  125. package/dist/util/miniYaml.js +3 -1
  126. package/package.json +1 -1
@@ -5,6 +5,7 @@ exports.lastToolResults = exports.summariseSubTaskProgress = exports.capSubTaskT
5
5
  exports.dispatchSubAgent = dispatchSubAgent;
6
6
  exports.dispatchBackgroundSubAgent = dispatchBackgroundSubAgent;
7
7
  exports.dispatchForkedSubAgent = dispatchForkedSubAgent;
8
+ exports.runForkedSkill = runForkedSkill;
8
9
  exports.runAgentLoop = runAgentLoop;
9
10
  exports.trimToResumableBoundary = trimToResumableBoundary;
10
11
  const readDedupe_1 = require("./readDedupe");
@@ -16,6 +17,7 @@ const executor_1 = require("../tools/executor");
16
17
  const sharedTasks_1 = require("./sharedTasks");
17
18
  const agentTypes_1 = require("./agentTypes");
18
19
  const skills_1 = require("./skills");
20
+ const nestedInstructions_1 = require("./nestedInstructions");
19
21
  const rules_1 = require("../permissions/rules");
20
22
  const modePolicy_1 = require("../permissions/modePolicy");
21
23
  const destructive_1 = require("../permissions/destructive");
@@ -25,6 +27,7 @@ const destructive_1 = require("../permissions/destructive");
25
27
  const bashClassify_1 = require("../permissions/bashClassify");
26
28
  Object.defineProperty(exports, "bashNeedsRepoLock", { enumerable: true, get: function () { return bashClassify_1.bashNeedsRepoLock; } });
27
29
  const planMode_1 = require("./planMode");
30
+ const instructions_1 = require("../mcp/instructions");
28
31
  const worktreeEnforcement_1 = require("./worktreeEnforcement");
29
32
  const sandbox_1 = require("../tools/sandbox");
30
33
  const testIntegrity_1 = require("./testIntegrity");
@@ -235,8 +238,11 @@ function dispatchBackgroundSubAgent(input, options, agentTypes, hub) {
235
238
  if (overBudget)
236
239
  return { error: overBudget };
237
240
  const agentType = (typeof input.subagent_type === 'string' && input.subagent_type) || 'general-purpose';
238
- const description = (typeof input.description === 'string' && input.description.trim())
239
- || String(input.prompt ?? '').trim().slice(0, 60) || agentType;
241
+ const description = (typeof input.description === 'string' && input.description.trim()) ||
242
+ String(input.prompt ?? '')
243
+ .trim()
244
+ .slice(0, 60) ||
245
+ agentType;
240
246
  const mode = (0, modePolicy_1.parseMode)(options.mode);
241
247
  const backgroundPermission = backgroundPermissionPolicy(options, mode);
242
248
  const started = hub.start({ description, agentType }, async ({ abort, onToolUse }) => {
@@ -344,6 +350,59 @@ function dispatchForkedSubAgent(conversation, prompt, options, agentTypes, hub)
344
350
  }
345
351
  return { id: started.id, description, agentType, messages: conversation.length };
346
352
  }
353
+ /**
354
+ * Resolve the skill a `use_skill` call names, returning it ONLY if it runs as a
355
+ * fork (`context: 'fork'`) — the signal `runAgentLoop` uses to divert the call
356
+ * away from the inline-injection path and into the sub-agent machinery.
357
+ *
358
+ * The snapshot is the list the catalogue was built from (loaded once per run);
359
+ * a MISS reloads from disk so a skill created mid-turn is found — the same
360
+ * trade-off `runSubTask` documents for `agentTypes`.
361
+ */
362
+ function resolveForkSkill(skills, input, options) {
363
+ const name = typeof input.name === 'string' ? input.name.trim() : '';
364
+ if (!name)
365
+ return undefined;
366
+ const skill = (0, skills_1.findSkill)(skills, name) ??
367
+ (0, skills_1.resolveNestedSkill)(options.workDir, name) ?? // `<dir>:<name>` - see skills.ts
368
+ (0, skills_1.findSkill)((0, skills_1.loadSkillsWithWarnings)(options.workDir, options._extraSkills ?? []).skills, name);
369
+ return skill?.context === 'fork' ? skill : undefined;
370
+ }
371
+ /**
372
+ * Run a `context: fork` skill as an ISOLATED sub-agent (Claude Code parity):
373
+ * the expanded body is the child's prompt and the parent conversation is NOT
374
+ * copied in — a forked skill must stand on its own, which is the point of the
375
+ * fork (the invoking context stays clean and the child cannot lean on it).
376
+ *
377
+ * Background by default (CC 2.1.x), exactly like `task(run_in_background)`: the
378
+ * call returns as soon as the child starts and the report arrives later as a
379
+ * <task-notification>; `background: false` awaits the child and returns its
380
+ * report as the tool result. Hosts with no hub (headless `-p`/SDK callers)
381
+ * always await — the same place CC waits regardless of the frontmatter.
382
+ *
383
+ * The `subagent_type` rides the ordinary `task` dispatch, so `deny:
384
+ * ["task(<name>)"]` rules, the depth limit and the session sub-agent budget all
385
+ * apply unchanged (see runSubTask / dispatchSubAgent).
386
+ */
387
+ async function runForkedSkill(skill, args, options, agentTypes) {
388
+ const workDir = options.workDir ?? process.cwd();
389
+ const input = {
390
+ prompt: (0, skills_1.expandSkill)(skill, args, workDir),
391
+ description: `/${skill.name}`,
392
+ subagent_type: skill.agent?.trim() || 'general-purpose',
393
+ };
394
+ // `model:` on the skill applies to the fork exactly as it does when the skill is
395
+ // typed as a turn (`/name` in chat.ts): the child starts on that model. A model
396
+ // the agent TYPE declares still wins — the child resolves it over ours, same as
397
+ // any `task` call (see subTask.ts).
398
+ const childOptions = skill.model ? { ...options, model: (0, modelCatalogue_1.resolveSubAgentModel)(skill.model, options.model) } : options;
399
+ const depth = options._depth ?? 0;
400
+ const wantsBackground = skill.background !== false && depth === 0;
401
+ if (wantsBackground && childOptions.backgroundAgents) {
402
+ return dispatchBackgroundSubAgent(input, childOptions, agentTypes, childOptions.backgroundAgents);
403
+ }
404
+ return dispatchSubAgent(input, childOptions, agentTypes);
405
+ }
347
406
  // ─── Agent Loop ───────────────────────────────────────────────────────────────
348
407
  async function runAgentLoop(initialMessages, options) {
349
408
  const messages = [...initialMessages];
@@ -400,7 +459,10 @@ async function runAgentLoop(initialMessages, options) {
400
459
  const addContext = (ctx) => {
401
460
  const m = messages[lastIdx];
402
461
  if (m && Array.isArray(m.content))
403
- messages[lastIdx] = { ...m, content: [...m.content, { type: 'text', text: `\n\n<hook-context>\n${ctx}\n</hook-context>` }] };
462
+ messages[lastIdx] = {
463
+ ...m,
464
+ content: [...m.content, { type: 'text', text: `\n\n<hook-context>\n${ctx}\n</hook-context>` }],
465
+ };
404
466
  };
405
467
  const sid = options.sessionId || subAgentBudget_1.PROCESS_BUDGET_KEY;
406
468
  if (isPrompt && !_sessionStarted.has(sid) && (hooks.SessionStart?.length ?? 0) > 0) {
@@ -436,7 +498,9 @@ async function runAgentLoop(initialMessages, options) {
436
498
  // the user request that caused it belong to the same run — which is precisely
437
499
  // the chain of custody the trail exists to prove.
438
500
  const auditCfg = options.audit
439
- ? (options.audit.sessionId ? options.audit : { ...options.audit, sessionId: (0, audit_1.newAuditSessionId)() })
501
+ ? options.audit.sessionId
502
+ ? options.audit
503
+ : { ...options.audit, sessionId: (0, audit_1.newAuditSessionId)() }
440
504
  : undefined;
441
505
  const auditCtx = auditCfg
442
506
  ? {
@@ -479,7 +543,24 @@ async function runAgentLoop(initialMessages, options) {
479
543
  // just a reusable prompt template (via use_skill), not another spawn point, so
480
544
  // sub-agents benefit from the same playbooks without the recursion concerns that
481
545
  // gate agentsCatalogue to the top level.
482
- const skillsCatalogue = (0, skills_1.summariseSkills)((0, skills_1.loadSkillsWithWarnings)(options.workDir, options._extraSkills ?? []).skills);
546
+ const skillsList = (0, skills_1.loadSkillsWithWarnings)(options.workDir, options._extraSkills ?? []).skills;
547
+ // Nested-skill discovery (CC 2.1.x parity): as tool calls touch files, their
548
+ // directories get checked for their own `.nexrall/skills`, and anything found is
549
+ // addressed as `<dir>:<name>` (see skills.ts). The catalogue below is rebuilt
550
+ // PER REQUEST precisely so a discovery mid-turn reaches the model on its next
551
+ // round-trip without a new user message; resolutions go through findSkill /
552
+ // resolveNestedSkill, so a scoped name works even before its turn advertises it.
553
+ const nestedSkillScan = options.workDir ? new skills_1.NestedSkillScanner(options.workDir) : null;
554
+ // Nested instruction files (S18c): a subdirectory's `nexrall.md` applies once the
555
+ // agent works there — the same 2.1.x behaviour as the nested skills above, and the
556
+ // same per-request composition (the prompt reads the ledger's list() every call).
557
+ // The HOST normally owns the ledger: one scanner per session, passed to every run,
558
+ // because a loaded file must not silently vanish between turns. A host that passes
559
+ // nothing gets run-scoped discovery — the right lifetime for a one-shot run. `false`
560
+ // disables the feature entirely (`--bare`: no discovery).
561
+ const nestedInstructionScan = options._nestedInstructions === false
562
+ ? null
563
+ : (options._nestedInstructions ?? (options.workDir ? new nestedInstructions_1.NestedInstructionScanner(options.workDir) : null));
483
564
  // Plan mode instructions ride the project-instructions channel, which is
484
565
  // authoritative in the system prompt. Prepended rather than appended: the
485
566
  // lock has to be read before the project conventions it overrides.
@@ -491,7 +572,26 @@ async function runAgentLoop(initialMessages, options) {
491
572
  options.planMode ? planMode_1.PLAN_MODE_INSTRUCTIONS : null,
492
573
  options.worktree ? (0, worktreeEnforcement_1.worktreeInstructions)(options.worktree) : null,
493
574
  options.nexrallMd ?? null,
494
- ].filter((s) => s !== null).join('\n\n---\n\n') || undefined; // '' (nothing at all was present) must behave exactly like the old `: options.nexrallMd` fallback (undefined), not an empty-but-truthy string.
575
+ ]
576
+ .filter((s) => s !== null)
577
+ .join('\n\n---\n\n') || undefined; // '' (nothing at all was present) must behave exactly like the old `: options.nexrallMd` fallback (undefined), not an empty-but-truthy string.
578
+ // MCP `initialize.instructions` — a server's own reference notes. Composed PER
579
+ // REQUEST so a server that connects (or is enabled) mid-session contributes
580
+ // without the caller rebuilding the session brief; a sub-agent only reads the
581
+ // servers its allowlist names (another server's notes are not its business).
582
+ // The size caps live in mcp/instructions.ts.
583
+ const mcpInstructionsBlock = () => (0, instructions_1.formatMcpInstructions)((options.mcpManager?.serverInstructions() ?? []).filter((e) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(e.server)));
584
+ // Per-server `alwaysLoad` (CC parity): names the user pinned in mcp.json. The
585
+ // backend keeps their `<server>__*` tools out of BOTH deferral paths (tool-search
586
+ // `defer_loading` and the cap selector's droppable set). Sub-agent runs narrow the
587
+ // list to servers their own allowlist can actually reach — naming a server the run
588
+ // cannot use would protect nothing and leak the catalogue's shape. Empty →
589
+ // undefined so older backends receive the exact same body as before.
590
+ const alwaysLoadServerNames = (() => {
591
+ const named = options.mcpManager?.alwaysLoadServers() ?? [];
592
+ const scoped = named.filter((n) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(n));
593
+ return scoped.length ? scoped : undefined;
594
+ })();
495
595
  // The request options every main-loop call sends. Shared with the compaction
496
596
  // summariser so its request has the SAME system prompt and tools as the conversation
497
597
  // — that is what lets the summary call read the whole history from cache (0.10×)
@@ -502,7 +602,13 @@ async function runAgentLoop(initialMessages, options) {
502
602
  effort: options.effort,
503
603
  env: options.env,
504
604
  editorContext: options.editorContext,
505
- nexrallMd: planAndWorktreeAwareNexrallMd,
605
+ nexrallMd: [
606
+ planAndWorktreeAwareNexrallMd,
607
+ (0, nestedInstructions_1.formatNestedInstructions)(nestedInstructionScan?.list() ?? []),
608
+ mcpInstructionsBlock(),
609
+ ]
610
+ .filter((s) => !!s)
611
+ .join('\n\n---\n\n') || undefined,
506
612
  clientType: options.clientType,
507
613
  abortSignal: options.abortSignal,
508
614
  // MCP tools, plus the per-agent memory tool when (and only when) this run
@@ -511,8 +617,7 @@ async function runAgentLoop(initialMessages, options) {
511
617
  // to every other run: a tool the model cannot see is one it cannot try,
512
618
  // which is better than advertising it everywhere and refusing it at the gate.
513
619
  extraTools: [
514
- ...(options.mcpManager?.getAnthropicTools() ?? [])
515
- .filter((t) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(String(t.name).split('__')[0])),
620
+ ...(options.mcpManager?.getAnthropicTools() ?? []).filter((t) => !options._mcpServerAllowlist || options._mcpServerAllowlist.has(String(t.name).split('__')[0])),
516
621
  ...(options._agentMemory ? [iterationPolicy_1.AGENT_MEMORY_TOOL_SCHEMA] : []),
517
622
  ...(options.extraToolSchemas ?? []),
518
623
  ],
@@ -522,11 +627,7 @@ async function runAgentLoop(initialMessages, options) {
522
627
  // memory_write, which no sub-agent allowlist contains.
523
628
  // Sub-agents NEVER write the shared store (the main agent decides what to persist) —
524
629
  // an unnamed/general-purpose child has no allowlist, which used to read as "may write".
525
- canWriteSharedMemory: depth > 0
526
- ? false
527
- : options._allowedTools
528
- ? options._allowedTools.has('memory_write')
529
- : true, // no allowlist = main agent = may write
630
+ canWriteSharedMemory: depth > 0 ? false : options._allowedTools ? options._allowedTools.has('memory_write') : true, // no allowlist = main agent = may write
530
631
  hasOwnAgentStore: !!options._agentMemory,
531
632
  // Same derivation, same reason, for the network. The base prompt's
532
633
  // "Web search" section tells the run to reach the network, but
@@ -543,11 +644,15 @@ async function runAgentLoop(initialMessages, options) {
543
644
  // (and gets refused on) write, office and image tools. Sorted so the tools cache
544
645
  // prefix is identical for every run of the same agent type.
545
646
  toolAllowlist: options._allowedTools ? [...options._allowedTools].sort() : undefined,
647
+ // Servers the user pinned with `alwaysLoad: true` (computed above). The backend
648
+ // keeps their `<server>__*` tools out of the tool cap and both deferral paths;
649
+ // undefined (not []) when nothing is pinned so older backends see the same body.
650
+ alwaysLoadServers: alwaysLoadServerNames,
546
651
  // Withholds the `task` schema and its instructions when this run cannot
547
652
  // delegate — see canSpawnSubAgents.
548
653
  canSpawnSubAgents: maySpawn,
549
654
  agents: agentsCatalogue || undefined,
550
- skills: skillsCatalogue || undefined,
655
+ skills: (0, skills_1.summariseSkills)(skillsList) || undefined,
551
656
  });
552
657
  // Optional OS-level bash sandbox (opt-in via settings.json "sandbox").
553
658
  const sandboxCfg = (0, sandbox_1.parseSandboxConfig)(settings.raw.sandbox) ?? undefined;
@@ -563,7 +668,7 @@ async function runAgentLoop(initialMessages, options) {
563
668
  // Per-run: a sub-agent's own runAgentLoop gets its own, so it never sees its parent's reads.
564
669
  const readDedupe = new readDedupe_1.ReadDedupe(options.workDir);
565
670
  // Only the main agent owns background agents (runSubTask never passes the hub down).
566
- const drainBackgroundNotifications = () => depth === 0 ? (options.backgroundAgents?.takeNotifications() ?? []) : [];
671
+ const drainBackgroundNotifications = () => (depth === 0 ? (options.backgroundAgents?.takeNotifications() ?? []) : []);
567
672
  // Live prompt-size estimate, updated from usage events after every stream.
568
673
  let lastPromptTokens = 0;
569
674
  let compacting = false; // re-entrancy guard — compaction itself calls streamChat
@@ -734,7 +839,11 @@ async function runAgentLoop(initialMessages, options) {
734
839
  compactDisabled = false;
735
840
  compactFailures = 0;
736
841
  }
737
- if (autoCompact && !compactDisabled && !compacting && (tokenPressure || bytePressure) && messages.length > compaction_1.COMPACT_KEEP_MIN + 2) {
842
+ if (autoCompact &&
843
+ !compactDisabled &&
844
+ !compacting &&
845
+ (tokenPressure || bytePressure) &&
846
+ messages.length > compaction_1.COMPACT_KEEP_MIN + 2) {
738
847
  compacting = true;
739
848
  try {
740
849
  // Measured BEFORE, so "did it actually help?" is a fact about bytes rather
@@ -759,9 +868,7 @@ async function runAgentLoop(initialMessages, options) {
759
868
  const reclaimed = bytesBefore - bytesAfter;
760
869
  if (did) {
761
870
  lastPromptTokens = 0; // stale — next usage event refreshes it
762
- const reason = bytePressure
763
- ? `body ~${(bodyBytes / (1024 * 1024)).toFixed(1)}MB`
764
- : 'context window';
871
+ const reason = bytePressure ? `body ~${(bodyBytes / (1024 * 1024)).toFixed(1)}MB` : 'context window';
765
872
  // Silent, same as the prune above — routine housekeeping. Only the
766
873
  // actionable "auto-compaction paused" warning below is surfaced.
767
874
  void reason;
@@ -1091,8 +1198,8 @@ async function runAgentLoop(initialMessages, options) {
1091
1198
  // be an example here too — as of the 2026-08-11+ provider-agnostic port it is a
1092
1199
  // plain client tool executed by THIS process, so it no longer belongs in this set.)
1093
1200
  const serverSideResultIds = new Set(assistantMessage.content
1094
- .filter(b => b.type === 'tool_result')
1095
- .map(b => b.tool_use_id)
1201
+ .filter((b) => b.type === 'tool_result')
1202
+ .map((b) => b.tool_use_id)
1096
1203
  .filter((id) => !!id));
1097
1204
  const toolUseBlocks = assistantMessage.content.filter((block) => block.type === 'tool_use' && !serverSideResultIds.has(block.id));
1098
1205
  // ─── TRUNCATED-TOOL-CALL GUARD ──────────────────────────────────────────
@@ -1117,9 +1224,7 @@ async function runAgentLoop(initialMessages, options) {
1117
1224
  // model retries with a smaller call, rather than either failing
1118
1225
  // mysteriously or silently mutating a file incompletely.
1119
1226
  const lastBlock = assistantMessage.content[assistantMessage.content.length - 1];
1120
- const truncatedToolUseId = assistantMessage.stopReason === 'max_tokens' && lastBlock?.type === 'tool_use'
1121
- ? lastBlock.id
1122
- : undefined;
1227
+ const truncatedToolUseId = assistantMessage.stopReason === 'max_tokens' && lastBlock?.type === 'tool_use' ? lastBlock.id : undefined;
1123
1228
  // 4. If no tool use → agent produced a final text response.
1124
1229
  // But if the user queued a follow-up while we were working, fold it in as the
1125
1230
  // next user turn and keep going instead of ending (Claude-Code style).
@@ -1170,7 +1275,8 @@ async function runAgentLoop(initialMessages, options) {
1170
1275
  const bullet = fresh.map((t) => ` • ${t.path}: ${t.reason}`).join('\n');
1171
1276
  messages.push({
1172
1277
  role: 'user',
1173
- content: [{
1278
+ content: [
1279
+ {
1174
1280
  type: 'text',
1175
1281
  text: 'STOP — test-integrity check. Before finishing, I detected that you WEAKENED test(s) this session:\n' +
1176
1282
  bullet +
@@ -1178,7 +1284,8 @@ async function runAgentLoop(initialMessages, options) {
1178
1284
  ' 1. Revert the weakening and fix the actual code so the ORIGINAL test passes, or\n' +
1179
1285
  ' 2. Justify concretely why the change is a legitimate test refactor (e.g. the behaviour was intentionally removed per the task, a duplicate test, or the assertion was genuinely wrong) — and confirm the remaining tests still meaningfully cover the behaviour.\n' +
1180
1286
  'Then re-run the test suite to prove it passes for real.',
1181
- }],
1287
+ },
1288
+ ],
1182
1289
  });
1183
1290
  continue;
1184
1291
  }
@@ -1191,15 +1298,19 @@ async function runAgentLoop(initialMessages, options) {
1191
1298
  if (flaky.length) {
1192
1299
  for (const f of flaky)
1193
1300
  flakyNudgedCmds.add(f.cmd);
1194
- const bullet = flaky.map((f) => ` • ${f.cmd} (${f.passes} pass / ${f.fails} fail on identical code)`).join('\n');
1301
+ const bullet = flaky
1302
+ .map((f) => ` • ${f.cmd} (${f.passes} pass / ${f.fails} fail on identical code)`)
1303
+ .join('\n');
1195
1304
  messages.push({
1196
1305
  role: 'user',
1197
- content: [{
1306
+ content: [
1307
+ {
1198
1308
  type: 'text',
1199
1309
  text: 'STOP — flaky-test check. These command(s) produced BOTH a pass and a fail with NO code change in between, so they are non-deterministic:\n' +
1200
1310
  bullet +
1201
1311
  '\n\nA green run of a flaky test proves nothing, and re-running until it passes is not a fix. Do NOT declare success on this basis. Instead: identify the source of non-determinism (unseeded RNG, timing/sleep races, test-ordering or shared-state dependence, wall-clock/timezone, network) and make the test deterministic — or, if it is out of scope, say so explicitly and flag it as pre-existing flakiness rather than treating the passing run as proof.',
1202
- }],
1312
+ },
1313
+ ],
1203
1314
  });
1204
1315
  continue;
1205
1316
  }
@@ -1213,10 +1324,12 @@ async function runAgentLoop(initialMessages, options) {
1213
1324
  verificationNudgeSent = true;
1214
1325
  messages.push({
1215
1326
  role: 'user',
1216
- content: [{
1327
+ content: [
1328
+ {
1217
1329
  type: 'text',
1218
- text: 'Before finishing: you edited file(s) in this task but I don\'t see a build/test/lint/typecheck command run to confirm the change works. If this project has one, please run it now and fix any failures. If verification genuinely doesn\'t apply here (e.g. docs-only change, no test suite), just say so and finish.',
1219
- }],
1330
+ text: "Before finishing: you edited file(s) in this task but I don't see a build/test/lint/typecheck command run to confirm the change works. If this project has one, please run it now and fix any failures. If verification genuinely doesn't apply here (e.g. docs-only change, no test suite), just say so and finish.",
1331
+ },
1332
+ ],
1220
1333
  });
1221
1334
  continue;
1222
1335
  }
@@ -1358,6 +1471,11 @@ async function runAgentLoop(initialMessages, options) {
1358
1471
  if (err instanceof subTaskSupport_1.ToolNotAllowedError)
1359
1472
  deniedReason = err.message;
1360
1473
  }
1474
+ // `context: fork` skills never reach the executor's inline injection: the
1475
+ // call is diverted into the same sub-agent machinery `task` uses, with the
1476
+ // expanded body as the child's prompt (see runForkedSkill). Resolved here —
1477
+ // only for an allowed `use_skill` — so a denied call pays nothing for it.
1478
+ const forkSkill = permitted && name === 'use_skill' ? resolveForkSkill(skillsList, input, options) : undefined;
1361
1479
  if (!permitted) {
1362
1480
  result = { error: deniedReason ?? 'Permission denied by user' };
1363
1481
  }
@@ -1373,8 +1491,9 @@ async function runAgentLoop(initialMessages, options) {
1373
1491
  // reimplementing depth/budget/concurrency enforcement a second time.
1374
1492
  // Background only for the MAIN agent, and only when the host can deliver the
1375
1493
  // result later; otherwise it quietly runs in the foreground.
1376
- const wantsBackground = input.run_in_background === true
1377
- || (input.run_in_background !== false && !!(0, agentTypes_1.findAgentType)(agentTypes, String(input.subagent_type ?? ''))?.background);
1494
+ const wantsBackground = input.run_in_background === true ||
1495
+ (input.run_in_background !== false &&
1496
+ !!(0, agentTypes_1.findAgentType)(agentTypes, String(input.subagent_type ?? ''))?.background);
1378
1497
  const taskOptions = { ...auditOptions, _taskToolUseId: id };
1379
1498
  // PreToolUse/PostToolUse cover spawns too (Claude Code's matcher "Task"/"Agent"
1380
1499
  // works the same way): a hook can veto a delegation or audit what came back.
@@ -1383,9 +1502,10 @@ async function runAgentLoop(initialMessages, options) {
1383
1502
  result = { error: `Blocked by PreToolUse hook: ${preTask.reason}` };
1384
1503
  }
1385
1504
  else {
1386
- result = wantsBackground && depth === 0 && options.backgroundAgents && !input.resume_agent_id
1387
- ? dispatchBackgroundSubAgent(input, taskOptions, agentTypes, options.backgroundAgents)
1388
- : await dispatchSubAgent(input, taskOptions, agentTypes);
1505
+ result =
1506
+ wantsBackground && depth === 0 && options.backgroundAgents && !input.resume_agent_id
1507
+ ? dispatchBackgroundSubAgent(input, taskOptions, agentTypes, options.backgroundAgents)
1508
+ : await dispatchSubAgent(input, taskOptions, agentTypes);
1389
1509
  const postTask = await (0, hooks_1.runToolHooks)(hooks.PostToolUse, 'PostToolUse', name, input, options.workDir, result, (0, hooks_1.hookRunOptsFor)(options));
1390
1510
  const injectedTask = [preTask.context, postTask.context].filter(Boolean).join('\n');
1391
1511
  if (injectedTask) {
@@ -1399,6 +1519,28 @@ async function runAgentLoop(initialMessages, options) {
1399
1519
  }
1400
1520
  }
1401
1521
  }
1522
+ else if (forkSkill) {
1523
+ // `use_skill` on a `context: fork` skill — the same hook pair as any call,
1524
+ // so a Pre can veto the fork and a Post sees its start/report.
1525
+ const preFork = await (0, hooks_1.runToolHooks)(hooks.PreToolUse, 'PreToolUse', name, input, options.workDir, undefined, (0, hooks_1.hookRunOptsFor)(options));
1526
+ if (preFork.block) {
1527
+ result = { error: `Blocked by PreToolUse hook: ${preFork.reason}` };
1528
+ }
1529
+ else {
1530
+ result = await runForkedSkill(forkSkill, typeof input.args === 'string' ? input.args : '', { ...auditOptions, _taskToolUseId: id }, agentTypes);
1531
+ const postFork = await (0, hooks_1.runToolHooks)(hooks.PostToolUse, 'PostToolUse', name, input, options.workDir, result, (0, hooks_1.hookRunOptsFor)(options));
1532
+ const injectedFork = [preFork.context, postFork.context].filter(Boolean).join('\n');
1533
+ if (injectedFork) {
1534
+ if (result.error !== undefined)
1535
+ result.error = `${result.error}\n\n[hook] ${injectedFork}`;
1536
+ else
1537
+ result.output = `${result.output ?? ''}\n\n[hook] ${injectedFork}`;
1538
+ }
1539
+ if (postFork.block) {
1540
+ result.error = `${result.error ? result.error + '\n' : ''}PostToolUse hook flagged: ${postFork.reason}`;
1541
+ }
1542
+ }
1543
+ }
1402
1544
  else {
1403
1545
  const pre = await (0, hooks_1.runToolHooks)(hooks.PreToolUse, 'PreToolUse', name, input, options.workDir, undefined, (0, hooks_1.hookRunOptsFor)(options));
1404
1546
  if (pre.block) {
@@ -1407,9 +1549,7 @@ async function runAgentLoop(initialMessages, options) {
1407
1549
  else {
1408
1550
  // TaskCreated needs the id of the task the tool is ABOUT to create, which only
1409
1551
  // the tool can know — so snapshot the ids first and diff afterwards (see below).
1410
- const taskIdsBefore = name === 'create_shared_task'
1411
- ? new Set((0, sharedTasks_1.listSharedTasks)(options.workDir).map((t) => t.id))
1412
- : null;
1552
+ const taskIdsBefore = name === 'create_shared_task' ? new Set((0, sharedTasks_1.listSharedTasks)(options.workDir).map((t) => t.id)) : null;
1413
1553
  try {
1414
1554
  // TaskCompleted (Claude Code): the hook is the GATE, so it runs BEFORE the
1415
1555
  // update — refusing means the task simply stays open, and the reason goes
@@ -1445,9 +1585,10 @@ async function runAgentLoop(initialMessages, options) {
1445
1585
  }
1446
1586
  else if (options.mcpManager?.isMcpTool(name)) {
1447
1587
  const mcpOutput = await (0, subTaskSupport_1.stopAwaiting)(options.mcpManager.callTool(name, input, options.abortSignal), options.abortSignal);
1448
- result = mcpOutput === subTaskSupport_1.STOP_TIMEOUT_RESULT
1449
- ? subTaskSupport_1.STOP_TIMEOUT_RESULT
1450
- : { output: (0, executor_1.capExternalOutput)(mcpOutput ?? '', `mcp-${name}`) };
1588
+ result =
1589
+ mcpOutput === subTaskSupport_1.STOP_TIMEOUT_RESULT
1590
+ ? subTaskSupport_1.STOP_TIMEOUT_RESULT
1591
+ : { output: (0, executor_1.capExternalOutput)(mcpOutput ?? '', `mcp-${name}`) };
1451
1592
  // 3. Fall through to built-in executor
1452
1593
  }
1453
1594
  else {
@@ -1472,7 +1613,9 @@ async function runAgentLoop(initialMessages, options) {
1472
1613
  // This matters far more with sub-agents than without: up to 4 run
1473
1614
  // concurrently, each issuing its own tool calls into this same process.
1474
1615
  const locks = name === 'bash'
1475
- ? ((0, bashClassify_1.bashNeedsRepoLock)(String(input.command ?? '')) ? [fileLocks_1.REPO_STATE_LOCK] : [])
1616
+ ? (0, bashClassify_1.bashNeedsRepoLock)(String(input.command ?? ''))
1617
+ ? [fileLocks_1.REPO_STATE_LOCK]
1618
+ : []
1476
1619
  : (0, fileLocks_1.lockPathsFor)(name, input, options.workDir);
1477
1620
  // Two lock layers, nested outer-then-inner:
1478
1621
  // 1. Cross-process (OS lock file) — the ONLY layer that protects
@@ -1523,7 +1666,9 @@ async function runAgentLoop(initialMessages, options) {
1523
1666
  const task = created[created.length - 1];
1524
1667
  if (task) {
1525
1668
  const veto = await taskHookVeto(hooks.TaskCreated, 'TaskCreated', input, options, {
1526
- task_id: task.id, task_subject: task.content, task_description: task.content,
1669
+ task_id: task.id,
1670
+ task_subject: task.content,
1671
+ task_description: task.content,
1527
1672
  });
1528
1673
  if (veto) {
1529
1674
  (0, sharedTasks_1.deleteSharedTask)(options.workDir, task.id);
@@ -1531,6 +1676,35 @@ async function runAgentLoop(initialMessages, options) {
1531
1676
  }
1532
1677
  }
1533
1678
  }
1679
+ // Nested-skill discovery: whatever this call touched is where the model
1680
+ // is working, so those directories' own `.nexrall/skills` now matter.
1681
+ // Runs regardless of success - a failed read still signals the area,
1682
+ // and a re-touched directory costs a Set lookup.
1683
+ if (nestedSkillScan) {
1684
+ for (const s of nestedSkillScan.scanInput(input)) {
1685
+ if (!skillsList.some((k) => k.name === s.name))
1686
+ skillsList.push(s);
1687
+ }
1688
+ }
1689
+ // Nested instruction files are discovered on the same touch, from the same
1690
+ // directories (S18c), and path-scoped `.nexrall/rules` files when their globs
1691
+ // match (S18d). A fresh file joins the ledger the prompt reads per request,
1692
+ // and is announced ONCE to the host — carrying the vocabulary's own name for
1693
+ // its event ('nested_traversal' for a directory the agent worked in,
1694
+ // 'path_glob_match' for a rule whose `paths:` matched the touched path).
1695
+ // Sub-agents clear both the callback and the ledger (subTask.ts), so only
1696
+ // the session's own discoveries fire hooks; a child's ephemeral load is
1697
+ // not a fact about this session's instruction set.
1698
+ if (nestedInstructionScan) {
1699
+ const fresh = nestedInstructionScan.scanInput(input);
1700
+ if (fresh.length && options._onInstructionsDiscovered) {
1701
+ options._onInstructionsDiscovered(fresh.map((f) => ({
1702
+ file_path: f.filePath,
1703
+ memory_type: 'Project',
1704
+ load_reason: f.loadReason,
1705
+ })));
1706
+ }
1707
+ }
1534
1708
  // Opportunistic memory compaction — memory_write is the only tool that can
1535
1709
  // grow a memory file, so this is the cheapest point to check whether it just
1536
1710
  // crossed the compaction threshold. Fire-and-forget: never blocks the turn,
@@ -1544,7 +1718,12 @@ async function runAgentLoop(initialMessages, options) {
1544
1718
  // PostToolUseFailure: only when the call errored. Its context/exit-2 text joins the
1545
1719
  // PostToolUse output, so the model sees it next to the error it explains.
1546
1720
  if (result.error !== undefined && (hooks.PostToolUseFailure?.length ?? 0) > 0) {
1547
- const f = await (0, hooks_1.runLifecycleHooks)(hooks.PostToolUseFailure, 'PostToolUseFailure', options.workDir, { session_id: options.sessionId ?? '', tool_name: name, tool_input: input, error: String(result.error).slice(0, 4000) }, name, (0, hooks_1.hookRunOptsFor)(options)).catch(() => ({ block: false }));
1721
+ const f = await (0, hooks_1.runLifecycleHooks)(hooks.PostToolUseFailure, 'PostToolUseFailure', options.workDir, {
1722
+ session_id: options.sessionId ?? '',
1723
+ tool_name: name,
1724
+ tool_input: input,
1725
+ error: String(result.error).slice(0, 4000),
1726
+ }, name, (0, hooks_1.hookRunOptsFor)(options)).catch(() => ({ block: false }));
1548
1727
  if (f.context)
1549
1728
  post.context = [post.context, f.context].filter(Boolean).join('\n');
1550
1729
  if (f.block) {
@@ -1630,8 +1809,10 @@ async function runAgentLoop(initialMessages, options) {
1630
1809
  // command that timed out mid-test-run), surface both so the model can
1631
1810
  // see WHY it hung — the failure detail is usually in the output tail.
1632
1811
  content: result.error
1633
- ? (result.output ? `Error: ${result.error}\n\n${result.output}` : `Error: ${result.error}`)
1634
- : result.output ?? '',
1812
+ ? result.output
1813
+ ? `Error: ${result.error}\n\n${result.output}`
1814
+ : `Error: ${result.error}`
1815
+ : (result.output ?? ''),
1635
1816
  is_error: result.error !== undefined,
1636
1817
  }));
1637
1818
  // Fold any follow-up the user queued mid-task into THIS user turn (alongside the
@@ -1687,7 +1868,9 @@ async function runAgentLoop(initialMessages, options) {
1687
1868
  tool_input: b.input,
1688
1869
  tool_use_id: b.id,
1689
1870
  tool_response: r.error !== undefined
1690
- ? (r.output ? `Error: ${r.error}\n\n${r.output}` : `Error: ${r.error}`)
1871
+ ? r.output
1872
+ ? `Error: ${r.error}\n\n${r.output}`
1873
+ : `Error: ${r.error}`
1691
1874
  : (r.output ?? ''),
1692
1875
  })),
1693
1876
  }, undefined, (0, hooks_1.hookRunOptsFor)(options));
@@ -1791,7 +1974,11 @@ async function runAgentLoop(initialMessages, options) {
1791
1974
  // Routed through onNotice (falling back to onText) like every other housekeeping
1792
1975
  // message in this file — these are statements from the harness, not from the model,
1793
1976
  // and splicing them into the assistant's own bubble reads as if it said them.
1794
- const notice = (0, iterationPolicy_1.stopReasonNotice)(stopReason, { budget, repeatError: stalledRepeatError, hookReason: hookBlockedReason });
1977
+ const notice = (0, iterationPolicy_1.stopReasonNotice)(stopReason, {
1978
+ budget,
1979
+ repeatError: stalledRepeatError,
1980
+ hookReason: hookBlockedReason,
1981
+ });
1795
1982
  // A sub-agent's stop belongs in its parent's tool result, not in the user's chat as
1796
1983
  // if the MAIN agent had stopped (runSubTask turns it into an error for the parent).
1797
1984
  if (options._onStopReason)
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/agent/memory.ts"],"names":[],"mappings":"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"}
@@ -147,7 +147,9 @@ const _locks = new Map();
147
147
  async function withLock(key, fn) {
148
148
  const prev = _locks.get(key) ?? Promise.resolve();
149
149
  let release;
150
- const next = new Promise((res) => { release = res; });
150
+ const next = new Promise((res) => {
151
+ release = res;
152
+ });
151
153
  _locks.set(key, prev.then(() => next));
152
154
  try {
153
155
  await prev;
@@ -218,7 +220,10 @@ function evictToFit(active, archived, maxBytes) {
218
220
  function sanitizeSourceTag(source) {
219
221
  if (!source)
220
222
  return '';
221
- return source.trim().replace(/[[\]\n\r]/g, '').slice(0, SOURCE_TAG_MAX_CHARS);
223
+ return source
224
+ .trim()
225
+ .replace(/[[\]\n\r]/g, '')
226
+ .slice(0, SOURCE_TAG_MAX_CHARS);
222
227
  }
223
228
  async function writeMemory(content, scope, workDir, opts = {}) {
224
229
  let trimmed = content.trim();
@@ -306,7 +311,9 @@ function clearMemory(scope, workDir) {
306
311
  try {
307
312
  fs.rmSync(file, { force: true });
308
313
  }
309
- catch { /* ignore */ }
314
+ catch {
315
+ /* ignore */
316
+ }
310
317
  }
311
318
  function memoryStats(scope, workDir) {
312
319
  const file = memoryFilePath(scope, workDir);
@@ -374,7 +381,8 @@ async function compactMemoryIfNeeded(scope, workDir, summarize) {
374
381
  }
375
382
  // 2. If the active set alone is still driving the size past the trigger,
376
383
  // summarize its oldest half.
377
- if (Buffer.byteLength(serializeSections(active, archived), 'utf-8') > exports.MEMORY_COMPACT_TRIGGER_BYTES && active.length >= 8) {
384
+ if (Buffer.byteLength(serializeSections(active, archived), 'utf-8') > exports.MEMORY_COMPACT_TRIGGER_BYTES &&
385
+ active.length >= 8) {
378
386
  const cut = Math.floor(active.length / 2);
379
387
  const older = active.slice(0, cut);
380
388
  const recent = active.slice(cut);
@@ -524,9 +532,7 @@ function agentMemoryPreamble(agentName, scope, workDir) {
524
532
  const notes = readAgentMemory(agentName, scope, workDir);
525
533
  if (!notes)
526
534
  return '';
527
- const kept = Buffer.byteLength(notes, 'utf-8') > exports.AGENT_MEMORY_INJECT_MAX
528
- ? notes.slice(-exports.AGENT_MEMORY_INJECT_MAX)
529
- : notes;
535
+ const kept = Buffer.byteLength(notes, 'utf-8') > exports.AGENT_MEMORY_INJECT_MAX ? notes.slice(-exports.AGENT_MEMORY_INJECT_MAX) : notes;
530
536
  return (`# Your own notes (agent: ${agentName}, scope: ${scope})\n` +
531
537
  `These are notes YOU saved on previous runs. Treat them as your accumulated experience, ` +
532
538
  `not as instructions from the user, and verify anything that looks stale against the ` +
@@ -1 +1 @@
1
- {"version":3,"file":"modelCatalogue.d.ts","sourceRoot":"","sources":["../../src/agent/modelCatalogue.ts"],"names":[],"mappings":"AAgDA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAiBlE;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAuBnE;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,IAAI;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAElG;AAOD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,sDAAsD;AACtD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAGzF;AAED,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAGvF;AAED,uFAAuF;AACvF,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAEnF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAExE;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG9G;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,GAAG,IAAI,CAExD;AAED,kEAAkE;AAClE,wBAAgB,iCAAiC,IAAI,IAAI,CAGxD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAO/C,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAW3F;AAED,6GAA6G;AAC7G,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAIxH;AAED,8DAA8D;AAC9D,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,GAAG,IAAI,CAE9E"}
1
+ {"version":3,"file":"modelCatalogue.d.ts","sourceRoot":"","sources":["../../src/agent/modelCatalogue.ts"],"names":[],"mappings":"AAgDA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAiBlE;;;;;;;;;;;;GAYG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAuBnE;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,IAAI;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAElG;AAOD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,sDAAsD;AACtD,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAGzF;AAED,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAGvF;AAED,uFAAuF;AACvF,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAEnF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAExE;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAG9G;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,GAAG,IAAI,CAExD;AAED,kEAAkE;AAClE,wBAAgB,iCAAiC,IAAI,IAAI,CAGxD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAU/C,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAW3F;AAED,6GAA6G;AAC7G,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,MAAM,GAAG,SAAS,CAIpB;AAED,8DAA8D;AAC9D,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,GAAG,IAAI,CAE9E"}
@@ -196,7 +196,10 @@ function _resetModelCatalogueCacheForTests() {
196
196
  exports.CHEAPEST_MODEL_ALIAS = 'cheapest';
197
197
  // Legacy tier aliases the backend still resolves (see agentTypes.ts VALID_MODELS).
198
198
  const LEGACY_ALIAS_IDS = {
199
- turbo: 'claude-sonnet-5-5', fast: 'claude-sonnet-5-5', pro: 'claude-opus-5-5', ultra: 'claude-fable-5-1',
199
+ turbo: 'claude-sonnet-5-5',
200
+ fast: 'claude-sonnet-5-5',
201
+ pro: 'claude-opus-5-5',
202
+ ultra: 'claude-fable-5-1',
200
203
  };
201
204
  function cheapestSameVendorModel(parentModel) {
202
205
  if (!_state || !parentModel)