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,477 @@
1
+ // whisper-server.mjs
2
+ //
3
+ // State-machine manager for ONE long-lived whisper-server.exe process.
4
+ //
5
+ // Invariant model (NOT a try-a-few-things model):
6
+ // There is at most ONE managed whisper-server child, identified by an exact
7
+ // runtime contract (serverCmd / modelPath / threadCount / host). ensureReady()
8
+ // guarantees that — when it resolves — a child matching the CURRENT contract is
9
+ // bound to the fixed port and has loaded its model. Any deviation (contract
10
+ // change, child death, port stolen) is repaired by deterministically tearing
11
+ // down and recreating the SAME contract. There is NO fallback to a CLI binary,
12
+ // to python, to another executable, or to another model. If the contract cannot
13
+ // be satisfied, the operation FAILS.
14
+ //
15
+ // States: STOPPED → STARTING → READY → STOPPING → STOPPED, plus DEAD on child exit.
16
+ //
17
+ // Why a FIXED port (not `--port 0`):
18
+ // whisper-server 1.8.4 does not support OS-assigned ephemeral bind. Passing
19
+ // `--port 0` makes it print `listening at http://127.0.0.1:0` and never bind a
20
+ // usable port. So we bind a single deterministic port and FAIL CLOSED when it is
21
+ // occupied by a process we do not positively own. We NEVER scan a port range.
22
+ //
23
+ // Readiness detection:
24
+ // The server prints `... listening at http://<host>:<port>` AFTER the model is
25
+ // loaded, but that line is block-buffered when stdout is a pipe and may not flush
26
+ // promptly. So readiness is gated on an active TCP connect to the fixed port
27
+ // (the socket is bound only after model load completes). When the listening line
28
+ // IS observed, the parsed port is asserted to equal the fixed port (contract
29
+ // invariant); a mismatch is fatal.
30
+ //
31
+ // PID metadata (<dataDir>/voice/whisper-server.pid.json) exists ONLY for cleanup
32
+ // across process restarts. We kill a stale child ONLY when it is positively owned
33
+ // (recorded pid is alive AND its image is whisper-server). A foreign process on the
34
+ // port is never killed — we fail closed instead.
35
+
36
+ import net from 'node:net';
37
+ import path from 'node:path';
38
+ import fs from 'node:fs';
39
+ import { spawn, spawnSync } from 'node:child_process';
40
+ import { setTimeout as delay } from 'node:timers/promises';
41
+
42
+ // ── Tunables (deterministic; no ranges) ──────────────────────────────────────
43
+ const IS_WIN = process.platform === 'win32';
44
+ // Single deterministic port. Override only via env for operator control; never
45
+ // auto-scanned. Fail closed if occupied by a non-owned process.
46
+ const FIXED_PORT = (() => {
47
+ const raw = process.env.MIXDOG_WHISPER_SERVER_PORT;
48
+ const n = raw ? Number.parseInt(raw, 10) : NaN;
49
+ return Number.isInteger(n) && n > 0 && n < 65536 ? n : 8771;
50
+ })();
51
+ const READY_TIMEOUT_MS = 120_000; // model load + bind budget
52
+ const READY_POLL_MS = 250; // TCP probe cadence
53
+ const PROBE_CONNECT_MS = 1_000; // per TCP-connect attempt
54
+ const STOP_GRACE_MS = 5_000; // SIGTERM → SIGKILL escalation window
55
+ const INFERENCE_PATH = '/inference'; // whisper-server default --inference-path
56
+
57
+ const STATE = Object.freeze({
58
+ STOPPED: 'STOPPED',
59
+ STARTING: 'STARTING',
60
+ READY: 'READY',
61
+ STOPPING: 'STOPPING',
62
+ DEAD: 'DEAD',
63
+ });
64
+
65
+ // ── Singleton manager state ───────────────────────────────────────────────────
66
+ const mgr = {
67
+ state: STATE.STOPPED,
68
+ child: null, // ChildProcess handle (non-detached)
69
+ port: null, // bound port (== FIXED_PORT when READY)
70
+ host: null,
71
+ runtimeKey: null, // exact contract fingerprint
72
+ contract: null, // { serverCmd, modelPath, threadCount, host }
73
+ startPromise: null, // in-flight ensureReady() start
74
+ inflight: new Set(), // active transcribe AbortControllers
75
+ logTail: '', // recent stdout/stderr for diagnostics
76
+ };
77
+
78
+ function runtimeKeyOf({ serverCmd, modelPath, threadCount, host }) {
79
+ return JSON.stringify([serverCmd, modelPath, threadCount, host]);
80
+ }
81
+
82
+ function pidMetaPath() {
83
+ // Co-located with the model store; serverCmd lives under <dataDir>/voice-runtime
84
+ // and the model under <dataDir>/voice/models, so derive the voice dir from cwd
85
+ // of the server binary's data root is brittle — instead key off serverCmd's
86
+ // runtime root. We persist next to a stable per-user temp path derived from the
87
+ // serverCmd so cleanup survives a manager-process restart.
88
+ const base = mgr.contract?.serverCmd
89
+ ? path.join(path.dirname(mgr.contract.serverCmd), '..', '..')
90
+ : process.cwd();
91
+ return path.join(base, 'whisper-server.pid.json');
92
+ }
93
+
94
+ function writePidMeta() {
95
+ if (!mgr.child) return;
96
+ const meta = {
97
+ pid: mgr.child.pid,
98
+ serverCmd: mgr.contract.serverCmd,
99
+ modelPath: mgr.contract.modelPath,
100
+ threadCount: mgr.contract.threadCount,
101
+ host: mgr.contract.host,
102
+ port: mgr.port,
103
+ runtimeKey: mgr.runtimeKey,
104
+ startedAt: Date.now(),
105
+ };
106
+ try {
107
+ fs.mkdirSync(path.dirname(pidMetaPath()), { recursive: true });
108
+ fs.writeFileSync(pidMetaPath(), JSON.stringify(meta), 'utf8');
109
+ } catch { /* metadata is advisory cleanup state; non-fatal */ }
110
+ }
111
+
112
+ function clearPidMeta() {
113
+ try { fs.rmSync(pidMetaPath(), { force: true }); } catch { /* non-fatal */ }
114
+ }
115
+
116
+ function readPidMeta() {
117
+ try {
118
+ return JSON.parse(fs.readFileSync(pidMetaPath(), 'utf8'));
119
+ } catch { return null; }
120
+ }
121
+
122
+ // Read the live process command line for `pid`. This is the proof-of-ownership
123
+ // query: a recorded pid being alive with the right image is NOT sufficient (pids
124
+ // are reused; another whisper-server could be unrelated). We must confirm the
125
+ // running process was launched against OUR contract.
126
+ // win32: query Win32_Process.CommandLine via wmic at a DETERMINISTIC System32
127
+ // path (never resolved through PATH, which an attacker/shadow binary
128
+ // could hijack).
129
+ // posix: `ps -o args= -p <pid>` prints the full argument vector.
130
+ // Returns '' on any failure → callers MUST treat '' as "cannot prove" and fail
131
+ // closed (never kill, never reuse).
132
+ function readProcessCommandLine(pid) {
133
+ if (IS_WIN) {
134
+ const sysRoot = process.env.SystemRoot || process.env.windir || 'C\u003a\\Windows';
135
+ const wmic = path.join(sysRoot, 'System32', 'wbem', 'wmic.exe');
136
+ try {
137
+ const r = spawnSync(
138
+ wmic,
139
+ ['process', 'where', `ProcessId=${pid}`, 'get', 'CommandLine', '/FORMAT:LIST'],
140
+ { encoding: 'utf8', windowsHide: true },
141
+ );
142
+ return r.stdout || '';
143
+ } catch { return ''; }
144
+ }
145
+ try {
146
+ const r = spawnSync('ps', ['-o', 'args=', '-p', String(pid)], { encoding: 'utf8', windowsHide: true });
147
+ return r.stdout || '';
148
+ } catch { return ''; }
149
+ }
150
+
151
+ // Positive ownership check: the recorded pid must be alive AND its image must be
152
+ // whisper-server AND its live command line must reference OUR contract — i.e. it
153
+ // must contain our modelPath OR our `--port <port>`. Image + alive alone is not
154
+ // proof (pids are reused, and an unrelated whisper-server could be running), so
155
+ // we additionally bind ownership to the running command line via an OS query.
156
+ // Any inability to prove the command-line match → return false (fail closed);
157
+ // only a positive match licenses killing the stale child.
158
+ function isOwnedWhisperServer(pid, { modelPath, port } = {}) {
159
+ if (!pid || !Number.isInteger(pid)) return false;
160
+ try { process.kill(pid, 0); } catch { return false; } // not alive / not ours
161
+ // 1) Image identity.
162
+ let imageOk = false;
163
+ if (IS_WIN) {
164
+ try {
165
+ const r = spawnSync(
166
+ 'tasklist',
167
+ ['/FI', `PID eq ${pid}`, '/FO', 'CSV', '/NH'],
168
+ { encoding: 'utf8', windowsHide: true },
169
+ );
170
+ imageOk = /whisper-server\.exe/i.test(r.stdout || '');
171
+ } catch { return false; }
172
+ } else {
173
+ try {
174
+ const r = spawnSync('ps', ['-p', String(pid), '-o', 'comm='], { encoding: 'utf8', windowsHide: true });
175
+ imageOk = /whisper-server/i.test(r.stdout || '');
176
+ } catch { return false; }
177
+ }
178
+ if (!imageOk) return false;
179
+ // 2) Command-line contract proof. Fail closed if we cannot read the cmdline.
180
+ const cmdline = readProcessCommandLine(pid);
181
+ if (!cmdline) return false;
182
+ const modelMatch = Boolean(modelPath) && cmdline.includes(modelPath);
183
+ const portMatch = Number.isInteger(port)
184
+ && new RegExp(`--port(?:\\s+|=)${port}(?!\\d)`).test(cmdline);
185
+ return modelMatch || portMatch;
186
+ }
187
+
188
+ function killPid(pid, force) {
189
+ if (!pid) return;
190
+ if (IS_WIN) {
191
+ const args = ['/PID', String(pid), '/T'];
192
+ if (force) args.push('/F');
193
+ try { spawnSync('taskkill', args, { windowsHide: true }); } catch { /* best-effort */ }
194
+ return;
195
+ }
196
+ try { process.kill(pid, force ? 'SIGKILL' : 'SIGTERM'); } catch { /* best-effort */ }
197
+ }
198
+
199
+ // Single TCP-connect probe. Resolves true when the socket accepts a connection
200
+ // (whisper-server binds only after the model is fully loaded).
201
+ function probePort(host, port) {
202
+ return new Promise((resolve) => {
203
+ const sock = net.connect({ host, port });
204
+ let done = false;
205
+ const finish = (ok) => {
206
+ if (done) return;
207
+ done = true;
208
+ try { sock.destroy(); } catch {}
209
+ resolve(ok);
210
+ };
211
+ sock.setTimeout(PROBE_CONNECT_MS);
212
+ sock.once('connect', () => finish(true));
213
+ sock.once('timeout', () => finish(false));
214
+ sock.once('error', () => finish(false));
215
+ });
216
+ }
217
+
218
+ function appendLog(buf) {
219
+ mgr.logTail = (mgr.logTail + buf.toString()).slice(-4000);
220
+ }
221
+
222
+ // Advisory: lower the child to Windows IDLE priority exactly once. Failure is
223
+ // non-fatal (matches os.setPriority best-effort usage elsewhere) and never blocks
224
+ // readiness — priority is not part of the transcription contract.
225
+ async function applyLowPriorityOnce(child) {
226
+ if (!IS_WIN || !child?.pid) return;
227
+ try {
228
+ const mod = await import('node-windows-process-info');
229
+ const api = mod.default ?? mod;
230
+ const setPriority = api.setPriority ?? api.setProcessPriority;
231
+ if (typeof setPriority === 'function') {
232
+ // IDLE_PRIORITY_CLASS === 0x40 (64); pass both the symbolic and numeric
233
+ // forms to tolerate the package's accepted argument shape.
234
+ try { setPriority(child.pid, 'idle'); }
235
+ catch { setPriority(child.pid, 0x40); }
236
+ }
237
+ } catch { /* package/API absent → priority stays default; non-fatal */ }
238
+ }
239
+
240
+ function detachChildHandlers() {
241
+ if (!mgr.child) return;
242
+ try { mgr.child.removeAllListeners(); } catch {}
243
+ try { mgr.child.stdout?.removeAllListeners(); } catch {}
244
+ try { mgr.child.stderr?.removeAllListeners(); } catch {}
245
+ }
246
+
247
+ // Deterministic teardown of the current child. Used both for STOPPING and for a
248
+ // contract change. Escalates SIGTERM → SIGKILL after a grace window.
249
+ async function teardown(reason) {
250
+ const child = mgr.child;
251
+ const pid = child?.pid;
252
+ // Abort all in-flight transcribe requests first.
253
+ for (const ctrl of mgr.inflight) { try { ctrl.abort(new Error(`whisper-server stopping: ${reason}`)); } catch {} }
254
+ mgr.inflight.clear();
255
+ if (child && pid) {
256
+ detachChildHandlers();
257
+ const exited = new Promise((resolve) => { try { child.once('exit', resolve); } catch { resolve(); } });
258
+ killPid(pid, false);
259
+ const raced = await Promise.race([exited, delay(STOP_GRACE_MS, 'timeout')]);
260
+ if (raced === 'timeout') {
261
+ killPid(pid, true); // escalate
262
+ await Promise.race([exited, delay(STOP_GRACE_MS, 'timeout')]);
263
+ }
264
+ }
265
+ mgr.child = null;
266
+ mgr.port = null;
267
+ mgr.host = null;
268
+ mgr.runtimeKey = null;
269
+ mgr.contract = null;
270
+ clearPidMeta();
271
+ }
272
+
273
+ function wireChildExit() {
274
+ const child = mgr.child;
275
+ if (!child) return;
276
+ child.stdout?.on('data', appendLog);
277
+ child.stderr?.on('data', appendLog);
278
+ child.once('error', () => {
279
+ // Spawn/runtime error → the contract is unsatisfied.
280
+ mgr.state = STATE.DEAD;
281
+ });
282
+ child.once('exit', () => {
283
+ // Child death is an invariant violation. Mark DEAD then settle to STOPPED so
284
+ // the next ensureReady() recreates the SAME contract (invariant repair).
285
+ for (const ctrl of mgr.inflight) { try { ctrl.abort(new Error('whisper-server exited')); } catch {} }
286
+ mgr.inflight.clear();
287
+ detachChildHandlers();
288
+ mgr.state = STATE.DEAD;
289
+ mgr.child = null;
290
+ mgr.port = null;
291
+ mgr.runtimeKey = null;
292
+ mgr.contract = null;
293
+ clearPidMeta();
294
+ mgr.state = STATE.STOPPED;
295
+ });
296
+ }
297
+
298
+ async function startServer(contract) {
299
+ const { serverCmd, modelPath, threadCount, host } = contract;
300
+ mgr.state = STATE.STARTING;
301
+ mgr.contract = contract;
302
+ mgr.runtimeKey = runtimeKeyOf(contract);
303
+ mgr.host = host;
304
+ mgr.port = FIXED_PORT;
305
+
306
+ // ── Pre-spawn: reclaim a positively-owned stale child, else fail closed. ──
307
+ const meta = readPidMeta();
308
+ // Prove ownership against OUR contract (modelPath OR --port) before killing.
309
+ // Prefer the recorded meta's own contract fields; fall back to the contract we
310
+ // are about to start. A non-positive match → never kill (fail closed).
311
+ const ownArgs = { modelPath: meta?.modelPath ?? modelPath, port: meta?.port ?? FIXED_PORT };
312
+ if (meta && isOwnedWhisperServer(meta.pid, ownArgs)) {
313
+ killPid(meta.pid, false);
314
+ await delay(500);
315
+ if (isOwnedWhisperServer(meta.pid, ownArgs)) { killPid(meta.pid, true); await delay(500); }
316
+ clearPidMeta();
317
+ }
318
+ if (await probePort(host, FIXED_PORT)) {
319
+ // Port held by a process we do NOT positively own → never scan a range,
320
+ // never kill a foreign process; fail closed.
321
+ mgr.state = STATE.STOPPED;
322
+ throw new Error(
323
+ `whisper-server: fixed port ${FIXED_PORT} on ${host} is occupied by a non-owned process; refusing to start (fail closed)`,
324
+ );
325
+ }
326
+
327
+ // ── Spawn ONCE: non-detached, cwd = dirname(serverCmd) for Windows DLL load. ──
328
+ const args = [
329
+ '--model', modelPath,
330
+ '--host', host,
331
+ '--port', String(FIXED_PORT),
332
+ '-t', String(threadCount),
333
+ ];
334
+ const child = spawn(serverCmd, args, {
335
+ cwd: path.dirname(serverCmd), // resolve co-located CUDA/runtime DLLs
336
+ detached: false,
337
+ windowsHide: true,
338
+ stdio: ['ignore', 'pipe', 'pipe'],
339
+ });
340
+ mgr.child = child;
341
+ wireChildExit();
342
+ writePidMeta();
343
+ await applyLowPriorityOnce(child); // advisory, once
344
+
345
+ // ── Wait for READY: TCP bind + (when flushed) listening-line port assertion. ──
346
+ const deadline = Date.now() + READY_TIMEOUT_MS;
347
+ while (Date.now() < deadline) {
348
+ if (mgr.state === STATE.DEAD || !mgr.child) {
349
+ throw new Error(`whisper-server died during startup:\n${mgr.logTail.slice(-1000)}`);
350
+ }
351
+ // Assert the printed bound port matches the fixed contract port when seen.
352
+ const m = mgr.logTail.match(/listening at https?:\/\/[^\s:/]+:(\d+)/i);
353
+ if (m) {
354
+ const printed = Number.parseInt(m[1], 10);
355
+ if (printed !== FIXED_PORT) {
356
+ await teardown('port-contract-mismatch');
357
+ mgr.state = STATE.DEAD;
358
+ throw new Error(
359
+ `whisper-server bound port ${printed} but contract requires ${FIXED_PORT}`,
360
+ );
361
+ }
362
+ }
363
+ if (await probePort(host, FIXED_PORT)) {
364
+ mgr.port = FIXED_PORT;
365
+ mgr.state = STATE.READY;
366
+ return;
367
+ }
368
+ await delay(READY_POLL_MS);
369
+ }
370
+ // Timed out: tear down deterministically and fail (no fallback).
371
+ await teardown('readiness-timeout');
372
+ mgr.state = STATE.DEAD;
373
+ throw new Error(`whisper-server did not become ready within ${READY_TIMEOUT_MS}ms`);
374
+ }
375
+
376
+ /**
377
+ * Ensure a READY whisper-server matching the exact contract. Spawns once; on a
378
+ * contract change deterministically stops the old child and starts the new one.
379
+ * Resolves only when the port is bound and the model is loaded.
380
+ */
381
+ export async function ensureReady({ serverCmd, modelPath, threadCount, host = '127.0.0.1' }) {
382
+ if (!serverCmd) throw new Error('ensureReady: serverCmd is required');
383
+ if (!modelPath) throw new Error('ensureReady: modelPath is required');
384
+ if (!Number.isInteger(threadCount) || threadCount < 1) {
385
+ throw new Error(`ensureReady: threadCount must be a positive integer (got ${threadCount})`);
386
+ }
387
+ const contract = { serverCmd, modelPath, threadCount, host };
388
+ const key = runtimeKeyOf(contract);
389
+
390
+ // Already READY on the same contract with a live child → done.
391
+ if (mgr.state === STATE.READY && mgr.runtimeKey === key && mgr.child) {
392
+ return;
393
+ }
394
+ // A start is already in flight for the SAME contract → await it.
395
+ if (mgr.startPromise && mgr.runtimeKey === key && mgr.state === STATE.STARTING) {
396
+ return mgr.startPromise;
397
+ }
398
+ // Contract changed (or stale/dead) → tear down anything current first.
399
+ if (mgr.child || mgr.state === STATE.STARTING) {
400
+ mgr.state = STATE.STOPPING;
401
+ await teardown('contract-change');
402
+ mgr.state = STATE.STOPPED;
403
+ }
404
+
405
+ mgr.startPromise = startServer(contract).finally(() => { mgr.startPromise = null; });
406
+ return mgr.startPromise;
407
+ }
408
+
409
+ /**
410
+ * Transcribe a WAV via POST <host:port>/inference (multipart). Returns the text.
411
+ * On ANY failure the request fails AND the server is marked DEAD (the next
412
+ * ensureReady recreates the contract). There is NO fallback.
413
+ */
414
+ export async function transcribe(wavPath, { language } = {}) {
415
+ if (mgr.state !== STATE.READY || !mgr.child || !mgr.port) {
416
+ throw new Error(`transcribe: whisper-server not READY (state=${mgr.state})`);
417
+ }
418
+ const host = mgr.host;
419
+ const port = mgr.port;
420
+ const ctrl = new AbortController();
421
+ mgr.inflight.add(ctrl);
422
+ try {
423
+ const data = await fs.promises.readFile(wavPath);
424
+ const form = new FormData();
425
+ form.append('file', new Blob([data], { type: 'audio/wav' }), path.basename(wavPath));
426
+ form.append('response_format', 'json'); // → { "text": "..." }
427
+ if (language) form.append('language', String(language));
428
+
429
+ const res = await fetch(`http://${host}:${port}${INFERENCE_PATH}`, {
430
+ method: 'POST',
431
+ body: form,
432
+ signal: ctrl.signal,
433
+ });
434
+ if (!res.ok) {
435
+ const body = await res.text().catch(() => '');
436
+ throw new Error(`whisper-server /inference HTTP ${res.status}: ${body.slice(0, 500)}`);
437
+ }
438
+ const json = await res.json();
439
+ if (typeof json?.text !== 'string') {
440
+ throw new Error(`whisper-server /inference returned no text field: ${JSON.stringify(json).slice(0, 300)}`);
441
+ }
442
+ return json.text;
443
+ } catch (err) {
444
+ // Request failed → tear the child down and settle to ONE resting state. No fallback.
445
+ mgr.inflight.delete(ctrl);
446
+ if (mgr.state === STATE.READY || mgr.state === STATE.STARTING) {
447
+ // Deterministic teardown → single resting state. This is NOT a contradiction:
448
+ // DEAD = transient "death detected" marker. An /inference failure means
449
+ // the contract is no longer satisfiable on the current child, so
450
+ // we record the death and abort/kill it via teardown().
451
+ // STOPPED = the durable resting state once teardown completes. teardown()
452
+ // nulls mgr.child/contract/runtimeKey and clears pid metadata, so
453
+ // STOPPED here is "awaiting recreate", indistinguishable from a
454
+ // clean stop. ensureReady() treats STOPPED-with-no-child as
455
+ // recreate-eligible (its READY/STARTING fast-paths fail and it
456
+ // falls through to startServer), so the NEXT ensureReady() rebuilds
457
+ // the SAME contract. There is no stuck-in-DEAD path: DEAD is only
458
+ // ever a momentary marker that always resolves to STOPPED.
459
+ mgr.state = STATE.DEAD; // transient: death detected
460
+ try { await teardown('transcribe-failure'); } catch {}
461
+ mgr.state = STATE.STOPPED; // resting: awaiting recreate
462
+ }
463
+ throw err;
464
+ } finally {
465
+ mgr.inflight.delete(ctrl);
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Stop the managed server: abort in-flight requests, terminate the child with
471
+ * deterministic SIGTERM → SIGKILL escalation, and settle to STOPPED.
472
+ */
473
+ export async function stopVoiceWhisperServer() {
474
+ if (mgr.state === STATE.STOPPED && !mgr.child) return;
475
+ mgr.state = STATE.STOPPING;
476
+ try { await teardown('explicit-stop'); } finally { mgr.state = STATE.STOPPED; }
477
+ }
@@ -0,0 +1,170 @@
1
+ export const TOOL_DEFS = [
2
+ {
3
+ name: "reply",
4
+ title: "Discord Reply",
5
+ annotations: { title: "Discord Reply", readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
6
+ description: "Reply on channel.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ chat_id: { type: "string" },
11
+ text: { type: "string" },
12
+ reply_to: { type: "string" },
13
+ files: {
14
+ type: "array",
15
+ items: { type: "string" }
16
+ },
17
+ embeds: {
18
+ type: "array",
19
+ items: { type: "object", additionalProperties: true }
20
+ },
21
+ components: {
22
+ type: "array",
23
+ items: { type: "object", additionalProperties: true }
24
+ }
25
+ },
26
+ required: ["chat_id", "text"]
27
+ }
28
+ },
29
+ {
30
+ name: "react",
31
+ title: "Reaction",
32
+ annotations: { title: "Reaction", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
33
+ description: "React to message.",
34
+ inputSchema: {
35
+ type: "object",
36
+ properties: {
37
+ chat_id: { type: "string" },
38
+ message_id: { type: "string" },
39
+ emoji: { type: "string" }
40
+ },
41
+ required: ["chat_id", "message_id", "emoji"]
42
+ }
43
+ },
44
+ {
45
+ name: "edit_message",
46
+ title: "Edit Message",
47
+ annotations: { title: "Edit Message", readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
48
+ description: "Edit bot message.",
49
+ inputSchema: {
50
+ type: "object",
51
+ properties: {
52
+ chat_id: { type: "string" },
53
+ message_id: { type: "string" },
54
+ text: { type: "string" },
55
+ embeds: {
56
+ type: "array",
57
+ items: { type: "object", additionalProperties: true }
58
+ },
59
+ components: {
60
+ type: "array",
61
+ items: { type: "object", additionalProperties: true }
62
+ }
63
+ },
64
+ required: ["chat_id", "message_id", "text"]
65
+ }
66
+ },
67
+ {
68
+ name: "download_attachment",
69
+ title: "Download Attachment",
70
+ annotations: { title: "Download Attachment", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
71
+ description: "Download attachments.",
72
+ inputSchema: {
73
+ type: "object",
74
+ properties: {
75
+ chat_id: { type: "string" },
76
+ message_id: { type: "string" }
77
+ },
78
+ required: ["chat_id", "message_id"]
79
+ }
80
+ },
81
+ {
82
+ name: "fetch",
83
+ title: "Fetch",
84
+ annotations: { title: "Fetch", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
85
+ description: "Discord-only: fetch recent messages from a Discord channel. Requires channel (id); optional limit (count). NOT for URLs or web pages — use web_fetch for those.",
86
+ inputSchema: {
87
+ type: "object",
88
+ properties: {
89
+ channel: { type: "string" },
90
+ limit: { type: "number" }
91
+ },
92
+ required: ["channel"]
93
+ }
94
+ },
95
+ {
96
+ name: "schedule_status",
97
+ title: "Schedule Status",
98
+ annotations: { title: "Schedule Status", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
99
+ description: "Show schedules.",
100
+ inputSchema: {
101
+ type: "object",
102
+ properties: {}
103
+ }
104
+ },
105
+ {
106
+ name: "trigger_schedule",
107
+ title: "Trigger Schedule",
108
+ annotations: { title: "Trigger Schedule", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
109
+ description: "Run a scheduled task immediately by name (fire it now). Requires name.",
110
+ inputSchema: {
111
+ type: "object",
112
+ properties: {
113
+ name: { type: "string" }
114
+ },
115
+ required: ["name"]
116
+ }
117
+ },
118
+ {
119
+ name: "schedule_control",
120
+ title: "Schedule Control",
121
+ annotations: { title: "Schedule Control", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
122
+ description: 'Defer or skip a schedule (not run it now): action=defer (push by `minutes`) | skip_today. Requires name + action.',
123
+ inputSchema: {
124
+ type: "object",
125
+ properties: {
126
+ name: { type: "string" },
127
+ action: { type: "string", enum: ["defer", "skip_today"] },
128
+ minutes: { type: "number" }
129
+ },
130
+ required: ["name", "action"]
131
+ }
132
+ },
133
+ {
134
+ name: "activate_channel_bridge",
135
+ title: "Activate Channel Bridge",
136
+ annotations: { title: "Activate Channel Bridge", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
137
+ description: "Set channel bridge active.",
138
+ inputSchema: {
139
+ type: "object",
140
+ properties: {
141
+ active: { type: "boolean" }
142
+ },
143
+ required: ["active"]
144
+ }
145
+ },
146
+ // memory and recall_memory tools are now provided by memory-service.mjs via MCP
147
+ {
148
+ name: "reload_config",
149
+ title: "Reload Config",
150
+ annotations: { title: "Reload Config", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
151
+ description: "Reload config.",
152
+ inputSchema: { type: "object", properties: {} }
153
+ },
154
+ {
155
+ name: "inject_command",
156
+ title: "Inject Claude Code Slash Command",
157
+ annotations: { title: "Inject Claude Code Slash Command", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
158
+ description: "Inject slash command.",
159
+ inputSchema: {
160
+ type: "object",
161
+ properties: {
162
+ command: {
163
+ type: "string",
164
+ enum: ["reload-plugins", "clear"]
165
+ }
166
+ },
167
+ required: ["command"]
168
+ }
169
+ }
170
+ ];