mixdog 0.7.17 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (836) hide show
  1. package/README.md +37 -331
  2. package/package.json +67 -99
  3. package/scripts/boot-smoke.mjs +94 -0
  4. package/scripts/build-tui.mjs +52 -0
  5. package/scripts/compact-smoke.mjs +199 -0
  6. package/scripts/lead-workflow-smoke.mjs +598 -0
  7. package/scripts/live-worker-smoke.mjs +239 -0
  8. package/scripts/output-style-smoke.mjs +101 -0
  9. package/scripts/smoke-loop-report.mjs +221 -0
  10. package/scripts/smoke-loop.mjs +201 -0
  11. package/scripts/smoke.mjs +113 -0
  12. package/scripts/tool-failures.mjs +143 -0
  13. package/scripts/tool-smoke.mjs +456 -0
  14. package/src/agents/debugger/AGENT.md +3 -0
  15. package/src/agents/debugger/agent.json +6 -0
  16. package/src/agents/explore/AGENT.md +4 -0
  17. package/src/agents/explore/agent.json +6 -0
  18. package/src/agents/heavy-worker/AGENT.md +3 -0
  19. package/src/agents/heavy-worker/agent.json +6 -0
  20. package/src/agents/maintainer/AGENT.md +3 -0
  21. package/src/agents/maintainer/agent.json +6 -0
  22. package/src/agents/reviewer/AGENT.md +3 -0
  23. package/src/agents/reviewer/agent.json +6 -0
  24. package/src/agents/scheduler-task.md +3 -0
  25. package/src/agents/web-researcher/AGENT.md +3 -0
  26. package/src/agents/web-researcher/agent.json +6 -0
  27. package/src/agents/webhook-handler.md +3 -0
  28. package/src/agents/worker/AGENT.md +3 -0
  29. package/src/agents/worker/agent.json +6 -0
  30. package/src/app.mjs +90 -0
  31. package/src/cli.mjs +11 -0
  32. package/src/defaults/hidden-roles.json +72 -0
  33. package/src/defaults/mixdog-config.template.json +15 -0
  34. package/src/hooks/lib/permission-evaluator.cjs +488 -0
  35. package/src/hooks/lib/settings-loader.cjs +112 -0
  36. package/src/lib/keychain-cjs.cjs +332 -0
  37. package/src/lib/plugin-paths.cjs +28 -0
  38. package/src/lib/rules-builder.cjs +315 -0
  39. package/src/mixdog-session-runtime.mjs +3704 -0
  40. package/src/output-styles/default.md +38 -0
  41. package/src/output-styles/extreme-simple.md +17 -0
  42. package/src/output-styles/simple.md +17 -0
  43. package/src/repl.mjs +322 -0
  44. package/src/rules/bridge/00-common.md +5 -0
  45. package/src/rules/bridge/20-skip-protocol.md +11 -0
  46. package/src/rules/bridge/30-explorer.md +4 -0
  47. package/src/rules/bridge/40-cycle1-agent.md +28 -0
  48. package/src/rules/bridge/41-cycle2-agent.md +59 -0
  49. package/src/rules/lead/00-tool-lead.md +5 -0
  50. package/src/rules/lead/01-general.md +5 -0
  51. package/src/rules/lead/02-channels.md +3 -0
  52. package/src/rules/lead/04-workflow.md +12 -0
  53. package/src/rules/shared/00-language.md +3 -0
  54. package/src/rules/shared/01-tool.md +3 -0
  55. package/src/runtime/agent/orchestrator/bridge-trace.mjs +814 -0
  56. package/src/runtime/agent/orchestrator/cache-mtime.mjs +60 -0
  57. package/src/runtime/agent/orchestrator/config.mjs +446 -0
  58. package/src/runtime/agent/orchestrator/context/collect.mjs +796 -0
  59. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +417 -0
  60. package/src/runtime/agent/orchestrator/internal-roles.mjs +188 -0
  61. package/src/runtime/agent/orchestrator/internal-tools.mjs +88 -0
  62. package/src/runtime/agent/orchestrator/mcp/client.mjs +345 -0
  63. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2104 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +784 -0
  65. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +341 -0
  66. package/src/runtime/agent/orchestrator/providers/gemini.mjs +1679 -0
  67. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +959 -0
  68. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +213 -0
  69. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +38 -0
  70. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +471 -0
  71. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +615 -0
  72. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +808 -0
  73. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +1719 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2587 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +1953 -0
  76. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +136 -0
  77. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +317 -0
  78. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +109 -0
  79. package/src/runtime/agent/orchestrator/providers/registry.mjs +247 -0
  80. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +332 -0
  81. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +11 -0
  82. package/src/runtime/agent/orchestrator/providers/trace-utils.mjs +50 -0
  83. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
  84. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +318 -0
  85. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +367 -0
  86. package/src/runtime/agent/orchestrator/session/compact.mjs +882 -0
  87. package/src/runtime/agent/orchestrator/session/context-utils.mjs +233 -0
  88. package/src/runtime/agent/orchestrator/session/loop.mjs +2320 -0
  89. package/src/runtime/agent/orchestrator/session/manager.mjs +2960 -0
  90. package/src/runtime/agent/orchestrator/session/result-classification.mjs +65 -0
  91. package/src/runtime/agent/orchestrator/session/store.mjs +663 -0
  92. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +166 -0
  93. package/src/runtime/agent/orchestrator/smart-bridge/bridge-llm.mjs +339 -0
  94. package/src/runtime/agent/orchestrator/smart-bridge/cache-strategy.mjs +419 -0
  95. package/src/runtime/agent/orchestrator/stall-policy.mjs +227 -0
  96. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +235 -0
  97. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +723 -0
  98. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +389 -0
  99. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +637 -0
  100. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +165 -0
  101. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +104 -0
  102. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +194 -0
  103. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +596 -0
  104. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +110 -0
  105. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +153 -0
  106. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
  107. package/src/runtime/agent/orchestrator/tools/builtin/read-open.mjs +189 -0
  108. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +731 -0
  109. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +168 -0
  110. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
  111. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +465 -0
  112. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +160 -0
  113. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +982 -0
  114. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +1087 -0
  115. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +231 -0
  116. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +223 -0
  117. package/src/runtime/agent/orchestrator/tools/builtin.mjs +478 -0
  118. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
  119. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4102 -0
  120. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +323 -0
  121. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +154 -0
  122. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +26 -0
  123. package/src/runtime/agent/orchestrator/tools/patch-binary-fetcher.mjs +143 -0
  124. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +18 -0
  125. package/src/runtime/agent/orchestrator/tools/patch.mjs +2772 -0
  126. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +114 -0
  127. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +880 -0
  128. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +312 -0
  129. package/src/runtime/channels/backends/discord.mjs +781 -0
  130. package/src/runtime/channels/data/voice-runtime-manifest.json +138 -0
  131. package/src/runtime/channels/index.mjs +3309 -0
  132. package/src/runtime/channels/lib/config.mjs +285 -0
  133. package/src/runtime/channels/lib/drop-trace.mjs +71 -0
  134. package/src/runtime/channels/lib/event-pipeline.mjs +81 -0
  135. package/src/runtime/channels/lib/holidays.mjs +138 -0
  136. package/src/runtime/channels/lib/hook-pipe-server.mjs +671 -0
  137. package/src/runtime/channels/lib/output-forwarder.mjs +765 -0
  138. package/src/runtime/channels/lib/runtime-paths.mjs +497 -0
  139. package/src/runtime/channels/lib/scheduler.mjs +710 -0
  140. package/src/runtime/channels/lib/session-discovery.mjs +102 -0
  141. package/src/runtime/channels/lib/state-file.mjs +68 -0
  142. package/src/runtime/channels/lib/status-snapshot.mjs +224 -0
  143. package/src/runtime/channels/lib/tool-format.mjs +124 -0
  144. package/src/runtime/channels/lib/transcript-discovery.mjs +195 -0
  145. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +734 -0
  146. package/src/runtime/channels/lib/webhook.mjs +1288 -0
  147. package/src/runtime/channels/tool-defs.mjs +177 -0
  148. package/src/runtime/lib/keychain-cjs.cjs +289 -0
  149. package/src/runtime/memory/data/runtime-manifest.json +40 -0
  150. package/src/runtime/memory/index.mjs +3600 -0
  151. package/src/runtime/memory/lib/core-memory-store.mjs +336 -0
  152. package/src/runtime/memory/lib/embedding-provider.mjs +275 -0
  153. package/src/runtime/memory/lib/embedding-worker.mjs +331 -0
  154. package/src/runtime/memory/lib/memory-cycle-requests.mjs +276 -0
  155. package/src/runtime/memory/lib/memory-cycle1.mjs +783 -0
  156. package/src/runtime/memory/lib/memory-cycle2.mjs +1389 -0
  157. package/src/runtime/memory/lib/memory-cycle3.mjs +646 -0
  158. package/src/runtime/memory/lib/memory-embed.mjs +300 -0
  159. package/src/runtime/memory/lib/memory-ops-policy.mjs +149 -0
  160. package/src/runtime/memory/lib/memory-recall-store.mjs +644 -0
  161. package/src/runtime/memory/lib/memory.mjs +418 -0
  162. package/src/runtime/memory/lib/pg/adapter.mjs +314 -0
  163. package/src/runtime/memory/lib/pg/process.mjs +366 -0
  164. package/src/runtime/memory/lib/pg/supervisor.mjs +495 -0
  165. package/src/runtime/memory/lib/runtime-fetcher.mjs +464 -0
  166. package/src/runtime/memory/lib/trace-store.mjs +734 -0
  167. package/src/runtime/memory/tool-defs.mjs +79 -0
  168. package/src/runtime/search/index.mjs +925 -0
  169. package/src/runtime/search/lib/config.mjs +61 -0
  170. package/src/runtime/search/lib/web-tools.mjs +1278 -0
  171. package/src/runtime/search/tool-defs.mjs +64 -0
  172. package/src/runtime/shared/atomic-file.mjs +435 -0
  173. package/src/runtime/shared/background-tasks.mjs +376 -0
  174. package/src/runtime/shared/child-guardian.mjs +98 -0
  175. package/src/runtime/shared/config.mjs +393 -0
  176. package/src/runtime/shared/err-text.mjs +121 -0
  177. package/src/runtime/shared/launcher-control.mjs +259 -0
  178. package/src/runtime/shared/llm/cost.mjs +66 -0
  179. package/src/runtime/shared/llm/http-agent.mjs +129 -0
  180. package/src/runtime/shared/open-url.mjs +37 -0
  181. package/src/runtime/shared/plugin-paths.mjs +25 -0
  182. package/src/runtime/shared/process-shutdown.mjs +147 -0
  183. package/src/runtime/shared/schedules-store.mjs +70 -0
  184. package/src/runtime/shared/tool-execution-contract.mjs +104 -0
  185. package/src/runtime/shared/tool-surface.mjs +950 -0
  186. package/src/runtime/shared/user-cwd.mjs +221 -0
  187. package/src/runtime/shared/user-data-guard.mjs +232 -0
  188. package/src/runtime/shared/workspace-router.mjs +259 -0
  189. package/src/standalone/bridge-tool.mjs +1414 -0
  190. package/src/standalone/channel-admin.mjs +366 -0
  191. package/src/standalone/channel-worker-preload.cjs +3 -0
  192. package/src/standalone/channel-worker.mjs +353 -0
  193. package/src/standalone/explore-tool.mjs +233 -0
  194. package/src/standalone/hook-bus.mjs +246 -0
  195. package/src/standalone/plugin-admin.mjs +247 -0
  196. package/src/standalone/provider-admin.mjs +338 -0
  197. package/src/standalone/seeds.mjs +94 -0
  198. package/src/standalone/usage-dashboard.mjs +510 -0
  199. package/src/tui/App.jsx +5438 -0
  200. package/src/tui/components/AnsiText.jsx +199 -0
  201. package/src/tui/components/ContextPanel.jsx +217 -0
  202. package/src/tui/components/Markdown.jsx +205 -0
  203. package/src/tui/components/MarkdownTable.jsx +204 -0
  204. package/src/tui/components/Message.jsx +103 -0
  205. package/src/tui/components/Picker.jsx +317 -0
  206. package/src/tui/components/PromptInput.jsx +584 -0
  207. package/src/tui/components/QueuedCommands.jsx +47 -0
  208. package/src/tui/components/SlashCommandPalette.jsx +114 -0
  209. package/src/tui/components/Spinner.jsx +317 -0
  210. package/src/tui/components/StatusLine.jsx +87 -0
  211. package/src/tui/components/TextEntryPanel.jsx +323 -0
  212. package/src/tui/components/ToolExecution.jsx +772 -0
  213. package/src/tui/components/TurnDone.jsx +78 -0
  214. package/src/tui/components/UsagePanel.jsx +331 -0
  215. package/src/tui/dist/index.mjs +12359 -0
  216. package/src/tui/engine.mjs +2410 -0
  217. package/src/tui/figures.mjs +50 -0
  218. package/src/tui/hooks/useEngine.mjs +16 -0
  219. package/src/tui/index.jsx +254 -0
  220. package/src/tui/input-editing.mjs +242 -0
  221. package/src/tui/markdown/format-token.mjs +194 -0
  222. package/src/tui/paste-attachments.mjs +198 -0
  223. package/src/tui/runtime/shared/process-shutdown.mjs +1 -0
  224. package/src/tui/spinner-verbs.mjs +45 -0
  225. package/src/tui/theme.mjs +67 -0
  226. package/src/tui/time-format.mjs +53 -0
  227. package/src/ui/ansi.mjs +115 -0
  228. package/src/ui/markdown.mjs +195 -0
  229. package/src/ui/statusline.mjs +730 -0
  230. package/src/ui/tool-card.mjs +101 -0
  231. package/src/vendor/statusline/bin/statusline-lib.mjs +805 -0
  232. package/src/vendor/statusline/bin/statusline-route.mjs +596 -0
  233. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +285 -0
  234. package/src/vendor/statusline/src/gateway/claude-current.mjs +320 -0
  235. package/src/vendor/statusline/src/gateway/route-meta.mjs +753 -0
  236. package/src/vendor/statusline/src/gateway/session-routes.mjs +244 -0
  237. package/src/workflows/default/WORKFLOW.md +7 -0
  238. package/src/workflows/default/workflow.json +14 -0
  239. package/vendor/ink/build/ansi-tokenizer.d.ts +38 -0
  240. package/vendor/ink/build/ansi-tokenizer.js +316 -0
  241. package/vendor/ink/build/ansi-tokenizer.js.map +1 -0
  242. package/vendor/ink/build/colorize.d.ts +3 -0
  243. package/vendor/ink/build/colorize.js +48 -0
  244. package/vendor/ink/build/colorize.js.map +1 -0
  245. package/vendor/ink/build/components/AccessibilityContext.d.ts +3 -0
  246. package/vendor/ink/build/components/AccessibilityContext.js +5 -0
  247. package/vendor/ink/build/components/AccessibilityContext.js.map +1 -0
  248. package/vendor/ink/build/components/AnimationContext.d.ts +9 -0
  249. package/vendor/ink/build/components/AnimationContext.js +13 -0
  250. package/vendor/ink/build/components/AnimationContext.js.map +1 -0
  251. package/vendor/ink/build/components/App.d.ts +24 -0
  252. package/vendor/ink/build/components/App.js +554 -0
  253. package/vendor/ink/build/components/App.js.map +1 -0
  254. package/vendor/ink/build/components/AppContext.d.ts +80 -0
  255. package/vendor/ink/build/components/AppContext.js +25 -0
  256. package/vendor/ink/build/components/AppContext.js.map +1 -0
  257. package/vendor/ink/build/components/BackgroundContext.d.ts +4 -0
  258. package/vendor/ink/build/components/BackgroundContext.js +3 -0
  259. package/vendor/ink/build/components/BackgroundContext.js.map +1 -0
  260. package/vendor/ink/build/components/Box.d.ts +130 -0
  261. package/vendor/ink/build/components/Box.js +34 -0
  262. package/vendor/ink/build/components/Box.js.map +1 -0
  263. package/vendor/ink/build/components/CursorContext.d.ts +11 -0
  264. package/vendor/ink/build/components/CursorContext.js +8 -0
  265. package/vendor/ink/build/components/CursorContext.js.map +1 -0
  266. package/vendor/ink/build/components/ErrorBoundary.d.ts +18 -0
  267. package/vendor/ink/build/components/ErrorBoundary.js +23 -0
  268. package/vendor/ink/build/components/ErrorBoundary.js.map +1 -0
  269. package/vendor/ink/build/components/ErrorOverview.d.ts +6 -0
  270. package/vendor/ink/build/components/ErrorOverview.js +90 -0
  271. package/vendor/ink/build/components/ErrorOverview.js.map +1 -0
  272. package/vendor/ink/build/components/FocusContext.d.ts +16 -0
  273. package/vendor/ink/build/components/FocusContext.js +17 -0
  274. package/vendor/ink/build/components/FocusContext.js.map +1 -0
  275. package/vendor/ink/build/components/Newline.d.ts +13 -0
  276. package/vendor/ink/build/components/Newline.js +8 -0
  277. package/vendor/ink/build/components/Newline.js.map +1 -0
  278. package/vendor/ink/build/components/Spacer.d.ts +7 -0
  279. package/vendor/ink/build/components/Spacer.js +11 -0
  280. package/vendor/ink/build/components/Spacer.js.map +1 -0
  281. package/vendor/ink/build/components/Static.d.ts +24 -0
  282. package/vendor/ink/build/components/Static.js +28 -0
  283. package/vendor/ink/build/components/Static.js.map +1 -0
  284. package/vendor/ink/build/components/StderrContext.d.ts +15 -0
  285. package/vendor/ink/build/components/StderrContext.js +13 -0
  286. package/vendor/ink/build/components/StderrContext.js.map +1 -0
  287. package/vendor/ink/build/components/StdinContext.d.ts +28 -0
  288. package/vendor/ink/build/components/StdinContext.js +20 -0
  289. package/vendor/ink/build/components/StdinContext.js.map +1 -0
  290. package/vendor/ink/build/components/StdoutContext.d.ts +15 -0
  291. package/vendor/ink/build/components/StdoutContext.js +13 -0
  292. package/vendor/ink/build/components/StdoutContext.js.map +1 -0
  293. package/vendor/ink/build/components/Text.d.ts +55 -0
  294. package/vendor/ink/build/components/Text.js +50 -0
  295. package/vendor/ink/build/components/Text.js.map +1 -0
  296. package/vendor/ink/build/components/Transform.d.ts +16 -0
  297. package/vendor/ink/build/components/Transform.js +15 -0
  298. package/vendor/ink/build/components/Transform.js.map +1 -0
  299. package/vendor/ink/build/cursor-helpers.d.ts +39 -0
  300. package/vendor/ink/build/cursor-helpers.js +62 -0
  301. package/vendor/ink/build/cursor-helpers.js.map +1 -0
  302. package/vendor/ink/build/devtools-window-polyfill.d.ts +1 -0
  303. package/vendor/ink/build/devtools-window-polyfill.js +68 -0
  304. package/vendor/ink/build/devtools-window-polyfill.js.map +1 -0
  305. package/vendor/ink/build/devtools.d.ts +1 -0
  306. package/vendor/ink/build/devtools.js +36 -0
  307. package/vendor/ink/build/devtools.js.map +1 -0
  308. package/vendor/ink/build/dom.d.ts +62 -0
  309. package/vendor/ink/build/dom.js +143 -0
  310. package/vendor/ink/build/dom.js.map +1 -0
  311. package/vendor/ink/build/get-max-width.d.ts +3 -0
  312. package/vendor/ink/build/get-max-width.js +10 -0
  313. package/vendor/ink/build/get-max-width.js.map +1 -0
  314. package/vendor/ink/build/hooks/use-animation.d.ts +49 -0
  315. package/vendor/ink/build/hooks/use-animation.js +87 -0
  316. package/vendor/ink/build/hooks/use-animation.js.map +1 -0
  317. package/vendor/ink/build/hooks/use-app.d.ts +5 -0
  318. package/vendor/ink/build/hooks/use-app.js +8 -0
  319. package/vendor/ink/build/hooks/use-app.js.map +1 -0
  320. package/vendor/ink/build/hooks/use-box-metrics.d.ts +59 -0
  321. package/vendor/ink/build/hooks/use-box-metrics.js +81 -0
  322. package/vendor/ink/build/hooks/use-box-metrics.js.map +1 -0
  323. package/vendor/ink/build/hooks/use-cursor.d.ts +12 -0
  324. package/vendor/ink/build/hooks/use-cursor.js +29 -0
  325. package/vendor/ink/build/hooks/use-cursor.js.map +1 -0
  326. package/vendor/ink/build/hooks/use-focus-manager.d.ts +43 -0
  327. package/vendor/ink/build/hooks/use-focus-manager.js +18 -0
  328. package/vendor/ink/build/hooks/use-focus-manager.js.map +1 -0
  329. package/vendor/ink/build/hooks/use-focus.d.ts +30 -0
  330. package/vendor/ink/build/hooks/use-focus.js +43 -0
  331. package/vendor/ink/build/hooks/use-focus.js.map +1 -0
  332. package/vendor/ink/build/hooks/use-input.d.ts +132 -0
  333. package/vendor/ink/build/hooks/use-input.js +126 -0
  334. package/vendor/ink/build/hooks/use-input.js.map +1 -0
  335. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.d.ts +6 -0
  336. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js +12 -0
  337. package/vendor/ink/build/hooks/use-is-screen-reader-enabled.js.map +1 -0
  338. package/vendor/ink/build/hooks/use-paste.d.ts +35 -0
  339. package/vendor/ink/build/hooks/use-paste.js +62 -0
  340. package/vendor/ink/build/hooks/use-paste.js.map +1 -0
  341. package/vendor/ink/build/hooks/use-stderr.d.ts +5 -0
  342. package/vendor/ink/build/hooks/use-stderr.js +8 -0
  343. package/vendor/ink/build/hooks/use-stderr.js.map +1 -0
  344. package/vendor/ink/build/hooks/use-stdin.d.ts +7 -0
  345. package/vendor/ink/build/hooks/use-stdin.js +9 -0
  346. package/vendor/ink/build/hooks/use-stdin.js.map +1 -0
  347. package/vendor/ink/build/hooks/use-stdout.d.ts +5 -0
  348. package/vendor/ink/build/hooks/use-stdout.js +8 -0
  349. package/vendor/ink/build/hooks/use-stdout.js.map +1 -0
  350. package/vendor/ink/build/hooks/use-window-size.d.ts +18 -0
  351. package/vendor/ink/build/hooks/use-window-size.js +22 -0
  352. package/vendor/ink/build/hooks/use-window-size.js.map +1 -0
  353. package/vendor/ink/build/index.d.ts +42 -0
  354. package/vendor/ink/build/index.js +24 -0
  355. package/vendor/ink/build/index.js.map +1 -0
  356. package/vendor/ink/build/ink.d.ts +146 -0
  357. package/vendor/ink/build/ink.js +1022 -0
  358. package/vendor/ink/build/ink.js.map +1 -0
  359. package/vendor/ink/build/input-parser.d.ts +10 -0
  360. package/vendor/ink/build/input-parser.js +194 -0
  361. package/vendor/ink/build/input-parser.js.map +1 -0
  362. package/vendor/ink/build/instances.d.ts +3 -0
  363. package/vendor/ink/build/instances.js +8 -0
  364. package/vendor/ink/build/instances.js.map +1 -0
  365. package/vendor/ink/build/kitty-keyboard.d.ts +23 -0
  366. package/vendor/ink/build/kitty-keyboard.js +32 -0
  367. package/vendor/ink/build/kitty-keyboard.js.map +1 -0
  368. package/vendor/ink/build/log-update.d.ts +20 -0
  369. package/vendor/ink/build/log-update.js +261 -0
  370. package/vendor/ink/build/log-update.js.map +1 -0
  371. package/vendor/ink/build/measure-element.d.ts +20 -0
  372. package/vendor/ink/build/measure-element.js +13 -0
  373. package/vendor/ink/build/measure-element.js.map +1 -0
  374. package/vendor/ink/build/measure-text.d.ts +6 -0
  375. package/vendor/ink/build/measure-text.js +21 -0
  376. package/vendor/ink/build/measure-text.js.map +1 -0
  377. package/vendor/ink/build/output.d.ts +35 -0
  378. package/vendor/ink/build/output.js +328 -0
  379. package/vendor/ink/build/output.js.map +1 -0
  380. package/vendor/ink/build/parse-keypress.d.ts +20 -0
  381. package/vendor/ink/build/parse-keypress.js +495 -0
  382. package/vendor/ink/build/parse-keypress.js.map +1 -0
  383. package/vendor/ink/build/reconciler.d.ts +4 -0
  384. package/vendor/ink/build/reconciler.js +306 -0
  385. package/vendor/ink/build/reconciler.js.map +1 -0
  386. package/vendor/ink/build/render-background.d.ts +4 -0
  387. package/vendor/ink/build/render-background.js +25 -0
  388. package/vendor/ink/build/render-background.js.map +1 -0
  389. package/vendor/ink/build/render-border.d.ts +4 -0
  390. package/vendor/ink/build/render-border.js +84 -0
  391. package/vendor/ink/build/render-border.js.map +1 -0
  392. package/vendor/ink/build/render-node-to-output.d.ts +14 -0
  393. package/vendor/ink/build/render-node-to-output.js +162 -0
  394. package/vendor/ink/build/render-node-to-output.js.map +1 -0
  395. package/vendor/ink/build/render-to-string.d.ts +38 -0
  396. package/vendor/ink/build/render-to-string.js +116 -0
  397. package/vendor/ink/build/render-to-string.js.map +1 -0
  398. package/vendor/ink/build/render.d.ts +176 -0
  399. package/vendor/ink/build/render.js +71 -0
  400. package/vendor/ink/build/render.js.map +1 -0
  401. package/vendor/ink/build/renderer.d.ts +8 -0
  402. package/vendor/ink/build/renderer.js +64 -0
  403. package/vendor/ink/build/renderer.js.map +1 -0
  404. package/vendor/ink/build/sanitize-ansi.d.ts +2 -0
  405. package/vendor/ink/build/sanitize-ansi.js +27 -0
  406. package/vendor/ink/build/sanitize-ansi.js.map +1 -0
  407. package/vendor/ink/build/squash-text-nodes.d.ts +3 -0
  408. package/vendor/ink/build/squash-text-nodes.js +36 -0
  409. package/vendor/ink/build/squash-text-nodes.js.map +1 -0
  410. package/vendor/ink/build/styles.d.ts +302 -0
  411. package/vendor/ink/build/styles.js +303 -0
  412. package/vendor/ink/build/styles.js.map +1 -0
  413. package/vendor/ink/build/utils.d.ts +9 -0
  414. package/vendor/ink/build/utils.js +19 -0
  415. package/vendor/ink/build/utils.js.map +1 -0
  416. package/vendor/ink/build/wrap-text.d.ts +3 -0
  417. package/vendor/ink/build/wrap-text.js +38 -0
  418. package/vendor/ink/build/wrap-text.js.map +1 -0
  419. package/vendor/ink/build/write-synchronized.d.ts +4 -0
  420. package/vendor/ink/build/write-synchronized.js +9 -0
  421. package/vendor/ink/build/write-synchronized.js.map +1 -0
  422. package/vendor/ink/license +10 -0
  423. package/vendor/ink/package.json +137 -0
  424. package/.claude-plugin/marketplace.json +0 -34
  425. package/.claude-plugin/plugin.json +0 -20
  426. package/.gitattributes +0 -34
  427. package/.mcp.json +0 -14
  428. package/ARCHITECTURE.md +0 -77
  429. package/CHANGELOG.md +0 -30
  430. package/CONTRIBUTING.md +0 -45
  431. package/DATA-FLOW.md +0 -79
  432. package/LICENSE +0 -21
  433. package/SECURITY.md +0 -138
  434. package/UNINSTALL.md +0 -112
  435. package/agents/maintenance.md +0 -5
  436. package/agents/memory-classification.md +0 -30
  437. package/agents/scheduler-task.md +0 -18
  438. package/agents/webhook-handler.md +0 -27
  439. package/agents/worker.md +0 -24
  440. package/bin/bridge +0 -133
  441. package/bin/statusline-launcher.mjs +0 -82
  442. package/bin/statusline-lib.mjs +0 -558
  443. package/bin/statusline.mjs +0 -615
  444. package/bun.lock +0 -927
  445. package/commands/config.md +0 -16
  446. package/commands/doctor.md +0 -13
  447. package/commands/setup.md +0 -17
  448. package/defaults/hidden-roles.json +0 -68
  449. package/defaults/memory-chunk-prompt.md +0 -63
  450. package/defaults/mixdog-config.template.json +0 -27
  451. package/defaults/user-workflow.json +0 -8
  452. package/defaults/user-workflow.md +0 -17
  453. package/hooks/hooks.json +0 -73
  454. package/hooks/lib/active-instance.cjs +0 -77
  455. package/hooks/lib/permission-evaluator.cjs +0 -411
  456. package/hooks/lib/permission-route.cjs +0 -63
  457. package/hooks/lib/settings-loader.cjs +0 -117
  458. package/hooks/post-tool-use.cjs +0 -84
  459. package/hooks/pre-mcp-sandbox.cjs +0 -158
  460. package/hooks/pre-tool-subagent.cjs +0 -258
  461. package/hooks/session-start.cjs +0 -1479
  462. package/hooks/shim-launcher.cjs +0 -65
  463. package/hooks/turn-timer.cjs +0 -82
  464. package/lib/claude-md-writer.cjs +0 -386
  465. package/lib/keychain-cjs.cjs +0 -290
  466. package/lib/plugin-paths.cjs +0 -69
  467. package/lib/rules-builder.cjs +0 -241
  468. package/native/README.md +0 -117
  469. package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
  470. package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
  471. package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
  472. package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
  473. package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
  474. package/prompts/code-review.txt +0 -16
  475. package/prompts/security-audit.txt +0 -17
  476. package/rules/bridge/00-common.md +0 -39
  477. package/rules/bridge/20-skip-protocol.md +0 -18
  478. package/rules/bridge/30-explorer.md +0 -33
  479. package/rules/bridge/40-cycle1-agent.md +0 -52
  480. package/rules/bridge/41-cycle2-agent.md +0 -62
  481. package/rules/lead/00-tool-lead.md +0 -61
  482. package/rules/lead/01-general.md +0 -23
  483. package/rules/lead/02-channels.md +0 -49
  484. package/rules/lead/03-team.md +0 -27
  485. package/rules/lead/04-workflow.md +0 -20
  486. package/rules/shared/00-language.md +0 -14
  487. package/rules/shared/01-tool.md +0 -138
  488. package/scripts/bootstrap.mjs +0 -130
  489. package/scripts/bridge-unify-smoke.mjs +0 -308
  490. package/scripts/build-runtime-linux.sh +0 -348
  491. package/scripts/build-runtime-macos.sh +0 -217
  492. package/scripts/build-runtime-windows.ps1 +0 -242
  493. package/scripts/builtin-utils-smoke.mjs +0 -398
  494. package/scripts/bump.mjs +0 -80
  495. package/scripts/check-json.mjs +0 -45
  496. package/scripts/check-syntax-changed.mjs +0 -102
  497. package/scripts/check-syntax.mjs +0 -58
  498. package/scripts/code-graph-batch.test.mjs +0 -33
  499. package/scripts/config-preserve-smoke.mjs +0 -180
  500. package/scripts/doctor.mjs +0 -489
  501. package/scripts/edit-normalize-fuzz.mjs +0 -130
  502. package/scripts/edit-normalize-smoke.mjs +0 -401
  503. package/scripts/edit-operation-smoke.mjs +0 -369
  504. package/scripts/edit2-smoke.mjs +0 -63
  505. package/scripts/ensure-deps.mjs +0 -259
  506. package/scripts/fuzzy-e2e.mjs +0 -28
  507. package/scripts/fuzzy-smoke.mjs +0 -26
  508. package/scripts/generate-runtime-manifest.mjs +0 -166
  509. package/scripts/guard-smoke.mjs +0 -66
  510. package/scripts/hidden-role-schema-smoke.mjs +0 -162
  511. package/scripts/hook-routing-smoke.mjs +0 -29
  512. package/scripts/inject-input.ps1 +0 -204
  513. package/scripts/io-complex-smoke.mjs +0 -667
  514. package/scripts/io-explore-bench.mjs +0 -424
  515. package/scripts/io-guardrails-smoke.mjs +0 -205
  516. package/scripts/io-mini-bench-baseline.json +0 -11
  517. package/scripts/io-mini-bench.mjs +0 -216
  518. package/scripts/io-route-harness.mjs +0 -933
  519. package/scripts/io-telemetry-report.mjs +0 -691
  520. package/scripts/mutation-bench.mjs +0 -564
  521. package/scripts/mutation-io-smoke.mjs +0 -1097
  522. package/scripts/native-patch-bridge-smoke.mjs +0 -288
  523. package/scripts/native-patch-smoke.mjs +0 -304
  524. package/scripts/openai-oauth-catalog-smoke.mjs +0 -53
  525. package/scripts/patch-interior-context-smoke.mjs +0 -49
  526. package/scripts/patch-newline-utf8-smoke.mjs +0 -157
  527. package/scripts/perf-hook-smoke.mjs +0 -71
  528. package/scripts/permission-eval-smoke.mjs +0 -443
  529. package/scripts/prep-patch.mjs +0 -53
  530. package/scripts/prep-shim.mjs +0 -96
  531. package/scripts/provider-cache-smoke.mjs +0 -687
  532. package/scripts/report-runtime-health.mjs +0 -132
  533. package/scripts/resolve-bun.mjs +0 -60
  534. package/scripts/run-mcp.mjs +0 -1448
  535. package/scripts/salvage-v4a-shatter.test.mjs +0 -58
  536. package/scripts/scoped-cache-io-smoke.mjs +0 -103
  537. package/scripts/shell-policy-round3-smoke.mjs +0 -46
  538. package/scripts/smoke-runtime-negative.ps1 +0 -100
  539. package/scripts/smoke-runtime-negative.sh +0 -95
  540. package/scripts/stall-policy-smoke.mjs +0 -50
  541. package/scripts/start-memory-worker.mjs +0 -23
  542. package/scripts/statusline-launcher-smoke.mjs +0 -82
  543. package/scripts/stress-atomic-write.mjs +0 -1028
  544. package/scripts/test-fault-inject.mjs +0 -164
  545. package/scripts/test-large-file.mjs +0 -174
  546. package/scripts/tool-edge-smoke.mjs +0 -209
  547. package/scripts/uninstall.mjs +0 -201
  548. package/scripts/webhook-selfheal-smoke.mjs +0 -27
  549. package/scripts/write-overwrite-guard-smoke.mjs +0 -56
  550. package/server-main.mjs +0 -3109
  551. package/server.mjs +0 -468
  552. package/setup/config-merge.mjs +0 -246
  553. package/setup/install.mjs +0 -574
  554. package/setup/launch-core.mjs +0 -617
  555. package/setup/launch.mjs +0 -101
  556. package/setup/locate-claude.mjs +0 -56
  557. package/setup/mixdog-cli.mjs +0 -122
  558. package/setup/setup-server.mjs +0 -3305
  559. package/setup/setup.html +0 -3740
  560. package/setup/tui.mjs +0 -325
  561. package/skills/retro-skill-proposer/SKILL.md +0 -92
  562. package/skills/schedule-add/SKILL.md +0 -77
  563. package/skills/setup/SKILL.md +0 -356
  564. package/skills/webhook-add/SKILL.md +0 -81
  565. package/src/agent/bridge-stall-watchdog.mjs +0 -337
  566. package/src/agent/index.mjs +0 -2138
  567. package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +0 -1010
  568. package/src/agent/orchestrator/bridge-retry.mjs +0 -220
  569. package/src/agent/orchestrator/bridge-trace.mjs +0 -583
  570. package/src/agent/orchestrator/cache-mtime.mjs +0 -58
  571. package/src/agent/orchestrator/config.mjs +0 -405
  572. package/src/agent/orchestrator/context/collect.mjs +0 -651
  573. package/src/agent/orchestrator/dispatch-persist.mjs +0 -549
  574. package/src/agent/orchestrator/drain-registry.mjs +0 -50
  575. package/src/agent/orchestrator/explore-validator.mjs +0 -8
  576. package/src/agent/orchestrator/internal-roles.mjs +0 -118
  577. package/src/agent/orchestrator/internal-tools.mjs +0 -88
  578. package/src/agent/orchestrator/jobs.mjs +0 -116
  579. package/src/agent/orchestrator/mcp/client.mjs +0 -364
  580. package/src/agent/orchestrator/providers/anthropic-oauth.mjs +0 -1881
  581. package/src/agent/orchestrator/providers/anthropic.mjs +0 -594
  582. package/src/agent/orchestrator/providers/gemini.mjs +0 -1530
  583. package/src/agent/orchestrator/providers/grok-oauth.mjs +0 -779
  584. package/src/agent/orchestrator/providers/model-catalog.mjs +0 -374
  585. package/src/agent/orchestrator/providers/openai-compat-stream.mjs +0 -366
  586. package/src/agent/orchestrator/providers/openai-compat.mjs +0 -1511
  587. package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +0 -1891
  588. package/src/agent/orchestrator/providers/openai-oauth.mjs +0 -1456
  589. package/src/agent/orchestrator/providers/openai-ws.mjs +0 -127
  590. package/src/agent/orchestrator/providers/registry.mjs +0 -192
  591. package/src/agent/orchestrator/providers/retry-classifier.mjs +0 -325
  592. package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +0 -142
  593. package/src/agent/orchestrator/session/cache/read-cache.mjs +0 -319
  594. package/src/agent/orchestrator/session/cache/scoped-cache.mjs +0 -361
  595. package/src/agent/orchestrator/session/loop.mjs +0 -1478
  596. package/src/agent/orchestrator/session/manager.mjs +0 -1991
  597. package/src/agent/orchestrator/session/result-classification.mjs +0 -65
  598. package/src/agent/orchestrator/session/store.mjs +0 -632
  599. package/src/agent/orchestrator/session/stream-watchdog.mjs +0 -130
  600. package/src/agent/orchestrator/session/tool-result-offload.mjs +0 -166
  601. package/src/agent/orchestrator/session/trim.mjs +0 -491
  602. package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +0 -115
  603. package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +0 -331
  604. package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +0 -150
  605. package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +0 -228
  606. package/src/agent/orchestrator/smart-bridge/index.mjs +0 -215
  607. package/src/agent/orchestrator/smart-bridge/profiles.mjs +0 -37
  608. package/src/agent/orchestrator/smart-bridge/registry.mjs +0 -348
  609. package/src/agent/orchestrator/stall-policy.mjs +0 -201
  610. package/src/agent/orchestrator/tool-loop-guard.mjs +0 -75
  611. package/src/agent/orchestrator/tools/bash-session.mjs +0 -722
  612. package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +0 -455
  613. package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -480
  614. package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +0 -256
  615. package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +0 -58
  616. package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +0 -240
  617. package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +0 -113
  618. package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +0 -74
  619. package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +0 -242
  620. package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +0 -211
  621. package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +0 -1364
  622. package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +0 -126
  623. package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +0 -141
  624. package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +0 -194
  625. package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +0 -60
  626. package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +0 -168
  627. package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +0 -173
  628. package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +0 -48
  629. package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -99
  630. package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +0 -193
  631. package/src/agent/orchestrator/tools/builtin/list-tool.mjs +0 -597
  632. package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  633. package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +0 -300
  634. package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +0 -152
  635. package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +0 -118
  636. package/src/agent/orchestrator/tools/builtin/read-open.mjs +0 -190
  637. package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +0 -728
  638. package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +0 -173
  639. package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +0 -602
  640. package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +0 -196
  641. package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +0 -422
  642. package/src/agent/orchestrator/tools/builtin/search-builders.mjs +0 -158
  643. package/src/agent/orchestrator/tools/builtin/search-tool.mjs +0 -869
  644. package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +0 -962
  645. package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +0 -223
  646. package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +0 -206
  647. package/src/agent/orchestrator/tools/builtin/write-tool.mjs +0 -401
  648. package/src/agent/orchestrator/tools/builtin.mjs +0 -503
  649. package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +0 -24
  650. package/src/agent/orchestrator/tools/code-graph.mjs +0 -4095
  651. package/src/agent/orchestrator/tools/cwd-tool.mjs +0 -298
  652. package/src/agent/orchestrator/tools/destructive-warning.mjs +0 -323
  653. package/src/agent/orchestrator/tools/edit-normalize.mjs +0 -603
  654. package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +0 -154
  655. package/src/agent/orchestrator/tools/graph-manifest.json +0 -26
  656. package/src/agent/orchestrator/tools/host-input.mjs +0 -204
  657. package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +0 -143
  658. package/src/agent/orchestrator/tools/patch-manifest.json +0 -26
  659. package/src/agent/orchestrator/tools/patch-tool-defs.mjs +0 -20
  660. package/src/agent/orchestrator/tools/patch.mjs +0 -2754
  661. package/src/agent/orchestrator/tools/progress-message.mjs +0 -118
  662. package/src/agent/orchestrator/tools/shell-command.mjs +0 -865
  663. package/src/agent/orchestrator/tools/shell-policy-imports.mjs +0 -7
  664. package/src/agent/orchestrator/tools/shell-snapshot.mjs +0 -313
  665. package/src/agent/orchestrator/workflow-store.mjs +0 -93
  666. package/src/agent/tool-defs.mjs +0 -103
  667. package/src/channels/backends/discord.mjs +0 -784
  668. package/src/channels/data/voice-runtime-manifest.json +0 -138
  669. package/src/channels/index.mjs +0 -3268
  670. package/src/channels/lib/config.mjs +0 -292
  671. package/src/channels/lib/drop-trace.mjs +0 -71
  672. package/src/channels/lib/event-pipeline.mjs +0 -81
  673. package/src/channels/lib/holidays.mjs +0 -138
  674. package/src/channels/lib/hook-pipe-server.mjs +0 -822
  675. package/src/channels/lib/output-forwarder.mjs +0 -765
  676. package/src/channels/lib/runtime-paths.mjs +0 -517
  677. package/src/channels/lib/scheduler.mjs +0 -723
  678. package/src/channels/lib/session-discovery.mjs +0 -103
  679. package/src/channels/lib/state-file.mjs +0 -68
  680. package/src/channels/lib/status-snapshot.mjs +0 -219
  681. package/src/channels/lib/tool-format.mjs +0 -140
  682. package/src/channels/lib/transcript-discovery.mjs +0 -195
  683. package/src/channels/lib/voice-runtime-fetcher.mjs +0 -734
  684. package/src/channels/lib/webhook.mjs +0 -1318
  685. package/src/channels/tool-defs.mjs +0 -170
  686. package/src/daemon/host.mjs +0 -118
  687. package/src/daemon/mcp-transport.mjs +0 -47
  688. package/src/daemon/session.mjs +0 -100
  689. package/src/daemon/thin-client.mjs +0 -71
  690. package/src/daemon/transport.mjs +0 -163
  691. package/src/memory/data/runtime-manifest.json +0 -40
  692. package/src/memory/index.mjs +0 -3332
  693. package/src/memory/lib/core-memory-store.mjs +0 -330
  694. package/src/memory/lib/embedding-provider.mjs +0 -269
  695. package/src/memory/lib/embedding-worker.mjs +0 -323
  696. package/src/memory/lib/memory-cycle1.mjs +0 -645
  697. package/src/memory/lib/memory-cycle2.mjs +0 -1284
  698. package/src/memory/lib/memory-cycle3.mjs +0 -540
  699. package/src/memory/lib/memory-embed.mjs +0 -299
  700. package/src/memory/lib/memory-ops-policy.mjs +0 -190
  701. package/src/memory/lib/memory-recall-store.mjs +0 -638
  702. package/src/memory/lib/memory.mjs +0 -412
  703. package/src/memory/lib/pg/adapter.mjs +0 -308
  704. package/src/memory/lib/pg/process.mjs +0 -360
  705. package/src/memory/lib/pg/supervisor.mjs +0 -396
  706. package/src/memory/lib/runtime-fetcher.mjs +0 -458
  707. package/src/memory/lib/trace-store.mjs +0 -728
  708. package/src/memory/tool-defs.mjs +0 -79
  709. package/src/search/index.mjs +0 -1173
  710. package/src/search/lib/backends/anthropic-oauth.mjs +0 -98
  711. package/src/search/lib/backends/exa.mjs +0 -50
  712. package/src/search/lib/backends/firecrawl.mjs +0 -61
  713. package/src/search/lib/backends/gemini-api.mjs +0 -83
  714. package/src/search/lib/backends/grok-oauth.mjs +0 -86
  715. package/src/search/lib/backends/index.mjs +0 -150
  716. package/src/search/lib/backends/openai-api.mjs +0 -144
  717. package/src/search/lib/backends/openai-oauth.mjs +0 -102
  718. package/src/search/lib/backends/openai-web-search.mjs +0 -76
  719. package/src/search/lib/backends/tavily.mjs +0 -55
  720. package/src/search/lib/backends/xai-api.mjs +0 -113
  721. package/src/search/lib/config.mjs +0 -192
  722. package/src/search/lib/provider-usage.mjs +0 -67
  723. package/src/search/lib/providers.mjs +0 -47
  724. package/src/search/lib/search-intent.mjs +0 -109
  725. package/src/search/lib/setup-handler.mjs +0 -261
  726. package/src/search/lib/web-tools.mjs +0 -1219
  727. package/src/search/tool-defs.mjs +0 -83
  728. package/src/setup/defender-exclusion.mjs +0 -183
  729. package/src/shared/atomic-file.mjs +0 -436
  730. package/src/shared/config.mjs +0 -372
  731. package/src/shared/daemon-recycle.mjs +0 -108
  732. package/src/shared/disable-claude-builtins.mjs +0 -91
  733. package/src/shared/err-text.mjs +0 -12
  734. package/src/shared/llm/cost.mjs +0 -66
  735. package/src/shared/llm/http-agent.mjs +0 -123
  736. package/src/shared/open-url.mjs +0 -62
  737. package/src/shared/plugin-paths.mjs +0 -58
  738. package/src/shared/schedules-store.mjs +0 -70
  739. package/src/shared/seed.mjs +0 -136
  740. package/src/shared/user-cwd.mjs +0 -225
  741. package/src/shared/user-data-guard.mjs +0 -244
  742. package/src/status/aggregator.mjs +0 -584
  743. package/src/status/server.mjs +0 -413
  744. package/tools.json +0 -1653
  745. /package/{defaults → src/defaults}/cycle3-review-prompt.md +0 -0
  746. /package/{defaults → src/defaults}/memory-promote-prompt.md +0 -0
  747. /package/{hooks → src/hooks}/lib/permission-rules.cjs +0 -0
  748. /package/{lib → src/lib}/config-cjs.cjs +0 -0
  749. /package/{lib → src/lib}/hook-pipe-path.cjs +0 -0
  750. /package/{lib → src/lib}/mixdog-debug.cjs +0 -0
  751. /package/{lib → src/lib}/text-utils.cjs +0 -0
  752. /package/{rules → src/rules}/bridge/42-cycle3-agent.md +0 -0
  753. /package/src/{agent → runtime/agent}/orchestrator/activity-bus.mjs +0 -0
  754. /package/src/{agent → runtime/agent}/orchestrator/providers/anthropic-betas.mjs +0 -0
  755. /package/src/{agent → runtime/agent}/orchestrator/session/abort-lookup.mjs +0 -0
  756. /package/src/{agent → runtime/agent}/orchestrator/session/cache/post-edit-marks.mjs +0 -0
  757. /package/src/{agent → runtime/agent}/orchestrator/session/cache/scoped-cache-outcome.mjs +0 -0
  758. /package/src/{agent → runtime/agent}/orchestrator/session/cache/util.mjs +0 -0
  759. /package/src/{agent → runtime/agent}/orchestrator/session/read-dedup.mjs +0 -0
  760. /package/src/{agent → runtime/agent}/orchestrator/session/save-session-worker.mjs +0 -0
  761. /package/src/{agent → runtime/agent}/orchestrator/smart-bridge/session-builder.mjs +0 -0
  762. /package/src/{agent → runtime/agent}/orchestrator/tools/bash-policy-scan.mjs +0 -0
  763. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/advisory-lock.mjs +0 -0
  764. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/atomic-write.mjs +0 -0
  765. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/binary-file.mjs +0 -0
  766. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/cache-layers.mjs +0 -0
  767. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/cwd-utils.mjs +0 -0
  768. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/device-paths.mjs +0 -0
  769. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -0
  770. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/diff-utils.mjs +0 -0
  771. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/fs-reachability.mjs +0 -0
  772. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/grep-formatting.mjs +0 -0
  773. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/hash-utils.mjs +0 -0
  774. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/list-formatting.mjs +0 -0
  775. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/open-config-tool.mjs +0 -0
  776. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/path-locks.mjs +0 -0
  777. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/path-utils.mjs +0 -0
  778. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-args.mjs +0 -0
  779. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-batch.mjs +0 -0
  780. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-constants.mjs +0 -0
  781. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-image-resize.mjs +0 -0
  782. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-image.mjs +0 -0
  783. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-lines.mjs +0 -0
  784. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-mode-tool.mjs +0 -0
  785. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-range-index.mjs +0 -0
  786. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-ranges.mjs +0 -0
  787. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-special-files.mjs +0 -0
  788. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-tool.mjs +0 -0
  789. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/read-windows.mjs +0 -0
  790. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/shell-analysis.mjs +0 -0
  791. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/shell-output.mjs +0 -0
  792. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/snapshot-helpers.mjs +0 -0
  793. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/snapshot-validation.mjs +0 -0
  794. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/text-stats.mjs +0 -0
  795. /package/src/{agent → runtime/agent}/orchestrator/tools/builtin/windows-roots.mjs +0 -0
  796. /package/src/{agent → runtime/agent}/orchestrator/tools/code-graph-prewarm-worker.mjs +0 -0
  797. /package/src/{agent → runtime/agent}/orchestrator/tools/env-scrub.mjs +0 -0
  798. /package/src/{agent → runtime/agent}/orchestrator/tools/mutation-content-cache.mjs +0 -0
  799. /package/src/{agent → runtime/agent}/orchestrator/tools/mutation-planner.mjs +0 -0
  800. /package/src/{agent → runtime/agent}/orchestrator/tools/next-call-utils.mjs +0 -0
  801. /package/src/{agent → runtime/agent}/orchestrator/tools/result-compression.mjs +0 -0
  802. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-exec-policy.mjs +0 -0
  803. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-policy-danger-target.mjs +0 -0
  804. /package/src/{agent → runtime/agent}/orchestrator/tools/shell-policy.mjs +0 -0
  805. /package/src/{channels → runtime/channels}/lib/cli-worker-host.mjs +0 -0
  806. /package/src/{channels → runtime/channels}/lib/config-lock.mjs +0 -0
  807. /package/src/{channels → runtime/channels}/lib/event-queue.mjs +0 -0
  808. /package/src/{channels → runtime/channels}/lib/executor.mjs +0 -0
  809. /package/src/{channels → runtime/channels}/lib/format.mjs +0 -0
  810. /package/src/{channels → runtime/channels}/lib/interaction-workflows.mjs +0 -0
  811. /package/src/{channels → runtime/channels}/lib/memory-client.mjs +0 -0
  812. /package/src/{channels → runtime/channels}/lib/session-control.mjs +0 -0
  813. /package/src/{channels → runtime/channels}/lib/settings.mjs +0 -0
  814. /package/src/{channels → runtime/channels}/lib/whisper-server.mjs +0 -0
  815. /package/src/{memory → runtime/memory}/lib/agent-ipc.mjs +0 -0
  816. /package/src/{memory → runtime/memory}/lib/bridge-trace-queries.mjs +0 -0
  817. /package/src/{memory → runtime/memory}/lib/llm-worker-host.mjs +0 -0
  818. /package/src/{memory → runtime/memory}/lib/memory-cycle.mjs +0 -0
  819. /package/src/{memory → runtime/memory}/lib/memory-extraction.mjs +0 -0
  820. /package/src/{memory → runtime/memory}/lib/memory-maintenance-store.mjs +0 -0
  821. /package/src/{memory → runtime/memory}/lib/memory-recall-id-patch.mjs +0 -0
  822. /package/src/{memory → runtime/memory}/lib/memory-recall-read-query.mjs +0 -0
  823. /package/src/{memory → runtime/memory}/lib/memory-recall-scope-filter.mjs +0 -0
  824. /package/src/{memory → runtime/memory}/lib/memory-retrievers.mjs +0 -0
  825. /package/src/{memory → runtime/memory}/lib/memory-score.mjs +0 -0
  826. /package/src/{memory → runtime/memory}/lib/memory-text-utils.mjs +0 -0
  827. /package/src/{memory → runtime/memory}/lib/model-profile.mjs +0 -0
  828. /package/src/{memory → runtime/memory}/lib/project-id-resolver.mjs +0 -0
  829. /package/src/{search → runtime/search}/lib/cache.mjs +0 -0
  830. /package/src/{search → runtime/search}/lib/formatter.mjs +0 -0
  831. /package/src/{search → runtime/search}/lib/state.mjs +0 -0
  832. /package/src/{shared → runtime/shared}/abort-controller.mjs +0 -0
  833. /package/src/{shared → runtime/shared}/llm/index.mjs +0 -0
  834. /package/src/{shared → runtime/shared}/llm/pid-cleanup.mjs +0 -0
  835. /package/src/{shared → runtime/shared}/llm/usage-log.mjs +0 -0
  836. /package/src/{shared → runtime/shared}/wsl.mjs +0 -0
@@ -1,1479 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const os = require('os');
6
- const http = require('http');
7
- const net = require('net');
8
- const { spawn } = require('child_process');
9
- const { resolvePluginData } = require(path.join(__dirname, '..', 'lib', 'plugin-paths.cjs'));
10
- const { readSection } = require(path.join(__dirname, '..', 'lib', 'config-cjs.cjs'));
11
- const {
12
- isMixdogDebugEnabled,
13
- pruneStalePluginDataLogSiblings,
14
- appendSessionStartCriticalLog,
15
- sessionStartCriticalFallback,
16
- DEFAULT_STALE_LOG_SIBLING_MAX,
17
- } = require(path.join(__dirname, '..', 'lib', 'mixdog-debug.cjs'));
18
-
19
- // Verbose tracing → session-start.log (MIXDOG_DEBUG). Ship mode → critical
20
- // fail-open lines only in bounded session-start-critical.log.
21
- let _SESSION_START_LOG_PATH = null;
22
- let _SESSION_START_DATA_DIR = null;
23
- const MIXDOG_DEBUG_ENABLED = isMixdogDebugEnabled();
24
- let _sessionStartLogsPruned = false;
25
-
26
- function sessionStartDataDir() {
27
- if (_SESSION_START_DATA_DIR) return _SESSION_START_DATA_DIR;
28
- try {
29
- if (typeof DATA_DIR === 'string' && DATA_DIR) {
30
- _SESSION_START_DATA_DIR = DATA_DIR;
31
- return _SESSION_START_DATA_DIR;
32
- }
33
- } catch { /* safe before DATA_DIR const init */ }
34
- _SESSION_START_DATA_DIR = process.env.CLAUDE_PLUGIN_DATA
35
- || path.join(os.homedir(), '.claude', 'plugins', 'data', 'mixdog-trib-plugin');
36
- return _SESSION_START_DATA_DIR;
37
- }
38
-
39
- function sessionStartLogPath() {
40
- if (_SESSION_START_LOG_PATH) return _SESSION_START_LOG_PATH;
41
- _SESSION_START_LOG_PATH = path.join(sessionStartDataDir(), 'session-start.log');
42
- return _SESSION_START_LOG_PATH;
43
- }
44
- const SESSION_START_LOG_MAX_BYTES = 256 * 1024;
45
- const SESSION_START_LOG_KEEP_BYTES = 64 * 1024;
46
-
47
- function appendVerboseSessionStartLog(line) {
48
- try {
49
- const dir = sessionStartDataDir();
50
- const p = sessionStartLogPath();
51
- fs.mkdirSync(dir, { recursive: true });
52
- if (!_sessionStartLogsPruned) {
53
- _sessionStartLogsPruned = true;
54
- pruneStalePluginDataLogSiblings(dir, DEFAULT_STALE_LOG_SIBLING_MAX);
55
- }
56
- try {
57
- const st = fs.statSync(p);
58
- if (st.size > SESSION_START_LOG_MAX_BYTES) {
59
- const buf = fs.readFileSync(p);
60
- fs.writeFileSync(p, buf.subarray(buf.length - SESSION_START_LOG_KEEP_BYTES));
61
- }
62
- } catch {}
63
- fs.appendFileSync(p, line);
64
- } catch {}
65
- }
66
-
67
- function teeStderr(line, opts = {}) {
68
- const critical = opts.critical === true
69
- || (opts.critical !== false && sessionStartCriticalFallback(line));
70
- if (!MIXDOG_DEBUG_ENABLED) {
71
- if (!critical) return;
72
- appendSessionStartCriticalLog(sessionStartDataDir(), line);
73
- try { process.stderr.write(line); } catch {}
74
- return;
75
- }
76
- appendVerboseSessionStartLog(line);
77
- try { process.stderr.write(line); } catch {}
78
- }
79
-
80
- // ---------------------------------------------------------------------------
81
- // argv parsing — supports `--part rules`, `--part=rules`.
82
- // Invalid/unknown part falls back to `rules`.
83
- // ---------------------------------------------------------------------------
84
- function parseArgs(argv) {
85
- const out = { part: 'rules' };
86
- for (let i = 2; i < argv.length; i++) {
87
- const a = argv[i];
88
- if (!a) continue;
89
- let key = null;
90
- let val = null;
91
- if (a.startsWith('--') && a.includes('=')) {
92
- const eq = a.indexOf('=');
93
- key = a.slice(2, eq);
94
- val = a.slice(eq + 1);
95
- } else if (a.startsWith('--')) {
96
- key = a.slice(2);
97
- const next = argv[i + 1];
98
- if (typeof next === 'string' && !next.startsWith('--')) {
99
- val = next;
100
- i++;
101
- }
102
- }
103
- if (key === 'part' && typeof val === 'string') out.part = val;
104
- }
105
- if (!['rules', 'core', 'recap'].includes(out.part)) out.part = 'rules';
106
- return out;
107
- }
108
-
109
- const ARGS = parseArgs(process.argv);
110
- let PART = ARGS.part;
111
-
112
- const DATA_DIR = resolvePluginData();
113
- const PLUGIN_ROOT = process.env.CLAUDE_PLUGIN_ROOT;
114
-
115
- let _event = {};
116
- const IS_DAEMON_REQUIRE = !!process.env.MIXDOG_SKIP_TOP_STDIN;
117
- // In-daemon `require()` would otherwise read fd 0 (the daemon's MCP stdio
118
- // pipe), corrupting it. Skip the top-level stdin read when this env-var is
119
- // set by hook-pipe-server.mjs around the require boundary.
120
- if (!IS_DAEMON_REQUIRE) {
121
- try {
122
- const _input = fs.readFileSync(0, 'utf8');
123
- if (_input) _event = JSON.parse(_input);
124
- } catch {}
125
- }
126
-
127
- if (_event.isSidechain) {
128
- teeStderr(`[session-start] skip PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()} reason=isSidechain\n`);
129
- process.exit(0);
130
- }
131
- if (_event.agentId) {
132
- teeStderr(`[session-start] skip PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()} reason=agentId=${_event.agentId}\n`);
133
- process.exit(0);
134
- }
135
- if (_event.is_sidechain) {
136
- teeStderr(`[session-start] skip PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()} reason=is_sidechain\n`);
137
- process.exit(0);
138
- }
139
- if (_event.agent_id) {
140
- teeStderr(`[session-start] skip PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()} reason=agent_id=${_event.agent_id}\n`);
141
- process.exit(0);
142
- }
143
- if (_event.kind && _event.kind !== 'interactive') {
144
- teeStderr(`[session-start] skip PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()} reason=kind=${_event.kind}\n`);
145
- process.exit(0);
146
- }
147
-
148
- const SESSION_START_CYCLE1_TIMEOUT_MS = Number.parseInt(process.env.MIXDOG_SESSION_START_CYCLE1_TIMEOUT_MS || '110000', 10);
149
- const MIXDOG_RUNTIME_ROOT = process.env.MIXDOG_RUNTIME_ROOT
150
- ? path.resolve(process.env.MIXDOG_RUNTIME_ROOT)
151
- : path.join(os.tmpdir(), 'mixdog');
152
- const ACTIVE_INSTANCE_FILE = path.join(MIXDOG_RUNTIME_ROOT, 'active-instance.json');
153
- if (!DATA_DIR || !PLUGIN_ROOT) {
154
- teeStderr(`[session-start] skip PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()} reason=missing-dirs DATA_DIR=${!!DATA_DIR} PLUGIN_ROOT=${!!PLUGIN_ROOT}\n`);
155
- process.exit(0);
156
- }
157
-
158
- if (!IS_DAEMON_REQUIRE) {
159
- teeStderr(`[session-start] enter PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()} sessionId=${_event.session_id || _event.sessionId || ''}\n`);
160
- }
161
-
162
- // ---------------------------------------------------------------------------
163
- // Common helpers (used by all parts).
164
- // ---------------------------------------------------------------------------
165
- function readJson(filePath) {
166
- try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch { return {}; }
167
- }
168
-
169
- function parsePositiveNumber(value) {
170
- const n = Number(value);
171
- return Number.isFinite(n) && n > 0 ? n : null;
172
- }
173
-
174
- function readAdvertisedMemoryPort() {
175
- const active = JSON.parse(fs.readFileSync(ACTIVE_INSTANCE_FILE, 'utf8'));
176
- const port = parsePositiveNumber(active && active.memory_port);
177
- if (!port) return null;
178
- const activeServerPid = parsePositiveNumber(active && active.server_pid);
179
- const memoryServerPid = parsePositiveNumber(active && active.memory_server_pid);
180
- if (activeServerPid && memoryServerPid && activeServerPid !== memoryServerPid) {
181
- return { stale: true, port, activeServerPid, memoryServerPid };
182
- }
183
- return { stale: false, port, active };
184
- }
185
-
186
- function sleepMs(ms) {
187
- return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
188
- }
189
-
190
- function readOwnerSecretFor(ownerInstanceId) {
191
- if (!ownerInstanceId) return '';
192
- try {
193
- const file = path.join(MIXDOG_RUNTIME_ROOT, `owner-secret-${String(ownerInstanceId)}.json`);
194
- const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
195
- return parsed && typeof parsed.secret === 'string' ? parsed.secret : '';
196
- } catch {
197
- return '';
198
- }
199
- }
200
-
201
- function ownerAuthHeaders(active) {
202
- const ownerInstanceId = active && active.instanceId;
203
- const ownerSecret = readOwnerSecretFor(ownerInstanceId);
204
- if (!ownerSecret) return null;
205
- return { 'x-owner-token': ownerSecret, 'x-owner-instance': String(ownerInstanceId) };
206
- }
207
-
208
- let _emitSink = null;
209
- function setEmitSink(fn) { _emitSink = fn || null; }
210
- function emit(additionalContext) {
211
- if (!additionalContext) {
212
- teeStderr(`[session-start] emit skipped: falsy context (empty string or null)\n`);
213
- return;
214
- }
215
- const byteLen = Buffer.byteLength(additionalContext, 'utf8');
216
- teeStderr(`[session-start] emit PART=${PART} bytes=${byteLen}\n`);
217
- const out = JSON.stringify({
218
- hookSpecificOutput: {
219
- hookEventName: 'SessionStart',
220
- additionalContext,
221
- },
222
- });
223
- if (_emitSink) _emitSink(out);
224
- else process.stdout.write(out);
225
- }
226
- function setEvent(e) { _event = e || {}; }
227
- function setPart(part) {
228
- if (['rules', 'core', 'recap'].includes(part)) PART = part;
229
- }
230
-
231
- // Memory-service HTTP port discovery. Single source of truth:
232
- // `<tmpdir>/mixdog/active-instance.json` `memory_port` field, written by
233
- // supervisor (server-main.mjs) on memory worker `ready` IPC. Throws when
234
- // missing — caller surfaces the failure rather than silently falling back
235
- // to a stale or default port.
236
- function getMemoryServicePort() {
237
- const advertised = readAdvertisedMemoryPort();
238
- if (!advertised || advertised.stale) {
239
- throw new Error(advertised && advertised.stale
240
- ? 'active-instance.json stale memory_port owner'
241
- : 'active-instance.json missing memory_port');
242
- }
243
- return advertised.port;
244
- }
245
-
246
- async function getLiveMemoryServicePort(probeMs = 200) {
247
- try {
248
- const advertised = readAdvertisedMemoryPort();
249
- if (!advertised || advertised.stale) return null;
250
- const port = advertised.port;
251
- const alive = await probeTcpPort(port, Math.max(1, probeMs));
252
- teeStderr(`[session-start] memoryDirect probePort=${port} probeMs=${probeMs} alive=${alive}\n`);
253
- return alive ? port : null;
254
- } catch {
255
- return null;
256
- }
257
- }
258
-
259
- async function memoryServicePost(urlPath, body, timeoutMs = 5000) {
260
- const port = getMemoryServicePort();
261
- const res = await httpPostJson({
262
- hostname: '127.0.0.1',
263
- port,
264
- path: urlPath,
265
- timeoutMs,
266
- body: body || {},
267
- });
268
- if (res.statusCode !== 200) {
269
- throw new Error(`memory-service ${urlPath} non-200 status=${res.statusCode}`);
270
- }
271
- try { return JSON.parse(res.body); }
272
- catch (e) { throw new Error(`memory-service ${urlPath} invalid JSON: ${e.message}`); }
273
- }
274
-
275
- function formatTs(ts) {
276
- const n = Number(ts);
277
- if (Number.isFinite(n) && n > 1e12) {
278
- return new Date(n).toLocaleString('sv-SE').slice(0, 16);
279
- }
280
- return String(ts ?? '').slice(0, 16);
281
- }
282
-
283
- // MM-DD HH:MM in local time for compact recap rendering.
284
- function formatTsShort(ts) {
285
- const n = Number(ts);
286
- if (!Number.isFinite(n) || n <= 1e12) return String(ts ?? '').slice(0, 16);
287
- const full = new Date(n).toLocaleString('sv-SE');
288
- return full.slice(5, 16);
289
- }
290
-
291
- // Single source of truth: lib/text-utils.cjs (also imported by memory-extraction.mjs).
292
- const { cleanMemoryText: cleanText } = require(path.join(PLUGIN_ROOT, 'lib', 'text-utils.cjs'));
293
-
294
- function resolveCwdScope(cwd) {
295
- try {
296
- let dir = path.resolve(cwd || process.cwd());
297
- while (true) {
298
- const candidate = path.join(dir, '.mixdog', 'project.id');
299
- try {
300
- const val = fs.readFileSync(candidate, 'utf8').trim();
301
- if (val.toLowerCase() === 'common') return null;
302
- if (val) {
303
- // Validate slug before returning — reject path traversal attempts.
304
- if (val.includes('..') || val.includes('\\')) return null;
305
- return val;
306
- }
307
- } catch {}
308
- const parent = path.dirname(dir);
309
- if (parent === dir) break;
310
- dir = parent;
311
- }
312
- } catch {}
313
- return null;
314
- }
315
-
316
- async function buildContext(cwd) {
317
- try {
318
- const _t0 = Date.now();
319
- const result = await memoryServicePost('/session-start/core-memory', {
320
- cwd: cwd || process.cwd(),
321
- });
322
- const _elapsed = Date.now() - _t0;
323
- if (!result || result.ok !== true) return '';
324
- const dbLines = Array.isArray(result.dbLines) ? result.dbLines : [];
325
- const userLines = Array.isArray(result.userLines) ? result.userLines : [];
326
- teeStderr(`[session-start] buildContext POST /session-start/core-memory elapsed=${_elapsed}ms dbLines=${dbLines.length} userLines=${userLines.length}\n`);
327
- const seen = new Set();
328
- const lines = [];
329
- // userLines (user-curated) and dbLines accumulate under the same char cap.
330
- // userLines go first so they win the budget. Each line is already short
331
- // (core_summary <=120, manual core <=120 at write time), so the cap is a
332
- // safety net rather than the primary limiter.
333
- const HEADER_LEN = '## Core Memory\n'.length;
334
- // CAP = 5000 chars — documented host-preview envelope. This is a byte cap,
335
- // not token-aware (adding a token counter would require a tokeniser dependency).
336
- // Deferred: token-aware cap once a lightweight counter is available.
337
- const CAP = 5000;
338
- let total = HEADER_LEN;
339
- for (const line of userLines) {
340
- const key = line.toLowerCase().replace(/\s+/g, ' ').slice(0, 120);
341
- if (seen.has(key)) continue;
342
- const add = line.length + 1;
343
- if (total + add > CAP) break;
344
- seen.add(key);
345
- lines.push(line);
346
- total += add;
347
- }
348
- // dbLines: accumulate score-DESC rows until the char cap is reached.
349
- for (const line of dbLines) {
350
- const key = line.toLowerCase().replace(/\s+/g, ' ').slice(0, 120);
351
- if (seen.has(key)) continue;
352
- const add = line.length + 1;
353
- if (total + add > CAP) break;
354
- seen.add(key);
355
- lines.push(line);
356
- total += add;
357
- }
358
- if (lines.length === 0) return '';
359
- return `## Core Memory\n${lines.join('\n')}`;
360
- } catch (e) {
361
- teeStderr(`[session-start] buildContext catch endpoint=/session-start/core-memory err=${e.message}\n`);
362
- process.stderr.write(`[session-start] context build failed: ${e.message}\n`);
363
- return '';
364
- }
365
- }
366
-
367
- // Returns { lines } — chronological "[MM-DD HH:MM] <summary>" entries
368
- // (oldest → newest), trimmed from the front so the rendered block fits the
369
- // SessionStart hook output cap (10,000 chars total — leaves margin for the
370
- // JSON wrapper around additionalContext; header "## Recap\n" reserved).
371
- async function buildRecapData(cwd) {
372
- const out = { lines: [] };
373
- try {
374
- const _t0 = Date.now();
375
- const result = await memoryServicePost('/session-start/recap', {
376
- cwd: cwd || process.cwd(),
377
- });
378
- const _elapsed = Date.now() - _t0;
379
- if (!result || result.ok !== true) return out;
380
- const rows = Array.isArray(result.rows) ? result.rows : [];
381
- teeStderr(`[session-start] buildRecapData POST /session-start/recap elapsed=${_elapsed}ms rows=${rows.length}\n`);
382
- if (rows.length === 0) return out;
383
-
384
- const rendered = rows.map(r => {
385
- const tsStr = formatTsShort(r.ts);
386
- const summary = String(r.summary || '').trim().slice(0, 1000);
387
- return summary ? `[${tsStr}] ${summary}` : '';
388
- }).filter(Boolean);
389
- if (rendered.length === 0) return out;
390
-
391
- // Dedup by normalized summary — newest-first, so older repeats drop.
392
- const seen = new Set();
393
- const uniq = [];
394
- for (const line of rendered) {
395
- const key = line.replace(/^\[[^\]]+\]\s*/, '').toLowerCase().replace(/\s+/g, ' ').slice(0, 80);
396
- if (seen.has(key)) continue;
397
- seen.add(key);
398
- uniq.push(line);
399
- }
400
-
401
- // Newest → oldest; keep accumulating from the newest end until the
402
- // running total would exceed the cap, then reverse to chronological.
403
- const HEADER_LEN = '## Recap\n'.length;
404
- const CAP = 5000;
405
- let total = HEADER_LEN;
406
- const kept = [];
407
- for (const line of uniq) {
408
- const add = line.length + 1;
409
- if (total + add > CAP) break;
410
- kept.push(line);
411
- total += add;
412
- }
413
- out.lines = kept.reverse();
414
- return out;
415
- } catch (e) {
416
- teeStderr(`[session-start] buildRecapData catch endpoint=/session-start/recap err=${e.message}\n`);
417
- process.stderr.write(`[session-start] recap build failed: ${e.message}\n`);
418
- return out;
419
- }
420
- }
421
-
422
- // ---------------------------------------------------------------------------
423
- // Skip flag — resume / compact reuses the existing context, so re-injecting
424
- // memory just bloats tokens. Rules still flow through so any rule changes
425
- // since the last turn take effect.
426
- // ---------------------------------------------------------------------------
427
- function _skipMemoryInject() { return _event.source === 'resume' || _event.source === 'compact'; }
428
- if (!IS_DAEMON_REQUIRE) {
429
- teeStderr(`[session-start] skipMemoryInject=${_skipMemoryInject()} PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()}\n`);
430
- }
431
-
432
- // ---------------------------------------------------------------------------
433
- // Part: rules (slot 1) — owns ALL one-shot session bootstrap work and emits
434
- // the rules block. Static .md content only; cycle1 is triggered by
435
- // core/recap slots (dedupe coalesces concurrent calls into one run).
436
- // ---------------------------------------------------------------------------
437
-
438
- function hasManagedClaudeMdBlock(targetPath) {
439
- if (!targetPath) return false;
440
- try {
441
- const { expandHome, MARKER_START, MARKER_END } = require(path.join(PLUGIN_ROOT, 'lib', 'claude-md-writer.cjs'));
442
- const resolved = expandHome(targetPath);
443
- if (!resolved || !fs.existsSync(resolved)) return false;
444
- const content = fs.readFileSync(resolved, 'utf8');
445
- return content.includes(MARKER_START) && content.includes(MARKER_END);
446
- } catch {
447
- return false;
448
- }
449
- }
450
-
451
- // Stable, NON-versioned launcher path in mixdog-owned plugin data — survives
452
- // cache cleanup and version rotation, so settings.json can point at it forever
453
- // (the launcher resolves the active install at runtime each tick).
454
- function stableLauncherPath() {
455
- return path.join(DATA_DIR, 'statusline-launcher.mjs').replace(/\\/g, '/');
456
- }
457
-
458
- function injectStatusLine(pluginRoot) {
459
- try {
460
- // Refresh the stable launcher copy from the current install each session
461
- // start so it tracks the latest committed launcher code.
462
- const launcherDst = stableLauncherPath();
463
- try {
464
- const launcherSrc = path.join(pluginRoot, 'bin', 'statusline-launcher.mjs');
465
- fs.mkdirSync(path.dirname(launcherDst), { recursive: true });
466
- fs.copyFileSync(launcherSrc, launcherDst);
467
- } catch (e) {
468
- process.stderr.write(`[session-start] statusLine launcher copy failed: ${e.message}\n`);
469
- }
470
-
471
- const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
472
- let raw;
473
- try { raw = fs.readFileSync(settingsPath, 'utf8'); } catch { return; }
474
- let settings;
475
- try { settings = JSON.parse(raw); } catch { return; }
476
- if (!settings || typeof settings !== 'object') return;
477
-
478
- const desiredCommand = `bun "${launcherDst}"`;
479
- // Launcher delegates to the fast native shim when present, so a constant
480
- // short tick is fine and avoids cadence ping-pong across versions.
481
- const desiredRefreshInterval = 5;
482
- const existing = settings.statusLine;
483
- // Recognize legacy mixdog-managed entries (versioned shim / statusline.mjs
484
- // commands and the new stable launcher) so old pinned commands are MIGRATED
485
- // to the stable launcher. A genuine user-custom statusLine is left alone.
486
- const _cmd = (existing && typeof existing === 'object' && typeof existing.command === 'string')
487
- ? existing.command : '';
488
- // Normalize Windows backslashes once, then require a mixdog-SPECIFIC marker
489
- // so a generic user command that merely mentions statusline.mjs is not
490
- // hijacked. A bare statusline.mjs NOT under a mixdog path must NOT match.
491
- const c = _cmd.replace(/\\/g, '/');
492
- // No standalone --kind=statusline arm: the real mixdog shim command always
493
- // carries the /mixdog-shim path (caught below), and a bare flag could be a
494
- // genuine user command (e.g. `node user-tool.mjs --kind=statusline`).
495
- const _looksMixdog = /\/mixdog-shim(?:\.exe)?/.test(c)
496
- || c.includes('/trib-plugin/mixdog/')
497
- || (c.includes('mixdog-trib-plugin') && c.includes('statusline-launcher.mjs'));
498
- const isOurs = existing && typeof existing === 'object'
499
- && (existing.source === 'mixdog-auto' || _looksMixdog);
500
-
501
- if (existing && !isOurs) return;
502
- if (
503
- isOurs
504
- && existing.command === desiredCommand
505
- && existing.type === 'command'
506
- && existing.refreshInterval === desiredRefreshInterval
507
- ) return;
508
-
509
- settings.statusLine = {
510
- type: 'command',
511
- command: desiredCommand,
512
- refreshInterval: desiredRefreshInterval,
513
- source: 'mixdog-auto',
514
- };
515
-
516
- const tmpPath = settingsPath + '.mixdog-tmp';
517
- fs.writeFileSync(tmpPath, JSON.stringify(settings, null, 2) + '\n');
518
- fs.renameSync(tmpPath, settingsPath);
519
- } catch (e) {
520
- process.stderr.write(`[session-start] statusLine inject failed: ${e.message}\n`);
521
- }
522
- }
523
-
524
- function cwdToProjectSlug(cwd) {
525
- return path.resolve(cwd).replace(/\\/g, '/').replace(/^([A-Za-z]):/, '$1-').replace(/\//g, '-');
526
- }
527
- function resolveTranscriptPath() {
528
- const direct = _event.transcript_path || _event.transcriptPath;
529
- if (typeof direct === 'string' && direct && fs.existsSync(direct)) return direct;
530
- const sessionId = _event.session_id || _event.sessionId;
531
- const cwd = _event.cwd || process.cwd();
532
- if (typeof sessionId === 'string' && sessionId) {
533
- const candidate = path.join(os.homedir(), '.claude', 'projects', cwdToProjectSlug(cwd), `${sessionId}.jsonl`);
534
- if (fs.existsSync(candidate)) return candidate;
535
- }
536
- return '';
537
- }
538
-
539
- // Prior-session transcript for the ingest-watermark barrier on `/clear`.
540
- // SessionStart carries the NEW session_id; rules-part rebind records the
541
- // outgoing path in active-instance.json `priorTranscriptPath` before updating
542
- // `transcriptPath`. Resolution is deterministic (no mtime scan).
543
- function resolvePriorTranscriptPath() {
544
- const sessionId = String(_event.session_id || _event.sessionId || '').trim();
545
- const active = readJson(ACTIVE_INSTANCE_FILE);
546
- const tp = active && active.transcriptPath;
547
- if (typeof tp === 'string' && tp && fs.existsSync(tp)) {
548
- const base = path.basename(tp, '.jsonl');
549
- if (sessionId && base !== sessionId) return tp;
550
- }
551
- const prior = active && active.priorTranscriptPath;
552
- if (typeof prior === 'string' && prior && fs.existsSync(prior)) {
553
- const base = path.basename(prior, '.jsonl');
554
- if (sessionId && base !== sessionId) return prior;
555
- }
556
- return '';
557
- }
558
-
559
- // Poll memory-service until offsetBytes >= fileSize for the prior transcript,
560
- // bounded by a dedicated sub-budget so cycle1 keeps a floor of the grace window.
561
- async function awaitPriorTranscriptIngested(deadline, opts = {}) {
562
- const slot = opts.slot || 'unknown';
563
- const start = Date.now();
564
- const windowMs = Math.max(0, deadline - start);
565
- // ~4s cap on the 8s graceMs case; smaller windows get half — cycle1 retains the rest.
566
- const BARRIER_BUDGET_MS = Math.min(4000, windowMs / 2);
567
- const barrierDeadline = Math.min(deadline, start + BARRIER_BUDGET_MS);
568
- const priorPath = resolvePriorTranscriptPath();
569
- if (!priorPath) {
570
- teeStderr(`[session-start] ingest-barrier slot=${slot} skip reason=no-prior-transcript source=${_event.source || ''}\n`);
571
- return;
572
- }
573
- teeStderr(`[session-start] ingest-barrier slot=${slot} priorPath=${priorPath}\n`);
574
- const pollMs = 200;
575
- while (Date.now() < barrierDeadline) {
576
- const remaining = barrierDeadline - Date.now();
577
- if (remaining <= 0) break;
578
- const port = await getLiveMemoryServicePort(Math.min(200, remaining));
579
- if (!port) {
580
- await sleepMs(Math.min(pollMs, remaining));
581
- continue;
582
- }
583
- try {
584
- const res = await httpPostJson({
585
- hostname: '127.0.0.1',
586
- port,
587
- path: '/transcript/ingest-sync',
588
- timeoutMs: Math.min(5000, remaining),
589
- body: { path: priorPath, cwd: _event.cwd || process.cwd() },
590
- });
591
- if (res.statusCode === 200) {
592
- let parsed;
593
- try { parsed = JSON.parse(res.body); } catch { parsed = null; }
594
- if (parsed && parsed.ok === true && parsed.complete === true) {
595
- teeStderr(`[session-start] ingest-barrier slot=${slot} complete offsetBytes=${parsed.offsetBytes} fileSize=${parsed.fileSize} elapsed=${Date.now() - start}ms\n`);
596
- return;
597
- }
598
- if (parsed && parsed.ok === true) {
599
- teeStderr(`[session-start] ingest-barrier slot=${slot} pending offsetBytes=${parsed.offsetBytes} fileSize=${parsed.fileSize}\n`);
600
- }
601
- }
602
- } catch (e) {
603
- teeStderr(`[session-start] ingest-barrier slot=${slot} err=${(e && e.message) || e}\n`);
604
- }
605
- await sleepMs(Math.min(pollMs, Math.max(0, barrierDeadline - Date.now())));
606
- }
607
- teeStderr(`[session-start] ingest-barrier slot=${slot} deadline-reached proceed elapsed=${Date.now() - start}ms\n`);
608
- }
609
-
610
- function rebindActiveInstance() {
611
- try {
612
- const activePath = ACTIVE_INSTANCE_FILE;
613
- if (!fs.existsSync(activePath)) return;
614
- const active = JSON.parse(fs.readFileSync(activePath, 'utf8'));
615
- if (!active.httpPort) return;
616
- const transcriptPath = resolveTranscriptPath();
617
- const payload = transcriptPath ? JSON.stringify({ transcriptPath }) : '';
618
- const authHeaders = ownerAuthHeaders(active);
619
- if (!authHeaders) return;
620
- const headers = { 'Content-Type': 'application/json', ...authHeaders };
621
- if (payload) headers['Content-Length'] = Buffer.byteLength(payload);
622
- const req2 = http.request({
623
- hostname: '127.0.0.1',
624
- port: active.httpPort,
625
- path: '/rebind',
626
- method: 'POST',
627
- timeout: 3000,
628
- headers,
629
- });
630
- req2.on('error', () => {});
631
- req2.on('timeout', () => req2.destroy());
632
- if (payload) req2.write(payload);
633
- req2.end();
634
- } catch {}
635
- }
636
-
637
- // TCP probe — resolves true if the port accepts a connection within probeMs,
638
- // false on ECONNREFUSED / EHOSTUNREACH / timeout / any other socket error.
639
- // Used by pollActiveInstance to skip stale active-instance.json entries left
640
- // over from a previous session whose channels owner is already dead.
641
- function probeTcpPort(port, probeMs) {
642
- return new Promise((resolve) => {
643
- let settled = false;
644
- const done = (alive) => {
645
- if (settled) return;
646
- settled = true;
647
- try { socket.destroy(); } catch {}
648
- resolve(alive);
649
- };
650
- const socket = net.createConnection({ port, host: '127.0.0.1' });
651
- socket.setTimeout(Math.max(1, probeMs));
652
- socket.once('connect', () => done(true));
653
- socket.once('error', () => done(false));
654
- socket.once('timeout', () => done(false));
655
- });
656
- }
657
-
658
- // Memory-runtime readiness probe. A promoted-but-uninitialized fork-proxy keeps
659
- // a live TCP listener while its `db` is still null (it short-circuited
660
- // _initRuntime), so a raw TCP accept does NOT prove the memory worker can serve
661
- // queries — recap/core would then 500 and silently drop context. GET /health
662
- // returns 200 only once the worker is _initialized with a live DB; 503 while
663
- // booting/promoting and 500 if the DB is broken. Gate awaitMemoryPort on a 200.
664
- function probeMemoryHealthy(port, timeoutMs) {
665
- return new Promise((resolve) => {
666
- let settled = false;
667
- const done = (ok) => { if (settled) return; settled = true; resolve(ok); };
668
- let req;
669
- try {
670
- req = http.request(
671
- { host: '127.0.0.1', port, path: '/health', method: 'GET', timeout: Math.max(1, timeoutMs) },
672
- (res) => {
673
- const ok = res.statusCode === 200;
674
- res.resume();
675
- res.once('end', () => done(ok));
676
- res.once('error', () => done(false));
677
- },
678
- );
679
- } catch { done(false); return; }
680
- req.once('error', () => done(false));
681
- req.once('timeout', () => { try { req.destroy(); } catch {} done(false); });
682
- req.end();
683
- });
684
- }
685
-
686
- async function pollActiveInstance(graceMs) {
687
- const activeDir = MIXDOG_RUNTIME_ROOT;
688
- const activeFile = ACTIVE_INSTANCE_FILE;
689
- const _pollStart = Date.now();
690
- const deadline = _pollStart + Math.max(0, graceMs);
691
- teeStderr(`[session-start] pollActiveInstance start graceMs=${graceMs}\n`);
692
- try { fs.mkdirSync(activeDir, { recursive: true }); } catch {}
693
-
694
- // Single read+probe attempt. Stale guard: file may be left by a dead owner,
695
- // so TCP-probe httpPort before accepting. Returns null on any failure
696
- // (missing file, missing httpPort, dead port, parse error, deadline hit).
697
- const tryRead = async () => {
698
- try {
699
- if (!fs.existsSync(activeFile)) return null;
700
- const active = JSON.parse(fs.readFileSync(activeFile, 'utf8'));
701
- if (!active || !active.httpPort) return null;
702
- const remaining = deadline - Date.now();
703
- if (remaining <= 0) return null;
704
- const probeMs = Math.min(200, remaining);
705
- const alive = await probeTcpPort(active.httpPort, probeMs);
706
- teeStderr(`[session-start] pollActiveInstance probePort=${active.httpPort} probeMs=${probeMs} alive=${alive}\n`);
707
- return alive ? active : null;
708
- } catch {
709
- return null;
710
- }
711
- };
712
-
713
- // Fast path: supervisor already up. Most sessions hit this — skips watch setup.
714
- const initial = await tryRead();
715
- if (initial) {
716
- teeStderr(`[session-start] pollActiveInstance done elapsed=${Date.now() - _pollStart}ms port=${initial.httpPort} via=initial\n`);
717
- return initial;
718
- }
719
-
720
- // Bounded poll loop. Windows fs.watch first-event latency averaged ~3.3s
721
- // on this directory (measured over recent sessions), so a fixed 200ms
722
- // tick is deterministic and faster on average. server.mjs writes
723
- // active-instance.json atomically via .tmp + rename, so every poll sees
724
- // either no file or a fully-written one — no torn-read window. setTimeout
725
- // caps total wait at graceMs so the function honors its contract.
726
- return new Promise((resolve) => {
727
- let settled = false;
728
- let inFlight = false;
729
- let pollTimer = null;
730
- let deadlineTimer = null;
731
-
732
- const finish = (result, via) => {
733
- if (settled) return;
734
- settled = true;
735
- if (pollTimer) { clearInterval(pollTimer); pollTimer = null; }
736
- if (deadlineTimer) { clearTimeout(deadlineTimer); deadlineTimer = null; }
737
- if (result) {
738
- teeStderr(`[session-start] pollActiveInstance done elapsed=${Date.now() - _pollStart}ms port=${result.httpPort} via=${via}\n`);
739
- } else {
740
- teeStderr(`[session-start] pollActiveInstance end elapsed=${Date.now() - _pollStart}ms result=null\n`, { critical: true });
741
- }
742
- resolve(result);
743
- };
744
-
745
- const checkOnce = async () => {
746
- if (settled || inFlight) return;
747
- inFlight = true;
748
- try {
749
- const a = await tryRead();
750
- if (a) finish(a, 'poll');
751
- } finally {
752
- inFlight = false;
753
- }
754
- };
755
-
756
- // Node setInterval / setTimeout are ref'd by default, so they keep the
757
- // event loop alive. The outer caller also awaits this Promise, which is
758
- // itself a liveness guarantee.
759
- pollTimer = setInterval(checkOnce, 200);
760
- const remaining = Math.max(0, deadline - Date.now());
761
- deadlineTimer = setTimeout(() => finish(null, null), remaining);
762
- });
763
- }
764
-
765
- // Wait until memory_port appears in active-instance.json and /health is ready.
766
- // pollActiveInstance only proves channels httpPort;
767
- // memory_port is written later via memory worker `ready` IPC. Without this,
768
- // getMemoryServicePort throws on fast /clear paths and recap emits 0 lines.
769
- async function awaitMemoryPort(graceMs) {
770
- const _t0 = Date.now();
771
- const deadline = _t0 + Math.max(0, graceMs);
772
- teeStderr(`[session-start] awaitMemoryPort start graceMs=${graceMs}\n`);
773
-
774
- while (Date.now() < deadline) {
775
- const remaining = deadline - Date.now();
776
- if (remaining <= 0) break;
777
- try {
778
- if (!fs.existsSync(ACTIVE_INSTANCE_FILE)) {
779
- await sleepMs(Math.min(200, remaining));
780
- continue;
781
- }
782
- const advertised = readAdvertisedMemoryPort();
783
- if (!advertised) {
784
- await sleepMs(Math.min(200, remaining));
785
- continue;
786
- }
787
- if (advertised.stale) {
788
- teeStderr(`[session-start] awaitMemoryPort stalePort=${advertised.port} activeServerPid=${advertised.activeServerPid} memoryServerPid=${advertised.memoryServerPid}\n`);
789
- await sleepMs(Math.min(200, remaining));
790
- continue;
791
- }
792
- const port = advertised.port;
793
- const probeMs = Math.min(200, remaining);
794
- const healthy = await probeMemoryHealthy(port, probeMs);
795
- let supersededBy = null;
796
- let staleAfterProbe = false;
797
- try {
798
- const latest = readAdvertisedMemoryPort();
799
- if (latest && latest.stale) staleAfterProbe = true;
800
- else if (latest && latest.port !== port) supersededBy = latest.port;
801
- } catch {}
802
- const suffix = `${staleAfterProbe ? ' staleAfterProbe=true' : ''}${supersededBy ? ` supersededBy=${supersededBy}` : ''}`;
803
- teeStderr(`[session-start] awaitMemoryPort probePort=${port} probeMs=${probeMs} healthy=${healthy}${suffix}\n`);
804
- if (staleAfterProbe || supersededBy) continue;
805
- if (healthy) {
806
- teeStderr(`[session-start] awaitMemoryPort done elapsed=${Date.now() - _t0}ms port=${port} via=poll\n`);
807
- return port;
808
- }
809
- } catch {
810
- // Re-read active-instance.json on the next tick; atomic rename can
811
- // transiently hide the file on Windows.
812
- }
813
- await sleepMs(Math.min(200, Math.max(0, deadline - Date.now())));
814
- }
815
-
816
- teeStderr(`[session-start] awaitMemoryPort end elapsed=${Date.now() - _t0}ms result=null\n`, { critical: true });
817
- return null;
818
- }
819
-
820
- function httpPostJson({ hostname, port, path: urlPath, timeoutMs, body, ownerActive, authHeaders: explicitAuthHeaders }) {
821
- return new Promise((resolve, reject) => {
822
- const _t0 = Date.now();
823
- const payload = typeof body === 'string' ? body : JSON.stringify(body);
824
- const authHeaders = explicitAuthHeaders || (ownerActive ? ownerAuthHeaders(ownerActive) : {});
825
- if (ownerActive && !authHeaders) {
826
- resolve({
827
- statusCode: 0,
828
- body: JSON.stringify({ ok: false, reason: 'owner-route-unavailable' }),
829
- ownerRouteUnavailable: true,
830
- });
831
- return;
832
- }
833
- const req = http.request({
834
- hostname,
835
- port,
836
- path: urlPath,
837
- method: 'POST',
838
- headers: {
839
- 'Content-Type': 'application/json',
840
- 'Content-Length': Buffer.byteLength(payload),
841
- ...authHeaders,
842
- },
843
- timeout: Math.max(1, timeoutMs),
844
- }, (res) => {
845
- const chunks = [];
846
- res.on('data', (c) => chunks.push(c));
847
- res.on('end', () => {
848
- const _body = Buffer.concat(chunks).toString('utf8');
849
- const _elapsed = Date.now() - _t0;
850
- let _bodyReason = null;
851
- try { const _p = JSON.parse(_body); if (_p && typeof _p.reason === 'string') _bodyReason = _p.reason; } catch {}
852
- teeStderr(`[session-start] httpPostJson path=${urlPath} port=${port} timeoutMs=${timeoutMs} statusCode=${res.statusCode} bodyReason=${_bodyReason || ''} elapsed=${_elapsed}ms\n`);
853
- resolve({ statusCode: res.statusCode, body: _body });
854
- });
855
- });
856
- req.on('error', (e) => reject(e));
857
- req.on('timeout', () => { req.destroy(new Error('timeout')); });
858
- req.end(payload);
859
- });
860
- }
861
-
862
- // Pull cycle1 signals from a cycle1 response. Memory worker returns an MCP
863
- // envelope { content:[{type:'text', text:'cycle1: chunks=N processed=M
864
- // skipped=K pending=P inFlight=B'}], isError }; channels owner returns
865
- // { ok, result } where result may carry the same text shape. Each field is
866
- // null when not parseable (treated downstream as "unknown / cannot verify").
867
- function extractCycleSignals(parsed) {
868
- const empty = { processed: null, pendingRows: null, skippedInFlight: null };
869
- if (!parsed) return empty;
870
- let text = '';
871
- if (Array.isArray(parsed.content) && parsed.content[0] && typeof parsed.content[0].text === 'string') {
872
- text = parsed.content[0].text;
873
- } else if (parsed.result && typeof parsed.result === 'object'
874
- && Array.isArray(parsed.result.content)
875
- && parsed.result.content[0]
876
- && typeof parsed.result.content[0].text === 'string') {
877
- // Channels owner wraps the memory worker's MCP envelope as
878
- // { ok, result: { content:[{type:'text',text:'cycle1: ...'}], isError } },
879
- // so the signal text lives at parsed.result.content[0].text.
880
- text = parsed.result.content[0].text;
881
- } else if (parsed.result && typeof parsed.result === 'object' && typeof parsed.result.text === 'string') {
882
- text = parsed.result.text;
883
- } else if (typeof parsed.result === 'string') {
884
- text = parsed.result;
885
- }
886
- if (typeof text !== 'string') return empty;
887
- const proc = text.match(/processed=(\d+)/);
888
- const pend = text.match(/pending=(\d+)/);
889
- const inflight = text.match(/inFlight=(true|false)/);
890
- return {
891
- processed: proc ? parseInt(proc[1], 10) : null,
892
- pendingRows: pend ? parseInt(pend[1], 10) : null,
893
- skippedInFlight: inflight ? inflight[1] === 'true' : null,
894
- };
895
- }
896
-
897
- async function requestCycle1MemoryDirect(deadline, opts = {}, priorReason = '') {
898
- const slot = opts.slot || 'unknown';
899
- const start = Date.now();
900
- const remainingForProbe = deadline - Date.now();
901
- if (remainingForProbe <= 0) {
902
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct reason=timeout-before-probe prior=${priorReason} elapsed=${Date.now() - start}ms\n`);
903
- return null;
904
- }
905
- const port = await getLiveMemoryServicePort(Math.min(200, remainingForProbe));
906
- if (!port) {
907
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct reason=no-memory-port prior=${priorReason} elapsed=${Date.now() - start}ms\n`);
908
- return null;
909
- }
910
- const remaining = deadline - Date.now();
911
- if (remaining <= 0) {
912
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct reason=timeout-before-post prior=${priorReason} elapsed=${Date.now() - start}ms\n`);
913
- return null;
914
- }
915
-
916
- try {
917
- const ON_DEMAND_CYCLE1_ARGS = { min_batch: 1, session_cap: 5, batch_size: 20, concurrency: 5 };
918
- const res = await httpPostJson({
919
- hostname: '127.0.0.1',
920
- port,
921
- path: '/api/tool',
922
- timeoutMs: remaining,
923
- body: {
924
- name: 'memory',
925
- arguments: {
926
- action: 'cycle1',
927
- ...ON_DEMAND_CYCLE1_ARGS,
928
- _callerDeadlineMs: remaining,
929
- },
930
- },
931
- });
932
- if (res.statusCode !== 200) {
933
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct reason=non-200 statusCode=${res.statusCode} prior=${priorReason} elapsed=${Date.now() - start}ms\n`);
934
- return { ok: false, reason: 'memory-direct-non-200', statusCode: res.statusCode, bodyReason: null, elapsedMs: Date.now() - start, route: 'memory-direct' };
935
- }
936
- let parsed;
937
- try { parsed = JSON.parse(res.body); }
938
- catch {
939
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct reason=parse-error prior=${priorReason} elapsed=${Date.now() - start}ms\n`);
940
- return { ok: false, reason: 'memory-direct-parse-error', statusCode: 200, bodyReason: null, elapsedMs: Date.now() - start, route: 'memory-direct' };
941
- }
942
- if (parsed && parsed.isError) {
943
- const sigText = Array.isArray(parsed.content) && parsed.content[0] ? parsed.content[0].text : '';
944
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct reason=body-is-error text=${String(sigText || '').slice(0, 200)} prior=${priorReason} elapsed=${Date.now() - start}ms\n`);
945
- return { ok: false, reason: 'memory-direct-body-is-error', statusCode: 200, bodyReason: null, elapsedMs: Date.now() - start, route: 'memory-direct' };
946
- }
947
- const sig = extractCycleSignals(parsed);
948
- const procStr = sig.processed != null ? sig.processed : '?';
949
- const pendStr = sig.pendingRows != null ? sig.pendingRows : '?';
950
- const inFlightStr = sig.skippedInFlight === true ? 'true'
951
- : sig.skippedInFlight === false ? 'false' : '?';
952
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct reason=ok processed=${procStr} pending=${pendStr} inFlight=${inFlightStr} prior=${priorReason} elapsed=${Date.now() - start}ms\n`);
953
- return {
954
- ok: true,
955
- processed: sig.processed,
956
- pendingRows: sig.pendingRows,
957
- skippedInFlight: sig.skippedInFlight,
958
- route: 'memory-direct',
959
- elapsedMs: Date.now() - start,
960
- };
961
- } catch (e) {
962
- const msg = (e && e.message) || e;
963
- const reason = /timeout/i.test(String(msg)) ? 'memory-direct-timeout' : 'memory-direct-http-error';
964
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct reason=${reason} err=${String(msg).slice(0, 200)} prior=${priorReason} elapsed=${Date.now() - start}ms\n`);
965
- return { ok: false, reason, statusCode: null, bodyReason: null, elapsedMs: Date.now() - start, route: 'memory-direct' };
966
- }
967
- }
968
-
969
- async function waitCycle1MemoryDirect(deadline, opts = {}, waitMs = 1800) {
970
- const slot = opts.slot || 'unknown';
971
- const start = Date.now();
972
- const waitUntil = Math.min(deadline, start + Math.max(0, waitMs));
973
- const pollMs = 50;
974
- let attempt = 0;
975
- while (Date.now() < waitUntil) {
976
- const sleepMs = Math.min(pollMs, waitUntil - Date.now());
977
- if (sleepMs > 0) await new Promise((r) => setTimeout(r, sleepMs));
978
- const direct = await requestCycle1MemoryDirect(deadline, opts, `pre-channels-wait-${attempt}`);
979
- if (direct && direct.ok) {
980
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct wait-hit attempt=${attempt} elapsed=${Date.now() - start}ms\n`);
981
- return direct;
982
- }
983
- if (direct && !direct.ok) {
984
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct wait-stop attempt=${attempt} reason=${direct.reason || 'unknown'} elapsed=${Date.now() - start}ms\n`);
985
- return null;
986
- }
987
- attempt++;
988
- }
989
- teeStderr(`[session-start] cycle1 slot=${slot} route=memory-direct wait-miss attempts=${attempt} elapsed=${Date.now() - start}ms\n`);
990
- return null;
991
- }
992
-
993
- // One full cycle1 attempt. Prefer the memory service when it is already
994
- // advertised and alive: channels /cycle1 is only a proxy to the same memory
995
- // action, while the channels owner can lag during session handoff. If memory
996
- // is not ready, fall back to the channels owner readiness path.
997
- async function requestCycle1Once(deadline, opts) {
998
- const slot = opts.slot || 'unknown';
999
- const graceMs = Number.isFinite(opts.graceMs) ? opts.graceMs : 5000;
1000
- const start = Date.now();
1001
-
1002
- const finish = (payload) => {
1003
- const elapsedMs = Date.now() - start;
1004
- if (payload.ok) {
1005
- const procStr = payload.processed != null ? payload.processed : '?';
1006
- const pendStr = payload.pendingRows != null ? payload.pendingRows : '?';
1007
- const inFlightStr = payload.skippedInFlight === true ? 'true'
1008
- : payload.skippedInFlight === false ? 'false' : '?';
1009
- teeStderr(`[session-start] cycle1 slot=${slot} route=channels reason=ok processed=${procStr} pending=${pendStr} inFlight=${inFlightStr} elapsed=${elapsedMs}ms\n`);
1010
- return {
1011
- ok: true,
1012
- processed: payload.processed,
1013
- pendingRows: payload.pendingRows,
1014
- skippedInFlight: payload.skippedInFlight,
1015
- route: 'channels',
1016
- elapsedMs,
1017
- };
1018
- }
1019
- const sc = payload.statusCode != null ? ` statusCode=${payload.statusCode}` : '';
1020
- teeStderr(`[session-start] cycle1 slot=${slot} route=channels reason=${payload.reason}${sc} elapsed=${elapsedMs}ms\n`);
1021
- return { ok: false, reason: payload.reason, statusCode: payload.statusCode, bodyReason: payload.bodyReason || null, elapsedMs, route: 'channels' };
1022
- };
1023
-
1024
- const classifyError = (e) => {
1025
- const msg = (e && e.message) || '';
1026
- if (/timeout/i.test(msg)) return 'timeout';
1027
- if ((e && e.code === 'ECONNREFUSED') || /ECONNREFUSED/i.test(msg)) return 'connect-refused';
1028
- return 'http-error';
1029
- };
1030
-
1031
- const directFirst = await requestCycle1MemoryDirect(deadline, opts, 'pre-channels');
1032
- if (directFirst && directFirst.ok) return directFirst;
1033
- if (!directFirst) {
1034
- const directAfterWait = await waitCycle1MemoryDirect(deadline, opts, Math.min(graceMs, 1800));
1035
- if (directAfterWait && directAfterWait.ok) return directAfterWait;
1036
- }
1037
-
1038
- const remainingForGrace = deadline - Date.now();
1039
- if (remainingForGrace <= 0) return finish({ ok: false, reason: 'timeout' });
1040
- const active = await pollActiveInstance(Math.min(graceMs, remainingForGrace));
1041
- const tPollEnd = Date.now();
1042
- if (!active) {
1043
- const direct = await requestCycle1MemoryDirect(deadline, opts, 'no-active-instance');
1044
- if (direct && direct.ok) return direct;
1045
- const reason = (Date.now() >= deadline) ? 'timeout' : 'no-active-instance';
1046
- return finish({ ok: false, reason });
1047
- }
1048
- const port = active.httpPort;
1049
- const remaining = deadline - Date.now();
1050
- if (remaining <= 0) return finish({ ok: false, reason: 'timeout' });
1051
- const authHeaders = ownerAuthHeaders(active);
1052
- if (!authHeaders) return finish({ ok: false, reason: 'owner-route-unavailable' });
1053
-
1054
- try {
1055
- const tPostStart = Date.now();
1056
- // On-demand cycle1 (SessionStart hook path): min_batch=1 triggers on a
1057
- // single pending row; session_cap=5 × batch_size=20 caps a single hook
1058
- // pass at 100 rows so wallclock stays low. Use modest 2-way fan-out when
1059
- // multiple windows are ready; periodic path also runs 2×50 in parallel.
1060
- const ON_DEMAND_CYCLE1_ARGS = { min_batch: 1, session_cap: 5, batch_size: 20, concurrency: 5 };
1061
- const res = await httpPostJson({
1062
- hostname: '127.0.0.1',
1063
- port,
1064
- path: '/cycle1',
1065
- timeoutMs: remaining,
1066
- ownerActive: active,
1067
- authHeaders,
1068
- body: { timeout_ms: remaining, args: ON_DEMAND_CYCLE1_ARGS },
1069
- });
1070
- teeStderr(`[session-start] cycle1 slot=${slot} timing pollMs=${tPollEnd - start} postMs=${Date.now() - tPostStart}\n`);
1071
- if (res.ownerRouteUnavailable) {
1072
- return finish({ ok: false, reason: 'owner-route-unavailable' });
1073
- }
1074
- if (res.statusCode !== 200) {
1075
- // Surface the channels endpoint's body `reason` (memory-not-ready,
1076
- // worker-unavailable, ipc-error, memory-timeout, ...) so downstream
1077
- // retry logic and operators can see the precise transient class.
1078
- let bodyReason = null;
1079
- try {
1080
- const parsed = JSON.parse(res.body);
1081
- if (parsed && typeof parsed.reason === 'string') bodyReason = parsed.reason;
1082
- } catch {}
1083
- const reason = bodyReason ? `non-200/${bodyReason}` : 'non-200';
1084
- return finish({ ok: false, reason, statusCode: res.statusCode, bodyReason });
1085
- }
1086
- try {
1087
- const parsed = JSON.parse(res.body);
1088
- if (parsed && parsed.ok) {
1089
- const sig = extractCycleSignals(parsed);
1090
- return finish({
1091
- ok: true,
1092
- processed: sig.processed,
1093
- pendingRows: sig.pendingRows,
1094
- skippedInFlight: sig.skippedInFlight,
1095
- });
1096
- }
1097
- return finish({ ok: false, reason: 'body-not-ok', statusCode: 200 });
1098
- } catch {
1099
- return finish({ ok: false, reason: 'parse-error', statusCode: 200 });
1100
- }
1101
- } catch (e) {
1102
- const reason = classifyError(e);
1103
- if (reason === 'connect-refused' || reason === 'timeout') {
1104
- const direct = await requestCycle1MemoryDirect(deadline, opts, reason);
1105
- if (direct && direct.ok) return direct;
1106
- }
1107
- return finish({ ok: false, reason });
1108
- }
1109
- }
1110
-
1111
- // Public entry point. Single in-flight call — server-main.callWorker now
1112
- // awaits the worker's first 'ready' IPC, so a pre-ready /cycle1 holds until
1113
- // memory is up instead of bouncing 503. Keep one follow-up retry for the
1114
- // processed=0 case: that means either an in-flight dedup hit (server
1115
- // returned the prior run's empty result) or a pre-ingest race
1116
- // (transcript-watch had not yet ingested pending raw entries). A short sleep
1117
- // + second call covers both. If the second pass also returns 0, genuinely
1118
- // empty.
1119
- async function requestCycle1(timeoutMs, opts = {}) {
1120
- const slot = opts.slot || 'unknown';
1121
- const graceMs = Number.isFinite(opts.graceMs) ? opts.graceMs : 5000;
1122
- const start = Date.now();
1123
- const deadline = start + Math.max(0, timeoutMs);
1124
- teeStderr(`[session-start] cycle1 slot=${slot} start graceMs=${graceMs} timeoutMs=${timeoutMs}\n`);
1125
- teeStderr(`[boot-time] tag=cycle1-entry slot=${slot} tMs=${start}\n`);
1126
-
1127
- // Boot-race transient classifier: a fresh session can fire cycle1 while the
1128
- // new owner's channels worker is still binding 3462 (connect-refused), the
1129
- // active-instance.json is briefly empty (no-active-instance), or channels is
1130
- // up but the parent's memory worker hasn't sent its first 'ready' IPC yet
1131
- // (503 with bodyReason in {memory-not-ready, worker-unavailable, ipc-error,
1132
- // memory-timeout}). Warm peer boots often clear in 1–3s; cold first-boot
1133
- // runtime init (PG attach + embedding warmup under multi-instance contention)
1134
- // can take longer, so retry with backoff up to a wider budget rather than
1135
- // skipping recap entirely.
1136
- const TRANSIENT_BOOT_BODY_REASONS = new Set([
1137
- 'memory-not-ready',
1138
- 'worker-unavailable',
1139
- 'ipc-error',
1140
- 'memory-timeout',
1141
- 'backend-not-ready',
1142
- 'beacon-booting',
1143
- ]);
1144
- // memory-degraded is NOT transient: restart cap exceeded or PG PANIC.
1145
- // Retrying it within the transient budget (~45s) would only delay the
1146
- // session with no benefit, so it is excluded before transient classification.
1147
- const NON_TRANSIENT_BOOT_BODY_REASONS = new Set([
1148
- 'memory-degraded',
1149
- ]);
1150
- const TRANSIENT_TOP_REASONS = new Set([
1151
- 'connect-refused',
1152
- 'no-active-instance',
1153
- ]);
1154
- const isTransientBootFailure = (r) => {
1155
- if (!r || r.ok) return false;
1156
- if (r.bodyReason && NON_TRANSIENT_BOOT_BODY_REASONS.has(r.bodyReason)) return false;
1157
- if (r.bodyReason && TRANSIENT_BOOT_BODY_REASONS.has(r.bodyReason)) return true;
1158
- if (TRANSIENT_TOP_REASONS.has(r.reason)) return true;
1159
- // 5xx + missing body — boot/restart race: a worker socket closed
1160
- // mid-request surfaces as a channels /cycle1 500 (bodyReason empty), and
1161
- // a pre-stub 503 looks the same. Both resolve once the worker finishes
1162
- // (re)starting, so retry within the boot budget instead of aborting
1163
- // recap/core. Known degraded states carry a bodyReason and are filtered
1164
- // by NON_TRANSIENT_BOOT_BODY_REASONS above before reaching here.
1165
- if (r.statusCode >= 500 && !r.bodyReason) return true;
1166
- return false;
1167
- };
1168
- const TRANSIENT_RETRY_DELAY_MS = 250;
1169
- const TRANSIENT_RETRY_BUDGET_MS = 45000;
1170
- const transientDeadline = start + TRANSIENT_RETRY_BUDGET_MS;
1171
-
1172
- try {
1173
- await awaitPriorTranscriptIngested(deadline, opts);
1174
- let r1 = await requestCycle1Once(deadline, opts);
1175
- let transientAttempt = 0;
1176
- while (
1177
- isTransientBootFailure(r1)
1178
- && Date.now() < transientDeadline
1179
- && (deadline - Date.now()) > TRANSIENT_RETRY_DELAY_MS + 500
1180
- ) {
1181
- transientAttempt++;
1182
- teeStderr(`[session-start] cycle1 slot=${slot} transient-retry attempt=${transientAttempt} bodyReason=${r1.bodyReason || ''} statusCode=${r1.statusCode != null ? r1.statusCode : ''} elapsed=${r1.elapsedMs}ms nextDelay=${TRANSIENT_RETRY_DELAY_MS}ms reason=${r1.reason}\n`);
1183
- await new Promise((r) => setTimeout(r, TRANSIENT_RETRY_DELAY_MS));
1184
- r1 = await requestCycle1Once(deadline, { ...opts, slot: `${slot}:t${transientAttempt}` });
1185
- }
1186
- if (!r1.ok) return r1;
1187
- if (r1.processed != null && r1.processed > 0) return r1;
1188
- // After ingest-barrier, pendingRows===0 with no in-flight dedup is terminal.
1189
- if (r1.pendingRows === 0 && r1.skippedInFlight === false) return r1;
1190
- const RETRY_DELAY_MS = 800;
1191
- const remaining = deadline - Date.now();
1192
- if (remaining <= RETRY_DELAY_MS + 200) return r1;
1193
- await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
1194
- const r2 = await requestCycle1Once(deadline, { ...opts, slot: `${slot}:r` });
1195
- if (!r2.ok) {
1196
- teeStderr(`[session-start] cycle1 slot=${slot} r2-failed stale-fallback r1.pendingRows=${r1.pendingRows != null ? r1.pendingRows : '?'} r1.skippedInFlight=${r1.skippedInFlight != null ? r1.skippedInFlight : '?'}\n`);
1197
- }
1198
- return r2.ok ? r2 : r1;
1199
- } catch (e) {
1200
- teeStderr(`[session-start] cycle1 slot=${slot} exception=${(e && e.message) || e}\n`);
1201
- return { ok: false, reason: 'exception', elapsedMs: Date.now() - start };
1202
- }
1203
- }
1204
-
1205
- // Best-effort POST /recap/reset to the channels owner. Used on `/clear` so
1206
- // the forked status server's recapState (which lives in a child process the
1207
- // hook can't reach via IPC) drops the prior session's badge. Bounded by
1208
- // graceMs and silent on failure — recap reset is cosmetic, never block
1209
- // SessionStart on it.
1210
- async function requestRecapReset(graceMs) {
1211
- try {
1212
- const active = await pollActiveInstance(Math.max(0, graceMs));
1213
- if (!active || !active.httpPort) {
1214
- teeStderr('[session-start] recap-reset skipped: no active instance\n');
1215
- return;
1216
- }
1217
- const authHeaders = ownerAuthHeaders(active);
1218
- if (!authHeaders) {
1219
- teeStderr('[session-start] recap-reset skipped: owner route unavailable in this session\n');
1220
- return;
1221
- }
1222
- const res = await httpPostJson({
1223
- hostname: '127.0.0.1',
1224
- port: active.httpPort,
1225
- path: '/recap/reset',
1226
- timeoutMs: 2000,
1227
- ownerActive: active,
1228
- authHeaders,
1229
- body: {},
1230
- });
1231
- if (res.statusCode !== 200) {
1232
- teeStderr(`[session-start] recap-reset non-200 status=${res.statusCode}\n`);
1233
- }
1234
- } catch (e) {
1235
- teeStderr(`[session-start] recap-reset failed: ${(e && e.message) || e}\n`);
1236
- }
1237
- }
1238
-
1239
- async function runRulesPart() {
1240
- teeStderr(`[session-start] runRulesPart enter PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()}\n`);
1241
- // First-boot one-shot work — only slot 1 (rules) runs this. Other slots
1242
- // skip it entirely so they stay read-only and side-effect free.
1243
- try {
1244
- const flagPath = path.join(DATA_DIR, '.first-boot-seen');
1245
- if (!fs.existsSync(flagPath)) {
1246
- // No first-boot config-window open here: it double-booted the
1247
- // setup-server against the unconditional every-session `--prewarm`
1248
- // below (both racing to bind the port). The server is warmed by the
1249
- // prewarm; the window opens only on an explicit `/mixdog:config`.
1250
- fs.writeFileSync(flagPath, '');
1251
- }
1252
- } catch {}
1253
-
1254
- // Every-session pre-warm: boot the config-UI setup-server in the
1255
- // background (window-free) so the first `/mixdog:config` open is instant.
1256
- // Fire-and-forget + hidden; `--prewarm` makes launch-core boot the server
1257
- // only when it is not already alive and never opens a browser window, so
1258
- // this is a cheap no-op once the server is running.
1259
- try {
1260
- const _prewarm = spawn('bun', [path.join(PLUGIN_ROOT, 'setup', 'launch.mjs'), '--prewarm'], {
1261
- detached: true,
1262
- stdio: 'ignore',
1263
- windowsHide: true,
1264
- });
1265
- // ENOENT (bun not on PATH) and other spawn failures surface ASYNC as an
1266
- // 'error' event, NOT via the try/catch above. Without a listener Node
1267
- // throws it as an uncaught exception and crashes the hook. Fail soft:
1268
- // prewarm is a best-effort optimization, never required for a session.
1269
- _prewarm.on('error', (e) => {
1270
- teeStderr(`[session-start] prewarm spawn failed (non-fatal): ${(e && e.message) || e}\n`);
1271
- });
1272
- _prewarm.unref();
1273
- } catch {}
1274
-
1275
- try {
1276
- const asp = path.join(DATA_DIR, 'active-session.txt');
1277
- if (fs.existsSync(asp)) fs.unlinkSync(asp);
1278
- } catch {}
1279
-
1280
- // Persist user cwd so the MCP server (spawned from cache dir) can resolve
1281
- // the correct sandbox root. Atomic rename prevents partial reads.
1282
- try {
1283
- const eventCwd = typeof _event.cwd === 'string' ? _event.cwd.trim() : '';
1284
- if (eventCwd) {
1285
- const cwdTxtPath = path.join(DATA_DIR, 'user-cwd.txt');
1286
- const cwdTmpPath = cwdTxtPath + '.tmp';
1287
- fs.writeFileSync(cwdTmpPath, eventCwd);
1288
- fs.renameSync(cwdTmpPath, cwdTxtPath);
1289
- }
1290
- } catch {}
1291
-
1292
- try {
1293
- const stalePending = path.join(DATA_DIR, 'recap-pending.json');
1294
- if (fs.existsSync(stalePending)) fs.unlinkSync(stalePending);
1295
- } catch {}
1296
-
1297
- injectStatusLine(PLUGIN_ROOT);
1298
- rebindActiveInstance();
1299
-
1300
- let _channelsConfig = {};
1301
- try { _channelsConfig = readSection('channels'); } catch {}
1302
- const injection = _channelsConfig && typeof _channelsConfig.promptInjection === 'object' ? _channelsConfig.promptInjection : {};
1303
- const claudeMdMode = injection.mode === 'claude_md';
1304
- const claudeMdTargetPath = typeof injection.targetPath === 'string' && injection.targetPath
1305
- ? injection.targetPath
1306
- : '~/.claude/CLAUDE.md';
1307
- const needsBootstrapInjection = claudeMdMode && !hasManagedClaudeMdBlock(claudeMdTargetPath);
1308
-
1309
- let additionalContext = '';
1310
- if (!claudeMdMode || needsBootstrapInjection) {
1311
- try {
1312
- const { buildInjectionContent } = require(path.join(PLUGIN_ROOT, 'lib', 'rules-builder.cjs'));
1313
- additionalContext = buildInjectionContent({ PLUGIN_ROOT, DATA_DIR }) || '';
1314
- } catch {}
1315
- }
1316
-
1317
- // claude_md mode + missing managed block: persist the file alongside emit so the
1318
- // next session boots from the managed block instead of the inline fallback.
1319
- if (claudeMdMode && needsBootstrapInjection && additionalContext) {
1320
- try {
1321
- const { upsertManagedBlock } = require(path.join(PLUGIN_ROOT, 'lib', 'claude-md-writer.cjs'));
1322
- upsertManagedBlock(claudeMdTargetPath, additionalContext);
1323
- teeStderr(`[session-start] claude_md: regenerated ${claudeMdTargetPath} after missing managed block\n`);
1324
- } catch (e) {
1325
- teeStderr(`[session-start] claude_md regenerate failed: ${e && e.message || e}\n`);
1326
- }
1327
- }
1328
-
1329
- // On `/clear`, drop the prior session's recap badge from the forked status
1330
- // server. Hook runs in a separate cjs process with no IPC handle to that
1331
- // child, so we POST /recap/reset to the channels owner instead. Best
1332
- // effort, short grace — channels owner is usually already up on /clear.
1333
- if (_event.source === 'clear') {
1334
- // Fire-and-forget — recap reset is cosmetic, never block response path.
1335
- requestRecapReset(3000).catch(() => {});
1336
- }
1337
-
1338
- // Surface the session-entry working directory so the Lead knows where
1339
- // relative paths resolve before any `cwd set`. Reads the user-cwd.txt seed
1340
- // written just above (mirrors the host's _event.cwd). Best-effort — never
1341
- // let it break rule injection.
1342
- let _startDir = '';
1343
- try {
1344
- _startDir = fs.readFileSync(path.join(DATA_DIR, 'user-cwd.txt'), 'utf8').trim();
1345
- if (_startDir) {
1346
- const _startBlock = `## Starting directory\n${_startDir}`;
1347
- additionalContext = additionalContext ? `${additionalContext}\n\n${_startBlock}` : _startBlock;
1348
- }
1349
- } catch {}
1350
-
1351
- // Other owned directories — full paths (the cwd tool's background scan
1352
- // persists them to cwd-projects.json). Best-effort — never let it break
1353
- // rule injection. Dynamic, so it lives here not in static rules.
1354
- try {
1355
- const _projParsed = JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'cwd-projects.json'), 'utf8'));
1356
- const _projects = Array.isArray(_projParsed && _projParsed.projects) ? _projParsed.projects : [];
1357
- const _paths = _projects
1358
- .map((p) => String(p.path || '').trim())
1359
- .filter((p) => p && p !== _startDir);
1360
- if (_paths.length) {
1361
- const _otherBlock = `## Other directories\n${_paths.map((p) => `- ${p}`).join('\n')}`;
1362
- additionalContext = additionalContext ? `${additionalContext}\n\n${_otherBlock}` : _otherBlock;
1363
- }
1364
- } catch {}
1365
-
1366
- emit(additionalContext);
1367
- teeStderr(`[session-start] runRulesPart done\n`);
1368
- }
1369
-
1370
- // ---------------------------------------------------------------------------
1371
- // Part: core (slot 2) — Core Memory only. Runs in its own process so each
1372
- // SessionStart additionalContext is sized independently against the host
1373
- // preview cap. Pairs with recap (slot 3); both are spawned in parallel by
1374
- // the host and share the cycle1 await on the server side.
1375
- // ---------------------------------------------------------------------------
1376
- async function runCorePart() {
1377
- if (_skipMemoryInject()) {
1378
- teeStderr(`[session-start] runCorePart skip PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()} reason=skipMemoryInject\n`);
1379
- return;
1380
- }
1381
- // graceMs=8000 covers supervisor cold-start ceiling. fs.watch unblocks
1382
- // immediately on active-instance.json creation, so normal boots pay only
1383
- // the actual startup time (no extra wait). Without this, first attempt
1384
- // timed out at 3000ms before supervisor finished spawning, forcing a
1385
- // transient-retry round-trip that added ~3s to wall-clock.
1386
- const r = await requestCycle1(SESSION_START_CYCLE1_TIMEOUT_MS, { graceMs: 8000, slot: 'core' });
1387
- if (r.ok !== true) {
1388
- if (r.reason === 'owner-route-unavailable') {
1389
- teeStderr('[session-start] core cycle1 skipped: owner route unavailable in this session\n');
1390
- } else {
1391
- teeStderr(`[session-start] core aborted: cycle1 await failed reason=${r.reason}\n`);
1392
- return;
1393
- }
1394
- }
1395
- // cycle1 ok guarantees channels owner liveness only; memory_port lands
1396
- // later via worker `ready` IPC. Wait so getMemoryServicePort below does
1397
- // not throw on fast /clear paths.
1398
- const memPort = await awaitMemoryPort(8000);
1399
- if (!memPort) {
1400
- teeStderr(`[session-start] core aborted: memory_port unavailable within graceMs\n`);
1401
- return;
1402
- }
1403
- const tStart = Date.now();
1404
- try {
1405
- const tCtxStart = Date.now();
1406
- const ctx = await buildContext(_event.cwd || process.cwd());
1407
- teeStderr(`[session-start] core stage=buildContext elapsed=${Date.now() - tCtxStart}ms hasCtx=${!!ctx}\n`);
1408
- if (ctx) {
1409
- const tEmitStart = Date.now();
1410
- emit(ctx);
1411
- teeStderr(`[session-start] core stage=emit elapsed=${Date.now() - tEmitStart}ms\n`);
1412
- }
1413
- } catch (e) {
1414
- process.stderr.write(`[session-start] core build failed: ${e.message}\n`);
1415
- }
1416
- teeStderr(`[session-start] core done totalElapsed=${Date.now() - tStart}ms\n`);
1417
- }
1418
-
1419
- // ---------------------------------------------------------------------------
1420
- // Part: recap (slot 3) — Recap entries only. Spawned in parallel with core.
1421
- // ---------------------------------------------------------------------------
1422
- async function runRecapPart() {
1423
- if (_skipMemoryInject()) {
1424
- teeStderr(`[session-start] runRecapPart skip PART=${PART} source=${_event.source || ''} cwd=${_event.cwd || process.cwd()} reason=skipMemoryInject\n`);
1425
- return;
1426
- }
1427
- // graceMs=8000: see runCorePart for invariant rationale.
1428
- const r = await requestCycle1(SESSION_START_CYCLE1_TIMEOUT_MS, { graceMs: 8000, slot: 'recap' });
1429
- if (r.ok !== true) {
1430
- if (r.reason === 'owner-route-unavailable') {
1431
- teeStderr('[session-start] recap cycle1 skipped: owner route unavailable in this session\n');
1432
- } else {
1433
- teeStderr(`[session-start] recap aborted: cycle1 await failed reason=${r.reason}\n`);
1434
- return;
1435
- }
1436
- }
1437
- // See runCorePart: cycle1 success does not imply memory_port readiness.
1438
- const memPort = await awaitMemoryPort(8000);
1439
- if (!memPort) {
1440
- teeStderr(`[session-start] recap aborted: memory_port unavailable within graceMs\n`);
1441
- return;
1442
- }
1443
- const tStart = Date.now();
1444
- try {
1445
- const tRecapStart = Date.now();
1446
- const recapData = await buildRecapData(_event.cwd || process.cwd());
1447
- const lines = (recapData && recapData.lines) || [];
1448
- teeStderr(`[session-start] recap stage=buildRecapData elapsed=${Date.now() - tRecapStart}ms lines=${lines.length}\n`);
1449
- if (lines.length > 0) {
1450
- const tEmitStart = Date.now();
1451
- emit(`## Recap\n${lines.join('\n')}`);
1452
- teeStderr(`[session-start] recap stage=emit elapsed=${Date.now() - tEmitStart}ms\n`);
1453
- }
1454
- } catch (e) {
1455
- process.stderr.write(`[session-start] recap build failed: ${e.message}\n`);
1456
- }
1457
- teeStderr(`[session-start] recap done totalElapsed=${Date.now() - tStart}ms\n`);
1458
- }
1459
-
1460
- // ---------------------------------------------------------------------------
1461
- // Exports for in-process daemon use (mixdog-shim → hook-pipe-server.mjs).
1462
- // ---------------------------------------------------------------------------
1463
- module.exports = { runRulesPart, runCorePart, runRecapPart, setEvent, setEmitSink, setPart };
1464
-
1465
- // ---------------------------------------------------------------------------
1466
- // Main IIFE — dispatch on PART. Only runs when invoked as the entry script;
1467
- // require'd from the daemon stays a no-op (PART is undefined).
1468
- // ---------------------------------------------------------------------------
1469
- if (require.main === module) {
1470
- (async () => {
1471
- if (PART === 'rules') {
1472
- await runRulesPart();
1473
- } else if (PART === 'core') {
1474
- await runCorePart();
1475
- } else if (PART === 'recap') {
1476
- await runRecapPart();
1477
- }
1478
- })();
1479
- }