@phuetz/code-buddy 2.0.0 → 2.2.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 (421) hide show
  1. package/README.fr.md +56 -0
  2. package/README.md +74 -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 +176 -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.d.ts +7 -0
  13. package/dist/agent/execution/tool-selection-strategy.js +32 -2
  14. package/dist/agent/facades/session-facade.d.ts +0 -6
  15. package/dist/agent/facades/session-facade.js +0 -45
  16. package/dist/agent/hermes-claw-migrate.d.ts +0 -21
  17. package/dist/agent/hermes-claw-migrate.js +9 -4
  18. package/dist/agent/hermes-protocol-gateways.js +8 -1
  19. package/dist/agent/repo-profiler.js +17 -4
  20. package/dist/agent/self-improvement/authored-tool-runtime.js +10 -2
  21. package/dist/agent/self-improvement/evolution/ast-novelty.d.ts +5 -0
  22. package/dist/agent/self-improvement/evolution/ast-novelty.js +58 -23
  23. package/dist/agent/self-improvement/evolution/code-variant-store.d.ts +3 -0
  24. package/dist/agent/self-improvement/evolution/evolution-engine.js +2 -0
  25. package/dist/agent/self-improvement/evolution/protected-paths.js +6 -0
  26. package/dist/agent/self-improvement/evolution/variant-fitness.d.ts +13 -2
  27. package/dist/agent/self-improvement/evolution/variant-fitness.js +184 -58
  28. package/dist/agent/self-improvement/evolution/worktree-scorer.d.ts +2 -0
  29. package/dist/agent/self-improvement/evolution/worktree-scorer.js +15 -0
  30. package/dist/agent/self-improvement/evolutionary-archive.d.ts +2 -1
  31. package/dist/agent/self-improvement/evolutionary-archive.js +34 -5
  32. package/dist/agent/self-improvement/proposal-store.d.ts +1 -0
  33. package/dist/agent/self-improvement/proposal-store.js +79 -17
  34. package/dist/agent/self-improvement/skill-apply-journal.d.ts +44 -0
  35. package/dist/agent/self-improvement/skill-apply-journal.js +178 -0
  36. package/dist/agent/self-improvement/skill-behavior-benchmark.d.ts +3 -0
  37. package/dist/agent/self-improvement/skill-behavior-benchmark.js +19 -0
  38. package/dist/agent/self-improvement/skill-behavior-gate.d.ts +34 -0
  39. package/dist/agent/self-improvement/skill-behavior-gate.js +96 -0
  40. package/dist/agent/self-improvement/skill-engine.d.ts +22 -0
  41. package/dist/agent/self-improvement/skill-engine.js +385 -13
  42. package/dist/agent/self-improvement/skill-gate.d.ts +2 -0
  43. package/dist/agent/self-improvement/skill-gate.js +17 -2
  44. package/dist/agent/self-improvement/skill-mutator.d.ts +6 -1
  45. package/dist/agent/self-improvement/skill-mutator.js +51 -15
  46. package/dist/agent/self-improvement/skill-types.d.ts +2 -1
  47. package/dist/agent/self-improvement/types.d.ts +11 -0
  48. package/dist/agent/tool-handler.d.ts +1 -1
  49. package/dist/agent/tool-handler.js +28 -9
  50. package/dist/auth/profile-manager.js +1 -1
  51. package/dist/channels/companion-channel-turn.d.ts +53 -4
  52. package/dist/channels/companion-channel-turn.js +218 -16
  53. package/dist/channels/slash-parity.d.ts +4 -6
  54. package/dist/channels/slash-parity.js +9 -44
  55. package/dist/cli/session-commands.d.ts +9 -0
  56. package/dist/cli/session-commands.js +45 -5
  57. package/dist/cli/session-picker.d.ts +43 -0
  58. package/dist/cli/session-picker.js +128 -0
  59. package/dist/codebuddy/a2a-call-tool-defs.d.ts +26 -0
  60. package/dist/codebuddy/a2a-call-tool-defs.js +12 -0
  61. package/dist/codebuddy/fleet-tool-defs.d.ts +2 -0
  62. package/dist/codebuddy/fleet-tool-defs.js +55 -0
  63. package/dist/codebuddy/providers/chatgpt-headers.d.ts +18 -0
  64. package/dist/codebuddy/providers/chatgpt-headers.js +37 -0
  65. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +2 -1
  66. package/dist/codebuddy/providers/provider-chatgpt-responses.js +10 -23
  67. package/dist/codebuddy/providers/provider-openai-compat.d.ts +7 -0
  68. package/dist/codebuddy/providers/provider-openai-compat.js +29 -1
  69. package/dist/codebuddy/ragchat-tool-defs.d.ts +31 -0
  70. package/dist/codebuddy/ragchat-tool-defs.js +19 -0
  71. package/dist/codebuddy/resource-catalog-tool-defs.d.ts +28 -0
  72. package/dist/codebuddy/resource-catalog-tool-defs.js +18 -0
  73. package/dist/codebuddy/tool-definitions/agent-tools.js +2 -2
  74. package/dist/codebuddy/tool-definitions/code-exec-tools.js +5 -1
  75. package/dist/codebuddy/tool-definitions/cron-tools.js +6 -0
  76. package/dist/codebuddy/tool-definitions/self-describe-tools.js +6 -1
  77. package/dist/codebuddy/tools.d.ts +0 -6
  78. package/dist/codebuddy/tools.js +25 -3
  79. package/dist/commands/cli/daemon-commands.js +3 -1
  80. package/dist/commands/cli/evolve-command.js +21 -5
  81. package/dist/commands/cli/fleet-collaboration-commands.d.ts +2 -0
  82. package/dist/commands/cli/fleet-collaboration-commands.js +57 -0
  83. package/dist/commands/cli/fleet-commands.js +29 -0
  84. package/dist/commands/cli/fleet-mission-commands.d.ts +3 -0
  85. package/dist/commands/cli/fleet-mission-commands.js +72 -0
  86. package/dist/commands/cli/fleet-rooms-commands.d.ts +10 -0
  87. package/dist/commands/cli/fleet-rooms-commands.js +289 -0
  88. package/dist/commands/cli/tools-commands.js +2 -1
  89. package/dist/commands/cli/triage-command.d.ts +8 -0
  90. package/dist/commands/cli/triage-command.js +72 -0
  91. package/dist/commands/cli/utility-commands.js +25 -1
  92. package/dist/commands/client-dispatcher.d.ts +7 -0
  93. package/dist/commands/client-dispatcher.js +73 -21
  94. package/dist/commands/cron-cli/index.d.ts +3 -1
  95. package/dist/commands/cron-cli/index.js +25 -0
  96. package/dist/commands/device-auth.d.ts +10 -0
  97. package/dist/commands/device-auth.js +91 -0
  98. package/dist/commands/enhanced-command-handler.d.ts +9 -0
  99. package/dist/commands/enhanced-command-handler.js +28 -6
  100. package/dist/commands/handlers/agent-handlers.d.ts +1 -1
  101. package/dist/commands/handlers/agent-handlers.js +4 -4
  102. package/dist/commands/handlers/channel-handlers.js +53 -2
  103. package/dist/commands/handlers/export-handlers.d.ts +3 -2
  104. package/dist/commands/handlers/export-handlers.js +55 -28
  105. package/dist/commands/handlers/extra-handlers.js +5 -1
  106. package/dist/commands/handlers/grill-me-handler.js +26 -18
  107. package/dist/commands/handlers/memory-handlers.d.ts +6 -2
  108. package/dist/commands/handlers/memory-handlers.js +35 -15
  109. package/dist/commands/handlers/missing-handlers.d.ts +2 -2
  110. package/dist/commands/handlers/missing-handlers.js +12 -4
  111. package/dist/commands/handlers/resources-handler.d.ts +12 -0
  112. package/dist/commands/handlers/resources-handler.js +52 -0
  113. package/dist/commands/handlers/starter-handlers.d.ts +1 -1
  114. package/dist/commands/handlers/starter-handlers.js +32 -2
  115. package/dist/commands/handlers/switch-handler.d.ts +1 -1
  116. package/dist/commands/handlers/switch-handler.js +15 -0
  117. package/dist/commands/handlers/test-handlers.js +4 -86
  118. package/dist/commands/handlers/ui-handlers.js +11 -5
  119. package/dist/commands/login-chatgpt.d.ts +8 -0
  120. package/dist/commands/login-chatgpt.js +17 -0
  121. package/dist/commands/mcp-import.d.ts +11 -0
  122. package/dist/commands/mcp-import.js +67 -0
  123. package/dist/commands/mcp-invoke.d.ts +2 -0
  124. package/dist/commands/mcp-invoke.js +68 -0
  125. package/dist/commands/mcp.d.ts +13 -0
  126. package/dist/commands/mcp.js +66 -21
  127. package/dist/commands/pipeline.d.ts +7 -1
  128. package/dist/commands/pipeline.js +104 -7
  129. package/dist/commands/resources.d.ts +2 -0
  130. package/dist/commands/resources.js +64 -0
  131. package/dist/commands/skills-cli/index.js +35 -12
  132. package/dist/commands/slash/builtin-commands.js +16 -3
  133. package/dist/commands/slash/index.d.ts +3 -1
  134. package/dist/commands/slash/index.js +2 -0
  135. package/dist/commands/slash/surfaces.d.ts +53 -0
  136. package/dist/commands/slash/surfaces.js +177 -0
  137. package/dist/commands/slash/types.d.ts +17 -0
  138. package/dist/commands/try.d.ts +1 -1
  139. package/dist/commands/try.js +1 -1
  140. package/dist/companion/companion-identity.d.ts +43 -0
  141. package/dist/companion/companion-identity.js +136 -0
  142. package/dist/companion/companion-toolset.d.ts +66 -0
  143. package/dist/companion/companion-toolset.js +368 -0
  144. package/dist/companion/companion-turn.d.ts +10 -2
  145. package/dist/companion/companion-turn.js +38 -1
  146. package/dist/config/code-exec-policy.d.ts +25 -0
  147. package/dist/config/code-exec-policy.js +39 -0
  148. package/dist/config/model-tools.d.ts +9 -0
  149. package/dist/config/model-tools.js +103 -0
  150. package/dist/context/context-manager-v2.js +2 -2
  151. package/dist/context/lm-resizer-compressor.js +3 -2
  152. package/dist/context/lm-resizer-diagnostics.d.ts +9 -0
  153. package/dist/context/lm-resizer-diagnostics.js +30 -0
  154. package/dist/context/tool-output-masking.js +3 -4
  155. package/dist/curator/curator.d.ts +6 -0
  156. package/dist/curator/curator.js +53 -3
  157. package/dist/daemon/cron-agent-bridge.d.ts +6 -0
  158. package/dist/daemon/cron-agent-bridge.js +31 -4
  159. package/dist/doctor/index.d.ts +11 -2
  160. package/dist/doctor/index.js +84 -21
  161. package/dist/doctor/integrations.d.ts +197 -0
  162. package/dist/doctor/integrations.js +241 -0
  163. package/dist/doctor/triage.d.ts +108 -0
  164. package/dist/doctor/triage.js +304 -0
  165. package/dist/errors/crash-handler.js +2 -0
  166. package/dist/fleet/collaboration.d.ts +44 -0
  167. package/dist/fleet/collaboration.js +170 -0
  168. package/dist/fleet/fleet-listener.d.ts +1 -0
  169. package/dist/fleet/fleet-listener.js +87 -46
  170. package/dist/fleet/fleet-registry.d.ts +2 -0
  171. package/dist/fleet/resource-catalog.d.ts +245 -0
  172. package/dist/fleet/resource-catalog.js +211 -0
  173. package/dist/fleet/rooms/room-access.d.ts +138 -0
  174. package/dist/fleet/rooms/room-access.js +196 -0
  175. package/dist/fleet/rooms/room-client.d.ts +158 -0
  176. package/dist/fleet/rooms/room-client.js +685 -0
  177. package/dist/fleet/rooms/room-event.d.ts +139 -0
  178. package/dist/fleet/rooms/room-event.js +330 -0
  179. package/dist/fleet/rooms/room-filter.d.ts +48 -0
  180. package/dist/fleet/rooms/room-filter.js +152 -0
  181. package/dist/fleet/rooms/room-hub.d.ts +154 -0
  182. package/dist/fleet/rooms/room-hub.js +363 -0
  183. package/dist/fleet/rooms/room-identity.d.ts +26 -0
  184. package/dist/fleet/rooms/room-identity.js +82 -0
  185. package/dist/fleet/rooms/room-lock.d.ts +34 -0
  186. package/dist/fleet/rooms/room-lock.js +180 -0
  187. package/dist/fleet/rooms/room-observations.d.ts +7 -0
  188. package/dist/fleet/rooms/room-observations.js +61 -0
  189. package/dist/fleet/rooms/room-server.d.ts +28 -0
  190. package/dist/fleet/rooms/room-server.js +75 -0
  191. package/dist/fleet/rooms/room-store.d.ts +119 -0
  192. package/dist/fleet/rooms/room-store.js +448 -0
  193. package/dist/fleet/rooms/room-ws-bridge.d.ts +27 -0
  194. package/dist/fleet/rooms/room-ws-bridge.js +137 -0
  195. package/dist/fleet/sensory-mission-bridge.d.ts +42 -0
  196. package/dist/fleet/sensory-mission-bridge.js +136 -0
  197. package/dist/git/worktree-sessions.js +1 -0
  198. package/dist/harness/contract.d.ts +4 -4
  199. package/dist/harness/fleet-supervisor.d.ts +16 -0
  200. package/dist/harness/fleet-supervisor.js +64 -0
  201. package/dist/harness/index.d.ts +4 -0
  202. package/dist/harness/index.js +4 -0
  203. package/dist/harness/mission-runner.d.ts +3 -0
  204. package/dist/harness/mission-runner.js +53 -0
  205. package/dist/harness/mission-store.d.ts +93 -0
  206. package/dist/harness/mission-store.js +297 -0
  207. package/dist/harness/tool-harness.d.ts +56 -0
  208. package/dist/harness/tool-harness.js +153 -0
  209. package/dist/hooks/use-enhanced-input.js +48 -4
  210. package/dist/hooks/use-input-handler.d.ts +1 -0
  211. package/dist/hooks/use-input-handler.js +82 -22
  212. package/dist/hooks/use-input-history.js +1 -0
  213. package/dist/identity/lisa-introspection.js +5 -2
  214. package/dist/identity/operational-self-model.d.ts +2 -0
  215. package/dist/identity/operational-self-model.js +4 -0
  216. package/dist/index.js +52 -6
  217. package/dist/mcp/client.d.ts +11 -0
  218. package/dist/mcp/client.js +68 -3
  219. package/dist/mcp/config.d.ts +20 -0
  220. package/dist/mcp/config.js +121 -8
  221. package/dist/mcp/import-normalize.d.ts +12 -0
  222. package/dist/mcp/import-normalize.js +114 -0
  223. package/dist/mcp/mcp-oauth-constants.d.ts +8 -0
  224. package/dist/mcp/mcp-oauth-constants.js +9 -0
  225. package/dist/mcp/mcp-oauth-provider.d.ts +80 -0
  226. package/dist/mcp/mcp-oauth-provider.js +241 -0
  227. package/dist/mcp/mcp-oauth.d.ts +60 -1
  228. package/dist/mcp/mcp-oauth.js +241 -65
  229. package/dist/mcp/transports.d.ts +14 -5
  230. package/dist/mcp/transports.js +112 -101
  231. package/dist/mcp/types.d.ts +5 -0
  232. package/dist/media/comfyui-recipe-contract.d.ts +7 -7
  233. package/dist/memory/facts-memory.d.ts +11 -1
  234. package/dist/memory/facts-memory.js +47 -8
  235. package/dist/memory/memory-candidate-queue.d.ts +2 -2
  236. package/dist/memory/memory-candidate-queue.js +6 -6
  237. package/dist/memory/persistent-memory.d.ts +13 -8
  238. package/dist/memory/persistent-memory.js +77 -30
  239. package/dist/nodes/device-node.js +5 -9
  240. package/dist/observability/mobile-supervision-gateway-contract.js +20 -7
  241. package/dist/observability/run-event-writer.d.ts +21 -0
  242. package/dist/observability/run-event-writer.js +81 -0
  243. package/dist/observability/run-store.d.ts +5 -0
  244. package/dist/observability/run-store.js +18 -13
  245. package/dist/observability/run-trajectory.js +1 -1
  246. package/dist/optimization/prompt-cache.d.ts +19 -2
  247. package/dist/optimization/prompt-cache.js +41 -6
  248. package/dist/orchestration/orchestrator.d.ts +21 -0
  249. package/dist/orchestration/orchestrator.js +122 -15
  250. package/dist/persistence/session-content.d.ts +8 -0
  251. package/dist/persistence/session-content.js +76 -0
  252. package/dist/persistence/session-handoff.d.ts +64 -0
  253. package/dist/persistence/session-handoff.js +80 -0
  254. package/dist/persistence/session-history.d.ts +4 -0
  255. package/dist/persistence/session-history.js +38 -0
  256. package/dist/persistence/session-store.d.ts +9 -0
  257. package/dist/persistence/session-store.js +48 -20
  258. package/dist/personas/persona-manager.js +17 -1
  259. package/dist/plugins/code-explorer/CodeExplorerManager.d.ts +10 -4
  260. package/dist/plugins/code-explorer/CodeExplorerManager.js +69 -16
  261. package/dist/plugins/plugin-manager.js +10 -1
  262. package/dist/prompts/prompt-manager.js +11 -4
  263. package/dist/prompts/system-base.js +6 -2
  264. package/dist/protocols/a2a/codebuddy-executor.d.ts +11 -2
  265. package/dist/protocols/a2a/codebuddy-executor.js +9 -8
  266. package/dist/protocols/a2a/index.d.ts +1 -1
  267. package/dist/protocols/a2a/jsonrpc-v1.d.ts +41 -0
  268. package/dist/protocols/a2a/jsonrpc-v1.js +145 -0
  269. package/dist/protocols/a2a/peer-config.d.ts +20 -0
  270. package/dist/protocols/a2a/peer-config.js +55 -0
  271. package/dist/providers/codex-oauth.js +30 -6
  272. package/dist/providers/model-provider-compat.js +2 -1
  273. package/dist/sandbox/os-sandbox.d.ts +1 -1
  274. package/dist/scheduler/cron-scheduler.d.ts +15 -2
  275. package/dist/scheduler/cron-scheduler.js +35 -4
  276. package/dist/scheduler/job-notepad.d.ts +69 -0
  277. package/dist/scheduler/job-notepad.js +339 -0
  278. package/dist/security/audit-logger.d.ts +1 -1
  279. package/dist/security/compute-confinement.d.ts +4 -0
  280. package/dist/security/compute-confinement.js +89 -0
  281. package/dist/security/session-encryption.d.ts +4 -0
  282. package/dist/security/session-encryption.js +31 -0
  283. package/dist/sensory/voice-loop.d.ts +17 -0
  284. package/dist/sensory/voice-loop.js +72 -15
  285. package/dist/server/auth/device-session-context.d.ts +9 -0
  286. package/dist/server/auth/device-session-context.js +13 -0
  287. package/dist/server/auth/device-store.d.ts +89 -0
  288. package/dist/server/auth/device-store.js +261 -0
  289. package/dist/server/auth/device-token.d.ts +3 -0
  290. package/dist/server/auth/device-token.js +6 -0
  291. package/dist/server/auth/jwt.js +4 -0
  292. package/dist/server/index.d.ts +0 -10
  293. package/dist/server/index.js +28 -0
  294. package/dist/server/middleware/auth.js +3 -2
  295. package/dist/server/routes/a2a-jsonrpc.d.ts +9 -0
  296. package/dist/server/routes/a2a-jsonrpc.js +66 -0
  297. package/dist/server/routes/a2a-protocol.d.ts +12 -0
  298. package/dist/server/routes/a2a-protocol.js +1 -1
  299. package/dist/server/routes/device-auth.d.ts +4 -0
  300. package/dist/server/routes/device-auth.js +40 -0
  301. package/dist/server/types.d.ts +4 -0
  302. package/dist/server/websocket/handler.d.ts +13 -0
  303. package/dist/server/websocket/handler.js +102 -17
  304. package/dist/services/prompt-builder.d.ts +2 -0
  305. package/dist/services/prompt-builder.js +16 -6
  306. package/dist/services/runtime-settings-context.d.ts +81 -0
  307. package/dist/services/runtime-settings-context.js +79 -0
  308. package/dist/skills/executor.js +2 -0
  309. package/dist/skills/hub.js +16 -14
  310. package/dist/skills/local-inventory.d.ts +21 -0
  311. package/dist/skills/local-inventory.js +72 -0
  312. package/dist/skills/skill-loader.js +2 -2
  313. package/dist/skills/skill-usage-store.d.ts +49 -0
  314. package/dist/skills/skill-usage-store.js +121 -0
  315. package/dist/testing/ai-integration-tests.js +8 -5
  316. package/dist/themes/theme-manager.d.ts +12 -0
  317. package/dist/themes/theme-manager.js +29 -0
  318. package/dist/themes/theme-schema.d.ts +24 -24
  319. package/dist/tools/a2a-call-tool.d.ts +34 -0
  320. package/dist/tools/a2a-call-tool.js +75 -0
  321. package/dist/tools/bash/streaming-executor.d.ts +1 -0
  322. package/dist/tools/bash/streaming-executor.js +78 -90
  323. package/dist/tools/code-exec-preflight-runner.d.ts +41 -0
  324. package/dist/tools/code-exec-preflight-runner.js +254 -0
  325. package/dist/tools/code-exec-preflight.d.ts +44 -0
  326. package/dist/tools/code-exec-preflight.js +501 -0
  327. package/dist/tools/code-exec-tool.d.ts +48 -1
  328. package/dist/tools/code-exec-tool.js +191 -47
  329. package/dist/tools/comment-watcher.js +29 -12
  330. package/dist/tools/core-code-inspection.d.ts +56 -0
  331. package/dist/tools/core-code-inspection.js +86 -0
  332. package/dist/tools/create-skill-tool.d.ts +2 -3
  333. package/dist/tools/create-skill-tool.js +16 -75
  334. package/dist/tools/cronjob-tool.d.ts +1 -0
  335. package/dist/tools/cronjob-tool.js +1 -0
  336. package/dist/tools/env-doctor-tool.d.ts +2 -0
  337. package/dist/tools/env-doctor-tool.js +4 -3
  338. package/dist/tools/execute-code-rpc-invoker.d.ts +1 -1
  339. package/dist/tools/execute-code-rpc-invoker.js +35 -19
  340. package/dist/tools/execute-code-runner.d.ts +2 -0
  341. package/dist/tools/execute-code-runner.js +52 -17
  342. package/dist/tools/fleet-room-tool.d.ts +104 -0
  343. package/dist/tools/fleet-room-tool.js +124 -0
  344. package/dist/tools/integration-tool-hints.d.ts +4 -0
  345. package/dist/tools/integration-tool-hints.js +19 -0
  346. package/dist/tools/list-peers-tool.d.ts +3 -0
  347. package/dist/tools/list-peers-tool.js +11 -4
  348. package/dist/tools/media-generation-tool.d.ts +16 -1
  349. package/dist/tools/media-generation-tool.js +341 -9
  350. package/dist/tools/merge-conflict-tool.js +53 -25
  351. package/dist/tools/metadata.d.ts +1 -5
  352. package/dist/tools/metadata.js +30 -28
  353. package/dist/tools/peer-tool-invoke-tool.d.ts +61 -0
  354. package/dist/tools/peer-tool-invoke-tool.js +396 -0
  355. package/dist/tools/ragchat-tool.d.ts +39 -0
  356. package/dist/tools/ragchat-tool.js +105 -0
  357. package/dist/tools/registry/fleet-tools.d.ts +21 -3
  358. package/dist/tools/registry/fleet-tools.js +137 -4
  359. package/dist/tools/registry/index.d.ts +1 -1
  360. package/dist/tools/registry/index.js +2 -2
  361. package/dist/tools/registry/knowledge-tools.d.ts +2 -2
  362. package/dist/tools/registry/knowledge-tools.js +2 -2
  363. package/dist/tools/registry/lessons-tools.d.ts +7 -0
  364. package/dist/tools/registry/lessons-tools.js +43 -3
  365. package/dist/tools/registry/memory-tools.d.ts +1 -1
  366. package/dist/tools/registry/memory-tools.js +8 -8
  367. package/dist/tools/registry/research-tools.d.ts +1 -0
  368. package/dist/tools/registry/research-tools.js +4 -13
  369. package/dist/tools/registry/self-describe-tools.d.ts +1 -1
  370. package/dist/tools/registry/self-describe-tools.js +43 -30
  371. package/dist/tools/resource-catalog-tool.d.ts +38 -0
  372. package/dist/tools/resource-catalog-tool.js +46 -0
  373. package/dist/tools/skills-inspection-tool.js +30 -1
  374. package/dist/tools/stream-tool-output.d.ts +3 -0
  375. package/dist/tools/stream-tool-output.js +33 -0
  376. package/dist/tools/tool-call-scheduler.d.ts +10 -0
  377. package/dist/tools/tool-call-scheduler.js +58 -0
  378. package/dist/tools/tool-effect.d.ts +5 -0
  379. package/dist/tools/tool-effect.js +26 -0
  380. package/dist/tools/tool-search.d.ts +12 -11
  381. package/dist/tools/tool-search.js +67 -26
  382. package/dist/tools/types.d.ts +6 -0
  383. package/dist/ui/components/ChatHistory.d.ts +1 -1
  384. package/dist/ui/components/ChatHistory.js +2 -2
  385. package/dist/ui/components/ChatInput.d.ts +2 -1
  386. package/dist/ui/components/ChatInput.js +42 -88
  387. package/dist/ui/components/ChatInterface.d.ts +4 -2
  388. package/dist/ui/components/ChatInterface.js +27 -63
  389. package/dist/ui/components/CommandSuggestions.d.ts +0 -1
  390. package/dist/ui/components/CommandSuggestions.js +4 -6
  391. package/dist/ui/components/FileAutocomplete.js +1 -1
  392. package/dist/ui/components/KeyboardHelp.js +8 -6
  393. package/dist/ui/components/ModelSelection.js +6 -5
  394. package/dist/ui/components/StatusBar.d.ts +2 -1
  395. package/dist/ui/components/StatusBar.js +8 -7
  396. package/dist/ui/context/theme-context.js +3 -2
  397. package/dist/ui/index.d.ts +1 -1
  398. package/dist/ui/index.js +1 -1
  399. package/dist/ui/utils/markdown-renderer.js +4 -13
  400. package/dist/utils/atomic-write.d.ts +22 -0
  401. package/dist/utils/atomic-write.js +29 -0
  402. package/dist/utils/bounded-output.d.ts +16 -0
  403. package/dist/utils/bounded-output.js +63 -0
  404. package/dist/utils/config-validation/schema.d.ts +2 -2
  405. package/dist/utils/device-store-file.d.ts +4 -0
  406. package/dist/utils/device-store-file.js +40 -0
  407. package/dist/utils/export-manager.d.ts +9 -0
  408. package/dist/utils/export-manager.js +19 -0
  409. package/dist/utils/first-use-hints.d.ts +25 -0
  410. package/dist/utils/first-use-hints.js +58 -0
  411. package/dist/utils/graceful-shutdown.d.ts +8 -0
  412. package/dist/utils/graceful-shutdown.js +23 -7
  413. package/dist/utils/history-manager.js +1 -1
  414. package/dist/utils/update-notifier.js +16 -18
  415. package/dist/wizard/environment-detection.d.ts +5 -1
  416. package/dist/wizard/environment-detection.js +49 -7
  417. package/dist/wizard/onboarding.d.ts +5 -1
  418. package/dist/wizard/onboarding.js +90 -30
  419. package/dist/wizard/provider-onboarding.d.ts +2 -0
  420. package/dist/wizard/provider-onboarding.js +55 -17
  421. package/package.json +7 -3
@@ -6,6 +6,16 @@ function serializePayload(payload) {
6
6
  data: payload,
7
7
  };
8
8
  }
9
+ /** Activity telemetry is best-effort: a failure never changes the tool result. */
10
+ async function recordView(name) {
11
+ try {
12
+ const { recordSkillActivity } = await import('../skills/skill-usage-store.js');
13
+ recordSkillActivity(name, 'view', { source: 'skill_view' });
14
+ }
15
+ catch {
16
+ // Telemetry must never make skill inspection fail.
17
+ }
18
+ }
9
19
  function stripUsage(skill) {
10
20
  const { usage: _usage, ...rest } = skill;
11
21
  return rest;
@@ -19,8 +29,16 @@ export async function executeSkillsListTool(input) {
19
29
  const includeUsage = input.include_usage !== false;
20
30
  const shown = includeDisabled ? all : all.filter((skill) => skill.enabled !== false);
21
31
  const skills = includeUsage ? shown : shown.map(stripUsage);
32
+ const { readLocalSkillInventory } = await import('../skills/local-inventory.js');
33
+ const inventory = await readLocalSkillInventory(all);
34
+ const availableSkills = inventory.entries.filter((skill) => skill.available);
22
35
  return serializePayload({
23
36
  action: 'skills_list',
37
+ availableSkills,
38
+ availableCount: availableSkills.length,
39
+ unavailableSkills: inventory.entries.filter((skill) => !skill.available),
40
+ discoveryErrors: inventory.errors,
41
+ guidance: 'Use availableSkills to answer which skills can be used. Requirements may need tools or external configuration. The skills/count/total fields below describe only hub records, not the complete local catalogue. Missing files are not configuration errors.',
24
42
  count: skills.length,
25
43
  total: all.length,
26
44
  includeDisabled,
@@ -40,9 +58,20 @@ export async function executeSkillViewTool(input) {
40
58
  const { getSkillsHub } = await import('../skills/hub.js');
41
59
  const result = getSkillsHub().info(name);
42
60
  if (!result) {
43
- return { success: false, error: `skill_view: skill not found: ${name}` };
61
+ const { readLocalSkillInventory, readLocalSkillContent } = await import('../skills/local-inventory.js');
62
+ const inventory = await readLocalSkillInventory(getSkillsHub().listWithIntegrity());
63
+ const skill = inventory.entries.find((entry) => entry.name === name);
64
+ if (!skill)
65
+ return { success: false, error: `skill_view: skill not found: ${name}` };
66
+ await recordView(name);
67
+ return serializePayload({
68
+ action: 'skill_view',
69
+ skill,
70
+ ...(input.include_content !== false ? { content: await readLocalSkillContent(skill) } : {}),
71
+ });
44
72
  }
45
73
  const includeContent = input.include_content !== false;
74
+ await recordView(name);
46
75
  return serializePayload({
47
76
  action: 'skill_view',
48
77
  installed: result.installed,
@@ -0,0 +1,3 @@
1
+ import type { ToolResult } from '../types/index.js';
2
+ /** Adapt a callback-based guarded tool invocation without bypassing its dispatch pipeline. */
3
+ export declare function streamToolOutput(run: (signal: AbortSignal, emit: (delta: string) => void) => Promise<ToolResult>, signal?: AbortSignal): AsyncGenerator<string, ToolResult, undefined>;
@@ -0,0 +1,33 @@
1
+ import { BoundedOutput } from '../utils/bounded-output.js';
2
+ /** Adapt a callback-based guarded tool invocation without bypassing its dispatch pipeline. */
3
+ export async function* streamToolOutput(run, signal) {
4
+ const controller = new AbortController();
5
+ const abort = () => controller.abort();
6
+ signal?.addEventListener('abort', abort, { once: true });
7
+ if (signal?.aborted)
8
+ abort();
9
+ const pending = new BoundedOutput(64 * 1024);
10
+ let result;
11
+ let wake;
12
+ const notify = () => { wake?.(); wake = undefined; };
13
+ const completion = Promise.resolve().then(() => run(controller.signal, delta => { pending.append(delta); notify(); }))
14
+ .then(value => { result = value; })
15
+ .catch(error => { result = { success: false, error: error instanceof Error ? error.message : String(error) }; })
16
+ .finally(notify);
17
+ try {
18
+ while (!result || pending.retainedBytes) {
19
+ if (pending.retainedBytes)
20
+ yield pending.drain();
21
+ else
22
+ await new Promise(resolve => { wake = resolve; });
23
+ }
24
+ return result;
25
+ }
26
+ finally {
27
+ abort();
28
+ signal?.removeEventListener('abort', abort);
29
+ // Observe completion without blocking a consumer that intentionally stops.
30
+ void completion;
31
+ }
32
+ }
33
+ //# sourceMappingURL=stream-tool-output.js.map
@@ -0,0 +1,10 @@
1
+ /** FIFO barriers for mutations, bounded parallelism for explicitly safe reads. */
2
+ export declare class ToolCallScheduler {
3
+ private readonly concurrency;
4
+ private active;
5
+ private exclusive;
6
+ private queue;
7
+ constructor(concurrency?: number);
8
+ schedule<T>(parallel: boolean, action: () => Promise<T>, signal?: AbortSignal): Promise<T>;
9
+ private pump;
10
+ }
@@ -0,0 +1,58 @@
1
+ /** FIFO barriers for mutations, bounded parallelism for explicitly safe reads. */
2
+ export class ToolCallScheduler {
3
+ concurrency;
4
+ active = 0;
5
+ exclusive = false;
6
+ queue = [];
7
+ constructor(concurrency = 4) {
8
+ this.concurrency = concurrency;
9
+ if (!Number.isSafeInteger(concurrency) || concurrency < 1 || concurrency > 64)
10
+ throw new Error('Concurrency must be an integer between 1 and 64');
11
+ }
12
+ schedule(parallel, action, signal) {
13
+ return new Promise((resolve, reject) => {
14
+ if (signal?.aborted) {
15
+ reject(new Error('Tool call cancelled'));
16
+ return;
17
+ }
18
+ const entry = { parallel, run: async () => {
19
+ signal?.removeEventListener('abort', abort);
20
+ try {
21
+ resolve(await action());
22
+ }
23
+ catch (error) {
24
+ reject(error);
25
+ }
26
+ } };
27
+ const abort = () => {
28
+ const index = this.queue.indexOf(entry);
29
+ if (index < 0)
30
+ return;
31
+ this.queue.splice(index, 1);
32
+ signal?.removeEventListener('abort', abort);
33
+ reject(new Error('Tool call cancelled'));
34
+ this.pump();
35
+ };
36
+ signal?.addEventListener('abort', abort, { once: true });
37
+ this.queue.push(entry);
38
+ this.pump();
39
+ });
40
+ }
41
+ pump() {
42
+ while (!this.exclusive && this.active < this.concurrency && this.queue.length) {
43
+ const next = this.queue[0];
44
+ if (!next.parallel && this.active)
45
+ return;
46
+ this.queue.shift();
47
+ this.active++;
48
+ this.exclusive = !next.parallel;
49
+ void next.run().finally(() => {
50
+ this.active--;
51
+ if (!next.parallel)
52
+ this.exclusive = false;
53
+ this.pump();
54
+ });
55
+ }
56
+ }
57
+ }
58
+ //# sourceMappingURL=tool-call-scheduler.js.map
@@ -0,0 +1,5 @@
1
+ import { type ToolEffectClass, type ToolMetadata } from './types.js';
2
+ /** Resolve a tool's declared effect class. Missing catalog/MCP entries warn once and return `unknown`. */
3
+ export declare function resolveToolEffect(name: string, metadata?: Pick<ToolMetadata, 'effect'> | null): ToolEffectClass | 'unknown';
4
+ /** Test hook: unique-warning latch. */
5
+ export declare function resetToolEffectWarningLatch(): void;
@@ -0,0 +1,26 @@
1
+ import { logger } from '../utils/logger.js';
2
+ import { TOOL_METADATA } from './metadata.js';
3
+ import { TOOL_EFFECT_CLASSES } from './types.js';
4
+ // Runtime warnings live here so the tool catalog can also serve browser previews.
5
+ const missingEffectWarned = new Set();
6
+ function isToolEffectClass(value) {
7
+ return TOOL_EFFECT_CLASSES.includes(value);
8
+ }
9
+ /** Resolve a tool's declared effect class. Missing catalog/MCP entries warn once and return `unknown`. */
10
+ export function resolveToolEffect(name, metadata) {
11
+ if (isToolEffectClass(metadata?.effect))
12
+ return metadata.effect;
13
+ const catalog = TOOL_METADATA.find((entry) => entry.name === name)?.effect;
14
+ if (isToolEffectClass(catalog))
15
+ return catalog;
16
+ if (!missingEffectWarned.has(name)) {
17
+ missingEffectWarned.add(name);
18
+ logger.warn('tool metadata missing effect class; treating as unknown', { tool: name });
19
+ }
20
+ return 'unknown';
21
+ }
22
+ /** Test hook: unique-warning latch. */
23
+ export function resetToolEffectWarningLatch() {
24
+ missingEffectWarned.clear();
25
+ }
26
+ //# sourceMappingURL=tool-effect.js.map
@@ -9,21 +9,26 @@
9
9
  */
10
10
  import { BaseTool, ParameterDefinition } from './base-tool.js';
11
11
  import { ToolResult } from '../types/index.js';
12
+ import type { IToolExecutionContext } from './registry/types.js';
13
+ export interface SearchableTool {
14
+ name: string;
15
+ description: string;
16
+ keywords?: string[];
17
+ parameters?: unknown;
18
+ }
12
19
  /**
13
20
  * Build a BM25 index from tool definitions.
14
21
  */
15
22
  export declare class BM25Index {
16
23
  private documents;
17
24
  private avgDL;
25
+ private catalog;
26
+ getTool(name: string): SearchableTool | undefined;
18
27
  private idf;
19
28
  /**
20
29
  * Add tool definitions to the index.
21
30
  */
22
- index(tools: Array<{
23
- name: string;
24
- description: string;
25
- keywords?: string[];
26
- }>): void;
31
+ index(tools: SearchableTool[]): void;
27
32
  /**
28
33
  * Search for tools matching a query.
29
34
  * Returns results sorted by BM25 score (highest first).
@@ -37,11 +42,7 @@ export declare class BM25Index {
37
42
  /**
38
43
  * Initialize the BM25 index with available tools.
39
44
  */
40
- export declare function initToolSearchIndex(tools: Array<{
41
- name: string;
42
- description: string;
43
- keywords?: string[];
44
- }>): void;
45
+ export declare function initToolSearchIndex(tools: SearchableTool[]): void;
45
46
  /**
46
47
  * Get the BM25 index (creates empty if not initialized).
47
48
  */
@@ -50,5 +51,5 @@ export declare class ToolSearchTool extends BaseTool {
50
51
  readonly name = "tool_search";
51
52
  readonly description = "Search for available tools by keyword. Useful when you need to find a specific tool from a large set (especially MCP tools).";
52
53
  protected getParameters(): Record<string, ParameterDefinition>;
53
- execute(input: Record<string, unknown>): Promise<ToolResult>;
54
+ execute(input: Record<string, unknown>, context?: IToolExecutionContext): Promise<ToolResult>;
54
55
  }
@@ -8,10 +8,36 @@
8
8
  * Inspired by OpenAI Codex CLI's tool_search with BM25.
9
9
  */
10
10
  import { BaseTool } from './base-tool.js';
11
- import { resolveToolEffect } from './metadata.js';
11
+ import { resolveToolEffect } from './tool-effect.js';
12
12
  // ============================================================================
13
13
  // BM25 Implementation
14
14
  // ============================================================================
15
+ const QUERY_EQUIVALENTS = {
16
+ lire: ['read', 'view'],
17
+ voir: ['view', 'read'],
18
+ contenu: ['content', 'contents'],
19
+ contenus: ['content', 'contents'],
20
+ consulter: ['view', 'read'],
21
+ ouvrir: ['open', 'read', 'view'],
22
+ afficher: ['show', 'display', 'view', 'list'],
23
+ fichier: ['file'],
24
+ fichiers: ['file', 'files'],
25
+ dossier: ['directory', 'folder'],
26
+ dossiers: ['directory', 'directories', 'folder', 'folders'],
27
+ repertoire: ['directory', 'folder'],
28
+ repertoires: ['directory', 'directories', 'folder', 'folders'],
29
+ chercher: ['search', 'find'],
30
+ rechercher: ['search', 'find'],
31
+ trouver: ['find', 'search'],
32
+ modifier: ['edit', 'replace'],
33
+ remplacer: ['replace', 'edit'],
34
+ creer: ['create'],
35
+ supprimer: ['delete', 'remove'],
36
+ memoire: ['memory'],
37
+ executer: ['execute', 'run'],
38
+ lancer: ['run', 'execute'],
39
+ commande: ['command'],
40
+ };
15
41
  /** BM25 parameters */
16
42
  const K1 = 1.2; // Term frequency saturation
17
43
  const B = 0.75; // Length normalization
@@ -20,10 +46,11 @@ const B = 0.75; // Length normalization
20
46
  */
21
47
  function tokenize(text) {
22
48
  return text
49
+ .replace(/([a-z\d])([A-Z])/g, '$1 $2')
50
+ .normalize('NFKD').replace(/\p{M}/gu, '')
23
51
  .toLowerCase()
24
- .replace(/[^a-z0-9_-]/g, ' ')
25
- .split(/\s+/)
26
- .filter(t => t.length > 1);
52
+ .split(/[^\p{L}\p{N}]+/u)
53
+ .filter(Boolean);
27
54
  }
28
55
  /**
29
56
  * Build a BM25 index from tool definitions.
@@ -31,14 +58,18 @@ function tokenize(text) {
31
58
  export class BM25Index {
32
59
  documents = [];
33
60
  avgDL = 0;
61
+ catalog = new Map();
62
+ getTool(name) { return this.catalog.get(name); }
34
63
  idf = new Map();
35
64
  /**
36
65
  * Add tool definitions to the index.
37
66
  */
38
67
  index(tools) {
39
- this.documents = tools.map(t => {
68
+ this.idf.clear();
69
+ this.catalog = new Map(tools.map(tool => [tool.name, tool]));
70
+ this.documents = [...this.catalog.values()].map(t => {
40
71
  const tokens = [
41
- ...tokenize(t.name),
72
+ ...Array.from({ length: 3 }, () => tokenize(t.name)).flat(),
42
73
  ...tokenize(t.description),
43
74
  ...(t.keywords ?? []).flatMap(k => tokenize(k)),
44
75
  ];
@@ -59,14 +90,13 @@ export class BM25Index {
59
90
  this.avgDL = this.documents.reduce((sum, d) => sum + d.length, 0) / Math.max(this.documents.length, 1);
60
91
  // Compute IDF for all terms
61
92
  const N = this.documents.length;
62
- const allTerms = new Set();
93
+ const frequencies = new Map();
63
94
  for (const doc of this.documents) {
64
95
  for (const token of doc.tf.keys()) {
65
- allTerms.add(token);
96
+ frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
66
97
  }
67
98
  }
68
- for (const term of allTerms) {
69
- const df = this.documents.filter(d => d.tf.has(term)).length;
99
+ for (const [term, df] of frequencies) {
70
100
  this.idf.set(term, Math.log((N - df + 0.5) / (df + 0.5) + 1));
71
101
  }
72
102
  }
@@ -75,11 +105,12 @@ export class BM25Index {
75
105
  * Returns results sorted by BM25 score (highest first).
76
106
  */
77
107
  search(query, maxResults = 10) {
78
- const queryTokens = tokenize(query);
108
+ const queryTokens = [...new Set(tokenize(query).flatMap(term => [term, ...(QUERY_EQUIVALENTS[term] ?? [])]))];
109
+ const limit = Number.isFinite(maxResults) ? Math.max(1, Math.min(50, Math.floor(maxResults))) : 10;
79
110
  if (queryTokens.length === 0)
80
111
  return [];
81
112
  const scored = this.documents.map(doc => {
82
- let score = 0;
113
+ let score = doc.name.toLowerCase() === query.trim().toLowerCase() ? 100 : 0;
83
114
  for (const qToken of queryTokens) {
84
115
  const tf = doc.tf.get(qToken) ?? 0;
85
116
  const idf = this.idf.get(qToken) ?? 0;
@@ -88,12 +119,15 @@ export class BM25Index {
88
119
  const denominator = tf + K1 * (1 - B + B * (doc.length / this.avgDL));
89
120
  score += idf * (numerator / denominator);
90
121
  }
122
+ const nameTerms = new Set(tokenize(doc.name));
123
+ if (queryTokens.every(term => nameTerms.has(term)))
124
+ score += 5;
91
125
  return { name: doc.name, description: doc.description, score };
92
126
  });
93
127
  return scored
94
128
  .filter(s => s.score > 0)
95
- .sort((a, b) => b.score - a.score)
96
- .slice(0, maxResults);
129
+ .sort((a, b) => b.score - a.score || a.name.localeCompare(b.name))
130
+ .slice(0, limit);
97
131
  }
98
132
  }
99
133
  // ============================================================================
@@ -133,12 +167,17 @@ export class ToolSearchTool extends BaseTool {
133
167
  },
134
168
  };
135
169
  }
136
- async execute(input) {
170
+ async execute(input, context) {
137
171
  const query = input.query;
138
- if (!query)
139
- return this.error('query is required');
172
+ if (typeof query !== 'string' || !query.trim() || query.length > 2000)
173
+ return this.error('query must be a nonempty string of at most 2000 characters');
174
+ if (input.max_results !== undefined && (typeof input.max_results !== 'number' || !Number.isInteger(input.max_results) || input.max_results < 1 || input.max_results > 50))
175
+ return this.error('max_results must be an integer between 1 and 50');
140
176
  const maxResults = typeof input.max_results === 'number' ? input.max_results : 10;
141
- const index = getToolSearchIndex();
177
+ const catalog = context?.extra?.toolSearchCatalog;
178
+ const index = Array.isArray(catalog) ? new BM25Index() : getToolSearchIndex();
179
+ if (Array.isArray(catalog))
180
+ index.index(catalog);
142
181
  const results = index.search(query, maxResults);
143
182
  if (results.length === 0) {
144
183
  return this.success(`No tools found matching "${query}".`);
@@ -154,22 +193,24 @@ export class ToolSearchTool extends BaseTool {
154
193
  catch {
155
194
  // tools.js not available — skip deferred schema resolution
156
195
  }
157
- const lines = results.map((r, i) => {
196
+ const tools = results.map(result => ({
197
+ ...result,
198
+ effect: resolveToolEffect(result.name),
199
+ parameters: index.getTool(result.name)?.parameters ??
200
+ deferredSchemas?.get(result.name)?.function?.parameters,
201
+ }));
202
+ const lines = tools.map((r, i) => {
158
203
  const effect = resolveToolEffect(r.name);
159
204
  let line = `${i + 1}. **${r.name}** (score: ${r.score.toFixed(2)})\n effect: ${effect}\n ${r.description}`;
160
- // If deferred, include the full schema so the LLM can call it
161
- if (deferredSchemas?.has(r.name)) {
162
- const fullTool = deferredSchemas.get(r.name);
163
- if (fullTool?.function?.parameters) {
164
- line += `\n Schema: ${JSON.stringify(fullTool.function.parameters)}`;
165
- }
166
- }
205
+ if (r.parameters)
206
+ line += `\n Schema: ${JSON.stringify(r.parameters)}`;
167
207
  return line;
168
208
  });
169
209
  // data.names lets the executor EXPAND the current turn's tool selection
170
210
  // with what was discovered — finding a tool must make it invocable on the
171
211
  // next round (true progressive disclosure), not just describe it.
172
212
  return this.success(`Found ${results.length} tools:\n\n${lines.join('\n\n')}`, {
213
+ tools,
173
214
  names: results.map((r) => r.name),
174
215
  effects: Object.fromEntries(results.map((r) => [r.name, resolveToolEffect(r.name)])),
175
216
  });
@@ -49,6 +49,12 @@ export interface ToolMetadata {
49
49
  * silently filtered out from peer-visible tool lists.
50
50
  */
51
51
  fleetSafe?: boolean;
52
+ /**
53
+ * Polling/status tool that may legitimately be called repeatedly with the
54
+ * same arguments and result (e.g. waiting on a background job). The tool
55
+ * loop guard (`src/agent/execution/tool-loop-guard.ts`) never counts it.
56
+ */
57
+ repeatSafe?: boolean;
52
58
  }
53
59
  /**
54
60
  * Result of classifying a user query into tool categories.
@@ -6,5 +6,5 @@ interface ChatHistoryProps {
6
6
  /** Maximum number of messages to display (default: 50) */
7
7
  maxMessages?: number;
8
8
  }
9
- export declare function ChatHistory({ entries, isConfirmationActive, maxMessages, }: ChatHistoryProps): React.JSX.Element;
9
+ export declare const ChatHistory: React.NamedExoticComponent<ChatHistoryProps>;
10
10
  export {};
@@ -265,7 +265,7 @@ const MemoizedChatEntry = React.memo(({ entry, index, colors, avatars }) => {
265
265
  }
266
266
  });
267
267
  MemoizedChatEntry.displayName = "MemoizedChatEntry";
268
- export function ChatHistory({ entries, isConfirmationActive = false, maxMessages = 50, }) {
268
+ export const ChatHistory = React.memo(function ChatHistory({ entries, isConfirmationActive = false, maxMessages = 50, }) {
269
269
  const { colors, avatars } = useTheme();
270
270
  // Number of entries permanently committed to <Static> (monotonically increasing)
271
271
  // Entries start in the dynamic section and move to static AFTER being finalized
@@ -336,5 +336,5 @@ export function ChatHistory({ entries, isConfirmationActive = false, maxMessages
336
336
  " in scrollback"))),
337
337
  React.createElement(Static, { items: staticItems }, (entry) => (React.createElement(MemoizedChatEntry, { key: entry.uniqueKey, entry: entry, index: 0, colors: colors, avatars: avatars }))),
338
338
  dynamicItems.map((entry, index) => (React.createElement(MemoizedChatEntry, { key: entry.uniqueKey, entry: entry, index: index, colors: colors, avatars: avatars })))));
339
- }
339
+ });
340
340
  //# sourceMappingURL=ChatHistory.js.map
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React from 'react';
2
2
  interface ChatInputProps {
3
3
  input: string;
4
4
  cursorPosition: number;
@@ -6,5 +6,6 @@ interface ChatInputProps {
6
6
  isStreaming: boolean;
7
7
  mode?: string;
8
8
  }
9
+ /** A bounded draft viewport. Editing remains available while the agent works. */
9
10
  export declare const ChatInput: React.NamedExoticComponent<ChatInputProps>;
10
11
  export {};
@@ -1,93 +1,47 @@
1
- import React, { useMemo } from "react";
2
- import { Box, Text } from "ink";
3
- import { useTheme } from "../context/theme-context.js";
4
- // Memoized ChatInput to reduce re-renders and prevent flickering
1
+ import React, { useMemo } from 'react';
2
+ import { Box, Text } from 'ink';
3
+ import { useTheme } from '../context/theme-context.js';
4
+ /** A bounded draft viewport. Editing remains available while the agent works. */
5
5
  export const ChatInput = React.memo(function ChatInput({ input, cursorPosition, isProcessing, isStreaming, mode = 'code', }) {
6
6
  const { colors } = useTheme();
7
- // Mode icons and colors for prompt
8
- const modePromptIcons = {
9
- plan: '📋',
10
- code: '',
11
- ask: '',
12
- };
13
- const modePromptColors = {
14
- plan: colors.info,
15
- code: colors.primary,
16
- ask: colors.secondary,
17
- };
18
- const promptIcon = modePromptIcons[mode] || '❯';
19
- const promptColor = modePromptColors[mode] || colors.primary;
20
- // Memoize cursor calculations to avoid recomputing on every render
21
- const cursorData = useMemo(() => {
22
- const lines = input.split("\n");
23
- const isMultiline = lines.length > 1;
24
- const beforeCursor = input.slice(0, cursorPosition);
25
- // Calculate cursor position across lines
26
- let currentLineIndex = 0;
27
- let currentCharIndex = 0;
28
- let totalChars = 0;
29
- for (let i = 0; i < lines.length; i++) {
30
- const line = lines[i] ?? "";
31
- if (totalChars + line.length >= cursorPosition) {
32
- currentLineIndex = i;
33
- currentCharIndex = cursorPosition - totalChars;
34
- break;
35
- }
36
- totalChars += line.length + 1; // +1 for newline
37
- }
38
- return { lines, isMultiline, beforeCursor, currentLineIndex, currentCharIndex };
7
+ const busy = isProcessing || isStreaming;
8
+ const viewport = useMemo(() => {
9
+ const position = Math.max(0, Math.min(input.length, cursorPosition));
10
+ const lines = input.split('\n');
11
+ const before = input.slice(0, position).split('\n');
12
+ const row = before.length - 1;
13
+ const column = before[row]?.length ?? 0;
14
+ const start = Math.max(0, Math.min(row - 3, lines.length - 6));
15
+ return { lines, row, column, start, visible: lines.slice(start, start + 6) };
39
16
  }, [input, cursorPosition]);
40
- const { lines, isMultiline, beforeCursor, currentLineIndex, currentCharIndex } = cursorData;
41
- const showCursor = !isProcessing && !isStreaming;
42
- const borderColor = isProcessing || isStreaming ? colors.borderBusy : colors.borderActive;
43
- // Display placeholder when input is empty
44
- const placeholderText = mode === 'plan'
45
- ? "Describe the task you want to plan..."
46
- : mode === 'ask'
47
- ? "Ask a question..."
48
- : "Ask me anything...";
49
- const isPlaceholder = !input;
50
- if (isMultiline) {
51
- return (React.createElement(Box, { borderStyle: "round", borderColor: borderColor, paddingY: 0, marginTop: 1 }, lines.map((line, index) => {
52
- const isCurrentLine = index === currentLineIndex;
53
- const promptChar = index === 0 ? promptIcon : "│";
54
- if (isCurrentLine) {
55
- const beforeCursorInLine = line.slice(0, currentCharIndex);
56
- const cursorChar = line.slice(currentCharIndex, currentCharIndex + 1) || " ";
57
- const afterCursorInLine = line.slice(currentCharIndex + 1);
58
- return (React.createElement(Box, { key: index },
59
- React.createElement(Text, { color: promptColor },
60
- promptChar,
61
- " "),
62
- React.createElement(Text, null,
63
- beforeCursorInLine,
64
- showCursor && (React.createElement(Text, { backgroundColor: "white", color: "black" }, cursorChar)),
65
- !showCursor && cursorChar !== " " && cursorChar,
66
- afterCursorInLine)));
67
- }
68
- else {
69
- return (React.createElement(Box, { key: index },
70
- React.createElement(Text, { color: promptColor },
71
- promptChar,
72
- " "),
73
- React.createElement(Text, null, line)));
74
- }
75
- })));
76
- }
77
- // Single line input box
78
- const cursorChar = input.slice(cursorPosition, cursorPosition + 1) || " ";
79
- const afterCursorText = input.slice(cursorPosition + 1);
80
- return (React.createElement(Box, { borderStyle: "round", borderColor: borderColor, paddingX: 1, paddingY: 0, marginTop: 1 },
81
- React.createElement(Box, null,
82
- React.createElement(Text, { color: promptColor },
83
- promptIcon,
84
- " "),
85
- isPlaceholder ? (React.createElement(React.Fragment, null,
86
- React.createElement(Text, { color: colors.textMuted, dimColor: true }, placeholderText),
87
- showCursor && (React.createElement(Text, { backgroundColor: "white", color: "black" }, " ")))) : (React.createElement(Text, null,
88
- beforeCursor,
89
- showCursor && (React.createElement(Text, { backgroundColor: "white", color: "black" }, cursorChar)),
90
- !showCursor && cursorChar !== " " && cursorChar,
91
- afterCursorText)))));
17
+ const placeholder = mode === 'plan' ? 'Describe what you want to plan…'
18
+ : mode === 'ask' ? 'Ask a question…'
19
+ : busy ? 'Write a follow-up while I work…' : 'Describe a task, or type / for commands…';
20
+ return (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: busy ? colors.borderBusy : colors.borderActive, paddingX: 1, marginTop: 1 },
21
+ React.createElement(Box, { justifyContent: "space-between" },
22
+ React.createElement(Text, { bold: true, color: colors.primary }, mode === 'plan' ? 'Plan' : mode === 'ask' ? 'Question' : 'Message'),
23
+ React.createElement(Text, { color: colors.textMuted },
24
+ busy ? 'Draft · working' : 'Ready',
25
+ viewport.lines.length > 1 ? ` · ${viewport.lines.length} lines` : '')),
26
+ viewport.start > 0 && React.createElement(Text, { dimColor: true },
27
+ "\u2026 ",
28
+ viewport.start,
29
+ " lines above"),
30
+ viewport.visible.map((line, index) => {
31
+ const active = viewport.start + index === viewport.row;
32
+ const char = Array.from(line.slice(viewport.column))[0] || ' ';
33
+ return (React.createElement(Box, { key: viewport.start + index },
34
+ React.createElement(Text, { color: colors.primary }, active ? '❯ ' : ' '),
35
+ !input ? (React.createElement(Text, null,
36
+ React.createElement(Text, { inverse: true }, " "),
37
+ React.createElement(Text, { dimColor: true }, placeholder))) : active ? (React.createElement(Text, null,
38
+ line.slice(0, viewport.column),
39
+ React.createElement(Text, { inverse: true }, char),
40
+ line.slice(viewport.column + char.length))) : React.createElement(Text, null, line || ' ')));
41
+ }),
42
+ viewport.start + 6 < viewport.lines.length && React.createElement(Text, { dimColor: true },
43
+ "\u2026 ",
44
+ viewport.lines.length - viewport.start - 6,
45
+ " lines below")));
92
46
  });
93
47
  //# sourceMappingURL=ChatInput.js.map
@@ -1,8 +1,10 @@
1
1
  import React from "react";
2
- import type { CodeBuddyAgent } from "../../agent/codebuddy-agent.js";
2
+ import type { CodeBuddyAgent, ChatEntry } from "../../agent/codebuddy-agent.js";
3
3
  interface ChatInterfaceProps {
4
4
  agent?: CodeBuddyAgent;
5
5
  initialMessage?: string;
6
+ /** History of a resumed session, shown before the first new turn. */
7
+ initialHistory?: ChatEntry[];
6
8
  }
7
- export default function ChatInterface({ agent, initialMessage, }: ChatInterfaceProps): React.JSX.Element;
9
+ export default function ChatInterface({ agent, initialMessage, initialHistory, }: ChatInterfaceProps): React.JSX.Element;
8
10
  export {};