@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.
- package/README.md +160 -164
- 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 +101 -13
- 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/lesson-auto-proposer.js +10 -0
- package/dist/agent/middleware/index.d.ts +1 -0
- package/dist/agent/middleware/index.js +1 -0
- package/dist/agent/middleware/session-duration.d.ts +36 -0
- package/dist/agent/middleware/session-duration.js +78 -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 +71 -0
- package/dist/agent/session-end-flush.js +233 -0
- 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 +107 -3
- package/dist/commands/cli/utility-commands.js +45 -0
- package/dist/commands/enhanced-command-handler.js +5 -0
- package/dist/commands/goal-cli.d.ts +71 -0
- package/dist/commands/goal-cli.js +280 -0
- package/dist/commands/handlers/goal-handler.d.ts +34 -0
- package/dist/commands/handlers/goal-handler.js +155 -0
- package/dist/commands/handlers/index.d.ts +1 -0
- package/dist/commands/handlers/index.js +2 -0
- 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/slash/builtin-commands.js +20 -0
- 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 +770 -0
- package/dist/config/feature-flags.js +7 -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.d.ts +39 -0
- package/dist/context/context-manager-v2.js +91 -0
- package/dist/daemon/agent-task-executor.js +12 -1
- package/dist/daemon/autonomous-daemon.d.ts +1 -1
- package/dist/daemon/autonomous-daemon.js +5 -3
- package/dist/daemon/autonomous-loop.d.ts +21 -1
- package/dist/daemon/autonomous-loop.js +69 -0
- package/dist/daemon/colab-goal.d.ts +38 -0
- package/dist/daemon/colab-goal.js +81 -0
- 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.d.ts +21 -0
- package/dist/fleet/colab-store.js +28 -0
- package/dist/fleet/peer-session-bridge.d.ts +1 -1
- package/dist/fleet/peer-session-bridge.js +243 -2
- package/dist/fleet/peer-session-store.d.ts +3 -0
- package/dist/fleet/peer-tool-bridge.js +14 -0
- package/dist/fleet/privacy-lint.d.ts +8 -0
- package/dist/fleet/privacy-lint.js +22 -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 +38 -0
- package/dist/goals/goal-judge.js +158 -0
- package/dist/goals/goal-loop.d.ts +37 -0
- package/dist/goals/goal-loop.js +101 -0
- package/dist/goals/goal-manager.d.ts +77 -0
- package/dist/goals/goal-manager.js +289 -0
- package/dist/goals/goal-state.d.ts +99 -0
- package/dist/goals/goal-state.js +315 -0
- package/dist/goals/goal-store.d.ts +28 -0
- package/dist/goals/goal-store.js +87 -0
- package/dist/goals/index.d.ts +5 -0
- package/dist/goals/index.js +6 -0
- package/dist/hooks/use-input-handler.js +36 -1
- package/dist/hooks/user-hooks.js +17 -3
- package/dist/index.js +506 -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/observability/run-store.d.ts +1 -1
- 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.d.ts +13 -1
- package/dist/server/websocket/fleet-bridge.js +16 -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
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GoalManager — per-session goal state + continuation decisions.
|
|
3
|
+
*
|
|
4
|
+
* The interactive UI holds one GoalManager per session key (lazily resolved
|
|
5
|
+
* on every operation, so `--resume`/`--continue` reattach automatically and
|
|
6
|
+
* sessionless runs stay durable via a cwd-derived key).
|
|
7
|
+
*
|
|
8
|
+
* Network-free by design: the judge is injected into `evaluateAfterTurn` so
|
|
9
|
+
* the manager (and its tests) never touch a provider.
|
|
10
|
+
*/
|
|
11
|
+
import { GoalJudgeFn } from './goal-judge.js';
|
|
12
|
+
import type { GoalPlan } from './goal-decomposer.js';
|
|
13
|
+
import { GoalState, GoalStatus, GoalVerdict } from './goal-state.js';
|
|
14
|
+
import { GoalStore } from './goal-store.js';
|
|
15
|
+
export interface GoalTurnDecision {
|
|
16
|
+
status: GoalStatus | null;
|
|
17
|
+
shouldContinue: boolean;
|
|
18
|
+
continuationPrompt: string | null;
|
|
19
|
+
verdict: GoalVerdict | 'inactive';
|
|
20
|
+
reason: string;
|
|
21
|
+
/** User-visible one-liner (✓ / ⏸ / ↻). Empty when nothing to show. */
|
|
22
|
+
message: string;
|
|
23
|
+
}
|
|
24
|
+
export interface GoalsConfig {
|
|
25
|
+
maxTurns: number;
|
|
26
|
+
judgeModel: string;
|
|
27
|
+
plannerModel: string;
|
|
28
|
+
judgeMaxTokens: number;
|
|
29
|
+
judgeTimeoutMs: number;
|
|
30
|
+
}
|
|
31
|
+
export declare class GoalManager {
|
|
32
|
+
readonly sessionKey: string;
|
|
33
|
+
private store;
|
|
34
|
+
private defaultMaxTurns;
|
|
35
|
+
private _state;
|
|
36
|
+
constructor(sessionKey: string, store: GoalStore, defaultMaxTurns?: number);
|
|
37
|
+
get state(): GoalState | null;
|
|
38
|
+
isActive(): boolean;
|
|
39
|
+
hasGoal(): boolean;
|
|
40
|
+
statusLine(): string;
|
|
41
|
+
set(goal: string, options?: {
|
|
42
|
+
maxTurns?: number;
|
|
43
|
+
goalPlan?: GoalPlan;
|
|
44
|
+
goalPlanAttempted?: boolean;
|
|
45
|
+
}): GoalState;
|
|
46
|
+
attachGoalPlan(plan: GoalPlan): GoalState | null;
|
|
47
|
+
markGoalPlanAttempted(error?: string): GoalState | null;
|
|
48
|
+
pause(reason?: string): GoalState | null;
|
|
49
|
+
resume(options?: {
|
|
50
|
+
resetBudget?: boolean;
|
|
51
|
+
}): GoalState | null;
|
|
52
|
+
clear(): void;
|
|
53
|
+
markDone(reason: string): void;
|
|
54
|
+
addSubgoal(text: string): string;
|
|
55
|
+
removeSubgoal(index1Based: number): string;
|
|
56
|
+
clearSubgoals(): number;
|
|
57
|
+
renderSubgoals(): string;
|
|
58
|
+
/**
|
|
59
|
+
* Run the judge and update state. Both real user prompts and continuation
|
|
60
|
+
* prompts we fed ourselves increment `turnsUsed` — both consume budget.
|
|
61
|
+
*/
|
|
62
|
+
evaluateAfterTurn(lastResponse: string, deps: {
|
|
63
|
+
judge: GoalJudgeFn;
|
|
64
|
+
}): Promise<GoalTurnDecision>;
|
|
65
|
+
nextContinuationPrompt(): string | null;
|
|
66
|
+
}
|
|
67
|
+
export declare function resolveGoalsConfig(): GoalsConfig;
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the key goals are persisted under. Prefers the live session id
|
|
70
|
+
* (so `--resume`/`--continue` reattach, Hermes `goal:<session_id>`
|
|
71
|
+
* semantics); falls back to a cwd-derived key so sessionless interactive
|
|
72
|
+
* runs are still durable.
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveGoalSessionKey(): string;
|
|
75
|
+
export declare function getGoalManager(sessionKey?: string): GoalManager;
|
|
76
|
+
/** Test seam: clear cached managers and optionally redirect persistence. */
|
|
77
|
+
export declare function resetGoalManagers(store?: GoalStore | null): void;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GoalManager — per-session goal state + continuation decisions.
|
|
3
|
+
*
|
|
4
|
+
* The interactive UI holds one GoalManager per session key (lazily resolved
|
|
5
|
+
* on every operation, so `--resume`/`--continue` reattach automatically and
|
|
6
|
+
* sessionless runs stay durable via a cwd-derived key).
|
|
7
|
+
*
|
|
8
|
+
* Network-free by design: the judge is injected into `evaluateAfterTurn` so
|
|
9
|
+
* the manager (and its tests) never touch a provider.
|
|
10
|
+
*/
|
|
11
|
+
import crypto from 'crypto';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { getSettingsHierarchy } from '../config/settings-hierarchy.js';
|
|
14
|
+
import { getSessionStore } from '../persistence/session-store.js';
|
|
15
|
+
import { logger } from '../utils/logger.js';
|
|
16
|
+
import { DEFAULT_JUDGE_MAX_TOKENS, DEFAULT_JUDGE_TIMEOUT_MS, DEFAULT_MAX_TURNS, applyJudgeOutcome, buildContinuationPrompt, createGoalState, formatGoalStatusLine, getGoalJudgeCriteria, renderSubgoalsBlock, } from './goal-state.js';
|
|
17
|
+
import { GoalStore } from './goal-store.js';
|
|
18
|
+
export class GoalManager {
|
|
19
|
+
sessionKey;
|
|
20
|
+
store;
|
|
21
|
+
defaultMaxTurns;
|
|
22
|
+
_state;
|
|
23
|
+
constructor(sessionKey, store, defaultMaxTurns = DEFAULT_MAX_TURNS) {
|
|
24
|
+
this.sessionKey = sessionKey;
|
|
25
|
+
this.store = store;
|
|
26
|
+
this.defaultMaxTurns = defaultMaxTurns;
|
|
27
|
+
const loaded = this.store.load(sessionKey);
|
|
28
|
+
// A cleared tombstone reads as "no goal" (kept on disk for audit).
|
|
29
|
+
this._state = loaded && loaded.status !== 'cleared' ? loaded : null;
|
|
30
|
+
}
|
|
31
|
+
// --- introspection ------------------------------------------------
|
|
32
|
+
get state() {
|
|
33
|
+
return this._state;
|
|
34
|
+
}
|
|
35
|
+
isActive() {
|
|
36
|
+
return this._state?.status === 'active';
|
|
37
|
+
}
|
|
38
|
+
hasGoal() {
|
|
39
|
+
return this._state !== null && ['active', 'paused'].includes(this._state.status);
|
|
40
|
+
}
|
|
41
|
+
statusLine() {
|
|
42
|
+
return formatGoalStatusLine(this._state);
|
|
43
|
+
}
|
|
44
|
+
// --- mutation -----------------------------------------------------
|
|
45
|
+
set(goal, options = {}) {
|
|
46
|
+
const text = (goal || '').trim();
|
|
47
|
+
if (!text) {
|
|
48
|
+
throw new Error('goal text is empty');
|
|
49
|
+
}
|
|
50
|
+
const state = createGoalState(text, options.maxTurns ?? this.defaultMaxTurns);
|
|
51
|
+
if (options.goalPlan)
|
|
52
|
+
state.goalPlan = options.goalPlan;
|
|
53
|
+
if (typeof options.goalPlanAttempted === 'boolean') {
|
|
54
|
+
state.goalPlanAttempted = options.goalPlanAttempted;
|
|
55
|
+
}
|
|
56
|
+
this._state = state;
|
|
57
|
+
this.store.save(this.sessionKey, state);
|
|
58
|
+
return state;
|
|
59
|
+
}
|
|
60
|
+
attachGoalPlan(plan) {
|
|
61
|
+
if (!this.hasGoal() || !this._state)
|
|
62
|
+
return null;
|
|
63
|
+
this._state.goalPlan = plan;
|
|
64
|
+
this._state.goalPlanAttempted = true;
|
|
65
|
+
delete this._state.goalPlanLastError;
|
|
66
|
+
this.store.save(this.sessionKey, this._state);
|
|
67
|
+
return this._state;
|
|
68
|
+
}
|
|
69
|
+
markGoalPlanAttempted(error) {
|
|
70
|
+
if (!this.hasGoal() || !this._state)
|
|
71
|
+
return null;
|
|
72
|
+
this._state.goalPlanAttempted = true;
|
|
73
|
+
if (error) {
|
|
74
|
+
this._state.goalPlanLastError = error;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
delete this._state.goalPlanLastError;
|
|
78
|
+
}
|
|
79
|
+
this.store.save(this.sessionKey, this._state);
|
|
80
|
+
return this._state;
|
|
81
|
+
}
|
|
82
|
+
pause(reason = 'user-paused') {
|
|
83
|
+
if (!this._state || !['active', 'paused'].includes(this._state.status))
|
|
84
|
+
return null;
|
|
85
|
+
this._state.status = 'paused';
|
|
86
|
+
this._state.pausedReason = reason;
|
|
87
|
+
this.store.save(this.sessionKey, this._state);
|
|
88
|
+
return this._state;
|
|
89
|
+
}
|
|
90
|
+
resume(options = {}) {
|
|
91
|
+
if (!this._state || this._state.status !== 'paused')
|
|
92
|
+
return null;
|
|
93
|
+
this._state.status = 'active';
|
|
94
|
+
delete this._state.pausedReason;
|
|
95
|
+
delete this._state.lastVerdict;
|
|
96
|
+
delete this._state.lastReason;
|
|
97
|
+
this._state.consecutiveParseFailures = 0;
|
|
98
|
+
if (options.resetBudget ?? true) {
|
|
99
|
+
this._state.turnsUsed = 0;
|
|
100
|
+
}
|
|
101
|
+
this.store.save(this.sessionKey, this._state);
|
|
102
|
+
return this._state;
|
|
103
|
+
}
|
|
104
|
+
clear() {
|
|
105
|
+
if (!this._state)
|
|
106
|
+
return;
|
|
107
|
+
this._state.status = 'cleared';
|
|
108
|
+
this.store.save(this.sessionKey, this._state);
|
|
109
|
+
this._state = null;
|
|
110
|
+
}
|
|
111
|
+
markDone(reason) {
|
|
112
|
+
if (!this._state)
|
|
113
|
+
return;
|
|
114
|
+
this._state.status = 'done';
|
|
115
|
+
this._state.lastVerdict = 'done';
|
|
116
|
+
this._state.lastReason = reason;
|
|
117
|
+
this.store.save(this.sessionKey, this._state);
|
|
118
|
+
}
|
|
119
|
+
// --- /subgoal user controls ---------------------------------------
|
|
120
|
+
addSubgoal(text) {
|
|
121
|
+
if (!this.hasGoal() || !this._state) {
|
|
122
|
+
throw new Error('no active goal');
|
|
123
|
+
}
|
|
124
|
+
const clean = (text || '').trim();
|
|
125
|
+
if (!clean) {
|
|
126
|
+
throw new Error('subgoal text is empty');
|
|
127
|
+
}
|
|
128
|
+
this._state.subgoals.push(clean);
|
|
129
|
+
this.store.save(this.sessionKey, this._state);
|
|
130
|
+
return clean;
|
|
131
|
+
}
|
|
132
|
+
removeSubgoal(index1Based) {
|
|
133
|
+
if (!this.hasGoal() || !this._state) {
|
|
134
|
+
throw new Error('no active goal');
|
|
135
|
+
}
|
|
136
|
+
if (!Number.isSafeInteger(index1Based) || index1Based < 1) {
|
|
137
|
+
throw new Error('index must be a positive integer');
|
|
138
|
+
}
|
|
139
|
+
const idx = index1Based - 1;
|
|
140
|
+
if (idx < 0 || idx >= this._state.subgoals.length) {
|
|
141
|
+
throw new Error(`index out of range (1..${this._state.subgoals.length})`);
|
|
142
|
+
}
|
|
143
|
+
const [removed] = this._state.subgoals.splice(idx, 1);
|
|
144
|
+
this.store.save(this.sessionKey, this._state);
|
|
145
|
+
return removed ?? '';
|
|
146
|
+
}
|
|
147
|
+
clearSubgoals() {
|
|
148
|
+
if (!this.hasGoal() || !this._state) {
|
|
149
|
+
throw new Error('no active goal');
|
|
150
|
+
}
|
|
151
|
+
const prev = this._state.subgoals.length;
|
|
152
|
+
this._state.subgoals = [];
|
|
153
|
+
this.store.save(this.sessionKey, this._state);
|
|
154
|
+
return prev;
|
|
155
|
+
}
|
|
156
|
+
renderSubgoals() {
|
|
157
|
+
if (!this._state)
|
|
158
|
+
return '(no active goal)';
|
|
159
|
+
if (!this._state.subgoals.length) {
|
|
160
|
+
return '(no subgoals — use /subgoal <text> to add criteria)';
|
|
161
|
+
}
|
|
162
|
+
return renderSubgoalsBlock(this._state.subgoals);
|
|
163
|
+
}
|
|
164
|
+
// --- the main entry point called after every turn -----------------
|
|
165
|
+
/**
|
|
166
|
+
* Run the judge and update state. Both real user prompts and continuation
|
|
167
|
+
* prompts we fed ourselves increment `turnsUsed` — both consume budget.
|
|
168
|
+
*/
|
|
169
|
+
async evaluateAfterTurn(lastResponse, deps) {
|
|
170
|
+
const state = this._state;
|
|
171
|
+
if (!state || state.status !== 'active') {
|
|
172
|
+
return {
|
|
173
|
+
status: state?.status ?? null,
|
|
174
|
+
shouldContinue: false,
|
|
175
|
+
continuationPrompt: null,
|
|
176
|
+
verdict: 'inactive',
|
|
177
|
+
reason: 'no active goal',
|
|
178
|
+
message: '',
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const criteria = getGoalJudgeCriteria(state);
|
|
182
|
+
const outcome = await deps.judge({
|
|
183
|
+
goal: state.goal,
|
|
184
|
+
lastResponse,
|
|
185
|
+
...(criteria.length ? { subgoals: criteria } : {}),
|
|
186
|
+
});
|
|
187
|
+
const decision = applyJudgeOutcome(state, outcome);
|
|
188
|
+
this.store.save(this.sessionKey, state);
|
|
189
|
+
return decision;
|
|
190
|
+
}
|
|
191
|
+
nextContinuationPrompt() {
|
|
192
|
+
if (!this._state || this._state.status !== 'active')
|
|
193
|
+
return null;
|
|
194
|
+
return buildContinuationPrompt(this._state);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// Config
|
|
199
|
+
// ============================================================================
|
|
200
|
+
export function resolveGoalsConfig() {
|
|
201
|
+
let raw = {};
|
|
202
|
+
try {
|
|
203
|
+
const hierarchy = getSettingsHierarchy(process.cwd());
|
|
204
|
+
hierarchy.loadAllLevels?.();
|
|
205
|
+
const settings = hierarchy.getAllSettings();
|
|
206
|
+
if (settings && typeof settings.goals === 'object' && settings.goals !== null) {
|
|
207
|
+
raw = settings.goals;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
logger.debug('goals: settings hierarchy unavailable, using defaults', { error: String(error) });
|
|
212
|
+
}
|
|
213
|
+
const maxTurns = positiveInt(process.env.CODEBUDDY_GOAL_MAX_TURNS, 0)
|
|
214
|
+
|| positiveInt(raw.maxTurns, DEFAULT_MAX_TURNS);
|
|
215
|
+
const judgeModel = nonEmptyString(process.env.CODEBUDDY_GOAL_JUDGE_MODEL)
|
|
216
|
+
|| nonEmptyString(raw.judgeModel);
|
|
217
|
+
const plannerModel = nonEmptyString(process.env.CODEBUDDY_GOAL_PLANNER_MODEL)
|
|
218
|
+
|| nonEmptyString(raw.plannerModel);
|
|
219
|
+
return {
|
|
220
|
+
maxTurns,
|
|
221
|
+
judgeModel,
|
|
222
|
+
plannerModel,
|
|
223
|
+
judgeMaxTokens: positiveInt(raw.judgeMaxTokens, DEFAULT_JUDGE_MAX_TOKENS),
|
|
224
|
+
judgeTimeoutMs: positiveInt(raw.judgeTimeoutMs, DEFAULT_JUDGE_TIMEOUT_MS),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function positiveInt(value, fallback) {
|
|
228
|
+
const n = parsePositiveSafeInteger(value);
|
|
229
|
+
return Number.isSafeInteger(n) && n > 0 ? n : fallback;
|
|
230
|
+
}
|
|
231
|
+
function nonEmptyString(value) {
|
|
232
|
+
if (typeof value !== 'string')
|
|
233
|
+
return '';
|
|
234
|
+
return value.trim();
|
|
235
|
+
}
|
|
236
|
+
function parsePositiveSafeInteger(value) {
|
|
237
|
+
if (typeof value === 'number')
|
|
238
|
+
return value;
|
|
239
|
+
if (typeof value !== 'string')
|
|
240
|
+
return Number.NaN;
|
|
241
|
+
const trimmed = value.trim();
|
|
242
|
+
if (!/^[1-9]\d*$/.test(trimmed))
|
|
243
|
+
return Number.NaN;
|
|
244
|
+
return Number(trimmed);
|
|
245
|
+
}
|
|
246
|
+
// ============================================================================
|
|
247
|
+
// Singleton registry (one manager per session key)
|
|
248
|
+
// ============================================================================
|
|
249
|
+
const registry = new Map();
|
|
250
|
+
let storeOverride = null;
|
|
251
|
+
/**
|
|
252
|
+
* Resolve the key goals are persisted under. Prefers the live session id
|
|
253
|
+
* (so `--resume`/`--continue` reattach, Hermes `goal:<session_id>`
|
|
254
|
+
* semantics); falls back to a cwd-derived key so sessionless interactive
|
|
255
|
+
* runs are still durable.
|
|
256
|
+
*/
|
|
257
|
+
export function resolveGoalSessionKey() {
|
|
258
|
+
try {
|
|
259
|
+
const sessionId = getSessionStore().getCurrentSessionId();
|
|
260
|
+
if (sessionId)
|
|
261
|
+
return sessionId;
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
logger.debug('goals: session store unavailable, using cwd key', { error: String(error) });
|
|
265
|
+
}
|
|
266
|
+
const cwdHash = crypto.createHash('sha256').update(path.resolve(process.cwd())).digest('hex');
|
|
267
|
+
return `dir-${cwdHash.slice(0, 12)}`;
|
|
268
|
+
}
|
|
269
|
+
export function getGoalManager(sessionKey) {
|
|
270
|
+
const key = sessionKey ?? resolveGoalSessionKey();
|
|
271
|
+
let manager = registry.get(key);
|
|
272
|
+
if (!manager) {
|
|
273
|
+
const store = storeOverride ?? new GoalStore();
|
|
274
|
+
manager = new GoalManager(key, store, resolveGoalsConfig().maxTurns);
|
|
275
|
+
registry.set(key, manager);
|
|
276
|
+
if (registry.size > 20) {
|
|
277
|
+
const firstKey = registry.keys().next().value;
|
|
278
|
+
if (firstKey)
|
|
279
|
+
registry.delete(firstKey);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return manager;
|
|
283
|
+
}
|
|
284
|
+
/** Test seam: clear cached managers and optionally redirect persistence. */
|
|
285
|
+
export function resetGoalManagers(store = null) {
|
|
286
|
+
registry.clear();
|
|
287
|
+
storeOverride = store;
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=goal-manager.js.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent session goals — the Ralph loop for Code Buddy.
|
|
3
|
+
*
|
|
4
|
+
* A goal is a free-form user objective that stays active across turns. After
|
|
5
|
+
* each turn completes, a small judge call asks an auxiliary model "is this
|
|
6
|
+
* goal satisfied by the assistant's last response?". If not, Code Buddy feeds
|
|
7
|
+
* a continuation prompt back into the same session and keeps working until
|
|
8
|
+
* the goal is done, the turn budget is exhausted, the user pauses/clears it,
|
|
9
|
+
* or a real user message preempts the loop.
|
|
10
|
+
*
|
|
11
|
+
* Ported from Hermes Agent's goal system (hermes_cli/goals.py). Invariants:
|
|
12
|
+
* - The continuation prompt is a plain user message — no system-prompt
|
|
13
|
+
* mutation, no toolset swap, prompt caching stays intact.
|
|
14
|
+
* - Judge failures are fail-OPEN ("continue"): a broken judge must not wedge
|
|
15
|
+
* progress; the turn budget is the backstop.
|
|
16
|
+
* - A real user message mid-loop preempts the continuation prompt; the judge
|
|
17
|
+
* re-runs after that turn.
|
|
18
|
+
*/
|
|
19
|
+
import type { GoalPlan } from './goal-decomposer.js';
|
|
20
|
+
export type GoalStatus = 'active' | 'paused' | 'done' | 'cleared';
|
|
21
|
+
export type GoalVerdict = 'done' | 'continue' | 'skipped';
|
|
22
|
+
export interface GoalState {
|
|
23
|
+
goal: string;
|
|
24
|
+
status: GoalStatus;
|
|
25
|
+
turnsUsed: number;
|
|
26
|
+
maxTurns: number;
|
|
27
|
+
createdAt: number;
|
|
28
|
+
lastTurnAt: number;
|
|
29
|
+
lastVerdict?: GoalVerdict;
|
|
30
|
+
lastReason?: string;
|
|
31
|
+
/** Why we auto-paused (budget exhausted, judge parse failures, interrupt). */
|
|
32
|
+
pausedReason?: string;
|
|
33
|
+
/** Judge-output parse failures in a row. API/transport errors don't count. */
|
|
34
|
+
consecutiveParseFailures: number;
|
|
35
|
+
/**
|
|
36
|
+
* User-added criteria appended mid-loop via /subgoal. When non-empty both
|
|
37
|
+
* the judge prompt and the continuation prompt include them. Defaults to
|
|
38
|
+
* empty so old persisted state loads unchanged.
|
|
39
|
+
*/
|
|
40
|
+
subgoals: string[];
|
|
41
|
+
/**
|
|
42
|
+
* Optional Hermes-style decomposition graph generated for complex goals.
|
|
43
|
+
* Its acceptance criteria are fed to the judge alongside user-added
|
|
44
|
+
* subgoals, while remaining separate so /subgoal stays a manual control.
|
|
45
|
+
*/
|
|
46
|
+
goalPlan?: GoalPlan;
|
|
47
|
+
/** True once an LLM planning attempt was made for this goal. */
|
|
48
|
+
goalPlanAttempted?: boolean;
|
|
49
|
+
/** Last planning failure, kept for status/debug without retrying forever. */
|
|
50
|
+
goalPlanLastError?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare const DEFAULT_MAX_TURNS = 20;
|
|
53
|
+
export declare const DEFAULT_JUDGE_TIMEOUT_MS = 30000;
|
|
54
|
+
export declare const DEFAULT_JUDGE_MAX_TOKENS = 4096;
|
|
55
|
+
export declare const JUDGE_GOAL_SNIPPET_CHARS = 2000;
|
|
56
|
+
export declare const JUDGE_SUBGOALS_SNIPPET_CHARS = 2000;
|
|
57
|
+
export declare const JUDGE_RESPONSE_SNIPPET_CHARS = 4000;
|
|
58
|
+
export declare const MAX_CONSECUTIVE_PARSE_FAILURES = 3;
|
|
59
|
+
export declare const CONTINUATION_PROMPT_TEMPLATE: string;
|
|
60
|
+
export declare const CONTINUATION_PROMPT_WITH_SUBGOALS_TEMPLATE: string;
|
|
61
|
+
export declare const CONTINUATION_PROMPT_WITH_PLAN_TEMPLATE: string;
|
|
62
|
+
export declare const JUDGE_SYSTEM_PROMPT: string;
|
|
63
|
+
export declare const JUDGE_USER_PROMPT_TEMPLATE: string;
|
|
64
|
+
export declare const JUDGE_USER_PROMPT_WITH_SUBGOALS_TEMPLATE: string;
|
|
65
|
+
export declare function truncateText(text: string, limit: number): string;
|
|
66
|
+
/** Render subgoals as a numbered `- N. text` block. Empty string when none. */
|
|
67
|
+
export declare function renderSubgoalsBlock(subgoals: string[]): string;
|
|
68
|
+
export declare function getGoalJudgeCriteria(state: Pick<GoalState, 'goalPlan' | 'subgoals'>): string[];
|
|
69
|
+
export declare function createGoalState(goal: string, maxTurns?: number): GoalState;
|
|
70
|
+
/**
|
|
71
|
+
* Defensive deserialization of a persisted goal state. Returns null when the
|
|
72
|
+
* payload isn't a usable goal. Old payloads without `subgoals` load unchanged.
|
|
73
|
+
*/
|
|
74
|
+
export declare function normalizeGoalState(raw: unknown): GoalState | null;
|
|
75
|
+
/** Printable one-liner for /goal status. */
|
|
76
|
+
export declare function formatGoalStatusLine(state: GoalState | null): string;
|
|
77
|
+
/** The canonical user-role continuation message for an active goal. */
|
|
78
|
+
export declare function buildContinuationPrompt(state: GoalState): string;
|
|
79
|
+
export interface GoalJudgeOutcome {
|
|
80
|
+
verdict: GoalVerdict;
|
|
81
|
+
reason: string;
|
|
82
|
+
parseFailed: boolean;
|
|
83
|
+
}
|
|
84
|
+
export interface GoalTurnDecisionCore {
|
|
85
|
+
status: GoalStatus;
|
|
86
|
+
shouldContinue: boolean;
|
|
87
|
+
continuationPrompt: string | null;
|
|
88
|
+
verdict: GoalVerdict;
|
|
89
|
+
reason: string;
|
|
90
|
+
/** User-visible one-liner (✓ / ⏸ / ↻). */
|
|
91
|
+
message: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Apply a judge outcome to an active goal — the exact Hermes ladder.
|
|
95
|
+
* MUTATES `state` (turn counter, verdict bookkeeping, status transitions)
|
|
96
|
+
* and returns the decision; the caller persists the state wherever it
|
|
97
|
+
* lives (GoalStore file, peer-session record, …).
|
|
98
|
+
*/
|
|
99
|
+
export declare function applyJudgeOutcome(state: GoalState, outcome: GoalJudgeOutcome, nowMs?: number): GoalTurnDecisionCore;
|