@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/CHANGELOG.md +17 -0
- package/dist/index.js +101 -67
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/pi-auth.ts +104 -66
- package/src/pi-model-resolver.ts +7 -4
- package/src/pi-session.ts +23 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/harness-pi",
|
|
3
|
-
"version": "1.0.
|
|
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.
|
|
29
|
+
"@earendil-works/pi-coding-agent": "^0.80.10",
|
|
30
30
|
"typebox": "^1.1.38",
|
|
31
|
-
"@ai-sdk/harness": "1.0.
|
|
32
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
registries
|
|
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
|
|
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
|
|
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:
|
|
173
|
+
registries: PiRegistries;
|
|
145
174
|
clientApp: string;
|
|
146
|
-
}):
|
|
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(
|
|
193
|
+
await register({
|
|
194
|
+
registries,
|
|
195
|
+
provider: 'openai',
|
|
169
196
|
apiKey: customEnv.OPENAI_API_KEY,
|
|
170
|
-
|
|
171
|
-
|
|
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(
|
|
207
|
+
await register({
|
|
208
|
+
registries,
|
|
209
|
+
provider: 'anthropic',
|
|
178
210
|
apiKey: customEnv.ANTHROPIC_API_KEY,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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(
|
|
242
|
+
await register({
|
|
243
|
+
registries,
|
|
244
|
+
provider,
|
|
208
245
|
apiKey,
|
|
209
|
-
|
|
210
|
-
|
|
246
|
+
config: {
|
|
247
|
+
apiKey,
|
|
248
|
+
baseUrl,
|
|
249
|
+
authHeader: true,
|
|
250
|
+
},
|
|
211
251
|
});
|
|
212
252
|
}
|
|
213
|
-
|
|
214
|
-
return out;
|
|
215
253
|
}
|
package/src/pi-model-resolver.ts
CHANGED
|
@@ -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
|
|
16
|
-
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 {
|
|
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
|
|
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
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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(
|
|
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
|
-
|
|
596
|
-
modelRegistry,
|
|
607
|
+
modelRuntime,
|
|
597
608
|
sessionManager,
|
|
598
609
|
settingsManager,
|
|
599
610
|
resourceLoader,
|