@openwop/spec-artifacts 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/CORPUS-STAMP.json +70 -67
- package/api/seams-v2.yaml +238 -322
- package/api/v2/asyncapi.yaml +83 -113
- package/api/v2/openapi.yaml +408 -724
- package/package.json +1 -1
- package/schemas/audit-verify-result.schema.json +2 -2
- package/schemas/v2/audit-verify-result.schema.json +2 -2
- package/schemas/v2/capabilities.schema.json +4 -7
- package/schemas/v2/error-envelope.schema.json +144 -69
- package/spec/v1/alias-detectors.json +6 -0
- package/spec/v1/core-standard-manifest.json +2 -2
- package/spec/v1/deprecations.json +22 -0
- package/spec/v1/gaps.json +188 -1
- package/spec/v1/migrations.json +18 -0
- package/spec/v2/README.md +35 -37
- package/spec/v2/core/capabilities.md +186 -119
- package/spec/v2/core/conformance.md +78 -27
- package/spec/v2/core/connection-packs.md +8 -3
- package/spec/v2/core/conversation.md +10 -4
- package/spec/v2/core/errors.md +30 -9
- package/spec/v2/core/events.md +116 -80
- package/spec/v2/core/form-content-packs.md +11 -13
- package/spec/v2/core/headers.md +23 -14
- package/spec/v2/core/host-services.md +22 -5
- package/spec/v2/core/i18n.md +64 -0
- package/spec/v2/core/idempotency.md +48 -36
- package/spec/v2/core/identity.md +110 -42
- package/spec/v2/core/interop.md +82 -21
- package/spec/v2/core/interrupt.md +79 -40
- package/spec/v2/core/node-pack-runtimes.md +18 -4
- package/spec/v2/core/oauth.md +40 -6
- package/spec/v2/core/overview.md +61 -33
- package/spec/v2/core/packs.md +53 -28
- package/spec/v2/core/persistence.md +61 -80
- package/spec/v2/core/portability.md +44 -0
- package/spec/v2/core/replay.md +56 -42
- package/spec/v2/core/runs.md +198 -60
- package/spec/v2/core/security-defaults.md +57 -33
- package/spec/v2/core/tool-catalog.md +29 -6
- package/spec/v2/core/versioning.md +115 -50
- package/spec/v2/core/webhooks.md +61 -20
- package/spec/v2/core/workflow-chain-packs.md +16 -14
- package/spec/v2/corrections.json +17 -1
- package/spec/v2/corrections.schema.json +1 -1
- package/spec/v2/declaration.json +26 -9
- package/spec/v2/declaration.schema.json +10 -0
- package/spec/v2/errors.json +121 -4
- package/spec/v2/errors.schema.json +38 -0
- package/spec/v2/ext/README.md +36 -23
- package/spec/v2/ext/a2uiSurface/README.md +75 -89
- package/spec/v2/ext/brand/README.md +15 -15
- package/spec/v2/ext/canvas/README.md +15 -15
- package/spec/v2/ext/chat/README.md +15 -15
- package/spec/v2/ext/coordination/README.md +15 -15
- package/spec/v2/ext/dataIntegration/README.md +17 -20
- package/spec/v2/ext/entities/README.md +15 -15
- package/spec/v2/ext/grpc-transport/README.md +6 -8
- package/spec/v2/ext/kanban/README.md +15 -15
- package/spec/v2/ext/knowledge/README.md +15 -15
- package/spec/v2/ext/launchStudio/README.md +16 -16
- package/spec/v2/ext/messaging/README.md +15 -15
- package/spec/v2/ext/portability/README.md +3 -17
- package/spec/v2/ext/provider-idempotency/README.md +7 -10
- package/spec/v2/ext/restTransport/README.md +30 -19
- package/spec/v2/ext/sandbox-runtime-notes/README.md +7 -9
- package/spec/v2/ext/webResearch/README.md +16 -16
- package/spec/v2/profiles.json +6 -2
- package/spec/v2/release.json +2 -2
- package/spec/v2/surface-baseline.json +544 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Interrupt
|
|
2
2
|
|
|
3
|
-
> **Status: Stable
|
|
3
|
+
> **Status: Stable.**
|
|
4
4
|
> **Normative home:** `interrupt`.
|
|
5
5
|
|
|
6
6
|
## Why this exists
|
|
@@ -11,25 +11,49 @@
|
|
|
11
11
|
|
|
12
12
|
`schemas/v2/suspend-request.schema.json` (`InterruptPayload`) is closed and discriminated by `kind`; `kind`, `key` and `data` are REQUIRED.
|
|
13
13
|
|
|
14
|
-
| Kind | `data` (required fields) |
|
|
14
|
+
| Kind | `data` (required fields) | Snapshot status / gate |
|
|
15
15
|
| --- | --- | --- |
|
|
16
|
-
| `approval` | `artifactId`, `artifactType`, `title`, `actions` | 5-action vocabulary
|
|
17
|
-
| `clarification` | `questions[]` (`id`, `question`, optional `schema`) |
|
|
18
|
-
| `external-event` | `eventType`, `correlation` |
|
|
19
|
-
| `custom` | `customKind`, optional `payload` |
|
|
20
|
-
| `conversation.start` | `conversationId` | Gated on
|
|
21
|
-
| `conversation.exchange` | `conversationId`, `prompt` |
|
|
16
|
+
| `approval` | `artifactId`, `artifactType`, `title`, `actions` | 5-action vocabulary, quorum and eligibility (§Approval) |
|
|
17
|
+
| `clarification` | `questions[]` (`id`, `question`, optional `schema`) | `waiting-input` |
|
|
18
|
+
| `external-event` | `eventType`, `correlation` | `waiting-external` |
|
|
19
|
+
| `custom` | `customKind`, optional `payload` | — |
|
|
20
|
+
| `conversation.start` | `conversationId` | Gated on `conversation` ([capabilities.md](capabilities.md)) |
|
|
21
|
+
| `conversation.exchange` | `conversationId`, `prompt` | — |
|
|
22
22
|
| `conversation.close` | `conversationId` | Gated as above |
|
|
23
|
-
| `low-confidence` | `agentId`, `threshold`, `observed` |
|
|
24
|
-
| `credential` | `provider`, `scopes`, `reason`, `connectUrl` |
|
|
23
|
+
| `low-confidence` | `agentId`, `threshold`, `observed` | — |
|
|
24
|
+
| `credential` | `provider`, `scopes`, `reason`, `connectUrl` | `waiting-input`; gated on `oauth.credentialInterrupt` ([oauth.md](oauth.md)) |
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Per-kind rules:
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
- `external-event`: the snapshot status MUST be `waiting-external`.
|
|
29
|
+
- `custom`: a host MUST accept and persist it; rendering is best-effort.
|
|
30
|
+
- `conversation.start`: `conversationId` MUST be tenant-unique and MUST NOT be assumed resolvable on another host.
|
|
31
|
+
- `conversation.exchange`: the resume value MUST validate against `outcomeSchema` when supplied.
|
|
32
|
+
- `low-confidence`: an `agent.decided` with `confidence` below the threshold MUST be followed by `node.suspended { reason: 'low-confidence' }`. The per-run threshold is `configurable.run.escalationThreshold` ([runs.md](runs.md)).
|
|
33
|
+
- `credential`: the resume value is `{ outcome }` and carries no credential.
|
|
34
|
+
- An interrupt of any kind MUST NOT solicit credential material; a credential is acquired through `credential`.
|
|
35
|
+
|
|
36
|
+
### Re-entry and resume values
|
|
37
|
+
|
|
38
|
+
`key` is the deterministic re-entry key.
|
|
39
|
+
|
|
40
|
+
- A host MUST invoke an interrupt with key `K` at most once for the lifetime of the run.
|
|
41
|
+
- On recovery the engine MUST consult the event log, find the prior `interrupt.resolved`, and return the persisted `resumeValue` without emitting a second `interrupt.requested`.
|
|
42
|
+
- An in-memory cache MAY serve in-process replays but MUST NOT replace the event log for cross-process replays.
|
|
43
|
+
- A host MUST validate the resume value against `resumeSchema` when one is declared, and MUST refuse a failing value with `400 validation_error`.
|
|
44
|
+
|
|
45
|
+
`timeoutMs`, when set, is the interrupt's own deadline.
|
|
29
46
|
|
|
30
47
|
## Events
|
|
31
48
|
|
|
32
|
-
Every kind uses two registered types (events.md
|
|
49
|
+
Every kind uses two registered types ([events.md](events.md)):
|
|
50
|
+
|
|
51
|
+
- `interrupt.requested` — the payload is the `InterruptPayload` verbatim.
|
|
52
|
+
- `interrupt.resolved` — the closed payload is `interruptResolved`. Resolving an approval-kind interrupt MUST record the applied `action` there, with the field §Approval requires.
|
|
53
|
+
|
|
54
|
+
The kind-specific `approval.*` and `clarification.*` types remain registered. Their payload definitions in `schemas/v2/run-event-payloads.schema.json` are `$ref` aliases of `interruptRequested` and `interruptResolved`, so there is one shape per direction. A host emitting `interrupt.requested` SHOULD also emit the kind-specific type until its consumers migrate.
|
|
55
|
+
|
|
56
|
+
Both events are durable and appear in the `updates` and `debug` stream modes. While suspended, `RunSnapshot.currentNodeId` names the node and `status` is `waiting-approval`, `waiting-input` or `waiting-external`.
|
|
33
57
|
|
|
34
58
|
## Resolve surfaces
|
|
35
59
|
|
|
@@ -39,28 +63,40 @@ Every kind uses two registered types (events.md): `interrupt.requested`, whose p
|
|
|
39
63
|
| `inspectInterruptByToken` | `GET /interrupts/{token}` | the token | — (returns the `InterruptPayload`) |
|
|
40
64
|
| `resolveInterruptByToken` | `POST /interrupts/{token}` | the token | `{ resumeValue }` (closed) |
|
|
41
65
|
|
|
42
|
-
A host MUST expose the run-scoped surface and SHOULD expose the signed-token surface for callers not authenticated to the protocol.
|
|
66
|
+
- A host MUST expose the run-scoped surface, and SHOULD expose the signed-token surface for callers not authenticated to the protocol.
|
|
67
|
+
- Every resolve MUST honor `Idempotency-Key` ([idempotency.md](idempotency.md)).
|
|
68
|
+
- Of two concurrent resolves, exactly one MUST succeed; the other MUST receive `409 interrupt_already_resolved`.
|
|
69
|
+
|
|
70
|
+
### Callback delivery
|
|
71
|
+
|
|
72
|
+
`createRun.callbackUrl` names where a host that advertises `interrupt.callbackDelivery: true` delivers notice of an interrupt, so its holder can resolve it through the token surface. The payload, timing and signing are host-defined.
|
|
73
|
+
|
|
74
|
+
A host advertising the facet:
|
|
43
75
|
|
|
44
|
-
|
|
76
|
+
- MUST refuse at `createRun`, with `400 validation_error` and `details.field: "callbackUrl"`, a URL the `webhooks.md` §Egress registration guard would refuse;
|
|
77
|
+
- MUST re-validate every resolved address at delivery;
|
|
78
|
+
- MUST NOT follow a redirect.
|
|
79
|
+
|
|
80
|
+
A host that does not advertise it SHOULD refuse the member and MUST NOT claim delivery it does not perform.
|
|
81
|
+
|
|
82
|
+
### Errors
|
|
45
83
|
|
|
46
84
|
| Status | Code | Condition |
|
|
47
85
|
| --- | --- | --- |
|
|
48
|
-
| `400` | `validation_error` | `resumeValue` fails `resumeSchema
|
|
86
|
+
| `400` | `validation_error` | `resumeValue` fails `resumeSchema`, or the approval action is not in `actions` |
|
|
49
87
|
| `401` | `interrupt_token_invalid` | MAC, `alg` or `kid` not accepted |
|
|
50
88
|
| `404` | `not_found` | No such run or node |
|
|
51
|
-
| `409` | `interrupt_already_resolved` | Already resolved
|
|
89
|
+
| `409` | `interrupt_already_resolved` | Already resolved; the run is cancelled or completed (both surfaces); or the token was invalidated |
|
|
52
90
|
| `410` | `interrupt_expired` | Token past `expiresAt` (token surface only) |
|
|
53
91
|
|
|
54
92
|
## Tokens
|
|
55
93
|
|
|
56
|
-
The token grammar
|
|
94
|
+
The token grammar and the `interrupt.tokenAlgs[]` / `kid` check are [identity.md](identity.md) §4 (`401 interrupt_token_invalid`).
|
|
57
95
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
| Verification | MAC comparison MUST be constant-time. `kid` selects the verification secret so secrets rotate without orphaning outstanding tokens. |
|
|
63
|
-
| Intent | A token minted with `intent: resolve` authorizes both operations; a host MAY mint `intent: inspect` tokens, and a resolve with one MUST be refused with `403`. |
|
|
96
|
+
- **Expiry.** Every token MUST carry `expiresAt`. The default SHOULD be 30 minutes, and a host MUST cap the lifetime at the interrupt's `timeoutMs` when one exists. A token MUST NOT outlive the interrupt it resolves; past `expiresAt` the host MUST answer `410 interrupt_expired`.
|
|
97
|
+
- **Invalidation.** A token MUST be invalidated when its interrupt is resolved or its run is cancelled or completed. Later use MUST answer `409 interrupt_already_resolved`.
|
|
98
|
+
- **Verification.** MAC comparison MUST be constant-time. `kid` selects the verification secret, so secrets rotate without orphaning outstanding tokens.
|
|
99
|
+
- **Intent.** A token minted with `intent: resolve` authorizes both operations. A host MAY mint `intent: inspect` tokens; a resolve with one MUST be refused with `403`.
|
|
64
100
|
|
|
65
101
|
## Approval
|
|
66
102
|
|
|
@@ -73,23 +109,26 @@ The token grammar, the `interrupt.tokenAlgs[]` / `kid` check and the v1 drain ar
|
|
|
73
109
|
| `refine` | `refineFeedback { scope: whole \| section \| items, sectionPath?, itemIds?, tags?, text? }` |
|
|
74
110
|
| `edit-accept` | `editedArtifactData` |
|
|
75
111
|
|
|
76
|
-
Every resume carries `decidedAt
|
|
112
|
+
- Every resume carries `decidedAt`. `decidedBy` MAY be omitted by an authenticated caller, and every consumer MUST treat it as an opaque string.
|
|
113
|
+
- `requiredApprovals` sets the quorum (default 1). `rejectionPolicy` is `single-veto` (default) or `majority`.
|
|
114
|
+
- When `overrideBypassesQuorum` is `true`, a configured override principal MAY release the gate alone; otherwise its vote counts once.
|
|
77
115
|
|
|
78
116
|
## Approver enforcement
|
|
79
117
|
|
|
80
|
-
Enforcement is an obligation of the fields, not a discovery flag
|
|
118
|
+
Enforcement is an obligation of the fields, not a discovery flag. The facet `spec/v2/facets/interrupt.schema.json` carries `tokenAlgs[]` (REQUIRED) and `refKinds[]` ⊆ `principal`, `group`, `role`.
|
|
81
119
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
host
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
120
|
+
- **`approversList`** (explicit principals) binds everywhere: a host advertising `interrupt` MUST refuse a resolver not in the list.
|
|
121
|
+
- **`approverGroupRefs`** binds only where `refKinds` includes `group`: the host MUST surface the field unchanged and MUST resolve and enforce its members as eligible approvers.
|
|
122
|
+
- **`approverRoleRefs`** binds only where `refKinds` includes `role`: as for groups, with holders.
|
|
123
|
+
- **`audience`** is a notification hint, never eligibility. When omitted, the host SHOULD notify the union of the eligibility refs.
|
|
124
|
+
- A host that does not advertise a ref kind MUST ignore that field.
|
|
125
|
+
|
|
126
|
+
Eligibility binds every writer of the suspension record, not every route. A host whose durable store is writable by a principal other than the engine MUST enforce the same eligibility at the store, or MUST NOT expose the record to that principal for write.
|
|
127
|
+
|
|
128
|
+
Refs are opaque to the engine; the host resolves them against its own identity model. Membership MUST be resolved at decision time and MUST NOT be re-resolved during replay or `forkRun`: the recorded eligibility decision is fixed history ([replay.md](replay.md)).
|
|
129
|
+
|
|
130
|
+
## During the v1 overlap
|
|
131
|
+
|
|
132
|
+
The v1 interrupt-token drain is [identity.md](identity.md) §4.
|
|
133
|
+
|
|
134
|
+
*Sources: RFC 0170, RFC 0171, RFC 0173, RFC 0187, RFC 0196.*
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Node-pack runtimes
|
|
2
2
|
|
|
3
|
-
> **Status: Stable
|
|
3
|
+
> **Status: Stable.**
|
|
4
4
|
> **Normative home:** `nodePackRuntimes`.
|
|
5
5
|
|
|
6
6
|
## Why this exists
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
This is the v2 contract for the language a node pack runs in. It also lets a `remote` pack name its MCP server by an inline MCP Registry record instead of a bare URL. The shape is `$defs/Runtime` in `schemas/v2/node-pack-manifest.schema.json`.
|
|
9
9
|
|
|
10
10
|
## Languages
|
|
11
11
|
|
|
@@ -13,8 +13,22 @@ v2 carried this family only by pointing at `spec/v1/node-packs.md`. This is its
|
|
|
13
13
|
|
|
14
14
|
## WASM
|
|
15
15
|
|
|
16
|
-
A host that loads `wasm` packs MUST advertise `nodePackRuntimes.wasm` with at least one `abiVersions[]` entry
|
|
16
|
+
- A host that loads `wasm` packs MUST advertise `nodePackRuntimes.wasm` with at least one `abiVersions[]` entry, and MUST reject at load a pack whose `openwop_abi_version()` is not listed.
|
|
17
|
+
- When it advertises `maxMemoryBytes` it MUST enforce it and emit `cap.breached` with `kind: "wasm-memory"` on a breach.
|
|
18
|
+
- `nodePackRuntimes.wasmComponent` advertises `wasm-component` loading; its interfaces are reserved for a later RFC.
|
|
17
19
|
|
|
18
20
|
## The MCP registry record
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
A `remote` runtime MAY carry `mcpServer`, an inline subset of an MCP Registry `server.json` (schema `2025-12-11`):
|
|
23
|
+
|
|
24
|
+
- `name`, `description`, `version`;
|
|
25
|
+
- optional `title`, `websiteUrl` and `repository`;
|
|
26
|
+
- exactly one `remotes[]` entry of `type: "streamable-http"` with an `https://` URL.
|
|
27
|
+
|
|
28
|
+
It has no `packages[]`, `headers`, `variables` or `_meta`, so it carries no install instruction and no credential. The record is by value; this contract defines no registry lookup.
|
|
29
|
+
|
|
30
|
+
- `mcpServer` under any other language, or an `entry` that differs from `remotes[0].url`, makes the manifest invalid (`pack_validation_failed`).
|
|
31
|
+
- A host SHOULD NOT treat `name` as a verified identity: an inline record carries no proof of namespace ownership.
|
|
32
|
+
- A host that authenticates to the server does so through the node's `requiredCredentials` or `auth`.
|
|
33
|
+
|
|
34
|
+
*Sources: RFC 0008, RFC 0203.*
|
package/spec/v2/core/oauth.md
CHANGED
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
# OAuth
|
|
2
2
|
|
|
3
|
-
> **Status: Stable
|
|
3
|
+
> **Status: Stable.**
|
|
4
4
|
> **Normative home:** `oauth`, `credentials`.
|
|
5
5
|
|
|
6
6
|
## Why this exists
|
|
7
7
|
|
|
8
|
-
A connector node needs a token a user granted to a third party. The host obtains
|
|
8
|
+
A connector node needs a token a user granted to a third party. The host obtains, stores and refreshes it and hands it to the node's sandbox, so a pack names a provider and scopes and never touches the grant. Here the host is an OAuth client; [identity.md](identity.md) covers the host as a protected resource.
|
|
9
9
|
|
|
10
10
|
## Credentials
|
|
11
11
|
|
|
12
|
-
A host advertising `credentials` MUST resolve a `{ ref, scope }` reference (`schemas/v2/credential-reference.schema.json`) at node execution and inject the material into the node sandbox only
|
|
12
|
+
A host advertising `credentials` MUST resolve a `{ ref, scope }` reference (`schemas/v2/credential-reference.schema.json`) at node execution and inject the material into the node sandbox only.
|
|
13
|
+
|
|
14
|
+
- The material MUST NOT appear in inputs, variables, events, the debug bundle or replay state (invariant `credential-payload-redaction`).
|
|
15
|
+
- A failed resolution is `credential_not_found`, `credential_forbidden` (outside the caller's scope; fail closed) or `credential_scope_unsupported` (a scope not in `credentials.scopes`).
|
|
16
|
+
- With `credentials.sharing`, every reference within a scope resolves one stored credential.
|
|
17
|
+
- With `credentials.rotation` `two-key-overlap`, old and new material both resolve during the grace window; after it the old fails `credential_not_found`.
|
|
18
|
+
- `credentials.encryptionAtRest` is a claim about storage; it gates nothing.
|
|
13
19
|
|
|
14
20
|
## Token lifecycle
|
|
15
21
|
|
|
16
|
-
A host advertising `oauth
|
|
22
|
+
A host advertising `oauth`:
|
|
23
|
+
|
|
24
|
+
- MUST perform only the grants in `oauth.grants`.
|
|
25
|
+
- MUST refuse to register a node whose `auth.provider` or scope is not in `oauth.providers` (`oauth_provider_unsupported`, `oauth_scope_unsupported`).
|
|
26
|
+
- Drives the redirect and callback host-side. The code, redirect URI, `state` and PKCE verifier MUST NOT enter a run-visible surface.
|
|
27
|
+
- Persists tokens as a `credentials` entry at scope `user` or `workspace`, and refreshes them host-side.
|
|
28
|
+
- On terminal refresh failure, MUST emit `connector.auth-expired` and fail the node with `connector_auth_expired`, unless it advertises `oauth.credentialInterrupt`.
|
|
17
29
|
|
|
18
30
|
## The authorization-code client
|
|
19
31
|
|
|
@@ -25,8 +37,30 @@ On every `authorization_code` grant the host MUST:
|
|
|
25
37
|
4. validate `iss` per RFC 9207 §2.4 where the provider's issuer is known (`oauth.providers[].issuer`), and otherwise give the provider a redirect URI no other provider shares (RFC 9700 §4.4.2);
|
|
26
38
|
5. use one fixed, registered redirect URI per provider.
|
|
27
39
|
|
|
28
|
-
|
|
40
|
+
### MCP-reach providers
|
|
41
|
+
|
|
42
|
+
Where the provider is reached as an MCP server:
|
|
43
|
+
|
|
44
|
+
- The host MUST also send `resource` (RFC 8707), the server's canonical URI, in both requests.
|
|
45
|
+
- The host MUST refuse a provider whose authorization-server metadata omits `S256`.
|
|
46
|
+
- It fetches the server's Protected Resource Metadata (RFC 9728) only from URLs derived from the manifest's server URL.
|
|
47
|
+
- Discovery verifies and never selects. A discovered issuer or endpoint that differs from the manifest's, or from the tuple pinned at registration, MUST be refused `connection_auth_metadata_mismatch`. A grant for such a provider whose manifest declares no `issuer` MUST be refused the same way.
|
|
48
|
+
|
|
49
|
+
This binds a provider the host configures itself as well as one a connection pack defines: the configured server URL and issuer stand in for the manifest's, and the tuple is pinned when the host loads that configuration.
|
|
29
50
|
|
|
30
51
|
## The credential interrupt
|
|
31
52
|
|
|
32
|
-
A host advertising `oauth.credentialInterrupt` MUST suspend the node with a `credential` interrupt (interrupt.md) instead of failing it when a node declaring `auth: { type: "oauth2", provider, scopes }` is about to run and
|
|
53
|
+
A host advertising `oauth.credentialInterrupt` MUST suspend the node with a `credential` interrupt ([interrupt.md](interrupt.md)) instead of failing it when a node declaring `auth: { type: "oauth2", provider, scopes }` is about to run and:
|
|
54
|
+
|
|
55
|
+
- no credential resolves for the Subject, provider and scopes (`reason: "missing"`);
|
|
56
|
+
- one resolves with fewer scopes (`"insufficient_scope"`); or
|
|
57
|
+
- refresh failed terminally (`"expired"`).
|
|
58
|
+
|
|
59
|
+
Then:
|
|
60
|
+
|
|
61
|
+
- `connectUrl` MUST be host-owned, MUST NOT be pre-authenticated, and MUST complete only for the initiating Subject.
|
|
62
|
+
- The host resolves the interrupt when the grant completes. A resolve of `authorized` MUST be refused `400 validation_error` unless a credential now resolves.
|
|
63
|
+
- `declined` fails the node with `connector_auth_declined`.
|
|
64
|
+
- A host that binds a node to one credential reference (for example, a connection) reads "resolves" as that reference resolving with the node's scopes, carries it as `credentialRef`, and still completes `connectUrl` only for the initiating Subject.
|
|
65
|
+
|
|
66
|
+
*Sources: RFC 0046, RFC 0047, RFC 0199.*
|
package/spec/v2/core/overview.md
CHANGED
|
@@ -1,41 +1,47 @@
|
|
|
1
1
|
# OpenWOP v2 Core — Overview
|
|
2
2
|
|
|
3
|
-
> **Status: Stable
|
|
3
|
+
> **Status: Stable.**
|
|
4
4
|
|
|
5
5
|
## Why this exists
|
|
6
6
|
|
|
7
|
-
`spec/v2/core/` is
|
|
7
|
+
`spec/v2/core/` is what a host implements to pass the 2.0.0 floor. This document sets the reading order, states the six axioms, and holds the rules other documents only reference.
|
|
8
8
|
|
|
9
9
|
## Reading order
|
|
10
10
|
|
|
11
|
-
1. `overview.md` — axioms, §0, §0a, claim vocabulary, `ext/` rule
|
|
12
|
-
2. `versioning.md` — major negotiation, `OpenWOP-Version`, the 18 axes, release identity
|
|
13
|
-
3. `capabilities.md` —
|
|
14
|
-
4. `identity.md` — Subject, lanes, `SubjectLink`, id grammars, resume tokens
|
|
15
|
-
5. `runs.md` — create
|
|
16
|
-
6. `events.md
|
|
17
|
-
7. `interrupt.md
|
|
18
|
-
8. `persistence.md` — era key, v1 reader rule, pinned runs
|
|
19
|
-
9. `security-defaults.md
|
|
20
|
-
10. `packs.md
|
|
21
|
-
11. `conformance.md` — requirement ids, witness classes, bundle v3, seams profile
|
|
11
|
+
1. [`overview.md`](overview.md) — axioms, §0, §0a, claim vocabulary, the `ext/` rule
|
|
12
|
+
2. [`versioning.md`](versioning.md) — major negotiation, `OpenWOP-Version`, the 18 axes, release identity
|
|
13
|
+
3. [`capabilities.md`](capabilities.md) — the well-known resource, record type, closed root, derived profiles
|
|
14
|
+
4. [`identity.md`](identity.md) — Subject, lanes, `SubjectLink`, id grammars, resume tokens
|
|
15
|
+
5. [`runs.md`](runs.md) — create, get, cancel, fork; `configurable`; snapshot; owner
|
|
16
|
+
6. [`events.md`](events.md), [`errors.md`](errors.md), [`headers.md`](headers.md) — event `oneOf`, payload and error registries, `OpenWOP-*` headers
|
|
17
|
+
7. [`interrupt.md`](interrupt.md), [`idempotency.md`](idempotency.md), [`replay.md`](replay.md), [`conversation.md`](conversation.md) — run-side surfaces
|
|
18
|
+
8. [`persistence.md`](persistence.md) — era key, v1 reader rule, pinned runs
|
|
19
|
+
9. [`security-defaults.md`](security-defaults.md), [`webhooks.md`](webhooks.md), [`interop.md`](interop.md), [`host-services.md`](host-services.md), [`tool-catalog.md`](tool-catalog.md), [`i18n.md`](i18n.md), [`portability.md`](portability.md) — surface obligations, signatures, A2A and MCP, host services, locale, estate export
|
|
20
|
+
10. [`packs.md`](packs.md), [`node-pack-runtimes.md`](node-pack-runtimes.md), [`connection-packs.md`](connection-packs.md), [`form-content-packs.md`](form-content-packs.md), [`workflow-chain-packs.md`](workflow-chain-packs.md) — pack identity, engines ceiling
|
|
21
|
+
11. [`conformance.md`](conformance.md) — requirement ids, witness classes, bundle v3, seams profile
|
|
22
22
|
|
|
23
|
-
## Axioms in force
|
|
23
|
+
## Axioms in force
|
|
24
24
|
|
|
25
25
|
1. A MUST without a witness class is not a requirement.
|
|
26
|
-
2. One name per thing
|
|
27
|
-
3. Closed by default
|
|
28
|
-
4. Registers are data
|
|
29
|
-
5. Security defaults are obligations of the surface
|
|
30
|
-
6. Nothing persisted under v1 is orphaned
|
|
26
|
+
2. One name per thing. Every alias has a removal date in `spec/v1/deprecations.json` and a codemod id.
|
|
27
|
+
3. Closed by default. The discovery root, event envelope, payload and error registries, bundle, and `configurable` are `additionalProperties: false`. Vendor extension is one positive pattern in one namespace.
|
|
28
|
+
4. Registers are data. Gaps, risks, deprecations, migrations, witness classes, and dispositions are files with schemas and gates; prose is checked against them, never the reverse.
|
|
29
|
+
5. Security defaults are obligations of the surface. A protecting behavior binds when the surface is advertised, never when a flag is set.
|
|
30
|
+
6. Nothing persisted under v1 is orphaned. Every v1 artifact has a disposition in the migration register.
|
|
31
31
|
|
|
32
|
-
## §0 Closed-enum growth rule
|
|
32
|
+
## §0 Closed-enum growth rule
|
|
33
33
|
|
|
34
|
-
A registry-backed enum (event types, error codes, envelope kinds, reason vocabularies, lanes) grows by adding a row to its registry and regenerating.
|
|
34
|
+
A registry-backed enum (event types, error codes, envelope kinds, reason vocabularies, lanes) grows by adding a row to its registry and regenerating.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
- Consumers MUST accept an unknown member of a registry-backed enum and MUST NOT act on it.
|
|
37
|
+
- Producers MUST NOT emit an unregistered member.
|
|
38
|
+
- Adding a member is additive in v2.x. Renaming one is a major; removing one is a major except under §0a.
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
## §0a Retiring a v2 surface
|
|
41
|
+
|
|
42
|
+
A 2.x minor MUST NOT change the shape of an existing v2 surface; a new shape is a new surface added beside the old one.
|
|
43
|
+
|
|
44
|
+
A 2.x minor MAY remove a surface only when `scripts/check-v2-retirement.mjs` proves all of the following; otherwise the removal waits for 3.0:
|
|
39
45
|
|
|
40
46
|
1. Its replacement shipped in an earlier 2.x minor.
|
|
41
47
|
2. A `v2-minor` row in `spec/v1/deprecations.json` named the removal minor at least two minors and 30 days earlier.
|
|
@@ -46,26 +52,48 @@ A 2.x minor MUST NOT change the shape of an existing v2 surface; a new shape is
|
|
|
46
52
|
|
|
47
53
|
Readers MUST keep accepting a retired shape on replay, fork and poll; only emission narrows.
|
|
48
54
|
|
|
49
|
-
## v1 end-of-support
|
|
55
|
+
## v1 end-of-support
|
|
56
|
+
|
|
57
|
+
v1 support ends at the later of:
|
|
58
|
+
|
|
59
|
+
- (a) every INTEROP-MATRIX host's non-vacuous v2 bundle, plus 90 days;
|
|
60
|
+
- (b) 18 months from the v2 release — applied if and only if an independent host is in the matrix at release.
|
|
61
|
+
|
|
62
|
+
The date is computed from the matrix; nothing else MAY set it.
|
|
50
63
|
|
|
51
|
-
|
|
64
|
+
- **Counted hosts.** Those with a row in the INTEROP-MATRIX v2 table. A reference host that stays on the 1.x line through the overlap (the matrix says which) is not a v2 host and does not count.
|
|
65
|
+
- **Non-vacuous.** At least one claimed profile carries `witnessCount ≥ 1`.
|
|
66
|
+
- **Anchor.** The date the host's signed bundle was committed to `evidence/v2-host-bundles/` in the spec repository, read from the public history (`git log --diff-filter=A`). Never `generatedAt` inside the bundle, which nothing signs. A later re-certification replaces the file and does not move the anchor.
|
|
67
|
+
- **The computed date.** `evidence/v1-end-of-support.json`, GENERATED by `scripts/generate-v1-eos-clock.mjs`. `check-removal-dates.mjs` reads it and fails the v1-tree sources of every `v1-end-of-support` row on or after it.
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
### Old-major retention floors
|
|
54
70
|
|
|
55
|
-
|
|
71
|
+
Every published old-major artifact MUST remain installable at its last 1.x version for 12 months from the 2.0.0 publish (the `v2.0.0` tag's commit date). This holds independent of v1 end-of-support, which may come first. A consumer pinned to the old major MUST be able to rebuild through that window.
|
|
56
72
|
|
|
57
|
-
|
|
73
|
+
The artifacts are the npm packages `@openwop/openwop` (1.x) and `@openwop/openwop-conformance` (1.x), the PyPI package `openwop-client` (1.x), and the Go module `github.com/openwop/openwop-sdks/go` (v1.x). Their identities and last 1.x versions are in `spec/v2/retention-floors.json`. `scripts/check-retention-floors.mjs` prints the floor state and, with `--network`, probes each registry for the pinned version.
|
|
74
|
+
|
|
75
|
+
Unpublishing, deprecating-with-removal, or retracting a listed version inside the window is a Phase 5 exit failure.
|
|
76
|
+
|
|
77
|
+
## Profile claim vocabulary
|
|
78
|
+
|
|
79
|
+
These rules bind any public conformance statement:
|
|
58
80
|
|
|
59
81
|
- An unqualified "OpenWOP conformant" or "OpenWOP compatible" statement MUST mean `openwop-core-standard`, the executable floor, never the discovery predicate.
|
|
60
82
|
- A discovery-only claim MUST say `openwop-discovery-core` and MUST NOT use the same badge as `openwop-core-standard`.
|
|
61
83
|
- Every claim MUST state every additional profile it relies on; an omitted profile is an unclaimed one.
|
|
62
|
-
- A certification bundle MUST name canonical profile ids; `openwop-core` is deleted (see `capabilities.md`).
|
|
84
|
+
- A certification bundle MUST name canonical profile ids; `openwop-core` is deleted (see [`capabilities.md`](capabilities.md)).
|
|
63
85
|
- A vendor extension MUST NOT use an `openwop-*` id without an accepted RFC.
|
|
64
86
|
|
|
65
|
-
## What is `ext/`
|
|
87
|
+
## What is `ext/`
|
|
88
|
+
|
|
89
|
+
`spec/v2/core/` stays within the word budget that `scripts/check-core-budget.mjs` enforces; `spec/v2/ext/` holds the rest.
|
|
90
|
+
|
|
91
|
+
- Every `spec/v2/ext/<key>/` document MUST declare `witness` and both maturity axes (`technical`, `adoption`) in its header.
|
|
92
|
+
- A MUST with `witness: unwitnessable` MUST NOT appear in `core/`. A document whose only witness is "deferred to Active → Accepted" enters `ext/` or is deleted.
|
|
93
|
+
- An `ext/` family is advertised only under a wire-legal witness class (see [`capabilities.md`](capabilities.md)).
|
|
66
94
|
|
|
67
|
-
|
|
95
|
+
## What a MUST means (Axiom 1)
|
|
68
96
|
|
|
69
|
-
|
|
97
|
+
Every MUST, SHOULD, and MAY in `core/` is a requirement with an id in `requirements.json` and a `witness` from `witnessable-unaided | witnessable-gated | seam-gated | claims-check | negative-existence`. A seam-gated MUST is governed by [`conformance.md`](conformance.md) §"Witness class".
|
|
70
98
|
|
|
71
|
-
|
|
99
|
+
*Sources: RFC 0155, RFC 0167, RFC 0168, RFC 0169, RFC 0171, RFC 0174, RFC 0190, RFC 0197.*
|
package/spec/v2/core/packs.md
CHANGED
|
@@ -1,39 +1,56 @@
|
|
|
1
1
|
# Packs
|
|
2
2
|
|
|
3
|
-
> **Status: Stable
|
|
3
|
+
> **Status: Stable.**
|
|
4
4
|
> **Normative home:** `packs`.
|
|
5
5
|
|
|
6
6
|
## Why this exists
|
|
7
7
|
|
|
8
|
-
The v2 contract for pack manifests, the registry tree, peer-dependency identifiers, and signing. The per-kind rules live in connection-packs.md, form-content-packs.md, and workflow-chain-packs.md; the capability vocabulary a pack requires is capabilities.md.
|
|
8
|
+
The v2 contract for pack manifests, the registry tree, peer-dependency identifiers, and signing. The per-kind rules live in [connection-packs.md](connection-packs.md), [form-content-packs.md](form-content-packs.md), and [workflow-chain-packs.md](workflow-chain-packs.md); the capability vocabulary a pack requires is [capabilities.md](capabilities.md).
|
|
9
9
|
|
|
10
10
|
## The engine range
|
|
11
11
|
|
|
12
|
-
A manifest's `engines.openwop` MUST match the grammar in `schemas/v2/node-pack-manifest.schema.json`: a `>=` lower bound and an explicit `<` major ceiling (`^>=\d+(\.\d+){0,2} <\d+\.0\.0$`).
|
|
12
|
+
A manifest's `engines.openwop` MUST match the grammar in `schemas/v2/node-pack-manifest.schema.json`: a `>=` lower bound and an explicit `<` major ceiling (`^>=\d+(\.\d+){0,2} <\d+\.0\.0$`).
|
|
13
|
+
|
|
14
|
+
- A v2 host MUST treat a range with no upper bound as bounded by `<2.0.0`.
|
|
15
|
+
- A host MUST refuse to install a version whose range does not admit the host's protocol major with `pack_engine_unsupported` (`spec/v2/errors.json`).
|
|
16
|
+
- `pack_runtime_requirement_unmet` remains a runtime-requirement code and MUST NOT be used for the protocol major.
|
|
17
|
+
- Both checks MUST run at install on every publication path — the canonical registry, a vendor registry's write API, and a mirror ingest — so no registry-side artifact can bypass it.
|
|
18
|
+
|
|
19
|
+
The range is a claim about the pack's own surface, not about run semantics. Admitting major M asserts that:
|
|
20
|
+
|
|
21
|
+
- the manifest validates against the `schemas/v2/` manifest schema for its `kind`;
|
|
22
|
+
- every `peerDependencies` key resolves (§"Peer-dependency identifiers");
|
|
23
|
+
- the version carries a §Signing signature.
|
|
24
|
+
|
|
25
|
+
Each conjunct keeps its own refusal code. A mechanical ceiling bump is not a verification.
|
|
13
26
|
|
|
14
27
|
## The `packs` capability
|
|
15
28
|
|
|
16
|
-
A host advertises `packs` when it serves the registry surface above. The record
|
|
17
|
-
is the advertisement: a host MUST NOT advertise `packs` unless it resolves pack
|
|
18
|
-
references through a registry reachable from its discovery document, and a
|
|
19
|
-
client MUST treat an absent record as "this host installs no packs" rather than
|
|
20
|
-
as an unknown.
|
|
29
|
+
A host advertises `packs` when it serves the registry surface above. The record is the advertisement:
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
profile instead, and MUST NOT treat `testMode` as a second way to claim one.
|
|
31
|
+
- A host MUST NOT advertise `packs` unless it resolves pack references through a registry reachable from its discovery document.
|
|
32
|
+
- A client MUST treat an absent record as "this host installs no packs", not as an unknown.
|
|
33
|
+
|
|
34
|
+
`testMode` is DEPRECATED and MUST NOT be relied on by a client (see §"During the v1 overlap"). A host mounting a test catalog SHOULD advertise the seams profile instead, and MUST NOT treat `testMode` as a second way to claim one.
|
|
27
35
|
|
|
28
36
|
## The registry tree
|
|
29
37
|
|
|
30
|
-
The registry is versioned by tree, not header. It publishes `registry/v2/packs/<name>/-/<version>.{json,sbom.json,sig,tgz}` as a parallel tree of re-signed manifests with regenerated SBOMs and index
|
|
38
|
+
The registry is versioned by tree, not header. It publishes `registry/v2/packs/<name>/-/<version>.{json,sbom.json,sig,tgz}` as a parallel tree of re-signed manifests with regenerated SBOMs and index.
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
- A signed compatibility overlay MUST be rejected: signatures authorize by namespace, and a mirror re-derives the signer at ingest.
|
|
41
|
+
- `.well-known/openwop-registry.json` `endpoints` is the negotiation: it names both trees, and a client MUST resolve every registry path through it rather than construct one.
|
|
42
|
+
- `publicKey` is unversioned: keys are not protocol-versioned.
|
|
33
43
|
|
|
34
44
|
## Peer-dependency identifiers
|
|
35
45
|
|
|
36
|
-
A `peerDependencies` key MUST
|
|
46
|
+
A `peerDependencies` key MUST either:
|
|
47
|
+
|
|
48
|
+
- be a `families[].key` in `spec/v2/declaration.json` whose `anchor` is not `deleted` — equivalently a root key of the generated `schemas/v2/capabilities.schema.json`; or
|
|
49
|
+
- carry a row in `spec/v2/peer-dependency-aliases.json` (§"The alias table").
|
|
50
|
+
|
|
51
|
+
The declaration key, the peer-dependency identifier, and the capabilities.md section anchor are one identifier. A host MUST refuse a key the declaration file does not name with `pack_peer_dependency_undefined`.
|
|
52
|
+
|
|
53
|
+
Facet paths are not identifiers: a pack requires a family by its key and names facets in `peerDependenciesMeta.<family>.facets[]`.
|
|
37
54
|
|
|
38
55
|
```jsonc
|
|
39
56
|
"peerDependencies": { "aiProviders": "required" },
|
|
@@ -42,11 +59,13 @@ A `peerDependencies` key MUST be a `families[].key` in `spec/v2/declaration.json
|
|
|
42
59
|
|
|
43
60
|
## The alias table
|
|
44
61
|
|
|
45
|
-
`spec/v2/peer-dependency-aliases.json` is generated from the declaration file and the published-manifest inventory, never hand-kept
|
|
62
|
+
`spec/v2/peer-dependency-aliases.json` is generated from the declaration file and the published-manifest inventory, never hand-kept. It is how a v1-era key reaches a v2 family through the overlap. Each row is `{ alias, family, facets?, publishedUses, removalTrigger }` and covers a v1 grammar found in the wild (`host.*` dotted twins, `openwop.agents.memoryBackends`, facet paths such as `aiProviders.imageGeneration`).
|
|
63
|
+
|
|
64
|
+
A v2 host MAY resolve an alias through the table during the overlap, and MUST NOT resolve one after v1 end-of-support (`removalTrigger: v1-end-of-support`).
|
|
46
65
|
|
|
47
66
|
## The manifest schema family
|
|
48
67
|
|
|
49
|
-
The
|
|
68
|
+
The manifest schemas carry `$id` under `https://openwop.dev/spec/v2/`; the v1 `$id`s are immutable and served read-only.
|
|
50
69
|
|
|
51
70
|
| Schema (`schemas/v2/…`) | Author | Vendor hatch |
|
|
52
71
|
| --- | --- | --- |
|
|
@@ -56,25 +75,24 @@ The 13 manifest schemas carry `$id` under `https://openwop.dev/spec/v2/`; the v1
|
|
|
56
75
|
| `security-advisory` | registry | closed |
|
|
57
76
|
| `prompt-ref` | leaf | none |
|
|
58
77
|
|
|
59
|
-
Every pack-authored document MUST admit `patternProperties` `^(openwop-|x-|vendor\.)`. A consumer that does not recognize a hatch property MUST ignore it and MUST NOT reject the document; the value is pack-authored and therefore untrusted (security-defaults.md).
|
|
78
|
+
Every pack-authored document MUST admit `patternProperties` `^(openwop-|x-|vendor\.)`. A consumer that does not recognize a hatch property MUST ignore it and MUST NOT reject the document; the value is pack-authored and therefore untrusted ([security-defaults.md](security-defaults.md)).
|
|
60
79
|
|
|
61
80
|
## Signing
|
|
62
81
|
|
|
63
|
-
There is one signing scheme. `signing` on a version manifest (`schemas/v2/registry-version-manifest.schema.json`) is `{ keyId, scheme }`, both REQUIRED:
|
|
82
|
+
There is one signing scheme. `signing` on a version manifest (`schemas/v2/registry-version-manifest.schema.json`) is the closed object `{ keyId, scheme }`, both REQUIRED:
|
|
64
83
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
| `keyId` | the signing key id; `publicKeyRef` does not exist |
|
|
69
|
-
| `method` | does not exist; a manifest carrying it fails validation |
|
|
84
|
+
- **`scheme`** MUST be `ed25519-canonical-json`: a detached 64-byte Ed25519 signature over the RFC 8785 (JCS) bytes of `pack.json` inside a deterministic tarball. The input MUST satisfy `conformance.md` §"Canonical JSON".
|
|
85
|
+
- **`keyId`** is the signing key id.
|
|
86
|
+
- A block carrying `method`, `publicKeyRef` or `signatureRef` fails validation.
|
|
70
87
|
|
|
71
|
-
The same block applies to a **bare manifest** — the `pack.json` inside the tarball, which is the document the signature covers. `signing` is OPTIONAL there, because an authoring-time `pack.json` exists before it is signed
|
|
88
|
+
The same block applies to a **bare manifest** — the `pack.json` inside the tarball, which is the document the signature covers. `signing` is OPTIONAL there, because an authoring-time `pack.json` exists before it is signed. When present it MUST be the identical closed `{ keyId, scheme }` object, on every bare manifest kind.
|
|
72
89
|
|
|
73
|
-
A verifier MUST verify the signature against the issuing registry's key for `keyId
|
|
90
|
+
- A verifier MUST verify the signature against the issuing registry's key for `keyId`, and MUST check the pack name against that key's `permittedNamespaces`.
|
|
91
|
+
- A signature over tarball bytes is not a v2 signature; such a pack MUST be re-signed, not relabeled.
|
|
74
92
|
|
|
75
93
|
## Version manifests
|
|
76
94
|
|
|
77
|
-
`kind` is REQUIRED on every version manifest and every bare manifest
|
|
95
|
+
`kind` is REQUIRED on every version manifest and every bare manifest. A deprecated version is flagged `versionDeprecated: true`; the registry continues to serve it, and a consumer MAY refuse to install it.
|
|
78
96
|
|
|
79
97
|
## The registry's own schemas
|
|
80
98
|
|
|
@@ -87,3 +105,10 @@ A registry MUST validate submissions against vendored copies of these schemas pi
|
|
|
87
105
|
| `pack_engine_unsupported` | the range does not admit the host's protocol major (install, every path) |
|
|
88
106
|
| `pack_peer_dependency_undefined` | a peer-dependency key is not a declaration-file key or an overlap alias |
|
|
89
107
|
| `pack_signature_invalid` | the signature, key, or namespace check fails |
|
|
108
|
+
|
|
109
|
+
## During the v1 overlap
|
|
110
|
+
|
|
111
|
+
- The v1 registry tree is frozen through the overlap, behind the v2 tree.
|
|
112
|
+
- `testMode` advertises the v1 `/v1/packs-test/*` mirror, a conformance seam ([conformance.md §"The seams profile"](conformance.md)). It remains advertisable through the overlap and is removed at 3.0.
|
|
113
|
+
|
|
114
|
+
*Sources: RFC 0177, RFC 0212.*
|