approval-box 0.1.20 → 0.1.22

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.
@@ -28,7 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
28
  SOFTWARE.
29
29
  ```
30
30
 
31
- ## aiterm-steer-delivery@0.3.0 (MIT)
31
+ ## aiterm-steer-delivery@0.4.4 (MIT)
32
32
 
33
33
  ```
34
34
  MIT License
@@ -2,7 +2,7 @@ import { createRequire as __kbCreateRequire } from 'node:module'; const require
2
2
  import {
3
3
  PROFILE,
4
4
  runClaudeHookMain
5
- } from "./chunks/chunk-OVDAILMZ.mjs";
5
+ } from "./chunks/chunk-SN7I52NL.mjs";
6
6
 
7
7
  // src/hooks/claude.ts
8
8
  await runClaudeHookMain(PROFILE);
@@ -5,7 +5,7 @@ import {
5
5
  handleCursorChannelHook,
6
6
  handleCursorHook,
7
7
  withoutBom
8
- } from "./chunks/chunk-OVDAILMZ.mjs";
8
+ } from "./chunks/chunk-SN7I52NL.mjs";
9
9
 
10
10
  // src/cursor-binding.ts
11
11
  async function bindCursorResult(profile, raw) {
@@ -2,7 +2,7 @@ import { createRequire as __kbCreateRequire } from 'node:module'; const require
2
2
  import {
3
3
  PROFILE,
4
4
  runChannelReceiveMain
5
- } from "./chunks/chunk-OVDAILMZ.mjs";
5
+ } from "./chunks/chunk-SN7I52NL.mjs";
6
6
 
7
7
  // src/hooks/receive.ts
8
8
  await runChannelReceiveMain(PROFILE);
@@ -20014,7 +20014,7 @@ function resolveCodexExecutable() {
20014
20014
  const fallback = path4.join(home2, ".local", "bin", "codex");
20015
20015
  if (isUsableAgentExecutableFile(fallback))
20016
20016
  return resolveWindowsCodexShim(fallback);
20017
- const w = spawnSync2(isWin ? "where" : "which", ["codex"], { encoding: "utf8", timeout: 5e3 });
20017
+ const w = spawnSync2(isWin ? "where" : "which", ["codex"], { encoding: "utf8", timeout: 5e3, windowsHide: true });
20018
20018
  if (w.status === 0 && (w.stdout ?? "").trim()) {
20019
20019
  const found = w.stdout.trim().split(/\r?\n/).filter(Boolean);
20020
20020
  const ordered = isWin ? [...found.filter((p) => /\.(?:exe|com|cmd|bat)$/i.test(p)), ...found.filter((p) => !/\.(?:exe|com|cmd|bat)$/i.test(p))] : found;
@@ -20025,8 +20025,17 @@ function resolveCodexExecutable() {
20025
20025
  }
20026
20026
  return null;
20027
20027
  }
20028
+ function codexSpawnEnv(env = process.env, node2 = process.execPath, platform = process.platform) {
20029
+ const windows = platform === "win32";
20030
+ const key = Object.keys(env).find((name) => windows ? name.toLowerCase() === "path" : name === "PATH") ?? "PATH";
20031
+ const delimiter2 = windows ? ";" : ":";
20032
+ const directory2 = (windows ? path4.win32 : path4.posix).dirname(node2);
20033
+ const current = env[key] ?? "";
20034
+ const listed = current.split(delimiter2).some((part) => windows ? part.toLowerCase() === directory2.toLowerCase() : part === directory2);
20035
+ return listed ? env : { ...env, [key]: current ? `${directory2}${delimiter2}${current}` : directory2 };
20036
+ }
20028
20037
  function command(executable, args) {
20029
- const result = spawnSync2(executable, args, { encoding: "utf8", timeout: 15e3 });
20038
+ const result = spawnSync2(executable, args, { encoding: "utf8", timeout: 15e3, env: codexSpawnEnv(), windowsHide: true });
20030
20039
  if (result.error || result.status !== 0)
20031
20040
  throw new SetupError("codex_steer_setup_failed", `${path4.basename(executable)}\u3092\u5B9F\u884C\u3067\u304D\u307E\u305B\u3093`);
20032
20041
  return result.stdout.trim();
@@ -20039,13 +20048,13 @@ function desktopBundledCodex(app) {
20039
20048
  return layouts.find((file2) => fs2.existsSync(file2)) ?? null;
20040
20049
  }
20041
20050
  function findDesktopBinary() {
20042
- const search = spawnSync2("/usr/bin/mdfind", ["kMDItemCFBundleIdentifier == 'com.openai.codex'"], { encoding: "utf8", timeout: 1e4 });
20051
+ const search = spawnSync2("/usr/bin/mdfind", ["kMDItemCFBundleIdentifier == 'com.openai.codex'"], { encoding: "utf8", timeout: 1e4, windowsHide: true });
20043
20052
  const candidates = [.../* @__PURE__ */ new Set(["/Applications/Codex.app", "/Applications/ChatGPT.app", ...search.status === 0 ? search.stdout.trim().split("\n") : []])];
20044
20053
  const found = candidates.flatMap((app) => {
20045
20054
  const binary2 = app ? desktopBundledCodex(app) : null;
20046
20055
  if (!binary2)
20047
20056
  return [];
20048
- const result = spawnSync2("/usr/libexec/PlistBuddy", ["-c", "Print:CFBundleIdentifier", path4.join(app, "Contents", "Info.plist")], { encoding: "utf8", timeout: 5e3 });
20057
+ const result = spawnSync2("/usr/libexec/PlistBuddy", ["-c", "Print:CFBundleIdentifier", path4.join(app, "Contents", "Info.plist")], { encoding: "utf8", timeout: 5e3, windowsHide: true });
20049
20058
  return result.status === 0 && result.stdout.trim() === "com.openai.codex" ? [binary2] : [];
20050
20059
  });
20051
20060
  if (found.length !== 1)
@@ -20086,7 +20095,7 @@ Join-Path $packages[0].InstallLocation 'app/resources'`, () => new CodexDelivery
20086
20095
  return value;
20087
20096
  }
20088
20097
  function assertSteerVersion(executable, subject) {
20089
- const result = spawnSync2(executable, ["--version"], { encoding: "utf8", windowsHide: true, timeout: 15e3 });
20098
+ const result = spawnSync2(executable, ["--version"], { encoding: "utf8", windowsHide: true, timeout: 15e3, env: codexSpawnEnv() });
20090
20099
  if (result.error || result.status !== 0)
20091
20100
  throw new SetupError("codex_binary_unavailable", `${subject}\u3092\u8D77\u52D5\u3067\u304D\u307E\u305B\u3093`);
20092
20101
  const match = /codex-cli (\d+)\.(\d+)\.(\d+)/.exec(result.stdout ?? "");
@@ -20139,7 +20148,7 @@ async function currentCodexDesktopBinary(profile, config2, options = {}) {
20139
20148
  }
20140
20149
 
20141
20150
  // ../../node_modules/aiterm-steer-delivery/dist/codex-receiver.js
20142
- import { spawn } from "node:child_process";
20151
+ import { spawn as spawn2 } from "node:child_process";
20143
20152
  import { createInterface } from "node:readline";
20144
20153
  import * as path6 from "node:path";
20145
20154
 
@@ -20316,6 +20325,53 @@ function assertCodexHooksReady(profile, response, command2, file2) {
20316
20325
  }
20317
20326
  }
20318
20327
 
20328
+ // ../../node_modules/aiterm-steer-delivery/dist/codex-wake.js
20329
+ import * as fs4 from "node:fs";
20330
+ import { spawn, spawnSync as spawnSync4 } from "node:child_process";
20331
+ import { fileURLToPath } from "node:url";
20332
+ var TAIL_FIRST_BYTES = 256 * 1024;
20333
+ var TAIL_MAX_BYTES = 16 * 1024 * 1024;
20334
+ var RECORD_KEEP_MS = 3 * 24 * 60 * 60 * 1e3;
20335
+ function codexWakeWatchEnabled(platform = process.platform, env = process.env) {
20336
+ if (env.AITERM_STEER_CODEX_WAKE === "0")
20337
+ return false;
20338
+ return platform === "darwin" || platform === "win32";
20339
+ }
20340
+ var CODEX_WAKE_PAYLOAD_ENV = "AITERM_STEER_CODEX_WAKE_PAYLOAD";
20341
+ function serializeWakeProfile(profile) {
20342
+ const { state_root, config_root, channels: _channels, ...rest } = profile;
20343
+ return { ...rest, dispatch_tools: [...profile.dispatch_tools], state_root: state_root(), config_root: config_root() };
20344
+ }
20345
+ function startCodexWakeWatch(profile, parent, deliveryId, options = {}) {
20346
+ const env = options.env ?? process.env;
20347
+ if (!codexWakeWatchEnabled(options.platform, env))
20348
+ return false;
20349
+ try {
20350
+ const entry = options.entry ?? fileURLToPath(new URL("./codex-wake-main.js", import.meta.url));
20351
+ if (!fs4.existsSync(entry))
20352
+ return false;
20353
+ const payload = JSON.stringify({
20354
+ profile: serializeWakeProfile(profile),
20355
+ parent,
20356
+ delivery_id: deliveryId,
20357
+ ...options.delay_ms === void 0 ? {} : { delay_ms: options.delay_ms }
20358
+ });
20359
+ const child = spawn(process.execPath, [entry], { detached: true, stdio: "ignore", windowsHide: true, env: { ...env, [CODEX_WAKE_PAYLOAD_ENV]: payload } });
20360
+ child.on("error", () => {
20361
+ });
20362
+ child.unref();
20363
+ return true;
20364
+ } catch {
20365
+ return false;
20366
+ }
20367
+ }
20368
+ var payloadSchema = external_exports.object({
20369
+ profile: external_exports.object({ state_root: external_exports.string(), config_root: external_exports.string() }).passthrough(),
20370
+ parent: external_exports.object({ thread_id: external_exports.uuid(), codex_home: external_exports.string() }).strict(),
20371
+ delivery_id: external_exports.uuid(),
20372
+ delay_ms: external_exports.number().int().min(0).optional()
20373
+ }).strict();
20374
+
20319
20375
  // ../../node_modules/aiterm-steer-delivery/dist/codex-receiver.js
20320
20376
  function codexParentFromRequest(clientName, metadata) {
20321
20377
  if (clientName !== "codex-mcp-client")
@@ -20331,9 +20387,9 @@ async function withCodexReceiver(profile, parent, action, runtime = {}) {
20331
20387
  const executable = runtime.executable ?? (config2?.enabled ? await currentCodexDesktopBinary(profile, config2) : null) ?? resolveCodexExecutable();
20332
20388
  if (!executable)
20333
20389
  throw new CodexDeliveryError("CODEX_RECEIVER_UNAVAILABLE", "Codex\u306E\u5B9F\u884C\u30D5\u30A1\u30A4\u30EB\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093");
20334
- const child = spawn(executable, runtime.args ?? ["app-server", "--listen", "stdio://"], {
20390
+ const child = spawn2(executable, runtime.args ?? ["app-server", "--listen", "stdio://"], {
20335
20391
  stdio: ["pipe", "pipe", "ignore"],
20336
- env: { ...runtime.env ?? process.env, CODEX_HOME: parent.codex_home },
20392
+ env: { ...codexSpawnEnv(runtime.env ?? process.env), CODEX_HOME: parent.codex_home },
20337
20393
  windowsHide: true
20338
20394
  });
20339
20395
  const pending = /* @__PURE__ */ new Map();
@@ -20410,8 +20466,9 @@ async function submitCodexParentAnswer(profile, parent, deliveryId, text, runtim
20410
20466
  const hook = root && readCodexHookConfig(profile, root)?.enabled;
20411
20467
  if (hook)
20412
20468
  registerCodexHookInput(parent.codex_home, parent.thread_id, deliveryId, text, root);
20469
+ let accepted;
20413
20470
  try {
20414
- return await withCodexReceiver(profile, parent, async (request) => {
20471
+ accepted = await withCodexReceiver(profile, parent, async (request) => {
20415
20472
  const result = await request("thread/queue/add", {
20416
20473
  threadId: parent.thread_id,
20417
20474
  input: [{ type: "text", text, text_elements: [] }],
@@ -20426,10 +20483,13 @@ async function submitCodexParentAnswer(profile, parent, deliveryId, text, runtim
20426
20483
  if (hook)
20427
20484
  finishCodexHookSubmission(parent.codex_home, parent.thread_id, deliveryId, root);
20428
20485
  }
20486
+ if (runtime?.wake ?? !runtime)
20487
+ startCodexWakeWatch(profile, parent, deliveryId);
20488
+ return accepted;
20429
20489
  }
20430
20490
 
20431
20491
  // ../../node_modules/aiterm-steer-delivery/dist/claude-receiver.js
20432
- import * as fs4 from "node:fs";
20492
+ import * as fs5 from "node:fs";
20433
20493
  import * as path7 from "node:path";
20434
20494
  var requestId = external_exports.string().regex(/^[A-Za-z0-9_-]{1,160}$/);
20435
20495
  var invocationSchema = external_exports.object({
@@ -20454,7 +20514,7 @@ function directory(parent) {
20454
20514
  function readInvocation(profile, parent) {
20455
20515
  let value;
20456
20516
  try {
20457
- value = invocationSchema.parse(JSON.parse(fs4.readFileSync(path7.join(directory(parent), "request.json"), "utf8")));
20517
+ value = invocationSchema.parse(JSON.parse(fs5.readFileSync(path7.join(directory(parent), "request.json"), "utf8")));
20458
20518
  } catch {
20459
20519
  throw new ClaudeDeliveryError("CLAUDE_PARENT_HOOK_UNAVAILABLE", `\u89AA\u306Ehook\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093\u3002${profile.setup_command}\u3092\u5B9F\u884C\u3057\u3001Claude Code\u306Ehook\u3092\u6709\u52B9\u306B\u3057\u3066\u304F\u3060\u3055\u3044`);
20460
20520
  }
@@ -20464,7 +20524,7 @@ function readInvocation(profile, parent) {
20464
20524
  return value;
20465
20525
  }
20466
20526
  function assertOpen(parent) {
20467
- if (fs4.existsSync(path7.join(directory(parent), "closed.json"))) {
20527
+ if (fs5.existsSync(path7.join(directory(parent), "closed.json"))) {
20468
20528
  throw new ClaudeDeliveryError("CLAUDE_PARENT_SESSION_CLOSED", "\u4F9D\u983C\u5143\u306E\u4F1A\u8A71\u306F\u7D42\u4E86\u3057\u307E\u3057\u305F\u3002\u56DE\u7B54\u3092\u5225\u306E\u4F1A\u8A71\u3078\u9001\u3063\u3066\u3044\u307E\u305B\u3093");
20469
20529
  }
20470
20530
  }
@@ -20475,7 +20535,7 @@ function prepareClaudeHookRequest(input2, root) {
20475
20535
  if (!owner)
20476
20536
  throw new ClaudeDeliveryError("CLAUDE_PARENT_PROCESS_UNAVAILABLE", "hook\u3092\u8D77\u52D5\u3057\u305F\u89AAprocess\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093");
20477
20537
  const dir = path7.join(root, event.tool_use_id);
20478
- fs4.mkdirSync(dir, { recursive: true, mode: 448 });
20538
+ fs5.mkdirSync(dir, { recursive: true, mode: 448 });
20479
20539
  writeJson0600(path7.join(dir, "request.json"), {
20480
20540
  request_id: event.tool_use_id,
20481
20541
  session_id: event.session_id,
@@ -20493,7 +20553,7 @@ var REQUEST_FILES = /* @__PURE__ */ new Set(["request.json", "delivery.json", "a
20493
20553
  function sweepClaudeHookRequests(root, rows, now = Date.now()) {
20494
20554
  let entries;
20495
20555
  try {
20496
- entries = fs4.readdirSync(root, { withFileTypes: true });
20556
+ entries = fs5.readdirSync(root, { withFileTypes: true });
20497
20557
  } catch {
20498
20558
  return 0;
20499
20559
  }
@@ -20503,23 +20563,23 @@ function sweepClaudeHookRequests(root, rows, now = Date.now()) {
20503
20563
  continue;
20504
20564
  const dir = path7.join(root, entry.name);
20505
20565
  try {
20506
- if (now - fs4.statSync(dir).mtimeMs < STALE_REQUEST_MS)
20566
+ if (now - fs5.statSync(dir).mtimeMs < STALE_REQUEST_MS)
20507
20567
  continue;
20508
- const names = fs4.readdirSync(dir);
20568
+ const names = fs5.readdirSync(dir);
20509
20569
  if (!names.every((name) => REQUEST_FILES.has(name) || name.endsWith(".tmp")))
20510
20570
  continue;
20511
- if (names.some((name) => now - fs4.statSync(path7.join(dir, name)).mtimeMs < STALE_REQUEST_MS))
20571
+ if (names.some((name) => now - fs5.statSync(path7.join(dir, name)).mtimeMs < STALE_REQUEST_MS))
20512
20572
  continue;
20513
20573
  if (names.includes("delivery.json") && !names.includes("emitted.json")) {
20514
20574
  let invocation;
20515
20575
  try {
20516
- invocation = invocationSchema.parse(JSON.parse(fs4.readFileSync(path7.join(dir, "request.json"), "utf8")));
20576
+ invocation = invocationSchema.parse(JSON.parse(fs5.readFileSync(path7.join(dir, "request.json"), "utf8")));
20517
20577
  } catch {
20518
20578
  }
20519
20579
  if (invocation && rows.find((row) => row.pid === invocation.parent_pid)?.started_identity === invocation.parent_started_identity)
20520
20580
  continue;
20521
20581
  }
20522
- fs4.rmSync(dir, { recursive: true, force: true });
20582
+ fs5.rmSync(dir, { recursive: true, force: true });
20523
20583
  removed += 1;
20524
20584
  } catch {
20525
20585
  }
@@ -20534,7 +20594,7 @@ function claudeParentFromRequest(profile, clientName, metadata, root) {
20534
20594
  throw new ClaudeDeliveryError("CLAUDE_PARENT_ID_UNAVAILABLE", "MCP\u8981\u6C42\u306BtoolUseId\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u5BFE\u5FDC\u3059\u308BClaude Code\u3078\u66F4\u65B0\u3057\u3066\u304F\u3060\u3055\u3044");
20535
20595
  let invocation;
20536
20596
  try {
20537
- invocation = invocationSchema.parse(JSON.parse(fs4.readFileSync(path7.join(root, parsed.data, "request.json"), "utf8")));
20597
+ invocation = invocationSchema.parse(JSON.parse(fs5.readFileSync(path7.join(root, parsed.data, "request.json"), "utf8")));
20538
20598
  } catch {
20539
20599
  throw new ClaudeDeliveryError("CLAUDE_PARENT_HOOK_UNAVAILABLE", `\u89AA\u306EPreToolUse hook\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3002${profile.setup_command}\u3092\u5B9F\u884C\u3057\u3001hook\u3092\u6709\u52B9\u306B\u3057\u3066\u304F\u3060\u3055\u3044`);
20540
20600
  }
@@ -20550,15 +20610,15 @@ function verifyClaudeParent(profile, parent) {
20550
20610
  }
20551
20611
  function closeClaudeParentSession(input2, root) {
20552
20612
  const { session_id } = external_exports.object({ session_id: external_exports.uuid() }).parse(input2);
20553
- if (!fs4.existsSync(root))
20613
+ if (!fs5.existsSync(root))
20554
20614
  return;
20555
20615
  let failure2;
20556
- for (const entry of fs4.readdirSync(root, { withFileTypes: true })) {
20616
+ for (const entry of fs5.readdirSync(root, { withFileTypes: true })) {
20557
20617
  if (!entry.isDirectory())
20558
20618
  continue;
20559
20619
  let invocation;
20560
20620
  try {
20561
- invocation = invocationSchema.parse(JSON.parse(fs4.readFileSync(path7.join(root, entry.name, "request.json"), "utf8")));
20621
+ invocation = invocationSchema.parse(JSON.parse(fs5.readFileSync(path7.join(root, entry.name, "request.json"), "utf8")));
20562
20622
  } catch {
20563
20623
  continue;
20564
20624
  }
@@ -20585,24 +20645,24 @@ async function runClaudeResultHook(profile, input2, emit, root) {
20585
20645
  const invocation = readInvocation(profile, parent);
20586
20646
  const dir = directory(parent);
20587
20647
  const binding = path7.join(dir, "delivery.json");
20588
- if (!fs4.existsSync(binding)) {
20589
- fs4.rmSync(dir, { recursive: true });
20648
+ if (!fs5.existsSync(binding)) {
20649
+ fs5.rmSync(dir, { recursive: true });
20590
20650
  return 0;
20591
20651
  }
20592
- const deliveryId = external_exports.uuid().parse(JSON.parse(fs4.readFileSync(binding, "utf8")).delivery_id);
20652
+ const deliveryId = external_exports.uuid().parse(JSON.parse(fs5.readFileSync(binding, "utf8")).delivery_id);
20593
20653
  const identity = processIdentity(process.pid);
20594
20654
  if (!identity)
20595
20655
  throw new ClaudeDeliveryError("CLAUDE_PARENT_HOOK_UNAVAILABLE", "\u53D7\u4FE1hook\u306Eprocess\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093");
20596
20656
  writeJson0600(path7.join(dir, "hook.json"), { pid: process.pid, started_identity: identity });
20597
20657
  try {
20598
20658
  const answer = await waitForFileState(dir, () => {
20599
- if (fs4.existsSync(path7.join(dir, "closed.json")))
20659
+ if (fs5.existsSync(path7.join(dir, "closed.json")))
20600
20660
  return null;
20601
20661
  assertParentAlive(invocation);
20602
20662
  const file2 = path7.join(dir, "answer.json");
20603
- if (!fs4.existsSync(file2))
20663
+ if (!fs5.existsSync(file2))
20604
20664
  return void 0;
20605
- const value = external_exports.object({ delivery_id: external_exports.uuid(), text: external_exports.string() }).strict().parse(JSON.parse(fs4.readFileSync(file2, "utf8")));
20665
+ const value = external_exports.object({ delivery_id: external_exports.uuid(), text: external_exports.string() }).strict().parse(JSON.parse(fs5.readFileSync(file2, "utf8")));
20606
20666
  if (value.delivery_id !== deliveryId)
20607
20667
  throw new ClaudeDeliveryError("CLAUDE_PARENT_DELIVERY_MISMATCH", "\u4FDD\u5B58\u3055\u308C\u305F\u56DE\u7B54\u306E\u914D\u9001ID\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
20608
20668
  return value;
@@ -20615,18 +20675,18 @@ async function runClaudeResultHook(profile, input2, emit, root) {
20615
20675
  writeJson0600(path7.join(dir, "emitted.json"), { delivery_id: deliveryId });
20616
20676
  return 2;
20617
20677
  } catch (error62) {
20618
- writeJson0600(path7.join(dir, "failed.json"), { outcome_unknown: fs4.existsSync(path7.join(dir, "sending.json")) });
20678
+ writeJson0600(path7.join(dir, "failed.json"), { outcome_unknown: fs5.existsSync(path7.join(dir, "sending.json")) });
20619
20679
  throw error62;
20620
20680
  }
20621
20681
  }
20622
20682
 
20623
20683
  // ../../node_modules/aiterm-steer-delivery/dist/cursor-receiver.js
20624
- import * as fs5 from "node:fs";
20684
+ import * as fs6 from "node:fs";
20625
20685
  import { homedir as homedir3 } from "node:os";
20626
20686
  import * as path8 from "node:path";
20627
20687
 
20628
20688
  // ../../node_modules/aiterm-steer-delivery/dist/hook-setup.js
20629
- import { copyFileSync, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync4, realpathSync as realpathSync3, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
20689
+ import { copyFileSync, existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync5, realpathSync as realpathSync3, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
20630
20690
  import { dirname as dirname3 } from "node:path";
20631
20691
  import { randomUUID as randomUUID2 } from "node:crypto";
20632
20692
  import { isDeepStrictEqual } from "node:util";
@@ -20652,16 +20712,22 @@ function claudeParentHookEntries(profile, hook, platform = process.platform) {
20652
20712
  PreToolUse: [{ matcher, hooks: [{ ...command2, timeout: 15 }] }],
20653
20713
  PostToolUse: [{ matcher, hooks: [{ ...command2, asyncRewake: true, timeout: 86400 }] }],
20654
20714
  // 長く続く受信(channel)では、turnが終わるたびに待機を張り直す。
20655
- ...profile.channels ? { Stop: [{ hooks: [{ ...command2, asyncRewake: true, timeout: 86400 }] }] } : {},
20715
+ // 会話の始まり(SessionStart)でも張る。Claude Codeが起動し直して同じ会話を再開した時、番が1つも回らないうちから、
20716
+ // 前のprocessが開いたchannelの本文を受け取れる。開いているchannelの無い会話では、何もせず終わる。
20717
+ ...profile.channels ? {
20718
+ Stop: [{ hooks: [{ ...command2, asyncRewake: true, timeout: 86400 }] }],
20719
+ SessionStart: [{ hooks: [{ ...command2, asyncRewake: true, timeout: 86400 }] }]
20720
+ } : {},
20656
20721
  SessionEnd: [{ hooks: [{ ...command2, timeout: 15 }] }]
20657
20722
  };
20658
20723
  }
20724
+ var OPTIONAL_CLAUDE_EVENTS = /* @__PURE__ */ new Set(["SessionStart"]);
20659
20725
  function mergeClaudeParentHooks(profile, file2, hook) {
20660
- const target2 = existsSync5(file2) ? realpathSync3(file2) : file2;
20726
+ const target2 = existsSync6(file2) ? realpathSync3(file2) : file2;
20661
20727
  let current = {};
20662
- if (existsSync5(target2)) {
20728
+ if (existsSync6(target2)) {
20663
20729
  try {
20664
- current = JSON.parse(readFileSync4(target2, "utf8"));
20730
+ current = JSON.parse(readFileSync5(target2, "utf8"));
20665
20731
  } catch {
20666
20732
  throw new SetupError("config_invalid", "Claude\u306Ehook\u8A2D\u5B9AJSON\u3092\u8AAD\u3081\u307E\u305B\u3093");
20667
20733
  }
@@ -20694,14 +20760,14 @@ function mergeClaudeParentHooks(profile, file2, hook) {
20694
20760
  try {
20695
20761
  writeFileSync3(temporary, `${JSON.stringify(next, null, 2)}
20696
20762
  `, { mode: 384, flag: "wx" });
20697
- if (existsSync5(target2))
20763
+ if (existsSync6(target2))
20698
20764
  copyFileSync(target2, `${target2}${profile.backup_suffix}`);
20699
20765
  renameSync2(temporary, target2);
20700
20766
  } finally {
20701
- if (existsSync5(temporary))
20767
+ if (existsSync6(temporary))
20702
20768
  unlinkSync2(temporary);
20703
20769
  }
20704
- if (!isDeepStrictEqual(JSON.parse(readFileSync4(target2, "utf8")), next)) {
20770
+ if (!isDeepStrictEqual(JSON.parse(readFileSync5(target2, "utf8")), next)) {
20705
20771
  throw new SetupError("config_readback_failed", "Claude\u306Ehook\u767B\u9332\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
20706
20772
  }
20707
20773
  return "configured";
@@ -20720,20 +20786,20 @@ function ownedClaudeParentHooks(profile, document, event) {
20720
20786
  return hooks.flatMap((group) => record2(group) && Array.isArray(group.hooks) ? group.hooks : []).filter((entry) => isClaudeParentHook(profile, entry));
20721
20787
  }
20722
20788
  function claudeParentHooksRegistered(profile, document) {
20723
- return Object.keys(claudeParentHookEntries(profile, { command: "", script: profile.hooks.claude })).every((event) => ownedClaudeParentHooks(profile, document, event).length > 0);
20789
+ return Object.keys(claudeParentHookEntries(profile, { command: "", script: profile.hooks.claude })).filter((event) => !OPTIONAL_CLAUDE_EVENTS.has(event)).every((event) => ownedClaudeParentHooks(profile, document, event).length > 0);
20724
20790
  }
20725
20791
  function removeClaudeParentHooks(profile, file2) {
20726
- if (!existsSync5(file2))
20792
+ if (!existsSync6(file2))
20727
20793
  return "unchanged";
20728
20794
  const target2 = realpathSync3(file2);
20729
- const current = JSON.parse(readFileSync4(target2, "utf8"));
20795
+ const current = JSON.parse(readFileSync5(target2, "utf8"));
20730
20796
  if (!record2(current) || current.hooks !== void 0 && !record2(current.hooks)) {
20731
20797
  throw new SetupError("config_invalid", "Claude\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
20732
20798
  }
20733
20799
  if (current.hooks === void 0)
20734
20800
  return "unchanged";
20735
20801
  const hooks = { ...current.hooks };
20736
- for (const event of ["PreToolUse", "PostToolUse", "Stop", "SessionEnd"]) {
20802
+ for (const event of ["PreToolUse", "PostToolUse", "Stop", "SessionStart", "SessionEnd"]) {
20737
20803
  if (hooks[event] === void 0)
20738
20804
  continue;
20739
20805
  if (!Array.isArray(hooks[event]))
@@ -20754,10 +20820,10 @@ function removeClaudeParentHooks(profile, file2) {
20754
20820
  copyFileSync(target2, `${target2}${profile.backup_suffix}`);
20755
20821
  renameSync2(temporary, target2);
20756
20822
  } finally {
20757
- if (existsSync5(temporary))
20823
+ if (existsSync6(temporary))
20758
20824
  unlinkSync2(temporary);
20759
20825
  }
20760
- if (!isDeepStrictEqual(JSON.parse(readFileSync4(target2, "utf8")), next))
20826
+ if (!isDeepStrictEqual(JSON.parse(readFileSync5(target2, "utf8")), next))
20761
20827
  throw new SetupError("config_readback_failed", "Claude hook\u89E3\u9664\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
20762
20828
  return "removed";
20763
20829
  }
@@ -20773,11 +20839,11 @@ function ownsCursorParentHook(profile, hook) {
20773
20839
  return record2(hook) && typeof hook.command === "string" && commandNamesFile(hook.command, profile.hooks.cursor);
20774
20840
  }
20775
20841
  function mergeCursorParentHooks(profile, file2, hook) {
20776
- const target2 = existsSync5(file2) ? realpathSync3(file2) : file2;
20842
+ const target2 = existsSync6(file2) ? realpathSync3(file2) : file2;
20777
20843
  let current = { version: 1, hooks: {} };
20778
- if (existsSync5(target2)) {
20844
+ if (existsSync6(target2)) {
20779
20845
  try {
20780
- current = JSON.parse(readFileSync4(target2, "utf8"));
20846
+ current = JSON.parse(readFileSync5(target2, "utf8"));
20781
20847
  } catch {
20782
20848
  throw new SetupError("config_invalid", "Cursor\u306Ehook\u8A2D\u5B9AJSON\u3092\u8AAD\u3081\u307E\u305B\u3093");
20783
20849
  }
@@ -20814,23 +20880,23 @@ function mergeCursorParentHooks(profile, file2, hook) {
20814
20880
  try {
20815
20881
  writeFileSync3(temporary, `${JSON.stringify(next, null, 2)}
20816
20882
  `, { mode: 384, flag: "wx" });
20817
- if (existsSync5(target2))
20883
+ if (existsSync6(target2))
20818
20884
  copyFileSync(target2, `${target2}${profile.backup_suffix}`);
20819
20885
  renameSync2(temporary, target2);
20820
20886
  } finally {
20821
- if (existsSync5(temporary))
20887
+ if (existsSync6(temporary))
20822
20888
  unlinkSync2(temporary);
20823
20889
  }
20824
- if (!isDeepStrictEqual(JSON.parse(readFileSync4(target2, "utf8")), next)) {
20890
+ if (!isDeepStrictEqual(JSON.parse(readFileSync5(target2, "utf8")), next)) {
20825
20891
  throw new SetupError("config_readback_failed", "Cursor\u306Ehook\u767B\u9332\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
20826
20892
  }
20827
20893
  return "configured";
20828
20894
  }
20829
20895
  function removeCursorParentHooks(profile, file2) {
20830
- if (!existsSync5(file2))
20896
+ if (!existsSync6(file2))
20831
20897
  return "unchanged";
20832
20898
  const target2 = realpathSync3(file2);
20833
- const current = JSON.parse(readFileSync4(target2, "utf8"));
20899
+ const current = JSON.parse(readFileSync5(target2, "utf8"));
20834
20900
  if (!record2(current) || current.hooks !== void 0 && !record2(current.hooks)) {
20835
20901
  throw new SetupError("config_invalid", "Cursor\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
20836
20902
  }
@@ -20858,10 +20924,10 @@ function removeCursorParentHooks(profile, file2) {
20858
20924
  copyFileSync(target2, `${target2}${profile.backup_suffix}`);
20859
20925
  renameSync2(temporary, target2);
20860
20926
  } finally {
20861
- if (existsSync5(temporary))
20927
+ if (existsSync6(temporary))
20862
20928
  unlinkSync2(temporary);
20863
20929
  }
20864
- if (!isDeepStrictEqual(JSON.parse(readFileSync4(target2, "utf8")), next)) {
20930
+ if (!isDeepStrictEqual(JSON.parse(readFileSync5(target2, "utf8")), next)) {
20865
20931
  throw new SetupError("config_readback_failed", "Cursor hook\u89E3\u9664\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
20866
20932
  }
20867
20933
  return "removed";
@@ -20902,7 +20968,7 @@ function verifyCursorParent(profile, parent, hooksFile = cursorHooksFile()) {
20902
20968
  cursorParentSchema.parse(parent);
20903
20969
  let document;
20904
20970
  try {
20905
- document = JSON.parse(fs5.readFileSync(hooksFile, "utf8"));
20971
+ document = JSON.parse(fs6.readFileSync(hooksFile, "utf8"));
20906
20972
  } catch {
20907
20973
  throw new CursorDeliveryError("CURSOR_PARENT_HOOK_UNAVAILABLE", `Cursor\u306Ehook\u304C\u767B\u9332\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002${profile.setup_command}\u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044`);
20908
20974
  }
@@ -20968,7 +21034,7 @@ function toolBaseName(profile, name) {
20968
21034
  }
20969
21035
  function claim2(dir, channel) {
20970
21036
  try {
20971
- fs5.writeFileSync(path8.join(dir, "claim.json"), JSON.stringify({ channel, at: (/* @__PURE__ */ new Date()).toISOString() }), { flag: "wx", mode: 384 });
21037
+ fs6.writeFileSync(path8.join(dir, "claim.json"), JSON.stringify({ channel, at: (/* @__PURE__ */ new Date()).toISOString() }), { flag: "wx", mode: 384 });
20972
21038
  return true;
20973
21039
  } catch (error62) {
20974
21040
  if (error62.code === "EEXIST")
@@ -20978,30 +21044,30 @@ function claim2(dir, channel) {
20978
21044
  }
20979
21045
  function bind(hookRoot, deliveryId, conv) {
20980
21046
  const dir = path8.join(hookRoot, "deliveries", deliveryId);
20981
- if (!fs5.existsSync(dir))
21047
+ if (!fs6.existsSync(dir))
20982
21048
  return;
20983
21049
  const bindFile = path8.join(dir, "bind.json");
20984
- if (!fs5.existsSync(bindFile)) {
21050
+ if (!fs6.existsSync(bindFile)) {
20985
21051
  writeJson0600(bindFile, { conversation_id: conv, created_at: (/* @__PURE__ */ new Date()).toISOString() });
20986
21052
  }
20987
21053
  const indexDir = path8.join(hookRoot, "conversations", conv);
20988
- fs5.mkdirSync(indexDir, { recursive: true, mode: 448 });
21054
+ fs6.mkdirSync(indexDir, { recursive: true, mode: 448 });
20989
21055
  const index = path8.join(indexDir, deliveryId);
20990
- if (!fs5.existsSync(index))
20991
- fs5.writeFileSync(index, "", { mode: 384 });
21056
+ if (!fs6.existsSync(index))
21057
+ fs6.writeFileSync(index, "", { mode: 384 });
20992
21058
  }
20993
21059
  function inject(hookRoot, conv) {
20994
21060
  const indexDir = path8.join(hookRoot, "conversations", conv);
20995
- if (!fs5.existsSync(indexDir))
21061
+ if (!fs6.existsSync(indexDir))
20996
21062
  return [];
20997
- const pending = fs5.readdirSync(indexDir).flatMap((deliveryId) => {
21063
+ const pending = fs6.readdirSync(indexDir).flatMap((deliveryId) => {
20998
21064
  const dir = path8.join(hookRoot, "deliveries", deliveryId);
20999
21065
  const answerFile = path8.join(dir, "answer.json");
21000
21066
  const bindFile = path8.join(dir, "bind.json");
21001
- if (!fs5.existsSync(answerFile) || !fs5.existsSync(bindFile))
21067
+ if (!fs6.existsSync(answerFile) || !fs6.existsSync(bindFile))
21002
21068
  return [];
21003
- const answer = external_exports.object({ delivery_id: external_exports.string(), text: external_exports.string() }).parse(JSON.parse(fs5.readFileSync(answerFile, "utf8")));
21004
- const bound = external_exports.object({ conversation_id: external_exports.string(), created_at: external_exports.string() }).parse(JSON.parse(fs5.readFileSync(bindFile, "utf8")));
21069
+ const answer = external_exports.object({ delivery_id: external_exports.string(), text: external_exports.string() }).parse(JSON.parse(fs6.readFileSync(answerFile, "utf8")));
21070
+ const bound = external_exports.object({ conversation_id: external_exports.string(), created_at: external_exports.string() }).parse(JSON.parse(fs6.readFileSync(bindFile, "utf8")));
21005
21071
  if (answer.delivery_id !== deliveryId || bound.conversation_id !== conv)
21006
21072
  return [];
21007
21073
  return [{ deliveryId, dir, created_at: bound.created_at, text: answer.text }];
@@ -21010,12 +21076,12 @@ function inject(hookRoot, conv) {
21010
21076
  for (const item of pending) {
21011
21077
  const index = path8.join(indexDir, item.deliveryId);
21012
21078
  if (!claim2(item.dir, "hook")) {
21013
- if (fs5.existsSync(path8.join(item.dir, "claim.json")))
21014
- fs5.rmSync(index, { force: true });
21079
+ if (fs6.existsSync(path8.join(item.dir, "claim.json")))
21080
+ fs6.rmSync(index, { force: true });
21015
21081
  continue;
21016
21082
  }
21017
21083
  texts.push(item.text);
21018
- fs5.rmSync(index, { force: true });
21084
+ fs6.rmSync(index, { force: true });
21019
21085
  }
21020
21086
  return texts;
21021
21087
  }
@@ -21059,7 +21125,7 @@ function waitProcessCommandLine(wait, platform = process.platform) {
21059
21125
  }
21060
21126
 
21061
21127
  // ../../node_modules/aiterm-steer-delivery/dist/channel.js
21062
- import * as fs6 from "node:fs";
21128
+ import * as fs7 from "node:fs";
21063
21129
  import * as path9 from "node:path";
21064
21130
  import { randomUUID as randomUUID3 } from "node:crypto";
21065
21131
  import { setTimeout as delay } from "node:timers/promises";
@@ -21084,7 +21150,7 @@ function processIdentity2(pid) {
21084
21150
  }
21085
21151
  function mkdirs(...dirs) {
21086
21152
  for (const dir of dirs)
21087
- fs6.mkdirSync(dir, { recursive: true, mode: 448 });
21153
+ fs7.mkdirSync(dir, { recursive: true, mode: 448 });
21088
21154
  }
21089
21155
  function openChannel(profile, parent) {
21090
21156
  const channel_id = randomUUID3();
@@ -21098,7 +21164,7 @@ function openChannel(profile, parent) {
21098
21164
  else if (parent.kind === "cursor")
21099
21165
  channel = { schema: "aiterm-steer.channel.v1", channel_id, kind: "cursor", created_at };
21100
21166
  else {
21101
- const request = external_exports.object({ session_id: external_exports.uuid(), parent_pid: external_exports.number().int().positive(), parent_started_identity: external_exports.string() }).loose().parse(JSON.parse(fs6.readFileSync(path9.join(parent.hook_root, parent.request_id, "request.json"), "utf8")));
21167
+ const request = external_exports.object({ session_id: external_exports.uuid(), parent_pid: external_exports.number().int().positive(), parent_started_identity: external_exports.string() }).loose().parse(JSON.parse(fs7.readFileSync(path9.join(parent.hook_root, parent.request_id, "request.json"), "utf8")));
21102
21168
  if (request.session_id !== parent.session_id)
21103
21169
  throw new ChannelError("CHANNEL_PARENT_SESSION_MISMATCH", "\u8981\u6C42\u3068hook\u306E\u4F1A\u8A71\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
21104
21170
  channel = {
@@ -21114,14 +21180,14 @@ function openChannel(profile, parent) {
21114
21180
  if (channel.claude) {
21115
21181
  const index = path9.join(channelRoot(profile), "claude-sessions", channel.claude.session_id);
21116
21182
  mkdirs(index);
21117
- fs6.writeFileSync(path9.join(index, channel_id), "", { mode: 384 });
21183
+ fs7.writeFileSync(path9.join(index, channel_id), "", { mode: 384 });
21118
21184
  }
21119
21185
  return channel;
21120
21186
  }
21121
21187
  function readChannel(profile, channelId) {
21122
21188
  const file2 = path9.join(channelDir(profile, channelId), "channel.json");
21123
21189
  try {
21124
- return channelSchema.parse(JSON.parse(fs6.readFileSync(file2, "utf8")));
21190
+ return channelSchema.parse(JSON.parse(fs7.readFileSync(file2, "utf8")));
21125
21191
  } catch (error62) {
21126
21192
  if (error62.code === "ENOENT")
21127
21193
  throw new ChannelError("CHANNEL_UNKNOWN", "channel\u306E\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093");
@@ -21129,11 +21195,11 @@ function readChannel(profile, channelId) {
21129
21195
  }
21130
21196
  }
21131
21197
  function channelClosed(profile, channelId) {
21132
- return fs6.existsSync(path9.join(channelDir(profile, channelId), "closed.json"));
21198
+ return fs7.existsSync(path9.join(channelDir(profile, channelId), "closed.json"));
21133
21199
  }
21134
21200
  function closeChannel(profile, channelId, reason = "closed") {
21135
21201
  const dir = channelDir(profile, channelId);
21136
- if (!fs6.existsSync(dir))
21202
+ if (!fs7.existsSync(dir))
21137
21203
  return;
21138
21204
  writeJson0600(path9.join(dir, "closed.json"), { reason, at: (/* @__PURE__ */ new Date()).toISOString() });
21139
21205
  }
@@ -21147,22 +21213,25 @@ async function sendToChannel(profile, channelId, deliveryId, text, runtime) {
21147
21213
  return { state: "submitted", queued_submission_id: result.queued_submission_id };
21148
21214
  }
21149
21215
  const dir = channelDir(profile, channelId);
21216
+ if (fs7.readdirSync(path9.join(dir, "inbox")).some((entry) => entry.endsWith(`-${deliveryId}.json`))) {
21217
+ throw new ChannelError("CHANNEL_DELIVERY_DUPLICATE", "\u540C\u3058\u914D\u9001ID\u306E\u672C\u6587\u304C\u3059\u3067\u306B\u53D7\u4FE1\u7BB1\u306B\u3042\u308A\u307E\u3059");
21218
+ }
21150
21219
  for (const sub of ["claims", "emitted"]) {
21151
- if (fs6.existsSync(path9.join(dir, sub, `${deliveryId}.json`)))
21220
+ if (fs7.existsSync(path9.join(dir, sub, `${deliveryId}.json`)))
21152
21221
  throw new ChannelError("CHANNEL_DELIVERY_DUPLICATE", "\u540C\u3058\u914D\u9001ID\u306E\u672C\u6587\u306F\u3059\u3067\u306B\u53D7\u3051\u53D6\u3089\u308C\u3066\u3044\u307E\u3059");
21153
21222
  }
21154
21223
  const name = `${Date.now().toString().padStart(15, "0")}-${(sendSequence++).toString().padStart(9, "0")}-${deliveryId}.json`;
21155
21224
  const item = { delivery_id: deliveryId, text, at: (/* @__PURE__ */ new Date()).toISOString() };
21156
21225
  const temporary = path9.join(dir, `.${deliveryId}.${randomUUID3()}.tmp`);
21157
- fs6.writeFileSync(temporary, JSON.stringify(item) + "\n", { flag: "wx", mode: 384 });
21226
+ fs7.writeFileSync(temporary, JSON.stringify(item) + "\n", { flag: "wx", mode: 384 });
21158
21227
  try {
21159
- fs6.linkSync(temporary, path9.join(dir, "inbox", name));
21228
+ fs7.linkSync(temporary, path9.join(dir, "inbox", name));
21160
21229
  } catch (error62) {
21161
21230
  if (error62.code === "EEXIST")
21162
21231
  throw new ChannelError("CHANNEL_DELIVERY_DUPLICATE", "\u540C\u3058\u914D\u9001ID\u306E\u672C\u6587\u304C\u3059\u3067\u306B\u53D7\u4FE1\u7BB1\u306B\u3042\u308A\u307E\u3059");
21163
21232
  throw error62;
21164
21233
  } finally {
21165
- fs6.rmSync(temporary, { force: true });
21234
+ fs7.rmSync(temporary, { force: true });
21166
21235
  }
21167
21236
  return { state: "queued", queued_submission_id: null };
21168
21237
  }
@@ -21172,13 +21241,13 @@ function channelDeliveryState(profile, channelId, deliveryId) {
21172
21241
  if (channel.kind === "codex")
21173
21242
  return null;
21174
21243
  const dir = channelDir(profile, channelId);
21175
- if (fs6.existsSync(path9.join(dir, "emitted", `${deliveryId}.json`)))
21244
+ if (fs7.existsSync(path9.join(dir, "emitted", `${deliveryId}.json`)))
21176
21245
  return "emitted";
21177
21246
  const claim3 = path9.join(dir, "claims", `${deliveryId}.json`);
21178
- if (fs6.existsSync(claim3)) {
21247
+ if (fs7.existsSync(claim3)) {
21179
21248
  let value;
21180
21249
  try {
21181
- value = JSON.parse(fs6.readFileSync(claim3, "utf8"));
21250
+ value = JSON.parse(fs7.readFileSync(claim3, "utf8"));
21182
21251
  } catch {
21183
21252
  return "sending";
21184
21253
  }
@@ -21190,41 +21259,48 @@ function channelDeliveryState(profile, channelId, deliveryId) {
21190
21259
  return "unknown";
21191
21260
  return "sending";
21192
21261
  }
21193
- return fs6.readdirSync(path9.join(dir, "inbox")).some((name) => name.endsWith(`-${deliveryId}.json`)) ? "queued" : null;
21262
+ return fs7.readdirSync(path9.join(dir, "inbox")).some((name) => name.endsWith(`-${deliveryId}.json`)) ? "queued" : null;
21194
21263
  }
21195
21264
  function withdrawFromChannel(profile, channelId, deliveryId) {
21196
21265
  uuid3.parse(deliveryId);
21197
21266
  const dir = channelDir(profile, channelId);
21198
21267
  const inbox = path9.join(dir, "inbox");
21199
- const name = fs6.existsSync(inbox) ? fs6.readdirSync(inbox).find((entry) => entry.endsWith(`-${deliveryId}.json`)) : void 0;
21268
+ const name = fs7.existsSync(inbox) ? fs7.readdirSync(inbox).find((entry) => entry.endsWith(`-${deliveryId}.json`)) : void 0;
21200
21269
  if (!name)
21201
21270
  return false;
21202
21271
  const claim3 = path9.join(dir, "claims", `${deliveryId}.json`);
21203
21272
  try {
21204
- fs6.linkSync(path9.join(inbox, name), claim3);
21205
- fs6.unlinkSync(path9.join(inbox, name));
21273
+ fs7.linkSync(path9.join(inbox, name), claim3);
21274
+ fs7.unlinkSync(path9.join(inbox, name));
21206
21275
  } catch (error62) {
21207
21276
  if (["ENOENT", "EEXIST"].includes(error62.code ?? ""))
21208
21277
  return false;
21209
21278
  throw error62;
21210
21279
  }
21280
+ if (alreadyEmitted(dir, deliveryId)) {
21281
+ fs7.rmSync(claim3, { force: true });
21282
+ return false;
21283
+ }
21211
21284
  writeJson0600(claim3, { delivery_id: deliveryId, state: "withdrawn", at: (/* @__PURE__ */ new Date()).toISOString() });
21212
21285
  return true;
21213
21286
  }
21287
+ function alreadyEmitted(dir, deliveryId) {
21288
+ return fs7.existsSync(path9.join(dir, "emitted", `${deliveryId}.json`));
21289
+ }
21214
21290
  function claimPending(profile, channelId, by, limit = Infinity) {
21215
21291
  const dir = channelDir(profile, channelId);
21216
21292
  const inbox = path9.join(dir, "inbox");
21217
- if (!fs6.existsSync(inbox))
21293
+ if (!fs7.existsSync(inbox))
21218
21294
  return [];
21219
21295
  let identity;
21220
21296
  const claimed = [];
21221
- for (const name of fs6.readdirSync(inbox).filter((entry) => entry.endsWith(".json")).sort()) {
21297
+ for (const name of fs7.readdirSync(inbox).filter((entry) => entry.endsWith(".json")).sort()) {
21222
21298
  if (claimed.length >= limit)
21223
21299
  break;
21224
21300
  const source = path9.join(inbox, name);
21225
21301
  let item;
21226
21302
  try {
21227
- item = itemSchema.parse(JSON.parse(fs6.readFileSync(source, "utf8")));
21303
+ item = itemSchema.parse(JSON.parse(fs7.readFileSync(source, "utf8")));
21228
21304
  } catch (error62) {
21229
21305
  if (error62.code === "ENOENT")
21230
21306
  continue;
@@ -21232,13 +21308,17 @@ function claimPending(profile, channelId, by, limit = Infinity) {
21232
21308
  }
21233
21309
  const claim3 = path9.join(dir, "claims", `${item.delivery_id}.json`);
21234
21310
  try {
21235
- fs6.linkSync(source, claim3);
21236
- fs6.unlinkSync(source);
21311
+ fs7.linkSync(source, claim3);
21312
+ fs7.unlinkSync(source);
21237
21313
  } catch (error62) {
21238
21314
  if (["ENOENT", "EEXIST"].includes(error62.code ?? ""))
21239
21315
  continue;
21240
21316
  throw error62;
21241
21317
  }
21318
+ if (alreadyEmitted(dir, item.delivery_id)) {
21319
+ fs7.rmSync(claim3, { force: true });
21320
+ continue;
21321
+ }
21242
21322
  identity ??= processIdentity2(process.pid) ?? "unknown";
21243
21323
  writeJson0600(claim3, { ...item, state: "sending", by, pid: process.pid, started_identity: identity });
21244
21324
  claimed.push({ channel_id: channelId, item, claim: claim3, dir });
@@ -21249,7 +21329,7 @@ function settle2(entries, by, state) {
21249
21329
  for (const entry of entries) {
21250
21330
  if (state === "emitted") {
21251
21331
  writeJson0600(path9.join(entry.dir, "emitted", `${entry.item.delivery_id}.json`), { delivery_id: entry.item.delivery_id, by, at: (/* @__PURE__ */ new Date()).toISOString() });
21252
- fs6.rmSync(entry.claim, { force: true });
21332
+ fs7.rmSync(entry.claim, { force: true });
21253
21333
  } else {
21254
21334
  writeJson0600(entry.claim, { ...entry.item, state: "unknown", by });
21255
21335
  }
@@ -21269,9 +21349,9 @@ function sessionIndex(profile, sessionId) {
21269
21349
  }
21270
21350
  function openClaudeChannels(profile, sessionId) {
21271
21351
  const index = sessionIndex(profile, sessionId);
21272
- if (!fs6.existsSync(index))
21352
+ if (!fs7.existsSync(index))
21273
21353
  return [];
21274
- return fs6.readdirSync(index).filter((name) => uuid3.safeParse(name).success).flatMap((name) => {
21354
+ return fs7.readdirSync(index).filter((name) => uuid3.safeParse(name).success).flatMap((name) => {
21275
21355
  try {
21276
21356
  const channel = readChannel(profile, name);
21277
21357
  return channel.claude?.session_id === sessionId && !channelClosed(profile, name) ? [channel] : [];
@@ -21289,7 +21369,7 @@ function acquireWaiter(index) {
21289
21369
  throw new ChannelError("CHANNEL_WAITER_UNAVAILABLE", "\u53D7\u4FE1hook\u306Eprocess\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093");
21290
21370
  for (let attempt = 0; attempt < 3; attempt += 1) {
21291
21371
  try {
21292
- fs6.writeFileSync(file2, JSON.stringify({ pid: process.pid, started_identity: identity }) + "\n", { flag: "wx", mode: 384 });
21372
+ fs7.writeFileSync(file2, JSON.stringify({ pid: process.pid, started_identity: identity }) + "\n", { flag: "wx", mode: 384 });
21293
21373
  return identity;
21294
21374
  } catch (error62) {
21295
21375
  if (error62.code !== "EEXIST")
@@ -21297,33 +21377,38 @@ function acquireWaiter(index) {
21297
21377
  }
21298
21378
  let owner = {};
21299
21379
  try {
21300
- owner = JSON.parse(fs6.readFileSync(file2, "utf8"));
21380
+ owner = JSON.parse(fs7.readFileSync(file2, "utf8"));
21301
21381
  } catch {
21302
21382
  }
21303
21383
  if (typeof owner.pid === "number" && typeof owner.started_identity === "string" && processIdentity2(owner.pid) === owner.started_identity)
21304
21384
  return null;
21305
21385
  try {
21306
- fs6.renameSync(file2, path9.join(index, `waiter.${randomUUID3()}.stale`));
21386
+ fs7.renameSync(file2, path9.join(index, `waiter.${randomUUID3()}.stale`));
21307
21387
  } catch {
21308
21388
  }
21309
- for (const name of fs6.readdirSync(index).filter((entry) => entry.endsWith(".stale")))
21310
- fs6.rmSync(path9.join(index, name), { force: true });
21389
+ for (const name of fs7.readdirSync(index).filter((entry) => entry.endsWith(".stale")))
21390
+ fs7.rmSync(path9.join(index, name), { force: true });
21311
21391
  }
21312
21392
  return null;
21313
21393
  }
21314
21394
  function releaseWaiter(index, identity) {
21315
21395
  const file2 = path9.join(index, "waiter.json");
21316
21396
  try {
21317
- const owner = JSON.parse(fs6.readFileSync(file2, "utf8"));
21397
+ const owner = JSON.parse(fs7.readFileSync(file2, "utf8"));
21318
21398
  if (owner.pid === process.pid && owner.started_identity === identity)
21319
- fs6.rmSync(file2, { force: true });
21399
+ fs7.rmSync(file2, { force: true });
21320
21400
  } catch {
21321
21401
  }
21322
21402
  }
21403
+ function currentHookOwner() {
21404
+ const owner = hookOwnerProcess(readRuntimeProcesses(), process.ppid);
21405
+ return owner ? { pid: owner.pid, started_identity: owner.started_identity } : null;
21406
+ }
21323
21407
  async function runClaudeChannelWaiter(profile, input2, emit, options = {}) {
21324
21408
  const event = external_exports.object({ session_id: external_exports.uuid() }).loose().parse(input2);
21325
21409
  if (!openClaudeChannels(profile, event.session_id).length)
21326
21410
  return 0;
21411
+ const owner = options.owner === void 0 ? currentHookOwner() : options.owner;
21327
21412
  const index = sessionIndex(profile, event.session_id);
21328
21413
  const identity = acquireWaiter(index);
21329
21414
  if (!identity)
@@ -21337,10 +21422,11 @@ async function runClaudeChannelWaiter(profile, input2, emit, options = {}) {
21337
21422
  return 0;
21338
21423
  if (Date.now() - lastParentCheck >= 5e3) {
21339
21424
  lastParentCheck = Date.now();
21340
- if (!channels.some((channel) => processIdentity2(channel.claude.parent_pid) === channel.claude.parent_started_identity))
21425
+ const alive = owner ? processIdentity2(owner.pid) === owner.started_identity : channels.some((channel) => processIdentity2(channel.claude.parent_pid) === channel.claude.parent_started_identity);
21426
+ if (!alive)
21341
21427
  return 0;
21342
21428
  }
21343
- const claimed = channels.flatMap((channel) => claimPending(profile, channel.channel_id, "claude_hook"));
21429
+ const claimed = channels.flatMap((channel) => claimPending(profile, channel.channel_id, "claude_hook")).sort((left, right) => left.item.at < right.item.at ? -1 : left.item.at > right.item.at ? 1 : 0);
21344
21430
  if (claimed.length) {
21345
21431
  await emitClaimed(claimed, "claude_hook", (texts) => emit(texts.join("\n\n")));
21346
21432
  return 2;
@@ -21425,25 +21511,25 @@ async function handleCursorChannelHook(profile, event, emit) {
21425
21511
  const payload = name === "afterMCPExecution" ? event.result_json : event.tool_output;
21426
21512
  const bound = channelIdFromResult(payload);
21427
21513
  const conversations = path9.join(channelRoot(profile), "cursor-conversations");
21428
- if (bound && fs6.existsSync(channelDir(profile, bound))) {
21514
+ if (bound && fs7.existsSync(channelDir(profile, bound))) {
21429
21515
  const channel = readChannel(profile, bound);
21430
21516
  if (channel.kind === "cursor") {
21431
21517
  const bindFile = path9.join(channelDir(profile, bound), "bind.json");
21432
- if (!fs6.existsSync(bindFile))
21518
+ if (!fs7.existsSync(bindFile))
21433
21519
  writeJson0600(bindFile, { conversation_id: conv, at: (/* @__PURE__ */ new Date()).toISOString() });
21434
- const bindConv = JSON.parse(fs6.readFileSync(bindFile, "utf8")).conversation_id;
21520
+ const bindConv = JSON.parse(fs7.readFileSync(bindFile, "utf8")).conversation_id;
21435
21521
  if (bindConv === conv) {
21436
21522
  mkdirs(path9.join(conversations, conv));
21437
- fs6.writeFileSync(path9.join(conversations, conv, bound), "", { mode: 384 });
21523
+ fs7.writeFileSync(path9.join(conversations, conv, bound), "", { mode: 384 });
21438
21524
  }
21439
21525
  }
21440
21526
  }
21441
21527
  if (name === "afterMCPExecution")
21442
21528
  return false;
21443
21529
  const index = path9.join(conversations, conv);
21444
- if (!fs6.existsSync(index))
21530
+ if (!fs7.existsSync(index))
21445
21531
  return false;
21446
- const claimed = fs6.readdirSync(index).filter((id) => uuid3.safeParse(id).success && !channelClosed(profile, id)).flatMap((id) => claimPending(profile, id, "cursor_hook"));
21532
+ const claimed = fs7.readdirSync(index).filter((id) => uuid3.safeParse(id).success && !channelClosed(profile, id)).flatMap((id) => claimPending(profile, id, "cursor_hook"));
21447
21533
  if (!claimed.length)
21448
21534
  return false;
21449
21535
  await emitClaimed(claimed, "cursor_hook", (texts) => emit(texts.join("\n\n")));
@@ -21512,6 +21598,11 @@ async function runClaudeHookMain(profile, root = claudeHookRoot(profile)) {
21512
21598
  throw new Error("CLAUDE_PARENT_HOOK_EVENT_INVALID: \u672A\u5BFE\u5FDC\u306Ehook\u3067\u3059");
21513
21599
  process.exitCode = await runClaudeChannelWaiter(profile, event, write);
21514
21600
  break;
21601
+ case "SessionStart":
21602
+ if (!profile.channels)
21603
+ throw new Error("CLAUDE_PARENT_HOOK_EVENT_INVALID: \u672A\u5BFE\u5FDC\u306Ehook\u3067\u3059");
21604
+ process.exitCode = await runClaudeChannelWaiter(profile, event, write);
21605
+ break;
21515
21606
  case "SessionEnd":
21516
21607
  closeClaudeParentSession(event, root);
21517
21608
  if (profile.channels)
@@ -21563,8 +21654,8 @@ function setupNodeExecutable(executable = process.execPath) {
21563
21654
  }
21564
21655
 
21565
21656
  // ../../node_modules/aiterm-steer-delivery/dist/aiterm-provider.js
21566
- import { spawn as spawn2 } from "node:child_process";
21567
- import * as fs7 from "node:fs";
21657
+ import { spawn as spawn3 } from "node:child_process";
21658
+ import * as fs8 from "node:fs";
21568
21659
  import * as os2 from "node:os";
21569
21660
  import * as path10 from "node:path";
21570
21661
  var AITERM_PARENT_DELIVERY_SCHEMA = "aiterm.parent-delivery.v1";
@@ -21577,7 +21668,7 @@ var recordSchema = external_exports.object({
21577
21668
  }).strict();
21578
21669
  var isFile = (file2) => {
21579
21670
  try {
21580
- return fs7.statSync(file2).isFile();
21671
+ return fs8.statSync(file2).isFile();
21581
21672
  } catch {
21582
21673
  return false;
21583
21674
  }
@@ -21590,7 +21681,7 @@ function findAitermDeliveryProvider(options = {}) {
21590
21681
  }
21591
21682
  const home2 = options.home ?? env.HOME ?? os2.homedir();
21592
21683
  try {
21593
- const record3 = recordSchema.parse(JSON.parse(fs7.readFileSync(path10.join(home2, ".config", "aiterm-mcp", "delivery-provider.json"), "utf8")));
21684
+ const record3 = recordSchema.parse(JSON.parse(fs8.readFileSync(path10.join(home2, ".config", "aiterm-mcp", "delivery-provider.json"), "utf8")));
21594
21685
  const node2 = options.node ?? record3.node;
21595
21686
  if (isFile(record3.cli) && isFile(node2))
21596
21687
  return { node: node2, cli: record3.cli, version: record3.version, source: "record" };
@@ -21607,7 +21698,7 @@ function findAitermDeliveryProvider(options = {}) {
21607
21698
  if (!isFile(shim))
21608
21699
  continue;
21609
21700
  try {
21610
- return { node: options.node ?? process.execPath, cli: fs7.realpathSync(shim), version: null, source: "path" };
21701
+ return { node: options.node ?? process.execPath, cli: fs8.realpathSync(shim), version: null, source: "path" };
21611
21702
  } catch {
21612
21703
  }
21613
21704
  }
@@ -21630,7 +21721,7 @@ async function callAiterm(args, input2, options, sends) {
21630
21721
  clearTimeout(timer);
21631
21722
  resolve2({ stdout, started, failure: failure2 });
21632
21723
  };
21633
- const child = spawn2(provider.node, [provider.cli, ...args], { stdio: ["pipe", "pipe", "ignore"], env: options.env ?? process.env, windowsHide: true });
21724
+ const child = spawn3(provider.node, [provider.cli, ...args], { stdio: ["pipe", "pipe", "ignore"], env: options.env ?? process.env, windowsHide: true });
21634
21725
  const timer = setTimeout(() => {
21635
21726
  child.kill("SIGKILL");
21636
21727
  finish("\u6642\u9593\u5185\u306B\u8FD4\u308A\u304C\u3042\u308A\u307E\u305B\u3093");
@@ -21682,7 +21773,8 @@ async function codexDeliveryDetailViaAiterm(parent, deliveryId, options = {}) {
21682
21773
  hook: value.hook ?? null,
21683
21774
  turn_id: value.turn_id ?? null,
21684
21775
  queued: value.queued ?? null,
21685
- ...typeof value.queue_error === "string" ? { queue_error: value.queue_error } : {}
21776
+ ...typeof value.queue_error === "string" ? { queue_error: value.queue_error } : {},
21777
+ ...value.wake !== null && typeof value.wake === "object" ? { wake: value.wake } : {}
21686
21778
  };
21687
21779
  }
21688
21780
  async function codexDeliveryStateViaAiterm(parent, deliveryId, options = {}) {
@@ -21690,7 +21782,7 @@ async function codexDeliveryStateViaAiterm(parent, deliveryId, options = {}) {
21690
21782
  }
21691
21783
 
21692
21784
  // ../../node_modules/aiterm-steer-delivery/dist/codex-setup.js
21693
- import * as fs8 from "node:fs";
21785
+ import * as fs9 from "node:fs";
21694
21786
  import * as path11 from "node:path";
21695
21787
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
21696
21788
  function codexHookCommand(node2, hook, directory2, platform = process.platform, powershell = () => resolveWindowsPowerShell7()) {
@@ -21701,20 +21793,20 @@ function codexHookCommand(node2, hook, directory2, platform = process.platform,
21701
21793
  return [node2, hook, directory2].map((value) => "'" + value.replace(/'/g, `'"'"'`) + "'").join(" ");
21702
21794
  }
21703
21795
  function planCodexParentHooks(file2, command2, previousCommand) {
21704
- if (!fs8.existsSync(file2)) {
21796
+ if (!fs9.existsSync(file2)) {
21705
21797
  try {
21706
- if (fs8.lstatSync(file2).isSymbolicLink())
21798
+ if (fs9.lstatSync(file2).isSymbolicLink())
21707
21799
  throw new SetupError("codex_hook_config_invalid", "hook\u8A2D\u5B9Asymlink\u306E\u53C2\u7167\u5148\u304C\u3042\u308A\u307E\u305B\u3093");
21708
21800
  } catch (error62) {
21709
21801
  if (error62.code !== "ENOENT")
21710
21802
  throw error62;
21711
21803
  }
21712
21804
  }
21713
- const target2 = fs8.existsSync(file2) ? fs8.realpathSync(file2) : file2;
21805
+ const target2 = fs9.existsSync(file2) ? fs9.realpathSync(file2) : file2;
21714
21806
  let current = {};
21715
- if (fs8.existsSync(target2)) {
21807
+ if (fs9.existsSync(target2)) {
21716
21808
  try {
21717
- current = JSON.parse(fs8.readFileSync(target2, "utf8"));
21809
+ current = JSON.parse(fs9.readFileSync(target2, "utf8"));
21718
21810
  } catch {
21719
21811
  throw new SetupError("codex_hook_config_invalid", "Codex\u306Ehook\u8A2D\u5B9AJSON\u3092\u8AAD\u3081\u307E\u305B\u3093");
21720
21812
  }
@@ -21767,10 +21859,10 @@ function planCodexParentHooks(file2, command2, previousCommand) {
21767
21859
  function writeCodexHookPlan(profile, plan) {
21768
21860
  if (!plan.changed)
21769
21861
  return false;
21770
- if (fs8.existsSync(plan.target))
21771
- fs8.copyFileSync(plan.target, `${plan.target}${profile.backup_suffix}`);
21862
+ if (fs9.existsSync(plan.target))
21863
+ fs9.copyFileSync(plan.target, `${plan.target}${profile.backup_suffix}`);
21772
21864
  writeHookJson(plan.target, plan.next);
21773
- if (!isDeepStrictEqual2(JSON.parse(fs8.readFileSync(plan.target, "utf8")), plan.next))
21865
+ if (!isDeepStrictEqual2(JSON.parse(fs9.readFileSync(plan.target, "utf8")), plan.next))
21774
21866
  throw new SetupError("codex_hook_readback_failed", "Codex hook\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
21775
21867
  return true;
21776
21868
  }
@@ -21790,7 +21882,7 @@ async function applyCodexHookPlan(profile, plan, codexHome, executable) {
21790
21882
  }
21791
21883
  return withCodexReceiver(profile, { thread_id: "00000000-0000-4000-8000-000000000000", codex_home: codexHome }, async (request) => {
21792
21884
  const listed = await request("hooks/list", { cwds: [codexHome] });
21793
- const source = fs8.realpathSync(plan.target);
21885
+ const source = fs9.realpathSync(plan.target);
21794
21886
  const sample = (listed?.data?.[0]?.hooks ?? []).find((hook) => hook.sourcePath === source && typeof hook.key === "string");
21795
21887
  if (!sample)
21796
21888
  throw new SetupError("codex_hook_schema_unknown", "\u516C\u5F0FAPI\u3067hook\u306E\u9375\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093");
@@ -21818,7 +21910,7 @@ async function applyCodexHookPlan(profile, plan, codexHome, executable) {
21818
21910
  }
21819
21911
  async function cleanVacatedTrust(profile, plan, codexHome, executable) {
21820
21912
  await withCodexReceiver(profile, { thread_id: "00000000-0000-4000-8000-000000000000", codex_home: codexHome }, async (request) => {
21821
- const source = fs8.realpathSync(plan.target);
21913
+ const source = fs9.realpathSync(plan.target);
21822
21914
  const prefix = `${source}`;
21823
21915
  const key = (event, [g, h]) => `${prefix}:${snake(event)}:${g}:${h}`;
21824
21916
  const state = (await request("config/read", { includeLayers: false }))?.config?.hooks?.state ?? {};
@@ -21839,12 +21931,12 @@ function samePath(a, b) {
21839
21931
  if (a === b)
21840
21932
  return true;
21841
21933
  try {
21842
- return fs8.realpathSync(a) === fs8.realpathSync(b);
21934
+ return fs9.realpathSync(a) === fs9.realpathSync(b);
21843
21935
  } catch {
21844
21936
  return process.platform === "win32" && a.toLowerCase() === b.toLowerCase();
21845
21937
  }
21846
21938
  }
21847
- function processCodexHome(pid, platform = process.platform, read = (file2) => fs8.readFileSync(file2)) {
21939
+ function processCodexHome(pid, platform = process.platform, read = (file2) => fs9.readFileSync(file2)) {
21848
21940
  if (platform !== "linux")
21849
21941
  return null;
21850
21942
  let raw;
@@ -21861,8 +21953,8 @@ function processCodexHome(pid, platform = process.platform, read = (file2) => fs
21861
21953
  return home2 ? path11.posix.resolve(home2) : null;
21862
21954
  }
21863
21955
  async function verifyCodexHookRegistration(profile, config2, approve, directory2 = codexHookDirectory(profile)) {
21864
- fs8.accessSync(config2.node, fs8.constants.X_OK);
21865
- fs8.accessSync(config2.hook, fs8.constants.R_OK);
21956
+ fs9.accessSync(config2.node, fs9.constants.X_OK);
21957
+ fs9.accessSync(config2.hook, fs9.constants.R_OK);
21866
21958
  const file2 = path11.join(config2.codex_home, "hooks.json");
21867
21959
  await withCodexReceiver(profile, { thread_id: "00000000-0000-4000-8000-000000000000", codex_home: config2.codex_home }, async (request) => {
21868
21960
  const list = () => request("hooks/list", { cwds: [config2.codex_home] });
@@ -21923,11 +22015,11 @@ async function configureCodexSteer(profile, action = "status", overrides) {
21923
22015
  return runtime.disableLegacy();
21924
22016
  return previous?.enabled ? { status: "restart_required", reason_code: "codex_restart_required" } : { status: "disabled" };
21925
22017
  }
21926
- if (previous?.enabled && fs8.realpathSync(previous.codex_home) !== fs8.realpathSync(runtime.codex_home))
22018
+ if (previous?.enabled && fs9.realpathSync(previous.codex_home) !== fs9.realpathSync(runtime.codex_home))
21927
22019
  throw new SetupError("codex_steer_configuration_conflict", "\u5225\u306ECODEX_HOME\u3067Steer\u304C\u6709\u52B9\u3067\u3059\u3002\u5148\u306B\u65E2\u5B58\u306E\u9078\u629E\u3092\u89E3\u9664\u3057\u3066\u304F\u3060\u3055\u3044");
21928
22020
  const node2 = setupNodeExecutable(runtime.node);
21929
- fs8.accessSync(node2, fs8.constants.X_OK);
21930
- fs8.accessSync(runtime.hook, fs8.constants.R_OK);
22021
+ fs9.accessSync(node2, fs9.constants.X_OK);
22022
+ fs9.accessSync(runtime.hook, fs9.constants.R_OK);
21931
22023
  const binary = runtime.findBinary();
21932
22024
  const command2 = codexHookCommand(node2, runtime.hook, runtime.directory, runtime.platform, runtime.powershell);
21933
22025
  const changed = await applyCodexHookPlan(profile, planCodexParentHooks(file2, command2, previous?.command), runtime.codex_home, binary);
@@ -21935,7 +22027,7 @@ async function configureCodexSteer(profile, action = "status", overrides) {
21935
22027
  const packageRoot = (file3) => {
21936
22028
  let real = file3;
21937
22029
  try {
21938
- real = fs8.realpathSync(file3);
22030
+ real = fs9.realpathSync(file3);
21939
22031
  } catch {
21940
22032
  }
21941
22033
  return /[\\/]bin[\\/]codex\.js$/u.test(real) ? path11.dirname(path11.dirname(real)) : null;
@@ -21960,10 +22052,12 @@ async function configureCodexSteer(profile, action = "status", overrides) {
21960
22052
  stale_processes: stale
21961
22053
  };
21962
22054
  await runtime.verify(config2, true);
21963
- save(config2);
22055
+ const alive = runtime.processes();
22056
+ const saved = { ...config2, stale_processes: config2.stale_processes.filter((stale2) => alive.some((row) => row.pid === stale2.pid && row.started_identity === stale2.started_identity)) };
22057
+ save(saved);
21964
22058
  if (legacy?.enabled)
21965
22059
  await runtime.disableLegacy();
21966
- return needsRestart(config2) ? { status: "restart_required", reason_code: "codex_restart_required" } : { status: "ready" };
22060
+ return saved.stale_processes.length ? { status: "restart_required", reason_code: "codex_restart_required" } : { status: "ready" };
21967
22061
  }
21968
22062
 
21969
22063
  export {
package/dist/cli.mjs CHANGED
@@ -56,7 +56,7 @@ import {
56
56
  waitProcessCommandLine,
57
57
  withCodexReceiver,
58
58
  withdrawFromChannel
59
- } from "./chunks/chunk-OVDAILMZ.mjs";
59
+ } from "./chunks/chunk-SN7I52NL.mjs";
60
60
 
61
61
  // ../../node_modules/qrcode/lib/can-promise.js
62
62
  var require_can_promise = __commonJS({
@@ -11761,7 +11761,7 @@ var require_dist = __commonJS({
11761
11761
  var import_qrcode = __toESM(require_lib(), 1);
11762
11762
  import { cpSync, existsSync as existsSync7, mkdirSync as mkdirSync5, rmSync as rmSync3 } from "node:fs";
11763
11763
  import { hostname } from "node:os";
11764
- import { join as join9 } from "node:path";
11764
+ import { join as join10 } from "node:path";
11765
11765
  import { createInterface } from "node:readline/promises";
11766
11766
 
11767
11767
  // src/api.ts
@@ -11916,7 +11916,16 @@ var provider = {
11916
11916
  async function sendChannelAnswer(channelId, deliveryId, text2, codex = provider) {
11917
11917
  if (channelClosed(PROFILE, channelId)) throw Object.assign(new Error("\u7533\u8ACB\u3092\u51FA\u3057\u305FAI\u306E\u4F1A\u8A71\u304C\u7D42\u308F\u3063\u3066\u3044\u307E\u3059"), { closed: true });
11918
11918
  const channel = readChannel(PROFILE, channelId);
11919
- if (channel.kind !== "codex") return sendToChannel(PROFILE, channelId, deliveryId, text2);
11919
+ if (channel.kind !== "codex") {
11920
+ try {
11921
+ return await sendToChannel(PROFILE, channelId, deliveryId, text2);
11922
+ } catch (error) {
11923
+ if (error.delivery_code !== "CHANNEL_DELIVERY_DUPLICATE") throw error;
11924
+ const state = channelDeliveryState(PROFILE, channelId, deliveryId);
11925
+ if (state === null || state === "withdrawn") throw error;
11926
+ return { state: "queued", queued_submission_id: null };
11927
+ }
11928
+ }
11920
11929
  if (!channel.codex) throw new CodexDeliveryError("CODEX_PARENT_ID_UNAVAILABLE", "channel\u306B\u5143\u306ECodex\u4F1A\u8A71\u304C\u3042\u308A\u307E\u305B\u3093");
11921
11930
  const result = await codex.submit(channel.codex, deliveryId, text2);
11922
11931
  return { state: "submitted", queued_submission_id: result.queued_submission_id };
@@ -15836,8 +15845,8 @@ var StdioServerTransport = class {
15836
15845
  };
15837
15846
 
15838
15847
  // src/mcp.ts
15839
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
15840
- import { basename as basename3, join as join8 } from "node:path";
15848
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "node:fs";
15849
+ import { basename as basename3, join as join9 } from "node:path";
15841
15850
 
15842
15851
  // src/request-images.ts
15843
15852
  import { open } from "node:fs/promises";
@@ -15882,8 +15891,21 @@ async function uploadImagePaths(api, paths) {
15882
15891
  return ids;
15883
15892
  }
15884
15893
 
15894
+ // src/claude-channel.ts
15895
+ import { readFileSync as readFileSync6 } from "node:fs";
15896
+ import { join as join8 } from "node:path";
15897
+ function claudeChannelIsCurrent(profile, channelId, parent) {
15898
+ try {
15899
+ const bound = readChannel(profile, channelId).claude;
15900
+ const now = JSON.parse(readFileSync6(join8(parent.hook_root, parent.request_id, "request.json"), "utf8"));
15901
+ return !!bound && bound.session_id === parent.session_id && bound.parent_pid === now.parent_pid && bound.parent_started_identity === now.parent_started_identity;
15902
+ } catch {
15903
+ return false;
15904
+ }
15905
+ }
15906
+
15885
15907
  // src/version.ts
15886
- var VERSION = true ? "0.1.20" : "0.0.0-dev";
15908
+ var VERSION = true ? "0.1.22" : "0.0.0-dev";
15887
15909
  function osName() {
15888
15910
  return process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : process.platform;
15889
15911
  }
@@ -15995,12 +16017,12 @@ var TOOLS = [
15995
16017
  ];
15996
16018
  var VIEWABLE_IMAGES = ["image/jpeg", "image/png", "image/gif", "image/webp"];
15997
16019
  var MAX_INLINE_IMAGE = 5 * 1024 * 1024;
15998
- var parentsFile = () => join8(stateRoot(), "parents.json");
16020
+ var parentsFile = () => join9(stateRoot(), "parents.json");
15999
16021
  var processChannels = /* @__PURE__ */ new Map();
16000
16022
  var lastChannel;
16001
16023
  function savedParents() {
16002
16024
  try {
16003
- return existsSync6(parentsFile()) ? JSON.parse(readFileSync6(parentsFile(), "utf8")) : {};
16025
+ return existsSync6(parentsFile()) ? JSON.parse(readFileSync7(parentsFile(), "utf8")) : {};
16004
16026
  } catch {
16005
16027
  return {};
16006
16028
  }
@@ -16037,7 +16059,7 @@ async function channelFor(harness, clientName, meta) {
16037
16059
  if (!parent) throw new Error("Claude Code\u306Ehook\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002npx approval-box setup \u3092\u3084\u308A\u76F4\u3057\u3001Claude Code\u3092\u518D\u8D77\u52D5\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
16038
16060
  const key2 = `claude:${parent.session_id}`;
16039
16061
  const existing2 = reuse(key2, true);
16040
- if (existing2) return existing2;
16062
+ if (existing2 && claudeChannelIsCurrent(PROFILE, existing2, parent)) return existing2;
16041
16063
  const channel2 = openChannel(PROFILE, parent);
16042
16064
  remember(key2, channel2.channel_id, true);
16043
16065
  return channel2.channel_id;
@@ -16196,7 +16218,7 @@ ${JSON.stringify(decision)}`, decision);
16196
16218
  const head = `${decisionId} \u306E\u6DFB\u4ED8 ${index + 1}: ${meta.name}\uFF08${meta.content_type}\u3001${meta.size}\u30D0\u30A4\u30C8\uFF09\u3092\u4FDD\u5B58\u3057\u307E\u3057\u305F: ${path}`;
16197
16219
  const structured = { decision_id: decisionId, attachment: meta, path };
16198
16220
  if (VIEWABLE_IMAGES.includes(meta.content_type) && meta.size <= MAX_INLINE_IMAGE) {
16199
- return { content: [{ type: "text", text: head }, { type: "image", data: readFileSync6(path).toString("base64"), mimeType: meta.content_type }], structuredContent: structured };
16221
+ return { content: [{ type: "text", text: head }, { type: "image", data: readFileSync7(path).toString("base64"), mimeType: meta.content_type }], structuredContent: structured };
16200
16222
  }
16201
16223
  return text(head, structured);
16202
16224
  }
@@ -16262,13 +16284,13 @@ async function ask(question) {
16262
16284
  }
16263
16285
  }
16264
16286
  function installRuntime() {
16265
- const target = join9(home(), "runtime", VERSION);
16287
+ const target = join10(home(), "runtime", VERSION);
16266
16288
  if (runtimeDir() !== target) {
16267
16289
  rmSync3(target, { recursive: true, force: true });
16268
16290
  mkdirSync5(target, { recursive: true });
16269
16291
  cpSync(runtimeDir(), target, { recursive: true });
16270
16292
  }
16271
- return join9(target, "cli.mjs");
16293
+ return join10(target, "cli.mjs");
16272
16294
  }
16273
16295
  function selectedTargets() {
16274
16296
  const only = option("only");
@@ -16337,7 +16359,7 @@ async function setup() {
16337
16359
  out("\u6B21\u306EAI\u306BApproval Box\u3092\u767B\u9332\u3057\u307E\u3059\u3002\u66F8\u304D\u63DB\u3048\u308B\u30D5\u30A1\u30A4\u30EB:");
16338
16360
  for (const target of targets) out(` ${LABEL[target]}: ${filesOf(target).join(", ")}`);
16339
16361
  out("");
16340
- if (targets.includes("claude")) out("\u30FBClaude Code \u306E Stop hook \u306F\u3001\u3059\u3079\u3066\u306E\u4F1A\u8A71\u3067\u30BF\u30FC\u30F3\u304C\u7D42\u308F\u308B\u305F\u3073\u306B node \u30921\u56DE\u8D77\u52D5\u3057\u307E\u3059\uFF08Approval Box\u306E\u7533\u8ACB\u304C\u7121\u3051\u308C\u3070\u3059\u3050\u7D42\u308F\u308A\u307E\u3059\uFF09\u3002");
16362
+ if (targets.includes("claude")) out("\u30FBClaude Code \u306E Stop hook \u3068 SessionStart hook \u306F\u3001\u3059\u3079\u3066\u306E\u4F1A\u8A71\u3067\u3001\u30BF\u30FC\u30F3\u304C\u7D42\u308F\u308B\u305F\u3073\u30FB\u4F1A\u8A71\u304C\u59CB\u307E\u308B\u305F\u3073\u306B node \u30921\u56DE\u8D77\u52D5\u3057\u307E\u3059\uFF08Approval Box\u306E\u7533\u8ACB\u304C\u7121\u3051\u308C\u3070\u3059\u3050\u7D42\u308F\u308A\u307E\u3059\uFF09\u3002");
16341
16363
  if (targets.includes("codex")) out("\u30FBCodex \u306E\u7B54\u3048\u306F Aiterm \u306E Steer \u3067\u5143\u306E\u4F1A\u8A71\u3078\u8FD4\u3057\u307E\u3059\u3002\u65E7\u7248\u306EApproval Box\u5C02\u7528\u914D\u9001hook\u306F\u64A4\u53BB\u3057\u307E\u3059\u3002");
16342
16364
  if (targets.includes("cursor") || targets.includes("grok")) out("\u30FBCursor\uFF08\u6B62\u307E\u3063\u3066\u3044\u308B\u6642\uFF09\u3068 Grok \u306F\u3001\u7533\u8ACB\u306E\u6642\u306BAI\u304C\u80CC\u666F\u3067\u53D7\u4FE1\u3092\u8D77\u52D5\u3057\u307E\u3059\u3002");
16343
16365
  out(`\u30FB\u66F8\u304D\u63DB\u3048\u308B\u524D\u306E\u30D5\u30A1\u30A4\u30EB\u306F\u300C${".approval-box-backup"}\u300D\u3092\u4ED8\u3051\u3066\u63A7\u3048\u307E\u3059\u3002\u5143\u306B\u623B\u3059\u306B\u306F npx approval-box uninstall\u3002
@@ -16413,7 +16435,7 @@ async function uninstall() {
16413
16435
  out(` \u30B5\u30FC\u30D0\u30FC: \u63A5\u7D9A\u306E\u89E3\u9664\u306B\u5931\u6557\u3057\u307E\u3057\u305F\uFF08\u30A2\u30D7\u30EA\u306E\u300C\u63A5\u7D9A\u300D\u304B\u3089\u5916\u3057\u3066\u304F\u3060\u3055\u3044\uFF09: ${error.message}`);
16414
16436
  }
16415
16437
  }
16416
- rmSync3(join9(home(), "config.json"), { force: true });
16438
+ rmSync3(join10(home(), "config.json"), { force: true });
16417
16439
  out("\nApproval Box\u3092\u5916\u3057\u307E\u3057\u305F\u3002npm install -g \u3067\u5165\u308C\u305F\u5834\u5408\u306F\u3001npm uninstall -g approval-box \u3067\u672C\u4F53\u3082\u6D88\u305B\u307E\u3059\u3002");
16418
16440
  }
16419
16441
  async function status() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "approval-box",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "Approval Box connector: lets Claude Code, Codex, Cursor and Grok ask you for a decision, and steers your answer back into the same session.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://approval-box.kitepon.dev",
@@ -32,7 +32,7 @@
32
32
  "@modelcontextprotocol/sdk": "^1.31.0",
33
33
  "@types/node": "^25.9.1",
34
34
  "@types/qrcode": "^1.5.5",
35
- "aiterm-steer-delivery": "0.3.0",
35
+ "aiterm-steer-delivery": "0.4.4",
36
36
  "esbuild": "^0.28.2",
37
37
  "qrcode": "^1.5.4",
38
38
  "typescript": "^6.0.3",