@ashx-j/lunr-dev 0.2.13-dev.4.1 → 0.2.13-dev.7.1

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 (208) hide show
  1. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  2. package/dist/builtin-extensions/ashxj-tui.js +4 -4
  3. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  4. package/dist/builtin-extensions/index.d.ts.map +1 -1
  5. package/dist/builtin-extensions/index.js +2 -0
  6. package/dist/builtin-extensions/index.js.map +1 -1
  7. package/dist/builtin-extensions/lunr-settings-tools.d.ts +4 -0
  8. package/dist/builtin-extensions/lunr-settings-tools.d.ts.map +1 -0
  9. package/dist/builtin-extensions/lunr-settings-tools.js +120 -0
  10. package/dist/builtin-extensions/lunr-settings-tools.js.map +1 -0
  11. package/dist/builtin-extensions/pi-prompt-template-model/subagent-runtime.d.ts +2 -2
  12. package/dist/builtin-extensions/pi-prompt-template-model/subagent-runtime.d.ts.map +1 -1
  13. package/dist/builtin-extensions/pi-prompt-template-model/subagent-runtime.js.map +1 -1
  14. package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.d.ts.map +1 -1
  15. package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.js +3 -3
  16. package/dist/builtin-extensions/pi-prompt-template-model/subagent-step.js.map +1 -1
  17. package/dist/builtin-extensions/pi-subagents/src/api/delegation.d.ts +2 -2
  18. package/dist/builtin-extensions/pi-subagents/src/api/delegation.d.ts.map +1 -1
  19. package/dist/builtin-extensions/pi-subagents/src/api/delegation.js +1 -1
  20. package/dist/builtin-extensions/pi-subagents/src/api/delegation.js.map +1 -1
  21. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  22. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +2 -1
  23. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  24. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts +5 -11
  25. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
  26. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +12 -15
  27. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
  28. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +3 -3
  29. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
  30. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +14 -27
  31. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
  32. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
  33. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +6 -4
  34. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
  35. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.d.ts.map +1 -1
  36. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js +2 -1
  37. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js.map +1 -1
  38. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.d.ts.map +1 -1
  39. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js +6 -4
  40. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-resume.js.map +1 -1
  41. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.d.ts.map +1 -1
  42. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.js +4 -2
  43. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-status.js.map +1 -1
  44. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.d.ts.map +1 -1
  45. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.js +3 -35
  46. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-clarify.js.map +1 -1
  47. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
  48. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +5 -12
  49. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
  50. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  51. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +14 -31
  52. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  53. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +2 -0
  54. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
  55. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +28 -22
  56. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
  57. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +1 -0
  58. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  59. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  60. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.d.ts.map +1 -1
  61. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js +9 -4
  62. package/dist/builtin-extensions/pi-subagents/src/shared/child-spec.js.map +1 -1
  63. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts +1 -1
  64. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.d.ts.map +1 -1
  65. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js +2 -2
  66. package/dist/builtin-extensions/pi-subagents/src/shared/formatters.js.map +1 -1
  67. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +4 -7
  68. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  69. package/dist/builtin-extensions/pi-subagents/src/shared/types.js +1 -1
  70. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  71. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.d.ts +3 -3
  72. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.d.ts.map +1 -1
  73. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.js +7 -6
  74. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.js.map +1 -1
  75. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-request.d.ts.map +1 -1
  76. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-request.js +3 -3
  77. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-request.js.map +1 -1
  78. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts +1 -1
  79. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
  80. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +6 -4
  81. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
  82. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +1 -1
  83. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
  84. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +15 -10
  85. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
  86. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.d.ts.map +1 -1
  87. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js +3 -2
  88. package/dist/builtin-extensions/pi-subagents/src/tui/fleet.js.map +1 -1
  89. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +2 -2
  90. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  91. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +75 -43
  92. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  93. package/dist/catalog/models.json +1 -1
  94. package/dist/catalog/providers/amazon-bedrock.json +1 -1
  95. package/dist/catalog/providers/anthropic.json +1 -1
  96. package/dist/catalog/providers/cloudflare-ai-gateway.json +1 -1
  97. package/dist/catalog/providers/fireworks.json +1 -1
  98. package/dist/catalog/providers/github-copilot.json +1 -1
  99. package/dist/catalog/providers/google-vertex.json +1 -1
  100. package/dist/catalog/providers/google.json +1 -1
  101. package/dist/catalog/providers/huggingface.json +1 -1
  102. package/dist/catalog/providers/nvidia.json +1 -1
  103. package/dist/catalog/providers/opencode-go.json +1 -1
  104. package/dist/catalog/providers/opencode.json +1 -1
  105. package/dist/catalog/providers/openrouter.json +1 -1
  106. package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
  107. package/dist/catalog/publication.json +3 -3
  108. package/dist/core/agent-session-runtime.d.ts +5 -1
  109. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  110. package/dist/core/agent-session-runtime.js +7 -2
  111. package/dist/core/agent-session-runtime.js.map +1 -1
  112. package/dist/core/agent-session.d.ts +2 -0
  113. package/dist/core/agent-session.d.ts.map +1 -1
  114. package/dist/core/agent-session.js +20 -5
  115. package/dist/core/agent-session.js.map +1 -1
  116. package/dist/core/context-breakdown.d.ts +2 -0
  117. package/dist/core/context-breakdown.d.ts.map +1 -1
  118. package/dist/core/context-breakdown.js +6 -2
  119. package/dist/core/context-breakdown.js.map +1 -1
  120. package/dist/core/injected-prompt.d.ts +3 -3
  121. package/dist/core/injected-prompt.d.ts.map +1 -1
  122. package/dist/core/injected-prompt.js +2 -7
  123. package/dist/core/injected-prompt.js.map +1 -1
  124. package/dist/core/large-subagent-launch.d.ts +13 -0
  125. package/dist/core/large-subagent-launch.d.ts.map +1 -0
  126. package/dist/core/large-subagent-launch.js +54 -0
  127. package/dist/core/large-subagent-launch.js.map +1 -0
  128. package/dist/core/model-instructions.d.ts +24 -0
  129. package/dist/core/model-instructions.d.ts.map +1 -0
  130. package/dist/core/model-instructions.js +123 -0
  131. package/dist/core/model-instructions.js.map +1 -0
  132. package/dist/core/permissions.d.ts +11 -10
  133. package/dist/core/permissions.d.ts.map +1 -1
  134. package/dist/core/permissions.js +61 -45
  135. package/dist/core/permissions.js.map +1 -1
  136. package/dist/core/plan-mode.d.ts.map +1 -1
  137. package/dist/core/plan-mode.js +10 -1
  138. package/dist/core/plan-mode.js.map +1 -1
  139. package/dist/core/resource-loader.d.ts +3 -0
  140. package/dist/core/resource-loader.d.ts.map +1 -1
  141. package/dist/core/resource-loader.js +13 -4
  142. package/dist/core/resource-loader.js.map +1 -1
  143. package/dist/core/runtime-bridges.d.ts +4 -0
  144. package/dist/core/runtime-bridges.d.ts.map +1 -0
  145. package/dist/core/runtime-bridges.js +16 -0
  146. package/dist/core/runtime-bridges.js.map +1 -0
  147. package/dist/core/sdk.d.ts.map +1 -1
  148. package/dist/core/sdk.js +1 -0
  149. package/dist/core/sdk.js.map +1 -1
  150. package/dist/core/session-manager.d.ts.map +1 -1
  151. package/dist/core/session-manager.js +17 -16
  152. package/dist/core/session-manager.js.map +1 -1
  153. package/dist/core/settings-manager.d.ts +13 -0
  154. package/dist/core/settings-manager.d.ts.map +1 -1
  155. package/dist/core/settings-manager.js +32 -0
  156. package/dist/core/settings-manager.js.map +1 -1
  157. package/dist/core/settings-tools-bridge.d.ts +35 -0
  158. package/dist/core/settings-tools-bridge.d.ts.map +1 -0
  159. package/dist/core/settings-tools-bridge.js +56 -0
  160. package/dist/core/settings-tools-bridge.js.map +1 -0
  161. package/dist/core/swarm.d.ts +0 -31
  162. package/dist/core/swarm.d.ts.map +1 -1
  163. package/dist/core/swarm.js +2 -76
  164. package/dist/core/swarm.js.map +1 -1
  165. package/dist/core/system-prompt.d.ts.map +1 -1
  166. package/dist/core/system-prompt.js +1 -1
  167. package/dist/core/system-prompt.js.map +1 -1
  168. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  169. package/dist/gateway/agent-bridge.js +3 -2
  170. package/dist/gateway/agent-bridge.js.map +1 -1
  171. package/dist/gateway/approval.d.ts.map +1 -1
  172. package/dist/gateway/approval.js +4 -3
  173. package/dist/gateway/approval.js.map +1 -1
  174. package/dist/gateway/cron.d.ts.map +1 -1
  175. package/dist/gateway/cron.js +3 -2
  176. package/dist/gateway/cron.js.map +1 -1
  177. package/dist/gateway/router.d.ts +1 -1
  178. package/dist/gateway/router.d.ts.map +1 -1
  179. package/dist/gateway/router.js +1 -1
  180. package/dist/gateway/router.js.map +1 -1
  181. package/dist/main.d.ts.map +1 -1
  182. package/dist/main.js +3 -9
  183. package/dist/main.js.map +1 -1
  184. package/dist/migrations.d.ts.map +1 -1
  185. package/dist/migrations.js +4 -0
  186. package/dist/migrations.js.map +1 -1
  187. package/dist/modes/interactive/components/injected-prompt-message.d.ts +1 -1
  188. package/dist/modes/interactive/components/injected-prompt-message.d.ts.map +1 -1
  189. package/dist/modes/interactive/components/injected-prompt-message.js +1 -1
  190. package/dist/modes/interactive/components/injected-prompt-message.js.map +1 -1
  191. package/dist/modes/interactive/components/settings-selector.d.ts +8 -1
  192. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  193. package/dist/modes/interactive/components/settings-selector.js +61 -1
  194. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  195. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  196. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  197. package/dist/modes/interactive/interactive-mode.js +31 -23
  198. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  199. package/dist/startup/interactive-shell.d.ts.map +1 -1
  200. package/dist/startup/interactive-shell.js +2 -1
  201. package/dist/startup/interactive-shell.js.map +1 -1
  202. package/docs/features.md +7 -6
  203. package/docs/quickstart.md +3 -2
  204. package/docs/settings.md +3 -1
  205. package/docs/termux.md +1 -1
  206. package/docs/usage.md +2 -1
  207. package/examples/extensions/subagent/README.md +1 -1
  208. package/package.json +4 -4
@@ -23,6 +23,8 @@ export interface ContextBreakdownInput {
23
23
  contextWindow: number;
24
24
  /** Absolute path of the user-level AGENTS.md, used only to distinguish its UI label. */
25
25
  globalAgentsPath?: string;
26
+ /** Absolute path of the selected model's AGENTS.md, used only for its UI label. */
27
+ modelAgentsPath?: string;
26
28
  }
27
29
  export interface ContextFileBreakdown {
28
30
  /** Display label for the context file (AGENTS.md, Global AGENTS.md, CLAUDE.md, …). */
@@ -1 +1 @@
1
- {"version":3,"file":"context-breakdown.d.ts","sourceRoot":"","sources":["../../src/core/context-breakdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAC3C,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACpC,wFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,6EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAChC,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,MAAM,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KAClB,CAAC;CACF;AAsBD,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,MAAM,EAAE,MAAM,CAAC;CACf,CAsBA;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,aAAa,CAAC,oBAAoB,CAAC,GAAG,MAAM,CAY/F;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,qBAAqB,GAAG,gBAAgB,CAgGtF","sourcesContent":["/**\n * lunr: Context window breakdown estimation for the /context command.\n *\n * Pure functions that estimate (chars/4, same heuristic as `estimateTokens` in\n * compaction.ts) what consumes the model's context window: the system prompt\n * (project context files are baked into it), the registered tool definitions,\n * and the live session messages split into user / assistant text / thinking /\n * tool calls / tool results / summaries.\n *\n * Pass the same message list the context actually contains (post-compaction,\n * that is `AgentSession.messages`) so the breakdown matches reality.\n */\n\nimport type { AgentMessage } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { estimateTokens } from \"./compaction/index.ts\";\n\nexport interface ContextBreakdownTool {\n\tname: string;\n\tdescription: string;\n\tparameters: unknown;\n}\n\nexport interface ContextBreakdownInput {\n\tsystemPrompt: string;\n\ttools: ReadonlyArray<ContextBreakdownTool>;\n\tmessages: ReadonlyArray<AgentMessage>;\n\tcontextWindow: number;\n\t/** Absolute path of the user-level AGENTS.md, used only to distinguish its UI label. */\n\tglobalAgentsPath?: string;\n}\n\nexport interface ContextFileBreakdown {\n\t/** Display label for the context file (AGENTS.md, Global AGENTS.md, CLAUDE.md, …). */\n\tlabel: string;\n\t/** Path from the <project_instructions path=\"…\"> wrapper, when present. */\n\tpath?: string;\n\ttokens: number;\n}\n\nexport interface ContextBreakdown {\n\t/** System prompt minus <project_context> and the skills block. */\n\tsystemPrompt: number;\n\t/** Each AGENTS.md / context file from <project_instructions>. */\n\tcontextFiles: ContextFileBreakdown[];\n\t/** formatSkillsForPrompt section, if present. */\n\tskills: number;\n\t/** Serialized tool schemas (name + description + JSON parameter schema). */\n\ttoolDefinitions: number;\n\t/** User and extension (\"custom\") messages. */\n\tuser: number;\n\t/** Assistant text blocks. */\n\tassistantText: number;\n\t/** Assistant thinking blocks. */\n\tthinking: number;\n\t/** Assistant tool call blocks (name + serialized arguments). */\n\ttoolCalls: number;\n\t/** Tool result messages and bash execution output. */\n\ttoolResults: number;\n\t/** Compaction and branch summaries. */\n\tsummaries: number;\n\t/** Sum of all categories above. */\n\ttotal: number;\n\tcontextWindow: number;\n\t/** max(0, contextWindow - total) */\n\tfree: number;\n\t/** Per-category message counts (not token counts). */\n\tcounts: {\n\t\tuser: number;\n\t\tassistantText: number;\n\t\tthinking: number;\n\t\ttoolCalls: number;\n\t\ttoolResults: number;\n\t\tsummaries: number;\n\t};\n}\n\nconst CHARS_PER_TOKEN = 4;\nconst PROJECT_CONTEXT_RE = /<project_context>[\\s\\S]*?<\\/project_context>/;\nconst PROJECT_INSTRUCTIONS_RE = /<project_instructions(?:\\s+path=\"([^\"]*)\")?>([\\s\\S]*?)<\\/project_instructions>/g;\nconst SKILLS_BLOCK_RE =\n\t/\\n\\nThe following skills provide specialized instructions for specific tasks\\.[\\s\\S]*<\\/available_skills>/;\n\nfunction estimateChars(text: string): number {\n\treturn text.length === 0 ? 0 : Math.ceil(text.length / CHARS_PER_TOKEN);\n}\n\nfunction contextFileLabel(filePath: string | undefined, globalAgentsPath: string | undefined): string {\n\tif (!filePath) return \"AGENTS.md\";\n\tconst normalized = filePath.replace(/\\\\/g, \"/\");\n\tif (globalAgentsPath && normalized.toLowerCase() === globalAgentsPath.replace(/\\\\/g, \"/\").toLowerCase()) {\n\t\treturn \"Global AGENTS.md\";\n\t}\n\tconst slash = normalized.lastIndexOf(\"/\");\n\treturn slash === -1 ? normalized : normalized.slice(slash + 1);\n}\n\n/** Split a live system prompt into base / context-file / skills parts. */\nexport function splitSystemPromptSections(systemPrompt: string): {\n\tbase: string;\n\tcontextFiles: Array<{ path?: string; content: string }>;\n\tskills: string;\n} {\n\tlet remaining = systemPrompt;\n\tconst contextFiles: Array<{ path?: string; content: string }> = [];\n\tconst contextMatch = remaining.match(PROJECT_CONTEXT_RE);\n\tif (contextMatch) {\n\t\tconst block = contextMatch[0];\n\t\tPROJECT_INSTRUCTIONS_RE.lastIndex = 0;\n\t\tfor (const match of block.matchAll(PROJECT_INSTRUCTIONS_RE)) {\n\t\t\tcontextFiles.push({\n\t\t\t\tpath: match[1],\n\t\t\t\tcontent: match[2] ?? \"\",\n\t\t\t});\n\t\t}\n\t\tremaining = remaining.replace(block, \"\");\n\t}\n\tlet skills = \"\";\n\tconst skillsMatch = remaining.match(SKILLS_BLOCK_RE);\n\tif (skillsMatch) {\n\t\tskills = skillsMatch[0];\n\t\tremaining = remaining.replace(skills, \"\");\n\t}\n\treturn { base: remaining, contextFiles, skills };\n}\n\n/**\n * Estimate the token cost of the tool definitions sent to the model.\n * The wire format is roughly `{ name, description, parameters(JSON schema) }` per tool.\n */\nexport function estimateToolDefinitionTokens(tools: ReadonlyArray<ContextBreakdownTool>): number {\n\tlet chars = 0;\n\tfor (const tool of tools) {\n\t\tchars += tool.name.length + tool.description.length;\n\t\ttry {\n\t\t\tconst schema = JSON.stringify(tool.parameters);\n\t\t\tif (schema) chars += schema.length;\n\t\t} catch {\n\t\t\t// Unserializable schema — skip rather than fail the whole breakdown.\n\t\t}\n\t}\n\treturn Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\n/**\n * Compute the estimated context window breakdown.\n * All numbers are chars/4 estimates, matching `estimateTokens`.\n */\nexport function computeContextBreakdown(input: ContextBreakdownInput): ContextBreakdown {\n\tconst sections = splitSystemPromptSections(input.systemPrompt);\n\tconst systemPrompt = estimateChars(sections.base);\n\tconst contextFiles = sections.contextFiles.map((file) => ({\n\t\tlabel: contextFileLabel(file.path, input.globalAgentsPath),\n\t\tpath: file.path,\n\t\ttokens: estimateChars(file.content),\n\t}));\n\tconst skills = estimateChars(sections.skills);\n\tconst toolDefinitions = estimateToolDefinitionTokens(input.tools);\n\n\tlet user = 0;\n\tlet assistantText = 0;\n\tlet thinking = 0;\n\tlet toolCalls = 0;\n\tlet toolResults = 0;\n\tlet summaries = 0;\n\n\tconst counts = {\n\t\tuser: 0,\n\t\tassistantText: 0,\n\t\tthinking: 0,\n\t\ttoolCalls: 0,\n\t\ttoolResults: 0,\n\t\tsummaries: 0,\n\t};\n\n\tfor (const message of input.messages) {\n\t\tswitch (message.role) {\n\t\t\tcase \"assistant\": {\n\t\t\t\tlet textChars = 0;\n\t\t\t\tlet thinkingChars = 0;\n\t\t\t\tlet toolCallChars = 0;\n\t\t\t\tfor (const block of (message as AssistantMessage).content) {\n\t\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\t\ttextChars += block.text.length;\n\t\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\t\tthinkingChars += block.thinking.length;\n\t\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\t\ttoolCallChars += block.name.length + JSON.stringify(block.arguments).length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassistantText += Math.ceil(textChars / CHARS_PER_TOKEN);\n\t\t\t\tthinking += Math.ceil(thinkingChars / CHARS_PER_TOKEN);\n\t\t\t\ttoolCalls += Math.ceil(toolCallChars / CHARS_PER_TOKEN);\n\t\t\t\tif (textChars > 0) counts.assistantText++;\n\t\t\t\tif (thinkingChars > 0) counts.thinking++;\n\t\t\t\tif (toolCallChars > 0) counts.toolCalls++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"user\":\n\t\t\tcase \"custom\":\n\t\t\t\tuser += estimateTokens(message);\n\t\t\t\tcounts.user++;\n\t\t\t\tbreak;\n\t\t\tcase \"toolResult\":\n\t\t\tcase \"bashExecution\":\n\t\t\t\ttoolResults += estimateTokens(message);\n\t\t\t\tcounts.toolResults++;\n\t\t\t\tbreak;\n\t\t\tcase \"branchSummary\":\n\t\t\tcase \"compactionSummary\":\n\t\t\t\tsummaries += estimateTokens(message);\n\t\t\t\tcounts.summaries++;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst contextFileTokens = contextFiles.reduce((sum, file) => sum + file.tokens, 0);\n\tconst total =\n\t\tsystemPrompt +\n\t\tcontextFileTokens +\n\t\tskills +\n\t\ttoolDefinitions +\n\t\tuser +\n\t\tassistantText +\n\t\tthinking +\n\t\ttoolCalls +\n\t\ttoolResults +\n\t\tsummaries;\n\treturn {\n\t\tsystemPrompt,\n\t\tcontextFiles,\n\t\tskills,\n\t\ttoolDefinitions,\n\t\tuser,\n\t\tassistantText,\n\t\tthinking,\n\t\ttoolCalls,\n\t\ttoolResults,\n\t\tsummaries,\n\t\ttotal,\n\t\tcontextWindow: input.contextWindow,\n\t\tfree: Math.max(0, input.contextWindow - total),\n\t\tcounts,\n\t};\n}\n"]}
1
+ {"version":3,"file":"context-breakdown.d.ts","sourceRoot":"","sources":["../../src/core/context-breakdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IAC3C,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACpC,wFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,6EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAChC,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,YAAY,EAAE,oBAAoB,EAAE,CAAC;IACrC,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,MAAM,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KAClB,CAAC;CACF;AA8BD,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,MAAM,EAAE,MAAM,CAAC;CACf,CAsBA;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,aAAa,CAAC,oBAAoB,CAAC,GAAG,MAAM,CAY/F;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,qBAAqB,GAAG,gBAAgB,CAgGtF","sourcesContent":["/**\n * lunr: Context window breakdown estimation for the /context command.\n *\n * Pure functions that estimate (chars/4, same heuristic as `estimateTokens` in\n * compaction.ts) what consumes the model's context window: the system prompt\n * (project context files are baked into it), the registered tool definitions,\n * and the live session messages split into user / assistant text / thinking /\n * tool calls / tool results / summaries.\n *\n * Pass the same message list the context actually contains (post-compaction,\n * that is `AgentSession.messages`) so the breakdown matches reality.\n */\n\nimport type { AgentMessage } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { estimateTokens } from \"./compaction/index.ts\";\n\nexport interface ContextBreakdownTool {\n\tname: string;\n\tdescription: string;\n\tparameters: unknown;\n}\n\nexport interface ContextBreakdownInput {\n\tsystemPrompt: string;\n\ttools: ReadonlyArray<ContextBreakdownTool>;\n\tmessages: ReadonlyArray<AgentMessage>;\n\tcontextWindow: number;\n\t/** Absolute path of the user-level AGENTS.md, used only to distinguish its UI label. */\n\tglobalAgentsPath?: string;\n\t/** Absolute path of the selected model's AGENTS.md, used only for its UI label. */\n\tmodelAgentsPath?: string;\n}\n\nexport interface ContextFileBreakdown {\n\t/** Display label for the context file (AGENTS.md, Global AGENTS.md, CLAUDE.md, …). */\n\tlabel: string;\n\t/** Path from the <project_instructions path=\"…\"> wrapper, when present. */\n\tpath?: string;\n\ttokens: number;\n}\n\nexport interface ContextBreakdown {\n\t/** System prompt minus <project_context> and the skills block. */\n\tsystemPrompt: number;\n\t/** Each AGENTS.md / context file from <project_instructions>. */\n\tcontextFiles: ContextFileBreakdown[];\n\t/** formatSkillsForPrompt section, if present. */\n\tskills: number;\n\t/** Serialized tool schemas (name + description + JSON parameter schema). */\n\ttoolDefinitions: number;\n\t/** User and extension (\"custom\") messages. */\n\tuser: number;\n\t/** Assistant text blocks. */\n\tassistantText: number;\n\t/** Assistant thinking blocks. */\n\tthinking: number;\n\t/** Assistant tool call blocks (name + serialized arguments). */\n\ttoolCalls: number;\n\t/** Tool result messages and bash execution output. */\n\ttoolResults: number;\n\t/** Compaction and branch summaries. */\n\tsummaries: number;\n\t/** Sum of all categories above. */\n\ttotal: number;\n\tcontextWindow: number;\n\t/** max(0, contextWindow - total) */\n\tfree: number;\n\t/** Per-category message counts (not token counts). */\n\tcounts: {\n\t\tuser: number;\n\t\tassistantText: number;\n\t\tthinking: number;\n\t\ttoolCalls: number;\n\t\ttoolResults: number;\n\t\tsummaries: number;\n\t};\n}\n\nconst CHARS_PER_TOKEN = 4;\nconst PROJECT_CONTEXT_RE = /<project_context>[\\s\\S]*?<\\/project_context>/;\nconst PROJECT_INSTRUCTIONS_RE = /<project_instructions(?:\\s+path=\"([^\"]*)\")?>([\\s\\S]*?)<\\/project_instructions>/g;\nconst SKILLS_BLOCK_RE =\n\t/\\n\\nThe following skills provide specialized instructions for specific tasks\\.[\\s\\S]*<\\/available_skills>/;\n\nfunction estimateChars(text: string): number {\n\treturn text.length === 0 ? 0 : Math.ceil(text.length / CHARS_PER_TOKEN);\n}\n\nfunction contextFileLabel(\n\tfilePath: string | undefined,\n\tglobalAgentsPath: string | undefined,\n\tmodelAgentsPath: string | undefined,\n): string {\n\tif (!filePath) return \"AGENTS.md\";\n\tconst normalized = filePath.replace(/\\\\/g, \"/\");\n\tif (globalAgentsPath && normalized.toLowerCase() === globalAgentsPath.replace(/\\\\/g, \"/\").toLowerCase()) {\n\t\treturn \"Global AGENTS.md\";\n\t}\n\tif (modelAgentsPath && normalized.toLowerCase() === modelAgentsPath.replace(/\\\\/g, \"/\").toLowerCase()) {\n\t\tconst parts = normalized.split(\"/\");\n\t\treturn `${parts.at(-2) ?? \"Model\"} AGENTS.md`;\n\t}\n\tconst slash = normalized.lastIndexOf(\"/\");\n\treturn slash === -1 ? normalized : normalized.slice(slash + 1);\n}\n\n/** Split a live system prompt into base / context-file / skills parts. */\nexport function splitSystemPromptSections(systemPrompt: string): {\n\tbase: string;\n\tcontextFiles: Array<{ path?: string; content: string }>;\n\tskills: string;\n} {\n\tlet remaining = systemPrompt;\n\tconst contextFiles: Array<{ path?: string; content: string }> = [];\n\tconst contextMatch = remaining.match(PROJECT_CONTEXT_RE);\n\tif (contextMatch) {\n\t\tconst block = contextMatch[0];\n\t\tPROJECT_INSTRUCTIONS_RE.lastIndex = 0;\n\t\tfor (const match of block.matchAll(PROJECT_INSTRUCTIONS_RE)) {\n\t\t\tcontextFiles.push({\n\t\t\t\tpath: match[1],\n\t\t\t\tcontent: match[2] ?? \"\",\n\t\t\t});\n\t\t}\n\t\tremaining = remaining.replace(block, \"\");\n\t}\n\tlet skills = \"\";\n\tconst skillsMatch = remaining.match(SKILLS_BLOCK_RE);\n\tif (skillsMatch) {\n\t\tskills = skillsMatch[0];\n\t\tremaining = remaining.replace(skills, \"\");\n\t}\n\treturn { base: remaining, contextFiles, skills };\n}\n\n/**\n * Estimate the token cost of the tool definitions sent to the model.\n * The wire format is roughly `{ name, description, parameters(JSON schema) }` per tool.\n */\nexport function estimateToolDefinitionTokens(tools: ReadonlyArray<ContextBreakdownTool>): number {\n\tlet chars = 0;\n\tfor (const tool of tools) {\n\t\tchars += tool.name.length + tool.description.length;\n\t\ttry {\n\t\t\tconst schema = JSON.stringify(tool.parameters);\n\t\t\tif (schema) chars += schema.length;\n\t\t} catch {\n\t\t\t// Unserializable schema — skip rather than fail the whole breakdown.\n\t\t}\n\t}\n\treturn Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\n/**\n * Compute the estimated context window breakdown.\n * All numbers are chars/4 estimates, matching `estimateTokens`.\n */\nexport function computeContextBreakdown(input: ContextBreakdownInput): ContextBreakdown {\n\tconst sections = splitSystemPromptSections(input.systemPrompt);\n\tconst systemPrompt = estimateChars(sections.base);\n\tconst contextFiles = sections.contextFiles.map((file) => ({\n\t\tlabel: contextFileLabel(file.path, input.globalAgentsPath, input.modelAgentsPath),\n\t\tpath: file.path,\n\t\ttokens: estimateChars(file.content),\n\t}));\n\tconst skills = estimateChars(sections.skills);\n\tconst toolDefinitions = estimateToolDefinitionTokens(input.tools);\n\n\tlet user = 0;\n\tlet assistantText = 0;\n\tlet thinking = 0;\n\tlet toolCalls = 0;\n\tlet toolResults = 0;\n\tlet summaries = 0;\n\n\tconst counts = {\n\t\tuser: 0,\n\t\tassistantText: 0,\n\t\tthinking: 0,\n\t\ttoolCalls: 0,\n\t\ttoolResults: 0,\n\t\tsummaries: 0,\n\t};\n\n\tfor (const message of input.messages) {\n\t\tswitch (message.role) {\n\t\t\tcase \"assistant\": {\n\t\t\t\tlet textChars = 0;\n\t\t\t\tlet thinkingChars = 0;\n\t\t\t\tlet toolCallChars = 0;\n\t\t\t\tfor (const block of (message as AssistantMessage).content) {\n\t\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\t\ttextChars += block.text.length;\n\t\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\t\tthinkingChars += block.thinking.length;\n\t\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\t\ttoolCallChars += block.name.length + JSON.stringify(block.arguments).length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassistantText += Math.ceil(textChars / CHARS_PER_TOKEN);\n\t\t\t\tthinking += Math.ceil(thinkingChars / CHARS_PER_TOKEN);\n\t\t\t\ttoolCalls += Math.ceil(toolCallChars / CHARS_PER_TOKEN);\n\t\t\t\tif (textChars > 0) counts.assistantText++;\n\t\t\t\tif (thinkingChars > 0) counts.thinking++;\n\t\t\t\tif (toolCallChars > 0) counts.toolCalls++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"user\":\n\t\t\tcase \"custom\":\n\t\t\t\tuser += estimateTokens(message);\n\t\t\t\tcounts.user++;\n\t\t\t\tbreak;\n\t\t\tcase \"toolResult\":\n\t\t\tcase \"bashExecution\":\n\t\t\t\ttoolResults += estimateTokens(message);\n\t\t\t\tcounts.toolResults++;\n\t\t\t\tbreak;\n\t\t\tcase \"branchSummary\":\n\t\t\tcase \"compactionSummary\":\n\t\t\t\tsummaries += estimateTokens(message);\n\t\t\t\tcounts.summaries++;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst contextFileTokens = contextFiles.reduce((sum, file) => sum + file.tokens, 0);\n\tconst total =\n\t\tsystemPrompt +\n\t\tcontextFileTokens +\n\t\tskills +\n\t\ttoolDefinitions +\n\t\tuser +\n\t\tassistantText +\n\t\tthinking +\n\t\ttoolCalls +\n\t\ttoolResults +\n\t\tsummaries;\n\treturn {\n\t\tsystemPrompt,\n\t\tcontextFiles,\n\t\tskills,\n\t\ttoolDefinitions,\n\t\tuser,\n\t\tassistantText,\n\t\tthinking,\n\t\ttoolCalls,\n\t\ttoolResults,\n\t\tsummaries,\n\t\ttotal,\n\t\tcontextWindow: input.contextWindow,\n\t\tfree: Math.max(0, input.contextWindow - total),\n\t\tcounts,\n\t};\n}\n"]}
@@ -18,13 +18,17 @@ const SKILLS_BLOCK_RE = /\n\nThe following skills provide specialized instructio
18
18
  function estimateChars(text) {
19
19
  return text.length === 0 ? 0 : Math.ceil(text.length / CHARS_PER_TOKEN);
20
20
  }
21
- function contextFileLabel(filePath, globalAgentsPath) {
21
+ function contextFileLabel(filePath, globalAgentsPath, modelAgentsPath) {
22
22
  if (!filePath)
23
23
  return "AGENTS.md";
24
24
  const normalized = filePath.replace(/\\/g, "/");
25
25
  if (globalAgentsPath && normalized.toLowerCase() === globalAgentsPath.replace(/\\/g, "/").toLowerCase()) {
26
26
  return "Global AGENTS.md";
27
27
  }
28
+ if (modelAgentsPath && normalized.toLowerCase() === modelAgentsPath.replace(/\\/g, "/").toLowerCase()) {
29
+ const parts = normalized.split("/");
30
+ return `${parts.at(-2) ?? "Model"} AGENTS.md`;
31
+ }
28
32
  const slash = normalized.lastIndexOf("/");
29
33
  return slash === -1 ? normalized : normalized.slice(slash + 1);
30
34
  }
@@ -79,7 +83,7 @@ export function computeContextBreakdown(input) {
79
83
  const sections = splitSystemPromptSections(input.systemPrompt);
80
84
  const systemPrompt = estimateChars(sections.base);
81
85
  const contextFiles = sections.contextFiles.map((file) => ({
82
- label: contextFileLabel(file.path, input.globalAgentsPath),
86
+ label: contextFileLabel(file.path, input.globalAgentsPath, input.modelAgentsPath),
83
87
  path: file.path,
84
88
  tokens: estimateChars(file.content),
85
89
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"context-breakdown.js","sourceRoot":"","sources":["../../src/core/context-breakdown.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunr: Context window breakdown estimation for the /context command.\n *\n * Pure functions that estimate (chars/4, same heuristic as `estimateTokens` in\n * compaction.ts) what consumes the model's context window: the system prompt\n * (project context files are baked into it), the registered tool definitions,\n * and the live session messages split into user / assistant text / thinking /\n * tool calls / tool results / summaries.\n *\n * Pass the same message list the context actually contains (post-compaction,\n * that is `AgentSession.messages`) so the breakdown matches reality.\n */\n\nimport type { AgentMessage } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { estimateTokens } from \"./compaction/index.ts\";\n\nexport interface ContextBreakdownTool {\n\tname: string;\n\tdescription: string;\n\tparameters: unknown;\n}\n\nexport interface ContextBreakdownInput {\n\tsystemPrompt: string;\n\ttools: ReadonlyArray<ContextBreakdownTool>;\n\tmessages: ReadonlyArray<AgentMessage>;\n\tcontextWindow: number;\n\t/** Absolute path of the user-level AGENTS.md, used only to distinguish its UI label. */\n\tglobalAgentsPath?: string;\n}\n\nexport interface ContextFileBreakdown {\n\t/** Display label for the context file (AGENTS.md, Global AGENTS.md, CLAUDE.md, …). */\n\tlabel: string;\n\t/** Path from the <project_instructions path=\"…\"> wrapper, when present. */\n\tpath?: string;\n\ttokens: number;\n}\n\nexport interface ContextBreakdown {\n\t/** System prompt minus <project_context> and the skills block. */\n\tsystemPrompt: number;\n\t/** Each AGENTS.md / context file from <project_instructions>. */\n\tcontextFiles: ContextFileBreakdown[];\n\t/** formatSkillsForPrompt section, if present. */\n\tskills: number;\n\t/** Serialized tool schemas (name + description + JSON parameter schema). */\n\ttoolDefinitions: number;\n\t/** User and extension (\"custom\") messages. */\n\tuser: number;\n\t/** Assistant text blocks. */\n\tassistantText: number;\n\t/** Assistant thinking blocks. */\n\tthinking: number;\n\t/** Assistant tool call blocks (name + serialized arguments). */\n\ttoolCalls: number;\n\t/** Tool result messages and bash execution output. */\n\ttoolResults: number;\n\t/** Compaction and branch summaries. */\n\tsummaries: number;\n\t/** Sum of all categories above. */\n\ttotal: number;\n\tcontextWindow: number;\n\t/** max(0, contextWindow - total) */\n\tfree: number;\n\t/** Per-category message counts (not token counts). */\n\tcounts: {\n\t\tuser: number;\n\t\tassistantText: number;\n\t\tthinking: number;\n\t\ttoolCalls: number;\n\t\ttoolResults: number;\n\t\tsummaries: number;\n\t};\n}\n\nconst CHARS_PER_TOKEN = 4;\nconst PROJECT_CONTEXT_RE = /<project_context>[\\s\\S]*?<\\/project_context>/;\nconst PROJECT_INSTRUCTIONS_RE = /<project_instructions(?:\\s+path=\"([^\"]*)\")?>([\\s\\S]*?)<\\/project_instructions>/g;\nconst SKILLS_BLOCK_RE =\n\t/\\n\\nThe following skills provide specialized instructions for specific tasks\\.[\\s\\S]*<\\/available_skills>/;\n\nfunction estimateChars(text: string): number {\n\treturn text.length === 0 ? 0 : Math.ceil(text.length / CHARS_PER_TOKEN);\n}\n\nfunction contextFileLabel(filePath: string | undefined, globalAgentsPath: string | undefined): string {\n\tif (!filePath) return \"AGENTS.md\";\n\tconst normalized = filePath.replace(/\\\\/g, \"/\");\n\tif (globalAgentsPath && normalized.toLowerCase() === globalAgentsPath.replace(/\\\\/g, \"/\").toLowerCase()) {\n\t\treturn \"Global AGENTS.md\";\n\t}\n\tconst slash = normalized.lastIndexOf(\"/\");\n\treturn slash === -1 ? normalized : normalized.slice(slash + 1);\n}\n\n/** Split a live system prompt into base / context-file / skills parts. */\nexport function splitSystemPromptSections(systemPrompt: string): {\n\tbase: string;\n\tcontextFiles: Array<{ path?: string; content: string }>;\n\tskills: string;\n} {\n\tlet remaining = systemPrompt;\n\tconst contextFiles: Array<{ path?: string; content: string }> = [];\n\tconst contextMatch = remaining.match(PROJECT_CONTEXT_RE);\n\tif (contextMatch) {\n\t\tconst block = contextMatch[0];\n\t\tPROJECT_INSTRUCTIONS_RE.lastIndex = 0;\n\t\tfor (const match of block.matchAll(PROJECT_INSTRUCTIONS_RE)) {\n\t\t\tcontextFiles.push({\n\t\t\t\tpath: match[1],\n\t\t\t\tcontent: match[2] ?? \"\",\n\t\t\t});\n\t\t}\n\t\tremaining = remaining.replace(block, \"\");\n\t}\n\tlet skills = \"\";\n\tconst skillsMatch = remaining.match(SKILLS_BLOCK_RE);\n\tif (skillsMatch) {\n\t\tskills = skillsMatch[0];\n\t\tremaining = remaining.replace(skills, \"\");\n\t}\n\treturn { base: remaining, contextFiles, skills };\n}\n\n/**\n * Estimate the token cost of the tool definitions sent to the model.\n * The wire format is roughly `{ name, description, parameters(JSON schema) }` per tool.\n */\nexport function estimateToolDefinitionTokens(tools: ReadonlyArray<ContextBreakdownTool>): number {\n\tlet chars = 0;\n\tfor (const tool of tools) {\n\t\tchars += tool.name.length + tool.description.length;\n\t\ttry {\n\t\t\tconst schema = JSON.stringify(tool.parameters);\n\t\t\tif (schema) chars += schema.length;\n\t\t} catch {\n\t\t\t// Unserializable schema — skip rather than fail the whole breakdown.\n\t\t}\n\t}\n\treturn Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\n/**\n * Compute the estimated context window breakdown.\n * All numbers are chars/4 estimates, matching `estimateTokens`.\n */\nexport function computeContextBreakdown(input: ContextBreakdownInput): ContextBreakdown {\n\tconst sections = splitSystemPromptSections(input.systemPrompt);\n\tconst systemPrompt = estimateChars(sections.base);\n\tconst contextFiles = sections.contextFiles.map((file) => ({\n\t\tlabel: contextFileLabel(file.path, input.globalAgentsPath),\n\t\tpath: file.path,\n\t\ttokens: estimateChars(file.content),\n\t}));\n\tconst skills = estimateChars(sections.skills);\n\tconst toolDefinitions = estimateToolDefinitionTokens(input.tools);\n\n\tlet user = 0;\n\tlet assistantText = 0;\n\tlet thinking = 0;\n\tlet toolCalls = 0;\n\tlet toolResults = 0;\n\tlet summaries = 0;\n\n\tconst counts = {\n\t\tuser: 0,\n\t\tassistantText: 0,\n\t\tthinking: 0,\n\t\ttoolCalls: 0,\n\t\ttoolResults: 0,\n\t\tsummaries: 0,\n\t};\n\n\tfor (const message of input.messages) {\n\t\tswitch (message.role) {\n\t\t\tcase \"assistant\": {\n\t\t\t\tlet textChars = 0;\n\t\t\t\tlet thinkingChars = 0;\n\t\t\t\tlet toolCallChars = 0;\n\t\t\t\tfor (const block of (message as AssistantMessage).content) {\n\t\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\t\ttextChars += block.text.length;\n\t\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\t\tthinkingChars += block.thinking.length;\n\t\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\t\ttoolCallChars += block.name.length + JSON.stringify(block.arguments).length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassistantText += Math.ceil(textChars / CHARS_PER_TOKEN);\n\t\t\t\tthinking += Math.ceil(thinkingChars / CHARS_PER_TOKEN);\n\t\t\t\ttoolCalls += Math.ceil(toolCallChars / CHARS_PER_TOKEN);\n\t\t\t\tif (textChars > 0) counts.assistantText++;\n\t\t\t\tif (thinkingChars > 0) counts.thinking++;\n\t\t\t\tif (toolCallChars > 0) counts.toolCalls++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"user\":\n\t\t\tcase \"custom\":\n\t\t\t\tuser += estimateTokens(message);\n\t\t\t\tcounts.user++;\n\t\t\t\tbreak;\n\t\t\tcase \"toolResult\":\n\t\t\tcase \"bashExecution\":\n\t\t\t\ttoolResults += estimateTokens(message);\n\t\t\t\tcounts.toolResults++;\n\t\t\t\tbreak;\n\t\t\tcase \"branchSummary\":\n\t\t\tcase \"compactionSummary\":\n\t\t\t\tsummaries += estimateTokens(message);\n\t\t\t\tcounts.summaries++;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst contextFileTokens = contextFiles.reduce((sum, file) => sum + file.tokens, 0);\n\tconst total =\n\t\tsystemPrompt +\n\t\tcontextFileTokens +\n\t\tskills +\n\t\ttoolDefinitions +\n\t\tuser +\n\t\tassistantText +\n\t\tthinking +\n\t\ttoolCalls +\n\t\ttoolResults +\n\t\tsummaries;\n\treturn {\n\t\tsystemPrompt,\n\t\tcontextFiles,\n\t\tskills,\n\t\ttoolDefinitions,\n\t\tuser,\n\t\tassistantText,\n\t\tthinking,\n\t\ttoolCalls,\n\t\ttoolResults,\n\t\tsummaries,\n\t\ttotal,\n\t\tcontextWindow: input.contextWindow,\n\t\tfree: Math.max(0, input.contextWindow - total),\n\t\tcounts,\n\t};\n}\n"]}
1
+ {"version":3,"file":"context-breakdown.js","sourceRoot":"","sources":["../../src/core/context-breakdown.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunr: Context window breakdown estimation for the /context command.\n *\n * Pure functions that estimate (chars/4, same heuristic as `estimateTokens` in\n * compaction.ts) what consumes the model's context window: the system prompt\n * (project context files are baked into it), the registered tool definitions,\n * and the live session messages split into user / assistant text / thinking /\n * tool calls / tool results / summaries.\n *\n * Pass the same message list the context actually contains (post-compaction,\n * that is `AgentSession.messages`) so the breakdown matches reality.\n */\n\nimport type { AgentMessage } from \"@ashx-j/lunr-agent\";\nimport type { AssistantMessage } from \"@ashx-j/lunr-ai/compat\";\nimport { estimateTokens } from \"./compaction/index.ts\";\n\nexport interface ContextBreakdownTool {\n\tname: string;\n\tdescription: string;\n\tparameters: unknown;\n}\n\nexport interface ContextBreakdownInput {\n\tsystemPrompt: string;\n\ttools: ReadonlyArray<ContextBreakdownTool>;\n\tmessages: ReadonlyArray<AgentMessage>;\n\tcontextWindow: number;\n\t/** Absolute path of the user-level AGENTS.md, used only to distinguish its UI label. */\n\tglobalAgentsPath?: string;\n\t/** Absolute path of the selected model's AGENTS.md, used only for its UI label. */\n\tmodelAgentsPath?: string;\n}\n\nexport interface ContextFileBreakdown {\n\t/** Display label for the context file (AGENTS.md, Global AGENTS.md, CLAUDE.md, …). */\n\tlabel: string;\n\t/** Path from the <project_instructions path=\"…\"> wrapper, when present. */\n\tpath?: string;\n\ttokens: number;\n}\n\nexport interface ContextBreakdown {\n\t/** System prompt minus <project_context> and the skills block. */\n\tsystemPrompt: number;\n\t/** Each AGENTS.md / context file from <project_instructions>. */\n\tcontextFiles: ContextFileBreakdown[];\n\t/** formatSkillsForPrompt section, if present. */\n\tskills: number;\n\t/** Serialized tool schemas (name + description + JSON parameter schema). */\n\ttoolDefinitions: number;\n\t/** User and extension (\"custom\") messages. */\n\tuser: number;\n\t/** Assistant text blocks. */\n\tassistantText: number;\n\t/** Assistant thinking blocks. */\n\tthinking: number;\n\t/** Assistant tool call blocks (name + serialized arguments). */\n\ttoolCalls: number;\n\t/** Tool result messages and bash execution output. */\n\ttoolResults: number;\n\t/** Compaction and branch summaries. */\n\tsummaries: number;\n\t/** Sum of all categories above. */\n\ttotal: number;\n\tcontextWindow: number;\n\t/** max(0, contextWindow - total) */\n\tfree: number;\n\t/** Per-category message counts (not token counts). */\n\tcounts: {\n\t\tuser: number;\n\t\tassistantText: number;\n\t\tthinking: number;\n\t\ttoolCalls: number;\n\t\ttoolResults: number;\n\t\tsummaries: number;\n\t};\n}\n\nconst CHARS_PER_TOKEN = 4;\nconst PROJECT_CONTEXT_RE = /<project_context>[\\s\\S]*?<\\/project_context>/;\nconst PROJECT_INSTRUCTIONS_RE = /<project_instructions(?:\\s+path=\"([^\"]*)\")?>([\\s\\S]*?)<\\/project_instructions>/g;\nconst SKILLS_BLOCK_RE =\n\t/\\n\\nThe following skills provide specialized instructions for specific tasks\\.[\\s\\S]*<\\/available_skills>/;\n\nfunction estimateChars(text: string): number {\n\treturn text.length === 0 ? 0 : Math.ceil(text.length / CHARS_PER_TOKEN);\n}\n\nfunction contextFileLabel(\n\tfilePath: string | undefined,\n\tglobalAgentsPath: string | undefined,\n\tmodelAgentsPath: string | undefined,\n): string {\n\tif (!filePath) return \"AGENTS.md\";\n\tconst normalized = filePath.replace(/\\\\/g, \"/\");\n\tif (globalAgentsPath && normalized.toLowerCase() === globalAgentsPath.replace(/\\\\/g, \"/\").toLowerCase()) {\n\t\treturn \"Global AGENTS.md\";\n\t}\n\tif (modelAgentsPath && normalized.toLowerCase() === modelAgentsPath.replace(/\\\\/g, \"/\").toLowerCase()) {\n\t\tconst parts = normalized.split(\"/\");\n\t\treturn `${parts.at(-2) ?? \"Model\"} AGENTS.md`;\n\t}\n\tconst slash = normalized.lastIndexOf(\"/\");\n\treturn slash === -1 ? normalized : normalized.slice(slash + 1);\n}\n\n/** Split a live system prompt into base / context-file / skills parts. */\nexport function splitSystemPromptSections(systemPrompt: string): {\n\tbase: string;\n\tcontextFiles: Array<{ path?: string; content: string }>;\n\tskills: string;\n} {\n\tlet remaining = systemPrompt;\n\tconst contextFiles: Array<{ path?: string; content: string }> = [];\n\tconst contextMatch = remaining.match(PROJECT_CONTEXT_RE);\n\tif (contextMatch) {\n\t\tconst block = contextMatch[0];\n\t\tPROJECT_INSTRUCTIONS_RE.lastIndex = 0;\n\t\tfor (const match of block.matchAll(PROJECT_INSTRUCTIONS_RE)) {\n\t\t\tcontextFiles.push({\n\t\t\t\tpath: match[1],\n\t\t\t\tcontent: match[2] ?? \"\",\n\t\t\t});\n\t\t}\n\t\tremaining = remaining.replace(block, \"\");\n\t}\n\tlet skills = \"\";\n\tconst skillsMatch = remaining.match(SKILLS_BLOCK_RE);\n\tif (skillsMatch) {\n\t\tskills = skillsMatch[0];\n\t\tremaining = remaining.replace(skills, \"\");\n\t}\n\treturn { base: remaining, contextFiles, skills };\n}\n\n/**\n * Estimate the token cost of the tool definitions sent to the model.\n * The wire format is roughly `{ name, description, parameters(JSON schema) }` per tool.\n */\nexport function estimateToolDefinitionTokens(tools: ReadonlyArray<ContextBreakdownTool>): number {\n\tlet chars = 0;\n\tfor (const tool of tools) {\n\t\tchars += tool.name.length + tool.description.length;\n\t\ttry {\n\t\t\tconst schema = JSON.stringify(tool.parameters);\n\t\t\tif (schema) chars += schema.length;\n\t\t} catch {\n\t\t\t// Unserializable schema — skip rather than fail the whole breakdown.\n\t\t}\n\t}\n\treturn Math.ceil(chars / CHARS_PER_TOKEN);\n}\n\n/**\n * Compute the estimated context window breakdown.\n * All numbers are chars/4 estimates, matching `estimateTokens`.\n */\nexport function computeContextBreakdown(input: ContextBreakdownInput): ContextBreakdown {\n\tconst sections = splitSystemPromptSections(input.systemPrompt);\n\tconst systemPrompt = estimateChars(sections.base);\n\tconst contextFiles = sections.contextFiles.map((file) => ({\n\t\tlabel: contextFileLabel(file.path, input.globalAgentsPath, input.modelAgentsPath),\n\t\tpath: file.path,\n\t\ttokens: estimateChars(file.content),\n\t}));\n\tconst skills = estimateChars(sections.skills);\n\tconst toolDefinitions = estimateToolDefinitionTokens(input.tools);\n\n\tlet user = 0;\n\tlet assistantText = 0;\n\tlet thinking = 0;\n\tlet toolCalls = 0;\n\tlet toolResults = 0;\n\tlet summaries = 0;\n\n\tconst counts = {\n\t\tuser: 0,\n\t\tassistantText: 0,\n\t\tthinking: 0,\n\t\ttoolCalls: 0,\n\t\ttoolResults: 0,\n\t\tsummaries: 0,\n\t};\n\n\tfor (const message of input.messages) {\n\t\tswitch (message.role) {\n\t\t\tcase \"assistant\": {\n\t\t\t\tlet textChars = 0;\n\t\t\t\tlet thinkingChars = 0;\n\t\t\t\tlet toolCallChars = 0;\n\t\t\t\tfor (const block of (message as AssistantMessage).content) {\n\t\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\t\ttextChars += block.text.length;\n\t\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\t\tthinkingChars += block.thinking.length;\n\t\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\t\ttoolCallChars += block.name.length + JSON.stringify(block.arguments).length;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tassistantText += Math.ceil(textChars / CHARS_PER_TOKEN);\n\t\t\t\tthinking += Math.ceil(thinkingChars / CHARS_PER_TOKEN);\n\t\t\t\ttoolCalls += Math.ceil(toolCallChars / CHARS_PER_TOKEN);\n\t\t\t\tif (textChars > 0) counts.assistantText++;\n\t\t\t\tif (thinkingChars > 0) counts.thinking++;\n\t\t\t\tif (toolCallChars > 0) counts.toolCalls++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"user\":\n\t\t\tcase \"custom\":\n\t\t\t\tuser += estimateTokens(message);\n\t\t\t\tcounts.user++;\n\t\t\t\tbreak;\n\t\t\tcase \"toolResult\":\n\t\t\tcase \"bashExecution\":\n\t\t\t\ttoolResults += estimateTokens(message);\n\t\t\t\tcounts.toolResults++;\n\t\t\t\tbreak;\n\t\t\tcase \"branchSummary\":\n\t\t\tcase \"compactionSummary\":\n\t\t\t\tsummaries += estimateTokens(message);\n\t\t\t\tcounts.summaries++;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst contextFileTokens = contextFiles.reduce((sum, file) => sum + file.tokens, 0);\n\tconst total =\n\t\tsystemPrompt +\n\t\tcontextFileTokens +\n\t\tskills +\n\t\ttoolDefinitions +\n\t\tuser +\n\t\tassistantText +\n\t\tthinking +\n\t\ttoolCalls +\n\t\ttoolResults +\n\t\tsummaries;\n\treturn {\n\t\tsystemPrompt,\n\t\tcontextFiles,\n\t\tskills,\n\t\ttoolDefinitions,\n\t\tuser,\n\t\tassistantText,\n\t\tthinking,\n\t\ttoolCalls,\n\t\ttoolResults,\n\t\tsummaries,\n\t\ttotal,\n\t\tcontextWindow: input.contextWindow,\n\t\tfree: Math.max(0, input.contextWindow - total),\n\t\tcounts,\n\t};\n}\n"]}
@@ -1,15 +1,15 @@
1
1
  /**
2
- * lunr: detect injected prompts (/swarm, /research, /goal) so the transcript can
2
+ * lunr: detect injected prompts (/research, /goal) so the transcript can
3
3
  * render them collapsed instead of dumping the full multi-line prompt verbatim.
4
4
  *
5
5
  * These prompts become plain {role:"user"} messages (PromptOptions.source is not
6
6
  * persisted), so detection is keyed on stable markers that already exist in the
7
- * prompt bodies: the `[SWARM MODE]` / `[DEEP RESEARCH]` literal prefixes and the
7
+ * prompt bodies: the `[DEEP RESEARCH]` literal prefix and the
8
8
  * goal extension's `<!-- pi-goal-prompt:… -->` HTML comment. This is render-time
9
9
  * only — the model still receives the full prompt unchanged, and old session
10
10
  * transcripts are cleaned up retroactively with no schema migration.
11
11
  */
12
- export type InjectedPromptKind = "swarm" | "research" | "goal";
12
+ export type InjectedPromptKind = "research" | "goal";
13
13
  export interface InjectedPromptInfo {
14
14
  kind: InjectedPromptKind;
15
15
  /** One-line summary extracted from the prompt body (task / question / objective). */
@@ -1 +1 @@
1
- {"version":3,"file":"injected-prompt.d.ts","sourceRoot":"","sources":["../../src/core/injected-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAE/D,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,kBAAkB,CAAC;IACzB,qFAAqF;IACrF,OAAO,EAAE,MAAM,CAAC;CAChB;AA+BD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAoBjF;AAED,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAIrE,CAAC","sourcesContent":["/**\n * lunr: detect injected prompts (/swarm, /research, /goal) so the transcript can\n * render them collapsed instead of dumping the full multi-line prompt verbatim.\n *\n * These prompts become plain {role:\"user\"} messages (PromptOptions.source is not\n * persisted), so detection is keyed on stable markers that already exist in the\n * prompt bodies: the `[SWARM MODE]` / `[DEEP RESEARCH]` literal prefixes and the\n * goal extension's `<!-- pi-goal-prompt:… -->` HTML comment. This is render-time\n * only — the model still receives the full prompt unchanged, and old session\n * transcripts are cleaned up retroactively with no schema migration.\n */\n\nexport type InjectedPromptKind = \"swarm\" | \"research\" | \"goal\";\n\nexport interface InjectedPromptInfo {\n\tkind: InjectedPromptKind;\n\t/** One-line summary extracted from the prompt body (task / question / objective). */\n\tsummary: string;\n}\n\nfunction firstNonEmptyLine(text: string): string | undefined {\n\tfor (const raw of text.split(/\\r?\\n/)) {\n\t\tconst line = raw.trim();\n\t\tif (line) return line;\n\t}\n\treturn undefined;\n}\n\n/** Extract the value following a `Label:` prefix on the first line, else undefined. */\nfunction extractAfterLabel(text: string, label: string): string | undefined {\n\tconst idx = text.indexOf(label);\n\tif (idx === -1) return undefined;\n\tconst rest = text.slice(idx + label.length).split(/\\r?\\n/)[0] ?? \"\";\n\tconst trimmed = rest.trim();\n\treturn trimmed || undefined;\n}\n\n/** Extract the contents of a `<goal_objective>…</goal_objective>` block. */\nfunction extractGoalObjective(text: string): string | undefined {\n\tconst open = text.indexOf(\"<goal_objective>\");\n\tif (open === -1) return undefined;\n\tconst close = text.indexOf(\"</goal_objective>\", open);\n\tif (close === -1) return undefined;\n\tconst inner = text.slice(open + \"<goal_objective>\".length, close);\n\tconst trimmed = inner.trim();\n\t// Unescape the minimal XML escaping the goal prompt builder applies.\n\treturn trimmed.replace(/&amp;/g, \"&\").replace(/&lt;/g, \"<\").replace(/&gt;/g, \">\").trim() || undefined;\n}\n\nexport function detectInjectedPrompt(text: string): InjectedPromptInfo | undefined {\n\tif (!text) return undefined;\n\n\tif (text.startsWith(\"[SWARM MODE]\")) {\n\t\tconst summary = extractAfterLabel(text, \"Task:\") ?? firstNonEmptyLine(text.slice(\"[SWARM MODE]\".length).trim());\n\t\treturn { kind: \"swarm\", summary: summary ?? \"swarm task\" };\n\t}\n\n\tif (text.startsWith(\"[DEEP RESEARCH]\")) {\n\t\tconst summary =\n\t\t\textractAfterLabel(text, \"Question:\") ?? firstNonEmptyLine(text.slice(\"[DEEP RESEARCH]\".length).trim());\n\t\treturn { kind: \"research\", summary: summary ?? \"research question\" };\n\t}\n\n\tif (text.includes(\"pi-goal-prompt:\")) {\n\t\tconst objective = extractGoalObjective(text);\n\t\treturn { kind: \"goal\", summary: objective ?? \"goal\" };\n\t}\n\n\treturn undefined;\n}\n\nexport const INJECTED_PROMPT_LABELS: Record<InjectedPromptKind, string> = {\n\tswarm: \"swarm\",\n\tresearch: \"research\",\n\tgoal: \"goal\",\n};\n"]}
1
+ {"version":3,"file":"injected-prompt.d.ts","sourceRoot":"","sources":["../../src/core/injected-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,kBAAkB,CAAC;IACzB,qFAAqF;IACrF,OAAO,EAAE,MAAM,CAAC;CAChB;AA+BD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAejF;AAED,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAGrE,CAAC","sourcesContent":["/**\n * lunr: detect injected prompts (/research, /goal) so the transcript can\n * render them collapsed instead of dumping the full multi-line prompt verbatim.\n *\n * These prompts become plain {role:\"user\"} messages (PromptOptions.source is not\n * persisted), so detection is keyed on stable markers that already exist in the\n * prompt bodies: the `[DEEP RESEARCH]` literal prefix and the\n * goal extension's `<!-- pi-goal-prompt:… -->` HTML comment. This is render-time\n * only — the model still receives the full prompt unchanged, and old session\n * transcripts are cleaned up retroactively with no schema migration.\n */\n\nexport type InjectedPromptKind = \"research\" | \"goal\";\n\nexport interface InjectedPromptInfo {\n\tkind: InjectedPromptKind;\n\t/** One-line summary extracted from the prompt body (task / question / objective). */\n\tsummary: string;\n}\n\nfunction firstNonEmptyLine(text: string): string | undefined {\n\tfor (const raw of text.split(/\\r?\\n/)) {\n\t\tconst line = raw.trim();\n\t\tif (line) return line;\n\t}\n\treturn undefined;\n}\n\n/** Extract the value following a `Label:` prefix on the first line, else undefined. */\nfunction extractAfterLabel(text: string, label: string): string | undefined {\n\tconst idx = text.indexOf(label);\n\tif (idx === -1) return undefined;\n\tconst rest = text.slice(idx + label.length).split(/\\r?\\n/)[0] ?? \"\";\n\tconst trimmed = rest.trim();\n\treturn trimmed || undefined;\n}\n\n/** Extract the contents of a `<goal_objective>…</goal_objective>` block. */\nfunction extractGoalObjective(text: string): string | undefined {\n\tconst open = text.indexOf(\"<goal_objective>\");\n\tif (open === -1) return undefined;\n\tconst close = text.indexOf(\"</goal_objective>\", open);\n\tif (close === -1) return undefined;\n\tconst inner = text.slice(open + \"<goal_objective>\".length, close);\n\tconst trimmed = inner.trim();\n\t// Unescape the minimal XML escaping the goal prompt builder applies.\n\treturn trimmed.replace(/&amp;/g, \"&\").replace(/&lt;/g, \"<\").replace(/&gt;/g, \">\").trim() || undefined;\n}\n\nexport function detectInjectedPrompt(text: string): InjectedPromptInfo | undefined {\n\tif (!text) return undefined;\n\n\tif (text.startsWith(\"[DEEP RESEARCH]\")) {\n\t\tconst summary =\n\t\t\textractAfterLabel(text, \"Question:\") ?? firstNonEmptyLine(text.slice(\"[DEEP RESEARCH]\".length).trim());\n\t\treturn { kind: \"research\", summary: summary ?? \"research question\" };\n\t}\n\n\tif (text.includes(\"pi-goal-prompt:\")) {\n\t\tconst objective = extractGoalObjective(text);\n\t\treturn { kind: \"goal\", summary: objective ?? \"goal\" };\n\t}\n\n\treturn undefined;\n}\n\nexport const INJECTED_PROMPT_LABELS: Record<InjectedPromptKind, string> = {\n\tresearch: \"research\",\n\tgoal: \"goal\",\n};\n"]}
@@ -1,10 +1,10 @@
1
1
  /**
2
- * lunr: detect injected prompts (/swarm, /research, /goal) so the transcript can
2
+ * lunr: detect injected prompts (/research, /goal) so the transcript can
3
3
  * render them collapsed instead of dumping the full multi-line prompt verbatim.
4
4
  *
5
5
  * These prompts become plain {role:"user"} messages (PromptOptions.source is not
6
6
  * persisted), so detection is keyed on stable markers that already exist in the
7
- * prompt bodies: the `[SWARM MODE]` / `[DEEP RESEARCH]` literal prefixes and the
7
+ * prompt bodies: the `[DEEP RESEARCH]` literal prefix and the
8
8
  * goal extension's `<!-- pi-goal-prompt:… -->` HTML comment. This is render-time
9
9
  * only — the model still receives the full prompt unchanged, and old session
10
10
  * transcripts are cleaned up retroactively with no schema migration.
@@ -42,10 +42,6 @@ function extractGoalObjective(text) {
42
42
  export function detectInjectedPrompt(text) {
43
43
  if (!text)
44
44
  return undefined;
45
- if (text.startsWith("[SWARM MODE]")) {
46
- const summary = extractAfterLabel(text, "Task:") ?? firstNonEmptyLine(text.slice("[SWARM MODE]".length).trim());
47
- return { kind: "swarm", summary: summary ?? "swarm task" };
48
- }
49
45
  if (text.startsWith("[DEEP RESEARCH]")) {
50
46
  const summary = extractAfterLabel(text, "Question:") ?? firstNonEmptyLine(text.slice("[DEEP RESEARCH]".length).trim());
51
47
  return { kind: "research", summary: summary ?? "research question" };
@@ -57,7 +53,6 @@ export function detectInjectedPrompt(text) {
57
53
  return undefined;
58
54
  }
59
55
  export const INJECTED_PROMPT_LABELS = {
60
- swarm: "swarm",
61
56
  research: "research",
62
57
  goal: "goal",
63
58
  };
@@ -1 +1 @@
1
- {"version":3,"file":"injected-prompt.js","sourceRoot":"","sources":["../../src/core/injected-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunr: detect injected prompts (/swarm, /research, /goal) so the transcript can\n * render them collapsed instead of dumping the full multi-line prompt verbatim.\n *\n * These prompts become plain {role:\"user\"} messages (PromptOptions.source is not\n * persisted), so detection is keyed on stable markers that already exist in the\n * prompt bodies: the `[SWARM MODE]` / `[DEEP RESEARCH]` literal prefixes and the\n * goal extension's `<!-- pi-goal-prompt:… -->` HTML comment. This is render-time\n * only — the model still receives the full prompt unchanged, and old session\n * transcripts are cleaned up retroactively with no schema migration.\n */\n\nexport type InjectedPromptKind = \"swarm\" | \"research\" | \"goal\";\n\nexport interface InjectedPromptInfo {\n\tkind: InjectedPromptKind;\n\t/** One-line summary extracted from the prompt body (task / question / objective). */\n\tsummary: string;\n}\n\nfunction firstNonEmptyLine(text: string): string | undefined {\n\tfor (const raw of text.split(/\\r?\\n/)) {\n\t\tconst line = raw.trim();\n\t\tif (line) return line;\n\t}\n\treturn undefined;\n}\n\n/** Extract the value following a `Label:` prefix on the first line, else undefined. */\nfunction extractAfterLabel(text: string, label: string): string | undefined {\n\tconst idx = text.indexOf(label);\n\tif (idx === -1) return undefined;\n\tconst rest = text.slice(idx + label.length).split(/\\r?\\n/)[0] ?? \"\";\n\tconst trimmed = rest.trim();\n\treturn trimmed || undefined;\n}\n\n/** Extract the contents of a `<goal_objective>…</goal_objective>` block. */\nfunction extractGoalObjective(text: string): string | undefined {\n\tconst open = text.indexOf(\"<goal_objective>\");\n\tif (open === -1) return undefined;\n\tconst close = text.indexOf(\"</goal_objective>\", open);\n\tif (close === -1) return undefined;\n\tconst inner = text.slice(open + \"<goal_objective>\".length, close);\n\tconst trimmed = inner.trim();\n\t// Unescape the minimal XML escaping the goal prompt builder applies.\n\treturn trimmed.replace(/&amp;/g, \"&\").replace(/&lt;/g, \"<\").replace(/&gt;/g, \">\").trim() || undefined;\n}\n\nexport function detectInjectedPrompt(text: string): InjectedPromptInfo | undefined {\n\tif (!text) return undefined;\n\n\tif (text.startsWith(\"[SWARM MODE]\")) {\n\t\tconst summary = extractAfterLabel(text, \"Task:\") ?? firstNonEmptyLine(text.slice(\"[SWARM MODE]\".length).trim());\n\t\treturn { kind: \"swarm\", summary: summary ?? \"swarm task\" };\n\t}\n\n\tif (text.startsWith(\"[DEEP RESEARCH]\")) {\n\t\tconst summary =\n\t\t\textractAfterLabel(text, \"Question:\") ?? firstNonEmptyLine(text.slice(\"[DEEP RESEARCH]\".length).trim());\n\t\treturn { kind: \"research\", summary: summary ?? \"research question\" };\n\t}\n\n\tif (text.includes(\"pi-goal-prompt:\")) {\n\t\tconst objective = extractGoalObjective(text);\n\t\treturn { kind: \"goal\", summary: objective ?? \"goal\" };\n\t}\n\n\treturn undefined;\n}\n\nexport const INJECTED_PROMPT_LABELS: Record<InjectedPromptKind, string> = {\n\tswarm: \"swarm\",\n\tresearch: \"research\",\n\tgoal: \"goal\",\n};\n"]}
1
+ {"version":3,"file":"injected-prompt.js","sourceRoot":"","sources":["../../src/core/injected-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,SAAS,iBAAiB,CAAC,IAAY,EAAsB;IAC5D,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACvB,CAAC;IACD,OAAO,SAAS,CAAC;AAAA,CACjB;AAED,uFAAuF;AACvF,SAAS,iBAAiB,CAAC,IAAY,EAAE,KAAa,EAAsB;IAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,OAAO,OAAO,IAAI,SAAS,CAAC;AAAA,CAC5B;AAED,8EAA4E;AAC5E,SAAS,oBAAoB,CAAC,IAAY,EAAsB;IAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC9C,IAAI,IAAI,KAAK,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAC;IACtD,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,qEAAqE;IACrE,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC;AAAA,CACtG;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAkC;IAClF,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAE5B,IAAI,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACxC,MAAM,OAAO,GACZ,iBAAiB,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACxG,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,CAAC;IACtE,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACtC,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,IAAI,MAAM,EAAE,CAAC;IACvD,CAAC;IAED,OAAO,SAAS,CAAC;AAAA,CACjB;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAuC;IACzE,QAAQ,EAAE,UAAU;IACpB,IAAI,EAAE,MAAM;CACZ,CAAC","sourcesContent":["/**\n * lunr: detect injected prompts (/research, /goal) so the transcript can\n * render them collapsed instead of dumping the full multi-line prompt verbatim.\n *\n * These prompts become plain {role:\"user\"} messages (PromptOptions.source is not\n * persisted), so detection is keyed on stable markers that already exist in the\n * prompt bodies: the `[DEEP RESEARCH]` literal prefix and the\n * goal extension's `<!-- pi-goal-prompt:… -->` HTML comment. This is render-time\n * only — the model still receives the full prompt unchanged, and old session\n * transcripts are cleaned up retroactively with no schema migration.\n */\n\nexport type InjectedPromptKind = \"research\" | \"goal\";\n\nexport interface InjectedPromptInfo {\n\tkind: InjectedPromptKind;\n\t/** One-line summary extracted from the prompt body (task / question / objective). */\n\tsummary: string;\n}\n\nfunction firstNonEmptyLine(text: string): string | undefined {\n\tfor (const raw of text.split(/\\r?\\n/)) {\n\t\tconst line = raw.trim();\n\t\tif (line) return line;\n\t}\n\treturn undefined;\n}\n\n/** Extract the value following a `Label:` prefix on the first line, else undefined. */\nfunction extractAfterLabel(text: string, label: string): string | undefined {\n\tconst idx = text.indexOf(label);\n\tif (idx === -1) return undefined;\n\tconst rest = text.slice(idx + label.length).split(/\\r?\\n/)[0] ?? \"\";\n\tconst trimmed = rest.trim();\n\treturn trimmed || undefined;\n}\n\n/** Extract the contents of a `<goal_objective>…</goal_objective>` block. */\nfunction extractGoalObjective(text: string): string | undefined {\n\tconst open = text.indexOf(\"<goal_objective>\");\n\tif (open === -1) return undefined;\n\tconst close = text.indexOf(\"</goal_objective>\", open);\n\tif (close === -1) return undefined;\n\tconst inner = text.slice(open + \"<goal_objective>\".length, close);\n\tconst trimmed = inner.trim();\n\t// Unescape the minimal XML escaping the goal prompt builder applies.\n\treturn trimmed.replace(/&amp;/g, \"&\").replace(/&lt;/g, \"<\").replace(/&gt;/g, \">\").trim() || undefined;\n}\n\nexport function detectInjectedPrompt(text: string): InjectedPromptInfo | undefined {\n\tif (!text) return undefined;\n\n\tif (text.startsWith(\"[DEEP RESEARCH]\")) {\n\t\tconst summary =\n\t\t\textractAfterLabel(text, \"Question:\") ?? firstNonEmptyLine(text.slice(\"[DEEP RESEARCH]\".length).trim());\n\t\treturn { kind: \"research\", summary: summary ?? \"research question\" };\n\t}\n\n\tif (text.includes(\"pi-goal-prompt:\")) {\n\t\tconst objective = extractGoalObjective(text);\n\t\treturn { kind: \"goal\", summary: objective ?? \"goal\" };\n\t}\n\n\treturn undefined;\n}\n\nexport const INJECTED_PROMPT_LABELS: Record<InjectedPromptKind, string> = {\n\tresearch: \"research\",\n\tgoal: \"goal\",\n};\n"]}
@@ -0,0 +1,13 @@
1
+ /** Number of children above which aggregate launch confirmation engages. */
2
+ export declare const LARGE_SUBAGENT_LAUNCH_THRESHOLD = 2;
3
+ export declare function effectiveLargeSubagentLaunchCount(args: Record<string, unknown>): number;
4
+ interface AssistantMessageLike {
5
+ content?: ReadonlyArray<{
6
+ type?: string;
7
+ name?: string;
8
+ arguments?: unknown;
9
+ }>;
10
+ }
11
+ export declare function effectiveLargeSubagentLaunchCountForTurn(args: Record<string, unknown>, assistantMessage?: AssistantMessageLike): number;
12
+ export {};
13
+ //# sourceMappingURL=large-subagent-launch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"large-subagent-launch.d.ts","sourceRoot":"","sources":["../../src/core/large-subagent-launch.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAejD,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAcvF;AAaD,UAAU,oBAAoB;IAC7B,OAAO,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC/E;AAED,wBAAgB,wCAAwC,CACvD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,gBAAgB,CAAC,EAAE,oBAAoB,GACrC,MAAM,CAWR","sourcesContent":["/** Number of children above which aggregate launch confirmation engages. */\nexport const LARGE_SUBAGENT_LAUNCH_THRESHOLD = 2;\n\nfunction countEntry(item: unknown): number {\n\tconst count = item && typeof item === \"object\" ? (item as { count?: unknown }).count : undefined;\n\treturn typeof count === \"number\" && Number.isInteger(count) && count >= 1 ? count : 1;\n}\n\nfunction isManagementCall(args: Record<string, unknown>): boolean {\n\treturn typeof args.action === \"string\" && args.action.length > 0;\n}\n\nfunction isAsyncDetached(args: Record<string, unknown>): boolean {\n\treturn args.async === true && args.clarify !== true;\n}\n\nexport function effectiveLargeSubagentLaunchCount(args: Record<string, unknown>): number {\n\tlet total = 0;\n\tif (Array.isArray(args.tasks)) {\n\t\tfor (const task of args.tasks) total += countEntry(task);\n\t}\n\tif (Array.isArray(args.chain)) {\n\t\tfor (const step of args.chain) {\n\t\t\tconst parallel = step && typeof step === \"object\" ? (step as { parallel?: unknown }).parallel : undefined;\n\t\t\tif (Array.isArray(parallel)) {\n\t\t\t\tfor (const child of parallel) total += countEntry(child);\n\t\t\t}\n\t\t}\n\t}\n\treturn total;\n}\n\nfunction siblingSingleCount(args: Record<string, unknown>): number {\n\tif (isManagementCall(args) || isAsyncDetached(args)) return 0;\n\tif (Array.isArray(args.tasks) || Array.isArray(args.chain)) return 0;\n\treturn typeof args.task === \"string\" &&\n\t\targs.task.length > 0 &&\n\t\ttypeof args.description === \"string\" &&\n\t\targs.description.length > 0\n\t\t? 1\n\t\t: 0;\n}\n\ninterface AssistantMessageLike {\n\tcontent?: ReadonlyArray<{ type?: string; name?: string; arguments?: unknown }>;\n}\n\nexport function effectiveLargeSubagentLaunchCountForTurn(\n\targs: Record<string, unknown>,\n\tassistantMessage?: AssistantMessageLike,\n): number {\n\tconst own = effectiveLargeSubagentLaunchCount(args);\n\tconst siblingSingles = (assistantMessage?.content ?? []).reduce((total, block) => {\n\t\tif (block.type !== \"toolCall\" || block.name !== \"subagent\") return total;\n\t\tconst siblingArgs =\n\t\t\tblock.arguments && typeof block.arguments === \"object\" && !Array.isArray(block.arguments)\n\t\t\t\t? (block.arguments as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\treturn siblingArgs ? total + siblingSingleCount(siblingArgs) : total;\n\t}, 0);\n\treturn Math.max(own, siblingSingles);\n}\n"]}
@@ -0,0 +1,54 @@
1
+ /** Number of children above which aggregate launch confirmation engages. */
2
+ export const LARGE_SUBAGENT_LAUNCH_THRESHOLD = 2;
3
+ function countEntry(item) {
4
+ const count = item && typeof item === "object" ? item.count : undefined;
5
+ return typeof count === "number" && Number.isInteger(count) && count >= 1 ? count : 1;
6
+ }
7
+ function isManagementCall(args) {
8
+ return typeof args.action === "string" && args.action.length > 0;
9
+ }
10
+ function isAsyncDetached(args) {
11
+ return args.async === true && args.clarify !== true;
12
+ }
13
+ export function effectiveLargeSubagentLaunchCount(args) {
14
+ let total = 0;
15
+ if (Array.isArray(args.tasks)) {
16
+ for (const task of args.tasks)
17
+ total += countEntry(task);
18
+ }
19
+ if (Array.isArray(args.chain)) {
20
+ for (const step of args.chain) {
21
+ const parallel = step && typeof step === "object" ? step.parallel : undefined;
22
+ if (Array.isArray(parallel)) {
23
+ for (const child of parallel)
24
+ total += countEntry(child);
25
+ }
26
+ }
27
+ }
28
+ return total;
29
+ }
30
+ function siblingSingleCount(args) {
31
+ if (isManagementCall(args) || isAsyncDetached(args))
32
+ return 0;
33
+ if (Array.isArray(args.tasks) || Array.isArray(args.chain))
34
+ return 0;
35
+ return typeof args.task === "string" &&
36
+ args.task.length > 0 &&
37
+ typeof args.description === "string" &&
38
+ args.description.length > 0
39
+ ? 1
40
+ : 0;
41
+ }
42
+ export function effectiveLargeSubagentLaunchCountForTurn(args, assistantMessage) {
43
+ const own = effectiveLargeSubagentLaunchCount(args);
44
+ const siblingSingles = (assistantMessage?.content ?? []).reduce((total, block) => {
45
+ if (block.type !== "toolCall" || block.name !== "subagent")
46
+ return total;
47
+ const siblingArgs = block.arguments && typeof block.arguments === "object" && !Array.isArray(block.arguments)
48
+ ? block.arguments
49
+ : undefined;
50
+ return siblingArgs ? total + siblingSingleCount(siblingArgs) : total;
51
+ }, 0);
52
+ return Math.max(own, siblingSingles);
53
+ }
54
+ //# sourceMappingURL=large-subagent-launch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"large-subagent-launch.js","sourceRoot":"","sources":["../../src/core/large-subagent-launch.ts"],"names":[],"mappings":"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","sourcesContent":["/** Number of children above which aggregate launch confirmation engages. */\nexport const LARGE_SUBAGENT_LAUNCH_THRESHOLD = 2;\n\nfunction countEntry(item: unknown): number {\n\tconst count = item && typeof item === \"object\" ? (item as { count?: unknown }).count : undefined;\n\treturn typeof count === \"number\" && Number.isInteger(count) && count >= 1 ? count : 1;\n}\n\nfunction isManagementCall(args: Record<string, unknown>): boolean {\n\treturn typeof args.action === \"string\" && args.action.length > 0;\n}\n\nfunction isAsyncDetached(args: Record<string, unknown>): boolean {\n\treturn args.async === true && args.clarify !== true;\n}\n\nexport function effectiveLargeSubagentLaunchCount(args: Record<string, unknown>): number {\n\tlet total = 0;\n\tif (Array.isArray(args.tasks)) {\n\t\tfor (const task of args.tasks) total += countEntry(task);\n\t}\n\tif (Array.isArray(args.chain)) {\n\t\tfor (const step of args.chain) {\n\t\t\tconst parallel = step && typeof step === \"object\" ? (step as { parallel?: unknown }).parallel : undefined;\n\t\t\tif (Array.isArray(parallel)) {\n\t\t\t\tfor (const child of parallel) total += countEntry(child);\n\t\t\t}\n\t\t}\n\t}\n\treturn total;\n}\n\nfunction siblingSingleCount(args: Record<string, unknown>): number {\n\tif (isManagementCall(args) || isAsyncDetached(args)) return 0;\n\tif (Array.isArray(args.tasks) || Array.isArray(args.chain)) return 0;\n\treturn typeof args.task === \"string\" &&\n\t\targs.task.length > 0 &&\n\t\ttypeof args.description === \"string\" &&\n\t\targs.description.length > 0\n\t\t? 1\n\t\t: 0;\n}\n\ninterface AssistantMessageLike {\n\tcontent?: ReadonlyArray<{ type?: string; name?: string; arguments?: unknown }>;\n}\n\nexport function effectiveLargeSubagentLaunchCountForTurn(\n\targs: Record<string, unknown>,\n\tassistantMessage?: AssistantMessageLike,\n): number {\n\tconst own = effectiveLargeSubagentLaunchCount(args);\n\tconst siblingSingles = (assistantMessage?.content ?? []).reduce((total, block) => {\n\t\tif (block.type !== \"toolCall\" || block.name !== \"subagent\") return total;\n\t\tconst siblingArgs =\n\t\t\tblock.arguments && typeof block.arguments === \"object\" && !Array.isArray(block.arguments)\n\t\t\t\t? (block.arguments as Record<string, unknown>)\n\t\t\t\t: undefined;\n\t\treturn siblingArgs ? total + siblingSingleCount(siblingArgs) : total;\n\t}, 0);\n\treturn Math.max(own, siblingSingles);\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { Model } from "@ashx-j/lunr-ai";
2
+ import type { Settings, SettingsManager } from "./settings-manager.ts";
3
+ export type ModelInstructionsMode = "both" | "model-only";
4
+ export interface ModelInstructionsMigrationResult {
5
+ status: "absent" | "migrated" | "conflict" | "failed";
6
+ diagnostic?: string;
7
+ }
8
+ export declare function getInstructionsRoot(agentDir: string): string;
9
+ export declare function getGlobalInstructionsPath(agentDir: string): string;
10
+ export declare function modelInstructionFolderName(modelId: string): string;
11
+ export declare function getModelInstructionsPath(agentDir: string, modelId: string): string;
12
+ export declare function isUserInstructionsPath(filePath: string, agentDir: string): boolean;
13
+ export declare function migrateLegacyGlobalInstructions(agentDir: string): ModelInstructionsMigrationResult;
14
+ export declare function ensureModelInstructionDirs(agentDir: string, settings: Pick<Settings, "defaultModel" | "enabledModels" | "modelTiers">, currentModel?: Pick<Model<any>, "id">): string[];
15
+ export declare function loadSelectedUserInstructions(options: {
16
+ agentDir: string;
17
+ settingsManager: SettingsManager;
18
+ model?: Pick<Model<any>, "id">;
19
+ }): Array<{
20
+ path: string;
21
+ content: string;
22
+ }>;
23
+ export declare function instructionPathLabel(filePath: string, agentDir: string): string | undefined;
24
+ //# sourceMappingURL=model-instructions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-instructions.d.ts","sourceRoot":"","sources":["../../src/core/model-instructions.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEvE,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,YAAY,CAAC;AAE1D,MAAM,WAAW,gCAAgC;IAChD,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAWlE;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAElF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGlF;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,gCAAgC,CAoBlG;AASD,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,cAAc,GAAG,eAAe,GAAG,YAAY,CAAC,EACzE,YAAY,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GACnC,MAAM,EAAE,CAwBV;AAWD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,eAAe,CAAC;IACjC,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;CAC/B,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAc3C;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO3F","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, statSync } from \"node:fs\";\nimport { basename, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport type { Model } from \"@ashx-j/lunr-ai\";\nimport type { Settings, SettingsManager } from \"./settings-manager.ts\";\n\nexport type ModelInstructionsMode = \"both\" | \"model-only\";\n\nexport interface ModelInstructionsMigrationResult {\n\tstatus: \"absent\" | \"migrated\" | \"conflict\" | \"failed\";\n\tdiagnostic?: string;\n}\n\nexport function getInstructionsRoot(agentDir: string): string {\n\treturn join(resolve(agentDir), \"agents\");\n}\n\nexport function getGlobalInstructionsPath(agentDir: string): string {\n\treturn join(getInstructionsRoot(agentDir), \"AGENTS.md\");\n}\n\nexport function modelInstructionFolderName(modelId: string): string {\n\tconst finalSegment = modelId.trim().split(/[\\\\/]/).filter(Boolean).at(-1) ?? \"model\";\n\tconst withoutThinking = finalSegment.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/i, \"\");\n\tlet safe = withoutThinking.replace(/[<>:\"/\\\\|?*\\u0000-\\u001f]/g, \"_\").replace(/[. ]+$/g, \"\");\n\tif (!safe || safe === \".\" || safe === \"..\") safe = \"model\";\n\tif (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\\.|$)/i.test(safe)) safe = `_${safe}`;\n\tif (safe.length > 100 || safe.toLowerCase() !== withoutThinking.toLowerCase()) {\n\t\tconst digest = createHash(\"sha256\").update(withoutThinking.toLowerCase()).digest(\"hex\").slice(0, 8);\n\t\tsafe = `${safe.slice(0, 90)}-${digest}`;\n\t}\n\treturn safe.toLowerCase();\n}\n\nexport function getModelInstructionsPath(agentDir: string, modelId: string): string {\n\treturn join(getInstructionsRoot(agentDir), modelInstructionFolderName(modelId), \"AGENTS.md\");\n}\n\nexport function isUserInstructionsPath(filePath: string, agentDir: string): boolean {\n\tconst rel = relative(resolve(getInstructionsRoot(agentDir)), resolve(filePath));\n\treturn rel === \"\" || (!isAbsolute(rel) && rel !== \"..\" && !rel.startsWith(`..${sep}`));\n}\n\nexport function migrateLegacyGlobalInstructions(agentDir: string): ModelInstructionsMigrationResult {\n\tconst legacyPath = join(resolve(agentDir), \"AGENTS.md\");\n\tconst destinationPath = getGlobalInstructionsPath(agentDir);\n\tif (!existsSync(legacyPath)) return { status: \"absent\" };\n\tif (existsSync(destinationPath)) {\n\t\treturn {\n\t\t\tstatus: \"conflict\",\n\t\t\tdiagnostic: `Both legacy '${legacyPath}' and current '${destinationPath}' exist. Kept both and will use the current file.`,\n\t\t};\n\t}\n\ttry {\n\t\tmkdirSync(getInstructionsRoot(agentDir), { recursive: true });\n\t\trenameSync(legacyPath, destinationPath);\n\t\treturn { status: \"migrated\" };\n\t} catch (error) {\n\t\treturn {\n\t\t\tstatus: \"failed\",\n\t\t\tdiagnostic: `Could not migrate '${legacyPath}' to '${destinationPath}': ${error instanceof Error ? error.message : String(error)}`,\n\t\t};\n\t}\n}\n\nfunction concreteModelId(value: unknown): string | undefined {\n\tif (typeof value !== \"string\") return undefined;\n\tconst trimmed = value.trim();\n\tif (!trimmed || /[*?{}]/.test(trimmed)) return undefined;\n\treturn trimmed.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/i, \"\");\n}\n\nexport function ensureModelInstructionDirs(\n\tagentDir: string,\n\tsettings: Pick<Settings, \"defaultModel\" | \"enabledModels\" | \"modelTiers\">,\n\tcurrentModel?: Pick<Model<any>, \"id\">,\n): string[] {\n\tconst modelIds = new Set<string>();\n\tconst add = (value: unknown) => {\n\t\tconst id = concreteModelId(value);\n\t\tif (id) modelIds.add(id);\n\t};\n\tadd(currentModel?.id);\n\tadd(settings.defaultModel);\n\tfor (const value of settings.enabledModels ?? []) add(value);\n\tadd(settings.modelTiers?.light);\n\tadd(settings.modelTiers?.standard);\n\tadd(settings.modelTiers?.heavy);\n\n\tconst diagnostics: string[] = [];\n\tfor (const id of modelIds) {\n\t\ttry {\n\t\t\tmkdirSync(join(getInstructionsRoot(agentDir), modelInstructionFolderName(id)), { recursive: true });\n\t\t} catch (error) {\n\t\t\tdiagnostics.push(\n\t\t\t\t`Could not prepare model-instruction folder for '${id}': ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t}\n\treturn diagnostics;\n}\n\nfunction safeReadInstruction(path: string): { path: string; content: string } | undefined {\n\ttry {\n\t\tif (!existsSync(path) || !statSync(path).isFile()) return undefined;\n\t\treturn { path, content: readFileSync(path, \"utf8\") };\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function loadSelectedUserInstructions(options: {\n\tagentDir: string;\n\tsettingsManager: SettingsManager;\n\tmodel?: Pick<Model<any>, \"id\">;\n}): Array<{ path: string; content: string }> {\n\tensureModelInstructionDirs(options.agentDir, options.settingsManager.getGlobalSettings(), options.model);\n\tconst enabled = options.settingsManager.getModelInstructionsEnabled();\n\tconst mode = options.settingsManager.getModelInstructionsMode();\n\tconst files: Array<{ path: string; content: string }> = [];\n\tif (!enabled || mode === \"both\") {\n\t\tconst globalFile = safeReadInstruction(getGlobalInstructionsPath(options.agentDir));\n\t\tif (globalFile) files.push(globalFile);\n\t}\n\tif (enabled && options.model) {\n\t\tconst modelFile = safeReadInstruction(getModelInstructionsPath(options.agentDir, options.model.id));\n\t\tif (modelFile) files.push(modelFile);\n\t}\n\treturn files;\n}\n\nexport function instructionPathLabel(filePath: string, agentDir: string): string | undefined {\n\tconst resolved = resolve(filePath);\n\tif (resolved.toLowerCase() === resolve(getGlobalInstructionsPath(agentDir)).toLowerCase()) {\n\t\treturn \"Global AGENTS.md\";\n\t}\n\tif (!isUserInstructionsPath(resolved, agentDir)) return undefined;\n\treturn `${basename(resolve(resolved, \"..\"))} AGENTS.md`;\n}\n"]}
@@ -0,0 +1,123 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync } from "node:fs";
3
+ import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
4
+ export function getInstructionsRoot(agentDir) {
5
+ return join(resolve(agentDir), "agents");
6
+ }
7
+ export function getGlobalInstructionsPath(agentDir) {
8
+ return join(getInstructionsRoot(agentDir), "AGENTS.md");
9
+ }
10
+ export function modelInstructionFolderName(modelId) {
11
+ const finalSegment = modelId.trim().split(/[\\/]/).filter(Boolean).at(-1) ?? "model";
12
+ const withoutThinking = finalSegment.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/i, "");
13
+ let safe = withoutThinking.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "_").replace(/[. ]+$/g, "");
14
+ if (!safe || safe === "." || safe === "..")
15
+ safe = "model";
16
+ if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(safe))
17
+ safe = `_${safe}`;
18
+ if (safe.length > 100 || safe.toLowerCase() !== withoutThinking.toLowerCase()) {
19
+ const digest = createHash("sha256").update(withoutThinking.toLowerCase()).digest("hex").slice(0, 8);
20
+ safe = `${safe.slice(0, 90)}-${digest}`;
21
+ }
22
+ return safe.toLowerCase();
23
+ }
24
+ export function getModelInstructionsPath(agentDir, modelId) {
25
+ return join(getInstructionsRoot(agentDir), modelInstructionFolderName(modelId), "AGENTS.md");
26
+ }
27
+ export function isUserInstructionsPath(filePath, agentDir) {
28
+ const rel = relative(resolve(getInstructionsRoot(agentDir)), resolve(filePath));
29
+ return rel === "" || (!isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${sep}`));
30
+ }
31
+ export function migrateLegacyGlobalInstructions(agentDir) {
32
+ const legacyPath = join(resolve(agentDir), "AGENTS.md");
33
+ const destinationPath = getGlobalInstructionsPath(agentDir);
34
+ if (!existsSync(legacyPath))
35
+ return { status: "absent" };
36
+ if (existsSync(destinationPath)) {
37
+ return {
38
+ status: "conflict",
39
+ diagnostic: `Both legacy '${legacyPath}' and current '${destinationPath}' exist. Kept both and will use the current file.`,
40
+ };
41
+ }
42
+ try {
43
+ mkdirSync(getInstructionsRoot(agentDir), { recursive: true });
44
+ renameSync(legacyPath, destinationPath);
45
+ return { status: "migrated" };
46
+ }
47
+ catch (error) {
48
+ return {
49
+ status: "failed",
50
+ diagnostic: `Could not migrate '${legacyPath}' to '${destinationPath}': ${error instanceof Error ? error.message : String(error)}`,
51
+ };
52
+ }
53
+ }
54
+ function concreteModelId(value) {
55
+ if (typeof value !== "string")
56
+ return undefined;
57
+ const trimmed = value.trim();
58
+ if (!trimmed || /[*?{}]/.test(trimmed))
59
+ return undefined;
60
+ return trimmed.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/i, "");
61
+ }
62
+ export function ensureModelInstructionDirs(agentDir, settings, currentModel) {
63
+ const modelIds = new Set();
64
+ const add = (value) => {
65
+ const id = concreteModelId(value);
66
+ if (id)
67
+ modelIds.add(id);
68
+ };
69
+ add(currentModel?.id);
70
+ add(settings.defaultModel);
71
+ for (const value of settings.enabledModels ?? [])
72
+ add(value);
73
+ add(settings.modelTiers?.light);
74
+ add(settings.modelTiers?.standard);
75
+ add(settings.modelTiers?.heavy);
76
+ const diagnostics = [];
77
+ for (const id of modelIds) {
78
+ try {
79
+ mkdirSync(join(getInstructionsRoot(agentDir), modelInstructionFolderName(id)), { recursive: true });
80
+ }
81
+ catch (error) {
82
+ diagnostics.push(`Could not prepare model-instruction folder for '${id}': ${error instanceof Error ? error.message : String(error)}`);
83
+ }
84
+ }
85
+ return diagnostics;
86
+ }
87
+ function safeReadInstruction(path) {
88
+ try {
89
+ if (!existsSync(path) || !statSync(path).isFile())
90
+ return undefined;
91
+ return { path, content: readFileSync(path, "utf8") };
92
+ }
93
+ catch {
94
+ return undefined;
95
+ }
96
+ }
97
+ export function loadSelectedUserInstructions(options) {
98
+ ensureModelInstructionDirs(options.agentDir, options.settingsManager.getGlobalSettings(), options.model);
99
+ const enabled = options.settingsManager.getModelInstructionsEnabled();
100
+ const mode = options.settingsManager.getModelInstructionsMode();
101
+ const files = [];
102
+ if (!enabled || mode === "both") {
103
+ const globalFile = safeReadInstruction(getGlobalInstructionsPath(options.agentDir));
104
+ if (globalFile)
105
+ files.push(globalFile);
106
+ }
107
+ if (enabled && options.model) {
108
+ const modelFile = safeReadInstruction(getModelInstructionsPath(options.agentDir, options.model.id));
109
+ if (modelFile)
110
+ files.push(modelFile);
111
+ }
112
+ return files;
113
+ }
114
+ export function instructionPathLabel(filePath, agentDir) {
115
+ const resolved = resolve(filePath);
116
+ if (resolved.toLowerCase() === resolve(getGlobalInstructionsPath(agentDir)).toLowerCase()) {
117
+ return "Global AGENTS.md";
118
+ }
119
+ if (!isUserInstructionsPath(resolved, agentDir))
120
+ return undefined;
121
+ return `${basename(resolve(resolved, ".."))} AGENTS.md`;
122
+ }
123
+ //# sourceMappingURL=model-instructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-instructions.js","sourceRoot":"","sources":["../../src/core/model-instructions.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, statSync } from \"node:fs\";\nimport { basename, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport type { Model } from \"@ashx-j/lunr-ai\";\nimport type { Settings, SettingsManager } from \"./settings-manager.ts\";\n\nexport type ModelInstructionsMode = \"both\" | \"model-only\";\n\nexport interface ModelInstructionsMigrationResult {\n\tstatus: \"absent\" | \"migrated\" | \"conflict\" | \"failed\";\n\tdiagnostic?: string;\n}\n\nexport function getInstructionsRoot(agentDir: string): string {\n\treturn join(resolve(agentDir), \"agents\");\n}\n\nexport function getGlobalInstructionsPath(agentDir: string): string {\n\treturn join(getInstructionsRoot(agentDir), \"AGENTS.md\");\n}\n\nexport function modelInstructionFolderName(modelId: string): string {\n\tconst finalSegment = modelId.trim().split(/[\\\\/]/).filter(Boolean).at(-1) ?? \"model\";\n\tconst withoutThinking = finalSegment.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/i, \"\");\n\tlet safe = withoutThinking.replace(/[<>:\"/\\\\|?*\\u0000-\\u001f]/g, \"_\").replace(/[. ]+$/g, \"\");\n\tif (!safe || safe === \".\" || safe === \"..\") safe = \"model\";\n\tif (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\\.|$)/i.test(safe)) safe = `_${safe}`;\n\tif (safe.length > 100 || safe.toLowerCase() !== withoutThinking.toLowerCase()) {\n\t\tconst digest = createHash(\"sha256\").update(withoutThinking.toLowerCase()).digest(\"hex\").slice(0, 8);\n\t\tsafe = `${safe.slice(0, 90)}-${digest}`;\n\t}\n\treturn safe.toLowerCase();\n}\n\nexport function getModelInstructionsPath(agentDir: string, modelId: string): string {\n\treturn join(getInstructionsRoot(agentDir), modelInstructionFolderName(modelId), \"AGENTS.md\");\n}\n\nexport function isUserInstructionsPath(filePath: string, agentDir: string): boolean {\n\tconst rel = relative(resolve(getInstructionsRoot(agentDir)), resolve(filePath));\n\treturn rel === \"\" || (!isAbsolute(rel) && rel !== \"..\" && !rel.startsWith(`..${sep}`));\n}\n\nexport function migrateLegacyGlobalInstructions(agentDir: string): ModelInstructionsMigrationResult {\n\tconst legacyPath = join(resolve(agentDir), \"AGENTS.md\");\n\tconst destinationPath = getGlobalInstructionsPath(agentDir);\n\tif (!existsSync(legacyPath)) return { status: \"absent\" };\n\tif (existsSync(destinationPath)) {\n\t\treturn {\n\t\t\tstatus: \"conflict\",\n\t\t\tdiagnostic: `Both legacy '${legacyPath}' and current '${destinationPath}' exist. Kept both and will use the current file.`,\n\t\t};\n\t}\n\ttry {\n\t\tmkdirSync(getInstructionsRoot(agentDir), { recursive: true });\n\t\trenameSync(legacyPath, destinationPath);\n\t\treturn { status: \"migrated\" };\n\t} catch (error) {\n\t\treturn {\n\t\t\tstatus: \"failed\",\n\t\t\tdiagnostic: `Could not migrate '${legacyPath}' to '${destinationPath}': ${error instanceof Error ? error.message : String(error)}`,\n\t\t};\n\t}\n}\n\nfunction concreteModelId(value: unknown): string | undefined {\n\tif (typeof value !== \"string\") return undefined;\n\tconst trimmed = value.trim();\n\tif (!trimmed || /[*?{}]/.test(trimmed)) return undefined;\n\treturn trimmed.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/i, \"\");\n}\n\nexport function ensureModelInstructionDirs(\n\tagentDir: string,\n\tsettings: Pick<Settings, \"defaultModel\" | \"enabledModels\" | \"modelTiers\">,\n\tcurrentModel?: Pick<Model<any>, \"id\">,\n): string[] {\n\tconst modelIds = new Set<string>();\n\tconst add = (value: unknown) => {\n\t\tconst id = concreteModelId(value);\n\t\tif (id) modelIds.add(id);\n\t};\n\tadd(currentModel?.id);\n\tadd(settings.defaultModel);\n\tfor (const value of settings.enabledModels ?? []) add(value);\n\tadd(settings.modelTiers?.light);\n\tadd(settings.modelTiers?.standard);\n\tadd(settings.modelTiers?.heavy);\n\n\tconst diagnostics: string[] = [];\n\tfor (const id of modelIds) {\n\t\ttry {\n\t\t\tmkdirSync(join(getInstructionsRoot(agentDir), modelInstructionFolderName(id)), { recursive: true });\n\t\t} catch (error) {\n\t\t\tdiagnostics.push(\n\t\t\t\t`Could not prepare model-instruction folder for '${id}': ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t}\n\treturn diagnostics;\n}\n\nfunction safeReadInstruction(path: string): { path: string; content: string } | undefined {\n\ttry {\n\t\tif (!existsSync(path) || !statSync(path).isFile()) return undefined;\n\t\treturn { path, content: readFileSync(path, \"utf8\") };\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function loadSelectedUserInstructions(options: {\n\tagentDir: string;\n\tsettingsManager: SettingsManager;\n\tmodel?: Pick<Model<any>, \"id\">;\n}): Array<{ path: string; content: string }> {\n\tensureModelInstructionDirs(options.agentDir, options.settingsManager.getGlobalSettings(), options.model);\n\tconst enabled = options.settingsManager.getModelInstructionsEnabled();\n\tconst mode = options.settingsManager.getModelInstructionsMode();\n\tconst files: Array<{ path: string; content: string }> = [];\n\tif (!enabled || mode === \"both\") {\n\t\tconst globalFile = safeReadInstruction(getGlobalInstructionsPath(options.agentDir));\n\t\tif (globalFile) files.push(globalFile);\n\t}\n\tif (enabled && options.model) {\n\t\tconst modelFile = safeReadInstruction(getModelInstructionsPath(options.agentDir, options.model.id));\n\t\tif (modelFile) files.push(modelFile);\n\t}\n\treturn files;\n}\n\nexport function instructionPathLabel(filePath: string, agentDir: string): string | undefined {\n\tconst resolved = resolve(filePath);\n\tif (resolved.toLowerCase() === resolve(getGlobalInstructionsPath(agentDir)).toLowerCase()) {\n\t\treturn \"Global AGENTS.md\";\n\t}\n\tif (!isUserInstructionsPath(resolved, agentDir)) return undefined;\n\treturn `${basename(resolve(resolved, \"..\"))} AGENTS.md`;\n}\n"]}
@@ -33,13 +33,13 @@ export interface ApprovalRequest {
33
33
  toolName: string;
34
34
  /** "bash" (detail = command), "edit"/"write" (detail = path),
35
35
  * "edit-outside"/"write-outside" when path escapes cwd,
36
- * "subagent-full" for full-access children, or "swarm" for swarms. */
36
+ * "subagent-full" for full-access children, or "large-subagent-launch" for aggregate confirmation. */
37
37
  action: string;
38
38
  detail: string;
39
- /** "swarm" when this is the agent-swarm approval prompt, "plan" for the
39
+ /** "large-subagent-launch" for aggregate child confirmation, "plan" for the
40
40
  * present_plan plan-approval prompt — the UI renders a dedicated dialog
41
41
  * for each instead of the generic tool dialog. */
42
- kind?: "swarm" | "plan";
42
+ kind?: "large-subagent-launch" | "plan";
43
43
  }
44
44
  export type ApprovalDecision = "once" | "session" | "reject";
45
45
  /** Handlers may return a bare decision string, a reject carrying user feedback
@@ -53,8 +53,8 @@ export type ApprovalResponse = ApprovalDecision | {
53
53
  feedback?: string;
54
54
  };
55
55
  export declare const NO_HANDLER_REASON = "Mutating tool blocked in manual mode: no approval channel available.";
56
- export declare const SWARM_REJECT_REASON = "Agent swarm rejected by user.";
57
- export declare const NO_SWARM_HANDLER_REASON = "Agent swarm blocked: no approval channel available.";
56
+ export declare const LARGE_SUBAGENT_LAUNCH_REJECT_REASON = "Large subagent launch rejected by user.";
57
+ export declare const NO_LARGE_SUBAGENT_LAUNCH_HANDLER_REASON = "Large subagent launch blocked: no approval channel available.";
58
58
  export declare function createPermissionContext(sessionId: string, mode?: PermissionMode): void;
59
59
  export declare function deletePermissionContext(sessionId: string): void;
60
60
  export declare function getPermissionMode(sessionId?: string): PermissionMode;
@@ -68,7 +68,7 @@ export declare const PLAN_PASS_THROUGH_TEXT = "Plan presented. The user will rev
68
68
  /** lunr: map an approval response to the model-facing present_plan result text. */
69
69
  export declare function planApprovalResultText(resp: ApprovalResponse): string;
70
70
  /**
71
- * lunr: present_plan approval request. Unlike the mutating-tool and swarm gates
71
+ * lunr: present_plan approval request. Unlike mutating-tool and aggregate launch gates
72
72
  * this fails OPEN when no approval handler is registered (or none is reachable,
73
73
  * e.g. a gateway turn without a chat context): headless sessions have no one to
74
74
  * show the dialog to, so the plan is presented and the user replies in chat
@@ -80,14 +80,15 @@ export declare function clearSessionApprovals(sessionId?: string): void;
80
80
  export declare function resetAllPermissionContexts(): void;
81
81
  /** Appended to the system prompt while auto mode is active. */
82
82
  export declare const AUTO_MODE_ADDENDUM = "You are running fully autonomously. Do not ask the user questions or wait for confirmation; make reasonable decisions and complete the task end-to-end.";
83
- export declare const GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON = "The global AGENTS.md file is user-managed. The agent cannot change ~/.lunr/agent/AGENTS.md.";
83
+ export declare const GLOBAL_AGENTS_FILE_WRITE_BLOCK_REASON = "The agents instruction tree is user-managed. The agent cannot change ~/.lunr/agent/agents/.";
84
84
  export declare const MEMORY_FILE_DIRECT_WRITE_BLOCK_REASON = "Memory is model-managed through the memory tools. Do not directly change ~/.lunr/simple-memory/memory.md.";
85
85
  export interface GateOptions {
86
- /** True when the current turn started from an explicit /swarm prompt — the
87
- * user already asked for the swarm, so the swarm approval gate is skipped. */
86
+ /** Global preference for aggregate confirmation. Defaults to enabled. */
87
+ confirmLargeSubagentLaunches?: boolean;
88
+ /** True when the current turn started from an explicit /swarm prompt. */
88
89
  explicitSwarmTurn?: boolean;
89
90
  /** Assistant message that issued this tool call. Same-turn sibling SINGLE
90
- * `subagent` calls count toward the swarm threshold. */
91
+ * `subagent` calls count toward the aggregate threshold. */
91
92
  assistantMessage?: {
92
93
  content?: ReadonlyArray<{
93
94
  type?: string;