pilotswarm-sdk 0.5.48 → 0.5.49

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.
@@ -0,0 +1,382 @@
1
+ /**
2
+ * Workload Identity Federation for the Anthropic API — no API key anywhere.
3
+ *
4
+ * A provider declared `type: "anthropic-wif"` carries no credential at all.
5
+ * The worker proves who it is with an identity token its platform already
6
+ * issues, trades that for a short-lived Anthropic access token, and sends the
7
+ * result as `Authorization: Bearer`. Nothing is stored, so nothing can leak
8
+ * from the database and nothing has to be rotated.
9
+ *
10
+ * Two shapes of identity token are understood, chosen by what the environment
11
+ * actually provides:
12
+ *
13
+ * ```
14
+ * ANTHROPIC_IDENTITY_TOKEN_FILE (or _TOKEN) one hop
15
+ * the file already holds a JWT the federation rule trusts
16
+ * → POST /v1/oauth/token
17
+ *
18
+ * AZURE_FEDERATED_TOKEN_FILE + AZURE_TENANT_ID two hops
19
+ * the file holds a Kubernetes-projected token, which Microsoft Entra ID
20
+ * does not accept as an assertion for anyone but itself
21
+ * → POST login.microsoftonline.com/<tenant>/oauth2/v2.0/token (Entra JWT)
22
+ * → POST /v1/oauth/token
23
+ * ```
24
+ *
25
+ * Both token files are re-read on every exchange: a projected token rotates
26
+ * on disk under the process, and a copy cached in memory goes stale.
27
+ *
28
+ * WHY A CALLBACK RATHER THAN A STORED KEY. The access token expires — an hour
29
+ * by default, a day at most — while a PilotSwarm session outlives any of that
30
+ * and may be resumed on a different worker days later. So the token is never
31
+ * put in the session config; `@github/copilot-sdk` takes a `bearerTokenProvider`
32
+ * callback instead and asks for a token before each outbound request. The
33
+ * runtime caches nothing, which is why the caching below is not an
34
+ * optimisation: without it every single request would mint two fresh tokens.
35
+ *
36
+ * @module
37
+ */
38
+ const DEFAULT_BASE_URL = "https://api.anthropic.com";
39
+ /**
40
+ * The three ids that name the rule, the organization and the identity the
41
+ * minted token acts as. All three are required whichever identity source is
42
+ * used, and none of them is a secret.
43
+ */
44
+ const REQUIRED_VARS = [
45
+ "ANTHROPIC_FEDERATION_RULE_ID",
46
+ "ANTHROPIC_ORGANIZATION_ID",
47
+ "ANTHROPIC_SERVICE_ACCOUNT_ID",
48
+ ];
49
+ function trimmed(value) {
50
+ return typeof value === "string" ? value.trim() : "";
51
+ }
52
+ /**
53
+ * Read the settings out of the worker's environment.
54
+ *
55
+ * Returns the missing variable names rather than throwing, so a worker whose
56
+ * deployment declares the type but has not configured identity yet can say
57
+ * which line is absent instead of failing an unrelated turn later.
58
+ */
59
+ export function readAnthropicWifSettings(env = process.env) {
60
+ const missing = REQUIRED_VARS.filter((name) => !trimmed(env[name]));
61
+ const identity = readIdentitySource(env);
62
+ if (!identity) {
63
+ missing.push("ANTHROPIC_IDENTITY_TOKEN_FILE (or ANTHROPIC_IDENTITY_TOKEN, or AZURE_FEDERATED_TOKEN_FILE with AZURE_TENANT_ID)");
64
+ }
65
+ if (missing.length > 0 || !identity)
66
+ return { ok: false, missing };
67
+ const workspaceId = trimmed(env.ANTHROPIC_WORKSPACE_ID);
68
+ return {
69
+ ok: true,
70
+ settings: {
71
+ federationRuleId: trimmed(env.ANTHROPIC_FEDERATION_RULE_ID),
72
+ organizationId: trimmed(env.ANTHROPIC_ORGANIZATION_ID),
73
+ serviceAccountId: trimmed(env.ANTHROPIC_SERVICE_ACCOUNT_ID),
74
+ ...(workspaceId ? { workspaceId } : {}),
75
+ // The assertion posted to this host is a signed identity token, so
76
+ // where it goes is worth naming deliberately. ANTHROPIC_BASE_URL is
77
+ // honoured because it is the conventional override and a worker
78
+ // that sets it means it — but a deployment that points that at a
79
+ // gateway for unrelated reasons can pin the exchange back with
80
+ // ANTHROPIC_WIF_TOKEN_URL.
81
+ baseUrl: (trimmed(env.ANTHROPIC_WIF_TOKEN_URL)
82
+ || trimmed(env.ANTHROPIC_BASE_URL)
83
+ || DEFAULT_BASE_URL).replace(/\/+$/, ""),
84
+ identity,
85
+ },
86
+ };
87
+ }
88
+ /**
89
+ * A ready-made assertion wins over one that has to be fetched: a deployment
90
+ * that sets `ANTHROPIC_IDENTITY_TOKEN_FILE` has said which token it means,
91
+ * and Azure's own variables may be present on the same pod for unrelated
92
+ * reasons (a sidecar, a storage credential) without being the intended path.
93
+ */
94
+ function readIdentitySource(env) {
95
+ const literal = trimmed(env.ANTHROPIC_IDENTITY_TOKEN);
96
+ if (literal)
97
+ return { kind: "literal", token: literal };
98
+ const file = trimmed(env.ANTHROPIC_IDENTITY_TOKEN_FILE);
99
+ if (file)
100
+ return { kind: "file", path: file };
101
+ const azureFile = trimmed(env.AZURE_FEDERATED_TOKEN_FILE);
102
+ const tenantId = trimmed(env.AZURE_TENANT_ID);
103
+ if (!azureFile || !tenantId)
104
+ return null;
105
+ // The identity to claim is not always the one the platform injected: a
106
+ // pod may run under its cluster's own identity while the federation rule
107
+ // names another that the same projected token is allowed to redeem. So an
108
+ // explicit override wins, and the injected value is only the default.
109
+ const clientId = trimmed(env.ANTHROPIC_WIF_AZURE_CLIENT_ID) || trimmed(env.AZURE_CLIENT_ID);
110
+ if (!clientId)
111
+ return null;
112
+ // The audience asked for. Defaulting to the identity's own id covers a
113
+ // rule that matches the identity as its own audience; a deployment with a
114
+ // separate audience app registration sets the scope explicitly.
115
+ const scope = trimmed(env.ANTHROPIC_WIF_AZURE_SCOPE) || `${clientId}/.default`;
116
+ return { kind: "entra", tokenFile: azureFile, tenantId, clientId, scope };
117
+ }
118
+ /** Anything a provider type may declare that authenticates without a stored key. */
119
+ export class WifExchangeError extends Error {
120
+ status;
121
+ requestId;
122
+ code = "WIF_EXCHANGE_FAILED";
123
+ constructor(message, status, requestId) {
124
+ super(message);
125
+ this.status = status;
126
+ this.requestId = requestId;
127
+ this.name = "WifExchangeError";
128
+ }
129
+ }
130
+ /**
131
+ * How early a token is replaced. A token used at the instant it expires is a
132
+ * failed request: the clock on the far side is not this one, and the request
133
+ * still has to travel. Half the lifetime is the ceiling so that a deliberately
134
+ * short-lived token (the rule may set 60 seconds) is still cached for a while
135
+ * rather than re-minted for every single request.
136
+ */
137
+ const REFRESH_MARGIN_MS = 5 * 60_000;
138
+ function marginFor(lifetimeMs) {
139
+ return Math.min(REFRESH_MARGIN_MS, Math.max(0, Math.floor(lifetimeMs / 2)));
140
+ }
141
+ /**
142
+ * What a lifetime is assumed to be when the response does not say.
143
+ *
144
+ * `expires_in` is optional in the type and could arrive absent, zero, or
145
+ * unparseable. Treating that as "expires now" silently turns the cache OFF —
146
+ * and because the callback runs before EVERY outbound request, that is one
147
+ * token exchange per LLM request, for ever, until the endpoint throttles.
148
+ * A minute is the shortest lifetime a federation rule may be configured for,
149
+ * so assuming it can never outlive the real token.
150
+ */
151
+ const ASSUMED_LIFETIME_MS = 60_000;
152
+ /**
153
+ * The longest a token is trusted, however long it says it is valid.
154
+ *
155
+ * Nothing tells this process that a token was revoked: the rule is disabled or
156
+ * the service account deleted, and the only symptom is that requests start
157
+ * failing. A token cached for its full 24 hours would keep failing for 24
158
+ * hours. Re-minting hourly costs one exchange per worker per hour and bounds
159
+ * how long a revoked credential can keep a worker down.
160
+ */
161
+ const MAX_CACHE_MS = 60 * 60_000;
162
+ /** When to replace a token, from whatever the endpoint claimed. */
163
+ function expiryFrom(now, expiresIn) {
164
+ const seconds = Number(expiresIn);
165
+ const claimed = Number.isFinite(seconds) && seconds > 0 ? seconds * 1000 : ASSUMED_LIFETIME_MS;
166
+ const lifetimeMs = Math.min(claimed, MAX_CACHE_MS);
167
+ return now + Math.max(0, lifetimeMs - marginFor(lifetimeMs));
168
+ }
169
+ /**
170
+ * Mints and caches the Anthropic access token for one set of settings.
171
+ *
172
+ * One instance serves every session on the worker. The callback is invoked
173
+ * once per outbound request — several turns running at once is the normal
174
+ * case, not the exception — so concurrent callers share a single exchange
175
+ * rather than each starting their own. A failure is never cached: the next
176
+ * request tries again, which is what recovers a worker that started before
177
+ * its token file was mounted.
178
+ */
179
+ export class AnthropicWifCredentials {
180
+ settings;
181
+ access = null;
182
+ assertion = null;
183
+ inflight = null;
184
+ /** Bumped by reset(), so a mint that started earlier does not store its result. */
185
+ generation = 0;
186
+ fetchImpl;
187
+ readFileImpl;
188
+ now;
189
+ constructor(settings, deps = {}) {
190
+ this.settings = settings;
191
+ this.fetchImpl = deps.fetch ?? globalThis.fetch;
192
+ this.readFileImpl = deps.readFile ?? (async (p) => {
193
+ const { readFile } = await import("node:fs/promises");
194
+ return readFile(p, "utf8");
195
+ });
196
+ this.now = deps.now ?? Date.now;
197
+ }
198
+ /** A valid Anthropic access token, minted or from cache. */
199
+ async getToken() {
200
+ const cached = this.access;
201
+ if (cached && this.now() < cached.expiresAtMs)
202
+ return cached.token;
203
+ if (this.inflight)
204
+ return this.inflight;
205
+ const startedIn = this.generation;
206
+ const run = this.mint()
207
+ .then((minted) => {
208
+ // Discarded by a reset() that landed while this was in flight:
209
+ // the token is still returned to whoever asked for it, but it
210
+ // is not written into a cache that was deliberately emptied.
211
+ if (this.generation === startedIn)
212
+ this.access = minted;
213
+ return minted.token;
214
+ })
215
+ .finally(() => {
216
+ if (this.generation === startedIn)
217
+ this.inflight = null;
218
+ });
219
+ this.inflight = run;
220
+ return run;
221
+ }
222
+ /**
223
+ * Drop what is cached. For tests, and for a caller that saw a 401.
224
+ *
225
+ * The generation bump is the point. Clearing the fields alone does not
226
+ * work while an exchange is in flight: that exchange resolves afterwards
227
+ * and writes the very token this was called to discard straight back into
228
+ * the cache. A mint only stores its result if the generation it started
229
+ * in is still current.
230
+ */
231
+ reset() {
232
+ this.access = null;
233
+ this.assertion = null;
234
+ this.generation += 1;
235
+ this.inflight = null;
236
+ }
237
+ async mint() {
238
+ const assertion = await this.identityToken();
239
+ const body = {
240
+ grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
241
+ assertion,
242
+ federation_rule_id: this.settings.federationRuleId,
243
+ organization_id: this.settings.organizationId,
244
+ service_account_id: this.settings.serviceAccountId,
245
+ };
246
+ // Sending an empty workspace is not the same as not naming one: the
247
+ // exchange validates the shape of the value it is given.
248
+ if (this.settings.workspaceId)
249
+ body.workspace_id = this.settings.workspaceId;
250
+ const response = await this.fetchImpl(`${this.settings.baseUrl}/v1/oauth/token`, {
251
+ method: "POST",
252
+ headers: { "content-type": "application/json" },
253
+ body: JSON.stringify(body),
254
+ });
255
+ if (!response.ok) {
256
+ // Throw the cached assertion away with it. That token is what was
257
+ // just refused, and it may be cached for hours — without this a
258
+ // retrying worker keeps presenting the same rejected assertion
259
+ // until it expires on its own.
260
+ this.assertion = null;
261
+ // Every denial is the same opaque 401 with the same sentence —
262
+ // which check failed is recorded against the request id in the
263
+ // Anthropic console's authentication history, and is unknowable
264
+ // from here. Carrying the id is the difference between a
265
+ // diagnosable failure and a shrug.
266
+ const requestId = response.headers?.get?.("request-id") ?? null;
267
+ throw new WifExchangeError(`Anthropic token exchange failed with HTTP ${response.status}`
268
+ + `${requestId ? ` (request id ${requestId})` : ""}.`
269
+ + " Check the authentication history in the Anthropic console for the reason.", response.status, requestId);
270
+ }
271
+ // Guarded like the Entra hop: a 200 carrying something that is not
272
+ // JSON (an intercepting proxy, an HTML error page) must still surface
273
+ // as a WifExchangeError with the guidance below, not a raw SyntaxError.
274
+ const payload = await response.json().catch(() => ({}));
275
+ const token = typeof payload.access_token === "string" ? payload.access_token : "";
276
+ if (!token) {
277
+ throw new WifExchangeError("Anthropic token exchange returned no access_token.", response.status);
278
+ }
279
+ return { token, expiresAtMs: expiryFrom(this.now(), payload.expires_in) };
280
+ }
281
+ /** The JWT presented to Anthropic as the assertion. */
282
+ async identityToken() {
283
+ const source = this.settings.identity;
284
+ if (source.kind === "literal")
285
+ return source.token;
286
+ if (source.kind === "file")
287
+ return (await this.readFileImpl(source.path)).trim();
288
+ const cached = this.assertion;
289
+ if (cached && this.now() < cached.expiresAtMs)
290
+ return cached.token;
291
+ const minted = await this.entraToken(source);
292
+ this.assertion = minted;
293
+ return minted.token;
294
+ }
295
+ /**
296
+ * Redeem the Kubernetes-projected token for an Entra-issued one.
297
+ *
298
+ * The projected token is read fresh every time: Kubernetes rewrites that
299
+ * file as it rotates, and the copy this process read at startup stops
300
+ * verifying the moment it does.
301
+ */
302
+ async entraToken(source) {
303
+ const projected = (await this.readFileImpl(source.tokenFile)).trim();
304
+ const form = new URLSearchParams({
305
+ grant_type: "client_credentials",
306
+ client_id: source.clientId,
307
+ scope: source.scope,
308
+ client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
309
+ client_assertion: projected,
310
+ });
311
+ const response = await this.fetchImpl(`https://login.microsoftonline.com/${encodeURIComponent(source.tenantId)}/oauth2/v2.0/token`, {
312
+ method: "POST",
313
+ headers: { "content-type": "application/x-www-form-urlencoded" },
314
+ body: form.toString(),
315
+ });
316
+ const payload = await response.json().catch(() => ({}));
317
+ if (!response.ok || !payload.access_token) {
318
+ // Entra says exactly what is wrong (a missing audience app, an
319
+ // unfederated service account), and its first line is the useful
320
+ // one — the rest is a stack of correlation ids.
321
+ const detail = String(payload.error_description ?? payload.error ?? "").split("\n")[0];
322
+ throw new WifExchangeError(`Microsoft Entra token request failed with HTTP ${response.status}${detail ? `: ${detail}` : "."}`, response.status);
323
+ }
324
+ return { token: payload.access_token, expiresAtMs: expiryFrom(this.now(), payload.expires_in) };
325
+ }
326
+ }
327
+ /**
328
+ * The worker's own credentials, made once and shared.
329
+ *
330
+ * Keyed by the settings themselves so that a test which changes the
331
+ * environment gets a fresh instance, while every session in a running worker
332
+ * shares one cache and one in-flight exchange.
333
+ */
334
+ const instances = new Map();
335
+ export function anthropicWifCredentials(settings, deps = {}) {
336
+ const key = JSON.stringify(settings);
337
+ let found = instances.get(key);
338
+ if (!found) {
339
+ found = new AnthropicWifCredentials(settings, deps);
340
+ instances.set(key, found);
341
+ }
342
+ return found;
343
+ }
344
+ /** Forget every shared instance. Tests only. */
345
+ export function resetAnthropicWifCredentials() {
346
+ instances.clear();
347
+ }
348
+ /**
349
+ * Give a workload-identity provider the callback that authenticates it.
350
+ *
351
+ * Every other provider reaches the Copilot SDK carrying its own key. This one
352
+ * carries a function, because the token it would carry instead expires long
353
+ * before the session does — a session resumed days later on another worker
354
+ * would come back with a dead credential baked into its config. The SDK keeps
355
+ * the callback client-side and asks for a token before each outbound request,
356
+ * so a token lives only as long as the call it authenticates.
357
+ *
358
+ * Called on the path that builds the SDK options and nowhere earlier: that
359
+ * object is rebuilt from the registry on every create and resume, and is
360
+ * never persisted. (`providerFingerprint` hashes the same object through
361
+ * JSON.stringify, which drops function-valued keys, so the fingerprint stays
362
+ * stable and a resume does not read as a provider change.)
363
+ *
364
+ * A provider that is not workload-identity is returned untouched, by
365
+ * identity, so this is safe to call on the whole path.
366
+ */
367
+ export function attachWorkloadIdentity(resolved, deps = {}) {
368
+ const provider = resolved.sdkProvider;
369
+ if (!resolved.usesWorkloadIdentity)
370
+ return provider;
371
+ // Said once at session creation, naming the variables that are absent,
372
+ // rather than as an opaque 401 on the first turn of every session that
373
+ // ever names this provider.
374
+ const found = readAnthropicWifSettings(deps.env ?? process.env);
375
+ if (!found.ok) {
376
+ throw new Error(`Provider "${resolved.providerId}" authenticates with workload identity, `
377
+ + `but the worker is missing ${found.missing.join(", ")}.`);
378
+ }
379
+ const credentials = (deps.credentials ?? anthropicWifCredentials)(found.settings);
380
+ return { ...provider, bearerTokenProvider: () => credentials.getToken() };
381
+ }
382
+ //# sourceMappingURL=wif-credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wif-credentials.js","sourceRoot":"","sources":["../src/wif-credentials.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pilotswarm-sdk",
3
- "version": "0.5.48",
3
+ "version": "0.5.49",
4
4
  "description": "A durable execution runtime for GitHub Copilot SDK agents. Crash recovery, durable timers, session dehydration, and multi-node scaling — powered by duroxide.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -88,7 +88,7 @@
88
88
  "pg": "^8.18.0"
89
89
  },
90
90
  "peerDependencies": {
91
- "pilotswarm-horizon-store": "0.5.48"
91
+ "pilotswarm-horizon-store": "0.5.49"
92
92
  },
93
93
  "peerDependenciesMeta": {
94
94
  "pilotswarm-horizon-store": {
@@ -98,7 +98,7 @@
98
98
  "devDependencies": {
99
99
  "@types/node": "^22.0.0",
100
100
  "@types/pg": "^8.16.0",
101
- "pilotswarm-horizon-store": "0.5.48",
101
+ "pilotswarm-horizon-store": "0.5.49",
102
102
  "typescript": "^5.0.0",
103
103
  "vitest": "^4.1.0"
104
104
  },