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,118 @@
1
+ /**
2
+ * Daemon host — one shared backend process per plugin data dir.
3
+ *
4
+ * `ensureDaemon(dataDir)` is the thin-client entry: connect to a running
5
+ * daemon, or spawn one (serialized so N cold-start terminals don't storm) and
6
+ * connect. `runDaemon(dataDir)` is the daemon process entry: it claims the
7
+ * owner lock (singleton invariant — a loser exits), listens on the shared pipe,
8
+ * and serves every connected client.
9
+ *
10
+ * The daemon process delegates per-session request routing to
11
+ * `serveDaemon` in server-main.mjs, so each connection is a real
12
+ * Claude Code session.
13
+ *
14
+ * Ownership is the owner lock + pipe ownership, both liveness-based (ESRCH ⇒
15
+ * dead). No time-boxed "run anyway" escape: a loser attaches or exits.
16
+ */
17
+ import { spawn } from 'child_process'
18
+ import * as fs from 'fs'
19
+ import * as path from 'path'
20
+ import * as os from 'os'
21
+ import { fileURLToPath } from 'url'
22
+ import { connect } from './transport.mjs'
23
+
24
+ const __filename = fileURLToPath(import.meta.url)
25
+ // The daemon BODY lives in server-main (reached via `server.mjs --daemon`); the
26
+ // host only spawns it and connects clients. server.mjs is two levels up from
27
+ // src/daemon/. Pipe ownership (transport.listen → null) is the singleton arbiter.
28
+ const SERVER_ENTRY = path.join(path.dirname(__filename), '..', '..', 'server.mjs')
29
+ const RUNTIME_ROOT = process.env.MIXDOG_RUNTIME_ROOT || path.join(os.tmpdir(), 'mixdog')
30
+
31
+ function pidAlive(pid) {
32
+ if (!Number.isFinite(pid) || pid <= 0) return false
33
+ try { process.kill(pid, 0); return true }
34
+ catch (e) { return e.code !== 'ESRCH' } // ESRCH ⇒ dead; EPERM/other ⇒ alive
35
+ }
36
+
37
+ function lockDir(dataDir) {
38
+ const dir = dataDir && dataDir.length ? dataDir : RUNTIME_ROOT
39
+ try { fs.mkdirSync(dir, { recursive: true }) } catch {}
40
+ return dir
41
+ }
42
+ const spawnLockPath = (dataDir) => path.join(lockDir(dataDir), '.daemon-spawn.lock')
43
+
44
+ // Atomic create-or-fail lock holding our pid. Reclaims a lock whose holder pid
45
+ // is dead. Returns true if we now hold it.
46
+ function tryAcquireLock(file) {
47
+ for (let attempt = 0; attempt < 2; attempt++) {
48
+ try {
49
+ fs.writeFileSync(file, String(process.pid), { flag: 'wx' })
50
+ return true
51
+ } catch (e) {
52
+ if (e.code !== 'EEXIST') return false
53
+ let holder = 0
54
+ try { holder = Number(fs.readFileSync(file, 'utf8')) } catch {}
55
+ if (holder === process.pid) return true
56
+ if (pidAlive(holder)) return false
57
+ try { fs.unlinkSync(file) } catch {} // stale holder dead — reclaim and retry
58
+ }
59
+ }
60
+ return false
61
+ }
62
+ function releaseLock(file) {
63
+ try {
64
+ if (Number(fs.readFileSync(file, 'utf8')) === process.pid) fs.unlinkSync(file)
65
+ } catch {}
66
+ }
67
+
68
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms))
69
+
70
+ // Poll-connect until a daemon is listening or the budget expires.
71
+ async function waitForDaemon(dataDir, budgetMs) {
72
+ const deadline = Date.now() + budgetMs
73
+ for (;;) {
74
+ try { return await connect(dataDir, { timeoutMs: 1000 }) } catch {}
75
+ if (Date.now() >= deadline) throw new Error('daemon did not come up within budget')
76
+ await sleep(150)
77
+ }
78
+ }
79
+
80
+ function spawnDaemonProcess(dataDir) {
81
+ const child = spawn(process.env.BUN_EXEC_PATH || process.execPath, [SERVER_ENTRY, '--daemon', dataDir], {
82
+ detached: true,
83
+ stdio: 'ignore',
84
+ env: { ...process.env, MIXDOG_DAEMON_MODE: '1', MIXDOG_DAEMON_DATA_DIR: dataDir },
85
+ ...(process.platform === 'win32' ? { windowsHide: true } : {}),
86
+ })
87
+ child.unref()
88
+ return child
89
+ }
90
+
91
+ // Thin-client entry: return a framed connection to the shared daemon, spawning
92
+ // it if needed. Spawning is serialized via the spawn lock so N co-booting
93
+ // terminals produce one daemon; the owner lock inside runDaemon is the final
94
+ // singleton arbiter if two ever race through.
95
+ export async function ensureDaemon(dataDir) {
96
+ const deadline = Date.now() + 30000 // final safety bound, not the recovery path
97
+ for (;;) {
98
+ // 1) Already running?
99
+ try { return await connect(dataDir, { timeoutMs: 1200 }) } catch {}
100
+ // 2) Become the spawner. tryAcquireLock reclaims a DEAD holder, so a
101
+ // spawner that died before its daemon listened is recovered here on the
102
+ // next pass — liveness-based, not a fixed wait that wedges all waiters.
103
+ const spawnLock = spawnLockPath(dataDir)
104
+ if (tryAcquireLock(spawnLock)) {
105
+ try {
106
+ spawnDaemonProcess(dataDir)
107
+ return await waitForDaemon(dataDir, 30000)
108
+ } finally {
109
+ releaseLock(spawnLock)
110
+ }
111
+ }
112
+ // 3) A live client is spawning — briefly wait for its pipe, then loop back
113
+ // to retry both (1) connect and (2) dead-holder reclaim.
114
+ try { return await connect(dataDir, { timeoutMs: 800 }) } catch {}
115
+ if (Date.now() >= deadline) throw new Error('daemon did not come up within budget')
116
+ await sleep(150)
117
+ }
118
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * MCP Transport adapter over a daemon framed connection.
3
+ *
4
+ * Lets the daemon hand each accepted client connection to its own MCP SDK
5
+ * `Server` instance, so the SDK does protocol-correct initialize / capabilities
6
+ * / tools negotiation per connection while we only supply the wire. The SDK
7
+ * `Transport` contract is minimal: start / send / close + onmessage / onclose /
8
+ * onerror. Our framed connection already speaks NDJSON JSON-RPC objects, so the
9
+ * adapter is a thin pass-through.
10
+ */
11
+ export class FramedServerTransport {
12
+ constructor(conn, opts = {}) {
13
+ this._conn = conn
14
+ // Out-of-band control-frame sink (session-identity handshake). Control
15
+ // frames carry a `__mixdog` marker, no jsonrpc, and must NOT reach the SDK.
16
+ this._onControl = typeof opts.onControl === 'function' ? opts.onControl : null
17
+ this._started = false
18
+ // SDK assigns these before/after start(); call them if present.
19
+ this.onmessage = undefined
20
+ this.onclose = undefined
21
+ this.onerror = undefined
22
+ }
23
+
24
+ async start() {
25
+ if (this._started) return
26
+ this._started = true
27
+ this._conn.onMessage((msg) => {
28
+ if (msg && msg.__mixdog) {
29
+ try { this._onControl?.(msg) } catch (e) { this.onerror?.(e) }
30
+ return
31
+ }
32
+ try { this.onmessage?.(msg) }
33
+ catch (e) { this.onerror?.(e) }
34
+ })
35
+ this._conn.onClose(() => {
36
+ try { this.onclose?.() } catch {}
37
+ })
38
+ }
39
+
40
+ async send(message) {
41
+ this._conn.send(message)
42
+ }
43
+
44
+ async close() {
45
+ this._conn.close()
46
+ }
47
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Per-connection Session — the unit of isolation in the shared daemon.
3
+ *
4
+ * Today `server-main` is one process per Claude Code terminal, so session
5
+ * identity (sessionId, cwd, transcript, instanceId, lead pid) lives in process
6
+ * globals / env (`SESSION_ID`, `MIXDOG_SESSION_CWD`, …). In the daemon, one
7
+ * process serves N terminals, so that state must move OFF process globals and
8
+ * onto a Session keyed by the client connection. `cwd` is the highest-risk
9
+ * field: it is mutable (the `cwd` tool changes it) and must never leak from one
10
+ * client to another — the old `MIXDOG_SESSION_CWD` env is replaced by
11
+ * `session.cwd`, threaded per call into `dispatchTool` and worker payloads.
12
+ *
13
+ * Identity-only and install/process-scoped state (shared DB, plugin config,
14
+ * the single memory/channels services) deliberately stays OUT of Session.
15
+ */
16
+
17
+ import { _safeProcessCwd } from '../shared/user-cwd.mjs'
18
+
19
+ // Fields a thin client sends in its `initialize` handshake. Everything is
20
+ // optional so a minimal client still gets a usable session.
21
+ // { sessionId, cwd, transcriptPath, leadPid, instanceId, env }
22
+ export class Session {
23
+ constructor(conn, init = {}) {
24
+ this.conn = conn
25
+ this.sessionId = init.sessionId || null
26
+ // Mutable per-session working dir. Resolution order at call time:
27
+ // session.cwd ?? init default ?? process.cwd(). NEVER read a global env.
28
+ this.cwd = init.cwd || null
29
+ // Optional live cwd resolver. The boot session (single stdio client) sets
30
+ // this to pwd() so it keeps tracking the legacy MIXDOG_SESSION_CWD env and
31
+ // stays behaviour-identical; daemon sessions leave it null and own a static
32
+ // per-connection `cwd` instead.
33
+ this.cwdFn = typeof init.cwdFn === 'function' ? init.cwdFn : null
34
+ // Optional write-side hook for cwd. The boot session points this at the
35
+ // legacy MIXDOG_SESSION_CWD env (which cwdFn=pwd reads back); daemon
36
+ // sessions leave it null and `setCwd` mutates this.cwd directly.
37
+ this.setCwdFn = typeof init.setCwdFn === 'function' ? init.setCwdFn : null
38
+ this.transcriptPath = init.transcriptPath || null
39
+ this.leadPid = Number.isFinite(init.leadPid) ? init.leadPid : null
40
+ this.clientHostPid = Number.isFinite(init.clientHostPid) ? init.clientHostPid : null
41
+ // Per-session runtime seat (was server.mjs `instanceId` = process.pid).
42
+ this.instanceId = init.instanceId || init.sessionId || null
43
+ // Opaque per-session scratch for state that is process-global today and
44
+ // gets migrated incrementally (lead guard, recent-bash tracker, recap
45
+ // status). Keyed by feature name so each wiring step claims its own slot
46
+ // without widening this constructor.
47
+ this.scratch = new Map()
48
+ this.createdAt = init.now ?? null // stamped by caller; daemon avoids Date.now at construct
49
+ }
50
+
51
+ // The cwd a tool call should resolve against. Falls back to the process cwd
52
+ // only when the session never advertised one (never another session's cwd).
53
+ resolveCwd() {
54
+ // Fallback uses _safeProcessCwd(), NOT captureOriginalUserCwd(): a daemon
55
+ // serves N terminals, so the fallback must consult NO per-session signal
56
+ // (MIXDOG_SESSION_CWD / user-cwd.txt) — that would leak one terminal's cwd
57
+ // into another. _safeProcessCwd() reads only the install-scoped
58
+ // CLAUDE_PLUGIN_ROOT to guard the daemon-launch-dir == plugin-cache case
59
+ // down to homedir(), surfacing a missing cwd as ENOENT (absolute paths
60
+ // required) instead of a silent stale read under the plugin cache.
61
+ return (this.cwdFn ? this.cwdFn() : this.cwd) || _safeProcessCwd()
62
+ }
63
+
64
+ // Per-session cwd write (the `cwd set` tool). The boot session routes to its
65
+ // legacy env via setCwdFn so cwdFn=pwd keeps reading it; daemon sessions
66
+ // store it on the session so one terminal's cwd never leaks into another.
67
+ setCwd(value) {
68
+ if (this.setCwdFn) this.setCwdFn(value)
69
+ else this.cwd = value
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Registry of live sessions keyed by connection. One Session per connection;
75
+ * a reconnecting client (daemon restart) re-initializes and gets a fresh
76
+ * Session carrying the same sessionId (resume semantics land with M4 reconnect).
77
+ */
78
+ export class SessionRegistry {
79
+ constructor() {
80
+ this._byConn = new Map()
81
+ }
82
+
83
+ open(conn, init) {
84
+ const session = new Session(conn, init)
85
+ this._byConn.set(conn, session)
86
+ return session
87
+ }
88
+
89
+ get(conn) {
90
+ return this._byConn.get(conn) || null
91
+ }
92
+
93
+ close(conn) {
94
+ this._byConn.delete(conn)
95
+ }
96
+
97
+ get size() {
98
+ return this._byConn.size
99
+ }
100
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Thin client — bridges one Claude Code terminal's stdio to the shared daemon.
3
+ *
4
+ * Every per-terminal `server.mjs` invocation (spawned by run-mcp) runs this
5
+ * instead of the heavy in-process backend. It connects to the shared daemon
6
+ * (spawning it if needed) and pipes NDJSON JSON-RPC both ways: run-mcp/CC stdin
7
+ * → daemon, daemon → stdout. When the daemon connection drops, the thin client
8
+ * exits; run-mcp then respawns this server.mjs (which re-attaches or re-spawns
9
+ * the daemon), reusing run-mcp's existing initialize cache/replay.
10
+ *
11
+ * Session identity is advertised out-of-band via a `__mixdog` control frame
12
+ * that the daemon transport filters BEFORE the MCP SDK sees it, so it has no
13
+ * ordering dependency on the MCP initialize handshake.
14
+ */
15
+ import { ensureDaemon } from './host.mjs'
16
+ import { explicitSessionCwd, readLastSessionCwd } from '../shared/user-cwd.mjs'
17
+
18
+ export async function runThinClient(dataDir) {
19
+ const conn = await ensureDaemon(dataDir)
20
+
21
+ // daemon → CC (stdout)
22
+ conn.onMessage((msg) => {
23
+ try { process.stdout.write(JSON.stringify(msg) + '\n') } catch {}
24
+ })
25
+ // daemon gone → exit so run-mcp respawns us and we re-attach/re-spawn it
26
+ conn.onClose(() => process.exit(0))
27
+
28
+ // Advertise this terminal's identity (filtered out-of-band on the daemon).
29
+ try {
30
+ const rawLeadPid = Number(process.env.MIXDOG_SUPERVISOR_PID)
31
+ const leadPid = Number.isFinite(rawLeadPid) && rawLeadPid > 0 ? rawLeadPid : undefined
32
+ const hasEnvSessionCwd = typeof process.env.MIXDOG_SESSION_CWD === 'string' && process.env.MIXDOG_SESSION_CWD.length > 0
33
+ const advertisedCwd = hasEnvSessionCwd
34
+ ? (explicitSessionCwd() || readLastSessionCwd(leadPid) || undefined)
35
+ : (readLastSessionCwd(leadPid) || explicitSessionCwd() || undefined)
36
+ conn.send({
37
+ __mixdog: 'session',
38
+ sessionId: process.env.MIXDOG_SESSION_ID || undefined,
39
+ // Advertise an explicit session cwd so the daemon Session never falls
40
+ // back to its own process.cwd() (the plugin CACHE dir), which would make
41
+ // every bridge worker resolve paths under ...\plugins\cache\...\<ver>.
42
+ // explicitSessionCwd() = MIXDOG_SESSION_CWD (if it exists) ?? user-cwd.txt
43
+ // (rewritten every session start); it NEVER returns process.cwd().
44
+ cwd: advertisedCwd,
45
+ leadPid,
46
+ transcriptPath: process.env.MIXDOG_TRANSCRIPT_PATH || undefined,
47
+ clientHostPid: Number(process.env.MIXDOG_OWNER_HOST_PID) || undefined,
48
+ })
49
+ } catch {}
50
+
51
+ // CC/run-mcp (stdin, NDJSON) → daemon
52
+ let buf = ''
53
+ process.stdin.setEncoding('utf8')
54
+ process.stdin.on('data', (chunk) => {
55
+ buf += chunk
56
+ let nl
57
+ while ((nl = buf.indexOf('\n')) >= 0) {
58
+ const line = buf.slice(0, nl)
59
+ buf = buf.slice(nl + 1)
60
+ if (!line) continue
61
+ let obj
62
+ try { obj = JSON.parse(line) } catch { continue }
63
+ conn.send(obj)
64
+ }
65
+ })
66
+ process.stdin.on('end', () => process.exit(0))
67
+ process.stdin.on('close', () => process.exit(0))
68
+
69
+ // Keep the process alive; the pipes drive everything.
70
+ await new Promise(() => {})
71
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Daemon transport — NDJSON JSON-RPC over a per-data-dir named pipe.
3
+ *
4
+ * Replaces the per-terminal `run-mcp ↔ server.mjs` stdio link with a single
5
+ * shared endpoint: one daemon process listens; N thin clients (one per Claude
6
+ * Code terminal) connect. This module is PURE transport — framing, accept,
7
+ * connect, liveness. It carries no session or JSON-RPC semantics; the daemon
8
+ * host layers those on top via the `onConnection` callback.
9
+ *
10
+ * Pipe ownership is the singleton primitive: on Windows a second `listen()`
11
+ * fails with EADDRINUSE, on Unix we probe-then-unlink. `listen()` returning
12
+ * null means "a live daemon already owns this pipe — attach as a client".
13
+ */
14
+ import net from 'net'
15
+ import { createHash } from 'crypto'
16
+ import { unlinkSync, mkdirSync } from 'fs'
17
+ import path from 'path'
18
+ import os from 'os'
19
+
20
+ // Inbound frame guardrail (mirrors run-mcp's MAX_LINE_BYTES). A JSON-RPC line
21
+ // is newline-terminated; an unterminated line growing past this cap signals a
22
+ // runaway producer. 4 MB comfortably fits any legitimate tool result.
23
+ const MAX_LINE_BYTES = 4 * 1024 * 1024
24
+
25
+ const RUNTIME_ROOT = process.env.MIXDOG_RUNTIME_ROOT || path.join(os.tmpdir(), 'mixdog')
26
+
27
+ // One pipe per plugin data dir so distinct installs / data dirs never collide.
28
+ function daemonPipePath(dataDir) {
29
+ const hash = createHash('sha1').update(String(dataDir || 'default')).digest('hex').slice(0, 12)
30
+ if (process.platform === 'win32') return `\\\\.\\pipe\\mixdog-daemon-${hash}`
31
+ return path.join(RUNTIME_ROOT, `mixdog-daemon-${hash}.sock`)
32
+ }
33
+
34
+ // Wrap a net.Socket in NDJSON framing. `send(obj)` writes one JSON line;
35
+ // `onMessage(cb)` fires per parsed object; `onClose(cb)` fires once on
36
+ // socket end/error. Non-JSON lines are dropped (never throw on the socket).
37
+ function frameConnection(socket) {
38
+ let buf = ''
39
+ let closed = false
40
+ const msgHandlers = []
41
+ const closeHandlers = []
42
+ socket.setEncoding('utf8')
43
+ socket.on('data', (chunk) => {
44
+ buf += chunk
45
+ let nl
46
+ while ((nl = buf.indexOf('\n')) >= 0) {
47
+ const line = buf.slice(0, nl)
48
+ buf = buf.slice(nl + 1)
49
+ if (!line) continue
50
+ let obj
51
+ try { obj = JSON.parse(line) } catch { continue }
52
+ for (const h of msgHandlers) { try { h(obj) } catch (e) { try { process.stderr.write(`[transport] message handler failed: ${e?.message ?? e}\n`) } catch {} } }
53
+ }
54
+ if (buf.length > MAX_LINE_BYTES) {
55
+ // Runaway unterminated frame — drop the connection rather than grow
56
+ // unbounded. A well-behaved peer never produces a 4 MB line without a \n.
57
+ try { socket.destroy() } catch {}
58
+ }
59
+ })
60
+ const fireClose = () => {
61
+ if (closed) return
62
+ closed = true
63
+ for (const h of closeHandlers) { try { h() } catch {} }
64
+ }
65
+ socket.on('close', fireClose)
66
+ socket.on('error', fireClose)
67
+ return {
68
+ socket,
69
+ send(obj) {
70
+ if (closed) return false
71
+ try { return socket.write(JSON.stringify(obj) + '\n') } catch { return false }
72
+ },
73
+ onMessage(cb) { msgHandlers.push(cb) },
74
+ onClose(cb) { closeHandlers.push(cb) },
75
+ close() { try { socket.end() } catch {} },
76
+ get closed() { return closed },
77
+ }
78
+ }
79
+
80
+ // Start the daemon endpoint. Resolves the net.Server on success, or `null`
81
+ // when another live daemon already owns the pipe (this process must attach as
82
+ // a client instead). `onConnection(conn)` is invoked with a framed connection
83
+ // per accepted client.
84
+ export async function listen({ dataDir, onConnection }) {
85
+ const pipePath = daemonPipePath(dataDir)
86
+ if (process.platform !== 'win32') {
87
+ // Unix domain sockets need the parent dir to exist; RUNTIME_ROOT may not.
88
+ try { mkdirSync(path.dirname(pipePath), { recursive: true }) } catch {}
89
+ }
90
+ // Fresh server per attempt: a net.Server that emitted 'error' cannot be
91
+ // re-listened reliably, so the Unix retry below recreates it each time.
92
+ const attempt = () => new Promise((resolve, reject) => {
93
+ const server = net.createServer((socket) => {
94
+ try { onConnection(frameConnection(socket)) } catch {}
95
+ })
96
+ const onErr = (e) => { server.removeListener('listening', onOk); reject(e) }
97
+ const onOk = () => { server.removeListener('error', onErr); resolve(server) }
98
+ server.once('error', onErr)
99
+ server.once('listening', onOk)
100
+ server.listen(pipePath)
101
+ })
102
+ if (process.platform === 'win32') {
103
+ // Windows named pipes reject a second listener with EADDRINUSE on their own,
104
+ // so no pre-listen probe is needed.
105
+ try { return await attempt() }
106
+ catch (e) { if (isAddrInUse(e)) return null; throw e }
107
+ }
108
+ // Unix: a leftover socket file from a crashed daemon makes bind fail with
109
+ // EADDRINUSE. Only unlink when a probe says nothing answers — and re-probe
110
+ // after every failed bind, so if a sibling rebinds in the probe→unlink
111
+ // window we see it alive on the next pass and attach instead of stealing it.
112
+ // (runDaemon calls this under the owner lock, which already serializes
113
+ // dataDir entry; this loop closes the residual TOCTOU window.)
114
+ for (let i = 0; i < 3; i++) {
115
+ try { return await attempt() }
116
+ catch (e) {
117
+ if (!isAddrInUse(e)) throw e
118
+ if (await probeAlive(pipePath)) return null
119
+ try { unlinkSync(pipePath) } catch {}
120
+ }
121
+ }
122
+ return null
123
+ }
124
+
125
+ // Connect to an existing daemon. Resolves a framed connection or rejects
126
+ // (ENOENT / ECONNREFUSED when no daemon is listening, or on timeout).
127
+ export function connect(dataDir, { timeoutMs = 2000 } = {}) {
128
+ const pipePath = daemonPipePath(dataDir)
129
+ return new Promise((resolve, reject) => {
130
+ const socket = net.connect(pipePath)
131
+ const timer = setTimeout(() => {
132
+ try { socket.destroy() } catch {}
133
+ reject(new Error('daemon connect timeout'))
134
+ }, timeoutMs)
135
+ socket.once('connect', () => { clearTimeout(timer); resolve(frameConnection(socket)) })
136
+ socket.once('error', (e) => { clearTimeout(timer); reject(e) })
137
+ })
138
+ }
139
+
140
+ function isAddrInUse(e) {
141
+ const msg = String(e?.message || e || '')
142
+ return e?.code === 'EADDRINUSE' || msg.includes('EADDRINUSE') || msg.includes('Failed to listen')
143
+ }
144
+
145
+ // Resolves true if something is listening on the pipe (daemon alive), false on
146
+ // ECONNREFUSED / ENOENT (dead/absent). Other errors / timeout resolve true so
147
+ // we err on NOT stealing a possibly-live peer's socket.
148
+ function probeAlive(pipePath) {
149
+ return new Promise((resolve) => {
150
+ let done = false
151
+ const finish = (alive) => {
152
+ if (done) return
153
+ done = true
154
+ try { c.destroy() } catch {}
155
+ clearTimeout(t)
156
+ resolve(alive)
157
+ }
158
+ const c = net.connect(pipePath)
159
+ const t = setTimeout(() => finish(true), 500)
160
+ c.once('connect', () => finish(true))
161
+ c.once('error', (e) => finish(!(e?.code === 'ECONNREFUSED' || e?.code === 'ENOENT')))
162
+ })
163
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "schema_version": 1,
3
+ "generated_at": "2026-06-11T13:16:44.033Z",
4
+ "release_tag": "runtime-v0.4.0",
5
+ "pg": {
6
+ "major": 16,
7
+ "minor": 4
8
+ },
9
+ "pgvector": {
10
+ "version": "0.8.2"
11
+ },
12
+ "assets": {
13
+ "darwin-arm64": {
14
+ "url": "https://github.com/trib-plugin/mixdog/releases/download/runtime-v0.4.0/mixdog-runtime-darwin-arm64-pg16.4-pgvector0.8.2.tar.gz",
15
+ "sha256": "7a642006ef60ccf8c385ba89c761c2b6f4927f6f01a1c4fb152604e6b41fe3d2",
16
+ "size": 23397361
17
+ },
18
+ "darwin-x64": {
19
+ "url": "https://github.com/trib-plugin/mixdog/releases/download/runtime-v0.4.0/mixdog-runtime-darwin-x64-pg16.4-pgvector0.8.2.tar.gz",
20
+ "sha256": "8e39bfc3f06d88e9854946d5d83f7d77d9530f99284d7c1c51872c12f389f7e7",
21
+ "size": 23687666
22
+ },
23
+ "linux-x64": {
24
+ "url": "https://github.com/trib-plugin/mixdog/releases/download/runtime-v0.4.0/mixdog-runtime-linux-x64-pg16.4-pgvector0.8.2.tar.gz",
25
+ "sha256": "28ee64125a0125ff36c47560c08ce8f38e49aef034c010c7e4867e8cdd783385",
26
+ "size": 23276106
27
+ },
28
+ "win32-x64": {
29
+ "url": "https://github.com/trib-plugin/mixdog/releases/download/runtime-v0.4.0/mixdog-runtime-win32-x64-pg16.4-pgvector0.8.2.tar.gz",
30
+ "sha256": "6faed8a49b3303b0adb1c5715b2b19c5cc47df2e738d586d0d0bd432f1ca035d",
31
+ "size": 44036958
32
+ },
33
+ "linux-arm64": {
34
+ "unsupported": true,
35
+ "url": "TBD",
36
+ "sha256": "TBD",
37
+ "size": 0
38
+ }
39
+ }
40
+ }