@ai-sdk/harness-pi 1.0.109 → 1.0.111
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 +23 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.js +65 -18
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/pi-harness.ts +14 -4
- package/src/pi-model-resolver.ts +9 -7
- package/src/pi-session.ts +29 -5
- package/src/pi-subscription.ts +27 -0
- package/src/pi-translate.ts +8 -2
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.111",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@ai-sdk/harness": "1.0.
|
|
30
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
29
|
+
"@ai-sdk/harness": "1.0.109",
|
|
30
|
+
"@ai-sdk/provider-utils": "5.0.40",
|
|
31
31
|
"@earendil-works/pi-ai": "0.74.2",
|
|
32
32
|
"@earendil-works/pi-coding-agent": "^0.84.3",
|
|
33
33
|
"pi-mcp-adapter": "2.12.1",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"zod": "^3.25.76 || ^4.1.8"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@ai-sdk/sandbox-just-bash": "1.0.
|
|
40
|
+
"@ai-sdk/sandbox-just-bash": "1.0.109",
|
|
41
41
|
"@types/node": "22.19.19",
|
|
42
42
|
"@vercel/ai-tsconfig": "0.0.0",
|
|
43
43
|
"tsup": "^8.5.1",
|
package/src/pi-harness.ts
CHANGED
|
@@ -4,7 +4,10 @@ import {
|
|
|
4
4
|
type HarnessV1BuiltinTool,
|
|
5
5
|
} from '@ai-sdk/harness';
|
|
6
6
|
import { tool } from '@ai-sdk/provider-utils';
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
ExtensionFactory,
|
|
9
|
+
ProviderConfig,
|
|
10
|
+
} from '@earendil-works/pi-coding-agent';
|
|
8
11
|
import { z } from 'zod/v4';
|
|
9
12
|
import type { PiAuthenticationMode } from './pi-auth';
|
|
10
13
|
import { piResumeStateSchema } from './pi-resume-state';
|
|
@@ -23,6 +26,12 @@ const PI_CLIENT_APP = `ai-sdk/harness-pi/${VERSION}`;
|
|
|
23
26
|
export type PiHarnessSettings = {
|
|
24
27
|
/** Where Pi sources API keys / gateway credentials from. */
|
|
25
28
|
readonly auth?: PiAuthenticationMode;
|
|
29
|
+
/**
|
|
30
|
+
* Explicit Pi provider configurations keyed by provider id. Use this to
|
|
31
|
+
* register custom models and their API protocol without coupling model
|
|
32
|
+
* metadata to authentication environment variables.
|
|
33
|
+
*/
|
|
34
|
+
readonly providers?: Readonly<Record<string, ProviderConfig>>;
|
|
26
35
|
/**
|
|
27
36
|
* Pi's extended-thinking budget level. Maps directly to the SDK's
|
|
28
37
|
* `thinkingLevel` option on `createAgentSession`.
|
|
@@ -30,9 +39,9 @@ export type PiHarnessSettings = {
|
|
|
30
39
|
readonly thinkingLevel?: PiThinkingLevel;
|
|
31
40
|
/**
|
|
32
41
|
* Directory holding Pi's global agent config (auth.json, models.json,
|
|
33
|
-
* settings.json). When omitted,
|
|
34
|
-
*
|
|
35
|
-
*
|
|
42
|
+
* settings.json). When omitted, native subscription auth is discovered from
|
|
43
|
+
* Pi's default agent directory while model and general settings remain
|
|
44
|
+
* isolated per session.
|
|
36
45
|
*/
|
|
37
46
|
readonly agentDir?: string;
|
|
38
47
|
/**
|
|
@@ -149,6 +158,7 @@ export function createPi(
|
|
|
149
158
|
? { thinkingLevel: settings.thinkingLevel }
|
|
150
159
|
: {}),
|
|
151
160
|
...(settings.mcpServers ? { mcpServers: settings.mcpServers } : {}),
|
|
161
|
+
...(settings.providers ? { providers: settings.providers } : {}),
|
|
152
162
|
...(settings.extensionFactories
|
|
153
163
|
? { extensionFactories: settings.extensionFactories }
|
|
154
164
|
: {}),
|
package/src/pi-model-resolver.ts
CHANGED
|
@@ -77,13 +77,15 @@ export function createPiModelResolver({
|
|
|
77
77
|
if (gatewayMatch) return gatewayMatch;
|
|
78
78
|
|
|
79
79
|
const scopedMatch = findScopedMatch(effectiveId, models);
|
|
80
|
-
if (scopedMatch &&
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
if (scopedMatch && modelRegistry.hasConfiguredAuth(scopedMatch)) {
|
|
81
|
+
return scopedMatch;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const authenticatedFlatMatches = models.filter(
|
|
85
|
+
m => matches(m) && modelRegistry.hasConfiguredAuth(m),
|
|
86
|
+
);
|
|
87
|
+
if (authenticatedFlatMatches.length === 1) {
|
|
88
|
+
return authenticatedFlatMatches[0];
|
|
87
89
|
}
|
|
88
90
|
|
|
89
91
|
return scopedMatch ?? models.find(matches);
|
package/src/pi-session.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type AgentSession,
|
|
9
9
|
type AgentToolResult,
|
|
10
10
|
type ExtensionFactory,
|
|
11
|
+
type ProviderConfig,
|
|
11
12
|
type Skill,
|
|
12
13
|
type ToolDefinition,
|
|
13
14
|
} from '@earendil-works/pi-coding-agent';
|
|
@@ -43,6 +44,7 @@ import {
|
|
|
43
44
|
resolvePiEnv,
|
|
44
45
|
type PiAuthenticationMode,
|
|
45
46
|
} from './pi-auth';
|
|
47
|
+
import { resolvePiSubscriptionAgentDir } from './pi-subscription';
|
|
46
48
|
import { getPiTerminalError, parseNativeEvent } from './pi-events';
|
|
47
49
|
import { createPiModelResolver } from './pi-model-resolver';
|
|
48
50
|
import { createPiPathMapper } from './pi-paths';
|
|
@@ -222,6 +224,7 @@ export interface PiSessionSettings {
|
|
|
222
224
|
readonly headers?: Readonly<Record<string, string>>;
|
|
223
225
|
readonly thinkingLevel?: PiThinkingLevel;
|
|
224
226
|
readonly mcpServers?: Record<string, unknown>;
|
|
227
|
+
readonly providers?: Readonly<Record<string, ProviderConfig>>;
|
|
225
228
|
readonly extensionFactories?: ReadonlyArray<ExtensionFactory>;
|
|
226
229
|
}
|
|
227
230
|
|
|
@@ -238,9 +241,9 @@ export interface CreatePiSessionInput {
|
|
|
238
241
|
readonly abortSignal?: AbortSignal;
|
|
239
242
|
/**
|
|
240
243
|
* Directory holding Pi's global agent config (auth.json, models.json,
|
|
241
|
-
* settings.json).
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
+
* settings.json). Native auth from this directory is considered after
|
|
245
|
+
* applicable environment credentials. Model and general settings are only
|
|
246
|
+
* reused when this option is explicit.
|
|
244
247
|
*/
|
|
245
248
|
readonly agentDir?: string;
|
|
246
249
|
}
|
|
@@ -428,9 +431,14 @@ export async function createPiSession(
|
|
|
428
431
|
* outside that record. General Pi settings still use agentDir below.
|
|
429
432
|
*/
|
|
430
433
|
const agentDir = input.agentDir ?? hostAgentDir;
|
|
434
|
+
const nativeAgentDir = resolvePiSubscriptionAgentDir({
|
|
435
|
+
options: input.settings.auth,
|
|
436
|
+
env: process.env,
|
|
437
|
+
agentDir: input.agentDir,
|
|
438
|
+
});
|
|
431
439
|
const modelRuntime = await createPiModelRuntime({
|
|
432
440
|
auth: input.settings.auth,
|
|
433
|
-
authPath: path.join(
|
|
441
|
+
authPath: path.join(nativeAgentDir ?? hostAgentDir, 'auth.json'),
|
|
434
442
|
modelsPath: path.join(agentDir, 'models.json'),
|
|
435
443
|
});
|
|
436
444
|
const modelRegistry = new ModelRegistry(modelRuntime);
|
|
@@ -454,6 +462,14 @@ export async function createPiSession(
|
|
|
454
462
|
clientApp: input.clientApp,
|
|
455
463
|
headers: input.settings.headers,
|
|
456
464
|
});
|
|
465
|
+
for (const [provider, config] of Object.entries(
|
|
466
|
+
input.settings.providers ?? {},
|
|
467
|
+
)) {
|
|
468
|
+
modelRegistry.registerProvider(provider, {
|
|
469
|
+
...modelRegistry.getRegisteredProviderConfig(provider),
|
|
470
|
+
...config,
|
|
471
|
+
});
|
|
472
|
+
}
|
|
457
473
|
const resolveModel = createPiModelResolver({
|
|
458
474
|
modelRegistry,
|
|
459
475
|
env: resolverEnv,
|
|
@@ -1468,13 +1484,21 @@ export async function createPiSession(
|
|
|
1468
1484
|
if (stopped) {
|
|
1469
1485
|
throw new Error('Pi session has been stopped.');
|
|
1470
1486
|
}
|
|
1487
|
+
if (piSession == null) {
|
|
1488
|
+
await rebuildPiSession([], true);
|
|
1489
|
+
lastToolsSignature = JSON.stringify([]);
|
|
1490
|
+
}
|
|
1491
|
+
const session = piSession;
|
|
1492
|
+
if (session == null) {
|
|
1493
|
+
throw new Error('Pi session failed to initialize.');
|
|
1494
|
+
}
|
|
1471
1495
|
/*
|
|
1472
1496
|
* Pi owns the compaction. We just request it; the resulting
|
|
1473
1497
|
* `compaction_end` event is observed by the session subscription and
|
|
1474
1498
|
* translated into a `compaction` stream part. The returned
|
|
1475
1499
|
* `CompactionResult` is intentionally discarded here.
|
|
1476
1500
|
*/
|
|
1477
|
-
await
|
|
1501
|
+
await session.compact(customInstructions);
|
|
1478
1502
|
},
|
|
1479
1503
|
|
|
1480
1504
|
doDestroy: async () => {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { isHarnessAuthenticationEnvironment } from '@ai-sdk/harness/utils';
|
|
4
|
+
import { resolvePiEnv, type PiAuthenticationMode } from './pi-auth';
|
|
5
|
+
|
|
6
|
+
export function resolvePiSubscriptionAgentDir({
|
|
7
|
+
options,
|
|
8
|
+
env,
|
|
9
|
+
agentDir,
|
|
10
|
+
homeDirectory = homedir(),
|
|
11
|
+
}: {
|
|
12
|
+
options: PiAuthenticationMode | undefined;
|
|
13
|
+
env: NodeJS.ProcessEnv;
|
|
14
|
+
agentDir?: string;
|
|
15
|
+
homeDirectory?: string;
|
|
16
|
+
}): string | undefined {
|
|
17
|
+
if (isHarnessAuthenticationEnvironment(options) || options === 'ai-gateway') {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const resolvedEnvironment = resolvePiEnv({ options, env });
|
|
21
|
+
if (resolvedEnvironment.AI_GATEWAY_API_KEY != null) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return resolve(
|
|
25
|
+
agentDir ?? env.PI_CODING_AGENT_DIR ?? `${homeDirectory}/.pi/agent`,
|
|
26
|
+
);
|
|
27
|
+
}
|
package/src/pi-translate.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
2
|
import type { HarnessV1StreamPart } from '@ai-sdk/harness';
|
|
3
3
|
import { secureJsonParse } from '@ai-sdk/provider-utils';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
extractAssistantText,
|
|
6
|
+
getPiTerminalError,
|
|
7
|
+
type PiSessionEvent,
|
|
8
|
+
} from './pi-events';
|
|
5
9
|
import { serializeToolOutput } from './pi-utils';
|
|
6
10
|
|
|
7
11
|
/**
|
|
@@ -411,7 +415,9 @@ export function translatePiEvent(
|
|
|
411
415
|
} else {
|
|
412
416
|
state.pendingStepToolCallIds.clear();
|
|
413
417
|
state.stepToolCallCount = undefined;
|
|
414
|
-
|
|
418
|
+
if (!getPiTerminalError(event)) {
|
|
419
|
+
parts.push(...finishStep(state));
|
|
420
|
+
}
|
|
415
421
|
}
|
|
416
422
|
return parts;
|
|
417
423
|
}
|