@phuetz/code-buddy 2.0.0 → 2.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.fr.md +56 -0
- package/README.md +74 -16
- package/codebuddy-runtime.json +5 -5
- package/dist/agent/codebuddy-agent.d.ts +18 -0
- package/dist/agent/codebuddy-agent.js +45 -8
- package/dist/agent/execution/agent-executor.d.ts +0 -9
- package/dist/agent/execution/agent-executor.js +176 -12
- package/dist/agent/execution/context-pipeline.d.ts +9 -0
- package/dist/agent/execution/context-pipeline.js +64 -2
- package/dist/agent/execution/tool-loop-guard.d.ts +76 -0
- package/dist/agent/execution/tool-loop-guard.js +165 -0
- package/dist/agent/execution/tool-selection-strategy.d.ts +7 -0
- package/dist/agent/execution/tool-selection-strategy.js +32 -2
- package/dist/agent/facades/session-facade.d.ts +0 -6
- package/dist/agent/facades/session-facade.js +0 -45
- package/dist/agent/hermes-claw-migrate.d.ts +0 -21
- package/dist/agent/hermes-claw-migrate.js +9 -4
- package/dist/agent/hermes-protocol-gateways.js +8 -1
- package/dist/agent/repo-profiler.js +17 -4
- package/dist/agent/self-improvement/authored-tool-runtime.js +10 -2
- package/dist/agent/self-improvement/evolution/ast-novelty.d.ts +5 -0
- package/dist/agent/self-improvement/evolution/ast-novelty.js +58 -23
- package/dist/agent/self-improvement/evolution/code-variant-store.d.ts +3 -0
- package/dist/agent/self-improvement/evolution/evolution-engine.js +2 -0
- package/dist/agent/self-improvement/evolution/protected-paths.js +6 -0
- package/dist/agent/self-improvement/evolution/variant-fitness.d.ts +13 -2
- package/dist/agent/self-improvement/evolution/variant-fitness.js +184 -58
- package/dist/agent/self-improvement/evolution/worktree-scorer.d.ts +2 -0
- package/dist/agent/self-improvement/evolution/worktree-scorer.js +15 -0
- package/dist/agent/self-improvement/evolutionary-archive.d.ts +2 -1
- package/dist/agent/self-improvement/evolutionary-archive.js +34 -5
- package/dist/agent/self-improvement/proposal-store.d.ts +1 -0
- package/dist/agent/self-improvement/proposal-store.js +79 -17
- package/dist/agent/self-improvement/skill-apply-journal.d.ts +44 -0
- package/dist/agent/self-improvement/skill-apply-journal.js +178 -0
- package/dist/agent/self-improvement/skill-behavior-benchmark.d.ts +3 -0
- package/dist/agent/self-improvement/skill-behavior-benchmark.js +19 -0
- package/dist/agent/self-improvement/skill-behavior-gate.d.ts +34 -0
- package/dist/agent/self-improvement/skill-behavior-gate.js +96 -0
- package/dist/agent/self-improvement/skill-engine.d.ts +22 -0
- package/dist/agent/self-improvement/skill-engine.js +385 -13
- package/dist/agent/self-improvement/skill-gate.d.ts +2 -0
- package/dist/agent/self-improvement/skill-gate.js +17 -2
- package/dist/agent/self-improvement/skill-mutator.d.ts +6 -1
- package/dist/agent/self-improvement/skill-mutator.js +51 -15
- package/dist/agent/self-improvement/skill-types.d.ts +2 -1
- package/dist/agent/self-improvement/types.d.ts +11 -0
- package/dist/agent/tool-handler.d.ts +1 -1
- package/dist/agent/tool-handler.js +28 -9
- package/dist/auth/profile-manager.js +1 -1
- package/dist/channels/companion-channel-turn.d.ts +53 -4
- package/dist/channels/companion-channel-turn.js +218 -16
- package/dist/channels/slash-parity.d.ts +4 -6
- package/dist/channels/slash-parity.js +9 -44
- package/dist/cli/session-commands.d.ts +9 -0
- package/dist/cli/session-commands.js +45 -5
- package/dist/cli/session-picker.d.ts +43 -0
- package/dist/cli/session-picker.js +128 -0
- package/dist/codebuddy/a2a-call-tool-defs.d.ts +26 -0
- package/dist/codebuddy/a2a-call-tool-defs.js +12 -0
- package/dist/codebuddy/fleet-tool-defs.d.ts +2 -0
- package/dist/codebuddy/fleet-tool-defs.js +55 -0
- package/dist/codebuddy/providers/chatgpt-headers.d.ts +18 -0
- package/dist/codebuddy/providers/chatgpt-headers.js +37 -0
- package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +2 -1
- package/dist/codebuddy/providers/provider-chatgpt-responses.js +10 -23
- package/dist/codebuddy/providers/provider-openai-compat.d.ts +7 -0
- package/dist/codebuddy/providers/provider-openai-compat.js +29 -1
- package/dist/codebuddy/ragchat-tool-defs.d.ts +31 -0
- package/dist/codebuddy/ragchat-tool-defs.js +19 -0
- package/dist/codebuddy/resource-catalog-tool-defs.d.ts +28 -0
- package/dist/codebuddy/resource-catalog-tool-defs.js +18 -0
- package/dist/codebuddy/tool-definitions/agent-tools.js +2 -2
- package/dist/codebuddy/tool-definitions/code-exec-tools.js +5 -1
- package/dist/codebuddy/tool-definitions/cron-tools.js +6 -0
- package/dist/codebuddy/tool-definitions/self-describe-tools.js +6 -1
- package/dist/codebuddy/tools.d.ts +0 -6
- package/dist/codebuddy/tools.js +25 -3
- package/dist/commands/cli/daemon-commands.js +3 -1
- package/dist/commands/cli/evolve-command.js +21 -5
- package/dist/commands/cli/fleet-collaboration-commands.d.ts +2 -0
- package/dist/commands/cli/fleet-collaboration-commands.js +57 -0
- package/dist/commands/cli/fleet-commands.js +29 -0
- package/dist/commands/cli/fleet-mission-commands.d.ts +3 -0
- package/dist/commands/cli/fleet-mission-commands.js +72 -0
- package/dist/commands/cli/fleet-rooms-commands.d.ts +10 -0
- package/dist/commands/cli/fleet-rooms-commands.js +289 -0
- package/dist/commands/cli/tools-commands.js +2 -1
- package/dist/commands/cli/triage-command.d.ts +8 -0
- package/dist/commands/cli/triage-command.js +72 -0
- package/dist/commands/cli/utility-commands.js +25 -1
- package/dist/commands/client-dispatcher.d.ts +7 -0
- package/dist/commands/client-dispatcher.js +73 -21
- package/dist/commands/cron-cli/index.d.ts +3 -1
- package/dist/commands/cron-cli/index.js +25 -0
- package/dist/commands/device-auth.d.ts +10 -0
- package/dist/commands/device-auth.js +91 -0
- package/dist/commands/enhanced-command-handler.d.ts +9 -0
- package/dist/commands/enhanced-command-handler.js +28 -6
- package/dist/commands/handlers/agent-handlers.d.ts +1 -1
- package/dist/commands/handlers/agent-handlers.js +4 -4
- package/dist/commands/handlers/channel-handlers.js +53 -2
- package/dist/commands/handlers/export-handlers.d.ts +3 -2
- package/dist/commands/handlers/export-handlers.js +55 -28
- package/dist/commands/handlers/extra-handlers.js +5 -1
- package/dist/commands/handlers/grill-me-handler.js +26 -18
- package/dist/commands/handlers/memory-handlers.d.ts +6 -2
- package/dist/commands/handlers/memory-handlers.js +35 -15
- package/dist/commands/handlers/missing-handlers.d.ts +2 -2
- package/dist/commands/handlers/missing-handlers.js +12 -4
- package/dist/commands/handlers/resources-handler.d.ts +12 -0
- package/dist/commands/handlers/resources-handler.js +52 -0
- package/dist/commands/handlers/starter-handlers.d.ts +1 -1
- package/dist/commands/handlers/starter-handlers.js +32 -2
- package/dist/commands/handlers/switch-handler.d.ts +1 -1
- package/dist/commands/handlers/switch-handler.js +15 -0
- package/dist/commands/handlers/test-handlers.js +4 -86
- package/dist/commands/handlers/ui-handlers.js +11 -5
- package/dist/commands/login-chatgpt.d.ts +8 -0
- package/dist/commands/login-chatgpt.js +17 -0
- package/dist/commands/mcp-import.d.ts +11 -0
- package/dist/commands/mcp-import.js +67 -0
- package/dist/commands/mcp-invoke.d.ts +2 -0
- package/dist/commands/mcp-invoke.js +68 -0
- package/dist/commands/mcp.d.ts +13 -0
- package/dist/commands/mcp.js +66 -21
- package/dist/commands/pipeline.d.ts +7 -1
- package/dist/commands/pipeline.js +104 -7
- package/dist/commands/resources.d.ts +2 -0
- package/dist/commands/resources.js +64 -0
- package/dist/commands/skills-cli/index.js +35 -12
- package/dist/commands/slash/builtin-commands.js +16 -3
- package/dist/commands/slash/index.d.ts +3 -1
- package/dist/commands/slash/index.js +2 -0
- package/dist/commands/slash/surfaces.d.ts +53 -0
- package/dist/commands/slash/surfaces.js +177 -0
- package/dist/commands/slash/types.d.ts +17 -0
- package/dist/commands/try.d.ts +1 -1
- package/dist/commands/try.js +1 -1
- package/dist/companion/companion-identity.d.ts +43 -0
- package/dist/companion/companion-identity.js +136 -0
- package/dist/companion/companion-toolset.d.ts +66 -0
- package/dist/companion/companion-toolset.js +368 -0
- package/dist/companion/companion-turn.d.ts +10 -2
- package/dist/companion/companion-turn.js +38 -1
- package/dist/config/code-exec-policy.d.ts +25 -0
- package/dist/config/code-exec-policy.js +39 -0
- package/dist/config/model-tools.d.ts +9 -0
- package/dist/config/model-tools.js +103 -0
- package/dist/context/context-manager-v2.js +2 -2
- package/dist/context/lm-resizer-compressor.js +3 -2
- package/dist/context/lm-resizer-diagnostics.d.ts +9 -0
- package/dist/context/lm-resizer-diagnostics.js +30 -0
- package/dist/context/tool-output-masking.js +3 -4
- package/dist/curator/curator.d.ts +6 -0
- package/dist/curator/curator.js +53 -3
- package/dist/daemon/cron-agent-bridge.d.ts +6 -0
- package/dist/daemon/cron-agent-bridge.js +31 -4
- package/dist/doctor/index.d.ts +11 -2
- package/dist/doctor/index.js +84 -21
- package/dist/doctor/integrations.d.ts +197 -0
- package/dist/doctor/integrations.js +241 -0
- package/dist/doctor/triage.d.ts +108 -0
- package/dist/doctor/triage.js +304 -0
- package/dist/errors/crash-handler.js +2 -0
- package/dist/fleet/collaboration.d.ts +44 -0
- package/dist/fleet/collaboration.js +170 -0
- package/dist/fleet/fleet-listener.d.ts +1 -0
- package/dist/fleet/fleet-listener.js +87 -46
- package/dist/fleet/fleet-registry.d.ts +2 -0
- package/dist/fleet/resource-catalog.d.ts +245 -0
- package/dist/fleet/resource-catalog.js +211 -0
- package/dist/fleet/rooms/room-access.d.ts +138 -0
- package/dist/fleet/rooms/room-access.js +196 -0
- package/dist/fleet/rooms/room-client.d.ts +158 -0
- package/dist/fleet/rooms/room-client.js +685 -0
- package/dist/fleet/rooms/room-event.d.ts +139 -0
- package/dist/fleet/rooms/room-event.js +330 -0
- package/dist/fleet/rooms/room-filter.d.ts +48 -0
- package/dist/fleet/rooms/room-filter.js +152 -0
- package/dist/fleet/rooms/room-hub.d.ts +154 -0
- package/dist/fleet/rooms/room-hub.js +363 -0
- package/dist/fleet/rooms/room-identity.d.ts +26 -0
- package/dist/fleet/rooms/room-identity.js +82 -0
- package/dist/fleet/rooms/room-lock.d.ts +34 -0
- package/dist/fleet/rooms/room-lock.js +180 -0
- package/dist/fleet/rooms/room-observations.d.ts +7 -0
- package/dist/fleet/rooms/room-observations.js +61 -0
- package/dist/fleet/rooms/room-server.d.ts +28 -0
- package/dist/fleet/rooms/room-server.js +75 -0
- package/dist/fleet/rooms/room-store.d.ts +119 -0
- package/dist/fleet/rooms/room-store.js +448 -0
- package/dist/fleet/rooms/room-ws-bridge.d.ts +27 -0
- package/dist/fleet/rooms/room-ws-bridge.js +137 -0
- package/dist/fleet/sensory-mission-bridge.d.ts +42 -0
- package/dist/fleet/sensory-mission-bridge.js +136 -0
- package/dist/git/worktree-sessions.js +1 -0
- package/dist/harness/contract.d.ts +4 -4
- package/dist/harness/fleet-supervisor.d.ts +16 -0
- package/dist/harness/fleet-supervisor.js +64 -0
- package/dist/harness/index.d.ts +4 -0
- package/dist/harness/index.js +4 -0
- package/dist/harness/mission-runner.d.ts +3 -0
- package/dist/harness/mission-runner.js +53 -0
- package/dist/harness/mission-store.d.ts +93 -0
- package/dist/harness/mission-store.js +297 -0
- package/dist/harness/tool-harness.d.ts +56 -0
- package/dist/harness/tool-harness.js +153 -0
- package/dist/hooks/use-enhanced-input.js +48 -4
- package/dist/hooks/use-input-handler.d.ts +1 -0
- package/dist/hooks/use-input-handler.js +82 -22
- package/dist/hooks/use-input-history.js +1 -0
- package/dist/identity/lisa-introspection.js +5 -2
- package/dist/identity/operational-self-model.d.ts +2 -0
- package/dist/identity/operational-self-model.js +4 -0
- package/dist/index.js +52 -6
- package/dist/mcp/client.d.ts +11 -0
- package/dist/mcp/client.js +68 -3
- package/dist/mcp/config.d.ts +20 -0
- package/dist/mcp/config.js +121 -8
- package/dist/mcp/import-normalize.d.ts +12 -0
- package/dist/mcp/import-normalize.js +114 -0
- package/dist/mcp/mcp-oauth-constants.d.ts +8 -0
- package/dist/mcp/mcp-oauth-constants.js +9 -0
- package/dist/mcp/mcp-oauth-provider.d.ts +80 -0
- package/dist/mcp/mcp-oauth-provider.js +241 -0
- package/dist/mcp/mcp-oauth.d.ts +60 -1
- package/dist/mcp/mcp-oauth.js +241 -65
- package/dist/mcp/transports.d.ts +14 -5
- package/dist/mcp/transports.js +112 -101
- package/dist/mcp/types.d.ts +5 -0
- package/dist/media/comfyui-recipe-contract.d.ts +7 -7
- package/dist/memory/facts-memory.d.ts +11 -1
- package/dist/memory/facts-memory.js +47 -8
- package/dist/memory/memory-candidate-queue.d.ts +2 -2
- package/dist/memory/memory-candidate-queue.js +6 -6
- package/dist/memory/persistent-memory.d.ts +13 -8
- package/dist/memory/persistent-memory.js +77 -30
- package/dist/nodes/device-node.js +5 -9
- package/dist/observability/mobile-supervision-gateway-contract.js +20 -7
- package/dist/observability/run-event-writer.d.ts +21 -0
- package/dist/observability/run-event-writer.js +81 -0
- package/dist/observability/run-store.d.ts +5 -0
- package/dist/observability/run-store.js +18 -13
- package/dist/observability/run-trajectory.js +1 -1
- package/dist/optimization/prompt-cache.d.ts +19 -2
- package/dist/optimization/prompt-cache.js +41 -6
- package/dist/orchestration/orchestrator.d.ts +21 -0
- package/dist/orchestration/orchestrator.js +122 -15
- package/dist/persistence/session-content.d.ts +8 -0
- package/dist/persistence/session-content.js +76 -0
- package/dist/persistence/session-handoff.d.ts +64 -0
- package/dist/persistence/session-handoff.js +80 -0
- package/dist/persistence/session-history.d.ts +4 -0
- package/dist/persistence/session-history.js +38 -0
- package/dist/persistence/session-store.d.ts +9 -0
- package/dist/persistence/session-store.js +48 -20
- package/dist/personas/persona-manager.js +17 -1
- package/dist/plugins/code-explorer/CodeExplorerManager.d.ts +10 -4
- package/dist/plugins/code-explorer/CodeExplorerManager.js +69 -16
- package/dist/plugins/plugin-manager.js +10 -1
- package/dist/prompts/prompt-manager.js +11 -4
- package/dist/prompts/system-base.js +6 -2
- package/dist/protocols/a2a/codebuddy-executor.d.ts +11 -2
- package/dist/protocols/a2a/codebuddy-executor.js +9 -8
- package/dist/protocols/a2a/index.d.ts +1 -1
- package/dist/protocols/a2a/jsonrpc-v1.d.ts +41 -0
- package/dist/protocols/a2a/jsonrpc-v1.js +145 -0
- package/dist/protocols/a2a/peer-config.d.ts +20 -0
- package/dist/protocols/a2a/peer-config.js +55 -0
- package/dist/providers/codex-oauth.js +30 -6
- package/dist/providers/model-provider-compat.js +2 -1
- package/dist/sandbox/os-sandbox.d.ts +1 -1
- package/dist/scheduler/cron-scheduler.d.ts +15 -2
- package/dist/scheduler/cron-scheduler.js +35 -4
- package/dist/scheduler/job-notepad.d.ts +69 -0
- package/dist/scheduler/job-notepad.js +339 -0
- package/dist/security/audit-logger.d.ts +1 -1
- package/dist/security/compute-confinement.d.ts +4 -0
- package/dist/security/compute-confinement.js +89 -0
- package/dist/security/session-encryption.d.ts +4 -0
- package/dist/security/session-encryption.js +31 -0
- package/dist/sensory/voice-loop.d.ts +17 -0
- package/dist/sensory/voice-loop.js +72 -15
- package/dist/server/auth/device-session-context.d.ts +9 -0
- package/dist/server/auth/device-session-context.js +13 -0
- package/dist/server/auth/device-store.d.ts +89 -0
- package/dist/server/auth/device-store.js +261 -0
- package/dist/server/auth/device-token.d.ts +3 -0
- package/dist/server/auth/device-token.js +6 -0
- package/dist/server/auth/jwt.js +4 -0
- package/dist/server/index.d.ts +0 -10
- package/dist/server/index.js +28 -0
- package/dist/server/middleware/auth.js +3 -2
- package/dist/server/routes/a2a-jsonrpc.d.ts +9 -0
- package/dist/server/routes/a2a-jsonrpc.js +66 -0
- package/dist/server/routes/a2a-protocol.d.ts +12 -0
- package/dist/server/routes/a2a-protocol.js +1 -1
- package/dist/server/routes/device-auth.d.ts +4 -0
- package/dist/server/routes/device-auth.js +40 -0
- package/dist/server/types.d.ts +4 -0
- package/dist/server/websocket/handler.d.ts +13 -0
- package/dist/server/websocket/handler.js +102 -17
- package/dist/services/prompt-builder.d.ts +2 -0
- package/dist/services/prompt-builder.js +16 -6
- package/dist/services/runtime-settings-context.d.ts +81 -0
- package/dist/services/runtime-settings-context.js +79 -0
- package/dist/skills/executor.js +2 -0
- package/dist/skills/hub.js +16 -14
- package/dist/skills/local-inventory.d.ts +21 -0
- package/dist/skills/local-inventory.js +72 -0
- package/dist/skills/skill-loader.js +2 -2
- package/dist/skills/skill-usage-store.d.ts +49 -0
- package/dist/skills/skill-usage-store.js +121 -0
- package/dist/testing/ai-integration-tests.js +8 -5
- package/dist/themes/theme-manager.d.ts +12 -0
- package/dist/themes/theme-manager.js +29 -0
- package/dist/themes/theme-schema.d.ts +24 -24
- package/dist/tools/a2a-call-tool.d.ts +34 -0
- package/dist/tools/a2a-call-tool.js +75 -0
- package/dist/tools/bash/streaming-executor.d.ts +1 -0
- package/dist/tools/bash/streaming-executor.js +78 -90
- package/dist/tools/code-exec-preflight-runner.d.ts +41 -0
- package/dist/tools/code-exec-preflight-runner.js +254 -0
- package/dist/tools/code-exec-preflight.d.ts +44 -0
- package/dist/tools/code-exec-preflight.js +501 -0
- package/dist/tools/code-exec-tool.d.ts +48 -1
- package/dist/tools/code-exec-tool.js +191 -47
- package/dist/tools/comment-watcher.js +29 -12
- package/dist/tools/core-code-inspection.d.ts +56 -0
- package/dist/tools/core-code-inspection.js +86 -0
- package/dist/tools/create-skill-tool.d.ts +2 -3
- package/dist/tools/create-skill-tool.js +16 -75
- package/dist/tools/cronjob-tool.d.ts +1 -0
- package/dist/tools/cronjob-tool.js +1 -0
- package/dist/tools/env-doctor-tool.d.ts +2 -0
- package/dist/tools/env-doctor-tool.js +4 -3
- package/dist/tools/execute-code-rpc-invoker.d.ts +1 -1
- package/dist/tools/execute-code-rpc-invoker.js +35 -19
- package/dist/tools/execute-code-runner.d.ts +2 -0
- package/dist/tools/execute-code-runner.js +52 -17
- package/dist/tools/fleet-room-tool.d.ts +104 -0
- package/dist/tools/fleet-room-tool.js +124 -0
- package/dist/tools/integration-tool-hints.d.ts +4 -0
- package/dist/tools/integration-tool-hints.js +19 -0
- package/dist/tools/list-peers-tool.d.ts +3 -0
- package/dist/tools/list-peers-tool.js +11 -4
- package/dist/tools/media-generation-tool.d.ts +16 -1
- package/dist/tools/media-generation-tool.js +341 -9
- package/dist/tools/merge-conflict-tool.js +53 -25
- package/dist/tools/metadata.d.ts +1 -5
- package/dist/tools/metadata.js +30 -28
- package/dist/tools/peer-tool-invoke-tool.d.ts +61 -0
- package/dist/tools/peer-tool-invoke-tool.js +396 -0
- package/dist/tools/ragchat-tool.d.ts +39 -0
- package/dist/tools/ragchat-tool.js +105 -0
- package/dist/tools/registry/fleet-tools.d.ts +21 -3
- package/dist/tools/registry/fleet-tools.js +137 -4
- package/dist/tools/registry/index.d.ts +1 -1
- package/dist/tools/registry/index.js +2 -2
- package/dist/tools/registry/knowledge-tools.d.ts +2 -2
- package/dist/tools/registry/knowledge-tools.js +2 -2
- package/dist/tools/registry/lessons-tools.d.ts +7 -0
- package/dist/tools/registry/lessons-tools.js +43 -3
- package/dist/tools/registry/memory-tools.d.ts +1 -1
- package/dist/tools/registry/memory-tools.js +8 -8
- package/dist/tools/registry/research-tools.d.ts +1 -0
- package/dist/tools/registry/research-tools.js +4 -13
- package/dist/tools/registry/self-describe-tools.d.ts +1 -1
- package/dist/tools/registry/self-describe-tools.js +43 -30
- package/dist/tools/resource-catalog-tool.d.ts +38 -0
- package/dist/tools/resource-catalog-tool.js +46 -0
- package/dist/tools/skills-inspection-tool.js +30 -1
- package/dist/tools/stream-tool-output.d.ts +3 -0
- package/dist/tools/stream-tool-output.js +33 -0
- package/dist/tools/tool-call-scheduler.d.ts +10 -0
- package/dist/tools/tool-call-scheduler.js +58 -0
- package/dist/tools/tool-effect.d.ts +5 -0
- package/dist/tools/tool-effect.js +26 -0
- package/dist/tools/tool-search.d.ts +12 -11
- package/dist/tools/tool-search.js +67 -26
- package/dist/tools/types.d.ts +6 -0
- package/dist/ui/components/ChatHistory.d.ts +1 -1
- package/dist/ui/components/ChatHistory.js +2 -2
- package/dist/ui/components/ChatInput.d.ts +2 -1
- package/dist/ui/components/ChatInput.js +42 -88
- package/dist/ui/components/ChatInterface.d.ts +4 -2
- package/dist/ui/components/ChatInterface.js +27 -63
- package/dist/ui/components/CommandSuggestions.d.ts +0 -1
- package/dist/ui/components/CommandSuggestions.js +4 -6
- package/dist/ui/components/FileAutocomplete.js +1 -1
- package/dist/ui/components/KeyboardHelp.js +8 -6
- package/dist/ui/components/ModelSelection.js +6 -5
- package/dist/ui/components/StatusBar.d.ts +2 -1
- package/dist/ui/components/StatusBar.js +8 -7
- package/dist/ui/context/theme-context.js +3 -2
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/utils/markdown-renderer.js +4 -13
- package/dist/utils/atomic-write.d.ts +22 -0
- package/dist/utils/atomic-write.js +29 -0
- package/dist/utils/bounded-output.d.ts +16 -0
- package/dist/utils/bounded-output.js +63 -0
- package/dist/utils/config-validation/schema.d.ts +2 -2
- package/dist/utils/device-store-file.d.ts +4 -0
- package/dist/utils/device-store-file.js +40 -0
- package/dist/utils/export-manager.d.ts +9 -0
- package/dist/utils/export-manager.js +19 -0
- package/dist/utils/first-use-hints.d.ts +25 -0
- package/dist/utils/first-use-hints.js +58 -0
- package/dist/utils/graceful-shutdown.d.ts +8 -0
- package/dist/utils/graceful-shutdown.js +23 -7
- package/dist/utils/history-manager.js +1 -1
- package/dist/utils/update-notifier.js +16 -18
- package/dist/wizard/environment-detection.d.ts +5 -1
- package/dist/wizard/environment-detection.js +49 -7
- package/dist/wizard/onboarding.d.ts +5 -1
- package/dist/wizard/onboarding.js +90 -30
- package/dist/wizard/provider-onboarding.d.ts +2 -0
- package/dist/wizard/provider-onboarding.js +55 -17
- package/package.json +7 -3
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module agent/self-improvement/evolution/code-variant-store
|
|
8
8
|
*/
|
|
9
|
+
import type { FitnessReport } from './variant-fitness.js';
|
|
9
10
|
export interface VariantRecord {
|
|
10
11
|
id: string;
|
|
11
12
|
branch: string;
|
|
12
13
|
sha: string;
|
|
14
|
+
baselineSha?: string;
|
|
15
|
+
fitness?: FitnessReport;
|
|
13
16
|
/** Aggregate fitness in [0,1]. */
|
|
14
17
|
score: number;
|
|
15
18
|
passedAll: boolean;
|
|
@@ -366,6 +366,8 @@ export async function runEvolutionCycle(opts) {
|
|
|
366
366
|
id: variantId,
|
|
367
367
|
branch,
|
|
368
368
|
sha,
|
|
369
|
+
baselineSha: git(['rev-parse', '--verify', `${opts.baselineRef}^{commit}`], basePath).trim(),
|
|
370
|
+
fitness: report,
|
|
369
371
|
score: report.score,
|
|
370
372
|
passedAll: report.passedAll,
|
|
371
373
|
regressions: report.regressions,
|
|
@@ -29,8 +29,14 @@ export const PROTECTED_GLOBS = [
|
|
|
29
29
|
'src/agent/self-improvement/paired-runner.ts',
|
|
30
30
|
'src/agent/self-improvement/tool-proposer.ts',
|
|
31
31
|
'src/agent/self-improvement/self-knowledge.ts',
|
|
32
|
+
// Application receipts and replay must not be rewritten by a scored candidate.
|
|
33
|
+
'src/agent/self-improvement/skill-apply-journal.ts',
|
|
34
|
+
'src/agent/self-improvement/skill-engine.ts',
|
|
35
|
+
'src/agent/self-improvement/evolutionary-archive.ts',
|
|
32
36
|
// Security scanners the skill firewall relies on
|
|
33
37
|
'src/security/skill-scanner.ts',
|
|
38
|
+
'src/security/compute-confinement.ts',
|
|
39
|
+
'src/agent/self-improvement/authored-tool-runtime.ts',
|
|
34
40
|
'src/security/dangerous-patterns.ts',
|
|
35
41
|
// Tests that verify the gates / self-improvement (used by the fitness unit-tests component)
|
|
36
42
|
'tests/agent/self-improvement/**',
|
|
@@ -19,6 +19,8 @@ export interface FitnessContext {
|
|
|
19
19
|
checkoutDir: string;
|
|
20
20
|
/** Per-process timeout for a component's subprocess. */
|
|
21
21
|
timeoutMs?: number;
|
|
22
|
+
/** Cancel the subprocess and its process group. */
|
|
23
|
+
signal?: AbortSignal;
|
|
22
24
|
/** Env for subprocesses. Callers may pass a SCRUBBED env (no host secrets) for untrusted variants. */
|
|
23
25
|
env?: NodeJS.ProcessEnv;
|
|
24
26
|
}
|
|
@@ -46,7 +48,14 @@ export interface FitnessReport {
|
|
|
46
48
|
/** Component names that regressed vs a baseline (score dropped or pass→fail). */
|
|
47
49
|
regressions: string[];
|
|
48
50
|
}
|
|
49
|
-
/**
|
|
51
|
+
/** Execute checkout-owned JavaScript CLIs directly, preserving argv on every OS. */
|
|
52
|
+
export declare function runCheckoutCli(modulePath: string, args: string[], ctx: FitnessContext): Promise<{
|
|
53
|
+
code: number;
|
|
54
|
+
stdout: string;
|
|
55
|
+
stderr: string;
|
|
56
|
+
timedOut: boolean;
|
|
57
|
+
}>;
|
|
58
|
+
/** Run a bounded subprocess; terminate its process group on timeout. */
|
|
50
59
|
export declare function runProc(cmd: string, args: string[], ctx: FitnessContext): Promise<{
|
|
51
60
|
code: number;
|
|
52
61
|
stdout: string;
|
|
@@ -79,8 +88,10 @@ export declare function unitTestsComponent(patterns: string[], weight?: number):
|
|
|
79
88
|
* STOCHASTIC (LLM calls) — opt-in. Requires the checkout to be built (dist/index.js).
|
|
80
89
|
*/
|
|
81
90
|
export declare function evalTasksComponent(tasks?: string[], weight?: number): FitnessComponent;
|
|
91
|
+
/** Protected, offline task objective. Guard checks can be green while task success improves. */
|
|
92
|
+
export declare function harnessTasksComponent(weight?: number): FitnessComponent;
|
|
82
93
|
/** Default deterministic set → a fast, reproducible baseline (no LLM, no build). */
|
|
83
|
-
export declare function defaultDeterministicComponents(): FitnessComponent[];
|
|
94
|
+
export declare function defaultDeterministicComponents(guardsOnly?: boolean): FitnessComponent[];
|
|
84
95
|
export declare function detectRegressions(baseline: FitnessReport, current: ComponentResult[], eps?: number): string[];
|
|
85
96
|
/** Run components, aggregate to a weighted [0,1] fitness, flag regressions vs an optional baseline. */
|
|
86
97
|
export declare function computeFitness(ctx: FitnessContext, components: FitnessComponent[], baseline?: FitnessReport): Promise<FitnessReport>;
|
|
@@ -14,60 +14,131 @@
|
|
|
14
14
|
*
|
|
15
15
|
* @module agent/self-improvement/evolution/variant-fitness
|
|
16
16
|
*/
|
|
17
|
-
import { spawn } from 'child_process';
|
|
18
|
-
import { existsSync, readdirSync, statSync } from 'fs';
|
|
17
|
+
import { spawn, execFile } from 'child_process';
|
|
18
|
+
import { existsSync, readdirSync, statSync, mkdtempSync, readFileSync, rmSync } from 'fs';
|
|
19
19
|
import { join } from 'path';
|
|
20
|
+
import { tmpdir } from 'node:os';
|
|
21
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
22
|
+
import { createRequire } from 'node:module';
|
|
23
|
+
import { dirname, resolve } from 'node:path';
|
|
20
24
|
const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
|
|
21
25
|
function msg(e) {
|
|
22
26
|
return e instanceof Error ? e.message : String(e);
|
|
23
27
|
}
|
|
24
|
-
/**
|
|
28
|
+
/** Execute checkout-owned JavaScript CLIs directly, preserving argv on every OS. */
|
|
29
|
+
export function runCheckoutCli(modulePath, args, ctx) {
|
|
30
|
+
try {
|
|
31
|
+
const require = createRequire(resolve(ctx.checkoutDir, 'package.json'));
|
|
32
|
+
// Vitest exports its manifest but deliberately does not export its CLI subpath.
|
|
33
|
+
const cli = modulePath === 'vitest/vitest.mjs'
|
|
34
|
+
? join(dirname(require.resolve('vitest/package.json')), 'vitest.mjs')
|
|
35
|
+
: require.resolve(modulePath);
|
|
36
|
+
return runProc(process.execPath, [cli, ...args], ctx);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return Promise.resolve({ code: 1, stdout: '', stderr: msg(error), timedOut: false });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Run a bounded subprocess; terminate its process group on timeout. */
|
|
25
43
|
export function runProc(cmd, args, ctx) {
|
|
26
44
|
return new Promise((resolve) => {
|
|
45
|
+
const MAX = 1_000_000;
|
|
46
|
+
const windows = process.platform === 'win32';
|
|
27
47
|
let stdout = '';
|
|
28
48
|
let stderr = '';
|
|
29
49
|
let timedOut = false;
|
|
30
|
-
|
|
50
|
+
let settled = false;
|
|
51
|
+
let stopping = false;
|
|
31
52
|
let child;
|
|
53
|
+
let timer;
|
|
54
|
+
let escalation;
|
|
55
|
+
const stopTree = (signal) => {
|
|
56
|
+
const pid = child?.pid;
|
|
57
|
+
if (!pid)
|
|
58
|
+
return Promise.resolve();
|
|
59
|
+
if (windows) {
|
|
60
|
+
return new Promise(done => {
|
|
61
|
+
execFile('taskkill', ['/F', '/T', '/PID', String(pid)], { windowsHide: true, timeout: 2000 }, error => {
|
|
62
|
+
if (error) {
|
|
63
|
+
try {
|
|
64
|
+
child?.kill(signal);
|
|
65
|
+
}
|
|
66
|
+
catch { /* already gone */ }
|
|
67
|
+
}
|
|
68
|
+
done();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
process.kill(-pid, signal);
|
|
74
|
+
}
|
|
75
|
+
catch { /* group already gone */ }
|
|
76
|
+
return Promise.resolve();
|
|
77
|
+
};
|
|
78
|
+
const settle = (code) => {
|
|
79
|
+
if (settled)
|
|
80
|
+
return;
|
|
81
|
+
settled = true;
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
clearTimeout(escalation);
|
|
84
|
+
ctx.signal?.removeEventListener('abort', abort);
|
|
85
|
+
child?.stdout?.destroy();
|
|
86
|
+
child?.stderr?.destroy();
|
|
87
|
+
child?.unref();
|
|
88
|
+
resolve({ code, stdout, stderr, timedOut });
|
|
89
|
+
};
|
|
90
|
+
const stop = (code, timeout) => {
|
|
91
|
+
if (settled || stopping)
|
|
92
|
+
return;
|
|
93
|
+
stopping = true;
|
|
94
|
+
timedOut = timeout;
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
void stopTree('SIGTERM');
|
|
97
|
+
escalation = setTimeout(() => {
|
|
98
|
+
void stopTree('SIGKILL').finally(() => settle(code));
|
|
99
|
+
}, 250);
|
|
100
|
+
};
|
|
101
|
+
const abort = () => {
|
|
102
|
+
stderr = (stderr + 'Operation cancelled').slice(0, MAX);
|
|
103
|
+
stop(130, false);
|
|
104
|
+
};
|
|
105
|
+
if (ctx.signal?.aborted) {
|
|
106
|
+
stderr = 'Operation cancelled before spawn';
|
|
107
|
+
settle(130);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
32
110
|
try {
|
|
33
|
-
child = spawn(cmd, args, {
|
|
111
|
+
child = spawn(cmd, args, {
|
|
112
|
+
cwd: ctx.checkoutDir, env: ctx.env ?? process.env,
|
|
113
|
+
detached: !windows,
|
|
114
|
+
windowsHide: true,
|
|
115
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
116
|
+
});
|
|
34
117
|
}
|
|
35
|
-
catch (
|
|
36
|
-
|
|
118
|
+
catch (error) {
|
|
119
|
+
stderr = msg(error).slice(0, MAX);
|
|
120
|
+
settle(1);
|
|
37
121
|
return;
|
|
38
122
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
/* ignore */
|
|
54
|
-
}
|
|
55
|
-
}, ctx.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
56
|
-
child.stdout?.on('data', (d) => {
|
|
57
|
-
if (stdout.length < MAX)
|
|
58
|
-
stdout += d.toString();
|
|
123
|
+
ctx.signal?.addEventListener('abort', abort, { once: true });
|
|
124
|
+
timer = setTimeout(() => stop(1, true), ctx.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
125
|
+
if (ctx.signal?.aborted)
|
|
126
|
+
abort();
|
|
127
|
+
child.stdout?.on('data', chunk => { stdout += chunk.toString().slice(0, Math.max(0, MAX - stdout.length)); });
|
|
128
|
+
child.stderr?.on('data', chunk => { stderr += chunk.toString().slice(0, Math.max(0, MAX - stderr.length)); });
|
|
129
|
+
child.once('error', error => {
|
|
130
|
+
if (settled)
|
|
131
|
+
return;
|
|
132
|
+
stderr = (stderr + msg(error)).slice(0, MAX);
|
|
133
|
+
if (!stopping)
|
|
134
|
+
void stopTree('SIGKILL').finally(() => settle(1));
|
|
59
135
|
});
|
|
60
|
-
child.
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
});
|
|
64
|
-
child.on('error', (err) => {
|
|
136
|
+
child.once('close', code => {
|
|
137
|
+
if (settled || stopping)
|
|
138
|
+
return;
|
|
65
139
|
clearTimeout(timer);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
child.on('close', (code) => {
|
|
69
|
-
clearTimeout(timer);
|
|
70
|
-
resolve({ code: code ?? 1, stdout, stderr, timedOut });
|
|
140
|
+
// A completed evaluation must not leave background work in its checkout.
|
|
141
|
+
void stopTree('SIGKILL').finally(() => settle(code ?? 1));
|
|
71
142
|
});
|
|
72
143
|
});
|
|
73
144
|
}
|
|
@@ -76,8 +147,17 @@ function lastLines(s, n = 8) {
|
|
|
76
147
|
}
|
|
77
148
|
/** Parse vitest summary lines: "Tests 3 passed (3)" / "Tests 2 failed | 5 passed (7)". */
|
|
78
149
|
export function parseVitestCounts(out) {
|
|
79
|
-
|
|
80
|
-
|
|
150
|
+
try {
|
|
151
|
+
const report = JSON.parse(out);
|
|
152
|
+
const passed = report.numPassedTests;
|
|
153
|
+
const failed = report.numFailedTests;
|
|
154
|
+
if (typeof passed === 'number' && Number.isSafeInteger(passed) && passed >= 0 && typeof failed === 'number' && Number.isSafeInteger(failed) && failed >= 0)
|
|
155
|
+
return { passed, failed };
|
|
156
|
+
}
|
|
157
|
+
catch { /* Compatibility for recorded human-readable summaries. */ }
|
|
158
|
+
const summary = stripVTControlCharacters(out).split('\n').find(line => /^\s*Tests\s/.test(line)) ?? '';
|
|
159
|
+
const passed = /(\d+)\s+passed/.exec(summary);
|
|
160
|
+
const failed = /(\d+)\s+failed/.exec(summary);
|
|
81
161
|
return { passed: passed ? Number(passed[1]) : 0, failed: failed ? Number(failed[1]) : 0 };
|
|
82
162
|
}
|
|
83
163
|
export function listEvalTasks(checkoutDir) {
|
|
@@ -101,7 +181,7 @@ export function typecheckComponent(weight = 3) {
|
|
|
101
181
|
weight,
|
|
102
182
|
deterministic: true,
|
|
103
183
|
async run(ctx) {
|
|
104
|
-
const r = await
|
|
184
|
+
const r = await runCheckoutCli('typescript/bin/tsc', ['--noEmit'], ctx);
|
|
105
185
|
const passed = r.code === 0 && !r.timedOut;
|
|
106
186
|
return {
|
|
107
187
|
name: 'typecheck',
|
|
@@ -134,18 +214,27 @@ export function unitTestsComponent(patterns, weight = 4) {
|
|
|
134
214
|
weight,
|
|
135
215
|
deterministic: true,
|
|
136
216
|
async run(ctx) {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
217
|
+
const dir = mkdtempSync(join(tmpdir(), 'cb-fitness-'));
|
|
218
|
+
try {
|
|
219
|
+
const file = join(dir, 'vitest.json');
|
|
220
|
+
const r = await runCheckoutCli('vitest/vitest.mjs', ['run', ...patterns, '--reporter=json', `--outputFile=${file}`], ctx);
|
|
221
|
+
// A separate report avoids mixing tool/subprocess stdout with the machine-readable results.
|
|
222
|
+
if (statSync(file).size > 16 * 1024 * 1024)
|
|
223
|
+
throw new Error('Test report exceeds 16 MiB');
|
|
224
|
+
const raw = readFileSync(file, 'utf8');
|
|
225
|
+
const parsed = JSON.parse(raw);
|
|
226
|
+
if (!Number.isSafeInteger(parsed.numPassedTests) || !Number.isSafeInteger(parsed.numFailedTests) || Number(parsed.numPassedTests) < 0 || Number(parsed.numFailedTests) < 0)
|
|
227
|
+
throw new Error('Invalid test report counts');
|
|
228
|
+
const { passed, failed } = parseVitestCounts(raw);
|
|
229
|
+
const { score, passed: passedFlag } = scoreVitestRun(passed, failed, r.code, r.timedOut);
|
|
230
|
+
return { name: 'unit-tests', weight, score, passed: passedFlag, detail: `${passed} passed / ${failed} failed`, metrics: { passed, failed } };
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
return { name: 'unit-tests', weight, score: 0, passed: false, detail: `Missing or invalid Vitest report: ${msg(error)}` };
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
rmSync(dir, { recursive: true, force: true });
|
|
237
|
+
}
|
|
149
238
|
},
|
|
150
239
|
};
|
|
151
240
|
}
|
|
@@ -163,26 +252,60 @@ export function evalTasksComponent(tasks, weight = 5) {
|
|
|
163
252
|
if (all.length === 0) {
|
|
164
253
|
return { name: 'eval-tasks', weight, score: 0, passed: false, detail: 'no eval tasks found' };
|
|
165
254
|
}
|
|
255
|
+
const build = await runCheckoutCli('typescript/bin/tsc', [], ctx);
|
|
256
|
+
if (build.code !== 0 || build.timedOut)
|
|
257
|
+
return { name: 'eval-tasks', weight, score: 0, passed: false, detail: 'candidate build failed' };
|
|
166
258
|
let pass = 0;
|
|
259
|
+
let completed = true;
|
|
260
|
+
const outcomes = {};
|
|
167
261
|
for (const t of all) {
|
|
168
262
|
const r = await runProc(process.execPath, ['eval/run-task.mjs', t], ctx);
|
|
169
|
-
|
|
263
|
+
const ok = r.code === 0 && !r.timedOut;
|
|
264
|
+
if (ok)
|
|
170
265
|
pass++;
|
|
266
|
+
if (r.timedOut)
|
|
267
|
+
completed = false;
|
|
268
|
+
outcomes[`task:${t}`] = ok ? 1 : 0;
|
|
171
269
|
}
|
|
172
270
|
return {
|
|
173
271
|
name: 'eval-tasks',
|
|
174
272
|
weight,
|
|
175
273
|
score: pass / all.length,
|
|
176
|
-
|
|
274
|
+
// Completion is the gate; task success is a graded optimization signal.
|
|
275
|
+
passed: completed,
|
|
177
276
|
detail: `${pass}/${all.length} eval tasks passed`,
|
|
178
|
-
metrics: { passed: pass, total: all.length },
|
|
277
|
+
metrics: { passed: pass, total: all.length, ...outcomes },
|
|
179
278
|
};
|
|
180
279
|
},
|
|
181
280
|
};
|
|
182
281
|
}
|
|
282
|
+
/** Protected, offline task objective. Guard checks can be green while task success improves. */
|
|
283
|
+
export function harnessTasksComponent(weight = 5) {
|
|
284
|
+
return {
|
|
285
|
+
name: 'harness-tasks', weight, deterministic: true,
|
|
286
|
+
async run(ctx) {
|
|
287
|
+
const build = await runCheckoutCli('typescript/bin/tsc', [], ctx);
|
|
288
|
+
if (build.code !== 0 || build.timedOut)
|
|
289
|
+
return { name: 'harness-tasks', weight, score: 0, passed: false, detail: 'candidate build failed' };
|
|
290
|
+
const result = await runProc(process.execPath, ['eval/harness-benchmark.mjs'], ctx);
|
|
291
|
+
try {
|
|
292
|
+
const lines = result.stdout.trim().split('\n');
|
|
293
|
+
const report = JSON.parse(lines[lines.length - 1] ?? '');
|
|
294
|
+
const entries = Object.entries(report.results ?? {});
|
|
295
|
+
if (result.code !== 0 || result.timedOut || report.kind !== 'harness_benchmark' || !entries.length || entries.some(([, value]) => typeof value !== 'boolean'))
|
|
296
|
+
throw new Error('Invalid harness benchmark report');
|
|
297
|
+
const pass = entries.filter(([, value]) => value).length;
|
|
298
|
+
return { name: 'harness-tasks', weight, passed: true, score: pass / entries.length, detail: `${pass}/${entries.length} harness tasks passed`, metrics: Object.fromEntries(entries.map(([id, value]) => [`task:${id}`, value ? 1 : 0])) };
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
return { name: 'harness-tasks', weight, score: 0, passed: false, detail: 'harness benchmark failed or emitted an invalid report' };
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
}
|
|
183
306
|
/** Default deterministic set → a fast, reproducible baseline (no LLM, no build). */
|
|
184
|
-
export function defaultDeterministicComponents() {
|
|
185
|
-
return [typecheckComponent(), unitTestsComponent(['tests/agent/self-improvement'])];
|
|
307
|
+
export function defaultDeterministicComponents(guardsOnly = false) {
|
|
308
|
+
return [typecheckComponent(guardsOnly ? 0 : 3), unitTestsComponent(['tests/agent/self-improvement'], guardsOnly ? 0 : 4)];
|
|
186
309
|
}
|
|
187
310
|
// ---- Aggregation (pure, unit-tested) ------------------------------------------------------
|
|
188
311
|
export function detectRegressions(baseline, current, eps = 1e-9) {
|
|
@@ -192,9 +315,12 @@ export function detectRegressions(baseline, current, eps = 1e-9) {
|
|
|
192
315
|
const b = byName.get(r.name);
|
|
193
316
|
if (!b)
|
|
194
317
|
continue;
|
|
195
|
-
if (r.score < b.score - eps || (b.passed && !r.passed))
|
|
318
|
+
if (r.score < b.score - eps || (b.passed && !r.passed) || Object.entries(b.metrics ?? {}).some(([key, value]) => key.startsWith('task:') && (r.metrics?.[key] ?? -1) < value))
|
|
196
319
|
out.push(r.name);
|
|
197
320
|
}
|
|
321
|
+
for (const b of baseline.components)
|
|
322
|
+
if (!current.some(c => c.name === b.name))
|
|
323
|
+
out.push(b.name);
|
|
198
324
|
return out;
|
|
199
325
|
}
|
|
200
326
|
/** Run components, aggregate to a weighted [0,1] fitness, flag regressions vs an optional baseline. */
|
|
@@ -42,3 +42,5 @@ export interface ScoreBranchResult {
|
|
|
42
42
|
* cleanup; throws only if the worktree cannot be created.
|
|
43
43
|
*/
|
|
44
44
|
export declare function scoreBranchInWorktree(branch: string, opts?: ScoreBranchOptions): Promise<ScoreBranchResult>;
|
|
45
|
+
/** Score exactly a pinned commit, including when that commit is already checked out elsewhere. */
|
|
46
|
+
export declare function scoreReferenceInWorktree(ref: string, opts?: ScoreBranchOptions): Promise<ScoreBranchResult>;
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*
|
|
11
11
|
* @module agent/self-improvement/evolution/worktree-scorer
|
|
12
12
|
*/
|
|
13
|
+
import { execFileSync } from 'node:child_process';
|
|
14
|
+
import { randomUUID } from 'node:crypto';
|
|
13
15
|
import { existsSync, symlinkSync } from 'fs';
|
|
14
16
|
import { join } from 'path';
|
|
15
17
|
import { logger } from '../../../utils/logger.js';
|
|
@@ -96,4 +98,17 @@ function linkNodeModules(basePath, worktreePath) {
|
|
|
96
98
|
logger.warn(`[evolve] node_modules symlink failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
97
99
|
}
|
|
98
100
|
}
|
|
101
|
+
/** Score exactly a pinned commit, including when that commit is already checked out elsewhere. */
|
|
102
|
+
export async function scoreReferenceInWorktree(ref, opts = {}) {
|
|
103
|
+
const cwd = opts.basePath ?? process.cwd();
|
|
104
|
+
const sha = execFileSync('git', ['rev-parse', '--verify', `${ref}^{commit}`], { cwd, encoding: 'utf8' }).trim();
|
|
105
|
+
const branch = `codebuddy/evaluate/${randomUUID()}`;
|
|
106
|
+
execFileSync('git', ['branch', branch, sha], { cwd });
|
|
107
|
+
try {
|
|
108
|
+
return await scoreBranchInWorktree(branch, { ...opts, baselineRef: sha });
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
execFileSync('git', ['branch', '-D', branch], { cwd, stdio: 'ignore' });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
99
114
|
//# sourceMappingURL=worktree-scorer.js.map
|
|
@@ -13,6 +13,7 @@ export interface EvolutionaryArchiveOptions {
|
|
|
13
13
|
now?: () => Date;
|
|
14
14
|
}
|
|
15
15
|
export declare class EvolutionaryArchive {
|
|
16
|
+
readonly workDir: string;
|
|
16
17
|
private readonly filePath;
|
|
17
18
|
private readonly now;
|
|
18
19
|
constructor(options?: EvolutionaryArchiveOptions);
|
|
@@ -20,7 +21,7 @@ export declare class EvolutionaryArchive {
|
|
|
20
21
|
private read;
|
|
21
22
|
private write;
|
|
22
23
|
list(): ArchiveEntry[];
|
|
23
|
-
/** Append a validated improvement.
|
|
24
|
+
/** Append a validated improvement. Idempotent on kind+proposalId+targetScenarioId. */
|
|
24
25
|
append(entry: Omit<ArchiveEntry, 'createdAt' | 'reviewedBy'> & {
|
|
25
26
|
reviewedBy?: string;
|
|
26
27
|
}): ArchiveEntry;
|
|
@@ -11,17 +11,35 @@ import path from 'path';
|
|
|
11
11
|
import { readJsonAtomicSync, writeJsonAtomicSync } from '../../utils/atomic-write.js';
|
|
12
12
|
export const SELF_IMPROVEMENT_ARCHIVE_SCHEMA_VERSION = 1;
|
|
13
13
|
export class EvolutionaryArchive {
|
|
14
|
+
workDir;
|
|
14
15
|
filePath;
|
|
15
16
|
now;
|
|
16
17
|
constructor(options = {}) {
|
|
17
|
-
|
|
18
|
-
this.filePath = path.join(
|
|
18
|
+
this.workDir = options.workDir ?? process.cwd();
|
|
19
|
+
this.filePath = path.join(this.workDir, '.codebuddy', 'self-improvement', 'archive.json');
|
|
19
20
|
this.now = options.now ?? (() => new Date());
|
|
20
21
|
}
|
|
21
22
|
get path() {
|
|
22
23
|
return this.filePath;
|
|
23
24
|
}
|
|
24
|
-
read() {
|
|
25
|
+
read(strict = false) {
|
|
26
|
+
if (strict) {
|
|
27
|
+
let raw;
|
|
28
|
+
try {
|
|
29
|
+
raw = fs.readFileSync(this.filePath, 'utf8');
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (error.code === 'ENOENT') {
|
|
33
|
+
return { schemaVersion: SELF_IMPROVEMENT_ARCHIVE_SCHEMA_VERSION, entries: [] };
|
|
34
|
+
}
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
const parsed = JSON.parse(raw);
|
|
38
|
+
if (!parsed || parsed.schemaVersion !== SELF_IMPROVEMENT_ARCHIVE_SCHEMA_VERSION || !Array.isArray(parsed.entries)) {
|
|
39
|
+
throw new Error('Invalid evolution archive; refusing to overwrite evidence');
|
|
40
|
+
}
|
|
41
|
+
return parsed;
|
|
42
|
+
}
|
|
25
43
|
try {
|
|
26
44
|
const parsed = readJsonAtomicSync(this.filePath, {}, { mode: 0o600 });
|
|
27
45
|
if (Array.isArray(parsed.entries)) {
|
|
@@ -40,9 +58,20 @@ export class EvolutionaryArchive {
|
|
|
40
58
|
list() {
|
|
41
59
|
return this.read().entries;
|
|
42
60
|
}
|
|
43
|
-
/** Append a validated improvement.
|
|
61
|
+
/** Append a validated improvement. Idempotent on kind+proposalId+targetScenarioId. */
|
|
44
62
|
append(entry) {
|
|
45
|
-
const file = this.read();
|
|
63
|
+
const file = this.read(true);
|
|
64
|
+
const existing = file.entries.find((stored) => stored.proposalId === entry.proposalId &&
|
|
65
|
+
stored.kind === entry.kind &&
|
|
66
|
+
stored.targetScenarioId === entry.targetScenarioId);
|
|
67
|
+
if (existing) {
|
|
68
|
+
const previousSha = existing.evidence?.artifactSha256;
|
|
69
|
+
const nextSha = entry.evidence?.artifactSha256;
|
|
70
|
+
if (typeof previousSha === 'string' && typeof nextSha === 'string' && previousSha !== nextSha) {
|
|
71
|
+
throw new Error(`archive fingerprint conflict for ${entry.kind}:${entry.proposalId}:${entry.targetScenarioId}`);
|
|
72
|
+
}
|
|
73
|
+
return existing;
|
|
74
|
+
}
|
|
46
75
|
const stored = {
|
|
47
76
|
...entry,
|
|
48
77
|
createdAt: this.now().toISOString(),
|
|
@@ -36,6 +36,7 @@ export declare class PendingProposalStore {
|
|
|
36
36
|
readonly dir: string;
|
|
37
37
|
constructor(options?: PendingProposalStoreOptions);
|
|
38
38
|
pathFor(kind: PendingProposalKind, scenarioId: string): string;
|
|
39
|
+
legacyPathFor(kind: PendingProposalKind, scenarioId: string): string;
|
|
39
40
|
saveTool(record: Omit<PendingToolProposalRecord, 'schemaVersion' | 'kind'>): PendingToolProposalRecord;
|
|
40
41
|
saveSkill(record: Omit<PendingSkillProposalRecord, 'schemaVersion' | 'kind'>): PendingSkillProposalRecord;
|
|
41
42
|
loadTool(scenarioId: string): PendingToolProposalRecord | null;
|
|
@@ -10,11 +10,15 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
|
+
import { createHash } from 'crypto';
|
|
13
14
|
import { readJsonAtomicSync, writeJsonAtomicSync } from '../../utils/atomic-write.js';
|
|
14
15
|
export const PENDING_PROPOSAL_SCHEMA_VERSION = 1;
|
|
15
16
|
function safeSegment(id) {
|
|
16
17
|
const cleaned = id.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
17
|
-
return cleaned || 'proposal';
|
|
18
|
+
return cleaned.slice(0, 80) || 'proposal';
|
|
19
|
+
}
|
|
20
|
+
function scenarioHash(id) {
|
|
21
|
+
return createHash('sha256').update(id).digest('hex').slice(0, 16);
|
|
18
22
|
}
|
|
19
23
|
export class PendingProposalStore {
|
|
20
24
|
dir;
|
|
@@ -23,6 +27,9 @@ export class PendingProposalStore {
|
|
|
23
27
|
this.dir = path.join(root, '.codebuddy', 'self-improvement', 'proposals');
|
|
24
28
|
}
|
|
25
29
|
pathFor(kind, scenarioId) {
|
|
30
|
+
return path.join(this.dir, `${kind}-${safeSegment(scenarioId)}-${scenarioHash(scenarioId)}.json`);
|
|
31
|
+
}
|
|
32
|
+
legacyPathFor(kind, scenarioId) {
|
|
26
33
|
return path.join(this.dir, `${kind}-${safeSegment(scenarioId)}.json`);
|
|
27
34
|
}
|
|
28
35
|
saveTool(record) {
|
|
@@ -44,38 +51,93 @@ export class PendingProposalStore {
|
|
|
44
51
|
return stored;
|
|
45
52
|
}
|
|
46
53
|
loadTool(scenarioId) {
|
|
47
|
-
const parsed = this.read(this.pathFor('tool', scenarioId));
|
|
48
|
-
if (
|
|
49
|
-
return
|
|
50
|
-
|
|
54
|
+
const parsed = this.read(this.pathFor('tool', scenarioId), 'tool', scenarioId);
|
|
55
|
+
if (parsed && parsed.kind === 'tool')
|
|
56
|
+
return parsed;
|
|
57
|
+
const legacyParsed = this.read(this.legacyPathFor('tool', scenarioId), 'tool', scenarioId);
|
|
58
|
+
if (legacyParsed && legacyParsed.kind === 'tool')
|
|
59
|
+
return legacyParsed;
|
|
60
|
+
return null;
|
|
51
61
|
}
|
|
52
62
|
loadSkill(scenarioId) {
|
|
53
|
-
const parsed = this.read(this.pathFor('skill', scenarioId));
|
|
54
|
-
if (
|
|
55
|
-
return
|
|
56
|
-
|
|
63
|
+
const parsed = this.read(this.pathFor('skill', scenarioId), 'skill', scenarioId);
|
|
64
|
+
if (parsed && parsed.kind === 'skill')
|
|
65
|
+
return parsed;
|
|
66
|
+
const legacyParsed = this.read(this.legacyPathFor('skill', scenarioId), 'skill', scenarioId);
|
|
67
|
+
if (legacyParsed && legacyParsed.kind === 'skill')
|
|
68
|
+
return legacyParsed;
|
|
69
|
+
return null;
|
|
57
70
|
}
|
|
58
71
|
remove(kind, scenarioId) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
let removed = false;
|
|
73
|
+
const canonicalPath = this.pathFor(kind, scenarioId);
|
|
74
|
+
if (fs.existsSync(canonicalPath)) {
|
|
75
|
+
const parsed = this.read(canonicalPath, kind, scenarioId);
|
|
76
|
+
if (parsed) {
|
|
77
|
+
fs.unlinkSync(canonicalPath);
|
|
78
|
+
removed = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const legacyPath = this.legacyPathFor(kind, scenarioId);
|
|
82
|
+
if (fs.existsSync(legacyPath)) {
|
|
83
|
+
const parsed = this.read(legacyPath, kind, scenarioId);
|
|
84
|
+
if (parsed && parsed.scenarioId === scenarioId) {
|
|
85
|
+
fs.unlinkSync(legacyPath);
|
|
86
|
+
removed = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return removed;
|
|
64
90
|
}
|
|
65
91
|
write(filePath, record) {
|
|
66
92
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
67
93
|
writeJsonAtomicSync(filePath, record, { mode: 0o600 });
|
|
68
94
|
}
|
|
69
|
-
read(filePath) {
|
|
95
|
+
read(filePath, expectedKind, expectedScenarioId) {
|
|
70
96
|
try {
|
|
97
|
+
if (!fs.existsSync(filePath))
|
|
98
|
+
return null;
|
|
71
99
|
const parsed = readJsonAtomicSync(filePath, null, { mode: 0o600 });
|
|
72
|
-
if (!parsed)
|
|
100
|
+
if (!parsed || typeof parsed !== 'object')
|
|
73
101
|
return null;
|
|
74
102
|
if (parsed.schemaVersion !== PENDING_PROPOSAL_SCHEMA_VERSION)
|
|
75
103
|
return null;
|
|
76
104
|
if (parsed.kind !== 'tool' && parsed.kind !== 'skill')
|
|
77
105
|
return null;
|
|
78
|
-
if (
|
|
106
|
+
if (expectedKind && parsed.kind !== expectedKind)
|
|
107
|
+
return null;
|
|
108
|
+
if (typeof parsed.scenarioId !== 'string' || !parsed.scenarioId.trim())
|
|
109
|
+
return null;
|
|
110
|
+
if (expectedScenarioId !== undefined && parsed.scenarioId !== expectedScenarioId)
|
|
111
|
+
return null;
|
|
112
|
+
// Validate proposal
|
|
113
|
+
const proposal = parsed.proposal;
|
|
114
|
+
if (!proposal || typeof proposal !== 'object')
|
|
115
|
+
return null;
|
|
116
|
+
if (typeof proposal.id !== 'string' || !proposal.id.trim())
|
|
117
|
+
return null;
|
|
118
|
+
if (proposal.targetScenarioId !== parsed.scenarioId)
|
|
119
|
+
return null;
|
|
120
|
+
// Validate usable spec fields
|
|
121
|
+
const spec = proposal.spec;
|
|
122
|
+
if (!spec || typeof spec !== 'object')
|
|
123
|
+
return null;
|
|
124
|
+
if (typeof spec.name !== 'string' || !spec.name.trim())
|
|
125
|
+
return null;
|
|
126
|
+
if (parsed.kind === 'tool') {
|
|
127
|
+
if (typeof spec.code !== 'string')
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
else if (parsed.kind === 'skill') {
|
|
131
|
+
if (typeof spec.content !== 'string')
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
// Validate gate identities
|
|
135
|
+
const gate = parsed.gate;
|
|
136
|
+
if (!gate || typeof gate !== 'object')
|
|
137
|
+
return null;
|
|
138
|
+
if (typeof gate.accepted !== 'boolean')
|
|
139
|
+
return null;
|
|
140
|
+
if (gate.scenarioId !== parsed.scenarioId || gate.proposalId !== proposal.id)
|
|
79
141
|
return null;
|
|
80
142
|
return parsed;
|
|
81
143
|
}
|