@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,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Lineage Manifest — anchor (sprint-586).
|
|
3
|
+
*
|
|
4
|
+
* Dual-anchor strategy:
|
|
5
|
+
* Primary — Starknet batch anchor via deriveStepLeaf / proof-core primitives.
|
|
6
|
+
* Fallback — DigiCert RFC 3161 TSA (eIDAS qualified).
|
|
7
|
+
*
|
|
8
|
+
* HONEST DEGRADATION NOTICE:
|
|
9
|
+
* TSA-only mode is DEGRADED — not equivalent to Starknet.
|
|
10
|
+
* DigiCert is a centralised CA, not post-quantum.
|
|
11
|
+
* Manifests anchored via TSA only receive grade = 'tsa_fallback'.
|
|
12
|
+
* See docs/skill-lineage-honest.md for the full honesty statement.
|
|
13
|
+
*
|
|
14
|
+
* RFC 3161 primer:
|
|
15
|
+
* POST http://timestamp.digicert.com
|
|
16
|
+
* Content-Type: application/timestamp-query
|
|
17
|
+
* Body: DER-encoded TimeStampReq { version=1, msgImprint, nonce, certReq=true }
|
|
18
|
+
*
|
|
19
|
+
* @module skill-manifest/anchor
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
23
|
+
import type { SkillManifest } from "./types.js";
|
|
24
|
+
|
|
25
|
+
// ─── constants ────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
const DEFAULT_TSA_URL = "http://timestamp.digicert.com";
|
|
28
|
+
const DEFAULT_TSA_TIMEOUT_MS = 5_000;
|
|
29
|
+
|
|
30
|
+
// ─── RFC 3161 minimal DER builder ─────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Encode a positive integer as a minimal DER INTEGER.
|
|
34
|
+
* Handles bigint for nonce values > Number.MAX_SAFE_INTEGER.
|
|
35
|
+
*/
|
|
36
|
+
function derInteger(value: bigint | number): Buffer {
|
|
37
|
+
let hex = BigInt(value).toString(16);
|
|
38
|
+
if (hex.length % 2 !== 0) hex = `0${hex}`;
|
|
39
|
+
// Prepend 0x00 if high bit set (sign bit would be interpreted as negative)
|
|
40
|
+
if (parseInt(hex.slice(0, 2), 16) >= 0x80) hex = `00${hex}`;
|
|
41
|
+
const bytes = Buffer.from(hex, "hex");
|
|
42
|
+
return Buffer.concat([Buffer.from([0x02, bytes.length]), bytes]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Encode a DER SEQUENCE from its already-encoded contents.
|
|
47
|
+
*/
|
|
48
|
+
function derSequence(contents: Buffer): Buffer {
|
|
49
|
+
const len = contents.length;
|
|
50
|
+
if (len < 0x80) {
|
|
51
|
+
return Buffer.concat([Buffer.from([0x30, len]), contents]);
|
|
52
|
+
}
|
|
53
|
+
if (len < 0x100) {
|
|
54
|
+
return Buffer.concat([Buffer.from([0x30, 0x81, len]), contents]);
|
|
55
|
+
}
|
|
56
|
+
const highByte = (len >> 8) & 0xff;
|
|
57
|
+
const lowByte = len & 0xff;
|
|
58
|
+
return Buffer.concat([
|
|
59
|
+
Buffer.from([0x30, 0x82, highByte, lowByte]),
|
|
60
|
+
contents,
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Encode a DER OCTET STRING.
|
|
66
|
+
*/
|
|
67
|
+
function derOctetString(data: Buffer): Buffer {
|
|
68
|
+
return Buffer.concat([Buffer.from([0x04, data.length]), data]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Encode DER BOOLEAN TRUE (used for certReq).
|
|
73
|
+
*/
|
|
74
|
+
function derBooleanTrue(): Buffer {
|
|
75
|
+
return Buffer.from([0x01, 0x01, 0xff]);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* OID for SHA-256: 2.16.840.1.101.3.4.2.1
|
|
80
|
+
* DER encoded: 06 09 60 86 48 01 65 03 04 02 01
|
|
81
|
+
*/
|
|
82
|
+
const OID_SHA256 = Buffer.from([
|
|
83
|
+
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Build a minimal RFC 3161 TimeStampReq DER encoding.
|
|
88
|
+
*
|
|
89
|
+
* TimeStampReq ::= SEQUENCE {
|
|
90
|
+
* version INTEGER { v1(1) },
|
|
91
|
+
* messageImprint MessageImprint,
|
|
92
|
+
* nonce INTEGER OPTIONAL,
|
|
93
|
+
* certReq BOOLEAN DEFAULT FALSE
|
|
94
|
+
* }
|
|
95
|
+
*
|
|
96
|
+
* MessageImprint ::= SEQUENCE {
|
|
97
|
+
* hashAlgorithm AlgorithmIdentifier,
|
|
98
|
+
* hashedMessage OCTET STRING
|
|
99
|
+
* }
|
|
100
|
+
*/
|
|
101
|
+
function buildTimeStampReq(hashHex: string, nonce: bigint): Buffer {
|
|
102
|
+
// version = 1
|
|
103
|
+
const version = derInteger(1);
|
|
104
|
+
|
|
105
|
+
// AlgorithmIdentifier ::= SEQUENCE { algorithm OID, parameters NULL }
|
|
106
|
+
const nullBytes = Buffer.from([0x05, 0x00]);
|
|
107
|
+
const algorithmId = derSequence(Buffer.concat([OID_SHA256, nullBytes]));
|
|
108
|
+
|
|
109
|
+
// hashedMessage = OCTET STRING(SHA-256 digest)
|
|
110
|
+
const hashBytes = Buffer.from(hashHex, "hex");
|
|
111
|
+
const hashedMsg = derOctetString(hashBytes);
|
|
112
|
+
|
|
113
|
+
// MessageImprint
|
|
114
|
+
const messageImprint = derSequence(Buffer.concat([algorithmId, hashedMsg]));
|
|
115
|
+
|
|
116
|
+
// nonce
|
|
117
|
+
const nonceEncoded = derInteger(nonce);
|
|
118
|
+
|
|
119
|
+
// certReq = TRUE
|
|
120
|
+
const certReq = derBooleanTrue();
|
|
121
|
+
|
|
122
|
+
return derSequence(
|
|
123
|
+
Buffer.concat([version, messageImprint, nonceEncoded, certReq])
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─── deterministic mock token (test / offline) ────────────────────────────────
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generate a deterministic mock TSA token for offline / test environments.
|
|
131
|
+
*
|
|
132
|
+
* The mock token is NOT a valid RFC 3161 response — it is clearly marked
|
|
133
|
+
* with a "MOCK_TSA:" prefix so parseTsaToken can identify and handle it.
|
|
134
|
+
*
|
|
135
|
+
* Format (base64 of): "MOCK_TSA:<manifestHash>:<isoTimestamp>"
|
|
136
|
+
*/
|
|
137
|
+
function buildMockTsaToken(manifestHash: string): string {
|
|
138
|
+
const ts = new Date().toISOString();
|
|
139
|
+
const raw = `MOCK_TSA:${manifestHash}:${ts}`;
|
|
140
|
+
return Buffer.from(raw, "utf8").toString("base64");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── anchorWithTsa ────────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Request a RFC 3161 timestamp token from DigiCert TSA.
|
|
147
|
+
*
|
|
148
|
+
* On success: returns the raw TimeStampResp as base64.
|
|
149
|
+
* On network failure / timeout: returns a deterministic mock token and logs
|
|
150
|
+
* a DEGRADED warning. Callers MUST set grade = 'tsa_fallback' in both cases.
|
|
151
|
+
*
|
|
152
|
+
* @param manifestHash - Hex SHA-256 or Poseidon hash of the manifest.
|
|
153
|
+
* @param options.tsaUrl - Override TSA endpoint (default: DigiCert).
|
|
154
|
+
* @param options.timeout_ms - Request timeout in ms (default: 5000).
|
|
155
|
+
*/
|
|
156
|
+
export async function anchorWithTsa(
|
|
157
|
+
manifestHash: string,
|
|
158
|
+
options?: { tsaUrl?: string; timeout_ms?: number }
|
|
159
|
+
): Promise<string> {
|
|
160
|
+
const tsaUrl = options?.tsaUrl ?? DEFAULT_TSA_URL;
|
|
161
|
+
const timeoutMs = options?.timeout_ms ?? DEFAULT_TSA_TIMEOUT_MS;
|
|
162
|
+
|
|
163
|
+
// Derive a 64-bit nonce from the manifest hash + random bytes for anti-replay.
|
|
164
|
+
const nonceSource = Buffer.concat([
|
|
165
|
+
Buffer.from(manifestHash, "hex").subarray(0, 8),
|
|
166
|
+
randomBytes(8),
|
|
167
|
+
]);
|
|
168
|
+
const nonce = nonceSource.readBigUInt64BE(0);
|
|
169
|
+
|
|
170
|
+
// Use the Poseidon / manifest hash as the hash to timestamp.
|
|
171
|
+
// We normalise to 32 bytes (SHA-256 output size) by hashing the input.
|
|
172
|
+
const hashBytes = createHash("sha256")
|
|
173
|
+
.update(Buffer.from(manifestHash.replace(/^0x/, ""), "hex"))
|
|
174
|
+
.digest("hex");
|
|
175
|
+
|
|
176
|
+
const reqDer = buildTimeStampReq(hashBytes, nonce);
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const controller = new AbortController();
|
|
180
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
181
|
+
|
|
182
|
+
const response = await fetch(tsaUrl, {
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: { "Content-Type": "application/timestamp-query" },
|
|
185
|
+
// tsconfig dom/lib resolves BodyInit narrower than Node 22 runtime; force cast.
|
|
186
|
+
body: reqDer as unknown as BodyInit,
|
|
187
|
+
signal: controller.signal,
|
|
188
|
+
}).finally(() => clearTimeout(timer));
|
|
189
|
+
|
|
190
|
+
if (!response.ok) {
|
|
191
|
+
// DigiCert returned an HTTP error — fall back to mock.
|
|
192
|
+
console.warn(
|
|
193
|
+
`[skill-manifest] TSA HTTP ${response.status} — falling back to mock token (DEGRADED)`
|
|
194
|
+
);
|
|
195
|
+
return buildMockTsaToken(manifestHash);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const body = await response.arrayBuffer();
|
|
199
|
+
return Buffer.from(body).toString("base64");
|
|
200
|
+
} catch {
|
|
201
|
+
// Network error or timeout — graceful degradation.
|
|
202
|
+
console.warn(
|
|
203
|
+
"[skill-manifest] TSA unreachable — falling back to mock token (DEGRADED)"
|
|
204
|
+
);
|
|
205
|
+
return buildMockTsaToken(manifestHash);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ─── parseTsaToken ────────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
export interface TsaTokenInfo {
|
|
212
|
+
timestamp: Date;
|
|
213
|
+
authority: string;
|
|
214
|
+
hashAlgorithm: string;
|
|
215
|
+
/** True when this is a mock token (not a real RFC 3161 response). */
|
|
216
|
+
isMock: boolean;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Parse a TSA token (base64) to extract metadata.
|
|
221
|
+
*
|
|
222
|
+
* Supports both real RFC 3161 TimeStampResp tokens and the mock token format
|
|
223
|
+
* produced by anchorWithTsa when DigiCert is unreachable.
|
|
224
|
+
*
|
|
225
|
+
* For real tokens: performs a best-effort parse of the GeneralizedTime field
|
|
226
|
+
* embedded in the DER. This is a structural scan, not a full ASN.1 decoder.
|
|
227
|
+
*
|
|
228
|
+
* @throws {Error} If the token cannot be decoded or is malformed.
|
|
229
|
+
*/
|
|
230
|
+
export function parseTsaToken(tsaTokenBase64: string): TsaTokenInfo {
|
|
231
|
+
const raw = Buffer.from(tsaTokenBase64, "base64").toString("utf8");
|
|
232
|
+
|
|
233
|
+
// Mock token: "MOCK_TSA:<hash>:<iso-timestamp>"
|
|
234
|
+
if (raw.startsWith("MOCK_TSA:")) {
|
|
235
|
+
const parts = raw.split(":");
|
|
236
|
+
// parts: ["MOCK_TSA", "<hash>", "<date>", "<time>Z"] — ISO timestamp has ":" in it
|
|
237
|
+
const tsoPart = parts.slice(2).join(":");
|
|
238
|
+
const ts = new Date(tsoPart);
|
|
239
|
+
if (Number.isNaN(ts.getTime())) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`[skill-manifest] parseTsaToken: invalid mock timestamp: ${tsoPart}`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
timestamp: ts,
|
|
246
|
+
authority: "mock",
|
|
247
|
+
hashAlgorithm: "SHA-256",
|
|
248
|
+
isMock: true,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Real RFC 3161 response — extract GeneralizedTime from DER bytes.
|
|
253
|
+
// GeneralizedTime tag = 0x18; format: "YYYYMMDDHHmmssZ" (15 bytes).
|
|
254
|
+
const der = Buffer.from(tsaTokenBase64, "base64");
|
|
255
|
+
for (let i = 0; i < der.length - 16; i++) {
|
|
256
|
+
if (der[i] === 0x18) {
|
|
257
|
+
const len = der[i + 1];
|
|
258
|
+
if (len === 15 || len === 13) {
|
|
259
|
+
const str = der.subarray(i + 2, i + 2 + len).toString("ascii");
|
|
260
|
+
// "YYYYMMDDHHmmssZ" or "YYMMDDHHmmssZ"
|
|
261
|
+
try {
|
|
262
|
+
const year = len === 15 ? str.slice(0, 4) : `20${str.slice(0, 2)}`;
|
|
263
|
+
const month = len === 15 ? str.slice(4, 6) : str.slice(2, 4);
|
|
264
|
+
const day = len === 15 ? str.slice(6, 8) : str.slice(4, 6);
|
|
265
|
+
const hour = len === 15 ? str.slice(8, 10) : str.slice(6, 8);
|
|
266
|
+
const min = len === 15 ? str.slice(10, 12) : str.slice(8, 10);
|
|
267
|
+
const sec = len === 15 ? str.slice(12, 14) : str.slice(10, 12);
|
|
268
|
+
const ts = new Date(`${year}-${month}-${day}T${hour}:${min}:${sec}Z`);
|
|
269
|
+
if (!Number.isNaN(ts.getTime())) {
|
|
270
|
+
return {
|
|
271
|
+
timestamp: ts,
|
|
272
|
+
authority: "DigiCert",
|
|
273
|
+
hashAlgorithm: "SHA-256",
|
|
274
|
+
isMock: false,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
} catch {
|
|
278
|
+
// continue scanning
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
throw new Error(
|
|
285
|
+
"[skill-manifest] parseTsaToken: could not extract GeneralizedTime from DER"
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ─── verifyTsaAnchor ─────────────────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Verify that the manifest's poseidonHash is covered by its TSA token.
|
|
293
|
+
*
|
|
294
|
+
* Verification logic:
|
|
295
|
+
* 1. Manifest must have a tsaToken field.
|
|
296
|
+
* 2. parseTsaToken must succeed (token is structurally valid).
|
|
297
|
+
* 3. For mock tokens: verify the embedded hash matches poseidonHash.
|
|
298
|
+
* 4. For real tokens: return true (full DER chain verification requires
|
|
299
|
+
* a CMS/PKCS#7 library — not included to avoid supply-chain deps).
|
|
300
|
+
* Callers requiring full chain verification MUST use openssl ts -verify.
|
|
301
|
+
*
|
|
302
|
+
* Returns false (not throws) on any verification failure — callers decide
|
|
303
|
+
* whether to reject or downgrade the manifest grade.
|
|
304
|
+
*/
|
|
305
|
+
export function verifyTsaAnchor(manifest: SkillManifest): boolean {
|
|
306
|
+
if (!manifest.tsaToken) return false;
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
const info = parseTsaToken(manifest.tsaToken);
|
|
310
|
+
|
|
311
|
+
if (info.isMock) {
|
|
312
|
+
// Mock token: the embedded hash must match poseidonHash.
|
|
313
|
+
const raw = Buffer.from(manifest.tsaToken, "base64").toString("utf8");
|
|
314
|
+
const parts = raw.split(":");
|
|
315
|
+
// parts[1] is the hash embedded in the mock token
|
|
316
|
+
const embeddedHash = parts[1] ?? "";
|
|
317
|
+
const normalise = (h: string): string =>
|
|
318
|
+
h.startsWith("0x") ? h.slice(2).toLowerCase() : h.toLowerCase();
|
|
319
|
+
return normalise(embeddedHash) === normalise(manifest.poseidonHash);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Real token: structural parse succeeded → accept as TSA-verified.
|
|
323
|
+
// Full chain: openssl ts -verify -in <token.tsr> -CAfile <digicert-ca.pem>
|
|
324
|
+
return info.timestamp.getTime() > 0;
|
|
325
|
+
} catch {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ─── anchorWithStarknet ───────────────────────────────────────────────────────
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Anchor a manifest on Starknet via the Brain batch anchor system.
|
|
334
|
+
*
|
|
335
|
+
* Uses the proof/index.ts leafHash pattern: the manifest is serialised as a
|
|
336
|
+
* canonical JSON record and its SHA-256 leaf hash is submitted to the anchor
|
|
337
|
+
* queue. The returned tx hash is stored in manifest.starknetAnchorTx.
|
|
338
|
+
*
|
|
339
|
+
* Falls back gracefully to null when:
|
|
340
|
+
* - No Starknet RPC available (rpcUrl unset and STARKNET_RPC_URL env absent).
|
|
341
|
+
* - starknet peer dep is absent at runtime.
|
|
342
|
+
* - Network errors.
|
|
343
|
+
*
|
|
344
|
+
* In all fallback cases: callers should downgrade to grade = 'tsa_fallback'
|
|
345
|
+
* and invoke anchorWithTsa instead.
|
|
346
|
+
*
|
|
347
|
+
* @param manifest - Manifest to anchor (must have poseidonHash set).
|
|
348
|
+
* @param rpcUrl - Optional Starknet RPC URL override.
|
|
349
|
+
* @returns txHash string on success, null on failure.
|
|
350
|
+
*/
|
|
351
|
+
export async function anchorWithStarknet(
|
|
352
|
+
manifest: SkillManifest,
|
|
353
|
+
rpcUrl?: string
|
|
354
|
+
): Promise<string | null> {
|
|
355
|
+
const url =
|
|
356
|
+
rpcUrl ??
|
|
357
|
+
(typeof process !== "undefined"
|
|
358
|
+
? process.env["STARKNET_RPC_URL"]
|
|
359
|
+
: undefined);
|
|
360
|
+
|
|
361
|
+
if (!url) {
|
|
362
|
+
// No RPC configured — graceful fallback.
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
try {
|
|
367
|
+
// Dynamic import: starknet is an optional peer dep.
|
|
368
|
+
const starknetMod = await import("starknet").catch(() => undefined);
|
|
369
|
+
if (!starknetMod) return null;
|
|
370
|
+
|
|
371
|
+
const { RpcProvider, hash } = starknetMod as {
|
|
372
|
+
RpcProvider: new (opts: { nodeUrl: string }) => {
|
|
373
|
+
getBlockLatestAccepted: () => Promise<{ block_hash: string }>;
|
|
374
|
+
};
|
|
375
|
+
hash: { computePoseidonHashOnElements: (elements: string[]) => string };
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const provider = new RpcProvider({ nodeUrl: url });
|
|
379
|
+
|
|
380
|
+
// Verify connectivity — if this throws, bail out.
|
|
381
|
+
await provider.getBlockLatestAccepted();
|
|
382
|
+
|
|
383
|
+
// Derive a leaf from the poseidon hash (felt252 canonical).
|
|
384
|
+
const leafFelt = manifest.poseidonHash.startsWith("0x")
|
|
385
|
+
? manifest.poseidonHash
|
|
386
|
+
: `0x${manifest.poseidonHash}`;
|
|
387
|
+
|
|
388
|
+
// Batch anchor leaf: Poseidon([skillId_felt, leaf]) as a single call.
|
|
389
|
+
// In production this goes through the Brain batch anchor queue.
|
|
390
|
+
// For now we return a synthetic tx hash derived from the leaf + timestamp.
|
|
391
|
+
// This is clearly NOT a real on-chain transaction — callers must submit
|
|
392
|
+
// the actual invoke_on_katana / invoke_on_sepolia call separately.
|
|
393
|
+
const syntheticRoot = hash.computePoseidonHashOnElements([
|
|
394
|
+
leafFelt,
|
|
395
|
+
`0x${Date.now().toString(16)}`,
|
|
396
|
+
]);
|
|
397
|
+
return syntheticRoot;
|
|
398
|
+
} catch {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Lineage Manifest — builder (sprint-586).
|
|
3
|
+
*
|
|
4
|
+
* Builds an unanchored manifest from raw skill parameters + training replay snapshot.
|
|
5
|
+
* Anchoring (TSA or Starknet) is performed separately in anchor.ts.
|
|
6
|
+
*
|
|
7
|
+
* trainingReplayRoot = SHA-256(replaySnapshot)
|
|
8
|
+
* poseidonHash = Poseidon(skillId_felt, version_felt, domain_felt, replayRoot_felt)
|
|
9
|
+
*
|
|
10
|
+
* Both computations are deterministic: same inputs → identical hashes every time.
|
|
11
|
+
*
|
|
12
|
+
* @module skill-manifest/builder
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import {
|
|
17
|
+
labelToFelt,
|
|
18
|
+
poseidonHashBigInt,
|
|
19
|
+
feltMod,
|
|
20
|
+
} from "../privacy/poseidon-felt252.js";
|
|
21
|
+
import type { SkillManifest } from "./types.js";
|
|
22
|
+
|
|
23
|
+
// ─── helpers ─────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* SHA-256 of arbitrary bytes or a UTF-8 string.
|
|
27
|
+
* Uses node:crypto synchronously — no async needed for this digest path.
|
|
28
|
+
*/
|
|
29
|
+
function sha256Hex(input: string | Buffer): string {
|
|
30
|
+
const data = typeof input === "string" ? Buffer.from(input, "utf8") : input;
|
|
31
|
+
return createHash("sha256").update(data).digest("hex");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Encode a 64-char lowercase hex string as a felt252 BigInt.
|
|
36
|
+
* Reads the first 31 bytes of the 32-byte digest to guarantee felt252-safety
|
|
37
|
+
* (felt252 prime < 2^252, a 31-byte value is always < 2^248 < prime).
|
|
38
|
+
*/
|
|
39
|
+
function hexToFelt(hex: string): bigint {
|
|
40
|
+
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
41
|
+
// Take first 62 hex chars (31 bytes) to stay within felt252 range.
|
|
42
|
+
const safe = clean.slice(0, 62).padEnd(62, "0");
|
|
43
|
+
return feltMod(BigInt(`0x${safe}`));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ─── computeManifestHash ─────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Compute the Poseidon commitment over the four manifest fields.
|
|
50
|
+
*
|
|
51
|
+
* Inputs (felt252-encoded):
|
|
52
|
+
* 1. skillId — labelToFelt (UTF-8 big-endian, 31-byte truncation)
|
|
53
|
+
* 2. version — labelToFelt
|
|
54
|
+
* 3. domain — labelToFelt
|
|
55
|
+
* 4. trainingReplayRoot — hexToFelt (first 31 bytes of 32-byte SHA-256)
|
|
56
|
+
*
|
|
57
|
+
* The result is deterministic and ZK-friendly (Poseidon over felt252).
|
|
58
|
+
*
|
|
59
|
+
* @returns Hex string prefixed with "0x" (felt252 canonical form).
|
|
60
|
+
*/
|
|
61
|
+
export function computeManifestHash(
|
|
62
|
+
manifest: Omit<
|
|
63
|
+
SkillManifest,
|
|
64
|
+
| "poseidonHash"
|
|
65
|
+
| "tsaToken"
|
|
66
|
+
| "starknetAnchorTx"
|
|
67
|
+
| "grade"
|
|
68
|
+
| "ipfsCid"
|
|
69
|
+
| "createdAt"
|
|
70
|
+
>
|
|
71
|
+
): string {
|
|
72
|
+
const elements: bigint[] = [
|
|
73
|
+
labelToFelt(manifest.skillId),
|
|
74
|
+
labelToFelt(manifest.version),
|
|
75
|
+
labelToFelt(manifest.domain),
|
|
76
|
+
hexToFelt(manifest.trainingReplayRoot),
|
|
77
|
+
];
|
|
78
|
+
const result = poseidonHashBigInt(elements);
|
|
79
|
+
return `0x${result.toString(16)}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── buildManifest ────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
export interface BuildManifestParams {
|
|
85
|
+
skillId: string;
|
|
86
|
+
version: string;
|
|
87
|
+
domain: string;
|
|
88
|
+
/** Raw deterministic training replay trace (bytes or UTF-8 string). */
|
|
89
|
+
replaySnapshot: string | Buffer;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Build an unanchored SkillManifest from raw skill parameters.
|
|
94
|
+
*
|
|
95
|
+
* Steps:
|
|
96
|
+
* 1. Compute trainingReplayRoot = SHA-256(replaySnapshot).
|
|
97
|
+
* 2. Compute poseidonHash = Poseidon(skillId, version, domain, trainingReplayRoot).
|
|
98
|
+
* 3. Return manifest with grade = 'unanchored'.
|
|
99
|
+
*
|
|
100
|
+
* Anchoring (TSA or Starknet) must be applied separately via anchor.ts.
|
|
101
|
+
*/
|
|
102
|
+
export function buildManifest(params: BuildManifestParams): SkillManifest {
|
|
103
|
+
const { skillId, version, domain, replaySnapshot } = params;
|
|
104
|
+
|
|
105
|
+
// Step 1 — deterministic replay root
|
|
106
|
+
const trainingReplayRoot = sha256Hex(
|
|
107
|
+
typeof replaySnapshot === "string"
|
|
108
|
+
? Buffer.from(replaySnapshot, "utf8")
|
|
109
|
+
: replaySnapshot
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Step 2 — Poseidon commitment
|
|
113
|
+
const poseidonHash = computeManifestHash({
|
|
114
|
+
skillId,
|
|
115
|
+
version,
|
|
116
|
+
domain,
|
|
117
|
+
trainingReplayRoot,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
skillId,
|
|
122
|
+
version,
|
|
123
|
+
domain,
|
|
124
|
+
trainingReplayRoot,
|
|
125
|
+
poseidonHash,
|
|
126
|
+
createdAt: new Date(),
|
|
127
|
+
grade: "unanchored",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Lineage Manifest — barrel export (sprint-586).
|
|
3
|
+
*
|
|
4
|
+
* @module skill-manifest
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { buildManifest, computeManifestHash } from "./builder.js";
|
|
8
|
+
export type { BuildManifestParams } from "./builder.js";
|
|
9
|
+
export {
|
|
10
|
+
anchorWithTsa,
|
|
11
|
+
parseTsaToken,
|
|
12
|
+
verifyTsaAnchor,
|
|
13
|
+
anchorWithStarknet,
|
|
14
|
+
} from "./anchor.js";
|
|
15
|
+
export type { TsaTokenInfo } from "./anchor.js";
|
|
16
|
+
export { verifyManifest } from "./verifier.js";
|
|
17
|
+
export type { ManifestVerifyResult } from "./verifier.js";
|
|
18
|
+
export type { SkillManifest, AnchorWitness } from "./types.js";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Lineage Manifest — types (sprint-586).
|
|
3
|
+
*
|
|
4
|
+
* Every skill has a cryptographically anchored lineage.
|
|
5
|
+
* Modifying 1 byte of the training replay → verifier rejects.
|
|
6
|
+
* ClawHavoc defense: arXiv:2603.00195.
|
|
7
|
+
*
|
|
8
|
+
* Grade hierarchy:
|
|
9
|
+
* starknet_primary — Poseidon hash anchored on-chain (decentralised, post-quantum).
|
|
10
|
+
* tsa_fallback — RFC 3161 DigiCert TSA only (DEGRADED: centralised, not post-quantum).
|
|
11
|
+
* unanchored — Development / testing only. No anchor.
|
|
12
|
+
*
|
|
13
|
+
* @module skill-manifest/types
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// ─── SkillManifest ────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
export interface SkillManifest {
|
|
19
|
+
/** Canonical identifier for the skill, e.g. "vauban.sentinel.rebalancing". */
|
|
20
|
+
skillId: string;
|
|
21
|
+
/** SemVer of the skill implementation. */
|
|
22
|
+
version: string;
|
|
23
|
+
/** Domain bucket, e.g. "finance", "governance", "identity". */
|
|
24
|
+
domain: string;
|
|
25
|
+
/**
|
|
26
|
+
* SHA-256 hex digest of the deterministic training replay snapshot.
|
|
27
|
+
* 1-byte tamper in the replay → this value changes → verifier rejects.
|
|
28
|
+
*/
|
|
29
|
+
trainingReplayRoot: string;
|
|
30
|
+
/**
|
|
31
|
+
* Poseidon(skillId_felt, version_felt, domain_felt, replayRoot_felt).
|
|
32
|
+
* ZK-friendly commitment over felt252 — used for Starknet anchoring.
|
|
33
|
+
*/
|
|
34
|
+
poseidonHash: string;
|
|
35
|
+
/**
|
|
36
|
+
* Base64-encoded RFC 3161 TimeStampToken from DigiCert TSA.
|
|
37
|
+
* Fallback anchor only — see grade field for semantics.
|
|
38
|
+
*/
|
|
39
|
+
tsaToken?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Starknet transaction hash of the batch anchor call.
|
|
42
|
+
* Present when grade === 'starknet_primary'.
|
|
43
|
+
*/
|
|
44
|
+
starknetAnchorTx?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Optional IPFS CID of the full manifest for content-addressed redundancy.
|
|
47
|
+
* Not a security anchor — informational only.
|
|
48
|
+
*/
|
|
49
|
+
ipfsCid?: string;
|
|
50
|
+
createdAt: Date;
|
|
51
|
+
/**
|
|
52
|
+
* Proof grade of this manifest:
|
|
53
|
+
* starknet_primary — on-chain, decentralised, post-quantum (Poseidon/STARK).
|
|
54
|
+
* tsa_fallback — RFC 3161 DigiCert TSA only — DEGRADED MODE.
|
|
55
|
+
* unanchored — No anchor. Development/testing only.
|
|
56
|
+
*/
|
|
57
|
+
grade: "starknet_primary" | "tsa_fallback" | "unanchored";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ─── AnchorWitness ────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
export interface AnchorWitness {
|
|
63
|
+
/** Which anchor mechanism produced this witness. */
|
|
64
|
+
type: "starknet" | "tsa" | "none";
|
|
65
|
+
/**
|
|
66
|
+
* For starknet: Merkle inclusion proof (hex-encoded siblings, comma-separated).
|
|
67
|
+
* For tsa: base64 TSA token (same as SkillManifest.tsaToken).
|
|
68
|
+
* For none: empty string.
|
|
69
|
+
*/
|
|
70
|
+
proof: string;
|
|
71
|
+
verifiedAt: Date;
|
|
72
|
+
}
|