@solongate/proxy 0.31.0 → 0.32.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.
package/dist/index.js CHANGED
@@ -459,8 +459,8 @@ var init_cli_utils = __esm({
459
459
 
460
460
  // src/init.ts
461
461
  var init_exports = {};
462
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
463
- import { resolve as resolve3, join, dirname as dirname2 } from "path";
462
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
463
+ import { resolve as resolve3, join as join2, dirname as dirname2 } from "path";
464
464
  import { homedir } from "os";
465
465
  import { fileURLToPath } from "url";
466
466
  import { execFileSync } from "child_process";
@@ -636,7 +636,7 @@ EXAMPLES
636
636
  console.log(help);
637
637
  }
638
638
  function readHookScript(filename) {
639
- return readFileSync4(join(HOOKS_DIR, filename), "utf-8");
639
+ return readFileSync4(join2(HOOKS_DIR, filename), "utf-8");
640
640
  }
641
641
  function unlockProtectedDirs() {
642
642
  const dirs = [".solongate", ".claude", ".cursor", ".gemini", ".antigravity", ".openclaw", ".perplexity"];
@@ -691,12 +691,12 @@ function unlockProtectedDirs() {
691
691
  function installHooks(selectedTools = [], wrappedMcpConfig) {
692
692
  unlockProtectedDirs();
693
693
  const hooksDir = resolve3(".solongate", "hooks");
694
- mkdirSync2(hooksDir, { recursive: true });
694
+ mkdirSync3(hooksDir, { recursive: true });
695
695
  const hookFiles = ["guard.mjs", "audit.mjs", "stop.mjs"];
696
696
  let hooksUpdated = 0;
697
697
  let hooksSkipped = 0;
698
698
  for (const filename of hookFiles) {
699
- const hookPath = join(hooksDir, filename);
699
+ const hookPath = join2(hooksDir, filename);
700
700
  const latest = readHookScript(filename);
701
701
  let needsWrite = true;
702
702
  if (existsSync4(hookPath)) {
@@ -728,7 +728,7 @@ function installHooks(selectedTools = [], wrappedMcpConfig) {
728
728
  const skippedNames = [];
729
729
  for (const client of clients) {
730
730
  const clientDir = resolve3(client.dir);
731
- mkdirSync2(clientDir, { recursive: true });
731
+ mkdirSync3(clientDir, { recursive: true });
732
732
  const guardCmd = `node .solongate/hooks/guard.mjs ${client.agentId} "${client.agentName}"`;
733
733
  const auditCmd = `node .solongate/hooks/audit.mjs ${client.agentId} "${client.agentName}"`;
734
734
  const stopCmd = `node .solongate/hooks/stop.mjs ${client.agentId} "${client.agentName}"`;
@@ -763,7 +763,7 @@ function installHooks(selectedTools = [], wrappedMcpConfig) {
763
763
  }
764
764
  }
765
765
  function installStandardHookConfig(clientDir, clientName, guardCmd, auditCmd, stopCmd) {
766
- const settingsPath = join(clientDir, "settings.json");
766
+ const settingsPath = join2(clientDir, "settings.json");
767
767
  const hookSettings = {
768
768
  PreToolUse: [
769
769
  { matcher: "", hooks: [{ type: "command", command: guardCmd }] }
@@ -796,12 +796,30 @@ function installCursorConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
796
796
  ],
797
797
  postToolUse: [
798
798
  { matcher: "", command: auditCmd }
799
+ ],
800
+ beforeShellExecution: [
801
+ { matcher: "", command: guardCmd, failClosed: true }
802
+ ],
803
+ afterShellExecution: [
804
+ { matcher: "", command: auditCmd }
805
+ ],
806
+ beforeReadFile: [
807
+ { matcher: "", command: guardCmd, failClosed: true }
808
+ ],
809
+ afterFileEdit: [
810
+ { matcher: "", command: auditCmd }
811
+ ],
812
+ beforeMCPExecution: [
813
+ { matcher: "", command: guardCmd, failClosed: true }
814
+ ],
815
+ afterMCPExecution: [
816
+ { matcher: "", command: auditCmd }
799
817
  ]
800
818
  };
801
819
  const hooksContent = JSON.stringify({ version: 1, hooks: hookConfig }, null, 2) + "\n";
802
- const userCursorDir = join(homedir(), ".cursor");
820
+ const userCursorDir = join2(homedir(), ".cursor");
803
821
  if (existsSync4(userCursorDir)) {
804
- const userHooksPath = join(userCursorDir, "hooks.json");
822
+ const userHooksPath = join2(userCursorDir, "hooks.json");
805
823
  const existingUserHooks = existsSync4(userHooksPath) ? readFileSync4(userHooksPath, "utf-8") : "";
806
824
  if (hooksContent !== existingUserHooks) {
807
825
  writeFileSync2(userHooksPath, hooksContent);
@@ -809,7 +827,7 @@ function installCursorConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
809
827
  changed = true;
810
828
  }
811
829
  }
812
- const hooksPath = join(clientDir, "hooks.json");
830
+ const hooksPath = join2(clientDir, "hooks.json");
813
831
  const existingHooks = existsSync4(hooksPath) ? readFileSync4(hooksPath, "utf-8") : "";
814
832
  if (hooksContent !== existingHooks) {
815
833
  writeFileSync2(hooksPath, hooksContent);
@@ -831,7 +849,7 @@ function installCursorConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
831
849
  }
832
850
  cursorMcpConfig.mcpServers[name] = { ...server, args };
833
851
  }
834
- const mcpPath = join(clientDir, "mcp.json");
852
+ const mcpPath = join2(clientDir, "mcp.json");
835
853
  const mcpStr = JSON.stringify(cursorMcpConfig, null, 2) + "\n";
836
854
  const existingMcp = existsSync4(mcpPath) ? readFileSync4(mcpPath, "utf-8") : "";
837
855
  if (mcpStr !== existingMcp) {
@@ -840,7 +858,7 @@ function installCursorConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
840
858
  changed = true;
841
859
  }
842
860
  }
843
- const staleSettings = join(clientDir, "settings.json");
861
+ const staleSettings = join2(clientDir, "settings.json");
844
862
  if (existsSync4(staleSettings)) {
845
863
  try {
846
864
  const content = JSON.parse(readFileSync4(staleSettings, "utf-8"));
@@ -855,7 +873,7 @@ function installCursorConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
855
873
  return changed ? "installed" : "skipped";
856
874
  }
857
875
  function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcpConfig) {
858
- const settingsPath = join(clientDir, "settings.json");
876
+ const settingsPath = join2(clientDir, "settings.json");
859
877
  let existing = {};
860
878
  try {
861
879
  existing = JSON.parse(readFileSync4(settingsPath, "utf-8"));
@@ -1209,7 +1227,7 @@ var init_init = __esm({
1209
1227
  "mcp.json",
1210
1228
  ".claude/mcp.json"
1211
1229
  ];
1212
- CLAUDE_DESKTOP_PATHS = process.platform === "win32" ? [join(process.env["APPDATA"] ?? "", "Claude", "claude_desktop_config.json")] : process.platform === "darwin" ? [join(process.env["HOME"] ?? "", "Library", "Application Support", "Claude", "claude_desktop_config.json")] : [join(process.env["HOME"] ?? "", ".config", "claude", "claude_desktop_config.json")];
1230
+ CLAUDE_DESKTOP_PATHS = process.platform === "win32" ? [join2(process.env["APPDATA"] ?? "", "Claude", "claude_desktop_config.json")] : process.platform === "darwin" ? [join2(process.env["HOME"] ?? "", "Library", "Application Support", "Claude", "claude_desktop_config.json")] : [join2(process.env["HOME"] ?? "", ".config", "claude", "claude_desktop_config.json")];
1213
1231
  sleep = (ms) => new Promise((r) => setTimeout(r, ms));
1214
1232
  SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1215
1233
  spinnerInterval = null;
@@ -1562,8 +1580,8 @@ var init_inject = __esm({
1562
1580
 
1563
1581
  // src/create.ts
1564
1582
  var create_exports = {};
1565
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
1566
- import { resolve as resolve5, join as join2 } from "path";
1583
+ import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
1584
+ import { resolve as resolve5, join as join3 } from "path";
1567
1585
  import { execSync as execSync2 } from "child_process";
1568
1586
  function withSpinner(message, fn) {
1569
1587
  const frames = ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"];
@@ -1643,7 +1661,7 @@ EXAMPLES
1643
1661
  }
1644
1662
  function createProject(dir, name, _policy) {
1645
1663
  writeFileSync4(
1646
- join2(dir, "package.json"),
1664
+ join3(dir, "package.json"),
1647
1665
  JSON.stringify(
1648
1666
  {
1649
1667
  name,
@@ -1673,7 +1691,7 @@ function createProject(dir, name, _policy) {
1673
1691
  ) + "\n"
1674
1692
  );
1675
1693
  writeFileSync4(
1676
- join2(dir, "tsconfig.json"),
1694
+ join3(dir, "tsconfig.json"),
1677
1695
  JSON.stringify(
1678
1696
  {
1679
1697
  compilerOptions: {
@@ -1693,9 +1711,9 @@ function createProject(dir, name, _policy) {
1693
1711
  2
1694
1712
  ) + "\n"
1695
1713
  );
1696
- mkdirSync3(join2(dir, "src"), { recursive: true });
1714
+ mkdirSync4(join3(dir, "src"), { recursive: true });
1697
1715
  writeFileSync4(
1698
- join2(dir, "src", "index.ts"),
1716
+ join3(dir, "src", "index.ts"),
1699
1717
  `#!/usr/bin/env node
1700
1718
 
1701
1719
  console.log = (...args: unknown[]) => {
@@ -1740,7 +1758,7 @@ console.log('Press Ctrl+C to stop.');
1740
1758
  `
1741
1759
  );
1742
1760
  writeFileSync4(
1743
- join2(dir, ".mcp.json"),
1761
+ join3(dir, ".mcp.json"),
1744
1762
  JSON.stringify(
1745
1763
  {
1746
1764
  mcpServers: {
@@ -1758,12 +1776,12 @@ console.log('Press Ctrl+C to stop.');
1758
1776
  ) + "\n"
1759
1777
  );
1760
1778
  writeFileSync4(
1761
- join2(dir, ".env"),
1779
+ join3(dir, ".env"),
1762
1780
  `SOLONGATE_API_KEY=sg_live_YOUR_KEY_HERE
1763
1781
  `
1764
1782
  );
1765
1783
  writeFileSync4(
1766
- join2(dir, ".gitignore"),
1784
+ join3(dir, ".gitignore"),
1767
1785
  `node_modules/
1768
1786
  dist/
1769
1787
  *.solongate-backup
@@ -1782,7 +1800,7 @@ async function main3() {
1782
1800
  process.exit(1);
1783
1801
  }
1784
1802
  withSpinner(`Setting up ${opts.name}...`, () => {
1785
- mkdirSync3(dir, { recursive: true });
1803
+ mkdirSync4(dir, { recursive: true });
1786
1804
  createProject(dir, opts.name, opts.policy);
1787
1805
  });
1788
1806
  if (!opts.noInstall) {
@@ -2193,8 +2211,8 @@ import {
2193
2211
  ListResourceTemplatesRequestSchema
2194
2212
  } from "@modelcontextprotocol/sdk/types.js";
2195
2213
  import { createServer as createHttpServer } from "http";
2196
- import { resolve as resolve2 } from "path";
2197
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
2214
+ import { resolve as resolve2, join } from "path";
2215
+ import { existsSync as existsSync3, readFileSync as readFileSync3, mkdirSync as mkdirSync2, appendFileSync } from "fs";
2198
2216
 
2199
2217
  // ../core/dist/index.js
2200
2218
  import { z } from "zod";
@@ -6249,6 +6267,20 @@ var SolonGateProxy = class {
6249
6267
  if (this.server) {
6250
6268
  const clientVersion = this.server.getClientVersion();
6251
6269
  log2(`MCP clientInfo raw: ${JSON.stringify(clientVersion)}`);
6270
+ try {
6271
+ const debugInfo = {
6272
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6273
+ clientInfo: clientVersion,
6274
+ agentNameFlag: this.config.agentName ?? null,
6275
+ resolvedAgentId: this.agentId,
6276
+ resolvedAgentName: this.agentName,
6277
+ pid: process.pid
6278
+ };
6279
+ const debugDir = resolve2(".solongate");
6280
+ mkdirSync2(debugDir, { recursive: true });
6281
+ appendFileSync(join(debugDir, ".debug-proxy"), JSON.stringify(debugInfo) + "\n");
6282
+ } catch {
6283
+ }
6252
6284
  if (clientVersion?.name && !this.config.agentName) {
6253
6285
  const normalized = this.normalizeAgentName(clientVersion.name);
6254
6286
  this.agentId = normalized.id;
package/dist/init.js CHANGED
@@ -382,6 +382,24 @@ function installCursorConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
382
382
  ],
383
383
  postToolUse: [
384
384
  { matcher: "", command: auditCmd }
385
+ ],
386
+ beforeShellExecution: [
387
+ { matcher: "", command: guardCmd, failClosed: true }
388
+ ],
389
+ afterShellExecution: [
390
+ { matcher: "", command: auditCmd }
391
+ ],
392
+ beforeReadFile: [
393
+ { matcher: "", command: guardCmd, failClosed: true }
394
+ ],
395
+ afterFileEdit: [
396
+ { matcher: "", command: auditCmd }
397
+ ],
398
+ beforeMCPExecution: [
399
+ { matcher: "", command: guardCmd, failClosed: true }
400
+ ],
401
+ afterMCPExecution: [
402
+ { matcher: "", command: auditCmd }
385
403
  ]
386
404
  };
387
405
  const hooksContent = JSON.stringify({ version: 1, hooks: hookConfig }, null, 2) + "\n";
package/dist/lib.js CHANGED
@@ -3612,8 +3612,8 @@ import {
3612
3612
  ListResourceTemplatesRequestSchema
3613
3613
  } from "@modelcontextprotocol/sdk/types.js";
3614
3614
  import { createServer as createHttpServer } from "http";
3615
- import { resolve as resolve2 } from "path";
3616
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
3615
+ import { resolve as resolve2, join } from "path";
3616
+ import { existsSync as existsSync3, readFileSync as readFileSync3, mkdirSync as mkdirSync2, appendFileSync } from "fs";
3617
3617
 
3618
3618
  // src/config.ts
3619
3619
  import { readFileSync, existsSync } from "fs";
@@ -4460,6 +4460,20 @@ var SolonGateProxy = class {
4460
4460
  if (this.server) {
4461
4461
  const clientVersion = this.server.getClientVersion();
4462
4462
  log2(`MCP clientInfo raw: ${JSON.stringify(clientVersion)}`);
4463
+ try {
4464
+ const debugInfo = {
4465
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4466
+ clientInfo: clientVersion,
4467
+ agentNameFlag: this.config.agentName ?? null,
4468
+ resolvedAgentId: this.agentId,
4469
+ resolvedAgentName: this.agentName,
4470
+ pid: process.pid
4471
+ };
4472
+ const debugDir = resolve2(".solongate");
4473
+ mkdirSync2(debugDir, { recursive: true });
4474
+ appendFileSync(join(debugDir, ".debug-proxy"), JSON.stringify(debugInfo) + "\n");
4475
+ } catch {
4476
+ }
4463
4477
  if (clientVersion?.name && !this.config.agentName) {
4464
4478
  const normalized = this.normalizeAgentName(clientVersion.name);
4465
4479
  this.agentId = normalized.id;
package/hooks/audit.mjs CHANGED
@@ -38,8 +38,12 @@ process.stdin.on('end', async () => {
38
38
  try {
39
39
  const data = JSON.parse(input);
40
40
 
41
- // Debug: dump raw stdin to file for agent detection troubleshooting
42
- try { writeFileSync(resolve('.solongate', '.debug-stdin'), JSON.stringify(data, null, 2)); } catch {}
41
+ // Debug: append raw stdin to file for agent detection troubleshooting
42
+ try {
43
+ const debugLine = JSON.stringify({ ts: new Date().toISOString(), argv: process.argv.slice(2), cursor_version: data.cursor_version || null, tool_name: data.tool_name || data.toolName, agent_id: AGENT_ID, hook_event_name: data.hook_event_name || null }) + '\n';
44
+ const { appendFileSync: afs } = await import('node:fs');
45
+ afs(resolve('.solongate', '.debug-audit-log'), debugLine);
46
+ } catch {}
43
47
 
44
48
  // Auto-detect agent from stdin data (overrides CLI args if detected)
45
49
  if (data.cursor_version) {
@@ -50,9 +54,35 @@ process.stdin.on('end', async () => {
50
54
  AGENT_NAME = 'Gemini CLI';
51
55
  }
52
56
 
53
- // Normalize field names across tools (Claude: tool_name, others may use toolName)
54
- const toolName = data.tool_name || data.toolName || 'unknown';
55
- const toolInput = data.tool_input || data.toolInput || data.params || {};
57
+ const hookEvent = data.hook_event_name || '';
58
+
59
+ // Map Cursor-specific hook events to standard tool_name/tool_input
60
+ let toolName = data.tool_name || data.toolName || '';
61
+ let toolInput = data.tool_input || data.toolInput || data.params || {};
62
+
63
+ if (!toolName && hookEvent) {
64
+ switch (hookEvent) {
65
+ case 'afterShellExecution':
66
+ toolName = 'Shell';
67
+ toolInput = { command: data.command || '' };
68
+ break;
69
+ case 'afterFileEdit':
70
+ toolName = 'Edit';
71
+ toolInput = { file_path: data.file_path || '' };
72
+ break;
73
+ case 'afterMCPExecution':
74
+ toolName = data.tool_name || data.toolName || `mcp:${data.server_name || 'unknown'}`;
75
+ toolInput = data.arguments || {};
76
+ break;
77
+ case 'postToolUse':
78
+ case 'postToolUseFailure':
79
+ toolName = data.tool_name || data.toolName || 'unknown';
80
+ break;
81
+ default:
82
+ break;
83
+ }
84
+ }
85
+ if (!toolName) toolName = 'unknown';
56
86
 
57
87
  if (toolName === 'Bash' && JSON.stringify(toolInput).includes('audit-logs')) {
58
88
  process.exit(0);
@@ -71,14 +101,16 @@ process.stdin.on('end', async () => {
71
101
  }
72
102
  } catch {}
73
103
 
74
- // Cursor uses result_json, Claude uses tool_response
104
+ // Cursor uses tool_output, Claude uses tool_response
75
105
  const toolResponse = data.tool_response || data.toolResponse || {};
106
+ const toolOutput = data.tool_output || data.toolOutput || '';
76
107
  const resultJson = data.result_json ? (typeof data.result_json === 'string' ? data.result_json : JSON.stringify(data.result_json)) : '';
77
108
 
78
109
  const hasError = guardDenied ||
79
110
  toolResponse.error ||
80
111
  toolResponse.exitCode > 0 ||
81
112
  toolResponse.isError ||
113
+ (toolOutput && typeof toolOutput === 'string' && toolOutput.includes('"error"')) ||
82
114
  (resultJson && resultJson.includes('"error"'));
83
115
 
84
116
  const argsSummary = {};
package/hooks/guard.mjs CHANGED
@@ -46,17 +46,33 @@ let AGENT_ID = process.argv[2] || 'claude-code';
46
46
  let AGENT_NAME = process.argv[3] || 'Claude Code';
47
47
 
48
48
  // ── Per-tool block/allow output ──
49
- // Claude Code: exit 2 + stderr = BLOCK, exit 0 = ALLOW
50
- // Cursor: JSON stdout {"permission": "deny", "user_message": "..."} = BLOCK
51
- // Gemini CLI: JSON stdout {"decision": "deny", "reason": "..."} = BLOCK
49
+ // Response format depends on BOTH the agent AND the hook event type:
50
+ // Claude Code: exit 2 + stderr = BLOCK, exit 0 = ALLOW
51
+ // Cursor preToolUse: {"permission": "deny/allow", "user_message": "..."}
52
+ // Cursor before*: {"continue": false/true, "userMessage": "..."}
53
+ // Gemini CLI: {"decision": "deny/allow", "reason": "..."}
54
+ let HOOK_EVENT = ''; // set from stdin hook_event_name
55
+ const CURSOR_BEFORE_EVENTS = new Set([
56
+ 'beforeShellExecution', 'beforeReadFile', 'beforeMCPExecution',
57
+ ]);
58
+
52
59
  function blockTool(reason) {
53
60
  if (AGENT_ID === 'cursor') {
54
- // Cursor: JSON stdout with permission: "deny", exit 0
55
- process.stdout.write(JSON.stringify({
56
- permission: 'deny',
57
- user_message: `[SolonGate] ${reason}`,
58
- agent_message: `BLOCKED by SolonGate security policy: ${reason}`,
59
- }));
61
+ if (CURSOR_BEFORE_EVENTS.has(HOOK_EVENT)) {
62
+ // Cursor before* events use continue: false
63
+ process.stdout.write(JSON.stringify({
64
+ continue: false,
65
+ userMessage: `[SolonGate] ${reason}`,
66
+ agentMessage: `BLOCKED by SolonGate security policy: ${reason}`,
67
+ }));
68
+ } else {
69
+ // Cursor preToolUse uses permission: deny
70
+ process.stdout.write(JSON.stringify({
71
+ permission: 'deny',
72
+ user_message: `[SolonGate] ${reason}`,
73
+ agent_message: `BLOCKED by SolonGate security policy: ${reason}`,
74
+ }));
75
+ }
60
76
  process.exit(0);
61
77
  } else if (AGENT_ID === 'gemini-cli') {
62
78
  process.stdout.write(JSON.stringify({
@@ -73,7 +89,11 @@ function blockTool(reason) {
73
89
 
74
90
  function allowTool() {
75
91
  if (AGENT_ID === 'cursor') {
76
- process.stdout.write(JSON.stringify({ permission: 'allow' }));
92
+ if (CURSOR_BEFORE_EVENTS.has(HOOK_EVENT)) {
93
+ process.stdout.write(JSON.stringify({ continue: true }));
94
+ } else {
95
+ process.stdout.write(JSON.stringify({ permission: 'allow' }));
96
+ }
77
97
  } else if (AGENT_ID === 'gemini-cli') {
78
98
  process.stdout.write(JSON.stringify({ decision: 'allow' }));
79
99
  }
@@ -384,14 +404,62 @@ process.stdin.on('end', async () => {
384
404
  AGENT_NAME = 'Gemini CLI';
385
405
  }
386
406
 
387
- // Normalize field names across tools (Claude: tool_name/tool_input, others may use toolName/toolInput/params)
407
+ // Track hook event name for response format selection
408
+ HOOK_EVENT = raw.hook_event_name || '';
409
+
410
+ // Debug: append guard invocation to debug log
411
+ try {
412
+ const { appendFileSync: afs, mkdirSync: mds } = await import('node:fs');
413
+ mds(resolve('.solongate'), { recursive: true });
414
+ const debugLine = JSON.stringify({ ts: new Date().toISOString(), hook: 'guard', argv: process.argv.slice(2), cursor_version: raw.cursor_version || null, tool_name: raw.tool_name || raw.toolName || raw.command, agent_id: AGENT_ID, hook_event_name: HOOK_EVENT }) + '\n';
415
+ afs(resolve('.solongate', '.debug-guard-log'), debugLine);
416
+ } catch {}
417
+
418
+ // Map Cursor-specific hook events to standard tool_name/tool_input format
419
+ // beforeShellExecution: {command, cwd} → tool_name=Shell, tool_input={command}
420
+ // beforeReadFile: {file_path} → tool_name=Read, tool_input={file_path}
421
+ // afterFileEdit: {file_path, new_content} → tool_name=Edit, tool_input={file_path}
422
+ // beforeMCPExecution: {server_name, tool_name, arguments} → tool_name=<mcp_tool>, tool_input=arguments
423
+ let mappedToolName = raw.tool_name || raw.toolName || '';
424
+ let mappedToolInput = raw.tool_input || raw.toolInput || raw.params || {};
425
+
426
+ if (!mappedToolName && HOOK_EVENT) {
427
+ switch (HOOK_EVENT) {
428
+ case 'beforeShellExecution':
429
+ mappedToolName = 'Shell';
430
+ mappedToolInput = { command: raw.command || '', cwd: raw.cwd || '' };
431
+ break;
432
+ case 'afterShellExecution':
433
+ mappedToolName = 'Shell';
434
+ mappedToolInput = { command: raw.command || '', cwd: raw.cwd || '' };
435
+ break;
436
+ case 'beforeReadFile':
437
+ mappedToolName = 'Read';
438
+ mappedToolInput = { file_path: raw.file_path || '' };
439
+ break;
440
+ case 'afterFileEdit':
441
+ mappedToolName = 'Edit';
442
+ mappedToolInput = { file_path: raw.file_path || '', new_content: raw.new_content || '' };
443
+ break;
444
+ case 'beforeMCPExecution':
445
+ mappedToolName = raw.tool_name || raw.toolName || `mcp:${raw.server_name || 'unknown'}`;
446
+ mappedToolInput = raw.arguments || raw.tool_input || {};
447
+ break;
448
+ case 'afterMCPExecution':
449
+ mappedToolName = raw.tool_name || raw.toolName || `mcp:${raw.server_name || 'unknown'}`;
450
+ mappedToolInput = raw.arguments || raw.tool_input || {};
451
+ break;
452
+ }
453
+ }
454
+
455
+ // Normalize field names across tools
388
456
  const data = {
389
457
  ...raw,
390
- tool_name: raw.tool_name || raw.toolName || '',
391
- tool_input: raw.tool_input || raw.toolInput || raw.params || {},
458
+ tool_name: mappedToolName,
459
+ tool_input: mappedToolInput,
392
460
  tool_response: raw.tool_response || raw.toolResponse || {},
393
461
  cwd: raw.cwd || process.cwd(),
394
- session_id: raw.session_id || raw.sessionId || '',
462
+ session_id: raw.session_id || raw.sessionId || raw.conversation_id || '',
395
463
  };
396
464
  const args = data.tool_input;
397
465
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "AI tool security proxy — protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. Zero code changes required.",
5
5
  "type": "module",
6
6
  "bin": {