@raysonmeng/agentbridge 0.1.9 → 0.1.10

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.
@@ -12,7 +12,7 @@
12
12
  {
13
13
  "name": "agentbridge",
14
14
  "description": "Bridge Claude Code and Codex through a shared daemon, push channel delivery, and reply/get_messages tools.",
15
- "version": "0.1.9",
15
+ "version": "0.1.10",
16
16
  "author": {
17
17
  "name": "AgentBridge Contributors",
18
18
  "email": "raysonmeng@qq.com"
package/README.md CHANGED
@@ -155,8 +155,9 @@ After modifying AgentBridge source code, re-run `agentbridge dev` to sync change
155
155
  | Command | Description |
156
156
  |---------|-------------|
157
157
  | `abg init` | Install plugin, check dependencies (bun/claude/codex), generate `.agentbridge/config.json` |
158
- | `abg claude [args...]` | Start Claude Code with push channel enabled. Clears any killed sentinel from a previous `kill`. Pass-through args are forwarded to `claude` |
159
- | `abg codex [args...]` | Start Codex TUI connected to AgentBridge daemon. **Bare `abg codex` auto-resumes the pair's last thread; use `abg codex --new` for a fresh thread.** Manages TUI process lifecycle (pid tracking, cleanup). Pass-through args forwarded to `codex` |
158
+ | `abg claude [args...]` | Start Claude Code with push channel enabled. **Runs with `--dangerously-skip-permissions` by default** (opt out: `--safe` or `AGENTBRIDGE_SAFE=1`). Clears any killed sentinel from a previous `kill`. Pass-through args are forwarded to `claude` |
159
+ | `abg codex [args...]` | Start Codex TUI connected to AgentBridge daemon. **Bare `abg codex` auto-resumes the pair's last thread; use `abg codex --new` for a fresh thread. TUI launches run with `--yolo` by default** (opt out: `--safe` or `AGENTBRIDGE_SAFE=1`; non-TUI subcommands like `exec` are never touched). Manages TUI process lifecycle (pid tracking, cleanup). Pass-through args forwarded to `codex` |
160
+ | `abg resume [claude\|codex]` | No target: print the resume commands for this directory's last Claude Code session and this pair's current Codex thread. With a target: resume that side directly (delegates to `abg claude --resume <id>` / `abg codex resume-current`) |
160
161
  | `abg pairs` | List registered pairs; `abg pairs rm <name\|id>` removes one, `abg pairs prune` deletes orphan state dirs |
161
162
  | `abg doctor [--json]` | Read-only diagnosis: env, daemon health/readiness, build drift, artifact alignment, TUI attachment, logs |
162
163
  | `abg budget [--json]` | Both agents' subscription quota snapshot (5h/weekly windows, drift, pause state) |
@@ -165,7 +166,7 @@ After modifying AgentBridge source code, re-run `agentbridge dev` to sync change
165
166
  | `abg --help` | Show help |
166
167
  | `abg --version` | Show version |
167
168
 
168
- The pair-aware commands (`claude`, `codex`, `kill`, `doctor`, `budget`) accept `--pair <name>` to target a specific pair — one pair per project directory by default, with ports allocated per pair in +10 strides from 4500.
169
+ The pair-aware commands (`claude`, `codex`, `resume`, `kill`, `doctor`, `budget`) accept `--pair <name>` to target a specific pair — one pair per project directory by default, with ports allocated per pair in +10 strides from 4500.
169
170
 
170
171
  ### Owned flags
171
172
 
@@ -173,6 +174,7 @@ Some flags are automatically injected and cannot be manually specified:
173
174
 
174
175
  - `agentbridge claude` owns: `--channels`, `--dangerously-load-development-channels`
175
176
  - `agentbridge codex` owns: `--remote`, `--enable tui_app_server`
177
+ - Both launchers consume the wrapper flag `--safe` (it is never forwarded): it disables the max-permission defaults for that launch. The defaults are also auto-suppressed when you pass any explicit permission flag yourself (`-a`/`--ask-for-approval`/`-s`/`--sandbox` for codex; `--permission-mode`/`--allow-dangerously-skip-permissions` for claude) — injecting `--yolo` next to an explicit approval policy is a hard codex CLI conflict.
176
178
 
177
179
  Passing these flags manually will result in a hard error with guidance to use the native command directly.
178
180
 
package/dist/cli.js CHANGED
@@ -120,7 +120,7 @@ function parsePositiveIntEnv(name, fallback, log = () => {}, env = process.env)
120
120
  var require_package = __commonJS((exports, module) => {
121
121
  module.exports = {
122
122
  name: "@raysonmeng/agentbridge",
123
- version: "0.1.9",
123
+ version: "0.1.10",
124
124
  description: "Bridge between Claude Code and Codex \u2014 bidirectional agent communication via MCP Channel + JSON-RPC",
125
125
  type: "module",
126
126
  packageManager: "bun@1.3.11",
@@ -1165,8 +1165,8 @@ function formatBuildInfo(build) {
1165
1165
  var BUILD_INFO;
1166
1166
  var init_build_info = __esm(() => {
1167
1167
  BUILD_INFO = Object.freeze({
1168
- version: defineString("0.1.9", "0.0.0-source"),
1169
- commit: defineString("10dfd58", "source"),
1168
+ version: defineString("0.1.10", "0.0.0-source"),
1169
+ commit: defineString("51a44cb", "source"),
1170
1170
  bundle: defineBundle("dist"),
1171
1171
  contractVersion: defineNumber(1, CONTRACT_VERSION)
1172
1172
  });
@@ -2441,6 +2441,46 @@ var init_trace_log = __esm(() => {
2441
2441
  RELEVANT_ENV_RE = /^(AGENTBRIDGE_|CODEX_)/;
2442
2442
  });
2443
2443
 
2444
+ // src/cli/max-permissions.ts
2445
+ function planMaxPermissions(args, suppressors, env = process.env) {
2446
+ let safeFlag = false;
2447
+ const rest = [];
2448
+ for (const a of args) {
2449
+ if (a === "--safe") {
2450
+ safeFlag = true;
2451
+ continue;
2452
+ }
2453
+ rest.push(a);
2454
+ }
2455
+ const safeMode = safeFlag || env.AGENTBRIDGE_SAFE === "1";
2456
+ const userExpressedPreference = rest.some((a) => suppressors.some((s) => matchesSuppressor(a, s)));
2457
+ return { args: rest, inject: !safeMode && !userExpressedPreference, safeMode };
2458
+ }
2459
+ function matchesSuppressor(token, suppressor) {
2460
+ if (token === suppressor || token.startsWith(`${suppressor}=`))
2461
+ return true;
2462
+ if (/^-[A-Za-z]$/.test(suppressor) && !token.startsWith("--") && token.startsWith(suppressor) && token.length > suppressor.length) {
2463
+ return true;
2464
+ }
2465
+ return false;
2466
+ }
2467
+ var CLAUDE_MAX_PERMISSION_FLAG = "--dangerously-skip-permissions", CLAUDE_MAX_PERMISSION_SUPPRESSORS, CODEX_MAX_PERMISSION_FLAG = "--yolo", CODEX_MAX_PERMISSION_SUPPRESSORS;
2468
+ var init_max_permissions = __esm(() => {
2469
+ CLAUDE_MAX_PERMISSION_SUPPRESSORS = [
2470
+ CLAUDE_MAX_PERMISSION_FLAG,
2471
+ "--allow-dangerously-skip-permissions",
2472
+ "--permission-mode"
2473
+ ];
2474
+ CODEX_MAX_PERMISSION_SUPPRESSORS = [
2475
+ CODEX_MAX_PERMISSION_FLAG,
2476
+ "--dangerously-bypass-approvals-and-sandbox",
2477
+ "-a",
2478
+ "--ask-for-approval",
2479
+ "-s",
2480
+ "--sandbox"
2481
+ ];
2482
+ });
2483
+
2444
2484
  // src/cli/claude.ts
2445
2485
  var exports_claude = {};
2446
2486
  __export(exports_claude, {
@@ -2457,7 +2497,9 @@ async function runClaude(args) {
2457
2497
  allowStrict: true,
2458
2498
  log: (msg) => console.error(msg)
2459
2499
  });
2460
- const { pairFlag, rest } = parsePairFlag(args);
2500
+ const { pairFlag, rest: pairRest } = parsePairFlag(args);
2501
+ const permissionPlan = planMaxPermissions(pairRest, CLAUDE_MAX_PERMISSION_SUPPRESSORS);
2502
+ const rest = permissionPlan.args;
2461
2503
  checkOwnedFlagConflicts(rest, "agentbridge claude", OWNED_FLAGS);
2462
2504
  let pair;
2463
2505
  try {
@@ -2484,9 +2526,13 @@ async function runClaude(args) {
2484
2526
  await assertPairNotLive(lifecycle, pair);
2485
2527
  lifecycle.clearKilled();
2486
2528
  const channelEntry = `plugin:${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
2529
+ if (permissionPlan.inject) {
2530
+ console.error(`[agentbridge] running with ${CLAUDE_MAX_PERMISSION_FLAG} (default; opt out with --safe or AGENTBRIDGE_SAFE=1)`);
2531
+ }
2487
2532
  const fullArgs = [
2488
2533
  "--dangerously-load-development-channels",
2489
2534
  channelEntry,
2535
+ ...permissionPlan.inject ? [CLAUDE_MAX_PERMISSION_FLAG] : [],
2490
2536
  ...rest
2491
2537
  ];
2492
2538
  const child = spawn2("claude", fullArgs, {
@@ -2588,6 +2634,7 @@ var init_claude = __esm(() => {
2588
2634
  init_env_guard();
2589
2635
  init_pair_resolver();
2590
2636
  init_trace_log();
2637
+ init_max_permissions();
2591
2638
  OWNED_FLAGS = ["--channels", "--dangerously-load-development-channels"];
2592
2639
  });
2593
2640
 
@@ -3145,8 +3192,14 @@ function resolveCodexResumeArgs(parsed, pair, env = process.env) {
3145
3192
  }
3146
3193
  return { rest: parsed.rest, mode: "passthrough" };
3147
3194
  }
3148
- function buildCodexArgs(userArgs, proxyUrl) {
3149
- const bridgeFlags = ["--enable", "tui_app_server", "--remote", proxyUrl];
3195
+ function buildCodexArgs(userArgs, proxyUrl, opts = {}) {
3196
+ const bridgeFlags = [
3197
+ "--enable",
3198
+ "tui_app_server",
3199
+ "--remote",
3200
+ proxyUrl,
3201
+ ...opts.yolo ? [CODEX_MAX_PERMISSION_FLAG] : []
3202
+ ];
3150
3203
  const first = userArgs[0];
3151
3204
  if (!first || first.startsWith("-")) {
3152
3205
  return { fullArgs: [...bridgeFlags, ...userArgs], injectedBridgeFlags: true };
@@ -3172,7 +3225,8 @@ async function runCodex(args) {
3172
3225
  log: (msg) => console.error(msg)
3173
3226
  });
3174
3227
  const { pairFlag, rest } = parsePairFlag(args);
3175
- const wrapperArgs = parseAgentBridgeCodexArgs(rest);
3228
+ const permissionPlan = planMaxPermissions(rest, CODEX_MAX_PERMISSION_SUPPRESSORS);
3229
+ const wrapperArgs = parseAgentBridgeCodexArgs(permissionPlan.args);
3176
3230
  const agentsContract = checkAgentsMdContract(process.cwd());
3177
3231
  if (!agentsContract.fresh) {
3178
3232
  console.error(`[agentbridge] ${agentsContract.message}`);
@@ -3297,7 +3351,12 @@ async function runCodex(args) {
3297
3351
  if (resumeArgs.mode === "auto-resume" || resumeArgs.mode === "resume-current") {
3298
3352
  console.error(`[agentbridge] Resuming current Codex thread ${resumeArgs.thread.threadId}`);
3299
3353
  }
3300
- const { fullArgs } = buildCodexArgs(resumeArgs.rest, proxyUrl);
3354
+ const { fullArgs, injectedBridgeFlags } = buildCodexArgs(resumeArgs.rest, proxyUrl, {
3355
+ yolo: permissionPlan.inject
3356
+ });
3357
+ if (permissionPlan.inject && injectedBridgeFlags) {
3358
+ console.error(`[agentbridge] running with ${CODEX_MAX_PERMISSION_FLAG} (default; opt out with --safe or AGENTBRIDGE_SAFE=1)`);
3359
+ }
3301
3360
  const stderrTail = new StderrRingBuffer;
3302
3361
  const wrapperLogPath = stateDir.codexWrapperLogFile;
3303
3362
  const startedAt = Date.now();
@@ -3550,6 +3609,7 @@ var init_codex = __esm(() => {
3550
3609
  init_trace_log();
3551
3610
  init_process_lifecycle();
3552
3611
  init_claude();
3612
+ init_max_permissions();
3553
3613
  OWNED_FLAGS2 = ["--remote"];
3554
3614
  TUI_SUBCOMMANDS = new Set(["resume", "fork"]);
3555
3615
  NON_TUI_SUBCOMMANDS = new Set([
@@ -3577,6 +3637,130 @@ var init_codex = __esm(() => {
3577
3637
  ]);
3578
3638
  });
3579
3639
 
3640
+ // src/claude-session.ts
3641
+ import { readdirSync as readdirSync4, statSync as statSync5 } from "fs";
3642
+ import { homedir as homedir5 } from "os";
3643
+ import { join as join12 } from "path";
3644
+ function encodeClaudeProjectDir(cwd) {
3645
+ return cwd.replace(/[^a-zA-Z0-9]/g, "-");
3646
+ }
3647
+ function findLatestClaudeSession(cwd, claudeHome = process.env.CLAUDE_CONFIG_DIR || join12(homedir5(), ".claude")) {
3648
+ const dir = join12(claudeHome, "projects", encodeClaudeProjectDir(cwd));
3649
+ let entries;
3650
+ try {
3651
+ entries = readdirSync4(dir);
3652
+ } catch {
3653
+ return null;
3654
+ }
3655
+ let best = null;
3656
+ for (const name of entries) {
3657
+ if (!name.endsWith(".jsonl"))
3658
+ continue;
3659
+ const sessionId = name.slice(0, -".jsonl".length);
3660
+ if (!SESSION_ID_PATTERN.test(sessionId))
3661
+ continue;
3662
+ const file = join12(dir, name);
3663
+ let mtimeMs;
3664
+ try {
3665
+ const st = statSync5(file);
3666
+ if (!st.isFile())
3667
+ continue;
3668
+ mtimeMs = st.mtimeMs;
3669
+ } catch {
3670
+ continue;
3671
+ }
3672
+ if (!best || mtimeMs > best.mtimeMs) {
3673
+ best = { sessionId, file, mtimeMs };
3674
+ }
3675
+ }
3676
+ return best;
3677
+ }
3678
+ var SESSION_ID_PATTERN;
3679
+ var init_claude_session = __esm(() => {
3680
+ SESSION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3681
+ });
3682
+
3683
+ // src/cli/resume.ts
3684
+ var exports_resume = {};
3685
+ __export(exports_resume, {
3686
+ runResume: () => runResume,
3687
+ resolveResumeTargets: () => resolveResumeTargets
3688
+ });
3689
+ function resolveResumeTargets(opts = {}) {
3690
+ const cwd = process.cwd();
3691
+ const { pair } = resolvePairReadOnly(opts.pairFlag);
3692
+ const claude = findLatestClaudeSession(cwd, opts.claudeHome);
3693
+ const codex = readUsableCurrentThread({
3694
+ stateDir: pair.stateDir,
3695
+ pairId: pair.manual ? null : pair.pairId,
3696
+ pairName: pair.name,
3697
+ cwd
3698
+ }, opts.env ?? process.env);
3699
+ return {
3700
+ pairName: pair.name,
3701
+ claudeSessionId: claude?.sessionId ?? null,
3702
+ codexThreadId: codex?.threadId ?? null
3703
+ };
3704
+ }
3705
+ async function runResume(args) {
3706
+ const { pairFlag, rest } = parsePairFlag(args);
3707
+ const target = rest[0];
3708
+ const extra = rest.slice(1);
3709
+ const pairPrefix = pairFlag !== undefined ? ["--pair", pairFlag] : [];
3710
+ if (target === "claude") {
3711
+ const session = findLatestClaudeSession(process.cwd());
3712
+ if (!session) {
3713
+ console.error("[agentbridge] No Claude Code session found for this directory.");
3714
+ console.error("[agentbridge] Start one with: abg claude");
3715
+ process.exit(1);
3716
+ }
3717
+ console.error(`[agentbridge] Resuming Claude Code session ${session.sessionId}`);
3718
+ const { runClaude: runClaude2 } = await Promise.resolve().then(() => (init_claude(), exports_claude));
3719
+ await runClaude2([...pairPrefix, "--resume", session.sessionId, ...extra]);
3720
+ return;
3721
+ }
3722
+ if (target === "codex") {
3723
+ const { runCodex: runCodex2 } = await Promise.resolve().then(() => (init_codex(), exports_codex));
3724
+ await runCodex2([...pairPrefix, "resume-current", ...extra]);
3725
+ return;
3726
+ }
3727
+ if (target !== undefined) {
3728
+ console.error(`Error: unknown resume target "${target}".`);
3729
+ console.error("");
3730
+ console.error("Usage:");
3731
+ console.error(" abg resume # print resume commands for this directory");
3732
+ console.error(" abg resume claude # resume the last Claude Code session here");
3733
+ console.error(" abg resume codex # resume this pair's current Codex thread");
3734
+ process.exit(1);
3735
+ }
3736
+ const targets = resolveResumeTargets({ pairFlag });
3737
+ const pairArg = pairFlag ? `--pair ${pairFlag} ` : "";
3738
+ const lines = [];
3739
+ if (targets.claudeSessionId) {
3740
+ lines.push(`abg ${pairArg}claude --resume ${targets.claudeSessionId}`);
3741
+ } else {
3742
+ lines.push(`# no Claude Code session found for this directory (start one: abg ${pairArg}claude)`);
3743
+ }
3744
+ if (targets.codexThreadId) {
3745
+ lines.push(`abg ${pairArg}codex resume ${targets.codexThreadId}`);
3746
+ } else {
3747
+ lines.push(`# no verified Codex thread for pair "${targets.pairName}" (start one: abg ${pairArg}codex --new)`);
3748
+ }
3749
+ console.log(lines.join(`
3750
+ `));
3751
+ console.error("");
3752
+ console.error("Tip: `abg resume claude` / `abg resume codex` runs these directly.");
3753
+ console.error("(max-permission flags are applied by default; opt out with --safe or AGENTBRIDGE_SAFE=1)");
3754
+ if (!targets.claudeSessionId && !targets.codexThreadId) {
3755
+ process.exit(1);
3756
+ }
3757
+ }
3758
+ var init_resume = __esm(() => {
3759
+ init_claude_session();
3760
+ init_pair_resolver();
3761
+ init_thread_state();
3762
+ });
3763
+
3580
3764
  // src/cli/kill.ts
3581
3765
  var exports_kill = {};
3582
3766
  __export(exports_kill, {
@@ -3585,7 +3769,7 @@ __export(exports_kill, {
3585
3769
  formatKillReport: () => formatKillReport
3586
3770
  });
3587
3771
  import { readFileSync as readFileSync10, unlinkSync as unlinkSync5 } from "fs";
3588
- import { join as join12 } from "path";
3772
+ import { join as join13 } from "path";
3589
3773
  async function runKill(args = []) {
3590
3774
  const argError = validateKillArgs(args);
3591
3775
  if (argError === "help") {
@@ -3637,7 +3821,7 @@ async function runKill(args = []) {
3637
3821
  for (const dirName of listPairDirsSafe(base)) {
3638
3822
  if (registeredIds.has(dirName))
3639
3823
  continue;
3640
- const stateDir = new StateDirResolver(join12(base, "pairs", dirName));
3824
+ const stateDir = new StateDirResolver(join13(base, "pairs", dirName));
3641
3825
  results.push(await stopStateDir(`${dirName} (unregistered)`, stateDir, portsFromStateDir(stateDir)));
3642
3826
  }
3643
3827
  const legacy = detectLegacyRootDaemon(base);
@@ -3722,7 +3906,7 @@ No arguments stop this directory's registered pairs and any legacy-root daemon.
3722
3906
  }
3723
3907
  async function stopPairEntry(base, pair) {
3724
3908
  const ports = portsForEntry(pair);
3725
- const stateDir = new StateDirResolver(join12(base, "pairs", pair.pairId));
3909
+ const stateDir = new StateDirResolver(join13(base, "pairs", pair.pairId));
3726
3910
  return stopStateDir(pair.pairId, stateDir, ports);
3727
3911
  }
3728
3912
  function listPairDirsSafe(base) {
@@ -3911,7 +4095,7 @@ var exports_pairs = {};
3911
4095
  __export(exports_pairs, {
3912
4096
  runPairs: () => runPairs
3913
4097
  });
3914
- import { join as join13 } from "path";
4098
+ import { join as join14 } from "path";
3915
4099
  async function runPairs(args = []) {
3916
4100
  const [command, ...rest] = args;
3917
4101
  if (command === "rm") {
@@ -4077,7 +4261,7 @@ async function collectRows() {
4077
4261
  }
4078
4262
  async function rowForPair(base, pair) {
4079
4263
  const ports = portsForEntry(pair);
4080
- const stateDir = new StateDirResolver(join13(base, "pairs", pair.pairId));
4264
+ const stateDir = new StateDirResolver(join14(base, "pairs", pair.pairId));
4081
4265
  const lifecycle = new DaemonLifecycle({
4082
4266
  stateDir,
4083
4267
  controlPort: ports.controlPort,
@@ -4188,7 +4372,7 @@ import {
4188
4372
  mkdirSync as mkdirSync7,
4189
4373
  readFileSync as readFileSync11
4190
4374
  } from "fs";
4191
- import { dirname as dirname5, join as join14 } from "path";
4375
+ import { dirname as dirname5, join as join15 } from "path";
4192
4376
  function isKickoffText(text) {
4193
4377
  if (!text)
4194
4378
  return false;
@@ -4219,7 +4403,7 @@ function extractFirstRealUserMessage(rolloutPath) {
4219
4403
  }
4220
4404
  function scanResumePollution(options = {}) {
4221
4405
  const codexHome2 = options.codexHome ?? codexHome();
4222
- const dbPath = options.dbPath ?? join14(codexHome2, "state_5.sqlite");
4406
+ const dbPath = options.dbPath ?? join15(codexHome2, "state_5.sqlite");
4223
4407
  if (!existsSync11(dbPath)) {
4224
4408
  return { codexHome: codexHome2, dbPath, scanned: 0, candidates: [], applied: 0, renamed: 0, deleted: 0 };
4225
4409
  }
@@ -4313,12 +4497,12 @@ function scanResumePollution(options = {}) {
4313
4497
  }
4314
4498
  function backupCodexStateFiles(dbPath, now = new Date().toISOString()) {
4315
4499
  const safeStamp = now.replace(/[:.]/g, "-");
4316
- const base = join14(dirname5(dbPath), "agentbridge-backups", `resume-pollution-${safeStamp}`);
4500
+ const base = join15(dirname5(dbPath), "agentbridge-backups", `resume-pollution-${safeStamp}`);
4317
4501
  mkdirSync7(base, { recursive: true });
4318
4502
  for (const path of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
4319
4503
  if (!existsSync11(path))
4320
4504
  continue;
4321
- const target = join14(base, path.split("/").pop());
4505
+ const target = join15(base, path.split("/").pop());
4322
4506
  mkdirSync7(dirname5(target), { recursive: true });
4323
4507
  copyFileSync(path, target);
4324
4508
  }
@@ -4366,9 +4550,9 @@ __export(exports_doctor, {
4366
4550
  runDoctor: () => runDoctor,
4367
4551
  formatDoctorReport: () => formatDoctorReport
4368
4552
  });
4369
- import { existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync4, realpathSync as realpathSync3, statSync as statSync5 } from "fs";
4370
- import { homedir as homedir5 } from "os";
4371
- import { join as join15 } from "path";
4553
+ import { existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync5, realpathSync as realpathSync3, statSync as statSync6 } from "fs";
4554
+ import { homedir as homedir6 } from "os";
4555
+ import { join as join16 } from "path";
4372
4556
  async function runDoctor(args = []) {
4373
4557
  if (args[0] === "resume-pollution") {
4374
4558
  runResumePollution(args.slice(1));
@@ -4557,15 +4741,15 @@ function artifactAlignmentCheck() {
4557
4741
  stamps.push({ label: "global-cli", commit });
4558
4742
  } catch {}
4559
4743
  }
4560
- const cacheRoot = join15(homedir5(), ".claude", "plugins", "cache", "agentbridge", "agentbridge");
4744
+ const cacheRoot = join16(homedir6(), ".claude", "plugins", "cache", "agentbridge", "agentbridge");
4561
4745
  try {
4562
- for (const version of readdirSync4(cacheRoot)) {
4563
- const commit = extractBundleCommit(join15(cacheRoot, version, "server", "daemon.js"));
4746
+ for (const version of readdirSync5(cacheRoot)) {
4747
+ const commit = extractBundleCommit(join16(cacheRoot, version, "server", "daemon.js"));
4564
4748
  if (commit)
4565
4749
  stamps.push({ label: `plugin-cache@${version}`, commit });
4566
4750
  }
4567
4751
  } catch {}
4568
- const repoBundle = join15(process.cwd(), "plugins", "agentbridge", "server", "daemon.js");
4752
+ const repoBundle = join16(process.cwd(), "plugins", "agentbridge", "server", "daemon.js");
4569
4753
  if (existsSync12(repoBundle)) {
4570
4754
  const commit = extractBundleCommit(repoBundle);
4571
4755
  if (commit)
@@ -4607,7 +4791,7 @@ function logCheck(name, path) {
4607
4791
  hint: "\u65E5\u5FD7\u4F1A\u5728\u76F8\u5E94\u8FDB\u7A0B\u9996\u6B21\u542F\u52A8\u65F6\u521B\u5EFA\uFF1B\u8FDB\u7A0B\u4ECE\u672A\u542F\u52A8\u8FC7\u65F6\u8FD9\u662F\u6B63\u5E38\u7684\u3002"
4608
4792
  };
4609
4793
  }
4610
- const stat = statSync5(path);
4794
+ const stat = statSync6(path);
4611
4795
  if (stat.size > LARGE_LOG_WARN_BYTES) {
4612
4796
  return {
4613
4797
  name,
@@ -4844,6 +5028,10 @@ async function main(command, restArgs) {
4844
5028
  const { runCodex: runCodex2 } = await Promise.resolve().then(() => (init_codex(), exports_codex));
4845
5029
  await runCodex2(restArgs);
4846
5030
  break;
5031
+ case "resume":
5032
+ const { runResume: runResume2 } = await Promise.resolve().then(() => (init_resume(), exports_resume));
5033
+ await runResume2(restArgs);
5034
+ break;
4847
5035
  case "kill":
4848
5036
  const { runKill: runKill2 } = await Promise.resolve().then(() => (init_kill(), exports_kill));
4849
5037
  await runKill2(restArgs);
@@ -4889,6 +5077,10 @@ Commands:
4889
5077
  claude [args...] Start Claude Code with push channel enabled
4890
5078
  codex [args...] Start Codex TUI connected to AgentBridge daemon
4891
5079
  (bare command auto-resumes the last thread; --new starts fresh)
5080
+ resume [claude|codex]
5081
+ No target: print resume commands for this directory's last
5082
+ Claude session + this pair's current Codex thread.
5083
+ With target: resume that side directly.
4892
5084
  pairs [rm <name|id> | prune [--dry-run]]
4893
5085
  List pairs; remove one (rm), or delete orphan state dirs (prune)
4894
5086
  doctor [--json] Diagnose env, daemon, build drift, logs, and current thread
@@ -4898,10 +5090,17 @@ Commands:
4898
5090
  Stop this directory's pairs (default), every pair (all/--all), or one (--pair)
4899
5091
 
4900
5092
  Options:
4901
- --pair <name> Run claude/codex/kill in a named pair. The name is scoped to
5093
+ --pair <name> Run claude/codex/resume/kill/doctor/budget in a named pair. The name is scoped to
4902
5094
  the current directory, so the same name in another directory
4903
5095
  is a separate pair. Goes BEFORE the command. Without it, the
4904
5096
  pair name defaults to "main" for the current directory.
5097
+ --safe Disable the max-permission defaults for this launch.
5098
+ Goes AFTER the command (abg claude --safe); also auto-
5099
+ suppressed when you pass any explicit permission flag
5100
+ (-a/--sandbox for codex, --permission-mode for claude).
5101
+ (abg claude runs with --dangerously-skip-permissions and
5102
+ abg codex with --yolo by default; AGENTBRIDGE_SAFE=1 also
5103
+ disables both.)
4905
5104
  --help, -h Show this help message
4906
5105
  --version, -v Show version
4907
5106
 
@@ -4915,6 +5114,10 @@ Examples:
4915
5114
  abg init # First-time setup
4916
5115
  abg claude # Start the "main" pair for this directory
4917
5116
  abg codex # Connect Codex to this directory's "main" pair
5117
+ abg resume # Print resume commands for both sides
5118
+ abg resume claude # Resume the last Claude Code session here
5119
+ abg resume codex # Resume this pair's current Codex thread
5120
+ abg claude --safe # One launch without the max-permission default
4918
5121
  abg --pair work claude # Start a named pair "work" (this directory)
4919
5122
  abg --pair work codex # Connect Codex to the "work" pair
4920
5123
  abg --pair review claude # A second, parallel pair
@@ -4940,9 +5143,9 @@ function printVersion() {
4940
5143
  }
4941
5144
  var MARKETPLACE_NAME = "agentbridge", PLUGIN_NAME = "agentbridge", REFRESH_COMMANDS, NOTIFY_COMMANDS, PAIR_AWARE_COMMANDS;
4942
5145
  var init_cli = __esm(() => {
4943
- REFRESH_COMMANDS = new Set(["claude", "codex"]);
4944
- NOTIFY_COMMANDS = new Set(["claude", "codex", "init", "dev"]);
4945
- PAIR_AWARE_COMMANDS = new Set(["claude", "codex", "kill", "doctor", "budget"]);
5146
+ REFRESH_COMMANDS = new Set(["claude", "codex", "resume"]);
5147
+ NOTIFY_COMMANDS = new Set(["claude", "codex", "init", "dev", "resume"]);
5148
+ PAIR_AWARE_COMMANDS = new Set(["claude", "codex", "kill", "doctor", "budget", "resume"]);
4946
5149
  if (import.meta.main) {
4947
5150
  const { command, restArgs } = parseTopLevel(process.argv.slice(2));
4948
5151
  main(command, restArgs).catch((err) => {
package/dist/daemon.js CHANGED
@@ -17,8 +17,8 @@ function defineNumber(value, fallback) {
17
17
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
18
18
  }
19
19
  var BUILD_INFO = Object.freeze({
20
- version: defineString("0.1.9", "0.0.0-source"),
21
- commit: defineString("10dfd58", "source"),
20
+ version: defineString("0.1.10", "0.0.0-source"),
21
+ commit: defineString("51a44cb", "source"),
22
22
  bundle: defineBundle("dist"),
23
23
  contractVersion: defineNumber(1, CONTRACT_VERSION)
24
24
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raysonmeng/agentbridge",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Bridge between Claude Code and Codex — bidirectional agent communication via MCP Channel + JSON-RPC",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.3.11",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentbridge",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Bridge Claude Code and Codex with a shared daemon, push channel delivery, and bidirectional reply tooling.",
5
5
  "author": {
6
6
  "name": "AgentBridge Contributors",
@@ -14227,8 +14227,8 @@ function defineNumber(value, fallback) {
14227
14227
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
14228
14228
  }
14229
14229
  var BUILD_INFO = Object.freeze({
14230
- version: defineString("0.1.9", "0.0.0-source"),
14231
- commit: defineString("10dfd58", "source"),
14230
+ version: defineString("0.1.10", "0.0.0-source"),
14231
+ commit: defineString("51a44cb", "source"),
14232
14232
  bundle: defineBundle("plugin"),
14233
14233
  contractVersion: defineNumber(1, CONTRACT_VERSION)
14234
14234
  });
@@ -17,8 +17,8 @@ function defineNumber(value, fallback) {
17
17
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
18
18
  }
19
19
  var BUILD_INFO = Object.freeze({
20
- version: defineString("0.1.9", "0.0.0-source"),
21
- commit: defineString("10dfd58", "source"),
20
+ version: defineString("0.1.10", "0.0.0-source"),
21
+ commit: defineString("51a44cb", "source"),
22
22
  bundle: defineBundle("plugin"),
23
23
  contractVersion: defineNumber(1, CONTRACT_VERSION)
24
24
  });