@pushary/agent-hooks 1.9.64 → 1.9.66

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 (123) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/dist/{api-HDLTZTVR.js → api-GDSVUTI2.js} +12 -10
  3. package/dist/bin/pushary-bell-hook.js +25 -27
  4. package/dist/bin/pushary-bell.js +10 -10
  5. package/dist/bin/pushary-claude.js +26 -28
  6. package/dist/bin/pushary-clean.js +39 -36
  7. package/dist/bin/pushary-codex-bridge.js +21 -22
  8. package/dist/bin/pushary-codex-hook.js +30 -32
  9. package/dist/bin/pushary-codex-session.js +8 -8
  10. package/dist/bin/pushary-codex.js +13 -13
  11. package/dist/bin/pushary-connect.js +25 -26
  12. package/dist/bin/pushary-cowork.js +5 -5
  13. package/dist/bin/pushary-daemon.js +37 -39
  14. package/dist/bin/pushary-disconnect.js +25 -26
  15. package/dist/bin/pushary-doctor.js +66 -44
  16. package/dist/bin/pushary-elicitation-hook.js +25 -27
  17. package/dist/bin/pushary-gemini-bridge.js +15 -16
  18. package/dist/bin/pushary-gemini-hook.js +27 -29
  19. package/dist/bin/pushary-hook.js +29 -31
  20. package/dist/bin/pushary-login.js +19 -19
  21. package/dist/bin/pushary-logout.js +25 -26
  22. package/dist/bin/pushary-mcp.js +11 -11
  23. package/dist/bin/pushary-mode.js +14 -14
  24. package/dist/bin/pushary-notification-hook.js +24 -26
  25. package/dist/bin/pushary-opencode-hook.js +26 -28
  26. package/dist/bin/pushary-permission-denied-hook.js +29 -31
  27. package/dist/bin/pushary-permission-hook.js +32 -31
  28. package/dist/bin/pushary-post-hook.js +24 -26
  29. package/dist/bin/pushary-prompt-hook.js +24 -26
  30. package/dist/bin/pushary-session-end-hook.js +26 -28
  31. package/dist/bin/pushary-session-start-hook.js +33 -35
  32. package/dist/bin/pushary-setup.js +68 -54
  33. package/dist/bin/pushary-stats.js +5 -5
  34. package/dist/bin/pushary-status.js +30 -31
  35. package/dist/bin/pushary-stop-hook.js +24 -26
  36. package/dist/bin/pushary-stopfailure-hook.js +24 -26
  37. package/dist/bin/pushary-suggestions.js +14 -14
  38. package/dist/bin/pushary-transcript-register.js +16 -18
  39. package/dist/bin/pushary-transcripts.js +17 -17
  40. package/dist/bin/pushary-upgrade.js +37 -38
  41. package/dist/bin/pushary-wait.js +14 -14
  42. package/dist/bin/pushary.js +7 -7
  43. package/dist/{chunk-7UBDBJTG.js → chunk-26W46E46.js} +4 -4
  44. package/dist/{chunk-MIPJ2VXP.js → chunk-2BL6KWN7.js} +9 -9
  45. package/dist/{chunk-42PD2MQH.js → chunk-32ZT2ATY.js} +5 -5
  46. package/dist/{chunk-2EY4HGLG.js → chunk-376TUN72.js} +3 -3
  47. package/dist/{chunk-A5KQXZR7.js → chunk-3ELJIG2R.js} +3 -3
  48. package/dist/{chunk-ML2Q6WWE.js → chunk-3EZP5ZPS.js} +5 -4
  49. package/dist/{chunk-66FRMK7V.js → chunk-3FQX74E4.js} +3 -3
  50. package/dist/{chunk-HVOQUPSO.js → chunk-45KMS76L.js} +1 -1
  51. package/dist/{chunk-LA7CKRZN.js → chunk-4APD2DPI.js} +2 -2
  52. package/dist/{chunk-5NP3OJ5P.js → chunk-57NTMVMI.js} +6 -6
  53. package/dist/{chunk-NYV45Q25.js → chunk-5FJDODOA.js} +3 -3
  54. package/dist/{chunk-RY4EPMZX.js → chunk-5J7OZBJ4.js} +2 -2
  55. package/dist/{chunk-XJIIU36L.js → chunk-6BGVSV2Q.js} +1 -1
  56. package/dist/{chunk-2YXBCZ3Z.js → chunk-6RJZSBPD.js} +1 -1
  57. package/dist/{chunk-MPPTIZ2G.js → chunk-6ZXCWK27.js} +5 -5
  58. package/dist/{chunk-VNRWQCGU.js → chunk-7LK3Y6ET.js} +63 -6
  59. package/dist/{chunk-Y64JGYTB.js → chunk-A47UKPC7.js} +1 -1
  60. package/dist/{chunk-5TNGBDIE.js → chunk-AVWECJSN.js} +2 -2
  61. package/dist/{chunk-CCNH4PJ2.js → chunk-AWGBL4P3.js} +211 -24
  62. package/dist/{chunk-LMC5UPL7.js → chunk-B6JC676X.js} +1 -1
  63. package/dist/{chunk-QPPBH5NY.js → chunk-CN6JFM62.js} +5 -5
  64. package/dist/{chunk-BLAIETMP.js → chunk-CNJ7SI5V.js} +4 -4
  65. package/dist/{chunk-DVYVGBPB.js → chunk-DBUHSUL7.js} +1 -1
  66. package/dist/{chunk-CJADXAJU.js → chunk-DIUTSF6I.js} +4 -4
  67. package/dist/{chunk-JX3DHGZX.js → chunk-DKMVVBXO.js} +30 -17
  68. package/dist/{chunk-2CEVEMJE.js → chunk-DPU6H4JY.js} +3 -3
  69. package/dist/{chunk-MNKIR2ZB.js → chunk-EJR5JXMV.js} +24 -1
  70. package/dist/{chunk-VHKK3US3.js → chunk-EU2ZOZC3.js} +3 -3
  71. package/dist/{chunk-AN7C3CHH.js → chunk-G6VJZ3RB.js} +1 -1
  72. package/dist/{chunk-H4BKC2GN.js → chunk-GDLT32I4.js} +3 -3
  73. package/dist/{chunk-EEM65CGJ.js → chunk-GECU7V44.js} +3 -3
  74. package/dist/{chunk-BAGBCSXA.js → chunk-GO7YKPT3.js} +1 -1
  75. package/dist/{chunk-QECRQWLV.js → chunk-GWYCKTXS.js} +5 -5
  76. package/dist/{chunk-2PKHGHMA.js → chunk-HUGVJBDN.js} +2 -2
  77. package/dist/{chunk-ADRPW7KU.js → chunk-IA5VWIHV.js} +2 -2
  78. package/dist/{chunk-6HPLEGUP.js → chunk-IGO3AGZK.js} +4 -4
  79. package/dist/{chunk-PMLQRB3D.js → chunk-IQ3GKPFM.js} +1 -1
  80. package/dist/{chunk-LV6DX4P6.js → chunk-JL3SZJJP.js} +2 -2
  81. package/dist/{chunk-BSZIPIFA.js → chunk-KS3O6O7E.js} +3 -3
  82. package/dist/{chunk-IZTDX2MX.js → chunk-MDIGG3Q7.js} +3 -3
  83. package/dist/{chunk-JTOBQUIL.js → chunk-MNNQEUQC.js} +3 -3
  84. package/dist/{chunk-2KID4FK4.js → chunk-Q5RNYE2L.js} +4 -4
  85. package/dist/{chunk-KX3RMF6T.js → chunk-QBNICXMC.js} +6 -6
  86. package/dist/{chunk-YUDZCJYC.js → chunk-QN25WBXL.js} +9 -7
  87. package/dist/{chunk-NUEPJ5W6.js → chunk-QOIPUYUD.js} +3 -5
  88. package/dist/{chunk-QNATBOUH.js → chunk-QR2QWGQI.js} +3 -3
  89. package/dist/{chunk-NOH3EJFH.js → chunk-R7C2FS33.js} +1 -1
  90. package/dist/{chunk-MXPOLQZH.js → chunk-RJ6JEHCP.js} +1 -1
  91. package/dist/{chunk-E2LB6JNV.js → chunk-RSZQEEEW.js} +7 -6
  92. package/dist/{chunk-57PU5OBL.js → chunk-SVKCYUYP.js} +11 -13
  93. package/dist/{chunk-DCMRNIDM.js → chunk-T6YLXO6N.js} +4 -4
  94. package/dist/{chunk-Q6ID23SU.js → chunk-TQNFDNJA.js} +6 -6
  95. package/dist/{chunk-SROWUG5W.js → chunk-UTAARXYQ.js} +1 -1
  96. package/dist/{chunk-OQKSF5ST.js → chunk-V3YBVOTZ.js} +2 -2
  97. package/dist/{chunk-XF3AWNMO.js → chunk-V4EOFOB2.js} +6 -6
  98. package/dist/{chunk-PLMX3IHG.js → chunk-VURDLVAQ.js} +3 -3
  99. package/dist/{chunk-AQAPPSDV.js → chunk-XDDSURY3.js} +3 -3
  100. package/dist/{chunk-QSW3HAKI.js → chunk-XEXF2OFZ.js} +61 -2
  101. package/dist/{chunk-2Z7I7DHT.js → chunk-XFQAK35L.js} +1 -1
  102. package/dist/{chunk-5TUDGSDC.js → chunk-XGFFNERF.js} +1 -1
  103. package/dist/{chunk-2VADJ6JH.js → chunk-XIJTXRWB.js} +2 -2
  104. package/dist/{chunk-3KXBRSL3.js → chunk-XXIJBYNW.js} +1 -1
  105. package/dist/{chunk-O7PCNYEY.js → chunk-YB3HMDKE.js} +2 -2
  106. package/dist/{chunk-KA7JIMTG.js → chunk-YQDCTID4.js} +115 -9
  107. package/dist/{chunk-E23RKH3T.js → chunk-ZAZUDYSF.js} +26 -12
  108. package/dist/{chunk-TR3GP5MH.js → chunk-ZJV7SFIJ.js} +1 -1
  109. package/dist/{codex-app-cards-switch-JQ6356VX.js → codex-app-cards-switch-W7OLKOMO.js} +11 -11
  110. package/dist/{codex-desktop-launch-YUB2LOAG.js → codex-desktop-launch-U6V3AXZF.js} +1 -1
  111. package/dist/{codex-session-launch-UKMAORXS.js → codex-session-launch-EFU46WBL.js} +18 -19
  112. package/dist/{config-YY54G3CC.js → config-UC4LMCNX.js} +3 -3
  113. package/dist/{global-upgrade-Y4USGPS2.js → global-upgrade-ABF5O3HU.js} +10 -10
  114. package/dist/{identity-W5CYL5RC.js → identity-3ZUZPHN6.js} +4 -4
  115. package/dist/{live-capabilities-JRNNA5EP.js → live-capabilities-BXWXJARK.js} +5 -5
  116. package/dist/{paths-4MEBINV3.js → paths-FO76CFRQ.js} +3 -3
  117. package/dist/{reapply-ZN7C3CB3.js → reapply-3T3DFUAI.js} +26 -27
  118. package/dist/src/index.js +30 -20
  119. package/dist/{upgrade-lock-QAWUV2DO.js → upgrade-lock-EZ5JIPJE.js} +4 -4
  120. package/dist/{usage-43C2DHDU.js → usage-Y6BL7RUD.js} +3 -3
  121. package/package.json +1 -1
  122. package/dist/chunk-6QPDBD7Q.js +0 -118
  123. package/dist/chunk-TJUJAK5L.js +0 -62
@@ -4,19 +4,24 @@ import {
4
4
  outcomeOfFailedAsk,
5
5
  reportMachineDenial,
6
6
  storedWaitDeadline
7
- } from "./chunk-XF3AWNMO.js";
7
+ } from "./chunk-V4EOFOB2.js";
8
8
  import {
9
9
  isGatingMoment,
10
10
  recordKeylessMoment
11
- } from "./chunk-XJIIU36L.js";
11
+ } from "./chunk-6BGVSV2Q.js";
12
+ import {
13
+ isAdmitted,
14
+ readAdmissionRules
15
+ } from "./chunk-QN25WBXL.js";
12
16
  import {
13
17
  isDeferAnswer,
14
18
  oncePerSession
15
- } from "./chunk-TR3GP5MH.js";
19
+ } from "./chunk-ZJV7SFIJ.js";
16
20
  import {
17
21
  activeHookSource,
18
- activeIdentity
19
- } from "./chunk-ML2Q6WWE.js";
22
+ activeIdentity,
23
+ namedSource
24
+ } from "./chunk-3EZP5ZPS.js";
20
25
  import {
21
26
  DEFAULT_SESSION,
22
27
  UNNAMED_ACTION,
@@ -40,10 +45,10 @@ import {
40
45
  reportEvent,
41
46
  scopePathFor,
42
47
  settleQuestionsForToolUse
43
- } from "./chunk-MIPJ2VXP.js";
48
+ } from "./chunk-2BL6KWN7.js";
44
49
  import {
45
50
  repoKeyFor
46
- } from "./chunk-3KXBRSL3.js";
51
+ } from "./chunk-XXIJBYNW.js";
47
52
  import {
48
53
  askUser,
49
54
  cancelQuestion,
@@ -58,21 +63,22 @@ import {
58
63
  requestGate,
59
64
  sendNotification,
60
65
  waitForQuestion,
61
- waitsForThePhone
62
- } from "./chunk-E23RKH3T.js";
66
+ waitsForThePhone,
67
+ withdrawQuestion
68
+ } from "./chunk-ZAZUDYSF.js";
63
69
  import {
64
70
  getMachineId
65
- } from "./chunk-ADRPW7KU.js";
71
+ } from "./chunk-IA5VWIHV.js";
66
72
  import {
67
73
  claudeConfigDir
68
- } from "./chunk-H4BKC2GN.js";
74
+ } from "./chunk-GDLT32I4.js";
69
75
  import {
70
76
  getApiKey,
71
77
  getBaseUrl
72
- } from "./chunk-Y64JGYTB.js";
78
+ } from "./chunk-A47UKPC7.js";
73
79
  import {
74
80
  NPX_LATEST_COMMAND
75
- } from "./chunk-BAGBCSXA.js";
81
+ } from "./chunk-GO7YKPT3.js";
76
82
  import {
77
83
  AGENT_QUESTION_STRATEGY,
78
84
  AGENT_QUESTION_TOOL,
@@ -81,11 +87,13 @@ import {
81
87
  GATE_WIRE,
82
88
  HANDED_BACK_REASON,
83
89
  PLAN_DECLINED_REASON,
90
+ PRETOOLUSE_HANDLED_TOOLS,
84
91
  QUESTION_LIMIT_PROMPT_REASON,
85
92
  SCOPE_BATCH_REASON,
86
93
  SOLE_GATE_SIGNED_OUT_REASON,
87
94
  SOLE_GATE_SUBSCRIPTION_REASON,
88
95
  SOLE_GATE_UNDECIDED_REASON,
96
+ TERMINAL_AND_PHONE_REASON,
89
97
  UNREADABLE_CALL_REASON,
90
98
  UNVERIFIABLE_APPROVAL_REASON,
91
99
  WITHDRAWN_APPROVAL_REASON,
@@ -99,6 +107,7 @@ import {
99
107
  claudePermissionRequestOutput,
100
108
  claudePermissionRulesFromSettings,
101
109
  claudePreToolUseOutput,
110
+ claudePromptTakesTheCall,
102
111
  claudeStoppedPermissionRequestOutput,
103
112
  claudeStoppedPreToolUseOutput,
104
113
  denyReasonFrom,
@@ -114,17 +123,113 @@ import {
114
123
  isCursorHookPayload,
115
124
  isPermissionRequestOwnedByPreToolUse,
116
125
  isPusharyOwnTool,
126
+ isRecord,
117
127
  parseAgentQuestions,
118
128
  questionHandedToTheTerminal,
119
129
  questionLimitHandBack,
120
130
  refusesWhenUnanswered,
121
131
  resolveGate,
132
+ terminalLiveWait,
133
+ terminalStaysLiveForTheCall,
122
134
  timeoutActionWithoutLocalPrompt,
123
135
  unansweredRefusalReason
124
- } from "./chunk-MNKIR2ZB.js";
136
+ } from "./chunk-EJR5JXMV.js";
137
+
138
+ // src/terminal-answer-watch.ts
139
+ import { closeSync, openSync, readSync, statSync } from "fs";
140
+ import { StringDecoder } from "string_decoder";
141
+ var INITIAL_TAIL_BYTES = 1048576;
142
+ var DEFAULT_POLL_MS = 500;
143
+ var sortedKeys = (value) => {
144
+ if (Array.isArray(value)) return value.map(sortedKeys);
145
+ if (!isRecord(value)) return value;
146
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortedKeys(value[key])]));
147
+ };
148
+ var stableJson = (value) => JSON.stringify(sortedKeys(value) ?? null);
149
+ var parsedLine = (line) => {
150
+ try {
151
+ return JSON.parse(line);
152
+ } catch {
153
+ return void 0;
154
+ }
155
+ };
156
+ var messageParts = (line) => {
157
+ const row = parsedLine(line);
158
+ const content = isRecord(row) && isRecord(row.message) ? row.message.content : void 0;
159
+ return Array.isArray(content) ? content.filter(isRecord) : [];
160
+ };
161
+ var isToolUse = (part) => part.type === "tool_use" && typeof part.id === "string" && typeof part.name === "string";
162
+ var isToolResultFor = (toolUseId) => (part) => part.type === "tool_result" && part.tool_use_id === toolUseId;
163
+ var matchingToolUseIds = (lines, call) => {
164
+ const wanted = stableJson(call.toolInput);
165
+ return lines.flatMap(messageParts).filter(isToolUse).filter((part) => part.name === call.toolName && stableJson(part.input ?? {}) === wanted).map((part) => part.id);
166
+ };
167
+ var toolUseIdOf = (lines, call) => matchingToolUseIds(lines, call).at(-1);
168
+ var toolResultArrived = (lines, toolUseId) => lines.some((line) => messageParts(line).some(isToolResultFor(toolUseId)));
169
+ var writtenAtMs = (line) => {
170
+ const row = parsedLine(line);
171
+ return isRecord(row) && typeof row.timestamp === "string" ? Date.parse(row.timestamp) : Number.NaN;
172
+ };
173
+ var answeredBefore = (lines, toolUseId, startedAtMs) => lines.some((line) => messageParts(line).some(isToolResultFor(toolUseId)) && !(writtenAtMs(line) >= startedAtMs));
174
+ var openToolUseIdOf = (lines, call, startedAtMs) => matchingToolUseIds(lines, call).filter((id) => !answeredBefore(lines, id, startedAtMs)).at(-1);
175
+ var readTranscriptFrom = (path, offset) => {
176
+ try {
177
+ const size = statSync(path).size;
178
+ if (size <= offset) return { bytes: Buffer.alloc(0), endOffset: size };
179
+ const descriptor = openSync(path, "r");
180
+ try {
181
+ const buffer = Buffer.alloc(size - offset);
182
+ readSync(descriptor, buffer, 0, buffer.length, offset);
183
+ return { bytes: buffer, endOffset: size };
184
+ } finally {
185
+ closeSync(descriptor);
186
+ }
187
+ } catch {
188
+ return null;
189
+ }
190
+ };
191
+ var startingOffset = (path) => {
192
+ try {
193
+ return Math.max(statSync(path).size - INITIAL_TAIL_BYTES, 0);
194
+ } catch {
195
+ return 0;
196
+ }
197
+ };
198
+ var watchForTerminalAnswer = (transcriptPath, call, startedAtMs, signal, pollMs = DEFAULT_POLL_MS) => new Promise((resolve) => {
199
+ if (!transcriptPath || signal.aborted) return;
200
+ let offset = startingOffset(transcriptPath);
201
+ const decoder = new StringDecoder("utf8");
202
+ let carry = "";
203
+ let readingHistory = true;
204
+ let toolUseId;
205
+ let timer;
206
+ const stop = () => {
207
+ if (timer) clearTimeout(timer);
208
+ };
209
+ signal.addEventListener("abort", stop, { once: true });
210
+ const tick = () => {
211
+ if (signal.aborted) return;
212
+ const chunk = readTranscriptFrom(transcriptPath, offset);
213
+ if (chunk) {
214
+ offset = chunk.endOffset;
215
+ const pieces = `${carry}${decoder.write(chunk.bytes)}`.split("\n");
216
+ carry = pieces.pop() ?? "";
217
+ toolUseId = toolUseId ?? (readingHistory ? openToolUseIdOf(pieces, call, startedAtMs) : toolUseIdOf(pieces, call));
218
+ readingHistory = false;
219
+ if (toolUseId && toolResultArrived(pieces, toolUseId)) {
220
+ signal.removeEventListener("abort", stop);
221
+ resolve();
222
+ return;
223
+ }
224
+ }
225
+ timer = setTimeout(tick, pollMs);
226
+ timer.unref?.();
227
+ };
228
+ tick();
229
+ });
125
230
 
126
231
  // src/claude-permission-rules.ts
127
- import { readFileSync, statSync } from "fs";
232
+ import { readFileSync, statSync as statSync2 } from "fs";
128
233
  import { homedir } from "os";
129
234
  import { join } from "path";
130
235
  var MANAGED_CLAUDE_SETTINGS_BY_PLATFORM = {
@@ -146,15 +251,15 @@ var claudeSettingsSources = (location) => [
146
251
  { path: join(location.projectDir, ".claude", "settings.local.json"), trust: "grants" }
147
252
  ] : []
148
253
  ];
149
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
254
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
150
255
  var restrictionsOnly = (document) => {
151
- if (!isRecord(document) || !isRecord(document.permissions)) return null;
256
+ if (!isRecord2(document) || !isRecord2(document.permissions)) return null;
152
257
  const { ask: ask2, deny: deny2 } = document.permissions;
153
258
  return { permissions: { ask: ask2, deny: deny2 } };
154
259
  };
155
260
  var readSettingsDocument = (path) => {
156
261
  try {
157
- const stats = statSync(path);
262
+ const stats = statSync2(path);
158
263
  if (!stats.isFile() || stats.size > MAX_CLAUDE_SETTINGS_BYTES) return null;
159
264
  return JSON.parse(readFileSync(path, "utf8"));
160
265
  } catch {
@@ -194,6 +299,8 @@ import { basename } from "path";
194
299
  var START_MS = Date.now();
195
300
  var ACTIVE_AGENT = activeIdentity(process.argv);
196
301
  var ACTIVE_SOURCE = activeHookSource(process.argv);
302
+ var HANDED_DOWN_BY_THE_MAC_BRIDGE = namedSource(process.argv) !== void 0;
303
+ var CLAUDE_SESSION_ATTENDED_ENV = "CLAUDE_CODE_SESSION_ATTENDED";
197
304
  var PUSHARY_IS_SOLE_GATE = ACTIVE_SOURCE !== null && hookCompatibleProfileBySource(ACTIVE_SOURCE)?.soleGate != null;
198
305
  var agentNameIn = (projectName) => `${ACTIVE_AGENT.label} - ${projectName}`;
199
306
  var allow = (reason) => claudePreToolUseOutput("allow", reason);
@@ -302,6 +409,64 @@ var handleNotifyOnly = async (apiKey, description, projectName, sessionId, machi
302
409
  }
303
410
  return ask();
304
411
  };
412
+ var TERMINAL_ANSWERED = /* @__PURE__ */ Symbol("terminal-answered");
413
+ var terminalAnswered = (input, signal) => watchForTerminalAnswer(input.transcript_path, { toolName: input.tool_name, toolInput: input.tool_input ?? {} }, START_MS, signal).then(() => TERMINAL_ANSWERED);
414
+ var terminalLiveTimeoutOutput = (onTimeout, question) => {
415
+ switch (onTimeout) {
416
+ case "allow":
417
+ return allow();
418
+ case "deny":
419
+ return deny(unansweredRefusalReason(isAwayPaused(question)));
420
+ case "leave_to_terminal":
421
+ return ask(WITHDRAWN_APPROVAL_REASON);
422
+ }
423
+ };
424
+ var outcomeOfFailedTerminalLiveAsk = (error, sessionId, refusesUnanswered) => isQuestionLimitRefusal(error) ? questionLimitOutput(questionLimitHandBack({ refusesUnanswered, agentPromptFollows: true })) : outcomeOfFailedAsk(error, sessionId, void 0);
425
+ var handleTerminalLive = async (apiKey, input, description, projectName, toolPolicy, holdBudgetSeconds, machineId, toolTarget, decision, signals) => {
426
+ const plan = terminalLiveWait(toolPolicy, holdBudgetSeconds);
427
+ const sessionId = input.session_id;
428
+ const stop = new AbortController();
429
+ const terminal = terminalAnswered(input, stop.signal);
430
+ let question;
431
+ const servedEndsAt = Date.now() + plan.waitSeconds * 1e3;
432
+ const requestedDeadline = Math.min(servedEndsAt, START_MS + plan.holdSeconds * 1e3);
433
+ try {
434
+ question = await askUser(apiKey, {
435
+ question: `Allow ${description || UNNAMED_ACTION}?`,
436
+ type: "confirm",
437
+ context: `Agent wants to run this in ${projectName}`,
438
+ agentName: agentNameIn(projectName),
439
+ sessionId,
440
+ machineId,
441
+ toolName: input.tool_name,
442
+ toolTarget,
443
+ waitEndsAt: plan.onTimeout === "leave_to_terminal" ? new Date(requestedDeadline).toISOString() : void 0,
444
+ ...decision
445
+ });
446
+ } catch (error) {
447
+ stop.abort();
448
+ return outcomeOfFailedTerminalLiveAsk(error, sessionId, plan.onTimeout === "deny");
449
+ }
450
+ if (isDecisionStopped(question)) {
451
+ stop.abort();
452
+ return void 0;
453
+ }
454
+ const deadline = storedWaitDeadline(question, requestedDeadline);
455
+ const first = await Promise.race([waitForQuestion(apiKey, question, deadline, 2e3, stop.signal), terminal]).finally(() => stop.abort());
456
+ if (first === TERMINAL_ANSWERED) {
457
+ await withdrawQuestion(apiKey, question.correlationId);
458
+ return void 0;
459
+ }
460
+ if (isDecisionStopped(first)) return void 0;
461
+ if (!first.answered && isLocalQuestionHandoff(question)) return ask("Approve in the current client.");
462
+ if (first.answered) {
463
+ if (isDeferAnswer(first.value)) return ask(HANDED_BACK_REASON);
464
+ signals.humanDecision = true;
465
+ return first.value === "yes" ? allow() : deny(denyReasonFrom(first.value, first.note));
466
+ }
467
+ return terminalLiveTimeoutOutput(deadline < servedEndsAt ? "leave_to_terminal" : plan.onTimeout, question);
468
+ };
469
+ var pushesToThePhone = (mode) => mode !== "terminal_only" && mode !== "notify_only";
305
470
  var emitDecisionEpisode = (apiKey, input, toolPolicy, machineId, toolTarget, decision, result) => {
306
471
  const verdict = result?.hookSpecificOutput?.permissionDecision;
307
472
  const outcome = verdict === "allow" ? "approve" : verdict === "deny" ? "deny" : null;
@@ -330,7 +495,7 @@ var emitDecisionEpisode = (apiKey, input, toolPolicy, machineId, toolTarget, dec
330
495
  machineId
331
496
  });
332
497
  };
333
- var dispatchModeHandler = async (apiKey, input, toolPolicy, machineId, trainingConsent = false, scopeReason, scopePath) => {
498
+ var dispatchModeHandler = async (apiKey, input, toolPolicy, machineId, trainingConsent = false, scopeReason, scopePath, terminalLive) => {
334
499
  const description = describeToolCall(input.tool_name, input.tool_input, "hook");
335
500
  const projectName = basename(input.cwd ?? process.cwd());
336
501
  const sessionId = input.session_id;
@@ -358,7 +523,11 @@ var dispatchModeHandler = async (apiKey, input, toolPolicy, machineId, trainingC
358
523
  };
359
524
  const signals = {};
360
525
  const runHandler = () => {
361
- switch (ownPrompt ? toolPolicy.mode : approvalModeWithoutLocalPrompt(toolPolicy.mode)) {
526
+ const mode = ownPrompt ? toolPolicy.mode : approvalModeWithoutLocalPrompt(toolPolicy.mode);
527
+ if (terminalLive && pushesToThePhone(mode)) {
528
+ return handleTerminalLive(apiKey, input, description, projectName, { ...toolPolicy, mode }, terminalLive.holdBudgetSeconds, machineId, toolTarget, decision, signals);
529
+ }
530
+ switch (mode) {
362
531
  case "push_only":
363
532
  return handlePushOnly(apiKey, description, projectName, toolPolicy.timeoutSeconds, ownPrompt ? toolPolicy.timeoutAction : timeoutActionWithoutLocalPrompt(toolPolicy), sessionId, machineId, input.tool_name, toolTarget, decision, signals, agentPromptFollows);
364
533
  case "terminal_only":
@@ -403,6 +572,16 @@ var keylessNoticeOnce = (sessionId) => {
403
572
  }
404
573
  };
405
574
  var shouldDeferToNativeMode = defersToNativeMode;
575
+ var terminalLiveFactsOf = (input) => ({
576
+ source: ACTIVE_SOURCE,
577
+ sessionAttended: process.env[CLAUDE_SESSION_ATTENDED_ENV] === "1",
578
+ permissionHookAdmitted: isAdmitted(readAdmissionRules(), "claude", "PermissionRequest"),
579
+ handedDownByTheMacBridge: HANDED_DOWN_BY_THE_MAC_BRIDGE,
580
+ fromCursor: isCursorHookPayload(input),
581
+ permissionMode: input.permission_mode,
582
+ toolName: input.tool_name
583
+ });
584
+ var preToolUseHandsOff = (input, gateInput, verdict) => claudePromptTakesTheCall(terminalLiveFactsOf(input), gateInput, verdict);
406
585
  var handleAskUserQuestion = async (apiKey, input, modeState) => {
407
586
  if (questionHandedToTheTerminal(input.tool_name, modeState)) return void 0;
408
587
  const questions = parseAgentQuestions(input.tool_input ?? {});
@@ -599,6 +778,7 @@ var handlePreToolUse = async (input, canonical) => {
599
778
  case "defer":
600
779
  return undecidedCall(SOLE_GATE_UNDECIDED_REASON);
601
780
  default:
781
+ if (preToolUseHandsOff(input, gateInput, verdict)) return ask(TERMINAL_AND_PHONE_REASON);
602
782
  return await dispatchModeHandler(apiKey, input, verdict.policy, getMachineId(), modeState.trainingConsent, verdict.scopeReason, verdict.scopePath);
603
783
  }
604
784
  } catch (error) {
@@ -616,9 +796,9 @@ var toPermissionRequestOutput = (hookOutput) => {
616
796
  if (d.permissionDecision === "deny") return permReqDeny(d.permissionDecisionReason);
617
797
  return void 0;
618
798
  };
619
- var handlePermissionRequest = async (input, canonical) => {
799
+ var handlePermissionRequest = async (input, canonical, holdBudgetSeconds) => {
620
800
  const claudeRules = claudePermissionRulesFor(input);
621
- if (canonical === void 0 && !claudeRulesMayCover(input, claudeRules) && isPermissionRequestOwnedByPreToolUse(input.tool_name, input.permission_mode, shouldDeferToNativeMode)) return void 0;
801
+ if (canonical === void 0 && !claudeRulesMayCover(input, claudeRules) && isPermissionRequestOwnedByPreToolUse(input.tool_name, input.permission_mode, shouldDeferToNativeMode) && !terminalStaysLiveForTheCall(terminalLiveFactsOf(input))) return void 0;
622
802
  if (isPusharyOwnTool(input.tool_name)) return void 0;
623
803
  let apiKey;
624
804
  try {
@@ -642,7 +822,14 @@ var handlePermissionRequest = async (input, canonical) => {
642
822
  if (canonical === null) return permReqDeny(UNREADABLE_CALL_REASON);
643
823
  if (agentQuestionStrategy(ACTIVE_SOURCE, "PermissionRequest", input.tool_name) === AGENT_QUESTION_STRATEGY.native) return void 0;
644
824
  if (verdict.kind === "gate" && verdict.scopeReason && (canonical?.toolInputs.length ?? 0) > 1) return permReqDeny(SCOPE_BATCH_REASON);
645
- if (!(verdict.kind === "gate" && verdict.scopeReason) && !claudeAllowlistOwnsCall(gateInput, verdict, input.permission_mode, claudeRules) && isPermissionRequestOwnedByPreToolUse(input.tool_name, input.permission_mode, shouldDeferToNativeMode)) return void 0;
825
+ const handedOff = preToolUseHandsOff(input, gateInput, verdict);
826
+ const terminalLive = terminalStaysLiveForTheCall(terminalLiveFactsOf(input));
827
+ const scopeQuestion = verdict.kind === "gate" && Boolean(verdict.scopeReason);
828
+ const preToolUseRanForTheCall = PRETOOLUSE_HANDLED_TOOLS.has(input.tool_name) && isAdmitted(readAdmissionRules(), "claude", "PreToolUse");
829
+ const preToolUseAskedTheScopeQuestion = scopeQuestion && terminalLive && preToolUseRanForTheCall;
830
+ const preToolUseOwnsTheCall = !scopeQuestion && !claudeAllowlistOwnsCall(gateInput, verdict, input.permission_mode, claudeRules) && isPermissionRequestOwnedByPreToolUse(input.tool_name, input.permission_mode, shouldDeferToNativeMode);
831
+ if (!handedOff && (preToolUseAskedTheScopeQuestion || preToolUseOwnsTheCall)) return void 0;
832
+ const terminalLiveDispatch = handedOff ? { holdBudgetSeconds } : void 0;
646
833
  switch (verdict.kind) {
647
834
  case "allow":
648
835
  return permReqAllow();
@@ -651,7 +838,7 @@ var handlePermissionRequest = async (input, canonical) => {
651
838
  return void 0;
652
839
  default:
653
840
  return toPermissionRequestOutput(
654
- await dispatchModeHandler(apiKey, input, verdict.policy, getMachineId(), modeState.trainingConsent, verdict.scopeReason, verdict.scopePath)
841
+ await dispatchModeHandler(apiKey, input, verdict.policy, getMachineId(), modeState.trainingConsent, verdict.scopeReason, verdict.scopePath, terminalLiveDispatch)
655
842
  );
656
843
  }
657
844
  } catch (error) {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  HOOK_HOLD_BUDGET_ENV,
3
3
  hookHoldBudgetProof
4
- } from "./chunk-MNKIR2ZB.js";
4
+ } from "./chunk-EJR5JXMV.js";
5
5
 
6
6
  // src/hold-budget.ts
7
7
  var takeHookHoldBudget = (args = process.argv.slice(2), env = process.env) => {
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  macAppBridgeAvailable
3
- } from "./chunk-VNRWQCGU.js";
3
+ } from "./chunk-7LK3Y6ET.js";
4
4
  import {
5
5
  keyPrefix
6
- } from "./chunk-BLAIETMP.js";
6
+ } from "./chunk-CNJ7SI5V.js";
7
7
  import {
8
8
  bridgeAgentKey
9
- } from "./chunk-H4BKC2GN.js";
9
+ } from "./chunk-GDLT32I4.js";
10
10
  import {
11
11
  readJsonSafe
12
- } from "./chunk-BSZIPIFA.js";
12
+ } from "./chunk-KS3O6O7E.js";
13
13
  import {
14
14
  isValidApiKey
15
- } from "./chunk-MNKIR2ZB.js";
15
+ } from "./chunk-EJR5JXMV.js";
16
16
 
17
17
  // src/shared-mcp-key.ts
18
18
  var asRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  resolveInstallSourceFor
3
- } from "./chunk-2VADJ6JH.js";
3
+ } from "./chunk-XIJTXRWB.js";
4
4
  import {
5
5
  backupFile,
6
6
  readJsonSafe,
7
7
  writeFileAtomic,
8
8
  writeJsonAtomic
9
- } from "./chunk-BSZIPIFA.js";
9
+ } from "./chunk-KS3O6O7E.js";
10
10
  import {
11
11
  configFilePath
12
- } from "./chunk-Y64JGYTB.js";
12
+ } from "./chunk-A47UKPC7.js";
13
13
  import {
14
14
  API_KEY_PREFIX_PATTERN,
15
15
  isRecord
16
- } from "./chunk-MNKIR2ZB.js";
16
+ } from "./chunk-EJR5JXMV.js";
17
17
 
18
18
  // src/shell-alias.ts
19
19
  import { homedir } from "os";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  writeJsonAtomicDurable
3
- } from "./chunk-BSZIPIFA.js";
3
+ } from "./chunk-KS3O6O7E.js";
4
4
 
5
5
  // src/transcript-recipient-pin.ts
6
6
  import { existsSync, mkdirSync, readFileSync, rmSync } from "fs";
@@ -12,7 +12,7 @@ import {
12
12
  reachVerdict,
13
13
  reachesYou,
14
14
  verifyDelivery
15
- } from "./chunk-JTOBQUIL.js";
15
+ } from "./chunk-MNNQEUQC.js";
16
16
  import {
17
17
  humanIsTty,
18
18
  writeHuman
@@ -24,14 +24,14 @@ import {
24
24
  } from "./chunk-FO3MDD26.js";
25
25
  import {
26
26
  reportedInstallSource
27
- } from "./chunk-2VADJ6JH.js";
27
+ } from "./chunk-XIJTXRWB.js";
28
28
  import {
29
29
  NPX_LATEST_COMMAND
30
- } from "./chunk-BAGBCSXA.js";
30
+ } from "./chunk-GO7YKPT3.js";
31
31
  import {
32
32
  NO_PAIRING_COMPUTER,
33
33
  PAIR_TTL_SECONDS
34
- } from "./chunk-MNKIR2ZB.js";
34
+ } from "./chunk-EJR5JXMV.js";
35
35
 
36
36
  // src/onboarding.ts
37
37
  import qrcodeTerminal from "qrcode-terminal";
@@ -1,46 +1,46 @@
1
1
  import {
2
2
  ensureCodexTrustsPusharyHooks
3
- } from "./chunk-LV6DX4P6.js";
3
+ } from "./chunk-JL3SZJJP.js";
4
4
  import {
5
5
  fxIgnoresInstructionsFile,
6
6
  fxInstructionsBody,
7
7
  fxServerHolder,
8
8
  macAppFxBridge,
9
9
  refreshClineKey
10
- } from "./chunk-MPPTIZ2G.js";
10
+ } from "./chunk-6ZXCWK27.js";
11
11
  import {
12
12
  OPENCODE_HOOK_BINARY,
13
13
  agentHookOwner,
14
14
  installOpenCodePlugin,
15
15
  macAppBridgeAvailable,
16
+ openCodeConfigFile,
16
17
  openCodeHookArgv,
17
18
  openCodeInstructionsFile,
18
19
  openCodePluginDir,
19
- openCodeWired
20
- } from "./chunk-VNRWQCGU.js";
20
+ openCodeWired,
21
+ withOpenCodeAskRules
22
+ } from "./chunk-7LK3Y6ET.js";
21
23
  import {
22
24
  detectClaudeVersion
23
- } from "./chunk-IZTDX2MX.js";
25
+ } from "./chunk-MDIGG3Q7.js";
24
26
  import {
25
27
  addCompatibleHooks,
26
28
  compatibleConfigFile,
27
29
  compatibleInstructionsFile,
28
30
  hasCompatibleHooks
29
- } from "./chunk-ML2Q6WWE.js";
31
+ } from "./chunk-3EZP5ZPS.js";
30
32
  import {
33
+ GEMINI_HOOK_BINARY,
34
+ addGeminiHooks,
35
+ addGeminiMcpServer,
31
36
  claudeUnprovenHolds,
32
37
  claudeWired,
33
38
  codexUnprovenHolds,
34
39
  codexWired,
35
40
  geminiWired,
41
+ hasGeminiHooks,
36
42
  planHooks
37
- } from "./chunk-KA7JIMTG.js";
38
- import {
39
- GEMINI_HOOK_BINARY,
40
- addGeminiHooks,
41
- addGeminiMcpServer,
42
- hasGeminiHooks
43
- } from "./chunk-6QPDBD7Q.js";
43
+ } from "./chunk-YQDCTID4.js";
44
44
  import {
45
45
  CLAUDE_HOOK_BINARY,
46
46
  CLAUDE_HOOK_EVENTS,
@@ -85,7 +85,7 @@ import {
85
85
  renderAgentInstructions,
86
86
  supportedClaudeEvents,
87
87
  vscodePluginMcp
88
- } from "./chunk-H4BKC2GN.js";
88
+ } from "./chunk-GDLT32I4.js";
89
89
  import {
90
90
  formatVersion,
91
91
  readJsonSafe,
@@ -93,12 +93,12 @@ import {
93
93
  resolveGlobalBinary,
94
94
  writeFileAtomic,
95
95
  writeJsonAtomic
96
- } from "./chunk-BSZIPIFA.js";
96
+ } from "./chunk-KS3O6O7E.js";
97
97
  import {
98
98
  HOOK_COMPATIBLE_PROFILES,
99
99
  isCompatibleAgentType,
100
100
  isRecord
101
- } from "./chunk-MNKIR2ZB.js";
101
+ } from "./chunk-EJR5JXMV.js";
102
102
 
103
103
  // src/reapply.ts
104
104
  import { dirname, join } from "path";
@@ -354,15 +354,28 @@ var reapplyCursorPlugin = (apiKey, paths = {}) => reapplyPluginMcp(
354
354
  apiKey
355
355
  );
356
356
  var reapplyVsCodePlugin = (apiKey, paths = {}) => reapplyPluginMcp(paths.pluginMcpPath ?? vscodePluginMcp(), apiKey);
357
+ var addOpenCodeAskRulesWhereUnset = (configFile) => {
358
+ const read = readJsonSafe(configFile);
359
+ if (read.kind !== "ok" || !isRecord(read.value)) return false;
360
+ const { config: withRules, change } = withOpenCodeAskRules(read.value);
361
+ if (change.kind !== "added") return false;
362
+ writeJsonAtomic(configFile, withRules, modeToPreserve(configFile));
363
+ return true;
364
+ };
357
365
  var reapplyOpenCodePlugin = (paths = {}) => {
358
366
  const pluginDir = paths.pluginDir ?? openCodePluginDir();
359
367
  if (!openCodeWired(pluginDir)) return { configured: false, reapplied: false, hooks: false, mcp: false };
360
368
  try {
361
369
  installOpenCodePlugin(openCodeHookArgv((paths.resolveBinary ?? resolveGlobalBinary)(OPENCODE_HOOK_BINARY)), pluginDir);
362
- return { configured: true, reapplied: true, hooks: true, mcp: false };
363
370
  } catch {
364
371
  return { configured: true, reapplied: false, hooks: false, mcp: false, failed: true };
365
372
  }
373
+ try {
374
+ const addedRules = addOpenCodeAskRulesWhereUnset(paths.configFile ?? openCodeConfigFile());
375
+ return { configured: true, reapplied: true, hooks: true, mcp: false, ...addedRules ? { approvalRules: true } : {} };
376
+ } catch {
377
+ return { configured: true, reapplied: true, hooks: true, mcp: false, failed: true };
378
+ }
366
379
  };
367
380
  var NOT_CONFIGURED = { configured: false, reapplied: false, hooks: false, mcp: false };
368
381
  var CONFIGURED_BUT_FAILED = { configured: true, reapplied: false, hooks: false, mcp: false, failed: true };
@@ -8,20 +8,20 @@ import {
8
8
  everyProgramFileIsThere,
9
9
  installedCodexApp,
10
10
  shellQuoted
11
- } from "./chunk-5TNGBDIE.js";
11
+ } from "./chunk-AVWECJSN.js";
12
12
  import {
13
13
  writeHuman
14
14
  } from "./chunk-QCGNGEGE.js";
15
15
  import {
16
16
  xml
17
- } from "./chunk-5NP3OJ5P.js";
17
+ } from "./chunk-57NTMVMI.js";
18
18
  import {
19
19
  CODEX_APP_LAUNCH_AT_LOGIN_VERB,
20
20
  CODEX_APP_LAUNCH_VARIABLE
21
21
  } from "./chunk-E46NE7FB.js";
22
22
  import {
23
23
  writeFileAtomic
24
- } from "./chunk-BSZIPIFA.js";
24
+ } from "./chunk-KS3O6O7E.js";
25
25
 
26
26
  // src/codex-app-cards-switch.ts
27
27
  import { execFileSync as execFileSync2 } from "child_process";