macp-sdk-typescript 0.10.0 → 0.11.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.
@@ -2,35 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TaskProjection = void 0;
4
4
  const constants_1 = require("../constants");
5
- const logging_1 = require("../logging");
6
- class TaskProjection {
5
+ const base_1 = require("./base");
6
+ class TaskProjection extends base_1.BaseProjection {
7
+ mode = constants_1.MODE_TASK;
7
8
  tasks = new Map();
8
9
  updates = [];
9
10
  completions = [];
10
11
  failures = [];
11
- /**
12
- * The session's accepted history, one envelope per unique `message_id`. See
13
- * `BaseProjection.transcript` (`src/projections/base.ts`) for the full
14
- * redelivery-idempotence contract (RFC-MACP-0006 §3.2); duplicated here
15
- * only as a one-line pointer.
16
- */
17
- transcript = [];
18
- /**
19
- * Cardinality anomalies recorded while replaying this projection's accepted
20
- * transcript. See `BaseProjection.anomalies` (`src/projections/base.ts`)
21
- * for the canonical description; duplicated here only as a one-line
22
- * pointer. No built-in detection populates this for Task mode.
23
- */
24
- anomalies = [];
25
12
  phase = 'Pending';
26
- commitment;
27
- /**
28
- * `message_id`s already applied to this projection. See
29
- * `BaseProjection.seenMessageIds` (`src/projections/base.ts`) for the full
30
- * redelivery-idempotence rationale (RFC-MACP-0006 §3.2); duplicated here
31
- * only as a one-line pointer.
32
- */
33
- seenMessageIds = new Set();
34
13
  /**
35
14
  * The **session-level** active assignment: who currently holds the session's
36
15
  * single assignee slot, and which `task_id` they took it on.
@@ -51,36 +30,8 @@ class TaskProjection {
51
30
  * the right `TaskRecord.assignee` even on a multi-task transcript.
52
31
  */
53
32
  activeAssignment;
54
- /**
55
- * Apply one envelope to this projection's in-process state.
56
- *
57
- * Input contract: **accepted-only**, caller-maintained (`Envelope` carries
58
- * no acceptance marker). Canonical source: `schemas/conformance/README.md`
59
- * "Notes:", RFC-MACP-0007 §5.3, RFC-MACP-0011 §5. Full rationale and failure
60
- * mode are documented once, on `BaseProjection.applyEnvelope`
61
- * (`src/projections/base.ts`), and duplicated here only as a one-line
62
- * pointer so six independent copies of the same prose cannot drift.
63
- *
64
- * Redelivery idempotence: a redelivered envelope (same `message_id`) is a
65
- * non-event — not appended to `transcript`, not passed to the `switch`.
66
- * See `BaseProjection.applyEnvelope` for the full RFC-MACP-0006 §3.2
67
- * citation; duplicated here only as a one-line pointer.
68
- */
69
- applyEnvelope(envelope, protoRegistry) {
70
- if (envelope.mode !== constants_1.MODE_TASK)
71
- return;
72
- if (envelope.messageId) {
73
- if (this.seenMessageIds.has(envelope.messageId)) {
74
- logging_1.logger.debug('projection redelivery ignored', {
75
- messageId: envelope.messageId,
76
- mode: envelope.mode,
77
- messageType: envelope.messageType,
78
- });
79
- return;
80
- }
81
- this.seenMessageIds.add(envelope.messageId);
82
- }
83
- this.transcript.push(envelope);
33
+ /** Handle a Task-mode envelope. See `BaseProjection.applyEnvelope` for the accepted-only input contract and redelivery/rollback semantics shared by all built-in modes. */
34
+ applyMode(envelope, protoRegistry) {
84
35
  const payload = protoRegistry.decodeKnownPayload(envelope.mode, envelope.messageType, envelope.payload);
85
36
  switch (envelope.messageType) {
86
37
  case 'TaskRequest': {
@@ -199,33 +150,10 @@ class TaskProjection {
199
150
  this.phase = 'Failed';
200
151
  break;
201
152
  }
202
- case 'Commitment': {
203
- this.commitment = payload;
204
- this.phase = 'Committed';
205
- break;
206
- }
207
153
  default:
208
154
  break;
209
155
  }
210
156
  }
211
- /**
212
- * True once at least one `ProjectionAnomaly` has been recorded. See
213
- * `BaseProjection.hasAnomalies` (`src/projections/base.ts`) for the
214
- * canonical description; duplicated here only as a one-line pointer.
215
- */
216
- get hasAnomalies() {
217
- return this.anomalies.length > 0;
218
- }
219
- get isCommitted() {
220
- return this.commitment !== undefined;
221
- }
222
- get isPositiveOutcome() {
223
- if (!this.commitment)
224
- return undefined;
225
- const val = this.commitment.outcomePositive ??
226
- this.commitment.outcome_positive;
227
- return val !== undefined ? Boolean(val) : true;
228
- }
229
157
  getTask(taskId) {
230
158
  return this.tasks.get(taskId);
231
159
  }
@@ -10,11 +10,25 @@ export declare class ProtoRegistry {
10
10
  /**
11
11
  * Decode an `ext.multi_round.v1` `Contribute` payload, accepting both wire
12
12
  * formats: legacy JSON (`{"value":"..."}`, still replayed verbatim from
13
- * pre-proto histories) and canonical protobuf (`ContributePayload`). JSON is
14
- * tried first — mirroring the runtime's permanent decode order — and the two
15
- * encodings are disjoint on their first byte (`{` = 0x7b vs proto field-1
16
- * tag 0x0A), so a canonical proto payload never mis-parses as JSON and vice
17
- * versa. Both normalize to `{ value: string }`.
13
+ * pre-proto histories) and canonical protobuf (`ContributePayload`). Both
14
+ * normalize to `{ value: string }`.
15
+ *
16
+ * Parse-then-fallback (issue #93), not a first-byte shortcut: JSON is always
17
+ * attempted first, and only a `JSON.parse` failure falls through to
18
+ * protobuf. A first-byte check (`{` = 0x7b vs proto field-1 tag 0x0A) does
19
+ * not generalize — e.g. legacy JSON with leading whitespace has neither byte
20
+ * first, and used to reach `decodeMessage` and throw. Parity with
21
+ * `macp-sdk-python`'s `_decode_json_first_then_proto`, which has always
22
+ * worked this way. Safe because a canonical proto payload's bytes are
23
+ * essentially never also syntactically valid JSON.
24
+ *
25
+ * An empty payload falls through the same path: `JSON.parse('')` throws, so
26
+ * it reaches `decodeMessage` on zero bytes, which yields `{}` (proto3
27
+ * defaults) rather than a decode error. This SDK's decode layer is
28
+ * observational, not an acceptance gate — whether an empty `Contribute`
29
+ * should be rejected is a runtime-acceptance question (tracked cross-repo,
30
+ * `contribute_acceptance.empty_payload` in `schemas/parity/contract.json`,
31
+ * currently `macp-runtime`-only), not something this method decides.
18
32
  */
19
33
  private decodeMultiRoundContribute;
20
34
  private lookupType;
@@ -169,26 +169,34 @@ class ProtoRegistry {
169
169
  /**
170
170
  * Decode an `ext.multi_round.v1` `Contribute` payload, accepting both wire
171
171
  * formats: legacy JSON (`{"value":"..."}`, still replayed verbatim from
172
- * pre-proto histories) and canonical protobuf (`ContributePayload`). JSON is
173
- * tried first — mirroring the runtime's permanent decode order — and the two
174
- * encodings are disjoint on their first byte (`{` = 0x7b vs proto field-1
175
- * tag 0x0A), so a canonical proto payload never mis-parses as JSON and vice
176
- * versa. Both normalize to `{ value: string }`.
172
+ * pre-proto histories) and canonical protobuf (`ContributePayload`). Both
173
+ * normalize to `{ value: string }`.
174
+ *
175
+ * Parse-then-fallback (issue #93), not a first-byte shortcut: JSON is always
176
+ * attempted first, and only a `JSON.parse` failure falls through to
177
+ * protobuf. A first-byte check (`{` = 0x7b vs proto field-1 tag 0x0A) does
178
+ * not generalize — e.g. legacy JSON with leading whitespace has neither byte
179
+ * first, and used to reach `decodeMessage` and throw. Parity with
180
+ * `macp-sdk-python`'s `_decode_json_first_then_proto`, which has always
181
+ * worked this way. Safe because a canonical proto payload's bytes are
182
+ * essentially never also syntactically valid JSON.
183
+ *
184
+ * An empty payload falls through the same path: `JSON.parse('')` throws, so
185
+ * it reaches `decodeMessage` on zero bytes, which yields `{}` (proto3
186
+ * defaults) rather than a decode error. This SDK's decode layer is
187
+ * observational, not an acceptance gate — whether an empty `Contribute`
188
+ * should be rejected is a runtime-acceptance question (tracked cross-repo,
189
+ * `contribute_acceptance.empty_payload` in `schemas/parity/contract.json`,
190
+ * currently `macp-runtime`-only), not something this method decides.
177
191
  */
178
192
  decodeMultiRoundContribute(payload) {
179
- if (!payload.length)
193
+ try {
194
+ const parsed = JSON.parse(payload.toString('utf8'));
195
+ return { value: typeof parsed.value === 'string' ? parsed.value : String(parsed.value ?? '') };
196
+ }
197
+ catch {
180
198
  return this.decodeMessage(MULTI_ROUND_CONTRIBUTE, payload);
181
- if (payload[0] === 0x7b) {
182
- // Leading `{` — legacy JSON. Parse and normalize to { value }.
183
- try {
184
- const parsed = JSON.parse(payload.toString('utf8'));
185
- return { value: typeof parsed.value === 'string' ? parsed.value : String(parsed.value ?? '') };
186
- }
187
- catch {
188
- // Not valid JSON despite the leading brace — fall through to protobuf.
189
- }
190
199
  }
191
- return this.decodeMessage(MULTI_ROUND_CONTRIBUTE, payload);
192
200
  }
193
201
  lookupType(typeName) {
194
202
  const lookedUp = this.root.lookupType(typeName);
package/dist/version.d.ts CHANGED
@@ -4,4 +4,4 @@
4
4
  * Kept in sync with package.json by release-please (see the
5
5
  * `x-release-please-version` annotation).
6
6
  */
7
- export declare const VERSION = "0.10.0";
7
+ export declare const VERSION = "0.11.0";
package/dist/version.js CHANGED
@@ -7,4 +7,4 @@ exports.VERSION = void 0;
7
7
  * Kept in sync with package.json by release-please (see the
8
8
  * `x-release-please-version` annotation).
9
9
  */
10
- exports.VERSION = '0.10.0'; // x-release-please-version
10
+ exports.VERSION = '0.11.0'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "macp-sdk-typescript",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "TypeScript SDK for the MACP Rust runtime",
5
5
  "license": "Apache-2.0",
6
6
  "type": "commonjs",
@@ -55,18 +55,18 @@
55
55
  "dependencies": {
56
56
  "@grpc/grpc-js": "^1.14.3",
57
57
  "@grpc/proto-loader": "^0.8.0",
58
- "@multiagentcoordinationprotocol/proto": "^0.1.9",
58
+ "@multiagentcoordinationprotocol/proto": "^0.1.10",
59
59
  "protobufjs": "^8.6.5"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/node": "^26.4.0",
63
63
  "@typescript-eslint/eslint-plugin": "^8.68.0",
64
64
  "@typescript-eslint/parser": "^8.68.0",
65
- "@vitest/coverage-v8": "^4.1.11",
65
+ "@vitest/coverage-v8": "^5.0.0",
66
66
  "eslint": "^10.9.1",
67
67
  "prettier": "^3.0.0",
68
68
  "tsx": "^4.21.0",
69
69
  "typescript": "^6.0.3",
70
- "vitest": "^4.1.11"
70
+ "vitest": "^5.0.0"
71
71
  }
72
72
  }