@phuetz/code-buddy 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/README.md +211 -163
  2. package/dist/agent/autonomous/agentic-coding-contract.d.ts +12 -12
  3. package/dist/agent/base-agent.d.ts +4 -0
  4. package/dist/agent/base-agent.js +6 -0
  5. package/dist/agent/codebuddy-agent.d.ts +1 -1
  6. package/dist/agent/codebuddy-agent.js +69 -14
  7. package/dist/agent/execution/agent-executor.js +122 -12
  8. package/dist/agent/execution/tool-dependency-graph.js +7 -0
  9. package/dist/agent/execution/tool-hooks.d.ts +6 -0
  10. package/dist/agent/execution/tool-hooks.js +13 -5
  11. package/dist/agent/execution/tool-selection-strategy.d.ts +11 -2
  12. package/dist/agent/execution/tool-selection-strategy.js +67 -10
  13. package/dist/agent/extended-thinking.d.ts +8 -0
  14. package/dist/agent/extended-thinking.js +27 -0
  15. package/dist/agent/facades/infrastructure-facade.d.ts +9 -2
  16. package/dist/agent/facades/infrastructure-facade.js +15 -6
  17. package/dist/agent/hermes-browser-backends.js +151 -11
  18. package/dist/agent/hermes-claw-migrate.d.ts +59 -0
  19. package/dist/agent/hermes-claw-migrate.js +427 -26
  20. package/dist/agent/hermes-parity-manifest.js +78 -71
  21. package/dist/agent/hermes-runtime-lifecycle.d.ts +67 -0
  22. package/dist/agent/hermes-runtime-lifecycle.js +468 -0
  23. package/dist/agent/middleware/index.d.ts +1 -0
  24. package/dist/agent/middleware/index.js +1 -0
  25. package/dist/agent/middleware/visual-validation-middleware.d.ts +17 -0
  26. package/dist/agent/middleware/visual-validation-middleware.js +67 -0
  27. package/dist/agent/model-benchmark.d.ts +77 -0
  28. package/dist/agent/model-benchmark.js +309 -0
  29. package/dist/agent/model-tier.d.ts +14 -0
  30. package/dist/agent/model-tier.js +71 -0
  31. package/dist/agent/multi-agent/agents/orchestrator-agent.js +17 -6
  32. package/dist/agent/multi-agent/multi-agent-system.js +3 -1
  33. package/dist/agent/multi-agent/session-fleet-bridge.d.ts +2 -0
  34. package/dist/agent/multi-agent/session-fleet-bridge.js +9 -1
  35. package/dist/agent/multi-agent/session-tools.js +22 -5
  36. package/dist/agent/reasoning/mcts.js +20 -6
  37. package/dist/agent/self-improvement/authored-artifact-gate.d.ts +18 -0
  38. package/dist/agent/self-improvement/authored-artifact-gate.js +42 -0
  39. package/dist/agent/self-improvement/authored-tool-runtime.d.ts +27 -0
  40. package/dist/agent/self-improvement/authored-tool-runtime.js +57 -0
  41. package/dist/agent/self-improvement/authored-tool-store.d.ts +24 -0
  42. package/dist/agent/self-improvement/authored-tool-store.js +57 -0
  43. package/dist/agent/self-improvement/llm-tool-proposer.d.ts +41 -0
  44. package/dist/agent/self-improvement/llm-tool-proposer.js +136 -0
  45. package/dist/agent/self-improvement/sandbox-scorer.d.ts +17 -0
  46. package/dist/agent/self-improvement/sandbox-scorer.js +43 -0
  47. package/dist/agent/self-improvement/self-knowledge.d.ts +8 -0
  48. package/dist/agent/self-improvement/self-knowledge.js +24 -0
  49. package/dist/agent/self-improvement/skill-benchmark.d.ts +9 -0
  50. package/dist/agent/self-improvement/skill-benchmark.js +22 -0
  51. package/dist/agent/self-improvement/skill-consolidator.d.ts +71 -0
  52. package/dist/agent/self-improvement/skill-consolidator.js +137 -0
  53. package/dist/agent/self-improvement/skill-engine.d.ts +42 -0
  54. package/dist/agent/self-improvement/skill-engine.js +87 -0
  55. package/dist/agent/self-improvement/skill-gate.d.ts +19 -0
  56. package/dist/agent/self-improvement/skill-gate.js +62 -0
  57. package/dist/agent/self-improvement/skill-mutator.d.ts +74 -0
  58. package/dist/agent/self-improvement/skill-mutator.js +223 -0
  59. package/dist/agent/self-improvement/skill-proposer.d.ts +40 -0
  60. package/dist/agent/self-improvement/skill-proposer.js +82 -0
  61. package/dist/agent/self-improvement/skill-types.d.ts +41 -0
  62. package/dist/agent/self-improvement/skill-types.js +13 -0
  63. package/dist/agent/self-improvement/tool-benchmark.d.ts +10 -0
  64. package/dist/agent/self-improvement/tool-benchmark.js +37 -0
  65. package/dist/agent/self-improvement/tool-engine.d.ts +54 -0
  66. package/dist/agent/self-improvement/tool-engine.js +101 -0
  67. package/dist/agent/self-improvement/tool-gate.d.ts +20 -0
  68. package/dist/agent/self-improvement/tool-gate.js +78 -0
  69. package/dist/agent/self-improvement/tool-proposer.d.ts +31 -0
  70. package/dist/agent/self-improvement/tool-proposer.js +34 -0
  71. package/dist/agent/self-improvement/tool-skill-mutator.d.ts +40 -0
  72. package/dist/agent/self-improvement/tool-skill-mutator.js +79 -0
  73. package/dist/agent/self-improvement/tool-types.d.ts +48 -0
  74. package/dist/agent/self-improvement/tool-types.js +9 -0
  75. package/dist/agent/self-improvement/types.d.ts +3 -1
  76. package/dist/agent/session-end-flush.d.ts +6 -1
  77. package/dist/agent/session-end-flush.js +20 -4
  78. package/dist/agent/streaming/streaming-handler.d.ts +7 -0
  79. package/dist/agent/streaming/streaming-handler.js +8 -1
  80. package/dist/agent/tool-executor.js +1 -0
  81. package/dist/agent/tool-handler.d.ts +5 -0
  82. package/dist/agent/tool-handler.js +133 -4
  83. package/dist/browser-automation/browser-operator-executor.js +15 -0
  84. package/dist/browser-automation/browser-use-runner.d.ts +96 -0
  85. package/dist/browser-automation/browser-use-runner.js +492 -0
  86. package/dist/browser-automation/camofox-runner.d.ts +107 -0
  87. package/dist/browser-automation/camofox-runner.js +287 -0
  88. package/dist/channels/dingtalk/index.js +3 -0
  89. package/dist/channels/discord/client.d.ts +9 -0
  90. package/dist/channels/discord/client.js +12 -0
  91. package/dist/channels/feishu/index.d.ts +167 -1
  92. package/dist/channels/feishu/index.js +432 -7
  93. package/dist/channels/gateway-lifecycle.d.ts +132 -0
  94. package/dist/channels/gateway-lifecycle.js +219 -0
  95. package/dist/channels/google-chat/index.d.ts +5 -1
  96. package/dist/channels/google-chat/index.js +5 -1
  97. package/dist/channels/imessage/index.d.ts +14 -0
  98. package/dist/channels/imessage/index.js +64 -17
  99. package/dist/channels/index.d.ts +4 -0
  100. package/dist/channels/index.js +4 -0
  101. package/dist/channels/irc/index.d.ts +73 -3
  102. package/dist/channels/irc/index.js +446 -11
  103. package/dist/channels/line/index.js +3 -0
  104. package/dist/channels/mattermost/index.d.ts +51 -4
  105. package/dist/channels/mattermost/index.js +303 -20
  106. package/dist/channels/nextcloud-talk/index.d.ts +89 -8
  107. package/dist/channels/nextcloud-talk/index.js +367 -16
  108. package/dist/channels/nostr/index.d.ts +121 -1
  109. package/dist/channels/nostr/index.js +396 -8
  110. package/dist/channels/ntfy/index.js +3 -0
  111. package/dist/channels/qq/index.js +3 -0
  112. package/dist/channels/slash-parity.d.ts +117 -0
  113. package/dist/channels/slash-parity.js +185 -0
  114. package/dist/channels/synology-chat/index.js +3 -0
  115. package/dist/channels/teams/index.d.ts +3 -0
  116. package/dist/channels/teams/index.js +3 -0
  117. package/dist/channels/telegram/client.d.ts +9 -0
  118. package/dist/channels/telegram/client.js +12 -0
  119. package/dist/channels/twilio-voice/index.js +3 -0
  120. package/dist/channels/webchat/index.d.ts +7 -1
  121. package/dist/channels/webchat/index.js +7 -1
  122. package/dist/channels/wecom/index.js +3 -0
  123. package/dist/channels/weixin/index.js +3 -0
  124. package/dist/channels/zalo/index.js +3 -0
  125. package/dist/cli/config-loader.js +1 -1
  126. package/dist/codebuddy/client.d.ts +30 -1
  127. package/dist/codebuddy/client.js +167 -20
  128. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +5 -0
  129. package/dist/codebuddy/providers/provider-chatgpt-responses.js +7 -1
  130. package/dist/codebuddy/providers/provider-gemini-native.js +1 -1
  131. package/dist/codebuddy/providers/provider-openai-compat.d.ts +5 -0
  132. package/dist/codebuddy/providers/provider-openai-compat.js +82 -2
  133. package/dist/codebuddy/tool-definitions/computer-control-tools.js +32 -2
  134. package/dist/codebuddy/tool-definitions/core-tools.js +3 -3
  135. package/dist/codebuddy/tool-definitions/index.d.ts +2 -1
  136. package/dist/codebuddy/tool-definitions/index.js +2 -1
  137. package/dist/codebuddy/tool-definitions/multimodal-tools.d.ts +1 -0
  138. package/dist/codebuddy/tool-definitions/multimodal-tools.js +44 -1
  139. package/dist/codebuddy/tool-definitions/windows-tools.d.ts +2 -0
  140. package/dist/codebuddy/tool-definitions/windows-tools.js +34 -0
  141. package/dist/codebuddy/tools.d.ts +8 -1
  142. package/dist/codebuddy/tools.js +67 -24
  143. package/dist/commands/cli/hermes-commands.js +108 -0
  144. package/dist/commands/cli/improve-command.js +123 -0
  145. package/dist/commands/cli/native-engine-commands.js +102 -3
  146. package/dist/commands/cli/utility-commands.js +45 -0
  147. package/dist/commands/enhanced-command-handler.js +2 -2
  148. package/dist/commands/goal-cli.d.ts +30 -0
  149. package/dist/commands/goal-cli.js +210 -27
  150. package/dist/commands/handlers/goal-handler.d.ts +9 -2
  151. package/dist/commands/handlers/goal-handler.js +36 -9
  152. package/dist/commands/handlers/infra-handlers.js +36 -0
  153. package/dist/commands/handlers/memory-handlers.js +96 -1
  154. package/dist/commands/handlers/missing-handlers.d.ts +1 -1
  155. package/dist/commands/handlers/missing-handlers.js +26 -3
  156. package/dist/commands/headless-slash.d.ts +2 -0
  157. package/dist/commands/headless-slash.js +12 -0
  158. package/dist/commands/llm-provider-resolution.d.ts +5 -3
  159. package/dist/commands/llm-provider-resolution.js +87 -33
  160. package/dist/commands/ollama.d.ts +25 -0
  161. package/dist/commands/ollama.js +100 -0
  162. package/dist/commands/provider.d.ts +5 -0
  163. package/dist/commands/provider.js +106 -55
  164. package/dist/commands/skills-cli/index.js +123 -0
  165. package/dist/commands/slash/builtin-commands.js +1 -1
  166. package/dist/commands/spec-next.js +2 -1
  167. package/dist/commands/spec-plan.js +5 -16
  168. package/dist/commands/tunnel.d.ts +2 -0
  169. package/dist/commands/tunnel.js +48 -0
  170. package/dist/companion/percepts.js +11 -1
  171. package/dist/config/config-resolver.d.ts +2 -1
  172. package/dist/config/config-resolver.js +54 -41
  173. package/dist/config/constants.d.ts +28 -0
  174. package/dist/config/constants.js +7 -0
  175. package/dist/config/env-schema.js +756 -0
  176. package/dist/config/model-tools.js +4 -4
  177. package/dist/config/toml-config.d.ts +16 -0
  178. package/dist/config/toml-config.js +3 -0
  179. package/dist/context/bootstrap-loader.js +6 -23
  180. package/dist/context/context-manager-v2.js +1 -0
  181. package/dist/context/import-directive-parser.d.ts +4 -0
  182. package/dist/context/import-directive-parser.js +51 -6
  183. package/dist/context/instruction-excludes.d.ts +30 -1
  184. package/dist/context/instruction-excludes.js +71 -1
  185. package/dist/context/jit-context.d.ts +8 -10
  186. package/dist/context/jit-context.js +28 -106
  187. package/dist/context/project-context.d.ts +90 -0
  188. package/dist/context/project-context.js +295 -0
  189. package/dist/daemon/autonomous-daemon.d.ts +1 -1
  190. package/dist/daemon/autonomous-daemon.js +3 -3
  191. package/dist/daemon/autonomous-loop.d.ts +31 -1
  192. package/dist/daemon/autonomous-loop.js +91 -4
  193. package/dist/daemon/colab-goal.js +9 -1
  194. package/dist/daemon/cron-agent-bridge.d.ts +12 -3
  195. package/dist/daemon/cron-agent-bridge.js +25 -9
  196. package/dist/desktop/codebuddy-engine-adapter.d.ts +32 -0
  197. package/dist/desktop/codebuddy-engine-adapter.js +257 -80
  198. package/dist/desktop/engine-adapter.d.ts +14 -0
  199. package/dist/desktop-automation/automation-manager.js +16 -0
  200. package/dist/desktop-automation/omniparser-runner.d.ts +63 -0
  201. package/dist/desktop-automation/omniparser-runner.js +115 -0
  202. package/dist/desktop-automation/smart-snapshot.d.ts +11 -0
  203. package/dist/desktop-automation/smart-snapshot.js +72 -5
  204. package/dist/doctor/index.js +40 -23
  205. package/dist/events/types.d.ts +11 -0
  206. package/dist/fleet/colab-store.js +14 -2
  207. package/dist/fleet/peer-session-bridge.js +50 -12
  208. package/dist/fleet/peer-tool-bridge.js +14 -0
  209. package/dist/goals/goal-decomposer.d.ts +27 -0
  210. package/dist/goals/goal-decomposer.js +293 -0
  211. package/dist/goals/goal-judge-client.d.ts +15 -0
  212. package/dist/goals/goal-judge-client.js +45 -0
  213. package/dist/goals/goal-judge.d.ts +2 -0
  214. package/dist/goals/goal-judge.js +35 -6
  215. package/dist/goals/goal-loop.d.ts +14 -0
  216. package/dist/goals/goal-loop.js +47 -2
  217. package/dist/goals/goal-manager.d.ts +6 -0
  218. package/dist/goals/goal-manager.js +67 -14
  219. package/dist/goals/goal-state.d.ts +13 -0
  220. package/dist/goals/goal-state.js +83 -13
  221. package/dist/goals/goal-store.d.ts +3 -0
  222. package/dist/goals/goal-store.js +31 -15
  223. package/dist/harness/contract.d.ts +28 -28
  224. package/dist/hooks/user-hooks.js +17 -3
  225. package/dist/identity/identity-manager.js +3 -2
  226. package/dist/index.js +481 -26
  227. package/dist/input/text-to-speech.d.ts +2 -6
  228. package/dist/input/text-to-speech.js +2 -27
  229. package/dist/integrations/screenpipe/screenpipe-client.d.ts +10 -0
  230. package/dist/integrations/screenpipe/screenpipe-client.js +12 -2
  231. package/dist/integrations/tailscale.d.ts +13 -0
  232. package/dist/integrations/tailscale.js +87 -34
  233. package/dist/knowledge/workspace-indexer.js +53 -9
  234. package/dist/mcp/client.js +1 -0
  235. package/dist/mcp/mcp-desktop-tools.d.ts +23 -0
  236. package/dist/mcp/mcp-desktop-tools.js +158 -0
  237. package/dist/mcp/mcp-resources.js +2 -3
  238. package/dist/mcp/mcp-server.js +2 -0
  239. package/dist/memory/enhanced-memory.d.ts +10 -0
  240. package/dist/memory/enhanced-memory.js +33 -2
  241. package/dist/memory/index.d.ts +3 -1
  242. package/dist/memory/index.js +4 -1
  243. package/dist/memory/memory-auto-proposer.d.ts +23 -0
  244. package/dist/memory/memory-auto-proposer.js +308 -0
  245. package/dist/memory/memory-candidate-queue.d.ts +92 -0
  246. package/dist/memory/memory-candidate-queue.js +261 -0
  247. package/dist/memory/persistent-memory.d.ts +45 -2
  248. package/dist/memory/persistent-memory.js +236 -40
  249. package/dist/ml/bayesian-qualifier.d.ts +1 -1
  250. package/dist/ml/bayesian-qualifier.js +35 -2
  251. package/dist/openclaw/gateway-bridge.js +5 -0
  252. package/dist/plugins/marketplace.d.ts +1 -0
  253. package/dist/plugins/marketplace.js +7 -0
  254. package/dist/prompts/execution-discipline.d.ts +14 -0
  255. package/dist/prompts/execution-discipline.js +29 -0
  256. package/dist/prompts/variation-injector.js +9 -2
  257. package/dist/protocols/acp/acp-agentic-runner.d.ts +7 -6
  258. package/dist/protocols/acp/acp-agentic-runner.js +115 -18
  259. package/dist/protocols/acp/acp-session-store.d.ts +23 -0
  260. package/dist/protocols/acp/acp-session-store.js +77 -0
  261. package/dist/protocols/acp/acp-stdio-server.d.ts +7 -0
  262. package/dist/protocols/acp/acp-stdio-server.js +71 -16
  263. package/dist/providers/active-llm-registry.d.ts +37 -0
  264. package/dist/providers/active-llm-registry.js +186 -0
  265. package/dist/providers/auxiliary-provider.d.ts +25 -0
  266. package/dist/providers/auxiliary-provider.js +192 -0
  267. package/dist/providers/codex-oauth.d.ts +1 -1
  268. package/dist/providers/codex-oauth.js +27 -4
  269. package/dist/providers/index.d.ts +3 -0
  270. package/dist/providers/index.js +4 -0
  271. package/dist/providers/provider-catalog.d.ts +70 -0
  272. package/dist/providers/provider-catalog.js +738 -0
  273. package/dist/providers/provider-fallback.d.ts +35 -0
  274. package/dist/providers/provider-fallback.js +171 -0
  275. package/dist/providers/xai-oauth.d.ts +128 -0
  276. package/dist/providers/xai-oauth.js +735 -0
  277. package/dist/scheduler/cron-scheduler.d.ts +15 -3
  278. package/dist/scheduler/cron-scheduler.js +53 -7
  279. package/dist/scheduler/script-runner.d.ts +2 -0
  280. package/dist/scheduler/script-runner.js +19 -4
  281. package/dist/scheduler/watchdog-handlers.js +7 -20
  282. package/dist/search/usearch-index.js +7 -2
  283. package/dist/security/tool-policy/tool-groups.js +2 -0
  284. package/dist/sensory/dreaming.d.ts +45 -0
  285. package/dist/sensory/dreaming.js +114 -0
  286. package/dist/sensory/heartbeat-scheduler.d.ts +38 -0
  287. package/dist/sensory/heartbeat-scheduler.js +72 -0
  288. package/dist/sensory/reactions.d.ts +24 -0
  289. package/dist/sensory/reactions.js +31 -0
  290. package/dist/sensory/screen-reaction.d.ts +23 -0
  291. package/dist/sensory/screen-reaction.js +59 -0
  292. package/dist/sensory/sensory-bridge.d.ts +23 -0
  293. package/dist/sensory/sensory-bridge.js +85 -0
  294. package/dist/sensory/sensory-memory.d.ts +20 -0
  295. package/dist/sensory/sensory-memory.js +39 -0
  296. package/dist/sensory/speech-reaction.d.ts +21 -0
  297. package/dist/sensory/speech-reaction.js +83 -0
  298. package/dist/sensory/vision-reaction.d.ts +31 -0
  299. package/dist/sensory/vision-reaction.js +74 -0
  300. package/dist/server/index.js +106 -2
  301. package/dist/server/routes/mobile.d.ts +12 -4
  302. package/dist/server/routes/mobile.js +116 -1
  303. package/dist/server/tls-config.d.ts +35 -0
  304. package/dist/server/tls-config.js +142 -0
  305. package/dist/server/tunnel-manager.d.ts +20 -0
  306. package/dist/server/tunnel-manager.js +58 -0
  307. package/dist/server/websocket/fleet-bridge.js +5 -0
  308. package/dist/services/prompt-builder.d.ts +11 -0
  309. package/dist/services/prompt-builder.js +124 -17
  310. package/dist/shared/engine-types.d.ts +15 -1
  311. package/dist/sidecar/sidecar-bridge.d.ts +1 -0
  312. package/dist/sidecar/sidecar-bridge.js +21 -10
  313. package/dist/skills/parser.js +3 -0
  314. package/dist/skills/skill-importer.d.ts +58 -0
  315. package/dist/skills/skill-importer.js +261 -0
  316. package/dist/skills/skill-sources.d.ts +20 -0
  317. package/dist/skills/skill-sources.js +102 -0
  318. package/dist/skills/types.d.ts +6 -0
  319. package/dist/spec/spec-store.js +8 -1
  320. package/dist/talk-mode/providers/audioreader-tts.d.ts +1 -1
  321. package/dist/talk-mode/providers/audioreader-tts.js +8 -8
  322. package/dist/templates/project-scaffolding.js +3 -2
  323. package/dist/themes/theme-schema.d.ts +10 -10
  324. package/dist/tools/application-profiles.js +38 -0
  325. package/dist/tools/bash/bash-tool.d.ts +1 -0
  326. package/dist/tools/bash/bash-tool.js +16 -2
  327. package/dist/tools/bash/command-validator.js +3 -0
  328. package/dist/tools/bash/rtk-rewrite.d.ts +16 -0
  329. package/dist/tools/bash/rtk-rewrite.js +100 -0
  330. package/dist/tools/bash/streaming-executor.js +14 -1
  331. package/dist/tools/computer-control-tool.d.ts +16 -1
  332. package/dist/tools/computer-control-tool.js +317 -5
  333. package/dist/tools/document-generator.d.ts +14 -0
  334. package/dist/tools/document-generator.js +79 -0
  335. package/dist/tools/execute-code-rpc-invoker.d.ts +26 -2
  336. package/dist/tools/execute-code-rpc-invoker.js +46 -5
  337. package/dist/tools/metadata.js +28 -0
  338. package/dist/tools/office-macro-tool.d.ts +10 -0
  339. package/dist/tools/office-macro-tool.js +93 -0
  340. package/dist/tools/register-tool-handler.d.ts +25 -0
  341. package/dist/tools/register-tool-handler.js +100 -0
  342. package/dist/tools/registry/index.d.ts +2 -1
  343. package/dist/tools/registry/index.js +2 -1
  344. package/dist/tools/registry/memory-tools.d.ts +20 -0
  345. package/dist/tools/registry/memory-tools.js +225 -3
  346. package/dist/tools/registry/multimodal-tools.js +2 -2
  347. package/dist/tools/registry/vision-tools.d.ts +33 -0
  348. package/dist/tools/registry/vision-tools.js +308 -6
  349. package/dist/tools/registry/windows-tools.d.ts +2 -0
  350. package/dist/tools/registry/windows-tools.js +47 -0
  351. package/dist/tools/registry.d.ts +6 -0
  352. package/dist/tools/registry.js +8 -0
  353. package/dist/tools/route-peer-tool.js +14 -0
  354. package/dist/tools/screenshot-tool.js +14 -2
  355. package/dist/tools/text-to-speech-tool.d.ts +1 -1
  356. package/dist/tools/text-to-speech-tool.js +2 -12
  357. package/dist/tools/tool-selector.js +3 -59
  358. package/dist/utils/config-validation/schema.d.ts +40 -7
  359. package/dist/utils/config-validation/schema.js +130 -1
  360. package/dist/utils/cost-tracker.js +21 -1
  361. package/dist/utils/disk-guard.d.ts +180 -0
  362. package/dist/utils/disk-guard.js +404 -0
  363. package/dist/utils/init-project.d.ts +7 -0
  364. package/dist/utils/init-project.js +37 -0
  365. package/dist/utils/interactive-setup.js +35 -2
  366. package/dist/utils/model-utils.js +11 -2
  367. package/dist/utils/output-sanitizer.js +11 -0
  368. package/dist/utils/provider-detector.d.ts +8 -7
  369. package/dist/utils/provider-detector.js +19 -77
  370. package/dist/utils/settings-manager.d.ts +19 -0
  371. package/dist/utils/token-display.d.ts +3 -3
  372. package/dist/utils/token-display.js +22 -4
  373. package/dist/wizard/onboarding.d.ts +39 -0
  374. package/dist/wizard/onboarding.js +433 -21
  375. package/dist/wizard/provider-onboarding.d.ts +8 -23
  376. package/dist/wizard/provider-onboarding.js +39 -158
  377. package/package.json +4 -2
  378. package/dist/plugins/provider-onboarding.d.ts +0 -23
  379. package/dist/plugins/provider-onboarding.js +0 -116
  380. package/dist/utils/kokoro-tts.d.ts +0 -8
  381. package/dist/utils/kokoro-tts.js +0 -67
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Screen reaction — on a `screen/change` event from the nervous-system daemon,
3
+ * record a companion percept and (optionally) run an injected analyzer (OCR /
4
+ * vision describe). DEBOUNCED, opt-in (`CODEBUDDY_SENSORY_SCREEN=true`),
5
+ * never-throws. The heavy OCR/describe is pluggable so we don't duplicate Code
6
+ * Buddy's existing OCR/vision; inject an analyzer to wire it.
7
+ *
8
+ * @module sensory/screen-reaction
9
+ */
10
+ import { getGlobalEventBus } from '../events/event-bus.js';
11
+ import { logger } from '../utils/logger.js';
12
+ import { perceptionOf } from './reactions.js';
13
+ export function wireScreenReaction(options = {}) {
14
+ const bus = getGlobalEventBus();
15
+ const debounceMs = options.debounceMs ?? Number(process.env.CODEBUDDY_SCREEN_DEBOUNCE_MS ?? 5000);
16
+ const now = options.now ?? (() => Date.now());
17
+ let lastAt = Number.NEGATIVE_INFINITY;
18
+ let inFlight = false;
19
+ const id = bus.on('sensory:perception', (evt) => {
20
+ const p = perceptionOf(evt);
21
+ if (p.modality !== 'screen' || p.kind !== 'change')
22
+ return;
23
+ const t = now();
24
+ if (t - lastAt < debounceMs) {
25
+ logger.info('[screen] change (debounced)');
26
+ return;
27
+ }
28
+ if (inFlight)
29
+ return; // a prior analyze() is still running — don't stampede
30
+ lastAt = t;
31
+ inFlight = true;
32
+ void (async () => {
33
+ try {
34
+ const analysis = options.analyzer ? await options.analyzer.analyze() : {};
35
+ const { recordCompanionPercept } = await import('../companion/percepts.js');
36
+ const score = p.payload?.score;
37
+ await recordCompanionPercept({
38
+ modality: 'screen',
39
+ source: 'sensory_screen_reaction',
40
+ summary: `Screen changed${analysis.description ? ` → ${analysis.description}` : ''}`,
41
+ confidence: 0.85,
42
+ payload: { score, description: analysis.description },
43
+ tags: ['screen', 'change'],
44
+ }, options.cwd ? { cwd: options.cwd } : {});
45
+ logger.info(`[screen] change recorded${analysis.description ? ` → ${analysis.description}` : ''}`);
46
+ }
47
+ catch (err) {
48
+ logger.warn(`[screen] reaction failed: ${err instanceof Error ? err.message : String(err)}`);
49
+ }
50
+ finally {
51
+ inFlight = false;
52
+ }
53
+ })();
54
+ });
55
+ return () => {
56
+ bus.off(id);
57
+ };
58
+ }
59
+ //# sourceMappingURL=screen-reaction.js.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Sensory bridge — the ingress for the Rust `buddy-sense` nervous-system daemon.
3
+ *
4
+ * A loopback-only WebSocket server that receives `SensoryEvent` JSON frames from
5
+ * the daemon and re-emits them onto Code Buddy's internal event bus as
6
+ * `sensory:perception` events. Reactions (reactions.ts) subscribe to the bus.
7
+ *
8
+ * Opt-in via `CODEBUDDY_SENSORY=true` (wired in `buddy server`). Loopback-only +
9
+ * optional shared token. Never throws on a malformed frame.
10
+ *
11
+ * @module sensory/sensory-bridge
12
+ */
13
+ export interface SensoryBridgeOptions {
14
+ host?: string;
15
+ port?: number;
16
+ /** If set, frames must carry a matching `token` field. */
17
+ token?: string;
18
+ }
19
+ export interface SensoryBridgeHandle {
20
+ port: number;
21
+ close(): Promise<void>;
22
+ }
23
+ export declare function startSensoryBridge(options?: SensoryBridgeOptions): SensoryBridgeHandle;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Sensory bridge — the ingress for the Rust `buddy-sense` nervous-system daemon.
3
+ *
4
+ * A loopback-only WebSocket server that receives `SensoryEvent` JSON frames from
5
+ * the daemon and re-emits them onto Code Buddy's internal event bus as
6
+ * `sensory:perception` events. Reactions (reactions.ts) subscribe to the bus.
7
+ *
8
+ * Opt-in via `CODEBUDDY_SENSORY=true` (wired in `buddy server`). Loopback-only +
9
+ * optional shared token. Never throws on a malformed frame.
10
+ *
11
+ * @module sensory/sensory-bridge
12
+ */
13
+ import { WebSocketServer } from 'ws';
14
+ import { getGlobalEventBus } from '../events/event-bus.js';
15
+ import { logger } from '../utils/logger.js';
16
+ /** The trust boundary: only known modalities pass; kinds are sanitized to a safe
17
+ * shape (no control chars / newlines, ≤64 chars) so a crafted local frame can't
18
+ * inject content downstream (e.g. into CODEBUDDY_MEMORY.md via dreaming). */
19
+ const KNOWN_MODALITIES = new Set(['audio', 'vision', 'screen', 'vital', 'ui']);
20
+ function sanitizeKind(kind) {
21
+ return kind.replace(/[^a-zA-Z0-9_:.-]/g, '').slice(0, 64);
22
+ }
23
+ export function startSensoryBridge(options = {}) {
24
+ const host = options.host ?? '127.0.0.1';
25
+ const port = options.port ?? Number(process.env.CODEBUDDY_SENSORY_PORT ?? 8129);
26
+ const token = options.token ?? process.env.CODEBUDDY_SENSORY_TOKEN;
27
+ const bus = getGlobalEventBus();
28
+ const wss = new WebSocketServer({ host, port });
29
+ wss.on('connection', (ws, req) => {
30
+ // Reject cross-origin (CSWSH): the Rust daemon sends NO Origin header; a
31
+ // browser page / extension always does. This stops a local web origin from
32
+ // injecting frames (and, with the camera reaction on, triggering the webcam).
33
+ if (req.headers.origin) {
34
+ ws.close();
35
+ return;
36
+ }
37
+ // Loopback-only: exact match, tolerating IPv4-mapped IPv6 (::ffff:127.0.0.1).
38
+ const remote = (req.socket.remoteAddress ?? '').replace(/^::ffff:/, '');
39
+ if (remote !== '127.0.0.1' && remote !== '::1') {
40
+ ws.close();
41
+ return;
42
+ }
43
+ logger.info('[sensory] daemon connected');
44
+ ws.on('message', (data) => {
45
+ let frame;
46
+ try {
47
+ frame = JSON.parse(String(data));
48
+ }
49
+ catch {
50
+ return; // ignore malformed
51
+ }
52
+ if (token && frame.token !== token)
53
+ return;
54
+ if (!frame.modality || !frame.kind)
55
+ return;
56
+ // Validate the string fields at the trust boundary (not just the numbers):
57
+ // unknown modality is dropped, kind is sanitized — blocks memory injection.
58
+ if (!KNOWN_MODALITIES.has(frame.modality))
59
+ return;
60
+ const kind = sanitizeKind(frame.kind);
61
+ if (!kind)
62
+ return;
63
+ // Validate/clamp numeric fields so a malformed frame can't poison downstream.
64
+ const salience = Number.isFinite(frame.salience) ? Math.max(0, Math.min(255, Math.round(frame.salience))) : 0;
65
+ const tsMs = Number.isFinite(frame.ts_ms) && frame.ts_ms >= 0 ? frame.ts_ms : undefined;
66
+ bus.emit('sensory:perception', {
67
+ source: 'buddy-sense',
68
+ metadata: {
69
+ modality: frame.modality,
70
+ kind,
71
+ salience,
72
+ tsMs,
73
+ payload: frame.payload,
74
+ },
75
+ });
76
+ });
77
+ });
78
+ wss.on('error', (err) => logger.warn(`[sensory] bridge error: ${err instanceof Error ? err.message : String(err)}`));
79
+ logger.info(`[sensory] bridge listening on ws://${host}:${port}`);
80
+ return {
81
+ port,
82
+ close: () => new Promise((resolve) => wss.close(() => resolve())),
83
+ };
84
+ }
85
+ //# sourceMappingURL=sensory-bridge.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Short-term sensory memory — a bounded rolling buffer of recent perceptions
3
+ * (the nervous system's working memory). Reactions push every perception here;
4
+ * the dreaming consolidator drains + summarizes it into long-term memory every N
5
+ * heartbeats (like OpenClaw's dreaming: short-term recall → consolidated store).
6
+ *
7
+ * @module sensory/sensory-memory
8
+ */
9
+ import type { Perception } from './reactions.js';
10
+ export declare class SensoryMemory {
11
+ private readonly cap;
12
+ private buf;
13
+ constructor(cap?: number);
14
+ push(p: Perception): void;
15
+ snapshot(): Perception[];
16
+ /** Take everything and clear (consolidation consumes the window). */
17
+ drain(): Perception[];
18
+ size(): number;
19
+ }
20
+ export declare function getSensoryMemory(): SensoryMemory;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Short-term sensory memory — a bounded rolling buffer of recent perceptions
3
+ * (the nervous system's working memory). Reactions push every perception here;
4
+ * the dreaming consolidator drains + summarizes it into long-term memory every N
5
+ * heartbeats (like OpenClaw's dreaming: short-term recall → consolidated store).
6
+ *
7
+ * @module sensory/sensory-memory
8
+ */
9
+ export class SensoryMemory {
10
+ cap;
11
+ buf = [];
12
+ constructor(cap = 1000) {
13
+ this.cap = cap;
14
+ }
15
+ push(p) {
16
+ this.buf.push(p);
17
+ if (this.buf.length > this.cap)
18
+ this.buf.shift();
19
+ }
20
+ snapshot() {
21
+ return [...this.buf];
22
+ }
23
+ /** Take everything and clear (consolidation consumes the window). */
24
+ drain() {
25
+ const out = this.buf;
26
+ this.buf = [];
27
+ return out;
28
+ }
29
+ size() {
30
+ return this.buf.length;
31
+ }
32
+ }
33
+ let singleton;
34
+ export function getSensoryMemory() {
35
+ if (!singleton)
36
+ singleton = new SensoryMemory();
37
+ return singleton;
38
+ }
39
+ //# sourceMappingURL=sensory-memory.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Speech reaction — closes the perception→cognition loop. On a `speech_end` event
3
+ * that carries the source WAV (the daemon tags it), transcribe the utterance (STT)
4
+ * and record what was heard as a `hearing` percept; an optional `onHeard` hook can
5
+ * drive further action (a turn, a command). DEBOUNCED (one transcription per
6
+ * utterance — the energy VAD over-segments), opt-in (`CODEBUDDY_SENSORY_SPEECH=true`),
7
+ * injectable transcriber, never-throws.
8
+ *
9
+ * @module sensory/speech-reaction
10
+ */
11
+ export type Transcriber = (wav: string) => Promise<string>;
12
+ export interface SpeechReactionOptions {
13
+ /** Injectable STT (tests / custom). Default: faster-whisper via python ($0). */
14
+ transcriber?: Transcriber;
15
+ debounceMs?: number;
16
+ cwd?: string;
17
+ now?: () => number;
18
+ /** Action hook for the transcript (e.g. trigger an agent turn). */
19
+ onHeard?: (text: string) => void | Promise<void>;
20
+ }
21
+ export declare function wireSpeechReaction(options?: SpeechReactionOptions): () => void;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Speech reaction — closes the perception→cognition loop. On a `speech_end` event
3
+ * that carries the source WAV (the daemon tags it), transcribe the utterance (STT)
4
+ * and record what was heard as a `hearing` percept; an optional `onHeard` hook can
5
+ * drive further action (a turn, a command). DEBOUNCED (one transcription per
6
+ * utterance — the energy VAD over-segments), opt-in (`CODEBUDDY_SENSORY_SPEECH=true`),
7
+ * injectable transcriber, never-throws.
8
+ *
9
+ * @module sensory/speech-reaction
10
+ */
11
+ import { getGlobalEventBus } from '../events/event-bus.js';
12
+ import { logger } from '../utils/logger.js';
13
+ import { perceptionOf } from './reactions.js';
14
+ /** Default transcriber: local faster-whisper (base), best-effort, $0. */
15
+ async function defaultTranscribe(wav) {
16
+ const { spawn } = await import('child_process');
17
+ const model = process.env.CODEBUDDY_SPEECH_MODEL ?? 'base';
18
+ const py = [
19
+ 'import sys',
20
+ 'from faster_whisper import WhisperModel',
21
+ `m = WhisperModel(${JSON.stringify(model)}, device='cpu', compute_type='int8')`,
22
+ 'segs, _ = m.transcribe(sys.argv[1])',
23
+ "print(' '.join(s.text for s in segs).strip())",
24
+ ].join('\n');
25
+ return new Promise((resolve) => {
26
+ const proc = spawn('python3', ['-c', py, wav], { stdio: ['ignore', 'pipe', 'ignore'] });
27
+ let out = '';
28
+ proc.stdout.on('data', (d) => (out += String(d)));
29
+ proc.on('close', () => resolve(out.trim()));
30
+ proc.on('error', () => resolve(''));
31
+ });
32
+ }
33
+ export function wireSpeechReaction(options = {}) {
34
+ const bus = getGlobalEventBus();
35
+ const debounceMs = options.debounceMs ?? Number(process.env.CODEBUDDY_SPEECH_DEBOUNCE_MS ?? 4000);
36
+ const now = options.now ?? (() => Date.now());
37
+ const transcribe = options.transcriber ?? defaultTranscribe;
38
+ let lastAt = Number.NEGATIVE_INFINITY;
39
+ let inFlight = false;
40
+ const id = bus.on('sensory:perception', (evt) => {
41
+ const p = perceptionOf(evt);
42
+ if (p.modality !== 'audio' || p.kind !== 'speech_end')
43
+ return;
44
+ const wav = p.payload?.wav;
45
+ if (!wav)
46
+ return; // no audio to transcribe (e.g. a live-mic path not yet wired)
47
+ const t = now();
48
+ if (t - lastAt < debounceMs)
49
+ return; // one transcription per utterance
50
+ if (inFlight)
51
+ return; // a prior STT (faster-whisper, seconds) is still running
52
+ lastAt = t;
53
+ inFlight = true;
54
+ void (async () => {
55
+ try {
56
+ const text = await transcribe(wav);
57
+ if (!text)
58
+ return;
59
+ const { recordCompanionPercept } = await import('../companion/percepts.js');
60
+ await recordCompanionPercept({
61
+ modality: 'hearing',
62
+ source: 'sensory_speech_reaction',
63
+ summary: `Heard: ${text}`,
64
+ confidence: 0.8,
65
+ payload: { text, wav },
66
+ tags: ['speech', 'stt'],
67
+ }, options.cwd ? { cwd: options.cwd } : {});
68
+ logger.info(`[speech] heard → ${text}`);
69
+ await options.onHeard?.(text);
70
+ }
71
+ catch (err) {
72
+ logger.warn(`[speech] reaction failed: ${err instanceof Error ? err.message : String(err)}`);
73
+ }
74
+ finally {
75
+ inFlight = false;
76
+ }
77
+ })();
78
+ });
79
+ return () => {
80
+ bus.off(id);
81
+ };
82
+ }
83
+ //# sourceMappingURL=speech-reaction.js.map
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Vision reaction — on a `vision/motion` event from the nervous-system daemon,
3
+ * run `camera_analyze` (capture a frame + describe it with a local gemma vision
4
+ * model) and record a companion percept. DEBOUNCED (gemma is heavy: 3–10s warm),
5
+ * opt-in (`CODEBUDDY_SENSORY_CAMERA=true`), best-effort, never-throws.
6
+ *
7
+ * @module sensory/vision-reaction
8
+ */
9
+ export interface VisionAnalysis {
10
+ success: boolean;
11
+ description?: string;
12
+ imagePath?: string;
13
+ }
14
+ export interface VisionAnalyzer {
15
+ analyze(prompt: string): Promise<VisionAnalysis>;
16
+ }
17
+ export interface VisionReactionOptions {
18
+ /** Injectable analyzer (tests / custom). Defaults to camera_analyze (local gemma). */
19
+ analyzer?: VisionAnalyzer;
20
+ debounceMs?: number;
21
+ cwd?: string;
22
+ now?: () => number;
23
+ }
24
+ /** Security invariant (pure + testable): the camera reaction may only be wired
25
+ * when explicitly enabled AND a shared token is set — a crafted local frame can
26
+ * trigger the webcam, so an unauthenticated bridge must not be able to. */
27
+ export declare function shouldWireVisionReaction(env: {
28
+ camera?: string;
29
+ token?: string;
30
+ }): boolean;
31
+ export declare function wireVisionReaction(options?: VisionReactionOptions): () => void;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Vision reaction — on a `vision/motion` event from the nervous-system daemon,
3
+ * run `camera_analyze` (capture a frame + describe it with a local gemma vision
4
+ * model) and record a companion percept. DEBOUNCED (gemma is heavy: 3–10s warm),
5
+ * opt-in (`CODEBUDDY_SENSORY_CAMERA=true`), best-effort, never-throws.
6
+ *
7
+ * @module sensory/vision-reaction
8
+ */
9
+ import { getGlobalEventBus } from '../events/event-bus.js';
10
+ import { logger } from '../utils/logger.js';
11
+ import { perceptionOf } from './reactions.js';
12
+ /** Default analyzer: Code Buddy's camera_analyze tool (ffmpeg capture + local gemma). */
13
+ async function defaultAnalyze(prompt) {
14
+ const { CameraAnalyzeTool } = await import('../tools/registry/vision-tools.js');
15
+ const tool = new CameraAnalyzeTool({});
16
+ const result = await tool.execute({ prompt });
17
+ const data = result.data;
18
+ return { success: result.success, description: data?.description ?? result.output, imagePath: data?.imagePath };
19
+ }
20
+ /** Security invariant (pure + testable): the camera reaction may only be wired
21
+ * when explicitly enabled AND a shared token is set — a crafted local frame can
22
+ * trigger the webcam, so an unauthenticated bridge must not be able to. */
23
+ export function shouldWireVisionReaction(env) {
24
+ return env.camera === 'true' && Boolean(env.token);
25
+ }
26
+ export function wireVisionReaction(options = {}) {
27
+ const bus = getGlobalEventBus();
28
+ const debounceMs = options.debounceMs ?? Number(process.env.CODEBUDDY_VISION_DEBOUNCE_MS ?? 8000);
29
+ const now = options.now ?? (() => Date.now());
30
+ const analyzer = options.analyzer ?? { analyze: defaultAnalyze };
31
+ let lastAt = Number.NEGATIVE_INFINITY;
32
+ let inFlight = false;
33
+ const id = bus.on('sensory:perception', (evt) => {
34
+ const p = perceptionOf(evt);
35
+ if (p.modality !== 'vision' || p.kind !== 'motion')
36
+ return;
37
+ const t = now();
38
+ if (t - lastAt < debounceMs) {
39
+ logger.info('[vision] motion (debounced — analysis throttled)');
40
+ return;
41
+ }
42
+ if (inFlight)
43
+ return; // a prior analyze() (ffmpeg+gemma, 3–10s) is still running
44
+ lastAt = t;
45
+ inFlight = true;
46
+ void (async () => {
47
+ try {
48
+ const res = await analyzer.analyze('Motion detected — describe the scene in one sentence.');
49
+ if (!res.success)
50
+ return;
51
+ const { recordCompanionPercept } = await import('../companion/percepts.js');
52
+ await recordCompanionPercept({
53
+ modality: 'vision',
54
+ source: 'sensory_motion_reaction',
55
+ summary: `Motion → ${res.description ?? '(no description)'}`,
56
+ confidence: 0.9,
57
+ payload: { description: res.description, imagePath: res.imagePath },
58
+ tags: ['motion', 'camera', 'vision'],
59
+ }, options.cwd ? { cwd: options.cwd } : {});
60
+ logger.info(`[vision] motion analyzed → ${res.description ?? '(no description)'}`);
61
+ }
62
+ catch (err) {
63
+ logger.warn(`[vision] reaction failed: ${err instanceof Error ? err.message : String(err)}`);
64
+ }
65
+ finally {
66
+ inFlight = false;
67
+ }
68
+ })();
69
+ });
70
+ return () => {
71
+ bus.off(id);
72
+ };
73
+ }
74
+ //# sourceMappingURL=vision-reaction.js.map
@@ -14,6 +14,8 @@ import { createRequire } from 'module';
14
14
  import express from 'express';
15
15
  import cors from 'cors';
16
16
  import { createServer } from 'http';
17
+ import { createServer as createHttpsServer, Server as HttpsServer } from 'https';
18
+ import { resolveServerTlsOptions } from './tls-config.js';
17
19
  const _require = createRequire(import.meta.url);
18
20
  let SERVER_VERSION = '0.0.0';
19
21
  try {
@@ -77,7 +79,10 @@ export function getServerBaseUrl(server, config) {
77
79
  const port = typeof address === 'object' && address ? address.port : config.port;
78
80
  const publicHost = config.host === '0.0.0.0' || config.host === '::' ? '127.0.0.1' : config.host;
79
81
  const host = publicHost.includes(':') && !publicHost.startsWith('[') ? `[${publicHost}]` : publicHost;
80
- return `http://${host}:${port}`;
82
+ // Report https:// when the server was constructed with TLS so the logged URL
83
+ // isn't a lie. Default (plain http.Server) stays http://.
84
+ const scheme = server instanceof HttpsServer ? 'https' : 'http';
85
+ return `${scheme}://${host}:${port}`;
81
86
  }
82
87
  // Default configuration
83
88
  const DEFAULT_CONFIG = {
@@ -839,6 +844,10 @@ function createApp(config) {
839
844
  app.use(errorHandler);
840
845
  return app;
841
846
  }
847
+ /** Guards the sensory layer against double-wiring on a second in-process start. */
848
+ let sensoryWired = false;
849
+ /** Teardown fns for the sensory layer (bridge close, listener unsubscribes, scheduler stop). */
850
+ let sensoryTeardown = [];
842
851
  /**
843
852
  * Start the server
844
853
  */
@@ -878,7 +887,17 @@ export async function startServer(userConfig = {}) {
878
887
  /* prometheus exporter optional */
879
888
  }
880
889
  const app = createApp(config);
881
- const server = createServer(app);
890
+ // Optional TLS: serve the API (including /api/mobile) over HTTPS when
891
+ // CODEBUDDY_HTTPS / CODEBUDDY_MOBILE_TLS is set so the mobile-supervision
892
+ // endpoint can be exposed off-device securely. Default (no env) is plain HTTP,
893
+ // byte-for-byte unchanged. resolveServerTlsOptions() throws (never silently
894
+ // downgrades) if TLS is requested but cannot be satisfied. The https.Server
895
+ // surface (listen/address/on('upgrade')/close) matches http.Server at runtime,
896
+ // so we keep the `HttpServer` type to avoid widening downstream signatures.
897
+ const tlsOptions = resolveServerTlsOptions();
898
+ const server = tlsOptions
899
+ ? createHttpsServer(tlsOptions, app)
900
+ : createServer(app);
882
901
  const startChannelBridge = async (baseUrl) => {
883
902
  try {
884
903
  const { getChannelManager } = await import('../channels/index.js');
@@ -1003,6 +1022,80 @@ export async function startServer(userConfig = {}) {
1003
1022
  logger.info(`Metrics Dashboard: ${baseUrl}/api/metrics/dashboard`);
1004
1023
  logger.info(`Docs: ${baseUrl}/api/docs`);
1005
1024
  logger.info(`WebSocket: ${config.websocketEnabled ? 'Enabled (/ws)' : 'Disabled'}`);
1025
+ // Sensory nervous-system bridge (opt-in): ingress for the Rust buddy-sense
1026
+ // daemon → internal event bus → reactions. Loopback-only.
1027
+ if (process.env.CODEBUDDY_SENSORY === 'true' && !sensoryWired) {
1028
+ sensoryWired = true; // wire once per process (a 2nd start would double listeners + re-bind the port)
1029
+ try {
1030
+ const { startSensoryBridge } = await import('../sensory/sensory-bridge.js');
1031
+ const { wireSensoryReactions } = await import('../sensory/reactions.js');
1032
+ const { getHeartbeatScheduler } = await import('../sensory/heartbeat-scheduler.js');
1033
+ const sensoryBridgeHandle = startSensoryBridge();
1034
+ const unwireReactions = wireSensoryReactions();
1035
+ sensoryTeardown.push(() => sensoryBridgeHandle.close(), unwireReactions);
1036
+ // Vision reaction (opt-in) — vision/motion → camera_analyze (local gemma).
1037
+ // Requires a shared token: a frame can trigger the webcam, so refuse to
1038
+ // wire it on an unauthenticated bridge.
1039
+ const sensoryToken = process.env.CODEBUDDY_SENSORY_TOKEN;
1040
+ {
1041
+ const { shouldWireVisionReaction, wireVisionReaction } = await import('../sensory/vision-reaction.js');
1042
+ if (shouldWireVisionReaction({ camera: process.env.CODEBUDDY_SENSORY_CAMERA, token: sensoryToken })) {
1043
+ sensoryTeardown.push(wireVisionReaction());
1044
+ logger.info('Sensory vision reaction: Enabled (vision/motion → camera_analyze)');
1045
+ }
1046
+ else if (process.env.CODEBUDDY_SENSORY_CAMERA === 'true') {
1047
+ logger.warn('Sensory vision reaction NOT enabled: set CODEBUDDY_SENSORY_TOKEN to allow camera triggering.');
1048
+ }
1049
+ }
1050
+ // Screen reaction (opt-in) — also token-gated (an injected analyzer could capture the desktop).
1051
+ if (process.env.CODEBUDDY_SENSORY_SCREEN === 'true') {
1052
+ if (sensoryToken) {
1053
+ const { wireScreenReaction } = await import('../sensory/screen-reaction.js');
1054
+ sensoryTeardown.push(wireScreenReaction());
1055
+ logger.info('Sensory screen reaction: Enabled (screen/change → percept)');
1056
+ }
1057
+ else {
1058
+ logger.warn('Sensory screen reaction NOT enabled: set CODEBUDDY_SENSORY_TOKEN.');
1059
+ }
1060
+ }
1061
+ // Speech reaction (opt-in) — speech_end → STT → 'hearing' percept (+ onHeard hook).
1062
+ if (process.env.CODEBUDDY_SENSORY_SPEECH === 'true') {
1063
+ const { wireSpeechReaction } = await import('../sensory/speech-reaction.js');
1064
+ sensoryTeardown.push(wireSpeechReaction());
1065
+ logger.info('Sensory speech reaction: Enabled (speech_end → STT → percept)');
1066
+ }
1067
+ // Privacy: camera/screen descriptions land in percepts.jsonl — warn if not encrypted at rest.
1068
+ if ((process.env.CODEBUDDY_SENSORY_CAMERA === 'true' || process.env.CODEBUDDY_SENSORY_SCREEN === 'true') &&
1069
+ !process.env.CODEBUDDY_COMPANION_ENCRYPTION_KEY &&
1070
+ !process.env.CODEBUDDY_MEMORY_KEY) {
1071
+ logger.warn('Sensory camera/screen percepts are written UNENCRYPTED — set CODEBUDDY_COMPANION_ENCRYPTION_KEY to encrypt scene/screen descriptions at rest.');
1072
+ }
1073
+ // Heartbeat pacemaker — heartbeats trigger periodic processing (every N beats).
1074
+ const heart = getHeartbeatScheduler();
1075
+ const everyBeats = Math.max(1, Number(process.env.CODEBUDDY_HEARTBEAT_EVERY ?? 10));
1076
+ heart.register({
1077
+ name: 'pacemaker-tick',
1078
+ everyBeats,
1079
+ handler: (ctx) => logger.info(`[heartbeat] pacemaker tick — beat ${ctx.beat} (load ${ctx.load1 ?? '?'})`),
1080
+ });
1081
+ // Dreaming — consolidate short-term sensory memory every N beats.
1082
+ const dreamEvery = Math.max(1, Number(process.env.CODEBUDDY_DREAM_EVERY ?? 30));
1083
+ heart.register({
1084
+ name: 'dreaming',
1085
+ everyBeats: dreamEvery,
1086
+ handler: async () => {
1087
+ const { runDreamingPass } = await import('../sensory/dreaming.js');
1088
+ await runDreamingPass();
1089
+ },
1090
+ });
1091
+ heart.start();
1092
+ sensoryTeardown.push(() => heart.stop());
1093
+ logger.info(`Sensory bridge: Enabled (buddy-sense → event bus; heartbeat treatments every ${everyBeats} beats)`);
1094
+ }
1095
+ catch (err) {
1096
+ logger.warn(`Sensory bridge failed to start: ${err instanceof Error ? err.message : String(err)}`);
1097
+ }
1098
+ }
1006
1099
  logger.info(`Auth: ${config.authEnabled ? 'Enabled' : 'Disabled'}`);
1007
1100
  if (!isLoopbackHost(config.host)) {
1008
1101
  logger.warn(`Server is bound to ${config.host} (non-loopback) and is reachable from the network. ` +
@@ -1057,6 +1150,17 @@ export async function startServer(userConfig = {}) {
1057
1150
  * Stop the server gracefully
1058
1151
  */
1059
1152
  export async function stopServer(server) {
1153
+ // Tear down the sensory layer (WS bridge, bus listeners, heartbeat scheduler) so
1154
+ // an in-process restart doesn't leak listeners or EADDRINUSE the bridge port.
1155
+ for (const teardown of sensoryTeardown.splice(0)) {
1156
+ try {
1157
+ await teardown();
1158
+ }
1159
+ catch {
1160
+ /* never throw on shutdown */
1161
+ }
1162
+ }
1163
+ sensoryWired = false;
1060
1164
  return new Promise((resolve, reject) => {
1061
1165
  // Phase (d).9 — cancel the heartbeat timer so it doesn't keep
1062
1166
  // emitting against a half-shut server. Idempotent.
@@ -1,4 +1,5 @@
1
1
  import { Request, Response, NextFunction } from 'express';
2
+ import { EventEmitter } from 'events';
2
3
  export declare const mobileRouter: import("express-serve-static-core").Router;
3
4
  export declare let activePairingCode: string;
4
5
  export declare let activePairingCodeExpiresAt: number;
@@ -10,11 +11,11 @@ export declare const activeTokens: Map<string, {
10
11
  * A follow-up prompt review draft. The mobile device (or a draft-only call) can
11
12
  * *propose* a prompt, but it never executes: it lands here as
12
13
  * `needs_local_operator` and only a local operator (loopback) can `approve` or
13
- * `cancel` it. Approval is a review-gate marker — it records who/when but never
14
- * dispatches work, matching the gateway contract's `autoDispatch:false` /
15
- * `remoteExecutionDisabled:true` invariants.
14
+ * `cancel` it. When `CODEBUDDY_MOBILE_ALLOW_DISPATCH=true`, an approved draft
15
+ * can optionally be dispatched to the agent for execution. When the env var is
16
+ * OFF (default), approval remains a review-gate marker only.
16
17
  */
17
- export type FollowupDraftStatus = 'needs_local_operator' | 'approved' | 'cancelled';
18
+ export type FollowupDraftStatus = 'needs_local_operator' | 'approved' | 'cancelled' | 'dispatched' | 'completed' | 'failed';
18
19
  export interface FollowupDraft {
19
20
  id: string;
20
21
  prompt: string;
@@ -24,9 +25,16 @@ export interface FollowupDraft {
24
25
  approvedBy?: string;
25
26
  approvedAt?: number;
26
27
  cancelledAt?: number;
28
+ dispatchedAt?: number;
29
+ completedAt?: number;
30
+ failedAt?: number;
31
+ failureReason?: string;
27
32
  [key: string]: unknown;
28
33
  }
29
34
  export declare const followupDrafts: FollowupDraft[];
35
+ export declare const mobileEventBus: EventEmitter<[never]>;
36
+ /** Check whether dispatch is enabled via environment variable. */
37
+ export declare function isDispatchEnabled(): boolean;
30
38
  /**
31
39
  * Decide whether a request originates from the loopback interface. We read the
32
40
  * raw socket address (`req.socket.remoteAddress`) on purpose: `req.ip` and the