agent-inspect 6.31.3 → 6.31.4

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 (60) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -1
  3. package/SECURITY.md +2 -0
  4. package/docs/GETTING-STARTED.md +2 -0
  5. package/docs/SAFE-TRACE-SHARING.md +1 -0
  6. package/package.json +1 -1
  7. package/packages/cli/dist/{chunk-SJ6FRNOE.mjs → chunk-LKEPABND.mjs} +86 -80
  8. package/packages/cli/dist/chunk-LKEPABND.mjs.map +1 -0
  9. package/packages/cli/dist/index.cjs +399 -91
  10. package/packages/cli/dist/index.cjs.map +1 -1
  11. package/packages/cli/dist/index.mjs +307 -14
  12. package/packages/cli/dist/index.mjs.map +1 -1
  13. package/packages/cli/dist/{src-YNEJOPRA.mjs → src-4WYF5RFI.mjs} +3 -3
  14. package/packages/cli/dist/{src-YNEJOPRA.mjs.map → src-4WYF5RFI.mjs.map} +1 -1
  15. package/packages/core/dist/advanced.cjs +97 -30
  16. package/packages/core/dist/advanced.cjs.map +1 -1
  17. package/packages/core/dist/advanced.mjs +13 -13
  18. package/packages/core/dist/checks.cjs +51 -49
  19. package/packages/core/dist/checks.cjs.map +1 -1
  20. package/packages/core/dist/checks.mjs +5 -5
  21. package/packages/core/dist/{chunk-PBNBQRXV.mjs → chunk-2KJR6X4V.mjs} +9 -9
  22. package/packages/core/dist/chunk-2KJR6X4V.mjs.map +1 -0
  23. package/packages/core/dist/{chunk-26YJSETN.mjs → chunk-7DHSWDKB.mjs} +19 -6
  24. package/packages/core/dist/chunk-7DHSWDKB.mjs.map +1 -0
  25. package/packages/core/dist/{chunk-BGF5JDJN.mjs → chunk-INE5IWLN.mjs} +3 -3
  26. package/packages/core/dist/{chunk-BGF5JDJN.mjs.map → chunk-INE5IWLN.mjs.map} +1 -1
  27. package/packages/core/dist/{chunk-ZFJ2ICTC.mjs → chunk-MTMTXTJL.mjs} +5 -5
  28. package/packages/core/dist/{chunk-ZFJ2ICTC.mjs.map → chunk-MTMTXTJL.mjs.map} +1 -1
  29. package/packages/core/dist/{chunk-KWBO36PH.mjs → chunk-NY4ILMXK.mjs} +4 -4
  30. package/packages/core/dist/{chunk-KWBO36PH.mjs.map → chunk-NY4ILMXK.mjs.map} +1 -1
  31. package/packages/core/dist/{chunk-WN5PT4GV.mjs → chunk-OSGNGRHF.mjs} +3 -3
  32. package/packages/core/dist/{chunk-WN5PT4GV.mjs.map → chunk-OSGNGRHF.mjs.map} +1 -1
  33. package/packages/core/dist/{chunk-CBPWLHNQ.mjs → chunk-PKDBFBJ7.mjs} +5 -76
  34. package/packages/core/dist/chunk-PKDBFBJ7.mjs.map +1 -0
  35. package/packages/core/dist/{chunk-VU6O5QAH.mjs → chunk-VLVCLAPB.mjs} +83 -5
  36. package/packages/core/dist/chunk-VLVCLAPB.mjs.map +1 -0
  37. package/packages/core/dist/exporters.cjs +217 -6
  38. package/packages/core/dist/exporters.cjs.map +1 -1
  39. package/packages/core/dist/exporters.mjs +118 -9
  40. package/packages/core/dist/exporters.mjs.map +1 -1
  41. package/packages/core/dist/index.cjs +97 -8
  42. package/packages/core/dist/index.cjs.map +1 -1
  43. package/packages/core/dist/index.mjs +6 -6
  44. package/packages/core/dist/logs.cjs +80 -2
  45. package/packages/core/dist/logs.cjs.map +1 -1
  46. package/packages/core/dist/logs.d.cts +2 -0
  47. package/packages/core/dist/logs.d.ts +2 -0
  48. package/packages/core/dist/logs.mjs +2 -2
  49. package/packages/core/dist/readers.cjs +2 -0
  50. package/packages/core/dist/readers.cjs.map +1 -1
  51. package/packages/core/dist/readers.mjs +2 -2
  52. package/packages/core/dist/writers.cjs +96 -8
  53. package/packages/core/dist/writers.cjs.map +1 -1
  54. package/packages/core/dist/writers.mjs +7 -8
  55. package/packages/core/dist/writers.mjs.map +1 -1
  56. package/packages/cli/dist/chunk-SJ6FRNOE.mjs.map +0 -1
  57. package/packages/core/dist/chunk-26YJSETN.mjs.map +0 -1
  58. package/packages/core/dist/chunk-CBPWLHNQ.mjs.map +0 -1
  59. package/packages/core/dist/chunk-PBNBQRXV.mjs.map +0 -1
  60. package/packages/core/dist/chunk-VU6O5QAH.mjs.map +0 -1
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { ATTRIBUTION_CONFIDENCES, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createSafetyOversizedAttributeRule, resolveTraceDir, parseDuration, buildSessionIndex, resolveBundleRunIds, getTraceFilePath, openTrace, sha256Hex, collectTraceSchemaVersions, aggregateBundleSafeStatus, bundleFailsOnSafety, bundleRunAssetRelativePath, buildPlaceholderArtifact, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceHtmlShell, buildEvidenceProvenanceViewHtml, EVIDENCE_FORMAT_VERSION, inferEvidenceFileRole, buildEvidenceSafetyViewHtml, buildEvidenceDiffViewHtml, buildEvidenceCircuitViewHtml, buildEvidenceOutcomesViewHtml, buildEvidenceContractsViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceCausalFailureViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceTreeViewHtml, buildEvidenceManifest, serializeEvidenceManifest, buildZipArchive, assertBundlePathContained, enrichSessionRunRecord, normalizeBundleOutputPath, sanitizeBundleRunId, defaultBundleOutputPath, runTraceChecks, assertEvidenceRelativePath, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, TraceReadError, resolveRedactionProfile, applyProfileMetadataCaps, Redactor, flattenTree, escapeHtml, safeString, compactAttributes, stableJson, escapeMarkdown, truncateStringForProfile, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, renderErrorLine, isAgentInspectTrace, defineTraceContract, filterMetasBySessionScope, evaluateTraceContract, aggregateSessionCheckResults, createBaselineRegressionRule, summarizeSemanticParity, buildEvidenceCiPackage, verifyEvidenceDirectory, diffTraceEvents, renderRunDiff, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, formatStepLabel, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, resolveSuiteTemplate, defaultSuiteConfigTemplate, loadSuiteConfig, validateSuiteConfig, runSuite, renderSuiteReport, analyzeCohort, renderCohortReport, gateHasThresholds, runGate, renderGateReport, persistedInspectEventsToTraceEvents, renderStepLine, getIndent, TreeBuilder, zeroKinds, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, projectLogicalEvents, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, isAttributionConfidence, diffRuns, manualTraceEventsToComparableRun } from './chunk-SJ6FRNOE.mjs';
2
+ import { ATTRIBUTION_CONFIDENCES, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createSafetyOversizedAttributeRule, resolveTraceDir, parseDuration, buildSessionIndex, resolveBundleRunIds, getTraceFilePath, openTrace, sha256Hex, collectTraceSchemaVersions, aggregateBundleSafeStatus, bundleFailsOnSafety, bundleRunAssetRelativePath, buildPlaceholderArtifact, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceHtmlShell, buildEvidenceProvenanceViewHtml, EVIDENCE_FORMAT_VERSION, inferEvidenceFileRole, buildEvidenceSafetyViewHtml, buildEvidenceDiffViewHtml, buildEvidenceCircuitViewHtml, buildEvidenceOutcomesViewHtml, buildEvidenceContractsViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceCausalFailureViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceTreeViewHtml, buildEvidenceManifest, serializeEvidenceManifest, buildZipArchive, assertBundlePathContained, enrichSessionRunRecord, normalizeBundleOutputPath, sanitizeBundleRunId, defaultBundleOutputPath, runTraceChecks, assertEvidenceRelativePath, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, TraceReadError, resolveRedactionProfile, applyProfileMetadataCaps, Redactor, flattenTree, escapeHtml, safeString, compactAttributes, stableJson, escapeMarkdown, DEFAULT_REDACT_KEYS, truncateStringForProfile, stringContainsHighConfidenceCredential, isCredentialSensitiveKey, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, renderErrorLine, isAgentInspectTrace, defineTraceContract, filterMetasBySessionScope, evaluateTraceContract, aggregateSessionCheckResults, createBaselineRegressionRule, summarizeSemanticParity, buildEvidenceCiPackage, verifyEvidenceDirectory, diffTraceEvents, renderRunDiff, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, formatStepLabel, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, resolveSuiteTemplate, defaultSuiteConfigTemplate, loadSuiteConfig, validateSuiteConfig, runSuite, renderSuiteReport, analyzeCohort, renderCohortReport, gateHasThresholds, runGate, renderGateReport, persistedInspectEventsToTraceEvents, renderStepLine, getIndent, TreeBuilder, zeroKinds, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, projectLogicalEvents, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, isAttributionConfidence, diffRuns, manualTraceEventsToComparableRun } from './chunk-LKEPABND.mjs';
3
3
  import { realpathSync, constants as constants$1, existsSync, readFileSync } from 'fs';
4
4
  import path19 from 'path';
5
5
  import { fileURLToPath, pathToFileURL } from 'url';
6
6
  import { Command, Option } from 'commander';
7
7
  import { readFile, stat, mkdir, writeFile, unlink, appendFile, access, rm, open, readdir, lstat, constants } from 'fs/promises';
8
8
  import process2, { stdin, stdout } from 'process';
9
+ import { StringDecoder } from 'string_decoder';
9
10
  import crypto, { createHash } from 'crypto';
10
11
  import { execFile, spawn } from 'child_process';
11
12
  import { promisify } from 'util';
@@ -13,7 +14,7 @@ import { createServer } from 'http';
13
14
  import { createRequire } from 'module';
14
15
 
15
16
  // package.json
16
- var version = "6.31.3";
17
+ var version = "6.31.4";
17
18
 
18
19
  // packages/cli/src/trace-dir-scale.ts
19
20
  var TRACE_COUNT_WARN = 1e3;
@@ -1576,6 +1577,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
1576
1577
  await readFileOnce(filePath, onLine);
1577
1578
  return;
1578
1579
  }
1580
+ let decoder = new StringDecoder("utf-8");
1579
1581
  pos = st.size;
1580
1582
  while (!shouldStop()) {
1581
1583
  let next;
@@ -1589,6 +1591,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
1589
1591
  pos = 0;
1590
1592
  carry = "";
1591
1593
  lineNumber = 0;
1594
+ decoder = new StringDecoder("utf-8");
1592
1595
  }
1593
1596
  if (next.size > pos) {
1594
1597
  const fh = await open(filePath, "r");
@@ -1597,7 +1600,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
1597
1600
  const buf = Buffer.allocUnsafe(Number(len));
1598
1601
  const { bytesRead } = await fh.read(buf, 0, buf.length, pos);
1599
1602
  pos += bytesRead;
1600
- const chunk = carry + buf.toString("utf-8", 0, bytesRead);
1603
+ const chunk = carry + decoder.write(buf.subarray(0, bytesRead));
1601
1604
  const parts = chunk.split(/\r?\n/);
1602
1605
  carry = parts.pop() ?? "";
1603
1606
  for (const p of parts) {
@@ -1757,6 +1760,99 @@ async function tail(options = {}) {
1757
1760
  // packages/core/src/exporters/types.ts
1758
1761
  var EXPORT_PAYLOAD_VERSION = "0.1.2";
1759
1762
 
1763
+ // packages/core/src/safety/url-redaction.ts
1764
+ var HTTP_URL = /^https?:\/\/[^\s]+$/i;
1765
+ var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1766
+ var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
1767
+ var NUMERIC_SEGMENT = /^\d{5,}$/;
1768
+ var OPAQUE_SEGMENT = /^[A-Za-z0-9_-]{20,}$/;
1769
+ function looksLikeHttpUrl(value) {
1770
+ return HTTP_URL.test(value.trim());
1771
+ }
1772
+ function parseHttpUrl(value) {
1773
+ try {
1774
+ const url = new URL(value.trim());
1775
+ return url.protocol === "http:" || url.protocol === "https:" ? url : void 0;
1776
+ } catch {
1777
+ return void 0;
1778
+ }
1779
+ }
1780
+ function isIdentifierSegment(segment) {
1781
+ if (UUID_SEGMENT.test(segment)) return true;
1782
+ if (HEX_SEGMENT.test(segment)) return true;
1783
+ if (NUMERIC_SEGMENT.test(segment)) return true;
1784
+ return OPAQUE_SEGMENT.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
1785
+ }
1786
+ function decodeParam(value) {
1787
+ try {
1788
+ return decodeURIComponent(value.replace(/\+/g, " "));
1789
+ } catch {
1790
+ return value;
1791
+ }
1792
+ }
1793
+ function redactParams(raw, options, replacement) {
1794
+ let redacted = false;
1795
+ const parts = raw.split("&").map((part) => {
1796
+ if (part.length === 0) return part;
1797
+ const separator = part.indexOf("=");
1798
+ if (separator < 0) return part;
1799
+ const rawKey = part.slice(0, separator);
1800
+ const rawValue = part.slice(separator + 1);
1801
+ const key = decodeParam(rawKey);
1802
+ const value = decodeParam(rawValue);
1803
+ if (isCredentialSensitiveKey(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
1804
+ redacted = true;
1805
+ return `${rawKey}=${replacement}`;
1806
+ }
1807
+ return part;
1808
+ });
1809
+ return { value: parts.join("&"), redacted };
1810
+ }
1811
+ function redactUrlString(value, options) {
1812
+ const unchanged = {
1813
+ value,
1814
+ credentialRedacted: false,
1815
+ identifiersMasked: false
1816
+ };
1817
+ if (!looksLikeHttpUrl(value)) return unchanged;
1818
+ const url = parseHttpUrl(value);
1819
+ if (!url) return unchanged;
1820
+ const replacement = options.replacement ?? "[REDACTED]";
1821
+ const idPlaceholder = options.idPlaceholder ?? "[id]";
1822
+ let credentialRedacted = url.username.length > 0 || url.password.length > 0;
1823
+ let identifiersMasked = false;
1824
+ let pathname = url.pathname;
1825
+ if (options.maskPathIds === true) {
1826
+ pathname = url.pathname.split("/").map((segment) => {
1827
+ if (segment.length === 0 || !isIdentifierSegment(decodeParam(segment))) {
1828
+ return segment;
1829
+ }
1830
+ identifiersMasked = true;
1831
+ return idPlaceholder;
1832
+ }).join("/");
1833
+ }
1834
+ let search = "";
1835
+ if (url.search.length > 1) {
1836
+ const params = redactParams(url.search.slice(1), options, replacement);
1837
+ if (params.redacted) credentialRedacted = true;
1838
+ search = `?${params.value}`;
1839
+ } else if (url.search.length === 1) {
1840
+ search = "?";
1841
+ }
1842
+ let hash = url.hash;
1843
+ if (url.hash.includes("=")) {
1844
+ const params = redactParams(url.hash.slice(1), options, replacement);
1845
+ if (params.redacted) credentialRedacted = true;
1846
+ hash = `#${params.value}`;
1847
+ }
1848
+ if (!credentialRedacted && !identifiersMasked) return unchanged;
1849
+ return {
1850
+ value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
1851
+ credentialRedacted,
1852
+ identifiersMasked
1853
+ };
1854
+ }
1855
+
1760
1856
  // packages/core/src/exporters/redact-export.ts
1761
1857
  function isRecord4(value) {
1762
1858
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1774,21 +1870,34 @@ function deepClone(value) {
1774
1870
  }
1775
1871
  return out;
1776
1872
  }
1777
- function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
1873
+ function boundAttributeValues(record, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
1778
1874
  if (depth > 32) {
1779
1875
  return { truncated: true, reason: "maxDepth" };
1780
1876
  }
1781
1877
  const out = {};
1782
1878
  for (const [key, value] of Object.entries(record)) {
1783
- out[key] = boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth);
1879
+ out[key] = boundValue(
1880
+ value,
1881
+ key,
1882
+ sensitiveKeys,
1883
+ maxMetadataValueLength,
1884
+ maxPreviewLength,
1885
+ seen,
1886
+ depth
1887
+ );
1784
1888
  }
1785
1889
  return out;
1786
1890
  }
1787
- function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
1891
+ function boundValue(value, key, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
1788
1892
  if (value === null || typeof value !== "object") {
1789
1893
  if (typeof value === "string") {
1894
+ const rewritten = redactUrlString(value, {
1895
+ sensitiveKeys,
1896
+ isSensitiveValue: stringContainsHighConfidenceCredential,
1897
+ maskPathIds: true
1898
+ }).value;
1790
1899
  return truncateStringForProfile(
1791
- value,
1900
+ stringContainsHighConfidenceCredential(rewritten) ? "[REDACTED]" : rewritten,
1792
1901
  key,
1793
1902
  maxMetadataValueLength,
1794
1903
  maxPreviewLength
@@ -1803,6 +1912,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
1803
1912
  (item, index) => boundValue(
1804
1913
  item,
1805
1914
  String(index),
1915
+ sensitiveKeys,
1806
1916
  maxMetadataValueLength,
1807
1917
  maxPreviewLength,
1808
1918
  seen,
@@ -1812,6 +1922,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
1812
1922
  }
1813
1923
  return boundAttributeValues(
1814
1924
  value,
1925
+ sensitiveKeys,
1815
1926
  maxMetadataValueLength,
1816
1927
  maxPreviewLength,
1817
1928
  seen,
@@ -1826,6 +1937,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
1826
1937
  const seen = /* @__PURE__ */ new WeakSet();
1827
1938
  const bounded = boundAttributeValues(
1828
1939
  redacted,
1940
+ [...DEFAULT_REDACT_KEYS, ...redactor.sensitiveKeys],
1829
1941
  maxMetadataValueLength,
1830
1942
  maxPreviewLength,
1831
1943
  seen,
@@ -3402,7 +3514,7 @@ async function newestTraceMtimeMs(traceDir) {
3402
3514
  }
3403
3515
  async function loadSessionRuns(traceDir) {
3404
3516
  try {
3405
- const mod = await import('./src-YNEJOPRA.mjs');
3517
+ const mod = await import('./src-4WYF5RFI.mjs');
3406
3518
  const dbPath = mod.resolveIndexDbPath(traceDir);
3407
3519
  const status = mod.indexStatus(dbPath);
3408
3520
  if (!status.healthy) {
@@ -3904,7 +4016,7 @@ function isTokenCredentialKey(normalized) {
3904
4016
  if (normalized.endsWith("tokens")) return false;
3905
4017
  return normalized.endsWith("token");
3906
4018
  }
3907
- function isCredentialSensitiveKey(key, sensitiveKeys) {
4019
+ function isCredentialSensitiveKey2(key, sensitiveKeys) {
3908
4020
  if (!key) return false;
3909
4021
  const normalized = normalizeSensitiveKey(key);
3910
4022
  if (!normalized) return false;
@@ -3925,7 +4037,7 @@ function isCredentialSensitiveKey(key, sensitiveKeys) {
3925
4037
  }
3926
4038
 
3927
4039
  // packages/redact/src/key-value-secret.ts
3928
- var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})`;
4040
+ var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})`;
3929
4041
  var KEY_VALUE_SECRET_PATTERN = new RegExp(
3930
4042
  KEY_VALUE_SECRET_PATTERN_SOURCE,
3931
4043
  "i"
@@ -3935,6 +4047,99 @@ function valueContainsKeyValueSecret(value) {
3935
4047
  return KEY_VALUE_SECRET_PATTERN.test(value);
3936
4048
  }
3937
4049
 
4050
+ // packages/redact/src/url-redaction.ts
4051
+ var HTTP_URL2 = /^https?:\/\/[^\s]+$/i;
4052
+ var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4053
+ var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
4054
+ var NUMERIC_SEGMENT2 = /^\d{5,}$/;
4055
+ var OPAQUE_SEGMENT2 = /^[A-Za-z0-9_-]{20,}$/;
4056
+ function looksLikeHttpUrl2(value) {
4057
+ return HTTP_URL2.test(value.trim());
4058
+ }
4059
+ function parseHttpUrl2(value) {
4060
+ try {
4061
+ const url = new URL(value.trim());
4062
+ return url.protocol === "http:" || url.protocol === "https:" ? url : void 0;
4063
+ } catch {
4064
+ return void 0;
4065
+ }
4066
+ }
4067
+ function isIdentifierSegment2(segment) {
4068
+ if (UUID_SEGMENT2.test(segment)) return true;
4069
+ if (HEX_SEGMENT2.test(segment)) return true;
4070
+ if (NUMERIC_SEGMENT2.test(segment)) return true;
4071
+ return OPAQUE_SEGMENT2.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
4072
+ }
4073
+ function decodeParam2(value) {
4074
+ try {
4075
+ return decodeURIComponent(value.replace(/\+/g, " "));
4076
+ } catch {
4077
+ return value;
4078
+ }
4079
+ }
4080
+ function redactParams2(raw, options, replacement) {
4081
+ let redacted = false;
4082
+ const parts = raw.split("&").map((part) => {
4083
+ if (part.length === 0) return part;
4084
+ const separator = part.indexOf("=");
4085
+ if (separator < 0) return part;
4086
+ const rawKey = part.slice(0, separator);
4087
+ const rawValue = part.slice(separator + 1);
4088
+ const key = decodeParam2(rawKey);
4089
+ const value = decodeParam2(rawValue);
4090
+ if (isCredentialSensitiveKey2(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
4091
+ redacted = true;
4092
+ return `${rawKey}=${replacement}`;
4093
+ }
4094
+ return part;
4095
+ });
4096
+ return { value: parts.join("&"), redacted };
4097
+ }
4098
+ function redactUrlString2(value, options) {
4099
+ const unchanged = {
4100
+ value,
4101
+ credentialRedacted: false,
4102
+ identifiersMasked: false
4103
+ };
4104
+ if (!looksLikeHttpUrl2(value)) return unchanged;
4105
+ const url = parseHttpUrl2(value);
4106
+ if (!url) return unchanged;
4107
+ const replacement = options.replacement ?? "[REDACTED]";
4108
+ const idPlaceholder = options.idPlaceholder ?? "[id]";
4109
+ let credentialRedacted = url.username.length > 0 || url.password.length > 0;
4110
+ let identifiersMasked = false;
4111
+ let pathname = url.pathname;
4112
+ if (options.maskPathIds === true) {
4113
+ pathname = url.pathname.split("/").map((segment) => {
4114
+ if (segment.length === 0 || !isIdentifierSegment2(decodeParam2(segment))) {
4115
+ return segment;
4116
+ }
4117
+ identifiersMasked = true;
4118
+ return idPlaceholder;
4119
+ }).join("/");
4120
+ }
4121
+ let search = "";
4122
+ if (url.search.length > 1) {
4123
+ const params = redactParams2(url.search.slice(1), options, replacement);
4124
+ if (params.redacted) credentialRedacted = true;
4125
+ search = `?${params.value}`;
4126
+ } else if (url.search.length === 1) {
4127
+ search = "?";
4128
+ }
4129
+ let hash = url.hash;
4130
+ if (url.hash.includes("=")) {
4131
+ const params = redactParams2(url.hash.slice(1), options, replacement);
4132
+ if (params.redacted) credentialRedacted = true;
4133
+ hash = `#${params.value}`;
4134
+ }
4135
+ if (!credentialRedacted && !identifiersMasked) return unchanged;
4136
+ return {
4137
+ value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
4138
+ credentialRedacted,
4139
+ identifiersMasked
4140
+ };
4141
+ }
4142
+
3938
4143
  // packages/redact/src/index.ts
3939
4144
  var DEFAULT_REDACT_KEYS2 = [
3940
4145
  "authorization",
@@ -4001,7 +4206,9 @@ function findCompiledKeyRule(key, rules) {
4001
4206
  const exact = toKey(key);
4002
4207
  const direct = rules.find((candidate) => candidate.key === exact);
4003
4208
  if (direct) return direct;
4004
- return rules.find((candidate) => isCredentialSensitiveKey(key, [candidate.key]));
4209
+ return rules.find(
4210
+ (candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
4211
+ );
4005
4212
  }
4006
4213
  function stableHash(value) {
4007
4214
  const hash = crypto.createHash("sha256").update(value, "utf8").digest("hex");
@@ -4200,7 +4407,7 @@ function compileRules(rules, extraKeys) {
4200
4407
  const out = /* @__PURE__ */ new Map();
4201
4408
  const set = (rule) => {
4202
4409
  const key = toKey(rule.key);
4203
- out.set(key, { ...rule, key });
4410
+ out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
4204
4411
  };
4205
4412
  for (const key of DEFAULT_REDACT_KEYS2) {
4206
4413
  set({ key, strategy: "full" });
@@ -4279,6 +4486,7 @@ function createRedactionProfile(profile = "local") {
4279
4486
  }
4280
4487
  var Redactor2 = class {
4281
4488
  #rules;
4489
+ #sensitiveKeys;
4282
4490
  #detectors;
4283
4491
  #profile;
4284
4492
  #replacement;
@@ -4291,6 +4499,7 @@ var Redactor2 = class {
4291
4499
  ...resolved.extraKeys,
4292
4500
  ...options?.extraKeys ?? []
4293
4501
  ]);
4502
+ this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
4294
4503
  this.#detectors = [
4295
4504
  ...builtInDetectorsForProfile(this.#profile),
4296
4505
  ...options?.detectors ?? []
@@ -4324,6 +4533,14 @@ var Redactor2 = class {
4324
4533
  #recordFinding(state, finding) {
4325
4534
  if (this.#collectFindings) state.findings.push(finding);
4326
4535
  }
4536
+ #detect(value, key, path30) {
4537
+ for (const detector of this.#detectors) {
4538
+ for (const detection of detector.detect({ path: path30, key, value })) {
4539
+ if ((detection.action ?? "replace") !== "keep") return { detector, detection };
4540
+ }
4541
+ }
4542
+ return void 0;
4543
+ }
4327
4544
  #redactValue(value, key, path30, depth, state) {
4328
4545
  if (depth > this.#maxDepth) {
4329
4546
  this.#recordFinding(
@@ -4342,6 +4559,43 @@ var Redactor2 = class {
4342
4559
  return applyRule(rule, value, this.#replacement);
4343
4560
  }
4344
4561
  }
4562
+ if (typeof value === "string" && looksLikeHttpUrl2(value)) {
4563
+ const url = redactUrlString2(value, {
4564
+ sensitiveKeys: this.#sensitiveKeys,
4565
+ isSensitiveValue: (candidate) => this.#detect(candidate, key, path30) !== void 0,
4566
+ replacement: this.#replacement,
4567
+ maskPathIds: this.#profile !== "local"
4568
+ });
4569
+ if (url.value !== value) {
4570
+ this.#recordFinding(
4571
+ state,
4572
+ makeFinding(
4573
+ path30,
4574
+ url.credentialRedacted ? "value.urlCredential" : "value.urlIdentifier",
4575
+ "replace",
4576
+ "value",
4577
+ url.credentialRedacted ? "error" : "warning"
4578
+ )
4579
+ );
4580
+ const residual = this.#detect(
4581
+ url.value.split(this.#replacement).join(""),
4582
+ key,
4583
+ path30
4584
+ );
4585
+ if (residual === void 0) return url.value;
4586
+ this.#recordFinding(
4587
+ state,
4588
+ makeFinding(
4589
+ path30,
4590
+ residual.detector.id,
4591
+ "replace",
4592
+ residual.detection.matchKind ?? residual.detector.matchKind ?? "custom",
4593
+ residual.detection.severity ?? residual.detector.severity ?? "warning"
4594
+ )
4595
+ );
4596
+ return this.#replacement;
4597
+ }
4598
+ }
4345
4599
  for (const detector of this.#detectors) {
4346
4600
  const detections = detector.detect({ path: path30, key, value });
4347
4601
  for (const detection of detections) {
@@ -12320,7 +12574,7 @@ function envCheck(name, optional = true) {
12320
12574
  async function traceDirWritable(traceDir) {
12321
12575
  const resolved = path19.resolve(traceDir);
12322
12576
  try {
12323
- await mkdir(resolved, { recursive: true });
12577
+ await mkdir(resolved, { recursive: true, mode: 448 });
12324
12578
  await access(resolved, constants.W_OK);
12325
12579
  return {
12326
12580
  id: "trace-dir-writable",
@@ -12338,6 +12592,44 @@ async function traceDirWritable(traceDir) {
12338
12592
  };
12339
12593
  }
12340
12594
  }
12595
+ async function traceDirPermissions(traceDir) {
12596
+ const resolved = path19.resolve(traceDir);
12597
+ if (process2.platform === "win32") {
12598
+ return {
12599
+ id: "trace-dir-permissions",
12600
+ status: "skipped",
12601
+ message: "POSIX confidentiality mode check skipped on Windows.",
12602
+ evidence: resolved
12603
+ };
12604
+ }
12605
+ try {
12606
+ const st = await stat(resolved);
12607
+ const mode = st.mode & 511;
12608
+ const octal = mode.toString(8).padStart(3, "0");
12609
+ if ((mode & 63) !== 0) {
12610
+ return {
12611
+ id: "trace-dir-permissions",
12612
+ status: "warn",
12613
+ message: `Trace directory allows group/other access (mode ${octal}): ${resolved}`,
12614
+ remediation: `Restrict with chmod 700 on the directory and review existing JSONL files with chmod 600. AgentInspect does not rewrite existing modes. See ${DOCS_BASE}/GETTING-STARTED.md`,
12615
+ evidence: `mode=${octal}`
12616
+ };
12617
+ }
12618
+ return {
12619
+ id: "trace-dir-permissions",
12620
+ status: "pass",
12621
+ message: `Trace directory mode is restrictive (${octal}): ${resolved}`,
12622
+ evidence: `mode=${octal}`
12623
+ };
12624
+ } catch (error) {
12625
+ return {
12626
+ id: "trace-dir-permissions",
12627
+ status: "skipped",
12628
+ message: `Could not inspect trace directory permissions: ${resolved}`,
12629
+ evidence: error instanceof Error ? error.message : String(error)
12630
+ };
12631
+ }
12632
+ }
12341
12633
  function readPackageVersionNearEntry(entryPath, packageName) {
12342
12634
  let dir = path19.dirname(path19.resolve(entryPath));
12343
12635
  const { root } = path19.parse(dir);
@@ -12469,6 +12761,7 @@ async function runDoctorChecks(options = {}) {
12469
12761
  evidence: version
12470
12762
  },
12471
12763
  await traceDirWritable(traceDir),
12764
+ await traceDirPermissions(traceDir),
12472
12765
  envCheck("AGENT_INSPECT"),
12473
12766
  envCheck("AGENT_INSPECT_TRACE_DIR"),
12474
12767
  versionMismatchCheck(cwd)
@@ -12996,7 +13289,7 @@ function isModuleNotFound4(e) {
12996
13289
  }
12997
13290
  async function loadIndexSqlite() {
12998
13291
  try {
12999
- return await import('./src-YNEJOPRA.mjs');
13292
+ return await import('./src-4WYF5RFI.mjs');
13000
13293
  } catch (e) {
13001
13294
  if (isModuleNotFound4(e)) {
13002
13295
  console.error(