@pasko70/pibo 2.4.1 → 2.4.3

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.
Files changed (46) hide show
  1. package/dist/agent-runtime/context-build.js +100 -11
  2. package/dist/agent-runtime/profile-validation.js +3 -0
  3. package/dist/agent-runtime/resource-service.js +16 -0
  4. package/dist/agent-runtime/routed-session.js +180 -30
  5. package/dist/agent-runtime/testing/fake-adapter.js +7 -0
  6. package/dist/agent-runtimes/pi/adapter.js +2 -0
  7. package/dist/agent-runtimes/pi/routed-session.js +104 -43
  8. package/dist/agent-runtimes/pi/runtime.js +8 -5
  9. package/dist/apps/chat/web-app.js +21 -6
  10. package/dist/apps/chat-ui/assets/{dist-Cw9po47P.js → dist-C4JGcQjh.js} +1 -1
  11. package/dist/apps/chat-ui/assets/{dist-BeqHbnGN.js → dist-CF92Lv76.js} +1 -1
  12. package/dist/apps/chat-ui/assets/{dist-DTRjeLwO.js → dist-CJ0JS-bE.js} +1 -1
  13. package/dist/apps/chat-ui/assets/{dist-3YG57JXi.js → dist-DrgKyb9n.js} +1 -1
  14. package/dist/apps/chat-ui/assets/{dist-CrDtveZB.js → dist-VoMyV-PE.js} +1 -1
  15. package/dist/apps/chat-ui/assets/index-0WZI2phJ.css +1 -0
  16. package/dist/apps/chat-ui/assets/index-DhX1_aRM.js +228 -0
  17. package/dist/apps/chat-ui/index.html +2 -2
  18. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  19. package/dist/apps/vscode-artifacts/{pibo-vscode-ext-2.4.1.vsix → pibo-vscode-ext-2.4.3.vsix} +0 -0
  20. package/dist/cli.js +16 -6
  21. package/dist/core/context-build.js +66 -6
  22. package/dist/core/model-defaults.js +11 -3
  23. package/dist/core/session-router.js +285 -112
  24. package/dist/gateway/server.js +1 -0
  25. package/dist/loops/accounting.js +80 -0
  26. package/dist/loops/service.js +52 -13
  27. package/dist/loops/store.js +67 -19
  28. package/dist/loops/tools.js +3 -1
  29. package/dist/reliability/store.js +11 -6
  30. package/dist/runs/lifecycle.js +38 -1
  31. package/dist/runs/registry.js +46 -45
  32. package/dist/runs/tools.js +34 -24
  33. package/dist/subagents/context.js +48 -0
  34. package/dist/subagents/runtime-selection.js +28 -0
  35. package/dist/subagents/tool.js +28 -6
  36. package/dist/tools/codex-compat.js +1 -0
  37. package/dist/tools/contract.js +10 -0
  38. package/dist/tools/mcp-bridge.js +4 -2
  39. package/dist/tools/runtime/node-backend.js +8 -2
  40. package/dist/tools/runtime/python-backend.js +8 -2
  41. package/dist/tools/runtime/tool.js +1 -0
  42. package/dist/tools/session-tool-set.js +19 -12
  43. package/npm-shrinkwrap.json +2 -2
  44. package/package.json +1 -1
  45. package/dist/apps/chat-ui/assets/index-AjnP3ci-.js +0 -228
  46. package/dist/apps/chat-ui/assets/index-BJ56TREg.css +0 -1
@@ -1,5 +1,11 @@
1
+ import { createPiboRuntimeResolutionManifest, } from "../core/context-build.js";
1
2
  import { InitialSessionContext } from "../core/profiles.js";
2
3
  import { PIBO_AGENT_TOOL_NAMES, listAvailableAgents } from "../subagents/tool.js";
4
+ import { PIBO_DELEGATED_AGENT_CONTEXT_PATH } from "../subagents/context.js";
5
+ import { PIBO_RUN_TOOL_NAMES } from "../runs/tools.js";
6
+ import { PIBO_GOAL_TOOL_NAMES } from "../loops/tools.js";
7
+ import { CODEX_COMPAT_TOOL_NAMES } from "../tools/codex-compat.js";
8
+ import { isEnabledRuntimeToolProfile, materializePiboProfileTools, } from "../tools/session-tool-set.js";
3
9
  export function profileWithRuntimeInstance(profile, runtimeInstanceId) {
4
10
  if (profile.runtimeInstanceId === runtimeInstanceId)
5
11
  return profile;
@@ -41,10 +47,100 @@ export function uniqueRuntimeDiagnostics(diagnostics) {
41
47
  return true;
42
48
  });
43
49
  }
50
+ function uniqueNames(values) {
51
+ return [...new Set(values)];
52
+ }
44
53
  export function buildPortableRuntimeContextSnapshot(input) {
45
54
  const profile = input.profile;
46
55
  const nodes = [];
47
56
  const addNode = (node) => nodes.push({ ...node, order: nodes.length });
57
+ const availableAgents = listAvailableAgents(profile.subagents);
58
+ const delegatedSendAvailable = availableAgents.length > 0;
59
+ const toolContext = {
60
+ piboSessionId: input.piboSessionId,
61
+ piboRoomId: input.piboRoomId,
62
+ profileName: profile.profileName,
63
+ cwd: input.cwd,
64
+ };
65
+ const materializedProfileTools = materializePiboProfileTools(profile, toolContext);
66
+ const runtimeProfileTool = profile.tools.find(isEnabledRuntimeToolProfile);
67
+ const callableProfileTools = [
68
+ ...materializedProfileTools.map((tool) => ({ name: tool.definition.name, yieldable: tool.profile.yieldable })),
69
+ ...(runtimeProfileTool ? [{ name: "runtime", yieldable: runtimeProfileTool.yieldable }] : []),
70
+ ];
71
+ const profileToolNames = callableProfileTools.map((tool) => tool.name);
72
+ const directAgentToolNames = delegatedSendAvailable
73
+ ? PIBO_AGENT_TOOL_NAMES.filter((name) => name !== "pibo_agents_send_message")
74
+ : [];
75
+ const codexCompatToolNames = profile.toolPackages.codexCompat === true ? [...CODEX_COMPAT_TOOL_NAMES] : [];
76
+ const explicitYieldableToolNames = uniqueNames([
77
+ ...callableProfileTools.filter((tool) => tool.yieldable !== false).map((tool) => tool.name),
78
+ ...(delegatedSendAvailable ? PIBO_AGENT_TOOL_NAMES : []),
79
+ ...codexCompatToolNames,
80
+ ]);
81
+ const yieldableToolNames = profile.toolPackages.runControl === true
82
+ ? explicitYieldableToolNames
83
+ : delegatedSendAvailable ? ["pibo_agents_send_message"] : [];
84
+ const runControlAvailable = yieldableToolNames.length > 0;
85
+ const activeToolNames = uniqueNames([
86
+ ...profileToolNames,
87
+ ...directAgentToolNames,
88
+ ...codexCompatToolNames,
89
+ ...(profile.toolPackages.goalControl !== false ? PIBO_GOAL_TOOL_NAMES : []),
90
+ ...(runControlAvailable ? PIBO_RUN_TOOL_NAMES : []),
91
+ ]);
92
+ const activeToolPackages = [
93
+ ...(profile.toolPackages.goalControl !== false ? ["pibo-goal-control"] : []),
94
+ ...(profile.toolPackages.codexCompat === true ? ["codex-compat"] : []),
95
+ ...(runControlAvailable ? ["pibo-run-control"] : []),
96
+ ];
97
+ const managedToolDisplayNames = [
98
+ ...activeToolNames,
99
+ ...yieldableToolNames.map((name) => `yielded-target:${name}`),
100
+ ...availableAgents.map((agent) => `agent:${agent.name} (${agent.profile}) — ${agent.description}`),
101
+ ...activeToolPackages.map((name) => name === "pibo-run-control" && profile.toolPackages.runControl !== true
102
+ ? "package:pibo-run-control (automatic for delegation)"
103
+ : `package:${name}`),
104
+ ];
105
+ const manifestContextPaths = input.resources
106
+ ? input.resources.context.flatMap((contribution) => {
107
+ const path = contribution.materializedPath ?? contribution.path ?? contribution.sourcePath;
108
+ return path ? [path] : [];
109
+ })
110
+ : [
111
+ "pibo://runtime/session-context.md",
112
+ ...profile.contextFiles.filter((file) => file.enabled !== false).map((file) => file.key ?? file.path),
113
+ ...(delegatedSendAvailable ? [PIBO_DELEGATED_AGENT_CONTEXT_PATH] : []),
114
+ ];
115
+ const runtimeManifest = createPiboRuntimeResolutionManifest({
116
+ profile,
117
+ cwd: input.cwd,
118
+ adapterId: input.runtime.adapterId,
119
+ piboSessionId: input.piboSessionId,
120
+ piboRoomId: input.piboRoomId,
121
+ activeModel: input.activeModel,
122
+ thinkingLevel: input.thinkingLevel,
123
+ toolSurface: "pibo-managed-only",
124
+ activeToolNames,
125
+ yieldableToolNames,
126
+ activeToolPackages,
127
+ contextFilePaths: manifestContextPaths,
128
+ skillNames: input.resources
129
+ ? input.resources.skills.map((skill) => skill.name)
130
+ : profile.skills.filter((skill) => skill.enabled !== false).map((skill) => skill.name),
131
+ modelDefaults: input.modelDefaults,
132
+ subagentProfileResolver: input.subagentProfileResolver,
133
+ });
134
+ addNode({
135
+ id: "runtime-manifest",
136
+ kind: "runtime_manifest",
137
+ title: "Runtime Resolution Manifest",
138
+ source: "runtime",
139
+ state: "active",
140
+ badges: ["RESOLVED", "READ-ONLY", "PIBO-MANAGED-TOOLS"],
141
+ payloadJson: runtimeManifest,
142
+ notes: ["Resolution evidence for this inspection only. It is not a second profile configuration and is not injected into the agent prompt. Harness-native tool names may remain discoverable only at runtime."],
143
+ });
48
144
  addNode({
49
145
  id: "runtime",
50
146
  kind: "metadata",
@@ -62,16 +158,9 @@ export function buildPortableRuntimeContextSnapshot(input) {
62
158
  },
63
159
  payloadJson: input.runtime.capabilities,
64
160
  });
65
- const availableAgents = listAvailableAgents(profile.subagents);
66
- addRuntimeContributionGroup(nodes, "tools", "Pibo Tools and Delegated Agents", [
67
- ...profile.tools.filter((tool) => tool.enabled !== false).map((tool) => tool.name),
68
- ...(availableAgents.length > 0 ? PIBO_AGENT_TOOL_NAMES : []),
69
- ...availableAgents.map((agent) => `agent:${agent.name} (${agent.profile}) — ${agent.description}`),
70
- ...(profile.toolPackages.goalControl !== false ? ["package:pibo-goal-control"] : []),
71
- ...(profile.toolPackages.runControl === true ? ["package:pibo-run-control"] : []),
72
- ], input.runtime.capabilities.tools.piboManaged);
161
+ addRuntimeContributionGroup(nodes, "tools", "Pibo Tools and Delegated Agents", managedToolDisplayNames, input.runtime.capabilities.tools.piboManaged);
73
162
  addNativeToolInspectionNode(nodes, input.runtime.capabilities.tools.nativeToolInspection);
74
- if (profile.toolPackages.runControl === true) {
163
+ if (runControlAvailable) {
75
164
  addNativeToolYieldingNode(nodes, input.runtime.capabilities.tools.nativeToolYielding);
76
165
  }
77
166
  if (input.resources) {
@@ -138,7 +227,7 @@ export function buildPortableRuntimeContextSnapshot(input) {
138
227
  source: "profile",
139
228
  state: "active",
140
229
  metadata: {
141
- activeModel: input.activeModel,
230
+ activeModel: runtimeManifest.effectiveModel,
142
231
  mainModel: profile.mainModel,
143
232
  subagentModel: profile.subagentModel,
144
233
  mainThinkingLevel: profile.mainThinkingLevel ?? profile.thinkingLevel,
@@ -187,7 +276,7 @@ export function buildPortableRuntimeContextSnapshot(input) {
187
276
  piboSessionId: input.piboSessionId,
188
277
  piboRoomId: input.piboRoomId,
189
278
  cwd: input.cwd,
190
- activeModel: input.activeModel,
279
+ activeModel: runtimeManifest.effectiveModel,
191
280
  runtime: input.runtime,
192
281
  summary: {
193
282
  topLevelNodes: nodes.length,
@@ -58,6 +58,9 @@ export function validateAgentRuntimeProfileCapabilities(profile, capabilities) {
58
58
  if (enabledContextFiles.length > 0) {
59
59
  pushUnsupportedDeliveryDiagnostic(diagnostics, capabilities.context, "runtime_context_unsupported", "contextFiles", `Profile "${profile.profileName}" selects managed or automatic context`);
60
60
  }
61
+ if (enabledSubagents.length > 0) {
62
+ pushUnsupportedDeliveryDiagnostic(diagnostics, capabilities.context, "runtime_delegated_agent_context_unsupported", "subagents", `Profile "${profile.profileName}" requires delegated-agent management context`);
63
+ }
61
64
  if (profile.nativeSubagents !== undefined && typeof profile.nativeSubagents !== "boolean") {
62
65
  diagnostics.push({
63
66
  severity: "error",
@@ -9,6 +9,7 @@ import { isHttpServer, loadConfigUnresolved, } from "../mcp/config.js";
9
9
  import { redactMcpRuntimeError as redactResourceError, scopePiboMcpServerConfig, verifyPiboMcpServer, } from "../mcp/runtime-session.js";
10
10
  import { getMcpAgentContextFileFromConfig } from "../mcp/agent-context.js";
11
11
  import { getInstalledCliToolContextFile } from "../tools/registry.js";
12
+ import { getDelegatedAgentContextFile } from "../subagents/context.js";
12
13
  import { copyAgentRuntimeSkillDirectory, createAgentRuntimeResourcePaths, } from "./resource-files.js";
13
14
  const DEFAULT_MCP_VERIFY_TIMEOUT_MS = 15_000;
14
15
  const DEFAULT_MAX_SKILL_FILES = 2_048;
@@ -550,6 +551,21 @@ class RuntimeResourceSession {
550
551
  this.diagnostics.push({ severity: "error", code: "runtime_context_file_failed", message, contributionId: id });
551
552
  }
552
553
  }
554
+ const delegatedAgents = getDelegatedAgentContextFile(this.input.profile.subagents);
555
+ if (delegatedAgents) {
556
+ this.requiredContributionIds.add("context:delegated-agents");
557
+ this.context.push({
558
+ id: "context:delegated-agents",
559
+ kind: "generated",
560
+ source: "generated",
561
+ intent: "developer",
562
+ label: "Delegated Agent Management",
563
+ required: true,
564
+ order: 250,
565
+ path: delegatedAgents.path,
566
+ content: delegatedAgents.content,
567
+ });
568
+ }
553
569
  const installedTools = getInstalledCliToolContextFile();
554
570
  if (installedTools) {
555
571
  this.context.push({
@@ -9,6 +9,20 @@ const RUN_REMINDER_CAPABILITY_TOOLS = new Set([
9
9
  "pibo_run_cancel",
10
10
  "pibo_run_ack",
11
11
  ]);
12
+ // Run reminders are autonomous service wakeups, so their provider/tool loop needs a deterministic boundary.
13
+ const RUN_REMINDER_MAX_TOOL_EXECUTIONS = 64;
14
+ const RUN_REMINDER_MAX_PROVIDER_ROUNDS = 64;
15
+ const RUN_REMINDER_MAX_TOTAL_TOKENS = 2_000_000;
16
+ const RUN_REMINDER_MAX_DURATION_MS = 15 * 60 * 1000;
17
+ const RUN_REMINDER_MAX_REPEATED_TOOL_CALLS = 12;
18
+ function serializedToolArgs(value) {
19
+ try {
20
+ return JSON.stringify(value) ?? String(value);
21
+ }
22
+ catch {
23
+ return String(value);
24
+ }
25
+ }
12
26
  function errorMessage(error) {
13
27
  return error instanceof Error ? error.message : String(error);
14
28
  }
@@ -82,9 +96,11 @@ export class RuntimeRoutedSession {
82
96
  runtimeDisposePromise;
83
97
  forceDisposalStarted = false;
84
98
  drainPromise;
99
+ inFlightMessage;
85
100
  activeMessage;
86
101
  activeExecutionEvent;
87
102
  activeMessageFailed = false;
103
+ runReminderTurnGuard;
88
104
  activeAssistantIndex;
89
105
  nextAssistantIndex = 0;
90
106
  activeThinkingIndex;
@@ -409,6 +425,28 @@ export class RuntimeRoutedSession {
409
425
  this.notifyState();
410
426
  return true;
411
427
  }
428
+ const inFlight = this.inFlightMessage;
429
+ if (inFlight?.event.id === eventId) {
430
+ if (!inFlight.cancellation) {
431
+ inFlight.cancelled = true;
432
+ this.notifyMessagesInterrupted([inFlight.event], "message cancelled");
433
+ const active = this.activeMessage?.id === eventId;
434
+ inFlight.cancellation = (async () => {
435
+ try {
436
+ if (active)
437
+ await this.runtimeSession.abort();
438
+ await inFlight.settled;
439
+ }
440
+ catch (error) {
441
+ inFlight.cancelled = false;
442
+ inFlight.cancellation = undefined;
443
+ throw error;
444
+ }
445
+ })();
446
+ }
447
+ await inFlight.cancellation;
448
+ return true;
449
+ }
412
450
  if (this.activeMessage?.id === eventId) {
413
451
  this.notifyMessagesInterrupted([this.activeMessage], "message cancelled");
414
452
  await this.runtimeSession.abort();
@@ -462,6 +500,7 @@ export class RuntimeRoutedSession {
462
500
  return;
463
501
  case "tool_execution_started":
464
502
  this.emit(this.withActiveMessage({ ...event, type: "tool_execution_started", piboSessionId: this.piboSessionId }));
503
+ this.trackRunReminderTurnGuard("tool_execution_started", { toolName: event.toolName, args: event.args });
465
504
  return;
466
505
  case "tool_execution_updated":
467
506
  this.emit(this.withActiveMessage({ ...event, type: "tool_execution_updated", piboSessionId: this.piboSessionId }));
@@ -479,7 +518,10 @@ export class RuntimeRoutedSession {
479
518
  cacheWriteTokens: event.usage.cacheWriteTokens,
480
519
  reasoningTokens: event.usage.reasoningTokens,
481
520
  totalTokens: event.usage.totalTokens,
521
+ costUsd: event.usage.costUsd,
522
+ provenance: this.activeMessage?.provenance,
482
523
  }));
524
+ this.trackRunReminderTurnGuard("usage", { totalTokens: event.usage.totalTokens });
483
525
  return;
484
526
  case "compaction_start":
485
527
  this.emit(this.withActiveMessage({
@@ -593,9 +635,10 @@ export class RuntimeRoutedSession {
593
635
  }
594
636
  }
595
637
  async processQueuedMessage(event) {
638
+ const inFlight = this.beginInFlightMessage(event);
596
639
  try {
597
640
  const preflight = await this.options.messagePreflight?.(event);
598
- if (this.disposed)
641
+ if (this.disposed || inFlight.cancelled)
599
642
  return;
600
643
  if (preflight && !preflight.allowed) {
601
644
  this.emit({
@@ -614,6 +657,10 @@ export class RuntimeRoutedSession {
614
657
  });
615
658
  return;
616
659
  }
660
+ this.activeMessage = event;
661
+ this.activeMessageFailed = false;
662
+ this.beginRunReminderTurnGuard(event);
663
+ this.resetContentIndices();
617
664
  this.emit({
618
665
  type: "message_started",
619
666
  piboSessionId: this.piboSessionId,
@@ -622,15 +669,14 @@ export class RuntimeRoutedSession {
622
669
  source: event.source,
623
670
  provenance: event.provenance,
624
671
  });
625
- this.activeMessage = event;
626
- this.activeMessageFailed = false;
627
- this.resetContentIndices();
672
+ if (inFlight.cancelled)
673
+ return;
628
674
  await this.runtimeSession.prompt({
629
675
  text: event.text,
630
676
  source: promptSource(event.source),
631
677
  capabilityScope: event.capabilityScope,
632
678
  });
633
- if (this.disposed)
679
+ if (this.disposed || inFlight.cancelled)
634
680
  return;
635
681
  if (!this.activeMessageFailed) {
636
682
  this.emit({
@@ -643,7 +689,7 @@ export class RuntimeRoutedSession {
643
689
  }
644
690
  }
645
691
  catch (error) {
646
- if (this.disposed)
692
+ if (this.disposed || inFlight.cancelled)
647
693
  return;
648
694
  if (!this.activeMessageFailed) {
649
695
  const message = errorMessage(error);
@@ -661,8 +707,103 @@ export class RuntimeRoutedSession {
661
707
  this.activeMessage = undefined;
662
708
  this.activeMessageFailed = false;
663
709
  this.resetContentIndices();
710
+ this.clearRunReminderTurnGuard();
711
+ if (this.inFlightMessage === inFlight)
712
+ this.inFlightMessage = undefined;
713
+ inFlight.resolveSettled();
714
+ }
715
+ }
716
+ beginInFlightMessage(event) {
717
+ let resolveSettled;
718
+ const inFlight = {
719
+ event,
720
+ cancelled: false,
721
+ settled: new Promise((resolve) => { resolveSettled = resolve; }),
722
+ resolveSettled: () => { resolveSettled?.(); },
723
+ };
724
+ this.inFlightMessage = inFlight;
725
+ return inFlight;
726
+ }
727
+ beginRunReminderTurnGuard(event) {
728
+ this.clearRunReminderTurnGuard();
729
+ if (event.source !== "service" || !event.text.startsWith("<pibo_run_notification>"))
730
+ return;
731
+ const guard = {
732
+ eventId: event.id,
733
+ toolExecutions: 0,
734
+ providerRounds: 0,
735
+ totalTokens: 0,
736
+ toolSignatures: new Map(),
737
+ tripped: false,
738
+ };
739
+ guard.timer = setTimeout(() => {
740
+ this.tripRunReminderTurnGuard(guard, `exceeded ${RUN_REMINDER_MAX_DURATION_MS / 60_000} minutes`);
741
+ }, RUN_REMINDER_MAX_DURATION_MS);
742
+ guard.timer.unref?.();
743
+ this.runReminderTurnGuard = guard;
744
+ }
745
+ clearRunReminderTurnGuard() {
746
+ const guard = this.runReminderTurnGuard;
747
+ if (!guard)
748
+ return;
749
+ if (guard.timer)
750
+ clearTimeout(guard.timer);
751
+ this.runReminderTurnGuard = undefined;
752
+ }
753
+ trackRunReminderTurnGuard(type, payload) {
754
+ const guard = this.runReminderTurnGuard;
755
+ if (!guard || guard.tripped || guard.eventId !== this.activeMessage?.id)
756
+ return;
757
+ if (type === "usage") {
758
+ const totalTokens = payload.totalTokens ?? 0;
759
+ guard.totalTokens += totalTokens;
760
+ guard.providerRounds += 1;
761
+ if (guard.totalTokens > RUN_REMINDER_MAX_TOTAL_TOKENS) {
762
+ this.tripRunReminderTurnGuard(guard, `exceeded ${RUN_REMINDER_MAX_TOTAL_TOKENS} total tokens`);
763
+ return;
764
+ }
765
+ if (guard.providerRounds > RUN_REMINDER_MAX_PROVIDER_ROUNDS) {
766
+ this.tripRunReminderTurnGuard(guard, `exceeded ${RUN_REMINDER_MAX_PROVIDER_ROUNDS} provider rounds`);
767
+ }
768
+ return;
769
+ }
770
+ const toolName = payload.toolName;
771
+ const args = payload.args;
772
+ guard.toolExecutions += 1;
773
+ if (guard.toolExecutions > RUN_REMINDER_MAX_TOOL_EXECUTIONS) {
774
+ this.tripRunReminderTurnGuard(guard, `exceeded ${RUN_REMINDER_MAX_TOOL_EXECUTIONS} tool executions`);
775
+ return;
776
+ }
777
+ const signature = `${String(toolName ?? "unknown")}:${serializedToolArgs(args)}`;
778
+ const repeated = (guard.toolSignatures.get(signature) ?? 0) + 1;
779
+ guard.toolSignatures.set(signature, repeated);
780
+ if (repeated > RUN_REMINDER_MAX_REPEATED_TOOL_CALLS) {
781
+ this.tripRunReminderTurnGuard(guard, `repeated the same tool call more than ${RUN_REMINDER_MAX_REPEATED_TOOL_CALLS} times`);
664
782
  }
665
783
  }
784
+ tripRunReminderTurnGuard(guard, reason) {
785
+ if (guard.tripped || this.runReminderTurnGuard !== guard)
786
+ return;
787
+ guard.tripped = true;
788
+ this.activeMessageFailed = true;
789
+ const error = `Run-reminder turn stopped because it ${reason}.`;
790
+ this.emit({
791
+ type: "session_error",
792
+ piboSessionId: this.piboSessionId,
793
+ eventId: guard.eventId,
794
+ error,
795
+ errorDetails: {
796
+ category: "runtime_abort",
797
+ errorClass: "runtime_abort",
798
+ code: "run_reminder_limit_exceeded",
799
+ origin: "runtime",
800
+ retryable: false,
801
+ userMessage: error,
802
+ },
803
+ provenance: this.activeMessage?.provenance,
804
+ });
805
+ void Promise.resolve(this.runtimeSession.abort()).catch(() => undefined);
806
+ }
666
807
  async processQueuedCompact(event) {
667
808
  this.activeExecutionEvent = event;
668
809
  try {
@@ -851,7 +992,10 @@ export class RuntimeRoutedSession {
851
992
  }
852
993
  activeAndQueuedMessages() {
853
994
  const messages = this.queue.flatMap((item) => item.kind === "message" ? [item.event] : []);
854
- return this.activeMessage ? [this.activeMessage, ...messages] : messages;
995
+ const inFlight = this.inFlightMessage?.event;
996
+ if (this.activeMessage)
997
+ return [this.activeMessage, ...messages];
998
+ return inFlight ? [inFlight, ...messages] : messages;
855
999
  }
856
1000
  notifyMessagesInterrupted(messages, reason) {
857
1001
  if (messages.length === 0)
@@ -872,53 +1016,59 @@ export class RuntimeRoutedSession {
872
1016
  this.nextThinkingIndex = 0;
873
1017
  }
874
1018
  withActiveMessage(event) {
875
- if (this.activeMessage?.id && event.type === "assistant_delta") {
1019
+ const activeMessage = this.activeMessage;
1020
+ if (!activeMessage?.id)
1021
+ return event;
1022
+ const correlation = {
1023
+ eventId: activeMessage.id,
1024
+ ...(activeMessage.provenance ? { provenance: activeMessage.provenance } : {}),
1025
+ };
1026
+ if (event.type === "assistant_delta") {
876
1027
  const assistantIndex = this.activeAssistantIndex ?? this.nextAssistantIndex;
877
1028
  if (this.activeAssistantIndex === undefined) {
878
1029
  this.nextAssistantIndex += 1;
879
1030
  this.activeAssistantIndex = assistantIndex;
880
1031
  }
881
- return { ...event, eventId: this.activeMessage.id, assistantIndex };
1032
+ return { ...event, ...correlation, assistantIndex };
882
1033
  }
883
- if (this.activeMessage?.id && event.type === "assistant_message") {
1034
+ if (event.type === "assistant_message") {
884
1035
  const assistantIndex = this.activeAssistantIndex ?? this.nextAssistantIndex;
885
1036
  if (this.activeAssistantIndex === undefined)
886
1037
  this.nextAssistantIndex += 1;
887
1038
  this.activeAssistantIndex = undefined;
888
- return { ...event, eventId: this.activeMessage.id, assistantIndex };
1039
+ return { ...event, ...correlation, assistantIndex };
889
1040
  }
890
- if (this.activeMessage?.id && event.type === "thinking_started") {
1041
+ if (event.type === "thinking_started") {
891
1042
  const thinkingIndex = this.nextThinkingIndex;
892
1043
  this.nextThinkingIndex += 1;
893
1044
  this.activeThinkingIndex = thinkingIndex;
894
- return { ...event, eventId: this.activeMessage.id, thinkingIndex };
1045
+ return { ...event, ...correlation, thinkingIndex };
895
1046
  }
896
- if (this.activeMessage?.id && (event.type === "thinking_delta" || event.type === "thinking_finished")) {
1047
+ if (event.type === "thinking_delta" || event.type === "thinking_finished") {
897
1048
  const thinkingIndex = this.activeThinkingIndex ?? this.nextThinkingIndex;
898
1049
  if (this.activeThinkingIndex === undefined) {
899
1050
  this.nextThinkingIndex += 1;
900
1051
  this.activeThinkingIndex = thinkingIndex;
901
1052
  }
902
- const output = { ...event, eventId: this.activeMessage.id, thinkingIndex };
1053
+ const output = { ...event, ...correlation, thinkingIndex };
903
1054
  if (event.type === "thinking_finished")
904
1055
  this.activeThinkingIndex = undefined;
905
1056
  return output;
906
1057
  }
907
- if (this.activeMessage?.id
908
- && (event.type === "assistant_usage"
909
- || event.type === "compaction_start"
910
- || event.type === "compaction_end"
911
- || event.type === "tool_call"
912
- || event.type === "tool_execution_started"
913
- || event.type === "tool_execution_updated"
914
- || event.type === "tool_execution_finished"
915
- || event.type === "approval_requested"
916
- || event.type === "approval_resolved"
917
- || event.type === "user_input_requested"
918
- || event.type === "user_input_resolved"
919
- || event.type === "session_error"
920
- || event.type === "execution_result")) {
921
- return { ...event, eventId: this.activeMessage.id };
1058
+ if (event.type === "assistant_usage"
1059
+ || event.type === "compaction_start"
1060
+ || event.type === "compaction_end"
1061
+ || event.type === "tool_call"
1062
+ || event.type === "tool_execution_started"
1063
+ || event.type === "tool_execution_updated"
1064
+ || event.type === "tool_execution_finished"
1065
+ || event.type === "approval_requested"
1066
+ || event.type === "approval_resolved"
1067
+ || event.type === "user_input_requested"
1068
+ || event.type === "user_input_resolved"
1069
+ || event.type === "session_error"
1070
+ || event.type === "execution_result") {
1071
+ return { ...event, ...correlation };
922
1072
  }
923
1073
  return event;
924
1074
  }
@@ -12,6 +12,7 @@ export class FakeAgentRuntimeSession {
12
12
  disposed = false;
13
13
  aborted = false;
14
14
  promptIndex = 0;
15
+ activeScript;
15
16
  abortWaiters = [];
16
17
  prompts = [];
17
18
  disposeCalls = 0;
@@ -47,6 +48,7 @@ export class FakeAgentRuntimeSession {
47
48
  this.aborted = false;
48
49
  const turnId = `fake-turn-${this.promptIndex}`;
49
50
  const script = typeof this.script === "function" ? this.script(input, this.promptIndex) : this.script ?? {};
51
+ this.activeScript = script;
50
52
  this.emit({ type: "turn_started", turnId });
51
53
  try {
52
54
  if (script.waitForAbort) {
@@ -72,6 +74,7 @@ export class FakeAgentRuntimeSession {
72
74
  this.emit({ type: "turn_completed", turnId, status: "completed" });
73
75
  }
74
76
  finally {
77
+ this.activeScript = undefined;
75
78
  this.streaming = false;
76
79
  }
77
80
  }
@@ -84,6 +87,10 @@ export class FakeAgentRuntimeSession {
84
87
  }
85
88
  async abort() {
86
89
  this.abortCalls += 1;
90
+ if (this.activeScript?.abortFailWith)
91
+ throw new Error(this.activeScript.abortFailWith);
92
+ if (this.activeScript?.abortNeverSettles)
93
+ return;
87
94
  const wasStreaming = this.streaming;
88
95
  this.aborted = true;
89
96
  const waiters = this.abortWaiters;
@@ -210,7 +210,9 @@ export function semanticEventFromPibo(event) {
210
210
  outputTokens: event.outputTokens,
211
211
  cacheReadTokens: event.cacheReadTokens,
212
212
  cacheWriteTokens: event.cacheWriteTokens,
213
+ reasoningTokens: event.reasoningTokens,
213
214
  totalTokens: event.totalTokens,
215
+ costUsd: event.costUsd,
214
216
  },
215
217
  };
216
218
  case "compaction_start":