@ai-sdk/harness-pi 1.0.92 → 1.0.94

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # @ai-sdk/harness-pi
2
2
 
3
+ ## 1.0.94
4
+
5
+ ### Patch Changes
6
+
7
+ - e0d7cfb: feat(harness): allow harness sessions to optionally authenticate from an isolated environment supplied through the `auth` option, and remove support for the formerly deprecated legacy auth options types
8
+ - Updated dependencies [e0d7cfb]
9
+ - @ai-sdk/harness@1.0.92
10
+
11
+ ## 1.0.93
12
+
13
+ ### Patch Changes
14
+
15
+ - @ai-sdk/harness@1.0.91
16
+
3
17
  ## 1.0.92
4
18
 
5
19
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,33 +1,12 @@
1
1
  import * as _ai_sdk_harness from '@ai-sdk/harness';
2
- import { HarnessV1, HarnessV1BuiltinTool } from '@ai-sdk/harness';
2
+ import { HarnessV1Authentication, HarnessV1, HarnessV1BuiltinTool } from '@ai-sdk/harness';
3
3
  import { ExtensionFactory } from '@earendil-works/pi-coding-agent';
4
4
 
5
5
  /**
6
6
  * Pi auth options. Choose an explicit mode or rely on 'auto' (precedence:
7
7
  * explicit gateway, then OpenAI / Anthropic / custom environment variables).
8
8
  */
9
- type PiAuthenticationMode = 'auto' | 'openai' | 'anthropic' | 'custom' | 'ai-gateway';
10
- /**
11
- * @deprecated Passing an object to auth options is deprecated. Use a `PiAuthenticationMode` string value ("auto" | "openai" | "anthropic" | "custom" | "ai-gateway") instead, and pass credentials via environment variables.
12
- */
13
- type LegacyPiAuthOptions = {
14
- readonly gateway?: {
15
- readonly apiKey?: string;
16
- readonly baseUrl?: string;
17
- };
18
- /**
19
- * Resolved environment-variable pairs of the form `<PREFIX>_API_KEY` and
20
- * (optionally) `<PREFIX>_BASE_URL`. Special-cased prefixes:
21
- * - `AI_GATEWAY` → registers `vercel-ai-gateway`
22
- * - `OPENAI` → registers `openai`
23
- * - `ANTHROPIC` → registers `anthropic` (`ANTHROPIC_AUTH_TOKEN` adds a
24
- * bearer auth header)
25
- * Any other `<PREFIX>_API_KEY` with a matching `<PREFIX>_BASE_URL` is
26
- * registered as the lowercased, dash-separated prefix.
27
- */
28
- readonly customEnv?: Record<string, string>;
29
- };
30
- type PiAuthOptions = PiAuthenticationMode | LegacyPiAuthOptions;
9
+ type PiAuthenticationMode = HarnessV1Authentication<'openai' | 'anthropic' | 'custom'>;
31
10
 
32
11
  type PiThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
33
12
 
@@ -37,7 +16,7 @@ type PiThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
37
16
  */
38
17
  type PiHarnessSettings = {
39
18
  /** Where Pi sources API keys / gateway credentials from. */
40
- readonly auth?: PiAuthOptions;
19
+ readonly auth?: PiAuthenticationMode;
41
20
  /**
42
21
  * Pi model id (or name). Leaving this unset falls back to the AI Gateway
43
22
  * default when `AI_GATEWAY_API_KEY` / `VERCEL_OIDC_TOKEN` is set, and to
@@ -143,4 +122,4 @@ declare const pi: _ai_sdk_harness.HarnessV1<{
143
122
  readonly ls: _ai_sdk_harness.HarnessV1BuiltinTool;
144
123
  }>;
145
124
 
146
- export { type PiAuthOptions, type PiAuthenticationMode, type PiHarnessSettings, VERSION, createPi, pi };
125
+ export { type PiAuthenticationMode, type PiHarnessSettings, VERSION, createPi, pi };
package/dist/index.js CHANGED
@@ -108,7 +108,6 @@ import {
108
108
  DefaultResourceLoader,
109
109
  defineTool,
110
110
  ModelRegistry,
111
- ModelRuntime,
112
111
  SessionManager,
113
112
  SettingsManager
114
113
  } from "@earendil-works/pi-coding-agent";
@@ -125,16 +124,121 @@ import {
125
124
  } from "@ai-sdk/harness/utils";
126
125
 
127
126
  // src/pi-auth.ts
128
- import { getAiGatewayAuthFromEnv } from "@ai-sdk/harness/utils";
127
+ import {
128
+ ModelRuntime
129
+ } from "@earendil-works/pi-coding-agent";
130
+ import {
131
+ getAiGatewayAuthFromEnv,
132
+ isHarnessAuthenticationEnvironment
133
+ } from "@ai-sdk/harness/utils";
134
+ import { access } from "fs/promises";
129
135
 
130
136
  // src/version.ts
131
- var VERSION = true ? "1.0.92" : "0.0.0-test";
137
+ var VERSION = true ? "1.0.94" : "0.0.0-test";
132
138
 
133
139
  // src/pi-auth.ts
134
140
  var DEFAULT_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh";
135
141
  var DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
136
142
  var DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com";
137
143
  var HARNESS_CLIENT_APP = `ai-sdk/harness-pi/${VERSION}`;
144
+ function createIsolatedPiCredentialStore() {
145
+ let initializing = true;
146
+ const bootstrapEnvironment = new Proxy(
147
+ {},
148
+ {
149
+ get: (_target, property) => typeof property === "string" ? "harness-pi-authentication-bootstrap" : void 0
150
+ }
151
+ );
152
+ const bootstrapCredential = {
153
+ type: "api_key",
154
+ key: "harness-pi-authentication-bootstrap",
155
+ env: bootstrapEnvironment
156
+ };
157
+ const credentials = {
158
+ async read() {
159
+ return initializing ? bootstrapCredential : void 0;
160
+ },
161
+ async list() {
162
+ return [];
163
+ },
164
+ async modify(..._input) {
165
+ return void 0;
166
+ },
167
+ async delete() {
168
+ }
169
+ };
170
+ return {
171
+ credentials,
172
+ finishInitialization() {
173
+ initializing = false;
174
+ }
175
+ };
176
+ }
177
+ function scopePiProviderEnvironment({
178
+ modelRuntime,
179
+ authenticationEnvironment
180
+ }) {
181
+ for (const provider of modelRuntime.getProviders()) {
182
+ const apiKeyAuthentication = provider.auth.apiKey;
183
+ if (!apiKeyAuthentication) continue;
184
+ provider.auth = {
185
+ ...provider.auth,
186
+ apiKey: {
187
+ ...apiKeyAuthentication,
188
+ resolve: async (input) => {
189
+ const result = await apiKeyAuthentication.resolve(input);
190
+ return result ? {
191
+ ...result,
192
+ env: {
193
+ ...authenticationEnvironment,
194
+ ...result.env
195
+ }
196
+ } : void 0;
197
+ }
198
+ }
199
+ };
200
+ }
201
+ }
202
+ async function createPiModelRuntime({
203
+ auth,
204
+ authPath,
205
+ modelsPath
206
+ }) {
207
+ if (!isHarnessAuthenticationEnvironment(auth)) {
208
+ return ModelRuntime.create({
209
+ authPath,
210
+ modelsPath,
211
+ allowModelNetwork: false
212
+ });
213
+ }
214
+ const isolatedCredentials = createIsolatedPiCredentialStore();
215
+ const modelRuntime = await ModelRuntime.create({
216
+ credentials: isolatedCredentials.credentials,
217
+ modelsPath: null,
218
+ allowModelNetwork: false
219
+ });
220
+ modelRuntime.models.authContext = {
221
+ async env(name) {
222
+ return auth[name];
223
+ },
224
+ async fileExists(filePath) {
225
+ if (filePath !== auth.GOOGLE_APPLICATION_CREDENTIALS) return false;
226
+ try {
227
+ await access(filePath);
228
+ return true;
229
+ } catch {
230
+ return false;
231
+ }
232
+ }
233
+ };
234
+ scopePiProviderEnvironment({
235
+ modelRuntime,
236
+ authenticationEnvironment: auth
237
+ });
238
+ isolatedCredentials.finishInitialization();
239
+ await modelRuntime.refresh({ allowNetwork: false });
240
+ return modelRuntime;
241
+ }
138
242
  function createGatewayProviderConfig({
139
243
  apiKey,
140
244
  baseUrl,
@@ -159,53 +263,41 @@ async function register({
159
263
  registries.modelRegistry.registerProvider(provider, config);
160
264
  await registries.modelRuntime.setRuntimeApiKey(provider, apiKey);
161
265
  }
162
- function hasConfiguredValue(value) {
163
- if (value == null) return false;
164
- if (typeof value === "string") return value.length > 0;
165
- if (typeof value !== "object") return true;
166
- return Object.values(value).some(hasConfiguredValue);
167
- }
168
266
  function resolvePiEnv({
169
267
  options,
170
268
  env
171
269
  }) {
172
- const normalizedOptions = normalizePiAuthToLegacyAuth(options);
173
- const customEnvConfigured = hasConfiguredValue(normalizedOptions?.customEnv);
174
- if (customEnvConfigured) {
175
- return resolveCustomEnv({ customEnv: normalizedOptions.customEnv ?? {} });
176
- }
177
- const gatewayConfigured = hasConfiguredValue(normalizedOptions?.gateway);
178
- const gatewayAuthFromEnv = getAiGatewayAuthFromEnv({ env });
179
- if (gatewayConfigured) {
180
- const apiKey = normalizedOptions.gateway?.apiKey ?? gatewayAuthFromEnv.apiKey;
181
- const baseUrl = normalizedOptions.gateway?.baseUrl ?? gatewayAuthFromEnv.baseUrl;
182
- if (apiKey) {
183
- return { AI_GATEWAY_API_KEY: apiKey, AI_GATEWAY_BASE_URL: baseUrl };
184
- }
185
- return {};
186
- }
270
+ const suppliedEnvironment = isHarnessAuthenticationEnvironment(options);
271
+ const authenticationEnvironment = suppliedEnvironment ? options : env;
272
+ const gatewayAuthFromEnv = getAiGatewayAuthFromEnv({
273
+ env: authenticationEnvironment
274
+ });
187
275
  if (typeof options === "string") {
188
276
  switch (options) {
189
277
  case "openai":
190
- if (env.OPENAI_API_KEY) {
278
+ if (authenticationEnvironment.OPENAI_API_KEY) {
191
279
  return {
192
- OPENAI_API_KEY: env.OPENAI_API_KEY,
193
- ...env.OPENAI_BASE_URL ? { OPENAI_BASE_URL: env.OPENAI_BASE_URL } : {}
280
+ OPENAI_API_KEY: authenticationEnvironment.OPENAI_API_KEY,
281
+ ...authenticationEnvironment.OPENAI_BASE_URL ? { OPENAI_BASE_URL: authenticationEnvironment.OPENAI_BASE_URL } : {}
194
282
  };
195
283
  }
196
284
  return {};
197
285
  case "anthropic":
198
- if (env.ANTHROPIC_API_KEY) {
286
+ if (authenticationEnvironment.ANTHROPIC_API_KEY) {
199
287
  return {
200
- ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY,
201
- ...env.ANTHROPIC_BASE_URL ? { ANTHROPIC_BASE_URL: env.ANTHROPIC_BASE_URL } : {},
202
- ...env.ANTHROPIC_AUTH_TOKEN ? { ANTHROPIC_AUTH_TOKEN: env.ANTHROPIC_AUTH_TOKEN } : {}
288
+ ANTHROPIC_API_KEY: authenticationEnvironment.ANTHROPIC_API_KEY,
289
+ ...authenticationEnvironment.ANTHROPIC_BASE_URL ? {
290
+ ANTHROPIC_BASE_URL: authenticationEnvironment.ANTHROPIC_BASE_URL
291
+ } : {},
292
+ ...authenticationEnvironment.ANTHROPIC_AUTH_TOKEN ? {
293
+ ANTHROPIC_AUTH_TOKEN: authenticationEnvironment.ANTHROPIC_AUTH_TOKEN
294
+ } : {}
203
295
  };
204
296
  }
205
297
  return {};
206
298
  case "custom": {
207
299
  const result = {};
208
- for (const [key, value] of Object.entries(env)) {
300
+ for (const [key, value] of Object.entries(authenticationEnvironment)) {
209
301
  if (value && (key.endsWith("_API_KEY") || key.endsWith("_BASE_URL") || key === "ANTHROPIC_AUTH_TOKEN")) {
210
302
  result[key] = value;
211
303
  }
@@ -232,7 +324,7 @@ function resolvePiEnv({
232
324
  };
233
325
  }
234
326
  const ambient = {};
235
- for (const [key, value] of Object.entries(env)) {
327
+ for (const [key, value] of Object.entries(authenticationEnvironment)) {
236
328
  if (value && (key.endsWith("_API_KEY") || key.endsWith("_BASE_URL") || key === "ANTHROPIC_AUTH_TOKEN")) {
237
329
  ambient[key] = value;
238
330
  }
@@ -245,21 +337,14 @@ async function registerPiProviders({
245
337
  registries,
246
338
  clientApp = HARNESS_CLIENT_APP
247
339
  }) {
248
- const normalizedOptions = normalizePiAuthToLegacyAuth(options);
249
- if (hasConfiguredValue(normalizedOptions?.customEnv)) {
250
- await registerCustomProviders({
251
- customEnv: normalizedOptions.customEnv ?? {},
252
- registries,
253
- clientApp
254
- });
255
- return;
256
- }
257
- const mode = typeof options === "string" ? options : options == null ? "auto" : "legacy";
340
+ const suppliedEnvironment = isHarnessAuthenticationEnvironment(options);
341
+ const authenticationEnvironment = suppliedEnvironment ? options : process.env;
342
+ const mode = typeof options === "string" ? options : "auto";
258
343
  switch (mode) {
259
344
  case "openai": {
260
345
  const env = pickOpenAIEnv(resolvedEnv);
261
346
  await registerCustomProviders({
262
- customEnv: { ...pickOpenAIEnv(process.env), ...env },
347
+ customEnv: { ...pickOpenAIEnv(authenticationEnvironment), ...env },
263
348
  registries,
264
349
  clientApp
265
350
  });
@@ -268,7 +353,7 @@ async function registerPiProviders({
268
353
  case "anthropic": {
269
354
  const env = pickAnthropicEnv(resolvedEnv);
270
355
  await registerCustomProviders({
271
- customEnv: { ...pickAnthropicEnv(process.env), ...env },
356
+ customEnv: { ...pickAnthropicEnv(authenticationEnvironment), ...env },
272
357
  registries,
273
358
  clientApp
274
359
  });
@@ -277,14 +362,16 @@ async function registerPiProviders({
277
362
  case "custom": {
278
363
  const env = pickProviderEnv(resolvedEnv);
279
364
  await registerCustomProviders({
280
- customEnv: { ...pickProviderEnv(process.env), ...env },
365
+ customEnv: { ...pickProviderEnv(authenticationEnvironment), ...env },
281
366
  registries,
282
367
  clientApp
283
368
  });
284
369
  return;
285
370
  }
286
371
  case "ai-gateway": {
287
- const gatewayAuth = getAiGatewayAuthFromEnv({ env: process.env });
372
+ const gatewayAuth = getAiGatewayAuthFromEnv({
373
+ env: authenticationEnvironment
374
+ });
288
375
  const gatewayApiKey = resolvedEnv.AI_GATEWAY_API_KEY ?? gatewayAuth.apiKey;
289
376
  const gatewayBaseUrl = resolvedEnv.AI_GATEWAY_BASE_URL ?? gatewayAuth.baseUrl;
290
377
  if (!gatewayApiKey) return;
@@ -300,12 +387,11 @@ async function registerPiProviders({
300
387
  });
301
388
  return;
302
389
  }
303
- case "legacy":
304
- break;
305
- // handled below
306
390
  case "auto":
307
391
  default: {
308
- const gatewayAuth = getAiGatewayAuthFromEnv({ env: process.env });
392
+ const gatewayAuth = getAiGatewayAuthFromEnv({
393
+ env: authenticationEnvironment
394
+ });
309
395
  const gatewayApiKey = resolvedEnv.AI_GATEWAY_API_KEY ?? gatewayAuth.apiKey;
310
396
  const gatewayBaseUrl = resolvedEnv.AI_GATEWAY_BASE_URL ?? gatewayAuth.baseUrl;
311
397
  if (gatewayApiKey) {
@@ -323,22 +409,13 @@ async function registerPiProviders({
323
409
  }
324
410
  const env = pickProviderEnv(resolvedEnv);
325
411
  await registerCustomProviders({
326
- customEnv: { ...pickProviderEnv(process.env), ...env },
412
+ customEnv: { ...pickProviderEnv(authenticationEnvironment), ...env },
327
413
  registries,
328
414
  clientApp
329
415
  });
330
416
  return;
331
417
  }
332
418
  }
333
- const apiKey = resolvedEnv.AI_GATEWAY_API_KEY;
334
- const baseUrl = resolvedEnv.AI_GATEWAY_BASE_URL;
335
- if (!apiKey || !baseUrl) return;
336
- await register({
337
- registries,
338
- provider: "vercel-ai-gateway",
339
- apiKey,
340
- config: createGatewayProviderConfig({ apiKey, baseUrl, clientApp })
341
- });
342
419
  }
343
420
  function pickOpenAIEnv(env) {
344
421
  const result = {};
@@ -364,37 +441,6 @@ function pickProviderEnv(env) {
364
441
  }
365
442
  return result;
366
443
  }
367
- function normalizePiAuthToLegacyAuth(options) {
368
- if (options == null || options === "auto") {
369
- return void 0;
370
- }
371
- if (typeof options === "string") {
372
- switch (options) {
373
- case "ai-gateway":
374
- return { gateway: {} };
375
- case "custom":
376
- case "openai":
377
- case "anthropic":
378
- return { customEnv: {} };
379
- default:
380
- return void 0;
381
- }
382
- }
383
- console.warn(
384
- '[pi] Passing an object to auth options is deprecated. Use a string mode ("auto" | "openai" | "anthropic" | "custom" | "ai-gateway") instead, and pass credentials via environment variables.'
385
- );
386
- return options;
387
- }
388
- function resolveCustomEnv({
389
- customEnv
390
- }) {
391
- const apiKey = customEnv.AI_GATEWAY_API_KEY;
392
- if (!apiKey) return {};
393
- return {
394
- AI_GATEWAY_API_KEY: apiKey,
395
- AI_GATEWAY_BASE_URL: customEnv.AI_GATEWAY_BASE_URL ?? DEFAULT_GATEWAY_BASE_URL
396
- };
397
- }
398
444
  async function registerCustomProviders({
399
445
  customEnv,
400
446
  registries,
@@ -1954,10 +2000,10 @@ async function createPiSession(input) {
1954
2000
  readableRoots: sandboxSkillRootDir ? [{ sandboxDir: sandboxSkillRootDir }] : []
1955
2001
  });
1956
2002
  const agentDir = input.agentDir ?? hostAgentDir;
1957
- const modelRuntime = await ModelRuntime.create({
2003
+ const modelRuntime = await createPiModelRuntime({
2004
+ auth: input.settings.auth,
1958
2005
  authPath: path7.join(agentDir, "auth.json"),
1959
- modelsPath: path7.join(agentDir, "models.json"),
1960
- allowModelNetwork: false
2006
+ modelsPath: path7.join(agentDir, "models.json")
1961
2007
  });
1962
2008
  const modelRegistry = new ModelRegistry(modelRuntime);
1963
2009
  const settingsManager = input.agentDir != null ? SettingsManager.create(hostWorkDir, agentDir) : SettingsManager.inMemory();