@phuetz/code-buddy 2.0.0 → 2.1.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 (403) hide show
  1. package/README.fr.md +56 -0
  2. package/README.md +73 -16
  3. package/codebuddy-runtime.json +5 -5
  4. package/dist/agent/codebuddy-agent.d.ts +18 -0
  5. package/dist/agent/codebuddy-agent.js +45 -8
  6. package/dist/agent/execution/agent-executor.d.ts +0 -9
  7. package/dist/agent/execution/agent-executor.js +165 -12
  8. package/dist/agent/execution/context-pipeline.d.ts +9 -0
  9. package/dist/agent/execution/context-pipeline.js +64 -2
  10. package/dist/agent/execution/tool-loop-guard.d.ts +76 -0
  11. package/dist/agent/execution/tool-loop-guard.js +165 -0
  12. package/dist/agent/execution/tool-selection-strategy.js +19 -2
  13. package/dist/agent/facades/session-facade.d.ts +0 -6
  14. package/dist/agent/facades/session-facade.js +0 -45
  15. package/dist/agent/hermes-claw-migrate.d.ts +0 -21
  16. package/dist/agent/hermes-claw-migrate.js +9 -4
  17. package/dist/agent/hermes-protocol-gateways.js +8 -1
  18. package/dist/agent/repo-profiler.js +17 -4
  19. package/dist/agent/self-improvement/authored-tool-runtime.js +10 -2
  20. package/dist/agent/self-improvement/evolution/ast-novelty.d.ts +5 -0
  21. package/dist/agent/self-improvement/evolution/ast-novelty.js +58 -23
  22. package/dist/agent/self-improvement/evolution/code-variant-store.d.ts +3 -0
  23. package/dist/agent/self-improvement/evolution/evolution-engine.js +2 -0
  24. package/dist/agent/self-improvement/evolution/protected-paths.js +6 -0
  25. package/dist/agent/self-improvement/evolution/variant-fitness.d.ts +13 -2
  26. package/dist/agent/self-improvement/evolution/variant-fitness.js +184 -58
  27. package/dist/agent/self-improvement/evolution/worktree-scorer.d.ts +2 -0
  28. package/dist/agent/self-improvement/evolution/worktree-scorer.js +15 -0
  29. package/dist/agent/self-improvement/evolutionary-archive.d.ts +2 -1
  30. package/dist/agent/self-improvement/evolutionary-archive.js +34 -5
  31. package/dist/agent/self-improvement/proposal-store.d.ts +1 -0
  32. package/dist/agent/self-improvement/proposal-store.js +79 -17
  33. package/dist/agent/self-improvement/skill-apply-journal.d.ts +44 -0
  34. package/dist/agent/self-improvement/skill-apply-journal.js +178 -0
  35. package/dist/agent/self-improvement/skill-behavior-benchmark.d.ts +3 -0
  36. package/dist/agent/self-improvement/skill-behavior-benchmark.js +19 -0
  37. package/dist/agent/self-improvement/skill-behavior-gate.d.ts +34 -0
  38. package/dist/agent/self-improvement/skill-behavior-gate.js +96 -0
  39. package/dist/agent/self-improvement/skill-engine.d.ts +22 -0
  40. package/dist/agent/self-improvement/skill-engine.js +385 -13
  41. package/dist/agent/self-improvement/skill-gate.d.ts +2 -0
  42. package/dist/agent/self-improvement/skill-gate.js +17 -2
  43. package/dist/agent/self-improvement/skill-mutator.d.ts +6 -1
  44. package/dist/agent/self-improvement/skill-mutator.js +51 -15
  45. package/dist/agent/self-improvement/skill-types.d.ts +2 -1
  46. package/dist/agent/self-improvement/types.d.ts +11 -0
  47. package/dist/agent/tool-handler.d.ts +1 -1
  48. package/dist/agent/tool-handler.js +28 -9
  49. package/dist/auth/profile-manager.js +1 -1
  50. package/dist/channels/companion-channel-turn.d.ts +53 -4
  51. package/dist/channels/companion-channel-turn.js +218 -16
  52. package/dist/channels/slash-parity.d.ts +4 -6
  53. package/dist/channels/slash-parity.js +9 -44
  54. package/dist/cli/session-commands.d.ts +9 -0
  55. package/dist/cli/session-commands.js +45 -5
  56. package/dist/cli/session-picker.d.ts +43 -0
  57. package/dist/cli/session-picker.js +128 -0
  58. package/dist/codebuddy/a2a-call-tool-defs.d.ts +26 -0
  59. package/dist/codebuddy/a2a-call-tool-defs.js +12 -0
  60. package/dist/codebuddy/fleet-tool-defs.d.ts +1 -0
  61. package/dist/codebuddy/fleet-tool-defs.js +24 -0
  62. package/dist/codebuddy/providers/chatgpt-headers.d.ts +18 -0
  63. package/dist/codebuddy/providers/chatgpt-headers.js +37 -0
  64. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +2 -1
  65. package/dist/codebuddy/providers/provider-chatgpt-responses.js +10 -23
  66. package/dist/codebuddy/providers/provider-openai-compat.js +1 -1
  67. package/dist/codebuddy/ragchat-tool-defs.d.ts +31 -0
  68. package/dist/codebuddy/ragchat-tool-defs.js +19 -0
  69. package/dist/codebuddy/resource-catalog-tool-defs.d.ts +28 -0
  70. package/dist/codebuddy/resource-catalog-tool-defs.js +18 -0
  71. package/dist/codebuddy/tool-definitions/agent-tools.js +2 -2
  72. package/dist/codebuddy/tool-definitions/code-exec-tools.js +5 -1
  73. package/dist/codebuddy/tool-definitions/cron-tools.js +6 -0
  74. package/dist/codebuddy/tool-definitions/self-describe-tools.js +6 -1
  75. package/dist/codebuddy/tools.d.ts +0 -6
  76. package/dist/codebuddy/tools.js +25 -3
  77. package/dist/commands/cli/daemon-commands.js +3 -1
  78. package/dist/commands/cli/evolve-command.js +21 -5
  79. package/dist/commands/cli/fleet-collaboration-commands.d.ts +2 -0
  80. package/dist/commands/cli/fleet-collaboration-commands.js +57 -0
  81. package/dist/commands/cli/fleet-commands.js +29 -0
  82. package/dist/commands/cli/fleet-mission-commands.d.ts +3 -0
  83. package/dist/commands/cli/fleet-mission-commands.js +72 -0
  84. package/dist/commands/cli/fleet-rooms-commands.d.ts +10 -0
  85. package/dist/commands/cli/fleet-rooms-commands.js +289 -0
  86. package/dist/commands/cli/tools-commands.js +2 -1
  87. package/dist/commands/cli/triage-command.d.ts +8 -0
  88. package/dist/commands/cli/triage-command.js +72 -0
  89. package/dist/commands/cli/utility-commands.js +25 -1
  90. package/dist/commands/client-dispatcher.d.ts +7 -0
  91. package/dist/commands/client-dispatcher.js +73 -21
  92. package/dist/commands/cron-cli/index.d.ts +3 -1
  93. package/dist/commands/cron-cli/index.js +25 -0
  94. package/dist/commands/device-auth.d.ts +10 -0
  95. package/dist/commands/device-auth.js +91 -0
  96. package/dist/commands/enhanced-command-handler.d.ts +9 -0
  97. package/dist/commands/enhanced-command-handler.js +28 -6
  98. package/dist/commands/handlers/agent-handlers.d.ts +1 -1
  99. package/dist/commands/handlers/agent-handlers.js +4 -4
  100. package/dist/commands/handlers/channel-handlers.js +53 -2
  101. package/dist/commands/handlers/export-handlers.d.ts +3 -2
  102. package/dist/commands/handlers/export-handlers.js +55 -28
  103. package/dist/commands/handlers/extra-handlers.js +5 -1
  104. package/dist/commands/handlers/grill-me-handler.js +26 -18
  105. package/dist/commands/handlers/memory-handlers.d.ts +6 -2
  106. package/dist/commands/handlers/memory-handlers.js +35 -15
  107. package/dist/commands/handlers/missing-handlers.d.ts +2 -2
  108. package/dist/commands/handlers/missing-handlers.js +4 -4
  109. package/dist/commands/handlers/resources-handler.d.ts +12 -0
  110. package/dist/commands/handlers/resources-handler.js +52 -0
  111. package/dist/commands/handlers/starter-handlers.d.ts +1 -1
  112. package/dist/commands/handlers/starter-handlers.js +32 -2
  113. package/dist/commands/handlers/switch-handler.d.ts +1 -1
  114. package/dist/commands/handlers/switch-handler.js +15 -0
  115. package/dist/commands/handlers/test-handlers.js +4 -86
  116. package/dist/commands/handlers/ui-handlers.js +11 -5
  117. package/dist/commands/login-chatgpt.d.ts +8 -0
  118. package/dist/commands/login-chatgpt.js +17 -0
  119. package/dist/commands/mcp-import.d.ts +11 -0
  120. package/dist/commands/mcp-import.js +67 -0
  121. package/dist/commands/mcp-invoke.d.ts +2 -0
  122. package/dist/commands/mcp-invoke.js +68 -0
  123. package/dist/commands/mcp.js +4 -0
  124. package/dist/commands/pipeline.d.ts +7 -1
  125. package/dist/commands/pipeline.js +104 -7
  126. package/dist/commands/resources.d.ts +2 -0
  127. package/dist/commands/resources.js +64 -0
  128. package/dist/commands/skills-cli/index.js +35 -12
  129. package/dist/commands/slash/builtin-commands.js +16 -3
  130. package/dist/commands/slash/index.d.ts +3 -1
  131. package/dist/commands/slash/index.js +2 -0
  132. package/dist/commands/slash/surfaces.d.ts +53 -0
  133. package/dist/commands/slash/surfaces.js +177 -0
  134. package/dist/commands/slash/types.d.ts +17 -0
  135. package/dist/commands/try.d.ts +1 -1
  136. package/dist/commands/try.js +1 -1
  137. package/dist/companion/companion-identity.d.ts +43 -0
  138. package/dist/companion/companion-identity.js +136 -0
  139. package/dist/companion/companion-toolset.d.ts +66 -0
  140. package/dist/companion/companion-toolset.js +368 -0
  141. package/dist/companion/companion-turn.d.ts +10 -2
  142. package/dist/companion/companion-turn.js +38 -1
  143. package/dist/config/code-exec-policy.d.ts +25 -0
  144. package/dist/config/code-exec-policy.js +39 -0
  145. package/dist/config/model-tools.d.ts +9 -0
  146. package/dist/config/model-tools.js +4 -0
  147. package/dist/context/context-manager-v2.js +2 -2
  148. package/dist/context/lm-resizer-compressor.js +3 -2
  149. package/dist/context/lm-resizer-diagnostics.d.ts +9 -0
  150. package/dist/context/lm-resizer-diagnostics.js +30 -0
  151. package/dist/context/tool-output-masking.js +3 -4
  152. package/dist/curator/curator.d.ts +6 -0
  153. package/dist/curator/curator.js +53 -3
  154. package/dist/daemon/cron-agent-bridge.d.ts +6 -0
  155. package/dist/daemon/cron-agent-bridge.js +31 -4
  156. package/dist/doctor/index.d.ts +11 -2
  157. package/dist/doctor/index.js +84 -21
  158. package/dist/doctor/integrations.d.ts +179 -0
  159. package/dist/doctor/integrations.js +227 -0
  160. package/dist/doctor/triage.d.ts +108 -0
  161. package/dist/doctor/triage.js +304 -0
  162. package/dist/fleet/collaboration.d.ts +44 -0
  163. package/dist/fleet/collaboration.js +170 -0
  164. package/dist/fleet/fleet-listener.d.ts +1 -0
  165. package/dist/fleet/fleet-listener.js +87 -46
  166. package/dist/fleet/fleet-registry.d.ts +2 -0
  167. package/dist/fleet/resource-catalog.d.ts +245 -0
  168. package/dist/fleet/resource-catalog.js +211 -0
  169. package/dist/fleet/rooms/room-access.d.ts +138 -0
  170. package/dist/fleet/rooms/room-access.js +196 -0
  171. package/dist/fleet/rooms/room-client.d.ts +158 -0
  172. package/dist/fleet/rooms/room-client.js +685 -0
  173. package/dist/fleet/rooms/room-event.d.ts +139 -0
  174. package/dist/fleet/rooms/room-event.js +330 -0
  175. package/dist/fleet/rooms/room-filter.d.ts +48 -0
  176. package/dist/fleet/rooms/room-filter.js +152 -0
  177. package/dist/fleet/rooms/room-hub.d.ts +154 -0
  178. package/dist/fleet/rooms/room-hub.js +363 -0
  179. package/dist/fleet/rooms/room-identity.d.ts +26 -0
  180. package/dist/fleet/rooms/room-identity.js +82 -0
  181. package/dist/fleet/rooms/room-lock.d.ts +34 -0
  182. package/dist/fleet/rooms/room-lock.js +180 -0
  183. package/dist/fleet/rooms/room-observations.d.ts +7 -0
  184. package/dist/fleet/rooms/room-observations.js +61 -0
  185. package/dist/fleet/rooms/room-server.d.ts +28 -0
  186. package/dist/fleet/rooms/room-server.js +75 -0
  187. package/dist/fleet/rooms/room-store.d.ts +119 -0
  188. package/dist/fleet/rooms/room-store.js +448 -0
  189. package/dist/fleet/rooms/room-ws-bridge.d.ts +27 -0
  190. package/dist/fleet/rooms/room-ws-bridge.js +137 -0
  191. package/dist/fleet/sensory-mission-bridge.d.ts +42 -0
  192. package/dist/fleet/sensory-mission-bridge.js +136 -0
  193. package/dist/git/worktree-sessions.js +1 -0
  194. package/dist/harness/contract.d.ts +4 -4
  195. package/dist/harness/fleet-supervisor.d.ts +16 -0
  196. package/dist/harness/fleet-supervisor.js +64 -0
  197. package/dist/harness/index.d.ts +4 -0
  198. package/dist/harness/index.js +4 -0
  199. package/dist/harness/mission-runner.d.ts +3 -0
  200. package/dist/harness/mission-runner.js +53 -0
  201. package/dist/harness/mission-store.d.ts +93 -0
  202. package/dist/harness/mission-store.js +297 -0
  203. package/dist/harness/tool-harness.d.ts +56 -0
  204. package/dist/harness/tool-harness.js +153 -0
  205. package/dist/hooks/use-enhanced-input.js +48 -4
  206. package/dist/hooks/use-input-handler.d.ts +1 -0
  207. package/dist/hooks/use-input-handler.js +82 -22
  208. package/dist/hooks/use-input-history.js +1 -0
  209. package/dist/identity/lisa-introspection.js +5 -2
  210. package/dist/identity/operational-self-model.d.ts +2 -0
  211. package/dist/identity/operational-self-model.js +4 -0
  212. package/dist/index.js +52 -6
  213. package/dist/mcp/client.js +6 -0
  214. package/dist/mcp/config.d.ts +20 -0
  215. package/dist/mcp/config.js +121 -8
  216. package/dist/mcp/import-normalize.d.ts +12 -0
  217. package/dist/mcp/import-normalize.js +114 -0
  218. package/dist/mcp/transports.d.ts +10 -5
  219. package/dist/mcp/transports.js +61 -99
  220. package/dist/mcp/types.d.ts +5 -0
  221. package/dist/media/comfyui-recipe-contract.d.ts +7 -7
  222. package/dist/memory/facts-memory.d.ts +11 -1
  223. package/dist/memory/facts-memory.js +47 -8
  224. package/dist/memory/memory-candidate-queue.d.ts +2 -2
  225. package/dist/memory/memory-candidate-queue.js +6 -6
  226. package/dist/memory/persistent-memory.d.ts +13 -8
  227. package/dist/memory/persistent-memory.js +77 -30
  228. package/dist/nodes/device-node.js +5 -9
  229. package/dist/observability/mobile-supervision-gateway-contract.js +20 -7
  230. package/dist/observability/run-event-writer.d.ts +21 -0
  231. package/dist/observability/run-event-writer.js +81 -0
  232. package/dist/observability/run-store.d.ts +5 -0
  233. package/dist/observability/run-store.js +18 -13
  234. package/dist/observability/run-trajectory.js +1 -1
  235. package/dist/optimization/prompt-cache.d.ts +19 -2
  236. package/dist/optimization/prompt-cache.js +41 -6
  237. package/dist/orchestration/orchestrator.d.ts +21 -0
  238. package/dist/orchestration/orchestrator.js +122 -15
  239. package/dist/persistence/session-content.d.ts +8 -0
  240. package/dist/persistence/session-content.js +76 -0
  241. package/dist/persistence/session-handoff.d.ts +64 -0
  242. package/dist/persistence/session-handoff.js +80 -0
  243. package/dist/persistence/session-history.d.ts +4 -0
  244. package/dist/persistence/session-history.js +38 -0
  245. package/dist/persistence/session-store.d.ts +9 -0
  246. package/dist/persistence/session-store.js +48 -20
  247. package/dist/personas/persona-manager.js +17 -1
  248. package/dist/plugins/code-explorer/CodeExplorerManager.d.ts +10 -4
  249. package/dist/plugins/code-explorer/CodeExplorerManager.js +69 -16
  250. package/dist/plugins/plugin-manager.js +10 -1
  251. package/dist/prompts/prompt-manager.js +11 -4
  252. package/dist/prompts/system-base.js +6 -2
  253. package/dist/protocols/a2a/codebuddy-executor.d.ts +11 -2
  254. package/dist/protocols/a2a/codebuddy-executor.js +9 -8
  255. package/dist/protocols/a2a/index.d.ts +1 -1
  256. package/dist/protocols/a2a/jsonrpc-v1.d.ts +41 -0
  257. package/dist/protocols/a2a/jsonrpc-v1.js +145 -0
  258. package/dist/protocols/a2a/peer-config.d.ts +20 -0
  259. package/dist/protocols/a2a/peer-config.js +55 -0
  260. package/dist/providers/codex-oauth.js +30 -6
  261. package/dist/providers/model-provider-compat.js +2 -1
  262. package/dist/sandbox/os-sandbox.d.ts +1 -1
  263. package/dist/scheduler/cron-scheduler.d.ts +15 -2
  264. package/dist/scheduler/cron-scheduler.js +35 -4
  265. package/dist/scheduler/job-notepad.d.ts +69 -0
  266. package/dist/scheduler/job-notepad.js +339 -0
  267. package/dist/security/audit-logger.d.ts +1 -1
  268. package/dist/security/compute-confinement.d.ts +4 -0
  269. package/dist/security/compute-confinement.js +89 -0
  270. package/dist/security/session-encryption.d.ts +4 -0
  271. package/dist/security/session-encryption.js +31 -0
  272. package/dist/sensory/voice-loop.d.ts +17 -0
  273. package/dist/sensory/voice-loop.js +72 -15
  274. package/dist/server/auth/device-session-context.d.ts +9 -0
  275. package/dist/server/auth/device-session-context.js +13 -0
  276. package/dist/server/auth/device-store.d.ts +89 -0
  277. package/dist/server/auth/device-store.js +261 -0
  278. package/dist/server/auth/device-token.d.ts +3 -0
  279. package/dist/server/auth/device-token.js +6 -0
  280. package/dist/server/auth/jwt.js +4 -0
  281. package/dist/server/index.d.ts +0 -10
  282. package/dist/server/index.js +28 -0
  283. package/dist/server/middleware/auth.js +3 -2
  284. package/dist/server/routes/a2a-jsonrpc.d.ts +9 -0
  285. package/dist/server/routes/a2a-jsonrpc.js +66 -0
  286. package/dist/server/routes/a2a-protocol.d.ts +12 -0
  287. package/dist/server/routes/a2a-protocol.js +1 -1
  288. package/dist/server/routes/device-auth.d.ts +4 -0
  289. package/dist/server/routes/device-auth.js +40 -0
  290. package/dist/server/types.d.ts +4 -0
  291. package/dist/server/websocket/handler.d.ts +13 -0
  292. package/dist/server/websocket/handler.js +102 -17
  293. package/dist/services/prompt-builder.d.ts +2 -0
  294. package/dist/services/prompt-builder.js +13 -5
  295. package/dist/services/runtime-settings-context.d.ts +73 -0
  296. package/dist/services/runtime-settings-context.js +69 -0
  297. package/dist/skills/executor.js +2 -0
  298. package/dist/skills/hub.js +16 -14
  299. package/dist/skills/local-inventory.d.ts +21 -0
  300. package/dist/skills/local-inventory.js +72 -0
  301. package/dist/skills/skill-loader.js +2 -2
  302. package/dist/skills/skill-usage-store.d.ts +49 -0
  303. package/dist/skills/skill-usage-store.js +121 -0
  304. package/dist/testing/ai-integration-tests.js +8 -5
  305. package/dist/themes/theme-manager.d.ts +12 -0
  306. package/dist/themes/theme-manager.js +29 -0
  307. package/dist/themes/theme-schema.d.ts +24 -24
  308. package/dist/tools/a2a-call-tool.d.ts +34 -0
  309. package/dist/tools/a2a-call-tool.js +75 -0
  310. package/dist/tools/bash/streaming-executor.d.ts +1 -0
  311. package/dist/tools/bash/streaming-executor.js +78 -90
  312. package/dist/tools/code-exec-preflight-runner.d.ts +41 -0
  313. package/dist/tools/code-exec-preflight-runner.js +254 -0
  314. package/dist/tools/code-exec-preflight.d.ts +44 -0
  315. package/dist/tools/code-exec-preflight.js +501 -0
  316. package/dist/tools/code-exec-tool.d.ts +48 -1
  317. package/dist/tools/code-exec-tool.js +191 -47
  318. package/dist/tools/comment-watcher.js +29 -12
  319. package/dist/tools/core-code-inspection.d.ts +56 -0
  320. package/dist/tools/core-code-inspection.js +86 -0
  321. package/dist/tools/create-skill-tool.d.ts +2 -3
  322. package/dist/tools/create-skill-tool.js +16 -75
  323. package/dist/tools/cronjob-tool.d.ts +1 -0
  324. package/dist/tools/cronjob-tool.js +1 -0
  325. package/dist/tools/env-doctor-tool.d.ts +2 -0
  326. package/dist/tools/env-doctor-tool.js +4 -3
  327. package/dist/tools/execute-code-rpc-invoker.d.ts +1 -1
  328. package/dist/tools/execute-code-rpc-invoker.js +35 -19
  329. package/dist/tools/execute-code-runner.d.ts +2 -0
  330. package/dist/tools/execute-code-runner.js +52 -17
  331. package/dist/tools/fleet-room-tool.d.ts +104 -0
  332. package/dist/tools/fleet-room-tool.js +124 -0
  333. package/dist/tools/integration-tool-hints.d.ts +4 -0
  334. package/dist/tools/integration-tool-hints.js +19 -0
  335. package/dist/tools/list-peers-tool.d.ts +3 -0
  336. package/dist/tools/list-peers-tool.js +11 -4
  337. package/dist/tools/media-generation-tool.d.ts +16 -1
  338. package/dist/tools/media-generation-tool.js +341 -9
  339. package/dist/tools/merge-conflict-tool.js +53 -25
  340. package/dist/tools/metadata.d.ts +1 -5
  341. package/dist/tools/metadata.js +21 -28
  342. package/dist/tools/ragchat-tool.d.ts +39 -0
  343. package/dist/tools/ragchat-tool.js +105 -0
  344. package/dist/tools/registry/fleet-tools.d.ts +9 -0
  345. package/dist/tools/registry/fleet-tools.js +22 -1
  346. package/dist/tools/registry/knowledge-tools.d.ts +2 -2
  347. package/dist/tools/registry/knowledge-tools.js +2 -2
  348. package/dist/tools/registry/lessons-tools.d.ts +7 -0
  349. package/dist/tools/registry/lessons-tools.js +43 -3
  350. package/dist/tools/registry/memory-tools.d.ts +1 -1
  351. package/dist/tools/registry/memory-tools.js +8 -8
  352. package/dist/tools/registry/research-tools.d.ts +1 -0
  353. package/dist/tools/registry/research-tools.js +4 -13
  354. package/dist/tools/registry/self-describe-tools.d.ts +1 -1
  355. package/dist/tools/registry/self-describe-tools.js +43 -30
  356. package/dist/tools/resource-catalog-tool.d.ts +38 -0
  357. package/dist/tools/resource-catalog-tool.js +46 -0
  358. package/dist/tools/skills-inspection-tool.js +30 -1
  359. package/dist/tools/stream-tool-output.d.ts +3 -0
  360. package/dist/tools/stream-tool-output.js +33 -0
  361. package/dist/tools/tool-call-scheduler.d.ts +10 -0
  362. package/dist/tools/tool-call-scheduler.js +58 -0
  363. package/dist/tools/tool-effect.d.ts +5 -0
  364. package/dist/tools/tool-effect.js +26 -0
  365. package/dist/tools/tool-search.d.ts +12 -11
  366. package/dist/tools/tool-search.js +67 -26
  367. package/dist/tools/types.d.ts +6 -0
  368. package/dist/ui/components/ChatHistory.d.ts +1 -1
  369. package/dist/ui/components/ChatHistory.js +2 -2
  370. package/dist/ui/components/ChatInput.d.ts +2 -1
  371. package/dist/ui/components/ChatInput.js +42 -88
  372. package/dist/ui/components/ChatInterface.d.ts +4 -2
  373. package/dist/ui/components/ChatInterface.js +27 -63
  374. package/dist/ui/components/CommandSuggestions.d.ts +0 -1
  375. package/dist/ui/components/CommandSuggestions.js +4 -6
  376. package/dist/ui/components/FileAutocomplete.js +1 -1
  377. package/dist/ui/components/KeyboardHelp.js +8 -6
  378. package/dist/ui/components/ModelSelection.js +6 -5
  379. package/dist/ui/components/StatusBar.d.ts +2 -1
  380. package/dist/ui/components/StatusBar.js +8 -7
  381. package/dist/ui/context/theme-context.js +3 -2
  382. package/dist/ui/index.d.ts +1 -1
  383. package/dist/ui/index.js +1 -1
  384. package/dist/ui/utils/markdown-renderer.js +4 -13
  385. package/dist/utils/atomic-write.d.ts +22 -0
  386. package/dist/utils/atomic-write.js +29 -0
  387. package/dist/utils/bounded-output.d.ts +16 -0
  388. package/dist/utils/bounded-output.js +63 -0
  389. package/dist/utils/device-store-file.d.ts +4 -0
  390. package/dist/utils/device-store-file.js +40 -0
  391. package/dist/utils/export-manager.d.ts +9 -0
  392. package/dist/utils/export-manager.js +19 -0
  393. package/dist/utils/first-use-hints.d.ts +25 -0
  394. package/dist/utils/first-use-hints.js +58 -0
  395. package/dist/utils/history-manager.js +1 -1
  396. package/dist/utils/update-notifier.js +16 -18
  397. package/dist/wizard/environment-detection.d.ts +5 -1
  398. package/dist/wizard/environment-detection.js +49 -7
  399. package/dist/wizard/onboarding.d.ts +5 -1
  400. package/dist/wizard/onboarding.js +90 -30
  401. package/dist/wizard/provider-onboarding.d.ts +2 -0
  402. package/dist/wizard/provider-onboarding.js +55 -17
  403. package/package.json +7 -3
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Per-job bounded continuity store (Hermes-inspired cron notepad).
3
+ *
4
+ * Isolated JSON files under `<cron-root>/notepads/<jobId>.json`, written
5
+ * atomically (0600): 16 KiB per value, 128-char keys, 64 KiB per serialized
6
+ * record. Mutations use an exclusive per-job lock across local processes.
7
+ * Contention requires retry; abandoned locks require operator inspection and
8
+ * are never stolen. Invalid/corrupt records are preserved for inspection.
9
+ */
10
+ import { promises as fs } from 'node:fs';
11
+ import { homedir } from 'node:os';
12
+ import * as path from 'node:path';
13
+ import { writeJsonAtomic } from '../utils/atomic-write.js';
14
+ import { logger } from '../utils/logger.js';
15
+ export const MAX_VALUE_BYTES = 16 * 1024;
16
+ export const MAX_KEY_CHARS = 128;
17
+ export const MAX_JOB_TOTAL_BYTES = 64 * 1024;
18
+ export const MAX_LAST_OUTPUT_BYTES = 16 * 1024;
19
+ export class JobNotepadBusyError extends Error {
20
+ constructor() {
21
+ super('Job notepad mutation locked; retry. An abandoned lock requires operator inspection.');
22
+ this.name = 'JobNotepadBusyError';
23
+ }
24
+ }
25
+ function isRecord(value) {
26
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
27
+ && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
28
+ }
29
+ /** Validate operator/tool input before changing jobs or writing any notes. */
30
+ export function validateContinuity(value) {
31
+ if (typeof value === 'boolean')
32
+ return value;
33
+ if (!isRecord(value) || Object.keys(value).some(key => key !== 'enabled' && key !== 'notes')
34
+ || (value.enabled !== undefined && typeof value.enabled !== 'boolean')) {
35
+ throw new Error('Invalid continuity: expected a boolean or an object with enabled and notes');
36
+ }
37
+ if (value.notes !== undefined)
38
+ assertNotesWithinCaps(value.notes);
39
+ return {
40
+ ...(value.enabled !== undefined ? { enabled: value.enabled } : {}),
41
+ ...(value.notes !== undefined ? { notes: { ...value.notes } } : {}),
42
+ };
43
+ }
44
+ export function isContinuityEnabled(continuity) {
45
+ if (!isRecord(continuity)) {
46
+ return continuity === true;
47
+ }
48
+ return continuity.enabled !== false;
49
+ }
50
+ /** Persist the opt-in flag only — notes live in the notepad file, not jobs.json. */
51
+ export function persistableContinuity(continuity) {
52
+ if (continuity === undefined)
53
+ return undefined;
54
+ continuity = validateContinuity(continuity);
55
+ if (typeof continuity === 'boolean')
56
+ return continuity;
57
+ return { enabled: continuity.enabled !== false };
58
+ }
59
+ export function defaultNotepadDir() {
60
+ const root = process.env.CODEBUDDY_CRON_HOME
61
+ ? path.resolve(process.env.CODEBUDDY_CRON_HOME)
62
+ : path.join(homedir(), '.codebuddy', 'cron');
63
+ return path.join(root, 'notepads');
64
+ }
65
+ export function utf8ByteLength(value) {
66
+ return Buffer.byteLength(value, 'utf8');
67
+ }
68
+ export function sanitizeJobId(jobId) {
69
+ if (typeof jobId !== 'string' || !jobId || jobId.length > 128 || !/^[A-Za-z0-9._-]+$/.test(jobId)) {
70
+ throw new Error('invalid job id');
71
+ }
72
+ return jobId;
73
+ }
74
+ export function truncateUtf8(value, maxBytes) {
75
+ if (maxBytes <= 0)
76
+ return '';
77
+ const buf = Buffer.from(value, 'utf8');
78
+ if (buf.length <= maxBytes)
79
+ return value;
80
+ let end = maxBytes;
81
+ while (end > 0 && ((buf[end] ?? 0) & 0xc0) === 0x80)
82
+ end -= 1;
83
+ return buf.subarray(0, end).toString('utf8');
84
+ }
85
+ export function notesByteTotal(notes) {
86
+ let total = 0;
87
+ for (const [key, value] of Object.entries(notes)) {
88
+ total += utf8ByteLength(key) + utf8ByteLength(value);
89
+ }
90
+ return total;
91
+ }
92
+ export function assertNotesWithinCaps(notes) {
93
+ if (!isRecord(notes))
94
+ throw new Error('notes must be an object of strings');
95
+ let total = 0;
96
+ for (const [key, value] of Object.entries(notes)) {
97
+ if (!key)
98
+ throw new Error('key must be non-empty');
99
+ if (key.length > MAX_KEY_CHARS) {
100
+ throw new Error(`key too long (max ${MAX_KEY_CHARS} characters)`);
101
+ }
102
+ if (typeof value !== 'string') {
103
+ throw new Error(`note '${key}' must be a string`);
104
+ }
105
+ const valueBytes = utf8ByteLength(value);
106
+ if (valueBytes > MAX_VALUE_BYTES) {
107
+ throw new Error(`value too large (max ${MAX_VALUE_BYTES} bytes per key)`);
108
+ }
109
+ total += utf8ByteLength(key) + valueBytes;
110
+ if (total > MAX_JOB_TOTAL_BYTES) {
111
+ throw new Error(`notepad full: would exceed ${MAX_JOB_TOTAL_BYTES} bytes total`);
112
+ }
113
+ }
114
+ assertRecordSize({ ...emptyRecord(), notes: notes });
115
+ }
116
+ function assertNotepadRecord(value) {
117
+ if (!isRecord(value) || Object.keys(value).some(key => !['notes', 'updatedAt', 'lastSuccessfulOutput'].includes(key))
118
+ || typeof value.updatedAt !== 'string' || !Number.isFinite(Date.parse(value.updatedAt))) {
119
+ throw new Error('Invalid notepad record');
120
+ }
121
+ assertNotesWithinCaps(value.notes);
122
+ if (value.lastSuccessfulOutput !== undefined && (typeof value.lastSuccessfulOutput !== 'string'
123
+ || utf8ByteLength(value.lastSuccessfulOutput) > MAX_LAST_OUTPUT_BYTES))
124
+ throw new Error('Invalid last successful output');
125
+ assertRecordSize(value);
126
+ }
127
+ function emptyRecord() {
128
+ return { notes: {}, updatedAt: new Date().toISOString() };
129
+ }
130
+ function serializedBytes(record) {
131
+ // Match writeJsonAtomic's indentation and final newline, including escaping.
132
+ return utf8ByteLength(`${JSON.stringify(record, null, 2)}\n`);
133
+ }
134
+ function assertRecordSize(record) {
135
+ if (serializedBytes(record) > MAX_JOB_TOTAL_BYTES) {
136
+ throw new Error(`notepad full: would exceed ${MAX_JOB_TOTAL_BYTES} serialized bytes total`);
137
+ }
138
+ }
139
+ export class JobNotepadStore {
140
+ dir;
141
+ constructor(dir = defaultNotepadDir()) {
142
+ this.dir = dir;
143
+ }
144
+ fileFor(jobId) {
145
+ const safe = sanitizeJobId(jobId);
146
+ const root = path.resolve(this.dir);
147
+ const file = path.resolve(root, `${safe}.json`);
148
+ const prefix = root.endsWith(path.sep) ? root : root + path.sep;
149
+ if (file !== path.join(root, `${safe}.json`) && !file.startsWith(prefix)) {
150
+ throw new Error('invalid job id');
151
+ }
152
+ return file;
153
+ }
154
+ async load(jobId) {
155
+ let handle;
156
+ try {
157
+ handle = await fs.open(this.fileFor(jobId), 'r');
158
+ }
159
+ catch (error) {
160
+ if (error.code === 'ENOENT')
161
+ return emptyRecord();
162
+ throw error;
163
+ }
164
+ try {
165
+ const stat = await handle.stat();
166
+ if (!stat.isFile())
167
+ throw new Error('Notepad must be a regular file');
168
+ if (stat.size > MAX_JOB_TOTAL_BYTES)
169
+ throw new Error('Notepad record exceeds size limit');
170
+ const buffer = Buffer.alloc(MAX_JOB_TOTAL_BYTES + 1);
171
+ let length = 0;
172
+ while (length < buffer.length) {
173
+ const { bytesRead } = await handle.read(buffer, length, buffer.length - length, length);
174
+ if (bytesRead === 0)
175
+ break;
176
+ length += bytesRead;
177
+ }
178
+ if (length > MAX_JOB_TOTAL_BYTES)
179
+ throw new Error('Notepad record exceeds size limit');
180
+ const loaded = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(buffer.subarray(0, length)));
181
+ assertNotepadRecord(loaded);
182
+ return loaded;
183
+ }
184
+ finally {
185
+ await handle.close();
186
+ }
187
+ }
188
+ async setNote(jobId, key, value) {
189
+ assertNotesWithinCaps({ [key]: value });
190
+ return this.withMutationLock(jobId, async () => {
191
+ const current = await this.load(jobId);
192
+ return this.commitNotes(jobId, current, { ...current.notes, [key]: value });
193
+ });
194
+ }
195
+ async replaceNotes(jobId, notes) {
196
+ assertNotesWithinCaps(notes);
197
+ const snapshot = { ...notes };
198
+ return this.withMutationLock(jobId, async () => this.commitNotes(jobId, await this.load(jobId), snapshot));
199
+ }
200
+ async getNote(jobId, key) {
201
+ const current = await this.load(jobId);
202
+ return Object.hasOwn(current.notes, key) ? current.notes[key] : undefined;
203
+ }
204
+ async deleteNote(jobId, key) {
205
+ return this.withMutationLock(jobId, async () => {
206
+ const current = await this.load(jobId);
207
+ if (!Object.hasOwn(current.notes, key))
208
+ return false;
209
+ const nextNotes = { ...current.notes };
210
+ delete nextNotes[key];
211
+ await this.commitNotes(jobId, current, nextNotes);
212
+ return true;
213
+ });
214
+ }
215
+ async listNotes(jobId) {
216
+ const current = await this.load(jobId);
217
+ return Object.keys(current.notes)
218
+ .sort()
219
+ .map((key) => ({ key, value: current.notes[key] ?? '' }));
220
+ }
221
+ /**
222
+ * Record the last *successful* job output. Truncates to the per-value and
223
+ * serialized per-job caps. Storage errors are reported to the caller.
224
+ * Empty output leaves the previous non-empty successful output in place.
225
+ */
226
+ async saveLastSuccessfulOutput(jobId, output) {
227
+ if (typeof output !== 'string' || output.length === 0)
228
+ return;
229
+ await this.withMutationLock(jobId, async () => {
230
+ const current = await this.load(jobId);
231
+ const capped = truncateUtf8(output, MAX_LAST_OUTPUT_BYTES);
232
+ const next = { ...current, lastSuccessfulOutput: capped, updatedAt: new Date().toISOString() };
233
+ let low = 0;
234
+ let high = utf8ByteLength(capped);
235
+ let best = '';
236
+ while (low <= high) {
237
+ const middle = Math.floor((low + high) / 2);
238
+ const candidate = truncateUtf8(capped, middle);
239
+ if (serializedBytes({ ...next, lastSuccessfulOutput: candidate }) <= MAX_JOB_TOTAL_BYTES) {
240
+ best = candidate;
241
+ low = middle + 1;
242
+ }
243
+ else
244
+ high = middle - 1;
245
+ }
246
+ if (!best)
247
+ throw new Error('notepad full: no room for the latest successful output');
248
+ await this.write(jobId, { ...next, lastSuccessfulOutput: best });
249
+ });
250
+ }
251
+ /** Missing files are a no-op; existing files share writer exclusion. */
252
+ async clear(jobId) {
253
+ try {
254
+ await fs.lstat(this.fileFor(jobId));
255
+ }
256
+ catch (error) {
257
+ if (error.code === 'ENOENT')
258
+ return;
259
+ throw error;
260
+ }
261
+ await this.withMutationLock(jobId, async () => {
262
+ try {
263
+ await fs.unlink(this.fileFor(jobId));
264
+ }
265
+ catch (error) {
266
+ if (error.code !== 'ENOENT')
267
+ throw error;
268
+ }
269
+ });
270
+ }
271
+ /**
272
+ * Prompt section for this job. Empty notepad MUST return '' so jobs that
273
+ * never use continuity keep a byte-identical agent prompt.
274
+ */
275
+ async renderSection(jobId) {
276
+ try {
277
+ const rec = await this.load(jobId);
278
+ const keys = Object.keys(rec.notes).sort();
279
+ const hasNotes = keys.length > 0;
280
+ const last = rec.lastSuccessfulOutput;
281
+ const hasOutput = typeof last === 'string' && last.length > 0;
282
+ if (!hasNotes && !hasOutput)
283
+ return '';
284
+ const parts = [];
285
+ if (hasNotes) {
286
+ const lines = keys.map((key) => `- ${key}: ${rec.notes[key] ?? ''}`);
287
+ parts.push('## Job notepad (persistent across runs)\n'
288
+ + 'This durable scratchpad survives between scheduled runs of this job.\n\n'
289
+ + lines.join('\n'));
290
+ }
291
+ if (hasOutput) {
292
+ parts.push(`## Last successful output (last non-empty result)\n${last}`);
293
+ }
294
+ return `${parts.join('\n\n')}\n`;
295
+ }
296
+ catch (error) {
297
+ logger.warn('Cron continuity could not be loaded; proceeding without stored context', { jobId, error: String(error) });
298
+ return '';
299
+ }
300
+ }
301
+ async commitNotes(jobId, current, nextNotes) {
302
+ const next = {
303
+ notes: nextNotes,
304
+ updatedAt: new Date().toISOString(),
305
+ ...(current.lastSuccessfulOutput !== undefined
306
+ ? { lastSuccessfulOutput: current.lastSuccessfulOutput }
307
+ : {}),
308
+ };
309
+ await this.write(jobId, next);
310
+ return next;
311
+ }
312
+ async write(jobId, record) {
313
+ assertNotepadRecord(record);
314
+ await writeJsonAtomic(this.fileFor(jobId), record, { mode: 0o600 });
315
+ }
316
+ async withMutationLock(jobId, action) {
317
+ const file = this.fileFor(jobId);
318
+ await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
319
+ const lock = `${file}.lock`;
320
+ let handle;
321
+ try {
322
+ handle = await fs.open(lock, 'wx', 0o600);
323
+ }
324
+ catch (error) {
325
+ if (error.code === 'EEXIST')
326
+ throw new JobNotepadBusyError();
327
+ throw error;
328
+ }
329
+ try {
330
+ await handle.writeFile(JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }));
331
+ return await action();
332
+ }
333
+ finally {
334
+ await handle.close();
335
+ await fs.unlink(lock);
336
+ }
337
+ }
338
+ }
339
+ //# sourceMappingURL=job-notepad.js.map
@@ -5,7 +5,7 @@
5
5
  * for code generation operations. Provides a persistent audit trail
6
6
  * that can be reviewed for compliance and debugging.
7
7
  */
8
- export type AuditAction = 'code_validation' | 'command_validation' | 'file_write' | 'file_edit' | 'file_delete' | 'patch_apply' | 'bash_execute' | 'confirmation_requested' | 'confirmation_granted' | 'confirmation_denied' | 'checkpoint_created' | 'checkpoint_restored' | 'sandbox_execute' | 'pattern_matched' | 'tool_execution' | 'self_improvement';
8
+ export type AuditAction = 'code_validation' | 'command_validation' | 'file_write' | 'file_edit' | 'file_delete' | 'patch_apply' | 'bash_execute' | 'confirmation_requested' | 'confirmation_granted' | 'confirmation_denied' | 'checkpoint_created' | 'checkpoint_restored' | 'sandbox_execute' | 'pattern_matched' | 'tool_execution' | 'self_improvement' | 'device_register' | 'device_verify' | 'device_revoke';
9
9
  export type AuditDecision = 'allow' | 'block' | 'warn' | 'confirm';
10
10
  export interface AuditEntry {
11
11
  /** Unique entry ID */
@@ -0,0 +1,4 @@
1
+ export declare function confineComputeInvocation(command: string, args: string[], root: string): {
2
+ command: string;
3
+ args: string[];
4
+ };
@@ -0,0 +1,89 @@
1
+ /** Kernel-enforced compute sandbox. Fail closed if the required Linux facilities are absent. */
2
+ import { realpathSync } from 'node:fs';
3
+ // Bootstrap is trusted, runs before user code, and applies restrictions inherited across exec.
4
+ // Landlock restricts file content access; seccomp denies networking and cross-process access.
5
+ const BOOTSTRAP = String.raw `
6
+ import ctypes, errno, os, platform, resource, sys
7
+ libc = ctypes.CDLL(None, use_errno=True)
8
+ def check(value):
9
+ if value < 0: raise OSError(ctypes.get_errno(), 'compute confinement unavailable')
10
+ return value
11
+ arch = platform.machine()
12
+ if arch not in ('x86_64', 'aarch64'): raise RuntimeError('unsupported compute sandbox architecture')
13
+ abi = check(libc.syscall(444, 0, 0, 1))
14
+ if abi < 3: raise RuntimeError('compute sandbox requires Landlock ABI 3 or newer')
15
+ root, executable = sys.argv[1:3]
16
+ class Rules(ctypes.Structure):
17
+ _fields_ = [('access', ctypes.c_uint64)]
18
+ class PathRule(ctypes.Structure):
19
+ _pack_ = 1
20
+ _fields_ = [('access', ctypes.c_uint64), ('fd', ctypes.c_int32)]
21
+ rights = (1 << 15) - 1
22
+ rules = Rules(rights)
23
+ fd = check(libc.syscall(444, ctypes.byref(rules), ctypes.sizeof(rules), 0))
24
+ def allow(p, access):
25
+ if not os.path.exists(p): return
26
+ p = os.path.realpath(p)
27
+ if not os.path.isdir(p): access &= (1 | 2 | 4 | (1 << 14))
28
+ handle = os.open(p, os.O_PATH | os.O_CLOEXEC)
29
+ try:
30
+ rule = PathRule(access, handle)
31
+ check(libc.syscall(445, fd, 1, ctypes.byref(rule), 0))
32
+ finally: os.close(handle)
33
+ # Runtime libraries are readable, never writable or executable as programs.
34
+ for p in ('/usr/lib', '/usr/lib64', '/usr/local/lib', '/usr/share/nodejs', '/usr/share/zoneinfo', '/lib', '/lib64', '/etc/ld.so.cache', '/dev/null', '/dev/urandom'):
35
+ allow(p, 4 | 8)
36
+ allow(root, rights & ~(1 | (1 << 6) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12)))
37
+ allow(executable, 1 | 4)
38
+ allow('/lib64/ld-linux-x86-64.so.2' if arch == 'x86_64' else '/lib/ld-linux-aarch64.so.1', 1 | 4)
39
+ # Drop inherited capabilities as well (the host may itself run as root).
40
+ class CapHeader(ctypes.Structure):
41
+ _fields_ = [('version',ctypes.c_uint32),('pid',ctypes.c_int)]
42
+ class CapData(ctypes.Structure):
43
+ _fields_ = [('effective',ctypes.c_uint32),('permitted',ctypes.c_uint32),('inheritable',ctypes.c_uint32)]
44
+ cap_header, cap_data = CapHeader(0x20080522, 0), (CapData * 2)()
45
+ check(libc.capset(ctypes.byref(cap_header), ctypes.byref(cap_data)))
46
+ check(libc.prctl(38, 1, 0, 0, 0)) # PR_SET_NO_NEW_PRIVS
47
+ check(libc.syscall(446, fd, 0))
48
+ os.close(fd)
49
+ class Filter(ctypes.Structure):
50
+ _fields_ = [('code',ctypes.c_ushort),('jt',ctypes.c_ubyte),('jf',ctypes.c_ubyte),('k',ctypes.c_uint32)]
51
+ class Program(ctypes.Structure):
52
+ _fields_ = [('len',ctypes.c_ushort),('filter',ctypes.POINTER(Filter))]
53
+ # Check audit architecture, deny x32 ABI, then block sockets, ptrace, process_vm,
54
+ # pidfd, io_uring, signals to other processes and namespace/mount manipulation.
55
+ expected = 0xc000003e if arch == 'x86_64' else 0xc00000b7
56
+ denied = ([41,42,43,44,45,46,47,49,50,53,288,101,310,311,424,425,426,434,438,62,200,234,57,58,165,166,272,308]
57
+ if arch == 'x86_64' else [198,203,202,206,207,211,212,200,201,199,242,117,270,271,424,425,426,434,438,129,130,131,40,39,97,268])
58
+ ops = [Filter(0x20,0,0,4), Filter(0x15,1,0,expected), Filter(0x06,0,0,0x80000000), Filter(0x20,0,0,0)]
59
+ if arch == 'x86_64': ops += [Filter(0x45,0,1,0x40000000), Filter(0x06,0,0,0x80000000)]
60
+ # Deny process creation, while allowing CLONE_THREAD for interpreter worker threads.
61
+ clone = 56 if arch == 'x86_64' else 220
62
+ ops += [Filter(0x15,0,4,clone), Filter(0x20,0,0,16), Filter(0x45,1,0,0x10000), Filter(0x06,0,0,0x00050000 | errno.EPERM), Filter(0x06,0,0,0x7fff0000)]
63
+ # libc retries clone() when clone3 reports ENOSYS.
64
+ ops += [Filter(0x15,0,1,435), Filter(0x06,0,0,0x00050000 | errno.ENOSYS)]
65
+ for nr in denied: ops += [Filter(0x15,0,1,nr),Filter(0x06,0,0,0x00050000 | errno.EPERM)]
66
+ ops += [Filter(0x06,0,0,0x7fff0000)]
67
+ filters = (Filter * len(ops))(*ops)
68
+ program = Program(len(ops), filters)
69
+ check(libc.prctl(22, 2, ctypes.byref(program), 0, 0)) # SECCOMP_MODE_FILTER
70
+ resource.setrlimit(resource.RLIMIT_CORE, (0,0))
71
+ resource.setrlimit(resource.RLIMIT_DATA, (536870912,536870912))
72
+ resource.setrlimit(resource.RLIMIT_FSIZE, (2097152,2097152))
73
+ resource.setrlimit(resource.RLIMIT_NOFILE, (128,128))
74
+ resource.setrlimit(resource.RLIMIT_CPU, (30,30))
75
+ os.environ['OPENSSL_CONF'] = '/dev/null'
76
+ for key in ('TMPDIR','TMP','TEMP'): os.environ[key] = root
77
+ os.environ.pop('NODE_PATH', None)
78
+ os.environ.pop('NODE_OPTIONS', None)
79
+ args = sys.argv[3:]
80
+ if os.path.basename(executable) == 'node': args = ['--jitless', '--max-old-space-size=96'] + args
81
+ os.execv(executable, [executable] + args)
82
+ `;
83
+ export function confineComputeInvocation(command, args, root) {
84
+ if (process.platform !== 'linux')
85
+ throw new Error('Compute confinement requires Linux Landlock/seccomp; refusing unrestricted execution');
86
+ const executable = realpathSync(command === 'python3' ? '/usr/bin/python3' : command);
87
+ return { command: '/usr/bin/python3', args: ['-I', '-c', BOOTSTRAP, realpathSync(root), executable, ...args] };
88
+ }
89
+ //# sourceMappingURL=compute-confinement.js.map
@@ -25,6 +25,8 @@ export interface EncryptionConfig {
25
25
  usePassword?: boolean;
26
26
  /** Enable encryption (can be disabled for performance) */
27
27
  enabled?: boolean;
28
+ /** Refuse volatile/machine-derived fallback for durable session storage. */
29
+ requirePersistentKey?: boolean;
28
30
  }
29
31
  /**
30
32
  * Session encryption manager
@@ -38,6 +40,8 @@ export declare class SessionEncryption {
38
40
  * Initialize encryption with machine-generated key
39
41
  */
40
42
  initialize(): Promise<void>;
43
+ /** Read existing key only: decrypting must never generate a replacement key. */
44
+ initializeForRead(): void;
41
45
  /**
42
46
  * Initialize with password-based key
43
47
  */
@@ -8,6 +8,8 @@
8
8
  */
9
9
  import * as crypto from 'crypto';
10
10
  import fs from 'fs-extra';
11
+ import { readFileSync } from 'node:fs';
12
+ import { withSessionLock } from '../persistence/session-lock.js';
11
13
  import * as path from 'path';
12
14
  import * as os from 'os';
13
15
  import { logger } from '../utils/logger.js';
@@ -39,6 +41,7 @@ export class SessionEncryption {
39
41
  keyPath: config.keyPath ?? DEFAULT_CONFIG.keyPath,
40
42
  usePassword: config.usePassword ?? DEFAULT_CONFIG.usePassword,
41
43
  enabled: config.enabled ?? DEFAULT_CONFIG.enabled,
44
+ requirePersistentKey: config.requirePersistentKey ?? false,
42
45
  };
43
46
  }
44
47
  /**
@@ -48,6 +51,26 @@ export class SessionEncryption {
48
51
  if (this.initialized || !this.config.enabled) {
49
52
  return;
50
53
  }
54
+ if (this.config.requirePersistentKey) {
55
+ await fs.ensureDir(path.dirname(this.config.keyPath));
56
+ await withSessionLock(this.config.keyPath, async () => {
57
+ let key;
58
+ try {
59
+ key = await fs.readFile(this.config.keyPath);
60
+ }
61
+ catch (error) {
62
+ if (error.code !== 'ENOENT')
63
+ throw error;
64
+ key = crypto.randomBytes(KEY_LENGTH);
65
+ await writeFileAtomic(this.config.keyPath, key, { mode: 0o600 });
66
+ }
67
+ if (key.length !== KEY_LENGTH)
68
+ throw new Error('Invalid session encryption key');
69
+ this.key = key;
70
+ this.initialized = true;
71
+ });
72
+ return;
73
+ }
51
74
  try {
52
75
  // Try to load existing key
53
76
  if (await fs.pathExists(this.config.keyPath)) {
@@ -69,6 +92,14 @@ export class SessionEncryption {
69
92
  this.initialized = true;
70
93
  }
71
94
  }
95
+ /** Read existing key only: decrypting must never generate a replacement key. */
96
+ initializeForRead() {
97
+ const key = readFileSync(this.config.keyPath);
98
+ if (key.length !== KEY_LENGTH)
99
+ throw new Error('Invalid session encryption key');
100
+ this.key = key;
101
+ this.initialized = true;
102
+ }
72
103
  /**
73
104
  * Initialize with password-based key
74
105
  */
@@ -22,6 +22,7 @@ import { type LocalTtsEngine } from '../voice/local-tts.js';
22
22
  import { type TwoSpeedTtsRouteHint } from '../voice/two-speed-voice.js';
23
23
  export type { TwoSpeedTtsRouteHint } from '../voice/two-speed-voice.js';
24
24
  import type { PermissionMode } from '../security/permission-modes.js';
25
+ import { type ResponseDecider } from './respond-decider.js';
25
26
  import { type VoiceDeliveryProfile, type VoiceTurnContext } from './voice-entrainment.js';
26
27
  import { type VisualGroundingFn } from '../companion/visual-grounding.js';
27
28
  import type { CameraShareOptions, CameraShareResult } from '../companion/camera-share.js';
@@ -89,6 +90,15 @@ export interface VoiceStepOptions {
89
90
  shortFirst?: VoiceShortFirstConfig;
90
91
  /** Tell the outer audio pipeline that the fast route accepted the short-first contract. */
91
92
  onShortFirstReady?: (config: VoiceShortFirstConfig) => void;
93
+ /** Decision from respond-decider if already evaluated */
94
+ respondDecision?: {
95
+ respond: boolean;
96
+ reason: string;
97
+ };
98
+ /** Injected response decider for conversational continuity */
99
+ responseDecider?: ResponseDecider;
100
+ /** Explicit override for whether the robot was named or in engagement window */
101
+ robotNamed?: boolean;
92
102
  }
93
103
  export interface VoiceShortFirstConfig {
94
104
  /** Total number of spoken answer sentences, including the first useful sentence. */
@@ -500,6 +510,13 @@ export declare function rewriteRepeatedVoiceOpener(text: string, recentOpeners?:
500
510
  * can be proven without starting a model.
501
511
  */
502
512
  export declare function buildSpokenPromptAugmentation(heard: string, history?: VoiceHistoryTurn[], spokenPrefix?: string, delivery?: VoiceDeliveryProfile, options?: SpokenPromptAugmentationOptions): Promise<string>;
513
+ export declare function getVoiceResponseDecider(): ResponseDecider;
514
+ export declare function setVoiceResponseDecider(decider: ResponseDecider | undefined): void;
515
+ /**
516
+ * Determine if the utterance addresses the robot by name or takes place inside
517
+ * an active engagement window, consulting the respond-decider.
518
+ */
519
+ export declare function resolveVoiceRobotNamed(heard: string, replyOpts?: VoiceStepOptions): Promise<boolean>;
503
520
  export declare function defaultReply(heard: string, history?: VoiceHistoryTurn[], replyOpts?: VoiceStepOptions): Promise<string>;
504
521
  /**
505
522
  * Fast first proposition for an eligible developed/deliberative turn. Eligibility and
@@ -45,6 +45,7 @@ import { resolveElevenLabsCacheVoice, resolveElevenLabsFallbackEngine, resolveTt
45
45
  import { resolveKyutaiCacheVoice } from '../voice/kyutai-local-voice.js';
46
46
  import { selectTwoSpeedTtsRoute, twoSpeedTtsEnabled, } from '../voice/two-speed-voice.js';
47
47
  import { resolveVoiceboxConfig } from '../voice/voicebox-tts.js';
48
+ import { createResponseDecider } from './respond-decider.js';
48
49
  import { applyLimitsContract, avoidOpenersGuidance, detectEmotion, emotionalContinuityGuidance, emotionGuidance, expressiveTextGuidance, immediateEmotionAcknowledgement, IMMEDIATE_EMOTION_ACKNOWLEDGEMENTS, limitsContractGuidance, openerKey, pushOpener, } from '../companion/reply-augment.js';
49
50
  import { crisisGuidanceFor } from '../companion/crisis-safety.js';
50
51
  import { evolveRelationshipFromUtterance } from '../companion/relationship-evolution.js';
@@ -1263,6 +1264,32 @@ async function prepareSpokenTurn(heard, history = [], spokenPrefix, delivery, re
1263
1264
  ...(cognitiveLease ? { cognitiveLease } : {}),
1264
1265
  };
1265
1266
  }
1267
+ let sharedVoiceResponseDecider;
1268
+ export function getVoiceResponseDecider() {
1269
+ if (!sharedVoiceResponseDecider) {
1270
+ sharedVoiceResponseDecider = createResponseDecider();
1271
+ }
1272
+ return sharedVoiceResponseDecider;
1273
+ }
1274
+ export function setVoiceResponseDecider(decider) {
1275
+ sharedVoiceResponseDecider = decider;
1276
+ }
1277
+ /**
1278
+ * Determine if the utterance addresses the robot by name or takes place inside
1279
+ * an active engagement window, consulting the respond-decider.
1280
+ */
1281
+ export async function resolveVoiceRobotNamed(heard, replyOpts) {
1282
+ if (typeof replyOpts?.robotNamed === 'boolean') {
1283
+ return replyOpts.robotNamed;
1284
+ }
1285
+ if (replyOpts?.respondDecision) {
1286
+ const reason = replyOpts.respondDecision.reason;
1287
+ return reason === 'addressed' || reason === 'engaged';
1288
+ }
1289
+ const decider = replyOpts?.responseDecider ?? getVoiceResponseDecider();
1290
+ const decision = await decider.decide(heard);
1291
+ return decision.reason === 'addressed' || decision.reason === 'engaged';
1292
+ }
1266
1293
  export async function defaultReply(heard, history = [], replyOpts) {
1267
1294
  const fast = fastCompanionReply(heard);
1268
1295
  if (fast) {
@@ -1331,21 +1358,51 @@ export async function defaultReply(heard, history = [], replyOpts) {
1331
1358
  cognitiveLease = prepared.cognitiveLease;
1332
1359
  replyOpts?.onProviderResolved?.(route);
1333
1360
  logger.debug(`[voice] reply model: ${route.model} — ${route.reason}`);
1334
- const client = new CodeBuddyClient(route.apiKey, route.model, route.baseURL);
1335
- const resp = await client.chat([
1336
- { role: 'system', content: systemPrompt },
1337
- ...history,
1338
- { role: 'user', content: heard },
1339
- ], [],
1340
- // Additive: thread the barge-in signal so an interrupt aborts the in-flight
1341
- // LLM call. Undefined when not interruptible → the call is unchanged.
1342
- {
1343
- temperature: voiceTemperature(),
1344
- maxTokens: voiceMaxTokens(heard, history),
1345
- ...(replyOpts?.signal ? { signal: replyOpts.signal } : {}),
1346
- });
1347
- reportReplyTimingPhase(replyOpts, replyOpts?.spokenPrefix ? 'continuation_generation_complete' : 'generation_complete');
1348
- const reply = (resp?.choices?.[0]?.message?.content ?? '').trim();
1361
+ let reply = '';
1362
+ const { isCompanionToolsEnabled } = await import('../companion/companion-toolset.js');
1363
+ if (isCompanionToolsEnabled(process.env)) {
1364
+ const { resolveCompanionIdentity } = await import('../companion/companion-identity.js');
1365
+ const { runCompanionChannelTurn } = await import('../channels/companion-channel-turn.js');
1366
+ const robotNamed = await resolveVoiceRobotNamed(heard, replyOpts);
1367
+ const identity = resolveCompanionIdentity({
1368
+ channel: 'voice',
1369
+ isVoicePresence: true,
1370
+ robotNamed,
1371
+ env: process.env,
1372
+ });
1373
+ const turnResult = await runCompanionChannelTurn({
1374
+ apiKey: route.apiKey,
1375
+ baseUrl: route.baseURL,
1376
+ model: route.model,
1377
+ messages: [
1378
+ { role: 'system', content: systemPrompt },
1379
+ ...history,
1380
+ { role: 'user', content: heard },
1381
+ ],
1382
+ identity,
1383
+ surface: 'voice',
1384
+ env: process.env,
1385
+ signal: replyOpts?.signal,
1386
+ maxTokens: voiceMaxTokens(heard, history),
1387
+ });
1388
+ reply = turnResult.text.trim();
1389
+ }
1390
+ else {
1391
+ const client = new CodeBuddyClient(route.apiKey, route.model, route.baseURL);
1392
+ const resp = await client.chat([
1393
+ { role: 'system', content: systemPrompt },
1394
+ ...history,
1395
+ { role: 'user', content: heard },
1396
+ ], [],
1397
+ // Additive: thread the barge-in signal so an interrupt aborts the in-flight
1398
+ // LLM call. Undefined when not interruptible → the call is unchanged.
1399
+ {
1400
+ temperature: voiceTemperature(),
1401
+ maxTokens: voiceMaxTokens(heard, history),
1402
+ ...(replyOpts?.signal ? { signal: replyOpts.signal } : {}),
1403
+ });
1404
+ reply = (resp?.choices?.[0]?.message?.content ?? '').trim();
1405
+ }
1349
1406
  if (reply && !replyOpts?.signal?.aborted)
1350
1407
  cognitiveLease?.commit();
1351
1408
  else