agentfootprint 9.72.0 → 9.74.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/CHANGELOG.md +160 -0
- 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 +117 -5
- 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/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 +111 -0
- package/dist/esm/adapters/llm/OpenAIProvider.js +115 -4
- 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/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/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 +111 -0
- 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/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/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,166 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [9.74.0] - 2026-08-27
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **The Azure/Foundry column exists — keyless auth and both Microsoft inference
|
|
15
|
+
doors, cloud and on-device.** AWS and GCP each had a full adapter column;
|
|
16
|
+
Azure had a static api-key string and nothing else. This release fills the
|
|
17
|
+
auth + inference tier, and every piece is a vendor adapter over ports that
|
|
18
|
+
did not change — the same seams the AWS and GCP columns already use.
|
|
19
|
+
|
|
20
|
+
**`foundry()` — the project-endpoint provider** (`agentfootprint/providers`).
|
|
21
|
+
The JS answer to Microsoft's `FoundryChatClient(project_endpoint, model,
|
|
22
|
+
credential)`: point it at a Foundry project endpoint (or let the hosted
|
|
23
|
+
platform's auto-injected `FOUNDRY_PROJECT_ENDPOINT` supply it), name the
|
|
24
|
+
deployment (`AZURE_AI_MODEL_DEPLOYMENT_NAME ?? MODEL_NAME`), and auth is an
|
|
25
|
+
Entra `TokenCredential`, an api key, or — given neither — the platform's own
|
|
26
|
+
blessed default, `DefaultAzureCredential` from the optional `@azure/identity`
|
|
27
|
+
peer. Inference rides the GA api-version-free `/openai/v1` route derived from
|
|
28
|
+
the project endpoint; the deployment name travels as the `model` field; the
|
|
29
|
+
token scope is `https://ai.azure.com/.default` (the management audience is a
|
|
30
|
+
different token — the docs say so out loud). Rotating tokens reuse the
|
|
31
|
+
per-request key-callback seam `openai()` has had since 9.29.0: the client is
|
|
32
|
+
rebuilt only when the token string actually changes.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { foundry } from 'agentfootprint/providers';
|
|
36
|
+
// In a Foundry hosted container: zero config — endpoint injected, identity ambient.
|
|
37
|
+
const llm = foundry();
|
|
38
|
+
// Locally: az login, then name the project.
|
|
39
|
+
const local = foundry({
|
|
40
|
+
projectEndpoint: 'https://acct.services.ai.azure.com/api/projects/my-project',
|
|
41
|
+
deployment: 'gpt-4.1-mini',
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**`foundryLocal()` — the on-device door.** Fetch-only, zero dependencies, the
|
|
46
|
+
`ollama()` discipline applied to Foundry Local's OpenAI-compatible `/v1`
|
|
47
|
+
wire: an alias like `qwen2.5-0.5b` resolves to a concrete variant through the
|
|
48
|
+
service's own catalog (`GET /foundry/list`, priority order, first wins;
|
|
49
|
+
cached per provider), a full variant id skips the catalog entirely, and the
|
|
50
|
+
typed `FoundryLocalUnavailableError` tells the truth a local runtime needs
|
|
51
|
+
telling — `foundry server start` to start it, `foundry server status` to find
|
|
52
|
+
the dynamic port, `foundry model run <alias>` when the model is the missing
|
|
53
|
+
piece, with the machine's actual model list attached when the service could
|
|
54
|
+
answer. No API key is sent because none exists. Streamed usage is read off
|
|
55
|
+
the final empty-choices frame — the exact bug class 9.73.0 fixed, pinned here
|
|
56
|
+
from day one.
|
|
57
|
+
|
|
58
|
+
**`entraIdentity()` — Azure credentials for tools**
|
|
59
|
+
(`agentfootprint/security`). The `googleIdentity` anatomy, law for law:
|
|
60
|
+
vends the deployment's identity as a bearer via any `TokenCredential`
|
|
61
|
+
(default: `DefaultAzureCredential`'s chain — env service principal, workload
|
|
62
|
+
identity, managed identity, VS Code, az CLI), scopes the request's way or
|
|
63
|
+
`AZURE_AI_SCOPE` by default, refuses `mode: 'user'` and per-request user
|
|
64
|
+
tokens BY NAME until an OBO surface exists, refuses services outside the
|
|
65
|
+
allowlist, and never lets an SDK's failure text — which echoes request
|
|
66
|
+
detail — reach a thrown message. `AZURE_AI_SCOPE` and
|
|
67
|
+
`AZURE_MANAGEMENT_SCOPE` are both exported because the audiences are not
|
|
68
|
+
interchangeable, and pretending there is one "azure scope" would be a lie
|
|
69
|
+
that 401s at runtime.
|
|
70
|
+
|
|
71
|
+
**`azureOpenai({ credential })` — the existing Azure door goes keyless.** An
|
|
72
|
+
Entra credential now rides the SDK's `azureADTokenProvider`; the static
|
|
73
|
+
api-key path is byte-identical to before; both given at once is refused by
|
|
74
|
+
name as the config bug it is. The docs' old caveat — "azureOpenai is the
|
|
75
|
+
wrong door for bearer auth" — is retired. Each keyless door defaults to the
|
|
76
|
+
audience ITS route documents: this one asks for
|
|
77
|
+
`https://cognitiveservices.azure.com/.default` (the classic deployment-scoped
|
|
78
|
+
route's own documented audience, `AZURE_COGNITIVE_SERVICES_SCOPE`), while
|
|
79
|
+
`foundry()` asks for `https://ai.azure.com/.default` (`AZURE_AI_SCOPE`, the
|
|
80
|
+
v1/project route's) — both overridable via `scope`, both pinned on the wire
|
|
81
|
+
by tests that record what the credential was actually asked for.
|
|
82
|
+
|
|
83
|
+
**`openai({ legacyEndpoint })` — the dialect dial goes public.** `baseURL`
|
|
84
|
+
has always implied the legacy dialect (`max_tokens`, no `stream_options`)
|
|
85
|
+
because most OpenAI-compatible servers are behind; `legacyEndpoint: false`
|
|
86
|
+
now declares "this baseURL speaks the current dialect" — which is exactly
|
|
87
|
+
what the Azure v1 route is. Default unchanged: `!!baseURL`.
|
|
88
|
+
|
|
89
|
+
**`providerFromEnv` learns both doors — upgrade-safely.** `FOUNDRY_LOCAL_MODEL`
|
|
90
|
+
slots directly after `OLLAMA_MODEL` (a model name you typed for a local
|
|
91
|
+
runtime — the same name-beats-leftover-credential law), and
|
|
92
|
+
`FOUNDRY_PROJECT_ENDPOINT` + `AZURE_AI_MODEL_DEPLOYMENT_NAME` — a pair of
|
|
93
|
+
product-specific spellings nobody exports by accident — outranks the
|
|
94
|
+
lingering-credential arms. Two guards keep an upgrade from breaking a
|
|
95
|
+
working environment, because the hosted platform auto-injects the endpoint
|
|
96
|
+
into every container, Foundry-bound or not: an endpoint with **no**
|
|
97
|
+
deployment named HOLDS its refusal (every arm below answers exactly as it
|
|
98
|
+
did before this arm existed, and the held refusal is raised only when
|
|
99
|
+
nothing else resolves), and the generic `MODEL_NAME` alone never carries the
|
|
100
|
+
endpoint past a bootable Azure config — choosing Foundry over working Azure
|
|
101
|
+
takes the deliberate spelling. Every existing precedence is pinned
|
|
102
|
+
untouched; each guard has its own test.
|
|
103
|
+
|
|
104
|
+
New from `agentfootprint/providers`: `foundry`, `foundryInferenceUrl`,
|
|
105
|
+
`FoundryProviderOptions`, `foundryLocal`, `FoundryLocalProvider`,
|
|
106
|
+
`FoundryLocalUnavailableError`, `FoundryLocalProviderOptions`,
|
|
107
|
+
`TokenCredentialLike`, `AccessTokenLike`. New from `agentfootprint/security`:
|
|
108
|
+
`entraIdentity`, `AZURE_AI_SCOPE`, `AZURE_MANAGEMENT_SCOPE`,
|
|
109
|
+
`AZURE_COGNITIVE_SERVICES_SCOPE`, `EntraIdentityOptions`,
|
|
110
|
+
`TokenCredentialLike`, `AccessTokenLike`, `AzureIdentitySdkModule`. New
|
|
111
|
+
optional peer: `@azure/identity`. Pinned by 178 new test cases (counted in
|
|
112
|
+
the diff, not the runner) across
|
|
113
|
+
`test/adapters/identity/entra-identity.test.ts`,
|
|
114
|
+
`test/adapters/unit/FoundryProvider.test.ts`,
|
|
115
|
+
`test/adapters/unit/FoundryLocalProvider.test.ts`,
|
|
116
|
+
`test/adapters/integration/foundry-wire.test.ts` (a real `openai` SDK against
|
|
117
|
+
a local fake of the `/openai/v1` wire — Bearer header, `max_completion_tokens`,
|
|
118
|
+
token rotation, and the audience each door asks its credential for, all
|
|
119
|
+
asserted on the wire, not assumed), the extended Azure env/wire suites, and
|
|
120
|
+
the fix pins from the adversarial review below. Deliberately NOT in this train: the App Insights sink,
|
|
121
|
+
Azure AI Search, the Toolbox MCP transport (next trains), and every
|
|
122
|
+
preview-only surface (A2A door, browser/computer-use, managed memory) —
|
|
123
|
+
refusing to ship against previews we cannot verify is a feature.
|
|
124
|
+
|
|
125
|
+
The whole train was adversarially reviewed before release (five lenses, every
|
|
126
|
+
serious finding independently re-verified with a reproduction): 1 blocker and
|
|
127
|
+
9 should-fix findings were confirmed and every one is fixed and pinned in
|
|
128
|
+
this release — including a mid-stream failure frame that `foundryLocal()`
|
|
129
|
+
would have reported as a clean stop, an abort signal that never reached a
|
|
130
|
+
streaming body, and an env-detection arm that would have broken working
|
|
131
|
+
deployments on upgrade.
|
|
132
|
+
|
|
133
|
+
### Fixed
|
|
134
|
+
|
|
135
|
+
- **`ollama()` inherited four stream/abort defects — found by reviewing the new
|
|
136
|
+
Foundry Local adapter, fixed at the root.** The adversarial review of
|
|
137
|
+
`foundryLocal()` proved its four streaming defects were byte-twin shapes
|
|
138
|
+
copied from `OllamaProvider`, which had shipped them for months: a caller's
|
|
139
|
+
already-aborted `AbortSignal` still sent the request; an abort after headers
|
|
140
|
+
never reached the streaming body (generation ran on, un-stoppable); an early
|
|
141
|
+
`break` leaked the response body (the reader was never cancelled); and a
|
|
142
|
+
mid-stream `{"error": …}` frame was silently dropped, reporting a failed
|
|
143
|
+
generation as a clean stop. All four now match the fixed Foundry Local
|
|
144
|
+
shapes — same helper names, Ollama's NDJSON wire. 10 of the 12 new pinning
|
|
145
|
+
tests fail against the previous source (proven by restoring it); no public
|
|
146
|
+
API change; `OllamaUnavailableError` is byte-identical.
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
## [9.73.0] - 2026-08-27
|
|
150
|
+
|
|
151
|
+
### Fixed
|
|
152
|
+
|
|
153
|
+
- **Every local model was reporting zero tokens, and nothing said so.**
|
|
154
|
+
OpenAI and Azure only emit usage on a stream when asked, via
|
|
155
|
+
`stream_options: { include_usage: true }` — and this adapter deliberately
|
|
156
|
+
withheld that field when `baseURL` was set, because some OpenAI-compatible
|
|
157
|
+
servers reject an unknown field and a hard failure is worse than a missing
|
|
158
|
+
number. That caution was right; its cost was invisible. **Anyone streaming
|
|
159
|
+
through `openai({ baseURL })` — llama.cpp, Ollama, vLLM, LM Studio — read 0
|
|
160
|
+
tokens everywhere usage is consumed:** cost recorders, dashboards, and the
|
|
161
|
+
per-step cost on `agentThinkingTrace`. Nothing was broken, nothing errored,
|
|
162
|
+
and every number was wrong.
|
|
163
|
+
|
|
164
|
+
`openai({ streamUsage: true })` opts a custom endpoint back in. The default
|
|
165
|
+
is unchanged, because the servers that reject the field still exist; both
|
|
166
|
+
halves are pinned by tests. Found by wiring a real local model (llama.cpp
|
|
167
|
+
serving Qwen3-4B) behind a four-agent workflow and noticing every card read
|
|
168
|
+
`0 tok` while the server, asked directly, answered usage perfectly well.
|
|
169
|
+
|
|
10
170
|
## [9.72.0] - 2026-08-27
|
|
11
171
|
|
|
12
172
|
### Added
|
|
@@ -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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"}
|