@essentialai/cogent-bridge-bundled 3.21.9 → 3.22.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 (2) hide show
  1. package/cogent-bridge.mjs +190 -58
  2. package/package.json +1 -1
package/cogent-bridge.mjs CHANGED
@@ -26389,8 +26389,8 @@ var init_stdio2 = __esm({
26389
26389
  // src/constants.ts
26390
26390
  import { createRequire } from "node:module";
26391
26391
  function resolveVersion() {
26392
- if ("3.21.9") {
26393
- return "3.21.9";
26392
+ if ("3.22.0") {
26393
+ return "3.22.0";
26394
26394
  }
26395
26395
  try {
26396
26396
  const require2 = createRequire(import.meta.url);
@@ -27747,8 +27747,13 @@ async function getRelayVersion(http, nowFn = Date.now) {
27747
27747
  if (cached2 !== null && now - cached2.fetchedAt < TTL_MS) {
27748
27748
  return cached2.version;
27749
27749
  }
27750
+ return (await _refresh(http, now))?.version ?? null;
27751
+ }
27752
+ async function _refresh(http, now) {
27750
27753
  try {
27751
- const resp = await http.get("/api/health");
27754
+ const resp = await http.get(
27755
+ "/api/health"
27756
+ );
27752
27757
  const version2 = resp?.version;
27753
27758
  if (typeof version2 !== "string" || version2.length === 0) {
27754
27759
  logger.debug(
@@ -27756,14 +27761,26 @@ async function getRelayVersion(http, nowFn = Date.now) {
27756
27761
  );
27757
27762
  return null;
27758
27763
  }
27759
- cached2 = { version: version2, fetchedAt: now };
27760
- return version2;
27764
+ const raw = resp?.capabilities;
27765
+ const capabilities = Array.isArray(raw) ? raw.filter((c2) => typeof c2 === "string") : [];
27766
+ cached2 = { version: version2, capabilities, fetchedAt: now };
27767
+ return cached2;
27761
27768
  } catch (err) {
27762
27769
  const msg = err instanceof Error ? err.message : String(err);
27763
27770
  logger.debug(`relay-version-cache: /api/health fetch failed: ${msg}`);
27764
27771
  return null;
27765
27772
  }
27766
27773
  }
27774
+ async function getRelayCapabilities(http, nowFn = Date.now) {
27775
+ const now = nowFn();
27776
+ if (cached2 !== null && now - cached2.fetchedAt < TTL_MS) {
27777
+ return cached2.capabilities;
27778
+ }
27779
+ return (await _refresh(http, now))?.capabilities ?? null;
27780
+ }
27781
+ async function relaySupports(http, capability, nowFn = Date.now) {
27782
+ return (await getRelayCapabilities(http, nowFn))?.includes(capability) ?? false;
27783
+ }
27767
27784
  var TTL_MS, cached2;
27768
27785
  var init_relay_version_cache = __esm({
27769
27786
  "src/backend/relay-version-cache.ts"() {
@@ -27779,6 +27796,8 @@ var MAILBOX_DEPROVISIONED_HEADER, PATHS, HttpBackend;
27779
27796
  var init_http_backend = __esm({
27780
27797
  "src/backend/http-backend.ts"() {
27781
27798
  "use strict";
27799
+ init_relay_version_cache();
27800
+ init_errors4();
27782
27801
  init_health_check();
27783
27802
  init_constants();
27784
27803
  init_config();
@@ -27806,7 +27825,7 @@ var init_http_backend = __esm({
27806
27825
  * The sessionId parameter from StorageBackend is the local CC sessionId;
27807
27826
  * in cloud mode we ignore it and use this.sessionId (the cloud session).
27808
27827
  */
27809
- async registerPeer(peerId, _sessionId, cwd, label, clientVersion, mode, _channelSessionId, capabilities, workspaceId, threadId) {
27828
+ async registerPeer(peerId, _sessionId, cwd, label, clientVersion, mode, _channelSessionId, capabilities, workspaceId, threadId, peerSecret) {
27810
27829
  const path20 = PATHS.peers.replace(":sessionId", this.sessionId);
27811
27830
  const body = {
27812
27831
  peerId,
@@ -27835,6 +27854,9 @@ var init_http_backend = __esm({
27835
27854
  body.role = roleCap.slice("role:".length);
27836
27855
  }
27837
27856
  }
27857
+ if (peerSecret !== void 0 && await relaySupports(this.http, "peer-ownership")) {
27858
+ body.peerSecret = peerSecret;
27859
+ }
27838
27860
  return this.http.post(path20, body);
27839
27861
  }
27840
27862
  /**
@@ -27852,8 +27874,11 @@ var init_http_backend = __esm({
27852
27874
  const headers = await this.http.delete(path20, {});
27853
27875
  const mailboxDeprovisioned = headers.get(MAILBOX_DEPROVISIONED_HEADER) === "true";
27854
27876
  return { removed: true, mailboxDeprovisioned };
27855
- } catch {
27856
- return { removed: false, mailboxDeprovisioned: false };
27877
+ } catch (err) {
27878
+ if (err instanceof BridgeError && err.code === "PEER_NOT_FOUND" /* PEER_NOT_FOUND */) {
27879
+ return { removed: false, mailboxDeprovisioned: false };
27880
+ }
27881
+ throw err;
27857
27882
  }
27858
27883
  }
27859
27884
  /**
@@ -27900,6 +27925,17 @@ var init_http_backend = __esm({
27900
27925
  if (record2.isRelayEcho === true) {
27901
27926
  body.isRelayEcho = true;
27902
27927
  }
27928
+ if (await relaySupports(this.http, "message-delivery-fields")) {
27929
+ if (record2.success === false) {
27930
+ body.success = false;
27931
+ }
27932
+ if (record2.error != null) {
27933
+ body.error = record2.error;
27934
+ }
27935
+ if (record2.durationMs != null) {
27936
+ body.durationMs = record2.durationMs;
27937
+ }
27938
+ }
27903
27939
  if (record2.attachments && record2.attachments.length > 0) {
27904
27940
  body.attachments = record2.attachments;
27905
27941
  }
@@ -33350,7 +33386,7 @@ function truncate(s) {
33350
33386
  const flat = s.replace(/\s+/g, " ").trim();
33351
33387
  return flat.length > DIAGNOSTIC_MAX ? `${flat.slice(0, DIAGNOSTIC_MAX - 1)}\u2026` : flat;
33352
33388
  }
33353
- var RELAY_STATUS, STATUS_MARKER_RE, DIAGNOSTIC_MAX, AutoRelayService, autoRelay;
33389
+ var RELAY_STATUS, STATUS_MARKER_RE, DIAGNOSTIC_MAX, EARLY_ACK_TTL_MS, EARLY_ACK_MAX, AutoRelayService, autoRelay;
33354
33390
  var init_auto_relay = __esm({
33355
33391
  "src/services/auto-relay.ts"() {
33356
33392
  "use strict";
@@ -33371,6 +33407,8 @@ var init_auto_relay = __esm({
33371
33407
  };
33372
33408
  STATUS_MARKER_RE = /^\[cogent:(queued|processing|delivered|needs-manual|failed)\]/;
33373
33409
  DIAGNOSTIC_MAX = 220;
33410
+ EARLY_ACK_TTL_MS = 12e4;
33411
+ EARLY_ACK_MAX = 256;
33374
33412
  AutoRelayService = class {
33375
33413
  localPeerId = null;
33376
33414
  localSessionId = null;
@@ -33417,7 +33455,24 @@ var init_auto_relay = __esm({
33417
33455
  // so the first send-message tool call returned literal "Superseded by
33418
33456
  // newer send_message call" instead of the actual peer response.
33419
33457
  responseWaiters = /* @__PURE__ */ new Map();
33458
+ /**
33459
+ * Pending delivery-ack waiters, keyed by MESSAGE ID.
33460
+ *
33461
+ * 🔴 AUD-002. This used to be keyed by PEER ID, so a peer could have only one outstanding
33462
+ * waiter and a second concurrent send displaced the first — resolving it, so the first send
33463
+ * reported "delivered" with no acknowledgment from anyone. Delivery acknowledgment is the one
33464
+ * claim this system must never fabricate.
33465
+ */
33420
33466
  deliveryAckWaiters = /* @__PURE__ */ new Map();
33467
+ /**
33468
+ * Acks that arrived before their waiter existed, keyed by message id.
33469
+ *
33470
+ * The recipient acknowledges over WS as soon as it receives the message, which can beat our own
33471
+ * POST response. Waiters used to be registered BEFORE the send to dodge that race, which is what
33472
+ * forced peer-keying. Buffering the early ack instead lets registration happen after the message
33473
+ * id is known, so correlation stays exact. Entries are consumed once and expire.
33474
+ */
33475
+ recentAcks = /* @__PURE__ */ new Map();
33421
33476
  sendDeliveryAckFn = null;
33422
33477
  /**
33423
33478
  * Set the local peer info. Called from register-peer tool after
@@ -33586,12 +33641,6 @@ var init_auto_relay = __esm({
33586
33641
  cancelResponseWaiter(fromPeerId) {
33587
33642
  this._cancelWaiter(fromPeerId);
33588
33643
  }
33589
- /**
33590
- * Cancel only the active delivery ack waiter for a peer.
33591
- */
33592
- cancelDeliveryAckWaiter(fromPeerId) {
33593
- this._cancelDeliveryAckWaiter(fromPeerId);
33594
- }
33595
33644
  /**
33596
33645
  * Cancel ALL pending response waiters for a peer by rejecting them with
33597
33646
  * a clear error. Used by clearExpectation()/cancelResponseWaiter() to
@@ -33614,39 +33663,73 @@ var init_auto_relay = __esm({
33614
33663
  * resolves when the target peer's MCP sends a delivery_ack frame, or
33615
33664
  * rejects on timeout (recipient likely offline).
33616
33665
  */
33617
- waitForDeliveryAck(fromPeerId, timeoutMs) {
33666
+ waitForDeliveryAck(fromPeerId, messageId, timeoutMs) {
33667
+ if (this.recentAcks.delete(messageId)) {
33668
+ logger.debug(`Auto-relay: delivery ack for ${messageId} was already buffered`);
33669
+ return Promise.resolve();
33670
+ }
33618
33671
  return new Promise((resolve, reject) => {
33619
- this._cancelDeliveryAckWaiter(fromPeerId);
33620
33672
  const timer = setTimeout(() => {
33621
- this.deliveryAckWaiters.delete(fromPeerId);
33622
- reject(new Error(`Delivery ack timeout from ${fromPeerId} after ${timeoutMs}ms`));
33673
+ this.deliveryAckWaiters.delete(messageId);
33674
+ reject(
33675
+ new Error(
33676
+ `Delivery ack timeout from ${fromPeerId} for ${messageId} after ${timeoutMs}ms`
33677
+ )
33678
+ );
33623
33679
  }, timeoutMs);
33624
- this.deliveryAckWaiters.set(fromPeerId, { resolve, timer });
33680
+ this.deliveryAckWaiters.set(messageId, { peerId: fromPeerId, resolve, reject, timer });
33625
33681
  });
33626
33682
  }
33627
33683
  /**
33628
- * Resolve a pending delivery ack waiter. Called by startup.ts onDeliveryAck
33629
- * callback when the server routes a delivery_ack frame to us.
33684
+ * Settle the waiter for exactly this message.
33685
+ *
33686
+ * 🔴 Correlation is by MESSAGE ID, and the acknowledging peer must match the peer we sent to.
33687
+ * The frame has carried `messageId` all along (src/cloud/ws-client.ts:52); startup.ts used to
33688
+ * drop it and resolve by peer, so any ack settled any outstanding send to that peer — and an ack
33689
+ * naming a peer the sender never messaged could settle a waiter outright (AUD-003).
33690
+ *
33691
+ * An ack with no matching waiter is BUFFERED rather than discarded: the recipient can legitimately
33692
+ * acknowledge before our POST response returns.
33630
33693
  */
33631
- resolveDeliveryAck(fromPeerId) {
33632
- const waiter = this.deliveryAckWaiters.get(fromPeerId);
33694
+ resolveDeliveryAck(fromPeerId, messageId) {
33695
+ const waiter = this.deliveryAckWaiters.get(messageId);
33633
33696
  if (waiter) {
33697
+ if (waiter.peerId !== fromPeerId) {
33698
+ logger.warn(
33699
+ `Auto-relay: IGNORED delivery ack for ${messageId} \u2014 claimed by '${fromPeerId}' but that message was sent to '${waiter.peerId}'`
33700
+ );
33701
+ return;
33702
+ }
33634
33703
  clearTimeout(waiter.timer);
33635
- this.deliveryAckWaiters.delete(fromPeerId);
33704
+ this.deliveryAckWaiters.delete(messageId);
33636
33705
  waiter.resolve();
33637
- logger.debug(`Auto-relay: delivery ack received from ${fromPeerId}`);
33706
+ logger.debug(`Auto-relay: delivery ack received from ${fromPeerId} for ${messageId}`);
33707
+ return;
33638
33708
  }
33709
+ this._bufferEarlyAck(messageId);
33710
+ }
33711
+ /** Remember an ack whose waiter does not exist yet, with bounded retention. */
33712
+ _bufferEarlyAck(messageId) {
33713
+ const now = Date.now();
33714
+ for (const [id, at] of this.recentAcks) {
33715
+ if (now - at > EARLY_ACK_TTL_MS) this.recentAcks.delete(id);
33716
+ }
33717
+ while (this.recentAcks.size >= EARLY_ACK_MAX) {
33718
+ const oldest = this.recentAcks.keys().next().value;
33719
+ if (oldest === void 0) break;
33720
+ this.recentAcks.delete(oldest);
33721
+ }
33722
+ this.recentAcks.set(messageId, now);
33723
+ logger.debug(`Auto-relay: buffered early delivery ack for ${messageId}`);
33639
33724
  }
33640
- /**
33641
- * Cancel a pending delivery ack waiter for a peer.
33642
- * Resolves the old promise so callers awaiting it don't hang forever.
33643
- */
33644
33725
  _cancelDeliveryAckWaiter(fromPeerId) {
33645
- const existing = this.deliveryAckWaiters.get(fromPeerId);
33646
- if (existing) {
33647
- clearTimeout(existing.timer);
33648
- this.deliveryAckWaiters.delete(fromPeerId);
33649
- existing.resolve();
33726
+ for (const [messageId, waiter] of this.deliveryAckWaiters) {
33727
+ if (waiter.peerId !== fromPeerId) continue;
33728
+ clearTimeout(waiter.timer);
33729
+ this.deliveryAckWaiters.delete(messageId);
33730
+ waiter.reject(
33731
+ new Error(`Delivery ack wait for ${messageId} cancelled (${fromPeerId})`)
33732
+ );
33650
33733
  }
33651
33734
  }
33652
33735
  /**
@@ -34149,15 +34232,18 @@ var init_auto_relay = __esm({
34149
34232
  for (const [, queue] of this.responseWaiters) {
34150
34233
  for (const waiter of queue) {
34151
34234
  clearTimeout(waiter.timer);
34152
- waiter.resolve("");
34235
+ waiter.reject(new Error("Auto-relay shut down while waiting for a response"));
34153
34236
  }
34154
34237
  }
34155
34238
  this.responseWaiters.clear();
34156
- for (const [, waiter] of this.deliveryAckWaiters) {
34239
+ for (const [messageId, waiter] of this.deliveryAckWaiters) {
34157
34240
  clearTimeout(waiter.timer);
34158
- waiter.resolve();
34241
+ waiter.reject(
34242
+ new Error(`Auto-relay shut down while waiting for delivery ack (${messageId})`)
34243
+ );
34159
34244
  }
34160
34245
  this.deliveryAckWaiters.clear();
34246
+ this.recentAcks.clear();
34161
34247
  this.queue = [];
34162
34248
  this.awarenessOnly.clear();
34163
34249
  this.localPeerId = null;
@@ -34580,7 +34666,7 @@ function createCloudWsClient(endpoint, sessionId, token, http, inbox, pollInterv
34580
34666
  logger.warn(`WebSocket error frame: ${err.code}: ${err.message}`);
34581
34667
  },
34582
34668
  onDeliveryAck: (payload) => {
34583
- autoRelay.resolveDeliveryAck(payload.peerId);
34669
+ autoRelay.resolveDeliveryAck(payload.peerId, payload.messageId);
34584
34670
  },
34585
34671
  onPeerEvicted: () => {
34586
34672
  triggerReRegistration().catch((err) => {
@@ -34872,6 +34958,10 @@ var init_mail_credential_store = __esm({
34872
34958
 
34873
34959
  // src/tools/register-peer.ts
34874
34960
  import crypto6 from "node:crypto";
34961
+ function unresolvedSessionReason(r) {
34962
+ if (r.sessionId) return null;
34963
+ return r.candidateCount === 0 ? "no-candidates" : "ambiguous";
34964
+ }
34875
34965
  function registerRegisterPeerTool(server) {
34876
34966
  server.registerTool(
34877
34967
  "cogent_register_peer",
@@ -34883,6 +34973,9 @@ function registerRegisterPeerTool(server) {
34883
34973
  sessionId: import_zod3.z.string().describe("Claude Code session ID (used with --resume)"),
34884
34974
  cwd: import_zod3.z.string().describe("Absolute path to the project working directory"),
34885
34975
  label: import_zod3.z.string().describe("Human-readable label, e.g. 'Cogent_Backend' or 'Cogent_Frontend'"),
34976
+ peerSecret: import_zod3.z.string().optional().describe(
34977
+ "AUD-003 ownership proof. Only needed to RECLAIM a peerId whose registration was made from a different connection \u2014 e.g. this agent re-joined the channel and was issued a new token. Normally omitted: the stored credential is replayed automatically."
34978
+ ),
34886
34979
  mode: import_zod3.z.enum(["observer", "agent"]).optional().default("agent").describe(
34887
34980
  'Peer participation mode. "agent" (default) auto-responds via execClaude on inbound directed messages. "observer" registers a passive watcher that receives WS frames and remains targetable but never spawns a subprocess. Useful for operators using their MCP client to monitor a channel without polluting transcript with phantom replies.'
34888
34981
  ),
@@ -34897,7 +34990,7 @@ function registerRegisterPeerTool(server) {
34897
34990
  openWorldHint: false
34898
34991
  }
34899
34992
  },
34900
- async ({ peerId, sessionId, cwd, label, mode, role }) => {
34993
+ async ({ peerId, sessionId, cwd, label, mode, role, peerSecret: argPeerSecret }) => {
34901
34994
  try {
34902
34995
  const config2 = getConfig();
34903
34996
  const hasExplicitCloudSession = !!config2.COGENT_SESSION_ID || process.env.COGENT_TRUST_PERSISTED_CREDENTIALS === "1";
@@ -34990,6 +35083,14 @@ function registerRegisterPeerTool(server) {
34990
35083
  }
34991
35084
  const workspaceId = deriveWorkspaceId(cwd);
34992
35085
  const threadId = sessionId;
35086
+ let storedPeerSecret = argPeerSecret;
35087
+ if (storedPeerSecret === void 0 && isCloudMode()) {
35088
+ try {
35089
+ storedPeerSecret = (await loadCredentials())?.peerSecret;
35090
+ } catch {
35091
+ storedPeerSecret = void 0;
35092
+ }
35093
+ }
34993
35094
  const peer = await backend.registerPeer(
34994
35095
  peerId,
34995
35096
  sessionId,
@@ -35000,15 +35101,18 @@ function registerRegisterPeerTool(server) {
35000
35101
  channelSessionId,
35001
35102
  capabilities,
35002
35103
  workspaceId,
35003
- threadId
35104
+ threadId,
35105
+ storedPeerSecret
35004
35106
  );
35005
35107
  if (isCloudMode()) {
35006
35108
  try {
35007
35109
  const creds = await loadCredentials();
35008
- if (creds && creds.peerId !== peerId) {
35110
+ const issuedSecret = peer?.peerSecret;
35111
+ if (creds && (creds.peerId !== peerId || issuedSecret && creds.peerSecret !== issuedSecret)) {
35009
35112
  await saveCredentials({
35010
35113
  ...creds,
35011
35114
  peerId,
35115
+ ...issuedSecret ? { peerSecret: issuedSecret } : {},
35012
35116
  savedAt: (/* @__PURE__ */ new Date()).toISOString()
35013
35117
  });
35014
35118
  }
@@ -35036,16 +35140,17 @@ function registerRegisterPeerTool(server) {
35036
35140
  logger.info(
35037
35141
  `Register: session resolved ${resolution.decision}/${resolution.confidence} (${resolution.candidateCount} candidate[s]) -> ${localSessionId}`
35038
35142
  );
35039
- const noLocalSession = resolution.candidateCount === 0;
35040
- if (noLocalSession) {
35143
+ const unresolved = unresolvedSessionReason(resolution);
35144
+ if (unresolved) {
35041
35145
  try {
35042
35146
  const base = getConfig().COGENT_PLATFORM === "cc" ? await transcriptBaseSummary() : "(codex: scans CODEX_HOME/sessions)";
35147
+ const cause = unresolved === "ambiguous" ? `${resolution.candidateCount} candidate sessions matched and NONE could be chosen, so the id you supplied was pinned UNCHECKED \u2014 if that is a channel UUID rather than a local session id, every wake will fail in ~2s` : `NO local ${getConfig().COGENT_PLATFORM} session was found for this cwd`;
35043
35148
  logger.warn(
35044
- `Register: NO local ${getConfig().COGENT_PLATFORM} session found for cwd \u2014 auto-wake WILL be refused. cwd=${cwd}; scanned ${(resolution.probedPaths ?? []).join(", ") || "(unknown)"}; base ${base}. If the base is missing or empty, this agent's config dir is elsewhere \u2014 set CLAUDE_CONFIG_DIR / CODEX_HOME so the BRIDGE process sees it too. If the base has project dirs but none match, the registered cwd is not where this agent runs.`
35149
+ `Register: ${cause} \u2014 auto-wake WILL be refused. cwd=${cwd}; scanned ${(resolution.probedPaths ?? []).join(", ") || "(unknown)"}; base ${base}. If the base is missing or empty, this agent's config dir is elsewhere \u2014 set CLAUDE_CONFIG_DIR / CODEX_HOME so the BRIDGE process sees it too. If the base has project dirs but none match, the registered cwd is not where this agent runs.`
35045
35150
  );
35046
35151
  } catch (err) {
35047
35152
  logger.warn(
35048
- `Register: NO local session found for cwd=${cwd} (auto-wake WILL be refused); could not describe the transcript base: ${err.message}`
35153
+ `Register: unresolved session (${unresolved}) for cwd=${cwd} (auto-wake WILL be refused); could not describe the transcript base: ${err.message}`
35049
35154
  );
35050
35155
  }
35051
35156
  }
@@ -35059,8 +35164,8 @@ function registerRegisterPeerTool(server) {
35059
35164
  confidence: resolution.confidence,
35060
35165
  candidateCount: resolution.candidateCount,
35061
35166
  // Only on the broken path — keeps the happy-path result terse.
35062
- ...noLocalSession ? {
35063
- warning: "No local session transcript found for this cwd \u2014 auto-wake will be REFUSED. Verify the cwd is where this agent actually runs and that CLAUDE_CONFIG_DIR / CODEX_HOME are visible to the bridge process.",
35167
+ ...unresolved ? {
35168
+ warning: (unresolved === "ambiguous" ? `${resolution.candidateCount} local session transcripts matched this cwd and none could be chosen, so the sessionId you supplied was pinned UNCHECKED \u2014 if it is a channel UUID rather than a local session id, auto-wake will be REFUSED. ` : "No local session transcript found for this cwd \u2014 auto-wake will be REFUSED. ") + "Verify the cwd is where this agent actually runs and that CLAUDE_CONFIG_DIR / CODEX_HOME are visible to the bridge process.",
35064
35169
  scannedPaths: resolution.probedPaths ?? []
35065
35170
  } : {}
35066
35171
  }
@@ -35276,8 +35381,9 @@ function registerSendMessageTool(server) {
35276
35381
  if (isCloud && success2) {
35277
35382
  const effectiveAckTimeout = Math.min(ACK_TIMEOUT_MS, config2.COGENT_TIMEOUT_MS);
35278
35383
  const fastPathMs = Math.min(config2.COGENT_FASTPATH_MS, config2.COGENT_TIMEOUT_MS);
35279
- const ackPromise = autoRelay.waitForDeliveryAck(toPeerId, effectiveAckTimeout);
35280
35384
  const responsePromise = autoRelay.waitForResponse(toPeerId, fastPathMs);
35385
+ responsePromise.catch(() => {
35386
+ });
35281
35387
  let outboundRecord;
35282
35388
  try {
35283
35389
  outboundRecord = await backend.recordMessage({
@@ -35293,6 +35399,11 @@ function registerSendMessageTool(server) {
35293
35399
  autoRelay.clearExpectation(toPeerId);
35294
35400
  throw recordErr;
35295
35401
  }
35402
+ const ackPromise = autoRelay.waitForDeliveryAck(
35403
+ toPeerId,
35404
+ outboundRecord.id,
35405
+ effectiveAckTimeout
35406
+ );
35296
35407
  let ackReceived = false;
35297
35408
  try {
35298
35409
  await ackPromise;
@@ -35473,6 +35584,7 @@ var init_message = __esm({
35473
35584
  timestamp: import_zod6.z.string().datetime().describe("ISO 8601 message timestamp"),
35474
35585
  durationMs: import_zod6.z.number().nullable().describe("Round-trip duration in ms"),
35475
35586
  success: import_zod6.z.boolean().describe("Whether delivery succeeded"),
35587
+ pending: import_zod6.z.boolean().optional().describe("Delivered+acked; the reply is still expected"),
35476
35588
  error: import_zod6.z.string().nullable().describe("Error message if delivery failed"),
35477
35589
  originPlatform: import_zod6.z.enum(["cc", "codex", "slack", "gchat", "web", "gemini", "whatsapp", "telegram", "discord"]).optional().describe("Platform that originated this message"),
35478
35590
  attachments: import_zod6.z.array(AttachmentSchema).optional().describe("Cogent Mail M2.5 \u2014 file references (byte transport = email)")
@@ -35562,15 +35674,31 @@ var init_errors5 = __esm({
35562
35674
  });
35563
35675
 
35564
35676
  // cogent/dist/schemas/api-requests.js
35565
- var import_zod10, CreateSessionRequestSchema, JoinSessionRequestSchema, ResolveSessionRequestSchema, RegisterPeerRequestSchema, DeregisterPeerRequestSchema, SendMessageRequestSchema, GetHistoryQuerySchema, PollQuerySchema;
35677
+ var import_zod10, FIELD_LIMITS, CreateSessionRequestSchema, JoinSessionRequestSchema, ResolveSessionRequestSchema, RegisterPeerRequestSchema, DeregisterPeerRequestSchema, SendMessageRequestSchema, GetHistoryQuerySchema, PollQuerySchema;
35566
35678
  var init_api_requests = __esm({
35567
35679
  "cogent/dist/schemas/api-requests.js"() {
35568
35680
  "use strict";
35569
35681
  import_zod10 = __toESM(require_zod(), 1);
35682
+ FIELD_LIMITS = {
35683
+ /** A shared channel secret. Long passphrases are fine; novels are not. */
35684
+ SECRET_MAX: 1024,
35685
+ /** Org_ID. */
35686
+ ORG_ID_MAX: 256,
35687
+ /** Peer identifier. */
35688
+ PEER_ID_MAX: 128,
35689
+ /** Human-readable peer label. */
35690
+ PEER_LABEL_MAX: 256,
35691
+ /** Working directory — comfortably above PATH_MAX (4096) on every platform we support. */
35692
+ CWD_MAX: 8192,
35693
+ /** A single message body. */
35694
+ MESSAGE_MAX: 1048576,
35695
+ /** One platform-identity value (e.g. a Slack user id). */
35696
+ PLATFORM_IDENTITY_VALUE_MAX: 512
35697
+ };
35570
35698
  CreateSessionRequestSchema = import_zod10.z.object({
35571
35699
  label: import_zod10.z.string().regex(/^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$/).optional().describe("Optional human-readable session label (3-32 lowercase alphanumeric + hyphens)"),
35572
- secret: import_zod10.z.string().min(8).describe("Shared secret for joining the session (min 8 chars)"),
35573
- orgId: import_zod10.z.string().min(1).optional().describe("Optional Org_ID -- gates the channel to Business Edition orgs (multi-tenant relay only)")
35700
+ secret: import_zod10.z.string().min(8).max(FIELD_LIMITS.SECRET_MAX).describe("Shared secret for joining the session (min 8 chars)"),
35701
+ orgId: import_zod10.z.string().min(1).max(FIELD_LIMITS.ORG_ID_MAX).optional().describe("Optional Org_ID -- gates the channel to Business Edition orgs (multi-tenant relay only)")
35574
35702
  }).strict();
35575
35703
  JoinSessionRequestSchema = import_zod10.z.object({
35576
35704
  secret: import_zod10.z.string().min(1).describe("Shared secret for the session"),
@@ -35581,13 +35709,14 @@ var init_api_requests = __esm({
35581
35709
  orgId: import_zod10.z.string().min(1).optional().describe("Optional Org_ID -- required to resolve an Org_ID-gated business channel")
35582
35710
  }).strict();
35583
35711
  RegisterPeerRequestSchema = import_zod10.z.object({
35584
- peerId: import_zod10.z.string().min(1).describe("Unique peer identifier"),
35585
- cwd: import_zod10.z.string().describe("Peer working directory path"),
35586
- label: import_zod10.z.string().describe("Human-readable peer label"),
35712
+ peerId: import_zod10.z.string().min(1).max(FIELD_LIMITS.PEER_ID_MAX).describe("Unique peer identifier"),
35713
+ peerSecret: import_zod10.z.string().min(1).max(256).optional().describe("AUD-003: proof of ownership when reclaiming a peer from a different token"),
35714
+ cwd: import_zod10.z.string().max(FIELD_LIMITS.CWD_MAX).describe("Peer working directory path"),
35715
+ label: import_zod10.z.string().max(FIELD_LIMITS.PEER_LABEL_MAX).describe("Human-readable peer label"),
35587
35716
  platform: import_zod10.z.enum(["cc", "codex", "slack", "gchat", "web", "gemini"]).optional().describe("Platform this peer belongs to"),
35588
35717
  type: import_zod10.z.enum(["agent", "human"]).optional().describe("Peer type"),
35589
35718
  transport: import_zod10.z.enum(["ws", "slack-bot", "gchat-bot", "websocket"]).optional().describe("Transport mechanism"),
35590
- platformIdentity: import_zod10.z.record(import_zod10.z.string(), import_zod10.z.string()).optional().describe("Platform-specific identity fields"),
35719
+ platformIdentity: import_zod10.z.record(import_zod10.z.string().max(64), import_zod10.z.string().max(FIELD_LIMITS.PLATFORM_IDENTITY_VALUE_MAX)).optional().describe("Platform-specific identity fields"),
35591
35720
  role: import_zod10.z.string().max(64).optional().describe("A2A role advertised by this peer (Agent Card)"),
35592
35721
  capabilities: import_zod10.z.array(import_zod10.z.string().max(64)).max(16).optional().describe("Capability strings \u2192 A2A Agent Card skills[]")
35593
35722
  }).strict();
@@ -35595,8 +35724,11 @@ var init_api_requests = __esm({
35595
35724
  SendMessageRequestSchema = import_zod10.z.object({
35596
35725
  fromPeerId: import_zod10.z.string().min(1).describe("Sender peer ID"),
35597
35726
  toPeerId: import_zod10.z.string().min(1).describe("Recipient peer ID (use '*' for broadcast)"),
35598
- message: import_zod10.z.string().min(1).describe("Message content"),
35599
- originPlatform: import_zod10.z.enum(["cc", "codex", "slack", "gchat", "web", "gemini"]).optional().describe("Platform originating this message")
35727
+ message: import_zod10.z.string().min(1).max(FIELD_LIMITS.MESSAGE_MAX).describe("Message content"),
35728
+ originPlatform: import_zod10.z.enum(["cc", "codex", "slack", "gchat", "web", "gemini"]).optional().describe("Platform originating this message"),
35729
+ success: import_zod10.z.boolean().optional().describe("Delivery outcome as reported by the sender"),
35730
+ error: import_zod10.z.string().nullable().optional().describe("Failure code as reported by the sender"),
35731
+ durationMs: import_zod10.z.number().int().nonnegative().nullable().optional().describe("Sender-measured duration")
35600
35732
  }).strict();
35601
35733
  GetHistoryQuerySchema = import_zod10.z.object({
35602
35734
  peerId: import_zod10.z.string().optional().describe("Filter messages by peer ID"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@essentialai/cogent-bridge-bundled",
3
- "version": "3.21.9",
3
+ "version": "3.22.0",
4
4
  "description": "Zero-dependency, pre-bundled Cogent MCP bridge — a single self-contained file for fast cold `npx` startup (used by the Codex plugin, whose MCP startup window is short). Functionally identical to @essentialai/cogent-bridge; carries no npm dependencies so `npx` skips dependency install.",
5
5
  "type": "module",
6
6
  "bin": {