@pushary/agent-hooks 1.9.65 → 1.9.67
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/CHANGELOG.md +18 -0
- package/data/SKILL.md +1 -1
- package/data/cursor-plugin/skills/pushary/SKILL.md +1 -1
- package/data/vscode-plugin/skills/pushary/SKILL.md +1 -1
- package/dist/{api-32B3XLJR.js → api-3WIPD5GC.js} +12 -10
- package/dist/bin/pushary-bell-hook.js +25 -27
- package/dist/bin/pushary-bell.js +10 -10
- package/dist/bin/pushary-claude.js +26 -28
- package/dist/bin/pushary-clean.js +74 -77
- package/dist/bin/pushary-codex-bridge.js +21 -22
- package/dist/bin/pushary-codex-hook.js +30 -32
- package/dist/bin/pushary-codex-session.js +8 -8
- package/dist/bin/pushary-codex.js +13 -13
- package/dist/bin/pushary-connect.js +28 -28
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +40 -41
- package/dist/bin/pushary-disconnect.js +25 -26
- package/dist/bin/pushary-doctor.js +118 -76
- package/dist/bin/pushary-elicitation-hook.js +25 -27
- package/dist/bin/pushary-gemini-bridge.js +15 -16
- package/dist/bin/pushary-gemini-hook.js +27 -29
- package/dist/bin/pushary-hook.js +29 -31
- package/dist/bin/pushary-login.js +20 -19
- package/dist/bin/pushary-logout.js +26 -28
- package/dist/bin/pushary-mcp.js +11 -11
- package/dist/bin/pushary-mode.js +16 -15
- package/dist/bin/pushary-notification-hook.js +24 -26
- package/dist/bin/pushary-opencode-hook.js +26 -28
- package/dist/bin/pushary-permission-denied-hook.js +29 -31
- package/dist/bin/pushary-permission-hook.js +32 -31
- package/dist/bin/pushary-post-hook.js +24 -26
- package/dist/bin/pushary-prompt-hook.js +24 -26
- package/dist/bin/pushary-session-end-hook.js +26 -28
- package/dist/bin/pushary-session-start-hook.js +34 -35
- package/dist/bin/pushary-setup.js +111 -99
- package/dist/bin/pushary-stats.js +5 -5
- package/dist/bin/pushary-status.js +37 -36
- package/dist/bin/pushary-stop-hook.js +24 -26
- package/dist/bin/pushary-stopfailure-hook.js +24 -26
- package/dist/bin/pushary-suggestions.js +15 -14
- package/dist/bin/pushary-transcript-register.js +16 -18
- package/dist/bin/pushary-transcripts.js +18 -17
- package/dist/bin/pushary-upgrade.js +45 -39
- package/dist/bin/pushary-wait.js +26 -20
- package/dist/bin/pushary.js +7 -7
- package/dist/{chunk-Y64JGYTB.js → chunk-32OSSYGZ.js} +1 -1
- package/dist/{chunk-UHS5MMAI.js → chunk-3THDNNVL.js} +3 -3
- package/dist/{chunk-OA6EADFT.js → chunk-564IPPBL.js} +3 -3
- package/dist/chunk-5MOJCOCF.js +25 -0
- package/dist/{chunk-RRHHWYPX.js → chunk-6H4E5UBA.js} +6 -6
- package/dist/{chunk-UM2X7HL6.js → chunk-6LXXKMV6.js} +42 -16
- package/dist/chunk-76SWHCAR.js +299 -0
- package/dist/{chunk-AN7C3CHH.js → chunk-77L2TNJW.js} +1 -1
- package/dist/{chunk-XJIIU36L.js → chunk-7YJSDXLM.js} +1 -1
- package/dist/{chunk-XZCJC3QO.js → chunk-B3V2WOJX.js} +285 -50
- package/dist/{chunk-RABLE5R6.js → chunk-BBFGDYYI.js} +1 -1
- package/dist/{chunk-GTBWWCCM.js → chunk-BFF3ABFV.js} +115 -9
- package/dist/{chunk-NOH3EJFH.js → chunk-C4EDAHXI.js} +1 -1
- package/dist/{chunk-ITYKN2K3.js → chunk-CHYBOTQN.js} +2 -2
- package/dist/{chunk-FFSIYBBU.js → chunk-CJZLIAJF.js} +27 -10
- package/dist/{chunk-UMTMFDL2.js → chunk-CPLQ5OHX.js} +1 -1
- package/dist/{chunk-DTVHZDYM.js → chunk-D3U6CN7T.js} +26 -12
- package/dist/{chunk-ICFHMJTW.js → chunk-D7Z4USSB.js} +27 -8
- package/dist/{chunk-DXMUJTYF.js → chunk-DP6LU6BB.js} +2 -2
- package/dist/{chunk-GA257T2E.js → chunk-DXCHIKAN.js} +8 -8
- package/dist/{chunk-PP7FC3Y7.js → chunk-EQR6JSFE.js} +9 -7
- package/dist/{chunk-Z3M627TQ.js → chunk-FONWVVDD.js} +1 -1
- package/dist/{chunk-3KXBRSL3.js → chunk-GMHQYX3D.js} +1 -1
- package/dist/{chunk-WY6YAMZW.js → chunk-ICQCQAUO.js} +5 -4
- package/dist/{chunk-CZ65D6Q5.js → chunk-IPG3RS23.js} +2 -2
- package/dist/{chunk-JCWHOB36.js → chunk-IRV2MNMA.js} +53 -3
- package/dist/{chunk-HIYLAC3C.js → chunk-IVB5O6NY.js} +3 -3
- package/dist/{chunk-DM24VZOI.js → chunk-IXTVUINH.js} +3 -3
- package/dist/{chunk-BEQ3MEE3.js → chunk-JBRMIADM.js} +2 -2
- package/dist/{chunk-CU2PXLZ6.js → chunk-JHB5SR5B.js} +4 -4
- package/dist/{chunk-ED2SWTPC.js → chunk-JOBSKQ2Z.js} +4 -4
- package/dist/{chunk-W5L2ZLHE.js → chunk-JOD2325S.js} +3 -3
- package/dist/{chunk-CSKABVSH.js → chunk-JSPCGPSH.js} +3 -3
- package/dist/{chunk-R4FL5JHK.js → chunk-KE4TSJUD.js} +4 -4
- package/dist/{chunk-3LF7T3NW.js → chunk-KIXRHNCW.js} +6 -6
- package/dist/{chunk-BKCI53W6.js → chunk-KNUYUQBP.js} +4 -4
- package/dist/{chunk-4LUWNBGD.js → chunk-KTLE7NIK.js} +23 -8
- package/dist/{chunk-ADXHPGBZ.js → chunk-LJHDUKBB.js} +6 -6
- package/dist/{chunk-U2P2LQ5B.js → chunk-LS42NV6E.js} +15 -6
- package/dist/{chunk-QNZ6G3LY.js → chunk-LYTWK5TF.js} +5 -5
- package/dist/{chunk-2VADJ6JH.js → chunk-METUT7V4.js} +2 -2
- package/dist/{chunk-P5HZEGT7.js → chunk-NSS2AIGI.js} +6 -6
- package/dist/{chunk-QSW3HAKI.js → chunk-NWN6GGJL.js} +61 -2
- package/dist/{chunk-7ILQX47W.js → chunk-P4RXSZDT.js} +3 -3
- package/dist/{chunk-FWS2KLV2.js → chunk-PIBWX3EF.js} +2 -2
- package/dist/{chunk-F56GTJFG.js → chunk-PO2P3XDC.js} +5 -5
- package/dist/{chunk-EEVE7H5Z.js → chunk-R2BZH2NK.js} +4 -4
- package/dist/{chunk-I2UBF2SS.js → chunk-RO2B4YBF.js} +1 -1
- package/dist/{chunk-MNKIR2ZB.js → chunk-RQOT6QL3.js} +67 -6
- package/dist/{chunk-Q2BD4IBX.js → chunk-SB6TYV4M.js} +11 -13
- package/dist/{chunk-ATPJGHUP.js → chunk-TI4JYPGQ.js} +7 -3
- package/dist/{chunk-LMC5UPL7.js → chunk-UC2J5RR4.js} +1 -1
- package/dist/{chunk-IZVEBBDH.js → chunk-UQIPWXBL.js} +3 -3
- package/dist/{chunk-VHKK3US3.js → chunk-UXN73YAE.js} +3 -3
- package/dist/{chunk-WOHP7EL6.js → chunk-VEYFXRZ6.js} +7 -6
- package/dist/{chunk-2Z7I7DHT.js → chunk-VHMZPN3N.js} +1 -1
- package/dist/{chunk-HVOQUPSO.js → chunk-VKZ3RJ5U.js} +1 -1
- package/dist/chunk-VVIQWGS7.js +2298 -0
- package/dist/{chunk-SCMEVDAR.js → chunk-WJLHZ6E6.js} +3 -3
- package/dist/{chunk-BAGBCSXA.js → chunk-X2MTVD4W.js} +1 -1
- package/dist/{chunk-3UNAPXED.js → chunk-XFDWLDHG.js} +9 -9
- package/dist/{chunk-6CZX6YOE.js → chunk-XNUWKKGS.js} +3 -3
- package/dist/{chunk-NUEPJ5W6.js → chunk-XZKSUJ55.js} +3 -5
- package/dist/{chunk-HDGB6SQ4.js → chunk-Y4MRHAE5.js} +2 -2
- package/dist/{chunk-SZBMTOUZ.js → chunk-Y6ZN454E.js} +1 -1
- package/dist/{chunk-TK4JU2F7.js → chunk-YSD4SMHE.js} +1 -1
- package/dist/{chunk-SROWUG5W.js → chunk-ZK5WYAMI.js} +1 -1
- package/dist/{chunk-JFKMPLNF.js → chunk-ZPIYJLQ3.js} +2 -2
- package/dist/{codex-app-cards-switch-HK4RMOBI.js → codex-app-cards-switch-25TDTMUZ.js} +11 -11
- package/dist/{codex-desktop-launch-OCO6N7SA.js → codex-desktop-launch-JDKUCD2Y.js} +1 -1
- package/dist/{codex-session-launch-ETZQTVFS.js → codex-session-launch-6N35AAEO.js} +18 -19
- package/dist/{config-YY54G3CC.js → config-OBGYURFX.js} +3 -3
- package/dist/{global-upgrade-5DN5G3ZQ.js → global-upgrade-2QNQD5OM.js} +10 -10
- package/dist/{identity-EGL6HAWG.js → identity-CUPX2U7Q.js} +4 -4
- package/dist/{live-capabilities-WU3FYJJG.js → live-capabilities-CUEIWMSM.js} +5 -5
- package/dist/{paths-ZG6KDM6Y.js → paths-BHLBZ6FY.js} +7 -3
- package/dist/{reapply-AQLNCCTE.js → reapply-4GXLWZ5M.js} +27 -27
- package/dist/src/index.js +30 -20
- package/dist/{upgrade-lock-BIRXIKHJ.js → upgrade-lock-RDKTAHWR.js} +4 -4
- package/dist/{usage-43C2DHDU.js → usage-KX2HN47E.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-ANP3DIST.js +0 -118
- package/dist/chunk-ONBL2GCU.js +0 -615
- package/dist/chunk-TJUJAK5L.js +0 -62
|
@@ -4,19 +4,24 @@ import {
|
|
|
4
4
|
outcomeOfFailedAsk,
|
|
5
5
|
reportMachineDenial,
|
|
6
6
|
storedWaitDeadline
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-6H4E5UBA.js";
|
|
8
8
|
import {
|
|
9
9
|
isGatingMoment,
|
|
10
10
|
recordKeylessMoment
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-7YJSDXLM.js";
|
|
12
|
+
import {
|
|
13
|
+
isAdmitted,
|
|
14
|
+
readAdmissionRules
|
|
15
|
+
} from "./chunk-EQR6JSFE.js";
|
|
12
16
|
import {
|
|
13
17
|
isDeferAnswer,
|
|
14
18
|
oncePerSession
|
|
15
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-CPLQ5OHX.js";
|
|
16
20
|
import {
|
|
17
21
|
activeHookSource,
|
|
18
|
-
activeIdentity
|
|
19
|
-
|
|
22
|
+
activeIdentity,
|
|
23
|
+
namedSource
|
|
24
|
+
} from "./chunk-ICQCQAUO.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-
|
|
48
|
+
} from "./chunk-XFDWLDHG.js";
|
|
44
49
|
import {
|
|
45
50
|
repoKeyFor
|
|
46
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-GMHQYX3D.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
|
-
|
|
66
|
+
waitsForThePhone,
|
|
67
|
+
withdrawQuestion
|
|
68
|
+
} from "./chunk-D3U6CN7T.js";
|
|
63
69
|
import {
|
|
64
70
|
getMachineId
|
|
65
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-PIBWX3EF.js";
|
|
66
72
|
import {
|
|
67
73
|
claudeConfigDir
|
|
68
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-TI4JYPGQ.js";
|
|
69
75
|
import {
|
|
70
76
|
getApiKey,
|
|
71
77
|
getBaseUrl
|
|
72
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-32OSSYGZ.js";
|
|
73
79
|
import {
|
|
74
80
|
NPX_LATEST_COMMAND
|
|
75
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-X2MTVD4W.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-
|
|
136
|
+
} from "./chunk-RQOT6QL3.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
|
|
254
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
150
255
|
var restrictionsOnly = (document) => {
|
|
151
|
-
if (!
|
|
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 =
|
|
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 {
|
|
@@ -191,34 +296,45 @@ var reportDecisionEpisode = async (apiKey, input) => {
|
|
|
191
296
|
|
|
192
297
|
// src/hook.ts
|
|
193
298
|
import { basename } from "path";
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
var ACTIVE_SOURCE = activeHookSource(process.argv);
|
|
197
|
-
var PUSHARY_IS_SOLE_GATE = ACTIVE_SOURCE !== null && hookCompatibleProfileBySource(ACTIVE_SOURCE)?.soleGate != null;
|
|
198
|
-
var agentNameIn = (projectName) => `${ACTIVE_AGENT.label} - ${projectName}`;
|
|
299
|
+
|
|
300
|
+
// src/pre-tool-use-output.ts
|
|
199
301
|
var allow = (reason) => claudePreToolUseOutput("allow", reason);
|
|
200
302
|
var deny = (reason) => claudePreToolUseOutput("deny", reason);
|
|
201
|
-
var allowWithInput = (updatedInput) => claudePreToolUseOutput("allow", void 0, updatedInput);
|
|
202
303
|
var ask = (reason) => claudePreToolUseOutput("ask", reason);
|
|
203
304
|
var halted = (reason) => claudeStoppedPreToolUseOutput(reason);
|
|
204
|
-
|
|
305
|
+
|
|
306
|
+
// src/permission-ask-outcome.ts
|
|
205
307
|
var questionLimitOutput = (handBack) => handBack.kind === "refuse" ? deny(handBack.reason) : ask(QUESTION_LIMIT_PROMPT_REASON);
|
|
206
|
-
var outcomeOfFailedPermissionAsk = (error, sessionId, questionLimitFacts) => {
|
|
308
|
+
var outcomeOfFailedPermissionAsk = (error, sessionId, questionLimitFacts, subscriptionRequiredOutput) => {
|
|
207
309
|
if (isQuestionLimitRefusal(error)) return questionLimitOutput(questionLimitHandBack(questionLimitFacts));
|
|
208
|
-
return outcomeOfFailedAsk(error, sessionId, halted(UNVERIFIABLE_APPROVAL_REASON)) ??
|
|
310
|
+
return outcomeOfFailedAsk(error, sessionId, halted(UNVERIFIABLE_APPROVAL_REASON)) ?? subscriptionRequiredOutput;
|
|
209
311
|
};
|
|
210
|
-
var handlePushOnly = async (apiKey,
|
|
312
|
+
var handlePushOnly = async (apiKey, call, timeoutSeconds, timeoutAction, signals) => {
|
|
313
|
+
const {
|
|
314
|
+
description,
|
|
315
|
+
projectName,
|
|
316
|
+
agentName,
|
|
317
|
+
sessionId,
|
|
318
|
+
machineId,
|
|
319
|
+
toolName,
|
|
320
|
+
toolTarget,
|
|
321
|
+
decision,
|
|
322
|
+
agentPromptFollows,
|
|
323
|
+
startMs,
|
|
324
|
+
subscriptionRequiredOutput,
|
|
325
|
+
announceWait: announceWait2
|
|
326
|
+
} = call;
|
|
211
327
|
const effectiveWait = effectiveWaitSeconds(timeoutAction, timeoutSeconds, "claude");
|
|
212
328
|
const now = Date.now();
|
|
213
|
-
const requestedDeadline = hookWaitDeadline(
|
|
214
|
-
const clampCut = hookWaitClamped(
|
|
329
|
+
const requestedDeadline = hookWaitDeadline(startMs, effectiveWait, "claude", now);
|
|
330
|
+
const clampCut = hookWaitClamped(startMs, effectiveWait, "claude", now);
|
|
215
331
|
let result;
|
|
216
332
|
try {
|
|
217
333
|
result = await askUser(apiKey, {
|
|
218
334
|
question: `Allow ${description || UNNAMED_ACTION}?`,
|
|
219
335
|
type: "confirm",
|
|
220
336
|
context: `Agent wants to run this in ${projectName}`,
|
|
221
|
-
agentName
|
|
337
|
+
agentName,
|
|
222
338
|
sessionId,
|
|
223
339
|
machineId,
|
|
224
340
|
toolName,
|
|
@@ -230,11 +346,11 @@ var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, ti
|
|
|
230
346
|
return outcomeOfFailedPermissionAsk(error, sessionId, {
|
|
231
347
|
refusesUnanswered: refusesWhenUnanswered({ mode: "push_only", timeoutAction }),
|
|
232
348
|
agentPromptFollows
|
|
233
|
-
});
|
|
349
|
+
}, subscriptionRequiredOutput);
|
|
234
350
|
}
|
|
235
351
|
if (isDecisionStopped(result)) return halted(DECISION_STOP_REASON);
|
|
236
352
|
const deadline = storedWaitDeadline(result, requestedDeadline);
|
|
237
|
-
if (waitsForThePhone(result))
|
|
353
|
+
if (waitsForThePhone(result)) announceWait2(effectiveWait, result.answerUrl, sessionId);
|
|
238
354
|
const answer = await waitForQuestion(apiKey, result, deadline);
|
|
239
355
|
if (isDecisionStopped(answer)) return halted(DECISION_STOP_REASON);
|
|
240
356
|
if (!answer.answered && isLocalQuestionHandoff(result)) return ask("Approve in the current client.");
|
|
@@ -255,18 +371,29 @@ var handlePushOnly = async (apiKey, description, projectName, timeoutSeconds, ti
|
|
|
255
371
|
return ask("No push response, asking in terminal");
|
|
256
372
|
}
|
|
257
373
|
};
|
|
258
|
-
var
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
374
|
+
var handlePushFirst = async (apiKey, call, pushFirstSeconds, signals) => {
|
|
375
|
+
const {
|
|
376
|
+
description,
|
|
377
|
+
projectName,
|
|
378
|
+
agentName,
|
|
379
|
+
sessionId,
|
|
380
|
+
machineId,
|
|
381
|
+
toolName,
|
|
382
|
+
toolTarget,
|
|
383
|
+
decision,
|
|
384
|
+
agentPromptFollows,
|
|
385
|
+
startMs,
|
|
386
|
+
subscriptionRequiredOutput,
|
|
387
|
+
announceWait: announceWait2
|
|
388
|
+
} = call;
|
|
389
|
+
const requestedDeadline = hookWaitDeadline(startMs, pushFirstSeconds, "claude", Date.now());
|
|
263
390
|
let result;
|
|
264
391
|
try {
|
|
265
392
|
result = await askUser(apiKey, {
|
|
266
393
|
question: `Allow ${description || UNNAMED_ACTION}?`,
|
|
267
394
|
type: "confirm",
|
|
268
395
|
context: `Agent wants to run this in ${projectName}`,
|
|
269
|
-
agentName
|
|
396
|
+
agentName,
|
|
270
397
|
sessionId,
|
|
271
398
|
machineId,
|
|
272
399
|
toolName,
|
|
@@ -275,10 +402,10 @@ var handlePushFirst = async (apiKey, description, projectName, pushFirstSeconds,
|
|
|
275
402
|
...decision
|
|
276
403
|
});
|
|
277
404
|
} catch (error) {
|
|
278
|
-
return outcomeOfFailedPermissionAsk(error, sessionId, { refusesUnanswered: false, agentPromptFollows });
|
|
405
|
+
return outcomeOfFailedPermissionAsk(error, sessionId, { refusesUnanswered: false, agentPromptFollows }, subscriptionRequiredOutput);
|
|
279
406
|
}
|
|
280
407
|
if (isDecisionStopped(result)) return halted(DECISION_STOP_REASON);
|
|
281
|
-
if (waitsForThePhone(result))
|
|
408
|
+
if (waitsForThePhone(result)) announceWait2(pushFirstSeconds, result.answerUrl, sessionId);
|
|
282
409
|
const deadline = storedWaitDeadline(result, requestedDeadline);
|
|
283
410
|
const answer = await waitForQuestion(apiKey, result, deadline, 1500);
|
|
284
411
|
if (isDecisionStopped(answer)) return halted(DECISION_STOP_REASON);
|
|
@@ -289,6 +416,20 @@ var handlePushFirst = async (apiKey, description, projectName, pushFirstSeconds,
|
|
|
289
416
|
}
|
|
290
417
|
return ask(WITHDRAWN_APPROVAL_REASON);
|
|
291
418
|
};
|
|
419
|
+
|
|
420
|
+
// src/hook.ts
|
|
421
|
+
var START_MS = Date.now();
|
|
422
|
+
var ACTIVE_AGENT = activeIdentity(process.argv);
|
|
423
|
+
var ACTIVE_SOURCE = activeHookSource(process.argv);
|
|
424
|
+
var HANDED_DOWN_BY_THE_MAC_BRIDGE = namedSource(process.argv) !== void 0;
|
|
425
|
+
var CLAUDE_SESSION_ATTENDED_ENV = "CLAUDE_CODE_SESSION_ATTENDED";
|
|
426
|
+
var PUSHARY_IS_SOLE_GATE = ACTIVE_SOURCE !== null && hookCompatibleProfileBySource(ACTIVE_SOURCE)?.soleGate != null;
|
|
427
|
+
var agentNameIn = (projectName) => `${ACTIVE_AGENT.label} - ${projectName}`;
|
|
428
|
+
var allowWithInput = (updatedInput) => claudePreToolUseOutput("allow", void 0, updatedInput);
|
|
429
|
+
var undecidedCall = (reason) => PUSHARY_IS_SOLE_GATE ? deny(reason) : void 0;
|
|
430
|
+
var handleTerminalOnly = () => {
|
|
431
|
+
return ask();
|
|
432
|
+
};
|
|
292
433
|
var handleNotifyOnly = async (apiKey, description, projectName, sessionId, machineId) => {
|
|
293
434
|
try {
|
|
294
435
|
await sendNotification(apiKey, {
|
|
@@ -302,6 +443,64 @@ var handleNotifyOnly = async (apiKey, description, projectName, sessionId, machi
|
|
|
302
443
|
}
|
|
303
444
|
return ask();
|
|
304
445
|
};
|
|
446
|
+
var TERMINAL_ANSWERED = /* @__PURE__ */ Symbol("terminal-answered");
|
|
447
|
+
var terminalAnswered = (input, signal) => watchForTerminalAnswer(input.transcript_path, { toolName: input.tool_name, toolInput: input.tool_input ?? {} }, START_MS, signal).then(() => TERMINAL_ANSWERED);
|
|
448
|
+
var terminalLiveTimeoutOutput = (onTimeout, question) => {
|
|
449
|
+
switch (onTimeout) {
|
|
450
|
+
case "allow":
|
|
451
|
+
return allow();
|
|
452
|
+
case "deny":
|
|
453
|
+
return deny(unansweredRefusalReason(isAwayPaused(question)));
|
|
454
|
+
case "leave_to_terminal":
|
|
455
|
+
return ask(WITHDRAWN_APPROVAL_REASON);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
var outcomeOfFailedTerminalLiveAsk = (error, sessionId, refusesUnanswered) => isQuestionLimitRefusal(error) ? questionLimitOutput(questionLimitHandBack({ refusesUnanswered, agentPromptFollows: true })) : outcomeOfFailedAsk(error, sessionId, void 0);
|
|
459
|
+
var handleTerminalLive = async (apiKey, input, description, projectName, toolPolicy, holdBudgetSeconds, machineId, toolTarget, decision, signals) => {
|
|
460
|
+
const plan = terminalLiveWait(toolPolicy, holdBudgetSeconds);
|
|
461
|
+
const sessionId = input.session_id;
|
|
462
|
+
const stop = new AbortController();
|
|
463
|
+
const terminal = terminalAnswered(input, stop.signal);
|
|
464
|
+
let question;
|
|
465
|
+
const servedEndsAt = Date.now() + plan.waitSeconds * 1e3;
|
|
466
|
+
const requestedDeadline = Math.min(servedEndsAt, START_MS + plan.holdSeconds * 1e3);
|
|
467
|
+
try {
|
|
468
|
+
question = await askUser(apiKey, {
|
|
469
|
+
question: `Allow ${description || UNNAMED_ACTION}?`,
|
|
470
|
+
type: "confirm",
|
|
471
|
+
context: `Agent wants to run this in ${projectName}`,
|
|
472
|
+
agentName: agentNameIn(projectName),
|
|
473
|
+
sessionId,
|
|
474
|
+
machineId,
|
|
475
|
+
toolName: input.tool_name,
|
|
476
|
+
toolTarget,
|
|
477
|
+
waitEndsAt: plan.onTimeout === "leave_to_terminal" ? new Date(requestedDeadline).toISOString() : void 0,
|
|
478
|
+
...decision
|
|
479
|
+
});
|
|
480
|
+
} catch (error) {
|
|
481
|
+
stop.abort();
|
|
482
|
+
return outcomeOfFailedTerminalLiveAsk(error, sessionId, plan.onTimeout === "deny");
|
|
483
|
+
}
|
|
484
|
+
if (isDecisionStopped(question)) {
|
|
485
|
+
stop.abort();
|
|
486
|
+
return void 0;
|
|
487
|
+
}
|
|
488
|
+
const deadline = storedWaitDeadline(question, requestedDeadline);
|
|
489
|
+
const first = await Promise.race([waitForQuestion(apiKey, question, deadline, 2e3, stop.signal), terminal]).finally(() => stop.abort());
|
|
490
|
+
if (first === TERMINAL_ANSWERED) {
|
|
491
|
+
await withdrawQuestion(apiKey, question.correlationId);
|
|
492
|
+
return void 0;
|
|
493
|
+
}
|
|
494
|
+
if (isDecisionStopped(first)) return void 0;
|
|
495
|
+
if (!first.answered && isLocalQuestionHandoff(question)) return ask("Approve in the current client.");
|
|
496
|
+
if (first.answered) {
|
|
497
|
+
if (isDeferAnswer(first.value)) return ask(HANDED_BACK_REASON);
|
|
498
|
+
signals.humanDecision = true;
|
|
499
|
+
return first.value === "yes" ? allow() : deny(denyReasonFrom(first.value, first.note));
|
|
500
|
+
}
|
|
501
|
+
return terminalLiveTimeoutOutput(deadline < servedEndsAt ? "leave_to_terminal" : plan.onTimeout, question);
|
|
502
|
+
};
|
|
503
|
+
var pushesToThePhone = (mode) => mode !== "terminal_only" && mode !== "notify_only";
|
|
305
504
|
var emitDecisionEpisode = (apiKey, input, toolPolicy, machineId, toolTarget, decision, result) => {
|
|
306
505
|
const verdict = result?.hookSpecificOutput?.permissionDecision;
|
|
307
506
|
const outcome = verdict === "allow" ? "approve" : verdict === "deny" ? "deny" : null;
|
|
@@ -330,7 +529,7 @@ var emitDecisionEpisode = (apiKey, input, toolPolicy, machineId, toolTarget, dec
|
|
|
330
529
|
machineId
|
|
331
530
|
});
|
|
332
531
|
};
|
|
333
|
-
var dispatchModeHandler = async (apiKey, input, toolPolicy, machineId, trainingConsent = false, scopeReason, scopePath) => {
|
|
532
|
+
var dispatchModeHandler = async (apiKey, input, toolPolicy, machineId, trainingConsent = false, scopeReason, scopePath, terminalLive) => {
|
|
334
533
|
const description = describeToolCall(input.tool_name, input.tool_input, "hook");
|
|
335
534
|
const projectName = basename(input.cwd ?? process.cwd());
|
|
336
535
|
const sessionId = input.session_id;
|
|
@@ -357,18 +556,36 @@ var dispatchModeHandler = async (apiKey, input, toolPolicy, machineId, trainingC
|
|
|
357
556
|
permissionMode: input.permission_mode
|
|
358
557
|
};
|
|
359
558
|
const signals = {};
|
|
559
|
+
const permissionAsk = {
|
|
560
|
+
description,
|
|
561
|
+
projectName,
|
|
562
|
+
agentName: agentNameIn(projectName),
|
|
563
|
+
sessionId,
|
|
564
|
+
machineId,
|
|
565
|
+
toolName: input.tool_name,
|
|
566
|
+
toolTarget,
|
|
567
|
+
decision,
|
|
568
|
+
agentPromptFollows,
|
|
569
|
+
startMs: START_MS,
|
|
570
|
+
subscriptionRequiredOutput: undecidedCall(SOLE_GATE_SUBSCRIPTION_REASON),
|
|
571
|
+
announceWait
|
|
572
|
+
};
|
|
360
573
|
const runHandler = () => {
|
|
361
|
-
|
|
574
|
+
const mode = ownPrompt ? toolPolicy.mode : approvalModeWithoutLocalPrompt(toolPolicy.mode);
|
|
575
|
+
if (terminalLive && pushesToThePhone(mode)) {
|
|
576
|
+
return handleTerminalLive(apiKey, input, description, projectName, { ...toolPolicy, mode }, terminalLive.holdBudgetSeconds, machineId, toolTarget, decision, signals);
|
|
577
|
+
}
|
|
578
|
+
switch (mode) {
|
|
362
579
|
case "push_only":
|
|
363
|
-
return handlePushOnly(apiKey,
|
|
580
|
+
return handlePushOnly(apiKey, permissionAsk, toolPolicy.timeoutSeconds, ownPrompt ? toolPolicy.timeoutAction : timeoutActionWithoutLocalPrompt(toolPolicy), signals);
|
|
364
581
|
case "terminal_only":
|
|
365
582
|
return Promise.resolve(handleTerminalOnly());
|
|
366
583
|
case "push_first":
|
|
367
|
-
return handlePushFirst(apiKey,
|
|
584
|
+
return handlePushFirst(apiKey, permissionAsk, toolPolicy.pushFirstSeconds, signals);
|
|
368
585
|
case "notify_only":
|
|
369
586
|
return handleNotifyOnly(apiKey, description, projectName, sessionId, machineId);
|
|
370
587
|
default:
|
|
371
|
-
return handlePushFirst(apiKey,
|
|
588
|
+
return handlePushFirst(apiKey, permissionAsk, toolPolicy.pushFirstSeconds, signals);
|
|
372
589
|
}
|
|
373
590
|
};
|
|
374
591
|
const result = await runHandler();
|
|
@@ -403,6 +620,16 @@ var keylessNoticeOnce = (sessionId) => {
|
|
|
403
620
|
}
|
|
404
621
|
};
|
|
405
622
|
var shouldDeferToNativeMode = defersToNativeMode;
|
|
623
|
+
var terminalLiveFactsOf = (input) => ({
|
|
624
|
+
source: ACTIVE_SOURCE,
|
|
625
|
+
sessionAttended: process.env[CLAUDE_SESSION_ATTENDED_ENV] === "1",
|
|
626
|
+
permissionHookAdmitted: isAdmitted(readAdmissionRules(), "claude", "PermissionRequest"),
|
|
627
|
+
handedDownByTheMacBridge: HANDED_DOWN_BY_THE_MAC_BRIDGE,
|
|
628
|
+
fromCursor: isCursorHookPayload(input),
|
|
629
|
+
permissionMode: input.permission_mode,
|
|
630
|
+
toolName: input.tool_name
|
|
631
|
+
});
|
|
632
|
+
var preToolUseHandsOff = (input, gateInput, verdict) => claudePromptTakesTheCall(terminalLiveFactsOf(input), gateInput, verdict);
|
|
406
633
|
var handleAskUserQuestion = async (apiKey, input, modeState) => {
|
|
407
634
|
if (questionHandedToTheTerminal(input.tool_name, modeState)) return void 0;
|
|
408
635
|
const questions = parseAgentQuestions(input.tool_input ?? {});
|
|
@@ -599,6 +826,7 @@ var handlePreToolUse = async (input, canonical) => {
|
|
|
599
826
|
case "defer":
|
|
600
827
|
return undecidedCall(SOLE_GATE_UNDECIDED_REASON);
|
|
601
828
|
default:
|
|
829
|
+
if (preToolUseHandsOff(input, gateInput, verdict)) return ask(TERMINAL_AND_PHONE_REASON);
|
|
602
830
|
return await dispatchModeHandler(apiKey, input, verdict.policy, getMachineId(), modeState.trainingConsent, verdict.scopeReason, verdict.scopePath);
|
|
603
831
|
}
|
|
604
832
|
} catch (error) {
|
|
@@ -616,9 +844,9 @@ var toPermissionRequestOutput = (hookOutput) => {
|
|
|
616
844
|
if (d.permissionDecision === "deny") return permReqDeny(d.permissionDecisionReason);
|
|
617
845
|
return void 0;
|
|
618
846
|
};
|
|
619
|
-
var handlePermissionRequest = async (input, canonical) => {
|
|
847
|
+
var handlePermissionRequest = async (input, canonical, holdBudgetSeconds) => {
|
|
620
848
|
const claudeRules = claudePermissionRulesFor(input);
|
|
621
|
-
if (canonical === void 0 && !claudeRulesMayCover(input, claudeRules) && isPermissionRequestOwnedByPreToolUse(input.tool_name, input.permission_mode, shouldDeferToNativeMode)) return void 0;
|
|
849
|
+
if (canonical === void 0 && !claudeRulesMayCover(input, claudeRules) && isPermissionRequestOwnedByPreToolUse(input.tool_name, input.permission_mode, shouldDeferToNativeMode) && !terminalStaysLiveForTheCall(terminalLiveFactsOf(input))) return void 0;
|
|
622
850
|
if (isPusharyOwnTool(input.tool_name)) return void 0;
|
|
623
851
|
let apiKey;
|
|
624
852
|
try {
|
|
@@ -642,7 +870,14 @@ var handlePermissionRequest = async (input, canonical) => {
|
|
|
642
870
|
if (canonical === null) return permReqDeny(UNREADABLE_CALL_REASON);
|
|
643
871
|
if (agentQuestionStrategy(ACTIVE_SOURCE, "PermissionRequest", input.tool_name) === AGENT_QUESTION_STRATEGY.native) return void 0;
|
|
644
872
|
if (verdict.kind === "gate" && verdict.scopeReason && (canonical?.toolInputs.length ?? 0) > 1) return permReqDeny(SCOPE_BATCH_REASON);
|
|
645
|
-
|
|
873
|
+
const handedOff = preToolUseHandsOff(input, gateInput, verdict);
|
|
874
|
+
const terminalLive = terminalStaysLiveForTheCall(terminalLiveFactsOf(input));
|
|
875
|
+
const scopeQuestion = verdict.kind === "gate" && Boolean(verdict.scopeReason);
|
|
876
|
+
const preToolUseRanForTheCall = PRETOOLUSE_HANDLED_TOOLS.has(input.tool_name) && isAdmitted(readAdmissionRules(), "claude", "PreToolUse");
|
|
877
|
+
const preToolUseAskedTheScopeQuestion = scopeQuestion && terminalLive && preToolUseRanForTheCall;
|
|
878
|
+
const preToolUseOwnsTheCall = !scopeQuestion && !claudeAllowlistOwnsCall(gateInput, verdict, input.permission_mode, claudeRules) && isPermissionRequestOwnedByPreToolUse(input.tool_name, input.permission_mode, shouldDeferToNativeMode);
|
|
879
|
+
if (!handedOff && (preToolUseAskedTheScopeQuestion || preToolUseOwnsTheCall)) return void 0;
|
|
880
|
+
const terminalLiveDispatch = handedOff ? { holdBudgetSeconds } : void 0;
|
|
646
881
|
switch (verdict.kind) {
|
|
647
882
|
case "allow":
|
|
648
883
|
return permReqAllow();
|
|
@@ -651,7 +886,7 @@ var handlePermissionRequest = async (input, canonical) => {
|
|
|
651
886
|
return void 0;
|
|
652
887
|
default:
|
|
653
888
|
return toPermissionRequestOutput(
|
|
654
|
-
await dispatchModeHandler(apiKey, input, verdict.policy, getMachineId(), modeState.trainingConsent, verdict.scopeReason, verdict.scopePath)
|
|
889
|
+
await dispatchModeHandler(apiKey, input, verdict.policy, getMachineId(), modeState.trainingConsent, verdict.scopeReason, verdict.scopePath, terminalLiveDispatch)
|
|
655
890
|
);
|
|
656
891
|
}
|
|
657
892
|
} catch (error) {
|