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.
- package/CHANGELOG.md +124 -1
- package/README.md +14 -3
- 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 +64 -0
- package/dist/base-session.js +112 -0
- package/dist/client.d.ts +20 -0
- package/dist/client.js +90 -9
- package/dist/constants.d.ts +21 -16
- package/dist/constants.js +24 -17
- package/dist/envelope.d.ts +23 -1
- package/dist/envelope.js +40 -0
- package/dist/errors.d.ts +17 -4
- package/dist/errors.js +19 -10
- package/dist/handoff.d.ts +0 -8
- package/dist/handoff.js +0 -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 +1 -1
- 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/task.d.ts +4 -4
- package/dist/task.js +4 -4
- package/dist/watchers.d.ts +13 -1
- package/dist/watchers.js +29 -4
- package/package.json +2 -2
package/dist/agent/runner.js
CHANGED
|
@@ -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;
|
package/dist/agent/strategies.js
CHANGED
|
@@ -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';
|
package/dist/agent/transports.js
CHANGED
|
@@ -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.
|
|
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
|
|
430
|
+
const signalPayload = (0, envelope_1.buildSignalPayload)({
|
|
352
431
|
signalType: options.signalType,
|
|
353
|
-
data: options.data
|
|
354
|
-
confidence: options.confidence
|
|
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
|
|
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',
|
package/dist/constants.d.ts
CHANGED
|
@@ -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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export declare const
|
|
16
|
-
export declare const
|
|
17
|
-
export declare const
|
|
18
|
-
export declare const
|
|
19
|
-
export declare const
|
|
20
|
-
export declare const
|
|
21
|
-
export declare const
|
|
22
|
-
export declare const
|
|
23
|
-
export declare const
|
|
24
|
-
export declare const
|
|
25
|
-
export declare const
|
|
26
|
-
export declare const
|
|
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.
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
exports.
|
|
19
|
-
|
|
20
|
-
exports
|
|
21
|
-
exports.
|
|
22
|
-
exports.
|
|
23
|
-
exports.
|
|
24
|
-
exports.
|
|
25
|
-
exports.
|
|
26
|
-
exports.
|
|
27
|
-
exports.
|
|
28
|
-
exports.
|
|
29
|
-
exports.
|
|
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';
|
package/dist/envelope.d.ts
CHANGED
|
@@ -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
|
*
|