agentfootprint 9.73.0 → 9.75.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 +1 -1
- package/CHANGELOG.md +201 -0
- package/CLAUDE.md +3 -2
- package/ai-instructions/claude-code/SKILL.md +1 -1
- package/dist/adapters/identity/azure.js +306 -0
- package/dist/adapters/identity/azure.js.map +1 -0
- package/dist/adapters/llm/FoundryLocalProvider.js +992 -0
- package/dist/adapters/llm/FoundryLocalProvider.js.map +1 -0
- package/dist/adapters/llm/FoundryProvider.js +273 -0
- package/dist/adapters/llm/FoundryProvider.js.map +1 -0
- package/dist/adapters/llm/OllamaProvider.js +171 -12
- package/dist/adapters/llm/OllamaProvider.js.map +1 -1
- package/dist/adapters/llm/OpenAIProvider.js +105 -2
- package/dist/adapters/llm/OpenAIProvider.js.map +1 -1
- package/dist/adapters/llm/createProvider.js +143 -10
- package/dist/adapters/llm/createProvider.js.map +1 -1
- package/dist/adapters/types.js.map +1 -1
- package/dist/core/Agent.js +23 -1
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/AgentBuilder.js +12 -0
- package/dist/core/agent/AgentBuilder.js.map +1 -1
- package/dist/core/agent/evidence/gate.js +22 -3
- package/dist/core/agent/evidence/gate.js.map +1 -1
- package/dist/core/agent/stagedRefs.js +161 -0
- package/dist/core/agent/stagedRefs.js.map +1 -0
- package/dist/core/agent/stages/callLLM.js +22 -2
- package/dist/core/agent/stages/callLLM.js.map +1 -1
- package/dist/core/agent/stages/evidenceRecheck.js +21 -2
- package/dist/core/agent/stages/evidenceRecheck.js.map +1 -1
- package/dist/esm/adapters/identity/azure.d.ts +188 -0
- package/dist/esm/adapters/identity/azure.js +302 -0
- package/dist/esm/adapters/identity/azure.js.map +1 -0
- package/dist/esm/adapters/llm/FoundryLocalProvider.d.ts +215 -0
- package/dist/esm/adapters/llm/FoundryLocalProvider.js +986 -0
- package/dist/esm/adapters/llm/FoundryLocalProvider.js.map +1 -0
- package/dist/esm/adapters/llm/FoundryProvider.d.ts +178 -0
- package/dist/esm/adapters/llm/FoundryProvider.js +268 -0
- package/dist/esm/adapters/llm/FoundryProvider.js.map +1 -0
- package/dist/esm/adapters/llm/OllamaProvider.js +171 -12
- package/dist/esm/adapters/llm/OllamaProvider.js.map +1 -1
- package/dist/esm/adapters/llm/OpenAIProvider.d.ts +92 -1
- package/dist/esm/adapters/llm/OpenAIProvider.js +103 -1
- package/dist/esm/adapters/llm/OpenAIProvider.js.map +1 -1
- package/dist/esm/adapters/llm/createProvider.d.ts +48 -12
- package/dist/esm/adapters/llm/createProvider.js +143 -10
- package/dist/esm/adapters/llm/createProvider.js.map +1 -1
- package/dist/esm/adapters/types.d.ts +4 -2
- package/dist/esm/adapters/types.js.map +1 -1
- package/dist/esm/core/Agent.js +23 -1
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/AgentBuilder.d.ts +12 -0
- package/dist/esm/core/agent/AgentBuilder.js +12 -0
- package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
- package/dist/esm/core/agent/evidence/gate.d.ts +11 -1
- package/dist/esm/core/agent/evidence/gate.js +22 -3
- package/dist/esm/core/agent/evidence/gate.js.map +1 -1
- package/dist/esm/core/agent/evidence/types.d.ts +23 -0
- package/dist/esm/core/agent/stagedRefs.d.ts +94 -0
- package/dist/esm/core/agent/stagedRefs.js +154 -0
- package/dist/esm/core/agent/stagedRefs.js.map +1 -0
- package/dist/esm/core/agent/stages/callLLM.d.ts +17 -0
- package/dist/esm/core/agent/stages/callLLM.js +22 -2
- package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
- package/dist/esm/core/agent/stages/evidenceRecheck.d.ts +19 -1
- package/dist/esm/core/agent/stages/evidenceRecheck.js +21 -2
- package/dist/esm/core/agent/stages/evidenceRecheck.js.map +1 -1
- package/dist/esm/events/payloads.d.ts +39 -0
- package/dist/esm/events/registry.d.ts +3 -1
- package/dist/esm/events/registry.js +2 -0
- package/dist/esm/events/registry.js.map +1 -1
- package/dist/esm/identity.d.ts +1 -0
- package/dist/esm/identity.js +9 -0
- package/dist/esm/identity.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/providers.d.ts +5 -0
- package/dist/esm/providers.js +14 -0
- package/dist/esm/providers.js.map +1 -1
- package/dist/events/registry.js +2 -0
- package/dist/events/registry.js.map +1 -1
- package/dist/identity.js +14 -1
- package/dist/identity.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/providers.js +20 -1
- package/dist/providers.js.map +1 -1
- package/dist/types/adapters/identity/azure.d.ts +189 -0
- package/dist/types/adapters/identity/azure.d.ts.map +1 -0
- package/dist/types/adapters/llm/FoundryLocalProvider.d.ts +216 -0
- package/dist/types/adapters/llm/FoundryLocalProvider.d.ts.map +1 -0
- package/dist/types/adapters/llm/FoundryProvider.d.ts +179 -0
- package/dist/types/adapters/llm/FoundryProvider.d.ts.map +1 -0
- package/dist/types/adapters/llm/OllamaProvider.d.ts.map +1 -1
- package/dist/types/adapters/llm/OpenAIProvider.d.ts +92 -1
- package/dist/types/adapters/llm/OpenAIProvider.d.ts.map +1 -1
- package/dist/types/adapters/llm/createProvider.d.ts +48 -12
- package/dist/types/adapters/llm/createProvider.d.ts.map +1 -1
- package/dist/types/adapters/types.d.ts +4 -2
- package/dist/types/adapters/types.d.ts.map +1 -1
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/AgentBuilder.d.ts +12 -0
- package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
- package/dist/types/core/agent/evidence/gate.d.ts +11 -1
- package/dist/types/core/agent/evidence/gate.d.ts.map +1 -1
- package/dist/types/core/agent/evidence/types.d.ts +23 -0
- package/dist/types/core/agent/evidence/types.d.ts.map +1 -1
- package/dist/types/core/agent/stagedRefs.d.ts +95 -0
- package/dist/types/core/agent/stagedRefs.d.ts.map +1 -0
- package/dist/types/core/agent/stages/callLLM.d.ts +17 -0
- package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
- package/dist/types/core/agent/stages/evidenceRecheck.d.ts +19 -1
- package/dist/types/core/agent/stages/evidenceRecheck.d.ts.map +1 -1
- package/dist/types/events/payloads.d.ts +39 -0
- package/dist/types/events/payloads.d.ts.map +1 -1
- package/dist/types/events/registry.d.ts +3 -1
- package/dist/types/events/registry.d.ts.map +1 -1
- package/dist/types/identity.d.ts +1 -0
- package/dist/types/identity.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/providers.d.ts +5 -0
- package/dist/types/providers.d.ts.map +1 -1
- package/package.json +5 -1
package/AGENTS.md
CHANGED
|
@@ -524,7 +524,7 @@ files `agentfootprint.tools.coverage_declared`; with
|
|
|
524
524
|
to the final answer, which is how a limit survives a model that would rather not
|
|
525
525
|
mention it.
|
|
526
526
|
|
|
527
|
-
### Observability —
|
|
527
|
+
### Observability — 109 typed events across 24 domains
|
|
528
528
|
|
|
529
529
|
```typescript
|
|
530
530
|
agent.on('agentfootprint.context.injected', (e) =>
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,207 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [9.75.0] - 2026-08-28
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Grounded numbers: the staged-refs nudge, and a revise correction that
|
|
15
|
+
names the route.** The field failure this closes, from a consumer's recorded
|
|
16
|
+
run: four tool results carried real numbers, a compute tool that could sum
|
|
17
|
+
them was registered — with `wants` declared over the staged dataset kind —
|
|
18
|
+
and the app's prompt said to use it. The model summed the numbers in its
|
|
19
|
+
head anyway and stated the total; the evidence gate recorded *"appears in no
|
|
20
|
+
tool result"* and the answer shipped, because the posture only observed. The
|
|
21
|
+
app patched it with more prose. The library-shaped fix is two mechanisms it
|
|
22
|
+
already owns, on the one dial it already has:
|
|
23
|
+
|
|
24
|
+
**`nudge: true` on `.namesAndNumbersFromEvidence()`** — when an iteration's
|
|
25
|
+
context holds a tool result staged by reference (an `artifacts.placement`
|
|
26
|
+
ticket) AND a tool the model can currently call declares `wants` over that
|
|
27
|
+
ticket's kind, ONE short line is appended at the very END of that request,
|
|
28
|
+
naming the refs and the spender tool by its registered name: derived numbers
|
|
29
|
+
come from the tool, not from mental arithmetic. Composed entirely from
|
|
30
|
+
declarations (`Tool.resultKind`, `Tool.wants`, matched by the exact-string
|
|
31
|
+
law dispatch uses — never by tool name); no prose surface for apps. The
|
|
32
|
+
placement is the point: the measured failure was recency — the app's own
|
|
33
|
+
instruction sat at the top of a long context and the numbers at the bottom,
|
|
34
|
+
so this one sits beside the data. Request-only (never history, so it never
|
|
35
|
+
enters the gate's exempt corpus), recomposed per iteration so it exists
|
|
36
|
+
exactly while both conditions hold, judged against the tools REALLY served
|
|
37
|
+
this call (the wrap-up's withheld surface arms nothing). Each firing lands
|
|
38
|
+
as `agentfootprint.agent.grounding_nudged` (event 109) with refs and tools
|
|
39
|
+
as data — the line's one record, since the line itself is not conversation.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const agent = Agent.create({ provider, model, artifacts: { store, placement } })
|
|
43
|
+
.tool(exportRows) // resultKind: 'dataset/rows' — staged over the threshold
|
|
44
|
+
.tool(compute) // wants: { dataset: 'dataset/rows' } — the declared spender
|
|
45
|
+
.namesAndNumbersFromEvidence({ posture: 'guard', nudge: true })
|
|
46
|
+
.build();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**The guarantee stays the postures the gate has had since 9.35.0** — the
|
|
50
|
+
existing value extraction is THE detector, unchanged: `'assist'` records,
|
|
51
|
+
`'guard'` allows the one bounded revision then delivers with both attempts
|
|
52
|
+
on the record, `'rails'` refuses with `UnsupportedValuesError`. What the
|
|
53
|
+
revision gains: when the flagged turn holds staged refs a served `wants`
|
|
54
|
+
tool can spend, the correction now names them — *"pass 'art_…'
|
|
55
|
+
(dataset/rows) to `compute` — compute the number there and answer with what
|
|
56
|
+
it returns"* — inside the authored frame, so the quoted values still come
|
|
57
|
+
last and the exempt-corpus fence is untouched. The `revision-asked`
|
|
58
|
+
`evidence_checked` event carries the same facts additively (`stagedRefs`,
|
|
59
|
+
`spenderTools`). Absent everything — no gate, `nudge` unset, or no
|
|
60
|
+
`wants`-declaring tool — every request, record and correction keeps its
|
|
61
|
+
exact bytes, pinned by test.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- **A `guard`/`rails` agent that also registers a `wants`-declaring tool will
|
|
66
|
+
see its evidence correction gain the refs clause above when staged refs are
|
|
67
|
+
in context.** That is the fix, not a side effect: a correction that says
|
|
68
|
+
"call the tool that provides it" without naming WHICH tool over WHICH ref
|
|
69
|
+
leaves the model to head-math again. Agents without a `wants` tool — or
|
|
70
|
+
without staged refs in the flagged turn — keep the exact 9.35.0 sentence.
|
|
71
|
+
|
|
72
|
+
## [9.74.0] - 2026-08-27
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- **The Azure/Foundry column exists — keyless auth and both Microsoft inference
|
|
77
|
+
doors, cloud and on-device.** AWS and GCP each had a full adapter column;
|
|
78
|
+
Azure had a static api-key string and nothing else. This release fills the
|
|
79
|
+
auth + inference tier, and every piece is a vendor adapter over ports that
|
|
80
|
+
did not change — the same seams the AWS and GCP columns already use.
|
|
81
|
+
|
|
82
|
+
**`foundry()` — the project-endpoint provider** (`agentfootprint/providers`).
|
|
83
|
+
The JS answer to Microsoft's `FoundryChatClient(project_endpoint, model,
|
|
84
|
+
credential)`: point it at a Foundry project endpoint (or let the hosted
|
|
85
|
+
platform's auto-injected `FOUNDRY_PROJECT_ENDPOINT` supply it), name the
|
|
86
|
+
deployment (`AZURE_AI_MODEL_DEPLOYMENT_NAME ?? MODEL_NAME`), and auth is an
|
|
87
|
+
Entra `TokenCredential`, an api key, or — given neither — the platform's own
|
|
88
|
+
blessed default, `DefaultAzureCredential` from the optional `@azure/identity`
|
|
89
|
+
peer. Inference rides the GA api-version-free `/openai/v1` route derived from
|
|
90
|
+
the project endpoint; the deployment name travels as the `model` field; the
|
|
91
|
+
token scope is `https://ai.azure.com/.default` (the management audience is a
|
|
92
|
+
different token — the docs say so out loud). Rotating tokens reuse the
|
|
93
|
+
per-request key-callback seam `openai()` has had since 9.29.0: the client is
|
|
94
|
+
rebuilt only when the token string actually changes.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { foundry } from 'agentfootprint/providers';
|
|
98
|
+
// In a Foundry hosted container: zero config — endpoint injected, identity ambient.
|
|
99
|
+
const llm = foundry();
|
|
100
|
+
// Locally: az login, then name the project.
|
|
101
|
+
const local = foundry({
|
|
102
|
+
projectEndpoint: 'https://acct.services.ai.azure.com/api/projects/my-project',
|
|
103
|
+
deployment: 'gpt-4.1-mini',
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**`foundryLocal()` — the on-device door.** Fetch-only, zero dependencies, the
|
|
108
|
+
`ollama()` discipline applied to Foundry Local's OpenAI-compatible `/v1`
|
|
109
|
+
wire: an alias like `qwen2.5-0.5b` resolves to a concrete variant through the
|
|
110
|
+
service's own catalog (`GET /foundry/list`, priority order, first wins;
|
|
111
|
+
cached per provider), a full variant id skips the catalog entirely, and the
|
|
112
|
+
typed `FoundryLocalUnavailableError` tells the truth a local runtime needs
|
|
113
|
+
telling — `foundry server start` to start it, `foundry server status` to find
|
|
114
|
+
the dynamic port, `foundry model run <alias>` when the model is the missing
|
|
115
|
+
piece, with the machine's actual model list attached when the service could
|
|
116
|
+
answer. No API key is sent because none exists. Streamed usage is read off
|
|
117
|
+
the final empty-choices frame — the exact bug class 9.73.0 fixed, pinned here
|
|
118
|
+
from day one.
|
|
119
|
+
|
|
120
|
+
**`entraIdentity()` — Azure credentials for tools**
|
|
121
|
+
(`agentfootprint/security`). The `googleIdentity` anatomy, law for law:
|
|
122
|
+
vends the deployment's identity as a bearer via any `TokenCredential`
|
|
123
|
+
(default: `DefaultAzureCredential`'s chain — env service principal, workload
|
|
124
|
+
identity, managed identity, VS Code, az CLI), scopes the request's way or
|
|
125
|
+
`AZURE_AI_SCOPE` by default, refuses `mode: 'user'` and per-request user
|
|
126
|
+
tokens BY NAME until an OBO surface exists, refuses services outside the
|
|
127
|
+
allowlist, and never lets an SDK's failure text — which echoes request
|
|
128
|
+
detail — reach a thrown message. `AZURE_AI_SCOPE` and
|
|
129
|
+
`AZURE_MANAGEMENT_SCOPE` are both exported because the audiences are not
|
|
130
|
+
interchangeable, and pretending there is one "azure scope" would be a lie
|
|
131
|
+
that 401s at runtime.
|
|
132
|
+
|
|
133
|
+
**`azureOpenai({ credential })` — the existing Azure door goes keyless.** An
|
|
134
|
+
Entra credential now rides the SDK's `azureADTokenProvider`; the static
|
|
135
|
+
api-key path is byte-identical to before; both given at once is refused by
|
|
136
|
+
name as the config bug it is. The docs' old caveat — "azureOpenai is the
|
|
137
|
+
wrong door for bearer auth" — is retired. Each keyless door defaults to the
|
|
138
|
+
audience ITS route documents: this one asks for
|
|
139
|
+
`https://cognitiveservices.azure.com/.default` (the classic deployment-scoped
|
|
140
|
+
route's own documented audience, `AZURE_COGNITIVE_SERVICES_SCOPE`), while
|
|
141
|
+
`foundry()` asks for `https://ai.azure.com/.default` (`AZURE_AI_SCOPE`, the
|
|
142
|
+
v1/project route's) — both overridable via `scope`, both pinned on the wire
|
|
143
|
+
by tests that record what the credential was actually asked for.
|
|
144
|
+
|
|
145
|
+
**`openai({ legacyEndpoint })` — the dialect dial goes public.** `baseURL`
|
|
146
|
+
has always implied the legacy dialect (`max_tokens`, no `stream_options`)
|
|
147
|
+
because most OpenAI-compatible servers are behind; `legacyEndpoint: false`
|
|
148
|
+
now declares "this baseURL speaks the current dialect" — which is exactly
|
|
149
|
+
what the Azure v1 route is. Default unchanged: `!!baseURL`.
|
|
150
|
+
|
|
151
|
+
**`providerFromEnv` learns both doors — upgrade-safely.** `FOUNDRY_LOCAL_MODEL`
|
|
152
|
+
slots directly after `OLLAMA_MODEL` (a model name you typed for a local
|
|
153
|
+
runtime — the same name-beats-leftover-credential law), and
|
|
154
|
+
`FOUNDRY_PROJECT_ENDPOINT` + `AZURE_AI_MODEL_DEPLOYMENT_NAME` — a pair of
|
|
155
|
+
product-specific spellings nobody exports by accident — outranks the
|
|
156
|
+
lingering-credential arms. Two guards keep an upgrade from breaking a
|
|
157
|
+
working environment, because the hosted platform auto-injects the endpoint
|
|
158
|
+
into every container, Foundry-bound or not: an endpoint with **no**
|
|
159
|
+
deployment named HOLDS its refusal (every arm below answers exactly as it
|
|
160
|
+
did before this arm existed, and the held refusal is raised only when
|
|
161
|
+
nothing else resolves), and the generic `MODEL_NAME` alone never carries the
|
|
162
|
+
endpoint past a bootable Azure config — choosing Foundry over working Azure
|
|
163
|
+
takes the deliberate spelling. Every existing precedence is pinned
|
|
164
|
+
untouched; each guard has its own test.
|
|
165
|
+
|
|
166
|
+
New from `agentfootprint/providers`: `foundry`, `foundryInferenceUrl`,
|
|
167
|
+
`FoundryProviderOptions`, `foundryLocal`, `FoundryLocalProvider`,
|
|
168
|
+
`FoundryLocalUnavailableError`, `FoundryLocalProviderOptions`,
|
|
169
|
+
`TokenCredentialLike`, `AccessTokenLike`. New from `agentfootprint/security`:
|
|
170
|
+
`entraIdentity`, `AZURE_AI_SCOPE`, `AZURE_MANAGEMENT_SCOPE`,
|
|
171
|
+
`AZURE_COGNITIVE_SERVICES_SCOPE`, `EntraIdentityOptions`,
|
|
172
|
+
`TokenCredentialLike`, `AccessTokenLike`, `AzureIdentitySdkModule`. New
|
|
173
|
+
optional peer: `@azure/identity`. Pinned by 178 new test cases (counted in
|
|
174
|
+
the diff, not the runner) across
|
|
175
|
+
`test/adapters/identity/entra-identity.test.ts`,
|
|
176
|
+
`test/adapters/unit/FoundryProvider.test.ts`,
|
|
177
|
+
`test/adapters/unit/FoundryLocalProvider.test.ts`,
|
|
178
|
+
`test/adapters/integration/foundry-wire.test.ts` (a real `openai` SDK against
|
|
179
|
+
a local fake of the `/openai/v1` wire — Bearer header, `max_completion_tokens`,
|
|
180
|
+
token rotation, and the audience each door asks its credential for, all
|
|
181
|
+
asserted on the wire, not assumed), the extended Azure env/wire suites, and
|
|
182
|
+
the fix pins from the adversarial review below. Deliberately NOT in this train: the App Insights sink,
|
|
183
|
+
Azure AI Search, the Toolbox MCP transport (next trains), and every
|
|
184
|
+
preview-only surface (A2A door, browser/computer-use, managed memory) —
|
|
185
|
+
refusing to ship against previews we cannot verify is a feature.
|
|
186
|
+
|
|
187
|
+
The whole train was adversarially reviewed before release (five lenses, every
|
|
188
|
+
serious finding independently re-verified with a reproduction): 1 blocker and
|
|
189
|
+
9 should-fix findings were confirmed and every one is fixed and pinned in
|
|
190
|
+
this release — including a mid-stream failure frame that `foundryLocal()`
|
|
191
|
+
would have reported as a clean stop, an abort signal that never reached a
|
|
192
|
+
streaming body, and an env-detection arm that would have broken working
|
|
193
|
+
deployments on upgrade.
|
|
194
|
+
|
|
195
|
+
### Fixed
|
|
196
|
+
|
|
197
|
+
- **`ollama()` inherited four stream/abort defects — found by reviewing the new
|
|
198
|
+
Foundry Local adapter, fixed at the root.** The adversarial review of
|
|
199
|
+
`foundryLocal()` proved its four streaming defects were byte-twin shapes
|
|
200
|
+
copied from `OllamaProvider`, which had shipped them for months: a caller's
|
|
201
|
+
already-aborted `AbortSignal` still sent the request; an abort after headers
|
|
202
|
+
never reached the streaming body (generation ran on, un-stoppable); an early
|
|
203
|
+
`break` leaked the response body (the reader was never cancelled); and a
|
|
204
|
+
mid-stream `{"error": …}` frame was silently dropped, reporting a failed
|
|
205
|
+
generation as a clean stop. All four now match the fixed Foundry Local
|
|
206
|
+
shapes — same helper names, Ollama's NDJSON wire. 10 of the 12 new pinning
|
|
207
|
+
tests fail against the previous source (proven by restoring it); no public
|
|
208
|
+
API change; `OllamaUnavailableError` is byte-identical.
|
|
209
|
+
|
|
210
|
+
|
|
10
211
|
## [9.73.0] - 2026-08-27
|
|
11
212
|
|
|
12
213
|
### Fixed
|
package/CLAUDE.md
CHANGED
|
@@ -47,6 +47,7 @@ not in this table, search `src/index.ts` for the nearest noun before writing cod
|
|
|
47
47
|
| a value that must say how it knows itself — an unknown count that can never render as zero | `Claim<T>` (`known`/`unknown`/`notApplicable`) | `src/lib/claim/claim.ts` | 9.58.0 |
|
|
48
48
|
| asking a debugging model "what did this run contradict itself about, and why?" — the Context Integrity findings, joined to the step that filed them | `find_context_errors` | `src/lib/trace-toolpack/traceToolpack.ts` | 9.61.0 |
|
|
49
49
|
| counting a HUMAN-VERIFIED value as ground for the choice-seam check — the person clicked a row, the app verified the cells against the artifact, and the id the model takes from that selection is not fabricated; the source label travels onto the record | `externalGrounds` + `ExternalGround` + `external_ground_used` | `src/integrity/unsupported-argument/check.ts` | 9.72.0 |
|
|
50
|
+
| a model head-mathing a total from tool-result numbers while a compute tool sits unused on the wire — a LATE line each iteration naming the staged refs and the `wants` tool that spends them (recency working FOR the instruction), plus the revise correction naming the same route | `nudge: true` + `stagedRefsNudgeLine` + `findStagedRefs` + `grounding_nudged` | `src/core/agent/stagedRefs.ts` | 9.75.0 |
|
|
50
51
|
| a tool answering "I looked and found nothing", routably | `absent` | `src/core/agent/coverage/absent.ts` | 9.43.0 |
|
|
51
52
|
| stating what a clean answer does NOT rule out | `coverage` | `src/core/agent/coverage/ledger.ts` | 9.43.0 |
|
|
52
53
|
| minting one of those shapes from a tool that is NOT JavaScript — the canonical note sentences and reserved marker keys as DATA, so a Python/Go/Rust sidecar reads a file instead of regex-scraping `dist/esm` (which a consumer really did) | `canonical-notes.json` at the package root + the `./canonical-notes.json` exports entry, GENERATED from the built barrel by `scripts/gen-canonical-notes.mjs` | `scripts/gen-canonical-notes.mjs` | 9.70.0 |
|
|
@@ -133,7 +134,7 @@ Traps: `src/observability/` holds the finder IMPLEMENTATIONS (canonical home; `d
|
|
|
133
134
|
- **Recordings as artifacts (9.26.0)**: `artifacts: { store, recordings: true | { label } }`. `Agent.startRunRecording()` calls the SAME `recordRun` (before `createExecutor` — `attach()` collects for the executor not yet built); `fileRunRecording` mints AFTER `finalizeResult`, awaited, every failure contained to `artifacts.refused`. Pure half in artifacts/recordingArtifact.ts; payload is the recording's JSON TEXT (a live snapshot handed to an in-process store would be a live view into a finished run). No new wire op — `artifact-get` serves it.
|
|
134
135
|
- **Code staging-in (9.26.0)**: `CodeSession.stageInputs?(inputs) → StagedCodeInput[]` — OPTIONAL, feature-detected via `canStageCodeInputs`, and its contract is TWO promises: the payloads are readable at the returned paths, AND every later `execute` exposes the manifest as `STAGED_INPUTS_ENV` (`AF_STAGED_INPUTS`, `name → path`). `CodeInput.name` is the MANIFEST KEY (the wants arg name, so a static description can name it) and `fileName` is the on-disk name — separate fields so the two cannot drift. `codeRunnerTool({ wants })` composes the schema properties + the description clause and refuses BY NAME on a non-staging runner. Implemented by localCodeRunner only.
|
|
135
136
|
- **Repeated-call nudge (9.26.0)**: `core/agent/repeatedCall.ts` (pure `noteRepeatedCall` + `repeatedCallLedgers()`) + the ONE batch-loop hook in toolCalls. Fingerprints (FNV-1a) of stable-stringified args and the tool's OWN delivered result — never values. **The counters are NOT tracked state**: they live in a bounded run-keyed map held by `buildToolCallsHandler` and keyed by `deps.currentRun().runId`, so a turn that repeats nothing is byte-identical in state, commit log, narrative and recordings (a scope key would have changed all four for every agent that merely upgraded); the repeat itself rides `agentfootprint.tools.repeated_call`, the emit channel per-attempt facts belong on. A resume mints a new runId ⇒ counting restarts. Fires at the SECOND identical landing, once. `AgentOptions.repeatedCallNudge: false` disables (threaded value-conditionally; ON is the default). Deliberately NOT applied on the pause-resume dispatch paths.
|
|
136
|
-
- **Evidence gate (9.35.0)** — `.namesAndNumbersFromEvidence({ posture, shapes, exempt, minDigits })`: every name/number in the final answer must appear in a `role:'tool'` result. Postures reuse the routing VOCABULARY (`assist`|`guard`|`rails`) as a SEPARATE option — routing authority ≠ evidence discipline, and overloading `skillGraphCascade.strictness` would deny "strict routing, loose evidence". Wiring is the stepNudge blast radius verbatim: `ResolvedEvidenceGate` (builder-resolved, refusals at the CALL SITE) → Agent ctor trailing param → `buildRouteDeciderStage`'s 4th arg (`judgeEvidence` runs LAST of the three judges — schema > steps > evidence — and NOT on a denied or schema-exhausted answer) → `evidenceRecheckStage` branch (`{loopTo}`, mounted only for a revising posture) → `STAGE_IDS.EVIDENCE_RECHECK` + BOUNDARY_LOCAL_IDS + milestoneFor. TWO deps flags, and the second is the one a reader misses: `evidenceRecheckStage` (branch) AND `hasEvidenceGate` (bubbles `systemPromptInjections` out of sf-llm-call in the GROUPED chart — without it the gate flags the app's own prompt). Per-check facts ride `agentfootprint.agent.evidence_checked` (emit channel); only the terminal verdict is committed (`unsupportedValues`), because the boundary raises off it. `UnsupportedValuesError` joins the TERMINAL-typed-error list in `run()`'s catch (a verdict is not a crash — no retry handle for a wall).
|
|
137
|
+
- **Evidence gate (9.35.0)** — `.namesAndNumbersFromEvidence({ posture, shapes, exempt, minDigits })`: every name/number in the final answer must appear in a `role:'tool'` result. Postures reuse the routing VOCABULARY (`assist`|`guard`|`rails`) as a SEPARATE option — routing authority ≠ evidence discipline, and overloading `skillGraphCascade.strictness` would deny "strict routing, loose evidence". Wiring is the stepNudge blast radius verbatim: `ResolvedEvidenceGate` (builder-resolved, refusals at the CALL SITE) → Agent ctor trailing param → `buildRouteDeciderStage`'s 4th arg (`judgeEvidence` runs LAST of the three judges — schema > steps > evidence — and NOT on a denied or schema-exhausted answer) → `evidenceRecheckStage` branch (`{loopTo}`, mounted only for a revising posture) → `STAGE_IDS.EVIDENCE_RECHECK` + BOUNDARY_LOCAL_IDS + milestoneFor. TWO deps flags, and the second is the one a reader misses: `evidenceRecheckStage` (branch) AND `hasEvidenceGate` (bubbles `systemPromptInjections` out of sf-llm-call in the GROUPED chart — without it the gate flags the app's own prompt). Per-check facts ride `agentfootprint.agent.evidence_checked` (emit channel); only the terminal verdict is committed (`unsupportedValues`), because the boundary raises off it. `UnsupportedValuesError` joins the TERMINAL-typed-error list in `run()`'s catch (a verdict is not a crash — no retry handle for a wall). **Grounded numbers (9.75.0)** rides the SAME dial: `nudge: true` arms the staged-refs nudge — `toolWantsOf` harvests `Tool.wants` beside the `toolGrounding` harvest in `Agent.buildChart` (same ToolProvider blindness), callLLM appends ONE request-only late line when a placed ticket's kind matches a SERVED `wants` tool (judged on `registeredToolSchemas`, so wrap-up's withheld surface arms nothing; history untouched ⇒ never in the exempt corpus), recorded as `agent.grounding_nudged`; and the recheck correction names the same refs+spender via `buildEvidenceCorrection`'s third arg — threaded whenever a `wants` tool exists, NOT gated on `nudge`, clause INSIDE the authored frame so `isLibraryAuthoredTurn`'s prefix match and values-last both hold.
|
|
137
138
|
- **Coverage primitives (this release)** — `absent()` / `coverage()`, both copied from FIELD USE. THE argument is the direction of the error: a *nothing-found* misread as an *outage* costs an investigation, an *outage* misread as *nothing-found* declares a system healthy that was never checked — so the two must not share a shape. Blast radius, and it is the `resultCeiling` radius verbatim: `readCoverageResult` called by `declareCoverage` at BOTH execute boundaries in toolCalls.ts (batch loop + `resolveCredentialAndExecute`), on the UNWRAPPED content and BEFORE the ceiling. FOUR downstream changes and no more: (1) `ToolResultStatus` gained a SEVENTH word `'absent'` — routable by `onToolStatus`, because folding it into `'failure'` is the confusion itself and into `'success'` leaves nothing to route on; (2) two events (`tools.absent`, `tools.coverage_declared`); (3) tracked `AgentState.coverageDeclared` — a limit is a fact about the ANSWER, not about an attempt, which is why it is state and the repeated-call counters are not; (4) the evidence corpus indexes an absence's COVERAGE ONLY (`coverage/evidence.ts` — a failed lookup is the cheapest laundering machine, and `absent()` would have made it cheaper: this is frames.ts's argument on the tool side). Deliberately UNCHANGED: no `error: true`, no retry, no refusal, no gate flag. Survival into the answer is `.limitsTravelWithTheAnswer()` → `attachCoverageLimits` dep → BOTH builders swap the final branch's first stage for `prepareFinalWithLimitsStage` (same id, same position). It APPENDS rather than judges: a check for "did the model state its limits?" needs a second model to decide what counts, which is what evidence/README.md forbids.
|
|
138
139
|
- **Out-of-budget wrap-up (9.56.0)** — `wrapUpAtMaxIterations` (AgentOptions, default ON, `repeatedCallNudge`'s opt-out grammar): the FOURTH Route branch, and the SchemaRetry mechanism verbatim — `STAGE_IDS.WRAP_UP` + same `{loopTo}`, so the last call is one ordinary turn with its own `iteration_start`/`llm_start`/`cost.tick`. Two things are its own: (1) the tools are WITHHELD at REQUEST ASSEMBLY in callLLM (`scope.wrapUpAsked`, the `schemaTool` seam's mirror — the schema tool still rides, so an output contract survives), which is what makes the call terminal BY CONSTRUCTION rather than by a rule, and is why it is exempt from `maxIterations`; (2) the CONDITIONAL MOUNT is on the agent having a TOOL SURFACE (`registryByName.size > 0 || externalToolProvider`) — a limit only cuts a turn short when tool calls were pending, so a toolless agent's chart must not grow a box that can never run. `decideBranch` treats a spent wrap-up as cut-short for every downstream judge (`toolCalls.length > 0 || scope.wrapUpAsked`), or a step nudge / evidence revision would loop past the limit that fired. `wrapUpAsked` is deliberately NOT seeded — a turn that finishes inside its budget commits the exact key set it always did. The record is FOUR channels: `stoppedEarly` (now with `wrappedUp`, corrected on the pass after so `answerWasEmpty` describes the answer the caller GOT), `cost.limit_hit` (unchanged), the new `agent.budget_exhausted {action: 'wrapped-up'|'cut-short'}`, and an optional `turn_end.stoppedEarly` projection.
|
|
139
140
|
- **Closed seams**: Agent chart internals (AgentChartDeps not exported — extend via injections/tools/memory/thinking, never by adding a ReAct stage); ContextSlot (3 slots fixed); ProviderKind factory; dormant ports with no consumer (ContextSourceAdapter, EmbeddingProvider, RiskDetector — adapters/types.ts only); reserved tool names under selfExplain — 8.16.0 made the list DERIVED: `TRACE_TOOL_NAMES` (traceToolpack.ts, 11 names since 9.61.0: run_overview/find_context_errors/find_in_trace/trace_node/trace_slice/backtrack/who_wrote/get_value/inspect_tool_call/inspect_tool_run/read_narrative) is what AgentBuilder.ts:~1560 reserves in inline mode (`explain_run` in delegate mode), so a NEW toolpack tool joins ONE list and the reservation follows — but it must ALSO join the lazy template's mounted set (lazyToolpack builds over `{narrative: [], events: []}` so the catalog shape is fixed at build time) and the count assertion in test/lib/trace-toolpack/selfExplainAgent.test.ts. **8.17.0 `inspect_tool_run`** is the descent THROUGH the tool boundary: `flowchartAsTool({ keepRecord: true })` files each invocation's record in a bounded LRU store (lib/trace-toolpack/innerRunRecords.ts) keyed by the executing `ctx.toolCallId`, riding the `Tool` under the `INNER_RUN_RECORDS` registry symbol; `AgentBuilder.build()` → `collectInnerRuns(registry, injections)` → `SelfExplainSource.getInnerRuns` → `TraceToolpackArtifacts.innerRuns`. The inner views are the pack ITSELF re-run over `openRecording(record.recording)` — do not add a second query implementation. Provider-delivered tools are NOT collected (no build-time list); inner runtimeStageIds are a SEPARATE namespace and only `inspect_tool_run` accepts them. **9.61.0 `find_context_errors`** is the Context Integrity read-out: it reads `agentfootprint.integrity.context_error` + `…disposition` off the artifacts' EVENT TAIL (never re-running a check), joins each finding to the step it was filed at, and mounts UNCONDITIONALLY — a tool that vanished with the tail could not say the evidence channel is ABSENT, which is the one sentence this family forbids collapsing into "no errors found".
|
|
@@ -145,7 +146,7 @@ Traps: `src/observability/` holds the finder IMPLEMENTATIONS (canonical home; `d
|
|
|
145
146
|
- **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`.
|
|
146
147
|
- **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.
|
|
147
148
|
- **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).
|
|
148
|
-
- **events/** →
|
|
149
|
+
- **events/** → 109 typed events across 24 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).
|
|
149
150
|
- **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.
|
|
150
151
|
- **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.
|
|
151
152
|
- **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).
|
|
@@ -252,7 +252,7 @@ const agent = Agent.create({ provider, model })
|
|
|
252
252
|
agent.on('agentfootprint.context.evaluated', (e) => console.log(e.payload.activeIds));
|
|
253
253
|
```
|
|
254
254
|
|
|
255
|
-
**
|
|
255
|
+
**109 typed events across 24 domains.** Two subscription shapes and no third:
|
|
256
256
|
`'*'` (every event) and `'agentfootprint.<domain>.*'` (one domain). **`'agentfootprint.*'`
|
|
257
257
|
is not a pattern** — TypeScript rejects it, and at runtime it would match nothing.
|
|
258
258
|
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* entraIdentity — the {@link CredentialProvider} port over Microsoft Entra ID
|
|
4
|
+
* (peer-dep `@azure/identity`).
|
|
5
|
+
*
|
|
6
|
+
* import { entraIdentity } from 'agentfootprint/security';
|
|
7
|
+
* const credentials = entraIdentity();
|
|
8
|
+
*
|
|
9
|
+
* ── What it is, and what it deliberately is not ─────────────────────────────
|
|
10
|
+
* This is the **narrow** adapter: it vends *Entra* access tokens for *Azure*
|
|
11
|
+
* APIs, from whatever credential the environment already has — the
|
|
12
|
+
* DefaultAzureCredential chain walks environment service principal, workload
|
|
13
|
+
* identity, managed identity, VS Code, Azure CLI, Azure PowerShell and the
|
|
14
|
+
* Azure Developer CLI, in that order. That is one job and it is done
|
|
15
|
+
* completely.
|
|
16
|
+
*
|
|
17
|
+
* It is **not** a user-delegation surface. Entra's on-behalf-of flow (and any
|
|
18
|
+
* 3-legged consent dance) needs a confidential client app registration that
|
|
19
|
+
* this adapter does not hold, so `mode: 'user'` is **refused by name** rather
|
|
20
|
+
* than quietly served with a machine token. A machine token returned where a
|
|
21
|
+
* user token was asked for is the exact silent downgrade the port exists to
|
|
22
|
+
* prevent: the call succeeds, the data comes back, and it was the agent's
|
|
23
|
+
* access rather than the person's. OBO is a later train; when it lands it will
|
|
24
|
+
* be its own provider, not a flag here.
|
|
25
|
+
*
|
|
26
|
+
* ── The audience split, and where it bites ──────────────────────────────────
|
|
27
|
+
* Azure tokens are minted for ONE audience. {@link AZURE_AI_SCOPE}
|
|
28
|
+
* (`https://ai.azure.com/.default`) is the data plane — every Foundry and
|
|
29
|
+
* Azure OpenAI inference call takes it. {@link AZURE_MANAGEMENT_SCOPE}
|
|
30
|
+
* (`https://management.azure.com/.default`) is the ARM control plane —
|
|
31
|
+
* listing deployments, creating resources. A token for one audience is a 401
|
|
32
|
+
* on the other, which is why BOTH are exported by name instead of leaving the
|
|
33
|
+
* caller to guess a string. The default here is the data-plane scope, because
|
|
34
|
+
* vending inference credentials is what an agent runtime does all day.
|
|
35
|
+
*
|
|
36
|
+
* ── Caching: the credential, never a token ──────────────────────────────────
|
|
37
|
+
* One `DefaultAzureCredential` is constructed for the life of the provider and
|
|
38
|
+
* every `getToken` call goes through it. MSAL — the machinery underneath
|
|
39
|
+
* `@azure/identity` — caches and proactively refreshes tokens internally, so
|
|
40
|
+
* caching a token HERE would mean owning an expiry this adapter did not
|
|
41
|
+
* compute and cannot see revoked. Unlike the Google adapter (where scopes are
|
|
42
|
+
* fixed at client construction and a different scope set needs its own
|
|
43
|
+
* client), Azure scopes travel per `getToken` call, so the ONE cached
|
|
44
|
+
* credential serves every scope set.
|
|
45
|
+
*
|
|
46
|
+
* ── Secrets ─────────────────────────────────────────────────────────────────
|
|
47
|
+
* The `sdkFailure` law, same as every other credential-touching adapter here:
|
|
48
|
+
* the library's own message never comes through, because auth libraries echo
|
|
49
|
+
* request detail into 401/403 text and a message thrown from a
|
|
50
|
+
* `CredentialProvider` reaches the LLM as a tool result AND rides
|
|
51
|
+
* `agentfootprint.credential.failed` to every sink. What comes through is the
|
|
52
|
+
* operation that failed and the error's NAME. The original is not attached as
|
|
53
|
+
* `cause` — a cause travels into every serializer that walks own properties,
|
|
54
|
+
* which would undo all of it in one `JSON.stringify`.
|
|
55
|
+
*
|
|
56
|
+
* Pattern: Adapter (GoF) + lazy peer-dep load — `@azure/identity` is required
|
|
57
|
+
* the first time `getCredential` runs, or never if you inject a credential.
|
|
58
|
+
*/
|
|
59
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
60
|
+
exports.entraIdentity = exports.AZURE_COGNITIVE_SERVICES_SCOPE = exports.AZURE_MANAGEMENT_SCOPE = exports.AZURE_AI_SCOPE = void 0;
|
|
61
|
+
const lazyRequire_js_1 = require("../../lib/lazyRequire.js");
|
|
62
|
+
const kinds_js_1 = require("../../identity/kinds.js");
|
|
63
|
+
const ADAPTER = 'entraIdentity';
|
|
64
|
+
/**
|
|
65
|
+
* The data-plane scope for ALL Foundry / Azure OpenAI inference
|
|
66
|
+
* (`https://ai.azure.com/.default`). This is the default scope this provider
|
|
67
|
+
* requests.
|
|
68
|
+
*
|
|
69
|
+
* The audience split matters: a token minted for this scope does NOT work on
|
|
70
|
+
* the ARM control plane, and a {@link AZURE_MANAGEMENT_SCOPE} token does not
|
|
71
|
+
* work here — Azure validates the audience on every call. Both are exported by
|
|
72
|
+
* name so nobody has to remember which string is which.
|
|
73
|
+
*/
|
|
74
|
+
exports.AZURE_AI_SCOPE = 'https://ai.azure.com/.default';
|
|
75
|
+
/**
|
|
76
|
+
* The ARM control-plane scope (`https://management.azure.com/.default`) —
|
|
77
|
+
* listing deployments, managing resources. A DIFFERENT audience from
|
|
78
|
+
* {@link AZURE_AI_SCOPE}: a token for one is a 401 on the other, which is why
|
|
79
|
+
* both are named rather than leaving the caller to guess.
|
|
80
|
+
*/
|
|
81
|
+
exports.AZURE_MANAGEMENT_SCOPE = 'https://management.azure.com/.default';
|
|
82
|
+
/**
|
|
83
|
+
* The CLASSIC Azure OpenAI data-plane scope
|
|
84
|
+
* (`https://cognitiveservices.azure.com/.default`) — the audience Microsoft's
|
|
85
|
+
* own keyless guidance names for the older deployment-scoped route
|
|
86
|
+
* (`{endpoint}/openai/deployments/{d}/…`), which is the route `azureOpenai()`
|
|
87
|
+
* builds and therefore its default. Current resources widely accept
|
|
88
|
+
* {@link AZURE_AI_SCOPE} too, but an older `*.openai.azure.com` resource may
|
|
89
|
+
* not — and a door should default to the audience ITS route documents, not the
|
|
90
|
+
* one its sibling uses. (Azure Government spells this
|
|
91
|
+
* `https://cognitiveservices.azure.us/.default`.)
|
|
92
|
+
*/
|
|
93
|
+
exports.AZURE_COGNITIVE_SERVICES_SCOPE = 'https://cognitiveservices.azure.com/.default';
|
|
94
|
+
/**
|
|
95
|
+
* Vend Entra access tokens from whatever credential this environment has —
|
|
96
|
+
* the DefaultAzureCredential chain: environment service principal, workload
|
|
97
|
+
* identity, managed identity, VS Code, Azure CLI, Azure PowerShell, Azure
|
|
98
|
+
* Developer CLI. (`AZURE_TOKEN_CREDENTIALS` can restrict the chain; that is
|
|
99
|
+
* the SDK's own dial and this adapter does not second-guess it.)
|
|
100
|
+
*
|
|
101
|
+
* @throws when `mode: 'user'` is requested — no user-delegation surface is
|
|
102
|
+
* wired for Entra yet (on-behalf-of is a later train), and a machine token
|
|
103
|
+
* returned in its place would be a silent downgrade.
|
|
104
|
+
* @throws when `services` is configured and the request names another one.
|
|
105
|
+
*
|
|
106
|
+
* @example A tool that calls an Azure API with the deployment's own identity
|
|
107
|
+
* const agent = Agent.create({ provider, credentials: entraIdentity() })
|
|
108
|
+
* .tool(defineTool({
|
|
109
|
+
* name: 'ask_foundry',
|
|
110
|
+
* needs: [{ credential: 'azure-ai' }],
|
|
111
|
+
* execute: async (args, ctx) =>
|
|
112
|
+
* fetch(url, { headers: ctx.credential!.toHeaders() }).then((r) => r.text()),
|
|
113
|
+
* }))
|
|
114
|
+
* .build();
|
|
115
|
+
*
|
|
116
|
+
* @example A control-plane token, without touching the data-plane default
|
|
117
|
+
* entraIdentity({ scopes: [AZURE_MANAGEMENT_SCOPE] });
|
|
118
|
+
*/
|
|
119
|
+
function entraIdentity(options = {}) {
|
|
120
|
+
const defaultScopes = options.scopes ?? [exports.AZURE_AI_SCOPE];
|
|
121
|
+
const allowed = options.services === undefined ? undefined : new Set(options.services);
|
|
122
|
+
const cache = {};
|
|
123
|
+
const resolveCredential = () => {
|
|
124
|
+
if (options._credential)
|
|
125
|
+
return options._credential;
|
|
126
|
+
// ONE credential for the life of the provider — scopes ride on getToken
|
|
127
|
+
// per call (unlike Google, where they are fixed at client construction),
|
|
128
|
+
// so no per-scope keying is needed. MSAL underneath caches and refreshes
|
|
129
|
+
// tokens on its own; we cache the CREDENTIAL, never a token.
|
|
130
|
+
if (cache.credential)
|
|
131
|
+
return cache.credential;
|
|
132
|
+
const mod = loadIdentitySdk(options._sdk);
|
|
133
|
+
if (typeof mod.DefaultAzureCredential !== 'function') {
|
|
134
|
+
throw new Error(`${ADAPTER}: \`@azure/identity\` is installed but exports no ` +
|
|
135
|
+
`\`DefaultAzureCredential\`. This adapter is built against the 4.x package — ` +
|
|
136
|
+
`update it, or pass \`_credential\`.`);
|
|
137
|
+
}
|
|
138
|
+
cache.credential = new mod.DefaultAzureCredential();
|
|
139
|
+
return cache.credential;
|
|
140
|
+
};
|
|
141
|
+
return {
|
|
142
|
+
id: options.id ?? 'entra-identity',
|
|
143
|
+
async getCredential(req) {
|
|
144
|
+
if (req.mode === 'user') {
|
|
145
|
+
throw new Error(`${ADAPTER}: a \`mode: 'user'\` request arrived for '${req.service}', and this ` +
|
|
146
|
+
`provider cannot serve one.\n` +
|
|
147
|
+
` It vends the DEPLOYMENT's Entra credential (the DefaultAzureCredential ` +
|
|
148
|
+
`chain — environment service principal, workload identity, managed identity, ` +
|
|
149
|
+
`or a developer's \`az login\`). No user-delegation surface is wired for Entra ` +
|
|
150
|
+
`yet — the on-behalf-of flow is a later train.\n` +
|
|
151
|
+
` Returning a machine token here would succeed and be wrong: the call would ` +
|
|
152
|
+
`run with the AGENT's access rather than the person's, and nothing downstream ` +
|
|
153
|
+
`could tell.\n` +
|
|
154
|
+
` Fix: declare \`mode: 'machine'\` if the deployment's own identity is really ` +
|
|
155
|
+
`what you want, or vend the user's token from a provider that holds one.`);
|
|
156
|
+
}
|
|
157
|
+
if (req.userToken !== undefined) {
|
|
158
|
+
// A user's signed token handed to a provider that cannot exchange it.
|
|
159
|
+
// Named rather than ignored: without a confidential client app
|
|
160
|
+
// registration there is no on-behalf-of exchange to perform, and
|
|
161
|
+
// silently dropping somebody's proof and vending machine access is
|
|
162
|
+
// the same downgrade in a quieter costume.
|
|
163
|
+
throw new Error(`${ADAPTER}: a \`userToken\` arrived for '${req.service}', but this provider has ` +
|
|
164
|
+
`nothing to exchange it against — the on-behalf-of flow needs a confidential ` +
|
|
165
|
+
`client app registration, and this provider vends the deployment's own Entra ` +
|
|
166
|
+
`credential.\n` +
|
|
167
|
+
` Ignoring it would hand back agent-scoped access while holding the user's ` +
|
|
168
|
+
`proof. Drop the token, or use a provider that can exchange one.`);
|
|
169
|
+
}
|
|
170
|
+
if (allowed !== undefined && !allowed.has(req.service)) {
|
|
171
|
+
throw new Error(`${ADAPTER}: this provider is configured for [${[...allowed].join(', ')}] and was ` +
|
|
172
|
+
`asked for '${req.service}'.\n` +
|
|
173
|
+
` It vends ENTRA access tokens; handing one to a tool that wanted a different ` +
|
|
174
|
+
`service's credential would fail downstream as a puzzling 401 instead of here ` +
|
|
175
|
+
`as a wiring error.\n` +
|
|
176
|
+
` Fix: add '${req.service}' to 'services', or attach a provider that serves it.`);
|
|
177
|
+
}
|
|
178
|
+
let credential;
|
|
179
|
+
try {
|
|
180
|
+
credential = resolveCredential();
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
// A refusal this adapter authored (a missing peer dependency, a
|
|
184
|
+
// too-old SDK) is already the right diagnosis; rewriting it through
|
|
185
|
+
// sdkFailure would send the reader chasing Entra sign-in logs for a
|
|
186
|
+
// problem `npm install` fixes. Wrong diagnoses are their own kind of
|
|
187
|
+
// silently-wrong.
|
|
188
|
+
if (isOwnRefusal(err))
|
|
189
|
+
throw err;
|
|
190
|
+
throw sdkFailure('new DefaultAzureCredential', err);
|
|
191
|
+
}
|
|
192
|
+
// A request's own non-empty scopes win over the provider's default.
|
|
193
|
+
// The array goes to getToken as-is — Azure scopes are per-call, and
|
|
194
|
+
// `.default` scopes are single-element by convention anyway.
|
|
195
|
+
const scopes = req.scopes !== undefined && req.scopes.length > 0 ? req.scopes : defaultScopes;
|
|
196
|
+
let answer;
|
|
197
|
+
try {
|
|
198
|
+
answer = await credential.getToken(scopes);
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
throw sdkFailure('getToken', err);
|
|
202
|
+
}
|
|
203
|
+
const token = answer?.token;
|
|
204
|
+
if (typeof token !== 'string' || token.trim() === '') {
|
|
205
|
+
// The SCOPES are quoted; nothing else is. An audience URI is public,
|
|
206
|
+
// and it is the datum most often wrong here — "could not mint for the
|
|
207
|
+
// requested scope" is useless when the reader cannot see WHICH scope
|
|
208
|
+
// was requested (a tool that declares `needs` without `scopes` gets
|
|
209
|
+
// this provider's default, which it never typed anywhere). The token
|
|
210
|
+
// response's own fields stay withheld: every one of them is a secret.
|
|
211
|
+
// Same conclusion `entraBearerToken` reached out loud in
|
|
212
|
+
// src/adapters/llm/OpenAIProvider.ts — the two siblings now agree.
|
|
213
|
+
throw new Error(`${ADAPTER}: the credential resolved but vended no access token for ` +
|
|
214
|
+
`'${req.service}' at scope${scopes.length === 1 ? '' : 's'} ` +
|
|
215
|
+
`[${scopes.join(', ')}] — ${answer === null
|
|
216
|
+
? '`getToken` returned null, the SDK\'s spelling of "no token available"'
|
|
217
|
+
: "the response's `token` field was empty"}.\n` +
|
|
218
|
+
` No token value is quoted here on purpose — every field of a token response is ` +
|
|
219
|
+
`a secret. This usually means the chain found a credential source that could not ` +
|
|
220
|
+
`actually mint for THAT audience.\n` +
|
|
221
|
+
` Fix: check the audience first — inference is ${exports.AZURE_AI_SCOPE} and the ARM ` +
|
|
222
|
+
`control plane is ${exports.AZURE_MANAGEMENT_SCOPE}; name the right one in the request's ` +
|
|
223
|
+
`\`scopes\` or this provider's \`scopes\` option. If it is already right, sign in as ` +
|
|
224
|
+
`an identity that can mint for it (\`az login\`, or a managed identity with the role).`);
|
|
225
|
+
}
|
|
226
|
+
// The SDK records expiry in unix MILLISECONDS; the port reports unix
|
|
227
|
+
// SECONDS. Reported when known and omitted when not — an invented
|
|
228
|
+
// expiry is worse than none, because a caller would cache against it.
|
|
229
|
+
// (`answer` cannot be null past the token guard; the optional chain is
|
|
230
|
+
// for the compiler, which does not carry the narrowing across fields.)
|
|
231
|
+
const expiryMs = answer?.expiresOnTimestamp;
|
|
232
|
+
const expiresAt = typeof expiryMs === 'number' && Number.isFinite(expiryMs) && expiryMs > 0
|
|
233
|
+
? Math.floor(expiryMs / 1000)
|
|
234
|
+
: undefined;
|
|
235
|
+
return {
|
|
236
|
+
status: 'issued',
|
|
237
|
+
credential: (0, kinds_js_1.bearer)(token),
|
|
238
|
+
...(expiresAt !== undefined && { expiresAt }),
|
|
239
|
+
};
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
exports.entraIdentity = entraIdentity;
|
|
244
|
+
// ─── Internals ───────────────────────────────────────────────────────
|
|
245
|
+
function loadIdentitySdk(injected) {
|
|
246
|
+
if (injected)
|
|
247
|
+
return injected;
|
|
248
|
+
try {
|
|
249
|
+
return (0, lazyRequire_js_1.lazyRequire)('@azure/identity');
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
throw new Error(`${ADAPTER} requires the \`@azure/identity\` peer dependency.\n` +
|
|
253
|
+
` Install: npm install @azure/identity\n` +
|
|
254
|
+
` It is optional and loaded only when this provider first vends, so nothing else ` +
|
|
255
|
+
`in this library pays for it.`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* "There is no usable credential here" — the most common real failure, given
|
|
260
|
+
* the fix instead of the library's own text. `@azure/identity` names it
|
|
261
|
+
* `CredentialUnavailableError` (one rung) or `AggregateAuthenticationError`
|
|
262
|
+
* (the whole chain came up empty); both mean the same thing to the reader.
|
|
263
|
+
*/
|
|
264
|
+
function credentialsUnavailable(name) {
|
|
265
|
+
const failure = new Error(`${ADAPTER}: could not acquire an Entra token in this environment — ${name}.\n` +
|
|
266
|
+
` The underlying message is withheld: auth libraries echo request detail into ` +
|
|
267
|
+
`failure text, and this message reaches the model as a tool result.\n` +
|
|
268
|
+
` Every rung of the DefaultAzureCredential chain was tried: environment service ` +
|
|
269
|
+
`principal, workload identity, managed identity, VS Code, Azure CLI, Azure ` +
|
|
270
|
+
`PowerShell, Azure Developer CLI.\n` +
|
|
271
|
+
` Fix: run \`az login\`, or set AZURE_CLIENT_ID / AZURE_TENANT_ID / ` +
|
|
272
|
+
`AZURE_CLIENT_SECRET, or run where a managed identity exists, or pass \`_credential\`.`);
|
|
273
|
+
failure.name = 'AzureCredentialsUnavailableError';
|
|
274
|
+
return failure;
|
|
275
|
+
}
|
|
276
|
+
/** Re-raise without the library's text. See the module header for why. */
|
|
277
|
+
function sdkFailure(operation, err) {
|
|
278
|
+
const name = errorName(err);
|
|
279
|
+
// The no-credential names get the fix, not just the diagnosis — but ONLY
|
|
280
|
+
// those names. Everything else is reported as what it is.
|
|
281
|
+
if (name === 'CredentialUnavailableError' || name === 'AggregateAuthenticationError') {
|
|
282
|
+
return credentialsUnavailable(name);
|
|
283
|
+
}
|
|
284
|
+
const failure = new Error(`${ADAPTER}: ${operation} failed — ${name}.\n` +
|
|
285
|
+
` The underlying message is withheld: this call handles an access token, and auth ` +
|
|
286
|
+
`libraries echo request detail into failure text. Check the Entra sign-in logs for ` +
|
|
287
|
+
`the full error.`);
|
|
288
|
+
failure.name = 'AzureCredentialError';
|
|
289
|
+
return failure;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Did THIS adapter write this error?
|
|
293
|
+
*
|
|
294
|
+
* Every refusal authored here opens with the adapter's own name — both as the
|
|
295
|
+
* marker and because it is what makes the message readable ("entraIdentity:
|
|
296
|
+
* …", "entraIdentity requires …"). A library's own failure never does, so the
|
|
297
|
+
* prefix is a reliable discriminator without an error subclass per refusal.
|
|
298
|
+
*/
|
|
299
|
+
function isOwnRefusal(err) {
|
|
300
|
+
return err instanceof Error && err.message.startsWith(ADAPTER);
|
|
301
|
+
}
|
|
302
|
+
function errorName(err) {
|
|
303
|
+
const name = err?.name;
|
|
304
|
+
return typeof name === 'string' && name.length > 0 ? name : 'an unnamed failure';
|
|
305
|
+
}
|
|
306
|
+
//# sourceMappingURL=azure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"azure.js","sourceRoot":"","sources":["../../../src/adapters/identity/azure.ts"],"names":[],"mappings":"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"}
|