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.
- package/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/SECURITY.md +2 -0
- package/docs/GETTING-STARTED.md +2 -0
- package/docs/SAFE-TRACE-SHARING.md +1 -0
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-SJ6FRNOE.mjs → chunk-LKEPABND.mjs} +86 -80
- package/packages/cli/dist/chunk-LKEPABND.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +406 -93
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +314 -16
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-YNEJOPRA.mjs → src-4WYF5RFI.mjs} +3 -3
- package/packages/cli/dist/{src-YNEJOPRA.mjs.map → src-4WYF5RFI.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +97 -30
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.mjs +13 -13
- package/packages/core/dist/checks.cjs +51 -49
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.mjs +5 -5
- package/packages/core/dist/{chunk-PBNBQRXV.mjs → chunk-2KJR6X4V.mjs} +9 -9
- package/packages/core/dist/chunk-2KJR6X4V.mjs.map +1 -0
- package/packages/core/dist/{chunk-26YJSETN.mjs → chunk-7DHSWDKB.mjs} +19 -6
- package/packages/core/dist/chunk-7DHSWDKB.mjs.map +1 -0
- package/packages/core/dist/{chunk-BGF5JDJN.mjs → chunk-INE5IWLN.mjs} +3 -3
- package/packages/core/dist/{chunk-BGF5JDJN.mjs.map → chunk-INE5IWLN.mjs.map} +1 -1
- package/packages/core/dist/{chunk-ZFJ2ICTC.mjs → chunk-MTMTXTJL.mjs} +5 -5
- package/packages/core/dist/{chunk-ZFJ2ICTC.mjs.map → chunk-MTMTXTJL.mjs.map} +1 -1
- package/packages/core/dist/{chunk-KWBO36PH.mjs → chunk-NY4ILMXK.mjs} +4 -4
- package/packages/core/dist/{chunk-KWBO36PH.mjs.map → chunk-NY4ILMXK.mjs.map} +1 -1
- package/packages/core/dist/{chunk-WN5PT4GV.mjs → chunk-OSGNGRHF.mjs} +3 -3
- package/packages/core/dist/{chunk-WN5PT4GV.mjs.map → chunk-OSGNGRHF.mjs.map} +1 -1
- package/packages/core/dist/{chunk-CBPWLHNQ.mjs → chunk-PKDBFBJ7.mjs} +5 -76
- package/packages/core/dist/chunk-PKDBFBJ7.mjs.map +1 -0
- package/packages/core/dist/{chunk-VU6O5QAH.mjs → chunk-VLVCLAPB.mjs} +83 -5
- package/packages/core/dist/chunk-VLVCLAPB.mjs.map +1 -0
- package/packages/core/dist/exporters.cjs +224 -8
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.mjs +125 -11
- package/packages/core/dist/exporters.mjs.map +1 -1
- package/packages/core/dist/index.cjs +97 -8
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.mjs +6 -6
- package/packages/core/dist/logs.cjs +80 -2
- package/packages/core/dist/logs.cjs.map +1 -1
- package/packages/core/dist/logs.d.cts +2 -0
- package/packages/core/dist/logs.d.ts +2 -0
- package/packages/core/dist/logs.mjs +2 -2
- package/packages/core/dist/readers.cjs +2 -0
- package/packages/core/dist/readers.cjs.map +1 -1
- package/packages/core/dist/readers.mjs +2 -2
- package/packages/core/dist/writers.cjs +96 -8
- package/packages/core/dist/writers.cjs.map +1 -1
- package/packages/core/dist/writers.mjs +7 -8
- package/packages/core/dist/writers.mjs.map +1 -1
- package/packages/cli/dist/chunk-SJ6FRNOE.mjs.map +0 -1
- package/packages/core/dist/chunk-26YJSETN.mjs.map +0 -1
- package/packages/core/dist/chunk-CBPWLHNQ.mjs.map +0 -1
- package/packages/core/dist/chunk-PBNBQRXV.mjs.map +0 -1
- 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-
|
|
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.
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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-
|
|
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
|
|
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(
|
|
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-
|
|
13292
|
+
return await import('./src-4WYF5RFI.mjs');
|
|
12995
13293
|
} catch (e) {
|
|
12996
13294
|
if (isModuleNotFound4(e)) {
|
|
12997
13295
|
console.error(
|