@essentialai/cogent-plugin 3.14.1 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogent",
3
- "version": "3.14.1",
3
+ "version": "3.15.0",
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: path12, errorMaps, issueData } = params;
1263
- const fullPath = [...path12, ...issueData.path || []];
1262
+ const { data, path: path16, errorMaps, issueData } = params;
1263
+ const fullPath = [...path16, ...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, path12, key) {
1543
+ constructor(parent, value, path16, key) {
1544
1544
  this._cachedPath = [];
1545
1545
  this.parent = parent;
1546
1546
  this.data = value;
1547
- this._path = path12;
1547
+ this._path = path16;
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, path12) {
5000
- if (!path12)
4999
+ function getElementAtPath(obj, path16) {
5000
+ if (!path16)
5001
5001
  return obj;
5002
- return path12.reduce((acc, key) => acc?.[key], obj);
5002
+ return path16.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(path12, issues) {
5251
+ function prefixIssues(path16, issues) {
5252
5252
  return issues.map((iss) => {
5253
5253
  var _a;
5254
5254
  (_a = iss).path ?? (_a.path = []);
5255
- iss.path.unshift(path12);
5255
+ iss.path.unshift(path16);
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(path12) {
17062
- let input = path12;
17061
+ function removeDotSegments(path16) {
17062
+ let input = path16;
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 [path12, query] = wsComponent.resourceName.split("?");
17262
- wsComponent.path = path12 && path12 !== "/" ? path12 : void 0;
17261
+ const [path16, query] = wsComponent.resourceName.split("?");
17262
+ wsComponent.path = path16 && path16 !== "/" ? path16 : 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, fs12, exportName) {
20615
+ function addFormats(ajv, list, fs15, 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, fs12[f]);
20620
+ ajv.addFormat(f, fs15[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: path12, errorMaps, issueData } = params;
21733
- const fullPath = [...path12, ...issueData.path || []];
21732
+ const { data, path: path16, errorMaps, issueData } = params;
21733
+ const fullPath = [...path16, ...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, path12, key) {
21887
+ constructor(parent, value, path16, key) {
21888
21888
  this._cachedPath = [];
21889
21889
  this.parent = parent;
21890
21890
  this.data = value;
21891
- this._path = path12;
21891
+ this._path = path16;
21892
21892
  this._key = key;
21893
21893
  }
21894
21894
  get path() {
@@ -25477,10 +25477,10 @@ var require_zod = __commonJS({
25477
25477
  };
25478
25478
  Object.defineProperty(exports, "__esModule", { value: true });
25479
25479
  exports.z = void 0;
25480
- var z15 = __importStar(require_external());
25481
- exports.z = z15;
25480
+ var z18 = __importStar(require_external());
25481
+ exports.z = z18;
25482
25482
  __exportStar(require_external(), exports);
25483
- exports.default = z15;
25483
+ exports.default = z18;
25484
25484
  }
25485
25485
  });
25486
25486
 
@@ -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.14.1") {
26393
- return "3.14.1";
26392
+ if ("3.15.0") {
26393
+ return "3.15.0";
26394
26394
  }
26395
26395
  try {
26396
26396
  const require2 = createRequire(import.meta.url);
@@ -27562,7 +27562,7 @@ var init_http_backend = __esm({
27562
27562
  * in cloud mode we ignore it and use this.sessionId (the cloud session).
27563
27563
  */
27564
27564
  async registerPeer(peerId, _sessionId, cwd, label, clientVersion, mode, _channelSessionId, capabilities, workspaceId, threadId) {
27565
- const path12 = PATHS.peers.replace(":sessionId", this.sessionId);
27565
+ const path16 = PATHS.peers.replace(":sessionId", this.sessionId);
27566
27566
  const body = {
27567
27567
  peerId,
27568
27568
  cwd,
@@ -27590,7 +27590,7 @@ var init_http_backend = __esm({
27590
27590
  body.role = roleCap.slice("role:".length);
27591
27591
  }
27592
27592
  }
27593
- return this.http.post(path12, body);
27593
+ return this.http.post(path16, body);
27594
27594
  }
27595
27595
  /**
27596
27596
  * Deregister a peer from the cloud session.
@@ -27599,9 +27599,9 @@ var init_http_backend = __esm({
27599
27599
  * Returns true on success, false if the peer was not found.
27600
27600
  */
27601
27601
  async deregisterPeer(peerId) {
27602
- const path12 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
27602
+ const path16 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
27603
27603
  try {
27604
- await this.http.delete(path12, {});
27604
+ await this.http.delete(path16, {});
27605
27605
  return true;
27606
27606
  } catch {
27607
27607
  return false;
@@ -27621,8 +27621,8 @@ var init_http_backend = __esm({
27621
27621
  * GET /api/sessions/:sessionId/peers
27622
27622
  */
27623
27623
  async listPeers() {
27624
- const path12 = PATHS.peers.replace(":sessionId", this.sessionId);
27625
- const result = await this.http.get(path12);
27624
+ const path16 = PATHS.peers.replace(":sessionId", this.sessionId);
27625
+ const result = await this.http.get(path16);
27626
27626
  return result.peers;
27627
27627
  }
27628
27628
  /**
@@ -27630,8 +27630,8 @@ var init_http_backend = __esm({
27630
27630
  * Calls the lightweight heartbeat endpoint on the cloud relay server.
27631
27631
  */
27632
27632
  async updateLastSeen(peerId) {
27633
- const path12 = PATHS.heartbeat.replace(":sessionId", this.sessionId);
27634
- await this.http.post(path12, { peerId });
27633
+ const path16 = PATHS.heartbeat.replace(":sessionId", this.sessionId);
27634
+ await this.http.post(path16, { peerId });
27635
27635
  }
27636
27636
  /**
27637
27637
  * Record a message in the cloud session.
@@ -27642,7 +27642,7 @@ var init_http_backend = __esm({
27642
27642
  * the input record to construct a full MessageRecord.
27643
27643
  */
27644
27644
  async recordMessage(record2) {
27645
- const path12 = PATHS.messages.replace(":sessionId", this.sessionId);
27645
+ const path16 = PATHS.messages.replace(":sessionId", this.sessionId);
27646
27646
  const body = {
27647
27647
  fromPeerId: record2.fromPeerId,
27648
27648
  toPeerId: record2.toPeerId,
@@ -27651,7 +27651,7 @@ var init_http_backend = __esm({
27651
27651
  if (record2.isRelayEcho === true) {
27652
27652
  body.isRelayEcho = true;
27653
27653
  }
27654
- const resp = await this.http.post(path12, body);
27654
+ const resp = await this.http.post(path16, body);
27655
27655
  return {
27656
27656
  ...record2,
27657
27657
  id: resp.id,
@@ -27665,7 +27665,7 @@ var init_http_backend = __esm({
27665
27665
  * Supports optional peerId filter and limit (default 300).
27666
27666
  */
27667
27667
  async getHistory(peerId, limit, includeRelayEchoes, metadataOnly, dedupInferredEchoes) {
27668
- const path12 = PATHS.messages.replace(":sessionId", this.sessionId);
27668
+ const path16 = PATHS.messages.replace(":sessionId", this.sessionId);
27669
27669
  const query = {};
27670
27670
  if (peerId) {
27671
27671
  query.peerId = peerId;
@@ -27680,7 +27680,7 @@ var init_http_backend = __esm({
27680
27680
  if (dedupInferredEchoes) {
27681
27681
  query.dedupInferredEchoes = "true";
27682
27682
  }
27683
- const result = await this.http.get(path12, query);
27683
+ const result = await this.http.get(path16, query);
27684
27684
  return result.messages;
27685
27685
  }
27686
27686
  /**
@@ -27691,12 +27691,12 @@ var init_http_backend = __esm({
27691
27691
  * so that get-history shows the actual response (not null).
27692
27692
  */
27693
27693
  async updateMessageResponse(messageId, response, durationMs, pending) {
27694
- const path12 = PATHS.message.replace(":sessionId", this.sessionId).replace(":messageId", messageId);
27694
+ const path16 = PATHS.message.replace(":sessionId", this.sessionId).replace(":messageId", messageId);
27695
27695
  const body = { response, durationMs };
27696
27696
  if (pending === true) {
27697
27697
  body.pending = true;
27698
27698
  }
27699
- await this.http.patch(path12, body);
27699
+ await this.http.patch(path16, body);
27700
27700
  }
27701
27701
  /**
27702
27702
  * Validate a session.
@@ -27838,8 +27838,8 @@ var init_http_client = __esm({
27838
27838
  * Perform an authenticated GET request.
27839
27839
  * Appends query parameters to the URL if provided.
27840
27840
  */
27841
- async get(path12, query) {
27842
- const url = new URL(path12, this.baseUrl);
27841
+ async get(path16, query) {
27842
+ const url = new URL(path16, this.baseUrl);
27843
27843
  if (query) {
27844
27844
  for (const [k, v] of Object.entries(query)) {
27845
27845
  url.searchParams.set(k, v);
@@ -27854,8 +27854,8 @@ var init_http_client = __esm({
27854
27854
  /**
27855
27855
  * Perform an authenticated POST request with a JSON body.
27856
27856
  */
27857
- async post(path12, body) {
27858
- const url = new URL(path12, this.baseUrl);
27857
+ async post(path16, body) {
27858
+ const url = new URL(path16, this.baseUrl);
27859
27859
  const resp = await this.fetchWithTimeout(url, {
27860
27860
  method: "POST",
27861
27861
  headers: this.headers(),
@@ -27873,8 +27873,8 @@ var init_http_client = __esm({
27873
27873
  * Perform an authenticated PATCH request with a JSON body.
27874
27874
  * Used to update existing resources (e.g., message response fields).
27875
27875
  */
27876
- async patch(path12, body) {
27877
- const url = new URL(path12, this.baseUrl);
27876
+ async patch(path16, body) {
27877
+ const url = new URL(path16, this.baseUrl);
27878
27878
  const resp = await this.fetchWithTimeout(url, {
27879
27879
  method: "PATCH",
27880
27880
  headers: this.headers(),
@@ -27897,8 +27897,8 @@ var init_http_client = __esm({
27897
27897
  * "Malformed JSON in request body" 400 from cogent-server <=3.1.2 when
27898
27898
  * any client (including third-party tools) DELETEd a peer without a body.
27899
27899
  */
27900
- async delete(path12, body) {
27901
- const url = new URL(path12, this.baseUrl);
27900
+ async delete(path16, body) {
27901
+ const url = new URL(path16, this.baseUrl);
27902
27902
  const headers = {
27903
27903
  "Authorization": `Bearer ${this.token}`
27904
27904
  };
@@ -32077,14 +32077,14 @@ var init_ws_client = __esm({
32077
32077
  if (this.pollTimer) return;
32078
32078
  const poll = async () => {
32079
32079
  try {
32080
- const path12 = `/api/sessions/${this.opts.sessionId}/poll`;
32080
+ const path16 = `/api/sessions/${this.opts.sessionId}/poll`;
32081
32081
  const query = {
32082
32082
  peerId: this.opts.peerId
32083
32083
  };
32084
32084
  if (this.lastMessageId) {
32085
32085
  query.lastMessageId = this.lastMessageId;
32086
32086
  }
32087
- const result = await this.opts.http.get(path12, query);
32087
+ const result = await this.opts.http.get(path16, query);
32088
32088
  if (result.messages && result.messages.length > 0) {
32089
32089
  this.opts.onMessages(result.messages);
32090
32090
  this.lastMessageId = result.messages[result.messages.length - 1].id;
@@ -33882,8 +33882,61 @@ var init_startup = __esm({
33882
33882
  }
33883
33883
  });
33884
33884
 
33885
- // src/tools/register-peer.ts
33885
+ // src/cloud/mail-credential-store.ts
33886
33886
  import crypto3 from "node:crypto";
33887
+ import fs12 from "node:fs/promises";
33888
+ import path12 from "node:path";
33889
+ import os7 from "node:os";
33890
+ function defaultMailCredentialPath(cwd = process.cwd()) {
33891
+ const hash = crypto3.createHash("sha256").update(path12.resolve(cwd)).digest("hex").slice(0, 16);
33892
+ return path12.join(os7.homedir(), ".cogent", "mail-credentials", `${hash}.json`);
33893
+ }
33894
+ function resolveMailCredentialPath(credentialPath) {
33895
+ if (credentialPath) return credentialPath;
33896
+ const envOverride = process.env.COGENT_MAIL_CREDENTIALS_FILE;
33897
+ if (envOverride) return envOverride;
33898
+ return defaultMailCredentialPath();
33899
+ }
33900
+ async function loadMailCredentials(credentialPath) {
33901
+ const filePath = resolveMailCredentialPath(credentialPath);
33902
+ try {
33903
+ return JSON.parse(await fs12.readFile(filePath, "utf-8"));
33904
+ } catch {
33905
+ return null;
33906
+ }
33907
+ }
33908
+ async function saveMailCredentials(creds, credentialPath) {
33909
+ const filePath = resolveMailCredentialPath(credentialPath);
33910
+ await fs12.mkdir(path12.dirname(filePath), { recursive: true, mode: 448 });
33911
+ await fs12.writeFile(filePath, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
33912
+ await fs12.chmod(filePath, 384);
33913
+ }
33914
+ async function persistProvisionedMailbox(mailbox, credentialPath) {
33915
+ if (!mailbox?.address || !mailbox.password) return false;
33916
+ await saveMailCredentials(
33917
+ {
33918
+ address: mailbox.address,
33919
+ password: mailbox.password,
33920
+ imapHost: MAIL_DEFAULT_HOST,
33921
+ imapPort: 993,
33922
+ smtpHost: MAIL_DEFAULT_HOST,
33923
+ smtpPort: 465,
33924
+ savedAt: (/* @__PURE__ */ new Date()).toISOString()
33925
+ },
33926
+ credentialPath
33927
+ );
33928
+ return true;
33929
+ }
33930
+ var MAIL_DEFAULT_HOST;
33931
+ var init_mail_credential_store = __esm({
33932
+ "src/cloud/mail-credential-store.ts"() {
33933
+ "use strict";
33934
+ MAIL_DEFAULT_HOST = "mail.cogent.tools";
33935
+ }
33936
+ });
33937
+
33938
+ // src/tools/register-peer.ts
33939
+ import crypto4 from "node:crypto";
33887
33940
  function registerRegisterPeerTool(server) {
33888
33941
  server.registerTool(
33889
33942
  "cogent_register_peer",
@@ -33942,7 +33995,7 @@ function registerRegisterPeerTool(server) {
33942
33995
  }
33943
33996
  }
33944
33997
  if (!cloudSessionId) {
33945
- autoSecret = crypto3.randomBytes(16).toString("hex");
33998
+ autoSecret = crypto4.randomBytes(16).toString("hex");
33946
33999
  const resp = await fetch(
33947
34000
  `${endpoint}/api/sessions`,
33948
34001
  {
@@ -34021,6 +34074,15 @@ function registerRegisterPeerTool(server) {
34021
34074
  );
34022
34075
  }
34023
34076
  }
34077
+ try {
34078
+ if (await persistProvisionedMailbox(peer?.mailbox)) {
34079
+ logger.info(`Cogent Mail: saved auto-provisioned mailbox ${peer.mailbox?.address}`);
34080
+ }
34081
+ } catch (err) {
34082
+ logger.warn(
34083
+ `Cogent Mail: could not save auto-provisioned mailbox creds: ${err.message}`
34084
+ );
34085
+ }
34024
34086
  const resolution = await resolveSession(
34025
34087
  getConfig().COGENT_PLATFORM,
34026
34088
  cwd,
@@ -34103,6 +34165,7 @@ var init_register_peer = __esm({
34103
34165
  init_backend();
34104
34166
  init_errors4();
34105
34167
  init_credential_store();
34168
+ init_mail_credential_store();
34106
34169
  init_logger();
34107
34170
  init_heartbeat();
34108
34171
  init_auto_relay();
@@ -34412,7 +34475,8 @@ var init_peer = __esm({
34412
34475
  workspaceId: import_zod5.z.string().optional().describe("Durable workspace identifier (Slice 5) \u2014 stable id not tied to the mutable cwd hint"),
34413
34476
  threadId: import_zod5.z.string().optional().describe("Durable thread/conversation identifier (Slice 5)"),
34414
34477
  role: import_zod5.z.string().max(64).optional().describe("A2A role advertised by this peer (Agent Card)"),
34415
- capabilities: import_zod5.z.array(import_zod5.z.string().max(64)).max(16).optional().describe("Capability strings \u2192 A2A Agent Card skills[]")
34478
+ capabilities: import_zod5.z.array(import_zod5.z.string().max(64)).max(16).optional().describe("Capability strings \u2192 A2A Agent Card skills[]"),
34479
+ mailboxAddress: import_zod5.z.string().max(320).optional().describe("Auto-provisioned Cogent Mail address (non-secret); NEVER the password")
34416
34480
  });
34417
34481
  }
34418
34482
  });
@@ -34760,6 +34824,19 @@ function toSkills(peer) {
34760
34824
  };
34761
34825
  });
34762
34826
  }
34827
+ function toMailboxExtensions(peer) {
34828
+ const address = peer.mailboxAddress;
34829
+ if (!address)
34830
+ return [];
34831
+ return [
34832
+ {
34833
+ uri: COGENT_MAILBOX_EXTENSION_URI,
34834
+ description: "Cogent Mail mailbox address (non-secret) for this peer.",
34835
+ required: false,
34836
+ params: { address }
34837
+ }
34838
+ ];
34839
+ }
34763
34840
  function synthDescription(peer) {
34764
34841
  const parts = [`Cogent fabric peer on ${peer.platform ?? "cc"}`];
34765
34842
  if (peer.role)
@@ -34775,6 +34852,7 @@ function peerInfoToAgentCard(peer, ctx) {
34775
34852
  const version2 = ctx.clientVersion ?? peer.clientVersion ?? "0.0.0";
34776
34853
  const provider = { organization: `Cogent \xB7 ${peer.platform ?? "cc"}`, url: "https://cogent.tools" };
34777
34854
  const skills = toSkills(peer);
34855
+ const extensions = toMailboxExtensions(peer);
34778
34856
  if (ctx.cardVersion === "0.3.0") {
34779
34857
  return {
34780
34858
  protocolVersion: "0.3.0",
@@ -34783,7 +34861,7 @@ function peerInfoToAgentCard(peer, ctx) {
34783
34861
  url,
34784
34862
  version: version2,
34785
34863
  provider,
34786
- capabilities: {},
34864
+ capabilities: extensions.length ? { extensions } : {},
34787
34865
  defaultInputModes: ["text/plain"],
34788
34866
  defaultOutputModes: ["text/plain"],
34789
34867
  skills,
@@ -34804,7 +34882,7 @@ function peerInfoToAgentCard(peer, ctx) {
34804
34882
  tenant: ctx.sessionId
34805
34883
  }
34806
34884
  ],
34807
- capabilities: { extensions: [] },
34885
+ capabilities: { extensions },
34808
34886
  defaultInputModes: ["text/plain"],
34809
34887
  defaultOutputModes: ["text/plain"],
34810
34888
  skills,
@@ -34813,12 +34891,13 @@ function peerInfoToAgentCard(peer, ctx) {
34813
34891
  signatures: []
34814
34892
  };
34815
34893
  }
34816
- var COGENT_PROTOCOL_BINDING, A2A_PROTOCOL_VERSION;
34894
+ var COGENT_PROTOCOL_BINDING, A2A_PROTOCOL_VERSION, COGENT_MAILBOX_EXTENSION_URI;
34817
34895
  var init_agent_card = __esm({
34818
34896
  "cogent/dist/a2a/agent-card.js"() {
34819
34897
  "use strict";
34820
34898
  COGENT_PROTOCOL_BINDING = "COGENT-RELAY";
34821
34899
  A2A_PROTOCOL_VERSION = "1.0";
34900
+ COGENT_MAILBOX_EXTENSION_URI = "https://cogent.tools/a2a/ext/mailbox";
34822
34901
  }
34823
34902
  });
34824
34903
 
@@ -35113,7 +35192,7 @@ var init_health_check2 = __esm({
35113
35192
  });
35114
35193
 
35115
35194
  // src/tools/create-session.ts
35116
- import crypto4 from "node:crypto";
35195
+ import crypto5 from "node:crypto";
35117
35196
  function registerCreateSessionTool(server) {
35118
35197
  server.registerTool(
35119
35198
  "cogent_create_session",
@@ -35144,7 +35223,7 @@ function registerCreateSessionTool(server) {
35144
35223
  )
35145
35224
  );
35146
35225
  }
35147
- const sessionSecret = secret ?? crypto4.randomBytes(16).toString("hex");
35226
+ const sessionSecret = secret ?? crypto5.randomBytes(16).toString("hex");
35148
35227
  const resp = await fetch(
35149
35228
  `${config2.COGENT_ENDPOINT}/api/sessions`,
35150
35229
  {
@@ -35372,6 +35451,386 @@ var init_join_session = __esm({
35372
35451
  }
35373
35452
  });
35374
35453
 
35454
+ // src/mail/validate.ts
35455
+ function isValidEmail(value) {
35456
+ return value.length <= 254 && EMAIL_RE.test(value);
35457
+ }
35458
+ function assertValidEmail(value, field) {
35459
+ if (!isValidEmail(value)) {
35460
+ throw new BridgeError(
35461
+ "INVALID_INPUT" /* INVALID_INPUT */,
35462
+ `${field} is not a valid email address: ${JSON.stringify(value)}`,
35463
+ `Provide a full address like agent-channel@mail.cogent.tools`
35464
+ );
35465
+ }
35466
+ }
35467
+ var EMAIL_RE;
35468
+ var init_validate = __esm({
35469
+ "src/mail/validate.ts"() {
35470
+ "use strict";
35471
+ init_errors4();
35472
+ EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
35473
+ }
35474
+ });
35475
+
35476
+ // src/tools/setup-mail.ts
35477
+ function registerSetupMailTool(server) {
35478
+ server.registerTool(
35479
+ "cogent_setup_mail",
35480
+ {
35481
+ title: "Configure this agent's mailbox",
35482
+ description: "Store the agent's mailbox address + password (from the admin Mail panel) locally so cogent_send_mail and cogent_fetch_mail can use them. Hosts default to mail.cogent.tools (IMAP 993 / SMTP 465).",
35483
+ inputSchema: {
35484
+ address: import_zod16.z.string().describe("Full mailbox address, e.g. claude-agent-backend@mail.cogent.tools"),
35485
+ password: import_zod16.z.string().describe("Mailbox password (shown once in the admin Mail panel on create/rotate)"),
35486
+ imapHost: import_zod16.z.string().optional().describe(`IMAP host (default ${DEFAULT_MAIL_HOST})`),
35487
+ imapPort: import_zod16.z.number().int().positive().optional().describe("IMAP port (default 993)"),
35488
+ smtpHost: import_zod16.z.string().optional().describe(`SMTP host (default ${DEFAULT_MAIL_HOST})`),
35489
+ smtpPort: import_zod16.z.number().int().positive().optional().describe("SMTP submission port (default 465)")
35490
+ },
35491
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
35492
+ },
35493
+ async ({ address, password, imapHost, imapPort, smtpHost, smtpPort }) => {
35494
+ try {
35495
+ assertValidEmail(address, "address");
35496
+ await saveMailCredentials({
35497
+ address,
35498
+ password,
35499
+ imapHost: imapHost ?? DEFAULT_MAIL_HOST,
35500
+ imapPort: imapPort ?? 993,
35501
+ smtpHost: smtpHost ?? DEFAULT_MAIL_HOST,
35502
+ smtpPort: smtpPort ?? 465,
35503
+ savedAt: (/* @__PURE__ */ new Date()).toISOString()
35504
+ });
35505
+ return successResult({ configured: true, address });
35506
+ } catch (err) {
35507
+ return errorResult(err);
35508
+ }
35509
+ }
35510
+ );
35511
+ }
35512
+ var import_zod16, DEFAULT_MAIL_HOST;
35513
+ var init_setup_mail = __esm({
35514
+ "src/tools/setup-mail.ts"() {
35515
+ "use strict";
35516
+ import_zod16 = __toESM(require_zod(), 1);
35517
+ init_errors4();
35518
+ init_mail_credential_store();
35519
+ init_validate();
35520
+ DEFAULT_MAIL_HOST = MAIL_DEFAULT_HOST;
35521
+ }
35522
+ });
35523
+
35524
+ // src/mail/mail-sender.ts
35525
+ import fs13 from "node:fs/promises";
35526
+ import path13 from "node:path";
35527
+ async function buildAndSendMail(sender, creds, params) {
35528
+ const attachments = [];
35529
+ for (const p of params.attachmentPaths ?? []) {
35530
+ const content = await fs13.readFile(p);
35531
+ attachments.push({ filename: path13.basename(p), content });
35532
+ }
35533
+ const { messageId } = await sender.send({
35534
+ from: creds.address,
35535
+ to: params.to,
35536
+ subject: params.subject,
35537
+ text: params.text,
35538
+ attachments
35539
+ });
35540
+ return { messageId, from: creds.address, to: params.to };
35541
+ }
35542
+ var NodemailerMailSender;
35543
+ var init_mail_sender = __esm({
35544
+ "src/mail/mail-sender.ts"() {
35545
+ "use strict";
35546
+ NodemailerMailSender = class {
35547
+ constructor(creds, transportFactory) {
35548
+ this.creds = creds;
35549
+ this.transportFactory = transportFactory;
35550
+ }
35551
+ creds;
35552
+ transportFactory;
35553
+ async send(mail) {
35554
+ const transport = this.transportFactory ? await this.transportFactory() : await this.defaultTransport();
35555
+ const info = await transport.sendMail({
35556
+ from: mail.from,
35557
+ to: mail.to,
35558
+ subject: mail.subject,
35559
+ text: mail.text,
35560
+ attachments: mail.attachments.map((a) => ({ filename: a.filename, content: a.content }))
35561
+ });
35562
+ return { messageId: info.messageId ?? "" };
35563
+ }
35564
+ async defaultTransport() {
35565
+ const specifier = "nodemailer";
35566
+ const nodemailer = await import(specifier);
35567
+ return nodemailer.createTransport({
35568
+ host: this.creds.smtpHost,
35569
+ port: this.creds.smtpPort,
35570
+ secure: this.creds.smtpPort === 465,
35571
+ // implicit TLS on 465, STARTTLS on 587
35572
+ auth: { user: this.creds.address, pass: this.creds.password }
35573
+ });
35574
+ }
35575
+ };
35576
+ }
35577
+ });
35578
+
35579
+ // src/tools/send-mail.ts
35580
+ function registerSendMailTool(server, deps = {}) {
35581
+ const makeSender = deps.senderFactory ?? ((creds) => new NodemailerMailSender(creds));
35582
+ server.registerTool(
35583
+ "cogent_send_mail",
35584
+ {
35585
+ title: "Send an email from this agent's mailbox",
35586
+ description: "Send an email (with optional file attachments) from this agent's Cogent mailbox to another address. Requires cogent_setup_mail to have stored the mailbox credentials first.",
35587
+ inputSchema: {
35588
+ to: import_zod17.z.string().describe("Recipient email address, e.g. other-agent-backend@mail.cogent.tools"),
35589
+ subject: import_zod17.z.string().describe("Email subject"),
35590
+ body: import_zod17.z.string().describe("Plain-text email body"),
35591
+ attachments: import_zod17.z.array(import_zod17.z.string()).optional().describe("Optional local file paths to attach (each is read and attached by basename)")
35592
+ },
35593
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
35594
+ },
35595
+ async ({ to, subject, body, attachments }) => {
35596
+ try {
35597
+ assertValidEmail(to, "to");
35598
+ const creds = await loadMailCredentials();
35599
+ if (!creds) {
35600
+ return errorResult(
35601
+ new BridgeError(
35602
+ "INVALID_INPUT" /* INVALID_INPUT */,
35603
+ "No mailbox is configured for this agent",
35604
+ "Run cogent_setup_mail with the address + password from the admin Mail panel first"
35605
+ )
35606
+ );
35607
+ }
35608
+ const result = await buildAndSendMail(makeSender(creds), creds, {
35609
+ to: [to],
35610
+ subject,
35611
+ text: body,
35612
+ attachmentPaths: attachments
35613
+ });
35614
+ return successResult({ ...result, attachmentCount: attachments?.length ?? 0 });
35615
+ } catch (err) {
35616
+ return errorResult(err);
35617
+ }
35618
+ }
35619
+ );
35620
+ }
35621
+ var import_zod17;
35622
+ var init_send_mail = __esm({
35623
+ "src/tools/send-mail.ts"() {
35624
+ "use strict";
35625
+ import_zod17 = __toESM(require_zod(), 1);
35626
+ init_errors4();
35627
+ init_mail_credential_store();
35628
+ init_mail_sender();
35629
+ init_validate();
35630
+ }
35631
+ });
35632
+
35633
+ // src/mail/mail-fetcher.ts
35634
+ import fs14 from "node:fs/promises";
35635
+ import path14 from "node:path";
35636
+ async function saveAttachments(attachments, downloadDir) {
35637
+ if (attachments.length === 0) return [];
35638
+ await fs14.mkdir(downloadDir, { recursive: true });
35639
+ const saved = [];
35640
+ for (const a of attachments) {
35641
+ const name = path14.basename(a.filename) || "attachment";
35642
+ const savedPath = path14.join(downloadDir, name);
35643
+ await fs14.writeFile(savedPath, a.content);
35644
+ saved.push({ filename: name, savedPath, size: a.content.length });
35645
+ }
35646
+ return saved;
35647
+ }
35648
+ async function defaultImapSessionFactory(creds) {
35649
+ const specifier = "imapflow";
35650
+ const { ImapFlow } = await import(specifier);
35651
+ const client = new ImapFlow({
35652
+ host: creds.imapHost,
35653
+ port: creds.imapPort,
35654
+ secure: creds.imapPort === 993,
35655
+ auth: { user: creds.address, pass: creds.password },
35656
+ logger: false
35657
+ });
35658
+ await client.connect();
35659
+ await client.mailboxOpen("INBOX");
35660
+ return {
35661
+ async list(unreadOnly) {
35662
+ const criteria = unreadOnly ? { seen: false } : { all: true };
35663
+ const rows = [];
35664
+ for await (const msg of client.fetch(criteria, { uid: true, envelope: true, flags: true })) {
35665
+ const env = msg.envelope ?? {};
35666
+ rows.push({
35667
+ uid: msg.uid,
35668
+ from: env.from?.[0]?.address ?? "",
35669
+ subject: env.subject ?? "",
35670
+ date: env.date instanceof Date ? env.date.toISOString() : String(env.date ?? ""),
35671
+ unread: !hasFlag(msg.flags, "\\Seen")
35672
+ });
35673
+ }
35674
+ return rows.reverse();
35675
+ },
35676
+ async fetch(uid) {
35677
+ const msg = await client.fetchOne(uid, { uid: true, envelope: true, bodyStructure: true }, { uid: true });
35678
+ if (!msg) throw new Error(`no message with uid ${uid}`);
35679
+ const parts = flattenBodyStructure(msg.bodyStructure);
35680
+ let text = "";
35681
+ const attachments = [];
35682
+ for (const p of parts) {
35683
+ if (p.filename) {
35684
+ const dl = await client.download(uid, p.part, { uid: true });
35685
+ attachments.push({ filename: p.filename, content: await streamToBuffer(dl.content) });
35686
+ } else if (p.isText && !text) {
35687
+ const dl = await client.download(uid, p.part, { uid: true });
35688
+ text = (await streamToBuffer(dl.content)).toString("utf-8");
35689
+ }
35690
+ }
35691
+ const env = msg.envelope ?? {};
35692
+ return {
35693
+ from: env.from?.[0]?.address ?? "",
35694
+ subject: env.subject ?? "",
35695
+ date: env.date instanceof Date ? env.date.toISOString() : String(env.date ?? ""),
35696
+ text,
35697
+ attachments
35698
+ };
35699
+ },
35700
+ async close() {
35701
+ try {
35702
+ await client.logout();
35703
+ } catch {
35704
+ }
35705
+ }
35706
+ };
35707
+ }
35708
+ function flattenBodyStructure(node, acc = []) {
35709
+ if (!node || typeof node !== "object") return acc;
35710
+ const n = node;
35711
+ if (Array.isArray(n.childNodes) && n.childNodes.length > 0) {
35712
+ for (const child of n.childNodes) flattenBodyStructure(child, acc);
35713
+ return acc;
35714
+ }
35715
+ const type = (n.type ?? "").toLowerCase();
35716
+ const filename = n.dispositionParameters?.filename ?? n.parameters?.name;
35717
+ const isAttachment = (n.disposition ?? "").toLowerCase() === "attachment" || Boolean(filename);
35718
+ acc.push({
35719
+ part: n.part ?? "1",
35720
+ // single-part messages have no `part`; imapflow addresses them as "1"
35721
+ type,
35722
+ isText: type === "text/plain" && !isAttachment,
35723
+ filename: isAttachment ? filename : void 0
35724
+ });
35725
+ return acc;
35726
+ }
35727
+ function hasFlag(flags, flag) {
35728
+ if (flags instanceof Set) return flags.has(flag);
35729
+ if (Array.isArray(flags)) return flags.includes(flag);
35730
+ return false;
35731
+ }
35732
+ async function streamToBuffer(stream) {
35733
+ const chunks = [];
35734
+ if (!stream) return Buffer.alloc(0);
35735
+ for await (const chunk of stream) chunks.push(Buffer.from(chunk));
35736
+ return Buffer.concat(chunks);
35737
+ }
35738
+ var ImapflowMailFetcher;
35739
+ var init_mail_fetcher = __esm({
35740
+ "src/mail/mail-fetcher.ts"() {
35741
+ "use strict";
35742
+ ImapflowMailFetcher = class {
35743
+ constructor(creds, sessionFactory = defaultImapSessionFactory) {
35744
+ this.creds = creds;
35745
+ this.sessionFactory = sessionFactory;
35746
+ }
35747
+ creds;
35748
+ sessionFactory;
35749
+ async list(opts = {}) {
35750
+ const session = await this.sessionFactory(this.creds);
35751
+ try {
35752
+ let rows = await session.list(opts.unreadOnly ?? false);
35753
+ if (opts.limit && opts.limit > 0) rows = rows.slice(0, opts.limit);
35754
+ return rows;
35755
+ } finally {
35756
+ await session.close();
35757
+ }
35758
+ }
35759
+ async fetchOne(uid, downloadDir) {
35760
+ const session = await this.sessionFactory(this.creds);
35761
+ try {
35762
+ const c2 = await session.fetch(uid);
35763
+ const saved = await saveAttachments(c2.attachments, downloadDir);
35764
+ return { uid, from: c2.from, subject: c2.subject, date: c2.date, text: c2.text, attachments: saved };
35765
+ } finally {
35766
+ await session.close();
35767
+ }
35768
+ }
35769
+ };
35770
+ }
35771
+ });
35772
+
35773
+ // src/tools/fetch-mail.ts
35774
+ import os8 from "node:os";
35775
+ import path15 from "node:path";
35776
+ function registerFetchMailTool(server, deps = {}) {
35777
+ const makeFetcher = deps.fetcherFactory ?? ((creds) => new ImapflowMailFetcher(creds));
35778
+ server.registerTool(
35779
+ "cogent_fetch_mail",
35780
+ {
35781
+ title: "Read this agent's mailbox",
35782
+ description: "List messages in this agent's Cogent mailbox, or fetch one message (text + attachments saved locally) by uid. Requires cogent_setup_mail to have stored the mailbox credentials.",
35783
+ inputSchema: {
35784
+ action: import_zod18.z.enum(["list", "fetch"]).optional().describe("'list' (default) or 'fetch' one message"),
35785
+ uid: import_zod18.z.number().int().positive().optional().describe("Message uid to fetch (required when action='fetch')"),
35786
+ unreadOnly: import_zod18.z.boolean().optional().describe("List only unread messages (default false)"),
35787
+ limit: import_zod18.z.number().int().positive().optional().describe("Max messages to list (default 25)"),
35788
+ downloadDir: import_zod18.z.string().optional().describe(`Directory to save fetched attachments (default ${DEFAULT_DOWNLOAD_DIR})`)
35789
+ },
35790
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
35791
+ },
35792
+ async ({ action, uid, unreadOnly, limit, downloadDir }) => {
35793
+ try {
35794
+ const creds = await loadMailCredentials();
35795
+ if (!creds) {
35796
+ return errorResult(
35797
+ new BridgeError(
35798
+ "INVALID_INPUT" /* INVALID_INPUT */,
35799
+ "No mailbox is configured for this agent",
35800
+ "Run cogent_setup_mail with the address + password from the admin Mail panel first"
35801
+ )
35802
+ );
35803
+ }
35804
+ const fetcher = makeFetcher(creds);
35805
+ if (action === "fetch") {
35806
+ if (uid === void 0) {
35807
+ return errorResult(
35808
+ new BridgeError("INVALID_INPUT" /* INVALID_INPUT */, "uid is required when action='fetch'", "Pass the uid from a cogent_fetch_mail list")
35809
+ );
35810
+ }
35811
+ const message = await fetcher.fetchOne(uid, downloadDir ?? DEFAULT_DOWNLOAD_DIR);
35812
+ return successResult({ mailbox: creds.address, message });
35813
+ }
35814
+ const messages = await fetcher.list({ unreadOnly, limit: limit ?? 25 });
35815
+ return successResult({ mailbox: creds.address, count: messages.length, messages });
35816
+ } catch (err) {
35817
+ return errorResult(err);
35818
+ }
35819
+ }
35820
+ );
35821
+ }
35822
+ var import_zod18, DEFAULT_DOWNLOAD_DIR;
35823
+ var init_fetch_mail = __esm({
35824
+ "src/tools/fetch-mail.ts"() {
35825
+ "use strict";
35826
+ import_zod18 = __toESM(require_zod(), 1);
35827
+ init_errors4();
35828
+ init_mail_credential_store();
35829
+ init_mail_fetcher();
35830
+ DEFAULT_DOWNLOAD_DIR = path15.join(os8.homedir(), ".cogent", "mail-downloads");
35831
+ }
35832
+ });
35833
+
35375
35834
  // src/index.ts
35376
35835
  var index_exports = {};
35377
35836
  async function main() {
@@ -35385,6 +35844,9 @@ async function main() {
35385
35844
  registerHealthCheckTool(server);
35386
35845
  registerCreateSessionTool(server);
35387
35846
  registerJoinSessionTool(server);
35847
+ registerSetupMailTool(server);
35848
+ registerSendMailTool(server);
35849
+ registerFetchMailTool(server);
35388
35850
  if (cloudInbox) {
35389
35851
  server.registerResource(
35390
35852
  "cogent_inbox",
@@ -35444,6 +35906,9 @@ var init_index = __esm({
35444
35906
  init_health_check2();
35445
35907
  init_create_session();
35446
35908
  init_join_session();
35909
+ init_setup_mail();
35910
+ init_send_mail();
35911
+ init_fetch_mail();
35447
35912
  init_heartbeat();
35448
35913
  process.on("uncaughtException", (err) => {
35449
35914
  if (err.code === "EPIPE") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@essentialai/cogent-plugin",
3
- "version": "3.14.1",
3
+ "version": "3.15.0",
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",