token-goat 2.9.26 → 2.9.29

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 (78) hide show
  1. package/README.md +30 -88
  2. package/SECURITY.md +14 -0
  3. package/dist/{token-goat-chunk-OZRSREKR.mjs → token-goat-chunk-2AWQ2R5X.mjs} +2 -2
  4. package/dist/{token-goat-chunk-BH4ZVRB2.mjs → token-goat-chunk-2NNUA2LR.mjs} +4 -4
  5. package/dist/token-goat-chunk-2WOUYWLM.mjs +955 -0
  6. package/dist/{token-goat-chunk-IKZLSRII.mjs → token-goat-chunk-435UQTKD.mjs} +5 -3
  7. package/dist/{token-goat-chunk-62IHOU6L.mjs → token-goat-chunk-44BM7DDQ.mjs} +5 -3
  8. package/dist/token-goat-chunk-52NSDWYY.mjs +96 -0
  9. package/dist/{token-goat-chunk-CXBOFPF4.mjs → token-goat-chunk-67BNIKKO.mjs} +32 -18
  10. package/dist/{token-goat-chunk-XBESNWEX.mjs → token-goat-chunk-6AK46ZT7.mjs} +2 -3
  11. package/dist/{token-goat-chunk-7SXPJOSZ.mjs → token-goat-chunk-7CPMDFGB.mjs} +166 -40
  12. package/dist/token-goat-chunk-ASLWOQ3I.mjs +14 -0
  13. package/dist/{token-goat-chunk-MWID3YOY.mjs → token-goat-chunk-BGYOZ6DO.mjs} +6 -4
  14. package/dist/{token-goat-chunk-PAV6NNY7.mjs → token-goat-chunk-C5YPNZKQ.mjs} +22 -8
  15. package/dist/{token-goat-chunk-D2X7XN5M.mjs → token-goat-chunk-CIQ2FTY3.mjs} +23 -242
  16. package/dist/token-goat-chunk-D4ETHJD6.mjs +246 -0
  17. package/dist/{token-goat-chunk-7Y6GWTII.mjs → token-goat-chunk-DNRPXHYH.mjs} +25 -17
  18. package/dist/{token-goat-chunk-L43RKNHA.mjs → token-goat-chunk-DNSPXQXQ.mjs} +2 -2
  19. package/dist/token-goat-chunk-DPNLUJWB.mjs +220 -0
  20. package/dist/token-goat-chunk-E4ZOJIZK.mjs +1382 -0
  21. package/dist/token-goat-chunk-E5TK5DNO.mjs +718 -0
  22. package/dist/{token-goat-chunk-VVROQBQJ.mjs → token-goat-chunk-EUSDXRXU.mjs} +62 -12
  23. package/dist/{token-goat-chunk-POOI26O6.mjs → token-goat-chunk-FTP4U4QA.mjs} +12 -8
  24. package/dist/token-goat-chunk-G23DWHV4.mjs +142 -0
  25. package/dist/{token-goat-chunk-MDV5VWF4.mjs → token-goat-chunk-G7IWXCFF.mjs} +223 -15
  26. package/dist/{token-goat-chunk-C2PG4K5D.mjs → token-goat-chunk-GLXRTTLE.mjs} +7 -5
  27. package/dist/{token-goat-chunk-M6CVNHTW.mjs → token-goat-chunk-H5J3C56Q.mjs} +95 -573
  28. package/dist/{token-goat-chunk-BAWKGODL.mjs → token-goat-chunk-HNFZZHFW.mjs} +2 -2
  29. package/dist/{token-goat-chunk-TG5ZPD6B.mjs → token-goat-chunk-HUYUHODZ.mjs} +1036 -759
  30. package/dist/token-goat-chunk-IVE6U7KV.mjs +53 -0
  31. package/dist/{token-goat-chunk-ZZT6PVB3.mjs → token-goat-chunk-IVWI2OCK.mjs} +72 -45
  32. package/dist/{token-goat-chunk-GMOUBOX4.mjs → token-goat-chunk-JTKCQMQA.mjs} +37 -5
  33. package/dist/{token-goat-chunk-H2CPLEMP.mjs → token-goat-chunk-KOPIR3KZ.mjs} +46 -23
  34. package/dist/token-goat-chunk-L7H3U27L.mjs +35 -0
  35. package/dist/{token-goat-chunk-FLEYOCAS.mjs → token-goat-chunk-LZNBQTDK.mjs} +6 -4
  36. package/dist/token-goat-chunk-MELOKFDJ.mjs +199 -0
  37. package/dist/{token-goat-chunk-RH27ZSPF.mjs → token-goat-chunk-NMBWLRHW.mjs} +164 -50
  38. package/dist/{token-goat-chunk-43JFN26X.mjs → token-goat-chunk-O6I2XU4P.mjs} +27 -967
  39. package/dist/{token-goat-chunk-G6EYO5CD.mjs → token-goat-chunk-ONXH6NZ5.mjs} +2 -87
  40. package/dist/token-goat-chunk-ORNFMIER.mjs +295 -0
  41. package/dist/{token-goat-chunk-LHUP4DXZ.mjs → token-goat-chunk-PGTH2U4C.mjs} +1 -1
  42. package/dist/{token-goat-chunk-5BQ7W7D7.mjs → token-goat-chunk-PKRWMHVP.mjs} +76 -59
  43. package/dist/{token-goat-chunk-DA43OE7L.mjs → token-goat-chunk-PQ53BSCF.mjs} +1 -1
  44. package/dist/{token-goat-chunk-STYRLQIW.mjs → token-goat-chunk-PYSUADX4.mjs} +2 -2
  45. package/dist/{token-goat-chunk-JALB3KWJ.mjs → token-goat-chunk-QO3LXVVR.mjs} +4247 -5005
  46. package/dist/{token-goat-chunk-L45IWWMC.mjs → token-goat-chunk-RLP6J7WR.mjs} +6 -6
  47. package/dist/token-goat-chunk-RQGGMPCR.mjs +205 -0
  48. package/dist/{token-goat-chunk-MBDJR6HU.mjs → token-goat-chunk-RTMVLHWX.mjs} +3708 -4003
  49. package/dist/{token-goat-chunk-E3K3BTEQ.mjs → token-goat-chunk-S4VSXURL.mjs} +8 -6
  50. package/dist/{token-goat-chunk-LKSXAMJB.mjs → token-goat-chunk-SE6E4BKJ.mjs} +1 -1
  51. package/dist/token-goat-chunk-TCQANXYC.mjs +351 -0
  52. package/dist/token-goat-chunk-TDUT2CL6.mjs +564 -0
  53. package/dist/{token-goat-chunk-LRIZ7K3F.mjs → token-goat-chunk-TGOXNAHW.mjs} +54 -111
  54. package/dist/{token-goat-chunk-WNBSCSAI.mjs → token-goat-chunk-UA5BKWWQ.mjs} +1 -1
  55. package/dist/{token-goat-chunk-RIB6XY2V.mjs → token-goat-chunk-UXSXQQIY.mjs} +1 -1
  56. package/dist/{token-goat-chunk-7EPYB34H.mjs → token-goat-chunk-VMFWNHGR.mjs} +84 -73
  57. package/dist/token-goat-chunk-VQ6F6T7X.mjs +36 -0
  58. package/dist/{token-goat-chunk-5M3NRJD2.mjs → token-goat-chunk-VZQEWRVH.mjs} +6 -4
  59. package/dist/{token-goat-chunk-OSUFN2FV.mjs → token-goat-chunk-XB5KKOCG.mjs} +5 -25
  60. package/dist/{token-goat-chunk-6E2IDLHF.mjs → token-goat-chunk-XYWPOSCN.mjs} +57 -615
  61. package/dist/{token-goat-chunk-CQHY4SL7.mjs → token-goat-chunk-YAGLESS3.mjs} +8 -5
  62. package/dist/token-goat-chunk-YKSRAWUU.mjs +33 -0
  63. package/dist/{token-goat-chunk-F2ARFHLJ.mjs → token-goat-chunk-YOOVFPTP.mjs} +2 -2
  64. package/dist/{token-goat-chunk-EEIDFMEM.mjs → token-goat-chunk-YZAAZU2S.mjs} +12 -3
  65. package/dist/token-goat-chunk-Z6KBMOBI.mjs +53 -0
  66. package/dist/{token-goat-chunk-4GEDH2WU.mjs → token-goat-chunk-ZER6EAXZ.mjs} +81 -70
  67. package/dist/token-goat-hook-client.cjs +475 -0
  68. package/dist/token-goat-hook-client.mjs +26 -0
  69. package/dist/token-goat-hook.mjs +31 -21
  70. package/dist/token-goat.core.mjs +9 -39
  71. package/docs/C4_RUNTIME_ARCHITECTURE.md +2 -2
  72. package/docs/cli.md +35 -23
  73. package/docs/install.md +18 -14
  74. package/package.json +1 -1
  75. package/dist/token-goat-chunk-ADMR3QVF.mjs +0 -41
  76. package/dist/token-goat-chunk-E6B2UFDF.mjs +0 -30
  77. package/dist/token-goat-chunk-GKZ2US5B.mjs +0 -187
  78. package/dist/token-goat-chunk-MZTO4FXH.mjs +0 -26
@@ -0,0 +1,718 @@
1
+ import { createRequire as __cjsRequire } from 'node:module';
2
+ const require = __cjsRequire(import.meta.url);
3
+ import {
4
+ COPILOT_CLI_TOOL_NAME_MAP,
5
+ foldToolName,
6
+ readCopilotMcpTools
7
+ } from "./token-goat-chunk-EUSDXRXU.mjs";
8
+ import {
9
+ countRedactionPlaceholders,
10
+ recordStat,
11
+ savedTokensFromBytes
12
+ } from "./token-goat-chunk-CIQ2FTY3.mjs";
13
+ import {
14
+ detectHarness,
15
+ loadConfig,
16
+ ownGet
17
+ } from "./token-goat-chunk-TGOXNAHW.mjs";
18
+ import {
19
+ registerReset
20
+ } from "./token-goat-chunk-YZAAZU2S.mjs";
21
+ import {
22
+ escapesOntoNetworkThroughLinks,
23
+ foldPathForContainment,
24
+ isInsideRoot
25
+ } from "./token-goat-chunk-TCQANXYC.mjs";
26
+ import {
27
+ displaySafeText,
28
+ isUncOrDevicePath,
29
+ neutralizeOutsideFences
30
+ } from "./token-goat-chunk-NMTKNYGF.mjs";
31
+ import {
32
+ init_define_import_meta_env
33
+ } from "./token-goat-chunk-A37V4PBF.mjs";
34
+
35
+ // src/hooks_cli.ts
36
+ init_define_import_meta_env();
37
+
38
+ // src/copilot_mcp_names.ts
39
+ init_define_import_meta_env();
40
+ var _cached = null;
41
+ function copilotMcpToolNameMap() {
42
+ if (_cached) return _cached;
43
+ const map = /* @__PURE__ */ new Map();
44
+ try {
45
+ for (const server of readCopilotMcpTools().servers) {
46
+ for (const toolName of server.toolNames) {
47
+ map.set(`${server.serverName}-${toolName}`, `mcp__${server.serverName}__${toolName}`);
48
+ }
49
+ }
50
+ } catch {
51
+ }
52
+ _cached = map;
53
+ return map;
54
+ }
55
+ function canonicalizeCopilotMcpToolName(toolName) {
56
+ return copilotMcpToolNameMap().get(toolName) ?? toolName;
57
+ }
58
+ function resetCopilotMcpToolNameCache() {
59
+ _cached = null;
60
+ }
61
+ registerReset(resetCopilotMcpToolNameCache, { perRequest: true });
62
+
63
+ // src/hooks_cli.ts
64
+ var _LOG = {
65
+ warn: (msg, ...args) => console.warn(`[hooks_cli] ${msg}`, ...args),
66
+ debug: (msg, ...args) => console.error(`[hooks_cli] ${msg}`, ...args),
67
+ error: (msg, err, ...args) => console.error(`[hooks_cli] ${msg}`, ...err ? [err] : [], ...args)
68
+ };
69
+ var CANONICAL_TG_TOOLS = [
70
+ "Bash",
71
+ "BashOutput",
72
+ "Read",
73
+ "Write",
74
+ "Edit",
75
+ "WebFetch",
76
+ "WebSearch",
77
+ "Grep",
78
+ "Glob",
79
+ "Skill",
80
+ "NotebookEdit",
81
+ "ExitPlanMode"
82
+ ];
83
+ function normalizeToolNameWithMap(toolName, nameMap) {
84
+ if (!toolName || typeof toolName !== "string") return toolName;
85
+ const stripped = toolName.includes(":") ? toolName.split(":").pop() : toolName;
86
+ if (nameMap) {
87
+ const exact = ownGet(nameMap, toolName) ?? ownGet(nameMap, stripped);
88
+ if (exact !== void 0) return exact;
89
+ }
90
+ const lower = toolName.toLowerCase();
91
+ const lowerStripped = stripped.toLowerCase();
92
+ const folded = foldToolName(toolName);
93
+ const foldedStripped = foldToolName(stripped);
94
+ if (nameMap) {
95
+ for (const [k, v] of Object.entries(nameMap)) {
96
+ const kLower = k.toLowerCase();
97
+ const kFolded = foldToolName(k);
98
+ if (kLower === lower || kLower === lowerStripped || kFolded === folded || kFolded === foldedStripped) {
99
+ return v;
100
+ }
101
+ }
102
+ for (const v of Object.values(nameMap)) {
103
+ const vLower = v.toLowerCase();
104
+ const vFolded = foldToolName(v);
105
+ if (vLower === lower || vLower === lowerStripped || vFolded === folded || vFolded === foldedStripped) {
106
+ return v;
107
+ }
108
+ }
109
+ }
110
+ for (const canonical of CANONICAL_TG_TOOLS) {
111
+ const cLower = canonical.toLowerCase();
112
+ const cFolded = foldToolName(canonical);
113
+ if (cLower === lower || cLower === lowerStripped || cFolded === folded || cFolded === foldedStripped) {
114
+ return canonical;
115
+ }
116
+ }
117
+ return toolName;
118
+ }
119
+ var COPILOT_CLI_INPUT_KEY_MAP = {
120
+ Read: { path: "file_path" },
121
+ Write: { path: "file_path" },
122
+ Edit: { path: "file_path", old_str: "old_string", new_str: "new_string" },
123
+ BashOutput: { shellId: "bash_id" }
124
+ };
125
+ var CODEX_TOOL_NAME_MAP = {
126
+ bash: "Bash",
127
+ // CAPTURE, codex-cli 0.155.0, 2026-09-22: a shell call reaches the hook wire already named 'Bash', and its internal spelling is 'exec'. 'shell' and 'bash' are kept for an older Codex or a fork; neither has been seen on a real payload, both came from this repo's own guidance text, and that text was wrong. See CODEX_MATCHERS in bridges/codex_install.ts.
128
+ exec: "Bash",
129
+ shell: "Bash",
130
+ edit_file: "Edit",
131
+ edit: "Edit",
132
+ apply_patch: "Edit",
133
+ write_file: "Write",
134
+ search_files: "Grep",
135
+ grep: "Grep",
136
+ list_files: "Glob",
137
+ glob: "Glob",
138
+ web_search: "WebFetch",
139
+ // Codex's only wired image-viewing tool (CODEX_MATCHERS in codex_install.ts wires it alongside the shell spellings, in one matcher that covers image reads and shell execution together, mirroring Claude Code's Read handling) -- without this entry, tool_name stayed the literal 'view_image', which matches neither preReadHandler's nor image_shrink.ts's preReadImageHandler's toolName: 'Read' filter, so every Codex image view silently skipped image shrinking and read-session tracking entirely.
140
+ view_image: "Read"
141
+ };
142
+ var GEMINI_TOOL_NAME_MAP = {
143
+ run_shell_command: "Bash",
144
+ read_file: "Read",
145
+ read_many_files: "Read",
146
+ // Deliberately 'Read', not 'Glob', even though list_directory enumerates a directory the way a Glob call does: Grok's equivalent list_dir maps to 'Glob' instead, but Grok's GROK_INPUT_KEY_MAP never remaps a 'pattern' key for it either, so on Grok this call is inert (preGlobHandler/globSignature require a non-empty toolInput['pattern'], which a plain directory listing never sends). Gemini's Read mapping, by contrast, is confirmed live: preReadHandler's node_modules deny fires on a list_directory call targeting node_modules (dogfooded in commit f1fc02de), which Glob's handler has no equivalent check for. Read was the considered choice, not an oversight; kept even though the two harnesses disagree.
147
+ list_directory: "Read",
148
+ write_file: "Write",
149
+ replace: "Edit",
150
+ glob: "Glob",
151
+ grep_search: "Grep",
152
+ // 'search_file_content' was GREP_TOOL_NAME's value before gemini-cli renamed it to 'grep_search' (confirmed against gemini-cli's current base-declarations.ts, which now defines GREP_TOOL_NAME = 'grep_search'; some of gemini-cli's own docs pages still lag the rename). Kept as a backward-compat entry for older installed gemini-cli versions that still emit the pre-rename name -- not dead/hallucinated, just legacy.
153
+ search_file_content: "Grep",
154
+ // Gemini's real web-search tool is registered as 'google_web_search' (WEB_SEARCH_TOOL_NAME in gemini-cli's tool-names.ts) -- 'web_search' is not a tool name Gemini CLI ever emits, so the old entry here silently never matched a real invocation.
155
+ // Previously mapped to 'WebFetch' as a workaround for GEMINI_PRE_TOOLS/POST_TOOLS (gemini_install.ts) excluding 'WebSearch' -- that made the hook fire under the installer's matcher, but hooks_fetch.ts's preFetchHandler/postFetchHandler require a `url` tool-input key, which a search call never sends (it sends `query`), so every google_web_search call silently no-op'd through WebFetch's handler: no URL policy check ever applied to it (it has no URL to check) and no search-result dedup ever ran. Now mapped to the dedicated 'WebSearch' handler (hooks_websearch.ts), which reads `query` directly; the installer gate above was extended to include 'WebSearch' so the hook stays wired.
156
+ google_web_search: "WebSearch",
157
+ web_fetch: "WebFetch"
158
+ };
159
+ var GEMINI_INPUT_KEY_MAP = {
160
+ // Gemini's real write_file/replace tool schemas already use 'file_path' (and replace's 'old_string'/'new_string') verbatim -- identical to token-goat's own canonical keys (see getFilePath() in hooks_common.ts, which reads event.toolInput['file_path']) -- confirmed against gemini-cli's own EditToolParams/WriteFileToolParams interfaces. No remap is needed for Write/Edit; the previous entries here actively renamed a key that was already correct (file_path -> path), which would have silently corrupted the path argument on every real Gemini Write/Edit call.
161
+ // 'Read' covers THREE distinct raw Gemini tools (see GEMINI_TOOL_NAME_MAP above), and they do NOT share one schema: read_file already uses 'file_path' verbatim, no remap needed; list_directory uses 'dir_path' (confirmed against gemini-cli's LSToolParams) -- remapped to 'file_path' below, exactly mirroring Grep's existing dir_path->path fix; read_many_files uses 'include' (confirmed against gemini-cli's ReadManyFilesParams), an ARRAY of glob patterns, not a single file path -- there is no single string to remap it to, so it is deliberately left unmapped. getFilePath() (hooks_common.ts) will return undefined for this call, and preReadHandler/postReadHandler already fall back to passOutput() on an undefined path (see preReadHandler's `if (filePath === undefined) return passOutput()`), so a real read_many_files call still succeeds normally; it just doesn't get session-dedup tracking or read-count hints. Accepted limitation, not a bug to force-fit.
162
+ Read: { dir_path: "file_path" },
163
+ // Grep's real tool (grep_search) calls its target-directory argument 'dir_path', which preReadHandler's Grep fallback (event.toolInput['path']) doesn't recognize, so that one remains remapped.
164
+ Grep: { dir_path: "path" }
165
+ };
166
+ var GROK_TOOL_NAME_MAP = {
167
+ read_file: "Read",
168
+ write: "Write",
169
+ search_replace: "Edit",
170
+ run_terminal_command: "Bash",
171
+ // The grok 0.2.93 binary registers its shell tool as `tool.run_terminal_cmd` (tracing-id table in ~/.grok/bin/agent.exe) and its newer embedded hooks doc's PreToolUse example sends `"toolName": "run_terminal_cmd"`, while the older embedded doc revision and a 2026-07-09 live capture on the same version both show `run_terminal_command`. Which spelling arrives is profile/version dependent, so BOTH are mapped: an unmatched extra entry is a harmless no-op, a missing one silently kills every Bash hook (compression, wrap, hints) on that profile.
172
+ run_terminal_cmd: "Bash",
173
+ grep: "Grep",
174
+ list_dir: "Glob",
175
+ // `glob` is a distinct registered grok tool (tool.glob in the 0.2.93 binary's tracing-id table), separate from list_dir; unmapped it arrived as lowercase 'glob' and matched no handler.
176
+ glob: "Glob",
177
+ // web_fetch/web_search were entirely absent from this map, so grok's URL fetches bypassed token-goat's WebFetch pipeline (URL-policy deny included) and its searches bypassed the WebSearch dedup/compression. Verified against the 0.2.93 binary: tool.web_fetch / tool.web_search registered ids, grok_build WebFetchInput's field described as "The URL to fetch content from" (i.e. `url`), grok_build WebSearchInput's fields query/citations/allowed_domains -- all matching the keys hooks_fetch.ts / hooks_websearch.ts already read, so a name map alone revives both.
178
+ web_fetch: "WebFetch",
179
+ web_search: "WebSearch",
180
+ // The Hashline prompt profile registers hashline_read/hashline_edit/hashline_grep (tool.hashline_* in the binary; grok's own hook alias table pairs them with Read/Edit/Grep). Their input key shapes were not individually verified: if one differs, the mapped handler degrades to the same no-op as an unmapped name, never a wrong rewrite. The *_concise twins are the GrokBuildConcise profile's registrations of the same three core tools.
181
+ hashline_read: "Read",
182
+ hashline_edit: "Edit",
183
+ hashline_grep: "Grep",
184
+ read_file_concise: "Read",
185
+ search_replace_concise: "Edit",
186
+ run_terminal_cmd_concise: "Bash"
187
+ };
188
+ var GROK_INPUT_KEY_MAP = {
189
+ Read: { target_file: "file_path" }
190
+ };
191
+ var KIMI_TOOL_NAME_MAP = {
192
+ FetchURL: "WebFetch",
193
+ ReadMediaFile: "Read"
194
+ };
195
+ var KIMI_INPUT_KEY_MAP = {
196
+ // line_offset/n_lines are Kimi's Read paging arguments (ReadInputSchema in MoonshotAI/kimi-code packages/agent-core-v2/src/agent/tools/os/read/read.ts: `path`, `line_offset` "the line number to start reading from", `n_lines` "the number of lines to read"). Unmapped, every ranged Kimi read looked unbounded to hooks_read.ts's estimateRequestedSlice and was gated on the whole file's size -- a small slice of a big file could draw the large-file deny meant for full reads. line_offset's 1-indexed positive form matches Read's own offset semantics exactly; its negative tail-read form has no token-goat equivalent and is clamped to 1 by estimateRequestedSlice's `offset >= 1` guard, degrading to a window of the right SIZE (which is all the gate consumes). ReadMediaFile shares this map via its Read rename and carries only `path`, so the extra entries never touch it.
197
+ Read: { path: "file_path", line_offset: "offset", n_lines: "limit" },
198
+ Write: { path: "file_path" },
199
+ Edit: { path: "file_path" }
200
+ };
201
+ var QWEN_TOOL_NAME_MAP = {
202
+ read_file: "Read",
203
+ write_file: "Write",
204
+ edit: "Edit",
205
+ replace: "Edit",
206
+ notebook_edit: "NotebookEdit",
207
+ run_shell_command: "Bash",
208
+ grep_search: "Grep",
209
+ search_file_content: "Grep",
210
+ glob: "Glob",
211
+ web_fetch: "WebFetch",
212
+ web_search: "WebSearch",
213
+ // Same deliberate 'Read' choice as Gemini's list_directory entry above (see its comment): node_modules deny fires, Grok's rival 'Glob' mapping is inert for the equivalent tool anyway.
214
+ list_directory: "Read"
215
+ };
216
+ var QWEN_INPUT_KEY_MAP = {
217
+ Read: { path: "file_path" }
218
+ };
219
+ var VSCODE_TOOL_NAME_MAP = {
220
+ read_file: "Read",
221
+ view_image: "Read",
222
+ list_dir: "Read",
223
+ grep_search: "Grep",
224
+ file_search: "Glob",
225
+ create_file: "Write",
226
+ replace_string_in_file: "Edit",
227
+ insert_edit_into_file: "Edit",
228
+ edit_notebook_file: "NotebookEdit",
229
+ run_in_terminal: "Bash"
230
+ };
231
+ var VSCODE_INPUT_KEY_MAP = {
232
+ read_file: { filePath: "file_path" },
233
+ view_image: { filePath: "file_path" },
234
+ list_dir: { path: "file_path" },
235
+ grep_search: { query: "pattern", includePattern: "glob" },
236
+ file_search: { query: "pattern" },
237
+ create_file: { filePath: "file_path" },
238
+ replace_string_in_file: { filePath: "file_path", oldString: "old_string", newString: "new_string" },
239
+ insert_edit_into_file: { filePath: "file_path" },
240
+ edit_notebook_file: { filePath: "notebook_path" },
241
+ run_in_terminal: {}
242
+ };
243
+ var VSCODE_TOOL_NAME_KEY = "_tg_vscode_tool_name";
244
+ function vscodeNativeToolInput(vscodeToolName, canonicalInput) {
245
+ const inverse = {};
246
+ for (const [nativeKey, canonicalKey] of Object.entries(ownGet(VSCODE_INPUT_KEY_MAP, vscodeToolName) ?? {})) {
247
+ inverse[canonicalKey] = nativeKey;
248
+ }
249
+ return remapInputKeys(canonicalInput, inverse);
250
+ }
251
+ function remapInputKeys(input, keyMap) {
252
+ const newInput = {};
253
+ for (const [k, v] of Object.entries(input)) {
254
+ newInput[ownGet(keyMap, k) || k] = v;
255
+ }
256
+ return newInput;
257
+ }
258
+ function remapToolName(obj, toolName, nameMap, inputKeyMap) {
259
+ const mapped = normalizeToolNameWithMap(toolName, nameMap);
260
+ const result = { ...obj };
261
+ result["tool_name"] = mapped;
262
+ const rawInput = obj["tool_input"];
263
+ const keyMap = ownGet(inputKeyMap, mapped) ?? ownGet(inputKeyMap, toolName);
264
+ if (keyMap && typeof rawInput === "object" && rawInput !== null && !Array.isArray(rawInput)) {
265
+ result["tool_input"] = remapInputKeys(rawInput, keyMap);
266
+ }
267
+ return result;
268
+ }
269
+ function grokToCanonicalWire(obj) {
270
+ const wire = { ...obj };
271
+ if (typeof wire["toolName"] === "string" && wire["tool_name"] === void 0) {
272
+ wire["tool_name"] = wire["toolName"];
273
+ }
274
+ if (typeof wire["toolInput"] === "object" && wire["toolInput"] !== null && !Array.isArray(wire["toolInput"]) && wire["tool_input"] === void 0) {
275
+ wire["tool_input"] = wire["toolInput"];
276
+ }
277
+ if (typeof wire["sessionId"] === "string" && wire["session_id"] === void 0) {
278
+ wire["session_id"] = wire["sessionId"];
279
+ }
280
+ const toolResult = wire["toolResult"];
281
+ if (toolResult !== null && typeof toolResult === "object" && !Array.isArray(toolResult)) {
282
+ const tr = toolResult;
283
+ if (typeof tr["output_for_prompt"] === "string") {
284
+ wire["tool_response"] = { content: tr["output_for_prompt"], exit_code: tr["exit_code"] };
285
+ } else {
286
+ const firstString = Object.entries(tr).find(([k, v]) => k !== "type" && typeof v === "string");
287
+ if (firstString) {
288
+ wire["tool_response"] = { content: firstString[1] };
289
+ }
290
+ }
291
+ }
292
+ return wire;
293
+ }
294
+ function aliasLlmContentToOutput(result) {
295
+ const toolResponse = result["tool_response"];
296
+ if (toolResponse === null || typeof toolResponse !== "object" || Array.isArray(toolResponse)) return;
297
+ const responseRecord = toolResponse;
298
+ if (typeof responseRecord["llmContent"] === "string" && responseRecord["output"] === void 0) {
299
+ result["tool_response"] = { ...responseRecord, output: responseRecord["llmContent"] };
300
+ }
301
+ }
302
+ function normalizePayload(payload, harness = "claude") {
303
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
304
+ _LOG.warn("normalizePayload: payload is not a dict; received %s", typeof payload);
305
+ return {};
306
+ }
307
+ const rawObj = payload;
308
+ const obj = harness === "grok" ? grokToCanonicalWire(rawObj) : rawObj;
309
+ const toolName = obj["tool_name"];
310
+ if (typeof toolName !== "string" || !toolName.trim()) {
311
+ _LOG.debug("normalizePayload: tool_name missing or invalid; received %s", toolName);
312
+ return {};
313
+ }
314
+ if (harness === "grok") {
315
+ const result2 = remapToolName(obj, toolName, GROK_TOOL_NAME_MAP, GROK_INPUT_KEY_MAP);
316
+ result2["_tg_harness"] = harness;
317
+ return result2;
318
+ }
319
+ if (harness === "codex") {
320
+ const mapped = normalizeToolNameWithMap(toolName, CODEX_TOOL_NAME_MAP);
321
+ const result2 = { ...obj };
322
+ if (mapped) {
323
+ result2["tool_name"] = mapped;
324
+ }
325
+ result2["_tg_harness"] = harness;
326
+ return result2;
327
+ }
328
+ if (harness === "copilot_cli") {
329
+ const result2 = remapToolName(obj, toolName, COPILOT_CLI_TOOL_NAME_MAP, COPILOT_CLI_INPUT_KEY_MAP);
330
+ result2["tool_name"] = canonicalizeCopilotMcpToolName(result2["tool_name"]);
331
+ result2["_tg_harness"] = harness;
332
+ return result2;
333
+ }
334
+ if (harness === "vscode") {
335
+ const mapped = normalizeToolNameWithMap(toolName, VSCODE_TOOL_NAME_MAP);
336
+ const result2 = { ...obj };
337
+ result2["tool_name"] = mapped;
338
+ const keyMap = ownGet(VSCODE_INPUT_KEY_MAP, toolName) ?? ownGet(VSCODE_INPUT_KEY_MAP, mapped);
339
+ const rawInput = obj["tool_input"];
340
+ if (keyMap && typeof rawInput === "object" && rawInput !== null && !Array.isArray(rawInput)) {
341
+ result2["tool_input"] = remapInputKeys(rawInput, keyMap);
342
+ }
343
+ result2[VSCODE_TOOL_NAME_KEY] = toolName;
344
+ result2["_tg_harness"] = harness;
345
+ return result2;
346
+ }
347
+ if (harness === "kimi") {
348
+ const result2 = remapToolName(obj, toolName, KIMI_TOOL_NAME_MAP, KIMI_INPUT_KEY_MAP);
349
+ result2["_tg_harness"] = harness;
350
+ return result2;
351
+ }
352
+ if (harness === "qwen") {
353
+ const result2 = remapToolName(obj, toolName, QWEN_TOOL_NAME_MAP, QWEN_INPUT_KEY_MAP);
354
+ aliasLlmContentToOutput(result2);
355
+ result2["_tg_harness"] = harness;
356
+ return result2;
357
+ }
358
+ if (harness === "gemini") {
359
+ const result2 = remapToolName(obj, toolName, GEMINI_TOOL_NAME_MAP, GEMINI_INPUT_KEY_MAP);
360
+ if ("functionCallId" in result2 && !("toolUseId" in result2)) {
361
+ result2["toolUseId"] = result2["functionCallId"];
362
+ delete result2["functionCallId"];
363
+ }
364
+ aliasLlmContentToOutput(result2);
365
+ result2["_tg_harness"] = harness;
366
+ return result2;
367
+ }
368
+ const result = { ...obj };
369
+ const normalizedCanonical = normalizeToolNameWithMap(toolName);
370
+ if (normalizedCanonical !== toolName) {
371
+ result["tool_name"] = normalizedCanonical;
372
+ }
373
+ result["_tg_harness"] = harness;
374
+ return result;
375
+ }
376
+
377
+ // src/hint_suggestion_guard.ts
378
+ init_define_import_meta_env();
379
+ function looksLikeSuggestion(slice) {
380
+ return slice.includes('"');
381
+ }
382
+ var SAFE_OUTSIDE_QUOTES = /^[A-Za-z0-9 \t_./=:,@+-]*$/;
383
+ var CONTROL_OR_BIDI = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F\u200E\u200F\u202A-\u202E\u2066-\u2069\u061C\u200B-\u200D\u2028\u2029\uFE00-\uFE0F]|[\u{E0000}-\u{E007F}]/u;
384
+ function suggestionIsUnsafe(slice) {
385
+ if (slice.includes("$") || slice.includes("\n") || slice.includes("\r") || slice.includes("`")) return true;
386
+ if (CONTROL_OR_BIDI.test(slice)) return true;
387
+ const regions = slice.split('"');
388
+ if (regions.length % 2 === 0) return true;
389
+ for (let i = 2; i < regions.length; i += 2) {
390
+ if (!SAFE_OUTSIDE_QUOTES.test(regions[i] ?? "")) return true;
391
+ }
392
+ return false;
393
+ }
394
+ var OMITTED = "token-goat (command omitted: the path contains shell metacharacters)";
395
+ function stripUnsafeSuggestions(text) {
396
+ if (!text.includes("token-goat ")) return text;
397
+ let out = "";
398
+ let at = 0;
399
+ for (; ; ) {
400
+ const start = text.indexOf("token-goat ", at);
401
+ if (start === -1) return out + text.slice(at);
402
+ const lineBreak = text.slice(start).search(/[\r\n]/);
403
+ const line = lineBreak === -1 ? text.slice(start) : text.slice(start, start + lineBreak);
404
+ const firstTick = line.indexOf("`");
405
+ const narrow = firstTick === -1 ? line : line.slice(0, firstTick);
406
+ out += text.slice(at, start);
407
+ if (!looksLikeSuggestion(narrow) || !suggestionIsUnsafe(narrow)) {
408
+ out += narrow;
409
+ at = start + narrow.length;
410
+ continue;
411
+ }
412
+ const lastTick = line.lastIndexOf("`");
413
+ const wide = lastTick === -1 ? line : line.slice(0, lastTick);
414
+ out += OMITTED;
415
+ at = start + wide.length;
416
+ }
417
+ }
418
+ function leadWithCommand(command, purpose = "", reason = "") {
419
+ const tail = reason.trim();
420
+ return "Run `" + command + "`" + (purpose === "" ? "" : " " + purpose) + "." + (tail === "" ? "" : (tail.includes("`") ? "\n" : " ") + tail);
421
+ }
422
+
423
+ // src/hooks_common.ts
424
+ init_define_import_meta_env();
425
+ function getToolName(event) {
426
+ return event.toolName;
427
+ }
428
+ function getToolInput(event) {
429
+ return event.toolInput;
430
+ }
431
+ function getFilePath(event) {
432
+ const value = event.toolInput["file_path"];
433
+ if (typeof value === "string" && value !== "") return value;
434
+ const notebookValue = event.toolInput["notebook_path"];
435
+ return typeof notebookValue === "string" && notebookValue !== "" ? notebookValue : void 0;
436
+ }
437
+ function getCwd(event) {
438
+ const value = event.raw && typeof event.raw === "object" ? event.raw["cwd"] : void 0;
439
+ return typeof value === "string" && value !== "" ? value : void 0;
440
+ }
441
+ function getTranscriptPath(event) {
442
+ const value = event.raw && typeof event.raw === "object" ? event.raw["transcript_path"] : void 0;
443
+ return typeof value === "string" && value !== "" ? value : void 0;
444
+ }
445
+ function extractToolResponseField(raw, keys) {
446
+ const resp = raw["tool_response"];
447
+ if (typeof resp === "string") return resp;
448
+ if (resp !== null && typeof resp === "object") {
449
+ const r = resp;
450
+ const path2 = resolveToolResponseFieldPath(r, keys);
451
+ if (path2 !== null) return readAtPath(r, path2);
452
+ }
453
+ return "";
454
+ }
455
+ var NESTED_TOOL_RESPONSE_PATHS = [["file", "content"]];
456
+ function readAtPath(resp, path2) {
457
+ let cur = resp;
458
+ for (const seg of path2) {
459
+ if (cur === null || typeof cur !== "object") return "";
460
+ cur = cur[seg];
461
+ }
462
+ return typeof cur === "string" ? cur : "";
463
+ }
464
+ function resolveToolResponseFieldPath(resp, keys) {
465
+ for (const key of keys) {
466
+ if (typeof resp[key] === "string" && resp[key] !== "") return [key];
467
+ }
468
+ for (const path2 of NESTED_TOOL_RESPONSE_PATHS) {
469
+ if (readAtPath(resp, path2) !== "") return path2;
470
+ }
471
+ return null;
472
+ }
473
+ function replaceToolResponseField(resp, keys, newText) {
474
+ const path2 = resolveToolResponseFieldPath(resp, keys);
475
+ if (path2 === null) return null;
476
+ const root = { ...resp };
477
+ let cur = root;
478
+ for (let i = 0; i < path2.length - 1; i++) {
479
+ const seg = path2[i];
480
+ const child = cur[seg];
481
+ if (child === null || typeof child !== "object") return null;
482
+ const cloned = { ...child };
483
+ cur[seg] = cloned;
484
+ cur = cloned;
485
+ }
486
+ cur[path2[path2.length - 1]] = newText;
487
+ return root;
488
+ }
489
+ var OUTPUT_FIRST_TOOL_RESPONSE_KEYS = ["output", "content", "text", "body", "stdout", "stderr"];
490
+ var BODY_FIRST_TOOL_RESPONSE_KEYS = ["output", "body", "text", "content", "result"];
491
+ function isMcpErrorResponse(raw) {
492
+ const tr = raw["tool_response"];
493
+ if (!tr || typeof tr !== "object") return false;
494
+ return tr["isError"] === true;
495
+ }
496
+ var MCP_CONTENT_BLOCK_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "resource", "resource_link"]);
497
+ function blocksToText(blocks) {
498
+ if (!Array.isArray(blocks)) return null;
499
+ const parts = [];
500
+ let recognised = false;
501
+ for (const block of blocks) {
502
+ if (!block || typeof block !== "object") continue;
503
+ const rec = block;
504
+ const type = rec["type"];
505
+ if (typeof type === "string" && MCP_CONTENT_BLOCK_TYPES.has(type)) recognised = true;
506
+ if (typeof rec["text"] === "string") {
507
+ parts.push(rec["text"]);
508
+ continue;
509
+ }
510
+ const resource = rec["resource"];
511
+ if (resource !== null && typeof resource === "object") {
512
+ const nested = resource["text"];
513
+ if (typeof nested === "string") parts.push(nested);
514
+ continue;
515
+ }
516
+ if (rec["type"] === "resource_link") {
517
+ const link = ["name", "uri", "description"].map((k) => rec[k]).filter((v) => typeof v === "string" && v !== "");
518
+ if (link.length > 0) parts.push(link.join(" "));
519
+ }
520
+ }
521
+ if (parts.length > 0) return parts.join("\n");
522
+ return recognised ? "" : null;
523
+ }
524
+ function extractToolResultText(raw) {
525
+ const tr = raw["tool_response"];
526
+ if (typeof tr === "string") return tr;
527
+ if (!tr || typeof tr !== "object") return "";
528
+ const bare = blocksToText(tr);
529
+ if (bare !== null) return bare;
530
+ const resp = tr;
531
+ const content = resp["content"];
532
+ const joined = blocksToText(content);
533
+ if (joined !== null) return joined;
534
+ for (const key of ["output", "text", "body"]) {
535
+ if (typeof resp[key] === "string") return resp[key];
536
+ }
537
+ if (typeof content === "string") return content;
538
+ try {
539
+ return JSON.stringify(resp);
540
+ } catch {
541
+ return "";
542
+ }
543
+ }
544
+ function asContentBlocks(value) {
545
+ if (!Array.isArray(value)) return null;
546
+ const blocks = value.filter((b) => b !== null && typeof b === "object");
547
+ const typed = blocks.some((b) => typeof b["type"] === "string" && MCP_CONTENT_BLOCK_TYPES.has(b["type"]));
548
+ return typed ? blocks : null;
549
+ }
550
+ function mcpContentBlocks(raw) {
551
+ const tr = raw["tool_response"];
552
+ if (tr === null || typeof tr !== "object") return null;
553
+ return asContentBlocks(tr) ?? asContentBlocks(tr["content"]);
554
+ }
555
+ function isTextualContentBlock(block) {
556
+ if (block === null || typeof block !== "object") return false;
557
+ const rec = block;
558
+ if (typeof rec["text"] === "string") return true;
559
+ if (rec["type"] === "resource_link") return true;
560
+ const resource = rec["resource"];
561
+ return resource !== null && typeof resource === "object" && typeof resource["text"] === "string";
562
+ }
563
+ function withPreservedBlocks(output, blocks) {
564
+ if (output.hookType !== "rewriteOutput" || blocks.length === 0) return output;
565
+ return { ...output, updatedBlocks: [{ type: "text", text: output.updatedOutput }, ...blocks] };
566
+ }
567
+ function passOutput() {
568
+ return { hookType: "pass" };
569
+ }
570
+ function denyOutput(message) {
571
+ return { hookType: "deny", message: `[tg] ${neutralizeOutsideFences(message)}` };
572
+ }
573
+ function contextOutput(context, correlators) {
574
+ return correlators === void 0 ? { hookType: "context", context } : { hookType: "context", context, correlators };
575
+ }
576
+ function emitRewrite(updatedOutput, detail, savings, redaction = "count-here") {
577
+ if (detectHarness() === "vscode") return passOutput();
578
+ if (redaction === "count-here") {
579
+ const count = countRedactionPlaceholders(updatedOutput);
580
+ if (count > 0) recordStat("secret_redacted", 0, count, void 0, detail);
581
+ }
582
+ if (savings !== void 0) {
583
+ const bytesSaved = savings.originalBytes - Buffer.byteLength(updatedOutput, "utf-8");
584
+ const credited = Math.max(0, bytesSaved);
585
+ recordStat(savings.kind, credited, savedTokensFromBytes(credited), void 0, savings.detail);
586
+ }
587
+ return { hookType: "rewriteOutput", updatedOutput };
588
+ }
589
+ function emitRewriteIfChanged(original, emitted, detail) {
590
+ if (emitted === original) return passOutput();
591
+ return emitRewrite(emitted, detail);
592
+ }
593
+ function countNonEmptyLines(text) {
594
+ return text.split(/\r\n|\r|\n/).filter((line) => line.length > 0).length;
595
+ }
596
+ function estimateResultCount(text) {
597
+ const firstLine = text.split(/\r\n|\r|\n/).find((line) => line.trim().length > 0);
598
+ if (firstLine !== void 0) {
599
+ const trimmed = firstLine.trim();
600
+ if (/^No (?:files|matches) found\.?$/i.test(trimmed)) return 0;
601
+ const found = /^Found (\d+) (?:files?|matches?)\.?$/i.exec(trimmed);
602
+ if (found) return Number.parseInt(found[1], 10);
603
+ }
604
+ return countNonEmptyLines(text);
605
+ }
606
+ var RESULT_COUNT_KEYS = ["numFiles", "numLines", "numMatches"];
607
+ function structuredResultCount(raw) {
608
+ const response = raw["tool_response"];
609
+ if (response === null || typeof response !== "object") return null;
610
+ const counts = RESULT_COUNT_KEYS.map((k) => response[k]).filter((v) => typeof v === "number");
611
+ return counts.length > 0 ? Math.max(...counts) : null;
612
+ }
613
+ function makeDedupHintHandlers(opts) {
614
+ const post = (event) => {
615
+ try {
616
+ if (getToolName(event) !== opts.toolName) return passOutput();
617
+ const signature = opts.buildSignature(getToolInput(event));
618
+ if (signature === null) return passOutput();
619
+ opts.recordQuery(signature, structuredResultCount(event.raw) ?? estimateResultCount(extractToolResponseField(event.raw, OUTPUT_FIRST_TOOL_RESPONSE_KEYS)));
620
+ return passOutput();
621
+ } catch {
622
+ return passOutput();
623
+ }
624
+ };
625
+ const pre = (event) => {
626
+ try {
627
+ if (getToolName(event) !== opts.toolName) return passOutput();
628
+ const toolInput = getToolInput(event);
629
+ const signature = opts.buildSignature(toolInput);
630
+ if (signature === null) return passOutput();
631
+ const priorCount = opts.getMatchCount(signature);
632
+ if (priorCount === null) return passOutput();
633
+ if (priorCount < loadConfig().hints[opts.minMatchesConfigKey]) return passOutput();
634
+ recordStat(opts.statName, 0, 0);
635
+ const pattern = displaySafeText(typeof toolInput["pattern"] === "string" ? toolInput["pattern"] : "");
636
+ return contextOutput(
637
+ "Note: an identical " + opts.toolName + ' for "' + pattern + '" already ran this session and returned ' + priorCount + (priorCount === 1 ? " match" : " matches") + ". If that result already answers this, you can skip re-running it."
638
+ );
639
+ } catch {
640
+ return passOutput();
641
+ }
642
+ };
643
+ return { post, pre };
644
+ }
645
+
646
+ // src/vscode_path_gate.ts
647
+ init_define_import_meta_env();
648
+ import * as path from "node:path";
649
+ function resolvesToUncPath(target, cwd) {
650
+ try {
651
+ return isUncOrDevicePath(path.resolve(cwd ?? process.cwd(), target));
652
+ } catch {
653
+ return false;
654
+ }
655
+ }
656
+ function vscodePathAllowed(filePath, workspace) {
657
+ if (isUncOrDevicePath(filePath)) return false;
658
+ if (workspace === void 0 || isUncOrDevicePath(workspace)) return false;
659
+ const resolvedRoot = path.resolve(workspace);
660
+ const resolvedTarget = path.resolve(workspace, filePath);
661
+ const root = foldPathForContainment(resolvedRoot.replace(/\\/g, "/"));
662
+ const target = foldPathForContainment(resolvedTarget.replace(/\\/g, "/"));
663
+ if (target !== root && !target.startsWith(root.endsWith("/") ? root : root + "/")) return false;
664
+ return isInsideRoot(resolvedTarget, resolvedRoot);
665
+ }
666
+ function isVscodeEvent(event) {
667
+ return event.raw["_tg_harness"] === "vscode" || event.raw[VSCODE_TOOL_NAME_KEY] !== void 0;
668
+ }
669
+ function preToolPathDeclined(event, target) {
670
+ if (target === void 0) return false;
671
+ const cwd = getCwd(event);
672
+ if (isUncOrDevicePath(target) || resolvesToUncPath(target, cwd)) return true;
673
+ if (isVscodeEvent(event)) return !vscodePathAllowed(target, cwd);
674
+ return escapesOntoNetworkThroughLinks(target, cwd);
675
+ }
676
+ function commandPathIsTouchable(filePath, event) {
677
+ if (event === void 0) return !escapesOntoNetworkThroughLinks(filePath);
678
+ return !preToolPathDeclined(event, filePath);
679
+ }
680
+
681
+ // src/delivering_deny.ts
682
+ init_define_import_meta_env();
683
+ var DELIVERS_CONTENT_RE = /is inlined below instead of the full body|headings below instead of the full body|in place of the full file/;
684
+
685
+ export {
686
+ getToolName,
687
+ getToolInput,
688
+ getFilePath,
689
+ getCwd,
690
+ getTranscriptPath,
691
+ extractToolResponseField,
692
+ replaceToolResponseField,
693
+ OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
694
+ BODY_FIRST_TOOL_RESPONSE_KEYS,
695
+ isMcpErrorResponse,
696
+ extractToolResultText,
697
+ mcpContentBlocks,
698
+ isTextualContentBlock,
699
+ withPreservedBlocks,
700
+ passOutput,
701
+ denyOutput,
702
+ contextOutput,
703
+ emitRewrite,
704
+ emitRewriteIfChanged,
705
+ estimateResultCount,
706
+ structuredResultCount,
707
+ makeDedupHintHandlers,
708
+ GEMINI_TOOL_NAME_MAP,
709
+ VSCODE_TOOL_NAME_KEY,
710
+ vscodeNativeToolInput,
711
+ normalizePayload,
712
+ vscodePathAllowed,
713
+ preToolPathDeclined,
714
+ commandPathIsTouchable,
715
+ stripUnsafeSuggestions,
716
+ leadWithCommand,
717
+ DELIVERS_CONTENT_RE
718
+ };