@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
package/src/lib/seams.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suite 2.0.0 — the seams profile (RFC 0168 §C; `openwop-conformance-seams-v2`).
|
|
3
|
+
*
|
|
4
|
+
* v1 scenarios reach the host-sample seams at `/v1/host/sample/…`,
|
|
5
|
+
* `/v1/host/workspace/files…` and `/v1/packs-test/…`. In v2 those operations
|
|
6
|
+
* leave the canonical API for `api/seams-v2.yaml` under `/conformance/seams/…`
|
|
7
|
+
* (RFC 0168 §C.2). Under target major 2 the driver rewrites the v1 seam path
|
|
8
|
+
* to its v2 address, so the 165 scenarios that name a seam keep working
|
|
9
|
+
* without a sweep; the sweep to `seamPath()` is the P3-F polish.
|
|
10
|
+
*
|
|
11
|
+
* How a v2 host advertises the profile: RFC 0168 §C.1 forbids a `testSeams`
|
|
12
|
+
* capability flag and RFC 0169 §C.1 removes the root `profiles[]`; the
|
|
13
|
+
* reconciliation (recorded against RFC 0168 at its flip) is the
|
|
14
|
+
* `conformance` METADATA key (RFC 0169 §A.1a): `conformance.seamsProfile:
|
|
15
|
+
* "openwop-conformance-seams-v2"`. Metadata, not a capability family.
|
|
16
|
+
*/
|
|
17
|
+
export const SEAMS_PROFILE_ID = 'openwop-conformance-seams-v2';
|
|
18
|
+
export const SEAMS_PREFIX = '/conformance/seams';
|
|
19
|
+
|
|
20
|
+
const V1_SEAM_PREFIXES: ReadonlyArray<readonly [RegExp, string]> = [
|
|
21
|
+
[/^\/v1\/host\/sample\//, `${SEAMS_PREFIX}/sample/`],
|
|
22
|
+
[/^\/v1\/host\/workspace\/files/, `${SEAMS_PREFIX}/workspace/files`],
|
|
23
|
+
[/^\/v1\/packs-test\//, `${SEAMS_PREFIX}/packs-test/`],
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
/** The v2 address of a v1 seam path; a non-seam path is returned unchanged. */
|
|
27
|
+
export function seamPath(v1Path: string): string {
|
|
28
|
+
for (const [re, to] of V1_SEAM_PREFIXES) if (re.test(v1Path)) return v1Path.replace(re, to);
|
|
29
|
+
return v1Path;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isSeamPath(path: string): boolean {
|
|
33
|
+
return V1_SEAM_PREFIXES.some(([re]) => re.test(path)) || path.startsWith(`${SEAMS_PREFIX}/`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Whether a (v2) discovery document advertises the seams profile. */
|
|
37
|
+
export function seamsProfileAdvertised(doc: Readonly<Record<string, unknown>> | null | undefined): boolean {
|
|
38
|
+
const conf = doc?.['conformance'];
|
|
39
|
+
return typeof conf === 'object' && conf !== null && (conf as Record<string, unknown>)['seamsProfile'] === SEAMS_PROFILE_ID;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The target major the runner selected (RFC 0168 §D.3); 1 when unset. */
|
|
43
|
+
export function targetMajor(): 1 | 2 {
|
|
44
|
+
return process.env['OPENWOP_TARGET_MAJOR'] === '2' ? 2 : 1;
|
|
45
|
+
}
|
|
@@ -80,38 +80,21 @@
|
|
|
80
80
|
* is no longer the only place the exclusion reason lives.
|
|
81
81
|
*/
|
|
82
82
|
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
'
|
|
97
|
-
|
|
98
|
-
'form-content-packs.test.ts',
|
|
99
|
-
'multi-region-effect-vocabulary.test.ts',
|
|
100
|
-
'normative-example-extraction.test.ts',
|
|
101
|
-
'openapi-asyncapi-sdk-parity.test.ts',
|
|
102
|
-
'pack-manifest-extensions.test.ts',
|
|
103
|
-
'protocol-version-grammar.test.ts',
|
|
104
|
-
'registry-declarative-kinds.test.ts',
|
|
105
|
-
'rfc-0147-self-audit.test.ts',
|
|
106
|
-
'rfc-lifecycle-coherence.test.ts',
|
|
107
|
-
'semantic-digest-v2.test.ts',
|
|
108
|
-
'spec-corpus-validity.test.ts',
|
|
109
|
-
'spec-section-citations.test.ts',
|
|
110
|
-
'tool-result-trust-monotone.test.ts',
|
|
111
|
-
'versioned-composition-profiles.test.ts',
|
|
112
|
-
'workflow-chain-internal-flag.test.ts',
|
|
113
|
-
'workload-identity-profile.test.ts',
|
|
114
|
-
]);
|
|
83
|
+
/**
|
|
84
|
+
* Scenarios whose subject is the corpus. Suite 2.0.0 (RFC 0168 §D.1): they
|
|
85
|
+
* live in `src/coherence/`, not `src/scenarios/`, and the set is the DIRECTORY
|
|
86
|
+
* LISTING — one source of truth instead of four (this list, the package.json
|
|
87
|
+
* negations, spec-coherence-scenarios.json and the registry self-test used to
|
|
88
|
+
* each carry a copy). They run in the spec repo's CI (scripts/check-spec-coherence.mjs)
|
|
89
|
+
* and never enter a host bundle; the published tarball excludes the directory.
|
|
90
|
+
*/
|
|
91
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
92
|
+
import { fileURLToPath } from 'node:url';
|
|
93
|
+
import { dirname, join } from 'node:path';
|
|
94
|
+
const COHERENCE_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'coherence');
|
|
95
|
+
export const SPEC_COHERENCE_SCENARIOS: ReadonlySet<string> = new Set(
|
|
96
|
+
existsSync(COHERENCE_DIR) ? readdirSync(COHERENCE_DIR).filter((f) => f.endsWith('.test.ts')).sort() : [],
|
|
97
|
+
);
|
|
115
98
|
|
|
116
99
|
/**
|
|
117
100
|
* Scenarios that gate on `V1_DIR` **and** drive the host, so their `blocked` is
|
package/src/lib/v2.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suite 2.0.0 — helpers for v2 scenarios (`v2-*.test.ts`, target major 2).
|
|
3
|
+
*
|
|
4
|
+
* v2Discovery() the v2 representation of /.well-known/openwop
|
|
5
|
+
* (OpenWOP-Version: 2.0), memoized per worker.
|
|
6
|
+
* familyAdvertised(key) RFC 0169 §A.2: presence of the family record IS the
|
|
7
|
+
* claim; there is no `supported` field. Returns the
|
|
8
|
+
* record or null.
|
|
9
|
+
* v2Validator(name) an Ajv 2020 validator for schemas/v2/<name>, with the
|
|
10
|
+
* whole v2 tree registered so cross-file $refs resolve
|
|
11
|
+
* (SCHEMAS_DIR is the spec-artifacts peer in the
|
|
12
|
+
* published layout — lib/paths.ts).
|
|
13
|
+
* gateFamily(key) the RFC 0148 §B gate for a v2 family: records
|
|
14
|
+
* `inapplicable` (family absent) or `skipped`
|
|
15
|
+
* (opted out) through behaviorGate under the
|
|
16
|
+
* requirement id `openwop.family.<key>`, and returns
|
|
17
|
+
* the record when advertised.
|
|
18
|
+
*/
|
|
19
|
+
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
22
|
+
import addFormats from 'ajv-formats';
|
|
23
|
+
import { driver } from './driver.js';
|
|
24
|
+
import { SCHEMAS_DIR } from './paths.js';
|
|
25
|
+
import { behaviorGate } from './behavior-gate.js';
|
|
26
|
+
|
|
27
|
+
let cached: Record<string, unknown> | null | undefined;
|
|
28
|
+
export async function v2Discovery(): Promise<Record<string, unknown> | null> {
|
|
29
|
+
if (cached !== undefined) return cached;
|
|
30
|
+
const res = await driver.get('/.well-known/openwop', { authenticated: false, headers: { 'OpenWOP-Version': '2.0' } });
|
|
31
|
+
cached = res.status === 200 && res.json && typeof res.json === 'object' ? (res.json as Record<string, unknown>) : null;
|
|
32
|
+
return cached;
|
|
33
|
+
}
|
|
34
|
+
export function resetV2Discovery(): void { cached = undefined; }
|
|
35
|
+
|
|
36
|
+
export async function familyAdvertised(key: string): Promise<Record<string, unknown> | null> {
|
|
37
|
+
const doc = await v2Discovery();
|
|
38
|
+
const rec = doc?.[key];
|
|
39
|
+
return rec && typeof rec === 'object' && !Array.isArray(rec) ? (rec as Record<string, unknown>) : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function gateFamily(key: string): Promise<Record<string, unknown> | null> {
|
|
43
|
+
const rec = await familyAdvertised(key);
|
|
44
|
+
return behaviorGate(`family.${key}`, rec !== null) ? rec : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let ajv: Ajv2020 | undefined;
|
|
48
|
+
function v2Ajv(): Ajv2020 {
|
|
49
|
+
if (ajv) return ajv;
|
|
50
|
+
ajv = new Ajv2020({ allErrors: true, strict: false });
|
|
51
|
+
addFormats(ajv);
|
|
52
|
+
const root = join(SCHEMAS_DIR, 'v2');
|
|
53
|
+
const walk = (d: string): string[] => readdirSync(d).flatMap((n) => { const p = join(d, n); return statSync(p).isDirectory() ? walk(p) : p.endsWith('.schema.json') ? [p] : []; });
|
|
54
|
+
for (const p of walk(root)) { try { ajv.addSchema(JSON.parse(readFileSync(p, 'utf8')) as Record<string, unknown>); } catch { /* a duplicate $id is registered once */ } }
|
|
55
|
+
return ajv;
|
|
56
|
+
}
|
|
57
|
+
export function v2Validator(name: string): (doc: unknown) => { ok: boolean; errors: string } {
|
|
58
|
+
const id = `https://openwop.dev/spec/v2/${name.endsWith('.schema.json') ? name : `${name}.schema.json`}`;
|
|
59
|
+
const a = v2Ajv();
|
|
60
|
+
const validate = a.getSchema(id) ?? a.compile(JSON.parse(readFileSync(join(SCHEMAS_DIR, 'v2', ...name.split('/')), 'utf8')) as Record<string, unknown>);
|
|
61
|
+
return (doc: unknown) => ({ ok: validate(doc) as boolean, errors: a.errorsText(validate.errors, { separator: '; ' }) });
|
|
62
|
+
}
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
36
36
|
import { A2AFakePeer } from '../lib/a2a-fake-peer.js';
|
|
37
|
+
import { req } from '../lib/requirement-ids.js';
|
|
37
38
|
|
|
38
39
|
export const HOST_CALLBACK_NOT_REQUIRED = 'the suite drives both ends itself: it boots the dual-era A2AFakePeer in-process and reads its agent card directly; no host connection is originated';
|
|
39
40
|
|
|
@@ -57,24 +58,24 @@ describe('RFC 0152 — the suite peer speaks A2A 1.0 (dual-era A2AFakePeer)', ()
|
|
|
57
58
|
expect(res.status).toBe(200);
|
|
58
59
|
const card = (await res.json()) as Record<string, unknown>;
|
|
59
60
|
const ifaces = card['supportedInterfaces'] as Array<{ url: string; protocolBinding: string; protocolVersion: string }>;
|
|
60
|
-
expect(Array.isArray(ifaces) && ifaces.length > 0, '1.0 REQUIRES supportedInterfaces[]').toBe(true);
|
|
61
|
+
expect(Array.isArray(ifaces) && ifaces.length > 0, req('openwop.it.a2a-1-0-agent-card.the-agent-card-is-1-0-shaped-supportedinterfaces-no-top-level-url-protocolversio', 'RFC 0152 §A/§B/§C/§D', '1.0 REQUIRES supportedInterfaces[]')).toBe(true);
|
|
61
62
|
expect(ifaces.map((i) => i.protocolVersion)).toEqual(['1.0', '0.3']);
|
|
62
63
|
for (const i of ifaces) {
|
|
63
64
|
expect(i.protocolBinding).toBe('JSONRPC');
|
|
64
65
|
expect(i.url.startsWith(peer.endpoint())).toBe(true);
|
|
65
66
|
}
|
|
66
|
-
expect(card['url'], '1.0 removed top-level url').toBeUndefined();
|
|
67
|
-
expect(card['protocolVersion'], '1.0 removed top-level protocolVersion (it is per interface)').toBeUndefined();
|
|
67
|
+
expect(card['url'], req('openwop.it.a2a-1-0-agent-card.the-agent-card-is-1-0-shaped-supportedinterfaces-no-top-level-url-protocolversio', 'RFC 0152 §A/§B/§C/§D', '1.0 removed top-level url')).toBeUndefined();
|
|
68
|
+
expect(card['protocolVersion'], req('openwop.it.a2a-1-0-agent-card.the-agent-card-is-1-0-shaped-supportedinterfaces-no-top-level-url-protocolversio', 'RFC 0152 §A/§B/§C/§D', '1.0 removed top-level protocolVersion (it is per interface)')).toBeUndefined();
|
|
68
69
|
const caps = card['capabilities'] as Record<string, unknown>;
|
|
69
70
|
expect(caps['extendedAgentCard']).toBe(false);
|
|
70
|
-
expect(Array.isArray(card['skills']) && (card['skills'] as unknown[]).length > 0, 'skills[] REQUIRED').toBe(true);
|
|
71
|
-
for (const k of ['name', 'description', 'version', 'defaultInputModes', 'defaultOutputModes']) expect(card[k], `${k} REQUIRED`).toBeDefined();
|
|
71
|
+
expect(Array.isArray(card['skills']) && (card['skills'] as unknown[]).length > 0, req('openwop.it.a2a-1-0-agent-card.the-agent-card-is-1-0-shaped-supportedinterfaces-no-top-level-url-protocolversio', 'RFC 0152 §A/§B/§C/§D', 'skills[] REQUIRED')).toBe(true);
|
|
72
|
+
for (const k of ['name', 'description', 'version', 'defaultInputModes', 'defaultOutputModes']) expect(card[k], req('openwop.it.a2a-1-0-agent-card.the-agent-card-is-1-0-shaped-supportedinterfaces-no-top-level-url-protocolversio', 'RFC 0152 §A/§B/§C/§D', `${k} REQUIRED`)).toBeDefined();
|
|
72
73
|
});
|
|
73
74
|
|
|
74
75
|
it('the card shape follows the era asked for: A2A-Version: 0.3 on the GET returns the 0.3 card from the same peer', async () => {
|
|
75
76
|
const res = await fetch(`${peer.endpoint()}/.well-known/agent-card.json`, { headers: { 'A2A-Version': '0.3' } });
|
|
76
77
|
const card = (await res.json()) as Record<string, unknown>;
|
|
77
|
-
expect(card['protocolVersion']).toBe('0.3.0');
|
|
78
|
+
expect(card['protocolVersion'], req('openwop.it.a2a-1-0-agent-card.the-card-shape-follows-the-era-asked-for-a2a-version-0-3-on-the-get-returns-the', 'RFC 0152 §A/§B/§C/§D', 'the card shape follows the era asked for: A2A-Version: 0.3 on the GET returns the 0.3 card from the same peer')).toBe('0.3.0');
|
|
78
79
|
expect(typeof card['url']).toBe('string');
|
|
79
80
|
expect(card['supportedInterfaces']).toBeUndefined();
|
|
80
81
|
// and the default-constructed peer (0.3-first) serves the 0.3 card to a header-less GET
|
|
@@ -95,7 +96,7 @@ describe('RFC 0152 — the suite peer speaks A2A 1.0 (dual-era A2AFakePeer)', ()
|
|
|
95
96
|
await legacy.start(0);
|
|
96
97
|
try {
|
|
97
98
|
const card = (await (await fetch(`${legacy.endpoint()}/.well-known/agent-card.json`)).json()) as Record<string, unknown>;
|
|
98
|
-
expect(card['protocolVersion']).toBe('0.3.0');
|
|
99
|
+
expect(card['protocolVersion'], req('openwop.it.a2a-1-0-agent-card.a-legacy-only-peer-serves-the-0-3-card-shape-follows-revision-not-calendar', 'RFC 0152 §A/§B/§C/§D', 'a legacy-only peer serves the 0.3 card — shape follows revision, not calendar')).toBe('0.3.0');
|
|
99
100
|
expect(typeof card['url']).toBe('string');
|
|
100
101
|
expect(card['supportedInterfaces']).toBeUndefined();
|
|
101
102
|
} finally {
|
|
@@ -105,14 +106,14 @@ describe('RFC 0152 — the suite peer speaks A2A 1.0 (dual-era A2AFakePeer)', ()
|
|
|
105
106
|
|
|
106
107
|
it('A2A-Version absent ⇒ 0.3 semantics; message/send returns a 0.3 task', async () => {
|
|
107
108
|
const r = await rpc(peer.endpoint(), 'message/send', { message: { kind: 'message', messageId: 'm1', role: 'user', parts: [{ kind: 'text', text: 'hi' }] } });
|
|
108
|
-
expect(r.error).toBeUndefined();
|
|
109
|
+
expect(r.error, req('openwop.it.a2a-1-0-agent-card.a2a-version-absent-0-3-semantics-message-send-returns-a-0-3-task', 'RFC 0152 §A/§B/§C/§D', 'A2A-Version absent ⇒ 0.3 semantics; message/send returns a 0.3 task')).toBeUndefined();
|
|
109
110
|
expect(r.result?.['kind']).toBe('task');
|
|
110
111
|
expect((r.result?.['status'] as { state: string }).state).toBe('submitted');
|
|
111
112
|
});
|
|
112
113
|
|
|
113
114
|
it('an unsupported version fails -32009 VERSION_NOT_SUPPORTED with supportedVersions[] (HTTP 400)', async () => {
|
|
114
115
|
const r = await rpc(peer.endpoint(), 'SendMessage', { message: { messageId: 'm2', role: 'ROLE_USER', parts: [{ text: 'hi' }] } }, '99.0');
|
|
115
|
-
expect(r.status).toBe(400);
|
|
116
|
+
expect(r.status, req('openwop.it.a2a-1-0-agent-card.an-unsupported-version-fails-32009-version-not-supported-with-supportedversions', 'RFC 0152 §A/§B/§C/§D', 'an unsupported version fails -32009 VERSION_NOT_SUPPORTED with supportedVersions[] (HTTP 400)')).toBe(400);
|
|
116
117
|
expect(r.error?.code).toBe(-32009);
|
|
117
118
|
expect(r.error?.data?.['reason']).toBe('VERSION_NOT_SUPPORTED');
|
|
118
119
|
expect(r.error?.data?.['supportedVersions']).toEqual(['1.0', '0.3']); // constructor order
|
|
@@ -123,7 +124,7 @@ describe('RFC 0152 — the suite peer speaks A2A 1.0 (dual-era A2AFakePeer)', ()
|
|
|
123
124
|
await only10.start(0);
|
|
124
125
|
try {
|
|
125
126
|
const r = await rpc(only10.endpoint(), 'message/send', { message: { parts: [] } });
|
|
126
|
-
expect(r.error?.code).toBe(-32009);
|
|
127
|
+
expect(r.error?.code, req('openwop.it.a2a-1-0-agent-card.a-1-0-only-peer-rejects-a-header-less-request-which-is-0-3-by-rule', 'RFC 0152 §A/§B/§C/§D', 'a 1.0-only peer rejects a header-less request (which is 0.3 by rule)')).toBe(-32009);
|
|
127
128
|
expect(r.error?.data?.['requested']).toBe('0.3');
|
|
128
129
|
} finally {
|
|
129
130
|
await only10.stop();
|
|
@@ -135,8 +136,8 @@ describe('RFC 0152 — the suite peer speaks A2A 1.0 (dual-era A2AFakePeer)', ()
|
|
|
135
136
|
const r = await rpc(peer.endpoint(), 'SendMessage', { message: { messageId: 'm3', role: 'ROLE_USER', parts: [{ text: 'hello 1.0' }] } }, '1.0');
|
|
136
137
|
expect(r.error).toBeUndefined();
|
|
137
138
|
const task = r.result?.['task'] as Record<string, unknown>;
|
|
138
|
-
expect(task, 'SendMessageResponse is a oneof payload: { task } | { message }').toBeDefined();
|
|
139
|
-
expect(task['kind'], '1.0 removed the kind discriminator').toBeUndefined();
|
|
139
|
+
expect(task, req('openwop.it.a2a-1-0-agent-card.sendmessage-1-0-returns-task-with-task-state-role-part-oneof-no-kind-status-time', 'RFC 0152 §A/§B/§C/§D', 'SendMessageResponse is a oneof payload: { task } | { message }')).toBeDefined();
|
|
140
|
+
expect(task['kind'], req('openwop.it.a2a-1-0-agent-card.sendmessage-1-0-returns-task-with-task-state-role-part-oneof-no-kind-status-time', 'RFC 0152 §A/§B/§C/§D', '1.0 removed the kind discriminator')).toBeUndefined();
|
|
140
141
|
const status = task['status'] as { state: string; timestamp?: string };
|
|
141
142
|
expect(status.state).toBe('TASK_STATE_SUBMITTED');
|
|
142
143
|
expect(typeof status.timestamp).toBe('string');
|
|
@@ -144,7 +145,7 @@ describe('RFC 0152 — the suite peer speaks A2A 1.0 (dual-era A2AFakePeer)', ()
|
|
|
144
145
|
expect(history[0]?.['role']).toBe('ROLE_USER');
|
|
145
146
|
const part = (history[0]?.['parts'] as Array<Record<string, unknown>>)[0]!;
|
|
146
147
|
expect(part['text']).toBe('hello 1.0');
|
|
147
|
-
expect(part['kind'], 'Part is a oneof — discriminate by member presence').toBeUndefined();
|
|
148
|
+
expect(part['kind'], req('openwop.it.a2a-1-0-agent-card.sendmessage-1-0-returns-task-with-task-state-role-part-oneof-no-kind-status-time', 'RFC 0152 §A/§B/§C/§D', 'Part is a oneof — discriminate by member presence')).toBeUndefined();
|
|
148
149
|
expect(Array.isArray(task['artifacts'])).toBe(true);
|
|
149
150
|
});
|
|
150
151
|
|
|
@@ -153,7 +154,7 @@ describe('RFC 0152 — the suite peer speaks A2A 1.0 (dual-era A2AFakePeer)', ()
|
|
|
153
154
|
const sent = await rpc(peer.endpoint(), 'SendMessage', { message: { messageId: 'm4', role: 'ROLE_USER', parts: [{ text: 'x' }] } }, '1.0');
|
|
154
155
|
const id = (sent.result?.['task'] as { id: string }).id;
|
|
155
156
|
const got = await rpc(peer.endpoint(), 'GetTask', { id }, '1.0');
|
|
156
|
-
expect((got.result as { status: { state: string } }).status.state).toBe('TASK_STATE_SUBMITTED');
|
|
157
|
+
expect((got.result as { status: { state: string } }).status.state, req('openwop.it.a2a-1-0-agent-card.gettask-canceltask-listtasks-per-d-1-incl-task-not-cancelable-on-a-terminal-task', 'RFC 0152 §A/§B/§C/§D', 'GetTask / CancelTask / ListTasks per §D.1, incl. TASK_NOT_CANCELABLE on a terminal task')).toBe('TASK_STATE_SUBMITTED');
|
|
157
158
|
const listed = await rpc(peer.endpoint(), 'ListTasks', { status: 'TASK_STATE_SUBMITTED' }, '1.0');
|
|
158
159
|
expect((listed.result as { totalSize: number }).totalSize).toBe(1);
|
|
159
160
|
const cancelled = await rpc(peer.endpoint(), 'CancelTask', { id }, '1.0');
|
|
@@ -168,12 +169,12 @@ describe('RFC 0152 — the suite peer speaks A2A 1.0 (dual-era A2AFakePeer)', ()
|
|
|
168
169
|
|
|
169
170
|
it('a 0.3 method name under a 1.0 header is method-not-found — loudly', async () => {
|
|
170
171
|
const r = await rpc(peer.endpoint(), 'message/send', { message: { parts: [] } }, '1.0');
|
|
171
|
-
expect(r.status).toBe(404);
|
|
172
|
+
expect(r.status, req('openwop.it.a2a-1-0-agent-card.a-0-3-method-name-under-a-1-0-header-is-method-not-found-loudly', 'RFC 0152 §A/§B/§C/§D', 'a 0.3 method name under a 1.0 header is method-not-found — loudly')).toBe(404);
|
|
172
173
|
expect(r.error?.code).toBe(-32601);
|
|
173
174
|
});
|
|
174
175
|
|
|
175
176
|
it('streaming is honestly unsupported: SubscribeToTask ⇒ UNSUPPORTED_OPERATION, matching capabilities.streaming=false', async () => {
|
|
176
177
|
const r = await rpc(peer.endpoint(), 'SubscribeToTask', { id: 'task-1' }, '1.0');
|
|
177
|
-
expect(r.error?.code).toBe(-32004);
|
|
178
|
+
expect(r.error?.code, req('openwop.it.a2a-1-0-agent-card.streaming-is-honestly-unsupported-subscribetotask-unsupported-operation-matching', 'RFC 0152 §A/§B/§C/§D', 'streaming is honestly unsupported: SubscribeToTask ⇒ UNSUPPORTED_OPERATION, matching capabilities.streaming=false')).toBe(-32004);
|
|
178
179
|
});
|
|
179
180
|
});
|
|
@@ -27,6 +27,7 @@ import { describe, it, expect } from 'vitest';
|
|
|
27
27
|
import { driver } from '../lib/driver.js';
|
|
28
28
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
29
29
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
30
|
+
import { req } from '../lib/requirement-ids.js';
|
|
30
31
|
|
|
31
32
|
const PROFILE = 'a2a-1.0';
|
|
32
33
|
const STATES_10 = [
|
|
@@ -75,27 +76,27 @@ describe('RFC 0152 §D — a2a-1.0-task-roundtrip (host as A2A 1.0 server, gated
|
|
|
75
76
|
it('SendMessage @1.0 creates a task whose id is the runId, in 1.0 spelling; GetTask reads it back; unknown id is TASK_NOT_FOUND', async () => {
|
|
76
77
|
if (!behaviorGate(PROFILE, await claims10())) return;
|
|
77
78
|
const url = await jsonrpc10Url();
|
|
78
|
-
expect(url,
|
|
79
|
+
expect(url, req('openwop.it.a2a-1-0-task-roundtrip.sendmessage-1-0-creates-a-task-whose-id-is-the-runid-in-1-0-spelling-gettask-rea', 'a2a-integration.md §C', 'a `a2a-1.0` host MUST publish a JSONRPC/1.0 interface in its card')).not.toBeNull();
|
|
79
80
|
const sent = await rpc10(url!, 'SendMessage', {
|
|
80
81
|
message: { messageId: `conf-${Date.now()}`, role: 'ROLE_USER', parts: [{ text: 'openwop conformance a2a-1.0 roundtrip' }] },
|
|
81
82
|
configuration: { returnImmediately: true },
|
|
82
83
|
}, 1);
|
|
83
|
-
expect(sent.status,
|
|
84
|
-
expect(sent.error,
|
|
84
|
+
expect(sent.status, req('openwop.it.a2a-1-0-task-roundtrip.sendmessage-1-0-creates-a-task-whose-id-is-the-runid-in-1-0-spelling-gettask-rea', 'a2a-integration.md §D.1', 'SendMessage MUST be accepted on the 1.0 interface')).toBe(200);
|
|
85
|
+
expect(sent.error, req('openwop.it.a2a-1-0-task-roundtrip.sendmessage-1-0-creates-a-task-whose-id-is-the-runid-in-1-0-spelling-gettask-rea', 'a2a-integration.md §D.1', `SendMessage MUST NOT error: ${JSON.stringify(sent.error)}`)).toBeUndefined();
|
|
85
86
|
const result = sent.result ?? {};
|
|
86
87
|
// SendMessageResponse is a oneof: { task } | { message }. A run-creating host answers with a task.
|
|
87
88
|
const task = (result['task'] ?? (result['id'] !== undefined ? result : undefined)) as { id?: string; contextId?: string; status?: { state?: string; timestamp?: string }; history?: Array<{ role?: string; parts?: Array<Record<string, unknown>> }>; kind?: unknown } | undefined;
|
|
88
|
-
expect(task,
|
|
89
|
-
expect(task!.kind,
|
|
89
|
+
expect(task, req('openwop.it.a2a-1-0-task-roundtrip.sendmessage-1-0-creates-a-task-whose-id-is-the-runid-in-1-0-spelling-gettask-rea', 'a2a-integration.md §D.1', 'SendMessage without taskId MUST create a run and return `{ task }`')).toBeDefined();
|
|
90
|
+
expect(task!.kind, req('openwop.it.a2a-1-0-task-roundtrip.sendmessage-1-0-creates-a-task-whose-id-is-the-runid-in-1-0-spelling-gettask-rea', 'a2a-integration.md §D.5', '1.0 has no `kind` discriminator')).toBeUndefined();
|
|
90
91
|
expect(typeof task!.id).toBe('string');
|
|
91
|
-
expect(STATES_10,
|
|
92
|
+
expect(STATES_10, req('openwop.it.a2a-1-0-task-roundtrip.sendmessage-1-0-creates-a-task-whose-id-is-the-runid-in-1-0-spelling-gettask-rea', 'a2a-integration.md §D.4', 'status.state MUST use the 1.0 TASK_STATE_* spelling')).toContain(task!.status?.state);
|
|
92
93
|
for (const m of task!.history ?? []) {
|
|
93
|
-
expect(['ROLE_USER', 'ROLE_AGENT'],
|
|
94
|
-
for (const p of m.parts ?? []) expect(p['kind'],
|
|
94
|
+
expect(['ROLE_USER', 'ROLE_AGENT'], req('openwop.it.a2a-1-0-task-roundtrip.sendmessage-1-0-creates-a-task-whose-id-is-the-runid-in-1-0-spelling-gettask-rea', 'a2a-integration.md §D.2', 'history roles are ROLE_*')).toContain(m.role);
|
|
95
|
+
for (const p of m.parts ?? []) expect(p['kind'], req('openwop.it.a2a-1-0-task-roundtrip.sendmessage-1-0-creates-a-task-whose-id-is-the-runid-in-1-0-spelling-gettask-rea', 'a2a-integration.md §D.3', 'Part is a oneof — no `kind`')).toBeUndefined();
|
|
95
96
|
}
|
|
96
97
|
// Task.id IS the runId (RFC 0100): the OpenWOP snapshot for it MUST exist for the same caller.
|
|
97
98
|
const snap = await driver.get(`/v1/runs/${encodeURIComponent(task!.id!)}`);
|
|
98
|
-
expect(snap.status,
|
|
99
|
+
expect(snap.status, req('openwop.it.a2a-1-0-task-roundtrip.sendmessage-1-0-creates-a-task-whose-id-is-the-runid-in-1-0-spelling-gettask-rea', 'a2a-integration.md §D.4', '`Task.id` MUST be the backing runId — GET /v1/runs/{id} resolves it for the same principal')).toBe(200);
|
|
99
100
|
// GetTask reads it back with the same id and a valid 1.0 state.
|
|
100
101
|
const got = await rpc10(url!, 'GetTask', { id: task!.id }, 2);
|
|
101
102
|
expect(got.error).toBeUndefined();
|
|
@@ -104,7 +105,7 @@ describe('RFC 0152 §D — a2a-1.0-task-roundtrip (host as A2A 1.0 server, gated
|
|
|
104
105
|
expect(STATES_10).toContain(gotTask.status?.state);
|
|
105
106
|
// Unknown id — and, per §E, a cross-tenant id — MUST be TASK_NOT_FOUND, never forbidden.
|
|
106
107
|
const missing = await rpc10(url!, 'GetTask', { id: `run_does_not_exist_${Date.now()}` }, 3);
|
|
107
|
-
expect(missing.error?.code,
|
|
108
|
+
expect(missing.error?.code, req('openwop.it.a2a-1-0-task-roundtrip.sendmessage-1-0-creates-a-task-whose-id-is-the-runid-in-1-0-spelling-gettask-rea', 'a2a-integration.md §D.7/§E', 'an unknown or unreadable task MUST be TASK_NOT_FOUND (-32001) — no enumeration')).toBe(-32001);
|
|
108
109
|
// Tidy: cancel the run we created (best effort; terminal runs answer TASK_NOT_CANCELABLE which is fine).
|
|
109
110
|
await rpc10(url!, 'CancelTask', { id: task!.id }, 4);
|
|
110
111
|
});
|
|
@@ -29,6 +29,7 @@ import { softSkip } from '../lib/soft-skip.js';
|
|
|
29
29
|
import { driver } from '../lib/driver.js';
|
|
30
30
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
31
31
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
32
|
+
import { req } from '../lib/requirement-ids.js';
|
|
32
33
|
|
|
33
34
|
const PROFILE = 'a2a-1.0';
|
|
34
35
|
|
|
@@ -81,14 +82,14 @@ describe('RFC 0152 §C — a2a-card-runtime-consistent (gated on a2a.profiles
|
|
|
81
82
|
if (!behaviorGate(PROFILE, await claims10())) return;
|
|
82
83
|
const caps = (await a2a())!;
|
|
83
84
|
const card = await fetchCard(caps);
|
|
84
|
-
expect(card,
|
|
85
|
+
expect(card, req('openwop.it.a2a-card-runtime-consistency.the-card-is-1-0-shaped-and-reachable-at-agentcardurl', 'a2a-integration.md §C', '`agentCardUrl` MUST resolve to the Agent Card (200)')).not.toBeNull();
|
|
85
86
|
expect(
|
|
86
87
|
Array.isArray(card!.supportedInterfaces) && card!.supportedInterfaces!.length > 0,
|
|
87
|
-
|
|
88
|
+
req('openwop.it.a2a-card-runtime-consistency.the-card-is-1-0-shaped-and-reachable-at-agentcardurl', 'a2a-integration.md §C', 'a host claiming `a2a-1.0` MUST publish a 1.0 card: `supportedInterfaces[]` REQUIRED (≥1)'),
|
|
88
89
|
).toBe(true);
|
|
89
|
-
expect(card!.url,
|
|
90
|
-
expect(card!.protocolVersion,
|
|
91
|
-
expect(Array.isArray(card!.skills) && card!.skills!.length > 0,
|
|
90
|
+
expect(card!.url, req('openwop.it.a2a-card-runtime-consistency.the-card-is-1-0-shaped-and-reachable-at-agentcardurl', 'a2a-integration.md §C', '1.0 removed the top-level `url` — a card with both shapes is neither')).toBeUndefined();
|
|
91
|
+
expect(card!.protocolVersion, req('openwop.it.a2a-card-runtime-consistency.the-card-is-1-0-shaped-and-reachable-at-agentcardurl', 'a2a-integration.md §C', '1.0 removed the top-level `protocolVersion` (per interface now)')).toBeUndefined();
|
|
92
|
+
expect(Array.isArray(card!.skills) && card!.skills!.length > 0, req('openwop.it.a2a-card-runtime-consistency.the-card-is-1-0-shaped-and-reachable-at-agentcardurl', 'a2a-integration.md §C', '`skills[]` MUST be non-empty — one per invocable workflow')).toBe(true);
|
|
92
93
|
});
|
|
93
94
|
|
|
94
95
|
it('a header-less card GET returns the 0.3-shaped card while a2a-0.3-legacy is advertised (§B receiver rule applied to discovery — S18 Q1)', async () => {
|
|
@@ -97,17 +98,17 @@ describe('RFC 0152 §C — a2a-card-runtime-consistent (gated on a2a.profiles
|
|
|
97
98
|
if (typeof caps.agentCardUrl !== 'string') return softSkip('blocked', 'no agentCardUrl advertised');
|
|
98
99
|
const legacy = (caps.protocolVersions ?? []).includes('0.3');
|
|
99
100
|
const res = await fetch(caps.agentCardUrl, { headers: { accept: 'application/json' } });
|
|
100
|
-
expect(res.status,
|
|
101
|
+
expect(res.status, req('openwop.it.a2a-card-runtime-consistency.a-header-less-card-get-returns-the-0-3-shaped-card-while-a2a-0-3-legacy-is-adver', 'a2a-integration.md §C', 'a header-less card GET MUST succeed — discovery must not fail')).toBe(200);
|
|
101
102
|
const card = (await res.json()) as Card10 & { url?: string; protocolVersion?: string };
|
|
102
103
|
if (legacy) {
|
|
103
104
|
// While 0.3 is advertised the header-less card is the 0.3 shape: an
|
|
104
105
|
// external 0.3 client reading `card.url` keeps working through the
|
|
105
106
|
// legacy window — the point of advertising `a2a-0.3-legacy` at all.
|
|
106
|
-
expect(typeof card.url,
|
|
107
|
-
expect(card.supportedInterfaces,
|
|
107
|
+
expect(typeof card.url, req('openwop.it.a2a-card-runtime-consistency.a-header-less-card-get-returns-the-0-3-shaped-card-while-a2a-0-3-legacy-is-adver', 'a2a-integration.md §C', 'header-less = 0.3 (§B receiver rule): the card MUST carry the 0.3 top-level `url` while `protocolVersions ∋ 0.3`')).toBe('string');
|
|
108
|
+
expect(card.supportedInterfaces, req('openwop.it.a2a-card-runtime-consistency.a-header-less-card-get-returns-the-0-3-shaped-card-while-a2a-0-3-legacy-is-adver', 'a2a-integration.md §C', 'the header-less card MUST NOT be the 1.0 shape while 0.3 is advertised (a card with both shapes is neither)')).toBeUndefined();
|
|
108
109
|
} else {
|
|
109
110
|
// 0.3 dropped: the preferred-version (1.0) card is served header-less.
|
|
110
|
-
expect(Array.isArray(card.supportedInterfaces) && card.supportedInterfaces!.length > 0,
|
|
111
|
+
expect(Array.isArray(card.supportedInterfaces) && card.supportedInterfaces!.length > 0, req('openwop.it.a2a-card-runtime-consistency.a-header-less-card-get-returns-the-0-3-shaped-card-while-a2a-0-3-legacy-is-adver', 'a2a-integration.md §C', 'with 0.3 dropped, the header-less card is the preferredVersion (1.0) card')).toBe(true);
|
|
111
112
|
}
|
|
112
113
|
});
|
|
113
114
|
|
|
@@ -115,12 +116,12 @@ describe('RFC 0152 §C — a2a-card-runtime-consistent (gated on a2a.profiles
|
|
|
115
116
|
if (!behaviorGate(PROFILE, await claims10())) return;
|
|
116
117
|
const caps = (await a2a())!;
|
|
117
118
|
const card = await fetchCard(caps);
|
|
118
|
-
if (card === null) return; // asserted above
|
|
119
|
+
if (card === null) return softSkip('blocked', 'precondition not met — `card === null` returned early (asserted above) (seam, prior step, or fixture unavailable)'); // asserted above
|
|
119
120
|
const fromCard = [...new Set((card.supportedInterfaces ?? []).map((i) => i.protocolVersion).filter((v): v is string => typeof v === 'string'))].sort();
|
|
120
121
|
const fromDiscovery = [...new Set(caps.protocolVersions ?? [])].sort();
|
|
121
122
|
expect(
|
|
122
123
|
fromCard,
|
|
123
|
-
|
|
124
|
+
req('openwop.it.a2a-card-runtime-consistency.the-set-of-supportedinterfaces-protocolversion-equals-capabilities-a2a-protocolv',
|
|
124
125
|
'a2a-integration.md §C',
|
|
125
126
|
'the SET of `supportedInterfaces[].protocolVersion` MUST equal `capabilities.a2a.protocolVersions` — two documents, one fact; ' +
|
|
126
127
|
'a version advertised in one and not the other is card/runtime drift (`a2a-card-runtime-consistent`)',
|
|
@@ -132,14 +133,14 @@ describe('RFC 0152 §C — a2a-card-runtime-consistent (gated on a2a.profiles
|
|
|
132
133
|
if (!behaviorGate(PROFILE, await claims10())) return;
|
|
133
134
|
const caps = (await a2a())!;
|
|
134
135
|
const card = await fetchCard(caps);
|
|
135
|
-
if (card === null) return;
|
|
136
|
+
if (card === null) return softSkip('blocked', 'precondition not met — `card === null` returned early (seam, prior step, or fixture unavailable)');
|
|
136
137
|
const ifaces = card.supportedInterfaces ?? [];
|
|
137
138
|
expect(
|
|
138
139
|
ifaces.some((i) => i.protocolBinding === 'JSONRPC' && i.protocolVersion === '1.0' && typeof i.url === 'string'),
|
|
139
|
-
|
|
140
|
+
req('openwop.it.a2a-card-runtime-consistency.a2a-1-0-must-serve-the-json-rpc-binding-at-1-0-the-mandatory-floor-and-should-li', 'a2a-integration.md §C', 'the `a2a-1.0` profile floor is the JSON-RPC binding at 1.0; HTTP+JSON / gRPC are optional additions'),
|
|
140
141
|
).toBe(true);
|
|
141
142
|
for (const i of ifaces) {
|
|
142
|
-
expect(['JSONRPC', 'GRPC', 'HTTP+JSON'],
|
|
143
|
+
expect(['JSONRPC', 'GRPC', 'HTTP+JSON'], req('openwop.it.a2a-card-runtime-consistency.a2a-1-0-must-serve-the-json-rpc-binding-at-1-0-the-mandatory-floor-and-should-li', 'a2a-integration.md §C', 'protocolBinding uses the upstream binding names')).toContain(i.protocolBinding);
|
|
143
144
|
}
|
|
144
145
|
if (typeof caps.preferredVersion === 'string' && ifaces.length > 0) {
|
|
145
146
|
// SHOULD, so a soft expectation: report, do not fail.
|
|
@@ -154,14 +155,14 @@ describe('RFC 0152 §C — a2a-card-runtime-consistent (gated on a2a.profiles
|
|
|
154
155
|
if (!behaviorGate(PROFILE, await claims10())) return;
|
|
155
156
|
const caps = (await a2a())!;
|
|
156
157
|
const card = await fetchCard(caps);
|
|
157
|
-
if (card === null) return;
|
|
158
|
+
if (card === null) return softSkip('blocked', 'precondition not met — `card === null` returned early (seam, prior step, or fixture unavailable)');
|
|
158
159
|
expect(
|
|
159
160
|
card.capabilities?.streaming === true,
|
|
160
|
-
|
|
161
|
+
req('openwop.it.a2a-card-runtime-consistency.card-capabilities-streaming-pushnotifications-equal-the-discovery-flags', 'a2a-integration.md §C', '`capabilities.streaming` on the card MUST equal `capabilities.a2a.streaming` on discovery'),
|
|
161
162
|
).toBe(caps.streaming === true);
|
|
162
163
|
expect(
|
|
163
164
|
card.capabilities?.pushNotifications === true,
|
|
164
|
-
|
|
165
|
+
req('openwop.it.a2a-card-runtime-consistency.card-capabilities-streaming-pushnotifications-equal-the-discovery-flags', 'a2a-integration.md §C', '`capabilities.pushNotifications` on the card MUST equal `capabilities.a2a.pushNotifications`'),
|
|
165
166
|
).toBe(caps.pushNotifications === true);
|
|
166
167
|
});
|
|
167
168
|
});
|
|
@@ -33,6 +33,7 @@ import { driver } from '../lib/driver.js';
|
|
|
33
33
|
import { behaviorGate } from '../lib/behavior-gate.js';
|
|
34
34
|
import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
35
35
|
import { getA2AFakePeer } from '../lib/a2a-fake-peer.js';
|
|
36
|
+
import { req } from '../lib/requirement-ids.js';
|
|
36
37
|
|
|
37
38
|
export const REQUIRES_HOST_CALLBACK = "the host issues A2A JSON-RPC calls to the suite's fake peer via /v1/host/sample/a2a/invoke (peerUrl), which the peer records";
|
|
38
39
|
|
|
@@ -75,25 +76,25 @@ describe('RFC 0152 §E — a2a-peer-no-authority-escalation (host as A2A client,
|
|
|
75
76
|
// The peer must actually have answered with the authority-asserting message,
|
|
76
77
|
// or this leg is vacuous.
|
|
77
78
|
const created = peer.taskCount();
|
|
78
|
-
expect(created,
|
|
79
|
+
expect(created, req('openwop.it.a2a-peer-authority.a-peer-reply-that-asserts-approval-scopes-and-a-foreign-task-reference-changes-n', 'RFCS/0152 §E', 'the host MUST have created a task on the peer for this leg to mean anything')).toBeGreaterThan(0);
|
|
79
80
|
const report = (drive.json as { peerAuthority?: PeerAuthorityReport }).peerAuthority;
|
|
80
81
|
if (report === undefined) {
|
|
81
82
|
// Additive seam field not yet implemented: blocked, not passed.
|
|
82
83
|
expect(
|
|
83
84
|
report,
|
|
84
|
-
|
|
85
|
+
req('openwop.it.a2a-peer-authority.a-peer-reply-that-asserts-approval-scopes-and-a-foreign-task-reference-changes-n',
|
|
85
86
|
'host-sample-test-seams.md §22',
|
|
86
87
|
'the invoke seam SHOULD report `peerAuthority: { approvalAdvanced, scopesWidened, referencedTaskDereferenced }` for ' +
|
|
87
88
|
'scenario "peer-asserts-authority"; until it does this requirement is unobservable and resolves to `blocked`, not passed',
|
|
88
89
|
),
|
|
89
90
|
).toBeDefined();
|
|
90
|
-
return;
|
|
91
|
+
return softSkip('blocked', 'precondition not met — `report === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
91
92
|
}
|
|
92
|
-
expect(report.approvalAdvanced,
|
|
93
|
-
expect(report.scopesWidened,
|
|
93
|
+
expect(report.approvalAdvanced, req('openwop.it.a2a-peer-authority.a-peer-reply-that-asserts-approval-scopes-and-a-foreign-task-reference-changes-n', 'RFCS/0152 §E', 'peer content MUST NOT advance an approval gate (a2a-peer-no-authority-escalation)')).toBe(false);
|
|
94
|
+
expect(report.scopesWidened, req('openwop.it.a2a-peer-authority.a-peer-reply-that-asserts-approval-scopes-and-a-foreign-task-reference-changes-n', 'RFCS/0152 §E', 'peer content MUST NOT widen the run’s scopes or tool allowlist')).toBe(false);
|
|
94
95
|
expect(
|
|
95
96
|
report.referencedTaskDereferenced,
|
|
96
|
-
|
|
97
|
+
req('openwop.it.a2a-peer-authority.a-peer-reply-that-asserts-approval-scopes-and-a-foreign-task-reference-changes-n', 'a2a-integration.md §D.2', '`referenceTaskIds[]` are opaque hints — MUST NOT be dereferenced on the peer’s say-so'),
|
|
97
98
|
).toBe(false);
|
|
98
99
|
});
|
|
99
100
|
});
|