@openwop/openwop-conformance 1.163.0 → 2.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/cli.js +262 -14
- package/dist/lib/certification-bundle-v3.js +142 -0
- package/dist/lib/paths.js +25 -3
- package/dist/lib/requirement-ids.js +57 -4
- package/dist/lib/requirement-ledger.js +1 -0
- package/dist/lib/scenario-disposition.js +6 -1
- package/dist/lib/spec-coherence.js +13 -32
- package/dist/spec-artifacts.lock.json +5 -0
- package/package.json +11 -33
- package/requirements.json +22892 -11701
- package/scenario-majors.json +1493 -0
- package/schemas/CORPUS-STAMP.json +137 -5
- package/src/cli.ts +236 -19
- package/src/global-setup.ts +3 -2
- package/src/lib/certification-bundle-v3.ts +175 -0
- package/src/lib/corpus-stamp.ts +29 -1
- package/src/lib/driver.ts +9 -1
- package/src/lib/era2-seed.ts +197 -0
- package/src/lib/paths.ts +25 -3
- package/src/lib/requirement-ids.ts +54 -4
- package/src/lib/requirement-ledger.ts +7 -1
- package/src/lib/scenario-disposition.ts +6 -1
- package/src/lib/seams.ts +45 -0
- package/src/lib/spec-coherence.ts +15 -32
- package/src/lib/v2.ts +62 -0
- package/src/scenarios/a2a-1-0-agent-card.test.ts +17 -16
- package/src/scenarios/a2a-1-0-task-roundtrip.test.ts +11 -10
- package/src/scenarios/a2a-card-runtime-consistency.test.ts +18 -17
- package/src/scenarios/a2a-peer-authority.test.ts +7 -6
- package/src/scenarios/a2a-task-roundtrip.test.ts +30 -29
- package/src/scenarios/a2a-version-negotiation.test.ts +16 -14
- package/src/scenarios/a2ui-surface-degrades.test.ts +5 -3
- package/src/scenarios/a2ui-surface-delta-transport.test.ts +35 -33
- package/src/scenarios/a2ui-surface-replay.test.ts +6 -4
- package/src/scenarios/a2ui-surface-shape.test.ts +11 -10
- package/src/scenarios/a2ui-surface-version-refusal.test.ts +6 -4
- package/src/scenarios/a2ui-untrusted-blocks-approval.test.ts +5 -3
- package/src/scenarios/agent-capability-degraded-projection.test.ts +12 -11
- package/src/scenarios/agent-channel-dispatch.test.ts +18 -16
- package/src/scenarios/agent-deployment-lifecycle.test.ts +20 -19
- package/src/scenarios/agent-deployment-shape.test.ts +22 -24
- package/src/scenarios/agent-eval-run.test.ts +19 -18
- package/src/scenarios/agent-eval-suite-shape.test.ts +20 -22
- package/src/scenarios/agent-live-allowlist-enforced.test.ts +6 -5
- package/src/scenarios/agent-live-invocation-bracket.test.ts +13 -12
- package/src/scenarios/agent-live-runtime-shape.test.ts +10 -12
- package/src/scenarios/agent-live-structured-output.test.ts +7 -6
- package/src/scenarios/agent-loop-iteration-monotonic.test.ts +3 -3
- package/src/scenarios/agent-loop-stateful-resume.test.ts +2 -2
- package/src/scenarios/agent-loop-version5-shape.test.ts +4 -4
- package/src/scenarios/agent-loop-workspace-snapshot.test.ts +3 -3
- package/src/scenarios/agent-manifest-role-profile.test.ts +9 -9
- package/src/scenarios/agent-manifest-runtime.test.ts +13 -12
- package/src/scenarios/agent-org-chart-scoping.test.ts +14 -13
- package/src/scenarios/agent-org-chart-shape.test.ts +11 -13
- package/src/scenarios/agent-platform-aggregate-evidence.test.ts +5 -4
- package/src/scenarios/agent-platform-profile.test.ts +18 -19
- package/src/scenarios/agent-requires-capabilities-shape.test.ts +4 -4
- package/src/scenarios/agent-roster-attribution.test.ts +18 -17
- package/src/scenarios/agent-roster-shape.test.ts +17 -19
- package/src/scenarios/agent-verifier-shape.test.ts +11 -11
- package/src/scenarios/agentConfidenceEscalation.test.ts +2 -1
- package/src/scenarios/agentMemoryCrossTenantIsolation.test.ts +6 -5
- package/src/scenarios/agentMemoryRedactionContract.test.ts +2 -1
- package/src/scenarios/agentMemoryRoundTrip.test.ts +2 -1
- package/src/scenarios/agentMemoryTtlExpiry.test.ts +4 -3
- package/src/scenarios/agentMessageReducer.test.ts +2 -1
- package/src/scenarios/agentMetadata.test.ts +2 -1
- package/src/scenarios/agentPackCatalog.test.ts +23 -21
- package/src/scenarios/agentPackExport.test.ts +4 -2
- package/src/scenarios/agentPackHandoffSchemaValidation.test.ts +5 -4
- package/src/scenarios/agentPackInstall.test.ts +4 -2
- package/src/scenarios/agentPackProvenance.test.ts +2 -1
- package/src/scenarios/agentReasoningEvents.test.ts +3 -2
- package/src/scenarios/agentReasoningStreaming.test.ts +11 -10
- package/src/scenarios/agents-run-tool-allowlist.test.ts +14 -12
- package/src/scenarios/ai-envelope-shape.test.ts +31 -29
- package/src/scenarios/aiEnvelope.capBreached.test.ts +26 -24
- package/src/scenarios/aiEnvelope.contractRefusal.test.ts +36 -34
- package/src/scenarios/aiEnvelope.correlationReplay.test.ts +17 -15
- package/src/scenarios/aiEnvelope.redaction.test.ts +22 -20
- package/src/scenarios/aiEnvelope.schemaDrift.test.ts +18 -16
- package/src/scenarios/aiEnvelope.trustBoundaryPropagation.test.ts +38 -36
- package/src/scenarios/aiEnvelope.universalKinds.test.ts +31 -29
- package/src/scenarios/aiproviders-input-shape.test.ts +5 -5
- package/src/scenarios/aiproviders-realtimevoice-shape.test.ts +14 -16
- package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +6 -4
- package/src/scenarios/aiproviders-selfhosted-shape.test.ts +15 -17
- package/src/scenarios/aiproviders-speechsynth-shape.test.ts +6 -8
- package/src/scenarios/aiproviders-subscription-scope.test.ts +7 -8
- package/src/scenarios/anonymous-actor-audit-opaque.test.ts +9 -8
- package/src/scenarios/anonymous-actor-default-deny.test.ts +10 -9
- package/src/scenarios/anonymous-actor-egress-guarded.test.ts +6 -5
- package/src/scenarios/anonymous-actor-no-secret-reach.test.ts +7 -6
- package/src/scenarios/anonymous-actor-shape.test.ts +17 -19
- package/src/scenarios/anonymous-actor-write-gated.test.ts +9 -8
- package/src/scenarios/append-ordering.test.ts +7 -5
- package/src/scenarios/approval-gate-events.test.ts +4 -3
- package/src/scenarios/approval-gate-flow.test.ts +4 -3
- package/src/scenarios/approval-payload.test.ts +5 -4
- package/src/scenarios/artifact-auth.test.ts +7 -6
- package/src/scenarios/artifact-type-pack-install.test.ts +6 -6
- package/src/scenarios/artifact-type-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/artifact-type-registration-source.test.ts +20 -20
- package/src/scenarios/artifact-type-store-emission.test.ts +15 -18
- package/src/scenarios/artifact-type-store-without-render.test.ts +4 -4
- package/src/scenarios/audit-log-integrity.test.ts +8 -6
- package/src/scenarios/auth-api-key-rotation.test.ts +11 -9
- package/src/scenarios/auth-mtls.test.ts +22 -20
- package/src/scenarios/auth-oauth2-client-credentials.test.ts +16 -14
- package/src/scenarios/auth-oidc-user-bearer.test.ts +26 -24
- package/src/scenarios/auth-saml-profile.test.ts +14 -12
- package/src/scenarios/auth-scim-profile.test.ts +3 -2
- package/src/scenarios/auth-subject-link-key-class.test.ts +8 -7
- package/src/scenarios/auth-subject-link.test.ts +11 -10
- package/src/scenarios/auth.test.ts +5 -4
- package/src/scenarios/authorization-fail-closed.test.ts +3 -2
- package/src/scenarios/authorization-roles-shape.test.ts +9 -7
- package/src/scenarios/blob-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/blob-presign-expiry.test.ts +12 -10
- package/src/scenarios/blob-roundtrip.test.ts +0 -0
- package/src/scenarios/budget-enforcement.test.ts +15 -14
- package/src/scenarios/budget-policy-shape.test.ts +18 -19
- package/src/scenarios/bulk-cancel.test.ts +9 -8
- package/src/scenarios/byok-auth-modes.test.ts +13 -14
- package/src/scenarios/byok-roundtrip.test.ts +12 -10
- package/src/scenarios/cache-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/cache-ttl-expiry.test.ts +7 -5
- package/src/scenarios/callai-multimodal.test.ts +5 -3
- package/src/scenarios/cancellation.test.ts +6 -5
- package/src/scenarios/cap-breach.test.ts +15 -14
- package/src/scenarios/capability-arm-witness.test.ts +5 -3
- package/src/scenarios/certification-bundle-non-vacuous.test.ts +20 -19
- package/src/scenarios/certification-bundle-redaction.test.ts +11 -10
- package/src/scenarios/certification-bundle-v2.test.ts +11 -10
- package/src/scenarios/chain-compensation-expansion.test.ts +14 -13
- package/src/scenarios/chain-produced-var-roundtrip.test.ts +13 -14
- package/src/scenarios/chain-subchain-cycle-rejected.test.ts +9 -10
- package/src/scenarios/chain-subchain-fanout.test.ts +8 -9
- package/src/scenarios/chain-subchain-sibling.test.ts +14 -16
- package/src/scenarios/channel-presence-behavioral.test.ts +9 -7
- package/src/scenarios/channel-presence-shape.test.ts +11 -12
- package/src/scenarios/channel-ttl.test.ts +6 -5
- package/src/scenarios/chat-card-pack-execution.test.ts +4 -4
- package/src/scenarios/chat-card-pack-manifest-validation.test.ts +9 -8
- package/src/scenarios/commitment-fired.test.ts +6 -5
- package/src/scenarios/compensation-behavior.test.ts +24 -23
- package/src/scenarios/compensation-recovery.test.ts +28 -27
- package/src/scenarios/configurable-schema.test.ts +8 -6
- package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -5
- package/src/scenarios/conformance-execution-witness.test.ts +9 -8
- package/src/scenarios/connection-pack-apihosts.test.ts +19 -17
- package/src/scenarios/connection-pack-manifest-valid.test.ts +12 -11
- package/src/scenarios/connection-pack-no-credential-material.test.ts +8 -6
- package/src/scenarios/connection-pack-reach-exclusive.test.ts +5 -4
- package/src/scenarios/connection-pack-write-reconsent.test.ts +6 -4
- package/src/scenarios/connection-provider-resolution.test.ts +18 -16
- package/src/scenarios/connector-manifest-validity.test.ts +8 -7
- package/src/scenarios/context-budget-transcript-bound.test.ts +17 -15
- package/src/scenarios/context-summarization-replay.test.ts +14 -12
- package/src/scenarios/contract-provenance.test.ts +18 -18
- package/src/scenarios/conversation-turn-model-provenance-shape.test.ts +14 -15
- package/src/scenarios/conversationCapabilityNegotiation.test.ts +2 -1
- package/src/scenarios/conversationLifecycle.test.ts +2 -1
- package/src/scenarios/conversationReplayDeterminism.test.ts +4 -2
- package/src/scenarios/conversationVsLegacySuspend.test.ts +2 -1
- package/src/scenarios/core-standard-profile.test.ts +8 -9
- package/src/scenarios/cost-attribution.test.ts +23 -21
- package/src/scenarios/credential-payload-redaction.test.ts +4 -3
- package/src/scenarios/credentials-capability-shape.test.ts +5 -4
- package/src/scenarios/cross-engine-append-behavior.test.ts +11 -10
- package/src/scenarios/cross-engine-append-ordering.test.ts +4 -3
- package/src/scenarios/cross-host-ancestry-endpoint.test.ts +11 -10
- package/src/scenarios/cross-host-causation-shape.test.ts +7 -6
- package/src/scenarios/cross-workspace-isolation.test.ts +4 -3
- package/src/scenarios/data-residency-admission.test.ts +15 -14
- package/src/scenarios/deadletter-capability-shape.test.ts +6 -4
- package/src/scenarios/deadletter-retry-exhaustion.test.ts +4 -3
- package/src/scenarios/debug-bundle-truncation.test.ts +9 -7
- package/src/scenarios/debugBundle.test.ts +25 -23
- package/src/scenarios/discovery-families-view.test.ts +5 -4
- package/src/scenarios/discovery.test.ts +30 -29
- package/src/scenarios/dispatch-cross-worker-handoff.test.ts +10 -8
- package/src/scenarios/dispatch-fanout-parallel.test.ts +24 -22
- package/src/scenarios/dispatch-input-mapping.test.ts +12 -10
- package/src/scenarios/dispatch-output-mapping.test.ts +9 -7
- package/src/scenarios/dispatch-per-item-input.test.ts +22 -20
- package/src/scenarios/dispatchLoop.test.ts +3 -2
- package/src/scenarios/distillation-index-roundtrip.test.ts +3 -3
- package/src/scenarios/distillation-secret-carryforward.test.ts +2 -2
- package/src/scenarios/distillation-shape.test.ts +4 -4
- package/src/scenarios/distillation-stable-archive.test.ts +6 -6
- package/src/scenarios/distillation-token-budget.test.ts +5 -5
- package/src/scenarios/durability-poison-exhaustion.test.ts +7 -6
- package/src/scenarios/egress-audience-binding.test.ts +11 -10
- package/src/scenarios/egress-decision-content-free.test.ts +6 -5
- package/src/scenarios/egress-provenance-shape.test.ts +17 -18
- package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +27 -25
- package/src/scenarios/envelope-nl-to-format-engaged.test.ts +19 -17
- package/src/scenarios/envelope-reasoning-secret-redaction.test.ts +28 -26
- package/src/scenarios/envelope-reasoning-shape.test.ts +19 -17
- package/src/scenarios/envelope-recovery-applied.test.ts +24 -22
- package/src/scenarios/envelope-refusal-shape.test.ts +31 -29
- package/src/scenarios/envelope-rendering-hint.test.ts +5 -4
- package/src/scenarios/envelope-retry-attempted.test.ts +33 -31
- package/src/scenarios/envelope-retry-exhausted.test.ts +22 -20
- package/src/scenarios/envelope-tier-one-subset-static.test.ts +9 -7
- package/src/scenarios/envelope-truncated.test.ts +19 -17
- package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +19 -17
- package/src/scenarios/envelope-variant-discriminator-static.test.ts +4 -3
- package/src/scenarios/errors.test.ts +20 -19
- package/src/scenarios/eventOrdering.test.ts +16 -14
- package/src/scenarios/exec-not-protocol-tier.test.ts +5 -7
- package/src/scenarios/experimental-tier-shape.test.ts +17 -15
- package/src/scenarios/export-bundle-portability.test.ts +17 -17
- package/src/scenarios/failure-path.test.ts +7 -6
- package/src/scenarios/feedback-capability-shape.test.ts +4 -3
- package/src/scenarios/feedback-correction-redaction.test.ts +2 -1
- package/src/scenarios/feedback-cross-tenant-isolation.test.ts +2 -1
- package/src/scenarios/feedback-fork-not-copied.test.ts +3 -2
- package/src/scenarios/feedback-on-terminal-run.test.ts +3 -2
- package/src/scenarios/feedback-record-and-list.test.ts +2 -1
- package/src/scenarios/feedback-unsupported-501.test.ts +2 -1
- package/src/scenarios/fixtures-gating.test.ts +29 -28
- package/src/scenarios/fixtures-valid.test.ts +16 -15
- package/src/scenarios/form-content-instantiation.test.ts +15 -14
- package/src/scenarios/frontend-plugin-packs.test.ts +29 -27
- package/src/scenarios/fs-path-traversal.test.ts +14 -12
- package/src/scenarios/goal-standing-continuation.test.ts +21 -21
- package/src/scenarios/grpc-transport.test.ts +9 -7
- package/src/scenarios/heartbeat-capability-shape.test.ts +3 -3
- package/src/scenarios/heartbeat-fires-once-per-tick.test.ts +2 -2
- package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +3 -3
- package/src/scenarios/heartbeat-runtime-bound.test.ts +2 -2
- package/src/scenarios/highConcurrency.test.ts +14 -13
- package/src/scenarios/host-callback-declaration.test.ts +8 -7
- package/src/scenarios/http-client-ssrf.test.ts +7 -5
- package/src/scenarios/i18n-negotiation.test.ts +14 -12
- package/src/scenarios/idempotency-concurrent-claim.test.ts +14 -13
- package/src/scenarios/idempotency-key-determinism.test.ts +20 -18
- package/src/scenarios/idempotency.test.ts +8 -7
- package/src/scenarios/idempotencyRetry.test.ts +13 -11
- package/src/scenarios/identity-owner-shape.test.ts +6 -5
- package/src/scenarios/identity-passthrough.test.ts +4 -3
- package/src/scenarios/interrupt-approval.test.ts +6 -5
- package/src/scenarios/interrupt-approver-routing.test.ts +15 -15
- package/src/scenarios/interrupt-auth-required-resume.test.ts +5 -3
- package/src/scenarios/interrupt-clarification.test.ts +4 -3
- package/src/scenarios/interrupt-external-event-correlation.test.ts +6 -5
- package/src/scenarios/interrupt-parent-child-cascade.test.ts +4 -3
- package/src/scenarios/interrupt-quorum-resolution.test.ts +5 -4
- package/src/scenarios/interrupt-token-matrix.test.ts +8 -6
- package/src/scenarios/interruptRace.test.ts +12 -10
- package/src/scenarios/kv-atomic-increment.test.ts +11 -9
- package/src/scenarios/kv-cas.test.ts +14 -12
- package/src/scenarios/kv-cross-tenant-isolation.test.ts +12 -10
- package/src/scenarios/kv-ttl-expiry.test.ts +7 -5
- package/src/scenarios/localized-content-delivery.test.ts +24 -26
- package/src/scenarios/maliciousManifest.test.ts +10 -8
- package/src/scenarios/mcp-2026-07-28-discover.test.ts +15 -14
- package/src/scenarios/mcp-cache-tenant-scope.test.ts +6 -5
- package/src/scenarios/mcp-current-auth-boundary.test.ts +7 -6
- package/src/scenarios/mcp-discoverability.test.ts +9 -7
- package/src/scenarios/mcp-extension-opacity.test.ts +7 -6
- package/src/scenarios/mcp-mrtr-roundtrip.test.ts +18 -17
- package/src/scenarios/mcp-server-elicitation-bridge.test.ts +13 -12
- package/src/scenarios/mcp-server-prompt-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-resource-roundtrip.test.ts +11 -10
- package/src/scenarios/mcp-server-sampling-bridge.test.ts +12 -11
- package/src/scenarios/mcp-server-tool-roundtrip.test.ts +14 -13
- package/src/scenarios/mcp-server-untrusted-args.test.ts +14 -13
- package/src/scenarios/mcp-stateless-request.test.ts +11 -10
- package/src/scenarios/mcp-tool-roundtrip.test.ts +4 -3
- package/src/scenarios/mcp-toolcall-redaction.test.ts +4 -3
- package/src/scenarios/mcp-version-negotiation.test.ts +15 -14
- package/src/scenarios/media-url-inline-cap.test.ts +21 -19
- package/src/scenarios/memory-attribution-emits-on-write.test.ts +5 -5
- package/src/scenarios/memory-attribution-no-content.test.ts +4 -4
- package/src/scenarios/memory-attribution-replay-stable.test.ts +4 -3
- package/src/scenarios/memory-attribution-shape.test.ts +4 -3
- package/src/scenarios/memory-attribution-tenant-scoped.test.ts +3 -3
- package/src/scenarios/memory-capability-model-shape.test.ts +14 -15
- package/src/scenarios/memory-compaction-event-emitted.test.ts +7 -6
- package/src/scenarios/memory-compaction-provenance-tag.test.ts +3 -2
- package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +7 -6
- package/src/scenarios/memory-consolidation-idempotent.test.ts +5 -4
- package/src/scenarios/memory-consolidation-shape.test.ts +9 -11
- package/src/scenarios/memory-degraded-projection.test.ts +10 -9
- package/src/scenarios/memory-injection-budget.test.ts +15 -14
- package/src/scenarios/metric-emission.test.ts +4 -3
- package/src/scenarios/model-capability-insufficient.test.ts +19 -17
- package/src/scenarios/model-capability-substituted.test.ts +20 -18
- package/src/scenarios/multi-agent-confidence-escalation.test.ts +12 -11
- package/src/scenarios/multi-agent-handoff-state-machine.test.ts +10 -9
- package/src/scenarios/multi-agent-memory-lifecycle.test.ts +11 -10
- package/src/scenarios/multi-node-ordering.test.ts +4 -3
- package/src/scenarios/multi-party-conversation-behavioral.test.ts +6 -6
- package/src/scenarios/multi-party-conversation-shape.test.ts +21 -23
- package/src/scenarios/multi-region-idempotency-behavior.test.ts +16 -15
- package/src/scenarios/multi-region-idempotency.test.ts +6 -5
- package/src/scenarios/node-failed-payload-shape.test.ts +5 -4
- package/src/scenarios/node-module-required-capabilities-shape.test.ts +10 -8
- package/src/scenarios/oauth-authorization-code-roundtrip.test.ts +5 -4
- package/src/scenarios/oauth-capability-shape.test.ts +5 -4
- package/src/scenarios/oauth-connector-redaction.test.ts +4 -3
- package/src/scenarios/openapi-resolved-paths.test.ts +6 -5
- package/src/scenarios/orchestratorConservativePath.test.ts +2 -1
- package/src/scenarios/orchestratorDispatch.test.ts +2 -1
- package/src/scenarios/orchestratorTermination.test.ts +2 -1
- package/src/scenarios/org-position-no-authority-escalation.test.ts +2 -2
- package/src/scenarios/otel-collector-canary-inspection.test.ts +8 -7
- package/src/scenarios/otel-emission-grpc.test.ts +3 -2
- package/src/scenarios/otel-emission.test.ts +6 -5
- package/src/scenarios/otel-trace-propagation-subworkflow.test.ts +7 -6
- package/src/scenarios/otel-trace-propagation.test.ts +2 -1
- package/src/scenarios/owner-subject-echo.test.ts +13 -12
- package/src/scenarios/owner-subject-shape.test.ts +11 -10
- package/src/scenarios/pack-manifest-extension-opacity.test.ts +8 -8
- package/src/scenarios/pack-registry-isolation.test.ts +4 -3
- package/src/scenarios/pack-registry-publish.test.ts +65 -63
- package/src/scenarios/pack-registry.test.ts +38 -36
- package/src/scenarios/pause-resume.test.ts +18 -16
- package/src/scenarios/policies.test.ts +20 -18
- package/src/scenarios/production-backpressure.test.ts +18 -16
- package/src/scenarios/production-retention-expiry.test.ts +8 -6
- package/src/scenarios/profile-discovery-core-alias.test.ts +13 -11
- package/src/scenarios/profileDerivation.test.ts +34 -33
- package/src/scenarios/prompt-all-four-kinds-events.test.ts +11 -9
- package/src/scenarios/prompt-composed-secret-redaction.test.ts +11 -9
- package/src/scenarios/prompt-composed-trust-marker.test.ts +10 -8
- package/src/scenarios/prompt-end-to-end-events.test.ts +17 -15
- package/src/scenarios/prompt-list-and-fetch.test.ts +16 -14
- package/src/scenarios/prompt-mutable-lifecycle.test.ts +13 -11
- package/src/scenarios/prompt-mutation-workspace-membership-enforced.test.ts +6 -4
- package/src/scenarios/prompt-pack-install.test.ts +14 -12
- package/src/scenarios/prompt-prefix-cache.test.ts +19 -17
- package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +11 -9
- package/src/scenarios/prompt-render-deterministic.test.ts +19 -17
- package/src/scenarios/prompt-resolution-chain-agent-intrinsic.test.ts +7 -5
- package/src/scenarios/prompt-resolution-chain-event.test.ts +12 -10
- package/src/scenarios/prompt-resolution-chain-fallback-cascade.test.ts +10 -8
- package/src/scenarios/prompt-resolution-chain-node-wins.test.ts +8 -6
- package/src/scenarios/prompt-template-shape.test.ts +30 -28
- package/src/scenarios/proposal-reviewable-learning.test.ts +19 -19
- package/src/scenarios/protocol-versions-array.test.ts +10 -9
- package/src/scenarios/provider-usage.test.ts +17 -15
- package/src/scenarios/providerPolicyEnforcement.test.ts +16 -14
- package/src/scenarios/purpose-propagation.test.ts +23 -21
- package/src/scenarios/queue-ack-nack-dlq.test.ts +12 -10
- package/src/scenarios/queue-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/queue-publish-consume-roundtrip.test.ts +10 -8
- package/src/scenarios/rate-limit-envelope.test.ts +6 -5
- package/src/scenarios/redaction.test.ts +13 -11
- package/src/scenarios/redactionAdversarial.test.ts +11 -9
- package/src/scenarios/registry-public.test.ts +12 -11
- package/src/scenarios/replay-divergence-at-refusal.test.ts +16 -15
- package/src/scenarios/replay-fanout-suppression.test.ts +16 -15
- package/src/scenarios/replay-fork-arbitrary.test.ts +24 -23
- package/src/scenarios/replay-fork.test.ts +20 -19
- package/src/scenarios/replay-llm-cache-key-portable.test.ts +11 -9
- package/src/scenarios/replay-llm-cache-key.test.ts +22 -21
- package/src/scenarios/replay-observable-sequence-determinism.test.ts +4 -3
- package/src/scenarios/replay-retention-expiry.test.ts +11 -9
- package/src/scenarios/replay-side-effect-suppression.test.ts +21 -19
- package/src/scenarios/replayDeterminism.test.ts +13 -12
- package/src/scenarios/requirement-ledger.test.ts +15 -14
- package/src/scenarios/restart-during-run.test.ts +3 -2
- package/src/scenarios/route-coverage.test.ts +20 -19
- package/src/scenarios/run-diff.test.ts +22 -20
- package/src/scenarios/run-execution-bounds-shape.test.ts +14 -12
- package/src/scenarios/run-transport-economy.test.ts +13 -11
- package/src/scenarios/runner-ledger.test.ts +17 -16
- package/src/scenarios/runs-lifecycle.test.ts +7 -6
- package/src/scenarios/runtime-capabilities.test.ts +12 -10
- package/src/scenarios/runtime-requires-install-gate.test.ts +8 -8
- package/src/scenarios/runtime-requires-shape.test.ts +7 -6
- package/src/scenarios/safefetch-behavior.test.ts +22 -21
- package/src/scenarios/safefetch-live-audit.test.ts +14 -13
- package/src/scenarios/sandbox-memory-cap.test.ts +2 -1
- package/src/scenarios/sandbox-mvp-behavior.test.ts +24 -23
- package/src/scenarios/sandbox-no-host-fs-escape.test.ts +3 -2
- package/src/scenarios/sandbox-timeout-cap.test.ts +2 -1
- package/src/scenarios/sandbox-wasm-isolation.test.ts +14 -15
- package/src/scenarios/sandbox-wasm-timeout.test.ts +5 -6
- package/src/scenarios/scheduling-capability-shape.test.ts +8 -6
- package/src/scenarios/scheduling-cron-fires-once.test.ts +8 -6
- package/src/scenarios/search-bm25-roundtrip.test.ts +8 -6
- package/src/scenarios/secret-leakage-otel-attribute.test.ts +22 -20
- package/src/scenarios/self-hosted-runner.test.ts +26 -24
- package/src/scenarios/semantic-digest-vectors.test.ts +8 -7
- package/src/scenarios/speech-synthesis-roundtrip.test.ts +9 -7
- package/src/scenarios/speech-synthesis-unadvertised.test.ts +5 -3
- package/src/scenarios/sql-injection-rejection.test.ts +8 -6
- package/src/scenarios/sql-transaction-atomicity.test.ts +10 -8
- package/src/scenarios/staleClaim.test.ts +5 -4
- package/src/scenarios/stream-modes-buffer.test.ts +8 -7
- package/src/scenarios/stream-modes-mixed.test.ts +10 -9
- package/src/scenarios/stream-modes.test.ts +12 -11
- package/src/scenarios/stream-subscribe-from-beginning.test.ts +11 -9
- package/src/scenarios/stream-text-fixture.test.ts +14 -13
- package/src/scenarios/streamReconnect.test.ts +13 -11
- package/src/scenarios/strict-behavior-gate.test.ts +9 -8
- package/src/scenarios/subrun-approval-fail-closed.test.ts +2 -2
- package/src/scenarios/subrun-approval-gate.test.ts +3 -3
- package/src/scenarios/subrun-attestation-shape.test.ts +2 -2
- package/src/scenarios/subrun-checksum-stable.test.ts +4 -4
- package/src/scenarios/subworkflow-input-mapping.test.ts +16 -14
- package/src/scenarios/subworkflow.test.ts +10 -9
- package/src/scenarios/table-cross-tenant-isolation.test.ts +8 -6
- package/src/scenarios/table-cursor-pagination.test.ts +8 -6
- package/src/scenarios/table-schema-enforcement.test.ts +7 -5
- package/src/scenarios/test-seam-unauthenticated.test.ts +2 -2
- package/src/scenarios/tool-catalog-compact-projection.test.ts +10 -8
- package/src/scenarios/tool-catalog-projection.test.ts +11 -9
- package/src/scenarios/tool-descriptor-shape.test.ts +14 -15
- package/src/scenarios/tool-hooks-authorization-fail-closed.test.ts +3 -3
- package/src/scenarios/tool-hooks-content-free.test.ts +4 -4
- package/src/scenarios/tool-hooks-failure-honesty.test.ts +4 -4
- package/src/scenarios/tool-hooks-rate-limit.test.ts +2 -2
- package/src/scenarios/tool-hooks-secret-redaction.test.ts +2 -2
- package/src/scenarios/tool-hooks-shape.test.ts +3 -3
- package/src/scenarios/tool-session-lifecycle.test.ts +14 -13
- package/src/scenarios/trigger-bridge-delivery.test.ts +14 -14
- package/src/scenarios/trigger-bridge-shape.test.ts +19 -20
- package/src/scenarios/trigger-ingestion.test.ts +19 -17
- package/src/scenarios/trigger-stream-cdc-sources.test.ts +16 -14
- package/src/scenarios/v2-approver-enforced.test.ts +97 -0
- package/src/scenarios/v2-assurance-downgrade-audited.test.ts +75 -0
- package/src/scenarios/v2-bundle-v3-signed.test.ts +96 -0
- package/src/scenarios/v2-capabilities-root-closed.test.ts +63 -0
- package/src/scenarios/v2-capability-record-shape.test.ts +104 -0
- package/src/scenarios/v2-chain-pin-exact.test.ts +118 -0
- package/src/scenarios/v2-coherence-not-in-bundle.test.ts +98 -0
- package/src/scenarios/v2-compensation-read-projection.test.ts +85 -0
- package/src/scenarios/v2-configurable-closed.test.ts +67 -0
- package/src/scenarios/v2-dual-stack-negotiation.test.ts +118 -0
- package/src/scenarios/v2-effect-identity-business-key.test.ts +132 -0
- package/src/scenarios/v2-effect-seam-manifest.test.ts +163 -0
- package/src/scenarios/v2-enum-growth-rule.test.ts +69 -0
- package/src/scenarios/v2-era-key.test.ts +86 -0
- package/src/scenarios/v2-error-registry.test.ts +89 -0
- package/src/scenarios/v2-event-type-closed.test.ts +97 -0
- package/src/scenarios/v2-fork-a-v1-run.test.ts +109 -0
- package/src/scenarios/v2-form-when-reuses-edge-conditions.test.ts +70 -0
- package/src/scenarios/v2-header-scheme.test.ts +67 -0
- package/src/scenarios/v2-id-grammar.test.ts +77 -0
- package/src/scenarios/v2-idempotency-key-grammar.test.ts +65 -0
- package/src/scenarios/v2-interrupt-token-scheme.test.ts +55 -0
- package/src/scenarios/v2-lane-issuer-advertised.test.ts +62 -0
- package/src/scenarios/v2-legacy-profiles-absent.test.ts +78 -0
- package/src/scenarios/v2-manifest-ceiling-refused.test.ts +130 -0
- package/src/scenarios/v2-manifest-hatch-carried.test.ts +120 -0
- package/src/scenarios/v2-min-client-version.test.ts +48 -0
- package/src/scenarios/v2-minimum-version-refused.test.ts +141 -0
- package/src/scenarios/v2-mrtr-rounds-ceiling.test.ts +95 -0
- package/src/scenarios/v2-negotiation-authenticated.test.ts +125 -0
- package/src/scenarios/v2-negotiation-decided-emitted.test.ts +149 -0
- package/src/scenarios/v2-no-transport-advertisement.test.ts +57 -0
- package/src/scenarios/v2-owner-subject-required.test.ts +72 -0
- package/src/scenarios/v2-pack-isolation.test.ts +147 -0
- package/src/scenarios/v2-payload-registry-closed.test.ts +110 -0
- package/src/scenarios/v2-peer-dependency-declared.test.ts +132 -0
- package/src/scenarios/v2-pinned-run-disposition.test.ts +124 -0
- package/src/scenarios/v2-poll-cursor-v2.test.ts +114 -0
- package/src/scenarios/v2-preferred-version-default.test.ts +83 -0
- package/src/scenarios/v2-profiles-derived-only.test.ts +70 -0
- package/src/scenarios/v2-provider-conflict.test.ts +115 -0
- package/src/scenarios/v2-refresh-sla.test.ts +78 -0
- package/src/scenarios/v2-relaxation-recorded.test.ts +134 -0
- package/src/scenarios/v2-revocation-honored.test.ts +66 -0
- package/src/scenarios/v2-subject-link-record.test.ts +102 -0
- package/src/scenarios/v2-unmapped-type-refused.test.ts +95 -0
- package/src/scenarios/v2-v1-events-translated.test.ts +105 -0
- package/src/scenarios/v2-v1-signed-webhook-accepted.test.ts +109 -0
- package/src/scenarios/v2-webhook-durable-delivery.test.ts +238 -0
- package/src/scenarios/v2-well-known-one-resource.test.ts +129 -0
- package/src/scenarios/vector-knn-roundtrip.test.ts +9 -7
- package/src/scenarios/verifier-gating.test.ts +8 -6
- package/src/scenarios/version-fold.test.ts +9 -8
- package/src/scenarios/version-negotiation.test.ts +12 -11
- package/src/scenarios/voice-bargein-no-partial-leak.test.ts +8 -6
- package/src/scenarios/voice-event-payloads-shape.test.ts +10 -11
- package/src/scenarios/voice-interim-not-durable.test.ts +7 -5
- package/src/scenarios/voice-streamref-tenant-bound.test.ts +5 -3
- package/src/scenarios/voice-synthesis-streaming.test.ts +11 -9
- package/src/scenarios/voice-transcription-streaming.test.ts +8 -6
- package/src/scenarios/voice-transcription-unadvertised.test.ts +5 -3
- package/src/scenarios/wasm-pack-abi-version-rejection.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-completed.test.ts +3 -2
- package/src/scenarios/wasm-pack-invoke-suspended.test.ts +3 -2
- package/src/scenarios/wasm-pack-load.test.ts +4 -3
- package/src/scenarios/wasm-pack-memory-cap.test.ts +5 -4
- package/src/scenarios/wasm-pack-replay-determinism.test.ts +2 -1
- package/src/scenarios/webhook-negative.test.ts +11 -9
- package/src/scenarios/webhook-receiver-adversarial.test.ts +8 -7
- package/src/scenarios/webhook-sig-algorithm.test.ts +5 -3
- package/src/scenarios/webhook-signed-delivery.test.ts +16 -15
- package/src/scenarios/webhook-tenant-isolation.test.ts +16 -14
- package/src/scenarios/workflow-chain-deferred-parameters.test.ts +28 -30
- package/src/scenarios/workflow-chain-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-host-expansion.test.ts +21 -20
- package/src/scenarios/workflow-chain-pack-manifest-validation.test.ts +14 -12
- package/src/scenarios/workflow-chain-pack-signature-verification.test.ts +5 -4
- package/src/scenarios/workflow-chain-unresolvable-typeid.test.ts +9 -8
- package/src/scenarios/workflow-primary-output-annotation.test.ts +7 -6
- package/src/scenarios/workflow-variable-format.test.ts +24 -24
- package/src/scenarios/workload-identity-behavior.test.ts +15 -13
- package/src/scenarios/workload-identity-chain-bounds.test.ts +18 -17
- package/src/scenarios/workspace-behavior.test.ts +19 -18
- package/src/scenarios/workspace-capability-shape.test.ts +3 -2
- package/src/scenarios/workspace-cross-tenant-isolation-blackbox.test.ts +6 -5
- package/src/scenarios/workspace-cross-tenant-isolation.test.ts +7 -6
- package/src/scenarios/x-openwop-form-pack-manifest.test.ts +8 -7
- package/src/setup.ts +44 -6
- package/vitest.config.ts +5 -1
- package/api/.redocly.lint-ignore.yaml +0 -22
- package/api/asyncapi.yaml +0 -834
- package/api/grpc/openwop.proto +0 -251
- package/api/openapi.yaml +0 -2693
- package/api/redocly.yaml +0 -23
- package/schemas/README.md +0 -144
- package/schemas/a2a-task-state.schema.json +0 -78
- package/schemas/a2ui-surface-delta-frame.schema.json +0 -48
- package/schemas/agent-deployment-transition.schema.json +0 -49
- package/schemas/agent-deployment.schema.json +0 -54
- package/schemas/agent-eval-suite.schema.json +0 -140
- package/schemas/agent-inventory-response.schema.json +0 -115
- package/schemas/agent-manifest.schema.json +0 -156
- package/schemas/agent-org-chart.schema.json +0 -82
- package/schemas/agent-ref.schema.json +0 -63
- package/schemas/agent-roster-entry.schema.json +0 -81
- package/schemas/agent-roster-response.schema.json +0 -21
- package/schemas/ai-envelope.schema.json +0 -134
- package/schemas/annotation-create.schema.json +0 -37
- package/schemas/annotation.schema.json +0 -56
- package/schemas/artifact-type-pack-manifest.schema.json +0 -244
- package/schemas/audit-verify-result.schema.json +0 -90
- package/schemas/budget-policy.schema.json +0 -18
- package/schemas/capabilities.schema.json +0 -4160
- package/schemas/certification-bundle-v2.schema.json +0 -118
- package/schemas/channel-presence-payload.schema.json +0 -41
- package/schemas/channel-written-payload.schema.json +0 -55
- package/schemas/chat-card-pack-manifest.schema.json +0 -253
- package/schemas/compact-tool-descriptor.schema.json +0 -51
- package/schemas/compensation-policy.schema.json +0 -64
- package/schemas/conformance-certification-bundle.schema.json +0 -86
- package/schemas/connection-pack-manifest.schema.json +0 -353
- package/schemas/conversation-event.schema.json +0 -168
- package/schemas/conversation-turn.schema.json +0 -117
- package/schemas/core-conformance-mock-agent-config.schema.json +0 -152
- package/schemas/credential-provenance.schema.json +0 -18
- package/schemas/credential-reference.schema.json +0 -21
- package/schemas/debug-bundle.schema.json +0 -196
- package/schemas/dispatch-config.schema.json +0 -112
- package/schemas/envelopes/clarification.request.schema.json +0 -52
- package/schemas/envelopes/error.schema.json +0 -30
- package/schemas/envelopes/media.audio.schema.json +0 -38
- package/schemas/envelopes/media.file.schema.json +0 -37
- package/schemas/envelopes/media.image.schema.json +0 -33
- package/schemas/envelopes/schema.request.schema.json +0 -26
- package/schemas/envelopes/schema.response.schema.json +0 -22
- package/schemas/envelopes/ui.a2ui-surface.schema.json +0 -154
- package/schemas/error-envelope.schema.json +0 -25
- package/schemas/eval-summary.schema.json +0 -92
- package/schemas/export-bundle.schema.json +0 -66
- package/schemas/form-content-pack-manifest.schema.json +0 -243
- package/schemas/frontend-plugin-manifest.schema.json +0 -109
- package/schemas/goal.schema.json +0 -104
- package/schemas/heartbeat-evaluated.schema.json +0 -14
- package/schemas/heartbeat-state-changed.schema.json +0 -14
- package/schemas/localized-content-language-settings.schema.json +0 -26
- package/schemas/localized-content-page-response.schema.json +0 -60
- package/schemas/localized-content-page.schema.json +0 -62
- package/schemas/localized-content-section.schema.json +0 -51
- package/schemas/memory-entry.schema.json +0 -36
- package/schemas/memory-list-options.schema.json +0 -37
- package/schemas/node-pack-manifest.schema.json +0 -654
- package/schemas/orchestrator-decision.schema.json +0 -90
- package/schemas/org-chart-responsibility-view.schema.json +0 -26
- package/schemas/pack-lockfile.schema.json +0 -108
- package/schemas/prompt-kind.schema.json +0 -8
- package/schemas/prompt-pack-manifest.schema.json +0 -117
- package/schemas/prompt-ref.schema.json +0 -40
- package/schemas/prompt-template.schema.json +0 -149
- package/schemas/proposal.schema.json +0 -84
- package/schemas/registry-version-manifest.schema.json +0 -208
- package/schemas/residency.schema.json +0 -16
- package/schemas/run-ancestry-response.schema.json +0 -54
- package/schemas/run-diff-response.schema.json +0 -64
- package/schemas/run-event-payloads.schema.json +0 -4452
- package/schemas/run-event.schema.json +0 -211
- package/schemas/run-options.schema.json +0 -80
- package/schemas/run-orchestrator-decided-event.schema.json +0 -20
- package/schemas/run-snapshot.schema.json +0 -148
- package/schemas/security-advisory.schema.json +0 -109
- package/schemas/self-hosted-runner-dispatch-frame.schema.json +0 -79
- package/schemas/self-hosted-runner-registration.schema.json +0 -53
- package/schemas/self-hosted-runner-result-frame.schema.json +0 -38
- package/schemas/subject.schema.json +0 -56
- package/schemas/suspend-request.schema.json +0 -207
- package/schemas/tool-descriptor.schema.json +0 -63
- package/schemas/trigger-event.schema.json +0 -222
- package/schemas/trigger-subscription-registration.schema.json +0 -67
- package/schemas/trigger-subscription.schema.json +0 -26
- package/schemas/ui-plugin-message.schema.json +0 -90
- package/schemas/workflow-chain-pack-manifest.schema.json +0 -615
- package/schemas/workflow-definition.schema.json +0 -850
- package/schemas/workload-identity.schema.json +0 -79
- package/schemas/workspace-file-create.schema.json +0 -20
- package/schemas/workspace-file.schema.json +0 -39
|
@@ -47,10 +47,10 @@ async function readSupportedEnvelopes(): Promise<string[] | null> {
|
|
|
47
47
|
describe('aiEnvelope.universalKinds: advertisement shape (FINAL v1.1)', () => {
|
|
48
48
|
it('capabilities.envelopeContracts is either absent or a well-formed object', async () => {
|
|
49
49
|
const block = await readEnvelopeContracts();
|
|
50
|
-
if (block === null) return; // host doesn't opt in — skip
|
|
50
|
+
if (block === null) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `block === null` returned early (host doesn\'t opt in — skip)'); // host doesn't opt in — skip
|
|
51
51
|
expect(
|
|
52
52
|
typeof block.advertised,
|
|
53
|
-
|
|
53
|
+
req('openwop.it.aiEnvelope.universalKinds.capabilities-envelopecontracts-is-either-absent-or-a-well-formed-object',
|
|
54
54
|
'ai-envelope.md §"Capability handshake integration"',
|
|
55
55
|
'capabilities.envelopeContracts.advertised MUST be a boolean when present',
|
|
56
56
|
),
|
|
@@ -59,11 +59,11 @@ describe('aiEnvelope.universalKinds: advertisement shape (FINAL v1.1)', () => {
|
|
|
59
59
|
|
|
60
60
|
it('opted-in hosts advertise every universal kind in supportedEnvelopes', async () => {
|
|
61
61
|
const block = await readEnvelopeContracts();
|
|
62
|
-
if (block === null || !block.advertised) return; // not opted in — skip
|
|
62
|
+
if (block === null || !block.advertised) return softSkip('blocked', 'precondition not met — `block === null || !block.advertised` returned early (not opted in — skip) (seam, prior step, or fixture unavailable)'); // not opted in — skip
|
|
63
63
|
const advertised = await readSupportedEnvelopes();
|
|
64
64
|
expect(
|
|
65
65
|
Array.isArray(advertised),
|
|
66
|
-
|
|
66
|
+
req('openwop.it.aiEnvelope.universalKinds.opted-in-hosts-advertise-every-universal-kind-in-supportedenvelopes',
|
|
67
67
|
'capabilities.schema.json §supportedEnvelopes',
|
|
68
68
|
'supportedEnvelopes MUST be present as an array on hosts that advertise envelopeContracts',
|
|
69
69
|
),
|
|
@@ -71,7 +71,7 @@ describe('aiEnvelope.universalKinds: advertisement shape (FINAL v1.1)', () => {
|
|
|
71
71
|
for (const kind of UNIVERSALS) {
|
|
72
72
|
expect(
|
|
73
73
|
advertised!.includes(kind),
|
|
74
|
-
|
|
74
|
+
req('openwop.it.aiEnvelope.universalKinds.opted-in-hosts-advertise-every-universal-kind-in-supportedenvelopes',
|
|
75
75
|
'ai-envelope.md §"Universal kinds"',
|
|
76
76
|
`supportedEnvelopes MUST include "${kind}" — universals are always-allowed`,
|
|
77
77
|
),
|
|
@@ -103,8 +103,8 @@ describe('aiEnvelope.universalKinds: behavioral accept via /v1/host/sample/envel
|
|
|
103
103
|
payload: { questions: [{ id: 'q1', question: 'Which provider?' }] },
|
|
104
104
|
meta: baseMeta,
|
|
105
105
|
});
|
|
106
|
-
if (r.status === 404) return;
|
|
107
|
-
expect(r.body.status,
|
|
106
|
+
if (r.status === 404) return softSkip('blocked', 'precondition not met — `r.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
107
|
+
expect(r.body.status, req('openwop.it.aiEnvelope.universalKinds.accept-clarification-request-with-valid-payload-status-accepted', 'ai-envelope.md §"Universal kinds"', 'valid clarification.request MUST be accepted')).toBe('accepted');
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
it('accept schema.request → status: accepted', async () => {
|
|
@@ -116,8 +116,8 @@ describe('aiEnvelope.universalKinds: behavioral accept via /v1/host/sample/envel
|
|
|
116
116
|
payload: { envelopeType: 'vendor.acme.prd.create' },
|
|
117
117
|
meta: baseMeta,
|
|
118
118
|
});
|
|
119
|
-
if (r.status === 404) return;
|
|
120
|
-
expect(r.body.status).toBe('accepted');
|
|
119
|
+
if (r.status === 404) return softSkip('blocked', 'precondition not met — `r.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
120
|
+
expect(r.body.status, req('openwop.it.aiEnvelope.universalKinds.accept-schema-request-status-accepted', 'spec/v1/ai-envelope.md', 'accept schema.request → status: accepted')).toBe('accepted');
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
it('accept schema.response (ack:true) → status: accepted', async () => {
|
|
@@ -129,8 +129,8 @@ describe('aiEnvelope.universalKinds: behavioral accept via /v1/host/sample/envel
|
|
|
129
129
|
payload: { envelopeType: 'vendor.acme.prd.create', ack: true },
|
|
130
130
|
meta: baseMeta,
|
|
131
131
|
});
|
|
132
|
-
if (r.status === 404) return;
|
|
133
|
-
expect(r.body.status).toBe('accepted');
|
|
132
|
+
if (r.status === 404) return softSkip('blocked', 'precondition not met — `r.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
133
|
+
expect(r.body.status, req('openwop.it.aiEnvelope.universalKinds.accept-schema-response-ack-true-status-accepted', 'spec/v1/ai-envelope.md', 'accept schema.response (ack:true) → status: accepted')).toBe('accepted');
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
it('accept error envelope (LLM-emitted) → status: accepted (distinct from host-level ErrorEnvelope)', async () => {
|
|
@@ -142,8 +142,8 @@ describe('aiEnvelope.universalKinds: behavioral accept via /v1/host/sample/envel
|
|
|
142
142
|
payload: { code: 'validation_failed', message: 'I cannot produce JSON matching that schema' },
|
|
143
143
|
meta: baseMeta,
|
|
144
144
|
});
|
|
145
|
-
if (r.status === 404) return;
|
|
146
|
-
expect(r.body.status,
|
|
145
|
+
if (r.status === 404) return softSkip('blocked', 'precondition not met — `r.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
146
|
+
expect(r.body.status, req('openwop.it.aiEnvelope.universalKinds.accept-error-envelope-llm-emitted-status-accepted-distinct-from-host-level-error', 'ai-envelope.md §error', 'LLM-emitted error envelope MUST be accepted (NOT the host HTTP ErrorEnvelope)')).toBe('accepted');
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
it('refuse invalid clarification.request (missing questions[]) → status: invalid', async () => {
|
|
@@ -155,22 +155,24 @@ describe('aiEnvelope.universalKinds: behavioral accept via /v1/host/sample/envel
|
|
|
155
155
|
payload: { contextType: 'form-field' }, // missing required `questions`
|
|
156
156
|
meta: baseMeta,
|
|
157
157
|
});
|
|
158
|
-
if (r.status === 404) return;
|
|
158
|
+
if (r.status === 404) return softSkip('blocked', 'precondition not met — `r.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
159
159
|
expect(
|
|
160
160
|
r.body.status,
|
|
161
|
-
|
|
161
|
+
req('openwop.it.aiEnvelope.universalKinds.refuse-invalid-clarification-request-missing-questions-status-invalid', 'ai-envelope.md §"Schema discipline"', 'malformed payload MUST be rejected with invalid'),
|
|
162
162
|
).toBe('invalid');
|
|
163
|
-
expect(Array.isArray(r.body.details), 'invalid outcome MUST carry validation details').toBe(true);
|
|
163
|
+
expect(Array.isArray(r.body.details), req('openwop.it.aiEnvelope.universalKinds.refuse-invalid-clarification-request-missing-questions-status-invalid', 'ai-envelope.md §"Schema discipline"', 'invalid outcome MUST carry validation details')).toBe(true);
|
|
164
164
|
});
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
// E.1 engine-projection via the test-only event-log seam.
|
|
168
168
|
import { queryTestEvents, isEventLogSeamAvailable, resetTestSeam } from '../lib/event-log-query.js';
|
|
169
169
|
import { capabilityFamily, discoveryFamilies } from '../lib/discovery-capabilities.js';
|
|
170
|
+
import { req } from '../lib/requirement-ids.js';
|
|
171
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
170
172
|
|
|
171
173
|
describe('aiEnvelope.universalKinds: engine projection via event-log seam', () => {
|
|
172
174
|
it('clarification.request MUST be lifted to interrupt.requested { kind: "clarification" } per interrupt.md', async () => {
|
|
173
|
-
if (!(await isEventLogSeamAvailable())) return;
|
|
175
|
+
if (!(await isEventLogSeamAvailable())) return softSkip('blocked', 'precondition not met — `!(await isEventLogSeamAvailable())` returned early (seam, prior step, or fixture unavailable)');
|
|
174
176
|
const runId = `r-uk-clar-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
175
177
|
const r = await accept(
|
|
176
178
|
{
|
|
@@ -183,20 +185,20 @@ describe('aiEnvelope.universalKinds: engine projection via event-log seam', () =
|
|
|
183
185
|
},
|
|
184
186
|
{ projectTo: { runId, nodeId: 'n' } },
|
|
185
187
|
);
|
|
186
|
-
if (r.status === 404) return;
|
|
188
|
+
if (r.status === 404) return softSkip('blocked', 'precondition not met — `r.status === 404` returned early (seam, prior step, or fixture unavailable)');
|
|
187
189
|
expect(r.body.status).toBe('accepted');
|
|
188
190
|
const events = await queryTestEvents(runId, { type: 'interrupt.requested' });
|
|
189
|
-
if (!events.ok) return;
|
|
191
|
+
if (!events.ok) return softSkip('blocked', 'precondition not met — `!events.ok` returned early (seam, prior step, or fixture unavailable)');
|
|
190
192
|
expect(
|
|
191
193
|
events.events.length,
|
|
192
|
-
|
|
194
|
+
req('openwop.it.aiEnvelope.universalKinds.clarification-request-must-be-lifted-to-interrupt-requested-kind-clarification-p', 'ai-envelope.md §"Universal kinds"', 'accepted clarification.request MUST project to interrupt.requested per interrupt.md'),
|
|
193
195
|
).toBe(1);
|
|
194
196
|
expect((events.events[0]!.payload as { kind?: string }).kind).toBe('clarification');
|
|
195
197
|
await resetTestSeam();
|
|
196
198
|
});
|
|
197
199
|
|
|
198
200
|
it('error envelope MUST project to log.appended { level: "error" } — NOT node.failed', async () => {
|
|
199
|
-
if (!(await isEventLogSeamAvailable())) return;
|
|
201
|
+
if (!(await isEventLogSeamAvailable())) return softSkip('blocked', 'precondition not met — `!(await isEventLogSeamAvailable())` returned early (seam, prior step, or fixture unavailable)');
|
|
200
202
|
const runId = `r-uk-err-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
201
203
|
await accept(
|
|
202
204
|
{
|
|
@@ -211,20 +213,20 @@ describe('aiEnvelope.universalKinds: engine projection via event-log seam', () =
|
|
|
211
213
|
);
|
|
212
214
|
const logs = await queryTestEvents(runId, { type: 'log.appended' });
|
|
213
215
|
const fails = await queryTestEvents(runId, { type: 'node.failed' });
|
|
214
|
-
if (!logs.ok || !fails.ok) return;
|
|
216
|
+
if (!logs.ok || !fails.ok) return softSkip('blocked', 'precondition not met — `!logs.ok || !fails.ok` returned early (seam, prior step, or fixture unavailable)');
|
|
215
217
|
expect(
|
|
216
218
|
logs.events.some((e) => (e.payload as { level?: string }).level === 'error'),
|
|
217
|
-
|
|
219
|
+
req('openwop.it.aiEnvelope.universalKinds.error-envelope-must-project-to-log-appended-level-error-not-node-failed', 'ai-envelope.md §"Universal kinds"', 'LLM-emitted error envelope MUST project to log.appended at error level'),
|
|
218
220
|
).toBe(true);
|
|
219
221
|
expect(
|
|
220
222
|
fails.events.length,
|
|
221
|
-
|
|
223
|
+
req('openwop.it.aiEnvelope.universalKinds.error-envelope-must-project-to-log-appended-level-error-not-node-failed', 'ai-envelope.md §"Universal kinds"', 'LLM-emitted error envelope MUST NOT project to node.failed (distinct from terminal node failure)'),
|
|
222
224
|
).toBe(0);
|
|
223
225
|
await resetTestSeam();
|
|
224
226
|
});
|
|
225
227
|
|
|
226
228
|
it('schema.request projects to log.appended (host implements next-turn injection out-of-band)', async () => {
|
|
227
|
-
if (!(await isEventLogSeamAvailable())) return;
|
|
229
|
+
if (!(await isEventLogSeamAvailable())) return softSkip('blocked', 'precondition not met — `!(await isEventLogSeamAvailable())` returned early (seam, prior step, or fixture unavailable)');
|
|
228
230
|
const runId = `r-uk-sr-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
229
231
|
await accept(
|
|
230
232
|
{
|
|
@@ -238,10 +240,10 @@ describe('aiEnvelope.universalKinds: engine projection via event-log seam', () =
|
|
|
238
240
|
{ projectTo: { runId, nodeId: 'n' } },
|
|
239
241
|
);
|
|
240
242
|
const events = await queryTestEvents(runId, { type: 'log.appended' });
|
|
241
|
-
if (!events.ok) return;
|
|
243
|
+
if (!events.ok) return softSkip('blocked', 'precondition not met — `!events.ok` returned early (seam, prior step, or fixture unavailable)');
|
|
242
244
|
expect(
|
|
243
245
|
events.events.length,
|
|
244
|
-
|
|
246
|
+
req('openwop.it.aiEnvelope.universalKinds.schema-request-projects-to-log-appended-host-implements-next-turn-injection-out', 'ai-envelope.md §"Universal kinds"', 'schema.request MUST project to log.appended (the schema delivery itself happens out-of-band via the host\'s next-turn system prompt)'),
|
|
245
247
|
).toBeGreaterThan(0);
|
|
246
248
|
await resetTestSeam();
|
|
247
249
|
});
|
|
@@ -256,10 +258,10 @@ describe('aiEnvelope.universalKinds: schema.response counter-policy advertisemen
|
|
|
256
258
|
const res = await driver.get('/.well-known/openwop');
|
|
257
259
|
const body = res.json as { capabilities?: { aiEnvelope?: { schemaResponseCounterPolicy?: string } } } | undefined;
|
|
258
260
|
const policy = capabilityFamily<{ schemaResponseCounterPolicy?: string }>(body, 'aiEnvelope')?.schemaResponseCounterPolicy;
|
|
259
|
-
if (policy === undefined) return; // no policy advertised — host MAY omit
|
|
261
|
+
if (policy === undefined) return softSkip('inapplicable', 'capability or profile not advertised by this host — gate `policy === undefined` returned early (no policy advertised — host MAY omit)'); // no policy advertised — host MAY omit
|
|
260
262
|
expect(
|
|
261
263
|
['counted', 'exempt'].includes(policy),
|
|
262
|
-
|
|
264
|
+
req('openwop.it.aiEnvelope.universalKinds.host-may-count-or-exempt-schema-response-against-envelopesperturn-when-advertise',
|
|
263
265
|
'ai-envelope.md §"Universal kinds"',
|
|
264
266
|
'when advertised, schemaResponseCounterPolicy MUST be either "counted" or "exempt"',
|
|
265
267
|
),
|
|
@@ -21,9 +21,9 @@ import { join } from 'node:path';
|
|
|
21
21
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
22
22
|
import addFormats from 'ajv-formats';
|
|
23
23
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
24
|
+
import { req } from '../lib/requirement-ids.js';
|
|
24
25
|
|
|
25
26
|
const BASE = 'https://openwop.dev/spec/v1/';
|
|
26
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
27
27
|
function loadSchema(name: string): Record<string, unknown> {
|
|
28
28
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
29
29
|
}
|
|
@@ -43,27 +43,27 @@ describe('aiproviders-input-shape: callAI perception input (RFC 0091 §B, server
|
|
|
43
43
|
const input = ajv.getSchema(`${BASE}capabilities.schema.json#/properties/aiProviders/properties/input`);
|
|
44
44
|
|
|
45
45
|
it('the aiProviders.input sub-schema exists', () => {
|
|
46
|
-
expect(input, 'capabilities.aiProviders.input MUST be declared').toBeTruthy();
|
|
46
|
+
expect(input, req('openwop.it.aiproviders-input-shape.the-aiproviders-input-sub-schema-exists', 'RFC 0091', 'capabilities.aiProviders.input MUST be declared')).toBeTruthy();
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
it('accepts a conforming modalities advertisement', () => {
|
|
50
50
|
expect(
|
|
51
51
|
input!({ modalities: ['text', 'image', 'document'], maxBytesPerPart: 1048576 }),
|
|
52
|
-
|
|
52
|
+
req('openwop.it.aiproviders-input-shape.accepts-a-conforming-modalities-advertisement', 'RFC 0091 §B', 'a conforming input advertisement MUST validate'),
|
|
53
53
|
).toBe(true);
|
|
54
54
|
});
|
|
55
55
|
|
|
56
56
|
it('rejects an out-of-enum modality', () => {
|
|
57
57
|
expect(
|
|
58
58
|
input!({ modalities: ['text', 'video'] }),
|
|
59
|
-
|
|
59
|
+
req('openwop.it.aiproviders-input-shape.rejects-an-out-of-enum-modality', 'RFC 0091 §B', 'modalities is a closed enum (text/image/audio/document)'),
|
|
60
60
|
).toBe(false);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
it('rejects a non-positive maxBytesPerPart', () => {
|
|
64
64
|
expect(
|
|
65
65
|
input!({ modalities: ['text'], maxBytesPerPart: 0 }),
|
|
66
|
-
|
|
66
|
+
req('openwop.it.aiproviders-input-shape.rejects-a-non-positive-maxbytesperpart', 'RFC 0091 §B', 'maxBytesPerPart MUST be a positive integer'),
|
|
67
67
|
).toBe(false);
|
|
68
68
|
});
|
|
69
69
|
});
|
|
@@ -30,9 +30,7 @@ import { join } from 'node:path';
|
|
|
30
30
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
31
31
|
import addFormats from 'ajv-formats';
|
|
32
32
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
33
|
-
|
|
34
|
-
/** Server-free assertion-message helper. */
|
|
35
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
33
|
+
import { req } from '../lib/requirement-ids.js';
|
|
36
34
|
|
|
37
35
|
function loadSchema(name: string): Record<string, unknown> {
|
|
38
36
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -49,14 +47,14 @@ describe('aiproviders-realtimevoice-shape: capability advertisement (RFC 0106 §
|
|
|
49
47
|
.realtimeVoice;
|
|
50
48
|
expect(
|
|
51
49
|
realtimeVoice,
|
|
52
|
-
|
|
50
|
+
req('openwop.it.aiproviders-realtimevoice-shape.aiproviders-realtimevoice-is-an-object-with-the-four-sub-flags', 'host-capabilities.md §host.aiProviders', 'aiProviders.realtimeVoice MUST be declared'),
|
|
53
51
|
).toBeDefined();
|
|
54
|
-
expect(realtimeVoice?.type,
|
|
52
|
+
expect(realtimeVoice?.type, req('openwop.it.aiproviders-realtimevoice-shape.aiproviders-realtimevoice-is-an-object-with-the-four-sub-flags', 'RFC 0106 §A', 'realtimeVoice MUST be an object')).toBe('object');
|
|
55
53
|
const props = realtimeVoice?.properties ?? {};
|
|
56
54
|
for (const flag of ['transcription', 'synthesis', 'turnDetection', 'bargeIn']) {
|
|
57
55
|
expect(
|
|
58
56
|
Object.prototype.hasOwnProperty.call(props, flag),
|
|
59
|
-
|
|
57
|
+
req('openwop.it.aiproviders-realtimevoice-shape.aiproviders-realtimevoice-is-an-object-with-the-four-sub-flags', 'RFC 0106 §A', `realtimeVoice.${flag} MUST be declared`),
|
|
60
58
|
).toBe(true);
|
|
61
59
|
}
|
|
62
60
|
});
|
|
@@ -66,7 +64,7 @@ describe('aiproviders-realtimevoice-shape: capability advertisement (RFC 0106 §
|
|
|
66
64
|
const required = Array.isArray(aiProviders.required) ? (aiProviders.required as string[]) : [];
|
|
67
65
|
expect(
|
|
68
66
|
required.includes('realtimeVoice'),
|
|
69
|
-
|
|
67
|
+
req('openwop.it.aiproviders-realtimevoice-shape.realtimevoice-is-not-in-aiproviders-required-absence-no-live-voice-is-a-valid-de', 'RFC 0106 §A', 'aiProviders.realtimeVoice MUST be optional (a host without live voice is valid)'),
|
|
70
68
|
).toBe(false);
|
|
71
69
|
});
|
|
72
70
|
|
|
@@ -78,24 +76,24 @@ describe('aiproviders-realtimevoice-shape: capability advertisement (RFC 0106 §
|
|
|
78
76
|
|
|
79
77
|
expect(
|
|
80
78
|
validate({ transcription: 'streaming', turnDetection: 'semantic', bargeIn: 'supported' }),
|
|
81
|
-
|
|
79
|
+
req('openwop.it.aiproviders-realtimevoice-shape.ajv-the-realtimevoice-subschema-accepts-the-floor-and-enforces-the-turndetection', 'RFC 0106 §A', 'a full transcription advertisement MUST validate'),
|
|
82
80
|
).toBe(true);
|
|
83
|
-
expect(validate({}),
|
|
81
|
+
expect(validate({}), req('openwop.it.aiproviders-realtimevoice-shape.ajv-the-realtimevoice-subschema-accepts-the-floor-and-enforces-the-turndetection', 'RFC 0106 §A', 'an empty realtimeVoice object MUST validate')).toBe(true);
|
|
84
82
|
expect(
|
|
85
83
|
validate({ turnDetection: 'semantic' }),
|
|
86
|
-
|
|
84
|
+
req('openwop.it.aiproviders-realtimevoice-shape.ajv-the-realtimevoice-subschema-accepts-the-floor-and-enforces-the-turndetection', 'RFC 0106 §A', 'turnDetection without transcription MUST be rejected (dependentRequired)'),
|
|
87
85
|
).toBe(false);
|
|
88
86
|
expect(
|
|
89
87
|
validate({ bargeIn: 'supported' }),
|
|
90
|
-
|
|
88
|
+
req('openwop.it.aiproviders-realtimevoice-shape.ajv-the-realtimevoice-subschema-accepts-the-floor-and-enforces-the-turndetection', 'RFC 0106 §A', 'bargeIn without transcription MUST be rejected (dependentRequired)'),
|
|
91
89
|
).toBe(false);
|
|
92
90
|
expect(
|
|
93
91
|
validate({ transcription: 'streaming', turnDetection: 'aggressive' }),
|
|
94
|
-
|
|
92
|
+
req('openwop.it.aiproviders-realtimevoice-shape.ajv-the-realtimevoice-subschema-accepts-the-floor-and-enforces-the-turndetection', 'RFC 0106 §A', 'an out-of-enum turnDetection MUST be rejected'),
|
|
95
93
|
).toBe(false);
|
|
96
94
|
expect(
|
|
97
95
|
validate({ transcription: true }),
|
|
98
|
-
|
|
96
|
+
req('openwop.it.aiproviders-realtimevoice-shape.ajv-the-realtimevoice-subschema-accepts-the-floor-and-enforces-the-turndetection', 'RFC 0106 §A', 'transcription MUST be the string const "streaming", not a boolean'),
|
|
99
97
|
).toBe(false);
|
|
100
98
|
});
|
|
101
99
|
|
|
@@ -106,15 +104,15 @@ describe('aiproviders-realtimevoice-shape: capability advertisement (RFC 0106 §
|
|
|
106
104
|
|
|
107
105
|
expect(
|
|
108
106
|
validate({ realtimeVoice: { transcription: 'streaming', synthesis: 'streaming' } }),
|
|
109
|
-
|
|
107
|
+
req('openwop.it.aiproviders-realtimevoice-shape.ajv-the-aiproviders-subschema-enforces-realtimevoice-synthesis-speechsynthesis', 'RFC 0106 §A', 'streaming synthesis without speechSynthesis: "supported" MUST be rejected'),
|
|
110
108
|
).toBe(false);
|
|
111
109
|
expect(
|
|
112
110
|
validate({ speechSynthesis: 'supported', realtimeVoice: { transcription: 'streaming', synthesis: 'streaming' } }),
|
|
113
|
-
|
|
111
|
+
req('openwop.it.aiproviders-realtimevoice-shape.ajv-the-aiproviders-subschema-enforces-realtimevoice-synthesis-speechsynthesis', 'RFC 0106 §A', 'streaming synthesis WITH speechSynthesis: "supported" MUST validate'),
|
|
114
112
|
).toBe(true);
|
|
115
113
|
expect(
|
|
116
114
|
validate({ realtimeVoice: { transcription: 'streaming' } }),
|
|
117
|
-
|
|
115
|
+
req('openwop.it.aiproviders-realtimevoice-shape.ajv-the-aiproviders-subschema-enforces-realtimevoice-synthesis-speechsynthesis', 'RFC 0106 §A', 'transcription-only (no synthesis) MUST validate without speechSynthesis'),
|
|
118
116
|
).toBe(true);
|
|
119
117
|
});
|
|
120
118
|
});
|
|
@@ -39,6 +39,8 @@ import { describe, it, expect } from 'vitest';
|
|
|
39
39
|
import { driver } from '../lib/driver.js';
|
|
40
40
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
41
41
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
42
|
+
import { req } from '../lib/requirement-ids.js';
|
|
43
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
42
44
|
|
|
43
45
|
const SEAM = '/v1/host/sample/ai/call';
|
|
44
46
|
|
|
@@ -91,7 +93,7 @@ describe('aiproviders-selfhosted-honesty (RFC 0108 §A.2/§D)', () => {
|
|
|
91
93
|
provider: providerId,
|
|
92
94
|
messages: [{ role: 'user', content: 'ping' }],
|
|
93
95
|
});
|
|
94
|
-
if (res.status === 404) return; // seam unwired — soft-skip the behavioral suite
|
|
96
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam unwired — soft-skip the behavioral suite) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip the behavioral suite
|
|
95
97
|
|
|
96
98
|
// §A.2 — the advertisement must be backed by a real endpoint. A success or a
|
|
97
99
|
// transport-class failure both prove a real endpoint was reached; a
|
|
@@ -100,7 +102,7 @@ describe('aiproviders-selfhosted-honesty (RFC 0108 §A.2/§D)', () => {
|
|
|
100
102
|
const code = errCode(res.json);
|
|
101
103
|
expect(
|
|
102
104
|
code === undefined || !NO_ENDPOINT_CODES.has(code),
|
|
103
|
-
|
|
105
|
+
req('openwop.it.aiproviders-selfhosted-honesty.a-selfhosted-dispatch-reaches-a-real-endpoint-a-2-and-never-discloses-the-endpoi',
|
|
104
106
|
'RFC 0108 §A.2',
|
|
105
107
|
`an advertised selfHosted id (${providerId}) MUST be backed by a configured, reachable endpoint ` +
|
|
106
108
|
`(dispatch succeeds or fails with a transport error) — MUST NOT refuse "${code}" (no endpoint configured)`,
|
|
@@ -116,14 +118,14 @@ describe('aiproviders-selfhosted-honesty (RFC 0108 §A.2/§D)', () => {
|
|
|
116
118
|
'[openwop-selfhosted-providers] §D disclosure check skipped — set OPENWOP_TEST_COMPAT_ENDPOINT ' +
|
|
117
119
|
"to the host's configured compat endpoint to assert non-disclosure on the wire.",
|
|
118
120
|
);
|
|
119
|
-
return;
|
|
121
|
+
return softSkip('blocked', 'precondition not met — `!endpoint` returned early (seam, prior step, or fixture unavailable)');
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
const serialized = JSON.stringify(res.json ?? {});
|
|
123
125
|
for (const needle of endpointNeedles(endpoint)) {
|
|
124
126
|
expect(
|
|
125
127
|
!serialized.includes(needle),
|
|
126
|
-
|
|
128
|
+
req('openwop.it.aiproviders-selfhosted-honesty.a-selfhosted-dispatch-reaches-a-real-endpoint-a-2-and-never-discloses-the-endpoi',
|
|
127
129
|
'RFC 0108 §D (self-hosted-endpoint-no-disclosure)',
|
|
128
130
|
`the endpoint location ("${needle}") MUST NOT appear in any selfHosted dispatch response or error payload`,
|
|
129
131
|
),
|
|
@@ -32,9 +32,7 @@ import { join } from 'node:path';
|
|
|
32
32
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
33
33
|
import addFormats from 'ajv-formats';
|
|
34
34
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
35
|
-
|
|
36
|
-
/** Server-free assertion-message helper. */
|
|
37
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
35
|
+
import { req } from '../lib/requirement-ids.js';
|
|
38
36
|
|
|
39
37
|
function loadSchema(name: string): Record<string, unknown> {
|
|
40
38
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -87,19 +85,19 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
|
|
|
87
85
|
| undefined;
|
|
88
86
|
expect(
|
|
89
87
|
selfHosted,
|
|
90
|
-
|
|
88
|
+
req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'capabilities.md §aiProviders.selfHosted', 'aiProviders.selfHosted MUST be declared'),
|
|
91
89
|
).toBeDefined();
|
|
92
|
-
expect(selfHosted?.type,
|
|
90
|
+
expect(selfHosted?.type, req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'selfHosted MUST be an array')).toBe('array');
|
|
93
91
|
expect(
|
|
94
92
|
selfHosted?.uniqueItems,
|
|
95
|
-
|
|
93
|
+
req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'selfHosted entries MUST be unique'),
|
|
96
94
|
).toBe(true);
|
|
97
|
-
expect(selfHosted?.items?.type,
|
|
95
|
+
expect(selfHosted?.items?.type, req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'selfHosted entries MUST be strings')).toBe(
|
|
98
96
|
'string',
|
|
99
97
|
);
|
|
100
98
|
expect(
|
|
101
99
|
selfHosted?.items?.minLength,
|
|
102
|
-
|
|
100
|
+
req('openwop.it.aiproviders-selfhosted-shape.aiproviders-selfhosted-is-declared-as-a-string-with-uniqueitems', 'RFC 0108 §A', 'a selfHosted id MUST be non-empty'),
|
|
103
101
|
).toBe(1);
|
|
104
102
|
});
|
|
105
103
|
|
|
@@ -109,7 +107,7 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
|
|
|
109
107
|
const required = Array.isArray(aiProviders?.required) ? (aiProviders!.required as string[]) : [];
|
|
110
108
|
expect(
|
|
111
109
|
required.includes('selfHosted'),
|
|
112
|
-
|
|
110
|
+
req('openwop.it.aiproviders-selfhosted-shape.selfhosted-is-not-in-aiproviders-required-absence-no-self-hosted-endpoint-is-a-v', 'RFC 0108 §A.1', 'aiProviders.selfHosted MUST be optional'),
|
|
113
111
|
).toBe(false);
|
|
114
112
|
});
|
|
115
113
|
|
|
@@ -125,19 +123,19 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
|
|
|
125
123
|
|
|
126
124
|
expect(
|
|
127
125
|
validate(['ollama', 'compat']),
|
|
128
|
-
|
|
126
|
+
req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'a conforming selfHosted array MUST validate'),
|
|
129
127
|
).toBe(true);
|
|
130
128
|
expect(
|
|
131
129
|
validate('ollama'),
|
|
132
|
-
|
|
130
|
+
req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'a non-array selfHosted MUST be rejected'),
|
|
133
131
|
).toBe(false);
|
|
134
132
|
expect(
|
|
135
133
|
validate(['ollama', 'ollama']),
|
|
136
|
-
|
|
134
|
+
req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'duplicate selfHosted entries MUST be rejected (uniqueItems)'),
|
|
137
135
|
).toBe(false);
|
|
138
136
|
expect(
|
|
139
137
|
validate(['']),
|
|
140
|
-
|
|
138
|
+
req('openwop.it.aiproviders-selfhosted-shape.ajv-accepts-a-conforming-selfhosted-array-and-rejects-non-array-duplicate-empty', 'RFC 0108 §A', 'an empty-string selfHosted id MUST be rejected (minLength 1)'),
|
|
141
139
|
).toBe(false);
|
|
142
140
|
});
|
|
143
141
|
|
|
@@ -149,13 +147,13 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
|
|
|
149
147
|
byok: ['anthropic', 'openai', 'compat'],
|
|
150
148
|
selfHosted: ['ollama', 'compat'],
|
|
151
149
|
}),
|
|
152
|
-
|
|
150
|
+
req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.1/§A.3', 'a conforming advertisement MUST have no violations'),
|
|
153
151
|
).toEqual([]);
|
|
154
152
|
|
|
155
153
|
// §A.1 violation: `vllm` not in supported.
|
|
156
154
|
expect(
|
|
157
155
|
validateSelfHosted({ supported: ['anthropic', 'ollama'], selfHosted: ['ollama', 'vllm'] }),
|
|
158
|
-
|
|
156
|
+
req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.1', 'a selfHosted id absent from supported MUST be flagged'),
|
|
159
157
|
).not.toEqual([]);
|
|
160
158
|
|
|
161
159
|
// §A.3 violations: URL-shaped ids leak the endpoint location.
|
|
@@ -166,14 +164,14 @@ describe('aiproviders-selfhosted-shape: self-hosted provider-class advertisement
|
|
|
166
164
|
]) {
|
|
167
165
|
expect(
|
|
168
166
|
isUrlShaped(leaky),
|
|
169
|
-
|
|
167
|
+
req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.3', `the URL-shaped id "${leaky}" MUST be detected as non-conformant`),
|
|
170
168
|
).toBe(true);
|
|
171
169
|
}
|
|
172
170
|
|
|
173
171
|
// A label with a non-numeric suffix (`compat:label`) is NOT URL-shaped — it is a permitted opaque id.
|
|
174
172
|
expect(
|
|
175
173
|
isUrlShaped('compat:local-ollama'),
|
|
176
|
-
|
|
174
|
+
req('openwop.it.aiproviders-selfhosted-shape.a-1-subset-a-3-non-disclosure-a-conforming-doc-has-no-violations-an-out-of-subse', 'RFC 0108 §A.3', 'an opaque `compat:<label>` id (no host:port) MUST be permitted'),
|
|
177
175
|
).toBe(false);
|
|
178
176
|
});
|
|
179
177
|
});
|
|
@@ -31,9 +31,7 @@ import { join } from 'node:path';
|
|
|
31
31
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
32
32
|
import addFormats from 'ajv-formats';
|
|
33
33
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
34
|
-
|
|
35
|
-
/** Server-free assertion-message helper. */
|
|
36
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
34
|
+
import { req } from '../lib/requirement-ids.js';
|
|
37
35
|
|
|
38
36
|
function loadSchema(name: string): Record<string, unknown> {
|
|
39
37
|
return JSON.parse(readFileSync(join(SCHEMAS_DIR, name), 'utf8')) as Record<string, unknown>;
|
|
@@ -49,11 +47,11 @@ describe('aiproviders-speechsynth-shape: capability advertisement (RFC 0105 §B,
|
|
|
49
47
|
| undefined;
|
|
50
48
|
expect(
|
|
51
49
|
speechSynthesis,
|
|
52
|
-
|
|
50
|
+
req('openwop.it.aiproviders-speechsynth-shape.aiproviders-speechsynthesis-is-declared-as-the-string-const-supported', 'host-capabilities.md §host.aiProviders', 'aiProviders.speechSynthesis MUST be declared'),
|
|
53
51
|
).toBeDefined();
|
|
54
52
|
expect(
|
|
55
53
|
speechSynthesis?.const,
|
|
56
|
-
|
|
54
|
+
req('openwop.it.aiproviders-speechsynth-shape.aiproviders-speechsynthesis-is-declared-as-the-string-const-supported', 'RFC 0105 §B', 'aiProviders.speechSynthesis MUST be the string const "supported" (not an object)'),
|
|
57
55
|
).toBe('supported');
|
|
58
56
|
});
|
|
59
57
|
|
|
@@ -63,7 +61,7 @@ describe('aiproviders-speechsynth-shape: capability advertisement (RFC 0105 §B,
|
|
|
63
61
|
const required = Array.isArray(aiProviders?.required) ? (aiProviders!.required as string[]) : [];
|
|
64
62
|
expect(
|
|
65
63
|
required.includes('speechSynthesis'),
|
|
66
|
-
|
|
64
|
+
req('openwop.it.aiproviders-speechsynth-shape.speechsynthesis-is-not-in-aiproviders-required-absence-no-tts-is-a-valid-default', 'RFC 0105 §B', 'aiProviders.speechSynthesis MUST be optional (a host without TTS is valid)'),
|
|
67
65
|
).toBe(false);
|
|
68
66
|
});
|
|
69
67
|
|
|
@@ -81,12 +79,12 @@ describe('aiproviders-speechsynth-shape: capability advertisement (RFC 0105 §B,
|
|
|
81
79
|
|
|
82
80
|
expect(
|
|
83
81
|
validate('supported'),
|
|
84
|
-
|
|
82
|
+
req('openwop.it.aiproviders-speechsynth-shape.ajv-accepts-the-string-const-form-and-rejects-the-object-form', 'RFC 0105 §B', 'aiProviders.speechSynthesis === "supported" MUST validate'),
|
|
85
83
|
).toBe(true);
|
|
86
84
|
|
|
87
85
|
expect(
|
|
88
86
|
validate({ supported: true }),
|
|
89
|
-
|
|
87
|
+
req('openwop.it.aiproviders-speechsynth-shape.ajv-accepts-the-string-const-form-and-rejects-the-object-form', 'RFC 0105 §B', 'the object form { supported: true } MUST be rejected by the const'),
|
|
90
88
|
).toBe(false);
|
|
91
89
|
});
|
|
92
90
|
});
|
|
@@ -38,9 +38,8 @@ import { driver } from '../lib/driver.js';
|
|
|
38
38
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
39
39
|
import { readCapabilityFamily } from '../lib/discovery-capabilities.js';
|
|
40
40
|
import { SCHEMAS_DIR } from '../lib/paths.js';
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const why = (specRef: string, requirement: string): string => `${specRef} — ${requirement}`;
|
|
41
|
+
import { req } from '../lib/requirement-ids.js';
|
|
42
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
44
43
|
|
|
45
44
|
/** Read the canonical error code from a response body (tolerant of
|
|
46
45
|
* `{error}` / `{code}` / `{error:{code}}` shapes). */
|
|
@@ -73,7 +72,7 @@ describe('aiproviders-subscription-scope: schema shape (RFC 0121, server-free)',
|
|
|
73
72
|
| undefined;
|
|
74
73
|
expect(
|
|
75
74
|
authModes?.additionalProperties?.items?.enum,
|
|
76
|
-
|
|
75
|
+
req('openwop.it.aiproviders-subscription-scope.the-aiproviders-authmodes-enum-includes-subscription', 'capabilities.md §aiProviders.authModes', 'the authModes enum MUST include "subscription" (RFC 0121)'),
|
|
77
76
|
).toContain('subscription');
|
|
78
77
|
});
|
|
79
78
|
});
|
|
@@ -94,7 +93,7 @@ describe('aiproviders-subscription-scope: §B.7 in-byok (advertisement-gated)',
|
|
|
94
93
|
for (const provider of subProviders) {
|
|
95
94
|
expect(
|
|
96
95
|
byok.has(provider),
|
|
97
|
-
|
|
96
|
+
req('openwop.it.aiproviders-subscription-scope.every-advertised-subscription-provider-appears-in-aiproviders-byok', 'RFC 0121 §B.7', `subscription provider '${provider}' MUST appear in aiProviders.byok`),
|
|
98
97
|
).toBe(true);
|
|
99
98
|
}
|
|
100
99
|
});
|
|
@@ -110,18 +109,18 @@ describe('aiproviders-subscription-scope: §B.8 user-scope-only (bind-seam-gated
|
|
|
110
109
|
// multi-instance deploy with no durable per-user login) can still prove the
|
|
111
110
|
// safety rail here. Soft-skips on 404 when the seam is unwired.
|
|
112
111
|
const res = await driver.post(SEAM, { provider: 'anthropic', mode: 'subscription', scope: 'tenant' });
|
|
113
|
-
if (res.status === 404) return; // seam unwired — soft-skip
|
|
112
|
+
if (res.status === 404) return softSkip('blocked', 'precondition not met — `res.status === 404` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
114
113
|
|
|
115
114
|
expect(
|
|
116
115
|
res.status >= 400,
|
|
117
|
-
|
|
116
|
+
req('openwop.it.aiproviders-subscription-scope.a-tenant-scope-subscription-binding-is-rejected-with-credential-scope-forbidden',
|
|
118
117
|
'RFC 0121 §B.8',
|
|
119
118
|
`binding a subscription credential at tenant scope MUST be rejected (got HTTP ${res.status})`,
|
|
120
119
|
),
|
|
121
120
|
).toBe(true);
|
|
122
121
|
expect(
|
|
123
122
|
errCode(res.json),
|
|
124
|
-
|
|
123
|
+
req('openwop.it.aiproviders-subscription-scope.a-tenant-scope-subscription-binding-is-rejected-with-credential-scope-forbidden',
|
|
125
124
|
'RFC 0121 §B.8',
|
|
126
125
|
'a rejected tenant-scope subscription binding MUST carry the canonical `credential_scope_forbidden` error code',
|
|
127
126
|
),
|
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
import { describe, it, expect } from 'vitest';
|
|
20
20
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
21
|
-
import { driver } from '../lib/driver.js';
|
|
22
21
|
import { isAnonymousActorAdvertised, anonDispatch } from '../lib/anonymousActor.js';
|
|
22
|
+
import { req } from '../lib/requirement-ids.js';
|
|
23
|
+
import { softSkip } from '../lib/soft-skip.js';
|
|
23
24
|
|
|
24
25
|
const PROFILE = 'openwop-anonymous-actor';
|
|
25
26
|
|
|
@@ -33,19 +34,19 @@ describe('anonymous-actor-audit-opaque (RFC 0132 §D)', () => {
|
|
|
33
34
|
it('the authorization.decided principal is opaque, non-PII, and the record carries no credential', async () => {
|
|
34
35
|
if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
|
|
35
36
|
const res = await anonDispatch({ tool: 'catalog.read' });
|
|
36
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
37
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
37
38
|
|
|
38
39
|
const decided = res.json?.authorizationDecided?.payload;
|
|
39
40
|
expect(
|
|
40
41
|
decided?.principal,
|
|
41
|
-
|
|
42
|
+
req('openwop.it.anonymous-actor-audit-opaque.the-authorization-decided-principal-is-opaque-non-pii-and-the-record-carries-no', 'RFC 0132 §D', 'an anon tool call MUST emit authorization.decided with a principal'),
|
|
42
43
|
).toBeTruthy();
|
|
43
44
|
|
|
44
45
|
const principal = decided?.principal ?? '';
|
|
45
46
|
for (const [pattern, label] of PII_PATTERNS) {
|
|
46
47
|
expect(
|
|
47
48
|
pattern.test(principal),
|
|
48
|
-
|
|
49
|
+
req('openwop.it.anonymous-actor-audit-opaque.the-authorization-decided-principal-is-opaque-non-pii-and-the-record-carries-no', 'SECURITY anon-actor-audit-opaque', `the anon principal MUST NOT embed ${label}`),
|
|
49
50
|
).toBe(false);
|
|
50
51
|
}
|
|
51
52
|
// The record MUST NOT carry credential material (reason is redaction-safe).
|
|
@@ -53,7 +54,7 @@ describe('anonymous-actor-audit-opaque (RFC 0132 §D)', () => {
|
|
|
53
54
|
for (const [pattern, label] of PII_PATTERNS) {
|
|
54
55
|
expect(
|
|
55
56
|
pattern.test(serialized),
|
|
56
|
-
|
|
57
|
+
req('openwop.it.anonymous-actor-audit-opaque.the-authorization-decided-principal-is-opaque-non-pii-and-the-record-carries-no', 'SECURITY anon-actor-audit-opaque', `the audit record MUST NOT carry ${label}`),
|
|
57
58
|
).toBe(false);
|
|
58
59
|
}
|
|
59
60
|
});
|
|
@@ -61,11 +62,11 @@ describe('anonymous-actor-audit-opaque (RFC 0132 §D)', () => {
|
|
|
61
62
|
it('the run snapshot echoes owner.principalKind "anonymous"', async () => {
|
|
62
63
|
if (!behaviorGate(PROFILE, await isAnonymousActorAdvertised())) return;
|
|
63
64
|
const res = await anonDispatch({ tool: 'catalog.read' });
|
|
64
|
-
if (res.status === 404 || res.status === 405) return; // seam unwired — soft-skip
|
|
65
|
-
if (!res.json?.owner) return; // seam does not echo an owner triple — nothing to assert
|
|
65
|
+
if (res.status === 404 || res.status === 405) return softSkip('blocked', 'precondition not met — `res.status === 404 || res.status === 405` returned early (seam unwired — soft-skip) (seam, prior step, or fixture unavailable)'); // seam unwired — soft-skip
|
|
66
|
+
if (!res.json?.owner) return softSkip('blocked', 'precondition not met — `!res.json?.owner` returned early (seam does not echo an owner triple — nothing to assert) (seam, prior step, or fixture unavailable)'); // seam does not echo an owner triple — nothing to assert
|
|
66
67
|
expect(
|
|
67
68
|
res.json.owner.principalKind,
|
|
68
|
-
|
|
69
|
+
req('openwop.it.anonymous-actor-audit-opaque.the-run-snapshot-echoes-owner-principalkind-anonymous', 'RFC 0132 §A', 'an anon-authorized run MUST set owner.principalKind "anonymous"'),
|
|
69
70
|
).toBe('anonymous');
|
|
70
71
|
});
|
|
71
72
|
});
|