@pasko70/pibo 1.12.0 → 1.12.1

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 (43) hide show
  1. package/dist/apps/chat/chat-trace-helpers.js +59 -2
  2. package/dist/apps/chat/data/timeline-query-service.js +1 -1
  3. package/dist/apps/chat/web-app.js +16 -8
  4. package/dist/apps/chat-ui/assets/{dist-DiPvo4rq.js → dist-3bD-92Wo.js} +1 -1
  5. package/dist/apps/chat-ui/assets/{dist-DxJWicji.js → dist-BFjy5Y59.js} +1 -1
  6. package/dist/apps/chat-ui/assets/{dist-D1U9ck8z.js → dist-BKwQlbhS.js} +1 -1
  7. package/dist/apps/chat-ui/assets/{dist-B2Pozq2c.js → dist-Cp68y_h5.js} +1 -1
  8. package/dist/apps/chat-ui/assets/{dist-DYdgxFZX.js → dist-DE0y_EI7.js} +1 -1
  9. package/dist/apps/chat-ui/assets/{dist-Ci4lft6y.js → dist-DPb0yDi4.js} +1 -1
  10. package/dist/apps/chat-ui/assets/{dist-CkLit_da.js → dist-D_dQ5GaA.js} +1 -1
  11. package/dist/apps/chat-ui/assets/{dist-Bgv5Fm3v.js → dist-Daq2KRpq.js} +1 -1
  12. package/dist/apps/chat-ui/assets/{dist-D4qbF8wy.js → dist-aZnuCezL.js} +1 -1
  13. package/dist/apps/chat-ui/assets/{dist-la7ZfDGk.js → dist-q6Vpm5yH.js} +1 -1
  14. package/dist/apps/chat-ui/assets/index-DXPKuGfs.js +237 -0
  15. package/dist/apps/chat-ui/index.html +1 -1
  16. package/dist/apps/chat-vscode-web/assets/index-B3dSrp-L.js +41 -0
  17. package/dist/apps/chat-vscode-web/index.html +1 -1
  18. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  19. package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.12.1.vsix +0 -0
  20. package/dist/core/gateway-resource-guard.js +46 -15
  21. package/dist/core/routed-session.js +40 -1
  22. package/dist/core/runtime.js +2 -0
  23. package/dist/core/session-errors.js +3 -0
  24. package/dist/core/session-router.js +25 -2
  25. package/dist/core/transcript-integrity.js +431 -0
  26. package/dist/data/pibo-store.js +2 -0
  27. package/dist/data/telemetry.js +148 -0
  28. package/dist/debug/index.js +11 -0
  29. package/dist/gateway/server.js +1 -0
  30. package/dist/reliability/store.js +11 -6
  31. package/dist/runs/registry.js +38 -1
  32. package/dist/runs/resource-isolation.js +437 -0
  33. package/dist/runs/tools.js +6 -3
  34. package/dist/sessions/pibo-data-store.js +114 -0
  35. package/dist/shared/trace-engine.js +3 -3
  36. package/dist/shared/trace-event-projection.js +165 -36
  37. package/dist/shared/trace-nodes.js +6 -4
  38. package/dist/shared/trace-page-merge.js +106 -2
  39. package/dist/shared/trace-transcript.js +194 -36
  40. package/package.json +1 -1
  41. package/dist/apps/chat-ui/assets/index-CYxPvrxL.js +0 -237
  42. package/dist/apps/chat-vscode-web/assets/index-CFSHKXsQ.js +0 -41
  43. package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.12.0.vsix +0 -0
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="theme-color" content="#101d22" />
7
7
  <title>Pibo</title>
8
- <script type="module" crossorigin src="/apps/chat-vscode/assets/index-CFSHKXsQ.js"></script>
8
+ <script type="module" crossorigin src="/apps/chat-vscode/assets/index-B3dSrp-L.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/apps/chat-vscode/assets/index-Dupu1DzQ.css">
10
10
  </head>
11
11
  <body>
@@ -2,6 +2,7 @@ import { execFile } from "node:child_process";
2
2
  import { freemem, totalmem } from "node:os";
3
3
  import { promisify } from "node:util";
4
4
  import { getHeapStatistics } from "node:v8";
5
+ import { collectYieldedRunHostResourceSnapshot } from "../runs/resource-isolation.js";
5
6
  const execFileAsync = promisify(execFile);
6
7
  const DEFAULT_POLICY = Object.freeze({
7
8
  mode: "block",
@@ -40,7 +41,8 @@ export function collectGatewayProcessMemory() {
40
41
  export function buildGatewayResourceSnapshot(options = {}) {
41
42
  const policy = resolveGatewayResourceGuardPolicy(options.env);
42
43
  const gateway = collectGatewayProcessMemory();
43
- const host = { freeBytes: freemem(), totalBytes: totalmem() };
44
+ const hostResources = options.hostResourceSnapshot ?? collectYieldedRunHostResourceSnapshot({ now: options.now });
45
+ const host = { freeBytes: hostResources.memoryFreeBytes || freemem(), availableBytes: hostResources.memoryAvailableBytes || freemem(), totalBytes: totalmem() };
44
46
  const processResult = processResultFromOptions(gateway.pid, options, policy);
45
47
  const checks = evaluateGatewayResourceChecks({ gateway, host, policy, knownDaemons: processResult.knownDaemons });
46
48
  const severity = maxSeverity(checks.map((check) => check.severity));
@@ -52,6 +54,7 @@ export function buildGatewayResourceSnapshot(options = {}) {
52
54
  host,
53
55
  checks,
54
56
  processes: processResult,
57
+ yieldedRunUnits: yieldedUnitResultFromOptions(options),
55
58
  severity,
56
59
  guardAction: guardAction(policy, severity),
57
60
  nextCommands: [
@@ -63,16 +66,22 @@ export function buildGatewayResourceSnapshot(options = {}) {
63
66
  };
64
67
  }
65
68
  export async function collectGatewayResourceSnapshot(options = {}) {
66
- if (options.includeProcesses === false || options.processListOutput !== undefined || options.processListError !== undefined) {
69
+ if (options.includeProcesses === false
70
+ || options.processListOutput !== undefined
71
+ || options.processListError !== undefined
72
+ || options.yieldedUnitListOutput !== undefined
73
+ || options.yieldedUnitListError !== undefined) {
67
74
  return buildGatewayResourceSnapshot(options);
68
75
  }
69
- try {
70
- const { stdout } = await execFileAsync("ps", ["-eo", "pid=,ppid=,rss=,comm=,args="], { maxBuffer: 10 * 1024 * 1024 });
71
- return buildGatewayResourceSnapshot({ ...options, processListOutput: stdout });
72
- }
73
- catch (error) {
74
- return buildGatewayResourceSnapshot({ ...options, processListError: error instanceof Error ? error.message : String(error) });
75
- }
76
+ const [processes, yieldedUnits] = await Promise.allSettled([
77
+ execFileAsync("ps", ["-eo", "pid=,ppid=,rss=,comm=,args="], { maxBuffer: 10 * 1024 * 1024 }),
78
+ execFileAsync("systemctl", ["list-units", "--all", "--plain", "--no-legend", "--no-pager", "pibo-yielded-*.service"], { maxBuffer: 1024 * 1024 }),
79
+ ]);
80
+ return buildGatewayResourceSnapshot({
81
+ ...options,
82
+ ...(processes.status === "fulfilled" ? { processListOutput: processes.value.stdout } : { processListError: errorText(processes.reason) }),
83
+ ...(yieldedUnits.status === "fulfilled" ? { yieldedUnitListOutput: yieldedUnits.value.stdout } : { yieldedUnitListError: errorText(yieldedUnits.reason) }),
84
+ });
76
85
  }
77
86
  export function assertGatewayResourceAvailableForWork(workLabel, env = process.env) {
78
87
  const snapshot = buildGatewayResourceSnapshot({ env, includeProcesses: false });
@@ -94,15 +103,16 @@ export class GatewayWorkAdmissionController {
94
103
  ]);
95
104
  }
96
105
  if (policy.mode === "block" &&
97
- snapshot.host.freeBytes < policy.minFreeMemoryBytes + policy.yieldedRunMemoryReservationBytes) {
106
+ snapshot.host.availableBytes < policy.minFreeMemoryBytes + policy.yieldedRunMemoryReservationBytes) {
98
107
  throwGatewayResourceBlock(workLabel, [
99
- `Host free memory ${snapshot.host.freeBytes} cannot preserve reserve ${policy.minFreeMemoryBytes} after the yielded-run reservation ${policy.yieldedRunMemoryReservationBytes}.`,
108
+ `Host available memory ${snapshot.host.availableBytes} cannot preserve reserve ${policy.minFreeMemoryBytes} after the yielded-run reservation ${policy.yieldedRunMemoryReservationBytes}.`,
100
109
  ]);
101
110
  }
102
111
  const reservation = Symbol(workLabel);
103
112
  this.activeReservations.add(reservation);
104
113
  let released = false;
105
114
  return {
115
+ admission: collectYieldedRunHostResourceSnapshot(),
106
116
  release: () => {
107
117
  if (released)
108
118
  return;
@@ -135,16 +145,27 @@ export function parseHostProcessResourceList(output, gatewayPid, policy = DEFAUL
135
145
  .filter((row) => row.kind === "gateway" || row.kind === "child" || row.kind === "known-daemon")
136
146
  .sort((a, b) => resourceProcessRank(a, policy) - resourceProcessRank(b, policy) || b.rssBytes - a.rssBytes);
137
147
  }
148
+ export function parseYieldedRunSystemdUnits(output) {
149
+ return output.split("\n").flatMap((line) => {
150
+ const match = line.trim().match(/^(pibo-yielded-[^\s]+\.service)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/);
151
+ return match ? [{ unitName: match[1], loadState: match[2], activeState: match[3], subState: match[4], description: match[5] ?? "" }] : [];
152
+ });
153
+ }
138
154
  export function renderGatewayResourceSnapshotText(snapshot) {
139
155
  const lines = [`Gateway resource health: ${snapshot.severity} (guard=${snapshot.policy.mode}, action=${snapshot.guardAction})`];
140
156
  lines.push(`Generated at: ${snapshot.generatedAt}`);
141
157
  lines.push(`Gateway PID: ${snapshot.gateway.pid}`);
142
158
  lines.push(`Gateway memory: rss=${snapshot.gateway.rssBytes} heapUsed=${snapshot.gateway.heapUsedBytes} heapAvailable=${snapshot.gateway.heapAvailableBytes} heapLimit=${snapshot.gateway.heapLimitBytes}`);
143
- lines.push(`Host memory: free=${snapshot.host.freeBytes} total=${snapshot.host.totalBytes}`);
159
+ lines.push(`Host memory: free=${snapshot.host.freeBytes} available=${snapshot.host.availableBytes} total=${snapshot.host.totalBytes}`);
144
160
  lines.push(`Thresholds: minFree=${snapshot.policy.minFreeMemoryBytes} minHeapAvailable=${snapshot.policy.minHeapAvailableBytes} maxRss=${snapshot.policy.maxRssBytes} daemonWarnRss=${snapshot.policy.knownDaemonWarningRssBytes} maxYieldedRuns=${snapshot.policy.maxConcurrentYieldedRuns} yieldedRunReservation=${snapshot.policy.yieldedRunMemoryReservationBytes}`);
145
161
  lines.push(`Related processes: children=${snapshot.processes.children.length} knownDaemons=${snapshot.processes.knownDaemons.length} processList=${snapshot.processes.available ? "available" : "unavailable"}`);
162
+ lines.push(`Yielded-run cgroups: units=${snapshot.yieldedRunUnits.units.length} systemdList=${snapshot.yieldedRunUnits.available ? "available" : "unavailable"}`);
146
163
  if (snapshot.processes.error)
147
164
  lines.push(`Process list error: ${snapshot.processes.error}`);
165
+ if (snapshot.yieldedRunUnits.error)
166
+ lines.push(`Yielded-run unit list error: ${snapshot.yieldedRunUnits.error}`);
167
+ for (const unit of snapshot.yieldedRunUnits.units)
168
+ lines.push(`- ${unit.unitName}: ${unit.activeState}/${unit.subState}`);
148
169
  const visibleProcesses = [...snapshot.processes.children, ...snapshot.processes.knownDaemons].slice(0, 10);
149
170
  if (visibleProcesses.length > 0) {
150
171
  lines.push("PID\tPPID\tRSS_BYTES\tKIND\tLABEL\tCOMMAND");
@@ -166,9 +187,9 @@ function evaluateGatewayResourceChecks(input) {
166
187
  checks.push({ id: "guard-disabled", severity: "ok", message: "Gateway resource guard is disabled." });
167
188
  return checks;
168
189
  }
169
- checks.push(input.host.freeBytes < input.policy.minFreeMemoryBytes
170
- ? { id: "host-memory-reserve", severity: "critical", message: `Host free memory ${input.host.freeBytes} is below reserve ${input.policy.minFreeMemoryBytes}.` }
171
- : { id: "host-memory-reserve", severity: "ok", message: `Host free memory ${input.host.freeBytes} satisfies reserve ${input.policy.minFreeMemoryBytes}.` });
190
+ checks.push(input.host.availableBytes < input.policy.minFreeMemoryBytes
191
+ ? { id: "host-memory-reserve", severity: "critical", message: `Host available memory ${input.host.availableBytes} is below reserve ${input.policy.minFreeMemoryBytes}.` }
192
+ : { id: "host-memory-reserve", severity: "ok", message: `Host available memory ${input.host.availableBytes} satisfies reserve ${input.policy.minFreeMemoryBytes}.` });
172
193
  checks.push(input.gateway.heapAvailableBytes < input.policy.minHeapAvailableBytes
173
194
  ? { id: "gateway-heap-reserve", severity: "critical", message: `Gateway heap availability ${input.gateway.heapAvailableBytes} is below reserve ${input.policy.minHeapAvailableBytes}.` }
174
195
  : { id: "gateway-heap-reserve", severity: "ok", message: `Gateway heap availability ${input.gateway.heapAvailableBytes} satisfies reserve ${input.policy.minHeapAvailableBytes}.` });
@@ -193,6 +214,16 @@ function processResultFromOptions(gatewayPid, options, policy) {
193
214
  knownDaemons: rows.filter((row) => row.kind === "known-daemon"),
194
215
  };
195
216
  }
217
+ function yieldedUnitResultFromOptions(options) {
218
+ if (options.yieldedUnitListError)
219
+ return { available: false, error: options.yieldedUnitListError, units: [] };
220
+ if (options.yieldedUnitListOutput === undefined)
221
+ return { available: false, units: [] };
222
+ return { available: true, units: parseYieldedRunSystemdUnits(options.yieldedUnitListOutput) };
223
+ }
224
+ function errorText(error) {
225
+ return error instanceof Error ? error.message : String(error);
226
+ }
196
227
  function parseMode(value, fallback) {
197
228
  const normalized = value?.trim().toLowerCase();
198
229
  if (normalized === undefined || normalized === "")
@@ -5,6 +5,7 @@ import { normalizeSessionErrorDetails, runtimeSessionErrorDetails } from "./sess
5
5
  import { expandInlineSkills } from "./skill-expansion.js";
6
6
  import { PIBO_CONTEXT_GUARD_RESUME_MESSAGE_TYPE, PIBO_CONTEXT_GUARD_RESUME_PROMPT, cancelPiboAssistantContextGuardRecovery, claimPiboAssistantContextGuardRecovery, waitForPiboAssistantContextGuardRecovery, } from "./context-guard.js";
7
7
  import { PIBO_PROVIDER_RECOVERY_MESSAGE_TYPE, PIBO_PROVIDER_RECOVERY_PROMPT, PiboProviderRecoveryCancelledError, isRetryablePiboAssistantError, isRetryablePiboProviderError, resolvePiboProviderRecoverySettings, waitForPiboProviderRecovery, } from "./provider-recovery.js";
8
+ import { PIBO_TRANSCRIPT_INTEGRITY_RESUME_MESSAGE_TYPE, PIBO_TRANSCRIPT_INTEGRITY_RESUME_PROMPT, PiboTranscriptIntegrityError, claimPiboTranscriptIntegrityContinuation, settlePiboTranscriptIntegrityContinuation, } from "./transcript-integrity.js";
8
9
  const FAST_SERVICE_TIER = "priority";
9
10
  const RUN_REMINDER_CAPABILITY_TOOLS = new Set([
10
11
  "pibo_run_status",
@@ -583,6 +584,12 @@ export class RoutedSession {
583
584
  this.pendingAssistantError = undefined;
584
585
  this.pendingAssistantErrorRetryable = false;
585
586
  }
587
+ takePendingAssistantError() {
588
+ const pending = this.pendingAssistantError;
589
+ this.pendingAssistantError = undefined;
590
+ this.pendingAssistantErrorRetryable = false;
591
+ return pending;
592
+ }
586
593
  cancelProviderRecovery() {
587
594
  this.providerRecoveryCancelled = true;
588
595
  this.providerRecoveryAbortController?.abort();
@@ -593,6 +600,34 @@ export class RoutedSession {
593
600
  if (waitForIdle)
594
601
  await waitForIdle.call(session);
595
602
  }
603
+ async resumeTranscriptIntegrityRecovery(session) {
604
+ const reports = claimPiboTranscriptIntegrityContinuation(session);
605
+ if (reports.length === 0)
606
+ return;
607
+ try {
608
+ this.pendingAssistantError = undefined;
609
+ this.pendingAssistantErrorRetryable = false;
610
+ await session.sendCustomMessage({
611
+ customType: PIBO_TRANSCRIPT_INTEGRITY_RESUME_MESSAGE_TYPE,
612
+ content: [{ type: "text", text: PIBO_TRANSCRIPT_INTEGRITY_RESUME_PROMPT }],
613
+ display: false,
614
+ details: { repairIds: reports.map((report) => report.repairId) },
615
+ }, { triggerTurn: true });
616
+ await this.waitForPiAgentSettlement(session);
617
+ await this.resumeContextGuardRecovery(session);
618
+ const pendingError = this.takePendingAssistantError();
619
+ if (pendingError) {
620
+ throw new PiboTranscriptIntegrityError(`Transcript integrity continuation failed: ${pendingError.error}`);
621
+ }
622
+ settlePiboTranscriptIntegrityContinuation(session, "completed");
623
+ }
624
+ catch (error) {
625
+ settlePiboTranscriptIntegrityContinuation(session, "failed", error);
626
+ throw error instanceof PiboTranscriptIntegrityError
627
+ ? error
628
+ : new PiboTranscriptIntegrityError(`Transcript integrity continuation failed: ${errorMessage(error)}`);
629
+ }
630
+ }
596
631
  async resumeContextGuardRecovery(session) {
597
632
  while (await waitForPiboAssistantContextGuardRecovery(session)) {
598
633
  try {
@@ -1092,6 +1127,10 @@ export class RoutedSession {
1092
1127
  });
1093
1128
  return;
1094
1129
  }
1130
+ const session = this.runtime.session;
1131
+ await this.resumeTranscriptIntegrityRecovery(session);
1132
+ if (this.disposed)
1133
+ return;
1095
1134
  this.emit({
1096
1135
  type: "message_started",
1097
1136
  piboSessionId: this.piboSessionId,
@@ -1109,7 +1148,6 @@ export class RoutedSession {
1109
1148
  this.nextAssistantIndex = 0;
1110
1149
  this.activeThinkingIndex = undefined;
1111
1150
  this.nextThinkingIndex = 0;
1112
- const session = this.runtime.session;
1113
1151
  this.applyMessageCapabilityScope(event, session);
1114
1152
  const expandedText = expandInlineSkills(event.text, session.resourceLoader.getSkills().skills);
1115
1153
  await session.prompt(expandedText, { source: promptSource(event.source) });
@@ -1172,6 +1210,7 @@ export class RoutedSession {
1172
1210
  async processQueuedCompact(event) {
1173
1211
  this.activeExecutionEvent = event;
1174
1212
  try {
1213
+ await this.resumeTranscriptIntegrityRecovery(this.runtime.session);
1175
1214
  const result = await this.runAction(event);
1176
1215
  if (this.disposed)
1177
1216
  return;
@@ -26,6 +26,7 @@ import { PIBO_APP_CONTEXT } from "../app-context.js";
26
26
  import { createRuntimeToolDefinition } from "../tools/runtime/tool.js";
27
27
  import { RuntimeSessionRegistry } from "../tools/runtime/registry.js";
28
28
  import { compactValidationToolResultForContext } from "./test-output-compaction.js";
29
+ import { installPiboTranscriptIntegrity } from "./transcript-integrity.js";
29
30
  function hasOwnRetrySetting(settings, key) {
30
31
  return settings !== undefined && settings !== null && Object.prototype.hasOwnProperty.call(settings, key);
31
32
  }
@@ -301,6 +302,7 @@ export async function createPiboRuntime(options = {}) {
301
302
  noTools: profile.builtinTools === "disabled" ? "builtin" : undefined,
302
303
  tools: getBuiltinToolAllowlist(profile, customTools),
303
304
  });
305
+ installPiboTranscriptIntegrity(created.session);
304
306
  installValidationOutputCompaction(created.session.agent);
305
307
  registerPiboAssistantContextGuardRecovery(created.session, contextGuardRecovery);
306
308
  if (options.contextGuardTuiQueueOrdering === true) {
@@ -18,6 +18,9 @@ const PROVIDER_NETWORK_ERROR_MARKERS = [
18
18
  ];
19
19
  export function classifySessionErrorMessage(message, options = {}) {
20
20
  const normalized = message.toLowerCase();
21
+ if (normalized.includes("transcript integrity") || normalized.includes("no tool call found for function call output")) {
22
+ return { category: "transcript_integrity", errorClass: "transcript_integrity", code: "invalid_tool_transcript", origin: "runtime", retryable: false, userMessage: "The persisted tool transcript was invalid and could not be repaired automatically." };
23
+ }
21
24
  if (normalized.includes("context_length_exceeded") || normalized.includes("context window")) {
22
25
  return { category: "context_overflow", errorClass: "provider_context", code: "context_length_exceeded", origin: "provider", retryable: false, userMessage: "The model context window was exceeded." };
23
26
  }
@@ -7,6 +7,7 @@ import { runtimeSessionErrorDetails } from "./session-errors.js";
7
7
  import { createSubagentToolName } from "../subagents/tool.js";
8
8
  import { PiboRunRegistry } from "../runs/registry.js";
9
9
  import { PiboRunExecutionTimeoutError } from "../runs/lifecycle.js";
10
+ import { PiboRunResourceLimitError } from "../runs/resource-isolation.js";
10
11
  import { createPiboSignalRegistry } from "../signals/registry.js";
11
12
  import { createDefaultPiboReliabilityStore } from "../reliability/store.js";
12
13
  import { InMemoryPiboSessionStore, } from "../sessions/store.js";
@@ -86,6 +87,8 @@ function formatRunReminderMessage(notification) {
86
87
  status: run.status,
87
88
  toolName: run.toolName,
88
89
  summary: run.summary,
90
+ resourceLimitReason: run.resources?.limitReason,
91
+ resourceUnit: run.resources?.unitName,
89
92
  })),
90
93
  timedOut: notification.timedOut.map((run) => ({
91
94
  runId: run.runId,
@@ -202,6 +205,17 @@ export class PiboSessionRouter {
202
205
  this.runtimeRegistry = new RuntimeSessionRegistry({ cwd: options.cwd ?? getDefaultPiboWorkspace() });
203
206
  this.runRegistry = new PiboRunRegistry({ store: this.reliabilityStore });
204
207
  this.runRegistry.subscribe((event) => this.projectRunRegistryEvent(event));
208
+ const recoveredRuntimeState = options.recoverInterruptedRuntimeState
209
+ ? this.sessionStore.recoverInterruptedRuntimeState?.({
210
+ recoveredRuns: this.runRegistry.listRecoveredRuns(),
211
+ }) ?? []
212
+ : [];
213
+ for (const recovery of recoveredRuntimeState) {
214
+ const session = this.sessionStore.get(recovery.event.piboSessionId);
215
+ if (session)
216
+ this.signalRegistry.project({ type: "session_created", session });
217
+ this.signalRegistry.project({ type: "pibo_output", event: recovery.event });
218
+ }
205
219
  for (const run of this.runRegistry.listAll({ includeConsumed: true, includeDetached: true })) {
206
220
  this.signalRegistry.project({ type: "run_changed", run, reason: "recovered" });
207
221
  }
@@ -863,8 +877,10 @@ export class PiboSessionRouter {
863
877
  }
864
878
  createRunToolController(parentPiboSessionId) {
865
879
  return {
866
- startToolRun: ({ toolName, params, completionPolicy, retryable, maxAttempts, timeoutMs, serviceWarning, execute }) => {
880
+ startToolRun: ({ toolName, params, completionPolicy, retryable, maxAttempts, timeoutMs, serviceWarning, resources, execute }) => {
867
881
  const admission = this.gatewayWorkAdmission.reserve(`yielded run ${toolName}`);
882
+ if (resources)
883
+ resources.admission = admission.admission;
868
884
  const reminderGeneration = this.runReminderGeneration(parentPiboSessionId);
869
885
  let run;
870
886
  try {
@@ -877,6 +893,7 @@ export class PiboSessionRouter {
877
893
  maxAttempts,
878
894
  timeoutMs,
879
895
  serviceWarning,
896
+ resources,
880
897
  });
881
898
  }
882
899
  catch (error) {
@@ -886,15 +903,21 @@ export class PiboSessionRouter {
886
903
  void (async () => {
887
904
  try {
888
905
  const result = await execute();
906
+ if (resources)
907
+ this.runRegistry.updateResources(run.runId, resources);
889
908
  const completed = this.runRegistry.complete(run.runId, result);
890
909
  if (completed)
891
910
  this.handleTerminalRunReminder(parentPiboSessionId, completed.runId, reminderGeneration);
892
911
  }
893
912
  catch (error) {
894
913
  const message = error instanceof Error ? error.message : String(error);
914
+ if (resources)
915
+ this.runRegistry.updateResources(run.runId, resources);
895
916
  const terminalRun = error instanceof PiboRunExecutionTimeoutError
896
917
  ? this.runRegistry.timeOut(run.runId, message, error.timeoutPhase)
897
- : this.runRegistry.fail(run.runId, message);
918
+ : error instanceof PiboRunResourceLimitError
919
+ ? this.runRegistry.resourceLimit(run.runId, message, error.resources)
920
+ : this.runRegistry.fail(run.runId, message);
898
921
  if (terminalRun)
899
922
  this.handleTerminalRunReminder(parentPiboSessionId, terminalRun.runId, reminderGeneration);
900
923
  }