@dcyfr/ai 1.0.4 → 2.1.1
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/CHANGELOG.md +116 -0
- package/README.md +381 -9
- package/dist/ai/agents/agent-loader.d.ts +12 -0
- package/dist/ai/agents/agent-loader.d.ts.map +1 -1
- package/dist/ai/agents/agent-loader.js +74 -65
- package/dist/ai/agents/agent-loader.js.map +1 -1
- package/dist/ai/agents/agent-registry.d.ts +2 -0
- package/dist/ai/agents/agent-registry.d.ts.map +1 -1
- package/dist/ai/agents/agent-registry.js +15 -14
- package/dist/ai/agents/agent-registry.js.map +1 -1
- package/dist/ai/agents/agent-router.d.ts +20 -5
- package/dist/ai/agents/agent-router.d.ts.map +1 -1
- package/dist/ai/agents/agent-router.js +89 -43
- package/dist/ai/agents/agent-router.js.map +1 -1
- package/dist/ai/agents/index.d.ts +6 -1
- package/dist/ai/agents/index.d.ts.map +1 -1
- package/dist/ai/agents/index.js +9 -3
- package/dist/ai/agents/index.js.map +1 -1
- package/dist/ai/agents/instruction-template.d.ts +45 -0
- package/dist/ai/agents/instruction-template.d.ts.map +1 -0
- package/dist/ai/agents/instruction-template.js +197 -0
- package/dist/ai/agents/instruction-template.js.map +1 -0
- package/dist/ai/agents/persona-resolver.d.ts +90 -0
- package/dist/ai/agents/persona-resolver.d.ts.map +1 -0
- package/dist/ai/agents/persona-resolver.js +121 -0
- package/dist/ai/agents/persona-resolver.js.map +1 -0
- package/dist/ai/agents/schema.d.ts +166 -0
- package/dist/ai/agents/schema.d.ts.map +1 -0
- package/dist/ai/agents/schema.js +94 -0
- package/dist/ai/agents/schema.js.map +1 -0
- package/dist/ai/agents/types.d.ts +102 -0
- package/dist/ai/agents/types.d.ts.map +1 -1
- package/dist/ai/agents-builtin/architecture/index.js +3 -3
- package/dist/ai/agents-builtin/content/index.js +1 -1
- package/dist/ai/agents-builtin/data/index.js +1 -1
- package/dist/ai/agents-builtin/development/index.js +4 -4
- package/dist/ai/agents-builtin/devops/index.js +1 -1
- package/dist/ai/agents-builtin/index.js +33 -33
- package/dist/ai/agents-builtin/performance/index.js +1 -1
- package/dist/ai/agents-builtin/research/index.js +1 -1
- package/dist/ai/agents-builtin/security/index.js +1 -1
- package/dist/ai/agents-builtin/testing/index.js +2 -2
- package/dist/ai/config/loader.d.ts.map +1 -1
- package/dist/ai/config/loader.js +6 -3
- package/dist/ai/config/loader.js.map +1 -1
- package/dist/ai/config/schema.d.ts +242 -1156
- package/dist/ai/config/schema.d.ts.map +1 -1
- package/dist/ai/config/schema.js +69 -20
- package/dist/ai/config/schema.js.map +1 -1
- package/dist/ai/core/provider-registry.d.ts +32 -0
- package/dist/ai/core/provider-registry.d.ts.map +1 -1
- package/dist/ai/core/provider-registry.js +189 -14
- package/dist/ai/core/provider-registry.js.map +1 -1
- package/dist/ai/core/telemetry-engine.d.ts +26 -0
- package/dist/ai/core/telemetry-engine.d.ts.map +1 -1
- package/dist/ai/core/telemetry-engine.js +81 -1
- package/dist/ai/core/telemetry-engine.js.map +1 -1
- package/dist/ai/delegation/agent-registry.d.ts +143 -0
- package/dist/ai/delegation/agent-registry.d.ts.map +1 -0
- package/dist/ai/delegation/agent-registry.js +231 -0
- package/dist/ai/delegation/agent-registry.js.map +1 -0
- package/dist/ai/delegation/blast-radius-tracker.d.ts +65 -0
- package/dist/ai/delegation/blast-radius-tracker.d.ts.map +1 -0
- package/dist/ai/delegation/blast-radius-tracker.js +81 -0
- package/dist/ai/delegation/blast-radius-tracker.js.map +1 -0
- package/dist/ai/delegation/capability-bootstrap.d.ts +40 -0
- package/dist/ai/delegation/capability-bootstrap.d.ts.map +1 -0
- package/dist/ai/delegation/capability-bootstrap.js +431 -0
- package/dist/ai/delegation/capability-bootstrap.js.map +1 -0
- package/dist/ai/delegation/capability-registry.d.ts +81 -0
- package/dist/ai/delegation/capability-registry.d.ts.map +1 -0
- package/dist/ai/delegation/capability-registry.js +339 -0
- package/dist/ai/delegation/capability-registry.js.map +1 -0
- package/dist/ai/delegation/chain-tracker.d.ts +152 -0
- package/dist/ai/delegation/chain-tracker.d.ts.map +1 -0
- package/dist/ai/delegation/chain-tracker.js +336 -0
- package/dist/ai/delegation/chain-tracker.js.map +1 -0
- package/dist/ai/delegation/circuit-breaker.d.ts +59 -0
- package/dist/ai/delegation/circuit-breaker.d.ts.map +1 -0
- package/dist/ai/delegation/circuit-breaker.js +153 -0
- package/dist/ai/delegation/circuit-breaker.js.map +1 -0
- package/dist/ai/delegation/contract-manager.d.ts +415 -0
- package/dist/ai/delegation/contract-manager.d.ts.map +1 -0
- package/dist/ai/delegation/contract-manager.js +1258 -0
- package/dist/ai/delegation/contract-manager.js.map +1 -0
- package/dist/ai/delegation/delegation-manager.d.ts +505 -0
- package/dist/ai/delegation/delegation-manager.d.ts.map +1 -0
- package/dist/ai/delegation/delegation-manager.js +773 -0
- package/dist/ai/delegation/delegation-manager.js.map +1 -0
- package/dist/ai/delegation/event-schemas.d.ts +101 -0
- package/dist/ai/delegation/event-schemas.d.ts.map +1 -0
- package/dist/ai/delegation/event-schemas.js +59 -0
- package/dist/ai/delegation/event-schemas.js.map +1 -0
- package/dist/ai/delegation/execution-mode-dashboard.d.ts +109 -0
- package/dist/ai/delegation/execution-mode-dashboard.d.ts.map +1 -0
- package/dist/ai/delegation/execution-mode-dashboard.js +167 -0
- package/dist/ai/delegation/execution-mode-dashboard.js.map +1 -0
- package/dist/ai/delegation/feature-flags.d.ts +191 -0
- package/dist/ai/delegation/feature-flags.d.ts.map +1 -0
- package/dist/ai/delegation/feature-flags.js +332 -0
- package/dist/ai/delegation/feature-flags.js.map +1 -0
- package/dist/ai/delegation/index.d.ts +51 -0
- package/dist/ai/delegation/index.d.ts.map +1 -0
- package/dist/ai/delegation/index.js +39 -0
- package/dist/ai/delegation/index.js.map +1 -0
- package/dist/ai/delegation/middleware/chain-depth-middleware.d.ts +39 -0
- package/dist/ai/delegation/middleware/chain-depth-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/chain-depth-middleware.js +77 -0
- package/dist/ai/delegation/middleware/chain-depth-middleware.js.map +1 -0
- package/dist/ai/delegation/middleware/chain-tracker-middleware.d.ts +46 -0
- package/dist/ai/delegation/middleware/chain-tracker-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/chain-tracker-middleware.js +89 -0
- package/dist/ai/delegation/middleware/chain-tracker-middleware.js.map +1 -0
- package/dist/ai/delegation/middleware/content-policy-middleware.d.ts +31 -0
- package/dist/ai/delegation/middleware/content-policy-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/content-policy-middleware.js +82 -0
- package/dist/ai/delegation/middleware/content-policy-middleware.js.map +1 -0
- package/dist/ai/delegation/middleware/feature-flag-middleware.d.ts +46 -0
- package/dist/ai/delegation/middleware/feature-flag-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/feature-flag-middleware.js +59 -0
- package/dist/ai/delegation/middleware/feature-flag-middleware.js.map +1 -0
- package/dist/ai/delegation/middleware/identity-middleware.d.ts +23 -0
- package/dist/ai/delegation/middleware/identity-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/identity-middleware.js +64 -0
- package/dist/ai/delegation/middleware/identity-middleware.js.map +1 -0
- package/dist/ai/delegation/middleware/permissions-middleware.d.ts +48 -0
- package/dist/ai/delegation/middleware/permissions-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/permissions-middleware.js +107 -0
- package/dist/ai/delegation/middleware/permissions-middleware.js.map +1 -0
- package/dist/ai/delegation/middleware/rate-limiter-middleware.d.ts +38 -0
- package/dist/ai/delegation/middleware/rate-limiter-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/rate-limiter-middleware.js +65 -0
- package/dist/ai/delegation/middleware/rate-limiter-middleware.js.map +1 -0
- package/dist/ai/delegation/middleware/reputation-middleware.d.ts +39 -0
- package/dist/ai/delegation/middleware/reputation-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/reputation-middleware.js +75 -0
- package/dist/ai/delegation/middleware/reputation-middleware.js.map +1 -0
- package/dist/ai/delegation/middleware/resource-limiter-middleware.d.ts +52 -0
- package/dist/ai/delegation/middleware/resource-limiter-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/resource-limiter-middleware.js +112 -0
- package/dist/ai/delegation/middleware/resource-limiter-middleware.js.map +1 -0
- package/dist/ai/delegation/middleware/threat-validator-middleware.d.ts +23 -0
- package/dist/ai/delegation/middleware/threat-validator-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/threat-validator-middleware.js +83 -0
- package/dist/ai/delegation/middleware/threat-validator-middleware.js.map +1 -0
- package/dist/ai/delegation/middleware/tlp-middleware.d.ts +23 -0
- package/dist/ai/delegation/middleware/tlp-middleware.d.ts.map +1 -0
- package/dist/ai/delegation/middleware/tlp-middleware.js +59 -0
- package/dist/ai/delegation/middleware/tlp-middleware.js.map +1 -0
- package/dist/ai/delegation/monitoring.d.ts +298 -0
- package/dist/ai/delegation/monitoring.d.ts.map +1 -0
- package/dist/ai/delegation/monitoring.js +584 -0
- package/dist/ai/delegation/monitoring.js.map +1 -0
- package/dist/ai/delegation/security-middleware-chain.d.ts +71 -0
- package/dist/ai/delegation/security-middleware-chain.d.ts.map +1 -0
- package/dist/ai/delegation/security-middleware-chain.js +163 -0
- package/dist/ai/delegation/security-middleware-chain.js.map +1 -0
- package/dist/ai/delegation/session-checkpoint.d.ts +77 -0
- package/dist/ai/delegation/session-checkpoint.d.ts.map +1 -0
- package/dist/ai/delegation/session-checkpoint.js +131 -0
- package/dist/ai/delegation/session-checkpoint.js.map +1 -0
- package/dist/ai/delegation/session-manager.d.ts +131 -0
- package/dist/ai/delegation/session-manager.d.ts.map +1 -0
- package/dist/ai/delegation/session-manager.js +243 -0
- package/dist/ai/delegation/session-manager.js.map +1 -0
- package/dist/ai/delegation/session-queue.d.ts +95 -0
- package/dist/ai/delegation/session-queue.d.ts.map +1 -0
- package/dist/ai/delegation/session-queue.js +136 -0
- package/dist/ai/delegation/session-queue.js.map +1 -0
- package/dist/ai/delegation/timeout-watchdog.d.ts +60 -0
- package/dist/ai/delegation/timeout-watchdog.d.ts.map +1 -0
- package/dist/ai/delegation/timeout-watchdog.js +100 -0
- package/dist/ai/delegation/timeout-watchdog.js.map +1 -0
- package/dist/ai/examples/integration-demo.d.ts +27 -0
- package/dist/ai/examples/integration-demo.d.ts.map +1 -0
- package/dist/ai/examples/integration-demo.js +536 -0
- package/dist/ai/examples/integration-demo.js.map +1 -0
- package/dist/ai/index.d.ts +25 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +34 -11
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/mcp/servers/analytics/index.js +67 -11
- package/dist/ai/mcp/servers/analytics/index.js.map +1 -1
- package/dist/ai/mcp/servers/content-manager/index.d.ts.map +1 -1
- package/dist/ai/mcp/servers/content-manager/index.js +50 -4
- package/dist/ai/mcp/servers/content-manager/index.js.map +1 -1
- package/dist/ai/mcp/servers/delegation-monitor/index.d.ts +36 -0
- package/dist/ai/mcp/servers/delegation-monitor/index.d.ts.map +1 -0
- package/dist/ai/mcp/servers/delegation-monitor/index.js +757 -0
- package/dist/ai/mcp/servers/delegation-monitor/index.js.map +1 -0
- package/dist/ai/mcp/servers/design-tokens/index.d.ts.map +1 -1
- package/dist/ai/mcp/servers/design-tokens/index.js +62 -39
- package/dist/ai/mcp/servers/design-tokens/index.js.map +1 -1
- package/dist/ai/mcp/servers/promptintel/index.js +15 -3
- package/dist/ai/mcp/servers/promptintel/index.js.map +1 -1
- package/dist/ai/mcp/servers/shared/rate-limiter.d.ts.map +1 -1
- package/dist/ai/mcp/servers/shared/rate-limiter.js +3 -1
- package/dist/ai/mcp/servers/shared/rate-limiter.js.map +1 -1
- package/dist/ai/mcp/servers/shared/redis-client.d.ts.map +1 -1
- package/dist/ai/mcp/servers/shared/redis-client.js +54 -47
- package/dist/ai/mcp/servers/shared/redis-client.js.map +1 -1
- package/dist/ai/mcp/servers/shared/types.d.ts +9 -0
- package/dist/ai/mcp/servers/shared/types.d.ts.map +1 -1
- package/dist/ai/mcp/servers/shared/utils.d.ts +9 -1
- package/dist/ai/mcp/servers/shared/utils.d.ts.map +1 -1
- package/dist/ai/mcp/servers/shared/utils.js +52 -2
- package/dist/ai/mcp/servers/shared/utils.js.map +1 -1
- package/dist/ai/memory/config.d.ts +18 -3
- package/dist/ai/memory/config.d.ts.map +1 -1
- package/dist/ai/memory/config.js +61 -15
- package/dist/ai/memory/config.js.map +1 -1
- package/dist/ai/memory/dcyfr-memory.d.ts +4 -0
- package/dist/ai/memory/dcyfr-memory.d.ts.map +1 -1
- package/dist/ai/memory/dcyfr-memory.js +44 -6
- package/dist/ai/memory/dcyfr-memory.js.map +1 -1
- package/dist/ai/memory/mem0-client.d.ts.map +1 -1
- package/dist/ai/memory/mem0-client.js +53 -14
- package/dist/ai/memory/mem0-client.js.map +1 -1
- package/dist/ai/permissions/attenuation-engine.d.ts +159 -0
- package/dist/ai/permissions/attenuation-engine.d.ts.map +1 -0
- package/dist/ai/permissions/attenuation-engine.js +511 -0
- package/dist/ai/permissions/attenuation-engine.js.map +1 -0
- package/dist/ai/plugins/plugin-loader.d.ts +4 -0
- package/dist/ai/plugins/plugin-loader.d.ts.map +1 -1
- package/dist/ai/plugins/plugin-loader.js +28 -28
- package/dist/ai/plugins/plugin-loader.js.map +1 -1
- package/dist/ai/reputation/execution-mode-reputation.d.ts +104 -0
- package/dist/ai/reputation/execution-mode-reputation.d.ts.map +1 -0
- package/dist/ai/reputation/execution-mode-reputation.js +142 -0
- package/dist/ai/reputation/execution-mode-reputation.js.map +1 -0
- package/dist/ai/reputation/reputation-engine.d.ts +204 -0
- package/dist/ai/reputation/reputation-engine.d.ts.map +1 -0
- package/dist/ai/reputation/reputation-engine.js +426 -0
- package/dist/ai/reputation/reputation-engine.js.map +1 -0
- package/dist/ai/runtime/agent-runtime.d.ts +193 -0
- package/dist/ai/runtime/agent-runtime.d.ts.map +1 -0
- package/dist/ai/runtime/agent-runtime.js +1009 -0
- package/dist/ai/runtime/agent-runtime.js.map +1 -0
- package/dist/ai/runtime/index.d.ts +11 -0
- package/dist/ai/runtime/index.d.ts.map +1 -0
- package/dist/ai/runtime/index.js +9 -0
- package/dist/ai/runtime/index.js.map +1 -0
- package/dist/ai/runtime/telemetry-schema.d.ts +192 -0
- package/dist/ai/runtime/telemetry-schema.d.ts.map +1 -0
- package/dist/ai/runtime/telemetry-schema.js +200 -0
- package/dist/ai/runtime/telemetry-schema.js.map +1 -0
- package/dist/ai/runtime/types.d.ts +236 -0
- package/dist/ai/runtime/types.d.ts.map +1 -0
- package/dist/ai/runtime/types.js +10 -0
- package/dist/ai/runtime/types.js.map +1 -0
- package/dist/ai/src/batch-processor.d.ts +298 -0
- package/dist/ai/src/batch-processor.d.ts.map +1 -0
- package/dist/ai/src/batch-processor.js +520 -0
- package/dist/ai/src/batch-processor.js.map +1 -0
- package/dist/ai/src/capability-bootstrap.d.ts +222 -0
- package/dist/ai/src/capability-bootstrap.d.ts.map +1 -0
- package/dist/ai/src/capability-bootstrap.js +421 -0
- package/dist/ai/src/capability-bootstrap.js.map +1 -0
- package/dist/ai/src/capability-manifest-generator.d.ts +61 -0
- package/dist/ai/src/capability-manifest-generator.d.ts.map +1 -0
- package/dist/ai/src/capability-manifest-generator.js +700 -0
- package/dist/ai/src/capability-manifest-generator.js.map +1 -0
- package/dist/ai/src/capability-registry.d.ts +157 -0
- package/dist/ai/src/capability-registry.d.ts.map +1 -0
- package/dist/ai/src/capability-registry.js +577 -0
- package/dist/ai/src/capability-registry.js.map +1 -0
- package/dist/ai/src/cli/telemetry-dashboard.d.ts +132 -0
- package/dist/ai/src/cli/telemetry-dashboard.d.ts.map +1 -0
- package/dist/ai/src/cli/telemetry-dashboard.js +565 -0
- package/dist/ai/src/cli/telemetry-dashboard.js.map +1 -0
- package/dist/ai/src/delegation/feature-flags.d.ts +213 -0
- package/dist/ai/src/delegation/feature-flags.d.ts.map +1 -0
- package/dist/ai/src/delegation/feature-flags.js +395 -0
- package/dist/ai/src/delegation/feature-flags.js.map +1 -0
- package/dist/ai/src/delegation/liability-firebreak.d.ts +303 -0
- package/dist/ai/src/delegation/liability-firebreak.d.ts.map +1 -0
- package/dist/ai/src/delegation/liability-firebreak.js +643 -0
- package/dist/ai/src/delegation/liability-firebreak.js.map +1 -0
- package/dist/ai/src/delegation/security-threat-model.d.ts +171 -0
- package/dist/ai/src/delegation/security-threat-model.d.ts.map +1 -0
- package/dist/ai/src/delegation/security-threat-model.js +723 -0
- package/dist/ai/src/delegation/security-threat-model.js.map +1 -0
- package/dist/ai/src/delegation/tlp-enforcement.d.ts +146 -0
- package/dist/ai/src/delegation/tlp-enforcement.d.ts.map +1 -0
- package/dist/ai/src/delegation/tlp-enforcement.js +382 -0
- package/dist/ai/src/delegation/tlp-enforcement.js.map +1 -0
- package/dist/ai/src/delegation-capability-integration.d.ts +154 -0
- package/dist/ai/src/delegation-capability-integration.d.ts.map +1 -0
- package/dist/ai/src/delegation-capability-integration.js +351 -0
- package/dist/ai/src/delegation-capability-integration.js.map +1 -0
- package/dist/ai/src/end-to-end-workflow-orchestrator.d.ts +325 -0
- package/dist/ai/src/end-to-end-workflow-orchestrator.d.ts.map +1 -0
- package/dist/ai/src/end-to-end-workflow-orchestrator.js +801 -0
- package/dist/ai/src/end-to-end-workflow-orchestrator.js.map +1 -0
- package/dist/ai/src/enhanced-capability-detection.d.ts +237 -0
- package/dist/ai/src/enhanced-capability-detection.d.ts.map +1 -0
- package/dist/ai/src/enhanced-capability-detection.js +448 -0
- package/dist/ai/src/enhanced-capability-detection.js.map +1 -0
- package/dist/ai/src/intelligent-cache-manager.d.ts +327 -0
- package/dist/ai/src/intelligent-cache-manager.d.ts.map +1 -0
- package/dist/ai/src/intelligent-cache-manager.js +634 -0
- package/dist/ai/src/intelligent-cache-manager.js.map +1 -0
- package/dist/ai/src/mcp-auto-configuration.d.ts +232 -0
- package/dist/ai/src/mcp-auto-configuration.d.ts.map +1 -0
- package/dist/ai/src/mcp-auto-configuration.js +445 -0
- package/dist/ai/src/mcp-auto-configuration.js.map +1 -0
- package/dist/ai/src/performance-profiler.d.ts +351 -0
- package/dist/ai/src/performance-profiler.d.ts.map +1 -0
- package/dist/ai/src/performance-profiler.js +475 -0
- package/dist/ai/src/performance-profiler.js.map +1 -0
- package/dist/ai/src/personas/hooks/before-llm-call.d.ts +96 -0
- package/dist/ai/src/personas/hooks/before-llm-call.d.ts.map +1 -0
- package/dist/ai/src/personas/hooks/before-llm-call.js +83 -0
- package/dist/ai/src/personas/hooks/before-llm-call.js.map +1 -0
- package/dist/ai/src/personas/index.d.ts +10 -0
- package/dist/ai/src/personas/index.d.ts.map +1 -0
- package/dist/ai/src/personas/index.js +10 -0
- package/dist/ai/src/personas/index.js.map +1 -0
- package/dist/ai/src/personas/persona-loader.d.ts +42 -0
- package/dist/ai/src/personas/persona-loader.d.ts.map +1 -0
- package/dist/ai/src/personas/persona-loader.js +162 -0
- package/dist/ai/src/personas/persona-loader.js.map +1 -0
- package/dist/ai/src/personas/types.d.ts +199 -0
- package/dist/ai/src/personas/types.d.ts.map +1 -0
- package/dist/ai/src/personas/types.js +7 -0
- package/dist/ai/src/personas/types.js.map +1 -0
- package/dist/ai/src/personas/voice-resolver.d.ts +40 -0
- package/dist/ai/src/personas/voice-resolver.d.ts.map +1 -0
- package/dist/ai/src/personas/voice-resolver.js +201 -0
- package/dist/ai/src/personas/voice-resolver.js.map +1 -0
- package/dist/ai/src/resource-monitor.d.ts +311 -0
- package/dist/ai/src/resource-monitor.d.ts.map +1 -0
- package/dist/ai/src/resource-monitor.js +475 -0
- package/dist/ai/src/resource-monitor.js.map +1 -0
- package/dist/ai/src/runtime/agent-runtime.d.ts +340 -0
- package/dist/ai/src/runtime/agent-runtime.d.ts.map +1 -0
- package/dist/ai/src/runtime/agent-runtime.js +1084 -0
- package/dist/ai/src/runtime/agent-runtime.js.map +1 -0
- package/dist/ai/src/telemetry/delegation-telemetry.d.ts +287 -0
- package/dist/ai/src/telemetry/delegation-telemetry.d.ts.map +1 -0
- package/dist/ai/src/telemetry/delegation-telemetry.js +389 -0
- package/dist/ai/src/telemetry/delegation-telemetry.js.map +1 -0
- package/dist/ai/src/telemetry/index.d.ts +48 -0
- package/dist/ai/src/telemetry/index.d.ts.map +1 -0
- package/dist/ai/src/telemetry/index.js +48 -0
- package/dist/ai/src/telemetry/index.js.map +1 -0
- package/dist/ai/src/telemetry/runtime-telemetry-integration.d.ts +67 -0
- package/dist/ai/src/telemetry/runtime-telemetry-integration.d.ts.map +1 -0
- package/dist/ai/src/telemetry/runtime-telemetry-integration.js +415 -0
- package/dist/ai/src/telemetry/runtime-telemetry-integration.js.map +1 -0
- package/dist/ai/src/telemetry/telemetry-utils.d.ts +119 -0
- package/dist/ai/src/telemetry/telemetry-utils.d.ts.map +1 -0
- package/dist/ai/src/telemetry/telemetry-utils.js +250 -0
- package/dist/ai/src/telemetry/telemetry-utils.js.map +1 -0
- package/dist/ai/src/types/agent-capabilities.d.ts +227 -0
- package/dist/ai/src/types/agent-capabilities.d.ts.map +1 -0
- package/dist/ai/src/types/agent-capabilities.js +11 -0
- package/dist/ai/src/types/agent-capabilities.js.map +1 -0
- package/dist/ai/src/types/context-verification.d.ts +158 -0
- package/dist/ai/src/types/context-verification.d.ts.map +1 -0
- package/dist/ai/src/types/context-verification.js +73 -0
- package/dist/ai/src/types/context-verification.js.map +1 -0
- package/dist/ai/src/types/delegation-contracts.d.ts +296 -0
- package/dist/ai/src/types/delegation-contracts.d.ts.map +1 -0
- package/dist/ai/src/types/delegation-contracts.js +17 -0
- package/dist/ai/src/types/delegation-contracts.js.map +1 -0
- package/dist/ai/src/validation-pipeline-integration.d.ts +266 -0
- package/dist/ai/src/validation-pipeline-integration.d.ts.map +1 -0
- package/dist/ai/src/validation-pipeline-integration.js +695 -0
- package/dist/ai/src/validation-pipeline-integration.js.map +1 -0
- package/dist/ai/src/verification/multi-modal-formatters.d.ts +57 -0
- package/dist/ai/src/verification/multi-modal-formatters.d.ts.map +1 -0
- package/dist/ai/src/verification/multi-modal-formatters.js +655 -0
- package/dist/ai/src/verification/multi-modal-formatters.js.map +1 -0
- package/dist/ai/src/verification/output-formatter.d.ts +186 -0
- package/dist/ai/src/verification/output-formatter.d.ts.map +1 -0
- package/dist/ai/src/verification/output-formatter.js +296 -0
- package/dist/ai/src/verification/output-formatter.js.map +1 -0
- package/dist/ai/src/verification/parser-integration.d.ts +137 -0
- package/dist/ai/src/verification/parser-integration.d.ts.map +1 -0
- package/dist/ai/src/verification/parser-integration.js +273 -0
- package/dist/ai/src/verification/parser-integration.js.map +1 -0
- package/dist/ai/types/agent-capabilities.d.ts +387 -0
- package/dist/ai/types/agent-capabilities.d.ts.map +1 -0
- package/dist/ai/types/agent-capabilities.js +32 -0
- package/dist/ai/types/agent-capabilities.js.map +1 -0
- package/dist/ai/types/delegation-contracts.d.ts +291 -0
- package/dist/ai/types/delegation-contracts.d.ts.map +1 -0
- package/dist/ai/types/delegation-contracts.js +14 -0
- package/dist/ai/types/delegation-contracts.js.map +1 -0
- package/dist/ai/types/index.d.ts +4 -1
- package/dist/ai/types/index.d.ts.map +1 -1
- package/dist/ai/types/index.js +4 -1
- package/dist/ai/types/index.js.map +1 -1
- package/dist/ai/types/permission-tokens.d.ts +365 -0
- package/dist/ai/types/permission-tokens.d.ts.map +1 -0
- package/dist/ai/types/permission-tokens.js +13 -0
- package/dist/ai/types/permission-tokens.js.map +1 -0
- package/dist/ai/types/security-middleware.d.ts +130 -0
- package/dist/ai/types/security-middleware.d.ts.map +1 -0
- package/dist/ai/types/security-middleware.js +13 -0
- package/dist/ai/types/security-middleware.js.map +1 -0
- package/dist/ai/utils/storage.d.ts.map +1 -1
- package/dist/ai/utils/storage.js +6 -3
- package/dist/ai/utils/storage.js.map +1 -1
- package/dist/ai/validation/validation-framework.js +1 -1
- package/dist/ai/verification/policy-framework.d.ts +161 -0
- package/dist/ai/verification/policy-framework.d.ts.map +1 -0
- package/dist/ai/verification/policy-framework.js +436 -0
- package/dist/ai/verification/policy-framework.js.map +1 -0
- package/package.json +36 -8
|
@@ -0,0 +1,1258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCYFR Delegation Contract Manager
|
|
3
|
+
* TLP:CLEAR
|
|
4
|
+
*
|
|
5
|
+
* Manages delegation contracts with database persistence, event emission,
|
|
6
|
+
* and lifecycle tracking.
|
|
7
|
+
*
|
|
8
|
+
* @module delegation/contract-manager
|
|
9
|
+
* @version 1.1.0
|
|
10
|
+
* @date 2026-02-15
|
|
11
|
+
*/
|
|
12
|
+
import { EventEmitter } from 'events';
|
|
13
|
+
import Database from 'better-sqlite3';
|
|
14
|
+
import { ExecutionMode } from '../types/agent-capabilities.js';
|
|
15
|
+
import { BackgroundSessionQueue, MAX_BACKGROUND_SESSIONS } from './session-queue.js';
|
|
16
|
+
import { SessionCheckpoint } from './session-checkpoint.js';
|
|
17
|
+
import { SessionManager } from './session-manager.js';
|
|
18
|
+
import { SecurityMiddlewareChain } from './security-middleware-chain.js';
|
|
19
|
+
import { IdentityMiddleware } from './middleware/identity-middleware.js';
|
|
20
|
+
import { TLPMiddleware } from './middleware/tlp-middleware.js';
|
|
21
|
+
import { ThreatValidatorMiddleware } from './middleware/threat-validator-middleware.js';
|
|
22
|
+
import { RateLimiterMiddleware } from './middleware/rate-limiter-middleware.js';
|
|
23
|
+
import { ChainDepthMiddleware } from './middleware/chain-depth-middleware.js';
|
|
24
|
+
import { ContentPolicyMiddleware } from './middleware/content-policy-middleware.js';
|
|
25
|
+
import { PermissionsMiddleware } from './middleware/permissions-middleware.js';
|
|
26
|
+
import { ReputationMiddleware } from './middleware/reputation-middleware.js';
|
|
27
|
+
import { CircuitBreaker, CircuitBreakerMiddleware } from './circuit-breaker.js';
|
|
28
|
+
import { ContractTimeoutWatchdog } from './timeout-watchdog.js';
|
|
29
|
+
import { BlastRadiusTracker } from './blast-radius-tracker.js';
|
|
30
|
+
import { TLPEnforcementEngine } from '../src/delegation/tlp-enforcement.js';
|
|
31
|
+
import { FeatureFlagMiddleware } from './middleware/feature-flag-middleware.js';
|
|
32
|
+
import { ChainTrackerMiddleware } from './middleware/chain-tracker-middleware.js';
|
|
33
|
+
import { ResourceLimiterMiddleware } from './middleware/resource-limiter-middleware.js';
|
|
34
|
+
import { FeatureFlagManager, DEFAULT_FEATURE_FLAGS } from './feature-flags.js';
|
|
35
|
+
/**
|
|
36
|
+
* DelegationContractManager - Core delegation contract lifecycle management
|
|
37
|
+
*/
|
|
38
|
+
export class DelegationContractManager extends EventEmitter {
|
|
39
|
+
db;
|
|
40
|
+
maxDelegationDepth;
|
|
41
|
+
debug;
|
|
42
|
+
/** In-memory agent name cache (DB may not store names) */
|
|
43
|
+
agentNames = new Map();
|
|
44
|
+
securityThreatEvents = [];
|
|
45
|
+
securityValidationCount = 0;
|
|
46
|
+
securityThreatCount = 0;
|
|
47
|
+
/** Optional registry for manifest-based mode selection. */
|
|
48
|
+
capabilityRegistry;
|
|
49
|
+
/** Background session slot management. */
|
|
50
|
+
backgroundQueue;
|
|
51
|
+
/** Session lifecycle tracking. */
|
|
52
|
+
sessionManager;
|
|
53
|
+
/** Checkpoint persistence. */
|
|
54
|
+
checkpoint;
|
|
55
|
+
/** Pluggable security middleware chain — evaluated on every createContract. */
|
|
56
|
+
securityChain;
|
|
57
|
+
/** Chain depth + fan-out middleware (also manages fan-out counters). */
|
|
58
|
+
chainDepthMiddleware;
|
|
59
|
+
/** Circuit breaker state machine for per-agent failure tracking. */
|
|
60
|
+
circuitBreaker;
|
|
61
|
+
/** Periodic contract timeout watchdog. */
|
|
62
|
+
watchdog;
|
|
63
|
+
/** Optional agent identity registry (enables IdentityMiddleware). */
|
|
64
|
+
agentRegistry;
|
|
65
|
+
/** Blast-radius limiter — caps contract-creation rate per root delegator tree. */
|
|
66
|
+
blastRadiusTracker;
|
|
67
|
+
/** Optional reputation engine (enables ReputationMiddleware + security penalties). */
|
|
68
|
+
reputationEngine;
|
|
69
|
+
/** Optional health monitor wired to live contract stats (8.1). */
|
|
70
|
+
healthMonitor;
|
|
71
|
+
constructor(config = {}) {
|
|
72
|
+
super();
|
|
73
|
+
this.maxDelegationDepth = config.maxDelegationDepth ?? 5;
|
|
74
|
+
this.debug = config.debug ?? false;
|
|
75
|
+
this.capabilityRegistry = config.capabilityRegistry;
|
|
76
|
+
this.agentRegistry = config.agentRegistry;
|
|
77
|
+
this.reputationEngine = config.reputationEngine;
|
|
78
|
+
// 8.1: Wire health monitor to live contract data
|
|
79
|
+
this.healthMonitor = config.healthMonitor;
|
|
80
|
+
if (this.healthMonitor) {
|
|
81
|
+
this.healthMonitor.setContractDataProvider(() => this.getStatistics());
|
|
82
|
+
}
|
|
83
|
+
this.backgroundQueue = new BackgroundSessionQueue(config.maxBackgroundSessions ?? MAX_BACKGROUND_SESSIONS);
|
|
84
|
+
this.sessionManager = new SessionManager({
|
|
85
|
+
archiveBaseDir: config.sessionArchiveDir,
|
|
86
|
+
flushIntervalMs: 60_000,
|
|
87
|
+
});
|
|
88
|
+
this.checkpoint = new SessionCheckpoint(config.checkpointDir);
|
|
89
|
+
// Forward background queue status events upstream
|
|
90
|
+
this.backgroundQueue.on('status', (status) => {
|
|
91
|
+
this.emit('background_queue_status', status);
|
|
92
|
+
});
|
|
93
|
+
const dbPath = config.databasePath ?? ':memory:';
|
|
94
|
+
this.db = new Database(dbPath);
|
|
95
|
+
this.initializeSchema();
|
|
96
|
+
// ── Security middleware chain ──────────────────────────────────────────
|
|
97
|
+
this.blastRadiusTracker = new BlastRadiusTracker();
|
|
98
|
+
this.chainDepthMiddleware = new ChainDepthMiddleware({ maxDepth: this.maxDelegationDepth });
|
|
99
|
+
this.circuitBreaker = new CircuitBreaker();
|
|
100
|
+
this.watchdog = new ContractTimeoutWatchdog();
|
|
101
|
+
this.securityChain = new SecurityMiddlewareChain();
|
|
102
|
+
// 1.3: FeatureFlagMiddleware — kill-switch, must be first in chain.
|
|
103
|
+
// When no featureFlagManager is provided, create a local instance with
|
|
104
|
+
// delegation_enabled=true so existing consumers work out of the box.
|
|
105
|
+
const flagManager = config.featureFlagManager ?? new FeatureFlagManager({
|
|
106
|
+
...DEFAULT_FEATURE_FLAGS,
|
|
107
|
+
delegation_enabled: true,
|
|
108
|
+
});
|
|
109
|
+
this.securityChain.use(new FeatureFlagMiddleware(flagManager));
|
|
110
|
+
if (this.agentRegistry) {
|
|
111
|
+
this.securityChain.use(new IdentityMiddleware(this.agentRegistry));
|
|
112
|
+
}
|
|
113
|
+
// Build TLP engine with any caller-supplied extra clearances
|
|
114
|
+
const tlpEngine = new TLPEnforcementEngine();
|
|
115
|
+
if (config.additionalTLPClearances) {
|
|
116
|
+
for (const c of config.additionalTLPClearances) {
|
|
117
|
+
tlpEngine.setAgentClearance(c);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
this.securityChain.use(new TLPMiddleware(tlpEngine));
|
|
121
|
+
this.securityChain.use(this.chainDepthMiddleware);
|
|
122
|
+
this.securityChain.use(new ThreatValidatorMiddleware());
|
|
123
|
+
this.securityChain.use(new RateLimiterMiddleware(config.rateLimiterOptions));
|
|
124
|
+
this.securityChain.use(new ContentPolicyMiddleware());
|
|
125
|
+
this.securityChain.use(new PermissionsMiddleware((parentContractId) => {
|
|
126
|
+
const parent = this.getContractById(parentContractId);
|
|
127
|
+
return parent?.permission_tokens ?? null;
|
|
128
|
+
}));
|
|
129
|
+
// 7.1: ReputationMiddleware — block low-reputation agents on TLP:AMBER+ tasks
|
|
130
|
+
if (this.reputationEngine) {
|
|
131
|
+
this.securityChain.use(new ReputationMiddleware(this.reputationEngine));
|
|
132
|
+
}
|
|
133
|
+
this.securityChain.use(new CircuitBreakerMiddleware(this.circuitBreaker));
|
|
134
|
+
// 3.4: ChainTrackerMiddleware — loop detection and depth validation (create-only)
|
|
135
|
+
this.securityChain.use(new ChainTrackerMiddleware(this, { maxChainDepth: this.maxDelegationDepth }));
|
|
136
|
+
// 4.3: ResourceLimiterMiddleware — aggregate resource cap (create-only)
|
|
137
|
+
this.securityChain.use(new ResourceLimiterMiddleware(() => this.queryContracts({ status: 'active' })));
|
|
138
|
+
// Forward chain events upstream for observability
|
|
139
|
+
this.securityChain.on('security_warning', (ev) => this.emit('security_warning', ev));
|
|
140
|
+
this.securityChain.on('security_blocked', (ev) => this.emit('security_blocked', ev));
|
|
141
|
+
this.securityChain.on('chain_evaluated', (ev) => this.emit('chain_evaluated', ev));
|
|
142
|
+
// Forward watchdog timeout events and update contract status
|
|
143
|
+
this.watchdog.on('contract_timeout', (ev) => {
|
|
144
|
+
this.emit('contract_timeout', ev);
|
|
145
|
+
try {
|
|
146
|
+
void this.updateContract({ contract_id: ev.contract_id, status: 'timeout' });
|
|
147
|
+
}
|
|
148
|
+
catch { /* contract may have already completed/been removed */ }
|
|
149
|
+
});
|
|
150
|
+
// Forward circuit breaker open → circuit_breaker_tripped (8.4)
|
|
151
|
+
this.circuitBreaker.on('circuit_opened', (ev) => {
|
|
152
|
+
this.emit('circuit_breaker_tripped', { agent_id: ev.agent_id, failure_count: ev.failure_count });
|
|
153
|
+
});
|
|
154
|
+
// 7.2: Security violation penalty — penalise delegatee reputation on threat detection
|
|
155
|
+
if (this.reputationEngine) {
|
|
156
|
+
const reputationEngine = this.reputationEngine;
|
|
157
|
+
this.on('security_threat_detected', (ev) => {
|
|
158
|
+
const contract = ev.contract_id ? this.getContractById(ev.contract_id) : undefined;
|
|
159
|
+
if (!contract?.delegatee?.agent_id)
|
|
160
|
+
return;
|
|
161
|
+
void reputationEngine.updateReputation({
|
|
162
|
+
contract_id: ev.contract_id ?? 'unknown',
|
|
163
|
+
agent_id: contract.delegatee.agent_id,
|
|
164
|
+
agent_name: contract.delegatee.agent_name ?? contract.delegatee.agent_id,
|
|
165
|
+
task_id: contract.task_id ?? 'unknown',
|
|
166
|
+
success: false,
|
|
167
|
+
completion_time_ms: 0,
|
|
168
|
+
security_violations: 1,
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
this.watchdog.start();
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Initialize database schema
|
|
176
|
+
*/
|
|
177
|
+
initializeSchema() {
|
|
178
|
+
this.db.exec(`
|
|
179
|
+
CREATE TABLE IF NOT EXISTS delegation_contracts (
|
|
180
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
181
|
+
contract_id TEXT NOT NULL UNIQUE,
|
|
182
|
+
delegator_agent_id TEXT NOT NULL,
|
|
183
|
+
delegatee_agent_id TEXT NOT NULL,
|
|
184
|
+
task_id TEXT NOT NULL,
|
|
185
|
+
task_description TEXT NOT NULL,
|
|
186
|
+
verification_policy TEXT NOT NULL,
|
|
187
|
+
success_criteria TEXT NOT NULL,
|
|
188
|
+
timeout_ms INTEGER NOT NULL,
|
|
189
|
+
permission_tokens TEXT,
|
|
190
|
+
priority INTEGER DEFAULT 3,
|
|
191
|
+
metadata TEXT,
|
|
192
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
193
|
+
created_at TEXT NOT NULL,
|
|
194
|
+
activated_at TEXT,
|
|
195
|
+
completed_at TEXT,
|
|
196
|
+
verification_result TEXT,
|
|
197
|
+
parent_contract_id TEXT,
|
|
198
|
+
delegation_depth INTEGER DEFAULT 0,
|
|
199
|
+
tlp_classification TEXT
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
CREATE INDEX IF NOT EXISTS idx_delegator ON delegation_contracts(delegator_agent_id);
|
|
203
|
+
CREATE INDEX IF NOT EXISTS idx_delegatee ON delegation_contracts(delegatee_agent_id);
|
|
204
|
+
CREATE INDEX IF NOT EXISTS idx_status ON delegation_contracts(status);
|
|
205
|
+
CREATE INDEX IF NOT EXISTS idx_task_id ON delegation_contracts(task_id);
|
|
206
|
+
CREATE INDEX IF NOT EXISTS idx_parent ON delegation_contracts(parent_contract_id);
|
|
207
|
+
`);
|
|
208
|
+
}
|
|
209
|
+
/** Normalize agents from legacy or current request shapes */
|
|
210
|
+
normalizeContractAgents(request, legacyRequest) {
|
|
211
|
+
const delegator = request.delegator || {
|
|
212
|
+
agent_id: legacyRequest?.delegator_agent_id || legacyRequest?.delegator?.agent_id || 'delegator-agent',
|
|
213
|
+
agent_name: legacyRequest?.delegator?.agent_name || legacyRequest?.delegator?.agent_id || 'Delegator Agent',
|
|
214
|
+
capabilities: legacyRequest?.delegator?.capabilities,
|
|
215
|
+
};
|
|
216
|
+
const delegatee = request.delegatee || {
|
|
217
|
+
agent_id: legacyRequest?.delegatee_agent_id || legacyRequest?.delegatee?.agent_id || 'delegatee-agent',
|
|
218
|
+
agent_name: legacyRequest?.delegatee?.agent_name || legacyRequest?.delegatee?.agent_id || 'Delegatee Agent',
|
|
219
|
+
capabilities: legacyRequest?.delegatee?.capabilities,
|
|
220
|
+
};
|
|
221
|
+
return { delegator, delegatee };
|
|
222
|
+
}
|
|
223
|
+
/** Normalize verification policy from legacy/current values */
|
|
224
|
+
normalizeVerificationPolicy(raw) {
|
|
225
|
+
if (raw === 'manual')
|
|
226
|
+
return 'human_required';
|
|
227
|
+
if (raw === 'automated' || raw === 'capability_match')
|
|
228
|
+
return 'direct_inspection';
|
|
229
|
+
return raw;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 5.3 Sanitize task description text — strip null bytes and Unicode direction
|
|
233
|
+
* override characters while preserving legitimate Unicode (emoji, CJK, etc.).
|
|
234
|
+
*/
|
|
235
|
+
sanitizeTaskDescription(text) {
|
|
236
|
+
return text
|
|
237
|
+
// Remove null bytes
|
|
238
|
+
.replace(/\0/g, '')
|
|
239
|
+
// Remove Unicode direction override characters (U+202A–U+202E, U+2066–U+2069)
|
|
240
|
+
.replace(/[\u202A-\u202E\u2066-\u2069]/g, '');
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* 6.2: Walk the parent chain to find the root delegator agent_id.
|
|
244
|
+
* Falls back to the provided delegator ID if no traceable parent exists.
|
|
245
|
+
*/
|
|
246
|
+
getRootDelegatorId(parentContractId, fallbackDelegatorId) {
|
|
247
|
+
if (!parentContractId) {
|
|
248
|
+
return fallbackDelegatorId;
|
|
249
|
+
}
|
|
250
|
+
// Walk up the chain (guard against cycles with a depth cap)
|
|
251
|
+
let current = this.getContractById(parentContractId);
|
|
252
|
+
for (let i = 0; i < 20 && current; i++) {
|
|
253
|
+
if (!current.parent_contract_id) {
|
|
254
|
+
return current.delegator.agent_id;
|
|
255
|
+
}
|
|
256
|
+
current = this.getContractById(current.parent_contract_id);
|
|
257
|
+
}
|
|
258
|
+
return fallbackDelegatorId;
|
|
259
|
+
}
|
|
260
|
+
/** Run security validation, emit threat event and throw if a threat is detected */
|
|
261
|
+
async validateContractSecurity(request, legacyRequest, normalizedPermissionTokens) {
|
|
262
|
+
this.securityValidationCount++;
|
|
263
|
+
// 2.6: Validate PermissionToken holder/issuer identity binding
|
|
264
|
+
if (normalizedPermissionTokens) {
|
|
265
|
+
for (const token of normalizedPermissionTokens) {
|
|
266
|
+
const t = token;
|
|
267
|
+
if (t.holder !== undefined && t.holder !== request.delegatee.agent_id) {
|
|
268
|
+
throw new Error(`Permission token '${token.token_id}': holder '${t.holder}' does not match delegatee '${request.delegatee.agent_id}'`);
|
|
269
|
+
}
|
|
270
|
+
if (t.issuer !== undefined && t.issuer !== request.delegator.agent_id) {
|
|
271
|
+
throw new Error(`Permission token '${token.token_id}': issuer '${t.issuer}' does not match delegator '${request.delegator.agent_id}'`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// ── Pre-flight threat guards ───────────────────────────────────────────
|
|
276
|
+
// These three checks mirror the behaviour of the original detectSecurityThreat()
|
|
277
|
+
// whose private helpers were removed in task 1.6. We keep them as inline
|
|
278
|
+
// pre-chain guards because the SecurityMiddlewareChain cannot (a) access
|
|
279
|
+
// permission_token.scopes from the array form, (b) evaluate resource
|
|
280
|
+
// requirements outside the DelegationContract type, or (c) guarantee the
|
|
281
|
+
// same risk-score thresholds as the original contract-level checks.
|
|
282
|
+
// Guard 1: critical permission scopes / actions → permission_escalation
|
|
283
|
+
{
|
|
284
|
+
const allScopes = new Set();
|
|
285
|
+
const allActions = new Set();
|
|
286
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
287
|
+
const legacyPt = legacyRequest?.permission_token;
|
|
288
|
+
if (legacyPt && typeof legacyPt === 'object' && !Array.isArray(legacyPt)) {
|
|
289
|
+
for (const s of legacyPt.scopes ?? [])
|
|
290
|
+
allScopes.add(String(s).toLowerCase());
|
|
291
|
+
for (const a of legacyPt.actions ?? [])
|
|
292
|
+
allActions.add(String(a).toLowerCase());
|
|
293
|
+
}
|
|
294
|
+
for (const token of normalizedPermissionTokens ?? []) {
|
|
295
|
+
for (const s of token.scopes ?? [])
|
|
296
|
+
allScopes.add(String(s).toLowerCase()); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
297
|
+
for (const a of token.actions ?? [])
|
|
298
|
+
allActions.add(String(a).toLowerCase()); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
299
|
+
}
|
|
300
|
+
const joined = `${Array.from(allScopes).join(' ')} ${Array.from(allActions).join(' ')}`;
|
|
301
|
+
if (/(root|admin|execute|delete|modify_system|system_admin|root_access|execute_arbitrary)/i.test(joined)) {
|
|
302
|
+
this.securityThreatCount++;
|
|
303
|
+
const te = { contract_id: request.contract_id || `preflight-${Date.now()}`, threat_detected: true, threat_type: 'permission_escalation', severity: 'critical', description: 'Detected high-risk permission scopes or actions.', action: 'block', timestamp: new Date().toISOString() };
|
|
304
|
+
this.securityThreatEvents.push(te);
|
|
305
|
+
this.emit('security_threat_detected', te);
|
|
306
|
+
throw new Error('Security threat detected: permission_escalation');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Guard 2: excessive delegation depth in metadata → permission_escalation
|
|
310
|
+
{
|
|
311
|
+
const depth = Number(legacyRequest?.metadata?.delegation_depth ?? 0);
|
|
312
|
+
if (Number.isFinite(depth) && depth >= 6) {
|
|
313
|
+
this.securityThreatCount++;
|
|
314
|
+
const te = { contract_id: request.contract_id || `preflight-${Date.now()}`, threat_detected: true, threat_type: 'permission_escalation', severity: 'critical', description: 'Delegation chain depth exceeds safe limits.', action: 'block', timestamp: new Date().toISOString() };
|
|
315
|
+
this.securityThreatEvents.push(te);
|
|
316
|
+
this.emit('security_threat_detected', te);
|
|
317
|
+
throw new Error('Security threat detected: permission_escalation');
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// Guard 3: excessive per-contract resource requirements → abuse_pattern
|
|
321
|
+
{
|
|
322
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
323
|
+
const rr = legacyRequest?.resource_requirements;
|
|
324
|
+
const memory = Number(rr?.memory_mb ?? 0);
|
|
325
|
+
const cpu = Number(rr?.cpu_cores ?? 0);
|
|
326
|
+
const disk = Number(rr?.disk_space_mb ?? 0);
|
|
327
|
+
if ((Number.isFinite(memory) && memory > 8192) || (Number.isFinite(cpu) && cpu > 8) || (Number.isFinite(disk) && disk > 512000)) {
|
|
328
|
+
this.securityThreatCount++;
|
|
329
|
+
const te = { contract_id: request.contract_id || `preflight-${Date.now()}`, threat_detected: true, threat_type: 'abuse_pattern', severity: 'critical', description: 'Resource requirements indicate possible abuse or exhaustion attempt.', action: 'block', timestamp: new Date().toISOString() };
|
|
330
|
+
this.securityThreatEvents.push(te);
|
|
331
|
+
this.emit('security_threat_detected', te);
|
|
332
|
+
throw new Error('Security threat detected: abuse_pattern');
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
// ── SecurityMiddlewareChain ────────────────────────────────────────────
|
|
336
|
+
const rawTlp = request.tlp_classification || legacyRequest?.tlp_classification;
|
|
337
|
+
const tlpLevel = rawTlp ? rawTlp.replace('TLP:', '') : undefined;
|
|
338
|
+
const context = {
|
|
339
|
+
operation: 'create',
|
|
340
|
+
contract: {
|
|
341
|
+
contract_id: request.contract_id,
|
|
342
|
+
delegator: request.delegator,
|
|
343
|
+
delegatee: request.delegatee,
|
|
344
|
+
delegation_depth: 0,
|
|
345
|
+
tlp_classification: tlpLevel,
|
|
346
|
+
permission_tokens: normalizedPermissionTokens,
|
|
347
|
+
parent_contract_id: request.parent_contract_id,
|
|
348
|
+
// Expose these fields so ThreatValidatorMiddleware can run its full checks
|
|
349
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
350
|
+
...(legacyRequest?.resource_requirements ? { resource_requirements: legacyRequest.resource_requirements } : {}),
|
|
351
|
+
metadata: legacyRequest?.metadata,
|
|
352
|
+
},
|
|
353
|
+
// Provide auth fields so IdentityMiddleware can verify signatures
|
|
354
|
+
delegator_auth: request.delegator,
|
|
355
|
+
delegatee_auth: request.delegatee,
|
|
356
|
+
task_content: legacyRequest?.task_content ??
|
|
357
|
+
(request.task_description ? { instruction: request.task_description } : undefined),
|
|
358
|
+
metadata: legacyRequest?.metadata,
|
|
359
|
+
timestamp_ms: Date.now(),
|
|
360
|
+
// Enable core security gates by default; callers may override via metadata
|
|
361
|
+
feature_flags: {
|
|
362
|
+
security_monitoring: true,
|
|
363
|
+
chain_tracking: true,
|
|
364
|
+
identity_auth: !!(this.agentRegistry),
|
|
365
|
+
content_security: true,
|
|
366
|
+
reputation_tracking: !!(this.reputationEngine),
|
|
367
|
+
...(legacyRequest?.feature_flags ?? {}),
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
const chainResult = await this.securityChain.evaluate(context);
|
|
371
|
+
if (chainResult.action === 'block') {
|
|
372
|
+
this.securityThreatCount++;
|
|
373
|
+
const bv = chainResult.blocking_verdict;
|
|
374
|
+
const threatEvent = {
|
|
375
|
+
contract_id: request.contract_id || request.task_id || `preflight-${Date.now()}`,
|
|
376
|
+
threat_detected: true,
|
|
377
|
+
threat_type: bv.threat_type ?? 'security_chain_block',
|
|
378
|
+
severity: bv.severity ?? 'critical',
|
|
379
|
+
description: bv.reason ?? 'Security middleware chain blocked this contract.',
|
|
380
|
+
action: 'block',
|
|
381
|
+
timestamp: new Date().toISOString(),
|
|
382
|
+
blocked_by: chainResult.blocked_by,
|
|
383
|
+
evidence: bv.evidence,
|
|
384
|
+
};
|
|
385
|
+
this.securityThreatEvents.push(threatEvent);
|
|
386
|
+
this.emit('security_threat_detected', threatEvent);
|
|
387
|
+
throw new Error(`Security threat detected: ${bv.threat_type ?? 'security_chain_block'}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Create a new delegation contract
|
|
392
|
+
*/
|
|
393
|
+
async createContract(request) {
|
|
394
|
+
const legacyRequest = request;
|
|
395
|
+
const { delegator: normalizedDelegator, delegatee: normalizedDelegatee } = this.normalizeContractAgents(request, legacyRequest);
|
|
396
|
+
const normalizedTaskDescription = this.sanitizeTaskDescription(request.task_description || legacyRequest?.description || request.task_id || 'Delegated task');
|
|
397
|
+
const normalizedTimeout = request.timeout_ms ?? legacyRequest?.timeout_ms ?? 30000;
|
|
398
|
+
const normalizedSuccessCriteria = Array.isArray(request.success_criteria)
|
|
399
|
+
? { required_checks: request.success_criteria }
|
|
400
|
+
: (request.success_criteria || {});
|
|
401
|
+
const rawVerificationPolicy = request.verification_policy || legacyRequest?.verification_policy || 'direct_inspection';
|
|
402
|
+
const normalizedVerificationPolicy = this.normalizeVerificationPolicy(rawVerificationPolicy);
|
|
403
|
+
const normalizedPermissionTokens = request.permission_tokens ||
|
|
404
|
+
(legacyRequest?.permission_token && typeof legacyRequest.permission_token === 'object' && !Array.isArray(legacyRequest.permission_token)
|
|
405
|
+
? [{
|
|
406
|
+
token_id: legacyRequest.permission_token.token_id ?? '',
|
|
407
|
+
scopes: legacyRequest.permission_token.scopes || [],
|
|
408
|
+
delegatable: legacyRequest.permission_token.delegatable ?? false,
|
|
409
|
+
max_delegation_depth: legacyRequest.permission_token.max_delegation_depth ?? 0,
|
|
410
|
+
}]
|
|
411
|
+
: undefined);
|
|
412
|
+
await this.validateContractSecurity(request, legacyRequest, normalizedPermissionTokens);
|
|
413
|
+
// Use explicit contract_id if provided (for testing), otherwise generate
|
|
414
|
+
const contract_id = request.contract_id || `contract-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
415
|
+
const created_at = request.created_at || new Date().toISOString();
|
|
416
|
+
const status = request.status || 'pending';
|
|
417
|
+
// Cache agent names for later retrieval
|
|
418
|
+
this.agentNames.set(normalizedDelegator.agent_id, normalizedDelegator.agent_name);
|
|
419
|
+
this.agentNames.set(normalizedDelegatee.agent_id, normalizedDelegatee.agent_name);
|
|
420
|
+
// Calculate delegation depth
|
|
421
|
+
let delegation_depth = 0;
|
|
422
|
+
if (request.parent_contract_id) {
|
|
423
|
+
const parent = this.getContractById(request.parent_contract_id);
|
|
424
|
+
if (parent) {
|
|
425
|
+
delegation_depth = (parent.delegation_depth ?? 0) + 1;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
// 6.2: Blast radius check — cap contract-creation rate per root delegator tree
|
|
429
|
+
const rootDelegatorId = this.getRootDelegatorId(request.parent_contract_id ?? null, normalizedDelegator.agent_id);
|
|
430
|
+
const blastCheck = this.blastRadiusTracker.check(rootDelegatorId);
|
|
431
|
+
if (!blastCheck.allowed) {
|
|
432
|
+
throw new Error(`Blast radius limit exceeded for root delegator '${rootDelegatorId}': ` +
|
|
433
|
+
`${blastCheck.currentCount}/${blastCheck.limit} contracts in the current window`);
|
|
434
|
+
}
|
|
435
|
+
// Insert contract
|
|
436
|
+
const stmt = this.db.prepare(`
|
|
437
|
+
INSERT INTO delegation_contracts (
|
|
438
|
+
contract_id, delegator_agent_id, delegatee_agent_id,
|
|
439
|
+
task_id, task_description, verification_policy,
|
|
440
|
+
success_criteria, timeout_ms, priority, permission_tokens,
|
|
441
|
+
metadata, parent_contract_id, delegation_depth, tlp_classification,
|
|
442
|
+
status, created_at
|
|
443
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
444
|
+
`);
|
|
445
|
+
// Store execution mode and session ID in metadata (backward-compatible, no schema migration needed)
|
|
446
|
+
const executionMode = request.execution_mode ?? ExecutionMode.INTERACTIVE;
|
|
447
|
+
const sessionId = request.session_id ?? (executionMode !== ExecutionMode.INTERACTIVE
|
|
448
|
+
? `session-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
449
|
+
: undefined);
|
|
450
|
+
const initialMetadata = {
|
|
451
|
+
// 8.4: always persist execution_mode so DelegationContract.execution_mode is never derived from default
|
|
452
|
+
execution_mode: executionMode,
|
|
453
|
+
};
|
|
454
|
+
if (sessionId) {
|
|
455
|
+
initialMetadata['session_id'] = sessionId;
|
|
456
|
+
}
|
|
457
|
+
stmt.run(contract_id, normalizedDelegator.agent_id, normalizedDelegatee.agent_id, request.task_id, normalizedTaskDescription, normalizedVerificationPolicy, JSON.stringify(normalizedSuccessCriteria), normalizedTimeout, request.priority ?? 3, normalizedPermissionTokens ? JSON.stringify(normalizedPermissionTokens) : null, Object.keys(initialMetadata).length > 0 ? JSON.stringify(initialMetadata) : null, request.parent_contract_id ?? null, delegation_depth, request.tlp_classification ?? 'TLP:CLEAR', status, created_at);
|
|
458
|
+
const contract = this.getContractById(contract_id);
|
|
459
|
+
// Emit event
|
|
460
|
+
this.emit('contract_created', contract);
|
|
461
|
+
// 6.2: Record contract in blast radius tracker
|
|
462
|
+
this.blastRadiusTracker.record(rootDelegatorId);
|
|
463
|
+
// Track fan-out counter for the delegator
|
|
464
|
+
if (request.parent_contract_id) {
|
|
465
|
+
this.chainDepthMiddleware.incrementFanOut(normalizedDelegator.agent_id);
|
|
466
|
+
}
|
|
467
|
+
// Register contract with timeout watchdog
|
|
468
|
+
this.watchdog.track({
|
|
469
|
+
contract_id,
|
|
470
|
+
created_at,
|
|
471
|
+
timeout_ms: normalizedTimeout,
|
|
472
|
+
status,
|
|
473
|
+
});
|
|
474
|
+
if (this.debug) {
|
|
475
|
+
console.log(`[ContractManager] Created contract ${contract_id}`);
|
|
476
|
+
}
|
|
477
|
+
return contract;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Query contracts with filters
|
|
481
|
+
*/
|
|
482
|
+
/** Add status condition (array or single value) */
|
|
483
|
+
addStatusCondition(status, conditions, params) {
|
|
484
|
+
if (!status)
|
|
485
|
+
return;
|
|
486
|
+
if (Array.isArray(status)) {
|
|
487
|
+
conditions.push(`status IN (${status.map(() => '?').join(',')})`);
|
|
488
|
+
params.push(...status);
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
conditions.push('status = ?');
|
|
492
|
+
params.push(status);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/** Append ORDER BY / LIMIT / OFFSET clauses */
|
|
496
|
+
appendQueryTail(query, options, params) {
|
|
497
|
+
if (options.sort_by) {
|
|
498
|
+
query += ` ORDER BY ${options.sort_by} ${options.sort_order === 'asc' ? 'ASC' : 'DESC'}`;
|
|
499
|
+
}
|
|
500
|
+
if (options.limit !== undefined) {
|
|
501
|
+
query += ' LIMIT ?';
|
|
502
|
+
params.push(options.limit);
|
|
503
|
+
}
|
|
504
|
+
if (options.offset !== undefined) {
|
|
505
|
+
if (options.limit === undefined)
|
|
506
|
+
query += ' LIMIT -1';
|
|
507
|
+
query += ' OFFSET ?';
|
|
508
|
+
params.push(options.offset);
|
|
509
|
+
}
|
|
510
|
+
return query;
|
|
511
|
+
}
|
|
512
|
+
/** Build WHERE clause and params from query options */
|
|
513
|
+
buildQueryFromOptions(options) {
|
|
514
|
+
const conditions = [];
|
|
515
|
+
const params = [];
|
|
516
|
+
const delegatorId = options.delegator_agent_id ?? options.delegator_id;
|
|
517
|
+
const delegateeId = options.delegatee_agent_id ?? options.delegatee_id;
|
|
518
|
+
if (delegatorId) {
|
|
519
|
+
conditions.push('delegator_agent_id = ?');
|
|
520
|
+
params.push(delegatorId);
|
|
521
|
+
}
|
|
522
|
+
if (delegateeId) {
|
|
523
|
+
conditions.push('delegatee_agent_id = ?');
|
|
524
|
+
params.push(delegateeId);
|
|
525
|
+
}
|
|
526
|
+
if (options.task_id) {
|
|
527
|
+
conditions.push('task_id = ?');
|
|
528
|
+
params.push(options.task_id);
|
|
529
|
+
}
|
|
530
|
+
this.addStatusCondition(options.status, conditions, params);
|
|
531
|
+
if (options.delegation_depth !== undefined) {
|
|
532
|
+
conditions.push('delegation_depth = ?');
|
|
533
|
+
params.push(options.delegation_depth);
|
|
534
|
+
}
|
|
535
|
+
if (options.parent_contract_id) {
|
|
536
|
+
conditions.push('parent_contract_id = ?');
|
|
537
|
+
params.push(options.parent_contract_id);
|
|
538
|
+
}
|
|
539
|
+
if (options.priority !== undefined) {
|
|
540
|
+
conditions.push('priority = ?');
|
|
541
|
+
params.push(options.priority);
|
|
542
|
+
}
|
|
543
|
+
let query = 'SELECT * FROM delegation_contracts';
|
|
544
|
+
if (conditions.length > 0)
|
|
545
|
+
query += ' WHERE ' + conditions.join(' AND ');
|
|
546
|
+
query = this.appendQueryTail(query, options, params);
|
|
547
|
+
return { query, params };
|
|
548
|
+
}
|
|
549
|
+
queryContracts(options = {}) {
|
|
550
|
+
const { query, params } = this.buildQueryFromOptions(options);
|
|
551
|
+
const rows = this.db.prepare(query).all(...params);
|
|
552
|
+
return rows.map(row => this.rowToContract(row));
|
|
553
|
+
}
|
|
554
|
+
/** Build SQL fields/params for an updateContract call */
|
|
555
|
+
buildUpdateFields(updates) {
|
|
556
|
+
const fields = [];
|
|
557
|
+
const params = [];
|
|
558
|
+
if (updates.status) {
|
|
559
|
+
fields.push('status = ?');
|
|
560
|
+
params.push(updates.status);
|
|
561
|
+
if (updates.status === 'active' && !updates.activated_at) {
|
|
562
|
+
fields.push('activated_at = ?');
|
|
563
|
+
params.push(new Date().toISOString());
|
|
564
|
+
}
|
|
565
|
+
if ((updates.status === 'completed' || updates.status === 'revoked') && !updates.completed_at) {
|
|
566
|
+
fields.push('completed_at = ?');
|
|
567
|
+
params.push(new Date().toISOString());
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (updates.activated_at) {
|
|
571
|
+
fields.push('activated_at = ?');
|
|
572
|
+
params.push(updates.activated_at);
|
|
573
|
+
}
|
|
574
|
+
if (updates.completed_at) {
|
|
575
|
+
fields.push('completed_at = ?');
|
|
576
|
+
params.push(updates.completed_at);
|
|
577
|
+
}
|
|
578
|
+
if (updates.verification_result) {
|
|
579
|
+
fields.push('verification_result = ?');
|
|
580
|
+
params.push(JSON.stringify(updates.verification_result));
|
|
581
|
+
}
|
|
582
|
+
if (updates.metadata) {
|
|
583
|
+
fields.push('metadata = ?');
|
|
584
|
+
params.push(JSON.stringify(updates.metadata));
|
|
585
|
+
}
|
|
586
|
+
return { fields, params };
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Update contract status and metadata.
|
|
590
|
+
* Accepts a single object with contract_id and fields to update.
|
|
591
|
+
* Automatically sets activated_at when status='active' and completed_at when status='completed'.
|
|
592
|
+
*/
|
|
593
|
+
async updateContract(updates) {
|
|
594
|
+
const { contract_id } = updates;
|
|
595
|
+
const existing = this.getContractById(contract_id);
|
|
596
|
+
if (!existing) {
|
|
597
|
+
throw new Error(`Contract not found: ${contract_id}`);
|
|
598
|
+
}
|
|
599
|
+
// 2.4: Caller ownership check
|
|
600
|
+
if (updates.caller_id) {
|
|
601
|
+
const isOwner = existing.delegator.agent_id === updates.caller_id ||
|
|
602
|
+
existing.delegatee.agent_id === updates.caller_id;
|
|
603
|
+
if (!isOwner) {
|
|
604
|
+
throw new Error(`Unauthorized: agent '${updates.caller_id}' is not the delegator or delegatee of contract '${contract_id}'`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
// 1.5: Run security chain (FeatureFlags + Identity) for update operations.
|
|
608
|
+
// Heavy create-only middleware (TLP, ThreatValidator, etc.) are skipped via appliesTo.
|
|
609
|
+
const updateContext = {
|
|
610
|
+
operation: 'update',
|
|
611
|
+
contract: {
|
|
612
|
+
contract_id: existing.contract_id,
|
|
613
|
+
delegator: existing.delegator,
|
|
614
|
+
delegatee: existing.delegatee,
|
|
615
|
+
},
|
|
616
|
+
timestamp_ms: Date.now(),
|
|
617
|
+
feature_flags: { identity_auth: !!(this.agentRegistry) },
|
|
618
|
+
};
|
|
619
|
+
const updateResult = await this.securityChain.evaluate(updateContext);
|
|
620
|
+
if (updateResult.action === 'block') {
|
|
621
|
+
const bv = updateResult.blocking_verdict;
|
|
622
|
+
throw new Error(`Security check failed for contract update: ${bv.reason ?? bv.threat_type}`);
|
|
623
|
+
}
|
|
624
|
+
// 5.1: State machine — once a contract reaches a terminal state it cannot be changed
|
|
625
|
+
const terminalStates = new Set(['completed', 'failed', 'cancelled', 'revoked', 'timeout']);
|
|
626
|
+
if (updates.status && terminalStates.has(existing.status)) {
|
|
627
|
+
throw new Error(`Invalid state transition: ${existing.status} → ${updates.status} (terminal state cannot be changed)`);
|
|
628
|
+
}
|
|
629
|
+
// 4.6: Non-empty output validation — reject completion without verification_result
|
|
630
|
+
if (updates.status === 'completed' && !updates.verification_result) {
|
|
631
|
+
throw new Error('Cannot complete contract without verification_result');
|
|
632
|
+
}
|
|
633
|
+
// 6.5: Quarantine on failed verification or quality below threshold
|
|
634
|
+
if (updates.status === 'completed' && updates.verification_result) {
|
|
635
|
+
const vr = updates.verification_result;
|
|
636
|
+
const qualityFail = typeof vr.quality_score === 'number' && vr.quality_score < 0.7;
|
|
637
|
+
if (vr.verified === false || qualityFail) {
|
|
638
|
+
const quarantineReason = vr.verified === false ? 'verification_failed' : 'quality_below_threshold';
|
|
639
|
+
return this.updateContract({
|
|
640
|
+
contract_id,
|
|
641
|
+
status: 'failed',
|
|
642
|
+
verification_result: vr,
|
|
643
|
+
metadata: { ...(existing.metadata ?? {}), quarantined: true, quarantine_reason: quarantineReason },
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const { fields, params } = this.buildUpdateFields(updates);
|
|
648
|
+
if (fields.length === 0) {
|
|
649
|
+
return existing;
|
|
650
|
+
}
|
|
651
|
+
params.push(contract_id);
|
|
652
|
+
const query = `UPDATE delegation_contracts SET ${fields.join(', ')} WHERE contract_id = ?`;
|
|
653
|
+
this.db.prepare(query).run(...params);
|
|
654
|
+
const contract = this.getContractById(contract_id);
|
|
655
|
+
if (!contract) {
|
|
656
|
+
throw new Error(`Contract not found: ${contract_id}`);
|
|
657
|
+
}
|
|
658
|
+
// Emit events based on status
|
|
659
|
+
if (updates.status === 'completed') {
|
|
660
|
+
this.emit('contract_completed', contract);
|
|
661
|
+
}
|
|
662
|
+
else if (updates.status === 'failed') {
|
|
663
|
+
this.emit('contract_failed', contract);
|
|
664
|
+
}
|
|
665
|
+
else if (updates.status === 'revoked') {
|
|
666
|
+
this.emit('contract_revoked', contract);
|
|
667
|
+
}
|
|
668
|
+
else if (updates.status === 'cancelled') {
|
|
669
|
+
this.emit('contract_cancelled', contract);
|
|
670
|
+
}
|
|
671
|
+
else if (updates.status === 'timeout') {
|
|
672
|
+
this.emit('contract_timeout_fired', contract);
|
|
673
|
+
}
|
|
674
|
+
// 6.3: Cascading revocation — when a contract is revoked, revoke all non-terminal children
|
|
675
|
+
if (updates.status === 'revoked') {
|
|
676
|
+
const terminalSet = new Set(['completed', 'failed', 'cancelled', 'revoked', 'timeout']);
|
|
677
|
+
const children = this.queryContracts({ parent_contract_id: contract_id });
|
|
678
|
+
for (const child of children) {
|
|
679
|
+
if (!terminalSet.has(child.status)) {
|
|
680
|
+
await this.updateContract({ contract_id: child.contract_id, status: 'revoked' });
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
// On terminal status: release fan-out slot + untrack from watchdog
|
|
685
|
+
const terminalStatuses = ['completed', 'failed', 'cancelled', 'revoked', 'timeout'];
|
|
686
|
+
if (updates.status && terminalStatuses.includes(updates.status)) {
|
|
687
|
+
if (contract.parent_contract_id) {
|
|
688
|
+
this.chainDepthMiddleware.decrementFanOut(contract.delegator.agent_id);
|
|
689
|
+
}
|
|
690
|
+
this.watchdog.untrack(contract_id);
|
|
691
|
+
// Record circuit breaker outcome
|
|
692
|
+
if (updates.status === 'completed') {
|
|
693
|
+
this.circuitBreaker.recordSuccess(contract.delegatee.agent_id);
|
|
694
|
+
}
|
|
695
|
+
else if (updates.status === 'failed') {
|
|
696
|
+
this.circuitBreaker.recordFailure(contract.delegatee.agent_id);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (this.debug) {
|
|
700
|
+
console.log(`[ContractManager] Updated contract ${contract_id}: status=${updates.status}`);
|
|
701
|
+
}
|
|
702
|
+
return contract;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Soft delete (revoke) a contract
|
|
706
|
+
*/
|
|
707
|
+
async deleteContract(contract_id, reason) {
|
|
708
|
+
const contract = this.getContractById(contract_id);
|
|
709
|
+
if (!contract) {
|
|
710
|
+
throw new Error(`Contract not found: ${contract_id}`);
|
|
711
|
+
}
|
|
712
|
+
await this.updateContract({
|
|
713
|
+
contract_id,
|
|
714
|
+
status: 'revoked',
|
|
715
|
+
});
|
|
716
|
+
if (this.debug) {
|
|
717
|
+
console.log(`[ContractManager] Deleted contract ${contract_id}: ${reason ?? 'no reason'}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Update contract status (convenience method)
|
|
722
|
+
*/
|
|
723
|
+
async updateContractStatus(contract_id, status, options) {
|
|
724
|
+
const updates = { contract_id, status };
|
|
725
|
+
// Add verification_result if provided
|
|
726
|
+
if (options?.verification_result) {
|
|
727
|
+
updates.verification_result = options.verification_result;
|
|
728
|
+
}
|
|
729
|
+
// Merge metadata if provided
|
|
730
|
+
if (options?.metadata) {
|
|
731
|
+
// Get existing contract to merge metadata
|
|
732
|
+
const existing = this.getContractById(contract_id);
|
|
733
|
+
const existingMetadata = existing?.metadata || {};
|
|
734
|
+
updates.metadata = { ...existingMetadata, ...options.metadata };
|
|
735
|
+
}
|
|
736
|
+
return this.updateContract(updates);
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Cancel a contract (convenience method for cancellation)
|
|
740
|
+
*/
|
|
741
|
+
async cancelContract(contract_id, reason) {
|
|
742
|
+
const contract = this.getContractById(contract_id);
|
|
743
|
+
if (!contract) {
|
|
744
|
+
throw new Error(`Contract not found: ${contract_id}`);
|
|
745
|
+
}
|
|
746
|
+
await this.updateContract({
|
|
747
|
+
contract_id,
|
|
748
|
+
status: 'cancelled',
|
|
749
|
+
metadata: reason ? { cancellation_reason: reason } : undefined,
|
|
750
|
+
});
|
|
751
|
+
if (this.debug) {
|
|
752
|
+
console.log(`[ContractManager] Cancelled contract ${contract_id}: ${reason ?? 'no reason'}`);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Send a heartbeat for a long-running active contract.
|
|
757
|
+
* Extends the timeout watchdog deadline by `heartbeatGraceMs` (default 30 s).
|
|
758
|
+
* The contract's `last_heartbeat_at` is recorded in metadata.
|
|
759
|
+
*
|
|
760
|
+
* @param contract_id - The active contract to keep alive.
|
|
761
|
+
*/
|
|
762
|
+
async heartbeat(contract_id) {
|
|
763
|
+
const contract = this.getContractById(contract_id);
|
|
764
|
+
if (!contract)
|
|
765
|
+
throw new Error(`Contract not found: ${contract_id}`);
|
|
766
|
+
if (contract.status !== 'active')
|
|
767
|
+
throw new Error(`Contract ${contract_id} is not active (status: ${contract.status})`);
|
|
768
|
+
const now = new Date().toISOString();
|
|
769
|
+
const metadata = { ...(contract.metadata ?? {}), last_heartbeat_at: now };
|
|
770
|
+
await this.updateContract({ contract_id, metadata });
|
|
771
|
+
this.watchdog.heartbeat(contract_id);
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Get a single contract by ID
|
|
775
|
+
*/
|
|
776
|
+
getContract(contract_id) {
|
|
777
|
+
const row = this.db
|
|
778
|
+
.prepare('SELECT * FROM delegation_contracts WHERE contract_id = ?')
|
|
779
|
+
.get(contract_id);
|
|
780
|
+
return row ? this.rowToContract(row) : null;
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Get contract by ID (alias for getContract)
|
|
784
|
+
*/
|
|
785
|
+
getContractById(contract_id) {
|
|
786
|
+
return this.getContract(contract_id);
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Get active contracts for an agent
|
|
790
|
+
*/
|
|
791
|
+
getActiveContracts(agent_id) {
|
|
792
|
+
return this.queryContracts({
|
|
793
|
+
delegatee_agent_id: agent_id,
|
|
794
|
+
status: ['pending', 'active'],
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Get contract statistics
|
|
799
|
+
*/
|
|
800
|
+
getStatistics(agent_id) {
|
|
801
|
+
let query = `
|
|
802
|
+
SELECT
|
|
803
|
+
COUNT(*) as total,
|
|
804
|
+
SUM(CASE WHEN status IN ('active', 'pending') THEN 1 ELSE 0 END) as active,
|
|
805
|
+
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
|
|
806
|
+
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
|
|
807
|
+
FROM delegation_contracts
|
|
808
|
+
`;
|
|
809
|
+
const params = [];
|
|
810
|
+
if (agent_id) {
|
|
811
|
+
query += ' WHERE delegatee_agent_id = ?';
|
|
812
|
+
params.push(agent_id);
|
|
813
|
+
}
|
|
814
|
+
const stats = this.db.prepare(query).get(...params);
|
|
815
|
+
const total = stats?.total || 0;
|
|
816
|
+
const completed = stats?.completed || 0;
|
|
817
|
+
const failed = stats?.failed || 0;
|
|
818
|
+
const decidedTotal = completed + failed;
|
|
819
|
+
return {
|
|
820
|
+
total,
|
|
821
|
+
active: stats?.active || 0,
|
|
822
|
+
completed,
|
|
823
|
+
failed,
|
|
824
|
+
success_rate: decidedTotal > 0
|
|
825
|
+
? completed / decidedTotal
|
|
826
|
+
: 0,
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Close database connection
|
|
831
|
+
*/
|
|
832
|
+
close() {
|
|
833
|
+
this.db.close();
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Clear all contracts (for testing)
|
|
837
|
+
*/
|
|
838
|
+
clearAll() {
|
|
839
|
+
this.db.prepare('DELETE FROM delegation_contracts').run();
|
|
840
|
+
// Safely attempt to clear audit log if it exists
|
|
841
|
+
try {
|
|
842
|
+
this.db.prepare('DELETE FROM reputation_audit_log').run();
|
|
843
|
+
}
|
|
844
|
+
catch {
|
|
845
|
+
// Table may not exist
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* Get total contract count (legacy compatibility helper)
|
|
850
|
+
*/
|
|
851
|
+
getContractCount() {
|
|
852
|
+
const row = this.db.prepare('SELECT COUNT(*) as count FROM delegation_contracts').get();
|
|
853
|
+
return row?.count ?? 0;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Get security threat statistics (legacy compatibility helper)
|
|
857
|
+
*/
|
|
858
|
+
getSecurityThreatStatistics() {
|
|
859
|
+
const threat_types = {};
|
|
860
|
+
const severity_distribution = {};
|
|
861
|
+
const action_distribution = {};
|
|
862
|
+
for (const threat of this.securityThreatEvents) {
|
|
863
|
+
const type = String(threat.threat_type || 'unknown');
|
|
864
|
+
const severity = String(threat.severity || 'warning');
|
|
865
|
+
const action = String(threat.action || 'block');
|
|
866
|
+
threat_types[type] = (threat_types[type] || 0) + 1;
|
|
867
|
+
severity_distribution[severity] = (severity_distribution[severity] || 0) + 1;
|
|
868
|
+
action_distribution[action] = (action_distribution[action] || 0) + 1;
|
|
869
|
+
}
|
|
870
|
+
return {
|
|
871
|
+
total_validations: this.securityValidationCount,
|
|
872
|
+
threats_detected: this.securityThreatCount,
|
|
873
|
+
threat_types,
|
|
874
|
+
severity_distribution,
|
|
875
|
+
action_distribution,
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Get recent security threats (legacy compatibility helper)
|
|
880
|
+
*/
|
|
881
|
+
getRecentSecurityThreats(limit = 10) {
|
|
882
|
+
return this.securityThreatEvents.slice(-limit).reverse();
|
|
883
|
+
}
|
|
884
|
+
/** Build security recommendations based on threat statistics */
|
|
885
|
+
buildSecurityRecommendations(stats, threatRate) {
|
|
886
|
+
const recommendations = [];
|
|
887
|
+
if (stats.threats_detected > 0) {
|
|
888
|
+
recommendations.push('Review and audit blocked delegation contracts for threat patterns.');
|
|
889
|
+
}
|
|
890
|
+
if (threatRate > 0.25) {
|
|
891
|
+
recommendations.push('Threat detection rate is elevated; consider tightening delegation policies.');
|
|
892
|
+
}
|
|
893
|
+
if (recommendations.length === 0) {
|
|
894
|
+
recommendations.push('Maintain periodic security review of delegation contracts and policies.');
|
|
895
|
+
}
|
|
896
|
+
return recommendations;
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Get security status summary (legacy compatibility helper)
|
|
900
|
+
*/
|
|
901
|
+
getSecurityStatus() {
|
|
902
|
+
const stats = this.getSecurityThreatStatistics();
|
|
903
|
+
const totalContracts = this.getContractCount();
|
|
904
|
+
const threatRate = stats.total_validations > 0 ? stats.threats_detected / stats.total_validations : 0;
|
|
905
|
+
return {
|
|
906
|
+
tlp_enforcement_enabled: true,
|
|
907
|
+
security_threat_validation_enabled: true,
|
|
908
|
+
contract_security_summary: {
|
|
909
|
+
total_contracts: totalContracts,
|
|
910
|
+
security_validations_performed: stats.total_validations,
|
|
911
|
+
threats_detected: stats.threats_detected,
|
|
912
|
+
threat_detection_rate: threatRate,
|
|
913
|
+
threat_types: stats.threat_types,
|
|
914
|
+
severity_distribution: stats.severity_distribution,
|
|
915
|
+
action_distribution: stats.action_distribution,
|
|
916
|
+
},
|
|
917
|
+
recent_security_events: this.getRecentSecurityThreats(10),
|
|
918
|
+
security_recommendations: this.buildSecurityRecommendations(stats, threatRate),
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
922
|
+
// Execution Mode: Phase 3 – Selection, Lifecycle Hooks, Handoff
|
|
923
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
924
|
+
/**
|
|
925
|
+
* Select the execution mode for a delegation request using a 4-tier priority:
|
|
926
|
+
*
|
|
927
|
+
* 1. **Explicit** — `request.execution_mode` field (highest priority)
|
|
928
|
+
* 2. **OpenSpec hint** — `request.metadata?.openspec_execution_mode`
|
|
929
|
+
* 3. **Agent manifest preference** — `delegateeManifest.preferred_execution_mode`
|
|
930
|
+
* 4. **Default** — `ExecutionMode.INTERACTIVE`
|
|
931
|
+
*
|
|
932
|
+
* In addition, the selected mode must be in the agent's `supported_execution_modes`
|
|
933
|
+
* (when declared), and background mode requires available queue capacity.
|
|
934
|
+
*
|
|
935
|
+
* @returns The resolved `ExecutionMode`.
|
|
936
|
+
*/
|
|
937
|
+
selectExecutionMode(request, delegateeManifest) {
|
|
938
|
+
const supportedModes = delegateeManifest
|
|
939
|
+
? new Set(delegateeManifest.supported_execution_modes) // 8.5: always present
|
|
940
|
+
: new Set(Object.values(ExecutionMode)); // no manifest — all modes allowed
|
|
941
|
+
const supportsMode = (m) => supportedModes.has(m);
|
|
942
|
+
// Tier 1: Explicit override
|
|
943
|
+
if (request.execution_mode && supportsMode(request.execution_mode)) {
|
|
944
|
+
return this._applyQueueGuard(request.execution_mode);
|
|
945
|
+
}
|
|
946
|
+
// Tier 2: OpenSpec hint stored in metadata
|
|
947
|
+
const openspecHint = request.metadata?.openspec_execution_mode;
|
|
948
|
+
if (openspecHint && Object.values(ExecutionMode).includes(openspecHint) && supportsMode(openspecHint)) {
|
|
949
|
+
return this._applyQueueGuard(openspecHint);
|
|
950
|
+
}
|
|
951
|
+
// Tier 3: Agent manifest preference
|
|
952
|
+
const manifestPref = delegateeManifest?.preferred_execution_mode;
|
|
953
|
+
if (manifestPref && supportsMode(manifestPref)) {
|
|
954
|
+
return this._applyQueueGuard(manifestPref);
|
|
955
|
+
}
|
|
956
|
+
// Tier 4: Default
|
|
957
|
+
return ExecutionMode.INTERACTIVE;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Degrade BACKGROUND → INTERACTIVE when the queue is at capacity.
|
|
961
|
+
* @private
|
|
962
|
+
*/
|
|
963
|
+
_applyQueueGuard(mode) {
|
|
964
|
+
if (mode === ExecutionMode.BACKGROUND && !this.backgroundQueue.hasCapacity()) {
|
|
965
|
+
this.emit('background_queue_full', this.backgroundQueue.getStatus());
|
|
966
|
+
return ExecutionMode.INTERACTIVE;
|
|
967
|
+
}
|
|
968
|
+
return mode;
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* 7.3 — Rank a list of candidate agent IDs by their reputation reliability_score,
|
|
972
|
+
* highest first. Falls back to the original order when the reputation engine is
|
|
973
|
+
* unavailable or when `featureFlags.reputation_tracking === false`.
|
|
974
|
+
*/
|
|
975
|
+
async rankCandidatesByReputation(agentIds, featureFlags) {
|
|
976
|
+
if (!this.reputationEngine || featureFlags?.reputation_tracking === false) {
|
|
977
|
+
return agentIds;
|
|
978
|
+
}
|
|
979
|
+
const scored = await Promise.all(agentIds.map(async (id) => {
|
|
980
|
+
const rep = await this.reputationEngine.getReputation(id);
|
|
981
|
+
return { id, score: rep?.reliability_score ?? 0.5 };
|
|
982
|
+
}));
|
|
983
|
+
return scored.sort((a, b) => b.score - a.score).map((e) => e.id);
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Get the current background session queue status.
|
|
987
|
+
*/
|
|
988
|
+
getBackgroundQueueStatus() {
|
|
989
|
+
return this.backgroundQueue.getStatus();
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Expose the session manager for external lifecycle queries.
|
|
993
|
+
*/
|
|
994
|
+
getSessionManager() {
|
|
995
|
+
return this.sessionManager;
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Expose the session checkpoint manager.
|
|
999
|
+
*/
|
|
1000
|
+
getCheckpoint() {
|
|
1001
|
+
return this.checkpoint;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* 8.1 — Expose the optional health monitor so callers can start/stop monitoring
|
|
1005
|
+
* and retrieve live metrics.
|
|
1006
|
+
*/
|
|
1007
|
+
getHealthMonitor() {
|
|
1008
|
+
return this.healthMonitor;
|
|
1009
|
+
}
|
|
1010
|
+
// ─────────── Lifecycle Hooks ───────────
|
|
1011
|
+
/**
|
|
1012
|
+
* **Phase 3.2 / 3.4** — Must be called after creating a BACKGROUND contract.
|
|
1013
|
+
* - Acquires a background queue slot (waits if at capacity)
|
|
1014
|
+
* - Registers the session in the session manager
|
|
1015
|
+
* - Optionally records the worktree path returned by `worktree-coordinator`
|
|
1016
|
+
*
|
|
1017
|
+
* Emits: `session.created`
|
|
1018
|
+
*/
|
|
1019
|
+
async beforeBackgroundExecution(contractId, sessionId, worktreePath) {
|
|
1020
|
+
// Acquire queue slot
|
|
1021
|
+
await this.backgroundQueue.acquire(sessionId, contractId);
|
|
1022
|
+
// Persist session_id into contract metadata so handoffSession can resolve it
|
|
1023
|
+
try {
|
|
1024
|
+
const existing = this.getContractById(contractId);
|
|
1025
|
+
if (existing) {
|
|
1026
|
+
const updatedMetadata = { ...existing.metadata, session_id: sessionId };
|
|
1027
|
+
await this.updateContract({ contract_id: contractId, metadata: updatedMetadata });
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
catch {
|
|
1031
|
+
// Non-fatal — handoff will fall back gracefully
|
|
1032
|
+
}
|
|
1033
|
+
// Register session
|
|
1034
|
+
const sessionState = {
|
|
1035
|
+
status: 'active',
|
|
1036
|
+
conversationMessages: [],
|
|
1037
|
+
lastActivity: new Date().toISOString(),
|
|
1038
|
+
...(worktreePath !== undefined && { worktreePath }),
|
|
1039
|
+
};
|
|
1040
|
+
this.sessionManager.register(sessionId, contractId, ExecutionMode.BACKGROUND, sessionState);
|
|
1041
|
+
this.emit('session.created', { sessionId, contractId, mode: ExecutionMode.BACKGROUND, worktreePath });
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* **Phase 3.2 / 3.4** — Must be called when a BACKGROUND contract completes.
|
|
1045
|
+
* - Releases the background queue slot
|
|
1046
|
+
* - Archives the session
|
|
1047
|
+
*
|
|
1048
|
+
* Emits: `session.archived`
|
|
1049
|
+
*/
|
|
1050
|
+
afterBackgroundExecution(contractId, sessionId) {
|
|
1051
|
+
this.backgroundQueue.release(sessionId);
|
|
1052
|
+
try {
|
|
1053
|
+
this.sessionManager.archive(sessionId);
|
|
1054
|
+
}
|
|
1055
|
+
catch {
|
|
1056
|
+
// Session may have already been archived via handoff — safe to ignore
|
|
1057
|
+
}
|
|
1058
|
+
this.emit('session.archived', { sessionId, contractId, mode: ExecutionMode.BACKGROUND });
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* **Phase 3.5** — Must be called after creating an ASYNC contract.
|
|
1062
|
+
* - Registers the session in the session manager
|
|
1063
|
+
* - Optionally records the feature branch name
|
|
1064
|
+
*
|
|
1065
|
+
* Emits: `session.created`
|
|
1066
|
+
*/
|
|
1067
|
+
beforeAsyncExecution(contractId, sessionId, branchName) {
|
|
1068
|
+
const sessionState = {
|
|
1069
|
+
status: 'active',
|
|
1070
|
+
conversationMessages: [],
|
|
1071
|
+
lastActivity: new Date().toISOString(),
|
|
1072
|
+
};
|
|
1073
|
+
this.sessionManager.register(sessionId, contractId, ExecutionMode.ASYNC, sessionState);
|
|
1074
|
+
this.emit('session.created', { sessionId, contractId, mode: ExecutionMode.ASYNC, branchName });
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* **Phase 3.5** — Must be called when an ASYNC contract completes.
|
|
1078
|
+
* - Archives the session
|
|
1079
|
+
* - Optionally records the PR number in session metadata before archiving
|
|
1080
|
+
*
|
|
1081
|
+
* Emits: `session.archived`
|
|
1082
|
+
*/
|
|
1083
|
+
afterAsyncExecution(contractId, sessionId, prNumber) {
|
|
1084
|
+
// Record PR number in session state before archiving
|
|
1085
|
+
if (prNumber !== undefined) {
|
|
1086
|
+
try {
|
|
1087
|
+
this.sessionManager.updateState(sessionId, { prNumber });
|
|
1088
|
+
}
|
|
1089
|
+
catch {
|
|
1090
|
+
// Session may not be registered — no-op
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
try {
|
|
1094
|
+
this.sessionManager.archive(sessionId);
|
|
1095
|
+
}
|
|
1096
|
+
catch {
|
|
1097
|
+
// Already archived — safe to ignore
|
|
1098
|
+
}
|
|
1099
|
+
this.emit('session.archived', { sessionId, contractId, mode: ExecutionMode.ASYNC, prNumber });
|
|
1100
|
+
}
|
|
1101
|
+
// ─────────── Session Handoff ───────────
|
|
1102
|
+
/**
|
|
1103
|
+
* **Phase 3.6** — Perform an atomic session handoff between execution modes.
|
|
1104
|
+
*
|
|
1105
|
+
* Steps (all-or-nothing; rolls back on failure):
|
|
1106
|
+
* 1. Create a `pre-handoff` checkpoint of the current session state
|
|
1107
|
+
* 2. Create a new contract in the target execution mode
|
|
1108
|
+
* 3. Archive the original session
|
|
1109
|
+
* 4. Emit `session.handoff`
|
|
1110
|
+
*
|
|
1111
|
+
* @param request - Source contract ID, target mode, context snapshot, and reason
|
|
1112
|
+
* @returns The newly created contract (in the target mode)
|
|
1113
|
+
* @throws If the source contract does not exist or the new contract cannot be created
|
|
1114
|
+
*/
|
|
1115
|
+
async handoffSession(request) {
|
|
1116
|
+
const { fromContractId, toExecutionMode, contextSnapshot, handoffReason } = request;
|
|
1117
|
+
// 1. Load source contract
|
|
1118
|
+
const sourceContract = this.getContractById(fromContractId);
|
|
1119
|
+
if (!sourceContract) {
|
|
1120
|
+
throw new Error(`handoffSession: source contract not found: ${fromContractId}`);
|
|
1121
|
+
}
|
|
1122
|
+
// 2.5: Delegatee ownership check — only the delegatee may initiate a handoff
|
|
1123
|
+
if (request.caller_id && sourceContract.delegatee.agent_id !== request.caller_id) {
|
|
1124
|
+
throw new Error(`Unauthorized: only delegatee '${sourceContract.delegatee.agent_id}' can initiate handoff from '${fromContractId}'`);
|
|
1125
|
+
}
|
|
1126
|
+
// 1.5: Run security chain (FeatureFlags + Identity) for handoff operations.
|
|
1127
|
+
// Heavy create-only middleware are skipped via appliesTo.
|
|
1128
|
+
const handoffCtx = {
|
|
1129
|
+
operation: 'handoff',
|
|
1130
|
+
contract: {
|
|
1131
|
+
contract_id: sourceContract.contract_id,
|
|
1132
|
+
delegator: sourceContract.delegator,
|
|
1133
|
+
delegatee: sourceContract.delegatee,
|
|
1134
|
+
},
|
|
1135
|
+
timestamp_ms: Date.now(),
|
|
1136
|
+
feature_flags: { identity_auth: !!(this.agentRegistry) },
|
|
1137
|
+
};
|
|
1138
|
+
const handoffResult = await this.securityChain.evaluate(handoffCtx);
|
|
1139
|
+
if (handoffResult.action === 'block') {
|
|
1140
|
+
const bv = handoffResult.blocking_verdict;
|
|
1141
|
+
throw new Error(`Security check failed for handoff: ${bv.reason ?? bv.threat_type}`);
|
|
1142
|
+
}
|
|
1143
|
+
const fromSessionId = sourceContract.session_id
|
|
1144
|
+
?? this.sessionManager.getActiveSessionForContract(fromContractId)?.sessionId;
|
|
1145
|
+
const fromMode = sourceContract.execution_mode; // 8.4: always present, no fallback needed
|
|
1146
|
+
// 2. Checkpoint current state before handoff
|
|
1147
|
+
const messageCount = Array.isArray(contextSnapshot?.conversationHistory)
|
|
1148
|
+
? contextSnapshot.conversationHistory.length
|
|
1149
|
+
: 0;
|
|
1150
|
+
let checkpointId;
|
|
1151
|
+
if (fromSessionId) {
|
|
1152
|
+
try {
|
|
1153
|
+
const sessionState = {
|
|
1154
|
+
status: 'active',
|
|
1155
|
+
conversationMessages: contextSnapshot?.conversationHistory ?? [],
|
|
1156
|
+
lastActivity: new Date().toISOString(),
|
|
1157
|
+
};
|
|
1158
|
+
const cp = this.checkpoint.create(fromSessionId, fromContractId, sessionState, 'pre-handoff', messageCount, { toMode: toExecutionMode, reason: handoffReason });
|
|
1159
|
+
checkpointId = cp.id;
|
|
1160
|
+
}
|
|
1161
|
+
catch {
|
|
1162
|
+
// Non-fatal — continue with handoff
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
// 3. Create new contract in the target mode
|
|
1166
|
+
let newContract;
|
|
1167
|
+
try {
|
|
1168
|
+
newContract = await this.createContract({
|
|
1169
|
+
delegator: sourceContract.delegator,
|
|
1170
|
+
delegatee: sourceContract.delegatee,
|
|
1171
|
+
task_id: `${sourceContract.task_id}-handoff-${Date.now()}`,
|
|
1172
|
+
task_description: sourceContract.task_description,
|
|
1173
|
+
verification_policy: sourceContract.verification_policy,
|
|
1174
|
+
success_criteria: sourceContract.success_criteria,
|
|
1175
|
+
timeout_ms: sourceContract.timeout_ms,
|
|
1176
|
+
priority: sourceContract.priority,
|
|
1177
|
+
parent_contract_id: fromContractId,
|
|
1178
|
+
tlp_classification: sourceContract.tlp_classification,
|
|
1179
|
+
execution_mode: toExecutionMode,
|
|
1180
|
+
...(request.newContractOverrides ?? {}),
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
catch (err) {
|
|
1184
|
+
// Rollback: restore source contract to active (it was never changed)
|
|
1185
|
+
throw new Error(`handoffSession: failed to create target contract — ${err.message}`);
|
|
1186
|
+
}
|
|
1187
|
+
// 4. Archive original session
|
|
1188
|
+
if (fromSessionId) {
|
|
1189
|
+
try {
|
|
1190
|
+
this.sessionManager.archive(fromSessionId);
|
|
1191
|
+
if (fromMode === ExecutionMode.BACKGROUND) {
|
|
1192
|
+
this.backgroundQueue.release(fromSessionId);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
catch {
|
|
1196
|
+
// Already archived — safe to ignore
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
// 5. Record handoff history and emit event
|
|
1200
|
+
const handoffRecord = {
|
|
1201
|
+
fromContractId,
|
|
1202
|
+
toContractId: newContract.contract_id,
|
|
1203
|
+
fromMode,
|
|
1204
|
+
toMode: toExecutionMode,
|
|
1205
|
+
handoffReason,
|
|
1206
|
+
handoffAt: new Date().toISOString(),
|
|
1207
|
+
contextSnapshot: {
|
|
1208
|
+
conversationHistory: contextSnapshot?.conversationHistory ?? [],
|
|
1209
|
+
artifacts: contextSnapshot?.artifacts ?? [],
|
|
1210
|
+
checkpointId,
|
|
1211
|
+
},
|
|
1212
|
+
};
|
|
1213
|
+
this.emit('session.handoff', handoffRecord);
|
|
1214
|
+
return newContract;
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Convert database row to DelegationContract
|
|
1218
|
+
*/
|
|
1219
|
+
rowToContract(row) {
|
|
1220
|
+
const metadata = row.metadata ? JSON.parse(row.metadata) : {};
|
|
1221
|
+
// Recover execution_mode / session_id stored in metadata (backward-compatible)
|
|
1222
|
+
const executionMode = metadata['execution_mode']
|
|
1223
|
+
?? ExecutionMode.INTERACTIVE;
|
|
1224
|
+
const sessionId = metadata['session_id'];
|
|
1225
|
+
return {
|
|
1226
|
+
contract_id: row.contract_id,
|
|
1227
|
+
delegator: {
|
|
1228
|
+
agent_id: row.delegator_agent_id,
|
|
1229
|
+
agent_name: this.agentNames.get(row.delegator_agent_id) ?? row.delegator_agent_id,
|
|
1230
|
+
},
|
|
1231
|
+
delegatee: {
|
|
1232
|
+
agent_id: row.delegatee_agent_id,
|
|
1233
|
+
agent_name: this.agentNames.get(row.delegatee_agent_id) ?? row.delegatee_agent_id,
|
|
1234
|
+
},
|
|
1235
|
+
task_id: row.task_id,
|
|
1236
|
+
task_description: row.task_description,
|
|
1237
|
+
verification_policy: row.verification_policy,
|
|
1238
|
+
success_criteria: JSON.parse(row.success_criteria),
|
|
1239
|
+
timeout_ms: row.timeout_ms,
|
|
1240
|
+
priority: row.priority,
|
|
1241
|
+
permission_tokens: row.permission_tokens ? JSON.parse(row.permission_tokens) : undefined,
|
|
1242
|
+
status: row.status,
|
|
1243
|
+
created_at: row.created_at,
|
|
1244
|
+
activated_at: row.activated_at ?? undefined,
|
|
1245
|
+
completed_at: row.completed_at ?? undefined,
|
|
1246
|
+
verification_result: row.verification_result ? JSON.parse(row.verification_result) : undefined,
|
|
1247
|
+
parent_contract_id: row.parent_contract_id ?? undefined,
|
|
1248
|
+
delegation_depth: row.delegation_depth ?? 0,
|
|
1249
|
+
tlp_classification: row.tlp_classification ?? 'CLEAR',
|
|
1250
|
+
execution_mode: executionMode,
|
|
1251
|
+
session_id: sessionId,
|
|
1252
|
+
metadata,
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
// Export aliases for backward compatibility
|
|
1257
|
+
export { DelegationContractManager as ContractManager };
|
|
1258
|
+
//# sourceMappingURL=contract-manager.js.map
|