@rivus/runtime 0.16.6 → 0.18.0
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/LICENSE +674 -21
- package/README.md +21 -0
- package/dist/acp.d.ts +7 -92
- package/dist/acp.js +298 -249
- package/dist/chunks/agent-loop.d.ts +143 -95
- package/dist/chunks/agent-user-input.js +109 -0
- package/dist/chunks/rivus-runtime-tool.d.ts +104 -4
- package/dist/chunks/{tool-input-digest.js → tool-risk-policy.js} +7 -14
- package/dist/chunks/user-input-xml.js +224 -0
- package/dist/index.d.ts +107 -823
- package/dist/index.js +242 -705
- package/dist/pi.d.ts +62 -332
- package/dist/pi.js +547 -546
- package/package.json +12 -11
- package/dist/chunks/agent-loop.js +0 -164
package/dist/pi.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import {
|
|
1
|
+
import { a as createInvocationAuthority, r as createToolInputDigest$1, s as normalizeAgentInvocation, t as requiresToolApproval } from "./chunks/tool-risk-policy.js";
|
|
2
|
+
import { i as isAgentSteeringRequest, o as projectUserInputText } from "./chunks/agent-user-input.js";
|
|
3
|
+
import { a as createAgentLoopSkillExecutionStart, c as createAgentLoopToolExecutionEnd, i as createAgentLoopSkillExecutionEnd, l as createAgentLoopToolExecutionStart, n as createAgentLoopModelExecutionEnd, o as createAgentLoopTextDelta, r as createAgentLoopModelExecutionStart, s as createAgentLoopThinkingDelta, t as prepareAgentModelInput, u as createAgentLoopToolExecutionUpdate } from "./chunks/user-input-xml.js";
|
|
3
4
|
import { Effect, Stream } from "effect";
|
|
5
|
+
import { unwrapExit } from "@rivus/platform/effect";
|
|
4
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
7
|
import { readFile, realpath, stat } from "node:fs/promises";
|
|
6
8
|
import { isAbsolute, join, relative } from "node:path";
|
|
7
9
|
import { writeAtomicTextFile } from "@rivus/platform/persistence";
|
|
8
|
-
import { createSha256Digest } from "@rivus/platform/identity";
|
|
9
10
|
import { Type, Unsafe } from "typebox";
|
|
10
11
|
import { DefaultResourceLoader, ModelRuntime, SessionManager, SettingsManager, createAgentSession, createBashToolDefinition, createReadToolDefinition, defineTool, estimateTokens } from "@earendil-works/pi-coding-agent";
|
|
11
12
|
import { realpathSync, statSync } from "node:fs";
|
|
13
|
+
import { createSha256Digest } from "@rivus/platform/identity";
|
|
12
14
|
//#region src/adapters/pi/config/pi-model-overrides.ts
|
|
13
15
|
async function mergePiProviderBaseUrlOverride(options) {
|
|
14
16
|
await updatePiProviderOverride(options.filePath, options.provider, (providerOverride) => ({
|
|
@@ -140,13 +142,38 @@ function readSkillId$1(input) {
|
|
|
140
142
|
async function runPiSessionPrompts(options) {
|
|
141
143
|
const { input, handle } = options;
|
|
142
144
|
const { session } = handle;
|
|
143
|
-
const prepare = (text) =>
|
|
144
|
-
...input
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
const prepare = async (text, userInput, attachmentAccess, updateActiveInput = false) => {
|
|
146
|
+
const { attachmentAccess: _initialAccess, userInput: _initialUserInput, ...runInput } = input;
|
|
147
|
+
const preparedInput = {
|
|
148
|
+
...runInput,
|
|
149
|
+
text,
|
|
150
|
+
...attachmentAccess ? { attachmentAccess } : {},
|
|
151
|
+
...userInput ? { userInput } : {}
|
|
152
|
+
};
|
|
153
|
+
const prepared = await Effect.runPromise(prepareAgentModelInput(preparedInput, { allowImages: handle.supportsImages ?? session.model?.input?.includes("image") === true }));
|
|
154
|
+
if (updateActiveInput) await handle.updateInput?.(preparedInput);
|
|
155
|
+
if (!prepared.hasUsableContent || !handle.preparePrompt) return prepared;
|
|
156
|
+
return {
|
|
157
|
+
...prepared,
|
|
158
|
+
text: await handle.preparePrompt({
|
|
159
|
+
...preparedInput,
|
|
160
|
+
text: prepared.text,
|
|
161
|
+
...attachmentAccess ? { attachmentAccess } : {},
|
|
162
|
+
...userInput ? { userInput } : {}
|
|
163
|
+
})
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
const prepareInitial = async () => {
|
|
167
|
+
const prepared = await prepare(input.text, input.userInput, input.attachmentAccess);
|
|
168
|
+
if (input.abortSignal.aborted) return;
|
|
169
|
+
if (prepared.hasUsableContent) return { prepared };
|
|
170
|
+
return prepared.fallbackText === void 0 ? {} : { fallbackText: prepared.fallbackText };
|
|
171
|
+
};
|
|
147
172
|
if (!options.supportsSteering) {
|
|
148
|
-
const
|
|
149
|
-
if (!
|
|
173
|
+
const initial = await prepareInitial();
|
|
174
|
+
if (!initial) return;
|
|
175
|
+
if (!("prepared" in initial)) return initial.fallbackText;
|
|
176
|
+
await session.prompt(initial.prepared.text, toPromptOptions(initial.prepared.images));
|
|
150
177
|
return;
|
|
151
178
|
}
|
|
152
179
|
if (!session.steer || typeof session.isStreaming !== "boolean" || !session.clearQueue) throw new Error("Pi steering requires native steer, isStreaming and clearQueue capabilities");
|
|
@@ -155,17 +182,17 @@ async function runPiSessionPrompts(options) {
|
|
|
155
182
|
const onAbort = () => stop.abort();
|
|
156
183
|
input.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
157
184
|
if (input.abortSignal.aborted) stop.abort();
|
|
158
|
-
const close = () => Effect.runPromise(mailbox?.close?.() ?? Effect.succeed([]));
|
|
159
|
-
const take = () => Effect.runPromise(mailbox?.next() ?? Effect.never, { signal: stop.signal }).then((
|
|
185
|
+
const close = () => Effect.runPromise(mailbox?.closeInput?.() ?? mailbox?.close?.() ?? Effect.succeed([]));
|
|
186
|
+
const take = () => Effect.runPromise(mailbox?.nextInput?.() ?? mailbox?.next() ?? Effect.never, { signal: stop.signal }).then((input) => ({
|
|
160
187
|
type: "steering",
|
|
161
|
-
|
|
188
|
+
input
|
|
162
189
|
}), (error) => {
|
|
163
190
|
if (stop.signal.aborted) return { type: "closed" };
|
|
164
191
|
throw error;
|
|
165
192
|
});
|
|
166
193
|
let prompt = Promise.resolve({ type: "completed" });
|
|
167
|
-
const startPrompt = (text) => {
|
|
168
|
-
prompt = session.prompt(text).then(() => ({ type: "completed" }), (error) => ({
|
|
194
|
+
const startPrompt = (text, images = []) => {
|
|
195
|
+
prompt = session.prompt(text, toPromptOptions(images)).then(() => ({ type: "completed" }), (error) => ({
|
|
169
196
|
type: "failed",
|
|
170
197
|
error
|
|
171
198
|
}));
|
|
@@ -175,11 +202,13 @@ async function runPiSessionPrompts(options) {
|
|
|
175
202
|
if (outcome.type === "failed") throw outcome.error;
|
|
176
203
|
};
|
|
177
204
|
let next;
|
|
205
|
+
const fallbackTexts = [];
|
|
178
206
|
let succeeded = false;
|
|
179
207
|
try {
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
|
|
208
|
+
const initial = await prepareInitial();
|
|
209
|
+
if (!initial) return;
|
|
210
|
+
if (!("prepared" in initial)) return initial.fallbackText;
|
|
211
|
+
startPrompt(initial.prepared.text, initial.prepared.images);
|
|
183
212
|
next = take();
|
|
184
213
|
while (!input.abortSignal.aborted) {
|
|
185
214
|
const outcome = await Promise.race([next, prompt]);
|
|
@@ -189,23 +218,34 @@ async function runPiSessionPrompts(options) {
|
|
|
189
218
|
const remaining = await close();
|
|
190
219
|
stop.abort();
|
|
191
220
|
const claimed = await next;
|
|
192
|
-
const accepted = claimed.type === "steering" ? [claimed.
|
|
193
|
-
for (const
|
|
194
|
-
const
|
|
221
|
+
const accepted = claimed.type === "steering" ? [claimed.input, ...remaining] : remaining;
|
|
222
|
+
for (const acceptedInput of accepted) {
|
|
223
|
+
const steering = toSteeringPrompt(acceptedInput);
|
|
224
|
+
const prepared = await prepare(steering.text, steering.userInput, steering.attachmentAccess, true);
|
|
195
225
|
if (input.abortSignal.aborted) return;
|
|
196
|
-
|
|
226
|
+
if (!prepared.hasUsableContent) {
|
|
227
|
+
if (prepared.fallbackText) fallbackTexts.push(prepared.fallbackText);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
startPrompt(prepared.text, prepared.images);
|
|
197
231
|
await settlePrompt();
|
|
198
232
|
}
|
|
199
233
|
succeeded = true;
|
|
200
|
-
return;
|
|
234
|
+
return formatSteeringFallback(fallbackTexts);
|
|
201
235
|
}
|
|
202
|
-
const
|
|
236
|
+
const steering = toSteeringPrompt(outcome.input);
|
|
237
|
+
const prepared = await prepare(steering.text, steering.userInput, steering.attachmentAccess, true);
|
|
203
238
|
if (input.abortSignal.aborted) break;
|
|
204
|
-
if (
|
|
239
|
+
if (!prepared.hasUsableContent) {
|
|
240
|
+
if (prepared.fallbackText) fallbackTexts.push(prepared.fallbackText);
|
|
241
|
+
next = take();
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (session.isStreaming) await session.steer(prepared.text, prepared.images);
|
|
205
245
|
else {
|
|
206
246
|
await settlePrompt();
|
|
207
247
|
if (input.abortSignal.aborted) break;
|
|
208
|
-
startPrompt(prepared);
|
|
248
|
+
startPrompt(prepared.text, prepared.images);
|
|
209
249
|
}
|
|
210
250
|
next = take();
|
|
211
251
|
}
|
|
@@ -220,30 +260,62 @@ async function runPiSessionPrompts(options) {
|
|
|
220
260
|
if (cancelledOrFailed) session.clearQueue();
|
|
221
261
|
}
|
|
222
262
|
}
|
|
263
|
+
function formatSteeringFallback(fallbackTexts) {
|
|
264
|
+
return fallbackTexts.length === 0 ? void 0 : `\n\n${fallbackTexts.join("\n\n")}`;
|
|
265
|
+
}
|
|
266
|
+
function toPromptOptions(images) {
|
|
267
|
+
return images.length > 0 ? { images } : void 0;
|
|
268
|
+
}
|
|
269
|
+
function toSteeringPrompt(input) {
|
|
270
|
+
if (typeof input === "string") return { text: input };
|
|
271
|
+
if (isAgentSteeringRequest(input)) return {
|
|
272
|
+
...input.attachmentAccess ? { attachmentAccess: input.attachmentAccess } : {},
|
|
273
|
+
text: projectUserInputText(input.userInput),
|
|
274
|
+
userInput: input.userInput
|
|
275
|
+
};
|
|
276
|
+
return {
|
|
277
|
+
text: projectUserInputText(input),
|
|
278
|
+
userInput: input
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/adapters/pi/execution/pi-serializable-value.ts
|
|
283
|
+
/** Copies plain Pi data, dropping functions and nested values that cannot be serialized. */
|
|
284
|
+
function sanitizeSerializable(value) {
|
|
285
|
+
return copySerializable(value, true);
|
|
286
|
+
}
|
|
287
|
+
/** Copies plain Pi data like `sanitizeSerializable`, but keeps `undefined` where a nested value cannot be serialized. */
|
|
288
|
+
function cloneSerializable(value) {
|
|
289
|
+
return copySerializable(value, false);
|
|
290
|
+
}
|
|
291
|
+
function copySerializable(value, dropUnserializable) {
|
|
292
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
|
|
293
|
+
if (Array.isArray(value)) {
|
|
294
|
+
const items = value.map((item) => copySerializable(item, dropUnserializable));
|
|
295
|
+
return dropUnserializable ? items.filter((item) => item !== void 0) : items;
|
|
296
|
+
}
|
|
297
|
+
if (!isRecord$5(value)) return;
|
|
298
|
+
const entries = Object.entries(value).filter(([, nested]) => typeof nested !== "function" && nested !== void 0).map(([key, nested]) => [key, copySerializable(nested, dropUnserializable)]);
|
|
299
|
+
return Object.fromEntries(dropUnserializable ? entries.filter(([, copied]) => copied !== void 0) : entries);
|
|
300
|
+
}
|
|
301
|
+
function isRecord$5(value) {
|
|
302
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
303
|
+
}
|
|
223
304
|
//#endregion
|
|
224
305
|
//#region src/adapters/pi/execution/pi-agent-loop.ts
|
|
225
|
-
function createPiAgentLoop
|
|
306
|
+
function createPiAgentLoop(options) {
|
|
226
307
|
return {
|
|
227
|
-
...options.supportsSteering ? {
|
|
308
|
+
...options.supportsSteering ? {
|
|
309
|
+
supportsSteering: true,
|
|
310
|
+
supportsStructuredSteering: true
|
|
311
|
+
} : {},
|
|
228
312
|
run: (input) => {
|
|
229
313
|
const stream = Stream.fromAsyncIterable(runPiSession(input, options), (error) => error);
|
|
230
314
|
if (!options.runBoundary) return stream;
|
|
231
|
-
return Stream.
|
|
315
|
+
return Stream.unwrap(Effect.acquireRelease(options.runBoundary.acquireRun({ signal: input.abortSignal }), (lease) => Effect.sync(lease.release)).pipe(Effect.as(stream)));
|
|
232
316
|
}
|
|
233
317
|
};
|
|
234
318
|
}
|
|
235
|
-
function createPiSdkAgentLoop$1(options) {
|
|
236
|
-
return createPiAgentLoop$1({
|
|
237
|
-
...options.supportsSteering ? { supportsSteering: true } : {},
|
|
238
|
-
resolveSession: async () => {
|
|
239
|
-
const result = await options.createAgentSession(options.sessionOptions);
|
|
240
|
-
return {
|
|
241
|
-
dispose: () => result.session.dispose?.(),
|
|
242
|
-
session: result.session
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
319
|
async function* runPiSession(input, options) {
|
|
248
320
|
const queue = createAsyncQueue();
|
|
249
321
|
let removeAbortListener;
|
|
@@ -299,12 +371,13 @@ async function* runPiSession(input, options) {
|
|
|
299
371
|
queue.offer(mapped);
|
|
300
372
|
}
|
|
301
373
|
});
|
|
302
|
-
await runPiSessionPrompts({
|
|
374
|
+
const fallbackText = await runPiSessionPrompts({
|
|
303
375
|
input,
|
|
304
376
|
handle,
|
|
305
377
|
supportsSteering: options.supportsSteering === true,
|
|
306
378
|
abort: abortSession
|
|
307
379
|
});
|
|
380
|
+
if (fallbackText && !input.abortSignal.aborted) queue.offer(createAgentLoopTextDelta(fallbackText));
|
|
308
381
|
complete(input.abortSignal.aborted || terminalErrorMessage === void 0 ? void 0 : new Error(terminalErrorMessage));
|
|
309
382
|
} catch (error) {
|
|
310
383
|
complete(input.abortSignal.aborted ? void 0 : error);
|
|
@@ -351,11 +424,11 @@ function observeModelContent(observer, session, event, runId, mapped) {
|
|
|
351
424
|
});
|
|
352
425
|
}
|
|
353
426
|
function snapshotModelInput(value) {
|
|
354
|
-
if (!isRecord$4(value)) return
|
|
427
|
+
if (!isRecord$4(value)) return;
|
|
355
428
|
const messages = Array.isArray(value.messages) ? value.messages.map(snapshotMessage).filter(isDefined) : void 0;
|
|
356
429
|
const systemPrompt = typeof value.systemPrompt === "string" ? value.systemPrompt : void 0;
|
|
357
430
|
const tools = Array.isArray(value.tools) ? value.tools.map(snapshotTool).filter(isDefined) : void 0;
|
|
358
|
-
if (!messages && systemPrompt === void 0 && !tools) return
|
|
431
|
+
if (!messages && systemPrompt === void 0 && !tools) return;
|
|
359
432
|
return {
|
|
360
433
|
...messages ? { messages } : {},
|
|
361
434
|
...systemPrompt === void 0 ? {} : { systemPrompt },
|
|
@@ -363,11 +436,11 @@ function snapshotModelInput(value) {
|
|
|
363
436
|
};
|
|
364
437
|
}
|
|
365
438
|
function snapshotAssistantOutput(value) {
|
|
366
|
-
if (!isRecord$4(value) || value.role !== "assistant") return
|
|
439
|
+
if (!isRecord$4(value) || value.role !== "assistant") return;
|
|
367
440
|
return snapshotMessage(value);
|
|
368
441
|
}
|
|
369
442
|
function snapshotMessage(value) {
|
|
370
|
-
if (!isRecord$4(value) || typeof value.role !== "string") return
|
|
443
|
+
if (!isRecord$4(value) || typeof value.role !== "string") return;
|
|
371
444
|
const content = sanitizeContent(value.content);
|
|
372
445
|
return {
|
|
373
446
|
...content === void 0 ? {} : { content },
|
|
@@ -377,7 +450,7 @@ function snapshotMessage(value) {
|
|
|
377
450
|
};
|
|
378
451
|
}
|
|
379
452
|
function snapshotTool(value) {
|
|
380
|
-
if (!isRecord$4(value) || typeof value.name !== "string") return
|
|
453
|
+
if (!isRecord$4(value) || typeof value.name !== "string") return;
|
|
381
454
|
return {
|
|
382
455
|
...typeof value.description === "string" ? { description: value.description } : {},
|
|
383
456
|
name: value.name,
|
|
@@ -388,12 +461,6 @@ function sanitizeContent(value) {
|
|
|
388
461
|
if (!Array.isArray(value)) return sanitizeSerializable(value);
|
|
389
462
|
return value.filter((item) => !isPrivateThinkingBlock(item)).map(sanitizeSerializable).filter(isDefined);
|
|
390
463
|
}
|
|
391
|
-
function sanitizeSerializable(value) {
|
|
392
|
-
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
|
|
393
|
-
if (Array.isArray(value)) return value.map(sanitizeSerializable).filter(isDefined);
|
|
394
|
-
if (!isRecord$4(value)) return void 0;
|
|
395
|
-
return Object.fromEntries(Object.entries(value).filter(([, nested]) => typeof nested !== "function" && nested !== void 0).map(([key, nested]) => [key, sanitizeSerializable(nested)]).filter((entry) => entry[1] !== void 0));
|
|
396
|
-
}
|
|
397
464
|
function isPrivateThinkingBlock(value) {
|
|
398
465
|
return isRecord$4(value) && (value.type === "thinking" || value.type === "redacted_thinking");
|
|
399
466
|
}
|
|
@@ -447,7 +514,7 @@ function mapPiEvent(event, runId, state, resolveToolName) {
|
|
|
447
514
|
switch (event.type) {
|
|
448
515
|
case "message_start": {
|
|
449
516
|
const message = readAssistantMessage(event.message);
|
|
450
|
-
if (!message) return
|
|
517
|
+
if (!message) return;
|
|
451
518
|
state.modelCallCount += 1;
|
|
452
519
|
state.activeModelCallId = `${runId}:model:${state.modelCallCount}`;
|
|
453
520
|
return createAgentLoopModelExecutionStart({
|
|
@@ -465,7 +532,7 @@ function mapPiEvent(event, runId, state, resolveToolName) {
|
|
|
465
532
|
return createAgentLoopTextDelta(assistantEvent.delta);
|
|
466
533
|
case "message_end": {
|
|
467
534
|
const message = readAssistantMessage(event.message);
|
|
468
|
-
if (!message) return
|
|
535
|
+
if (!message) return;
|
|
469
536
|
if (!state.activeModelCallId) {
|
|
470
537
|
state.modelCallCount += 1;
|
|
471
538
|
state.activeModelCallId = `${runId}:model:${state.modelCallCount}`;
|
|
@@ -535,7 +602,7 @@ function readSkillId(value) {
|
|
|
535
602
|
return isRecord$4(value) && typeof value.skillId === "string" ? value.skillId : void 0;
|
|
536
603
|
}
|
|
537
604
|
function readSkillDetails(value) {
|
|
538
|
-
if (!isRecord$4(value) || !isRecord$4(value.details)) return
|
|
605
|
+
if (!isRecord$4(value) || !isRecord$4(value.details)) return;
|
|
539
606
|
const details = value.details;
|
|
540
607
|
if (typeof details.contentLength !== "number" || typeof details.digest !== "string" || typeof details.skillId !== "string" || typeof details.title !== "string" || typeof details.version !== "string") return;
|
|
541
608
|
return details;
|
|
@@ -562,7 +629,7 @@ function createAsyncQueue() {
|
|
|
562
629
|
}
|
|
563
630
|
//#endregion
|
|
564
631
|
//#region src/adapters/pi/execution/pi-session-registry.ts
|
|
565
|
-
function createPiSessionRegistry
|
|
632
|
+
function createPiSessionRegistry(options) {
|
|
566
633
|
const sessions = /* @__PURE__ */ new Map();
|
|
567
634
|
const resolve = async (input) => {
|
|
568
635
|
const cached = sessions.get(input.sessionKey);
|
|
@@ -591,6 +658,233 @@ function createPiSessionRegistry$1(options) {
|
|
|
591
658
|
};
|
|
592
659
|
}
|
|
593
660
|
//#endregion
|
|
661
|
+
//#region src/adapters/pi/model-management/pi-model-resolution.ts
|
|
662
|
+
var PiModelResolutionError = class extends Error {
|
|
663
|
+
name = "PiModelResolutionError";
|
|
664
|
+
code;
|
|
665
|
+
resolutionCode;
|
|
666
|
+
constructor(resolutionCode, message) {
|
|
667
|
+
super(message);
|
|
668
|
+
this.code = resolutionCode === "provider-unknown" ? "model_change_provider_unsupported" : "model_change_target_unsupported";
|
|
669
|
+
this.resolutionCode = resolutionCode;
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
/**
|
|
673
|
+
* Resolve one exact provider/model pair from the installed Pi model runtime.
|
|
674
|
+
*
|
|
675
|
+
* Model aliases and fuzzy matches are intentionally excluded here. Natural-language
|
|
676
|
+
* interpretation belongs to the application/Agent; the activation boundary must bind
|
|
677
|
+
* to the exact model that the SDK can actually construct.
|
|
678
|
+
*/
|
|
679
|
+
function resolvePiModel(options) {
|
|
680
|
+
const provider = options.target.provider.trim();
|
|
681
|
+
const modelId = options.target.model.trim();
|
|
682
|
+
if (!provider || !modelId || provider.includes("/") || modelId.includes("\n")) throw new PiModelResolutionError("invalid-reference", "Pi model target must contain a provider and model id without a provider separator in either component");
|
|
683
|
+
if (options.modelRuntime.getModels(provider).length === 0) {
|
|
684
|
+
if (!options.modelRuntime.getProvider(provider)) throw new PiModelResolutionError("provider-unknown", `Pi provider is not installed: ${provider}`);
|
|
685
|
+
throw new PiModelResolutionError("model-unknown", `Pi provider ${provider} has no known model metadata; configure a declarative model before activation`);
|
|
686
|
+
}
|
|
687
|
+
const model = options.modelRuntime.getModel(provider, modelId);
|
|
688
|
+
if (!model) throw new PiModelResolutionError("model-unknown", `Pi model ${provider}/${modelId} is not present in the installed model catalog`);
|
|
689
|
+
return model;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Ensure metadata for an exact model id that is absent from the installed SDK
|
|
693
|
+
* catalog, then recompose the provider from models.json. Phase 1 only ships the
|
|
694
|
+
* verified DeepSeek declaration required by the management contract; all other
|
|
695
|
+
* unknown ids fail closed and must be supplied by a future explicit catalog path.
|
|
696
|
+
*/
|
|
697
|
+
async function ensurePiModel(options) {
|
|
698
|
+
try {
|
|
699
|
+
return resolvePiModel(options);
|
|
700
|
+
} catch (error) {
|
|
701
|
+
if (!(error instanceof PiModelResolutionError) || error.resolutionCode !== "model-unknown") throw error;
|
|
702
|
+
}
|
|
703
|
+
const definition = knownPiDeclarativeModel(options.modelRuntime, options.target);
|
|
704
|
+
if (!definition) throw new PiModelResolutionError("model-unknown", `Pi model ${options.target.provider}/${options.target.model} is not present in the installed model catalog and has no approved declarative metadata`);
|
|
705
|
+
await mergePiProviderModelDeclaration({
|
|
706
|
+
filePath: options.modelsPath,
|
|
707
|
+
model: definition,
|
|
708
|
+
provider: options.target.provider
|
|
709
|
+
});
|
|
710
|
+
const refreshError = (await refreshPiModelCatalog(options.modelRuntime, {
|
|
711
|
+
allowNetwork: false,
|
|
712
|
+
provider: options.target.provider,
|
|
713
|
+
...options.signal ? { signal: options.signal } : {}
|
|
714
|
+
})).errors.get(options.target.provider);
|
|
715
|
+
if (refreshError) throw new PiModelResolutionError("model-unknown", `Pi provider ${options.target.provider} could not load declarative model metadata: ${refreshError.message}`);
|
|
716
|
+
return resolvePiModel(options);
|
|
717
|
+
}
|
|
718
|
+
/** Refresh installed SDK metadata, retaining the SDK's persisted declarative catalog. */
|
|
719
|
+
async function refreshPiModelCatalog(modelRuntime, options = {}) {
|
|
720
|
+
const providers = options.provider === void 0 ? void 0 : [options.provider];
|
|
721
|
+
const result = await modelRuntime.refresh({
|
|
722
|
+
allowNetwork: options.allowNetwork ?? false,
|
|
723
|
+
...providers ? { providers } : {},
|
|
724
|
+
...options.signal ? { signal: options.signal } : {}
|
|
725
|
+
});
|
|
726
|
+
const refreshedProviders = providers ?? modelRuntime.getRegisteredProviderIds();
|
|
727
|
+
return Object.freeze({
|
|
728
|
+
errors: result.errors,
|
|
729
|
+
refreshedProviders: Object.freeze([...refreshedProviders])
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
function knownPiDeclarativeModel(modelRuntime, target) {
|
|
733
|
+
if (target.provider !== "deepseek" || target.model !== "deepseek-flash") return;
|
|
734
|
+
const source = modelRuntime.getModel("deepseek", "deepseek-v4-flash");
|
|
735
|
+
if (!source) return;
|
|
736
|
+
return {
|
|
737
|
+
api: source.api,
|
|
738
|
+
baseUrl: source.baseUrl,
|
|
739
|
+
...source.compat ? { compat: source.compat } : {},
|
|
740
|
+
contextWindow: 1e6,
|
|
741
|
+
cost: {
|
|
742
|
+
cacheRead: .006,
|
|
743
|
+
cacheWrite: 0,
|
|
744
|
+
input: .3,
|
|
745
|
+
output: 1.2
|
|
746
|
+
},
|
|
747
|
+
id: target.model,
|
|
748
|
+
input: ["text", "image"],
|
|
749
|
+
maxTokens: 384e3,
|
|
750
|
+
name: "DeepSeek V4.1 Flash",
|
|
751
|
+
reasoning: true,
|
|
752
|
+
...source.samplingParams ? { samplingParams: source.samplingParams } : {},
|
|
753
|
+
...source.thinkingLevelMap ? { thinkingLevelMap: source.thinkingLevelMap } : {}
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
//#endregion
|
|
757
|
+
//#region src/adapters/pi/model-management/pi-history-compatibility.ts
|
|
758
|
+
var PiHistoryCompatibilityError = class extends Error {
|
|
759
|
+
name = "PiHistoryCompatibilityError";
|
|
760
|
+
code = "model_change_history_incompatible";
|
|
761
|
+
messageIndex;
|
|
762
|
+
constructor(message, messageIndex) {
|
|
763
|
+
super(message);
|
|
764
|
+
this.messageIndex = messageIndex;
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
/**
|
|
768
|
+
* Check the structural parts of a persisted Pi transcript that providers need for
|
|
769
|
+
* the next request. The transcript is never returned or copied; only counts are
|
|
770
|
+
* exposed for diagnostics. This deliberately rejects orphaned tool calls/results
|
|
771
|
+
* instead of deleting or rewriting history during a model change.
|
|
772
|
+
*/
|
|
773
|
+
function inspectPiSessionHistory(sessionManager) {
|
|
774
|
+
const context = sessionManager.buildSessionContext();
|
|
775
|
+
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
776
|
+
let assistantThinkingBlocks = 0;
|
|
777
|
+
let assistantToolCalls = 0;
|
|
778
|
+
let imageBlocks = 0;
|
|
779
|
+
let toolResults = 0;
|
|
780
|
+
for (const [messageIndex, message] of context.messages.entries()) {
|
|
781
|
+
if (!isRecord$3(message) || typeof message.role !== "string") throw new PiHistoryCompatibilityError("Pi session history contains a malformed message", messageIndex);
|
|
782
|
+
if (message.role === "assistant") {
|
|
783
|
+
const content = message.content;
|
|
784
|
+
if (!Array.isArray(content)) throw new PiHistoryCompatibilityError("Pi assistant history message content must be an array", messageIndex);
|
|
785
|
+
for (const block of content) {
|
|
786
|
+
if (!isRecord$3(block) || typeof block.type !== "string") throw new PiHistoryCompatibilityError("Pi assistant history contains a malformed content block", messageIndex);
|
|
787
|
+
if (block.type === "thinking") {
|
|
788
|
+
if (typeof block.thinking !== "string") throw new PiHistoryCompatibilityError("Pi assistant thinking content is malformed", messageIndex);
|
|
789
|
+
assistantThinkingBlocks += 1;
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
if (block.type === "toolCall") {
|
|
793
|
+
if (typeof block.id !== "string" || !block.id || typeof block.name !== "string" || !block.name) throw new PiHistoryCompatibilityError("Pi assistant tool call is malformed", messageIndex);
|
|
794
|
+
if (!isRecord$3(block.arguments)) throw new PiHistoryCompatibilityError("Pi assistant tool call arguments are malformed", messageIndex);
|
|
795
|
+
pendingToolCalls.set(block.id, block.name);
|
|
796
|
+
assistantToolCalls += 1;
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if (block.type === "text") {
|
|
800
|
+
if (typeof block.text !== "string") throw new PiHistoryCompatibilityError("Pi assistant text content is malformed", messageIndex);
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
if (block.type === "image") {
|
|
804
|
+
if (typeof block.data !== "string" || typeof block.mimeType !== "string") throw new PiHistoryCompatibilityError("Pi assistant image content is malformed", messageIndex);
|
|
805
|
+
imageBlocks += 1;
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
throw new PiHistoryCompatibilityError(`Pi assistant content type is unsupported: ${String(block.type)}`, messageIndex);
|
|
809
|
+
}
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (message.role === "toolResult") {
|
|
813
|
+
const content = message.content;
|
|
814
|
+
if (!Array.isArray(content)) throw new PiHistoryCompatibilityError("Pi tool result content must be an array", messageIndex);
|
|
815
|
+
if (typeof message.toolCallId !== "string" || !pendingToolCalls.has(message.toolCallId)) throw new PiHistoryCompatibilityError("Pi tool result has no preceding assistant tool call", messageIndex);
|
|
816
|
+
if (typeof message.toolName !== "string" || !message.toolName) throw new PiHistoryCompatibilityError("Pi tool result is missing its tool name", messageIndex);
|
|
817
|
+
if (pendingToolCalls.get(message.toolCallId) !== message.toolName) throw new PiHistoryCompatibilityError("Pi tool result tool name does not match its assistant tool call", messageIndex);
|
|
818
|
+
imageBlocks += countImageBlocks(content);
|
|
819
|
+
validatePlainContent(content, messageIndex, "tool result");
|
|
820
|
+
pendingToolCalls.delete(message.toolCallId);
|
|
821
|
+
toolResults += 1;
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
if (message.role === "user") {
|
|
825
|
+
if (typeof message.content !== "string") {
|
|
826
|
+
if (!Array.isArray(message.content)) throw new PiHistoryCompatibilityError("Pi user history content must be text or an array", messageIndex);
|
|
827
|
+
imageBlocks += countImageBlocks(message.content);
|
|
828
|
+
validatePlainContent(message.content, messageIndex, "user");
|
|
829
|
+
}
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
if (message.role === "custom") {
|
|
833
|
+
if (typeof message.customType !== "string" || typeof message.display !== "boolean") throw new PiHistoryCompatibilityError("Pi custom history message metadata is malformed", messageIndex);
|
|
834
|
+
if (typeof message.content === "string") continue;
|
|
835
|
+
if (!Array.isArray(message.content)) throw new PiHistoryCompatibilityError("Pi custom history content must be text or an array", messageIndex);
|
|
836
|
+
imageBlocks += countImageBlocks(message.content);
|
|
837
|
+
validatePlainContent(message.content, messageIndex, "custom");
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
if (message.role === "bashExecution") {
|
|
841
|
+
if (typeof message.command !== "string" || typeof message.output !== "string" || typeof message.cancelled !== "boolean" || typeof message.truncated !== "boolean" || message.exitCode !== void 0 && message.exitCode !== null && typeof message.exitCode !== "number" || message.fullOutputPath !== void 0 && typeof message.fullOutputPath !== "string" || message.excludeFromContext !== void 0 && typeof message.excludeFromContext !== "boolean") throw new PiHistoryCompatibilityError("Pi bash history message is malformed", messageIndex);
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
if (message.role === "branchSummary") {
|
|
845
|
+
if (typeof message.summary !== "string" || typeof message.fromId !== "string") throw new PiHistoryCompatibilityError("Pi branch summary history message is malformed", messageIndex);
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
if (message.role === "compactionSummary") {
|
|
849
|
+
if (typeof message.summary !== "string" || typeof message.tokensBefore !== "number" || !Number.isFinite(message.tokensBefore) || message.tokensBefore < 0) throw new PiHistoryCompatibilityError("Pi compaction summary history message is malformed", messageIndex);
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
throw new PiHistoryCompatibilityError(`Pi history role is unsupported: ${String(message.role)}`, messageIndex);
|
|
853
|
+
}
|
|
854
|
+
if (pendingToolCalls.size > 0) throw new PiHistoryCompatibilityError("Pi session history contains an assistant tool call without a result", context.messages.length);
|
|
855
|
+
return Object.freeze({
|
|
856
|
+
assistantThinkingBlocks,
|
|
857
|
+
assistantToolCalls,
|
|
858
|
+
compatible: true,
|
|
859
|
+
imageBlocks,
|
|
860
|
+
messageCount: context.messages.length,
|
|
861
|
+
toolResults
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
function countImageBlocks(content) {
|
|
865
|
+
return content.filter((block) => isRecord$3(block) && block.type === "image").length;
|
|
866
|
+
}
|
|
867
|
+
function assertPiSessionHistoryCompatible(sessionManager) {
|
|
868
|
+
return inspectPiSessionHistory(sessionManager);
|
|
869
|
+
}
|
|
870
|
+
function validatePlainContent(content, messageIndex, role) {
|
|
871
|
+
for (const block of content) {
|
|
872
|
+
if (!isRecord$3(block) || typeof block.type !== "string") throw new PiHistoryCompatibilityError(`Pi ${role} history contains a malformed content block`, messageIndex);
|
|
873
|
+
if (block.type === "text") {
|
|
874
|
+
if (typeof block.text !== "string") throw new PiHistoryCompatibilityError(`Pi ${role} text content is malformed`, messageIndex);
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (block.type === "image") {
|
|
878
|
+
if (typeof block.data !== "string" || typeof block.mimeType !== "string") throw new PiHistoryCompatibilityError(`Pi ${role} image content is malformed`, messageIndex);
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
throw new PiHistoryCompatibilityError(`Pi ${role} content type is unsupported: ${block.type}`, messageIndex);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
function isRecord$3(value) {
|
|
885
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
886
|
+
}
|
|
887
|
+
//#endregion
|
|
594
888
|
//#region src/adapters/pi/model-management/pi-model-call-deadline.ts
|
|
595
889
|
var PiModelCallDeadlineExceeded = class extends Error {
|
|
596
890
|
name = "PiModelCallDeadlineExceeded";
|
|
@@ -634,7 +928,6 @@ async function awaitPiModelCall(promise, options = {}) {
|
|
|
634
928
|
//#region src/adapters/pi/model-management/pi-model-probe.ts
|
|
635
929
|
const PI_MODEL_PROBE_TOOL_NAME = "rivus_model_management_probe";
|
|
636
930
|
const PI_MODEL_PROBE_TOOL_RESULT = "RIVUS_MODEL_PROBE_TOOL_OK";
|
|
637
|
-
const PI_MODEL_PROBE_FOLLOWUP_RESULT = "RIVUS_MODEL_PROBE_FOLLOWUP_OK";
|
|
638
931
|
const probeToolParameters = Type.Object({ probe: Type.String() });
|
|
639
932
|
var PiModelProbeError = class extends Error {
|
|
640
933
|
name = "PiModelProbeError";
|
|
@@ -709,7 +1002,7 @@ function probePiModel(options) {
|
|
|
709
1002
|
if (findToolCalls(followup.message).length > 0) return yield* Effect.fail(new PiModelProbeError("candidate requested a Tool during the probe follow-up", "known"));
|
|
710
1003
|
const followupText = readText(followup.message);
|
|
711
1004
|
if (!followupText.includes("RIVUS_MODEL_PROBE_FOLLOWUP_OK")) return yield* Effect.fail(new PiModelProbeError("candidate did not produce the fixed probe follow-up response", "known"));
|
|
712
|
-
const assistantThinkingBlocks = transcript.reduce((count, turn) => count + turn.assistant.content.filter((content) => isRecord$
|
|
1005
|
+
const assistantThinkingBlocks = transcript.reduce((count, turn) => count + turn.assistant.content.filter((content) => isRecord$2(content) && content.type === "thinking").length, 0);
|
|
713
1006
|
const outputTokens = transcript.reduce((sum, turn) => sum + readOutputTokens(turn.assistant.usage), 0);
|
|
714
1007
|
return Object.freeze({
|
|
715
1008
|
assistantThinkingBlocks,
|
|
@@ -815,13 +1108,13 @@ function streamPaidProbeCall(options) {
|
|
|
815
1108
|
});
|
|
816
1109
|
},
|
|
817
1110
|
catch: (error) => error
|
|
818
|
-
}).pipe(Effect.
|
|
1111
|
+
}).pipe(Effect.catch((error) => {
|
|
819
1112
|
const deadlineExceeded = callSignal.isDeadlineExceeded() || error instanceof PiModelCallDeadlineExceeded;
|
|
820
1113
|
const probeError = new PiModelProbeError(deadlineExceeded ? `${options.description} stream exceeded the model change deadline` : `${options.description} stream failed: ${error instanceof Error ? error.message : String(error)}`, "unknown", { cause: error }, deadlineExceeded ? "model_change_deadline_exceeded" : "model_change_probe_failed");
|
|
821
1114
|
return settle({
|
|
822
1115
|
outcome: "unknown",
|
|
823
1116
|
outputTokens: reservation.maxOutputTokens
|
|
824
|
-
}).pipe(Effect.
|
|
1117
|
+
}).pipe(Effect.andThen(Effect.fail(probeError)));
|
|
825
1118
|
}));
|
|
826
1119
|
const outputTokens = readOutputTokens(message.usage);
|
|
827
1120
|
yield* settle({
|
|
@@ -833,330 +1126,85 @@ function streamPaidProbeCall(options) {
|
|
|
833
1126
|
catch: (error) => error
|
|
834
1127
|
});
|
|
835
1128
|
return {
|
|
836
|
-
message,
|
|
837
|
-
outputTokens,
|
|
838
|
-
reservation
|
|
839
|
-
};
|
|
840
|
-
}).pipe(Effect.ensuring(Effect.uninterruptible(Effect.suspend(() => settled ? Effect.void : settle({
|
|
841
|
-
outcome: "unknown",
|
|
842
|
-
outputTokens: reservation.maxOutputTokens
|
|
843
|
-
})).pipe(Effect.orDie))), Effect.ensuring(Effect.sync(callSignal.dispose)));
|
|
844
|
-
});
|
|
845
|
-
}
|
|
846
|
-
function createPaidCallSignal(parent, deadlineAt) {
|
|
847
|
-
const controller = new AbortController();
|
|
848
|
-
const deadlineMs = Date.parse(deadlineAt);
|
|
849
|
-
const remainingMs = deadlineMs - Date.now();
|
|
850
|
-
let deadlineExceeded = !Number.isFinite(deadlineMs) || remainingMs <= 0;
|
|
851
|
-
let timer;
|
|
852
|
-
const onParentAbort = () => controller.abort(parent?.reason);
|
|
853
|
-
if (parent) if (parent.aborted) controller.abort(parent.reason);
|
|
854
|
-
else parent.addEventListener("abort", onParentAbort, { once: true });
|
|
855
|
-
if (remainingMs > 0 && !controller.signal.aborted) timer = setTimeout(() => {
|
|
856
|
-
deadlineExceeded = true;
|
|
857
|
-
controller.abort(/* @__PURE__ */ new Error("model change deadline elapsed"));
|
|
858
|
-
}, Math.min(remainingMs, 2147483647));
|
|
859
|
-
return {
|
|
860
|
-
expired: deadlineExceeded,
|
|
861
|
-
isDeadlineExceeded: () => deadlineExceeded || Number.isFinite(deadlineMs) && Date.now() >= deadlineMs,
|
|
862
|
-
signal: controller.signal,
|
|
863
|
-
dispose: () => {
|
|
864
|
-
if (timer) clearTimeout(timer);
|
|
865
|
-
parent?.removeEventListener("abort", onParentAbort);
|
|
866
|
-
if (!controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("model probe cancelled"));
|
|
867
|
-
}
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
function assertProbeStopReason(message, kind) {
|
|
871
|
-
if (message.stopReason === "error" || message.stopReason === "aborted") throw new PiModelProbeError(`candidate streamed an ${message.stopReason} response: ${message.errorMessage ?? "unknown provider error"}`, "known");
|
|
872
|
-
if (message.stopReason === "length" || message.stopReason === "deferred") throw new PiModelProbeError(`candidate response was truncated or deferred during ${kind}`, "known");
|
|
873
|
-
if (kind !== "initial" && message.stopReason !== "stop") throw new PiModelProbeError(`candidate used unsupported stop reason ${message.stopReason} during ${kind}`, "known");
|
|
874
|
-
}
|
|
875
|
-
function assertCanaryStopReason(message) {
|
|
876
|
-
if (message.stopReason === "error" || message.stopReason === "aborted") throw new PiModelProbeError(`model canary streamed an ${message.stopReason} response: ${message.errorMessage ?? "unknown provider error"}`, "known");
|
|
877
|
-
if (message.stopReason === "length" || message.stopReason === "deferred" || message.stopReason !== "stop") throw new PiModelProbeError(`model canary used unsupported stop reason ${message.stopReason}`, "known");
|
|
878
|
-
}
|
|
879
|
-
function findSingleProbeToolCall(message) {
|
|
880
|
-
const calls = findToolCalls(message);
|
|
881
|
-
if (calls.length !== 1 || calls[0]?.name !== "rivus_model_management_probe") return void 0;
|
|
882
|
-
const call = calls[0];
|
|
883
|
-
return {
|
|
884
|
-
arguments: call.arguments,
|
|
885
|
-
id: call.id
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
|
-
function findToolCalls(message) {
|
|
889
|
-
return message.content.flatMap((content) => {
|
|
890
|
-
if (!isRecord$3(content) || content.type !== "toolCall") return [];
|
|
891
|
-
return [{
|
|
892
|
-
arguments: isRecord$3(content.arguments) ? content.arguments : {},
|
|
893
|
-
id: typeof content.id === "string" ? content.id : "",
|
|
894
|
-
name: typeof content.name === "string" ? content.name : ""
|
|
895
|
-
}];
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
function readText(message) {
|
|
899
|
-
return message.content.filter((content) => isRecord$3(content) && content.type === "text" && typeof content.text === "string").map((content) => content.text).join("");
|
|
900
|
-
}
|
|
901
|
-
function snapshotAssistant(message) {
|
|
902
|
-
return Object.freeze({
|
|
903
|
-
content: Object.freeze(message.content.map((content) => cloneSerializable(content))),
|
|
904
|
-
model: message.model,
|
|
905
|
-
provider: message.provider,
|
|
906
|
-
stopReason: message.stopReason,
|
|
907
|
-
usage: Object.freeze(cloneSerializable(message.usage))
|
|
908
|
-
});
|
|
909
|
-
}
|
|
910
|
-
function readOutputTokens(usage) {
|
|
911
|
-
if (isRecord$3(usage) && typeof usage.output === "number" && Number.isSafeInteger(usage.output) && usage.output >= 0) return usage.output;
|
|
912
|
-
return 0;
|
|
913
|
-
}
|
|
914
|
-
function cloneSerializable(value) {
|
|
915
|
-
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
|
|
916
|
-
if (Array.isArray(value)) return value.map(cloneSerializable);
|
|
917
|
-
if (!isRecord$3(value)) return void 0;
|
|
918
|
-
return Object.fromEntries(Object.entries(value).filter(([, nested]) => typeof nested !== "function" && nested !== void 0).map(([key, nested]) => [key, cloneSerializable(nested)]));
|
|
919
|
-
}
|
|
920
|
-
function isRecord$3(value) {
|
|
921
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
922
|
-
}
|
|
923
|
-
//#endregion
|
|
924
|
-
//#region src/adapters/pi/model-management/pi-model-resolution.ts
|
|
925
|
-
var PiModelResolutionError = class extends Error {
|
|
926
|
-
name = "PiModelResolutionError";
|
|
927
|
-
code;
|
|
928
|
-
resolutionCode;
|
|
929
|
-
constructor(resolutionCode, message) {
|
|
930
|
-
super(message);
|
|
931
|
-
this.code = resolutionCode === "provider-unknown" ? "model_change_provider_unsupported" : "model_change_target_unsupported";
|
|
932
|
-
this.resolutionCode = resolutionCode;
|
|
933
|
-
}
|
|
934
|
-
};
|
|
935
|
-
/**
|
|
936
|
-
* Resolve one exact provider/model pair from the installed Pi model runtime.
|
|
937
|
-
*
|
|
938
|
-
* Model aliases and fuzzy matches are intentionally excluded here. Natural-language
|
|
939
|
-
* interpretation belongs to the application/Agent; the activation boundary must bind
|
|
940
|
-
* to the exact model that the SDK can actually construct.
|
|
941
|
-
*/
|
|
942
|
-
function resolvePiModel(options) {
|
|
943
|
-
const provider = options.target.provider.trim();
|
|
944
|
-
const modelId = options.target.model.trim();
|
|
945
|
-
if (!provider || !modelId || provider.includes("/") || modelId.includes("\n")) throw new PiModelResolutionError("invalid-reference", "Pi model target must contain a provider and model id without a provider separator in either component");
|
|
946
|
-
if (options.modelRuntime.getModels(provider).length === 0) {
|
|
947
|
-
if (!options.modelRuntime.getProvider(provider)) throw new PiModelResolutionError("provider-unknown", `Pi provider is not installed: ${provider}`);
|
|
948
|
-
throw new PiModelResolutionError("model-unknown", `Pi provider ${provider} has no known model metadata; configure a declarative model before activation`);
|
|
949
|
-
}
|
|
950
|
-
const model = options.modelRuntime.getModel(provider, modelId);
|
|
951
|
-
if (!model) throw new PiModelResolutionError("model-unknown", `Pi model ${provider}/${modelId} is not present in the installed model catalog`);
|
|
952
|
-
return model;
|
|
953
|
-
}
|
|
954
|
-
/**
|
|
955
|
-
* Ensure metadata for an exact model id that is absent from the installed SDK
|
|
956
|
-
* catalog, then recompose the provider from models.json. Phase 1 only ships the
|
|
957
|
-
* verified DeepSeek declaration required by the management contract; all other
|
|
958
|
-
* unknown ids fail closed and must be supplied by a future explicit catalog path.
|
|
959
|
-
*/
|
|
960
|
-
async function ensurePiModel(options) {
|
|
961
|
-
try {
|
|
962
|
-
return resolvePiModel(options);
|
|
963
|
-
} catch (error) {
|
|
964
|
-
if (!(error instanceof PiModelResolutionError) || error.resolutionCode !== "model-unknown") throw error;
|
|
965
|
-
}
|
|
966
|
-
const definition = knownPiDeclarativeModel(options.modelRuntime, options.target);
|
|
967
|
-
if (!definition) throw new PiModelResolutionError("model-unknown", `Pi model ${options.target.provider}/${options.target.model} is not present in the installed model catalog and has no approved declarative metadata`);
|
|
968
|
-
await mergePiProviderModelDeclaration({
|
|
969
|
-
filePath: options.modelsPath,
|
|
970
|
-
model: definition,
|
|
971
|
-
provider: options.target.provider
|
|
972
|
-
});
|
|
973
|
-
const refreshError = (await refreshPiModelCatalog(options.modelRuntime, {
|
|
974
|
-
allowNetwork: false,
|
|
975
|
-
provider: options.target.provider,
|
|
976
|
-
...options.signal ? { signal: options.signal } : {}
|
|
977
|
-
})).errors.get(options.target.provider);
|
|
978
|
-
if (refreshError) throw new PiModelResolutionError("model-unknown", `Pi provider ${options.target.provider} could not load declarative model metadata: ${refreshError.message}`);
|
|
979
|
-
return resolvePiModel(options);
|
|
980
|
-
}
|
|
981
|
-
function resolvePiModelBinding(options) {
|
|
982
|
-
const model = resolvePiModel(options);
|
|
983
|
-
return Object.freeze({
|
|
984
|
-
binding: Object.freeze({
|
|
985
|
-
bindingRevision: options.bindingRevision,
|
|
986
|
-
model: model.id,
|
|
987
|
-
provider: model.provider
|
|
988
|
-
}),
|
|
989
|
-
model
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
/** Refresh installed SDK metadata, retaining the SDK's persisted declarative catalog. */
|
|
993
|
-
async function refreshPiModelCatalog(modelRuntime, options = {}) {
|
|
994
|
-
const providers = options.provider === void 0 ? void 0 : [options.provider];
|
|
995
|
-
const result = await modelRuntime.refresh({
|
|
996
|
-
allowNetwork: options.allowNetwork ?? false,
|
|
997
|
-
...providers ? { providers } : {},
|
|
998
|
-
...options.signal ? { signal: options.signal } : {}
|
|
999
|
-
});
|
|
1000
|
-
const refreshedProviders = providers ?? modelRuntime.getRegisteredProviderIds();
|
|
1001
|
-
return Object.freeze({
|
|
1002
|
-
errors: result.errors,
|
|
1003
|
-
refreshedProviders: Object.freeze([...refreshedProviders])
|
|
1129
|
+
message,
|
|
1130
|
+
outputTokens,
|
|
1131
|
+
reservation
|
|
1132
|
+
};
|
|
1133
|
+
}).pipe(Effect.ensuring(Effect.uninterruptible(Effect.suspend(() => settled ? Effect.void : settle({
|
|
1134
|
+
outcome: "unknown",
|
|
1135
|
+
outputTokens: reservation.maxOutputTokens
|
|
1136
|
+
})).pipe(Effect.orDie))), Effect.ensuring(Effect.sync(callSignal.dispose)));
|
|
1004
1137
|
});
|
|
1005
1138
|
}
|
|
1006
|
-
function
|
|
1007
|
-
|
|
1139
|
+
function createPaidCallSignal(parent, deadlineAt) {
|
|
1140
|
+
const controller = new AbortController();
|
|
1141
|
+
const deadlineMs = Date.parse(deadlineAt);
|
|
1142
|
+
const remainingMs = deadlineMs - Date.now();
|
|
1143
|
+
let deadlineExceeded = !Number.isFinite(deadlineMs) || remainingMs <= 0;
|
|
1144
|
+
let timer;
|
|
1145
|
+
const onParentAbort = () => controller.abort(parent?.reason);
|
|
1146
|
+
if (parent) {
|
|
1147
|
+
if (parent.aborted) controller.abort(parent.reason);
|
|
1148
|
+
else parent.addEventListener("abort", onParentAbort, { once: true });
|
|
1149
|
+
}
|
|
1150
|
+
if (remainingMs > 0 && !controller.signal.aborted) timer = setTimeout(() => {
|
|
1151
|
+
deadlineExceeded = true;
|
|
1152
|
+
controller.abort(/* @__PURE__ */ new Error("model change deadline elapsed"));
|
|
1153
|
+
}, Math.min(remainingMs, 2147483647));
|
|
1154
|
+
return {
|
|
1155
|
+
expired: deadlineExceeded,
|
|
1156
|
+
isDeadlineExceeded: () => deadlineExceeded || Number.isFinite(deadlineMs) && Date.now() >= deadlineMs,
|
|
1157
|
+
signal: controller.signal,
|
|
1158
|
+
dispose: () => {
|
|
1159
|
+
if (timer) clearTimeout(timer);
|
|
1160
|
+
parent?.removeEventListener("abort", onParentAbort);
|
|
1161
|
+
if (!controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("model probe cancelled"));
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1008
1164
|
}
|
|
1009
|
-
function
|
|
1010
|
-
if (
|
|
1011
|
-
|
|
1012
|
-
if (
|
|
1165
|
+
function assertProbeStopReason(message, kind) {
|
|
1166
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") throw new PiModelProbeError(`candidate streamed an ${message.stopReason} response: ${message.errorMessage ?? "unknown provider error"}`, "known");
|
|
1167
|
+
if (message.stopReason === "length" || message.stopReason === "deferred") throw new PiModelProbeError(`candidate response was truncated or deferred during ${kind}`, "known");
|
|
1168
|
+
if (kind !== "initial" && message.stopReason !== "stop") throw new PiModelProbeError(`candidate used unsupported stop reason ${message.stopReason} during ${kind}`, "known");
|
|
1169
|
+
}
|
|
1170
|
+
function assertCanaryStopReason(message) {
|
|
1171
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") throw new PiModelProbeError(`model canary streamed an ${message.stopReason} response: ${message.errorMessage ?? "unknown provider error"}`, "known");
|
|
1172
|
+
if (message.stopReason === "length" || message.stopReason === "deferred" || message.stopReason !== "stop") throw new PiModelProbeError(`model canary used unsupported stop reason ${message.stopReason}`, "known");
|
|
1173
|
+
}
|
|
1174
|
+
function findSingleProbeToolCall(message) {
|
|
1175
|
+
const calls = findToolCalls(message);
|
|
1176
|
+
if (calls.length !== 1 || calls[0]?.name !== "rivus_model_management_probe") return;
|
|
1177
|
+
const call = calls[0];
|
|
1013
1178
|
return {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
...source.compat ? { compat: source.compat } : {},
|
|
1017
|
-
contextWindow: 1e6,
|
|
1018
|
-
cost: {
|
|
1019
|
-
cacheRead: .006,
|
|
1020
|
-
cacheWrite: 0,
|
|
1021
|
-
input: .3,
|
|
1022
|
-
output: 1.2
|
|
1023
|
-
},
|
|
1024
|
-
id: target.model,
|
|
1025
|
-
input: ["text", "image"],
|
|
1026
|
-
maxTokens: 384e3,
|
|
1027
|
-
name: "DeepSeek V4.1 Flash",
|
|
1028
|
-
reasoning: true,
|
|
1029
|
-
...source.samplingParams ? { samplingParams: source.samplingParams } : {},
|
|
1030
|
-
...source.thinkingLevelMap ? { thinkingLevelMap: source.thinkingLevelMap } : {}
|
|
1179
|
+
arguments: call.arguments,
|
|
1180
|
+
id: call.id
|
|
1031
1181
|
};
|
|
1032
1182
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
this.messageIndex = messageIndex;
|
|
1042
|
-
}
|
|
1043
|
-
};
|
|
1044
|
-
/**
|
|
1045
|
-
* Check the structural parts of a persisted Pi transcript that providers need for
|
|
1046
|
-
* the next request. The transcript is never returned or copied; only counts are
|
|
1047
|
-
* exposed for diagnostics. This deliberately rejects orphaned tool calls/results
|
|
1048
|
-
* instead of deleting or rewriting history during a model change.
|
|
1049
|
-
*/
|
|
1050
|
-
function inspectPiSessionHistory(sessionManager) {
|
|
1051
|
-
const context = sessionManager.buildSessionContext();
|
|
1052
|
-
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
1053
|
-
let assistantThinkingBlocks = 0;
|
|
1054
|
-
let assistantToolCalls = 0;
|
|
1055
|
-
let imageBlocks = 0;
|
|
1056
|
-
let toolResults = 0;
|
|
1057
|
-
for (const [messageIndex, message] of context.messages.entries()) {
|
|
1058
|
-
if (!isRecord$2(message) || typeof message.role !== "string") throw new PiHistoryCompatibilityError("Pi session history contains a malformed message", messageIndex);
|
|
1059
|
-
if (message.role === "assistant") {
|
|
1060
|
-
const content = message.content;
|
|
1061
|
-
if (!Array.isArray(content)) throw new PiHistoryCompatibilityError("Pi assistant history message content must be an array", messageIndex);
|
|
1062
|
-
for (const block of content) {
|
|
1063
|
-
if (!isRecord$2(block) || typeof block.type !== "string") throw new PiHistoryCompatibilityError("Pi assistant history contains a malformed content block", messageIndex);
|
|
1064
|
-
if (block.type === "thinking") {
|
|
1065
|
-
if (typeof block.thinking !== "string") throw new PiHistoryCompatibilityError("Pi assistant thinking content is malformed", messageIndex);
|
|
1066
|
-
assistantThinkingBlocks += 1;
|
|
1067
|
-
continue;
|
|
1068
|
-
}
|
|
1069
|
-
if (block.type === "toolCall") {
|
|
1070
|
-
if (typeof block.id !== "string" || !block.id || typeof block.name !== "string" || !block.name) throw new PiHistoryCompatibilityError("Pi assistant tool call is malformed", messageIndex);
|
|
1071
|
-
if (!isRecord$2(block.arguments)) throw new PiHistoryCompatibilityError("Pi assistant tool call arguments are malformed", messageIndex);
|
|
1072
|
-
pendingToolCalls.set(block.id, block.name);
|
|
1073
|
-
assistantToolCalls += 1;
|
|
1074
|
-
continue;
|
|
1075
|
-
}
|
|
1076
|
-
if (block.type === "text") {
|
|
1077
|
-
if (typeof block.text !== "string") throw new PiHistoryCompatibilityError("Pi assistant text content is malformed", messageIndex);
|
|
1078
|
-
continue;
|
|
1079
|
-
}
|
|
1080
|
-
if (block.type === "image") {
|
|
1081
|
-
if (typeof block.data !== "string" || typeof block.mimeType !== "string") throw new PiHistoryCompatibilityError("Pi assistant image content is malformed", messageIndex);
|
|
1082
|
-
imageBlocks += 1;
|
|
1083
|
-
continue;
|
|
1084
|
-
}
|
|
1085
|
-
throw new PiHistoryCompatibilityError(`Pi assistant content type is unsupported: ${String(block.type)}`, messageIndex);
|
|
1086
|
-
}
|
|
1087
|
-
continue;
|
|
1088
|
-
}
|
|
1089
|
-
if (message.role === "toolResult") {
|
|
1090
|
-
const content = message.content;
|
|
1091
|
-
if (!Array.isArray(content)) throw new PiHistoryCompatibilityError("Pi tool result content must be an array", messageIndex);
|
|
1092
|
-
if (typeof message.toolCallId !== "string" || !pendingToolCalls.has(message.toolCallId)) throw new PiHistoryCompatibilityError("Pi tool result has no preceding assistant tool call", messageIndex);
|
|
1093
|
-
if (typeof message.toolName !== "string" || !message.toolName) throw new PiHistoryCompatibilityError("Pi tool result is missing its tool name", messageIndex);
|
|
1094
|
-
if (pendingToolCalls.get(message.toolCallId) !== message.toolName) throw new PiHistoryCompatibilityError("Pi tool result tool name does not match its assistant tool call", messageIndex);
|
|
1095
|
-
imageBlocks += countImageBlocks(content);
|
|
1096
|
-
validatePlainContent(content, messageIndex, "tool result");
|
|
1097
|
-
pendingToolCalls.delete(message.toolCallId);
|
|
1098
|
-
toolResults += 1;
|
|
1099
|
-
continue;
|
|
1100
|
-
}
|
|
1101
|
-
if (message.role === "user") {
|
|
1102
|
-
if (typeof message.content !== "string") {
|
|
1103
|
-
if (!Array.isArray(message.content)) throw new PiHistoryCompatibilityError("Pi user history content must be text or an array", messageIndex);
|
|
1104
|
-
imageBlocks += countImageBlocks(message.content);
|
|
1105
|
-
validatePlainContent(message.content, messageIndex, "user");
|
|
1106
|
-
}
|
|
1107
|
-
continue;
|
|
1108
|
-
}
|
|
1109
|
-
if (message.role === "custom") {
|
|
1110
|
-
if (typeof message.customType !== "string" || typeof message.display !== "boolean") throw new PiHistoryCompatibilityError("Pi custom history message metadata is malformed", messageIndex);
|
|
1111
|
-
if (typeof message.content === "string") continue;
|
|
1112
|
-
if (!Array.isArray(message.content)) throw new PiHistoryCompatibilityError("Pi custom history content must be text or an array", messageIndex);
|
|
1113
|
-
imageBlocks += countImageBlocks(message.content);
|
|
1114
|
-
validatePlainContent(message.content, messageIndex, "custom");
|
|
1115
|
-
continue;
|
|
1116
|
-
}
|
|
1117
|
-
if (message.role === "bashExecution") {
|
|
1118
|
-
if (typeof message.command !== "string" || typeof message.output !== "string" || typeof message.cancelled !== "boolean" || typeof message.truncated !== "boolean" || message.exitCode !== void 0 && message.exitCode !== null && typeof message.exitCode !== "number" || message.fullOutputPath !== void 0 && typeof message.fullOutputPath !== "string" || message.excludeFromContext !== void 0 && typeof message.excludeFromContext !== "boolean") throw new PiHistoryCompatibilityError("Pi bash history message is malformed", messageIndex);
|
|
1119
|
-
continue;
|
|
1120
|
-
}
|
|
1121
|
-
if (message.role === "branchSummary") {
|
|
1122
|
-
if (typeof message.summary !== "string" || typeof message.fromId !== "string") throw new PiHistoryCompatibilityError("Pi branch summary history message is malformed", messageIndex);
|
|
1123
|
-
continue;
|
|
1124
|
-
}
|
|
1125
|
-
if (message.role === "compactionSummary") {
|
|
1126
|
-
if (typeof message.summary !== "string" || typeof message.tokensBefore !== "number" || !Number.isFinite(message.tokensBefore) || message.tokensBefore < 0) throw new PiHistoryCompatibilityError("Pi compaction summary history message is malformed", messageIndex);
|
|
1127
|
-
continue;
|
|
1128
|
-
}
|
|
1129
|
-
throw new PiHistoryCompatibilityError(`Pi history role is unsupported: ${String(message.role)}`, messageIndex);
|
|
1130
|
-
}
|
|
1131
|
-
if (pendingToolCalls.size > 0) throw new PiHistoryCompatibilityError("Pi session history contains an assistant tool call without a result", context.messages.length);
|
|
1132
|
-
return Object.freeze({
|
|
1133
|
-
assistantThinkingBlocks,
|
|
1134
|
-
assistantToolCalls,
|
|
1135
|
-
compatible: true,
|
|
1136
|
-
imageBlocks,
|
|
1137
|
-
messageCount: context.messages.length,
|
|
1138
|
-
toolResults
|
|
1183
|
+
function findToolCalls(message) {
|
|
1184
|
+
return message.content.flatMap((content) => {
|
|
1185
|
+
if (!isRecord$2(content) || content.type !== "toolCall") return [];
|
|
1186
|
+
return [{
|
|
1187
|
+
arguments: isRecord$2(content.arguments) ? content.arguments : {},
|
|
1188
|
+
id: typeof content.id === "string" ? content.id : "",
|
|
1189
|
+
name: typeof content.name === "string" ? content.name : ""
|
|
1190
|
+
}];
|
|
1139
1191
|
});
|
|
1140
1192
|
}
|
|
1141
|
-
function
|
|
1142
|
-
return content.filter((
|
|
1193
|
+
function readText(message) {
|
|
1194
|
+
return message.content.filter((content) => isRecord$2(content) && content.type === "text" && typeof content.text === "string").map((content) => content.text).join("");
|
|
1143
1195
|
}
|
|
1144
|
-
function
|
|
1145
|
-
return
|
|
1196
|
+
function snapshotAssistant(message) {
|
|
1197
|
+
return Object.freeze({
|
|
1198
|
+
content: Object.freeze(message.content.map((content) => cloneSerializable(content))),
|
|
1199
|
+
model: message.model,
|
|
1200
|
+
provider: message.provider,
|
|
1201
|
+
stopReason: message.stopReason,
|
|
1202
|
+
usage: Object.freeze(cloneSerializable(message.usage))
|
|
1203
|
+
});
|
|
1146
1204
|
}
|
|
1147
|
-
function
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
if (block.type === "text") {
|
|
1151
|
-
if (typeof block.text !== "string") throw new PiHistoryCompatibilityError(`Pi ${role} text content is malformed`, messageIndex);
|
|
1152
|
-
continue;
|
|
1153
|
-
}
|
|
1154
|
-
if (block.type === "image") {
|
|
1155
|
-
if (typeof block.data !== "string" || typeof block.mimeType !== "string") throw new PiHistoryCompatibilityError(`Pi ${role} image content is malformed`, messageIndex);
|
|
1156
|
-
continue;
|
|
1157
|
-
}
|
|
1158
|
-
throw new PiHistoryCompatibilityError(`Pi ${role} content type is unsupported: ${block.type}`, messageIndex);
|
|
1159
|
-
}
|
|
1205
|
+
function readOutputTokens(usage) {
|
|
1206
|
+
if (isRecord$2(usage) && typeof usage.output === "number" && Number.isSafeInteger(usage.output) && usage.output >= 0) return usage.output;
|
|
1207
|
+
return 0;
|
|
1160
1208
|
}
|
|
1161
1209
|
function isRecord$2(value) {
|
|
1162
1210
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1554,8 +1602,10 @@ function createOperationSignal(deadlineAt, parent) {
|
|
|
1554
1602
|
const controller = new AbortController();
|
|
1555
1603
|
let timer;
|
|
1556
1604
|
const onParentAbort = () => controller.abort(parent?.reason);
|
|
1557
|
-
if (parent)
|
|
1558
|
-
|
|
1605
|
+
if (parent) {
|
|
1606
|
+
if (parent.aborted) onParentAbort();
|
|
1607
|
+
else parent.addEventListener("abort", onParentAbort, { once: true });
|
|
1608
|
+
}
|
|
1559
1609
|
if (deadlineAt !== void 0) {
|
|
1560
1610
|
const remaining = Date.parse(deadlineAt) - Date.now();
|
|
1561
1611
|
if (!Number.isFinite(remaining) || remaining <= 0) controller.abort(/* @__PURE__ */ new Error("Pi model management deadline elapsed"));
|
|
@@ -1583,6 +1633,14 @@ function assertOperationActive(operation) {
|
|
|
1583
1633
|
}
|
|
1584
1634
|
}
|
|
1585
1635
|
//#endregion
|
|
1636
|
+
//#region src/adapters/pi/skills/skill-command.ts
|
|
1637
|
+
const SKILL_COMMAND_PREFIX = "/skill:";
|
|
1638
|
+
/** Returns the Skill named by a `/skill:<name>` command, or undefined when the text is not a Skill command. */
|
|
1639
|
+
function skillCommandName(text) {
|
|
1640
|
+
if (!text.startsWith(SKILL_COMMAND_PREFIX)) return;
|
|
1641
|
+
return text.slice(7).split(/\s/, 1)[0];
|
|
1642
|
+
}
|
|
1643
|
+
//#endregion
|
|
1586
1644
|
//#region src/adapters/pi/skills/pi-skill-catalog.ts
|
|
1587
1645
|
var InvalidPiSkillCatalog = class extends Error {
|
|
1588
1646
|
name = "InvalidPiSkillCatalog";
|
|
@@ -1602,9 +1660,8 @@ function validateSkillName(name) {
|
|
|
1602
1660
|
if (typeof name !== "string" || name.length === 0 || name.length > 64 || !/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(name) || name.includes("--")) throw new InvalidPiSkillCatalog(`Pi Skill discovery contains unsafe name: ${String(name)}`);
|
|
1603
1661
|
}
|
|
1604
1662
|
function validatePiSkillCommand(text, skillNames) {
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
if (!skillNames.has(name)) throw new InvalidPiSkillCatalog(`Unknown Pi Skill: ${name}`);
|
|
1663
|
+
const name = skillCommandName(text);
|
|
1664
|
+
if (name !== void 0 && !skillNames.has(name)) throw new InvalidPiSkillCatalog(`Unknown Pi Skill: ${name}`);
|
|
1608
1665
|
}
|
|
1609
1666
|
//#endregion
|
|
1610
1667
|
//#region src/adapters/pi/skills/pi-skill-sources.ts
|
|
@@ -1626,7 +1683,7 @@ async function optionalDirectory(path) {
|
|
|
1626
1683
|
try {
|
|
1627
1684
|
canonical = await realpath(path);
|
|
1628
1685
|
} catch (error) {
|
|
1629
|
-
if (isMissingPath(error)) return
|
|
1686
|
+
if (isMissingPath(error)) return;
|
|
1630
1687
|
throw new InvalidPiSkillSource(`failed to resolve optional user Skill directory: ${path}`, { cause: error });
|
|
1631
1688
|
}
|
|
1632
1689
|
if (!(await stat(canonical)).isDirectory()) throw new InvalidPiSkillSource(`user Skill source must be a directory: ${path}`);
|
|
@@ -1795,8 +1852,8 @@ async function createPiAssembly(options) {
|
|
|
1795
1852
|
const { model, sessionCwd, sessionDirectory, thinkingLevel, resources, ...restOptions } = sessionOptions;
|
|
1796
1853
|
const activeResources = resources ?? await createResources();
|
|
1797
1854
|
const effectiveThinkingLevel = thinkingLevel ?? options.defaultThinkingLevel;
|
|
1798
|
-
const nativeSessionManager = sessionDirectory ? SessionManager.create(sessionCwd ?? options.cwd, sessionDirectory) : void 0;
|
|
1799
|
-
const
|
|
1855
|
+
const nativeSessionManager = sessionDirectory !== void 0 || sessionCwd !== void 0 ? SessionManager.create(sessionCwd ?? options.cwd, sessionDirectory) : void 0;
|
|
1856
|
+
const nativeOptions = activeResources.withSessionOptions({
|
|
1800
1857
|
modelRuntime: nativeModelRuntime,
|
|
1801
1858
|
...restOptions.noTools === void 0 ? {} : { noTools: restOptions.noTools },
|
|
1802
1859
|
...restOptions.tools === void 0 ? {} : { tools: [...restOptions.tools] },
|
|
@@ -1809,7 +1866,8 @@ async function createPiAssembly(options) {
|
|
|
1809
1866
|
...model ? { model } : {},
|
|
1810
1867
|
...effectiveThinkingLevel ? { thinkingLevel: effectiveThinkingLevel } : {},
|
|
1811
1868
|
...nativeSessionManager ? { sessionManager: nativeSessionManager } : {}
|
|
1812
|
-
})
|
|
1869
|
+
});
|
|
1870
|
+
const sessionResult = await createAgentSession(nativeOptions);
|
|
1813
1871
|
return {
|
|
1814
1872
|
...sessionResult,
|
|
1815
1873
|
session: sessionResult.session,
|
|
@@ -1839,12 +1897,12 @@ function createPiSkillReadTool(options) {
|
|
|
1839
1897
|
});
|
|
1840
1898
|
return createReadToolDefinition(options.cwd, { operations: {
|
|
1841
1899
|
access: async (absolutePath) => {
|
|
1842
|
-
|
|
1900
|
+
const path = await authorize(absolutePath, allowedSources);
|
|
1901
|
+
await stat(path);
|
|
1843
1902
|
},
|
|
1844
1903
|
readFile: async (absolutePath) => readFile(await authorize(absolutePath, allowedSources))
|
|
1845
1904
|
} });
|
|
1846
1905
|
}
|
|
1847
|
-
const createPiProjectSkillReadTool = createPiSkillReadTool;
|
|
1848
1906
|
function createPiSkillReadTools(options) {
|
|
1849
1907
|
if (options.skillPaths.length === 0 || options.runtimeToolIds.includes("read")) return [];
|
|
1850
1908
|
return [createPiSkillReadTool(options)];
|
|
@@ -1862,26 +1920,11 @@ async function authorize(path, sources) {
|
|
|
1862
1920
|
throw new ProjectSkillReadDenied("read is restricted to the bound Pi Skill sources");
|
|
1863
1921
|
}
|
|
1864
1922
|
//#endregion
|
|
1865
|
-
//#region src/adapters/pi/skills/project-skill-catalog.ts
|
|
1866
|
-
var InvalidProjectSkillCatalog = class extends Error {
|
|
1867
|
-
name = "InvalidProjectSkillCatalog";
|
|
1868
|
-
};
|
|
1869
|
-
function validateProjectSkillCatalog(input) {
|
|
1870
|
-
const diagnostic = input.diagnostics.find(({ type }) => type === "error" || type === "collision");
|
|
1871
|
-
if (diagnostic) throw new InvalidProjectSkillCatalog(`Project Skill discovery failed: ${diagnostic.message}${diagnostic.path ? ` (${diagnostic.path})` : ""}`);
|
|
1872
|
-
return new Set(input.skills.map(({ name }) => name));
|
|
1873
|
-
}
|
|
1874
|
-
function validateProjectSkillCommand(text, skillNames) {
|
|
1875
|
-
if (!text.startsWith("/skill:")) return;
|
|
1876
|
-
const name = text.slice(7).split(/\s/, 1)[0];
|
|
1877
|
-
if (!skillNames.has(name)) throw new InvalidProjectSkillCatalog(`Unknown or ungranted Project Skill: ${name}`);
|
|
1878
|
-
}
|
|
1879
|
-
//#endregion
|
|
1880
1923
|
//#region src/adapters/pi/tool-execution/pi-bash-tool.ts
|
|
1881
1924
|
const DEFAULT_TIMEOUT_SECONDS = 120;
|
|
1882
1925
|
/** Keep Pi's native execution, rendering and abort cleanup with a bounded default. */
|
|
1883
1926
|
function createPiBashTool(cwd, options) {
|
|
1884
|
-
const { commandPrefix, shellPath, spawnHook } = options ?? {};
|
|
1927
|
+
const { commandPrefix, recovery, shellPath, spawnHook } = options ?? {};
|
|
1885
1928
|
const tool = createBashToolDefinition(cwd, {
|
|
1886
1929
|
...commandPrefix === void 0 ? {} : { commandPrefix },
|
|
1887
1930
|
...shellPath === void 0 ? {} : { shellPath },
|
|
@@ -1900,15 +1943,29 @@ function createPiBashTool(cwd, options) {
|
|
|
1900
1943
|
}
|
|
1901
1944
|
}
|
|
1902
1945
|
},
|
|
1903
|
-
execute: (id, args, signal, onUpdate, context) =>
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1946
|
+
execute: async (id, args, signal, onUpdate, context) => {
|
|
1947
|
+
if (recovery) {
|
|
1948
|
+
const recoveryResult = await recovery({
|
|
1949
|
+
args: {
|
|
1950
|
+
...args,
|
|
1951
|
+
timeout: args.timeout === void 0 ? DEFAULT_TIMEOUT_SECONDS : args.timeout
|
|
1952
|
+
},
|
|
1953
|
+
...context === void 0 ? {} : { context },
|
|
1954
|
+
...signal === void 0 ? {} : { signal }
|
|
1955
|
+
});
|
|
1956
|
+
if (recoveryResult !== void 0) return recoveryResult;
|
|
1957
|
+
}
|
|
1958
|
+
if (args.command.includes("rivus-attachment-recover")) throw new Error("Attachment recovery action is not authorized for this Run");
|
|
1959
|
+
return tool.execute(id, {
|
|
1960
|
+
...args,
|
|
1961
|
+
timeout: args.timeout === void 0 ? DEFAULT_TIMEOUT_SECONDS : args.timeout
|
|
1962
|
+
}, signal, onUpdate, context);
|
|
1963
|
+
}
|
|
1907
1964
|
});
|
|
1908
1965
|
}
|
|
1909
1966
|
//#endregion
|
|
1910
1967
|
//#region src/adapters/pi/tool-execution/pi-tool-proxy.ts
|
|
1911
|
-
function createPiToolProxyDefinitions
|
|
1968
|
+
function createPiToolProxyDefinitions(options) {
|
|
1912
1969
|
const names = /* @__PURE__ */ new Set();
|
|
1913
1970
|
return options.tools.map((tool) => {
|
|
1914
1971
|
const name = toPiToolName(tool.id);
|
|
@@ -1918,28 +1975,42 @@ function createPiToolProxyDefinitions$1(options) {
|
|
|
1918
1975
|
description: tool.description,
|
|
1919
1976
|
execute: async (callId, input, signal) => {
|
|
1920
1977
|
const activeInput = options.getActiveInput();
|
|
1921
|
-
const
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1978
|
+
const abortSignal = signal ?? activeInput?.abortSignal;
|
|
1979
|
+
const execution = Effect.gen(function* () {
|
|
1980
|
+
const prepared = yield* Effect.try({
|
|
1981
|
+
catch: (error) => error,
|
|
1982
|
+
try: () => {
|
|
1983
|
+
const origin = activeInput?.invocation;
|
|
1984
|
+
if (!activeInput || !origin) throw new Error(`tool ${tool.id} requires an active agent run with a trusted invocation`);
|
|
1985
|
+
const invocation = normalizeAgentInvocation(origin);
|
|
1986
|
+
if (!invocation.tenantKey) throw new Error(`tool ${tool.id} requires a trusted tenant identity`);
|
|
1987
|
+
throwIfAborted(abortSignal ?? activeInput.abortSignal);
|
|
1988
|
+
const inputDigest = createToolInputDigest(input);
|
|
1989
|
+
const operationId = createBoundId("operation", {
|
|
1990
|
+
agentId: options.agentId,
|
|
1991
|
+
inputDigest,
|
|
1992
|
+
instanceId: options.instanceId,
|
|
1993
|
+
sourceMessageId: invocation.sourceMessageId,
|
|
1994
|
+
toolId: tool.id,
|
|
1995
|
+
toolVersion: tool.version
|
|
1996
|
+
});
|
|
1997
|
+
const approvalId = createBoundId("approval", {
|
|
1998
|
+
callId,
|
|
1999
|
+
operationId,
|
|
2000
|
+
runId: activeInput.runId
|
|
2001
|
+
});
|
|
2002
|
+
if (requiresToolApproval(tool.risk) && invocation.allowedActorOpenIds.length === 0) throw new Error(`tool ${tool.id} requires at least one trusted approval actor`);
|
|
2003
|
+
return {
|
|
2004
|
+
activeInput,
|
|
2005
|
+
approvalId,
|
|
2006
|
+
inputDigest,
|
|
2007
|
+
invocation,
|
|
2008
|
+
operationId
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
});
|
|
2012
|
+
const { approvalId, inputDigest, invocation, operationId } = prepared;
|
|
2013
|
+
if (requiresToolApproval(tool.risk)) yield* options.approvals.requestApproval({
|
|
1943
2014
|
agentId: options.agentId,
|
|
1944
2015
|
allowedActorOpenIds: invocation.allowedActorOpenIds,
|
|
1945
2016
|
approvalId,
|
|
@@ -1949,47 +2020,57 @@ function createPiToolProxyDefinitions$1(options) {
|
|
|
1949
2020
|
instanceId: options.instanceId,
|
|
1950
2021
|
operationId,
|
|
1951
2022
|
risk: tool.risk,
|
|
1952
|
-
runId: activeInput.runId,
|
|
1953
|
-
sessionKey: activeInput.sessionKey,
|
|
1954
|
-
signal:
|
|
2023
|
+
runId: prepared.activeInput.runId,
|
|
2024
|
+
sessionKey: prepared.activeInput.sessionKey,
|
|
2025
|
+
signal: abortSignal,
|
|
1955
2026
|
sourceMessageId: invocation.sourceMessageId,
|
|
1956
2027
|
tenantKey: invocation.tenantKey,
|
|
1957
2028
|
toolId: tool.id,
|
|
1958
2029
|
toolVersion: tool.version
|
|
1959
2030
|
});
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
2031
|
+
const authority = yield* Effect.try({
|
|
2032
|
+
catch: (error) => error,
|
|
2033
|
+
try: () => {
|
|
2034
|
+
throwIfAborted(abortSignal ?? prepared.activeInput.abortSignal);
|
|
2035
|
+
return createInvocationAuthority({
|
|
2036
|
+
agentId: options.agentId,
|
|
2037
|
+
allowedActorOpenIds: invocation.allowedActorOpenIds,
|
|
2038
|
+
...invocation.conversationId ? { conversationId: invocation.conversationId } : {},
|
|
2039
|
+
endpointId: invocation.endpointId,
|
|
2040
|
+
instanceId: options.instanceId,
|
|
2041
|
+
...invocation.memory ? { memory: {
|
|
2042
|
+
...invocation.memory,
|
|
2043
|
+
scopes: options.memoryScopes ?? []
|
|
2044
|
+
} } : {},
|
|
2045
|
+
runId: prepared.activeInput.runId,
|
|
2046
|
+
sessionKey: prepared.activeInput.sessionKey,
|
|
2047
|
+
sourceMessageId: invocation.sourceMessageId,
|
|
2048
|
+
tenantKey: invocation.tenantKey,
|
|
2049
|
+
toolGrantSet: options.toolGrantSet
|
|
2050
|
+
});
|
|
2051
|
+
}
|
|
2052
|
+
});
|
|
2053
|
+
const result = yield* options.broker.execute({
|
|
2054
|
+
authority,
|
|
2055
|
+
callId,
|
|
2056
|
+
input,
|
|
2057
|
+
operationId,
|
|
2058
|
+
...requiresToolApproval(tool.risk) ? { approvalId } : {},
|
|
2059
|
+
toolId: tool.id,
|
|
2060
|
+
version: tool.version
|
|
2061
|
+
});
|
|
2062
|
+
return yield* Effect.try({
|
|
2063
|
+
catch: (error) => error,
|
|
2064
|
+
try: () => ({
|
|
2065
|
+
content: [{
|
|
2066
|
+
text: stringifyToolResult(result),
|
|
2067
|
+
type: "text"
|
|
2068
|
+
}],
|
|
2069
|
+
details: result
|
|
2070
|
+
})
|
|
2071
|
+
});
|
|
1985
2072
|
});
|
|
1986
|
-
return {
|
|
1987
|
-
content: [{
|
|
1988
|
-
text: stringifyToolResult(result),
|
|
1989
|
-
type: "text"
|
|
1990
|
-
}],
|
|
1991
|
-
details: result
|
|
1992
|
-
};
|
|
2073
|
+
return Effect.runPromiseExit(execution, { signal: abortSignal }).then(unwrapExit);
|
|
1993
2074
|
},
|
|
1994
2075
|
executionMode: "sequential",
|
|
1995
2076
|
label: tool.id,
|
|
@@ -2002,7 +2083,7 @@ function createPiToolProxyDefinitions$1(options) {
|
|
|
2002
2083
|
function createToolInputDigest(input) {
|
|
2003
2084
|
return createToolInputDigest$1(input, createSha256Digest);
|
|
2004
2085
|
}
|
|
2005
|
-
function createPiToolNameResolver
|
|
2086
|
+
function createPiToolNameResolver(tools) {
|
|
2006
2087
|
const toolIdsByPiName = new Map(tools.map((tool) => [toPiToolName(tool.id), tool.id]));
|
|
2007
2088
|
return (toolName) => toolIdsByPiName.get(toolName) ?? toolName;
|
|
2008
2089
|
}
|
|
@@ -2059,85 +2140,5 @@ function createPiManagedRunBinding(options) {
|
|
|
2059
2140
|
});
|
|
2060
2141
|
}
|
|
2061
2142
|
//#endregion
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
return fromEffectAgentLoop(createPiAgentLoop$1({
|
|
2065
|
-
...options.supportsSteering ? { supportsSteering: true } : {},
|
|
2066
|
-
...options.disposeSessionAfterRun === void 0 ? {} : { disposeSessionAfterRun: options.disposeSessionAfterRun },
|
|
2067
|
-
...options.modelContentObserver ? { modelContentObserver: options.modelContentObserver } : {},
|
|
2068
|
-
...options.runBoundary ? { runBoundary: options.runBoundary } : {},
|
|
2069
|
-
resolveSession: async (input) => toEffectPiAgentSessionHandle(await options.resolveSession(toCompatibilityAgentLoopInput(input)))
|
|
2070
|
-
}));
|
|
2071
|
-
}
|
|
2072
|
-
function createPiSdkAgentLoop(options) {
|
|
2073
|
-
return fromEffectAgentLoop(createPiSdkAgentLoop$1(options));
|
|
2074
|
-
}
|
|
2075
|
-
function toEffectPiAgentSessionHandle(handle) {
|
|
2076
|
-
return {
|
|
2077
|
-
...handle.activate ? { activate: (input) => handle.activate(toCompatibilityAgentLoopInput(input)) } : {},
|
|
2078
|
-
...handle.dispose ? { dispose: handle.dispose } : {},
|
|
2079
|
-
...handle.deactivate ? { deactivate: handle.deactivate } : {},
|
|
2080
|
-
...handle.preparePrompt ? { preparePrompt: (input) => handle.preparePrompt(toCompatibilityAgentLoopInput(input)) } : {},
|
|
2081
|
-
...handle.resolveToolName ? { resolveToolName: handle.resolveToolName } : {},
|
|
2082
|
-
...handle.refreshResources ? { refreshResources: handle.refreshResources } : {},
|
|
2083
|
-
session: handle.session
|
|
2084
|
-
};
|
|
2085
|
-
}
|
|
2086
|
-
function fromEffectPiAgentSessionHandle(handle) {
|
|
2087
|
-
return {
|
|
2088
|
-
...handle.activate ? { activate: (input) => handle.activate(toEffectAgentLoopInput(input)) } : {},
|
|
2089
|
-
...handle.dispose ? { dispose: handle.dispose } : {},
|
|
2090
|
-
...handle.deactivate ? { deactivate: handle.deactivate } : {},
|
|
2091
|
-
...handle.preparePrompt ? { preparePrompt: (input) => handle.preparePrompt(toEffectAgentLoopInput(input)) } : {},
|
|
2092
|
-
...handle.resolveToolName ? { resolveToolName: handle.resolveToolName } : {},
|
|
2093
|
-
...handle.refreshResources ? { refreshResources: handle.refreshResources } : {},
|
|
2094
|
-
session: handle.session
|
|
2095
|
-
};
|
|
2096
|
-
}
|
|
2097
|
-
//#endregion
|
|
2098
|
-
//#region src/adapters/compatibility/agent-execution/pi/pi-session-registry.ts
|
|
2099
|
-
function createPiSessionRegistry(options) {
|
|
2100
|
-
const compatibilityHandles = /* @__PURE__ */ new WeakMap();
|
|
2101
|
-
const registry = createPiSessionRegistry$1({ createSession: async (input) => {
|
|
2102
|
-
const handle = await options.createSession(toCompatibilityAgentLoopInput(input));
|
|
2103
|
-
const effectHandle = toEffectPiAgentSessionHandle(handle);
|
|
2104
|
-
compatibilityHandles.set(effectHandle, handle);
|
|
2105
|
-
return effectHandle;
|
|
2106
|
-
} });
|
|
2107
|
-
return {
|
|
2108
|
-
dispose: (sessionKey) => registry.dispose(sessionKey),
|
|
2109
|
-
disposeAll: () => registry.disposeAll(),
|
|
2110
|
-
list: async () => (await registry.list()).map((effectHandle) => {
|
|
2111
|
-
const cached = compatibilityHandles.get(effectHandle);
|
|
2112
|
-
if (cached) return cached;
|
|
2113
|
-
const handle = fromEffectPiAgentSessionHandle(effectHandle);
|
|
2114
|
-
compatibilityHandles.set(effectHandle, handle);
|
|
2115
|
-
return handle;
|
|
2116
|
-
}),
|
|
2117
|
-
resolve: async (input) => {
|
|
2118
|
-
const effectHandle = await registry.resolve(toEffectAgentLoopInput(input));
|
|
2119
|
-
const cached = compatibilityHandles.get(effectHandle);
|
|
2120
|
-
if (cached) return cached;
|
|
2121
|
-
const handle = fromEffectPiAgentSessionHandle(effectHandle);
|
|
2122
|
-
compatibilityHandles.set(effectHandle, handle);
|
|
2123
|
-
return handle;
|
|
2124
|
-
},
|
|
2125
|
-
size: () => registry.size()
|
|
2126
|
-
};
|
|
2127
|
-
}
|
|
2128
|
-
//#endregion
|
|
2129
|
-
//#region src/adapters/compatibility/agent-execution/pi/pi-tool-proxy.ts
|
|
2130
|
-
function createPiToolProxyDefinitions(options) {
|
|
2131
|
-
return createPiToolProxyDefinitions$1({
|
|
2132
|
-
...options,
|
|
2133
|
-
getActiveInput: () => {
|
|
2134
|
-
const input = options.getActiveInput();
|
|
2135
|
-
return input ? toEffectAgentLoopInput(input) : void 0;
|
|
2136
|
-
}
|
|
2137
|
-
});
|
|
2138
|
-
}
|
|
2139
|
-
function createPiToolNameResolver(tools) {
|
|
2140
|
-
return createPiToolNameResolver$1(tools);
|
|
2141
|
-
}
|
|
2142
|
-
//#endregion
|
|
2143
|
-
export { InvalidPiSkillCatalog, InvalidPiSkillSource, InvalidProjectSkillCatalog, PI_MODEL_PROBE_FOLLOWUP_RESULT, PI_MODEL_PROBE_TOOL_NAME, PI_MODEL_PROBE_TOOL_RESULT, PI_SKILL_READER_TOOL_NAME, PiHistoryCompatibilityError, PiModelProbeError, PiModelResolutionError, PiModelRuntimeMutationUnknownError, PiModelTargetCompatibilityError, ProjectSkillReadDenied, assertPiSessionHistoryCompatible, createPiAgentLoop as createLegacyPiAgentLoop, createPiSdkAgentLoop as createLegacyPiSdkAgentLoop, createPiSessionRegistry as createLegacyPiSessionRegistry, createPiToolNameResolver as createLegacyPiToolNameResolver, createPiToolProxyDefinitions as createLegacyPiToolProxyDefinitions, createPiAgentLoop$1 as createPiAgentLoop, createPiAssembly, createPiBashTool, createPiManagedRunBinding, createPiModelRuntime, createPiProjectSkillReadTool, createPiSdkAgentLoop$1 as createPiSdkAgentLoop, createPiSessionRegistry$1 as createPiSessionRegistry, createPiSessionResources, createPiSkillReadTool, createPiSkillReadTools, createPiSkillRuntime, createPiToolNameResolver$1 as createPiToolNameResolver, createPiToolProxyDefinitions$1 as createPiToolProxyDefinitions, ensurePiModel, fromEffectPiAgentSessionHandle as fromEffectLegacyPiAgentSessionHandle, inspectPiSessionHistory, mergePiProviderBaseUrlOverride, mergePiProviderModelDeclaration, probePiModel, refreshPiModelCatalog, resolvePiModel, resolvePiModelBinding, resolvePiSessionToolNames, resolvePiSkillSources, runPiModelCanary, runPiSessionPrompts, samePiProvider, toEffectPiAgentSessionHandle as toEffectLegacyPiAgentSessionHandle, validatePiSkillCatalog, validatePiSkillCommand, validateProjectSkillCatalog, validateProjectSkillCommand };
|
|
2143
|
+
export { createPiAgentLoop, createPiAssembly, createPiBashTool, createPiManagedRunBinding, createPiModelRuntime, createPiSessionRegistry, createPiSessionResources, createPiSkillReadTools, createPiSkillRuntime, createPiToolNameResolver, createPiToolProxyDefinitions, ensurePiModel, mergePiProviderBaseUrlOverride, resolvePiSessionToolNames, validatePiSkillCommand };
|
|
2144
|
+
|