agentfootprint 9.80.0 → 9.82.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 (71) hide show
  1. package/AGENTS.md +20 -0
  2. package/CHANGELOG.md +167 -0
  3. package/CLAUDE.md +3 -1
  4. package/dist/core/runbook/verdicts.js +30 -6
  5. package/dist/core/runbook/verdicts.js.map +1 -1
  6. package/dist/esm/core/runbook/types.d.ts +8 -0
  7. package/dist/esm/core/runbook/verdicts.d.ts +18 -5
  8. package/dist/esm/core/runbook/verdicts.js +30 -6
  9. package/dist/esm/core/runbook/verdicts.js.map +1 -1
  10. package/dist/esm/lib/mcp/connectionRefusals.d.ts +30 -0
  11. package/dist/esm/lib/mcp/connectionRefusals.js +110 -0
  12. package/dist/esm/lib/mcp/connectionRefusals.js.map +1 -0
  13. package/dist/esm/lib/mcp/index.d.ts +3 -2
  14. package/dist/esm/lib/mcp/index.js +4 -0
  15. package/dist/esm/lib/mcp/index.js.map +1 -1
  16. package/dist/esm/lib/mcp/mcpClient.d.ts +18 -5
  17. package/dist/esm/lib/mcp/mcpClient.js +102 -38
  18. package/dist/esm/lib/mcp/mcpClient.js.map +1 -1
  19. package/dist/esm/lib/mcp/mcpServe.js +45 -13
  20. package/dist/esm/lib/mcp/mcpServe.js.map +1 -1
  21. package/dist/esm/lib/mcp/sdkLoadFailure.d.ts +60 -0
  22. package/dist/esm/lib/mcp/sdkLoadFailure.js +74 -0
  23. package/dist/esm/lib/mcp/sdkLoadFailure.js.map +1 -0
  24. package/dist/esm/lib/mcp/throttleRetry.d.ts +20 -1
  25. package/dist/esm/lib/mcp/throttleRetry.js +20 -1
  26. package/dist/esm/lib/mcp/throttleRetry.js.map +1 -1
  27. package/dist/esm/lib/mcp/transportUrl.d.ts +30 -0
  28. package/dist/esm/lib/mcp/transportUrl.js +71 -0
  29. package/dist/esm/lib/mcp/transportUrl.js.map +1 -0
  30. package/dist/esm/lib/mcp/types.d.ts +143 -13
  31. package/dist/esm/tool-providers/index.d.ts +2 -2
  32. package/dist/esm/tool-providers/index.js +6 -1
  33. package/dist/esm/tool-providers/index.js.map +1 -1
  34. package/dist/lib/mcp/connectionRefusals.js +114 -0
  35. package/dist/lib/mcp/connectionRefusals.js.map +1 -0
  36. package/dist/lib/mcp/index.js +6 -1
  37. package/dist/lib/mcp/index.js.map +1 -1
  38. package/dist/lib/mcp/mcpClient.js +102 -38
  39. package/dist/lib/mcp/mcpClient.js.map +1 -1
  40. package/dist/lib/mcp/mcpServe.js +45 -13
  41. package/dist/lib/mcp/mcpServe.js.map +1 -1
  42. package/dist/lib/mcp/sdkLoadFailure.js +78 -0
  43. package/dist/lib/mcp/sdkLoadFailure.js.map +1 -0
  44. package/dist/lib/mcp/throttleRetry.js +20 -1
  45. package/dist/lib/mcp/throttleRetry.js.map +1 -1
  46. package/dist/lib/mcp/transportUrl.js +75 -0
  47. package/dist/lib/mcp/transportUrl.js.map +1 -0
  48. package/dist/tool-providers/index.js +6 -1
  49. package/dist/tool-providers/index.js.map +1 -1
  50. package/dist/types/core/runbook/types.d.ts +8 -0
  51. package/dist/types/core/runbook/types.d.ts.map +1 -1
  52. package/dist/types/core/runbook/verdicts.d.ts +18 -5
  53. package/dist/types/core/runbook/verdicts.d.ts.map +1 -1
  54. package/dist/types/lib/mcp/connectionRefusals.d.ts +31 -0
  55. package/dist/types/lib/mcp/connectionRefusals.d.ts.map +1 -0
  56. package/dist/types/lib/mcp/index.d.ts +3 -2
  57. package/dist/types/lib/mcp/index.d.ts.map +1 -1
  58. package/dist/types/lib/mcp/mcpClient.d.ts +18 -5
  59. package/dist/types/lib/mcp/mcpClient.d.ts.map +1 -1
  60. package/dist/types/lib/mcp/mcpServe.d.ts.map +1 -1
  61. package/dist/types/lib/mcp/sdkLoadFailure.d.ts +61 -0
  62. package/dist/types/lib/mcp/sdkLoadFailure.d.ts.map +1 -0
  63. package/dist/types/lib/mcp/throttleRetry.d.ts +20 -1
  64. package/dist/types/lib/mcp/throttleRetry.d.ts.map +1 -1
  65. package/dist/types/lib/mcp/transportUrl.d.ts +31 -0
  66. package/dist/types/lib/mcp/transportUrl.d.ts.map +1 -0
  67. package/dist/types/lib/mcp/types.d.ts +143 -13
  68. package/dist/types/lib/mcp/types.d.ts.map +1 -1
  69. package/dist/types/tool-providers/index.d.ts +2 -2
  70. package/dist/types/tool-providers/index.d.ts.map +1 -1
  71. package/package.json +3 -3
package/AGENTS.md CHANGED
@@ -112,6 +112,26 @@ Transports: `stdio` (local subprocess), `http` (Streamable HTTP). The
112
112
  `@modelcontextprotocol/sdk` peer-dep is lazy-required — zero runtime
113
113
  cost when MCP isn't used. Friendly install hint if missing.
114
114
 
115
+ **In a BROWSER** the Node `require` loader does not exist, so supply what it
116
+ would have found. Give the two SDK modules as `sdk` (the library still builds
117
+ the transport, so headers / your own `fetch` / gateway vending / throttle retry
118
+ all keep working), or a client you connected yourself as `connection`:
119
+
120
+ ```typescript
121
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
122
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
123
+
124
+ const sidecar = await mcpClient({
125
+ name: 'sidecar',
126
+ sdk: { Client, StreamableHTTPClientTransport },
127
+ transport: { transport: 'http', url: '/py/mcp' }, // relative resolves against the page
128
+ });
129
+ ```
130
+
131
+ `stdio` spawns a subprocess and is refused in words there. On the `connection`
132
+ arm the library builds no transport, so `retryOnThrottle` / `clientInfo` are
133
+ REFUSED rather than ignored — wrap your own fetch with `retryingFetch` instead.
134
+
115
135
  `agent.tools(arr)` is the bulk-register companion to `agent.tool(t)`.
116
136
  Pair with `await client.tools()` to register everything an MCP server
117
137
  exposes in one builder call. Tool-name uniqueness is still validated
package/CHANGELOG.md CHANGED
@@ -7,6 +7,173 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.82.0] - 2026-08-30
11
+
12
+ ### Added
13
+
14
+ - **A runbook can finally name the verdict no rule chose.** `verdict_meanings`
15
+ is generated from what the run itself said: the branch descriptions the chart
16
+ declared, and the rule labels this run's `decide()` evidence carried. For one
17
+ branch, both sources are silent by construction — the DEFAULT. It is the
18
+ branch chosen by *no rule* (it fires exactly when every rule failed, so no
19
+ `label` describes it), and when the decider lives inside a dynamically
20
+ generated fan-out branch the branch chart does not exist at build time either,
21
+ so there is no declared description to fall back on.
22
+
23
+ The library shipped visible proof of the gap: this repo's own worked example
24
+ and the published docs page showed a `"verdict": "protected"` row beside a
25
+ `verdict_meanings` map with no `protected` key.
26
+
27
+ The meaning is now declared where the rules are declared — one line at the
28
+ `decide()` call, on `footprintjs` ≥ 9.16.0:
29
+
30
+ ```ts
31
+ // before
32
+ decide(scope, POSTURE_RULES, 'protected');
33
+ // after
34
+ decide(scope, POSTURE_RULES, {
35
+ branch: 'protected',
36
+ label: 'no rule fired — last backup within the 7-day threshold',
37
+ });
38
+ ```
39
+
40
+ It rides `DecisionEvidence.defaultLabel` and is harvested exactly like a rule
41
+ label — on every decision, including runs where a rule won, so a published
42
+ meanings map does not gain and lose a key with the day's data.
43
+
44
+ What deliberately did NOT change: there is **no caller-supplied meanings map**
45
+ at the tool boundary. A map a caller can hand in is a map that can describe
46
+ rules that never ran, and it would be indistinguishable in the answer from
47
+ meanings the run produced. Declare nothing and `verdict_meanings` stays
48
+ honestly silent about that branch — the bridge never invents a sentence from a
49
+ branch id. A blank label (`''`) is recorded as no meaning at all, for a rule or
50
+ for the default.
51
+
52
+ Example: `examples/features/68-runbook-as-tool.ts` — its `verdict_meanings`
53
+ now explains every verdict its own rowset shows.
54
+
55
+ ### Changed
56
+
57
+ - **`footprintjs` peer dependency: `^9.15.0` → `^9.16.1`** — 9.16.0 carries
58
+ `DefaultBranch` / `DecisionEvidence.defaultLabel`; 9.16.1 is the floor because
59
+ 9.16.0 threw on a `decide()` call that omits its default (this repo's own
60
+ suite caught it).
61
+ - **The engine version stamp on a recording envelope is real again.** footprintjs
62
+ 9.15.1 added `'./package.json'` to its `exports` map, so `engineVersion()` can
63
+ resolve the manifest it always tried to read: `producer.footprintjsVersion` and
64
+ a bug report's `environment.footprintjs` now carry the installed version
65
+ instead of the honest-but-useless `'unknown'`. The test that pinned the defect
66
+ (and asked to be tripped when it was fixed) now pins the version instead.
67
+
68
+ ## [9.81.0] - 2026-08-30
69
+
70
+ ### Added
71
+
72
+ - **MCP from a browser — because the barrier was never the protocol, it was one
73
+ line of ours.** `mcpClient` loads `@modelcontextprotocol/sdk` through a Node
74
+ `require` loader, and that loader does not exist in a browser bundle. The SDK
75
+ itself is fine: its `client/index.js` and `client/streamableHttp.js` bundle at
76
+ `platform: 'browser'` with **zero** `node:` edges and never pull in
77
+ `client/stdio.js`. So the fix is not to reimplement anything — it is to let the
78
+ caller supply what the library would otherwise have loaded.
79
+
80
+ **`sdk?: McpSdk`** — hand over the two SDK modules, imported statically by your
81
+ own bundler, and the library **still builds the transport**. Everything the
82
+ transport carries keeps working: `headers`, your own `fetch`, gateway
83
+ vending, `retryOnThrottle`, `_meta` ingestion.
84
+
85
+ ```ts
86
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
87
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
88
+ import { mcpClient } from 'agentfootprint/providers';
89
+
90
+ const sidecar = await mcpClient({
91
+ name: 'sidecar',
92
+ sdk: { Client, StreamableHTTPClientTransport },
93
+ transport: { transport: 'http', url: '/py/mcp' },
94
+ });
95
+ const tools = await sidecar.tools(); // the same readonly Tool[], _meta and all
96
+ ```
97
+
98
+ **`connection?: McpConnection`** — the full escape hatch: you connect the
99
+ client, the library only adapts its tools. Three methods over JSON-RPC and no
100
+ vendor named, so an SDK `Client`, a fake, or a future fetch-only transport all
101
+ satisfy it. `connect()` is deliberately **absent** from the type: you already
102
+ connected it, and the library never calls it. Reach for this when the library
103
+ must not construct anything — a strict CSP, where the SDK's own
104
+ `jsonSchemaValidator` (reachable only here) is what keeps ajv's `new Function`
105
+ off the page.
106
+
107
+ **The refusals are the design.** On the `connection` arm the library builds no
108
+ transport, so every option consumed INSIDE one is refused at construction,
109
+ naming where the behaviour went — `retryOnThrottle`, `clientInfo`, `transport`,
110
+ `sdk`, `_client`. Accepting a knob that names a behaviour which no longer
111
+ happens is the defect class this release exists to close, not a convenience.
112
+ `signal` IS honoured on both arms: it rides the SDK's request options.
113
+
114
+ - **`retryingFetch` is public** (`agentfootprint/providers`), with
115
+ `ThrottleFetch`. It was `@internal`, and on the `connection` arm — the one a
116
+ browser takes — a caller would otherwise have silently lost the HTTP 429
117
+ handling every Node consumer gets ON by default. Same implementation, applied
118
+ where you build the transport: `fetch: retryingFetch(yourFetch, { maxAttempts: 5 })`.
119
+
120
+ ### Fixed
121
+
122
+ - **A relative `transport.url` resolves against the page.** `new URL('/py/mcp')`
123
+ throws `TypeError: Invalid URL` — correct in Node, wrong in a browser, where a
124
+ same-origin path is the ordinary way to reach a sidecar (and the way to avoid a
125
+ CORS preflight entirely). It now resolves against `globalThis.location.href`
126
+ when there is one, and in Node refuses **by name**, saying which world it is in
127
+ and to pass an absolute URL. An absolute url takes the identical first branch,
128
+ so Node behaviour has not moved.
129
+
130
+ - **The SDK-load errors stop lying.** Every load sat behind a bare `catch`, which
131
+ cannot tell "the package is absent" from "the LOADER is absent" — so a browser
132
+ that had the SDK installed all along was told to `npm install` it, and nothing
133
+ changed when it did. The failure is now classified at all seven sites (three in
134
+ `mcpClient`, four in `mcpServe`): a resolution failure produces the
135
+ **byte-identical** message every release before this one produced, and anything
136
+ else names the underlying error and the seam that gets past it. `mcpServe` says
137
+ the honest thing instead — it listens on stdio or a Node socket, so that
138
+ direction cannot run in a browser at all.
139
+
140
+ ### Unchanged, deliberately
141
+
142
+ - **Zero packaging change.** No new subpath, no `browser` export condition (a
143
+ compiler is blind to it), no `typesVersions` row, no `postbuild-esm.mjs` edit.
144
+ `@modelcontextprotocol/sdk` stays an OPTIONAL peer, and there is no literal
145
+ dynamic `import()` of it anywhere — one would be statically resolved by a
146
+ bundler and would hard-fail the build for every consumer who does not have it.
147
+
148
+ - **Every existing Node consumer.** A call with neither `sdk` nor `connection`
149
+ reaches the same `lazyRequire` on the same specifiers and builds the same
150
+ transport; the difference is a `??` on an `undefined` parameter. `stdio` keeps
151
+ the loader permanently — it spawns a subprocess, so it can never be portable,
152
+ and keeping it there is what keeps the SDK's one Node-importing client module
153
+ off every browser graph.
154
+
155
+ ### Proof, and its limits
156
+
157
+ `test/lib/mcp/browserGraph.test.ts` bundles the SHIPPED `dist/` the way a browser
158
+ build does and asserts: `agentfootprint/providers` still bundles with the MCP SDK
159
+ **blocked at resolve time** (the optional-peer property, stated as a build); its
160
+ `node:` edges are EXACTLY the two known ones; the builtins hidden behind
161
+ `lazyRequire` — which no module graph can see — are exactly the four known ones;
162
+ the path a browser walks reaches `node:module` and nothing else and never pulls
163
+ in `client/stdio.js`; and everything on that path except the loader bundles with
164
+ NO externals and zero node edges. `mcpConnection.real.test.ts` drives both new
165
+ arms through a real socket against the real SDK.
166
+
167
+ **What none of that proves: a browser.** This repo has no browser test
168
+ environment, so nobody has yet driven initialize/listTools/callTool from an
169
+ actual page. The honest status is *proven in Node, fenced at the graph, not gated
170
+ in a browser.* Three costs land on the app, not here: your server must send CORS
171
+ headers (every MCP request preflights, and `Mcp-Session-Id` must be in
172
+ `Access-Control-Expose-Headers`) — `mcpServe` sends none; SSE through a dev or
173
+ production proxy is unproven; and the SDK's client path adds roughly 260 KB
174
+ minified, about half of it ajv, whose `new Function` needs `unsafe-eval` the
175
+ first time a tool with an `outputSchema` is validated.
176
+
10
177
  ## [9.80.0] - 2026-08-30
11
178
 
12
179
  ### Added
package/CLAUDE.md CHANGED
@@ -17,7 +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
- | turning a written operational procedure (a runbook, a triage playbook) into ONE agent tool whose every answer is EVIDENCE — coverage folded up from the inner tools it calls, rule name+version, verdict rows with GENERATED meanings, and the recorded walk as an artifact ticket, never bytes | `runbookAsTool` — dials: `procedure` (factory, invoked per call with `ctx.tools`) + `resultKind` (`'verdict/*'` arms the rowset projection; anything else ships spine + the chart's `report`) + `rules {name, version}` (default absent ⇒ `rule_version: 'undeclared'`) + `verdicts {decider, maxRows}` (default 50) + `presentation` (`'prose'` default = ship `table` + `VERDICT_RENDER_NOTE` "output it VERBATIM"; `'panel'` = the HOST renders the rowset, so NO `table` key at all + `PANEL_RENDER_NOTE` "the rows are already on the reader's screen — do not reproduce them"; the rowset half is byte-identical across modes, `table` stays RESERVED in both, an unknown value THROWS at definition) + `walk {cap, recording}` (cap default 500; over-cap ⇒ control-flow projection, declared · `recording: true` or `{label, maxBytes}`, default OFF — ALSO files the inner chart's own `{snapshot, events, structure}` under `recording/run` and puts its ref on the spine as `walk.recording_ref`, because the ROW projection cannot be drawn; snapshot read from the REDACTED mirror so one `redact` means the same for both; over `maxBytes` (`DEFAULT_RECORDING_MAX_BYTES` = 5,000,000) it is REFUSED not truncated; every absence — no store / over size / unserializable / store threw — is STATED in `walk.recording_note`, and the four `recording_*` fields are absent entirely when the dial is off) + `composedOf` (drift-checked at agent BUILD) + kept `recorders`/`keepRecord`/`keepRecordLimit`/`redact`; reserved state keys `verdicts`/`coverage`/`report` (the `report` bag lands BESIDE the spine, never over it — a report field spelling `af_coverage`/`af_provenance`/`rule_version`/`walk`/`report_note` or a live projection key is discarded and NAMED in `result.report_note`; precedence is explicit in `report.ts`, never spread order); inner `absent()` passes through VERBATIM unless the call said `allowAbsent`; walk kind `recording/chart-walk` + `walk_segment` discriminant | `src/core/runbook/` | 9.76.0, recording 9.79.0 |
20
+ | turning a written operational procedure (a runbook, a triage playbook) into ONE agent tool whose every answer is EVIDENCE — coverage folded up from the inner tools it calls, rule name+version, verdict rows with GENERATED meanings, and the recorded walk as an artifact ticket, never bytes | `runbookAsTool` — dials: `procedure` (factory, invoked per call with `ctx.tools`) + `resultKind` (`'verdict/*'` arms the rowset projection; anything else ships spine + the chart's `report`) + `rules {name, version}` (default absent ⇒ `rule_version: 'undeclared'`) + `verdicts {decider, maxRows}` (default 50; `verdict_meanings` = statically declared branches + this run's rule labels + the DEFAULT branch's label, which reaches evidence only when the chart calls `decide(s, rules, {branch, label})` — fp ≥9.16.1, the default is chosen by NO rule so nothing else can name it, and inside a generated fan-out branch the static walk is blind too; an undeclared or blank label stays ABSENT from the map, never invented from a branch id, and there is deliberately NO caller-supplied meanings map) + `presentation` (`'prose'` default = ship `table` + `VERDICT_RENDER_NOTE` "output it VERBATIM"; `'panel'` = the HOST renders the rowset, so NO `table` key at all + `PANEL_RENDER_NOTE` "the rows are already on the reader's screen — do not reproduce them"; the rowset half is byte-identical across modes, `table` stays RESERVED in both, an unknown value THROWS at definition) + `walk {cap, recording}` (cap default 500; over-cap ⇒ control-flow projection, declared · `recording: true` or `{label, maxBytes}`, default OFF — ALSO files the inner chart's own `{snapshot, events, structure}` under `recording/run` and puts its ref on the spine as `walk.recording_ref`, because the ROW projection cannot be drawn; snapshot read from the REDACTED mirror so one `redact` means the same for both; over `maxBytes` (`DEFAULT_RECORDING_MAX_BYTES` = 5,000,000) it is REFUSED not truncated; every absence — no store / over size / unserializable / store threw — is STATED in `walk.recording_note`, and the four `recording_*` fields are absent entirely when the dial is off) + `composedOf` (drift-checked at agent BUILD) + kept `recorders`/`keepRecord`/`keepRecordLimit`/`redact`; reserved state keys `verdicts`/`coverage`/`report` (the `report` bag lands BESIDE the spine, never over it — a report field spelling `af_coverage`/`af_provenance`/`rule_version`/`walk`/`report_note` or a live projection key is discarded and NAMED in `result.report_note`; precedence is explicit in `report.ts`, never spread order); inner `absent()` passes through VERBATIM unless the call said `allowAbsent`; walk kind `recording/chart-walk` + `walk_segment` discriminant | `src/core/runbook/` | 9.76.0, recording 9.79.0 |
21
+ | speaking MCP from a BROWSER — the SDK is browser-clean (its client + streamableHttp bundle at `platform:'browser'` with zero `node:` edges and never pull in `client/stdio.js`); the one barrier was that `lazyRequire` gets CALLED there, so the fix is to let the caller supply what the loader would have found | `mcpClient({ sdk })` (`McpSdk` = the two SDK modules you imported statically; the library STILL builds the transport, so headers/fetch/gateway vending/`retryOnThrottle`/`_meta` all keep working) · `mcpClient({ connection })` (`McpConnection` = listTools/callTool/close, deliberately NO `connect` — you already did; the only arm that reaches the SDK's `jsonSchemaValidator`, i.e. CSP) + `McpConnectionOptions`; `refuseConflictingOptions` REFUSES at construction every option a transport would have consumed, naming where it moved (a knob naming a behaviour that no longer happens is the defect class); `transportUrl` resolves a relative `url` against `globalThis.location.href` (absolute takes the identical branch, Node refuses by name); `sdkLoadFailure` classifies the seven load sites so a browser is never told to install a package it has (resolution failure ⇒ byte-identical historical message); `retryingFetch` + `ThrottleFetch` promoted out of `@internal` so the connection arm keeps its 429 handling. stdio keeps `lazyRequire` FOREVER — it spawns a subprocess. ZERO packaging change: no subpath, no `browser` condition (TypeScript is blind to it), peer stays optional (no literal dynamic `import()`). Fenced by `test/lib/mcp/browserGraph.test.ts` | `src/lib/mcp/` | 9.81.0 |
22
+ | calling ANOTHER registered tool from inside a tool's `execute` — composition over the agent's own dispatch map instead of importing the module and building a second query stack | `agentToolDispatch` + `ctx.tools` (ToolDispatch has/call) — sees static and skill-carried tools, NEVER ToolProvider-delivered ones (no build-time list, the 9.72.0 caveat); inner calls get the outer facts with hasArtifacts false and a derived toolCallId, `needs` resolved fail-closed non-interactively, `checkIn` and `wants` tools refused by name, no nested dispatch; declare ingredients via `composedOf` (and `gates` for a pausing procedure) — both travel MCP `_meta` | `src/core/agent/toolDispatch.ts` | 9.76.0 |
21
23
  | 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 |
22
24
  | a build gate that refuses a triage/inventory tool that forgot its caveats, by tool name and field name | `checkSemantics` + `defineTool({ resultClass })` (`'triage'`/`'inventory'`, the closed set) + bin `agentfootprint-check-semantics` | `src/lib/semantics/check.ts` | 9.53.0 |
23
25
  | an agent that runs out of `maxIterations` mid-task handing back a HALF-SENTENCE as its answer — and nothing saying the budget ran out | `wrapUpAtMaxIterations` (default ON) + `WRAP_UP_INSTRUCTION` + `agent.budget_exhausted` + `stoppedEarly.wrappedUp` | `src/core/agent/stages/wrapUp.ts` | 9.56.0 |
@@ -24,10 +24,21 @@
24
24
  * GENERATED MEANINGS, never hand-restated: `verdict_meanings` is composed
25
25
  * from (a) the named decider's declared branches in the chart's own structure
26
26
  * (branch description, falling back to branch name), overlaid by (b) the rule
27
- * LABELS this run's decide() evidence carried — the rule speaking for itself.
27
+ * LABELS this run's decide() evidence carried — the rule speaking for itself
28
+ * and (c) the DEFAULT branch's declared label, carried by the same evidence.
28
29
  * A decider inside a dynamically generated fan-out branch is invisible to (a)
29
30
  * by construction (the branch chart does not exist at build time); (b) still
30
31
  * covers every verdict an executed rule produced.
32
+ *
33
+ * (c) exists because the default branch is chosen by NO rule — it fires
34
+ * exactly when every rule failed, so it appears in no `rules[]` entry and (b)
35
+ * can never name it. In a generated fan-out branch, where (a) is blind too, it
36
+ * was the one verdict the rowset could show and the meanings map could not
37
+ * explain. `decide(scope, rules, { branch, label })` (footprintjs ≥ 9.16.1) puts
38
+ * that label on the decision evidence, so it arrives here the way every other
39
+ * label does: harvested from the run, never declared at this boundary. There is
40
+ * deliberately no caller-supplied meanings map — a map a caller can write is a
41
+ * map that can describe rules that never ran.
31
42
  */
32
43
  Object.defineProperty(exports, "__esModule", { value: true });
33
44
  exports.composeMeanings = exports.meaningsRecorder = exports.resolveDecider = exports.renderVerdictTable = exports.verdictRowsOf = exports.DECLINED_VERDICT = exports.PANEL_RENDER_NOTE = exports.VERDICT_RENDER_NOTE = exports.DEFAULT_MAX_ROWS = void 0;
@@ -132,11 +143,17 @@ function resolveDecider(chart, decider) {
132
143
  return { spellings, declared };
133
144
  }
134
145
  exports.resolveDecider = resolveDecider;
146
+ /** A label is a meaning only when it says something — a blank one is recorded
147
+ * as no meaning at all, never as a verdict that means the empty string. */
148
+ function meaningful(label) {
149
+ return typeof label === 'string' && label.length > 0;
150
+ }
135
151
  /**
136
152
  * A tiny flow recorder capturing the named decider's decide() evidence as it
137
153
  * fires — collected DURING the traversal, never reconstructed after. Rule
138
154
  * labels are harvested for every rule the evidence lists (matched or not):
139
- * a rule that was evaluated has spoken its label, whichever branch won.
155
+ * a rule that was evaluated has spoken its label, whichever branch won. The
156
+ * DEFAULT branch's label rides the same evidence and is harvested with them.
140
157
  *
141
158
  * A decider inside a subflow (including a generated fan-out branch) reports
142
159
  * itself PATH-PREFIXED (`per-subject~0/Protection posture`), so the match is
@@ -153,8 +170,14 @@ function meaningsRecorder(identity) {
153
170
  if (!identity.spellings.has(lastSegment(event.decider)))
154
171
  return;
155
172
  const evidence = event.evidence;
173
+ // The default first, the rules over it. The default is the branch NO
174
+ // rule chose, so it is normally the only speaker for its own name; where
175
+ // a rule also routes to it, the rule is the sharper sentence and wins.
176
+ if (typeof evidence?.default === 'string' && meaningful(evidence.defaultLabel)) {
177
+ observed.set(evidence.default, evidence.defaultLabel);
178
+ }
156
179
  for (const rule of evidence?.rules ?? []) {
157
- if (typeof rule.branch === 'string' && typeof rule.label === 'string') {
180
+ if (typeof rule.branch === 'string' && meaningful(rule.label)) {
158
181
  observed.set(rule.branch, rule.label);
159
182
  }
160
183
  }
@@ -163,9 +186,10 @@ function meaningsRecorder(identity) {
163
186
  return { recorder, observed };
164
187
  }
165
188
  exports.meaningsRecorder = meaningsRecorder;
166
- /** Compose the final meanings: declared branches first, observed rule labels
167
- * winning where both speak (the rule is the sharper sentence). Undefined
168
- * when neither source produced anything absent, never `{}`. */
189
+ /** Compose the final meanings: declared branches first, the labels this run
190
+ * observed (rule labels, and the default branch's own) winning where both
191
+ * speak the declaration made beside the rules is the sharper sentence.
192
+ * Undefined when neither source produced anything — absent, never `{}`. */
169
193
  function composeMeanings(identity, observed) {
170
194
  const meanings = {};
171
195
  for (const [branch, meaning] of identity.declared)
@@ -1 +1 @@
1
- {"version":3,"file":"verdicts.js","sourceRoot":"","sources":["../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;;;AAMH,6DAA6D;AAChD,QAAA,gBAAgB,GAAG,EAAE,CAAC;AAEnC;qEACqE;AACxD,QAAA,mBAAmB,GAC9B,qFAAqF;IACrF,wFAAwF;IACxF,kDAAkD,CAAC;AAErD;;;;;;;GAOG;AACU,QAAA,iBAAiB,GAC5B,oFAAoF;IACpF,sFAAsF;IACtF,wFAAwF;IACxF,wFAAwF;IACxF,qFAAqF;IACrF,oEAAoE,CAAC;AAEvE;;yEAEyE;AAC5D,QAAA,gBAAgB,GAAG,UAAU,CAAC;AAE3C;4EAC4E;AAC5E,SAAgB,aAAa,CAAC,KAAwC;IACpE,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC;IAC3B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,GAAG,CAAC,MAAM,CACf,CAAC,GAAG,EAAqB,EAAE,CACzB,GAAG,KAAK,IAAI;QACZ,OAAO,GAAG,KAAK,QAAQ;QACvB,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QACnB,OAAQ,GAA6B,CAAC,OAAO,KAAK,QAAQ,CAC7D,CAAC;AACJ,CAAC;AAVD,sCAUC;AAED;kEACkE;AAClE,SAAS,IAAI,CAAC,KAAc;IAC1B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC;IACtE,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACjC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACjE,CAAC;AAED;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,IAA2B;IAC5D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,+BAA+B,CAAC;IAC9D,oEAAoE;IACpE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;IACrF,OAAO,CACL,IAAI;QACJ,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC9F,CAAC;AACJ,CAAC;AATD,gDASC;AAwBD;;;;GAIG;AACH,SAAgB,cAAc,CAAC,KAAgB,EAAE,OAAe;IAC9D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAY,CAAC;IACpC,MAAM,IAAI,GAAG,CAAC,IAA0B,EAAQ,EAAE;QAChD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO;QAC/E,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACvF,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS;gBAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;gBACxC,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,CAAC;gBAC1C,IAAI,MAAM,KAAK,SAAS;oBAAE,SAAS;gBACnC,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC;gBAChD,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC;oBAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE;YAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,IAAI,CAAC,KAAK,CAAC,IAAgB,CAAC,CAAC;IAC7B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,OAAO,CAAC,IAAgB,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC;AAzBD,wCAyBC;AAQD;;;;;;;;;;;GAWG;AACH,SAAgB,gBAAgB,CAAC,QAAyB;IACxD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,WAAW,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACpF,MAAM,QAAQ,GAAG;QACf,EAAE,EAAE,0BAA0B;QAC9B,UAAU,CAAC,KAAwB;YACjC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAAE,OAAO;YAChE,MAAM,QAAQ,GAAG,KAAK,CAAC,QAEV,CAAC;YACd,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;gBACzC,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;oBACtE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gBACxC,CAAC;YACH,CAAC;QACH,CAAC;KAC6B,CAAC;IACjC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAlBD,4CAkBC;AAED;;kEAEkE;AAClE,SAAgB,eAAe,CAC7B,QAAyB,EACzB,QAAqC;IAErC,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,QAAQ,CAAC,QAAQ;QAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;IAC9E,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,QAAQ;QAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;IACjE,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC;AARD,0CAQC"}
1
+ {"version":3,"file":"verdicts.js","sourceRoot":"","sources":["../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":"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"}
@@ -57,6 +57,14 @@ export interface RunbookVerdictsOptions {
57
57
  * in this run's decide() evidence refine them (and are the only source
58
58
  * when the decider lives inside a dynamically generated fan-out branch,
59
59
  * where build-time structure cannot see it).
60
+ *
61
+ * The DEFAULT branch is chosen by no rule, so no rule label describes it.
62
+ * Name it where the rules are named — `decide(scope, rules, { branch,
63
+ * label })` — and the label arrives here on the same evidence:
64
+ *
65
+ * ```ts
66
+ * decide(scope, rules, { branch: 'protected', label: 'No rule fired — asset stays protected' });
67
+ * ```
60
68
  */
61
69
  readonly decider: string;
62
70
  /** Cap on `verdicts` rows AND the rendered table — ONE number for both
@@ -23,10 +23,21 @@
23
23
  * GENERATED MEANINGS, never hand-restated: `verdict_meanings` is composed
24
24
  * from (a) the named decider's declared branches in the chart's own structure
25
25
  * (branch description, falling back to branch name), overlaid by (b) the rule
26
- * LABELS this run's decide() evidence carried — the rule speaking for itself.
26
+ * LABELS this run's decide() evidence carried — the rule speaking for itself
27
+ * and (c) the DEFAULT branch's declared label, carried by the same evidence.
27
28
  * A decider inside a dynamically generated fan-out branch is invisible to (a)
28
29
  * by construction (the branch chart does not exist at build time); (b) still
29
30
  * covers every verdict an executed rule produced.
31
+ *
32
+ * (c) exists because the default branch is chosen by NO rule — it fires
33
+ * exactly when every rule failed, so it appears in no `rules[]` entry and (b)
34
+ * can never name it. In a generated fan-out branch, where (a) is blind too, it
35
+ * was the one verdict the rowset could show and the meanings map could not
36
+ * explain. `decide(scope, rules, { branch, label })` (footprintjs ≥ 9.16.1) puts
37
+ * that label on the decision evidence, so it arrives here the way every other
38
+ * label does: harvested from the run, never declared at this boundary. There is
39
+ * deliberately no caller-supplied meanings map — a map a caller can write is a
40
+ * map that can describe rules that never ran.
30
41
  */
31
42
  import type { CombinedRecorder } from 'footprintjs';
32
43
  import type { FlowChart } from 'footprintjs';
@@ -80,7 +91,8 @@ export interface MeaningsHarvest {
80
91
  * A tiny flow recorder capturing the named decider's decide() evidence as it
81
92
  * fires — collected DURING the traversal, never reconstructed after. Rule
82
93
  * labels are harvested for every rule the evidence lists (matched or not):
83
- * a rule that was evaluated has spoken its label, whichever branch won.
94
+ * a rule that was evaluated has spoken its label, whichever branch won. The
95
+ * DEFAULT branch's label rides the same evidence and is harvested with them.
84
96
  *
85
97
  * A decider inside a subflow (including a generated fan-out branch) reports
86
98
  * itself PATH-PREFIXED (`per-subject~0/Protection posture`), so the match is
@@ -89,7 +101,8 @@ export interface MeaningsHarvest {
89
101
  * marker opaque here.
90
102
  */
91
103
  export declare function meaningsRecorder(identity: DeciderIdentity): MeaningsHarvest;
92
- /** Compose the final meanings: declared branches first, observed rule labels
93
- * winning where both speak (the rule is the sharper sentence). Undefined
94
- * when neither source produced anything absent, never `{}`. */
104
+ /** Compose the final meanings: declared branches first, the labels this run
105
+ * observed (rule labels, and the default branch's own) winning where both
106
+ * speak the declaration made beside the rules is the sharper sentence.
107
+ * Undefined when neither source produced anything — absent, never `{}`. */
95
108
  export declare function composeMeanings(identity: DeciderIdentity, observed: ReadonlyMap<string, string>): Record<string, string> | undefined;
@@ -23,10 +23,21 @@
23
23
  * GENERATED MEANINGS, never hand-restated: `verdict_meanings` is composed
24
24
  * from (a) the named decider's declared branches in the chart's own structure
25
25
  * (branch description, falling back to branch name), overlaid by (b) the rule
26
- * LABELS this run's decide() evidence carried — the rule speaking for itself.
26
+ * LABELS this run's decide() evidence carried — the rule speaking for itself
27
+ * and (c) the DEFAULT branch's declared label, carried by the same evidence.
27
28
  * A decider inside a dynamically generated fan-out branch is invisible to (a)
28
29
  * by construction (the branch chart does not exist at build time); (b) still
29
30
  * covers every verdict an executed rule produced.
31
+ *
32
+ * (c) exists because the default branch is chosen by NO rule — it fires
33
+ * exactly when every rule failed, so it appears in no `rules[]` entry and (b)
34
+ * can never name it. In a generated fan-out branch, where (a) is blind too, it
35
+ * was the one verdict the rowset could show and the meanings map could not
36
+ * explain. `decide(scope, rules, { branch, label })` (footprintjs ≥ 9.16.1) puts
37
+ * that label on the decision evidence, so it arrives here the way every other
38
+ * label does: harvested from the run, never declared at this boundary. There is
39
+ * deliberately no caller-supplied meanings map — a map a caller can write is a
40
+ * map that can describe rules that never ran.
30
41
  */
31
42
  /** Default cap on `verdicts` rows and the rendered table. */
32
43
  export const DEFAULT_MAX_ROWS = 50;
@@ -126,11 +137,17 @@ export function resolveDecider(chart, decider) {
126
137
  }
127
138
  return { spellings, declared };
128
139
  }
140
+ /** A label is a meaning only when it says something — a blank one is recorded
141
+ * as no meaning at all, never as a verdict that means the empty string. */
142
+ function meaningful(label) {
143
+ return typeof label === 'string' && label.length > 0;
144
+ }
129
145
  /**
130
146
  * A tiny flow recorder capturing the named decider's decide() evidence as it
131
147
  * fires — collected DURING the traversal, never reconstructed after. Rule
132
148
  * labels are harvested for every rule the evidence lists (matched or not):
133
- * a rule that was evaluated has spoken its label, whichever branch won.
149
+ * a rule that was evaluated has spoken its label, whichever branch won. The
150
+ * DEFAULT branch's label rides the same evidence and is harvested with them.
134
151
  *
135
152
  * A decider inside a subflow (including a generated fan-out branch) reports
136
153
  * itself PATH-PREFIXED (`per-subject~0/Protection posture`), so the match is
@@ -147,8 +164,14 @@ export function meaningsRecorder(identity) {
147
164
  if (!identity.spellings.has(lastSegment(event.decider)))
148
165
  return;
149
166
  const evidence = event.evidence;
167
+ // The default first, the rules over it. The default is the branch NO
168
+ // rule chose, so it is normally the only speaker for its own name; where
169
+ // a rule also routes to it, the rule is the sharper sentence and wins.
170
+ if (typeof evidence?.default === 'string' && meaningful(evidence.defaultLabel)) {
171
+ observed.set(evidence.default, evidence.defaultLabel);
172
+ }
150
173
  for (const rule of evidence?.rules ?? []) {
151
- if (typeof rule.branch === 'string' && typeof rule.label === 'string') {
174
+ if (typeof rule.branch === 'string' && meaningful(rule.label)) {
152
175
  observed.set(rule.branch, rule.label);
153
176
  }
154
177
  }
@@ -156,9 +179,10 @@ export function meaningsRecorder(identity) {
156
179
  };
157
180
  return { recorder, observed };
158
181
  }
159
- /** Compose the final meanings: declared branches first, observed rule labels
160
- * winning where both speak (the rule is the sharper sentence). Undefined
161
- * when neither source produced anything absent, never `{}`. */
182
+ /** Compose the final meanings: declared branches first, the labels this run
183
+ * observed (rule labels, and the default branch's own) winning where both
184
+ * speak the declaration made beside the rules is the sharper sentence.
185
+ * Undefined when neither source produced anything — absent, never `{}`. */
162
186
  export function composeMeanings(identity, observed) {
163
187
  const meanings = {};
164
188
  for (const [branch, meaning] of identity.declared)
@@ -1 +1 @@
1
- {"version":3,"file":"verdicts.js","sourceRoot":"","sources":["../../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"verdicts.js","sourceRoot":"","sources":["../../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * connectionRefusals — the two arms of `mcpClient` refuse to be mixed, at
3
+ * CONSTRUCTION, in words that name where the behaviour went.
4
+ *
5
+ * ── Why refuse rather than ignore ────────────────────────────────────────────
6
+ * `mcpClient({ connection })` hands over a client somebody else built, so this
7
+ * library builds no transport on that arm — and every option consumed INSIDE a
8
+ * transport therefore has nothing to act on. `retryOnThrottle` is the sharp
9
+ * one: it is ON by default, it is consumed by `retryingFetch` around the
10
+ * transport's `fetch`, and accepting it here would leave a caller holding an
11
+ * option that NAMES a behaviour which no longer happens. A knob that lies about
12
+ * what it does is worse than one that is absent, so these throw.
13
+ *
14
+ * The type union refuses the same combinations at compile time
15
+ * (`McpConnectionOptions` declares each of them `?: undefined`). This is the
16
+ * runtime half, and it is not redundant: excess-property checking does not
17
+ * survive a spread, and JavaScript callers have no compiler at all.
18
+ *
19
+ * Pattern: pure guard. Role: Layer-3 tool integration.
20
+ */
21
+ import type { McpClientOptions, McpConnectionOptions } from './types.js';
22
+ /**
23
+ * Refuse a mixed or malformed options object before anything connects.
24
+ *
25
+ * @param opts what the caller passed, before any defaulting
26
+ * @param name the client's logical name, so a multi-server app knows which one
27
+ * @throws naming the two options that cannot travel together, or the member the
28
+ * connection is missing
29
+ */
30
+ export declare function refuseConflictingOptions(opts: McpClientOptions | McpConnectionOptions, name: string): void;
@@ -0,0 +1,110 @@
1
+ /**
2
+ * connectionRefusals — the two arms of `mcpClient` refuse to be mixed, at
3
+ * CONSTRUCTION, in words that name where the behaviour went.
4
+ *
5
+ * ── Why refuse rather than ignore ────────────────────────────────────────────
6
+ * `mcpClient({ connection })` hands over a client somebody else built, so this
7
+ * library builds no transport on that arm — and every option consumed INSIDE a
8
+ * transport therefore has nothing to act on. `retryOnThrottle` is the sharp
9
+ * one: it is ON by default, it is consumed by `retryingFetch` around the
10
+ * transport's `fetch`, and accepting it here would leave a caller holding an
11
+ * option that NAMES a behaviour which no longer happens. A knob that lies about
12
+ * what it does is worse than one that is absent, so these throw.
13
+ *
14
+ * The type union refuses the same combinations at compile time
15
+ * (`McpConnectionOptions` declares each of them `?: undefined`). This is the
16
+ * runtime half, and it is not redundant: excess-property checking does not
17
+ * survive a spread, and JavaScript callers have no compiler at all.
18
+ *
19
+ * Pattern: pure guard. Role: Layer-3 tool integration.
20
+ */
21
+ /** The three methods this library calls on a connection. Nothing else. */
22
+ const CONNECTION_METHODS = ['listTools', 'callTool', 'close'];
23
+ /**
24
+ * Refuse a mixed or malformed options object before anything connects.
25
+ *
26
+ * @param opts what the caller passed, before any defaulting
27
+ * @param name the client's logical name, so a multi-server app knows which one
28
+ * @throws naming the two options that cannot travel together, or the member the
29
+ * connection is missing
30
+ */
31
+ export function refuseConflictingOptions(opts, name) {
32
+ const at = `mcpClient[${name}]`;
33
+ // Read through a permissive view on purpose. The union already narrows these
34
+ // combinations away at compile time, so narrowing here would leave the guard
35
+ // reasoning about a shape it exists precisely to disbelieve — an object built
36
+ // by a spread, or by JavaScript.
37
+ const given = opts;
38
+ if (given['connection'] === undefined) {
39
+ if (given['transport'] === undefined && given['_client'] === undefined) {
40
+ throw new Error(`${at}: nothing to connect to. Pass \`transport\` (the library builds the connection) ` +
41
+ 'or `connection` (a client you connected yourself).');
42
+ }
43
+ return;
44
+ }
45
+ // From here on the caller chose the `connection` arm.
46
+ for (const [option, moved] of BUILT_BY_THE_TRANSPORT) {
47
+ if (given[option] !== undefined) {
48
+ throw new Error(`${at}: \`connection\` and \`${option}\` cannot travel together. ` +
49
+ `A connection you built yourself carries its own transport, and \`${option}\` is ` +
50
+ `consumed inside the transport this library did not build. ${moved}`);
51
+ }
52
+ }
53
+ assertConnection(given['connection'], at);
54
+ }
55
+ /**
56
+ * The options that only exist because the library builds the transport, each
57
+ * paired with the sentence naming where that behaviour moved to. Ordered so the
58
+ * most surprising loss — throttle retry, which is ON by default — is named
59
+ * first when a caller passes several.
60
+ */
61
+ const BUILT_BY_THE_TRANSPORT = [
62
+ [
63
+ 'retryOnThrottle',
64
+ 'Wrap your own `fetch` with `retryingFetch(yourFetch, options)` (exported from ' +
65
+ '`agentfootprint/providers`) and hand THAT to your transport — it is the same ' +
66
+ 'implementation, applied where you build it.',
67
+ ],
68
+ [
69
+ 'clientInfo',
70
+ 'Pass it to the SDK `Client` constructor instead: `new Client(clientInfo, { capabilities: {} })`.',
71
+ ],
72
+ [
73
+ 'transport',
74
+ 'Drop one of the two: `transport` asks the library to connect, `connection` says it already is.',
75
+ ],
76
+ [
77
+ 'sdk',
78
+ '`sdk` exists so the library can build the transport without its Node loader; ' +
79
+ 'on this arm you have already built it.',
80
+ ],
81
+ [
82
+ '_client',
83
+ '`_client` is the same idea as `connection` and predates it — pass `connection` alone.',
84
+ ],
85
+ ];
86
+ /**
87
+ * A connection is only a connection if it can be called.
88
+ *
89
+ * This catches the near-miss people actually make: handing over the TRANSPORT
90
+ * rather than the client. A transport has none of these three methods, and
91
+ * without this check it fails on the first `tools()`, one stack frame deep
92
+ * inside the SDK.
93
+ *
94
+ * The OTHER near-miss — a `Client` that was constructed but never `connect()`ed
95
+ * — cannot be caught here, because it has all three methods. It is named in the
96
+ * message anyway, since it produces the same "my connection does not work" and
97
+ * the SDK's own "Not connected" is the thing to look for.
98
+ */
99
+ function assertConnection(connection, at) {
100
+ const members = (connection ?? {});
101
+ for (const method of CONNECTION_METHODS) {
102
+ if (typeof members[method] !== 'function') {
103
+ throw new Error(`${at}: \`connection\` has no \`${method}()\`. It must be an MCP client that is ` +
104
+ 'already connected, and the likely mistake is passing the TRANSPORT instead of the ' +
105
+ 'client. (A `new Client(...)` you never awaited `connect()` on passes this check ' +
106
+ 'and fails later with the SDK\'s own "Not connected".)');
107
+ }
108
+ }
109
+ }
110
+ //# sourceMappingURL=connectionRefusals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectionRefusals.js","sourceRoot":"","sources":["../../../../src/lib/mcp/connectionRefusals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,0EAA0E;AAC1E,MAAM,kBAAkB,GAAG,CAAC,WAAW,EAAE,UAAU,EAAE,OAAO,CAAU,CAAC;AAEvE;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAA6C,EAC7C,IAAY;IAEZ,MAAM,EAAE,GAAG,aAAa,IAAI,GAAG,CAAC;IAChC,6EAA6E;IAC7E,6EAA6E;IAC7E,8EAA8E;IAC9E,iCAAiC;IACjC,MAAM,KAAK,GAAG,IAAoD,CAAC;IAEnE,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,SAAS,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,KAAK,SAAS,EAAE,CAAC;YACvE,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,kFAAkF;gBACrF,oDAAoD,CACvD,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IAED,sDAAsD;IACtD,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,sBAAsB,EAAE,CAAC;QACrD,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,0BAA0B,MAAM,6BAA6B;gBAChE,oEAAoE,MAAM,QAAQ;gBAClF,6DAA6D,KAAK,EAAE,CACvE,CAAC;QACJ,CAAC;IACH,CAAC;IACD,gBAAgB,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;GAKG;AACH,MAAM,sBAAsB,GAExB;IACF;QACE,iBAAiB;QACjB,gFAAgF;YAC9E,+EAA+E;YAC/E,6CAA6C;KAChD;IACD;QACE,YAAY;QACZ,kGAAkG;KACnG;IACD;QACE,WAAW;QACX,gGAAgG;KACjG;IACD;QACE,KAAK;QACL,+EAA+E;YAC7E,wCAAwC;KAC3C;IACD;QACE,SAAS;QACT,uFAAuF;KACxF;CACF,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,SAAS,gBAAgB,CAAC,UAAmB,EAAE,EAAU;IACvD,MAAM,OAAO,GAAG,CAAC,UAAU,IAAI,EAAE,CAAsC,CAAC;IACxE,KAAK,MAAM,MAAM,IAAI,kBAAkB,EAAE,CAAC;QACxC,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,GAAG,EAAE,6BAA6B,MAAM,yCAAyC;gBAC/E,oFAAoF;gBACpF,kFAAkF;gBAClF,uDAAuD,CAC1D,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}