@openwop/openwop-conformance 1.21.0 → 1.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +43 -2
  2. package/README.md +61 -63
  3. package/api/asyncapi.yaml +108 -38
  4. package/api/openapi.yaml +34 -6
  5. package/coverage.md +389 -202
  6. package/fixtures/connection-packs/connection-pack-github.json +31 -0
  7. package/fixtures.md +120 -101
  8. package/package.json +1 -1
  9. package/schemas/README.md +4 -0
  10. package/schemas/capabilities.schema.json +127 -0
  11. package/schemas/connection-pack-manifest.schema.json +161 -0
  12. package/schemas/export-bundle.schema.json +66 -0
  13. package/schemas/goal.schema.json +104 -0
  14. package/schemas/proposal.schema.json +84 -0
  15. package/schemas/run-event-payloads.schema.json +86 -7
  16. package/schemas/run-event.schema.json +17 -3
  17. package/schemas/run-options.schema.json +1 -2
  18. package/schemas/run-snapshot.schema.json +2 -1
  19. package/schemas/suspend-request.schema.json +5 -0
  20. package/src/scenarios/connection-pack-manifest-valid.test.ts +122 -0
  21. package/src/scenarios/connection-pack-no-credential-material.test.ts +125 -0
  22. package/src/scenarios/connection-pack-reach-exclusive.test.ts +85 -0
  23. package/src/scenarios/connection-pack-write-reconsent.test.ts +91 -0
  24. package/src/scenarios/connection-provider-resolution.test.ts +153 -0
  25. package/src/scenarios/cross-host-traceparent-propagation.test.ts +3 -3
  26. package/src/scenarios/export-bundle-portability.test.ts +120 -0
  27. package/src/scenarios/fixtures-valid.test.ts +34 -0
  28. package/src/scenarios/goal-standing-continuation.test.ts +139 -0
  29. package/src/scenarios/grpc-transport.test.ts +108 -0
  30. package/src/scenarios/i18n-negotiation.test.ts +181 -0
  31. package/src/scenarios/interrupt-token-matrix.test.ts +2 -2
  32. package/src/scenarios/media-url-inline-cap.test.ts +5 -3
  33. package/src/scenarios/proposal-reviewable-learning.test.ts +129 -0
  34. package/src/scenarios/spec-corpus-validity.test.ts +107 -0
  35. package/src/scenarios/stream-text-fixture.test.ts +212 -0
  36. package/src/scenarios/version-fold.test.ts +193 -0
  37. package/src/scenarios/wasm-pack-memory-cap.test.ts +4 -2
  38. package/src/scenarios/webhook-tenant-isolation.test.ts +184 -0
package/api/asyncapi.yaml CHANGED
@@ -2,12 +2,12 @@ asyncapi: 3.1.0
2
2
 
3
3
  info:
4
4
  title: Workflow Orchestration Protocol (openwop) SSE Event Stream
5
- version: "1.0"
5
+ version: "1.1.0"
6
6
  externalDocs:
7
7
  description: openwop spec v1 corpus
8
8
  url: https://openwop.dev/spec/v1/
9
9
  description: |
10
- Canonical AsyncAPI 3.0 specification for the openwop server's
10
+ Canonical AsyncAPI 3.1.0 specification for the openwop server's
11
11
  Server-Sent Events surface. Formalizes `stream-modes.md`
12
12
  and references the run-event JSON Schema via `$ref` so external SDK
13
13
  authors can codegen typed consumers without re-reading the prose.
@@ -55,18 +55,27 @@ servers:
55
55
  channels:
56
56
 
57
57
  heartbeatEvents:
58
- address: /heartbeats/{heartbeatId}/events
58
+ # Logical channel — `address: null` per AsyncAPI 3.x ("address not
59
+ # applicable / host-defined"). RFC 0094 §I: host-capabilities.md
60
+ # §host.heartbeat defines the two heartbeat events but documents NO
61
+ # HTTP delivery path for them (they are heartbeat-scoped, NOT
62
+ # run-event-log entries, so they do not ride /runs/{runId}/events
63
+ # either). The previous `/heartbeats/{heartbeatId}/events` address
64
+ # implied an undocumented REST surface; the delivery transport is a
65
+ # host concern until an RFC specifies one.
66
+ address: null
59
67
  title: Heartbeat evaluation events (RFC 0060)
60
- summary: Per-tick heartbeat evaluation + state-change notifications.
68
+ summary: Per-tick heartbeat evaluation + state-change notifications (logical channel).
61
69
  description: |
62
70
  RFC 0060 `host.heartbeat`. Heartbeat-scoped (NOT a run-event
63
71
  stream): a host advertising `capabilities.heartbeat.supported: true`
64
72
  emits `heartbeat.evaluated` every tick and `heartbeat.stateChanged`
65
73
  only on a predicate-state transition. Both are observability-only;
66
74
  consumers MAY ignore them.
67
- parameters:
68
- heartbeatId:
69
- description: Host-assigned heartbeat identifier.
75
+
76
+ LOGICAL channel: `host-capabilities.md` §host.heartbeat documents
77
+ the event shapes but no HTTP address; how a host delivers them
78
+ (webhook, host-internal bus, vendor stream) is host-defined.
70
79
  messages:
71
80
  heartbeatEvaluated: { $ref: '#/components/messages/HeartbeatEvaluated' }
72
81
  heartbeatStateChanged: { $ref: '#/components/messages/HeartbeatStateChanged' }
@@ -117,6 +126,11 @@ channels:
117
126
  deploymentRolledBack: { $ref: '#/components/messages/DeploymentRolledBack' }
118
127
  deploymentCanaryAdjusted: { $ref: '#/components/messages/DeploymentCanaryAdjusted' }
119
128
  deploymentStateChanged: { $ref: '#/components/messages/DeploymentStateChanged' }
129
+ proposalCreated: { $ref: '#/components/messages/ProposalCreated' }
130
+ proposalActivated: { $ref: '#/components/messages/ProposalActivated' }
131
+ goalEvaluated: { $ref: '#/components/messages/GoalEvaluated' }
132
+ goalClosed: { $ref: '#/components/messages/GoalClosed' }
133
+ importApplied: { $ref: '#/components/messages/ImportApplied' }
120
134
 
121
135
  runEventsValues:
122
136
  address: /runs/{runId}/events
@@ -200,6 +214,13 @@ operations:
200
214
  for resumption — server begins streaming from the sequence
201
215
  AFTER the supplied ID and MUST NOT re-emit the resumption
202
216
  point itself.
217
+
218
+ Mixed mode (RFC 0094 §I note): the binding's single-value
219
+ `streamMode` enum below describes THIS mode's pure subscription;
220
+ `streamMode` additionally accepts comma-separated combinations
221
+ (e.g. `updates,messages`) per `stream-modes.md` §"Mixed mode" —
222
+ union-of-filters semantics, per-event `event:` labels.
223
+ `values` MUST NOT combine with other modes.
203
224
  bindings:
204
225
  http:
205
226
  method: GET
@@ -217,6 +238,11 @@ operations:
217
238
  $ref: '#/channels/runEventsValues'
218
239
  title: Subscribe to values stream
219
240
  summary: Receive full state snapshots after every transition.
241
+ description: |
242
+ Mixed mode (RFC 0094 §I note): `values` is EXCLUSIVE — it MUST NOT
243
+ be combined in a comma-separated `streamMode` list
244
+ (`stream-modes.md` §"Mixed mode": state.snapshot semantics need
245
+ exclusive ownership). The binding's single-value enum is exact here.
220
246
  bindings:
221
247
  http:
222
248
  method: GET
@@ -234,6 +260,12 @@ operations:
234
260
  $ref: '#/channels/runEventsMessages'
235
261
  title: Subscribe to messages stream
236
262
  summary: Receive per-token AI chunks.
263
+ description: |
264
+ Mixed mode (RFC 0094 §I note): the binding's single-value
265
+ `streamMode` enum below describes the pure `messages` subscription;
266
+ `streamMode` additionally accepts comma-separated combinations
267
+ (e.g. `updates,messages`) per `stream-modes.md` §"Mixed mode".
268
+ `values` MUST NOT combine with other modes.
237
269
  bindings:
238
270
  http:
239
271
  method: GET
@@ -251,6 +283,12 @@ operations:
251
283
  $ref: '#/channels/runEventsDebug'
252
284
  title: Subscribe to debug stream
253
285
  summary: Receive every engine event including internal/log/lease.
286
+ description: |
287
+ Mixed mode (RFC 0094 §I note): the binding's single-value
288
+ `streamMode` enum below describes the pure `debug` subscription;
289
+ `streamMode` additionally accepts comma-separated combinations
290
+ (e.g. `updates,debug`) per `stream-modes.md` §"Mixed mode".
291
+ `values` MUST NOT combine with other modes.
254
292
  bindings:
255
293
  http:
256
294
  method: GET
@@ -342,6 +380,47 @@ components:
342
380
  payload:
343
381
  $ref: '#/components/schemas/DeploymentStateChangedPayload'
344
382
 
383
+ # ── Reviewable learning (RFC 0096) — content-free proposal lifecycle ──
384
+ ProposalCreated:
385
+ name: proposal.created
386
+ title: Proposal created (RFC 0096)
387
+ summary: The host synthesized a reviewable-learning draft. Content-free — ids/kind/refs only, never the artifact body or rationale (proposal-inert-until-applied). Emitted only when capabilities.agents.proposals is advertised.
388
+ contentType: application/json
389
+ payload:
390
+ $ref: '#/components/schemas/ProposalCreatedPayload'
391
+ ProposalActivated:
392
+ name: proposal.activated
393
+ title: Proposal activated (RFC 0096)
394
+ summary: A proposal was applied (RFC 0051/0049-gated). Content-free; the installed artifact byte-matches the last-persisted draft (proposal-no-resynthesis).
395
+ contentType: application/json
396
+ payload:
397
+ $ref: '#/components/schemas/ProposalActivatedPayload'
398
+
399
+ # ── Standing goals (RFC 0097) — content-free judge/continuation events ─
400
+ GoalEvaluated:
401
+ name: goal.evaluated
402
+ title: Goal evaluated (RFC 0097)
403
+ summary: A judge check ran against a standing goal. Content-free — no objective text; the verdict is recorded (not recomputed on replay).
404
+ contentType: application/json
405
+ payload:
406
+ $ref: '#/components/schemas/GoalEvaluatedPayload'
407
+ GoalClosed:
408
+ name: goal.closed
409
+ title: Goal closed (RFC 0097)
410
+ summary: A standing goal stopped continuation (satisfied / escalated / abandoned / bound-exceeded). Content-free.
411
+ contentType: application/json
412
+ payload:
413
+ $ref: '#/components/schemas/GoalClosedPayload'
414
+
415
+ # ── Portability (RFC 0098) — content-free import event ───────────────
416
+ ImportApplied:
417
+ name: import.applied
418
+ title: Import applied (RFC 0098)
419
+ summary: An estate import was applied. Content-free — counts + refs only, never item payloads or secret values (export-bundle-no-credential-material).
420
+ contentType: application/json
421
+ payload:
422
+ $ref: '#/components/schemas/ImportAppliedPayload'
423
+
345
424
  # ── Run-lifecycle ────────────────────────────────────────────────────
346
425
  RunStarted:
347
426
  name: run.started
@@ -496,7 +575,10 @@ components:
496
575
  name: interrupt.requested
497
576
  title: Interrupt requested (canonical HITL primitive)
498
577
  summary: |
499
- The discriminated-union form of approval/clarification/external-event/custom.
578
+ The discriminated-union form of the full `interrupt.md` kind union
579
+ (RFC 0094 §E): approval / clarification / external-event / custom /
580
+ conversation.start / conversation.exchange / conversation.close /
581
+ low-confidence.
500
582
  Servers emitting `interrupt.requested` SHOULD also emit the legacy
501
583
  kind-specific event (`approval.requested` etc) for backward compat
502
584
  until consumers migrate.
@@ -578,6 +660,14 @@ components:
578
660
  DeploymentRolledBackPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/deploymentRolledBack' }
579
661
  DeploymentCanaryAdjustedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/deploymentCanaryAdjusted' }
580
662
  DeploymentStateChangedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/deploymentStateChanged' }
663
+ # RFC 0096 — reviewable-learning proposal event payloads.
664
+ ProposalCreatedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/proposalCreated' }
665
+ ProposalActivatedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/proposalActivated' }
666
+ # RFC 0097 — standing-goal event payloads.
667
+ GoalEvaluatedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/goalEvaluated' }
668
+ GoalClosedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/goalClosed' }
669
+ # RFC 0098 — portability import event payload.
670
+ ImportAppliedPayload: { $ref: '../schemas/run-event-payloads.schema.json#/$defs/importApplied' }
581
671
 
582
672
  # RFC 0056. The run.annotated notification carries an Annotation —
583
673
  # NOT a RunEventDoc — because annotations are a side-resource, not
@@ -605,33 +695,13 @@ components:
605
695
  $ref: '../schemas/run-snapshot.schema.json'
606
696
 
607
697
  AiMessageChunkPayload:
608
- # S2 closure (2026-04-27): tiered metadata. Bare {nodeId, runId,
609
- # chunk, isLast} is the minimum compliant payload; `meta`
610
- # adds Tier 1 typed slots (finishReason / logprobs / toolCalls /
611
- # model / usage) and a Tier 2 provider-pass-through escape hatch.
612
- # Schema definition lives at run-event-payloads.schema.json#$defs.outputChunk
613
- # referenced here verbatim so the SSE consumer + run-event log
614
- # share a single shape contract.
615
- type: object
616
- required: [nodeId, runId, chunk, isLast]
617
- properties:
618
- nodeId: { type: string }
619
- runId: { type: string }
620
- chunk:
621
- type: string
622
- description: The new token(s) since the previous chunk.
623
- isLast:
624
- type: boolean
625
- description: True for the final chunk of a given AI node call.
626
- meta:
627
- type: object
628
- description: |
629
- Tiered metadata. Tier 1: typed slots — `finishReason`
630
- ("stop"|"length"|"tool_calls"|"content_filter"), `logprobs`,
631
- `toolCalls`, `model`, `usage` ({promptTokens,
632
- completionTokens, totalTokens}). Tier 2: provider-pass-through
633
- via `provider` + `providerExtensions`. Consumers SHOULD prefer
634
- Tier 1; Tier 2 is the escape hatch for fields the spec hasn't
635
- typed yet. See run-event-payloads.schema.json#$defs._chunkMeta
636
- for the full definition + per-field constraints.
637
- additionalProperties: true
698
+ # S2 closure (2026-04-27) + RFC 0094 §D single-sourcing: the payload
699
+ # is the canonical `outputChunk` definition in
700
+ # run-event-payloads.schema.json referenced (not hand-copied, the
701
+ # prior inline copy was one of the three drifting definitions) so the
702
+ # SSE consumer + run-event log share exactly one shape contract.
703
+ # Minimum compliant payload: {nodeId, runId, chunk, isLast} per
704
+ # stream-modes.md §messages; `meta` adds Tier 1 typed slots
705
+ # (finishReason / logprobs / toolCalls / model / usage) and a Tier 2
706
+ # provider-pass-through escape hatch (see #$defs/_chunkMeta).
707
+ $ref: '../schemas/run-event-payloads.schema.json#/$defs/outputChunk'
package/api/openapi.yaml CHANGED
@@ -2,7 +2,7 @@ openapi: 3.1.0
2
2
 
3
3
  info:
4
4
  title: Workflow Orchestration Protocol (openwop) API
5
- version: "1.0"
5
+ version: "1.1.0"
6
6
  summary: REST surface for declaring, executing, suspending, resuming, and observing multi-step workflows.
7
7
  description: |
8
8
  Canonical OpenAPI 3.1 specification for openwop-compliant servers. Generated from `rest-endpoints.md` and references the JSON Schemas in `schemas/`.
@@ -16,6 +16,16 @@ info:
16
16
  - `run-options.md` — `configurable`/`tags`/`metadata`
17
17
  - `interrupt.md` — HITL primitive
18
18
  - `replay.md` — `:fork` endpoint
19
+
20
+ **Registry scope (RFC 0094 §I).** This document specifies the HOST
21
+ surface. The production node-pack registry surface (`/v1/packs/*` —
22
+ publish/get/delete/sig, deprecation, yank, key rotation) is specified in
23
+ `spec/v1/node-packs.md` §"Registry HTTP API" + `spec/v1/registry-operations.md`
24
+ and is served by a registry service (e.g. the planned hosted reference
25
+ registry `packs.openwop.dev`, or a third-party/private registry
26
+ implementation) — a distinct deployable, out of scope for this host
27
+ OpenAPI document. Only the test-mode mirror (`/v1/packs-test/*`, RFC 0025)
28
+ is host-mounted and documented here.
19
29
  contact:
20
30
  name: openwop spec working group
21
31
  url: https://openwop.dev/spec/v1/
@@ -75,6 +85,11 @@ tags:
75
85
  19-code publish error catalog without `packs:publish` scope on the real registry. Hosts that haven't
76
86
  mounted this surface MUST return `404 Not Found` for every path under `/v1/packs-test/`.
77
87
 
88
+ Scope note (RFC 0094 §I): the PRODUCTION `/v1/packs/*` surface these paths mirror is specified in
89
+ `spec/v1/registry-operations.md` + `node-packs.md` §"Registry HTTP API" and is served by a registry
90
+ service (a distinct deployable from the host), so it is intentionally NOT defined in this host
91
+ OpenAPI document.
92
+
78
93
  # ─────────────────────────────────────────────────────────────────────────────
79
94
  # PATHS
80
95
  # ─────────────────────────────────────────────────────────────────────────────
@@ -178,7 +193,13 @@ paths:
178
193
  # routing fields, plus the openwop RunOptions overlay (configurable,
179
194
  # tags, metadata) hoisted into a first-class JSON Schema at
180
195
  # ../schemas/run-options.schema.json. allOf composes the two
181
- # so callers see one unified body shape.
196
+ # so callers see one unified body shape. RFC 0094 §A: neither
197
+ # allOf branch is closed via additionalProperties (two closed
198
+ # branches inside one allOf made every documented body
199
+ # unsatisfiable); the composed request is closed here with
200
+ # `unevaluatedProperties: false` (JSON Schema 2020-12), so
201
+ # undeclared properties still fail at the composition level.
202
+ unevaluatedProperties: false
182
203
  allOf:
183
204
  - type: object
184
205
  properties:
@@ -215,7 +236,6 @@ paths:
215
236
  type: string
216
237
  minLength: 1
217
238
  description: RFC 0081 — the manifest agent the eval suite targets. Required when mode is `eval`.
218
- additionalProperties: false
219
239
  if:
220
240
  properties: { mode: { const: eval } }
221
241
  required: [mode]
@@ -1843,6 +1863,9 @@ paths:
1843
1863
  `conflict`/`version_conflict`) MUST be served verbatim. The
1844
1864
  test catalog MUST be isolated per RFC 0025 §C — a pack PUT'd
1845
1865
  here MUST NOT appear in `GET /v1/packs/{name}` listings.
1866
+ The mirrored production path is registry-service surface
1867
+ (`registry-operations.md`), not defined in this host document
1868
+ (see the `packs-test` tag scope note).
1846
1869
  operationId: putTestPackTarball
1847
1870
  parameters:
1848
1871
  - in: header
@@ -1914,7 +1937,7 @@ paths:
1914
1937
  get:
1915
1938
  tags: [packs-test]
1916
1939
  summary: Fetch a published test-catalog tarball.
1917
- description: 'Mirror of `GET /v1/packs/{name}/-/{version}.tgz`. Returns the gzipped tarball bytes with `Content-Type: application/tar+gzip` and an `ETag: "sha256-..."` matching the manifest''s `tarballSha256`.'
1940
+ description: 'Mirror of `GET /v1/packs/{name}/-/{version}.tgz`. Returns the gzipped tarball bytes with `Content-Type: application/tar+gzip` and an `ETag: "sha256-..."` matching the manifest''s `tarballSha256`. The mirrored production path is registry-service surface (`registry-operations.md`), not defined in this host document (see the `packs-test` tag scope note).'
1918
1941
  operationId: getTestPackTarball
1919
1942
  responses:
1920
1943
  '200':
@@ -1951,7 +1974,10 @@ paths:
1951
1974
  for versions older than the registry's unpublish window
1952
1975
  (default 72h). Test-mode implementations MAY shorten the
1953
1976
  window for tractable conformance fixtures but MUST surface
1954
- the same error code.
1977
+ the same error code. The mirrored production path is
1978
+ registry-service surface (`registry-operations.md`), not
1979
+ defined in this host document (see the `packs-test` tag
1980
+ scope note).
1955
1981
  operationId: deleteTestPackVersion
1956
1982
  responses:
1957
1983
  '204':
@@ -1983,7 +2009,9 @@ paths:
1983
2009
  description: |
1984
2010
  Mirror of `GET /v1/packs/{name}/-/{version}.sig`. Returns the
1985
2011
  signature blob over `pack.json` for this version. MAY 302-redirect
1986
- to a storage-backend signed URL.
2012
+ to a storage-backend signed URL. The mirrored production path is
2013
+ registry-service surface (`registry-operations.md`), not defined
2014
+ in this host document (see the `packs-test` tag scope note).
1987
2015
  operationId: getTestPackSignature
1988
2016
  responses:
1989
2017
  '200':