macp-sdk-typescript 0.5.0 → 0.7.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,63 @@ All notable changes to `macp-sdk-typescript` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
5
  project uses [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.7.0](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/compare/v0.6.0...v0.7.0) (2026-08-30)
8
+
9
+
10
+ ### ⚠ BREAKING CHANGES
11
+
12
+ * RFC-MACP-0013 canonical commitment hash ([#45](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/issues/45))
13
+
14
+ ### Features
15
+
16
+ * RFC-MACP-0013 canonical commitment hash ([#45](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/issues/45)) ([2af3c51](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/commit/2af3c5156e5831a4d2767175fd8f3154e34ec1af))
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * reject non-object supersedes instead of silently dropping it ([#49](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/issues/49)) ([b4a68e3](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/commit/b4a68e3f6059d968dabc3f6b43c5e8033fb4957c))
22
+
23
+ ## [0.6.0](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/compare/v0.5.0...v0.6.0) (2026-07-10)
24
+
25
+
26
+ ### Features
27
+
28
+ * export package VERSION constant ([#37](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/issues/37)) ([4ae5cda](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/commit/4ae5cda23255b82339490f740a30ca0b7fee0d5a))
29
+
30
+ ## [Unreleased]
31
+
32
+ ### Changed
33
+
34
+ - **BREAKING: requires Node.js >= 20** (`engines.node`). Node 18 reached
35
+ end-of-life in April 2025 and was never exercised in CI; the CI matrix now
36
+ tests Node 20, 22, and 24.
37
+ - CI installs with `npm ci` against the committed lockfile (regenerated so all
38
+ platform-specific optional binaries are recorded), with npm caching and a
39
+ concurrency group that cancels superseded PR runs. The advisory-only
40
+ `npm audit` job is replaced by Dependabot (weekly grouped npm and
41
+ github-actions updates). All workflow actions are pinned by commit SHA.
42
+ - Publishing now runs the full `prepublishOnly` gate (check + lint +
43
+ format:check + test + build) against the exact tree being published —
44
+ the `--ignore-scripts` workaround is gone.
45
+ - Pull requests get a coverage summary comment from CI.
46
+
47
+ ### Added
48
+
49
+ - Extensive unit coverage for previously integration-only surface: the
50
+ `MacpStream` data path (envelope unwrapping, inline errors, timeouts,
51
+ end-of-stream semantics), the entire `MacpClient` unary RPC surface and its
52
+ metadata/deadline dispatch, the `watch*` stream factories, session
53
+ `cancel`/`suspend`/`resume` delegation for all five modes, `BaseSession`/
54
+ `BaseProjection` extension-point behavior, and the `Participant` run loop
55
+ (terminal dispatch, cancel-callback wiring, initiator kickoff defaults).
56
+ Coverage thresholds raised to lines 94 / branches 88 / functions 90 /
57
+ statements 94.
58
+ - Conformance: `ext.multi_round.v1` fixtures now replay with real assertions
59
+ (previously silently skipped); fixtures for unmapped modes fail loudly; and
60
+ reject-path fixtures are contract-checked (canonical `expected_error_code`,
61
+ resolvable `payload_type`) with explicit skip markers pointing at the
62
+ runtime conformance oracle for the NACK behavior itself.
63
+
7
64
  ## [0.5.0] - 2026-07-06
8
65
 
9
66
  Absorbs `macp-runtime` 0.5.0 and `@multiagentcoordinationprotocol/proto` 0.1.6
package/README.md CHANGED
@@ -2,8 +2,14 @@
2
2
 
3
3
  TypeScript SDK for the [Multi-Agent Coordination Protocol (MACP)](https://github.com/multiagentcoordinationprotocol/multiagentcoordinationprotocol) runtime. Connects TypeScript/Node.js applications to the Rust MACP runtime over gRPC.
4
4
 
5
+ SDK documentation lives in [`docs/`](docs/index.md) and covers the TypeScript API.
6
+ Protocol semantics are normative in the [spec repo's docs and RFCs](https://github.com/multiagentcoordinationprotocol/multiagentcoordinationprotocol/tree/main/rfcs);
7
+ runtime behavior is documented in the [runtime repo's docs](https://github.com/multiagentcoordinationprotocol/macp-runtime/blob/main/docs/getting-started.md).
8
+
5
9
  ## Install
6
10
 
11
+ Requires Node.js >= 20.
12
+
7
13
  ```bash
8
14
  npm install macp-sdk-typescript
9
15
  ```
@@ -287,7 +293,7 @@ reference: [`docs/api/strategies.md`](docs/api/strategies.md).
287
293
  ### Cancel Callback
288
294
 
289
295
  Long-running agents can expose an HTTP endpoint that an orchestrator POSTs to
290
- in order to request a clean shutdown (RFC-MACP-0001 §7.2 Option A). Bootstrap
296
+ in order to request a clean shutdown ([RFC-MACP-0001 (Core)](https://github.com/multiagentcoordinationprotocol/multiagentcoordinationprotocol/blob/main/rfcs/RFC-MACP-0001-core.md) §7.2 Option A). Bootstrap
291
297
  JSON with a `cancel_callback: { host, port, path }` block auto-wires the
292
298
  server via `fromBootstrap()`; for the manual path use
293
299
  `startCancelCallbackServer(...)` + `Participant.attachCancelCallbackServer(...)`.
@@ -323,7 +329,7 @@ await session.vote({
323
329
 
324
330
  ## TLS
325
331
 
326
- TLS is on by default (RFC-MACP-0006 §3). To connect to an insecure runtime
332
+ TLS is on by default ([RFC-MACP-0006 (Transport Bindings)](https://github.com/multiagentcoordinationprotocol/multiagentcoordinationprotocol/blob/main/rfcs/RFC-MACP-0006-transport-bindings.md) §3). To connect to an insecure runtime
327
333
  during local development, you must opt out explicitly:
328
334
 
329
335
  ```typescript
@@ -405,10 +411,14 @@ npm run lint # ESLint
405
411
  npm run format # Prettier
406
412
  npm test # Run unit + conformance tests
407
413
  npm run test:watch # Watch mode
408
- npm run test:coverage # With coverage
409
- npm run test:integration # Integration tests (requires Docker runtime)
414
+ npm run test:coverage # With coverage (CI enforces thresholds from vitest.config.ts)
415
+ npm run test:integration # Integration tests (requires Docker runtime; not run in CI)
410
416
  ```
411
417
 
418
+ CI (GitHub Actions) runs type-check, lint, format check, the coverage-gated
419
+ test suite, and the build on Node 20, 22, and 24 for every push and pull
420
+ request, and posts a coverage summary comment on PRs.
421
+
412
422
  ### Integration Tests
413
423
 
414
424
  Run the full SDK against a live MACP runtime:
@@ -0,0 +1,41 @@
1
+ import type { CommitmentPayload } from './types';
2
+ /**
3
+ * Projects a `CommitmentPayload` (this SDK's camelCase shape) to its RFC
4
+ * 8785 canonical JSON string (snake_case, D2.1) and serializes it directly
5
+ * — no intermediate object graph — in the fixed member order below.
6
+ *
7
+ * Member order is RFC 8785 §3.2.3: object members sorted lexicographically
8
+ * by UTF-16 code unit on the member name. Every member name here is ASCII
9
+ * and the field set is frozen (it mirrors `CommitmentPayload`, a stable
10
+ * wire type), so this order is hard-coded rather than sorted at runtime:
11
+ * action, authority_scope, commitment_id, configuration_version,
12
+ * mode_version, outcome_positive, policy_version, reason, then supersedes
13
+ * last (only when present). Do not "simplify" this to `.localeCompare()` —
14
+ * that is locale-sensitive and not equivalent to code-unit ordering; it is
15
+ * moot only because the names above are already in the right order.
16
+ *
17
+ * `supersedes` is omitted entirely when `payload.supersedes === undefined`
18
+ * (D2.3) — including when the payload itself is missing/malformed, `payload
19
+ * ?? {}` never manufactures a `supersedes` key. An explicit
20
+ * `supersedes: { sessionId: '', commitmentHash: '' }` is NOT collapsed into
21
+ * omission; it is present with two empty-string members, `commitment_hash`
22
+ * before `session_id` (also alphabetical).
23
+ *
24
+ * Exported separately from `commitmentHash` so a vector-runner test can
25
+ * localize a mismatch to projection/JCS (compare this string) vs. digest
26
+ * (compare the final `sha256:` hash).
27
+ */
28
+ export declare function canonicalizeCommitmentPayload(payload: CommitmentPayload): string;
29
+ /**
30
+ * Computes the RFC-MACP-0013 canonical commitment hash for `payload`:
31
+ * `sha256:` followed by 64 lowercase hex digits of
32
+ * `SHA-256(LABEL + ':' + JCS(project(payload)))`.
33
+ *
34
+ * Never throws (D3), for any input — well-formed or not. Every step that
35
+ * could plausibly throw on malformed input (unknown-typed field reads,
36
+ * string iteration, control-character formatting) is guarded in
37
+ * `canonicalizeCommitmentPayload` / `jcsString` / `jcsBoolean` instead of
38
+ * being caught here, so a genuine bug in this file shows up as a test
39
+ * failure rather than being silently swallowed by a catch-all.
40
+ */
41
+ export declare function commitmentHash(payload: CommitmentPayload): string;
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canonicalizeCommitmentPayload = canonicalizeCommitmentPayload;
4
+ exports.commitmentHash = commitmentHash;
5
+ /**
6
+ * RFC-MACP-0013 canonical commitment hashing.
7
+ *
8
+ * Computes a deterministic content hash for a `CommitmentPayload` by
9
+ * projecting it to a frozen snake_case shape, serializing that shape with a
10
+ * hand-written subset of RFC 8785 (JSON Canonicalization Scheme, "JCS"), and
11
+ * hashing the labeled preimage with SHA-256.
12
+ *
13
+ * Dependency decision (deliberate, not an oversight): this module does NOT
14
+ * depend on a JCS library. RFC 8785's hardest requirement — canonical
15
+ * ECMAScript float formatting for JSON numbers — is unreachable here: the
16
+ * `CommitmentPayload` projection below is a frozen 9-field shape containing
17
+ * only strings, one boolean, and one optional 2-string nested object. No
18
+ * JSON numbers, no JSON arrays. That leaves only object-member ordering and
19
+ * string escaping, both of which are small enough to hand-write and test
20
+ * directly rather than pull in this SDK's first non-gRPC/proto runtime
21
+ * dependency. `node:crypto` is a Node builtin, not a dependency.
22
+ */
23
+ const node_crypto_1 = require("node:crypto");
24
+ /**
25
+ * Domain-separation label mixed into the hash preimage (RFC-MACP-0013 §4).
26
+ * Changing this string changes every hash this module produces — that is a
27
+ * protocol MINOR version bump, not a routine edit.
28
+ */
29
+ const LABEL = 'macp-commitment-hash/1';
30
+ /**
31
+ * Escapes a single JSON string value per RFC 8785 §3.2.2.2:
32
+ * - short-form escapes for backspace/tab/newline/formfeed/carriage-return,
33
+ * `"`, and `\`
34
+ * - `\u00xx` (lowercase hex) for other C0 control characters (0x00-0x1F)
35
+ * - every other character, including non-ASCII and astral-plane
36
+ * codepoints, is emitted literally (NOT escaped).
37
+ *
38
+ * Lone surrogates: a bare UTF-16 surrogate code unit not paired into a valid
39
+ * codepoint is out-of-contract input for this function — a `CommitmentPayload`
40
+ * should never legitimately contain one, since this SDK's strings originate
41
+ * from valid UTF-16 sources (JS string literals, JSON-decoded wire payloads,
42
+ * etc.). If one appears anyway, this function does not detect or escape it
43
+ * specially: `for (const ch of str)` below yields it as its own one-code-unit
44
+ * step and it is emitted literally, unescaped, like any other character. The
45
+ * actual loss happens one step later, in `commitmentHash`, when the finished
46
+ * JCS string is encoded to bytes via `Buffer.from(preimage, 'utf8')` — Node's
47
+ * standard lossy UTF-8 encoder silently substitutes a lone surrogate with
48
+ * U+FFFD (the Unicode replacement character) before hashing. The observable
49
+ * consequence: a field containing a lone surrogate (e.g. `'\uD800'`) hashes
50
+ * IDENTICALLY to the same field containing a literal U+FFFD in that position
51
+ * — pinned by the "lone surrogate" test in commitment-hash.test.ts. This is
52
+ * an accepted consequence of D3 (never throw on malformed input), not a
53
+ * claimed point of compatibility (or divergence) with any other
54
+ * implementation of RFC-MACP-0013.
55
+ *
56
+ * `value` is typed `unknown`, not `string`, on purpose: this function is the
57
+ * single choke point that keeps `commitmentHash` from ever throwing (D3) —
58
+ * every field of a `CommitmentPayload` flows through here, and at runtime a
59
+ * decoded or hand-built payload is not guaranteed to actually match its
60
+ * static type. Non-string input is coerced via `String()` (never a template
61
+ * literal or `+`, both of which throw on a bare `Symbol`) rather than
62
+ * rejected — this file does not "fix" a malformed payload into a valid one,
63
+ * it only guarantees serialization cannot crash.
64
+ *
65
+ * Iterates with `for (const ch of str)`, which walks Unicode codepoints, not
66
+ * UTF-16 code units. Indexing by code unit would split an astral-plane
67
+ * codepoint's surrogate pair (e.g. U+1F702) and emit half of it — iterating
68
+ * by codepoint is what keeps a full character, including a surrogate pair,
69
+ * together as a single loop step so it can be appended whole.
70
+ */
71
+ function jcsString(value) {
72
+ const str = typeof value === 'string' ? value : String(value ?? '');
73
+ let out = '"';
74
+ for (const ch of str) {
75
+ switch (ch) {
76
+ case '"':
77
+ out += '\\"';
78
+ break;
79
+ case '\\':
80
+ out += '\\\\';
81
+ break;
82
+ case '\b':
83
+ out += '\\b';
84
+ break;
85
+ case '\t':
86
+ out += '\\t';
87
+ break;
88
+ case '\n':
89
+ out += '\\n';
90
+ break;
91
+ case '\f':
92
+ out += '\\f';
93
+ break;
94
+ case '\r':
95
+ out += '\\r';
96
+ break;
97
+ default: {
98
+ // Non-null assertion is safe: `for (const ch of str)` always yields
99
+ // a non-empty code-point substring, so `codePointAt(0)` can never
100
+ // actually be `undefined` here — a `?? 0` fallback would be dead
101
+ // code that misleadingly reads as if codepoints can be absent.
102
+ const code = ch.codePointAt(0);
103
+ if (code < 0x20) {
104
+ // Other C0 control character with no short form (e.g. U+0000).
105
+ out += '\\u' + code.toString(16).padStart(4, '0');
106
+ }
107
+ else {
108
+ // Printable ASCII, non-ASCII, and astral-plane codepoints alike:
109
+ // JCS emits these as literal UTF-8, never escaped.
110
+ out += ch;
111
+ }
112
+ }
113
+ }
114
+ }
115
+ return out + '"';
116
+ }
117
+ /**
118
+ * Emits `value` as a JCS boolean literal. `??` (not `||`) materializes an
119
+ * absent `outcomePositive` as `false` rather than omitting the key (D2.2) —
120
+ * `||` would also fold an explicit falsy-but-not-nullish value incorrectly
121
+ * for fields added in the future, so `??` documents the intent even though
122
+ * `boolean` only has two values today. A non-boolean value (possible only
123
+ * for a malformed/untyped payload at runtime) is coerced via a truthy check
124
+ * rather than thrown on, keeping D3 intact.
125
+ */
126
+ function jcsBoolean(value) {
127
+ return (value ?? false) ? 'true' : 'false';
128
+ }
129
+ /**
130
+ * Projects a `CommitmentPayload` (this SDK's camelCase shape) to its RFC
131
+ * 8785 canonical JSON string (snake_case, D2.1) and serializes it directly
132
+ * — no intermediate object graph — in the fixed member order below.
133
+ *
134
+ * Member order is RFC 8785 §3.2.3: object members sorted lexicographically
135
+ * by UTF-16 code unit on the member name. Every member name here is ASCII
136
+ * and the field set is frozen (it mirrors `CommitmentPayload`, a stable
137
+ * wire type), so this order is hard-coded rather than sorted at runtime:
138
+ * action, authority_scope, commitment_id, configuration_version,
139
+ * mode_version, outcome_positive, policy_version, reason, then supersedes
140
+ * last (only when present). Do not "simplify" this to `.localeCompare()` —
141
+ * that is locale-sensitive and not equivalent to code-unit ordering; it is
142
+ * moot only because the names above are already in the right order.
143
+ *
144
+ * `supersedes` is omitted entirely when `payload.supersedes === undefined`
145
+ * (D2.3) — including when the payload itself is missing/malformed, `payload
146
+ * ?? {}` never manufactures a `supersedes` key. An explicit
147
+ * `supersedes: { sessionId: '', commitmentHash: '' }` is NOT collapsed into
148
+ * omission; it is present with two empty-string members, `commitment_hash`
149
+ * before `session_id` (also alphabetical).
150
+ *
151
+ * Exported separately from `commitmentHash` so a vector-runner test can
152
+ * localize a mismatch to projection/JCS (compare this string) vs. digest
153
+ * (compare the final `sha256:` hash).
154
+ */
155
+ function canonicalizeCommitmentPayload(payload) {
156
+ // D3: a malformed/absent payload must not throw. `{}` lets every field
157
+ // read below fall through to `jcsString`'s/`jcsBoolean`'s own defensive
158
+ // coercion instead of dereferencing a null/undefined payload.
159
+ const p = payload ?? {};
160
+ const members = [
161
+ `"action":${jcsString(p.action)}`,
162
+ `"authority_scope":${jcsString(p.authorityScope)}`,
163
+ `"commitment_id":${jcsString(p.commitmentId)}`,
164
+ `"configuration_version":${jcsString(p.configurationVersion)}`,
165
+ `"mode_version":${jcsString(p.modeVersion)}`,
166
+ `"outcome_positive":${jcsBoolean(p.outcomePositive)}`,
167
+ `"policy_version":${jcsString(p.policyVersion ?? '')}`,
168
+ `"reason":${jcsString(p.reason)}`,
169
+ ];
170
+ if (p.supersedes !== undefined) {
171
+ // Optional chaining tolerates `supersedes` being present but not
172
+ // actually an object (null, a primitive, ...) on a malformed payload —
173
+ // it never throws, it just yields `undefined` for the sub-fields, which
174
+ // `jcsString` then coerces to `""` the same way it handles any other
175
+ // missing string field.
176
+ const sup = p.supersedes;
177
+ members.push(`"supersedes":{"commitment_hash":${jcsString(sup?.commitmentHash)},"session_id":${jcsString(sup?.sessionId)}}`);
178
+ }
179
+ return `{${members.join(',')}}`;
180
+ }
181
+ /**
182
+ * Computes the RFC-MACP-0013 canonical commitment hash for `payload`:
183
+ * `sha256:` followed by 64 lowercase hex digits of
184
+ * `SHA-256(LABEL + ':' + JCS(project(payload)))`.
185
+ *
186
+ * Never throws (D3), for any input — well-formed or not. Every step that
187
+ * could plausibly throw on malformed input (unknown-typed field reads,
188
+ * string iteration, control-character formatting) is guarded in
189
+ * `canonicalizeCommitmentPayload` / `jcsString` / `jcsBoolean` instead of
190
+ * being caught here, so a genuine bug in this file shows up as a test
191
+ * failure rather than being silently swallowed by a catch-all.
192
+ */
193
+ function commitmentHash(payload) {
194
+ const jcs = canonicalizeCommitmentPayload(payload);
195
+ const preimage = `${LABEL}:${jcs}`;
196
+ const digest = (0, node_crypto_1.createHash)('sha256').update(Buffer.from(preimage, 'utf8')).digest('hex');
197
+ return `sha256:${digest}`;
198
+ }
@@ -24,7 +24,10 @@ export declare function inferOutcomePositive(action: string): boolean;
24
24
  /**
25
25
  * Build a {@link CommitmentRef} pointing at a prior accepted commitment, for
26
26
  * use as `buildCommitmentPayload({ supersedes })` (cross-session supersession,
27
- * RFC-MACP-0001 §7.3). Parity with python-sdk `build_commitment_ref`.
27
+ * RFC-MACP-0001 §7.3). Use `commitmentHash()` (from `./commitment-hash`) to
28
+ * produce a valid `commitmentHash` value — this function validates the hash
29
+ * shape (`sha256:<64 lowercase hex>`) and throws `MacpSessionError` if it
30
+ * doesn't match. Parity with python-sdk `build_commitment_ref`.
28
31
  */
29
32
  export declare function buildCommitmentRef(input: {
30
33
  sessionId: string;
@@ -56,7 +59,11 @@ export declare function buildCommitmentPayload(input: {
56
59
  /**
57
60
  * Cross-session supersession (RFC-MACP-0001 §7.3): reference to the prior
58
61
  * commitment this one supersedes. Distinct from proposal-mode
59
- * `supersedesProposalId`. Absent by default. Backward-compatible.
62
+ * `supersedesProposalId`. Absent by default. Backward-compatible. A
63
+ * non-`undefined` value that isn't a `CommitmentRef` object (e.g. `null`,
64
+ * from a caller bypassing the type system) throws `MacpSessionError`
65
+ * rather than being silently dropped — see RFC-MACP-0013 reconcile,
66
+ * 2026-08-30.
60
67
  */
61
68
  supersedes?: CommitmentRef;
62
69
  }): CommitmentPayload;
package/dist/envelope.js CHANGED
@@ -16,6 +16,8 @@ exports.toProtoPayload = toProtoPayload;
16
16
  exports.buildEnvelope = buildEnvelope;
17
17
  const node_crypto_1 = require("node:crypto");
18
18
  const constants_1 = require("./constants");
19
+ const errors_1 = require("./errors");
20
+ const validation_1 = require("./validation");
19
21
  function newSessionId() {
20
22
  return (0, node_crypto_1.randomUUID)();
21
23
  }
@@ -52,9 +54,13 @@ function inferOutcomePositive(action) {
52
54
  /**
53
55
  * Build a {@link CommitmentRef} pointing at a prior accepted commitment, for
54
56
  * use as `buildCommitmentPayload({ supersedes })` (cross-session supersession,
55
- * RFC-MACP-0001 §7.3). Parity with python-sdk `build_commitment_ref`.
57
+ * RFC-MACP-0001 §7.3). Use `commitmentHash()` (from `./commitment-hash`) to
58
+ * produce a valid `commitmentHash` value — this function validates the hash
59
+ * shape (`sha256:<64 lowercase hex>`) and throws `MacpSessionError` if it
60
+ * doesn't match. Parity with python-sdk `build_commitment_ref`.
56
61
  */
57
62
  function buildCommitmentRef(input) {
63
+ (0, validation_1.validateCommitmentHash)(input.commitmentHash);
58
64
  return { sessionId: input.sessionId, commitmentHash: input.commitmentHash };
59
65
  }
60
66
  /**
@@ -82,8 +88,16 @@ function buildCommitmentPayload(input) {
82
88
  policyVersion: input.policyVersion ?? constants_1.DEFAULT_POLICY_VERSION,
83
89
  outcomePositive: input.outcomePositive ?? inferOutcomePositive(input.action),
84
90
  };
85
- if (input.supersedes)
91
+ if (input.supersedes !== undefined) {
92
+ if (typeof input.supersedes !== 'object' || input.supersedes === null) {
93
+ // `String()`, not `JSON.stringify()`: the latter throws on a bigint,
94
+ // which would replace this deliberate `MacpSessionError` with an
95
+ // unrelated `TypeError` for that one input shape.
96
+ throw new errors_1.MacpSessionError(`supersedes must be a CommitmentRef object, got: ${String(input.supersedes)}`);
97
+ }
98
+ (0, validation_1.validateCommitmentHash)(input.supersedes.commitmentHash);
86
99
  payload.supersedes = input.supersedes;
100
+ }
87
101
  return payload;
88
102
  }
89
103
  function buildRoot(uri, name = '') {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { Auth, type AuthConfig, type BearerAuthOptions } from './auth';
2
2
  export * from './base-session';
3
3
  export * from './client';
4
+ export { commitmentHash } from './commitment-hash';
4
5
  export * from './constants';
5
6
  export * from './decision';
6
7
  export * from './envelope';
@@ -16,5 +17,6 @@ export * from './quorum';
16
17
  export * from './task';
17
18
  export * from './types';
18
19
  export * from './validation';
20
+ export { VERSION } from './version';
19
21
  export * from './watchers';
20
22
  export * as agent from './agent';
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.agent = exports.Auth = void 0;
39
+ exports.agent = exports.VERSION = exports.commitmentHash = exports.Auth = void 0;
40
40
  // Auth: export only the public constructors and types. The helpers
41
41
  // `validateAuth`, `authSender`, `assertSenderMatchesIdentity`, and
42
42
  // `metadataFromAuth` are SDK-internal — importable from the `./auth`
@@ -46,6 +46,13 @@ var auth_1 = require("./auth");
46
46
  Object.defineProperty(exports, "Auth", { enumerable: true, get: function () { return auth_1.Auth; } });
47
47
  __exportStar(require("./base-session"), exports);
48
48
  __exportStar(require("./client"), exports);
49
+ // Commitment hash: export only `commitmentHash`. `canonicalizeCommitmentPayload`
50
+ // is the projection/JCS half of the algorithm — importable from the
51
+ // `./commitment-hash` submodule (as the vector-runner tests do) but kept off
52
+ // the top-level surface to match python-sdk's public API, which does not
53
+ // expose its `canonical_projection` equivalent either.
54
+ var commitment_hash_1 = require("./commitment-hash");
55
+ Object.defineProperty(exports, "commitmentHash", { enumerable: true, get: function () { return commitment_hash_1.commitmentHash; } });
49
56
  __exportStar(require("./constants"), exports);
50
57
  __exportStar(require("./decision"), exports);
51
58
  __exportStar(require("./envelope"), exports);
@@ -61,5 +68,7 @@ __exportStar(require("./quorum"), exports);
61
68
  __exportStar(require("./task"), exports);
62
69
  __exportStar(require("./types"), exports);
63
70
  __exportStar(require("./validation"), exports);
71
+ var version_1 = require("./version");
72
+ Object.defineProperty(exports, "VERSION", { enumerable: true, get: function () { return version_1.VERSION; } });
64
73
  __exportStar(require("./watchers"), exports);
65
74
  exports.agent = __importStar(require("./agent"));
@@ -9,6 +9,7 @@ export declare function validateTtlMs(ttlMs: number): void;
9
9
  export declare function validateMaxSuspendMs(maxSuspendMs: number): void;
10
10
  export declare function validateParticipants(participants: string[]): void;
11
11
  export declare function validateRequiredField(fieldName: string, value: string): void;
12
+ export declare function validateCommitmentHash(value: string, field?: string): void;
12
13
  export declare function validateSessionStart(input: {
13
14
  intent: string;
14
15
  participants: string[];
@@ -11,6 +11,7 @@ exports.validateTtlMs = validateTtlMs;
11
11
  exports.validateMaxSuspendMs = validateMaxSuspendMs;
12
12
  exports.validateParticipants = validateParticipants;
13
13
  exports.validateRequiredField = validateRequiredField;
14
+ exports.validateCommitmentHash = validateCommitmentHash;
14
15
  exports.validateSessionStart = validateSessionStart;
15
16
  const errors_1 = require("./errors");
16
17
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
@@ -91,6 +92,12 @@ function validateRequiredField(fieldName, value) {
91
92
  throw new errors_1.MacpSessionError(`${fieldName} must be non-empty`);
92
93
  }
93
94
  }
95
+ const COMMITMENT_HASH_RE = /^sha256:[0-9a-f]{64}$/;
96
+ function validateCommitmentHash(value, field = 'commitmentHash') {
97
+ if (!COMMITMENT_HASH_RE.test(value)) {
98
+ throw new errors_1.MacpSessionError(`${field} must be 'sha256:' followed by 64 lowercase hex characters, got: ${value}`);
99
+ }
100
+ }
94
101
  function validateSessionStart(input) {
95
102
  validateRequiredField('intent', input.intent);
96
103
  validateParticipants(input.participants);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The installed `macp-sdk-typescript` package version.
3
+ *
4
+ * Kept in sync with package.json by release-please (see the
5
+ * `x-release-please-version` annotation).
6
+ */
7
+ export declare const VERSION = "0.7.0";
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VERSION = void 0;
4
+ /**
5
+ * The installed `macp-sdk-typescript` package version.
6
+ *
7
+ * Kept in sync with package.json by release-please (see the
8
+ * `x-release-please-version` annotation).
9
+ */
10
+ exports.VERSION = '0.7.0'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "macp-sdk-typescript",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "TypeScript SDK for the MACP Rust runtime",
5
5
  "license": "Apache-2.0",
6
6
  "type": "commonjs",
@@ -39,7 +39,7 @@
39
39
  "prepublishOnly": "npm run check && npm run lint && npm run format:check && npm test && npm run build"
40
40
  },
41
41
  "engines": {
42
- "node": ">=18"
42
+ "node": ">=20"
43
43
  },
44
44
  "keywords": [
45
45
  "macp",
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "@grpc/grpc-js": "^1.14.3",
57
57
  "@grpc/proto-loader": "^0.8.0",
58
- "@multiagentcoordinationprotocol/proto": "^0.1.6",
58
+ "@multiagentcoordinationprotocol/proto": "^0.1.8",
59
59
  "protobufjs": "^8.6.5"
60
60
  },
61
61
  "devDependencies": {