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.
- package/AGENTS.md +21 -1
- package/CLAUDE.md +6 -2
- package/ai-instructions/claude-code/SKILL.md +18 -1
- package/bin/agentfootprint-check-semantics.mjs +14 -0
- package/dist/core/agent/coverage/read.js +13 -0
- package/dist/core/agent/coverage/read.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +141 -4
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/tools.js +25 -1
- package/dist/core/tools.js.map +1 -1
- package/dist/debug.js +14 -3
- package/dist/debug.js.map +1 -1
- package/dist/esm/core/agent/coverage/read.js +13 -0
- package/dist/esm/core/agent/coverage/read.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.js +141 -4
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/tools.d.ts +64 -0
- package/dist/esm/core/tools.js +23 -0
- package/dist/esm/core/tools.js.map +1 -1
- package/dist/esm/debug.d.ts +1 -0
- package/dist/esm/debug.js +7 -0
- package/dist/esm/debug.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +49 -0
- package/dist/esm/events/registry.d.ts +5 -1
- package/dist/esm/events/registry.js +4 -0
- package/dist/esm/events/registry.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +11 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/mcp/mcpServe.js +10 -0
- package/dist/esm/lib/mcp/mcpServe.js.map +1 -1
- package/dist/esm/lib/semantics/check.d.ts +70 -0
- package/dist/esm/lib/semantics/check.js +154 -0
- package/dist/esm/lib/semantics/check.js.map +1 -0
- package/dist/esm/lib/semantics/cli.d.ts +40 -0
- package/dist/esm/lib/semantics/cli.js +148 -0
- package/dist/esm/lib/semantics/cli.js.map +1 -0
- package/dist/esm/lib/semantics/envelope.d.ts +139 -0
- package/dist/esm/lib/semantics/envelope.js +592 -0
- package/dist/esm/lib/semantics/envelope.js.map +1 -0
- package/dist/esm/lib/semantics/format.d.ts +11 -0
- package/dist/esm/lib/semantics/format.js +33 -0
- package/dist/esm/lib/semantics/format.js.map +1 -0
- package/dist/esm/lib/semantics/index.d.ts +14 -0
- package/dist/esm/lib/semantics/index.js +15 -0
- package/dist/esm/lib/semantics/index.js.map +1 -0
- package/dist/esm/lib/semantics/types.d.ts +230 -0
- package/dist/esm/lib/semantics/types.js +71 -0
- package/dist/esm/lib/semantics/types.js.map +1 -0
- package/dist/esm/lib/trace-toolpack/traceToolpack.js +6 -0
- package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -1
- package/dist/events/registry.js +4 -0
- package/dist/events/registry.js.map +1 -1
- package/dist/index.js +65 -44
- package/dist/index.js.map +1 -1
- package/dist/lib/mcp/mcpServe.js +10 -0
- package/dist/lib/mcp/mcpServe.js.map +1 -1
- package/dist/lib/semantics/check.js +158 -0
- package/dist/lib/semantics/check.js.map +1 -0
- package/dist/lib/semantics/cli.js +176 -0
- package/dist/lib/semantics/cli.js.map +1 -0
- package/dist/lib/semantics/envelope.js +603 -0
- package/dist/lib/semantics/envelope.js.map +1 -0
- package/dist/lib/semantics/format.js +37 -0
- package/dist/lib/semantics/format.js.map +1 -0
- package/dist/lib/semantics/index.js +34 -0
- package/dist/lib/semantics/index.js.map +1 -0
- package/dist/lib/semantics/types.js +74 -0
- package/dist/lib/semantics/types.js.map +1 -0
- package/dist/lib/trace-toolpack/traceToolpack.js +6 -0
- package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -1
- package/dist/types/core/agent/coverage/read.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/tools.d.ts +64 -0
- package/dist/types/core/tools.d.ts.map +1 -1
- package/dist/types/debug.d.ts +1 -0
- package/dist/types/debug.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +49 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/events/registry.d.ts +5 -1
- package/dist/types/events/registry.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/mcp/mcpServe.d.ts.map +1 -1
- package/dist/types/lib/semantics/check.d.ts +71 -0
- package/dist/types/lib/semantics/check.d.ts.map +1 -0
- package/dist/types/lib/semantics/cli.d.ts +41 -0
- package/dist/types/lib/semantics/cli.d.ts.map +1 -0
- package/dist/types/lib/semantics/envelope.d.ts +140 -0
- package/dist/types/lib/semantics/envelope.d.ts.map +1 -0
- package/dist/types/lib/semantics/format.d.ts +12 -0
- package/dist/types/lib/semantics/format.d.ts.map +1 -0
- package/dist/types/lib/semantics/index.d.ts +15 -0
- package/dist/types/lib/semantics/index.d.ts.map +1 -0
- package/dist/types/lib/semantics/types.d.ts +231 -0
- package/dist/types/lib/semantics/types.d.ts.map +1 -0
- package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -1
- package/package.json +2 -1
package/AGENTS.md
CHANGED
|
@@ -217,6 +217,26 @@ const weather = defineTool({
|
|
|
217
217
|
|
|
218
218
|
Tool-name uniqueness is validated at `agent.build()` time across `.tool()` registrations AND every Skill's `inject.tools[]`.
|
|
219
219
|
|
|
220
|
+
A long-running tool can say where it is, mid-call, with `ctx.progress(payload)` —
|
|
221
|
+
each call files one `agentfootprint.stream.tool_progress` event between that
|
|
222
|
+
call's `tool_start` and `tool_end`, so a forty-second walk reads as "hop 3 of 12"
|
|
223
|
+
instead of silence:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
execute: async (args, ctx) => {
|
|
227
|
+
for (const [i, hop] of hops.entries()) {
|
|
228
|
+
await visit(hop);
|
|
229
|
+
ctx.progress({ done: i + 1, total: hops.length }); // telemetry, never the model's to read
|
|
230
|
+
}
|
|
231
|
+
return summarize(hops);
|
|
232
|
+
},
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The framework stamps `toolCallId` / `toolName` / `iteration`; `payload` is yours,
|
|
236
|
+
forwarded verbatim (it must survive `structuredClone`). Always present, never
|
|
237
|
+
throws, never blocks — with nothing listening it is a no-op, and a tool that
|
|
238
|
+
never calls it emits exactly what it emitted before.
|
|
239
|
+
|
|
220
240
|
### Context Engineering — 4 typed factories
|
|
221
241
|
|
|
222
242
|
All four return an `Injection` evaluated by the same engine; all emit the same `agentfootprint.context.injected` event with `source` discriminating the flavor.
|
|
@@ -504,7 +524,7 @@ files `agentfootprint.tools.coverage_declared`; with
|
|
|
504
524
|
to the final answer, which is how a limit survives a model that would rather not
|
|
505
525
|
mention it.
|
|
506
526
|
|
|
507
|
-
### Observability —
|
|
527
|
+
### Observability — 102 typed events across 22 domains
|
|
508
528
|
|
|
509
529
|
```typescript
|
|
510
530
|
agent.on('agentfootprint.context.injected', (e) =>
|
package/CLAUDE.md
CHANGED
|
@@ -17,6 +17,9 @@ not in this table, search `src/index.ts` for the nearest noun before writing cod
|
|
|
17
17
|
|
|
18
18
|
| If you are about to build… | It is | Where | Since |
|
|
19
19
|
|---|---|---|---|
|
|
20
|
+
| a tool returning numbers WITH the caveats that make them honest — interval/aggregation grain, is-it-a-counter, when the world was measured, which ground was NOT covered — as typed data the model reads compactly and the record keeps whole | `semantic()` + `tools.semantics_declared` (model sees `semanticsForModel` projection; `coverage` field absorbed by the coverage()/absent() channel) | `src/lib/semantics/` | 9.53.0 |
|
|
21
|
+
| a build gate that refuses a triage/inventory tool that forgot its caveats, by tool name and field name | `checkSemantics` + `defineTool({ resultClass })` + bin `agentfootprint-check-semantics` | `src/lib/semantics/check.ts` | 9.53.0 |
|
|
22
|
+
| report progress from inside a tool — "hop 3 of 12 done", said mid-call while a long-running `execute` is still working (the record was otherwise atomic: tool_start, silence, tool_end) | `ctx.progress` + `agentfootprint.stream.tool_progress` | `src/core/tools.ts` | 9.52.0 |
|
|
20
23
|
| declaring which skills connect — the SkillMap as one named thing (the agent that mounts it is the SkillWalker; there is deliberately no walker class) | `defineSkillMap` + `SkillMap` (permanent reference-equal aliases of `skillGraph`/`SkillGraph`) | `src/lib/injection-engine/skillGraph.ts` | 9.51.0 |
|
|
21
24
|
| guarding a skill transition on state or a tool result's fields — a route condition as DATA (comparable, drawable, evidence-recorded), not an opaque predicate | `guard:` + `compileGuard` + `guard-unsatisfiable` | `src/lib/injection-engine/skillGuard.ts` | 9.51.0 |
|
|
22
25
|
| seeing the skill map in a recording — the author's nodes + edges (guards included) as DATA, never parsed from prose or inferred from fired hops | `skill.graph_declared` + `buildSkillGraphDeclared` | `src/core/agent/skillGraphDeclared.ts` | 9.50.0 |
|
|
@@ -88,7 +91,7 @@ Traps: `src/observability/` holds the finder IMPLEMENTATIONS (canonical home; `d
|
|
|
88
91
|
- $break is the only clean-stop channel; structured fail context rides scope fields (`policyHalt*`, `reliabilityFail*`), decoded post-run by `Agent.finalizeResult` (Agent.ts:884) into typed errors.
|
|
89
92
|
|
|
90
93
|
## Extension points
|
|
91
|
-
- **Tool**: `defineTool` (core/tools.ts:155); shape `Tool = {schema, needs?, execute(args, ctx)}` (:23). Register `AgentBuilder.tool()` (AgentBuilder.ts:184); merged with auto `read_skill` in buildToolRegistry (:65; same-reference skill tools dedupe, any other name collision THROWS at build). Chart-as-tool: `flowchartAsTool` (core/flowchartAsTool.ts:203). MCP: `mcpClient(...).tools()`.
|
|
94
|
+
- **Tool**: `defineTool` (core/tools.ts:155); shape `Tool = {schema, needs?, execute(args, ctx)}` (:23). `ctx.progress(payload)` (9.52.0) is the tool's OWN emit door — a `Pick<ToolExecutionContext,'progress'>` closure (`toolProgress`, stages/toolCalls.ts, spread at BOTH dispatch sites beside `toolArtifacts`/`sessionContext`) that typedEmits `stream.tool_progress` mid-stage; framework stamps toolCallId/toolName/iteration, author owns `payload` only. ALWAYS present and never fatal (try/catch + dev warn) — the streamless doors (`mcpServe`, traceToolpack `OFFLINE_CONTEXT`) supply the no-op, which is the whole list of places a new required ctx field must also land. Register `AgentBuilder.tool()` (AgentBuilder.ts:184); merged with auto `read_skill` in buildToolRegistry (:65; same-reference skill tools dedupe, any other name collision THROWS at build). Chart-as-tool: `flowchartAsTool` (core/flowchartAsTool.ts:203). MCP: `mcpClient(...).tools()`.
|
|
92
95
|
- **ToolProvider** (per-iteration visibility): `list(ctx): Tool[]` (tool-providers/types.ts:121); max ONE per agent (AgentBuilder.ts:238 throws on second); combinators staticTools/gatedTools/skillScopedTools chain decorator-style.
|
|
93
96
|
- **LLM provider**: `LLMProvider = {name, complete, stream?}` (adapters/types.ts:230) passed as `AgentOptions.provider`. `provider.name` keys THREE auto-resolutions: cache strategy (cache/strategyRegistry.ts:40), thinking handler (thinking/registry.ts:43), Lens labels.
|
|
94
97
|
- **Recorder, 3 layers**: (1) raw footprintjs CombinedRecorder via `agent.attach()` (RunnerBase.ts:474 — NOT idempotent); (2) typed stream `agent.on(type|'*')`; (3) new built-in = factory taking `{dispatcher, getRunContext}`, registered in the attach block inside `Agent.run()` (Agent.ts:807-845), barreled in src/observe.ts.
|
|
@@ -126,7 +129,7 @@ Traps: `src/observability/` holds the finder IMPLEMENTATIONS (canonical home; `d
|
|
|
126
129
|
- **Embedder fingerprint** (8.9.0) → `Embedder.id` (optional; every shipped embedder sets one, and NONE include dims — the store appends `@<dims>` itself, so an id carrying its own size double-stamps) + `indexDocuments` defaulting `embedderId` to it + `SqliteVectorStore.reconcileFingerprint` (the only comparison site). Rule: dimensions ALWAYS decide, model ids decide only when BOTH sides named themselves — refusing on an absent name would block the majority of callers who never pass `embedderId`.
|
|
127
130
|
- **Retrieval record** (8.8.0) → FOUR stages write one object in sequence: `loadRelevant` (candidates+scores+threshold verdicts) → `pickByBudget` (re-marks admitted→over-budget/over-max-entries) → `formatDefault` (`promptFragment` + `promptPosition`) → the read mount's outputMapper lifts it to root as `retrievalEvidence_<id>`. `memoryRecallInjections` then splits ONE recall into one ActiveInjection PER CHUNK — guarded by a byte-equality check (`fragments.join('\n\n') === systemContent`) that falls back to the single injection rather than change the prompt. `rank` (score order) and `promptPosition` (picker order) are DIFFERENT and both load-bearing: joining fragments in rank order reproduces the right bytes in a sequence the model never saw.
|
|
128
131
|
- **AgentState** → all 8 stages/ files, both builders' mappers, memory-wire STRING-TYPED keys ('runIdentity'/'turnNumber'/… buildAgentChart.ts:177-180 — not refactor-safe), finalizeResult's `reliabilityFail*`/`policyHalt*` reads (rename silently kills the typed errors).
|
|
129
|
-
- **events/** →
|
|
132
|
+
- **events/** → 102 typed events across 22 domains (counts anti-drift-tested against this file — update BOTH when adding events): ALL_EVENT_TYPES exhaustiveness tests, DomainWildcard hand-list, ~42 importers (recorders, strategies, stream, commentary).
|
|
130
133
|
- **Run-configuration manifest (9.41.0)** → `agentfootprint.agent.run_configured`, the JOIN KEY that turns N runs into N labelled ARMS: one event naming the adapters/strategies in play (provider+model, reactMode, each memory's declared strategy/retrieval/embedder, window, graph posture+classifier, evidence posture, artifacts-present). Composed by the PURE `core/agent/runManifest.ts`, dispatched from `Agent.emitRunManifest()` at the END of `createExecutor` — the ONE funnel `run()` AND `resume()` share, both of which mint a fresh runId. Direct `dispatcher.dispatch` with a STATED pseudo-stage (`run-configured#0`), the `emitToolSessionReport` precedent — there is no stage yet, and `minimalMeta()` would make the one joinable-by-design event unjoinable. TWO laws, both tested: NAMES ONLY (a store is reported PRESENT and unnamed rather than identified by a directory/endpoint — `MemoryStore` and `ArtifactStore` declare no id), and ABSENT means "not configured", never a guessed `'default'`. Graph presence is read off `skillGraphNextSkill`, NOT `skillGraphCascade` (a 9.16-style mount sets no cascade and would read as "no graph"). `MemoryDefinition` gained `strategy`/`retrieval`/`embedderId` for it — declared names the compiled pipeline had closed over, the `store`-in-the-open precedent.
|
|
131
134
|
- **adapters/types.ts LLMMessage/LLMRequest** → 62 importers: tool_use round-trip (toolCalls.ts:115-135), wire assembly (callLLM.ts:150-160), providers, cache strategies, security/extractSequence, reliability loop.
|
|
132
135
|
- **Cache** → strategy registration is a MODULE SIDE EFFECT (src/index.ts:15-17); an entry point skipping that import silently falls back to NoOp. Resolved once per Agent at construction (Agent.ts:347).
|
|
@@ -143,6 +146,7 @@ Traps: `src/observability/` holds the finder IMPLEMENTATIONS (canonical home; `d
|
|
|
143
146
|
- **Skill visibility** (9.11.0) → the async half rides the tools slot's Discover stage (the ONE awaitable stage there) into a closure-shared `hiddenSkillIds`, read by the sync Compose stage — the `providerToolCache` pattern exactly. `readSkillFor` now takes `{currentSkillId?, hiddenSkillIds?}`; `ReadSkillOffer.grantable` became OPTIONAL (no graph ⇒ plain filtered catalog) and `hiddenIds` removes rows from BOTH sections. The ENUM stays the full catalog (8.5.0's law — narrowing it turns a policy refusal into a generic schema error that `toolArgValidation` raises before any gate). `skill:<id>` targets have ONE owner: `security/skillTarget.ts`.
|
|
144
147
|
- **Tool-result cap** (9.11.0) → `core/agent/toolResultCap.ts` + `ToolCallsHandlerDeps.maxToolResultChars` + the `capResults` closure applied at all FIVE `tool_end`-emitting dispatch paths in toolCalls.ts (execute, ask-resume, check-in decision, credential-consent resume, pauseHere answer). Both channels are measured separately — `result` (the event) and `modelResult` (history) — because a middleware may have already shrunk one. A new dispatch path MUST call `capResults` or it silently escapes the ceiling.
|
|
145
148
|
- **Refusing result ceiling** (9.20.0) → the PER-TOOL sibling of the cap with the OPPOSITE overflow answer: `Tool.resultCeiling {maxChars, narrowBy?}` (type + `assertResultCeiling` in core/tools.ts; measurement + refusal sentence in `core/agent/resultCeiling.ts`) REFUSES — "No data was returned" + how to narrow — never truncates (truncation reads as complete data ⇒ fabrication). Applied by the `refuseOverCeiling` closure at BOTH execute boundaries (batch loop + `resolveCredentialAndExecute`, covering all four resume paths; the pauseHere answer is a HUMAN's value, deliberately not measured), at the moment the handler's return lands — BEFORE gates/after-tool chain (read_skill-gate precedent). The payload enters NO channel; the record keeps `tools.result_refused {sizeChars, maxChars, narrowBy?, declaredStatus?}`; delivered status = `'invalid'` (routes via `onToolStatus`); an envelope's DECLARED effects are still judged (the effects channel didn't overflow) but its status is overridden; `ceilingRefused` blocks step-pointer advance on every path. Zero-cost: no `resultCeiling` ⇒ one undefined check, byte-identical.
|
|
149
|
+
- **Semantic tool results** (9.53.0) → ONE vocabulary leaf `lib/semantics/types.ts` (the toolOutcome precedent; type-only imports of coverage types — absorbed, never duplicated) + ONE rule set `semanticIssues` serving three doors: `semantic()` mint refusals, `readSemantics` strict recognition (any fault ⇒ data path byte-for-byte + dev warn — never half-applied), and `checkSemantics` findings (same codes). Dispatch wiring is the coverage/ceiling radius verbatim: `declareSemantics` closure at BOTH execute boundaries in toolCalls.ts, ordered declareCoverage → declareSemantics → refuseOverCeiling — the coverage funnel (`readCoverageResult`) grew a semantic arm so the envelope's `coverage` field flows the coverage() channel with zero boundary edits, the FULL envelope rides `tools.semantics_declared` (structuredClone-detached) BEFORE the ceiling (caveats survive an oversized refusal), and the ceiling measures the PROJECTION (`semanticsForModel`: drops marker/render/coverage-detail, composes `not_covered` FROM coverage) because that is what the model reads. `Tool.resultClass` ('triage'|'inventory', closed) validated at defineTool (`assertResultClass`, the assertResultCeiling law) and consumed ONLY by the gate. Gate = tool-lint humble-shell verbatim: core in lib/semantics/{check,format,cli}.ts, bin `agentfootprint-check-semantics.mjs`, exit 0/1/2, judges SAMPLE results (mock returns) — never executes tools. Deliberately UNCHANGED: no new scope key (the envelope is per-attempt ⇒ event channel; coverage rows reuse `coverageDeclared`), no status word, no gate flag, pause-answer paths not recognized (human values are not envelopes).
|
|
146
150
|
- **Conjunction matcher** (9.20.0) → `SkillMatch` gains `{all: [...]}` (AND of the SYNC arms only), all inside skillMatch.ts: `compileAllArm` (ONE compilation → AND predicate + flat `{kind:'all', parts}` data; intent member = teaching refusal naming the separate-rule alternative; nested `all` FLATTENED — associativity, the drop-stateful-flags normalize-to-truth precedent; empty refused), `compareWithAll` (shadows-ONLY via `partCoversProvably` part coverage — identical regex / keyword superset; plain-earlier-covers-a-part shadows a later all; NO overlap claims — a conjunction can be unsatisfiable and the specific-first layout is a design), `rawCaption` recursion (parts joined ` AND `, escaped once). Checkup/provenance/toMermaid see through it with zero changes of their own (parts are guaranteed leaves). NOTE `rawCaption` is now the EXPORTED `plainMatchCaption` (one grammar, two consumers: the mermaid label escapes it; skillExamples quotes it in prose).
|
|
147
151
|
- **Examples on start rules** (SG-G) → `examples?: readonly string[]` on `SkillEntryOptions` + BOTH `SkillStartRule` arms + `EntryDecl`; ALL the logic in skillExamples.ts (validate at the ONE funnel `builder.entry` — the config form translates through it): FOUR codes on `GraphProblemCode` proved by RUNNING the compiled predicates on a cold-start context, in declaration order. **TWO START LAWS, and the check-up may not pick one**: the declaration-order cold walk (unconditional entries claim) vs the turn-start cascade's tier-1 `firstRuleMatch` (CONDITIONAL entries only) — mounted by `.classify()` AND by `continuity: 'conversation'`, an AGENT-MOUNT option invisible at graph build. They differ in exactly one place (whether a default claims), so both are computed and an ERROR is asserted only where they AGREE: `example-shadowed-by-earlier` ERROR (earlier CONDITIONAL claimant, or both laws putting the turn elsewhere) vs `example-shadowed-by-default` WARNING (earlier UNCONDITIONAL claimant — both readings named; erroring here contradicted the live router, which routes that turn to the later rule under `continuity:'conversation'`). SEVERITY FOLLOWS PROVABILITY on `example-misses-own-rule`: ERROR for a data `match` (reads `userMessage` only ⇒ no-match holds under every context) or a THROW (turn 1 really hands it that context); WARNING for an opaque `when` (may be gated on `ctx.history.length > 0` and claim on a later turn — history survives a run, the cursor does not). Every message NAMES the judged context (iteration 1 / phrase / empty history / no cursor) and `describeCondition` has an UNCONDITIONAL arm — printing "its `when` predicate" for an entry that declares none was a message describing a graph nobody wrote. `example-unclaimed` WARNING. Ordering checks gated on the SAME `!exclusiveEntries || hasClassifier` premise as the pairwise rule checks; self-match is order-independent and always runs. `GraphCheckup` gains `notes?` (the boundary sentence — rendered by formatCheckup as `[note]`, carried through the deferred-body-contract filter) and `GraphProblem` gains `example?`. TIER LAW: tier-2 `match:{intent,examples}` examples are SCORING material (run time); rule-level `examples` are TEST material (build time only) — both lists on one rule is a teaching refusal, as are an empty/blank list and examples on an UNCONDITIONAL entry (it claims everything, so they would prove nothing). Zero-cost when unused: one `Array.some`, no notes key, byte-identical skills/edges/events.
|
|
148
152
|
|
|
@@ -193,6 +193,23 @@ const calculator = defineTool({
|
|
|
193
193
|
});
|
|
194
194
|
```
|
|
195
195
|
|
|
196
|
+
### A long tool that says where it is
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
execute: async (args, ctx) => {
|
|
200
|
+
for (const [i, hop] of hops.entries()) {
|
|
201
|
+
await visit(hop);
|
|
202
|
+
ctx.progress({ done: i + 1, total: hops.length }); // → stream.tool_progress
|
|
203
|
+
}
|
|
204
|
+
return summarize(hops);
|
|
205
|
+
},
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`ctx.progress` is always present, never throws, never blocks, and never reaches
|
|
209
|
+
the model — the framework stamps `toolCallId` / `toolName` / `iteration`, you own
|
|
210
|
+
`payload`. `agent.on('agentfootprint.stream.*')` and `toSSE(agent)` carry it with
|
|
211
|
+
no extra wiring.
|
|
212
|
+
|
|
196
213
|
### When a tool finds nothing, and what a clean result does not cover
|
|
197
214
|
|
|
198
215
|
```typescript
|
|
@@ -235,7 +252,7 @@ const agent = Agent.create({ provider, model })
|
|
|
235
252
|
agent.on('agentfootprint.context.evaluated', (e) => console.log(e.payload.activeIds));
|
|
236
253
|
```
|
|
237
254
|
|
|
238
|
-
**
|
|
255
|
+
**102 typed events across 22 domains.** Two subscription shapes and no third:
|
|
239
256
|
`'*'` (every event) and `'agentfootprint.<domain>.*'` (one domain). **`'agentfootprint.*'`
|
|
240
257
|
is not a pattern** — TypeScript rejects it, and at runtime it would match nothing.
|
|
241
258
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* agentfootprint-check-semantics — CI gate for semantic tool results
|
|
4
|
+
* (9.53.0). Humble shell: all behavior lives in src/lib/semantics/cli.ts
|
|
5
|
+
* (unit-tested there); this wrapper only resolves the built module and maps
|
|
6
|
+
* the returned code onto process.exitCode.
|
|
7
|
+
*
|
|
8
|
+
* npx agentfootprint-check-semantics semantics-catalog.json
|
|
9
|
+
* npx agentfootprint-check-semantics semantics-catalog.json --strict
|
|
10
|
+
*
|
|
11
|
+
* Guide: docs-next/content/docs/build/semantic-results.mdx
|
|
12
|
+
*/
|
|
13
|
+
const { runCheckSemanticsCli } = await import('../dist/esm/lib/semantics/cli.js');
|
|
14
|
+
process.exitCode = await runCheckSemanticsCli(process.argv.slice(2));
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.readCoverageResult = void 0;
|
|
20
|
+
const envelope_js_1 = require("../../../lib/semantics/envelope.js");
|
|
20
21
|
const absent_js_1 = require("./absent.js");
|
|
21
22
|
const ledger_js_1 = require("./ledger.js");
|
|
22
23
|
const ABSENT_STATUS = 'absent';
|
|
@@ -38,6 +39,18 @@ function readCoverageResult(value) {
|
|
|
38
39
|
],
|
|
39
40
|
};
|
|
40
41
|
}
|
|
42
|
+
// A semantic envelope's `coverage` field (9.53.0) is ABSORBED here — the
|
|
43
|
+
// one recognizer funnel — so the boundary a semantic tool declared flows
|
|
44
|
+
// through the exact channel `coverage()` uses (the `tools.coverage_declared`
|
|
45
|
+
// event, tracked state, the final-answer limits block) with zero extra
|
|
46
|
+
// wiring at any dispatch door. A semantic envelope without `coverage`
|
|
47
|
+
// declares no boundary, exactly like a bare result.
|
|
48
|
+
const sem = (0, envelope_js_1.readSemantics)(value);
|
|
49
|
+
if (sem !== undefined) {
|
|
50
|
+
if (sem.coverage === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
return { declared: [{ kind: 'ledger', coverage: (0, envelope_js_1.coverageOfSemantics)(sem) }] };
|
|
53
|
+
}
|
|
41
54
|
const covered = (0, ledger_js_1.readCoverageLedger)(value);
|
|
42
55
|
if (covered === undefined)
|
|
43
56
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../../../src/core/agent/coverage/read.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAGH,2CAA6D;AAC7D,2CAAmE;AA6BnE,MAAM,aAAa,GAAqB,QAAQ,CAAC;AAEjD;;;;;;;GAOG;AACH,SAAgB,kBAAkB,CAAC,KAAc;IAC/C,MAAM,OAAO,GAAG,IAAA,uBAAW,EAAC,KAAK,CAAC,CAAC;IACnC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO;YACL,MAAM,EAAE,aAAa;YACrB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAA,6BAAiB,EAAC,OAAO,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,EAAE;aACzF;SACF,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,IAAA,8BAAkB,EAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC5C,MAAM,QAAQ,GAAoB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAA,4BAAgB,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5F,MAAM,KAAK,GAAG,IAAA,uBAAW,EAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC7C,QAAQ,CAAC,IAAI,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,IAAA,6BAAiB,EAAC,KAAK,CAAC;QAClC,SAAS,EAAE,KAAK,CAAC,UAAU;KAC5B,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAC7C,CAAC;
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../../../src/core/agent/coverage/read.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAGH,oEAAwF;AACxF,2CAA6D;AAC7D,2CAAmE;AA6BnE,MAAM,aAAa,GAAqB,QAAQ,CAAC;AAEjD;;;;;;;GAOG;AACH,SAAgB,kBAAkB,CAAC,KAAc;IAC/C,MAAM,OAAO,GAAG,IAAA,uBAAW,EAAC,KAAK,CAAC,CAAC;IACnC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO;YACL,MAAM,EAAE,aAAa;YACrB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAA,6BAAiB,EAAC,OAAO,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,EAAE;aACzF;SACF,CAAC;IACJ,CAAC;IACD,yEAAyE;IACzE,yEAAyE;IACzE,6EAA6E;IAC7E,uEAAuE;IACvE,sEAAsE;IACtE,oDAAoD;IACpD,MAAM,GAAG,GAAG,IAAA,2BAAa,EAAC,KAAK,CAAC,CAAC;IACjC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACjD,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAA,iCAAmB,EAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;IAChF,CAAC;IACD,MAAM,OAAO,GAAG,IAAA,8BAAkB,EAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC5C,MAAM,QAAQ,GAAoB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAA,4BAAgB,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5F,MAAM,KAAK,GAAG,IAAA,uBAAW,EAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAC7C,QAAQ,CAAC,IAAI,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,IAAA,6BAAiB,EAAC,KAAK,CAAC;QAClC,SAAS,EAAE,KAAK,CAAC,UAAU;KAC5B,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAC7C,CAAC;AAhCD,gDAgCC"}
|
|
@@ -69,6 +69,7 @@ const validators_js_1 = require("../validators.js");
|
|
|
69
69
|
const toolResultCap_js_1 = require("../toolResultCap.js");
|
|
70
70
|
const resultCeiling_js_1 = require("../resultCeiling.js");
|
|
71
71
|
const skillSteps_js_1 = require("../../../lib/injection-engine/skillSteps.js");
|
|
72
|
+
const envelope_js_1 = require("../../../lib/semantics/envelope.js");
|
|
72
73
|
const index_js_1 = require("../coverage/index.js");
|
|
73
74
|
const toolEffects_js_1 = require("../toolEffects.js");
|
|
74
75
|
/** Declaration order preserved, ids de-duplicated — the shape the gate's re-prompt
|
|
@@ -381,6 +382,66 @@ function buildToolCallsHandler(deps) {
|
|
|
381
382
|
scope.coverageDeclared = [...(scope.coverageDeclared ?? []), ...rows];
|
|
382
383
|
return reading.status;
|
|
383
384
|
};
|
|
385
|
+
/**
|
|
386
|
+
* The semantic envelope (9.53.0) — recognized at the SAME execute
|
|
387
|
+
* boundaries as coverage and the ceiling (one implementation, every door),
|
|
388
|
+
* AFTER `declareCoverage` (which absorbs the envelope's `coverage` field
|
|
389
|
+
* through the one coverage funnel) and BEFORE `refuseOverCeiling` (so the
|
|
390
|
+
* FULL envelope — grain, provenance, render, coverage — is on the record
|
|
391
|
+
* even when the content is refused as oversized: a caveat that died with
|
|
392
|
+
* an oversized result would be the silence the envelope exists to break).
|
|
393
|
+
*
|
|
394
|
+
* Returns the MODEL's view — the compact rendering-free projection
|
|
395
|
+
* (`semanticsForModel`: data + grain + provenance + composed `not_covered`
|
|
396
|
+
* + non-null clarify + the static note; never the marker, `render`, or the
|
|
397
|
+
* three-list coverage detail) — or `undefined` for every non-envelope
|
|
398
|
+
* value, which keeps today's path byte for byte. The full envelope rides
|
|
399
|
+
* the typed `tools.semantics_declared` event, so recordings and UIs get
|
|
400
|
+
* everything the model was spared.
|
|
401
|
+
*
|
|
402
|
+
* A value CARRYING the marker that this library cannot honor stays DATA
|
|
403
|
+
* (never half-applied — the recognition strictness law) and is dev-warned
|
|
404
|
+
* here naming its first fault; `check:semantics` names them all at build
|
|
405
|
+
* time.
|
|
406
|
+
*/
|
|
407
|
+
const declareSemantics = (scope, call, value) => {
|
|
408
|
+
const sem = (0, envelope_js_1.readSemantics)(value);
|
|
409
|
+
if (sem === undefined) {
|
|
410
|
+
const faults = (0, envelope_js_1.explainSemantics)(value);
|
|
411
|
+
if (faults !== undefined && faults.length > 0 && (0, footprintjs_1.isDevMode)()) {
|
|
412
|
+
// eslint-disable-next-line no-console
|
|
413
|
+
console.warn(`agentfootprint semantics: tool '${call.toolName}' returned a value carrying ` +
|
|
414
|
+
`af_semantics that was NOT recognized (treated as plain data): ` +
|
|
415
|
+
`${faults[0].message} (field: ${faults[0].field})` +
|
|
416
|
+
(faults.length > 1 ? ` — and ${faults.length - 1} more; run check:semantics.` : ''));
|
|
417
|
+
}
|
|
418
|
+
return undefined;
|
|
419
|
+
}
|
|
420
|
+
// Detached plain data for the record — the event must not hold a live
|
|
421
|
+
// reference into a value the tool still owns. An envelope that cannot
|
|
422
|
+
// survive structuredClone cannot ride the record (or a checkpoint), so
|
|
423
|
+
// it is declined here — data path, dev-warned — rather than half-filed.
|
|
424
|
+
let detached;
|
|
425
|
+
try {
|
|
426
|
+
detached = structuredClone(sem);
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
if ((0, footprintjs_1.isDevMode)()) {
|
|
430
|
+
// eslint-disable-next-line no-console
|
|
431
|
+
console.warn(`agentfootprint semantics: tool '${call.toolName}' returned a semantic envelope ` +
|
|
432
|
+
`that does not survive structuredClone (a function or live handle in a fact row?) ` +
|
|
433
|
+
`— treated as plain data. Envelope values must be plain data.`);
|
|
434
|
+
}
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.tools.semantics_declared', {
|
|
438
|
+
toolName: call.toolName,
|
|
439
|
+
toolCallId: call.toolCallId,
|
|
440
|
+
iteration: call.iteration,
|
|
441
|
+
semantics: detached,
|
|
442
|
+
});
|
|
443
|
+
return (0, envelope_js_1.semanticsForModel)(sem);
|
|
444
|
+
};
|
|
384
445
|
// ── Step-procedure boundary (9.18.0) — ONE implementation, five sites ──
|
|
385
446
|
// The batch loop and the four resume paths all finalize results; each
|
|
386
447
|
// calls these where its result becomes final, so a pointer can never miss
|
|
@@ -733,6 +794,50 @@ function buildToolCallsHandler(deps) {
|
|
|
733
794
|
},
|
|
734
795
|
};
|
|
735
796
|
};
|
|
797
|
+
/**
|
|
798
|
+
* `ctx.progress` for one dispatch (9.52.0) — the tool's mid-call report.
|
|
799
|
+
*
|
|
800
|
+
* The whole feature is this closure. A tool call is atomic on the record
|
|
801
|
+
* (`tool_start`, then silence for as long as the handler runs, then
|
|
802
|
+
* `tool_end`), and a twelve-hop walk spends forty seconds there with nothing
|
|
803
|
+
* to show. `progress` breaks the silence over the channel already built for
|
|
804
|
+
* it: `typedEmit` mid-stage, so each report carries the real
|
|
805
|
+
* `runtimeStageId`, rides the `agentfootprint.stream.` prefix the EmitBridge
|
|
806
|
+
* already forwards, and lands in recordings with the rest of the run.
|
|
807
|
+
*
|
|
808
|
+
* The three identity facts are stamped HERE, from the dispatch this closure
|
|
809
|
+
* was built for — the tool sends only its own payload. A report that could
|
|
810
|
+
* name its own `toolCallId` could name somebody else's, and a correlation id
|
|
811
|
+
* a consumer cannot trust is worse than none.
|
|
812
|
+
*
|
|
813
|
+
* NEVER fatal: an emit that throws (a recorder that dies at the wrong moment,
|
|
814
|
+
* a scope past its stage) must not fail a tool call that is otherwise
|
|
815
|
+
* succeeding — telemetry is not the work. Named in dev mode rather than
|
|
816
|
+
* swallowed, and once per tool: a twelve-hop walk whose channel is broken
|
|
817
|
+
* would otherwise print twelve identical warnings per call.
|
|
818
|
+
*/
|
|
819
|
+
const progressWarned = new Set();
|
|
820
|
+
const toolProgress = (scope, call) => ({
|
|
821
|
+
progress: (payload) => {
|
|
822
|
+
try {
|
|
823
|
+
(0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.stream.tool_progress', {
|
|
824
|
+
toolCallId: call.toolCallId,
|
|
825
|
+
toolName: call.toolName,
|
|
826
|
+
iteration: call.iteration,
|
|
827
|
+
payload,
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
catch (err) {
|
|
831
|
+
if ((0, footprintjs_1.isDevMode)() && !progressWarned.has(call.toolName)) {
|
|
832
|
+
progressWarned.add(call.toolName);
|
|
833
|
+
// eslint-disable-next-line no-console
|
|
834
|
+
console.warn(`[agentfootprint] tool '${call.toolName}': ctx.progress could not file a report ` +
|
|
835
|
+
`(${err instanceof Error ? err.message : String(err)}). The call itself is ` +
|
|
836
|
+
'unaffected — progress is telemetry, and a dropped report never fails a tool.');
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
},
|
|
840
|
+
});
|
|
736
841
|
/**
|
|
737
842
|
* `ctx.artifacts` + `ctx.hasArtifacts` for one dispatch — the claim-check
|
|
738
843
|
* capability, shaped exactly like `ctx.credentials` (9.21.0).
|
|
@@ -1240,7 +1345,9 @@ function buildToolCallsHandler(deps) {
|
|
|
1240
1345
|
}
|
|
1241
1346
|
}
|
|
1242
1347
|
try {
|
|
1243
|
-
const
|
|
1348
|
+
// `let`, not `const`: the semantic projection below replaces the value
|
|
1349
|
+
// on the non-envelope path exactly as the batch loop does.
|
|
1350
|
+
let result = await tool.execute(args, {
|
|
1244
1351
|
toolCallId,
|
|
1245
1352
|
iteration,
|
|
1246
1353
|
...(env.signal && { signal: env.signal }),
|
|
@@ -1249,6 +1356,7 @@ function buildToolCallsHandler(deps) {
|
|
|
1249
1356
|
...(resolvedCredential && { credential: resolvedCredential }),
|
|
1250
1357
|
...toolArtifacts(scope, toolName, toolCallId),
|
|
1251
1358
|
...(wantedMeta !== undefined && { wanted: wantedMeta }),
|
|
1359
|
+
...toolProgress(scope, { toolName, toolCallId, iteration }),
|
|
1252
1360
|
...sessionContext(scope, toolName, toolCallId),
|
|
1253
1361
|
});
|
|
1254
1362
|
await endCall(toolCallId);
|
|
@@ -1265,6 +1373,12 @@ function buildToolCallsHandler(deps) {
|
|
|
1265
1373
|
// and wins over the envelope's only when the envelope declared none.
|
|
1266
1374
|
const coverageStatus = declareCoverage(scope, { toolName, toolCallId, iteration }, envelope.content);
|
|
1267
1375
|
const declaredStatus = envelope.status ?? coverageStatus;
|
|
1376
|
+
// The semantic envelope (9.53.0), on the unwrapped content — the
|
|
1377
|
+
// effects+semantics composition point: full envelope to the record,
|
|
1378
|
+
// projection to the model. BEFORE the ceiling, which must measure
|
|
1379
|
+
// what the model will actually read.
|
|
1380
|
+
const semanticView = declareSemantics(scope, { toolName, toolCallId, iteration }, envelope.content);
|
|
1381
|
+
const content = semanticView ?? envelope.content;
|
|
1268
1382
|
// The tool's own ceiling (9.20.0) measures the CONTENT — the channel
|
|
1269
1383
|
// that can overflow a context window. The DECLARED effects are small
|
|
1270
1384
|
// validated data and are still judged by the caller: a tool that
|
|
@@ -1272,7 +1386,7 @@ function buildToolCallsHandler(deps) {
|
|
|
1272
1386
|
// transition. The delivered status becomes 'invalid' (the declared
|
|
1273
1387
|
// status described a result the model never received — the
|
|
1274
1388
|
// `result_refused` event keeps what was declared).
|
|
1275
|
-
const refusal = refuseOverCeiling(scope, { toolName, toolCallId, iteration }, tool,
|
|
1389
|
+
const refusal = refuseOverCeiling(scope, { toolName, toolCallId, iteration }, tool, content, declaredStatus);
|
|
1276
1390
|
if (refusal !== undefined) {
|
|
1277
1391
|
return {
|
|
1278
1392
|
result: refusal,
|
|
@@ -1282,12 +1396,23 @@ function buildToolCallsHandler(deps) {
|
|
|
1282
1396
|
};
|
|
1283
1397
|
}
|
|
1284
1398
|
return {
|
|
1285
|
-
result:
|
|
1399
|
+
result: content,
|
|
1286
1400
|
executed: true,
|
|
1287
|
-
envelope: declaredStatus === envelope.status
|
|
1401
|
+
envelope: declaredStatus === envelope.status && semanticView === undefined
|
|
1402
|
+
? envelope
|
|
1403
|
+
: {
|
|
1404
|
+
...envelope,
|
|
1405
|
+
content,
|
|
1406
|
+
...(declaredStatus !== undefined && { status: declaredStatus }),
|
|
1407
|
+
},
|
|
1288
1408
|
};
|
|
1289
1409
|
}
|
|
1290
1410
|
const coverageStatus = declareCoverage(scope, { toolName, toolCallId, iteration }, result);
|
|
1411
|
+
// The semantic envelope (9.53.0) on this path's own execute boundary —
|
|
1412
|
+
// a resumed call's envelope is judged exactly as an inline one's.
|
|
1413
|
+
const semanticView = declareSemantics(scope, { toolName, toolCallId, iteration }, result);
|
|
1414
|
+
if (semanticView !== undefined)
|
|
1415
|
+
result = semanticView;
|
|
1291
1416
|
const refusal = refuseOverCeiling(scope, { toolName, toolCallId, iteration }, tool, result, coverageStatus);
|
|
1292
1417
|
if (refusal !== undefined) {
|
|
1293
1418
|
// Synthesized empty-effects envelope: the resume paths read the
|
|
@@ -1955,6 +2080,7 @@ function buildToolCallsHandler(deps) {
|
|
|
1955
2080
|
...(resolvedCredential && { credential: resolvedCredential }),
|
|
1956
2081
|
...toolArtifacts(scope, tc.name, tc.id),
|
|
1957
2082
|
...(wantedMeta !== undefined && { wanted: wantedMeta }),
|
|
2083
|
+
...toolProgress(scope, { toolName: tc.name, toolCallId: tc.id, iteration }),
|
|
1958
2084
|
...sessionContext(scope, tc.name, tc.id),
|
|
1959
2085
|
});
|
|
1960
2086
|
// A code-runner tool leaves the SHAPE of what it just ran, keyed by
|
|
@@ -2000,6 +2126,17 @@ function buildToolCallsHandler(deps) {
|
|
|
2000
2126
|
const coverageStatus = declareCoverage(scope, { toolName: tc.name, toolCallId: tc.id, iteration }, result);
|
|
2001
2127
|
if (toolStatus === undefined)
|
|
2002
2128
|
toolStatus = coverageStatus;
|
|
2129
|
+
// The semantic envelope (9.53.0), on the unwrapped content and
|
|
2130
|
+
// AFTER coverage (whose funnel absorbs the envelope's own
|
|
2131
|
+
// `coverage` field), BEFORE the ceiling (which must measure
|
|
2132
|
+
// what the model will actually read, and must not silently
|
|
2133
|
+
// delete grain/provenance — they are already on the record by
|
|
2134
|
+
// the time it fires). Everything downstream — governance, the
|
|
2135
|
+
// cap, history, `tool_end` — sees the compact projection; the
|
|
2136
|
+
// full envelope rides `tools.semantics_declared`.
|
|
2137
|
+
const semanticView = declareSemantics(scope, { toolName: tc.name, toolCallId: tc.id, iteration }, result);
|
|
2138
|
+
if (semanticView !== undefined)
|
|
2139
|
+
result = semanticView;
|
|
2003
2140
|
// The tool's own refusing ceiling (9.20.0) — at the execute
|
|
2004
2141
|
// boundary, BEFORE the gates and the after-tool chain, so
|
|
2005
2142
|
// governance and the agent-level cap compose over what the
|