macp-sdk-typescript 0.6.0 → 0.7.1
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 +23 -0
- package/dist/base-session.js +1 -1
- package/dist/commitment-hash.d.ts +41 -0
- package/dist/commitment-hash.js +198 -0
- package/dist/decision.js +1 -1
- package/dist/envelope.d.ts +9 -2
- package/dist/envelope.js +16 -2
- package/dist/handoff.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -1
- package/dist/proposal.js +1 -1
- package/dist/quorum.js +1 -1
- package/dist/task.js +1 -1
- package/dist/validation.d.ts +1 -0
- package/dist/validation.js +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/watchers.js +8 -0
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ 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.1](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/compare/v0.7.0...v0.7.1) (2026-08-31)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
* validate explicit empty-string sessionId, and freeze the commitment field set ([#52](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/issues/52)) ([5e4dc25](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/commit/5e4dc25389d948570afbd9653cfd52bc12e97150)), closes [#47](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/issues/47) [#48](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/issues/48)
|
|
13
|
+
|
|
14
|
+
## [0.7.0](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/compare/v0.6.0...v0.7.0) (2026-08-30)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### ⚠ BREAKING CHANGES
|
|
18
|
+
|
|
19
|
+
* RFC-MACP-0013 canonical commitment hash ([#45](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/issues/45))
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* 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))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Bug Fixes
|
|
27
|
+
|
|
28
|
+
* 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))
|
|
29
|
+
|
|
7
30
|
## [0.6.0](https://github.com/multiagentcoordinationprotocol/macp-sdk-typescript/compare/v0.5.0...v0.6.0) (2026-07-10)
|
|
8
31
|
|
|
9
32
|
|
package/dist/base-session.js
CHANGED
|
@@ -26,7 +26,7 @@ class BaseSession {
|
|
|
26
26
|
projection;
|
|
27
27
|
constructor(client, options = {}) {
|
|
28
28
|
this.client = client;
|
|
29
|
-
if (options.sessionId)
|
|
29
|
+
if (options.sessionId !== undefined)
|
|
30
30
|
(0, validation_1.validateSessionId)(options.sessionId);
|
|
31
31
|
this.sessionId = options.sessionId ?? (0, envelope_1.newSessionId)();
|
|
32
32
|
this.modeVersion = options.modeVersion ?? constants_1.DEFAULT_MODE_VERSION;
|
|
@@ -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
|
+
}
|
package/dist/decision.js
CHANGED
|
@@ -16,7 +16,7 @@ class DecisionSession {
|
|
|
16
16
|
projection = new projections_1.DecisionProjection();
|
|
17
17
|
constructor(client, options = {}) {
|
|
18
18
|
this.client = client;
|
|
19
|
-
if (options.sessionId)
|
|
19
|
+
if (options.sessionId !== undefined)
|
|
20
20
|
(0, validation_1.validateSessionId)(options.sessionId);
|
|
21
21
|
this.sessionId = options.sessionId ?? (0, envelope_1.newSessionId)();
|
|
22
22
|
this.modeVersion = options.modeVersion ?? constants_1.DEFAULT_MODE_VERSION;
|
package/dist/envelope.d.ts
CHANGED
|
@@ -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).
|
|
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).
|
|
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/handoff.js
CHANGED
|
@@ -16,7 +16,7 @@ class HandoffSession {
|
|
|
16
16
|
projection = new handoff_1.HandoffProjection();
|
|
17
17
|
constructor(client, options = {}) {
|
|
18
18
|
this.client = client;
|
|
19
|
-
if (options.sessionId)
|
|
19
|
+
if (options.sessionId !== undefined)
|
|
20
20
|
(0, validation_1.validateSessionId)(options.sessionId);
|
|
21
21
|
this.sessionId = options.sessionId ?? (0, envelope_1.newSessionId)();
|
|
22
22
|
this.modeVersion = options.modeVersion ?? constants_1.DEFAULT_MODE_VERSION;
|
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';
|
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.VERSION = 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);
|
package/dist/proposal.js
CHANGED
|
@@ -16,7 +16,7 @@ class ProposalSession {
|
|
|
16
16
|
projection = new proposal_1.ProposalProjection();
|
|
17
17
|
constructor(client, options = {}) {
|
|
18
18
|
this.client = client;
|
|
19
|
-
if (options.sessionId)
|
|
19
|
+
if (options.sessionId !== undefined)
|
|
20
20
|
(0, validation_1.validateSessionId)(options.sessionId);
|
|
21
21
|
this.sessionId = options.sessionId ?? (0, envelope_1.newSessionId)();
|
|
22
22
|
this.modeVersion = options.modeVersion ?? constants_1.DEFAULT_MODE_VERSION;
|
package/dist/quorum.js
CHANGED
|
@@ -16,7 +16,7 @@ class QuorumSession {
|
|
|
16
16
|
projection = new quorum_1.QuorumProjection();
|
|
17
17
|
constructor(client, options = {}) {
|
|
18
18
|
this.client = client;
|
|
19
|
-
if (options.sessionId)
|
|
19
|
+
if (options.sessionId !== undefined)
|
|
20
20
|
(0, validation_1.validateSessionId)(options.sessionId);
|
|
21
21
|
this.sessionId = options.sessionId ?? (0, envelope_1.newSessionId)();
|
|
22
22
|
this.modeVersion = options.modeVersion ?? constants_1.DEFAULT_MODE_VERSION;
|
package/dist/task.js
CHANGED
|
@@ -16,7 +16,7 @@ class TaskSession {
|
|
|
16
16
|
projection = new task_1.TaskProjection();
|
|
17
17
|
constructor(client, options = {}) {
|
|
18
18
|
this.client = client;
|
|
19
|
-
if (options.sessionId)
|
|
19
|
+
if (options.sessionId !== undefined)
|
|
20
20
|
(0, validation_1.validateSessionId)(options.sessionId);
|
|
21
21
|
this.sessionId = options.sessionId ?? (0, envelope_1.newSessionId)();
|
|
22
22
|
this.modeVersion = options.modeVersion ?? constants_1.DEFAULT_MODE_VERSION;
|
package/dist/validation.d.ts
CHANGED
|
@@ -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[];
|
package/dist/validation.js
CHANGED
|
@@ -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);
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/dist/watchers.js
CHANGED
|
@@ -96,6 +96,14 @@ function serverStreamToAsyncGenerator(stream) {
|
|
|
96
96
|
stream.cancel();
|
|
97
97
|
throw err;
|
|
98
98
|
},
|
|
99
|
+
// Required since TypeScript 6 / @types/node 26, which make `AsyncGenerator`
|
|
100
|
+
// extend `AsyncDisposable` (lib.esnext.disposable). Native `async function*`
|
|
101
|
+
// generators get this for free; this hand-rolled one has to declare it. It
|
|
102
|
+
// also makes every watcher stream usable with `await using`, which cancels
|
|
103
|
+
// the underlying gRPC stream on scope exit exactly like `return()` does.
|
|
104
|
+
async [Symbol.asyncDispose]() {
|
|
105
|
+
stream.cancel();
|
|
106
|
+
},
|
|
99
107
|
};
|
|
100
108
|
}
|
|
101
109
|
class ModeRegistryWatcher {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "macp-sdk-typescript",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "TypeScript SDK for the MACP Rust runtime",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -59,14 +59,14 @@
|
|
|
59
59
|
"protobufjs": "^8.6.5"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@types/node": "^
|
|
63
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
64
|
-
"@typescript-eslint/parser": "^8.
|
|
65
|
-
"@vitest/coverage-v8": "^
|
|
66
|
-
"eslint": "^9.
|
|
62
|
+
"@types/node": "^26.4.0",
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "^8.68.0",
|
|
64
|
+
"@typescript-eslint/parser": "^8.68.0",
|
|
65
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
66
|
+
"eslint": "^10.9.1",
|
|
67
67
|
"prettier": "^3.0.0",
|
|
68
68
|
"tsx": "^4.21.0",
|
|
69
|
-
"typescript": "^
|
|
70
|
-
"vitest": "^
|
|
69
|
+
"typescript": "^6.0.3",
|
|
70
|
+
"vitest": "^4.1.11"
|
|
71
71
|
}
|
|
72
72
|
}
|