@essentialai/cogent-bridge-bundled 3.25.9 → 3.25.11

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.
Files changed (2) hide show
  1. package/cogent-bridge.mjs +78 -27
  2. package/package.json +1 -1
package/cogent-bridge.mjs CHANGED
@@ -26475,8 +26475,8 @@ var init_stdio2 = __esm({
26475
26475
  // src/constants.ts
26476
26476
  import { createRequire } from "node:module";
26477
26477
  function resolveVersion() {
26478
- if ("3.25.9") {
26479
- return "3.25.9";
26478
+ if ("3.25.11") {
26479
+ return "3.25.11";
26480
26480
  }
26481
26481
  try {
26482
26482
  const require2 = createRequire(import.meta.url);
@@ -26692,6 +26692,9 @@ ${issues}`);
26692
26692
  _config = Object.freeze(data);
26693
26693
  return _config;
26694
26694
  }
26695
+ function readOnlyWake(config2) {
26696
+ return config2.COGENT_READ_ONLY_WAKE || config2.COGENT_WAKE_TOOLS === "read";
26697
+ }
26695
26698
  function getConfig() {
26696
26699
  if (!_config) throw new Error("Config not loaded. Call loadConfig() first.");
26697
26700
  return _config;
@@ -26755,11 +26758,44 @@ var init_config = __esm({
26755
26758
  // launches via the daemon (see bin/cogent-codex) and sets this to `app-server`.
26756
26759
  // Only affects COGENT_PLATFORM=codex; claude/gemini/mail rails never read it.
26757
26760
  COGENT_CODEX_WAKE: import_zod2.z.enum(["auto", "exec-resume", "app-server"]).default("auto"),
26758
- // COG-86: what a Claude wake may do. `read` (default) = Read, Glob and Grep only: the wake answers but cannot edit,
26759
- // run commands or commit. A wake resumes the SAME session a human may have open in a terminal, and nothing can
26760
- // tell reliably that the terminal is there; on 2026-10-15 a full-tool wake edited a live session's files, then
26761
- // committed and pushed. `full` = every tool (the behaviour before 3.25.9), for an agent no human drives.
26762
- COGENT_WAKE_TOOLS: import_zod2.z.enum(["read", "full"]).default("read"),
26761
+ // COG-86 / COG-88: what a Claude wake may do. `full` (default) = every tool, as before 3.25.9. `read` = Read,
26762
+ // Glob and Grep only: the wake answers but cannot edit, run commands or commit. A wake resumes the SAME session
26763
+ // a human may have open in a terminal, and nothing can tell reliably that the terminal is there; on 2026-10-15
26764
+ // a full-tool wake edited a live session's files, then committed and pushed. 3.25.9 made `read` the default;
26765
+ // the operator reversed that on 2026-10-16 (COG-88): read-only is an opt-in, switched with the environment
26766
+ // variable COGENT_READ_ONLY_WAKE (below) or here. An EMPTY value counts as unset — a host that substitutes an empty
26767
+ // string for a missing setting must not take the bridge down. Case and surrounding spaces are ignored
26768
+ // ("Read", "read " = read), the same rule COGENT_READ_ONLY_WAKE uses — a user opting in must not stop the
26769
+ // bridge over a capital letter. Any other unknown value still fails loudly.
26770
+ // Read it through readOnlyWake(), never directly: either opt-in turns read-only on.
26771
+ COGENT_WAKE_TOOLS: import_zod2.z.preprocess(
26772
+ (v) => {
26773
+ if (typeof v !== "string") return v;
26774
+ const s = v.trim().toLowerCase();
26775
+ return s === "" ? void 0 : s;
26776
+ },
26777
+ import_zod2.z.enum(["read", "full"]).default("full")
26778
+ ),
26779
+ // COG-88: the read-only wake opt-in — an ENVIRONMENT VARIABLE only: no plugin setting, so no Claude Code
26780
+ // version floor. Plugin users set it in the "env" block of ~/.claude/settings.json or export it in the shell
26781
+ // (both reach the plugin's MCP server and its SessionStart hook — measured, Claude Code 2.1.74 and 2.1.302); a custom/Team config sets it
26782
+ // in its server env block. Applied at bridge start: /reload-plugins does NOT restart the server (measured).
26783
+ // The plugin's .mcp.json must NEVER set this key (nor COGENT_WAKE_TOOLS): a plugin env-block key wins over the
26784
+ // shell and settings.json, so any value there would silently block the user's opt-in (measured).
26785
+ //
26786
+ // FUTURE /config option (operator, 2026-10-16: may become the only switch; NOT shipped now). It would be a
26787
+ // plugin.json "userConfig": { "read_only_wake": { "type": "boolean", ... } } mapped by plugin/.mcp.json
26788
+ // "COGENT_READ_ONLY_WAKE": "${user_config.read_only_wake}" — Claude Code passes the STRING "true"/"false"
26789
+ // (measured, 2.1.301). userConfig needs Claude Code >= 2.1.75: 2.1.74 and older reject the manifest
26790
+ // ('Unrecognized key: "userConfig"') and load NONE of the plugin (measured 2026-10-16). Adding that env line
26791
+ // also ends the env-variable path for plugin users (the block wins), so the two cannot ship side by side, and
26792
+ // the guards in src/config.test.ts and cogent-plugin/scripts/sync-from-plugin.test.mjs must change with it.
26793
+ //
26794
+ // Only an explicit affirmative turns it on. Anything else is OFF and never an error — "", "false", a typo, or
26795
+ // an unsubstituted "${user_config.read_only_wake}": a config error here would stop the bridge, which is worse
26796
+ // than a wake that behaves as it did before 3.25.9. The plugin's SessionStart notice reads it with the same rule
26797
+ // (readOnlyWakeFromEnv in plugin/hooks/check-on-stop.lib.mjs, parity-tested).
26798
+ COGENT_READ_ONLY_WAKE: import_zod2.z.string().optional().transform((v) => v !== void 0 && ["true", "1", "yes", "on"].includes(v.trim().toLowerCase())),
26763
26799
  // Auto-recovery for a blocked Codex sandbox. On a host where the OS sandbox
26764
26800
  // can't initialize (unprivileged user namespaces blocked — Ubuntu 24.04's
26765
26801
  // apparmor_restrict_unprivileged_userns, containers, hardened kernels),
@@ -27424,15 +27460,17 @@ async function execClaude(sessionId, message, cwd, timeoutMs) {
27424
27460
  // (the Monitor tool, loaded through ToolSearch, ran a shell and wrote a file) and would miss every tool a later
27425
27461
  // claude adds. --tools is variadic, so it must be followed by another option, never by the message. A claude
27426
27462
  // too old to know --tools exits "unknown option" — the wake fails, it never runs with every tool.
27427
- ...config2.COGENT_WAKE_TOOLS === "read" ? ["--tools", READ_ONLY_WAKE_TOOLS, "--append-system-prompt", READ_ONLY_WAKE_NOTICE] : [],
27463
+ // COG-88: opt-in (COGENT_READ_ONLY_WAKE=true or COGENT_WAKE_TOOLS=read in the env); off by default.
27464
+ ...readOnlyWake(config2) ? ["--tools", READ_ONLY_WAKE_TOOLS, "--append-system-prompt", READ_ONLY_WAKE_NOTICE] : [],
27428
27465
  "-p",
27429
- truncated
27466
+ readOnlyWake(config2) ? `${truncated}${READ_ONLY_WAKE_PROMPT_SEPARATOR}${READ_ONLY_WAKE_NOTICE}` : truncated
27430
27467
  ];
27431
27468
  const cleanEnv = buildResumeEnv(process.env, "CLAUDE", ["CLAUDE_CONFIG_DIR"]);
27432
27469
  if (resolvedRoot && resolvedRoot !== path10.resolve(claudeConfigDir())) {
27433
27470
  cleanEnv.CLAUDE_CONFIG_DIR = resolvedRoot;
27434
27471
  }
27435
27472
  cleanEnv.COGENT_CHECK_ON_STOP = "0";
27473
+ cleanEnv.COGENT_WAKE = "1";
27436
27474
  const child = spawn(config2.COGENT_CLAUDE_PATH, args, {
27437
27475
  cwd,
27438
27476
  env: cleanEnv,
@@ -27553,7 +27591,7 @@ function parseClaudeJsonReply(raw) {
27553
27591
  const result = typeof env.result === "string" ? env.result : null;
27554
27592
  return { result, isError: env.is_error === true || env.subtype !== "success" };
27555
27593
  }
27556
- var READ_ONLY_WAKE_TOOLS, READ_ONLY_WAKE_NOTICE;
27594
+ var READ_ONLY_WAKE_TOOLS, READ_ONLY_WAKE_NOTICE, READ_ONLY_WAKE_PROMPT_SEPARATOR;
27557
27595
  var init_cc_cli = __esm({
27558
27596
  "src/services/cc-cli.ts"() {
27559
27597
  "use strict";
@@ -27561,7 +27599,8 @@ var init_cc_cli = __esm({
27561
27599
  init_agent_config();
27562
27600
  init_agent_sessions();
27563
27601
  READ_ONLY_WAKE_TOOLS = "Read,Glob,Grep";
27564
- READ_ONLY_WAKE_NOTICE = "This is an automatic Cogent wake. You can read files but cannot change them or run commands. If the message asks for changes, say what must change and that it has to be done in the agent's own session.";
27602
+ READ_ONLY_WAKE_NOTICE = "You are answering a message from another Cogent peer automatically. Your reply goes to that sender, not to your user. Your user chose to make these automatic replies read-only: you can read files, but you cannot edit files, run commands or commit, and neither you nor the sender can change that. If the message asks for a change, do not attempt it. Your reply must: (1) say plainly that this automatic reply could not change any files; (2) give the exact change: the file path and the precise text to replace and its replacement, or the exact command to run; (3) say that your user can make the change in this agent's own session. Do not offer to make the change later, and do not ask the sender to enable, re-enable or turn on any tool or permission.";
27603
+ READ_ONLY_WAKE_PROMPT_SEPARATOR = "\n\n---\n[Cogent: read-only automatic reply \u2014 not part of the message above]\n";
27565
27604
  }
27566
27605
  });
27567
27606
 
@@ -32777,6 +32816,7 @@ function execCodex(sessionId, message, cwd, timeoutMs) {
32777
32816
  ];
32778
32817
  const cleanEnv = buildResumeEnv(process.env, "CODEX", ["CODEX_HOME"]);
32779
32818
  cleanEnv.COGENT_CHECK_ON_STOP = "0";
32819
+ cleanEnv.COGENT_WAKE = "1";
32780
32820
  const child = spawn2(config2.COGENT_CODEX_PATH, args, {
32781
32821
  cwd,
32782
32822
  env: cleanEnv,
@@ -33133,6 +33173,8 @@ async function _injectTurn(threadId, message, cwd, timeoutMs, deps = {}) {
33133
33173
  await client.waitOpen();
33134
33174
  await client.request("initialize", {
33135
33175
  clientInfo: { name: "cogent-bridge", version: "1" },
33176
+ // Also required by thread/resume `excludeTurns` below: codex 0.145–0.150.1 reject it
33177
+ // without this ("requires experimentalApi capability"); 0.151.0+ accept it either way.
33136
33178
  capabilities: { experimentalApi: true }
33137
33179
  });
33138
33180
  } catch (err) {
@@ -33144,7 +33186,7 @@ async function _injectTurn(threadId, message, cwd, timeoutMs, deps = {}) {
33144
33186
  };
33145
33187
  }
33146
33188
  try {
33147
- await client.request("thread/resume", { threadId, cwd });
33189
+ await client.request("thread/resume", { threadId, cwd, excludeTurns: true });
33148
33190
  const buffered = [];
33149
33191
  let myTurnId;
33150
33192
  const isMine = (msg) => msg.method === "turn/completed" && (myTurnId !== void 0 ? msg.params?.turn?.id === myTurnId : msg.params?.threadId === threadId);
@@ -34319,7 +34361,10 @@ ${message}${formatAttachmentsBlock(attachments)}
34319
34361
  ---
34320
34362
  FYI ONLY \u2014 another agent addressed the whole channel. Take note for context, but do NOT reply and do NOT call any cogent_* tool in response. A human will drive any reply.`;
34321
34363
  }
34322
- function describeWakeFailure(exitCode, stderr, stdout) {
34364
+ function wakeAgentFor(platform) {
34365
+ return platform === "codex" ? "codex" : "claude";
34366
+ }
34367
+ function describeWakeFailure(agent, exitCode, stderr, stdout) {
34323
34368
  const raw = (stderr ?? "").trim();
34324
34369
  if (!raw && exitCode !== 0 && !stdout) return "";
34325
34370
  const firstLine = raw.split("\n").map((l) => l.trim()).find(Boolean) ?? "";
@@ -34331,7 +34376,7 @@ function describeWakeFailure(exitCode, stderr, stdout) {
34331
34376
  }
34332
34377
  if (/CLI_NOT_FOUND/.test(raw)) {
34333
34378
  return truncate(
34334
- `the claude executable was not found \u2014 install Claude Code or set COGENT_CLAUDE_PATH.`
34379
+ agent === "codex" ? `the codex executable was not found \u2014 install the Codex CLI or set COGENT_CODEX_PATH.` : `the claude executable was not found \u2014 install Claude Code or set COGENT_CLAUDE_PATH.`
34335
34380
  );
34336
34381
  }
34337
34382
  const timedOut = /CLI_TIMEOUT: CLI subprocess timed out after (\d+)ms/.exec(raw);
@@ -34353,15 +34398,15 @@ function describeWakeFailure(exitCode, stderr, stdout) {
34353
34398
  }
34354
34399
  if (env.result) bits.push(env.result);
34355
34400
  if (!bits.length && env.isError) bits.push("the run reported is_error with no detail");
34356
- if (bits.length) return truncate(`claude exited ${code}: ${bits.join(" \u2014 ")}`);
34401
+ if (bits.length) return truncate(`${agent} exited ${code}: ${bits.join(" \u2014 ")}`);
34357
34402
  }
34358
34403
  return truncate(
34359
- stdout?.trim() ? `claude exited ${code} with nothing on stderr; its stdout was not a readable result envelope` : `claude exited ${code} with no output at all on stdout or stderr`
34404
+ stdout?.trim() ? `${agent} exited ${code} with nothing on stderr; its stdout was not a readable result envelope` : `${agent} exited ${code} with no output at all on stdout or stderr`
34360
34405
  );
34361
34406
  }
34362
34407
  if (firstLine) {
34363
34408
  const code = exitCode === null || exitCode === void 0 ? "?" : exitCode;
34364
- return truncate(`claude exited ${code}: ${firstLine}`);
34409
+ return truncate(`${agent} exited ${code}: ${firstLine}`);
34365
34410
  }
34366
34411
  if (exitCode === 0) {
34367
34412
  return truncate(`the resume completed successfully but returned no text.`);
@@ -35267,7 +35312,7 @@ var init_auto_relay = __esm({
35267
35312
  */
35268
35313
  async _recordNoReplyFailure(msg, traceId, durationMs, opts) {
35269
35314
  const codexLive = opts?.codexLiveSession === true;
35270
- 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}". ${describeWakeFailure(opts?.exitCode, opts?.stderr, opts?.stdout) || "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})`;
35315
+ 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}". ${describeWakeFailure(wakeAgentFor(getConfig().COGENT_PLATFORM), opts?.exitCode, opts?.stderr, opts?.stdout) || "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})`;
35271
35316
  try {
35272
35317
  await getBackend().recordMessage({
35273
35318
  fromPeerId: this.localPeerId,
@@ -35828,14 +35873,18 @@ var init_heartbeat = __esm({
35828
35873
  // src/services/claude-preflight.ts
35829
35874
  import { execFile as execFile4 } from "node:child_process";
35830
35875
  import { promisify as promisify3 } from "node:util";
35831
- function analyzeClaudeHelp(helpText) {
35832
- const checked = EXPECTED_CLAUDE_FLAGS.map((f) => f.flag);
35833
- const missing = EXPECTED_CLAUDE_FLAGS.filter(
35876
+ function expectedClaudeFlags(readOnly) {
35877
+ return readOnly ? [...EXPECTED_CLAUDE_FLAGS, ...READ_ONLY_WAKE_CLAUDE_FLAGS] : EXPECTED_CLAUDE_FLAGS;
35878
+ }
35879
+ function analyzeClaudeHelp(helpText, readOnly = false) {
35880
+ const expected = expectedClaudeFlags(readOnly);
35881
+ const checked = expected.map((f) => f.flag);
35882
+ const missing = expected.filter(
35834
35883
  (f) => !helpText.includes(f.flag)
35835
35884
  ).map((f) => f.flag);
35836
35885
  return { missing, checked };
35837
35886
  }
35838
- async function preflightClaude(claudePath) {
35887
+ async function preflightClaude(claudePath, readOnly = false) {
35839
35888
  let helpText;
35840
35889
  try {
35841
35890
  const { stdout, stderr } = await execFileAsync2(claudePath, ["--help"], {
@@ -35849,9 +35898,9 @@ ${stderr}`;
35849
35898
  );
35850
35899
  return null;
35851
35900
  }
35852
- const analysis = analyzeClaudeHelp(helpText);
35901
+ const analysis = analyzeClaudeHelp(helpText, readOnly);
35853
35902
  if (analysis.missing.length > 0) {
35854
- const detail = EXPECTED_CLAUDE_FLAGS.filter(
35903
+ const detail = expectedClaudeFlags(readOnly).filter(
35855
35904
  (f) => analysis.missing.includes(f.flag)
35856
35905
  ).map((f) => `${f.flag} (${f.usedFor})`).join(", ");
35857
35906
  logger.error(
@@ -35864,7 +35913,7 @@ ${stderr}`;
35864
35913
  }
35865
35914
  return analysis;
35866
35915
  }
35867
- var execFileAsync2, EXPECTED_CLAUDE_FLAGS;
35916
+ var execFileAsync2, EXPECTED_CLAUDE_FLAGS, READ_ONLY_WAKE_CLAUDE_FLAGS;
35868
35917
  var init_claude_preflight = __esm({
35869
35918
  "src/services/claude-preflight.ts"() {
35870
35919
  "use strict";
@@ -35876,7 +35925,9 @@ var init_claude_preflight = __esm({
35876
35925
  { flag: "--dangerously-skip-permissions", usedFor: "unattended auto-relay execution" },
35877
35926
  { flag: "--no-session-persistence", usedFor: "don't pollute the resumed session transcript" },
35878
35927
  { flag: "--strict-mcp-config", usedFor: "isolate MCP config of the resumed process" },
35879
- { flag: "--output-format", usedFor: "structured JSON reply envelope (Slice 3 capture)" },
35928
+ { flag: "--output-format", usedFor: "structured JSON reply envelope (Slice 3 capture)" }
35929
+ ];
35930
+ READ_ONLY_WAKE_CLAUDE_FLAGS = [
35880
35931
  { flag: "--tools", usedFor: "read-only wake: Read, Glob and Grep only (COG-86)" },
35881
35932
  { flag: "--append-system-prompt", usedFor: "tell a read-only wake it cannot change anything (COG-86)" }
35882
35933
  ];
@@ -36400,7 +36451,7 @@ async function runPreflights() {
36400
36451
  setSandboxOverride("bypass");
36401
36452
  }
36402
36453
  } else {
36403
- await preflightClaude(config2.COGENT_CLAUDE_PATH);
36454
+ await preflightClaude(config2.COGENT_CLAUDE_PATH, readOnlyWake(config2));
36404
36455
  }
36405
36456
  }
36406
36457
  var UUID_V4_RE, execFileAsync3, PERSIST_PATH, legacyWarnEmitted, cloudWsClient, cloudInbox, cloudHttpClient, cloudModeActive, inboxNotifier, notifierBoundTo, reRegistrationInProgress, presenceRetry, ignoredHolders, presenceSupersedeBound, PRESENCE_SUPERSEDED, pendingLabelResolve, lastRestoreSkip;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@essentialai/cogent-bridge-bundled",
3
- "version": "3.25.9",
3
+ "version": "3.25.11",
4
4
  "description": "Zero-dependency, pre-bundled Cogent MCP bridge — a single self-contained file for fast cold `npx` startup (used by the Codex plugin, whose MCP startup window is short). Functionally identical to @essentialai/cogent-bridge; carries no npm dependencies so `npx` skips dependency install.",
5
5
  "type": "module",
6
6
  "bin": {