@openwop/openwop 1.9.0 → 2.0.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 (60) hide show
  1. package/README.md +64 -117
  2. package/dist/client.d.ts +131 -245
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +233 -440
  5. package/dist/client.js.map +1 -1
  6. package/dist/cost-attribution.d.ts +2 -2
  7. package/dist/cost-attribution.js +2 -2
  8. package/dist/envelope-directive.d.ts +1 -1
  9. package/dist/envelope-directive.js +1 -1
  10. package/dist/event-helpers.js +1 -1
  11. package/dist/event-helpers.js.map +1 -1
  12. package/dist/generated.d.ts +17 -0
  13. package/dist/generated.d.ts.map +1 -0
  14. package/dist/generated.js +311 -0
  15. package/dist/generated.js.map +1 -0
  16. package/dist/index.d.ts +16 -18
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +26 -56
  19. package/dist/index.js.map +1 -1
  20. package/dist/run-helpers.d.ts +20 -21
  21. package/dist/run-helpers.d.ts.map +1 -1
  22. package/dist/run-helpers.js +23 -72
  23. package/dist/run-helpers.js.map +1 -1
  24. package/dist/sse.d.ts +33 -15
  25. package/dist/sse.d.ts.map +1 -1
  26. package/dist/sse.js +28 -30
  27. package/dist/sse.js.map +1 -1
  28. package/dist/types.d.ts +253 -559
  29. package/dist/types.d.ts.map +1 -1
  30. package/dist/types.js.map +1 -1
  31. package/dist/webhook-header-families.d.ts +22 -13
  32. package/dist/webhook-header-families.d.ts.map +1 -1
  33. package/dist/webhook-header-families.js +30 -27
  34. package/dist/webhook-header-families.js.map +1 -1
  35. package/dist/webhook-helpers.browser.d.ts +15 -29
  36. package/dist/webhook-helpers.browser.d.ts.map +1 -1
  37. package/dist/webhook-helpers.browser.js +16 -31
  38. package/dist/webhook-helpers.browser.js.map +1 -1
  39. package/dist/webhook-helpers.d.ts +41 -41
  40. package/dist/webhook-helpers.d.ts.map +1 -1
  41. package/dist/webhook-helpers.js +40 -47
  42. package/dist/webhook-helpers.js.map +1 -1
  43. package/package.json +6 -4
  44. package/src/client.ts +255 -454
  45. package/src/cost-attribution.ts +2 -2
  46. package/src/envelope-directive.ts +1 -1
  47. package/src/event-helpers.ts +1 -1
  48. package/src/generated.ts +322 -0
  49. package/src/index.ts +78 -110
  50. package/src/run-helpers.ts +27 -85
  51. package/src/sse.ts +63 -42
  52. package/src/types.ts +268 -603
  53. package/src/webhook-header-families.ts +41 -29
  54. package/src/webhook-helpers.browser.ts +23 -32
  55. package/src/webhook-helpers.ts +67 -53
  56. package/dist/registry-helpers.d.ts +0 -118
  57. package/dist/registry-helpers.d.ts.map +0 -1
  58. package/dist/registry-helpers.js +0 -82
  59. package/dist/registry-helpers.js.map +0 -1
  60. package/src/registry-helpers.ts +0 -173
package/README.md CHANGED
@@ -1,155 +1,102 @@
1
- # `@openwop/openwop` — TypeScript SDK for the Multi-Agent Workflow Orchestration Protocol
1
+ # `@openwop/openwop` 2.x — TypeScript SDK for OpenWOP v2 hosts
2
2
 
3
- **openwop is an open, wire-level protocol for multi-agent workflow orchestration** a single contract for runs in which LLM agents, deterministic tools, sub-workflows, and human reviewers collaborate, with durable suspend / resume, replay, version negotiation, and observability owned by the protocol itself. This package is the reference TypeScript client: typed methods for every spec'd REST endpoint plus an async-iterable SSE consumer, zero runtime deps.
3
+ **openwop is an open, wire-level protocol for multi-agent workflow orchestration.** This package is the reference TypeScript client for the **v2 major** (`spec/v2/`, RFC 0168 §D): one typed method per operation in `spec/v2/path-manifest.json` (51 operations), an async-iterable SSE consumer for the run and host event channels, and zero runtime dependencies.
4
4
 
5
5
  ```bash
6
- npm install @openwop/openwop
6
+ npm install @openwop/openwop@2 # 2.0.0 — v2-only; the 1.x client stays on `@openwop/openwop@1`
7
7
  ```
8
8
 
9
- > **Spec:** [github.com/openwop/openwop](https://github.com/openwop/openwop) · **Status:** FINAL v1 (2026-04-27) · **Mirrors:** [`api/openapi.yaml`](https://github.com/openwop/openwop/blob/main/api/openapi.yaml)
9
+ > **Spec:** [github.com/openwop/openwop](https://github.com/openwop/openwop) · **Corpus tag:** see [`CORPUS_TAG`](../../CORPUS_TAG) · **Mirrors:** [`api/v2/openapi.yaml`](../../api/v2/openapi.yaml), [`api/v2/asyncapi.yaml`](../../api/v2/asyncapi.yaml), [`schemas/v2/`](../../schemas/v2/), [`spec/v2/errors.json`](../../spec/v2/errors.json)
10
+ >
11
+ > The 1.x package (`sdk/typescript/`) is untouched and keeps publishing for v1 hosts. This is a v2-ONLY client: it never sends a `/v1/…` path.
10
12
 
11
- The SDK is hand-authored rather than codegen'd from OpenAPI for two reasons:
13
+ ## What is different from 1.x (RFC 0172 / 0171 / 0173)
12
14
 
13
- 1. **Idiomatic shape.** OpenAPI codegen produces verbose accessors (`api.runs.runs_create()`, etc.) that are nicer if hand-curated. A v1 reference SDK should set the API style other ecosystems (Python, Go) follow.
14
- 2. **Stays close to the spec.** Each method maps 1:1 to a documented endpoint, and types come from the spec's JSON Schemas (referenced via the OpenAPI doc), not from a generator's intermediate representation.
15
-
16
- ---
15
+ | 1.x | 2.x |
16
+ | --- | --- |
17
+ | `/v1/runs`, `/v1/agents`, … | Bare origin, unversioned path keys: `/runs`, `/agents`, … There is no `/v2/` path space. |
18
+ | Negotiation by `protocolVersion` | `OpenWOP-Version: <major>.0` on **every** request (ctor option `major`, default `2`); the host answers `406 protocol_version_unsupported` with `details.protocolVersions[]` when it does not list the major. |
19
+ | `X-Dedup` | `OpenWOP-Dedup` (`MutationOptions.dedup`). Every non-standard header is `OpenWOP-<Name>` (headers.md). |
20
+ | `pollEvents({ lastSequence })` | `pollEvents({ afterSequence })`; the response is the closed `{ runId, events, lastSequence, status, isTerminal }`. |
21
+ | Open discovery root, `supported: boolean` | The closed v2 root: `protocolVersions[]` + `preferredVersion` REQUIRED, every family a `CapabilityRecord` `{ status, since, until?, witness, …facets }` (presence is the claim). Family and metadata keys are generated from `schemas/v2/capabilities.schema.json`. |
22
+ | `ErrorEnvelope.error: string` | `ErrorCode \| VendorErrorCode` — the 94-member union is generated from `spec/v2/errors.json` (`ERROR_CODES`, `ERROR_CODE_HTTP_STATUS`, `RETRIABLE_ERROR_CODES`). |
23
+ | `workspace.*` (4), `runs.debugBundle`, `userAgents.*` (host-sample seams), `RegistryClient` | Removed — not v2 operations. The pack registry is a separate wire surface a client resolves through `.well-known/openwop-registry.json` `endpoints` (packs.md). |
24
+ | — | `runs.compensation`, `runs.effects`, `host.effectSeams` (RFC 0173), `host.events` (the `hostEvents` SSE channel). |
25
+ | Webhook `openwop-Webhook-*` legacy names, `v1=<hex>` | `OpenWOP-*` only (`X-openwop-*` accepted through the overlap); `sha256=<hex>`; an unrecognized `OpenWOP-Signature-Algorithm` is rejected. Import from `@openwop/openwop/webhooks` — the barrel no longer carries `node:crypto`. |
17
26
 
18
27
  ## Quickstart
19
28
 
20
29
  ```typescript
21
- import { OpenwopClient } from '@openwop/openwop';
30
+ import { OpenwopClient, WopError, isTerminalRunStatus } from '@openwop/openwop';
22
31
 
23
32
  const client = new OpenwopClient({
24
33
  baseUrl: 'https://api.example.com',
25
34
  apiKey: 'hk_test_abc123',
35
+ // major: 2 — the default; every request carries `OpenWOP-Version: 2.0`.
26
36
  });
27
37
 
28
- // Discovery
38
+ // Discovery — the closed v2 root; `webhooks` is a record or absent, never `supported: false`.
29
39
  const caps = await client.discovery.capabilities();
30
- console.log(caps.protocolVersion, caps.limits);
31
-
32
- // Workflows
33
- const wf = await client.workflows.get('my-workflow-id');
40
+ console.log(caps.preferredVersion, caps.protocolVersions, caps.webhooks?.status);
34
41
 
35
- // Run lifecycle
36
- const { runId } = await client.runs.create({
37
- workflowId: 'my-workflow-id',
38
- inputs: { foo: 'bar' },
39
- });
42
+ // Runs
43
+ const { runId } = await client.runs.create(
44
+ { workflowId: 'my-workflow', inputs: { q: 'hello' }, configurable: { version: 1, run: { runTimeoutMs: 60_000 } } },
45
+ { idempotencyKey: crypto.randomUUID(), dedup: 'enforce' },
46
+ );
40
47
 
41
- // Poll (or use SSE see below)
42
- let snap = await client.runs.get(runId);
43
- while (snap.status !== 'completed' && snap.status !== 'failed') {
44
- await new Promise((r) => setTimeout(r, 500));
45
- snap = await client.runs.get(runId);
48
+ for await (const event of client.runs.events(runId, { streamMode: ['updates', 'messages'] })) {
49
+ console.log(event.sequence, event.type);
46
50
  }
47
51
 
48
- // Cancel mid-flight
49
- await client.runs.cancel(runId, { reason: 'user request' });
50
-
51
- // HITL approval (run-scoped)
52
- await client.interrupts.resolveByRun(runId, 'gate', { resumeValue: { action: 'accept' } });
53
-
54
- // Replay / fork
55
- const fork = await client.runs.fork(runId, { fromSeq: 5, mode: 'branch' });
56
-
57
- // SSE stream
58
- for await (const event of client.runs.events(runId, { streamMode: 'updates' })) {
59
- console.log(event.type, event.payload);
52
+ // Long-poll fallback: feed `lastSequence` back as `afterSequence`.
53
+ let cursor: number | undefined;
54
+ for (;;) {
55
+ const page = await client.runs.pollEvents(runId, cursor === undefined ? {} : { afterSequence: cursor });
56
+ cursor = page.lastSequence;
57
+ if (page.isTerminal) break;
60
58
  }
61
- ```
62
59
 
63
- ---
60
+ // RFC 0173 read projections
61
+ const compensation = await client.runs.compensation(runId); // null when `compensation` is unadvertised
62
+ const effects = await client.runs.effects(runId);
64
63
 
65
- ## Quickstart (Node)
64
+ // Errors route on the registered code, never on `message`.
65
+ try {
66
+ await client.runs.get('tenant/does-not-exist');
67
+ } catch (err) {
68
+ if (err instanceof WopError && err.envelope?.error === 'not_found') { /* … */ }
69
+ }
66
70
 
67
- ```bash
68
- cd sdk/typescript
69
- npm install # installs @openwop/openwop deps locally (NOT in parent monorepo)
70
- npx tsc --noEmit # typecheck the SDK source
71
+ void isTerminalRunStatus;
71
72
  ```
72
73
 
73
- ---
74
-
75
- ## What's Covered In v1.0
76
-
77
- | Endpoint | SDK method |
78
- |---|---|
79
- | `GET /.well-known/openwop` | `client.discovery.capabilities()` |
80
- | `GET /v1/openapi.json` | `client.discovery.openapi()` |
81
- | `GET /v1/workflows/{id}` | `client.workflows.get(id)` |
82
- | `POST /v1/runs` | `client.runs.create(body, opts?)` |
83
- | `GET /v1/runs/{id}` | `client.runs.get(id)` |
84
- | `GET /v1/runs/{id}/events` (SSE) | `client.runs.events(id, opts?)` (async iterable) |
85
- | `GET /v1/runs/{id}/events/poll` | `client.runs.pollEvents(id, opts?)` |
86
- | `POST /v1/runs/{id}/cancel` | `client.runs.cancel(id, body?)` |
87
- | `POST /v1/runs:bulk-cancel` | `client.runs.bulkCancel(body, opts?)` |
88
- | `POST /v1/runs/{id}:pause` | `client.runs.pause(id, body?, opts?)` |
89
- | `POST /v1/runs/{id}:resume` | `client.runs.resume(id, body?, opts?)` |
90
- | `POST /v1/runs/{id}:fork` | `client.runs.fork(id, body)` |
91
- | `POST /v1/runs/{id}/interrupts/{nodeId}` | `client.interrupts.resolveByRun(id, nodeId, body)` |
92
- | `GET /v1/interrupts/{token}` | `client.interrupts.inspectByToken(token)` |
93
- | `POST /v1/interrupts/{token}` | `client.interrupts.resolveByToken(token, body)` |
94
- | `GET /v1/audit/verify` | `client.audit.verify(fromSeq, toSeq)` |
95
-
96
- **Idempotency-Key** is supported via the `idempotencyKey` option on every mutation method.
97
-
98
- **Typed `RunConfigurable`** — `client.runs.create(...).configurable` is now a typed surface with reserved keys (`recursionLimit`, `model`, `temperature`, `maxTokens`, `promptOverrides`) plus pass-through for impl extensions.
99
-
100
- ## Error Handling
74
+ Webhook receivers (server-only):
101
75
 
102
76
  ```typescript
103
- import { HTTP_ERROR_CODES, isHttpErrorCode, WopError } from '@openwop/openwop';
104
-
105
- try {
106
- await client.runs.create({ workflowId: 'my-workflow-id' });
107
- } catch (err) {
108
- if (err instanceof WopError && isHttpErrorCode(err.envelope?.error)) {
109
- console.error(err.envelope.error, err.envelope.details);
110
- }
111
- }
77
+ import { readWebhookHeaders, verifyWebhookSignature } from '@openwop/openwop/webhooks';
78
+
79
+ const read = readWebhookHeaders(req.headers);
80
+ const outcome = read
81
+ ? verifyWebhookSignature(secret, read.signatureHeader, read.timestampHeader, rawBody, {
82
+ ...(read.algorithmHeader === undefined ? {} : { algorithmHeader: read.algorithmHeader }),
83
+ })
84
+ : { valid: false as const, reason: 'malformed_signature_header' as const };
112
85
  ```
113
86
 
114
- `HTTP_ERROR_CODES` is the canonical REST/MCP error-envelope vocabulary (`unauthenticated`, `validation_error`, `run_already_active`, etc.). Contextual fields live under `ErrorEnvelope.details`; for example retry hints are `details.retryAfter`, not a top-level response field. `RUN_ERROR_CODES` is separate and applies to `RunSnapshot.error.code` after a run itself fails.
115
-
116
- ---
87
+ ## Generated surface
117
88
 
118
- ## Not In The v1.0 SDK
89
+ `src/generated.ts` is produced by `scripts/generate.mjs` from the vendored `spec/v2/errors.json` and `schemas/v2/capabilities.schema.json`. `npm run generate` rewrites it; `npm run generate:check` (run by `scripts/sdks-check.sh`) fails when it drifts from the corpus at `CORPUS_TAG`.
119
90
 
120
- | Feature | Why |
121
- |---|---|
122
- | Webhook subscription helpers | v1 specifies webhook delivery, but the SDK keeps endpoint coverage focused on the run lifecycle and conformance-critical surfaces. |
123
- | Hosted registry publishing helpers | Node-pack registry publishing needs operator-specific credentials and policy; use direct HTTP until a dedicated package workflow is warranted. |
124
- | Auto-retry with exponential backoff | Retry policy is application-specific. The SDK exposes structured errors so callers can implement their own retry envelope. |
125
- | Separate browser entrypoint (`@openwop/openwop/browser`) | The default ESM build uses standard `fetch`/`ReadableStream` primitives and can be bundled by modern tools; a dedicated browser subpath can ship later without changing the v1 surface. |
91
+ ## Method operation map
126
92
 
127
- ---
93
+ Every one of the 51 `spec/v2/path-manifest.json` operations has exactly one method; `scripts/check-sdk-parity.mjs --manifest spec/v2/path-manifest.json --expectations sdk/parity-expectations-v2.json` enforces it. See [`sdk/PARITY.md`](../PARITY.md) §v2.
128
94
 
129
- ## Layout
95
+ ## Development
130
96
 
97
+ ```bash
98
+ npm install
99
+ npm run typecheck # strict + exactOptionalPropertyTypes
100
+ npm test
101
+ npm run generate:check
131
102
  ```
132
- sdk/typescript/
133
- README.md — this file
134
- package.json — @openwop/openwop package manifest
135
- tsconfig.json — strict TS, ESM
136
- src/
137
- index.ts — public surface (OpenwopClient + types)
138
- client.ts — OpenwopClient class (auth, request helper)
139
- types.ts — request/response types mirrored from the OpenAPI spec
140
- sse.ts — async-iterable SSE consumer
141
- ```
142
-
143
- ---
144
-
145
- ## Versioning
146
-
147
- This SDK tracks OpenWOP v1. SDK majors match the protocol major; v1.x SDK releases remain forward-compatible with the v1 wire contract. Mismatch behavior is forward-compat tolerant — see `../../version-negotiation.md`. Breaking spec changes will increment the SDK major.
148
-
149
- ## References
150
-
151
- - Spec corpus: `../../README.md`
152
- - OpenAPI: `../../api/openapi.yaml` (the SDK mirrors this surface)
153
- - AsyncAPI: `../../api/asyncapi.yaml` (the SSE consumer follows these channels)
154
- - **[`../PARITY.md`](../PARITY.md)** — cross-SDK feature-parity matrix (TS/Python/Go).
155
- - **[`../smoke/`](../smoke/)** — runnable wire-smoke scripts against a reference host.