@ai-sdk/harness-pi 1.0.37 → 1.0.39

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/harness-pi",
3
- "version": "1.0.37",
3
+ "version": "1.0.39",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -26,10 +26,10 @@
26
26
  }
27
27
  },
28
28
  "dependencies": {
29
- "@earendil-works/pi-coding-agent": "^0.79.0",
29
+ "@earendil-works/pi-coding-agent": "^0.80.10",
30
30
  "typebox": "^1.1.38",
31
- "@ai-sdk/harness": "1.0.37",
32
- "@ai-sdk/provider-utils": "5.0.11"
31
+ "@ai-sdk/harness": "1.0.39",
32
+ "@ai-sdk/provider-utils": "5.0.12"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "zod": "^3.25.76 || ^4.1.8"
package/src/pi-auth.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type {
2
- AuthStorage,
3
2
  ModelRegistry,
3
+ ModelRuntime,
4
4
  } from '@earendil-works/pi-coding-agent';
5
5
  import { getAiGatewayAuthFromEnv } from '@ai-sdk/harness/utils';
6
6
  import { VERSION } from './version';
@@ -56,14 +56,24 @@ function createGatewayProviderConfig({
56
56
  };
57
57
  }
58
58
 
59
- function register(
60
- registries: { authStorage: AuthStorage; modelRegistry: ModelRegistry },
61
- provider: string,
62
- apiKey: string,
63
- config: ProviderConfigInput,
64
- ): void {
65
- registries.authStorage.setRuntimeApiKey(provider, apiKey);
59
+ type PiRegistries = {
60
+ modelRegistry: ModelRegistry;
61
+ modelRuntime: ModelRuntime;
62
+ };
63
+
64
+ async function register({
65
+ registries,
66
+ provider,
67
+ apiKey,
68
+ config,
69
+ }: {
70
+ registries: PiRegistries;
71
+ provider: string;
72
+ apiKey: string;
73
+ config: ProviderConfigInput;
74
+ }): Promise<void> {
66
75
  registries.modelRegistry.registerProvider(provider, config);
76
+ await registries.modelRuntime.setRuntimeApiKey(provider, apiKey);
67
77
  }
68
78
 
69
79
  function hasConfiguredValue(value: unknown): boolean {
@@ -76,21 +86,13 @@ function hasConfiguredValue(value: unknown): boolean {
76
86
  export function resolvePiEnv({
77
87
  options,
78
88
  env,
79
- registries,
80
- clientApp = HARNESS_CLIENT_APP,
81
89
  }: {
82
90
  options: PiAuthOptions | undefined;
83
91
  env: NodeJS.ProcessEnv;
84
- registries: { authStorage: AuthStorage; modelRegistry: ModelRegistry };
85
- clientApp?: string;
86
92
  }): Record<string, string> {
87
93
  const customEnvConfigured = hasConfiguredValue(options?.customEnv);
88
94
  if (customEnvConfigured) {
89
- return applyCustomEnv({
90
- customEnv: options!.customEnv ?? {},
91
- registries,
92
- clientApp,
93
- });
95
+ return resolveCustomEnv({ customEnv: options!.customEnv ?? {} });
94
96
  }
95
97
 
96
98
  const gatewayConfigured = hasConfiguredValue(options?.gateway);
@@ -99,16 +101,6 @@ export function resolvePiEnv({
99
101
  const apiKey = options!.gateway?.apiKey ?? gatewayAuthFromEnv.apiKey;
100
102
  const baseUrl = options!.gateway?.baseUrl ?? gatewayAuthFromEnv.baseUrl;
101
103
  if (apiKey) {
102
- register(
103
- registries,
104
- 'vercel-ai-gateway',
105
- apiKey,
106
- createGatewayProviderConfig({
107
- apiKey,
108
- baseUrl,
109
- clientApp,
110
- }),
111
- );
112
104
  return { AI_GATEWAY_API_KEY: apiKey, AI_GATEWAY_BASE_URL: baseUrl };
113
105
  }
114
106
  return {};
@@ -116,16 +108,6 @@ export function resolvePiEnv({
116
108
 
117
109
  // Ambient gateway fallback.
118
110
  if (gatewayAuthFromEnv.apiKey) {
119
- register(
120
- registries,
121
- 'vercel-ai-gateway',
122
- gatewayAuthFromEnv.apiKey,
123
- createGatewayProviderConfig({
124
- apiKey: gatewayAuthFromEnv.apiKey,
125
- baseUrl: gatewayAuthFromEnv.baseUrl,
126
- clientApp,
127
- }),
128
- );
129
111
  return {
130
112
  AI_GATEWAY_API_KEY: gatewayAuthFromEnv.apiKey,
131
113
  AI_GATEWAY_BASE_URL: gatewayAuthFromEnv.baseUrl,
@@ -135,55 +117,108 @@ export function resolvePiEnv({
135
117
  return {};
136
118
  }
137
119
 
138
- function applyCustomEnv({
120
+ export async function registerPiProviders({
121
+ options,
122
+ resolvedEnv,
123
+ registries,
124
+ clientApp = HARNESS_CLIENT_APP,
125
+ }: {
126
+ options: PiAuthOptions | undefined;
127
+ resolvedEnv: Record<string, string>;
128
+ registries: PiRegistries;
129
+ clientApp?: string;
130
+ }): Promise<void> {
131
+ if (hasConfiguredValue(options?.customEnv)) {
132
+ await registerCustomProviders({
133
+ customEnv: options!.customEnv ?? {},
134
+ registries,
135
+ clientApp,
136
+ });
137
+ return;
138
+ }
139
+
140
+ const apiKey = resolvedEnv.AI_GATEWAY_API_KEY;
141
+ const baseUrl = resolvedEnv.AI_GATEWAY_BASE_URL;
142
+ if (!apiKey || !baseUrl) return;
143
+
144
+ await register({
145
+ registries,
146
+ provider: 'vercel-ai-gateway',
147
+ apiKey,
148
+ config: createGatewayProviderConfig({ apiKey, baseUrl, clientApp }),
149
+ });
150
+ }
151
+
152
+ function resolveCustomEnv({
153
+ customEnv,
154
+ }: {
155
+ customEnv: Record<string, string>;
156
+ }): Record<string, string> {
157
+ const apiKey = customEnv.AI_GATEWAY_API_KEY;
158
+ if (!apiKey) return {};
159
+
160
+ return {
161
+ AI_GATEWAY_API_KEY: apiKey,
162
+ AI_GATEWAY_BASE_URL:
163
+ customEnv.AI_GATEWAY_BASE_URL ?? DEFAULT_GATEWAY_BASE_URL,
164
+ };
165
+ }
166
+
167
+ async function registerCustomProviders({
139
168
  customEnv,
140
169
  registries,
141
170
  clientApp,
142
171
  }: {
143
172
  customEnv: Record<string, string>;
144
- registries: { authStorage: AuthStorage; modelRegistry: ModelRegistry };
173
+ registries: PiRegistries;
145
174
  clientApp: string;
146
- }): Record<string, string> {
147
- const out: Record<string, string> = {};
148
-
175
+ }): Promise<void> {
149
176
  const gatewayKey = customEnv.AI_GATEWAY_API_KEY;
150
177
  if (gatewayKey) {
151
178
  const baseUrl = customEnv.AI_GATEWAY_BASE_URL ?? DEFAULT_GATEWAY_BASE_URL;
152
- register(
179
+ await register({
153
180
  registries,
154
- 'vercel-ai-gateway',
155
- gatewayKey,
156
- createGatewayProviderConfig({
181
+ provider: 'vercel-ai-gateway',
182
+ apiKey: gatewayKey,
183
+ config: createGatewayProviderConfig({
157
184
  apiKey: gatewayKey,
158
185
  baseUrl,
159
186
  clientApp,
160
187
  }),
161
- );
162
- out.AI_GATEWAY_API_KEY = gatewayKey;
163
- out.AI_GATEWAY_BASE_URL = baseUrl;
188
+ });
164
189
  }
165
190
 
166
191
  if (customEnv.OPENAI_API_KEY) {
167
192
  const baseUrl = customEnv.OPENAI_BASE_URL ?? DEFAULT_OPENAI_BASE_URL;
168
- register(registries, 'openai', customEnv.OPENAI_API_KEY, {
193
+ await register({
194
+ registries,
195
+ provider: 'openai',
169
196
  apiKey: customEnv.OPENAI_API_KEY,
170
- baseUrl,
171
- authHeader: true,
197
+ config: {
198
+ apiKey: customEnv.OPENAI_API_KEY,
199
+ baseUrl,
200
+ authHeader: true,
201
+ },
172
202
  });
173
203
  }
174
204
 
175
205
  if (customEnv.ANTHROPIC_API_KEY) {
176
206
  const baseUrl = customEnv.ANTHROPIC_BASE_URL ?? DEFAULT_ANTHROPIC_BASE_URL;
177
- register(registries, 'anthropic', customEnv.ANTHROPIC_API_KEY, {
207
+ await register({
208
+ registries,
209
+ provider: 'anthropic',
178
210
  apiKey: customEnv.ANTHROPIC_API_KEY,
179
- baseUrl,
180
- ...(customEnv.ANTHROPIC_AUTH_TOKEN
181
- ? {
182
- headers: {
183
- authorization: `Bearer ${customEnv.ANTHROPIC_AUTH_TOKEN}`,
184
- },
185
- }
186
- : {}),
211
+ config: {
212
+ apiKey: customEnv.ANTHROPIC_API_KEY,
213
+ baseUrl,
214
+ ...(customEnv.ANTHROPIC_AUTH_TOKEN
215
+ ? {
216
+ headers: {
217
+ authorization: `Bearer ${customEnv.ANTHROPIC_AUTH_TOKEN}`,
218
+ },
219
+ }
220
+ : {}),
221
+ },
187
222
  });
188
223
  }
189
224
 
@@ -204,12 +239,15 @@ function applyCustomEnv({
204
239
  if (!baseUrl) {
205
240
  continue;
206
241
  }
207
- register(registries, provider, apiKey, {
242
+ await register({
243
+ registries,
244
+ provider,
208
245
  apiKey,
209
- baseUrl,
210
- authHeader: true,
246
+ config: {
247
+ apiKey,
248
+ baseUrl,
249
+ authHeader: true,
250
+ },
211
251
  });
212
252
  }
213
-
214
- return out;
215
253
  }
@@ -11,10 +11,13 @@ type PiModel = ReturnType<ModelRegistry['getAll']>[number];
11
11
  */
12
12
  export const DEFAULT_PI_GATEWAY_MODEL_ID = 'anthropic/claude-sonnet-4.6';
13
13
 
14
- export function createPiModelResolver(
15
- modelRegistry: ModelRegistry,
16
- env: NodeJS.ProcessEnv = process.env,
17
- ) {
14
+ export function createPiModelResolver({
15
+ modelRegistry,
16
+ env = process.env,
17
+ }: {
18
+ modelRegistry: ModelRegistry;
19
+ env?: NodeJS.ProcessEnv;
20
+ }) {
18
21
  let cachedModels: PiModel[] | undefined;
19
22
 
20
23
  const loadModels = (): PiModel[] => {
package/src/pi-session.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
- AuthStorage,
3
2
  createAgentSession,
4
3
  DefaultResourceLoader,
5
4
  defineTool,
6
5
  ModelRegistry,
6
+ ModelRuntime,
7
7
  SessionManager,
8
8
  SettingsManager,
9
9
  type AgentSession,
@@ -30,7 +30,11 @@ import {
30
30
  type HarnessV1ToolSpec,
31
31
  } from '@ai-sdk/harness';
32
32
  import { resolveSandboxHomeDir } from '@ai-sdk/harness/utils';
33
- import { resolvePiEnv, type PiAuthOptions } from './pi-auth';
33
+ import {
34
+ registerPiProviders,
35
+ resolvePiEnv,
36
+ type PiAuthOptions,
37
+ } from './pi-auth';
34
38
  import { getPiTerminalError, parseNativeEvent } from './pi-events';
35
39
  import { createPiModelResolver } from './pi-model-resolver';
36
40
  import { createPiPathMapper } from './pi-paths';
@@ -327,15 +331,16 @@ export async function createPiSession(
327
331
  });
328
332
 
329
333
  // Pi auth + model registry are global to this Pi session. These live on the
330
- // real host filesystem (`hostAgentDir`), never in the sandbox/workspace.
334
+ // real host filesystem, never in the sandbox/workspace.
331
335
  // When `agentDir` is provided, use it instead so the harness can reuse
332
336
  // existing CLI logins and model/settings config.
333
337
  const agentDir = input.agentDir ?? hostAgentDir;
334
- const authStorage = AuthStorage.create(path.join(agentDir, 'auth.json'));
335
- const modelRegistry = ModelRegistry.create(
336
- authStorage,
337
- path.join(agentDir, 'models.json'),
338
- );
338
+ const modelRuntime = await ModelRuntime.create({
339
+ authPath: path.join(agentDir, 'auth.json'),
340
+ modelsPath: path.join(agentDir, 'models.json'),
341
+ allowModelNetwork: false,
342
+ });
343
+ const modelRegistry = new ModelRegistry(modelRuntime);
339
344
  const settingsManager =
340
345
  input.agentDir != null
341
346
  ? SettingsManager.create(hostWorkDir, agentDir)
@@ -345,13 +350,20 @@ export async function createPiSession(
345
350
  const resolverEnv = resolvePiEnv({
346
351
  options: input.settings.auth,
347
352
  env: process.env,
353
+ });
354
+ await registerPiProviders({
355
+ options: input.settings.auth,
356
+ resolvedEnv: resolverEnv,
348
357
  registries: {
349
- authStorage,
350
358
  modelRegistry,
359
+ modelRuntime,
351
360
  },
352
361
  clientApp: input.clientApp,
353
362
  });
354
- const resolveModel = createPiModelResolver(modelRegistry, resolverEnv);
363
+ const resolveModel = createPiModelResolver({
364
+ modelRegistry,
365
+ env: resolverEnv,
366
+ });
355
367
  // Resolve once: deterministic given the configured model. This is the Pi
356
368
  // `Model` object handed to `createAgentSession`.
357
369
  const resolvedModel = resolveModel(input.settings.model);
@@ -592,8 +604,7 @@ export async function createPiSession(
592
604
  const { session } = await createAgentSession({
593
605
  cwd: sessionWorkDir,
594
606
  agentDir: hostAgentDir,
595
- authStorage,
596
- modelRegistry,
607
+ modelRuntime,
597
608
  sessionManager,
598
609
  settingsManager,
599
610
  resourceLoader,