mixdog 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (404) hide show
  1. package/.claude-plugin/marketplace.json +31 -0
  2. package/.claude-plugin/plugin.json +20 -0
  3. package/.gitattributes +34 -0
  4. package/.mcp.json +14 -0
  5. package/ARCHITECTURE.md +77 -0
  6. package/CHANGELOG.md +7 -0
  7. package/CONTRIBUTING.md +45 -0
  8. package/DATA-FLOW.md +79 -0
  9. package/LICENSE +21 -0
  10. package/README.md +389 -0
  11. package/SECURITY.md +138 -0
  12. package/UNINSTALL.md +112 -0
  13. package/agents/maintenance.md +5 -0
  14. package/agents/memory-classification.md +30 -0
  15. package/agents/scheduler-task.md +18 -0
  16. package/agents/webhook-handler.md +27 -0
  17. package/agents/worker.md +24 -0
  18. package/bin/bridge +133 -0
  19. package/bin/statusline-launcher.mjs +78 -0
  20. package/bin/statusline-lib.mjs +550 -0
  21. package/bin/statusline.mjs +607 -0
  22. package/bun.lock +802 -0
  23. package/commands/config.md +16 -0
  24. package/commands/doctor.md +13 -0
  25. package/commands/setup.md +17 -0
  26. package/defaults/cycle3-review-prompt.md +90 -0
  27. package/defaults/hidden-roles.json +65 -0
  28. package/defaults/memory-chunk-prompt.md +63 -0
  29. package/defaults/memory-promote-prompt.md +135 -0
  30. package/defaults/mixdog-config.template.json +27 -0
  31. package/defaults/user-workflow.json +8 -0
  32. package/defaults/user-workflow.md +12 -0
  33. package/hooks/hooks.json +73 -0
  34. package/hooks/lib/active-instance.cjs +77 -0
  35. package/hooks/lib/permission-evaluator.cjs +411 -0
  36. package/hooks/lib/permission-route.cjs +63 -0
  37. package/hooks/lib/permission-rules.cjs +170 -0
  38. package/hooks/lib/settings-loader.cjs +116 -0
  39. package/hooks/post-tool-use.cjs +84 -0
  40. package/hooks/pre-mcp-sandbox.cjs +158 -0
  41. package/hooks/pre-tool-subagent.cjs +253 -0
  42. package/hooks/session-start.cjs +1372 -0
  43. package/hooks/turn-timer.cjs +82 -0
  44. package/lib/claude-md-writer.cjs +386 -0
  45. package/lib/config-cjs.cjs +61 -0
  46. package/lib/hook-pipe-path.cjs +10 -0
  47. package/lib/keychain-cjs.cjs +263 -0
  48. package/lib/plugin-paths.cjs +61 -0
  49. package/lib/rules-builder.cjs +241 -0
  50. package/lib/text-utils.cjs +61 -0
  51. package/native/README.md +117 -0
  52. package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
  53. package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
  54. package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
  55. package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
  56. package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
  57. package/package.json +107 -0
  58. package/prompts/code-review.txt +16 -0
  59. package/prompts/security-audit.txt +17 -0
  60. package/rules/bridge/00-common.md +39 -0
  61. package/rules/bridge/20-skip-protocol.md +18 -0
  62. package/rules/bridge/30-explorer.md +33 -0
  63. package/rules/bridge/40-cycle1-agent.md +52 -0
  64. package/rules/bridge/41-cycle2-agent.md +62 -0
  65. package/rules/bridge/42-cycle3-agent.md +44 -0
  66. package/rules/lead/00-tool-lead.md +61 -0
  67. package/rules/lead/01-general.md +23 -0
  68. package/rules/lead/02-channels.md +49 -0
  69. package/rules/lead/03-team.md +27 -0
  70. package/rules/lead/04-workflow.md +20 -0
  71. package/rules/shared/00-language.md +14 -0
  72. package/rules/shared/01-tool.md +138 -0
  73. package/scripts/bootstrap.mjs +184 -0
  74. package/scripts/bridge-unify-smoke.mjs +308 -0
  75. package/scripts/build-runtime-linux.sh +348 -0
  76. package/scripts/build-runtime-macos.sh +217 -0
  77. package/scripts/build-runtime-windows.ps1 +242 -0
  78. package/scripts/builtin-utils-smoke.mjs +392 -0
  79. package/scripts/check-json.mjs +45 -0
  80. package/scripts/check-syntax-changed.mjs +102 -0
  81. package/scripts/check-syntax.mjs +58 -0
  82. package/scripts/code-graph-batch.test.mjs +33 -0
  83. package/scripts/config-preserve-smoke.mjs +180 -0
  84. package/scripts/doctor.mjs +484 -0
  85. package/scripts/edit-normalize-fuzz.mjs +130 -0
  86. package/scripts/edit-normalize-smoke.mjs +401 -0
  87. package/scripts/edit-operation-smoke.mjs +369 -0
  88. package/scripts/edit2-smoke.mjs +63 -0
  89. package/scripts/fuzzy-e2e.mjs +28 -0
  90. package/scripts/fuzzy-smoke.mjs +26 -0
  91. package/scripts/generate-runtime-manifest.mjs +166 -0
  92. package/scripts/guard-smoke.mjs +66 -0
  93. package/scripts/hidden-role-schema-smoke.mjs +162 -0
  94. package/scripts/hook-routing-smoke.mjs +29 -0
  95. package/scripts/inject-input.ps1 +204 -0
  96. package/scripts/io-complex-smoke.mjs +667 -0
  97. package/scripts/io-explore-bench.mjs +424 -0
  98. package/scripts/io-guardrails-smoke.mjs +205 -0
  99. package/scripts/io-mini-bench-baseline.json +11 -0
  100. package/scripts/io-mini-bench.mjs +216 -0
  101. package/scripts/io-route-harness.mjs +933 -0
  102. package/scripts/io-telemetry-report.mjs +691 -0
  103. package/scripts/mutation-bench.mjs +564 -0
  104. package/scripts/mutation-io-smoke.mjs +1081 -0
  105. package/scripts/native-patch-bridge-smoke.mjs +288 -0
  106. package/scripts/native-patch-smoke.mjs +304 -0
  107. package/scripts/patch-interior-context-smoke.mjs +49 -0
  108. package/scripts/patch-newline-utf8-smoke.mjs +157 -0
  109. package/scripts/perf-hook-smoke.mjs +71 -0
  110. package/scripts/permission-eval-smoke.mjs +426 -0
  111. package/scripts/prep-patch.mjs +53 -0
  112. package/scripts/prep-shim.mjs +96 -0
  113. package/scripts/provider-cache-smoke.mjs +687 -0
  114. package/scripts/report-runtime-health.mjs +132 -0
  115. package/scripts/run-mcp.mjs +1547 -0
  116. package/scripts/salvage-v4a-shatter.test.mjs +58 -0
  117. package/scripts/scoped-cache-io-smoke.mjs +103 -0
  118. package/scripts/shell-policy-round3-smoke.mjs +46 -0
  119. package/scripts/smoke-runtime-negative.ps1 +100 -0
  120. package/scripts/smoke-runtime-negative.sh +95 -0
  121. package/scripts/stall-policy-smoke.mjs +50 -0
  122. package/scripts/start-memory-worker.mjs +23 -0
  123. package/scripts/statusline-launcher-smoke.mjs +82 -0
  124. package/scripts/stress-atomic-write.mjs +1028 -0
  125. package/scripts/test-config-rmw-restore.mjs +122 -0
  126. package/scripts/test-fault-inject.mjs +164 -0
  127. package/scripts/test-large-file.mjs +174 -0
  128. package/scripts/tool-edge-smoke.mjs +209 -0
  129. package/scripts/uninstall.mjs +201 -0
  130. package/scripts/webhook-selfheal-smoke.mjs +29 -0
  131. package/scripts/write-overwrite-guard-smoke.mjs +56 -0
  132. package/server-main.mjs +3055 -0
  133. package/server.mjs +468 -0
  134. package/setup/config-merge.mjs +254 -0
  135. package/setup/install.mjs +120 -0
  136. package/setup/launch-core.mjs +507 -0
  137. package/setup/launch.mjs +101 -0
  138. package/setup/setup-server.mjs +3206 -0
  139. package/setup/setup.html +3693 -0
  140. package/skills/retro-skill-proposer/SKILL.md +92 -0
  141. package/skills/schedule-add/SKILL.md +77 -0
  142. package/skills/setup/SKILL.md +346 -0
  143. package/skills/webhook-add/SKILL.md +81 -0
  144. package/src/agent/bridge-stall-watchdog.mjs +337 -0
  145. package/src/agent/index.mjs +2138 -0
  146. package/src/agent/orchestrator/activity-bus.mjs +38 -0
  147. package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
  148. package/src/agent/orchestrator/bridge-retry.mjs +220 -0
  149. package/src/agent/orchestrator/bridge-trace.mjs +583 -0
  150. package/src/agent/orchestrator/cache-mtime.mjs +58 -0
  151. package/src/agent/orchestrator/config.mjs +358 -0
  152. package/src/agent/orchestrator/context/collect.mjs +651 -0
  153. package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
  154. package/src/agent/orchestrator/drain-registry.mjs +50 -0
  155. package/src/agent/orchestrator/explore-validator.mjs +8 -0
  156. package/src/agent/orchestrator/internal-roles.mjs +118 -0
  157. package/src/agent/orchestrator/internal-tools.mjs +88 -0
  158. package/src/agent/orchestrator/jobs.mjs +116 -0
  159. package/src/agent/orchestrator/mcp/client.mjs +364 -0
  160. package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
  161. package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
  162. package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
  163. package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
  164. package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
  165. package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
  166. package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
  167. package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
  168. package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
  169. package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
  170. package/src/agent/orchestrator/providers/registry.mjs +192 -0
  171. package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
  172. package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
  173. package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
  174. package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
  175. package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
  176. package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
  177. package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
  178. package/src/agent/orchestrator/session/cache/util.mjs +49 -0
  179. package/src/agent/orchestrator/session/loop.mjs +1478 -0
  180. package/src/agent/orchestrator/session/manager.mjs +1975 -0
  181. package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
  182. package/src/agent/orchestrator/session/result-classification.mjs +65 -0
  183. package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
  184. package/src/agent/orchestrator/session/store.mjs +624 -0
  185. package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
  186. package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
  187. package/src/agent/orchestrator/session/trim.mjs +491 -0
  188. package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
  189. package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
  190. package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
  191. package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
  192. package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
  193. package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
  194. package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
  195. package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
  196. package/src/agent/orchestrator/stall-policy.mjs +195 -0
  197. package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
  198. package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
  199. package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
  200. package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
  201. package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
  202. package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
  203. package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
  204. package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
  205. package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
  206. package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
  207. package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
  208. package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
  209. package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
  210. package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
  211. package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
  212. package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
  213. package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
  214. package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
  215. package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
  216. package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
  217. package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
  218. package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
  219. package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
  220. package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
  221. package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
  222. package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
  223. package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
  224. package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
  225. package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
  226. package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
  227. package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
  228. package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
  229. package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
  230. package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
  231. package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
  232. package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
  233. package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
  234. package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
  235. package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
  236. package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
  237. package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
  238. package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
  239. package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
  240. package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
  241. package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
  242. package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
  243. package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
  244. package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
  245. package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
  246. package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
  247. package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
  248. package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
  249. package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
  250. package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
  251. package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
  252. package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
  253. package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
  254. package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
  255. package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
  256. package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
  257. package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
  258. package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
  259. package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
  260. package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
  261. package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
  262. package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
  263. package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
  264. package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
  265. package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
  266. package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
  267. package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
  268. package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
  269. package/src/agent/orchestrator/tools/builtin.mjs +500 -0
  270. package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
  271. package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
  272. package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
  273. package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
  274. package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
  275. package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
  276. package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
  277. package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
  278. package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
  279. package/src/agent/orchestrator/tools/host-input.mjs +204 -0
  280. package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
  281. package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
  282. package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
  283. package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
  284. package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
  285. package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
  286. package/src/agent/orchestrator/tools/patch.mjs +2754 -0
  287. package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
  288. package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
  289. package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
  290. package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
  291. package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
  292. package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
  293. package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
  294. package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
  295. package/src/agent/orchestrator/workflow-store.mjs +93 -0
  296. package/src/agent/tool-defs.mjs +103 -0
  297. package/src/channels/backends/discord.mjs +784 -0
  298. package/src/channels/data/voice-runtime-manifest.json +138 -0
  299. package/src/channels/index.mjs +3229 -0
  300. package/src/channels/lib/cli-worker-host.mjs +12 -0
  301. package/src/channels/lib/config-lock.mjs +13 -0
  302. package/src/channels/lib/config.mjs +292 -0
  303. package/src/channels/lib/drop-trace.mjs +71 -0
  304. package/src/channels/lib/event-pipeline.mjs +81 -0
  305. package/src/channels/lib/event-queue.mjs +345 -0
  306. package/src/channels/lib/executor.mjs +168 -0
  307. package/src/channels/lib/format.mjs +188 -0
  308. package/src/channels/lib/holidays.mjs +138 -0
  309. package/src/channels/lib/hook-pipe-server.mjs +802 -0
  310. package/src/channels/lib/interaction-workflows.mjs +184 -0
  311. package/src/channels/lib/memory-client.mjs +149 -0
  312. package/src/channels/lib/output-forwarder.mjs +765 -0
  313. package/src/channels/lib/runtime-paths.mjs +479 -0
  314. package/src/channels/lib/scheduler.mjs +723 -0
  315. package/src/channels/lib/session-control.mjs +36 -0
  316. package/src/channels/lib/session-discovery.mjs +103 -0
  317. package/src/channels/lib/settings.mjs +11 -0
  318. package/src/channels/lib/state-file.mjs +68 -0
  319. package/src/channels/lib/status-snapshot.mjs +219 -0
  320. package/src/channels/lib/tool-format.mjs +140 -0
  321. package/src/channels/lib/transcript-discovery.mjs +195 -0
  322. package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
  323. package/src/channels/lib/webhook.mjs +1179 -0
  324. package/src/channels/lib/whisper-server.mjs +477 -0
  325. package/src/channels/tool-defs.mjs +170 -0
  326. package/src/daemon/host.mjs +118 -0
  327. package/src/daemon/mcp-transport.mjs +47 -0
  328. package/src/daemon/session.mjs +100 -0
  329. package/src/daemon/thin-client.mjs +71 -0
  330. package/src/daemon/transport.mjs +163 -0
  331. package/src/memory/data/runtime-manifest.json +40 -0
  332. package/src/memory/index.mjs +3305 -0
  333. package/src/memory/lib/agent-ipc.mjs +93 -0
  334. package/src/memory/lib/bridge-trace-queries.mjs +120 -0
  335. package/src/memory/lib/core-memory-store.mjs +330 -0
  336. package/src/memory/lib/embedding-provider.mjs +269 -0
  337. package/src/memory/lib/embedding-worker.mjs +323 -0
  338. package/src/memory/lib/llm-worker-host.mjs +17 -0
  339. package/src/memory/lib/memory-cycle.mjs +11 -0
  340. package/src/memory/lib/memory-cycle1.mjs +641 -0
  341. package/src/memory/lib/memory-cycle2.mjs +1284 -0
  342. package/src/memory/lib/memory-cycle3.mjs +540 -0
  343. package/src/memory/lib/memory-embed.mjs +299 -0
  344. package/src/memory/lib/memory-extraction.mjs +5 -0
  345. package/src/memory/lib/memory-maintenance-store.mjs +32 -0
  346. package/src/memory/lib/memory-ops-policy.mjs +190 -0
  347. package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
  348. package/src/memory/lib/memory-recall-read-query.mjs +7 -0
  349. package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
  350. package/src/memory/lib/memory-recall-store.mjs +621 -0
  351. package/src/memory/lib/memory-retrievers.mjs +112 -0
  352. package/src/memory/lib/memory-score.mjs +71 -0
  353. package/src/memory/lib/memory-text-utils.mjs +58 -0
  354. package/src/memory/lib/memory.mjs +412 -0
  355. package/src/memory/lib/model-profile.mjs +85 -0
  356. package/src/memory/lib/pg/adapter.mjs +308 -0
  357. package/src/memory/lib/pg/process.mjs +360 -0
  358. package/src/memory/lib/pg/supervisor.mjs +396 -0
  359. package/src/memory/lib/project-id-resolver.mjs +86 -0
  360. package/src/memory/lib/runtime-fetcher.mjs +442 -0
  361. package/src/memory/lib/trace-store.mjs +728 -0
  362. package/src/memory/tool-defs.mjs +79 -0
  363. package/src/search/index.mjs +1173 -0
  364. package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
  365. package/src/search/lib/backends/exa.mjs +50 -0
  366. package/src/search/lib/backends/firecrawl.mjs +61 -0
  367. package/src/search/lib/backends/gemini-api.mjs +83 -0
  368. package/src/search/lib/backends/grok-oauth.mjs +86 -0
  369. package/src/search/lib/backends/index.mjs +150 -0
  370. package/src/search/lib/backends/openai-api.mjs +144 -0
  371. package/src/search/lib/backends/openai-oauth.mjs +98 -0
  372. package/src/search/lib/backends/openai-web-search.mjs +76 -0
  373. package/src/search/lib/backends/tavily.mjs +55 -0
  374. package/src/search/lib/backends/xai-api.mjs +113 -0
  375. package/src/search/lib/cache.mjs +131 -0
  376. package/src/search/lib/config.mjs +192 -0
  377. package/src/search/lib/formatter.mjs +115 -0
  378. package/src/search/lib/provider-usage.mjs +67 -0
  379. package/src/search/lib/providers.mjs +47 -0
  380. package/src/search/lib/search-intent.mjs +109 -0
  381. package/src/search/lib/setup-handler.mjs +261 -0
  382. package/src/search/lib/state.mjs +201 -0
  383. package/src/search/lib/web-tools.mjs +1207 -0
  384. package/src/search/tool-defs.mjs +83 -0
  385. package/src/setup/defender-exclusion.mjs +183 -0
  386. package/src/shared/abort-controller.mjs +15 -0
  387. package/src/shared/atomic-file.mjs +420 -0
  388. package/src/shared/config.mjs +350 -0
  389. package/src/shared/daemon-recycle.mjs +108 -0
  390. package/src/shared/disable-claude-builtins.mjs +88 -0
  391. package/src/shared/err-text.mjs +12 -0
  392. package/src/shared/llm/cost.mjs +66 -0
  393. package/src/shared/llm/http-agent.mjs +123 -0
  394. package/src/shared/llm/index.mjs +41 -0
  395. package/src/shared/llm/pid-cleanup.mjs +27 -0
  396. package/src/shared/llm/usage-log.mjs +47 -0
  397. package/src/shared/plugin-paths.mjs +58 -0
  398. package/src/shared/schedules-store.mjs +70 -0
  399. package/src/shared/seed.mjs +119 -0
  400. package/src/shared/user-cwd.mjs +213 -0
  401. package/src/shared/user-data-guard.mjs +238 -0
  402. package/src/status/aggregator.mjs +584 -0
  403. package/src/status/server.mjs +413 -0
  404. package/tools.json +1653 -0
@@ -0,0 +1,413 @@
1
+ /**
2
+ * Status HTTP server (forked child process).
3
+ *
4
+ * A tiny loopback-only HTTP server that exposes GET /bridge/status for
5
+ * statusline consumers. Lives in its OWN process (forked by server.mjs
6
+ * at boot) so bursty MCP tool activity in the parent can't starve the
7
+ * statusline's short 1-second curl timeout.
8
+ *
9
+ * Binds to an ephemeral port on 127.0.0.1 and advertises the port via
10
+ * ~/.claude/mixdog-status.json so bin/statusline.sh can discover it
11
+ * without hard-coded port numbers.
12
+ *
13
+ * Can be used two ways:
14
+ * 1. Imported: `startStatusServer({ dataDir, advertisePath, log })` —
15
+ * kept as a named export for tests / ad-hoc in-process hosting.
16
+ * 2. Spawned directly via `fork('src/status/server.mjs')` — config is
17
+ * read from MIXDOG_STATUS_DATA_DIR / MIXDOG_STATUS_ADVERTISE_PATH
18
+ * env vars. The parent kills this process at shutdown.
19
+ */
20
+
21
+ import http from 'http';
22
+ import { unlinkSync, mkdirSync, existsSync } from 'fs';
23
+ import { readdir as readdirAsync, readFile as readFileAsync, unlink as unlinkAsync } from 'fs/promises';
24
+ import { basename, dirname, join } from 'path';
25
+ import { pathToFileURL } from 'url';
26
+ import { buildBridgeStatus, buildBridgeStatusCompact, renderBridgeStatusText } from './aggregator.mjs';
27
+ import { writeJsonAtomicSync } from '../shared/atomic-file.mjs';
28
+
29
+ const OWNER_SESSION_ID = process.env.MIXDOG_OWNER_SESSION_ID || null;
30
+ const OWNER_LEAD_PID = (() => {
31
+ const pid = Number(process.env.MIXDOG_OWNER_LEAD_PID);
32
+ return Number.isFinite(pid) && pid > 0 ? pid : null;
33
+ })();
34
+ const OWNER_HOST_PID = (() => {
35
+ const pid = Number(process.env.MIXDOG_OWNER_HOST_PID);
36
+ return Number.isFinite(pid) && pid > 0 ? pid : null;
37
+ })();
38
+
39
+ // Stale-session sweep. saveSession() never unlinks closed sessions, so the
40
+ // sessions/ directory grows without bound for the lifetime of the install.
41
+ // The aggregator scan cost is O(N) per statusline poll; once N reaches the
42
+ // hundreds, the scan saturates a CPU core. Closed sessions older than 30 min
43
+ // have no consumer (active filter uses .hb heartbeats; recent-completed
44
+ // window is 30 min) so removing them is safe.
45
+ const SESSION_SWEEP_INTERVAL_MS = 60 * 60 * 1000;
46
+ const SESSION_STALE_MS = 30 * 60 * 1000;
47
+
48
+ // Shared sweep helper. Both stale-session and stale-advert sweepers share
49
+ // the same shape: readdir the directory, fan out per-file work via
50
+ // Promise.all (parallel scan keeps a large dir from blocking the first
51
+ // status response after boot), swallow per-file errors so one corrupt
52
+ // file does not abort the batch, count removals, log only when >0.
53
+ // `perFile` returns 1 when a file was removed, 0 otherwise.
54
+ async function sweepDir(dir, perFile, log, message) {
55
+ if (!existsSync(dir)) return;
56
+ let names;
57
+ try { names = await readdirAsync(dir); } catch { return; }
58
+ const results = await Promise.all(names.map((f) => perFile(f)));
59
+ const removed = results.reduce((a, b) => a + b, 0);
60
+ if (removed > 0) log(`[status-server] sweep: removed ${removed} ${message}`);
61
+ }
62
+
63
+ async function sweepStaleSessions(sessionsDir, log) {
64
+ const now = Date.now();
65
+ await sweepDir(sessionsDir, async (f) => {
66
+ if (!f.endsWith('.json')) return 0;
67
+ const full = join(sessionsDir, f);
68
+ try {
69
+ const j = JSON.parse(await readFileAsync(full, 'utf-8'));
70
+ // Anchor stale-age on the terminal updatedAt (or closedAt), not
71
+ // createdAt: a long-lived bridge session that closed just before a
72
+ // sweep would otherwise be removed inside the aggregator's 30 min
73
+ // recent-completed window.
74
+ const terminalAt = Number.isFinite(j.updatedAt) ? j.updatedAt
75
+ : (Number.isFinite(j.closedAt) ? j.closedAt : null);
76
+ if (j.closed === true && terminalAt !== null && (now - terminalAt) > SESSION_STALE_MS) {
77
+ await unlinkAsync(full);
78
+ try { await unlinkAsync(full.replace(/\.json$/, '.hb')); } catch {}
79
+ return 1;
80
+ }
81
+ } catch { /* skip corrupt */ }
82
+ return 0;
83
+ }, log, 'stale closed sessions');
84
+ }
85
+
86
+ // Advert directory sweep. Each forked status-server writes its own
87
+ // {session_id}.json into mixdog-status/. Crashes / SIGKILL / abrupt
88
+ // supervisor restarts can leave files behind whose pid is no longer
89
+ // alive. statusline.sh greps every advert each tick for cc_session_id
90
+ // pairing, so accumulation linearly slows every refresh. Invariant: a
91
+ // live owner is the only writer of its advert, so a pid whose process
92
+ // is gone proves the advert is dead. process.kill(pid, 0) returns ESRCH
93
+ // for non-existent pids on POSIX and Windows alike.
94
+ async function sweepStaleAdverts(advertisePath, log) {
95
+ const dir = dirname(advertisePath);
96
+ await sweepDir(dir, async (f) => {
97
+ const full = join(dir, f);
98
+ if (full === advertisePath) return 0;
99
+ // .cc-<sessionId>.path mapping files: orphan when the referenced
100
+ // advert is gone. Invariant: mapping body is the advert path, so a
101
+ // missing target proves the mapping is stale and safe to remove.
102
+ if (f.startsWith('.cc-') && f.endsWith('.path')) {
103
+ try {
104
+ const target = (await readFileAsync(full, 'utf-8')).trim();
105
+ if (target && !existsSync(target)) { await unlinkAsync(full); return 1; }
106
+ } catch { /* skip */ }
107
+ return 0;
108
+ }
109
+ if (!f.endsWith('.json')) return 0;
110
+ try {
111
+ const j = JSON.parse(await readFileAsync(full, 'utf-8'));
112
+ const pid = Number(j.pid);
113
+ if (!Number.isFinite(pid) || pid <= 0) return 0;
114
+ try { process.kill(pid, 0); return 0; }
115
+ catch (e) {
116
+ if (e.code === 'ESRCH') { await unlinkAsync(full); return 1; }
117
+ }
118
+ } catch { /* skip corrupt */ }
119
+ return 0;
120
+ }, log, 'dead advert/mapping files');
121
+ }
122
+
123
+ function writeAdvertisement(path, record) {
124
+ mkdirSync(dirname(path), { recursive: true });
125
+ writeJsonAtomicSync(path, record, { lock: true });
126
+ }
127
+
128
+ function sanitizeCcId(ccId) {
129
+ return String(ccId).replace(/[^a-zA-Z0-9-]/g, '-');
130
+ }
131
+
132
+ function perCcAdvertPath(beaconPath, ccId) {
133
+ const dir = dirname(beaconPath);
134
+ const base = basename(beaconPath, '.json');
135
+ return join(dir, `${base}-cc-${sanitizeCcId(ccId)}.json`);
136
+ }
137
+
138
+ function beaconRecord(record) {
139
+ if (!record || typeof record !== 'object') return record;
140
+ const { cc_session_id: _drop, ...rest } = record;
141
+ return rest;
142
+ }
143
+
144
+ function claimAdvertPath(claim) {
145
+ if (typeof claim === 'string' && claim) return claim;
146
+ const p = claim?.path;
147
+ return typeof p === 'string' && p ? p : null;
148
+ }
149
+
150
+ function claimClientHostPid(claim) {
151
+ const pid = Number(claim?.clientHostPid);
152
+ return Number.isFinite(pid) && pid > 0 ? pid : 0;
153
+ }
154
+
155
+ const recapState = { state: 'idle', running: false, startedAt: null, lastCompletedAt: null, updatedAt: null, errorMessage: null };
156
+
157
+ function normalizeTimestamp(value) {
158
+ if (value === null || value === undefined || value === '') return null;
159
+ const n = Number(value);
160
+ return Number.isFinite(n) ? n : null;
161
+ }
162
+
163
+ function updateRecapState(next = {}) {
164
+ if (typeof next.state === 'string') recapState.state = next.state;
165
+ recapState.running = next.running === true;
166
+ recapState.startedAt = normalizeTimestamp(next.startedAt);
167
+ recapState.lastCompletedAt = normalizeTimestamp(next.lastCompletedAt);
168
+ recapState.updatedAt = normalizeTimestamp(next.updatedAt);
169
+ recapState.errorMessage = typeof next.errorMessage === 'string' ? next.errorMessage : null;
170
+ }
171
+
172
+ function readRecapState() {
173
+ return { ...recapState };
174
+ }
175
+
176
+ export async function startStatusServer({ dataDir, advertisePath, log = () => {}, getRecapState = readRecapState, ownerSessionId = OWNER_SESSION_ID }) {
177
+ // Multi-terminal claim: advertisePath is a permanent unclaimed BEACON
178
+ // (never cc_session_id). Each CC POSTs /register-cc-session and gets a
179
+ // dedicated advert file with cc_session_id set; statusline matches or
180
+ // claims via the beacon per bin/statusline-lib.mjs.
181
+ const claimedCcById = new Map(); // ccId -> { path, clientHostPid }
182
+ const recordRef = { current: null }; // populated after listen() so advert helper can re-emit
183
+ function rewriteAdvert() {
184
+ if (!recordRef.current) return;
185
+ const beacon = beaconRecord(recordRef.current);
186
+ try { writeAdvertisement(advertisePath, beacon); }
187
+ catch (e) { log(`[status-server] advert rewrite failed: ${e.message}`); }
188
+ for (const [ccId, claim] of claimedCcById) {
189
+ const perPath = claimAdvertPath(claim);
190
+ const clientHostPid = claimClientHostPid(claim);
191
+ if (!perPath) continue;
192
+ try {
193
+ writeAdvertisement(perPath, {
194
+ ...beacon,
195
+ cc_session_id: ccId,
196
+ ...(clientHostPid > 0 ? { clientHostPid } : {}),
197
+ });
198
+ } catch (e) {
199
+ log(`[status-server] per-cc advert rewrite failed (${ccId}): ${e.message}`);
200
+ }
201
+ }
202
+ }
203
+ const server = http.createServer(async (req, res) => {
204
+ // Loopback-only: reject non-localhost peers (belt-and-braces —
205
+ // binding to 127.0.0.1 should already exclude them).
206
+ const remote = req.socket?.remoteAddress || '';
207
+ const isLoopback = remote === '127.0.0.1' || remote === '::1' || remote === '::ffff:127.0.0.1';
208
+ if (!isLoopback) {
209
+ res.writeHead(403, { 'Content-Type': 'application/json' });
210
+ res.end(JSON.stringify({ ok: false, error: 'forbidden' }));
211
+ return;
212
+ }
213
+
214
+ try {
215
+ const url = new URL(req.url || '/', 'http://localhost');
216
+ if (req.method === 'GET' && url.pathname === '/bridge/status') {
217
+ const fmt = url.searchParams.get('format');
218
+ const wantText = fmt === 'text'
219
+ || (req.headers['accept'] || '').includes('text/plain');
220
+ const wantCompact = fmt === 'statusline-json';
221
+ const filterClientHostPid = Number(url.searchParams.get('clientHostPid')) || undefined;
222
+ const payload = wantCompact
223
+ ? await buildBridgeStatusCompact(dataDir, { ownerSessionId, clientHostPid: filterClientHostPid, ownerHostPid: OWNER_HOST_PID })
224
+ : await buildBridgeStatus(dataDir, { recap: getRecapState(), ownerSessionId, clientHostPid: filterClientHostPid, ownerHostPid: OWNER_HOST_PID });
225
+ if (wantText) {
226
+ res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
227
+ res.end(renderBridgeStatusText(payload));
228
+ } else {
229
+ res.writeHead(200, { 'Content-Type': 'application/json' });
230
+ res.end(JSON.stringify(payload));
231
+ }
232
+ return;
233
+ }
234
+ if (req.method === 'POST' && url.pathname === '/register-cc-session') {
235
+ let body = '';
236
+ // Cap the accumulated body at 16 KiB. The expected payload is a
237
+ // tiny JSON object ({cc_session_id, client_host_pid}); anything
238
+ // larger is either malformed or a memory-exhaustion attempt
239
+ // against the loopback endpoint. Aborting closes the socket and
240
+ // skips the JSON.parse pass.
241
+ const MAX_BODY_BYTES = 16 * 1024;
242
+ let bodyOverflow = false;
243
+ req.on('data', d => {
244
+ if (bodyOverflow) return;
245
+ body += d;
246
+ if (body.length > MAX_BODY_BYTES) {
247
+ bodyOverflow = true;
248
+ res.writeHead(413, { 'Content-Type': 'application/json' });
249
+ res.end(JSON.stringify({ ok: false, error: 'payload too large' }));
250
+ req.destroy();
251
+ }
252
+ });
253
+ req.on('end', () => {
254
+ if (bodyOverflow) return;
255
+ let parsed = null;
256
+ try { parsed = JSON.parse(body); } catch { /* fall through */ }
257
+ const ccId = String(parsed?.cc_session_id ?? '').trim();
258
+ if (!ccId) {
259
+ res.writeHead(400, { 'Content-Type': 'application/json' });
260
+ res.end(JSON.stringify({ ok: false, error: 'cc_session_id required' }));
261
+ return;
262
+ }
263
+ const rawClientHostPid = Number(parsed?.client_host_pid ?? parsed?.clientHostPid ?? 0);
264
+ const clientHostPid = Number.isFinite(rawClientHostPid) && rawClientHostPid > 0 ? rawClientHostPid : 0;
265
+ const perPath = perCcAdvertPath(advertisePath, ccId);
266
+ const existing = claimedCcById.get(ccId);
267
+ const existingPath = claimAdvertPath(existing);
268
+ const existingHostPid = claimClientHostPid(existing);
269
+ if (existingPath && existingPath !== perPath) {
270
+ res.writeHead(409, { 'Content-Type': 'application/json' });
271
+ res.end(JSON.stringify({ ok: false, claimed: ccId, ownerHostPid: OWNER_HOST_PID, clientHostPid: existingHostPid || null }));
272
+ return;
273
+ }
274
+ claimedCcById.set(ccId, { path: perPath, clientHostPid });
275
+ rewriteAdvert();
276
+ res.writeHead(200, { 'Content-Type': 'application/json' });
277
+ res.end(JSON.stringify({ ok: true, claimed: ccId, ownerHostPid: OWNER_HOST_PID, clientHostPid: clientHostPid || null }));
278
+ });
279
+ return;
280
+ }
281
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
282
+ res.end('not found');
283
+ } catch (e) {
284
+ res.writeHead(500, { 'Content-Type': 'application/json' });
285
+ res.end(JSON.stringify({ error: String(e?.message || e) }));
286
+ }
287
+ });
288
+
289
+ await new Promise((resolve, reject) => {
290
+ server.once('error', reject);
291
+ server.listen(0, '127.0.0.1', () => {
292
+ server.removeListener('error', reject);
293
+ resolve();
294
+ });
295
+ });
296
+
297
+ const address = server.address();
298
+ const port = typeof address === 'object' && address ? address.port : 0;
299
+
300
+ const record = {
301
+ pid: process.pid,
302
+ port,
303
+ startedAt: Date.now(),
304
+ ...ownerSessionId ? { ownerSessionId } : {},
305
+ ...OWNER_LEAD_PID ? { ownerLeadPid: OWNER_LEAD_PID } : {},
306
+ ...OWNER_HOST_PID ? { ownerHostPid: OWNER_HOST_PID } : {},
307
+ };
308
+ recordRef.current = record; // expose to /register-cc-session handler for in-place cc_session_id append
309
+ try { writeAdvertisement(advertisePath, beaconRecord(record)); }
310
+ catch (e) { log(`[status-server] advertise write failed: ${e.message}`); }
311
+
312
+ log(`[status-server] listening on 127.0.0.1:${port} (advertise=${advertisePath})`);
313
+
314
+ // Self-heal: external cleanup (antivirus, indexer, user tools) can unlink
315
+ // the advert file while the child stays alive. Statusline then logs
316
+ // NOBRIDGE despite a healthy server. Re-write the advert every 30s if
317
+ // missing so any external removal recovers within one tick.
318
+ const advertHeal = setInterval(() => {
319
+ if (!recordRef.current) return;
320
+ const beacon = beaconRecord(recordRef.current);
321
+ if (!existsSync(advertisePath)) {
322
+ try {
323
+ writeAdvertisement(advertisePath, beacon);
324
+ log(`[status-server] advert restored at ${advertisePath}`);
325
+ } catch (e) {
326
+ log(`[status-server] advert restore failed: ${e.message}`);
327
+ }
328
+ }
329
+ for (const [ccId, claim] of claimedCcById) {
330
+ const perPath = claimAdvertPath(claim);
331
+ const clientHostPid = claimClientHostPid(claim);
332
+ if (!perPath) continue;
333
+ if (!existsSync(perPath)) {
334
+ try {
335
+ writeAdvertisement(perPath, {
336
+ ...beacon,
337
+ cc_session_id: ccId,
338
+ ...(clientHostPid > 0 ? { clientHostPid } : {}),
339
+ });
340
+ log(`[status-server] per-cc advert restored at ${perPath}`);
341
+ } catch (e) {
342
+ log(`[status-server] per-cc advert restore failed (${ccId}): ${e.message}`);
343
+ }
344
+ }
345
+ }
346
+ }, 30000);
347
+ advertHeal.unref?.();
348
+
349
+ sweepStaleAdverts(advertisePath, log);
350
+ const advertSweep = setInterval(() => sweepStaleAdverts(advertisePath, log), SESSION_SWEEP_INTERVAL_MS);
351
+ advertSweep.unref?.();
352
+
353
+ const sessionsDir = join(dataDir, 'sessions');
354
+ sweepStaleSessions(sessionsDir, log);
355
+ const sessionSweep = setInterval(() => sweepStaleSessions(sessionsDir, log), SESSION_SWEEP_INTERVAL_MS);
356
+ sessionSweep.unref?.();
357
+
358
+ return {
359
+ port,
360
+ rewriteAdvert,
361
+ close: () => new Promise((resolve) => {
362
+ clearInterval(advertHeal);
363
+ clearInterval(advertSweep);
364
+ clearInterval(sessionSweep);
365
+ try { unlinkSync(advertisePath); } catch {}
366
+ for (const claim of claimedCcById.values()) {
367
+ const perPath = claimAdvertPath(claim);
368
+ if (!perPath) continue;
369
+ try { unlinkSync(perPath); } catch {}
370
+ }
371
+ server.close(() => resolve());
372
+ }),
373
+ };
374
+ }
375
+
376
+ // ── Child-process entry point ─────────────────────────────────────────
377
+ // When invoked directly (`node src/status/server.mjs`), read config
378
+ // from env and boot. Parent signals shutdown by disconnecting IPC or
379
+ // sending SIGTERM; either way we tear down the server and exit.
380
+ const isMain = import.meta.url === pathToFileURL(process.argv[1] || '').href;
381
+ if (isMain) {
382
+ const dataDir = process.env.MIXDOG_STATUS_DATA_DIR;
383
+ const advertisePath = process.env.MIXDOG_STATUS_ADVERTISE_PATH;
384
+ if (!dataDir || !advertisePath) {
385
+ process.stderr.write('[status-server] missing MIXDOG_STATUS_DATA_DIR or MIXDOG_STATUS_ADVERTISE_PATH\n');
386
+ process.exit(2);
387
+ }
388
+ const log = (m) => process.stdout.write(m + '\n');
389
+ let handle = null;
390
+ let shuttingDown = false;
391
+ process.on('message', (msg) => {
392
+ if (msg?.type === 'recap_status') {
393
+ updateRecapState(msg.recap);
394
+ try { handle?.rewriteAdvert?.(); } catch {}
395
+ }
396
+ });
397
+ const shutdown = async (reason) => {
398
+ if (shuttingDown) return;
399
+ shuttingDown = true;
400
+ log(`[status-server] shutdown (${reason})`);
401
+ try { if (handle) await handle.close(); } catch {}
402
+ process.exit(0);
403
+ };
404
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
405
+ process.on('SIGINT', () => shutdown('SIGINT'));
406
+ process.on('disconnect', () => shutdown('parent-disconnect'));
407
+ try {
408
+ handle = await startStatusServer({ dataDir, advertisePath, log, ownerSessionId: OWNER_SESSION_ID });
409
+ } catch (e) {
410
+ process.stderr.write(`[status-server] failed to start: ${e && (e.stack || e.message) || e}\n`);
411
+ process.exit(1);
412
+ }
413
+ }