@phuetz/code-buddy 1.1.0 → 1.3.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 +211 -163
- package/dist/agent/autonomous/agentic-coding-contract.d.ts +12 -12
- package/dist/agent/base-agent.d.ts +4 -0
- package/dist/agent/base-agent.js +6 -0
- 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/facades/infrastructure-facade.d.ts +9 -2
- package/dist/agent/facades/infrastructure-facade.js +15 -6
- 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/self-improvement/authored-artifact-gate.d.ts +18 -0
- package/dist/agent/self-improvement/authored-artifact-gate.js +42 -0
- package/dist/agent/self-improvement/authored-tool-runtime.d.ts +27 -0
- package/dist/agent/self-improvement/authored-tool-runtime.js +57 -0
- package/dist/agent/self-improvement/authored-tool-store.d.ts +24 -0
- package/dist/agent/self-improvement/authored-tool-store.js +57 -0
- package/dist/agent/self-improvement/llm-tool-proposer.d.ts +41 -0
- package/dist/agent/self-improvement/llm-tool-proposer.js +136 -0
- package/dist/agent/self-improvement/sandbox-scorer.d.ts +17 -0
- package/dist/agent/self-improvement/sandbox-scorer.js +43 -0
- package/dist/agent/self-improvement/self-knowledge.d.ts +8 -0
- package/dist/agent/self-improvement/self-knowledge.js +24 -0
- package/dist/agent/self-improvement/skill-benchmark.d.ts +9 -0
- package/dist/agent/self-improvement/skill-benchmark.js +22 -0
- package/dist/agent/self-improvement/skill-consolidator.d.ts +71 -0
- package/dist/agent/self-improvement/skill-consolidator.js +137 -0
- package/dist/agent/self-improvement/skill-engine.d.ts +42 -0
- package/dist/agent/self-improvement/skill-engine.js +87 -0
- package/dist/agent/self-improvement/skill-gate.d.ts +19 -0
- package/dist/agent/self-improvement/skill-gate.js +62 -0
- package/dist/agent/self-improvement/skill-mutator.d.ts +74 -0
- package/dist/agent/self-improvement/skill-mutator.js +223 -0
- package/dist/agent/self-improvement/skill-proposer.d.ts +40 -0
- package/dist/agent/self-improvement/skill-proposer.js +82 -0
- package/dist/agent/self-improvement/skill-types.d.ts +41 -0
- package/dist/agent/self-improvement/skill-types.js +13 -0
- package/dist/agent/self-improvement/tool-benchmark.d.ts +10 -0
- package/dist/agent/self-improvement/tool-benchmark.js +37 -0
- package/dist/agent/self-improvement/tool-engine.d.ts +54 -0
- package/dist/agent/self-improvement/tool-engine.js +101 -0
- package/dist/agent/self-improvement/tool-gate.d.ts +20 -0
- package/dist/agent/self-improvement/tool-gate.js +78 -0
- package/dist/agent/self-improvement/tool-proposer.d.ts +31 -0
- package/dist/agent/self-improvement/tool-proposer.js +34 -0
- package/dist/agent/self-improvement/tool-skill-mutator.d.ts +40 -0
- package/dist/agent/self-improvement/tool-skill-mutator.js +79 -0
- package/dist/agent/self-improvement/tool-types.d.ts +48 -0
- package/dist/agent/self-improvement/tool-types.js +9 -0
- package/dist/agent/self-improvement/types.d.ts +3 -1
- 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 +133 -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 +7 -1
- 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 +8 -1
- package/dist/codebuddy/tools.js +67 -24
- package/dist/commands/cli/hermes-commands.js +108 -0
- package/dist/commands/cli/improve-command.js +123 -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 +2 -2
- 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/handlers/missing-handlers.d.ts +1 -1
- package/dist/commands/handlers/missing-handlers.js +26 -3
- 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/skills-cli/index.js +123 -0
- package/dist/commands/slash/builtin-commands.js +1 -1
- 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/companion/percepts.js +11 -1
- 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/bootstrap-loader.js +6 -23
- package/dist/context/context-manager-v2.js +1 -0
- package/dist/context/import-directive-parser.d.ts +4 -0
- package/dist/context/import-directive-parser.js +51 -6
- package/dist/context/instruction-excludes.d.ts +30 -1
- package/dist/context/instruction-excludes.js +71 -1
- package/dist/context/jit-context.d.ts +8 -10
- package/dist/context/jit-context.js +28 -106
- package/dist/context/project-context.d.ts +90 -0
- package/dist/context/project-context.js +295 -0
- package/dist/daemon/autonomous-daemon.d.ts +1 -1
- package/dist/daemon/autonomous-daemon.js +3 -3
- package/dist/daemon/autonomous-loop.d.ts +31 -1
- package/dist/daemon/autonomous-loop.js +91 -4
- 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/harness/contract.d.ts +28 -28
- package/dist/hooks/user-hooks.js +17 -3
- package/dist/identity/identity-manager.js +3 -2
- package/dist/index.js +481 -26
- 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-resources.js +2 -3
- 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/sensory/dreaming.d.ts +45 -0
- package/dist/sensory/dreaming.js +114 -0
- package/dist/sensory/heartbeat-scheduler.d.ts +38 -0
- package/dist/sensory/heartbeat-scheduler.js +72 -0
- package/dist/sensory/reactions.d.ts +24 -0
- package/dist/sensory/reactions.js +31 -0
- package/dist/sensory/screen-reaction.d.ts +23 -0
- package/dist/sensory/screen-reaction.js +59 -0
- package/dist/sensory/sensory-bridge.d.ts +23 -0
- package/dist/sensory/sensory-bridge.js +85 -0
- package/dist/sensory/sensory-memory.d.ts +20 -0
- package/dist/sensory/sensory-memory.js +39 -0
- package/dist/sensory/speech-reaction.d.ts +21 -0
- package/dist/sensory/speech-reaction.js +83 -0
- package/dist/sensory/vision-reaction.d.ts +31 -0
- package/dist/sensory/vision-reaction.js +74 -0
- package/dist/server/index.js +106 -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 +11 -0
- package/dist/services/prompt-builder.js +124 -17
- 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/skills/parser.js +3 -0
- package/dist/skills/skill-importer.d.ts +58 -0
- package/dist/skills/skill-importer.js +261 -0
- package/dist/skills/skill-sources.d.ts +20 -0
- package/dist/skills/skill-sources.js +102 -0
- package/dist/skills/types.d.ts +6 -0
- 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/register-tool-handler.d.ts +25 -0
- package/dist/tools/register-tool-handler.js +100 -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/registry.d.ts +6 -0
- package/dist/tools/registry.js +8 -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/init-project.d.ts +7 -0
- package/dist/utils/init-project.js +37 -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 +19 -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,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Context — the canonical hierarchical loader for project-instruction
|
|
3
|
+
* files (the AGENTS.md / CLAUDE.md / GEMINI.md / CODEBUDDY.md hierarchy).
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth that replaces the previously fragmented readers
|
|
6
|
+
* (jit-context, bootstrap-loader's hierarchical walk, context-files). It owns:
|
|
7
|
+
* - the config-driven accepted-filename list (`loadContextConfig`),
|
|
8
|
+
* - the per-directory probe (incl. `.codebuddy/` + `.claude/` descent),
|
|
9
|
+
* - variant precedence (`<name>.local.md` > `<name>.override.md` > `<name>`),
|
|
10
|
+
* - `@import` resolution (code-fence-safe), token/byte budgeting,
|
|
11
|
+
* - realpath dedup shared across the startup pass and JIT (the `ContextRegistry`).
|
|
12
|
+
*
|
|
13
|
+
* Precedence (documented once, here):
|
|
14
|
+
* - Tiers: global (`~/.codebuddy/`) is appended FIRST (lowest precedence),
|
|
15
|
+
* then the project hierarchy from project-root DOWN to cwd. Files closer to
|
|
16
|
+
* cwd appear later in the text → win on conflict (Codex AGENTS.md model).
|
|
17
|
+
* - Within a directory, every accepted filename present is COMPOSED (so
|
|
18
|
+
* `CODEBUDDY.md` extends `AGENTS.md`), in the configured order.
|
|
19
|
+
* - Within a (directory, name), the first existing variant wins and REPLACES
|
|
20
|
+
* the base: `<name>.local.md` (gitignored) > `<name>.override.md` > `<name>`.
|
|
21
|
+
* - Dedup key is `fs.realpathSync` so a `CLAUDE.md`/`GEMINI.md` symlinked to
|
|
22
|
+
* `AGENTS.md` collapses to one injection.
|
|
23
|
+
*
|
|
24
|
+
* Determinism: iteration is over the fixed `dirs × probeDirs × names` product
|
|
25
|
+
* with relative origin headers and no mtimes — the rendered text is byte-stable
|
|
26
|
+
* across runs, which keeps prompt caching intact.
|
|
27
|
+
*/
|
|
28
|
+
export type ContextTier = 'global' | 'hierarchy' | 'jit';
|
|
29
|
+
export type ContextVariant = 'base' | 'override' | 'local';
|
|
30
|
+
export interface ContextSource {
|
|
31
|
+
/** Absolute path of the loaded file. */
|
|
32
|
+
path: string;
|
|
33
|
+
/** `fs.realpathSync` of `path` — the dedup key. */
|
|
34
|
+
realpath: string;
|
|
35
|
+
/** Display path (relative to project root; `~/.codebuddy/...` for global). */
|
|
36
|
+
relPath: string;
|
|
37
|
+
tier: ContextTier;
|
|
38
|
+
variant: ContextVariant;
|
|
39
|
+
/** Size of this source's rendered body (chars). */
|
|
40
|
+
bytes: number;
|
|
41
|
+
/** Whether this source was truncated to fit the budget. */
|
|
42
|
+
truncated: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface ResolvedContext {
|
|
45
|
+
/** Merged, header-decorated text, ready to inject. */
|
|
46
|
+
text: string;
|
|
47
|
+
sources: ContextSource[];
|
|
48
|
+
bytes: number;
|
|
49
|
+
truncated: boolean;
|
|
50
|
+
}
|
|
51
|
+
export interface ResolveOptions {
|
|
52
|
+
cwd?: string;
|
|
53
|
+
/** Project root; resolved via ROOT_MARKERS when omitted. */
|
|
54
|
+
projectRoot?: string;
|
|
55
|
+
/** Shared dedup registry (startup ↔ JIT). A private one is used if omitted. */
|
|
56
|
+
registry?: ContextRegistry;
|
|
57
|
+
/** Byte budget override (defaults to config `maxBytes` / `jitMaxBytes`). */
|
|
58
|
+
budgetBytes?: number;
|
|
59
|
+
/** Accepted filenames override (defaults to config). */
|
|
60
|
+
fileNames?: string[];
|
|
61
|
+
/** Home-dir override (for testing). */
|
|
62
|
+
homeDir?: string;
|
|
63
|
+
}
|
|
64
|
+
/** Tracks realpaths already injected so a file is never loaded twice. */
|
|
65
|
+
export declare class ContextRegistry {
|
|
66
|
+
private readonly seen;
|
|
67
|
+
has(realpath: string): boolean;
|
|
68
|
+
add(realpath: string): void;
|
|
69
|
+
clear(): void;
|
|
70
|
+
get size(): number;
|
|
71
|
+
}
|
|
72
|
+
export declare function createContextRegistry(): ContextRegistry;
|
|
73
|
+
export declare function setActiveContextRegistry(registry: ContextRegistry): void;
|
|
74
|
+
export declare function getActiveContextRegistry(): ContextRegistry;
|
|
75
|
+
/** Project root via ROOT_MARKERS, walking up from `cwd` (null if none found). */
|
|
76
|
+
export declare function findProjectRoot(cwd: string, markers?: string[]): string | null;
|
|
77
|
+
/** Accepted instruction filenames for a project (config-driven). */
|
|
78
|
+
export declare function getAcceptedFileNames(projectRoot?: string): string[];
|
|
79
|
+
/**
|
|
80
|
+
* Startup pass: global tier + the project hierarchy from root DOWN to cwd.
|
|
81
|
+
* Call once per prompt build. Populates `registry` (if given) so the later JIT
|
|
82
|
+
* pass skips files already in the system prompt.
|
|
83
|
+
*/
|
|
84
|
+
export declare function resolveProjectContext(opts?: ResolveOptions): ResolvedContext;
|
|
85
|
+
/**
|
|
86
|
+
* JIT pass: when a tool touches `accessedPath`, load any context files along its
|
|
87
|
+
* directory chain (root → accessed dir) that weren't already injected at
|
|
88
|
+
* startup (skipped via the shared `registry`). Smaller per-touch budget.
|
|
89
|
+
*/
|
|
90
|
+
export declare function resolveJitContext(accessedPath: string, opts?: ResolveOptions): ResolvedContext;
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Context — the canonical hierarchical loader for project-instruction
|
|
3
|
+
* files (the AGENTS.md / CLAUDE.md / GEMINI.md / CODEBUDDY.md hierarchy).
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth that replaces the previously fragmented readers
|
|
6
|
+
* (jit-context, bootstrap-loader's hierarchical walk, context-files). It owns:
|
|
7
|
+
* - the config-driven accepted-filename list (`loadContextConfig`),
|
|
8
|
+
* - the per-directory probe (incl. `.codebuddy/` + `.claude/` descent),
|
|
9
|
+
* - variant precedence (`<name>.local.md` > `<name>.override.md` > `<name>`),
|
|
10
|
+
* - `@import` resolution (code-fence-safe), token/byte budgeting,
|
|
11
|
+
* - realpath dedup shared across the startup pass and JIT (the `ContextRegistry`).
|
|
12
|
+
*
|
|
13
|
+
* Precedence (documented once, here):
|
|
14
|
+
* - Tiers: global (`~/.codebuddy/`) is appended FIRST (lowest precedence),
|
|
15
|
+
* then the project hierarchy from project-root DOWN to cwd. Files closer to
|
|
16
|
+
* cwd appear later in the text → win on conflict (Codex AGENTS.md model).
|
|
17
|
+
* - Within a directory, every accepted filename present is COMPOSED (so
|
|
18
|
+
* `CODEBUDDY.md` extends `AGENTS.md`), in the configured order.
|
|
19
|
+
* - Within a (directory, name), the first existing variant wins and REPLACES
|
|
20
|
+
* the base: `<name>.local.md` (gitignored) > `<name>.override.md` > `<name>`.
|
|
21
|
+
* - Dedup key is `fs.realpathSync` so a `CLAUDE.md`/`GEMINI.md` symlinked to
|
|
22
|
+
* `AGENTS.md` collapses to one injection.
|
|
23
|
+
*
|
|
24
|
+
* Determinism: iteration is over the fixed `dirs × probeDirs × names` product
|
|
25
|
+
* with relative origin headers and no mtimes — the rendered text is byte-stable
|
|
26
|
+
* across runs, which keeps prompt caching intact.
|
|
27
|
+
*/
|
|
28
|
+
import * as fs from 'fs';
|
|
29
|
+
import * as path from 'path';
|
|
30
|
+
import * as os from 'os';
|
|
31
|
+
import { logger } from '../utils/logger.js';
|
|
32
|
+
import { resolveImportDirectives } from './import-directive-parser.js';
|
|
33
|
+
import { loadContextConfig, shouldExcludeInstructionFile, } from './instruction-excludes.js';
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Constants
|
|
36
|
+
// ============================================================================
|
|
37
|
+
/** Project-root markers, walked upward from cwd. */
|
|
38
|
+
const ROOT_MARKERS = ['.git', 'package.json', 'Cargo.toml', 'go.mod', 'pyproject.toml', '.hg'];
|
|
39
|
+
const MAX_HIERARCHY_DEPTH = 10;
|
|
40
|
+
/** Subdirectories probed inside every level of the hierarchy. */
|
|
41
|
+
const SUBDIR_PROBES = ['.codebuddy', '.claude'];
|
|
42
|
+
const TRUNCATION_MARKER = '\n\n... (truncated)';
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Dedup registry (shared between the startup pass and JIT)
|
|
45
|
+
// ============================================================================
|
|
46
|
+
/** Tracks realpaths already injected so a file is never loaded twice. */
|
|
47
|
+
export class ContextRegistry {
|
|
48
|
+
seen = new Set();
|
|
49
|
+
has(realpath) {
|
|
50
|
+
return this.seen.has(realpath);
|
|
51
|
+
}
|
|
52
|
+
add(realpath) {
|
|
53
|
+
this.seen.add(realpath);
|
|
54
|
+
}
|
|
55
|
+
clear() {
|
|
56
|
+
this.seen.clear();
|
|
57
|
+
}
|
|
58
|
+
get size() {
|
|
59
|
+
return this.seen.size;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function createContextRegistry() {
|
|
63
|
+
return new ContextRegistry();
|
|
64
|
+
}
|
|
65
|
+
// Module-level "active" registry. The prompt builder publishes its per-build
|
|
66
|
+
// registry here (`setActiveContextRegistry`) so the standalone JIT pass — which
|
|
67
|
+
// has no handle to the PromptBuilder — can dedup against files already injected
|
|
68
|
+
// at startup. Defaults to a persistent registry until the first build.
|
|
69
|
+
let activeRegistry = new ContextRegistry();
|
|
70
|
+
export function setActiveContextRegistry(registry) {
|
|
71
|
+
activeRegistry = registry;
|
|
72
|
+
}
|
|
73
|
+
export function getActiveContextRegistry() {
|
|
74
|
+
return activeRegistry;
|
|
75
|
+
}
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// Public API
|
|
78
|
+
// ============================================================================
|
|
79
|
+
/** Project root via ROOT_MARKERS, walking up from `cwd` (null if none found). */
|
|
80
|
+
export function findProjectRoot(cwd, markers = ROOT_MARKERS) {
|
|
81
|
+
let dir = path.resolve(cwd);
|
|
82
|
+
let depth = 0;
|
|
83
|
+
while (depth < MAX_HIERARCHY_DEPTH) {
|
|
84
|
+
for (const marker of markers) {
|
|
85
|
+
try {
|
|
86
|
+
if (fs.existsSync(path.join(dir, marker)))
|
|
87
|
+
return dir;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
/* ignore */
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const parent = path.dirname(dir);
|
|
94
|
+
if (parent === dir)
|
|
95
|
+
break;
|
|
96
|
+
dir = parent;
|
|
97
|
+
depth++;
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
/** Accepted instruction filenames for a project (config-driven). */
|
|
102
|
+
export function getAcceptedFileNames(projectRoot = process.cwd()) {
|
|
103
|
+
return loadContextConfig(projectRoot).fileNames;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Startup pass: global tier + the project hierarchy from root DOWN to cwd.
|
|
107
|
+
* Call once per prompt build. Populates `registry` (if given) so the later JIT
|
|
108
|
+
* pass skips files already in the system prompt.
|
|
109
|
+
*/
|
|
110
|
+
export function resolveProjectContext(opts = {}) {
|
|
111
|
+
const cwd = path.resolve(opts.cwd ?? process.cwd());
|
|
112
|
+
const root = path.resolve(opts.projectRoot ?? findProjectRoot(cwd) ?? cwd);
|
|
113
|
+
const cfg = loadContextConfig(root);
|
|
114
|
+
const names = opts.fileNames ?? cfg.fileNames;
|
|
115
|
+
const homeDir = opts.homeDir ?? os.homedir();
|
|
116
|
+
const registry = opts.registry ?? new ContextRegistry();
|
|
117
|
+
const loaded = [];
|
|
118
|
+
// Tier 1 — global (~/.codebuddy/), lowest precedence (appended first).
|
|
119
|
+
const globalDir = path.join(homeDir, '.codebuddy');
|
|
120
|
+
loaded.push(...collectDir(globalDir, 'global', names, root, registry, homeDir, cfg));
|
|
121
|
+
// Tier 2 — hierarchy, project root → cwd inclusive (closest appended last).
|
|
122
|
+
for (const dir of getDirectoryChain(root, cwd)) {
|
|
123
|
+
for (const probe of [dir, ...SUBDIR_PROBES.map((s) => path.join(dir, s))]) {
|
|
124
|
+
loaded.push(...collectDir(probe, 'hierarchy', names, root, registry, homeDir, cfg));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return budgetMerge(loaded, opts.budgetBytes ?? cfg.maxBytes);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* JIT pass: when a tool touches `accessedPath`, load any context files along its
|
|
131
|
+
* directory chain (root → accessed dir) that weren't already injected at
|
|
132
|
+
* startup (skipped via the shared `registry`). Smaller per-touch budget.
|
|
133
|
+
*/
|
|
134
|
+
export function resolveJitContext(accessedPath, opts = {}) {
|
|
135
|
+
const accessedDir = path.dirname(path.resolve(accessedPath));
|
|
136
|
+
const root = path.resolve(opts.projectRoot ?? findProjectRoot(accessedDir) ?? accessedDir);
|
|
137
|
+
const cfg = loadContextConfig(root);
|
|
138
|
+
const names = opts.fileNames ?? cfg.fileNames;
|
|
139
|
+
const homeDir = opts.homeDir ?? os.homedir();
|
|
140
|
+
const registry = opts.registry ?? new ContextRegistry();
|
|
141
|
+
const loaded = [];
|
|
142
|
+
for (const dir of getDirectoryChain(root, accessedDir)) {
|
|
143
|
+
for (const probe of [dir, ...SUBDIR_PROBES.map((s) => path.join(dir, s))]) {
|
|
144
|
+
loaded.push(...collectDir(probe, 'jit', names, root, registry, homeDir, cfg));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return budgetMerge(loaded, opts.budgetBytes ?? cfg.jitMaxBytes);
|
|
148
|
+
}
|
|
149
|
+
/** Directory chain from `root` to `target` (inclusive). */
|
|
150
|
+
function getDirectoryChain(root, target) {
|
|
151
|
+
const resolved = path.resolve(target);
|
|
152
|
+
const resolvedRoot = path.resolve(root);
|
|
153
|
+
const chain = [resolvedRoot];
|
|
154
|
+
const rel = path.relative(resolvedRoot, resolved);
|
|
155
|
+
if (!rel || rel.startsWith('..'))
|
|
156
|
+
return chain;
|
|
157
|
+
let current = resolvedRoot;
|
|
158
|
+
for (const seg of rel.split(path.sep).filter(Boolean)) {
|
|
159
|
+
current = path.join(current, seg);
|
|
160
|
+
chain.push(current);
|
|
161
|
+
}
|
|
162
|
+
return chain;
|
|
163
|
+
}
|
|
164
|
+
/** Variant filenames for a base name, most-specific first. */
|
|
165
|
+
function variantNames(base) {
|
|
166
|
+
const ext = path.extname(base); // '.md'
|
|
167
|
+
const stem = ext ? base.slice(0, base.length - ext.length) : base;
|
|
168
|
+
return [
|
|
169
|
+
{ name: `${stem}.local${ext}`, variant: 'local' },
|
|
170
|
+
{ name: `${stem}.override${ext}`, variant: 'override' },
|
|
171
|
+
{ name: base, variant: 'base' },
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Collect every accepted instruction file present in one directory (composed),
|
|
176
|
+
* applying variant precedence, excludes, realpath dedup and @import resolution.
|
|
177
|
+
*/
|
|
178
|
+
function collectDir(probeDir, tier, names, root, registry, homeDir, cfg) {
|
|
179
|
+
const out = [];
|
|
180
|
+
const isGlobal = tier === 'global';
|
|
181
|
+
for (const base of names) {
|
|
182
|
+
// First existing variant wins (local > override > base) and replaces it.
|
|
183
|
+
let chosen = null;
|
|
184
|
+
for (const v of variantNames(base)) {
|
|
185
|
+
const filePath = path.join(probeDir, v.name);
|
|
186
|
+
try {
|
|
187
|
+
if (fs.statSync(filePath).isFile()) {
|
|
188
|
+
chosen = { filePath, variant: v.variant };
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
/* not present */
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (!chosen)
|
|
197
|
+
continue;
|
|
198
|
+
// Project files honor codebuddyMdExcludes; global files are never excluded.
|
|
199
|
+
if (!isGlobal && shouldExcludeInstructionFile(chosen.filePath, root))
|
|
200
|
+
continue;
|
|
201
|
+
let realpath;
|
|
202
|
+
try {
|
|
203
|
+
realpath = fs.realpathSync(chosen.filePath);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
realpath = chosen.filePath;
|
|
207
|
+
}
|
|
208
|
+
if (registry.has(realpath))
|
|
209
|
+
continue;
|
|
210
|
+
let body;
|
|
211
|
+
try {
|
|
212
|
+
body = fs.readFileSync(chosen.filePath, 'utf-8');
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (!body.trim())
|
|
218
|
+
continue;
|
|
219
|
+
// User-authored, trusted files: warn (don't drop) on shell-exec-like
|
|
220
|
+
// patterns — dropping would break legitimate AGENTS.md that documents them.
|
|
221
|
+
if (containsDangerousPatterns(body)) {
|
|
222
|
+
logger.warn(`Context file ${chosen.filePath} contains shell-exec-like patterns; loading anyway (user-authored).`);
|
|
223
|
+
}
|
|
224
|
+
body = resolveImportDirectives(body, {
|
|
225
|
+
baseDir: probeDir,
|
|
226
|
+
projectRoot: root,
|
|
227
|
+
homeDir,
|
|
228
|
+
maxDepth: cfg.importMaxDepth,
|
|
229
|
+
maxBytes: cfg.importMaxBytes,
|
|
230
|
+
});
|
|
231
|
+
registry.add(realpath);
|
|
232
|
+
out.push({
|
|
233
|
+
path: chosen.filePath,
|
|
234
|
+
realpath,
|
|
235
|
+
relPath: displayPath(chosen.filePath, root, homeDir, isGlobal),
|
|
236
|
+
tier,
|
|
237
|
+
variant: chosen.variant,
|
|
238
|
+
body,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
return out;
|
|
242
|
+
}
|
|
243
|
+
/** Deterministic, abs-path-free display path for the origin header. */
|
|
244
|
+
function displayPath(filePath, root, homeDir, isGlobal) {
|
|
245
|
+
if (isGlobal) {
|
|
246
|
+
const rel = path.relative(path.join(homeDir, '.codebuddy'), filePath).replace(/\\/g, '/');
|
|
247
|
+
return `~/.codebuddy/${rel}`;
|
|
248
|
+
}
|
|
249
|
+
return path.relative(root, filePath).replace(/\\/g, '/') || path.basename(filePath);
|
|
250
|
+
}
|
|
251
|
+
/** Merge loaded sources under a budget, truncating the overflowing source. */
|
|
252
|
+
function budgetMerge(sources, budget) {
|
|
253
|
+
let total = 0;
|
|
254
|
+
let truncated = false;
|
|
255
|
+
const parts = [];
|
|
256
|
+
const outSources = [];
|
|
257
|
+
for (const s of sources) {
|
|
258
|
+
if (total >= budget) {
|
|
259
|
+
truncated = true;
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
let body = s.body;
|
|
263
|
+
let sourceTruncated = false;
|
|
264
|
+
const remaining = budget - total;
|
|
265
|
+
if (body.length > remaining) {
|
|
266
|
+
body = body.slice(0, remaining) + TRUNCATION_MARKER;
|
|
267
|
+
sourceTruncated = true;
|
|
268
|
+
truncated = true;
|
|
269
|
+
}
|
|
270
|
+
parts.push(`<!-- context: ${s.relPath} (${s.tier}) -->\n${body}`);
|
|
271
|
+
total += body.length;
|
|
272
|
+
outSources.push({
|
|
273
|
+
path: s.path,
|
|
274
|
+
realpath: s.realpath,
|
|
275
|
+
relPath: s.relPath,
|
|
276
|
+
tier: s.tier,
|
|
277
|
+
variant: s.variant,
|
|
278
|
+
bytes: body.length,
|
|
279
|
+
truncated: sourceTruncated,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return { text: parts.join('\n\n'), sources: outSources, bytes: total, truncated };
|
|
283
|
+
}
|
|
284
|
+
/** Shell-exec-like patterns — used to WARN (not drop) on user-authored files. */
|
|
285
|
+
function containsDangerousPatterns(content) {
|
|
286
|
+
return [
|
|
287
|
+
/\beval\s*\(/,
|
|
288
|
+
/\bnew\s+Function\s*\(/,
|
|
289
|
+
/\brequire\s*\(\s*['"]child_process['"]\s*\)/,
|
|
290
|
+
/\bexec(?:Sync)?\s*\(/,
|
|
291
|
+
/\bspawn(?:Sync)?\s*\(/,
|
|
292
|
+
/<script\b/i,
|
|
293
|
+
].some((p) => p.test(content));
|
|
294
|
+
}
|
|
295
|
+
//# sourceMappingURL=project-context.js.map
|
|
@@ -89,7 +89,7 @@ export interface DefaultAutonomousLoopOptions {
|
|
|
89
89
|
* local/network executor. The CLI uses this so the same config drives one-shot
|
|
90
90
|
* and continuous runs.
|
|
91
91
|
*/
|
|
92
|
-
export declare function createDefaultAutonomousLoop(opts?: DefaultAutonomousLoopOptions): FleetAutonomousLoop
|
|
92
|
+
export declare function createDefaultAutonomousLoop(opts?: DefaultAutonomousLoopOptions): Promise<FleetAutonomousLoop>;
|
|
93
93
|
/**
|
|
94
94
|
* Event source for {@link AutonomousDaemonConfig.eventSourceFactory}: watches the
|
|
95
95
|
* fleet queue file (`colab-tasks.json`) and calls `onChange` when it changes —
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* (see {@link createAgentTaskExecutor}).
|
|
14
14
|
*/
|
|
15
15
|
import { FleetColabStore } from '../fleet/colab-store.js';
|
|
16
|
-
import {
|
|
16
|
+
import { resolveLiveModelTierConfig, } from '../agent/model-tier.js';
|
|
17
17
|
import { FileWatcherTrigger } from '../agent/file-watcher-trigger.js';
|
|
18
18
|
import { FleetAutonomousLoop } from './autonomous-loop.js';
|
|
19
19
|
import { createLocalModelTaskExecutor } from './ollama-task-executor.js';
|
|
@@ -116,12 +116,12 @@ export class FleetAutonomousDaemon {
|
|
|
116
116
|
* local/network executor. The CLI uses this so the same config drives one-shot
|
|
117
117
|
* and continuous runs.
|
|
118
118
|
*/
|
|
119
|
-
export function createDefaultAutonomousLoop(opts = {}) {
|
|
119
|
+
export async function createDefaultAutonomousLoop(opts = {}) {
|
|
120
120
|
const store = new FleetColabStore({
|
|
121
121
|
...(opts.dir ? { dir: opts.dir } : {}),
|
|
122
122
|
...(opts.agentId ? { agentId: opts.agentId } : {}),
|
|
123
123
|
});
|
|
124
|
-
const tierConfig =
|
|
124
|
+
const tierConfig = await resolveLiveModelTierConfig();
|
|
125
125
|
const executorMode = opts.executorMode ?? (process.env['CODEBUDDY_AUTONOMY_EXECUTOR'] === 'agent' ? 'agent' : 'artifact');
|
|
126
126
|
const executor = executorMode === 'agent'
|
|
127
127
|
? createAgentTaskExecutor({ ...(opts.workspaceRoot ? { workspaceRoot: opts.workspaceRoot } : {}) })
|
|
@@ -37,6 +37,15 @@ export interface TaskExecutionResult {
|
|
|
37
37
|
output?: string;
|
|
38
38
|
}
|
|
39
39
|
export type TaskExecutor = (task: ColabTask, model: AutonomousModelChoice) => Promise<TaskExecutionResult>;
|
|
40
|
+
/**
|
|
41
|
+
* Idle-time self-improvement hook. Runs ONE bounded improvement cycle and reports
|
|
42
|
+
* whether it kept anything. Injected so the loop stays testable and the heavy
|
|
43
|
+
* engine is swappable; the default (lazy) hook runs the tool-improvement engine.
|
|
44
|
+
*/
|
|
45
|
+
export type SelfImproveHook = () => Promise<{
|
|
46
|
+
applied: boolean;
|
|
47
|
+
detail: string;
|
|
48
|
+
}>;
|
|
40
49
|
export interface AutonomousLoopConfig {
|
|
41
50
|
store: FleetColabStore;
|
|
42
51
|
tierConfig: ModelTierConfig;
|
|
@@ -49,9 +58,19 @@ export interface AutonomousLoopConfig {
|
|
|
49
58
|
* goal-mode tasks complete like plain tasks (no judge gate).
|
|
50
59
|
*/
|
|
51
60
|
goalJudge?: ColabGoalJudge;
|
|
61
|
+
/**
|
|
62
|
+
* Idle-time self-improvement. When the queue is empty AND
|
|
63
|
+
* `CODEBUDDY_SELF_IMPROVE=true`, run one bounded improvement cycle (cooldown-
|
|
64
|
+
* gated). Injected for tests; defaults to the tool-improvement engine.
|
|
65
|
+
*/
|
|
66
|
+
selfImprove?: SelfImproveHook;
|
|
67
|
+
/** Minimum ms between idle self-improvement cycles (default 15 min). */
|
|
68
|
+
selfImproveCooldownMs?: number;
|
|
69
|
+
/** Clock for the cooldown (tests). Default Date.now. */
|
|
70
|
+
now?: () => number;
|
|
52
71
|
}
|
|
53
72
|
export interface TickResult {
|
|
54
|
-
outcome: 'disabled' | 'idle' | 'completed' | 'failed' | 'saturated' | 'goal_continue' | 'goal_blocked';
|
|
73
|
+
outcome: 'disabled' | 'idle' | 'completed' | 'failed' | 'saturated' | 'goal_continue' | 'goal_blocked' | 'self_improved';
|
|
55
74
|
taskId?: string;
|
|
56
75
|
taskTitle?: string;
|
|
57
76
|
model?: AutonomousModelChoice;
|
|
@@ -71,7 +90,18 @@ export declare class FleetAutonomousLoop {
|
|
|
71
90
|
*/
|
|
72
91
|
private readonly failures;
|
|
73
92
|
private readonly goalJudge;
|
|
93
|
+
private readonly selfImprove;
|
|
94
|
+
private readonly selfImproveCooldownMs;
|
|
95
|
+
private readonly now;
|
|
96
|
+
private lastSelfImproveAt;
|
|
74
97
|
constructor(config: AutonomousLoopConfig);
|
|
98
|
+
/**
|
|
99
|
+
* Idle hook: when the queue is empty and self-improvement is opted in, run one
|
|
100
|
+
* bounded, cooldown-gated improvement cycle instead of sitting fully idle.
|
|
101
|
+
* Double-gated (idle + CODEBUDDY_SELF_IMPROVE) and bounded (cooldown + the
|
|
102
|
+
* engine no-ops once seed scenarios are covered). Never throws.
|
|
103
|
+
*/
|
|
104
|
+
private maybeSelfImprove;
|
|
75
105
|
/** Run a single autonomous tick. Never throws — failures are logged + reported. */
|
|
76
106
|
tick(): Promise<TickResult>;
|
|
77
107
|
/**
|
|
@@ -23,6 +23,53 @@
|
|
|
23
23
|
import { chooseAutonomousModel, } from '../agent/model-tier.js';
|
|
24
24
|
import { beginFleetWork, isFleetSaturated } from '../fleet/fleet-load.js';
|
|
25
25
|
import { DEFAULT_COLAB_GOAL_MAX_TURNS } from './colab-goal.js';
|
|
26
|
+
/**
|
|
27
|
+
* Default idle self-improvement: author tools for any seed scenario NOT yet in the
|
|
28
|
+
* evolutionary archive (so it stops once the seed benchmark is covered, and the
|
|
29
|
+
* bound persists across restarts). Auto-apply; gated by the caller on idle +
|
|
30
|
+
* CODEBUDDY_SELF_IMPROVE + cooldown.
|
|
31
|
+
*/
|
|
32
|
+
async function defaultSelfImproveHook() {
|
|
33
|
+
const { EvolutionaryArchive } = await import('../agent/self-improvement/evolutionary-archive.js');
|
|
34
|
+
const archived = new Set(new EvolutionaryArchive().list().map((e) => e.targetScenarioId));
|
|
35
|
+
// Tools first.
|
|
36
|
+
const { ToolImprovementEngine } = await import('../agent/self-improvement/tool-engine.js');
|
|
37
|
+
const { LlmToolProposer } = await import('../agent/self-improvement/llm-tool-proposer.js');
|
|
38
|
+
const { SEED_TOOL_SCENARIOS } = await import('../agent/self-improvement/tool-benchmark.js');
|
|
39
|
+
const uncoveredTools = SEED_TOOL_SCENARIOS.filter((s) => !archived.has(s.id));
|
|
40
|
+
if (uncoveredTools.length > 0) {
|
|
41
|
+
const r = await new ToolImprovementEngine({
|
|
42
|
+
scenarios: uncoveredTools,
|
|
43
|
+
proposer: new LlmToolProposer(),
|
|
44
|
+
autonomy: 'auto-apply',
|
|
45
|
+
}).runCycle();
|
|
46
|
+
if (r.applied)
|
|
47
|
+
return { applied: true, detail: `authored tool ${r.gate?.appliedRef}` };
|
|
48
|
+
}
|
|
49
|
+
// Then skills.
|
|
50
|
+
const { SkillImprovementEngine } = await import('../agent/self-improvement/skill-engine.js');
|
|
51
|
+
const { LlmSkillProposer } = await import('../agent/self-improvement/skill-proposer.js');
|
|
52
|
+
const { SEED_SKILL_SCENARIOS } = await import('../agent/self-improvement/skill-benchmark.js');
|
|
53
|
+
const uncoveredSkills = SEED_SKILL_SCENARIOS.filter((s) => !archived.has(s.id));
|
|
54
|
+
if (uncoveredSkills.length > 0) {
|
|
55
|
+
const r = await new SkillImprovementEngine({
|
|
56
|
+
scenarios: uncoveredSkills,
|
|
57
|
+
proposer: new LlmSkillProposer(),
|
|
58
|
+
autonomy: 'auto-apply',
|
|
59
|
+
}).runCycle();
|
|
60
|
+
if (r.applied)
|
|
61
|
+
return { applied: true, detail: `authored skill ${r.gate?.appliedRef}` };
|
|
62
|
+
}
|
|
63
|
+
return { applied: false, detail: 'all seed tool + skill scenarios already covered (or no proposal)' };
|
|
64
|
+
}
|
|
65
|
+
function resolveGoalMaxTurns(raw) {
|
|
66
|
+
return typeof raw === 'number' && Number.isSafeInteger(raw) && raw > 0
|
|
67
|
+
? raw
|
|
68
|
+
: DEFAULT_COLAB_GOAL_MAX_TURNS;
|
|
69
|
+
}
|
|
70
|
+
function resolveGoalTurnsUsed(raw) {
|
|
71
|
+
return typeof raw === 'number' && Number.isSafeInteger(raw) && raw >= 0 ? raw : 0;
|
|
72
|
+
}
|
|
26
73
|
export class FleetAutonomousLoop {
|
|
27
74
|
store;
|
|
28
75
|
tierConfig;
|
|
@@ -37,6 +84,10 @@ export class FleetAutonomousLoop {
|
|
|
37
84
|
*/
|
|
38
85
|
failures = new Map();
|
|
39
86
|
goalJudge;
|
|
87
|
+
selfImprove;
|
|
88
|
+
selfImproveCooldownMs;
|
|
89
|
+
now;
|
|
90
|
+
lastSelfImproveAt = Number.NEGATIVE_INFINITY;
|
|
40
91
|
constructor(config) {
|
|
41
92
|
this.store = config.store;
|
|
42
93
|
this.tierConfig = config.tierConfig;
|
|
@@ -44,6 +95,40 @@ export class FleetAutonomousLoop {
|
|
|
44
95
|
this.policy = config.policy ?? {};
|
|
45
96
|
this.enabled = config.enabled ?? (() => true);
|
|
46
97
|
this.goalJudge = config.goalJudge;
|
|
98
|
+
this.selfImprove = config.selfImprove ?? defaultSelfImproveHook;
|
|
99
|
+
this.selfImproveCooldownMs = config.selfImproveCooldownMs ?? 15 * 60 * 1000;
|
|
100
|
+
this.now = config.now ?? (() => Date.now());
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Idle hook: when the queue is empty and self-improvement is opted in, run one
|
|
104
|
+
* bounded, cooldown-gated improvement cycle instead of sitting fully idle.
|
|
105
|
+
* Double-gated (idle + CODEBUDDY_SELF_IMPROVE) and bounded (cooldown + the
|
|
106
|
+
* engine no-ops once seed scenarios are covered). Never throws.
|
|
107
|
+
*/
|
|
108
|
+
async maybeSelfImprove() {
|
|
109
|
+
if (process.env.CODEBUDDY_SELF_IMPROVE !== 'true') {
|
|
110
|
+
this.store.updatePresence({ status: 'idle', currentTask: null });
|
|
111
|
+
return { outcome: 'idle' };
|
|
112
|
+
}
|
|
113
|
+
const now = this.now();
|
|
114
|
+
if (now - this.lastSelfImproveAt < this.selfImproveCooldownMs) {
|
|
115
|
+
this.store.updatePresence({ status: 'idle', currentTask: null });
|
|
116
|
+
return { outcome: 'idle', detail: 'self-improve on cooldown' };
|
|
117
|
+
}
|
|
118
|
+
this.lastSelfImproveAt = now;
|
|
119
|
+
this.store.updatePresence({ status: 'active', currentTask: 'self-improvement' });
|
|
120
|
+
const doneLoad = beginFleetWork('autonomy.task');
|
|
121
|
+
try {
|
|
122
|
+
const r = await this.selfImprove();
|
|
123
|
+
return { outcome: r.applied ? 'self_improved' : 'idle', detail: r.detail };
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
return { outcome: 'idle', detail: `self-improve error: ${err instanceof Error ? err.message : String(err)}` };
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
doneLoad();
|
|
130
|
+
this.store.updatePresence({ status: 'idle', currentTask: null });
|
|
131
|
+
}
|
|
47
132
|
}
|
|
48
133
|
/** Run a single autonomous tick. Never throws — failures are logged + reported. */
|
|
49
134
|
async tick() {
|
|
@@ -63,8 +148,9 @@ export class FleetAutonomousLoop {
|
|
|
63
148
|
this.store.updatePresence({ status: 'active' });
|
|
64
149
|
const next = this.store.nextClaimable();
|
|
65
150
|
if (!next) {
|
|
66
|
-
|
|
67
|
-
|
|
151
|
+
// No real work — use the idle moment for one bounded self-improvement
|
|
152
|
+
// cycle (opt-in + cooldown), otherwise sit idle.
|
|
153
|
+
return this.maybeSelfImprove();
|
|
68
154
|
}
|
|
69
155
|
// Claim. If another agent won the race between read and claim, treat it as
|
|
70
156
|
// idle for this tick rather than crashing.
|
|
@@ -145,11 +231,12 @@ export class FleetAutonomousLoop {
|
|
|
145
231
|
}
|
|
146
232
|
if (verdict.verdict !== 'continue')
|
|
147
233
|
return null;
|
|
148
|
-
const maxTurns = task.goalMaxTurns
|
|
149
|
-
const turnsUsed = (task.goalTurnsUsed
|
|
234
|
+
const maxTurns = resolveGoalMaxTurns(task.goalMaxTurns);
|
|
235
|
+
const turnsUsed = resolveGoalTurnsUsed(task.goalTurnsUsed) + 1;
|
|
150
236
|
if (turnsUsed >= maxTurns) {
|
|
151
237
|
// Hermes rule: block for human review instead of spinning forever.
|
|
152
238
|
const reason = `goal budget exhausted (${turnsUsed}/${maxTurns}) — judge: ${verdict.reason}`;
|
|
239
|
+
this.store.recordGoalTurn(task.id, verdict.reason);
|
|
153
240
|
this.store.blockTask(task.id, reason);
|
|
154
241
|
this.store.appendWorklog({
|
|
155
242
|
agent: this.store.agentId,
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* "continue", and the turn budget is the backstop.
|
|
13
13
|
*/
|
|
14
14
|
import { CodeBuddyClient } from '../codebuddy/client.js';
|
|
15
|
+
import { resolveGoalJudgeClient } from '../goals/goal-judge-client.js';
|
|
15
16
|
import { judgeGoal } from '../goals/goal-judge.js';
|
|
16
17
|
import { resolveGoalsConfig } from '../goals/goal-manager.js';
|
|
17
18
|
import { logger } from '../utils/logger.js';
|
|
@@ -54,12 +55,19 @@ export function createColabGoalJudge() {
|
|
|
54
55
|
return async (task, result, model) => {
|
|
55
56
|
try {
|
|
56
57
|
const config = resolveGoalsConfig();
|
|
57
|
-
const
|
|
58
|
+
const apiKey = process.env.GROK_API_KEY || process.env.OPENAI_API_KEY || 'local';
|
|
59
|
+
const baseClient = new CodeBuddyClient(apiKey, model.model, model.baseUrl);
|
|
60
|
+
const client = await resolveGoalJudgeClient(baseClient, config.judgeModel, {
|
|
61
|
+
apiKey,
|
|
62
|
+
...(model.baseUrl ? { baseURL: model.baseUrl } : {}),
|
|
63
|
+
providerLabel: model.baseUrl?.includes(':11434') ? 'ollama' : model.tier,
|
|
64
|
+
});
|
|
58
65
|
return await judgeGoal(client, {
|
|
59
66
|
goal: goalTextForTask(task),
|
|
60
67
|
lastResponse: result.output || result.summary,
|
|
61
68
|
...(task.acceptanceCriteria?.length ? { subgoals: task.acceptanceCriteria } : {}),
|
|
62
69
|
...(config.judgeModel ? { model: config.judgeModel } : {}),
|
|
70
|
+
maxTokens: config.judgeMaxTokens,
|
|
63
71
|
timeoutMs: config.judgeTimeoutMs,
|
|
64
72
|
});
|
|
65
73
|
}
|
|
@@ -40,6 +40,12 @@ export interface JobExecutionResult {
|
|
|
40
40
|
skipReason?: string;
|
|
41
41
|
/** For watchdog jobs: false when any check produced an alert/error. */
|
|
42
42
|
watchdogOk?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Structured output data for cross-job data passing. When this job is part
|
|
45
|
+
* of a chain (`then`), its outputData is forwarded as the next job's
|
|
46
|
+
* inputData.
|
|
47
|
+
*/
|
|
48
|
+
outputData?: string;
|
|
43
49
|
}
|
|
44
50
|
export declare class CronAgentBridge extends EventEmitter {
|
|
45
51
|
private config;
|
|
@@ -48,11 +54,11 @@ export declare class CronAgentBridge extends EventEmitter {
|
|
|
48
54
|
/**
|
|
49
55
|
* Create a task executor function for the CronScheduler
|
|
50
56
|
*/
|
|
51
|
-
createTaskExecutor(): (job: CronJob) => Promise<unknown>;
|
|
57
|
+
createTaskExecutor(): (job: CronJob, inputData?: string) => Promise<unknown>;
|
|
52
58
|
/**
|
|
53
59
|
* Execute a cron job by creating an agent instance
|
|
54
60
|
*/
|
|
55
|
-
executeJob(job: CronJob): Promise<JobExecutionResult>;
|
|
61
|
+
executeJob(job: CronJob, inputData?: string): Promise<JobExecutionResult>;
|
|
56
62
|
/**
|
|
57
63
|
* Start a durable run record for a job execution, when an observability store
|
|
58
64
|
* is configured. Returns the run id, or undefined when recording is disabled
|
|
@@ -86,8 +92,11 @@ export declare class CronAgentBridge extends EventEmitter {
|
|
|
86
92
|
* WITHOUT instantiating a CodeBuddyAgent or calling any model provider.
|
|
87
93
|
* A non-zero exit (or timeout) throws so the run is recorded as failed and
|
|
88
94
|
* any chained `then` job does not fire.
|
|
95
|
+
*
|
|
96
|
+
* Returns both the combined output (for logging/delivery) and the separate
|
|
97
|
+
* stdout (for cross-job data passing, capped at 64KB by the script runner).
|
|
89
98
|
*/
|
|
90
|
-
private
|
|
99
|
+
private executeScriptTaskFull;
|
|
91
100
|
/**
|
|
92
101
|
* Execute a skill-type task — resolves a named skill from the SkillRegistry
|
|
93
102
|
* and runs it via the SkillExecutor. Without a configured tool executor the
|