macp-sdk-typescript 0.2.2 → 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.
@@ -77,6 +77,8 @@ function fromBootstrap(bootstrapPath) {
77
77
  intent: ss.intent,
78
78
  participants: ss.participants,
79
79
  ttlMs: ss.ttl_ms,
80
+ contextId: ss.context_id,
81
+ extensions: encodeExtensions(ss.extensions),
80
82
  roots: ss.roots,
81
83
  },
82
84
  kickoff: payload.initiator.kickoff
@@ -98,6 +100,38 @@ function fromBootstrap(bootstrapPath) {
98
100
  configurationVersion: payload.configuration_version,
99
101
  policyVersion: payload.policy_version ?? constants_1.DEFAULT_POLICY_VERSION,
100
102
  initiator,
103
+ cancelCallback: payload.cancel_callback
104
+ ? {
105
+ host: payload.cancel_callback.host,
106
+ port: payload.cancel_callback.port,
107
+ path: payload.cancel_callback.path,
108
+ }
109
+ : undefined,
101
110
  };
102
111
  return new participant_1.Participant(config);
103
112
  }
113
+ /**
114
+ * Serialise a JSON-native extensions map (as delivered in the bootstrap
115
+ * payload) into the `Record<string, Buffer>` form the SessionStart envelope
116
+ * expects. Each value is encoded as UTF-8 JSON so arbitrary structures
117
+ * round-trip through the protobuf `bytes` field. `Buffer` / `Uint8Array`
118
+ * values are passed through untouched so callers that already hold raw bytes
119
+ * don't pay a double-encode.
120
+ */
121
+ function encodeExtensions(extensions) {
122
+ if (!extensions)
123
+ return undefined;
124
+ const out = {};
125
+ for (const [key, value] of Object.entries(extensions)) {
126
+ if (Buffer.isBuffer(value)) {
127
+ out[key] = value;
128
+ }
129
+ else if (value instanceof Uint8Array) {
130
+ out[key] = Buffer.from(value);
131
+ }
132
+ else {
133
+ out[key] = Buffer.from(JSON.stringify(value), 'utf8');
134
+ }
135
+ }
136
+ return out;
137
+ }
@@ -19,6 +19,7 @@ export interface VotingStrategy {
19
19
  decideVote(projection: DecisionProjection): Promise<VoteResult>;
20
20
  }
21
21
  export declare function votingHandler(strategy: VotingStrategy): MessageHandler;
22
+ export declare function functionVoter(shouldVote: (projection: DecisionProjection) => boolean, decideVote: (projection: DecisionProjection) => Promise<VoteResult>): VotingStrategy;
22
23
  export declare function majorityVoter(options?: {
23
24
  positiveThreshold?: number;
24
25
  }): VotingStrategy;
@@ -33,6 +34,7 @@ export interface CommitmentStrategy {
33
34
  decideCommitment(projection: DecisionProjection): Promise<CommitmentResult>;
34
35
  }
35
36
  export declare function commitmentHandler(strategy: CommitmentStrategy): MessageHandler;
37
+ export declare function functionCommitter(shouldCommit: (projection: DecisionProjection) => boolean, decideCommitment: (projection: DecisionProjection) => Promise<CommitmentResult>): CommitmentStrategy;
36
38
  export declare function majorityCommitter(options?: {
37
39
  quorumSize?: number;
38
40
  action?: string;
@@ -3,8 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.evaluationHandler = evaluationHandler;
4
4
  exports.functionEvaluator = functionEvaluator;
5
5
  exports.votingHandler = votingHandler;
6
+ exports.functionVoter = functionVoter;
6
7
  exports.majorityVoter = majorityVoter;
7
8
  exports.commitmentHandler = commitmentHandler;
9
+ exports.functionCommitter = functionCommitter;
8
10
  exports.majorityCommitter = majorityCommitter;
9
11
  const envelope_1 = require("../envelope");
10
12
  function evaluationHandler(strategy) {
@@ -43,6 +45,9 @@ function votingHandler(strategy) {
43
45
  }
44
46
  };
45
47
  }
48
+ function functionVoter(shouldVote, decideVote) {
49
+ return { shouldVote, decideVote };
50
+ }
46
51
  function majorityVoter(options) {
47
52
  const threshold = options?.positiveThreshold ?? 0.5;
48
53
  return {
@@ -80,6 +85,9 @@ function commitmentHandler(strategy) {
80
85
  }
81
86
  };
82
87
  }
88
+ function functionCommitter(shouldCommit, decideCommitment) {
89
+ return { shouldCommit, decideCommitment };
90
+ }
83
91
  function majorityCommitter(options) {
84
92
  const quorum = options?.quorumSize ?? 1;
85
93
  const action = options?.action ?? 'commit';
@@ -25,6 +25,11 @@ class GrpcTransportAdapter {
25
25
  }
26
26
  async *start() {
27
27
  this.stream = this.client.openStream({ auth: this.auth });
28
+ // RFC-MACP-0006-A1: Subscribe to the session with history replay.
29
+ // The runtime replays accepted envelopes then switches to live broadcast.
30
+ // This ensures non-initiator agents receive the SessionStart + Proposal
31
+ // regardless of spawn order or connection timing.
32
+ await this.stream.sendSubscribe(this.sessionId);
28
33
  for await (const envelope of this.stream.responses()) {
29
34
  if (envelope.sessionId !== this.sessionId)
30
35
  continue;
@@ -0,0 +1,64 @@
1
+ import { type AuthConfig } from './auth';
2
+ import type { MacpClient, MacpStream } from './client';
3
+ import type { BaseProjection } from './projections/base';
4
+ import type { Ack, Envelope, Root, SessionMetadata } from './types';
5
+ export interface BaseSessionOptions {
6
+ sessionId?: string;
7
+ modeVersion?: string;
8
+ configurationVersion?: string;
9
+ policyVersion?: string;
10
+ auth?: AuthConfig;
11
+ }
12
+ /**
13
+ * Abstract base class for mode session helpers — parity with python-sdk's
14
+ * `macp_sdk.base_session.BaseSession`. Provides the shared lifecycle
15
+ * (start, commit, cancel, metadata, openStream) and the two identity-guarded
16
+ * helpers (`senderFor`, `sendAndTrack`) that every mode session needs.
17
+ *
18
+ * Concrete session classes (DecisionSession, etc.) pre-date this base class
19
+ * and implement their own equivalents; BaseSession is the recommended
20
+ * extension point for custom modes registered via `registerExtMode`.
21
+ */
22
+ export declare abstract class BaseSession<P extends BaseProjection> {
23
+ readonly client: MacpClient;
24
+ readonly sessionId: string;
25
+ readonly modeVersion: string;
26
+ readonly configurationVersion: string;
27
+ readonly policyVersion: string;
28
+ readonly auth?: AuthConfig;
29
+ readonly projection: P;
30
+ protected abstract readonly mode: string;
31
+ constructor(client: MacpClient, options?: BaseSessionOptions);
32
+ /** Return a new projection instance. Subclasses construct their mode-specific projection here. */
33
+ protected abstract createProjection(): P;
34
+ /** Resolve the envelope sender, raising {@link MacpIdentityMismatchError} on conflict. */
35
+ protected senderFor(sender: string | undefined, auth?: AuthConfig): string;
36
+ /** Send an envelope and, on ACK, feed it to the projection. */
37
+ protected sendAndTrack(envelope: Envelope, auth?: AuthConfig): Promise<Ack>;
38
+ /** Send SessionStart and begin tracking via the projection. */
39
+ start(input: {
40
+ intent: string;
41
+ participants: string[];
42
+ ttlMs: number;
43
+ contextId?: string;
44
+ extensions?: Record<string, Buffer>;
45
+ roots?: Root[];
46
+ sender?: string;
47
+ auth?: AuthConfig;
48
+ }): Promise<Ack>;
49
+ /** Send a Commitment envelope to resolve the session. */
50
+ commit(input: {
51
+ action: string;
52
+ authorityScope: string;
53
+ reason: string;
54
+ commitmentId?: string;
55
+ outcomePositive?: boolean;
56
+ sender?: string;
57
+ auth?: AuthConfig;
58
+ }): Promise<Ack>;
59
+ metadata(auth?: AuthConfig): Promise<{
60
+ metadata: SessionMetadata;
61
+ }>;
62
+ cancel(reason?: string, auth?: AuthConfig): Promise<Ack>;
63
+ openStream(auth?: AuthConfig): MacpStream;
64
+ }
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseSession = void 0;
4
+ const auth_1 = require("./auth");
5
+ const constants_1 = require("./constants");
6
+ const envelope_1 = require("./envelope");
7
+ const logging_1 = require("./logging");
8
+ const validation_1 = require("./validation");
9
+ /**
10
+ * Abstract base class for mode session helpers — parity with python-sdk's
11
+ * `macp_sdk.base_session.BaseSession`. Provides the shared lifecycle
12
+ * (start, commit, cancel, metadata, openStream) and the two identity-guarded
13
+ * helpers (`senderFor`, `sendAndTrack`) that every mode session needs.
14
+ *
15
+ * Concrete session classes (DecisionSession, etc.) pre-date this base class
16
+ * and implement their own equivalents; BaseSession is the recommended
17
+ * extension point for custom modes registered via `registerExtMode`.
18
+ */
19
+ class BaseSession {
20
+ client;
21
+ sessionId;
22
+ modeVersion;
23
+ configurationVersion;
24
+ policyVersion;
25
+ auth;
26
+ projection;
27
+ constructor(client, options = {}) {
28
+ this.client = client;
29
+ if (options.sessionId)
30
+ (0, validation_1.validateSessionId)(options.sessionId);
31
+ this.sessionId = options.sessionId ?? (0, envelope_1.newSessionId)();
32
+ this.modeVersion = options.modeVersion ?? constants_1.DEFAULT_MODE_VERSION;
33
+ this.configurationVersion = options.configurationVersion ?? constants_1.DEFAULT_CONFIGURATION_VERSION;
34
+ this.policyVersion = options.policyVersion ?? constants_1.DEFAULT_POLICY_VERSION;
35
+ this.auth = options.auth;
36
+ this.projection = this.createProjection();
37
+ }
38
+ /** Resolve the envelope sender, raising {@link MacpIdentityMismatchError} on conflict. */
39
+ senderFor(sender, auth) {
40
+ const effectiveAuth = auth ?? this.auth ?? this.client.auth;
41
+ (0, auth_1.assertSenderMatchesIdentity)(effectiveAuth, sender);
42
+ return sender ?? (0, auth_1.authSender)(effectiveAuth) ?? '';
43
+ }
44
+ /** Send an envelope and, on ACK, feed it to the projection. */
45
+ async sendAndTrack(envelope, auth) {
46
+ logging_1.logger.debug('send', {
47
+ session: envelope.sessionId,
48
+ messageType: envelope.messageType,
49
+ sender: envelope.sender,
50
+ });
51
+ const ack = await this.client.send(envelope, { auth: auth ?? this.auth });
52
+ if (ack.ok)
53
+ this.projection.applyEnvelope(envelope, this.client.protoRegistry);
54
+ else
55
+ logging_1.logger.warn('nack', { session: envelope.sessionId, code: ack.error?.code });
56
+ return ack;
57
+ }
58
+ /** Send SessionStart and begin tracking via the projection. */
59
+ async start(input) {
60
+ (0, validation_1.validateParticipantCount)(input.participants.length);
61
+ const payload = (0, envelope_1.buildSessionStartPayload)({
62
+ intent: input.intent,
63
+ participants: input.participants,
64
+ ttlMs: input.ttlMs,
65
+ contextId: input.contextId,
66
+ extensions: input.extensions,
67
+ roots: input.roots,
68
+ modeVersion: this.modeVersion,
69
+ configurationVersion: this.configurationVersion,
70
+ policyVersion: this.policyVersion,
71
+ });
72
+ const envelope = (0, envelope_1.buildEnvelope)({
73
+ mode: this.mode,
74
+ messageType: 'SessionStart',
75
+ sessionId: this.sessionId,
76
+ sender: this.senderFor(input.sender, input.auth),
77
+ payload: this.client.protoRegistry.encodeKnownPayload(this.mode, 'SessionStart', (0, envelope_1.toProtoPayload)(payload)),
78
+ });
79
+ return this.sendAndTrack(envelope, input.auth ?? this.auth);
80
+ }
81
+ /** Send a Commitment envelope to resolve the session. */
82
+ async commit(input) {
83
+ const payload = (0, envelope_1.buildCommitmentPayload)({
84
+ action: input.action,
85
+ authorityScope: input.authorityScope,
86
+ reason: input.reason,
87
+ commitmentId: input.commitmentId,
88
+ outcomePositive: input.outcomePositive,
89
+ modeVersion: this.modeVersion,
90
+ configurationVersion: this.configurationVersion,
91
+ policyVersion: this.policyVersion,
92
+ });
93
+ const envelope = (0, envelope_1.buildEnvelope)({
94
+ mode: this.mode,
95
+ messageType: 'Commitment',
96
+ sessionId: this.sessionId,
97
+ sender: this.senderFor(input.sender, input.auth),
98
+ payload: this.client.protoRegistry.encodeKnownPayload(this.mode, 'Commitment', (0, envelope_1.toProtoPayload)(payload)),
99
+ });
100
+ return this.sendAndTrack(envelope, input.auth);
101
+ }
102
+ metadata(auth) {
103
+ return this.client.getSession(this.sessionId, { auth: auth ?? this.auth });
104
+ }
105
+ cancel(reason = '', auth) {
106
+ return this.client.cancelSession(this.sessionId, reason, { auth: auth ?? this.auth });
107
+ }
108
+ openStream(auth) {
109
+ return this.client.openStream({ auth: auth ?? this.auth });
110
+ }
111
+ }
112
+ exports.BaseSession = BaseSession;
package/dist/client.d.ts CHANGED
@@ -35,7 +35,27 @@ export declare class MacpStream {
35
35
  constructor(call: grpc.ClientDuplexStream<any, any>);
36
36
  onInlineError(callback: InlineErrorCallback): void;
37
37
  send(envelope: Envelope): Promise<void>;
38
+ /**
39
+ * RFC-MACP-0006-A1: Send a subscribe-only frame to receive session history
40
+ * + live broadcast. The runtime replays accepted envelopes from
41
+ * `afterSequence` onwards, then continues with live broadcast.
42
+ */
43
+ sendSubscribe(sessionId: string, afterSequence?: number): Promise<void>;
38
44
  responses(): AsyncGenerator<Envelope, void, void>;
45
+ /**
46
+ * Read a single envelope from the stream. Parity with python-sdk's
47
+ * {@code MacpStream.read(timeout)}.
48
+ *
49
+ * @param timeoutMs Maximum time to wait for the next envelope. If omitted,
50
+ * blocks indefinitely until an envelope arrives, the stream
51
+ * ends, or an error occurs.
52
+ * @returns The next envelope, or {@code null} if the stream has
53
+ * ended.
54
+ * @throws MacpTimeoutError If {@code timeoutMs} elapses before an envelope
55
+ * arrives.
56
+ * @throws MacpTransportError If the underlying stream errored.
57
+ */
58
+ read(timeoutMs?: number): Promise<Envelope | null>;
39
59
  close(): void;
40
60
  }
41
61
  export declare class MacpClient {
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;
@@ -348,12 +427,13 @@ class MacpClient {
348
427
  (0, validation_1.validateSignalType)(options.signalType, options.data);
349
428
  const auth = this.requireAuth(options.auth);
350
429
  (0, auth_1.assertSenderMatchesIdentity)(auth, options.sender);
351
- const payload = this.protoRegistry.encodeKnownPayload('', 'Signal', {
430
+ const signalPayload = (0, envelope_1.buildSignalPayload)({
352
431
  signalType: options.signalType,
353
- data: options.data ?? Buffer.alloc(0),
354
- confidence: options.confidence ?? 0,
355
- correlationSessionId: options.correlationSessionId ?? '',
432
+ data: options.data,
433
+ confidence: options.confidence,
434
+ correlationSessionId: options.correlationSessionId,
356
435
  });
436
+ const payload = this.protoRegistry.encodeKnownPayload('', 'Signal', signalPayload);
357
437
  const envelope = (0, envelope_1.buildEnvelope)({
358
438
  mode: '',
359
439
  messageType: 'Signal',
@@ -366,13 +446,14 @@ class MacpClient {
366
446
  async sendProgress(options) {
367
447
  const auth = this.requireAuth(options.auth);
368
448
  (0, auth_1.assertSenderMatchesIdentity)(auth, options.sender);
369
- const payload = this.protoRegistry.encodeKnownPayload('', 'Progress', {
449
+ const progressPayload = (0, envelope_1.buildProgressPayload)({
370
450
  progressToken: options.progressToken,
371
451
  progress: options.progress,
372
452
  total: options.total,
373
- message: options.message ?? '',
374
- targetMessageId: options.targetMessageId ?? '',
453
+ message: options.message,
454
+ targetMessageId: options.targetMessageId,
375
455
  });
456
+ const payload = this.protoRegistry.encodeKnownPayload('', 'Progress', progressPayload);
376
457
  const envelope = (0, envelope_1.buildEnvelope)({
377
458
  mode: options.mode ?? '',
378
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';
@@ -1,4 +1,4 @@
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;
@@ -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
@@ -8,6 +8,9 @@ exports.buildSessionStartPayload = buildSessionStartPayload;
8
8
  exports.inferOutcomePositive = inferOutcomePositive;
9
9
  exports.buildCommitmentPayload = buildCommitmentPayload;
10
10
  exports.buildRoot = buildRoot;
11
+ exports.buildSignalPayload = buildSignalPayload;
12
+ exports.buildProgressPayload = buildProgressPayload;
13
+ exports.serializeMessage = serializeMessage;
11
14
  exports.toProtoPayload = toProtoPayload;
12
15
  exports.buildEnvelope = buildEnvelope;
13
16
  const node_crypto_1 = require("node:crypto");
@@ -59,6 +62,43 @@ function buildCommitmentPayload(input) {
59
62
  function buildRoot(uri, name = '') {
60
63
  return { uri, name };
61
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
+ }
62
102
  /**
63
103
  * Type-erasure helper for protobuf payload encoders.
64
104
  *