agent-first-data 0.4.2 → 0.6.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
@@ -63,11 +63,11 @@ Plain: args.input_path=/data/backup.tar.gz code=log event=startup config.max_fil
63
63
 
64
64
  ## API Reference
65
65
 
66
- Total: **12 public APIs and 1 type** + **AFDATA logging** (3 protocol builders + 3 output functions + 1 internal + 1 utility + 4 CLI helpers + `OutputFormat`)
66
+ Total: **13 public APIs and 2 types** + **AFDATA logging** (3 protocol builders + 4 output functions + 1 internal + 1 utility + 4 CLI helpers + `OutputFormat` + `RedactionPolicy`)
67
67
 
68
68
  ### Protocol Builders (returns JsonValue)
69
69
 
70
- Build AFDATA protocol structures. Return JSON-serializable objects for API responses.
70
+ Build AFDATA protocol structures. Return JSON-serializable objects for transport payloads.
71
71
 
72
72
  ```typescript
73
73
  type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
@@ -75,14 +75,14 @@ type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string
75
75
  // Success (result)
76
76
  buildJsonOk(result: JsonValue, trace?: JsonValue): JsonValue
77
77
 
78
- // Error (simple message)
79
- buildJsonError(message: string, trace?: JsonValue): JsonValue
78
+ // Error (simple message, optional hint)
79
+ buildJsonError(message: string, hint?: string, trace?: JsonValue): JsonValue
80
80
 
81
81
  // Generic (any code + fields)
82
82
  buildJson(code: string, fields: JsonValue, trace?: JsonValue): JsonValue
83
83
  ```
84
84
 
85
- **Use case:** API responses (frameworks like Express automatically serialize)
85
+ **Use case:** structured protocol payloads (frameworks automatically serialize)
86
86
 
87
87
  **Example:**
88
88
  ```typescript
@@ -106,7 +106,10 @@ const response = buildJsonOk(
106
106
  );
107
107
 
108
108
  // Error
109
- const error = buildJsonError("user not found", { duration_ms: 5 });
109
+ const error = buildJsonError("user not found", undefined, { duration_ms: 5 });
110
+
111
+ // Error with hint
112
+ const errorHint = buildJsonError("wallet not found", "list wallets with: afpay wallet list", { duration_ms: 5 });
110
113
 
111
114
  // Specific error code
112
115
  const notFound = buildJson(
@@ -118,14 +121,22 @@ const notFound = buildJson(
118
121
 
119
122
  ### CLI/Log Output (returns string)
120
123
 
121
- Format values for CLI output and logs. **All formats redact `_secret` fields.** YAML and Plain also strip suffixes from keys and format values for human readability.
124
+ Format values for CLI output and logs. `outputJson` uses full `_secret` redaction by default. `outputJsonWith` supports explicit scoped policies. YAML and Plain always redact `_secret` and apply human-readable formatting.
122
125
 
123
126
  ```typescript
124
127
  outputJson(value: JsonValue): string // Single-line JSON, original keys, for programs/logs
128
+ outputJsonWith(value: JsonValue, redactionPolicy: RedactionPolicy): string
125
129
  outputYaml(value: JsonValue): string // Multi-line YAML, keys stripped, values formatted
126
130
  outputPlain(value: JsonValue): string // Single-line logfmt, keys stripped, values formatted
127
131
  ```
128
132
 
133
+ ```typescript
134
+ enum RedactionPolicy {
135
+ RedactionTraceOnly = "RedactionTraceOnly",
136
+ RedactionNone = "RedactionNone",
137
+ }
138
+ ```
139
+
129
140
  **Example:**
130
141
  ```typescript
131
142
  import { outputJson, outputYaml, outputPlain } from "agent-first-data";
@@ -168,6 +179,8 @@ Most users don't need this. Output functions automatically protect secrets.
168
179
  parseSize(s: string): number | null // Parse "10M" → bytes
169
180
  ```
170
181
 
182
+ Returns `null` for invalid input and for values that cannot be represented as a safe JavaScript integer.
183
+
171
184
  **Example:**
172
185
  ```typescript
173
186
  import { parseSize } from "agent-first-data";
@@ -187,7 +200,7 @@ type OutputFormat = "json" | "yaml" | "plain"
187
200
  cliParseOutput(s: string): OutputFormat // Parse --output flag; throws on unknown
188
201
  cliParseLogFilters(entries: string[]): string[] // Normalize --log: trim, lowercase, dedup, remove empty
189
202
  cliOutput(value: JsonValue, format: OutputFormat): string // Dispatch to outputJson/Yaml/Plain
190
- buildCliError(message: string): JsonValue // {code:"error", error_code:"invalid_request", retryable:false, trace:{duration_ms:0}}
203
+ buildCliError(message: string, hint?: string): JsonValue // {code:"error", error_code:"invalid_request", hint?, retryable:false, trace:{duration_ms:0}}
191
204
  ```
192
205
 
193
206
  **Canonical pattern** — parse all flags before doing work, emit JSONL errors to stdout:
package/dist/cli.d.ts CHANGED
@@ -39,4 +39,4 @@ export declare function cliOutput(value: JsonValue, format: OutputFormat): strin
39
39
  * console.log(outputJson(err));
40
40
  * process.exit(2);
41
41
  */
42
- export declare function buildCliError(message: string): JsonValue;
42
+ export declare function buildCliError(message: string, hint?: string): JsonValue;
package/dist/cli.js CHANGED
@@ -57,12 +57,15 @@ export function cliOutput(value, format) {
57
57
  * console.log(outputJson(err));
58
58
  * process.exit(2);
59
59
  */
60
- export function buildCliError(message) {
61
- return {
60
+ export function buildCliError(message, hint) {
61
+ const m = {
62
62
  code: "error",
63
63
  error_code: "invalid_request",
64
64
  error: message,
65
65
  retryable: false,
66
66
  trace: { duration_ms: 0 },
67
67
  };
68
+ if (hint !== undefined)
69
+ m.hint = hint;
70
+ return m;
68
71
  }
package/dist/format.d.ts CHANGED
@@ -1,19 +1,25 @@
1
1
  /**
2
2
  * AFDATA output formatting and protocol templates.
3
3
  *
4
- * 8 public APIs: 3 protocol builders + 3 output formatters + 1 redaction + 1 utility.
4
+ * 9 public APIs and 1 type: 3 protocol builders + 4 output formatters + 1 redaction + 1 utility + RedactionPolicy.
5
5
  */
6
6
  export type JsonValue = string | number | boolean | null | JsonValue[] | {
7
7
  [key: string]: JsonValue;
8
8
  };
9
9
  /** Build {code: "ok", result, trace?}. */
10
10
  export declare function buildJsonOk(result: JsonValue, trace?: JsonValue): JsonValue;
11
- /** Build {code: "error", error: message, trace?}. */
12
- export declare function buildJsonError(message: string, trace?: JsonValue): JsonValue;
11
+ /** Build {code: "error", error: message, hint?, trace?}. */
12
+ export declare function buildJsonError(message: string, hint?: string, trace?: JsonValue): JsonValue;
13
13
  /** Build {code: "<custom>", ...fields, trace?}. */
14
14
  export declare function buildJson(code: string, fields: JsonValue, trace?: JsonValue): JsonValue;
15
+ export declare enum RedactionPolicy {
16
+ RedactionTraceOnly = "RedactionTraceOnly",
17
+ RedactionNone = "RedactionNone"
18
+ }
15
19
  /** Format as single-line JSON. Secrets redacted, original keys, raw values. */
16
20
  export declare function outputJson(value: JsonValue): string;
21
+ /** Format as single-line JSON with explicit redaction policy. */
22
+ export declare function outputJsonWith(value: JsonValue, redactionPolicy: RedactionPolicy): string;
17
23
  /** Format as multi-line YAML. Keys stripped, values formatted, secrets redacted. */
18
24
  export declare function outputYaml(value: JsonValue): string;
19
25
  /** Format as single-line logfmt. Keys stripped, values formatted, secrets redacted. */
package/dist/format.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AFDATA output formatting and protocol templates.
3
3
  *
4
- * 8 public APIs: 3 protocol builders + 3 output formatters + 1 redaction + 1 utility.
4
+ * 9 public APIs and 1 type: 3 protocol builders + 4 output formatters + 1 redaction + 1 utility + RedactionPolicy.
5
5
  */
6
6
  // ═══════════════════════════════════════════
7
7
  // Public API: Protocol Builders
@@ -13,9 +13,11 @@ export function buildJsonOk(result, trace) {
13
13
  m.trace = trace;
14
14
  return m;
15
15
  }
16
- /** Build {code: "error", error: message, trace?}. */
17
- export function buildJsonError(message, trace) {
16
+ /** Build {code: "error", error: message, hint?, trace?}. */
17
+ export function buildJsonError(message, hint, trace) {
18
18
  const m = { code: "error", error: message };
19
+ if (hint !== undefined)
20
+ m.hint = hint;
19
21
  if (trace !== undefined)
20
22
  m.trace = trace;
21
23
  return m;
@@ -31,20 +33,33 @@ export function buildJson(code, fields, trace) {
31
33
  // ═══════════════════════════════════════════
32
34
  // Public API: Output Formatters
33
35
  // ═══════════════════════════════════════════
36
+ export var RedactionPolicy;
37
+ (function (RedactionPolicy) {
38
+ RedactionPolicy["RedactionTraceOnly"] = "RedactionTraceOnly";
39
+ RedactionPolicy["RedactionNone"] = "RedactionNone";
40
+ })(RedactionPolicy || (RedactionPolicy = {}));
34
41
  /** Format as single-line JSON. Secrets redacted, original keys, raw values. */
35
42
  export function outputJson(value) {
36
- const v = JSON.parse(JSON.stringify(value));
43
+ const v = sanitizeForJson(value);
37
44
  redactSecrets(v);
38
45
  return JSON.stringify(v);
39
46
  }
47
+ /** Format as single-line JSON with explicit redaction policy. */
48
+ export function outputJsonWith(value, redactionPolicy) {
49
+ const v = sanitizeForJson(value);
50
+ applyRedactionPolicy(v, redactionPolicy);
51
+ return JSON.stringify(v);
52
+ }
40
53
  /** Format as multi-line YAML. Keys stripped, values formatted, secrets redacted. */
41
54
  export function outputYaml(value) {
55
+ value = sanitizeForJson(value);
42
56
  const lines = ["---"];
43
57
  renderYamlProcessed(value, 0, lines);
44
58
  return lines.join("\n");
45
59
  }
46
60
  /** Format as single-line logfmt. Keys stripped, values formatted, secrets redacted. */
47
61
  export function outputPlain(value) {
62
+ value = sanitizeForJson(value);
48
63
  const pairs = [];
49
64
  collectPlainPairs(value, "", pairs);
50
65
  pairs.sort(([a], [b]) => jcsCompare(a, b));
@@ -90,7 +105,10 @@ export function parseSize(s) {
90
105
  const n = Number(numStr);
91
106
  if (isNaN(n) || n < 0 || !isFinite(n))
92
107
  return null;
93
- return Math.trunc(n * mult);
108
+ const result = Math.trunc(n * mult);
109
+ if (!Number.isSafeInteger(result))
110
+ return null;
111
+ return result;
94
112
  }
95
113
  // ═══════════════════════════════════════════
96
114
  // Secret Redaction
@@ -118,6 +136,21 @@ function redactSecrets(value) {
118
136
  }
119
137
  }
120
138
  }
139
+ function applyRedactionPolicy(value, redactionPolicy) {
140
+ switch (redactionPolicy) {
141
+ case RedactionPolicy.RedactionTraceOnly:
142
+ if (isObject(value) && value.trace !== undefined) {
143
+ redactSecrets(value.trace);
144
+ }
145
+ break;
146
+ case RedactionPolicy.RedactionNone:
147
+ break;
148
+ default:
149
+ // Safety fallback for unknown values.
150
+ redactSecrets(value);
151
+ break;
152
+ }
153
+ }
121
154
  // ═══════════════════════════════════════════
122
155
  // Suffix Processing
123
156
  // ═══════════════════════════════════════════
@@ -473,6 +506,52 @@ function plainScalar(value) {
473
506
  // ═══════════════════════════════════════════
474
507
  // Utilities
475
508
  // ═══════════════════════════════════════════
509
+ function sanitizeForJson(value, stack = new WeakSet()) {
510
+ if (value === null)
511
+ return null;
512
+ const t = typeof value;
513
+ if (t === "string")
514
+ return value;
515
+ if (t === "boolean")
516
+ return value;
517
+ if (t === "number") {
518
+ const n = value;
519
+ return Number.isFinite(n) ? n : "<unsupported:number>";
520
+ }
521
+ if (t === "bigint")
522
+ return "<unsupported:bigint>";
523
+ if (t === "undefined")
524
+ return "<unsupported:undefined>";
525
+ if (t === "function")
526
+ return "<unsupported:function>";
527
+ if (t === "symbol")
528
+ return "<unsupported:symbol>";
529
+ if (value instanceof Error)
530
+ return value.message;
531
+ if (value instanceof Date)
532
+ return value.toISOString();
533
+ if (Array.isArray(value)) {
534
+ if (stack.has(value))
535
+ return "<unsupported:circular>";
536
+ stack.add(value);
537
+ const out = value.map((item) => sanitizeForJson(item, stack));
538
+ stack.delete(value);
539
+ return out;
540
+ }
541
+ if (typeof value === "object") {
542
+ const obj = value;
543
+ if (stack.has(obj))
544
+ return "<unsupported:circular>";
545
+ stack.add(obj);
546
+ const out = {};
547
+ for (const [k, v] of Object.entries(obj)) {
548
+ out[k] = sanitizeForJson(v, stack);
549
+ }
550
+ stack.delete(obj);
551
+ return out;
552
+ }
553
+ return "<unsupported:unknown>";
554
+ }
476
555
  function isObject(value) {
477
556
  return typeof value === "object" && value !== null && !Array.isArray(value);
478
557
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates.
3
3
  */
4
- export { buildJsonOk, buildJsonError, buildJson, outputJson, outputYaml, outputPlain, internalRedactSecrets, parseSize } from "./format.js";
4
+ export { buildJsonOk, buildJsonError, buildJson, RedactionPolicy, outputJson, outputJsonWith, outputYaml, outputPlain, internalRedactSecrets, parseSize, } from "./format.js";
5
5
  export { log, span, initJson, initPlain, initYaml } from "./afdata_logging.js";
6
6
  export { type OutputFormat, cliParseOutput, cliParseLogFilters, cliOutput, buildCliError } from "./cli.js";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates.
3
3
  */
4
- export { buildJsonOk, buildJsonError, buildJson, outputJson, outputYaml, outputPlain, internalRedactSecrets, parseSize } from "./format.js";
4
+ export { buildJsonOk, buildJsonError, buildJson, RedactionPolicy, outputJson, outputJsonWith, outputYaml, outputPlain, internalRedactSecrets, parseSize, } from "./format.js";
5
5
  export { log, span, initJson, initPlain, initYaml } from "./afdata_logging.js";
6
6
  export { cliParseOutput, cliParseLogFilters, cliOutput, buildCliError } from "./cli.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-first-data",
3
- "version": "0.4.2",
3
+ "version": "0.6.0",
4
4
  "description": "Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates for AI agents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -26,10 +26,11 @@
26
26
  ],
27
27
  "scripts": {
28
28
  "build": "tsc",
29
- "test": "node --test src/*.test.ts --experimental-strip-types"
29
+ "test": "tsx --test src/*.test.ts"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^25.2.3",
33
+ "tsx": "^4.20.6",
33
34
  "typescript": "^5.0"
34
35
  }
35
36
  }