@phuetz/code-buddy 2.0.0 → 2.1.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 +73 -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 +165 -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.js +19 -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 +1 -0
- package/dist/codebuddy/fleet-tool-defs.js +24 -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.js +1 -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 +4 -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.js +4 -0
- 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 +4 -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 +179 -0
- package/dist/doctor/integrations.js +227 -0
- package/dist/doctor/triage.d.ts +108 -0
- package/dist/doctor/triage.js +304 -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.js +6 -0
- 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/transports.d.ts +10 -5
- package/dist/mcp/transports.js +61 -99
- 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 +13 -5
- package/dist/services/runtime-settings-context.d.ts +73 -0
- package/dist/services/runtime-settings-context.js +69 -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 +21 -28
- 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 +9 -0
- package/dist/tools/registry/fleet-tools.js +22 -1
- 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/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/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
|
@@ -5,12 +5,18 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module agent/self-improvement/skill-engine
|
|
7
7
|
*/
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
10
|
+
import { evaluateSkillBehavior } from './skill-behavior-gate.js';
|
|
11
|
+
import { SKILL_BEHAVIOR_TASKS } from './skill-behavior-benchmark.js';
|
|
8
12
|
import { EvolutionaryArchive } from './evolutionary-archive.js';
|
|
9
13
|
import { resolveAutonomy } from './engine.js';
|
|
10
14
|
import { PendingProposalStore } from './proposal-store.js';
|
|
15
|
+
import { SkillApplyJournal } from './skill-apply-journal.js';
|
|
11
16
|
import { validateSkillProposal } from './skill-gate.js';
|
|
12
|
-
import { LiveSkillMutator } from './skill-mutator.js';
|
|
17
|
+
import { ensureFrontmatter, isAuthoredSkillName, LiveSkillMutator } from './skill-mutator.js';
|
|
13
18
|
export class SkillImprovementEngine {
|
|
19
|
+
evaluateBehavior;
|
|
14
20
|
scenarios;
|
|
15
21
|
proposer;
|
|
16
22
|
mutator;
|
|
@@ -18,27 +24,64 @@ export class SkillImprovementEngine {
|
|
|
18
24
|
autonomy;
|
|
19
25
|
now;
|
|
20
26
|
proposals;
|
|
27
|
+
applyJournal;
|
|
28
|
+
workDir;
|
|
29
|
+
readInstalledSkill;
|
|
21
30
|
covered = new Set();
|
|
22
31
|
attempted = new Set();
|
|
23
32
|
constructor(options) {
|
|
24
33
|
this.scenarios = options.scenarios;
|
|
34
|
+
this.evaluateBehavior = options.evaluateBehavior ?? ((content, id) => evaluateSkillBehavior(content, SKILL_BEHAVIOR_TASKS[id] ?? []));
|
|
25
35
|
this.proposer = options.proposer;
|
|
26
|
-
this.mutator = options.mutator ?? new LiveSkillMutator();
|
|
27
36
|
this.archive = options.archive ?? new EvolutionaryArchive({ workDir: options.workDir });
|
|
37
|
+
this.workDir = options.workDir ?? this.archive.workDir;
|
|
38
|
+
this.mutator = options.mutator ?? new LiveSkillMutator(path.join(this.workDir, '.codebuddy', 'skills'));
|
|
28
39
|
this.autonomy = options.autonomy ?? resolveAutonomy();
|
|
29
40
|
this.now = options.now ?? (() => new Date());
|
|
30
|
-
this.proposals = options.proposalStore ?? new PendingProposalStore({ workDir:
|
|
41
|
+
this.proposals = options.proposalStore ?? new PendingProposalStore({ workDir: this.workDir });
|
|
42
|
+
this.applyJournal = options.applyJournal ?? new SkillApplyJournal({ workDir: this.workDir, now: this.now });
|
|
43
|
+
this.readInstalledSkill = options.readInstalledSkill ?? ((name) => this.mutator.readInstalled?.(name) ?? null);
|
|
31
44
|
}
|
|
32
45
|
async runCycle() {
|
|
33
46
|
const startedAt = this.now().toISOString();
|
|
34
47
|
const base = { kind: 'skill_improvement_cycle', startedAt, autonomy: this.autonomy };
|
|
48
|
+
const pendingPeek = this.peekPendingRecords();
|
|
49
|
+
if (!pendingPeek.ok) {
|
|
50
|
+
return {
|
|
51
|
+
...base,
|
|
52
|
+
selectedScenarioId: null,
|
|
53
|
+
proposalId: null,
|
|
54
|
+
gate: null,
|
|
55
|
+
applied: false,
|
|
56
|
+
notes: ['apply journal unreadable; refusing to install', pendingPeek.error],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (this.autonomy !== 'auto-apply') {
|
|
60
|
+
const pending = pendingPeek.records[0];
|
|
61
|
+
if (pending) {
|
|
62
|
+
return {
|
|
63
|
+
...base,
|
|
64
|
+
selectedScenarioId: pending.scenarioId,
|
|
65
|
+
proposalId: pending.proposalId,
|
|
66
|
+
gate: null,
|
|
67
|
+
applied: false,
|
|
68
|
+
applyPhase: pending.phase,
|
|
69
|
+
notes: ['pending skill apply requires auto-apply; not installed'],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const resumed = this.resumePending(base, pendingPeek.records);
|
|
75
|
+
if (resumed)
|
|
76
|
+
return resumed;
|
|
77
|
+
}
|
|
35
78
|
for (const scenario of this.scenarios) {
|
|
36
79
|
if (this.covered.has(scenario.id) || this.attempted.has(scenario.id))
|
|
37
80
|
continue;
|
|
38
81
|
if (this.autonomy === 'auto-apply') {
|
|
39
82
|
const pending = this.proposals.loadSkill(scenario.id);
|
|
40
83
|
if (pending) {
|
|
41
|
-
const appliedPending = this.finishCycle(base, scenario, pending.proposal, true);
|
|
84
|
+
const appliedPending = await this.finishCycle(base, scenario, pending.proposal, true);
|
|
42
85
|
if (appliedPending.applied)
|
|
43
86
|
this.proposals.remove('skill', scenario.id);
|
|
44
87
|
return appliedPending;
|
|
@@ -76,10 +119,26 @@ export class SkillImprovementEngine {
|
|
|
76
119
|
}
|
|
77
120
|
return results;
|
|
78
121
|
}
|
|
79
|
-
finishCycle(base, scenario, proposal, keepOnAccept) {
|
|
122
|
+
async finishCycle(base, scenario, proposal, keepOnAccept) {
|
|
123
|
+
let gate = validateSkillProposal(proposal, scenario, this.mutator, { keepOnAccept: false });
|
|
124
|
+
let behavior;
|
|
125
|
+
if (gate.accepted && keepOnAccept) {
|
|
126
|
+
const content = proposal.spec.content;
|
|
127
|
+
try {
|
|
128
|
+
behavior = await this.evaluateBehavior(content, scenario.id);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
behavior = { accepted: false, wins: 0, losses: 0, tested: 0, cases: [], error: error instanceof Error ? error.message : String(error) };
|
|
132
|
+
}
|
|
133
|
+
if (content !== proposal.spec.content)
|
|
134
|
+
behavior = { ...behavior, accepted: false, error: 'Proposal changed during behavioral validation' };
|
|
135
|
+
if (!behavior.accepted) {
|
|
136
|
+
this.attempted.add(scenario.id);
|
|
137
|
+
return { ...base, selectedScenarioId: scenario.id, proposalId: proposal.id, gate: { ...gate, accepted: false, reasons: ['Behavioral validation did not demonstrate a gain without regression'] }, behavior, applied: false, notes: [behavior.error ?? 'No verified behavioral improvement; proposal not installed'] };
|
|
138
|
+
}
|
|
139
|
+
return this.settleApplication(base, scenario, proposal, behavior, gate);
|
|
140
|
+
}
|
|
80
141
|
this.attempted.add(scenario.id);
|
|
81
|
-
const gate = validateSkillProposal(proposal, scenario, this.mutator, { keepOnAccept });
|
|
82
|
-
const applied = gate.accepted && !!gate.appliedRef;
|
|
83
142
|
if (gate.accepted && !keepOnAccept) {
|
|
84
143
|
this.proposals.saveSkill({
|
|
85
144
|
scenarioId: scenario.id,
|
|
@@ -88,8 +147,181 @@ export class SkillImprovementEngine {
|
|
|
88
147
|
gate,
|
|
89
148
|
});
|
|
90
149
|
}
|
|
91
|
-
|
|
92
|
-
|
|
150
|
+
return {
|
|
151
|
+
...base,
|
|
152
|
+
selectedScenarioId: scenario.id,
|
|
153
|
+
proposalId: proposal.id,
|
|
154
|
+
gate,
|
|
155
|
+
applied: false,
|
|
156
|
+
notes: gate.reasons,
|
|
157
|
+
...(behavior ? { behavior } : {}),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
peekPendingRecords() {
|
|
161
|
+
try {
|
|
162
|
+
return { ok: true, records: this.applyJournal.listPending() };
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
resumePending(base, pending) {
|
|
169
|
+
const record = pending.find((item) => !this.attempted.has(item.scenarioId));
|
|
170
|
+
if (!record)
|
|
171
|
+
return null;
|
|
172
|
+
const scenario = this.scenarios.find((item) => item.id === record.scenarioId);
|
|
173
|
+
if (!scenario) {
|
|
174
|
+
return {
|
|
175
|
+
...base,
|
|
176
|
+
selectedScenarioId: record.scenarioId,
|
|
177
|
+
proposalId: record.proposalId,
|
|
178
|
+
gate: null,
|
|
179
|
+
applied: false,
|
|
180
|
+
notes: ['pending apply scenario is missing; not installed'],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const identityError = savedIdentityMismatch(record, scenario, record.proposal);
|
|
184
|
+
if (identityError) {
|
|
185
|
+
this.attempted.add(scenario.id);
|
|
186
|
+
return {
|
|
187
|
+
...base,
|
|
188
|
+
selectedScenarioId: scenario.id,
|
|
189
|
+
proposalId: record.proposalId,
|
|
190
|
+
gate: null,
|
|
191
|
+
applied: false,
|
|
192
|
+
applyPhase: 'conflict',
|
|
193
|
+
notes: [identityError],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const coverage = validateSkillProposal(record.proposal, scenario, this.mutator, { keepOnAccept: false });
|
|
197
|
+
if (!coverage.accepted) {
|
|
198
|
+
this.attempted.add(scenario.id);
|
|
199
|
+
return {
|
|
200
|
+
...base,
|
|
201
|
+
selectedScenarioId: scenario.id,
|
|
202
|
+
proposalId: record.proposal.id,
|
|
203
|
+
gate: coverage,
|
|
204
|
+
applied: false,
|
|
205
|
+
notes: coverage.reasons,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
if (!record.behavior) {
|
|
209
|
+
this.attempted.add(scenario.id);
|
|
210
|
+
return {
|
|
211
|
+
...base,
|
|
212
|
+
selectedScenarioId: scenario.id,
|
|
213
|
+
proposalId: record.proposal.id,
|
|
214
|
+
gate: {
|
|
215
|
+
...coverage,
|
|
216
|
+
accepted: false,
|
|
217
|
+
rejectionReason: 'behavior-required',
|
|
218
|
+
reasons: ['pending apply is missing a behavioral receipt'],
|
|
219
|
+
},
|
|
220
|
+
applied: false,
|
|
221
|
+
notes: ['pending apply is missing a behavioral receipt'],
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
return this.settleApplication(base, scenario, record.proposal, record.behavior, coverage, record);
|
|
225
|
+
}
|
|
226
|
+
settleApplication(base, scenario, proposal, behavior, coverageGate, existing) {
|
|
227
|
+
const name = proposal.spec.name;
|
|
228
|
+
let record = existing ?? this.applyJournal.get(scenario.id);
|
|
229
|
+
const computedSha = sha256Hex(intendedSkillBody(proposal));
|
|
230
|
+
if (record) {
|
|
231
|
+
const identityError = savedIdentityMismatch(record, scenario, proposal);
|
|
232
|
+
if (identityError) {
|
|
233
|
+
this.attempted.add(scenario.id);
|
|
234
|
+
return {
|
|
235
|
+
...base,
|
|
236
|
+
selectedScenarioId: scenario.id,
|
|
237
|
+
proposalId: proposal.id,
|
|
238
|
+
gate: coverageGate,
|
|
239
|
+
applied: false,
|
|
240
|
+
applyPhase: 'conflict',
|
|
241
|
+
notes: [identityError],
|
|
242
|
+
behavior,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const intendedSha256 = record?.intendedSha256 ?? computedSha;
|
|
247
|
+
let gate = coverageGate;
|
|
248
|
+
if (!record) {
|
|
249
|
+
try {
|
|
250
|
+
record = this.applyJournal.upsert({
|
|
251
|
+
scenarioId: scenario.id,
|
|
252
|
+
proposalId: proposal.id,
|
|
253
|
+
proposal,
|
|
254
|
+
intendedSha256,
|
|
255
|
+
phase: 'intent',
|
|
256
|
+
behavior,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
return {
|
|
261
|
+
...base,
|
|
262
|
+
selectedScenarioId: scenario.id,
|
|
263
|
+
proposalId: proposal.id,
|
|
264
|
+
gate: coverageGate,
|
|
265
|
+
applied: false,
|
|
266
|
+
applyPhase: 'intent',
|
|
267
|
+
notes: [
|
|
268
|
+
'apply journal write failed before installation; skill not installed',
|
|
269
|
+
error instanceof Error ? error.message : String(error),
|
|
270
|
+
],
|
|
271
|
+
behavior,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const installed = this.ensureInstalled(proposal, scenario, behavior, intendedSha256);
|
|
276
|
+
if (installed.kind === 'conflict') {
|
|
277
|
+
this.markConflict(scenario.id, proposal, intendedSha256, behavior);
|
|
278
|
+
this.attempted.add(scenario.id);
|
|
279
|
+
return this.conflictResult(base, scenario, proposal, coverageGate, behavior, name);
|
|
280
|
+
}
|
|
281
|
+
if (installed.kind === 'failed') {
|
|
282
|
+
return {
|
|
283
|
+
...base,
|
|
284
|
+
selectedScenarioId: scenario.id,
|
|
285
|
+
proposalId: proposal.id,
|
|
286
|
+
gate: coverageGate,
|
|
287
|
+
applied: false,
|
|
288
|
+
applyPhase: 'intent',
|
|
289
|
+
notes: ['installation pending', installed.error],
|
|
290
|
+
behavior,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (installed.kind === 'rejected') {
|
|
294
|
+
this.attempted.add(scenario.id);
|
|
295
|
+
if (record.phase === 'intent' && this.peekInstalled(name) === null) {
|
|
296
|
+
this.applyJournal.remove(scenario.id);
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
...base,
|
|
300
|
+
selectedScenarioId: scenario.id,
|
|
301
|
+
proposalId: proposal.id,
|
|
302
|
+
gate: installed.gate,
|
|
303
|
+
applied: false,
|
|
304
|
+
notes: installed.gate.reasons,
|
|
305
|
+
behavior,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
try {
|
|
309
|
+
record = this.applyJournal.upsert({
|
|
310
|
+
scenarioId: scenario.id,
|
|
311
|
+
proposalId: proposal.id,
|
|
312
|
+
proposal,
|
|
313
|
+
intendedSha256,
|
|
314
|
+
phase: 'installed',
|
|
315
|
+
appliedRef: installed.appliedRef,
|
|
316
|
+
behavior,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
/* disk has the skill; retry still sees identical content + intent/installed */
|
|
321
|
+
}
|
|
322
|
+
gate = installed.gate;
|
|
323
|
+
const appliedRef = record?.appliedRef ?? gate.appliedRef ?? name;
|
|
324
|
+
try {
|
|
93
325
|
this.archive.append({
|
|
94
326
|
proposalId: proposal.id,
|
|
95
327
|
kind: 'skill',
|
|
@@ -97,17 +329,157 @@ export class SkillImprovementEngine {
|
|
|
97
329
|
experienceId: proposal.experienceId,
|
|
98
330
|
delta: 1,
|
|
99
331
|
scoreAfter: 1,
|
|
100
|
-
appliedRef
|
|
332
|
+
appliedRef,
|
|
333
|
+
evidence: {
|
|
334
|
+
artifactSha256: intendedSha256,
|
|
335
|
+
benchmarkSha256: sha256Hex(JSON.stringify(SKILL_BEHAVIOR_TASKS[scenario.id] ?? [])),
|
|
336
|
+
wins: behavior.wins,
|
|
337
|
+
losses: behavior.losses,
|
|
338
|
+
cases: behavior.cases,
|
|
339
|
+
},
|
|
101
340
|
});
|
|
102
341
|
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
return {
|
|
344
|
+
...base,
|
|
345
|
+
selectedScenarioId: scenario.id,
|
|
346
|
+
proposalId: proposal.id,
|
|
347
|
+
gate: { ...gate, accepted: true, appliedRef },
|
|
348
|
+
applied: false,
|
|
349
|
+
proofPending: true,
|
|
350
|
+
applyPhase: 'installed',
|
|
351
|
+
notes: ['installed, proof pending', error instanceof Error ? error.message : String(error)],
|
|
352
|
+
behavior,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
this.applyJournal.remove(scenario.id);
|
|
356
|
+
this.proposals.remove('skill', scenario.id);
|
|
357
|
+
this.covered.add(scenario.id);
|
|
358
|
+
this.attempted.add(scenario.id);
|
|
103
359
|
return {
|
|
104
360
|
...base,
|
|
105
361
|
selectedScenarioId: scenario.id,
|
|
106
362
|
proposalId: proposal.id,
|
|
107
|
-
gate
|
|
108
|
-
|
|
109
|
-
|
|
363
|
+
gate: {
|
|
364
|
+
...gate,
|
|
365
|
+
accepted: true,
|
|
366
|
+
validationLevel: 'behavioral',
|
|
367
|
+
appliedRef,
|
|
368
|
+
reasons: gate.appliedRef
|
|
369
|
+
? gate.reasons
|
|
370
|
+
: ['accepted and installed (auto-apply): firewall-clean + covers the scenario'],
|
|
371
|
+
},
|
|
372
|
+
applied: true,
|
|
373
|
+
applyPhase: 'archived',
|
|
374
|
+
notes: gate.appliedRef
|
|
375
|
+
? gate.reasons
|
|
376
|
+
: ['accepted and installed (auto-apply): firewall-clean + covers the scenario'],
|
|
377
|
+
behavior,
|
|
110
378
|
};
|
|
111
379
|
}
|
|
380
|
+
ensureInstalled(proposal, scenario, behavior, intendedSha256) {
|
|
381
|
+
const name = proposal.spec.name;
|
|
382
|
+
if (!isAuthoredSkillName(name)) {
|
|
383
|
+
return {
|
|
384
|
+
kind: 'rejected',
|
|
385
|
+
gate: {
|
|
386
|
+
proposalId: proposal.id,
|
|
387
|
+
scenarioId: scenario.id,
|
|
388
|
+
accepted: false,
|
|
389
|
+
reasons: ['not an authored skill name'],
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
const disk = this.peekInstalled(name);
|
|
394
|
+
if (disk !== null && sha256Hex(disk) !== intendedSha256) {
|
|
395
|
+
return { kind: 'conflict' };
|
|
396
|
+
}
|
|
397
|
+
const dryGate = validateSkillProposal(proposal, scenario, this.dryMutator(), {
|
|
398
|
+
keepOnAccept: true,
|
|
399
|
+
behavior,
|
|
400
|
+
});
|
|
401
|
+
if (!dryGate.accepted) {
|
|
402
|
+
return { kind: 'rejected', gate: dryGate };
|
|
403
|
+
}
|
|
404
|
+
if (disk !== null && sha256Hex(disk) === intendedSha256) {
|
|
405
|
+
return { kind: 'ready', appliedRef: dryGate.appliedRef ?? name, gate: dryGate };
|
|
406
|
+
}
|
|
407
|
+
if (this.mutator.has(name))
|
|
408
|
+
return { kind: 'conflict' };
|
|
409
|
+
try {
|
|
410
|
+
const gate = validateSkillProposal(proposal, scenario, this.mutator, { keepOnAccept: true, behavior });
|
|
411
|
+
if (!gate.accepted || !gate.appliedRef)
|
|
412
|
+
return { kind: 'rejected', gate };
|
|
413
|
+
return { kind: 'ready', appliedRef: gate.appliedRef, gate };
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
return { kind: 'failed', error: error instanceof Error ? error.message : String(error) };
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
dryMutator() {
|
|
420
|
+
const port = {
|
|
421
|
+
create: (spec) => ({ name: spec.name }),
|
|
422
|
+
remove: () => false,
|
|
423
|
+
has: (name) => this.mutator.has(name),
|
|
424
|
+
};
|
|
425
|
+
if (this.mutator.readInstalled) {
|
|
426
|
+
port.readInstalled = (name) => this.mutator.readInstalled(name);
|
|
427
|
+
}
|
|
428
|
+
return port;
|
|
429
|
+
}
|
|
430
|
+
peekInstalled(name) {
|
|
431
|
+
if (!isAuthoredSkillName(name))
|
|
432
|
+
return null;
|
|
433
|
+
return this.readInstalledSkill(name);
|
|
434
|
+
}
|
|
435
|
+
markConflict(scenarioId, proposal, intendedSha256, behavior, appliedRef) {
|
|
436
|
+
try {
|
|
437
|
+
this.applyJournal.upsert({
|
|
438
|
+
scenarioId,
|
|
439
|
+
proposalId: proposal.id,
|
|
440
|
+
proposal,
|
|
441
|
+
intendedSha256,
|
|
442
|
+
phase: 'conflict',
|
|
443
|
+
behavior,
|
|
444
|
+
...(appliedRef ? { appliedRef } : {}),
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
/* conflict is also represented by the on-disk mismatch */
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
conflictResult(base, scenario, proposal, gate, behavior, name) {
|
|
452
|
+
return {
|
|
453
|
+
...base,
|
|
454
|
+
selectedScenarioId: scenario.id,
|
|
455
|
+
proposalId: proposal.id,
|
|
456
|
+
gate: { ...gate, appliedRef: gate.appliedRef ?? (this.mutator.has(name) ? name : undefined) },
|
|
457
|
+
applied: false,
|
|
458
|
+
applyPhase: 'conflict',
|
|
459
|
+
notes: ['installed artifact differs from intended; not archived'],
|
|
460
|
+
behavior,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function intendedSkillBody(proposal) {
|
|
465
|
+
return ensureFrontmatter(proposal.spec.name, proposal.spec.description, proposal.spec.content);
|
|
466
|
+
}
|
|
467
|
+
function sha256Hex(value) {
|
|
468
|
+
return createHash('sha256').update(value).digest('hex');
|
|
469
|
+
}
|
|
470
|
+
function savedIdentityMismatch(record, scenario, proposal) {
|
|
471
|
+
if (record.proposalId !== proposal.id)
|
|
472
|
+
return 'pending apply proposalId does not match proposal.id';
|
|
473
|
+
if (record.scenarioId !== scenario.id)
|
|
474
|
+
return 'pending apply scenarioId does not match scenario';
|
|
475
|
+
if (proposal.targetScenarioId !== scenario.id)
|
|
476
|
+
return 'pending apply targetScenarioId does not match scenario';
|
|
477
|
+
if (record.scenarioId !== proposal.targetScenarioId) {
|
|
478
|
+
return 'pending apply scenarioId does not match targetScenarioId';
|
|
479
|
+
}
|
|
480
|
+
if (record.intendedSha256 !== sha256Hex(intendedSkillBody(proposal))) {
|
|
481
|
+
return 'pending apply intendedSha256 does not match proposal artifact';
|
|
482
|
+
}
|
|
483
|
+
return null;
|
|
112
484
|
}
|
|
113
485
|
//# sourceMappingURL=skill-engine.js.map
|
|
@@ -9,10 +9,12 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module agent/self-improvement/skill-gate
|
|
11
11
|
*/
|
|
12
|
+
import type { SkillBehaviorResult } from './skill-behavior-gate.js';
|
|
12
13
|
import { type SkillMutatorPort } from './skill-mutator.js';
|
|
13
14
|
import type { SkillBenchmarkScenario, SkillGateOutcome, SkillProposal } from './skill-types.js';
|
|
14
15
|
export interface ValidateSkillOptions {
|
|
15
16
|
keepOnAccept: boolean;
|
|
17
|
+
behavior?: SkillBehaviorResult;
|
|
16
18
|
}
|
|
17
19
|
/** Deterministic coverage check: the skill content surfaces all expected guidance. */
|
|
18
20
|
export declare function coversScenario(content: string, scenario: SkillBenchmarkScenario): boolean;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module agent/self-improvement/skill-gate
|
|
11
11
|
*/
|
|
12
|
-
import { safetyGateSkill } from './skill-mutator.js';
|
|
12
|
+
import { ensureFrontmatter, safetyGateSkill } from './skill-mutator.js';
|
|
13
13
|
/** Deterministic coverage check: the skill content surfaces all expected guidance. */
|
|
14
14
|
export function coversScenario(content, scenario) {
|
|
15
15
|
const lower = content.toLowerCase();
|
|
@@ -30,6 +30,14 @@ export function validateSkillProposal(proposal, scenario, mutator, options) {
|
|
|
30
30
|
reasons: ['SG1: skill content is empty or too short, or missing name'],
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
+
try {
|
|
34
|
+
ensureFrontmatter(proposal.spec.name, proposal.spec.description, content);
|
|
35
|
+
if (!/^#{1,6}\s+\S/m.test(content))
|
|
36
|
+
throw new Error('skill must have a Markdown title');
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return { ...base, accepted: false, rejectionReason: 'static-scan', reasons: [`SG1: ${error instanceof Error ? error.message : String(error)}`] };
|
|
40
|
+
}
|
|
33
41
|
// SG2: static scan + skill firewall (prompt-injection / exfiltration / safety)
|
|
34
42
|
const safety = safetyGateSkill(content);
|
|
35
43
|
if (!safety.ok) {
|
|
@@ -63,6 +71,12 @@ export function validateSkillProposal(proposal, scenario, mutator, options) {
|
|
|
63
71
|
reasons: [`SG4: held-out secret coverage failed, missing: ${JSON.stringify(missingHeldOut)}`],
|
|
64
72
|
};
|
|
65
73
|
}
|
|
74
|
+
if (options.keepOnAccept) {
|
|
75
|
+
const evidence = options.behavior;
|
|
76
|
+
if (!evidence?.accepted || evidence.tested < 1 || evidence.cases.length !== evidence.tested || evidence.wins < 1 || evidence.losses !== 0 || !evidence.cases.every(item => item.after) || evidence.cases.filter(item => !item.before && item.after).length !== evidence.wins) {
|
|
77
|
+
return { ...base, accepted: false, rejectionReason: 'behavior-required', reasons: ['Installation requires a measured behavioral gain without regression'] };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
66
80
|
// Accepted. Install (auto-apply) or just report (propose-only).
|
|
67
81
|
let appliedRef;
|
|
68
82
|
if (options.keepOnAccept) {
|
|
@@ -71,9 +85,10 @@ export function validateSkillProposal(proposal, scenario, mutator, options) {
|
|
|
71
85
|
return {
|
|
72
86
|
...base,
|
|
73
87
|
accepted: true,
|
|
88
|
+
validationLevel: options.keepOnAccept ? 'behavioral' : 'coverage',
|
|
74
89
|
reasons: options.keepOnAccept
|
|
75
90
|
? ['accepted and installed (auto-apply): firewall-clean + covers the scenario']
|
|
76
|
-
: ['
|
|
91
|
+
: ['coverage validated only; behavioral verification is required before the improvement engine installs this proposal'],
|
|
77
92
|
...(appliedRef ? { appliedRef } : {}),
|
|
78
93
|
};
|
|
79
94
|
}
|
|
@@ -49,6 +49,8 @@ export interface SkillMutatorPort {
|
|
|
49
49
|
};
|
|
50
50
|
remove(name: string): boolean;
|
|
51
51
|
has(name: string): boolean;
|
|
52
|
+
/** Read SKILL.md from this mutator's actual root. Engine must not guess paths. */
|
|
53
|
+
readInstalled?(name: string): string | null;
|
|
52
54
|
}
|
|
53
55
|
export interface MutationResult {
|
|
54
56
|
ok: boolean;
|
|
@@ -64,8 +66,11 @@ export declare class LiveSkillMutator implements SkillMutatorPort {
|
|
|
64
66
|
private reload;
|
|
65
67
|
private readContent;
|
|
66
68
|
has(name: string): boolean;
|
|
69
|
+
readInstalled(name: string): string | null;
|
|
67
70
|
isPinned(name: string): boolean;
|
|
68
|
-
create(spec: SkillSpec
|
|
71
|
+
create(spec: SkillSpec, options?: {
|
|
72
|
+
overwrite?: boolean;
|
|
73
|
+
}): {
|
|
69
74
|
name: string;
|
|
70
75
|
};
|
|
71
76
|
/** Full re-author of an existing authored skill, re-gated. Refuses pinned. */
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
import fs from 'fs';
|
|
14
14
|
import path from 'path';
|
|
15
15
|
import os from 'os';
|
|
16
|
+
import * as yaml from 'yaml';
|
|
17
|
+
import { parseSkillFile, validateSkill } from '../../skills/parser.js';
|
|
16
18
|
import { randomUUID } from 'crypto';
|
|
17
19
|
import { getSkillRegistry } from '../../skills/registry.js';
|
|
18
20
|
import { logger } from '../../utils/logger.js';
|
|
@@ -61,10 +63,18 @@ export function isAuthoredSkillName(name) {
|
|
|
61
63
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
62
64
|
/** Ensure the content begins with YAML frontmatter carrying name + description. */
|
|
63
65
|
export function ensureFrontmatter(name, description, content) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
const match = content.match(FRONTMATTER_RE);
|
|
67
|
+
const metadata = match ? yaml.parse(match[1]) : {};
|
|
68
|
+
if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata))
|
|
69
|
+
throw new Error('Invalid skill metadata');
|
|
70
|
+
if (metadata.name !== undefined && metadata.name !== name)
|
|
71
|
+
throw new Error('Skill frontmatter identity differs from requested name');
|
|
72
|
+
const body = match ? content.slice(match[0].length) : content;
|
|
73
|
+
const result = `---\n${yaml.stringify({ ...metadata, name, description: description.trim() || metadata.description })}---\n\n${body.trim()}\n`;
|
|
74
|
+
const validation = validateSkill(parseSkillFile(result, 'authored-skill', 'workspace'));
|
|
75
|
+
if (!validation.valid)
|
|
76
|
+
throw new Error(validation.errors.join('; '));
|
|
77
|
+
return result;
|
|
68
78
|
}
|
|
69
79
|
export function readPinned(content) {
|
|
70
80
|
const m = content.match(FRONTMATTER_RE);
|
|
@@ -187,13 +197,18 @@ export class LiveSkillMutator {
|
|
|
187
197
|
return false;
|
|
188
198
|
return fs.existsSync(this.skillFile(name));
|
|
189
199
|
}
|
|
200
|
+
readInstalled(name) {
|
|
201
|
+
if (!isAuthoredSkillName(name))
|
|
202
|
+
return null;
|
|
203
|
+
return this.readContent(name);
|
|
204
|
+
}
|
|
190
205
|
isPinned(name) {
|
|
191
206
|
if (!isSafeSkillSegment(name))
|
|
192
207
|
return false;
|
|
193
208
|
const c = this.readContent(name);
|
|
194
209
|
return c ? readPinned(c) : false;
|
|
195
210
|
}
|
|
196
|
-
create(spec) {
|
|
211
|
+
create(spec, options = {}) {
|
|
197
212
|
// Backstops durs, miroir de LiveToolMutator.register : (a) namespace
|
|
198
213
|
// authored-* obligatoire — sans lui, un spec nommé comme une skill user/
|
|
199
214
|
// bundled l'écraserait ; (b) re-gate de sûreté — les appelants légitimes
|
|
@@ -203,6 +218,12 @@ export class LiveSkillMutator {
|
|
|
203
218
|
if (!isAuthoredSkillName(spec.name)) {
|
|
204
219
|
throw new Error(`refusing to create skill "${spec.name}": authored skills must be named "${AUTHORED_SKILL_PREFIX}*" (never shadow a user/bundled skill)`);
|
|
205
220
|
}
|
|
221
|
+
if (this.has(spec.name)) {
|
|
222
|
+
if (this.isPinned(spec.name))
|
|
223
|
+
throw new Error('Skill is pinned');
|
|
224
|
+
if (!options.overwrite)
|
|
225
|
+
throw new Error('Skill already exists; explicit overwrite required');
|
|
226
|
+
}
|
|
206
227
|
const content = ensureFrontmatter(spec.name, spec.description, spec.content);
|
|
207
228
|
const gate = safetyGateSkill(content);
|
|
208
229
|
if (!gate.ok) {
|
|
@@ -210,9 +231,24 @@ export class LiveSkillMutator {
|
|
|
210
231
|
}
|
|
211
232
|
const dir = this.dirFor(spec.name);
|
|
212
233
|
fs.mkdirSync(dir, { recursive: true });
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
234
|
+
const file = this.skillFile(spec.name);
|
|
235
|
+
const previous = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : undefined;
|
|
236
|
+
try {
|
|
237
|
+
writeFileAtomicSync(file, content);
|
|
238
|
+
const registry = getSkillRegistry();
|
|
239
|
+
const installed = registry.registerSkillFileSync(file, 'workspace');
|
|
240
|
+
if (registry.get(spec.name) !== installed)
|
|
241
|
+
throw new Error('Skill registry refused installation');
|
|
242
|
+
return { name: spec.name };
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
if (previous !== undefined)
|
|
246
|
+
writeFileAtomicSync(file, previous);
|
|
247
|
+
else
|
|
248
|
+
fs.rmSync(file, { force: true });
|
|
249
|
+
this.reload(spec.name);
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
216
252
|
}
|
|
217
253
|
/** Full re-author of an existing authored skill, re-gated. Refuses pinned. */
|
|
218
254
|
update(name, newContent, description = '') {
|
|
@@ -222,13 +258,13 @@ export class LiveSkillMutator {
|
|
|
222
258
|
return { ok: false, reasons: ['skill does not exist'] };
|
|
223
259
|
if (this.isPinned(name))
|
|
224
260
|
return { ok: false, reasons: ['skill is pinned'] };
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
261
|
+
try {
|
|
262
|
+
this.create({ name, description: description || parseSkillFile(this.readContent(name), this.skillFile(name), 'workspace').metadata.description, content: newContent }, { overwrite: true });
|
|
263
|
+
return { ok: true, reasons: [] };
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
return { ok: false, reasons: [error instanceof Error ? error.message : String(error)] };
|
|
267
|
+
}
|
|
232
268
|
}
|
|
233
269
|
/** Find/replace within an authored skill body (exact; fail on multiple unless replaceAll). */
|
|
234
270
|
patch(name, oldStr, newStr, opts = {}) {
|
|
@@ -33,9 +33,10 @@ export interface SkillProposal {
|
|
|
33
33
|
experienceId?: string;
|
|
34
34
|
spec: SkillSpec;
|
|
35
35
|
}
|
|
36
|
-
export type SkillGateRejection = 'static-scan' | 'firewall' | 'coverage-fail';
|
|
36
|
+
export type SkillGateRejection = 'static-scan' | 'firewall' | 'coverage-fail' | 'behavior-required';
|
|
37
37
|
export interface SkillGateOutcome {
|
|
38
38
|
accepted: boolean;
|
|
39
|
+
validationLevel?: 'coverage' | 'behavioral';
|
|
39
40
|
proposalId: string;
|
|
40
41
|
scenarioId: string;
|
|
41
42
|
rejectionReason?: SkillGateRejection;
|