agent-inspect 6.31.3 → 6.31.5

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 +14 -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 +449 -91
  10. package/packages/cli/dist/index.cjs.map +1 -1
  11. package/packages/cli/dist/index.mjs +357 -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 +242 -6
  38. package/packages/core/dist/exporters.cjs.map +1 -1
  39. package/packages/core/dist/exporters.mjs +143 -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.5";
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,124 @@ 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 CONNECTION_PROTOCOLS = /* @__PURE__ */ new Set([
1766
+ "postgres:",
1767
+ "postgresql:",
1768
+ "mysql:",
1769
+ "mariadb:",
1770
+ "mongodb:",
1771
+ "mongodb+srv:",
1772
+ "redis:",
1773
+ "rediss:",
1774
+ "amqp:",
1775
+ "amqps:"
1776
+ ]);
1777
+ var CONNECTION_URI = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
1778
+ var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1779
+ var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
1780
+ var NUMERIC_SEGMENT = /^\d{5,}$/;
1781
+ var OPAQUE_SEGMENT = /^[A-Za-z0-9_-]{20,}$/;
1782
+ function looksLikeHttpUrl(value) {
1783
+ return HTTP_URL.test(value.trim());
1784
+ }
1785
+ function looksLikeConnectionUri(value) {
1786
+ return CONNECTION_URI.test(value.trim());
1787
+ }
1788
+ function looksLikeUrlForRedaction(value) {
1789
+ return looksLikeHttpUrl(value) || looksLikeConnectionUri(value);
1790
+ }
1791
+ function parseRedactableUrl(value) {
1792
+ try {
1793
+ const url = new URL(value.trim());
1794
+ if (url.protocol === "http:" || url.protocol === "https:") return url;
1795
+ if (CONNECTION_PROTOCOLS.has(url.protocol)) return url;
1796
+ return void 0;
1797
+ } catch {
1798
+ return void 0;
1799
+ }
1800
+ }
1801
+ function isHttpProtocol(protocol) {
1802
+ return protocol === "http:" || protocol === "https:";
1803
+ }
1804
+ function isIdentifierSegment(segment) {
1805
+ if (UUID_SEGMENT.test(segment)) return true;
1806
+ if (HEX_SEGMENT.test(segment)) return true;
1807
+ if (NUMERIC_SEGMENT.test(segment)) return true;
1808
+ return OPAQUE_SEGMENT.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
1809
+ }
1810
+ function decodeParam(value) {
1811
+ try {
1812
+ return decodeURIComponent(value.replace(/\+/g, " "));
1813
+ } catch {
1814
+ return value;
1815
+ }
1816
+ }
1817
+ function redactParams(raw, options, replacement) {
1818
+ let redacted = false;
1819
+ const parts = raw.split("&").map((part) => {
1820
+ if (part.length === 0) return part;
1821
+ const separator = part.indexOf("=");
1822
+ if (separator < 0) return part;
1823
+ const rawKey = part.slice(0, separator);
1824
+ const rawValue = part.slice(separator + 1);
1825
+ const key = decodeParam(rawKey);
1826
+ const value = decodeParam(rawValue);
1827
+ if (isCredentialSensitiveKey(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
1828
+ redacted = true;
1829
+ return `${rawKey}=${replacement}`;
1830
+ }
1831
+ return part;
1832
+ });
1833
+ return { value: parts.join("&"), redacted };
1834
+ }
1835
+ function redactUrlString(value, options) {
1836
+ const unchanged = {
1837
+ value,
1838
+ credentialRedacted: false,
1839
+ identifiersMasked: false
1840
+ };
1841
+ if (!looksLikeUrlForRedaction(value)) return unchanged;
1842
+ const url = parseRedactableUrl(value);
1843
+ if (!url) return unchanged;
1844
+ const replacement = options.replacement ?? "[REDACTED]";
1845
+ const idPlaceholder = options.idPlaceholder ?? "[id]";
1846
+ const http = isHttpProtocol(url.protocol);
1847
+ let credentialRedacted = url.username.length > 0 || url.password.length > 0;
1848
+ let identifiersMasked = false;
1849
+ let pathname = url.pathname;
1850
+ if (http && options.maskPathIds === true) {
1851
+ pathname = url.pathname.split("/").map((segment) => {
1852
+ if (segment.length === 0 || !isIdentifierSegment(decodeParam(segment))) {
1853
+ return segment;
1854
+ }
1855
+ identifiersMasked = true;
1856
+ return idPlaceholder;
1857
+ }).join("/");
1858
+ }
1859
+ let search = "";
1860
+ if (url.search.length > 1) {
1861
+ const params = redactParams(url.search.slice(1), options, replacement);
1862
+ if (params.redacted) credentialRedacted = true;
1863
+ search = `?${params.value}`;
1864
+ } else if (url.search.length === 1) {
1865
+ search = "?";
1866
+ }
1867
+ let hash = url.hash;
1868
+ if (url.hash.includes("=")) {
1869
+ const params = redactParams(url.hash.slice(1), options, replacement);
1870
+ if (params.redacted) credentialRedacted = true;
1871
+ hash = `#${params.value}`;
1872
+ }
1873
+ if (!credentialRedacted && !identifiersMasked) return unchanged;
1874
+ return {
1875
+ value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
1876
+ credentialRedacted,
1877
+ identifiersMasked
1878
+ };
1879
+ }
1880
+
1760
1881
  // packages/core/src/exporters/redact-export.ts
1761
1882
  function isRecord4(value) {
1762
1883
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -1774,21 +1895,34 @@ function deepClone(value) {
1774
1895
  }
1775
1896
  return out;
1776
1897
  }
1777
- function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
1898
+ function boundAttributeValues(record, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
1778
1899
  if (depth > 32) {
1779
1900
  return { truncated: true, reason: "maxDepth" };
1780
1901
  }
1781
1902
  const out = {};
1782
1903
  for (const [key, value] of Object.entries(record)) {
1783
- out[key] = boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth);
1904
+ out[key] = boundValue(
1905
+ value,
1906
+ key,
1907
+ sensitiveKeys,
1908
+ maxMetadataValueLength,
1909
+ maxPreviewLength,
1910
+ seen,
1911
+ depth
1912
+ );
1784
1913
  }
1785
1914
  return out;
1786
1915
  }
1787
- function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
1916
+ function boundValue(value, key, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
1788
1917
  if (value === null || typeof value !== "object") {
1789
1918
  if (typeof value === "string") {
1919
+ const rewritten = redactUrlString(value, {
1920
+ sensitiveKeys,
1921
+ isSensitiveValue: stringContainsHighConfidenceCredential,
1922
+ maskPathIds: true
1923
+ }).value;
1790
1924
  return truncateStringForProfile(
1791
- value,
1925
+ stringContainsHighConfidenceCredential(rewritten) ? "[REDACTED]" : rewritten,
1792
1926
  key,
1793
1927
  maxMetadataValueLength,
1794
1928
  maxPreviewLength
@@ -1803,6 +1937,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
1803
1937
  (item, index) => boundValue(
1804
1938
  item,
1805
1939
  String(index),
1940
+ sensitiveKeys,
1806
1941
  maxMetadataValueLength,
1807
1942
  maxPreviewLength,
1808
1943
  seen,
@@ -1812,6 +1947,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
1812
1947
  }
1813
1948
  return boundAttributeValues(
1814
1949
  value,
1950
+ sensitiveKeys,
1815
1951
  maxMetadataValueLength,
1816
1952
  maxPreviewLength,
1817
1953
  seen,
@@ -1826,6 +1962,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
1826
1962
  const seen = /* @__PURE__ */ new WeakSet();
1827
1963
  const bounded = boundAttributeValues(
1828
1964
  redacted,
1965
+ [...DEFAULT_REDACT_KEYS, ...redactor.sensitiveKeys],
1829
1966
  maxMetadataValueLength,
1830
1967
  maxPreviewLength,
1831
1968
  seen,
@@ -3402,7 +3539,7 @@ async function newestTraceMtimeMs(traceDir) {
3402
3539
  }
3403
3540
  async function loadSessionRuns(traceDir) {
3404
3541
  try {
3405
- const mod = await import('./src-YNEJOPRA.mjs');
3542
+ const mod = await import('./src-4WYF5RFI.mjs');
3406
3543
  const dbPath = mod.resolveIndexDbPath(traceDir);
3407
3544
  const status = mod.indexStatus(dbPath);
3408
3545
  if (!status.healthy) {
@@ -3904,7 +4041,7 @@ function isTokenCredentialKey(normalized) {
3904
4041
  if (normalized.endsWith("tokens")) return false;
3905
4042
  return normalized.endsWith("token");
3906
4043
  }
3907
- function isCredentialSensitiveKey(key, sensitiveKeys) {
4044
+ function isCredentialSensitiveKey2(key, sensitiveKeys) {
3908
4045
  if (!key) return false;
3909
4046
  const normalized = normalizeSensitiveKey(key);
3910
4047
  if (!normalized) return false;
@@ -3925,7 +4062,7 @@ function isCredentialSensitiveKey(key, sensitiveKeys) {
3925
4062
  }
3926
4063
 
3927
4064
  // 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,})`;
4065
+ 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
4066
  var KEY_VALUE_SECRET_PATTERN = new RegExp(
3930
4067
  KEY_VALUE_SECRET_PATTERN_SOURCE,
3931
4068
  "i"
@@ -3935,6 +4072,124 @@ function valueContainsKeyValueSecret(value) {
3935
4072
  return KEY_VALUE_SECRET_PATTERN.test(value);
3936
4073
  }
3937
4074
 
4075
+ // packages/redact/src/url-redaction.ts
4076
+ var HTTP_URL2 = /^https?:\/\/[^\s]+$/i;
4077
+ var CONNECTION_PROTOCOLS2 = /* @__PURE__ */ new Set([
4078
+ "postgres:",
4079
+ "postgresql:",
4080
+ "mysql:",
4081
+ "mariadb:",
4082
+ "mongodb:",
4083
+ "mongodb+srv:",
4084
+ "redis:",
4085
+ "rediss:",
4086
+ "amqp:",
4087
+ "amqps:"
4088
+ ]);
4089
+ var CONNECTION_URI2 = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
4090
+ var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4091
+ var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
4092
+ var NUMERIC_SEGMENT2 = /^\d{5,}$/;
4093
+ var OPAQUE_SEGMENT2 = /^[A-Za-z0-9_-]{20,}$/;
4094
+ function looksLikeHttpUrl2(value) {
4095
+ return HTTP_URL2.test(value.trim());
4096
+ }
4097
+ function looksLikeConnectionUri2(value) {
4098
+ return CONNECTION_URI2.test(value.trim());
4099
+ }
4100
+ function looksLikeUrlForRedaction2(value) {
4101
+ return looksLikeHttpUrl2(value) || looksLikeConnectionUri2(value);
4102
+ }
4103
+ function parseRedactableUrl2(value) {
4104
+ try {
4105
+ const url = new URL(value.trim());
4106
+ if (url.protocol === "http:" || url.protocol === "https:") return url;
4107
+ if (CONNECTION_PROTOCOLS2.has(url.protocol)) return url;
4108
+ return void 0;
4109
+ } catch {
4110
+ return void 0;
4111
+ }
4112
+ }
4113
+ function isHttpProtocol2(protocol) {
4114
+ return protocol === "http:" || protocol === "https:";
4115
+ }
4116
+ function isIdentifierSegment2(segment) {
4117
+ if (UUID_SEGMENT2.test(segment)) return true;
4118
+ if (HEX_SEGMENT2.test(segment)) return true;
4119
+ if (NUMERIC_SEGMENT2.test(segment)) return true;
4120
+ return OPAQUE_SEGMENT2.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
4121
+ }
4122
+ function decodeParam2(value) {
4123
+ try {
4124
+ return decodeURIComponent(value.replace(/\+/g, " "));
4125
+ } catch {
4126
+ return value;
4127
+ }
4128
+ }
4129
+ function redactParams2(raw, options, replacement) {
4130
+ let redacted = false;
4131
+ const parts = raw.split("&").map((part) => {
4132
+ if (part.length === 0) return part;
4133
+ const separator = part.indexOf("=");
4134
+ if (separator < 0) return part;
4135
+ const rawKey = part.slice(0, separator);
4136
+ const rawValue = part.slice(separator + 1);
4137
+ const key = decodeParam2(rawKey);
4138
+ const value = decodeParam2(rawValue);
4139
+ if (isCredentialSensitiveKey2(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
4140
+ redacted = true;
4141
+ return `${rawKey}=${replacement}`;
4142
+ }
4143
+ return part;
4144
+ });
4145
+ return { value: parts.join("&"), redacted };
4146
+ }
4147
+ function redactUrlString2(value, options) {
4148
+ const unchanged = {
4149
+ value,
4150
+ credentialRedacted: false,
4151
+ identifiersMasked: false
4152
+ };
4153
+ if (!looksLikeUrlForRedaction2(value)) return unchanged;
4154
+ const url = parseRedactableUrl2(value);
4155
+ if (!url) return unchanged;
4156
+ const replacement = options.replacement ?? "[REDACTED]";
4157
+ const idPlaceholder = options.idPlaceholder ?? "[id]";
4158
+ const http = isHttpProtocol2(url.protocol);
4159
+ let credentialRedacted = url.username.length > 0 || url.password.length > 0;
4160
+ let identifiersMasked = false;
4161
+ let pathname = url.pathname;
4162
+ if (http && options.maskPathIds === true) {
4163
+ pathname = url.pathname.split("/").map((segment) => {
4164
+ if (segment.length === 0 || !isIdentifierSegment2(decodeParam2(segment))) {
4165
+ return segment;
4166
+ }
4167
+ identifiersMasked = true;
4168
+ return idPlaceholder;
4169
+ }).join("/");
4170
+ }
4171
+ let search = "";
4172
+ if (url.search.length > 1) {
4173
+ const params = redactParams2(url.search.slice(1), options, replacement);
4174
+ if (params.redacted) credentialRedacted = true;
4175
+ search = `?${params.value}`;
4176
+ } else if (url.search.length === 1) {
4177
+ search = "?";
4178
+ }
4179
+ let hash = url.hash;
4180
+ if (url.hash.includes("=")) {
4181
+ const params = redactParams2(url.hash.slice(1), options, replacement);
4182
+ if (params.redacted) credentialRedacted = true;
4183
+ hash = `#${params.value}`;
4184
+ }
4185
+ if (!credentialRedacted && !identifiersMasked) return unchanged;
4186
+ return {
4187
+ value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
4188
+ credentialRedacted,
4189
+ identifiersMasked
4190
+ };
4191
+ }
4192
+
3938
4193
  // packages/redact/src/index.ts
3939
4194
  var DEFAULT_REDACT_KEYS2 = [
3940
4195
  "authorization",
@@ -4001,7 +4256,9 @@ function findCompiledKeyRule(key, rules) {
4001
4256
  const exact = toKey(key);
4002
4257
  const direct = rules.find((candidate) => candidate.key === exact);
4003
4258
  if (direct) return direct;
4004
- return rules.find((candidate) => isCredentialSensitiveKey(key, [candidate.key]));
4259
+ return rules.find(
4260
+ (candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
4261
+ );
4005
4262
  }
4006
4263
  function stableHash(value) {
4007
4264
  const hash = crypto.createHash("sha256").update(value, "utf8").digest("hex");
@@ -4200,7 +4457,7 @@ function compileRules(rules, extraKeys) {
4200
4457
  const out = /* @__PURE__ */ new Map();
4201
4458
  const set = (rule) => {
4202
4459
  const key = toKey(rule.key);
4203
- out.set(key, { ...rule, key });
4460
+ out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
4204
4461
  };
4205
4462
  for (const key of DEFAULT_REDACT_KEYS2) {
4206
4463
  set({ key, strategy: "full" });
@@ -4279,6 +4536,7 @@ function createRedactionProfile(profile = "local") {
4279
4536
  }
4280
4537
  var Redactor2 = class {
4281
4538
  #rules;
4539
+ #sensitiveKeys;
4282
4540
  #detectors;
4283
4541
  #profile;
4284
4542
  #replacement;
@@ -4291,6 +4549,7 @@ var Redactor2 = class {
4291
4549
  ...resolved.extraKeys,
4292
4550
  ...options?.extraKeys ?? []
4293
4551
  ]);
4552
+ this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
4294
4553
  this.#detectors = [
4295
4554
  ...builtInDetectorsForProfile(this.#profile),
4296
4555
  ...options?.detectors ?? []
@@ -4324,6 +4583,14 @@ var Redactor2 = class {
4324
4583
  #recordFinding(state, finding) {
4325
4584
  if (this.#collectFindings) state.findings.push(finding);
4326
4585
  }
4586
+ #detect(value, key, path30) {
4587
+ for (const detector of this.#detectors) {
4588
+ for (const detection of detector.detect({ path: path30, key, value })) {
4589
+ if ((detection.action ?? "replace") !== "keep") return { detector, detection };
4590
+ }
4591
+ }
4592
+ return void 0;
4593
+ }
4327
4594
  #redactValue(value, key, path30, depth, state) {
4328
4595
  if (depth > this.#maxDepth) {
4329
4596
  this.#recordFinding(
@@ -4342,6 +4609,43 @@ var Redactor2 = class {
4342
4609
  return applyRule(rule, value, this.#replacement);
4343
4610
  }
4344
4611
  }
4612
+ if (typeof value === "string" && looksLikeUrlForRedaction2(value)) {
4613
+ const url = redactUrlString2(value, {
4614
+ sensitiveKeys: this.#sensitiveKeys,
4615
+ isSensitiveValue: (candidate) => this.#detect(candidate, key, path30) !== void 0,
4616
+ replacement: this.#replacement,
4617
+ maskPathIds: this.#profile !== "local"
4618
+ });
4619
+ if (url.value !== value) {
4620
+ this.#recordFinding(
4621
+ state,
4622
+ makeFinding(
4623
+ path30,
4624
+ url.credentialRedacted ? "value.urlCredential" : "value.urlIdentifier",
4625
+ "replace",
4626
+ "value",
4627
+ url.credentialRedacted ? "error" : "warning"
4628
+ )
4629
+ );
4630
+ const residual = this.#detect(
4631
+ url.value.split(this.#replacement).join(""),
4632
+ key,
4633
+ path30
4634
+ );
4635
+ if (residual === void 0) return url.value;
4636
+ this.#recordFinding(
4637
+ state,
4638
+ makeFinding(
4639
+ path30,
4640
+ residual.detector.id,
4641
+ "replace",
4642
+ residual.detection.matchKind ?? residual.detector.matchKind ?? "custom",
4643
+ residual.detection.severity ?? residual.detector.severity ?? "warning"
4644
+ )
4645
+ );
4646
+ return this.#replacement;
4647
+ }
4648
+ }
4345
4649
  for (const detector of this.#detectors) {
4346
4650
  const detections = detector.detect({ path: path30, key, value });
4347
4651
  for (const detection of detections) {
@@ -12320,7 +12624,7 @@ function envCheck(name, optional = true) {
12320
12624
  async function traceDirWritable(traceDir) {
12321
12625
  const resolved = path19.resolve(traceDir);
12322
12626
  try {
12323
- await mkdir(resolved, { recursive: true });
12627
+ await mkdir(resolved, { recursive: true, mode: 448 });
12324
12628
  await access(resolved, constants.W_OK);
12325
12629
  return {
12326
12630
  id: "trace-dir-writable",
@@ -12338,6 +12642,44 @@ async function traceDirWritable(traceDir) {
12338
12642
  };
12339
12643
  }
12340
12644
  }
12645
+ async function traceDirPermissions(traceDir) {
12646
+ const resolved = path19.resolve(traceDir);
12647
+ if (process2.platform === "win32") {
12648
+ return {
12649
+ id: "trace-dir-permissions",
12650
+ status: "skipped",
12651
+ message: "POSIX confidentiality mode check skipped on Windows.",
12652
+ evidence: resolved
12653
+ };
12654
+ }
12655
+ try {
12656
+ const st = await stat(resolved);
12657
+ const mode = st.mode & 511;
12658
+ const octal = mode.toString(8).padStart(3, "0");
12659
+ if ((mode & 63) !== 0) {
12660
+ return {
12661
+ id: "trace-dir-permissions",
12662
+ status: "warn",
12663
+ message: `Trace directory allows group/other access (mode ${octal}): ${resolved}`,
12664
+ 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`,
12665
+ evidence: `mode=${octal}`
12666
+ };
12667
+ }
12668
+ return {
12669
+ id: "trace-dir-permissions",
12670
+ status: "pass",
12671
+ message: `Trace directory mode is restrictive (${octal}): ${resolved}`,
12672
+ evidence: `mode=${octal}`
12673
+ };
12674
+ } catch (error) {
12675
+ return {
12676
+ id: "trace-dir-permissions",
12677
+ status: "skipped",
12678
+ message: `Could not inspect trace directory permissions: ${resolved}`,
12679
+ evidence: error instanceof Error ? error.message : String(error)
12680
+ };
12681
+ }
12682
+ }
12341
12683
  function readPackageVersionNearEntry(entryPath, packageName) {
12342
12684
  let dir = path19.dirname(path19.resolve(entryPath));
12343
12685
  const { root } = path19.parse(dir);
@@ -12469,6 +12811,7 @@ async function runDoctorChecks(options = {}) {
12469
12811
  evidence: version
12470
12812
  },
12471
12813
  await traceDirWritable(traceDir),
12814
+ await traceDirPermissions(traceDir),
12472
12815
  envCheck("AGENT_INSPECT"),
12473
12816
  envCheck("AGENT_INSPECT_TRACE_DIR"),
12474
12817
  versionMismatchCheck(cwd)
@@ -12996,7 +13339,7 @@ function isModuleNotFound4(e) {
12996
13339
  }
12997
13340
  async function loadIndexSqlite() {
12998
13341
  try {
12999
- return await import('./src-YNEJOPRA.mjs');
13342
+ return await import('./src-4WYF5RFI.mjs');
13000
13343
  } catch (e) {
13001
13344
  if (isModuleNotFound4(e)) {
13002
13345
  console.error(