agent-inspect 6.31.2 → 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 +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 +406 -93
  10. package/packages/cli/dist/index.cjs.map +1 -1
  11. package/packages/cli/dist/index.mjs +314 -16
  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 +224 -8
  38. package/packages/core/dist/exporters.cjs.map +1 -1
  39. package/packages/core/dist/exporters.mjs +125 -11
  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.2";
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,
@@ -2396,6 +2508,11 @@ function exportOpenInference(tree, options) {
2396
2508
  function hexFrom2(seed, byteLen) {
2397
2509
  return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
2398
2510
  }
2511
+ function unixNano2(ms) {
2512
+ const whole = Math.trunc(ms);
2513
+ const fractionNs = Math.round((ms - whole) * 1e6);
2514
+ return BigInt(whole) * 1000000n + BigInt(fractionNs);
2515
+ }
2399
2516
  function stringAttr(key, value) {
2400
2517
  return { key, value: { stringValue: value } };
2401
2518
  }
@@ -2429,10 +2546,10 @@ function exportOtlpJson(tree, options) {
2429
2546
  const ev = n.event;
2430
2547
  const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
2431
2548
  const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
2432
- const startNs = String(Math.round(ev.timestamp * 1e6));
2549
+ const startNs = String(unixNano2(ev.timestamp));
2433
2550
  let endNs;
2434
2551
  if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
2435
- endNs = String(Math.round(ev.timestamp * 1e6 + ev.durationMs * 1e6));
2552
+ endNs = String(unixNano2(ev.timestamp) + unixNano2(ev.durationMs));
2436
2553
  }
2437
2554
  const attrs = [
2438
2555
  stringAttr("agent_inspect.kind", ev.kind),
@@ -3397,7 +3514,7 @@ async function newestTraceMtimeMs(traceDir) {
3397
3514
  }
3398
3515
  async function loadSessionRuns(traceDir) {
3399
3516
  try {
3400
- const mod = await import('./src-YNEJOPRA.mjs');
3517
+ const mod = await import('./src-4WYF5RFI.mjs');
3401
3518
  const dbPath = mod.resolveIndexDbPath(traceDir);
3402
3519
  const status = mod.indexStatus(dbPath);
3403
3520
  if (!status.healthy) {
@@ -3899,7 +4016,7 @@ function isTokenCredentialKey(normalized) {
3899
4016
  if (normalized.endsWith("tokens")) return false;
3900
4017
  return normalized.endsWith("token");
3901
4018
  }
3902
- function isCredentialSensitiveKey(key, sensitiveKeys) {
4019
+ function isCredentialSensitiveKey2(key, sensitiveKeys) {
3903
4020
  if (!key) return false;
3904
4021
  const normalized = normalizeSensitiveKey(key);
3905
4022
  if (!normalized) return false;
@@ -3920,7 +4037,7 @@ function isCredentialSensitiveKey(key, sensitiveKeys) {
3920
4037
  }
3921
4038
 
3922
4039
  // packages/redact/src/key-value-secret.ts
3923
- 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,})`;
3924
4041
  var KEY_VALUE_SECRET_PATTERN = new RegExp(
3925
4042
  KEY_VALUE_SECRET_PATTERN_SOURCE,
3926
4043
  "i"
@@ -3930,6 +4047,99 @@ function valueContainsKeyValueSecret(value) {
3930
4047
  return KEY_VALUE_SECRET_PATTERN.test(value);
3931
4048
  }
3932
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
+
3933
4143
  // packages/redact/src/index.ts
3934
4144
  var DEFAULT_REDACT_KEYS2 = [
3935
4145
  "authorization",
@@ -3996,7 +4206,9 @@ function findCompiledKeyRule(key, rules) {
3996
4206
  const exact = toKey(key);
3997
4207
  const direct = rules.find((candidate) => candidate.key === exact);
3998
4208
  if (direct) return direct;
3999
- return rules.find((candidate) => isCredentialSensitiveKey(key, [candidate.key]));
4209
+ return rules.find(
4210
+ (candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
4211
+ );
4000
4212
  }
4001
4213
  function stableHash(value) {
4002
4214
  const hash = crypto.createHash("sha256").update(value, "utf8").digest("hex");
@@ -4195,7 +4407,7 @@ function compileRules(rules, extraKeys) {
4195
4407
  const out = /* @__PURE__ */ new Map();
4196
4408
  const set = (rule) => {
4197
4409
  const key = toKey(rule.key);
4198
- out.set(key, { ...rule, key });
4410
+ out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
4199
4411
  };
4200
4412
  for (const key of DEFAULT_REDACT_KEYS2) {
4201
4413
  set({ key, strategy: "full" });
@@ -4274,6 +4486,7 @@ function createRedactionProfile(profile = "local") {
4274
4486
  }
4275
4487
  var Redactor2 = class {
4276
4488
  #rules;
4489
+ #sensitiveKeys;
4277
4490
  #detectors;
4278
4491
  #profile;
4279
4492
  #replacement;
@@ -4286,6 +4499,7 @@ var Redactor2 = class {
4286
4499
  ...resolved.extraKeys,
4287
4500
  ...options?.extraKeys ?? []
4288
4501
  ]);
4502
+ this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
4289
4503
  this.#detectors = [
4290
4504
  ...builtInDetectorsForProfile(this.#profile),
4291
4505
  ...options?.detectors ?? []
@@ -4319,6 +4533,14 @@ var Redactor2 = class {
4319
4533
  #recordFinding(state, finding) {
4320
4534
  if (this.#collectFindings) state.findings.push(finding);
4321
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
+ }
4322
4544
  #redactValue(value, key, path30, depth, state) {
4323
4545
  if (depth > this.#maxDepth) {
4324
4546
  this.#recordFinding(
@@ -4337,6 +4559,43 @@ var Redactor2 = class {
4337
4559
  return applyRule(rule, value, this.#replacement);
4338
4560
  }
4339
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
+ }
4340
4599
  for (const detector of this.#detectors) {
4341
4600
  const detections = detector.detect({ path: path30, key, value });
4342
4601
  for (const detection of detections) {
@@ -12315,7 +12574,7 @@ function envCheck(name, optional = true) {
12315
12574
  async function traceDirWritable(traceDir) {
12316
12575
  const resolved = path19.resolve(traceDir);
12317
12576
  try {
12318
- await mkdir(resolved, { recursive: true });
12577
+ await mkdir(resolved, { recursive: true, mode: 448 });
12319
12578
  await access(resolved, constants.W_OK);
12320
12579
  return {
12321
12580
  id: "trace-dir-writable",
@@ -12333,6 +12592,44 @@ async function traceDirWritable(traceDir) {
12333
12592
  };
12334
12593
  }
12335
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
+ }
12336
12633
  function readPackageVersionNearEntry(entryPath, packageName) {
12337
12634
  let dir = path19.dirname(path19.resolve(entryPath));
12338
12635
  const { root } = path19.parse(dir);
@@ -12464,6 +12761,7 @@ async function runDoctorChecks(options = {}) {
12464
12761
  evidence: version
12465
12762
  },
12466
12763
  await traceDirWritable(traceDir),
12764
+ await traceDirPermissions(traceDir),
12467
12765
  envCheck("AGENT_INSPECT"),
12468
12766
  envCheck("AGENT_INSPECT_TRACE_DIR"),
12469
12767
  versionMismatchCheck(cwd)
@@ -12991,7 +13289,7 @@ function isModuleNotFound4(e) {
12991
13289
  }
12992
13290
  async function loadIndexSqlite() {
12993
13291
  try {
12994
- return await import('./src-YNEJOPRA.mjs');
13292
+ return await import('./src-4WYF5RFI.mjs');
12995
13293
  } catch (e) {
12996
13294
  if (isModuleNotFound4(e)) {
12997
13295
  console.error(