@phuetz/code-buddy 1.0.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 (312) hide show
  1. package/README.md +160 -164
  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 +101 -13
  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/lesson-auto-proposer.js +10 -0
  20. package/dist/agent/middleware/index.d.ts +1 -0
  21. package/dist/agent/middleware/index.js +1 -0
  22. package/dist/agent/middleware/session-duration.d.ts +36 -0
  23. package/dist/agent/middleware/session-duration.js +78 -0
  24. package/dist/agent/middleware/visual-validation-middleware.d.ts +17 -0
  25. package/dist/agent/middleware/visual-validation-middleware.js +67 -0
  26. package/dist/agent/model-benchmark.d.ts +77 -0
  27. package/dist/agent/model-benchmark.js +309 -0
  28. package/dist/agent/model-tier.d.ts +14 -0
  29. package/dist/agent/model-tier.js +71 -0
  30. package/dist/agent/multi-agent/agents/orchestrator-agent.js +17 -6
  31. package/dist/agent/multi-agent/multi-agent-system.js +3 -1
  32. package/dist/agent/multi-agent/session-fleet-bridge.d.ts +2 -0
  33. package/dist/agent/multi-agent/session-fleet-bridge.js +9 -1
  34. package/dist/agent/multi-agent/session-tools.js +22 -5
  35. package/dist/agent/reasoning/mcts.js +20 -6
  36. package/dist/agent/session-end-flush.d.ts +71 -0
  37. package/dist/agent/session-end-flush.js +233 -0
  38. package/dist/agent/streaming/streaming-handler.d.ts +7 -0
  39. package/dist/agent/streaming/streaming-handler.js +8 -1
  40. package/dist/agent/tool-executor.js +1 -0
  41. package/dist/agent/tool-handler.d.ts +5 -0
  42. package/dist/agent/tool-handler.js +130 -4
  43. package/dist/browser-automation/browser-operator-executor.js +15 -0
  44. package/dist/browser-automation/browser-use-runner.d.ts +96 -0
  45. package/dist/browser-automation/browser-use-runner.js +492 -0
  46. package/dist/browser-automation/camofox-runner.d.ts +107 -0
  47. package/dist/browser-automation/camofox-runner.js +287 -0
  48. package/dist/channels/dingtalk/index.js +3 -0
  49. package/dist/channels/discord/client.d.ts +9 -0
  50. package/dist/channels/discord/client.js +12 -0
  51. package/dist/channels/feishu/index.d.ts +167 -1
  52. package/dist/channels/feishu/index.js +432 -7
  53. package/dist/channels/gateway-lifecycle.d.ts +132 -0
  54. package/dist/channels/gateway-lifecycle.js +219 -0
  55. package/dist/channels/google-chat/index.d.ts +5 -1
  56. package/dist/channels/google-chat/index.js +5 -1
  57. package/dist/channels/imessage/index.d.ts +14 -0
  58. package/dist/channels/imessage/index.js +64 -17
  59. package/dist/channels/index.d.ts +4 -0
  60. package/dist/channels/index.js +4 -0
  61. package/dist/channels/irc/index.d.ts +73 -3
  62. package/dist/channels/irc/index.js +446 -11
  63. package/dist/channels/line/index.js +3 -0
  64. package/dist/channels/mattermost/index.d.ts +51 -4
  65. package/dist/channels/mattermost/index.js +303 -20
  66. package/dist/channels/nextcloud-talk/index.d.ts +89 -8
  67. package/dist/channels/nextcloud-talk/index.js +367 -16
  68. package/dist/channels/nostr/index.d.ts +121 -1
  69. package/dist/channels/nostr/index.js +396 -8
  70. package/dist/channels/ntfy/index.js +3 -0
  71. package/dist/channels/qq/index.js +3 -0
  72. package/dist/channels/slash-parity.d.ts +117 -0
  73. package/dist/channels/slash-parity.js +185 -0
  74. package/dist/channels/synology-chat/index.js +3 -0
  75. package/dist/channels/teams/index.d.ts +3 -0
  76. package/dist/channels/teams/index.js +3 -0
  77. package/dist/channels/telegram/client.d.ts +9 -0
  78. package/dist/channels/telegram/client.js +12 -0
  79. package/dist/channels/twilio-voice/index.js +3 -0
  80. package/dist/channels/webchat/index.d.ts +7 -1
  81. package/dist/channels/webchat/index.js +7 -1
  82. package/dist/channels/wecom/index.js +3 -0
  83. package/dist/channels/weixin/index.js +3 -0
  84. package/dist/channels/zalo/index.js +3 -0
  85. package/dist/cli/config-loader.js +1 -1
  86. package/dist/codebuddy/client.d.ts +30 -1
  87. package/dist/codebuddy/client.js +167 -20
  88. package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +5 -0
  89. package/dist/codebuddy/providers/provider-chatgpt-responses.js +1 -0
  90. package/dist/codebuddy/providers/provider-gemini-native.js +1 -1
  91. package/dist/codebuddy/providers/provider-openai-compat.d.ts +5 -0
  92. package/dist/codebuddy/providers/provider-openai-compat.js +82 -2
  93. package/dist/codebuddy/tool-definitions/computer-control-tools.js +32 -2
  94. package/dist/codebuddy/tool-definitions/core-tools.js +3 -3
  95. package/dist/codebuddy/tool-definitions/index.d.ts +2 -1
  96. package/dist/codebuddy/tool-definitions/index.js +2 -1
  97. package/dist/codebuddy/tool-definitions/multimodal-tools.d.ts +1 -0
  98. package/dist/codebuddy/tool-definitions/multimodal-tools.js +44 -1
  99. package/dist/codebuddy/tool-definitions/windows-tools.d.ts +2 -0
  100. package/dist/codebuddy/tool-definitions/windows-tools.js +34 -0
  101. package/dist/codebuddy/tools.d.ts +1 -1
  102. package/dist/codebuddy/tools.js +27 -24
  103. package/dist/commands/cli/hermes-commands.js +108 -0
  104. package/dist/commands/cli/native-engine-commands.js +107 -3
  105. package/dist/commands/cli/utility-commands.js +45 -0
  106. package/dist/commands/enhanced-command-handler.js +5 -0
  107. package/dist/commands/goal-cli.d.ts +71 -0
  108. package/dist/commands/goal-cli.js +280 -0
  109. package/dist/commands/handlers/goal-handler.d.ts +34 -0
  110. package/dist/commands/handlers/goal-handler.js +155 -0
  111. package/dist/commands/handlers/index.d.ts +1 -0
  112. package/dist/commands/handlers/index.js +2 -0
  113. package/dist/commands/handlers/infra-handlers.js +36 -0
  114. package/dist/commands/handlers/memory-handlers.js +96 -1
  115. package/dist/commands/headless-slash.d.ts +2 -0
  116. package/dist/commands/headless-slash.js +12 -0
  117. package/dist/commands/llm-provider-resolution.d.ts +5 -3
  118. package/dist/commands/llm-provider-resolution.js +87 -33
  119. package/dist/commands/ollama.d.ts +25 -0
  120. package/dist/commands/ollama.js +100 -0
  121. package/dist/commands/provider.d.ts +5 -0
  122. package/dist/commands/provider.js +106 -55
  123. package/dist/commands/slash/builtin-commands.js +20 -0
  124. package/dist/commands/spec-next.js +2 -1
  125. package/dist/commands/spec-plan.js +5 -16
  126. package/dist/commands/tunnel.d.ts +2 -0
  127. package/dist/commands/tunnel.js +48 -0
  128. package/dist/config/config-resolver.d.ts +2 -1
  129. package/dist/config/config-resolver.js +54 -41
  130. package/dist/config/constants.d.ts +28 -0
  131. package/dist/config/constants.js +7 -0
  132. package/dist/config/env-schema.js +770 -0
  133. package/dist/config/feature-flags.js +7 -0
  134. package/dist/config/model-tools.js +4 -4
  135. package/dist/config/toml-config.d.ts +16 -0
  136. package/dist/config/toml-config.js +3 -0
  137. package/dist/context/context-manager-v2.d.ts +39 -0
  138. package/dist/context/context-manager-v2.js +91 -0
  139. package/dist/daemon/agent-task-executor.js +12 -1
  140. package/dist/daemon/autonomous-daemon.d.ts +1 -1
  141. package/dist/daemon/autonomous-daemon.js +5 -3
  142. package/dist/daemon/autonomous-loop.d.ts +21 -1
  143. package/dist/daemon/autonomous-loop.js +69 -0
  144. package/dist/daemon/colab-goal.d.ts +38 -0
  145. package/dist/daemon/colab-goal.js +81 -0
  146. package/dist/daemon/cron-agent-bridge.d.ts +12 -3
  147. package/dist/daemon/cron-agent-bridge.js +25 -9
  148. package/dist/desktop/codebuddy-engine-adapter.d.ts +32 -0
  149. package/dist/desktop/codebuddy-engine-adapter.js +257 -80
  150. package/dist/desktop/engine-adapter.d.ts +14 -0
  151. package/dist/desktop-automation/automation-manager.js +16 -0
  152. package/dist/desktop-automation/omniparser-runner.d.ts +63 -0
  153. package/dist/desktop-automation/omniparser-runner.js +115 -0
  154. package/dist/desktop-automation/smart-snapshot.d.ts +11 -0
  155. package/dist/desktop-automation/smart-snapshot.js +72 -5
  156. package/dist/doctor/index.js +40 -23
  157. package/dist/events/types.d.ts +11 -0
  158. package/dist/fleet/colab-store.d.ts +21 -0
  159. package/dist/fleet/colab-store.js +28 -0
  160. package/dist/fleet/peer-session-bridge.d.ts +1 -1
  161. package/dist/fleet/peer-session-bridge.js +243 -2
  162. package/dist/fleet/peer-session-store.d.ts +3 -0
  163. package/dist/fleet/peer-tool-bridge.js +14 -0
  164. package/dist/fleet/privacy-lint.d.ts +8 -0
  165. package/dist/fleet/privacy-lint.js +22 -0
  166. package/dist/goals/goal-decomposer.d.ts +27 -0
  167. package/dist/goals/goal-decomposer.js +293 -0
  168. package/dist/goals/goal-judge-client.d.ts +15 -0
  169. package/dist/goals/goal-judge-client.js +45 -0
  170. package/dist/goals/goal-judge.d.ts +38 -0
  171. package/dist/goals/goal-judge.js +158 -0
  172. package/dist/goals/goal-loop.d.ts +37 -0
  173. package/dist/goals/goal-loop.js +101 -0
  174. package/dist/goals/goal-manager.d.ts +77 -0
  175. package/dist/goals/goal-manager.js +289 -0
  176. package/dist/goals/goal-state.d.ts +99 -0
  177. package/dist/goals/goal-state.js +315 -0
  178. package/dist/goals/goal-store.d.ts +28 -0
  179. package/dist/goals/goal-store.js +87 -0
  180. package/dist/goals/index.d.ts +5 -0
  181. package/dist/goals/index.js +6 -0
  182. package/dist/hooks/use-input-handler.js +36 -1
  183. package/dist/hooks/user-hooks.js +17 -3
  184. package/dist/index.js +506 -25
  185. package/dist/input/text-to-speech.d.ts +2 -6
  186. package/dist/input/text-to-speech.js +2 -27
  187. package/dist/integrations/screenpipe/screenpipe-client.d.ts +10 -0
  188. package/dist/integrations/screenpipe/screenpipe-client.js +12 -2
  189. package/dist/integrations/tailscale.d.ts +13 -0
  190. package/dist/integrations/tailscale.js +87 -34
  191. package/dist/knowledge/workspace-indexer.js +53 -9
  192. package/dist/mcp/client.js +1 -0
  193. package/dist/mcp/mcp-desktop-tools.d.ts +23 -0
  194. package/dist/mcp/mcp-desktop-tools.js +158 -0
  195. package/dist/mcp/mcp-server.js +2 -0
  196. package/dist/memory/enhanced-memory.d.ts +10 -0
  197. package/dist/memory/enhanced-memory.js +33 -2
  198. package/dist/memory/index.d.ts +3 -1
  199. package/dist/memory/index.js +4 -1
  200. package/dist/memory/memory-auto-proposer.d.ts +23 -0
  201. package/dist/memory/memory-auto-proposer.js +308 -0
  202. package/dist/memory/memory-candidate-queue.d.ts +92 -0
  203. package/dist/memory/memory-candidate-queue.js +261 -0
  204. package/dist/memory/persistent-memory.d.ts +45 -2
  205. package/dist/memory/persistent-memory.js +236 -40
  206. package/dist/ml/bayesian-qualifier.d.ts +1 -1
  207. package/dist/ml/bayesian-qualifier.js +35 -2
  208. package/dist/observability/run-store.d.ts +1 -1
  209. package/dist/openclaw/gateway-bridge.js +5 -0
  210. package/dist/plugins/marketplace.d.ts +1 -0
  211. package/dist/plugins/marketplace.js +7 -0
  212. package/dist/prompts/execution-discipline.d.ts +14 -0
  213. package/dist/prompts/execution-discipline.js +29 -0
  214. package/dist/prompts/variation-injector.js +9 -2
  215. package/dist/protocols/acp/acp-agentic-runner.d.ts +7 -6
  216. package/dist/protocols/acp/acp-agentic-runner.js +115 -18
  217. package/dist/protocols/acp/acp-session-store.d.ts +23 -0
  218. package/dist/protocols/acp/acp-session-store.js +77 -0
  219. package/dist/protocols/acp/acp-stdio-server.d.ts +7 -0
  220. package/dist/protocols/acp/acp-stdio-server.js +71 -16
  221. package/dist/providers/active-llm-registry.d.ts +37 -0
  222. package/dist/providers/active-llm-registry.js +186 -0
  223. package/dist/providers/auxiliary-provider.d.ts +25 -0
  224. package/dist/providers/auxiliary-provider.js +192 -0
  225. package/dist/providers/codex-oauth.d.ts +1 -1
  226. package/dist/providers/codex-oauth.js +27 -4
  227. package/dist/providers/index.d.ts +3 -0
  228. package/dist/providers/index.js +4 -0
  229. package/dist/providers/provider-catalog.d.ts +70 -0
  230. package/dist/providers/provider-catalog.js +738 -0
  231. package/dist/providers/provider-fallback.d.ts +35 -0
  232. package/dist/providers/provider-fallback.js +171 -0
  233. package/dist/providers/xai-oauth.d.ts +128 -0
  234. package/dist/providers/xai-oauth.js +735 -0
  235. package/dist/scheduler/cron-scheduler.d.ts +15 -3
  236. package/dist/scheduler/cron-scheduler.js +53 -7
  237. package/dist/scheduler/script-runner.d.ts +2 -0
  238. package/dist/scheduler/script-runner.js +19 -4
  239. package/dist/scheduler/watchdog-handlers.js +7 -20
  240. package/dist/search/usearch-index.js +7 -2
  241. package/dist/security/tool-policy/tool-groups.js +2 -0
  242. package/dist/server/index.js +17 -2
  243. package/dist/server/routes/mobile.d.ts +12 -4
  244. package/dist/server/routes/mobile.js +116 -1
  245. package/dist/server/tls-config.d.ts +35 -0
  246. package/dist/server/tls-config.js +142 -0
  247. package/dist/server/tunnel-manager.d.ts +20 -0
  248. package/dist/server/tunnel-manager.js +58 -0
  249. package/dist/server/websocket/fleet-bridge.d.ts +13 -1
  250. package/dist/server/websocket/fleet-bridge.js +16 -0
  251. package/dist/services/prompt-builder.d.ts +1 -0
  252. package/dist/services/prompt-builder.js +49 -8
  253. package/dist/shared/engine-types.d.ts +15 -1
  254. package/dist/sidecar/sidecar-bridge.d.ts +1 -0
  255. package/dist/sidecar/sidecar-bridge.js +21 -10
  256. package/dist/spec/spec-store.js +8 -1
  257. package/dist/talk-mode/providers/audioreader-tts.d.ts +1 -1
  258. package/dist/talk-mode/providers/audioreader-tts.js +8 -8
  259. package/dist/templates/project-scaffolding.js +3 -2
  260. package/dist/themes/theme-schema.d.ts +10 -10
  261. package/dist/tools/application-profiles.js +38 -0
  262. package/dist/tools/bash/bash-tool.d.ts +1 -0
  263. package/dist/tools/bash/bash-tool.js +16 -2
  264. package/dist/tools/bash/command-validator.js +3 -0
  265. package/dist/tools/bash/rtk-rewrite.d.ts +16 -0
  266. package/dist/tools/bash/rtk-rewrite.js +100 -0
  267. package/dist/tools/bash/streaming-executor.js +14 -1
  268. package/dist/tools/computer-control-tool.d.ts +16 -1
  269. package/dist/tools/computer-control-tool.js +317 -5
  270. package/dist/tools/document-generator.d.ts +14 -0
  271. package/dist/tools/document-generator.js +79 -0
  272. package/dist/tools/execute-code-rpc-invoker.d.ts +26 -2
  273. package/dist/tools/execute-code-rpc-invoker.js +46 -5
  274. package/dist/tools/metadata.js +28 -0
  275. package/dist/tools/office-macro-tool.d.ts +10 -0
  276. package/dist/tools/office-macro-tool.js +93 -0
  277. package/dist/tools/registry/index.d.ts +2 -1
  278. package/dist/tools/registry/index.js +2 -1
  279. package/dist/tools/registry/memory-tools.d.ts +20 -0
  280. package/dist/tools/registry/memory-tools.js +225 -3
  281. package/dist/tools/registry/multimodal-tools.js +2 -2
  282. package/dist/tools/registry/vision-tools.d.ts +33 -0
  283. package/dist/tools/registry/vision-tools.js +308 -6
  284. package/dist/tools/registry/windows-tools.d.ts +2 -0
  285. package/dist/tools/registry/windows-tools.js +47 -0
  286. package/dist/tools/route-peer-tool.js +14 -0
  287. package/dist/tools/screenshot-tool.js +14 -2
  288. package/dist/tools/text-to-speech-tool.d.ts +1 -1
  289. package/dist/tools/text-to-speech-tool.js +2 -12
  290. package/dist/tools/tool-selector.js +3 -59
  291. package/dist/utils/config-validation/schema.d.ts +40 -7
  292. package/dist/utils/config-validation/schema.js +130 -1
  293. package/dist/utils/cost-tracker.js +21 -1
  294. package/dist/utils/disk-guard.d.ts +180 -0
  295. package/dist/utils/disk-guard.js +404 -0
  296. package/dist/utils/interactive-setup.js +35 -2
  297. package/dist/utils/model-utils.js +11 -2
  298. package/dist/utils/output-sanitizer.js +11 -0
  299. package/dist/utils/provider-detector.d.ts +8 -7
  300. package/dist/utils/provider-detector.js +19 -77
  301. package/dist/utils/settings-manager.d.ts +7 -0
  302. package/dist/utils/token-display.d.ts +3 -3
  303. package/dist/utils/token-display.js +22 -4
  304. package/dist/wizard/onboarding.d.ts +39 -0
  305. package/dist/wizard/onboarding.js +433 -21
  306. package/dist/wizard/provider-onboarding.d.ts +8 -23
  307. package/dist/wizard/provider-onboarding.js +39 -158
  308. package/package.json +4 -2
  309. package/dist/plugins/provider-onboarding.d.ts +0 -23
  310. package/dist/plugins/provider-onboarding.js +0 -116
  311. package/dist/utils/kokoro-tts.d.ts +0 -8
  312. package/dist/utils/kokoro-tts.js +0 -67
@@ -0,0 +1,155 @@
1
+ import { decomposeGoal, formatGoalPlan, shouldAutoDecomposeGoal, } from '../../goals/goal-decomposer.js';
2
+ import { getGoalManager } from '../../goals/goal-manager.js';
3
+ /**
4
+ * /goal — standing goal with judge + auto-continue loop (the Ralph loop,
5
+ * ported from Hermes Agent).
6
+ *
7
+ * Forms:
8
+ * /goal <text> set a new goal (replaces any old one) and start turn 1
9
+ * /goal | status show current state
10
+ * /goal pause halt auto-continuation, keep the goal
11
+ * /goal resume restart the loop (resets the turn budget)
12
+ * /goal clear discard the goal (aliases: stop, done)
13
+ */
14
+ export async function handleGoal(args, options = {}) {
15
+ const arg = args.join(' ').trim();
16
+ const lower = arg.toLowerCase();
17
+ const mgr = getGoalManager(options.sessionKey);
18
+ if (!arg || lower === 'status') {
19
+ return textResult(mgr.statusLine());
20
+ }
21
+ if (lower === 'pause') {
22
+ const state = mgr.pause('user-paused');
23
+ return textResult(state ? `⏸ Goal paused: ${state.goal}` : 'No goal set.');
24
+ }
25
+ if (lower === 'resume') {
26
+ const state = mgr.resume();
27
+ if (!state) {
28
+ return textResult('No goal to resume.');
29
+ }
30
+ return textResult(`▶ Goal resumed: ${state.goal}\n` +
31
+ 'Send any message to kick the loop off (e.g. "continue").');
32
+ }
33
+ if (['clear', 'stop', 'done'].includes(lower)) {
34
+ const had = mgr.hasGoal();
35
+ mgr.clear();
36
+ return textResult(had ? '✓ Goal cleared.' : 'No active goal.');
37
+ }
38
+ // Otherwise treat the arg as the goal text.
39
+ let plan = null;
40
+ let planAttempted = false;
41
+ let planError = '';
42
+ if (options.client && shouldAutoDecomposeGoal(arg)) {
43
+ planAttempted = true;
44
+ try {
45
+ plan = await (options.planner ?? decomposeGoal)(arg, options.client);
46
+ }
47
+ catch (error) {
48
+ planError = error instanceof Error ? error.message : String(error);
49
+ }
50
+ }
51
+ let state;
52
+ try {
53
+ state = mgr.set(arg, {
54
+ ...(plan ? { goalPlan: plan } : {}),
55
+ ...(planAttempted ? { goalPlanAttempted: true } : {}),
56
+ });
57
+ if (planAttempted && !plan && planError) {
58
+ mgr.markGoalPlanAttempted(planError);
59
+ state = mgr.state ?? state;
60
+ }
61
+ }
62
+ catch (error) {
63
+ return textResult(`Invalid goal: ${error instanceof Error ? error.message : String(error)}`);
64
+ }
65
+ const planBlock = state.goalPlan
66
+ ? `\n\nHermes-style task graph attached:\n${formatGoalPlan(state.goalPlan)}`
67
+ : planError
68
+ ? `\n\nGoal planning was skipped after an LLM planner error: ${planError}`
69
+ : '';
70
+ return {
71
+ handled: true,
72
+ entry: {
73
+ type: 'assistant',
74
+ content: `⊙ Goal set (${state.maxTurns}-turn budget): ${state.goal}\n` +
75
+ 'After each turn, a judge model checks if the goal is done. Code Buddy ' +
76
+ 'keeps working until it is, you pause/clear it, or the budget is ' +
77
+ 'exhausted. Use /goal status, /goal pause, /goal resume, /goal clear. ' +
78
+ 'Tip: for unattended runs, enable auto-approval (/yolo or auto-edit) so ' +
79
+ 'the loop is not blocked on confirmations.' +
80
+ planBlock,
81
+ timestamp: new Date(),
82
+ },
83
+ // Kick the loop off immediately — the dispatcher feeds the goal text as
84
+ // the first turn, then the after-turn hook drives the continuation loop.
85
+ passToAI: true,
86
+ prompt: state.goal,
87
+ };
88
+ }
89
+ /**
90
+ * /subgoal — extra acceptance criteria added mid-loop.
91
+ *
92
+ * Forms:
93
+ * /subgoal show current subgoals
94
+ * /subgoal <text> append a criterion
95
+ * /subgoal remove <n> drop subgoal n (1-based)
96
+ * /subgoal clear wipe all subgoals
97
+ *
98
+ * Subgoals get appended to both the judge prompt (verdict must consider
99
+ * them) and the continuation prompt (agent sees them) on the next turn
100
+ * boundary — no special kick needed.
101
+ */
102
+ export async function handleSubgoal(args, options = {}) {
103
+ const arg = args.join(' ').trim();
104
+ const mgr = getGoalManager(options.sessionKey);
105
+ if (!mgr.hasGoal()) {
106
+ return textResult('No active goal. Set one with /goal <text>.');
107
+ }
108
+ if (!arg) {
109
+ return textResult(`${mgr.statusLine()}\n${mgr.renderSubgoals()}`);
110
+ }
111
+ const [verb, ...restTokens] = arg.split(/\s+/);
112
+ const rest = restTokens.join(' ').trim();
113
+ if (verb?.toLowerCase() === 'remove') {
114
+ if (!rest) {
115
+ return textResult('Usage: /subgoal remove <n>');
116
+ }
117
+ const indexToken = rest.split(/\s+/)[0] ?? '';
118
+ const idx = Number(indexToken);
119
+ if (!/^[1-9]\d*$/.test(indexToken) || !Number.isSafeInteger(idx)) {
120
+ return textResult('/subgoal remove: <n> must be a positive integer (1-based index).');
121
+ }
122
+ try {
123
+ const removed = mgr.removeSubgoal(idx);
124
+ return textResult(`✓ Removed subgoal ${idx}: ${removed}`);
125
+ }
126
+ catch (error) {
127
+ return textResult(`/subgoal remove: ${error instanceof Error ? error.message : String(error)}`);
128
+ }
129
+ }
130
+ if (verb?.toLowerCase() === 'clear') {
131
+ try {
132
+ const prev = mgr.clearSubgoals();
133
+ return textResult(prev ? `✓ Cleared ${prev} subgoal${prev !== 1 ? 's' : ''}.` : 'No subgoals to clear.');
134
+ }
135
+ catch (error) {
136
+ return textResult(`/subgoal clear: ${error instanceof Error ? error.message : String(error)}`);
137
+ }
138
+ }
139
+ // Otherwise — append the whole arg as a new subgoal.
140
+ try {
141
+ const text = mgr.addSubgoal(arg);
142
+ const idx = mgr.state?.subgoals.length ?? 0;
143
+ return textResult(`✓ Added subgoal ${idx}: ${text}`);
144
+ }
145
+ catch (error) {
146
+ return textResult(`/subgoal: ${error instanceof Error ? error.message : String(error)}`);
147
+ }
148
+ }
149
+ function textResult(content) {
150
+ return {
151
+ handled: true,
152
+ entry: { type: 'assistant', content, timestamp: new Date() },
153
+ };
154
+ }
155
+ //# sourceMappingURL=goal-handler.js.map
@@ -36,6 +36,7 @@ export { handleFastMode, isFastModeEnabled, getFastModeModel, getFastModeService
36
36
  export { handleBackup, } from './backup-handlers.js';
37
37
  export { handleBtw, setBtwClient, } from './btw-handler.js';
38
38
  export { handleHeartbeat } from './heartbeat-handler.js';
39
+ export { handleGoal, handleSubgoal } from './goal-handler.js';
39
40
  export { handleDailyReset } from './daily-reset-handler.js';
40
41
  export { handleShare } from './team-session-handler.js';
41
42
  export { handleAgents } from './agents-handler.js';
@@ -74,6 +74,8 @@ export { handleBackup, } from './backup-handlers.js';
74
74
  export { handleBtw, setBtwClient, } from './btw-handler.js';
75
75
  // Heartbeat handler (fleet AUTONOMOUS-FLEET-PROTOCOL v0.1)
76
76
  export { handleHeartbeat } from './heartbeat-handler.js';
77
+ // Goal handler (Hermes Agent parity — Ralph loop)
78
+ export { handleGoal, handleSubgoal } from './goal-handler.js';
77
79
  // Daily reset handler (audit OpenClaw heritage activation)
78
80
  export { handleDailyReset } from './daily-reset-handler.js';
79
81
  // Team session handler — slash /share (audit OpenClaw heritage activation, TeamSessionManager wake)
@@ -6,6 +6,7 @@
6
6
  * and the current TurboQuant routing statistics.
7
7
  */
8
8
  import { logger } from '../../utils/logger.js';
9
+ import { fetchOllamaStatus } from '../ollama.js';
9
10
  async function checkEndpoint(name, url, timeoutMs = 4000) {
10
11
  const start = Date.now();
11
12
  try {
@@ -49,6 +50,35 @@ function formatRoutingStats(stats) {
49
50
  }
50
51
  return lines.join('\n');
51
52
  }
53
+ function formatOllamaSummary(status) {
54
+ const lines = [
55
+ ` Reachable: ${status.reachable ? 'YES' : 'NO'}`,
56
+ ` Version: ${status.version ?? '(unknown)'}`,
57
+ ` Models: ${status.models.length > 0 ? status.models.join(', ') : '(none)'}`,
58
+ ];
59
+ if (status.error) {
60
+ lines.push(` Error: ${status.error}`);
61
+ }
62
+ lines.push(` Gemma 4 ready: ${isGemma4Ready(status.version, status.models) ? 'YES' : 'NO'}`);
63
+ return lines.join('\n');
64
+ }
65
+ function isGemma4Ready(version, models) {
66
+ if (!version)
67
+ return false;
68
+ const [majorRaw, minorRaw, patchRaw] = version.split('.');
69
+ const major = Number.parseInt(majorRaw ?? '', 10);
70
+ const minor = Number.parseInt(minorRaw ?? '', 10);
71
+ const patch = Number.parseInt(patchRaw ?? '', 10);
72
+ if (!Number.isFinite(major) || !Number.isFinite(minor) || !Number.isFinite(patch))
73
+ return false;
74
+ if (major > 0)
75
+ return true;
76
+ if (minor > 24)
77
+ return true;
78
+ if (minor === 24 && patch >= 1)
79
+ return true;
80
+ return models.some((model) => model.toLowerCase().includes('gemma4'));
81
+ }
52
82
  // ---------------------------------------------------------------------------
53
83
  // Main handler
54
84
  // ---------------------------------------------------------------------------
@@ -102,6 +132,9 @@ async function handleInfraDashboard() {
102
132
  lines.push(formatStatus(s));
103
133
  }
104
134
  lines.push('');
135
+ lines.push('Ollama Summary:');
136
+ lines.push(formatOllamaSummary(await fetchOllamaStatus(ollamaUrl)));
137
+ lines.push('');
105
138
  // Routing stats from TurboQuant plugin
106
139
  try {
107
140
  const { getTurboQuantStats } = await import('../../plugins/bundled/turboquant-plugin.js');
@@ -159,6 +192,9 @@ async function handleInfraHealth() {
159
192
  for (const s of statuses) {
160
193
  lines.push(formatStatus(s));
161
194
  }
195
+ lines.push('');
196
+ lines.push('Ollama Summary:');
197
+ lines.push(formatOllamaSummary(await fetchOllamaStatus(ollamaUrl)));
162
198
  return { handled: true, response: lines.join('\n') };
163
199
  }
164
200
  //# sourceMappingURL=infra-handlers.js.map
@@ -27,6 +27,9 @@ function formatTimeAgo(date, now = new Date()) {
27
27
  const yr = Math.floor(day / 365);
28
28
  return `${yr} year${yr === 1 ? "" : "s"} ago`;
29
29
  }
30
+ function clip(text, max = 180) {
31
+ return text.length > max ? text.slice(0, max - 1) + "…" : text;
32
+ }
30
33
  /**
31
34
  * Memory - Manage persistent memory using PersistentMemoryManager (Markdown) and EnhancedMemory (SQLite/Vector)
32
35
  */
@@ -103,10 +106,10 @@ export async function handleMemory(args) {
103
106
  content = `Usage: /memory remember <key> <content> [project|user]`;
104
107
  break;
105
108
  }
106
- const value = args.slice(2).join(" ");
107
109
  const scope = (args[args.length - 1] === "user" || args[args.length - 1] === "project")
108
110
  ? args.pop()
109
111
  : "project";
112
+ const value = args.slice(2).join(" ");
110
113
  // Store in both for redundancy and better retrieval
111
114
  await persistentMemory.remember(key, value, { scope, category: "custom" });
112
115
  await enhancedMemory.store({
@@ -121,6 +124,30 @@ export async function handleMemory(args) {
121
124
  content = `Usage: /memory remember <key> <content> [project|user]`;
122
125
  }
123
126
  break;
127
+ case "replace":
128
+ case "update":
129
+ if (args.length >= 3) {
130
+ const key = args[1];
131
+ if (key === undefined) {
132
+ content = `Usage: /memory replace <key> <content> [project|user]`;
133
+ break;
134
+ }
135
+ const scope = (args[args.length - 1] === "user" || args[args.length - 1] === "project")
136
+ ? args.pop()
137
+ : "project";
138
+ const value = args.slice(2).join(" ");
139
+ const result = await persistentMemory.replace(key, value, { scope });
140
+ if (result.status === 'missing') {
141
+ content = result.message;
142
+ }
143
+ else {
144
+ content = `✅ ${result.message}\nCapacity: ${result.usage.used}/${result.usage.limit} chars (${result.usage.percent}%).`;
145
+ }
146
+ }
147
+ else {
148
+ content = `Usage: /memory replace <key> <content> [project|user]`;
149
+ }
150
+ break;
124
151
  case "context":
125
152
  const enhancedContext = await enhancedMemory.buildContext({
126
153
  includeProject: true,
@@ -154,6 +181,74 @@ export async function handleMemory(args) {
154
181
  }
155
182
  break;
156
183
  }
184
+ case "candidates":
185
+ case "candidate":
186
+ case "pending": {
187
+ const { getMemoryCandidateQueue } = await import("../../memory/memory-candidate-queue.js");
188
+ const statusArg = args[1];
189
+ const status = statusArg === "pending" || statusArg === "accepted" || statusArg === "rejected"
190
+ ? statusArg
191
+ : undefined;
192
+ const rawLimit = parseInt(args[status ? 2 : 1] ?? "20", 10);
193
+ const limit = Math.min(50, Math.max(1, Number.isFinite(rawLimit) ? rawLimit : 20));
194
+ const candidates = getMemoryCandidateQueue(process.cwd()).list(status).slice(0, limit);
195
+ if (candidates.length === 0) {
196
+ content = status ? `No ${status} memory candidates.` : "No memory candidates yet.";
197
+ }
198
+ else {
199
+ const lines = [`🧠 Memory candidates (${candidates.length}${status ? ` ${status}` : ""})`, "═".repeat(50)];
200
+ for (const candidate of candidates) {
201
+ const created = formatTimeAgo(new Date(candidate.createdAt));
202
+ const confidence = typeof candidate.confidence === "number"
203
+ ? ` | confidence ${(candidate.confidence * 100).toFixed(0)}%`
204
+ : "";
205
+ lines.push(`[${candidate.status}] ${candidate.id} | ${candidate.scope}/${candidate.category}${confidence} | ${created}`);
206
+ lines.push(` ${candidate.key}: ${clip(candidate.value)}`);
207
+ const citation = candidate.citations?.[0];
208
+ if (citation) {
209
+ const label = citation.sessionId
210
+ ? `${citation.sessionId}${citation.messageId ? `#${citation.messageId}` : citation.messageIndex ? `#${citation.messageIndex}` : ""}`
211
+ : citation.messageIndex ? `message ${citation.messageIndex}` : "evidence";
212
+ lines.push(` citation: ${label}${citation.role ? ` (${citation.role})` : ""} — ${clip(citation.snippet, 140)}`);
213
+ }
214
+ lines.push("");
215
+ }
216
+ lines.push("Approve: /memory accept <id> [reviewer]");
217
+ lines.push("Reject: /memory reject <id> [reason]");
218
+ content = lines.join("\n").trimEnd();
219
+ }
220
+ break;
221
+ }
222
+ case "accept":
223
+ case "approve": {
224
+ const id = args[1];
225
+ if (!id) {
226
+ content = `Usage: /memory accept <candidate-id> [reviewer]`;
227
+ break;
228
+ }
229
+ const reviewedBy = args.slice(2).join(" ").trim() || "user";
230
+ const { getMemoryCandidateQueue } = await import("../../memory/memory-candidate-queue.js");
231
+ const { candidate, write } = await getMemoryCandidateQueue(process.cwd()).accept(id, { reviewedBy });
232
+ content = `✅ Accepted ${candidate.id} into ${candidate.scope} memory as "${candidate.key}".\n` +
233
+ `Write status: ${write.status}. Capacity: ${write.usage.used}/${write.usage.limit} chars (${write.usage.percent}%).`;
234
+ break;
235
+ }
236
+ case "reject":
237
+ case "discard": {
238
+ const id = args[1];
239
+ if (!id) {
240
+ content = `Usage: /memory reject <candidate-id> [reason]`;
241
+ break;
242
+ }
243
+ const reason = args.slice(2).join(" ").trim();
244
+ const { getMemoryCandidateQueue } = await import("../../memory/memory-candidate-queue.js");
245
+ const candidate = getMemoryCandidateQueue(process.cwd()).reject(id, {
246
+ reviewedBy: "user",
247
+ ...(reason ? { reason } : {}),
248
+ });
249
+ content = `🗑️ Rejected memory candidate ${candidate.id}.`;
250
+ break;
251
+ }
157
252
  case "status":
158
253
  case "list":
159
254
  default:
@@ -33,6 +33,8 @@ export interface HeadlessSlashContext {
33
33
  conversationHistory?: ChatEntry[];
34
34
  /** LLM client, for tokens that call the model directly (ai-test, btw). */
35
35
  client?: CodeBuddyClient;
36
+ /** Optional goal-state key for non-TUI surfaces with their own session ids. */
37
+ goalSessionKey?: string;
36
38
  }
37
39
  /** Result of running a special token outside the TUI. */
38
40
  export interface HeadlessSlashResult {
@@ -55,6 +55,18 @@ export async function executeHeadlessSlashToken(token, args, allow, ctx = {}) {
55
55
  if (ctx.client)
56
56
  handler.setCodeBuddyClient(ctx.client);
57
57
  try {
58
+ if (ctx.goalSessionKey && (token === '__GOAL__' || token === '__SUBGOAL__')) {
59
+ const { handleGoal, handleSubgoal } = await import('./handlers/goal-handler.js');
60
+ const result = token === '__GOAL__'
61
+ ? await handleGoal(args, { sessionKey: ctx.goalSessionKey, client: ctx.client ?? null })
62
+ : await handleSubgoal(args, { sessionKey: ctx.goalSessionKey });
63
+ return {
64
+ handled: result.handled,
65
+ output: result.entry?.content,
66
+ prompt: result.prompt,
67
+ passToAI: result.passToAI,
68
+ };
69
+ }
58
70
  const result = await handler.handleCommand(token, args, [token, ...args].join(" "));
59
71
  return {
60
72
  handled: result.handled,
@@ -6,9 +6,11 @@
6
6
  * provider + the `PROVIDERS` map (paid keys), so a machine running on
7
7
  * local Ollama (`CODEBUDDY_PROVIDER=ollama`, $0) hit the "no API key"
8
8
  * exit even though the rest of Code Buddy — including the autonomous
9
- * daemon — runs fine there. This resolver keeps the legacy path first
10
- * (backward compatible) and falls back to {@link detectProviderFromEnv}
11
- * (ollama / chatgpt-oauth / env-detected providers) when either:
9
+ * daemon — runs fine there. This resolver keeps the legacy path for
10
+ * ambient paid-key defaults, but honors explicit local / ChatGPT
11
+ * subscription model choices before a cloud key can hijack them. It
12
+ * falls back to {@link detectProviderFromEnv} (ollama / chatgpt-oauth /
13
+ * env-detected providers) when either:
12
14
  * - `CODEBUDDY_PROVIDER` is explicitly set (operator intent wins), or
13
15
  * - the legacy path found no key.
14
16
  */
@@ -6,58 +6,69 @@
6
6
  * provider + the `PROVIDERS` map (paid keys), so a machine running on
7
7
  * local Ollama (`CODEBUDDY_PROVIDER=ollama`, $0) hit the "no API key"
8
8
  * exit even though the rest of Code Buddy — including the autonomous
9
- * daemon — runs fine there. This resolver keeps the legacy path first
10
- * (backward compatible) and falls back to {@link detectProviderFromEnv}
11
- * (ollama / chatgpt-oauth / env-detected providers) when either:
9
+ * daemon — runs fine there. This resolver keeps the legacy path for
10
+ * ambient paid-key defaults, but honors explicit local / ChatGPT
11
+ * subscription model choices before a cloud key can hijack them. It
12
+ * falls back to {@link detectProviderFromEnv} (ollama / chatgpt-oauth /
13
+ * env-detected providers) when either:
12
14
  * - `CODEBUDDY_PROVIDER` is explicitly set (operator intent wins), or
13
15
  * - the legacy path found no key.
14
16
  */
15
17
  import { getSettingsManager } from '../utils/settings-manager.js';
16
18
  import { detectProviderFromEnv } from '../utils/provider-detector.js';
17
- import { PROVIDERS } from './provider.js';
19
+ import { inferProvider } from '../config/resolve-model.js';
20
+ import { hasCodexCredentials } from '../providers/codex-oauth.js';
21
+ import { resolveProviderFromCatalog } from '../providers/provider-catalog.js';
22
+ import { PROVIDERS, resolveProviderCommandKey } from './provider.js';
18
23
  export function resolveCommandProvider(options = {}) {
24
+ const settingsManager = getSettingsManager();
25
+ const settings = settingsManager.loadUserSettings();
26
+ const configuredModel = options.explicitModel || settingsManager.getCurrentModel();
19
27
  const detectFirst = Boolean(process.env.CODEBUDDY_PROVIDER);
20
28
  if (detectFirst) {
21
29
  const detected = fromEnvDetection(options.explicitModel);
22
30
  if (detected)
23
31
  return detected;
24
32
  }
25
- const settingsManager = getSettingsManager();
26
- const settings = settingsManager.loadUserSettings();
33
+ const explicitLocal = resolveExplicitOllamaModel(configuredModel);
34
+ if (explicitLocal)
35
+ return explicitLocal;
36
+ const explicitChatGpt = resolveExplicitChatGptModel(configuredModel);
37
+ if (explicitChatGpt)
38
+ return explicitChatGpt;
27
39
  const currentProviderKey = settings.provider || 'grok';
28
- const providerInfo = PROVIDERS[currentProviderKey];
29
- let apiKey = process.env[providerInfo?.envVar || ''] || '';
30
- if (!apiKey && currentProviderKey === 'grok')
31
- apiKey = process.env.XAI_API_KEY || '';
32
- if (!apiKey && currentProviderKey === 'gemini')
33
- apiKey = process.env.GOOGLE_API_KEY || '';
34
- if (!apiKey) {
35
- apiKey =
36
- process.env.GROK_API_KEY ||
37
- process.env.ANTHROPIC_API_KEY ||
38
- process.env.OPENAI_API_KEY ||
39
- process.env.GOOGLE_API_KEY ||
40
- '';
40
+ const providerInfoKey = resolveProviderCommandKey(currentProviderKey) || currentProviderKey;
41
+ const providerInfo = PROVIDERS[providerInfoKey];
42
+ if (providerInfo) {
43
+ const configuredProvider = resolveProviderFromCatalog({
44
+ providerOverride: providerInfo.providerId,
45
+ hasChatGptOAuth: hasCodexCredentials(),
46
+ requireConfigured: providerInfo.authMode === 'api-key',
47
+ });
48
+ if (configuredProvider) {
49
+ return {
50
+ apiKey: configuredProvider.apiKey,
51
+ baseURL: configuredProvider.baseURL,
52
+ model: configuredModel || configuredProvider.defaultModel,
53
+ providerLabel: configuredProvider.provider,
54
+ };
55
+ }
41
56
  }
42
- if (apiKey) {
43
- const providerEnvBaseURL = {
44
- grok: process.env.GROK_BASE_URL,
45
- claude: process.env.ANTHROPIC_BASE_URL,
46
- openai: process.env.OPENAI_BASE_URL,
47
- gemini: process.env.GEMINI_BASE_URL,
48
- };
57
+ const ambientProvider = resolveProviderFromCatalog({
58
+ hasChatGptOAuth: hasCodexCredentials(),
59
+ requireConfigured: true,
60
+ });
61
+ if (ambientProvider) {
49
62
  return {
50
- apiKey,
51
- baseURL: providerEnvBaseURL[currentProviderKey] || providerInfo?.baseURL,
52
- model: options.explicitModel ||
53
- settingsManager.getCurrentModel() ||
54
- providerInfo?.defaultModel,
55
- providerLabel: providerInfo?.name || currentProviderKey,
63
+ apiKey: ambientProvider.apiKey,
64
+ baseURL: ambientProvider.baseURL,
65
+ model: configuredModel || ambientProvider.defaultModel,
66
+ providerLabel: ambientProvider.provider,
56
67
  };
57
68
  }
58
69
  // Legacy path found nothing — fall back to env detection (local
59
70
  // Ollama, ChatGPT OAuth, …) so a $0 local machine can still run.
60
- return fromEnvDetection(options.explicitModel);
71
+ return fromEnvDetection(configuredModel);
61
72
  }
62
73
  function fromEnvDetection(explicitModel) {
63
74
  const detected = detectProviderFromEnv();
@@ -70,4 +81,47 @@ function fromEnvDetection(explicitModel) {
70
81
  providerLabel: detected.provider,
71
82
  };
72
83
  }
84
+ function resolveExplicitOllamaModel(explicitModel) {
85
+ const model = explicitModel?.trim();
86
+ if (!model || !isLocalOllamaModel(model))
87
+ return null;
88
+ let host = process.env.OLLAMA_HOST || 'http://localhost:11434';
89
+ if (!/^https?:\/\//i.test(host))
90
+ host = `http://${host}`;
91
+ if (!host.endsWith('/v1'))
92
+ host = host.replace(/\/+$/, '') + '/v1';
93
+ return {
94
+ apiKey: 'ollama',
95
+ baseURL: host,
96
+ model,
97
+ providerLabel: 'ollama',
98
+ };
99
+ }
100
+ function isLocalOllamaModel(model) {
101
+ const normalized = model.trim().toLowerCase();
102
+ return inferProvider(normalized) === 'ollama' || normalized.startsWith('devstral-small-2');
103
+ }
104
+ function resolveExplicitChatGptModel(explicitModel) {
105
+ const model = explicitModel?.trim();
106
+ if (!model || !isChatGptSubscriptionModel(model))
107
+ return null;
108
+ const detected = detectProviderFromEnv();
109
+ if (detected?.provider !== 'chatgpt')
110
+ return null;
111
+ return {
112
+ apiKey: detected.apiKey,
113
+ baseURL: detected.baseURL,
114
+ model,
115
+ providerLabel: detected.provider,
116
+ };
117
+ }
118
+ function isChatGptSubscriptionModel(model) {
119
+ const m = model.trim().toLowerCase();
120
+ return (m === 'gpt-5.2' ||
121
+ m === 'gpt-5.5' ||
122
+ m.startsWith('gpt-5.5-') ||
123
+ m.includes('-codex') ||
124
+ m === 'codex-1' ||
125
+ m.startsWith('codex-mini'));
126
+ }
73
127
  //# sourceMappingURL=llm-provider-resolution.js.map
@@ -0,0 +1,25 @@
1
+ export interface OllamaStatus {
2
+ baseUrl: string;
3
+ reachable: boolean;
4
+ version: string | null;
5
+ models: string[];
6
+ error: string | null;
7
+ }
8
+ export interface OllamaUpdatePlan {
9
+ supported: boolean;
10
+ platform: NodeJS.Platform;
11
+ repoRoot: string;
12
+ scriptPath: string;
13
+ scriptUrl: string;
14
+ command?: string;
15
+ args?: string[];
16
+ message: string;
17
+ }
18
+ export declare function normalizeOllamaBaseUrl(rawUrl?: string): string;
19
+ export declare function fetchOllamaStatus(baseUrl?: string): Promise<OllamaStatus>;
20
+ export declare function buildOllamaUpdatePlan(options: {
21
+ platform?: NodeJS.Platform;
22
+ repoRoot: string;
23
+ scriptUrl?: string;
24
+ }): OllamaUpdatePlan;
25
+ export declare function runOllamaUpdatePlan(plan: OllamaUpdatePlan): Promise<void>;