agent-first-data 0.34.0 → 0.35.0

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/README.md CHANGED
@@ -22,11 +22,11 @@ Useful names use TypeScript casing: `render`, `outputOptionsForPolicy`, `redacte
22
22
  ## Behavior Notes
23
23
 
24
24
  - Default redaction replaces every `_secret` or configured secret-name subtree with `***`, including objects and arrays.
25
- - `_url` fields scrub userinfo passwords and secret-named query parameters; surrounding whitespace is trimmed and internal whitespace redacts the whole field.
25
+ - `_url` fields scrub userinfo passwords and secret-named query parameters; surrounding whitespace is trimmed, and internal whitespace or a `://` that is not the start of a clean URL redacts the whole field, where whitespace is the explicit Unicode `White_Space` set the spec enumerates, not the language's native predicate.
26
26
  - `OutputOptions.redaction.urlNames` applies URL treatment to exact legacy field names
27
27
  and recurses through collections. `redactUrlsInText` is explicit and scans
28
28
  only complete scheme URLs, never arbitrary prose secrets.
29
- - YAML keeps original keys and values (structure-preserving, like JSON), sorting keys by UTF-16 code unit order and quoting/escaping unsafe keys and string scalars. Plain strips formatting suffixes, formats values, sorts the same way, and renders nested objects/arrays as canonical JSON.
29
+ - YAML keeps original keys and values (structure-preserving, like JSON), sorting keys by UTF-16 code unit order and quoting/escaping unsafe keys and string scalars. Plain strips formatting suffixes, formats values, sorts the same way, and renders nested objects/arrays as canonical JSON; it quotes a value only when it contains ASCII space, tab, newline, carriage return, form feed, vertical tab, NBSP, `=`, `"`, or `\`, and escapes `\`, `"`, newline, carriage return, tab, form feed, and vertical tab inside the quotes.
30
30
  - Logging records use `kind:"log"` with a nested `log` payload and a separate `level` field, so error-level logs are not terminal protocol errors.
31
31
  - `buildCliError(message, hint?)` returns a strict-ready CLI error with `error.retryable:false` and `trace:{}`.
32
32
  - Do not add a raw version/help pre-parser. Until the TypeScript `CliSpec` compiler lands, applications should keep lifecycle parsing in their own parser rather than claim AFDATA closed-world help compatibility.
package/dist/cli.js CHANGED
@@ -110,8 +110,19 @@ export function parseOutputTo(value) {
110
110
  }
111
111
  throw new Error("invalid --output-to: expected split, stdout, or stderr");
112
112
  }
113
+ const guardedProcessStreams = new WeakSet();
114
+ function guardProcessStream(stream) {
115
+ if (guardedProcessStreams.has(stream))
116
+ return;
117
+ guardedProcessStreams.add(stream);
118
+ stream.on("error", (error) => {
119
+ if (error.code !== "EPIPE")
120
+ throw error;
121
+ });
122
+ }
113
123
  /** Event writer bound to the process stdout stream. */
114
124
  const processStdoutWriter = (line) => {
125
+ guardProcessStream(process.stdout);
115
126
  process.stdout.write(line);
116
127
  };
117
128
  /**
@@ -121,6 +132,7 @@ const processStdoutWriter = (line) => {
121
132
  * `stderr-sink` marker is what no_stderr_policy.test.ts allows through.
122
133
  */
123
134
  const processStderrWriter = (line) => {
135
+ guardProcessStream(process.stderr); // stderr-sink: CliEmitter diagnostic channel
124
136
  process.stderr.write(line); // stderr-sink: CliEmitter diagnostic channel
125
137
  };
126
138
  /**
package/dist/format.js CHANGED
@@ -1029,19 +1029,28 @@ function redactUrlFieldValue(s, secretNames) {
1029
1029
  const redacted = redactUrlInStr(s, secretNames);
1030
1030
  if (redacted !== null)
1031
1031
  return redacted;
1032
- const trimmed = s.trim();
1032
+ let start = 0;
1033
+ let end = s.length;
1034
+ while (start < end && isSpanTerminatorWhitespace(s.charCodeAt(start)))
1035
+ start += 1;
1036
+ while (end > start && isSpanTerminatorWhitespace(s.charCodeAt(end - 1)))
1037
+ end -= 1;
1038
+ const trimmed = s.slice(start, end);
1033
1039
  if (trimmed !== s) {
1034
1040
  const trimmedRedacted = redactUrlInStr(trimmed, secretNames);
1035
1041
  if (trimmedRedacted !== null)
1036
1042
  return trimmedRedacted;
1037
1043
  }
1038
1044
  // Fail closed: a _url value we could not parse as a clean scheme-prefixed
1039
- // URL, yet which carries a credential sigil ('@' userinfo) or internal
1040
- // whitespace, is redacted wholesale rather than passed through. A schemeless
1041
- // connection string like user:pass@host/db has no scheme anchor for the
1042
- // surgical span logic above, so blanket redaction is the safe default.
1043
- if (/\s/.test(s) || s.includes("@"))
1045
+ // URL, yet which carries a credential sigil ('@' userinfo), internal
1046
+ // whitespace, or a '://' scheme separator, is redacted wholesale rather than
1047
+ // passed through. A schemeless connection string like user:pass@host/db
1048
+ // has no scheme anchor for the surgical span logic above, and a URL behind
1049
+ // a non-whitespace prefix (a BOM, a zero-width space, a label) has one the
1050
+ // span logic cannot reach, so blanket redaction is the safe default.
1051
+ if ([...s].some(c => isSpanTerminatorWhitespace(c.charCodeAt(0))) || s.includes("@") || s.includes("://")) {
1044
1052
  return REDACTED_MARKER;
1053
+ }
1045
1054
  return s;
1046
1055
  }
1047
1056
  /**
@@ -1629,7 +1638,8 @@ function isAmbiguousYamlKey(key) {
1629
1638
  if (["true", "false", "null", "~", ".nan", ".inf", "+.inf", "-.inf"].includes(key.toLowerCase())) {
1630
1639
  return true;
1631
1640
  }
1632
- return key.trim() !== "" && Number.isFinite(Number(key));
1641
+ return ["inf", "infinity", "nan"].includes(key.replace(/^[+-]/, "").toLowerCase())
1642
+ || /^[-+]?(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][-+]?[0-9]+)?$/.test(key);
1633
1643
  }
1634
1644
  function yamlScalar(value) {
1635
1645
  if (typeof value === "string")
@@ -1726,7 +1736,7 @@ function plainScalarRaw(value) {
1726
1736
  function quoteLogfmtValue(value) {
1727
1737
  if (value === "")
1728
1738
  return "";
1729
- if (!/[\s="\\]/.test(value))
1739
+ if (!/[ \t\n\r\f\v\u00a0="\\]/.test(value))
1730
1740
  return value;
1731
1741
  const escaped = value
1732
1742
  .replace(/\\/g, "\\\\")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-first-data",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "description": "A naming convention that lets AI agents understand your data without being told what it means, plus a CLI and library for reading Markdown structure and safely editing structured JSON, TOML, YAML, dotenv, and INI documents.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -27,8 +27,8 @@
27
27
  "test": "tsx --test src/*.test.ts"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/node": "^26.2.0",
31
- "tsx": "^4.23.12",
30
+ "@types/node": "^26.6.4",
31
+ "tsx": "^4.23.15",
32
32
  "typescript": "^7.0.2"
33
33
  },
34
34
  "dependencies": {