@pushary/agent-hooks 0.89.9 → 0.89.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/bin/pushary-bell-hook.js +4 -4
  3. package/dist/bin/pushary-bell.js +7 -7
  4. package/dist/bin/pushary-claude.js +84 -36
  5. package/dist/bin/pushary-clean.js +52 -45
  6. package/dist/bin/pushary-codex-bridge.js +15 -9
  7. package/dist/bin/pushary-codex-hook.js +12 -8
  8. package/dist/bin/pushary-codex.js +7 -7
  9. package/dist/bin/pushary-connect.js +6 -6
  10. package/dist/bin/pushary-daemon.js +16 -16
  11. package/dist/bin/pushary-disconnect.js +34 -9
  12. package/dist/bin/pushary-doctor.js +19 -19
  13. package/dist/bin/pushary-elicitation-hook.js +4 -4
  14. package/dist/bin/pushary-gemini-bridge.js +15 -8
  15. package/dist/bin/pushary-gemini-hook.js +8 -8
  16. package/dist/bin/pushary-hook.js +11 -10
  17. package/dist/bin/pushary-login.js +4 -4
  18. package/dist/bin/pushary-logout.js +4 -4
  19. package/dist/bin/pushary-mode.js +3 -3
  20. package/dist/bin/pushary-notification-hook.js +7 -7
  21. package/dist/bin/pushary-opencode-hook.js +8 -8
  22. package/dist/bin/pushary-permission-denied-hook.js +11 -10
  23. package/dist/bin/pushary-permission-hook.js +11 -10
  24. package/dist/bin/pushary-post-hook.js +7 -7
  25. package/dist/bin/pushary-prompt-hook.js +7 -7
  26. package/dist/bin/pushary-session-end-hook.js +7 -7
  27. package/dist/bin/pushary-session-start-hook.js +7 -7
  28. package/dist/bin/pushary-setup.js +52 -54
  29. package/dist/bin/pushary-stats.js +2 -2
  30. package/dist/bin/pushary-status.js +8 -8
  31. package/dist/bin/pushary-stop-hook.js +7 -7
  32. package/dist/bin/pushary-stopfailure-hook.js +7 -7
  33. package/dist/bin/pushary-upgrade.js +15 -15
  34. package/dist/bin/pushary-wait.js +3 -3
  35. package/dist/{chunk-SS72Z7FV.js → chunk-253WC6O5.js} +1 -1
  36. package/dist/{chunk-T3LRWZKI.js → chunk-2LPEZYMC.js} +1 -1
  37. package/dist/{chunk-O5B7EK5U.js → chunk-34GN3IPX.js} +1 -1
  38. package/dist/{chunk-V6N4Q223.js → chunk-3CUDMTL4.js} +1 -1
  39. package/dist/{chunk-VZKXBFUA.js → chunk-3HFK3UXM.js} +1 -1
  40. package/dist/{chunk-543KNJKC.js → chunk-3SQYKLBW.js} +3 -3
  41. package/dist/{chunk-ATBKJNWS.js → chunk-6EKQZA6R.js} +5 -1
  42. package/dist/{chunk-NKHEHEUM.js → chunk-6XUABEX2.js} +1 -1
  43. package/dist/{chunk-I5L6SK5V.js → chunk-BLODMQKJ.js} +1 -1
  44. package/dist/{chunk-DKDDLF5P.js → chunk-DKWM5T3P.js} +1 -1
  45. package/dist/{chunk-4XZCDSNG.js → chunk-FXIL7RCL.js} +5 -5
  46. package/dist/{chunk-BAE45YLP.js → chunk-FZ73G2RO.js} +3 -3
  47. package/dist/{chunk-PMHTCDEO.js → chunk-IUN6VOKV.js} +1 -1
  48. package/dist/{chunk-OFRNZWC4.js → chunk-IUWRVTQK.js} +1 -1
  49. package/dist/{chunk-EJT2ER6G.js → chunk-J4K75V4D.js} +46 -35
  50. package/dist/{chunk-GKAKAOKC.js → chunk-JHJHCMAF.js} +65 -12
  51. package/dist/{chunk-J2JZJZKB.js → chunk-KVE5RCQL.js} +1 -1
  52. package/dist/{chunk-A4O5ROZK.js → chunk-MZGWKIUZ.js} +2 -2
  53. package/dist/chunk-NCVIWGVA.js +18 -0
  54. package/dist/{chunk-C623LWJR.js → chunk-OLAGKKSR.js} +1 -1
  55. package/dist/{chunk-EA3E74P2.js → chunk-PZRWFWSR.js} +6 -6
  56. package/dist/{chunk-VA7VUNRD.js → chunk-Q223OM5J.js} +58 -8
  57. package/dist/{chunk-GMI5QZLG.js → chunk-QA72MKIT.js} +5 -1
  58. package/dist/{chunk-AIGDBRIJ.js → chunk-SYUIXXQW.js} +1 -1
  59. package/dist/{chunk-TF247BET.js → chunk-TEZ2FZD6.js} +1 -1
  60. package/dist/{chunk-KOPUTJQC.js → chunk-TG3T3M4K.js} +1 -1
  61. package/dist/{chunk-FX5QSMPY.js → chunk-TRVIRETT.js} +1 -1
  62. package/dist/{chunk-U5TQCQ25.js → chunk-TY5HVMXT.js} +1 -1
  63. package/dist/chunk-UDBJHQMG.js +240 -0
  64. package/dist/{chunk-PZA7G4CN.js → chunk-X4ZUUFKV.js} +1 -0
  65. package/dist/{chunk-L4C2JXJS.js → chunk-X6RXX2NF.js} +5 -0
  66. package/dist/src/index.d.ts +5 -0
  67. package/dist/src/index.js +11 -10
  68. package/package.json +1 -1
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  reapplyAllAgents
4
- } from "../chunk-EA3E74P2.js";
5
- import "../chunk-J2JZJZKB.js";
6
- import "../chunk-BAE45YLP.js";
4
+ } from "../chunk-PZRWFWSR.js";
5
+ import "../chunk-KVE5RCQL.js";
6
+ import "../chunk-FZ73G2RO.js";
7
7
  import {
8
8
  waitForMachineReadiness
9
- } from "../chunk-U5TQCQ25.js";
10
- import "../chunk-VA7VUNRD.js";
11
- import "../chunk-ATBKJNWS.js";
12
- import "../chunk-AIGDBRIJ.js";
9
+ } from "../chunk-TY5HVMXT.js";
10
+ import "../chunk-Q223OM5J.js";
11
+ import "../chunk-6EKQZA6R.js";
12
+ import "../chunk-SYUIXXQW.js";
13
13
  import {
14
14
  daemonHasRemoteProof,
15
15
  daemonServiceManagerAvailable,
@@ -20,12 +20,12 @@ import {
20
20
  replaceDaemonService,
21
21
  stopDaemonService,
22
22
  waitForDaemonRemoteProof
23
- } from "../chunk-543KNJKC.js";
23
+ } from "../chunk-3SQYKLBW.js";
24
24
  import {
25
25
  detectProviderLiveCapabilities,
26
26
  hasSpawnProvider,
27
27
  resolveControlPolicy
28
- } from "../chunk-NKHEHEUM.js";
28
+ } from "../chunk-6XUABEX2.js";
29
29
  import {
30
30
  needsShell
31
31
  } from "../chunk-XHKBHWLX.js";
@@ -33,18 +33,18 @@ import {
33
33
  execNpm,
34
34
  npmErrorMessage,
35
35
  resolveGlobalBinary
36
- } from "../chunk-PZA7G4CN.js";
37
- import "../chunk-DKDDLF5P.js";
36
+ } from "../chunk-X4ZUUFKV.js";
37
+ import "../chunk-DKWM5T3P.js";
38
38
  import {
39
39
  claudeJson,
40
40
  codexConfigToml,
41
41
  cursorPluginDir,
42
42
  geminiSettings,
43
43
  vscodePluginDir
44
- } from "../chunk-GMI5QZLG.js";
44
+ } from "../chunk-QA72MKIT.js";
45
45
  import {
46
46
  readControlMode
47
- } from "../chunk-V6N4Q223.js";
47
+ } from "../chunk-3CUDMTL4.js";
48
48
  import {
49
49
  exitOnHelpFlag
50
50
  } from "../chunk-LURY4WNF.js";
@@ -53,13 +53,13 @@ import "../chunk-BC3VCZ3E.js";
53
53
  import {
54
54
  isNewerVersion
55
55
  } from "../chunk-DINDL2CF.js";
56
- import "../chunk-OFRNZWC4.js";
56
+ import "../chunk-IUWRVTQK.js";
57
57
  import "../chunk-KZERVKTD.js";
58
58
  import "../chunk-XX2V4QS5.js";
59
59
  import {
60
60
  getApiKey
61
61
  } from "../chunk-2UMNXADU.js";
62
- import "../chunk-L4C2JXJS.js";
62
+ import "../chunk-X6RXX2NF.js";
63
63
 
64
64
  // bin/pushary-upgrade.ts
65
65
  import { existsSync } from "fs";
@@ -4,8 +4,8 @@ import {
4
4
  } from "../chunk-IIENZE2J.js";
5
5
  import {
6
6
  exitOnFailedResponse
7
- } from "../chunk-SS72Z7FV.js";
8
- import "../chunk-I5L6SK5V.js";
7
+ } from "../chunk-253WC6O5.js";
8
+ import "../chunk-BLODMQKJ.js";
9
9
  import {
10
10
  createIo
11
11
  } from "../chunk-5RUIFDTP.js";
@@ -28,7 +28,7 @@ import {
28
28
  getApiKey,
29
29
  getBaseUrl
30
30
  } from "../chunk-2UMNXADU.js";
31
- import "../chunk-L4C2JXJS.js";
31
+ import "../chunk-X6RXX2NF.js";
32
32
 
33
33
  // bin/pushary-wait.ts
34
34
  exitOnHelpFlag("wait");
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  describeKeyCheck,
3
3
  keyCheckFromResponse
4
- } from "./chunk-I5L6SK5V.js";
4
+ } from "./chunk-BLODMQKJ.js";
5
5
  import {
6
6
  createIo
7
7
  } from "./chunk-5RUIFDTP.js";
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-BSZYIAZL.js";
12
12
  import {
13
13
  reportedInstallSource
14
- } from "./chunk-OFRNZWC4.js";
14
+ } from "./chunk-IUWRVTQK.js";
15
15
 
16
16
  // src/reach.ts
17
17
  var reachVerdict = (channels) => {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HOOK_BUDGETS,
3
3
  OPENCODE_APPROVAL_EVENT
4
- } from "./chunk-L4C2JXJS.js";
4
+ } from "./chunk-X6RXX2NF.js";
5
5
 
6
6
  // src/opencode-plugin.ts
7
7
  var OPENCODE_PLUGIN_MARKER = "pushary-opencode-plugin w1";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-BC3VCZ3E.js";
4
4
  import {
5
5
  resolveInstallSourceFor
6
- } from "./chunk-OFRNZWC4.js";
6
+ } from "./chunk-IUWRVTQK.js";
7
7
  import {
8
8
  backupFile,
9
9
  readJsonSafe,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-L4C2JXJS.js";
3
+ } from "./chunk-X6RXX2NF.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -4,13 +4,13 @@ import {
4
4
  } from "./chunk-XHKBHWLX.js";
5
5
  import {
6
6
  resolveGlobalBinary
7
- } from "./chunk-PZA7G4CN.js";
7
+ } from "./chunk-X4ZUUFKV.js";
8
8
  import {
9
9
  getMachineId
10
- } from "./chunk-DKDDLF5P.js";
10
+ } from "./chunk-DKWM5T3P.js";
11
11
  import {
12
12
  pusharyDir
13
- } from "./chunk-GMI5QZLG.js";
13
+ } from "./chunk-QA72MKIT.js";
14
14
  import {
15
15
  getPackageVersion
16
16
  } from "./chunk-DINDL2CF.js";
@@ -1,3 +1,7 @@
1
+ import {
2
+ ASKED_IN_EVERY_MODE_TOOLS
3
+ } from "./chunk-X6RXX2NF.js";
4
+
1
5
  // src/claude-tools.ts
2
6
  var PRETOOLUSE_GATED_TOOLS = [
3
7
  "Bash",
@@ -8,7 +12,7 @@ var PRETOOLUSE_GATED_TOOLS = [
8
12
  "PowerShell",
9
13
  "Monitor"
10
14
  ];
11
- var PRETOOLUSE_PHONE_ANSWERED_TOOLS = ["AskUserQuestion", "ExitPlanMode"];
15
+ var PRETOOLUSE_PHONE_ANSWERED_TOOLS = ASKED_IN_EVERY_MODE_TOOLS;
12
16
  var POSTTOOLUSE_MIRRORED_TOOLS = [...PRETOOLUSE_GATED_TOOLS, "TodoWrite"];
13
17
  var PRETOOLUSE_HANDLED_TOOLS = /* @__PURE__ */ new Set([...PRETOOLUSE_GATED_TOOLS, ...PRETOOLUSE_PHONE_ANSWERED_TOOLS]);
14
18
 
@@ -2,7 +2,7 @@ import {
2
2
  resolveBinary,
3
3
  resolveGlobalPackageVersion,
4
4
  resolvePackageVersionFromBinary
5
- } from "./chunk-PZA7G4CN.js";
5
+ } from "./chunk-X4ZUUFKV.js";
6
6
 
7
7
  // src/live-capabilities.ts
8
8
  var CLAUDE_BRIDGE_CAPABILITY = "claude-bridge";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-2UMNXADU.js";
13
13
  import {
14
14
  isValidApiKey
15
- } from "./chunk-L4C2JXJS.js";
15
+ } from "./chunk-X6RXX2NF.js";
16
16
 
17
17
  // src/session.ts
18
18
  var IDENTITY_PATH = "/api/v1/server/identity";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  pusharyDir
3
- } from "./chunk-GMI5QZLG.js";
3
+ } from "./chunk-QA72MKIT.js";
4
4
  import {
5
5
  writeFileAtomic
6
6
  } from "./chunk-XX2V4QS5.js";
@@ -12,7 +12,7 @@ import {
12
12
  savePendingCommand,
13
13
  sendNotification,
14
14
  waitForAnswer
15
- } from "./chunk-GKAKAOKC.js";
15
+ } from "./chunk-JHJHCMAF.js";
16
16
  import {
17
17
  AGENT_IDENTITIES,
18
18
  deriveReceiptMeta,
@@ -26,16 +26,16 @@ import {
26
26
  summarizeStop,
27
27
  taskTitleFrom,
28
28
  toolResultErrorText
29
- } from "./chunk-KOPUTJQC.js";
29
+ } from "./chunk-TG3T3M4K.js";
30
30
  import {
31
31
  getMachineId
32
- } from "./chunk-DKDDLF5P.js";
32
+ } from "./chunk-DKWM5T3P.js";
33
33
  import {
34
34
  withRetry
35
35
  } from "./chunk-BSZYIAZL.js";
36
36
  import {
37
37
  reportedInstallSource
38
- } from "./chunk-OFRNZWC4.js";
38
+ } from "./chunk-IUWRVTQK.js";
39
39
  import {
40
40
  getApiKey,
41
41
  getBaseUrl
@@ -51,7 +51,7 @@ import {
51
51
  pendingCommandEvents,
52
52
  resolveAutoResolveOrigin,
53
53
  resolvePolicy
54
- } from "./chunk-L4C2JXJS.js";
54
+ } from "./chunk-X6RXX2NF.js";
55
55
 
56
56
  // src/policy.ts
57
57
  import { createHash } from "crypto";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  hasGeminiHooks
3
- } from "./chunk-VA7VUNRD.js";
3
+ } from "./chunk-Q223OM5J.js";
4
4
  import {
5
5
  hasCodexHooks
6
- } from "./chunk-GMI5QZLG.js";
6
+ } from "./chunk-QA72MKIT.js";
7
7
  import {
8
8
  HOOK_LOCATOR_BINARY
9
- } from "./chunk-L4C2JXJS.js";
9
+ } from "./chunk-X6RXX2NF.js";
10
10
 
11
11
  // src/diagnostics/wiring.ts
12
12
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2UMNXADU.js";
4
4
  import {
5
5
  redactSecrets
6
- } from "./chunk-L4C2JXJS.js";
6
+ } from "./chunk-X6RXX2NF.js";
7
7
 
8
8
  // src/crypto.ts
9
9
  import nacl from "tweetnacl";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-2UMNXADU.js";
4
4
  import {
5
5
  normalizeInstallSource
6
- } from "./chunk-L4C2JXJS.js";
6
+ } from "./chunk-X6RXX2NF.js";
7
7
 
8
8
  // src/install-source.ts
9
9
  import { readFileSync } from "fs";
@@ -1,11 +1,14 @@
1
1
  import {
2
2
  PRETOOLUSE_HANDLED_TOOLS,
3
3
  PRETOOLUSE_PHONE_ANSWERED_TOOLS
4
- } from "./chunk-ATBKJNWS.js";
4
+ } from "./chunk-6EKQZA6R.js";
5
+ import {
6
+ defersToNativeMode
7
+ } from "./chunk-NCVIWGVA.js";
5
8
  import {
6
9
  isGatingMoment,
7
10
  recordKeylessMoment
8
- } from "./chunk-VZKXBFUA.js";
11
+ } from "./chunk-3HFK3UXM.js";
9
12
  import {
10
13
  denyReasonFrom,
11
14
  isDeferAnswer
@@ -17,7 +20,7 @@ import {
17
20
  readLastUserPrompt,
18
21
  repoKeyFor,
19
22
  throttlePass
20
- } from "./chunk-4XZCDSNG.js";
23
+ } from "./chunk-FXIL7RCL.js";
21
24
  import {
22
25
  DEFAULT_SESSION,
23
26
  askUser,
@@ -25,7 +28,7 @@ import {
25
28
  pollForAnswer,
26
29
  savePendingQuestion,
27
30
  sendNotification
28
- } from "./chunk-GKAKAOKC.js";
31
+ } from "./chunk-JHJHCMAF.js";
29
32
  import {
30
33
  deriveAction,
31
34
  deriveActionBody,
@@ -33,10 +36,10 @@ import {
33
36
  deriveToolTarget,
34
37
  describeToolCall,
35
38
  scopePathFor
36
- } from "./chunk-KOPUTJQC.js";
39
+ } from "./chunk-TG3T3M4K.js";
37
40
  import {
38
41
  getMachineId
39
- } from "./chunk-DKDDLF5P.js";
42
+ } from "./chunk-DKWM5T3P.js";
40
43
  import {
41
44
  getApiKey,
42
45
  getBaseUrl
@@ -50,9 +53,8 @@ import {
50
53
  hookWaitDeadline,
51
54
  parseAgentQuestionAnswers,
52
55
  parseAgentQuestions,
53
- resolveGate,
54
- shouldDeferToNativeMode
55
- } from "./chunk-L4C2JXJS.js";
56
+ resolveGate
57
+ } from "./chunk-X6RXX2NF.js";
56
58
 
57
59
  // src/decision-episode.ts
58
60
  var EPISODE_PATH = "/api/agent/decision-episode";
@@ -85,7 +87,16 @@ var allow = () => claudePreToolUseOutput("allow");
85
87
  var deny = (reason) => claudePreToolUseOutput("deny", reason);
86
88
  var allowWithInput = (updatedInput) => claudePreToolUseOutput("allow", void 0, updatedInput);
87
89
  var ask = (reason) => claudePreToolUseOutput("ask", reason);
90
+ var storedWaitDeadline = (result, requestedDeadline) => {
91
+ const stored = result.waitEndsAt ? Date.parse(result.waitEndsAt) : Number.NaN;
92
+ return Number.isFinite(stored) ? Math.min(stored, requestedDeadline) : requestedDeadline;
93
+ };
94
+ var returnedAnswer = (result) => result.answered === true ? { answered: true, value: result.value, note: result.note } : null;
88
95
  var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, timeoutAction, sessionId, machineId, toolName, toolTarget, decision, signals) => {
96
+ const effectiveWait = effectiveWaitSeconds(timeoutAction, timeoutSeconds, "claude");
97
+ const now = Date.now();
98
+ const requestedDeadline = hookWaitDeadline(START_MS, effectiveWait, "claude", now);
99
+ const clampCut = hookWaitClamped(START_MS, effectiveWait, "claude", now);
89
100
  let result;
90
101
  try {
91
102
  result = await askUser(apiKey, {
@@ -97,6 +108,7 @@ var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, ti
97
108
  machineId,
98
109
  toolName,
99
110
  toolTarget,
111
+ waitEndsAt: timeoutAction !== "approve" && timeoutAction !== "deny" || clampCut ? new Date(requestedDeadline).toISOString() : void 0,
100
112
  ...decision
101
113
  });
102
114
  } catch {
@@ -115,6 +127,8 @@ var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, ti
115
127
  return ask(result.suppressed ? "You are at the keyboard, approve here." : "Delivery mode is Terminal, approve here.");
116
128
  }
117
129
  if (result.noDevices) {
130
+ await cancelQuestion(apiKey, result.correlationId).catch(() => {
131
+ });
118
132
  switch (timeoutAction) {
119
133
  case "approve":
120
134
  return allow();
@@ -124,12 +138,9 @@ var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, ti
124
138
  return void 0;
125
139
  }
126
140
  }
127
- const effectiveWait = effectiveWaitSeconds(timeoutAction, timeoutSeconds, "claude");
128
- const now = Date.now();
129
- const deadline = hookWaitDeadline(START_MS, effectiveWait, "claude", now);
130
- const clampCut = hookWaitClamped(START_MS, effectiveWait, "claude", now);
141
+ const deadline = storedWaitDeadline(result, requestedDeadline);
131
142
  announceWait(effectiveWait, result.answerUrl, sessionId);
132
- const answer = await pollForAnswer(apiKey, result.correlationId, deadline);
143
+ const answer = returnedAnswer(result) ?? await pollForAnswer(apiKey, result.correlationId, deadline);
133
144
  if (answer.answered) {
134
145
  if (isDeferAnswer(answer.value)) return ask("Handling on your machine");
135
146
  signals.humanDecision = true;
@@ -145,6 +156,7 @@ var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, ti
145
156
  case "deny":
146
157
  return deny("No response within timeout");
147
158
  default:
159
+ savePendingQuestion(sessionId || DEFAULT_SESSION, result.correlationId);
148
160
  return ask("No push response, asking in terminal");
149
161
  }
150
162
  };
@@ -152,6 +164,7 @@ var handleTerminalOnly = () => {
152
164
  return ask();
153
165
  };
154
166
  var handlePushFirst = async (apiKey, description, projectName, pushFirstSeconds, sessionId, machineId, toolName, toolTarget, decision, signals) => {
167
+ const requestedDeadline = hookWaitDeadline(START_MS, pushFirstSeconds, "claude", Date.now());
155
168
  let result;
156
169
  try {
157
170
  result = await askUser(apiKey, {
@@ -163,6 +176,7 @@ var handlePushFirst = async (apiKey, description, projectName, pushFirstSeconds,
163
176
  machineId,
164
177
  toolName,
165
178
  toolTarget,
179
+ waitEndsAt: new Date(requestedDeadline).toISOString(),
166
180
  ...decision
167
181
  });
168
182
  } catch {
@@ -174,11 +188,13 @@ var handlePushFirst = async (apiKey, description, projectName, pushFirstSeconds,
174
188
  return ask("You are at the keyboard, approve here.");
175
189
  }
176
190
  if (result.noDevices) {
191
+ await cancelQuestion(apiKey, result.correlationId).catch(() => {
192
+ });
177
193
  return void 0;
178
194
  }
179
195
  announceWait(pushFirstSeconds, result.answerUrl, sessionId);
180
- const deadline = hookWaitDeadline(START_MS, pushFirstSeconds, "claude", Date.now());
181
- const answer = await pollForAnswer(apiKey, result.correlationId, deadline, 1500);
196
+ const deadline = storedWaitDeadline(result, requestedDeadline);
197
+ const answer = returnedAnswer(result) ?? await pollForAnswer(apiKey, result.correlationId, deadline, 1500);
182
198
  if (answer.answered) {
183
199
  if (isDeferAnswer(answer.value)) return ask("Handling on your machine");
184
200
  signals.humanDecision = true;
@@ -302,15 +318,7 @@ var keylessNoticeOnce = (sessionId) => {
302
318
  } catch {
303
319
  }
304
320
  };
305
- var nativeDeferOptions = () => {
306
- const respect = process.env.PUSHARY_RESPECT_PERMISSION_MODE;
307
- const bypass = process.env.PUSHARY_DEFER_BYPASS;
308
- return {
309
- respectPermissionMode: !(respect === "0" || respect === "false"),
310
- deferBypass: bypass === "1" || bypass === "true"
311
- };
312
- };
313
- var shouldDeferToNativeMode2 = (permissionMode, toolName) => shouldDeferToNativeMode(permissionMode, toolName, nativeDeferOptions());
321
+ var shouldDeferToNativeMode = defersToNativeMode;
314
322
  var handedToTerminal = (result) => result.suppressed === true || result.noDevices === true || result.status === "terminal";
315
323
  var withLabels = (question) => ({
316
324
  ...question,
@@ -336,7 +344,8 @@ var askAsOneCard = async (apiKey, input, parsed, projectName, deadline) => {
336
344
  sessionId: input.session_id,
337
345
  machineId: getMachineId(),
338
346
  toolName: "AskUserQuestion",
339
- toolUseId: input.tool_use_id
347
+ toolUseId: input.tool_use_id,
348
+ waitEndsAt: new Date(deadline).toISOString()
340
349
  });
341
350
  } catch {
342
351
  return void 0;
@@ -346,7 +355,7 @@ var askAsOneCard = async (apiKey, input, parsed, projectName, deadline) => {
346
355
  });
347
356
  return void 0;
348
357
  }
349
- const answer = await pollForAnswer(apiKey, result.correlationId, deadline);
358
+ const answer = returnedAnswer(result) ?? await pollForAnswer(apiKey, result.correlationId, storedWaitDeadline(result, deadline));
350
359
  const values = answer.answered && answer.value ? parseAgentQuestionAnswers(parsed, answer.value) : null;
351
360
  if (!values || isDeferAnswer(answer.value)) {
352
361
  savePendingQuestion(input.session_id || DEFAULT_SESSION, result.correlationId);
@@ -372,7 +381,8 @@ var askQuestionByQuestion = async (apiKey, input, parsed, projectName, deadline)
372
381
  // One AskUserQuestion call raises several questions, and their payload
373
382
  // fingerprints are identical (same tool, no target, no body). Without the
374
383
  // index they would all collapse onto the first decision.
375
- toolUseId: input.tool_use_id ? `${input.tool_use_id}#q${index}` : void 0
384
+ toolUseId: input.tool_use_id ? `${input.tool_use_id}#q${index}` : void 0,
385
+ waitEndsAt: new Date(deadline).toISOString()
376
386
  });
377
387
  } catch {
378
388
  return void 0;
@@ -382,7 +392,7 @@ var askQuestionByQuestion = async (apiKey, input, parsed, projectName, deadline)
382
392
  });
383
393
  return void 0;
384
394
  }
385
- const answer = await pollForAnswer(apiKey, result.correlationId, deadline);
395
+ const answer = returnedAnswer(result) ?? await pollForAnswer(apiKey, result.correlationId, storedWaitDeadline(result, deadline));
386
396
  const legacyValue = answer.answered ? answer.value?.trim() : void 0;
387
397
  const values = legacyValue ? parseAgentQuestionAnswers([question], legacyValue) ?? parseAgentQuestionAnswers([question], JSON.stringify({
388
398
  [question.question]: question.multiSelect ? JSON.stringify([legacyValue]) : legacyValue
@@ -410,6 +420,7 @@ var PLAN_KEEP_PLANNING = "Keep planning";
410
420
  var handleExitPlanMode = async (apiKey, input) => {
411
421
  const toolInput = input.tool_input ?? {};
412
422
  const projectName = basename(input.cwd ?? process.cwd());
423
+ const deadline = hookWaitDeadline(START_MS, effectiveWaitSeconds("wait", 0, "claude"), "claude", Date.now());
413
424
  let result;
414
425
  try {
415
426
  result = await askUser(apiKey, {
@@ -426,7 +437,8 @@ var handleExitPlanMode = async (apiKey, input) => {
426
437
  // so approving from a lock screen is a real decision and not a blind yes.
427
438
  actionBody: deriveActionBody("ExitPlanMode", toolInput),
428
439
  intent: readLastPrompt(input.session_id ?? DEFAULT_SESSION) ?? (input.transcript_path ? readLastUserPrompt(input.transcript_path) : void 0),
429
- blocker: "Waiting for you to approve the plan"
440
+ blocker: "Waiting for you to approve the plan",
441
+ waitEndsAt: new Date(deadline).toISOString()
430
442
  });
431
443
  } catch {
432
444
  return void 0;
@@ -436,8 +448,7 @@ var handleExitPlanMode = async (apiKey, input) => {
436
448
  });
437
449
  return void 0;
438
450
  }
439
- const deadline = hookWaitDeadline(START_MS, effectiveWaitSeconds("wait", 0, "claude"), "claude", Date.now());
440
- const answer = await pollForAnswer(apiKey, result.correlationId, deadline);
451
+ const answer = returnedAnswer(result) ?? await pollForAnswer(apiKey, result.correlationId, storedWaitDeadline(result, deadline));
441
452
  const value = answer.answered ? (answer.value ?? "").trim() : "";
442
453
  if (!value || isDeferAnswer(answer.value)) {
443
454
  savePendingQuestion(input.session_id || DEFAULT_SESSION, result.correlationId);
@@ -487,14 +498,14 @@ var handlePreToolUse = async (input) => {
487
498
  const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code", true, input.session_id);
488
499
  const verdict = resolveGate(claudeGateInput(modeState, policy, input));
489
500
  if (verdict.kind === "kill") return deny(verdict.reason);
501
+ if (verdict.kind === "deny") return deny(verdict.reason);
490
502
  if (input.tool_name === "AskUserQuestion") {
491
503
  return handleAskUserQuestion(apiKey, input);
492
504
  }
493
- if (verdict.kind === "deny") return deny(verdict.reason);
494
505
  if (input.tool_name === "ExitPlanMode") {
495
506
  return handleExitPlanMode(apiKey, input);
496
507
  }
497
- if (shouldDeferToNativeMode2(input.permission_mode, input.tool_name)) {
508
+ if (shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
498
509
  return void 0;
499
510
  }
500
511
  switch (verdict.kind) {
@@ -525,7 +536,7 @@ var toPermissionRequestOutput = (hookOutput) => {
525
536
  };
526
537
  var handlePermissionRequest = async (input) => {
527
538
  if (PRETOOLUSE_HANDLED_TOOLS.has(input.tool_name)) {
528
- const reclaimDeferredExec = !PRETOOLUSE_PHONE_ANSWERED_TOOLS.includes(input.tool_name) && shouldDeferToNativeMode2(input.permission_mode, input.tool_name);
539
+ const reclaimDeferredExec = !PRETOOLUSE_PHONE_ANSWERED_TOOLS.includes(input.tool_name) && shouldDeferToNativeMode(input.permission_mode, input.tool_name);
529
540
  if (!reclaimDeferredExec) return void 0;
530
541
  }
531
542
  if (input.tool_name.startsWith("mcp__pushary__")) return void 0;
@@ -17,7 +17,7 @@ var isPolicyConfig = (data) => {
17
17
  var isAskUserResponse = (data) => {
18
18
  if (!data || typeof data !== "object") return false;
19
19
  const d = data;
20
- return typeof d.correlationId === "string" && typeof d.status === "string";
20
+ return typeof d.correlationId === "string" && typeof d.status === "string" && (d.answered === void 0 || typeof d.answered === "boolean") && (d.value === void 0 || typeof d.value === "string") && (d.note === void 0 || typeof d.note === "string") && (d.waitEndsAt === void 0 || typeof d.waitEndsAt === "string");
21
21
  };
22
22
  var isWaitForAnswerResponse = (data) => {
23
23
  if (!data || typeof data !== "object") return false;
@@ -82,13 +82,14 @@ var commandDirFor = (sessionId, namespace = "") => join(
82
82
  createHash("sha256").update(namespace).digest("hex").slice(0, 16),
83
83
  sanitize(sessionId)
84
84
  );
85
- var commandFile = (sessionId, namespace = "") => join(commandDirFor(sessionId, namespace), "command.json");
86
- var readPendingCommand = (sessionId, namespace = "") => {
85
+ var legacyCommandFile = (sessionId, namespace = "") => join(commandDirFor(sessionId, namespace), "command.json");
86
+ var commandFile = (sessionId, id, namespace = "") => join(commandDirFor(sessionId, namespace), `${createHash("sha256").update(id).digest("hex")}.json`);
87
+ var readCommandFile = (file) => {
87
88
  try {
88
- const value = JSON.parse(readFileSync(commandFile(sessionId, namespace), "utf-8"));
89
+ const value = JSON.parse(readFileSync(file, "utf-8"));
89
90
  if (typeof value.id !== "string" || typeof value.text !== "string" || typeof value.expiresAtMs !== "number") return null;
90
91
  if (value.expiresAtMs <= Date.now()) {
91
- unlinkSync(commandFile(sessionId, namespace));
92
+ unlinkSync(file);
92
93
  return null;
93
94
  }
94
95
  return {
@@ -100,23 +101,68 @@ var readPendingCommand = (sessionId, namespace = "") => {
100
101
  return null;
101
102
  }
102
103
  };
104
+ var readPendingCommandById = (sessionId, id, namespace = "") => {
105
+ const current = readCommandFile(commandFile(sessionId, id, namespace));
106
+ if (current?.id === id) return current;
107
+ const legacy = readCommandFile(legacyCommandFile(sessionId, namespace));
108
+ return legacy?.id === id ? legacy : null;
109
+ };
110
+ var pruneExpiredCommands = (dir) => {
111
+ let files;
112
+ try {
113
+ files = readdirSync(dir).filter((file) => file.endsWith(".json"));
114
+ } catch {
115
+ return [];
116
+ }
117
+ return files.flatMap((file) => {
118
+ const command = readCommandFile(join(dir, file));
119
+ return command ? [command] : [];
120
+ });
121
+ };
122
+ var pruneExpiredCommandStore = () => {
123
+ let namespaces;
124
+ try {
125
+ namespaces = readdirSync(COMMAND_DIR, { withFileTypes: true });
126
+ } catch {
127
+ return;
128
+ }
129
+ for (const namespace of namespaces.filter((entry) => entry.isDirectory())) {
130
+ const namespaceDir = join(COMMAND_DIR, namespace.name);
131
+ let sessions;
132
+ try {
133
+ sessions = readdirSync(namespaceDir, { withFileTypes: true });
134
+ } catch {
135
+ continue;
136
+ }
137
+ for (const session of sessions.filter((entry) => entry.isDirectory())) {
138
+ pruneExpiredCommands(join(namespaceDir, session.name));
139
+ }
140
+ }
141
+ };
142
+ var readPendingCommand = (sessionId, namespace = "") => {
143
+ const commands = pruneExpiredCommands(commandDirFor(sessionId, namespace));
144
+ return commands.find((command) => !command.delivered && !command.failed) ?? commands[0] ?? null;
145
+ };
103
146
  var savePendingCommand = (sessionId, command, namespace = "") => {
104
147
  const dir = commandDirFor(sessionId, namespace);
105
148
  try {
106
149
  mkdirSync(dir, { recursive: true, mode: 448 });
107
- writeFileSync(commandFile(sessionId, namespace), JSON.stringify(command), { encoding: "utf-8", mode: 384, flag: "wx" });
150
+ pruneExpiredCommandStore();
151
+ if (readPendingCommandById(sessionId, command.id, namespace)) return false;
152
+ writeFileSync(commandFile(sessionId, command.id, namespace), JSON.stringify(command), { encoding: "utf-8", mode: 384, flag: "wx" });
108
153
  return true;
109
154
  } catch {
110
155
  return false;
111
156
  }
112
157
  };
113
158
  var updatePendingCommand = (sessionId, id, update, namespace = "") => {
114
- const command = readPendingCommand(sessionId, namespace);
115
- if (!command || command.id !== id) return false;
116
- const file = commandFile(sessionId, namespace);
159
+ const command = readPendingCommandById(sessionId, id, namespace);
160
+ if (!command) return false;
161
+ const currentFile = commandFile(sessionId, id, namespace);
162
+ const file = existsSync(currentFile) ? currentFile : legacyCommandFile(sessionId, namespace);
117
163
  const temporary = `${file}.${process.pid}.tmp`;
118
164
  try {
119
- writeFileSync(temporary, JSON.stringify({ ...command, ...update }), { encoding: "utf-8", mode: 384 });
165
+ writeFileSync(temporary, JSON.stringify({ ...command, ...update, text: "" }), { encoding: "utf-8", mode: 384 });
120
166
  renameSync(temporary, file);
121
167
  return true;
122
168
  } catch {
@@ -130,9 +176,15 @@ var updatePendingCommand = (sessionId, id, update, namespace = "") => {
130
176
  var markPendingCommandDelivered = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: true, failed: void 0 }, namespace);
131
177
  var markPendingCommandFailed = (sessionId, id, namespace = "") => updatePendingCommand(sessionId, id, { delivered: false, failed: true }, namespace);
132
178
  var removePendingCommand = (sessionId, id, namespace = "") => {
133
- if (readPendingCommand(sessionId, namespace)?.id !== id) return;
179
+ if (!readPendingCommandById(sessionId, id, namespace)) return;
180
+ try {
181
+ unlinkSync(commandFile(sessionId, id, namespace));
182
+ } catch {
183
+ }
134
184
  try {
135
- unlinkSync(commandFile(sessionId, namespace));
185
+ if (readCommandFile(legacyCommandFile(sessionId, namespace))?.id === id) {
186
+ unlinkSync(legacyCommandFile(sessionId, namespace));
187
+ }
136
188
  } catch {
137
189
  }
138
190
  };
@@ -181,6 +233,7 @@ export {
181
233
  cancelQuestion,
182
234
  sendNotification,
183
235
  DEFAULT_SESSION,
236
+ readPendingCommandById,
184
237
  readPendingCommand,
185
238
  savePendingCommand,
186
239
  markPendingCommandDelivered,