@brightbeamai/chap-coordinator 0.2.8 → 0.2.9

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/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CoordinatorOptions, a as Coordinator } from './api-C3nkEk-B.cjs';
2
- export { b as CoordinatorApi, c as CoordinatorError, d as CredentialVerifier, E as EscalationPolicyFn, H as Handler, I as IdFactory, P as PRIVILEGED_METHODS, R as ReviewDepthPolicyFn, e as RoutingPolicyFn, S as ScittReceiptVerifier, f as ScittSubmitter, T as TokenVerifier, g as call, m as makeApi } from './api-C3nkEk-B.cjs';
1
+ import { C as CoordinatorOptions, a as Coordinator } from './api-DE78DA0o.cjs';
2
+ export { b as CoordinatorApi, c as CoordinatorError, d as CredentialVerifier, E as EscalationPolicyFn, H as Handler, I as IdFactory, P as PRIVILEGED_METHODS, R as ReviewDepthPolicyFn, e as RoutingPolicyFn, S as ScittReceiptVerifier, f as ScittSubmitter, T as TokenVerifier, g as call, m as makeApi } from './api-DE78DA0o.cjs';
3
3
  export { MethodName, MethodParams, MethodResult, MethodTable } from './methods.cjs';
4
4
  export { MemoryStore, Store, WorkspaceRecord } from './storage/store.cjs';
5
5
  export { ArtefactId, ArtefactRoutingHints, AuditEntry, AuditListener, Deliberation, DeliberationVote, Envelope, Handoff, HandoffTaskItem, JsonPatchOp, Jwk, KeyRecord, Member, Mode, OverrideArtefact, ParticipantType, ParticipantUri, PauseScope, ReviewDecision, RouteDecisionArtefact, SnapshotArtefact, Task, TaskId, TaskRoutingHints, TaskState, WhisperPrompt, Workspace, WorkspaceId, WorkspaceState, modeLE } from './types.cjs';
@@ -107,7 +107,7 @@ declare function makeDefaultPolicy(seniorTarget: string): Pick<CoordinatorOption
107
107
  * happened outside CHAP, wrap it so the audit log carries a faithful,
108
108
  * citable record.
109
109
  *
110
- * Spec target: CHAP 0.2. MCP 2025-11-25. A2A 1.0.
110
+ * Spec target: CHAP 0.2. MCP 2026-07-28. A2A 1.0.
111
111
  */
112
112
 
113
113
  interface WrapMcpToolCallOptions {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CoordinatorOptions, a as Coordinator } from './api-CmSvlIqO.js';
2
- export { b as CoordinatorApi, c as CoordinatorError, d as CredentialVerifier, E as EscalationPolicyFn, H as Handler, I as IdFactory, P as PRIVILEGED_METHODS, R as ReviewDepthPolicyFn, e as RoutingPolicyFn, S as ScittReceiptVerifier, f as ScittSubmitter, T as TokenVerifier, g as call, m as makeApi } from './api-CmSvlIqO.js';
1
+ import { C as CoordinatorOptions, a as Coordinator } from './api-CPEk0V6i.js';
2
+ export { b as CoordinatorApi, c as CoordinatorError, d as CredentialVerifier, E as EscalationPolicyFn, H as Handler, I as IdFactory, P as PRIVILEGED_METHODS, R as ReviewDepthPolicyFn, e as RoutingPolicyFn, S as ScittReceiptVerifier, f as ScittSubmitter, T as TokenVerifier, g as call, m as makeApi } from './api-CPEk0V6i.js';
3
3
  export { MethodName, MethodParams, MethodResult, MethodTable } from './methods.js';
4
4
  export { MemoryStore, Store, WorkspaceRecord } from './storage/store.js';
5
5
  export { ArtefactId, ArtefactRoutingHints, AuditEntry, AuditListener, Deliberation, DeliberationVote, Envelope, Handoff, HandoffTaskItem, JsonPatchOp, Jwk, KeyRecord, Member, Mode, OverrideArtefact, ParticipantType, ParticipantUri, PauseScope, ReviewDecision, RouteDecisionArtefact, SnapshotArtefact, Task, TaskId, TaskRoutingHints, TaskState, WhisperPrompt, Workspace, WorkspaceId, WorkspaceState, modeLE } from './types.js';
@@ -107,7 +107,7 @@ declare function makeDefaultPolicy(seniorTarget: string): Pick<CoordinatorOption
107
107
  * happened outside CHAP, wrap it so the audit log carries a faithful,
108
108
  * citable record.
109
109
  *
110
- * Spec target: CHAP 0.2. MCP 2025-11-25. A2A 1.0.
110
+ * Spec target: CHAP 0.2. MCP 2026-07-28. A2A 1.0.
111
111
  */
112
112
 
113
113
  interface WrapMcpToolCallOptions {
package/dist/index.js CHANGED
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/canonical.ts
9
2
  import { createHash } from "crypto";
10
3
  var ZERO_HASH = "sha256:" + "0".repeat(64);
@@ -169,12 +162,7 @@ var IdFactory = class {
169
162
  return this.counter * 0x9E3779B97F4A7C15n & (1n << 80n) - 1n;
170
163
  }
171
164
  const buf = new Uint8Array(10);
172
- if (typeof crypto !== "undefined" && crypto.getRandomValues) {
173
- crypto.getRandomValues(buf);
174
- } else {
175
- const nodeCrypto = __require("crypto");
176
- buf.set(nodeCrypto.randomBytes(10));
177
- }
165
+ crypto.getRandomValues(buf);
178
166
  let n = 0n;
179
167
  for (const b of buf) n = n << 8n | BigInt(b);
180
168
  return n;
@@ -1517,6 +1505,16 @@ function registerSecuritySigned(coord) {
1517
1505
  for (const f of ["target_uri", "kid"]) {
1518
1506
  if (!(f in p)) return { error: rpcError(E.PARAMS, `Missing field: ${f}`) };
1519
1507
  }
1508
+ const sender = p.from;
1509
+ if (sender !== p.target_uri) {
1510
+ const caller = sender ? ws.members.get(sender) : void 0;
1511
+ if (!caller || caller.role !== "admin") {
1512
+ return { error: rpcError(
1513
+ E.NOT_AUTHORISED,
1514
+ "Revoking another member's key requires the admin role"
1515
+ ) };
1516
+ }
1517
+ }
1520
1518
  const member = ws.members.get(p.target_uri);
1521
1519
  if (!member) return { error: rpcError(E.SIG_KEY_NOT_FOUND, `Unknown target: ${p.target_uri}`) };
1522
1520
  const key = member.keys.find((k) => k.kid === p.kid);
@@ -1608,7 +1606,8 @@ function registerWhisper(coord) {
1608
1606
  prompt.answer_option = answerOption;
1609
1607
  prompt.answer_text = answerText;
1610
1608
  prompt.comment = p.comment;
1611
- return { result: { answered: true, whisper_id: prompt.id } };
1609
+ p.task_id = prompt.task_id;
1610
+ return { result: { answered: true, whisper_id: prompt.id, task_id: prompt.task_id } };
1612
1611
  });
1613
1612
  coord.checkWhisperLapses = (workspaceId, now) => {
1614
1613
  const ws = coord.workspaces.get(workspaceId);
@@ -1632,6 +1631,11 @@ function registerWhisper(coord) {
1632
1631
  ts: coord.now(),
1633
1632
  kind: "whisper_lapsed",
1634
1633
  whisper_id: prompt.id,
1634
+ // Carry the task so a task-filtered audit.read surfaces the
1635
+ // lapse. A lapsed whisper applies its default with no human
1636
+ // input, so it is the case most in need of being visible on
1637
+ // the task's thread.
1638
+ task_id: prompt.task_id,
1635
1639
  default_applied: prompt.default_if_lapsed
1636
1640
  }
1637
1641
  };
@@ -1833,6 +1837,7 @@ var Coordinator = class {
1833
1837
  mode_ceiling: ws.mode_ceiling,
1834
1838
  routing_policy_uri: ws.routing_policy_uri,
1835
1839
  step_up_window_sec: ws.step_up_window_sec,
1840
+ min_acr: ws.min_acr,
1836
1841
  members: Array.from(ws.members.values()),
1837
1842
  tasks: Array.from(ws.tasks.values()),
1838
1843
  overrides: Array.from(ws.overrides.values()),
@@ -1841,6 +1846,7 @@ var Coordinator = class {
1841
1846
  handoffs: Array.from(ws.handoffs.values()),
1842
1847
  snapshots: Array.from(ws.snapshots.values()),
1843
1848
  route_decisions: Array.from(ws.route_decisions.values()),
1849
+ idempotency_keys: ws.idempotency_keys,
1844
1850
  audit: ws.audit,
1845
1851
  chain_head: ws.chain_head,
1846
1852
  chain_enabled: ws.chain_enabled
@@ -1863,6 +1869,7 @@ var Coordinator = class {
1863
1869
  mode_ceiling: w.mode_ceiling ?? "production",
1864
1870
  routing_policy_uri: w.routing_policy_uri,
1865
1871
  step_up_window_sec: w.step_up_window_sec ?? 300,
1872
+ min_acr: w.min_acr,
1866
1873
  members: /* @__PURE__ */ new Map(),
1867
1874
  tasks: /* @__PURE__ */ new Map(),
1868
1875
  overrides: /* @__PURE__ */ new Map(),
@@ -1871,6 +1878,7 @@ var Coordinator = class {
1871
1878
  handoffs: /* @__PURE__ */ new Map(),
1872
1879
  snapshots: /* @__PURE__ */ new Map(),
1873
1880
  route_decisions: /* @__PURE__ */ new Map(),
1881
+ idempotency_keys: w.idempotency_keys ?? {},
1874
1882
  audit: w.audit ?? [],
1875
1883
  chain_head: w.chain_head,
1876
1884
  chain_enabled: !!w.chain_enabled
@@ -1909,6 +1917,16 @@ var Coordinator = class {
1909
1917
  if (this.options.requireSignatures && method !== "participant.join" && method !== "workspace.create") {
1910
1918
  const sigErr = this.verifySignature(envelope);
1911
1919
  if (sigErr) return reply(envelope, { error: sigErr });
1920
+ if (method === "participant.rotate_key") {
1921
+ const signingKid = envelope.sig.split(":")[1];
1922
+ const oldKid = params.old_kid;
1923
+ if (oldKid !== void 0 && signingKid !== oldKid) {
1924
+ return reply(envelope, { error: rpcError(
1925
+ E.SIG_ROTATION_KEY_MISMATCH,
1926
+ "Rotation must be signed with the old key"
1927
+ ) });
1928
+ }
1929
+ }
1912
1930
  }
1913
1931
  if (this.options.enforceStepUp && PRIVILEGED_METHODS.has(method)) {
1914
1932
  const stale = this.checkStepUp(params);
@@ -1939,6 +1957,16 @@ var Coordinator = class {
1939
1957
  }
1940
1958
  }
1941
1959
  }
1960
+ if (method === "task.create") {
1961
+ const wsId2 = params.workspace;
1962
+ const ws = typeof wsId2 === "string" ? this.workspaces.get(wsId2) : void 0;
1963
+ const key = params.idempotency_key;
1964
+ if (ws && typeof key === "string") {
1965
+ const existingId = ws.idempotency_keys[key];
1966
+ const task = existingId ? ws.tasks.get(existingId) : void 0;
1967
+ if (task) return reply(envelope, { result: { task_id: existingId, state: task.state } });
1968
+ }
1969
+ }
1942
1970
  const handler = this.handlers.get(method);
1943
1971
  if (!handler) {
1944
1972
  return reply(envelope, { error: rpcError(E.METHOD, `Unknown method: ${method}`) });
@@ -2052,7 +2080,13 @@ var Coordinator = class {
2052
2080
  const ws = this.workspaces.get(wsId);
2053
2081
  if (!ws) return null;
2054
2082
  const member = ws.members.get(sender);
2055
- if (!member || member.oidc_auth_time === void 0) return null;
2083
+ if (!member) return null;
2084
+ if (member.type !== "human" && member.oidc_sub === void 0) return null;
2085
+ if (member.oidc_auth_time === void 0) {
2086
+ return rpcError(E.OIDC_STEP_UP_REQUIRED, "Step-up authentication required", {
2087
+ window_sec: ws.step_up_window_sec
2088
+ });
2089
+ }
2056
2090
  const nowSec = Math.floor(Date.now() / 1e3);
2057
2091
  const age = nowSec - member.oidc_auth_time;
2058
2092
  if (age > ws.step_up_window_sec) {
@@ -2061,6 +2095,12 @@ var Coordinator = class {
2061
2095
  age_sec: age
2062
2096
  });
2063
2097
  }
2098
+ if (ws.min_acr !== void 0 && member.oidc_acr !== ws.min_acr) {
2099
+ return rpcError(E.OIDC_STEP_UP_REQUIRED, "Step-up required: acr below the workspace minimum", {
2100
+ required_acr: ws.min_acr,
2101
+ acr: member.oidc_acr
2102
+ });
2103
+ }
2064
2104
  return null;
2065
2105
  }
2066
2106
  // ==========================================================
@@ -2110,6 +2150,7 @@ var Coordinator = class {
2110
2150
  mode_ceiling: p.mode_ceiling ?? "production",
2111
2151
  routing_policy_uri: p.routing_policy_uri,
2112
2152
  step_up_window_sec: typeof p.step_up_window_sec === "number" ? p.step_up_window_sec : 300,
2153
+ min_acr: p.min_acr,
2113
2154
  members: /* @__PURE__ */ new Map(),
2114
2155
  tasks: /* @__PURE__ */ new Map(),
2115
2156
  overrides: /* @__PURE__ */ new Map(),
@@ -2118,6 +2159,7 @@ var Coordinator = class {
2118
2159
  handoffs: /* @__PURE__ */ new Map(),
2119
2160
  snapshots: /* @__PURE__ */ new Map(),
2120
2161
  route_decisions: /* @__PURE__ */ new Map(),
2162
+ idempotency_keys: {},
2121
2163
  audit: [],
2122
2164
  chain_enabled: chainEnabled,
2123
2165
  chain_head: chainEnabled ? ZERO_HASH : void 0
@@ -2130,6 +2172,10 @@ var Coordinator = class {
2130
2172
  if (miss) return { error: rpcError(E.PARAMS, `Missing field: ${miss}`) };
2131
2173
  const ws = this.workspaces.get(p.workspace);
2132
2174
  if (!ws) return { error: rpcError(E.PARAMS, `Unknown workspace: ${p.workspace}`) };
2175
+ if (this.options.requireReadMembership) {
2176
+ const notMember = this.requireMember(ws, p.from);
2177
+ if (notMember) return notMember;
2178
+ }
2133
2179
  return { result: {
2134
2180
  id: ws.id,
2135
2181
  created: ws.created,
@@ -2147,10 +2193,15 @@ var Coordinator = class {
2147
2193
  } };
2148
2194
  }
2149
2195
  opWorkspaceSetProfiles(p) {
2150
- const miss = missing(p, ["workspace", "profiles"]);
2196
+ const miss = missing(p, ["workspace", "from", "profiles"]);
2151
2197
  if (miss) return { error: rpcError(E.PARAMS, `Missing field: ${miss}`) };
2152
2198
  const ws = this.workspaces.get(p.workspace);
2153
2199
  if (!ws) return { error: rpcError(E.PARAMS, "Unknown workspace") };
2200
+ const notMember = this.requireMember(ws, p.from);
2201
+ if (notMember) return notMember;
2202
+ if (ws.members.get(p.from).role !== "admin") {
2203
+ return { error: rpcError(E.NOT_AUTHORISED, "workspace.set_profiles requires the admin role") };
2204
+ }
2154
2205
  const newProfiles = [...p.profiles];
2155
2206
  if (!newProfiles.some((pr) => pr.startsWith("core/"))) newProfiles.push("core/1.0");
2156
2207
  ws.profiles = newProfiles;
@@ -2188,6 +2239,7 @@ var Coordinator = class {
2188
2239
  member.oidc_sub = claims.sub;
2189
2240
  const at = claims.auth_time;
2190
2241
  if (typeof at === "number") member.oidc_auth_time = at;
2242
+ member.oidc_acr = claims.acr;
2191
2243
  const cnf = claims.cnf;
2192
2244
  const cnfJwk = cnf?.jwk;
2193
2245
  if (cnfJwk && typeof cnfJwk.kid === "string") {
@@ -2216,6 +2268,7 @@ var Coordinator = class {
2216
2268
  if (existing) {
2217
2269
  if (member.oidc_sub !== void 0) existing.oidc_sub = member.oidc_sub;
2218
2270
  if (member.oidc_auth_time !== void 0) existing.oidc_auth_time = member.oidc_auth_time;
2271
+ if (member.oidc_acr !== void 0) existing.oidc_acr = member.oidc_acr;
2219
2272
  if (member.vc_holder !== void 0) existing.vc_holder = member.vc_holder;
2220
2273
  for (const k of attested) {
2221
2274
  if (!existing.keys.some((x) => x.kid === k.kid)) existing.keys.push(k);
@@ -2236,6 +2289,8 @@ var Coordinator = class {
2236
2289
  if (miss) return { error: rpcError(E.PARAMS, `Missing field: ${miss}`) };
2237
2290
  const ws = this.workspaces.get(p.workspace);
2238
2291
  if (!ws) return { error: rpcError(E.PARAMS, "Unknown workspace") };
2292
+ const notMember = this.requireMember(ws, p.from);
2293
+ if (notMember) return notMember;
2239
2294
  const assignee = p.assignee || p.to;
2240
2295
  if (!assignee || !ws.members.has(assignee)) {
2241
2296
  return { error: rpcError(E.PARAMS, "Assignee not in workspace") };
@@ -2270,11 +2325,14 @@ var Coordinator = class {
2270
2325
  if (task.mode === "trial") task.review_required = true;
2271
2326
  else if ("review_required" in p) task.review_required = !!p.review_required;
2272
2327
  ws.tasks.set(taskId, task);
2328
+ if (typeof p.idempotency_key === "string") ws.idempotency_keys[p.idempotency_key] = taskId;
2273
2329
  return { result: { task_id: taskId, state: "created" } };
2274
2330
  }
2275
2331
  opTaskUpdate(p) {
2276
2332
  const ws = this.workspaces.get(p.workspace);
2277
2333
  if (!ws) return { error: rpcError(E.PARAMS, "Unknown workspace") };
2334
+ const notMember = this.requireMember(ws, p.from);
2335
+ if (notMember) return notMember;
2278
2336
  const task = ws.tasks.get(p.task_id);
2279
2337
  if (!task) return { error: rpcError(E.PARAMS, "Unknown task") };
2280
2338
  const newState = p.state;
@@ -2317,6 +2375,10 @@ var Coordinator = class {
2317
2375
  opAuditRead(p) {
2318
2376
  const ws = this.workspaces.get(p.workspace);
2319
2377
  if (!ws) return { error: rpcError(E.PARAMS, "Unknown workspace") };
2378
+ if (this.options.requireReadMembership) {
2379
+ const notMember = this.requireMember(ws, p.from);
2380
+ if (notMember) return notMember;
2381
+ }
2320
2382
  const range = p.range ?? {};
2321
2383
  const filter = p.filter ?? {};
2322
2384
  const fromSeq = range.from_seq ?? 0;