@essentialai/cogent-plugin 3.25.5 → 3.25.7

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.
@@ -625,6 +625,20 @@ function hasJoinedInProcess() {
625
625
  function markCloudAttemptInProcess() {
626
626
  cloudAttemptInProcessFlag = true;
627
627
  joinedInProcessFlag = false;
628
+ cloudAttemptGeneration++;
629
+ for (const fn of cloudAttemptListeners) {
630
+ try {
631
+ fn();
632
+ } catch {
633
+ }
634
+ }
635
+ }
636
+ function getCloudAttemptGeneration() {
637
+ return cloudAttemptGeneration;
638
+ }
639
+ function onCloudAttemptInProcess(fn) {
640
+ cloudAttemptListeners.add(fn);
641
+ return () => cloudAttemptListeners.delete(fn);
628
642
  }
629
643
  function hasCloudAttemptInProcess() {
630
644
  return cloudAttemptInProcessFlag;
@@ -662,7 +676,7 @@ async function legacyGlobalCredentialsExist() {
662
676
  return false;
663
677
  }
664
678
  }
665
- var LEGACY_GLOBAL_CREDENTIAL_PATH, joinedInProcessFlag, cloudAttemptInProcessFlag;
679
+ var LEGACY_GLOBAL_CREDENTIAL_PATH, joinedInProcessFlag, cloudAttemptInProcessFlag, cloudAttemptGeneration, cloudAttemptListeners;
666
680
  var init_credential_store = __esm({
667
681
  "src/cloud/credential-store.ts"() {
668
682
  "use strict";
@@ -672,6 +686,8 @@ var init_credential_store = __esm({
672
686
  );
673
687
  joinedInProcessFlag = false;
674
688
  cloudAttemptInProcessFlag = false;
689
+ cloudAttemptGeneration = 0;
690
+ cloudAttemptListeners = /* @__PURE__ */ new Set();
675
691
  }
676
692
  });
677
693
 
@@ -1266,8 +1282,8 @@ var init_parseUtil = __esm({
1266
1282
  init_errors();
1267
1283
  init_en();
1268
1284
  makeIssue = (params) => {
1269
- const { data, path: path26, errorMaps, issueData } = params;
1270
- const fullPath = [...path26, ...issueData.path || []];
1285
+ const { data, path: path27, errorMaps, issueData } = params;
1286
+ const fullPath = [...path27, ...issueData.path || []];
1271
1287
  const fullIssue = {
1272
1288
  ...issueData,
1273
1289
  path: fullPath
@@ -1547,11 +1563,11 @@ var init_types = __esm({
1547
1563
  init_parseUtil();
1548
1564
  init_util();
1549
1565
  ParseInputLazyPath = class {
1550
- constructor(parent, value, path26, key) {
1566
+ constructor(parent, value, path27, key) {
1551
1567
  this._cachedPath = [];
1552
1568
  this.parent = parent;
1553
1569
  this.data = value;
1554
- this._path = path26;
1570
+ this._path = path27;
1555
1571
  this._key = key;
1556
1572
  }
1557
1573
  get path() {
@@ -5003,10 +5019,10 @@ function assignProp(target, prop, value) {
5003
5019
  configurable: true
5004
5020
  });
5005
5021
  }
5006
- function getElementAtPath(obj, path26) {
5007
- if (!path26)
5022
+ function getElementAtPath(obj, path27) {
5023
+ if (!path27)
5008
5024
  return obj;
5009
- return path26.reduce((acc, key) => acc?.[key], obj);
5025
+ return path27.reduce((acc, key) => acc?.[key], obj);
5010
5026
  }
5011
5027
  function promiseAllObject(promisesObj) {
5012
5028
  const keys = Object.keys(promisesObj);
@@ -5255,11 +5271,11 @@ function aborted(x, startIndex = 0) {
5255
5271
  }
5256
5272
  return false;
5257
5273
  }
5258
- function prefixIssues(path26, issues) {
5274
+ function prefixIssues(path27, issues) {
5259
5275
  return issues.map((iss) => {
5260
5276
  var _a;
5261
5277
  (_a = iss).path ?? (_a.path = []);
5262
- iss.path.unshift(path26);
5278
+ iss.path.unshift(path27);
5263
5279
  return iss;
5264
5280
  });
5265
5281
  }
@@ -17065,8 +17081,8 @@ var require_utils = __commonJS({
17065
17081
  }
17066
17082
  return ind;
17067
17083
  }
17068
- function removeDotSegments(path26) {
17069
- let input = path26;
17084
+ function removeDotSegments(path27) {
17085
+ let input = path27;
17070
17086
  const output = [];
17071
17087
  let nextSlash = -1;
17072
17088
  let len = 0;
@@ -17265,8 +17281,8 @@ var require_schemes = __commonJS({
17265
17281
  wsComponent.secure = void 0;
17266
17282
  }
17267
17283
  if (wsComponent.resourceName) {
17268
- const [path26, query] = wsComponent.resourceName.split("?");
17269
- wsComponent.path = path26 && path26 !== "/" ? path26 : void 0;
17284
+ const [path27, query] = wsComponent.resourceName.split("?");
17285
+ wsComponent.path = path27 && path27 !== "/" ? path27 : void 0;
17270
17286
  wsComponent.query = query;
17271
17287
  wsComponent.resourceName = void 0;
17272
17288
  }
@@ -20619,12 +20635,12 @@ var require_dist = __commonJS({
20619
20635
  throw new Error(`Unknown format "${name}"`);
20620
20636
  return f;
20621
20637
  };
20622
- function addFormats(ajv, list, fs24, exportName) {
20638
+ function addFormats(ajv, list, fs25, exportName) {
20623
20639
  var _a;
20624
20640
  var _b;
20625
20641
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
20626
20642
  for (const f of list)
20627
- ajv.addFormat(f, fs24[f]);
20643
+ ajv.addFormat(f, fs25[f]);
20628
20644
  }
20629
20645
  module.exports = exports = formatsPlugin;
20630
20646
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -21736,8 +21752,8 @@ var require_parseUtil = __commonJS({
21736
21752
  var errors_js_1 = require_errors2();
21737
21753
  var en_js_1 = __importDefault(require_en());
21738
21754
  var makeIssue2 = (params) => {
21739
- const { data, path: path26, errorMaps, issueData } = params;
21740
- const fullPath = [...path26, ...issueData.path || []];
21755
+ const { data, path: path27, errorMaps, issueData } = params;
21756
+ const fullPath = [...path27, ...issueData.path || []];
21741
21757
  const fullIssue = {
21742
21758
  ...issueData,
21743
21759
  path: fullPath
@@ -21891,11 +21907,11 @@ var require_types2 = __commonJS({
21891
21907
  var parseUtil_js_1 = require_parseUtil();
21892
21908
  var util_js_1 = require_util2();
21893
21909
  var ParseInputLazyPath2 = class {
21894
- constructor(parent, value, path26, key) {
21910
+ constructor(parent, value, path27, key) {
21895
21911
  this._cachedPath = [];
21896
21912
  this.parent = parent;
21897
21913
  this.data = value;
21898
- this._path = path26;
21914
+ this._path = path27;
21899
21915
  this._key = key;
21900
21916
  }
21901
21917
  get path() {
@@ -26396,8 +26412,8 @@ var init_stdio2 = __esm({
26396
26412
  // src/constants.ts
26397
26413
  import { createRequire } from "node:module";
26398
26414
  function resolveVersion() {
26399
- if ("3.25.5") {
26400
- return "3.25.5";
26415
+ if ("3.25.7") {
26416
+ return "3.25.7";
26401
26417
  }
26402
26418
  try {
26403
26419
  const require2 = createRequire(import.meta.url);
@@ -26416,6 +26432,34 @@ var init_constants = __esm({
26416
26432
  }
26417
26433
  });
26418
26434
 
26435
+ // src/instructions.ts
26436
+ var ENV_KEY_NAMES_COMMAND, SERVER_INSTRUCTIONS;
26437
+ var init_instructions = __esm({
26438
+ "src/instructions.ts"() {
26439
+ "use strict";
26440
+ ENV_KEY_NAMES_COMMAND = "grep -noE '^#COGENT[A-Za-z0-9_ -]*|^(CHANNEL|COG[A-Z]*NT)_(CHANNEL_)?(NAME|PASSWORD|ORG_ID|PEER_NAME|PEER_LABEL)=' .env";
26441
+ SERVER_INSTRUCTIONS = [
26442
+ "Cogent connects this agent to a shared channel with other agents and with people in Slack. Follow the steps below only when the user asks you to register, join or get online on Cogent; never read .env or register unprompted.",
26443
+ "To get online:",
26444
+ "1. cogent_join_session { channel, secret, orgId? }. channel is the channel NAME (3-32 characters: lowercase letters, digits, hyphens), never a UUID. If you were given an Org_ID (Team channel) pass it as orgId on every call and never retry without it.",
26445
+ "2. Only if step 1 answers CHANNEL_NOT_FOUND: cogent_create_session { label, secret, orgId? } with the SAME name, password and Org_ID. It creates the channel and joins you. Never change the name. Do not repeat step 1 first: five failed joins lock the name for 15 minutes.",
26446
+ "3. cogent_register_peer { peerId, sessionId, cwd, label }. peerId is the peer name you were given. sessionId is this agent session's own resumable id, not the channel id. Do not call this first.",
26447
+ "Values come from the user's message, else from the project .env: the keys ending in CHANNEL_NAME, CHANNEL_PASSWORD, ORG_ID, PEER_NAME and PEER_LABEL. To find them, list the key NAMES only:",
26448
+ ENV_KEY_NAMES_COMMAND,
26449
+ "Then pass the values straight into the tool call. Do not print them and do not write a script that prints them. A key with a misspelled prefix is still used: report its KEY NAME. If the channel name or peer name is missing, or appears more than once, ask. Never invent either.",
26450
+ "Errors:",
26451
+ "- CHANNEL_NOT_FOUND: do step 2 once. Exception: if you were given an Org_ID and left it out, do step 1 again WITH orgId and create nothing.",
26452
+ "- 'already in use' from create: the channel exists. Do step 1 again with the same name.",
26453
+ "- Invalid credentials or Invalid session secret (HTTP 401): the password or Org_ID is wrong. Stop and ask.",
26454
+ "- RATE_LIMITED or HTTP 429: wait the seconds it states, then repeat the same call once. Change nothing.",
26455
+ "- JOIN_NOT_CONFIRMED: step 1 or 2 has not succeeded in this session. Do it, then register.",
26456
+ "- PEER_NOT_YOURS: your own earlier session holds the name. Retry the SAME peerId when the error says. Never rename.",
26457
+ "- Anything else: report it and stop.",
26458
+ "Never print the channel password or the Org_ID. When online, report the peer name, the channel name, whether you created or joined it, and the Slack command from the tool result with its placeholders left as they are."
26459
+ ].join("\n");
26460
+ }
26461
+ });
26462
+
26419
26463
  // src/logger.ts
26420
26464
  import fs5 from "node:fs";
26421
26465
  import path5 from "node:path";
@@ -26787,11 +26831,15 @@ var init_errors4 = __esm({
26787
26831
  BridgeErrorCode2["DIR_NOT_WRITABLE"] = "DIR_NOT_WRITABLE";
26788
26832
  BridgeErrorCode2["NETWORK_TIMEOUT"] = "NETWORK_TIMEOUT";
26789
26833
  BridgeErrorCode2["JOIN_NOT_CONFIRMED"] = "JOIN_NOT_CONFIRMED";
26834
+ BridgeErrorCode2["CHANNEL_NOT_FOUND"] = "CHANNEL_NOT_FOUND";
26835
+ BridgeErrorCode2["PEER_NOT_YOURS"] = "PEER_NOT_YOURS";
26790
26836
  return BridgeErrorCode2;
26791
26837
  })(BridgeErrorCode || {});
26792
26838
  BridgeError = class extends Error {
26793
26839
  code;
26794
26840
  suggestion;
26841
+ /** Relay hint: ms until a refused peerId can be reclaimed (PEER_NOT_YOURS only). */
26842
+ retryAfterMs;
26795
26843
  constructor(code, message, suggestion) {
26796
26844
  const fullMessage = suggestion ? `${code}: ${message}. ${suggestion}` : `${code}: ${message}`;
26797
26845
  super(fullMessage);
@@ -27838,7 +27886,7 @@ var init_http_backend = __esm({
27838
27886
  * in cloud mode we ignore it and use this.sessionId (the cloud session).
27839
27887
  */
27840
27888
  async registerPeer(peerId, _sessionId, cwd, label, clientVersion, mode, _channelSessionId, capabilities, workspaceId, threadId, peerSecret) {
27841
- const path26 = PATHS.peers.replace(":sessionId", this.sessionId);
27889
+ const path27 = PATHS.peers.replace(":sessionId", this.sessionId);
27842
27890
  const body = {
27843
27891
  peerId,
27844
27892
  cwd,
@@ -27869,7 +27917,7 @@ var init_http_backend = __esm({
27869
27917
  if (peerSecret !== void 0 && await relaySupports(this.http, "peer-ownership")) {
27870
27918
  body.peerSecret = peerSecret;
27871
27919
  }
27872
- const peer = await this.http.post(path26, body);
27920
+ const peer = await this.http.post(path27, body);
27873
27921
  const issued = peer?.peerSecret;
27874
27922
  if (issued) {
27875
27923
  try {
@@ -27889,9 +27937,9 @@ var init_http_backend = __esm({
27889
27937
  * local mail creds. A 404 (peer not found) resolves to `{ removed: false, ... }`, not a throw.
27890
27938
  */
27891
27939
  async deregisterPeer(peerId, peerSecret) {
27892
- const path26 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
27940
+ const path27 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
27893
27941
  try {
27894
- const headers = await this.http.delete(path26, peerSecret ? { peerSecret } : {});
27942
+ const headers = await this.http.delete(path27, peerSecret ? { peerSecret } : {});
27895
27943
  const mailboxDeprovisioned = headers.get(MAILBOX_DEPROVISIONED_HEADER) === "true";
27896
27944
  return { removed: true, mailboxDeprovisioned };
27897
27945
  } catch (err) {
@@ -27915,8 +27963,8 @@ var init_http_backend = __esm({
27915
27963
  * GET /api/sessions/:sessionId/peers
27916
27964
  */
27917
27965
  async listPeers() {
27918
- const path26 = PATHS.peers.replace(":sessionId", this.sessionId);
27919
- const result = await this.http.get(path26);
27966
+ const path27 = PATHS.peers.replace(":sessionId", this.sessionId);
27967
+ const result = await this.http.get(path27);
27920
27968
  return result.peers;
27921
27969
  }
27922
27970
  /**
@@ -27924,8 +27972,8 @@ var init_http_backend = __esm({
27924
27972
  * Calls the lightweight heartbeat endpoint on the cloud relay server.
27925
27973
  */
27926
27974
  async updateLastSeen(peerId) {
27927
- const path26 = PATHS.heartbeat.replace(":sessionId", this.sessionId);
27928
- await this.http.post(path26, { peerId });
27975
+ const path27 = PATHS.heartbeat.replace(":sessionId", this.sessionId);
27976
+ await this.http.post(path27, { peerId });
27929
27977
  }
27930
27978
  /**
27931
27979
  * Record a message in the cloud session.
@@ -27936,7 +27984,7 @@ var init_http_backend = __esm({
27936
27984
  * the input record to construct a full MessageRecord.
27937
27985
  */
27938
27986
  async recordMessage(record2) {
27939
- const path26 = PATHS.messages.replace(":sessionId", this.sessionId);
27987
+ const path27 = PATHS.messages.replace(":sessionId", this.sessionId);
27940
27988
  const body = {
27941
27989
  fromPeerId: record2.fromPeerId,
27942
27990
  toPeerId: record2.toPeerId,
@@ -27962,7 +28010,7 @@ var init_http_backend = __esm({
27962
28010
  if (record2.replyExpected === false && await relaySupports(this.http, "terminal-messages")) {
27963
28011
  body.replyExpected = false;
27964
28012
  }
27965
- const resp = await this.http.post(path26, body);
28013
+ const resp = await this.http.post(path27, body);
27966
28014
  return {
27967
28015
  ...record2,
27968
28016
  id: resp.id,
@@ -27976,7 +28024,7 @@ var init_http_backend = __esm({
27976
28024
  * Supports optional peerId filter and limit (default 300).
27977
28025
  */
27978
28026
  async getHistory(peerId, limit, includeRelayEchoes, metadataOnly, dedupInferredEchoes) {
27979
- const path26 = PATHS.messages.replace(":sessionId", this.sessionId);
28027
+ const path27 = PATHS.messages.replace(":sessionId", this.sessionId);
27980
28028
  const query = {};
27981
28029
  if (peerId) {
27982
28030
  query.peerId = peerId;
@@ -27991,7 +28039,7 @@ var init_http_backend = __esm({
27991
28039
  if (dedupInferredEchoes) {
27992
28040
  query.dedupInferredEchoes = "true";
27993
28041
  }
27994
- const result = await this.http.get(path26, query);
28042
+ const result = await this.http.get(path27, query);
27995
28043
  return result.messages;
27996
28044
  }
27997
28045
  /**
@@ -28002,12 +28050,12 @@ var init_http_backend = __esm({
28002
28050
  * so that get-history shows the actual response (not null).
28003
28051
  */
28004
28052
  async updateMessageResponse(messageId, response, durationMs, pending) {
28005
- const path26 = PATHS.message.replace(":sessionId", this.sessionId).replace(":messageId", messageId);
28053
+ const path27 = PATHS.message.replace(":sessionId", this.sessionId).replace(":messageId", messageId);
28006
28054
  const body = { response, durationMs };
28007
28055
  if (pending === true) {
28008
28056
  body.pending = true;
28009
28057
  }
28010
- await this.http.patch(path26, body);
28058
+ await this.http.patch(path27, body);
28011
28059
  }
28012
28060
  /**
28013
28061
  * Validate a session.
@@ -28181,8 +28229,8 @@ var init_http_client = __esm({
28181
28229
  * Perform an authenticated GET request.
28182
28230
  * Appends query parameters to the URL if provided.
28183
28231
  */
28184
- async get(path26, query) {
28185
- const url = new URL(path26, this.baseUrl);
28232
+ async get(path27, query) {
28233
+ const url = new URL(path27, this.baseUrl);
28186
28234
  if (query) {
28187
28235
  for (const [k, v] of Object.entries(query)) {
28188
28236
  url.searchParams.set(k, v);
@@ -28197,8 +28245,8 @@ var init_http_client = __esm({
28197
28245
  /**
28198
28246
  * Perform an authenticated POST request with a JSON body.
28199
28247
  */
28200
- async post(path26, body) {
28201
- const url = new URL(path26, this.baseUrl);
28248
+ async post(path27, body) {
28249
+ const url = new URL(path27, this.baseUrl);
28202
28250
  const resp = await this.fetchWithTimeout(url, {
28203
28251
  method: "POST",
28204
28252
  headers: this.headers(),
@@ -28216,8 +28264,8 @@ var init_http_client = __esm({
28216
28264
  * Perform an authenticated PATCH request with a JSON body.
28217
28265
  * Used to update existing resources (e.g., message response fields).
28218
28266
  */
28219
- async patch(path26, body) {
28220
- const url = new URL(path26, this.baseUrl);
28267
+ async patch(path27, body) {
28268
+ const url = new URL(path27, this.baseUrl);
28221
28269
  const resp = await this.fetchWithTimeout(url, {
28222
28270
  method: "PATCH",
28223
28271
  headers: this.headers(),
@@ -28242,8 +28290,8 @@ var init_http_client = __esm({
28242
28290
  * "Malformed JSON in request body" 400 from cogent-server <=3.1.2 when
28243
28291
  * any client (including third-party tools) DELETEd a peer without a body.
28244
28292
  */
28245
- async delete(path26, body) {
28246
- const url = new URL(path26, this.baseUrl);
28293
+ async delete(path27, body) {
28294
+ const url = new URL(path27, this.baseUrl);
28247
28295
  const headers = {
28248
28296
  "Authorization": `Bearer ${this.token}`
28249
28297
  };
@@ -28278,12 +28326,16 @@ var init_http_client = __esm({
28278
28326
  } catch {
28279
28327
  errorBody = { message: resp.statusText };
28280
28328
  }
28281
- const code = errorBody.code && errorBody.code in BridgeErrorCode ? errorBody.code : "STARTUP_FAILED" /* STARTUP_FAILED */;
28282
- throw new BridgeError(
28329
+ const code = /PEER_NOT_YOURS/.test(errorBody.message ?? "") ? "PEER_NOT_YOURS" /* PEER_NOT_YOURS */ : errorBody.code && errorBody.code in BridgeErrorCode ? errorBody.code : "STARTUP_FAILED" /* STARTUP_FAILED */;
28330
+ const err = new BridgeError(
28283
28331
  code,
28284
28332
  errorBody.message ?? `HTTP ${resp.status}`,
28285
28333
  errorBody.suggestion
28286
28334
  );
28335
+ if (typeof errorBody.retryAfterMs === "number" && Number.isFinite(errorBody.retryAfterMs)) {
28336
+ err.retryAfterMs = errorBody.retryAfterMs;
28337
+ }
28338
+ throw err;
28287
28339
  }
28288
28340
  };
28289
28341
  }
@@ -32540,14 +32592,14 @@ var init_ws_client = __esm({
32540
32592
  this.restoreCursor();
32541
32593
  const poll = async () => {
32542
32594
  try {
32543
- const path26 = `/api/sessions/${this.opts.sessionId}/poll`;
32595
+ const path27 = `/api/sessions/${this.opts.sessionId}/poll`;
32544
32596
  const query = {
32545
32597
  peerId: this.opts.peerId
32546
32598
  };
32547
32599
  if (this.lastMessageId) {
32548
32600
  query.lastMessageId = this.lastMessageId;
32549
32601
  }
32550
- const result = await this.opts.http.get(path26, query);
32602
+ const result = await this.opts.http.get(path27, query);
32551
32603
  if (result.messages && result.messages.length > 0) {
32552
32604
  this.opts.onMessages(result.messages);
32553
32605
  this.advanceCursor(result.messages[result.messages.length - 1].id);
@@ -33464,6 +33516,10 @@ import crypto5 from "node:crypto";
33464
33516
  import fs15 from "node:fs";
33465
33517
  import os11 from "node:os";
33466
33518
  import path16 from "node:path";
33519
+ function drainCapMs(timeoutMs) {
33520
+ const t = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 3e5;
33521
+ return Math.min(INFLIGHT_TIMEOUT_FACTOR * t, INFLIGHT_MAX_MS);
33522
+ }
33467
33523
  function hashCwd3(cwd) {
33468
33524
  return crypto5.createHash("sha256").update(path16.resolve(cwd)).digest("hex").slice(0, 16);
33469
33525
  }
@@ -33790,12 +33846,36 @@ import crypto8 from "node:crypto";
33790
33846
  import fs18 from "node:fs";
33791
33847
  import os14 from "node:os";
33792
33848
  import path19 from "node:path";
33849
+ function agentSessionId(env = process.env) {
33850
+ if (env.COGENT_PLATFORM && env.COGENT_PLATFORM !== "cc") return void 0;
33851
+ const id = env.CLAUDE_CODE_SESSION_ID?.trim();
33852
+ return id && id.length <= 128 ? id : void 0;
33853
+ }
33854
+ function startedWithoutClient() {
33855
+ if (withoutClient === void 0) {
33856
+ try {
33857
+ withoutClient = fs18.fstatSync(0).isCharacterDevice();
33858
+ } catch {
33859
+ withoutClient = false;
33860
+ }
33861
+ }
33862
+ return withoutClient;
33863
+ }
33793
33864
  function hashCwd6(cwd) {
33794
33865
  return crypto8.createHash("sha256").update(path19.resolve(cwd)).digest("hex").slice(0, 16);
33795
33866
  }
33796
33867
  function bridgeDirFor(cwd, peerId) {
33797
33868
  return path19.join(os14.homedir(), ".cogent", "bridges", `${hashCwd6(cwd)}.${safe4(peerId)}`);
33798
33869
  }
33870
+ function isReservedPeerName(peerId) {
33871
+ return safe4(peerId).toLowerCase() === safe4(PROCESS_MARK).toLowerCase();
33872
+ }
33873
+ function registryText(value) {
33874
+ return String(value ?? "").replace(/[^A-Za-z0-9._-]/g, "").slice(0, 64) || "unreadable";
33875
+ }
33876
+ function registryOwnerLabel(o) {
33877
+ return `pid ${registryText(o.pid)} (v${registryText(o.version)}, ${JSON.stringify(String(o.endpoint ?? "").slice(0, 200))})`;
33878
+ }
33799
33879
  function pidAlive(pid) {
33800
33880
  if (typeof pid !== "number" || !Number.isInteger(pid) || pid <= 0) return false;
33801
33881
  if (pid === process.pid) return true;
@@ -33806,7 +33886,7 @@ function pidAlive(pid) {
33806
33886
  return err?.code === "EPERM";
33807
33887
  }
33808
33888
  }
33809
- function announceBridge(cwd, peerId, version2, endpoint) {
33889
+ function announceBridge(cwd, peerId, version2, endpoint, sessionId = agentSessionId()) {
33810
33890
  const dir = bridgeDirFor(cwd, peerId);
33811
33891
  try {
33812
33892
  fs18.mkdirSync(dir, { recursive: true });
@@ -33816,13 +33896,30 @@ function announceBridge(cwd, peerId, version2, endpoint) {
33816
33896
  version: version2,
33817
33897
  peerId,
33818
33898
  endpoint,
33819
- cwd: path19.resolve(cwd)
33899
+ cwd: path19.resolve(cwd),
33900
+ parentPid: process.ppid,
33901
+ ...sessionId ? { sessionId } : {},
33902
+ // 🔴 A headless bridge never had a client to lose (review r7 FIX2-2, SEC2-2): when its launching
33903
+ // shell exited, the changed parent read as "client gone" and any bridge that started took its name.
33904
+ ...startedWithoutClient() ? { noClient: true } : {}
33820
33905
  };
33821
33906
  fs18.writeFileSync(path19.join(dir, `${process.pid}.json`), JSON.stringify(entry));
33822
33907
  } catch (err) {
33823
33908
  logger.debug(`wake-ownership: could not announce this bridge (${err.message})`);
33824
33909
  }
33825
33910
  }
33911
+ function markBridgeDraining(cwd, peerId) {
33912
+ const file = path19.join(bridgeDirFor(cwd, peerId), `${process.pid}.json`);
33913
+ try {
33914
+ const entry = JSON.parse(fs18.readFileSync(file, "utf-8"));
33915
+ fs18.writeFileSync(file, JSON.stringify({ ...entry, draining: true }));
33916
+ } catch {
33917
+ }
33918
+ }
33919
+ function markBridgeProcess(cwd, version2) {
33920
+ listLiveBridges(cwd, PROCESS_MARK);
33921
+ announceBridge(cwd, PROCESS_MARK, version2, "");
33922
+ }
33826
33923
  function withdrawBridge(cwd, peerId) {
33827
33924
  try {
33828
33925
  fs18.unlinkSync(path19.join(bridgeDirFor(cwd, peerId), `${process.pid}.json`));
@@ -33859,6 +33956,16 @@ function listLiveBridges(cwd, peerId) {
33859
33956
  live.sort((a, b) => b.startedAt - a.startedAt || b.pid - a.pid);
33860
33957
  return live;
33861
33958
  }
33959
+ function listLiveBridgesInFolder(cwd) {
33960
+ const prefix = `${hashCwd6(cwd)}.`;
33961
+ let dirs;
33962
+ try {
33963
+ dirs = fs18.readdirSync(path19.join(os14.homedir(), ".cogent", "bridges"));
33964
+ } catch {
33965
+ return [];
33966
+ }
33967
+ return dirs.filter((d) => d.startsWith(prefix)).flatMap((d) => listLiveBridges(cwd, d.slice(prefix.length)));
33968
+ }
33862
33969
  function checkWakeOwnership(cwd, peerId) {
33863
33970
  let live;
33864
33971
  try {
@@ -33879,13 +33986,14 @@ function checkWakeOwnership(cwd, peerId) {
33879
33986
  }
33880
33987
  return { isOwner: true, liveCount: live.length + 1 };
33881
33988
  }
33882
- var PROCESS_STARTED_AT, safe4;
33989
+ var PROCESS_STARTED_AT, withoutClient, safe4, PROCESS_MARK;
33883
33990
  var init_wake_ownership = __esm({
33884
33991
  "src/services/wake-ownership.ts"() {
33885
33992
  "use strict";
33886
33993
  init_logger();
33887
33994
  PROCESS_STARTED_AT = Date.now() - Math.floor(process.uptime() * 1e3);
33888
33995
  safe4 = (s) => String(s).replace(/[^A-Za-z0-9._-]/g, "_");
33996
+ PROCESS_MARK = ".process";
33889
33997
  }
33890
33998
  });
33891
33999
 
@@ -33904,6 +34012,39 @@ function versionFromArgv(argv) {
33904
34012
  const m = argv.match(/\/cogent\/(\d+\.\d+\.\d+)\//);
33905
34013
  return m ? m[1] : null;
33906
34014
  }
34015
+ function versionAtLeast(a, b) {
34016
+ const pa = a.split(/[-+]/)[0].split(".").map(Number);
34017
+ const pb = b.split(/[-+]/)[0].split(".").map(Number);
34018
+ for (let i = 0; i < 3; i++) {
34019
+ const x = pa[i] ?? 0;
34020
+ const y = pb[i] ?? 0;
34021
+ if (x !== y) return x > y;
34022
+ }
34023
+ return true;
34024
+ }
34025
+ function bridgeVersion(argv, readFile = (p) => fs19.readFileSync(p, "utf-8")) {
34026
+ const fromPath = versionFromArgv(argv);
34027
+ if (fromPath) return fromPath;
34028
+ const candidates = [];
34029
+ const pkg = argv.match(/(\S*\/@essentialai\/cogent-bridge(?:-bundled)?)\/dist\/(?:cli|index)\.js(?:\s|$)/);
34030
+ if (pkg) candidates.push(path20.join(pkg[1], "package.json"));
34031
+ const bin = argv.match(/(\S*\/node_modules)\/\.bin\/cogent-bridge(?:\s|$)/);
34032
+ if (bin) {
34033
+ for (const name of ["cogent-bridge", "cogent-bridge-bundled"]) {
34034
+ candidates.push(path20.join(bin[1], "@essentialai", name, "package.json"));
34035
+ }
34036
+ }
34037
+ for (const file of candidates) {
34038
+ try {
34039
+ const pj = JSON.parse(readFile(file));
34040
+ if (typeof pj.name === "string" && pj.name.startsWith("@essentialai/cogent-bridge") && typeof pj.version === "string" && /^\d+\.\d+\.\d+/.test(pj.version)) {
34041
+ return pj.version;
34042
+ }
34043
+ } catch {
34044
+ }
34045
+ }
34046
+ return null;
34047
+ }
33907
34048
  function listProcesses() {
33908
34049
  let out;
33909
34050
  try {
@@ -33965,25 +34106,13 @@ function ancestorsOf(pid, procs) {
33965
34106
  }
33966
34107
  return seen;
33967
34108
  }
33968
- function announcedPidsFor(cwd) {
33969
- const pids = /* @__PURE__ */ new Set();
33970
- try {
33971
- const base = path20.dirname(bridgeDirFor(cwd, "x"));
33972
- const prefix = path20.basename(bridgeDirFor(cwd, "x")).split(".")[0];
33973
- for (const dir of fs19.readdirSync(base)) {
33974
- if (!dir.startsWith(`${prefix}.`)) continue;
33975
- const peerId = dir.slice(prefix.length + 1);
33976
- for (const e of listLiveBridges(cwd, peerId)) pids.add(e.pid);
33977
- }
33978
- } catch {
33979
- }
33980
- return pids;
33981
- }
33982
34109
  function findStaleBridges(cwd, procs = listProcesses(), opts = {}) {
34110
+ const versionOf = opts.versionOf ?? ((argv) => bridgeVersion(argv));
33983
34111
  const selfPid = opts.selfPid ?? process.pid;
33984
- const selfStartedAt = opts.selfStartedAt ?? Date.now() - Math.floor(process.uptime() * 1e3);
34112
+ const selfStartedAt = opts.selfStartedAt ?? procs.find((p) => p.pid === selfPid)?.startedAt ?? NaN;
34113
+ if (!Number.isFinite(selfStartedAt)) return [];
33985
34114
  const cwdOf = opts.cwdOf ?? processCwd;
33986
- const announced = opts.announced ?? announcedPidsFor(cwd);
34115
+ const announced = opts.announced ?? new Set(listLiveBridgesInFolder(cwd).map((e) => e.pid));
33987
34116
  const target = canonicalPath(cwd);
33988
34117
  const ancestors = ancestorsOf(selfPid, procs);
33989
34118
  const out = [];
@@ -33995,10 +34124,11 @@ function findStaleBridges(cwd, procs = listProcesses(), opts = {}) {
33995
34124
  if (!Number.isFinite(p.startedAt) || p.startedAt >= selfStartedAt) continue;
33996
34125
  const theirCwd = cwdOf(p.pid);
33997
34126
  if (!theirCwd || canonicalPath(theirCwd) !== target) continue;
33998
- const ver = versionFromArgv(p.argv);
34127
+ const ver = versionOf(p.argv);
34128
+ if (!ver || versionAtLeast(ver, SELF_SUPERSESSION_VERSION)) continue;
33999
34129
  out.push({
34000
34130
  proc: p,
34001
- reason: `pid ${p.pid}${ver ? ` (plugin ${ver})` : ""} is a cogent bridge in this exact checkout, started ${new Date(p.startedAt).toISOString()} \u2014 before this process \u2014 and has not announced itself, so it predates self-supersession and would keep waking this session forever`
34131
+ reason: `pid ${p.pid} (version ${ver}) is a cogent bridge in this exact checkout, started ${new Date(p.startedAt).toISOString()} \u2014 before this process \u2014 and is not in the bridge registry; version ${ver} predates ${SELF_SUPERSESSION_VERSION} self-supersession and would keep waking this session forever`
34002
34132
  });
34003
34133
  }
34004
34134
  return out;
@@ -34032,21 +34162,25 @@ function reapStaleBridges(cwd, opts = {}) {
34032
34162
  }
34033
34163
  if (reaped.length) {
34034
34164
  logger.warn(
34035
- `Cogent: reaped ${reaped.length} stale bridge process(es) left behind by an earlier plugin version. They could not stand down on their own because they predate 3.23.4.`
34165
+ `Cogent: reaped ${reaped.length} stale bridge process(es) for this checkout, each at a version before ${SELF_SUPERSESSION_VERSION}: they could not stand down on their own.`
34036
34166
  );
34037
34167
  }
34038
34168
  return reaped;
34039
34169
  }
34170
+ var SELF_SUPERSESSION_VERSION;
34040
34171
  var init_stale_bridge_reaper = __esm({
34041
34172
  "src/services/stale-bridge-reaper.ts"() {
34042
34173
  "use strict";
34043
34174
  init_logger();
34044
34175
  init_wake_ownership();
34176
+ SELF_SUPERSESSION_VERSION = "3.23.4";
34045
34177
  }
34046
34178
  });
34047
34179
 
34048
34180
  // src/services/auto-relay.ts
34049
34181
  import { randomUUID as randomUUID2 } from "node:crypto";
34182
+ import fs20 from "node:fs";
34183
+ import path21 from "node:path";
34050
34184
  function buildStatusNotice(status, peerId, traceId) {
34051
34185
  const tail = traceId ? ` (trace ${traceId})` : "";
34052
34186
  if (status === RELAY_STATUS.QUEUED) {
@@ -34173,6 +34307,21 @@ function truncate(s) {
34173
34307
  const flat = s.replace(/\s+/g, " ").trim();
34174
34308
  return flat.length > DIAGNOSTIC_MAX ? `${flat.slice(0, DIAGNOSTIC_MAX - 1)}\u2026` : flat;
34175
34309
  }
34310
+ function isInsideOwnFolder(dir) {
34311
+ try {
34312
+ const real = fs20.realpathSync.native(dir);
34313
+ return real === process.cwd() || real.startsWith(process.cwd() + path21.sep);
34314
+ } catch {
34315
+ return false;
34316
+ }
34317
+ }
34318
+ function isOwnFolder(dir) {
34319
+ try {
34320
+ return fs20.realpathSync.native(dir) === fs20.realpathSync.native(process.cwd());
34321
+ } catch {
34322
+ return false;
34323
+ }
34324
+ }
34176
34325
  var RELAY_STATUS, STATUS_MARKER_RE, DIAGNOSTIC_MAX, EARLY_ACK_TTL_MS, EARLY_ACK_MAX, AutoRelayService, autoRelay;
34177
34326
  var init_auto_relay = __esm({
34178
34327
  "src/services/auto-relay.ts"() {
@@ -34213,6 +34362,8 @@ var init_auto_relay = __esm({
34213
34362
  */
34214
34363
  pinnedSessionId = null;
34215
34364
  localCwd = null;
34365
+ /** Every folder this process announced the current peer name under — what a drain or a stop must cover. */
34366
+ announcedCwds = /* @__PURE__ */ new Set();
34216
34367
  /**
34217
34368
  * Peer participation mode (3.2.0, Phase 17 OBS-03).
34218
34369
  * - "agent" (default): full auto-relay — inbound directed messages run
@@ -34276,6 +34427,7 @@ var init_auto_relay = __esm({
34276
34427
  * successful registration so we know which session to inject into.
34277
34428
  */
34278
34429
  setLocalPeer(peerId, sessionId, cwd, mode, label, pinnedSessionId) {
34430
+ if (this.localPeerId && this.localPeerId !== peerId) this.withdrawAnnouncement();
34279
34431
  this.localPeerId = peerId;
34280
34432
  this.localSessionId = sessionId;
34281
34433
  this.pinnedSessionId = pinnedSessionId ?? sessionId;
@@ -34285,7 +34437,12 @@ var init_auto_relay = __esm({
34285
34437
  logger.info(
34286
34438
  `Auto-relay configured for peer ${peerId} (session: ${sessionId}, pinned: ${this.pinnedSessionId}, mode: ${this.localMode}, label: ${this.localLabel})`
34287
34439
  );
34288
- announceBridge(cwd, peerId, CLIENT_VERSION, getConfig().COGENT_ENDPOINT ?? "local");
34440
+ const dirs = /* @__PURE__ */ new Set([path21.resolve(cwd)]);
34441
+ if (isInsideOwnFolder(path21.resolve(cwd))) dirs.add(process.cwd());
34442
+ for (const dir of dirs) {
34443
+ this.announcedCwds.add(dir);
34444
+ announceBridge(dir, peerId, CLIENT_VERSION, getConfig().COGENT_ENDPOINT ?? "local");
34445
+ }
34289
34446
  this.supersededNotified.clear();
34290
34447
  if (!this.reapedStaleBridges) {
34291
34448
  this.reapedStaleBridges = true;
@@ -34660,10 +34817,13 @@ var init_auto_relay = __esm({
34660
34817
  }
34661
34818
  }
34662
34819
  if (this.localPeerId && this.localCwd) {
34663
- const own = checkWakeOwnership(this.localCwd, this.localPeerId);
34664
- if (!own.isOwner) {
34665
- await this._reportSuperseded(msg, own.owner);
34666
- return;
34820
+ const dirs = /* @__PURE__ */ new Set([path21.resolve(this.localCwd), ...isOwnFolder(this.localCwd) ? [process.cwd()] : []]);
34821
+ for (const dir of dirs) {
34822
+ const own = checkWakeOwnership(dir, this.localPeerId);
34823
+ if (!own.isOwner) {
34824
+ await this._reportSuperseded(msg, own.owner);
34825
+ return;
34826
+ }
34667
34827
  }
34668
34828
  }
34669
34829
  if (this.localPeerId && !this.awarenessOnly.has(msg.id)) {
@@ -34708,7 +34868,7 @@ var init_auto_relay = __esm({
34708
34868
  * notice generator.
34709
34869
  */
34710
34870
  async _reportSuperseded(msg, owner) {
34711
- const who = owner ? `pid ${owner.pid} (v${owner.version}, ${owner.endpoint})` : "a newer bridge";
34871
+ const who = owner ? registryOwnerLabel(owner) : "a newer bridge";
34712
34872
  logger.warn(
34713
34873
  `Auto-relay: NOT waking for ${msg.fromPeerId} \u2014 this bridge (pid ${process.pid}, v${CLIENT_VERSION}) has been superseded for peer "${this.localPeerId}" in this checkout by ${who}. It is a leftover process; the newer bridge is answering. Standing down.`
34714
34874
  );
@@ -35139,6 +35299,24 @@ var init_auto_relay = __esm({
35139
35299
  activeDeliveryAckWaiters: Array.from(this.deliveryAckWaiters.keys())
35140
35300
  };
35141
35301
  }
35302
+ /**
35303
+ * Stop claiming this peer's name for this checkout, WITHOUT stopping work already in flight.
35304
+ * Called the moment the MCP client is gone: a draining bridge serves no agent, and while it
35305
+ * counted as the peer's holder, the restarted agent's new bridge stood by behind it (review r2
35306
+ * NEW-1). The announcement is MARKED, not removed — see markBridgeDraining (R3-2). Idempotent.
35307
+ * 🔴 EVERY announcement, not only the last registered cwd's (review r7 SEC2-1, ABC2-2, FIX2-5):
35308
+ * after a register with another spelling of the folder, the one for the bridge's own folder stayed
35309
+ * unmarked, and the restarted agent waited out the drain or stood by for good.
35310
+ */
35311
+ announceDraining() {
35312
+ if (!this.localPeerId) return;
35313
+ for (const dir of this.announcedCwds) markBridgeDraining(dir, this.localPeerId);
35314
+ }
35315
+ /** Remove this bridge's ownership announcements (graceful shutdown, a rename). Idempotent. */
35316
+ withdrawAnnouncement() {
35317
+ if (this.localPeerId) for (const dir of this.announcedCwds) withdrawBridge(dir, this.localPeerId);
35318
+ this.announcedCwds.clear();
35319
+ }
35142
35320
  /**
35143
35321
  * Stop the auto-relay service: clear queue and reset state.
35144
35322
  */
@@ -35158,7 +35336,7 @@ var init_auto_relay = __esm({
35158
35336
  }
35159
35337
  this.deliveryAckWaiters.clear();
35160
35338
  this.recentAcks.clear();
35161
- if (this.localCwd && this.localPeerId) withdrawBridge(this.localCwd, this.localPeerId);
35339
+ this.withdrawAnnouncement();
35162
35340
  this.queue = [];
35163
35341
  this.awarenessOnly.clear();
35164
35342
  this.supersededNotified.clear();
@@ -35178,6 +35356,63 @@ var init_auto_relay = __esm({
35178
35356
  });
35179
35357
 
35180
35358
  // src/services/peer-presence.ts
35359
+ function holderRelation(holderSession, ownSession) {
35360
+ if (holderSession && ownSession) return holderSession === ownSession ? "same-session" : "other-session";
35361
+ return holderSession || ownSession ? "unknown" : "no-session";
35362
+ }
35363
+ function holderClientGone(holder, procs) {
35364
+ if (holder.noClient === true) return false;
35365
+ const byPid = new Map(procs.map((p) => [p.pid, p]));
35366
+ let cur = byPid.get(holder.pid);
35367
+ if (!cur) return false;
35368
+ if (typeof holder.parentPid === "number") return cur.ppid !== holder.parentPid;
35369
+ for (let hops = 0; hops < 3; hops++) {
35370
+ if (cur.ppid === 1) return true;
35371
+ const parent = byPid.get(cur.ppid);
35372
+ if (!parent) return false;
35373
+ const exe = parent.argv.trim().split(/\s+/)[0] ?? "";
35374
+ if (ORPHAN_PARENT_RE.test(exe.split("/").pop() ?? "")) return true;
35375
+ if (!NPX_WRAPPER_RE.test(parent.argv.trim())) return false;
35376
+ cur = parent;
35377
+ }
35378
+ return false;
35379
+ }
35380
+ function insideCodexWakeTurn(selfPid, procs) {
35381
+ const byPid = new Map(procs.map((p) => [p.pid, p]));
35382
+ const seen = /* @__PURE__ */ new Set();
35383
+ let cur = byPid.get(selfPid)?.ppid;
35384
+ while (cur && cur > 1 && !seen.has(cur)) {
35385
+ seen.add(cur);
35386
+ const p = byPid.get(cur);
35387
+ if (!p) break;
35388
+ if (/(^|[\s/])codex(\.js)?\s+exec\s+resume(\s|$)/.test(p.argv)) return true;
35389
+ cur = p.ppid;
35390
+ }
35391
+ return false;
35392
+ }
35393
+ function otherBridgesInFolder(entries, self, listProcs) {
35394
+ let procs = null;
35395
+ return entries.filter(
35396
+ (e) => e.peerId !== PROCESS_MARK && e.pid !== self.pid && !e.draining && holderRelation(e.sessionId, self.sessionId) !== "same-session" && !holderClientGone(e, procs ??= listProcs())
35397
+ );
35398
+ }
35399
+ function anotherAgentSessionAmong(others, ownSession) {
35400
+ return others.some((o) => holderRelation(o.sessionId, ownSession) === "other-session");
35401
+ }
35402
+ function otherBridgeNotice(others, peerId, verdict, ownSession) {
35403
+ const lines = others.map((o) => {
35404
+ const version2 = registryText(o.version);
35405
+ const who = holderRelation(o.sessionId, ownSession) === "other-session" ? `Another live agent session in this folder (bridge pid ${o.pid}, version ${version2}, agent session ${registryText(o.sessionId)})` : `Another live bridge in this folder (pid ${o.pid}, version ${version2}; its agent session cannot be compared with this one, so it may be this agent's own previous or second bridge)`;
35406
+ if (o.peerId !== peerId) {
35407
+ return `${who} is still registered as '${registryText(o.peerId)}'. It keeps that name while it runs, but the folder's saved identity now points at this bridge ('${peerId}'): after a restart that bridge's agent comes back as '${peerId}' only if this bridge is no longer running then; otherwise it stays out of the channel until it registers again.`;
35408
+ }
35409
+ if (verdict.isOwner) {
35410
+ return `${who} was registered as '${peerId}'. It is now superseded: this bridge holds '${peerId}' and that bridge has stopped answering for it.`;
35411
+ }
35412
+ return `${who} is registered as '${peerId}' too. NOBODY answers for '${peerId}' in this channel now: the bridge that started last (pid ${verdict.owner?.pid ?? "unknown"}) is the one that wakes its agent, but in this channel the relay registration now belongs to this bridge, so the relay refuses that bridge's replies here and this bridge does not wake. It stays that way until that bridge is closed or its agent registers again. If that bridge is registered in another channel, it still answers there.`;
35413
+ });
35414
+ return lines.join(" ") + (anotherAgentSessionAmong(others, ownSession) ? ` Two agents in one folder are not supported yet: each agent needs its own peer name and, for now, its own folder.` : "");
35415
+ }
35181
35416
  function autoPresenceEnabled(env = process.env) {
35182
35417
  const v = env.COGENT_AUTO_PRESENCE;
35183
35418
  if (v === void 0 || v === "") return true;
@@ -35207,11 +35442,42 @@ async function restorePresence(deps) {
35207
35442
  };
35208
35443
  }
35209
35444
  const peerId = creds.peerId;
35210
- if (deps.peerAlreadyServed(peerId)) {
35445
+ if (deps.startedByWakeTurn?.()) {
35211
35446
  return {
35212
- state: "online",
35447
+ state: "deferred",
35213
35448
  restored: false,
35214
- detail: `peer '${peerId}' is already served by a live bridge for this working directory`
35449
+ detail: `this bridge runs inside a codex wake turn ('codex exec resume'); peer '${peerId}' belongs to the agent's own bridge, which started this turn \u2014 nothing to restore here`
35450
+ };
35451
+ }
35452
+ const served = deps.peerAlreadyServed(peerId);
35453
+ if (served) {
35454
+ const session = deps.agentSessionId?.();
35455
+ const holder = holderRelation(served.sessionId, session);
35456
+ const servedBy = {
35457
+ ...served,
35458
+ ...served.version === void 0 ? {} : { version: registryText(served.version) },
35459
+ ...served.sessionId === void 0 ? {} : { sessionId: registryText(served.sessionId) }
35460
+ };
35461
+ const started = Number.isFinite(servedBy.startedAt) ? new Date(servedBy.startedAt).toISOString() : "unknown";
35462
+ const recheck = holder === "same-session";
35463
+ const every = deps.recheckSeconds ? ` (re-checked every ${deps.recheckSeconds}s)` : "";
35464
+ const take = `To take '${peerId}' for this agent now, run /cogent:register (cogent_join_session, then cogent_register_peer)`;
35465
+ const why = {
35466
+ "same-session": `That bridge serves this same agent session (${session})`,
35467
+ "no-session": `Neither bridge records an agent session (codex, gemini), so it cannot be told whether that bridge is this agent's previous one or another agent's. ${take}`,
35468
+ "other-session": `That bridge serves another agent session (${servedBy.sessionId}; ours: ${session}). Two agents in one folder are not supported yet: a second agent needs its own folder and peer name. ${take}`,
35469
+ unknown: `Its agent session cannot be compared with this one (that bridge: ${servedBy.sessionId ?? "not recorded"}; this bridge: ${session ?? "not known"}): it may be this agent's own other bridge of an older version, or another agent's. ${take}`
35470
+ };
35471
+ return {
35472
+ state: "standby",
35473
+ restored: false,
35474
+ peerId,
35475
+ servedBy,
35476
+ holder,
35477
+ recheck,
35478
+ // A final standby is evaluated once, so it says "when this bridge started": the health
35479
+ // check keeps showing this text after that bridge is gone.
35480
+ detail: `peer '${peerId}' ${recheck ? "is already served" : "was already served when this bridge started"} by a live bridge for this working directory (pid ${servedBy.pid}, version ${servedBy.version ?? "unknown"}, started ${started}); this bridge has NOT registered and ` + (recheck ? `restores '${peerId}' by itself once that bridge is gone${every}. ` : `does NOT take '${peerId}' by itself, also after that bridge is gone. `) + why[holder]
35215
35481
  };
35216
35482
  }
35217
35483
  if (!autoPresenceEnabled(deps.env ?? process.env)) {
@@ -35271,15 +35537,22 @@ function logPresence(outcome) {
35271
35537
  );
35272
35538
  } else if (outcome.state === "offline") {
35273
35539
  logger.warn(`Presence: OFFLINE \u2014 ${outcome.detail}.`);
35540
+ } else if (outcome.state === "standby" && outcome.holder === "other-session") {
35541
+ logger.warn(`Presence: STANDBY \u2014 ${outcome.detail}.`);
35542
+ } else if (outcome.state === "standby" || outcome.state === "deferred") {
35543
+ logger.info(`Presence: ${outcome.state.toUpperCase()} \u2014 ${outcome.detail}.`);
35274
35544
  } else {
35275
35545
  logger.debug(`Presence: ${outcome.state} \u2014 ${outcome.detail}.`);
35276
35546
  }
35277
35547
  }
35278
- var lastPresence;
35548
+ var ORPHAN_PARENT_RE, NPX_WRAPPER_RE, lastPresence;
35279
35549
  var init_peer_presence = __esm({
35280
35550
  "src/services/peer-presence.ts"() {
35281
35551
  "use strict";
35282
35552
  init_logger();
35553
+ init_wake_ownership();
35554
+ ORPHAN_PARENT_RE = /^(init|launchd|systemd)$/;
35555
+ NPX_WRAPPER_RE = /^(?:\S*\/)?(?:sh|bash|dash) -c cogent-bridge\b|^npm exec @essentialai\/cogent-bridge/;
35283
35556
  lastPresence = null;
35284
35557
  }
35285
35558
  });
@@ -35517,6 +35790,7 @@ __export(startup_exports, {
35517
35790
  hasPendingLabelResolve: () => hasPendingLabelResolve,
35518
35791
  isCloudMode: () => isCloudMode2,
35519
35792
  presenceRegisterArgs: () => presenceRegisterArgs,
35793
+ presenceRetryPending: () => presenceRetryPending,
35520
35794
  reinitCloudBackend: () => reinitCloudBackend,
35521
35795
  resolvePendingLabel: () => resolvePendingLabel,
35522
35796
  restorePeerPresence: () => restorePeerPresence,
@@ -35526,8 +35800,8 @@ __export(startup_exports, {
35526
35800
  triggerReRegistration: () => triggerReRegistration
35527
35801
  });
35528
35802
  import os15 from "node:os";
35529
- import path21 from "node:path";
35530
- import fs20 from "node:fs/promises";
35803
+ import path22 from "node:path";
35804
+ import fs21 from "node:fs/promises";
35531
35805
  import readline2 from "node:readline";
35532
35806
  import { execFile as execFile5 } from "node:child_process";
35533
35807
  import { promisify as promisify4 } from "node:util";
@@ -35545,7 +35819,7 @@ function bindInboxNotifier() {
35545
35819
  }
35546
35820
  async function loadPersistedConfig() {
35547
35821
  try {
35548
- const raw = await fs20.readFile(PERSIST_PATH, "utf-8");
35822
+ const raw = await fs21.readFile(PERSIST_PATH, "utf-8");
35549
35823
  return JSON.parse(raw);
35550
35824
  } catch {
35551
35825
  return {};
@@ -35553,7 +35827,7 @@ async function loadPersistedConfig() {
35553
35827
  }
35554
35828
  async function savePersistedConfig(config2) {
35555
35829
  try {
35556
- await fs20.writeFile(PERSIST_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
35830
+ await fs21.writeFile(PERSIST_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
35557
35831
  } catch (err) {
35558
35832
  const msg = err instanceof Error ? err.message : String(err);
35559
35833
  logger.warn(`Could not save config to ${PERSIST_PATH}: ${msg}`);
@@ -35588,11 +35862,11 @@ First run detected. Where should cogent-bridge store its data?
35588
35862
  });
35589
35863
  }
35590
35864
  async function validateStateDir(statePath) {
35591
- await fs20.mkdir(statePath, { recursive: true });
35592
- const testFile = path21.join(statePath, ".write-test");
35865
+ await fs21.mkdir(statePath, { recursive: true });
35866
+ const testFile = path22.join(statePath, ".write-test");
35593
35867
  try {
35594
- await fs20.writeFile(testFile, "test", "utf-8");
35595
- await fs20.unlink(testFile);
35868
+ await fs21.writeFile(testFile, "test", "utf-8");
35869
+ await fs21.unlink(testFile);
35596
35870
  } catch {
35597
35871
  throw new BridgeError(
35598
35872
  "STARTUP_FAILED" /* STARTUP_FAILED */,
@@ -35669,15 +35943,37 @@ function presenceRegisterArgs(p) {
35669
35943
  // AUD-003 ownership proof — reclaims a peer owned by an earlier token
35670
35944
  ];
35671
35945
  }
35946
+ function presenceRetryPending() {
35947
+ return presenceRetry !== null;
35948
+ }
35672
35949
  function cancelPresenceRetry() {
35673
35950
  if (presenceRetry) clearTimeout(presenceRetry);
35674
35951
  presenceRetry = null;
35675
35952
  }
35676
- function presenceRetryDelayMs(attempt) {
35953
+ function presenceRetryBaseMs() {
35677
35954
  const base = Number(process.env.COGENT_PRESENCE_RETRY_MS);
35678
- return Math.min((Number.isFinite(base) && base > 0 ? base : 3e4) * 2 ** attempt, 3e5);
35955
+ return Number.isFinite(base) && base > 0 ? base : 3e4;
35956
+ }
35957
+ function presenceRetryDelayMs(attempt) {
35958
+ return Math.min(presenceRetryBaseMs() * 2 ** attempt, 3e5);
35959
+ }
35960
+ function standbyRecheckMs() {
35961
+ return Math.max(250, Math.min(presenceRetryBaseMs(), 15e3));
35679
35962
  }
35680
35963
  async function restorePeerPresence(attempt = 0) {
35964
+ if (!presenceSupersedeBound) {
35965
+ presenceSupersedeBound = true;
35966
+ onCloudAttemptInProcess(() => cancelPresenceRetry());
35967
+ }
35968
+ const superseded = { state: "deferred", restored: false, detail: PRESENCE_SUPERSEDED };
35969
+ if (hasCloudAttemptInProcess()) {
35970
+ logPresence(superseded);
35971
+ return superseded;
35972
+ }
35973
+ const generation = getCloudAttemptGeneration();
35974
+ const stillCurrent = () => getCloudAttemptGeneration() === generation;
35975
+ let procs = null;
35976
+ const ps = () => procs ??= listProcesses();
35681
35977
  const outcome = await restorePresence({
35682
35978
  // The endpoint being a relay URL is what makes presence meaningful. We deliberately do
35683
35979
  // NOT gate on the active backend being the cloud one: at startup it is still the
@@ -35690,34 +35986,65 @@ async function restorePeerPresence(attempt = 0) {
35690
35986
  alreadyRegisteredInProcess: () => autoRelay.getDiagnostics().registered,
35691
35987
  peerAlreadyServed: (peerId) => {
35692
35988
  try {
35693
- return listLiveBridges(process.cwd(), peerId).some((b) => b.pid !== process.pid);
35989
+ const holders = listLiveBridges(process.cwd(), peerId).filter((b) => b.pid !== process.pid && !b.draining);
35990
+ const other = holders.find((b) => {
35991
+ if (!holderClientGone(b, ps())) return true;
35992
+ if (!ignoredHolders.has(b.pid)) {
35993
+ ignoredHolders.add(b.pid);
35994
+ logger.info(
35995
+ `Presence: ignoring bridge pid ${b.pid} (${registryText(b.version)}) announced for '${peerId}' \u2014 its MCP client is gone (parent then: ${b.parentPid === void 0 ? "not recorded" : registryText(b.parentPid)}), so it serves no agent`
35996
+ );
35997
+ }
35998
+ return false;
35999
+ });
36000
+ return other ? { pid: other.pid, version: other.version, startedAt: other.startedAt, sessionId: other.sessionId } : null;
35694
36001
  } catch {
35695
- return false;
36002
+ return null;
35696
36003
  }
35697
36004
  },
36005
+ agentSessionId: () => agentSessionId(),
36006
+ startedByWakeTurn: () => insideCodexWakeTurn(process.pid, ps()),
35698
36007
  loadCredentials: () => loadCredentials(),
35699
36008
  cwd: () => process.cwd(),
36009
+ recheckSeconds: Math.round(standbyRecheckMs() / 1e3),
35700
36010
  goOnline: async ({ peerId, endpoint, sessionId, token, cwd, label, peerSecret }) => {
36011
+ if (!stillCurrent()) throw new Error(PRESENCE_SUPERSEDED);
35701
36012
  await reinitCloudBackend(endpoint, sessionId, token);
35702
36013
  const backend = getBackend();
35703
36014
  await backend.registerPeer(...presenceRegisterArgs({ peerId, sessionId, cwd, label, peerSecret }));
36015
+ const slot = await loadCredentials();
36016
+ if (!stillCurrent() || slot?.sessionId !== sessionId || slot?.endpoint !== endpoint) {
36017
+ throw new Error(PRESENCE_SUPERSEDED);
36018
+ }
35704
36019
  autoRelay.setLocalPeer(peerId, sessionId, cwd, "agent", label, sessionId);
35705
36020
  if (cloudWsClient) cloudWsClient.setPeerId(peerId);
35706
36021
  heartbeat.restart();
36022
+ markJoinedInProcess();
35707
36023
  }
35708
36024
  });
36025
+ if (!stillCurrent()) {
36026
+ logPresence(superseded);
36027
+ return superseded;
36028
+ }
36029
+ const prev = getPresence();
35709
36030
  setPresence(outcome);
35710
- logPresence(outcome);
35711
- if (outcome.state === "offline" && outcome.detail.startsWith("presence restore failed")) {
35712
- const delay = presenceRetryDelayMs(attempt);
36031
+ if (outcome.state === "standby" && prev?.state === "standby" && prev.servedBy.pid === outcome.servedBy.pid) {
36032
+ logger.debug(`Presence: STANDBY (unchanged) \u2014 ${outcome.detail}.`);
36033
+ } else {
36034
+ logPresence(outcome);
36035
+ }
36036
+ const failed = outcome.state === "offline" && outcome.detail.startsWith("presence restore failed");
36037
+ const standby = outcome.state === "standby" && outcome.recheck;
36038
+ if (failed || standby) {
36039
+ const delay = standby ? standbyRecheckMs() : presenceRetryDelayMs(attempt);
35713
36040
  cancelPresenceRetry();
35714
36041
  presenceRetry = setTimeout(() => {
35715
36042
  presenceRetry = null;
35716
- void restorePeerPresence(attempt + 1).catch(() => {
36043
+ void restorePeerPresence(standby ? 0 : attempt + 1).catch(() => {
35717
36044
  });
35718
36045
  }, delay);
35719
36046
  presenceRetry.unref?.();
35720
- logger.warn(`presence: restore failed \u2014 retrying in ${Math.round(delay / 1e3)}s`);
36047
+ if (failed) logger.warn(`presence: restore failed \u2014 retrying in ${Math.round(delay / 1e3)}s`);
35721
36048
  }
35722
36049
  return outcome;
35723
36050
  }
@@ -35851,10 +36178,10 @@ async function runStartup() {
35851
36178
  if (envStatePath) {
35852
36179
  statePath = envStatePath;
35853
36180
  } else {
35854
- const defaultPath = path21.join(os15.homedir(), ".cogent");
36181
+ const defaultPath = path22.join(os15.homedir(), ".cogent");
35855
36182
  statePath = await firstRunPrompt(defaultPath);
35856
36183
  }
35857
- if (!envStatePath && statePath !== path21.join(os15.homedir(), ".cogent")) {
36184
+ if (!envStatePath && statePath !== path22.join(os15.homedir(), ".cogent")) {
35858
36185
  process.env.COGENT_STATE_PATH = statePath;
35859
36186
  } else if (!envStatePath) {
35860
36187
  process.env.COGENT_STATE_PATH = statePath;
@@ -35918,7 +36245,7 @@ async function runStartup() {
35918
36245
  }
35919
36246
  initLogger(
35920
36247
  config2.COGENT_LOG_LEVEL,
35921
- path21.join(config2.COGENT_STATE_PATH, "logs")
36248
+ path22.join(config2.COGENT_STATE_PATH, "logs")
35922
36249
  );
35923
36250
  if (isCloud && effectiveSessionId && effectiveToken) {
35924
36251
  cloudInbox = new MessageInbox();
@@ -35958,7 +36285,7 @@ async function runPreflights() {
35958
36285
  await preflightClaude(config2.COGENT_CLAUDE_PATH);
35959
36286
  }
35960
36287
  }
35961
- var UUID_V4_RE, execFileAsync3, PERSIST_PATH, legacyWarnEmitted, cloudWsClient, cloudInbox, cloudHttpClient, cloudModeActive, inboxNotifier, notifierBoundTo, reRegistrationInProgress, presenceRetry, pendingLabelResolve;
36288
+ var UUID_V4_RE, execFileAsync3, PERSIST_PATH, legacyWarnEmitted, cloudWsClient, cloudInbox, cloudHttpClient, cloudModeActive, inboxNotifier, notifierBoundTo, reRegistrationInProgress, presenceRetry, ignoredHolders, presenceSupersedeBound, PRESENCE_SUPERSEDED, pendingLabelResolve;
35962
36289
  var init_startup = __esm({
35963
36290
  "src/startup.ts"() {
35964
36291
  "use strict";
@@ -35974,6 +36301,7 @@ var init_startup = __esm({
35974
36301
  init_auto_relay();
35975
36302
  init_peer_presence();
35976
36303
  init_wake_ownership();
36304
+ init_stale_bridge_reaper();
35977
36305
  init_heartbeat();
35978
36306
  init_codex_preflight();
35979
36307
  init_codex_cli();
@@ -35982,7 +36310,7 @@ var init_startup = __esm({
35982
36310
  init_auto_relay();
35983
36311
  UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
35984
36312
  execFileAsync3 = promisify4(execFile5);
35985
- PERSIST_PATH = path21.join(os15.homedir(), ".cogent-config.json");
36313
+ PERSIST_PATH = path22.join(os15.homedir(), ".cogent-config.json");
35986
36314
  legacyWarnEmitted = false;
35987
36315
  cloudWsClient = null;
35988
36316
  cloudInbox = null;
@@ -35992,18 +36320,21 @@ var init_startup = __esm({
35992
36320
  notifierBoundTo = null;
35993
36321
  reRegistrationInProgress = false;
35994
36322
  presenceRetry = null;
36323
+ ignoredHolders = /* @__PURE__ */ new Set();
36324
+ presenceSupersedeBound = false;
36325
+ PRESENCE_SUPERSEDED = "presence restore superseded: an explicit cogent_join_session/cogent_create_session ran in this process and owns the channel choice";
35995
36326
  pendingLabelResolve = null;
35996
36327
  }
35997
36328
  });
35998
36329
 
35999
36330
  // src/cloud/mail-credential-store.ts
36000
36331
  import crypto9 from "node:crypto";
36001
- import fs21 from "node:fs/promises";
36002
- import path22 from "node:path";
36332
+ import fs22 from "node:fs/promises";
36333
+ import path23 from "node:path";
36003
36334
  import os16 from "node:os";
36004
36335
  function defaultMailCredentialPath(cwd = process.cwd()) {
36005
- const hash = crypto9.createHash("sha256").update(path22.resolve(cwd)).digest("hex").slice(0, 16);
36006
- return path22.join(os16.homedir(), ".cogent", "mail-credentials", `${hash}.json`);
36336
+ const hash = crypto9.createHash("sha256").update(path23.resolve(cwd)).digest("hex").slice(0, 16);
36337
+ return path23.join(os16.homedir(), ".cogent", "mail-credentials", `${hash}.json`);
36007
36338
  }
36008
36339
  function resolveMailCredentialPath(credentialPath) {
36009
36340
  if (credentialPath) return credentialPath;
@@ -36014,16 +36345,16 @@ function resolveMailCredentialPath(credentialPath) {
36014
36345
  async function loadMailCredentials(credentialPath) {
36015
36346
  const filePath = resolveMailCredentialPath(credentialPath);
36016
36347
  try {
36017
- return JSON.parse(await fs21.readFile(filePath, "utf-8"));
36348
+ return JSON.parse(await fs22.readFile(filePath, "utf-8"));
36018
36349
  } catch {
36019
36350
  return null;
36020
36351
  }
36021
36352
  }
36022
36353
  async function saveMailCredentials(creds, credentialPath) {
36023
36354
  const filePath = resolveMailCredentialPath(credentialPath);
36024
- await fs21.mkdir(path22.dirname(filePath), { recursive: true, mode: 448 });
36025
- await fs21.writeFile(filePath, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
36026
- await fs21.chmod(filePath, 384);
36355
+ await fs22.mkdir(path23.dirname(filePath), { recursive: true, mode: 448 });
36356
+ await fs22.writeFile(filePath, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
36357
+ await fs22.chmod(filePath, 384);
36027
36358
  }
36028
36359
  async function persistProvisionedMailbox(mailbox, credentialPath) {
36029
36360
  if (!mailbox?.address || !mailbox.password) return false;
@@ -36044,7 +36375,7 @@ async function persistProvisionedMailbox(mailbox, credentialPath) {
36044
36375
  async function clearMailCredentials(credentialPath) {
36045
36376
  const filePath = resolveMailCredentialPath(credentialPath);
36046
36377
  try {
36047
- await fs21.unlink(filePath);
36378
+ await fs22.unlink(filePath);
36048
36379
  } catch (err) {
36049
36380
  if (err.code !== "ENOENT") throw err;
36050
36381
  }
@@ -36064,15 +36395,39 @@ function unresolvedSessionReason(r) {
36064
36395
  if (r.sessionId) return null;
36065
36396
  return r.candidateCount === 0 ? "no-candidates" : "ambiguous";
36066
36397
  }
36398
+ function peerReclaimWaitMs() {
36399
+ const v = Number(process.env.COGENT_PEER_RECLAIM_WAIT_MS);
36400
+ return Number.isFinite(v) && v >= 0 ? v : 5e4;
36401
+ }
36402
+ async function retrySamePeerId(first, attempt, budgetMs = peerReclaimWaitMs(), sleep4 = (ms) => new Promise((r) => setTimeout(r, ms))) {
36403
+ let err = first;
36404
+ const start = Date.now();
36405
+ let backoff = 1e3;
36406
+ for (; ; ) {
36407
+ const remaining = budgetMs - (Date.now() - start);
36408
+ const retryAfter = err.retryAfterMs;
36409
+ if (retryAfter === void 0 || remaining <= 0 || retryAfter > remaining) throw err;
36410
+ const wait = Math.min(Math.max(retryAfter, backoff), remaining);
36411
+ logger.warn(`register_peer: peerId held by an earlier registration \u2014 retrying the SAME id in ${Math.round(wait / 1e3)}s`);
36412
+ await sleep4(wait);
36413
+ backoff = Math.min(backoff * 2, 15e3);
36414
+ try {
36415
+ return await attempt();
36416
+ } catch (next) {
36417
+ if (!(next instanceof BridgeError && next.code === "PEER_NOT_YOURS" /* PEER_NOT_YOURS */)) throw next;
36418
+ err = next;
36419
+ }
36420
+ }
36421
+ }
36067
36422
  function registerRegisterPeerTool(server) {
36068
36423
  server.registerTool(
36069
36424
  "cogent_register_peer",
36070
36425
  {
36071
36426
  title: "Register Peer",
36072
- description: "Register a Claude Code session as a named peer on the bridge. Each peer needs a unique peerId, the CC sessionId to resume, the working directory (cwd), and a human-readable label.",
36427
+ description: "Step 3: put this agent online in the channel joined or created in this session. peerId = the peer name you were given (never invent or suffix one); sessionId = this agent session's own resumable id (Claude Code or Codex), not the channel id. Also needs the working directory (cwd) and a human-readable label.",
36073
36428
  inputSchema: {
36074
- peerId: import_zod3.z.string().describe("Unique identifier for this peer, e.g. 'backend' or 'frontend'"),
36075
- sessionId: import_zod3.z.string().describe("Claude Code session ID (used with --resume)"),
36429
+ peerId: import_zod3.z.string().describe("The peer name you were given, e.g. 'backend' or 'frontend'. Never invent or suffix one."),
36430
+ sessionId: import_zod3.z.string().describe("This agent session's own resumable id (Claude Code or Codex) \u2014 NOT the channel id"),
36076
36431
  cwd: import_zod3.z.string().describe("Absolute path to the project working directory"),
36077
36432
  label: import_zod3.z.string().describe("Human-readable label, e.g. 'Cogent_Backend' or 'Cogent_Frontend'"),
36078
36433
  peerSecret: import_zod3.z.string().optional().describe(
@@ -36095,13 +36450,22 @@ function registerRegisterPeerTool(server) {
36095
36450
  async ({ peerId, sessionId, cwd, label, mode, role, peerSecret: argPeerSecret }) => {
36096
36451
  try {
36097
36452
  const config2 = getConfig();
36453
+ if (isReservedPeerName(peerId)) {
36454
+ return errorResult(
36455
+ new BridgeError(
36456
+ "INVALID_INPUT" /* INVALID_INPUT */,
36457
+ `peerId '${PROCESS_MARK}' is reserved by the bridge`,
36458
+ "Register under another peerId"
36459
+ )
36460
+ );
36461
+ }
36098
36462
  const hasExplicitCloudSession = !!config2.COGENT_SESSION_ID || process.env.COGENT_TRUST_PERSISTED_CREDENTIALS === "1";
36099
36463
  if (!hasExplicitCloudSession && hasCloudAttemptInProcess() && !hasJoinedInProcess()) {
36100
36464
  return errorResult(
36101
36465
  new BridgeError(
36102
36466
  "JOIN_NOT_CONFIRMED" /* JOIN_NOT_CONFIRMED */,
36103
36467
  "A cogent_join_session/cogent_create_session call was attempted this session but did not succeed, so cogent_register_peer will not fall back to local mode or auto-create a new channel (that would silently put you on the WRONG channel while reporting success)",
36104
- "Re-run cogent_join_session with the correct channel name + secret (+ Org_ID for Team channels) and confirm it returns success before calling cogent_register_peer"
36468
+ "Re-run cogent_join_session with the correct channel name + secret (+ Org_ID for Team channels) and confirm it returns success before calling cogent_register_peer; or, if the join answered CHANNEL_NOT_FOUND, cogent_create_session with the same name, secret and Org_ID"
36105
36469
  )
36106
36470
  );
36107
36471
  }
@@ -36125,6 +36489,17 @@ function registerRegisterPeerTool(server) {
36125
36489
  }
36126
36490
  }
36127
36491
  if (!cloudSessionId) {
36492
+ const stored = await loadCredentials();
36493
+ if (stored?.sessionId) {
36494
+ const channel = stored.label ? `'${stored.label}' (${stored.sessionId})` : `'${stored.sessionId}'`;
36495
+ return errorResult(
36496
+ new BridgeError(
36497
+ "JOIN_NOT_CONFIRMED" /* JOIN_NOT_CONFIRMED */,
36498
+ `This working directory is already on channel ${channel} at ${stored.endpoint}, but this bridge is not online on it, so cogent_register_peer will NOT auto-create a new channel (that would silently move you off your channel while reporting success)`,
36499
+ `Call cogent_join_session for channel ${channel} (name + secret, + Org_ID for Team channels) and confirm it returns success, then retry cogent_register_peer with the SAME peerId. If that join reports the channel no longer exists, or you do not know its secret, ask the user which channel this agent belongs on and join it (cogent_join_session) or create it (cogent_create_session) explicitly, then register with the SAME peerId \u2014 a channel is never auto-created over a stored one`
36500
+ )
36501
+ );
36502
+ }
36128
36503
  autoSecret = crypto10.randomBytes(16).toString("hex");
36129
36504
  const resp = await fetch(
36130
36505
  `${endpoint}/api/sessions`,
@@ -36188,7 +36563,7 @@ function registerRegisterPeerTool(server) {
36188
36563
  new BridgeError(
36189
36564
  "JOIN_NOT_CONFIRMED" /* JOIN_NOT_CONFIRMED */,
36190
36565
  "Cloud endpoint + COGENT_SESSION_ID are configured but this bridge has not joined the channel (no session token \u2014 the relay was unreachable or the session label did not resolve at startup). cogent_register_peer will NOT register against local state (that would leave you on no channel while other local agents' data sits on this host).",
36191
- "Call cogent_join_session with the channel name + secret (+ Org_ID for Team channels) and confirm it returns success, then retry cogent_register_peer."
36566
+ "Call cogent_join_session with the channel name + secret (+ Org_ID for Team channels) and confirm it returns success (or, if the join answered CHANNEL_NOT_FOUND, cogent_create_session with the same name, secret and Org_ID), then retry cogent_register_peer."
36192
36567
  )
36193
36568
  );
36194
36569
  }
@@ -36260,6 +36635,24 @@ function registerRegisterPeerTool(server) {
36260
36635
  logger.debug(`register_peer: ownership recovery failed: ${recoverErr.message}`);
36261
36636
  }
36262
36637
  }
36638
+ if (!recovered && regErr instanceof BridgeError && regErr.code === "PEER_NOT_YOURS" /* PEER_NOT_YOURS */) {
36639
+ recovered = await retrySamePeerId(
36640
+ regErr,
36641
+ () => backend.registerPeer(
36642
+ peerId,
36643
+ sessionId,
36644
+ cwd,
36645
+ label,
36646
+ CLIENT_VERSION,
36647
+ mode,
36648
+ channelSessionId,
36649
+ capabilities,
36650
+ workspaceId,
36651
+ threadId,
36652
+ storedPeerSecret
36653
+ )
36654
+ );
36655
+ }
36263
36656
  if (!recovered) throw regErr;
36264
36657
  peer = recovered;
36265
36658
  }
@@ -36319,9 +36712,25 @@ function registerRegisterPeerTool(server) {
36319
36712
  }
36320
36713
  }
36321
36714
  autoRelay.setLocalPeer(peerId, localSessionId, cwd, mode, label, localSessionId);
36715
+ if (isCloudMode()) {
36716
+ setPresence({ state: "online", restored: false, detail: `registered as '${peerId}' by cogent_register_peer` });
36717
+ }
36718
+ let otherBridges;
36719
+ try {
36720
+ const own = agentSessionId();
36721
+ const others = otherBridgesInFolder(listLiveBridgesInFolder(cwd), { pid: process.pid, sessionId: own }, listProcesses);
36722
+ if (others.length) {
36723
+ otherBridges = otherBridgeNotice(others, peerId, checkWakeOwnership(cwd, peerId), own);
36724
+ if (anotherAgentSessionAmong(others, own)) logger.warn(`Register: ${otherBridges}`);
36725
+ else logger.info(`Register: ${otherBridges}`);
36726
+ }
36727
+ } catch (err) {
36728
+ logger.debug(`Register: could not look for other bridges in this folder: ${err.message}`);
36729
+ }
36322
36730
  const result = {
36323
36731
  success: true,
36324
36732
  action: existing ? "updated" : "registered",
36733
+ ...otherBridges ? { otherBridgeInThisFolder: otherBridges } : {},
36325
36734
  peer,
36326
36735
  sessionResolution: {
36327
36736
  decision: resolution.decision,
@@ -36400,6 +36809,9 @@ var init_register_peer = __esm({
36400
36809
  init_logger();
36401
36810
  init_heartbeat();
36402
36811
  init_auto_relay();
36812
+ init_wake_ownership();
36813
+ init_peer_presence();
36814
+ init_stale_bridge_reaper();
36403
36815
  init_session_resolver();
36404
36816
  init_cc_cli();
36405
36817
  init_durable_identity();
@@ -37069,9 +37481,14 @@ var init_ids = __esm({
37069
37481
  });
37070
37482
 
37071
37483
  // cogent/dist/helpers/validation.js
37484
+ function isValidSessionLabel(value) {
37485
+ return SESSION_LABEL_REGEX.test(value);
37486
+ }
37487
+ var SESSION_LABEL_REGEX;
37072
37488
  var init_validation = __esm({
37073
37489
  "cogent/dist/helpers/validation.js"() {
37074
37490
  "use strict";
37491
+ SESSION_LABEL_REGEX = /^[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$/;
37075
37492
  }
37076
37493
  });
37077
37494
 
@@ -37524,7 +37941,8 @@ function registerHealthCheckTool(server) {
37524
37941
  // PRESENCE — the agent's own view of whether it is IN its channel, so it can
37525
37942
  // answer "am I reachable?" without inferring it from transport internals.
37526
37943
  // "online" registered · "offline" owns a peer but is not registered (nobody can
37527
- // reach it) · "deferred" never joined · "local" no relay.
37944
+ // reach it) · "standby" another live bridge serves this peer, so THIS process is not
37945
+ // registered (servedBy names it) · "deferred" never joined · "local" no relay.
37528
37946
  // services/peer-presence.ts carries the vocabulary and how each state arises.
37529
37947
  presence: (() => {
37530
37948
  const p = getPresence();
@@ -37535,7 +37953,8 @@ function registerHealthCheckTool(server) {
37535
37953
  state: p.state,
37536
37954
  restored: p.restored,
37537
37955
  detail: p.detail,
37538
- ..."peerId" in p ? { peerId: p.peerId } : {}
37956
+ ..."peerId" in p ? { peerId: p.peerId } : {},
37957
+ ..."servedBy" in p ? { servedBy: p.servedBy } : {}
37539
37958
  };
37540
37959
  })()
37541
37960
  };
@@ -37622,18 +38041,43 @@ var init_wake_log2 = __esm({
37622
38041
  }
37623
38042
  });
37624
38043
 
38044
+ // src/tools/channel-text.ts
38045
+ function invalidChannelName(name) {
38046
+ if (isValidSessionLabel(name)) return null;
38047
+ return new BridgeError(
38048
+ "INVALID_INPUT" /* INVALID_INPUT */,
38049
+ `Channel names are 3-32 characters: lowercase letters, digits and hyphens. '${name}' is not valid`,
38050
+ "If it differs only by capital letters, retry with it in lowercase; otherwise ask the user."
38051
+ );
38052
+ }
38053
+ function slackMapCommand(o) {
38054
+ return o.team ? `/cogent map ${o.label} <password> <org-id>` : `/cogent map ${o.sessionId} <password>`;
38055
+ }
38056
+ var init_channel_text = __esm({
38057
+ "src/tools/channel-text.ts"() {
38058
+ "use strict";
38059
+ init_dist();
38060
+ init_errors4();
38061
+ }
38062
+ });
38063
+
37625
38064
  // src/tools/create-session.ts
37626
38065
  import crypto11 from "node:crypto";
37627
38066
  function registerCreateSessionTool(server) {
37628
38067
  server.registerTool(
37629
38068
  "cogent_create_session",
37630
38069
  {
37631
- title: "Create Cloud Session",
37632
- description: "Create a new cloud bridge session on the relay server. Returns session ID and secret for other peers to join. Only available when COGENT_ENDPOINT is configured.",
38070
+ title: "Create Cloud Channel",
38071
+ description: "Step 2: create a Cogent channel and join it. Call this ONLY after cogent_join_session answered CHANNEL_NOT_FOUND, with the SAME channel name, secret and (Team channel) orgId. Never choose a different name. Next call: cogent_register_peer.",
37633
38072
  inputSchema: {
37634
- label: import_zod15.z.string().optional().describe("Human-readable label for the session"),
38073
+ label: import_zod15.z.string().optional().describe(
38074
+ "The channel name (3-32 characters: lowercase letters, digits, hyphens) \u2014 the same name the join was given. Auto-generated if omitted."
38075
+ ),
37635
38076
  secret: import_zod15.z.string().min(8).optional().describe(
37636
- "Shared secret for session authentication (min 8 chars). Auto-generated if omitted."
38077
+ "Shared secret / password for the channel (min 8 chars). Auto-generated if omitted."
38078
+ ),
38079
+ orgId: import_zod15.z.string().min(1).optional().describe(
38080
+ "Org_ID \u2014 the 3rd credential for a Team (business) channel. Provide it to create a Team channel; omit for a free channel."
37637
38081
  )
37638
38082
  },
37639
38083
  annotations: {
@@ -37643,28 +38087,57 @@ function registerCreateSessionTool(server) {
37643
38087
  openWorldHint: true
37644
38088
  }
37645
38089
  },
37646
- async ({ label, secret }) => {
38090
+ async ({ label, secret, orgId }) => {
37647
38091
  try {
37648
38092
  markCloudAttemptInProcess();
38093
+ if (label !== void 0 && label.trim() === "") {
38094
+ return errorResult(invalidChannelName(label) ?? new Error("label is empty"));
38095
+ }
38096
+ if (secret !== void 0 && secret.trim() === "") {
38097
+ return errorResult(
38098
+ new BridgeError(
38099
+ "INVALID_INPUT" /* INVALID_INPUT */,
38100
+ "The channel password is empty",
38101
+ "Pass the password you were given, or omit `secret` to have one generated."
38102
+ )
38103
+ );
38104
+ }
38105
+ label = label?.trim() || void 0;
38106
+ secret = secret?.trim() || void 0;
38107
+ orgId = orgId?.trim() || void 0;
37649
38108
  const config2 = getConfig();
37650
- if (!config2.COGENT_ENDPOINT) {
38109
+ const endpoint = orgId ? config2.COGENT_TEAM_ENDPOINT : config2.COGENT_ENDPOINT;
38110
+ if (!endpoint) {
37651
38111
  return errorResult(
37652
38112
  new Error(
37653
- "COGENT_ENDPOINT not configured. Set it to use cloud mode."
38113
+ orgId ? "COGENT_TEAM_ENDPOINT not configured. Set it to create Team (Org_ID) channels." : "COGENT_ENDPOINT not configured. Set it to use cloud mode."
37654
38114
  )
37655
38115
  );
37656
38116
  }
38117
+ if (label !== void 0) {
38118
+ const invalid = invalidChannelName(label);
38119
+ if (invalid) return errorResult(invalid);
38120
+ }
38121
+ const secretWasGenerated = secret === void 0;
37657
38122
  const sessionSecret = secret ?? crypto11.randomBytes(16).toString("hex");
38123
+ const createBody = orgId ? { secret: sessionSecret, label, orgId } : { secret: sessionSecret, label };
37658
38124
  const resp = await fetch(
37659
- `${config2.COGENT_ENDPOINT}/api/sessions`,
38125
+ `${endpoint}/api/sessions`,
37660
38126
  {
37661
38127
  method: "POST",
37662
38128
  headers: { "Content-Type": "application/json" },
37663
- body: JSON.stringify({ secret: sessionSecret, label })
38129
+ body: JSON.stringify(createBody)
37664
38130
  }
37665
38131
  );
37666
38132
  if (!resp.ok) {
37667
38133
  const body = await resp.text().catch(() => "");
38134
+ if (resp.status === 400 && body.includes("already in use")) {
38135
+ return errorResult(
38136
+ new Error(
38137
+ `Channel '${label}' already exists (already in use). Call cogent_join_session with the same name, secret${orgId ? " and Org_ID" : ""}. Do not choose another name.`
38138
+ )
38139
+ );
38140
+ }
37668
38141
  return errorResult(
37669
38142
  new Error(
37670
38143
  `Failed to create session: HTTP ${resp.status}${body ? ` - ${body}` : ""}`
@@ -37673,7 +38146,7 @@ function registerCreateSessionTool(server) {
37673
38146
  }
37674
38147
  const data = await resp.json();
37675
38148
  await saveCredentials({
37676
- endpoint: config2.COGENT_ENDPOINT,
38149
+ endpoint,
37677
38150
  sessionId: data.sessionId,
37678
38151
  token: data.token,
37679
38152
  label: data.label,
@@ -37683,7 +38156,7 @@ function registerCreateSessionTool(server) {
37683
38156
  try {
37684
38157
  const startupModule = await Promise.resolve().then(() => (init_startup(), startup_exports));
37685
38158
  if (typeof startupModule.reinitCloudBackend === "function") {
37686
- await startupModule.reinitCloudBackend(config2.COGENT_ENDPOINT, data.sessionId, data.token);
38159
+ await startupModule.reinitCloudBackend(endpoint, data.sessionId, data.token);
37687
38160
  logger.info(
37688
38161
  "Cloud backend reinitialized with new session credentials"
37689
38162
  );
@@ -37691,32 +38164,40 @@ function registerCreateSessionTool(server) {
37691
38164
  } catch (err) {
37692
38165
  logger.debug(`Backend reinit not available: ${err}`);
37693
38166
  }
38167
+ const channelName = data.label ?? data.sessionId;
37694
38168
  return successResult({
37695
38169
  success: true,
38170
+ created: true,
37696
38171
  sessionId: data.sessionId,
37697
38172
  label: data.label,
37698
- secret: sessionSecret,
37699
38173
  token: data.token,
37700
38174
  createdAt: data.createdAt,
37701
- config_snippet: {
37702
- env_vars: `COGENT_ENDPOINT=${config2.COGENT_ENDPOINT}
37703
- COGENT_SESSION_ID=${data.label ?? data.sessionId}
38175
+ slackMapCommand: slackMapCommand({ team: !!orgId, label: channelName, sessionId: data.sessionId }),
38176
+ // A secret the CALLER supplied is never echoed back: the caller already has it, and a
38177
+ // tool result can be quoted into a channel by an auto-reply. Only a secret this tool
38178
+ // generated is returned — nothing else knows it.
38179
+ ...secretWasGenerated ? {
38180
+ secret: sessionSecret,
38181
+ config_snippet: {
38182
+ env_vars: `COGENT_ENDPOINT=${endpoint}
38183
+ COGENT_SESSION_ID=${channelName}
37704
38184
  COGENT_SECRET=${sessionSecret}
37705
38185
  COGENT_PLATFORM=${config2.COGENT_PLATFORM}`,
37706
- mcp_config: {
37707
- "cogent": {
37708
- command: "npx",
37709
- args: ["-y", "@essentialai/cogent-bridge"],
37710
- env: {
37711
- COGENT_ENDPOINT: config2.COGENT_ENDPOINT,
37712
- COGENT_SESSION_ID: data.label ?? data.sessionId,
37713
- COGENT_SECRET: sessionSecret,
37714
- COGENT_PLATFORM: config2.COGENT_PLATFORM
38186
+ mcp_config: {
38187
+ "cogent": {
38188
+ command: "npx",
38189
+ args: ["-y", "@essentialai/cogent-bridge"],
38190
+ env: {
38191
+ COGENT_ENDPOINT: endpoint,
38192
+ COGENT_SESSION_ID: channelName,
38193
+ COGENT_SECRET: sessionSecret,
38194
+ COGENT_PLATFORM: config2.COGENT_PLATFORM
38195
+ }
37715
38196
  }
37716
38197
  }
37717
38198
  }
37718
- },
37719
- note: `Session created with label "${data.label ?? data.sessionId}". Share the secret with peers who need to join. Credentials saved locally. Cloud backend updated.`
38199
+ } : {},
38200
+ note: `${orgId ? "Team channel" : "Channel"} "${channelName}" was created by this call and you are joined to it. Credentials saved locally. Cloud backend updated. Next: cogent_register_peer.`
37720
38201
  });
37721
38202
  } catch (err) {
37722
38203
  logger.error("create-session failed", { error: err });
@@ -37734,6 +38215,7 @@ var init_create_session = __esm({
37734
38215
  init_errors4();
37735
38216
  init_credential_store();
37736
38217
  init_logger();
38218
+ init_channel_text();
37737
38219
  }
37738
38220
  });
37739
38221
 
@@ -37743,14 +38225,14 @@ function registerJoinSessionTool(server) {
37743
38225
  "cogent_join_session",
37744
38226
  {
37745
38227
  title: "Join Cloud Channel",
37746
- description: 'Join an existing cloud bridge channel by its channel name (e.g. "mt-space") and shared secret. The channel name is the human-readable label provided by the user, NOT a UUID. Do NOT pass a Claude Code session ID here \u2014 use the channel/space name the user gave you.',
38228
+ description: 'Step 1: join an existing Cogent channel by its channel name (e.g. "mt-space") and shared secret (plus orgId for a Team channel). The channel name is the human-readable label provided by the user, NOT a UUID. Do NOT pass an agent session ID here \u2014 use the channel/space name the user gave you. Next call: cogent_register_peer. If this answers CHANNEL_NOT_FOUND, the next call is cogent_create_session with the same name, secret and orgId.',
37747
38229
  inputSchema: {
37748
38230
  channel: import_zod16.z.string().describe(
37749
- 'The channel name to join (e.g. "mt-space"). This is the human-readable name the user provided, NOT a UUID. Do NOT use a Claude Code session ID here.'
38231
+ 'The channel name to join (e.g. "mt-space"). This is the human-readable name the user provided, NOT a UUID. Do NOT use an agent session ID here.'
37750
38232
  ),
37751
38233
  secret: import_zod16.z.string().min(1).describe("The shared secret / password for the channel"),
37752
38234
  orgId: import_zod16.z.string().min(1).optional().describe(
37753
- "Org_ID \u2014 the 3rd credential for a Team (business) channel. Provide it for team channels; omit for free channels."
38235
+ "Org_ID \u2014 the 3rd credential for a Team (business) channel. Provide it for team channels; omit for free channels. If you were given one, never retry without it."
37754
38236
  )
37755
38237
  },
37756
38238
  annotations: {
@@ -37778,10 +38260,12 @@ function registerJoinSessionTool(server) {
37778
38260
  if (UUID_V4_RE2.test(channel)) {
37779
38261
  return errorResult(
37780
38262
  new Error(
37781
- `The "channel" parameter ("${channel}") looks like a UUID. You should pass the human-readable channel name (e.g. "mt-space") that the user provided, NOT a Claude Code session ID or any UUID. Please retry with the correct channel name.`
38263
+ `The "channel" parameter ("${channel}") looks like a UUID. You should pass the human-readable channel name (e.g. "mt-space") that the user provided, NOT an agent session ID or any UUID. Please retry with the correct channel name.`
37782
38264
  )
37783
38265
  );
37784
38266
  }
38267
+ const invalid = invalidChannelName(channel);
38268
+ if (invalid) return errorResult(invalid);
37785
38269
  let resolvedSessionId;
37786
38270
  let resolvedLabel;
37787
38271
  if (orgId) {
@@ -37793,6 +38277,13 @@ function registerJoinSessionTool(server) {
37793
38277
  body: JSON.stringify({ label: channel, orgId })
37794
38278
  }
37795
38279
  );
38280
+ if (resolveResp.status === 404) {
38281
+ return errorResult(new BridgeError(
38282
+ "CHANNEL_NOT_FOUND" /* CHANNEL_NOT_FOUND */,
38283
+ `Team channel '${channel}' was not found on ${endpoint} for this Org_ID (HTTP 404)`,
38284
+ `If the name and Org_ID are exactly as given, the channel does not exist yet: call cogent_create_session with label '${channel}', the same secret and the same orgId, then cogent_register_peer. Do not call cogent_join_session again first, and never without orgId.`
38285
+ ));
38286
+ }
37796
38287
  if (!resolveResp.ok) {
37797
38288
  const body = await resolveResp.text().catch(() => "");
37798
38289
  return errorResult(new Error(
@@ -37806,6 +38297,13 @@ function registerJoinSessionTool(server) {
37806
38297
  const resolveResp = await fetch(
37807
38298
  `${endpoint}/api/sessions/resolve/${encodeURIComponent(channel)}`
37808
38299
  );
38300
+ if (resolveResp.status === 404) {
38301
+ return errorResult(new BridgeError(
38302
+ "CHANNEL_NOT_FOUND" /* CHANNEL_NOT_FOUND */,
38303
+ `Channel '${channel}' was not found on ${endpoint} (HTTP 404)`,
38304
+ `If you were given an Org_ID you left it out: call cogent_join_session again WITH orgId and do not create anything. Otherwise call cogent_create_session with label '${channel}' and the same secret.`
38305
+ ));
38306
+ }
37809
38307
  if (!resolveResp.ok) {
37810
38308
  const body = await resolveResp.text().catch(() => "");
37811
38309
  return errorResult(new Error(`Failed to resolve channel name "${channel}": HTTP ${resolveResp.status}${body ? ` - ${body}` : ""}`));
@@ -37825,9 +38323,10 @@ function registerJoinSessionTool(server) {
37825
38323
  );
37826
38324
  if (!resp.ok) {
37827
38325
  const body = await resp.text().catch(() => "");
38326
+ const wrong = resp.status === 401 ? `. The password${orgId ? " or Org_ID" : ""} is wrong. Do not retry with guesses - ask the user.` : "";
37828
38327
  return errorResult(
37829
38328
  new Error(
37830
- `Failed to join session: HTTP ${resp.status}${body ? ` - ${body}` : ""}`
38329
+ `Failed to join session: HTTP ${resp.status}${body ? ` - ${body}` : ""}${wrong}`
37831
38330
  )
37832
38331
  );
37833
38332
  }
@@ -37876,6 +38375,11 @@ function registerJoinSessionTool(server) {
37876
38375
  token: data.token,
37877
38376
  label: resolvedLabel ?? data.label ?? channel,
37878
38377
  peerCount: data.peerCount,
38378
+ slackMapCommand: slackMapCommand({
38379
+ team: !!orgId,
38380
+ label: resolvedLabel ?? data.label ?? channel,
38381
+ sessionId: data.sessionId
38382
+ }),
37879
38383
  note: "Successfully joined session. Credentials saved locally. Cloud backend updated. You can now use cogent_register_peer.",
37880
38384
  ...updateNudge ? { updateAvailable: updateNudge } : {}
37881
38385
  });
@@ -37896,6 +38400,7 @@ var init_join_session = __esm({
37896
38400
  init_credential_store();
37897
38401
  init_logger();
37898
38402
  init_npm_update_check();
38403
+ init_channel_text();
37899
38404
  UUID_V4_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
37900
38405
  }
37901
38406
  });
@@ -38130,8 +38635,8 @@ var require_cookies = __commonJS({
38130
38635
  if (urlparts.hostname !== cookie.domain && (cookie.domain.charAt(0) !== "." || ("." + urlparts.hostname).substr(-cookie.domain.length) !== cookie.domain)) {
38131
38636
  return false;
38132
38637
  }
38133
- let path26 = this.getPath(urlparts.pathname);
38134
- if (path26.substr(0, cookie.path.length) !== cookie.path) {
38638
+ let path27 = this.getPath(urlparts.pathname);
38639
+ if (path27.substr(0, cookie.path.length) !== cookie.path) {
38135
38640
  return false;
38136
38641
  }
38137
38642
  if (cookie.secure && urlparts.protocol !== "https:") {
@@ -38191,16 +38696,16 @@ var require_cookies = __commonJS({
38191
38696
  * @returns {String} Normalized path
38192
38697
  */
38193
38698
  getPath(pathname) {
38194
- let path26 = (pathname || "/").split("/");
38195
- path26.pop();
38196
- path26 = path26.join("/").trim();
38197
- if (path26.charAt(0) !== "/") {
38198
- path26 = "/" + path26;
38699
+ let path27 = (pathname || "/").split("/");
38700
+ path27.pop();
38701
+ path27 = path27.join("/").trim();
38702
+ if (path27.charAt(0) !== "/") {
38703
+ path27 = "/" + path27;
38199
38704
  }
38200
- if (path26.substr(-1) !== "/") {
38201
- path26 += "/";
38705
+ if (path27.substr(-1) !== "/") {
38706
+ path27 += "/";
38202
38707
  }
38203
- return path26;
38708
+ return path27;
38204
38709
  }
38205
38710
  };
38206
38711
  module.exports = Cookies;
@@ -38501,7 +39006,7 @@ var require_shared = __commonJS({
38501
39006
  "use strict";
38502
39007
  var urllib = __require("url");
38503
39008
  var util2 = __require("util");
38504
- var fs24 = __require("fs");
39009
+ var fs25 = __require("fs");
38505
39010
  var nmfetch = require_fetch();
38506
39011
  var dns = __require("dns");
38507
39012
  var net = __require("net");
@@ -38887,7 +39392,7 @@ var require_shared = __commonJS({
38887
39392
  }
38888
39393
  return callback(null, parsedDataUri.data);
38889
39394
  } else if (content.path) {
38890
- return resolveStream(fs24.createReadStream(content.path), callback);
39395
+ return resolveStream(fs25.createReadStream(content.path), callback);
38891
39396
  }
38892
39397
  }
38893
39398
  if (typeof data[key].content === "string" && !["utf8", "usascii", "ascii"].includes(encoding)) {
@@ -39011,7 +39516,7 @@ var require_shared = __commonJS({
39011
39516
  var require_mime_types = __commonJS({
39012
39517
  "node_modules/nodemailer/lib/mime-funcs/mime-types.js"(exports, module) {
39013
39518
  "use strict";
39014
- var path26 = __require("path");
39519
+ var path27 = __require("path");
39015
39520
  var defaultMimeType = "application/octet-stream";
39016
39521
  var defaultExtension = "bin";
39017
39522
  var mimeTypes = /* @__PURE__ */ new Map([
@@ -41069,7 +41574,7 @@ var require_mime_types = __commonJS({
41069
41574
  if (!filename) {
41070
41575
  return defaultMimeType;
41071
41576
  }
41072
- let parsed = path26.parse(filename);
41577
+ let parsed = path27.parse(filename);
41073
41578
  let extension = (parsed.ext.substr(1) || parsed.name || "").split("?").shift().trim().toLowerCase();
41074
41579
  let value = defaultMimeType;
41075
41580
  if (extensions.has(extension)) {
@@ -42471,7 +42976,7 @@ var require_mime_node = __commonJS({
42471
42976
  "node_modules/nodemailer/lib/mime-node/index.js"(exports, module) {
42472
42977
  "use strict";
42473
42978
  var crypto12 = __require("crypto");
42474
- var fs24 = __require("fs");
42979
+ var fs25 = __require("fs");
42475
42980
  var punycode = require_punycode();
42476
42981
  var PassThrough = __require("stream").PassThrough;
42477
42982
  var shared = require_shared();
@@ -43186,7 +43691,7 @@ var require_mime_node = __commonJS({
43186
43691
  setImmediate(() => contentStream.emit("error", new Error("File access rejected for " + content.path)));
43187
43692
  return contentStream;
43188
43693
  }
43189
- return fs24.createReadStream(content.path);
43694
+ return fs25.createReadStream(content.path);
43190
43695
  } else if (content && typeof content.href === "string") {
43191
43696
  if (this.disableUrlAccess) {
43192
43697
  contentStream = new PassThrough();
@@ -44207,8 +44712,8 @@ var require_dkim = __commonJS({
44207
44712
  var RelaxedBody = require_relaxed_body();
44208
44713
  var sign = require_sign();
44209
44714
  var PassThrough = __require("stream").PassThrough;
44210
- var fs24 = __require("fs");
44211
- var path26 = __require("path");
44715
+ var fs25 = __require("fs");
44716
+ var path27 = __require("path");
44212
44717
  var crypto12 = __require("crypto");
44213
44718
  var DKIM_ALGO = "sha256";
44214
44719
  var MAX_MESSAGE_SIZE = 128 * 1024;
@@ -44222,7 +44727,7 @@ var require_dkim = __commonJS({
44222
44727
  this.chunks = [];
44223
44728
  this.chunklen = 0;
44224
44729
  this.readPos = 0;
44225
- this.cachePath = this.cacheDir ? path26.join(this.cacheDir, "message." + Date.now() + "-" + crypto12.randomBytes(14).toString("hex")) : false;
44730
+ this.cachePath = this.cacheDir ? path27.join(this.cacheDir, "message." + Date.now() + "-" + crypto12.randomBytes(14).toString("hex")) : false;
44226
44731
  this.cache = false;
44227
44732
  this.headers = false;
44228
44733
  this.bodyHash = false;
@@ -44242,10 +44747,10 @@ var require_dkim = __commonJS({
44242
44747
  if (!this.cache || !this.cachePath) {
44243
44748
  return;
44244
44749
  }
44245
- fs24.unlink(this.cachePath, () => false);
44750
+ fs25.unlink(this.cachePath, () => false);
44246
44751
  }
44247
44752
  createReadCache() {
44248
- this.cache = fs24.createReadStream(this.cachePath);
44753
+ this.cache = fs25.createReadStream(this.cachePath);
44249
44754
  this.cache.once("error", (err) => {
44250
44755
  this.cleanup();
44251
44756
  this.output.emit("error", err);
@@ -44301,7 +44806,7 @@ var require_dkim = __commonJS({
44301
44806
  }
44302
44807
  createWriteCache() {
44303
44808
  this.output.usingCache = true;
44304
- this.cache = fs24.createWriteStream(this.cachePath);
44809
+ this.cache = fs25.createWriteStream(this.cachePath);
44305
44810
  this.cache.once("error", (err) => {
44306
44811
  this.cleanup();
44307
44812
  this.relaxedBody.unpipe(this.cache);
@@ -49190,13 +49695,13 @@ var require_nodemailer = __commonJS({
49190
49695
  });
49191
49696
 
49192
49697
  // src/mail/mail-sender.ts
49193
- import fs22 from "node:fs/promises";
49194
- import path23 from "node:path";
49698
+ import fs23 from "node:fs/promises";
49699
+ import path24 from "node:path";
49195
49700
  async function buildAndSendMail(sender, creds, params) {
49196
49701
  const attachments = [];
49197
49702
  for (const p of params.attachmentPaths ?? []) {
49198
- const content = await fs22.readFile(p);
49199
- attachments.push({ filename: path23.basename(p), content });
49703
+ const content = await fs23.readFile(p);
49704
+ attachments.push({ filename: path24.basename(p), content });
49200
49705
  }
49201
49706
  const { messageId } = await sender.send({
49202
49707
  from: creds.address,
@@ -49585,8 +50090,8 @@ var require_req = __commonJS({
49585
50090
  if (req.originalUrl) {
49586
50091
  _req.url = req.originalUrl;
49587
50092
  } else {
49588
- const path26 = req.path;
49589
- _req.url = typeof path26 === "string" ? path26 : req.url ? req.url.path || req.url : void 0;
50093
+ const path27 = req.path;
50094
+ _req.url = typeof path27 === "string" ? path27 : req.url ? req.url.path || req.url : void 0;
49590
50095
  }
49591
50096
  if (req.query) {
49592
50097
  _req.query = req.query;
@@ -49751,14 +50256,14 @@ var require_redact = __commonJS({
49751
50256
  }
49752
50257
  return obj;
49753
50258
  }
49754
- function parsePath(path26) {
50259
+ function parsePath(path27) {
49755
50260
  const parts = [];
49756
50261
  let current = "";
49757
50262
  let inBrackets = false;
49758
50263
  let inQuotes = false;
49759
50264
  let quoteChar = "";
49760
- for (let i = 0; i < path26.length; i++) {
49761
- const char = path26[i];
50265
+ for (let i = 0; i < path27.length; i++) {
50266
+ const char = path27[i];
49762
50267
  if (!inBrackets && char === ".") {
49763
50268
  if (current) {
49764
50269
  parts.push(current);
@@ -49889,10 +50394,10 @@ var require_redact = __commonJS({
49889
50394
  return current;
49890
50395
  }
49891
50396
  function redactPaths(obj, paths, censor, remove = false) {
49892
- for (const path26 of paths) {
49893
- const parts = parsePath(path26);
50397
+ for (const path27 of paths) {
50398
+ const parts = parsePath(path27);
49894
50399
  if (parts.includes("*")) {
49895
- redactWildcardPath(obj, parts, censor, path26, remove);
50400
+ redactWildcardPath(obj, parts, censor, path27, remove);
49896
50401
  } else {
49897
50402
  if (remove) {
49898
50403
  removeKey(obj, parts);
@@ -49977,8 +50482,8 @@ var require_redact = __commonJS({
49977
50482
  }
49978
50483
  } else {
49979
50484
  if (afterWildcard.includes("*")) {
49980
- const wrappedCensor = typeof censor === "function" ? (value, path26) => {
49981
- const fullPath = [...pathArray.slice(0, pathLength), ...path26];
50485
+ const wrappedCensor = typeof censor === "function" ? (value, path27) => {
50486
+ const fullPath = [...pathArray.slice(0, pathLength), ...path27];
49982
50487
  return censor(value, fullPath);
49983
50488
  } : censor;
49984
50489
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -50013,8 +50518,8 @@ var require_redact = __commonJS({
50013
50518
  return null;
50014
50519
  }
50015
50520
  const pathStructure = /* @__PURE__ */ new Map();
50016
- for (const path26 of pathsToClone) {
50017
- const parts = parsePath(path26);
50521
+ for (const path27 of pathsToClone) {
50522
+ const parts = parsePath(path27);
50018
50523
  let current = pathStructure;
50019
50524
  for (let i = 0; i < parts.length; i++) {
50020
50525
  const part = parts[i];
@@ -50066,24 +50571,24 @@ var require_redact = __commonJS({
50066
50571
  }
50067
50572
  return cloneSelectively(obj, pathStructure);
50068
50573
  }
50069
- function validatePath(path26) {
50070
- if (typeof path26 !== "string") {
50574
+ function validatePath(path27) {
50575
+ if (typeof path27 !== "string") {
50071
50576
  throw new Error("Paths must be (non-empty) strings");
50072
50577
  }
50073
- if (path26 === "") {
50578
+ if (path27 === "") {
50074
50579
  throw new Error("Invalid redaction path ()");
50075
50580
  }
50076
- if (path26.includes("..")) {
50077
- throw new Error(`Invalid redaction path (${path26})`);
50581
+ if (path27.includes("..")) {
50582
+ throw new Error(`Invalid redaction path (${path27})`);
50078
50583
  }
50079
- if (path26.includes(",")) {
50080
- throw new Error(`Invalid redaction path (${path26})`);
50584
+ if (path27.includes(",")) {
50585
+ throw new Error(`Invalid redaction path (${path27})`);
50081
50586
  }
50082
50587
  let bracketCount = 0;
50083
50588
  let inQuotes = false;
50084
50589
  let quoteChar = "";
50085
- for (let i = 0; i < path26.length; i++) {
50086
- const char = path26[i];
50590
+ for (let i = 0; i < path27.length; i++) {
50591
+ const char = path27[i];
50087
50592
  if ((char === '"' || char === "'") && bracketCount > 0) {
50088
50593
  if (!inQuotes) {
50089
50594
  inQuotes = true;
@@ -50097,20 +50602,20 @@ var require_redact = __commonJS({
50097
50602
  } else if (char === "]" && !inQuotes) {
50098
50603
  bracketCount--;
50099
50604
  if (bracketCount < 0) {
50100
- throw new Error(`Invalid redaction path (${path26})`);
50605
+ throw new Error(`Invalid redaction path (${path27})`);
50101
50606
  }
50102
50607
  }
50103
50608
  }
50104
50609
  if (bracketCount !== 0) {
50105
- throw new Error(`Invalid redaction path (${path26})`);
50610
+ throw new Error(`Invalid redaction path (${path27})`);
50106
50611
  }
50107
50612
  }
50108
50613
  function validatePaths(paths) {
50109
50614
  if (!Array.isArray(paths)) {
50110
50615
  throw new TypeError("paths must be an array");
50111
50616
  }
50112
- for (const path26 of paths) {
50113
- validatePath(path26);
50617
+ for (const path27 of paths) {
50618
+ validatePath(path27);
50114
50619
  }
50115
50620
  }
50116
50621
  function slowRedact(options = {}) {
@@ -50278,8 +50783,8 @@ var require_redaction = __commonJS({
50278
50783
  if (shape[k] === null) {
50279
50784
  o[k] = (value) => topCensor(value, [k]);
50280
50785
  } else {
50281
- const wrappedCensor = typeof censor === "function" ? (value, path26) => {
50282
- return censor(value, [k, ...path26]);
50786
+ const wrappedCensor = typeof censor === "function" ? (value, path27) => {
50787
+ return censor(value, [k, ...path27]);
50283
50788
  } : censor;
50284
50789
  o[k] = Redact({
50285
50790
  paths: shape[k],
@@ -50497,10 +51002,10 @@ var require_atomic_sleep = __commonJS({
50497
51002
  var require_sonic_boom = __commonJS({
50498
51003
  "node_modules/sonic-boom/index.js"(exports, module) {
50499
51004
  "use strict";
50500
- var fs24 = __require("fs");
51005
+ var fs25 = __require("fs");
50501
51006
  var EventEmitter = __require("events");
50502
51007
  var inherits = __require("util").inherits;
50503
- var path26 = __require("path");
51008
+ var path27 = __require("path");
50504
51009
  var sleep4 = require_atomic_sleep();
50505
51010
  var assert2 = __require("assert");
50506
51011
  var BUSY_WRITE_TIMEOUT = 100;
@@ -50554,20 +51059,20 @@ var require_sonic_boom = __commonJS({
50554
51059
  const mode = sonic.mode;
50555
51060
  if (sonic.sync) {
50556
51061
  try {
50557
- if (sonic.mkdir) fs24.mkdirSync(path26.dirname(file), { recursive: true });
50558
- const fd = fs24.openSync(file, flags, mode);
51062
+ if (sonic.mkdir) fs25.mkdirSync(path27.dirname(file), { recursive: true });
51063
+ const fd = fs25.openSync(file, flags, mode);
50559
51064
  fileOpened(null, fd);
50560
51065
  } catch (err) {
50561
51066
  fileOpened(err);
50562
51067
  throw err;
50563
51068
  }
50564
51069
  } else if (sonic.mkdir) {
50565
- fs24.mkdir(path26.dirname(file), { recursive: true }, (err) => {
51070
+ fs25.mkdir(path27.dirname(file), { recursive: true }, (err) => {
50566
51071
  if (err) return fileOpened(err);
50567
- fs24.open(file, flags, mode, fileOpened);
51072
+ fs25.open(file, flags, mode, fileOpened);
50568
51073
  });
50569
51074
  } else {
50570
- fs24.open(file, flags, mode, fileOpened);
51075
+ fs25.open(file, flags, mode, fileOpened);
50571
51076
  }
50572
51077
  }
50573
51078
  function SonicBoom(opts) {
@@ -50608,8 +51113,8 @@ var require_sonic_boom = __commonJS({
50608
51113
  this.flush = flushBuffer;
50609
51114
  this.flushSync = flushBufferSync;
50610
51115
  this._actualWrite = actualWriteBuffer;
50611
- fsWriteSync = () => fs24.writeSync(this.fd, this._writingBuf);
50612
- fsWrite = () => fs24.write(this.fd, this._writingBuf, this.release);
51116
+ fsWriteSync = () => fs25.writeSync(this.fd, this._writingBuf);
51117
+ fsWrite = () => fs25.write(this.fd, this._writingBuf, this.release);
50613
51118
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
50614
51119
  this._writingBuf = "";
50615
51120
  this.write = write;
@@ -50618,15 +51123,15 @@ var require_sonic_boom = __commonJS({
50618
51123
  this._actualWrite = actualWrite;
50619
51124
  fsWriteSync = () => {
50620
51125
  if (Buffer.isBuffer(this._writingBuf)) {
50621
- return fs24.writeSync(this.fd, this._writingBuf);
51126
+ return fs25.writeSync(this.fd, this._writingBuf);
50622
51127
  }
50623
- return fs24.writeSync(this.fd, this._writingBuf, "utf8");
51128
+ return fs25.writeSync(this.fd, this._writingBuf, "utf8");
50624
51129
  };
50625
51130
  fsWrite = () => {
50626
51131
  if (Buffer.isBuffer(this._writingBuf)) {
50627
- return fs24.write(this.fd, this._writingBuf, this.release);
51132
+ return fs25.write(this.fd, this._writingBuf, this.release);
50628
51133
  }
50629
- return fs24.write(this.fd, this._writingBuf, "utf8", this.release);
51134
+ return fs25.write(this.fd, this._writingBuf, "utf8", this.release);
50630
51135
  };
50631
51136
  } else {
50632
51137
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -50683,7 +51188,7 @@ var require_sonic_boom = __commonJS({
50683
51188
  }
50684
51189
  }
50685
51190
  if (this._fsync) {
50686
- fs24.fsyncSync(this.fd);
51191
+ fs25.fsyncSync(this.fd);
50687
51192
  }
50688
51193
  const len = this._len;
50689
51194
  if (this._reopening) {
@@ -50797,7 +51302,7 @@ var require_sonic_boom = __commonJS({
50797
51302
  const onDrain = () => {
50798
51303
  if (!this._fsync) {
50799
51304
  try {
50800
- fs24.fsync(this.fd, (err) => {
51305
+ fs25.fsync(this.fd, (err) => {
50801
51306
  this._flushPending = false;
50802
51307
  cb(err);
50803
51308
  });
@@ -50899,7 +51404,7 @@ var require_sonic_boom = __commonJS({
50899
51404
  const fd = this.fd;
50900
51405
  this.once("ready", () => {
50901
51406
  if (fd !== this.fd) {
50902
- fs24.close(fd, (err) => {
51407
+ fs25.close(fd, (err) => {
50903
51408
  if (err) {
50904
51409
  return this.emit("error", err);
50905
51410
  }
@@ -50948,7 +51453,7 @@ var require_sonic_boom = __commonJS({
50948
51453
  buf = this._bufs[0];
50949
51454
  }
50950
51455
  try {
50951
- const n = Buffer.isBuffer(buf) ? fs24.writeSync(this.fd, buf) : fs24.writeSync(this.fd, buf, "utf8");
51456
+ const n = Buffer.isBuffer(buf) ? fs25.writeSync(this.fd, buf) : fs25.writeSync(this.fd, buf, "utf8");
50952
51457
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
50953
51458
  buf = releasedBufObj.writingBuf;
50954
51459
  this._len = releasedBufObj.len;
@@ -50964,7 +51469,7 @@ var require_sonic_boom = __commonJS({
50964
51469
  }
50965
51470
  }
50966
51471
  try {
50967
- fs24.fsyncSync(this.fd);
51472
+ fs25.fsyncSync(this.fd);
50968
51473
  } catch {
50969
51474
  }
50970
51475
  }
@@ -50985,7 +51490,7 @@ var require_sonic_boom = __commonJS({
50985
51490
  buf = mergeBuf(this._bufs[0], this._lens[0]);
50986
51491
  }
50987
51492
  try {
50988
- const n = fs24.writeSync(this.fd, buf);
51493
+ const n = fs25.writeSync(this.fd, buf);
50989
51494
  buf = buf.subarray(n);
50990
51495
  this._len = Math.max(this._len - n, 0);
50991
51496
  if (buf.length <= 0) {
@@ -51013,13 +51518,13 @@ var require_sonic_boom = __commonJS({
51013
51518
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
51014
51519
  if (this.sync) {
51015
51520
  try {
51016
- const written = Buffer.isBuffer(this._writingBuf) ? fs24.writeSync(this.fd, this._writingBuf) : fs24.writeSync(this.fd, this._writingBuf, "utf8");
51521
+ const written = Buffer.isBuffer(this._writingBuf) ? fs25.writeSync(this.fd, this._writingBuf) : fs25.writeSync(this.fd, this._writingBuf, "utf8");
51017
51522
  release(null, written);
51018
51523
  } catch (err) {
51019
51524
  release(err);
51020
51525
  }
51021
51526
  } else {
51022
- fs24.write(this.fd, this._writingBuf, release);
51527
+ fs25.write(this.fd, this._writingBuf, release);
51023
51528
  }
51024
51529
  }
51025
51530
  function actualWriteBuffer() {
@@ -51028,7 +51533,7 @@ var require_sonic_boom = __commonJS({
51028
51533
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
51029
51534
  if (this.sync) {
51030
51535
  try {
51031
- const written = fs24.writeSync(this.fd, this._writingBuf);
51536
+ const written = fs25.writeSync(this.fd, this._writingBuf);
51032
51537
  release(null, written);
51033
51538
  } catch (err) {
51034
51539
  release(err);
@@ -51037,7 +51542,7 @@ var require_sonic_boom = __commonJS({
51037
51542
  if (kCopyBuffer) {
51038
51543
  this._writingBuf = Buffer.from(this._writingBuf);
51039
51544
  }
51040
- fs24.write(this.fd, this._writingBuf, release);
51545
+ fs25.write(this.fd, this._writingBuf, release);
51041
51546
  }
51042
51547
  }
51043
51548
  function actualClose(sonic) {
@@ -51053,12 +51558,12 @@ var require_sonic_boom = __commonJS({
51053
51558
  sonic._lens = [];
51054
51559
  assert2(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
51055
51560
  try {
51056
- fs24.fsync(sonic.fd, closeWrapped);
51561
+ fs25.fsync(sonic.fd, closeWrapped);
51057
51562
  } catch {
51058
51563
  }
51059
51564
  function closeWrapped() {
51060
51565
  if (sonic.fd !== 1 && sonic.fd !== 2) {
51061
- fs24.close(sonic.fd, done);
51566
+ fs25.close(sonic.fd, done);
51062
51567
  } else {
51063
51568
  done();
51064
51569
  }
@@ -51894,15 +52399,15 @@ var require_transport = __commonJS({
51894
52399
  if (!unquoted) {
51895
52400
  return false;
51896
52401
  }
51897
- let path26 = unquoted;
51898
- if (path26.startsWith("file://")) {
52402
+ let path27 = unquoted;
52403
+ if (path27.startsWith("file://")) {
51899
52404
  try {
51900
- path26 = fileURLToPath(path26);
52405
+ path27 = fileURLToPath(path27);
51901
52406
  } catch {
51902
52407
  return false;
51903
52408
  }
51904
52409
  }
51905
- return isAbsolute(path26) && !existsSync(path26);
52410
+ return isAbsolute(path27) && !existsSync(path27);
51906
52411
  }
51907
52412
  function stripQuotes(value) {
51908
52413
  const first = value[0];
@@ -85969,15 +86474,15 @@ var require_tools2 = __commonJS({
85969
86474
  * @param {String} path - Mailbox path to encode
85970
86475
  * @returns {String} Encoded mailbox path
85971
86476
  */
85972
- encodePath(connection, path26) {
85973
- path26 = (path26 || "").toString();
85974
- if (!connection.enabled.has("UTF8=ACCEPT") && !tools.isRev2Active(connection) && /[&\x00-\x08\x0b-\x0c\x0e-\x1f\u0080-\uffff]/.test(path26)) {
86477
+ encodePath(connection, path27) {
86478
+ path27 = (path27 || "").toString();
86479
+ if (!connection.enabled.has("UTF8=ACCEPT") && !tools.isRev2Active(connection) && /[&\x00-\x08\x0b-\x0c\x0e-\x1f\u0080-\uffff]/.test(path27)) {
85975
86480
  try {
85976
- path26 = iconv.encode(path26, "utf-7-imap").toString();
86481
+ path27 = iconv.encode(path27, "utf-7-imap").toString();
85977
86482
  } catch {
85978
86483
  }
85979
86484
  }
85980
- return path26;
86485
+ return path27;
85981
86486
  },
85982
86487
  /**
85983
86488
  * Decodes a mailbox path from modified UTF-7 if the server does not support UTF8=ACCEPT.
@@ -85986,15 +86491,15 @@ var require_tools2 = __commonJS({
85986
86491
  * @param {String} path - Mailbox path to decode
85987
86492
  * @returns {String} Decoded mailbox path
85988
86493
  */
85989
- decodePath(connection, path26) {
85990
- path26 = (path26 || "").toString();
85991
- if (!connection.enabled.has("UTF8=ACCEPT") && !tools.isRev2Active(connection) && /[&]/.test(path26)) {
86494
+ decodePath(connection, path27) {
86495
+ path27 = (path27 || "").toString();
86496
+ if (!connection.enabled.has("UTF8=ACCEPT") && !tools.isRev2Active(connection) && /[&]/.test(path27)) {
85992
86497
  try {
85993
- path26 = iconv.decode(Buffer.from(path26), "utf-7-imap").toString();
86498
+ path27 = iconv.decode(Buffer.from(path27), "utf-7-imap").toString();
85994
86499
  } catch {
85995
86500
  }
85996
86501
  }
85997
- return path26;
86502
+ return path27;
85998
86503
  },
85999
86504
  /**
86000
86505
  * Normalizes a mailbox path by joining array segments with the namespace delimiter,
@@ -86005,17 +86510,17 @@ var require_tools2 = __commonJS({
86005
86510
  * @param {Boolean} [skipNamespace] - If true, skips prepending the namespace prefix
86006
86511
  * @returns {String} Normalized mailbox path
86007
86512
  */
86008
- normalizePath(connection, path26, skipNamespace) {
86009
- if (Array.isArray(path26)) {
86010
- path26 = path26.join(connection.namespace && connection.namespace.delimiter || "");
86513
+ normalizePath(connection, path27, skipNamespace) {
86514
+ if (Array.isArray(path27)) {
86515
+ path27 = path27.join(connection.namespace && connection.namespace.delimiter || "");
86011
86516
  }
86012
- if (path26.toUpperCase() === "INBOX") {
86517
+ if (path27.toUpperCase() === "INBOX") {
86013
86518
  return "INBOX";
86014
86519
  }
86015
- if (!skipNamespace && connection.namespace && connection.namespace.prefix && !path26.startsWith(connection.namespace.prefix)) {
86016
- path26 = connection.namespace.prefix + path26;
86520
+ if (!skipNamespace && connection.namespace && connection.namespace.prefix && !path27.startsWith(connection.namespace.prefix)) {
86521
+ path27 = connection.namespace.prefix + path27;
86017
86522
  }
86018
- return path26;
86523
+ return path27;
86019
86524
  },
86020
86525
  /**
86021
86526
  * Compares two mailbox paths for equality after normalization.
@@ -86346,14 +86851,14 @@ var require_tools2 = __commonJS({
86346
86851
  }
86347
86852
  }
86348
86853
  if (map.emailId || map.uid) {
86349
- let path26 = mailbox.path;
86350
- if (/[\u0080-\uffff]/.test(path26)) {
86854
+ let path27 = mailbox.path;
86855
+ if (/[\u0080-\uffff]/.test(path27)) {
86351
86856
  try {
86352
- path26 = iconv.encode(path26, "utf-7-imap").toString();
86857
+ path27 = iconv.encode(path27, "utf-7-imap").toString();
86353
86858
  } catch {
86354
86859
  }
86355
86860
  }
86356
- map.id = map.emailId || createHash2("md5").update([path26, mailbox.uidValidity?.toString() || "", map.uid.toString()].join(":")).digest("hex");
86861
+ map.id = map.emailId || createHash2("md5").update([path27, mailbox.uidValidity?.toString() || "", map.uid.toString()].join(":")).digest("hex");
86357
86862
  }
86358
86863
  if (map.flags) {
86359
86864
  let flagColor = tools.getFlagColor(map.flags);
@@ -86528,16 +87033,16 @@ var require_tools2 = __commonJS({
86528
87033
  * @returns {Object} Parsed body structure tree with part numbers, types, parameters, and child nodes
86529
87034
  */
86530
87035
  parseBodystructure(entry) {
86531
- let walk = (node, path26) => {
86532
- path26 = path26 || [];
87036
+ let walk = (node, path27) => {
87037
+ path27 = path27 || [];
86533
87038
  let curNode = {}, i = 0, part = 0;
86534
- if (path26.length) {
86535
- curNode.part = path26.join(".");
87039
+ if (path27.length) {
87040
+ curNode.part = path27.join(".");
86536
87041
  }
86537
87042
  if (Array.isArray(node[0])) {
86538
87043
  curNode.childNodes = [];
86539
87044
  while (Array.isArray(node[i])) {
86540
- curNode.childNodes.push(walk(node[i], path26.concat(++part)));
87045
+ curNode.childNodes.push(walk(node[i], path27.concat(++part)));
86541
87046
  i++;
86542
87047
  }
86543
87048
  curNode.type = "multipart/" + ((node[i++] || {}).value || "").toString().toLowerCase();
@@ -86580,7 +87085,7 @@ var require_tools2 = __commonJS({
86580
87085
  // the encapsulated message shares the part number with its wrapper.
86581
87086
  // Distinction is via suffixes: path.MIME = wrapper headers,
86582
87087
  // path.HEADER = encapsulated message headers.
86583
- walk(node[i], path26)
87088
+ walk(node[i], path27)
86584
87089
  ];
86585
87090
  }
86586
87091
  i++;
@@ -88542,14 +89047,14 @@ var require_select = __commonJS({
88542
89047
  // update into a silent no-op for the rest of the session.
88543
89048
  permanentflags: { key: "permanentFlags", parse: (value) => Array.isArray(value) && new Set(value) }
88544
89049
  });
88545
- module.exports = async (connection, path26, options) => {
89050
+ module.exports = async (connection, path27, options) => {
88546
89051
  if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state)) {
88547
89052
  return;
88548
89053
  }
88549
89054
  options = options || {};
88550
- path26 = normalizePath(connection, path26);
88551
- if (!connection.folders.has(path26)) {
88552
- let folders = await connection.run("LIST", "", path26);
89055
+ path27 = normalizePath(connection, path27);
89056
+ if (!connection.folders.has(path27)) {
89057
+ let folders = await connection.run("LIST", "", path27);
88553
89058
  if (!folders) {
88554
89059
  throw new Error("Failed to fetch folders");
88555
89060
  }
@@ -88557,10 +89062,10 @@ var require_select = __commonJS({
88557
89062
  connection.folders.set(folder.path, folder);
88558
89063
  });
88559
89064
  }
88560
- let folderListData = connection.folders.has(path26) ? connection.folders.get(path26) : false;
89065
+ let folderListData = connection.folders.has(path27) ? connection.folders.get(path27) : false;
88561
89066
  let response;
88562
89067
  try {
88563
- let map = { path: path26 };
89068
+ let map = { path: path27 };
88564
89069
  if (folderListData) {
88565
89070
  ["delimiter", "specialUse", "subscribed", "listed"].forEach((key) => {
88566
89071
  if (folderListData[key]) {
@@ -88579,7 +89084,7 @@ var require_select = __commonJS({
88579
89084
  ]);
88580
89085
  map.qresync = true;
88581
89086
  }
88582
- let encodedPath = encodePath(connection, path26);
89087
+ let encodedPath = encodePath(connection, path27);
88583
89088
  let selectCommand = {
88584
89089
  command: !options.readOnly ? "SELECT" : "EXAMINE",
88585
89090
  /* c8 ignore next */
@@ -88639,12 +89144,12 @@ var require_select = __commonJS({
88639
89144
  // since the client's last known state. Only received when QRESYNC was requested.
88640
89145
  // A dummy mailbox object is passed because the mailbox isn't officially open yet.
88641
89146
  VANISHED: async (untagged) => {
88642
- await connection.untaggedVanished(untagged, { path: path26, uidNext: false, uidValidity: false });
89147
+ await connection.untaggedVanished(untagged, { path: path27, uidNext: false, uidValidity: false });
88643
89148
  },
88644
89149
  // Untagged FETCH during SELECT/EXAMINE: only occurs with QRESYNC, delivering
88645
89150
  // updated flags for messages that changed since the client's last modseq.
88646
89151
  FETCH: async (untagged) => {
88647
- await connection.untaggedFetch(untagged, { path: path26, uidNext: false, uidValidity: false });
89152
+ await connection.untaggedFetch(untagged, { path: path27, uidNext: false, uidValidity: false });
88648
89153
  }
88649
89154
  }
88650
89155
  });
@@ -88658,13 +89163,13 @@ var require_select = __commonJS({
88658
89163
  }
88659
89164
  let currentMailbox = connection.mailbox;
88660
89165
  connection.mailbox = false;
88661
- if (currentMailbox && currentMailbox.path !== path26) {
89166
+ if (currentMailbox && currentMailbox.path !== path27) {
88662
89167
  connection.emit("mailboxClose", currentMailbox);
88663
89168
  }
88664
89169
  connection.mailbox = map;
88665
89170
  connection.currentSelectCommand = selectCommand;
88666
89171
  connection.state = connection.states.SELECTED;
88667
- if (!currentMailbox || currentMailbox.path !== path26) {
89172
+ if (!currentMailbox || currentMailbox.path !== path27) {
88668
89173
  connection.emit("mailboxOpen", connection.mailbox);
88669
89174
  }
88670
89175
  response.next();
@@ -88879,17 +89384,17 @@ var require_create = __commonJS({
88879
89384
  "node_modules/imapflow/lib/commands/create.js"(exports, module) {
88880
89385
  "use strict";
88881
89386
  var { encodePath, normalizePath, getStatusCode, enhanceCommandError } = require_tools2();
88882
- module.exports = async (connection, path26) => {
89387
+ module.exports = async (connection, path27) => {
88883
89388
  if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state)) {
88884
89389
  return;
88885
89390
  }
88886
- path26 = normalizePath(connection, path26);
89391
+ path27 = normalizePath(connection, path27);
88887
89392
  let response;
88888
89393
  try {
88889
89394
  let map = {
88890
- path: path26
89395
+ path: path27
88891
89396
  };
88892
- response = await connection.exec("CREATE", [{ type: "ATOM", value: encodePath(connection, path26) }]);
89397
+ response = await connection.exec("CREATE", [{ type: "ATOM", value: encodePath(connection, path27) }]);
88893
89398
  let section = response.response.attributes && response.response.attributes[0] && response.response.attributes[0].section && response.response.attributes[0].section.length ? response.response.attributes[0].section : false;
88894
89399
  if (section) {
88895
89400
  let key;
@@ -88915,13 +89420,13 @@ var require_create = __commonJS({
88915
89420
  }
88916
89421
  map.created = true;
88917
89422
  response.next();
88918
- await connection.run("SUBSCRIBE", path26);
89423
+ await connection.run("SUBSCRIBE", path27);
88919
89424
  return map;
88920
89425
  } catch (err) {
88921
89426
  let errorCode = getStatusCode(err.response);
88922
89427
  if (errorCode === "ALREADYEXISTS") {
88923
89428
  return {
88924
- path: path26,
89429
+ path: path27,
88925
89430
  created: false
88926
89431
  };
88927
89432
  }
@@ -88938,20 +89443,20 @@ var require_delete = __commonJS({
88938
89443
  "node_modules/imapflow/lib/commands/delete.js"(exports, module) {
88939
89444
  "use strict";
88940
89445
  var { encodePath, normalizePath, enhanceCommandError } = require_tools2();
88941
- module.exports = async (connection, path26) => {
89446
+ module.exports = async (connection, path27) => {
88942
89447
  if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state)) {
88943
89448
  return;
88944
89449
  }
88945
- path26 = normalizePath(connection, path26);
88946
- if (connection.state === connection.states.SELECTED && connection.mailbox.path === path26) {
89450
+ path27 = normalizePath(connection, path27);
89451
+ if (connection.state === connection.states.SELECTED && connection.mailbox.path === path27) {
88947
89452
  await connection.run("CLOSE");
88948
89453
  }
88949
89454
  let response;
88950
89455
  try {
88951
89456
  let map = {
88952
- path: path26
89457
+ path: path27
88953
89458
  };
88954
- response = await connection.exec("DELETE", [{ type: "ATOM", value: encodePath(connection, path26) }]);
89459
+ response = await connection.exec("DELETE", [{ type: "ATOM", value: encodePath(connection, path27) }]);
88955
89460
  response.next();
88956
89461
  return map;
88957
89462
  } catch (err) {
@@ -88968,23 +89473,23 @@ var require_rename = __commonJS({
88968
89473
  "node_modules/imapflow/lib/commands/rename.js"(exports, module) {
88969
89474
  "use strict";
88970
89475
  var { encodePath, normalizePath, enhanceCommandError } = require_tools2();
88971
- module.exports = async (connection, path26, newPath) => {
89476
+ module.exports = async (connection, path27, newPath) => {
88972
89477
  if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state)) {
88973
89478
  return;
88974
89479
  }
88975
- path26 = normalizePath(connection, path26);
89480
+ path27 = normalizePath(connection, path27);
88976
89481
  newPath = normalizePath(connection, newPath);
88977
- if (connection.state === connection.states.SELECTED && connection.mailbox.path === path26) {
89482
+ if (connection.state === connection.states.SELECTED && connection.mailbox.path === path27) {
88978
89483
  await connection.run("CLOSE");
88979
89484
  }
88980
89485
  let response;
88981
89486
  try {
88982
89487
  let map = {
88983
- path: path26,
89488
+ path: path27,
88984
89489
  newPath
88985
89490
  };
88986
89491
  response = await connection.exec("RENAME", [
88987
- { type: "ATOM", value: encodePath(connection, path26) },
89492
+ { type: "ATOM", value: encodePath(connection, path27) },
88988
89493
  { type: "ATOM", value: encodePath(connection, newPath) }
88989
89494
  ]);
88990
89495
  response.next();
@@ -89031,14 +89536,14 @@ var require_subscribe = __commonJS({
89031
89536
  "node_modules/imapflow/lib/commands/subscribe.js"(exports, module) {
89032
89537
  "use strict";
89033
89538
  var { encodePath, normalizePath, enhanceCommandError } = require_tools2();
89034
- module.exports = async (connection, path26) => {
89539
+ module.exports = async (connection, path27) => {
89035
89540
  if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state)) {
89036
89541
  return;
89037
89542
  }
89038
- path26 = normalizePath(connection, path26);
89543
+ path27 = normalizePath(connection, path27);
89039
89544
  let response;
89040
89545
  try {
89041
- response = await connection.exec("SUBSCRIBE", [{ type: "ATOM", value: encodePath(connection, path26) }]);
89546
+ response = await connection.exec("SUBSCRIBE", [{ type: "ATOM", value: encodePath(connection, path27) }]);
89042
89547
  response.next();
89043
89548
  return true;
89044
89549
  } catch (err) {
@@ -89055,14 +89560,14 @@ var require_unsubscribe = __commonJS({
89055
89560
  "node_modules/imapflow/lib/commands/unsubscribe.js"(exports, module) {
89056
89561
  "use strict";
89057
89562
  var { encodePath, normalizePath, enhanceCommandError } = require_tools2();
89058
- module.exports = async (connection, path26) => {
89563
+ module.exports = async (connection, path27) => {
89059
89564
  if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state)) {
89060
89565
  return;
89061
89566
  }
89062
- path26 = normalizePath(connection, path26);
89567
+ path27 = normalizePath(connection, path27);
89063
89568
  let response;
89064
89569
  try {
89065
- response = await connection.exec("UNSUBSCRIBE", [{ type: "ATOM", value: encodePath(connection, path26) }]);
89570
+ response = await connection.exec("UNSUBSCRIBE", [{ type: "ATOM", value: encodePath(connection, path27) }]);
89066
89571
  response.next();
89067
89572
  return true;
89068
89573
  } catch (err) {
@@ -89868,11 +90373,11 @@ var require_status = __commonJS({
89868
90373
  var { encodePath, normalizePath, buildStatusQueryAttributes, isRev2Active } = require_tools2();
89869
90374
  var { parseStatusList } = require_status_fields();
89870
90375
  var MAILBOX_UPDATERS = {
89871
- messages: (value, connection, path26) => {
90376
+ messages: (value, connection, path27) => {
89872
90377
  let prevCount = connection.mailbox.exists;
89873
90378
  if (prevCount !== value) {
89874
90379
  connection.mailbox.exists = value;
89875
- connection.emit("exists", { path: path26, count: value, prevCount });
90380
+ connection.emit("exists", { path: path27, count: value, prevCount });
89876
90381
  }
89877
90382
  },
89878
90383
  uidNext: (value, connection) => {
@@ -89882,28 +90387,28 @@ var require_status = __commonJS({
89882
90387
  connection.mailbox.highestModseq = value;
89883
90388
  }
89884
90389
  };
89885
- module.exports = async (connection, path26, query) => {
89886
- if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state) || !path26) {
90390
+ module.exports = async (connection, path27, query) => {
90391
+ if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state) || !path27) {
89887
90392
  return false;
89888
90393
  }
89889
- path26 = normalizePath(connection, path26);
89890
- let encodedPath = encodePath(connection, path26);
90394
+ path27 = normalizePath(connection, path27);
90395
+ let encodedPath = encodePath(connection, path27);
89891
90396
  let attributes = [{ type: encodedPath.indexOf("&") >= 0 ? "STRING" : "ATOM", value: encodedPath }];
89892
90397
  let queryAttributes = buildStatusQueryAttributes(connection, query);
89893
90398
  let syntheticRecent = query && query.recent && isRev2Active(connection);
89894
90399
  if (!queryAttributes.length) {
89895
- return syntheticRecent ? { path: path26, recent: 0 } : false;
90400
+ return syntheticRecent ? { path: path27, recent: 0 } : false;
89896
90401
  }
89897
90402
  attributes.push(queryAttributes);
89898
90403
  let response;
89899
90404
  try {
89900
- let map = { path: path26 };
90405
+ let map = { path: path27 };
89901
90406
  response = await connection.exec("STATUS", attributes, {
89902
90407
  untagged: {
89903
90408
  // STATUS response: * STATUS <mailbox> (<key> <value> <key> <value> ...)
89904
90409
  // Parsed as alternating key-value pairs (i % 2 pattern).
89905
90410
  STATUS: async (untagged) => {
89906
- let updateCurrent = connection.state === connection.states.SELECTED && path26 === connection.mailbox.path;
90411
+ let updateCurrent = connection.state === connection.states.SELECTED && path27 === connection.mailbox.path;
89907
90412
  let list = untagged.attributes && Array.isArray(untagged.attributes[1]) ? untagged.attributes[1] : false;
89908
90413
  if (!list) {
89909
90414
  return;
@@ -89911,7 +90416,7 @@ var require_status = __commonJS({
89911
90416
  parseStatusList(list, (key, value) => {
89912
90417
  map[key] = value;
89913
90418
  if (updateCurrent && MAILBOX_UPDATERS[key]) {
89914
- MAILBOX_UPDATERS[key](value, connection, path26);
90419
+ MAILBOX_UPDATERS[key](value, connection, path27);
89915
90420
  }
89916
90421
  });
89917
90422
  }
@@ -89924,9 +90429,9 @@ var require_status = __commonJS({
89924
90429
  return map;
89925
90430
  } catch (err) {
89926
90431
  if (err.responseStatus === "NO") {
89927
- let folders = await connection.run("LIST", "", path26, { listOnly: true });
90432
+ let folders = await connection.run("LIST", "", path27, { listOnly: true });
89928
90433
  if (folders && !folders.length) {
89929
- let error2 = new Error(`Mailbox doesn't exist: ${path26}`);
90434
+ let error2 = new Error(`Mailbox doesn't exist: ${path27}`);
89930
90435
  error2.code = "NotFound";
89931
90436
  error2.response = err;
89932
90437
  throw error2;
@@ -90073,15 +90578,15 @@ var require_quota = __commonJS({
90073
90578
  "node_modules/imapflow/lib/commands/quota.js"(exports, module) {
90074
90579
  "use strict";
90075
90580
  var { encodePath, normalizePath, enhanceCommandError, parseUintValue, isUnsafeKey } = require_tools2();
90076
- module.exports = async (connection, path26) => {
90077
- if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state) || !path26) {
90581
+ module.exports = async (connection, path27) => {
90582
+ if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state) || !path27) {
90078
90583
  return;
90079
90584
  }
90080
90585
  if (!connection.capabilities.has("QUOTA")) {
90081
90586
  return false;
90082
90587
  }
90083
- path26 = normalizePath(connection, path26);
90084
- let map = { path: path26 };
90588
+ path27 = normalizePath(connection, path27);
90589
+ let map = { path: path27 };
90085
90590
  let processQuotaResponse = (untagged) => {
90086
90591
  let attributes = untagged.attributes && untagged.attributes[1];
90087
90592
  if (!attributes || !attributes.length) {
@@ -90121,7 +90626,7 @@ var require_quota = __commonJS({
90121
90626
  let quotaFound = false;
90122
90627
  let response;
90123
90628
  try {
90124
- response = await connection.exec("GETQUOTAROOT", [{ type: "ATOM", value: encodePath(connection, path26) }], {
90629
+ response = await connection.exec("GETQUOTAROOT", [{ type: "ATOM", value: encodePath(connection, path27) }], {
90125
90630
  untagged: {
90126
90631
  // QUOTAROOT response tells us which quota root applies to this mailbox.
90127
90632
  // A mailbox may have zero or one quota root.
@@ -90264,15 +90769,15 @@ var require_idle = __commonJS({
90264
90769
  }
90265
90770
  }
90266
90771
  async function pollOnce2(connection, session) {
90267
- let path26 = connection.mailbox && connection.mailbox.path;
90772
+ let path27 = connection.mailbox && connection.mailbox.path;
90268
90773
  switch (connection.missingIdleCommand) {
90269
90774
  case "SELECT":
90270
90775
  connection.log.debug({ src: "c", msg: `Running SELECT to detect changes in folder`, cid: connection.id });
90271
- await connection.runInternal("SELECT", path26, { readOnly: session.selectCommand.command === "EXAMINE" });
90776
+ await connection.runInternal("SELECT", path27, { readOnly: session.selectCommand.command === "EXAMINE" });
90272
90777
  break;
90273
90778
  case "STATUS": {
90274
90779
  connection.log.debug({ src: "c", msg: `Running STATUS to detect changes in folder`, cid: connection.id });
90275
- let status = await connection.runInternal("STATUS", path26, {
90780
+ let status = await connection.runInternal("STATUS", path27, {
90276
90781
  messages: true,
90277
90782
  uidNext: true,
90278
90783
  uidValidity: true,
@@ -91959,12 +92464,12 @@ var require_imap_flow = __commonJS({
91959
92464
  this.emit("flags", updateEvent);
91960
92465
  }
91961
92466
  }
91962
- async ensureSelectedMailbox(path26) {
91963
- if (!path26) {
92467
+ async ensureSelectedMailbox(path27) {
92468
+ if (!path27) {
91964
92469
  return false;
91965
92470
  }
91966
- if (!this.mailbox || !comparePaths(this, this.mailbox.path, path26)) {
91967
- return await this.mailboxOpen(path26);
92471
+ if (!this.mailbox || !comparePaths(this, this.mailbox.path, path27)) {
92472
+ return await this.mailboxOpen(path27);
91968
92473
  }
91969
92474
  return true;
91970
92475
  }
@@ -92379,9 +92884,9 @@ var require_imap_flow = __commonJS({
92379
92884
  * let quota = await client.getQuota();
92380
92885
  * console.log(quota.storage.used, quota.storage.limit)
92381
92886
  */
92382
- async getQuota(path26) {
92383
- path26 = path26 || "INBOX";
92384
- return await this.run("QUOTA", path26);
92887
+ async getQuota(path27) {
92888
+ path27 = path27 || "INBOX";
92889
+ return await this.run("QUOTA", path27);
92385
92890
  }
92386
92891
  /**
92387
92892
  * @typedef {Object} ListResponse
@@ -92491,8 +92996,8 @@ var require_imap_flow = __commonJS({
92491
92996
  * console.log(info.path);
92492
92997
  * // "INBOX.parent.child" // assumes "INBOX." as namespace prefix and "." as delimiter
92493
92998
  */
92494
- async mailboxCreate(path26) {
92495
- return await this.run("CREATE", path26);
92999
+ async mailboxCreate(path27) {
93000
+ return await this.run("CREATE", path27);
92496
93001
  }
92497
93002
  /**
92498
93003
  * @typedef {Object} MailboxRenameResponse
@@ -92513,8 +93018,8 @@ var require_imap_flow = __commonJS({
92513
93018
  * console.log(info.newPath);
92514
93019
  * // "INBOX.Important stuff ❗️" // assumes "INBOX." as namespace prefix
92515
93020
  */
92516
- async mailboxRename(path26, newPath) {
92517
- return await this.run("RENAME", path26, newPath);
93021
+ async mailboxRename(path27, newPath) {
93022
+ return await this.run("RENAME", path27, newPath);
92518
93023
  }
92519
93024
  /**
92520
93025
  * @typedef {Object} MailboxDeleteResponse
@@ -92533,8 +93038,8 @@ var require_imap_flow = __commonJS({
92533
93038
  * console.log(info.path);
92534
93039
  * // "INBOX.Important stuff ❗️" // assumes "INBOX." as namespace prefix
92535
93040
  */
92536
- async mailboxDelete(path26) {
92537
- return await this.run("DELETE", path26);
93041
+ async mailboxDelete(path27) {
93042
+ return await this.run("DELETE", path27);
92538
93043
  }
92539
93044
  /**
92540
93045
  * Subscribes to a mailbox
@@ -92545,8 +93050,8 @@ var require_imap_flow = __commonJS({
92545
93050
  * @example
92546
93051
  * await client.mailboxSubscribe('Important stuff ❗️');
92547
93052
  */
92548
- async mailboxSubscribe(path26) {
92549
- return await this.run("SUBSCRIBE", path26);
93053
+ async mailboxSubscribe(path27) {
93054
+ return await this.run("SUBSCRIBE", path27);
92550
93055
  }
92551
93056
  /**
92552
93057
  * Unsubscribes from a mailbox
@@ -92557,8 +93062,8 @@ var require_imap_flow = __commonJS({
92557
93062
  * @example
92558
93063
  * await client.mailboxUnsubscribe('Important stuff ❗️');
92559
93064
  */
92560
- async mailboxUnsubscribe(path26) {
92561
- return await this.run("UNSUBSCRIBE", path26);
93065
+ async mailboxUnsubscribe(path27) {
93066
+ return await this.run("UNSUBSCRIBE", path27);
92562
93067
  }
92563
93068
  /**
92564
93069
  * Opens a mailbox to access messages. You can perform message operations only against an opened mailbox.
@@ -92576,8 +93081,8 @@ var require_imap_flow = __commonJS({
92576
93081
  * console.log(mailbox.exists);
92577
93082
  * // 125
92578
93083
  */
92579
- async mailboxOpen(path26, options) {
92580
- return await this.run("SELECT", path26, options);
93084
+ async mailboxOpen(path27, options) {
93085
+ return await this.run("SELECT", path27, options);
92581
93086
  }
92582
93087
  /**
92583
93088
  * Closes a previously opened mailbox
@@ -92624,8 +93129,8 @@ var require_imap_flow = __commonJS({
92624
93129
  * console.log(status.unseen);
92625
93130
  * // 123
92626
93131
  */
92627
- async status(path26, query) {
92628
- return await this.run("STATUS", path26, query);
93132
+ async status(path27, query) {
93133
+ return await this.run("STATUS", path27, query);
92629
93134
  }
92630
93135
  /**
92631
93136
  * Starts listening for new or deleted messages from the currently opened mailbox. Only required if {@link ImapFlow#disableAutoIdle} is set to `true`
@@ -92879,8 +93384,8 @@ var require_imap_flow = __commonJS({
92879
93384
  * @example
92880
93385
  * await client.append('INBOX', rawMessageBuffer, ['\\Seen'], new Date(2000, 1, 1));
92881
93386
  */
92882
- async append(path26, content, flags, idate) {
92883
- return await this.run("APPEND", path26, content, flags, idate) || false;
93387
+ async append(path27, content, flags, idate) {
93388
+ return await this.run("APPEND", path27, content, flags, idate) || false;
92884
93389
  }
92885
93390
  /**
92886
93391
  * @typedef {Object} CopyResponseObject
@@ -93724,7 +94229,7 @@ var require_imap_flow = __commonJS({
93724
94229
  await new Promise((resolve2) => setImmediate(resolve2));
93725
94230
  }
93726
94231
  const lock = this.locks.shift();
93727
- const { resolve, reject, path: path26, options, lockId } = lock;
94232
+ const { resolve, reject, path: path27, options, lockId } = lock;
93728
94233
  if (lock.acquireTimer) {
93729
94234
  clearTimeout(lock.acquireTimer);
93730
94235
  lock.acquireTimer = null;
@@ -93740,7 +94245,7 @@ var require_imap_flow = __commonJS({
93740
94245
  this.log.warn({
93741
94246
  msg: "Mailbox lock held for a long time",
93742
94247
  lockId: lock.lockId,
93743
- path: path26,
94248
+ path: path27,
93744
94249
  heldFor: Date.now() - lock.heldAt,
93745
94250
  /* c8 ignore next */
93746
94251
  // the held-lock-warning diagnostic with a description set is a timing-dependent log detail
@@ -93776,52 +94281,52 @@ var require_imap_flow = __commonJS({
93776
94281
  }
93777
94282
  };
93778
94283
  if (!this.usable || !this.socket || this.socket.destroyed) {
93779
- this.log.trace({ msg: "Failed to acquire mailbox lock", path: path26, lockId, idling: this.idling });
94284
+ this.log.trace({ msg: "Failed to acquire mailbox lock", path: path27, lockId, idling: this.idling });
93780
94285
  let error2 = new Error("Connection not available");
93781
94286
  error2.code = "NoConnection";
93782
94287
  reject(error2);
93783
94288
  continue;
93784
94289
  }
93785
- if (this.mailbox && this.mailbox.path === path26 && !!this.mailbox.readOnly === !!options.readOnly) {
94290
+ if (this.mailbox && this.mailbox.path === path27 && !!this.mailbox.readOnly === !!options.readOnly) {
93786
94291
  this.log.trace({
93787
94292
  msg: "Mailbox lock acquired [existing]",
93788
- path: path26,
94293
+ path: path27,
93789
94294
  lockId,
93790
94295
  idling: this.idling,
93791
94296
  ...options.description && { description: options.description }
93792
94297
  });
93793
94298
  this.currentLock = lock;
93794
94299
  armHeldTimer();
93795
- resolve({ path: path26, release });
94300
+ resolve({ path: path27, release });
93796
94301
  break;
93797
94302
  }
93798
94303
  try {
93799
- await this.mailboxOpen(path26, options);
94304
+ await this.mailboxOpen(path27, options);
93800
94305
  this.log.trace({
93801
94306
  msg: "Mailbox lock acquired [selected]",
93802
- path: path26,
94307
+ path: path27,
93803
94308
  lockId,
93804
94309
  idling: this.idling,
93805
94310
  ...options.description && { description: options.description }
93806
94311
  });
93807
94312
  this.currentLock = lock;
93808
94313
  armHeldTimer();
93809
- resolve({ path: path26, release });
94314
+ resolve({ path: path27, release });
93810
94315
  break;
93811
94316
  } catch (err) {
93812
94317
  if (err.responseStatus === "NO") {
93813
94318
  try {
93814
- let folders = await this.run("LIST", "", path26, { listOnly: true });
94319
+ let folders = await this.run("LIST", "", path27, { listOnly: true });
93815
94320
  if (!folders || !folders.length) {
93816
94321
  err.mailboxMissing = true;
93817
94322
  }
93818
94323
  } catch (E) {
93819
- this.log.trace({ msg: "Failed to verify failed mailbox", path: path26, err: E });
94324
+ this.log.trace({ msg: "Failed to verify failed mailbox", path: path27, err: E });
93820
94325
  }
93821
94326
  }
93822
94327
  this.log.trace({
93823
94328
  msg: "Failed to acquire mailbox lock",
93824
- path: path26,
94329
+ path: path27,
93825
94330
  lockId,
93826
94331
  idling: this.idling,
93827
94332
  ...options.description && { description: options.description },
@@ -93860,13 +94365,13 @@ var require_imap_flow = __commonJS({
93860
94365
  * lock.release();
93861
94366
  * }
93862
94367
  */
93863
- getMailboxLock(path26, options) {
94368
+ getMailboxLock(path27, options) {
93864
94369
  options = options || {};
93865
- path26 = normalizePath(this, path26);
94370
+ path27 = normalizePath(this, path27);
93866
94371
  let lockId = ++this.lockCounter;
93867
94372
  this.log.trace({
93868
94373
  msg: "Requesting lock",
93869
- path: path26,
94374
+ path: path27,
93870
94375
  lockId,
93871
94376
  ...options.description && { description: options.description },
93872
94377
  activeLock: this.currentLock ? {
@@ -93875,7 +94380,7 @@ var require_imap_flow = __commonJS({
93875
94380
  } : null
93876
94381
  });
93877
94382
  let lockPromise = new Promise((resolve, reject) => {
93878
- let lockEntry = { resolve, reject, path: path26, options, lockId };
94383
+ let lockEntry = { resolve, reject, path: path27, options, lockId };
93879
94384
  this.locks.push(lockEntry);
93880
94385
  if (Number(options.acquireTimeout) > 0) {
93881
94386
  lockEntry.acquireTimer = setTimeout(() => {
@@ -93964,16 +94469,16 @@ var require_imap_flow = __commonJS({
93964
94469
  });
93965
94470
 
93966
94471
  // src/mail/mail-fetcher.ts
93967
- import fs23 from "node:fs/promises";
93968
- import path24 from "node:path";
94472
+ import fs24 from "node:fs/promises";
94473
+ import path25 from "node:path";
93969
94474
  async function saveAttachments(attachments, downloadDir) {
93970
94475
  if (attachments.length === 0) return [];
93971
- await fs23.mkdir(downloadDir, { recursive: true });
94476
+ await fs24.mkdir(downloadDir, { recursive: true });
93972
94477
  const saved = [];
93973
94478
  for (const a of attachments) {
93974
- const name = path24.basename(a.filename) || "attachment";
93975
- const savedPath = path24.join(downloadDir, name);
93976
- await fs23.writeFile(savedPath, a.content);
94479
+ const name = path25.basename(a.filename) || "attachment";
94480
+ const savedPath = path25.join(downloadDir, name);
94481
+ await fs24.writeFile(savedPath, a.content);
93977
94482
  saved.push({ filename: name, savedPath, size: a.content.length });
93978
94483
  }
93979
94484
  return saved;
@@ -94104,7 +94609,7 @@ var init_mail_fetcher = __esm({
94104
94609
 
94105
94610
  // src/tools/fetch-mail.ts
94106
94611
  import os17 from "node:os";
94107
- import path25 from "node:path";
94612
+ import path26 from "node:path";
94108
94613
  function registerFetchMailTool(server, deps = {}) {
94109
94614
  const makeFetcher = deps.fetcherFactory ?? ((creds) => new ImapflowMailFetcher(creds));
94110
94615
  server.registerTool(
@@ -94166,7 +94671,7 @@ var init_fetch_mail = __esm({
94166
94671
  init_mail_fetcher();
94167
94672
  init_auth_error();
94168
94673
  init_validate();
94169
- DEFAULT_DOWNLOAD_DIR = path25.join(os17.homedir(), ".cogent", "mail-downloads");
94674
+ DEFAULT_DOWNLOAD_DIR = path26.join(os17.homedir(), ".cogent", "mail-downloads");
94170
94675
  }
94171
94676
  });
94172
94677
 
@@ -94257,9 +94762,74 @@ var init_rotate_mail_creds = __esm({
94257
94762
 
94258
94763
  // src/index.ts
94259
94764
  var index_exports = {};
94765
+ function shutdown(reason) {
94766
+ if (shuttingDown) return;
94767
+ shuttingDown = true;
94768
+ try {
94769
+ logger.info(`Shutting down: ${reason}`);
94770
+ autoRelay.stop();
94771
+ withdrawBridge(process.cwd(), PROCESS_MARK);
94772
+ heartbeat.stop();
94773
+ if (cloudWsClient) {
94774
+ cloudWsClient.disconnect();
94775
+ }
94776
+ logger.close();
94777
+ } finally {
94778
+ process.exit(0);
94779
+ }
94780
+ }
94781
+ async function clientGone(reason) {
94782
+ if (draining || shuttingDown) return;
94783
+ draining = true;
94784
+ autoRelay.announceDraining();
94785
+ const busy = () => {
94786
+ const d = autoRelay.getDiagnostics();
94787
+ return { requests: stdoutDead ? [] : [...inFlightRequests.values()], wake: d.processing, queued: d.queueLength };
94788
+ };
94789
+ const b0 = busy();
94790
+ if (!b0.requests.length && !b0.wake && !b0.queued) return shutdown(reason);
94791
+ let capMs = drainCapMs(3e5);
94792
+ try {
94793
+ capMs = drainCapMs(getConfig().COGENT_TIMEOUT_MS);
94794
+ } catch {
94795
+ }
94796
+ logger.info(
94797
+ `Client gone (${reason}) \u2014 draining before exit (cap ${Math.round(capMs / 1e3)}s): ${b0.requests.length} MCP request(s) [${b0.requests.join(", ")}], wake running: ${b0.wake}, queued: ${b0.queued}`
94798
+ );
94799
+ cancelPresenceRetry();
94800
+ heartbeat.stop();
94801
+ cloudWsClient?.disconnect();
94802
+ const t0 = Date.now();
94803
+ let b = b0;
94804
+ while (Date.now() - t0 < capMs) {
94805
+ b = busy();
94806
+ if (!b.requests.length && !b.wake && !b.queued) break;
94807
+ await new Promise((r) => setTimeout(r, 50));
94808
+ }
94809
+ if (!stdoutDead) {
94810
+ await new Promise((r) => {
94811
+ const t = setTimeout(r, 1e3);
94812
+ try {
94813
+ process.stdout.write("", () => {
94814
+ clearTimeout(t);
94815
+ r();
94816
+ });
94817
+ } catch {
94818
+ clearTimeout(t);
94819
+ r();
94820
+ }
94821
+ });
94822
+ }
94823
+ const left = b.requests.length || b.wake || b.queued;
94824
+ const undeliverable = stdoutDead && inFlightRequests.size ? ` (${inFlightRequests.size} MCP response(s) undeliverable: stdout closed)` : "";
94825
+ shutdown(
94826
+ left ? `${reason}; drain cap reached after ${Date.now() - t0}ms \u2014 ABANDONED ${b.requests.length} MCP request(s) [${b.requests.join(", ")}], wake running: ${b.wake}, queued: ${b.queued}${undeliverable}` : `${reason}; drained in ${Date.now() - t0}ms (${b0.requests.length} MCP request(s), wake: ${b0.wake}, queued: ${b0.queued})${undeliverable}`
94827
+ );
94828
+ }
94260
94829
  async function main() {
94830
+ markBridgeProcess(process.cwd(), SERVER_VERSION);
94261
94831
  await runStartup();
94262
- const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
94832
+ const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION }, { instructions: SERVER_INSTRUCTIONS });
94263
94833
  registerRegisterPeerTool(server);
94264
94834
  registerDeregisterPeerTool(server);
94265
94835
  registerSendMessageTool(server);
@@ -94304,7 +94874,31 @@ async function main() {
94304
94874
  });
94305
94875
  }
94306
94876
  const transport = new StdioServerTransport();
94877
+ transport.onclose = () => void clientGone("MCP transport closed");
94307
94878
  await server.connect(transport);
94879
+ const sdkOnMessage = transport.onmessage;
94880
+ transport.onmessage = (msg) => {
94881
+ if ("method" in msg && "id" in msg) {
94882
+ const tool = msg.method === "tools/call" ? msg.params?.name : void 0;
94883
+ inFlightRequests.set(msg.id, typeof tool === "string" ? `tools/call ${tool}` : msg.method);
94884
+ } else if ("method" in msg && msg.method === "notifications/cancelled") {
94885
+ const id = msg.params?.requestId;
94886
+ if (typeof id === "string" || typeof id === "number") inFlightRequests.delete(id);
94887
+ }
94888
+ sdkOnMessage?.(msg);
94889
+ };
94890
+ const sdkSend = transport.send.bind(transport);
94891
+ transport.send = (msg) => {
94892
+ const settles = !("method" in msg) && "id" in msg ? msg.id : void 0;
94893
+ return sdkSend(msg).finally(() => {
94894
+ if (settles !== void 0) inFlightRequests.delete(settles);
94895
+ });
94896
+ };
94897
+ const stdinIsDevice = startedWithoutClient();
94898
+ if (!process.stdin.isTTY && !stdinIsDevice) {
94899
+ process.stdin.once("end", () => void clientGone("stdin EOF \u2014 the MCP client is gone"));
94900
+ process.stdin.once("close", () => void clientGone("stdin closed \u2014 the MCP client is gone"));
94901
+ }
94308
94902
  process.stderr.write(
94309
94903
  `${SERVER_NAME} v${SERVER_VERSION} running on stdio
94310
94904
  `
@@ -94329,14 +94923,17 @@ async function main() {
94329
94923
  "Server ready. Tools: cogent_register_peer, cogent_deregister_peer, cogent_send_message, cogent_list_peers, cogent_get_history, cogent_health_check, cogent_create_session, cogent_join_session"
94330
94924
  );
94331
94925
  }
94926
+ var shuttingDown, draining, stdoutDead, inFlightRequests;
94332
94927
  var init_index = __esm({
94333
94928
  "src/index.ts"() {
94334
94929
  "use strict";
94335
94930
  init_mcp();
94336
94931
  init_stdio2();
94337
94932
  init_constants();
94933
+ init_instructions();
94338
94934
  init_npm_update_check();
94339
94935
  init_startup();
94936
+ init_config();
94340
94937
  init_logger();
94341
94938
  init_register_peer();
94342
94939
  init_send_message();
@@ -94352,10 +94949,12 @@ var init_index = __esm({
94352
94949
  init_fetch_mail();
94353
94950
  init_rotate_mail_creds();
94354
94951
  init_heartbeat();
94952
+ init_wake_inflight();
94953
+ init_wake_ownership();
94355
94954
  process.on("uncaughtException", (err) => {
94356
94955
  if (err.code === "EPIPE") {
94357
- logger.close();
94358
- process.exit(0);
94956
+ stdoutDead = true;
94957
+ void clientGone("stdout EPIPE \u2014 the MCP client is gone");
94359
94958
  return;
94360
94959
  }
94361
94960
  logger.error(`Uncaught exception: ${err.message}`, { stack: err.stack });
@@ -94364,26 +94963,12 @@ var init_index = __esm({
94364
94963
  const msg = reason instanceof Error ? reason.message : String(reason);
94365
94964
  logger.error(`Unhandled rejection: ${msg}`);
94366
94965
  });
94367
- process.on("SIGTERM", () => {
94368
- logger.info("SIGTERM received, shutting down");
94369
- autoRelay.stop();
94370
- heartbeat.stop();
94371
- if (cloudWsClient) {
94372
- cloudWsClient.disconnect();
94373
- }
94374
- logger.close();
94375
- process.exit(0);
94376
- });
94377
- process.on("SIGINT", () => {
94378
- logger.info("SIGINT received, shutting down");
94379
- autoRelay.stop();
94380
- heartbeat.stop();
94381
- if (cloudWsClient) {
94382
- cloudWsClient.disconnect();
94383
- }
94384
- logger.close();
94385
- process.exit(0);
94386
- });
94966
+ shuttingDown = false;
94967
+ draining = false;
94968
+ stdoutDead = false;
94969
+ process.on("SIGTERM", () => shutdown("SIGTERM received"));
94970
+ process.on("SIGINT", () => shutdown("SIGINT received"));
94971
+ inFlightRequests = /* @__PURE__ */ new Map();
94387
94972
  main().catch((err) => {
94388
94973
  const msg = err instanceof Error ? err.message : String(err);
94389
94974
  logger.error(`Fatal startup error: ${msg}`);