autotel-schema 3.0.0 → 5.0.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.
Files changed (36) hide show
  1. package/README.md +39 -15
  2. package/dist/cli.cjs.map +1 -1
  3. package/dist/cli.d.cts.map +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js.map +1 -1
  6. package/dist/contract-Ymh_Q37N.d.cts.map +1 -1
  7. package/dist/contract-Ymh_Q37N.d.ts.map +1 -1
  8. package/dist/{diff-B1DoDhUn.d.ts → diff-CAIC04O4.d.ts} +4 -3
  9. package/dist/diff-CAIC04O4.d.ts.map +1 -0
  10. package/dist/{diff-Cjs6OPFN.d.cts → diff-CfvhW7ux.d.cts} +4 -3
  11. package/dist/diff-CfvhW7ux.d.cts.map +1 -0
  12. package/dist/diff.cjs.map +1 -1
  13. package/dist/diff.d.cts +1 -1
  14. package/dist/diff.d.ts +1 -1
  15. package/dist/diff.js.map +1 -1
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +2 -3
  18. package/dist/index.d.cts.map +1 -1
  19. package/dist/index.d.ts +2 -3
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/processor-BPp_DewZ.js.map +1 -1
  23. package/dist/{processor-BsO4WD73.d.cts → processor-DVcsS6Rz.d.cts} +1 -2
  24. package/dist/processor-DVcsS6Rz.d.cts.map +1 -0
  25. package/dist/processor-Gu8Yl_dS.cjs.map +1 -1
  26. package/dist/{processor-CIEfOOYi.d.ts → processor-TpuacLr5.d.ts} +1 -2
  27. package/dist/processor-TpuacLr5.d.ts.map +1 -0
  28. package/dist/processor.d.cts +1 -1
  29. package/dist/processor.d.ts +1 -1
  30. package/dist/snapshot-CyWGJaJT.cjs.map +1 -1
  31. package/dist/snapshot-h8pb_Up_.js.map +1 -1
  32. package/package.json +4 -4
  33. package/dist/diff-B1DoDhUn.d.ts.map +0 -1
  34. package/dist/diff-Cjs6OPFN.d.cts.map +0 -1
  35. package/dist/processor-BsO4WD73.d.cts.map +0 -1
  36. package/dist/processor-CIEfOOYi.d.ts.map +0 -1
package/README.md CHANGED
@@ -29,13 +29,21 @@ export const contract = defineContract({
29
29
  service: 'checkout',
30
30
  version: '1.2.0', // semver of the *contract*, not the app
31
31
  commonAttributes: {
32
- 'user.id': { type: 'string', highCardinality: true, description: 'Authenticated user' },
32
+ 'user.id': {
33
+ type: 'string',
34
+ highCardinality: true,
35
+ description: 'Authenticated user',
36
+ },
33
37
  },
34
38
  spans: {
35
39
  'checkout.charge': {
36
40
  description: 'Charge a payment method',
37
41
  attributes: {
38
- 'payment.provider': { type: 'string', required: true, enum: ['stripe', 'paypal'] },
42
+ 'payment.provider': {
43
+ type: 'string',
44
+ required: true,
45
+ enum: ['stripe', 'paypal'],
46
+ },
39
47
  'payment.amount_cents': { type: 'number', required: true },
40
48
  },
41
49
  },
@@ -71,7 +79,8 @@ const violations = validateSpan(
71
79
  contract,
72
80
  );
73
81
  // → [missing_required payment.amount_cents, enum_violation payment.provider]
74
- if (hasErrors(violations)) violations.forEach((v) => console.error(formatViolation(v)));
82
+ if (hasErrors(violations))
83
+ violations.forEach((v) => console.error(formatViolation(v)));
75
84
  ```
76
85
 
77
86
  Violation codes: `missing_required`, `type_mismatch`, `enum_violation`, `unknown_attribute` (with a "did you mean?" suggestion via edit distance), and `unknown_span`.
@@ -85,7 +94,10 @@ import { contractToSnapshot, serializeSnapshot } from 'autotel-schema';
85
94
  import { writeFileSync } from 'node:fs';
86
95
  import { contract } from './telemetry.contract';
87
96
 
88
- writeFileSync('telemetry.snapshot.json', serializeSnapshot(contractToSnapshot(contract)));
97
+ writeFileSync(
98
+ 'telemetry.snapshot.json',
99
+ serializeSnapshot(contractToSnapshot(contract)),
100
+ );
89
101
  ```
90
102
 
91
103
  Then in CI, with the bundled `autotel-schema` CLI:
@@ -100,7 +112,11 @@ autotel-schema diff telemetry.baseline.json telemetry.current.json --json
100
112
  Programmatically:
101
113
 
102
114
  ```ts
103
- import { diffSnapshots, hasBreakingChanges, formatDiff } from 'autotel-schema/diff';
115
+ import {
116
+ diffSnapshots,
117
+ hasBreakingChanges,
118
+ formatDiff,
119
+ } from 'autotel-schema/diff';
104
120
 
105
121
  const diff = diffSnapshots(baseline, current);
106
122
  if (hasBreakingChanges(diff)) throw new Error(formatDiff(diff));
@@ -117,22 +133,29 @@ import { contract } from './telemetry.contract';
117
133
 
118
134
  init({
119
135
  service: 'checkout',
120
- attributeRedactor: { allowKeys: highCardinalityKeys(contract), preset: 'strict' },
136
+ attributeRedactor: {
137
+ allowKeys: highCardinalityKeys(contract),
138
+ preset: 'strict',
139
+ },
121
140
  });
122
141
  ```
123
142
 
124
143
  ## 5. Scenario conformance: contract one exercised flow
125
144
 
126
- Spans and attributes contract your telemetry *surface*. A **scenario** contracts the *behaviour* of one exercised flow: which events must fire, how many times, in what topology — and, for async flows, **when the observation is complete**, because without a completion boundary a missing event and an event that hasn't fired *yet* are indistinguishable.
145
+ Spans and attributes contract your telemetry _surface_. A **scenario**contracts the _behaviour_ of one exercised flow: which events must fire, how many times, in what topology, and, for async flows,**when the observation is complete**, because without a completion boundary a missing event and an event that hasn't fired _yet_ are indistinguishable.
127
146
 
128
147
  ```ts
129
148
  export const contract = defineContract({
130
149
  service: 'transfer',
131
150
  version: '1.3.0',
132
- spans: { /* ... */ },
151
+ spans: {/* ... */},
133
152
  scenarios: {
134
153
  'transfer.accept': {
135
- completion: { mode: 'terminal-event', event: 'transfer.queued', observationBudgetMs: 5000 },
154
+ completion: {
155
+ mode: 'terminal-event',
156
+ event: 'transfer.queued',
157
+ observationBudgetMs: 5000,
158
+ },
136
159
  events: {
137
160
  'transfer.request': { cardinality: 'exactly 1' },
138
161
  'transfer.validate': { cardinality: 'exactly 1' },
@@ -145,7 +168,7 @@ export const contract = defineContract({
145
168
  });
146
169
  ```
147
170
 
148
- Checking yields **three** outcomes, not two — infrastructure slowness is not reported as behavioural regression:
171
+ Checking yields **three** outcomes, not two. Infrastructure slowness is not reported as behavioural regression:
149
172
 
150
173
  ```ts
151
174
  import { checkScenario, formatScenarioResult } from 'autotel-schema';
@@ -159,17 +182,18 @@ const result = await checkScenario(
159
182
  // 'conformant' — boundary closed, signature satisfied
160
183
  // 'non-conformant' — required behaviour missing or invalid (definitive)
161
184
  // 'incomplete' — boundary didn't close within the observation budget
162
- if (result.outcome !== 'conformant') throw new Error(formatScenarioResult(result));
185
+ if (result.outcome !== 'conformant')
186
+ throw new Error(formatScenarioResult(result));
163
187
  ```
164
188
 
165
189
  Semantics worth knowing:
166
190
 
167
191
  - **Absence is definitive only after closure** (closed-world). Excess is definitive immediately: an unexpected error span or an exceeded `max` cardinality fails fast while the flow is still open.
168
- - **Undeclared events are additive** — reported in `result.additions`, never a failure. Improving instrumentation must not break CI.
169
- - **The observation budget bounds how long the checker waits.** It is not a business SLO — assert deadlines separately on `result.spans`.
192
+ - **Undeclared events are additive**: reported in `result.additions`, never a failure. Improving instrumentation must not break CI.
193
+ - **The observation budget bounds how long the checker waits.** It is not a business SLO: assert deadlines separately on `result.spans`.
170
194
  - An `externally-reconciled` boundary never closes in-process: use it for phases verified by a deferred job keyed by a durable business ID.
171
195
 
172
- Don't hand-write the first draft — record it:
196
+ Don't hand-write the first draft. Record it:
173
197
 
174
198
  ```ts
175
199
  import { proposeScenario } from 'autotel-schema';
@@ -182,7 +206,7 @@ Stable events become required cardinality, variable ones get ranges flagged for
182
206
 
183
207
  ## What this is / isn't
184
208
 
185
- - **Is**: a contract for your telemetry surface: span names, attribute keys, types, enums, stability, and breaking-vs-additive evolution — and, per scenario, the behavioural signature of one exercised flow (events, cardinality, topology, completion).
209
+ - **Is**: a contract for your telemetry surface: span names, attribute keys, types, enums, stability, and breaking-vs-additive evolution: and, per scenario, the behavioural signature of one exercised flow (events, cardinality, topology, completion).
186
210
  - **Isn't**: a contract for application message payloads (use [`autotel-message-contract`](../autotel-message-contract)) or evidence that contracted interactions ran (use [`autotel-pact`](../autotel-pact)). It does not require the OpenTelemetry SDK. The processor works against structural span types.
187
211
 
188
212
  ## License
package/dist/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.cjs","names":["diffSnapshots","parseSnapshot","formatDiff","hasBreakingChanges","path"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * autotel-schema CLI — the CI gate for your telemetry's public API.\n *\n * autotel-schema diff <baseline.json> <current.json> # classify changes\n * autotel-schema check <baseline.json> <current.json> # exit 1 on breaking\n *\n * Both operate on snapshot JSON produced by `serializeSnapshot(contractToSnapshot(contract))`.\n * Commit the baseline; regenerate `current` in CI; gate the merge on `check`.\n */\n\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { parseSnapshot } from './snapshot.js';\nimport {\n diffSnapshots,\n formatDiff,\n hasBreakingChanges,\n type SnapshotDiff,\n} from './diff.js';\n\ninterface Parsed {\n command: string | undefined;\n baseline: string | undefined;\n current: string | undefined;\n json: boolean;\n}\n\nfunction parseArgs(argv: string[]): Parsed {\n const positional: string[] = [];\n let json = false;\n for (const arg of argv) {\n if (arg === '--json') json = true;\n else if (arg === '-h' || arg === '--help') positional.unshift('help');\n else positional.push(arg);\n }\n return {\n command: positional[0],\n baseline: positional[1],\n current: positional[2],\n json,\n };\n}\n\nconst USAGE = `autotel-schema — treat your trace surface like a versioned public API\n\nUsage:\n autotel-schema diff <baseline.json> <current.json> [--json]\n autotel-schema check <baseline.json> <current.json> [--json]\n\nCommands:\n diff Print every change (breaking / additive / neutral). Always exits 0.\n check Like diff, but exits 1 if any breaking change is found (CI gate).\n\nSnapshots are produced with serializeSnapshot(contractToSnapshot(contract)).`;\n\nfunction loadDiff(baseline: string, current: string): SnapshotDiff {\n const prev = parseSnapshot(readFileSync(baseline, 'utf8'));\n const next = parseSnapshot(readFileSync(current, 'utf8'));\n return diffSnapshots(prev, next);\n}\n\nfunction emit(diff: SnapshotDiff, json: boolean): void {\n if (json) {\n console.log(JSON.stringify(diff, null, 2));\n } else {\n console.log(formatDiff(diff));\n }\n}\n\nexport function run(argv: string[]): number {\n const { command, baseline, current, json } = parseArgs(argv);\n\n if (!command || command === 'help') {\n console.log(USAGE);\n return command ? 0 : 1;\n }\n\n if (command !== 'diff' && command !== 'check') {\n console.error(`autotel-schema: unknown command \"${command}\"\\n\\n${USAGE}`);\n return 1;\n }\n\n if (!baseline || !current) {\n console.error('autotel-schema: both <baseline.json> and <current.json> are required\\n');\n console.error(USAGE);\n return 1;\n }\n\n let diff: SnapshotDiff;\n try {\n diff = loadDiff(baseline, current);\n } catch (error) {\n console.error(\n `autotel-schema: ${error instanceof Error ? error.message : String(error)}`,\n );\n return 1;\n }\n\n emit(diff, json);\n\n if (command === 'check' && hasBreakingChanges(diff)) {\n console.error(\n `\\nautotel-schema: ${diff.breaking.length} breaking change(s) to the telemetry contract. ` +\n `Bump the contract major version and update the committed snapshot.`,\n );\n return 1;\n }\n return 0;\n}\n\n// Only auto-run when invoked directly as the binary, not when imported in tests.\n// Match the basename so a repo path containing \"autotel-schema\" can't trigger it.\nconst entry = process.argv[1] ? path.basename(process.argv[1]) : '';\nif (entry === 'autotel-schema' || entry === 'cli.js' || entry === 'cli.cjs') {\n process.exit(run(process.argv.slice(2)));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAS,UAAU,MAAwB;CACzC,MAAM,aAAuB,CAAC;CAC9B,IAAI,OAAO;CACX,KAAK,MAAM,OAAO,MAChB,IAAI,QAAQ,UAAU,OAAO;MACxB,IAAI,QAAQ,QAAQ,QAAQ,UAAU,WAAW,QAAQ,MAAM;MAC/D,WAAW,KAAK,GAAG;CAE1B,OAAO;EACL,SAAS,WAAW;EACpB,UAAU,WAAW;EACrB,SAAS,WAAW;EACpB;CACF;AACF;AAEA,MAAM,QAAQ;;;;;;;;;;;AAYd,SAAS,SAAS,UAAkB,SAA+B;CAGjE,OAAOA,2BAFMC,yDAA2B,UAAU,MAAM,CAEhC,GADXA,yDAA2B,SAAS,MAAM,CACzB,CAAC;AACjC;AAEA,SAAS,KAAK,MAAoB,MAAqB;CACrD,IAAI,MACF,QAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;MAEzC,QAAQ,IAAIC,wBAAW,IAAI,CAAC;AAEhC;AAEA,SAAgB,IAAI,MAAwB;CAC1C,MAAM,EAAE,SAAS,UAAU,SAAS,SAAS,UAAU,IAAI;CAE3D,IAAI,CAAC,WAAW,YAAY,QAAQ;EAClC,QAAQ,IAAI,KAAK;EACjB,OAAO,UAAU,IAAI;CACvB;CAEA,IAAI,YAAY,UAAU,YAAY,SAAS;EAC7C,QAAQ,MAAM,oCAAoC,QAAQ,OAAO,OAAO;EACxE,OAAO;CACT;CAEA,IAAI,CAAC,YAAY,CAAC,SAAS;EACzB,QAAQ,MAAM,wEAAwE;EACtF,QAAQ,MAAM,KAAK;EACnB,OAAO;CACT;CAEA,IAAI;CACJ,IAAI;EACF,OAAO,SAAS,UAAU,OAAO;CACnC,SAAS,OAAO;EACd,QAAQ,MACN,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC1E;EACA,OAAO;CACT;CAEA,KAAK,MAAM,IAAI;CAEf,IAAI,YAAY,WAAWC,gCAAmB,IAAI,GAAG;EACnD,QAAQ,MACN,qBAAqB,KAAK,SAAS,OAAO,kHAE5C;EACA,OAAO;CACT;CACA,OAAO;AACT;AAIA,MAAM,QAAQ,QAAQ,KAAK,KAAKC,kBAAK,SAAS,QAAQ,KAAK,EAAE,IAAI;AACjE,IAAI,UAAU,oBAAoB,UAAU,YAAY,UAAU,WAChE,QAAQ,KAAK,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.cjs","names":["diffSnapshots","parseSnapshot","formatDiff","hasBreakingChanges","path"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * autotel-schema CLI — the CI gate for your telemetry's public API.\n *\n * autotel-schema diff <baseline.json> <current.json> # classify changes\n * autotel-schema check <baseline.json> <current.json> # exit 1 on breaking\n *\n * Both operate on snapshot JSON produced by `serializeSnapshot(contractToSnapshot(contract))`.\n * Commit the baseline; regenerate `current` in CI; gate the merge on `check`.\n */\n\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { parseSnapshot } from './snapshot.js';\nimport {\n diffSnapshots,\n formatDiff,\n hasBreakingChanges,\n type SnapshotDiff,\n} from './diff.js';\n\ninterface Parsed {\n command: string | undefined;\n baseline: string | undefined;\n current: string | undefined;\n json: boolean;\n}\n\nfunction parseArgs(argv: string[]): Parsed {\n const positional: string[] = [];\n let json = false;\n for (const arg of argv) {\n if (arg === '--json') json = true;\n else if (arg === '-h' || arg === '--help') positional.unshift('help');\n else positional.push(arg);\n }\n return {\n command: positional[0],\n baseline: positional[1],\n current: positional[2],\n json,\n };\n}\n\nconst USAGE = `autotel-schema — treat your trace surface like a versioned public API\n\nUsage:\n autotel-schema diff <baseline.json> <current.json> [--json]\n autotel-schema check <baseline.json> <current.json> [--json]\n\nCommands:\n diff Print every change (breaking / additive / neutral). Always exits 0.\n check Like diff, but exits 1 if any breaking change is found (CI gate).\n\nSnapshots are produced with serializeSnapshot(contractToSnapshot(contract)).`;\n\nfunction loadDiff(baseline: string, current: string): SnapshotDiff {\n const prev = parseSnapshot(readFileSync(baseline, 'utf8'));\n const next = parseSnapshot(readFileSync(current, 'utf8'));\n return diffSnapshots(prev, next);\n}\n\nfunction emit(diff: SnapshotDiff, json: boolean): void {\n if (json) {\n console.log(JSON.stringify(diff, null, 2));\n } else {\n console.log(formatDiff(diff));\n }\n}\n\nexport function run(argv: string[]): number {\n const { command, baseline, current, json } = parseArgs(argv);\n\n if (!command || command === 'help') {\n console.log(USAGE);\n return command ? 0 : 1;\n }\n\n if (command !== 'diff' && command !== 'check') {\n console.error(`autotel-schema: unknown command \"${command}\"\\n\\n${USAGE}`);\n return 1;\n }\n\n if (!baseline || !current) {\n console.error(\n 'autotel-schema: both <baseline.json> and <current.json> are required\\n',\n );\n console.error(USAGE);\n return 1;\n }\n\n let diff: SnapshotDiff;\n try {\n diff = loadDiff(baseline, current);\n } catch (error) {\n console.error(\n `autotel-schema: ${error instanceof Error ? error.message : String(error)}`,\n );\n return 1;\n }\n\n emit(diff, json);\n\n if (command === 'check' && hasBreakingChanges(diff)) {\n console.error(\n `\\nautotel-schema: ${diff.breaking.length} breaking change(s) to the telemetry contract. ` +\n `Bump the contract major version and update the committed snapshot.`,\n );\n return 1;\n }\n return 0;\n}\n\n// Only auto-run when invoked directly as the binary, not when imported in tests.\n// Match the basename so a repo path containing \"autotel-schema\" can't trigger it.\nconst entry = process.argv[1] ? path.basename(process.argv[1]) : '';\nif (entry === 'autotel-schema' || entry === 'cli.js' || entry === 'cli.cjs') {\n process.exit(run(process.argv.slice(2)));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAS,UAAU,MAAwB;CACzC,MAAM,aAAuB,CAAC;CAC9B,IAAI,OAAO;CACX,KAAK,MAAM,OAAO,MAChB,IAAI,QAAQ,UAAU,OAAO;MACxB,IAAI,QAAQ,QAAQ,QAAQ,UAAU,WAAW,QAAQ,MAAM;MAC/D,WAAW,KAAK,GAAG;CAE1B,OAAO;EACL,SAAS,WAAW;EACpB,UAAU,WAAW;EACrB,SAAS,WAAW;EACpB;CACF;AACF;AAEA,MAAM,QAAQ;;;;;;;;;;;AAYd,SAAS,SAAS,UAAkB,SAA+B;CAGjE,OAAOA,2BAFMC,yDAA2B,UAAU,MAAM,CAEhC,GADXA,yDAA2B,SAAS,MAAM,CACzB,CAAC;AACjC;AAEA,SAAS,KAAK,MAAoB,MAAqB;CACrD,IAAI,MACF,QAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;MAEzC,QAAQ,IAAIC,wBAAW,IAAI,CAAC;AAEhC;AAEA,SAAgB,IAAI,MAAwB;CAC1C,MAAM,EAAE,SAAS,UAAU,SAAS,SAAS,UAAU,IAAI;CAE3D,IAAI,CAAC,WAAW,YAAY,QAAQ;EAClC,QAAQ,IAAI,KAAK;EACjB,OAAO,UAAU,IAAI;CACvB;CAEA,IAAI,YAAY,UAAU,YAAY,SAAS;EAC7C,QAAQ,MAAM,oCAAoC,QAAQ,OAAO,OAAO;EACxE,OAAO;CACT;CAEA,IAAI,CAAC,YAAY,CAAC,SAAS;EACzB,QAAQ,MACN,wEACF;EACA,QAAQ,MAAM,KAAK;EACnB,OAAO;CACT;CAEA,IAAI;CACJ,IAAI;EACF,OAAO,SAAS,UAAU,OAAO;CACnC,SAAS,OAAO;EACd,QAAQ,MACN,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC1E;EACA,OAAO;CACT;CAEA,KAAK,MAAM,IAAI;CAEf,IAAI,YAAY,WAAWC,gCAAmB,IAAI,GAAG;EACnD,QAAQ,MACN,qBAAqB,KAAK,SAAS,OAAO,kHAE5C;EACA,OAAO;CACT;CACA,OAAO;AACT;AAIA,MAAM,QAAQ,QAAQ,KAAK,KAAKC,kBAAK,SAAS,QAAQ,KAAK,EAAE,IAAI;AACjE,IAAI,UAAU,oBAAoB,UAAU,YAAY,UAAU,WAChE,QAAQ,KAAK,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.cts","names":[],"sources":["../src/cli.ts"],"mappings":";AAsEA;;;;AAAkC;;;;;AAAlC,iBAAgB,GAAA,CAAI,IAAc"}
1
+ {"version":3,"file":"cli.d.cts","names":[],"sources":["../src/cli.ts"],"mappings":";;;;;;;;;;iBAsEgB,IAAI"}
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","names":[],"sources":["../src/cli.ts"],"mappings":";AAsEA;;;;AAAkC;;;;;AAAlC,iBAAgB,GAAA,CAAI,IAAc"}
1
+ {"version":3,"file":"cli.d.ts","names":[],"sources":["../src/cli.ts"],"mappings":";;;;;;;;;;iBAsEgB,IAAI"}
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * autotel-schema CLI — the CI gate for your telemetry's public API.\n *\n * autotel-schema diff <baseline.json> <current.json> # classify changes\n * autotel-schema check <baseline.json> <current.json> # exit 1 on breaking\n *\n * Both operate on snapshot JSON produced by `serializeSnapshot(contractToSnapshot(contract))`.\n * Commit the baseline; regenerate `current` in CI; gate the merge on `check`.\n */\n\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { parseSnapshot } from './snapshot.js';\nimport {\n diffSnapshots,\n formatDiff,\n hasBreakingChanges,\n type SnapshotDiff,\n} from './diff.js';\n\ninterface Parsed {\n command: string | undefined;\n baseline: string | undefined;\n current: string | undefined;\n json: boolean;\n}\n\nfunction parseArgs(argv: string[]): Parsed {\n const positional: string[] = [];\n let json = false;\n for (const arg of argv) {\n if (arg === '--json') json = true;\n else if (arg === '-h' || arg === '--help') positional.unshift('help');\n else positional.push(arg);\n }\n return {\n command: positional[0],\n baseline: positional[1],\n current: positional[2],\n json,\n };\n}\n\nconst USAGE = `autotel-schema — treat your trace surface like a versioned public API\n\nUsage:\n autotel-schema diff <baseline.json> <current.json> [--json]\n autotel-schema check <baseline.json> <current.json> [--json]\n\nCommands:\n diff Print every change (breaking / additive / neutral). Always exits 0.\n check Like diff, but exits 1 if any breaking change is found (CI gate).\n\nSnapshots are produced with serializeSnapshot(contractToSnapshot(contract)).`;\n\nfunction loadDiff(baseline: string, current: string): SnapshotDiff {\n const prev = parseSnapshot(readFileSync(baseline, 'utf8'));\n const next = parseSnapshot(readFileSync(current, 'utf8'));\n return diffSnapshots(prev, next);\n}\n\nfunction emit(diff: SnapshotDiff, json: boolean): void {\n if (json) {\n console.log(JSON.stringify(diff, null, 2));\n } else {\n console.log(formatDiff(diff));\n }\n}\n\nexport function run(argv: string[]): number {\n const { command, baseline, current, json } = parseArgs(argv);\n\n if (!command || command === 'help') {\n console.log(USAGE);\n return command ? 0 : 1;\n }\n\n if (command !== 'diff' && command !== 'check') {\n console.error(`autotel-schema: unknown command \"${command}\"\\n\\n${USAGE}`);\n return 1;\n }\n\n if (!baseline || !current) {\n console.error('autotel-schema: both <baseline.json> and <current.json> are required\\n');\n console.error(USAGE);\n return 1;\n }\n\n let diff: SnapshotDiff;\n try {\n diff = loadDiff(baseline, current);\n } catch (error) {\n console.error(\n `autotel-schema: ${error instanceof Error ? error.message : String(error)}`,\n );\n return 1;\n }\n\n emit(diff, json);\n\n if (command === 'check' && hasBreakingChanges(diff)) {\n console.error(\n `\\nautotel-schema: ${diff.breaking.length} breaking change(s) to the telemetry contract. ` +\n `Bump the contract major version and update the committed snapshot.`,\n );\n return 1;\n }\n return 0;\n}\n\n// Only auto-run when invoked directly as the binary, not when imported in tests.\n// Match the basename so a repo path containing \"autotel-schema\" can't trigger it.\nconst entry = process.argv[1] ? path.basename(process.argv[1]) : '';\nif (entry === 'autotel-schema' || entry === 'cli.js' || entry === 'cli.cjs') {\n process.exit(run(process.argv.slice(2)));\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA4BA,SAAS,UAAU,MAAwB;CACzC,MAAM,aAAuB,CAAC;CAC9B,IAAI,OAAO;CACX,KAAK,MAAM,OAAO,MAChB,IAAI,QAAQ,UAAU,OAAO;MACxB,IAAI,QAAQ,QAAQ,QAAQ,UAAU,WAAW,QAAQ,MAAM;MAC/D,WAAW,KAAK,GAAG;CAE1B,OAAO;EACL,SAAS,WAAW;EACpB,UAAU,WAAW;EACrB,SAAS,WAAW;EACpB;CACF;AACF;AAEA,MAAM,QAAQ;;;;;;;;;;;AAYd,SAAS,SAAS,UAAkB,SAA+B;CAGjE,OAAO,cAFM,cAAc,aAAa,UAAU,MAAM,CAEhC,GADX,cAAc,aAAa,SAAS,MAAM,CACzB,CAAC;AACjC;AAEA,SAAS,KAAK,MAAoB,MAAqB;CACrD,IAAI,MACF,QAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;MAEzC,QAAQ,IAAI,WAAW,IAAI,CAAC;AAEhC;AAEA,SAAgB,IAAI,MAAwB;CAC1C,MAAM,EAAE,SAAS,UAAU,SAAS,SAAS,UAAU,IAAI;CAE3D,IAAI,CAAC,WAAW,YAAY,QAAQ;EAClC,QAAQ,IAAI,KAAK;EACjB,OAAO,UAAU,IAAI;CACvB;CAEA,IAAI,YAAY,UAAU,YAAY,SAAS;EAC7C,QAAQ,MAAM,oCAAoC,QAAQ,OAAO,OAAO;EACxE,OAAO;CACT;CAEA,IAAI,CAAC,YAAY,CAAC,SAAS;EACzB,QAAQ,MAAM,wEAAwE;EACtF,QAAQ,MAAM,KAAK;EACnB,OAAO;CACT;CAEA,IAAI;CACJ,IAAI;EACF,OAAO,SAAS,UAAU,OAAO;CACnC,SAAS,OAAO;EACd,QAAQ,MACN,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC1E;EACA,OAAO;CACT;CAEA,KAAK,MAAM,IAAI;CAEf,IAAI,YAAY,WAAW,mBAAmB,IAAI,GAAG;EACnD,QAAQ,MACN,qBAAqB,KAAK,SAAS,OAAO,kHAE5C;EACA,OAAO;CACT;CACA,OAAO;AACT;AAIA,MAAM,QAAQ,QAAQ,KAAK,KAAK,KAAK,SAAS,QAAQ,KAAK,EAAE,IAAI;AACjE,IAAI,UAAU,oBAAoB,UAAU,YAAY,UAAU,WAChE,QAAQ,KAAK,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * autotel-schema CLI — the CI gate for your telemetry's public API.\n *\n * autotel-schema diff <baseline.json> <current.json> # classify changes\n * autotel-schema check <baseline.json> <current.json> # exit 1 on breaking\n *\n * Both operate on snapshot JSON produced by `serializeSnapshot(contractToSnapshot(contract))`.\n * Commit the baseline; regenerate `current` in CI; gate the merge on `check`.\n */\n\nimport { readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { parseSnapshot } from './snapshot.js';\nimport {\n diffSnapshots,\n formatDiff,\n hasBreakingChanges,\n type SnapshotDiff,\n} from './diff.js';\n\ninterface Parsed {\n command: string | undefined;\n baseline: string | undefined;\n current: string | undefined;\n json: boolean;\n}\n\nfunction parseArgs(argv: string[]): Parsed {\n const positional: string[] = [];\n let json = false;\n for (const arg of argv) {\n if (arg === '--json') json = true;\n else if (arg === '-h' || arg === '--help') positional.unshift('help');\n else positional.push(arg);\n }\n return {\n command: positional[0],\n baseline: positional[1],\n current: positional[2],\n json,\n };\n}\n\nconst USAGE = `autotel-schema — treat your trace surface like a versioned public API\n\nUsage:\n autotel-schema diff <baseline.json> <current.json> [--json]\n autotel-schema check <baseline.json> <current.json> [--json]\n\nCommands:\n diff Print every change (breaking / additive / neutral). Always exits 0.\n check Like diff, but exits 1 if any breaking change is found (CI gate).\n\nSnapshots are produced with serializeSnapshot(contractToSnapshot(contract)).`;\n\nfunction loadDiff(baseline: string, current: string): SnapshotDiff {\n const prev = parseSnapshot(readFileSync(baseline, 'utf8'));\n const next = parseSnapshot(readFileSync(current, 'utf8'));\n return diffSnapshots(prev, next);\n}\n\nfunction emit(diff: SnapshotDiff, json: boolean): void {\n if (json) {\n console.log(JSON.stringify(diff, null, 2));\n } else {\n console.log(formatDiff(diff));\n }\n}\n\nexport function run(argv: string[]): number {\n const { command, baseline, current, json } = parseArgs(argv);\n\n if (!command || command === 'help') {\n console.log(USAGE);\n return command ? 0 : 1;\n }\n\n if (command !== 'diff' && command !== 'check') {\n console.error(`autotel-schema: unknown command \"${command}\"\\n\\n${USAGE}`);\n return 1;\n }\n\n if (!baseline || !current) {\n console.error(\n 'autotel-schema: both <baseline.json> and <current.json> are required\\n',\n );\n console.error(USAGE);\n return 1;\n }\n\n let diff: SnapshotDiff;\n try {\n diff = loadDiff(baseline, current);\n } catch (error) {\n console.error(\n `autotel-schema: ${error instanceof Error ? error.message : String(error)}`,\n );\n return 1;\n }\n\n emit(diff, json);\n\n if (command === 'check' && hasBreakingChanges(diff)) {\n console.error(\n `\\nautotel-schema: ${diff.breaking.length} breaking change(s) to the telemetry contract. ` +\n `Bump the contract major version and update the committed snapshot.`,\n );\n return 1;\n }\n return 0;\n}\n\n// Only auto-run when invoked directly as the binary, not when imported in tests.\n// Match the basename so a repo path containing \"autotel-schema\" can't trigger it.\nconst entry = process.argv[1] ? path.basename(process.argv[1]) : '';\nif (entry === 'autotel-schema' || entry === 'cli.js' || entry === 'cli.cjs') {\n process.exit(run(process.argv.slice(2)));\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA4BA,SAAS,UAAU,MAAwB;CACzC,MAAM,aAAuB,CAAC;CAC9B,IAAI,OAAO;CACX,KAAK,MAAM,OAAO,MAChB,IAAI,QAAQ,UAAU,OAAO;MACxB,IAAI,QAAQ,QAAQ,QAAQ,UAAU,WAAW,QAAQ,MAAM;MAC/D,WAAW,KAAK,GAAG;CAE1B,OAAO;EACL,SAAS,WAAW;EACpB,UAAU,WAAW;EACrB,SAAS,WAAW;EACpB;CACF;AACF;AAEA,MAAM,QAAQ;;;;;;;;;;;AAYd,SAAS,SAAS,UAAkB,SAA+B;CAGjE,OAAO,cAFM,cAAc,aAAa,UAAU,MAAM,CAEhC,GADX,cAAc,aAAa,SAAS,MAAM,CACzB,CAAC;AACjC;AAEA,SAAS,KAAK,MAAoB,MAAqB;CACrD,IAAI,MACF,QAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;MAEzC,QAAQ,IAAI,WAAW,IAAI,CAAC;AAEhC;AAEA,SAAgB,IAAI,MAAwB;CAC1C,MAAM,EAAE,SAAS,UAAU,SAAS,SAAS,UAAU,IAAI;CAE3D,IAAI,CAAC,WAAW,YAAY,QAAQ;EAClC,QAAQ,IAAI,KAAK;EACjB,OAAO,UAAU,IAAI;CACvB;CAEA,IAAI,YAAY,UAAU,YAAY,SAAS;EAC7C,QAAQ,MAAM,oCAAoC,QAAQ,OAAO,OAAO;EACxE,OAAO;CACT;CAEA,IAAI,CAAC,YAAY,CAAC,SAAS;EACzB,QAAQ,MACN,wEACF;EACA,QAAQ,MAAM,KAAK;EACnB,OAAO;CACT;CAEA,IAAI;CACJ,IAAI;EACF,OAAO,SAAS,UAAU,OAAO;CACnC,SAAS,OAAO;EACd,QAAQ,MACN,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC1E;EACA,OAAO;CACT;CAEA,KAAK,MAAM,IAAI;CAEf,IAAI,YAAY,WAAW,mBAAmB,IAAI,GAAG;EACnD,QAAQ,MACN,qBAAqB,KAAK,SAAS,OAAO,kHAE5C;EACA,OAAO;CACT;CACA,OAAO;AACT;AAIA,MAAM,QAAQ,QAAQ,KAAK,KAAK,KAAK,SAAS,QAAQ,KAAK,EAAE,IAAI;AACjE,IAAI,UAAU,oBAAoB,UAAU,YAAY,UAAU,WAChE,QAAQ,KAAK,IAAI,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"contract-Ymh_Q37N.d.cts","names":[],"sources":["../src/scenario.ts","../src/contract.ts"],"mappings":"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"}
1
+ {"version":3,"file":"contract-Ymh_Q37N.d.cts","names":[],"sources":["../src/scenario.ts","../src/contract.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAyCiB;EACf;EACA;EACA;EACA;EACA,aAAa;;EAEb;EACA;;;UAIe;EACf;EACA;;;;;;iBAOc,iBAAiB,gBAAgB,cAAc;;UAmC9C;;EAEf,uBAAuB;;;;;EAKvB;EACA;;;;;;;;;;KAWU;EACN;EAA0B;;EAC1B;EAAwB;EAAe;;EACvC;EAA+B;;;UAGpB;EACf;;EAEA,YAAY;;EAEZ,QAAQ,eAAe;;;;;;EAMvB,QAAQ;;EAER,gBAAgB;;KAGN;KAEA;;UAOK;EACf,MAAM;EACN;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA;;UAGe;EACf;EACA,SAAS;;EAET;EACA,YAAY;EACZ,WAAW;;EAEX,OAAO;;;;;;iBAmBO,qBAAqB,cAAc,MAAM;;iBA8DzC,iBACd,MAAM,cACN,gBAAgB;UAgCD;;EAEf;;EAEA;;;;;;;;;iBAUc,iBACd,MAAM,cACN,gBAAgB,gBAChB,UAAU,0BACT;UA0Fc;;EAEf;;EAEA;;EAEA;;;;;;;;;;;;;;;iBAgBoB,cACpB,MAAM,cACN,yBAAyB,iBAAiB,iBAAiB,iBAC3D,UAAU,uBACT,QAAQ;;iBAkDK,qBAAqB,QAAQ;UAS5B;EACf,UAAU;;EAEV;;;;;;;;iBASc,gBACd,MAAM,cAAc,cAAc,gBAClC;EAAY;IACX;;;;KCxdS;cAGC,0BAA0B;;;;;;KAc3B;cAEC,sBAAsB;;UAOlB;;EAEf,MAAM;;EAEN,YAAY;;EAEZ;;EAEA;;;;;;;EAOA;;EAEA;;EAEA;;EAEA;;;UAIe;;EAEf;;EAEA,YAAY;;EAEZ,aAAa,eAAe;;;;;EAK5B;;;UAIe;;EAEf;;;;;;EAMA;;EAEA,OAAO,eAAe;;EAEtB,mBAAmB,eAAe;;;;;EAKlC;;;;;;EAMA,YAAY,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiEb,eAAe,UAAU,oBAAoB;;;;;;iBAwC7C,qBACd,UAAU,mBACV,kBACA,cACC;;iBAQa,2BACd,UAAU,mBACV"}
@@ -1 +1 @@
1
- {"version":3,"file":"contract-Ymh_Q37N.d.ts","names":[],"sources":["../src/scenario.ts","../src/contract.ts"],"mappings":";;AAyCA;;;;;;;;;;;;;AAQY;AAIZ;;;;AAEK;AAOL;;;;;;;;AAA0E;AAgC1E;;;;;;;;;AAQa;AAWb;AAAA,UAxEiB,YAAA;EACf,MAAA;EACA,YAAA;EACA,IAAA;EACA,MAAA;EACA,UAAA,GAAa,MAAM;EAqES;EAnE5B,WAAA;EACA,UAAA;AAAA;;UAIe,WAAA;EACf,GAAA;EACA,GAAG;AAAA;;;;;iBAOW,gBAAA,CAAiB,KAAA,WAAgB,WAAA,GAAc,WAAW;;UAgCzD,iBAAA;EA0Bf;EAxBA,WAAA,YAAuB,WAAW;EA0BtB;;;;EArBZ,MAAA;EACA,WAAA;AAAA;;;AA8B6B;AAG/B;;;;AAA2B;KAtBf,kBAAA;EACN,IAAA;EAA0B,mBAAA;AAAA;EAC1B,IAAA;EAAwB,KAAA;EAAe,mBAAA;AAAA;EACvC,IAAA;EAA+B,wBAAA;AAAA;;UAGpB,YAAA;EACf,WAAA;EA4BO;EA1BP,UAAA,EAAY,kBAAA;EA8BG;EA5Bf,MAAA,EAAQ,MAAA,SAAe,iBAAA;;;;;;EAMvB,KAAA,GAAQ,aAAA;EA0BD;EAxBP,aAAA,GAAgB,aAAA;AAAA;AAAA,KAGN,eAAA;AAAA,KAEA,qBAAA;;UAOK,iBAAA;EACf,IAAA,EAAM,qBAAqB;EAC3B,KAAA;EACA,IAAA;EACA,OAAA;AAAA;;UAIe,gBAAA;EACf,IAAA;EACA,KAAA;EACA,KAAA;EACA,OAAA;AAAA;AAAA,UAGe,cAAA;EACf,QAAA;EACA,OAAA,EAAS,eAAA;EAMU;EAJnB,MAAA;EACA,UAAA,EAAY,iBAAA;EACZ,SAAA,EAAW,gBAAA;EAqBwD;EAnBnE,KAAA,EAAO,YAAA;AAAA;;;AAmB4D;AA0DrE;iBA1DgB,oBAAA,CAAqB,IAAA,UAAc,IAAA,EAAM,YAAY;;iBA0DrD,gBAAA,CACd,IAAA,EAAM,YAAA,EACN,KAAA,WAAgB,YAAY;AAAA,UAgCb,uBAAA;EAjCf;EAmCA,IAAA;EAlCA;EAoCA,MAAM;AAAA;AAJR;;;;AAIQ;AAUR;;AAdA,iBAcgB,gBAAA,CACd,IAAA,EAAM,YAAA,EACN,KAAA,WAAgB,YAAA,IAChB,OAAA,GAAU,uBAAA,GACT,cAAA;AAAA,UA4Fc,oBAAA;EA9FC;EAgGhB,IAAA;EA9FC;EAgGD,cAAA;EAhGe;EAkGf,QAAA;AAAA;;;;;;;AAlGe;AA4FjB;;;;;;iBAsBsB,aAAA,CACpB,IAAA,EAAM,YAAA,EACN,QAAA,iBAAyB,YAAA,KAAiB,OAAA,UAAiB,YAAA,KAC3D,OAAA,GAAU,oBAAA,GACT,OAAA,CAAQ,cAAA;;iBAmDK,oBAAA,CAAqB,MAAsB,EAAd,cAAc;AAAA,UAS1C,gBAAA;EACf,QAAA,EAAU,YAAY;;EAEtB,KAAA;AAAA;;;;;;;iBASc,eAAA,CACd,IAAA,EAAM,aAAA,CAAc,aAAA,CAAc,YAAA,IAClC,OAAA;EAAY,IAAA;AAAA,IACX,gBAAA;;;;KCpdS,aAAA;AAAA,cAQC,eAAA,WAA0B,aAAa;;;;;;KAcxC,SAAA;AAAA,cAEC,WAAA,WAAsB,SAAS;ADiB8B;AAAA,UCVzD,aAAA;ED0CiB;ECxChC,IAAA,EAAM,aAAA;ED0C4B;ECxClC,SAAA,GAAY,SAAS;EDwCE;ECtCvB,QAAA;ED4CA;EC1CA,WAAA;ED0CW;AAWb;;;;;EC9CE,eAAA;EDgDI;EC9CJ,IAAA;ED8C2C;EC5C3C,UAAA;ED6CmC;EC3CnC,gBAAA;AAAA;AD8CF;AAAA,UC1CiB,QAAA;;EAEf,WAAA;ED6CuB;EC3CvB,SAAA,GAAY,SAAA;EDiDJ;EC/CR,UAAA,GAAa,MAAA,SAAe,aAAA;EDiDC;;;;EC5C7B,oBAAA;AAAA;;UAIe,iBAAA;EDsCf;ECpCA,OAAA;EDsCA;;;AAA6B;AAG/B;ECnCE,OAAA;;EAEA,KAAA,EAAO,MAAA,SAAe,QAAA;EDiCG;EC/BzB,gBAAA,GAAmB,MAAA,SAAe,aAAA;EDiCH;;;AAAA;EC5B/B,oBAAA;EDmCgC;;;;;EC7BhC,SAAA,GAAY,MAAA,SAAe,YAAA;AAAA;;;ADiCpB;AAIT;;;;;;;;;AAIS;AAGT;;;;;;;;;;;;iBCqBgB,cAAA,CAAe,QAAA,EAAU,iBAAA,GAAoB,iBAAiB;;;;;;iBAwC9D,oBAAA,CACd,QAAA,EAAU,iBAAA,EACV,QAAA,UACA,GAAA,WACC,aAAa;;iBAQA,0BAAA,CACd,QAAA,EAAU,iBAAiB,EAC3B,QAAA"}
1
+ {"version":3,"file":"contract-Ymh_Q37N.d.ts","names":[],"sources":["../src/scenario.ts","../src/contract.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAyCiB;EACf;EACA;EACA;EACA;EACA,aAAa;;EAEb;EACA;;;UAIe;EACf;EACA;;;;;;iBAOc,iBAAiB,gBAAgB,cAAc;;UAmC9C;;EAEf,uBAAuB;;;;;EAKvB;EACA;;;;;;;;;;KAWU;EACN;EAA0B;;EAC1B;EAAwB;EAAe;;EACvC;EAA+B;;;UAGpB;EACf;;EAEA,YAAY;;EAEZ,QAAQ,eAAe;;;;;;EAMvB,QAAQ;;EAER,gBAAgB;;KAGN;KAEA;;UAOK;EACf,MAAM;EACN;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA;;UAGe;EACf;EACA,SAAS;;EAET;EACA,YAAY;EACZ,WAAW;;EAEX,OAAO;;;;;;iBAmBO,qBAAqB,cAAc,MAAM;;iBA8DzC,iBACd,MAAM,cACN,gBAAgB;UAgCD;;EAEf;;EAEA;;;;;;;;;iBAUc,iBACd,MAAM,cACN,gBAAgB,gBAChB,UAAU,0BACT;UA0Fc;;EAEf;;EAEA;;EAEA;;;;;;;;;;;;;;;iBAgBoB,cACpB,MAAM,cACN,yBAAyB,iBAAiB,iBAAiB,iBAC3D,UAAU,uBACT,QAAQ;;iBAkDK,qBAAqB,QAAQ;UAS5B;EACf,UAAU;;EAEV;;;;;;;;iBASc,gBACd,MAAM,cAAc,cAAc,gBAClC;EAAY;IACX;;;;KCxdS;cAGC,0BAA0B;;;;;;KAc3B;cAEC,sBAAsB;;UAOlB;;EAEf,MAAM;;EAEN,YAAY;;EAEZ;;EAEA;;;;;;;EAOA;;EAEA;;EAEA;;EAEA;;;UAIe;;EAEf;;EAEA,YAAY;;EAEZ,aAAa,eAAe;;;;;EAK5B;;;UAIe;;EAEf;;;;;;EAMA;;EAEA,OAAO,eAAe;;EAEtB,mBAAmB,eAAe;;;;;EAKlC;;;;;;EAMA,YAAY,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiEb,eAAe,UAAU,oBAAoB;;;;;;iBAwC7C,qBACd,UAAU,mBACV,kBACA,cACC;;iBAQa,2BACd,UAAU,mBACV"}
@@ -1,5 +1,4 @@
1
1
  import { o as Stability, r as AttributeType, s as TelemetryContract } from "./contract-Ymh_Q37N.js";
2
-
3
2
  //#region src/attrs.d.ts
4
3
  /**
5
4
  * Wire constants for the schema contract — the keys autotel-schema reads from
@@ -13,7 +12,9 @@ import { o as Stability, r as AttributeType, s as TelemetryContract } from "./co
13
12
  * between "confidently correct" and "confidently wrong" after a rename.
14
13
  */
15
14
  declare const SCHEMA_ATTRS: {
16
- /** The service this contract describes (mirrors `service.name`). */readonly SERVICE: "telemetry.schema.service"; /** Semver of the telemetry contract that produced this span. */
15
+ /** The service this contract describes (mirrors `service.name`). */
16
+ readonly SERVICE: "telemetry.schema.service";
17
+ /** Semver of the telemetry contract that produced this span. */
17
18
  readonly VERSION: "telemetry.schema.version";
18
19
  };
19
20
  type SchemaAttributeKey = (typeof SCHEMA_ATTRS)[keyof typeof SCHEMA_ATTRS];
@@ -86,4 +87,4 @@ declare function hasBreakingChanges(diff: SnapshotDiff): boolean;
86
87
  declare function formatDiff(diff: SnapshotDiff): string;
87
88
  //#endregion
88
89
  export { diffSnapshots as a, ContractSnapshot as c, contractToSnapshot as d, parseSnapshot as f, SchemaAttributeKey as g, SNAPSHOT_SPEC as h, SnapshotDiff as i, SnapshotAttribute as l, SCHEMA_ATTRS as m, ChangeType as n, formatDiff as o, serializeSnapshot as p, SnapshotChange as r, hasBreakingChanges as s, ChangeKind as t, SnapshotSpan as u };
89
- //# sourceMappingURL=diff-B1DoDhUn.d.ts.map
90
+ //# sourceMappingURL=diff-CAIC04O4.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-CAIC04O4.d.ts","names":[],"sources":["../src/attrs.ts","../src/snapshot.ts","../src/diff.ts"],"mappings":";;;;;;;;;;;;;cAYa;;WAAA;;WAAA;;KAOD,6BACF,2BAA2B;;cAGxB;;;;UCPI;EACf,MAAM;EACN,WAAW;EACX;EACA;EACA;EACA;EACA;;UAGe;EACf,WAAW;EACX;EACA;EACA,YAAY,eAAe;;;UAIZ;EACf,aAAa;EACb;EACA;EACA,kBAAkB,eAAe;EACjC,OAAO,eAAe;;;;;;;iBA6BR,mBACd,UAAU,oBACT;;iBAgCa,kBAAkB,UAAU;;iBAK5B,cAAc,eAAe;;;KC/FjC;KAEA;UAeK;EACf,MAAM;EACN,MAAM;;EAEN;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA,UAAU;EACV,UAAU;EACV,SAAS;;;;;;iBAuKK,cACd,UAAU,kBACV,MAAM,mBACL;;iBA6Ca,mBAAmB,MAAM;;iBAKzB,WAAW,MAAM"}
@@ -1,5 +1,4 @@
1
1
  import { o as Stability, r as AttributeType, s as TelemetryContract } from "./contract-Ymh_Q37N.cjs";
2
-
3
2
  //#region src/attrs.d.ts
4
3
  /**
5
4
  * Wire constants for the schema contract — the keys autotel-schema reads from
@@ -13,7 +12,9 @@ import { o as Stability, r as AttributeType, s as TelemetryContract } from "./co
13
12
  * between "confidently correct" and "confidently wrong" after a rename.
14
13
  */
15
14
  declare const SCHEMA_ATTRS: {
16
- /** The service this contract describes (mirrors `service.name`). */readonly SERVICE: "telemetry.schema.service"; /** Semver of the telemetry contract that produced this span. */
15
+ /** The service this contract describes (mirrors `service.name`). */
16
+ readonly SERVICE: "telemetry.schema.service";
17
+ /** Semver of the telemetry contract that produced this span. */
17
18
  readonly VERSION: "telemetry.schema.version";
18
19
  };
19
20
  type SchemaAttributeKey = (typeof SCHEMA_ATTRS)[keyof typeof SCHEMA_ATTRS];
@@ -86,4 +87,4 @@ declare function hasBreakingChanges(diff: SnapshotDiff): boolean;
86
87
  declare function formatDiff(diff: SnapshotDiff): string;
87
88
  //#endregion
88
89
  export { diffSnapshots as a, ContractSnapshot as c, contractToSnapshot as d, parseSnapshot as f, SchemaAttributeKey as g, SNAPSHOT_SPEC as h, SnapshotDiff as i, SnapshotAttribute as l, SCHEMA_ATTRS as m, ChangeType as n, formatDiff as o, serializeSnapshot as p, SnapshotChange as r, hasBreakingChanges as s, ChangeKind as t, SnapshotSpan as u };
89
- //# sourceMappingURL=diff-Cjs6OPFN.d.cts.map
90
+ //# sourceMappingURL=diff-CfvhW7ux.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-CfvhW7ux.d.cts","names":[],"sources":["../src/attrs.ts","../src/snapshot.ts","../src/diff.ts"],"mappings":";;;;;;;;;;;;;cAYa;;WAAA;;WAAA;;KAOD,6BACF,2BAA2B;;cAGxB;;;;UCPI;EACf,MAAM;EACN,WAAW;EACX;EACA;EACA;EACA;EACA;;UAGe;EACf,WAAW;EACX;EACA;EACA,YAAY,eAAe;;;UAIZ;EACf,aAAa;EACb;EACA;EACA,kBAAkB,eAAe;EACjC,OAAO,eAAe;;;;;;;iBA6BR,mBACd,UAAU,oBACT;;iBAgCa,kBAAkB,UAAU;;iBAK5B,cAAc,eAAe;;;KC/FjC;KAEA;UAeK;EACf,MAAM;EACN,MAAM;;EAEN;EACA;EACA;;UAGe;EACf;EACA;EACA;EACA,UAAU;EACV,UAAU;EACV,SAAS;;;;;;iBAuKK,cACd,UAAU,kBACV,MAAM,mBACL;;iBA6Ca,mBAAmB,MAAM;;iBAKzB,WAAW,MAAM"}
package/dist/diff.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"diff.cjs","names":[],"sources":["../src/diff.ts"],"sourcesContent":["/**\n * Snapshot diffing — the CI gate that catches breaking changes to your trace\n * surface before they ship.\n *\n * \"If you wouldn't ship a rename to your public API without a changelog, don't\n * do it to your traces.\" This module is what makes that enforceable: classify\n * every change between two snapshots as breaking, additive, or neutral, and let\n * CI fail on the breaking ones.\n */\n\nimport type { ContractSnapshot, SnapshotAttribute } from './snapshot.js';\n\nexport type ChangeKind = 'breaking' | 'additive' | 'neutral';\n\nexport type ChangeType =\n | 'span_removed'\n | 'span_added'\n | 'attribute_removed'\n | 'attribute_added'\n | 'type_changed'\n | 'required_added'\n | 'required_removed'\n | 'enum_value_removed'\n | 'enum_value_added'\n | 'stability_downgraded'\n | 'stability_advanced'\n | 'deprecated'\n | 'replacement_documented';\n\nexport interface SnapshotChange {\n kind: ChangeKind;\n type: ChangeType;\n /** Span the change applies to (`*` = common attributes / contract-wide). */\n span: string;\n attribute?: string;\n message: string;\n}\n\nexport interface SnapshotDiff {\n service: string;\n previousVersion: string;\n nextVersion: string;\n breaking: SnapshotChange[];\n additive: SnapshotChange[];\n neutral: SnapshotChange[];\n}\n\n/**\n * How every cross-stability transition is classified. Keyed `prev->next` so all\n * six transitions are explicit and auditable — no silent fall-through. Same-\n * stability transitions are absent (no change to report).\n */\nconst STABILITY_TRANSITIONS: Record<\n string,\n { kind: ChangeKind; type: ChangeType }\n> = {\n 'stable->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n 'stable->deprecated': { kind: 'additive', type: 'deprecated' },\n 'experimental->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'experimental->deprecated': { kind: 'additive', type: 'deprecated' },\n 'deprecated->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'deprecated->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n};\n\nfunction stabilityMessage(\n type: ChangeType,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): string {\n if (type === 'deprecated') {\n return `attribute \"${attribute}\" was deprecated${next.replacedBy ? ` (use \"${next.replacedBy}\")` : ''}`;\n }\n if (type === 'stability_downgraded') {\n return `attribute \"${attribute}\" stability downgraded ${prev.stability} → ${next.stability}`;\n }\n return `attribute \"${attribute}\" promoted ${prev.stability} → ${next.stability}`;\n}\n\nfunction push(\n diff: SnapshotDiff,\n change: SnapshotChange,\n): void {\n if (change.kind === 'breaking') diff.breaking.push(change);\n else if (change.kind === 'additive') diff.additive.push(change);\n else diff.neutral.push(change);\n}\n\nfunction diffAttribute(\n diff: SnapshotDiff,\n span: string,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): void {\n if (prev.type !== next.type) {\n push(diff, {\n kind: 'breaking',\n type: 'type_changed',\n span,\n attribute,\n message: `attribute \"${attribute}\" changed type ${prev.type} → ${next.type}`,\n });\n }\n\n if (!prev.required && next.required) {\n push(diff, {\n kind: 'breaking',\n type: 'required_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" became required`,\n });\n } else if (prev.required && !next.required) {\n push(diff, {\n kind: 'additive',\n type: 'required_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" is no longer required`,\n });\n }\n\n // Enum: removing a permitted value can break a producer that still emits it.\n if (prev.enum && next.enum) {\n const nextSet = new Set(next.enum);\n const removed = prev.enum.filter((v) => !nextSet.has(v));\n const prevSet = new Set(prev.enum);\n const added = next.enum.filter((v) => !prevSet.has(v));\n if (removed.length > 0) {\n push(diff, {\n kind: 'breaking',\n type: 'enum_value_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" dropped enum value(s) ${JSON.stringify(removed)}`,\n });\n }\n if (added.length > 0) {\n push(diff, {\n kind: 'additive',\n type: 'enum_value_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" added enum value(s) ${JSON.stringify(added)}`,\n });\n }\n }\n\n if (prev.stability !== next.stability) {\n const transition =\n STABILITY_TRANSITIONS[`${prev.stability}->${next.stability}`];\n if (transition) {\n push(diff, {\n ...transition,\n span,\n attribute,\n message: stabilityMessage(transition.type, attribute, prev, next),\n });\n }\n }\n}\n\nfunction diffAttributeMaps(\n diff: SnapshotDiff,\n span: string,\n prev: Record<string, SnapshotAttribute>,\n next: Record<string, SnapshotAttribute>,\n): void {\n for (const [key, prevAttr] of Object.entries(prev)) {\n const nextAttr = next[key];\n if (!nextAttr) {\n // A removed attribute whose replacement is named is a documented\n // migration (still breaking — but reported as such with the pointer).\n push(diff, {\n kind: 'breaking',\n type: prevAttr.replacedBy ? 'replacement_documented' : 'attribute_removed',\n span,\n attribute: key,\n message: prevAttr.replacedBy\n ? `attribute \"${key}\" removed — replaced by \"${prevAttr.replacedBy}\"`\n : `attribute \"${key}\" was removed`,\n });\n continue;\n }\n diffAttribute(diff, span, key, prevAttr, nextAttr);\n }\n for (const key of Object.keys(next)) {\n if (!prev[key]) {\n const added = next[key];\n // Always `attribute_added` (the event is \"new attribute\"); severity rides\n // on `kind` — a new *required* attribute breaks existing producers.\n push(diff, {\n kind: added.required ? 'breaking' : 'additive',\n type: 'attribute_added',\n span,\n attribute: key,\n message: added.required\n ? `new required attribute \"${key}\" added`\n : `new attribute \"${key}\" added`,\n });\n }\n }\n}\n\n/**\n * Diff two snapshots, classifying every change. The `breaking` array is what a\n * CI gate keys off; `hasBreakingChanges()` is the convenience predicate.\n */\nexport function diffSnapshots(\n previous: ContractSnapshot,\n next: ContractSnapshot,\n): SnapshotDiff {\n const diff: SnapshotDiff = {\n service: next.service,\n previousVersion: previous.version,\n nextVersion: next.version,\n breaking: [],\n additive: [],\n neutral: [],\n };\n\n diffAttributeMaps(diff, '*', previous.commonAttributes, next.commonAttributes);\n\n for (const [name, prevSpan] of Object.entries(previous.spans)) {\n const nextSpan = next.spans[name];\n if (!nextSpan) {\n push(diff, {\n kind: 'breaking',\n type: 'span_removed',\n span: name,\n message: `span \"${name}\" was removed`,\n });\n continue;\n }\n diffAttributeMaps(diff, name, prevSpan.attributes, nextSpan.attributes);\n }\n for (const name of Object.keys(next.spans)) {\n if (!previous.spans[name]) {\n push(diff, {\n kind: 'additive',\n type: 'span_added',\n span: name,\n message: `new span \"${name}\" added`,\n });\n }\n }\n\n return diff;\n}\n\n/** `true` when the diff contains at least one breaking change. */\nexport function hasBreakingChanges(diff: SnapshotDiff): boolean {\n return diff.breaking.length > 0;\n}\n\n/** Markdown rendering of a diff — for CI logs and PR comments. */\nexport function formatDiff(diff: SnapshotDiff): string {\n const lines: string[] = [ \n `# Telemetry contract diff: ${diff.service} ${diff.previousVersion} → ${diff.nextVersion}`,\n ''];\n const section = (title: string, changes: SnapshotChange[]) => {\n if (changes.length === 0) return;\n lines.push(`## ${title} (${changes.length})`, '');\n for (const c of changes) {\n const where = c.attribute ? `\\`${c.span}.${c.attribute}\\`` : `\\`${c.span}\\``;\n lines.push(`- ${where}: ${c.message}`);\n }\n lines.push('');\n };\n section('💥 Breaking', diff.breaking);\n section('➕ Additive', diff.additive);\n section('• Neutral', diff.neutral);\n if (\n diff.breaking.length === 0 &&\n diff.additive.length === 0 &&\n diff.neutral.length === 0\n ) {\n lines.push('No changes to the telemetry contract.', '');\n }\n return lines.join('\\n');\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"diff.cjs","names":[],"sources":["../src/diff.ts"],"sourcesContent":["/**\n * Snapshot diffing — the CI gate that catches breaking changes to your trace\n * surface before they ship.\n *\n * \"If you wouldn't ship a rename to your public API without a changelog, don't\n * do it to your traces.\" This module is what makes that enforceable: classify\n * every change between two snapshots as breaking, additive, or neutral, and let\n * CI fail on the breaking ones.\n */\n\nimport type { ContractSnapshot, SnapshotAttribute } from './snapshot.js';\n\nexport type ChangeKind = 'breaking' | 'additive' | 'neutral';\n\nexport type ChangeType =\n | 'span_removed'\n | 'span_added'\n | 'attribute_removed'\n | 'attribute_added'\n | 'type_changed'\n | 'required_added'\n | 'required_removed'\n | 'enum_value_removed'\n | 'enum_value_added'\n | 'stability_downgraded'\n | 'stability_advanced'\n | 'deprecated'\n | 'replacement_documented';\n\nexport interface SnapshotChange {\n kind: ChangeKind;\n type: ChangeType;\n /** Span the change applies to (`*` = common attributes / contract-wide). */\n span: string;\n attribute?: string;\n message: string;\n}\n\nexport interface SnapshotDiff {\n service: string;\n previousVersion: string;\n nextVersion: string;\n breaking: SnapshotChange[];\n additive: SnapshotChange[];\n neutral: SnapshotChange[];\n}\n\n/**\n * How every cross-stability transition is classified. Keyed `prev->next` so all\n * six transitions are explicit and auditable — no silent fall-through. Same-\n * stability transitions are absent (no change to report).\n */\nconst STABILITY_TRANSITIONS: Record<\n string,\n { kind: ChangeKind; type: ChangeType }\n> = {\n 'stable->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n 'stable->deprecated': { kind: 'additive', type: 'deprecated' },\n 'experimental->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'experimental->deprecated': { kind: 'additive', type: 'deprecated' },\n 'deprecated->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'deprecated->experimental': {\n kind: 'breaking',\n type: 'stability_downgraded',\n },\n};\n\nfunction stabilityMessage(\n type: ChangeType,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): string {\n if (type === 'deprecated') {\n return `attribute \"${attribute}\" was deprecated${next.replacedBy ? ` (use \"${next.replacedBy}\")` : ''}`;\n }\n if (type === 'stability_downgraded') {\n return `attribute \"${attribute}\" stability downgraded ${prev.stability} → ${next.stability}`;\n }\n return `attribute \"${attribute}\" promoted ${prev.stability} → ${next.stability}`;\n}\n\nfunction push(diff: SnapshotDiff, change: SnapshotChange): void {\n if (change.kind === 'breaking') diff.breaking.push(change);\n else if (change.kind === 'additive') diff.additive.push(change);\n else diff.neutral.push(change);\n}\n\nfunction diffAttribute(\n diff: SnapshotDiff,\n span: string,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): void {\n if (prev.type !== next.type) {\n push(diff, {\n kind: 'breaking',\n type: 'type_changed',\n span,\n attribute,\n message: `attribute \"${attribute}\" changed type ${prev.type} → ${next.type}`,\n });\n }\n\n if (!prev.required && next.required) {\n push(diff, {\n kind: 'breaking',\n type: 'required_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" became required`,\n });\n } else if (prev.required && !next.required) {\n push(diff, {\n kind: 'additive',\n type: 'required_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" is no longer required`,\n });\n }\n\n // Enum: removing a permitted value can break a producer that still emits it.\n if (prev.enum && next.enum) {\n const nextSet = new Set(next.enum);\n const removed = prev.enum.filter((v) => !nextSet.has(v));\n const prevSet = new Set(prev.enum);\n const added = next.enum.filter((v) => !prevSet.has(v));\n if (removed.length > 0) {\n push(diff, {\n kind: 'breaking',\n type: 'enum_value_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" dropped enum value(s) ${JSON.stringify(removed)}`,\n });\n }\n if (added.length > 0) {\n push(diff, {\n kind: 'additive',\n type: 'enum_value_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" added enum value(s) ${JSON.stringify(added)}`,\n });\n }\n }\n\n if (prev.stability !== next.stability) {\n const transition =\n STABILITY_TRANSITIONS[`${prev.stability}->${next.stability}`];\n if (transition) {\n push(diff, {\n ...transition,\n span,\n attribute,\n message: stabilityMessage(transition.type, attribute, prev, next),\n });\n }\n }\n}\n\nfunction diffAttributeMaps(\n diff: SnapshotDiff,\n span: string,\n prev: Record<string, SnapshotAttribute>,\n next: Record<string, SnapshotAttribute>,\n): void {\n for (const [key, prevAttr] of Object.entries(prev)) {\n const nextAttr = next[key];\n if (!nextAttr) {\n // A removed attribute whose replacement is named is a documented\n // migration (still breaking — but reported as such with the pointer).\n push(diff, {\n kind: 'breaking',\n type: prevAttr.replacedBy\n ? 'replacement_documented'\n : 'attribute_removed',\n span,\n attribute: key,\n message: prevAttr.replacedBy\n ? `attribute \"${key}\" removed — replaced by \"${prevAttr.replacedBy}\"`\n : `attribute \"${key}\" was removed`,\n });\n continue;\n }\n diffAttribute(diff, span, key, prevAttr, nextAttr);\n }\n for (const key of Object.keys(next)) {\n if (!prev[key]) {\n const added = next[key];\n // Always `attribute_added` (the event is \"new attribute\"); severity rides\n // on `kind` — a new *required* attribute breaks existing producers.\n push(diff, {\n kind: added.required ? 'breaking' : 'additive',\n type: 'attribute_added',\n span,\n attribute: key,\n message: added.required\n ? `new required attribute \"${key}\" added`\n : `new attribute \"${key}\" added`,\n });\n }\n }\n}\n\n/**\n * Diff two snapshots, classifying every change. The `breaking` array is what a\n * CI gate keys off; `hasBreakingChanges()` is the convenience predicate.\n */\nexport function diffSnapshots(\n previous: ContractSnapshot,\n next: ContractSnapshot,\n): SnapshotDiff {\n const diff: SnapshotDiff = {\n service: next.service,\n previousVersion: previous.version,\n nextVersion: next.version,\n breaking: [],\n additive: [],\n neutral: [],\n };\n\n diffAttributeMaps(\n diff,\n '*',\n previous.commonAttributes,\n next.commonAttributes,\n );\n\n for (const [name, prevSpan] of Object.entries(previous.spans)) {\n const nextSpan = next.spans[name];\n if (!nextSpan) {\n push(diff, {\n kind: 'breaking',\n type: 'span_removed',\n span: name,\n message: `span \"${name}\" was removed`,\n });\n continue;\n }\n diffAttributeMaps(diff, name, prevSpan.attributes, nextSpan.attributes);\n }\n for (const name of Object.keys(next.spans)) {\n if (!previous.spans[name]) {\n push(diff, {\n kind: 'additive',\n type: 'span_added',\n span: name,\n message: `new span \"${name}\" added`,\n });\n }\n }\n\n return diff;\n}\n\n/** `true` when the diff contains at least one breaking change. */\nexport function hasBreakingChanges(diff: SnapshotDiff): boolean {\n return diff.breaking.length > 0;\n}\n\n/** Markdown rendering of a diff — for CI logs and PR comments. */\nexport function formatDiff(diff: SnapshotDiff): string {\n const lines: string[] = [\n `# Telemetry contract diff: ${diff.service} ${diff.previousVersion} → ${diff.nextVersion}`,\n '',\n ];\n const section = (title: string, changes: SnapshotChange[]) => {\n if (changes.length === 0) return;\n lines.push(`## ${title} (${changes.length})`, '');\n for (const c of changes) {\n const where = c.attribute\n ? `\\`${c.span}.${c.attribute}\\``\n : `\\`${c.span}\\``;\n lines.push(`- ${where}: ${c.message}`);\n }\n lines.push('');\n };\n section('💥 Breaking', diff.breaking);\n section('➕ Additive', diff.additive);\n section('• Neutral', diff.neutral);\n if (\n diff.breaking.length === 0 &&\n diff.additive.length === 0 &&\n diff.neutral.length === 0\n ) {\n lines.push('No changes to the telemetry contract.', '');\n }\n return lines.join('\\n');\n}\n"],"mappings":"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"}
package/dist/diff.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as diffSnapshots, i as SnapshotDiff, n as ChangeType, o as formatDiff, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind } from "./diff-Cjs6OPFN.cjs";
1
+ import { a as diffSnapshots, i as SnapshotDiff, n as ChangeType, o as formatDiff, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind } from "./diff-CfvhW7ux.cjs";
2
2
  export { ChangeKind, ChangeType, SnapshotChange, SnapshotDiff, diffSnapshots, formatDiff, hasBreakingChanges };
package/dist/diff.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as diffSnapshots, i as SnapshotDiff, n as ChangeType, o as formatDiff, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind } from "./diff-B1DoDhUn.js";
1
+ import { a as diffSnapshots, i as SnapshotDiff, n as ChangeType, o as formatDiff, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind } from "./diff-CAIC04O4.js";
2
2
  export { ChangeKind, ChangeType, SnapshotChange, SnapshotDiff, diffSnapshots, formatDiff, hasBreakingChanges };
package/dist/diff.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"diff.js","names":[],"sources":["../src/diff.ts"],"sourcesContent":["/**\n * Snapshot diffing — the CI gate that catches breaking changes to your trace\n * surface before they ship.\n *\n * \"If you wouldn't ship a rename to your public API without a changelog, don't\n * do it to your traces.\" This module is what makes that enforceable: classify\n * every change between two snapshots as breaking, additive, or neutral, and let\n * CI fail on the breaking ones.\n */\n\nimport type { ContractSnapshot, SnapshotAttribute } from './snapshot.js';\n\nexport type ChangeKind = 'breaking' | 'additive' | 'neutral';\n\nexport type ChangeType =\n | 'span_removed'\n | 'span_added'\n | 'attribute_removed'\n | 'attribute_added'\n | 'type_changed'\n | 'required_added'\n | 'required_removed'\n | 'enum_value_removed'\n | 'enum_value_added'\n | 'stability_downgraded'\n | 'stability_advanced'\n | 'deprecated'\n | 'replacement_documented';\n\nexport interface SnapshotChange {\n kind: ChangeKind;\n type: ChangeType;\n /** Span the change applies to (`*` = common attributes / contract-wide). */\n span: string;\n attribute?: string;\n message: string;\n}\n\nexport interface SnapshotDiff {\n service: string;\n previousVersion: string;\n nextVersion: string;\n breaking: SnapshotChange[];\n additive: SnapshotChange[];\n neutral: SnapshotChange[];\n}\n\n/**\n * How every cross-stability transition is classified. Keyed `prev->next` so all\n * six transitions are explicit and auditable — no silent fall-through. Same-\n * stability transitions are absent (no change to report).\n */\nconst STABILITY_TRANSITIONS: Record<\n string,\n { kind: ChangeKind; type: ChangeType }\n> = {\n 'stable->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n 'stable->deprecated': { kind: 'additive', type: 'deprecated' },\n 'experimental->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'experimental->deprecated': { kind: 'additive', type: 'deprecated' },\n 'deprecated->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'deprecated->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n};\n\nfunction stabilityMessage(\n type: ChangeType,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): string {\n if (type === 'deprecated') {\n return `attribute \"${attribute}\" was deprecated${next.replacedBy ? ` (use \"${next.replacedBy}\")` : ''}`;\n }\n if (type === 'stability_downgraded') {\n return `attribute \"${attribute}\" stability downgraded ${prev.stability} → ${next.stability}`;\n }\n return `attribute \"${attribute}\" promoted ${prev.stability} → ${next.stability}`;\n}\n\nfunction push(\n diff: SnapshotDiff,\n change: SnapshotChange,\n): void {\n if (change.kind === 'breaking') diff.breaking.push(change);\n else if (change.kind === 'additive') diff.additive.push(change);\n else diff.neutral.push(change);\n}\n\nfunction diffAttribute(\n diff: SnapshotDiff,\n span: string,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): void {\n if (prev.type !== next.type) {\n push(diff, {\n kind: 'breaking',\n type: 'type_changed',\n span,\n attribute,\n message: `attribute \"${attribute}\" changed type ${prev.type} → ${next.type}`,\n });\n }\n\n if (!prev.required && next.required) {\n push(diff, {\n kind: 'breaking',\n type: 'required_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" became required`,\n });\n } else if (prev.required && !next.required) {\n push(diff, {\n kind: 'additive',\n type: 'required_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" is no longer required`,\n });\n }\n\n // Enum: removing a permitted value can break a producer that still emits it.\n if (prev.enum && next.enum) {\n const nextSet = new Set(next.enum);\n const removed = prev.enum.filter((v) => !nextSet.has(v));\n const prevSet = new Set(prev.enum);\n const added = next.enum.filter((v) => !prevSet.has(v));\n if (removed.length > 0) {\n push(diff, {\n kind: 'breaking',\n type: 'enum_value_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" dropped enum value(s) ${JSON.stringify(removed)}`,\n });\n }\n if (added.length > 0) {\n push(diff, {\n kind: 'additive',\n type: 'enum_value_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" added enum value(s) ${JSON.stringify(added)}`,\n });\n }\n }\n\n if (prev.stability !== next.stability) {\n const transition =\n STABILITY_TRANSITIONS[`${prev.stability}->${next.stability}`];\n if (transition) {\n push(diff, {\n ...transition,\n span,\n attribute,\n message: stabilityMessage(transition.type, attribute, prev, next),\n });\n }\n }\n}\n\nfunction diffAttributeMaps(\n diff: SnapshotDiff,\n span: string,\n prev: Record<string, SnapshotAttribute>,\n next: Record<string, SnapshotAttribute>,\n): void {\n for (const [key, prevAttr] of Object.entries(prev)) {\n const nextAttr = next[key];\n if (!nextAttr) {\n // A removed attribute whose replacement is named is a documented\n // migration (still breaking — but reported as such with the pointer).\n push(diff, {\n kind: 'breaking',\n type: prevAttr.replacedBy ? 'replacement_documented' : 'attribute_removed',\n span,\n attribute: key,\n message: prevAttr.replacedBy\n ? `attribute \"${key}\" removed — replaced by \"${prevAttr.replacedBy}\"`\n : `attribute \"${key}\" was removed`,\n });\n continue;\n }\n diffAttribute(diff, span, key, prevAttr, nextAttr);\n }\n for (const key of Object.keys(next)) {\n if (!prev[key]) {\n const added = next[key];\n // Always `attribute_added` (the event is \"new attribute\"); severity rides\n // on `kind` — a new *required* attribute breaks existing producers.\n push(diff, {\n kind: added.required ? 'breaking' : 'additive',\n type: 'attribute_added',\n span,\n attribute: key,\n message: added.required\n ? `new required attribute \"${key}\" added`\n : `new attribute \"${key}\" added`,\n });\n }\n }\n}\n\n/**\n * Diff two snapshots, classifying every change. The `breaking` array is what a\n * CI gate keys off; `hasBreakingChanges()` is the convenience predicate.\n */\nexport function diffSnapshots(\n previous: ContractSnapshot,\n next: ContractSnapshot,\n): SnapshotDiff {\n const diff: SnapshotDiff = {\n service: next.service,\n previousVersion: previous.version,\n nextVersion: next.version,\n breaking: [],\n additive: [],\n neutral: [],\n };\n\n diffAttributeMaps(diff, '*', previous.commonAttributes, next.commonAttributes);\n\n for (const [name, prevSpan] of Object.entries(previous.spans)) {\n const nextSpan = next.spans[name];\n if (!nextSpan) {\n push(diff, {\n kind: 'breaking',\n type: 'span_removed',\n span: name,\n message: `span \"${name}\" was removed`,\n });\n continue;\n }\n diffAttributeMaps(diff, name, prevSpan.attributes, nextSpan.attributes);\n }\n for (const name of Object.keys(next.spans)) {\n if (!previous.spans[name]) {\n push(diff, {\n kind: 'additive',\n type: 'span_added',\n span: name,\n message: `new span \"${name}\" added`,\n });\n }\n }\n\n return diff;\n}\n\n/** `true` when the diff contains at least one breaking change. */\nexport function hasBreakingChanges(diff: SnapshotDiff): boolean {\n return diff.breaking.length > 0;\n}\n\n/** Markdown rendering of a diff — for CI logs and PR comments. */\nexport function formatDiff(diff: SnapshotDiff): string {\n const lines: string[] = [ \n `# Telemetry contract diff: ${diff.service} ${diff.previousVersion} → ${diff.nextVersion}`,\n ''];\n const section = (title: string, changes: SnapshotChange[]) => {\n if (changes.length === 0) return;\n lines.push(`## ${title} (${changes.length})`, '');\n for (const c of changes) {\n const where = c.attribute ? `\\`${c.span}.${c.attribute}\\`` : `\\`${c.span}\\``;\n lines.push(`- ${where}: ${c.message}`);\n }\n lines.push('');\n };\n section('💥 Breaking', diff.breaking);\n section('➕ Additive', diff.additive);\n section('• Neutral', diff.neutral);\n if (\n diff.breaking.length === 0 &&\n diff.additive.length === 0 &&\n diff.neutral.length === 0\n ) {\n lines.push('No changes to the telemetry contract.', '');\n }\n return lines.join('\\n');\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"diff.js","names":[],"sources":["../src/diff.ts"],"sourcesContent":["/**\n * Snapshot diffing — the CI gate that catches breaking changes to your trace\n * surface before they ship.\n *\n * \"If you wouldn't ship a rename to your public API without a changelog, don't\n * do it to your traces.\" This module is what makes that enforceable: classify\n * every change between two snapshots as breaking, additive, or neutral, and let\n * CI fail on the breaking ones.\n */\n\nimport type { ContractSnapshot, SnapshotAttribute } from './snapshot.js';\n\nexport type ChangeKind = 'breaking' | 'additive' | 'neutral';\n\nexport type ChangeType =\n | 'span_removed'\n | 'span_added'\n | 'attribute_removed'\n | 'attribute_added'\n | 'type_changed'\n | 'required_added'\n | 'required_removed'\n | 'enum_value_removed'\n | 'enum_value_added'\n | 'stability_downgraded'\n | 'stability_advanced'\n | 'deprecated'\n | 'replacement_documented';\n\nexport interface SnapshotChange {\n kind: ChangeKind;\n type: ChangeType;\n /** Span the change applies to (`*` = common attributes / contract-wide). */\n span: string;\n attribute?: string;\n message: string;\n}\n\nexport interface SnapshotDiff {\n service: string;\n previousVersion: string;\n nextVersion: string;\n breaking: SnapshotChange[];\n additive: SnapshotChange[];\n neutral: SnapshotChange[];\n}\n\n/**\n * How every cross-stability transition is classified. Keyed `prev->next` so all\n * six transitions are explicit and auditable — no silent fall-through. Same-\n * stability transitions are absent (no change to report).\n */\nconst STABILITY_TRANSITIONS: Record<\n string,\n { kind: ChangeKind; type: ChangeType }\n> = {\n 'stable->experimental': { kind: 'breaking', type: 'stability_downgraded' },\n 'stable->deprecated': { kind: 'additive', type: 'deprecated' },\n 'experimental->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'experimental->deprecated': { kind: 'additive', type: 'deprecated' },\n 'deprecated->stable': { kind: 'neutral', type: 'stability_advanced' },\n 'deprecated->experimental': {\n kind: 'breaking',\n type: 'stability_downgraded',\n },\n};\n\nfunction stabilityMessage(\n type: ChangeType,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): string {\n if (type === 'deprecated') {\n return `attribute \"${attribute}\" was deprecated${next.replacedBy ? ` (use \"${next.replacedBy}\")` : ''}`;\n }\n if (type === 'stability_downgraded') {\n return `attribute \"${attribute}\" stability downgraded ${prev.stability} → ${next.stability}`;\n }\n return `attribute \"${attribute}\" promoted ${prev.stability} → ${next.stability}`;\n}\n\nfunction push(diff: SnapshotDiff, change: SnapshotChange): void {\n if (change.kind === 'breaking') diff.breaking.push(change);\n else if (change.kind === 'additive') diff.additive.push(change);\n else diff.neutral.push(change);\n}\n\nfunction diffAttribute(\n diff: SnapshotDiff,\n span: string,\n attribute: string,\n prev: SnapshotAttribute,\n next: SnapshotAttribute,\n): void {\n if (prev.type !== next.type) {\n push(diff, {\n kind: 'breaking',\n type: 'type_changed',\n span,\n attribute,\n message: `attribute \"${attribute}\" changed type ${prev.type} → ${next.type}`,\n });\n }\n\n if (!prev.required && next.required) {\n push(diff, {\n kind: 'breaking',\n type: 'required_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" became required`,\n });\n } else if (prev.required && !next.required) {\n push(diff, {\n kind: 'additive',\n type: 'required_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" is no longer required`,\n });\n }\n\n // Enum: removing a permitted value can break a producer that still emits it.\n if (prev.enum && next.enum) {\n const nextSet = new Set(next.enum);\n const removed = prev.enum.filter((v) => !nextSet.has(v));\n const prevSet = new Set(prev.enum);\n const added = next.enum.filter((v) => !prevSet.has(v));\n if (removed.length > 0) {\n push(diff, {\n kind: 'breaking',\n type: 'enum_value_removed',\n span,\n attribute,\n message: `attribute \"${attribute}\" dropped enum value(s) ${JSON.stringify(removed)}`,\n });\n }\n if (added.length > 0) {\n push(diff, {\n kind: 'additive',\n type: 'enum_value_added',\n span,\n attribute,\n message: `attribute \"${attribute}\" added enum value(s) ${JSON.stringify(added)}`,\n });\n }\n }\n\n if (prev.stability !== next.stability) {\n const transition =\n STABILITY_TRANSITIONS[`${prev.stability}->${next.stability}`];\n if (transition) {\n push(diff, {\n ...transition,\n span,\n attribute,\n message: stabilityMessage(transition.type, attribute, prev, next),\n });\n }\n }\n}\n\nfunction diffAttributeMaps(\n diff: SnapshotDiff,\n span: string,\n prev: Record<string, SnapshotAttribute>,\n next: Record<string, SnapshotAttribute>,\n): void {\n for (const [key, prevAttr] of Object.entries(prev)) {\n const nextAttr = next[key];\n if (!nextAttr) {\n // A removed attribute whose replacement is named is a documented\n // migration (still breaking — but reported as such with the pointer).\n push(diff, {\n kind: 'breaking',\n type: prevAttr.replacedBy\n ? 'replacement_documented'\n : 'attribute_removed',\n span,\n attribute: key,\n message: prevAttr.replacedBy\n ? `attribute \"${key}\" removed — replaced by \"${prevAttr.replacedBy}\"`\n : `attribute \"${key}\" was removed`,\n });\n continue;\n }\n diffAttribute(diff, span, key, prevAttr, nextAttr);\n }\n for (const key of Object.keys(next)) {\n if (!prev[key]) {\n const added = next[key];\n // Always `attribute_added` (the event is \"new attribute\"); severity rides\n // on `kind` — a new *required* attribute breaks existing producers.\n push(diff, {\n kind: added.required ? 'breaking' : 'additive',\n type: 'attribute_added',\n span,\n attribute: key,\n message: added.required\n ? `new required attribute \"${key}\" added`\n : `new attribute \"${key}\" added`,\n });\n }\n }\n}\n\n/**\n * Diff two snapshots, classifying every change. The `breaking` array is what a\n * CI gate keys off; `hasBreakingChanges()` is the convenience predicate.\n */\nexport function diffSnapshots(\n previous: ContractSnapshot,\n next: ContractSnapshot,\n): SnapshotDiff {\n const diff: SnapshotDiff = {\n service: next.service,\n previousVersion: previous.version,\n nextVersion: next.version,\n breaking: [],\n additive: [],\n neutral: [],\n };\n\n diffAttributeMaps(\n diff,\n '*',\n previous.commonAttributes,\n next.commonAttributes,\n );\n\n for (const [name, prevSpan] of Object.entries(previous.spans)) {\n const nextSpan = next.spans[name];\n if (!nextSpan) {\n push(diff, {\n kind: 'breaking',\n type: 'span_removed',\n span: name,\n message: `span \"${name}\" was removed`,\n });\n continue;\n }\n diffAttributeMaps(diff, name, prevSpan.attributes, nextSpan.attributes);\n }\n for (const name of Object.keys(next.spans)) {\n if (!previous.spans[name]) {\n push(diff, {\n kind: 'additive',\n type: 'span_added',\n span: name,\n message: `new span \"${name}\" added`,\n });\n }\n }\n\n return diff;\n}\n\n/** `true` when the diff contains at least one breaking change. */\nexport function hasBreakingChanges(diff: SnapshotDiff): boolean {\n return diff.breaking.length > 0;\n}\n\n/** Markdown rendering of a diff — for CI logs and PR comments. */\nexport function formatDiff(diff: SnapshotDiff): string {\n const lines: string[] = [\n `# Telemetry contract diff: ${diff.service} ${diff.previousVersion} → ${diff.nextVersion}`,\n '',\n ];\n const section = (title: string, changes: SnapshotChange[]) => {\n if (changes.length === 0) return;\n lines.push(`## ${title} (${changes.length})`, '');\n for (const c of changes) {\n const where = c.attribute\n ? `\\`${c.span}.${c.attribute}\\``\n : `\\`${c.span}\\``;\n lines.push(`- ${where}: ${c.message}`);\n }\n lines.push('');\n };\n section('💥 Breaking', diff.breaking);\n section('➕ Additive', diff.additive);\n section('• Neutral', diff.neutral);\n if (\n diff.breaking.length === 0 &&\n diff.additive.length === 0 &&\n diff.neutral.length === 0\n ) {\n lines.push('No changes to the telemetry contract.', '');\n }\n return lines.join('\\n');\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["defineContract"],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"sourcesContent":["/**\n * Cardinality posture helpers.\n *\n * The old cardinality rule — \"keep unique-value counts down\" — was a constraint\n * invented because dashboards have pixels and a graph with 10k series is\n * unreadable to a human. An agent does not look at the graph; it reads the\n * spans. A high-cardinality field (the user id, the sender domain, the request\n * id) is then the single most useful attribute on a trace when the agent is\n * chasing one specific failure.\n *\n * So the contract lets you mark attributes `highCardinality: true` as a\n * deliberate signal, and this module turns that into a *protect list*: the keys\n * a redactor or span-name normalizer must NOT strip, even when an aggressive\n * default would otherwise drop them.\n */\n\nimport type { TelemetryContract } from './contract.js';\n\n/**\n * Every attribute key in the contract flagged `highCardinality: true`, across\n * both common and per-span attributes. Feed this into a redaction/normalization\n * allow-list so the fields most useful to an agent reader survive.\n *\n * @example\n * ```ts\n * import { init } from 'autotel';\n * import { highCardinalityKeys } from 'autotel-schema';\n * import { contract } from './telemetry.contract';\n *\n * init({\n * service: 'checkout',\n * // keep user.id / request.id intact even under the strict redactor\n * attributeRedactor: { allowKeys: highCardinalityKeys(contract), preset: 'strict' },\n * });\n * ```\n */\nexport function highCardinalityKeys(contract: TelemetryContract): string[] {\n const keys = new Set<string>();\n for (const [key, spec] of Object.entries(contract.commonAttributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n for (const spanSpec of Object.values(contract.spans)) {\n for (const [key, spec] of Object.entries(spanSpec.attributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n }\n return [...keys].toSorted();\n}\n\n/**\n * Predicate form of {@link highCardinalityKeys} — `true` when `key` is declared\n * high-cardinality anywhere in the contract. Useful inside a custom\n * `spanNameNormalizer` or redactor callback.\n */\nexport function isHighCardinalityKey(\n contract: TelemetryContract,\n key: string,\n): boolean {\n if (contract.commonAttributes?.[key]?.highCardinality) return true;\n for (const spanSpec of Object.values(contract.spans)) {\n if (spanSpec.attributes?.[key]?.highCardinality) return true;\n }\n return false;\n}\n","import { defineContract } from '../contract.js';\n\nconst stringAttr = { type: 'string' as const };\nconst boolAttr = { type: 'boolean' as const };\nconst numberAttr = { type: 'number' as const };\nconst stringArrayAttr = { type: 'string[]' as const };\n\n/**\n * Published telemetry contract for Google SAIF-aligned agent security observability.\n * Span names are illustrative — attributes are the stable surface under validation.\n */\nexport const AGENT_SECURITY_TELEMETRY_CONTRACT = defineContract({\n service: 'autotel-agent-security',\n version: '1.0.0',\n commonAttributes: {\n 'autotel.agent': { ...boolAttr, required: false, description: 'Agent audit marker' },\n 'agent.controller.id': {\n ...stringAttr,\n highCardinality: true,\n description: 'Hashed controlling human user id',\n },\n 'agent.input.provenance': {\n ...stringAttr,\n enum: [\n 'user_direct',\n 'user_voice',\n 'rag',\n 'memory',\n 'tool_result',\n 'external_untrusted',\n ],\n },\n 'agent.action.risk_class': {\n ...stringAttr,\n enum: ['read', 'write', 'destructive', 'financial', 'exfiltration_capable'],\n },\n 'agent.consent.required': { ...boolAttr },\n 'agent.consent.outcome': {\n ...stringAttr,\n enum: ['approved', 'denied', 'timeout', 'revoked'],\n },\n 'agent.scope.active': { ...stringArrayAttr },\n 'agent.memory.operation': {\n ...stringAttr,\n enum: ['read', 'write', 'delete', 'search'],\n },\n 'agent.memory.isolation_key': { ...stringAttr, highCardinality: true },\n 'agent.plan.step_index': { ...numberAttr },\n 'agent.plan.tool_intents': { ...stringArrayAttr },\n 'agent.plan.risk.verdict': {\n ...stringAttr,\n enum: ['low', 'medium', 'high', 'critical'],\n },\n 'agent.plan.risk.score': { ...numberAttr },\n 'agent.plan.risk.categories': { ...stringArrayAttr },\n 'policy.decision': {\n ...stringAttr,\n enum: ['permit', 'deny', 'challenge', 'observe', 'error'],\n },\n 'tool.input_hash': { ...stringAttr },\n 'tool.output_hash': { ...stringAttr },\n 'mcp.tool.destructive': { ...boolAttr },\n 'mcp.tool.untrusted_content': { ...boolAttr },\n 'mcp.security.injection.verdict': {\n ...stringAttr,\n enum: ['clean', 'suspicious', 'malicious'],\n },\n 'security.event': { ...stringAttr },\n 'security.category': { ...stringAttr },\n 'security.outcome': { ...stringAttr },\n 'security.severity': {\n ...stringAttr,\n enum: ['info', 'warning', 'error', 'critical'],\n },\n },\n spans: {\n 'agent.action': {\n description: 'Scoped agent action or tool call with audit metadata',\n attributes: {\n 'agent.id': { ...stringAttr, required: true },\n 'tool.name': { ...stringAttr },\n },\n },\n 'tools/call': {\n description: 'MCP tool invocation with boundary security signals',\n attributes: {\n 'mcp.tool.name': { ...stringAttr, required: true },\n },\n },\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,oBAAoB,UAAuC;CACzE,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,oBAAoB,CAAC,CAAC,GACtE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAExC,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,cAAc,CAAC,CAAC,GAChE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAG1C,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,SAAS;AAC5B;;;;;;AAOA,SAAgB,qBACd,UACA,KACS;CACT,IAAI,SAAS,mBAAmB,IAAI,EAAE,iBAAiB,OAAO;CAC9D,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,IAAI,SAAS,aAAa,IAAI,EAAE,iBAAiB,OAAO;CAE1D,OAAO;AACT;;;;AC7DA,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,WAAW,EAAE,MAAM,UAAmB;AAC5C,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,kBAAkB,EAAE,MAAM,WAAoB;;;;;AAMpD,MAAa,oCAAoCA,iCAAe;CAC9D,SAAS;CACT,SAAS;CACT,kBAAkB;EAChB,iBAAiB;GAAE,GAAG;GAAU,UAAU;GAAO,aAAa;EAAqB;EACnF,uBAAuB;GACrB,GAAG;GACH,iBAAiB;GACjB,aAAa;EACf;EACA,0BAA0B;GACxB,GAAG;GACH,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;GACF;EACF;EACA,2BAA2B;GACzB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAS;IAAe;IAAa;GAAsB;EAC5E;EACA,0BAA0B,EAAE,GAAG,SAAS;EACxC,yBAAyB;GACvB,GAAG;GACH,MAAM;IAAC;IAAY;IAAU;IAAW;GAAS;EACnD;EACA,sBAAsB,EAAE,GAAG,gBAAgB;EAC3C,0BAA0B;GACxB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAS;IAAU;GAAQ;EAC5C;EACA,8BAA8B;GAAE,GAAG;GAAY,iBAAiB;EAAK;EACrE,yBAAyB,EAAE,GAAG,WAAW;EACzC,2BAA2B,EAAE,GAAG,gBAAgB;EAChD,2BAA2B;GACzB,GAAG;GACH,MAAM;IAAC;IAAO;IAAU;IAAQ;GAAU;EAC5C;EACA,yBAAyB,EAAE,GAAG,WAAW;EACzC,8BAA8B,EAAE,GAAG,gBAAgB;EACnD,mBAAmB;GACjB,GAAG;GACH,MAAM;IAAC;IAAU;IAAQ;IAAa;IAAW;GAAO;EAC1D;EACA,mBAAmB,EAAE,GAAG,WAAW;EACnC,oBAAoB,EAAE,GAAG,WAAW;EACpC,wBAAwB,EAAE,GAAG,SAAS;EACtC,8BAA8B,EAAE,GAAG,SAAS;EAC5C,kCAAkC;GAChC,GAAG;GACH,MAAM;IAAC;IAAS;IAAc;GAAW;EAC3C;EACA,kBAAkB,EAAE,GAAG,WAAW;EAClC,qBAAqB,EAAE,GAAG,WAAW;EACrC,oBAAoB,EAAE,GAAG,WAAW;EACpC,qBAAqB;GACnB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAW;IAAS;GAAU;EAC/C;CACF;CACA,OAAO;EACL,gBAAgB;GACd,aAAa;GACb,YAAY;IACV,YAAY;KAAE,GAAG;KAAY,UAAU;IAAK;IAC5C,aAAa,EAAE,GAAG,WAAW;GAC/B;EACF;EACA,cAAc;GACZ,aAAa;GACb,YAAY,EACV,iBAAiB;IAAE,GAAG;IAAY,UAAU;GAAK,EACnD;EACF;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"index.cjs","names":["defineContract"],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"sourcesContent":["/**\n * Cardinality posture helpers.\n *\n * The old cardinality rule — \"keep unique-value counts down\" — was a constraint\n * invented because dashboards have pixels and a graph with 10k series is\n * unreadable to a human. An agent does not look at the graph; it reads the\n * spans. A high-cardinality field (the user id, the sender domain, the request\n * id) is then the single most useful attribute on a trace when the agent is\n * chasing one specific failure.\n *\n * So the contract lets you mark attributes `highCardinality: true` as a\n * deliberate signal, and this module turns that into a *protect list*: the keys\n * a redactor or span-name normalizer must NOT strip, even when an aggressive\n * default would otherwise drop them.\n */\n\nimport type { TelemetryContract } from './contract.js';\n\n/**\n * Every attribute key in the contract flagged `highCardinality: true`, across\n * both common and per-span attributes. Feed this into a redaction/normalization\n * allow-list so the fields most useful to an agent reader survive.\n *\n * @example\n * ```ts\n * import { init } from 'autotel';\n * import { highCardinalityKeys } from 'autotel-schema';\n * import { contract } from './telemetry.contract';\n *\n * init({\n * service: 'checkout',\n * // keep user.id / request.id intact even under the strict redactor\n * attributeRedactor: { allowKeys: highCardinalityKeys(contract), preset: 'strict' },\n * });\n * ```\n */\nexport function highCardinalityKeys(contract: TelemetryContract): string[] {\n const keys = new Set<string>();\n for (const [key, spec] of Object.entries(contract.commonAttributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n for (const spanSpec of Object.values(contract.spans)) {\n for (const [key, spec] of Object.entries(spanSpec.attributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n }\n return [...keys].toSorted();\n}\n\n/**\n * Predicate form of {@link highCardinalityKeys} — `true` when `key` is declared\n * high-cardinality anywhere in the contract. Useful inside a custom\n * `spanNameNormalizer` or redactor callback.\n */\nexport function isHighCardinalityKey(\n contract: TelemetryContract,\n key: string,\n): boolean {\n if (contract.commonAttributes?.[key]?.highCardinality) return true;\n for (const spanSpec of Object.values(contract.spans)) {\n if (spanSpec.attributes?.[key]?.highCardinality) return true;\n }\n return false;\n}\n","import { defineContract } from '../contract.js';\n\nconst stringAttr = { type: 'string' as const };\nconst boolAttr = { type: 'boolean' as const };\nconst numberAttr = { type: 'number' as const };\nconst stringArrayAttr = { type: 'string[]' as const };\n\n/**\n * Published telemetry contract for Google SAIF-aligned agent security observability.\n * Span names are illustrative — attributes are the stable surface under validation.\n */\nexport const AGENT_SECURITY_TELEMETRY_CONTRACT = defineContract({\n service: 'autotel-agent-security',\n version: '1.0.0',\n commonAttributes: {\n 'autotel.agent': {\n ...boolAttr,\n required: false,\n description: 'Agent audit marker',\n },\n 'agent.controller.id': {\n ...stringAttr,\n highCardinality: true,\n description: 'Hashed controlling human user id',\n },\n 'agent.input.provenance': {\n ...stringAttr,\n enum: [\n 'user_direct',\n 'user_voice',\n 'rag',\n 'memory',\n 'tool_result',\n 'external_untrusted',\n ],\n },\n 'agent.action.risk_class': {\n ...stringAttr,\n enum: [\n 'read',\n 'write',\n 'destructive',\n 'financial',\n 'exfiltration_capable',\n ],\n },\n 'agent.consent.required': { ...boolAttr },\n 'agent.consent.outcome': {\n ...stringAttr,\n enum: ['approved', 'denied', 'timeout', 'revoked'],\n },\n 'agent.scope.active': { ...stringArrayAttr },\n 'agent.memory.operation': {\n ...stringAttr,\n enum: ['read', 'write', 'delete', 'search'],\n },\n 'agent.memory.isolation_key': { ...stringAttr, highCardinality: true },\n 'agent.plan.step_index': { ...numberAttr },\n 'agent.plan.tool_intents': { ...stringArrayAttr },\n 'agent.plan.risk.verdict': {\n ...stringAttr,\n enum: ['low', 'medium', 'high', 'critical'],\n },\n 'agent.plan.risk.score': { ...numberAttr },\n 'agent.plan.risk.categories': { ...stringArrayAttr },\n 'policy.decision': {\n ...stringAttr,\n enum: ['permit', 'deny', 'challenge', 'observe', 'error'],\n },\n 'tool.input_hash': { ...stringAttr },\n 'tool.output_hash': { ...stringAttr },\n 'mcp.tool.destructive': { ...boolAttr },\n 'mcp.tool.untrusted_content': { ...boolAttr },\n 'mcp.security.injection.verdict': {\n ...stringAttr,\n enum: ['clean', 'suspicious', 'malicious'],\n },\n 'security.event': { ...stringAttr },\n 'security.category': { ...stringAttr },\n 'security.outcome': { ...stringAttr },\n 'security.severity': {\n ...stringAttr,\n enum: ['info', 'warning', 'error', 'critical'],\n },\n },\n spans: {\n 'agent.action': {\n description: 'Scoped agent action or tool call with audit metadata',\n attributes: {\n 'agent.id': { ...stringAttr, required: true },\n 'tool.name': { ...stringAttr },\n },\n },\n 'tools/call': {\n description: 'MCP tool invocation with boundary security signals',\n attributes: {\n 'mcp.tool.name': { ...stringAttr, required: true },\n },\n },\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,oBAAoB,UAAuC;CACzE,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,oBAAoB,CAAC,CAAC,GACtE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAExC,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,cAAc,CAAC,CAAC,GAChE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAG1C,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,SAAS;AAC5B;;;;;;AAOA,SAAgB,qBACd,UACA,KACS;CACT,IAAI,SAAS,mBAAmB,IAAI,EAAE,iBAAiB,OAAO;CAC9D,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,IAAI,SAAS,aAAa,IAAI,EAAE,iBAAiB,OAAO;CAE1D,OAAO;AACT;;;;AC7DA,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,WAAW,EAAE,MAAM,UAAmB;AAC5C,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,kBAAkB,EAAE,MAAM,WAAoB;;;;;AAMpD,MAAa,oCAAoCA,iCAAe;CAC9D,SAAS;CACT,SAAS;CACT,kBAAkB;EAChB,iBAAiB;GACf,GAAG;GACH,UAAU;GACV,aAAa;EACf;EACA,uBAAuB;GACrB,GAAG;GACH,iBAAiB;GACjB,aAAa;EACf;EACA,0BAA0B;GACxB,GAAG;GACH,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;GACF;EACF;EACA,2BAA2B;GACzB,GAAG;GACH,MAAM;IACJ;IACA;IACA;IACA;IACA;GACF;EACF;EACA,0BAA0B,EAAE,GAAG,SAAS;EACxC,yBAAyB;GACvB,GAAG;GACH,MAAM;IAAC;IAAY;IAAU;IAAW;GAAS;EACnD;EACA,sBAAsB,EAAE,GAAG,gBAAgB;EAC3C,0BAA0B;GACxB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAS;IAAU;GAAQ;EAC5C;EACA,8BAA8B;GAAE,GAAG;GAAY,iBAAiB;EAAK;EACrE,yBAAyB,EAAE,GAAG,WAAW;EACzC,2BAA2B,EAAE,GAAG,gBAAgB;EAChD,2BAA2B;GACzB,GAAG;GACH,MAAM;IAAC;IAAO;IAAU;IAAQ;GAAU;EAC5C;EACA,yBAAyB,EAAE,GAAG,WAAW;EACzC,8BAA8B,EAAE,GAAG,gBAAgB;EACnD,mBAAmB;GACjB,GAAG;GACH,MAAM;IAAC;IAAU;IAAQ;IAAa;IAAW;GAAO;EAC1D;EACA,mBAAmB,EAAE,GAAG,WAAW;EACnC,oBAAoB,EAAE,GAAG,WAAW;EACpC,wBAAwB,EAAE,GAAG,SAAS;EACtC,8BAA8B,EAAE,GAAG,SAAS;EAC5C,kCAAkC;GAChC,GAAG;GACH,MAAM;IAAC;IAAS;IAAc;GAAW;EAC3C;EACA,kBAAkB,EAAE,GAAG,WAAW;EAClC,qBAAqB,EAAE,GAAG,WAAW;EACrC,oBAAoB,EAAE,GAAG,WAAW;EACpC,qBAAqB;GACnB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAW;IAAS;GAAU;EAC/C;CACF;CACA,OAAO;EACL,gBAAgB;GACd,aAAa;GACb,YAAY;IACV,YAAY;KAAE,GAAG;KAAY,UAAU;IAAK;IAC5C,aAAa,EAAE,GAAG,WAAW;GAC/B;EACF;EACA,cAAc;GACZ,aAAa;GACb,YAAY,EACV,iBAAiB;IAAE,GAAG;IAAY,UAAU;GAAK,EACnD;EACF;CACF;AACF,CAAC"}
package/dist/index.d.cts CHANGED
@@ -1,7 +1,6 @@
1
- import { a as diffSnapshots, c as ContractSnapshot, d as contractToSnapshot, f as parseSnapshot, g as SchemaAttributeKey, h as SNAPSHOT_SPEC, i as SnapshotDiff, l as SnapshotAttribute, m as SCHEMA_ATTRS, n as ChangeType, o as formatDiff, p as serializeSnapshot, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind, u as SnapshotSpan } from "./diff-Cjs6OPFN.cjs";
1
+ import { a as diffSnapshots, c as ContractSnapshot, d as contractToSnapshot, f as parseSnapshot, g as SchemaAttributeKey, h as SNAPSHOT_SPEC, i as SnapshotDiff, l as SnapshotAttribute, m as SCHEMA_ATTRS, n as ChangeType, o as formatDiff, p as serializeSnapshot, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind, u as SnapshotSpan } from "./diff-CfvhW7ux.cjs";
2
2
  import { A as validateScenarioSpec, C as ScenarioViolationCode, D as isScenarioClosed, E as formatScenarioResult, O as parseCardinality, S as ScenarioViolation, T as evaluateScenario, _ as ScenarioOutcome, a as SpanSpec, b as ScenarioSpan, c as allowsAdditionalAttributes, d as Cardinality, f as CheckScenarioOptions, g as ScenarioEventSpec, h as ScenarioAddition, i as STABILITIES, k as proposeScenario, l as defineContract, m as EvaluateScenarioOptions, n as AttributeSpec, o as Stability, p as CompletionBoundary, r as AttributeType, s as TelemetryContract, t as ATTRIBUTE_TYPES, u as resolveAttributeSpec, v as ScenarioProposal, w as checkScenario, x as ScenarioSpec, y as ScenarioResult } from "./contract-Ymh_Q37N.cjs";
3
- import { a as SchemaValidationSpanProcessor, c as createSchemaValidationProcessor, d as ValidateOptions, f as ViolationCode, g as validateSpan, h as hasErrors, i as SchemaValidationProcessorOptions, l as SchemaViolation, m as formatViolation, n as ReadableSpanLike, o as SpanLike, p as ViolationSeverity, r as SchemaProcessorMode, s as SpanProcessorLike, t as OtelContext, u as SpanShape } from "./processor-BsO4WD73.cjs";
4
-
3
+ import { a as SchemaValidationSpanProcessor, c as createSchemaValidationProcessor, d as ValidateOptions, f as ViolationCode, g as validateSpan, h as hasErrors, i as SchemaValidationProcessorOptions, l as SchemaViolation, m as formatViolation, n as ReadableSpanLike, o as SpanLike, p as ViolationSeverity, r as SchemaProcessorMode, s as SpanProcessorLike, t as OtelContext, u as SpanShape } from "./processor-DVcsS6Rz.cjs";
5
4
  //#region src/redaction.d.ts
6
5
  /**
7
6
  * Every attribute key in the contract flagged `highCardinality: true`, across
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"mappings":";;;;;;;AAwDa;;;;AC7Cb;;;;AA+EE;;;;;;;;iBDtDc,mBAAA,CAAoB,QAA2B,EAAjB,iBAAiB;;;;;;iBAkB/C,oBAAA,CACd,QAAA,EAAU,iBAAiB,EAC3B,GAAA;;;;;;;cC7CW,iCAAA,EA+EX,iBAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;iBAoCgB,oBAAoB,UAAU;;;;;;iBAkB9B,qBACd,UAAU,mBACV;;;;;;;cC7CW,mCAAA"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { a as diffSnapshots, c as ContractSnapshot, d as contractToSnapshot, f as parseSnapshot, g as SchemaAttributeKey, h as SNAPSHOT_SPEC, i as SnapshotDiff, l as SnapshotAttribute, m as SCHEMA_ATTRS, n as ChangeType, o as formatDiff, p as serializeSnapshot, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind, u as SnapshotSpan } from "./diff-B1DoDhUn.js";
1
+ import { a as diffSnapshots, c as ContractSnapshot, d as contractToSnapshot, f as parseSnapshot, g as SchemaAttributeKey, h as SNAPSHOT_SPEC, i as SnapshotDiff, l as SnapshotAttribute, m as SCHEMA_ATTRS, n as ChangeType, o as formatDiff, p as serializeSnapshot, r as SnapshotChange, s as hasBreakingChanges, t as ChangeKind, u as SnapshotSpan } from "./diff-CAIC04O4.js";
2
2
  import { A as validateScenarioSpec, C as ScenarioViolationCode, D as isScenarioClosed, E as formatScenarioResult, O as parseCardinality, S as ScenarioViolation, T as evaluateScenario, _ as ScenarioOutcome, a as SpanSpec, b as ScenarioSpan, c as allowsAdditionalAttributes, d as Cardinality, f as CheckScenarioOptions, g as ScenarioEventSpec, h as ScenarioAddition, i as STABILITIES, k as proposeScenario, l as defineContract, m as EvaluateScenarioOptions, n as AttributeSpec, o as Stability, p as CompletionBoundary, r as AttributeType, s as TelemetryContract, t as ATTRIBUTE_TYPES, u as resolveAttributeSpec, v as ScenarioProposal, w as checkScenario, x as ScenarioSpec, y as ScenarioResult } from "./contract-Ymh_Q37N.js";
3
- import { a as SchemaValidationSpanProcessor, c as createSchemaValidationProcessor, d as ValidateOptions, f as ViolationCode, g as validateSpan, h as hasErrors, i as SchemaValidationProcessorOptions, l as SchemaViolation, m as formatViolation, n as ReadableSpanLike, o as SpanLike, p as ViolationSeverity, r as SchemaProcessorMode, s as SpanProcessorLike, t as OtelContext, u as SpanShape } from "./processor-CIEfOOYi.js";
4
-
3
+ import { a as SchemaValidationSpanProcessor, c as createSchemaValidationProcessor, d as ValidateOptions, f as ViolationCode, g as validateSpan, h as hasErrors, i as SchemaValidationProcessorOptions, l as SchemaViolation, m as formatViolation, n as ReadableSpanLike, o as SpanLike, p as ViolationSeverity, r as SchemaProcessorMode, s as SpanProcessorLike, t as OtelContext, u as SpanShape } from "./processor-TpuacLr5.js";
5
4
  //#region src/redaction.d.ts
6
5
  /**
7
6
  * Every attribute key in the contract flagged `highCardinality: true`, across
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"mappings":";;;;;;;AAwDa;;;;AC7Cb;;;;AA+EE;;;;;;;;iBDtDc,mBAAA,CAAoB,QAA2B,EAAjB,iBAAiB;;;;;;iBAkB/C,oBAAA,CACd,QAAA,EAAU,iBAAiB,EAC3B,GAAA;;;;;;;cC7CW,iCAAA,EA+EX,iBAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;iBAoCgB,oBAAoB,UAAU;;;;;;iBAkB9B,qBACd,UAAU,mBACV;;;;;;;cC7CW,mCAAA"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"sourcesContent":["/**\n * Cardinality posture helpers.\n *\n * The old cardinality rule — \"keep unique-value counts down\" — was a constraint\n * invented because dashboards have pixels and a graph with 10k series is\n * unreadable to a human. An agent does not look at the graph; it reads the\n * spans. A high-cardinality field (the user id, the sender domain, the request\n * id) is then the single most useful attribute on a trace when the agent is\n * chasing one specific failure.\n *\n * So the contract lets you mark attributes `highCardinality: true` as a\n * deliberate signal, and this module turns that into a *protect list*: the keys\n * a redactor or span-name normalizer must NOT strip, even when an aggressive\n * default would otherwise drop them.\n */\n\nimport type { TelemetryContract } from './contract.js';\n\n/**\n * Every attribute key in the contract flagged `highCardinality: true`, across\n * both common and per-span attributes. Feed this into a redaction/normalization\n * allow-list so the fields most useful to an agent reader survive.\n *\n * @example\n * ```ts\n * import { init } from 'autotel';\n * import { highCardinalityKeys } from 'autotel-schema';\n * import { contract } from './telemetry.contract';\n *\n * init({\n * service: 'checkout',\n * // keep user.id / request.id intact even under the strict redactor\n * attributeRedactor: { allowKeys: highCardinalityKeys(contract), preset: 'strict' },\n * });\n * ```\n */\nexport function highCardinalityKeys(contract: TelemetryContract): string[] {\n const keys = new Set<string>();\n for (const [key, spec] of Object.entries(contract.commonAttributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n for (const spanSpec of Object.values(contract.spans)) {\n for (const [key, spec] of Object.entries(spanSpec.attributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n }\n return [...keys].toSorted();\n}\n\n/**\n * Predicate form of {@link highCardinalityKeys} — `true` when `key` is declared\n * high-cardinality anywhere in the contract. Useful inside a custom\n * `spanNameNormalizer` or redactor callback.\n */\nexport function isHighCardinalityKey(\n contract: TelemetryContract,\n key: string,\n): boolean {\n if (contract.commonAttributes?.[key]?.highCardinality) return true;\n for (const spanSpec of Object.values(contract.spans)) {\n if (spanSpec.attributes?.[key]?.highCardinality) return true;\n }\n return false;\n}\n","import { defineContract } from '../contract.js';\n\nconst stringAttr = { type: 'string' as const };\nconst boolAttr = { type: 'boolean' as const };\nconst numberAttr = { type: 'number' as const };\nconst stringArrayAttr = { type: 'string[]' as const };\n\n/**\n * Published telemetry contract for Google SAIF-aligned agent security observability.\n * Span names are illustrative — attributes are the stable surface under validation.\n */\nexport const AGENT_SECURITY_TELEMETRY_CONTRACT = defineContract({\n service: 'autotel-agent-security',\n version: '1.0.0',\n commonAttributes: {\n 'autotel.agent': { ...boolAttr, required: false, description: 'Agent audit marker' },\n 'agent.controller.id': {\n ...stringAttr,\n highCardinality: true,\n description: 'Hashed controlling human user id',\n },\n 'agent.input.provenance': {\n ...stringAttr,\n enum: [\n 'user_direct',\n 'user_voice',\n 'rag',\n 'memory',\n 'tool_result',\n 'external_untrusted',\n ],\n },\n 'agent.action.risk_class': {\n ...stringAttr,\n enum: ['read', 'write', 'destructive', 'financial', 'exfiltration_capable'],\n },\n 'agent.consent.required': { ...boolAttr },\n 'agent.consent.outcome': {\n ...stringAttr,\n enum: ['approved', 'denied', 'timeout', 'revoked'],\n },\n 'agent.scope.active': { ...stringArrayAttr },\n 'agent.memory.operation': {\n ...stringAttr,\n enum: ['read', 'write', 'delete', 'search'],\n },\n 'agent.memory.isolation_key': { ...stringAttr, highCardinality: true },\n 'agent.plan.step_index': { ...numberAttr },\n 'agent.plan.tool_intents': { ...stringArrayAttr },\n 'agent.plan.risk.verdict': {\n ...stringAttr,\n enum: ['low', 'medium', 'high', 'critical'],\n },\n 'agent.plan.risk.score': { ...numberAttr },\n 'agent.plan.risk.categories': { ...stringArrayAttr },\n 'policy.decision': {\n ...stringAttr,\n enum: ['permit', 'deny', 'challenge', 'observe', 'error'],\n },\n 'tool.input_hash': { ...stringAttr },\n 'tool.output_hash': { ...stringAttr },\n 'mcp.tool.destructive': { ...boolAttr },\n 'mcp.tool.untrusted_content': { ...boolAttr },\n 'mcp.security.injection.verdict': {\n ...stringAttr,\n enum: ['clean', 'suspicious', 'malicious'],\n },\n 'security.event': { ...stringAttr },\n 'security.category': { ...stringAttr },\n 'security.outcome': { ...stringAttr },\n 'security.severity': {\n ...stringAttr,\n enum: ['info', 'warning', 'error', 'critical'],\n },\n },\n spans: {\n 'agent.action': {\n description: 'Scoped agent action or tool call with audit metadata',\n attributes: {\n 'agent.id': { ...stringAttr, required: true },\n 'tool.name': { ...stringAttr },\n },\n },\n 'tools/call': {\n description: 'MCP tool invocation with boundary security signals',\n attributes: {\n 'mcp.tool.name': { ...stringAttr, required: true },\n },\n },\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,oBAAoB,UAAuC;CACzE,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,oBAAoB,CAAC,CAAC,GACtE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAExC,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,cAAc,CAAC,CAAC,GAChE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAG1C,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,SAAS;AAC5B;;;;;;AAOA,SAAgB,qBACd,UACA,KACS;CACT,IAAI,SAAS,mBAAmB,IAAI,EAAE,iBAAiB,OAAO;CAC9D,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,IAAI,SAAS,aAAa,IAAI,EAAE,iBAAiB,OAAO;CAE1D,OAAO;AACT;;;;AC7DA,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,WAAW,EAAE,MAAM,UAAmB;AAC5C,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,kBAAkB,EAAE,MAAM,WAAoB;;;;;AAMpD,MAAa,oCAAoC,eAAe;CAC9D,SAAS;CACT,SAAS;CACT,kBAAkB;EAChB,iBAAiB;GAAE,GAAG;GAAU,UAAU;GAAO,aAAa;EAAqB;EACnF,uBAAuB;GACrB,GAAG;GACH,iBAAiB;GACjB,aAAa;EACf;EACA,0BAA0B;GACxB,GAAG;GACH,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;GACF;EACF;EACA,2BAA2B;GACzB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAS;IAAe;IAAa;GAAsB;EAC5E;EACA,0BAA0B,EAAE,GAAG,SAAS;EACxC,yBAAyB;GACvB,GAAG;GACH,MAAM;IAAC;IAAY;IAAU;IAAW;GAAS;EACnD;EACA,sBAAsB,EAAE,GAAG,gBAAgB;EAC3C,0BAA0B;GACxB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAS;IAAU;GAAQ;EAC5C;EACA,8BAA8B;GAAE,GAAG;GAAY,iBAAiB;EAAK;EACrE,yBAAyB,EAAE,GAAG,WAAW;EACzC,2BAA2B,EAAE,GAAG,gBAAgB;EAChD,2BAA2B;GACzB,GAAG;GACH,MAAM;IAAC;IAAO;IAAU;IAAQ;GAAU;EAC5C;EACA,yBAAyB,EAAE,GAAG,WAAW;EACzC,8BAA8B,EAAE,GAAG,gBAAgB;EACnD,mBAAmB;GACjB,GAAG;GACH,MAAM;IAAC;IAAU;IAAQ;IAAa;IAAW;GAAO;EAC1D;EACA,mBAAmB,EAAE,GAAG,WAAW;EACnC,oBAAoB,EAAE,GAAG,WAAW;EACpC,wBAAwB,EAAE,GAAG,SAAS;EACtC,8BAA8B,EAAE,GAAG,SAAS;EAC5C,kCAAkC;GAChC,GAAG;GACH,MAAM;IAAC;IAAS;IAAc;GAAW;EAC3C;EACA,kBAAkB,EAAE,GAAG,WAAW;EAClC,qBAAqB,EAAE,GAAG,WAAW;EACrC,oBAAoB,EAAE,GAAG,WAAW;EACpC,qBAAqB;GACnB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAW;IAAS;GAAU;EAC/C;CACF;CACA,OAAO;EACL,gBAAgB;GACd,aAAa;GACb,YAAY;IACV,YAAY;KAAE,GAAG;KAAY,UAAU;IAAK;IAC5C,aAAa,EAAE,GAAG,WAAW;GAC/B;EACF;EACA,cAAc;GACZ,aAAa;GACb,YAAY,EACV,iBAAiB;IAAE,GAAG;IAAY,UAAU;GAAK,EACnD;EACF;CACF;AACF,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/redaction.ts","../src/contracts/agent-security.ts"],"sourcesContent":["/**\n * Cardinality posture helpers.\n *\n * The old cardinality rule — \"keep unique-value counts down\" — was a constraint\n * invented because dashboards have pixels and a graph with 10k series is\n * unreadable to a human. An agent does not look at the graph; it reads the\n * spans. A high-cardinality field (the user id, the sender domain, the request\n * id) is then the single most useful attribute on a trace when the agent is\n * chasing one specific failure.\n *\n * So the contract lets you mark attributes `highCardinality: true` as a\n * deliberate signal, and this module turns that into a *protect list*: the keys\n * a redactor or span-name normalizer must NOT strip, even when an aggressive\n * default would otherwise drop them.\n */\n\nimport type { TelemetryContract } from './contract.js';\n\n/**\n * Every attribute key in the contract flagged `highCardinality: true`, across\n * both common and per-span attributes. Feed this into a redaction/normalization\n * allow-list so the fields most useful to an agent reader survive.\n *\n * @example\n * ```ts\n * import { init } from 'autotel';\n * import { highCardinalityKeys } from 'autotel-schema';\n * import { contract } from './telemetry.contract';\n *\n * init({\n * service: 'checkout',\n * // keep user.id / request.id intact even under the strict redactor\n * attributeRedactor: { allowKeys: highCardinalityKeys(contract), preset: 'strict' },\n * });\n * ```\n */\nexport function highCardinalityKeys(contract: TelemetryContract): string[] {\n const keys = new Set<string>();\n for (const [key, spec] of Object.entries(contract.commonAttributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n for (const spanSpec of Object.values(contract.spans)) {\n for (const [key, spec] of Object.entries(spanSpec.attributes ?? {})) {\n if (spec.highCardinality) keys.add(key);\n }\n }\n return [...keys].toSorted();\n}\n\n/**\n * Predicate form of {@link highCardinalityKeys} — `true` when `key` is declared\n * high-cardinality anywhere in the contract. Useful inside a custom\n * `spanNameNormalizer` or redactor callback.\n */\nexport function isHighCardinalityKey(\n contract: TelemetryContract,\n key: string,\n): boolean {\n if (contract.commonAttributes?.[key]?.highCardinality) return true;\n for (const spanSpec of Object.values(contract.spans)) {\n if (spanSpec.attributes?.[key]?.highCardinality) return true;\n }\n return false;\n}\n","import { defineContract } from '../contract.js';\n\nconst stringAttr = { type: 'string' as const };\nconst boolAttr = { type: 'boolean' as const };\nconst numberAttr = { type: 'number' as const };\nconst stringArrayAttr = { type: 'string[]' as const };\n\n/**\n * Published telemetry contract for Google SAIF-aligned agent security observability.\n * Span names are illustrative — attributes are the stable surface under validation.\n */\nexport const AGENT_SECURITY_TELEMETRY_CONTRACT = defineContract({\n service: 'autotel-agent-security',\n version: '1.0.0',\n commonAttributes: {\n 'autotel.agent': {\n ...boolAttr,\n required: false,\n description: 'Agent audit marker',\n },\n 'agent.controller.id': {\n ...stringAttr,\n highCardinality: true,\n description: 'Hashed controlling human user id',\n },\n 'agent.input.provenance': {\n ...stringAttr,\n enum: [\n 'user_direct',\n 'user_voice',\n 'rag',\n 'memory',\n 'tool_result',\n 'external_untrusted',\n ],\n },\n 'agent.action.risk_class': {\n ...stringAttr,\n enum: [\n 'read',\n 'write',\n 'destructive',\n 'financial',\n 'exfiltration_capable',\n ],\n },\n 'agent.consent.required': { ...boolAttr },\n 'agent.consent.outcome': {\n ...stringAttr,\n enum: ['approved', 'denied', 'timeout', 'revoked'],\n },\n 'agent.scope.active': { ...stringArrayAttr },\n 'agent.memory.operation': {\n ...stringAttr,\n enum: ['read', 'write', 'delete', 'search'],\n },\n 'agent.memory.isolation_key': { ...stringAttr, highCardinality: true },\n 'agent.plan.step_index': { ...numberAttr },\n 'agent.plan.tool_intents': { ...stringArrayAttr },\n 'agent.plan.risk.verdict': {\n ...stringAttr,\n enum: ['low', 'medium', 'high', 'critical'],\n },\n 'agent.plan.risk.score': { ...numberAttr },\n 'agent.plan.risk.categories': { ...stringArrayAttr },\n 'policy.decision': {\n ...stringAttr,\n enum: ['permit', 'deny', 'challenge', 'observe', 'error'],\n },\n 'tool.input_hash': { ...stringAttr },\n 'tool.output_hash': { ...stringAttr },\n 'mcp.tool.destructive': { ...boolAttr },\n 'mcp.tool.untrusted_content': { ...boolAttr },\n 'mcp.security.injection.verdict': {\n ...stringAttr,\n enum: ['clean', 'suspicious', 'malicious'],\n },\n 'security.event': { ...stringAttr },\n 'security.category': { ...stringAttr },\n 'security.outcome': { ...stringAttr },\n 'security.severity': {\n ...stringAttr,\n enum: ['info', 'warning', 'error', 'critical'],\n },\n },\n spans: {\n 'agent.action': {\n description: 'Scoped agent action or tool call with audit metadata',\n attributes: {\n 'agent.id': { ...stringAttr, required: true },\n 'tool.name': { ...stringAttr },\n },\n },\n 'tools/call': {\n description: 'MCP tool invocation with boundary security signals',\n attributes: {\n 'mcp.tool.name': { ...stringAttr, required: true },\n },\n },\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,oBAAoB,UAAuC;CACzE,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,oBAAoB,CAAC,CAAC,GACtE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAExC,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,SAAS,cAAc,CAAC,CAAC,GAChE,IAAI,KAAK,iBAAiB,KAAK,IAAI,GAAG;CAG1C,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,SAAS;AAC5B;;;;;;AAOA,SAAgB,qBACd,UACA,KACS;CACT,IAAI,SAAS,mBAAmB,IAAI,EAAE,iBAAiB,OAAO;CAC9D,KAAK,MAAM,YAAY,OAAO,OAAO,SAAS,KAAK,GACjD,IAAI,SAAS,aAAa,IAAI,EAAE,iBAAiB,OAAO;CAE1D,OAAO;AACT;;;;AC7DA,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,WAAW,EAAE,MAAM,UAAmB;AAC5C,MAAM,aAAa,EAAE,MAAM,SAAkB;AAC7C,MAAM,kBAAkB,EAAE,MAAM,WAAoB;;;;;AAMpD,MAAa,oCAAoC,eAAe;CAC9D,SAAS;CACT,SAAS;CACT,kBAAkB;EAChB,iBAAiB;GACf,GAAG;GACH,UAAU;GACV,aAAa;EACf;EACA,uBAAuB;GACrB,GAAG;GACH,iBAAiB;GACjB,aAAa;EACf;EACA,0BAA0B;GACxB,GAAG;GACH,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;GACF;EACF;EACA,2BAA2B;GACzB,GAAG;GACH,MAAM;IACJ;IACA;IACA;IACA;IACA;GACF;EACF;EACA,0BAA0B,EAAE,GAAG,SAAS;EACxC,yBAAyB;GACvB,GAAG;GACH,MAAM;IAAC;IAAY;IAAU;IAAW;GAAS;EACnD;EACA,sBAAsB,EAAE,GAAG,gBAAgB;EAC3C,0BAA0B;GACxB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAS;IAAU;GAAQ;EAC5C;EACA,8BAA8B;GAAE,GAAG;GAAY,iBAAiB;EAAK;EACrE,yBAAyB,EAAE,GAAG,WAAW;EACzC,2BAA2B,EAAE,GAAG,gBAAgB;EAChD,2BAA2B;GACzB,GAAG;GACH,MAAM;IAAC;IAAO;IAAU;IAAQ;GAAU;EAC5C;EACA,yBAAyB,EAAE,GAAG,WAAW;EACzC,8BAA8B,EAAE,GAAG,gBAAgB;EACnD,mBAAmB;GACjB,GAAG;GACH,MAAM;IAAC;IAAU;IAAQ;IAAa;IAAW;GAAO;EAC1D;EACA,mBAAmB,EAAE,GAAG,WAAW;EACnC,oBAAoB,EAAE,GAAG,WAAW;EACpC,wBAAwB,EAAE,GAAG,SAAS;EACtC,8BAA8B,EAAE,GAAG,SAAS;EAC5C,kCAAkC;GAChC,GAAG;GACH,MAAM;IAAC;IAAS;IAAc;GAAW;EAC3C;EACA,kBAAkB,EAAE,GAAG,WAAW;EAClC,qBAAqB,EAAE,GAAG,WAAW;EACrC,oBAAoB,EAAE,GAAG,WAAW;EACpC,qBAAqB;GACnB,GAAG;GACH,MAAM;IAAC;IAAQ;IAAW;IAAS;GAAU;EAC/C;CACF;CACA,OAAO;EACL,gBAAgB;GACd,aAAa;GACb,YAAY;IACV,YAAY;KAAE,GAAG;KAAY,UAAU;IAAK;IAC5C,aAAa,EAAE,GAAG,WAAW;GAC/B;EACF;EACA,cAAc;GACZ,aAAa;GACb,YAAY,EACV,iBAAiB;IAAE,GAAG;IAAY,UAAU;GAAK,EACnD;EACF;CACF;AACF,CAAC"}