macp-sdk-typescript 0.3.0 → 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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,58 @@ All notable changes to `macp-sdk-typescript` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
5
  project uses [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.4.0] - 2026-06-22
8
+
9
+ Adopts `@multiagentcoordinationprotocol/proto` 0.1.3 — session
10
+ suspend/resume, explicit cancellation, and cross-session commitment
11
+ supersession. Plan: `plans/macp-proto-0.1.3-suspend-cancel-supersede.md`.
12
+
13
+ ### Added
14
+
15
+ - **`MacpClient.suspendSession(sessionId, reason, options?)` /
16
+ `resumeSession(...)`** — control-plane RPCs for the new non-terminal
17
+ `SUSPENDED` state. Suspension banks the session's remaining TTL and
18
+ causes the runtime to reject messages until resume restores `OPEN`.
19
+ Mirrors `cancelSession`'s generic-invoke pattern and NACK handling.
20
+ - **`suspend()` / `resume()` on every session helper** (`DecisionSession`,
21
+ `ProposalSession`, `TaskSession`, `HandoffSession`, `QuorumSession`, and
22
+ `BaseSession`) — delegate to the new client RPCs with `raiseOnNack: true`.
23
+ - **`SessionState` string-union type** (`src/types.ts`) — mirrors the proto
24
+ `SessionState` enum and now types `Ack.sessionState` and
25
+ `SessionMetadata.state`. Adds `SESSION_STATE_SUSPENDED` and
26
+ `SESSION_STATE_CANCELLED`.
27
+ - **`CommitmentRef` type + `supersedes?` on `CommitmentPayload`** — supports
28
+ cross-session commitment supersession (RFC-MACP-0001 §7.3).
29
+ `buildCommitmentPayload({ ..., supersedes })` attaches it; absent by default.
30
+ - **Three new `SessionLifecycleEventType` values** — `EVENT_TYPE_SUSPENDED`,
31
+ `EVENT_TYPE_RESUMED`, `EVENT_TYPE_CANCELLED`. `SessionLifecycleWatcher`
32
+ surfaces them unchanged; exhaustive `switch (event.eventType)` blocks now
33
+ type-check against the widened union.
34
+ - **`buildCommitmentRef({ sessionId, commitmentHash })`** in `src/envelope.ts`
35
+ — convenience builder for a `CommitmentRef` to pass as
36
+ `buildCommitmentPayload({ supersedes })`. Parity with python-sdk
37
+ `build_commitment_ref`.
38
+ - **Session lifecycle predicates** in `src/watchers.ts` —
39
+ `isSessionCreated` / `isSessionResolved` / `isSessionExpired` /
40
+ `isSessionCancelled` / `isSessionSuspended` / `isSessionResumed` /
41
+ `isTerminalSessionLifecycleEvent`, plus the
42
+ `TERMINAL_SESSION_LIFECYCLE_EVENT_TYPES` constant. They accept either a
43
+ `SessionLifecycleEvent` or a bare `SessionLifecycleEventType`, mirror
44
+ python-sdk's `SessionLifecycle` properties, and treat `RESOLVED` / `EXPIRED`
45
+ / `CANCELLED` as terminal.
46
+
47
+ ### Changed
48
+
49
+ - **`cancelSession` now resolves to `SESSION_STATE_CANCELLED`** (was
50
+ `SESSION_STATE_EXPIRED`). The terminal cancellation state is distinct from
51
+ TTL/policy expiry so consumers can tell an explicit cancel apart from an
52
+ expiry. No change to the `cancelSession` call signature.
53
+ - **Policy `require_vote_quorum` parity**: `serializeCommitment` now emits
54
+ `require_vote_quorum` for **every** mode's commitment block (quorum /
55
+ proposal / task / handoff), not just decision, so policy JSON is
56
+ byte-identical to python-sdk's `_commitment_dict` across all modes.
57
+ - **Dependency**: bumped `@multiagentcoordinationprotocol/proto` to `^0.1.3`.
58
+
7
59
  ## [0.3.0] - 2026-04-21
8
60
 
9
61
  Parity release — brings TypeScript SDK to full feature parity with
package/README.md CHANGED
@@ -62,10 +62,13 @@ client.close();
62
62
 
63
63
  ## Architecture
64
64
 
65
- The SDK uses a **two-layer** design:
65
+ The SDK uses a **three-layer** design:
66
66
 
67
- - **Low-level transport** (`MacpClient`): gRPC connection to the runtime with all 14 RPCs — `initialize`, `send`, `openStream`, `getSession`, `cancelSession`, `getManifest`, `listModes`, `listRoots`, `listExtModes`, `registerExtMode`, `unregisterExtMode`, `promoteMode`, plus streaming watchers.
68
- - **High-level session helpers**: One class per coordination mode (`DecisionSession`, `ProposalSession`, `TaskSession`, `HandoffSession`, `QuorumSession`). Each wraps `MacpClient`, builds envelopes, encodes payloads, and maintains a local state projection.
67
+ - **Low-level transport** (`MacpClient`): typed wrappers around every `MACPRuntimeService` RPC — `initialize`, `send`, `openStream`, `getSession`, `cancelSession`, `suspendSession`, `resumeSession`, `listSessions`, `getManifest`, `listModes`, `listRoots`, `listExtModes`, `registerExtMode`, `unregisterExtMode`, `promoteMode`, the policy RPCs (`register`/`unregister`/`get`/`listPolicies`), plus streaming watchers (`ModeRegistryWatcher`, `RootsWatcher`, `SignalWatcher`, `PolicyWatcher`, `SessionLifecycleWatcher`).
68
+ - **High-level session helpers**: one class per coordination mode (`DecisionSession`, `ProposalSession`, `TaskSession`, `HandoffSession`, `QuorumSession`) — wraps `MacpClient`, builds envelopes, encodes payloads, and maintains a local state projection.
69
+ - **Agent framework** (`src/agent/`): event-driven `Participant` with strategy-based handlers, bootstrap runner (`fromBootstrap`), gRPC/HTTP transports, and the optional cancel-callback HTTP server.
70
+
71
+ See [`docs/guides/architecture.md`](docs/guides/architecture.md) for the full map.
69
72
 
70
73
  Every session class follows the same pattern:
71
74
  1. Construct with a `MacpClient` and options
@@ -262,6 +265,35 @@ Bootstrap files can include an `initiator` section for participants that start t
262
265
  }
263
266
  ```
264
267
 
268
+ ### Strategies
269
+
270
+ Composable handler factories for Decision Mode — register them directly on a
271
+ `Participant`:
272
+
273
+ ```typescript
274
+ participant
275
+ .on('Proposal', agent.evaluationHandler(agent.functionEvaluator(async (p) => ({
276
+ recommendation: 'APPROVE', confidence: 0.9, reason: 'looks good',
277
+ }))))
278
+ .on('Evaluation', agent.votingHandler(agent.majorityVoter({ positiveThreshold: 0.6 })))
279
+ .on('Vote', agent.commitmentHandler(agent.majorityCommitter({ quorumSize: 2 })));
280
+ ```
281
+
282
+ Each layer has a prebuilt and a function-wrapper form: `functionEvaluator`,
283
+ `functionVoter`, `functionCommitter` (zero-ceremony wrappers) next to
284
+ `majorityVoter` / `majorityCommitter` (batteries-included defaults). Full
285
+ reference: [`docs/api/strategies.md`](docs/api/strategies.md).
286
+
287
+ ### Cancel Callback
288
+
289
+ Long-running agents can expose an HTTP endpoint that an orchestrator POSTs to
290
+ in order to request a clean shutdown (RFC-MACP-0001 §7.2 Option A). Bootstrap
291
+ JSON with a `cancel_callback: { host, port, path }` block auto-wires the
292
+ server via `fromBootstrap()`; for the manual path use
293
+ `startCancelCallbackServer(...)` + `Participant.attachCancelCallbackServer(...)`.
294
+ See [`docs/api/cancel-callback.md`](docs/api/cancel-callback.md) and
295
+ [`examples/cancel-callback.ts`](examples/cancel-callback.ts).
296
+
265
297
  ## Authentication
266
298
 
267
299
  ```typescript
@@ -60,5 +60,16 @@ export declare abstract class BaseSession<P extends BaseProjection> {
60
60
  metadata: SessionMetadata;
61
61
  }>;
62
62
  cancel(reason?: string, auth?: AuthConfig): Promise<Ack>;
63
+ /**
64
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: TTL
65
+ * is banked and messages are rejected until {@link resume}. See
66
+ * {@link MacpClient.suspendSession}.
67
+ */
68
+ suspend(reason?: string, auth?: AuthConfig): Promise<Ack>;
69
+ /**
70
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
71
+ * restoring OPEN and the banked TTL. See {@link MacpClient.resumeSession}.
72
+ */
73
+ resume(reason?: string, auth?: AuthConfig): Promise<Ack>;
63
74
  openStream(auth?: AuthConfig): MacpStream;
64
75
  }
@@ -105,6 +105,21 @@ class BaseSession {
105
105
  cancel(reason = '', auth) {
106
106
  return this.client.cancelSession(this.sessionId, reason, { auth: auth ?? this.auth });
107
107
  }
108
+ /**
109
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: TTL
110
+ * is banked and messages are rejected until {@link resume}. See
111
+ * {@link MacpClient.suspendSession}.
112
+ */
113
+ suspend(reason = '', auth) {
114
+ return this.client.suspendSession(this.sessionId, reason, { auth: auth ?? this.auth });
115
+ }
116
+ /**
117
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
118
+ * restoring OPEN and the banked TTL. See {@link MacpClient.resumeSession}.
119
+ */
120
+ resume(reason = '', auth) {
121
+ return this.client.resumeSession(this.sessionId, reason, { auth: auth ?? this.auth });
122
+ }
108
123
  openStream(auth) {
109
124
  return this.client.openStream({ auth: auth ?? this.auth });
110
125
  }
package/dist/client.d.ts CHANGED
@@ -83,12 +83,46 @@ export declare class MacpClient {
83
83
  }): Promise<{
84
84
  metadata: SessionMetadata;
85
85
  }>;
86
+ /**
87
+ * Terminate a session via the `CancelSession` control-plane RPC.
88
+ *
89
+ * As of proto 0.1.3 the resulting `Ack.sessionState` is
90
+ * `SESSION_STATE_CANCELLED` (previously `SESSION_STATE_EXPIRED`) — distinct
91
+ * from TTL/policy expiry so consumers can tell explicit cancellation apart.
92
+ */
86
93
  cancelSession(sessionId: string, reason: string, options?: {
87
94
  auth?: AuthConfig;
88
95
  deadlineMs?: number;
89
96
  raiseOnNack?: boolean;
90
97
  cancelledBy?: string;
91
98
  }): Promise<Ack>;
99
+ /**
100
+ * Pause a session via the `SuspendSession` control-plane RPC (proto 0.1.3+).
101
+ *
102
+ * Suspension is non-terminal: the resulting `Ack.sessionState` is
103
+ * `SESSION_STATE_SUSPENDED`, the session's remaining TTL is banked, and
104
+ * messages sent to a suspended session are rejected until {@link resumeSession}
105
+ * restores it to `SESSION_STATE_OPEN`. Restricted to the initiator and
106
+ * policy-delegated roles, mirroring {@link cancelSession}.
107
+ */
108
+ suspendSession(sessionId: string, reason: string, options?: {
109
+ auth?: AuthConfig;
110
+ deadlineMs?: number;
111
+ raiseOnNack?: boolean;
112
+ }): Promise<Ack>;
113
+ /**
114
+ * Resume a suspended session via the `ResumeSession` control-plane RPC
115
+ * (proto 0.1.3+).
116
+ *
117
+ * Restores `SESSION_STATE_OPEN` and adds the banked TTL back to the session's
118
+ * absolute deadline. Restricted to the initiator and policy-delegated roles,
119
+ * mirroring {@link suspendSession}.
120
+ */
121
+ resumeSession(sessionId: string, reason: string, options?: {
122
+ auth?: AuthConfig;
123
+ deadlineMs?: number;
124
+ raiseOnNack?: boolean;
125
+ }): Promise<Ack>;
92
126
  getManifest(agentId?: string, deadlineMs?: number): Promise<{
93
127
  manifest: AgentManifest;
94
128
  }>;
package/dist/client.js CHANGED
@@ -319,6 +319,13 @@ class MacpClient {
319
319
  const auth = this.requireAuth(options?.auth);
320
320
  return this.unary('GetSession', { sessionId }, auth, options?.deadlineMs);
321
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
+ */
322
329
  async cancelSession(sessionId, reason, options) {
323
330
  const auth = this.requireAuth(options?.auth);
324
331
  const request = { sessionId, reason };
@@ -330,6 +337,39 @@ class MacpClient {
330
337
  throw new errors_1.MacpAckError(ack ?? {});
331
338
  return ack;
332
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
+ }
333
373
  async getManifest(agentId = '', deadlineMs) {
334
374
  return this.unary('GetManifest', { agentId }, undefined, deadlineMs);
335
375
  }
@@ -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, ProgressPayload, Root, SessionStartPayload, SignalPayload } 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,6 +33,12 @@ 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;
29
44
  export declare function buildSignalPayload(input: {
package/dist/envelope.js CHANGED
@@ -6,6 +6,7 @@ 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;
11
12
  exports.buildSignalPayload = buildSignalPayload;
@@ -47,8 +48,16 @@ function inferOutcomePositive(action) {
47
48
  return false;
48
49
  return true;
49
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
+ }
50
59
  function buildCommitmentPayload(input) {
51
- return {
60
+ const payload = {
52
61
  commitmentId: input.commitmentId ?? newCommitmentId(),
53
62
  action: input.action,
54
63
  authorityScope: input.authorityScope,
@@ -58,6 +67,9 @@ function buildCommitmentPayload(input) {
58
67
  policyVersion: input.policyVersion ?? constants_1.DEFAULT_POLICY_VERSION,
59
68
  outcomePositive: input.outcomePositive ?? inferOutcomePositive(input.action),
60
69
  };
70
+ if (input.supersedes)
71
+ payload.supersedes = input.supersedes;
72
+ return payload;
61
73
  }
62
74
  function buildRoot(uri, name = '') {
63
75
  return { uri, name };
package/dist/handoff.d.ts CHANGED
@@ -61,6 +61,16 @@ export declare class HandoffSession {
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/handoff.js CHANGED
@@ -138,6 +138,26 @@ class HandoffSession {
138
138
  raiseOnNack: true,
139
139
  });
140
140
  }
141
+ /**
142
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: the
143
+ * runtime banks remaining TTL and rejects messages until {@link resume}.
144
+ */
145
+ async suspend(reason = '', auth) {
146
+ return this.client.suspendSession(this.sessionId, reason, {
147
+ auth: auth ?? this.auth,
148
+ raiseOnNack: true,
149
+ });
150
+ }
151
+ /**
152
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
153
+ * restoring `SESSION_STATE_OPEN` and the banked TTL.
154
+ */
155
+ async resume(reason = '', auth) {
156
+ return this.client.resumeSession(this.sessionId, reason, {
157
+ auth: auth ?? this.auth,
158
+ raiseOnNack: true,
159
+ });
160
+ }
141
161
  openStream(auth) {
142
162
  return this.client.openStream({ auth: auth ?? this.auth });
143
163
  }
package/dist/policy.js CHANGED
@@ -10,6 +10,9 @@ function serializeCommitment(commitment) {
10
10
  return {
11
11
  authority: commitment?.authority ?? 'initiator_only',
12
12
  designated_roles: commitment?.designatedRoles ?? [],
13
+ // Parity with python-sdk `_commitment_dict`: emitted for every mode, not
14
+ // just decision, so policy JSON is byte-identical across SDKs.
15
+ require_vote_quorum: commitment?.requireVoteQuorum ?? false,
13
16
  };
14
17
  }
15
18
  function buildDecisionPolicy(policyId, description, rules) {
@@ -28,11 +31,7 @@ function buildDecisionPolicy(policyId, description, rules) {
28
31
  minimum_confidence: rules.evaluation?.minimumConfidence ?? 0,
29
32
  required_before_voting: rules.evaluation?.requiredBeforeVoting ?? false,
30
33
  },
31
- commitment: {
32
- authority: rules.commitment?.authority ?? 'initiator_only',
33
- designated_roles: rules.commitment?.designatedRoles ?? [],
34
- require_vote_quorum: rules.commitment?.requireVoteQuorum ?? false,
35
- },
34
+ commitment: serializeCommitment(rules.commitment),
36
35
  };
37
36
  return {
38
37
  policyId,
@@ -65,6 +65,16 @@ export declare class ProposalSession {
65
65
  metadata: SessionMetadata;
66
66
  }>;
67
67
  cancel(reason?: string, auth?: AuthConfig): Promise<Ack>;
68
+ /**
69
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: the
70
+ * runtime banks remaining TTL and rejects messages until {@link resume}.
71
+ */
72
+ suspend(reason?: string, auth?: AuthConfig): Promise<Ack>;
73
+ /**
74
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
75
+ * restoring `SESSION_STATE_OPEN` and the banked TTL.
76
+ */
77
+ resume(reason?: string, auth?: AuthConfig): Promise<Ack>;
68
78
  openStream(auth?: AuthConfig): MacpStream;
69
79
  }
70
80
  export {};
package/dist/proposal.js CHANGED
@@ -149,6 +149,26 @@ class ProposalSession {
149
149
  raiseOnNack: true,
150
150
  });
151
151
  }
152
+ /**
153
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: the
154
+ * runtime banks remaining TTL and rejects messages until {@link resume}.
155
+ */
156
+ async suspend(reason = '', auth) {
157
+ return this.client.suspendSession(this.sessionId, reason, {
158
+ auth: auth ?? this.auth,
159
+ raiseOnNack: true,
160
+ });
161
+ }
162
+ /**
163
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
164
+ * restoring `SESSION_STATE_OPEN` and the banked TTL.
165
+ */
166
+ async resume(reason = '', auth) {
167
+ return this.client.resumeSession(this.sessionId, reason, {
168
+ auth: auth ?? this.auth,
169
+ raiseOnNack: true,
170
+ });
171
+ }
152
172
  openStream(auth) {
153
173
  return this.client.openStream({ auth: auth ?? this.auth });
154
174
  }
package/dist/quorum.d.ts CHANGED
@@ -61,6 +61,16 @@ export declare class QuorumSession {
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/quorum.js CHANGED
@@ -138,6 +138,26 @@ class QuorumSession {
138
138
  raiseOnNack: true,
139
139
  });
140
140
  }
141
+ /**
142
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: the
143
+ * runtime banks remaining TTL and rejects messages until {@link resume}.
144
+ */
145
+ async suspend(reason = '', auth) {
146
+ return this.client.suspendSession(this.sessionId, reason, {
147
+ auth: auth ?? this.auth,
148
+ raiseOnNack: true,
149
+ });
150
+ }
151
+ /**
152
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
153
+ * restoring `SESSION_STATE_OPEN` and the banked TTL.
154
+ */
155
+ async resume(reason = '', auth) {
156
+ return this.client.resumeSession(this.sessionId, reason, {
157
+ auth: auth ?? this.auth,
158
+ raiseOnNack: true,
159
+ });
160
+ }
141
161
  openStream(auth) {
142
162
  return this.client.openStream({ auth: auth ?? this.auth });
143
163
  }
package/dist/task.d.ts CHANGED
@@ -69,6 +69,16 @@ export declare class TaskSession {
69
69
  metadata: SessionMetadata;
70
70
  }>;
71
71
  cancel(reason?: string, auth?: AuthConfig): Promise<Ack>;
72
+ /**
73
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: the
74
+ * runtime banks remaining TTL and rejects messages until {@link resume}.
75
+ */
76
+ suspend(reason?: string, auth?: AuthConfig): Promise<Ack>;
77
+ /**
78
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
79
+ * restoring `SESSION_STATE_OPEN` and the banked TTL.
80
+ */
81
+ resume(reason?: string, auth?: AuthConfig): Promise<Ack>;
72
82
  openStream(auth?: AuthConfig): MacpStream;
73
83
  }
74
84
  export {};
package/dist/task.js CHANGED
@@ -160,6 +160,26 @@ class TaskSession {
160
160
  raiseOnNack: true,
161
161
  });
162
162
  }
163
+ /**
164
+ * Pause this session via `SuspendSession` (proto 0.1.3+). Non-terminal: the
165
+ * runtime banks remaining TTL and rejects messages until {@link resume}.
166
+ */
167
+ async suspend(reason = '', auth) {
168
+ return this.client.suspendSession(this.sessionId, reason, {
169
+ auth: auth ?? this.auth,
170
+ raiseOnNack: true,
171
+ });
172
+ }
173
+ /**
174
+ * Resume this suspended session via `ResumeSession` (proto 0.1.3+),
175
+ * restoring `SESSION_STATE_OPEN` and the banked TTL.
176
+ */
177
+ async resume(reason = '', auth) {
178
+ return this.client.resumeSession(this.sessionId, reason, {
179
+ auth: auth ?? this.auth,
180
+ raiseOnNack: true,
181
+ });
182
+ }
163
183
  openStream(auth) {
164
184
  return this.client.openStream({ auth: auth ?? this.auth });
165
185
  }
package/dist/types.d.ts CHANGED
@@ -19,13 +19,24 @@ export interface MacpErrorShape {
19
19
  messageId?: string;
20
20
  details?: Buffer;
21
21
  }
22
+ /**
23
+ * Lifecycle state of a session. Mirrors the `SessionState` enum in
24
+ * `macp/v1/envelope.proto`.
25
+ *
26
+ * `SESSION_STATE_SUSPENDED` is a non-terminal pause (TTL is banked while
27
+ * suspended and restored on resume); `SESSION_STATE_CANCELLED` is the terminal
28
+ * state for an explicit `CancelSession` — distinct from `SESSION_STATE_EXPIRED`
29
+ * (TTL / deterministic runtime policy) so consumers can tell explicit
30
+ * cancellation from expiry.
31
+ */
32
+ export type SessionState = 'SESSION_STATE_UNSPECIFIED' | 'SESSION_STATE_OPEN' | 'SESSION_STATE_RESOLVED' | 'SESSION_STATE_EXPIRED' | 'SESSION_STATE_SUSPENDED' | 'SESSION_STATE_CANCELLED';
22
33
  export interface Ack {
23
34
  ok?: boolean;
24
35
  duplicate?: boolean;
25
36
  messageId?: string;
26
37
  sessionId?: string;
27
38
  acceptedAtUnixMs?: string;
28
- sessionState?: string;
39
+ sessionState?: SessionState;
29
40
  error?: MacpErrorShape;
30
41
  }
31
42
  export interface ParticipantActivity {
@@ -36,7 +47,7 @@ export interface ParticipantActivity {
36
47
  export interface SessionMetadata {
37
48
  sessionId?: string;
38
49
  mode?: string;
39
- state?: string;
50
+ state?: SessionState;
40
51
  startedAtUnixMs?: string;
41
52
  expiresAtUnixMs?: string;
42
53
  modeVersion?: string;
@@ -99,6 +110,15 @@ export interface SessionStartPayload {
99
110
  extensions?: Record<string, Buffer>;
100
111
  roots?: Root[];
101
112
  }
113
+ /**
114
+ * Reference to a specific accepted commitment, used for cross-session
115
+ * supersession (RFC-MACP-0001 §7.3). Mirrors `macp.v1.CommitmentRef`.
116
+ */
117
+ export interface CommitmentRef {
118
+ sessionId: string;
119
+ /** Content hash of the superseded `CommitmentPayload`. */
120
+ commitmentHash: string;
121
+ }
102
122
  export interface CommitmentPayload {
103
123
  commitmentId: string;
104
124
  action: string;
@@ -108,6 +128,13 @@ export interface CommitmentPayload {
108
128
  policyVersion?: string;
109
129
  configurationVersion: string;
110
130
  outcomePositive?: boolean;
131
+ /**
132
+ * Cross-session supersession (RFC-MACP-0001 §7.3): when present, this
133
+ * commitment supersedes a prior commitment identified by
134
+ * `{sessionId, commitmentHash}`. The superseding commitment lives in a NEW
135
+ * session — a RESOLVED session accepts no further messages.
136
+ */
137
+ supersedes?: CommitmentRef;
111
138
  }
112
139
  export interface SignalPayload {
113
140
  signalType: string;
@@ -264,7 +291,7 @@ export interface RegistryChanged {
264
291
  export interface RootsChanged {
265
292
  observedAtUnixMs: string;
266
293
  }
267
- export type SessionLifecycleEventType = 'EVENT_TYPE_UNSPECIFIED' | 'EVENT_TYPE_CREATED' | 'EVENT_TYPE_RESOLVED' | 'EVENT_TYPE_EXPIRED';
294
+ export type SessionLifecycleEventType = 'EVENT_TYPE_UNSPECIFIED' | 'EVENT_TYPE_CREATED' | 'EVENT_TYPE_RESOLVED' | 'EVENT_TYPE_EXPIRED' | 'EVENT_TYPE_SUSPENDED' | 'EVENT_TYPE_RESUMED' | 'EVENT_TYPE_CANCELLED';
268
295
  export interface SessionLifecycleEvent {
269
296
  eventType: SessionLifecycleEventType;
270
297
  session: SessionMetadata;
@@ -1,6 +1,28 @@
1
1
  import type { AuthConfig } from './auth';
2
2
  import type { MacpClient } from './client';
3
- import type { Envelope, PolicyDescriptor, RegistryChanged, RootsChanged, SessionLifecycleEvent } from './types';
3
+ import type { Envelope, PolicyDescriptor, RegistryChanged, RootsChanged, SessionLifecycleEvent, SessionLifecycleEventType } from './types';
4
+ /**
5
+ * The lifecycle event types after which a session emits no further events.
6
+ * `EVENT_TYPE_CANCELLED` (proto 0.1.3) is terminal alongside `RESOLVED` and
7
+ * `EXPIRED`; `SUSPENDED` / `RESUMED` are non-terminal. Parity with python-sdk
8
+ * `SessionLifecycle.is_terminal`.
9
+ */
10
+ export declare const TERMINAL_SESSION_LIFECYCLE_EVENT_TYPES: readonly SessionLifecycleEventType[];
11
+ type LifecycleEventOrType = SessionLifecycleEvent | SessionLifecycleEventType;
12
+ /**
13
+ * Predicates over a {@link SessionLifecycleEvent} (or a bare
14
+ * {@link SessionLifecycleEventType}), mirroring python-sdk's `SessionLifecycle`
15
+ * properties so consumers can classify lifecycle events without re-deriving the
16
+ * proto enum strings. {@link isTerminalSessionLifecycleEvent} deliberately
17
+ * includes `EVENT_TYPE_CANCELLED`.
18
+ */
19
+ export declare const isSessionCreated: (e: LifecycleEventOrType) => boolean;
20
+ export declare const isSessionResolved: (e: LifecycleEventOrType) => boolean;
21
+ export declare const isSessionExpired: (e: LifecycleEventOrType) => boolean;
22
+ export declare const isSessionCancelled: (e: LifecycleEventOrType) => boolean;
23
+ export declare const isSessionSuspended: (e: LifecycleEventOrType) => boolean;
24
+ export declare const isSessionResumed: (e: LifecycleEventOrType) => boolean;
25
+ export declare const isTerminalSessionLifecycleEvent: (e: LifecycleEventOrType) => boolean;
4
26
  export declare class ModeRegistryWatcher {
5
27
  private readonly client;
6
28
  private readonly auth?;
@@ -67,3 +89,4 @@ export declare class SessionLifecycleWatcher {
67
89
  */
68
90
  nextEvent(): Promise<SessionLifecycleEvent>;
69
91
  }
92
+ export {};
package/dist/watchers.js CHANGED
@@ -1,7 +1,42 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SessionLifecycleWatcher = exports.PolicyWatcher = exports.SignalWatcher = exports.RootsWatcher = exports.ModeRegistryWatcher = void 0;
3
+ exports.SessionLifecycleWatcher = exports.PolicyWatcher = exports.SignalWatcher = exports.RootsWatcher = exports.ModeRegistryWatcher = exports.isTerminalSessionLifecycleEvent = exports.isSessionResumed = exports.isSessionSuspended = exports.isSessionCancelled = exports.isSessionExpired = exports.isSessionResolved = exports.isSessionCreated = exports.TERMINAL_SESSION_LIFECYCLE_EVENT_TYPES = void 0;
4
4
  const logging_1 = require("./logging");
5
+ /**
6
+ * The lifecycle event types after which a session emits no further events.
7
+ * `EVENT_TYPE_CANCELLED` (proto 0.1.3) is terminal alongside `RESOLVED` and
8
+ * `EXPIRED`; `SUSPENDED` / `RESUMED` are non-terminal. Parity with python-sdk
9
+ * `SessionLifecycle.is_terminal`.
10
+ */
11
+ exports.TERMINAL_SESSION_LIFECYCLE_EVENT_TYPES = [
12
+ 'EVENT_TYPE_RESOLVED',
13
+ 'EVENT_TYPE_EXPIRED',
14
+ 'EVENT_TYPE_CANCELLED',
15
+ ];
16
+ function lifecycleEventType(e) {
17
+ return typeof e === 'string' ? e : e.eventType;
18
+ }
19
+ /**
20
+ * Predicates over a {@link SessionLifecycleEvent} (or a bare
21
+ * {@link SessionLifecycleEventType}), mirroring python-sdk's `SessionLifecycle`
22
+ * properties so consumers can classify lifecycle events without re-deriving the
23
+ * proto enum strings. {@link isTerminalSessionLifecycleEvent} deliberately
24
+ * includes `EVENT_TYPE_CANCELLED`.
25
+ */
26
+ const isSessionCreated = (e) => lifecycleEventType(e) === 'EVENT_TYPE_CREATED';
27
+ exports.isSessionCreated = isSessionCreated;
28
+ const isSessionResolved = (e) => lifecycleEventType(e) === 'EVENT_TYPE_RESOLVED';
29
+ exports.isSessionResolved = isSessionResolved;
30
+ const isSessionExpired = (e) => lifecycleEventType(e) === 'EVENT_TYPE_EXPIRED';
31
+ exports.isSessionExpired = isSessionExpired;
32
+ const isSessionCancelled = (e) => lifecycleEventType(e) === 'EVENT_TYPE_CANCELLED';
33
+ exports.isSessionCancelled = isSessionCancelled;
34
+ const isSessionSuspended = (e) => lifecycleEventType(e) === 'EVENT_TYPE_SUSPENDED';
35
+ exports.isSessionSuspended = isSessionSuspended;
36
+ const isSessionResumed = (e) => lifecycleEventType(e) === 'EVENT_TYPE_RESUMED';
37
+ exports.isSessionResumed = isSessionResumed;
38
+ const isTerminalSessionLifecycleEvent = (e) => exports.TERMINAL_SESSION_LIFECYCLE_EVENT_TYPES.includes(lifecycleEventType(e));
39
+ exports.isTerminalSessionLifecycleEvent = isTerminalSessionLifecycleEvent;
5
40
  function serverStreamToAsyncGenerator(stream) {
6
41
  const queue = [];
7
42
  let resolve = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "macp-sdk-typescript",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "TypeScript SDK for the MACP Rust runtime",
5
5
  "license": "Apache-2.0",
6
6
  "type": "commonjs",
@@ -55,7 +55,7 @@
55
55
  "dependencies": {
56
56
  "@grpc/grpc-js": "^1.14.3",
57
57
  "@grpc/proto-loader": "^0.8.0",
58
- "@multiagentcoordinationprotocol/proto": "^0.1.2",
58
+ "@multiagentcoordinationprotocol/proto": "^0.1.3",
59
59
  "protobufjs": "^8.0.0"
60
60
  },
61
61
  "devDependencies": {