macp-sdk-typescript 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +176 -1
  2. package/README.md +49 -6
  3. package/dist/agent/cancel-callback.d.ts +23 -0
  4. package/dist/agent/cancel-callback.js +108 -0
  5. package/dist/agent/index.d.ts +1 -0
  6. package/dist/agent/index.js +1 -0
  7. package/dist/agent/participant.d.ts +39 -0
  8. package/dist/agent/participant.js +114 -45
  9. package/dist/agent/runner.d.ts +13 -0
  10. package/dist/agent/runner.js +34 -0
  11. package/dist/agent/strategies.d.ts +2 -0
  12. package/dist/agent/strategies.js +8 -0
  13. package/dist/agent/transports.js +5 -0
  14. package/dist/base-session.d.ts +75 -0
  15. package/dist/base-session.js +127 -0
  16. package/dist/client.d.ts +54 -0
  17. package/dist/client.js +130 -9
  18. package/dist/constants.d.ts +21 -16
  19. package/dist/constants.js +24 -17
  20. package/dist/decision.d.ts +10 -0
  21. package/dist/decision.js +20 -0
  22. package/dist/envelope.d.ts +38 -1
  23. package/dist/envelope.js +53 -1
  24. package/dist/errors.d.ts +17 -4
  25. package/dist/errors.js +19 -10
  26. package/dist/handoff.d.ts +10 -8
  27. package/dist/handoff.js +20 -16
  28. package/dist/index.d.ts +3 -1
  29. package/dist/index.js +10 -2
  30. package/dist/logging.d.ts +21 -0
  31. package/dist/logging.js +64 -0
  32. package/dist/policy.d.ts +47 -42
  33. package/dist/policy.js +5 -6
  34. package/dist/projections/base.d.ts +20 -0
  35. package/dist/projections/base.js +37 -0
  36. package/dist/projections.d.ts +1 -0
  37. package/dist/projections.js +1 -0
  38. package/dist/proposal.d.ts +10 -0
  39. package/dist/proposal.js +20 -0
  40. package/dist/quorum.d.ts +10 -0
  41. package/dist/quorum.js +20 -0
  42. package/dist/task.d.ts +14 -4
  43. package/dist/task.js +24 -4
  44. package/dist/types.d.ts +30 -3
  45. package/dist/watchers.d.ts +37 -2
  46. package/dist/watchers.js +65 -5
  47. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -4,15 +4,190 @@ 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
- ## [Unreleased]
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
+
59
+ ## [0.3.0] - 2026-04-21
60
+
61
+ Parity release — brings TypeScript SDK to full feature parity with
62
+ `macp-sdk-python` 0.3.0. Plan: `plans/sdk-parity-plan.md`.
63
+
64
+ ### Added
65
+
66
+ - **Cancel-callback server** (Gap B, RFC-0001 §7.2 Option A):
67
+ `startCancelCallbackServer({ host, port, path, onCancel })` returns a
68
+ handle backed by Node's `http` module; `Participant` starts the server
69
+ automatically when `ParticipantConfig.cancelCallback` is set and closes
70
+ it on `stop()`. `fromBootstrap()` wires the new `cancel_callback`
71
+ field in the bootstrap JSON. Exports:
72
+ `startCancelCallbackServer`, `CancelCallbackServer`, `CancelHandler`.
73
+ - **Function-wrapped voting and commitment strategies** (Gap C):
74
+ `functionVoter(shouldVote, decideVote)` and
75
+ `functionCommitter(shouldCommit, decideCommitment)` in
76
+ `src/agent/strategies.ts` — the low-friction way to plug a custom rule
77
+ into a `Participant` without a class.
78
+ - **Standalone signal and progress builders** (Gap D):
79
+ `buildSignalPayload(input)` and `buildProgressPayload(input)` in
80
+ `src/envelope.ts`. `MacpClient.sendSignal` / `sendProgress` now use
81
+ the shared builders internally.
82
+ - **`serializeMessage(msg)` helper** (Gap N) — parity with
83
+ `python-sdk envelope.serialize_message`; dispatches to
84
+ `serializeBinary()` / `toBinary()` / `finish()` on the passed
85
+ protobuf message.
86
+ - **Structured logger** (Gap G): `src/logging.ts` exports `logger` with
87
+ `error`/`warn`/`info`/`debug` level methods and `configureLogging({ level, sink })`.
88
+ Reads `MACP_LOG_LEVEL` on module init (default `warn`). The two
89
+ remaining `console.*` callsites in `src/agent/participant.ts` and
90
+ `src/handoff.ts` now route through the structured logger.
91
+ - **`AckFailure` shape** (Gap I): structured NACK record exported from
92
+ `src/errors.ts`. Populated on `MacpAckError.failure` from both
93
+ `ack.error.details` and gRPC trailing metadata.
94
+ - **`BaseSession` / `BaseProjection`** (Gap J): abstract extension
95
+ points exported from `src/base-session.ts` and `src/projections/base.ts`
96
+ for custom mode helpers (modes registered via `registerExtMode`).
97
+ Mirrors `python-sdk`'s `BaseSession` / `BaseProjection`.
98
+ - **Named policy rule interfaces** (Gap K): `VotingRules`,
99
+ `ObjectionHandlingRules`, `EvaluationRules`, `CommitmentRules`,
100
+ `QuorumThreshold`, `AbstentionRules`, `ProposalAcceptanceRules`,
101
+ `CounterProposalRules`, `RejectionRules`, `TaskAssignmentRules`,
102
+ `TaskCompletionRules`, `HandoffAcceptanceRules` now exported as
103
+ first-class interfaces matching `macp-sdk-python` exports.
104
+ - **`STANDARD_MODES` tuple** (Gap L) in `src/constants.ts`.
105
+ - **Parity examples** (Gap M): `examples/policy-registration.ts`,
106
+ `examples/agent-policy-aware.ts`, `examples/direct-agent-auth-initiator.ts`,
107
+ `examples/direct-agent-auth-observer.ts`.
108
+
109
+ ### Changed
110
+
111
+ - **Error-code constants renamed** (Gap F, breaking): dropped the
112
+ `ERR_` prefix so TS constants match the on-the-wire strings and the
113
+ `macp-sdk-python` exports. Rename table —
114
+ `ERR_UNSUPPORTED_PROTOCOL_VERSION` → `UNSUPPORTED_PROTOCOL_VERSION`,
115
+ `ERR_INVALID_ENVELOPE` → `INVALID_ENVELOPE`,
116
+ `ERR_SESSION_NOT_FOUND` → `SESSION_NOT_FOUND`,
117
+ `ERR_SESSION_NOT_OPEN` → `SESSION_NOT_OPEN`,
118
+ `ERR_SESSION_ALREADY_EXISTS` → `SESSION_ALREADY_EXISTS`,
119
+ `ERR_MODE_NOT_SUPPORTED` → `MODE_NOT_SUPPORTED`,
120
+ `ERR_FORBIDDEN` → `FORBIDDEN`,
121
+ `ERR_UNAUTHENTICATED` → `UNAUTHENTICATED`,
122
+ `ERR_DUPLICATE_MESSAGE` → `DUPLICATE_MESSAGE`,
123
+ `ERR_PAYLOAD_TOO_LARGE` → `PAYLOAD_TOO_LARGE`,
124
+ `ERR_RATE_LIMITED` → `RATE_LIMITED`,
125
+ `ERR_INTERNAL_ERROR` → `INTERNAL_ERROR`,
126
+ `ERR_POLICY_DENIED` → `POLICY_DENIED`,
127
+ `ERR_INVALID_SESSION_ID` → `INVALID_SESSION_ID`,
128
+ `ERR_UNKNOWN_POLICY_VERSION` → `UNKNOWN_POLICY_VERSION`,
129
+ `ERR_INVALID_POLICY_DEFINITION` → `INVALID_POLICY_DEFINITION`.
130
+ - `MacpClient.clientVersion` default now `0.3.0`.
131
+
132
+ ### Removed
133
+
134
+ - **`HandoffSession.sendContext()`** (Gap A, breaking): the deprecated
135
+ alias (warning first emitted in 0.2.3) has been removed. Use
136
+ `HandoffSession.addContext()`.
137
+ - **`MacpAckError.reasons` getter** (breaking): use
138
+ `MacpAckError.failure.reasons` instead. `.failure` is the canonical
139
+ structured NACK record and matches `macp-sdk-python`'s
140
+ `MacpAckError.failure`.
141
+ - **Mode-prefixed policy rule type aliases** (breaking): the
142
+ `CommitmentRulesInput`, `DecisionVotingRules`,
143
+ `DecisionObjectionHandling`, `DecisionEvaluationRules`, and
144
+ `DecisionCommitmentRules` type aliases have been removed. Use the
145
+ unprefixed names (`CommitmentRules`, `VotingRules`,
146
+ `ObjectionHandlingRules`, `EvaluationRules`) introduced in Gap K.
147
+
148
+ ## [0.2.3] - 2026-04-21
8
149
 
9
150
  ### Added
151
+ - `MacpStream.sendSubscribe(sessionId, afterSequence?)` — subscribe-only stream
152
+ frame (RFC-MACP-0006-A1). The runtime replays accepted envelopes from the
153
+ cursor before switching to live broadcast, so non-initiator agents observe
154
+ `SessionStart` and earlier mode envelopes regardless of join order.
155
+ - `GrpcTransportAdapter` now sends a subscribe frame automatically on start so
156
+ `Participant`-based agents pick up history without bespoke wiring.
157
+ - Unit coverage for `MacpStream.sendSubscribe` (default cursor, custom cursor,
158
+ closed-stream rejection, write-error propagation, sequential resubscribe) and
159
+ `GrpcTransportAdapter` (subscribe ordering, empty-stream subscribe, auth
160
+ pass-through to `openStream`).
161
+ - Integration tests for late-subscriber replay and future-cursor skip
162
+ (`tests/integration/runtime.test.ts`).
163
+ - `InitiatorConfig.sessionStart` now accepts `contextId` and `extensions`, and
164
+ `Participant.emitInitiatorEnvelopes()` forwards both to the mode-session
165
+ `start()` call (SDK-TS-1). `fromBootstrap()` decodes `context_id` plus a
166
+ JSON-native `extensions` map from the bootstrap file — each value is
167
+ serialised as UTF-8 JSON into the `Record<string, Buffer>` shape the
168
+ envelope requires; pre-encoded `Buffer` / `Uint8Array` values pass through
169
+ unchanged.
170
+ - Documented `MacpClient.listSessions()` (parity with the python-sdk SDK-PY-2
171
+ gap) and `SessionLifecycleWatcher` (parity with python-sdk SDK-PY-3). Both
172
+ were already implemented — this release adds the API reference, a
173
+ `docs/guides/streaming.md` section, unit coverage for `listSessions`, and
174
+ integration tests that exercise enumeration + a `CREATED` lifecycle event
175
+ round-trip against a live runtime.
10
176
  - Warn-once migration hint when the deprecated `HandoffSession.sendContext()`
11
177
  alias is invoked. Scheduled for removal in `0.4.0`; use `addContext()`.
12
178
  - `docs/api/sessions.md` now documents the identity-guard contract.
13
179
  - `docs/guides/architecture.md` rewritten to reflect the three-layer design
14
180
  (transport / session helpers / agent framework).
15
181
 
182
+ ### Changed
183
+ - Integration tests updated to match the proto field renames that landed in
184
+ `@multiagentcoordinationprotocol/proto@0.1.x`: `Evaluation.analysis` →
185
+ `reason`, `Proposal.description` → `summary`, `TaskRequest.assignee` →
186
+ `requestedAssignee` (Task accept/update/complete/fail/reject now carry an
187
+ `assignee` field), `TaskUpdate.progress` is required, `HandoffContext.data`
188
+ → `context` (+ `contentType`), `HandoffAccept.acceptedBy` and
189
+ `HandoffDecline.declinedBy` are now required.
190
+
16
191
  ### Deprecated
17
192
  - `HandoffSession.sendContext()` remains available as an alias but emits a
18
193
  one-shot `console.warn`. Use `HandoffSession.addContext()` instead.
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
@@ -141,10 +144,10 @@ import { TaskSession } from 'macp-sdk-typescript';
141
144
 
142
145
  const session = new TaskSession(client);
143
146
  await session.start({ intent: '...', participants: ['worker'], ttlMs: 120_000 });
144
- await session.request({ taskId: 't1', title: 'Build feature', instructions: '...' });
147
+ await session.requestTask({ taskId: 't1', title: 'Build feature', instructions: '...' });
145
148
  await session.acceptTask({ taskId: 't1', assignee: 'worker' });
146
- await session.update({ taskId: 't1', status: 'working', progress: 0.5, message: 'halfway' });
147
- await session.complete({ taskId: 't1', assignee: 'worker', summary: 'done' });
149
+ await session.updateTask({ taskId: 't1', status: 'working', progress: 0.5, message: 'halfway' });
150
+ await session.completeTask({ taskId: 't1', assignee: 'worker', summary: 'done' });
148
151
  await session.commit({ action: 'task.completed', authorityScope: 'lead', reason: '...' });
149
152
 
150
153
  // Projection queries
@@ -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
@@ -319,6 +351,17 @@ for await (const envelope of stream.responses()) {
319
351
  stream.close();
320
352
  ```
321
353
 
354
+ To attach to a session that is already in flight and receive accepted history before live broadcast (RFC-MACP-0006 §3.2 passive subscribe):
355
+
356
+ ```typescript
357
+ const stream = client.openStream({ auth });
358
+ await stream.sendSubscribe(sessionId); // full replay then live
359
+ // or, after a reconnect:
360
+ await stream.sendSubscribe(sessionId, lastSeq); // resume from cursor
361
+ ```
362
+
363
+ The agent framework's `GrpcTransportAdapter` calls this for you on start, so non-initiator agents see `SessionStart` regardless of join order.
364
+
322
365
  ### Registry & Root Watchers
323
366
 
324
367
  ```typescript
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Cancel-callback HTTP endpoint — RFC-0001 §7.2 Option A.
3
+ *
4
+ * Parity with python-sdk's `macp_sdk.agent.cancel_callback`. The
5
+ * examples-service's `BootstrapPayload.cancel_callback` field asks each
6
+ * agent to listen on `http://host:port{path}` for a `POST` whose JSON
7
+ * body is `{"runId": ..., "reason": ...}`. Receipt of the POST should
8
+ * stop the participant cleanly — typically by calling `participant.stop()`.
9
+ */
10
+ export type CancelHandler = (runId: string, reason: string) => void | Promise<void>;
11
+ export interface CancelCallbackServerOptions {
12
+ host: string;
13
+ port: number;
14
+ path: string;
15
+ onCancel: CancelHandler;
16
+ }
17
+ export interface CancelCallbackServer {
18
+ readonly host: string;
19
+ readonly port: number;
20
+ readonly path: string;
21
+ close(): Promise<void>;
22
+ }
23
+ export declare function startCancelCallbackServer(options: CancelCallbackServerOptions): Promise<CancelCallbackServer>;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.startCancelCallbackServer = startCancelCallbackServer;
37
+ const http = __importStar(require("node:http"));
38
+ const logging_1 = require("../logging");
39
+ function normalisePath(path) {
40
+ return path.startsWith('/') ? path : `/${path}`;
41
+ }
42
+ async function readBody(req) {
43
+ return new Promise((resolve, reject) => {
44
+ const chunks = [];
45
+ req.on('data', (chunk) => chunks.push(chunk));
46
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
47
+ req.on('error', reject);
48
+ });
49
+ }
50
+ function startCancelCallbackServer(options) {
51
+ const path = normalisePath(options.path);
52
+ return new Promise((resolve, reject) => {
53
+ const server = http.createServer(async (req, res) => {
54
+ if (req.method !== 'POST' || req.url !== path) {
55
+ res.statusCode = 404;
56
+ res.end();
57
+ return;
58
+ }
59
+ let body = {};
60
+ try {
61
+ const raw = await readBody(req);
62
+ if (raw)
63
+ body = JSON.parse(raw);
64
+ }
65
+ catch {
66
+ body = {};
67
+ }
68
+ const runId = String(body.runId ?? body.run_id ?? '');
69
+ const reason = String(body.reason ?? '');
70
+ logging_1.logger.info('cancel_callback invoked', { runId, reason });
71
+ try {
72
+ await options.onCancel(runId, reason);
73
+ }
74
+ catch (err) {
75
+ logging_1.logger.error('cancel_callback handler raised', err);
76
+ res.statusCode = 500;
77
+ res.end();
78
+ return;
79
+ }
80
+ res.statusCode = 202;
81
+ res.setHeader('Content-Type', 'application/json');
82
+ res.end('{"ok":true}');
83
+ });
84
+ server.once('error', reject);
85
+ server.listen(options.port, options.host, () => {
86
+ server.removeListener('error', reject);
87
+ const addr = server.address();
88
+ const boundHost = addr.address;
89
+ const boundPort = addr.port;
90
+ logging_1.logger.debug(`cancel_callback listening on http://${boundHost}:${boundPort}${path}`);
91
+ let closing = false;
92
+ const handle = {
93
+ host: boundHost,
94
+ port: boundPort,
95
+ path,
96
+ close() {
97
+ if (closing)
98
+ return Promise.resolve();
99
+ closing = true;
100
+ return new Promise((res, rej) => {
101
+ server.close((err) => (err ? rej(err) : res()));
102
+ });
103
+ },
104
+ };
105
+ resolve(handle);
106
+ });
107
+ });
108
+ }
@@ -1,4 +1,5 @@
1
1
  export * from './types';
2
+ export * from './cancel-callback';
2
3
  export * from './dispatcher';
3
4
  export * from './participant';
4
5
  export * from './strategies';
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.HttpTransportAdapter = exports.GrpcTransportAdapter = void 0;
18
18
  __exportStar(require("./types"), exports);
19
+ __exportStar(require("./cancel-callback"), exports);
19
20
  __exportStar(require("./dispatcher"), exports);
20
21
  __exportStar(require("./participant"), exports);
21
22
  __exportStar(require("./strategies"), exports);
@@ -1,5 +1,7 @@
1
1
  import type { AuthConfig } from '../auth';
2
2
  import type { MacpClient } from '../client';
3
+ import type { Envelope } from '../types';
4
+ import { type CancelCallbackServer } from './cancel-callback';
3
5
  import { type TransportAdapter } from './transports';
4
6
  import type { MessageHandler, PhaseChangeHandler, ProjectionLike, SessionActions, TerminalHandler } from './types';
5
7
  export interface InitiatorConfig {
@@ -7,6 +9,8 @@ export interface InitiatorConfig {
7
9
  intent: string;
8
10
  participants: string[];
9
11
  ttlMs: number;
12
+ contextId?: string;
13
+ extensions?: Record<string, Buffer>;
10
14
  context?: Record<string, unknown>;
11
15
  roots?: Array<{
12
16
  uri: string;
@@ -30,6 +34,17 @@ export interface ParticipantConfig {
30
34
  policyVersion?: string;
31
35
  transport?: TransportAdapter;
32
36
  initiator?: InitiatorConfig;
37
+ /**
38
+ * Bind a cancel-callback HTTP endpoint (RFC-0001 §7.2 Option A) when
39
+ * this participant's event loop starts. The server is closed
40
+ * automatically when {@link Participant.stop} is called. Parity with
41
+ * python-sdk's bootstrap `cancel_callback` field.
42
+ */
43
+ cancelCallback?: {
44
+ host: string;
45
+ port: number;
46
+ path: string;
47
+ };
33
48
  }
34
49
  export declare class Participant {
35
50
  readonly participantId: string;
@@ -43,15 +58,39 @@ export declare class Participant {
43
58
  private readonly session;
44
59
  private readonly transport;
45
60
  private readonly initiatorConfig?;
61
+ private readonly participants;
62
+ private readonly modeVersion?;
63
+ private readonly configurationVersion?;
64
+ private readonly policyVersion?;
46
65
  private running;
47
66
  private lastPhase;
67
+ private cancelCallbackServer?;
68
+ private readonly cancelCallbackConfig?;
48
69
  constructor(config: ParticipantConfig);
49
70
  private createModeSession;
50
71
  private buildActions;
51
72
  on(messageType: string, handler: MessageHandler): Participant;
52
73
  onPhaseChange(phase: string, handler: PhaseChangeHandler): Participant;
53
74
  onTerminal(handler: TerminalHandler): Participant;
75
+ /** Whether {@link stop} has been called (or the loop has exited). */
76
+ get isStopped(): boolean;
54
77
  run(): Promise<void>;
78
+ /**
79
+ * Manually process a single envelope. Parity with python-sdk's
80
+ * {@code Participant.process_event}. Useful for deterministic unit tests
81
+ * and for driving the participant from a foreign event loop without
82
+ * installing a {@link TransportAdapter}.
83
+ */
84
+ processEvent(envelope: Envelope): Promise<void>;
85
+ private buildHandlerContext;
86
+ /** Returns true if a terminal state was reached. */
87
+ private processMessage;
55
88
  stop(): Promise<void>;
89
+ /**
90
+ * Attach a running cancel-callback HTTP server to this participant.
91
+ * The server is closed automatically when {@link stop} is called.
92
+ * Parity with python-sdk's `Participant.attach_cancel_callback_server`.
93
+ */
94
+ attachCancelCallbackServer(server: CancelCallbackServer): void;
56
95
  private emitInitiatorEnvelopes;
57
96
  }