@openwop/openwop-conformance 2.42.6 → 2.42.8
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/CHANGELOG.md +34 -0
- package/README.md +3 -3
- package/coverage.md +1 -1
- package/dist/lib/soft-skip.js +13 -0
- package/dist/spec-artifacts.lock.json +2 -2
- package/fixtures/conformance-envelope-retry-attempted.json +2 -1
- package/package.json +2 -2
- package/requirements.json +581 -175
- package/scenario-majors.json +22 -3
- package/schemas/CORPUS-STAMP.json +70 -67
- package/src/lib/audit-checkpoint.ts +72 -0
- package/src/lib/ext-claims.ts +96 -0
- package/src/lib/mock-ai-budgets.ts +40 -0
- package/src/lib/soft-skip.ts +14 -0
- package/src/scenarios/a2a-peer-authority.test.ts +6 -13
- package/src/scenarios/a2a-task-roundtrip.test.ts +6 -34
- package/src/scenarios/a2a-version-negotiation.test.ts +6 -12
- package/src/scenarios/a2ui-surface-delta-transport.test.ts +23 -12
- package/src/scenarios/agent-channel-dispatch.test.ts +13 -4
- package/src/scenarios/agent-live-invocation-bracket.test.ts +1 -1
- package/src/scenarios/agent-manifest-runtime.test.ts +3 -0
- package/src/scenarios/aiEnvelope.capBreached.test.ts +2 -2
- package/src/scenarios/aiEnvelope.contractRefusal.test.ts +2 -2
- package/src/scenarios/aiEnvelope.schemaDrift.test.ts +2 -2
- package/src/scenarios/aiEnvelope.universalKinds.test.ts +2 -2
- package/src/scenarios/aiproviders-selfhosted-honesty.test.ts +2 -2
- package/src/scenarios/append-ordering.test.ts +26 -21
- package/src/scenarios/artifact-auth.test.ts +3 -1
- package/src/scenarios/artifact-type-registration-source.test.ts +2 -2
- package/src/scenarios/audit-checkpoint-signature.test.ts +63 -0
- package/src/scenarios/audit-checkpoint-vectors.test.ts +93 -0
- package/src/scenarios/audit-log-integrity.test.ts +7 -16
- package/src/scenarios/auth-mtls.test.ts +7 -6
- package/src/scenarios/auth-subject-link-key-class.test.ts +3 -3
- package/src/scenarios/auth-subject-link.test.ts +19 -18
- package/src/scenarios/blob-presign-expiry.test.ts +2 -2
- package/src/scenarios/byok-roundtrip.test.ts +4 -1
- package/src/scenarios/compensation-behavior.test.ts +10 -5
- package/src/scenarios/conformance-advertised-seam-required.test.ts +6 -0
- package/src/scenarios/connection-pack-apihosts.test.ts +2 -2
- package/src/scenarios/connection-provider-resolution.test.ts +2 -2
- package/src/scenarios/context-budget-transcript-bound.test.ts +7 -3
- package/src/scenarios/context-summarization-replay.test.ts +2 -0
- package/src/scenarios/cost-attribution.test.ts +3 -1
- package/src/scenarios/debugBundle.test.ts +12 -4
- package/src/scenarios/discovery.test.ts +6 -2
- package/src/scenarios/dispatch-cross-worker-handoff.test.ts +2 -2
- package/src/scenarios/dispatch-input-mapping.test.ts +2 -2
- package/src/scenarios/dispatch-output-mapping.test.ts +15 -2
- package/src/scenarios/distillation-token-budget.test.ts +2 -2
- package/src/scenarios/durability-poison-exhaustion.test.ts +2 -2
- package/src/scenarios/envelope-completion-distinguishes-truncation.test.ts +51 -15
- package/src/scenarios/envelope-nl-to-format-engaged.test.ts +2 -2
- package/src/scenarios/envelope-recovery-applied.test.ts +2 -2
- package/src/scenarios/envelope-refusal-shape.test.ts +2 -2
- package/src/scenarios/envelope-retry-attempted.test.ts +2 -2
- package/src/scenarios/envelope-retry-exhausted.test.ts +2 -2
- package/src/scenarios/envelope-tier-one-subset-static.test.ts +1 -1
- package/src/scenarios/envelope-truncation-cap-exhaustion.test.ts +2 -2
- package/src/scenarios/eventOrdering.test.ts +17 -10
- package/src/scenarios/form-content-instantiation.test.ts +2 -2
- package/src/scenarios/heartbeat-idempotent-no-spam.test.ts +2 -2
- package/src/scenarios/highConcurrency.test.ts +9 -9
- package/src/scenarios/idempotency-concurrent-claim.test.ts +5 -5
- package/src/scenarios/idempotency-key-determinism.test.ts +8 -8
- package/src/scenarios/idempotency.test.ts +6 -6
- package/src/scenarios/idempotencyRetry.test.ts +16 -11
- package/src/scenarios/interrupt-token-matrix.test.ts +24 -11
- package/src/scenarios/mcp-cache-tenant-scope.test.ts +3 -3
- package/src/scenarios/mcp-current-auth-boundary.test.ts +9 -6
- package/src/scenarios/mcp-extension-opacity.test.ts +6 -5
- package/src/scenarios/mcp-mrtr-roundtrip.test.ts +9 -8
- package/src/scenarios/mcp-version-negotiation.test.ts +5 -10
- package/src/scenarios/media-url-inline-cap.test.ts +3 -2
- package/src/scenarios/memory-compaction-provenance-tag.test.ts +2 -0
- package/src/scenarios/memory-compaction-sr1-carry-forward.test.ts +2 -2
- package/src/scenarios/memory-consolidation-idempotent.test.ts +2 -2
- package/src/scenarios/memory-injection-budget.test.ts +2 -2
- package/src/scenarios/multi-party-conversation-behavioral.test.ts +2 -2
- package/src/scenarios/multi-region-idempotency-behavior.test.ts +9 -9
- package/src/scenarios/multi-region-idempotency.test.ts +2 -2
- package/src/scenarios/pack-registry-isolation.test.ts +13 -12
- package/src/scenarios/pack-registry.test.ts +3 -2
- package/src/scenarios/pause-resume.test.ts +30 -11
- package/src/scenarios/prompt-read-workspace-membership-enforced.test.ts +43 -34
- package/src/scenarios/provider-usage.test.ts +2 -2
- package/src/scenarios/redaction.test.ts +7 -5
- package/src/scenarios/redactionAdversarial.test.ts +5 -4
- package/src/scenarios/registry-public.test.ts +5 -1
- package/src/scenarios/replay-divergence-at-refusal.test.ts +10 -7
- package/src/scenarios/replay-fanout-suppression.test.ts +7 -4
- package/src/scenarios/replay-fork-arbitrary.test.ts +2 -2
- package/src/scenarios/replay-side-effect-suppression.test.ts +2 -2
- package/src/scenarios/replayDeterminism.test.ts +9 -4
- package/src/scenarios/runner-ledger.test.ts +4 -0
- package/src/scenarios/runtime-capabilities.test.ts +2 -0
- package/src/scenarios/speech-synthesis-roundtrip.test.ts +1 -1
- package/src/scenarios/streamReconnect.test.ts +2 -2
- package/src/scenarios/subrun-approval-gate.test.ts +2 -2
- package/src/scenarios/subworkflow-input-mapping.test.ts +2 -2
- package/src/scenarios/tool-catalog-projection.test.ts +10 -7
- package/src/scenarios/tool-session-lifecycle.test.ts +1 -1
- package/src/scenarios/trigger-ingestion.test.ts +2 -2
- package/src/scenarios/v2-a2ui-v09-surface.test.ts +15 -0
- package/src/scenarios/v2-client-version-header.test.ts +124 -0
- package/src/scenarios/v2-effect-identity-business-key.test.ts +12 -0
- package/src/scenarios/v2-ext-family-claims.test.ts +62 -0
- package/src/scenarios/v2-ext-rest-transport.test.ts +151 -0
- package/src/scenarios/v2-id-grammar.test.ts +17 -5
- package/src/scenarios/v2-idempotency-in-flight.test.ts +3 -0
- package/src/scenarios/v2-legacy-profiles-absent.test.ts +4 -6
- package/src/scenarios/v2-mcp-tasks.test.ts +1 -1
- package/src/scenarios/v2-min-client-version.test.ts +20 -14
- package/src/scenarios/v2-preferred-version-default.test.ts +19 -1
- package/src/scenarios/v2-refresh-sla.test.ts +6 -5
- package/src/scenarios/v2-sse-last-event-id-cursor.test.ts +3 -3
- package/src/scenarios/v2-sse-last-event-id.test.ts +2 -0
- package/src/scenarios/v2-version-header-honored.test.ts +3 -0
- package/src/scenarios/v2-webhook-durable-delivery.test.ts +2 -0
- package/src/scenarios/v2-webhook-generated-secret.test.ts +76 -0
- package/src/scenarios/v2-webhook-secret-rotation.test.ts +8 -1
- package/src/scenarios/verifier-gating.test.ts +2 -2
- package/src/scenarios/version-fold.test.ts +2 -2
- package/src/scenarios/wasm-pack-invoke-suspended.test.ts +7 -6
- package/src/scenarios/webhook-signed-delivery.test.ts +2 -0
- package/src/scenarios/workflow-chain-host-expansion.test.ts +10 -1
- package/src/scenarios/workflow-variable-format.test.ts +2 -2
- package/src/scenarios/workload-identity-chain-bounds.test.ts +6 -3
- package/vectors/audit-checkpoint-v1.json +260 -0
package/scenario-majors.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "GENERATED by conformance/scripts/generate-scenario-majors.mjs (RFC 0168 §D.3). Do not edit; add a file to BOTH_MAJORS in the generator to target both majors.",
|
|
3
3
|
"counts": {
|
|
4
|
-
"files":
|
|
5
|
-
"v1":
|
|
6
|
-
"v2":
|
|
4
|
+
"files": 567,
|
|
5
|
+
"v1": 451,
|
|
6
|
+
"v2": 128
|
|
7
7
|
},
|
|
8
8
|
"majors": {
|
|
9
9
|
"a2a-1-0-agent-card.test.ts": [
|
|
@@ -249,6 +249,13 @@
|
|
|
249
249
|
"artifact-type-store-without-render.test.ts": [
|
|
250
250
|
1
|
|
251
251
|
],
|
|
252
|
+
"audit-checkpoint-signature.test.ts": [
|
|
253
|
+
1
|
|
254
|
+
],
|
|
255
|
+
"audit-checkpoint-vectors.test.ts": [
|
|
256
|
+
1,
|
|
257
|
+
2
|
|
258
|
+
],
|
|
252
259
|
"audit-log-integrity.test.ts": [
|
|
253
260
|
1
|
|
254
261
|
],
|
|
@@ -1310,6 +1317,9 @@
|
|
|
1310
1317
|
"v2-chain-pin-exact.test.ts": [
|
|
1311
1318
|
2
|
|
1312
1319
|
],
|
|
1320
|
+
"v2-client-version-header.test.ts": [
|
|
1321
|
+
2
|
|
1322
|
+
],
|
|
1313
1323
|
"v2-coherence-not-in-bundle.test.ts": [
|
|
1314
1324
|
2
|
|
1315
1325
|
],
|
|
@@ -1364,6 +1374,12 @@
|
|
|
1364
1374
|
"v2-event-type-closed.test.ts": [
|
|
1365
1375
|
2
|
|
1366
1376
|
],
|
|
1377
|
+
"v2-ext-family-claims.test.ts": [
|
|
1378
|
+
2
|
|
1379
|
+
],
|
|
1380
|
+
"v2-ext-rest-transport.test.ts": [
|
|
1381
|
+
2
|
|
1382
|
+
],
|
|
1367
1383
|
"v2-fork-a-v1-run.test.ts": [
|
|
1368
1384
|
2
|
|
1369
1385
|
],
|
|
@@ -1574,6 +1590,9 @@
|
|
|
1574
1590
|
"v2-webhook-endpoint-verification.test.ts": [
|
|
1575
1591
|
2
|
|
1576
1592
|
],
|
|
1593
|
+
"v2-webhook-generated-secret.test.ts": [
|
|
1594
|
+
2
|
|
1595
|
+
],
|
|
1577
1596
|
"v2-webhook-message-id-stable.test.ts": [
|
|
1578
1597
|
2
|
|
1579
1598
|
],
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "Provenance of @openwop/spec-artifacts (RFC 0168 §D.2). files: SHA-256 per file; the conformance suite compares the installed peer against dist/spec-artifacts.lock.json at start.",
|
|
3
3
|
"package": "@openwop/spec-artifacts",
|
|
4
|
-
"version": "2.42.
|
|
5
|
-
"corpusTag": "v2.42.
|
|
4
|
+
"version": "2.42.8",
|
|
5
|
+
"corpusTag": "v2.42.8",
|
|
6
6
|
"files": {
|
|
7
7
|
"api/.redocly.lint-ignore.yaml": "bf5a8350b88a72fa43f59605ed8d903ed24b6cfccda5e45509c9f6ed9ee4e712",
|
|
8
8
|
"api/asyncapi.yaml": "d5ecb9ee6114582be3b1f662c84bfac9ae96dae7bacb853e461168f70a8e1c7d",
|
|
9
9
|
"api/grpc/openwop.proto": "c3e72bb17cba514ee98feb6434e6c9b6ea6795bfd086489ec69fd882dd1ad977",
|
|
10
10
|
"api/openapi.yaml": "69464bc0e1e71ef3b543a73d4fa4f67202343d2603c80af8033f6c3619ef1b30",
|
|
11
11
|
"api/redocly.yaml": "b0604c89b2ca6d5076ec25725c539dad44a741a811fe524439ee6daef8baa09f",
|
|
12
|
-
"api/seams-v2.yaml": "
|
|
13
|
-
"api/v2/asyncapi.yaml": "
|
|
14
|
-
"api/v2/openapi.yaml": "
|
|
12
|
+
"api/seams-v2.yaml": "deda53b9f8917a1dc4b283e50cb47985c0fd06525c96fdf853c2fdb1a2de5cb8",
|
|
13
|
+
"api/v2/asyncapi.yaml": "a35292b35d003361a9a0a7997557e022641cd869a6aefcd31ab266d0c3fea215",
|
|
14
|
+
"api/v2/openapi.yaml": "8381ff1d3cca7c0610a5f5a885763a625630c8b36bb5c5f6cd1d3f3cceb176a7",
|
|
15
15
|
"api/v2/redocly.yaml": "1e66b60e6118ad11a823bb620678be464d99dfe50a40e3e6f93ec9429b88b34c",
|
|
16
16
|
"schemas/README.md": "0c0b737ffcf8f30e7d2809cec8a498232de710f41443212922ad8337cdde0b51",
|
|
17
17
|
"schemas/a2a-task-state.schema.json": "75d5049dea8bd873ff0e7546f1c60c8d36c219bec7084264360be54a8be30ae1",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"schemas/annotation-create.schema.json": "567e8327777968e0ddab8e1547b4acd7beda734cf9d1fc6cde17037d8d9540d4",
|
|
30
30
|
"schemas/annotation.schema.json": "b26d22f9a97e1ae7c195e8799964aca804e78452a079ca3a0d61ea013907ce91",
|
|
31
31
|
"schemas/artifact-type-pack-manifest.schema.json": "60342fcb15eb7f3ab46271f0641409a30d3a4a8f318b1020c53070886b191252",
|
|
32
|
-
"schemas/audit-verify-result.schema.json": "
|
|
32
|
+
"schemas/audit-verify-result.schema.json": "de9cc3b9d7b97995b906dc96438244ea6b93c26b5faaf4926320676b48ba4fcc",
|
|
33
33
|
"schemas/budget-policy.schema.json": "3d4fe30ae129fe136ab9ece395f2a2a42a76360786b1b9a1d5f1cbc753eb5b39",
|
|
34
34
|
"schemas/capabilities.schema.json": "56b367074bce0f02ff87e0b84ccdf91856793d3d285e28f261656edba6352cc0",
|
|
35
35
|
"schemas/certification-bundle-v2.schema.json": "2f85530dd04824a15810c94e96869362dee1e78396eca21fdd18e5501416bb7d",
|
|
@@ -114,9 +114,9 @@
|
|
|
114
114
|
"schemas/v2/annotation.schema.json": "eb4813cdaa8e325412cfcaeeee5c8a28f3751c946dec64e546c7ba0ad3aad8d4",
|
|
115
115
|
"schemas/v2/artifact-type-pack-manifest.schema.json": "c8820351a2ab93855bd654ad3d8a9788f97975d09b3a7a0418e374ed6114595d",
|
|
116
116
|
"schemas/v2/artifact.schema.json": "85b9dc284d39b99d2a66fa8cbf47f2dd3d8526d3bd6cc411ad315b372de38975",
|
|
117
|
-
"schemas/v2/audit-verify-result.schema.json": "
|
|
117
|
+
"schemas/v2/audit-verify-result.schema.json": "2a4bfc42976717f78c10468fdba756ca6b19e49762699e6dffd418ffff08e55d",
|
|
118
118
|
"schemas/v2/budget-policy.schema.json": "c7449daeb6e1d95e7a047b8c2814d3d54748697c27046972834874867d9b5f4c",
|
|
119
|
-
"schemas/v2/capabilities.schema.json": "
|
|
119
|
+
"schemas/v2/capabilities.schema.json": "959b7a1b8e40aeafb8b9702bd0e4b220c0beb02a6afa3ca64fe9b04b95ae3df5",
|
|
120
120
|
"schemas/v2/certification-bundle.schema.json": "82ce31a1ed6c7e21b0ba88ff62a7b8b768fc3b176b1d06fce023203830b94725",
|
|
121
121
|
"schemas/v2/channel-presence-payload.schema.json": "4eeb224910e6f7eee3ab5e7c3899559c25c83a26d0022e0ad195c4672611977a",
|
|
122
122
|
"schemas/v2/channel-written-payload.schema.json": "ccecff3c71a3275ad8db035ac0e04db25e6f8d23cf091ddbcd5a60d1be1cbb6b",
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
"schemas/v2/envelopes/schema.request.schema.json": "be2c5f7aa881cdd252ccff47ac02193659e1393633a1541f4297383205d384a2",
|
|
143
143
|
"schemas/v2/envelopes/schema.response.schema.json": "d049a081267c06c28879c331b147f3ed821b14ccfd93005c67aff64db0a8c68e",
|
|
144
144
|
"schemas/v2/envelopes/ui.a2ui-surface.schema.json": "e92a2548ba65454c9e0a34f72ea6768d18f59e61fa5da7668d0b2d5baddaddc8",
|
|
145
|
-
"schemas/v2/error-envelope.schema.json": "
|
|
145
|
+
"schemas/v2/error-envelope.schema.json": "467d7109c54bff71a6cfd457d3bd65a23ca34b8e5577e742d080692771a78ebf",
|
|
146
146
|
"schemas/v2/eval-summary.schema.json": "2f384daf05b8f3b4f604688cae504d6d9971ba41b8c771e55fe9a17e09e86b7a",
|
|
147
147
|
"schemas/v2/export-bundle.schema.json": "bd28905a7729437ab7f2ad8bb33e13a371b2bf5605a7eb2962a0d246de312d69",
|
|
148
148
|
"schemas/v2/form-content-pack-manifest.schema.json": "a1942a124e250a6e9fbdb6b811c5a125e78fbcbcaa46dfc71d0636f24710a339",
|
|
@@ -202,72 +202,75 @@
|
|
|
202
202
|
"schemas/workload-identity.schema.json": "64ed93e12cc9f0657e56ff9b8f045e5d68d397df5276d103c07f42c7f34f0579",
|
|
203
203
|
"schemas/workspace-file-create.schema.json": "70a23b92c2958c0165d7d29aea9020a045dc7816af82c80c5f50d0aac19c4856",
|
|
204
204
|
"schemas/workspace-file.schema.json": "464de85c2a068243084ee9c1d969bc7cd5d8f7948574e58450d6493c38a0e1e4",
|
|
205
|
-
"spec/v1/alias-detectors.json": "
|
|
205
|
+
"spec/v1/alias-detectors.json": "29f21e0fb2c7a232a92a5c19b4cba3a86bc3b5e16f06780452f0421c66c6ec38",
|
|
206
206
|
"spec/v1/capability-declaration-classes.json": "e7729aed5c4b4e1dd02abab0530f14cc95f5d4070fe51fb139e7f5cccefa00c6",
|
|
207
|
-
"spec/v1/core-standard-manifest.json": "
|
|
208
|
-
"spec/v1/deprecations.json": "
|
|
207
|
+
"spec/v1/core-standard-manifest.json": "1359a69ad091b8bd3fb8fb7b641b32496d0d7a70d40cb0d1b93c6f171566276e",
|
|
208
|
+
"spec/v1/deprecations.json": "46df807fb4adf0aae24671c20f69975cf3496e3bc3a521cb7e6ea556bdce563c",
|
|
209
209
|
"spec/v1/deprecations.schema.json": "3e393c405d2a41b467d8c5e3c468549078df1ce6a6d2588fc488097b95d9b55b",
|
|
210
210
|
"spec/v1/event-codemap.json": "3da60d884157793a360da532a9fcbbfb5285636db325a74cec94b34622186d97",
|
|
211
211
|
"spec/v1/event-codemap.schema.json": "d05933b2e88103aff51a2f774df97b9bda065e0fc88dbd9b114cc47a32189174",
|
|
212
212
|
"spec/v1/extensions.json": "79a60754aa16cbdbdb604f8e5c00038af535a1690a4d07bd5ff7c38f0256a7bc",
|
|
213
|
-
"spec/v1/gaps.json": "
|
|
213
|
+
"spec/v1/gaps.json": "2b032d91d614a758db347be4f5e617b8ac3c4622b6f95ffcfa928276c0fb7377",
|
|
214
214
|
"spec/v1/gaps.schema.json": "8fd83259f556553c9df0f53e7a82ca8c2a4771d471197f69b4e2ae0ceeacfd66",
|
|
215
|
-
"spec/v1/migrations.json": "
|
|
215
|
+
"spec/v1/migrations.json": "2efcd9064b2bc65a84a3b3d281aa86a91efd002690c1f4f14e8335c4a18194d1",
|
|
216
216
|
"spec/v1/migrations.schema.json": "886779aa6c22e646db097f5df210adb018a4dd14a7b815465a18c8a7056c8f72",
|
|
217
217
|
"spec/v1/operation-path-manifest.json": "fada0342815fb129c1ae61f1829bd6e34417717408ce86fcc54280f373c19e74",
|
|
218
218
|
"spec/v1/spec-gaps.json": "ea9612af55bea9dae3e82223d4f714243f8f590b0193f1188257b13491a38afc",
|
|
219
|
-
"spec/v2/README.md": "
|
|
220
|
-
"spec/v2/core/capabilities.md": "
|
|
221
|
-
"spec/v2/core/conformance.md": "
|
|
222
|
-
"spec/v2/core/connection-packs.md": "
|
|
223
|
-
"spec/v2/core/conversation.md": "
|
|
224
|
-
"spec/v2/core/errors.md": "
|
|
225
|
-
"spec/v2/core/events.md": "
|
|
226
|
-
"spec/v2/core/form-content-packs.md": "
|
|
227
|
-
"spec/v2/core/headers.md": "
|
|
228
|
-
"spec/v2/core/host-services.md": "
|
|
229
|
-
"spec/v2/core/
|
|
230
|
-
"spec/v2/core/
|
|
231
|
-
"spec/v2/core/
|
|
232
|
-
"spec/v2/core/
|
|
233
|
-
"spec/v2/core/
|
|
234
|
-
"spec/v2/core/
|
|
235
|
-
"spec/v2/core/
|
|
236
|
-
"spec/v2/core/
|
|
237
|
-
"spec/v2/core/
|
|
238
|
-
"spec/v2/core/
|
|
239
|
-
"spec/v2/core/
|
|
240
|
-
"spec/v2/core/
|
|
241
|
-
"spec/v2/core/
|
|
242
|
-
"spec/v2/core/
|
|
243
|
-
"spec/v2/core/
|
|
244
|
-
"spec/v2/core/
|
|
245
|
-
"spec/v2/
|
|
246
|
-
"spec/v2/
|
|
247
|
-
"spec/v2/
|
|
248
|
-
"spec/v2/
|
|
249
|
-
"spec/v2/
|
|
219
|
+
"spec/v2/README.md": "4fa067d377580495e60baf08a4eafc3214b8ca48d8fb270bf91fc5066fad7186",
|
|
220
|
+
"spec/v2/core/capabilities.md": "225269a257421ebe677070dd13b05f6f297a2a343916e440b7428b839e2da9a4",
|
|
221
|
+
"spec/v2/core/conformance.md": "e7bd7d54062f28801071165a1b92edbeee3774bb3710f31eed75817139b66d95",
|
|
222
|
+
"spec/v2/core/connection-packs.md": "3168d2c8f1a59417a7256a678f4203570be434f64dead6b11fdec86b352686df",
|
|
223
|
+
"spec/v2/core/conversation.md": "6db43d8160bc45d3944aa1151816a6f0fd1669d59d7f822267a2d6cc9db22990",
|
|
224
|
+
"spec/v2/core/errors.md": "d14649ee60f8260ec163464163409ad7f50cb86fee7b846e0190f66070b3774b",
|
|
225
|
+
"spec/v2/core/events.md": "234338deb97be648731e8ab2126a90b7b8ad0a7070d98ffc85ee4689ff059791",
|
|
226
|
+
"spec/v2/core/form-content-packs.md": "41d40afe0a3760bcd792a4c406e16f28b0ab0aa7b0925323110d3fa2730d5400",
|
|
227
|
+
"spec/v2/core/headers.md": "5cb1624fa63acc81992b6a6d842ec392cf7271aaf2f65d1f83c8c4fa286e6730",
|
|
228
|
+
"spec/v2/core/host-services.md": "15923cceb95cba148a32e0944f6a2d1d25e9cc36b0c169b67ee42d483bcac800",
|
|
229
|
+
"spec/v2/core/i18n.md": "846c6d2878fc8454c2ae0a1576f6b6e6c8958d47187a6c96cc5826ca3c3e2e91",
|
|
230
|
+
"spec/v2/core/idempotency.md": "21830b7d23dd03e9e8ba4b0de253dd001fc960f166310d6b6b60b1c33cf60b30",
|
|
231
|
+
"spec/v2/core/identity.md": "acb7f739729c69a1f40dcaa5b1d873ba9f32993c17bfd297f5fa56351cf47c69",
|
|
232
|
+
"spec/v2/core/interop.md": "3cfa2af70ae6ba1f4b4268447d46d1d2f5fb65f7917f15bad159d9779d25b1a6",
|
|
233
|
+
"spec/v2/core/interrupt.md": "b31b3f3be7888043ef38c82367a8d743a775c2b6bd499f7da44355ff00452435",
|
|
234
|
+
"spec/v2/core/node-pack-runtimes.md": "d52966319c53aaa03575025915b73b470a42832c039c41d73aff6931758630cb",
|
|
235
|
+
"spec/v2/core/oauth.md": "57f167d899993b6959c243e4102819f4014b365299b9b2b253decd2781e6b8b7",
|
|
236
|
+
"spec/v2/core/overview.md": "3dbc8c0cf7474b31816b1a12412c1edf5597066c38edcdbca4dd2e465880f65b",
|
|
237
|
+
"spec/v2/core/packs.md": "26b0fc3b81f98adbdf70796482db89fa81a6ce3da6f830cd5e8b3d71541c497f",
|
|
238
|
+
"spec/v2/core/persistence.md": "bba672ed3bf6d83af6e4e65c0f4f480fc21fe12207811e321306af2108d9010d",
|
|
239
|
+
"spec/v2/core/portability.md": "a35fbe66b8b5ccf63cf0216157d4b61d4c47f6dc8e757c830569ddf244e21e8a",
|
|
240
|
+
"spec/v2/core/replay.md": "a5be4e05242f25f8b5a8a4fb31bd359d5abb4bf34fa8e536403a8ee5779ab876",
|
|
241
|
+
"spec/v2/core/runs.md": "72a94c27d884036d84290e9b108702e98b0c6b2fb598213bbda44fba9c9f860d",
|
|
242
|
+
"spec/v2/core/security-defaults.md": "179fdd2efcc1454453243f8ab6473335dfd4389e70cbbc2669f5b83714092973",
|
|
243
|
+
"spec/v2/core/tool-catalog.md": "6b54b8ce221107de19c718f374a720123c6a5f55db4054e6625f73d06e0a442f",
|
|
244
|
+
"spec/v2/core/versioning.md": "2bfe16a72a909c46b4585fab39dc9e94c97506ed95df6b1b527f97b4cd3684ef",
|
|
245
|
+
"spec/v2/core/webhooks.md": "78fe97ab5bdee20218183efe32abf5ba1996705d757cb37c8d6a4d808c44199f",
|
|
246
|
+
"spec/v2/core/workflow-chain-packs.md": "35a957f1dd22016febd76bf2c4dfb79cda58b95d8efd8893b5e5babda9f79cf3",
|
|
247
|
+
"spec/v2/corrections.json": "b19f8a80fce7e6473012a20a216e358dd38ae7411e6a348f8031e325ed1a0a01",
|
|
248
|
+
"spec/v2/corrections.schema.json": "46bbffb954d13bd9995aebc912ceeb2544f7064331abf25fd6ab85c81a5cec94",
|
|
249
|
+
"spec/v2/declaration.json": "1253a5590ec2d9ba511d15e8a9d9da5e6926c4daa981a26b73b29bc915477013",
|
|
250
|
+
"spec/v2/declaration.schema.json": "ef951633e4070899f64828f5cf1b5f4870270a0360f81dbb49b5b4d6cd407550",
|
|
251
|
+
"spec/v2/errors.json": "b89664e9e0e7603253712263ce0843ce607e276c67d0acaf4a680c39bb2bd682",
|
|
252
|
+
"spec/v2/errors.schema.json": "47404ad53e6147cf9017b813483712466573e419fb33e6c4b6c12d3e7deba1bc",
|
|
250
253
|
"spec/v2/event-codemap.json": "37869a03cdfd8758b57e6f350641a6ebbd383231cebda51f5473ad85f94ab74c",
|
|
251
254
|
"spec/v2/event-codemap.schema.json": "b173f2a9bcc0bf9b62a474ce3b91e1431d597e64fd1560fc58662cd8606eaf9c",
|
|
252
|
-
"spec/v2/ext/README.md": "
|
|
253
|
-
"spec/v2/ext/a2uiSurface/README.md": "
|
|
254
|
-
"spec/v2/ext/brand/README.md": "
|
|
255
|
-
"spec/v2/ext/canvas/README.md": "
|
|
256
|
-
"spec/v2/ext/chat/README.md": "
|
|
257
|
-
"spec/v2/ext/coordination/README.md": "
|
|
258
|
-
"spec/v2/ext/dataIntegration/README.md": "
|
|
259
|
-
"spec/v2/ext/entities/README.md": "
|
|
260
|
-
"spec/v2/ext/grpc-transport/README.md": "
|
|
261
|
-
"spec/v2/ext/kanban/README.md": "
|
|
262
|
-
"spec/v2/ext/knowledge/README.md": "
|
|
263
|
-
"spec/v2/ext/launchStudio/README.md": "
|
|
264
|
-
"spec/v2/ext/messaging/README.md": "
|
|
265
|
-
"spec/v2/ext/portability/README.md": "
|
|
266
|
-
"spec/v2/ext/provider-idempotency/README.md": "
|
|
255
|
+
"spec/v2/ext/README.md": "e2731ebb5156ece1840ed3b96e7a347f6345e73071ff4e54df0f21c1585c5027",
|
|
256
|
+
"spec/v2/ext/a2uiSurface/README.md": "5518c7f355262eb7cc584422f4ff8e548f066b211878f969274a3a578127622d",
|
|
257
|
+
"spec/v2/ext/brand/README.md": "52b31ddb8e86820afc8fb1afd29fddf7940dc00454f60ba0dbada718a9b63157",
|
|
258
|
+
"spec/v2/ext/canvas/README.md": "0e91b68566d2be90319feedfaebe2cd2db0bd8f72fb59c958fa723c05e38023f",
|
|
259
|
+
"spec/v2/ext/chat/README.md": "a934ad42134a58968a1ccc03d7fe317fc8b8b14bf847d97bfe7773086c3d22b7",
|
|
260
|
+
"spec/v2/ext/coordination/README.md": "9c3513aab388afbe3ab95db60ef15e9c9d4a7a0cd7d3e92b567136235a7183d2",
|
|
261
|
+
"spec/v2/ext/dataIntegration/README.md": "88be31dcabacdd4e012b8c0795d9a3d82bc2cc62b67de4c05ccdd5d9afed9166",
|
|
262
|
+
"spec/v2/ext/entities/README.md": "78096383811fefa264f3ed0d97c8174a61870fda5a78cc97a5c1bb761ac8cfd7",
|
|
263
|
+
"spec/v2/ext/grpc-transport/README.md": "6ba378e3c6d1f2e3269b392cfdf0c40565ee9bfa259d1baaa9ee1d350e38e792",
|
|
264
|
+
"spec/v2/ext/kanban/README.md": "1d5bc89133ae02984065cb79d38e7627c36ffd6e0d2a58eb6207aaa020e7b0ad",
|
|
265
|
+
"spec/v2/ext/knowledge/README.md": "67be1dc97e5cdc9527d56f4836dc96841548a9c2f8f964777825d56fdf9f0e53",
|
|
266
|
+
"spec/v2/ext/launchStudio/README.md": "c732bb855252c31ee1e971cbc2936f2ca29b234c111d548c648095634507babb",
|
|
267
|
+
"spec/v2/ext/messaging/README.md": "32c222c0bce515e75b62a5a44b9f09d0e23c1b40f424e1ab8309deefd296c4b6",
|
|
268
|
+
"spec/v2/ext/portability/README.md": "c59423ee446638b748a911b9e97e803c4786157be6120f656f0a060586bc7b82",
|
|
269
|
+
"spec/v2/ext/provider-idempotency/README.md": "9d1bccef0ec16a19a3370ae79c2f5a1726ce0d1d25a51352907f288a2ccb15d7",
|
|
267
270
|
"spec/v2/ext/provider-idempotency/registry.json": "5b5fecad604fb4ab38da39f5abe697a4e57b05406c398f8afeb377331b2bd965",
|
|
268
|
-
"spec/v2/ext/restTransport/README.md": "
|
|
269
|
-
"spec/v2/ext/sandbox-runtime-notes/README.md": "
|
|
270
|
-
"spec/v2/ext/webResearch/README.md": "
|
|
271
|
+
"spec/v2/ext/restTransport/README.md": "d485417310616b2f1da97106d970293f3f5712f6cadb6b840f5ac84ee28bdda6",
|
|
272
|
+
"spec/v2/ext/sandbox-runtime-notes/README.md": "0d1290fb1e20b2e35c0b15051541ab6546e4e2a6f693b4f912cb3f7d6a65e92c",
|
|
273
|
+
"spec/v2/ext/webResearch/README.md": "3ddca9180c4ef9971722a2e6a0c1ffb5cc783411ba554fb9fbdf15b4da624942",
|
|
271
274
|
"spec/v2/facets/a2a.schema.json": "b68bf5e29e455e46c2ad7cfcccf5963730ec2310ff9db81884550d8df30fd6ef",
|
|
272
275
|
"spec/v2/facets/aiProviders.schema.json": "81fb39b6698564d9d07a0d10525ecb6360c152fe3f3a5ea0439112509ca130e2",
|
|
273
276
|
"spec/v2/facets/auth.schema.json": "7d98bc81a0f7d307460d039eebfebd21388091bfe1d538762583940f94c0bd2a",
|
|
@@ -293,10 +296,10 @@
|
|
|
293
296
|
"spec/v2/migrations.schema.json": "b91ade6320ad1b1185c9495b9424f3aa63438ed8e412c45171c227d82c0afd86",
|
|
294
297
|
"spec/v2/path-manifest.json": "17298a245c5da9a70aa7ab0b7f2db4277e06209d6fcdd7187a8b4cea179c08f3",
|
|
295
298
|
"spec/v2/peer-dependency-aliases.json": "d10299280abee08258502925bc327293ee413e0108cd6e6ec75ff6110653308d",
|
|
296
|
-
"spec/v2/profiles.json": "
|
|
297
|
-
"spec/v2/release.json": "
|
|
299
|
+
"spec/v2/profiles.json": "180beb9ef2d161766fa23b2987e7e829851e3d316273147cf6a4a3f324befd6b",
|
|
300
|
+
"spec/v2/release.json": "f2f87ba5a5476f2213b3486a2618ef3c7381b47c96dd3f082bee0e07ac4a35bc",
|
|
298
301
|
"spec/v2/retention-floors.json": "eaf3722d95c79947af1d4269ef85117e126518c588cfcf1a2b21b97269f51624",
|
|
299
|
-
"spec/v2/surface-baseline.json": "
|
|
302
|
+
"spec/v2/surface-baseline.json": "fce9d8eeea653476e09b19e525a0b104caab6c51a0b7b427d7d941e2b7118573"
|
|
300
303
|
},
|
|
301
|
-
"corpusCommit": "
|
|
304
|
+
"corpusCommit": "0ed567fece2f9c5c5ad337fed92f6926ad7547af"
|
|
302
305
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 0218 — the audit checkpoint preimage (`spec/v1/auth-profiles.md`
|
|
3
|
+
* §"Audit-log integrity" 3).
|
|
4
|
+
*
|
|
5
|
+
* A checkpoint anchors the entries with sequence in `(P, atSequence]`, where `P`
|
|
6
|
+
* is the previous checkpoint's `atSequence` (0 for the first). Its leaves are
|
|
7
|
+
* those entries' hashes in sequence order: the lowercase-hex SHA-256 of each
|
|
8
|
+
* entry's RFC 8785 JCS bytes, the same value the next entry carries as
|
|
9
|
+
* `prevHash`. The root is built pairwise, one level at a time. A parent is the
|
|
10
|
+
* SHA-256 of the ASCII bytes of `left ‖ right`, both lowercase hex. A last odd
|
|
11
|
+
* node is promoted unchanged, never duplicated. The signature is Ed25519 over
|
|
12
|
+
* the 32 bytes the root hex-decodes to.
|
|
13
|
+
*
|
|
14
|
+
* There is no leaf/interior domain separation, so a verifier that recomputes a
|
|
15
|
+
* root MUST also hold the leaf count to `atSequence − P`
|
|
16
|
+
* (`checkpointMerkleRoot`'s `expectedLeaves`). With the count fixed the tree
|
|
17
|
+
* shape is fixed, and presenting interior nodes as leaves is refused.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { createHash, createPublicKey, verify as cryptoVerify, type KeyObject } from 'node:crypto';
|
|
21
|
+
import { canonicalJSON } from './jcs.js';
|
|
22
|
+
|
|
23
|
+
const HEX64 = /^[0-9a-f]{64}$/;
|
|
24
|
+
|
|
25
|
+
const sha256Hex = (s: string): string => createHash('sha256').update(s, 'utf8').digest('hex');
|
|
26
|
+
|
|
27
|
+
/** The entry hash: lowercase-hex SHA-256 of the entry's RFC 8785 JCS bytes. */
|
|
28
|
+
export function auditEntryHash(entry: unknown): string {
|
|
29
|
+
return sha256Hex(canonicalJSON(entry));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class CheckpointPreimageError extends Error {}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The checkpoint root over `leaves` (entry hashes in sequence order). Refuses an
|
|
36
|
+
* empty range, a leaf that is not 64 lowercase hex characters, and, when
|
|
37
|
+
* `expectedLeaves` is given, any other count.
|
|
38
|
+
*/
|
|
39
|
+
export function checkpointMerkleRoot(leaves: readonly string[], expectedLeaves?: number): string {
|
|
40
|
+
if (leaves.length === 0) throw new CheckpointPreimageError('a checkpoint anchors at least one entry');
|
|
41
|
+
if (expectedLeaves !== undefined && leaves.length !== expectedLeaves) {
|
|
42
|
+
throw new CheckpointPreimageError(`expected ${expectedLeaves} leaves (atSequence − P), got ${leaves.length}`);
|
|
43
|
+
}
|
|
44
|
+
for (const l of leaves) if (!HEX64.test(l)) throw new CheckpointPreimageError(`leaf is not 64 lowercase hex: ${JSON.stringify(l)}`);
|
|
45
|
+
let level = leaves.slice();
|
|
46
|
+
while (level.length > 1) {
|
|
47
|
+
const next: string[] = [];
|
|
48
|
+
for (let i = 0; i < level.length; i += 2) {
|
|
49
|
+
const right = level[i + 1];
|
|
50
|
+
next.push(right === undefined ? level[i]! : sha256Hex(level[i]! + right));
|
|
51
|
+
}
|
|
52
|
+
level = next;
|
|
53
|
+
}
|
|
54
|
+
return level[0]!;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The advertised `checkpointPublicKey` is base64 SPKI DER (`MCowBQYDK2VwAyEA…`).
|
|
59
|
+
* A PEM string is accepted too.
|
|
60
|
+
*/
|
|
61
|
+
export function checkpointPublicKey(advertised: string): KeyObject {
|
|
62
|
+
if (advertised.startsWith('-----BEGIN')) return createPublicKey(advertised);
|
|
63
|
+
return createPublicKey({ key: Buffer.from(advertised, 'base64'), format: 'der', type: 'spki' });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Ed25519 over the 32 bytes `merkleRoot` hex-decodes to; `signature` is base64. */
|
|
67
|
+
export function verifyCheckpointSignature(merkleRoot: string, signature: string, key: KeyObject): boolean {
|
|
68
|
+
if (!HEX64.test(merkleRoot)) return false;
|
|
69
|
+
const sig = Buffer.from(signature, 'base64');
|
|
70
|
+
if (sig.length !== 64) return false;
|
|
71
|
+
return cryptoVerify(null, Buffer.from(merkleRoot, 'hex'), key, sig);
|
|
72
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ext-claims — the `claims-check` witness for an extension family (RFC 0220 §B).
|
|
3
|
+
*
|
|
4
|
+
* An extension family (`anchor: ext` in `spec/v2/declaration.json`) is
|
|
5
|
+
* advertised as `extensions["<org>.<extensionName>"]` (capabilities.md §3.2),
|
|
6
|
+
* where `extensionName` is the kebab-case name the declaration row carries —
|
|
7
|
+
* the family key itself is camelCase and could never match the key pattern.
|
|
8
|
+
*
|
|
9
|
+
* `claims-check` checks the claim, not behavior (conformance.md §Witness
|
|
10
|
+
* classes). For an org-shaped record that means exactly:
|
|
11
|
+
*
|
|
12
|
+
* 1. the key matches `extensionsKeyPattern`;
|
|
13
|
+
* 2. its org is registered in the declaration and is not reserved;
|
|
14
|
+
* 3. the record is a JSON object (not an array, not a scalar, not null);
|
|
15
|
+
* 4. the family key is NOT also a member of the discovery root — an ext
|
|
16
|
+
* family is never a root family, and a root copy is a second claim the
|
|
17
|
+
* client would have to reconcile.
|
|
18
|
+
*
|
|
19
|
+
* `classifyExtClaim` is pure so the self-test can run each negative control
|
|
20
|
+
* without a host; the scenario only reads discovery and calls it.
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
import { SPEC_V2_DIR } from './paths.js';
|
|
25
|
+
|
|
26
|
+
type Json = Record<string, unknown>;
|
|
27
|
+
|
|
28
|
+
export interface ExtFamily {
|
|
29
|
+
readonly key: string;
|
|
30
|
+
readonly extensionName: string;
|
|
31
|
+
readonly witness: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ExtDeclaration {
|
|
35
|
+
readonly families: readonly ExtFamily[];
|
|
36
|
+
readonly registeredOrgs: ReadonlySet<string>;
|
|
37
|
+
readonly reservedOrgs: ReadonlySet<string>;
|
|
38
|
+
readonly keyPattern: RegExp;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let cached: ExtDeclaration | null | undefined;
|
|
42
|
+
/** The ext rows of the declaration, or `undefined` when it is not on disk in this layout. */
|
|
43
|
+
export function extDeclaration(): ExtDeclaration | undefined {
|
|
44
|
+
if (cached !== undefined) return cached ?? undefined;
|
|
45
|
+
cached = null;
|
|
46
|
+
const file = SPEC_V2_DIR ? join(SPEC_V2_DIR, 'declaration.json') : null;
|
|
47
|
+
if (file && existsSync(file)) {
|
|
48
|
+
try {
|
|
49
|
+
const d = JSON.parse(readFileSync(file, 'utf8')) as Json;
|
|
50
|
+
const fams = (Array.isArray(d['families']) ? d['families'] : []) as Json[];
|
|
51
|
+
cached = {
|
|
52
|
+
families: fams
|
|
53
|
+
.filter((f) => f['anchor'] === 'ext' && typeof f['extensionName'] === 'string')
|
|
54
|
+
.map((f) => ({ key: String(f['key']), extensionName: String(f['extensionName']), witness: String(f['witness']) })),
|
|
55
|
+
registeredOrgs: new Set(Object.keys((d['extensions'] ?? {}) as Json)),
|
|
56
|
+
reservedOrgs: new Set((Array.isArray(d['reservedOrgs']) ? d['reservedOrgs'] : []) as string[]),
|
|
57
|
+
keyPattern: new RegExp(String(d['extensionsKeyPattern'])),
|
|
58
|
+
};
|
|
59
|
+
} catch { /* left null: unreadable and absent are the same soft-skip */ }
|
|
60
|
+
}
|
|
61
|
+
return cached ?? undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type ExtClaim =
|
|
65
|
+
| { readonly state: 'absent' }
|
|
66
|
+
| { readonly state: 'well-formed'; readonly key: string; readonly org: string }
|
|
67
|
+
| { readonly state: 'malformed'; readonly key: string; readonly why: string };
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Every claim `discovery` makes for `family`, classified. A host that
|
|
71
|
+
* advertises the family under more than one registered org yields one entry
|
|
72
|
+
* per key; a host that advertises it nowhere yields `[{ state: 'absent' }]`.
|
|
73
|
+
*/
|
|
74
|
+
export function classifyExtClaim(discovery: Json, family: ExtFamily, decl: ExtDeclaration): ExtClaim[] {
|
|
75
|
+
const ext = discovery['extensions'];
|
|
76
|
+
const members = ext && typeof ext === 'object' && !Array.isArray(ext) ? Object.entries(ext as Json) : [];
|
|
77
|
+
const suffix = `.${family.extensionName}`;
|
|
78
|
+
const claims: ExtClaim[] = [];
|
|
79
|
+
for (const [key, rec] of members) {
|
|
80
|
+
if (!key.endsWith(suffix)) continue;
|
|
81
|
+
const org = key.slice(0, -suffix.length);
|
|
82
|
+
if (!decl.keyPattern.test(key)) { claims.push({ state: 'malformed', key, why: `the key does not match extensionsKeyPattern ${decl.keyPattern.source}` }); continue; }
|
|
83
|
+
if (decl.reservedOrgs.has(org)) { claims.push({ state: 'malformed', key, why: `org '${org}' is reserved (declaration reservedOrgs); a host MUST NOT use it` }); continue; }
|
|
84
|
+
if (!decl.registeredOrgs.has(org)) continue; // an unregistered org is someone else's name, not a claim on this family
|
|
85
|
+
if (rec === null || typeof rec !== 'object' || Array.isArray(rec)) {
|
|
86
|
+
claims.push({ state: 'malformed', key, why: `the record is ${rec === null ? 'null' : Array.isArray(rec) ? 'an array' : typeof rec}, not an object` });
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (Object.prototype.hasOwnProperty.call(discovery, family.key)) {
|
|
90
|
+
claims.push({ state: 'malformed', key, why: `the family key '${family.key}' is also a member of the discovery root; an ext family is advertised only under extensions` });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
claims.push({ state: 'well-formed', key, org });
|
|
94
|
+
}
|
|
95
|
+
return claims.length > 0 ? claims : [{ state: 'absent' }];
|
|
96
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-attempt dispatch budgets — `GET /v1/host/sample/test/mock-ai/dispatch-budgets`
|
|
3
|
+
* (host-sample-test-seams.md §28; v2 `api/seams-v2.yaml` `getMockDispatchBudgets`).
|
|
4
|
+
*
|
|
5
|
+
* The companion `last-dispatch-budget` seam reports only the most recent call's
|
|
6
|
+
* `maxTokens`, so a retry's budget cannot be compared with the attempt before
|
|
7
|
+
* it. This seam returns every call's budget in call order. OPTIONAL: an
|
|
8
|
+
* unserved seam (404/405/501) is `unserved`, so the caller falls back to its
|
|
9
|
+
* pre-seam partial witness; a served seam that answers anything else is
|
|
10
|
+
* `broken`, which the caller records as `blocked`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { driver } from './driver.js';
|
|
14
|
+
|
|
15
|
+
export type DispatchBudgets =
|
|
16
|
+
| { ok: true; attempts: Array<number | null> }
|
|
17
|
+
| { ok: false; unserved: true; reason: string }
|
|
18
|
+
| { ok: false; unserved: false; reason: string };
|
|
19
|
+
|
|
20
|
+
const UNSERVED = new Set([404, 405, 501]);
|
|
21
|
+
|
|
22
|
+
export async function readDispatchBudgets(nodeId: string): Promise<DispatchBudgets> {
|
|
23
|
+
const res = await driver.get(`/v1/host/sample/test/mock-ai/dispatch-budgets?nodeId=${encodeURIComponent(nodeId)}`);
|
|
24
|
+
if (UNSERVED.has(res.status)) {
|
|
25
|
+
return { ok: false, unserved: true, reason: `the dispatch-budgets seam is not served (${res.status})` };
|
|
26
|
+
}
|
|
27
|
+
const attempts = (res.json as { attempts?: unknown } | undefined)?.attempts;
|
|
28
|
+
if (res.status !== 200 || !Array.isArray(attempts)) {
|
|
29
|
+
return { ok: false, unserved: false, reason: `the dispatch-budgets seam answered ${res.status} without an attempts[] array (host-sample-test-seams.md §28)` };
|
|
30
|
+
}
|
|
31
|
+
const out: Array<number | null> = [];
|
|
32
|
+
for (const a of attempts) {
|
|
33
|
+
const m = (a as { maxTokens?: unknown } | null)?.maxTokens;
|
|
34
|
+
if (m !== null && !(typeof m === 'number' && Number.isInteger(m))) {
|
|
35
|
+
return { ok: false, unserved: false, reason: `the dispatch-budgets seam returned an entry whose maxTokens is ${JSON.stringify(m)}, not an integer or null (host-sample-test-seams.md §28)` };
|
|
36
|
+
}
|
|
37
|
+
out.push(m);
|
|
38
|
+
}
|
|
39
|
+
return { ok: true, attempts: out };
|
|
40
|
+
}
|
package/src/lib/soft-skip.ts
CHANGED
|
@@ -109,6 +109,20 @@ export function blockedDespiteAssertions(reason: string): undefined {
|
|
|
109
109
|
return undefined;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* `seamAbsent` for a seam that goes missing AFTER the leg has asserted setup
|
|
114
|
+
* facts: strict mode fails exactly as `seamAbsent` does, and default mode
|
|
115
|
+
* records a `blocked` that stands at both majors (`blockedDespiteAssertions`).
|
|
116
|
+
* A plain `seamAbsent` there notes `blocked`, which major 1 records as a
|
|
117
|
+
* partial-witness pass.
|
|
118
|
+
*/
|
|
119
|
+
export function seamAbsentDespiteAssertions(reason: string): undefined {
|
|
120
|
+
if (process.env['OPENWOP_REQUIRE_BEHAVIOR'] === 'true') {
|
|
121
|
+
throw new Error(`RFC 0148 §B: advertised behaviour is not observable — ${reason} (OPENWOP_REQUIRE_BEHAVIOR=true fails an advertised-missing seam)`);
|
|
122
|
+
}
|
|
123
|
+
return blockedDespiteAssertions(reason);
|
|
124
|
+
}
|
|
125
|
+
|
|
112
126
|
const RANK: Record<SoftSkipKind, number> = { blocked: 0, skipped: 1, inapplicable: 2 };
|
|
113
127
|
|
|
114
128
|
function fold(arr: readonly Note[]): { kind: SoftSkipKind; reason: string; conclusive?: true } | null {
|
|
@@ -73,23 +73,16 @@ describe('RFC 0152 §E — a2a-peer-no-authority-escalation (host as A2A client,
|
|
|
73
73
|
// an advertised-missing seam (RFC 0148 §B). A 403 is NOT a pass.
|
|
74
74
|
return seamAbsent(`host advertises A2A but the invoke seam /v1/host/sample/a2a/invoke answered ${drive.status}`);
|
|
75
75
|
}
|
|
76
|
+
// `peerAuthority` is an OPTIONAL seam field (host-sample-test-seams.md §22). A host
|
|
77
|
+
// that omits it is not violating anything, and certification must not be denied for
|
|
78
|
+
// it, so its absence is `inapplicable`, decided before any assertion (the 2.42.6
|
|
79
|
+
// appendEra2Event precedent). A report that IS present is held to the contract below.
|
|
80
|
+
const report = (drive.json as { peerAuthority?: PeerAuthorityReport }).peerAuthority;
|
|
81
|
+
if (report === undefined) return softSkip('inapplicable', 'the invoke seam reports no peerAuthority (an optional seam field, host-sample-test-seams.md §22), so peer-authority non-escalation is not observable on this host');
|
|
76
82
|
// The peer must actually have answered with the authority-asserting message,
|
|
77
83
|
// or this leg is vacuous.
|
|
78
84
|
const created = peer.taskCount();
|
|
79
85
|
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);
|
|
80
|
-
const report = (drive.json as { peerAuthority?: PeerAuthorityReport }).peerAuthority;
|
|
81
|
-
if (report === undefined) {
|
|
82
|
-
// Additive seam field not yet implemented: blocked, not passed.
|
|
83
|
-
expect(
|
|
84
|
-
report,
|
|
85
|
-
req('openwop.it.a2a-peer-authority.a-peer-reply-that-asserts-approval-scopes-and-a-foreign-task-reference-changes-n',
|
|
86
|
-
'host-sample-test-seams.md §22',
|
|
87
|
-
'the invoke seam SHOULD report `peerAuthority: { approvalAdvanced, scopesWidened, referencedTaskDereferenced }` for ' +
|
|
88
|
-
'scenario "peer-asserts-authority"; until it does this requirement is unobservable and resolves to `blocked`, not passed',
|
|
89
|
-
),
|
|
90
|
-
).toBeDefined();
|
|
91
|
-
return softSkip('blocked', 'precondition not met — `report === undefined` returned early (seam, prior step, or fixture unavailable)');
|
|
92
|
-
}
|
|
93
86
|
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
87
|
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);
|
|
95
88
|
expect(
|
|
@@ -116,7 +116,12 @@ describe('a2a-task-roundtrip: AgentCard + task lifecycle', () => {
|
|
|
116
116
|
);
|
|
117
117
|
return softSkip('blocked', 'precondition not met — `!probe` returned early (seam, prior step, or fixture unavailable)');
|
|
118
118
|
}
|
|
119
|
-
|
|
119
|
+
// softskip ratchet, 2026-09-27: with only the in-process fake configured, this
|
|
120
|
+
// leg probed the SUITE'S OWN fake A2A peer and recorded `executed-pass`. The
|
|
121
|
+
// host under test was never contacted, so every host passed it. The fake-path
|
|
122
|
+
// self-check is now `inapplicable` before any assertion, as mcp-tool-roundtrip's
|
|
123
|
+
// is; the drift-point legs below are the ones that drive the host against the fake.
|
|
124
|
+
if (!probe.isReal) return softSkip('inapplicable', 'suite fixture self-test — host not exercised (only the in-process A2A fake is configured; set OPENWOP_A2A_REAL_PEER_URL for real-peer interop evidence)');
|
|
120
125
|
|
|
121
126
|
// AgentCard at the A2A v0.3 well-known path
|
|
122
127
|
// (`AGENT_CARD_PATH` from @a2a-js/sdk: `.well-known/agent-card.json`).
|
|
@@ -182,40 +187,7 @@ describe('a2a-task-roundtrip: AgentCard + task lifecycle', () => {
|
|
|
182
187
|
`[a2a-task-roundtrip] real-peer interop OK against ${probe.url} ` +
|
|
183
188
|
`(skill=${firstSkill?.id ?? firstSkill?.name}, kind=${kind})`,
|
|
184
189
|
);
|
|
185
|
-
return softSkip('blocked', 'precondition not met — `probe.isReal` returned early (seam, prior step, or fixture unavailable)');
|
|
186
190
|
}
|
|
187
|
-
|
|
188
|
-
// Fake-peer path: deterministic state forcing, assert verbatim.
|
|
189
|
-
const fake = getA2AFakePeer()!;
|
|
190
|
-
const sendRes = await rpc(
|
|
191
|
-
rpcUrl,
|
|
192
|
-
'message/send',
|
|
193
|
-
{
|
|
194
|
-
message: {
|
|
195
|
-
kind: 'message',
|
|
196
|
-
messageId: 'probe-fake-1',
|
|
197
|
-
role: 'user',
|
|
198
|
-
parts: [{ kind: 'text', text: 'hello' }],
|
|
199
|
-
},
|
|
200
|
-
},
|
|
201
|
-
1,
|
|
202
|
-
);
|
|
203
|
-
expect(sendRes.status).toBe(200);
|
|
204
|
-
expect(sendRes.error).toBeUndefined();
|
|
205
|
-
const task = sendRes.result as { id?: string; kind?: string; status?: { state?: string } };
|
|
206
|
-
expect(task.kind).toBe('task');
|
|
207
|
-
expect(typeof task.id).toBe('string');
|
|
208
|
-
|
|
209
|
-
// Advance through WORKING → COMPLETED via the fake's internal API.
|
|
210
|
-
fake.advanceTask(task.id!, 'WORKING');
|
|
211
|
-
fake.advanceTask(task.id!, 'COMPLETED');
|
|
212
|
-
|
|
213
|
-
const getRes = await rpc(rpcUrl, 'tasks/get', { id: task.id }, 2);
|
|
214
|
-
expect(getRes.status).toBe(200);
|
|
215
|
-
expect(getRes.error).toBeUndefined();
|
|
216
|
-
const finalTask = getRes.result as { status?: { state?: string } };
|
|
217
|
-
// A2A v0.3 wire form uses lowercase-hyphen state names.
|
|
218
|
-
expect(finalTask.status?.state).toBe('completed');
|
|
219
191
|
});
|
|
220
192
|
});
|
|
221
193
|
|
|
@@ -30,7 +30,7 @@ import { capabilityFamily } from '../lib/discovery-capabilities.js';
|
|
|
30
30
|
import { getA2AFakePeer } from '../lib/a2a-fake-peer.js';
|
|
31
31
|
import { readErrorCode } from '../lib/error-envelope.js';
|
|
32
32
|
import { req } from '../lib/requirement-ids.js';
|
|
33
|
-
import { softSkip } from '../lib/soft-skip.js';
|
|
33
|
+
import { softSkip, seamAbsent } from '../lib/soft-skip.js';
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Callback-shaped: the host issues A2A calls to the suite's fake peer, which records the negotiated version header.
|
|
@@ -80,17 +80,11 @@ describe('RFC 0152 §B — A2A version negotiation', () => {
|
|
|
80
80
|
peer.reset();
|
|
81
81
|
const drive = await driver.post('/v1/host/sample/a2a/invoke', { peerUrl: peer.hostFacingEndpoint() });
|
|
82
82
|
if (drive.status === 404 || drive.status === 403) {
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
'a host advertising A2A version negotiation MUST expose an invoke seam so the negotiated ' +
|
|
89
|
-
'version is observable. Without it the requirement cannot be witnessed and resolves to ' +
|
|
90
|
-
'`blocked` per RFC 0148 §A.',
|
|
91
|
-
),
|
|
92
|
-
).not.toBe(404);
|
|
93
|
-
return softSkip('blocked', 'precondition not met — `drive.status === 404 || drive.status === 403` returned early (seam, prior step, or fixture unavailable)');
|
|
83
|
+
// A host advertising A2A version negotiation MUST expose the invoke seam so the negotiated
|
|
84
|
+
// version is observable. Default mode records `blocked` (RFC 0148 §A); strict mode
|
|
85
|
+
// fails (RFC 0148 §B). This was a not-404 assert followed by a blocked note, so a 403
|
|
86
|
+
// recorded a partial-witness pass at major 1.
|
|
87
|
+
return seamAbsent(`host advertises A2A version negotiation but the invoke seam /v1/host/sample/a2a/invoke answered ${drive.status}`);
|
|
94
88
|
}
|
|
95
89
|
const calls = peer.invocations().filter((i: { method: string }) => i.method !== 'GET');
|
|
96
90
|
expect(calls.length, req('openwop.it.a2a-version-negotiation.outbound-calls-carry-an-explicit-a2a-version-header', 'RFCS/0152 §B', 'the host MUST have called the peer')).toBeGreaterThan(0);
|