@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.
- package/README.md +160 -165
- package/dist/agent/autonomous/agentic-coding-contract.d.ts +6 -6
- package/dist/agent/codebuddy-agent.d.ts +1 -1
- package/dist/agent/codebuddy-agent.js +69 -14
- package/dist/agent/execution/agent-executor.js +122 -12
- package/dist/agent/execution/tool-dependency-graph.js +7 -0
- package/dist/agent/execution/tool-hooks.d.ts +6 -0
- package/dist/agent/execution/tool-hooks.js +13 -5
- package/dist/agent/execution/tool-selection-strategy.d.ts +11 -2
- package/dist/agent/execution/tool-selection-strategy.js +67 -10
- package/dist/agent/extended-thinking.d.ts +8 -0
- package/dist/agent/extended-thinking.js +27 -0
- package/dist/agent/hermes-browser-backends.js +151 -11
- package/dist/agent/hermes-claw-migrate.d.ts +59 -0
- package/dist/agent/hermes-claw-migrate.js +427 -26
- package/dist/agent/hermes-parity-manifest.js +78 -71
- package/dist/agent/hermes-runtime-lifecycle.d.ts +67 -0
- package/dist/agent/hermes-runtime-lifecycle.js +468 -0
- package/dist/agent/middleware/index.d.ts +1 -0
- package/dist/agent/middleware/index.js +1 -0
- package/dist/agent/middleware/visual-validation-middleware.d.ts +17 -0
- package/dist/agent/middleware/visual-validation-middleware.js +67 -0
- package/dist/agent/model-benchmark.d.ts +77 -0
- package/dist/agent/model-benchmark.js +309 -0
- package/dist/agent/model-tier.d.ts +14 -0
- package/dist/agent/model-tier.js +71 -0
- package/dist/agent/multi-agent/agents/orchestrator-agent.js +17 -6
- package/dist/agent/multi-agent/multi-agent-system.js +3 -1
- package/dist/agent/multi-agent/session-fleet-bridge.d.ts +2 -0
- package/dist/agent/multi-agent/session-fleet-bridge.js +9 -1
- package/dist/agent/multi-agent/session-tools.js +22 -5
- package/dist/agent/reasoning/mcts.js +20 -6
- package/dist/agent/session-end-flush.d.ts +6 -1
- package/dist/agent/session-end-flush.js +20 -4
- package/dist/agent/streaming/streaming-handler.d.ts +7 -0
- package/dist/agent/streaming/streaming-handler.js +8 -1
- package/dist/agent/tool-executor.js +1 -0
- package/dist/agent/tool-handler.d.ts +5 -0
- package/dist/agent/tool-handler.js +130 -4
- package/dist/browser-automation/browser-operator-executor.js +15 -0
- package/dist/browser-automation/browser-use-runner.d.ts +96 -0
- package/dist/browser-automation/browser-use-runner.js +492 -0
- package/dist/browser-automation/camofox-runner.d.ts +107 -0
- package/dist/browser-automation/camofox-runner.js +287 -0
- package/dist/channels/dingtalk/index.js +3 -0
- package/dist/channels/discord/client.d.ts +9 -0
- package/dist/channels/discord/client.js +12 -0
- package/dist/channels/feishu/index.d.ts +167 -1
- package/dist/channels/feishu/index.js +432 -7
- package/dist/channels/gateway-lifecycle.d.ts +132 -0
- package/dist/channels/gateway-lifecycle.js +219 -0
- package/dist/channels/google-chat/index.d.ts +5 -1
- package/dist/channels/google-chat/index.js +5 -1
- package/dist/channels/imessage/index.d.ts +14 -0
- package/dist/channels/imessage/index.js +64 -17
- package/dist/channels/index.d.ts +4 -0
- package/dist/channels/index.js +4 -0
- package/dist/channels/irc/index.d.ts +73 -3
- package/dist/channels/irc/index.js +446 -11
- package/dist/channels/line/index.js +3 -0
- package/dist/channels/mattermost/index.d.ts +51 -4
- package/dist/channels/mattermost/index.js +303 -20
- package/dist/channels/nextcloud-talk/index.d.ts +89 -8
- package/dist/channels/nextcloud-talk/index.js +367 -16
- package/dist/channels/nostr/index.d.ts +121 -1
- package/dist/channels/nostr/index.js +396 -8
- package/dist/channels/ntfy/index.js +3 -0
- package/dist/channels/qq/index.js +3 -0
- package/dist/channels/slash-parity.d.ts +117 -0
- package/dist/channels/slash-parity.js +185 -0
- package/dist/channels/synology-chat/index.js +3 -0
- package/dist/channels/teams/index.d.ts +3 -0
- package/dist/channels/teams/index.js +3 -0
- package/dist/channels/telegram/client.d.ts +9 -0
- package/dist/channels/telegram/client.js +12 -0
- package/dist/channels/twilio-voice/index.js +3 -0
- package/dist/channels/webchat/index.d.ts +7 -1
- package/dist/channels/webchat/index.js +7 -1
- package/dist/channels/wecom/index.js +3 -0
- package/dist/channels/weixin/index.js +3 -0
- package/dist/channels/zalo/index.js +3 -0
- package/dist/cli/config-loader.js +1 -1
- package/dist/codebuddy/client.d.ts +30 -1
- package/dist/codebuddy/client.js +167 -20
- package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +5 -0
- package/dist/codebuddy/providers/provider-chatgpt-responses.js +1 -0
- package/dist/codebuddy/providers/provider-gemini-native.js +1 -1
- package/dist/codebuddy/providers/provider-openai-compat.d.ts +5 -0
- package/dist/codebuddy/providers/provider-openai-compat.js +82 -2
- package/dist/codebuddy/tool-definitions/computer-control-tools.js +32 -2
- package/dist/codebuddy/tool-definitions/core-tools.js +3 -3
- package/dist/codebuddy/tool-definitions/index.d.ts +2 -1
- package/dist/codebuddy/tool-definitions/index.js +2 -1
- package/dist/codebuddy/tool-definitions/multimodal-tools.d.ts +1 -0
- package/dist/codebuddy/tool-definitions/multimodal-tools.js +44 -1
- package/dist/codebuddy/tool-definitions/windows-tools.d.ts +2 -0
- package/dist/codebuddy/tool-definitions/windows-tools.js +34 -0
- package/dist/codebuddy/tools.d.ts +1 -1
- package/dist/codebuddy/tools.js +27 -24
- package/dist/commands/cli/hermes-commands.js +108 -0
- package/dist/commands/cli/native-engine-commands.js +102 -3
- package/dist/commands/cli/utility-commands.js +45 -0
- package/dist/commands/enhanced-command-handler.js +1 -1
- package/dist/commands/goal-cli.d.ts +30 -0
- package/dist/commands/goal-cli.js +210 -27
- package/dist/commands/handlers/goal-handler.d.ts +9 -2
- package/dist/commands/handlers/goal-handler.js +36 -9
- package/dist/commands/handlers/infra-handlers.js +36 -0
- package/dist/commands/handlers/memory-handlers.js +96 -1
- package/dist/commands/headless-slash.d.ts +2 -0
- package/dist/commands/headless-slash.js +12 -0
- package/dist/commands/llm-provider-resolution.d.ts +5 -3
- package/dist/commands/llm-provider-resolution.js +87 -33
- package/dist/commands/ollama.d.ts +25 -0
- package/dist/commands/ollama.js +100 -0
- package/dist/commands/provider.d.ts +5 -0
- package/dist/commands/provider.js +106 -55
- package/dist/commands/spec-next.js +2 -1
- package/dist/commands/spec-plan.js +5 -16
- package/dist/commands/tunnel.d.ts +2 -0
- package/dist/commands/tunnel.js +48 -0
- package/dist/config/config-resolver.d.ts +2 -1
- package/dist/config/config-resolver.js +54 -41
- package/dist/config/constants.d.ts +28 -0
- package/dist/config/constants.js +7 -0
- package/dist/config/env-schema.js +756 -0
- package/dist/config/model-tools.js +4 -4
- package/dist/config/toml-config.d.ts +16 -0
- package/dist/config/toml-config.js +3 -0
- package/dist/context/context-manager-v2.js +1 -0
- package/dist/daemon/autonomous-daemon.d.ts +1 -1
- package/dist/daemon/autonomous-daemon.js +3 -3
- package/dist/daemon/autonomous-loop.js +11 -2
- package/dist/daemon/colab-goal.js +9 -1
- package/dist/daemon/cron-agent-bridge.d.ts +12 -3
- package/dist/daemon/cron-agent-bridge.js +25 -9
- package/dist/desktop/codebuddy-engine-adapter.d.ts +32 -0
- package/dist/desktop/codebuddy-engine-adapter.js +257 -80
- package/dist/desktop/engine-adapter.d.ts +14 -0
- package/dist/desktop-automation/automation-manager.js +16 -0
- package/dist/desktop-automation/omniparser-runner.d.ts +63 -0
- package/dist/desktop-automation/omniparser-runner.js +115 -0
- package/dist/desktop-automation/smart-snapshot.d.ts +11 -0
- package/dist/desktop-automation/smart-snapshot.js +72 -5
- package/dist/doctor/index.js +40 -23
- package/dist/events/types.d.ts +11 -0
- package/dist/fleet/colab-store.js +14 -2
- package/dist/fleet/peer-session-bridge.js +50 -12
- package/dist/fleet/peer-tool-bridge.js +14 -0
- package/dist/goals/goal-decomposer.d.ts +27 -0
- package/dist/goals/goal-decomposer.js +293 -0
- package/dist/goals/goal-judge-client.d.ts +15 -0
- package/dist/goals/goal-judge-client.js +45 -0
- package/dist/goals/goal-judge.d.ts +2 -0
- package/dist/goals/goal-judge.js +35 -6
- package/dist/goals/goal-loop.d.ts +14 -0
- package/dist/goals/goal-loop.js +47 -2
- package/dist/goals/goal-manager.d.ts +6 -0
- package/dist/goals/goal-manager.js +67 -14
- package/dist/goals/goal-state.d.ts +13 -0
- package/dist/goals/goal-state.js +83 -13
- package/dist/goals/goal-store.d.ts +3 -0
- package/dist/goals/goal-store.js +31 -15
- package/dist/hooks/user-hooks.js +17 -3
- package/dist/index.js +464 -25
- package/dist/input/text-to-speech.d.ts +2 -6
- package/dist/input/text-to-speech.js +2 -27
- package/dist/integrations/screenpipe/screenpipe-client.d.ts +10 -0
- package/dist/integrations/screenpipe/screenpipe-client.js +12 -2
- package/dist/integrations/tailscale.d.ts +13 -0
- package/dist/integrations/tailscale.js +87 -34
- package/dist/knowledge/workspace-indexer.js +53 -9
- package/dist/mcp/client.js +1 -0
- package/dist/mcp/mcp-desktop-tools.d.ts +23 -0
- package/dist/mcp/mcp-desktop-tools.js +158 -0
- package/dist/mcp/mcp-server.js +2 -0
- package/dist/memory/enhanced-memory.d.ts +10 -0
- package/dist/memory/enhanced-memory.js +33 -2
- package/dist/memory/index.d.ts +3 -1
- package/dist/memory/index.js +4 -1
- package/dist/memory/memory-auto-proposer.d.ts +23 -0
- package/dist/memory/memory-auto-proposer.js +308 -0
- package/dist/memory/memory-candidate-queue.d.ts +92 -0
- package/dist/memory/memory-candidate-queue.js +261 -0
- package/dist/memory/persistent-memory.d.ts +45 -2
- package/dist/memory/persistent-memory.js +236 -40
- package/dist/ml/bayesian-qualifier.d.ts +1 -1
- package/dist/ml/bayesian-qualifier.js +35 -2
- package/dist/openclaw/gateway-bridge.js +5 -0
- package/dist/plugins/marketplace.d.ts +1 -0
- package/dist/plugins/marketplace.js +7 -0
- package/dist/prompts/execution-discipline.d.ts +14 -0
- package/dist/prompts/execution-discipline.js +29 -0
- package/dist/prompts/variation-injector.js +9 -2
- package/dist/protocols/acp/acp-agentic-runner.d.ts +7 -6
- package/dist/protocols/acp/acp-agentic-runner.js +115 -18
- package/dist/protocols/acp/acp-session-store.d.ts +23 -0
- package/dist/protocols/acp/acp-session-store.js +77 -0
- package/dist/protocols/acp/acp-stdio-server.d.ts +7 -0
- package/dist/protocols/acp/acp-stdio-server.js +71 -16
- package/dist/providers/active-llm-registry.d.ts +37 -0
- package/dist/providers/active-llm-registry.js +186 -0
- package/dist/providers/auxiliary-provider.d.ts +25 -0
- package/dist/providers/auxiliary-provider.js +192 -0
- package/dist/providers/codex-oauth.d.ts +1 -1
- package/dist/providers/codex-oauth.js +27 -4
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/provider-catalog.d.ts +70 -0
- package/dist/providers/provider-catalog.js +738 -0
- package/dist/providers/provider-fallback.d.ts +35 -0
- package/dist/providers/provider-fallback.js +171 -0
- package/dist/providers/xai-oauth.d.ts +128 -0
- package/dist/providers/xai-oauth.js +735 -0
- package/dist/scheduler/cron-scheduler.d.ts +15 -3
- package/dist/scheduler/cron-scheduler.js +53 -7
- package/dist/scheduler/script-runner.d.ts +2 -0
- package/dist/scheduler/script-runner.js +19 -4
- package/dist/scheduler/watchdog-handlers.js +7 -20
- package/dist/search/usearch-index.js +7 -2
- package/dist/security/tool-policy/tool-groups.js +2 -0
- package/dist/server/index.js +17 -2
- package/dist/server/routes/mobile.d.ts +12 -4
- package/dist/server/routes/mobile.js +116 -1
- package/dist/server/tls-config.d.ts +35 -0
- package/dist/server/tls-config.js +142 -0
- package/dist/server/tunnel-manager.d.ts +20 -0
- package/dist/server/tunnel-manager.js +58 -0
- package/dist/server/websocket/fleet-bridge.js +5 -0
- package/dist/services/prompt-builder.d.ts +1 -0
- package/dist/services/prompt-builder.js +49 -8
- package/dist/shared/engine-types.d.ts +15 -1
- package/dist/sidecar/sidecar-bridge.d.ts +1 -0
- package/dist/sidecar/sidecar-bridge.js +21 -10
- package/dist/spec/spec-store.js +8 -1
- package/dist/talk-mode/providers/audioreader-tts.d.ts +1 -1
- package/dist/talk-mode/providers/audioreader-tts.js +8 -8
- package/dist/templates/project-scaffolding.js +3 -2
- package/dist/themes/theme-schema.d.ts +10 -10
- package/dist/tools/application-profiles.js +38 -0
- package/dist/tools/bash/bash-tool.d.ts +1 -0
- package/dist/tools/bash/bash-tool.js +16 -2
- package/dist/tools/bash/command-validator.js +3 -0
- package/dist/tools/bash/rtk-rewrite.d.ts +16 -0
- package/dist/tools/bash/rtk-rewrite.js +100 -0
- package/dist/tools/bash/streaming-executor.js +14 -1
- package/dist/tools/computer-control-tool.d.ts +16 -1
- package/dist/tools/computer-control-tool.js +317 -5
- package/dist/tools/document-generator.d.ts +14 -0
- package/dist/tools/document-generator.js +79 -0
- package/dist/tools/execute-code-rpc-invoker.d.ts +26 -2
- package/dist/tools/execute-code-rpc-invoker.js +46 -5
- package/dist/tools/metadata.js +28 -0
- package/dist/tools/office-macro-tool.d.ts +10 -0
- package/dist/tools/office-macro-tool.js +93 -0
- package/dist/tools/registry/index.d.ts +2 -1
- package/dist/tools/registry/index.js +2 -1
- package/dist/tools/registry/memory-tools.d.ts +20 -0
- package/dist/tools/registry/memory-tools.js +225 -3
- package/dist/tools/registry/multimodal-tools.js +2 -2
- package/dist/tools/registry/vision-tools.d.ts +33 -0
- package/dist/tools/registry/vision-tools.js +308 -6
- package/dist/tools/registry/windows-tools.d.ts +2 -0
- package/dist/tools/registry/windows-tools.js +47 -0
- package/dist/tools/route-peer-tool.js +14 -0
- package/dist/tools/screenshot-tool.js +14 -2
- package/dist/tools/text-to-speech-tool.d.ts +1 -1
- package/dist/tools/text-to-speech-tool.js +2 -12
- package/dist/tools/tool-selector.js +3 -59
- package/dist/utils/config-validation/schema.d.ts +40 -7
- package/dist/utils/config-validation/schema.js +130 -1
- package/dist/utils/cost-tracker.js +21 -1
- package/dist/utils/disk-guard.d.ts +180 -0
- package/dist/utils/disk-guard.js +404 -0
- package/dist/utils/interactive-setup.js +35 -2
- package/dist/utils/model-utils.js +11 -2
- package/dist/utils/output-sanitizer.js +11 -0
- package/dist/utils/provider-detector.d.ts +8 -7
- package/dist/utils/provider-detector.js +19 -77
- package/dist/utils/settings-manager.d.ts +7 -0
- package/dist/utils/token-display.d.ts +3 -3
- package/dist/utils/token-display.js +22 -4
- package/dist/wizard/onboarding.d.ts +39 -0
- package/dist/wizard/onboarding.js +433 -21
- package/dist/wizard/provider-onboarding.d.ts +8 -23
- package/dist/wizard/provider-onboarding.js +39 -158
- package/package.json +4 -2
- package/dist/plugins/provider-onboarding.d.ts +0 -23
- package/dist/plugins/provider-onboarding.js +0 -116
- package/dist/utils/kokoro-tts.d.ts +0 -8
- package/dist/utils/kokoro-tts.js +0 -67
|
@@ -9,6 +9,16 @@ import { buildHeartbeatStatusReport } from '../../daemon/status-reports.js';
|
|
|
9
9
|
// Heartbeat commands
|
|
10
10
|
// ============================================================================
|
|
11
11
|
export { buildHeartbeatStatusReport };
|
|
12
|
+
function parsePositiveIntegerCliOption(value, flagName) {
|
|
13
|
+
if (value === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
const trimmed = value.trim();
|
|
16
|
+
const n = Number(trimmed);
|
|
17
|
+
if (!/^[1-9]\d*$/.test(trimmed) || !Number.isSafeInteger(n)) {
|
|
18
|
+
throw new Error(`${flagName} must be a positive integer`);
|
|
19
|
+
}
|
|
20
|
+
return n;
|
|
21
|
+
}
|
|
12
22
|
export function registerHeartbeatCommands(program) {
|
|
13
23
|
const heartbeat = program
|
|
14
24
|
.command('heartbeat')
|
|
@@ -745,7 +755,7 @@ export function registerFleetAutonomyCommands(program) {
|
|
|
745
755
|
.action(async (opts) => {
|
|
746
756
|
const { createDefaultAutonomousLoop, FleetAutonomousDaemon, watchFleetTasks } = await import('../../daemon/autonomous-daemon.js');
|
|
747
757
|
const path = await import('path');
|
|
748
|
-
const loop = createDefaultAutonomousLoop({
|
|
758
|
+
const loop = await createDefaultAutonomousLoop({
|
|
749
759
|
...(opts.dir ? { dir: opts.dir } : {}),
|
|
750
760
|
...(opts.outputDir ? { outputDir: opts.outputDir } : {}),
|
|
751
761
|
});
|
|
@@ -780,6 +790,86 @@ export function registerFleetAutonomyCommands(program) {
|
|
|
780
790
|
console.log(`\nDone: ${summary.ticks} tick(s), ${JSON.stringify(summary.outcomes)} (${summary.stoppedReason}).`);
|
|
781
791
|
}
|
|
782
792
|
});
|
|
793
|
+
fleet
|
|
794
|
+
.command('bench')
|
|
795
|
+
.description('Benchmark live Tailnet Ollama peers and rank the network model tier')
|
|
796
|
+
.option('--peer <pattern>', 'only benchmark peers whose hostname or IP contains the pattern')
|
|
797
|
+
.option('--models <csv>', 'only benchmark models whose name contains one of the comma-separated patterns')
|
|
798
|
+
.option('--prompt-set <name>', 'prompt set: balanced, coding, or latency', 'coding')
|
|
799
|
+
.option('--runs <n>', 'repeat the prompt set N times per candidate', '1')
|
|
800
|
+
.option('--timeout <ms>', 'request timeout per prompt', '60000')
|
|
801
|
+
.option('--json', 'output JSON')
|
|
802
|
+
.option('--no-write-cache', 'do not persist the score cache used by the router')
|
|
803
|
+
.action(async (opts) => {
|
|
804
|
+
const { TailscaleManager } = await import('../../integrations/tailscale.js');
|
|
805
|
+
const { BENCHMARK_PROMPT_SETS, benchmarkCandidates, writeBenchmarkIndex, defaultBenchmarkIndexPath, } = await import('../../agent/model-benchmark.js');
|
|
806
|
+
const peers = await TailscaleManager.getInstance().discoverOllamaPeers();
|
|
807
|
+
const filteredPeers = opts.peer
|
|
808
|
+
? peers.filter((peer) => peer.hostname.toLowerCase().includes(opts.peer.toLowerCase())
|
|
809
|
+
|| peer.ip.includes(opts.peer.trim()))
|
|
810
|
+
: peers;
|
|
811
|
+
const modelFilters = opts.models
|
|
812
|
+
? opts.models.split(',').map((part) => part.trim()).filter(Boolean)
|
|
813
|
+
: [];
|
|
814
|
+
const candidates = filteredPeers.flatMap((peer) => peer.models
|
|
815
|
+
.filter((model) => modelFilters.length === 0
|
|
816
|
+
? true
|
|
817
|
+
: modelFilters.some((filter) => model.toLowerCase().includes(filter.toLowerCase())))
|
|
818
|
+
.map((model) => ({
|
|
819
|
+
model,
|
|
820
|
+
baseUrl: peer.baseURL,
|
|
821
|
+
label: peer.hostname,
|
|
822
|
+
})));
|
|
823
|
+
if (candidates.length === 0) {
|
|
824
|
+
const message = opts.peer
|
|
825
|
+
? `No Tailnet Ollama peers matched "${opts.peer}".`
|
|
826
|
+
: 'No Tailnet Ollama peers were discovered.';
|
|
827
|
+
if (opts.json) {
|
|
828
|
+
console.log(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
console.log(message);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
const suite = BENCHMARK_PROMPT_SETS[opts.promptSet] ? opts.promptSet : 'coding';
|
|
835
|
+
const reports = await benchmarkCandidates(candidates, {
|
|
836
|
+
promptSet: suite,
|
|
837
|
+
runs: Math.max(1, parseInt(opts.runs, 10) || 1),
|
|
838
|
+
timeoutMs: Math.max(5_000, parseInt(opts.timeout, 10) || 60_000),
|
|
839
|
+
});
|
|
840
|
+
const ranked = [...reports].sort((a, b) => b.summary.score - a.summary.score);
|
|
841
|
+
let indexPath;
|
|
842
|
+
if (opts.writeCache !== false) {
|
|
843
|
+
indexPath = defaultBenchmarkIndexPath();
|
|
844
|
+
await writeBenchmarkIndex(ranked, suite, indexPath);
|
|
845
|
+
}
|
|
846
|
+
const best = ranked[0];
|
|
847
|
+
if (opts.json) {
|
|
848
|
+
console.log(JSON.stringify({
|
|
849
|
+
ok: true,
|
|
850
|
+
suite,
|
|
851
|
+
indexPath,
|
|
852
|
+
best,
|
|
853
|
+
reports: ranked,
|
|
854
|
+
}, null, 2));
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
console.log(`\nTailnet Ollama benchmark (${suite})`);
|
|
858
|
+
if (indexPath)
|
|
859
|
+
console.log(` Cache: ${indexPath}`);
|
|
860
|
+
console.log(` Candidates: ${ranked.length}`);
|
|
861
|
+
if (best) {
|
|
862
|
+
console.log(` Best: ${best.candidate.label ?? best.candidate.model} / ${best.candidate.model}`);
|
|
863
|
+
console.log(` Score: ${best.summary.score.toFixed(1)} compliance ${(best.summary.complianceRate * 100).toFixed(0)}% ttft ${Math.round(best.summary.avgTtftMs)}ms total ${Math.round(best.summary.avgTotalMs)}ms`);
|
|
864
|
+
}
|
|
865
|
+
console.log('');
|
|
866
|
+
ranked.forEach((report, index) => {
|
|
867
|
+
console.log(`${String(index + 1).padStart(2, ' ')}. ${report.candidate.label ?? report.candidate.model} / ${report.candidate.model}`);
|
|
868
|
+
console.log(` ${report.candidate.baseUrl}`);
|
|
869
|
+
console.log(` score=${report.summary.score.toFixed(1)} compliance=${(report.summary.complianceRate * 100).toFixed(0)}% ttft=${Math.round(report.summary.avgTtftMs)}ms total=${Math.round(report.summary.avgTotalMs)}ms`);
|
|
870
|
+
});
|
|
871
|
+
console.log('');
|
|
872
|
+
});
|
|
783
873
|
fleet
|
|
784
874
|
.command('status')
|
|
785
875
|
.description('Show the fleet task queue + presence')
|
|
@@ -822,6 +912,15 @@ export function registerFleetAutonomyCommands(program) {
|
|
|
822
912
|
.option('--dir <path>', 'colab dir')
|
|
823
913
|
.option('--json', 'output JSON')
|
|
824
914
|
.action(async (title, opts) => {
|
|
915
|
+
let goalMaxTurns;
|
|
916
|
+
try {
|
|
917
|
+
goalMaxTurns = parsePositiveIntegerCliOption(opts.goalMaxTurns, '--goal-max-turns');
|
|
918
|
+
}
|
|
919
|
+
catch (error) {
|
|
920
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
921
|
+
process.exit(1);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
825
924
|
const { FleetColabStore } = await import('../../fleet/colab-store.js');
|
|
826
925
|
const store = new FleetColabStore({ ...(opts.dir ? { dir: opts.dir } : {}) });
|
|
827
926
|
const priority = ['critical', 'high', 'medium', 'low'].find((p) => p === opts.priority) ?? 'medium';
|
|
@@ -831,7 +930,7 @@ export function registerFleetAutonomyCommands(program) {
|
|
|
831
930
|
...(opts.description ? { description: opts.description } : {}),
|
|
832
931
|
...(opts.dependsOn ? { dependsOn: opts.dependsOn.split(',').map((s) => s.trim()).filter(Boolean) } : {}),
|
|
833
932
|
...(opts.goalMode ? { goalMode: true } : {}),
|
|
834
|
-
...(
|
|
933
|
+
...(goalMaxTurns !== undefined ? { goalMaxTurns } : {}),
|
|
835
934
|
});
|
|
836
935
|
if (opts.json) {
|
|
837
936
|
console.log(JSON.stringify({ task }, null, 2));
|
|
@@ -1129,7 +1228,7 @@ export function registerCompanionCommands(program) {
|
|
|
1129
1228
|
.option('--no-set-model', 'Do not set the project model to the ChatGPT companion default')
|
|
1130
1229
|
.option('--language <lang>', 'Voice language', 'fr')
|
|
1131
1230
|
.option('--stt-provider <provider>', 'Voice input provider (system, whisper-local, whisper-api)')
|
|
1132
|
-
.option('--tts-provider <provider>', 'TTS provider (edge-tts, espeak, say, piper, audioreader
|
|
1231
|
+
.option('--tts-provider <provider>', 'TTS provider (edge-tts, espeak, say, piper, audioreader)')
|
|
1133
1232
|
.option('--tts-voice <voice>', 'TTS voice name')
|
|
1134
1233
|
.option('--model <model>', 'ChatGPT model to use when OAuth credentials are present')
|
|
1135
1234
|
.action(async (opts) => {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Extracted from index.ts for modularity.
|
|
5
5
|
*/
|
|
6
6
|
import { resolve } from 'path';
|
|
7
|
+
import { buildOllamaUpdatePlan, fetchOllamaStatus, runOllamaUpdatePlan } from '../ollama.js';
|
|
7
8
|
function resolveCommandDirectory(program) {
|
|
8
9
|
const directory = program.opts().directory;
|
|
9
10
|
return resolve(process.cwd(), directory || process.cwd());
|
|
@@ -93,6 +94,50 @@ export function registerUtilityCommands(program) {
|
|
|
93
94
|
const { runOnboarding } = await import('../../wizard/onboarding.js');
|
|
94
95
|
await runOnboarding();
|
|
95
96
|
});
|
|
97
|
+
const ollamaCommand = program
|
|
98
|
+
.command('ollama')
|
|
99
|
+
.description('Inspect or update the local Ollama runtime');
|
|
100
|
+
ollamaCommand
|
|
101
|
+
.command('status')
|
|
102
|
+
.description('Show local Ollama version and models')
|
|
103
|
+
.option('--url <url>', 'Ollama base URL', process.env.OLLAMA_HOST ?? 'http://localhost:11434')
|
|
104
|
+
.option('--json', 'Output JSON')
|
|
105
|
+
.action(async (options) => {
|
|
106
|
+
const status = await fetchOllamaStatus(options.url);
|
|
107
|
+
if (options.json) {
|
|
108
|
+
console.log(JSON.stringify(status, null, 2));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
console.log('\nOllama status\n');
|
|
112
|
+
console.log(` Base URL: ${status.baseUrl}`);
|
|
113
|
+
console.log(` Reachable: ${status.reachable ? 'YES' : 'NO'}`);
|
|
114
|
+
console.log(` Version: ${status.version ?? '(unknown)'}`);
|
|
115
|
+
console.log(` Models: ${status.models.length > 0 ? status.models.join(', ') : '(none)'}`);
|
|
116
|
+
if (status.error) {
|
|
117
|
+
console.log(` Error: ${status.error}`);
|
|
118
|
+
}
|
|
119
|
+
console.log('');
|
|
120
|
+
});
|
|
121
|
+
ollamaCommand
|
|
122
|
+
.command('update')
|
|
123
|
+
.description('Run the official Ollama Windows update script')
|
|
124
|
+
.option('--script-url <url>', 'Official installer/update script URL', 'https://ollama.com/install.ps1')
|
|
125
|
+
.option('--repo-root <dir>', 'Repository root', resolveCommandDirectory(program))
|
|
126
|
+
.action(async (options) => {
|
|
127
|
+
const plan = buildOllamaUpdatePlan({
|
|
128
|
+
repoRoot: options.repoRoot,
|
|
129
|
+
scriptUrl: options.scriptUrl,
|
|
130
|
+
});
|
|
131
|
+
if (!plan.supported) {
|
|
132
|
+
console.error(plan.message);
|
|
133
|
+
console.error(`Use the update script directly on Windows: ${plan.scriptPath}`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
console.log(`Running Ollama updater: ${plan.scriptPath}`);
|
|
138
|
+
await runOllamaUpdatePlan(plan);
|
|
139
|
+
console.log('Ollama update completed.');
|
|
140
|
+
});
|
|
96
141
|
// Webhook command
|
|
97
142
|
const webhookCommand = program
|
|
98
143
|
.command('webhook')
|
|
@@ -340,7 +340,7 @@ export class EnhancedCommandHandler {
|
|
|
340
340
|
// Heartbeat engine activation (fleet AUTONOMOUS-FLEET-PROTOCOL v0.1)
|
|
341
341
|
['__HEARTBEAT__', (args) => handleHeartbeat(args)],
|
|
342
342
|
// Standing goal — judge + auto-continue loop (Hermes Agent parity)
|
|
343
|
-
['__GOAL__', (args) => handleGoal(args)],
|
|
343
|
+
['__GOAL__', (args) => handleGoal(args, { client: this.codebuddyClient })],
|
|
344
344
|
['__SUBGOAL__', (args) => handleSubgoal(args)],
|
|
345
345
|
// Daily reset scheduler (audit OpenClaw heritage activation)
|
|
346
346
|
['__DAILY_RESET__', (args) => handleDailyReset(args)],
|
|
@@ -15,14 +15,22 @@
|
|
|
15
15
|
import { Command } from 'commander';
|
|
16
16
|
import type { ChatEntry } from '../agent/codebuddy-agent.js';
|
|
17
17
|
import type { CodeBuddyClient } from '../codebuddy/client.js';
|
|
18
|
+
import { type GoalJudgeProviderInfo } from '../goals/goal-judge-client.js';
|
|
18
19
|
import { GoalStatus } from '../goals/goal-state.js';
|
|
19
20
|
/** The slice of CodeBuddyAgent the loop needs — injectable for tests. */
|
|
20
21
|
export interface GoalLoopAgent {
|
|
21
22
|
processUserMessage(input: string): Promise<ChatEntry[]>;
|
|
22
23
|
getClient(): CodeBuddyClient;
|
|
23
24
|
}
|
|
25
|
+
export interface GoalCommandRuntimeAgent extends GoalLoopAgent {
|
|
26
|
+
systemPromptReady: Promise<unknown>;
|
|
27
|
+
setSystemPrompt?: (prompt: string) => void;
|
|
28
|
+
dispose?: () => void | Promise<void>;
|
|
29
|
+
}
|
|
24
30
|
export interface GoalLoopRunOptions {
|
|
25
31
|
maxTurns?: number;
|
|
32
|
+
/** Optional standalone judge client. Defaults to the agent client. */
|
|
33
|
+
judgeClient?: CodeBuddyClient;
|
|
26
34
|
/** Progress sink (status lines ⊙/↻/✓/⏸). Defaults to silent. */
|
|
27
35
|
onMessage?: (text: string) => void;
|
|
28
36
|
}
|
|
@@ -31,6 +39,19 @@ export interface GoalLoopRunResult {
|
|
|
31
39
|
turnsUsed: number;
|
|
32
40
|
lastReason?: string;
|
|
33
41
|
}
|
|
42
|
+
export interface GoalCommandRuntimeOptions {
|
|
43
|
+
maxTurns?: number;
|
|
44
|
+
judgeModel?: string;
|
|
45
|
+
provider: GoalJudgeProviderInfo;
|
|
46
|
+
workingDirectory?: string;
|
|
47
|
+
onMessage?: (text: string) => void;
|
|
48
|
+
createJudgeClient?: typeof createStandaloneJudgeClient;
|
|
49
|
+
runLoop?: typeof runGoalLoop;
|
|
50
|
+
}
|
|
51
|
+
export declare const LOCAL_GOAL_ACTOR_SYSTEM_PROMPT: string;
|
|
52
|
+
export declare function buildLocalGoalActorSystemPrompt(workingDirectory?: string): string;
|
|
53
|
+
export declare function shouldUseLocalGoalActorPrompt(provider: GoalJudgeProviderInfo): boolean;
|
|
54
|
+
export declare function resolveLocalGoalActorSystemPrompt(provider: GoalJudgeProviderInfo, workingDirectory?: string): string | undefined;
|
|
34
55
|
/**
|
|
35
56
|
* Drive the goal loop headlessly on an in-process agent. Sets the goal,
|
|
36
57
|
* runs the first turn with the goal text (mirroring the interactive
|
|
@@ -38,4 +59,13 @@ export interface GoalLoopRunResult {
|
|
|
38
59
|
* stops continuing.
|
|
39
60
|
*/
|
|
40
61
|
export declare function runGoalLoop(agent: GoalLoopAgent, goalText: string, options?: GoalLoopRunOptions): Promise<GoalLoopRunResult>;
|
|
62
|
+
declare function createStandaloneJudgeClient(judgeModel: string | undefined, agentProvider: GoalJudgeProviderInfo, currentClient: CodeBuddyClient): Promise<CodeBuddyClient | undefined>;
|
|
63
|
+
export declare function runGoalCommandWithAgent(agent: GoalCommandRuntimeAgent, goal: string, options: GoalCommandRuntimeOptions): Promise<GoalLoopRunResult>;
|
|
64
|
+
export declare function resolveGoalCliJudgeModel(explicitJudgeModel: string | undefined): string | undefined;
|
|
65
|
+
export declare function resolveGoalCliMaxToolRounds(commandValue: number | undefined, command?: Command): number;
|
|
66
|
+
export declare function resolveGoalCliWorkingDirectory(command?: Command): string | undefined;
|
|
67
|
+
export declare function applyGoalCliWorkingDirectory(command?: Command): string;
|
|
68
|
+
export declare function parsePositiveIntegerOption(value: string, optionName: string): number;
|
|
69
|
+
export declare function validateGoalCommandNumericOptions(argv: readonly string[]): void;
|
|
41
70
|
export declare function createGoalCommand(): Command;
|
|
71
|
+
export {};
|
|
@@ -12,10 +12,72 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Exit codes: 0 = goal done, 1 = paused (budget/judge) or error.
|
|
14
14
|
*/
|
|
15
|
-
import { Command } from 'commander';
|
|
15
|
+
import { Command, InvalidArgumentError } from 'commander';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { resolveGoalJudgeClient } from '../goals/goal-judge-client.js';
|
|
16
18
|
import { maybeContinueGoalAfterTurn } from '../goals/goal-loop.js';
|
|
17
|
-
import { getGoalManager } from '../goals/goal-manager.js';
|
|
19
|
+
import { getGoalManager, resolveGoalsConfig } from '../goals/goal-manager.js';
|
|
18
20
|
import { resolveCommandProvider } from './llm-provider-resolution.js';
|
|
21
|
+
const TOOL_RESULT_SNIPPET_CHARS = 1600;
|
|
22
|
+
export const LOCAL_GOAL_ACTOR_SYSTEM_PROMPT = 'You are Code Buddy running in headless goal mode on a local model. ' +
|
|
23
|
+
'Work only toward the user standing goal. Use available tools when they are necessary, ' +
|
|
24
|
+
'then report concrete progress or the final result. For simple answer-only goals, ' +
|
|
25
|
+
'output exactly the requested answer and no extra text. Do not greet the user, add markdown, ' +
|
|
26
|
+
'or explain unless the goal asks for it.';
|
|
27
|
+
export function buildLocalGoalActorSystemPrompt(workingDirectory = process.cwd()) {
|
|
28
|
+
return `${LOCAL_GOAL_ACTOR_SYSTEM_PROMPT}
|
|
29
|
+
|
|
30
|
+
Workspace:
|
|
31
|
+
- Current project directory: ${workingDirectory}
|
|
32
|
+
- Use paths relative to the current project directory whenever possible.
|
|
33
|
+
- Do not invent container paths such as /workspace or root paths such as /src unless a tool result proves they exist.`;
|
|
34
|
+
}
|
|
35
|
+
export function shouldUseLocalGoalActorPrompt(provider) {
|
|
36
|
+
const label = provider.providerLabel?.toLowerCase() ?? '';
|
|
37
|
+
const baseURL = provider.baseURL?.toLowerCase() ?? '';
|
|
38
|
+
return provider.apiKey === 'ollama' || label.includes('ollama') || baseURL.includes(':11434');
|
|
39
|
+
}
|
|
40
|
+
export function resolveLocalGoalActorSystemPrompt(provider, workingDirectory = process.cwd()) {
|
|
41
|
+
return shouldUseLocalGoalActorPrompt(provider)
|
|
42
|
+
? buildLocalGoalActorSystemPrompt(workingDirectory)
|
|
43
|
+
: undefined;
|
|
44
|
+
}
|
|
45
|
+
function truncateGoalTurnPart(text) {
|
|
46
|
+
if (text.length <= TOOL_RESULT_SNIPPET_CHARS)
|
|
47
|
+
return text;
|
|
48
|
+
return `${text.slice(0, TOOL_RESULT_SNIPPET_CHARS)}... [truncated]`;
|
|
49
|
+
}
|
|
50
|
+
function buildGoalTurnSummary(entries) {
|
|
51
|
+
const parts = [];
|
|
52
|
+
let hasToolResult = false;
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
if (entry.type === 'assistant' && entry.content.trim()) {
|
|
55
|
+
parts.push(entry.content.trim());
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (entry.type !== 'tool_result')
|
|
59
|
+
continue;
|
|
60
|
+
hasToolResult = true;
|
|
61
|
+
const toolOutput = (entry.toolResult?.success ? entry.toolResult.output : entry.toolResult?.error) ??
|
|
62
|
+
entry.content;
|
|
63
|
+
const trimmedOutput = String(toolOutput ?? '').trim();
|
|
64
|
+
if (!trimmedOutput)
|
|
65
|
+
continue;
|
|
66
|
+
const toolName = entry.toolCall?.function?.name ?? 'tool';
|
|
67
|
+
const status = entry.toolResult ? (entry.toolResult.success ? 'success' : 'error') : 'result';
|
|
68
|
+
parts.push(`[tool:${toolName} ${status}]\n${truncateGoalTurnPart(trimmedOutput)}`);
|
|
69
|
+
}
|
|
70
|
+
const summary = parts.join('\n\n');
|
|
71
|
+
if (summary && !hasToolResult) {
|
|
72
|
+
return `[tool evidence: none]\n\n${summary}`;
|
|
73
|
+
}
|
|
74
|
+
return summary;
|
|
75
|
+
}
|
|
76
|
+
function buildGoalRunSummary(turnSummaries) {
|
|
77
|
+
return turnSummaries
|
|
78
|
+
.map((summary, index) => `[Goal turn ${index + 1}]\n${summary}`)
|
|
79
|
+
.join('\n\n');
|
|
80
|
+
}
|
|
19
81
|
/**
|
|
20
82
|
* Drive the goal loop headlessly on an in-process agent. Sets the goal,
|
|
21
83
|
* runs the first turn with the goal text (mirroring the interactive
|
|
@@ -24,25 +86,32 @@ import { resolveCommandProvider } from './llm-provider-resolution.js';
|
|
|
24
86
|
*/
|
|
25
87
|
export async function runGoalLoop(agent, goalText, options = {}) {
|
|
26
88
|
const manager = getGoalManager();
|
|
27
|
-
const state = manager.set(goalText, options.maxTurns ? { maxTurns: options.maxTurns } : {});
|
|
89
|
+
const state = manager.set(goalText, options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {});
|
|
28
90
|
const emit = options.onMessage ?? (() => { });
|
|
91
|
+
const judgeClient = options.judgeClient ?? agent.getClient();
|
|
29
92
|
emit(`⊙ Goal set (${state.maxTurns}-turn budget): ${state.goal}`);
|
|
30
93
|
let prompt = state.goal;
|
|
94
|
+
const turnSummaries = [];
|
|
31
95
|
// Hard backstop on top of the manager's own budget/auto-pause guards.
|
|
32
96
|
const maxIterations = state.maxTurns + 1;
|
|
33
97
|
for (let i = 0; i < maxIterations; i++) {
|
|
34
98
|
const entries = await agent.processUserMessage(prompt);
|
|
35
|
-
const lastResponse = entries
|
|
36
|
-
|
|
37
|
-
.
|
|
38
|
-
|
|
99
|
+
const lastResponse = buildGoalTurnSummary(entries);
|
|
100
|
+
if (lastResponse.trim()) {
|
|
101
|
+
turnSummaries.push(lastResponse);
|
|
102
|
+
}
|
|
39
103
|
const outcome = await maybeContinueGoalAfterTurn({
|
|
40
|
-
client:
|
|
41
|
-
lastResponse,
|
|
104
|
+
client: judgeClient,
|
|
105
|
+
lastResponse: lastResponse.trim() ? buildGoalRunSummary(turnSummaries) : lastResponse,
|
|
42
106
|
interrupted: false,
|
|
43
107
|
});
|
|
44
108
|
if (outcome?.message)
|
|
45
109
|
emit(outcome.message);
|
|
110
|
+
if (!outcome && !lastResponse.trim() && manager.isActive()) {
|
|
111
|
+
manager.pause('empty response (nothing to evaluate)');
|
|
112
|
+
emit('⏸ Goal paused — the agent produced no judgeable response.');
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
46
115
|
if (!outcome?.continuationPrompt)
|
|
47
116
|
break;
|
|
48
117
|
prompt = outcome.continuationPrompt;
|
|
@@ -54,37 +123,151 @@ export async function runGoalLoop(agent, goalText, options = {}) {
|
|
|
54
123
|
...(final?.lastReason ? { lastReason: final.lastReason } : {}),
|
|
55
124
|
};
|
|
56
125
|
}
|
|
126
|
+
async function createStandaloneJudgeClient(judgeModel, agentProvider, currentClient) {
|
|
127
|
+
const resolved = await resolveGoalJudgeClient(currentClient, judgeModel, agentProvider);
|
|
128
|
+
return resolved === currentClient ? undefined : resolved;
|
|
129
|
+
}
|
|
130
|
+
export async function runGoalCommandWithAgent(agent, goal, options) {
|
|
131
|
+
try {
|
|
132
|
+
const createJudgeClient = options.createJudgeClient ?? createStandaloneJudgeClient;
|
|
133
|
+
const runLoop = options.runLoop ?? runGoalLoop;
|
|
134
|
+
const judgeClient = await createJudgeClient(options.judgeModel, options.provider, agent.getClient());
|
|
135
|
+
await agent.systemPromptReady;
|
|
136
|
+
if (shouldUseLocalGoalActorPrompt(options.provider)) {
|
|
137
|
+
agent.setSystemPrompt?.(buildLocalGoalActorSystemPrompt(options.workingDirectory));
|
|
138
|
+
}
|
|
139
|
+
return await runLoop(agent, goal, {
|
|
140
|
+
...(options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {}),
|
|
141
|
+
...(judgeClient ? { judgeClient } : {}),
|
|
142
|
+
...(options.onMessage ? { onMessage: options.onMessage } : {}),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
await Promise.resolve(agent.dispose?.());
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export function resolveGoalCliJudgeModel(explicitJudgeModel) {
|
|
150
|
+
const explicit = explicitJudgeModel?.trim();
|
|
151
|
+
if (explicit)
|
|
152
|
+
return explicit;
|
|
153
|
+
const configured = resolveGoalsConfig().judgeModel.trim();
|
|
154
|
+
return configured || undefined;
|
|
155
|
+
}
|
|
156
|
+
export function resolveGoalCliMaxToolRounds(commandValue, command) {
|
|
157
|
+
const commandSource = command?.getOptionValueSource?.('maxToolRounds');
|
|
158
|
+
if (commandSource && commandSource !== 'default') {
|
|
159
|
+
return commandValue ?? 50;
|
|
160
|
+
}
|
|
161
|
+
const parentSource = command?.parent?.getOptionValueSource?.('maxToolRounds');
|
|
162
|
+
const parentValue = command?.optsWithGlobals?.()?.maxToolRounds;
|
|
163
|
+
if (parentSource && parentSource !== 'default' && parentValue !== undefined) {
|
|
164
|
+
return parsePositiveIntegerOption(String(parentValue), '--max-tool-rounds');
|
|
165
|
+
}
|
|
166
|
+
return commandValue ?? 50;
|
|
167
|
+
}
|
|
168
|
+
export function resolveGoalCliWorkingDirectory(command) {
|
|
169
|
+
const value = command?.optsWithGlobals?.()?.directory;
|
|
170
|
+
if (typeof value !== 'string')
|
|
171
|
+
return undefined;
|
|
172
|
+
const directory = value.trim();
|
|
173
|
+
if (!directory)
|
|
174
|
+
return undefined;
|
|
175
|
+
return path.resolve(process.cwd(), directory);
|
|
176
|
+
}
|
|
177
|
+
export function applyGoalCliWorkingDirectory(command) {
|
|
178
|
+
const directory = resolveGoalCliWorkingDirectory(command);
|
|
179
|
+
if (directory) {
|
|
180
|
+
process.chdir(directory);
|
|
181
|
+
}
|
|
182
|
+
return process.cwd();
|
|
183
|
+
}
|
|
184
|
+
async function loadGoalCliEnv(directory) {
|
|
185
|
+
const dotenv = await import('dotenv');
|
|
186
|
+
dotenv.config({ path: path.join(directory, '.env') });
|
|
187
|
+
}
|
|
188
|
+
async function prepareGoalCliWorkspace(command) {
|
|
189
|
+
const launchDirectory = process.cwd();
|
|
190
|
+
await loadGoalCliEnv(launchDirectory);
|
|
191
|
+
const cwd = applyGoalCliWorkingDirectory(command);
|
|
192
|
+
if (cwd !== launchDirectory) {
|
|
193
|
+
await loadGoalCliEnv(cwd);
|
|
194
|
+
}
|
|
195
|
+
const opts = command?.optsWithGlobals?.();
|
|
196
|
+
const { initializeWorkspaceIsolation } = await import('../workspace/workspace-isolation.js');
|
|
197
|
+
initializeWorkspaceIsolation({
|
|
198
|
+
allowOutside: opts?.allowOutside,
|
|
199
|
+
directory: cwd,
|
|
200
|
+
additionalPaths: opts?.addDir,
|
|
201
|
+
});
|
|
202
|
+
return cwd;
|
|
203
|
+
}
|
|
204
|
+
export function parsePositiveIntegerOption(value, optionName) {
|
|
205
|
+
const trimmed = value.trim();
|
|
206
|
+
const n = Number(trimmed);
|
|
207
|
+
if (!/^[1-9]\d*$/.test(trimmed) || !Number.isSafeInteger(n)) {
|
|
208
|
+
throw new InvalidArgumentError(`${optionName} must be a positive integer`);
|
|
209
|
+
}
|
|
210
|
+
return n;
|
|
211
|
+
}
|
|
212
|
+
export function validateGoalCommandNumericOptions(argv) {
|
|
213
|
+
for (let i = 2; i < argv.length; i++) {
|
|
214
|
+
const arg = argv[i];
|
|
215
|
+
if (arg === undefined)
|
|
216
|
+
continue;
|
|
217
|
+
if (arg === '--')
|
|
218
|
+
break;
|
|
219
|
+
if (arg === '--max-turns' || arg === '--max-tool-rounds') {
|
|
220
|
+
const value = argv[i + 1];
|
|
221
|
+
if (value === undefined)
|
|
222
|
+
continue;
|
|
223
|
+
parsePositiveIntegerOption(value, arg);
|
|
224
|
+
i++;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const eqIndex = arg?.indexOf('=') ?? -1;
|
|
228
|
+
if (eqIndex <= 0)
|
|
229
|
+
continue;
|
|
230
|
+
const optionName = arg.slice(0, eqIndex);
|
|
231
|
+
if (optionName === '--max-turns' || optionName === '--max-tool-rounds') {
|
|
232
|
+
parsePositiveIntegerOption(arg.slice(eqIndex + 1), optionName);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
57
236
|
export function createGoalCommand() {
|
|
58
237
|
const cmd = new Command('goal')
|
|
59
238
|
.description('Run the agent toward a standing goal until a judge model confirms it is done (Ralph loop)')
|
|
60
239
|
.argument('<goal>', 'The goal to pursue')
|
|
61
|
-
.option('--max-turns <n>', 'Turn budget (default 20, or goals.maxTurns from settings)')
|
|
240
|
+
.option('--max-turns <n>', 'Turn budget (default 20, or goals.maxTurns from settings)', value => parsePositiveIntegerOption(value, '--max-turns'))
|
|
62
241
|
.option('--judge-model <model>', 'Model for the goal judge (default: session model)')
|
|
63
242
|
.option('-m, --model <model>', 'Override the agent model for this run')
|
|
64
|
-
.option('--max-tool-rounds <n>', 'Max tool rounds per turn', '
|
|
243
|
+
.option('--max-tool-rounds <n>', 'Max tool rounds per turn', value => parsePositiveIntegerOption(value, '--max-tool-rounds'), 50)
|
|
65
244
|
.action(async (goal, options, command) => {
|
|
66
|
-
const modelOverride = options.model ?? command?.optsWithGlobals?.()?.model;
|
|
67
|
-
const resolved = resolveCommandProvider({ explicitModel: modelOverride });
|
|
68
|
-
if (!resolved) {
|
|
69
|
-
console.error('Error: No provider available — set an API key, run `buddy onboard`, or point CODEBUDDY_PROVIDER=ollama at a local Ollama.');
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
if (options.judgeModel) {
|
|
73
|
-
process.env.CODEBUDDY_GOAL_JUDGE_MODEL = options.judgeModel;
|
|
74
|
-
}
|
|
75
|
-
process.env.CODEBUDDY_DISABLE_MCP = process.env.CODEBUDDY_DISABLE_MCP ?? 'true';
|
|
76
|
-
process.env.CODEBUDDY_HEADLESS = 'true';
|
|
77
245
|
try {
|
|
246
|
+
const cwd = await prepareGoalCliWorkspace(command);
|
|
247
|
+
const modelOverride = options.model ?? command?.optsWithGlobals?.()?.model;
|
|
248
|
+
const resolved = resolveCommandProvider({ explicitModel: modelOverride });
|
|
249
|
+
if (!resolved) {
|
|
250
|
+
console.error('Error: No provider available — set an API key, run `buddy onboard`, or point CODEBUDDY_PROVIDER=ollama at a local Ollama.');
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
if (options.judgeModel) {
|
|
254
|
+
process.env.CODEBUDDY_GOAL_JUDGE_MODEL = options.judgeModel;
|
|
255
|
+
}
|
|
256
|
+
const judgeModel = resolveGoalCliJudgeModel(options.judgeModel);
|
|
257
|
+
const maxToolRounds = resolveGoalCliMaxToolRounds(options.maxToolRounds, command);
|
|
258
|
+
process.env.CODEBUDDY_DISABLE_MCP = process.env.CODEBUDDY_DISABLE_MCP ?? 'true';
|
|
259
|
+
process.env.CODEBUDDY_HEADLESS = 'true';
|
|
78
260
|
const { CodeBuddyAgent } = await import('../agent/codebuddy-agent.js');
|
|
79
261
|
const { ConfirmationService } = await import('../utils/confirmation-service.js');
|
|
80
262
|
ConfirmationService.getInstance().setSessionFlag('allOperations', true);
|
|
81
|
-
const agent = new CodeBuddyAgent(resolved.apiKey, resolved.baseURL, resolved.model,
|
|
82
|
-
await agent
|
|
83
|
-
|
|
84
|
-
|
|
263
|
+
const agent = new CodeBuddyAgent(resolved.apiKey, resolved.baseURL, resolved.model, maxToolRounds, true, undefined, cwd, undefined, resolveLocalGoalActorSystemPrompt(resolved, cwd));
|
|
264
|
+
const result = await runGoalCommandWithAgent(agent, goal, {
|
|
265
|
+
provider: resolved,
|
|
266
|
+
judgeModel,
|
|
267
|
+
workingDirectory: cwd,
|
|
268
|
+
...(options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {}),
|
|
85
269
|
onMessage: text => console.log(`\n${text}`),
|
|
86
270
|
});
|
|
87
|
-
agent.dispose?.();
|
|
88
271
|
process.exit(result.status === 'done' ? 0 : 1);
|
|
89
272
|
}
|
|
90
273
|
catch (err) {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import type { CodeBuddyClient } from '../../codebuddy/client.js';
|
|
2
|
+
import { type GoalPlan } from '../../goals/goal-decomposer.js';
|
|
1
3
|
import { CommandHandlerResult } from './branch-handlers.js';
|
|
4
|
+
export interface GoalHandlerOptions {
|
|
5
|
+
sessionKey?: string;
|
|
6
|
+
client?: CodeBuddyClient | null;
|
|
7
|
+
planner?: (goal: string, client: CodeBuddyClient) => Promise<GoalPlan | null>;
|
|
8
|
+
}
|
|
2
9
|
/**
|
|
3
10
|
* /goal — standing goal with judge + auto-continue loop (the Ralph loop,
|
|
4
11
|
* ported from Hermes Agent).
|
|
@@ -10,7 +17,7 @@ import { CommandHandlerResult } from './branch-handlers.js';
|
|
|
10
17
|
* /goal resume restart the loop (resets the turn budget)
|
|
11
18
|
* /goal clear discard the goal (aliases: stop, done)
|
|
12
19
|
*/
|
|
13
|
-
export declare function handleGoal(args: string[]): Promise<CommandHandlerResult>;
|
|
20
|
+
export declare function handleGoal(args: string[], options?: GoalHandlerOptions): Promise<CommandHandlerResult>;
|
|
14
21
|
/**
|
|
15
22
|
* /subgoal — extra acceptance criteria added mid-loop.
|
|
16
23
|
*
|
|
@@ -24,4 +31,4 @@ export declare function handleGoal(args: string[]): Promise<CommandHandlerResult
|
|
|
24
31
|
* them) and the continuation prompt (agent sees them) on the next turn
|
|
25
32
|
* boundary — no special kick needed.
|
|
26
33
|
*/
|
|
27
|
-
export declare function handleSubgoal(args: string[]): Promise<CommandHandlerResult>;
|
|
34
|
+
export declare function handleSubgoal(args: string[], options?: GoalHandlerOptions): Promise<CommandHandlerResult>;
|