@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
|
@@ -31,7 +31,11 @@
|
|
|
31
31
|
*/
|
|
32
32
|
import { beginFleetWork } from './fleet-load.js';
|
|
33
33
|
import { registerPeerMethod, unregisterPeerMethod } from '../server/websocket/peer-rpc.js';
|
|
34
|
-
import { broadcastChatSessionEnd, broadcastChatSessionStart, broadcastChatSessionTurn, } from '../server/websocket/fleet-bridge.js';
|
|
34
|
+
import { broadcastChatSessionEnd, broadcastChatSessionGoal, broadcastChatSessionStart, broadcastChatSessionTurn, } from '../server/websocket/fleet-bridge.js';
|
|
35
|
+
import { resolveGoalJudgeClientFailOpen } from '../goals/goal-judge-client.js';
|
|
36
|
+
import { judgeGoal } from '../goals/goal-judge.js';
|
|
37
|
+
import { resolveGoalsConfig } from '../goals/goal-manager.js';
|
|
38
|
+
import { applyJudgeOutcome, createGoalState, formatGoalStatusLine, getGoalJudgeCriteria, normalizeGoalState, renderSubgoalsBlock, } from '../goals/goal-state.js';
|
|
35
39
|
import { logger } from '../utils/logger.js';
|
|
36
40
|
import { getPeerSessionStore, } from './peer-session-store.js';
|
|
37
41
|
import { DEFAULT_DISPATCH_POLICY_PREVIEW_TOOLS, FLEET_DISPATCH_PROFILES, buildDispatchSystemPrompt, buildHermesToolsetDescriptor, getDispatchToolPolicy, isFleetDispatchProfile, mergeDispatchSystemPrompt, normalizeDispatchProfile, } from './dispatch-profile.js';
|
|
@@ -104,6 +108,14 @@ function newSessionId() {
|
|
|
104
108
|
const rand = Math.floor(Math.random() * 1e9).toString(36);
|
|
105
109
|
return `sess_${ts}_${rand}`;
|
|
106
110
|
}
|
|
111
|
+
function resolvePeerGoalMaxTurns(raw) {
|
|
112
|
+
if (raw === undefined)
|
|
113
|
+
return resolveGoalsConfig().maxTurns;
|
|
114
|
+
if (typeof raw !== 'number' || !Number.isSafeInteger(raw) || raw <= 0) {
|
|
115
|
+
throw new Error('peer.chat-session.goal: maxTurns must be a positive integer');
|
|
116
|
+
}
|
|
117
|
+
return raw;
|
|
118
|
+
}
|
|
107
119
|
/**
|
|
108
120
|
* Opportunistic GC — drop sessions whose `lastUsedAt` is older than
|
|
109
121
|
* the configured idle window. Called at the top of each `start` and
|
|
@@ -144,10 +156,82 @@ function snapshot(session) {
|
|
|
144
156
|
toolDecisions: session.toolDecisions,
|
|
145
157
|
toolset: session.toolset,
|
|
146
158
|
messages: [...session.messages],
|
|
159
|
+
...(session.goal ? { goal: { ...session.goal, subgoals: [...session.goal.subgoals] } } : {}),
|
|
147
160
|
createdAt: session.createdAt,
|
|
148
161
|
lastUsedAt: session.lastUsedAt,
|
|
149
162
|
};
|
|
150
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Post-turn goal hook shared by `continue` and `continue-stream`. Judges the
|
|
166
|
+
* assistant's response against the session goal, applies the Hermes decision
|
|
167
|
+
* ladder, and returns the report embedded in the RPC response. The judge is
|
|
168
|
+
* fail-open; this never throws. The caller persists the session afterwards.
|
|
169
|
+
*/
|
|
170
|
+
async function evaluateSessionGoalAfterTurn(session, assistantText) {
|
|
171
|
+
const goal = session.goal;
|
|
172
|
+
if (!goal || goal.status !== 'active')
|
|
173
|
+
return null;
|
|
174
|
+
if (!assistantText.trim()) {
|
|
175
|
+
goal.status = 'paused';
|
|
176
|
+
goal.pausedReason = 'empty response (nothing to evaluate)';
|
|
177
|
+
const report = {
|
|
178
|
+
status: 'paused',
|
|
179
|
+
verdict: 'skipped',
|
|
180
|
+
reason: goal.pausedReason,
|
|
181
|
+
turnsUsed: goal.turnsUsed,
|
|
182
|
+
maxTurns: goal.maxTurns,
|
|
183
|
+
message: '⏸ Goal paused — the peer produced no judgeable response.',
|
|
184
|
+
};
|
|
185
|
+
broadcastChatSessionGoal({
|
|
186
|
+
sessionId: session.sessionId,
|
|
187
|
+
status: report.status,
|
|
188
|
+
verdict: report.verdict,
|
|
189
|
+
turnsUsed: report.turnsUsed,
|
|
190
|
+
maxTurns: report.maxTurns,
|
|
191
|
+
});
|
|
192
|
+
return report;
|
|
193
|
+
}
|
|
194
|
+
let report;
|
|
195
|
+
try {
|
|
196
|
+
const config = resolveGoalsConfig();
|
|
197
|
+
const baseClient = cachedGetter?.() ?? null;
|
|
198
|
+
const judgeClient = await resolveGoalJudgeClientFailOpen(baseClient, config.judgeModel);
|
|
199
|
+
const criteria = getGoalJudgeCriteria(goal);
|
|
200
|
+
const outcome = await judgeGoal(judgeClient, {
|
|
201
|
+
goal: goal.goal,
|
|
202
|
+
lastResponse: assistantText,
|
|
203
|
+
...(criteria.length ? { subgoals: criteria } : {}),
|
|
204
|
+
...(config.judgeModel ? { model: config.judgeModel } : {}),
|
|
205
|
+
maxTokens: config.judgeMaxTokens,
|
|
206
|
+
timeoutMs: config.judgeTimeoutMs,
|
|
207
|
+
});
|
|
208
|
+
const decision = applyJudgeOutcome(goal, outcome);
|
|
209
|
+
report = {
|
|
210
|
+
status: decision.status,
|
|
211
|
+
verdict: decision.verdict,
|
|
212
|
+
reason: decision.reason,
|
|
213
|
+
turnsUsed: goal.turnsUsed,
|
|
214
|
+
maxTurns: goal.maxTurns,
|
|
215
|
+
message: decision.message,
|
|
216
|
+
...(decision.continuationPrompt ? { continuationPrompt: decision.continuationPrompt } : {}),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
logger.warn('[peer-session-bridge] goal evaluation failed — skipping this turn', {
|
|
221
|
+
sessionId: session.sessionId,
|
|
222
|
+
error: err instanceof Error ? err.message : String(err),
|
|
223
|
+
});
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
broadcastChatSessionGoal({
|
|
227
|
+
sessionId: session.sessionId,
|
|
228
|
+
status: report.status,
|
|
229
|
+
verdict: report.verdict,
|
|
230
|
+
turnsUsed: report.turnsUsed,
|
|
231
|
+
maxTurns: report.maxTurns,
|
|
232
|
+
});
|
|
233
|
+
return report;
|
|
234
|
+
}
|
|
151
235
|
/**
|
|
152
236
|
* Register the `peer.chat-session.*` methods. The `getClient` closure
|
|
153
237
|
* is captured and called fresh on each invocation so the caller can
|
|
@@ -174,6 +258,7 @@ export async function wirePeerSessionBridge(getClient) {
|
|
|
174
258
|
for (const p of persisted) {
|
|
175
259
|
if (now - p.lastUsedAt > idleMs)
|
|
176
260
|
continue; // double-check vs the boundary
|
|
261
|
+
const goal = p.goal ? normalizeGoalState(p.goal) : null;
|
|
177
262
|
sessions.set(p.sessionId, {
|
|
178
263
|
sessionId: p.sessionId,
|
|
179
264
|
systemPrompt: p.systemPrompt,
|
|
@@ -183,6 +268,7 @@ export async function wirePeerSessionBridge(getClient) {
|
|
|
183
268
|
toolDecisions: p.toolDecisions,
|
|
184
269
|
toolset: p.toolset,
|
|
185
270
|
messages: [...p.messages],
|
|
271
|
+
...(goal && goal.status !== 'cleared' ? { goal } : {}),
|
|
186
272
|
createdAt: p.createdAt,
|
|
187
273
|
lastUsedAt: p.lastUsedAt,
|
|
188
274
|
pending: Promise.resolve(),
|
|
@@ -312,6 +398,11 @@ export async function wirePeerSessionBridge(getClient) {
|
|
|
312
398
|
const text = response?.choices?.[0]?.message?.content ?? '';
|
|
313
399
|
session.messages.push({ role: 'assistant', content: text });
|
|
314
400
|
session.lastUsedAt = Date.now();
|
|
401
|
+
// Goal Ralph-loop (Hermes gateway parity): judge the turn server-side,
|
|
402
|
+
// mutate the session goal state, and report the verdict to the caller
|
|
403
|
+
// (who drives the continuation). Runs BEFORE the disk flush so the
|
|
404
|
+
// snapshot below persists the updated goal counters.
|
|
405
|
+
const goalReport = await evaluateSessionGoalAfterTurn(session, text);
|
|
315
406
|
// V1.2-saga — flush the new turn to disk before returning so a
|
|
316
407
|
// crash mid-conversation can be replayed on next boot. Failure
|
|
317
408
|
// logs but doesn't fail the turn: the caller already got an
|
|
@@ -340,6 +431,7 @@ export async function wirePeerSessionBridge(getClient) {
|
|
|
340
431
|
finishReason: response?.choices?.[0]?.finish_reason,
|
|
341
432
|
usage: response?.usage,
|
|
342
433
|
traceId: ctx.traceId,
|
|
434
|
+
...(goalReport ? { goal: goalReport } : {}),
|
|
343
435
|
...sessionPolicyMetadata(session),
|
|
344
436
|
};
|
|
345
437
|
};
|
|
@@ -439,6 +531,8 @@ export async function wirePeerSessionBridge(getClient) {
|
|
|
439
531
|
}
|
|
440
532
|
session.messages.push({ role: 'assistant', content: aggregate });
|
|
441
533
|
session.lastUsedAt = Date.now();
|
|
534
|
+
// Goal Ralph-loop — same server-side judge as the non-streaming path.
|
|
535
|
+
const goalReport = await evaluateSessionGoalAfterTurn(session, aggregate);
|
|
442
536
|
try {
|
|
443
537
|
await getPeerSessionStore().save(snapshot(session));
|
|
444
538
|
}
|
|
@@ -460,6 +554,7 @@ export async function wirePeerSessionBridge(getClient) {
|
|
|
460
554
|
finishReason,
|
|
461
555
|
usage,
|
|
462
556
|
traceId: ctx.traceId,
|
|
557
|
+
...(goalReport ? { goal: goalReport } : {}),
|
|
463
558
|
...sessionPolicyMetadata(session),
|
|
464
559
|
};
|
|
465
560
|
};
|
|
@@ -497,6 +592,150 @@ export async function wirePeerSessionBridge(getClient) {
|
|
|
497
592
|
traceId: ctx.traceId,
|
|
498
593
|
};
|
|
499
594
|
});
|
|
595
|
+
// Goal Ralph-loop controls (Hermes gateway parity). One method, action-
|
|
596
|
+
// dispatched: set | status | pause | resume | clear | subgoal-add |
|
|
597
|
+
// subgoal-list | subgoal-remove | subgoal-clear. Mirrors the /goal +
|
|
598
|
+
// /subgoal slash surface. Setting a NEW goal while one is active is
|
|
599
|
+
// rejected (Hermes mid-run rule) — pause/clear first. status/pause/
|
|
600
|
+
// resume/clear are safe mid-turn: they only touch goal metadata.
|
|
601
|
+
registerPeerMethod('peer.chat-session.goal', async (params, ctx) => {
|
|
602
|
+
const sessionId = typeof params.sessionId === 'string' ? params.sessionId : '';
|
|
603
|
+
const action = typeof params.action === 'string' ? params.action : 'status';
|
|
604
|
+
if (!sessionId) {
|
|
605
|
+
throw new Error('peer.chat-session.goal: sessionId is required (string)');
|
|
606
|
+
}
|
|
607
|
+
const session = sessions.get(sessionId);
|
|
608
|
+
if (!session) {
|
|
609
|
+
throw new Error(`SESSION_NOT_FOUND: no session with id "${sessionId}"`);
|
|
610
|
+
}
|
|
611
|
+
const persist = async () => {
|
|
612
|
+
try {
|
|
613
|
+
await getPeerSessionStore().save(snapshot(session));
|
|
614
|
+
}
|
|
615
|
+
catch (err) {
|
|
616
|
+
logger.warn('[peer-session-bridge] save on goal mutation failed', {
|
|
617
|
+
sessionId,
|
|
618
|
+
error: err instanceof Error ? err.message : String(err),
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
const emitGoal = (status) => {
|
|
623
|
+
broadcastChatSessionGoal({
|
|
624
|
+
sessionId,
|
|
625
|
+
status,
|
|
626
|
+
...(session.goal ? { turnsUsed: session.goal.turnsUsed, maxTurns: session.goal.maxTurns } : {}),
|
|
627
|
+
});
|
|
628
|
+
};
|
|
629
|
+
const goalView = () => session.goal
|
|
630
|
+
? {
|
|
631
|
+
goal: session.goal.goal,
|
|
632
|
+
status: session.goal.status,
|
|
633
|
+
turnsUsed: session.goal.turnsUsed,
|
|
634
|
+
maxTurns: session.goal.maxTurns,
|
|
635
|
+
subgoals: [...session.goal.subgoals],
|
|
636
|
+
...(session.goal.goalPlan ? { goalPlan: session.goal.goalPlan } : {}),
|
|
637
|
+
...(session.goal.lastVerdict ? { lastVerdict: session.goal.lastVerdict } : {}),
|
|
638
|
+
...(session.goal.lastReason ? { lastReason: session.goal.lastReason } : {}),
|
|
639
|
+
...(session.goal.pausedReason ? { pausedReason: session.goal.pausedReason } : {}),
|
|
640
|
+
statusLine: formatGoalStatusLine(session.goal),
|
|
641
|
+
}
|
|
642
|
+
: { status: 'none', statusLine: formatGoalStatusLine(null) };
|
|
643
|
+
switch (action) {
|
|
644
|
+
case 'set': {
|
|
645
|
+
const text = typeof params.goal === 'string' ? params.goal.trim() : '';
|
|
646
|
+
if (!text) {
|
|
647
|
+
throw new Error('peer.chat-session.goal: action "set" requires goal text (string)');
|
|
648
|
+
}
|
|
649
|
+
if (session.goal?.status === 'active') {
|
|
650
|
+
throw new Error('GOAL_ACTIVE: a goal is already active on this session. ' +
|
|
651
|
+
'Use action "status"/"pause"/"clear" mid-run; clear it before setting a new goal.');
|
|
652
|
+
}
|
|
653
|
+
const maxTurns = resolvePeerGoalMaxTurns(params.maxTurns);
|
|
654
|
+
session.goal = createGoalState(text, maxTurns);
|
|
655
|
+
await persist();
|
|
656
|
+
emitGoal('active');
|
|
657
|
+
return { ...goalView(), traceId: ctx.traceId };
|
|
658
|
+
}
|
|
659
|
+
case 'status':
|
|
660
|
+
return { ...goalView(), traceId: ctx.traceId };
|
|
661
|
+
case 'pause': {
|
|
662
|
+
if (!session.goal || !['active', 'paused'].includes(session.goal.status)) {
|
|
663
|
+
return { ...goalView(), traceId: ctx.traceId };
|
|
664
|
+
}
|
|
665
|
+
session.goal.status = 'paused';
|
|
666
|
+
session.goal.pausedReason = 'user-paused';
|
|
667
|
+
await persist();
|
|
668
|
+
emitGoal('paused');
|
|
669
|
+
return { ...goalView(), traceId: ctx.traceId };
|
|
670
|
+
}
|
|
671
|
+
case 'resume': {
|
|
672
|
+
if (!session.goal || session.goal.status !== 'paused') {
|
|
673
|
+
return { ...goalView(), traceId: ctx.traceId };
|
|
674
|
+
}
|
|
675
|
+
session.goal.status = 'active';
|
|
676
|
+
delete session.goal.pausedReason;
|
|
677
|
+
delete session.goal.lastVerdict;
|
|
678
|
+
delete session.goal.lastReason;
|
|
679
|
+
session.goal.consecutiveParseFailures = 0;
|
|
680
|
+
session.goal.turnsUsed = 0; // Hermes resume semantics: budget reset
|
|
681
|
+
await persist();
|
|
682
|
+
emitGoal('active');
|
|
683
|
+
return { ...goalView(), traceId: ctx.traceId };
|
|
684
|
+
}
|
|
685
|
+
case 'clear': {
|
|
686
|
+
const had = Boolean(session.goal);
|
|
687
|
+
delete session.goal;
|
|
688
|
+
await persist();
|
|
689
|
+
if (had)
|
|
690
|
+
emitGoal('cleared');
|
|
691
|
+
return { cleared: had, ...goalView(), traceId: ctx.traceId };
|
|
692
|
+
}
|
|
693
|
+
case 'subgoal-add': {
|
|
694
|
+
const text = typeof params.text === 'string' ? params.text.trim() : '';
|
|
695
|
+
if (!text) {
|
|
696
|
+
throw new Error('peer.chat-session.goal: action "subgoal-add" requires text (string)');
|
|
697
|
+
}
|
|
698
|
+
if (!session.goal || !['active', 'paused'].includes(session.goal.status)) {
|
|
699
|
+
throw new Error('NO_ACTIVE_GOAL: set a goal before adding subgoals');
|
|
700
|
+
}
|
|
701
|
+
session.goal.subgoals.push(text);
|
|
702
|
+
await persist();
|
|
703
|
+
return { ...goalView(), traceId: ctx.traceId };
|
|
704
|
+
}
|
|
705
|
+
case 'subgoal-list':
|
|
706
|
+
return {
|
|
707
|
+
...goalView(),
|
|
708
|
+
rendered: session.goal ? renderSubgoalsBlock(session.goal.subgoals) : '',
|
|
709
|
+
traceId: ctx.traceId,
|
|
710
|
+
};
|
|
711
|
+
case 'subgoal-remove': {
|
|
712
|
+
const index = typeof params.index === 'number' ? params.index : NaN;
|
|
713
|
+
if (!session.goal || !['active', 'paused'].includes(session.goal.status)) {
|
|
714
|
+
throw new Error('NO_ACTIVE_GOAL: set a goal before removing subgoals');
|
|
715
|
+
}
|
|
716
|
+
if (!Number.isSafeInteger(index) || index < 1) {
|
|
717
|
+
throw new Error('peer.chat-session.goal: subgoal index must be a positive integer');
|
|
718
|
+
}
|
|
719
|
+
if (index > session.goal.subgoals.length) {
|
|
720
|
+
throw new Error(`peer.chat-session.goal: subgoal index out of range (1..${session.goal.subgoals.length})`);
|
|
721
|
+
}
|
|
722
|
+
const [removed] = session.goal.subgoals.splice(index - 1, 1);
|
|
723
|
+
await persist();
|
|
724
|
+
return { removed, ...goalView(), traceId: ctx.traceId };
|
|
725
|
+
}
|
|
726
|
+
case 'subgoal-clear': {
|
|
727
|
+
if (!session.goal || !['active', 'paused'].includes(session.goal.status)) {
|
|
728
|
+
throw new Error('NO_ACTIVE_GOAL: set a goal before clearing subgoals');
|
|
729
|
+
}
|
|
730
|
+
const previous = session.goal.subgoals.length;
|
|
731
|
+
session.goal.subgoals = [];
|
|
732
|
+
await persist();
|
|
733
|
+
return { cleared: previous, ...goalView(), traceId: ctx.traceId };
|
|
734
|
+
}
|
|
735
|
+
default:
|
|
736
|
+
throw new Error(`peer.chat-session.goal: unknown action "${action}" (expected set | status | pause | resume | clear | subgoal-add | subgoal-list | subgoal-remove | subgoal-clear)`);
|
|
737
|
+
}
|
|
738
|
+
});
|
|
500
739
|
registerPeerMethod('peer.chat-session.end', async (params, ctx) => {
|
|
501
740
|
const sessionId = typeof params.sessionId === 'string' ? params.sessionId : '';
|
|
502
741
|
if (!sessionId) {
|
|
@@ -520,13 +759,14 @@ export async function wirePeerSessionBridge(getClient) {
|
|
|
520
759
|
wired = true;
|
|
521
760
|
logger.debug('[peer-session-bridge] wired');
|
|
522
761
|
}
|
|
523
|
-
/** Detach all
|
|
762
|
+
/** Detach all registered methods. Idempotent. Does NOT clear in-memory sessions. */
|
|
524
763
|
export function unwirePeerSessionBridge() {
|
|
525
764
|
if (!wired)
|
|
526
765
|
return;
|
|
527
766
|
unregisterPeerMethod('peer.chat-session.start');
|
|
528
767
|
unregisterPeerMethod('peer.chat-session.continue');
|
|
529
768
|
unregisterPeerMethod('peer.chat-session.continue-stream');
|
|
769
|
+
unregisterPeerMethod('peer.chat-session.goal');
|
|
530
770
|
unregisterPeerMethod('peer.chat-session.end');
|
|
531
771
|
cachedGetter = null;
|
|
532
772
|
wired = false;
|
|
@@ -546,6 +786,7 @@ export function _unwireForTests() {
|
|
|
546
786
|
unregisterPeerMethod('peer.chat-session.start');
|
|
547
787
|
unregisterPeerMethod('peer.chat-session.continue');
|
|
548
788
|
unregisterPeerMethod('peer.chat-session.continue-stream');
|
|
789
|
+
unregisterPeerMethod('peer.chat-session.goal');
|
|
549
790
|
unregisterPeerMethod('peer.chat-session.list');
|
|
550
791
|
unregisterPeerMethod('peer.chat-session.end');
|
|
551
792
|
}
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
*
|
|
28
28
|
* @module fleet/peer-session-store
|
|
29
29
|
*/
|
|
30
|
+
import type { GoalState } from '../goals/goal-state.js';
|
|
30
31
|
import type { FleetDispatchProfile, FleetHermesToolsetDescriptor, FleetDispatchToolDecision, FleetDispatchToolPolicy } from './dispatch-profile.js';
|
|
31
32
|
export interface PersistedChatMessage {
|
|
32
33
|
role: 'system' | 'user' | 'assistant';
|
|
@@ -42,6 +43,8 @@ export interface PersistedChatSession {
|
|
|
42
43
|
toolset?: FleetHermesToolsetDescriptor;
|
|
43
44
|
/** User/assistant turns (system prompt is held separately). */
|
|
44
45
|
messages: PersistedChatMessage[];
|
|
46
|
+
/** Standing goal attached via `peer.chat-session.goal` (Hermes gateway parity). */
|
|
47
|
+
goal?: GoalState;
|
|
45
48
|
createdAt: number;
|
|
46
49
|
lastUsedAt: number;
|
|
47
50
|
}
|
|
@@ -34,6 +34,7 @@ import { PolicyEngine } from '../security/policy-engine.js';
|
|
|
34
34
|
import { ConfirmationService } from '../utils/confirmation-service.js';
|
|
35
35
|
import { auditLogger } from '../security/audit-logger.js';
|
|
36
36
|
import { assertPeerToolInvokeAllowed } from './permissions.js';
|
|
37
|
+
import { getGlobalEventBus } from '../events/event-bus.js';
|
|
37
38
|
// ──────────────────────────────────────────────────────────────────
|
|
38
39
|
// Allowlist & gates
|
|
39
40
|
// ──────────────────────────────────────────────────────────────────
|
|
@@ -397,6 +398,19 @@ async function runInvocation(params, ctx, stream) {
|
|
|
397
398
|
}
|
|
398
399
|
}
|
|
399
400
|
function logAudit(input) {
|
|
401
|
+
try {
|
|
402
|
+
getGlobalEventBus().emit('fleet:activity', {
|
|
403
|
+
activityType: 'fleet.tool.invoke',
|
|
404
|
+
title: 'Peer Tool Invoked',
|
|
405
|
+
description: input.error
|
|
406
|
+
? `Peer ${input.ctx.connectionId} failed to invoke ${input.tool}: ${input.error}`
|
|
407
|
+
: `Peer ${input.ctx.connectionId} invoked ${input.tool}`,
|
|
408
|
+
metadata: { peerId: input.ctx.connectionId, tool: input.tool, ok: input.ok, error: input.error }
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
catch (err) {
|
|
412
|
+
// ignore
|
|
413
|
+
}
|
|
400
414
|
auditLogger.log({
|
|
401
415
|
action: 'tool_execution',
|
|
402
416
|
decision: input.ok ? 'allow' : 'block',
|
|
@@ -35,3 +35,11 @@ export interface PrivacyLintResult {
|
|
|
35
35
|
* Scan a prompt for secrets. Returns matches with previews.
|
|
36
36
|
*/
|
|
37
37
|
export declare function scanForSecrets(prompt: string): PrivacyLintResult;
|
|
38
|
+
/**
|
|
39
|
+
* Replace every secret/PII match with a `[REDACTED:<kind>]` marker.
|
|
40
|
+
*
|
|
41
|
+
* Run this on FULL text before any truncation — cutting a PEM block (or
|
|
42
|
+
* any multi-line secret) in half can hide it from the patterns above.
|
|
43
|
+
* Used by every memory-persistence path (WS3 guard-rail).
|
|
44
|
+
*/
|
|
45
|
+
export declare function redactSecrets(text: string): string;
|
|
@@ -164,4 +164,26 @@ function redactPreview(text, start, end) {
|
|
|
164
164
|
: matched.slice(0, 4) + '…[redacted]…' + matched.slice(-4);
|
|
165
165
|
return `${before}${redacted}${after}`.replace(/\s+/g, ' ').trim();
|
|
166
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Replace every secret/PII match with a `[REDACTED:<kind>]` marker.
|
|
169
|
+
*
|
|
170
|
+
* Run this on FULL text before any truncation — cutting a PEM block (or
|
|
171
|
+
* any multi-line secret) in half can hide it from the patterns above.
|
|
172
|
+
* Used by every memory-persistence path (WS3 guard-rail).
|
|
173
|
+
*/
|
|
174
|
+
export function redactSecrets(text) {
|
|
175
|
+
const lint = scanForSecrets(text);
|
|
176
|
+
if (!lint.hasSecrets)
|
|
177
|
+
return text;
|
|
178
|
+
let out = '';
|
|
179
|
+
let cursor = 0;
|
|
180
|
+
for (const match of [...lint.matches].sort((a, b) => a.start - b.start)) {
|
|
181
|
+
if (match.start < cursor)
|
|
182
|
+
continue; // overlapping match already covered
|
|
183
|
+
out += text.slice(cursor, match.start) + `[REDACTED:${match.kind}]`;
|
|
184
|
+
cursor = match.end;
|
|
185
|
+
}
|
|
186
|
+
out += text.slice(cursor);
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
167
189
|
//# sourceMappingURL=privacy-lint.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CodeBuddyClient } from '../codebuddy/client.js';
|
|
2
|
+
export interface GoalPlanSubtask {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
acceptanceCriteria: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface GoalPlanTask extends GoalPlanSubtask {
|
|
9
|
+
dependsOn: string[];
|
|
10
|
+
subtasks: GoalPlanSubtask[];
|
|
11
|
+
}
|
|
12
|
+
export interface GoalPlan {
|
|
13
|
+
summary: string;
|
|
14
|
+
tasks: GoalPlanTask[];
|
|
15
|
+
notes?: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface DecomposeGoalOptions {
|
|
18
|
+
model?: string;
|
|
19
|
+
maxTokens?: number;
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function shouldAutoDecomposeGoal(goal: string): boolean;
|
|
23
|
+
export declare function decomposeGoal(goal: string, client: CodeBuddyClient, options?: DecomposeGoalOptions): Promise<GoalPlan | null>;
|
|
24
|
+
export declare function parseGoalPlan(raw: string, originalGoal?: string): GoalPlan | null;
|
|
25
|
+
export declare function normalizeGoalPlan(raw: unknown, originalGoal?: string): GoalPlan | null;
|
|
26
|
+
export declare function goalPlanToCriteria(plan: GoalPlan | undefined, maxCriteria?: number): string[];
|
|
27
|
+
export declare function formatGoalPlan(plan: GoalPlan): string;
|