approval-box 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,3 +35,14 @@ npx approval-box uninstall # remove everything and unlink this device / 全部
35
35
  Self-hosted server: `npx -y approval-box@latest setup --server https://your-server`.
36
36
 
37
37
  Requires Node.js 20 or later. License: MIT.
38
+
39
+ ### 会話を開き直した時
40
+
41
+ `list_my_decisions` で、自分が引き継ぐ申請を確かめ、対象ごとに
42
+ `resume_decision({decision_id: "K-…"})` を呼びます。未決・保留なら今の会話へ
43
+ 配送先を移し、回答済みなら答えを直接返します。同じ端末にある他の作業の申請は、
44
+ 引き継ぐ対象に含めないでください。`amend_decision` も、修正が成功した時に配送先を
45
+ 呼び出した会話へ更新します。Cursor・Grokでは、返る案内に従って背景の受信を起動します。
46
+
47
+ この機能はコネクタ0.1.13以降と対応サーバーを使います。会話を開き直しても、
48
+ 再開を指定するまでは元の会話が配送先です。答えは `get_decision` でも取得できます。
@@ -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.1.9 (MIT)
31
+ ## aiterm-steer-delivery@0.2.0 (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-S4DEUZZH.mjs";
5
+ } from "./chunks/chunk-GH2JG56Y.mjs";
6
6
 
7
7
  // src/hooks/claude.ts
8
8
  await runClaudeHookMain(PROFILE);
@@ -2,7 +2,7 @@ import { createRequire as __kbCreateRequire } from 'node:module'; const require
2
2
  import {
3
3
  PROFILE,
4
4
  runCodexHookMain
5
- } from "./chunks/chunk-S4DEUZZH.mjs";
5
+ } from "./chunks/chunk-GH2JG56Y.mjs";
6
6
 
7
7
  // src/hooks/codex.ts
8
8
  await runCodexHookMain(PROFILE, process.argv[2]);
@@ -5,7 +5,7 @@ import {
5
5
  handleCursorChannelHook,
6
6
  handleCursorHook,
7
7
  withoutBom
8
- } from "./chunks/chunk-S4DEUZZH.mjs";
8
+ } from "./chunks/chunk-GH2JG56Y.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-S4DEUZZH.mjs";
5
+ } from "./chunks/chunk-GH2JG56Y.mjs";
6
6
 
7
7
  // src/hooks/receive.ts
8
8
  await runChannelReceiveMain(PROFILE);
@@ -43,7 +43,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
43
43
  import { homedir } from "node:os";
44
44
  import { join } from "node:path";
45
45
  var MCP_SERVER = "approval-box";
46
- var DISPATCH_TOOLS = ["request_decision", "setup_test"];
46
+ var DISPATCH_TOOLS = ["request_decision", "setup_test", "amend_decision", "resume_decision"];
47
47
  var home = () => process.env.APPROVAL_BOX_HOME ?? join(homedir(), ".approval-box");
48
48
  var stateRoot = () => join(home(), "state");
49
49
  var HOOK_FILES = {
@@ -19822,7 +19822,7 @@ function writeHookJson(file2, value) {
19822
19822
  fs.unlinkSync(temporary);
19823
19823
  }
19824
19824
  }
19825
- function waitForFileState(dir, inspect, timeoutMs) {
19825
+ function waitForFileState(dir, inspect, timeoutMs, signal) {
19826
19826
  return new Promise((resolve2, reject) => {
19827
19827
  let finished = false;
19828
19828
  let timer;
@@ -19837,11 +19837,13 @@ function waitForFileState(dir, inspect, timeoutMs) {
19837
19837
  clearInterval(timer);
19838
19838
  if (timeout)
19839
19839
  clearTimeout(timeout);
19840
+ signal?.removeEventListener("abort", abort);
19840
19841
  if (error62)
19841
19842
  reject(error62);
19842
19843
  else
19843
19844
  resolve2(value);
19844
19845
  };
19846
+ const abort = () => finish(new Error("WAIT_FOR_FILE_ABORTED"));
19845
19847
  const check2 = () => {
19846
19848
  if (finished)
19847
19849
  return;
@@ -19857,12 +19859,17 @@ function waitForFileState(dir, inspect, timeoutMs) {
19857
19859
  timer = setInterval(check2, 5e3);
19858
19860
  if (timeoutMs !== void 0)
19859
19861
  timeout = setTimeout(() => finish(new Error("WAIT_FOR_FILE_TIMEOUT")), timeoutMs);
19862
+ if (signal?.aborted)
19863
+ abort();
19864
+ else
19865
+ signal?.addEventListener("abort", abort, { once: true });
19860
19866
  check2();
19861
19867
  });
19862
19868
  }
19863
19869
 
19864
19870
  // ../../node_modules/aiterm-steer-delivery/dist/windows.js
19865
19871
  import { execFileSync, spawnSync } from "node:child_process";
19872
+ import { realpathSync } from "node:fs";
19866
19873
  import * as path3 from "node:path";
19867
19874
  var WINDOWS_POWERSHELL_7_COMMAND = "pwsh.exe";
19868
19875
  var WINDOWS_POWERSHELL_7_INSTALL = "winget install --id Microsoft.PowerShell --source winget";
@@ -19899,9 +19906,15 @@ function resolveWindowsPowerShell7(probe = defaultProbe) {
19899
19906
  }
19900
19907
  if (version2.status !== 0 || identity?.edition !== "Core" || !Number.isInteger(identity.major) || Number(identity.major) < 7)
19901
19908
  fail();
19909
+ let canonical;
19910
+ try {
19911
+ canonical = realpathSync.native(executable);
19912
+ } catch {
19913
+ return fail();
19914
+ }
19902
19915
  if (probe === defaultProbe)
19903
- resolvedDefault = executable;
19904
- return executable;
19916
+ resolvedDefault = canonical;
19917
+ return canonical;
19905
19918
  }
19906
19919
  var quotePowerShell = (value) => `'${value.replace(/'/g, "''")}'`;
19907
19920
  function windowsPowerShellSync(script, onFailure, timeout = 15e3) {
@@ -20170,6 +20183,18 @@ function parsePosixProcessTable(text) {
20170
20183
  };
20171
20184
  });
20172
20185
  }
20186
+ var HOOK_SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "dash", "zsh", "ksh", "ash", "busybox", "pwsh", "powershell", "cmd"]);
20187
+ function executableName(row) {
20188
+ const source = row.executable || (row.command.startsWith('"') ? row.command.slice(1).split('"')[0] : row.command.split(/\s+/u)[0]);
20189
+ return (source.split(/[\\/]/u).pop() ?? "").replace(/^-/u, "").replace(/\.exe$/iu, "").toLowerCase();
20190
+ }
20191
+ function hookOwnerProcess(rows, pid) {
20192
+ const byPid = new Map(rows.map((row) => [row.pid, row]));
20193
+ let current = byPid.get(pid);
20194
+ for (let depth = 0; current && depth < 4 && HOOK_SHELLS.has(executableName(current)); depth += 1)
20195
+ current = byPid.get(current.parent_pid);
20196
+ return current;
20197
+ }
20173
20198
  function readRuntimeProcesses() {
20174
20199
  if (!isWin2) {
20175
20200
  const result2 = spawnSync3("/bin/ps", ["-axww", "-o", "pid=,ppid=,pgid=,stat=,lstart=,time=,command="], {
@@ -20595,8 +20620,8 @@ function assertOpen(parent) {
20595
20620
  }
20596
20621
  function prepareClaudeHookRequest(input2, root) {
20597
20622
  const event = external_exports.object({ session_id: external_exports.uuid(), tool_use_id: requestId, agent_id: external_exports.string().optional() }).parse(input2);
20598
- const identity = processIdentity(process.ppid);
20599
- if (!identity)
20623
+ const owner = hookOwnerProcess(readRuntimeProcesses(), process.ppid);
20624
+ if (!owner)
20600
20625
  throw new ClaudeDeliveryError("CLAUDE_PARENT_PROCESS_UNAVAILABLE", "hook\u3092\u8D77\u52D5\u3057\u305F\u89AAprocess\u3092\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093");
20601
20626
  const dir = path8.join(root, event.tool_use_id);
20602
20627
  fs5.mkdirSync(dir, { recursive: true, mode: 448 });
@@ -20604,8 +20629,8 @@ function prepareClaudeHookRequest(input2, root) {
20604
20629
  request_id: event.tool_use_id,
20605
20630
  session_id: event.session_id,
20606
20631
  agent_id: event.agent_id ?? null,
20607
- parent_pid: process.ppid,
20608
- parent_started_identity: identity
20632
+ parent_pid: owner.pid,
20633
+ parent_started_identity: owner.started_identity
20609
20634
  });
20610
20635
  }
20611
20636
  function claudeParentFromRequest(profile, clientName, metadata, root) {
@@ -20695,6 +20720,250 @@ async function runClaudeResultHook(profile, input2, emit, root) {
20695
20720
  import * as fs6 from "node:fs";
20696
20721
  import { homedir as homedir3 } from "node:os";
20697
20722
  import * as path9 from "node:path";
20723
+
20724
+ // ../../node_modules/aiterm-steer-delivery/dist/hook-setup.js
20725
+ import { copyFileSync, existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, readFileSync as readFileSync5, realpathSync as realpathSync4, renameSync as renameSync2, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "node:fs";
20726
+ import { dirname as dirname3 } from "node:path";
20727
+ import { randomUUID as randomUUID2 } from "node:crypto";
20728
+ import { isDeepStrictEqual } from "node:util";
20729
+ var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20730
+ function endsWithFile(value, file2) {
20731
+ return value === file2 || value.endsWith(`/${file2}`) || value.endsWith(`\\${file2}`);
20732
+ }
20733
+ function shellQuote(value, platform = process.platform) {
20734
+ if (platform === "win32")
20735
+ return `'${value.replace(/'/g, "''")}'`;
20736
+ return `'${value.replace(/'/g, `'"'"'`)}'`;
20737
+ }
20738
+ function claudeParentHookCommand(hook, platform = process.platform) {
20739
+ const words = `${shellQuote(hook.command, platform)} ${shellQuote(hook.script, platform)}`;
20740
+ if (platform === "win32")
20741
+ return { command: `& ${words}; exit (Get-Variable LASTEXITCODE -ValueOnly)`, shell: "powershell" };
20742
+ return { command: `exec ${words}` };
20743
+ }
20744
+ function claudeParentHookEntries(profile, hook, platform = process.platform) {
20745
+ const command2 = { type: "command", ...claudeParentHookCommand(hook, platform) };
20746
+ const matcher = `^mcp__${profile.mcp_server}__(${profile.dispatch_tools.join("|")})$`;
20747
+ return {
20748
+ PreToolUse: [{ matcher, hooks: [{ ...command2, timeout: 15 }] }],
20749
+ PostToolUse: [{ matcher, hooks: [{ ...command2, asyncRewake: true, timeout: 86400 }] }],
20750
+ // 長く続く受信(channel)では、turnが終わるたびに待機を張り直す。
20751
+ ...profile.channels ? { Stop: [{ hooks: [{ ...command2, asyncRewake: true, timeout: 86400 }] }] } : {},
20752
+ SessionEnd: [{ hooks: [{ ...command2, timeout: 15 }] }]
20753
+ };
20754
+ }
20755
+ function mergeClaudeParentHooks(profile, file2, hook) {
20756
+ const target = existsSync6(file2) ? realpathSync4(file2) : file2;
20757
+ let current = {};
20758
+ if (existsSync6(target)) {
20759
+ try {
20760
+ current = JSON.parse(readFileSync5(target, "utf8"));
20761
+ } catch {
20762
+ throw new SetupError("config_invalid", "Claude\u306Ehook\u8A2D\u5B9AJSON\u3092\u8AAD\u3081\u307E\u305B\u3093");
20763
+ }
20764
+ }
20765
+ if (!record2(current) || current.hooks !== void 0 && !record2(current.hooks)) {
20766
+ throw new SetupError("config_invalid", "Claude\u306Ehooks\u8A2D\u5B9A\u306Fobject\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059");
20767
+ }
20768
+ if (current.disableAllHooks === true)
20769
+ throw new SetupError("claude_parent_hooks_disabled", "Claude\u306Ehook\u304C\u7121\u52B9\u3067\u3059\u3002disableAllHooks\u3092false\u306B\u5909\u66F4\u3057\u3066\u304B\u3089setup\u3057\u3066\u304F\u3060\u3055\u3044");
20770
+ const hooks = { ...current.hooks };
20771
+ for (const [event, additions] of Object.entries(claudeParentHookEntries(profile, hook))) {
20772
+ const previous = hooks[event] ?? [];
20773
+ if (!Array.isArray(previous) || previous.some((group) => !record2(group) || !Array.isArray(group.hooks))) {
20774
+ throw new SetupError("config_invalid", `Claude\u306E${event} hook\u5F62\u5F0F\u3092\u8AAD\u3081\u307E\u305B\u3093`);
20775
+ }
20776
+ const owned = previous.flatMap((group) => group.hooks.filter((entry) => isClaudeParentHook(profile, entry)));
20777
+ if (owned.length === additions.flatMap((group) => group.hooks).length && additions.every((addition) => previous.some((group) => isDeepStrictEqual(group, addition))))
20778
+ continue;
20779
+ const retained = previous.map((group) => ({
20780
+ ...group,
20781
+ hooks: group.hooks.filter((entry) => !isClaudeParentHook(profile, entry))
20782
+ })).filter((group) => group.hooks.length > 0);
20783
+ hooks[event] = [...retained, ...additions];
20784
+ }
20785
+ const next = { ...current, hooks };
20786
+ if (isDeepStrictEqual(current, next))
20787
+ return "unchanged";
20788
+ mkdirSync5(dirname3(target), { recursive: true });
20789
+ const temporary = `${target}.${profile.id}-${randomUUID2()}`;
20790
+ try {
20791
+ writeFileSync3(temporary, `${JSON.stringify(next, null, 2)}
20792
+ `, { mode: 384, flag: "wx" });
20793
+ if (existsSync6(target))
20794
+ copyFileSync(target, `${target}${profile.backup_suffix}`);
20795
+ renameSync2(temporary, target);
20796
+ } finally {
20797
+ if (existsSync6(temporary))
20798
+ unlinkSync3(temporary);
20799
+ }
20800
+ if (!isDeepStrictEqual(JSON.parse(readFileSync5(target, "utf8")), next)) {
20801
+ throw new SetupError("config_readback_failed", "Claude\u306Ehook\u767B\u9332\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
20802
+ }
20803
+ return "configured";
20804
+ }
20805
+ function isClaudeParentHook(profile, hook) {
20806
+ if (!record2(hook) || hook.type !== "command")
20807
+ return false;
20808
+ if (Array.isArray(hook.args))
20809
+ return typeof hook.args[0] === "string" && endsWithFile(hook.args[0], profile.hooks.claude);
20810
+ return typeof hook.command === "string" && commandNamesFile(hook.command, profile.hooks.claude);
20811
+ }
20812
+ function ownedClaudeParentHooks(profile, document, event) {
20813
+ const hooks = record2(document) && record2(document.hooks) ? document.hooks[event] : void 0;
20814
+ if (!Array.isArray(hooks))
20815
+ return [];
20816
+ return hooks.flatMap((group) => record2(group) && Array.isArray(group.hooks) ? group.hooks : []).filter((entry) => isClaudeParentHook(profile, entry));
20817
+ }
20818
+ function claudeParentHooksRegistered(profile, document) {
20819
+ return Object.keys(claudeParentHookEntries(profile, { command: "", script: profile.hooks.claude })).every((event) => ownedClaudeParentHooks(profile, document, event).length > 0);
20820
+ }
20821
+ function removeClaudeParentHooks(profile, file2) {
20822
+ if (!existsSync6(file2))
20823
+ return "unchanged";
20824
+ const target = realpathSync4(file2);
20825
+ const current = JSON.parse(readFileSync5(target, "utf8"));
20826
+ if (!record2(current) || current.hooks !== void 0 && !record2(current.hooks)) {
20827
+ throw new SetupError("config_invalid", "Claude\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
20828
+ }
20829
+ if (current.hooks === void 0)
20830
+ return "unchanged";
20831
+ const hooks = { ...current.hooks };
20832
+ for (const event of ["PreToolUse", "PostToolUse", "Stop", "SessionEnd"]) {
20833
+ if (hooks[event] === void 0)
20834
+ continue;
20835
+ if (!Array.isArray(hooks[event]))
20836
+ throw new SetupError("config_invalid", "Claude\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
20837
+ hooks[event] = hooks[event].map((group) => {
20838
+ if (!record2(group) || !Array.isArray(group.hooks))
20839
+ throw new SetupError("config_invalid", "Claude\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
20840
+ return { ...group, hooks: group.hooks.filter((entry) => !isClaudeParentHook(profile, entry)) };
20841
+ }).filter((group) => group.hooks.length > 0);
20842
+ }
20843
+ const next = { ...current, hooks };
20844
+ if (isDeepStrictEqual(current, next))
20845
+ return "unchanged";
20846
+ const temporary = `${target}.${profile.id}-${randomUUID2()}`;
20847
+ try {
20848
+ writeFileSync3(temporary, `${JSON.stringify(next, null, 2)}
20849
+ `, { mode: 384, flag: "wx" });
20850
+ copyFileSync(target, `${target}${profile.backup_suffix}`);
20851
+ renameSync2(temporary, target);
20852
+ } finally {
20853
+ if (existsSync6(temporary))
20854
+ unlinkSync3(temporary);
20855
+ }
20856
+ if (!isDeepStrictEqual(JSON.parse(readFileSync5(target, "utf8")), next))
20857
+ throw new SetupError("config_readback_failed", "Claude hook\u89E3\u9664\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
20858
+ return "removed";
20859
+ }
20860
+ function cursorParentHookCommand(hook) {
20861
+ const command2 = `${shellQuote(hook.command)} ${shellQuote(hook.script)}`;
20862
+ return process.platform === "win32" ? `& ${command2}` : command2;
20863
+ }
20864
+ function commandNamesFile(command2, file2) {
20865
+ const escaped = file2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20866
+ return new RegExp(`(?:^|[\\\\/'"\\s])${escaped}(?:['"\\s]|$)`, "u").test(command2);
20867
+ }
20868
+ function ownsCursorParentHook(profile, hook) {
20869
+ return record2(hook) && typeof hook.command === "string" && commandNamesFile(hook.command, profile.hooks.cursor);
20870
+ }
20871
+ function mergeCursorParentHooks(profile, file2, hook) {
20872
+ const target = existsSync6(file2) ? realpathSync4(file2) : file2;
20873
+ let current = { version: 1, hooks: {} };
20874
+ if (existsSync6(target)) {
20875
+ try {
20876
+ current = JSON.parse(readFileSync5(target, "utf8"));
20877
+ } catch {
20878
+ throw new SetupError("config_invalid", "Cursor\u306Ehook\u8A2D\u5B9AJSON\u3092\u8AAD\u3081\u307E\u305B\u3093");
20879
+ }
20880
+ } else {
20881
+ try {
20882
+ if (lstatSync(file2).isSymbolicLink())
20883
+ throw new SetupError("config_invalid", "Cursor\u306Ehooks\u8A2D\u5B9Asymlink\u306E\u53C2\u7167\u5148\u304C\u3042\u308A\u307E\u305B\u3093");
20884
+ } catch (error62) {
20885
+ if (error62.code !== "ENOENT")
20886
+ throw error62;
20887
+ }
20888
+ }
20889
+ if (!record2(current) || current.hooks !== void 0 && !record2(current.hooks)) {
20890
+ throw new SetupError("config_invalid", "Cursor\u306Ehooks\u8A2D\u5B9A\u306Fobject\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059");
20891
+ }
20892
+ const hooks = { ...current.hooks };
20893
+ const entry = { command: cursorParentHookCommand(hook), timeout: 15 };
20894
+ for (const event of ["afterMCPExecution", "postToolUse"]) {
20895
+ const previous = hooks[event] ?? [];
20896
+ if (!Array.isArray(previous))
20897
+ throw new SetupError("config_invalid", `Cursor\u306E${event} hook\u5F62\u5F0F\u3092\u8AAD\u3081\u307E\u305B\u3093`);
20898
+ const retained = previous.filter((hook2) => !ownsCursorParentHook(profile, hook2));
20899
+ const owned = previous.filter((hook2) => ownsCursorParentHook(profile, hook2));
20900
+ if (owned.length === 1 && isDeepStrictEqual(owned[0], entry) && retained.length + 1 === previous.length)
20901
+ continue;
20902
+ const index = previous.findIndex((hook2) => ownsCursorParentHook(profile, hook2));
20903
+ hooks[event] = index < 0 ? [...retained, entry] : previous.map((hook2, i) => i === index ? entry : hook2).filter((hook2, i) => i === index || !ownsCursorParentHook(profile, hook2));
20904
+ }
20905
+ const next = { ...current, version: current.version ?? 1, hooks };
20906
+ if (isDeepStrictEqual(current, next))
20907
+ return "unchanged";
20908
+ mkdirSync5(dirname3(target), { recursive: true });
20909
+ const temporary = `${target}.${profile.id}-${randomUUID2()}`;
20910
+ try {
20911
+ writeFileSync3(temporary, `${JSON.stringify(next, null, 2)}
20912
+ `, { mode: 384, flag: "wx" });
20913
+ if (existsSync6(target))
20914
+ copyFileSync(target, `${target}${profile.backup_suffix}`);
20915
+ renameSync2(temporary, target);
20916
+ } finally {
20917
+ if (existsSync6(temporary))
20918
+ unlinkSync3(temporary);
20919
+ }
20920
+ if (!isDeepStrictEqual(JSON.parse(readFileSync5(target, "utf8")), next)) {
20921
+ throw new SetupError("config_readback_failed", "Cursor\u306Ehook\u767B\u9332\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
20922
+ }
20923
+ return "configured";
20924
+ }
20925
+ function removeCursorParentHooks(profile, file2) {
20926
+ if (!existsSync6(file2))
20927
+ return "unchanged";
20928
+ const target = realpathSync4(file2);
20929
+ const current = JSON.parse(readFileSync5(target, "utf8"));
20930
+ if (!record2(current) || current.hooks !== void 0 && !record2(current.hooks)) {
20931
+ throw new SetupError("config_invalid", "Cursor\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
20932
+ }
20933
+ if (current.hooks === void 0)
20934
+ return "unchanged";
20935
+ const hooks = { ...current.hooks };
20936
+ for (const event of ["afterMCPExecution", "postToolUse"]) {
20937
+ if (hooks[event] === void 0)
20938
+ continue;
20939
+ if (!Array.isArray(hooks[event]))
20940
+ throw new SetupError("config_invalid", "Cursor\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
20941
+ const retained = hooks[event].filter((hook) => !ownsCursorParentHook(profile, hook));
20942
+ if (retained.length)
20943
+ hooks[event] = retained;
20944
+ else
20945
+ delete hooks[event];
20946
+ }
20947
+ const next = { ...current, hooks };
20948
+ if (isDeepStrictEqual(current, next))
20949
+ return "unchanged";
20950
+ const temporary = `${target}.${profile.id}-${randomUUID2()}`;
20951
+ try {
20952
+ writeFileSync3(temporary, `${JSON.stringify(next, null, 2)}
20953
+ `, { mode: 384, flag: "wx" });
20954
+ copyFileSync(target, `${target}${profile.backup_suffix}`);
20955
+ renameSync2(temporary, target);
20956
+ } finally {
20957
+ if (existsSync6(temporary))
20958
+ unlinkSync3(temporary);
20959
+ }
20960
+ if (!isDeepStrictEqual(JSON.parse(readFileSync5(target, "utf8")), next)) {
20961
+ throw new SetupError("config_readback_failed", "Cursor hook\u89E3\u9664\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
20962
+ }
20963
+ return "removed";
20964
+ }
20965
+
20966
+ // ../../node_modules/aiterm-steer-delivery/dist/cursor-receiver.js
20698
20967
  var cursorParentSchema = external_exports.object({
20699
20968
  kind: external_exports.literal("cursor"),
20700
20969
  hook_root: external_exports.string()
@@ -20720,7 +20989,7 @@ function cursorParentHooksRegistered(profile, document) {
20720
20989
  if (entry === null || typeof entry !== "object")
20721
20990
  return false;
20722
20991
  const command2 = entry.command;
20723
- return typeof command2 === "string" && command2.includes(profile.hooks.cursor);
20992
+ return typeof command2 === "string" && commandNamesFile(command2, profile.hooks.cursor);
20724
20993
  });
20725
20994
  };
20726
20995
  return owns("postToolUse") && owns("afterMCPExecution");
@@ -20888,7 +21157,7 @@ function waitProcessCommandLine(wait, platform = process.platform) {
20888
21157
  // ../../node_modules/aiterm-steer-delivery/dist/channel.js
20889
21158
  import * as fs7 from "node:fs";
20890
21159
  import * as path10 from "node:path";
20891
- import { randomUUID as randomUUID2 } from "node:crypto";
21160
+ import { randomUUID as randomUUID3 } from "node:crypto";
20892
21161
  import { setTimeout as delay } from "node:timers/promises";
20893
21162
  var uuid3 = external_exports.uuid();
20894
21163
  var channelSchema = external_exports.object({
@@ -20914,7 +21183,7 @@ function mkdirs(...dirs) {
20914
21183
  fs7.mkdirSync(dir, { recursive: true, mode: 448 });
20915
21184
  }
20916
21185
  function openChannel(profile, parent) {
20917
- const channel_id = randomUUID2();
21186
+ const channel_id = randomUUID3();
20918
21187
  const dir = channelDir(profile, channel_id);
20919
21188
  const created_at = (/* @__PURE__ */ new Date()).toISOString();
20920
21189
  let channel;
@@ -20980,7 +21249,7 @@ async function sendToChannel(profile, channelId, deliveryId, text, runtime) {
20980
21249
  }
20981
21250
  const name = `${Date.now().toString().padStart(15, "0")}-${(sendSequence++).toString().padStart(9, "0")}-${deliveryId}.json`;
20982
21251
  const item = { delivery_id: deliveryId, text, at: (/* @__PURE__ */ new Date()).toISOString() };
20983
- const temporary = path10.join(dir, `.${deliveryId}.${randomUUID2()}.tmp`);
21252
+ const temporary = path10.join(dir, `.${deliveryId}.${randomUUID3()}.tmp`);
20984
21253
  fs7.writeFileSync(temporary, JSON.stringify(item) + "\n", { flag: "wx", mode: 384 });
20985
21254
  try {
20986
21255
  fs7.linkSync(temporary, path10.join(dir, "inbox", name));
@@ -21019,6 +21288,25 @@ function channelDeliveryState(profile, channelId, deliveryId) {
21019
21288
  }
21020
21289
  return fs7.readdirSync(path10.join(dir, "inbox")).some((name) => name.endsWith(`-${deliveryId}.json`)) ? "queued" : null;
21021
21290
  }
21291
+ function withdrawFromChannel(profile, channelId, deliveryId) {
21292
+ uuid3.parse(deliveryId);
21293
+ const dir = channelDir(profile, channelId);
21294
+ const inbox = path10.join(dir, "inbox");
21295
+ const name = fs7.existsSync(inbox) ? fs7.readdirSync(inbox).find((entry) => entry.endsWith(`-${deliveryId}.json`)) : void 0;
21296
+ if (!name)
21297
+ return false;
21298
+ const claim3 = path10.join(dir, "claims", `${deliveryId}.json`);
21299
+ try {
21300
+ fs7.linkSync(path10.join(inbox, name), claim3);
21301
+ fs7.unlinkSync(path10.join(inbox, name));
21302
+ } catch (error62) {
21303
+ if (["ENOENT", "EEXIST"].includes(error62.code ?? ""))
21304
+ return false;
21305
+ throw error62;
21306
+ }
21307
+ writeJson0600(claim3, { delivery_id: deliveryId, state: "withdrawn", at: (/* @__PURE__ */ new Date()).toISOString() });
21308
+ return true;
21309
+ }
21022
21310
  function claimPending(profile, channelId, by, limit = Infinity) {
21023
21311
  const dir = channelDir(profile, channelId);
21024
21312
  const inbox = path10.join(dir, "inbox");
@@ -21111,7 +21399,7 @@ function acquireWaiter(index) {
21111
21399
  if (typeof owner.pid === "number" && typeof owner.started_identity === "string" && processIdentity2(owner.pid) === owner.started_identity)
21112
21400
  return null;
21113
21401
  try {
21114
- fs7.renameSync(file2, path10.join(index, `waiter.${randomUUID2()}.stale`));
21402
+ fs7.renameSync(file2, path10.join(index, `waiter.${randomUUID3()}.stale`));
21115
21403
  } catch {
21116
21404
  }
21117
21405
  for (const name of fs7.readdirSync(index).filter((entry) => entry.endsWith(".stale")))
@@ -21313,6 +21601,8 @@ async function runCodexHookMain(profile, directory2 = process.argv[2]) {
21313
21601
  async function runClaudeHookMain(profile, root = claudeHookRoot(profile)) {
21314
21602
  try {
21315
21603
  const event = JSON.parse(await readStdin());
21604
+ if (event !== null && typeof event === "object" && typeof event.hookEventName === "string")
21605
+ return;
21316
21606
  const write = (text) => new Promise((resolve2, reject) => {
21317
21607
  process.stderr.write(text, (error62) => error62 ? reject(error62) : resolve2());
21318
21608
  });
@@ -21383,7 +21673,7 @@ function setupNodeExecutable(executable = process.execPath) {
21383
21673
  // ../../node_modules/aiterm-steer-delivery/dist/codex-setup.js
21384
21674
  import * as fs8 from "node:fs";
21385
21675
  import * as path11 from "node:path";
21386
- import { isDeepStrictEqual } from "node:util";
21676
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
21387
21677
  function codexHookCommand(node2, hook, directory2, platform = process.platform, powershell = () => resolveWindowsPowerShell7()) {
21388
21678
  if (platform === "win32") {
21389
21679
  const script = `& ${[node2, hook, directory2].map(quotePowerShell).join(" ")}; exit $LASTEXITCODE`;
@@ -21453,7 +21743,7 @@ function planCodexParentHooks(file2, command2, previousCommand) {
21453
21743
  delete hooks[event];
21454
21744
  }
21455
21745
  const next = { ...current, hooks };
21456
- return { target, current, next, changed: !isDeepStrictEqual(current, next), moves, owned: ownedAt };
21746
+ return { target, current, next, changed: !isDeepStrictEqual2(current, next), moves, owned: ownedAt };
21457
21747
  }
21458
21748
  function writeCodexHookPlan(profile, plan) {
21459
21749
  if (!plan.changed)
@@ -21461,7 +21751,7 @@ function writeCodexHookPlan(profile, plan) {
21461
21751
  if (fs8.existsSync(plan.target))
21462
21752
  fs8.copyFileSync(plan.target, `${plan.target}${profile.backup_suffix}`);
21463
21753
  writeHookJson(plan.target, plan.next);
21464
- if (!isDeepStrictEqual(JSON.parse(fs8.readFileSync(plan.target, "utf8")), plan.next))
21754
+ if (!isDeepStrictEqual2(JSON.parse(fs8.readFileSync(plan.target, "utf8")), plan.next))
21465
21755
  throw new SetupError("codex_hook_readback_failed", "Codex hook\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
21466
21756
  return true;
21467
21757
  }
@@ -21657,222 +21947,6 @@ async function configureCodexSteer(profile, action = "status", overrides) {
21657
21947
  return needsRestart(config2) ? { status: "restart_required", reason_code: "codex_restart_required" } : { status: "ready" };
21658
21948
  }
21659
21949
 
21660
- // ../../node_modules/aiterm-steer-delivery/dist/hook-setup.js
21661
- import { copyFileSync as copyFileSync2, existsSync as existsSync9, lstatSync as lstatSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync8, realpathSync as realpathSync4, renameSync as renameSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "node:fs";
21662
- import { dirname as dirname4 } from "node:path";
21663
- import { randomUUID as randomUUID3 } from "node:crypto";
21664
- import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
21665
- var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21666
- function endsWithFile(value, file2) {
21667
- return value === file2 || value.endsWith(`/${file2}`) || value.endsWith(`\\${file2}`);
21668
- }
21669
- function claudeParentHookEntries(profile, hook) {
21670
- const command2 = { type: "command", command: hook.command, args: [hook.script] };
21671
- const matcher = `^mcp__${profile.mcp_server}__(${profile.dispatch_tools.join("|")})$`;
21672
- return {
21673
- PreToolUse: [{ matcher, hooks: [{ ...command2, timeout: 15 }] }],
21674
- PostToolUse: [{ matcher, hooks: [{ ...command2, asyncRewake: true, timeout: 86400 }] }],
21675
- // 長く続く受信(channel)では、turnが終わるたびに待機を張り直す。
21676
- ...profile.channels ? { Stop: [{ hooks: [{ ...command2, asyncRewake: true, timeout: 86400 }] }] } : {},
21677
- SessionEnd: [{ hooks: [{ ...command2, timeout: 15 }] }]
21678
- };
21679
- }
21680
- function mergeClaudeParentHooks(profile, file2, hook) {
21681
- const target = existsSync9(file2) ? realpathSync4(file2) : file2;
21682
- let current = {};
21683
- if (existsSync9(target)) {
21684
- try {
21685
- current = JSON.parse(readFileSync8(target, "utf8"));
21686
- } catch {
21687
- throw new SetupError("config_invalid", "Claude\u306Ehook\u8A2D\u5B9AJSON\u3092\u8AAD\u3081\u307E\u305B\u3093");
21688
- }
21689
- }
21690
- if (!record2(current) || current.hooks !== void 0 && !record2(current.hooks)) {
21691
- throw new SetupError("config_invalid", "Claude\u306Ehooks\u8A2D\u5B9A\u306Fobject\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059");
21692
- }
21693
- if (current.disableAllHooks === true)
21694
- throw new SetupError("claude_parent_hooks_disabled", "Claude\u306Ehook\u304C\u7121\u52B9\u3067\u3059\u3002disableAllHooks\u3092false\u306B\u5909\u66F4\u3057\u3066\u304B\u3089setup\u3057\u3066\u304F\u3060\u3055\u3044");
21695
- const hooks = { ...current.hooks };
21696
- for (const [event, additions] of Object.entries(claudeParentHookEntries(profile, hook))) {
21697
- const previous = hooks[event] ?? [];
21698
- if (!Array.isArray(previous) || previous.some((group) => !record2(group) || !Array.isArray(group.hooks))) {
21699
- throw new SetupError("config_invalid", `Claude\u306E${event} hook\u5F62\u5F0F\u3092\u8AAD\u3081\u307E\u305B\u3093`);
21700
- }
21701
- const retained = previous.map((group) => ({
21702
- ...group,
21703
- hooks: group.hooks.filter((entry) => !isClaudeParentHook(profile, entry))
21704
- })).filter((group) => group.hooks.length > 0);
21705
- hooks[event] = [...retained, ...additions];
21706
- }
21707
- const next = { ...current, hooks };
21708
- if (isDeepStrictEqual2(current, next))
21709
- return "unchanged";
21710
- mkdirSync7(dirname4(target), { recursive: true });
21711
- const temporary = `${target}.${profile.id}-${randomUUID3()}`;
21712
- try {
21713
- writeFileSync5(temporary, `${JSON.stringify(next, null, 2)}
21714
- `, { mode: 384, flag: "wx" });
21715
- if (existsSync9(target))
21716
- copyFileSync2(target, `${target}${profile.backup_suffix}`);
21717
- renameSync3(temporary, target);
21718
- } finally {
21719
- if (existsSync9(temporary))
21720
- unlinkSync4(temporary);
21721
- }
21722
- if (!isDeepStrictEqual2(JSON.parse(readFileSync8(target, "utf8")), next)) {
21723
- throw new SetupError("config_readback_failed", "Claude\u306Ehook\u767B\u9332\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
21724
- }
21725
- return "configured";
21726
- }
21727
- function isClaudeParentHook(profile, hook) {
21728
- return record2(hook) && hook.type === "command" && Array.isArray(hook.args) && typeof hook.args[0] === "string" && endsWithFile(hook.args[0], profile.hooks.claude);
21729
- }
21730
- function removeClaudeParentHooks(profile, file2) {
21731
- if (!existsSync9(file2))
21732
- return "unchanged";
21733
- const target = realpathSync4(file2);
21734
- const current = JSON.parse(readFileSync8(target, "utf8"));
21735
- if (!record2(current) || current.hooks !== void 0 && !record2(current.hooks)) {
21736
- throw new SetupError("config_invalid", "Claude\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
21737
- }
21738
- if (current.hooks === void 0)
21739
- return "unchanged";
21740
- const hooks = { ...current.hooks };
21741
- for (const event of ["PreToolUse", "PostToolUse", "Stop", "SessionEnd"]) {
21742
- if (hooks[event] === void 0)
21743
- continue;
21744
- if (!Array.isArray(hooks[event]))
21745
- throw new SetupError("config_invalid", "Claude\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
21746
- hooks[event] = hooks[event].map((group) => {
21747
- if (!record2(group) || !Array.isArray(group.hooks))
21748
- throw new SetupError("config_invalid", "Claude\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
21749
- return { ...group, hooks: group.hooks.filter((entry) => !isClaudeParentHook(profile, entry)) };
21750
- }).filter((group) => group.hooks.length > 0);
21751
- }
21752
- const next = { ...current, hooks };
21753
- if (isDeepStrictEqual2(current, next))
21754
- return "unchanged";
21755
- const temporary = `${target}.${profile.id}-${randomUUID3()}`;
21756
- try {
21757
- writeFileSync5(temporary, `${JSON.stringify(next, null, 2)}
21758
- `, { mode: 384, flag: "wx" });
21759
- copyFileSync2(target, `${target}${profile.backup_suffix}`);
21760
- renameSync3(temporary, target);
21761
- } finally {
21762
- if (existsSync9(temporary))
21763
- unlinkSync4(temporary);
21764
- }
21765
- if (!isDeepStrictEqual2(JSON.parse(readFileSync8(target, "utf8")), next))
21766
- throw new SetupError("config_readback_failed", "Claude hook\u89E3\u9664\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
21767
- return "removed";
21768
- }
21769
- function shellQuote(value) {
21770
- if (process.platform === "win32")
21771
- return `'${value.replace(/'/g, "''")}'`;
21772
- return `'${value.replace(/'/g, `'"'"'`)}'`;
21773
- }
21774
- function cursorParentHookCommand(hook) {
21775
- const command2 = `${shellQuote(hook.command)} ${shellQuote(hook.script)}`;
21776
- return process.platform === "win32" ? `& ${command2}` : command2;
21777
- }
21778
- function ownsCursorParentHook(profile, hook) {
21779
- return record2(hook) && typeof hook.command === "string" && hook.command.includes(profile.hooks.cursor);
21780
- }
21781
- function mergeCursorParentHooks(profile, file2, hook) {
21782
- const target = existsSync9(file2) ? realpathSync4(file2) : file2;
21783
- let current = { version: 1, hooks: {} };
21784
- if (existsSync9(target)) {
21785
- try {
21786
- current = JSON.parse(readFileSync8(target, "utf8"));
21787
- } catch {
21788
- throw new SetupError("config_invalid", "Cursor\u306Ehook\u8A2D\u5B9AJSON\u3092\u8AAD\u3081\u307E\u305B\u3093");
21789
- }
21790
- } else {
21791
- try {
21792
- if (lstatSync2(file2).isSymbolicLink())
21793
- throw new SetupError("config_invalid", "Cursor\u306Ehooks\u8A2D\u5B9Asymlink\u306E\u53C2\u7167\u5148\u304C\u3042\u308A\u307E\u305B\u3093");
21794
- } catch (error62) {
21795
- if (error62.code !== "ENOENT")
21796
- throw error62;
21797
- }
21798
- }
21799
- if (!record2(current) || current.hooks !== void 0 && !record2(current.hooks)) {
21800
- throw new SetupError("config_invalid", "Cursor\u306Ehooks\u8A2D\u5B9A\u306Fobject\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059");
21801
- }
21802
- const hooks = { ...current.hooks };
21803
- const entry = { command: cursorParentHookCommand(hook), timeout: 15 };
21804
- for (const event of ["afterMCPExecution", "postToolUse"]) {
21805
- const previous = hooks[event] ?? [];
21806
- if (!Array.isArray(previous))
21807
- throw new SetupError("config_invalid", `Cursor\u306E${event} hook\u5F62\u5F0F\u3092\u8AAD\u3081\u307E\u305B\u3093`);
21808
- const retained = previous.filter((hook2) => !ownsCursorParentHook(profile, hook2));
21809
- const owned = previous.filter((hook2) => ownsCursorParentHook(profile, hook2));
21810
- if (owned.length === 1 && isDeepStrictEqual2(owned[0], entry) && retained.length + 1 === previous.length)
21811
- continue;
21812
- const index = previous.findIndex((hook2) => ownsCursorParentHook(profile, hook2));
21813
- hooks[event] = index < 0 ? [...retained, entry] : previous.map((hook2, i) => i === index ? entry : hook2).filter((hook2, i) => i === index || !ownsCursorParentHook(profile, hook2));
21814
- }
21815
- const next = { ...current, version: current.version ?? 1, hooks };
21816
- if (isDeepStrictEqual2(current, next))
21817
- return "unchanged";
21818
- mkdirSync7(dirname4(target), { recursive: true });
21819
- const temporary = `${target}.${profile.id}-${randomUUID3()}`;
21820
- try {
21821
- writeFileSync5(temporary, `${JSON.stringify(next, null, 2)}
21822
- `, { mode: 384, flag: "wx" });
21823
- if (existsSync9(target))
21824
- copyFileSync2(target, `${target}${profile.backup_suffix}`);
21825
- renameSync3(temporary, target);
21826
- } finally {
21827
- if (existsSync9(temporary))
21828
- unlinkSync4(temporary);
21829
- }
21830
- if (!isDeepStrictEqual2(JSON.parse(readFileSync8(target, "utf8")), next)) {
21831
- throw new SetupError("config_readback_failed", "Cursor\u306Ehook\u767B\u9332\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
21832
- }
21833
- return "configured";
21834
- }
21835
- function removeCursorParentHooks(profile, file2) {
21836
- if (!existsSync9(file2))
21837
- return "unchanged";
21838
- const target = realpathSync4(file2);
21839
- const current = JSON.parse(readFileSync8(target, "utf8"));
21840
- if (!record2(current) || current.hooks !== void 0 && !record2(current.hooks)) {
21841
- throw new SetupError("config_invalid", "Cursor\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
21842
- }
21843
- if (current.hooks === void 0)
21844
- return "unchanged";
21845
- const hooks = { ...current.hooks };
21846
- for (const event of ["afterMCPExecution", "postToolUse"]) {
21847
- if (hooks[event] === void 0)
21848
- continue;
21849
- if (!Array.isArray(hooks[event]))
21850
- throw new SetupError("config_invalid", "Cursor\u306Ehook\u8A2D\u5B9A\u3092\u8AAD\u3081\u307E\u305B\u3093");
21851
- const retained = hooks[event].filter((hook) => !ownsCursorParentHook(profile, hook));
21852
- if (retained.length)
21853
- hooks[event] = retained;
21854
- else
21855
- delete hooks[event];
21856
- }
21857
- const next = { ...current, hooks };
21858
- if (isDeepStrictEqual2(current, next))
21859
- return "unchanged";
21860
- const temporary = `${target}.${profile.id}-${randomUUID3()}`;
21861
- try {
21862
- writeFileSync5(temporary, `${JSON.stringify(next, null, 2)}
21863
- `, { mode: 384, flag: "wx" });
21864
- copyFileSync2(target, `${target}${profile.backup_suffix}`);
21865
- renameSync3(temporary, target);
21866
- } finally {
21867
- if (existsSync9(temporary))
21868
- unlinkSync4(temporary);
21869
- }
21870
- if (!isDeepStrictEqual2(JSON.parse(readFileSync8(target, "utf8")), next)) {
21871
- throw new SetupError("config_readback_failed", "Cursor hook\u89E3\u9664\u306E\u8AAD\u623B\u3057\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093");
21872
- }
21873
- return "removed";
21874
- }
21875
-
21876
21950
  export {
21877
21951
  __require,
21878
21952
  __commonJS,
@@ -21910,21 +21984,23 @@ export {
21910
21984
  verifyCodexParent,
21911
21985
  configureCodexSteer,
21912
21986
  claudeParentFromRequest,
21913
- isCursorMcpClient,
21914
- cursorParentHooksRegistered,
21915
- verifyCursorParent,
21916
- handleCursorHook,
21917
- waitProcessCommandLine,
21918
21987
  mergeClaudeParentHooks,
21988
+ claudeParentHooksRegistered,
21919
21989
  removeClaudeParentHooks,
21920
21990
  cursorParentHookCommand,
21921
21991
  mergeCursorParentHooks,
21922
21992
  removeCursorParentHooks,
21993
+ isCursorMcpClient,
21994
+ cursorParentHooksRegistered,
21995
+ verifyCursorParent,
21996
+ handleCursorHook,
21997
+ waitProcessCommandLine,
21923
21998
  openChannel,
21924
21999
  readChannel,
21925
22000
  channelClosed,
21926
22001
  sendToChannel,
21927
22002
  channelDeliveryState,
22003
+ withdrawFromChannel,
21928
22004
  channelMarker,
21929
22005
  handleCursorChannelHook,
21930
22006
  channelReceiveProcess,
package/dist/cli.mjs CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  channelReceiveProcess,
18
18
  claudeHookRoot,
19
19
  claudeParentFromRequest,
20
+ claudeParentHooksRegistered,
20
21
  codexParentFromRequest,
21
22
  configureCodexSteer,
22
23
  cursorHookRoot,
@@ -51,8 +52,9 @@ import {
51
52
  verifyCodexParent,
52
53
  verifyCursorParent,
53
54
  waitProcessCommandLine,
54
- withCodexReceiver
55
- } from "./chunks/chunk-S4DEUZZH.mjs";
55
+ withCodexReceiver,
56
+ withdrawFromChannel
57
+ } from "./chunks/chunk-GH2JG56Y.mjs";
56
58
 
57
59
  // ../../node_modules/qrcode/lib/can-promise.js
58
60
  var require_can_promise = __commonJS({
@@ -12074,7 +12076,24 @@ async function runDaemon() {
12074
12076
  again = false;
12075
12077
  try {
12076
12078
  const { items } = await api.call("GET", "/deliveries");
12077
- for (const item of items) await deliver(item);
12079
+ const waiting = new Map(items.map((item) => [item.decision_id, item]));
12080
+ for (const [id, entry] of Object.entries(journal)) {
12081
+ if (entry.state !== "queued") continue;
12082
+ const item = waiting.get(id);
12083
+ if (item && item.delivery_id === entry.delivery_id && item.route.channel_id === entry.channel_id) continue;
12084
+ const withdrawn = withdrawFromChannel(PROFILE, entry.channel_id, entry.delivery_id);
12085
+ if (withdrawn || !item) {
12086
+ delete journal[id];
12087
+ save();
12088
+ log(`\u914D\u9001 ${id} \u2192 ${withdrawn ? "withdrawn" : "resolved"}`);
12089
+ } else {
12090
+ Object.assign(entry, safeState(entry) === "emitted" ? { state: "report", result: "delivered" } : { state: "report", result: "unknown", detail: "\u914D\u9001\u5148\u306E\u5909\u66F4\u524D\u306B\u53D7\u3051\u53E3\u304C\u672C\u6587\u3092\u53D6\u5F97\u3057\u307E\u3057\u305F\u3002\u518D\u9001\u3057\u307E\u305B\u3093" });
12091
+ save();
12092
+ await report(id);
12093
+ waiting.delete(id);
12094
+ }
12095
+ }
12096
+ for (const item of waiting.values()) await deliver(item);
12078
12097
  } catch (error) {
12079
12098
  log(`\u914D\u9001\u5F85\u3061\u306E\u53D6\u5F97\u306B\u5931\u6557: ${error.message}`);
12080
12099
  }
@@ -12247,7 +12266,7 @@ function stableNode() {
12247
12266
  return process.execPath;
12248
12267
  }
12249
12268
  }
12250
- var CURSOR_TOOLS = ["list_my_decisions", "request_decision", "amend_decision", "cancel_decision", "get_decision", "get_attachment", "setup_test", "confirm_setup_test"];
12269
+ var CURSOR_TOOLS = ["list_my_decisions", "request_decision", "amend_decision", "resume_decision", "cancel_decision", "get_decision", "get_attachment", "setup_test", "confirm_setup_test"];
12251
12270
  var cursorCliConfig = () => join6(cursorDir(), "cli-config.json");
12252
12271
  function setCursorPermissions(enable) {
12253
12272
  const file = cursorCliConfig();
@@ -12389,9 +12408,9 @@ function registeredTools(target) {
12389
12408
  const entryOf = (value) => (value?.args ?? [])[0];
12390
12409
  if (target === "claude") {
12391
12410
  const entry = readJson(claudeJson()).mcpServers?.[MCP_SERVER];
12392
- const settings = JSON.stringify(readJson(join6(claudeDir(), "settings.json")));
12411
+ const settings = readJson(join6(claudeDir(), "settings.json"));
12393
12412
  const e = entryOf(entry);
12394
- return { mcp: !!entry, hooks: settings.includes(PROFILE.hooks.claude), ...e ? { entry: e } : {} };
12413
+ return { mcp: !!entry, hooks: claudeParentHooksRegistered(PROFILE, settings), ...e ? { entry: e } : {} };
12395
12414
  }
12396
12415
  if (target === "cursor") {
12397
12416
  const entry = readJson(join6(cursorDir(), "mcp.json")).mcpServers?.[MCP_SERVER];
@@ -15770,7 +15789,7 @@ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:f
15770
15789
  import { basename as basename2, join as join7 } from "node:path";
15771
15790
 
15772
15791
  // src/version.ts
15773
- var VERSION = true ? "0.1.12" : "0.0.0-dev";
15792
+ var VERSION = true ? "0.1.14" : "0.0.0-dev";
15774
15793
  function osName() {
15775
15794
  return process.platform === "darwin" ? "macos" : process.platform === "win32" ? "windows" : process.platform;
15776
15795
  }
@@ -15786,6 +15805,7 @@ function harnessOf(clientName) {
15786
15805
  return "other";
15787
15806
  }
15788
15807
  var INSTRUCTIONS = [
15808
+ "\u4F1A\u8A71\u304C\u66FF\u308F\u3063\u305F\u3089 list_my_decisions \u3067\u81EA\u5206\u304C\u5F15\u304D\u7D99\u3050\u7533\u8ACB\u3092\u78BA\u304B\u3081\u3001\u7533\u8ACB\u3054\u3068\u306B resume_decision \u3092\u547C\u3076\u3002\u672A\u6C7A\u30FB\u4FDD\u7559\u306E\u7B54\u3048\u306F\u65B0\u3057\u3044\u4F1A\u8A71\u3078\u5C4A\u304D\u3001\u56DE\u7B54\u6E08\u307F\u306A\u3089\u305D\u306E\u5834\u3067\u7B54\u3048\u304C\u8FD4\u308B\u3002",
15789
15809
  "Approval Box\u306F\u3001\u5229\u7528\u8005\uFF08\u4EBA\u9593\uFF09\u306E\u5224\u65AD\u30FB\u627F\u8A8D\u30FB\u64CD\u4F5C\u304C\u8981\u308B\u6642\u306B\u4F7F\u3046\u3002URL\u3092\u958B\u3044\u3066\u306E\u627F\u8A8D\u3084\u3001\u5229\u7528\u8005\u306E\u624B\u3067\u884C\u3046\u4F5C\u696D\u3092\u983C\u3080\u6642\u3082\u542B\u3080\u3002\u305D\u306E\u6642\u306F request_decision \u3067\u3082\u7533\u8ACB\u3059\u308B\u3002\u5229\u7528\u8005\u304C\u7533\u8ACB\u306B\u6C17\u3065\u304D\u3084\u3059\u3044\u306E\u3067\u3001\u5F37\u304F\u63A8\u5968\u3059\u308B\u3002\u7B54\u3048\u306F\u5229\u7528\u8005\u304C\u30B9\u30DE\u30DB\u3084Web\u3067\u51FA\u3057\u3001\u3053\u306E\u4F1A\u8A71\u3078\u81EA\u52D5\u3067\u5C4A\u304F\u3002",
15790
15810
  "request_decision \u3092\u547C\u3076\u3068\u3001\u7533\u8ACB\u306F\u3059\u3050\u306B\u306F\u53D7\u3051\u4ED8\u3051\u3089\u308C\u305A\u3001\u81EA\u5206\u306E\u7533\u8ACB\u306E\u4E00\u89A7\u3068 check_token \u304C\u8FD4\u308B\u3002\u4E00\u89A7\u3092\u898B\u3066\u3001\u5185\u5BB9\u304C\u5909\u308F\u3063\u305F\u7533\u8ACB\u306F amend_decision\u3001\u8981\u3089\u306A\u304F\u306A\u3063\u305F\u7533\u8ACB\u306F cancel_decision \u3067\u5148\u306B\u6E08\u307E\u305B\u308B\u3002\u7B54\u3048\u304C\u51FA\u3066\u3044\u308C\u3070\u3001\u305D\u306E\u7B54\u3048\u306B\u5F93\u3046\u3002\u554F\u984C\u304C\u7121\u3051\u308C\u3070\u3001\u540C\u3058\u7533\u8ACB\u306B check_token \u3092\u4ED8\u3051\u3066\u51FA\u3057\u76F4\u3059\u3068\u53D7\u3051\u4ED8\u3051\u3089\u308C\u308B\u3002",
15791
15811
  "\u7533\u8ACB\u3057\u305F\u3089\u7B54\u3048\u3092\u5F85\u3063\u3066\u4F55\u5EA6\u3082\u547C\u3070\u306A\u3044\u3002\u7B54\u3048\u306F\u5C4A\u304F\u306E\u3067\u3001\u4ED6\u306E\u4F5C\u696D\u3092\u7D9A\u3051\u308B\u304B\u30BF\u30FC\u30F3\u3092\u7D42\u3048\u3066\u3088\u3044\u3002",
@@ -15844,6 +15864,11 @@ var TOOLS = [
15844
15864
  additionalProperties: false
15845
15865
  }
15846
15866
  },
15867
+ {
15868
+ name: "resume_decision",
15869
+ description: "\u524D\u306E\u4F1A\u8A71\u304B\u3089\u5F15\u304D\u7D99\u3044\u3060\u81EA\u5206\u306E\u7533\u8ACB\u3092\u3001\u3053\u306E\u4F1A\u8A71\u3067\u518D\u958B\u3059\u308B\u3002\u672A\u6C7A\u30FB\u4FDD\u7559\u306A\u3089\u7B54\u3048\u306E\u914D\u9001\u5148\u3092\u3053\u306E\u4F1A\u8A71\u3078\u5909\u66F4\u3057\u3001\u56DE\u7B54\u6E08\u307F\u306A\u3089\u7B54\u3048\u3092\u76F4\u63A5\u8FD4\u3059\u3002list_my_decisions\u3067\u5BFE\u8C61\u3092\u78BA\u304B\u3081\u3001\u5F15\u304D\u7D99\u3050\u7533\u8ACB\u3054\u3068\u306B\u547C\u3076\u3002",
15870
+ inputSchema: { type: "object", properties: { decision_id: { type: "string" } }, required: ["decision_id"], additionalProperties: false }
15871
+ },
15847
15872
  {
15848
15873
  name: "cancel_decision",
15849
15874
  description: "\u672A\u6C7A\u30FB\u4FDD\u7559\u306E\u81EA\u5206\u306E\u7533\u8ACB\u3092\u53D6\u308A\u4E0B\u3052\u308B\u3002reason \u306B\u7406\u7531\u3092\u66F8\u304F\u3002",
@@ -16028,10 +16053,21 @@ ${JSON.stringify({ error: "duplicate_suspected", existing })}`, { error: "duplic
16028
16053
  }
16029
16054
  if (name === "amend_decision") {
16030
16055
  const { decision_id, ...rest } = args;
16031
- const decision = await api.call("POST", `/decisions/${encodeURIComponent(String(decision_id))}/amend`, rest);
16032
- return text(`\u76F4\u3057\u307E\u3057\u305F: ${decision.decision_id} version=${decision.version}
16056
+ const channelId = await channelFor(harness, clientName, request.params._meta);
16057
+ const decision = await api.call("POST", `/decisions/${encodeURIComponent(String(decision_id))}/amend`, { ...rest, route: { channel_id: channelId, harness } });
16058
+ lastChannel = channelId;
16059
+ return text(`\u76F4\u3057\u307E\u3057\u305F: ${decision.decision_id} version=${decision.version}${receiveGuide(harness, channelId)}
16033
16060
 
16034
- ${JSON.stringify(decision)}`, decision);
16061
+ ${JSON.stringify(decision)}`, { ...decision, steer_channel: { channel_id: channelId } });
16062
+ }
16063
+ if (name === "resume_decision") {
16064
+ const channelId = await channelFor(harness, clientName, request.params._meta);
16065
+ const decision = await api.call("POST", `/decisions/${encodeURIComponent(String(args.decision_id))}/resume`, { channel_id: channelId, harness });
16066
+ lastChannel = channelId;
16067
+ const head = decision.answer_text ? String(decision.answer_text) : `\u518D\u958B\u3057\u307E\u3057\u305F: ${decision.decision_id}\u3002\u7B54\u3048\u306F\u3053\u306E\u4F1A\u8A71\u3078\u5C4A\u304D\u307E\u3059\u3002`;
16068
+ return text(`${head}${decision.answer_text ? "" : receiveGuide(harness, channelId)}
16069
+
16070
+ ${JSON.stringify(decision)}`, { ...decision, steer_channel: { channel_id: channelId } });
16035
16071
  }
16036
16072
  if (name === "cancel_decision") {
16037
16073
  const decision = await api.call("POST", `/decisions/${encodeURIComponent(String(args.decision_id))}/cancel`, { reason: args.reason });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "approval-box",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
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",
@@ -28,14 +28,13 @@
28
28
  "typecheck": "tsc -p tsconfig.json --noEmit",
29
29
  "test": "node --test 'test/**/*.test.ts'"
30
30
  },
31
- "dependencies": {},
32
31
  "devDependencies": {
33
32
  "@modelcontextprotocol/sdk": "^1.31.0",
34
33
  "@types/node": "^25.9.1",
35
- "aiterm-steer-delivery": "^0.1.9",
34
+ "@types/qrcode": "^1.5.5",
35
+ "aiterm-steer-delivery": "^0.2.0",
36
36
  "esbuild": "^0.28.2",
37
37
  "qrcode": "^1.5.4",
38
- "@types/qrcode": "^1.5.5",
39
38
  "typescript": "^6.0.3",
40
39
  "zod": "^4.4.3"
41
40
  },