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
@@ -13,6 +13,7 @@ var readline = require('readline');
13
13
  var url = require('url');
14
14
  var module$1 = require('module');
15
15
  var commander = require('commander');
16
+ var string_decoder = require('string_decoder');
16
17
  var child_process = require('child_process');
17
18
  var util = require('util');
18
19
  var http = require('http');
@@ -66,6 +67,83 @@ var init_correlation_metadata = __esm({
66
67
  ];
67
68
  }
68
69
  });
70
+
71
+ // packages/core/src/safety/sensitive-key.ts
72
+ function keyHasExplicitSeparator(value) {
73
+ return /[_\-.]/.test(value);
74
+ }
75
+ function normalizeSensitiveKey(value) {
76
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
77
+ }
78
+ function isTokenCredentialKey(normalized) {
79
+ if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
80
+ if (normalized === "token") return true;
81
+ if (normalized.endsWith("tokens")) return false;
82
+ return normalized.endsWith("token");
83
+ }
84
+ function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSITIVE_KEYS) {
85
+ if (!key) return false;
86
+ const normalized = normalizeSensitiveKey(key);
87
+ if (!normalized) return false;
88
+ if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
89
+ const allowPrefixCompound = keyHasExplicitSeparator(key);
90
+ for (const sensitive of sensitiveKeys) {
91
+ const s = normalizeSensitiveKey(sensitive);
92
+ if (!s) continue;
93
+ if (s === "token") {
94
+ if (isTokenCredentialKey(normalized)) return true;
95
+ continue;
96
+ }
97
+ if (normalized === s) return true;
98
+ if (normalized.endsWith(`_${s}`)) return true;
99
+ if (allowPrefixCompound && normalized.startsWith(`${s}_`)) return true;
100
+ }
101
+ return false;
102
+ }
103
+ var NON_CREDENTIAL_TOKEN_CONFIG_KEYS, DEFAULT_CREDENTIAL_SENSITIVE_KEYS;
104
+ var init_sensitive_key = __esm({
105
+ "packages/core/src/safety/sensitive-key.ts"() {
106
+ NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
107
+ [
108
+ "tokens",
109
+ "max_tokens",
110
+ "min_tokens",
111
+ "ls_max_tokens",
112
+ "token_count",
113
+ "token_limit",
114
+ "token_budget",
115
+ "input_tokens",
116
+ "output_tokens",
117
+ "total_tokens",
118
+ "cached_tokens",
119
+ "prompt_tokens",
120
+ "completion_tokens"
121
+ ].map(normalizeSensitiveKey)
122
+ );
123
+ DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
124
+ "authorization",
125
+ "cookie",
126
+ "token",
127
+ "access_token",
128
+ "accesstoken",
129
+ "auth_token",
130
+ "authtoken",
131
+ "refresh_token",
132
+ "refreshtoken",
133
+ "id_token",
134
+ "idtoken",
135
+ "bearer_token",
136
+ "bearertoken",
137
+ "api_token",
138
+ "apitoken",
139
+ "apikey",
140
+ "api_key",
141
+ "password",
142
+ "secret",
143
+ "email"
144
+ ];
145
+ }
146
+ });
69
147
  function isRecord(v) {
70
148
  return typeof v === "object" && v !== null && !Array.isArray(v);
71
149
  }
@@ -80,7 +158,7 @@ function compileRules(rules, extraKeys) {
80
158
  const out = /* @__PURE__ */ new Map();
81
159
  const set = (r) => {
82
160
  const k = toKey(r.key);
83
- out.set(k, { ...r, key: k });
161
+ out.set(k, { ...r, key: k, raw: r.raw ?? r.key });
84
162
  };
85
163
  for (const k of DEFAULT_REDACT_KEYS) {
86
164
  set({ key: k, strategy: "full" });
@@ -107,6 +185,7 @@ function compileRules(rules, extraKeys) {
107
185
  var DEFAULT_REDACT_KEYS, Redactor;
108
186
  var init_redactor = __esm({
109
187
  "packages/core/src/logs/redactor.ts"() {
188
+ init_sensitive_key();
110
189
  DEFAULT_REDACT_KEYS = [
111
190
  "authorization",
112
191
  "cookie",
@@ -121,9 +200,13 @@ var init_redactor = __esm({
121
200
  constructor(options) {
122
201
  this.#rules = compileRules(options?.rules, options?.extraKeys);
123
202
  }
203
+ /** Field names this redactor treats as sensitive, for value-level helpers. */
204
+ get sensitiveKeys() {
205
+ return this.#rules.map((rule) => rule.raw ?? rule.key);
206
+ }
124
207
  redactValue(key, value) {
125
208
  const k = toKey(key);
126
- const rule = this.#rules.find((r) => r.key === k);
209
+ const rule = this.#rules.find((r) => r.key === k) ?? this.#rules.find((r) => isCredentialSensitiveKey(key, [r.raw ?? r.key]));
127
210
  if (!rule) {
128
211
  return this.#redactNested(value);
129
212
  }
@@ -178,7 +261,7 @@ function stringContainsHighConfidenceCredential(value) {
178
261
  var KEY_VALUE_SECRET_PATTERN, HIGH_CONFIDENCE_CREDENTIAL_PATTERNS;
179
262
  var init_credential_value_patterns = __esm({
180
263
  "packages/core/src/safety/credential-value-patterns.ts"() {
181
- KEY_VALUE_SECRET_PATTERN = /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})/i;
264
+ KEY_VALUE_SECRET_PATTERN = /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})/i;
182
265
  HIGH_CONFIDENCE_CREDENTIAL_PATTERNS = [
183
266
  /^(?:basic|bearer|digest|apikey)\s+[a-z0-9._~+/=-]+$/i,
184
267
  /\bbearer\s+[a-z0-9._~+/=-]{12,}\b/i,
@@ -829,6 +912,10 @@ var init_nanoid = __esm({
829
912
  POOL_SIZE_MULTIPLIER = 128;
830
913
  }
831
914
  });
915
+ var init_trace_filesystem = __esm({
916
+ "packages/core/src/trace-filesystem.ts"() {
917
+ }
918
+ });
832
919
 
833
920
  // packages/core/src/utils/duration.ts
834
921
  function parseDuration(duration) {
@@ -979,6 +1066,7 @@ function warn(message, error) {
979
1066
  var DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME, FALLBACK_TRACE_DIR, MAX_NAME_LENGTH;
980
1067
  var init_utils = __esm({
981
1068
  "packages/core/src/utils.ts"() {
1069
+ init_trace_filesystem();
982
1070
  init_duration();
983
1071
  DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
984
1072
  RUNS_DIR_NAME = "runs";
@@ -1149,6 +1237,7 @@ var init_storage = __esm({
1149
1237
  init_types();
1150
1238
  init_trace_event_safety();
1151
1239
  init_read_trace();
1240
+ init_trace_filesystem();
1152
1241
  init_utils();
1153
1242
  }
1154
1243
  });
@@ -4662,83 +4751,6 @@ var init_programmatic = __esm({
4662
4751
  }
4663
4752
  });
4664
4753
 
4665
- // packages/core/src/safety/sensitive-key.ts
4666
- function keyHasExplicitSeparator(value) {
4667
- return /[_\-.]/.test(value);
4668
- }
4669
- function normalizeSensitiveKey(value) {
4670
- return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
4671
- }
4672
- function isTokenCredentialKey(normalized) {
4673
- if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
4674
- if (normalized === "token") return true;
4675
- if (normalized.endsWith("tokens")) return false;
4676
- return normalized.endsWith("token");
4677
- }
4678
- function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSITIVE_KEYS) {
4679
- if (!key) return false;
4680
- const normalized = normalizeSensitiveKey(key);
4681
- if (!normalized) return false;
4682
- if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
4683
- const allowPrefixCompound = keyHasExplicitSeparator(key);
4684
- for (const sensitive of sensitiveKeys) {
4685
- const s = normalizeSensitiveKey(sensitive);
4686
- if (!s) continue;
4687
- if (s === "token") {
4688
- if (isTokenCredentialKey(normalized)) return true;
4689
- continue;
4690
- }
4691
- if (normalized === s) return true;
4692
- if (normalized.endsWith(`_${s}`)) return true;
4693
- if (allowPrefixCompound && normalized.startsWith(`${s}_`)) return true;
4694
- }
4695
- return false;
4696
- }
4697
- var NON_CREDENTIAL_TOKEN_CONFIG_KEYS, DEFAULT_CREDENTIAL_SENSITIVE_KEYS;
4698
- var init_sensitive_key = __esm({
4699
- "packages/core/src/safety/sensitive-key.ts"() {
4700
- NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
4701
- [
4702
- "tokens",
4703
- "max_tokens",
4704
- "min_tokens",
4705
- "ls_max_tokens",
4706
- "token_count",
4707
- "token_limit",
4708
- "token_budget",
4709
- "input_tokens",
4710
- "output_tokens",
4711
- "total_tokens",
4712
- "cached_tokens",
4713
- "prompt_tokens",
4714
- "completion_tokens"
4715
- ].map(normalizeSensitiveKey)
4716
- );
4717
- DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
4718
- "authorization",
4719
- "cookie",
4720
- "token",
4721
- "access_token",
4722
- "accesstoken",
4723
- "auth_token",
4724
- "authtoken",
4725
- "refresh_token",
4726
- "refreshtoken",
4727
- "id_token",
4728
- "idtoken",
4729
- "bearer_token",
4730
- "bearertoken",
4731
- "api_token",
4732
- "apitoken",
4733
- "apikey",
4734
- "api_key",
4735
- "password",
4736
- "secret",
4737
- "email"
4738
- ];
4739
- }
4740
- });
4741
-
4742
4754
  // packages/core/src/checks/logical-events.ts
4743
4755
  function isRecord7(value) {
4744
4756
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -7350,7 +7362,7 @@ var init_checks2 = __esm({
7350
7362
  // Keep in sync with packages/redact/src/key-value-secret.ts (KEY_VALUE_SECRET_PATTERN_SOURCE).
7351
7363
  {
7352
7364
  id: "key-value-secret",
7353
- pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})/i
7365
+ pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})/i
7354
7366
  }
7355
7367
  ];
7356
7368
  }
@@ -16153,7 +16165,7 @@ var init_src = __esm({
16153
16165
  });
16154
16166
 
16155
16167
  // package.json
16156
- var version = "6.31.3";
16168
+ var version = "6.31.5";
16157
16169
 
16158
16170
  // packages/cli/src/list.ts
16159
16171
  init_advanced();
@@ -17757,6 +17769,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
17757
17769
  await readFileOnce(filePath, onLine);
17758
17770
  return;
17759
17771
  }
17772
+ let decoder = new string_decoder.StringDecoder("utf-8");
17760
17773
  pos = st.size;
17761
17774
  while (!shouldStop()) {
17762
17775
  let next;
@@ -17770,6 +17783,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
17770
17783
  pos = 0;
17771
17784
  carry = "";
17772
17785
  lineNumber = 0;
17786
+ decoder = new string_decoder.StringDecoder("utf-8");
17773
17787
  }
17774
17788
  if (next.size > pos) {
17775
17789
  const fh = await promises.open(filePath, "r");
@@ -17778,7 +17792,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
17778
17792
  const buf = Buffer.allocUnsafe(Number(len));
17779
17793
  const { bytesRead } = await fh.read(buf, 0, buf.length, pos);
17780
17794
  pos += bytesRead;
17781
- const chunk = carry + buf.toString("utf-8", 0, bytesRead);
17795
+ const chunk = carry + decoder.write(buf.subarray(0, bytesRead));
17782
17796
  const parts = chunk.split(/\r?\n/);
17783
17797
  carry = parts.pop() ?? "";
17784
17798
  for (const p of parts) {
@@ -17943,6 +17957,128 @@ var EXPORT_PAYLOAD_VERSION = "0.1.2";
17943
17957
 
17944
17958
  // packages/core/src/exporters/redact-export.ts
17945
17959
  init_redactor();
17960
+ init_credential_value_patterns();
17961
+
17962
+ // packages/core/src/safety/url-redaction.ts
17963
+ init_sensitive_key();
17964
+ var HTTP_URL = /^https?:\/\/[^\s]+$/i;
17965
+ var CONNECTION_PROTOCOLS = /* @__PURE__ */ new Set([
17966
+ "postgres:",
17967
+ "postgresql:",
17968
+ "mysql:",
17969
+ "mariadb:",
17970
+ "mongodb:",
17971
+ "mongodb+srv:",
17972
+ "redis:",
17973
+ "rediss:",
17974
+ "amqp:",
17975
+ "amqps:"
17976
+ ]);
17977
+ var CONNECTION_URI = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
17978
+ var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17979
+ var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
17980
+ var NUMERIC_SEGMENT = /^\d{5,}$/;
17981
+ var OPAQUE_SEGMENT = /^[A-Za-z0-9_-]{20,}$/;
17982
+ function looksLikeHttpUrl(value) {
17983
+ return HTTP_URL.test(value.trim());
17984
+ }
17985
+ function looksLikeConnectionUri(value) {
17986
+ return CONNECTION_URI.test(value.trim());
17987
+ }
17988
+ function looksLikeUrlForRedaction(value) {
17989
+ return looksLikeHttpUrl(value) || looksLikeConnectionUri(value);
17990
+ }
17991
+ function parseRedactableUrl(value) {
17992
+ try {
17993
+ const url = new URL(value.trim());
17994
+ if (url.protocol === "http:" || url.protocol === "https:") return url;
17995
+ if (CONNECTION_PROTOCOLS.has(url.protocol)) return url;
17996
+ return void 0;
17997
+ } catch {
17998
+ return void 0;
17999
+ }
18000
+ }
18001
+ function isHttpProtocol(protocol) {
18002
+ return protocol === "http:" || protocol === "https:";
18003
+ }
18004
+ function isIdentifierSegment(segment) {
18005
+ if (UUID_SEGMENT.test(segment)) return true;
18006
+ if (HEX_SEGMENT.test(segment)) return true;
18007
+ if (NUMERIC_SEGMENT.test(segment)) return true;
18008
+ return OPAQUE_SEGMENT.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
18009
+ }
18010
+ function decodeParam(value) {
18011
+ try {
18012
+ return decodeURIComponent(value.replace(/\+/g, " "));
18013
+ } catch {
18014
+ return value;
18015
+ }
18016
+ }
18017
+ function redactParams(raw, options, replacement) {
18018
+ let redacted = false;
18019
+ const parts = raw.split("&").map((part) => {
18020
+ if (part.length === 0) return part;
18021
+ const separator = part.indexOf("=");
18022
+ if (separator < 0) return part;
18023
+ const rawKey = part.slice(0, separator);
18024
+ const rawValue = part.slice(separator + 1);
18025
+ const key = decodeParam(rawKey);
18026
+ const value = decodeParam(rawValue);
18027
+ if (isCredentialSensitiveKey(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
18028
+ redacted = true;
18029
+ return `${rawKey}=${replacement}`;
18030
+ }
18031
+ return part;
18032
+ });
18033
+ return { value: parts.join("&"), redacted };
18034
+ }
18035
+ function redactUrlString(value, options) {
18036
+ const unchanged = {
18037
+ value,
18038
+ credentialRedacted: false,
18039
+ identifiersMasked: false
18040
+ };
18041
+ if (!looksLikeUrlForRedaction(value)) return unchanged;
18042
+ const url = parseRedactableUrl(value);
18043
+ if (!url) return unchanged;
18044
+ const replacement = options.replacement ?? "[REDACTED]";
18045
+ const idPlaceholder = options.idPlaceholder ?? "[id]";
18046
+ const http = isHttpProtocol(url.protocol);
18047
+ let credentialRedacted = url.username.length > 0 || url.password.length > 0;
18048
+ let identifiersMasked = false;
18049
+ let pathname = url.pathname;
18050
+ if (http && options.maskPathIds === true) {
18051
+ pathname = url.pathname.split("/").map((segment) => {
18052
+ if (segment.length === 0 || !isIdentifierSegment(decodeParam(segment))) {
18053
+ return segment;
18054
+ }
18055
+ identifiersMasked = true;
18056
+ return idPlaceholder;
18057
+ }).join("/");
18058
+ }
18059
+ let search = "";
18060
+ if (url.search.length > 1) {
18061
+ const params = redactParams(url.search.slice(1), options, replacement);
18062
+ if (params.redacted) credentialRedacted = true;
18063
+ search = `?${params.value}`;
18064
+ } else if (url.search.length === 1) {
18065
+ search = "?";
18066
+ }
18067
+ let hash = url.hash;
18068
+ if (url.hash.includes("=")) {
18069
+ const params = redactParams(url.hash.slice(1), options, replacement);
18070
+ if (params.redacted) credentialRedacted = true;
18071
+ hash = `#${params.value}`;
18072
+ }
18073
+ if (!credentialRedacted && !identifiersMasked) return unchanged;
18074
+ return {
18075
+ value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
18076
+ credentialRedacted,
18077
+ identifiersMasked
18078
+ };
18079
+ }
18080
+
18081
+ // packages/core/src/exporters/redact-export.ts
17946
18082
  init_redaction_profiles();
17947
18083
  function isRecord15(value) {
17948
18084
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -17960,21 +18096,34 @@ function deepClone(value) {
17960
18096
  }
17961
18097
  return out;
17962
18098
  }
17963
- function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
18099
+ function boundAttributeValues(record, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
17964
18100
  if (depth > 32) {
17965
18101
  return { truncated: true, reason: "maxDepth" };
17966
18102
  }
17967
18103
  const out = {};
17968
18104
  for (const [key, value] of Object.entries(record)) {
17969
- out[key] = boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth);
18105
+ out[key] = boundValue(
18106
+ value,
18107
+ key,
18108
+ sensitiveKeys,
18109
+ maxMetadataValueLength,
18110
+ maxPreviewLength,
18111
+ seen,
18112
+ depth
18113
+ );
17970
18114
  }
17971
18115
  return out;
17972
18116
  }
17973
- function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
18117
+ function boundValue(value, key, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
17974
18118
  if (value === null || typeof value !== "object") {
17975
18119
  if (typeof value === "string") {
18120
+ const rewritten = redactUrlString(value, {
18121
+ sensitiveKeys,
18122
+ isSensitiveValue: stringContainsHighConfidenceCredential,
18123
+ maskPathIds: true
18124
+ }).value;
17976
18125
  return truncateStringForProfile(
17977
- value,
18126
+ stringContainsHighConfidenceCredential(rewritten) ? "[REDACTED]" : rewritten,
17978
18127
  key,
17979
18128
  maxMetadataValueLength,
17980
18129
  maxPreviewLength
@@ -17989,6 +18138,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
17989
18138
  (item, index) => boundValue(
17990
18139
  item,
17991
18140
  String(index),
18141
+ sensitiveKeys,
17992
18142
  maxMetadataValueLength,
17993
18143
  maxPreviewLength,
17994
18144
  seen,
@@ -17998,6 +18148,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
17998
18148
  }
17999
18149
  return boundAttributeValues(
18000
18150
  value,
18151
+ sensitiveKeys,
18001
18152
  maxMetadataValueLength,
18002
18153
  maxPreviewLength,
18003
18154
  seen,
@@ -18012,6 +18163,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
18012
18163
  const seen = /* @__PURE__ */ new WeakSet();
18013
18164
  const bounded = boundAttributeValues(
18014
18165
  redacted,
18166
+ [...DEFAULT_REDACT_KEYS, ...redactor.sensitiveKeys],
18015
18167
  maxMetadataValueLength,
18016
18168
  maxPreviewLength,
18017
18169
  seen,
@@ -20150,7 +20302,7 @@ function isCredentialSensitiveKey2(key, sensitiveKeys) {
20150
20302
  }
20151
20303
 
20152
20304
  // packages/redact/src/key-value-secret.ts
20153
- var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})`;
20305
+ 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,})`;
20154
20306
  var KEY_VALUE_SECRET_PATTERN2 = new RegExp(
20155
20307
  KEY_VALUE_SECRET_PATTERN_SOURCE,
20156
20308
  "i"
@@ -20160,6 +20312,124 @@ function valueContainsKeyValueSecret(value) {
20160
20312
  return KEY_VALUE_SECRET_PATTERN2.test(value);
20161
20313
  }
20162
20314
 
20315
+ // packages/redact/src/url-redaction.ts
20316
+ var HTTP_URL2 = /^https?:\/\/[^\s]+$/i;
20317
+ var CONNECTION_PROTOCOLS2 = /* @__PURE__ */ new Set([
20318
+ "postgres:",
20319
+ "postgresql:",
20320
+ "mysql:",
20321
+ "mariadb:",
20322
+ "mongodb:",
20323
+ "mongodb+srv:",
20324
+ "redis:",
20325
+ "rediss:",
20326
+ "amqp:",
20327
+ "amqps:"
20328
+ ]);
20329
+ var CONNECTION_URI2 = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
20330
+ var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20331
+ var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
20332
+ var NUMERIC_SEGMENT2 = /^\d{5,}$/;
20333
+ var OPAQUE_SEGMENT2 = /^[A-Za-z0-9_-]{20,}$/;
20334
+ function looksLikeHttpUrl2(value) {
20335
+ return HTTP_URL2.test(value.trim());
20336
+ }
20337
+ function looksLikeConnectionUri2(value) {
20338
+ return CONNECTION_URI2.test(value.trim());
20339
+ }
20340
+ function looksLikeUrlForRedaction2(value) {
20341
+ return looksLikeHttpUrl2(value) || looksLikeConnectionUri2(value);
20342
+ }
20343
+ function parseRedactableUrl2(value) {
20344
+ try {
20345
+ const url = new URL(value.trim());
20346
+ if (url.protocol === "http:" || url.protocol === "https:") return url;
20347
+ if (CONNECTION_PROTOCOLS2.has(url.protocol)) return url;
20348
+ return void 0;
20349
+ } catch {
20350
+ return void 0;
20351
+ }
20352
+ }
20353
+ function isHttpProtocol2(protocol) {
20354
+ return protocol === "http:" || protocol === "https:";
20355
+ }
20356
+ function isIdentifierSegment2(segment) {
20357
+ if (UUID_SEGMENT2.test(segment)) return true;
20358
+ if (HEX_SEGMENT2.test(segment)) return true;
20359
+ if (NUMERIC_SEGMENT2.test(segment)) return true;
20360
+ return OPAQUE_SEGMENT2.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
20361
+ }
20362
+ function decodeParam2(value) {
20363
+ try {
20364
+ return decodeURIComponent(value.replace(/\+/g, " "));
20365
+ } catch {
20366
+ return value;
20367
+ }
20368
+ }
20369
+ function redactParams2(raw, options, replacement) {
20370
+ let redacted = false;
20371
+ const parts = raw.split("&").map((part) => {
20372
+ if (part.length === 0) return part;
20373
+ const separator = part.indexOf("=");
20374
+ if (separator < 0) return part;
20375
+ const rawKey = part.slice(0, separator);
20376
+ const rawValue = part.slice(separator + 1);
20377
+ const key = decodeParam2(rawKey);
20378
+ const value = decodeParam2(rawValue);
20379
+ if (isCredentialSensitiveKey2(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
20380
+ redacted = true;
20381
+ return `${rawKey}=${replacement}`;
20382
+ }
20383
+ return part;
20384
+ });
20385
+ return { value: parts.join("&"), redacted };
20386
+ }
20387
+ function redactUrlString2(value, options) {
20388
+ const unchanged = {
20389
+ value,
20390
+ credentialRedacted: false,
20391
+ identifiersMasked: false
20392
+ };
20393
+ if (!looksLikeUrlForRedaction2(value)) return unchanged;
20394
+ const url = parseRedactableUrl2(value);
20395
+ if (!url) return unchanged;
20396
+ const replacement = options.replacement ?? "[REDACTED]";
20397
+ const idPlaceholder = options.idPlaceholder ?? "[id]";
20398
+ const http = isHttpProtocol2(url.protocol);
20399
+ let credentialRedacted = url.username.length > 0 || url.password.length > 0;
20400
+ let identifiersMasked = false;
20401
+ let pathname = url.pathname;
20402
+ if (http && options.maskPathIds === true) {
20403
+ pathname = url.pathname.split("/").map((segment) => {
20404
+ if (segment.length === 0 || !isIdentifierSegment2(decodeParam2(segment))) {
20405
+ return segment;
20406
+ }
20407
+ identifiersMasked = true;
20408
+ return idPlaceholder;
20409
+ }).join("/");
20410
+ }
20411
+ let search = "";
20412
+ if (url.search.length > 1) {
20413
+ const params = redactParams2(url.search.slice(1), options, replacement);
20414
+ if (params.redacted) credentialRedacted = true;
20415
+ search = `?${params.value}`;
20416
+ } else if (url.search.length === 1) {
20417
+ search = "?";
20418
+ }
20419
+ let hash = url.hash;
20420
+ if (url.hash.includes("=")) {
20421
+ const params = redactParams2(url.hash.slice(1), options, replacement);
20422
+ if (params.redacted) credentialRedacted = true;
20423
+ hash = `#${params.value}`;
20424
+ }
20425
+ if (!credentialRedacted && !identifiersMasked) return unchanged;
20426
+ return {
20427
+ value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
20428
+ credentialRedacted,
20429
+ identifiersMasked
20430
+ };
20431
+ }
20432
+
20163
20433
  // packages/redact/src/index.ts
20164
20434
  var DEFAULT_REDACT_KEYS2 = [
20165
20435
  "authorization",
@@ -20226,7 +20496,9 @@ function findCompiledKeyRule(key, rules) {
20226
20496
  const exact = toKey2(key);
20227
20497
  const direct = rules.find((candidate) => candidate.key === exact);
20228
20498
  if (direct) return direct;
20229
- return rules.find((candidate) => isCredentialSensitiveKey2(key, [candidate.key]));
20499
+ return rules.find(
20500
+ (candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
20501
+ );
20230
20502
  }
20231
20503
  function stableHash2(value) {
20232
20504
  const hash = crypto__default.default.createHash("sha256").update(value, "utf8").digest("hex");
@@ -20425,7 +20697,7 @@ function compileRules2(rules, extraKeys) {
20425
20697
  const out = /* @__PURE__ */ new Map();
20426
20698
  const set = (rule) => {
20427
20699
  const key = toKey2(rule.key);
20428
- out.set(key, { ...rule, key });
20700
+ out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
20429
20701
  };
20430
20702
  for (const key of DEFAULT_REDACT_KEYS2) {
20431
20703
  set({ key, strategy: "full" });
@@ -20504,6 +20776,7 @@ function createRedactionProfile(profile = "local") {
20504
20776
  }
20505
20777
  var Redactor2 = class {
20506
20778
  #rules;
20779
+ #sensitiveKeys;
20507
20780
  #detectors;
20508
20781
  #profile;
20509
20782
  #replacement;
@@ -20516,6 +20789,7 @@ var Redactor2 = class {
20516
20789
  ...resolved.extraKeys,
20517
20790
  ...options?.extraKeys ?? []
20518
20791
  ]);
20792
+ this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
20519
20793
  this.#detectors = [
20520
20794
  ...builtInDetectorsForProfile(this.#profile),
20521
20795
  ...options?.detectors ?? []
@@ -20549,6 +20823,14 @@ var Redactor2 = class {
20549
20823
  #recordFinding(state, finding) {
20550
20824
  if (this.#collectFindings) state.findings.push(finding);
20551
20825
  }
20826
+ #detect(value, key, path44) {
20827
+ for (const detector of this.#detectors) {
20828
+ for (const detection of detector.detect({ path: path44, key, value })) {
20829
+ if ((detection.action ?? "replace") !== "keep") return { detector, detection };
20830
+ }
20831
+ }
20832
+ return void 0;
20833
+ }
20552
20834
  #redactValue(value, key, path44, depth, state) {
20553
20835
  if (depth > this.#maxDepth) {
20554
20836
  this.#recordFinding(
@@ -20567,6 +20849,43 @@ var Redactor2 = class {
20567
20849
  return applyRule(rule, value, this.#replacement);
20568
20850
  }
20569
20851
  }
20852
+ if (typeof value === "string" && looksLikeUrlForRedaction2(value)) {
20853
+ const url = redactUrlString2(value, {
20854
+ sensitiveKeys: this.#sensitiveKeys,
20855
+ isSensitiveValue: (candidate) => this.#detect(candidate, key, path44) !== void 0,
20856
+ replacement: this.#replacement,
20857
+ maskPathIds: this.#profile !== "local"
20858
+ });
20859
+ if (url.value !== value) {
20860
+ this.#recordFinding(
20861
+ state,
20862
+ makeFinding(
20863
+ path44,
20864
+ url.credentialRedacted ? "value.urlCredential" : "value.urlIdentifier",
20865
+ "replace",
20866
+ "value",
20867
+ url.credentialRedacted ? "error" : "warning"
20868
+ )
20869
+ );
20870
+ const residual = this.#detect(
20871
+ url.value.split(this.#replacement).join(""),
20872
+ key,
20873
+ path44
20874
+ );
20875
+ if (residual === void 0) return url.value;
20876
+ this.#recordFinding(
20877
+ state,
20878
+ makeFinding(
20879
+ path44,
20880
+ residual.detector.id,
20881
+ "replace",
20882
+ residual.detection.matchKind ?? residual.detector.matchKind ?? "custom",
20883
+ residual.detection.severity ?? residual.detector.severity ?? "warning"
20884
+ )
20885
+ );
20886
+ return this.#replacement;
20887
+ }
20888
+ }
20570
20889
  for (const detector of this.#detectors) {
20571
20890
  const detections = detector.detect({ path: path44, key, value });
20572
20891
  for (const detection of detections) {
@@ -28582,7 +28901,7 @@ function envCheck(name, optional = true) {
28582
28901
  async function traceDirWritable(traceDir) {
28583
28902
  const resolved = path33__default.default.resolve(traceDir);
28584
28903
  try {
28585
- await promises.mkdir(resolved, { recursive: true });
28904
+ await promises.mkdir(resolved, { recursive: true, mode: 448 });
28586
28905
  await promises.access(resolved, promises.constants.W_OK);
28587
28906
  return {
28588
28907
  id: "trace-dir-writable",
@@ -28600,6 +28919,44 @@ async function traceDirWritable(traceDir) {
28600
28919
  };
28601
28920
  }
28602
28921
  }
28922
+ async function traceDirPermissions(traceDir) {
28923
+ const resolved = path33__default.default.resolve(traceDir);
28924
+ if (process3__default.default.platform === "win32") {
28925
+ return {
28926
+ id: "trace-dir-permissions",
28927
+ status: "skipped",
28928
+ message: "POSIX confidentiality mode check skipped on Windows.",
28929
+ evidence: resolved
28930
+ };
28931
+ }
28932
+ try {
28933
+ const st = await promises.stat(resolved);
28934
+ const mode = st.mode & 511;
28935
+ const octal = mode.toString(8).padStart(3, "0");
28936
+ if ((mode & 63) !== 0) {
28937
+ return {
28938
+ id: "trace-dir-permissions",
28939
+ status: "warn",
28940
+ message: `Trace directory allows group/other access (mode ${octal}): ${resolved}`,
28941
+ 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`,
28942
+ evidence: `mode=${octal}`
28943
+ };
28944
+ }
28945
+ return {
28946
+ id: "trace-dir-permissions",
28947
+ status: "pass",
28948
+ message: `Trace directory mode is restrictive (${octal}): ${resolved}`,
28949
+ evidence: `mode=${octal}`
28950
+ };
28951
+ } catch (error) {
28952
+ return {
28953
+ id: "trace-dir-permissions",
28954
+ status: "skipped",
28955
+ message: `Could not inspect trace directory permissions: ${resolved}`,
28956
+ evidence: error instanceof Error ? error.message : String(error)
28957
+ };
28958
+ }
28959
+ }
28603
28960
  function readPackageVersionNearEntry(entryPath, packageName) {
28604
28961
  let dir = path33__default.default.dirname(path33__default.default.resolve(entryPath));
28605
28962
  const { root } = path33__default.default.parse(dir);
@@ -28731,6 +29088,7 @@ async function runDoctorChecks(options = {}) {
28731
29088
  evidence: version
28732
29089
  },
28733
29090
  await traceDirWritable(traceDir),
29091
+ await traceDirPermissions(traceDir),
28734
29092
  envCheck("AGENT_INSPECT"),
28735
29093
  envCheck("AGENT_INSPECT_TRACE_DIR"),
28736
29094
  versionMismatchCheck(cwd)