macp-sdk-typescript 0.2.2 → 0.4.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 (47) hide show
  1. package/CHANGELOG.md +176 -1
  2. package/README.md +49 -6
  3. package/dist/agent/cancel-callback.d.ts +23 -0
  4. package/dist/agent/cancel-callback.js +108 -0
  5. package/dist/agent/index.d.ts +1 -0
  6. package/dist/agent/index.js +1 -0
  7. package/dist/agent/participant.d.ts +39 -0
  8. package/dist/agent/participant.js +114 -45
  9. package/dist/agent/runner.d.ts +13 -0
  10. package/dist/agent/runner.js +34 -0
  11. package/dist/agent/strategies.d.ts +2 -0
  12. package/dist/agent/strategies.js +8 -0
  13. package/dist/agent/transports.js +5 -0
  14. package/dist/base-session.d.ts +75 -0
  15. package/dist/base-session.js +127 -0
  16. package/dist/client.d.ts +54 -0
  17. package/dist/client.js +130 -9
  18. package/dist/constants.d.ts +21 -16
  19. package/dist/constants.js +24 -17
  20. package/dist/decision.d.ts +10 -0
  21. package/dist/decision.js +20 -0
  22. package/dist/envelope.d.ts +38 -1
  23. package/dist/envelope.js +53 -1
  24. package/dist/errors.d.ts +17 -4
  25. package/dist/errors.js +19 -10
  26. package/dist/handoff.d.ts +10 -8
  27. package/dist/handoff.js +20 -16
  28. package/dist/index.d.ts +3 -1
  29. package/dist/index.js +10 -2
  30. package/dist/logging.d.ts +21 -0
  31. package/dist/logging.js +64 -0
  32. package/dist/policy.d.ts +47 -42
  33. package/dist/policy.js +5 -6
  34. package/dist/projections/base.d.ts +20 -0
  35. package/dist/projections/base.js +37 -0
  36. package/dist/projections.d.ts +1 -0
  37. package/dist/projections.js +1 -0
  38. package/dist/proposal.d.ts +10 -0
  39. package/dist/proposal.js +20 -0
  40. package/dist/quorum.d.ts +10 -0
  41. package/dist/quorum.js +20 -0
  42. package/dist/task.d.ts +14 -4
  43. package/dist/task.js +24 -4
  44. package/dist/types.d.ts +30 -3
  45. package/dist/watchers.d.ts +37 -2
  46. package/dist/watchers.js +65 -5
  47. package/package.json +2 -2
package/dist/client.js CHANGED
@@ -60,13 +60,45 @@ class AsyncQueue {
60
60
  else
61
61
  this.items.push(item);
62
62
  }
63
+ unshift(item) {
64
+ this.items.unshift(item);
65
+ }
63
66
  shift() {
64
67
  const item = this.items.shift();
65
68
  if (item !== undefined)
66
69
  return Promise.resolve(item);
67
70
  return new Promise((resolve) => this.resolvers.push(resolve));
68
71
  }
72
+ shiftWithTimeout(timeoutMs) {
73
+ const item = this.items.shift();
74
+ if (item !== undefined)
75
+ return Promise.resolve(item);
76
+ return new Promise((resolve) => {
77
+ let settled = false;
78
+ const resolver = (value) => {
79
+ if (settled) {
80
+ // Timeout already fired; put the value back so it isn't lost.
81
+ this.items.unshift(value);
82
+ return;
83
+ }
84
+ settled = true;
85
+ clearTimeout(timer);
86
+ resolve(value);
87
+ };
88
+ this.resolvers.push(resolver);
89
+ const timer = setTimeout(() => {
90
+ if (settled)
91
+ return;
92
+ settled = true;
93
+ const idx = this.resolvers.indexOf(resolver);
94
+ if (idx >= 0)
95
+ this.resolvers.splice(idx, 1);
96
+ resolve(TIMEOUT);
97
+ }, timeoutMs);
98
+ });
99
+ }
69
100
  }
101
+ const TIMEOUT = Symbol('stream-read-timeout');
70
102
  const STREAM_END = Symbol('stream-end');
71
103
  class MacpStream {
72
104
  call;
@@ -109,16 +141,63 @@ class MacpStream {
109
141
  });
110
142
  });
111
143
  }
144
+ /**
145
+ * RFC-MACP-0006-A1: Send a subscribe-only frame to receive session history
146
+ * + live broadcast. The runtime replays accepted envelopes from
147
+ * `afterSequence` onwards, then continues with live broadcast.
148
+ */
149
+ sendSubscribe(sessionId, afterSequence = 0) {
150
+ if (this.closed)
151
+ return Promise.reject(new errors_1.MacpSdkError('stream is already closed'));
152
+ return new Promise((resolve, reject) => {
153
+ this.call.write({ subscribeSessionId: sessionId, afterSequence }, (error) => {
154
+ if (error)
155
+ reject(error);
156
+ else
157
+ resolve();
158
+ });
159
+ });
160
+ }
112
161
  async *responses() {
113
162
  while (true) {
114
163
  const item = await this.queue.shift();
115
- if (item === STREAM_END)
164
+ if (item === STREAM_END) {
165
+ // Re-push so a subsequent read()/responses() call also observes end-of-stream.
166
+ this.queue.unshift(STREAM_END);
116
167
  return;
168
+ }
117
169
  if (item instanceof Error)
118
170
  throw item;
119
171
  yield item;
120
172
  }
121
173
  }
174
+ /**
175
+ * Read a single envelope from the stream. Parity with python-sdk's
176
+ * {@code MacpStream.read(timeout)}.
177
+ *
178
+ * @param timeoutMs Maximum time to wait for the next envelope. If omitted,
179
+ * blocks indefinitely until an envelope arrives, the stream
180
+ * ends, or an error occurs.
181
+ * @returns The next envelope, or {@code null} if the stream has
182
+ * ended.
183
+ * @throws MacpTimeoutError If {@code timeoutMs} elapses before an envelope
184
+ * arrives.
185
+ * @throws MacpTransportError If the underlying stream errored.
186
+ */
187
+ async read(timeoutMs) {
188
+ const item = timeoutMs === undefined ? await this.queue.shift() : await this.queue.shiftWithTimeout(timeoutMs);
189
+ if (item === TIMEOUT) {
190
+ throw new errors_1.MacpTimeoutError(`stream read timed out after ${timeoutMs}ms`);
191
+ }
192
+ if (item === STREAM_END) {
193
+ // Re-push so future calls (including responses()) also observe the end.
194
+ this.queue.unshift(STREAM_END);
195
+ return null;
196
+ }
197
+ if (item instanceof Error)
198
+ throw item;
199
+ return item;
200
+ }
122
201
  close() {
123
202
  if (this.closed)
124
203
  return;
@@ -144,7 +223,7 @@ class MacpClient {
144
223
  }
145
224
  this.defaultDeadlineMs = options.defaultDeadlineMs;
146
225
  this.clientName = options.clientName ?? 'macp-sdk-typescript';
147
- this.clientVersion = options.clientVersion ?? '0.2.0';
226
+ this.clientVersion = options.clientVersion ?? '0.3.0';
148
227
  // eslint-disable-next-line @typescript-eslint/no-require-imports
149
228
  const { protoDir: defaultProtoDir } = require('@multiagentcoordinationprotocol/proto');
150
229
  const protoDir = options.protoDir ?? defaultProtoDir;
@@ -240,6 +319,13 @@ class MacpClient {
240
319
  const auth = this.requireAuth(options?.auth);
241
320
  return this.unary('GetSession', { sessionId }, auth, options?.deadlineMs);
242
321
  }
322
+ /**
323
+ * Terminate a session via the `CancelSession` control-plane RPC.
324
+ *
325
+ * As of proto 0.1.3 the resulting `Ack.sessionState` is
326
+ * `SESSION_STATE_CANCELLED` (previously `SESSION_STATE_EXPIRED`) — distinct
327
+ * from TTL/policy expiry so consumers can tell explicit cancellation apart.
328
+ */
243
329
  async cancelSession(sessionId, reason, options) {
244
330
  const auth = this.requireAuth(options?.auth);
245
331
  const request = { sessionId, reason };
@@ -251,6 +337,39 @@ class MacpClient {
251
337
  throw new errors_1.MacpAckError(ack ?? {});
252
338
  return ack;
253
339
  }
340
+ /**
341
+ * Pause a session via the `SuspendSession` control-plane RPC (proto 0.1.3+).
342
+ *
343
+ * Suspension is non-terminal: the resulting `Ack.sessionState` is
344
+ * `SESSION_STATE_SUSPENDED`, the session's remaining TTL is banked, and
345
+ * messages sent to a suspended session are rejected until {@link resumeSession}
346
+ * restores it to `SESSION_STATE_OPEN`. Restricted to the initiator and
347
+ * policy-delegated roles, mirroring {@link cancelSession}.
348
+ */
349
+ async suspendSession(sessionId, reason, options) {
350
+ const auth = this.requireAuth(options?.auth);
351
+ const response = await this.unary('SuspendSession', { sessionId, reason }, auth, options?.deadlineMs);
352
+ const ack = response.ack;
353
+ if (options?.raiseOnNack !== false && !ack?.ok)
354
+ throw new errors_1.MacpAckError(ack ?? {});
355
+ return ack;
356
+ }
357
+ /**
358
+ * Resume a suspended session via the `ResumeSession` control-plane RPC
359
+ * (proto 0.1.3+).
360
+ *
361
+ * Restores `SESSION_STATE_OPEN` and adds the banked TTL back to the session's
362
+ * absolute deadline. Restricted to the initiator and policy-delegated roles,
363
+ * mirroring {@link suspendSession}.
364
+ */
365
+ async resumeSession(sessionId, reason, options) {
366
+ const auth = this.requireAuth(options?.auth);
367
+ const response = await this.unary('ResumeSession', { sessionId, reason }, auth, options?.deadlineMs);
368
+ const ack = response.ack;
369
+ if (options?.raiseOnNack !== false && !ack?.ok)
370
+ throw new errors_1.MacpAckError(ack ?? {});
371
+ return ack;
372
+ }
254
373
  async getManifest(agentId = '', deadlineMs) {
255
374
  return this.unary('GetManifest', { agentId }, undefined, deadlineMs);
256
375
  }
@@ -348,12 +467,13 @@ class MacpClient {
348
467
  (0, validation_1.validateSignalType)(options.signalType, options.data);
349
468
  const auth = this.requireAuth(options.auth);
350
469
  (0, auth_1.assertSenderMatchesIdentity)(auth, options.sender);
351
- const payload = this.protoRegistry.encodeKnownPayload('', 'Signal', {
470
+ const signalPayload = (0, envelope_1.buildSignalPayload)({
352
471
  signalType: options.signalType,
353
- data: options.data ?? Buffer.alloc(0),
354
- confidence: options.confidence ?? 0,
355
- correlationSessionId: options.correlationSessionId ?? '',
472
+ data: options.data,
473
+ confidence: options.confidence,
474
+ correlationSessionId: options.correlationSessionId,
356
475
  });
476
+ const payload = this.protoRegistry.encodeKnownPayload('', 'Signal', signalPayload);
357
477
  const envelope = (0, envelope_1.buildEnvelope)({
358
478
  mode: '',
359
479
  messageType: 'Signal',
@@ -366,13 +486,14 @@ class MacpClient {
366
486
  async sendProgress(options) {
367
487
  const auth = this.requireAuth(options.auth);
368
488
  (0, auth_1.assertSenderMatchesIdentity)(auth, options.sender);
369
- const payload = this.protoRegistry.encodeKnownPayload('', 'Progress', {
489
+ const progressPayload = (0, envelope_1.buildProgressPayload)({
370
490
  progressToken: options.progressToken,
371
491
  progress: options.progress,
372
492
  total: options.total,
373
- message: options.message ?? '',
374
- targetMessageId: options.targetMessageId ?? '',
493
+ message: options.message,
494
+ targetMessageId: options.targetMessageId,
375
495
  });
496
+ const payload = this.protoRegistry.encodeKnownPayload('', 'Progress', progressPayload);
376
497
  const envelope = (0, envelope_1.buildEnvelope)({
377
498
  mode: options.mode ?? '',
378
499
  messageType: 'Progress',
@@ -8,19 +8,24 @@ export declare const MODE_TASK = "macp.mode.task.v1";
8
8
  export declare const MODE_HANDOFF = "macp.mode.handoff.v1";
9
9
  export declare const MODE_QUORUM = "macp.mode.quorum.v1";
10
10
  export declare const MODE_MULTI_ROUND = "ext.multi_round.v1";
11
- export declare const ERR_UNSUPPORTED_PROTOCOL_VERSION = "UNSUPPORTED_PROTOCOL_VERSION";
12
- export declare const ERR_INVALID_ENVELOPE = "INVALID_ENVELOPE";
13
- export declare const ERR_SESSION_ALREADY_EXISTS = "SESSION_ALREADY_EXISTS";
14
- export declare const ERR_SESSION_NOT_FOUND = "SESSION_NOT_FOUND";
15
- export declare const ERR_SESSION_NOT_OPEN = "SESSION_NOT_OPEN";
16
- export declare const ERR_MODE_NOT_SUPPORTED = "MODE_NOT_SUPPORTED";
17
- export declare const ERR_FORBIDDEN = "FORBIDDEN";
18
- export declare const ERR_UNAUTHENTICATED = "UNAUTHENTICATED";
19
- export declare const ERR_DUPLICATE_MESSAGE = "DUPLICATE_MESSAGE";
20
- export declare const ERR_PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE";
21
- export declare const ERR_RATE_LIMITED = "RATE_LIMITED";
22
- export declare const ERR_INTERNAL_ERROR = "INTERNAL_ERROR";
23
- export declare const ERR_POLICY_DENIED = "POLICY_DENIED";
24
- export declare const ERR_INVALID_SESSION_ID = "INVALID_SESSION_ID";
25
- export declare const ERR_UNKNOWN_POLICY_VERSION = "UNKNOWN_POLICY_VERSION";
26
- export declare const ERR_INVALID_POLICY_DEFINITION = "INVALID_POLICY_DEFINITION";
11
+ /**
12
+ * The five first-class coordination modes. Parity with
13
+ * python-sdk `macp_sdk.constants.STANDARD_MODES`.
14
+ */
15
+ export declare const STANDARD_MODES: readonly ["macp.mode.decision.v1", "macp.mode.proposal.v1", "macp.mode.task.v1", "macp.mode.handoff.v1", "macp.mode.quorum.v1"];
16
+ export declare const UNSUPPORTED_PROTOCOL_VERSION = "UNSUPPORTED_PROTOCOL_VERSION";
17
+ export declare const INVALID_ENVELOPE = "INVALID_ENVELOPE";
18
+ export declare const SESSION_ALREADY_EXISTS = "SESSION_ALREADY_EXISTS";
19
+ export declare const SESSION_NOT_FOUND = "SESSION_NOT_FOUND";
20
+ export declare const SESSION_NOT_OPEN = "SESSION_NOT_OPEN";
21
+ export declare const MODE_NOT_SUPPORTED = "MODE_NOT_SUPPORTED";
22
+ export declare const FORBIDDEN = "FORBIDDEN";
23
+ export declare const UNAUTHENTICATED = "UNAUTHENTICATED";
24
+ export declare const DUPLICATE_MESSAGE = "DUPLICATE_MESSAGE";
25
+ export declare const PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE";
26
+ export declare const RATE_LIMITED = "RATE_LIMITED";
27
+ export declare const INTERNAL_ERROR = "INTERNAL_ERROR";
28
+ export declare const POLICY_DENIED = "POLICY_DENIED";
29
+ export declare const INVALID_SESSION_ID = "INVALID_SESSION_ID";
30
+ export declare const UNKNOWN_POLICY_VERSION = "UNKNOWN_POLICY_VERSION";
31
+ export declare const INVALID_POLICY_DEFINITION = "INVALID_POLICY_DEFINITION";
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ERR_INVALID_POLICY_DEFINITION = exports.ERR_UNKNOWN_POLICY_VERSION = exports.ERR_INVALID_SESSION_ID = exports.ERR_POLICY_DENIED = exports.ERR_INTERNAL_ERROR = exports.ERR_RATE_LIMITED = exports.ERR_PAYLOAD_TOO_LARGE = exports.ERR_DUPLICATE_MESSAGE = exports.ERR_UNAUTHENTICATED = exports.ERR_FORBIDDEN = exports.ERR_MODE_NOT_SUPPORTED = exports.ERR_SESSION_NOT_OPEN = exports.ERR_SESSION_NOT_FOUND = exports.ERR_SESSION_ALREADY_EXISTS = exports.ERR_INVALID_ENVELOPE = exports.ERR_UNSUPPORTED_PROTOCOL_VERSION = exports.MODE_MULTI_ROUND = exports.MODE_QUORUM = exports.MODE_HANDOFF = exports.MODE_TASK = exports.MODE_PROPOSAL = exports.MODE_DECISION = exports.DEFAULT_POLICY_VERSION = exports.DEFAULT_CONFIGURATION_VERSION = exports.DEFAULT_MODE_VERSION = exports.MACP_VERSION = void 0;
3
+ exports.INVALID_POLICY_DEFINITION = exports.UNKNOWN_POLICY_VERSION = exports.INVALID_SESSION_ID = exports.POLICY_DENIED = exports.INTERNAL_ERROR = exports.RATE_LIMITED = exports.PAYLOAD_TOO_LARGE = exports.DUPLICATE_MESSAGE = exports.UNAUTHENTICATED = exports.FORBIDDEN = exports.MODE_NOT_SUPPORTED = exports.SESSION_NOT_OPEN = exports.SESSION_NOT_FOUND = exports.SESSION_ALREADY_EXISTS = exports.INVALID_ENVELOPE = exports.UNSUPPORTED_PROTOCOL_VERSION = exports.STANDARD_MODES = exports.MODE_MULTI_ROUND = exports.MODE_QUORUM = exports.MODE_HANDOFF = exports.MODE_TASK = exports.MODE_PROPOSAL = exports.MODE_DECISION = exports.DEFAULT_POLICY_VERSION = exports.DEFAULT_CONFIGURATION_VERSION = exports.DEFAULT_MODE_VERSION = exports.MACP_VERSION = void 0;
4
4
  exports.MACP_VERSION = '1.0';
5
5
  exports.DEFAULT_MODE_VERSION = '1.0.0';
6
6
  exports.DEFAULT_CONFIGURATION_VERSION = 'config.default';
@@ -11,19 +11,26 @@ exports.MODE_TASK = 'macp.mode.task.v1';
11
11
  exports.MODE_HANDOFF = 'macp.mode.handoff.v1';
12
12
  exports.MODE_QUORUM = 'macp.mode.quorum.v1';
13
13
  exports.MODE_MULTI_ROUND = 'ext.multi_round.v1';
14
- exports.ERR_UNSUPPORTED_PROTOCOL_VERSION = 'UNSUPPORTED_PROTOCOL_VERSION';
15
- exports.ERR_INVALID_ENVELOPE = 'INVALID_ENVELOPE';
16
- exports.ERR_SESSION_ALREADY_EXISTS = 'SESSION_ALREADY_EXISTS';
17
- exports.ERR_SESSION_NOT_FOUND = 'SESSION_NOT_FOUND';
18
- exports.ERR_SESSION_NOT_OPEN = 'SESSION_NOT_OPEN';
19
- exports.ERR_MODE_NOT_SUPPORTED = 'MODE_NOT_SUPPORTED';
20
- exports.ERR_FORBIDDEN = 'FORBIDDEN';
21
- exports.ERR_UNAUTHENTICATED = 'UNAUTHENTICATED';
22
- exports.ERR_DUPLICATE_MESSAGE = 'DUPLICATE_MESSAGE';
23
- exports.ERR_PAYLOAD_TOO_LARGE = 'PAYLOAD_TOO_LARGE';
24
- exports.ERR_RATE_LIMITED = 'RATE_LIMITED';
25
- exports.ERR_INTERNAL_ERROR = 'INTERNAL_ERROR';
26
- exports.ERR_POLICY_DENIED = 'POLICY_DENIED';
27
- exports.ERR_INVALID_SESSION_ID = 'INVALID_SESSION_ID';
28
- exports.ERR_UNKNOWN_POLICY_VERSION = 'UNKNOWN_POLICY_VERSION';
29
- exports.ERR_INVALID_POLICY_DEFINITION = 'INVALID_POLICY_DEFINITION';
14
+ /**
15
+ * The five first-class coordination modes. Parity with
16
+ * python-sdk `macp_sdk.constants.STANDARD_MODES`.
17
+ */
18
+ exports.STANDARD_MODES = [exports.MODE_DECISION, exports.MODE_PROPOSAL, exports.MODE_TASK, exports.MODE_HANDOFF, exports.MODE_QUORUM];
19
+ // Well-known error codes. Names match the on-the-wire strings the runtime
20
+ // emits and python-sdk's exports (no ERR_ prefix).
21
+ exports.UNSUPPORTED_PROTOCOL_VERSION = 'UNSUPPORTED_PROTOCOL_VERSION';
22
+ exports.INVALID_ENVELOPE = 'INVALID_ENVELOPE';
23
+ exports.SESSION_ALREADY_EXISTS = 'SESSION_ALREADY_EXISTS';
24
+ exports.SESSION_NOT_FOUND = 'SESSION_NOT_FOUND';
25
+ exports.SESSION_NOT_OPEN = 'SESSION_NOT_OPEN';
26
+ exports.MODE_NOT_SUPPORTED = 'MODE_NOT_SUPPORTED';
27
+ exports.FORBIDDEN = 'FORBIDDEN';
28
+ exports.UNAUTHENTICATED = 'UNAUTHENTICATED';
29
+ exports.DUPLICATE_MESSAGE = 'DUPLICATE_MESSAGE';
30
+ exports.PAYLOAD_TOO_LARGE = 'PAYLOAD_TOO_LARGE';
31
+ exports.RATE_LIMITED = 'RATE_LIMITED';
32
+ exports.INTERNAL_ERROR = 'INTERNAL_ERROR';
33
+ exports.POLICY_DENIED = 'POLICY_DENIED';
34
+ exports.INVALID_SESSION_ID = 'INVALID_SESSION_ID';
35
+ exports.UNKNOWN_POLICY_VERSION = 'UNKNOWN_POLICY_VERSION';
36
+ exports.INVALID_POLICY_DEFINITION = 'INVALID_POLICY_DEFINITION';
@@ -61,6 +61,16 @@ export declare class DecisionSession {
61
61
  metadata: SessionMetadata;
62
62
  }>;
63
63
  cancel(reason?: string, auth?: AuthConfig): Promise<Ack>;
64
+ /**
65
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: the
66
+ * runtime banks remaining TTL and rejects messages until {@link resume}.
67
+ */
68
+ suspend(reason?: string, auth?: AuthConfig): Promise<Ack>;
69
+ /**
70
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
71
+ * restoring `SESSION_STATE_OPEN` and the banked TTL.
72
+ */
73
+ resume(reason?: string, auth?: AuthConfig): Promise<Ack>;
64
74
  openStream(auth?: AuthConfig): MacpStream;
65
75
  }
66
76
  export {};
package/dist/decision.js CHANGED
@@ -142,6 +142,26 @@ class DecisionSession {
142
142
  raiseOnNack: true,
143
143
  });
144
144
  }
145
+ /**
146
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: the
147
+ * runtime banks remaining TTL and rejects messages until {@link resume}.
148
+ */
149
+ async suspend(reason = '', auth) {
150
+ return this.client.suspendSession(this.sessionId, reason, {
151
+ auth: auth ?? this.auth,
152
+ raiseOnNack: true,
153
+ });
154
+ }
155
+ /**
156
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
157
+ * restoring `SESSION_STATE_OPEN` and the banked TTL.
158
+ */
159
+ async resume(reason = '', auth) {
160
+ return this.client.resumeSession(this.sessionId, reason, {
161
+ auth: auth ?? this.auth,
162
+ raiseOnNack: true,
163
+ });
164
+ }
145
165
  openStream(auth) {
146
166
  return this.client.openStream({ auth: auth ?? this.auth });
147
167
  }
@@ -1,4 +1,4 @@
1
- import type { CommitmentPayload, Envelope, Root, SessionStartPayload } from './types';
1
+ import type { CommitmentPayload, CommitmentRef, Envelope, ProgressPayload, Root, SessionStartPayload, SignalPayload } from './types';
2
2
  export declare function newSessionId(): string;
3
3
  export declare function newMessageId(): string;
4
4
  export declare function newCommitmentId(): string;
@@ -15,6 +15,15 @@ export declare function buildSessionStartPayload(input: {
15
15
  roots?: Root[];
16
16
  }): SessionStartPayload;
17
17
  export declare function inferOutcomePositive(action: string): boolean;
18
+ /**
19
+ * Build a {@link CommitmentRef} pointing at a prior accepted commitment, for
20
+ * use as `buildCommitmentPayload({ supersedes })` (cross-session supersession,
21
+ * RFC-MACP-0001 §7.3). Parity with python-sdk `build_commitment_ref`.
22
+ */
23
+ export declare function buildCommitmentRef(input: {
24
+ sessionId: string;
25
+ commitmentHash: string;
26
+ }): CommitmentRef;
18
27
  export declare function buildCommitmentPayload(input: {
19
28
  action: string;
20
29
  authorityScope: string;
@@ -24,8 +33,35 @@ export declare function buildCommitmentPayload(input: {
24
33
  configurationVersion?: string;
25
34
  policyVersion?: string;
26
35
  outcomePositive?: boolean;
36
+ /**
37
+ * Cross-session supersession (RFC-MACP-0001 §7.3): reference to the prior
38
+ * commitment this one supersedes. Distinct from proposal-mode
39
+ * `supersedesProposalId`. Absent by default. Backward-compatible.
40
+ */
41
+ supersedes?: CommitmentRef;
27
42
  }): CommitmentPayload;
28
43
  export declare function buildRoot(uri: string, name?: string): Root;
44
+ export declare function buildSignalPayload(input: {
45
+ signalType: string;
46
+ data?: Buffer | Uint8Array;
47
+ confidence?: number;
48
+ correlationSessionId?: string;
49
+ }): SignalPayload;
50
+ export declare function buildProgressPayload(input: {
51
+ progressToken: string;
52
+ progress: number;
53
+ total: number;
54
+ message?: string;
55
+ targetMessageId?: string;
56
+ }): ProgressPayload;
57
+ type ProtoSerializable = {
58
+ serializeBinary(): Uint8Array;
59
+ } | {
60
+ toBinary(): Uint8Array;
61
+ } | {
62
+ finish(): Uint8Array;
63
+ };
64
+ export declare function serializeMessage(message: ProtoSerializable | unknown): Uint8Array;
29
65
  /**
30
66
  * Type-erasure helper for protobuf payload encoders.
31
67
  *
@@ -48,3 +84,4 @@ export declare function buildEnvelope(input: {
48
84
  macpVersion?: string;
49
85
  timestampUnixMs?: string | number;
50
86
  }): Envelope;
87
+ export {};
package/dist/envelope.js CHANGED
@@ -6,8 +6,12 @@ exports.newCommitmentId = newCommitmentId;
6
6
  exports.nowUnixMs = nowUnixMs;
7
7
  exports.buildSessionStartPayload = buildSessionStartPayload;
8
8
  exports.inferOutcomePositive = inferOutcomePositive;
9
+ exports.buildCommitmentRef = buildCommitmentRef;
9
10
  exports.buildCommitmentPayload = buildCommitmentPayload;
10
11
  exports.buildRoot = buildRoot;
12
+ exports.buildSignalPayload = buildSignalPayload;
13
+ exports.buildProgressPayload = buildProgressPayload;
14
+ exports.serializeMessage = serializeMessage;
11
15
  exports.toProtoPayload = toProtoPayload;
12
16
  exports.buildEnvelope = buildEnvelope;
13
17
  const node_crypto_1 = require("node:crypto");
@@ -44,8 +48,16 @@ function inferOutcomePositive(action) {
44
48
  return false;
45
49
  return true;
46
50
  }
51
+ /**
52
+ * Build a {@link CommitmentRef} pointing at a prior accepted commitment, for
53
+ * use as `buildCommitmentPayload({ supersedes })` (cross-session supersession,
54
+ * RFC-MACP-0001 §7.3). Parity with python-sdk `build_commitment_ref`.
55
+ */
56
+ function buildCommitmentRef(input) {
57
+ return { sessionId: input.sessionId, commitmentHash: input.commitmentHash };
58
+ }
47
59
  function buildCommitmentPayload(input) {
48
- return {
60
+ const payload = {
49
61
  commitmentId: input.commitmentId ?? newCommitmentId(),
50
62
  action: input.action,
51
63
  authorityScope: input.authorityScope,
@@ -55,10 +67,50 @@ function buildCommitmentPayload(input) {
55
67
  policyVersion: input.policyVersion ?? constants_1.DEFAULT_POLICY_VERSION,
56
68
  outcomePositive: input.outcomePositive ?? inferOutcomePositive(input.action),
57
69
  };
70
+ if (input.supersedes)
71
+ payload.supersedes = input.supersedes;
72
+ return payload;
58
73
  }
59
74
  function buildRoot(uri, name = '') {
60
75
  return { uri, name };
61
76
  }
77
+ function buildSignalPayload(input) {
78
+ const data = input.data instanceof Buffer ? input.data : input.data ? Buffer.from(input.data) : Buffer.alloc(0);
79
+ return {
80
+ signalType: input.signalType,
81
+ data,
82
+ confidence: input.confidence ?? 0,
83
+ correlationSessionId: input.correlationSessionId ?? '',
84
+ };
85
+ }
86
+ function buildProgressPayload(input) {
87
+ return {
88
+ progressToken: input.progressToken,
89
+ progress: input.progress,
90
+ total: input.total,
91
+ message: input.message ?? '',
92
+ targetMessageId: input.targetMessageId ?? '',
93
+ };
94
+ }
95
+ function serializeMessage(message) {
96
+ // Parity with python-sdk `envelope.serialize_message`: invoke the protobuf
97
+ // serializer exposed on the message. Supports objects with `serializeBinary`
98
+ // (protoc-gen-js), `toBinary` (protobuf-es / ts-proto), or `finish`
99
+ // (protobufjs Writer). For plain JS interface payloads, use
100
+ // `ProtoRegistry.encodeKnownPayload(mode, messageType, payload)` instead —
101
+ // the registry owns the mode/messageType → descriptor mapping.
102
+ const candidate = message;
103
+ if (typeof candidate?.serializeBinary === 'function') {
104
+ return candidate.serializeBinary();
105
+ }
106
+ if (typeof candidate?.toBinary === 'function') {
107
+ return candidate.toBinary();
108
+ }
109
+ if (typeof candidate?.finish === 'function') {
110
+ return candidate.finish();
111
+ }
112
+ throw new TypeError('serializeMessage: object is not a protobuf message — expected serializeBinary(), toBinary(), or finish() method. For plain JS payloads, use ProtoRegistry.encodeKnownPayload().');
113
+ }
62
114
  /**
63
115
  * Type-erasure helper for protobuf payload encoders.
64
116
  *
package/dist/errors.d.ts CHANGED
@@ -5,6 +5,18 @@ export declare class MacpSdkError extends Error {
5
5
  export declare class MacpTransportError extends MacpSdkError {
6
6
  constructor(message: string);
7
7
  }
8
+ /**
9
+ * Structured NACK record. Mirrors python-sdk's `AckFailure` dataclass so
10
+ * cross-SDK code can share the same shape when logging or persisting a
11
+ * rejected message.
12
+ */
13
+ export interface AckFailure {
14
+ code: string;
15
+ message: string;
16
+ sessionId: string;
17
+ messageId: string;
18
+ reasons: string[];
19
+ }
8
20
  export declare class MacpAckError extends MacpSdkError {
9
21
  readonly ack: Ack;
10
22
  /** Optional gRPC trailing metadata for extracting additional error details. */
@@ -12,14 +24,15 @@ export declare class MacpAckError extends MacpSdkError {
12
24
  key: string;
13
25
  value: string | Buffer;
14
26
  }>;
27
+ /** Structured NACK record — parity with python-sdk `MacpAckError.failure`. */
28
+ readonly failure: AckFailure;
15
29
  constructor(ack: Ack, grpcMetadata?: Array<{
16
30
  key: string;
17
31
  value: string | Buffer;
18
32
  }>);
19
- /** Extract structured denial reasons from the ACK error details and/or gRPC trailing metadata. */
20
- get reasons(): string[];
21
- private _parseAckReasons;
22
- private _parseGrpcMetadataReasons;
33
+ private static _extractReasons;
34
+ private static _parseAckReasons;
35
+ private static _parseGrpcMetadataReasons;
23
36
  }
24
37
  export declare class MacpSessionError extends MacpSdkError {
25
38
  constructor(message: string);
package/dist/errors.js CHANGED
@@ -19,26 +19,35 @@ class MacpAckError extends MacpSdkError {
19
19
  ack;
20
20
  /** Optional gRPC trailing metadata for extracting additional error details. */
21
21
  grpcMetadata;
22
+ /** Structured NACK record — parity with python-sdk `MacpAckError.failure`. */
23
+ failure;
22
24
  constructor(ack, grpcMetadata) {
23
25
  super(`${ack.error?.code ?? 'UNKNOWN'}: ${ack.error?.message ?? 'runtime returned nack'}`);
24
26
  this.name = 'MacpAckError';
25
27
  this.ack = ack;
26
28
  this.grpcMetadata = grpcMetadata;
29
+ const reasons = MacpAckError._extractReasons(ack, grpcMetadata);
30
+ this.failure = {
31
+ code: ack.error?.code ?? 'UNKNOWN',
32
+ message: ack.error?.message ?? 'runtime returned nack',
33
+ sessionId: ack.sessionId ?? ack.error?.sessionId ?? '',
34
+ messageId: ack.messageId ?? ack.error?.messageId ?? '',
35
+ reasons,
36
+ };
27
37
  }
28
- /** Extract structured denial reasons from the ACK error details and/or gRPC trailing metadata. */
29
- get reasons() {
38
+ static _extractReasons(ack, grpcMetadata) {
30
39
  // First try: parse from ack.error.details (matches Python _parse_ack_reasons)
31
- const fromDetails = this._parseAckReasons();
40
+ const fromDetails = MacpAckError._parseAckReasons(ack);
32
41
  if (fromDetails.length > 0)
33
42
  return fromDetails;
34
43
  // Second try: parse from gRPC trailing metadata (matches Python _parse_grpc_metadata_reasons)
35
- return this._parseGrpcMetadataReasons();
44
+ return MacpAckError._parseGrpcMetadataReasons(grpcMetadata);
36
45
  }
37
- _parseAckReasons() {
38
- if (!this.ack.error?.details)
46
+ static _parseAckReasons(ack) {
47
+ if (!ack.error?.details)
39
48
  return [];
40
49
  try {
41
- const details = this.ack.error.details;
50
+ const details = ack.error.details;
42
51
  const raw = Buffer.isBuffer(details) ? details.toString('utf-8') : String(details);
43
52
  const parsed = JSON.parse(raw);
44
53
  const reasons = parsed.reasons;
@@ -48,11 +57,11 @@ class MacpAckError extends MacpSdkError {
48
57
  return [];
49
58
  }
50
59
  }
51
- _parseGrpcMetadataReasons() {
52
- if (!this.grpcMetadata)
60
+ static _parseGrpcMetadataReasons(grpcMetadata) {
61
+ if (!grpcMetadata)
53
62
  return [];
54
63
  try {
55
- for (const item of this.grpcMetadata) {
64
+ for (const item of grpcMetadata) {
56
65
  if (item.key === 'macp-error-details-bin') {
57
66
  const data = Buffer.isBuffer(item.value)
58
67
  ? item.value.toString('utf-8')
package/dist/handoff.d.ts CHANGED
@@ -40,14 +40,6 @@ export declare class HandoffSession {
40
40
  sender?: string;
41
41
  auth?: AuthConfig;
42
42
  }): Promise<Ack>;
43
- /**
44
- * @deprecated Use {@link addContext} instead. Scheduled for removal in 0.4.0.
45
- * First call logs a one-shot warning to guide migrations without spamming long-lived processes.
46
- */
47
- sendContext(input: HandoffContextPayload & {
48
- sender?: string;
49
- auth?: AuthConfig;
50
- }): Promise<Ack>;
51
43
  acceptHandoff(input: HandoffAcceptPayload & {
52
44
  sender?: string;
53
45
  auth?: AuthConfig;
@@ -69,6 +61,16 @@ export declare class HandoffSession {
69
61
  metadata: SessionMetadata;
70
62
  }>;
71
63
  cancel(reason?: string, auth?: AuthConfig): Promise<Ack>;
64
+ /**
65
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: the
66
+ * runtime banks remaining TTL and rejects messages until {@link resume}.
67
+ */
68
+ suspend(reason?: string, auth?: AuthConfig): Promise<Ack>;
69
+ /**
70
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
71
+ * restoring `SESSION_STATE_OPEN` and the banked TTL.
72
+ */
73
+ resume(reason?: string, auth?: AuthConfig): Promise<Ack>;
72
74
  openStream(auth?: AuthConfig): MacpStream;
73
75
  }
74
76
  export {};