@vauban-org/agent-sdk 1.0.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRACT.md +6918 -742
- 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 +41 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -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/agent.d.ts.map +1 -1
- package/dist/orchestration/ooda/agent.js +36 -0
- package/dist/orchestration/ooda/agent.js.map +1 -1
- 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/orchestration/ooda/types.d.ts +11 -0
- package/dist/orchestration/ooda/types.d.ts.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/_secrets.d.ts +16 -0
- package/dist/skills/_secrets.d.ts.map +1 -0
- package/dist/skills/_secrets.js +20 -0
- package/dist/skills/_secrets.js.map +1 -0
- package/dist/skills/alpaca-quote.d.ts +2 -2
- package/dist/skills/alpaca-quote.d.ts.map +1 -1
- package/dist/skills/alpaca-quote.js +51 -20
- package/dist/skills/alpaca-quote.js.map +1 -1
- 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 +2 -2
- package/dist/skills/send-email.d.ts.map +1 -1
- package/dist/skills/send-email.js +4 -3
- package/dist/skills/send-email.js.map +1 -1
- package/dist/skills/slack-notify.d.ts +4 -4
- package/dist/skills/slack-notify.d.ts.map +1 -1
- package/dist/skills/slack-notify.js +52 -21
- package/dist/skills/slack-notify.js.map +1 -1
- package/dist/skills/starknet-balance.d.ts +1 -1
- package/dist/skills/telegram-notify.d.ts +4 -4
- package/dist/skills/telegram-notify.d.ts.map +1 -1
- package/dist/skills/telegram-notify.js +48 -19
- package/dist/skills/telegram-notify.js.map +1 -1
- package/dist/skills/web-search.d.ts +1 -1
- package/dist/skills/web-search.d.ts.map +1 -1
- package/dist/skills/web-search.js +85 -40
- package/dist/skills/web-search.js.map +1 -1
- package/dist/telemetry/bus.d.ts +54 -0
- package/dist/telemetry/bus.d.ts.map +1 -0
- package/dist/telemetry/bus.js +159 -0
- package/dist/telemetry/bus.js.map +1 -0
- package/dist/telemetry/index.d.ts +35 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +30 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/port.d.ts +121 -0
- package/dist/telemetry/port.d.ts.map +1 -0
- package/dist/telemetry/port.js +48 -0
- package/dist/telemetry/port.js.map +1 -0
- package/dist/telemetry/sinks/otlp.d.ts +45 -0
- package/dist/telemetry/sinks/otlp.d.ts.map +1 -0
- package/dist/telemetry/sinks/otlp.js +195 -0
- package/dist/telemetry/sinks/otlp.js.map +1 -0
- package/dist/telemetry/sinks/sqlite.d.ts +32 -0
- package/dist/telemetry/sinks/sqlite.d.ts.map +1 -0
- package/dist/telemetry/sinks/sqlite.js +170 -0
- package/dist/telemetry/sinks/sqlite.js.map +1 -0
- package/dist/telemetry/sinks/stdout.d.ts +22 -0
- package/dist/telemetry/sinks/stdout.d.ts.map +1 -0
- package/dist/telemetry/sinks/stdout.js +38 -0
- package/dist/telemetry/sinks/stdout.js.map +1 -0
- 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/docs/telemetry/migration.md +155 -0
- package/docs/telemetry/overview.md +154 -0
- package/docs/telemetry/privacy.md +127 -0
- package/docs/telemetry/sinks/cc.md +155 -0
- package/docs/telemetry/sinks/otlp.md +146 -0
- package/docs/telemetry/sinks/sqlite.md +126 -0
- package/docs/telemetry/sinks/stdout.md +82 -0
- package/package.json +18 -2
- 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 +368 -2
- 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/agent.ts +50 -0
- package/src/orchestration/ooda/skills.ts +177 -0
- package/src/orchestration/ooda/types.ts +12 -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/_secrets.ts +25 -0
- package/src/skills/alpaca-quote.ts +68 -23
- 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 +4 -3
- package/src/skills/slack-notify.ts +73 -30
- package/src/skills/telegram-notify.ts +70 -24
- package/src/skills/web-search.ts +132 -50
- package/src/telemetry/bus.test.ts +231 -0
- package/src/telemetry/bus.ts +241 -0
- package/src/telemetry/index.ts +49 -0
- package/src/telemetry/port.ts +160 -0
- package/src/telemetry/sinks/otlp.test.ts +146 -0
- package/src/telemetry/sinks/otlp.ts +250 -0
- package/src/telemetry/sinks/sqlite.test.ts +121 -0
- package/src/telemetry/sinks/sqlite.ts +260 -0
- package/src/telemetry/sinks/stdout.test.ts +109 -0
- package/src/telemetry/sinks/stdout.ts +59 -0
- 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,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry primitives with exponential backoff and optional jitter.
|
|
3
|
+
*
|
|
4
|
+
* Provides three usage modes:
|
|
5
|
+
* - `retry(fn, opts)` — wrap an async call.
|
|
6
|
+
* - `RetryContext` — manual control flow for granular retry logic.
|
|
7
|
+
* - {@link ./presets.js} — 4 named configurations for common scenarios.
|
|
8
|
+
*
|
|
9
|
+
* Sleep is injectable via `RetryOptions.sleepFn` for deterministic tests.
|
|
10
|
+
* BaseError.retryable flag is honored by default presets (see ./presets.ts).
|
|
11
|
+
*
|
|
12
|
+
* @public @since 1.2.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { RETRY_TRANSIENT } from "./presets.js";
|
|
16
|
+
|
|
17
|
+
export type SleepFn = (ms: number) => Promise<void>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Retry policy. `jitter: true` adds ±25% randomization to each delay
|
|
21
|
+
* (anti-thundering-herd when multiple callers retry simultaneously).
|
|
22
|
+
*
|
|
23
|
+
* Selection precedence on each error:
|
|
24
|
+
* 1. `retryIf` predicate if provided
|
|
25
|
+
* 2. `retryOn` class membership if provided
|
|
26
|
+
* 3. Default: retry on any error
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
export interface RetryConfig {
|
|
31
|
+
/** Total attempts including the first try. Must be >= 1. */
|
|
32
|
+
readonly maxAttempts: number;
|
|
33
|
+
/** Initial delay before retry 1, in ms. */
|
|
34
|
+
readonly baseDelayMs: number;
|
|
35
|
+
/** Upper bound on any computed delay, in ms. */
|
|
36
|
+
readonly maxDelayMs: number;
|
|
37
|
+
/** Exponent for `baseDelayMs * base^attempt`. Typically 2.0. */
|
|
38
|
+
readonly exponentialBase: number;
|
|
39
|
+
/** Whether to add ±25% random jitter to each delay. */
|
|
40
|
+
readonly jitter: boolean;
|
|
41
|
+
/** If set, only retry when the error is an instance of one of these classes. */
|
|
42
|
+
readonly retryOn?: ReadonlyArray<new (...args: never[]) => Error>;
|
|
43
|
+
/** If set, called per error; return true to retry. Overrides retryOn. */
|
|
44
|
+
readonly retryIf?: (err: unknown) => boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Options to {@link retry}.
|
|
49
|
+
* @public
|
|
50
|
+
*/
|
|
51
|
+
export interface RetryOptions {
|
|
52
|
+
/** Retry policy. Defaults to RETRY_TRANSIENT. */
|
|
53
|
+
config?: RetryConfig;
|
|
54
|
+
/** Called before each retry sleep. Receives the error, the 0-indexed attempt that failed, and the upcoming delay. */
|
|
55
|
+
onRetry?: (err: unknown, attempt: number, delayMs: number) => void;
|
|
56
|
+
/** Sleep injection — defaults to setTimeout. Tests pass a zero-delay or mock. */
|
|
57
|
+
sleepFn?: SleepFn;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Thrown after all retry attempts have been exhausted.
|
|
62
|
+
* @public
|
|
63
|
+
*/
|
|
64
|
+
export class RetryExhaustedError extends Error {
|
|
65
|
+
readonly attempts: number;
|
|
66
|
+
readonly lastError: unknown;
|
|
67
|
+
constructor(attempts: number, lastError: unknown) {
|
|
68
|
+
const reason =
|
|
69
|
+
lastError instanceof Error ? lastError.message : String(lastError);
|
|
70
|
+
super(`All ${attempts} retry attempts exhausted: ${reason}`);
|
|
71
|
+
this.name = "RetryExhaustedError";
|
|
72
|
+
this.attempts = attempts;
|
|
73
|
+
this.lastError = lastError;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const defaultSleep: SleepFn = (ms) =>
|
|
78
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Compute the delay before retry `attempt` (0-indexed). Caps at `maxDelayMs`.
|
|
82
|
+
* Jitter, when enabled, adds a uniform-random ±25% offset.
|
|
83
|
+
*
|
|
84
|
+
* Visible for tests and for callers that need to inspect timing.
|
|
85
|
+
* @public
|
|
86
|
+
*/
|
|
87
|
+
export function calculateDelay(config: RetryConfig, attempt: number): number {
|
|
88
|
+
const raw = config.baseDelayMs * Math.pow(config.exponentialBase, attempt);
|
|
89
|
+
const capped = Math.min(raw, config.maxDelayMs);
|
|
90
|
+
if (!config.jitter) return Math.max(0, capped);
|
|
91
|
+
const jitterRange = capped * 0.25;
|
|
92
|
+
const offset = (Math.random() * 2 - 1) * jitterRange;
|
|
93
|
+
return Math.max(0, capped + offset);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Decide whether to retry after `err` at 0-indexed `attempt`.
|
|
98
|
+
*
|
|
99
|
+
* Returns false on the final attempt (caller should throw exhaustion).
|
|
100
|
+
*
|
|
101
|
+
* @public
|
|
102
|
+
*/
|
|
103
|
+
export function shouldRetry(
|
|
104
|
+
config: RetryConfig,
|
|
105
|
+
err: unknown,
|
|
106
|
+
attempt: number
|
|
107
|
+
): boolean {
|
|
108
|
+
if (attempt >= config.maxAttempts - 1) return false;
|
|
109
|
+
if (config.retryIf) return config.retryIf(err);
|
|
110
|
+
if (config.retryOn && config.retryOn.length > 0) {
|
|
111
|
+
return config.retryOn.some((Ctor) => err instanceof Ctor);
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Execute `fn` with retry and exponential backoff.
|
|
118
|
+
*
|
|
119
|
+
* @throws The non-retryable error as-is if `shouldRetry` returns false mid-loop.
|
|
120
|
+
* @throws {@link RetryExhaustedError} when all attempts have failed.
|
|
121
|
+
*
|
|
122
|
+
* @public
|
|
123
|
+
*/
|
|
124
|
+
export async function retry<T>(
|
|
125
|
+
fn: () => Promise<T>,
|
|
126
|
+
opts: RetryOptions = {}
|
|
127
|
+
): Promise<T> {
|
|
128
|
+
const config = opts.config ?? RETRY_TRANSIENT;
|
|
129
|
+
const sleep = opts.sleepFn ?? defaultSleep;
|
|
130
|
+
|
|
131
|
+
let lastError: unknown;
|
|
132
|
+
|
|
133
|
+
for (let attempt = 0; attempt < config.maxAttempts; attempt++) {
|
|
134
|
+
try {
|
|
135
|
+
return await fn();
|
|
136
|
+
} catch (err) {
|
|
137
|
+
lastError = err;
|
|
138
|
+
if (!shouldRetry(config, err, attempt)) {
|
|
139
|
+
if (attempt >= config.maxAttempts - 1) {
|
|
140
|
+
throw new RetryExhaustedError(config.maxAttempts, err);
|
|
141
|
+
}
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
144
|
+
const delay = calculateDelay(config, attempt);
|
|
145
|
+
opts.onRetry?.(err, attempt, delay);
|
|
146
|
+
await sleep(delay);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
throw new RetryExhaustedError(config.maxAttempts, lastError);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Manual retry control. Use when the operation doesn't fit a single `fn()` —
|
|
155
|
+
* e.g. when multiple endpoints are tried per attempt, or when partial state
|
|
156
|
+
* must be reset between tries.
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* const ctx = new RetryContext({ config: RETRY_AGGRESSIVE });
|
|
161
|
+
* while (ctx.shouldContinue) {
|
|
162
|
+
* try {
|
|
163
|
+
* return await op();
|
|
164
|
+
* } catch (err) {
|
|
165
|
+
* await ctx.handleError(err);
|
|
166
|
+
* }
|
|
167
|
+
* }
|
|
168
|
+
* ```
|
|
169
|
+
*
|
|
170
|
+
* @public
|
|
171
|
+
*/
|
|
172
|
+
export class RetryContext {
|
|
173
|
+
private readonly config: RetryConfig;
|
|
174
|
+
private readonly sleep: SleepFn;
|
|
175
|
+
private readonly onRetry?: (
|
|
176
|
+
err: unknown,
|
|
177
|
+
attempt: number,
|
|
178
|
+
delayMs: number
|
|
179
|
+
) => void;
|
|
180
|
+
private attemptCount = 0;
|
|
181
|
+
private exhausted = false;
|
|
182
|
+
|
|
183
|
+
constructor(opts: RetryOptions & { config: RetryConfig }) {
|
|
184
|
+
this.config = opts.config;
|
|
185
|
+
this.sleep = opts.sleepFn ?? defaultSleep;
|
|
186
|
+
this.onRetry = opts.onRetry;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** True while attempts remain and no non-retryable error has been thrown. */
|
|
190
|
+
get shouldContinue(): boolean {
|
|
191
|
+
return !this.exhausted && this.attemptCount < this.config.maxAttempts;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** 0-indexed count of completed (failed) attempts. */
|
|
195
|
+
get attempt(): number {
|
|
196
|
+
return this.attemptCount;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Record an error and sleep before the next try.
|
|
201
|
+
*
|
|
202
|
+
* @throws The non-retryable error as-is.
|
|
203
|
+
* @throws {@link RetryExhaustedError} if attempts are exhausted.
|
|
204
|
+
*/
|
|
205
|
+
async handleError(err: unknown): Promise<void> {
|
|
206
|
+
const failedAttempt = this.attemptCount;
|
|
207
|
+
this.attemptCount += 1;
|
|
208
|
+
if (this.attemptCount >= this.config.maxAttempts) {
|
|
209
|
+
this.exhausted = true;
|
|
210
|
+
throw new RetryExhaustedError(this.config.maxAttempts, err);
|
|
211
|
+
}
|
|
212
|
+
if (!shouldRetry(this.config, err, failedAttempt)) {
|
|
213
|
+
this.exhausted = true;
|
|
214
|
+
throw err;
|
|
215
|
+
}
|
|
216
|
+
const delay = calculateDelay(this.config, failedAttempt);
|
|
217
|
+
this.onRetry?.(err, failedAttempt, delay);
|
|
218
|
+
await this.sleep(delay);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export {
|
|
223
|
+
RETRY_TRANSIENT,
|
|
224
|
+
RETRY_AGGRESSIVE,
|
|
225
|
+
RETRY_PATIENT,
|
|
226
|
+
NO_RETRY,
|
|
227
|
+
} from "./presets.js";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named retry presets covering common scenarios.
|
|
3
|
+
*
|
|
4
|
+
* All presets honor a `retryable: boolean` flag on the thrown error when
|
|
5
|
+
* present (compatible with {@link ../errors.js#BaseError}). When the flag is
|
|
6
|
+
* absent, defaults fall back to: transient = retry, no_retry = no retry.
|
|
7
|
+
*
|
|
8
|
+
* @public @since 1.2.0
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { RetryConfig } from "./index.js";
|
|
12
|
+
|
|
13
|
+
function honorsRetryableFlag(err: unknown, defaultIfMissing: boolean): boolean {
|
|
14
|
+
if (typeof err === "object" && err !== null && "retryable" in err) {
|
|
15
|
+
return (err as { retryable: unknown }).retryable === true;
|
|
16
|
+
}
|
|
17
|
+
return defaultIfMissing;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 3 attempts, 1s base, 10s cap, jitter on. Honors `retryable` flag (defaults
|
|
22
|
+
* to retry when absent). Use for short-lived network calls.
|
|
23
|
+
*
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
export const RETRY_TRANSIENT: RetryConfig = {
|
|
27
|
+
maxAttempts: 3,
|
|
28
|
+
baseDelayMs: 1000,
|
|
29
|
+
maxDelayMs: 10_000,
|
|
30
|
+
exponentialBase: 2.0,
|
|
31
|
+
jitter: true,
|
|
32
|
+
retryIf: (err) => honorsRetryableFlag(err, true),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 5 attempts, 500ms base, 30s cap, jitter on. Use for flaky external APIs
|
|
37
|
+
* where rapid recovery is plausible.
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
export const RETRY_AGGRESSIVE: RetryConfig = {
|
|
42
|
+
maxAttempts: 5,
|
|
43
|
+
baseDelayMs: 500,
|
|
44
|
+
maxDelayMs: 30_000,
|
|
45
|
+
exponentialBase: 2.0,
|
|
46
|
+
jitter: true,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 10 attempts, 2s base, 120s cap, jitter on. Use for long-recovery
|
|
51
|
+
* dependencies (database failover, restart-storm tolerant calls).
|
|
52
|
+
*
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
export const RETRY_PATIENT: RetryConfig = {
|
|
56
|
+
maxAttempts: 10,
|
|
57
|
+
baseDelayMs: 2000,
|
|
58
|
+
maxDelayMs: 120_000,
|
|
59
|
+
exponentialBase: 2.0,
|
|
60
|
+
jitter: true,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Single attempt, no retry. Use as an explicit policy to disable retry at
|
|
65
|
+
* call sites without scattering booleans.
|
|
66
|
+
*
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
export const NO_RETRY: RetryConfig = {
|
|
70
|
+
maxAttempts: 1,
|
|
71
|
+
baseDelayMs: 0,
|
|
72
|
+
maxDelayMs: 0,
|
|
73
|
+
exponentialBase: 2.0,
|
|
74
|
+
jitter: false,
|
|
75
|
+
};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/skill-loop/ab-runner.ts
|
|
3
|
+
*
|
|
4
|
+
* A/B test runner with failure budgets for SkillCandidates.
|
|
5
|
+
*
|
|
6
|
+
* Invariants:
|
|
7
|
+
* - Max 3 candidates simultaneously (4th is rejected).
|
|
8
|
+
* - Traffic fraction cap: 10% per candidate.
|
|
9
|
+
* - Rollback triggered when a candidate's mean score drops >5% vs incumbent.
|
|
10
|
+
* - Winner declared when: p < 0.05 AND delta >= 10% AND no rollback triggered.
|
|
11
|
+
*
|
|
12
|
+
* Statistical significance uses the same Welch approach as evaluator.ts.
|
|
13
|
+
*
|
|
14
|
+
* @module skill-loop/ab-runner
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { SkillCandidate } from "./candidate.js";
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Types
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
export interface ABConfig {
|
|
24
|
+
/** Maximum concurrent candidates. Hard cap: 3. */
|
|
25
|
+
maxCandidates: number;
|
|
26
|
+
/** Traffic fraction routed to each candidate. Hard cap: 0.10. */
|
|
27
|
+
trafficPct: number;
|
|
28
|
+
/** Rollback threshold: if candidate mean drops by this fraction vs incumbent. */
|
|
29
|
+
rollbackThresholdPct: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ABSlot {
|
|
33
|
+
candidateId: string;
|
|
34
|
+
trafficFraction: number;
|
|
35
|
+
outcomes: number[];
|
|
36
|
+
rollbackTriggered: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Statistical helpers (inline — avoid circular dep with evaluator)
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
function mean(xs: number[]): number {
|
|
44
|
+
if (xs.length === 0) return 0;
|
|
45
|
+
return xs.reduce((a, b) => a + b, 0) / xs.length;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function variance(xs: number[], m: number): number {
|
|
49
|
+
if (xs.length < 2) return 0;
|
|
50
|
+
return xs.reduce((acc, x) => acc + (x - m) ** 2, 0) / (xs.length - 1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function normalCdf(z: number): number {
|
|
54
|
+
const t = 1 / (1 + 0.2316419 * z);
|
|
55
|
+
const d = 0.3989423 * Math.exp((-z * z) / 2);
|
|
56
|
+
const p =
|
|
57
|
+
d *
|
|
58
|
+
t *
|
|
59
|
+
(0.3193815 +
|
|
60
|
+
t * (-0.3565638 + t * (1.7814779 + t * (-1.8212559 + t * 1.3302744))));
|
|
61
|
+
return 1 - p;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function welchPValue(a: number[], b: number[]): number {
|
|
65
|
+
const n1 = a.length;
|
|
66
|
+
const n2 = b.length;
|
|
67
|
+
if (n1 < 2 || n2 < 2) return 1.0;
|
|
68
|
+
|
|
69
|
+
const m1 = mean(a);
|
|
70
|
+
const m2 = mean(b);
|
|
71
|
+
const v1 = variance(a, m1);
|
|
72
|
+
const v2 = variance(b, m2);
|
|
73
|
+
const se = Math.sqrt(v1 / n1 + v2 / n2);
|
|
74
|
+
if (se === 0) return m1 === m2 ? 1.0 : 0.0;
|
|
75
|
+
|
|
76
|
+
const tStat = Math.abs((m1 - m2) / se);
|
|
77
|
+
const df =
|
|
78
|
+
(v1 / n1 + v2 / n2) ** 2 /
|
|
79
|
+
((v1 / n1) ** 2 / (n1 - 1) + (v2 / n2) ** 2 / (n2 - 1));
|
|
80
|
+
|
|
81
|
+
if (df >= 30) {
|
|
82
|
+
return Math.max(0, Math.min(1, 2 * (1 - normalCdf(tStat))));
|
|
83
|
+
}
|
|
84
|
+
// Conservative: approximate p via critical value at df ≈ 30 (t-crit = 2.042)
|
|
85
|
+
return tStat > 2.042 ? 0.04 : 0.1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// ABRunner
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
export class ABRunner {
|
|
93
|
+
private readonly config: Readonly<ABConfig>;
|
|
94
|
+
private readonly slots = new Map<string, ABSlot>();
|
|
95
|
+
private readonly candidates = new Map<string, SkillCandidate>();
|
|
96
|
+
private readonly incumbentOutcomes: number[] = [];
|
|
97
|
+
|
|
98
|
+
constructor(config: ABConfig) {
|
|
99
|
+
// Enforce hard caps
|
|
100
|
+
this.config = {
|
|
101
|
+
maxCandidates: Math.min(config.maxCandidates, 3),
|
|
102
|
+
trafficPct: Math.min(config.trafficPct, 0.1),
|
|
103
|
+
rollbackThresholdPct: config.rollbackThresholdPct,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Register a new candidate for A/B testing.
|
|
109
|
+
*
|
|
110
|
+
* @returns true if added, false if maxCandidates already reached.
|
|
111
|
+
*/
|
|
112
|
+
addCandidate(candidate: SkillCandidate): boolean {
|
|
113
|
+
if (this.slots.size >= this.config.maxCandidates) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
this.candidates.set(candidate.id, candidate);
|
|
117
|
+
this.slots.set(candidate.id, {
|
|
118
|
+
candidateId: candidate.id,
|
|
119
|
+
trafficFraction: this.config.trafficPct,
|
|
120
|
+
outcomes: [],
|
|
121
|
+
rollbackTriggered: false,
|
|
122
|
+
});
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Record an outcome score for a candidate or the incumbent.
|
|
128
|
+
*
|
|
129
|
+
* @param candidateId - Candidate ID or "incumbent".
|
|
130
|
+
* @param score - Outcome score [0, 1].
|
|
131
|
+
*/
|
|
132
|
+
recordOutcome(candidateId: string | "incumbent", score: number): void {
|
|
133
|
+
if (candidateId === "incumbent") {
|
|
134
|
+
this.incumbentOutcomes.push(score);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const slot = this.slots.get(candidateId);
|
|
138
|
+
if (!slot) return;
|
|
139
|
+
|
|
140
|
+
slot.outcomes.push(score);
|
|
141
|
+
|
|
142
|
+
// Check rollback condition after each new data point
|
|
143
|
+
if (!slot.rollbackTriggered && this.shouldRollback(candidateId)) {
|
|
144
|
+
slot.rollbackTriggered = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Returns true if the candidate's mean score has dropped >rollbackThresholdPct
|
|
150
|
+
* vs the incumbent mean.
|
|
151
|
+
*/
|
|
152
|
+
shouldRollback(candidateId: string): boolean {
|
|
153
|
+
const slot = this.slots.get(candidateId);
|
|
154
|
+
if (!slot || slot.outcomes.length === 0) return false;
|
|
155
|
+
if (this.incumbentOutcomes.length === 0) return false;
|
|
156
|
+
|
|
157
|
+
const candMean = mean(slot.outcomes);
|
|
158
|
+
const incMean = mean(this.incumbentOutcomes);
|
|
159
|
+
|
|
160
|
+
// Degradation: candidate is worse than incumbent by more than threshold
|
|
161
|
+
const degradation = incMean - candMean;
|
|
162
|
+
return degradation > this.config.rollbackThresholdPct;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Return the winning candidate if statistical significance is established.
|
|
167
|
+
*
|
|
168
|
+
* Winner criteria:
|
|
169
|
+
* - p < 0.05 (Welch's t-test vs incumbent)
|
|
170
|
+
* - delta >= 10% vs incumbent mean
|
|
171
|
+
* - rollback NOT triggered
|
|
172
|
+
*
|
|
173
|
+
* Returns null if no candidate meets all criteria.
|
|
174
|
+
*/
|
|
175
|
+
getWinner(): SkillCandidate | null {
|
|
176
|
+
if (this.incumbentOutcomes.length < 2) return null;
|
|
177
|
+
|
|
178
|
+
const incMean = mean(this.incumbentOutcomes);
|
|
179
|
+
|
|
180
|
+
for (const [id, slot] of this.slots) {
|
|
181
|
+
if (slot.rollbackTriggered) continue;
|
|
182
|
+
if (slot.outcomes.length < 2) continue;
|
|
183
|
+
|
|
184
|
+
const candMean = mean(slot.outcomes);
|
|
185
|
+
const delta = candMean - incMean;
|
|
186
|
+
if (delta < 0.1) continue;
|
|
187
|
+
|
|
188
|
+
const p = welchPValue(slot.outcomes, this.incumbentOutcomes);
|
|
189
|
+
if (p < 0.05) {
|
|
190
|
+
return this.candidates.get(id) ?? null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/skill-loop/adoption.ts
|
|
3
|
+
*
|
|
4
|
+
* Skill adoption — auto-promote and deprecate logic gated by sign-off.
|
|
5
|
+
*
|
|
6
|
+
* Promotion conditions (ALL must hold):
|
|
7
|
+
* 1. EvalResult.pValue < 0.05 (statistical significance)
|
|
8
|
+
* 2. EvalResult.deltaVsIncumbent >= 0.10 (>=10% improvement)
|
|
9
|
+
* 3. A valid approved SignOffRecord exists for the candidateId
|
|
10
|
+
*
|
|
11
|
+
* Violation of condition 3 → throws PromotionWithoutSignOffError.
|
|
12
|
+
*
|
|
13
|
+
* Underperformers (deltaVsIncumbent < -0.05 OR pValue > 0.5) are
|
|
14
|
+
* automatically deprecated via deprecateUnderperformers().
|
|
15
|
+
*
|
|
16
|
+
* @module skill-loop/adoption
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { SkillCandidate } from "./candidate.js";
|
|
20
|
+
import type { EvalResult } from "./evaluator.js";
|
|
21
|
+
import type { SignOffRecord } from "./sign-off.js";
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Errors
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
export class PromotionWithoutSignOffError extends Error {
|
|
28
|
+
readonly candidateId: string;
|
|
29
|
+
|
|
30
|
+
constructor(candidateId: string) {
|
|
31
|
+
super(
|
|
32
|
+
`adoption: cannot promote candidate "${candidateId}" without a valid approved sign-off. ` +
|
|
33
|
+
"Request human approval via SignOffManager.requestSignOff() first."
|
|
34
|
+
);
|
|
35
|
+
this.name = "PromotionWithoutSignOffError";
|
|
36
|
+
this.candidateId = candidateId;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Types
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
export type AdoptionStatus =
|
|
45
|
+
| "promoted"
|
|
46
|
+
| "rejected"
|
|
47
|
+
| "deprecated"
|
|
48
|
+
| "pending_signoff"
|
|
49
|
+
| "insufficient_signal";
|
|
50
|
+
|
|
51
|
+
export interface AdoptionRecord {
|
|
52
|
+
candidateId: string;
|
|
53
|
+
status: AdoptionStatus;
|
|
54
|
+
evalResult: EvalResult;
|
|
55
|
+
signOff: SignOffRecord | null;
|
|
56
|
+
promotedAt: Date | null;
|
|
57
|
+
deprecatedAt: Date | null;
|
|
58
|
+
reason: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Adoption helpers
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Attempt to promote a candidate to production.
|
|
67
|
+
*
|
|
68
|
+
* @param candidate - The skill candidate.
|
|
69
|
+
* @param evalResult - Evaluation result against the verifier set.
|
|
70
|
+
* @param signOff - Approved sign-off record (must be decision === 'approved').
|
|
71
|
+
* @param nowDate - Optional date override for deterministic testing.
|
|
72
|
+
*
|
|
73
|
+
* @throws PromotionWithoutSignOffError if signOff is null or not approved.
|
|
74
|
+
*/
|
|
75
|
+
export function promoteCandidate(
|
|
76
|
+
candidate: SkillCandidate,
|
|
77
|
+
evalResult: EvalResult,
|
|
78
|
+
signOff: SignOffRecord | null,
|
|
79
|
+
nowDate?: Date
|
|
80
|
+
): AdoptionRecord {
|
|
81
|
+
// Gate 1: sign-off required — never auto-promote without human approval
|
|
82
|
+
if (!signOff || signOff.decision !== "approved") {
|
|
83
|
+
throw new PromotionWithoutSignOffError(candidate.id);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Gate 2: statistical significance
|
|
87
|
+
if (!evalResult.significant) {
|
|
88
|
+
return {
|
|
89
|
+
candidateId: candidate.id,
|
|
90
|
+
status: "insufficient_signal",
|
|
91
|
+
evalResult,
|
|
92
|
+
signOff,
|
|
93
|
+
promotedAt: null,
|
|
94
|
+
deprecatedAt: null,
|
|
95
|
+
reason: `insufficient signal: p=${evalResult.pValue.toFixed(
|
|
96
|
+
4
|
|
97
|
+
)} delta=${evalResult.deltaVsIncumbent.toFixed(
|
|
98
|
+
4
|
|
99
|
+
)} (need p<0.05 AND delta>=0.10)`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// All gates passed → promote
|
|
104
|
+
return {
|
|
105
|
+
candidateId: candidate.id,
|
|
106
|
+
status: "promoted",
|
|
107
|
+
evalResult,
|
|
108
|
+
signOff,
|
|
109
|
+
promotedAt: nowDate ?? new Date(),
|
|
110
|
+
deprecatedAt: null,
|
|
111
|
+
reason: `promoted: p=${evalResult.pValue.toFixed(4)} delta=${(
|
|
112
|
+
evalResult.deltaVsIncumbent * 100
|
|
113
|
+
).toFixed(1)}% sign-off by ${signOff.approverId}`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Reject a candidate explicitly (e.g. sign-off decision = rejected).
|
|
119
|
+
*/
|
|
120
|
+
export function rejectCandidate(
|
|
121
|
+
candidate: SkillCandidate,
|
|
122
|
+
evalResult: EvalResult,
|
|
123
|
+
signOff: SignOffRecord | null,
|
|
124
|
+
reason: string
|
|
125
|
+
): AdoptionRecord {
|
|
126
|
+
return {
|
|
127
|
+
candidateId: candidate.id,
|
|
128
|
+
status: "rejected",
|
|
129
|
+
evalResult,
|
|
130
|
+
signOff,
|
|
131
|
+
promotedAt: null,
|
|
132
|
+
deprecatedAt: null,
|
|
133
|
+
reason,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Underperformer deprecation
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Deprecation threshold: candidate's delta < -5% OR p-value > 0.5 with negative delta.
|
|
143
|
+
*/
|
|
144
|
+
const UNDERPERFORMER_DELTA_THRESHOLD = -0.05;
|
|
145
|
+
const UNDERPERFORMER_PVALUE_THRESHOLD = 0.5;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Automatically deprecate underperforming candidates from a pool.
|
|
149
|
+
*
|
|
150
|
+
* A candidate is an underperformer if:
|
|
151
|
+
* - deltaVsIncumbent < -0.05 (degrades by more than 5%)
|
|
152
|
+
* - OR (deltaVsIncumbent < 0 AND pValue > 0.5) — statistically noisy & negative
|
|
153
|
+
*
|
|
154
|
+
* Deprecation is automatic — no sign-off required (sign-off is only for promotion).
|
|
155
|
+
*
|
|
156
|
+
* @param candidates - Pool of (candidate, evalResult) pairs to assess.
|
|
157
|
+
* @param nowDate - Optional date override.
|
|
158
|
+
* @returns AdoptionRecords for every deprecated candidate.
|
|
159
|
+
*/
|
|
160
|
+
export function deprecateUnderperformers(
|
|
161
|
+
candidates: Array<{ candidate: SkillCandidate; evalResult: EvalResult }>,
|
|
162
|
+
nowDate?: Date
|
|
163
|
+
): AdoptionRecord[] {
|
|
164
|
+
const deprecated: AdoptionRecord[] = [];
|
|
165
|
+
|
|
166
|
+
for (const { candidate, evalResult } of candidates) {
|
|
167
|
+
const isUnderperformer =
|
|
168
|
+
evalResult.deltaVsIncumbent < UNDERPERFORMER_DELTA_THRESHOLD ||
|
|
169
|
+
(evalResult.deltaVsIncumbent < 0 &&
|
|
170
|
+
evalResult.pValue > UNDERPERFORMER_PVALUE_THRESHOLD);
|
|
171
|
+
|
|
172
|
+
if (isUnderperformer) {
|
|
173
|
+
deprecated.push({
|
|
174
|
+
candidateId: candidate.id,
|
|
175
|
+
status: "deprecated",
|
|
176
|
+
evalResult,
|
|
177
|
+
signOff: null,
|
|
178
|
+
promotedAt: null,
|
|
179
|
+
deprecatedAt: nowDate ?? new Date(),
|
|
180
|
+
reason: `auto-deprecated: delta=${(
|
|
181
|
+
evalResult.deltaVsIncumbent * 100
|
|
182
|
+
).toFixed(1)}% p=${evalResult.pValue.toFixed(4)}`,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return deprecated;
|
|
188
|
+
}
|