@essentialai/cogent-plugin 3.19.2 → 3.20.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogent",
3
- "version": "3.19.2",
3
+ "version": "3.20.1",
4
4
  "description": "Inter-session communication bridge for Claude Code with Slack integration. Enables CC agents and Slack team members to communicate in real time.",
5
5
  "author": {
6
6
  "name": "Essential AI Solutions",
@@ -1259,8 +1259,8 @@ var init_parseUtil = __esm({
1259
1259
  init_errors();
1260
1260
  init_en();
1261
1261
  makeIssue = (params) => {
1262
- const { data, path: path17, errorMaps, issueData } = params;
1263
- const fullPath = [...path17, ...issueData.path || []];
1262
+ const { data, path: path18, errorMaps, issueData } = params;
1263
+ const fullPath = [...path18, ...issueData.path || []];
1264
1264
  const fullIssue = {
1265
1265
  ...issueData,
1266
1266
  path: fullPath
@@ -1540,11 +1540,11 @@ var init_types = __esm({
1540
1540
  init_parseUtil();
1541
1541
  init_util();
1542
1542
  ParseInputLazyPath = class {
1543
- constructor(parent, value, path17, key) {
1543
+ constructor(parent, value, path18, key) {
1544
1544
  this._cachedPath = [];
1545
1545
  this.parent = parent;
1546
1546
  this.data = value;
1547
- this._path = path17;
1547
+ this._path = path18;
1548
1548
  this._key = key;
1549
1549
  }
1550
1550
  get path() {
@@ -4996,10 +4996,10 @@ function assignProp(target, prop, value) {
4996
4996
  configurable: true
4997
4997
  });
4998
4998
  }
4999
- function getElementAtPath(obj, path17) {
5000
- if (!path17)
4999
+ function getElementAtPath(obj, path18) {
5000
+ if (!path18)
5001
5001
  return obj;
5002
- return path17.reduce((acc, key) => acc?.[key], obj);
5002
+ return path18.reduce((acc, key) => acc?.[key], obj);
5003
5003
  }
5004
5004
  function promiseAllObject(promisesObj) {
5005
5005
  const keys = Object.keys(promisesObj);
@@ -5248,11 +5248,11 @@ function aborted(x, startIndex = 0) {
5248
5248
  }
5249
5249
  return false;
5250
5250
  }
5251
- function prefixIssues(path17, issues) {
5251
+ function prefixIssues(path18, issues) {
5252
5252
  return issues.map((iss) => {
5253
5253
  var _a;
5254
5254
  (_a = iss).path ?? (_a.path = []);
5255
- iss.path.unshift(path17);
5255
+ iss.path.unshift(path18);
5256
5256
  return iss;
5257
5257
  });
5258
5258
  }
@@ -17058,8 +17058,8 @@ var require_utils = __commonJS({
17058
17058
  }
17059
17059
  return ind;
17060
17060
  }
17061
- function removeDotSegments(path17) {
17062
- let input = path17;
17061
+ function removeDotSegments(path18) {
17062
+ let input = path18;
17063
17063
  const output = [];
17064
17064
  let nextSlash = -1;
17065
17065
  let len = 0;
@@ -17258,8 +17258,8 @@ var require_schemes = __commonJS({
17258
17258
  wsComponent.secure = void 0;
17259
17259
  }
17260
17260
  if (wsComponent.resourceName) {
17261
- const [path17, query] = wsComponent.resourceName.split("?");
17262
- wsComponent.path = path17 && path17 !== "/" ? path17 : void 0;
17261
+ const [path18, query] = wsComponent.resourceName.split("?");
17262
+ wsComponent.path = path18 && path18 !== "/" ? path18 : void 0;
17263
17263
  wsComponent.query = query;
17264
17264
  wsComponent.resourceName = void 0;
17265
17265
  }
@@ -20612,12 +20612,12 @@ var require_dist = __commonJS({
20612
20612
  throw new Error(`Unknown format "${name}"`);
20613
20613
  return f;
20614
20614
  };
20615
- function addFormats(ajv, list, fs15, exportName) {
20615
+ function addFormats(ajv, list, fs16, exportName) {
20616
20616
  var _a;
20617
20617
  var _b;
20618
20618
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
20619
20619
  for (const f of list)
20620
- ajv.addFormat(f, fs15[f]);
20620
+ ajv.addFormat(f, fs16[f]);
20621
20621
  }
20622
20622
  module.exports = exports = formatsPlugin;
20623
20623
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -21729,8 +21729,8 @@ var require_parseUtil = __commonJS({
21729
21729
  var errors_js_1 = require_errors2();
21730
21730
  var en_js_1 = __importDefault(require_en());
21731
21731
  var makeIssue2 = (params) => {
21732
- const { data, path: path17, errorMaps, issueData } = params;
21733
- const fullPath = [...path17, ...issueData.path || []];
21732
+ const { data, path: path18, errorMaps, issueData } = params;
21733
+ const fullPath = [...path18, ...issueData.path || []];
21734
21734
  const fullIssue = {
21735
21735
  ...issueData,
21736
21736
  path: fullPath
@@ -21884,11 +21884,11 @@ var require_types2 = __commonJS({
21884
21884
  var parseUtil_js_1 = require_parseUtil();
21885
21885
  var util_js_1 = require_util2();
21886
21886
  var ParseInputLazyPath2 = class {
21887
- constructor(parent, value, path17, key) {
21887
+ constructor(parent, value, path18, key) {
21888
21888
  this._cachedPath = [];
21889
21889
  this.parent = parent;
21890
21890
  this.data = value;
21891
- this._path = path17;
21891
+ this._path = path18;
21892
21892
  this._key = key;
21893
21893
  }
21894
21894
  get path() {
@@ -26389,8 +26389,8 @@ var init_stdio2 = __esm({
26389
26389
  // src/constants.ts
26390
26390
  import { createRequire } from "node:module";
26391
26391
  function resolveVersion() {
26392
- if ("3.19.2") {
26393
- return "3.19.2";
26392
+ if ("3.20.1") {
26393
+ return "3.20.1";
26394
26394
  }
26395
26395
  try {
26396
26396
  const require2 = createRequire(import.meta.url);
@@ -26610,6 +26610,18 @@ var init_config = __esm({
26610
26610
  COGENT_PLATFORM: import_zod2.z.enum(["cc", "codex", "slack", "gchat", "web"]).default("cc"),
26611
26611
  COGENT_CODEX_PATH: import_zod2.z.string().default("codex"),
26612
26612
  COGENT_CODEX_SANDBOX: import_zod2.z.enum(["full-auto", "read-only", "workspace-write", "danger-full-access", "bypass"]).default("full-auto"),
26613
+ // Codex real-time-wake rail selector. codex >=0.145 has a SINGLE-WRITER
26614
+ // thread-store: a codex agent running in an interactive TUI holds an exclusive
26615
+ // writer on its own thread, so the current rail (`exec-resume` =
26616
+ // `codex exec resume <same id>`) fast-fails `-32600 already has an active
26617
+ // writer` and the peer never gets a real-time reply. The `app-server` rail
26618
+ // injects the peer's message as a turn THROUGH codex's app-server daemon (the
26619
+ // daemon is the single writer; the human's `codex --remote` TUI and the bridge
26620
+ // are both just clients → no conflict). DEFAULT stays `exec-resume` so this is
26621
+ // purely additive/opt-in: nothing changes for any agent until a codex user
26622
+ // launches via the daemon (see bin/cogent-codex) and sets this to `app-server`.
26623
+ // Only affects COGENT_PLATFORM=codex; claude/gemini/mail rails never read it.
26624
+ COGENT_CODEX_WAKE: import_zod2.z.enum(["exec-resume", "app-server"]).default("exec-resume"),
26613
26625
  // Auto-recovery for a blocked Codex sandbox. On a host where the OS sandbox
26614
26626
  // can't initialize (unprivileged user namespaces blocked — Ubuntu 24.04's
26615
26627
  // apparmor_restrict_unprivileged_userns, containers, hardened kernels),
@@ -27241,6 +27253,8 @@ var init_cc_cli = __esm({
27241
27253
  import { execFile as execFile2 } from "node:child_process";
27242
27254
  import { promisify as promisify2 } from "node:util";
27243
27255
  import fs8 from "node:fs/promises";
27256
+ import { join } from "node:path";
27257
+ import os5 from "node:os";
27244
27258
  function analyzeCodexHelp(helpText) {
27245
27259
  const checked = EXPECTED_CODEX_RESUME_FLAGS.map((f) => f.flag);
27246
27260
  const missing = EXPECTED_CODEX_RESUME_FLAGS.filter(
@@ -27345,6 +27359,27 @@ async function preflightCodexSandbox(config2) {
27345
27359
  }
27346
27360
  return res;
27347
27361
  }
27362
+ function detectCodexCogentServerCollision(configTomlText) {
27363
+ return /^[ \t]*\[mcp_servers\.cogent(\.[^\]]+)?\]/m.test(configTomlText);
27364
+ }
27365
+ function codexConfigPath(codexHomeDir2) {
27366
+ const home = codexHomeDir2 || process.env.CODEX_HOME || join(os5.homedir(), ".codex");
27367
+ return join(home, "config.toml");
27368
+ }
27369
+ async function preflightCodexServerCollision(codexHomeDir2) {
27370
+ const cfgPath = codexConfigPath(codexHomeDir2);
27371
+ let text;
27372
+ try {
27373
+ text = await fs8.readFile(cfgPath, "utf8");
27374
+ } catch {
27375
+ return false;
27376
+ }
27377
+ if (!detectCodexCogentServerCollision(text)) return false;
27378
+ logger.error(
27379
+ `Codex config collision: ${cfgPath} defines a standalone [mcp_servers.cogent] server, which has the SAME NAME as the cogent@cogent plugin's MCP server. Codex collapses the two by name and MCP startup can fail ("MCP client for cogent failed to start \u2026 connection closed: initialize response"). FIX: remove the [mcp_servers.cogent] block from ${cfgPath} \u2014 the plugin already provides the cogent tools \u2014 then restart Codex (new thread). Use the plugin OR a manual 'codex mcp add cogent', never both.`
27380
+ );
27381
+ return true;
27382
+ }
27348
27383
  var execFileAsync, EXPECTED_CODEX_RESUME_FLAGS;
27349
27384
  var init_codex_preflight = __esm({
27350
27385
  "src/services/codex-preflight.ts"() {
@@ -27589,7 +27624,7 @@ var init_http_backend = __esm({
27589
27624
  * in cloud mode we ignore it and use this.sessionId (the cloud session).
27590
27625
  */
27591
27626
  async registerPeer(peerId, _sessionId, cwd, label, clientVersion, mode, _channelSessionId, capabilities, workspaceId, threadId) {
27592
- const path17 = PATHS.peers.replace(":sessionId", this.sessionId);
27627
+ const path18 = PATHS.peers.replace(":sessionId", this.sessionId);
27593
27628
  const body = {
27594
27629
  peerId,
27595
27630
  cwd,
@@ -27617,7 +27652,7 @@ var init_http_backend = __esm({
27617
27652
  body.role = roleCap.slice("role:".length);
27618
27653
  }
27619
27654
  }
27620
- return this.http.post(path17, body);
27655
+ return this.http.post(path18, body);
27621
27656
  }
27622
27657
  /**
27623
27658
  * Deregister a peer from the cloud session.
@@ -27629,9 +27664,9 @@ var init_http_backend = __esm({
27629
27664
  * local mail creds. A 404 (peer not found) resolves to `{ removed: false, ... }`, not a throw.
27630
27665
  */
27631
27666
  async deregisterPeer(peerId) {
27632
- const path17 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
27667
+ const path18 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
27633
27668
  try {
27634
- const headers = await this.http.delete(path17, {});
27669
+ const headers = await this.http.delete(path18, {});
27635
27670
  const mailboxDeprovisioned = headers.get(MAILBOX_DEPROVISIONED_HEADER) === "true";
27636
27671
  return { removed: true, mailboxDeprovisioned };
27637
27672
  } catch {
@@ -27652,8 +27687,8 @@ var init_http_backend = __esm({
27652
27687
  * GET /api/sessions/:sessionId/peers
27653
27688
  */
27654
27689
  async listPeers() {
27655
- const path17 = PATHS.peers.replace(":sessionId", this.sessionId);
27656
- const result = await this.http.get(path17);
27690
+ const path18 = PATHS.peers.replace(":sessionId", this.sessionId);
27691
+ const result = await this.http.get(path18);
27657
27692
  return result.peers;
27658
27693
  }
27659
27694
  /**
@@ -27661,8 +27696,8 @@ var init_http_backend = __esm({
27661
27696
  * Calls the lightweight heartbeat endpoint on the cloud relay server.
27662
27697
  */
27663
27698
  async updateLastSeen(peerId) {
27664
- const path17 = PATHS.heartbeat.replace(":sessionId", this.sessionId);
27665
- await this.http.post(path17, { peerId });
27699
+ const path18 = PATHS.heartbeat.replace(":sessionId", this.sessionId);
27700
+ await this.http.post(path18, { peerId });
27666
27701
  }
27667
27702
  /**
27668
27703
  * Record a message in the cloud session.
@@ -27673,7 +27708,7 @@ var init_http_backend = __esm({
27673
27708
  * the input record to construct a full MessageRecord.
27674
27709
  */
27675
27710
  async recordMessage(record2) {
27676
- const path17 = PATHS.messages.replace(":sessionId", this.sessionId);
27711
+ const path18 = PATHS.messages.replace(":sessionId", this.sessionId);
27677
27712
  const body = {
27678
27713
  fromPeerId: record2.fromPeerId,
27679
27714
  toPeerId: record2.toPeerId,
@@ -27685,7 +27720,7 @@ var init_http_backend = __esm({
27685
27720
  if (record2.attachments && record2.attachments.length > 0) {
27686
27721
  body.attachments = record2.attachments;
27687
27722
  }
27688
- const resp = await this.http.post(path17, body);
27723
+ const resp = await this.http.post(path18, body);
27689
27724
  return {
27690
27725
  ...record2,
27691
27726
  id: resp.id,
@@ -27699,7 +27734,7 @@ var init_http_backend = __esm({
27699
27734
  * Supports optional peerId filter and limit (default 300).
27700
27735
  */
27701
27736
  async getHistory(peerId, limit, includeRelayEchoes, metadataOnly, dedupInferredEchoes) {
27702
- const path17 = PATHS.messages.replace(":sessionId", this.sessionId);
27737
+ const path18 = PATHS.messages.replace(":sessionId", this.sessionId);
27703
27738
  const query = {};
27704
27739
  if (peerId) {
27705
27740
  query.peerId = peerId;
@@ -27714,7 +27749,7 @@ var init_http_backend = __esm({
27714
27749
  if (dedupInferredEchoes) {
27715
27750
  query.dedupInferredEchoes = "true";
27716
27751
  }
27717
- const result = await this.http.get(path17, query);
27752
+ const result = await this.http.get(path18, query);
27718
27753
  return result.messages;
27719
27754
  }
27720
27755
  /**
@@ -27725,12 +27760,12 @@ var init_http_backend = __esm({
27725
27760
  * so that get-history shows the actual response (not null).
27726
27761
  */
27727
27762
  async updateMessageResponse(messageId, response, durationMs, pending) {
27728
- const path17 = PATHS.message.replace(":sessionId", this.sessionId).replace(":messageId", messageId);
27763
+ const path18 = PATHS.message.replace(":sessionId", this.sessionId).replace(":messageId", messageId);
27729
27764
  const body = { response, durationMs };
27730
27765
  if (pending === true) {
27731
27766
  body.pending = true;
27732
27767
  }
27733
- await this.http.patch(path17, body);
27768
+ await this.http.patch(path18, body);
27734
27769
  }
27735
27770
  /**
27736
27771
  * Validate a session.
@@ -27872,8 +27907,8 @@ var init_http_client = __esm({
27872
27907
  * Perform an authenticated GET request.
27873
27908
  * Appends query parameters to the URL if provided.
27874
27909
  */
27875
- async get(path17, query) {
27876
- const url = new URL(path17, this.baseUrl);
27910
+ async get(path18, query) {
27911
+ const url = new URL(path18, this.baseUrl);
27877
27912
  if (query) {
27878
27913
  for (const [k, v] of Object.entries(query)) {
27879
27914
  url.searchParams.set(k, v);
@@ -27888,8 +27923,8 @@ var init_http_client = __esm({
27888
27923
  /**
27889
27924
  * Perform an authenticated POST request with a JSON body.
27890
27925
  */
27891
- async post(path17, body) {
27892
- const url = new URL(path17, this.baseUrl);
27926
+ async post(path18, body) {
27927
+ const url = new URL(path18, this.baseUrl);
27893
27928
  const resp = await this.fetchWithTimeout(url, {
27894
27929
  method: "POST",
27895
27930
  headers: this.headers(),
@@ -27907,8 +27942,8 @@ var init_http_client = __esm({
27907
27942
  * Perform an authenticated PATCH request with a JSON body.
27908
27943
  * Used to update existing resources (e.g., message response fields).
27909
27944
  */
27910
- async patch(path17, body) {
27911
- const url = new URL(path17, this.baseUrl);
27945
+ async patch(path18, body) {
27946
+ const url = new URL(path18, this.baseUrl);
27912
27947
  const resp = await this.fetchWithTimeout(url, {
27913
27948
  method: "PATCH",
27914
27949
  headers: this.headers(),
@@ -27933,8 +27968,8 @@ var init_http_client = __esm({
27933
27968
  * "Malformed JSON in request body" 400 from cogent-server <=3.1.2 when
27934
27969
  * any client (including third-party tools) DELETEd a peer without a body.
27935
27970
  */
27936
- async delete(path17, body) {
27937
- const url = new URL(path17, this.baseUrl);
27971
+ async delete(path18, body) {
27972
+ const url = new URL(path18, this.baseUrl);
27938
27973
  const headers = {
27939
27974
  "Authorization": `Bearer ${this.token}`
27940
27975
  };
@@ -32117,14 +32152,14 @@ var init_ws_client = __esm({
32117
32152
  if (this.pollTimer) return;
32118
32153
  const poll = async () => {
32119
32154
  try {
32120
- const path17 = `/api/sessions/${this.opts.sessionId}/poll`;
32155
+ const path18 = `/api/sessions/${this.opts.sessionId}/poll`;
32121
32156
  const query = {
32122
32157
  peerId: this.opts.peerId
32123
32158
  };
32124
32159
  if (this.lastMessageId) {
32125
32160
  query.lastMessageId = this.lastMessageId;
32126
32161
  }
32127
- const result = await this.opts.http.get(path17, query);
32162
+ const result = await this.opts.http.get(path18, query);
32128
32163
  if (result.messages && result.messages.length > 0) {
32129
32164
  this.opts.onMessages(result.messages);
32130
32165
  this.lastMessageId = result.messages[result.messages.length - 1].id;
@@ -32182,7 +32217,7 @@ var init_cloud = __esm({
32182
32217
  // src/services/codex-cli.ts
32183
32218
  import { spawn as spawn2 } from "node:child_process";
32184
32219
  import fs10 from "node:fs/promises";
32185
- import os5 from "node:os";
32220
+ import os6 from "node:os";
32186
32221
  import path11 from "node:path";
32187
32222
  import { randomUUID } from "node:crypto";
32188
32223
  import { createReadStream } from "node:fs";
@@ -32211,7 +32246,7 @@ function execCodex(sessionId, message, cwd, timeoutMs) {
32211
32246
  const config2 = getConfig();
32212
32247
  const effectiveTimeout = timeoutMs ?? config2.COGENT_TIMEOUT_MS;
32213
32248
  const truncated = config2.COGENT_CHAR_LIMIT > 0 && message.length > config2.COGENT_CHAR_LIMIT ? message.slice(0, config2.COGENT_CHAR_LIMIT) + "\n...[truncated]" : message;
32214
- const lastMsgFile = path11.join(os5.tmpdir(), `cogent-codex-${randomUUID()}.txt`);
32249
+ const lastMsgFile = path11.join(os6.tmpdir(), `cogent-codex-${randomUUID()}.txt`);
32215
32250
  const args = [
32216
32251
  "exec",
32217
32252
  "resume",
@@ -32278,11 +32313,13 @@ function execCodex(sessionId, message, cwd, timeoutMs) {
32278
32313
  if (code !== 0) {
32279
32314
  await cleanupFile();
32280
32315
  const sandboxBlocked = SANDBOX_FAILURE_RE.test(stderr);
32316
+ const codexLiveSession = ACTIVE_WRITER_RE.test(stderr);
32281
32317
  safeResolve({
32282
32318
  stdout: "",
32283
32319
  stderr: `CLI_EXEC_FAILED: codex exited with code ${code}. stderr: ${stderr}`,
32284
32320
  exitCode: code,
32285
- ...sandboxBlocked ? { sandboxBlocked: true } : {}
32321
+ ...sandboxBlocked ? { sandboxBlocked: true } : {},
32322
+ ...codexLiveSession ? { codexLiveSession: true } : {}
32286
32323
  });
32287
32324
  return;
32288
32325
  }
@@ -32412,7 +32449,7 @@ async function validateCodexSession(sessionId) {
32412
32449
  return false;
32413
32450
  }
32414
32451
  }
32415
- var sandboxOverride, SANDBOX_FAILURE_RE;
32452
+ var sandboxOverride, SANDBOX_FAILURE_RE, ACTIVE_WRITER_RE;
32416
32453
  var init_codex_cli = __esm({
32417
32454
  "src/services/codex-cli.ts"() {
32418
32455
  "use strict";
@@ -32420,14 +32457,329 @@ var init_codex_cli = __esm({
32420
32457
  init_agent_config();
32421
32458
  sandboxOverride = null;
32422
32459
  SANDBOX_FAILURE_RE = /bwrap:|setting up uid ?map|\/proc\/self\/(uid|gid)_map|newuidmap|user namespaces?|RTM_NEWADDR|unshare[^\n]*(Operation not permitted|Permission denied)|clone3?[^\n]*(EPERM|not permitted)/i;
32460
+ ACTIVE_WRITER_RE = /already has an active writer|thread-store conflict|-32600/i;
32461
+ }
32462
+ });
32463
+
32464
+ // src/services/codex-app-server.ts
32465
+ import path12 from "node:path";
32466
+ import fs11 from "node:fs/promises";
32467
+ import { spawn as spawn3 } from "node:child_process";
32468
+ function appServerSocketPath(codexHome = codexHomeDir()) {
32469
+ return path12.join(codexHome, "app-server-control", "app-server-control.sock");
32470
+ }
32471
+ function rpcUrl(sock) {
32472
+ return `ws+unix://${sock}:/rpc`;
32473
+ }
32474
+ async function ensureDaemon(codexHome = codexHomeDir(), deps = {}) {
32475
+ if (ensuredSock) return ensuredSock;
32476
+ const run = deps.run ?? defaultRun;
32477
+ const socketExists = deps.socketExists ?? defaultSocketExists;
32478
+ const waitMs = deps.waitMs ?? 8e3;
32479
+ const pollMs = deps.pollMs ?? 150;
32480
+ const codexPath = getConfig().COGENT_CODEX_PATH;
32481
+ const env = { ...process.env, CODEX_HOME: codexHome };
32482
+ const res = await run(codexPath, ["app-server", "daemon", "start"], env);
32483
+ if (/managed standalone Codex install not found/i.test(`${res.stdout}
32484
+ ${res.stderr}`)) {
32485
+ throw new Error(
32486
+ "MANAGED_CODEX_REQUIRED: the codex app-server daemon needs the managed-standalone codex install, which the npm/Homebrew @openai/codex package does not provide. Install it with: curl -fsSL https://chatgpt.com/codex/install.sh | sh then relaunch via cogent-codex. (Until then, codex answers at its next turn, not in real time.)"
32487
+ );
32488
+ }
32489
+ let sock = appServerSocketPath(codexHome);
32490
+ try {
32491
+ const parsed = JSON.parse(res.stdout.trim());
32492
+ if (parsed && typeof parsed.socketPath === "string") sock = parsed.socketPath;
32493
+ } catch {
32494
+ }
32495
+ const deadline = Date.now() + waitMs;
32496
+ while (!await socketExists(sock)) {
32497
+ if (Date.now() >= deadline) {
32498
+ throw new Error(
32499
+ `app-server daemon socket did not appear at ${sock} within ${waitMs}ms (stderr: ${res.stderr.trim() || "none"})`
32500
+ );
32501
+ }
32502
+ await sleep2(pollMs);
32503
+ }
32504
+ ensuredSock = sock;
32505
+ return sock;
32506
+ }
32507
+ function isAgentMessage(item) {
32508
+ return typeof item?.type === "string" && /^agent[_-]?message$/i.test(item.type);
32509
+ }
32510
+ function itemText(item) {
32511
+ if (typeof item?.text === "string") return item.text;
32512
+ if (Array.isArray(item?.content)) {
32513
+ return item.content.filter((c2) => typeof c2?.text === "string").map((c2) => c2.text).join("");
32514
+ }
32515
+ return "";
32516
+ }
32517
+ function extractAgentMessageText(notes) {
32518
+ const fromCompleted = [];
32519
+ const fromStream = [];
32520
+ for (const n of notes) {
32521
+ if (n.method === "turn/completed") {
32522
+ const items = n.params?.items ?? n.params?.turn?.items ?? [];
32523
+ for (const it of items) if (isAgentMessage(it)) fromCompleted.push(itemText(it));
32524
+ } else if (n.method === "item/completed") {
32525
+ const it = n.params?.item;
32526
+ if (isAgentMessage(it)) fromStream.push(itemText(it));
32527
+ }
32528
+ }
32529
+ const chosen = fromCompleted.length > 0 ? fromCompleted : fromStream;
32530
+ return chosen.join("").trim();
32531
+ }
32532
+ function sandboxPolicyFor(mode, cwd) {
32533
+ switch (mode) {
32534
+ case "read-only":
32535
+ return { type: "readOnly", networkAccess: false };
32536
+ case "danger-full-access":
32537
+ case "bypass":
32538
+ return { type: "dangerFullAccess" };
32539
+ case "workspace-write":
32540
+ case "full-auto":
32541
+ default:
32542
+ return {
32543
+ type: "workspaceWrite",
32544
+ writableRoots: [cwd],
32545
+ networkAccess: false,
32546
+ excludeTmpdirEnvVar: false,
32547
+ excludeSlashTmp: false
32548
+ };
32549
+ }
32550
+ }
32551
+ function execCodexViaAppServer(threadId, message, cwd, timeoutMs, deps = {}) {
32552
+ const key = deps.idempotencyKey ? `${threadId}::${deps.idempotencyKey}` : null;
32553
+ if (!key) return _injectTurn(threadId, message, cwd, timeoutMs, deps);
32554
+ const existing = idempotencyCache.get(key);
32555
+ if (existing) {
32556
+ return existing.done ? Promise.resolve(existing.done.result) : existing.promise;
32557
+ }
32558
+ const promise = _injectTurn(threadId, message, cwd, timeoutMs, deps);
32559
+ const entry = { promise };
32560
+ idempotencyCache.set(key, entry);
32561
+ void promise.then((result) => {
32562
+ entry.done = { result, at: Date.now() };
32563
+ const t = setTimeout(() => idempotencyCache.delete(key), IDEMPOTENCY_TTL_MS);
32564
+ if (typeof t.unref === "function") t.unref();
32565
+ });
32566
+ return promise;
32567
+ }
32568
+ async function _injectTurn(threadId, message, cwd, timeoutMs, deps = {}) {
32569
+ const config2 = getConfig();
32570
+ const effectiveTimeout = timeoutMs ?? config2.COGENT_TIMEOUT_MS;
32571
+ const truncated = config2.COGENT_CHAR_LIMIT > 0 && message.length > config2.COGENT_CHAR_LIMIT ? message.slice(0, config2.COGENT_CHAR_LIMIT) + "\n...[truncated]" : message;
32572
+ const sock = deps.socketPath ?? appServerSocketPath();
32573
+ const wsFactory = deps.wsFactory ?? defaultWsFactory;
32574
+ let client;
32575
+ try {
32576
+ const ws = wsFactory(rpcUrl(sock), { perMessageDeflate: false, headers: { Host: "localhost" } });
32577
+ client = new AppServerClient(ws);
32578
+ await client.waitOpen();
32579
+ await client.request("initialize", {
32580
+ clientInfo: { name: "cogent-bridge", version: "1" },
32581
+ capabilities: { experimentalApi: true }
32582
+ });
32583
+ } catch (err) {
32584
+ return {
32585
+ stdout: "",
32586
+ stderr: `APP_SERVER_UNAVAILABLE: ${err.message}`,
32587
+ exitCode: 1,
32588
+ appServerUnavailable: true
32589
+ };
32590
+ }
32591
+ try {
32592
+ await client.request("thread/resume", { threadId, cwd });
32593
+ const buffered = [];
32594
+ let myTurnId;
32595
+ const isMine = (msg) => msg.method === "turn/completed" && (myTurnId !== void 0 ? msg.params?.turn?.id === myTurnId : msg.params?.threadId === threadId);
32596
+ let deliver = null;
32597
+ const off = client.onNotification((msg) => {
32598
+ if (msg.method !== "turn/completed") return;
32599
+ if (myTurnId === void 0) {
32600
+ buffered.push(msg);
32601
+ return;
32602
+ }
32603
+ if (isMine(msg) && deliver) {
32604
+ const d = deliver;
32605
+ deliver = null;
32606
+ d(msg);
32607
+ }
32608
+ });
32609
+ const startResult = await client.request("turn/start", {
32610
+ threadId,
32611
+ input: [{ type: "text", text: truncated }],
32612
+ // Bound the injected peer turn by the CONFIGURED sandbox (not the human
32613
+ // session's privilege). See sandboxPolicyFor.
32614
+ sandboxPolicy: sandboxPolicyFor(config2.COGENT_CODEX_SANDBOX, cwd),
32615
+ // Native correlation id (daemon does NOT auto-dedupe on it — our own
32616
+ // idempotency guard does; this is for daemon-side correlation/telemetry).
32617
+ ...deps.idempotencyKey ? { clientUserMessageId: deps.idempotencyKey } : {}
32618
+ });
32619
+ myTurnId = startResult?.turn?.id;
32620
+ const turnCompleted = await new Promise((resolve) => {
32621
+ deliver = resolve;
32622
+ const early = buffered.find(isMine);
32623
+ if (early) {
32624
+ deliver = null;
32625
+ resolve(early);
32626
+ return;
32627
+ }
32628
+ setTimeout(() => {
32629
+ if (deliver) {
32630
+ deliver = null;
32631
+ resolve(null);
32632
+ }
32633
+ }, effectiveTimeout);
32634
+ });
32635
+ off();
32636
+ if (!turnCompleted) {
32637
+ return {
32638
+ stdout: "",
32639
+ stderr: `CLI_TIMEOUT: app-server turn did not complete within ${effectiveTimeout}ms`,
32640
+ exitCode: null
32641
+ };
32642
+ }
32643
+ const text = extractAgentMessageText([turnCompleted]);
32644
+ return {
32645
+ stdout: text,
32646
+ stderr: "",
32647
+ exitCode: 0,
32648
+ captureContract: "event_stream",
32649
+ stable: true
32650
+ };
32651
+ } catch (err) {
32652
+ return {
32653
+ stdout: "",
32654
+ stderr: `CLI_EXEC_FAILED: app-server turn failed: ${err.message}`,
32655
+ exitCode: 1
32656
+ };
32657
+ } finally {
32658
+ client.close();
32659
+ }
32660
+ }
32661
+ var defaultWsFactory, ensuredSock, defaultRun, defaultSocketExists, sleep2, AppServerClient, idempotencyCache, IDEMPOTENCY_TTL_MS;
32662
+ var init_codex_app_server = __esm({
32663
+ "src/services/codex-app-server.ts"() {
32664
+ "use strict";
32665
+ init_wrapper();
32666
+ init_config();
32667
+ init_agent_config();
32668
+ defaultWsFactory = (url, opts) => new wrapper_default(url, opts);
32669
+ ensuredSock = null;
32670
+ defaultRun = (cmd, args, env) => new Promise((resolve) => {
32671
+ const child = spawn3(cmd, args, { env, stdio: ["ignore", "pipe", "pipe"] });
32672
+ let stdout = "";
32673
+ let stderr = "";
32674
+ child.stdout.on("data", (c2) => stdout += c2.toString());
32675
+ child.stderr.on("data", (c2) => stderr += c2.toString());
32676
+ child.on("error", (err) => resolve({ stdout, stderr: stderr + String(err), code: 127 }));
32677
+ child.on("close", (code) => resolve({ stdout, stderr, code }));
32678
+ });
32679
+ defaultSocketExists = async (p) => {
32680
+ try {
32681
+ await fs11.stat(p);
32682
+ return true;
32683
+ } catch {
32684
+ return false;
32685
+ }
32686
+ };
32687
+ sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
32688
+ AppServerClient = class {
32689
+ ws;
32690
+ nextId = 1;
32691
+ pending = /* @__PURE__ */ new Map();
32692
+ notifyListeners = /* @__PURE__ */ new Set();
32693
+ opened;
32694
+ openResolved = false;
32695
+ constructor(ws) {
32696
+ this.ws = ws;
32697
+ this.opened = new Promise((resolve, reject) => {
32698
+ ws.on("open", () => {
32699
+ this.openResolved = true;
32700
+ resolve();
32701
+ });
32702
+ ws.on("unexpected-response", (_req, res) => {
32703
+ if (!this.openResolved) reject(new Error(`app-server ws upgrade rejected: HTTP ${res?.statusCode}`));
32704
+ });
32705
+ ws.on("error", (err) => {
32706
+ if (!this.openResolved) reject(err);
32707
+ else this._failAll(err);
32708
+ });
32709
+ ws.on("close", () => {
32710
+ if (this.openResolved) this._failAll(new Error("app-server ws closed"));
32711
+ });
32712
+ });
32713
+ ws.on("message", (data) => this._onMessage(data));
32714
+ }
32715
+ /** Await the open handshake. Rejects (before open) on connect/upgrade errors. */
32716
+ waitOpen() {
32717
+ return this.opened;
32718
+ }
32719
+ _failAll(err) {
32720
+ for (const [, p] of this.pending) p.reject(err);
32721
+ this.pending.clear();
32722
+ }
32723
+ _onMessage(data) {
32724
+ let msg;
32725
+ try {
32726
+ msg = JSON.parse(data.toString());
32727
+ } catch {
32728
+ return;
32729
+ }
32730
+ if (typeof msg.id === "number" && this.pending.has(msg.id)) {
32731
+ const p = this.pending.get(msg.id);
32732
+ this.pending.delete(msg.id);
32733
+ if (msg.error) p.reject(Object.assign(new Error(msg.error.message ?? "rpc error"), { rpcError: msg.error }));
32734
+ else p.resolve(msg.result);
32735
+ return;
32736
+ }
32737
+ if (typeof msg.method === "string") {
32738
+ for (const cb of this.notifyListeners) cb(msg);
32739
+ }
32740
+ }
32741
+ /** Issue a JSON-RPC request and resolve with its result (rejects on rpc error). */
32742
+ request(method, params) {
32743
+ const id = this.nextId++;
32744
+ const p = new Promise((resolve, reject) => {
32745
+ this.pending.set(id, { resolve, reject });
32746
+ });
32747
+ this.ws.send(JSON.stringify({ jsonrpc: "2.0", id, method, params }));
32748
+ return p;
32749
+ }
32750
+ /** Subscribe to server notifications; returns an unsubscribe fn. */
32751
+ onNotification(cb) {
32752
+ this.notifyListeners.add(cb);
32753
+ return () => this.notifyListeners.delete(cb);
32754
+ }
32755
+ close() {
32756
+ try {
32757
+ this.ws.close();
32758
+ } catch {
32759
+ }
32760
+ }
32761
+ };
32762
+ idempotencyCache = /* @__PURE__ */ new Map();
32763
+ IDEMPOTENCY_TTL_MS = 3e5;
32423
32764
  }
32424
32765
  });
32425
32766
 
32426
32767
  // src/services/exec-remote.ts
32427
- function execRemote(platform, sessionId, message, cwd, timeoutMs) {
32768
+ async function execCodexWake(sessionId, message, cwd, timeoutMs, idempotencyKey) {
32769
+ if (getConfig().COGENT_CODEX_WAKE === "app-server") {
32770
+ try {
32771
+ const sock = await ensureDaemon();
32772
+ const result = await execCodexViaAppServer(sessionId, message, cwd, timeoutMs, { socketPath: sock, idempotencyKey });
32773
+ if (!result.appServerUnavailable) return result;
32774
+ } catch {
32775
+ }
32776
+ }
32777
+ return execCodex(sessionId, message, cwd, timeoutMs);
32778
+ }
32779
+ function execRemote(platform, sessionId, message, cwd, timeoutMs, opts) {
32428
32780
  switch (platform) {
32429
32781
  case "codex":
32430
- return execCodex(sessionId, message, cwd, timeoutMs);
32782
+ return execCodexWake(sessionId, message, cwd, timeoutMs, opts?.idempotencyKey);
32431
32783
  case "cc":
32432
32784
  default:
32433
32785
  return execClaude(sessionId, message, cwd, timeoutMs);
@@ -32463,8 +32815,10 @@ function validateSessionFor(platform, sessionId, cwd) {
32463
32815
  var init_exec_remote = __esm({
32464
32816
  "src/services/exec-remote.ts"() {
32465
32817
  "use strict";
32818
+ init_config();
32466
32819
  init_cc_cli();
32467
32820
  init_codex_cli();
32821
+ init_codex_app_server();
32468
32822
  }
32469
32823
  });
32470
32824
 
@@ -33172,7 +33526,12 @@ var init_auto_relay = __esm({
33172
33526
  getConfig().COGENT_PLATFORM,
33173
33527
  this.localSessionId,
33174
33528
  formatted,
33175
- this.localCwd
33529
+ this.localCwd,
33530
+ void 0,
33531
+ // Idempotency: the codex app-server rail runs turns on the daemon
33532
+ // independent of our client, so a redelivery of this same message must
33533
+ // not start a second turn. Keyed by the stable relay message id.
33534
+ { idempotencyKey: msg.id }
33176
33535
  );
33177
33536
  const durationMs = Date.now() - startMs;
33178
33537
  if (result.exitCode === 0 && result.stdout) {
@@ -33221,7 +33580,7 @@ var init_auto_relay = __esm({
33221
33580
  if (freshId && freshId !== this.localSessionId) {
33222
33581
  logger.info(`Auto-relay: retry with rotated session ${this.localSessionId} -> ${freshId}`);
33223
33582
  this.localSessionId = freshId;
33224
- const retry = await execRemote(getConfig().COGENT_PLATFORM, this.localSessionId, formatted, this.localCwd);
33583
+ const retry = await execRemote(getConfig().COGENT_PLATFORM, this.localSessionId, formatted, this.localCwd, void 0, { idempotencyKey: msg.id });
33225
33584
  if (retry.exitCode === 0 && retry.stdout) {
33226
33585
  await this._relayCapturedReply(msg, retry, traceId, Date.now() - startMs, true);
33227
33586
  replied = true;
@@ -33231,7 +33590,9 @@ var init_auto_relay = __esm({
33231
33590
  }
33232
33591
  }
33233
33592
  if (!replied) {
33234
- await this._recordNoReplyFailure(msg, traceId, Date.now() - startMs);
33593
+ await this._recordNoReplyFailure(msg, traceId, Date.now() - startMs, {
33594
+ codexLiveSession: result.codexLiveSession === true
33595
+ });
33235
33596
  }
33236
33597
  } catch (err) {
33237
33598
  const durationMs = Date.now() - startMs;
@@ -33252,16 +33613,18 @@ var init_auto_relay = __esm({
33252
33613
  * at the next turn boundary. Refusal (AMBIGUOUS_SESSION) and sandbox-blocked
33253
33614
  * paths record their own failure and return before this, so no double-record.
33254
33615
  */
33255
- async _recordNoReplyFailure(msg, traceId, durationMs) {
33616
+ async _recordNoReplyFailure(msg, traceId, durationMs, opts) {
33617
+ const codexLive = opts?.codexLiveSession === true;
33618
+ const message = codexLive ? `\u{1F4E8} Queued for "${this.localPeerId}": it's live in an interactive Codex session, so Cogent can't resume it in real time \u2014 it will see this at its next turn. For real-time replies, launch it via \`cogent-codex\` (COGENT_CODEX_WAKE=app-server). (trace ${traceId})` : `\u26A0\uFE0F Cogent could not capture a reply from "${this.localPeerId}" (it may have been busy, or the resume produced no output). Manual response required \u2014 the target agent must reply in its own session. (trace ${traceId})`;
33256
33619
  try {
33257
33620
  await getBackend().recordMessage({
33258
33621
  fromPeerId: this.localPeerId,
33259
33622
  toPeerId: msg.fromPeerId,
33260
- message: `\u26A0\uFE0F Cogent could not capture a reply from "${this.localPeerId}" (it may have been busy, or the resume produced no output). Manual response required \u2014 the target agent must reply in its own session. (trace ${traceId})`,
33623
+ message,
33261
33624
  response: null,
33262
33625
  durationMs,
33263
33626
  success: false,
33264
- error: "NO_REPLY_CAPTURED",
33627
+ error: codexLive ? "CODEX_LIVE_SESSION" : "NO_REPLY_CAPTURED",
33265
33628
  isRelayEcho: true,
33266
33629
  traceId
33267
33630
  });
@@ -33623,15 +33986,15 @@ __export(startup_exports, {
33623
33986
  runStartup: () => runStartup,
33624
33987
  triggerReRegistration: () => triggerReRegistration
33625
33988
  });
33626
- import os6 from "node:os";
33627
- import path12 from "node:path";
33628
- import fs11 from "node:fs/promises";
33989
+ import os7 from "node:os";
33990
+ import path13 from "node:path";
33991
+ import fs12 from "node:fs/promises";
33629
33992
  import readline2 from "node:readline";
33630
33993
  import { execFile as execFile5 } from "node:child_process";
33631
33994
  import { promisify as promisify4 } from "node:util";
33632
33995
  async function loadPersistedConfig() {
33633
33996
  try {
33634
- const raw = await fs11.readFile(PERSIST_PATH, "utf-8");
33997
+ const raw = await fs12.readFile(PERSIST_PATH, "utf-8");
33635
33998
  return JSON.parse(raw);
33636
33999
  } catch {
33637
34000
  return {};
@@ -33639,7 +34002,7 @@ async function loadPersistedConfig() {
33639
34002
  }
33640
34003
  async function savePersistedConfig(config2) {
33641
34004
  try {
33642
- await fs11.writeFile(PERSIST_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
34005
+ await fs12.writeFile(PERSIST_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
33643
34006
  } catch (err) {
33644
34007
  const msg = err instanceof Error ? err.message : String(err);
33645
34008
  logger.warn(`Could not save config to ${PERSIST_PATH}: ${msg}`);
@@ -33674,11 +34037,11 @@ First run detected. Where should cogent-bridge store its data?
33674
34037
  });
33675
34038
  }
33676
34039
  async function validateStateDir(statePath) {
33677
- await fs11.mkdir(statePath, { recursive: true });
33678
- const testFile = path12.join(statePath, ".write-test");
34040
+ await fs12.mkdir(statePath, { recursive: true });
34041
+ const testFile = path13.join(statePath, ".write-test");
33679
34042
  try {
33680
- await fs11.writeFile(testFile, "test", "utf-8");
33681
- await fs11.unlink(testFile);
34043
+ await fs12.writeFile(testFile, "test", "utf-8");
34044
+ await fs12.unlink(testFile);
33682
34045
  } catch {
33683
34046
  throw new BridgeError(
33684
34047
  "STARTUP_FAILED" /* STARTUP_FAILED */,
@@ -33824,10 +34187,10 @@ async function runStartup() {
33824
34187
  if (envStatePath) {
33825
34188
  statePath = envStatePath;
33826
34189
  } else {
33827
- const defaultPath = path12.join(os6.homedir(), ".cogent");
34190
+ const defaultPath = path13.join(os7.homedir(), ".cogent");
33828
34191
  statePath = await firstRunPrompt(defaultPath);
33829
34192
  }
33830
- if (!envStatePath && statePath !== path12.join(os6.homedir(), ".cogent")) {
34193
+ if (!envStatePath && statePath !== path13.join(os7.homedir(), ".cogent")) {
33831
34194
  process.env.COGENT_STATE_PATH = statePath;
33832
34195
  } else if (!envStatePath) {
33833
34196
  process.env.COGENT_STATE_PATH = statePath;
@@ -33905,7 +34268,7 @@ async function runStartup() {
33905
34268
  }
33906
34269
  initLogger(
33907
34270
  config2.COGENT_LOG_LEVEL,
33908
- path12.join(config2.COGENT_STATE_PATH, "logs")
34271
+ path13.join(config2.COGENT_STATE_PATH, "logs")
33909
34272
  );
33910
34273
  if (isCloud && effectiveSessionId && effectiveToken) {
33911
34274
  cloudInbox = new MessageInbox();
@@ -33935,6 +34298,7 @@ async function runPreflights() {
33935
34298
  const config2 = getConfig();
33936
34299
  if (config2.COGENT_PLATFORM === "codex") {
33937
34300
  await preflightCodex(config2.COGENT_CODEX_PATH);
34301
+ await preflightCodexServerCollision();
33938
34302
  const sandbox = await preflightCodexSandbox(config2);
33939
34303
  if (sandbox.status === "blocked" && config2.COGENT_CODEX_SANDBOX_FALLBACK) {
33940
34304
  setSandboxOverride("bypass");
@@ -33965,7 +34329,7 @@ var init_startup = __esm({
33965
34329
  init_auto_relay();
33966
34330
  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;
33967
34331
  execFileAsync3 = promisify4(execFile5);
33968
- PERSIST_PATH = path12.join(os6.homedir(), ".cogent-config.json");
34332
+ PERSIST_PATH = path13.join(os7.homedir(), ".cogent-config.json");
33969
34333
  legacyWarnEmitted = false;
33970
34334
  cloudWsClient = null;
33971
34335
  cloudInbox = null;
@@ -33976,12 +34340,12 @@ var init_startup = __esm({
33976
34340
 
33977
34341
  // src/cloud/mail-credential-store.ts
33978
34342
  import crypto3 from "node:crypto";
33979
- import fs12 from "node:fs/promises";
33980
- import path13 from "node:path";
33981
- import os7 from "node:os";
34343
+ import fs13 from "node:fs/promises";
34344
+ import path14 from "node:path";
34345
+ import os8 from "node:os";
33982
34346
  function defaultMailCredentialPath(cwd = process.cwd()) {
33983
- const hash = crypto3.createHash("sha256").update(path13.resolve(cwd)).digest("hex").slice(0, 16);
33984
- return path13.join(os7.homedir(), ".cogent", "mail-credentials", `${hash}.json`);
34347
+ const hash = crypto3.createHash("sha256").update(path14.resolve(cwd)).digest("hex").slice(0, 16);
34348
+ return path14.join(os8.homedir(), ".cogent", "mail-credentials", `${hash}.json`);
33985
34349
  }
33986
34350
  function resolveMailCredentialPath(credentialPath) {
33987
34351
  if (credentialPath) return credentialPath;
@@ -33992,16 +34356,16 @@ function resolveMailCredentialPath(credentialPath) {
33992
34356
  async function loadMailCredentials(credentialPath) {
33993
34357
  const filePath = resolveMailCredentialPath(credentialPath);
33994
34358
  try {
33995
- return JSON.parse(await fs12.readFile(filePath, "utf-8"));
34359
+ return JSON.parse(await fs13.readFile(filePath, "utf-8"));
33996
34360
  } catch {
33997
34361
  return null;
33998
34362
  }
33999
34363
  }
34000
34364
  async function saveMailCredentials(creds, credentialPath) {
34001
34365
  const filePath = resolveMailCredentialPath(credentialPath);
34002
- await fs12.mkdir(path13.dirname(filePath), { recursive: true, mode: 448 });
34003
- await fs12.writeFile(filePath, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
34004
- await fs12.chmod(filePath, 384);
34366
+ await fs13.mkdir(path14.dirname(filePath), { recursive: true, mode: 448 });
34367
+ await fs13.writeFile(filePath, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
34368
+ await fs13.chmod(filePath, 384);
34005
34369
  }
34006
34370
  async function persistProvisionedMailbox(mailbox, credentialPath) {
34007
34371
  if (!mailbox?.address || !mailbox.password) return false;
@@ -34022,7 +34386,7 @@ async function persistProvisionedMailbox(mailbox, credentialPath) {
34022
34386
  async function clearMailCredentials(credentialPath) {
34023
34387
  const filePath = resolveMailCredentialPath(credentialPath);
34024
34388
  try {
34025
- await fs12.unlink(filePath);
34389
+ await fs13.unlink(filePath);
34026
34390
  } catch (err) {
34027
34391
  if (err.code !== "ENOENT") throw err;
34028
34392
  }
@@ -35708,13 +36072,13 @@ var init_setup_mail = __esm({
35708
36072
  });
35709
36073
 
35710
36074
  // src/mail/mail-sender.ts
35711
- import fs13 from "node:fs/promises";
35712
- import path14 from "node:path";
36075
+ import fs14 from "node:fs/promises";
36076
+ import path15 from "node:path";
35713
36077
  async function buildAndSendMail(sender, creds, params) {
35714
36078
  const attachments = [];
35715
36079
  for (const p of params.attachmentPaths ?? []) {
35716
- const content = await fs13.readFile(p);
35717
- attachments.push({ filename: path14.basename(p), content });
36080
+ const content = await fs14.readFile(p);
36081
+ attachments.push({ filename: path15.basename(p), content });
35718
36082
  }
35719
36083
  const { messageId } = await sender.send({
35720
36084
  from: creds.address,
@@ -35842,16 +36206,16 @@ var init_send_mail = __esm({
35842
36206
  });
35843
36207
 
35844
36208
  // src/mail/mail-fetcher.ts
35845
- import fs14 from "node:fs/promises";
35846
- import path15 from "node:path";
36209
+ import fs15 from "node:fs/promises";
36210
+ import path16 from "node:path";
35847
36211
  async function saveAttachments(attachments, downloadDir) {
35848
36212
  if (attachments.length === 0) return [];
35849
- await fs14.mkdir(downloadDir, { recursive: true });
36213
+ await fs15.mkdir(downloadDir, { recursive: true });
35850
36214
  const saved = [];
35851
36215
  for (const a of attachments) {
35852
- const name = path15.basename(a.filename) || "attachment";
35853
- const savedPath = path15.join(downloadDir, name);
35854
- await fs14.writeFile(savedPath, a.content);
36216
+ const name = path16.basename(a.filename) || "attachment";
36217
+ const savedPath = path16.join(downloadDir, name);
36218
+ await fs15.writeFile(savedPath, a.content);
35855
36219
  saved.push({ filename: name, savedPath, size: a.content.length });
35856
36220
  }
35857
36221
  return saved;
@@ -35982,8 +36346,8 @@ var init_mail_fetcher = __esm({
35982
36346
  });
35983
36347
 
35984
36348
  // src/tools/fetch-mail.ts
35985
- import os8 from "node:os";
35986
- import path16 from "node:path";
36349
+ import os9 from "node:os";
36350
+ import path17 from "node:path";
35987
36351
  function registerFetchMailTool(server, deps = {}) {
35988
36352
  const makeFetcher = deps.fetcherFactory ?? ((creds) => new ImapflowMailFetcher(creds));
35989
36353
  server.registerTool(
@@ -36045,7 +36409,7 @@ var init_fetch_mail = __esm({
36045
36409
  init_mail_fetcher();
36046
36410
  init_auth_error();
36047
36411
  init_validate();
36048
- DEFAULT_DOWNLOAD_DIR = path16.join(os8.homedir(), ".cogent", "mail-downloads");
36412
+ DEFAULT_DOWNLOAD_DIR = path17.join(os9.homedir(), ".cogent", "mail-downloads");
36049
36413
  }
36050
36414
  });
36051
36415
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@essentialai/cogent-plugin",
3
- "version": "3.19.2",
3
+ "version": "3.20.1",
4
4
  "description": "Cogent — Claude Code plugin (skills + slash-commands + MCP server) for the cross-agent comms fabric.",
5
5
  "author": { "name": "Essential AI Solutions Ltd.", "url": "https://essentialai.uk" },
6
6
  "homepage": "https://cogent.tools",