@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.
- package/.claude-plugin/plugin.json +1 -1
- package/bridge/cogent-bridge.mjs +457 -93
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cogent",
|
|
3
|
-
"version": "3.
|
|
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",
|
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: 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,
|
|
1543
|
+
constructor(parent, value, path18, key) {
|
|
1544
1544
|
this._cachedPath = [];
|
|
1545
1545
|
this.parent = parent;
|
|
1546
1546
|
this.data = value;
|
|
1547
|
-
this._path =
|
|
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,
|
|
5000
|
-
if (!
|
|
4999
|
+
function getElementAtPath(obj, path18) {
|
|
5000
|
+
if (!path18)
|
|
5001
5001
|
return obj;
|
|
5002
|
-
return
|
|
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(
|
|
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(
|
|
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(
|
|
17062
|
-
let input =
|
|
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 [
|
|
17262
|
-
wsComponent.path =
|
|
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,
|
|
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,
|
|
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:
|
|
21733
|
-
const fullPath = [...
|
|
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,
|
|
21887
|
+
constructor(parent, value, path18, key) {
|
|
21888
21888
|
this._cachedPath = [];
|
|
21889
21889
|
this.parent = parent;
|
|
21890
21890
|
this.data = value;
|
|
21891
|
-
this._path =
|
|
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.
|
|
26393
|
-
return "3.
|
|
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
|
|
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(
|
|
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
|
|
27667
|
+
const path18 = PATHS.peer.replace(":sessionId", this.sessionId).replace(":peerId", peerId);
|
|
27633
27668
|
try {
|
|
27634
|
-
const headers = await this.http.delete(
|
|
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
|
|
27656
|
-
const result = await this.http.get(
|
|
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
|
|
27665
|
-
await this.http.post(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
27876
|
-
const url = new URL(
|
|
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(
|
|
27892
|
-
const url = new URL(
|
|
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(
|
|
27911
|
-
const url = new URL(
|
|
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(
|
|
27937
|
-
const url = new URL(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
33627
|
-
import
|
|
33628
|
-
import
|
|
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
|
|
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
|
|
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
|
|
33678
|
-
const testFile =
|
|
34040
|
+
await fs12.mkdir(statePath, { recursive: true });
|
|
34041
|
+
const testFile = path13.join(statePath, ".write-test");
|
|
33679
34042
|
try {
|
|
33680
|
-
await
|
|
33681
|
-
await
|
|
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 =
|
|
34190
|
+
const defaultPath = path13.join(os7.homedir(), ".cogent");
|
|
33828
34191
|
statePath = await firstRunPrompt(defaultPath);
|
|
33829
34192
|
}
|
|
33830
|
-
if (!envStatePath && statePath !==
|
|
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
|
-
|
|
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 =
|
|
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
|
|
33980
|
-
import
|
|
33981
|
-
import
|
|
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(
|
|
33984
|
-
return
|
|
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
|
|
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
|
|
34003
|
-
await
|
|
34004
|
-
await
|
|
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
|
|
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
|
|
35712
|
-
import
|
|
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
|
|
35717
|
-
attachments.push({ filename:
|
|
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
|
|
35846
|
-
import
|
|
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
|
|
36213
|
+
await fs15.mkdir(downloadDir, { recursive: true });
|
|
35850
36214
|
const saved = [];
|
|
35851
36215
|
for (const a of attachments) {
|
|
35852
|
-
const name =
|
|
35853
|
-
const savedPath =
|
|
35854
|
-
await
|
|
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
|
|
35986
|
-
import
|
|
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 =
|
|
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.
|
|
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",
|