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.
- package/CHANGELOG.md +176 -1
- package/README.md +49 -6
- package/dist/agent/cancel-callback.d.ts +23 -0
- package/dist/agent/cancel-callback.js +108 -0
- package/dist/agent/index.d.ts +1 -0
- package/dist/agent/index.js +1 -0
- package/dist/agent/participant.d.ts +39 -0
- package/dist/agent/participant.js +114 -45
- package/dist/agent/runner.d.ts +13 -0
- package/dist/agent/runner.js +34 -0
- package/dist/agent/strategies.d.ts +2 -0
- package/dist/agent/strategies.js +8 -0
- package/dist/agent/transports.js +5 -0
- package/dist/base-session.d.ts +75 -0
- package/dist/base-session.js +127 -0
- package/dist/client.d.ts +54 -0
- package/dist/client.js +130 -9
- package/dist/constants.d.ts +21 -16
- package/dist/constants.js +24 -17
- package/dist/decision.d.ts +10 -0
- package/dist/decision.js +20 -0
- package/dist/envelope.d.ts +38 -1
- package/dist/envelope.js +53 -1
- package/dist/errors.d.ts +17 -4
- package/dist/errors.js +19 -10
- package/dist/handoff.d.ts +10 -8
- package/dist/handoff.js +20 -16
- package/dist/index.d.ts +3 -1
- package/dist/index.js +10 -2
- package/dist/logging.d.ts +21 -0
- package/dist/logging.js +64 -0
- package/dist/policy.d.ts +47 -42
- package/dist/policy.js +5 -6
- package/dist/projections/base.d.ts +20 -0
- package/dist/projections/base.js +37 -0
- package/dist/projections.d.ts +1 -0
- package/dist/projections.js +1 -0
- package/dist/proposal.d.ts +10 -0
- package/dist/proposal.js +20 -0
- package/dist/quorum.d.ts +10 -0
- package/dist/quorum.js +20 -0
- package/dist/task.d.ts +14 -4
- package/dist/task.js +24 -4
- package/dist/types.d.ts +30 -3
- package/dist/watchers.d.ts +37 -2
- package/dist/watchers.js +65 -5
- 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
|
-
## [
|
|
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 **
|
|
65
|
+
The SDK uses a **three-layer** design:
|
|
66
66
|
|
|
67
|
-
- **Low-level transport** (`MacpClient`):
|
|
68
|
-
- **High-level session helpers**:
|
|
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.
|
|
147
|
+
await session.requestTask({ taskId: 't1', title: 'Build feature', instructions: '...' });
|
|
145
148
|
await session.acceptTask({ taskId: 't1', assignee: 'worker' });
|
|
146
|
-
await session.
|
|
147
|
-
await session.
|
|
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
|
+
}
|
package/dist/agent/index.d.ts
CHANGED
package/dist/agent/index.js
CHANGED
|
@@ -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
|
}
|