agentfootprint 9.51.0 → 9.53.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 (98) hide show
  1. package/AGENTS.md +21 -1
  2. package/CLAUDE.md +6 -2
  3. package/ai-instructions/claude-code/SKILL.md +18 -1
  4. package/bin/agentfootprint-check-semantics.mjs +14 -0
  5. package/dist/core/agent/coverage/read.js +13 -0
  6. package/dist/core/agent/coverage/read.js.map +1 -1
  7. package/dist/core/agent/stages/toolCalls.js +141 -4
  8. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  9. package/dist/core/tools.js +25 -1
  10. package/dist/core/tools.js.map +1 -1
  11. package/dist/debug.js +14 -3
  12. package/dist/debug.js.map +1 -1
  13. package/dist/esm/core/agent/coverage/read.js +13 -0
  14. package/dist/esm/core/agent/coverage/read.js.map +1 -1
  15. package/dist/esm/core/agent/stages/toolCalls.js +141 -4
  16. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  17. package/dist/esm/core/tools.d.ts +64 -0
  18. package/dist/esm/core/tools.js +23 -0
  19. package/dist/esm/core/tools.js.map +1 -1
  20. package/dist/esm/debug.d.ts +1 -0
  21. package/dist/esm/debug.js +7 -0
  22. package/dist/esm/debug.js.map +1 -1
  23. package/dist/esm/events/payloads.d.ts +49 -0
  24. package/dist/esm/events/registry.d.ts +5 -1
  25. package/dist/esm/events/registry.js +4 -0
  26. package/dist/esm/events/registry.js.map +1 -1
  27. package/dist/esm/index.d.ts +2 -1
  28. package/dist/esm/index.js +11 -1
  29. package/dist/esm/index.js.map +1 -1
  30. package/dist/esm/lib/mcp/mcpServe.js +10 -0
  31. package/dist/esm/lib/mcp/mcpServe.js.map +1 -1
  32. package/dist/esm/lib/semantics/check.d.ts +70 -0
  33. package/dist/esm/lib/semantics/check.js +154 -0
  34. package/dist/esm/lib/semantics/check.js.map +1 -0
  35. package/dist/esm/lib/semantics/cli.d.ts +40 -0
  36. package/dist/esm/lib/semantics/cli.js +148 -0
  37. package/dist/esm/lib/semantics/cli.js.map +1 -0
  38. package/dist/esm/lib/semantics/envelope.d.ts +139 -0
  39. package/dist/esm/lib/semantics/envelope.js +592 -0
  40. package/dist/esm/lib/semantics/envelope.js.map +1 -0
  41. package/dist/esm/lib/semantics/format.d.ts +11 -0
  42. package/dist/esm/lib/semantics/format.js +33 -0
  43. package/dist/esm/lib/semantics/format.js.map +1 -0
  44. package/dist/esm/lib/semantics/index.d.ts +14 -0
  45. package/dist/esm/lib/semantics/index.js +15 -0
  46. package/dist/esm/lib/semantics/index.js.map +1 -0
  47. package/dist/esm/lib/semantics/types.d.ts +230 -0
  48. package/dist/esm/lib/semantics/types.js +71 -0
  49. package/dist/esm/lib/semantics/types.js.map +1 -0
  50. package/dist/esm/lib/trace-toolpack/traceToolpack.js +6 -0
  51. package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -1
  52. package/dist/events/registry.js +4 -0
  53. package/dist/events/registry.js.map +1 -1
  54. package/dist/index.js +65 -44
  55. package/dist/index.js.map +1 -1
  56. package/dist/lib/mcp/mcpServe.js +10 -0
  57. package/dist/lib/mcp/mcpServe.js.map +1 -1
  58. package/dist/lib/semantics/check.js +158 -0
  59. package/dist/lib/semantics/check.js.map +1 -0
  60. package/dist/lib/semantics/cli.js +176 -0
  61. package/dist/lib/semantics/cli.js.map +1 -0
  62. package/dist/lib/semantics/envelope.js +603 -0
  63. package/dist/lib/semantics/envelope.js.map +1 -0
  64. package/dist/lib/semantics/format.js +37 -0
  65. package/dist/lib/semantics/format.js.map +1 -0
  66. package/dist/lib/semantics/index.js +34 -0
  67. package/dist/lib/semantics/index.js.map +1 -0
  68. package/dist/lib/semantics/types.js +74 -0
  69. package/dist/lib/semantics/types.js.map +1 -0
  70. package/dist/lib/trace-toolpack/traceToolpack.js +6 -0
  71. package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -1
  72. package/dist/types/core/agent/coverage/read.d.ts.map +1 -1
  73. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  74. package/dist/types/core/tools.d.ts +64 -0
  75. package/dist/types/core/tools.d.ts.map +1 -1
  76. package/dist/types/debug.d.ts +1 -0
  77. package/dist/types/debug.d.ts.map +1 -1
  78. package/dist/types/events/payloads.d.ts +49 -0
  79. package/dist/types/events/payloads.d.ts.map +1 -1
  80. package/dist/types/events/registry.d.ts +5 -1
  81. package/dist/types/events/registry.d.ts.map +1 -1
  82. package/dist/types/index.d.ts +2 -1
  83. package/dist/types/index.d.ts.map +1 -1
  84. package/dist/types/lib/mcp/mcpServe.d.ts.map +1 -1
  85. package/dist/types/lib/semantics/check.d.ts +71 -0
  86. package/dist/types/lib/semantics/check.d.ts.map +1 -0
  87. package/dist/types/lib/semantics/cli.d.ts +41 -0
  88. package/dist/types/lib/semantics/cli.d.ts.map +1 -0
  89. package/dist/types/lib/semantics/envelope.d.ts +140 -0
  90. package/dist/types/lib/semantics/envelope.d.ts.map +1 -0
  91. package/dist/types/lib/semantics/format.d.ts +12 -0
  92. package/dist/types/lib/semantics/format.d.ts.map +1 -0
  93. package/dist/types/lib/semantics/index.d.ts +15 -0
  94. package/dist/types/lib/semantics/index.d.ts.map +1 -0
  95. package/dist/types/lib/semantics/types.d.ts +231 -0
  96. package/dist/types/lib/semantics/types.d.ts.map +1 -0
  97. package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -1
  98. package/package.json +2 -1
@@ -0,0 +1,154 @@
1
+ /**
2
+ * semantics/check — the `check:semantics` gate core (9.53.0).
3
+ *
4
+ * Pattern: humble shell (the tool-lint precedent) — ALL behavior lives here,
5
+ * unit-tested; `bin/agentfootprint-check-semantics.mjs` only resolves
6
+ * the built module and maps the exit code. Consumers wire the bin
7
+ * into their own `check:` scripts beside `check:tools`.
8
+ * Role: lib/ layer, pure. Judges SAMPLE RESULTS against the semantic
9
+ * vocabulary and the per-class rules; never executes a tool, never
10
+ * touches a network — the samples come from the consumer's own mock
11
+ * tools or fixtures, the same way `check:tools` receives a dumped
12
+ * catalog.
13
+ * Emits: N/A.
14
+ *
15
+ * ## Severity follows provability (the skillGraph check-up law)
16
+ *
17
+ * Only what the declaration PROVES wrong is an error:
18
+ *
19
+ * • a `'triage'` / `'inventory'` tool whose sample result declares no
20
+ * coverage — the CLASS declared the requirement, the sample violates it;
21
+ * • a marker-bearing envelope with faults (series without grain, data
22
+ * without provenance, a counter-looking aggregation with `is_counter`
23
+ * unstated, malformed shapes) — the marker declared the vocabulary, the
24
+ * shape violates it.
25
+ *
26
+ * Everything else warns: a classed tool with NO samples (the gate cannot
27
+ * check what it cannot see — but silence must not read as a pass), and an
28
+ * inventory whose facts carry no render hint (useful, not provably harmful).
29
+ */
30
+ import { readCoverageResult } from '../../core/agent/coverage/read.js';
31
+ import { explainSemantics, readSemantics } from './envelope.js';
32
+ import { RESULT_CLASSES } from './types.js';
33
+ const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
34
+ /** True when a result states its boundary through ANY recognized door: a
35
+ * semantic envelope with `coverage`, a `coverage()` ledger, or an
36
+ * `absent()` (whose `checked` IS its coverage). */
37
+ function declaresCoverage(result) {
38
+ const sem = readSemantics(result);
39
+ if (sem !== undefined)
40
+ return sem.coverage !== undefined;
41
+ return readCoverageResult(result) !== undefined;
42
+ }
43
+ function classCoverageFinding(entry, resultIndex) {
44
+ const cls = entry.resultClass;
45
+ return {
46
+ tool: entry.name,
47
+ code: cls === 'triage' ? 'triage-without-coverage' : 'inventory-without-coverage',
48
+ severity: 'error',
49
+ field: 'coverage',
50
+ resultIndex,
51
+ message: `tool '${entry.name}' is declared '${cls}' but sample result ${resultIndex + 1} ` +
52
+ `declares no coverage — field \`coverage\` is required on every ${cls} result. ` +
53
+ (cls === 'triage'
54
+ ? `A triage that cannot say what it did NOT check turns "everything looks fine" into ` +
55
+ `a claim about ground it never stood on. `
56
+ : `An inventory that cannot say which population it covered ("4 of 5 clusters") reads ` +
57
+ `as the whole fleet. `) +
58
+ `Return semantic({ …, coverage: { checked, notChecked, cannotCover } }), or coverage(result, …), ` +
59
+ `or absent({ … }) when nothing was found.`,
60
+ };
61
+ }
62
+ /**
63
+ * Judge a semantics catalog. Pure; throws only on a malformed CATALOG (a
64
+ * definition-side mistake — the CLI maps it to exit code 2), never on a
65
+ * malformed RESULT (that is a finding, the thing this gate exists to report).
66
+ */
67
+ export function checkSemantics(entries) {
68
+ if (!Array.isArray(entries)) {
69
+ throw new Error('checkSemantics: takes an array of { name, resultClass?, results } entries.');
70
+ }
71
+ const findings = [];
72
+ let checkedResults = 0;
73
+ entries.forEach((raw, entryIndex) => {
74
+ if (!isPlainObject(raw)) {
75
+ throw new Error(`checkSemantics: entries[${entryIndex}] is not an object.`);
76
+ }
77
+ const entry = raw;
78
+ if (typeof entry.name !== 'string' || entry.name.trim().length === 0) {
79
+ throw new Error(`checkSemantics: entries[${entryIndex}] has no tool name.`);
80
+ }
81
+ const cls = entry.resultClass;
82
+ if (cls !== undefined && !RESULT_CLASSES.includes(cls)) {
83
+ throw new Error(`checkSemantics: tool '${entry.name}' declares resultClass '${String(cls)}', which is ` +
84
+ `not a class this library has. The classes are: ${RESULT_CLASSES.join(', ')}. ` +
85
+ `Omit the field for a tool with no class rules.`);
86
+ }
87
+ const results = entry.results;
88
+ if (!Array.isArray(results)) {
89
+ throw new Error(`checkSemantics: tool '${entry.name}' has no \`results\` array — the gate judges ` +
90
+ `sample results; give it at least one (your mock tool's return is the natural sample).`);
91
+ }
92
+ if (cls !== undefined && results.length === 0) {
93
+ findings.push({
94
+ tool: entry.name,
95
+ code: 'unsampled-tool-class',
96
+ severity: 'warning',
97
+ field: 'results',
98
+ message: `tool '${entry.name}' is declared '${cls}' but no sample results were given — the ` +
99
+ `gate cannot check what it cannot see, and a class rule with no samples is a promise ` +
100
+ `nobody is keeping. Add one sample result (a mock return is enough).`,
101
+ });
102
+ }
103
+ results.forEach((result, resultIndex) => {
104
+ checkedResults += 1;
105
+ // A marker-bearing value with faults: every fault becomes a finding
106
+ // under the recognizer's own code, naming the field.
107
+ const issues = explainSemantics(result);
108
+ if (issues !== undefined) {
109
+ for (const issue of issues) {
110
+ findings.push({
111
+ tool: entry.name,
112
+ code: issue.code,
113
+ severity: 'error',
114
+ field: issue.field,
115
+ resultIndex,
116
+ message: `tool '${entry.name}', sample result ${resultIndex + 1}: ${issue.message}`,
117
+ });
118
+ }
119
+ // An unrecognizable envelope cannot be judged for class rules — the
120
+ // faults above are already errors, and guessing at a broken shape
121
+ // would report the same problem twice under two names.
122
+ return;
123
+ }
124
+ const sem = readSemantics(result);
125
+ // ── The class rules ──
126
+ if ((cls === 'triage' || cls === 'inventory') && !declaresCoverage(result)) {
127
+ findings.push(classCoverageFinding(entry, resultIndex));
128
+ }
129
+ if (cls === 'inventory' &&
130
+ sem !== undefined &&
131
+ sem.facts !== undefined &&
132
+ sem.render === undefined) {
133
+ findings.push({
134
+ tool: entry.name,
135
+ code: 'inventory-without-render',
136
+ severity: 'warning',
137
+ field: 'render',
138
+ resultIndex,
139
+ message: `tool '${entry.name}', sample result ${resultIndex + 1}: an inventory with ` +
140
+ `\`facts\` and no \`render\` hint — a UI can still draw the rows, but the tool ` +
141
+ `author knows the right columns and sort better than any consumer. Consider ` +
142
+ `render: { default: 'table', columns: […] }.`,
143
+ });
144
+ }
145
+ });
146
+ });
147
+ return {
148
+ ok: !findings.some((f) => f.severity === 'error'),
149
+ findings,
150
+ checkedTools: entries.length,
151
+ checkedResults,
152
+ };
153
+ }
154
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../../../src/lib/semantics/check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * check:semantics CLI core (9.53.0 — the build gate).
3
+ *
4
+ * Pattern: humble shell (the tool-lint `cli.ts` precedent) —
5
+ * `bin/agentfootprint-check-semantics.mjs` is a 2-line wrapper;
6
+ * ALL behavior (arg parsing, catalog coercion, report, exit code)
7
+ * lives here so it is unit-testable without spawning a process.
8
+ * Role: `src/lib/semantics/`. Reads ONE JSON file of tools + sample
9
+ * results, prints a report, returns the process exit code:
10
+ * 0 — report.ok (and, under --strict, zero warnings)
11
+ * 1 — findings failed the gate
12
+ * 2 — usage / input error (bad flags, unreadable file,
13
+ * unrecognized JSON shape, unknown resultClass)
14
+ *
15
+ * Consumers wire it beside their other gates (the `check:tools` convention):
16
+ *
17
+ * "check:semantics": "node scripts/dump-semantics-catalog.mjs && agentfootprint-check-semantics semantics-catalog.json"
18
+ *
19
+ * The catalog is sample results — what your MOCK tools return — because the
20
+ * gate judges result shapes, and a build must never run tools that touch
21
+ * live systems. A mock catalog is something this ecosystem already has
22
+ * everywhere the check matters.
23
+ */
24
+ import { type SemanticsCatalogEntry } from './check.js';
25
+ export interface SemanticsCliIO {
26
+ readonly stdout: (line: string) => void;
27
+ readonly stderr: (line: string) => void;
28
+ }
29
+ /**
30
+ * Normalize the accepted JSON shapes to the checker's catalog. Throws (with
31
+ * a shape description) on unrecognized input — the CLI maps that to exit
32
+ * code 2. `resultClass` values are passed through untouched; the checker is
33
+ * the one owner of the closed-set refusal.
34
+ */
35
+ export declare function coerceSemanticsCatalog(json: unknown): readonly SemanticsCatalogEntry[];
36
+ /**
37
+ * Run the gate CLI. Returns the exit code (never calls `process.exit` — the
38
+ * bin wrapper assigns it to `process.exitCode`).
39
+ */
40
+ export declare function runCheckSemanticsCli(argv: readonly string[], io?: SemanticsCliIO): Promise<number>;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * check:semantics CLI core (9.53.0 — the build gate).
3
+ *
4
+ * Pattern: humble shell (the tool-lint `cli.ts` precedent) —
5
+ * `bin/agentfootprint-check-semantics.mjs` is a 2-line wrapper;
6
+ * ALL behavior (arg parsing, catalog coercion, report, exit code)
7
+ * lives here so it is unit-testable without spawning a process.
8
+ * Role: `src/lib/semantics/`. Reads ONE JSON file of tools + sample
9
+ * results, prints a report, returns the process exit code:
10
+ * 0 — report.ok (and, under --strict, zero warnings)
11
+ * 1 — findings failed the gate
12
+ * 2 — usage / input error (bad flags, unreadable file,
13
+ * unrecognized JSON shape, unknown resultClass)
14
+ *
15
+ * Consumers wire it beside their other gates (the `check:tools` convention):
16
+ *
17
+ * "check:semantics": "node scripts/dump-semantics-catalog.mjs && agentfootprint-check-semantics semantics-catalog.json"
18
+ *
19
+ * The catalog is sample results — what your MOCK tools return — because the
20
+ * gate judges result shapes, and a build must never run tools that touch
21
+ * live systems. A mock catalog is something this ecosystem already has
22
+ * everywhere the check matters.
23
+ */
24
+ import { checkSemantics } from './check.js';
25
+ import { formatSemanticsReport } from './format.js';
26
+ import { RESULT_CLASSES } from './types.js';
27
+ const USAGE = `usage: agentfootprint-check-semantics <semantics-catalog.json> [options]
28
+
29
+ <semantics-catalog.json> JSON file of tools + sample results. Accepted shapes:
30
+ [{ name, resultClass?, results: [...] }]
31
+ [{ name, resultClass?, result: ... }] (single sample)
32
+ { tools: [...] } (either row shape)
33
+
34
+ resultClass ∈ { ${RESULT_CLASSES.join(', ')} } — the class
35
+ declared on defineTool({ resultClass }); omit for tools
36
+ with no class rules (envelope rules still apply).
37
+
38
+ --strict warnings also fail the gate
39
+ --json print the full report as JSON instead of text
40
+
41
+ exit codes: 0 ok · 1 findings failed the gate · 2 usage/input error`;
42
+ /**
43
+ * Normalize the accepted JSON shapes to the checker's catalog. Throws (with
44
+ * a shape description) on unrecognized input — the CLI maps that to exit
45
+ * code 2. `resultClass` values are passed through untouched; the checker is
46
+ * the one owner of the closed-set refusal.
47
+ */
48
+ export function coerceSemanticsCatalog(json) {
49
+ const list = Array.isArray(json)
50
+ ? json
51
+ : json !== null &&
52
+ typeof json === 'object' &&
53
+ Array.isArray(json.tools)
54
+ ? json.tools
55
+ : undefined;
56
+ if (list === undefined) {
57
+ throw new Error('expected a JSON array of { name, resultClass?, results } rows or { tools: [...] }');
58
+ }
59
+ return list.map((raw, index) => {
60
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
61
+ throw new Error(`tools[${index}] is not an object`);
62
+ }
63
+ const entry = raw;
64
+ const name = entry.name;
65
+ if (typeof name !== 'string' || name.length === 0) {
66
+ throw new Error(`tools[${index}] has no string 'name'`);
67
+ }
68
+ const results = Array.isArray(entry.results)
69
+ ? entry.results
70
+ : 'result' in entry
71
+ ? [entry.result]
72
+ : undefined;
73
+ if (results === undefined) {
74
+ throw new Error(`tools[${index}] ('${name}') has neither 'results' (an array of sample results) nor ` +
75
+ `'result' (one sample) — the gate judges sample results; a mock tool's return is ` +
76
+ `the natural sample.`);
77
+ }
78
+ return {
79
+ name,
80
+ ...(entry.resultClass !== undefined
81
+ ? { resultClass: entry.resultClass }
82
+ : {}),
83
+ results,
84
+ };
85
+ });
86
+ }
87
+ /**
88
+ * Run the gate CLI. Returns the exit code (never calls `process.exit` — the
89
+ * bin wrapper assigns it to `process.exitCode`).
90
+ */
91
+ export async function runCheckSemanticsCli(argv, io = {
92
+ // eslint-disable-next-line no-console
93
+ stdout: (line) => console.log(line),
94
+ // eslint-disable-next-line no-console
95
+ stderr: (line) => console.error(line),
96
+ }) {
97
+ let file;
98
+ let strict = false;
99
+ let json = false;
100
+ for (const arg of argv) {
101
+ if (arg === '--strict')
102
+ strict = true;
103
+ else if (arg === '--json')
104
+ json = true;
105
+ else if (arg === '--help' || arg === '-h') {
106
+ io.stderr(USAGE);
107
+ return 2;
108
+ }
109
+ else if (arg.startsWith('-')) {
110
+ io.stderr(`unknown flag '${arg}'\n\n${USAGE}`);
111
+ return 2;
112
+ }
113
+ else if (file === undefined)
114
+ file = arg;
115
+ else {
116
+ io.stderr(`unexpected extra argument '${arg}'\n\n${USAGE}`);
117
+ return 2;
118
+ }
119
+ }
120
+ if (file === undefined) {
121
+ io.stderr(USAGE);
122
+ return 2;
123
+ }
124
+ let report;
125
+ try {
126
+ // Lazy node:fs import (browser-compat — the tool-lint precedent):
127
+ // `agentfootprint/observe` re-exports this module, and a top-level
128
+ // node:fs import detonates a browser bundle at module-eval. The CLI
129
+ // path is the only consumer that touches the filesystem.
130
+ const { readFile } = await import('node:fs/promises');
131
+ const catalog = coerceSemanticsCatalog(JSON.parse(await readFile(file, 'utf8')));
132
+ report = checkSemantics(catalog);
133
+ }
134
+ catch (error) {
135
+ io.stderr(`agentfootprint-check-semantics: ${file}: ${error.message}`);
136
+ return 2;
137
+ }
138
+ if (json)
139
+ io.stdout(JSON.stringify(report, null, 2));
140
+ else
141
+ io.stdout(formatSemanticsReport(report));
142
+ if (!report.ok)
143
+ return 1;
144
+ if (strict && report.findings.length > 0)
145
+ return 1;
146
+ return 0;
147
+ }
148
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../../src/lib/semantics/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * semantics/envelope — minting, recognizing and projecting the semantic
3
+ * tool-result envelope (9.53.0).
4
+ *
5
+ * Pattern: minted by a helper, RECOGNIZED by the framework (the `absent()` /
6
+ * tool-effects precedent). A return shape the framework does not
7
+ * understand is a convention, and a convention cannot ride the
8
+ * record, feed a UI, or be refused by a build gate.
9
+ * Role: lib/ layer, pure. The dispatch loop calls `readSemantics` at the
10
+ * execute boundary; `semantic()` is what a tool author writes;
11
+ * `checkSemantics` (check.ts) judges the same shapes offline.
12
+ * Emits: N/A (the caller emits `agentfootprint.tools.semantics_declared`).
13
+ *
14
+ * ## Two views of one envelope — the design decision, stated
15
+ *
16
+ * The MODEL reads a compact, rendering-free projection
17
+ * ({@link semanticsForModel}): the data (`series`/`facts`/`edges`), the
18
+ * caveats that must travel with it (`grain`, `provenance`), the composed
19
+ * `not_covered` prose, a non-null `clarify`, and the static note. Dropped
20
+ * from the model's view: the `af_semantics` marker (machine-only), `render`
21
+ * (a UI hint — the tool never renders, and a model parroting rendering
22
+ * directives is noise), the three-list `coverage` detail (it rides the
23
+ * coverage channel and the record; the model reads the composed
24
+ * `not_covered` lines instead), and a `clarify: null` (a stated non-question
25
+ * is a fact for the record, not something the model acts on).
26
+ *
27
+ * The RECORD gets everything: the full envelope — render, coverage,
28
+ * marker and all — lands on the `tools.semantics_declared` event BEFORE the
29
+ * result ceiling is measured, so grain and provenance survive to recordings
30
+ * and UIs even when the content itself is refused as oversized. The
31
+ * `coverage` field is additionally declared through the SAME channel the
32
+ * `coverage()` primitive uses (`tools.coverage_declared`, tracked state, the
33
+ * final-answer limits block) — absorbed, never duplicated.
34
+ *
35
+ * ## One rule set, two doors
36
+ *
37
+ * `semantic()` refuses a declaration this vocabulary cannot honor at the
38
+ * CALL SITE (the `absent()` law) — so a minted envelope is honest by
39
+ * construction: series carry their grain, data carries its provenance,
40
+ * counter-looking aggregations state `is_counter`. `semanticIssues()` judges
41
+ * the RENDERED shape — the same rules over a value somebody may have built
42
+ * by hand — and is what recognition and the `check:semantics` gate both
43
+ * stand on. Recognition is STRICT (the zero-cost guarantee): a marker-
44
+ * bearing value with any issue is NOT recognized — it keeps its bytes on
45
+ * the data path (dev-warned, and named field-by-field by the gate), because
46
+ * this library does not half-apply a shape it cannot fully honor.
47
+ */
48
+ import type { Coverage } from '../../core/agent/coverage/types.js';
49
+ import { type SemanticCoverage, type SemanticDeclaration, type ToolSemantics } from './types.js';
50
+ /** The codes an envelope can be faulted with — shared by recognition (any
51
+ * issue ⇒ not recognized) and the `check:semantics` gate (issues become
52
+ * findings under these same names). */
53
+ export type SemanticIssueCode = 'malformed-semantics' | 'series-without-grain' | 'counter-aggregation-unstated' | 'data-without-provenance';
54
+ /** One fault, naming the field so a refusal can teach and a gate can point. */
55
+ export interface SemanticIssue {
56
+ readonly code: SemanticIssueCode;
57
+ /** The offending / missing field, dot-pathed ('grain.is_counter'). */
58
+ readonly field: string;
59
+ readonly message: string;
60
+ }
61
+ /** Whole-token match against {@link COUNTER_AGGREGATION_WORDS}, singular or
62
+ * plural, case-insensitive — 'sum' and 'Counts' look like counters,
63
+ * 'summary' does not. */
64
+ export declare function isCounterLookingAggregation(aggregation: string): boolean;
65
+ /** Compose the `not_covered` prose lines FROM coverage — the one derivation,
66
+ * used by the mint and by the drift check, so the two can never disagree. */
67
+ export declare function composeNotCovered(coverage: SemanticCoverage): readonly string[];
68
+ /**
69
+ * Judge one RENDERED envelope shape against the whole rule set. Empty = a
70
+ * well-formed envelope this library can honor. Non-empty = the faults, each
71
+ * naming its field.
72
+ *
73
+ * Called with values that carry the marker; on anything else it reports the
74
+ * missing marker rather than guessing.
75
+ */
76
+ export declare function semanticIssues(value: unknown): readonly SemanticIssue[];
77
+ /**
78
+ * Say "here is typed data, with the caveats that make it honest" in a shape
79
+ * the framework recognizes, the record keeps whole, and a build gate can
80
+ * refuse.
81
+ *
82
+ * Returns the value a tool's `execute` should return. The framework
83
+ * recognizes it at the dispatch boundary: the MODEL reads the compact
84
+ * projection ({@link semanticsForModel}), the FULL envelope rides the typed
85
+ * `agentfootprint.tools.semantics_declared` event, and a declared `coverage`
86
+ * flows through the same channel `coverage()` uses.
87
+ *
88
+ * Refuses (throws, at the call site — the `absent()` law) any declaration
89
+ * this vocabulary cannot honor: series without grain, data without
90
+ * provenance, a counter-looking aggregation with `is_counter` unstated, and
91
+ * every malformed shape — each refusal names the field and the fix.
92
+ *
93
+ * @example a per-port IOPS tool
94
+ * return semantic({
95
+ * series: rows.map((r) => ({ t: r.time, entity: r.port, metric: 'avg_iops', value: r.iops })),
96
+ * grain: { interval: '30m', aggregation: 'avg', is_counter: false },
97
+ * provenance: { measured_at: latestSampleTime, source: 'InfluxDB SwitchPortStats' },
98
+ * coverage: {
99
+ * checked: ['shq-fab-a: all 48 FC ports'],
100
+ * notChecked: [{ what: 'the peer fabric', why: 'this collector is scoped to one fabric' }],
101
+ * },
102
+ * render: { default: 'table', columns: ['entity', 'value'], sort: 'value desc' },
103
+ * });
104
+ */
105
+ export declare function semantic(decl: SemanticDeclaration): ToolSemantics;
106
+ /**
107
+ * Recognize (or decline to recognize) a value as a semantic envelope —
108
+ * STRICT, and the strictness is the zero-cost guarantee. Only a plain object
109
+ * whose `af_semantics` is exactly `true` AND that passes the whole rule set
110
+ * qualifies; every other value any tool has ever returned takes the path it
111
+ * always took, byte for byte.
112
+ *
113
+ * `undefined` means "not an envelope this library can honor" — a marker-
114
+ * bearing value with faults stays DATA (never half-applied); the dispatch
115
+ * loop dev-warns it and `check:semantics` names every fault.
116
+ */
117
+ export declare function readSemantics(value: unknown): ToolSemantics | undefined;
118
+ /**
119
+ * Name what is wrong with a value that CARRIES the marker but was not
120
+ * recognized. `undefined` for values without the marker (they are data, not
121
+ * near-misses) and for well-formed envelopes. Diagnosis only — never changes
122
+ * what any value does.
123
+ */
124
+ export declare function explainSemantics(value: unknown): readonly SemanticIssue[] | undefined;
125
+ /**
126
+ * The MODEL's view of one recognized envelope — compact and rendering-free.
127
+ *
128
+ * Keeps: the data (`series`/`facts`/`edges`), the caveats that must travel
129
+ * with it (`grain`, `provenance`), the composed `not_covered` prose, a
130
+ * non-null `clarify`, and the static note. Drops: the marker, `render`
131
+ * (UI hint), the three-list `coverage` detail (rides the coverage channel
132
+ * and the record), and a `clarify: null`. Shallow-copied so the history
133
+ * entry is not the object the tool still holds.
134
+ */
135
+ export declare function semanticsForModel(sem: ToolSemantics): Record<string, unknown>;
136
+ /** The envelope's coverage in the normalized three-list shape the coverage
137
+ * machinery reads — how `readCoverageResult` absorbs a semantic envelope's
138
+ * boundary into the one coverage channel. */
139
+ export declare function coverageOfSemantics(sem: ToolSemantics): Coverage;