@solongate/proxy 0.81.74 → 0.81.76

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
@@ -9390,9 +9390,9 @@ function DlpPanel({ focused }) {
9390
9390
  entry: idx
9391
9391
  });
9392
9392
  });
9393
- header("h:custom", "custom patterns", "regex over tool args \xB7 a add \xB7 d remove");
9394
- note("n:custom-help", " a JS regex, e.g. sk-[a-z0-9]{20} AKIA[0-9A-Z]{16} password=\\S+");
9395
- if ((dlp?.custom.length ?? 0) === 0) note("n:custom", " (none \u2014 press a to add a name + regex)");
9393
+ header("h:custom", "custom patterns", "a add \xB7 d remove");
9394
+ note("n:custom-help", " same glob syntax as ghost, * = any chars, e.g. sk-* AKIA* *PRIVATE KEY*");
9395
+ if ((dlp?.custom.length ?? 0) === 0) note("n:custom", " (none \u2014 press a to add a name + pattern)");
9396
9396
  (dlp?.custom ?? []).forEach((c2, i) => {
9397
9397
  const isCur = focused && ei === selC;
9398
9398
  const idx = ei++;
@@ -9420,7 +9420,7 @@ function DlpPanel({ focused }) {
9420
9420
  entry: idx
9421
9421
  });
9422
9422
  });
9423
- const editRows = adding === "re" ? 2 : adding ? 1 : 0;
9423
+ const editRows = adding ? 1 : 0;
9424
9424
  const headerRows = 2 + editRows + (status ? 1 : 0);
9425
9425
  const budget = Math.max(3, rows - headerRows - 1);
9426
9426
  const selLine = Math.max(0, lines.findIndex((l) => l.entry === selC));
@@ -9437,59 +9437,40 @@ function DlpPanel({ focused }) {
9437
9437
  dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
9438
9438
  ] }),
9439
9439
  /* @__PURE__ */ jsx5(Text5, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 move \xB7 space on/off \xB7 m mode \xB7 a add pattern \xB7 r add route \xB7 d remove \xB7 g ghost \xB7 s save${moreAbove ? ` \xB7 \u25B2${moreAbove}` : ""}${moreBelow ? ` \xB7 \u25BC${moreBelow}` : ""}` : "press \u2192 to edit" }),
9440
- adding ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
9441
- /* @__PURE__ */ jsxs5(Box5, { children: [
9442
- /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: adding === "name" ? "new pattern name: " : adding === "re" ? `regex for "${newName}" (e.g. sk-[a-z0-9]{20}): ` : "ghost path to hide (glob, * = any chars, e.g. */.env *.pem): " }),
9443
- /* @__PURE__ */ jsx5(
9444
- TextInput3,
9445
- {
9446
- value: input,
9447
- onChange: setInput,
9448
- onSubmit: (v) => {
9449
- const t = v.trim();
9450
- if (adding === "name") {
9451
- if (!t) return setAdding(null);
9452
- setNewName(t);
9453
- setInput("");
9454
- setAdding("re");
9455
- } else if (adding === "re") {
9456
- if (t && dlp) mutate({ ...dlp, custom: [...dlp.custom, { name: newName, re: t }] });
9457
- setAdding(null);
9458
- } else {
9459
- if (t && !ghostPats.includes(t)) {
9460
- setGhostPats((ps) => [...ps, t]);
9461
- setDirty(true);
9462
- setStatus(null);
9463
- }
9464
- setAdding(null);
9440
+ adding ? /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsxs5(Box5, { children: [
9441
+ /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: adding === "name" ? "new pattern name: " : adding === "re" ? `pattern for "${newName}" (glob, * = any chars, e.g. sk-* AKIA*): ` : "ghost path to hide (glob, * = any chars, e.g. */.env *.pem): " }),
9442
+ /* @__PURE__ */ jsx5(
9443
+ TextInput3,
9444
+ {
9445
+ value: input,
9446
+ onChange: setInput,
9447
+ onSubmit: (v) => {
9448
+ const t = v.trim();
9449
+ if (adding === "name") {
9450
+ if (!t) return setAdding(null);
9451
+ setNewName(t);
9452
+ setInput("");
9453
+ setAdding("re");
9454
+ } else if (adding === "re") {
9455
+ if (t && dlp) mutate({ ...dlp, custom: [...dlp.custom, { name: newName, re: t }] });
9456
+ setAdding(null);
9457
+ } else {
9458
+ if (t && !ghostPats.includes(t)) {
9459
+ setGhostPats((ps) => [...ps, t]);
9460
+ setDirty(true);
9461
+ setStatus(null);
9465
9462
  }
9463
+ setAdding(null);
9466
9464
  }
9467
9465
  }
9468
- )
9469
- ] }),
9470
- adding === "re" ? /* @__PURE__ */ jsx5(RegexTest, { re: input }) : null
9471
- ] }) : null,
9466
+ }
9467
+ )
9468
+ ] }) }) : null,
9472
9469
  /* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", height: budget, overflow: "hidden", children: win.map((l) => l.node) }),
9473
9470
  status ? /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, wrap: "truncate", children: status }) : null
9474
9471
  ] }) : null });
9475
9472
  }
9476
- function RegexTest({ re }) {
9477
- if (!re.trim()) return /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " regex: type a pattern\u2026" });
9478
- let rx = null;
9479
- let err2 = "";
9480
- try {
9481
- rx = new RegExp(re);
9482
- } catch (e) {
9483
- err2 = e instanceof Error ? e.message : String(e);
9484
- }
9485
- if (!rx) return /* @__PURE__ */ jsx5(Text5, { color: theme.bad, children: ` \u2717 invalid: ${err2.slice(0, 50)}` });
9486
- const hits = SAMPLES.filter((s) => rx.test(s));
9487
- return /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
9488
- /* @__PURE__ */ jsx5(Text5, { color: theme.ok, children: " \u2713 valid" }),
9489
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: hits.length ? ` matches ${hits.length}/${SAMPLES.length} samples: ${hits.map((h) => h.slice(0, 8)).join(", ")}` : " (no sample match \u2014 will only catch your custom secrets)" })
9490
- ] });
9491
- }
9492
- var MODES2, SAMPLES;
9473
+ var MODES2;
9493
9474
  var init_Dlp = __esm({
9494
9475
  "src/tui/panels/Dlp.tsx"() {
9495
9476
  "use strict";
@@ -9498,7 +9479,6 @@ var init_Dlp = __esm({
9498
9479
  init_hooks();
9499
9480
  init_theme();
9500
9481
  MODES2 = ["off", "detect", "block"];
9501
- SAMPLES = ["AKIA1234567890ABCD00", "sk-ant-api03-xxxxxxxx", "ghp_16chars0000000000000000000000000000", "password=hunter2", "Bearer eyJhbGciOi"];
9502
9482
  }
9503
9483
  });
9504
9484
 
@@ -11156,7 +11136,7 @@ var init_App = __esm({
11156
11136
  ["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
11157
11137
  ["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["s", "save"], ["x", "discard"]]],
11158
11138
  ["Rate limit", [["\u2191\u2193", "field"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["s", "save"]]],
11159
- ["DLP", [["\u2191\u2193", "move"], ["space", "toggle a built-in pattern on/off"], ["m", "cycle mode"], ["a", "add custom pattern (name \u2192 regex)"], ["r", "add a ghost route (path to hide)"], ["d", "remove custom / route"], ["g", "ghost on/off"], ["s", "save \xB7 x discard"]]],
11139
+ ["DLP", [["\u2191\u2193", "move"], ["space", "toggle a built-in pattern on/off"], ["m", "cycle mode"], ["a", "add custom pattern (name \u2192 glob, * = any chars)"], ["r", "add a ghost route (glob path to hide)"], ["d", "remove custom / route"], ["g", "ghost on/off"], ["s", "save \xB7 x discard"]]],
11160
11140
  ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["x / X", "delete entry / ALL (press twice)"], ["c", "clear filters"]]],
11161
11141
  ["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]],
11162
11142
  ["Accounts", [["a", "switch account (view another account logged in on this device)"], ["", "the header shows which account you are viewing; guard/logging keep the active key"]]]
@@ -12687,6 +12667,7 @@ function redactDeep(value, cfg) {
12687
12667
  if (typeof value === "string") {
12688
12668
  let out2 = redactString(value, cfg);
12689
12669
  if (ghost && ghost.length) out2 = ghostStripLines(out2, ghost);
12670
+ if (out2 === "" && value !== "") out2 = "\n";
12690
12671
  return out2;
12691
12672
  }
12692
12673
  if (Array.isArray(value)) {
package/dist/shield.js CHANGED
@@ -182,6 +182,7 @@ function redactDeep(value, cfg) {
182
182
  if (typeof value === "string") {
183
183
  let out = redactString(value, cfg);
184
184
  if (ghost && ghost.length) out = ghostStripLines(out, ghost);
185
+ if (out === "" && value !== "") out = "\n";
185
186
  return out;
186
187
  }
187
188
  if (Array.isArray(value)) {
package/dist/tui/index.js CHANGED
@@ -2438,9 +2438,9 @@ function DlpPanel({ focused }) {
2438
2438
  entry: idx
2439
2439
  });
2440
2440
  });
2441
- header("h:custom", "custom patterns", "regex over tool args \xB7 a add \xB7 d remove");
2442
- note("n:custom-help", " a JS regex, e.g. sk-[a-z0-9]{20} AKIA[0-9A-Z]{16} password=\\S+");
2443
- if ((dlp?.custom.length ?? 0) === 0) note("n:custom", " (none \u2014 press a to add a name + regex)");
2441
+ header("h:custom", "custom patterns", "a add \xB7 d remove");
2442
+ note("n:custom-help", " same glob syntax as ghost, * = any chars, e.g. sk-* AKIA* *PRIVATE KEY*");
2443
+ if ((dlp?.custom.length ?? 0) === 0) note("n:custom", " (none \u2014 press a to add a name + pattern)");
2444
2444
  (dlp?.custom ?? []).forEach((c2, i) => {
2445
2445
  const isCur = focused && ei === selC;
2446
2446
  const idx = ei++;
@@ -2468,7 +2468,7 @@ function DlpPanel({ focused }) {
2468
2468
  entry: idx
2469
2469
  });
2470
2470
  });
2471
- const editRows = adding === "re" ? 2 : adding ? 1 : 0;
2471
+ const editRows = adding ? 1 : 0;
2472
2472
  const headerRows = 2 + editRows + (status ? 1 : 0);
2473
2473
  const budget = Math.max(3, rows - headerRows - 1);
2474
2474
  const selLine = Math.max(0, lines.findIndex((l) => l.entry === selC));
@@ -2485,59 +2485,39 @@ function DlpPanel({ focused }) {
2485
2485
  dirty ? /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: " \u25CF unsaved (s save \xB7 x discard)" }) : null
2486
2486
  ] }),
2487
2487
  /* @__PURE__ */ jsx5(Text5, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 move \xB7 space on/off \xB7 m mode \xB7 a add pattern \xB7 r add route \xB7 d remove \xB7 g ghost \xB7 s save${moreAbove ? ` \xB7 \u25B2${moreAbove}` : ""}${moreBelow ? ` \xB7 \u25BC${moreBelow}` : ""}` : "press \u2192 to edit" }),
2488
- adding ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
2489
- /* @__PURE__ */ jsxs5(Box5, { children: [
2490
- /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: adding === "name" ? "new pattern name: " : adding === "re" ? `regex for "${newName}" (e.g. sk-[a-z0-9]{20}): ` : "ghost path to hide (glob, * = any chars, e.g. */.env *.pem): " }),
2491
- /* @__PURE__ */ jsx5(
2492
- TextInput3,
2493
- {
2494
- value: input,
2495
- onChange: setInput,
2496
- onSubmit: (v) => {
2497
- const t = v.trim();
2498
- if (adding === "name") {
2499
- if (!t) return setAdding(null);
2500
- setNewName(t);
2501
- setInput("");
2502
- setAdding("re");
2503
- } else if (adding === "re") {
2504
- if (t && dlp) mutate({ ...dlp, custom: [...dlp.custom, { name: newName, re: t }] });
2505
- setAdding(null);
2506
- } else {
2507
- if (t && !ghostPats.includes(t)) {
2508
- setGhostPats((ps) => [...ps, t]);
2509
- setDirty(true);
2510
- setStatus(null);
2511
- }
2512
- setAdding(null);
2488
+ adding ? /* @__PURE__ */ jsx5(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsxs5(Box5, { children: [
2489
+ /* @__PURE__ */ jsx5(Text5, { color: theme.warn, children: adding === "name" ? "new pattern name: " : adding === "re" ? `pattern for "${newName}" (glob, * = any chars, e.g. sk-* AKIA*): ` : "ghost path to hide (glob, * = any chars, e.g. */.env *.pem): " }),
2490
+ /* @__PURE__ */ jsx5(
2491
+ TextInput3,
2492
+ {
2493
+ value: input,
2494
+ onChange: setInput,
2495
+ onSubmit: (v) => {
2496
+ const t = v.trim();
2497
+ if (adding === "name") {
2498
+ if (!t) return setAdding(null);
2499
+ setNewName(t);
2500
+ setInput("");
2501
+ setAdding("re");
2502
+ } else if (adding === "re") {
2503
+ if (t && dlp) mutate({ ...dlp, custom: [...dlp.custom, { name: newName, re: t }] });
2504
+ setAdding(null);
2505
+ } else {
2506
+ if (t && !ghostPats.includes(t)) {
2507
+ setGhostPats((ps) => [...ps, t]);
2508
+ setDirty(true);
2509
+ setStatus(null);
2513
2510
  }
2511
+ setAdding(null);
2514
2512
  }
2515
2513
  }
2516
- )
2517
- ] }),
2518
- adding === "re" ? /* @__PURE__ */ jsx5(RegexTest, { re: input }) : null
2519
- ] }) : null,
2514
+ }
2515
+ )
2516
+ ] }) }) : null,
2520
2517
  /* @__PURE__ */ jsx5(Box5, { marginTop: 1, flexDirection: "column", height: budget, overflow: "hidden", children: win.map((l) => l.node) }),
2521
2518
  status ? /* @__PURE__ */ jsx5(Text5, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, wrap: "truncate", children: status }) : null
2522
2519
  ] }) : null });
2523
2520
  }
2524
- var SAMPLES = ["AKIA1234567890ABCD00", "sk-ant-api03-xxxxxxxx", "ghp_16chars0000000000000000000000000000", "password=hunter2", "Bearer eyJhbGciOi"];
2525
- function RegexTest({ re }) {
2526
- if (!re.trim()) return /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: " regex: type a pattern\u2026" });
2527
- let rx = null;
2528
- let err = "";
2529
- try {
2530
- rx = new RegExp(re);
2531
- } catch (e) {
2532
- err = e instanceof Error ? e.message : String(e);
2533
- }
2534
- if (!rx) return /* @__PURE__ */ jsx5(Text5, { color: theme.bad, children: ` \u2717 invalid: ${err.slice(0, 50)}` });
2535
- const hits = SAMPLES.filter((s) => rx.test(s));
2536
- return /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
2537
- /* @__PURE__ */ jsx5(Text5, { color: theme.ok, children: " \u2713 valid" }),
2538
- /* @__PURE__ */ jsx5(Text5, { color: theme.dim, children: hits.length ? ` matches ${hits.length}/${SAMPLES.length} samples: ${hits.map((h) => h.slice(0, 8)).join(", ")}` : " (no sample match \u2014 will only catch your custom secrets)" })
2539
- ] });
2540
- }
2541
2521
 
2542
2522
  // src/tui/panels/Audit.tsx
2543
2523
  import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
@@ -4121,7 +4101,7 @@ var HELP = [
4121
4101
  ["Live", [["\u2191\u2193", "select a stream row"], ["enter", "full entry content"], ["w", "whitelist the selected DENY"], ["b", "block the selected ALLOW"], ["d / x / r", "filter denies / dlp / rate-limit"], ["f", "local / cloud filter"], ["/", "search"], ["s", "sessions (\u2191\u2193 pick, enter open)"], ["space", "copy mode (freeze)"]]],
4122
4102
  ["Policies", [["\u2191\u2193", "browse / select"], ["a", "activate (pin) selected policy"], ["x", "deactivate \u2014 no active policy"], ["enter", "open rules \u2192 open a rule"], ["space", "toggle a rule on/off"], ["e", "flip effect"], ["n", "new rule"], ["d", "delete rule"], ["m", "flip mode"], ["D", "dry-run the draft"], ["s", "save"], ["x", "discard"]]],
4123
4103
  ["Rate limit", [["\u2191\u2193", "field"], ["\u2190\u2192", "adjust (shift = \xB110)"], ["s", "save"]]],
4124
- ["DLP", [["\u2191\u2193", "move"], ["space", "toggle a built-in pattern on/off"], ["m", "cycle mode"], ["a", "add custom pattern (name \u2192 regex)"], ["r", "add a ghost route (path to hide)"], ["d", "remove custom / route"], ["g", "ghost on/off"], ["s", "save \xB7 x discard"]]],
4104
+ ["DLP", [["\u2191\u2193", "move"], ["space", "toggle a built-in pattern on/off"], ["m", "cycle mode"], ["a", "add custom pattern (name \u2192 glob, * = any chars)"], ["r", "add a ghost route (glob path to hide)"], ["d", "remove custom / route"], ["g", "ghost on/off"], ["s", "save \xB7 x discard"]]],
4125
4105
  ["Audit", [["v", "logs \u2194 sessions"], ["s", "source: cloud \u2194 local"], ["\u2190 \u2192", "prev / next page (500 each)"], ["\u2191\u2193", "select (list scrolls)"], ["enter", "full entry / session logs"], ["f / g", "decision / signal filter"], ["t / n / /", "tool / agent / search"], ["x / X", "delete entry / ALL (press twice)"], ["c", "clear filters"]]],
4126
4106
  ["Settings", [["\u2191\u2193", "move"], ["enter / space", "toggle \xB7 edit \xB7 add"], ["e", "webhook events"], ["d d", "delete"], ["r", "refresh"]]],
4127
4107
  ["Accounts", [["a", "switch account (view another account logged in on this device)"], ["", "the header shows which account you are viewing; guard/logging keep the active key"]]]
package/hooks/audit.mjs CHANGED
@@ -565,6 +565,13 @@ try { input += readFileSync(0, 'utf-8'); } catch {}
565
565
  }
566
566
  }
567
567
  if (typeof out === 'string') {
568
+ // Never emit EMPTY content. When ghost/DLP stripping removes every line
569
+ // (e.g. a grep/cat that hit only hidden or secret lines) and the tool
570
+ // ALSO errored, the resulting tool_result is is_error:true with empty
571
+ // content — which the API rejects ("content cannot be empty if is_error
572
+ // is true") and hard-crashes the whole agent conversation. Fall back to
573
+ // a bare newline: still looks like empty output, but is non-empty.
574
+ if (out === '') out = '\n';
568
575
  // Preserve the tool's result SHAPE. Bash and other tools deliver a
569
576
  // STRUCTURED result ({ stdout, stderr, ... } or { content }); Claude
570
577
  // Code rejects a bare-string replacement for those (hook warning) and
@@ -6808,7 +6808,10 @@ function blockTool(reason) {
6808
6808
  hookSpecificOutput: {
6809
6809
  hookEventName: "PreToolUse",
6810
6810
  permissionDecision: "deny",
6811
- permissionDecisionReason: reason
6811
+ // Never empty: a deny with an empty reason becomes an is_error tool_result
6812
+ // with empty content, which the API rejects and crashes the agent. Always
6813
+ // carry a SolonGate-worded reason so the block says WHAT stopped it.
6814
+ permissionDecisionReason: reason || "[SolonGate] Blocked by policy"
6812
6815
  }
6813
6816
  }));
6814
6817
  sgFinish(0);
package/hooks/guard.mjs CHANGED
@@ -395,7 +395,10 @@ function blockTool(reason) {
395
395
  hookSpecificOutput: {
396
396
  hookEventName: 'PreToolUse',
397
397
  permissionDecision: 'deny',
398
- permissionDecisionReason: reason,
398
+ // Never empty: a deny with an empty reason becomes an is_error tool_result
399
+ // with empty content, which the API rejects and crashes the agent. Always
400
+ // carry a SolonGate-worded reason so the block says WHAT stopped it.
401
+ permissionDecisionReason: reason || '[SolonGate] Blocked by policy',
399
402
  },
400
403
  }));
401
404
  sgFinish(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.74",
3
+ "version": "0.81.76",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {