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
package/server.mjs ADDED
@@ -0,0 +1,468 @@
1
+ #!/usr/bin/env bun
2
+ // Thin entry. A node:-builtin-only prelude registers active-instance.json
3
+ // as early as possible, then dynamically imports server-main.mjs.
4
+ //
5
+ // ES module import hoisting causes statically-imported heavy modules to
6
+ // push the prelude back. Dynamic import is the only way to isolate it.
7
+ import { createServer } from 'node:http'
8
+ import * as fs from 'node:fs'
9
+ import * as path from 'node:path'
10
+ import * as os from 'node:os'
11
+
12
+ process.stderr.write(`[boot-time] tag=server-prelude-entry tMs=${Date.now()}\n`)
13
+
14
+ // ── Daemon model fork ───────────────────────────────────────────────────────
15
+ // Only the `--daemon` process runs the heavy backend (server-main → serveDaemon,
16
+ // booting memory/channels/PG ONCE per data dir). Every other invocation is a
17
+ // THIN CLIENT that bridges its stdio (from run-mcp) to the shared daemon over a
18
+ // pipe. When the daemon connection drops, the thin client exits and run-mcp
19
+ // respawns it (re-attaching or re-spawning the daemon), reusing run-mcp's
20
+ // initialize cache/replay. Replaces the per-terminal in-process server +
21
+ // fork-proxy/election model (legacy deleted post-cutover).
22
+ const __DAEMON_MODE = process.argv.includes('--daemon') || process.env.MIXDOG_DAEMON_MODE === '1'
23
+ if (!__DAEMON_MODE) {
24
+ const __dataDir = process.env.CLAUDE_PLUGIN_DATA
25
+ || path.join(os.homedir(), '.claude', 'plugins', 'data', 'mixdog-trib-plugin')
26
+ const { runThinClient } = await import('./src/daemon/thin-client.mjs')
27
+ await runThinClient(__dataDir)
28
+ process.exit(0) // the bridge keeps us alive; if it ever returns, never run the daemon prelude
29
+ }
30
+
31
+ // ── Resolve PLUGIN_DATA (same rule as lib/plugin-paths.cjs: env wins) ─
32
+ const PLUGIN_DATA = process.env.CLAUDE_PLUGIN_DATA
33
+ || path.join(os.homedir(), '.claude', 'plugins', 'data', 'mixdog-trib-plugin')
34
+ fs.mkdirSync(PLUGIN_DATA, { recursive: true })
35
+ const __parsePositivePid = (value) => {
36
+ const pid = Number(value)
37
+ return Number.isFinite(pid) && pid > 0 ? pid : null
38
+ }
39
+ const __ownerLeadPid = __parsePositivePid(process.env.MIXDOG_SUPERVISOR_PID) ?? process.pid
40
+ const __runtimeRoot = process.env.MIXDOG_RUNTIME_ROOT
41
+ ? path.resolve(process.env.MIXDOG_RUNTIME_ROOT)
42
+ : path.join(os.tmpdir(), 'mixdog')
43
+
44
+ // ── Singleton lock (hoisted from server.mjs:46~74) ──────────────────
45
+ const LOCK_PATH = path.join(PLUGIN_DATA, 'server.lock')
46
+ function _isPidAlive(pid) {
47
+ if (!pid || pid === process.pid) return false
48
+ try { process.kill(pid, 0); return true }
49
+ catch (err) { return err?.code === 'EPERM' }
50
+ }
51
+ // Atomic lock acquisition: open with 'wx' (exclusive create) so two racing
52
+ // processes cannot both believe they won. If the file already exists, 'wx'
53
+ // throws EEXIST — read the PID, decide if the owner is still alive, and
54
+ // either retry (stale) or exit (live).
55
+ let _lockFd = null
56
+ function _acquireLock() {
57
+ // Multi-instance escape hatch. When MIXDOG_MULTI_INSTANCE=1, multiple
58
+ // server.mjs processes can coexist in the same data dir — each one is its
59
+ // own CC stdio MCP server, and downstream singletons (PG, memory worker
60
+ // via fork-proxy mode, channels via proxyMode, status server via
61
+ // per-instance advert) handle the shared state. Skip the lock entirely so
62
+ // CC's plugin loader doesn't see "already running" rejections every time
63
+ // it spawns a second terminal.
64
+ if (process.env.MIXDOG_MULTI_INSTANCE === '1') return
65
+ // First try: atomic exclusive create.
66
+ try {
67
+ _lockFd = fs.openSync(LOCK_PATH, 'wx')
68
+ fs.writeSync(_lockFd, String(process.pid))
69
+ fs.closeSync(_lockFd)
70
+ _lockFd = null
71
+ return // success — fast path
72
+ } catch (err) {
73
+ if (err?.code !== 'EEXIST') {
74
+ // Unexpected error — log and abort. Continuing without a valid lock risks
75
+ // two concurrent instances sharing the same data directory.
76
+ try { if (_lockFd !== null) { fs.closeSync(_lockFd); _lockFd = null } } catch {}
77
+ process.stderr.write(`[server] failed to create server.lock (${err?.code ?? err?.message}); exiting.\n`)
78
+ process.exit(1)
79
+ }
80
+ }
81
+
82
+ // EEXIST — inspect the current owner.
83
+ const _LOCK_RETRIES = 3
84
+ const _LOCK_RETRY_MS = 50
85
+ for (let attempt = 0; attempt <= _LOCK_RETRIES; attempt++) {
86
+ let existingPid = NaN
87
+ try { existingPid = Number.parseInt(fs.readFileSync(LOCK_PATH, 'utf-8').trim(), 10) } catch {}
88
+ if (Number.isFinite(existingPid) && _isPidAlive(existingPid)) {
89
+ process.stderr.write(`[server] another mixdog instance already running (pid=${existingPid}); exiting.\n`)
90
+ process.exit(0)
91
+ }
92
+ // Stale lock — unlink then re-acquire atomically via 'wx'.
93
+ try { fs.unlinkSync(LOCK_PATH) } catch {}
94
+ try {
95
+ _lockFd = fs.openSync(LOCK_PATH, 'wx')
96
+ fs.writeSync(_lockFd, String(process.pid))
97
+ fs.closeSync(_lockFd)
98
+ _lockFd = null
99
+ return // atomic re-acquire succeeded
100
+ } catch (retryErr) {
101
+ if (retryErr?.code !== 'EEXIST') {
102
+ // Unexpected error on retry — log and abort.
103
+ try { if (_lockFd !== null) { fs.closeSync(_lockFd); _lockFd = null } } catch {}
104
+ process.stderr.write(`[server] unexpected lock error on retry (${retryErr?.code ?? retryErr?.message}); exiting.\n`)
105
+ process.exit(1)
106
+ }
107
+ // Another process beat us to 'wx' — wait and retry.
108
+ if (attempt < _LOCK_RETRIES) {
109
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, _LOCK_RETRY_MS)
110
+ }
111
+ }
112
+ }
113
+ // All retries exhausted — another process won the race.
114
+ let finalPid = NaN
115
+ try { finalPid = Number.parseInt(fs.readFileSync(LOCK_PATH, 'utf-8').trim(), 10) } catch {}
116
+ process.stderr.write(`[server] failed to acquire server.lock after ${_LOCK_RETRIES} retries (owner pid=${finalPid}); exiting.\n`)
117
+ process.exit(0)
118
+ }
119
+ _acquireLock()
120
+
121
+ // ── Process-level crash guards (mirror CC gracefulShutdown.ts:301/313) ──
122
+ // uncaughtException: best-effort append to crash log then exit(1).
123
+ // unhandledRejection: log-only; do NOT exit (avoid killing supervisor on
124
+ // transient promise rejections).
125
+ // Soft-net policy: log only. server-main.mjs installs its own
126
+ // uncaughtException listener (around line 594) that classifies recoverable
127
+ // vs fatal conditions and only exits on the fatal subset. Unconditionally
128
+ // exiting here would preempt that policy for any exception that lands
129
+ // before/while server-main loads. Real fatals (EPIPE on stdio, EADDRINUSE,
130
+ // AssertionError, ERR_INTERNAL_ASSERTION) still terminate via the
131
+ // server-main classifier or the explicit stdout/stdin guards there.
132
+ // Pre-load exception: if an exception lands BEFORE server-main has wired
133
+ // its classifier and connected the MCP transport, swallowing it would leave
134
+ // a half-initialised process that neither serves requests nor exits — the
135
+ // supervisor would never restart. server-main sets
136
+ // globalThis.__mixdogServerReady = true immediately after server.connect();
137
+ // any uncaught seen before that flag must exit(1) so the supervisor
138
+ // respawns. Once ready, fall through to soft-net (server-main classifies).
139
+ process.on('uncaughtException', (err) => {
140
+ try {
141
+ const __crashLog = path.join(__runtimeRoot, 'crash.log')
142
+ fs.appendFileSync(__crashLog, `[${new Date().toISOString()}] uncaughtException pid=${process.pid}: ${err && (err.stack || err.message || err)}\n`)
143
+ } catch {}
144
+ try { process.stderr.write(`[server.mjs] uncaughtException: ${err && (err.stack || err.message || err)}\n`) } catch {}
145
+ if (!globalThis.__mixdogServerReady) {
146
+ try { process.stderr.write(`[server.mjs] uncaughtException before server ready — exiting for supervisor restart\n`) } catch {}
147
+ process.exit(1)
148
+ }
149
+ })
150
+ // unhandledRejection is ALWAYS log-only (soft-net), even pre-ready. Many
151
+ // pre-ready code paths use fire-and-forget imports / IPC dispatch that can
152
+ // transiently reject; exiting on those would cause supervisor restart
153
+ // loops during normal boot. Only uncaughtException keeps the pre-ready
154
+ // exit(1) gate above — that path indicates a synchronous throw with no
155
+ // promise owner, which truly cannot be recovered.
156
+ process.on('unhandledRejection', (reason) => {
157
+ try {
158
+ const __crashLog = path.join(__runtimeRoot, 'crash.log')
159
+ fs.appendFileSync(__crashLog, `[${new Date().toISOString()}] unhandledRejection pid=${process.pid}: ${reason && (reason.stack || reason.message || reason)}\n`)
160
+ } catch {}
161
+ try { process.stderr.write(`[server.mjs] unhandledRejection: ${reason && (reason.stack || reason.message || reason)}\n`) } catch {}
162
+ })
163
+
164
+ const _releaseLock = () => {
165
+ try {
166
+ const raw = fs.readFileSync(LOCK_PATH, 'utf-8').trim()
167
+ if (Number.parseInt(raw, 10) === process.pid) fs.unlinkSync(LOCK_PATH)
168
+ } catch {}
169
+ // Clean up active-instance.json if we own it (graceful shutdown is
170
+ // normally handled by channels' clearActiveInstance; this guards crash / early-exit cases).
171
+ try {
172
+ const __af = path.join(__runtimeRoot, 'active-instance.json')
173
+ const cur = JSON.parse(fs.readFileSync(__af, 'utf-8'))
174
+ const curServerPid = __parsePositivePid(cur?.server_pid)
175
+ if (cur && (cur.instanceId === String(process.pid) || curServerPid === process.pid)) fs.unlinkSync(__af)
176
+ } catch {}
177
+ }
178
+ process.on('exit', _releaseLock)
179
+ // Do NOT call process.exit() directly on SIGTERM/SIGINT — that preempts the
180
+ // graceful shutdown chain in server-main.mjs (worker ACK, etc.).
181
+ // _releaseLock is registered on 'exit' above so it always runs; the signal
182
+ // handlers here just ensure the lock file is released even when server-main's
183
+ // shutdown() calls process.exit(), which itself fires 'exit'. SIGHUP/SIGBREAK
184
+ // are non-standard on Windows; keep them as exit-only guards.
185
+ for (const sig of ['SIGHUP', 'SIGBREAK']) {
186
+ try { process.on(sig, () => { _releaseLock(); process.exit(0) }) } catch {}
187
+ }
188
+ // SIGTERM and SIGINT: let server-main's handlers own graceful shutdown.
189
+ // _releaseLock fires automatically via 'exit' when server-main calls process.exit().
190
+
191
+ // ── Graceful supervisor-control shutdown (fd-3) ──────────────────
192
+ // Supervisor (run-mcp.mjs) sends "shutdown\n" or closes fd-3 to request
193
+ // graceful shutdown. fd-3 is dedicated to lifecycle control and is
194
+ // independent of MCP stdio transport — so transient stdin events from
195
+ // the MCP host can never trigger shutdown. Registered before
196
+ // server-main loads so no boot window is missed; server-main re-reads
197
+ // __mixdogSupervisorShutdownRequested when its shutdown() is wired.
198
+ const SUPERVISOR_CONTROL_FD = Number(process.env.MIXDOG_SUPERVISOR_CONTROL_FD || '0') || 0;
199
+ if (SUPERVISOR_CONTROL_FD > 0) {
200
+ try {
201
+ const ctrl = fs.createReadStream(null, { fd: SUPERVISOR_CONTROL_FD });
202
+ ctrl.setEncoding('utf8');
203
+ let ctrlBuf = '';
204
+ const fire = () => {
205
+ if (typeof globalThis.__mixdogShutdownFromSupervisor === 'function') {
206
+ globalThis.__mixdogShutdownFromSupervisor();
207
+ } else {
208
+ globalThis.__mixdogSupervisorShutdownRequested = true;
209
+ }
210
+ };
211
+ ctrl.on('data', (chunk) => {
212
+ ctrlBuf += chunk;
213
+ if (ctrlBuf.includes('shutdown')) fire();
214
+ });
215
+ ctrl.on('end', fire);
216
+ // Swallow stream errors so the supervisor stays up; control fd errors
217
+ // are non-fatal — graceful shutdown will still arrive via SIGTERM/SIGINT.
218
+ ctrl.on('error', () => {});
219
+ } catch (e) {
220
+ process.stderr.write(`[server.mjs] supervisor control fd setup failed: ${e && e.message}\n`);
221
+ }
222
+ }
223
+
224
+ // ── Preload/refresh active-instance.json when safe ──────────────────
225
+ // Same schema as channels' buildActiveInstanceState. instanceId follows
226
+ // makeInstanceId() = String(pid). (Sanitize regex /[^a-zA-Z0-9._-]/g
227
+ // is a no-op for numeric PIDs.)
228
+ const __activeDir = __runtimeRoot
229
+ fs.mkdirSync(__activeDir, { recursive: true })
230
+ const __activeFile = path.join(__activeDir, 'active-instance.json')
231
+ const __instanceId = String(process.pid)
232
+ const __multiInstanceMode = process.env.MIXDOG_MULTI_INSTANCE === '1'
233
+ const __renameRetryCodes = new Set(['EPERM', 'EBUSY', 'EACCES', 'EEXIST'])
234
+ function __sleepSync(ms) {
235
+ try { Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms) }
236
+ catch {
237
+ const deadline = Date.now() + ms
238
+ while (Date.now() < deadline) {}
239
+ }
240
+ }
241
+ function __renameSyncWithRetry(src, dest) {
242
+ const waits = [20, 50, 100, 200, 400, 800]
243
+ for (let i = 0; i <= waits.length; i++) {
244
+ try {
245
+ fs.renameSync(src, dest)
246
+ return
247
+ } catch (err) {
248
+ if (!__renameRetryCodes.has(err?.code) || i === waits.length) throw err
249
+ __sleepSync(waits[i])
250
+ }
251
+ }
252
+ }
253
+
254
+ // Split-brain guard. In single-instance mode, server.lock proves we are the
255
+ // owner and this prelude may refresh active-instance.json. In multi-instance
256
+ // mode, server.lock is intentionally skipped, so the prelude must not create
257
+ // or steal ownership; channels' ownership loop is the only writer that may
258
+ // claim a new active owner.
259
+ let __shouldWriteActiveInstance = !__multiInstanceMode
260
+ try {
261
+ if (fs.existsSync(__activeFile)) {
262
+ const prev = JSON.parse(fs.readFileSync(__activeFile, 'utf-8'))
263
+ const prevOwnerPid = __parsePositivePid(prev?.ownerLeadPid)
264
+ ?? __parsePositivePid(prev?.terminalLeadPid)
265
+ ?? __parsePositivePid(prev?.supervisor_pid)
266
+ ?? __parsePositivePid(prev?.instanceId)
267
+ let prevOwnerLive = false
268
+ if (prevOwnerPid) {
269
+ try { process.kill(prevOwnerPid, 0); prevOwnerLive = true }
270
+ catch (err) { prevOwnerLive = err?.code === 'EPERM' }
271
+ }
272
+ if (__multiInstanceMode) {
273
+ __shouldWriteActiveInstance = prevOwnerPid === __ownerLeadPid
274
+ if (!__shouldWriteActiveInstance && prevOwnerPid && prevOwnerLive) {
275
+ process.stderr.write(
276
+ `[server] active owner lead=${prevOwnerPid} is live; ` +
277
+ `starting standby lead=${__ownerLeadPid} without overwriting active-instance.json\n`
278
+ )
279
+ }
280
+ } else if (prevOwnerPid && prevOwnerPid !== __ownerLeadPid && prevOwnerLive) {
281
+ process.stderr.write(
282
+ `[server] split-brain warning: active-instance.json points to live owner PID ${prevOwnerPid} ` +
283
+ `but server.lock is ours — overwriting\n`
284
+ )
285
+ }
286
+ }
287
+ } catch {
288
+ // Malformed active-instance.json should not let a multi-instance prelude
289
+ // seize ownership. channels will treat it as unreadable and claim only if
290
+ // this terminal is actually a channel owner.
291
+ __shouldWriteActiveInstance = !__multiInstanceMode
292
+ }
293
+
294
+ const __nowMs = Date.now()
295
+ if (__shouldWriteActiveInstance) {
296
+ // Atomic write: write to .tmp then rename so readers never observe partial JSON.
297
+ // POSIX rename is atomic per directory entry; on Windows fs.renameSync uses
298
+ // MoveFileEx with REPLACE_EXISTING which is atomic-equivalent for same-volume.
299
+ const __activeTmp = `${__activeFile}.${process.pid}.tmp`
300
+ // B1: read-modify-write so pg_* fields written by supervisor-pg.mjs survive
301
+ // the initial prelude write. Spread order: cur first so new fields win.
302
+ const __curActive = (() => { try { return JSON.parse(fs.readFileSync(__activeFile, 'utf-8')) } catch { return {} } })()
303
+ // Strip stale field names written by older server versions; no callers — destructured to discard only.
304
+ const { pid: _legacyPid, startedAt: _legacyStartedAt, httpPort: _legacyHttpPort, ...__curActiveRest } = __curActive ?? {}
305
+ fs.writeFileSync(__activeTmp, JSON.stringify({
306
+ ...__curActiveRest,
307
+ instanceId: __instanceId,
308
+ ownerLeadPid: __ownerLeadPid,
309
+ terminalLeadPid: __ownerLeadPid,
310
+ supervisor_pid: __ownerLeadPid,
311
+ server_pid: process.pid,
312
+ supervisor_started_at: __nowMs,
313
+ updatedAt: __nowMs,
314
+ turnEndFile: path.join(__activeDir, `turn-end-${__instanceId}`),
315
+ statusFile: path.join(__activeDir, `status-${__instanceId}.json`),
316
+ backendReady: false,
317
+ bootPhase: 'prelude',
318
+ }))
319
+ __renameSyncWithRetry(__activeTmp, __activeFile)
320
+ }
321
+
322
+ // ── Supervisor cache-version advert ────────────────────────────────
323
+ // dev-sync reads this to know which cache version is in active use without
324
+ // having to introspect supervisor cmdline. Lives in server.mjs (not the
325
+ // proxy supervisor run-mcp.mjs) so future updates land via child-only
326
+ // restart and never sever the stdio bridge. Validity is verified at read
327
+ // time (pid alive + cacheDir exists) so a stale file from a dead supervisor
328
+ // is naturally rejected — no explicit cleanup required.
329
+ //
330
+ // run-mcp.mjs passes its identity (pid + cacheDir) via env vars at spawn.
331
+ // If they're missing (e.g. someone runs server.mjs standalone without the
332
+ // supervisor wrapper), skip — there's no supervisor to advertise.
333
+ try {
334
+ const _supPid = Number(process.env.MIXDOG_SUPERVISOR_PID)
335
+ const _supCacheDir = process.env.MIXDOG_SUPERVISOR_CACHE_DIR
336
+ if (Number.isFinite(_supPid) && _supPid > 0 && _supCacheDir) {
337
+ const _writeAdvert = (_file, _payload) => {
338
+ const _tmp = `${_file}.${process.pid}.${Date.now()}.tmp`
339
+ fs.writeFileSync(_tmp, JSON.stringify(_payload))
340
+ __renameSyncWithRetry(_tmp, _file)
341
+ }
342
+ const _readAdvert = (_file) => {
343
+ try { return JSON.parse(fs.readFileSync(_file, 'utf8')) } catch { return null }
344
+ }
345
+ const _advertFile = path.join(__activeDir, 'supervisor-advert.json')
346
+ const _advertByPidFile = path.join(__activeDir, `supervisor-advert.${_supPid}.json`)
347
+ const _advertIndexFile = path.join(__activeDir, 'supervisor-adverts.json')
348
+ const _supVer = (() => {
349
+ try { return JSON.parse(fs.readFileSync(path.join(_supCacheDir, '.claude-plugin', 'plugin.json'), 'utf8')).version }
350
+ catch { return null }
351
+ })()
352
+ const _record = {
353
+ pid: _supPid,
354
+ ownerLeadPid: _supPid,
355
+ serverPid: process.pid,
356
+ cacheDir: _supCacheDir,
357
+ version: _supVer,
358
+ startedAt: __nowMs,
359
+ updatedAt: Date.now(),
360
+ }
361
+ _writeAdvert(_advertFile, _record)
362
+ _writeAdvert(_advertByPidFile, _record)
363
+
364
+ const _recordsByPid = new Map()
365
+ for (const _ent of fs.readdirSync(__activeDir, { withFileTypes: true })) {
366
+ if (!_ent.isFile()) continue
367
+ const _name = _ent.name
368
+ if (!/^supervisor-advert(?:\.\d+)?\.json$/.test(_name)) continue
369
+ const _entry = _readAdvert(path.join(__activeDir, _name))
370
+ const _pid = __parsePositivePid(_entry?.ownerLeadPid) ?? __parsePositivePid(_entry?.pid)
371
+ if (!_pid || !_isPidAlive(_pid) || !_entry?.cacheDir || !fs.existsSync(_entry.cacheDir)) {
372
+ if (_name !== 'supervisor-advert.json') {
373
+ try { fs.unlinkSync(path.join(__activeDir, _name)) } catch {}
374
+ }
375
+ continue
376
+ }
377
+ _recordsByPid.set(_pid, { ..._entry, pid: _pid, ownerLeadPid: _pid })
378
+ }
379
+ _recordsByPid.set(_supPid, _record)
380
+ const _supervisors = [..._recordsByPid.values()].sort((a, b) => Number(a.pid) - Number(b.pid))
381
+ _writeAdvert(_advertIndexFile, { updatedAt: Date.now(), supervisors: _supervisors })
382
+ if (_supervisors.length > 1) {
383
+ process.stderr.write(
384
+ `[server] multi-supervisor notice: active supervisors count=${_supervisors.length} ` +
385
+ `pids=${_supervisors.map(s => s.pid).join(',')}\n`
386
+ )
387
+ }
388
+ }
389
+ } catch (e) {
390
+ process.stderr.write(`[server] supervisor advert write failed: ${e && e.message}\n`)
391
+ }
392
+
393
+ // ── Stale status-*.json sweep ──────────────────────────────────────
394
+ // `status-${pid}.json` files outlive their owning supervisor when the
395
+ // process exits without graceful shutdown (crash, SIGKILL, host reboot).
396
+ // Without sweep they accumulate indefinitely and bloat list responses
397
+ // over the runtime dir. Invariant: a status file is stale iff its PID
398
+ // is no longer alive. Live-PID files belong to peer instances and are
399
+ // left untouched. _isPidAlive is the same check used for server.lock.
400
+ try {
401
+ const _entries = fs.readdirSync(__activeDir, { withFileTypes: true })
402
+ for (const _ent of _entries) {
403
+ if (!_ent.isFile()) continue
404
+ const _name = _ent.name
405
+ if (!_name.startsWith('status-') || !_name.endsWith('.json')) continue
406
+ const _pid = Number.parseInt(_name.slice(7, -5), 10)
407
+ if (!Number.isFinite(_pid) || _pid <= 0) continue
408
+ if (_isPidAlive(_pid)) continue
409
+ try { fs.unlinkSync(path.join(__activeDir, _name)) } catch {}
410
+ }
411
+ } catch (e) {
412
+ process.stderr.write(`[server] stale status sweep failed: ${e && e.message}\n`)
413
+ }
414
+
415
+ // ── Bulk log rotation at boot ──────────────────────────────────────
416
+ // Worker logs (drop-trace, memory-worker, channels-worker, mcp-debug,
417
+ // webhook, schedule, event, statusline-trace, history/*.jsonl) outlive
418
+ // the supervisor process and grow without bound; over time the append
419
+ // fsync churn becomes the dominant background IO. Rotate each past
420
+ // 5 MB by renaming to `.1` (single archive; subsequent boots overwrite
421
+ // the archive). Invariant: rotation only at supervisor boot — worker
422
+ // processes have not started yet so there is no append-collision
423
+ // window.
424
+ const LOG_ROTATE_THRESHOLD = 5 * 1024 * 1024
425
+ const _logsToRotate = [
426
+ 'drop-trace.log',
427
+ 'memory-worker.log',
428
+ 'channels-worker.log',
429
+ 'mcp-debug.log',
430
+ 'webhook.log',
431
+ 'schedule.log',
432
+ 'event.log',
433
+ 'statusline-trace.log',
434
+ 'history/model-profile.jsonl',
435
+ ]
436
+ for (const _rel of _logsToRotate) {
437
+ try {
438
+ const _full = path.join(PLUGIN_DATA, _rel)
439
+ const _st = fs.statSync(_full)
440
+ if (_st.size > LOG_ROTATE_THRESHOLD) {
441
+ const _archive = _full + '.1'
442
+ try { fs.unlinkSync(_archive) } catch {}
443
+ fs.renameSync(_full, _archive)
444
+ }
445
+ } catch { /* missing or stat failure — skip */ }
446
+ }
447
+ // Also rotate scoped per-Lead-per-server mcp-debug.<lead>.<pid>.log files.
448
+ // server-main.mjs emits these alongside the fixed mcp-debug.log; without
449
+ // scoped rotation they grow unbounded across restarts of the same Lead.
450
+ try {
451
+ for (const _ent of fs.readdirSync(PLUGIN_DATA, { withFileTypes: true })) {
452
+ if (!_ent.isFile()) continue
453
+ const _name = _ent.name
454
+ if (!/^mcp-debug\.\d+\.\d+\.log$/.test(_name)) continue
455
+ const _full = path.join(PLUGIN_DATA, _name)
456
+ try {
457
+ const _st = fs.statSync(_full)
458
+ if (_st.size > LOG_ROTATE_THRESHOLD) {
459
+ const _archive = _full + '.1'
460
+ try { fs.unlinkSync(_archive) } catch {}
461
+ fs.renameSync(_full, _archive)
462
+ }
463
+ } catch {}
464
+ }
465
+ } catch { /* readdir failed — skip */ }
466
+
467
+ // ── Enter the main server ───────────────────────────────────────────
468
+ await import('./server-main.mjs')