@phuetz/code-buddy 1.1.0 → 1.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 (291) hide show
  1. package/README.md +160 -165
  2. package/dist/agent/autonomous/agentic-coding-contract.d.ts +6 -6
  3. package/dist/agent/codebuddy-agent.d.ts +1 -1
  4. package/dist/agent/codebuddy-agent.js +69 -14
  5. package/dist/agent/execution/agent-executor.js +122 -12
  6. package/dist/agent/execution/tool-dependency-graph.js +7 -0
  7. package/dist/agent/execution/tool-hooks.d.ts +6 -0
  8. package/dist/agent/execution/tool-hooks.js +13 -5
  9. package/dist/agent/execution/tool-selection-strategy.d.ts +11 -2
  10. package/dist/agent/execution/tool-selection-strategy.js +67 -10
  11. package/dist/agent/extended-thinking.d.ts +8 -0
  12. package/dist/agent/extended-thinking.js +27 -0
  13. package/dist/agent/hermes-browser-backends.js +151 -11
  14. package/dist/agent/hermes-claw-migrate.d.ts +59 -0
  15. package/dist/agent/hermes-claw-migrate.js +427 -26
  16. package/dist/agent/hermes-parity-manifest.js +78 -71
  17. package/dist/agent/hermes-runtime-lifecycle.d.ts +67 -0
  18. package/dist/agent/hermes-runtime-lifecycle.js +468 -0
  19. package/dist/agent/middleware/index.d.ts +1 -0
  20. package/dist/agent/middleware/index.js +1 -0
  21. package/dist/agent/middleware/visual-validation-middleware.d.ts +17 -0
  22. package/dist/agent/middleware/visual-validation-middleware.js +67 -0
  23. package/dist/agent/model-benchmark.d.ts +77 -0
  24. package/dist/agent/model-benchmark.js +309 -0
  25. package/dist/agent/model-tier.d.ts +14 -0
  26. package/dist/agent/model-tier.js +71 -0
  27. package/dist/agent/multi-agent/agents/orchestrator-agent.js +17 -6
  28. package/dist/agent/multi-agent/multi-agent-system.js +3 -1
  29. package/dist/agent/multi-agent/session-fleet-bridge.d.ts +2 -0
  30. package/dist/agent/multi-agent/session-fleet-bridge.js +9 -1
  31. package/dist/agent/multi-agent/session-tools.js +22 -5
  32. package/dist/agent/reasoning/mcts.js +20 -6
  33. package/dist/agent/session-end-flush.d.ts +6 -1
  34. package/dist/agent/session-end-flush.js +20 -4
  35. package/dist/agent/streaming/streaming-handler.d.ts +7 -0
  36. package/dist/agent/streaming/streaming-handler.js +8 -1
  37. package/dist/agent/tool-executor.js +1 -0
  38. package/dist/agent/tool-handler.d.ts +5 -0
  39. package/dist/agent/tool-handler.js +130 -4
  40. package/dist/browser-automation/browser-operator-executor.js +15 -0
  41. package/dist/browser-automation/browser-use-runner.d.ts +96 -0
  42. package/dist/browser-automation/browser-use-runner.js +492 -0
  43. package/dist/browser-automation/camofox-runner.d.ts +107 -0
  44. package/dist/browser-automation/camofox-runner.js +287 -0
  45. package/dist/channels/dingtalk/index.js +3 -0
  46. package/dist/channels/discord/client.d.ts +9 -0
  47. package/dist/channels/discord/client.js +12 -0
  48. package/dist/channels/feishu/index.d.ts +167 -1
  49. package/dist/channels/feishu/index.js +432 -7
  50. package/dist/channels/gateway-lifecycle.d.ts +132 -0
  51. package/dist/channels/gateway-lifecycle.js +219 -0
  52. package/dist/channels/google-chat/index.d.ts +5 -1
  53. package/dist/channels/google-chat/index.js +5 -1
  54. package/dist/channels/imessage/index.d.ts +14 -0
  55. package/dist/channels/imessage/index.js +64 -17
  56. package/dist/channels/index.d.ts +4 -0
  57. package/dist/channels/index.js +4 -0
  58. package/dist/channels/irc/index.d.ts +73 -3
  59. package/dist/channels/irc/index.js +446 -11
  60. package/dist/channels/line/index.js +3 -0
  61. package/dist/channels/mattermost/index.d.ts +51 -4
  62. package/dist/channels/mattermost/index.js +303 -20
  63. package/dist/channels/nextcloud-talk/index.d.ts +89 -8
  64. package/dist/channels/nextcloud-talk/index.js +367 -16
  65. package/dist/channels/nostr/index.d.ts +121 -1
  66. package/dist/channels/nostr/index.js +396 -8
  67. package/dist/channels/ntfy/index.js +3 -0
  68. package/dist/channels/qq/index.js +3 -0
  69. package/dist/channels/slash-parity.d.ts +117 -0
  70. package/dist/channels/slash-parity.js +185 -0
  71. package/dist/channels/synology-chat/index.js +3 -0
  72. package/dist/channels/teams/index.d.ts +3 -0
  73. package/dist/channels/teams/index.js +3 -0
  74. package/dist/channels/telegram/client.d.ts +9 -0
  75. package/dist/channels/telegram/client.js +12 -0
  76. package/dist/channels/twilio-voice/index.js +3 -0
  77. package/dist/channels/webchat/index.d.ts +7 -1
  78. package/dist/channels/webchat/index.js +7 -1
  79. package/dist/channels/wecom/index.js +3 -0
  80. package/dist/channels/weixin/index.js +3 -0
  81. package/dist/channels/zalo/index.js +3 -0
  82. package/dist/cli/config-loader.js +1 -1
  83. package/dist/codebuddy/client.d.ts +30 -1
  84. package/dist/codebuddy/client.js +167 -20
  85. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +5 -0
  86. package/dist/codebuddy/providers/provider-chatgpt-responses.js +1 -0
  87. package/dist/codebuddy/providers/provider-gemini-native.js +1 -1
  88. package/dist/codebuddy/providers/provider-openai-compat.d.ts +5 -0
  89. package/dist/codebuddy/providers/provider-openai-compat.js +82 -2
  90. package/dist/codebuddy/tool-definitions/computer-control-tools.js +32 -2
  91. package/dist/codebuddy/tool-definitions/core-tools.js +3 -3
  92. package/dist/codebuddy/tool-definitions/index.d.ts +2 -1
  93. package/dist/codebuddy/tool-definitions/index.js +2 -1
  94. package/dist/codebuddy/tool-definitions/multimodal-tools.d.ts +1 -0
  95. package/dist/codebuddy/tool-definitions/multimodal-tools.js +44 -1
  96. package/dist/codebuddy/tool-definitions/windows-tools.d.ts +2 -0
  97. package/dist/codebuddy/tool-definitions/windows-tools.js +34 -0
  98. package/dist/codebuddy/tools.d.ts +1 -1
  99. package/dist/codebuddy/tools.js +27 -24
  100. package/dist/commands/cli/hermes-commands.js +108 -0
  101. package/dist/commands/cli/native-engine-commands.js +102 -3
  102. package/dist/commands/cli/utility-commands.js +45 -0
  103. package/dist/commands/enhanced-command-handler.js +1 -1
  104. package/dist/commands/goal-cli.d.ts +30 -0
  105. package/dist/commands/goal-cli.js +210 -27
  106. package/dist/commands/handlers/goal-handler.d.ts +9 -2
  107. package/dist/commands/handlers/goal-handler.js +36 -9
  108. package/dist/commands/handlers/infra-handlers.js +36 -0
  109. package/dist/commands/handlers/memory-handlers.js +96 -1
  110. package/dist/commands/headless-slash.d.ts +2 -0
  111. package/dist/commands/headless-slash.js +12 -0
  112. package/dist/commands/llm-provider-resolution.d.ts +5 -3
  113. package/dist/commands/llm-provider-resolution.js +87 -33
  114. package/dist/commands/ollama.d.ts +25 -0
  115. package/dist/commands/ollama.js +100 -0
  116. package/dist/commands/provider.d.ts +5 -0
  117. package/dist/commands/provider.js +106 -55
  118. package/dist/commands/spec-next.js +2 -1
  119. package/dist/commands/spec-plan.js +5 -16
  120. package/dist/commands/tunnel.d.ts +2 -0
  121. package/dist/commands/tunnel.js +48 -0
  122. package/dist/config/config-resolver.d.ts +2 -1
  123. package/dist/config/config-resolver.js +54 -41
  124. package/dist/config/constants.d.ts +28 -0
  125. package/dist/config/constants.js +7 -0
  126. package/dist/config/env-schema.js +756 -0
  127. package/dist/config/model-tools.js +4 -4
  128. package/dist/config/toml-config.d.ts +16 -0
  129. package/dist/config/toml-config.js +3 -0
  130. package/dist/context/context-manager-v2.js +1 -0
  131. package/dist/daemon/autonomous-daemon.d.ts +1 -1
  132. package/dist/daemon/autonomous-daemon.js +3 -3
  133. package/dist/daemon/autonomous-loop.js +11 -2
  134. package/dist/daemon/colab-goal.js +9 -1
  135. package/dist/daemon/cron-agent-bridge.d.ts +12 -3
  136. package/dist/daemon/cron-agent-bridge.js +25 -9
  137. package/dist/desktop/codebuddy-engine-adapter.d.ts +32 -0
  138. package/dist/desktop/codebuddy-engine-adapter.js +257 -80
  139. package/dist/desktop/engine-adapter.d.ts +14 -0
  140. package/dist/desktop-automation/automation-manager.js +16 -0
  141. package/dist/desktop-automation/omniparser-runner.d.ts +63 -0
  142. package/dist/desktop-automation/omniparser-runner.js +115 -0
  143. package/dist/desktop-automation/smart-snapshot.d.ts +11 -0
  144. package/dist/desktop-automation/smart-snapshot.js +72 -5
  145. package/dist/doctor/index.js +40 -23
  146. package/dist/events/types.d.ts +11 -0
  147. package/dist/fleet/colab-store.js +14 -2
  148. package/dist/fleet/peer-session-bridge.js +50 -12
  149. package/dist/fleet/peer-tool-bridge.js +14 -0
  150. package/dist/goals/goal-decomposer.d.ts +27 -0
  151. package/dist/goals/goal-decomposer.js +293 -0
  152. package/dist/goals/goal-judge-client.d.ts +15 -0
  153. package/dist/goals/goal-judge-client.js +45 -0
  154. package/dist/goals/goal-judge.d.ts +2 -0
  155. package/dist/goals/goal-judge.js +35 -6
  156. package/dist/goals/goal-loop.d.ts +14 -0
  157. package/dist/goals/goal-loop.js +47 -2
  158. package/dist/goals/goal-manager.d.ts +6 -0
  159. package/dist/goals/goal-manager.js +67 -14
  160. package/dist/goals/goal-state.d.ts +13 -0
  161. package/dist/goals/goal-state.js +83 -13
  162. package/dist/goals/goal-store.d.ts +3 -0
  163. package/dist/goals/goal-store.js +31 -15
  164. package/dist/hooks/user-hooks.js +17 -3
  165. package/dist/index.js +464 -25
  166. package/dist/input/text-to-speech.d.ts +2 -6
  167. package/dist/input/text-to-speech.js +2 -27
  168. package/dist/integrations/screenpipe/screenpipe-client.d.ts +10 -0
  169. package/dist/integrations/screenpipe/screenpipe-client.js +12 -2
  170. package/dist/integrations/tailscale.d.ts +13 -0
  171. package/dist/integrations/tailscale.js +87 -34
  172. package/dist/knowledge/workspace-indexer.js +53 -9
  173. package/dist/mcp/client.js +1 -0
  174. package/dist/mcp/mcp-desktop-tools.d.ts +23 -0
  175. package/dist/mcp/mcp-desktop-tools.js +158 -0
  176. package/dist/mcp/mcp-server.js +2 -0
  177. package/dist/memory/enhanced-memory.d.ts +10 -0
  178. package/dist/memory/enhanced-memory.js +33 -2
  179. package/dist/memory/index.d.ts +3 -1
  180. package/dist/memory/index.js +4 -1
  181. package/dist/memory/memory-auto-proposer.d.ts +23 -0
  182. package/dist/memory/memory-auto-proposer.js +308 -0
  183. package/dist/memory/memory-candidate-queue.d.ts +92 -0
  184. package/dist/memory/memory-candidate-queue.js +261 -0
  185. package/dist/memory/persistent-memory.d.ts +45 -2
  186. package/dist/memory/persistent-memory.js +236 -40
  187. package/dist/ml/bayesian-qualifier.d.ts +1 -1
  188. package/dist/ml/bayesian-qualifier.js +35 -2
  189. package/dist/openclaw/gateway-bridge.js +5 -0
  190. package/dist/plugins/marketplace.d.ts +1 -0
  191. package/dist/plugins/marketplace.js +7 -0
  192. package/dist/prompts/execution-discipline.d.ts +14 -0
  193. package/dist/prompts/execution-discipline.js +29 -0
  194. package/dist/prompts/variation-injector.js +9 -2
  195. package/dist/protocols/acp/acp-agentic-runner.d.ts +7 -6
  196. package/dist/protocols/acp/acp-agentic-runner.js +115 -18
  197. package/dist/protocols/acp/acp-session-store.d.ts +23 -0
  198. package/dist/protocols/acp/acp-session-store.js +77 -0
  199. package/dist/protocols/acp/acp-stdio-server.d.ts +7 -0
  200. package/dist/protocols/acp/acp-stdio-server.js +71 -16
  201. package/dist/providers/active-llm-registry.d.ts +37 -0
  202. package/dist/providers/active-llm-registry.js +186 -0
  203. package/dist/providers/auxiliary-provider.d.ts +25 -0
  204. package/dist/providers/auxiliary-provider.js +192 -0
  205. package/dist/providers/codex-oauth.d.ts +1 -1
  206. package/dist/providers/codex-oauth.js +27 -4
  207. package/dist/providers/index.d.ts +3 -0
  208. package/dist/providers/index.js +4 -0
  209. package/dist/providers/provider-catalog.d.ts +70 -0
  210. package/dist/providers/provider-catalog.js +738 -0
  211. package/dist/providers/provider-fallback.d.ts +35 -0
  212. package/dist/providers/provider-fallback.js +171 -0
  213. package/dist/providers/xai-oauth.d.ts +128 -0
  214. package/dist/providers/xai-oauth.js +735 -0
  215. package/dist/scheduler/cron-scheduler.d.ts +15 -3
  216. package/dist/scheduler/cron-scheduler.js +53 -7
  217. package/dist/scheduler/script-runner.d.ts +2 -0
  218. package/dist/scheduler/script-runner.js +19 -4
  219. package/dist/scheduler/watchdog-handlers.js +7 -20
  220. package/dist/search/usearch-index.js +7 -2
  221. package/dist/security/tool-policy/tool-groups.js +2 -0
  222. package/dist/server/index.js +17 -2
  223. package/dist/server/routes/mobile.d.ts +12 -4
  224. package/dist/server/routes/mobile.js +116 -1
  225. package/dist/server/tls-config.d.ts +35 -0
  226. package/dist/server/tls-config.js +142 -0
  227. package/dist/server/tunnel-manager.d.ts +20 -0
  228. package/dist/server/tunnel-manager.js +58 -0
  229. package/dist/server/websocket/fleet-bridge.js +5 -0
  230. package/dist/services/prompt-builder.d.ts +1 -0
  231. package/dist/services/prompt-builder.js +49 -8
  232. package/dist/shared/engine-types.d.ts +15 -1
  233. package/dist/sidecar/sidecar-bridge.d.ts +1 -0
  234. package/dist/sidecar/sidecar-bridge.js +21 -10
  235. package/dist/spec/spec-store.js +8 -1
  236. package/dist/talk-mode/providers/audioreader-tts.d.ts +1 -1
  237. package/dist/talk-mode/providers/audioreader-tts.js +8 -8
  238. package/dist/templates/project-scaffolding.js +3 -2
  239. package/dist/themes/theme-schema.d.ts +10 -10
  240. package/dist/tools/application-profiles.js +38 -0
  241. package/dist/tools/bash/bash-tool.d.ts +1 -0
  242. package/dist/tools/bash/bash-tool.js +16 -2
  243. package/dist/tools/bash/command-validator.js +3 -0
  244. package/dist/tools/bash/rtk-rewrite.d.ts +16 -0
  245. package/dist/tools/bash/rtk-rewrite.js +100 -0
  246. package/dist/tools/bash/streaming-executor.js +14 -1
  247. package/dist/tools/computer-control-tool.d.ts +16 -1
  248. package/dist/tools/computer-control-tool.js +317 -5
  249. package/dist/tools/document-generator.d.ts +14 -0
  250. package/dist/tools/document-generator.js +79 -0
  251. package/dist/tools/execute-code-rpc-invoker.d.ts +26 -2
  252. package/dist/tools/execute-code-rpc-invoker.js +46 -5
  253. package/dist/tools/metadata.js +28 -0
  254. package/dist/tools/office-macro-tool.d.ts +10 -0
  255. package/dist/tools/office-macro-tool.js +93 -0
  256. package/dist/tools/registry/index.d.ts +2 -1
  257. package/dist/tools/registry/index.js +2 -1
  258. package/dist/tools/registry/memory-tools.d.ts +20 -0
  259. package/dist/tools/registry/memory-tools.js +225 -3
  260. package/dist/tools/registry/multimodal-tools.js +2 -2
  261. package/dist/tools/registry/vision-tools.d.ts +33 -0
  262. package/dist/tools/registry/vision-tools.js +308 -6
  263. package/dist/tools/registry/windows-tools.d.ts +2 -0
  264. package/dist/tools/registry/windows-tools.js +47 -0
  265. package/dist/tools/route-peer-tool.js +14 -0
  266. package/dist/tools/screenshot-tool.js +14 -2
  267. package/dist/tools/text-to-speech-tool.d.ts +1 -1
  268. package/dist/tools/text-to-speech-tool.js +2 -12
  269. package/dist/tools/tool-selector.js +3 -59
  270. package/dist/utils/config-validation/schema.d.ts +40 -7
  271. package/dist/utils/config-validation/schema.js +130 -1
  272. package/dist/utils/cost-tracker.js +21 -1
  273. package/dist/utils/disk-guard.d.ts +180 -0
  274. package/dist/utils/disk-guard.js +404 -0
  275. package/dist/utils/interactive-setup.js +35 -2
  276. package/dist/utils/model-utils.js +11 -2
  277. package/dist/utils/output-sanitizer.js +11 -0
  278. package/dist/utils/provider-detector.d.ts +8 -7
  279. package/dist/utils/provider-detector.js +19 -77
  280. package/dist/utils/settings-manager.d.ts +7 -0
  281. package/dist/utils/token-display.d.ts +3 -3
  282. package/dist/utils/token-display.js +22 -4
  283. package/dist/wizard/onboarding.d.ts +39 -0
  284. package/dist/wizard/onboarding.js +433 -21
  285. package/dist/wizard/provider-onboarding.d.ts +8 -23
  286. package/dist/wizard/provider-onboarding.js +39 -158
  287. package/package.json +4 -2
  288. package/dist/plugins/provider-onboarding.d.ts +0 -23
  289. package/dist/plugins/provider-onboarding.js +0 -116
  290. package/dist/utils/kokoro-tts.d.ts +0 -8
  291. package/dist/utils/kokoro-tts.js +0 -67
@@ -9,7 +9,10 @@
9
9
  * 2. reusable lesson candidates via the existing review-gated
10
10
  * auto-proposer (PENDING only — a human approves them into lessons.md,
11
11
  * which the per-turn `<lessons_context>` injection then re-injects on
12
- * future runs).
12
+ * future runs);
13
+ * 3. declarative long-term memory candidates via the review-gated memory
14
+ * queue. These do not enter prompt-injected persistent memory until a
15
+ * human accepts them.
13
16
  *
14
17
  * Guard-rails: trivial sessions are skipped (no LLM call, no file), and
15
18
  * everything written here passes the privacy lint first (PII/secret spans
@@ -145,7 +148,7 @@ export function writeHandoffSync(history, workDir = process.cwd(), options = {})
145
148
  }
146
149
  lines.push('', '## Reprise', '', risks.length > 0
147
150
  ? '- Commencer par lever les risques ouverts ci-dessus.'
148
- : '- Continuer le dernier objectif, ou `buddy --continue` pour recharger la session.', '- Leçons en attente de revue : `buddy lessons` (candidats proposés en fin de session).', '');
151
+ : '- Continuer le dernier objectif, ou `buddy --continue` pour recharger la session.', '- Leçons en attente de revue : `buddy lessons` (candidats proposés en fin de session).', '- Mémoires en attente de revue : `/memory candidates` puis `/memory accept <id> <reviewer>`.', '');
149
152
  const content = redactSecrets(lines.join('\n'));
150
153
  const dir = path.join(workDir, '.codebuddy');
151
154
  const target = path.join(dir, 'HANDOFF.md');
@@ -168,7 +171,7 @@ export function writeHandoffSync(history, workDir = process.cwd(), options = {})
168
171
  * no-ops on trivial sessions, never throws.
169
172
  */
170
173
  export async function runSessionEndFlush(input) {
171
- const empty = { proposedLessons: 0, openRisks: [] };
174
+ const empty = { proposedLessons: 0, proposedMemories: 0, openRisks: [] };
172
175
  try {
173
176
  if (!isFeatureEnabled('SESSION_END_FLUSH')) {
174
177
  return { ...empty, skipped: 'disabled' };
@@ -195,12 +198,25 @@ export async function runSessionEndFlush(input) {
195
198
  catch (err) {
196
199
  logger.debug('[session-end-flush] lesson proposal failed', { err: String(err) });
197
200
  }
198
- if (proposedLessons > 0 || handoffPath) {
201
+ let proposedMemories = 0;
202
+ if (process.env.CODEBUDDY_MEMORY_AUTO_PROPOSE !== 'false') {
203
+ try {
204
+ const { proposeMemoryCandidatesFromSession } = await import('../memory/memory-auto-proposer.js');
205
+ const proposed = await proposeMemoryCandidatesFromSession(history, workDir, input.client, input.sessionId);
206
+ proposedMemories = proposed.length;
207
+ }
208
+ catch (err) {
209
+ logger.debug('[session-end-flush] memory proposal failed', { err: String(err) });
210
+ }
211
+ }
212
+ if (proposedLessons > 0 || proposedMemories > 0 || handoffPath) {
199
213
  logger.info(`[session-end-flush] ${proposedLessons} lesson candidate(s) proposed` +
214
+ `, ${proposedMemories} memory candidate(s) proposed` +
200
215
  (handoffPath ? `, handoff written to ${path.relative(workDir, handoffPath)}` : ''));
201
216
  }
202
217
  return {
203
218
  proposedLessons,
219
+ proposedMemories,
204
220
  ...(handoffPath ? { handoffPath } : {}),
205
221
  openRisks,
206
222
  };
@@ -118,6 +118,12 @@ export interface AccumulatedMessage {
118
118
  content?: string;
119
119
  /** Accumulated tool calls */
120
120
  tool_calls?: CodeBuddyToolCall[];
121
+ /**
122
+ * The terminal `finish_reason`/`stop_reason` reported by the provider on the
123
+ * final chunk (`'stop'` | `'length'` | `'tool_calls'` | …). Used by the
124
+ * executor's in-loop recovery to detect a length-truncated turn.
125
+ */
126
+ finishReason?: string | null;
121
127
  /** Any additional fields from the API */
122
128
  [key: string]: unknown;
123
129
  }
@@ -153,6 +159,7 @@ export declare class StreamingHandler {
153
159
  private accumulatedMessage;
154
160
  private accumulatedRawContent;
155
161
  private accumulatedReasoningContent;
162
+ private accumulatedFinishReason;
156
163
  private tokenCounter;
157
164
  private lastTokenUpdate;
158
165
  private toolCallsYielded;
@@ -42,6 +42,7 @@ export class StreamingHandler {
42
42
  accumulatedMessage = {};
43
43
  accumulatedRawContent = '';
44
44
  accumulatedReasoningContent = '';
45
+ accumulatedFinishReason = null;
45
46
  tokenCounter = null;
46
47
  lastTokenUpdate = 0;
47
48
  toolCallsYielded = false;
@@ -86,6 +87,11 @@ export class StreamingHandler {
86
87
  }
87
88
  // Accumulate the message using reducer
88
89
  this.accumulatedMessage = reduceStreamChunk(this.accumulatedMessage, chunk);
90
+ // Capture the terminal finish_reason (present on the final chunk). The last
91
+ // non-null value wins so a trailing usage-only chunk can't clear it.
92
+ if (chunk.choices[0].finish_reason) {
93
+ this.accumulatedFinishReason = chunk.choices[0].finish_reason;
94
+ }
89
95
  // Extract content delta
90
96
  const rawContentDelta = chunk.choices[0].delta?.content || '';
91
97
  this.accumulatedRawContent += rawContentDelta;
@@ -208,7 +214,7 @@ export class StreamingHandler {
208
214
  * @returns The accumulated message with all processed content and tool calls
209
215
  */
210
216
  getAccumulatedMessage() {
211
- return { ...this.accumulatedMessage };
217
+ return { ...this.accumulatedMessage, finishReason: this.accumulatedFinishReason };
212
218
  }
213
219
  /**
214
220
  * Gets the raw accumulated content before sanitization.
@@ -261,6 +267,7 @@ export class StreamingHandler {
261
267
  this.accumulatedMessage = {};
262
268
  this.accumulatedRawContent = '';
263
269
  this.accumulatedReasoningContent = '';
270
+ this.accumulatedFinishReason = null;
264
271
  this.lastTokenUpdate = 0;
265
272
  this.toolCallsYielded = false;
266
273
  }
@@ -97,6 +97,7 @@ const TOOL_CATEGORY_MAP = {
97
97
  // Media
98
98
  screenshot: 'media',
99
99
  camera_snapshot: 'media',
100
+ camera_analyze: 'media',
100
101
  audio: 'media',
101
102
  video: 'media',
102
103
  ocr: 'media',
@@ -37,6 +37,11 @@ export interface ToolHandlerDependencies {
37
37
  /** Coordinates auto-repair attempts for failed commands */
38
38
  repairCoordinator: RepairCoordinator;
39
39
  }
40
+ export interface NormalizedHallucinatedToolCall {
41
+ toolName: string;
42
+ args: Record<string, unknown>;
43
+ }
44
+ export declare function normalizeHallucinatedLocalToolCall(toolName: string, args: Record<string, unknown>): NormalizedHallucinatedToolCall | null;
40
45
  /**
41
46
  * ToolHandler manages tool instantiation and execution
42
47
  *
@@ -16,7 +16,7 @@
16
16
  * Tool instances are lazy-loaded on first access for optimal startup time.
17
17
  */
18
18
  import { TextEditorTool, MorphEditorTool, ImageTool, BashTool, } from "../tools/index.js";
19
- import { getFormalToolRegistry, createTextEditorTools, createBashTools, createLsTools, createSearchTools, createWebTools, createTodoTools, createCronjobTools, createDockerTools, createKubernetesTools, createGitTools, createMiscTools, createBrowserTools, createProcessTools, createVisionTools, createScriptTools, createPlanTools, createKnowledgeTools, createRelationshipIntelligenceTools, createInternetScoutTools, createLeadScoutTools, createBrowserOperatorTools, createMemoryTools, createParallelTools, createAttentionTools, createSkillsInspectionTools, createLessonsTools, createUserModelTools, createAliasTools, createMultimodalTools, createAdvancedTools, createCanvasTools, createLspTools, createMergeConflictTools, createVulnScannerTools, createCodebaseReplaceTools, createAdvisorTools, createFleetTools, createAskUserQuestionTools, createExitPlanModeTools, createGuiTools, createSessionTools, createGitNexusTools, createScreenpipeTools, } from "../tools/registry/index.js";
19
+ import { getFormalToolRegistry, createTextEditorTools, createBashTools, createLsTools, createSearchTools, createWebTools, createTodoTools, createCronjobTools, createDockerTools, createKubernetesTools, createGitTools, createMiscTools, createBrowserTools, createProcessTools, createVisionTools, createScriptTools, createPlanTools, createKnowledgeTools, createRelationshipIntelligenceTools, createInternetScoutTools, createLeadScoutTools, createBrowserOperatorTools, createWindowsTools, createMemoryTools, createParallelTools, createAttentionTools, createSkillsInspectionTools, createLessonsTools, createUserModelTools, createAliasTools, createMultimodalTools, createAdvancedTools, createCanvasTools, createLspTools, createMergeConflictTools, createVulnScannerTools, createCodebaseReplaceTools, createAdvisorTools, createFleetTools, createAskUserQuestionTools, createExitPlanModeTools, createGuiTools, createSessionTools, createGitNexusTools, createScreenpipeTools, } from "../tools/registry/index.js";
20
20
  import { getMCPManager } from "../codebuddy/tools.js";
21
21
  import { getErrorMessage } from "../errors/index.js";
22
22
  import { logger } from "../utils/logger.js";
@@ -44,6 +44,67 @@ const READ_ONLY_FILE_TOOLS = new Set([
44
44
  'find_definition',
45
45
  'search_multi',
46
46
  ]);
47
+ export function normalizeHallucinatedLocalToolCall(toolName, args) {
48
+ const normalizedName = toolName.toLowerCase().replace(/\s+/g, '');
49
+ const thoughtToolCallSuffix = normalizedName.match(/^thought_tool_call_\d+(?:_\d+)*\.(bash|create_file|write_file|view_file|read_file)$/)?.[1];
50
+ if (thoughtToolCallSuffix === 'bash') {
51
+ const command = firstStringValue(args.command, args.cmd, args.shell_command, args.command_to_execute, args.code);
52
+ return {
53
+ toolName: 'bash',
54
+ args: command ? { ...args, command } : args,
55
+ };
56
+ }
57
+ if (thoughtToolCallSuffix === 'create_file' || thoughtToolCallSuffix === 'write_file') {
58
+ const path = firstStringValue(args.path, args.file_path, args.target_file, args.filename, args.file);
59
+ const content = firstStringValue(args.content, args.file_content, args.contents, args.text);
60
+ return {
61
+ toolName: 'create_file',
62
+ args: {
63
+ ...args,
64
+ ...(path ? { path } : {}),
65
+ ...(content !== undefined ? { content } : {}),
66
+ },
67
+ };
68
+ }
69
+ if (thoughtToolCallSuffix === 'view_file' || thoughtToolCallSuffix === 'read_file') {
70
+ const path = firstStringValue(args.path, args.file_path, args.target_file, args.filename, args.file);
71
+ return {
72
+ toolName: 'view_file',
73
+ args: path ? { ...args, path } : args,
74
+ };
75
+ }
76
+ if (normalizedName.includes('bash_tool:execute') ||
77
+ normalizedName.includes('thought-tool:execute_command') ||
78
+ normalizedName === 'execute_command') {
79
+ const command = firstStringValue(args.command, args.cmd, args.shell_command, args.command_to_execute, args.code);
80
+ return {
81
+ toolName: 'bash',
82
+ args: command ? { ...args, command } : args,
83
+ };
84
+ }
85
+ if (normalizedName.includes('bash_tool:write_file') ||
86
+ normalizedName.includes('thought-tool:write_file') ||
87
+ normalizedName.includes('call:bash_tool:write_file')) {
88
+ const path = firstStringValue(args.path, args.file_path, args.target_file, args.filename, args.file);
89
+ const content = firstStringValue(args.content, args.file_content, args.contents, args.text);
90
+ return {
91
+ toolName: 'create_file',
92
+ args: {
93
+ ...args,
94
+ ...(path ? { path } : {}),
95
+ ...(content !== undefined ? { content } : {}),
96
+ },
97
+ };
98
+ }
99
+ return null;
100
+ }
101
+ function firstStringValue(...values) {
102
+ for (const value of values) {
103
+ if (typeof value === 'string' && value.trim())
104
+ return value;
105
+ }
106
+ return undefined;
107
+ }
47
108
  /**
48
109
  * ToolHandler manages tool instantiation and execution
49
110
  *
@@ -156,6 +217,7 @@ export class ToolHandler {
156
217
  ...createInternetScoutTools(),
157
218
  ...createLeadScoutTools(),
158
219
  ...createBrowserOperatorTools(),
220
+ ...(process.platform === 'win32' ? createWindowsTools() : []),
159
221
  ...createMemoryTools(),
160
222
  ...createParallelTools(),
161
223
  ...createAttentionTools(),
@@ -244,8 +306,25 @@ export class ToolHandler {
244
306
  const startTime = Date.now();
245
307
  const hooksManager = getToolHooksManager();
246
308
  try {
247
- const args = JSON.parse(toolCall.function.arguments);
248
- const toolName = toolCall.function.name;
309
+ let args = JSON.parse(toolCall.function.arguments);
310
+ let toolName = toolCall.function.name;
311
+ const normalized = normalizeHallucinatedLocalToolCall(toolName, args);
312
+ if (normalized) {
313
+ logger.debug('Normalized hallucinated local tool name', {
314
+ from: toolName,
315
+ to: normalized.toolName,
316
+ });
317
+ toolName = normalized.toolName;
318
+ args = normalized.args;
319
+ toolCall = {
320
+ ...toolCall,
321
+ function: {
322
+ ...toolCall.function,
323
+ name: toolName,
324
+ arguments: JSON.stringify(args),
325
+ },
326
+ };
327
+ }
249
328
  // Create hook context
250
329
  let hookContext = {
251
330
  toolName,
@@ -693,6 +772,12 @@ export class ToolHandler {
693
772
  */
694
773
  async executeBashWithHooks(args, context) {
695
774
  const command = args.command;
775
+ if (typeof command !== 'string' || command.trim() === '') {
776
+ return {
777
+ success: false,
778
+ error: 'bash: missing a non-empty "command" string argument. Put the shell command to run in the "command" field.',
779
+ };
780
+ }
696
781
  // Execute pre-bash hooks
697
782
  try {
698
783
  await this.deps.hooksManager.executeHooks("pre-bash", { command });
@@ -754,6 +839,15 @@ export class ToolHandler {
754
839
  const args = JSON.parse(toolCall.function.arguments);
755
840
  const command = args.command;
756
841
  const timeout = args.timeout || 30000;
842
+ // A weaker model can emit a `bash` call without a `command` (or a
843
+ // non-string one). Fail with a clear, recoverable message instead of
844
+ // crashing downstream on `command.trim()` (auto-repair can then re-prompt).
845
+ if (typeof command !== 'string' || command.trim() === '') {
846
+ return {
847
+ success: false,
848
+ error: 'bash: missing a non-empty "command" string argument. Put the shell command to run in the "command" field.',
849
+ };
850
+ }
757
851
  const gen = this.bash.executeStreaming(command, timeout);
758
852
  let result = await gen.next();
759
853
  while (!result.done) {
@@ -778,8 +872,15 @@ export class ToolHandler {
778
872
  mode: mode,
779
873
  });
780
874
  reasoner.setMode(mode);
875
+ const problem = args.problem;
876
+ if (typeof problem !== 'string' || problem.trim() === '') {
877
+ return {
878
+ success: false,
879
+ error: 'reason: missing a non-empty "problem" string argument. Put the problem to reason about in the "problem" field.',
880
+ };
881
+ }
781
882
  const gen = reasoner.solveStreaming({
782
- description: args.problem,
883
+ description: problem,
783
884
  context: args.context,
784
885
  constraints: args.constraints,
785
886
  });
@@ -796,6 +897,31 @@ export class ToolHandler {
796
897
  return { success: false, error: `Reasoning error: ${getErrorMessage(error)}` };
797
898
  }
798
899
  }
900
+ // generate_document: stream the build phases (prepare → parse → build →
901
+ // write → verify → done) so the UI shows the steps unfold one by one
902
+ // instead of a single opaque "Created X" at the end.
903
+ if (toolName === 'generate_document') {
904
+ try {
905
+ const args = JSON.parse(toolCall.function.arguments);
906
+ const { executeGenerateDocumentStreaming } = await import('../tools/document-generator.js');
907
+ const gen = executeGenerateDocumentStreaming({
908
+ type: args.type,
909
+ title: args.title,
910
+ content: args.content,
911
+ outputPath: args.outputPath ?? args.output_path,
912
+ theme: args.theme,
913
+ });
914
+ let r = await gen.next();
915
+ while (!r.done) {
916
+ yield r.value;
917
+ r = await gen.next();
918
+ }
919
+ return r.value ?? { success: false, error: 'Document tool returned no result' };
920
+ }
921
+ catch (error) {
922
+ return { success: false, error: `Document streaming error: ${getErrorMessage(error)}` };
923
+ }
924
+ }
799
925
  // Fallback: non-streaming execution
800
926
  return await this.executeTool(toolCall);
801
927
  }
@@ -189,12 +189,27 @@ export class BrowserOperatorExecutor {
189
189
  'access denied',
190
190
  'security checkpoint',
191
191
  ];
192
+ const sensitivePlatforms = [
193
+ 'yandex.com',
194
+ 'yandex.ru',
195
+ 'login.microsoftonline.com',
196
+ 'zoom.us',
197
+ ];
192
198
  for (const indicator of antiBotIndicators) {
193
199
  if (lower.includes(indicator)) {
194
200
  raiseCheckpoint(`Security checkpoint detected: "${indicator}"`);
195
201
  break;
196
202
  }
197
203
  }
204
+ if (!checkpointDetected) {
205
+ const currentUrl = String(this.page.url?.() ?? '').toLowerCase();
206
+ for (const platform of sensitivePlatforms) {
207
+ if (currentUrl.includes(platform)) {
208
+ raiseCheckpoint(`OSINT Policy Violation: Target-specific exploit scripts are strictly declined for "${platform}"`);
209
+ break;
210
+ }
211
+ }
212
+ }
198
213
  }
199
214
  catch {
200
215
  // Ignore polling errors while pages are navigating or closing.
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Browser Use Runner
3
+ *
4
+ * browser-use is an open-source agentic browser-automation library. This runner
5
+ * routes a task through one of three paths, in precedence order:
6
+ *
7
+ * 1. The Browser Use **managed API** (via `BROWSER_USE_API_KEY`), or
8
+ * 2. The **Nous Tool Gateway** (`CODEBUDDY_NOUS_TOOL_GATEWAY_URL`), or
9
+ * 3. A **local** browser-use install driven against a local Ollama model
10
+ * (no paid account required). Enabled with `CODEBUDDY_BROWSER_USE_LOCAL=1`
11
+ * or auto-detected when the `browser_use` Python package is importable and
12
+ * neither managed path is configured.
13
+ *
14
+ * The managed paths (1 and 2) speak HTTP via `fetch`. The local path (3) spawns
15
+ * a Python subprocess that runs `Agent(task=..., llm=ChatOllama(...)).run()` and
16
+ * prints a sentinel-delimited JSON result line, which we parse and normalise
17
+ * back into the same `BrowserUseActionResult` shape.
18
+ *
19
+ * Returns structured results with optional screenshot data. Falls back
20
+ * gracefully (typed error result, never throws) when nothing is configured.
21
+ */
22
+ export interface BrowserUseRunnerOptions {
23
+ /** Browser Use API key (falls back to `BROWSER_USE_API_KEY` env). */
24
+ apiKey?: string;
25
+ /** Nous Tool Gateway URL (falls back to `CODEBUDDY_NOUS_TOOL_GATEWAY_URL` env). */
26
+ gatewayUrl?: string;
27
+ /** Request/run timeout in milliseconds (default: 60 000 managed, 180 000 local). */
28
+ timeout?: number;
29
+ /**
30
+ * Force/disable the local browser-use path explicitly. When undefined it is
31
+ * read from `CODEBUDDY_BROWSER_USE_LOCAL` and otherwise auto-detected.
32
+ */
33
+ local?: boolean;
34
+ /**
35
+ * Python interpreter that has the `browser_use` package installed.
36
+ * Defaults to `$CODEBUDDY_BROWSER_USE_PYTHON`, then `python3`.
37
+ */
38
+ pythonPath?: string;
39
+ /**
40
+ * Ollama model the local browser-use agent should drive. Defaults to
41
+ * `$CODEBUDDY_BROWSER_USE_MODEL`, then `qwen2.5:7b-instruct` (a tool-calling
42
+ * capable model — browser-use relies on structured tool calls).
43
+ */
44
+ model?: string;
45
+ /**
46
+ * Ollama host the local agent should connect to. Defaults to `$OLLAMA_HOST`,
47
+ * then `http://localhost:11434`.
48
+ */
49
+ ollamaHost?: string;
50
+ /**
51
+ * Path to a Chrome/Chromium binary for browser-use to launch
52
+ * (`BrowserProfile.executable_path`). Defaults to `$CODEBUDDY_BROWSER_USE_CHROME`.
53
+ * On sandboxed Linux/CI hosts the snap-confined chromium often fails the
54
+ * 30 s browser-start watchdog; pointing at a plain Chromium binary (e.g. the
55
+ * Playwright cache) plus `chromiumSandbox: false` is what makes launch work.
56
+ */
57
+ chromePath?: string;
58
+ /**
59
+ * Disable Chromium's sandbox and add `--no-sandbox` (needed inside most
60
+ * containers / sandboxed CI). Defaults from `$CODEBUDDY_BROWSER_USE_NO_SANDBOX`
61
+ * (truthy), else true on Linux where the sandbox commonly blocks launch.
62
+ */
63
+ noSandbox?: boolean;
64
+ /**
65
+ * Whether browser-use should send screenshots to the model. browser-use sends
66
+ * them by default, which 400s on text-only Ollama models
67
+ * (`Multimodal data provided, but model does not support multimodal requests`).
68
+ * Defaults to `false` so the default text model works; set true with a vision
69
+ * model. Read from `$CODEBUDDY_BROWSER_USE_VISION` when undefined.
70
+ */
71
+ useVision?: boolean;
72
+ /** Max agent steps for the local run (default: 6). */
73
+ maxSteps?: number;
74
+ }
75
+ export interface BrowserUseActionResult {
76
+ ok: boolean;
77
+ /** Extracted page content or action result text. */
78
+ content?: string;
79
+ /** Base64-encoded screenshot, when the service provides one. */
80
+ screenshot?: string;
81
+ error?: string;
82
+ }
83
+ /** Pull the last sentinel-prefixed JSON object out of accumulated stdout. */
84
+ export declare function parseLocalResult(stdout: string): BrowserUseActionResult | null;
85
+ /**
86
+ * Execute a browser action through the Browser Use service.
87
+ *
88
+ * ```ts
89
+ * const result = await executeBrowserUseAction(
90
+ * 'Extract the main heading',
91
+ * 'https://example.com',
92
+ * );
93
+ * if (result.ok) console.log(result.content);
94
+ * ```
95
+ */
96
+ export declare function executeBrowserUseAction(action: string, url: string, options?: BrowserUseRunnerOptions): Promise<BrowserUseActionResult>;