macp-sdk-typescript 0.2.0 → 0.3.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 +124 -1
  2. package/README.md +99 -3
  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 +58 -0
  8. package/dist/agent/participant.js +143 -45
  9. package/dist/agent/runner.d.ts +34 -1
  10. package/dist/agent/runner.js +53 -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 +64 -0
  15. package/dist/base-session.js +112 -0
  16. package/dist/client.d.ts +25 -0
  17. package/dist/client.js +100 -10
  18. package/dist/constants.d.ts +21 -16
  19. package/dist/constants.js +24 -17
  20. package/dist/decision.d.ts +2 -1
  21. package/dist/decision.js +2 -1
  22. package/dist/envelope.d.ts +25 -3
  23. package/dist/envelope.js +42 -11
  24. package/dist/errors.d.ts +17 -4
  25. package/dist/errors.js +19 -10
  26. package/dist/handoff.d.ts +2 -9
  27. package/dist/handoff.js +2 -17
  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 +6 -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 +2 -1
  39. package/dist/proposal.js +2 -1
  40. package/dist/quorum.d.ts +2 -1
  41. package/dist/quorum.js +2 -1
  42. package/dist/task.d.ts +6 -5
  43. package/dist/task.js +6 -5
  44. package/dist/types.d.ts +11 -2
  45. package/dist/watchers.d.ts +23 -1
  46. package/dist/watchers.js +59 -1
  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;
@@ -214,7 +293,7 @@ class MacpClient {
214
293
  websiteUrl: '',
215
294
  },
216
295
  capabilities: {
217
- sessions: { stream: true },
296
+ sessions: { stream: true, listSessions: true, watchSessions: true },
218
297
  cancellation: { cancelSession: true },
219
298
  progress: { progress: true },
220
299
  manifest: { getManifest: true },
@@ -293,6 +372,15 @@ class MacpClient {
293
372
  const res = await this.unary('ListPolicies', { mode: mode || '' }, auth, options?.deadlineMs);
294
373
  return res.descriptors || [];
295
374
  }
375
+ async listSessions(options) {
376
+ const auth = this.requireAuth(options?.auth);
377
+ const res = await this.unary('ListSessions', {}, auth, options?.deadlineMs);
378
+ return res.sessions || [];
379
+ }
380
+ watchSessions(auth) {
381
+ const metadata = this.metadata(auth);
382
+ return metadata ? this.client.WatchSessions({}, metadata) : this.client.WatchSessions({});
383
+ }
296
384
  watchPolicies(auth) {
297
385
  const metadata = this.metadata(auth);
298
386
  return metadata ? this.client.WatchPolicies({}, metadata) : this.client.WatchPolicies({});
@@ -339,12 +427,13 @@ class MacpClient {
339
427
  (0, validation_1.validateSignalType)(options.signalType, options.data);
340
428
  const auth = this.requireAuth(options.auth);
341
429
  (0, auth_1.assertSenderMatchesIdentity)(auth, options.sender);
342
- const payload = this.protoRegistry.encodeKnownPayload('', 'Signal', {
430
+ const signalPayload = (0, envelope_1.buildSignalPayload)({
343
431
  signalType: options.signalType,
344
- data: options.data ?? Buffer.alloc(0),
345
- confidence: options.confidence ?? 0,
346
- correlationSessionId: options.correlationSessionId ?? '',
432
+ data: options.data,
433
+ confidence: options.confidence,
434
+ correlationSessionId: options.correlationSessionId,
347
435
  });
436
+ const payload = this.protoRegistry.encodeKnownPayload('', 'Signal', signalPayload);
348
437
  const envelope = (0, envelope_1.buildEnvelope)({
349
438
  mode: '',
350
439
  messageType: 'Signal',
@@ -357,13 +446,14 @@ class MacpClient {
357
446
  async sendProgress(options) {
358
447
  const auth = this.requireAuth(options.auth);
359
448
  (0, auth_1.assertSenderMatchesIdentity)(auth, options.sender);
360
- const payload = this.protoRegistry.encodeKnownPayload('', 'Progress', {
449
+ const progressPayload = (0, envelope_1.buildProgressPayload)({
361
450
  progressToken: options.progressToken,
362
451
  progress: options.progress,
363
452
  total: options.total,
364
- message: options.message ?? '',
365
- targetMessageId: options.targetMessageId ?? '',
453
+ message: options.message,
454
+ targetMessageId: options.targetMessageId,
366
455
  });
456
+ const payload = this.protoRegistry.encodeKnownPayload('', 'Progress', progressPayload);
367
457
  const envelope = (0, envelope_1.buildEnvelope)({
368
458
  mode: options.mode ?? '',
369
459
  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';
@@ -24,7 +24,8 @@ export declare class DecisionSession {
24
24
  intent: string;
25
25
  participants: string[];
26
26
  ttlMs: number;
27
- context?: Buffer | string | Record<string, unknown>;
27
+ contextId?: string;
28
+ extensions?: Record<string, Buffer>;
28
29
  roots?: {
29
30
  uri: string;
30
31
  name?: string;
package/dist/decision.js CHANGED
@@ -47,7 +47,8 @@ class DecisionSession {
47
47
  intent: input.intent,
48
48
  participants: input.participants,
49
49
  ttlMs: input.ttlMs,
50
- context: input.context,
50
+ contextId: input.contextId,
51
+ extensions: input.extensions,
51
52
  roots: input.roots,
52
53
  modeVersion: this.modeVersion,
53
54
  configurationVersion: this.configurationVersion,
@@ -1,9 +1,8 @@
1
- import type { CommitmentPayload, Envelope, Root, SessionStartPayload } from './types';
1
+ import type { CommitmentPayload, 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;
5
5
  export declare function nowUnixMs(): number;
6
- export declare function encodeContext(context?: Buffer | string | Record<string, unknown>): Buffer;
7
6
  export declare function buildSessionStartPayload(input: {
8
7
  intent: string;
9
8
  participants: string[];
@@ -11,7 +10,8 @@ export declare function buildSessionStartPayload(input: {
11
10
  modeVersion?: string;
12
11
  configurationVersion?: string;
13
12
  policyVersion?: string;
14
- context?: Buffer | string | Record<string, unknown>;
13
+ contextId?: string;
14
+ extensions?: Record<string, Buffer>;
15
15
  roots?: Root[];
16
16
  }): SessionStartPayload;
17
17
  export declare function inferOutcomePositive(action: string): boolean;
@@ -26,6 +26,27 @@ export declare function buildCommitmentPayload(input: {
26
26
  outcomePositive?: boolean;
27
27
  }): CommitmentPayload;
28
28
  export declare function buildRoot(uri: string, name?: string): Root;
29
+ export declare function buildSignalPayload(input: {
30
+ signalType: string;
31
+ data?: Buffer | Uint8Array;
32
+ confidence?: number;
33
+ correlationSessionId?: string;
34
+ }): SignalPayload;
35
+ export declare function buildProgressPayload(input: {
36
+ progressToken: string;
37
+ progress: number;
38
+ total: number;
39
+ message?: string;
40
+ targetMessageId?: string;
41
+ }): ProgressPayload;
42
+ type ProtoSerializable = {
43
+ serializeBinary(): Uint8Array;
44
+ } | {
45
+ toBinary(): Uint8Array;
46
+ } | {
47
+ finish(): Uint8Array;
48
+ };
49
+ export declare function serializeMessage(message: ProtoSerializable | unknown): Uint8Array;
29
50
  /**
30
51
  * Type-erasure helper for protobuf payload encoders.
31
52
  *
@@ -48,3 +69,4 @@ export declare function buildEnvelope(input: {
48
69
  macpVersion?: string;
49
70
  timestampUnixMs?: string | number;
50
71
  }): Envelope;
72
+ export {};
package/dist/envelope.js CHANGED
@@ -4,11 +4,13 @@ exports.newSessionId = newSessionId;
4
4
  exports.newMessageId = newMessageId;
5
5
  exports.newCommitmentId = newCommitmentId;
6
6
  exports.nowUnixMs = nowUnixMs;
7
- exports.encodeContext = encodeContext;
8
7
  exports.buildSessionStartPayload = buildSessionStartPayload;
9
8
  exports.inferOutcomePositive = inferOutcomePositive;
10
9
  exports.buildCommitmentPayload = buildCommitmentPayload;
11
10
  exports.buildRoot = buildRoot;
11
+ exports.buildSignalPayload = buildSignalPayload;
12
+ exports.buildProgressPayload = buildProgressPayload;
13
+ exports.serializeMessage = serializeMessage;
12
14
  exports.toProtoPayload = toProtoPayload;
13
15
  exports.buildEnvelope = buildEnvelope;
14
16
  const node_crypto_1 = require("node:crypto");
@@ -25,15 +27,6 @@ function newCommitmentId() {
25
27
  function nowUnixMs() {
26
28
  return Date.now();
27
29
  }
28
- function encodeContext(context) {
29
- if (!context)
30
- return Buffer.alloc(0);
31
- if (Buffer.isBuffer(context))
32
- return context;
33
- if (typeof context === 'string')
34
- return Buffer.from(context, 'utf8');
35
- return Buffer.from(JSON.stringify(context), 'utf8');
36
- }
37
30
  function buildSessionStartPayload(input) {
38
31
  return {
39
32
  intent: input.intent,
@@ -42,7 +35,8 @@ function buildSessionStartPayload(input) {
42
35
  configurationVersion: input.configurationVersion ?? constants_1.DEFAULT_CONFIGURATION_VERSION,
43
36
  policyVersion: input.policyVersion ?? constants_1.DEFAULT_POLICY_VERSION,
44
37
  ttlMs: input.ttlMs,
45
- context: encodeContext(input.context),
38
+ contextId: input.contextId ?? '',
39
+ extensions: input.extensions ?? {},
46
40
  roots: input.roots ?? [],
47
41
  };
48
42
  }
@@ -68,6 +62,43 @@ function buildCommitmentPayload(input) {
68
62
  function buildRoot(uri, name = '') {
69
63
  return { uri, name };
70
64
  }
65
+ function buildSignalPayload(input) {
66
+ const data = input.data instanceof Buffer ? input.data : input.data ? Buffer.from(input.data) : Buffer.alloc(0);
67
+ return {
68
+ signalType: input.signalType,
69
+ data,
70
+ confidence: input.confidence ?? 0,
71
+ correlationSessionId: input.correlationSessionId ?? '',
72
+ };
73
+ }
74
+ function buildProgressPayload(input) {
75
+ return {
76
+ progressToken: input.progressToken,
77
+ progress: input.progress,
78
+ total: input.total,
79
+ message: input.message ?? '',
80
+ targetMessageId: input.targetMessageId ?? '',
81
+ };
82
+ }
83
+ function serializeMessage(message) {
84
+ // Parity with python-sdk `envelope.serialize_message`: invoke the protobuf
85
+ // serializer exposed on the message. Supports objects with `serializeBinary`
86
+ // (protoc-gen-js), `toBinary` (protobuf-es / ts-proto), or `finish`
87
+ // (protobufjs Writer). For plain JS interface payloads, use
88
+ // `ProtoRegistry.encodeKnownPayload(mode, messageType, payload)` instead —
89
+ // the registry owns the mode/messageType → descriptor mapping.
90
+ const candidate = message;
91
+ if (typeof candidate?.serializeBinary === 'function') {
92
+ return candidate.serializeBinary();
93
+ }
94
+ if (typeof candidate?.toBinary === 'function') {
95
+ return candidate.toBinary();
96
+ }
97
+ if (typeof candidate?.finish === 'function') {
98
+ return candidate.finish();
99
+ }
100
+ throw new TypeError('serializeMessage: object is not a protobuf message — expected serializeBinary(), toBinary(), or finish() method. For plain JS payloads, use ProtoRegistry.encodeKnownPayload().');
101
+ }
71
102
  /**
72
103
  * Type-erasure helper for protobuf payload encoders.
73
104
  *
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
@@ -24,7 +24,8 @@ export declare class HandoffSession {
24
24
  intent: string;
25
25
  participants: string[];
26
26
  ttlMs: number;
27
- context?: Buffer | string | Record<string, unknown>;
27
+ contextId?: string;
28
+ extensions?: Record<string, Buffer>;
28
29
  roots?: {
29
30
  uri: string;
30
31
  name?: string;
@@ -39,14 +40,6 @@ export declare class HandoffSession {
39
40
  sender?: string;
40
41
  auth?: AuthConfig;
41
42
  }): Promise<Ack>;
42
- /**
43
- * @deprecated Use {@link addContext} instead. Scheduled for removal in 0.4.0.
44
- * First call logs a one-shot warning to guide migrations without spamming long-lived processes.
45
- */
46
- sendContext(input: HandoffContextPayload & {
47
- sender?: string;
48
- auth?: AuthConfig;
49
- }): Promise<Ack>;
50
43
  acceptHandoff(input: HandoffAcceptPayload & {
51
44
  sender?: string;
52
45
  auth?: AuthConfig;
package/dist/handoff.js CHANGED
@@ -6,14 +6,6 @@ const constants_1 = require("./constants");
6
6
  const envelope_1 = require("./envelope");
7
7
  const handoff_1 = require("./projections/handoff");
8
8
  const validation_1 = require("./validation");
9
- let sendContextWarned = false;
10
- function warnSendContextOnce() {
11
- if (sendContextWarned)
12
- return;
13
- sendContextWarned = true;
14
- // eslint-disable-next-line no-console
15
- console.warn('[macp-sdk-typescript] HandoffSession.sendContext() is deprecated and will be removed in 0.4.0. Use addContext() instead.');
16
- }
17
9
  class HandoffSession {
18
10
  client;
19
11
  sessionId;
@@ -55,7 +47,8 @@ class HandoffSession {
55
47
  intent: input.intent,
56
48
  participants: input.participants,
57
49
  ttlMs: input.ttlMs,
58
- context: input.context,
50
+ contextId: input.contextId,
51
+ extensions: input.extensions,
59
52
  roots: input.roots,
60
53
  modeVersion: this.modeVersion,
61
54
  configurationVersion: this.configurationVersion,
@@ -94,14 +87,6 @@ class HandoffSession {
94
87
  });
95
88
  return this.sendAndTrack(envelope, input.auth);
96
89
  }
97
- /**
98
- * @deprecated Use {@link addContext} instead. Scheduled for removal in 0.4.0.
99
- * First call logs a one-shot warning to guide migrations without spamming long-lived processes.
100
- */
101
- async sendContext(input) {
102
- warnSendContextOnce();
103
- return this.addContext(input);
104
- }
105
90
  async acceptHandoff(input) {
106
91
  (0, validation_1.validateRequiredField)('handoffId', input.handoffId);
107
92
  const envelope = (0, envelope_1.buildEnvelope)({
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
- export * from './auth';
1
+ export { Auth, type AuthConfig, type BearerAuthOptions } from './auth';
2
+ export * from './base-session';
2
3
  export * from './client';
3
4
  export * from './constants';
4
5
  export * from './decision';
5
6
  export * from './envelope';
6
7
  export * from './errors';
7
8
  export * from './handoff';
9
+ export * from './logging';
8
10
  export * from './policy';
9
11
  export * from './projections';
10
12
  export * from './proposal';
package/dist/index.js CHANGED
@@ -36,14 +36,22 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.agent = void 0;
40
- __exportStar(require("./auth"), exports);
39
+ exports.agent = exports.Auth = void 0;
40
+ // Auth: export only the public constructors and types. The helpers
41
+ // `validateAuth`, `authSender`, `assertSenderMatchesIdentity`, and
42
+ // `metadataFromAuth` are SDK-internal — importable from the `./auth`
43
+ // submodule if needed, but intentionally absent from the top-level surface
44
+ // to match python-sdk's `__all__` (which only exposes `AuthConfig`).
45
+ var auth_1 = require("./auth");
46
+ Object.defineProperty(exports, "Auth", { enumerable: true, get: function () { return auth_1.Auth; } });
47
+ __exportStar(require("./base-session"), exports);
41
48
  __exportStar(require("./client"), exports);
42
49
  __exportStar(require("./constants"), exports);
43
50
  __exportStar(require("./decision"), exports);
44
51
  __exportStar(require("./envelope"), exports);
45
52
  __exportStar(require("./errors"), exports);
46
53
  __exportStar(require("./handoff"), exports);
54
+ __exportStar(require("./logging"), exports);
47
55
  __exportStar(require("./policy"), exports);
48
56
  __exportStar(require("./projections"), exports);
49
57
  __exportStar(require("./proposal"), exports);
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Structured logger for the MACP SDK.
3
+ *
4
+ * Parity with python-sdk's `macp_sdk._logging.logger` + `configure_logging()`:
5
+ * production Node apps need controllable log levels (silent by default-ish,
6
+ * opt-in verbose) and the ability to re-route to Pino / Winston / etc.
7
+ */
8
+ export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';
9
+ export type LogSink = (level: Exclude<LogLevel, 'silent'>, args: unknown[]) => void;
10
+ export declare const logger: {
11
+ error: (...args: unknown[]) => void;
12
+ warn: (...args: unknown[]) => void;
13
+ info: (...args: unknown[]) => void;
14
+ debug: (...args: unknown[]) => void;
15
+ };
16
+ export declare function configureLogging(options: {
17
+ level?: LogLevel;
18
+ sink?: LogSink;
19
+ }): void;
20
+ /** Test-only: reset to env-derived level + default sink. */
21
+ export declare function _resetLoggingForTests(): void;