@vauban-org/agent-sdk 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRACT.md +6401 -813
- package/dist/adapters/llm/anthropic-direct.d.ts +1 -0
- package/dist/adapters/llm/anthropic-direct.d.ts.map +1 -1
- package/dist/adapters/llm/anthropic-direct.js +43 -0
- package/dist/adapters/llm/anthropic-direct.js.map +1 -1
- package/dist/adapters/llm/cascade.d.ts.map +1 -1
- package/dist/adapters/llm/cascade.js +57 -14
- package/dist/adapters/llm/cascade.js.map +1 -1
- package/dist/adapters/llm/litellm.d.ts +2 -0
- package/dist/adapters/llm/litellm.d.ts.map +1 -1
- package/dist/adapters/llm/litellm.js +44 -0
- package/dist/adapters/llm/litellm.js.map +1 -1
- package/dist/compute/difficulty-estimator.d.ts +53 -0
- package/dist/compute/difficulty-estimator.d.ts.map +1 -0
- package/dist/compute/difficulty-estimator.js +82 -0
- package/dist/compute/difficulty-estimator.js.map +1 -0
- package/dist/compute/strategies/mixture-of-agents.d.ts +40 -0
- package/dist/compute/strategies/mixture-of-agents.d.ts.map +1 -0
- package/dist/compute/strategies/mixture-of-agents.js +110 -0
- package/dist/compute/strategies/mixture-of-agents.js.map +1 -0
- package/dist/compute/strategies/tree-of-thoughts.d.ts +48 -0
- package/dist/compute/strategies/tree-of-thoughts.d.ts.map +1 -0
- package/dist/compute/strategies/tree-of-thoughts.js +242 -0
- package/dist/compute/strategies/tree-of-thoughts.js.map +1 -0
- package/dist/compute/strategies/two-phase-orient.d.ts +72 -0
- package/dist/compute/strategies/two-phase-orient.d.ts.map +1 -0
- package/dist/compute/strategies/two-phase-orient.js +85 -0
- package/dist/compute/strategies/two-phase-orient.js.map +1 -0
- package/dist/constitution/types.d.ts +10 -10
- package/dist/container/protocol.d.ts +134 -0
- package/dist/container/protocol.d.ts.map +1 -0
- package/dist/container/protocol.js +157 -0
- package/dist/container/protocol.js.map +1 -0
- package/dist/container/runtime.d.ts +140 -0
- package/dist/container/runtime.d.ts.map +1 -0
- package/dist/container/runtime.js +256 -0
- package/dist/container/runtime.js.map +1 -0
- package/dist/events/catalogue.d.ts +46 -46
- package/dist/events/schemas/agent.completed.v1.d.ts +4 -4
- package/dist/events/schemas/agent.failed.v1.d.ts +2 -2
- package/dist/events/schemas/agent.hitl_resolved.v1.d.ts +2 -2
- package/dist/events/schemas/agent.started.v1.d.ts +2 -2
- package/dist/events/schemas/brain.skill.extracted.v1.d.ts +4 -4
- package/dist/events/schemas/cc.cost.anomaly_detected.v1.d.ts +2 -2
- package/dist/events/schemas/cc.cost.recorded.v1.d.ts +4 -4
- package/dist/events/schemas/citadel.sprint.analyzed.v1.d.ts +6 -6
- package/dist/events/schemas/citadel.sprint.closed.v1.d.ts +2 -2
- package/dist/events/schemas/forge.inbox.reply_classified.v1.d.ts +6 -6
- package/dist/events/schemas/forge.lead.qualified.v1.d.ts +2 -2
- package/dist/events/schemas/forge.outreach.sent.v1.d.ts +4 -4
- package/dist/events/schemas/incident.detected.v1.d.ts +2 -2
- package/dist/events/schemas/vauban.goal.checked.v1.d.ts +2 -2
- package/dist/events/schemas/vauban.rebalancing.checked.v1.d.ts +2 -2
- package/dist/events/schemas/vauban.tax.checked.v1.d.ts +2 -2
- package/dist/events/schemas/vauban.vault.analyzed.v1.d.ts +6 -6
- package/dist/identity/agent-persona.d.ts +73 -0
- package/dist/identity/agent-persona.d.ts.map +1 -0
- package/dist/identity/agent-persona.js +165 -0
- package/dist/identity/agent-persona.js.map +1 -0
- package/dist/identity/persona-prompt.d.ts +25 -0
- package/dist/identity/persona-prompt.d.ts.map +1 -0
- package/dist/identity/persona-prompt.js +71 -0
- package/dist/identity/persona-prompt.js.map +1 -0
- package/dist/identity/persona-schema.d.ts +120 -0
- package/dist/identity/persona-schema.d.ts.map +1 -0
- package/dist/identity/persona-schema.js +103 -0
- package/dist/identity/persona-schema.js.map +1 -0
- package/dist/index.d.ts +37 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -1
- package/dist/index.js.map +1 -1
- package/dist/loop/minimal-loop.js +293 -287
- package/dist/memory/episodic-rrf.d.ts +114 -0
- package/dist/memory/episodic-rrf.d.ts.map +1 -0
- package/dist/memory/episodic-rrf.js +148 -0
- package/dist/memory/episodic-rrf.js.map +1 -0
- package/dist/mesh/attenuation.d.ts +78 -0
- package/dist/mesh/attenuation.d.ts.map +1 -0
- package/dist/mesh/attenuation.js +141 -0
- package/dist/mesh/attenuation.js.map +1 -0
- package/dist/mesh/delegate.d.ts +96 -0
- package/dist/mesh/delegate.d.ts.map +1 -0
- package/dist/mesh/delegate.js +172 -0
- package/dist/mesh/delegate.js.map +1 -0
- package/dist/mesh/dispatcher.d.ts +119 -0
- package/dist/mesh/dispatcher.d.ts.map +1 -0
- package/dist/mesh/dispatcher.js +207 -0
- package/dist/mesh/dispatcher.js.map +1 -0
- package/dist/mesh/index.d.ts +12 -0
- package/dist/mesh/index.d.ts.map +1 -0
- package/dist/mesh/index.js +11 -0
- package/dist/mesh/index.js.map +1 -0
- package/dist/mesh/types.d.ts +30 -0
- package/dist/mesh/types.d.ts.map +1 -0
- package/dist/mesh/types.js +11 -0
- package/dist/mesh/types.js.map +1 -0
- package/dist/orchestration/ooda/skills.d.ts +104 -0
- package/dist/orchestration/ooda/skills.d.ts.map +1 -1
- package/dist/orchestration/ooda/skills.js +106 -0
- package/dist/orchestration/ooda/skills.js.map +1 -1
- package/dist/ports/bastion-action.contract.test.d.ts +11 -0
- package/dist/ports/bastion-action.contract.test.d.ts.map +1 -0
- package/dist/ports/bastion-action.contract.test.js +238 -0
- package/dist/ports/bastion-action.contract.test.js.map +1 -0
- package/dist/ports/bastion-action.d.ts +133 -0
- package/dist/ports/bastion-action.d.ts.map +1 -0
- package/dist/ports/bastion-action.js +73 -0
- package/dist/ports/bastion-action.js.map +1 -0
- package/dist/ports/brain.d.ts +31 -0
- package/dist/ports/brain.d.ts.map +1 -1
- package/dist/ports/brain.js +115 -1
- package/dist/ports/brain.js.map +1 -1
- package/dist/ports/citadel-action.contract.test.d.ts +11 -0
- package/dist/ports/citadel-action.contract.test.d.ts.map +1 -0
- package/dist/ports/citadel-action.contract.test.js +317 -0
- package/dist/ports/citadel-action.contract.test.js.map +1 -0
- package/dist/ports/citadel-action.d.ts +111 -0
- package/dist/ports/citadel-action.d.ts.map +1 -0
- package/dist/ports/citadel-action.js +62 -0
- package/dist/ports/citadel-action.js.map +1 -0
- package/dist/ports/compliance-contract.d.ts +123 -0
- package/dist/ports/compliance-contract.d.ts.map +1 -0
- package/dist/ports/compliance-contract.js +35 -0
- package/dist/ports/compliance-contract.js.map +1 -0
- package/dist/ports/db.d.ts +38 -0
- package/dist/ports/db.d.ts.map +1 -1
- package/dist/ports/db.js +88 -1
- package/dist/ports/db.js.map +1 -1
- package/dist/ports/delegation.contract.test.d.ts +9 -0
- package/dist/ports/delegation.contract.test.d.ts.map +1 -0
- package/dist/ports/delegation.contract.test.js +337 -0
- package/dist/ports/delegation.contract.test.js.map +1 -0
- package/dist/ports/delegation.d.ts +134 -0
- package/dist/ports/delegation.d.ts.map +1 -0
- package/dist/ports/delegation.js +105 -0
- package/dist/ports/delegation.js.map +1 -0
- package/dist/ports/event-bus.d.ts +29 -0
- package/dist/ports/event-bus.d.ts.map +1 -1
- package/dist/ports/event-bus.js +106 -1
- package/dist/ports/event-bus.js.map +1 -1
- package/dist/ports/federation.contract.test.d.ts +9 -0
- package/dist/ports/federation.contract.test.d.ts.map +1 -0
- package/dist/ports/federation.contract.test.js +279 -0
- package/dist/ports/federation.contract.test.js.map +1 -0
- package/dist/ports/federation.d.ts +140 -0
- package/dist/ports/federation.d.ts.map +1 -0
- package/dist/ports/federation.js +57 -0
- package/dist/ports/federation.js.map +1 -0
- package/dist/ports/index.d.ts +28 -2
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js +17 -2
- package/dist/ports/index.js.map +1 -1
- package/dist/ports/llm-provider.d.ts +37 -0
- package/dist/ports/llm-provider.d.ts.map +1 -1
- package/dist/ports/llm-provider.js +99 -1
- package/dist/ports/llm-provider.js.map +1 -1
- package/dist/ports/logger.d.ts +27 -0
- package/dist/ports/logger.d.ts.map +1 -1
- package/dist/ports/logger.js +87 -0
- package/dist/ports/logger.js.map +1 -1
- package/dist/ports/manifest-registry.contract.test.d.ts +9 -0
- package/dist/ports/manifest-registry.contract.test.d.ts.map +1 -0
- package/dist/ports/manifest-registry.contract.test.js +246 -0
- package/dist/ports/manifest-registry.contract.test.js.map +1 -0
- package/dist/ports/manifest-registry.d.ts +116 -0
- package/dist/ports/manifest-registry.d.ts.map +1 -0
- package/dist/ports/manifest-registry.js +79 -0
- package/dist/ports/manifest-registry.js.map +1 -0
- package/dist/ports/observability.contract.test.d.ts +12 -0
- package/dist/ports/observability.contract.test.d.ts.map +1 -0
- package/dist/ports/observability.contract.test.js +260 -0
- package/dist/ports/observability.contract.test.js.map +1 -0
- package/dist/ports/observability.d.ts +98 -0
- package/dist/ports/observability.d.ts.map +1 -0
- package/dist/ports/observability.js +59 -0
- package/dist/ports/observability.js.map +1 -0
- package/dist/ports/outcome.d.ts +26 -0
- package/dist/ports/outcome.d.ts.map +1 -1
- package/dist/ports/outcome.js +62 -1
- package/dist/ports/outcome.js.map +1 -1
- package/dist/ports/privacy.contract.test.d.ts +12 -0
- package/dist/ports/privacy.contract.test.d.ts.map +1 -0
- package/dist/ports/privacy.contract.test.js +325 -0
- package/dist/ports/privacy.contract.test.js.map +1 -0
- package/dist/ports/privacy.d.ts +132 -0
- package/dist/ports/privacy.d.ts.map +1 -0
- package/dist/ports/privacy.js +83 -0
- package/dist/ports/privacy.js.map +1 -0
- package/dist/ports/tenant-context.contract.test.d.ts +14 -0
- package/dist/ports/tenant-context.contract.test.d.ts.map +1 -0
- package/dist/ports/tenant-context.contract.test.js +352 -0
- package/dist/ports/tenant-context.contract.test.js.map +1 -0
- package/dist/ports/tenant-context.d.ts +103 -0
- package/dist/ports/tenant-context.d.ts.map +1 -0
- package/dist/ports/tenant-context.js +48 -0
- package/dist/ports/tenant-context.js.map +1 -0
- package/dist/ports/vauban-finance-action.contract.test.d.ts +11 -0
- package/dist/ports/vauban-finance-action.contract.test.d.ts.map +1 -0
- package/dist/ports/vauban-finance-action.contract.test.js +260 -0
- package/dist/ports/vauban-finance-action.contract.test.js.map +1 -0
- package/dist/ports/vauban-finance-action.d.ts +106 -0
- package/dist/ports/vauban-finance-action.d.ts.map +1 -0
- package/dist/ports/vauban-finance-action.js +60 -0
- package/dist/ports/vauban-finance-action.js.map +1 -0
- package/dist/ports/workflow-runtime.d.ts +204 -0
- package/dist/ports/workflow-runtime.d.ts.map +1 -0
- package/dist/ports/workflow-runtime.js +72 -0
- package/dist/ports/workflow-runtime.js.map +1 -0
- package/dist/proof/cert-verify.d.ts +80 -0
- package/dist/proof/cert-verify.d.ts.map +1 -0
- package/dist/proof/cert-verify.js +178 -0
- package/dist/proof/cert-verify.js.map +1 -0
- package/dist/replay/replay.d.ts.map +1 -1
- package/dist/replay/replay.js +5 -1
- package/dist/replay/replay.js.map +1 -1
- package/dist/retry/index.d.ts +129 -0
- package/dist/retry/index.d.ts.map +1 -0
- package/dist/retry/index.js +156 -0
- package/dist/retry/index.js.map +1 -0
- package/dist/retry/presets.d.ts +39 -0
- package/dist/retry/presets.d.ts.map +1 -0
- package/dist/retry/presets.js +69 -0
- package/dist/retry/presets.js.map +1 -0
- package/dist/skill-loop/ab-runner.d.ts +67 -0
- package/dist/skill-loop/ab-runner.d.ts.map +1 -0
- package/dist/skill-loop/ab-runner.js +160 -0
- package/dist/skill-loop/ab-runner.js.map +1 -0
- package/dist/skill-loop/adoption.d.ts +67 -0
- package/dist/skill-loop/adoption.d.ts.map +1 -0
- package/dist/skill-loop/adoption.js +126 -0
- package/dist/skill-loop/adoption.js.map +1 -0
- package/dist/skill-loop/candidate.d.ts +45 -0
- package/dist/skill-loop/candidate.d.ts.map +1 -0
- package/dist/skill-loop/candidate.js +43 -0
- package/dist/skill-loop/candidate.js.map +1 -0
- package/dist/skill-loop/evaluator.d.ts +42 -0
- package/dist/skill-loop/evaluator.d.ts.map +1 -0
- package/dist/skill-loop/evaluator.js +184 -0
- package/dist/skill-loop/evaluator.js.map +1 -0
- package/dist/skill-loop/index.d.ts +27 -0
- package/dist/skill-loop/index.d.ts.map +1 -0
- package/dist/skill-loop/index.js +27 -0
- package/dist/skill-loop/index.js.map +1 -0
- package/dist/skill-loop/reflexion-replay.d.ts +87 -0
- package/dist/skill-loop/reflexion-replay.d.ts.map +1 -0
- package/dist/skill-loop/reflexion-replay.js +110 -0
- package/dist/skill-loop/reflexion-replay.js.map +1 -0
- package/dist/skill-loop/sign-off.d.ts +88 -0
- package/dist/skill-loop/sign-off.d.ts.map +1 -0
- package/dist/skill-loop/sign-off.js +146 -0
- package/dist/skill-loop/sign-off.js.map +1 -0
- package/dist/skill-loop/value-metric.d.ts +55 -0
- package/dist/skill-loop/value-metric.d.ts.map +1 -0
- package/dist/skill-loop/value-metric.js +69 -0
- package/dist/skill-loop/value-metric.js.map +1 -0
- package/dist/skill-loop/versioning.d.ts +36 -0
- package/dist/skill-loop/versioning.d.ts.map +1 -0
- package/dist/skill-loop/versioning.js +47 -0
- package/dist/skill-loop/versioning.js.map +1 -0
- package/dist/skill-manifest/anchor.d.ts +91 -0
- package/dist/skill-manifest/anchor.d.ts.map +1 -0
- package/dist/skill-manifest/anchor.js +331 -0
- package/dist/skill-manifest/anchor.js.map +1 -0
- package/dist/skill-manifest/builder.d.ts +47 -0
- package/dist/skill-manifest/builder.d.ts.map +1 -0
- package/dist/skill-manifest/builder.js +93 -0
- package/dist/skill-manifest/builder.js.map +1 -0
- package/dist/skill-manifest/index.d.ts +13 -0
- package/dist/skill-manifest/index.d.ts.map +1 -0
- package/dist/skill-manifest/index.js +9 -0
- package/dist/skill-manifest/index.js.map +1 -0
- package/dist/skill-manifest/types.d.ts +67 -0
- package/dist/skill-manifest/types.d.ts.map +1 -0
- package/dist/skill-manifest/types.js +16 -0
- package/dist/skill-manifest/types.js.map +1 -0
- package/dist/skill-manifest/verifier.d.ts +42 -0
- package/dist/skill-manifest/verifier.d.ts.map +1 -0
- package/dist/skill-manifest/verifier.js +136 -0
- package/dist/skill-manifest/verifier.js.map +1 -0
- package/dist/skills/brain-query.d.ts +4 -4
- package/dist/skills/brain-store.d.ts +6 -6
- package/dist/skills/errors.d.ts +15 -0
- package/dist/skills/errors.d.ts.map +1 -1
- package/dist/skills/errors.js +21 -0
- package/dist/skills/errors.js.map +1 -1
- package/dist/skills/hitl-request.d.ts +2 -2
- package/dist/skills/index.d.ts +3 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +4 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/markdown/loader.d.ts +52 -0
- package/dist/skills/markdown/loader.d.ts.map +1 -0
- package/dist/skills/markdown/loader.js +93 -0
- package/dist/skills/markdown/loader.js.map +1 -0
- package/dist/skills/markdown/schema.d.ts +432 -0
- package/dist/skills/markdown/schema.d.ts.map +1 -0
- package/dist/skills/markdown/schema.js +121 -0
- package/dist/skills/markdown/schema.js.map +1 -0
- package/dist/skills/poc-md-loader/markdown-loader.d.ts +77 -0
- package/dist/skills/poc-md-loader/markdown-loader.d.ts.map +1 -0
- package/dist/skills/poc-md-loader/markdown-loader.js +125 -0
- package/dist/skills/poc-md-loader/markdown-loader.js.map +1 -0
- package/dist/skills/poc-md-loader/runner.d.ts +24 -0
- package/dist/skills/poc-md-loader/runner.d.ts.map +1 -0
- package/dist/skills/poc-md-loader/runner.js +57 -0
- package/dist/skills/poc-md-loader/runner.js.map +1 -0
- package/dist/skills/poc-md-loader/vitest.poc.config.d.ts +3 -0
- package/dist/skills/poc-md-loader/vitest.poc.config.d.ts.map +1 -0
- package/dist/skills/poc-md-loader/vitest.poc.config.js +13 -0
- package/dist/skills/poc-md-loader/vitest.poc.config.js.map +1 -0
- package/dist/skills/poc-md-loader/web-search/script.d.ts +33 -0
- package/dist/skills/poc-md-loader/web-search/script.d.ts.map +1 -0
- package/dist/skills/poc-md-loader/web-search/script.js +75 -0
- package/dist/skills/poc-md-loader/web-search/script.js.map +1 -0
- package/dist/skills/record-outcome.d.ts +4 -4
- package/dist/skills/send-email.d.ts.map +1 -1
- package/dist/skills/send-email.js +15 -3
- package/dist/skills/send-email.js.map +1 -1
- package/dist/skills/slack-notify.d.ts +4 -4
- package/dist/skills/starknet-balance.d.ts +1 -1
- package/dist/skills/telegram-notify.d.ts +4 -4
- package/dist/skills/web-search.d.ts +1 -1
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/test-brain-port.d.ts +4 -0
- package/dist/testing/test-brain-port.d.ts.map +1 -1
- package/dist/testing/test-brain-port.js +75 -20
- package/dist/testing/test-brain-port.js.map +1 -1
- package/dist/testing/test-event-bus.d.ts.map +1 -1
- package/dist/testing/test-event-bus.js +89 -36
- package/dist/testing/test-event-bus.js.map +1 -1
- package/dist/trace/schema.d.ts +1 -1
- package/dist/trace/schema.d.ts.map +1 -1
- package/dist/trace/schema.js +1 -1
- package/dist/trace/schema.js.map +1 -1
- package/dist/verify/formal/index.d.ts +44 -0
- package/dist/verify/formal/index.d.ts.map +1 -0
- package/dist/verify/formal/index.js +98 -0
- package/dist/verify/formal/index.js.map +1 -0
- package/dist/verify/formal/policy.d.ts +105 -0
- package/dist/verify/formal/policy.d.ts.map +1 -0
- package/dist/verify/formal/policy.js +159 -0
- package/dist/verify/formal/policy.js.map +1 -0
- package/dist/verify/formal/result.d.ts +50 -0
- package/dist/verify/formal/result.d.ts.map +1 -0
- package/dist/verify/formal/result.js +21 -0
- package/dist/verify/formal/result.js.map +1 -0
- package/dist/verify/formal/solver.d.ts +67 -0
- package/dist/verify/formal/solver.d.ts.map +1 -0
- package/dist/verify/formal/solver.js +184 -0
- package/dist/verify/formal/solver.js.map +1 -0
- package/dist/verify/formal/spec-language.d.ts +80 -0
- package/dist/verify/formal/spec-language.d.ts.map +1 -0
- package/dist/verify/formal/spec-language.js +219 -0
- package/dist/verify/formal/spec-language.js.map +1 -0
- package/docs/attestation.md +199 -0
- package/docs/identity.md +193 -0
- package/package.json +34 -17
- package/src/adapters/llm/anthropic-direct.ts +51 -0
- package/src/adapters/llm/cascade.ts +64 -19
- package/src/adapters/llm/litellm.ts +49 -0
- package/src/compute/difficulty-estimator.ts +111 -0
- package/src/compute/strategies/mixture-of-agents.ts +150 -0
- package/src/compute/strategies/tree-of-thoughts.ts +293 -0
- package/src/compute/strategies/two-phase-orient.ts +147 -0
- package/src/container/protocol.ts +243 -0
- package/src/container/runtime.ts +424 -0
- package/src/db/migrations/026_formal_verify_results.sql +30 -0
- package/src/identity/agent-persona.ts +203 -0
- package/src/identity/persona-prompt.ts +84 -0
- package/src/identity/persona-schema.ts +127 -0
- package/src/index.ts +338 -1
- package/src/memory/episodic-rrf.ts +224 -0
- package/src/mesh/attenuation.ts +190 -0
- package/src/mesh/delegate.ts +254 -0
- package/src/mesh/dispatcher.ts +301 -0
- package/src/mesh/index.ts +39 -0
- package/src/mesh/types.ts +31 -0
- package/src/orchestration/ooda/skills.ts +177 -0
- package/src/ports/bastion-action.contract.test.ts +355 -0
- package/src/ports/bastion-action.ts +198 -0
- package/src/ports/brain.ts +177 -15
- package/src/ports/citadel-action.contract.test.ts +430 -0
- package/src/ports/citadel-action.ts +174 -0
- package/src/ports/compliance-contract.ts +191 -0
- package/src/ports/db.ts +98 -0
- package/src/ports/delegation.contract.test.ts +428 -0
- package/src/ports/delegation.ts +211 -0
- package/src/ports/event-bus.ts +133 -0
- package/src/ports/federation.contract.test.ts +355 -0
- package/src/ports/federation.ts +190 -0
- package/src/ports/index.ts +186 -1
- package/src/ports/llm-provider.ts +123 -0
- package/src/ports/logger.ts +104 -0
- package/src/ports/manifest-registry.contract.test.ts +324 -0
- package/src/ports/manifest-registry.ts +188 -0
- package/src/ports/observability.contract.test.ts +315 -0
- package/src/ports/observability.ts +150 -0
- package/src/ports/outcome.ts +69 -0
- package/src/ports/privacy.contract.test.ts +413 -0
- package/src/ports/privacy.ts +207 -0
- package/src/ports/tenant-context.contract.test.ts +454 -0
- package/src/ports/tenant-context.ts +150 -0
- package/src/ports/vauban-finance-action.contract.test.ts +335 -0
- package/src/ports/vauban-finance-action.ts +166 -0
- package/src/ports/workflow-runtime.ts +327 -0
- package/src/proof/cert-verify.ts +249 -0
- package/src/replay/replay.ts +11 -8
- package/src/retry/index.ts +227 -0
- package/src/retry/presets.ts +75 -0
- package/src/skill-loop/ab-runner.ts +196 -0
- package/src/skill-loop/adoption.ts +188 -0
- package/src/skill-loop/candidate.ts +75 -0
- package/src/skill-loop/evaluator.ts +238 -0
- package/src/skill-loop/index.ts +51 -0
- package/src/skill-loop/reflexion-replay.ts +173 -0
- package/src/skill-loop/sign-off.ts +247 -0
- package/src/skill-loop/value-metric.ts +120 -0
- package/src/skill-loop/versioning.ts +75 -0
- package/src/skill-manifest/anchor.ts +401 -0
- package/src/skill-manifest/builder.ts +129 -0
- package/src/skill-manifest/index.ts +18 -0
- package/src/skill-manifest/types.ts +72 -0
- package/src/skill-manifest/verifier.ts +198 -0
- package/src/skills/errors.ts +30 -2
- package/src/skills/index.ts +19 -0
- package/src/skills/markdown/loader.ts +129 -0
- package/src/skills/markdown/schema.ts +144 -0
- package/src/skills/poc-md-loader/e2e-parity.test.ts +237 -0
- package/src/skills/poc-md-loader/markdown-loader.ts +161 -0
- package/src/skills/poc-md-loader/runner.ts +82 -0
- package/src/skills/poc-md-loader/vitest.poc.config.ts +13 -0
- package/src/skills/poc-md-loader/web-search/SKILL.md +42 -0
- package/src/skills/poc-md-loader/web-search/script.ts +109 -0
- package/src/skills/send-email.ts +15 -3
- package/src/testing/test-brain-port.ts +98 -24
- package/src/testing/test-event-bus.ts +104 -43
- package/src/trace/schema.ts +1 -1
- package/src/verify/formal/index.ts +154 -0
- package/src/verify/formal/policy.ts +253 -0
- package/src/verify/formal/result.ts +52 -0
- package/src/verify/formal/solver.ts +235 -0
- package/src/verify/formal/spec-language.ts +274 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContainerRuntime — execute automations as local binaries or as containers,
|
|
3
|
+
* speaking the {@link ./protocol.ts | Container Execution Protocol}.
|
|
4
|
+
*
|
|
5
|
+
* Two modes:
|
|
6
|
+
*
|
|
7
|
+
* - **Binary**: spawn a local executable directly (Rust/Go/Python binary).
|
|
8
|
+
* The runtime uses `node:child_process.spawn` by default; tests inject a
|
|
9
|
+
* `SpawnFn` to avoid actually running anything.
|
|
10
|
+
*
|
|
11
|
+
* - **Container**: delegate to a host-provided {@link SandboxExecutor}.
|
|
12
|
+
* This is structurally compatible with Command Center's `DockerExecutor`
|
|
13
|
+
* (hardened isolation), so the SDK stays decoupled from any specific
|
|
14
|
+
* sandbox implementation.
|
|
15
|
+
*
|
|
16
|
+
* @public @since 1.2.0
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { randomUUID } from "node:crypto";
|
|
20
|
+
import { type ChildProcess, spawn as nodeSpawn } from "node:child_process";
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
buildProtocolEnv,
|
|
24
|
+
type ExecutionResult,
|
|
25
|
+
parseProtocolOutput,
|
|
26
|
+
parseStderrLogs,
|
|
27
|
+
ProtocolParseError,
|
|
28
|
+
} from "./protocol.js";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Description of a sandboxed job — structurally identical to Command Center's
|
|
32
|
+
* `SandboxJob`. Defined here so the SDK has no dependency on a specific
|
|
33
|
+
* sandbox implementation.
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export interface SandboxJob {
|
|
38
|
+
image: string;
|
|
39
|
+
command: string[];
|
|
40
|
+
stdin?: string;
|
|
41
|
+
env?: Record<string, string>;
|
|
42
|
+
cwd?: string;
|
|
43
|
+
timeoutMs: number;
|
|
44
|
+
networkMode?: "none" | "outbound" | "bridge";
|
|
45
|
+
readOnlyRoot?: boolean;
|
|
46
|
+
memoryMb?: number;
|
|
47
|
+
cpuQuota?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Outcome of a sandbox run — structurally identical to Command Center's
|
|
52
|
+
* `SandboxResult`.
|
|
53
|
+
*
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
export interface SandboxResult {
|
|
57
|
+
exitCode: number;
|
|
58
|
+
stdout: string;
|
|
59
|
+
stderr: string;
|
|
60
|
+
timedOut: boolean;
|
|
61
|
+
durationMs: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Port for a process sandbox (Docker, Firecracker, etc.). The host injects an
|
|
66
|
+
* implementation at runtime; the SDK never depends on a concrete sandbox.
|
|
67
|
+
*
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
export interface SandboxExecutor {
|
|
71
|
+
run(job: SandboxJob): Promise<SandboxResult>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Spawn function signature, mirrors `node:child_process.spawn`. Injectable so
|
|
76
|
+
* tests can avoid spawning real subprocesses.
|
|
77
|
+
*
|
|
78
|
+
* @public
|
|
79
|
+
*/
|
|
80
|
+
export type SpawnFn = (
|
|
81
|
+
cmd: string,
|
|
82
|
+
args: string[],
|
|
83
|
+
opts: {
|
|
84
|
+
env?: NodeJS.ProcessEnv;
|
|
85
|
+
cwd?: string;
|
|
86
|
+
stdio: ["pipe", "pipe", "pipe"];
|
|
87
|
+
signal?: AbortSignal;
|
|
88
|
+
}
|
|
89
|
+
) => ChildProcess;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Options for {@link ContainerRuntime.executeContainer}.
|
|
93
|
+
*
|
|
94
|
+
* @public
|
|
95
|
+
*/
|
|
96
|
+
export interface ContainerExecutionOptions {
|
|
97
|
+
executionId?: string;
|
|
98
|
+
timeoutSeconds?: number;
|
|
99
|
+
extraEnv?: Record<string, string>;
|
|
100
|
+
mode?: string;
|
|
101
|
+
networkMode?: "none" | "outbound" | "bridge";
|
|
102
|
+
readOnlyRoot?: boolean;
|
|
103
|
+
memoryMb?: number;
|
|
104
|
+
cpuQuota?: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Options for {@link ContainerRuntime.executeBinary}.
|
|
109
|
+
*
|
|
110
|
+
* @public
|
|
111
|
+
*/
|
|
112
|
+
export interface BinaryExecutionOptions {
|
|
113
|
+
executionId?: string;
|
|
114
|
+
timeoutSeconds?: number;
|
|
115
|
+
extraEnv?: Record<string, string>;
|
|
116
|
+
cwd?: string;
|
|
117
|
+
mode?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Construct a {@link ContainerRuntime} bound to a {@link SandboxExecutor}
|
|
122
|
+
* (for container mode) and optionally a custom {@link SpawnFn} (for binary
|
|
123
|
+
* mode tests).
|
|
124
|
+
*
|
|
125
|
+
* @public
|
|
126
|
+
*/
|
|
127
|
+
export interface ContainerRuntimeOptions {
|
|
128
|
+
/** Sandbox executor for container mode. Required if executeContainer() is called. */
|
|
129
|
+
sandbox?: SandboxExecutor;
|
|
130
|
+
/** Spawn function for binary mode. Defaults to node:child_process.spawn. */
|
|
131
|
+
spawnFn?: SpawnFn;
|
|
132
|
+
/** Default timeout when callers don't specify one. */
|
|
133
|
+
defaultTimeoutSeconds?: number;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const DEFAULT_TIMEOUT_SECONDS = 300;
|
|
137
|
+
const MAX_OUTPUT_BYTES = 1_048_576; // 1 MB
|
|
138
|
+
const TRUNCATION_MARKER = "\n[TRUNCATED — output exceeded 1 MB]";
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Run automations via either a sandboxed container (host-injected) or a local
|
|
142
|
+
* binary subprocess. Both paths produce a uniform {@link ExecutionResult}.
|
|
143
|
+
*
|
|
144
|
+
* @public
|
|
145
|
+
*/
|
|
146
|
+
export class ContainerRuntime {
|
|
147
|
+
private readonly sandbox: SandboxExecutor | undefined;
|
|
148
|
+
private readonly spawnFn: SpawnFn;
|
|
149
|
+
private readonly defaultTimeoutSeconds: number;
|
|
150
|
+
|
|
151
|
+
constructor(opts: ContainerRuntimeOptions = {}) {
|
|
152
|
+
this.sandbox = opts.sandbox;
|
|
153
|
+
this.spawnFn = opts.spawnFn ?? (nodeSpawn as unknown as SpawnFn);
|
|
154
|
+
this.defaultTimeoutSeconds =
|
|
155
|
+
opts.defaultTimeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Execute an automation packaged as a container image. Delegates to the
|
|
160
|
+
* injected {@link SandboxExecutor} so isolation policy (caps, network,
|
|
161
|
+
* read-only FS) lives in the host implementation.
|
|
162
|
+
*/
|
|
163
|
+
async executeContainer<T = unknown>(
|
|
164
|
+
image: string,
|
|
165
|
+
automationName: string,
|
|
166
|
+
input: unknown,
|
|
167
|
+
options: ContainerExecutionOptions = {}
|
|
168
|
+
): Promise<ExecutionResult<T>> {
|
|
169
|
+
if (!this.sandbox) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
"ContainerRuntime.executeContainer: no SandboxExecutor injected"
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
const executionId = options.executionId ?? randomUUID();
|
|
175
|
+
const timeoutSeconds = options.timeoutSeconds ?? this.defaultTimeoutSeconds;
|
|
176
|
+
const startedAt = new Date();
|
|
177
|
+
|
|
178
|
+
const env = buildProtocolEnv({
|
|
179
|
+
executionId,
|
|
180
|
+
automationName,
|
|
181
|
+
input,
|
|
182
|
+
timeoutSeconds,
|
|
183
|
+
mode: options.mode,
|
|
184
|
+
extraEnv: options.extraEnv,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const job: SandboxJob = {
|
|
188
|
+
image,
|
|
189
|
+
command: [],
|
|
190
|
+
env,
|
|
191
|
+
timeoutMs: timeoutSeconds * 1000,
|
|
192
|
+
networkMode: options.networkMode ?? "none",
|
|
193
|
+
readOnlyRoot: options.readOnlyRoot ?? true,
|
|
194
|
+
memoryMb: options.memoryMb ?? 256,
|
|
195
|
+
cpuQuota: options.cpuQuota ?? 0.5,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const result = await this.sandbox.run(job);
|
|
199
|
+
return this.toExecutionResult<T>({
|
|
200
|
+
executionId,
|
|
201
|
+
automationName,
|
|
202
|
+
input,
|
|
203
|
+
startedAt,
|
|
204
|
+
sandboxResult: result,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Execute an automation packaged as a local binary (Rust release, Python
|
|
210
|
+
* wrapper, shell script). No container — just a hardened subprocess.
|
|
211
|
+
*/
|
|
212
|
+
async executeBinary<T = unknown>(
|
|
213
|
+
command: string[],
|
|
214
|
+
automationName: string,
|
|
215
|
+
input: unknown,
|
|
216
|
+
options: BinaryExecutionOptions = {}
|
|
217
|
+
): Promise<ExecutionResult<T>> {
|
|
218
|
+
if (!Array.isArray(command) || command.length === 0) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
"ContainerRuntime.executeBinary: command must be a non-empty array"
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
const executionId = options.executionId ?? randomUUID();
|
|
224
|
+
const timeoutSeconds = options.timeoutSeconds ?? this.defaultTimeoutSeconds;
|
|
225
|
+
const startedAt = new Date();
|
|
226
|
+
|
|
227
|
+
const protocolEnv = buildProtocolEnv({
|
|
228
|
+
executionId,
|
|
229
|
+
automationName,
|
|
230
|
+
input,
|
|
231
|
+
timeoutSeconds,
|
|
232
|
+
mode: options.mode,
|
|
233
|
+
extraEnv: options.extraEnv,
|
|
234
|
+
});
|
|
235
|
+
const env: NodeJS.ProcessEnv = { ...process.env, ...protocolEnv };
|
|
236
|
+
|
|
237
|
+
const result = await this.runSubprocess({
|
|
238
|
+
cmd: command[0]!,
|
|
239
|
+
args: command.slice(1),
|
|
240
|
+
env,
|
|
241
|
+
cwd: options.cwd,
|
|
242
|
+
timeoutMs: timeoutSeconds * 1000,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
return this.toExecutionResult<T>({
|
|
246
|
+
executionId,
|
|
247
|
+
automationName,
|
|
248
|
+
input,
|
|
249
|
+
startedAt,
|
|
250
|
+
sandboxResult: result,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ─── Internals ────────────────────────────────────────────────────────
|
|
255
|
+
|
|
256
|
+
private async runSubprocess(args: {
|
|
257
|
+
cmd: string;
|
|
258
|
+
args: string[];
|
|
259
|
+
env: NodeJS.ProcessEnv;
|
|
260
|
+
cwd: string | undefined;
|
|
261
|
+
timeoutMs: number;
|
|
262
|
+
}): Promise<SandboxResult> {
|
|
263
|
+
const startedAt = Date.now();
|
|
264
|
+
const abortController = new AbortController();
|
|
265
|
+
const timeoutHandle = setTimeout(
|
|
266
|
+
() => abortController.abort(),
|
|
267
|
+
args.timeoutMs
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
let stdoutBuf = "";
|
|
271
|
+
let stderrBuf = "";
|
|
272
|
+
let timedOut = false;
|
|
273
|
+
|
|
274
|
+
const spawnOpts: {
|
|
275
|
+
env?: NodeJS.ProcessEnv;
|
|
276
|
+
cwd?: string;
|
|
277
|
+
stdio: ["pipe", "pipe", "pipe"];
|
|
278
|
+
signal?: AbortSignal;
|
|
279
|
+
} = {
|
|
280
|
+
env: args.env,
|
|
281
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
282
|
+
signal: abortController.signal,
|
|
283
|
+
};
|
|
284
|
+
if (args.cwd !== undefined) spawnOpts.cwd = args.cwd;
|
|
285
|
+
|
|
286
|
+
const child = this.spawnFn(args.cmd, args.args, spawnOpts);
|
|
287
|
+
|
|
288
|
+
if (child.stdin) child.stdin.end();
|
|
289
|
+
|
|
290
|
+
child.stdout?.on("data", (chunk: Buffer) => {
|
|
291
|
+
const remaining = MAX_OUTPUT_BYTES - Buffer.byteLength(stdoutBuf, "utf8");
|
|
292
|
+
if (remaining <= 0) return;
|
|
293
|
+
const piece = chunk.toString("utf8");
|
|
294
|
+
if (Buffer.byteLength(piece, "utf8") <= remaining) {
|
|
295
|
+
stdoutBuf += piece;
|
|
296
|
+
} else {
|
|
297
|
+
stdoutBuf += piece.slice(0, remaining) + TRUNCATION_MARKER;
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
child.stderr?.on("data", (chunk: Buffer) => {
|
|
302
|
+
const remaining = MAX_OUTPUT_BYTES - Buffer.byteLength(stderrBuf, "utf8");
|
|
303
|
+
if (remaining <= 0) return;
|
|
304
|
+
const piece = chunk.toString("utf8");
|
|
305
|
+
if (Buffer.byteLength(piece, "utf8") <= remaining) {
|
|
306
|
+
stderrBuf += piece;
|
|
307
|
+
} else {
|
|
308
|
+
stderrBuf += piece.slice(0, remaining) + TRUNCATION_MARKER;
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const exitCode = await new Promise<number>((resolve) => {
|
|
313
|
+
child.on("close", (code) => resolve(code ?? 1));
|
|
314
|
+
child.on("error", (err: NodeJS.ErrnoException) => {
|
|
315
|
+
if (err.name === "AbortError" || abortController.signal.aborted) {
|
|
316
|
+
timedOut = true;
|
|
317
|
+
resolve(137);
|
|
318
|
+
} else {
|
|
319
|
+
resolve(1);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
clearTimeout(timeoutHandle);
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
exitCode,
|
|
328
|
+
stdout: stdoutBuf,
|
|
329
|
+
stderr: stderrBuf,
|
|
330
|
+
timedOut,
|
|
331
|
+
durationMs: Date.now() - startedAt,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private toExecutionResult<T>(args: {
|
|
336
|
+
executionId: string;
|
|
337
|
+
automationName: string;
|
|
338
|
+
input: unknown;
|
|
339
|
+
startedAt: Date;
|
|
340
|
+
sandboxResult: SandboxResult;
|
|
341
|
+
}): ExecutionResult<T> {
|
|
342
|
+
const completedAt = new Date(
|
|
343
|
+
args.startedAt.getTime() + args.sandboxResult.durationMs
|
|
344
|
+
);
|
|
345
|
+
const logs = parseStderrLogs(args.sandboxResult.stderr);
|
|
346
|
+
|
|
347
|
+
if (args.sandboxResult.timedOut) {
|
|
348
|
+
return {
|
|
349
|
+
executionId: args.executionId,
|
|
350
|
+
automationName: args.automationName,
|
|
351
|
+
status: "timeout",
|
|
352
|
+
input: args.input,
|
|
353
|
+
error: {
|
|
354
|
+
code: "TIMEOUT",
|
|
355
|
+
message: `execution timed out after ${args.sandboxResult.durationMs}ms`,
|
|
356
|
+
},
|
|
357
|
+
startedAt: args.startedAt,
|
|
358
|
+
completedAt,
|
|
359
|
+
durationMs: args.sandboxResult.durationMs,
|
|
360
|
+
logs,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Try to parse the protocol result FIRST — a non-zero exit with a valid
|
|
365
|
+
// protocol "failed" payload is a business failure, not a container crash.
|
|
366
|
+
try {
|
|
367
|
+
const protocol = parseProtocolOutput<T>(args.sandboxResult.stdout);
|
|
368
|
+
if (protocol.status === "completed") {
|
|
369
|
+
return {
|
|
370
|
+
executionId: args.executionId,
|
|
371
|
+
automationName: args.automationName,
|
|
372
|
+
status: "completed",
|
|
373
|
+
input: args.input,
|
|
374
|
+
output: protocol.output,
|
|
375
|
+
startedAt: args.startedAt,
|
|
376
|
+
completedAt,
|
|
377
|
+
durationMs: args.sandboxResult.durationMs,
|
|
378
|
+
logs,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
executionId: args.executionId,
|
|
383
|
+
automationName: args.automationName,
|
|
384
|
+
status: "failed",
|
|
385
|
+
input: args.input,
|
|
386
|
+
error: { ...protocol.error },
|
|
387
|
+
startedAt: args.startedAt,
|
|
388
|
+
completedAt,
|
|
389
|
+
durationMs: args.sandboxResult.durationMs,
|
|
390
|
+
logs,
|
|
391
|
+
};
|
|
392
|
+
} catch (err) {
|
|
393
|
+
// No parseable protocol output. If the process exited non-zero, surface
|
|
394
|
+
// it as a container crash; otherwise the binary returned 0 but said
|
|
395
|
+
// nothing useful — also a protocol violation.
|
|
396
|
+
const code =
|
|
397
|
+
args.sandboxResult.exitCode === 0
|
|
398
|
+
? "INVALID_OUTPUT"
|
|
399
|
+
: "CONTAINER_EXIT_ERROR";
|
|
400
|
+
const message =
|
|
401
|
+
args.sandboxResult.exitCode === 0
|
|
402
|
+
? "process exited 0 but produced no protocol result"
|
|
403
|
+
: `process exited with code ${args.sandboxResult.exitCode}`;
|
|
404
|
+
const stderrTail = args.sandboxResult.stderr.slice(-2000);
|
|
405
|
+
const stdoutTail = args.sandboxResult.stdout.slice(-2000);
|
|
406
|
+
const rawTail = err instanceof ProtocolParseError ? err.rawTail : "";
|
|
407
|
+
return {
|
|
408
|
+
executionId: args.executionId,
|
|
409
|
+
automationName: args.automationName,
|
|
410
|
+
status: "failed",
|
|
411
|
+
input: args.input,
|
|
412
|
+
error: {
|
|
413
|
+
code,
|
|
414
|
+
message,
|
|
415
|
+
details: { stderr: stderrTail, stdout: stdoutTail, rawTail },
|
|
416
|
+
},
|
|
417
|
+
startedAt: args.startedAt,
|
|
418
|
+
completedAt,
|
|
419
|
+
durationMs: args.sandboxResult.durationMs,
|
|
420
|
+
logs,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- 026_formal_verify_results.sql
|
|
2
|
+
--
|
|
3
|
+
-- Sprint-587 — Persistence for Z3 formal verification outcomes.
|
|
4
|
+
--
|
|
5
|
+
-- One row per (cycle_id, axiom) verification attempt. UNKNOWN and SKIPPED
|
|
6
|
+
-- are first-class states (NOT folded into SAFE/UNSAFE). The `mode` and
|
|
7
|
+
-- `context` columns preserve enough metadata to reproduce the decision
|
|
8
|
+
-- under a different policy without re-running the solver.
|
|
9
|
+
|
|
10
|
+
CREATE TABLE IF NOT EXISTS formal_verify_results (
|
|
11
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
12
|
+
cycle_id TEXT NOT NULL,
|
|
13
|
+
axiom TEXT NOT NULL,
|
|
14
|
+
state TEXT NOT NULL CHECK (state IN ('SAFE','UNSAFE','UNKNOWN','SKIPPED')),
|
|
15
|
+
rationale TEXT,
|
|
16
|
+
witness TEXT,
|
|
17
|
+
counterexample TEXT,
|
|
18
|
+
time_ms INT,
|
|
19
|
+
mode TEXT NOT NULL DEFAULT 'permissive'
|
|
20
|
+
CHECK (mode IN ('strict','permissive','audit_only')),
|
|
21
|
+
context TEXT NOT NULL DEFAULT 'runtime'
|
|
22
|
+
CHECK (context IN ('runtime','skill_ingestion')),
|
|
23
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_formal_verify_cycle
|
|
27
|
+
ON formal_verify_results (cycle_id);
|
|
28
|
+
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_formal_verify_state
|
|
30
|
+
ON formal_verify_results (state, created_at DESC);
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentPersona — load, save, and resolve a per-agent persona.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline:
|
|
5
|
+
* 1. Resolve persona for agent X
|
|
6
|
+
* 2. base = DEFAULT_PERSONA
|
|
7
|
+
* 3. brain = loadFromBrain(X) (Tier 3 semantic memory query)
|
|
8
|
+
* 4. local = loadFromFile(`.cc/persona.yaml`) — optional
|
|
9
|
+
* 5. effective = merge(base, brain, local)
|
|
10
|
+
*
|
|
11
|
+
* Brain entries use category `agent-persona`, tags `["persona", agentId]`.
|
|
12
|
+
* The latest entry per agent wins (Brain query returns entries sorted by
|
|
13
|
+
* recency).
|
|
14
|
+
*
|
|
15
|
+
* @see ./persona-schema.ts
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
20
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
21
|
+
import type { SemanticMemoryPort } from "../ports/brain.js";
|
|
22
|
+
import {
|
|
23
|
+
DEFAULT_PERSONA,
|
|
24
|
+
mergePersona,
|
|
25
|
+
PersonaSchema,
|
|
26
|
+
validatePersona,
|
|
27
|
+
type AgentPersona,
|
|
28
|
+
} from "./persona-schema.js";
|
|
29
|
+
|
|
30
|
+
export const PERSONA_BRAIN_CATEGORY = "agent-persona";
|
|
31
|
+
|
|
32
|
+
/** Tags used to query/archive a persona for a given agent. */
|
|
33
|
+
export function personaTags(agentId: string): string[] {
|
|
34
|
+
return ["persona", agentId];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ─── Brain layer ────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Save (archive) a persona for an agent in Brain Tier 3 (semantic).
|
|
41
|
+
*
|
|
42
|
+
* The persona is serialized as JSON in the `content` field — keep this
|
|
43
|
+
* structured so future queries can parse it deterministically. Tags include
|
|
44
|
+
* the agent id so per-agent retrieval is precise.
|
|
45
|
+
*
|
|
46
|
+
* Returns the archived entry id, or null when the brain port returns null
|
|
47
|
+
* (e.g. replay mode, no brain configured).
|
|
48
|
+
*/
|
|
49
|
+
export async function savePersonaToBrain(
|
|
50
|
+
brain: SemanticMemoryPort,
|
|
51
|
+
agentId: string,
|
|
52
|
+
persona: AgentPersona
|
|
53
|
+
): Promise<string | null> {
|
|
54
|
+
validatePersona(persona);
|
|
55
|
+
// Wrap the JSON body with a discoverable header line — FTS-backed Brain
|
|
56
|
+
// backends use the query string as a keyword filter, so the content MUST
|
|
57
|
+
// contain the literal "persona" + agent id for `query("persona ...")` hits.
|
|
58
|
+
const content = `persona for agent ${agentId}\n${JSON.stringify(persona)}`;
|
|
59
|
+
const entry = await brain.archive({
|
|
60
|
+
content,
|
|
61
|
+
content_type: "persona",
|
|
62
|
+
category: PERSONA_BRAIN_CATEGORY,
|
|
63
|
+
tags: personaTags(agentId),
|
|
64
|
+
metadata: { agent_id: agentId, schema_version: 1 },
|
|
65
|
+
});
|
|
66
|
+
return entry?.id ?? null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Extract the JSON body from a persona Brain entry (strips the header line).
|
|
71
|
+
* Returns null if the content doesn't follow the wrapper format.
|
|
72
|
+
*/
|
|
73
|
+
function extractPersonaJson(content: string): string | null {
|
|
74
|
+
const nl = content.indexOf("\n");
|
|
75
|
+
if (nl === -1) {
|
|
76
|
+
// Backward-compat: legacy entries stored raw JSON.
|
|
77
|
+
return content;
|
|
78
|
+
}
|
|
79
|
+
return content.slice(nl + 1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Query Brain for the latest persona for an agent.
|
|
84
|
+
*
|
|
85
|
+
* Returns the parsed persona or `null` if no entry is found (or if the
|
|
86
|
+
* latest entry's content does not parse).
|
|
87
|
+
*/
|
|
88
|
+
export async function loadPersonaFromBrain(
|
|
89
|
+
brain: SemanticMemoryPort,
|
|
90
|
+
agentId: string
|
|
91
|
+
): Promise<AgentPersona | null> {
|
|
92
|
+
const results = await brain.query("persona", {
|
|
93
|
+
category: PERSONA_BRAIN_CATEGORY,
|
|
94
|
+
tags: personaTags(agentId),
|
|
95
|
+
limit: 5,
|
|
96
|
+
});
|
|
97
|
+
if (results.length === 0) return null;
|
|
98
|
+
// Prefer the most recent entry by created_at if available.
|
|
99
|
+
const sorted = [...results].sort((a, b) => {
|
|
100
|
+
const ta = a.created_at ? Date.parse(a.created_at) : 0;
|
|
101
|
+
const tb = b.created_at ? Date.parse(b.created_at) : 0;
|
|
102
|
+
return tb - ta;
|
|
103
|
+
});
|
|
104
|
+
for (const entry of sorted) {
|
|
105
|
+
const json = extractPersonaJson(entry.content);
|
|
106
|
+
if (!json) continue;
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(json) as unknown;
|
|
109
|
+
const validated = PersonaSchema.safeParse(parsed);
|
|
110
|
+
if (validated.success) return validated.data;
|
|
111
|
+
} catch {
|
|
112
|
+
// skip malformed entry, try next
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─── File layer ─────────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Load a persona from a YAML file. Returns null if the file is missing or
|
|
122
|
+
* the content does not validate. Throws only on filesystem errors other
|
|
123
|
+
* than ENOENT.
|
|
124
|
+
*/
|
|
125
|
+
export async function loadPersonaFromFile(
|
|
126
|
+
path: string
|
|
127
|
+
): Promise<AgentPersona | null> {
|
|
128
|
+
let raw: string;
|
|
129
|
+
try {
|
|
130
|
+
raw = await readFile(path, "utf-8");
|
|
131
|
+
} catch (err) {
|
|
132
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
133
|
+
if (code === "ENOENT") return null;
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
136
|
+
let parsed: unknown;
|
|
137
|
+
try {
|
|
138
|
+
parsed = parseYaml(raw);
|
|
139
|
+
} catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const result = PersonaSchema.safeParse(parsed);
|
|
143
|
+
return result.success ? result.data : null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Persist a persona to a YAML file (overwrites). Validates first.
|
|
148
|
+
*/
|
|
149
|
+
export async function savePersonaToFile(
|
|
150
|
+
path: string,
|
|
151
|
+
persona: AgentPersona
|
|
152
|
+
): Promise<void> {
|
|
153
|
+
const validated = validatePersona(persona);
|
|
154
|
+
const yaml = stringifyYaml(validated, { indent: 2 });
|
|
155
|
+
await writeFile(path, yaml, "utf-8");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ─── Resolution ─────────────────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
export interface ResolvePersonaOptions {
|
|
161
|
+
agentId: string;
|
|
162
|
+
brain?: SemanticMemoryPort;
|
|
163
|
+
localPath?: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface ResolvedPersona {
|
|
167
|
+
effective: AgentPersona;
|
|
168
|
+
/** Layers actually applied, in order (latest wins). */
|
|
169
|
+
layers: Array<"defaults" | "brain" | "local">;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Resolve the effective persona for an agent.
|
|
174
|
+
*
|
|
175
|
+
* Composition order (each layer overrides the previous):
|
|
176
|
+
* 1. DEFAULT_PERSONA
|
|
177
|
+
* 2. Brain entry (Tier 3 semantic, optional)
|
|
178
|
+
* 3. Local `.cc/persona.yaml` (optional)
|
|
179
|
+
*
|
|
180
|
+
* The returned `layers` array documents which sources contributed, useful
|
|
181
|
+
* for diagnostics (e.g. `cc persona show --explain`).
|
|
182
|
+
*/
|
|
183
|
+
export async function resolvePersona(
|
|
184
|
+
opts: ResolvePersonaOptions
|
|
185
|
+
): Promise<ResolvedPersona> {
|
|
186
|
+
const layers: ResolvedPersona["layers"] = ["defaults"];
|
|
187
|
+
let effective: AgentPersona = DEFAULT_PERSONA;
|
|
188
|
+
if (opts.brain) {
|
|
189
|
+
const fromBrain = await loadPersonaFromBrain(opts.brain, opts.agentId);
|
|
190
|
+
if (fromBrain) {
|
|
191
|
+
effective = mergePersona(effective, fromBrain);
|
|
192
|
+
layers.push("brain");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (opts.localPath) {
|
|
196
|
+
const fromFile = await loadPersonaFromFile(opts.localPath);
|
|
197
|
+
if (fromFile) {
|
|
198
|
+
effective = mergePersona(effective, fromFile);
|
|
199
|
+
layers.push("local");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return { effective, layers };
|
|
203
|
+
}
|