open-multi-agent-kit 0.80.8 → 0.90.3

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/CHANGELOG.md +44 -0
  2. package/README.md +21 -31
  3. package/dist/cli/args.d.ts +0 -2
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +2 -7
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-session.d.ts +2 -0
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +41 -5
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/automation/index.d.ts +359 -0
  12. package/dist/core/automation/index.d.ts.map +1 -0
  13. package/dist/core/automation/index.js +665 -0
  14. package/dist/core/automation/index.js.map +1 -0
  15. package/dist/core/bang-skill-invocation.d.ts +26 -0
  16. package/dist/core/bang-skill-invocation.d.ts.map +1 -0
  17. package/dist/core/bang-skill-invocation.js +59 -0
  18. package/dist/core/bang-skill-invocation.js.map +1 -0
  19. package/dist/core/browser-use.d.ts +8 -0
  20. package/dist/core/browser-use.d.ts.map +1 -1
  21. package/dist/core/browser-use.js +61 -23
  22. package/dist/core/browser-use.js.map +1 -1
  23. package/dist/core/compactor.d.ts.map +1 -1
  24. package/dist/core/compactor.js +0 -3
  25. package/dist/core/compactor.js.map +1 -1
  26. package/dist/core/context-budget-governor-v2.d.ts +2 -1
  27. package/dist/core/context-budget-governor-v2.d.ts.map +1 -1
  28. package/dist/core/context-budget-governor-v2.js +1 -0
  29. package/dist/core/context-budget-governor-v2.js.map +1 -1
  30. package/dist/core/context-budget-headroom-types.d.ts +8 -0
  31. package/dist/core/context-budget-headroom-types.d.ts.map +1 -1
  32. package/dist/core/context-budget-headroom-types.js.map +1 -1
  33. package/dist/core/context-budget-headroom.d.ts +1 -1
  34. package/dist/core/context-budget-headroom.d.ts.map +1 -1
  35. package/dist/core/context-budget-headroom.js +17 -4
  36. package/dist/core/context-budget-headroom.js.map +1 -1
  37. package/dist/core/context-budget-system-prompt.d.ts +2 -1
  38. package/dist/core/context-budget-system-prompt.d.ts.map +1 -1
  39. package/dist/core/context-budget-system-prompt.js +6 -1
  40. package/dist/core/context-budget-system-prompt.js.map +1 -1
  41. package/dist/core/context-budget-v2-cache-hash.d.ts +3 -0
  42. package/dist/core/context-budget-v2-cache-hash.d.ts.map +1 -0
  43. package/dist/core/context-budget-v2-cache-hash.js +16 -0
  44. package/dist/core/context-budget-v2-cache-hash.js.map +1 -0
  45. package/dist/core/context-budget-v2-cache-keys.d.ts +58 -0
  46. package/dist/core/context-budget-v2-cache-keys.d.ts.map +1 -0
  47. package/dist/core/context-budget-v2-cache-keys.js +128 -0
  48. package/dist/core/context-budget-v2-cache-keys.js.map +1 -0
  49. package/dist/core/context-budget-v2-cache-provider.d.ts +23 -0
  50. package/dist/core/context-budget-v2-cache-provider.d.ts.map +1 -0
  51. package/dist/core/context-budget-v2-cache-provider.js +29 -0
  52. package/dist/core/context-budget-v2-cache-provider.js.map +1 -0
  53. package/dist/core/context-budget-v2-cache-telemetry.d.ts +36 -0
  54. package/dist/core/context-budget-v2-cache-telemetry.d.ts.map +1 -0
  55. package/dist/core/context-budget-v2-cache-telemetry.js +64 -0
  56. package/dist/core/context-budget-v2-cache-telemetry.js.map +1 -0
  57. package/dist/core/context-budget-v2-cache-validation.d.ts +36 -0
  58. package/dist/core/context-budget-v2-cache-validation.d.ts.map +1 -0
  59. package/dist/core/context-budget-v2-cache-validation.js +98 -0
  60. package/dist/core/context-budget-v2-cache-validation.js.map +1 -0
  61. package/dist/core/context-budget-v2-cache.d.ts +36 -0
  62. package/dist/core/context-budget-v2-cache.d.ts.map +1 -0
  63. package/dist/core/context-budget-v2-cache.js +50 -0
  64. package/dist/core/context-budget-v2-cache.js.map +1 -0
  65. package/dist/core/context-budget-v2-plan-cache-keys.d.ts +16 -0
  66. package/dist/core/context-budget-v2-plan-cache-keys.d.ts.map +1 -0
  67. package/dist/core/context-budget-v2-plan-cache-keys.js +49 -0
  68. package/dist/core/context-budget-v2-plan-cache-keys.js.map +1 -0
  69. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  70. package/dist/core/context-budget-v2-planner.js +55 -2
  71. package/dist/core/context-budget-v2-planner.js.map +1 -1
  72. package/dist/core/context-budget-v2-representation-cache.d.ts +27 -0
  73. package/dist/core/context-budget-v2-representation-cache.d.ts.map +1 -0
  74. package/dist/core/context-budget-v2-representation-cache.js +173 -0
  75. package/dist/core/context-budget-v2-representation-cache.js.map +1 -0
  76. package/dist/core/context-budget-v2-selection.d.ts +2 -0
  77. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  78. package/dist/core/context-budget-v2-selection.js +12 -2
  79. package/dist/core/context-budget-v2-selection.js.map +1 -1
  80. package/dist/core/context-budget-v2-types.d.ts +107 -1
  81. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  82. package/dist/core/context-budget-v2-types.js.map +1 -1
  83. package/dist/core/diagnostics.d.ts +2 -0
  84. package/dist/core/diagnostics.d.ts.map +1 -1
  85. package/dist/core/diagnostics.js.map +1 -1
  86. package/dist/core/extensions/builtin/todo-checklist.d.ts.map +1 -1
  87. package/dist/core/extensions/builtin/todo-checklist.js +3 -0
  88. package/dist/core/extensions/builtin/todo-checklist.js.map +1 -1
  89. package/dist/core/footer-data-provider.d.ts +3 -1
  90. package/dist/core/footer-data-provider.d.ts.map +1 -1
  91. package/dist/core/footer-data-provider.js +4 -0
  92. package/dist/core/footer-data-provider.js.map +1 -1
  93. package/dist/core/headroom.d.ts.map +1 -1
  94. package/dist/core/headroom.js +0 -4
  95. package/dist/core/headroom.js.map +1 -1
  96. package/dist/core/hook-inventory.d.ts.map +1 -1
  97. package/dist/core/hook-inventory.js +6 -0
  98. package/dist/core/hook-inventory.js.map +1 -1
  99. package/dist/core/mcp-computer-use-presets.d.ts +64 -0
  100. package/dist/core/mcp-computer-use-presets.d.ts.map +1 -0
  101. package/dist/core/mcp-computer-use-presets.js +158 -0
  102. package/dist/core/mcp-computer-use-presets.js.map +1 -0
  103. package/dist/core/mcp-public-presets.d.ts +578 -0
  104. package/dist/core/mcp-public-presets.d.ts.map +1 -0
  105. package/dist/core/mcp-public-presets.js +647 -0
  106. package/dist/core/mcp-public-presets.js.map +1 -0
  107. package/dist/core/model-registry.d.ts.map +1 -1
  108. package/dist/core/model-registry.js +1 -0
  109. package/dist/core/model-registry.js.map +1 -1
  110. package/dist/core/model-resolver.d.ts.map +1 -1
  111. package/dist/core/model-resolver.js +1 -0
  112. package/dist/core/model-resolver.js.map +1 -1
  113. package/dist/core/pi-package-intake-candidates.d.ts +34 -0
  114. package/dist/core/pi-package-intake-candidates.d.ts.map +1 -0
  115. package/dist/core/pi-package-intake-candidates.js +291 -0
  116. package/dist/core/pi-package-intake-candidates.js.map +1 -0
  117. package/dist/core/pi-package-intake.d.ts +50 -0
  118. package/dist/core/pi-package-intake.d.ts.map +1 -0
  119. package/dist/core/pi-package-intake.js +132 -0
  120. package/dist/core/pi-package-intake.js.map +1 -0
  121. package/dist/core/provider-display-names.d.ts.map +1 -1
  122. package/dist/core/provider-display-names.js +1 -0
  123. package/dist/core/provider-display-names.js.map +1 -1
  124. package/dist/core/resource-loader.d.ts.map +1 -1
  125. package/dist/core/resource-loader.js +1 -0
  126. package/dist/core/resource-loader.js.map +1 -1
  127. package/dist/core/reverse-skill.d.ts +2 -0
  128. package/dist/core/reverse-skill.d.ts.map +1 -0
  129. package/dist/core/reverse-skill.js +7 -0
  130. package/dist/core/reverse-skill.js.map +1 -0
  131. package/dist/core/settings-manager.d.ts +3 -3
  132. package/dist/core/settings-manager.d.ts.map +1 -1
  133. package/dist/core/settings-manager.js.map +1 -1
  134. package/dist/core/skills.d.ts +4 -0
  135. package/dist/core/skills.d.ts.map +1 -1
  136. package/dist/core/skills.js +115 -43
  137. package/dist/core/skills.js.map +1 -1
  138. package/dist/core/system-prompt.d.ts +2 -0
  139. package/dist/core/system-prompt.d.ts.map +1 -1
  140. package/dist/core/system-prompt.js +58 -5
  141. package/dist/core/system-prompt.js.map +1 -1
  142. package/dist/core/todo-runtime-state.d.ts +5 -0
  143. package/dist/core/todo-runtime-state.d.ts.map +1 -0
  144. package/dist/core/todo-runtime-state.js +12 -0
  145. package/dist/core/todo-runtime-state.js.map +1 -0
  146. package/dist/index.d.ts +2 -0
  147. package/dist/index.d.ts.map +1 -1
  148. package/dist/index.js +2 -0
  149. package/dist/index.js.map +1 -1
  150. package/dist/migrations.d.ts +19 -0
  151. package/dist/migrations.d.ts.map +1 -1
  152. package/dist/migrations.js +39 -15
  153. package/dist/migrations.js.map +1 -1
  154. package/dist/modes/interactive/components/control-panel-box.d.ts +3 -3
  155. package/dist/modes/interactive/components/control-panel-box.d.ts.map +1 -1
  156. package/dist/modes/interactive/components/control-panel-box.js +10 -10
  157. package/dist/modes/interactive/components/control-panel-box.js.map +1 -1
  158. package/dist/modes/interactive/components/control-panel-gradient-motion.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/control-panel-gradient-motion.js +2 -1
  160. package/dist/modes/interactive/components/control-panel-gradient-motion.js.map +1 -1
  161. package/dist/modes/interactive/components/control-panel-layout.d.ts +22 -1
  162. package/dist/modes/interactive/components/control-panel-layout.d.ts.map +1 -1
  163. package/dist/modes/interactive/components/control-panel-layout.js +244 -68
  164. package/dist/modes/interactive/components/control-panel-layout.js.map +1 -1
  165. package/dist/modes/interactive/components/control-panel-runtime-status.d.ts +2 -1
  166. package/dist/modes/interactive/components/control-panel-runtime-status.d.ts.map +1 -1
  167. package/dist/modes/interactive/components/control-panel-runtime-status.js +20 -1
  168. package/dist/modes/interactive/components/control-panel-runtime-status.js.map +1 -1
  169. package/dist/modes/interactive/components/control-panel-sparkles.d.ts +27 -0
  170. package/dist/modes/interactive/components/control-panel-sparkles.d.ts.map +1 -0
  171. package/dist/modes/interactive/components/control-panel-sparkles.js +42 -0
  172. package/dist/modes/interactive/components/control-panel-sparkles.js.map +1 -0
  173. package/dist/modes/interactive/components/control-panel.d.ts +9 -0
  174. package/dist/modes/interactive/components/control-panel.d.ts.map +1 -1
  175. package/dist/modes/interactive/components/control-panel.js +50 -6
  176. package/dist/modes/interactive/components/control-panel.js.map +1 -1
  177. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  178. package/dist/modes/interactive/components/footer.js +6 -0
  179. package/dist/modes/interactive/components/footer.js.map +1 -1
  180. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  181. package/dist/modes/interactive/components/settings-selector.js +2 -1
  182. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  183. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  184. package/dist/modes/interactive/components/thinking-selector.js +2 -1
  185. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  186. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  187. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  188. package/dist/modes/interactive/interactive-mode.js +70 -15
  189. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  190. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  191. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  192. package/dist/modes/interactive/theme/theme.js +5 -0
  193. package/dist/modes/interactive/theme/theme.js.map +1 -1
  194. package/docs/providers.md +12 -0
  195. package/docs/skills.md +8 -4
  196. package/docs/usage.md +4 -3
  197. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  198. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  199. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  200. package/examples/extensions/gondolin/package-lock.json +2 -2
  201. package/examples/extensions/gondolin/package.json +1 -1
  202. package/examples/extensions/preset.ts +2 -2
  203. package/examples/extensions/sandbox/package-lock.json +2 -2
  204. package/examples/extensions/sandbox/package.json +1 -1
  205. package/examples/extensions/with-deps/package-lock.json +2 -2
  206. package/examples/extensions/with-deps/package.json +1 -1
  207. package/npm-shrinkwrap.json +27 -34
  208. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"footer-data-provider.d.ts","sourceRoot":"","sources":["../../src/core/footer-data-provider.ts"],"names":[],"mappings":"AA+FA;;;GAGG;AACH,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAO;IAEhD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,QAAQ,CAA0C;IAC1D,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,qBAAqB,CAA4D;IACzF,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,yBAAyB,CAA0B;IAC3D,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,qBAAqB,CAAyB;IACtD,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,YAAY,CAA8C;IAClE,OAAO,CAAC,oBAAoB,CAA8C;IAC1E,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAiB;IAEvC,YAAY,GAAG,EAAE,MAAM,EAMtB;IAED,2EAA2E;IAC3E,YAAY,IAAI,MAAM,GAAG,IAAI,CAK5B;IAED,wDAAwD;IACxD,oBAAoB,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAElD;IAED,qEAAqE;IACrE,cAAc,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAG/C;IAED,qCAAqC;IACrC,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAM9D;IAED,yCAAyC;IACzC,sBAAsB,IAAI,IAAI,CAE7B;IAED,4EAA4E;IAC5E,yBAAyB,IAAI,MAAM,CAElC;IAED,6EAA6E;IAC7E,aAAa,IAAI,MAAM,GAAG,IAAI,CAE7B;IAED,kFAAkF;IAClF,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAEjC;IAED,gDAAgD;IAChD,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE7C;IAED,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAexB;IAED,wBAAwB;IACxB,OAAO,IAAI,IAAI,CASd;IAED,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,eAAe;YAYT,qBAAqB;IA0BnC,OAAO,CAAC,oBAAoB;YAcd,qBAAqB;IAgBnC,OAAO,CAAC,gBAAgB;IAsBxB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,eAAe;CA2EvB;AAED,yGAAyG;AACzG,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC5C,kBAAkB,EAChB,cAAc,GACd,sBAAsB,GACtB,2BAA2B,GAC3B,gBAAgB,GAChB,eAAe,GACf,mBAAmB,CACrB,CAAC","sourcesContent":["import { type ExecFileException, execFile, spawnSync } from \"child_process\";\nimport { existsSync, type FSWatcher, readFileSync, type Stats, statSync, unwatchFile, watchFile } from \"fs\";\nimport { dirname, join, resolve } from \"path\";\nimport { closeWatcher, FS_WATCH_RETRY_DELAY_MS, watchWithErrorHandler } from \"../utils/fs-watch.ts\";\nimport { MetricsSampler } from \"./metrics-sampler.ts\";\n\ntype GitPaths = {\n\trepoDir: string;\n\tcommonGitDir: string;\n\theadPath: string;\n};\n\n/**\n * Find git metadata paths by walking up from cwd.\n * Handles both regular git repos (.git is a directory) and worktrees (.git is a file).\n */\nfunction findGitPaths(cwd: string): GitPaths | null {\n\tlet dir = cwd;\n\twhile (true) {\n\t\tconst gitPath = join(dir, \".git\");\n\t\tif (existsSync(gitPath)) {\n\t\t\ttry {\n\t\t\t\tconst stat = statSync(gitPath);\n\t\t\t\tif (stat.isFile()) {\n\t\t\t\t\tconst content = readFileSync(gitPath, \"utf8\").trim();\n\t\t\t\t\tif (content.startsWith(\"gitdir: \")) {\n\t\t\t\t\t\tconst gitDir = resolve(dir, content.slice(8).trim());\n\t\t\t\t\t\tconst headPath = join(gitDir, \"HEAD\");\n\t\t\t\t\t\tif (!existsSync(headPath)) return null;\n\t\t\t\t\t\tconst commonDirPath = join(gitDir, \"commondir\");\n\t\t\t\t\t\tconst commonGitDir = existsSync(commonDirPath)\n\t\t\t\t\t\t\t? resolve(gitDir, readFileSync(commonDirPath, \"utf8\").trim())\n\t\t\t\t\t\t\t: gitDir;\n\t\t\t\t\t\treturn { repoDir: dir, commonGitDir, headPath };\n\t\t\t\t\t}\n\t\t\t\t} else if (stat.isDirectory()) {\n\t\t\t\t\tconst headPath = join(gitPath, \"HEAD\");\n\t\t\t\t\tif (!existsSync(headPath)) return null;\n\t\t\t\t\treturn { repoDir: dir, commonGitDir: gitPath, headPath };\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Ask git for the current branch. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitSync(repoDir: string): string | null {\n\tconst result = spawnSync(\"git\", [\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"], {\n\t\tcwd: repoDir,\n\t\tencoding: \"utf8\",\n\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t});\n\tconst branch = result.status === 0 ? result.stdout.trim() : \"\";\n\treturn branch || null;\n}\n\n/** Ask git for the current branch asynchronously. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitAsync(repoDir: string): Promise<string | null> {\n\treturn new Promise((resolvePromise) => {\n\t\texecFile(\n\t\t\t\"git\",\n\t\t\t[\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"],\n\t\t\t{\n\t\t\t\tcwd: repoDir,\n\t\t\t\tencoding: \"utf8\",\n\t\t\t},\n\t\t\t(error: ExecFileException | null, stdout: string) => {\n\t\t\t\tif (error) {\n\t\t\t\t\tresolvePromise(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst branch = stdout.trim();\n\t\t\t\tresolvePromise(branch || null);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction isWslEnvironment(): boolean {\n\treturn process.platform === \"linux\" && !!(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);\n}\n\nfunction isWindowsMountedRepoPath(repoDir: string): boolean {\n\treturn /^\\/mnt\\/[a-z](?:\\/|$)/i.test(repoDir);\n}\n\nfunction shouldPollGitHead(repoDir: string): boolean {\n\treturn isWslEnvironment() && isWindowsMountedRepoPath(repoDir);\n}\n\n/**\n * Provides git branch and extension statuses - data not otherwise accessible to extensions.\n * Token stats, model info available via ctx.sessionManager and ctx.model.\n */\nexport class FooterDataProvider {\n\tprivate cwd: string;\n\tprivate static readonly WATCH_DEBOUNCE_MS = 500;\n\n\tprivate extensionStatuses = new Map<string, string>();\n\tprivate cachedBranch: string | null | undefined = undefined;\n\tprivate gitPaths: GitPaths | null | undefined = undefined;\n\tprivate headWatcher: FSWatcher | null = null;\n\tprivate headWatchFilePath: string | null = null;\n\tprivate headWatchFileListener: ((current: Stats, previous: Stats) => void) | null = null;\n\tprivate reftableWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListPath: string | null = null;\n\tprivate branchChangeCallbacks = new Set<() => void>();\n\tprivate availableProviderCount = 0;\n\tprivate refreshTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate gitWatcherRetryTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate refreshInFlight = false;\n\tprivate refreshPending = false;\n\tprivate disposed = false;\n\tprivate metricsSampler: MetricsSampler;\n\n\tconstructor(cwd: string) {\n\t\tthis.cwd = cwd;\n\t\tthis.gitPaths = findGitPaths(cwd);\n\t\tthis.setupGitWatcher();\n\t\tthis.metricsSampler = new MetricsSampler();\n\t\tthis.metricsSampler.start();\n\t}\n\n\t/** Current git branch, null if not in repo, \"detached\" if detached HEAD */\n\tgetGitBranch(): string | null {\n\t\tif (this.cachedBranch === undefined) {\n\t\t\tthis.cachedBranch = this.resolveGitBranchSync();\n\t\t}\n\t\treturn this.cachedBranch;\n\t}\n\n\t/** Extension status texts set via ctx.ui.setStatus() */\n\tgetExtensionStatuses(): ReadonlyMap<string, string> {\n\t\treturn this.extensionStatuses;\n\t}\n\n\t/** Subscribe to git branch changes. Returns unsubscribe function. */\n\tonBranchChange(callback: () => void): () => void {\n\t\tthis.branchChangeCallbacks.add(callback);\n\t\treturn () => this.branchChangeCallbacks.delete(callback);\n\t}\n\n\t/** Internal: set extension status */\n\tsetExtensionStatus(key: string, text: string | undefined): void {\n\t\tif (text === undefined) {\n\t\t\tthis.extensionStatuses.delete(key);\n\t\t} else {\n\t\t\tthis.extensionStatuses.set(key, text);\n\t\t}\n\t}\n\n\t/** Internal: clear extension statuses */\n\tclearExtensionStatuses(): void {\n\t\tthis.extensionStatuses.clear();\n\t}\n\n\t/** Number of unique providers with available models (for footer display) */\n\tgetAvailableProviderCount(): number {\n\t\treturn this.availableProviderCount;\n\t}\n\n\t/** Most recent process CPU percent (0-100), or `null` if not yet sampled. */\n\tgetCpuPercent(): number | null {\n\t\treturn this.metricsSampler.getCpuPercent();\n\t}\n\n\t/** Most recent process RSS memory size in bytes, or `null` if not yet sampled. */\n\tgetMemoryRssBytes(): number | null {\n\t\treturn this.metricsSampler.getMemoryRssBytes();\n\t}\n\n\t/** Internal: update available provider count */\n\tsetAvailableProviderCount(count: number): void {\n\t\tthis.availableProviderCount = count;\n\t}\n\n\tsetCwd(cwd: string): void {\n\t\tif (this.cwd === cwd) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.cwd = cwd;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.cachedBranch = undefined;\n\t\tthis.gitPaths = findGitPaths(cwd);\n\t\tthis.setupGitWatcher();\n\t\tthis.notifyBranchChange();\n\t}\n\n\t/** Internal: cleanup */\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.branchChangeCallbacks.clear();\n\t\tthis.metricsSampler.stop();\n\t}\n\n\tprivate notifyBranchChange(): void {\n\t\tfor (const cb of this.branchChangeCallbacks) cb();\n\t}\n\n\tprivate scheduleRefresh(): void {\n\t\tif (this.disposed || this.refreshTimer) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\t\tthis.refreshTimer = setTimeout(() => {\n\t\t\tthis.refreshTimer = null;\n\t\t\tvoid this.refreshGitBranchAsync();\n\t\t}, FooterDataProvider.WATCH_DEBOUNCE_MS);\n\t}\n\n\tprivate async refreshGitBranchAsync(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.refreshInFlight = true;\n\t\ttry {\n\t\t\tconst nextBranch = await this.resolveGitBranchAsync();\n\t\t\tif (this.disposed) return;\n\t\t\tif (this.cachedBranch !== undefined && this.cachedBranch !== nextBranch) {\n\t\t\t\tthis.cachedBranch = nextBranch;\n\t\t\t\tthis.notifyBranchChange();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.cachedBranch = nextBranch;\n\t\t} finally {\n\t\t\tthis.refreshInFlight = false;\n\t\t\tif (this.refreshPending && !this.disposed) {\n\t\t\t\tthis.refreshPending = false;\n\t\t\t\tthis.scheduleRefresh();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate resolveGitBranchSync(): string | null {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = readFileSync(this.gitPaths.headPath, \"utf8\").trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\" ? (resolveBranchWithGitSync(this.gitPaths.repoDir) ?? \"detached\") : branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate async resolveGitBranchAsync(): Promise<string | null> {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = readFileSync(this.gitPaths.headPath, \"utf8\").trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\"\n\t\t\t\t\t? ((await resolveBranchWithGitAsync(this.gitPaths.repoDir)) ?? \"detached\")\n\t\t\t\t\t: branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate clearGitWatchers(): void {\n\t\tcloseWatcher(this.headWatcher);\n\t\tthis.headWatcher = null;\n\t\tif (this.headWatchFilePath && this.headWatchFileListener) {\n\t\t\tunwatchFile(this.headWatchFilePath, this.headWatchFileListener);\n\t\t\tthis.headWatchFilePath = null;\n\t\t\tthis.headWatchFileListener = null;\n\t\t}\n\t\tcloseWatcher(this.reftableWatcher);\n\t\tthis.reftableWatcher = null;\n\t\tcloseWatcher(this.reftableTablesListWatcher);\n\t\tthis.reftableTablesListWatcher = null;\n\t\tif (this.reftableTablesListPath) {\n\t\t\tunwatchFile(this.reftableTablesListPath);\n\t\t\tthis.reftableTablesListPath = null;\n\t\t}\n\t\tif (this.gitWatcherRetryTimer) {\n\t\t\tclearTimeout(this.gitWatcherRetryTimer);\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t}\n\t}\n\n\tprivate scheduleGitWatcherRetry(): void {\n\t\tif (this.disposed || this.gitWatcherRetryTimer) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.gitWatcherRetryTimer = setTimeout(() => {\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t\tthis.setupGitWatcher();\n\t\t}, FS_WATCH_RETRY_DELAY_MS);\n\t}\n\n\tprivate handleGitWatcherError(): void {\n\t\tthis.clearGitWatchers();\n\t\tthis.scheduleGitWatcherRetry();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\tthis.clearGitWatchers();\n\t\tif (!this.gitPaths) return;\n\n\t\tconst pollGitHead = shouldPollGitHead(this.gitPaths.repoDir);\n\n\t\t// Watch the directory containing HEAD, not HEAD itself.\n\t\t// Git uses atomic writes (write temp, rename over HEAD), which changes the inode.\n\t\t// fs.watch on a file stops working after the inode changes.\n\t\tthis.headWatcher = watchWithErrorHandler(\n\t\t\tdirname(this.gitPaths.headPath),\n\t\t\t(_eventType, filename) => {\n\t\t\t\tif (!filename || filename === \"HEAD\") {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => this.handleGitWatcherError(),\n\t\t);\n\t\tif (pollGitHead) {\n\t\t\tthis.headWatchFilePath = this.gitPaths.headPath;\n\t\t\tthis.headWatchFileListener = (current, previous) => {\n\t\t\t\tif (\n\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t) {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t};\n\t\t\twatchFile(this.headWatchFilePath, { interval: 1000 }, this.headWatchFileListener);\n\t\t}\n\t\tif (!this.headWatcher && !pollGitHead) {\n\t\t\treturn;\n\t\t}\n\n\t\t// In reftable repos, branch switches update files in the reftable directory\n\t\t// instead of HEAD. Watch it separately so the footer picks up those changes.\n\t\tconst reftableDir = join(this.gitPaths.commonGitDir, \"reftable\");\n\t\tif (existsSync(reftableDir)) {\n\t\t\tthis.reftableWatcher = watchWithErrorHandler(\n\t\t\t\treftableDir,\n\t\t\t\t() => {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t},\n\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t);\n\t\t\tif (!this.reftableWatcher) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst tablesListPath = join(reftableDir, \"tables.list\");\n\t\t\tif (existsSync(tablesListPath)) {\n\t\t\t\tthis.reftableTablesListPath = tablesListPath;\n\t\t\t\tthis.reftableTablesListWatcher = watchWithErrorHandler(\n\t\t\t\t\ttablesListPath,\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t},\n\t\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t\t);\n\t\t\t\tif (!this.reftableTablesListWatcher) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\twatchFile(tablesListPath, { interval: 250 }, (current, previous) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Read-only view for extensions - excludes setExtensionStatus, setAvailableProviderCount and dispose */\nexport type ReadonlyFooterDataProvider = Pick<\n\tFooterDataProvider,\n\t| \"getGitBranch\"\n\t| \"getExtensionStatuses\"\n\t| \"getAvailableProviderCount\"\n\t| \"onBranchChange\"\n\t| \"getCpuPercent\"\n\t| \"getMemoryRssBytes\"\n>;\n"]}
1
+ {"version":3,"file":"footer-data-provider.d.ts","sourceRoot":"","sources":["../../src/core/footer-data-provider.ts"],"names":[],"mappings":"AAKA,OAAO,EAA2B,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AA2F9F;;;GAGG;AACH,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAO;IAEhD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,QAAQ,CAA0C;IAC1D,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,qBAAqB,CAA4D;IACzF,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,yBAAyB,CAA0B;IAC3D,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,qBAAqB,CAAyB;IACtD,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,YAAY,CAA8C;IAClE,OAAO,CAAC,oBAAoB,CAA8C;IAC1E,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAiB;IAEvC,YAAY,GAAG,EAAE,MAAM,EAMtB;IAED,2EAA2E;IAC3E,YAAY,IAAI,MAAM,GAAG,IAAI,CAK5B;IAED,wDAAwD;IACxD,oBAAoB,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAElD;IAED,qEAAqE;IACrE,cAAc,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAG/C;IAED,qCAAqC;IACrC,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAM9D;IAED,yCAAyC;IACzC,sBAAsB,IAAI,IAAI,CAE7B;IAED,4EAA4E;IAC5E,yBAAyB,IAAI,MAAM,CAElC;IAED,6EAA6E;IAC7E,aAAa,IAAI,MAAM,GAAG,IAAI,CAE7B;IAED,kFAAkF;IAClF,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAEjC;IAED,uBAAuB,IAAI,sBAAsB,CAEhD;IAED,gDAAgD;IAChD,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE7C;IAED,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAexB;IAED,wBAAwB;IACxB,OAAO,IAAI,IAAI,CASd;IAED,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,eAAe;YAYT,qBAAqB;IA0BnC,OAAO,CAAC,oBAAoB;YAcd,qBAAqB;IAgBnC,OAAO,CAAC,gBAAgB;IAsBxB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,eAAe;CA2EvB;AAED,yGAAyG;AACzG,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC5C,kBAAkB,EAChB,cAAc,GACd,sBAAsB,GACtB,2BAA2B,GAC3B,gBAAgB,GAChB,eAAe,GACf,mBAAmB,GACnB,yBAAyB,CAC3B,CAAC","sourcesContent":["import { type ExecFileException, execFile, spawnSync } from \"child_process\";\nimport { existsSync, type FSWatcher, readFileSync, type Stats, statSync, unwatchFile, watchFile } from \"fs\";\nimport { dirname, join, resolve } from \"path\";\nimport { closeWatcher, FS_WATCH_RETRY_DELAY_MS, watchWithErrorHandler } from \"../utils/fs-watch.ts\";\nimport { MetricsSampler } from \"./metrics-sampler.ts\";\nimport { evaluatePiPackageIntake, type PiPackageIntakeSummary } from \"./pi-package-intake.ts\";\n\ntype GitPaths = {\n\trepoDir: string;\n\tcommonGitDir: string;\n\theadPath: string;\n};\n\n/**\n * Find git metadata paths by walking up from cwd.\n * Handles both regular git repos (.git is a directory) and worktrees (.git is a file).\n */\nfunction findGitPaths(cwd: string): GitPaths | null {\n\tlet dir = cwd;\n\twhile (true) {\n\t\tconst gitPath = join(dir, \".git\");\n\t\tif (existsSync(gitPath)) {\n\t\t\ttry {\n\t\t\t\tconst stat = statSync(gitPath);\n\t\t\t\tif (stat.isFile()) {\n\t\t\t\t\tconst content = readFileSync(gitPath, \"utf8\").trim();\n\t\t\t\t\tif (content.startsWith(\"gitdir: \")) {\n\t\t\t\t\t\tconst gitDir = resolve(dir, content.slice(8).trim());\n\t\t\t\t\t\tconst headPath = join(gitDir, \"HEAD\");\n\t\t\t\t\t\tif (!existsSync(headPath)) return null;\n\t\t\t\t\t\tconst commonDirPath = join(gitDir, \"commondir\");\n\t\t\t\t\t\tconst commonGitDir = existsSync(commonDirPath)\n\t\t\t\t\t\t\t? resolve(gitDir, readFileSync(commonDirPath, \"utf8\").trim())\n\t\t\t\t\t\t\t: gitDir;\n\t\t\t\t\t\treturn { repoDir: dir, commonGitDir, headPath };\n\t\t\t\t\t}\n\t\t\t\t} else if (stat.isDirectory()) {\n\t\t\t\t\tconst headPath = join(gitPath, \"HEAD\");\n\t\t\t\t\tif (!existsSync(headPath)) return null;\n\t\t\t\t\treturn { repoDir: dir, commonGitDir: gitPath, headPath };\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Ask git for the current branch. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitSync(repoDir: string): string | null {\n\tconst result = spawnSync(\"git\", [\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"], {\n\t\tcwd: repoDir,\n\t\tencoding: \"utf8\",\n\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t});\n\tconst branch = result.status === 0 ? result.stdout.trim() : \"\";\n\treturn branch || null;\n}\n\n/** Ask git for the current branch asynchronously. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitAsync(repoDir: string): Promise<string | null> {\n\treturn new Promise((resolvePromise) => {\n\t\texecFile(\n\t\t\t\"git\",\n\t\t\t[\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"],\n\t\t\t{\n\t\t\t\tcwd: repoDir,\n\t\t\t\tencoding: \"utf8\",\n\t\t\t},\n\t\t\t(error: ExecFileException | null, stdout: string) => {\n\t\t\t\tif (error) {\n\t\t\t\t\tresolvePromise(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst branch = stdout.trim();\n\t\t\t\tresolvePromise(branch || null);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction isWslEnvironment(): boolean {\n\treturn process.platform === \"linux\" && !!(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);\n}\n\nfunction isWindowsMountedRepoPath(repoDir: string): boolean {\n\treturn /^\\/mnt\\/[a-z](?:\\/|$)/i.test(repoDir);\n}\n\nfunction shouldPollGitHead(repoDir: string): boolean {\n\treturn isWslEnvironment() && isWindowsMountedRepoPath(repoDir);\n}\n\n/**\n * Provides git branch and extension statuses - data not otherwise accessible to extensions.\n * Token stats, model info available via ctx.sessionManager and ctx.model.\n */\nexport class FooterDataProvider {\n\tprivate cwd: string;\n\tprivate static readonly WATCH_DEBOUNCE_MS = 500;\n\n\tprivate extensionStatuses = new Map<string, string>();\n\tprivate cachedBranch: string | null | undefined = undefined;\n\tprivate gitPaths: GitPaths | null | undefined = undefined;\n\tprivate headWatcher: FSWatcher | null = null;\n\tprivate headWatchFilePath: string | null = null;\n\tprivate headWatchFileListener: ((current: Stats, previous: Stats) => void) | null = null;\n\tprivate reftableWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListPath: string | null = null;\n\tprivate branchChangeCallbacks = new Set<() => void>();\n\tprivate availableProviderCount = 0;\n\tprivate refreshTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate gitWatcherRetryTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate refreshInFlight = false;\n\tprivate refreshPending = false;\n\tprivate disposed = false;\n\tprivate metricsSampler: MetricsSampler;\n\n\tconstructor(cwd: string) {\n\t\tthis.cwd = cwd;\n\t\tthis.gitPaths = findGitPaths(cwd);\n\t\tthis.setupGitWatcher();\n\t\tthis.metricsSampler = new MetricsSampler();\n\t\tthis.metricsSampler.start();\n\t}\n\n\t/** Current git branch, null if not in repo, \"detached\" if detached HEAD */\n\tgetGitBranch(): string | null {\n\t\tif (this.cachedBranch === undefined) {\n\t\t\tthis.cachedBranch = this.resolveGitBranchSync();\n\t\t}\n\t\treturn this.cachedBranch;\n\t}\n\n\t/** Extension status texts set via ctx.ui.setStatus() */\n\tgetExtensionStatuses(): ReadonlyMap<string, string> {\n\t\treturn this.extensionStatuses;\n\t}\n\n\t/** Subscribe to git branch changes. Returns unsubscribe function. */\n\tonBranchChange(callback: () => void): () => void {\n\t\tthis.branchChangeCallbacks.add(callback);\n\t\treturn () => this.branchChangeCallbacks.delete(callback);\n\t}\n\n\t/** Internal: set extension status */\n\tsetExtensionStatus(key: string, text: string | undefined): void {\n\t\tif (text === undefined) {\n\t\t\tthis.extensionStatuses.delete(key);\n\t\t} else {\n\t\t\tthis.extensionStatuses.set(key, text);\n\t\t}\n\t}\n\n\t/** Internal: clear extension statuses */\n\tclearExtensionStatuses(): void {\n\t\tthis.extensionStatuses.clear();\n\t}\n\n\t/** Number of unique providers with available models (for footer display) */\n\tgetAvailableProviderCount(): number {\n\t\treturn this.availableProviderCount;\n\t}\n\n\t/** Most recent process CPU percent (0-100), or `null` if not yet sampled. */\n\tgetCpuPercent(): number | null {\n\t\treturn this.metricsSampler.getCpuPercent();\n\t}\n\n\t/** Most recent process RSS memory size in bytes, or `null` if not yet sampled. */\n\tgetMemoryRssBytes(): number | null {\n\t\treturn this.metricsSampler.getMemoryRssBytes();\n\t}\n\n\tgetPackageIntakeSummary(): PiPackageIntakeSummary {\n\t\treturn evaluatePiPackageIntake().summary;\n\t}\n\n\t/** Internal: update available provider count */\n\tsetAvailableProviderCount(count: number): void {\n\t\tthis.availableProviderCount = count;\n\t}\n\n\tsetCwd(cwd: string): void {\n\t\tif (this.cwd === cwd) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.cwd = cwd;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.cachedBranch = undefined;\n\t\tthis.gitPaths = findGitPaths(cwd);\n\t\tthis.setupGitWatcher();\n\t\tthis.notifyBranchChange();\n\t}\n\n\t/** Internal: cleanup */\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.branchChangeCallbacks.clear();\n\t\tthis.metricsSampler.stop();\n\t}\n\n\tprivate notifyBranchChange(): void {\n\t\tfor (const cb of this.branchChangeCallbacks) cb();\n\t}\n\n\tprivate scheduleRefresh(): void {\n\t\tif (this.disposed || this.refreshTimer) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\t\tthis.refreshTimer = setTimeout(() => {\n\t\t\tthis.refreshTimer = null;\n\t\t\tvoid this.refreshGitBranchAsync();\n\t\t}, FooterDataProvider.WATCH_DEBOUNCE_MS);\n\t}\n\n\tprivate async refreshGitBranchAsync(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.refreshInFlight = true;\n\t\ttry {\n\t\t\tconst nextBranch = await this.resolveGitBranchAsync();\n\t\t\tif (this.disposed) return;\n\t\t\tif (this.cachedBranch !== undefined && this.cachedBranch !== nextBranch) {\n\t\t\t\tthis.cachedBranch = nextBranch;\n\t\t\t\tthis.notifyBranchChange();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.cachedBranch = nextBranch;\n\t\t} finally {\n\t\t\tthis.refreshInFlight = false;\n\t\t\tif (this.refreshPending && !this.disposed) {\n\t\t\t\tthis.refreshPending = false;\n\t\t\t\tthis.scheduleRefresh();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate resolveGitBranchSync(): string | null {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = readFileSync(this.gitPaths.headPath, \"utf8\").trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\" ? (resolveBranchWithGitSync(this.gitPaths.repoDir) ?? \"detached\") : branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate async resolveGitBranchAsync(): Promise<string | null> {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = readFileSync(this.gitPaths.headPath, \"utf8\").trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\"\n\t\t\t\t\t? ((await resolveBranchWithGitAsync(this.gitPaths.repoDir)) ?? \"detached\")\n\t\t\t\t\t: branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate clearGitWatchers(): void {\n\t\tcloseWatcher(this.headWatcher);\n\t\tthis.headWatcher = null;\n\t\tif (this.headWatchFilePath && this.headWatchFileListener) {\n\t\t\tunwatchFile(this.headWatchFilePath, this.headWatchFileListener);\n\t\t\tthis.headWatchFilePath = null;\n\t\t\tthis.headWatchFileListener = null;\n\t\t}\n\t\tcloseWatcher(this.reftableWatcher);\n\t\tthis.reftableWatcher = null;\n\t\tcloseWatcher(this.reftableTablesListWatcher);\n\t\tthis.reftableTablesListWatcher = null;\n\t\tif (this.reftableTablesListPath) {\n\t\t\tunwatchFile(this.reftableTablesListPath);\n\t\t\tthis.reftableTablesListPath = null;\n\t\t}\n\t\tif (this.gitWatcherRetryTimer) {\n\t\t\tclearTimeout(this.gitWatcherRetryTimer);\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t}\n\t}\n\n\tprivate scheduleGitWatcherRetry(): void {\n\t\tif (this.disposed || this.gitWatcherRetryTimer) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.gitWatcherRetryTimer = setTimeout(() => {\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t\tthis.setupGitWatcher();\n\t\t}, FS_WATCH_RETRY_DELAY_MS);\n\t}\n\n\tprivate handleGitWatcherError(): void {\n\t\tthis.clearGitWatchers();\n\t\tthis.scheduleGitWatcherRetry();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\tthis.clearGitWatchers();\n\t\tif (!this.gitPaths) return;\n\n\t\tconst pollGitHead = shouldPollGitHead(this.gitPaths.repoDir);\n\n\t\t// Watch the directory containing HEAD, not HEAD itself.\n\t\t// Git uses atomic writes (write temp, rename over HEAD), which changes the inode.\n\t\t// fs.watch on a file stops working after the inode changes.\n\t\tthis.headWatcher = watchWithErrorHandler(\n\t\t\tdirname(this.gitPaths.headPath),\n\t\t\t(_eventType, filename) => {\n\t\t\t\tif (!filename || filename === \"HEAD\") {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => this.handleGitWatcherError(),\n\t\t);\n\t\tif (pollGitHead) {\n\t\t\tthis.headWatchFilePath = this.gitPaths.headPath;\n\t\t\tthis.headWatchFileListener = (current, previous) => {\n\t\t\t\tif (\n\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t) {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t};\n\t\t\twatchFile(this.headWatchFilePath, { interval: 1000 }, this.headWatchFileListener);\n\t\t}\n\t\tif (!this.headWatcher && !pollGitHead) {\n\t\t\treturn;\n\t\t}\n\n\t\t// In reftable repos, branch switches update files in the reftable directory\n\t\t// instead of HEAD. Watch it separately so the footer picks up those changes.\n\t\tconst reftableDir = join(this.gitPaths.commonGitDir, \"reftable\");\n\t\tif (existsSync(reftableDir)) {\n\t\t\tthis.reftableWatcher = watchWithErrorHandler(\n\t\t\t\treftableDir,\n\t\t\t\t() => {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t},\n\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t);\n\t\t\tif (!this.reftableWatcher) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst tablesListPath = join(reftableDir, \"tables.list\");\n\t\t\tif (existsSync(tablesListPath)) {\n\t\t\t\tthis.reftableTablesListPath = tablesListPath;\n\t\t\t\tthis.reftableTablesListWatcher = watchWithErrorHandler(\n\t\t\t\t\ttablesListPath,\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t},\n\t\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t\t);\n\t\t\t\tif (!this.reftableTablesListWatcher) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\twatchFile(tablesListPath, { interval: 250 }, (current, previous) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Read-only view for extensions - excludes setExtensionStatus, setAvailableProviderCount and dispose */\nexport type ReadonlyFooterDataProvider = Pick<\n\tFooterDataProvider,\n\t| \"getGitBranch\"\n\t| \"getExtensionStatuses\"\n\t| \"getAvailableProviderCount\"\n\t| \"onBranchChange\"\n\t| \"getCpuPercent\"\n\t| \"getMemoryRssBytes\"\n\t| \"getPackageIntakeSummary\"\n>;\n"]}
@@ -3,6 +3,7 @@ import { existsSync, readFileSync, statSync, unwatchFile, watchFile } from "fs";
3
3
  import { dirname, join, resolve } from "path";
4
4
  import { closeWatcher, FS_WATCH_RETRY_DELAY_MS, watchWithErrorHandler } from "../utils/fs-watch.js";
5
5
  import { MetricsSampler } from "./metrics-sampler.js";
6
+ import { evaluatePiPackageIntake } from "./pi-package-intake.js";
6
7
  /**
7
8
  * Find git metadata paths by walking up from cwd.
8
9
  * Handles both regular git repos (.git is a directory) and worktrees (.git is a file).
@@ -152,6 +153,9 @@ export class FooterDataProvider {
152
153
  getMemoryRssBytes() {
153
154
  return this.metricsSampler.getMemoryRssBytes();
154
155
  }
156
+ getPackageIntakeSummary() {
157
+ return evaluatePiPackageIntake().summary;
158
+ }
155
159
  /** Internal: update available provider count */
156
160
  setAvailableProviderCount(count) {
157
161
  this.availableProviderCount = count;
@@ -1 +1 @@
1
- {"version":3,"file":"footer-data-provider.js","sourceRoot":"","sources":["../../src/core/footer-data-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ExecFileException, execFile, spawnSync } from \"child_process\";\nimport { existsSync, type FSWatcher, readFileSync, type Stats, statSync, unwatchFile, watchFile } from \"fs\";\nimport { dirname, join, resolve } from \"path\";\nimport { closeWatcher, FS_WATCH_RETRY_DELAY_MS, watchWithErrorHandler } from \"../utils/fs-watch.ts\";\nimport { MetricsSampler } from \"./metrics-sampler.ts\";\n\ntype GitPaths = {\n\trepoDir: string;\n\tcommonGitDir: string;\n\theadPath: string;\n};\n\n/**\n * Find git metadata paths by walking up from cwd.\n * Handles both regular git repos (.git is a directory) and worktrees (.git is a file).\n */\nfunction findGitPaths(cwd: string): GitPaths | null {\n\tlet dir = cwd;\n\twhile (true) {\n\t\tconst gitPath = join(dir, \".git\");\n\t\tif (existsSync(gitPath)) {\n\t\t\ttry {\n\t\t\t\tconst stat = statSync(gitPath);\n\t\t\t\tif (stat.isFile()) {\n\t\t\t\t\tconst content = readFileSync(gitPath, \"utf8\").trim();\n\t\t\t\t\tif (content.startsWith(\"gitdir: \")) {\n\t\t\t\t\t\tconst gitDir = resolve(dir, content.slice(8).trim());\n\t\t\t\t\t\tconst headPath = join(gitDir, \"HEAD\");\n\t\t\t\t\t\tif (!existsSync(headPath)) return null;\n\t\t\t\t\t\tconst commonDirPath = join(gitDir, \"commondir\");\n\t\t\t\t\t\tconst commonGitDir = existsSync(commonDirPath)\n\t\t\t\t\t\t\t? resolve(gitDir, readFileSync(commonDirPath, \"utf8\").trim())\n\t\t\t\t\t\t\t: gitDir;\n\t\t\t\t\t\treturn { repoDir: dir, commonGitDir, headPath };\n\t\t\t\t\t}\n\t\t\t\t} else if (stat.isDirectory()) {\n\t\t\t\t\tconst headPath = join(gitPath, \"HEAD\");\n\t\t\t\t\tif (!existsSync(headPath)) return null;\n\t\t\t\t\treturn { repoDir: dir, commonGitDir: gitPath, headPath };\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Ask git for the current branch. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitSync(repoDir: string): string | null {\n\tconst result = spawnSync(\"git\", [\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"], {\n\t\tcwd: repoDir,\n\t\tencoding: \"utf8\",\n\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t});\n\tconst branch = result.status === 0 ? result.stdout.trim() : \"\";\n\treturn branch || null;\n}\n\n/** Ask git for the current branch asynchronously. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitAsync(repoDir: string): Promise<string | null> {\n\treturn new Promise((resolvePromise) => {\n\t\texecFile(\n\t\t\t\"git\",\n\t\t\t[\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"],\n\t\t\t{\n\t\t\t\tcwd: repoDir,\n\t\t\t\tencoding: \"utf8\",\n\t\t\t},\n\t\t\t(error: ExecFileException | null, stdout: string) => {\n\t\t\t\tif (error) {\n\t\t\t\t\tresolvePromise(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst branch = stdout.trim();\n\t\t\t\tresolvePromise(branch || null);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction isWslEnvironment(): boolean {\n\treturn process.platform === \"linux\" && !!(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);\n}\n\nfunction isWindowsMountedRepoPath(repoDir: string): boolean {\n\treturn /^\\/mnt\\/[a-z](?:\\/|$)/i.test(repoDir);\n}\n\nfunction shouldPollGitHead(repoDir: string): boolean {\n\treturn isWslEnvironment() && isWindowsMountedRepoPath(repoDir);\n}\n\n/**\n * Provides git branch and extension statuses - data not otherwise accessible to extensions.\n * Token stats, model info available via ctx.sessionManager and ctx.model.\n */\nexport class FooterDataProvider {\n\tprivate cwd: string;\n\tprivate static readonly WATCH_DEBOUNCE_MS = 500;\n\n\tprivate extensionStatuses = new Map<string, string>();\n\tprivate cachedBranch: string | null | undefined = undefined;\n\tprivate gitPaths: GitPaths | null | undefined = undefined;\n\tprivate headWatcher: FSWatcher | null = null;\n\tprivate headWatchFilePath: string | null = null;\n\tprivate headWatchFileListener: ((current: Stats, previous: Stats) => void) | null = null;\n\tprivate reftableWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListPath: string | null = null;\n\tprivate branchChangeCallbacks = new Set<() => void>();\n\tprivate availableProviderCount = 0;\n\tprivate refreshTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate gitWatcherRetryTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate refreshInFlight = false;\n\tprivate refreshPending = false;\n\tprivate disposed = false;\n\tprivate metricsSampler: MetricsSampler;\n\n\tconstructor(cwd: string) {\n\t\tthis.cwd = cwd;\n\t\tthis.gitPaths = findGitPaths(cwd);\n\t\tthis.setupGitWatcher();\n\t\tthis.metricsSampler = new MetricsSampler();\n\t\tthis.metricsSampler.start();\n\t}\n\n\t/** Current git branch, null if not in repo, \"detached\" if detached HEAD */\n\tgetGitBranch(): string | null {\n\t\tif (this.cachedBranch === undefined) {\n\t\t\tthis.cachedBranch = this.resolveGitBranchSync();\n\t\t}\n\t\treturn this.cachedBranch;\n\t}\n\n\t/** Extension status texts set via ctx.ui.setStatus() */\n\tgetExtensionStatuses(): ReadonlyMap<string, string> {\n\t\treturn this.extensionStatuses;\n\t}\n\n\t/** Subscribe to git branch changes. Returns unsubscribe function. */\n\tonBranchChange(callback: () => void): () => void {\n\t\tthis.branchChangeCallbacks.add(callback);\n\t\treturn () => this.branchChangeCallbacks.delete(callback);\n\t}\n\n\t/** Internal: set extension status */\n\tsetExtensionStatus(key: string, text: string | undefined): void {\n\t\tif (text === undefined) {\n\t\t\tthis.extensionStatuses.delete(key);\n\t\t} else {\n\t\t\tthis.extensionStatuses.set(key, text);\n\t\t}\n\t}\n\n\t/** Internal: clear extension statuses */\n\tclearExtensionStatuses(): void {\n\t\tthis.extensionStatuses.clear();\n\t}\n\n\t/** Number of unique providers with available models (for footer display) */\n\tgetAvailableProviderCount(): number {\n\t\treturn this.availableProviderCount;\n\t}\n\n\t/** Most recent process CPU percent (0-100), or `null` if not yet sampled. */\n\tgetCpuPercent(): number | null {\n\t\treturn this.metricsSampler.getCpuPercent();\n\t}\n\n\t/** Most recent process RSS memory size in bytes, or `null` if not yet sampled. */\n\tgetMemoryRssBytes(): number | null {\n\t\treturn this.metricsSampler.getMemoryRssBytes();\n\t}\n\n\t/** Internal: update available provider count */\n\tsetAvailableProviderCount(count: number): void {\n\t\tthis.availableProviderCount = count;\n\t}\n\n\tsetCwd(cwd: string): void {\n\t\tif (this.cwd === cwd) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.cwd = cwd;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.cachedBranch = undefined;\n\t\tthis.gitPaths = findGitPaths(cwd);\n\t\tthis.setupGitWatcher();\n\t\tthis.notifyBranchChange();\n\t}\n\n\t/** Internal: cleanup */\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.branchChangeCallbacks.clear();\n\t\tthis.metricsSampler.stop();\n\t}\n\n\tprivate notifyBranchChange(): void {\n\t\tfor (const cb of this.branchChangeCallbacks) cb();\n\t}\n\n\tprivate scheduleRefresh(): void {\n\t\tif (this.disposed || this.refreshTimer) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\t\tthis.refreshTimer = setTimeout(() => {\n\t\t\tthis.refreshTimer = null;\n\t\t\tvoid this.refreshGitBranchAsync();\n\t\t}, FooterDataProvider.WATCH_DEBOUNCE_MS);\n\t}\n\n\tprivate async refreshGitBranchAsync(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.refreshInFlight = true;\n\t\ttry {\n\t\t\tconst nextBranch = await this.resolveGitBranchAsync();\n\t\t\tif (this.disposed) return;\n\t\t\tif (this.cachedBranch !== undefined && this.cachedBranch !== nextBranch) {\n\t\t\t\tthis.cachedBranch = nextBranch;\n\t\t\t\tthis.notifyBranchChange();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.cachedBranch = nextBranch;\n\t\t} finally {\n\t\t\tthis.refreshInFlight = false;\n\t\t\tif (this.refreshPending && !this.disposed) {\n\t\t\t\tthis.refreshPending = false;\n\t\t\t\tthis.scheduleRefresh();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate resolveGitBranchSync(): string | null {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = readFileSync(this.gitPaths.headPath, \"utf8\").trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\" ? (resolveBranchWithGitSync(this.gitPaths.repoDir) ?? \"detached\") : branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate async resolveGitBranchAsync(): Promise<string | null> {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = readFileSync(this.gitPaths.headPath, \"utf8\").trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\"\n\t\t\t\t\t? ((await resolveBranchWithGitAsync(this.gitPaths.repoDir)) ?? \"detached\")\n\t\t\t\t\t: branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate clearGitWatchers(): void {\n\t\tcloseWatcher(this.headWatcher);\n\t\tthis.headWatcher = null;\n\t\tif (this.headWatchFilePath && this.headWatchFileListener) {\n\t\t\tunwatchFile(this.headWatchFilePath, this.headWatchFileListener);\n\t\t\tthis.headWatchFilePath = null;\n\t\t\tthis.headWatchFileListener = null;\n\t\t}\n\t\tcloseWatcher(this.reftableWatcher);\n\t\tthis.reftableWatcher = null;\n\t\tcloseWatcher(this.reftableTablesListWatcher);\n\t\tthis.reftableTablesListWatcher = null;\n\t\tif (this.reftableTablesListPath) {\n\t\t\tunwatchFile(this.reftableTablesListPath);\n\t\t\tthis.reftableTablesListPath = null;\n\t\t}\n\t\tif (this.gitWatcherRetryTimer) {\n\t\t\tclearTimeout(this.gitWatcherRetryTimer);\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t}\n\t}\n\n\tprivate scheduleGitWatcherRetry(): void {\n\t\tif (this.disposed || this.gitWatcherRetryTimer) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.gitWatcherRetryTimer = setTimeout(() => {\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t\tthis.setupGitWatcher();\n\t\t}, FS_WATCH_RETRY_DELAY_MS);\n\t}\n\n\tprivate handleGitWatcherError(): void {\n\t\tthis.clearGitWatchers();\n\t\tthis.scheduleGitWatcherRetry();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\tthis.clearGitWatchers();\n\t\tif (!this.gitPaths) return;\n\n\t\tconst pollGitHead = shouldPollGitHead(this.gitPaths.repoDir);\n\n\t\t// Watch the directory containing HEAD, not HEAD itself.\n\t\t// Git uses atomic writes (write temp, rename over HEAD), which changes the inode.\n\t\t// fs.watch on a file stops working after the inode changes.\n\t\tthis.headWatcher = watchWithErrorHandler(\n\t\t\tdirname(this.gitPaths.headPath),\n\t\t\t(_eventType, filename) => {\n\t\t\t\tif (!filename || filename === \"HEAD\") {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => this.handleGitWatcherError(),\n\t\t);\n\t\tif (pollGitHead) {\n\t\t\tthis.headWatchFilePath = this.gitPaths.headPath;\n\t\t\tthis.headWatchFileListener = (current, previous) => {\n\t\t\t\tif (\n\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t) {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t};\n\t\t\twatchFile(this.headWatchFilePath, { interval: 1000 }, this.headWatchFileListener);\n\t\t}\n\t\tif (!this.headWatcher && !pollGitHead) {\n\t\t\treturn;\n\t\t}\n\n\t\t// In reftable repos, branch switches update files in the reftable directory\n\t\t// instead of HEAD. Watch it separately so the footer picks up those changes.\n\t\tconst reftableDir = join(this.gitPaths.commonGitDir, \"reftable\");\n\t\tif (existsSync(reftableDir)) {\n\t\t\tthis.reftableWatcher = watchWithErrorHandler(\n\t\t\t\treftableDir,\n\t\t\t\t() => {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t},\n\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t);\n\t\t\tif (!this.reftableWatcher) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst tablesListPath = join(reftableDir, \"tables.list\");\n\t\t\tif (existsSync(tablesListPath)) {\n\t\t\t\tthis.reftableTablesListPath = tablesListPath;\n\t\t\t\tthis.reftableTablesListWatcher = watchWithErrorHandler(\n\t\t\t\t\ttablesListPath,\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t},\n\t\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t\t);\n\t\t\t\tif (!this.reftableTablesListWatcher) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\twatchFile(tablesListPath, { interval: 250 }, (current, previous) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Read-only view for extensions - excludes setExtensionStatus, setAvailableProviderCount and dispose */\nexport type ReadonlyFooterDataProvider = Pick<\n\tFooterDataProvider,\n\t| \"getGitBranch\"\n\t| \"getExtensionStatuses\"\n\t| \"getAvailableProviderCount\"\n\t| \"onBranchChange\"\n\t| \"getCpuPercent\"\n\t| \"getMemoryRssBytes\"\n>;\n"]}
1
+ {"version":3,"file":"footer-data-provider.js","sourceRoot":"","sources":["../../src/core/footer-data-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ExecFileException, execFile, spawnSync } from \"child_process\";\nimport { existsSync, type FSWatcher, readFileSync, type Stats, statSync, unwatchFile, watchFile } from \"fs\";\nimport { dirname, join, resolve } from \"path\";\nimport { closeWatcher, FS_WATCH_RETRY_DELAY_MS, watchWithErrorHandler } from \"../utils/fs-watch.ts\";\nimport { MetricsSampler } from \"./metrics-sampler.ts\";\nimport { evaluatePiPackageIntake, type PiPackageIntakeSummary } from \"./pi-package-intake.ts\";\n\ntype GitPaths = {\n\trepoDir: string;\n\tcommonGitDir: string;\n\theadPath: string;\n};\n\n/**\n * Find git metadata paths by walking up from cwd.\n * Handles both regular git repos (.git is a directory) and worktrees (.git is a file).\n */\nfunction findGitPaths(cwd: string): GitPaths | null {\n\tlet dir = cwd;\n\twhile (true) {\n\t\tconst gitPath = join(dir, \".git\");\n\t\tif (existsSync(gitPath)) {\n\t\t\ttry {\n\t\t\t\tconst stat = statSync(gitPath);\n\t\t\t\tif (stat.isFile()) {\n\t\t\t\t\tconst content = readFileSync(gitPath, \"utf8\").trim();\n\t\t\t\t\tif (content.startsWith(\"gitdir: \")) {\n\t\t\t\t\t\tconst gitDir = resolve(dir, content.slice(8).trim());\n\t\t\t\t\t\tconst headPath = join(gitDir, \"HEAD\");\n\t\t\t\t\t\tif (!existsSync(headPath)) return null;\n\t\t\t\t\t\tconst commonDirPath = join(gitDir, \"commondir\");\n\t\t\t\t\t\tconst commonGitDir = existsSync(commonDirPath)\n\t\t\t\t\t\t\t? resolve(gitDir, readFileSync(commonDirPath, \"utf8\").trim())\n\t\t\t\t\t\t\t: gitDir;\n\t\t\t\t\t\treturn { repoDir: dir, commonGitDir, headPath };\n\t\t\t\t\t}\n\t\t\t\t} else if (stat.isDirectory()) {\n\t\t\t\t\tconst headPath = join(gitPath, \"HEAD\");\n\t\t\t\t\tif (!existsSync(headPath)) return null;\n\t\t\t\t\treturn { repoDir: dir, commonGitDir: gitPath, headPath };\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return null;\n\t\tdir = parent;\n\t}\n}\n\n/** Ask git for the current branch. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitSync(repoDir: string): string | null {\n\tconst result = spawnSync(\"git\", [\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"], {\n\t\tcwd: repoDir,\n\t\tencoding: \"utf8\",\n\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t});\n\tconst branch = result.status === 0 ? result.stdout.trim() : \"\";\n\treturn branch || null;\n}\n\n/** Ask git for the current branch asynchronously. Returns null on detached HEAD or if git is unavailable. */\nfunction resolveBranchWithGitAsync(repoDir: string): Promise<string | null> {\n\treturn new Promise((resolvePromise) => {\n\t\texecFile(\n\t\t\t\"git\",\n\t\t\t[\"--no-optional-locks\", \"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"],\n\t\t\t{\n\t\t\t\tcwd: repoDir,\n\t\t\t\tencoding: \"utf8\",\n\t\t\t},\n\t\t\t(error: ExecFileException | null, stdout: string) => {\n\t\t\t\tif (error) {\n\t\t\t\t\tresolvePromise(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst branch = stdout.trim();\n\t\t\t\tresolvePromise(branch || null);\n\t\t\t},\n\t\t);\n\t});\n}\n\nfunction isWslEnvironment(): boolean {\n\treturn process.platform === \"linux\" && !!(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP);\n}\n\nfunction isWindowsMountedRepoPath(repoDir: string): boolean {\n\treturn /^\\/mnt\\/[a-z](?:\\/|$)/i.test(repoDir);\n}\n\nfunction shouldPollGitHead(repoDir: string): boolean {\n\treturn isWslEnvironment() && isWindowsMountedRepoPath(repoDir);\n}\n\n/**\n * Provides git branch and extension statuses - data not otherwise accessible to extensions.\n * Token stats, model info available via ctx.sessionManager and ctx.model.\n */\nexport class FooterDataProvider {\n\tprivate cwd: string;\n\tprivate static readonly WATCH_DEBOUNCE_MS = 500;\n\n\tprivate extensionStatuses = new Map<string, string>();\n\tprivate cachedBranch: string | null | undefined = undefined;\n\tprivate gitPaths: GitPaths | null | undefined = undefined;\n\tprivate headWatcher: FSWatcher | null = null;\n\tprivate headWatchFilePath: string | null = null;\n\tprivate headWatchFileListener: ((current: Stats, previous: Stats) => void) | null = null;\n\tprivate reftableWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListWatcher: FSWatcher | null = null;\n\tprivate reftableTablesListPath: string | null = null;\n\tprivate branchChangeCallbacks = new Set<() => void>();\n\tprivate availableProviderCount = 0;\n\tprivate refreshTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate gitWatcherRetryTimer: ReturnType<typeof setTimeout> | null = null;\n\tprivate refreshInFlight = false;\n\tprivate refreshPending = false;\n\tprivate disposed = false;\n\tprivate metricsSampler: MetricsSampler;\n\n\tconstructor(cwd: string) {\n\t\tthis.cwd = cwd;\n\t\tthis.gitPaths = findGitPaths(cwd);\n\t\tthis.setupGitWatcher();\n\t\tthis.metricsSampler = new MetricsSampler();\n\t\tthis.metricsSampler.start();\n\t}\n\n\t/** Current git branch, null if not in repo, \"detached\" if detached HEAD */\n\tgetGitBranch(): string | null {\n\t\tif (this.cachedBranch === undefined) {\n\t\t\tthis.cachedBranch = this.resolveGitBranchSync();\n\t\t}\n\t\treturn this.cachedBranch;\n\t}\n\n\t/** Extension status texts set via ctx.ui.setStatus() */\n\tgetExtensionStatuses(): ReadonlyMap<string, string> {\n\t\treturn this.extensionStatuses;\n\t}\n\n\t/** Subscribe to git branch changes. Returns unsubscribe function. */\n\tonBranchChange(callback: () => void): () => void {\n\t\tthis.branchChangeCallbacks.add(callback);\n\t\treturn () => this.branchChangeCallbacks.delete(callback);\n\t}\n\n\t/** Internal: set extension status */\n\tsetExtensionStatus(key: string, text: string | undefined): void {\n\t\tif (text === undefined) {\n\t\t\tthis.extensionStatuses.delete(key);\n\t\t} else {\n\t\t\tthis.extensionStatuses.set(key, text);\n\t\t}\n\t}\n\n\t/** Internal: clear extension statuses */\n\tclearExtensionStatuses(): void {\n\t\tthis.extensionStatuses.clear();\n\t}\n\n\t/** Number of unique providers with available models (for footer display) */\n\tgetAvailableProviderCount(): number {\n\t\treturn this.availableProviderCount;\n\t}\n\n\t/** Most recent process CPU percent (0-100), or `null` if not yet sampled. */\n\tgetCpuPercent(): number | null {\n\t\treturn this.metricsSampler.getCpuPercent();\n\t}\n\n\t/** Most recent process RSS memory size in bytes, or `null` if not yet sampled. */\n\tgetMemoryRssBytes(): number | null {\n\t\treturn this.metricsSampler.getMemoryRssBytes();\n\t}\n\n\tgetPackageIntakeSummary(): PiPackageIntakeSummary {\n\t\treturn evaluatePiPackageIntake().summary;\n\t}\n\n\t/** Internal: update available provider count */\n\tsetAvailableProviderCount(count: number): void {\n\t\tthis.availableProviderCount = count;\n\t}\n\n\tsetCwd(cwd: string): void {\n\t\tif (this.cwd === cwd) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.cwd = cwd;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.cachedBranch = undefined;\n\t\tthis.gitPaths = findGitPaths(cwd);\n\t\tthis.setupGitWatcher();\n\t\tthis.notifyBranchChange();\n\t}\n\n\t/** Internal: cleanup */\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t\tif (this.refreshTimer) {\n\t\t\tclearTimeout(this.refreshTimer);\n\t\t\tthis.refreshTimer = null;\n\t\t}\n\t\tthis.clearGitWatchers();\n\t\tthis.branchChangeCallbacks.clear();\n\t\tthis.metricsSampler.stop();\n\t}\n\n\tprivate notifyBranchChange(): void {\n\t\tfor (const cb of this.branchChangeCallbacks) cb();\n\t}\n\n\tprivate scheduleRefresh(): void {\n\t\tif (this.disposed || this.refreshTimer) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\t\tthis.refreshTimer = setTimeout(() => {\n\t\t\tthis.refreshTimer = null;\n\t\t\tvoid this.refreshGitBranchAsync();\n\t\t}, FooterDataProvider.WATCH_DEBOUNCE_MS);\n\t}\n\n\tprivate async refreshGitBranchAsync(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tif (this.refreshInFlight) {\n\t\t\tthis.refreshPending = true;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.refreshInFlight = true;\n\t\ttry {\n\t\t\tconst nextBranch = await this.resolveGitBranchAsync();\n\t\t\tif (this.disposed) return;\n\t\t\tif (this.cachedBranch !== undefined && this.cachedBranch !== nextBranch) {\n\t\t\t\tthis.cachedBranch = nextBranch;\n\t\t\t\tthis.notifyBranchChange();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.cachedBranch = nextBranch;\n\t\t} finally {\n\t\t\tthis.refreshInFlight = false;\n\t\t\tif (this.refreshPending && !this.disposed) {\n\t\t\t\tthis.refreshPending = false;\n\t\t\t\tthis.scheduleRefresh();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate resolveGitBranchSync(): string | null {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = readFileSync(this.gitPaths.headPath, \"utf8\").trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\" ? (resolveBranchWithGitSync(this.gitPaths.repoDir) ?? \"detached\") : branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate async resolveGitBranchAsync(): Promise<string | null> {\n\t\ttry {\n\t\t\tif (!this.gitPaths) return null;\n\t\t\tconst content = readFileSync(this.gitPaths.headPath, \"utf8\").trim();\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\tconst branch = content.slice(16);\n\t\t\t\treturn branch === \".invalid\"\n\t\t\t\t\t? ((await resolveBranchWithGitAsync(this.gitPaths.repoDir)) ?? \"detached\")\n\t\t\t\t\t: branch;\n\t\t\t}\n\t\t\treturn \"detached\";\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate clearGitWatchers(): void {\n\t\tcloseWatcher(this.headWatcher);\n\t\tthis.headWatcher = null;\n\t\tif (this.headWatchFilePath && this.headWatchFileListener) {\n\t\t\tunwatchFile(this.headWatchFilePath, this.headWatchFileListener);\n\t\t\tthis.headWatchFilePath = null;\n\t\t\tthis.headWatchFileListener = null;\n\t\t}\n\t\tcloseWatcher(this.reftableWatcher);\n\t\tthis.reftableWatcher = null;\n\t\tcloseWatcher(this.reftableTablesListWatcher);\n\t\tthis.reftableTablesListWatcher = null;\n\t\tif (this.reftableTablesListPath) {\n\t\t\tunwatchFile(this.reftableTablesListPath);\n\t\t\tthis.reftableTablesListPath = null;\n\t\t}\n\t\tif (this.gitWatcherRetryTimer) {\n\t\t\tclearTimeout(this.gitWatcherRetryTimer);\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t}\n\t}\n\n\tprivate scheduleGitWatcherRetry(): void {\n\t\tif (this.disposed || this.gitWatcherRetryTimer) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.gitWatcherRetryTimer = setTimeout(() => {\n\t\t\tthis.gitWatcherRetryTimer = null;\n\t\t\tthis.setupGitWatcher();\n\t\t}, FS_WATCH_RETRY_DELAY_MS);\n\t}\n\n\tprivate handleGitWatcherError(): void {\n\t\tthis.clearGitWatchers();\n\t\tthis.scheduleGitWatcherRetry();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\tthis.clearGitWatchers();\n\t\tif (!this.gitPaths) return;\n\n\t\tconst pollGitHead = shouldPollGitHead(this.gitPaths.repoDir);\n\n\t\t// Watch the directory containing HEAD, not HEAD itself.\n\t\t// Git uses atomic writes (write temp, rename over HEAD), which changes the inode.\n\t\t// fs.watch on a file stops working after the inode changes.\n\t\tthis.headWatcher = watchWithErrorHandler(\n\t\t\tdirname(this.gitPaths.headPath),\n\t\t\t(_eventType, filename) => {\n\t\t\t\tif (!filename || filename === \"HEAD\") {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => this.handleGitWatcherError(),\n\t\t);\n\t\tif (pollGitHead) {\n\t\t\tthis.headWatchFilePath = this.gitPaths.headPath;\n\t\t\tthis.headWatchFileListener = (current, previous) => {\n\t\t\t\tif (\n\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t) {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t}\n\t\t\t};\n\t\t\twatchFile(this.headWatchFilePath, { interval: 1000 }, this.headWatchFileListener);\n\t\t}\n\t\tif (!this.headWatcher && !pollGitHead) {\n\t\t\treturn;\n\t\t}\n\n\t\t// In reftable repos, branch switches update files in the reftable directory\n\t\t// instead of HEAD. Watch it separately so the footer picks up those changes.\n\t\tconst reftableDir = join(this.gitPaths.commonGitDir, \"reftable\");\n\t\tif (existsSync(reftableDir)) {\n\t\t\tthis.reftableWatcher = watchWithErrorHandler(\n\t\t\t\treftableDir,\n\t\t\t\t() => {\n\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t},\n\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t);\n\t\t\tif (!this.reftableWatcher) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst tablesListPath = join(reftableDir, \"tables.list\");\n\t\t\tif (existsSync(tablesListPath)) {\n\t\t\t\tthis.reftableTablesListPath = tablesListPath;\n\t\t\t\tthis.reftableTablesListWatcher = watchWithErrorHandler(\n\t\t\t\t\ttablesListPath,\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t},\n\t\t\t\t\t() => this.handleGitWatcherError(),\n\t\t\t\t);\n\t\t\t\tif (!this.reftableTablesListWatcher) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\twatchFile(tablesListPath, { interval: 250 }, (current, previous) => {\n\t\t\t\t\tif (\n\t\t\t\t\t\tcurrent.mtimeMs !== previous.mtimeMs ||\n\t\t\t\t\t\tcurrent.ctimeMs !== previous.ctimeMs ||\n\t\t\t\t\t\tcurrent.size !== previous.size\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.scheduleRefresh();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Read-only view for extensions - excludes setExtensionStatus, setAvailableProviderCount and dispose */\nexport type ReadonlyFooterDataProvider = Pick<\n\tFooterDataProvider,\n\t| \"getGitBranch\"\n\t| \"getExtensionStatuses\"\n\t| \"getAvailableProviderCount\"\n\t| \"onBranchChange\"\n\t| \"getCpuPercent\"\n\t| \"getMemoryRssBytes\"\n\t| \"getPackageIntakeSummary\"\n>;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"headroom.d.ts","sourceRoot":"","sources":["../../src/core/headroom.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;CAChE;AAED,MAAM,WAAW,kBAAkB;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,YAAY,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC9C,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,eAAgB,SAAQ,YAAY;IAChD,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,UAAU,CAAO;IAEzB,MAAM,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAOvC;IAED,OAAO,IAAI,kBAAkB,CAoB5B;IAED,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CA6B5E;CACD;AAED,qBAAa,uBAAwB,SAAQ,YAAY;IACxD,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,SAAS,SAAQ,EAG5B;IAED,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAU5E;CACD;AAED,qBAAa,eAAgB,SAAQ,YAAY;IAChD,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,SAAS,SAAQ,EAK5B;IAED,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,gBAAgB,SAAI,EAAE,cAAc,SAAI,GAAG,gBAAgB,CAyBtF;IAED,OAAO,IAAI,kBAAkB,CAE5B;IAED,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAEjF;CACD;AAED,eAAe,eAAe,CAAC","sourcesContent":["// OMK v6.2 — Headroom Manager (TypeScript)\n// ==================================================================\n// packages/coding-agent/src/core/headroom.ts\n//\n// STATUS: compatibility utility.\n// This file is preserved for legacy extension/API consumers and regression tests.\n//\n// ACTUAL HEADROOM / TOKEN BUDGET SYSTEM:\n// - types/jailbreak.ts → defines HeadroomSnapshot, HeadroomPrediction (different shape)\n// - context-budget-compressors.ts → context budget compression with adapters\n// - context-budget-token-counter.ts → token counting for budget decisions\n// - compaction/compaction.ts getCompactionHeadroomThreshold() → headroom threshold\n// - compaction/compaction.ts shouldCompact() → triggers compaction when headroom low\n//\n// NOTE: types/jailbreak.ts defines similar types (HeadroomSnapshot, HeadroomPrediction)\n// but with different field structures. Both are distinct from this file's types.\n//\n// Keep this utility deterministic and aligned with the active context budget policy.\n\nimport { EventEmitter } from \"events\";\n\nexport interface HeadroomSnapshot {\n\ttimestamp: number;\n\tpromptTokens: number;\n\ttoolOutputTokens: number;\n\tresponseTokens: number;\n\ttotalTokens: number;\n\theadroomRemaining: number;\n\tstatus: \"idle\" | \"active\" | \"stressed\" | \"critical\" | \"blocked\";\n}\n\nexport interface HeadroomPrediction {\n\tpredictedTokens: number;\n\tconfidence: number;\n\ttrend: \"increasing\" | \"stable\" | \"decreasing\";\n\trecommendedAction: string;\n}\n\nexport class HeadroomMonitor extends EventEmitter {\n\tprivate history: HeadroomSnapshot[] = [];\n\tprivate maxHistory = 100;\n\n\trecord(snapshot: HeadroomSnapshot): void {\n\t\tconst sanitized = sanitizeSnapshot(snapshot);\n\t\tthis.history.push(sanitized);\n\t\tif (this.history.length > this.maxHistory) {\n\t\t\tthis.history = this.history.slice(-this.maxHistory);\n\t\t}\n\t\tthis.emit(\"snapshot\", sanitized);\n\t}\n\n\tpredict(): HeadroomPrediction {\n\t\tif (this.history.length < 3) {\n\t\t\treturn { predictedTokens: 0, confidence: 0, trend: \"stable\", recommendedAction: \"collect more data\" };\n\t\t}\n\n\t\tconst recent = this.history.slice(-10);\n\t\tconst first = recent[0].totalTokens;\n\t\tconst last = recent[recent.length - 1].totalTokens;\n\t\tconst delta = last - first;\n\t\tconst slope = delta / Math.max(recent.length - 1, 1);\n\t\tconst trend = Math.abs(delta) <= Math.max(1, last * 0.02) ? \"stable\" : delta > 0 ? \"increasing\" : \"decreasing\";\n\t\tconst projectedTokens = trend === \"increasing\" ? last + Math.max(1, slope) : Math.max(0, last + slope);\n\t\tconst confidence = Math.min(0.95, 0.45 + recent.length * 0.05);\n\n\t\treturn {\n\t\t\tpredictedTokens: Math.round(projectedTokens),\n\t\t\tconfidence,\n\t\t\ttrend,\n\t\t\trecommendedAction: trend === \"increasing\" ? \"compress context\" : \"maintain\",\n\t\t};\n\t}\n\n\tgetStatus(totalTokens: number, maxTokens: number): HeadroomSnapshot[\"status\"] {\n\t\tconst normalizedTotalTokens = normalizeTokenInput(totalTokens);\n\t\tconst normalizedMaxTokens = normalizeTokenLimit(maxTokens);\n\t\tif (normalizedMaxTokens === 0) {\n\t\t\treturn normalizedTotalTokens === 0 ? \"idle\" : \"blocked\";\n\t\t}\n\n\t\tconst ratio = normalizedTotalTokens / normalizedMaxTokens;\n\t\tlet status: HeadroomSnapshot[\"status\"];\n\t\tif (ratio < 0.4) {\n\t\t\tstatus = \"idle\";\n\t\t} else if (ratio < 0.7) {\n\t\t\tstatus = \"active\";\n\t\t} else if (ratio < 0.8) {\n\t\t\tstatus = \"stressed\";\n\t\t} else if (ratio < 0.9) {\n\t\t\tstatus = \"critical\";\n\t\t} else {\n\t\t\tstatus = \"blocked\";\n\t\t}\n\n\t\tconst previous = this.history.at(-1)?.status;\n\t\tif (previous === \"critical\" && status === \"stressed\" && ratio >= 0.75) {\n\t\t\treturn \"critical\";\n\t\t}\n\t\tif (previous === \"blocked\" && status !== \"blocked\" && ratio >= 0.85) {\n\t\t\treturn \"blocked\";\n\t\t}\n\t\treturn status;\n\t}\n}\n\nexport class DynamicContextAllocator extends EventEmitter {\n\tprivate maxTokens: number;\n\n\tconstructor(maxTokens = 15000) {\n\t\tsuper();\n\t\tthis.maxTokens = normalizeTokenLimit(maxTokens);\n\t}\n\n\tallocate(currentTokens: number): { maxTokens: number; canAddAgents: boolean } {\n\t\tif (this.maxTokens === 0) {\n\t\t\treturn { maxTokens: 0, canAddAgents: false };\n\t\t}\n\t\tconst ratio = normalizeTokenInput(currentTokens) / this.maxTokens;\n\t\tif (ratio < 0.4) return { maxTokens: this.maxTokens, canAddAgents: true };\n\t\tif (ratio < 0.7) return { maxTokens: this.maxTokens * 0.9, canAddAgents: true };\n\t\tif (ratio < 0.8) return { maxTokens: this.maxTokens * 0.75, canAddAgents: false };\n\t\tif (ratio < 0.9) return { maxTokens: this.maxTokens * 0.5, canAddAgents: false };\n\t\treturn { maxTokens: 0, canAddAgents: false };\n\t}\n}\n\nexport class HeadroomManager extends EventEmitter {\n\tprivate monitor: HeadroomMonitor;\n\tprivate allocator: DynamicContextAllocator;\n\tprivate maxTokens: number;\n\n\tconstructor(maxTokens = 15000) {\n\t\tsuper();\n\t\tthis.maxTokens = normalizeTokenLimit(maxTokens);\n\t\tthis.monitor = new HeadroomMonitor();\n\t\tthis.allocator = new DynamicContextAllocator(this.maxTokens);\n\t}\n\n\tcheck(promptTokens: number, toolOutputTokens = 0, responseTokens = 0): HeadroomSnapshot {\n\t\tconst normalizedPromptTokens = normalizeTokenInput(promptTokens);\n\t\tconst normalizedToolOutputTokens = normalizeTokenInput(toolOutputTokens);\n\t\tconst normalizedResponseTokens = normalizeTokenInput(responseTokens);\n\t\tconst totalTokens = normalizedPromptTokens + normalizedToolOutputTokens + normalizedResponseTokens;\n\t\tconst status = this.monitor.getStatus(totalTokens, this.maxTokens);\n\t\tthis.allocator.allocate(totalTokens);\n\n\t\tconst snapshot: HeadroomSnapshot = {\n\t\t\ttimestamp: Date.now(),\n\t\t\tpromptTokens: normalizedPromptTokens,\n\t\t\ttoolOutputTokens: normalizedToolOutputTokens,\n\t\t\tresponseTokens: normalizedResponseTokens,\n\t\t\ttotalTokens,\n\t\t\theadroomRemaining: this.maxTokens - totalTokens,\n\t\t\tstatus,\n\t\t};\n\n\t\tthis.monitor.record(snapshot);\n\n\t\tif (status === \"critical\" || status === \"blocked\") {\n\t\t\tthis.emit(\"warning\", { status, message: `Headroom ${status}: ${totalTokens}/${this.maxTokens} tokens` });\n\t\t}\n\n\t\treturn snapshot;\n\t}\n\n\tpredict(): HeadroomPrediction {\n\t\treturn this.monitor.predict();\n\t}\n\n\tgetAllocation(currentTokens: number): { maxTokens: number; canAddAgents: boolean } {\n\t\treturn this.allocator.allocate(currentTokens);\n\t}\n}\n\nexport default HeadroomManager;\n\nfunction sanitizeSnapshot(snapshot: HeadroomSnapshot): HeadroomSnapshot {\n\treturn {\n\t\ttimestamp: Number.isFinite(snapshot.timestamp) ? Math.floor(snapshot.timestamp) : 0,\n\t\tpromptTokens: normalizeTokenInput(snapshot.promptTokens),\n\t\ttoolOutputTokens: normalizeTokenInput(snapshot.toolOutputTokens),\n\t\tresponseTokens: normalizeTokenInput(snapshot.responseTokens),\n\t\ttotalTokens: normalizeTokenInput(snapshot.totalTokens),\n\t\theadroomRemaining: normalizeFiniteInteger(snapshot.headroomRemaining),\n\t\tstatus: snapshot.status,\n\t};\n}\n\nfunction normalizeTokenLimit(value: number): number {\n\treturn normalizeTokenInput(value);\n}\n\nfunction normalizeTokenInput(value: number): number {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n\nfunction normalizeFiniteInteger(value: number): number {\n\tif (!Number.isFinite(value)) {\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n"]}
1
+ {"version":3,"file":"headroom.d.ts","sourceRoot":"","sources":["../../src/core/headroom.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;CAChE;AAED,MAAM,WAAW,kBAAkB;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,YAAY,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC9C,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,eAAgB,SAAQ,YAAY;IAChD,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,UAAU,CAAO;IAEzB,MAAM,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAOvC;IAED,OAAO,IAAI,kBAAkB,CAoB5B;IAED,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CA6B5E;CACD;AAED,qBAAa,uBAAwB,SAAQ,YAAY;IACxD,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,SAAS,SAAQ,EAG5B;IAED,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAU5E;CACD;AAED,qBAAa,eAAgB,SAAQ,YAAY;IAChD,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,SAAS,CAAS;IAE1B,YAAY,SAAS,SAAQ,EAK5B;IAED,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,gBAAgB,SAAI,EAAE,cAAc,SAAI,GAAG,gBAAgB,CAyBtF;IAED,OAAO,IAAI,kBAAkB,CAE5B;IAED,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAEjF;CACD;AAED,eAAe,eAAe,CAAC","sourcesContent":["// OMK v6.2 — Headroom Manager (TypeScript)\n// ==================================================================\n// packages/coding-agent/src/core/headroom.ts\n//\n// STATUS: compatibility utility.\n// This file is preserved for legacy extension/API consumers and regression tests.\n//\n// ACTUAL HEADROOM / TOKEN BUDGET SYSTEM:\n// - context-budget-compressors.ts → context budget compression with adapters\n// - context-budget-token-counter.ts → token counting for budget decisions\n// - compaction/compaction.ts getCompactionHeadroomThreshold() → headroom threshold\n// - compaction/compaction.ts shouldCompact() → triggers compaction when headroom low\n//\n// Keep this utility deterministic and aligned with the active context budget policy.\n\nimport { EventEmitter } from \"events\";\n\nexport interface HeadroomSnapshot {\n\ttimestamp: number;\n\tpromptTokens: number;\n\ttoolOutputTokens: number;\n\tresponseTokens: number;\n\ttotalTokens: number;\n\theadroomRemaining: number;\n\tstatus: \"idle\" | \"active\" | \"stressed\" | \"critical\" | \"blocked\";\n}\n\nexport interface HeadroomPrediction {\n\tpredictedTokens: number;\n\tconfidence: number;\n\ttrend: \"increasing\" | \"stable\" | \"decreasing\";\n\trecommendedAction: string;\n}\n\nexport class HeadroomMonitor extends EventEmitter {\n\tprivate history: HeadroomSnapshot[] = [];\n\tprivate maxHistory = 100;\n\n\trecord(snapshot: HeadroomSnapshot): void {\n\t\tconst sanitized = sanitizeSnapshot(snapshot);\n\t\tthis.history.push(sanitized);\n\t\tif (this.history.length > this.maxHistory) {\n\t\t\tthis.history = this.history.slice(-this.maxHistory);\n\t\t}\n\t\tthis.emit(\"snapshot\", sanitized);\n\t}\n\n\tpredict(): HeadroomPrediction {\n\t\tif (this.history.length < 3) {\n\t\t\treturn { predictedTokens: 0, confidence: 0, trend: \"stable\", recommendedAction: \"collect more data\" };\n\t\t}\n\n\t\tconst recent = this.history.slice(-10);\n\t\tconst first = recent[0].totalTokens;\n\t\tconst last = recent[recent.length - 1].totalTokens;\n\t\tconst delta = last - first;\n\t\tconst slope = delta / Math.max(recent.length - 1, 1);\n\t\tconst trend = Math.abs(delta) <= Math.max(1, last * 0.02) ? \"stable\" : delta > 0 ? \"increasing\" : \"decreasing\";\n\t\tconst projectedTokens = trend === \"increasing\" ? last + Math.max(1, slope) : Math.max(0, last + slope);\n\t\tconst confidence = Math.min(0.95, 0.45 + recent.length * 0.05);\n\n\t\treturn {\n\t\t\tpredictedTokens: Math.round(projectedTokens),\n\t\t\tconfidence,\n\t\t\ttrend,\n\t\t\trecommendedAction: trend === \"increasing\" ? \"compress context\" : \"maintain\",\n\t\t};\n\t}\n\n\tgetStatus(totalTokens: number, maxTokens: number): HeadroomSnapshot[\"status\"] {\n\t\tconst normalizedTotalTokens = normalizeTokenInput(totalTokens);\n\t\tconst normalizedMaxTokens = normalizeTokenLimit(maxTokens);\n\t\tif (normalizedMaxTokens === 0) {\n\t\t\treturn normalizedTotalTokens === 0 ? \"idle\" : \"blocked\";\n\t\t}\n\n\t\tconst ratio = normalizedTotalTokens / normalizedMaxTokens;\n\t\tlet status: HeadroomSnapshot[\"status\"];\n\t\tif (ratio < 0.4) {\n\t\t\tstatus = \"idle\";\n\t\t} else if (ratio < 0.7) {\n\t\t\tstatus = \"active\";\n\t\t} else if (ratio < 0.8) {\n\t\t\tstatus = \"stressed\";\n\t\t} else if (ratio < 0.9) {\n\t\t\tstatus = \"critical\";\n\t\t} else {\n\t\t\tstatus = \"blocked\";\n\t\t}\n\n\t\tconst previous = this.history.at(-1)?.status;\n\t\tif (previous === \"critical\" && status === \"stressed\" && ratio >= 0.75) {\n\t\t\treturn \"critical\";\n\t\t}\n\t\tif (previous === \"blocked\" && status !== \"blocked\" && ratio >= 0.85) {\n\t\t\treturn \"blocked\";\n\t\t}\n\t\treturn status;\n\t}\n}\n\nexport class DynamicContextAllocator extends EventEmitter {\n\tprivate maxTokens: number;\n\n\tconstructor(maxTokens = 15000) {\n\t\tsuper();\n\t\tthis.maxTokens = normalizeTokenLimit(maxTokens);\n\t}\n\n\tallocate(currentTokens: number): { maxTokens: number; canAddAgents: boolean } {\n\t\tif (this.maxTokens === 0) {\n\t\t\treturn { maxTokens: 0, canAddAgents: false };\n\t\t}\n\t\tconst ratio = normalizeTokenInput(currentTokens) / this.maxTokens;\n\t\tif (ratio < 0.4) return { maxTokens: this.maxTokens, canAddAgents: true };\n\t\tif (ratio < 0.7) return { maxTokens: this.maxTokens * 0.9, canAddAgents: true };\n\t\tif (ratio < 0.8) return { maxTokens: this.maxTokens * 0.75, canAddAgents: false };\n\t\tif (ratio < 0.9) return { maxTokens: this.maxTokens * 0.5, canAddAgents: false };\n\t\treturn { maxTokens: 0, canAddAgents: false };\n\t}\n}\n\nexport class HeadroomManager extends EventEmitter {\n\tprivate monitor: HeadroomMonitor;\n\tprivate allocator: DynamicContextAllocator;\n\tprivate maxTokens: number;\n\n\tconstructor(maxTokens = 15000) {\n\t\tsuper();\n\t\tthis.maxTokens = normalizeTokenLimit(maxTokens);\n\t\tthis.monitor = new HeadroomMonitor();\n\t\tthis.allocator = new DynamicContextAllocator(this.maxTokens);\n\t}\n\n\tcheck(promptTokens: number, toolOutputTokens = 0, responseTokens = 0): HeadroomSnapshot {\n\t\tconst normalizedPromptTokens = normalizeTokenInput(promptTokens);\n\t\tconst normalizedToolOutputTokens = normalizeTokenInput(toolOutputTokens);\n\t\tconst normalizedResponseTokens = normalizeTokenInput(responseTokens);\n\t\tconst totalTokens = normalizedPromptTokens + normalizedToolOutputTokens + normalizedResponseTokens;\n\t\tconst status = this.monitor.getStatus(totalTokens, this.maxTokens);\n\t\tthis.allocator.allocate(totalTokens);\n\n\t\tconst snapshot: HeadroomSnapshot = {\n\t\t\ttimestamp: Date.now(),\n\t\t\tpromptTokens: normalizedPromptTokens,\n\t\t\ttoolOutputTokens: normalizedToolOutputTokens,\n\t\t\tresponseTokens: normalizedResponseTokens,\n\t\t\ttotalTokens,\n\t\t\theadroomRemaining: this.maxTokens - totalTokens,\n\t\t\tstatus,\n\t\t};\n\n\t\tthis.monitor.record(snapshot);\n\n\t\tif (status === \"critical\" || status === \"blocked\") {\n\t\t\tthis.emit(\"warning\", { status, message: `Headroom ${status}: ${totalTokens}/${this.maxTokens} tokens` });\n\t\t}\n\n\t\treturn snapshot;\n\t}\n\n\tpredict(): HeadroomPrediction {\n\t\treturn this.monitor.predict();\n\t}\n\n\tgetAllocation(currentTokens: number): { maxTokens: number; canAddAgents: boolean } {\n\t\treturn this.allocator.allocate(currentTokens);\n\t}\n}\n\nexport default HeadroomManager;\n\nfunction sanitizeSnapshot(snapshot: HeadroomSnapshot): HeadroomSnapshot {\n\treturn {\n\t\ttimestamp: Number.isFinite(snapshot.timestamp) ? Math.floor(snapshot.timestamp) : 0,\n\t\tpromptTokens: normalizeTokenInput(snapshot.promptTokens),\n\t\ttoolOutputTokens: normalizeTokenInput(snapshot.toolOutputTokens),\n\t\tresponseTokens: normalizeTokenInput(snapshot.responseTokens),\n\t\ttotalTokens: normalizeTokenInput(snapshot.totalTokens),\n\t\theadroomRemaining: normalizeFiniteInteger(snapshot.headroomRemaining),\n\t\tstatus: snapshot.status,\n\t};\n}\n\nfunction normalizeTokenLimit(value: number): number {\n\treturn normalizeTokenInput(value);\n}\n\nfunction normalizeTokenInput(value: number): number {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n\nfunction normalizeFiniteInteger(value: number): number {\n\tif (!Number.isFinite(value)) {\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n"]}
@@ -6,15 +6,11 @@
6
6
  // This file is preserved for legacy extension/API consumers and regression tests.
7
7
  //
8
8
  // ACTUAL HEADROOM / TOKEN BUDGET SYSTEM:
9
- // - types/jailbreak.ts → defines HeadroomSnapshot, HeadroomPrediction (different shape)
10
9
  // - context-budget-compressors.ts → context budget compression with adapters
11
10
  // - context-budget-token-counter.ts → token counting for budget decisions
12
11
  // - compaction/compaction.ts getCompactionHeadroomThreshold() → headroom threshold
13
12
  // - compaction/compaction.ts shouldCompact() → triggers compaction when headroom low
14
13
  //
15
- // NOTE: types/jailbreak.ts defines similar types (HeadroomSnapshot, HeadroomPrediction)
16
- // but with different field structures. Both are distinct from this file's types.
17
- //
18
14
  // Keep this utility deterministic and aligned with the active context budget policy.
19
15
  import { EventEmitter } from "events";
20
16
  export class HeadroomMonitor extends EventEmitter {
@@ -1 +1 @@
1
- {"version":3,"file":"headroom.js","sourceRoot":"","sources":["../../src/core/headroom.ts"],"names":[],"mappings":"AAAA,6CAA2C;AAC3C,qEAAqE;AACrE,6CAA6C;AAC7C,EAAE;AACF,iCAAiC;AACjC,kFAAkF;AAClF,EAAE;AACF,yCAAyC;AACzC,4FAA0F;AAC1F,iFAA+E;AAC/E,8EAA4E;AAC5E,uFAAqF;AACrF,yFAAuF;AACvF,EAAE;AACF,wFAAwF;AACxF,iFAAiF;AACjF,EAAE;AACF,qFAAqF;AAErF,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAmBtC,MAAM,OAAO,eAAgB,SAAQ,YAAY;IACxC,OAAO,GAAuB,EAAE,CAAC;IACjC,UAAU,GAAG,GAAG,CAAC;IAEzB,MAAM,CAAC,QAA0B,EAAQ;QACxC,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAC3C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACrD,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAAA,CACjC;IAED,OAAO,GAAuB;QAC7B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,EAAE,eAAe,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAC;QACvG,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;QACpC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC;QACnD,MAAM,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;QAC3B,MAAM,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;QAC/G,MAAM,eAAe,GAAG,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,KAAK,CAAC,CAAC;QACvG,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QAE/D,OAAO;YACN,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC;YAC5C,UAAU;YACV,KAAK;YACL,iBAAiB,EAAE,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,UAAU;SAC3E,CAAC;IAAA,CACF;IAED,SAAS,CAAC,WAAmB,EAAE,SAAiB,EAA8B;QAC7E,MAAM,qBAAqB,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAC/D,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;QAC3D,IAAI,mBAAmB,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,qBAAqB,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QACzD,CAAC;QAED,MAAM,KAAK,GAAG,qBAAqB,GAAG,mBAAmB,CAAC;QAC1D,IAAI,MAAkC,CAAC;QACvC,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;YACjB,MAAM,GAAG,MAAM,CAAC;QACjB,CAAC;aAAM,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;YACxB,MAAM,GAAG,QAAQ,CAAC;QACnB,CAAC;aAAM,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;YACxB,MAAM,GAAG,UAAU,CAAC;QACrB,CAAC;aAAM,IAAI,KAAK,GAAG,GAAG,EAAE,CAAC;YACxB,MAAM,GAAG,UAAU,CAAC;QACrB,CAAC;aAAM,CAAC;YACP,MAAM,GAAG,SAAS,CAAC;QACpB,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;QAC7C,IAAI,QAAQ,KAAK,UAAU,IAAI,MAAM,KAAK,UAAU,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YACvE,OAAO,UAAU,CAAC;QACnB,CAAC;QACD,IAAI,QAAQ,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YACrE,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,OAAO,MAAM,CAAC;IAAA,CACd;CACD;AAED,MAAM,OAAO,uBAAwB,SAAQ,YAAY;IAChD,SAAS,CAAS;IAE1B,YAAY,SAAS,GAAG,KAAK,EAAE;QAC9B,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,SAAS,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAAA,CAChD;IAED,QAAQ,CAAC,aAAqB,EAAgD;QAC7E,IAAI,IAAI,CAAC,SAAS,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QAC9C,CAAC;QACD,MAAM,KAAK,GAAG,mBAAmB,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;QAClE,IAAI,KAAK,GAAG,GAAG;YAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;QAC1E,IAAI,KAAK,GAAG,GAAG;YAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,GAAG,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;QAChF,IAAI,KAAK,GAAG,GAAG;YAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,GAAG,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QAClF,IAAI,KAAK,GAAG,GAAG;YAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,GAAG,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QACjF,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAAA,CAC7C;CACD;AAED,MAAM,OAAO,eAAgB,SAAQ,YAAY;IACxC,OAAO,CAAkB;IACzB,SAAS,CAA0B;IACnC,SAAS,CAAS;IAE1B,YAAY,SAAS,GAAG,KAAK,EAAE;QAC9B,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,SAAS,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,IAAI,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAAA,CAC7D;IAED,KAAK,CAAC,YAAoB,EAAE,gBAAgB,GAAG,CAAC,EAAE,cAAc,GAAG,CAAC,EAAoB;QACvF,MAAM,sBAAsB,GAAG,mBAAmB,CAAC,YAAY,CAAC,CAAC;QACjE,MAAM,0BAA0B,GAAG,mBAAmB,CAAC,gBAAgB,CAAC,CAAC;QACzE,MAAM,wBAAwB,GAAG,mBAAmB,CAAC,cAAc,CAAC,CAAC;QACrE,MAAM,WAAW,GAAG,sBAAsB,GAAG,0BAA0B,GAAG,wBAAwB,CAAC;QACnG,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACnE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAErC,MAAM,QAAQ,GAAqB;YAClC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,YAAY,EAAE,sBAAsB;YACpC,gBAAgB,EAAE,0BAA0B;YAC5C,cAAc,EAAE,wBAAwB;YACxC,WAAW;YACX,iBAAiB,EAAE,IAAI,CAAC,SAAS,GAAG,WAAW;YAC/C,MAAM;SACN,CAAC;QAEF,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE9B,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACnD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,MAAM,KAAK,WAAW,IAAI,IAAI,CAAC,SAAS,SAAS,EAAE,CAAC,CAAC;QAC1G,CAAC;QAED,OAAO,QAAQ,CAAC;IAAA,CAChB;IAED,OAAO,GAAuB;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAAA,CAC9B;IAED,aAAa,CAAC,aAAqB,EAAgD;QAClF,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAAA,CAC9C;CACD;AAED,eAAe,eAAe,CAAC;AAE/B,SAAS,gBAAgB,CAAC,QAA0B,EAAoB;IACvE,OAAO;QACN,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QACnF,YAAY,EAAE,mBAAmB,CAAC,QAAQ,CAAC,YAAY,CAAC;QACxD,gBAAgB,EAAE,mBAAmB,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAChE,cAAc,EAAE,mBAAmB,CAAC,QAAQ,CAAC,cAAc,CAAC;QAC5D,WAAW,EAAE,mBAAmB,CAAC,QAAQ,CAAC,WAAW,CAAC;QACtD,iBAAiB,EAAE,sBAAsB,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QACrE,MAAM,EAAE,QAAQ,CAAC,MAAM;KACvB,CAAC;AAAA,CACF;AAED,SAAS,mBAAmB,CAAC,KAAa,EAAU;IACnD,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC;AAAA,CAClC;AAED,SAAS,mBAAmB,CAAC,KAAa,EAAU;IACnD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC1C,OAAO,CAAC,CAAC;IACV,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAAA,CACzB;AAED,SAAS,sBAAsB,CAAC,KAAa,EAAU;IACtD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,CAAC;IACV,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAAA,CACzB","sourcesContent":["// OMK v6.2 — Headroom Manager (TypeScript)\n// ==================================================================\n// packages/coding-agent/src/core/headroom.ts\n//\n// STATUS: compatibility utility.\n// This file is preserved for legacy extension/API consumers and regression tests.\n//\n// ACTUAL HEADROOM / TOKEN BUDGET SYSTEM:\n// - types/jailbreak.ts → defines HeadroomSnapshot, HeadroomPrediction (different shape)\n// - context-budget-compressors.ts → context budget compression with adapters\n// - context-budget-token-counter.ts → token counting for budget decisions\n// - compaction/compaction.ts getCompactionHeadroomThreshold() → headroom threshold\n// - compaction/compaction.ts shouldCompact() → triggers compaction when headroom low\n//\n// NOTE: types/jailbreak.ts defines similar types (HeadroomSnapshot, HeadroomPrediction)\n// but with different field structures. Both are distinct from this file's types.\n//\n// Keep this utility deterministic and aligned with the active context budget policy.\n\nimport { EventEmitter } from \"events\";\n\nexport interface HeadroomSnapshot {\n\ttimestamp: number;\n\tpromptTokens: number;\n\ttoolOutputTokens: number;\n\tresponseTokens: number;\n\ttotalTokens: number;\n\theadroomRemaining: number;\n\tstatus: \"idle\" | \"active\" | \"stressed\" | \"critical\" | \"blocked\";\n}\n\nexport interface HeadroomPrediction {\n\tpredictedTokens: number;\n\tconfidence: number;\n\ttrend: \"increasing\" | \"stable\" | \"decreasing\";\n\trecommendedAction: string;\n}\n\nexport class HeadroomMonitor extends EventEmitter {\n\tprivate history: HeadroomSnapshot[] = [];\n\tprivate maxHistory = 100;\n\n\trecord(snapshot: HeadroomSnapshot): void {\n\t\tconst sanitized = sanitizeSnapshot(snapshot);\n\t\tthis.history.push(sanitized);\n\t\tif (this.history.length > this.maxHistory) {\n\t\t\tthis.history = this.history.slice(-this.maxHistory);\n\t\t}\n\t\tthis.emit(\"snapshot\", sanitized);\n\t}\n\n\tpredict(): HeadroomPrediction {\n\t\tif (this.history.length < 3) {\n\t\t\treturn { predictedTokens: 0, confidence: 0, trend: \"stable\", recommendedAction: \"collect more data\" };\n\t\t}\n\n\t\tconst recent = this.history.slice(-10);\n\t\tconst first = recent[0].totalTokens;\n\t\tconst last = recent[recent.length - 1].totalTokens;\n\t\tconst delta = last - first;\n\t\tconst slope = delta / Math.max(recent.length - 1, 1);\n\t\tconst trend = Math.abs(delta) <= Math.max(1, last * 0.02) ? \"stable\" : delta > 0 ? \"increasing\" : \"decreasing\";\n\t\tconst projectedTokens = trend === \"increasing\" ? last + Math.max(1, slope) : Math.max(0, last + slope);\n\t\tconst confidence = Math.min(0.95, 0.45 + recent.length * 0.05);\n\n\t\treturn {\n\t\t\tpredictedTokens: Math.round(projectedTokens),\n\t\t\tconfidence,\n\t\t\ttrend,\n\t\t\trecommendedAction: trend === \"increasing\" ? \"compress context\" : \"maintain\",\n\t\t};\n\t}\n\n\tgetStatus(totalTokens: number, maxTokens: number): HeadroomSnapshot[\"status\"] {\n\t\tconst normalizedTotalTokens = normalizeTokenInput(totalTokens);\n\t\tconst normalizedMaxTokens = normalizeTokenLimit(maxTokens);\n\t\tif (normalizedMaxTokens === 0) {\n\t\t\treturn normalizedTotalTokens === 0 ? \"idle\" : \"blocked\";\n\t\t}\n\n\t\tconst ratio = normalizedTotalTokens / normalizedMaxTokens;\n\t\tlet status: HeadroomSnapshot[\"status\"];\n\t\tif (ratio < 0.4) {\n\t\t\tstatus = \"idle\";\n\t\t} else if (ratio < 0.7) {\n\t\t\tstatus = \"active\";\n\t\t} else if (ratio < 0.8) {\n\t\t\tstatus = \"stressed\";\n\t\t} else if (ratio < 0.9) {\n\t\t\tstatus = \"critical\";\n\t\t} else {\n\t\t\tstatus = \"blocked\";\n\t\t}\n\n\t\tconst previous = this.history.at(-1)?.status;\n\t\tif (previous === \"critical\" && status === \"stressed\" && ratio >= 0.75) {\n\t\t\treturn \"critical\";\n\t\t}\n\t\tif (previous === \"blocked\" && status !== \"blocked\" && ratio >= 0.85) {\n\t\t\treturn \"blocked\";\n\t\t}\n\t\treturn status;\n\t}\n}\n\nexport class DynamicContextAllocator extends EventEmitter {\n\tprivate maxTokens: number;\n\n\tconstructor(maxTokens = 15000) {\n\t\tsuper();\n\t\tthis.maxTokens = normalizeTokenLimit(maxTokens);\n\t}\n\n\tallocate(currentTokens: number): { maxTokens: number; canAddAgents: boolean } {\n\t\tif (this.maxTokens === 0) {\n\t\t\treturn { maxTokens: 0, canAddAgents: false };\n\t\t}\n\t\tconst ratio = normalizeTokenInput(currentTokens) / this.maxTokens;\n\t\tif (ratio < 0.4) return { maxTokens: this.maxTokens, canAddAgents: true };\n\t\tif (ratio < 0.7) return { maxTokens: this.maxTokens * 0.9, canAddAgents: true };\n\t\tif (ratio < 0.8) return { maxTokens: this.maxTokens * 0.75, canAddAgents: false };\n\t\tif (ratio < 0.9) return { maxTokens: this.maxTokens * 0.5, canAddAgents: false };\n\t\treturn { maxTokens: 0, canAddAgents: false };\n\t}\n}\n\nexport class HeadroomManager extends EventEmitter {\n\tprivate monitor: HeadroomMonitor;\n\tprivate allocator: DynamicContextAllocator;\n\tprivate maxTokens: number;\n\n\tconstructor(maxTokens = 15000) {\n\t\tsuper();\n\t\tthis.maxTokens = normalizeTokenLimit(maxTokens);\n\t\tthis.monitor = new HeadroomMonitor();\n\t\tthis.allocator = new DynamicContextAllocator(this.maxTokens);\n\t}\n\n\tcheck(promptTokens: number, toolOutputTokens = 0, responseTokens = 0): HeadroomSnapshot {\n\t\tconst normalizedPromptTokens = normalizeTokenInput(promptTokens);\n\t\tconst normalizedToolOutputTokens = normalizeTokenInput(toolOutputTokens);\n\t\tconst normalizedResponseTokens = normalizeTokenInput(responseTokens);\n\t\tconst totalTokens = normalizedPromptTokens + normalizedToolOutputTokens + normalizedResponseTokens;\n\t\tconst status = this.monitor.getStatus(totalTokens, this.maxTokens);\n\t\tthis.allocator.allocate(totalTokens);\n\n\t\tconst snapshot: HeadroomSnapshot = {\n\t\t\ttimestamp: Date.now(),\n\t\t\tpromptTokens: normalizedPromptTokens,\n\t\t\ttoolOutputTokens: normalizedToolOutputTokens,\n\t\t\tresponseTokens: normalizedResponseTokens,\n\t\t\ttotalTokens,\n\t\t\theadroomRemaining: this.maxTokens - totalTokens,\n\t\t\tstatus,\n\t\t};\n\n\t\tthis.monitor.record(snapshot);\n\n\t\tif (status === \"critical\" || status === \"blocked\") {\n\t\t\tthis.emit(\"warning\", { status, message: `Headroom ${status}: ${totalTokens}/${this.maxTokens} tokens` });\n\t\t}\n\n\t\treturn snapshot;\n\t}\n\n\tpredict(): HeadroomPrediction {\n\t\treturn this.monitor.predict();\n\t}\n\n\tgetAllocation(currentTokens: number): { maxTokens: number; canAddAgents: boolean } {\n\t\treturn this.allocator.allocate(currentTokens);\n\t}\n}\n\nexport default HeadroomManager;\n\nfunction sanitizeSnapshot(snapshot: HeadroomSnapshot): HeadroomSnapshot {\n\treturn {\n\t\ttimestamp: Number.isFinite(snapshot.timestamp) ? Math.floor(snapshot.timestamp) : 0,\n\t\tpromptTokens: normalizeTokenInput(snapshot.promptTokens),\n\t\ttoolOutputTokens: normalizeTokenInput(snapshot.toolOutputTokens),\n\t\tresponseTokens: normalizeTokenInput(snapshot.responseTokens),\n\t\ttotalTokens: normalizeTokenInput(snapshot.totalTokens),\n\t\theadroomRemaining: normalizeFiniteInteger(snapshot.headroomRemaining),\n\t\tstatus: snapshot.status,\n\t};\n}\n\nfunction normalizeTokenLimit(value: number): number {\n\treturn normalizeTokenInput(value);\n}\n\nfunction normalizeTokenInput(value: number): number {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n\nfunction normalizeFiniteInteger(value: number): number {\n\tif (!Number.isFinite(value)) {\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n"]}
1
+ {"version":3,"file":"headroom.js","sourceRoot":"","sources":["../../src/core/headroom.ts"],"names":[],"mappings":"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","sourcesContent":["// OMK v6.2 — Headroom Manager (TypeScript)\n// ==================================================================\n// packages/coding-agent/src/core/headroom.ts\n//\n// STATUS: compatibility utility.\n// This file is preserved for legacy extension/API consumers and regression tests.\n//\n// ACTUAL HEADROOM / TOKEN BUDGET SYSTEM:\n// - context-budget-compressors.ts → context budget compression with adapters\n// - context-budget-token-counter.ts → token counting for budget decisions\n// - compaction/compaction.ts getCompactionHeadroomThreshold() → headroom threshold\n// - compaction/compaction.ts shouldCompact() → triggers compaction when headroom low\n//\n// Keep this utility deterministic and aligned with the active context budget policy.\n\nimport { EventEmitter } from \"events\";\n\nexport interface HeadroomSnapshot {\n\ttimestamp: number;\n\tpromptTokens: number;\n\ttoolOutputTokens: number;\n\tresponseTokens: number;\n\ttotalTokens: number;\n\theadroomRemaining: number;\n\tstatus: \"idle\" | \"active\" | \"stressed\" | \"critical\" | \"blocked\";\n}\n\nexport interface HeadroomPrediction {\n\tpredictedTokens: number;\n\tconfidence: number;\n\ttrend: \"increasing\" | \"stable\" | \"decreasing\";\n\trecommendedAction: string;\n}\n\nexport class HeadroomMonitor extends EventEmitter {\n\tprivate history: HeadroomSnapshot[] = [];\n\tprivate maxHistory = 100;\n\n\trecord(snapshot: HeadroomSnapshot): void {\n\t\tconst sanitized = sanitizeSnapshot(snapshot);\n\t\tthis.history.push(sanitized);\n\t\tif (this.history.length > this.maxHistory) {\n\t\t\tthis.history = this.history.slice(-this.maxHistory);\n\t\t}\n\t\tthis.emit(\"snapshot\", sanitized);\n\t}\n\n\tpredict(): HeadroomPrediction {\n\t\tif (this.history.length < 3) {\n\t\t\treturn { predictedTokens: 0, confidence: 0, trend: \"stable\", recommendedAction: \"collect more data\" };\n\t\t}\n\n\t\tconst recent = this.history.slice(-10);\n\t\tconst first = recent[0].totalTokens;\n\t\tconst last = recent[recent.length - 1].totalTokens;\n\t\tconst delta = last - first;\n\t\tconst slope = delta / Math.max(recent.length - 1, 1);\n\t\tconst trend = Math.abs(delta) <= Math.max(1, last * 0.02) ? \"stable\" : delta > 0 ? \"increasing\" : \"decreasing\";\n\t\tconst projectedTokens = trend === \"increasing\" ? last + Math.max(1, slope) : Math.max(0, last + slope);\n\t\tconst confidence = Math.min(0.95, 0.45 + recent.length * 0.05);\n\n\t\treturn {\n\t\t\tpredictedTokens: Math.round(projectedTokens),\n\t\t\tconfidence,\n\t\t\ttrend,\n\t\t\trecommendedAction: trend === \"increasing\" ? \"compress context\" : \"maintain\",\n\t\t};\n\t}\n\n\tgetStatus(totalTokens: number, maxTokens: number): HeadroomSnapshot[\"status\"] {\n\t\tconst normalizedTotalTokens = normalizeTokenInput(totalTokens);\n\t\tconst normalizedMaxTokens = normalizeTokenLimit(maxTokens);\n\t\tif (normalizedMaxTokens === 0) {\n\t\t\treturn normalizedTotalTokens === 0 ? \"idle\" : \"blocked\";\n\t\t}\n\n\t\tconst ratio = normalizedTotalTokens / normalizedMaxTokens;\n\t\tlet status: HeadroomSnapshot[\"status\"];\n\t\tif (ratio < 0.4) {\n\t\t\tstatus = \"idle\";\n\t\t} else if (ratio < 0.7) {\n\t\t\tstatus = \"active\";\n\t\t} else if (ratio < 0.8) {\n\t\t\tstatus = \"stressed\";\n\t\t} else if (ratio < 0.9) {\n\t\t\tstatus = \"critical\";\n\t\t} else {\n\t\t\tstatus = \"blocked\";\n\t\t}\n\n\t\tconst previous = this.history.at(-1)?.status;\n\t\tif (previous === \"critical\" && status === \"stressed\" && ratio >= 0.75) {\n\t\t\treturn \"critical\";\n\t\t}\n\t\tif (previous === \"blocked\" && status !== \"blocked\" && ratio >= 0.85) {\n\t\t\treturn \"blocked\";\n\t\t}\n\t\treturn status;\n\t}\n}\n\nexport class DynamicContextAllocator extends EventEmitter {\n\tprivate maxTokens: number;\n\n\tconstructor(maxTokens = 15000) {\n\t\tsuper();\n\t\tthis.maxTokens = normalizeTokenLimit(maxTokens);\n\t}\n\n\tallocate(currentTokens: number): { maxTokens: number; canAddAgents: boolean } {\n\t\tif (this.maxTokens === 0) {\n\t\t\treturn { maxTokens: 0, canAddAgents: false };\n\t\t}\n\t\tconst ratio = normalizeTokenInput(currentTokens) / this.maxTokens;\n\t\tif (ratio < 0.4) return { maxTokens: this.maxTokens, canAddAgents: true };\n\t\tif (ratio < 0.7) return { maxTokens: this.maxTokens * 0.9, canAddAgents: true };\n\t\tif (ratio < 0.8) return { maxTokens: this.maxTokens * 0.75, canAddAgents: false };\n\t\tif (ratio < 0.9) return { maxTokens: this.maxTokens * 0.5, canAddAgents: false };\n\t\treturn { maxTokens: 0, canAddAgents: false };\n\t}\n}\n\nexport class HeadroomManager extends EventEmitter {\n\tprivate monitor: HeadroomMonitor;\n\tprivate allocator: DynamicContextAllocator;\n\tprivate maxTokens: number;\n\n\tconstructor(maxTokens = 15000) {\n\t\tsuper();\n\t\tthis.maxTokens = normalizeTokenLimit(maxTokens);\n\t\tthis.monitor = new HeadroomMonitor();\n\t\tthis.allocator = new DynamicContextAllocator(this.maxTokens);\n\t}\n\n\tcheck(promptTokens: number, toolOutputTokens = 0, responseTokens = 0): HeadroomSnapshot {\n\t\tconst normalizedPromptTokens = normalizeTokenInput(promptTokens);\n\t\tconst normalizedToolOutputTokens = normalizeTokenInput(toolOutputTokens);\n\t\tconst normalizedResponseTokens = normalizeTokenInput(responseTokens);\n\t\tconst totalTokens = normalizedPromptTokens + normalizedToolOutputTokens + normalizedResponseTokens;\n\t\tconst status = this.monitor.getStatus(totalTokens, this.maxTokens);\n\t\tthis.allocator.allocate(totalTokens);\n\n\t\tconst snapshot: HeadroomSnapshot = {\n\t\t\ttimestamp: Date.now(),\n\t\t\tpromptTokens: normalizedPromptTokens,\n\t\t\ttoolOutputTokens: normalizedToolOutputTokens,\n\t\t\tresponseTokens: normalizedResponseTokens,\n\t\t\ttotalTokens,\n\t\t\theadroomRemaining: this.maxTokens - totalTokens,\n\t\t\tstatus,\n\t\t};\n\n\t\tthis.monitor.record(snapshot);\n\n\t\tif (status === \"critical\" || status === \"blocked\") {\n\t\t\tthis.emit(\"warning\", { status, message: `Headroom ${status}: ${totalTokens}/${this.maxTokens} tokens` });\n\t\t}\n\n\t\treturn snapshot;\n\t}\n\n\tpredict(): HeadroomPrediction {\n\t\treturn this.monitor.predict();\n\t}\n\n\tgetAllocation(currentTokens: number): { maxTokens: number; canAddAgents: boolean } {\n\t\treturn this.allocator.allocate(currentTokens);\n\t}\n}\n\nexport default HeadroomManager;\n\nfunction sanitizeSnapshot(snapshot: HeadroomSnapshot): HeadroomSnapshot {\n\treturn {\n\t\ttimestamp: Number.isFinite(snapshot.timestamp) ? Math.floor(snapshot.timestamp) : 0,\n\t\tpromptTokens: normalizeTokenInput(snapshot.promptTokens),\n\t\ttoolOutputTokens: normalizeTokenInput(snapshot.toolOutputTokens),\n\t\tresponseTokens: normalizeTokenInput(snapshot.responseTokens),\n\t\ttotalTokens: normalizeTokenInput(snapshot.totalTokens),\n\t\theadroomRemaining: normalizeFiniteInteger(snapshot.headroomRemaining),\n\t\tstatus: snapshot.status,\n\t};\n}\n\nfunction normalizeTokenLimit(value: number): number {\n\treturn normalizeTokenInput(value);\n}\n\nfunction normalizeTokenInput(value: number): number {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n\nfunction normalizeFiniteInteger(value: number): number {\n\tif (!Number.isFinite(value)) {\n\t\treturn 0;\n\t}\n\treturn Math.floor(value);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"hook-inventory.d.ts","sourceRoot":"","sources":["../../src/core/hook-inventory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3D,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,kBAAkB,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;CACjC;AAMD,eAAO,MAAM,aAAa,EAAE,SAAS,cAAc,EAyElD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,aAAa,CAsBlE","sourcesContent":["/**\n * Minimal hook inventory for the loadout runtime.\n *\n * The external harness resolves `scriptPath` to an actual shell script.\n * This module provides canonical names plus safe policy metadata so the\n * loadout layer can validate selectors without executing hooks.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { HookPolicyMetadata } from \"./hooks/index.ts\";\nimport { DEFAULT_HOOK_POLICY, sanitizeHookPolicy } from \"./hooks/index.ts\";\n\nexport interface HookDescriptor {\n\tname: string;\n\tscriptPath?: string;\n\tbuiltin: boolean;\n\tpolicy: HookPolicyMetadata;\n}\n\nexport interface HookInventory {\n\thooks: readonly HookDescriptor[];\n}\n\nfunction builtinHook(name: string, policy: HookPolicyMetadata): HookDescriptor {\n\treturn { name, builtin: true, policy: sanitizeHookPolicy(policy) };\n}\n\nexport const BUILTIN_HOOKS: readonly HookDescriptor[] = [\n\tbuiltinHook(\"pre-shell-guard\", {\n\t\tstages: [\"tool_call\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 3_000,\n\t}),\n\tbuiltinHook(\"protect-secrets\", {\n\t\tstages: [\"tool_call\", \"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"session-context\", {\n\t\tstages: [\"session_start\"],\n\t\teffects: [\"mutator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"precompact-checkpoint\", {\n\t\tstages: [\"pre_compact\"],\n\t\teffects: [\"observer\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"typecheck-after-edit\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"stop-verify\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"subagent-stop-audit\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n\tbuiltinHook(\"npm-audit-summary\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"observer\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"bounded-evidence\", {\n\t\tstages: [\"tool_result\", \"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n\tbuiltinHook(\"document-artifact-guard\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"component-spec-before-build\", {\n\t\tstages: [\"tool_call\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"visual-diff-after-edit\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n];\n\n/**\n * Load the hook inventory. Builtin hooks are always present; if an agent\n * directory is provided and its `hooks` subdirectory exists, any shell scripts\n * discovered there are appended as non-builtin descriptors.\n */\nexport function loadHookInventory(agentDir?: string): HookInventory {\n\tconst hooks: HookDescriptor[] = [...BUILTIN_HOOKS];\n\tif (agentDir) {\n\t\ttry {\n\t\t\tconst hooksDir = path.join(agentDir, \"hooks\");\n\t\t\tconst entries = fs.readdirSync(hooksDir).sort();\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (!entry.endsWith(\".sh\")) continue;\n\t\t\t\tconst name = entry.slice(0, -3);\n\t\t\t\tif (hooks.some((h) => h.name === name)) continue;\n\t\t\t\thooks.push({\n\t\t\t\t\tname,\n\t\t\t\t\tscriptPath: path.join(hooksDir, entry),\n\t\t\t\t\tbuiltin: false,\n\t\t\t\t\tpolicy: sanitizeHookPolicy(DEFAULT_HOOK_POLICY),\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// Hooks directory may be absent; builtin inventory is sufficient.\n\t\t}\n\t}\n\treturn { hooks };\n}\n"]}
1
+ {"version":3,"file":"hook-inventory.d.ts","sourceRoot":"","sources":["../../src/core/hook-inventory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3D,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,kBAAkB,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;CACjC;AAMD,eAAO,MAAM,aAAa,EAAE,SAAS,cAAc,EA+ElD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,aAAa,CAsBlE","sourcesContent":["/**\n * Minimal hook inventory for the loadout runtime.\n *\n * The external harness resolves `scriptPath` to an actual shell script.\n * This module provides canonical names plus safe policy metadata so the\n * loadout layer can validate selectors without executing hooks.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { HookPolicyMetadata } from \"./hooks/index.ts\";\nimport { DEFAULT_HOOK_POLICY, sanitizeHookPolicy } from \"./hooks/index.ts\";\n\nexport interface HookDescriptor {\n\tname: string;\n\tscriptPath?: string;\n\tbuiltin: boolean;\n\tpolicy: HookPolicyMetadata;\n}\n\nexport interface HookInventory {\n\thooks: readonly HookDescriptor[];\n}\n\nfunction builtinHook(name: string, policy: HookPolicyMetadata): HookDescriptor {\n\treturn { name, builtin: true, policy: sanitizeHookPolicy(policy) };\n}\n\nexport const BUILTIN_HOOKS: readonly HookDescriptor[] = [\n\tbuiltinHook(\"pre-shell-guard\", {\n\t\tstages: [\"tool_call\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 3_000,\n\t}),\n\tbuiltinHook(\"protect-secrets\", {\n\t\tstages: [\"tool_call\", \"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"session-context\", {\n\t\tstages: [\"session_start\"],\n\t\teffects: [\"mutator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"precompact-checkpoint\", {\n\t\tstages: [\"pre_compact\"],\n\t\teffects: [\"observer\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"typecheck-after-edit\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"stop-verify\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"subagent-stop-audit\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n\tbuiltinHook(\"npm-audit-summary\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"observer\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"notify-sound-on-stop\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"observer\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"bounded-evidence\", {\n\t\tstages: [\"tool_result\", \"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n\tbuiltinHook(\"document-artifact-guard\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"component-spec-before-build\", {\n\t\tstages: [\"tool_call\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"visual-diff-after-edit\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n];\n\n/**\n * Load the hook inventory. Builtin hooks are always present; if an agent\n * directory is provided and its `hooks` subdirectory exists, any shell scripts\n * discovered there are appended as non-builtin descriptors.\n */\nexport function loadHookInventory(agentDir?: string): HookInventory {\n\tconst hooks: HookDescriptor[] = [...BUILTIN_HOOKS];\n\tif (agentDir) {\n\t\ttry {\n\t\t\tconst hooksDir = path.join(agentDir, \"hooks\");\n\t\t\tconst entries = fs.readdirSync(hooksDir).sort();\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (!entry.endsWith(\".sh\")) continue;\n\t\t\t\tconst name = entry.slice(0, -3);\n\t\t\t\tif (hooks.some((h) => h.name === name)) continue;\n\t\t\t\thooks.push({\n\t\t\t\t\tname,\n\t\t\t\t\tscriptPath: path.join(hooksDir, entry),\n\t\t\t\t\tbuiltin: false,\n\t\t\t\t\tpolicy: sanitizeHookPolicy(DEFAULT_HOOK_POLICY),\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// Hooks directory may be absent; builtin inventory is sufficient.\n\t\t}\n\t}\n\treturn { hooks };\n}\n"]}
@@ -60,6 +60,12 @@ export const BUILTIN_HOOKS = [
60
60
  failureMode: "fail-closed",
61
61
  timeoutMs: 30_000,
62
62
  }),
63
+ builtinHook("notify-sound-on-stop", {
64
+ stages: ["session_stop"],
65
+ effects: ["observer"],
66
+ failureMode: "fail-closed",
67
+ timeoutMs: 5_000,
68
+ }),
63
69
  builtinHook("bounded-evidence", {
64
70
  stages: ["tool_result", "session_stop"],
65
71
  effects: ["validator"],
@@ -1 +1 @@
1
- {"version":3,"file":"hook-inventory.js","sourceRoot":"","sources":["../../src/core/hook-inventory.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Minimal hook inventory for the loadout runtime.\n *\n * The external harness resolves `scriptPath` to an actual shell script.\n * This module provides canonical names plus safe policy metadata so the\n * loadout layer can validate selectors without executing hooks.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { HookPolicyMetadata } from \"./hooks/index.ts\";\nimport { DEFAULT_HOOK_POLICY, sanitizeHookPolicy } from \"./hooks/index.ts\";\n\nexport interface HookDescriptor {\n\tname: string;\n\tscriptPath?: string;\n\tbuiltin: boolean;\n\tpolicy: HookPolicyMetadata;\n}\n\nexport interface HookInventory {\n\thooks: readonly HookDescriptor[];\n}\n\nfunction builtinHook(name: string, policy: HookPolicyMetadata): HookDescriptor {\n\treturn { name, builtin: true, policy: sanitizeHookPolicy(policy) };\n}\n\nexport const BUILTIN_HOOKS: readonly HookDescriptor[] = [\n\tbuiltinHook(\"pre-shell-guard\", {\n\t\tstages: [\"tool_call\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 3_000,\n\t}),\n\tbuiltinHook(\"protect-secrets\", {\n\t\tstages: [\"tool_call\", \"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"session-context\", {\n\t\tstages: [\"session_start\"],\n\t\teffects: [\"mutator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"precompact-checkpoint\", {\n\t\tstages: [\"pre_compact\"],\n\t\teffects: [\"observer\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"typecheck-after-edit\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"stop-verify\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"subagent-stop-audit\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n\tbuiltinHook(\"npm-audit-summary\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"observer\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"bounded-evidence\", {\n\t\tstages: [\"tool_result\", \"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n\tbuiltinHook(\"document-artifact-guard\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"component-spec-before-build\", {\n\t\tstages: [\"tool_call\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"visual-diff-after-edit\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n];\n\n/**\n * Load the hook inventory. Builtin hooks are always present; if an agent\n * directory is provided and its `hooks` subdirectory exists, any shell scripts\n * discovered there are appended as non-builtin descriptors.\n */\nexport function loadHookInventory(agentDir?: string): HookInventory {\n\tconst hooks: HookDescriptor[] = [...BUILTIN_HOOKS];\n\tif (agentDir) {\n\t\ttry {\n\t\t\tconst hooksDir = path.join(agentDir, \"hooks\");\n\t\t\tconst entries = fs.readdirSync(hooksDir).sort();\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (!entry.endsWith(\".sh\")) continue;\n\t\t\t\tconst name = entry.slice(0, -3);\n\t\t\t\tif (hooks.some((h) => h.name === name)) continue;\n\t\t\t\thooks.push({\n\t\t\t\t\tname,\n\t\t\t\t\tscriptPath: path.join(hooksDir, entry),\n\t\t\t\t\tbuiltin: false,\n\t\t\t\t\tpolicy: sanitizeHookPolicy(DEFAULT_HOOK_POLICY),\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// Hooks directory may be absent; builtin inventory is sufficient.\n\t\t}\n\t}\n\treturn { hooks };\n}\n"]}
1
+ {"version":3,"file":"hook-inventory.js","sourceRoot":"","sources":["../../src/core/hook-inventory.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Minimal hook inventory for the loadout runtime.\n *\n * The external harness resolves `scriptPath` to an actual shell script.\n * This module provides canonical names plus safe policy metadata so the\n * loadout layer can validate selectors without executing hooks.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { HookPolicyMetadata } from \"./hooks/index.ts\";\nimport { DEFAULT_HOOK_POLICY, sanitizeHookPolicy } from \"./hooks/index.ts\";\n\nexport interface HookDescriptor {\n\tname: string;\n\tscriptPath?: string;\n\tbuiltin: boolean;\n\tpolicy: HookPolicyMetadata;\n}\n\nexport interface HookInventory {\n\thooks: readonly HookDescriptor[];\n}\n\nfunction builtinHook(name: string, policy: HookPolicyMetadata): HookDescriptor {\n\treturn { name, builtin: true, policy: sanitizeHookPolicy(policy) };\n}\n\nexport const BUILTIN_HOOKS: readonly HookDescriptor[] = [\n\tbuiltinHook(\"pre-shell-guard\", {\n\t\tstages: [\"tool_call\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 3_000,\n\t}),\n\tbuiltinHook(\"protect-secrets\", {\n\t\tstages: [\"tool_call\", \"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"session-context\", {\n\t\tstages: [\"session_start\"],\n\t\teffects: [\"mutator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"precompact-checkpoint\", {\n\t\tstages: [\"pre_compact\"],\n\t\teffects: [\"observer\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"typecheck-after-edit\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"stop-verify\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"subagent-stop-audit\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n\tbuiltinHook(\"npm-audit-summary\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"observer\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 30_000,\n\t}),\n\tbuiltinHook(\"notify-sound-on-stop\", {\n\t\tstages: [\"session_stop\"],\n\t\teffects: [\"observer\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"bounded-evidence\", {\n\t\tstages: [\"tool_result\", \"session_stop\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n\tbuiltinHook(\"document-artifact-guard\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"component-spec-before-build\", {\n\t\tstages: [\"tool_call\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 5_000,\n\t}),\n\tbuiltinHook(\"visual-diff-after-edit\", {\n\t\tstages: [\"tool_result\"],\n\t\teffects: [\"validator\"],\n\t\tfailureMode: \"fail-closed\",\n\t\ttimeoutMs: 10_000,\n\t}),\n];\n\n/**\n * Load the hook inventory. Builtin hooks are always present; if an agent\n * directory is provided and its `hooks` subdirectory exists, any shell scripts\n * discovered there are appended as non-builtin descriptors.\n */\nexport function loadHookInventory(agentDir?: string): HookInventory {\n\tconst hooks: HookDescriptor[] = [...BUILTIN_HOOKS];\n\tif (agentDir) {\n\t\ttry {\n\t\t\tconst hooksDir = path.join(agentDir, \"hooks\");\n\t\t\tconst entries = fs.readdirSync(hooksDir).sort();\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (!entry.endsWith(\".sh\")) continue;\n\t\t\t\tconst name = entry.slice(0, -3);\n\t\t\t\tif (hooks.some((h) => h.name === name)) continue;\n\t\t\t\thooks.push({\n\t\t\t\t\tname,\n\t\t\t\t\tscriptPath: path.join(hooksDir, entry),\n\t\t\t\t\tbuiltin: false,\n\t\t\t\t\tpolicy: sanitizeHookPolicy(DEFAULT_HOOK_POLICY),\n\t\t\t\t});\n\t\t\t}\n\t\t} catch {\n\t\t\t// Hooks directory may be absent; builtin inventory is sufficient.\n\t\t}\n\t}\n\treturn { hooks };\n}\n"]}
@@ -0,0 +1,64 @@
1
+ export declare const OMK_COMPUTER_USE_DEFAULT_PRESET_ID = "playwright-computer-use";
2
+ export type ComputerUsePresetKey = "browserUseAgent" | "playwrightComputerUse";
3
+ export type ComputerUsePresetId = "browser-use-agent" | "playwright-computer-use";
4
+ export type ComputerUseRisk = "R3";
5
+ export type ComputerUseEngine = "mcp" | "python-rust-sidecar";
6
+ export type ComputerUseBrowser = "chrome" | "firefox" | "msedge" | "webkit";
7
+ export type ComputerUseCookieMode = "existing-browser-session" | "isolated-storage-state" | "managed-profile" | "none";
8
+ export type ComputerUseCredentialScope = "none" | "session" | "workspace";
9
+ export type ComputerUseLaunchOption = "browser" | "cdp-endpoint" | "extension" | "storage-state" | "user-data-dir";
10
+ export interface ComputerUseTransport {
11
+ readonly args: readonly string[];
12
+ readonly command: string;
13
+ readonly packageName?: string;
14
+ readonly type: "stdio";
15
+ }
16
+ export interface ComputerUseBrowserMode {
17
+ readonly argsExtra?: readonly string[];
18
+ readonly browser?: ComputerUseBrowser;
19
+ readonly browserLimit?: "chrome-edge-only";
20
+ readonly cookieMode: ComputerUseCookieMode;
21
+ readonly description: string;
22
+ readonly id: "existing-chrome-session" | "isolated-storage-state" | "managed-chrome-profile" | "managed-firefox-profile";
23
+ readonly profileDir?: string;
24
+ }
25
+ export interface ComputerUsePolicy {
26
+ readonly allowedDomainsRequired?: boolean;
27
+ readonly allowedOriginsSecurityBoundary?: boolean;
28
+ readonly blockMetadataIp: boolean;
29
+ readonly blockPrivateNetwork: boolean;
30
+ readonly credentialScopes: readonly ComputerUseCredentialScope[];
31
+ readonly denyNoSandbox: boolean;
32
+ readonly descriptorHashPinned: boolean;
33
+ readonly evidenceLedger: boolean;
34
+ readonly maxActions: number;
35
+ readonly maxOutputBytes?: number;
36
+ readonly noManualProfileShellScript?: boolean;
37
+ readonly noUnrestrictedFileAccess: boolean;
38
+ readonly originAllowlistRequiredForCredentialedMode: boolean;
39
+ readonly outputGuard: boolean;
40
+ readonly profileBrokerOnly?: boolean;
41
+ readonly requiresExplicitPostconditions: boolean;
42
+ readonly requiresRequestInterception: boolean;
43
+ readonly sandboxRequired: boolean;
44
+ readonly secretBrokerOnly: boolean;
45
+ }
46
+ export interface ComputerUsePreset {
47
+ readonly browserModes: readonly ComputerUseBrowserMode[];
48
+ readonly defaultEnabled: boolean;
49
+ readonly engine: ComputerUseEngine;
50
+ readonly id: ComputerUsePresetId;
51
+ readonly key: ComputerUsePresetKey;
52
+ readonly label: string;
53
+ readonly notes: readonly string[];
54
+ readonly policy: ComputerUsePolicy;
55
+ readonly risk: ComputerUseRisk;
56
+ readonly sourceUrls: readonly string[];
57
+ readonly supportedLaunchOptions: readonly ComputerUseLaunchOption[];
58
+ readonly transport?: ComputerUseTransport;
59
+ }
60
+ export declare const COMPUTER_USE_PRESETS: Record<ComputerUsePresetKey, ComputerUsePreset>;
61
+ export declare function listComputerUsePresets(): ComputerUsePreset[];
62
+ export declare function getComputerUsePreset(id: string): ComputerUsePreset | undefined;
63
+ export declare function requiresCredentialedOriginAllowlist(cookieMode: ComputerUseCookieMode): boolean;
64
+ //# sourceMappingURL=mcp-computer-use-presets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-computer-use-presets.d.ts","sourceRoot":"","sources":["../../src/core/mcp-computer-use-presets.ts"],"names":[],"mappings":"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","sourcesContent":["export const OMK_COMPUTER_USE_DEFAULT_PRESET_ID = \"playwright-computer-use\";\n\nexport type ComputerUsePresetKey = \"browserUseAgent\" | \"playwrightComputerUse\";\nexport type ComputerUsePresetId = \"browser-use-agent\" | \"playwright-computer-use\";\nexport type ComputerUseRisk = \"R3\";\nexport type ComputerUseEngine = \"mcp\" | \"python-rust-sidecar\";\nexport type ComputerUseBrowser = \"chrome\" | \"firefox\" | \"msedge\" | \"webkit\";\nexport type ComputerUseCookieMode = \"existing-browser-session\" | \"isolated-storage-state\" | \"managed-profile\" | \"none\";\nexport type ComputerUseCredentialScope = \"none\" | \"session\" | \"workspace\";\nexport type ComputerUseLaunchOption = \"browser\" | \"cdp-endpoint\" | \"extension\" | \"storage-state\" | \"user-data-dir\";\n\nexport interface ComputerUseTransport {\n\treadonly args: readonly string[];\n\treadonly command: string;\n\treadonly packageName?: string;\n\treadonly type: \"stdio\";\n}\n\nexport interface ComputerUseBrowserMode {\n\treadonly argsExtra?: readonly string[];\n\treadonly browser?: ComputerUseBrowser;\n\treadonly browserLimit?: \"chrome-edge-only\";\n\treadonly cookieMode: ComputerUseCookieMode;\n\treadonly description: string;\n\treadonly id:\n\t\t| \"existing-chrome-session\"\n\t\t| \"isolated-storage-state\"\n\t\t| \"managed-chrome-profile\"\n\t\t| \"managed-firefox-profile\";\n\treadonly profileDir?: string;\n}\n\nexport interface ComputerUsePolicy {\n\treadonly allowedDomainsRequired?: boolean;\n\treadonly allowedOriginsSecurityBoundary?: boolean;\n\treadonly blockMetadataIp: boolean;\n\treadonly blockPrivateNetwork: boolean;\n\treadonly credentialScopes: readonly ComputerUseCredentialScope[];\n\treadonly denyNoSandbox: boolean;\n\treadonly descriptorHashPinned: boolean;\n\treadonly evidenceLedger: boolean;\n\treadonly maxActions: number;\n\treadonly maxOutputBytes?: number;\n\treadonly noManualProfileShellScript?: boolean;\n\treadonly noUnrestrictedFileAccess: boolean;\n\treadonly originAllowlistRequiredForCredentialedMode: boolean;\n\treadonly outputGuard: boolean;\n\treadonly profileBrokerOnly?: boolean;\n\treadonly requiresExplicitPostconditions: boolean;\n\treadonly requiresRequestInterception: boolean;\n\treadonly sandboxRequired: boolean;\n\treadonly secretBrokerOnly: boolean;\n}\n\nexport interface ComputerUsePreset {\n\treadonly browserModes: readonly ComputerUseBrowserMode[];\n\treadonly defaultEnabled: boolean;\n\treadonly engine: ComputerUseEngine;\n\treadonly id: ComputerUsePresetId;\n\treadonly key: ComputerUsePresetKey;\n\treadonly label: string;\n\treadonly notes: readonly string[];\n\treadonly policy: ComputerUsePolicy;\n\treadonly risk: ComputerUseRisk;\n\treadonly sourceUrls: readonly string[];\n\treadonly supportedLaunchOptions: readonly ComputerUseLaunchOption[];\n\treadonly transport?: ComputerUseTransport;\n}\n\nconst PLAYWRIGHT_BROWSER_MODES: readonly ComputerUseBrowserMode[] = [\n\t{\n\t\tbrowser: \"chrome\",\n\t\tcookieMode: \"managed-profile\",\n\t\tdescription: \"OMK-managed Chrome profile with project-scoped persistent cookies and local storage.\",\n\t\tid: \"managed-chrome-profile\",\n\t\tprofileDir: \".omk/browser-profiles/chrome\",\n\t},\n\t{\n\t\tbrowser: \"firefox\",\n\t\tcookieMode: \"managed-profile\",\n\t\tdescription: \"OMK-managed Firefox profile; avoids attaching to the user's real Firefox profile.\",\n\t\tid: \"managed-firefox-profile\",\n\t\tprofileDir: \".omk/browser-profiles/firefox\",\n\t},\n\t{\n\t\targsExtra: [\"--isolated\", \"--storage-state\", \"<storageStatePath>\"],\n\t\tcookieMode: \"isolated-storage-state\",\n\t\tdescription: \"Loads cookies and local storage from an explicit Playwright storage-state file.\",\n\t\tid: \"isolated-storage-state\",\n\t},\n\t{\n\t\targsExtra: [\"--extension\"],\n\t\tbrowser: \"chrome\",\n\t\tbrowserLimit: \"chrome-edge-only\",\n\t\tcookieMode: \"existing-browser-session\",\n\t\tdescription: \"Connects to an existing Chrome or Edge session through the Playwright extension.\",\n\t\tid: \"existing-chrome-session\",\n\t},\n];\n\nconst PLAYWRIGHT_COMPUTER_USE_PRESET: ComputerUsePreset = {\n\tbrowserModes: PLAYWRIGHT_BROWSER_MODES,\n\tdefaultEnabled: true,\n\tengine: \"mcp\",\n\tid: \"playwright-computer-use\",\n\tkey: \"playwrightComputerUse\",\n\tlabel: \"Playwright Computer Use\",\n\tnotes: [\n\t\t\"Default computer-use preset built on Microsoft Playwright MCP accessibility-tree automation.\",\n\t\t\"Credentialed browser sessions must go through SecureComputerUseBroker preflight and request interception.\",\n\t\t\"Playwright allowed-origins are recorded as advisory only; OMK policy remains the security boundary.\",\n\t],\n\tpolicy: {\n\t\tallowedOriginsSecurityBoundary: false,\n\t\tblockMetadataIp: true,\n\t\tblockPrivateNetwork: true,\n\t\tcredentialScopes: [\"none\", \"session\", \"workspace\"],\n\t\tdenyNoSandbox: true,\n\t\tdescriptorHashPinned: true,\n\t\tevidenceLedger: true,\n\t\tmaxActions: 40,\n\t\tmaxOutputBytes: 25_000_000,\n\t\tnoUnrestrictedFileAccess: true,\n\t\toriginAllowlistRequiredForCredentialedMode: true,\n\t\toutputGuard: true,\n\t\trequiresExplicitPostconditions: true,\n\t\trequiresRequestInterception: true,\n\t\tsandboxRequired: true,\n\t\tsecretBrokerOnly: true,\n\t},\n\trisk: \"R3\",\n\tsourceUrls: [\"https://github.com/microsoft/playwright-mcp\"],\n\tsupportedLaunchOptions: [\"browser\", \"user-data-dir\", \"storage-state\", \"extension\", \"cdp-endpoint\"],\n\ttransport: {\n\t\targs: [\n\t\t\t\"-y\",\n\t\t\t\"@playwright/mcp@0.0.76\",\n\t\t\t\"--browser\",\n\t\t\t\"<browser>\",\n\t\t\t\"--user-data-dir\",\n\t\t\t\"<profileDir>\",\n\t\t\t\"--output-dir\",\n\t\t\t\"<outputDir>\",\n\t\t\t\"--output-mode\",\n\t\t\t\"file\",\n\t\t\t\"--block-service-workers\",\n\t\t\t\"--timeout-action\",\n\t\t\t\"5000\",\n\t\t\t\"--timeout-navigation\",\n\t\t\t\"60000\",\n\t\t],\n\t\tcommand: \"npx\",\n\t\tpackageName: \"@playwright/mcp\",\n\t\ttype: \"stdio\",\n\t},\n};\n\nconst BROWSER_USE_AGENT_PRESET: ComputerUsePreset = {\n\tbrowserModes: [],\n\tdefaultEnabled: false,\n\tengine: \"python-rust-sidecar\",\n\tid: \"browser-use-agent\",\n\tkey: \"browserUseAgent\",\n\tlabel: \"Browser Use Agent\",\n\tnotes: [\n\t\t\"Optional advanced runner for agentic browser tasks that need recovery loops or custom tools.\",\n\t\t\"Profiles and secrets must be injected through OMK brokers instead of manual shell profile scripts.\",\n\t],\n\tpolicy: {\n\t\tallowedDomainsRequired: true,\n\t\tblockMetadataIp: true,\n\t\tblockPrivateNetwork: true,\n\t\tcredentialScopes: [\"none\", \"session\", \"workspace\"],\n\t\tdenyNoSandbox: true,\n\t\tdescriptorHashPinned: true,\n\t\tevidenceLedger: true,\n\t\tmaxActions: 60,\n\t\tnoManualProfileShellScript: true,\n\t\tnoUnrestrictedFileAccess: true,\n\t\toriginAllowlistRequiredForCredentialedMode: true,\n\t\toutputGuard: true,\n\t\tprofileBrokerOnly: true,\n\t\trequiresExplicitPostconditions: true,\n\t\trequiresRequestInterception: true,\n\t\tsandboxRequired: true,\n\t\tsecretBrokerOnly: true,\n\t},\n\trisk: \"R3\",\n\tsourceUrls: [\"https://github.com/browser-use/browser-use\"],\n\tsupportedLaunchOptions: [],\n};\n\nexport const COMPUTER_USE_PRESETS: Record<ComputerUsePresetKey, ComputerUsePreset> = {\n\tplaywrightComputerUse: PLAYWRIGHT_COMPUTER_USE_PRESET,\n\tbrowserUseAgent: BROWSER_USE_AGENT_PRESET,\n} as const;\n\nexport function listComputerUsePresets(): ComputerUsePreset[] {\n\treturn Object.values(COMPUTER_USE_PRESETS).map((preset) => cloneComputerUsePreset(preset));\n}\n\nexport function getComputerUsePreset(id: string): ComputerUsePreset | undefined {\n\tconst preset = Object.values(COMPUTER_USE_PRESETS).find((candidate) => candidate.id === id);\n\treturn preset ? cloneComputerUsePreset(preset) : undefined;\n}\n\nexport function requiresCredentialedOriginAllowlist(cookieMode: ComputerUseCookieMode): boolean {\n\treturn cookieMode !== \"none\";\n}\n\nfunction cloneComputerUsePreset(preset: ComputerUsePreset): ComputerUsePreset {\n\treturn {\n\t\t...preset,\n\t\tbrowserModes: preset.browserModes.map((mode) => ({\n\t\t\t...mode,\n\t\t\targsExtra: mode.argsExtra ? [...mode.argsExtra] : undefined,\n\t\t})),\n\t\tnotes: [...preset.notes],\n\t\tpolicy: {\n\t\t\t...preset.policy,\n\t\t\tcredentialScopes: [...preset.policy.credentialScopes],\n\t\t},\n\t\tsourceUrls: [...preset.sourceUrls],\n\t\tsupportedLaunchOptions: [...preset.supportedLaunchOptions],\n\t\ttransport: preset.transport\n\t\t\t? {\n\t\t\t\t\t...preset.transport,\n\t\t\t\t\targs: [...preset.transport.args],\n\t\t\t\t}\n\t\t\t: undefined,\n\t};\n}\n"]}