@essentialai/cogent-plugin 3.14.1 → 3.16.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.
- package/.claude-plugin/plugin.json +1 -1
- package/bridge/cogent-bridge.mjs +558 -56
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogent",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.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",
|
package/bridge/cogent-bridge.mjs
CHANGED
|
@@ -1259,8 +1259,8 @@ var init_parseUtil = __esm({
|
|
|
1259
1259
|
init_errors();
|
|
1260
1260
|
init_en();
|
|
1261
1261
|
makeIssue = (params) => {
|
|
1262
|
-
const { data, path:
|
|
1263
|
-
const fullPath = [...
|
|
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,
|
|
1543
|
+
constructor(parent, value, path16, key) {
|
|
1544
1544
|
this._cachedPath = [];
|
|
1545
1545
|
this.parent = parent;
|
|
1546
1546
|
this.data = value;
|
|
1547
|
-
this._path =
|
|
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,
|
|
5000
|
-
if (!
|
|
4999
|
+
function getElementAtPath(obj, path16) {
|
|
5000
|
+
if (!path16)
|
|
5001
5001
|
return obj;
|
|
5002
|
-
return
|
|
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(
|
|
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(
|
|
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(
|
|
17062
|
-
let input =
|
|
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 [
|
|
17262
|
-
wsComponent.path =
|
|
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,
|
|
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,
|
|
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:
|
|
21733
|
-
const fullPath = [...
|
|
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,
|
|
21887
|
+
constructor(parent, value, path16, key) {
|
|
21888
21888
|
this._cachedPath = [];
|
|
21889
21889
|
this.parent = parent;
|
|
21890
21890
|
this.data = value;
|
|
21891
|
-
this._path =
|
|
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
|
|
25481
|
-
exports.z =
|
|
25480
|
+
var z18 = __importStar(require_external());
|
|
25481
|
+
exports.z = z18;
|
|
25482
25482
|
__exportStar(require_external(), exports);
|
|
25483
|
-
exports.default =
|
|
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.
|
|
26393
|
-
return "3.
|
|
26392
|
+
if ("3.16.0") {
|
|
26393
|
+
return "3.16.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
|
|
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(
|
|
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
|
|
27602
|
+
const path16 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
|
|
27603
27603
|
try {
|
|
27604
|
-
await this.http.delete(
|
|
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
|
|
27625
|
-
const result = await this.http.get(
|
|
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
|
|
27634
|
-
await this.http.post(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
27842
|
-
const url = new URL(
|
|
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(
|
|
27858
|
-
const url = new URL(
|
|
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(
|
|
27877
|
-
const url = new URL(
|
|
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(
|
|
27901
|
-
const url = new URL(
|
|
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
|
|
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(
|
|
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;
|
|
@@ -33080,6 +33080,7 @@ var init_auto_relay = __esm({
|
|
|
33080
33080
|
logger.info(`Auto-relay: processing message from ${fromLabel} (${msg.fromPeerId})`);
|
|
33081
33081
|
const traceId = msg.traceId ?? randomUUID2();
|
|
33082
33082
|
const startMs = Date.now();
|
|
33083
|
+
let replied = false;
|
|
33083
33084
|
try {
|
|
33084
33085
|
const res = await this.refreshSessionId();
|
|
33085
33086
|
this._trace(traceId, "resolved", {
|
|
@@ -33126,6 +33127,7 @@ var init_auto_relay = __esm({
|
|
|
33126
33127
|
const durationMs = Date.now() - startMs;
|
|
33127
33128
|
if (result.exitCode === 0 && result.stdout) {
|
|
33128
33129
|
await this._relayCapturedReply(msg, result, traceId, durationMs, false);
|
|
33130
|
+
replied = true;
|
|
33129
33131
|
} else {
|
|
33130
33132
|
this._trace(traceId, "failed", { exitCode: result.exitCode, sandboxBlocked: result.sandboxBlocked, durationMs });
|
|
33131
33133
|
logger.warn(
|
|
@@ -33142,6 +33144,7 @@ var init_auto_relay = __esm({
|
|
|
33142
33144
|
const bypassRetry = await execRemote(cfg.COGENT_PLATFORM, this.localSessionId, formatted, this.localCwd);
|
|
33143
33145
|
if (bypassRetry.exitCode === 0 && bypassRetry.stdout) {
|
|
33144
33146
|
await this._relayCapturedReply(msg, bypassRetry, traceId, Date.now() - startMs, true);
|
|
33147
|
+
replied = true;
|
|
33145
33148
|
return;
|
|
33146
33149
|
}
|
|
33147
33150
|
logger.warn(`Auto-relay: sandbox-bypass retry also failed (exit=${bypassRetry.exitCode})`);
|
|
@@ -33171,15 +33174,49 @@ var init_auto_relay = __esm({
|
|
|
33171
33174
|
const retry = await execRemote(getConfig().COGENT_PLATFORM, this.localSessionId, formatted, this.localCwd);
|
|
33172
33175
|
if (retry.exitCode === 0 && retry.stdout) {
|
|
33173
33176
|
await this._relayCapturedReply(msg, retry, traceId, Date.now() - startMs, true);
|
|
33177
|
+
replied = true;
|
|
33174
33178
|
} else {
|
|
33175
33179
|
logger.warn(`Auto-relay: retry also failed (exit=${retry.exitCode})`);
|
|
33176
33180
|
}
|
|
33177
33181
|
}
|
|
33178
33182
|
}
|
|
33183
|
+
if (!replied) {
|
|
33184
|
+
await this._recordNoReplyFailure(msg, traceId, Date.now() - startMs);
|
|
33185
|
+
}
|
|
33179
33186
|
} catch (err) {
|
|
33180
33187
|
const durationMs = Date.now() - startMs;
|
|
33181
33188
|
this._trace(traceId, "failed", { threw: true, durationMs });
|
|
33182
33189
|
logger.error(`Auto-relay: execRemote threw after ${durationMs}ms: ${err}`);
|
|
33190
|
+
if (!replied) {
|
|
33191
|
+
await this._recordNoReplyFailure(msg, traceId, durationMs);
|
|
33192
|
+
}
|
|
33193
|
+
}
|
|
33194
|
+
}
|
|
33195
|
+
/**
|
|
33196
|
+
* Fail-loud (2026-08-11): a wake that captured NO reply — empty stdout, a
|
|
33197
|
+
* non-zero exit with no rotated-session retry, a failed retry, or a thrown
|
|
33198
|
+
* execRemote — used to record NOTHING, so the sender saw response:null with no
|
|
33199
|
+
* error (the invisible failure that hid the demo busy-miss). Record ONE visible
|
|
33200
|
+
* success:false notice to the sender so a human/agent knows to reply manually.
|
|
33201
|
+
* On Codex this pairs with Wake-C (check-on-stop), which auto-recovers the reply
|
|
33202
|
+
* at the next turn boundary. Refusal (AMBIGUOUS_SESSION) and sandbox-blocked
|
|
33203
|
+
* paths record their own failure and return before this, so no double-record.
|
|
33204
|
+
*/
|
|
33205
|
+
async _recordNoReplyFailure(msg, traceId, durationMs) {
|
|
33206
|
+
try {
|
|
33207
|
+
await getBackend().recordMessage({
|
|
33208
|
+
fromPeerId: this.localPeerId,
|
|
33209
|
+
toPeerId: msg.fromPeerId,
|
|
33210
|
+
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})`,
|
|
33211
|
+
response: null,
|
|
33212
|
+
durationMs,
|
|
33213
|
+
success: false,
|
|
33214
|
+
error: "NO_REPLY_CAPTURED",
|
|
33215
|
+
isRelayEcho: true,
|
|
33216
|
+
traceId
|
|
33217
|
+
});
|
|
33218
|
+
} catch (recordErr) {
|
|
33219
|
+
logger.error(`Auto-relay: failed to record no-reply failure: ${recordErr}`);
|
|
33183
33220
|
}
|
|
33184
33221
|
}
|
|
33185
33222
|
/**
|
|
@@ -33882,8 +33919,61 @@ var init_startup = __esm({
|
|
|
33882
33919
|
}
|
|
33883
33920
|
});
|
|
33884
33921
|
|
|
33885
|
-
// src/
|
|
33922
|
+
// src/cloud/mail-credential-store.ts
|
|
33886
33923
|
import crypto3 from "node:crypto";
|
|
33924
|
+
import fs12 from "node:fs/promises";
|
|
33925
|
+
import path12 from "node:path";
|
|
33926
|
+
import os7 from "node:os";
|
|
33927
|
+
function defaultMailCredentialPath(cwd = process.cwd()) {
|
|
33928
|
+
const hash = crypto3.createHash("sha256").update(path12.resolve(cwd)).digest("hex").slice(0, 16);
|
|
33929
|
+
return path12.join(os7.homedir(), ".cogent", "mail-credentials", `${hash}.json`);
|
|
33930
|
+
}
|
|
33931
|
+
function resolveMailCredentialPath(credentialPath) {
|
|
33932
|
+
if (credentialPath) return credentialPath;
|
|
33933
|
+
const envOverride = process.env.COGENT_MAIL_CREDENTIALS_FILE;
|
|
33934
|
+
if (envOverride) return envOverride;
|
|
33935
|
+
return defaultMailCredentialPath();
|
|
33936
|
+
}
|
|
33937
|
+
async function loadMailCredentials(credentialPath) {
|
|
33938
|
+
const filePath = resolveMailCredentialPath(credentialPath);
|
|
33939
|
+
try {
|
|
33940
|
+
return JSON.parse(await fs12.readFile(filePath, "utf-8"));
|
|
33941
|
+
} catch {
|
|
33942
|
+
return null;
|
|
33943
|
+
}
|
|
33944
|
+
}
|
|
33945
|
+
async function saveMailCredentials(creds, credentialPath) {
|
|
33946
|
+
const filePath = resolveMailCredentialPath(credentialPath);
|
|
33947
|
+
await fs12.mkdir(path12.dirname(filePath), { recursive: true, mode: 448 });
|
|
33948
|
+
await fs12.writeFile(filePath, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
33949
|
+
await fs12.chmod(filePath, 384);
|
|
33950
|
+
}
|
|
33951
|
+
async function persistProvisionedMailbox(mailbox, credentialPath) {
|
|
33952
|
+
if (!mailbox?.address || !mailbox.password) return false;
|
|
33953
|
+
await saveMailCredentials(
|
|
33954
|
+
{
|
|
33955
|
+
address: mailbox.address,
|
|
33956
|
+
password: mailbox.password,
|
|
33957
|
+
imapHost: MAIL_DEFAULT_HOST,
|
|
33958
|
+
imapPort: 993,
|
|
33959
|
+
smtpHost: MAIL_DEFAULT_HOST,
|
|
33960
|
+
smtpPort: 465,
|
|
33961
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
33962
|
+
},
|
|
33963
|
+
credentialPath
|
|
33964
|
+
);
|
|
33965
|
+
return true;
|
|
33966
|
+
}
|
|
33967
|
+
var MAIL_DEFAULT_HOST;
|
|
33968
|
+
var init_mail_credential_store = __esm({
|
|
33969
|
+
"src/cloud/mail-credential-store.ts"() {
|
|
33970
|
+
"use strict";
|
|
33971
|
+
MAIL_DEFAULT_HOST = "mail.cogent.tools";
|
|
33972
|
+
}
|
|
33973
|
+
});
|
|
33974
|
+
|
|
33975
|
+
// src/tools/register-peer.ts
|
|
33976
|
+
import crypto4 from "node:crypto";
|
|
33887
33977
|
function registerRegisterPeerTool(server) {
|
|
33888
33978
|
server.registerTool(
|
|
33889
33979
|
"cogent_register_peer",
|
|
@@ -33942,7 +34032,7 @@ function registerRegisterPeerTool(server) {
|
|
|
33942
34032
|
}
|
|
33943
34033
|
}
|
|
33944
34034
|
if (!cloudSessionId) {
|
|
33945
|
-
autoSecret =
|
|
34035
|
+
autoSecret = crypto4.randomBytes(16).toString("hex");
|
|
33946
34036
|
const resp = await fetch(
|
|
33947
34037
|
`${endpoint}/api/sessions`,
|
|
33948
34038
|
{
|
|
@@ -34021,6 +34111,15 @@ function registerRegisterPeerTool(server) {
|
|
|
34021
34111
|
);
|
|
34022
34112
|
}
|
|
34023
34113
|
}
|
|
34114
|
+
try {
|
|
34115
|
+
if (await persistProvisionedMailbox(peer?.mailbox)) {
|
|
34116
|
+
logger.info(`Cogent Mail: saved auto-provisioned mailbox ${peer.mailbox?.address}`);
|
|
34117
|
+
}
|
|
34118
|
+
} catch (err) {
|
|
34119
|
+
logger.warn(
|
|
34120
|
+
`Cogent Mail: could not save auto-provisioned mailbox creds: ${err.message}`
|
|
34121
|
+
);
|
|
34122
|
+
}
|
|
34024
34123
|
const resolution = await resolveSession(
|
|
34025
34124
|
getConfig().COGENT_PLATFORM,
|
|
34026
34125
|
cwd,
|
|
@@ -34103,6 +34202,7 @@ var init_register_peer = __esm({
|
|
|
34103
34202
|
init_backend();
|
|
34104
34203
|
init_errors4();
|
|
34105
34204
|
init_credential_store();
|
|
34205
|
+
init_mail_credential_store();
|
|
34106
34206
|
init_logger();
|
|
34107
34207
|
init_heartbeat();
|
|
34108
34208
|
init_auto_relay();
|
|
@@ -34412,7 +34512,8 @@ var init_peer = __esm({
|
|
|
34412
34512
|
workspaceId: import_zod5.z.string().optional().describe("Durable workspace identifier (Slice 5) \u2014 stable id not tied to the mutable cwd hint"),
|
|
34413
34513
|
threadId: import_zod5.z.string().optional().describe("Durable thread/conversation identifier (Slice 5)"),
|
|
34414
34514
|
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[]")
|
|
34515
|
+
capabilities: import_zod5.z.array(import_zod5.z.string().max(64)).max(16).optional().describe("Capability strings \u2192 A2A Agent Card skills[]"),
|
|
34516
|
+
mailboxAddress: import_zod5.z.string().max(320).optional().describe("Auto-provisioned Cogent Mail address (non-secret); NEVER the password")
|
|
34416
34517
|
});
|
|
34417
34518
|
}
|
|
34418
34519
|
});
|
|
@@ -34760,6 +34861,19 @@ function toSkills(peer) {
|
|
|
34760
34861
|
};
|
|
34761
34862
|
});
|
|
34762
34863
|
}
|
|
34864
|
+
function toMailboxExtensions(peer) {
|
|
34865
|
+
const address = peer.mailboxAddress;
|
|
34866
|
+
if (!address)
|
|
34867
|
+
return [];
|
|
34868
|
+
return [
|
|
34869
|
+
{
|
|
34870
|
+
uri: COGENT_MAILBOX_EXTENSION_URI,
|
|
34871
|
+
description: "Cogent Mail mailbox address (non-secret) for this peer.",
|
|
34872
|
+
required: false,
|
|
34873
|
+
params: { address }
|
|
34874
|
+
}
|
|
34875
|
+
];
|
|
34876
|
+
}
|
|
34763
34877
|
function synthDescription(peer) {
|
|
34764
34878
|
const parts = [`Cogent fabric peer on ${peer.platform ?? "cc"}`];
|
|
34765
34879
|
if (peer.role)
|
|
@@ -34775,6 +34889,7 @@ function peerInfoToAgentCard(peer, ctx) {
|
|
|
34775
34889
|
const version2 = ctx.clientVersion ?? peer.clientVersion ?? "0.0.0";
|
|
34776
34890
|
const provider = { organization: `Cogent \xB7 ${peer.platform ?? "cc"}`, url: "https://cogent.tools" };
|
|
34777
34891
|
const skills = toSkills(peer);
|
|
34892
|
+
const extensions = toMailboxExtensions(peer);
|
|
34778
34893
|
if (ctx.cardVersion === "0.3.0") {
|
|
34779
34894
|
return {
|
|
34780
34895
|
protocolVersion: "0.3.0",
|
|
@@ -34783,7 +34898,7 @@ function peerInfoToAgentCard(peer, ctx) {
|
|
|
34783
34898
|
url,
|
|
34784
34899
|
version: version2,
|
|
34785
34900
|
provider,
|
|
34786
|
-
capabilities: {},
|
|
34901
|
+
capabilities: extensions.length ? { extensions } : {},
|
|
34787
34902
|
defaultInputModes: ["text/plain"],
|
|
34788
34903
|
defaultOutputModes: ["text/plain"],
|
|
34789
34904
|
skills,
|
|
@@ -34804,7 +34919,7 @@ function peerInfoToAgentCard(peer, ctx) {
|
|
|
34804
34919
|
tenant: ctx.sessionId
|
|
34805
34920
|
}
|
|
34806
34921
|
],
|
|
34807
|
-
capabilities: { extensions
|
|
34922
|
+
capabilities: { extensions },
|
|
34808
34923
|
defaultInputModes: ["text/plain"],
|
|
34809
34924
|
defaultOutputModes: ["text/plain"],
|
|
34810
34925
|
skills,
|
|
@@ -34813,12 +34928,13 @@ function peerInfoToAgentCard(peer, ctx) {
|
|
|
34813
34928
|
signatures: []
|
|
34814
34929
|
};
|
|
34815
34930
|
}
|
|
34816
|
-
var COGENT_PROTOCOL_BINDING, A2A_PROTOCOL_VERSION;
|
|
34931
|
+
var COGENT_PROTOCOL_BINDING, A2A_PROTOCOL_VERSION, COGENT_MAILBOX_EXTENSION_URI;
|
|
34817
34932
|
var init_agent_card = __esm({
|
|
34818
34933
|
"cogent/dist/a2a/agent-card.js"() {
|
|
34819
34934
|
"use strict";
|
|
34820
34935
|
COGENT_PROTOCOL_BINDING = "COGENT-RELAY";
|
|
34821
34936
|
A2A_PROTOCOL_VERSION = "1.0";
|
|
34937
|
+
COGENT_MAILBOX_EXTENSION_URI = "https://cogent.tools/a2a/ext/mailbox";
|
|
34822
34938
|
}
|
|
34823
34939
|
});
|
|
34824
34940
|
|
|
@@ -35113,7 +35229,7 @@ var init_health_check2 = __esm({
|
|
|
35113
35229
|
});
|
|
35114
35230
|
|
|
35115
35231
|
// src/tools/create-session.ts
|
|
35116
|
-
import
|
|
35232
|
+
import crypto5 from "node:crypto";
|
|
35117
35233
|
function registerCreateSessionTool(server) {
|
|
35118
35234
|
server.registerTool(
|
|
35119
35235
|
"cogent_create_session",
|
|
@@ -35144,7 +35260,7 @@ function registerCreateSessionTool(server) {
|
|
|
35144
35260
|
)
|
|
35145
35261
|
);
|
|
35146
35262
|
}
|
|
35147
|
-
const sessionSecret = secret ??
|
|
35263
|
+
const sessionSecret = secret ?? crypto5.randomBytes(16).toString("hex");
|
|
35148
35264
|
const resp = await fetch(
|
|
35149
35265
|
`${config2.COGENT_ENDPOINT}/api/sessions`,
|
|
35150
35266
|
{
|
|
@@ -35372,6 +35488,386 @@ var init_join_session = __esm({
|
|
|
35372
35488
|
}
|
|
35373
35489
|
});
|
|
35374
35490
|
|
|
35491
|
+
// src/mail/validate.ts
|
|
35492
|
+
function isValidEmail(value) {
|
|
35493
|
+
return value.length <= 254 && EMAIL_RE.test(value);
|
|
35494
|
+
}
|
|
35495
|
+
function assertValidEmail(value, field) {
|
|
35496
|
+
if (!isValidEmail(value)) {
|
|
35497
|
+
throw new BridgeError(
|
|
35498
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
35499
|
+
`${field} is not a valid email address: ${JSON.stringify(value)}`,
|
|
35500
|
+
`Provide a full address like agent-channel@mail.cogent.tools`
|
|
35501
|
+
);
|
|
35502
|
+
}
|
|
35503
|
+
}
|
|
35504
|
+
var EMAIL_RE;
|
|
35505
|
+
var init_validate = __esm({
|
|
35506
|
+
"src/mail/validate.ts"() {
|
|
35507
|
+
"use strict";
|
|
35508
|
+
init_errors4();
|
|
35509
|
+
EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
35510
|
+
}
|
|
35511
|
+
});
|
|
35512
|
+
|
|
35513
|
+
// src/tools/setup-mail.ts
|
|
35514
|
+
function registerSetupMailTool(server) {
|
|
35515
|
+
server.registerTool(
|
|
35516
|
+
"cogent_setup_mail",
|
|
35517
|
+
{
|
|
35518
|
+
title: "Configure this agent's mailbox",
|
|
35519
|
+
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).",
|
|
35520
|
+
inputSchema: {
|
|
35521
|
+
address: import_zod16.z.string().describe("Full mailbox address, e.g. claude-agent-backend@mail.cogent.tools"),
|
|
35522
|
+
password: import_zod16.z.string().describe("Mailbox password (shown once in the admin Mail panel on create/rotate)"),
|
|
35523
|
+
imapHost: import_zod16.z.string().optional().describe(`IMAP host (default ${DEFAULT_MAIL_HOST})`),
|
|
35524
|
+
imapPort: import_zod16.z.number().int().positive().optional().describe("IMAP port (default 993)"),
|
|
35525
|
+
smtpHost: import_zod16.z.string().optional().describe(`SMTP host (default ${DEFAULT_MAIL_HOST})`),
|
|
35526
|
+
smtpPort: import_zod16.z.number().int().positive().optional().describe("SMTP submission port (default 465)")
|
|
35527
|
+
},
|
|
35528
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
35529
|
+
},
|
|
35530
|
+
async ({ address, password, imapHost, imapPort, smtpHost, smtpPort }) => {
|
|
35531
|
+
try {
|
|
35532
|
+
assertValidEmail(address, "address");
|
|
35533
|
+
await saveMailCredentials({
|
|
35534
|
+
address,
|
|
35535
|
+
password,
|
|
35536
|
+
imapHost: imapHost ?? DEFAULT_MAIL_HOST,
|
|
35537
|
+
imapPort: imapPort ?? 993,
|
|
35538
|
+
smtpHost: smtpHost ?? DEFAULT_MAIL_HOST,
|
|
35539
|
+
smtpPort: smtpPort ?? 465,
|
|
35540
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
35541
|
+
});
|
|
35542
|
+
return successResult({ configured: true, address });
|
|
35543
|
+
} catch (err) {
|
|
35544
|
+
return errorResult(err);
|
|
35545
|
+
}
|
|
35546
|
+
}
|
|
35547
|
+
);
|
|
35548
|
+
}
|
|
35549
|
+
var import_zod16, DEFAULT_MAIL_HOST;
|
|
35550
|
+
var init_setup_mail = __esm({
|
|
35551
|
+
"src/tools/setup-mail.ts"() {
|
|
35552
|
+
"use strict";
|
|
35553
|
+
import_zod16 = __toESM(require_zod(), 1);
|
|
35554
|
+
init_errors4();
|
|
35555
|
+
init_mail_credential_store();
|
|
35556
|
+
init_validate();
|
|
35557
|
+
DEFAULT_MAIL_HOST = MAIL_DEFAULT_HOST;
|
|
35558
|
+
}
|
|
35559
|
+
});
|
|
35560
|
+
|
|
35561
|
+
// src/mail/mail-sender.ts
|
|
35562
|
+
import fs13 from "node:fs/promises";
|
|
35563
|
+
import path13 from "node:path";
|
|
35564
|
+
async function buildAndSendMail(sender, creds, params) {
|
|
35565
|
+
const attachments = [];
|
|
35566
|
+
for (const p of params.attachmentPaths ?? []) {
|
|
35567
|
+
const content = await fs13.readFile(p);
|
|
35568
|
+
attachments.push({ filename: path13.basename(p), content });
|
|
35569
|
+
}
|
|
35570
|
+
const { messageId } = await sender.send({
|
|
35571
|
+
from: creds.address,
|
|
35572
|
+
to: params.to,
|
|
35573
|
+
subject: params.subject,
|
|
35574
|
+
text: params.text,
|
|
35575
|
+
attachments
|
|
35576
|
+
});
|
|
35577
|
+
return { messageId, from: creds.address, to: params.to };
|
|
35578
|
+
}
|
|
35579
|
+
var NodemailerMailSender;
|
|
35580
|
+
var init_mail_sender = __esm({
|
|
35581
|
+
"src/mail/mail-sender.ts"() {
|
|
35582
|
+
"use strict";
|
|
35583
|
+
NodemailerMailSender = class {
|
|
35584
|
+
constructor(creds, transportFactory) {
|
|
35585
|
+
this.creds = creds;
|
|
35586
|
+
this.transportFactory = transportFactory;
|
|
35587
|
+
}
|
|
35588
|
+
creds;
|
|
35589
|
+
transportFactory;
|
|
35590
|
+
async send(mail) {
|
|
35591
|
+
const transport = this.transportFactory ? await this.transportFactory() : await this.defaultTransport();
|
|
35592
|
+
const info = await transport.sendMail({
|
|
35593
|
+
from: mail.from,
|
|
35594
|
+
to: mail.to,
|
|
35595
|
+
subject: mail.subject,
|
|
35596
|
+
text: mail.text,
|
|
35597
|
+
attachments: mail.attachments.map((a) => ({ filename: a.filename, content: a.content }))
|
|
35598
|
+
});
|
|
35599
|
+
return { messageId: info.messageId ?? "" };
|
|
35600
|
+
}
|
|
35601
|
+
async defaultTransport() {
|
|
35602
|
+
const specifier = "nodemailer";
|
|
35603
|
+
const nodemailer = await import(specifier);
|
|
35604
|
+
return nodemailer.createTransport({
|
|
35605
|
+
host: this.creds.smtpHost,
|
|
35606
|
+
port: this.creds.smtpPort,
|
|
35607
|
+
secure: this.creds.smtpPort === 465,
|
|
35608
|
+
// implicit TLS on 465, STARTTLS on 587
|
|
35609
|
+
auth: { user: this.creds.address, pass: this.creds.password }
|
|
35610
|
+
});
|
|
35611
|
+
}
|
|
35612
|
+
};
|
|
35613
|
+
}
|
|
35614
|
+
});
|
|
35615
|
+
|
|
35616
|
+
// src/tools/send-mail.ts
|
|
35617
|
+
function registerSendMailTool(server, deps = {}) {
|
|
35618
|
+
const makeSender = deps.senderFactory ?? ((creds) => new NodemailerMailSender(creds));
|
|
35619
|
+
server.registerTool(
|
|
35620
|
+
"cogent_send_mail",
|
|
35621
|
+
{
|
|
35622
|
+
title: "Send an email from this agent's mailbox",
|
|
35623
|
+
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.",
|
|
35624
|
+
inputSchema: {
|
|
35625
|
+
to: import_zod17.z.string().describe("Recipient email address, e.g. other-agent-backend@mail.cogent.tools"),
|
|
35626
|
+
subject: import_zod17.z.string().describe("Email subject"),
|
|
35627
|
+
body: import_zod17.z.string().describe("Plain-text email body"),
|
|
35628
|
+
attachments: import_zod17.z.array(import_zod17.z.string()).optional().describe("Optional local file paths to attach (each is read and attached by basename)")
|
|
35629
|
+
},
|
|
35630
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
|
|
35631
|
+
},
|
|
35632
|
+
async ({ to, subject, body, attachments }) => {
|
|
35633
|
+
try {
|
|
35634
|
+
assertValidEmail(to, "to");
|
|
35635
|
+
const creds = await loadMailCredentials();
|
|
35636
|
+
if (!creds) {
|
|
35637
|
+
return errorResult(
|
|
35638
|
+
new BridgeError(
|
|
35639
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
35640
|
+
"No mailbox is configured for this agent",
|
|
35641
|
+
"Run cogent_setup_mail with the address + password from the admin Mail panel first"
|
|
35642
|
+
)
|
|
35643
|
+
);
|
|
35644
|
+
}
|
|
35645
|
+
const result = await buildAndSendMail(makeSender(creds), creds, {
|
|
35646
|
+
to: [to],
|
|
35647
|
+
subject,
|
|
35648
|
+
text: body,
|
|
35649
|
+
attachmentPaths: attachments
|
|
35650
|
+
});
|
|
35651
|
+
return successResult({ ...result, attachmentCount: attachments?.length ?? 0 });
|
|
35652
|
+
} catch (err) {
|
|
35653
|
+
return errorResult(err);
|
|
35654
|
+
}
|
|
35655
|
+
}
|
|
35656
|
+
);
|
|
35657
|
+
}
|
|
35658
|
+
var import_zod17;
|
|
35659
|
+
var init_send_mail = __esm({
|
|
35660
|
+
"src/tools/send-mail.ts"() {
|
|
35661
|
+
"use strict";
|
|
35662
|
+
import_zod17 = __toESM(require_zod(), 1);
|
|
35663
|
+
init_errors4();
|
|
35664
|
+
init_mail_credential_store();
|
|
35665
|
+
init_mail_sender();
|
|
35666
|
+
init_validate();
|
|
35667
|
+
}
|
|
35668
|
+
});
|
|
35669
|
+
|
|
35670
|
+
// src/mail/mail-fetcher.ts
|
|
35671
|
+
import fs14 from "node:fs/promises";
|
|
35672
|
+
import path14 from "node:path";
|
|
35673
|
+
async function saveAttachments(attachments, downloadDir) {
|
|
35674
|
+
if (attachments.length === 0) return [];
|
|
35675
|
+
await fs14.mkdir(downloadDir, { recursive: true });
|
|
35676
|
+
const saved = [];
|
|
35677
|
+
for (const a of attachments) {
|
|
35678
|
+
const name = path14.basename(a.filename) || "attachment";
|
|
35679
|
+
const savedPath = path14.join(downloadDir, name);
|
|
35680
|
+
await fs14.writeFile(savedPath, a.content);
|
|
35681
|
+
saved.push({ filename: name, savedPath, size: a.content.length });
|
|
35682
|
+
}
|
|
35683
|
+
return saved;
|
|
35684
|
+
}
|
|
35685
|
+
async function defaultImapSessionFactory(creds) {
|
|
35686
|
+
const specifier = "imapflow";
|
|
35687
|
+
const { ImapFlow } = await import(specifier);
|
|
35688
|
+
const client = new ImapFlow({
|
|
35689
|
+
host: creds.imapHost,
|
|
35690
|
+
port: creds.imapPort,
|
|
35691
|
+
secure: creds.imapPort === 993,
|
|
35692
|
+
auth: { user: creds.address, pass: creds.password },
|
|
35693
|
+
logger: false
|
|
35694
|
+
});
|
|
35695
|
+
await client.connect();
|
|
35696
|
+
await client.mailboxOpen("INBOX");
|
|
35697
|
+
return {
|
|
35698
|
+
async list(unreadOnly) {
|
|
35699
|
+
const criteria = unreadOnly ? { seen: false } : { all: true };
|
|
35700
|
+
const rows = [];
|
|
35701
|
+
for await (const msg of client.fetch(criteria, { uid: true, envelope: true, flags: true })) {
|
|
35702
|
+
const env = msg.envelope ?? {};
|
|
35703
|
+
rows.push({
|
|
35704
|
+
uid: msg.uid,
|
|
35705
|
+
from: env.from?.[0]?.address ?? "",
|
|
35706
|
+
subject: env.subject ?? "",
|
|
35707
|
+
date: env.date instanceof Date ? env.date.toISOString() : String(env.date ?? ""),
|
|
35708
|
+
unread: !hasFlag(msg.flags, "\\Seen")
|
|
35709
|
+
});
|
|
35710
|
+
}
|
|
35711
|
+
return rows.reverse();
|
|
35712
|
+
},
|
|
35713
|
+
async fetch(uid) {
|
|
35714
|
+
const msg = await client.fetchOne(uid, { uid: true, envelope: true, bodyStructure: true }, { uid: true });
|
|
35715
|
+
if (!msg) throw new Error(`no message with uid ${uid}`);
|
|
35716
|
+
const parts = flattenBodyStructure(msg.bodyStructure);
|
|
35717
|
+
let text = "";
|
|
35718
|
+
const attachments = [];
|
|
35719
|
+
for (const p of parts) {
|
|
35720
|
+
if (p.filename) {
|
|
35721
|
+
const dl = await client.download(uid, p.part, { uid: true });
|
|
35722
|
+
attachments.push({ filename: p.filename, content: await streamToBuffer(dl.content) });
|
|
35723
|
+
} else if (p.isText && !text) {
|
|
35724
|
+
const dl = await client.download(uid, p.part, { uid: true });
|
|
35725
|
+
text = (await streamToBuffer(dl.content)).toString("utf-8");
|
|
35726
|
+
}
|
|
35727
|
+
}
|
|
35728
|
+
const env = msg.envelope ?? {};
|
|
35729
|
+
return {
|
|
35730
|
+
from: env.from?.[0]?.address ?? "",
|
|
35731
|
+
subject: env.subject ?? "",
|
|
35732
|
+
date: env.date instanceof Date ? env.date.toISOString() : String(env.date ?? ""),
|
|
35733
|
+
text,
|
|
35734
|
+
attachments
|
|
35735
|
+
};
|
|
35736
|
+
},
|
|
35737
|
+
async close() {
|
|
35738
|
+
try {
|
|
35739
|
+
await client.logout();
|
|
35740
|
+
} catch {
|
|
35741
|
+
}
|
|
35742
|
+
}
|
|
35743
|
+
};
|
|
35744
|
+
}
|
|
35745
|
+
function flattenBodyStructure(node, acc = []) {
|
|
35746
|
+
if (!node || typeof node !== "object") return acc;
|
|
35747
|
+
const n = node;
|
|
35748
|
+
if (Array.isArray(n.childNodes) && n.childNodes.length > 0) {
|
|
35749
|
+
for (const child of n.childNodes) flattenBodyStructure(child, acc);
|
|
35750
|
+
return acc;
|
|
35751
|
+
}
|
|
35752
|
+
const type = (n.type ?? "").toLowerCase();
|
|
35753
|
+
const filename = n.dispositionParameters?.filename ?? n.parameters?.name;
|
|
35754
|
+
const isAttachment = (n.disposition ?? "").toLowerCase() === "attachment" || Boolean(filename);
|
|
35755
|
+
acc.push({
|
|
35756
|
+
part: n.part ?? "1",
|
|
35757
|
+
// single-part messages have no `part`; imapflow addresses them as "1"
|
|
35758
|
+
type,
|
|
35759
|
+
isText: type === "text/plain" && !isAttachment,
|
|
35760
|
+
filename: isAttachment ? filename : void 0
|
|
35761
|
+
});
|
|
35762
|
+
return acc;
|
|
35763
|
+
}
|
|
35764
|
+
function hasFlag(flags, flag) {
|
|
35765
|
+
if (flags instanceof Set) return flags.has(flag);
|
|
35766
|
+
if (Array.isArray(flags)) return flags.includes(flag);
|
|
35767
|
+
return false;
|
|
35768
|
+
}
|
|
35769
|
+
async function streamToBuffer(stream) {
|
|
35770
|
+
const chunks = [];
|
|
35771
|
+
if (!stream) return Buffer.alloc(0);
|
|
35772
|
+
for await (const chunk of stream) chunks.push(Buffer.from(chunk));
|
|
35773
|
+
return Buffer.concat(chunks);
|
|
35774
|
+
}
|
|
35775
|
+
var ImapflowMailFetcher;
|
|
35776
|
+
var init_mail_fetcher = __esm({
|
|
35777
|
+
"src/mail/mail-fetcher.ts"() {
|
|
35778
|
+
"use strict";
|
|
35779
|
+
ImapflowMailFetcher = class {
|
|
35780
|
+
constructor(creds, sessionFactory = defaultImapSessionFactory) {
|
|
35781
|
+
this.creds = creds;
|
|
35782
|
+
this.sessionFactory = sessionFactory;
|
|
35783
|
+
}
|
|
35784
|
+
creds;
|
|
35785
|
+
sessionFactory;
|
|
35786
|
+
async list(opts = {}) {
|
|
35787
|
+
const session = await this.sessionFactory(this.creds);
|
|
35788
|
+
try {
|
|
35789
|
+
let rows = await session.list(opts.unreadOnly ?? false);
|
|
35790
|
+
if (opts.limit && opts.limit > 0) rows = rows.slice(0, opts.limit);
|
|
35791
|
+
return rows;
|
|
35792
|
+
} finally {
|
|
35793
|
+
await session.close();
|
|
35794
|
+
}
|
|
35795
|
+
}
|
|
35796
|
+
async fetchOne(uid, downloadDir) {
|
|
35797
|
+
const session = await this.sessionFactory(this.creds);
|
|
35798
|
+
try {
|
|
35799
|
+
const c2 = await session.fetch(uid);
|
|
35800
|
+
const saved = await saveAttachments(c2.attachments, downloadDir);
|
|
35801
|
+
return { uid, from: c2.from, subject: c2.subject, date: c2.date, text: c2.text, attachments: saved };
|
|
35802
|
+
} finally {
|
|
35803
|
+
await session.close();
|
|
35804
|
+
}
|
|
35805
|
+
}
|
|
35806
|
+
};
|
|
35807
|
+
}
|
|
35808
|
+
});
|
|
35809
|
+
|
|
35810
|
+
// src/tools/fetch-mail.ts
|
|
35811
|
+
import os8 from "node:os";
|
|
35812
|
+
import path15 from "node:path";
|
|
35813
|
+
function registerFetchMailTool(server, deps = {}) {
|
|
35814
|
+
const makeFetcher = deps.fetcherFactory ?? ((creds) => new ImapflowMailFetcher(creds));
|
|
35815
|
+
server.registerTool(
|
|
35816
|
+
"cogent_fetch_mail",
|
|
35817
|
+
{
|
|
35818
|
+
title: "Read this agent's mailbox",
|
|
35819
|
+
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.",
|
|
35820
|
+
inputSchema: {
|
|
35821
|
+
action: import_zod18.z.enum(["list", "fetch"]).optional().describe("'list' (default) or 'fetch' one message"),
|
|
35822
|
+
uid: import_zod18.z.number().int().positive().optional().describe("Message uid to fetch (required when action='fetch')"),
|
|
35823
|
+
unreadOnly: import_zod18.z.boolean().optional().describe("List only unread messages (default false)"),
|
|
35824
|
+
limit: import_zod18.z.number().int().positive().optional().describe("Max messages to list (default 25)"),
|
|
35825
|
+
downloadDir: import_zod18.z.string().optional().describe(`Directory to save fetched attachments (default ${DEFAULT_DOWNLOAD_DIR})`)
|
|
35826
|
+
},
|
|
35827
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
35828
|
+
},
|
|
35829
|
+
async ({ action, uid, unreadOnly, limit, downloadDir }) => {
|
|
35830
|
+
try {
|
|
35831
|
+
const creds = await loadMailCredentials();
|
|
35832
|
+
if (!creds) {
|
|
35833
|
+
return errorResult(
|
|
35834
|
+
new BridgeError(
|
|
35835
|
+
"INVALID_INPUT" /* INVALID_INPUT */,
|
|
35836
|
+
"No mailbox is configured for this agent",
|
|
35837
|
+
"Run cogent_setup_mail with the address + password from the admin Mail panel first"
|
|
35838
|
+
)
|
|
35839
|
+
);
|
|
35840
|
+
}
|
|
35841
|
+
const fetcher = makeFetcher(creds);
|
|
35842
|
+
if (action === "fetch") {
|
|
35843
|
+
if (uid === void 0) {
|
|
35844
|
+
return errorResult(
|
|
35845
|
+
new BridgeError("INVALID_INPUT" /* INVALID_INPUT */, "uid is required when action='fetch'", "Pass the uid from a cogent_fetch_mail list")
|
|
35846
|
+
);
|
|
35847
|
+
}
|
|
35848
|
+
const message = await fetcher.fetchOne(uid, downloadDir ?? DEFAULT_DOWNLOAD_DIR);
|
|
35849
|
+
return successResult({ mailbox: creds.address, message });
|
|
35850
|
+
}
|
|
35851
|
+
const messages = await fetcher.list({ unreadOnly, limit: limit ?? 25 });
|
|
35852
|
+
return successResult({ mailbox: creds.address, count: messages.length, messages });
|
|
35853
|
+
} catch (err) {
|
|
35854
|
+
return errorResult(err);
|
|
35855
|
+
}
|
|
35856
|
+
}
|
|
35857
|
+
);
|
|
35858
|
+
}
|
|
35859
|
+
var import_zod18, DEFAULT_DOWNLOAD_DIR;
|
|
35860
|
+
var init_fetch_mail = __esm({
|
|
35861
|
+
"src/tools/fetch-mail.ts"() {
|
|
35862
|
+
"use strict";
|
|
35863
|
+
import_zod18 = __toESM(require_zod(), 1);
|
|
35864
|
+
init_errors4();
|
|
35865
|
+
init_mail_credential_store();
|
|
35866
|
+
init_mail_fetcher();
|
|
35867
|
+
DEFAULT_DOWNLOAD_DIR = path15.join(os8.homedir(), ".cogent", "mail-downloads");
|
|
35868
|
+
}
|
|
35869
|
+
});
|
|
35870
|
+
|
|
35375
35871
|
// src/index.ts
|
|
35376
35872
|
var index_exports = {};
|
|
35377
35873
|
async function main() {
|
|
@@ -35385,6 +35881,9 @@ async function main() {
|
|
|
35385
35881
|
registerHealthCheckTool(server);
|
|
35386
35882
|
registerCreateSessionTool(server);
|
|
35387
35883
|
registerJoinSessionTool(server);
|
|
35884
|
+
registerSetupMailTool(server);
|
|
35885
|
+
registerSendMailTool(server);
|
|
35886
|
+
registerFetchMailTool(server);
|
|
35388
35887
|
if (cloudInbox) {
|
|
35389
35888
|
server.registerResource(
|
|
35390
35889
|
"cogent_inbox",
|
|
@@ -35444,6 +35943,9 @@ var init_index = __esm({
|
|
|
35444
35943
|
init_health_check2();
|
|
35445
35944
|
init_create_session();
|
|
35446
35945
|
init_join_session();
|
|
35946
|
+
init_setup_mail();
|
|
35947
|
+
init_send_mail();
|
|
35948
|
+
init_fetch_mail();
|
|
35447
35949
|
init_heartbeat();
|
|
35448
35950
|
process.on("uncaughtException", (err) => {
|
|
35449
35951
|
if (err.code === "EPIPE") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@essentialai/cogent-plugin",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.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",
|