@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/dist/pi.js CHANGED
@@ -1,14 +1,16 @@
1
- import { a as requiresToolApproval, l as normalizeAgentInvocation, r as createToolInputDigest$1, s as createInvocationAuthority } from "./chunks/tool-input-digest.js";
2
- import { _ as createAgentLoopToolExecutionStart, d as createAgentLoopModelExecutionStart, f as createAgentLoopSkillExecutionEnd, g as createAgentLoopToolExecutionEnd, h as createAgentLoopThinkingDelta, l as toEffectAgentLoopInput, m as createAgentLoopTextDelta, o as fromEffectAgentLoop, p as createAgentLoopSkillExecutionStart, s as toCompatibilityAgentLoopInput, u as createAgentLoopModelExecutionEnd, v as createAgentLoopToolExecutionUpdate } from "./chunks/agent-loop.js";
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) => handle.preparePrompt?.({
144
- ...input,
145
- text
146
- }) ?? text;
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 text = await prepare(input.text);
149
- if (!input.abortSignal.aborted) await session.prompt(text);
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((text) => ({
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
- text
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 text = await prepare(input.text);
181
- if (input.abortSignal.aborted) return;
182
- startPrompt(text);
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.text, ...remaining] : remaining;
193
- for (const acceptedText of accepted) {
194
- const prepared = await prepare(acceptedText);
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
- startPrompt(prepared);
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 prepared = await prepare(outcome.text);
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 (session.isStreaming) await session.steer(prepared);
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$1(options) {
306
+ function createPiAgentLoop(options) {
226
307
  return {
227
- ...options.supportsSteering ? { supportsSteering: true } : {},
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.unwrapScoped(Effect.acquireRelease(options.runBoundary.acquireRun({ signal: input.abortSignal }), (lease) => Effect.sync(lease.release)).pipe(Effect.as(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 void 0;
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 void 0;
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 void 0;
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 void 0;
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 void 0;
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 void 0;
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 void 0;
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 void 0;
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$1(options) {
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$3(content) && content.type === "thinking").length, 0);
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.catchAll((error) => {
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.zipRight(Effect.fail(probeError)));
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 samePiProvider(left, right) {
1007
- return left.provider.trim() === right.provider.trim();
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 knownPiDeclarativeModel(modelRuntime, target) {
1010
- if (target.provider !== "deepseek" || target.model !== "deepseek-flash") return void 0;
1011
- const source = modelRuntime.getModel("deepseek", "deepseek-v4-flash");
1012
- if (!source) return void 0;
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
- api: source.api,
1015
- baseUrl: source.baseUrl,
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
- //#endregion
1034
- //#region src/adapters/pi/model-management/pi-history-compatibility.ts
1035
- var PiHistoryCompatibilityError = class extends Error {
1036
- name = "PiHistoryCompatibilityError";
1037
- code = "model_change_history_incompatible";
1038
- messageIndex;
1039
- constructor(message, messageIndex) {
1040
- super(message);
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 countImageBlocks(content) {
1142
- return content.filter((block) => isRecord$2(block) && block.type === "image").length;
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 assertPiSessionHistoryCompatible(sessionManager) {
1145
- return inspectPiSessionHistory(sessionManager);
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 validatePlainContent(content, messageIndex, role) {
1148
- for (const block of content) {
1149
- if (!isRecord$2(block) || typeof block.type !== "string") throw new PiHistoryCompatibilityError(`Pi ${role} history contains a malformed content block`, messageIndex);
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) if (parent.aborted) onParentAbort();
1558
- else parent.addEventListener("abort", onParentAbort, { once: true });
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
- if (!text.startsWith("/skill:")) return;
1606
- const name = text.slice(7).split(/\s/, 1)[0];
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 void 0;
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 sessionResult = await createAgentSession(activeResources.withSessionOptions({
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
- await stat(await authorize(absolutePath, allowedSources));
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) => tool.execute(id, {
1904
- ...args,
1905
- timeout: args.timeout === void 0 ? DEFAULT_TIMEOUT_SECONDS : args.timeout
1906
- }, signal, onUpdate, context)
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$1(options) {
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 origin = activeInput?.invocation;
1922
- if (!activeInput || !origin) throw new Error(`tool ${tool.id} requires an active agent run with a trusted invocation`);
1923
- const invocation = normalizeAgentInvocation(origin);
1924
- if (!invocation.tenantKey) throw new Error(`tool ${tool.id} requires a trusted tenant identity`);
1925
- throwIfAborted(signal ?? activeInput.abortSignal);
1926
- const inputDigest = createToolInputDigest(input);
1927
- const operationId = createBoundId("operation", {
1928
- agentId: options.agentId,
1929
- inputDigest,
1930
- instanceId: options.instanceId,
1931
- sourceMessageId: invocation.sourceMessageId,
1932
- toolId: tool.id,
1933
- toolVersion: tool.version
1934
- });
1935
- const approvalId = createBoundId("approval", {
1936
- callId,
1937
- operationId,
1938
- runId: activeInput.runId
1939
- });
1940
- if (requiresToolApproval(tool.risk)) {
1941
- if (invocation.allowedActorOpenIds.length === 0) throw new Error(`tool ${tool.id} requires at least one trusted approval actor`);
1942
- await options.approvals.requestApproval({
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: signal ?? activeInput.abortSignal,
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
- throwIfAborted(signal ?? activeInput.abortSignal);
1961
- }
1962
- const result = await options.broker.execute({
1963
- authority: createInvocationAuthority({
1964
- agentId: options.agentId,
1965
- allowedActorOpenIds: invocation.allowedActorOpenIds,
1966
- ...invocation.conversationId ? { conversationId: invocation.conversationId } : {},
1967
- endpointId: invocation.endpointId,
1968
- instanceId: options.instanceId,
1969
- ...invocation.memory ? { memory: {
1970
- ...invocation.memory,
1971
- scopes: options.memoryScopes ?? []
1972
- } } : {},
1973
- runId: activeInput.runId,
1974
- sessionKey: activeInput.sessionKey,
1975
- sourceMessageId: invocation.sourceMessageId,
1976
- tenantKey: invocation.tenantKey,
1977
- toolGrantSet: options.toolGrantSet
1978
- }),
1979
- callId,
1980
- input,
1981
- operationId,
1982
- ...requiresToolApproval(tool.risk) ? { approvalId } : {},
1983
- toolId: tool.id,
1984
- version: tool.version
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$1(tools) {
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
- //#region src/adapters/compatibility/agent-execution/pi/pi-agent-loop.ts
2063
- function createPiAgentLoop(options) {
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
+