opencode-swarm 7.150.0 → 7.151.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 (70) hide show
  1. package/dist/cli/{config-doctor-96qyv31a.js → config-doctor-23ega26j.js} +2 -2
  2. package/dist/cli/{core-xx0x5tfv.js → core-3g4krkj8.js} +1 -1
  3. package/dist/cli/{curation-policy-nck9g765.js → curation-policy-fmvccdrg.js} +6 -6
  4. package/dist/cli/{curator-llm-factory-b83jsg02.js → curator-llm-factory-k0nxn5gt.js} +28 -28
  5. package/dist/cli/{curator-frrehxxe.js → curator-rym2wtn6.js} +28 -28
  6. package/dist/cli/{evidence-summary-service-dv4bt2bx.js → evidence-summary-service-f0gvjesf.js} +12 -12
  7. package/dist/cli/{gate-evidence-dkvs7aez.js → gate-evidence-cghq17xc.js} +6 -6
  8. package/dist/cli/{guardrail-explain-1zbk21fq.js → guardrail-explain-cv52smw2.js} +29 -29
  9. package/dist/cli/{guardrail-log-d20jewtr.js → guardrail-log-wgwmh9b7.js} +5 -5
  10. package/dist/cli/{guardrail-reset-wb3ba243.js → guardrail-reset-vx7zsmzd.js} +28 -28
  11. package/dist/cli/{hive-promoter-10zv1b45.js → hive-promoter-jb2nzv3j.js} +28 -28
  12. package/dist/cli/{index-s0xz2520.js → index-02qc4ehj.js} +3 -3
  13. package/dist/cli/{index-k2sm9677.js → index-0q2mjxp2.js} +1 -1
  14. package/dist/cli/index-2qte9nr7.js +149 -0
  15. package/dist/cli/{index-mnhagjvn.js → index-5ep31be7.js} +4 -4
  16. package/dist/cli/{index-exn0ztsv.js → index-6zkm4p7p.js} +1 -1
  17. package/dist/cli/{index-t54jt9cs.js → index-7qbyetsf.js} +1 -1
  18. package/dist/cli/{index-x3pt7d0b.js → index-8wy7pb4a.js} +2 -2
  19. package/dist/cli/{index-prxrmc3b.js → index-9wpr1p2g.js} +2 -2
  20. package/dist/cli/{index-xggpmtfx.js → index-aw492hpn.js} +57 -40
  21. package/dist/cli/{index-an922xj4.js → index-bfc4bbq4.js} +7 -7
  22. package/dist/cli/{index-dtmgt2xn.js → index-dvycdkhj.js} +1 -0
  23. package/dist/cli/{index-r5djc1cc.js → index-e9qh1j19.js} +5 -5
  24. package/dist/cli/{index-4sdhbxgq.js → index-f73px21b.js} +1 -1
  25. package/dist/cli/{index-mef0trex.js → index-fkqfcv14.js} +2 -1
  26. package/dist/cli/{index-apwcvfjt.js → index-frp2hwdg.js} +2 -2
  27. package/dist/cli/{index-qqs44wcp.js → index-fybg880e.js} +77 -64
  28. package/dist/cli/{index-n99s022c.js → index-jjpppdvb.js} +1 -1
  29. package/dist/cli/{index-htqs4p52.js → index-kghpgs47.js} +1 -1
  30. package/dist/cli/{index-bhg6brtw.js → index-qa8bmwav.js} +745 -8
  31. package/dist/cli/{index-ma1p1w05.js → index-sdff3rvj.js} +6 -6
  32. package/dist/cli/{index-reknv4f9.js → index-sg48w5e4.js} +2 -2
  33. package/dist/cli/{index-1rpjd5na.js → index-tfkc0zf4.js} +1 -1
  34. package/dist/cli/{index-w9pb72vg.js → index-tp1avc8j.js} +2 -2
  35. package/dist/cli/{index-m8dn0qnm.js → index-txbd25bc.js} +1 -1
  36. package/dist/cli/{index-jb9tb1sd.js → index-v3gshkpz.js} +1 -1
  37. package/dist/cli/{index-8cn6545x.js → index-vradyw0x.js} +30 -30
  38. package/dist/cli/{index-zyv56em5.js → index-vs8wtavs.js} +3 -3
  39. package/dist/cli/{index-1rww5k9e.js → index-wna9f75x.js} +4 -4
  40. package/dist/cli/{index-0tn1kxej.js → index-wxkgy9gh.js} +7 -7
  41. package/dist/cli/{index-1wmd5wn1.js → index-xeptzfb6.js} +4 -4
  42. package/dist/cli/{index-vy6q9d8j.js → index-z4bg1hv8.js} +1 -1
  43. package/dist/cli/{index-g75rbw3e.js → index-zkxnba57.js} +5 -5
  44. package/dist/cli/index.js +28 -28
  45. package/dist/cli/{knowledge-escalator-pc7smwrd.js → knowledge-escalator-a9dwxqz8.js} +11 -11
  46. package/dist/cli/{knowledge-events-jmb3jm84.js → knowledge-events-0q4sb713.js} +9 -9
  47. package/dist/cli/{knowledge-link-mzn8x0qd.js → knowledge-link-v7hj1h8m.js} +5 -5
  48. package/dist/cli/{knowledge-store-wd6zh05h.js → knowledge-store-y2nbt9jk.js} +6 -6
  49. package/dist/cli/{knowledge-validator-2v6ar1wk.js → knowledge-validator-4wr1m0xa.js} +8 -8
  50. package/dist/cli/{pending-delegations-qzy6rnvw.js → pending-delegations-a2nmssr1.js} +3 -3
  51. package/dist/cli/{pr-subscriptions-q862pvz1.js → pr-subscriptions-7879nq5k.js} +3 -3
  52. package/dist/cli/{runner-1aygs98c.js → runner-7znestx4.js} +6 -6
  53. package/dist/cli/{scan-cursor-hr8e3jqj.js → scan-cursor-bsrfrxm6.js} +7 -7
  54. package/dist/cli/{schema-g44y7s0w.js → schema-s6yh3rst.js} +1 -1
  55. package/dist/cli/{scope-persistence-0n2cv4kn.js → scope-persistence-f77w4jrx.js} +8 -8
  56. package/dist/cli/{skill-generator-0gmpxwgk.js → skill-generator-jr7c8fcg.js} +13 -13
  57. package/dist/cli/{telemetry-5deacb7w.js → telemetry-52pw5xac.js} +1 -1
  58. package/dist/cli/{worktree-collision-ownership-9v8e0dz7.js → worktree-collision-ownership-p31zm5k0.js} +3 -3
  59. package/dist/cli/{worktree-isolation-pcr5zv0c.js → worktree-isolation-ka20b0rm.js} +28 -28
  60. package/dist/commands/close.d.ts +1 -0
  61. package/dist/hooks/guardrails/audit-log.d.ts +70 -19
  62. package/dist/hooks/guardrails/helpers.d.ts +16 -1
  63. package/dist/hooks/guardrails/shell-audit-store.d.ts +211 -0
  64. package/dist/hooks/guardrails/tool-before.d.ts +0 -2
  65. package/dist/index.js +337 -317
  66. package/dist/observability/catalog.d.ts +7 -5
  67. package/dist/services/guardrail-log-service.d.ts +4 -0
  68. package/dist/telemetry.d.ts +22 -1
  69. package/package.json +2 -1
  70. package/dist/cli/index-ggs716fj.js +0 -110
@@ -11,10 +11,13 @@ import {
11
11
  import {
12
12
  init_schema,
13
13
  stripKnownSwarmPrefix
14
- } from "./index-mef0trex.js";
14
+ } from "./index-fkqfcv14.js";
15
15
  import {
16
16
  init_constants
17
17
  } from "./index-cpg98bh1.js";
18
+ import {
19
+ telemetry
20
+ } from "./index-aw492hpn.js";
18
21
  import {
19
22
  init_logger,
20
23
  warn
@@ -2588,20 +2591,704 @@ function isInDeclaredScope(filePath, scopeEntries, cwd) {
2588
2591
  function redactShellCommand(cmd) {
2589
2592
  if (typeof cmd !== "string")
2590
2593
  return "";
2591
- let out = cmd.replace(/\/home\/[^/\s"']+/g, "~").replace(/[A-Za-z]:\\Users\\[^\\\s"']+/g, "~").replace(/\/Users\/[^/\s"']+/g, "~");
2592
- out = out.replace(/\b([A-Z_]*(?:TOKEN|SECRET|PASSWORD|PASSWD|API[_]?KEY|APIKEY|AUTH|CREDENTIAL|PRIVATE[_]?KEY|ACCESS[_]?KEY|_KEY)[A-Z_0-9]*)\s*=\s*(\S+)/gi, "$1=[REDACTED]");
2594
+ let out = cmd.replace(/\/home\/[^/\s"']+/g, "~").replace(/[A-Za-z]:\\Users\\[^\\\s"']+/gi, "~").replace(/\/Users\/[^/\s"']+/g, "~").replace(/\\\\[^\\\s"']+\\[^\\\s"']+\\[^\\\s"']+(?:\\[^\s"']*)?/g, "~");
2595
+ out = out.replace(/([a-zA-Z][a-zA-Z0-9+.-]{0,32}:\/\/[^\s"'@/]+):([^@\s"'/]+)@/g, "$1:[REDACTED]@");
2596
+ const VALUE = String.raw`(?:"[^"]*"|'[^']*'|\S+)`;
2597
+ out = out.replace(new RegExp(String.raw`\b([A-Z_]{0,64}(?:TOKEN|SECRET|PASSWORD|PASSWD|API[_]?KEY|APIKEY|AUTH|CREDENTIAL|PRIVATE[_]?KEY|ACCESS[_]?KEY|_KEY)[A-Z_0-9]{0,64})\s*=\s*` + VALUE, "gi"), "$1=[REDACTED]");
2598
+ out = out.replace(new RegExp(String.raw`(\$env:[A-Za-z_][A-Za-z_0-9]{0,64}(?:TOKEN|SECRET|PASSWORD|PASSWD|API[_]?KEY|APIKEY|AUTH|CREDENTIAL|PRIVATE[_]?KEY|ACCESS[_]?KEY|_KEY)[A-Za-z_0-9]{0,64})(\s*=\s*)` + VALUE, "gi"), "$1=[REDACTED]");
2599
+ out = out.replace(new RegExp(String.raw`\b(set|SET)(\s+"?)([A-Za-z_][A-Za-z_0-9]{0,64}(?:TOKEN|SECRET|PASSWORD|PASSWD|API[_]?KEY|APIKEY|AUTH|CREDENTIAL|PRIVATE[_]?KEY|ACCESS[_]?KEY|_KEY)[A-Za-z_0-9]{0,64})(=?)(\s+)` + VALUE, "gi"), "$1$2$3=[REDACTED]");
2593
2600
  out = out.replace(/--([a-zA-Z-]*(?:token|secret|password|passwd|api[_-]?key|apikey|auth|credential|private[_-]?key|access[_-]?key)[a-zA-Z-]*)=(\S+)/gi, "--$1=[REDACTED]");
2594
2601
  out = out.replace(/(--[a-zA-Z-]*(?:token|secret|password|passwd|api[_-]?key|apikey|auth|credential|private[_-]?key|access[_-]?key)[a-zA-Z-]*)(\s+)(?!--)(\S+)/gi, "$1$2[REDACTED]");
2595
2602
  out = out.replace(/\b(Bearer|Basic)\s+[A-Za-z0-9+/=._-]{4,}/gi, "$1 [REDACTED]");
2596
2603
  out = out.replace(/(-H\s+['"]?(?:Authorization|X-API-Key|X-Auth-Token|[A-Za-z][A-Za-z-]*-(?:key|token|secret|auth|credential)):\s*)([^'">\s][^'">\n]*)(['"]?)/gi, "$1[REDACTED]$3");
2604
+ out = out.replace(/\bsk-[A-Za-z0-9_-]{20,}/g, "[REDACTED:openai]").replace(/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}/g, "[REDACTED:github]").replace(/\bgithub_pat_[A-Za-z0-9_]{22,}/g, "[REDACTED:github]").replace(/\bAKIA[0-9A-Z]{16}\b/g, "[REDACTED:aws]").replace(/\bxox[baprs]-[A-Za-z0-9-]{10,}/g, "[REDACTED:slack]").replace(/\bAIza[A-Za-z0-9_-]{30,}/g, "[REDACTED:google]");
2605
+ out = out.replace(/[A-Za-z0-9+/]{80,}={0,2}/g, (run) => /[A-Z]/.test(run) && /[a-z]/.test(run) && /[0-9]/.test(run) ? "[REDACTED:base64]" : run);
2597
2606
  return out;
2598
2607
  }
2599
2608
 
2609
+ // src/hooks/guardrails/shell-audit-store.ts
2610
+ import * as fs from "fs";
2611
+ import * as path2 from "path";
2612
+ import { StringDecoder } from "string_decoder";
2613
+ init_logger();
2614
+ var SHELL_AUDIT_LIMITS = {
2615
+ activeMaxBytes: 1024 * 1024,
2616
+ securityMaxEntries: 4000,
2617
+ allowedMaxEntries: 2000,
2618
+ allowedAgeMaxMs: 72 * 60 * 60 * 1000,
2619
+ compactMaxBytes: 256 * 1024,
2620
+ readMaxBytes: 256 * 1024,
2621
+ checkInterval: 25,
2622
+ warnCooldownMs: 60000,
2623
+ headerMaxBytes: 16 * 1024,
2624
+ maxLineBytes: 64 * 1024,
2625
+ maxCommandChars: 4096,
2626
+ maxReasonChars: 1024,
2627
+ migrationMaxBytes: 8 * 1024 * 1024
2628
+ };
2629
+ function decisionClassOf(event) {
2630
+ return typeof event.type === "string" && event.type.length > 0 ? "security" : "allowed";
2631
+ }
2632
+ function decisionDiscriminator(event) {
2633
+ if (typeof event.type === "string" && event.type.length > 0) {
2634
+ return event.type.slice(0, 64);
2635
+ }
2636
+ return "shell";
2637
+ }
2638
+ function decisionTimestamp(event) {
2639
+ if (typeof event.ts !== "string")
2640
+ return null;
2641
+ const ts = Date.parse(event.ts);
2642
+ return Number.isNaN(ts) ? null : event.ts;
2643
+ }
2644
+ var MANIFEST_TYPE = "swarm-shell-audit-manifest";
2645
+ var MANIFEST_SCHEMA = 1;
2646
+ var BY_TYPE_MAX_KEYS = 16;
2647
+ var BY_TYPE_OTHER = "__other__";
2648
+ function emptyFolded() {
2649
+ return {
2650
+ totalDecisions: 0,
2651
+ byType: {},
2652
+ corrupt: 0,
2653
+ dropped: 0,
2654
+ oldestTimestamp: null,
2655
+ newestTimestamp: null
2656
+ };
2657
+ }
2658
+ function emptyManifest() {
2659
+ return {
2660
+ v: 1,
2661
+ type: MANIFEST_TYPE,
2662
+ schemaVersion: MANIFEST_SCHEMA,
2663
+ folded: emptyFolded(),
2664
+ updatedAt: new Date().toISOString()
2665
+ };
2666
+ }
2667
+ function parseManifestLine(line) {
2668
+ const trimmed = line.trim();
2669
+ if (!trimmed)
2670
+ return null;
2671
+ try {
2672
+ const obj = JSON.parse(trimmed);
2673
+ if (typeof obj !== "object" || obj === null)
2674
+ return null;
2675
+ const rec = obj;
2676
+ if (rec.type !== MANIFEST_TYPE || rec.v !== 1 || rec.schemaVersion !== MANIFEST_SCHEMA || typeof rec.folded !== "object" || rec.folded === null) {
2677
+ return null;
2678
+ }
2679
+ const f = rec.folded;
2680
+ const manifest = emptyManifest();
2681
+ const num = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
2682
+ manifest.folded.totalDecisions = num(f.totalDecisions);
2683
+ manifest.folded.corrupt = num(f.corrupt);
2684
+ manifest.folded.dropped = num(f.dropped);
2685
+ if (typeof f.byType === "object" && f.byType !== null) {
2686
+ for (const [k, v] of Object.entries(f.byType)) {
2687
+ if (typeof v === "number" && Number.isFinite(v) && v >= 0) {
2688
+ manifest.folded.byType[k] = v;
2689
+ }
2690
+ }
2691
+ }
2692
+ if (typeof f.oldestTimestamp === "string") {
2693
+ manifest.folded.oldestTimestamp = f.oldestTimestamp;
2694
+ }
2695
+ if (typeof f.newestTimestamp === "string") {
2696
+ manifest.folded.newestTimestamp = f.newestTimestamp;
2697
+ }
2698
+ manifest.updatedAt = typeof rec.updatedAt === "string" ? rec.updatedAt : new Date().toISOString();
2699
+ return manifest;
2700
+ } catch {
2701
+ return null;
2702
+ }
2703
+ }
2704
+ var _internals = {
2705
+ appendFileSync: fs.appendFileSync,
2706
+ readFileSync: fs.readFileSync,
2707
+ existsSync: fs.existsSync,
2708
+ mkdirSync: fs.mkdirSync,
2709
+ statSync: fs.statSync,
2710
+ renameSync: fs.renameSync,
2711
+ writeFileSync: fs.writeFileSync,
2712
+ unlinkSync: fs.unlinkSync,
2713
+ openSync: fs.openSync,
2714
+ closeSync: fs.closeSync,
2715
+ readSync: fs.readSync,
2716
+ now: () => Date.now(),
2717
+ limits: SHELL_AUDIT_LIMITS,
2718
+ emitHealth: emitShellAuditHealth
2719
+ };
2720
+ function shellAuditFilePath(directory) {
2721
+ return path2.join(directory, ".swarm", "session", "shell-audit.jsonl");
2722
+ }
2723
+ function lockFilePath(directory) {
2724
+ return path2.join(directory, ".swarm", "session", "shell-audit.lock");
2725
+ }
2726
+ function tmpPathFor(finalPath) {
2727
+ return `${finalPath}.${process.pid}.tmp`;
2728
+ }
2729
+ function readBoundedChunk(filePath, maxBytes, offset = 0) {
2730
+ try {
2731
+ if (!_internals.existsSync(filePath)) {
2732
+ return { text: "", truncated: false };
2733
+ }
2734
+ const fd = _internals.openSync(filePath, "r");
2735
+ try {
2736
+ const size = _internals.statSync(filePath).size;
2737
+ const readable = Math.max(0, size - offset);
2738
+ const truncated = readable > maxBytes;
2739
+ const len = Math.min(readable, maxBytes);
2740
+ const buf = Buffer.alloc(len);
2741
+ let read = 0;
2742
+ while (read < len) {
2743
+ const n = _internals.readSync(fd, buf, read, len - read, offset + read);
2744
+ if (n <= 0)
2745
+ break;
2746
+ read += n;
2747
+ }
2748
+ return { text: buf.toString("utf-8", 0, read), truncated };
2749
+ } finally {
2750
+ _internals.closeSync(fd);
2751
+ }
2752
+ } catch {
2753
+ warnThrottled("bounded read failed (transient I/O)");
2754
+ return { text: "", truncated: false };
2755
+ }
2756
+ }
2757
+ function fileSizeOrZero(filePath) {
2758
+ try {
2759
+ if (!_internals.existsSync(filePath))
2760
+ return 0;
2761
+ return _internals.statSync(filePath).size;
2762
+ } catch {
2763
+ return 0;
2764
+ }
2765
+ }
2766
+ function foldLineInto(folded, event) {
2767
+ folded.totalDecisions += 1;
2768
+ const key = decisionDiscriminator(event);
2769
+ const keys = Object.keys(folded.byType);
2770
+ if (folded.byType[key] !== undefined || keys.length < BY_TYPE_MAX_KEYS) {
2771
+ folded.byType[key] = (folded.byType[key] ?? 0) + 1;
2772
+ } else {
2773
+ folded.byType[BY_TYPE_OTHER] = (folded.byType[BY_TYPE_OTHER] ?? 0) + 1;
2774
+ }
2775
+ const ts = decisionTimestamp(event);
2776
+ if (ts !== null) {
2777
+ if (folded.oldestTimestamp === null || ts < folded.oldestTimestamp) {
2778
+ folded.oldestTimestamp = ts;
2779
+ }
2780
+ if (folded.newestTimestamp === null || ts > folded.newestTimestamp) {
2781
+ folded.newestTimestamp = ts;
2782
+ }
2783
+ }
2784
+ }
2785
+ function cloneFolded(folded) {
2786
+ return {
2787
+ totalDecisions: folded.totalDecisions,
2788
+ byType: { ...folded.byType },
2789
+ corrupt: folded.corrupt,
2790
+ dropped: folded.dropped,
2791
+ oldestTimestamp: folded.oldestTimestamp,
2792
+ newestTimestamp: folded.newestTimestamp
2793
+ };
2794
+ }
2795
+ function atomicReplace(directory, content) {
2796
+ const lines = content.split(`
2797
+ `);
2798
+ if (lines.length < 2 || lines[lines.length - 1] !== "") {
2799
+ throw new Error("shell-audit atomic replace failed (framing)");
2800
+ }
2801
+ if (parseManifestLine(lines[0] ?? "") === null) {
2802
+ throw new Error("shell-audit atomic replace failed (manifest)");
2803
+ }
2804
+ for (let i = 1;i < lines.length - 1; i += 1) {
2805
+ const line = lines[i];
2806
+ if (line.trim() === "")
2807
+ continue;
2808
+ JSON.parse(line);
2809
+ }
2810
+ const finalPath = shellAuditFilePath(directory);
2811
+ const tmpPath = tmpPathFor(finalPath);
2812
+ try {
2813
+ if (_internals.existsSync(tmpPath)) {
2814
+ try {
2815
+ _internals.unlinkSync(tmpPath);
2816
+ } catch {}
2817
+ }
2818
+ _internals.writeFileSync(tmpPath, content, "utf-8");
2819
+ if (_internals.readFileSync(tmpPath, "utf-8") !== content) {
2820
+ throw new Error("tmp verify mismatch");
2821
+ }
2822
+ renameSyncWithRetry(tmpPath, finalPath);
2823
+ } catch {
2824
+ try {
2825
+ if (_internals.existsSync(tmpPath)) {
2826
+ _internals.unlinkSync(tmpPath);
2827
+ }
2828
+ } catch {}
2829
+ throw new Error("shell-audit atomic replace failed");
2830
+ }
2831
+ }
2832
+ var LOCK_RETRY_ATTEMPTS = 20;
2833
+ var LOCK_RETRY_DELAY_MS = 5;
2834
+ var LOCK_STALE_MS = 5 * 60000;
2835
+ var sleepScratch = new Int32Array(new SharedArrayBuffer(4));
2836
+ function syncSleep(ms) {
2837
+ try {
2838
+ Atomics.wait(sleepScratch, 0, 0, ms);
2839
+ } catch {
2840
+ const start = _internals.now();
2841
+ while (_internals.now() - start < ms) {}
2842
+ }
2843
+ }
2844
+ var RENAME_RETRY_DELAYS_MS = [10, 25, 50, 100, 200];
2845
+ var RETRYABLE_RENAME_CODES = new Set(["EPERM", "EBUSY", "EACCES", "EEXIST"]);
2846
+ function renameSyncWithRetry(fromPath, toPath) {
2847
+ let lastError;
2848
+ for (let attempt = 0;attempt <= RENAME_RETRY_DELAYS_MS.length; attempt += 1) {
2849
+ try {
2850
+ _internals.renameSync(fromPath, toPath);
2851
+ return;
2852
+ } catch (err) {
2853
+ lastError = err;
2854
+ const code = err?.code;
2855
+ if (!code || !RETRYABLE_RENAME_CODES.has(code))
2856
+ throw err;
2857
+ if (attempt < RENAME_RETRY_DELAYS_MS.length) {
2858
+ syncSleep(RENAME_RETRY_DELAYS_MS[attempt]);
2859
+ }
2860
+ }
2861
+ }
2862
+ throw lastError;
2863
+ }
2864
+ function tryLockOnce(directory) {
2865
+ const lockPath = lockFilePath(directory);
2866
+ try {
2867
+ _internals.mkdirSync(path2.dirname(lockPath), { recursive: true });
2868
+ const fd = _internals.openSync(lockPath, "wx");
2869
+ _internals.closeSync(fd);
2870
+ return true;
2871
+ } catch {
2872
+ try {
2873
+ const age = _internals.now() - _internals.statSync(lockPath).mtimeMs;
2874
+ if (age > LOCK_STALE_MS) {
2875
+ try {
2876
+ _internals.unlinkSync(lockPath);
2877
+ } catch {}
2878
+ try {
2879
+ const fd = _internals.openSync(lockPath, "wx");
2880
+ _internals.closeSync(fd);
2881
+ return true;
2882
+ } catch {
2883
+ return false;
2884
+ }
2885
+ }
2886
+ } catch {}
2887
+ return false;
2888
+ }
2889
+ }
2890
+ function withShellAuditStoreLock(directory, fn) {
2891
+ for (let attempt = 0;attempt < LOCK_RETRY_ATTEMPTS; attempt += 1) {
2892
+ if (tryLockOnce(directory)) {
2893
+ try {
2894
+ return fn();
2895
+ } finally {
2896
+ try {
2897
+ _internals.unlinkSync(lockFilePath(directory));
2898
+ } catch {}
2899
+ }
2900
+ }
2901
+ if (attempt < LOCK_RETRY_ATTEMPTS - 1) {
2902
+ syncSleep(LOCK_RETRY_DELAY_MS);
2903
+ }
2904
+ }
2905
+ return null;
2906
+ }
2907
+ var _lastWarnAt = 0;
2908
+ function warnThrottled(message) {
2909
+ const now = _internals.now();
2910
+ if (now - _lastWarnAt < _internals.limits.warnCooldownMs)
2911
+ return;
2912
+ _lastWarnAt = now;
2913
+ warn(`shell-audit: ${message}`);
2914
+ }
2915
+ var EMPTY_VIEW = {
2916
+ manifest: null,
2917
+ lines: [],
2918
+ corruptLines: 0,
2919
+ prefixFolded: null
2920
+ };
2921
+ function streamLegacyStore(filePath) {
2922
+ const size = fileSizeOrZero(filePath);
2923
+ const prefixFolded = emptyFolded();
2924
+ const retained = [];
2925
+ let retainedBytes = 0;
2926
+ const windowBudget = _internals.limits.activeMaxBytes + _internals.limits.headerMaxBytes;
2927
+ const chunkBytes = Math.min(512 * 1024, Math.max(1 * 1024, _internals.limits.compactMaxBytes * 2));
2928
+ const ingest = (line) => {
2929
+ if (line.trim() === "")
2930
+ return;
2931
+ let event = null;
2932
+ try {
2933
+ const parsed = JSON.parse(line);
2934
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
2935
+ event = parsed;
2936
+ }
2937
+ } catch {}
2938
+ if (event === null) {
2939
+ prefixFolded.corrupt += 1;
2940
+ return;
2941
+ }
2942
+ const lineBytes = Buffer.byteLength(line) + 1;
2943
+ if (retainedBytes + lineBytes > windowBudget && retained.length > 0) {
2944
+ const oldest = retained.shift();
2945
+ retainedBytes -= Buffer.byteLength(oldest.line) + 1;
2946
+ foldLineInto(prefixFolded, oldest.event);
2947
+ }
2948
+ retained.push({ line, event });
2949
+ retainedBytes += lineBytes;
2950
+ };
2951
+ let offset = 0;
2952
+ let carry = "";
2953
+ const decoder = new StringDecoder("utf-8");
2954
+ const fd = _internals.openSync(filePath, "r");
2955
+ try {
2956
+ const buf = Buffer.alloc(chunkBytes);
2957
+ for (;; ) {
2958
+ const n = _internals.readSync(fd, buf, 0, chunkBytes, offset);
2959
+ if (n <= 0)
2960
+ break;
2961
+ offset += n;
2962
+ const decoded = decoder.write(buf.subarray(0, n));
2963
+ const parts = `${carry}${decoded}`.split(`
2964
+ `);
2965
+ carry = parts.pop() ?? "";
2966
+ for (const line of parts)
2967
+ ingest(line);
2968
+ if (offset >= size)
2969
+ break;
2970
+ }
2971
+ } finally {
2972
+ _internals.closeSync(fd);
2973
+ }
2974
+ const residue = decoder.end();
2975
+ if (residue !== "")
2976
+ carry += residue;
2977
+ if (carry.trim() !== "")
2978
+ ingest(carry);
2979
+ return { manifest: null, lines: retained, corruptLines: 0, prefixFolded };
2980
+ }
2981
+ function readStoreFull(directory) {
2982
+ const filePath = shellAuditFilePath(directory);
2983
+ if (!_internals.existsSync(filePath)) {
2984
+ return EMPTY_VIEW;
2985
+ }
2986
+ if (fileSizeOrZero(filePath) > _internals.limits.migrationMaxBytes) {
2987
+ return streamLegacyStore(filePath);
2988
+ }
2989
+ let text;
2990
+ try {
2991
+ text = _internals.readFileSync(filePath, "utf-8");
2992
+ } catch {
2993
+ return EMPTY_VIEW;
2994
+ }
2995
+ const rawLines = text.split(`
2996
+ `);
2997
+ const lines = [];
2998
+ let corruptLines = 0;
2999
+ let manifest = null;
3000
+ for (let i = 0;i < rawLines.length; i += 1) {
3001
+ const line = rawLines[i];
3002
+ if (line.trim() === "")
3003
+ continue;
3004
+ if (i === 0) {
3005
+ manifest = parseManifestLine(line);
3006
+ if (manifest)
3007
+ continue;
3008
+ }
3009
+ try {
3010
+ const parsed = JSON.parse(line);
3011
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
3012
+ lines.push({ line, event: parsed });
3013
+ } else {
3014
+ corruptLines += 1;
3015
+ }
3016
+ } catch {
3017
+ corruptLines += 1;
3018
+ }
3019
+ }
3020
+ return { manifest, lines, corruptLines, prefixFolded: null };
3021
+ }
3022
+ function coverageFor(fileExists, lineCount, truncated) {
3023
+ if (!fileExists || lineCount === 0)
3024
+ return "empty";
3025
+ return truncated ? "truncated" : "complete";
3026
+ }
3027
+ function readShellAuditTail(directory, maxBytes) {
3028
+ const bound = Math.max(1024, Math.min(maxBytes ?? _internals.limits.readMaxBytes, _internals.limits.readMaxBytes));
3029
+ const filePath = shellAuditFilePath(directory);
3030
+ if (!_internals.existsSync(filePath)) {
3031
+ return { text: "", truncated: false, coverage: "empty" };
3032
+ }
3033
+ let size = fileSizeOrZero(filePath);
3034
+ let truncated = size > bound;
3035
+ let offset = truncated ? Math.max(0, size - bound) : 0;
3036
+ let text = readBoundedChunk(filePath, bound, offset).text;
3037
+ if (truncated && text === "") {
3038
+ size = fileSizeOrZero(filePath);
3039
+ truncated = size > bound;
3040
+ offset = truncated ? Math.max(0, size - bound) : 0;
3041
+ text = readBoundedChunk(filePath, bound, offset).text;
3042
+ }
3043
+ if (truncated) {
3044
+ const firstNewline = text.indexOf(`
3045
+ `);
3046
+ text = firstNewline === -1 ? "" : text.slice(firstNewline + 1);
3047
+ }
3048
+ const lines = text.split(`
3049
+ `);
3050
+ const start = parseManifestLine(lines[0] ?? "") !== null ? 1 : 0;
3051
+ if (start === 1) {
3052
+ text = lines.slice(1).join(`
3053
+ `);
3054
+ }
3055
+ const decisionLineCount = text.split(`
3056
+ `).filter((l) => l.trim() !== "").length;
3057
+ const coverage = start === 1 ? truncated ? "truncated" : "complete" : coverageFor(true, decisionLineCount, truncated);
3058
+ return { text, truncated, coverage };
3059
+ }
3060
+ function getShellAuditFoldedSummary(directory) {
3061
+ const filePath = shellAuditFilePath(directory);
3062
+ if (!_internals.existsSync(filePath))
3063
+ return null;
3064
+ const head = readBoundedChunk(filePath, _internals.limits.headerMaxBytes + 64);
3065
+ const manifest = parseManifestLine(head.text.split(`
3066
+ `)[0] ?? "");
3067
+ return manifest ? manifest.folded : null;
3068
+ }
3069
+ function foldPass(directory, trigger, forceFull) {
3070
+ const filePath = shellAuditFilePath(directory);
3071
+ if (!_internals.existsSync(filePath))
3072
+ return;
3073
+ const view = readStoreFull(directory);
3074
+ if (view.manifest === null && view.lines.length === 0 && view.corruptLines === 0 && view.prefixFolded === null) {
3075
+ return;
3076
+ }
3077
+ const folded = view.manifest ? cloneFolded(view.manifest.folded) : emptyFolded();
3078
+ if (view.prefixFolded !== null) {
3079
+ folded.totalDecisions += view.prefixFolded.totalDecisions;
3080
+ for (const [k, v] of Object.entries(view.prefixFolded.byType)) {
3081
+ folded.byType[k] = (folded.byType[k] ?? 0) + v;
3082
+ }
3083
+ folded.corrupt += view.prefixFolded.corrupt;
3084
+ folded.dropped += view.prefixFolded.dropped;
3085
+ if (view.prefixFolded.oldestTimestamp !== null && (folded.oldestTimestamp === null || view.prefixFolded.oldestTimestamp < folded.oldestTimestamp)) {
3086
+ folded.oldestTimestamp = view.prefixFolded.oldestTimestamp;
3087
+ }
3088
+ if (view.prefixFolded.newestTimestamp !== null && (folded.newestTimestamp === null || view.prefixFolded.newestTimestamp > folded.newestTimestamp)) {
3089
+ folded.newestTimestamp = view.prefixFolded.newestTimestamp;
3090
+ }
3091
+ }
3092
+ const now = _internals.now();
3093
+ const agePruned = new Set;
3094
+ const kept = [];
3095
+ for (const entry of view.lines) {
3096
+ if (entry.event === null) {
3097
+ agePruned.add(entry);
3098
+ continue;
3099
+ }
3100
+ if (decisionClassOf(entry.event) === "security") {
3101
+ kept.push(entry);
3102
+ continue;
3103
+ }
3104
+ const ts = decisionTimestamp(entry.event);
3105
+ if (ts !== null && now - Date.parse(ts) > _internals.limits.allowedAgeMaxMs) {
3106
+ agePruned.add(entry);
3107
+ } else {
3108
+ kept.push(entry);
3109
+ }
3110
+ }
3111
+ const retained = [];
3112
+ let securityCount = 0;
3113
+ let allowedCount = 0;
3114
+ let bytes = Buffer.byteLength(JSON.stringify(view.manifest ?? emptyManifest())) + 1;
3115
+ for (let i = kept.length - 1;i >= 0; i -= 1) {
3116
+ const entry = kept[i];
3117
+ const lineBytes = Buffer.byteLength(entry.line) + 1;
3118
+ if (bytes + lineBytes > _internals.limits.activeMaxBytes) {
3119
+ break;
3120
+ }
3121
+ const cls = decisionClassOf(entry.event);
3122
+ if (cls === "security") {
3123
+ if (securityCount >= _internals.limits.securityMaxEntries) {
3124
+ continue;
3125
+ }
3126
+ securityCount += 1;
3127
+ } else {
3128
+ if (allowedCount >= _internals.limits.allowedMaxEntries) {
3129
+ continue;
3130
+ }
3131
+ allowedCount += 1;
3132
+ }
3133
+ retained.push(entry);
3134
+ bytes += lineBytes;
3135
+ }
3136
+ retained.reverse();
3137
+ const retainedSet = new Set(retained);
3138
+ const candidates = [];
3139
+ for (const entry of view.lines) {
3140
+ if (entry.event === null) {
3141
+ candidates.push(entry);
3142
+ } else if (!retainedSet.has(entry)) {
3143
+ candidates.push(entry);
3144
+ }
3145
+ }
3146
+ const toFold = new Set;
3147
+ let foldedBytes = 0;
3148
+ for (const cand of candidates) {
3149
+ const cb = Buffer.byteLength(cand.line) + 1;
3150
+ if (!forceFull && foldedBytes + cb > _internals.limits.compactMaxBytes && toFold.size > 0) {
3151
+ break;
3152
+ }
3153
+ toFold.add(cand);
3154
+ foldedBytes += cb;
3155
+ }
3156
+ let dropped = 0;
3157
+ for (const cand of toFold) {
3158
+ if (cand.event === null) {
3159
+ folded.corrupt += 1;
3160
+ } else {
3161
+ foldLineInto(folded, cand.event);
3162
+ if (agePruned.has(cand))
3163
+ dropped += 1;
3164
+ }
3165
+ }
3166
+ folded.dropped += dropped;
3167
+ folded.corrupt += view.corruptLines;
3168
+ const finalRetained = [];
3169
+ for (const entry of view.lines) {
3170
+ if (!toFold.has(entry))
3171
+ finalRetained.push(entry);
3172
+ }
3173
+ const manifest = {
3174
+ ...emptyManifest(),
3175
+ folded,
3176
+ updatedAt: new Date().toISOString()
3177
+ };
3178
+ const outLines = [`${JSON.stringify(manifest)}`];
3179
+ for (const entry of finalRetained)
3180
+ outLines.push(entry.line);
3181
+ atomicReplace(directory, outLines.length > 1 ? `${outLines.join(`
3182
+ `)}
3183
+ ` : `${outLines[0]}
3184
+ `);
3185
+ emitHealth(directory, {
3186
+ trigger,
3187
+ accepted: folded.totalDecisions + finalRetained.length,
3188
+ compacted: toFold.size - dropped,
3189
+ retained: finalRetained.length,
3190
+ dropped,
3191
+ corrupt: folded.corrupt,
3192
+ oldest: folded.oldestTimestamp,
3193
+ newest: folded.newestTimestamp,
3194
+ bytes: fileSizeOrZero(filePath),
3195
+ limitBytes: _internals.limits.activeMaxBytes
3196
+ });
3197
+ }
3198
+ function finalizeShellAuditForClose(directory, options) {
3199
+ try {
3200
+ const filePath = shellAuditFilePath(directory);
3201
+ if (!_internals.existsSync(filePath))
3202
+ return;
3203
+ withShellAuditStoreLock(directory, () => {
3204
+ let prev = Number.POSITIVE_INFINITY;
3205
+ for (let pass = 0;pass < 1e4; pass += 1) {
3206
+ const view = readStoreFull(directory);
3207
+ if (view.manifest !== null)
3208
+ break;
3209
+ if (view.lines.length === 0)
3210
+ break;
3211
+ if (view.lines.length >= prev)
3212
+ break;
3213
+ prev = view.lines.length;
3214
+ foldPass(directory, "close", false);
3215
+ }
3216
+ foldPass(directory, "close", true);
3217
+ const transform = options?.lineTransform;
3218
+ if (transform !== undefined) {
3219
+ const view = readStoreFull(directory);
3220
+ if (view.manifest !== null) {
3221
+ const manifest = {
3222
+ ...view.manifest,
3223
+ folded: cloneFolded(view.manifest.folded),
3224
+ updatedAt: new Date().toISOString()
3225
+ };
3226
+ const outLines = [`${JSON.stringify(manifest)}`];
3227
+ for (const entry of view.lines)
3228
+ outLines.push(transform(entry.line));
3229
+ try {
3230
+ atomicReplace(directory, `${outLines.join(`
3231
+ `)}
3232
+ `);
3233
+ } catch {
3234
+ warnThrottled("archive re-redaction rewrite failed");
3235
+ }
3236
+ }
3237
+ }
3238
+ return true;
3239
+ });
3240
+ } catch {
3241
+ warnThrottled("finalize failed");
3242
+ }
3243
+ }
3244
+ function emitShellAuditHealth(directory, payload) {
3245
+ try {
3246
+ telemetry.shellAuditHealth({
3247
+ trigger: payload.trigger,
3248
+ accepted_count: payload.accepted,
3249
+ compacted_count: payload.compacted,
3250
+ retained_count: payload.retained,
3251
+ dropped_count: payload.dropped,
3252
+ corrupt_count: payload.corrupt,
3253
+ oldest_timestamp: payload.oldest,
3254
+ newest_timestamp: payload.newest,
3255
+ bytes: payload.bytes,
3256
+ limit_bytes: payload.limitBytes
3257
+ });
3258
+ } catch {}
3259
+ }
3260
+ function emitHealth(directory, payload) {
3261
+ _internals.emitHealth(directory, payload);
3262
+ }
3263
+
2600
3264
  // src/hooks/guardrails/audit-log.ts
2601
3265
  init_logger();
2602
- import * as path2 from "path";
3266
+ import { createHash } from "crypto";
3267
+ import * as path3 from "path";
3268
+ var SHELL_AUDIT_FIELD_CLASSES = Object.freeze({
3269
+ ts: "timestamp",
3270
+ sessionID: "identifier",
3271
+ agent: "identifier",
3272
+ tool: "identifier",
3273
+ type: "decision-type",
3274
+ command: "redacted-command",
3275
+ commandHash: "content-hash",
3276
+ path: "redacted-path",
3277
+ reason: "free-text-redacted",
3278
+ resolvedScope: "redacted-path",
3279
+ declaredScope: "redacted-path",
3280
+ action: "enum",
3281
+ destructiveCategory: "enum",
3282
+ executorMechanism: "enum",
3283
+ skipReason: "enum"
3284
+ });
3285
+ function hashRedactedCommand(redacted) {
3286
+ return createHash("sha256").update(redacted, "utf-8").digest("hex").slice(0, 16);
3287
+ }
2603
3288
  function redactPath(filePath) {
2604
- if (typeof filePath !== "string" || filePath.length === 0)
3289
+ if (typeof filePath !== "string")
3290
+ return "";
3291
+ if (filePath.length === 0)
2605
3292
  return filePath;
2606
3293
  const rawPosixHomeMatch = filePath.match(/^(\/home\/[^/]+)(\/.*)$/);
2607
3294
  if (rawPosixHomeMatch) {
@@ -2611,8 +3298,8 @@ function redactPath(filePath) {
2611
3298
  if (rawMacHomeMatch) {
2612
3299
  return `~${rawMacHomeMatch[2]}`;
2613
3300
  }
2614
- const normalized = path2.normalize(filePath);
2615
- const windowsHomeMatch = normalized.match(/^([A-Za-z]:\\Users\\[^\\]+)(\\.*)$/);
3301
+ const normalized = path3.normalize(filePath);
3302
+ const windowsHomeMatch = normalized.match(/^([A-Za-z]:\\[Uu][Ss][Ee][Rr][Ss]\\[^\\]+)(\\.*)$/i);
2616
3303
  if (windowsHomeMatch) {
2617
3304
  return `~\\${windowsHomeMatch[2]}`;
2618
3305
  }
@@ -2622,5 +3309,55 @@ function redactPath(filePath) {
2622
3309
  }
2623
3310
  return normalized;
2624
3311
  }
3312
+ function redactEmbeddedPaths(text) {
3313
+ return text.replace(/\/home\/[^/\s]+(\/[^\s"'`)]*)?/g, (match) => redactPath(match)).replace(/\/Users\/[^/\s]+(\/[^\s"'`)]*)?/g, (match) => redactPath(match)).replace(/[A-Za-z]:\\Users\\[^\\\s]+(\\[^\s"'`)]*)?/gi, (match) => redactPath(match)).replace(/\\\\[^\\\s]+\\[^\\\s]+\\[^\\\s]+(\\[^\s"'`)]*)?/g, (match) => redactPath(match));
3314
+ }
3315
+ function redactDecisionLineForArchive(line) {
3316
+ let parsed;
3317
+ try {
3318
+ parsed = JSON.parse(line);
3319
+ } catch {
3320
+ return line;
3321
+ }
3322
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
3323
+ return line;
3324
+ }
3325
+ const record = parsed;
3326
+ let changed = false;
3327
+ if (typeof record.command === "string") {
3328
+ const redacted = redactShellCommand(record.command);
3329
+ if (redacted !== record.command) {
3330
+ record.command = redacted;
3331
+ changed = true;
3332
+ if (typeof record.commandHash === "string") {
3333
+ record.commandHash = hashRedactedCommand(redacted);
3334
+ }
3335
+ }
3336
+ }
3337
+ if (typeof record.path === "string") {
3338
+ const redacted = redactPath(record.path);
3339
+ if (redacted !== record.path) {
3340
+ record.path = redacted;
3341
+ changed = true;
3342
+ }
3343
+ }
3344
+ if (typeof record.reason === "string") {
3345
+ const redacted = redactEmbeddedPaths(record.reason);
3346
+ if (redacted !== record.reason) {
3347
+ record.reason = redacted;
3348
+ changed = true;
3349
+ }
3350
+ }
3351
+ for (const field of ["declaredScope", "resolvedScope"]) {
3352
+ if (typeof record[field] === "string") {
3353
+ const redacted = redactPath(record[field]);
3354
+ if (redacted !== record[field]) {
3355
+ record[field] = redacted;
3356
+ changed = true;
3357
+ }
3358
+ }
3359
+ }
3360
+ return changed ? JSON.stringify(record) : line;
3361
+ }
2625
3362
 
2626
- export { classifyFile, stableCanonicalStringify, checkFileAuthority, normalizeToolNameLowerCase, isInDeclaredScope, redactShellCommand, redactPath };
3363
+ export { classifyFile, stableCanonicalStringify, checkFileAuthority, normalizeToolNameLowerCase, isInDeclaredScope, redactShellCommand, readShellAuditTail, getShellAuditFoldedSummary, finalizeShellAuditForClose, redactPath, redactDecisionLineForArchive };