@pushary/agent-hooks 0.98.0 → 1.0.0

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 (81) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/bin/pushary-bell-hook.js +20 -19
  3. package/dist/bin/pushary-bell.js +7 -6
  4. package/dist/bin/pushary-claude.js +24 -25
  5. package/dist/bin/pushary-clean.js +61 -18
  6. package/dist/bin/pushary-codex-bridge.js +11 -10
  7. package/dist/bin/pushary-codex-hook.js +43 -35
  8. package/dist/bin/pushary-codex.js +7 -7
  9. package/dist/bin/pushary-connect.js +7 -6
  10. package/dist/bin/pushary-cowork.js +3 -3
  11. package/dist/bin/pushary-daemon.js +24 -23
  12. package/dist/bin/pushary-disconnect.js +48 -7
  13. package/dist/bin/pushary-doctor.js +61 -21
  14. package/dist/bin/pushary-elicitation-hook.js +19 -18
  15. package/dist/bin/pushary-gemini-bridge.js +10 -9
  16. package/dist/bin/pushary-gemini-hook.js +27 -28
  17. package/dist/bin/pushary-hook.js +63 -34
  18. package/dist/bin/pushary-login.js +8 -7
  19. package/dist/bin/pushary-logout.js +5 -4
  20. package/dist/bin/pushary-mode.js +3 -3
  21. package/dist/bin/pushary-notification-hook.js +28 -22
  22. package/dist/bin/pushary-opencode-hook.js +29 -30
  23. package/dist/bin/pushary-permission-denied-hook.js +26 -24
  24. package/dist/bin/pushary-permission-hook.js +34 -25
  25. package/dist/bin/pushary-post-hook.js +32 -23
  26. package/dist/bin/pushary-prompt-hook.js +28 -22
  27. package/dist/bin/pushary-session-end-hook.js +28 -22
  28. package/dist/bin/pushary-session-start-hook.js +32 -26
  29. package/dist/bin/pushary-setup.js +146 -37
  30. package/dist/bin/pushary-stats.js +2 -2
  31. package/dist/bin/pushary-status.js +11 -10
  32. package/dist/bin/pushary-stop-hook.js +29 -23
  33. package/dist/bin/pushary-stopfailure-hook.js +28 -22
  34. package/dist/bin/pushary-transcript-register.js +18 -17
  35. package/dist/bin/pushary-transcripts.js +6 -5
  36. package/dist/bin/pushary-upgrade.js +15 -14
  37. package/dist/bin/pushary-wait.js +3 -3
  38. package/dist/{chunk-HFQ7WSHZ.js → chunk-2PS3DTHQ.js} +7 -4
  39. package/dist/{chunk-W2MG2P46.js → chunk-2SL5X7UB.js} +9 -6
  40. package/dist/{chunk-53QCTSWY.js → chunk-3C4ERARU.js} +4 -4
  41. package/dist/{chunk-LBNAGGLV.js → chunk-3UUAK2AK.js} +3 -2
  42. package/dist/{chunk-V47GITZV.js → chunk-447SBIC7.js} +15 -2
  43. package/dist/{chunk-VZD2GEKB.js → chunk-4LL6LG5X.js} +1 -43
  44. package/dist/{chunk-WU6LSXJ6.js → chunk-7WMFPYR5.js} +2 -2
  45. package/dist/{chunk-XPPXOKIG.js → chunk-7ZDFVWRR.js} +1 -1
  46. package/dist/chunk-A3D43VXO.js +44 -0
  47. package/dist/{chunk-3RVCQDC4.js → chunk-BOZETFXB.js} +1 -1
  48. package/dist/{chunk-LX4NNYII.js → chunk-BPD4P3NN.js} +2 -2
  49. package/dist/{chunk-IRLTW6KF.js → chunk-CDTGL7TT.js} +1 -1
  50. package/dist/{chunk-CP34FI4B.js → chunk-D2N2EJH2.js} +2 -2
  51. package/dist/{chunk-J44RPHUZ.js → chunk-DMJTWULU.js} +2 -2
  52. package/dist/{chunk-SJLGOVC6.js → chunk-DOYARIYB.js} +4 -2
  53. package/dist/{chunk-XOOWC62T.js → chunk-DUOG74E7.js} +1 -1
  54. package/dist/{chunk-OPR2BPD4.js → chunk-EMCNSBF3.js} +1 -1
  55. package/dist/{chunk-5KLAETSU.js → chunk-GTR72MIU.js} +1 -1
  56. package/dist/{chunk-5B5QV3KC.js → chunk-HGEI7SVM.js} +8 -8
  57. package/dist/chunk-HS2KEAFD.js +224 -0
  58. package/dist/{chunk-6CYB77VD.js → chunk-I3TWY7GH.js} +30 -29
  59. package/dist/{chunk-NQGYPTJQ.js → chunk-IBU7UU3R.js} +1 -1
  60. package/dist/{chunk-M4TVEMLH.js → chunk-IWZG2Y4Z.js} +1 -1
  61. package/dist/{chunk-TZHDKRMG.js → chunk-IYA3NCZG.js} +2 -2
  62. package/dist/{chunk-XL3RKJYQ.js → chunk-LTPQKBJH.js} +65 -30
  63. package/dist/{chunk-YJDRHQLG.js → chunk-MDV7ZRAT.js} +698 -43
  64. package/dist/{chunk-QXIF6R4P.js → chunk-MGI6MLFU.js} +16 -16
  65. package/dist/{chunk-DOD3A6EU.js → chunk-MSOFZYI3.js} +4 -4
  66. package/dist/{chunk-JQU6PMH4.js → chunk-R744XA2M.js} +62 -45
  67. package/dist/{chunk-FAJ4R7IB.js → chunk-RFNMSC6N.js} +1 -1
  68. package/dist/{chunk-NIXVMMJM.js → chunk-S2W4ZSHE.js} +1 -1
  69. package/dist/{chunk-OUAE3GKO.js → chunk-TTBRBWAK.js} +278 -1
  70. package/dist/{chunk-6C6MS57P.js → chunk-TVY7AT6O.js} +1 -1
  71. package/dist/{chunk-2GLOHBYB.js → chunk-UWQVODHT.js} +1 -1
  72. package/dist/{chunk-6LRGZJFB.js → chunk-XK34HTMW.js} +10 -6
  73. package/dist/{chunk-SI7GYBQP.js → chunk-XKDKUFMP.js} +1 -1
  74. package/dist/{chunk-HB2KB7QL.js → chunk-XNXIMIFY.js} +1 -1
  75. package/dist/{chunk-K6CH5NVW.js → chunk-Y55JNI2I.js} +1 -1
  76. package/dist/{chunk-OBYC6SN4.js → chunk-YKLCWWEI.js} +1 -1
  77. package/dist/{chunk-B35OCFXH.js → chunk-ZZIUQVAN.js} +4 -4
  78. package/dist/reapply-I4NB5SSM.js +30 -0
  79. package/dist/src/index.js +13 -10
  80. package/package.json +1 -1
  81. package/dist/chunk-A76GNRIK.js +0 -608
@@ -1,608 +0,0 @@
1
- import {
2
- ACTION_BODY_MAX,
3
- AGENT_LABELS_BY_TYPE,
4
- DECISION_LINE_MAX,
5
- FILE_TARGET_TOOLS,
6
- GATE_MAX_TOOL_INPUTS,
7
- redactSecrets,
8
- redactSecretsDeep
9
- } from "./chunk-OUAE3GKO.js";
10
-
11
- // ../hook-mapping/src/describe.ts
12
- import { isAbsolute, relative, resolve } from "path";
13
- var hookPrefixes = {
14
- Bash: (input) => `bash: ${input.command ?? "(no command)"}`,
15
- PowerShell: (input) => `powershell: ${input.command ?? "(no command)"}`,
16
- Monitor: (input) => `monitor: ${input.command ?? "(no command)"}`,
17
- Write: (input) => `write file: ${input.file_path ?? "(unknown path)"}`,
18
- Edit: (input) => `edit file: ${input.file_path ?? "(unknown path)"}`,
19
- MultiEdit: (input) => `edit file: ${input.file_path ?? "(unknown path)"}`,
20
- Read: (input) => `read file: ${input.file_path ?? "(unknown path)"}`,
21
- // Without an entry here the fallback stringifies the whole tool input, and for
22
- // ExitPlanMode that input IS the plan: the action line became a truncated dump of
23
- // the plan markdown. The plan belongs in the body, not the one-line summary.
24
- ExitPlanMode: () => "finished planning and wants to start coding",
25
- // Mirrored on PostToolUse since the matcher was widened. Without entries the
26
- // fallback prints `Task: done` and `WebSearch: done`, which name the tool and
27
- // drop the only part a person reads: which subagent, and what it looked for.
28
- Task: (input) => `subagent: ${describeTaskCall(input)}`,
29
- WebSearch: (input) => `web search: ${firstString(input.query) ?? "(no query)"}`
30
- };
31
- var describeTaskCall = (input) => firstString(input.description) ?? firstString(input.subagent_type) ?? "(unnamed)";
32
- var describeTodoProgress = (todos) => {
33
- if (!Array.isArray(todos)) return "updated the plan";
34
- const items = todos.filter((t) => !!t && typeof t === "object");
35
- const total = items.length;
36
- if (total === 0) return "updated the plan";
37
- const done = items.filter((t) => t.status === "completed").length;
38
- const current = items.find((t) => t.status === "in_progress");
39
- const label = current ? [current.activeForm, current.content].find((v) => typeof v === "string" && v.trim().length > 0) : void 0;
40
- if (done >= total) return `finished all ${total} tasks`;
41
- if (label) return `${label.trim()} (${done}/${total} done)`;
42
- return `${done}/${total} tasks done`;
43
- };
44
- var eventPrefixes = {
45
- Bash: (input) => `ran: ${String(input.command ?? "")}`,
46
- PowerShell: (input) => `ran: ${String(input.command ?? "")}`,
47
- Monitor: (input) => `ran: ${String(input.command ?? "")}`,
48
- Write: (input) => `wrote: ${input.file_path ?? "unknown"}`,
49
- Edit: (input) => `edited: ${input.file_path ?? "unknown"}`,
50
- MultiEdit: (input) => `edited: ${input.file_path ?? "unknown"}`,
51
- Read: (input) => `read: ${input.file_path ?? "unknown"}`,
52
- TodoWrite: (input) => describeTodoProgress(input.todos),
53
- Task: (input) => `ran subagent: ${describeTaskCall(input)}`,
54
- WebSearch: (input) => `searched: ${firstString(input.query) ?? "the web"}`
55
- };
56
- var MCP_TOOL_PREFIX = "mcp__";
57
- var parseMcpToolName = (toolName) => {
58
- if (!toolName.startsWith(MCP_TOOL_PREFIX)) return void 0;
59
- const rest = toolName.slice(MCP_TOOL_PREFIX.length);
60
- const split = rest.indexOf("__");
61
- if (split <= 0 || split + 2 >= rest.length) return void 0;
62
- return { server: rest.slice(0, split), tool: rest.slice(split + 2) };
63
- };
64
- var NETWORK_TARGET_KEYS = ["url", "urls", "href", "host", "hosts", "hostname", "domain", "domains"];
65
- var ABSOLUTE_HTTP_URL = /^https?:\/\//i;
66
- var HOSTNAME = /^(?=.{1,253}$)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?::\d{1,5})?$/i;
67
- var hostOfUrl = (value) => {
68
- try {
69
- return new URL(value).host || void 0;
70
- } catch {
71
- return void 0;
72
- }
73
- };
74
- var hostOf = (value, bareHostnames) => {
75
- if (typeof value !== "string") return void 0;
76
- const trimmed = value.trim();
77
- if (ABSOLUTE_HTTP_URL.test(trimmed)) return hostOfUrl(trimmed);
78
- return bareHostnames && HOSTNAME.test(trimmed) ? trimmed : void 0;
79
- };
80
- var firstHost = (value, bareHostnames) => Array.isArray(value) ? value.map((entry) => hostOf(entry, bareHostnames)).find((host) => host !== void 0) : hostOf(value, bareHostnames);
81
- var deriveNetworkTarget = (toolInput) => {
82
- for (const key of NETWORK_TARGET_KEYS) {
83
- const host = firstHost(toolInput[key], true);
84
- if (host) return host;
85
- }
86
- return Object.values(toolInput).map((value) => firstHost(value, false)).find((host) => host !== void 0);
87
- };
88
- var describeUnbuiltCall = (toolName, toolInput, format) => {
89
- const mcp = parseMcpToolName(toolName);
90
- const host = deriveNetworkTarget(toolInput);
91
- if (mcp) {
92
- if (format === "event") return `called ${mcp.tool} on ${mcp.server}`;
93
- return host ? `call ${mcp.tool} on ${mcp.server} to reach ${host}` : `call ${mcp.tool} on ${mcp.server}`;
94
- }
95
- if (host) return format === "event" ? `accessed ${host}` : `network access to ${host}`;
96
- return void 0;
97
- };
98
- var EVENT_ACTION_MAX = 120;
99
- var HOOK_FALLBACK_MAX = 200;
100
- var UNNAMED_ACTION = "this request";
101
- var describeToolCall = (toolName, toolInput, format = "hook") => {
102
- if (!toolName) return "";
103
- const prefixes = format === "hook" ? hookPrefixes : eventPrefixes;
104
- const builder = prefixes[toolName];
105
- const unbuilt = builder ? void 0 : describeUnbuiltCall(toolName, toolInput ?? {}, format);
106
- const detail = JSON.stringify(toolInput ?? {});
107
- const raw = builder ? builder(toolInput) : unbuilt ?? (format === "hook" ? detail === "{}" || detail === void 0 ? toolName : `${toolName}: ${detail}` : `${toolName}: done`);
108
- const redacted = redactSecrets(raw);
109
- if (format === "event") return redacted.slice(0, EVENT_ACTION_MAX);
110
- return builder || unbuilt ? redacted : redacted.slice(0, HOOK_FALLBACK_MAX);
111
- };
112
- var TOOL_TARGET_MAX_LENGTH = 80;
113
- var deriveCommandHead = (command) => {
114
- if (typeof command !== "string") return void 0;
115
- const head = command.trim().split(/\s+/).slice(0, 2).join(" ");
116
- return head ? redactSecrets(head).slice(0, TOOL_TARGET_MAX_LENGTH) : void 0;
117
- };
118
- var deriveToolTarget = (toolName, toolInput) => {
119
- if (toolName === "Bash" || toolName === "PowerShell" || toolName === "Monitor") {
120
- return deriveCommandHead(toolInput.command);
121
- }
122
- if (FILE_TARGET_TOOLS.has(toolName)) {
123
- const filePath = toolInput.file_path;
124
- if (typeof filePath !== "string") return void 0;
125
- const separator = Math.max(filePath.lastIndexOf("/"), filePath.lastIndexOf("\\"));
126
- const base = filePath.slice(separator + 1);
127
- const dot = base.lastIndexOf(".");
128
- if (dot <= 0) return void 0;
129
- return base.slice(dot).slice(0, TOOL_TARGET_MAX_LENGTH);
130
- }
131
- const mcp = parseMcpToolName(toolName);
132
- if (mcp) return mcp.server.slice(0, TOOL_TARGET_MAX_LENGTH);
133
- return deriveNetworkTarget(toolInput)?.slice(0, TOOL_TARGET_MAX_LENGTH);
134
- };
135
- var SCRIPT_RUNNERS = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun", "npx", "deno", "rake", "make", "just", "task", "turbo"]);
136
- var RUN_SUBCOMMANDS = /* @__PURE__ */ new Set(["run", "run-script", "exec", "task"]);
137
- var deriveReceiptCommandHead = (command) => {
138
- if (typeof command !== "string") return void 0;
139
- const tokens = command.trim().split(/\s+/);
140
- const [first, second] = tokens;
141
- const keep = first && second && SCRIPT_RUNNERS.has(first.toLowerCase()) && RUN_SUBCOMMANDS.has(second.toLowerCase()) ? 3 : 2;
142
- const head = tokens.slice(0, keep).join(" ");
143
- return head ? redactSecrets(head).slice(0, TOOL_TARGET_MAX_LENGTH) : void 0;
144
- };
145
- var RECEIPT_TARGET_MAX_LENGTH = 256;
146
- var parseApplyPatchOps = (command) => {
147
- if (typeof command !== "string") return [];
148
- const ops = [];
149
- for (const line of command.split("\n")) {
150
- const file = line.match(/^\*\*\*\s+(Add|Update|Delete) File:\s+(.+?)\s*$/);
151
- if (file) {
152
- ops.push({ op: file[1].toLowerCase(), path: file[2] });
153
- continue;
154
- }
155
- const move = line.match(/^\*\*\*\s+Move to:\s+(.+?)\s*$/);
156
- if (move) ops.push({ op: "move", path: move[1] });
157
- }
158
- return ops;
159
- };
160
- var parseApplyPatchFiles = (command) => [...new Set(parseApplyPatchOps(command).map((entry) => entry.path))];
161
- var OPENCODE_TOOL_NAMES = {
162
- bash: "Bash",
163
- // A shell command reaching outside the worktree asks a second time under its
164
- // own key. It is still the same command, and still Bash.
165
- external_directory: "Bash",
166
- edit: "Edit",
167
- patch: "Edit",
168
- apply_patch: "Edit",
169
- write: "Write",
170
- read: "Read",
171
- glob: "Glob",
172
- grep: "Grep",
173
- webfetch: "WebFetch",
174
- websearch: "WebSearch",
175
- task: "Task"
176
- };
177
- var canonicalGateInputs = (source, toolName, toolInputs, cwd) => {
178
- if (source === "codex" && toolName === "apply_patch" || source === "vscode" && ["apply_patch", "applyPatch", "copilot_applyPatch"].includes(toolName)) {
179
- const paths = [...new Set(toolInputs.flatMap((input) => parseApplyPatchFiles(input.command ?? input.input ?? input.patch)))];
180
- if (paths.length > GATE_MAX_TOOL_INPUTS) return null;
181
- return {
182
- toolName: "Edit",
183
- toolInputs: paths.length === 0 ? toolInputs.map((input) => ({ file_path: input.command })) : paths.map((path) => ({ file_path: cwd && !isAbsolute(path) ? resolve(cwd, path) : path }))
184
- };
185
- }
186
- if (source === "opencode") {
187
- const canonical2 = OPENCODE_TOOL_NAMES[toolName] ?? toolName;
188
- return {
189
- toolName: canonical2,
190
- toolInputs: toolInputs.map((input) => {
191
- const path = input.file_path;
192
- return typeof path === "string" && path.length > 0 && cwd && !isAbsolute(path) ? { ...input, file_path: resolve(cwd, path) } : input;
193
- })
194
- };
195
- }
196
- if (source === "vscode" || source === "cursor") {
197
- if (source === "vscode" && toolName.startsWith("mcp_pushary_")) return { toolName: `mcp__pushary__${toolName.slice("mcp_pushary_".length)}`, toolInputs };
198
- const alias = toolName.replace(/^copilot_/, "").toLowerCase().replace(/[^a-z0-9]/g, "");
199
- const shells = [
200
- "runterminalcommand",
201
- "runinterminal",
202
- "runcommand",
203
- "terminalcommand",
204
- "executecommand",
205
- "runinterminalcommand",
206
- "bash",
207
- "shell",
208
- "terminal",
209
- "sendtoterminal",
210
- "powershell",
211
- "localshell"
212
- ];
213
- const canonical2 = shells.includes(alias) ? "Bash" : ["createfile", "write", "createdirectory", "createnewjupyternotebook"].includes(alias) ? "Write" : ["replacestringinfile", "multireplacestringinfile", "replacestring", "multireplacestring", "inserteditintofile", "insertedit", "edit", "editfile", "editfiles", "editnotebookfile", "editnotebook"].includes(alias) ? "Edit" : [
214
- "readfile",
215
- "viewimage",
216
- "listdir",
217
- "listdirectory",
218
- "filesearch",
219
- "findfiles",
220
- "grepsearch",
221
- "findtextinfiles",
222
- "semanticsearch",
223
- "searchcodebase",
224
- "searchworkspacesymbols",
225
- "geterrors",
226
- "getchangedfiles",
227
- "readprojectstructure",
228
- "getnotebooksummary",
229
- "readnotebookcelloutput",
230
- "testsearch",
231
- "findtestfiles",
232
- "testfailure",
233
- "getvscodeapi",
234
- "getterminaloutput",
235
- "gettaskoutput",
236
- "terminalselection",
237
- "terminallastcommand"
238
- ].includes(alias) ? "Read" : toolName;
239
- const inputs = toolInputs.flatMap((input) => ["multireplacestringinfile", "multireplacestring"].includes(alias) && Array.isArray(input.replacements) ? input.replacements.filter((item) => !!item && typeof item === "object" && !Array.isArray(item)) : alias === "editfiles" && Array.isArray(input.files) ? input.files.map((file) => typeof file === "string" ? { file_path: file } : file).filter((file) => !!file && typeof file === "object" && !Array.isArray(file)) : [input]);
240
- if (inputs.length > GATE_MAX_TOOL_INPUTS) return null;
241
- return { toolName: canonical2, toolInputs: inputs.map((input) => {
242
- if (canonical2 === "Bash") return { ...input, command: [input.command, input.commandLine, input.command_line, input.cmd, input.script, input.input, input.text].find((value) => typeof value === "string") };
243
- const path = input.file_path ?? input.filePath ?? input.path;
244
- return typeof path === "string" ? { ...input, file_path: cwd && !isAbsolute(path) ? resolve(cwd, path) : path } : input;
245
- }) };
246
- }
247
- if (source !== "gemini") return { toolName, toolInputs };
248
- if (toolName === "run_shell_command") {
249
- return { toolName: "Bash", toolInputs: toolInputs.map((input) => ({ command: input.command })) };
250
- }
251
- const canonical = { write_file: "Write", replace: "Edit", read_file: "Read" }[toolName];
252
- if (!canonical) return { toolName, toolInputs };
253
- return {
254
- toolName: canonical,
255
- toolInputs: toolInputs.map((input) => {
256
- const path = [input.file_path, input.absolute_path, input.path].find((value) => typeof value === "string" && value.length > 0);
257
- return path ? { file_path: cwd && !isAbsolute(path) ? resolve(cwd, path) : path } : {};
258
- })
259
- };
260
- };
261
- var EXIT_CODE_FAILURE = /^Error: Exit code \d+/;
262
- var isToolResultError = (toolResult) => {
263
- try {
264
- if (typeof toolResult === "string") return EXIT_CODE_FAILURE.test(toolResult);
265
- if (!toolResult || typeof toolResult !== "object") return false;
266
- return "error" in toolResult || "is_error" in toolResult;
267
- } catch {
268
- return false;
269
- }
270
- };
271
- var toolResultErrorText = (toolResult) => {
272
- try {
273
- const raw = typeof toolResult === "string" ? toolResult : toolResult && typeof toolResult === "object" ? String(
274
- toolResult.error ?? toolResult.stderr ?? ""
275
- ) : "";
276
- return redactSecrets(raw);
277
- } catch {
278
- return "";
279
- }
280
- };
281
- var relativizeReceiptPath = (filePath, cwd) => {
282
- if (!cwd || !isAbsolute(filePath)) return filePath;
283
- return relative(cwd, filePath);
284
- };
285
- var deriveReceiptMeta = (toolName, toolInput, toolResult, cwd) => {
286
- try {
287
- const ok = !isToolResultError(toolResult);
288
- if (toolName === "apply_patch") {
289
- const files = parseApplyPatchFiles(toolInput.command);
290
- const target = files.length === 1 ? files[0] : toolInput.command;
291
- if (typeof target !== "string" || !target) return void 0;
292
- return { kind: "edit", target: redactSecrets(target).slice(0, RECEIPT_TARGET_MAX_LENGTH), ok };
293
- }
294
- const canonicalTool = {
295
- run_shell_command: "Bash",
296
- write_file: "Write",
297
- replace: "Edit"
298
- }[toolName] ?? toolName;
299
- const canonicalInput = canonicalTool === toolName ? toolInput : {
300
- ...toolInput,
301
- file_path: toolInput.file_path ?? toolInput.absolute_path ?? toolInput.path
302
- };
303
- if (FILE_TARGET_TOOLS.has(canonicalTool)) {
304
- const filePath = canonicalInput.file_path;
305
- if (typeof filePath !== "string" || !filePath) return void 0;
306
- return {
307
- kind: canonicalTool === "Write" ? "write" : "edit",
308
- // A path is an unlikely place for a credential but not an impossible one
309
- // (a token in a temp directory name), and this field is persisted.
310
- target: redactSecrets(relativizeReceiptPath(filePath, cwd)).slice(0, RECEIPT_TARGET_MAX_LENGTH),
311
- ok
312
- };
313
- }
314
- if (canonicalTool === "Bash" || canonicalTool === "PowerShell" || canonicalTool === "Monitor") {
315
- const head = deriveReceiptCommandHead(canonicalInput.command);
316
- if (!head) return void 0;
317
- return {
318
- kind: head === "git commit" ? "commit" : "bash",
319
- target: head,
320
- ok
321
- };
322
- }
323
- return void 0;
324
- } catch {
325
- return void 0;
326
- }
327
- };
328
- var scopePathFor = (toolName, toolInput, cwd) => {
329
- const meta = deriveReceiptMeta(toolName, toolInput, void 0, cwd);
330
- if (!meta) return void 0;
331
- return meta.kind === "edit" || meta.kind === "write" ? meta.target : void 0;
332
- };
333
- var firstString = (...values) => values.find((v) => typeof v === "string" && v.length > 0);
334
- var ACTION_BODY_TRUNCATION_MARKER = "\n\u2026 [truncated]";
335
- var capActionBody = (text) => text.length <= ACTION_BODY_MAX ? text : `${text.slice(0, ACTION_BODY_MAX - ACTION_BODY_TRUNCATION_MARKER.length)}${ACTION_BODY_TRUNCATION_MARKER}`;
336
- var rawActionBody = (toolName, toolInput) => {
337
- if (toolName === "Edit" || toolName === "replace") {
338
- const before = firstString(toolInput.old_string);
339
- const after = firstString(toolInput.new_string);
340
- if (before === void 0 && after === void 0) return firstString(toolInput.diff);
341
- return `- ${before ?? ""}
342
- + ${after ?? ""}`;
343
- }
344
- if (toolName === "Write" || toolName === "write_file") {
345
- return firstString(toolInput.content) ?? firstString(toolInput.diff);
346
- }
347
- if (toolName === "ExitPlanMode") return firstString(toolInput.plan);
348
- if (toolName === "Bash" || toolName === "PowerShell" || toolName === "Monitor" || toolName === "run_shell_command" || toolName === "shell" || toolName === "apply_patch") {
349
- return firstString(toolInput.command);
350
- }
351
- return firstString(toolInput.url, toolInput.href);
352
- };
353
- var deriveActionBody = (toolName, toolInput) => {
354
- try {
355
- const raw = rawActionBody(toolName, toolInput);
356
- if (!raw) return void 0;
357
- return capActionBody(redactSecretsDeep(raw));
358
- } catch {
359
- return void 0;
360
- }
361
- };
362
- var deriveAction = (toolName, toolInput) => describeToolCall(toolName, toolInput, "hook").slice(0, DECISION_LINE_MAX);
363
- var BLOCKER_BY_MODE = {
364
- push_only: "Approval required before this runs",
365
- push_first: "Paused for your approval before continuing",
366
- escalate: "Escalated to you after the approval timeout"
367
- };
368
- var deriveBlocker = (mode, reason) => {
369
- const text = reason?.replace(/\s+/g, " ").trim() || BLOCKER_BY_MODE[mode] || "Waiting for your approval";
370
- return text.slice(0, DECISION_LINE_MAX);
371
- };
372
-
373
- // ../hook-mapping/src/map.ts
374
- import { basename } from "path";
375
- var AGENT_IDENTITIES = {
376
- claude: { type: "claude_code", label: AGENT_LABELS_BY_TYPE.claude_code },
377
- codex: { type: "codex", label: AGENT_LABELS_BY_TYPE.codex },
378
- gemini: { type: "gemini_cli", label: AGENT_LABELS_BY_TYPE.gemini_cli },
379
- cursor: { type: "cursor", label: AGENT_LABELS_BY_TYPE.cursor },
380
- vscode: { type: "vscode", label: AGENT_LABELS_BY_TYPE.vscode },
381
- opencode: { type: "opencode", label: AGENT_LABELS_BY_TYPE.opencode }
382
- };
383
- var TEAMMATE_IDLE_ACTION = "A teammate went idle";
384
- var TASK_TITLE_MAX_LENGTH = 120;
385
- var STOP_SUMMARY_MAX_LENGTH = 200;
386
- var SESSION_END_ACTIONS = {
387
- clear: "Session cleared",
388
- logout: "Logged out",
389
- resume: "Session suspended",
390
- prompt_input_exit: "Session ended",
391
- bypass_permissions_disabled: "Session ended",
392
- other: "Session ended"
393
- };
394
- var countArray = (value) => Array.isArray(value) ? value.length : 0;
395
- var summarizeFinalMessage = (message) => {
396
- if (typeof message !== "string") return "Session ended";
397
- const collapsed = message.replace(/\s+/g, " ").trim();
398
- if (!collapsed) return "Session ended";
399
- return collapsed.slice(0, STOP_SUMMARY_MAX_LENGTH);
400
- };
401
- var summarizeStop = (input) => {
402
- const running = countArray(input.background_tasks);
403
- if (running > 0) return `Paused with ${running} background task${running === 1 ? "" : "s"} still running`;
404
- const crons = countArray(input.session_crons);
405
- if (crons > 0) return `Idle until a scheduled task wakes it (${crons} pending)`;
406
- return summarizeFinalMessage(input.last_assistant_message ?? input.prompt_response);
407
- };
408
- var subagentStopAction = (agentType) => {
409
- const name = typeof agentType === "string" ? agentType.replace(/\s+/g, " ").trim() : "";
410
- return name ? `${name} subagent finished` : "Subagent finished";
411
- };
412
- var subagentStopSummary = (input) => {
413
- const message = input.last_assistant_message ?? input.prompt_response;
414
- if (typeof message === "string" && message.trim()) return summarizeStop(input);
415
- return subagentStopAction(input.agent_type);
416
- };
417
- var COMPACTION_TRIGGERS = {
418
- manual: "on request",
419
- auto: "automatically"
420
- };
421
- var compactionAction = (hookEvent, trigger) => {
422
- const how = typeof trigger === "string" ? COMPACTION_TRIGGERS[trigger] : void 0;
423
- const base = hookEvent === "PostCompact" ? "Compacted context" : "Compacting context";
424
- return how ? `${base} ${how}` : base;
425
- };
426
- var sessionStartAction = (source) => source === "resume" ? "Session resumed" : "Session started";
427
- var sessionEndAction = (reason) => typeof reason === "string" && SESSION_END_ACTIONS[reason] || "Session ended";
428
- var taskTitleFrom = (prompt) => typeof prompt === "string" ? prompt.replace(/\s+/g, " ").trim().slice(0, TASK_TITLE_MAX_LENGTH) || void 0 : void 0;
429
- var asString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
430
- var NOTIFICATION_ACTION_MAX_LENGTH = 200;
431
- var STOP_FAILURE_REASON_MAX_LENGTH = 300;
432
- var notificationTypeOf = (payload) => asString(payload.notification_type) ?? asString(payload.type);
433
- var notificationAction = (payload) => {
434
- const text = asString(payload.message) ?? asString(payload.title);
435
- const collapsed = redactSecrets(text ?? "").replace(/\s+/g, " ").trim();
436
- return collapsed ? collapsed.slice(0, NOTIFICATION_ACTION_MAX_LENGTH) : "Notification";
437
- };
438
- var STOP_FAILURE_REASONS = {
439
- rate_limit: "Rate limited by the API",
440
- overloaded: "The API is overloaded right now",
441
- authentication_failed: "Authentication failed",
442
- billing_error: "A billing error stopped the turn"
443
- };
444
- var STOP_FAILURE_FALLBACK = "The turn ended on an API error (rate limit, billing, or auth).";
445
- var describeStopFailure = (error) => {
446
- const fromString = (value) => {
447
- const trimmed = value.trim();
448
- if (!trimmed) return void 0;
449
- return STOP_FAILURE_REASONS[trimmed] ?? redactSecrets(trimmed).slice(0, STOP_FAILURE_REASON_MAX_LENGTH);
450
- };
451
- if (typeof error === "string") {
452
- const described = fromString(error);
453
- if (described) return described;
454
- } else if (error && typeof error === "object") {
455
- for (const key of ["type", "message", "error", "code"]) {
456
- const value = error[key];
457
- if (typeof value === "string") {
458
- const described = fromString(value);
459
- if (described) return described;
460
- }
461
- }
462
- }
463
- return STOP_FAILURE_FALLBACK;
464
- };
465
- var permissionDeniedAction = (toolName) => {
466
- const tool = asString(toolName);
467
- return tool ? `Auto-mode blocked ${tool}` : "Auto-mode blocked a tool call";
468
- };
469
- var sessionIdentityOf = (payload) => {
470
- const sessionId = asString(payload.session_id ?? payload.sessionId ?? payload.conversation_id);
471
- const agentId = asString(payload.agent_id);
472
- if (!agentId) return { sessionId };
473
- return { sessionId: agentId, parentSessionId: sessionId };
474
- };
475
-
476
- // ../hook-mapping/src/codex-rollout.ts
477
- var CODEX_TURN_CONTEXT_TYPE = "turn_context";
478
- var CODEX_AUTO_REVIEWER = "auto_review";
479
- var CODEX_GUARDIAN_THREAD_SOURCE = "guardian_review";
480
- var CODEX_PLANNED_ACTION_MARKER = "Planned action JSON:\n";
481
- var CODEX_APPROVAL_REQUEST_END = ">>> APPROVAL REQUEST END";
482
- var CODEX_SHELL_TOOL = "exec_command";
483
- var CODEX_SHELL_COMMAND_FLAGS = ["-lc", "-c"];
484
- var asRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
485
- var parseRecord = (text) => {
486
- try {
487
- return asRecord(JSON.parse(text));
488
- } catch {
489
- return null;
490
- }
491
- };
492
- var nonEmptyString = (value) => typeof value === "string" && value.length > 0 ? value : void 0;
493
- var isTimestamp = (value) => typeof value === "string" && Number.isFinite(Date.parse(value));
494
- var readCodexTurnContext = (line) => {
495
- const entry = parseRecord(line);
496
- if (entry?.type !== CODEX_TURN_CONTEXT_TYPE) return null;
497
- const payload = asRecord(entry.payload);
498
- const turnId = nonEmptyString(payload?.turn_id);
499
- if (!payload || !turnId) return null;
500
- return {
501
- turnId,
502
- approvalsReviewer: payload.approvals_reviewer === CODEX_AUTO_REVIEWER ? "auto_review" : "user",
503
- ...isTimestamp(entry.timestamp) ? { startedAt: entry.timestamp } : {}
504
- };
505
- };
506
- var isCodexGuardianRolloutHead = (line, sessionId) => {
507
- if (!sessionId) return false;
508
- const entry = parseRecord(line);
509
- const payload = asRecord(entry?.payload);
510
- return entry?.type === "session_meta" && payload?.parent_thread_id === sessionId && payload.thread_source === CODEX_GUARDIAN_THREAD_SOURCE;
511
- };
512
- var readCodexPlannedAction = (message) => {
513
- const marker = message.lastIndexOf(CODEX_PLANNED_ACTION_MARKER);
514
- if (marker < 0) return null;
515
- const body = message.slice(marker + CODEX_PLANNED_ACTION_MARKER.length);
516
- const end = body.lastIndexOf(CODEX_APPROVAL_REQUEST_END);
517
- if (end < 0 || body.slice(end + CODEX_APPROVAL_REQUEST_END.length).trim().length > 0) return null;
518
- return parseRecord(body.slice(0, end).trim());
519
- };
520
- var readCodexReviewVerdict = (message) => {
521
- const verdict = parseRecord(message.trim());
522
- if (verdict?.outcome !== "allow" && verdict?.outcome !== "deny") return null;
523
- const rationale = typeof verdict.rationale === "string" ? verdict.rationale.trim() : "";
524
- return { outcome: verdict.outcome, ...rationale ? { rationale } : {} };
525
- };
526
- var scanCodexGuardianReviews = (lines) => {
527
- const reviews = [];
528
- let pending = null;
529
- for (const line of lines) {
530
- const entry = parseRecord(line.text);
531
- const payload = asRecord(entry?.payload);
532
- if (entry?.type !== "event_msg" || typeof payload?.message !== "string") continue;
533
- if (payload.type === "user_message") {
534
- const plannedAction = readCodexPlannedAction(payload.message);
535
- pending = plannedAction ? { offset: line.offset, plannedAction, ...isTimestamp(entry.timestamp) ? { requestedAt: entry.timestamp } : {} } : null;
536
- continue;
537
- }
538
- if (payload.type !== "agent_message" || !pending) continue;
539
- const verdict = readCodexReviewVerdict(payload.message);
540
- if (!verdict) continue;
541
- reviews.push({ ...pending, verdict });
542
- pending = null;
543
- }
544
- return { reviews, ...pending ? { pendingOffset: pending.offset } : {} };
545
- };
546
- var codexPlannedShellCommand = (plannedAction) => {
547
- if (plannedAction?.tool !== CODEX_SHELL_TOOL) return null;
548
- const argv = plannedAction.command;
549
- if (!Array.isArray(argv) || argv.length !== 3) return null;
550
- const [shell, flag, command] = argv;
551
- if (typeof shell !== "string" || shell.trim().length === 0) return null;
552
- if (typeof flag !== "string" || !CODEX_SHELL_COMMAND_FLAGS.includes(flag)) return null;
553
- if (typeof command !== "string" || command.trim().length === 0) return null;
554
- const cwd = nonEmptyString(plannedAction.cwd);
555
- return { command, ...cwd ? { cwd } : {} };
556
- };
557
- var codexReviewerDenialsSince = (reviews, since) => {
558
- const sinceMs = Date.parse(since);
559
- if (!Number.isFinite(sinceMs)) return [];
560
- return reviews.flatMap((review) => {
561
- if (review.verdict.outcome !== "deny" || !review.requestedAt) return [];
562
- if (Date.parse(review.requestedAt) < sinceMs) return [];
563
- const shell = codexPlannedShellCommand(review.plannedAction);
564
- if (!shell) return [];
565
- return [{
566
- ...shell,
567
- offset: review.offset,
568
- requestedAt: review.requestedAt,
569
- plannedAction: review.plannedAction,
570
- ...review.verdict.rationale ? { rationale: review.verdict.rationale } : {}
571
- }];
572
- });
573
- };
574
-
575
- export {
576
- HOOK_FALLBACK_MAX,
577
- UNNAMED_ACTION,
578
- describeToolCall,
579
- deriveToolTarget,
580
- parseApplyPatchOps,
581
- parseApplyPatchFiles,
582
- OPENCODE_TOOL_NAMES,
583
- canonicalGateInputs,
584
- isToolResultError,
585
- toolResultErrorText,
586
- deriveReceiptMeta,
587
- scopePathFor,
588
- deriveActionBody,
589
- deriveAction,
590
- deriveBlocker,
591
- AGENT_IDENTITIES,
592
- TEAMMATE_IDLE_ACTION,
593
- summarizeStop,
594
- subagentStopSummary,
595
- compactionAction,
596
- sessionStartAction,
597
- sessionEndAction,
598
- taskTitleFrom,
599
- notificationTypeOf,
600
- notificationAction,
601
- describeStopFailure,
602
- permissionDeniedAction,
603
- sessionIdentityOf,
604
- readCodexTurnContext,
605
- isCodexGuardianRolloutHead,
606
- scanCodexGuardianReviews,
607
- codexReviewerDenialsSince
608
- };