@pasko70/pibo 1.11.2 → 1.11.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 (30) hide show
  1. package/dist/apps/chat/loop-api.js +22 -6
  2. package/dist/apps/chat-ui/assets/{dist-BBVpFHAq.js → dist-1w_WVrcu.js} +1 -1
  3. package/dist/apps/chat-ui/assets/{dist-DBnh8gXR.js → dist-BEd6jKzd.js} +1 -1
  4. package/dist/apps/chat-ui/assets/{dist-BXyVMdHv.js → dist-BI1eS8pb.js} +1 -1
  5. package/dist/apps/chat-ui/assets/{dist-CCGKu-Wj.js → dist-BLdgeEs8.js} +1 -1
  6. package/dist/apps/chat-ui/assets/{dist-CuGiEm5l.js → dist-BQmnOdXD.js} +1 -1
  7. package/dist/apps/chat-ui/assets/{dist-DeOnZ-pw.js → dist-BmGSbokp.js} +1 -1
  8. package/dist/apps/chat-ui/assets/{dist-CE0MvPLM.js → dist-Bwx_CaKF.js} +1 -1
  9. package/dist/apps/chat-ui/assets/{dist-BiGfVaXN.js → dist-CRYLB6HZ.js} +1 -1
  10. package/dist/apps/chat-ui/assets/{dist-DnQYnLQS.js → dist-CoUOMSbW.js} +1 -1
  11. package/dist/apps/chat-ui/assets/{dist-VT4x40uL.js → dist-Dehi8o5p.js} +1 -1
  12. package/dist/apps/chat-ui/assets/{dist-xtnVygdr.js → dist-o1kTkdhi.js} +1 -1
  13. package/dist/apps/chat-ui/assets/{index-DNeE4HrG.css → index-CYLZe0Y0.css} +1 -1
  14. package/dist/apps/chat-ui/assets/{index-vcg8JNj9.js → index-DT80TM0S.js} +12 -12
  15. package/dist/apps/chat-ui/index.html +2 -2
  16. package/dist/apps/vscode-artifacts/latest.vsix +0 -0
  17. package/dist/apps/vscode-artifacts/{pibo-vscode-ext-1.11.2.vsix → pibo-vscode-ext-1.11.3.vsix} +0 -0
  18. package/dist/core/routed-session.js +143 -24
  19. package/dist/core/runtime-telemetry.js +90 -0
  20. package/dist/core/runtime.js +1 -0
  21. package/dist/core/session-router.js +262 -50
  22. package/dist/gateway/server.js +2 -0
  23. package/dist/gateway/web.js +1 -0
  24. package/dist/loops/accounting.js +8 -1
  25. package/dist/loops/cli.js +1 -1
  26. package/dist/loops/service.js +167 -27
  27. package/dist/loops/store.js +229 -17
  28. package/dist/loops/tools.js +30 -9
  29. package/dist/runs/registry.js +19 -0
  30. package/package.json +1 -1
@@ -11,10 +11,10 @@
11
11
  <link rel="manifest" href="/apps/chat/manifest.webmanifest" />
12
12
  <link rel="apple-touch-icon" href="/apps/chat/assets/pwa-images/ios/180.png" />
13
13
  <title>Pibo Web Chat</title>
14
- <script type="module" crossorigin src="/apps/chat/assets/index-vcg8JNj9.js"></script>
14
+ <script type="module" crossorigin src="/apps/chat/assets/index-DT80TM0S.js"></script>
15
15
  <link rel="modulepreload" crossorigin href="/apps/chat/assets/rolldown-runtime-S-ySWqyJ.js">
16
16
  <link rel="modulepreload" crossorigin href="/apps/chat/assets/dist-SVPsM5Oi.js">
17
- <link rel="stylesheet" crossorigin href="/apps/chat/assets/index-DNeE4HrG.css">
17
+ <link rel="stylesheet" crossorigin href="/apps/chat/assets/index-CYLZe0Y0.css">
18
18
  </head>
19
19
  <body>
20
20
  <div id="root"></div>
@@ -6,6 +6,13 @@ 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
8
  const FAST_SERVICE_TIER = "priority";
9
+ const RUN_REMINDER_CAPABILITY_TOOLS = new Set([
10
+ "pibo_run_status",
11
+ "pibo_run_wait",
12
+ "pibo_run_read",
13
+ "pibo_run_cancel",
14
+ "pibo_run_ack",
15
+ ]);
9
16
  function modelSupportsFastServiceTier(model) {
10
17
  if (!model)
11
18
  return false;
@@ -420,14 +427,18 @@ export class RoutedSession {
420
427
  onKillChildren;
421
428
  onStateChange;
422
429
  onMessagesInterrupted;
430
+ messagePreflight;
423
431
  queue = [];
424
432
  processing = false;
425
433
  disposed = false;
426
434
  disposePromise;
435
+ runtimeDisposePromise;
436
+ forceDisposalStarted = false;
427
437
  drainPromise;
428
438
  fastMode = false;
429
439
  fastModePatchedAgents = new WeakSet();
430
440
  activeMessage;
441
+ activeExecutionEvent;
431
442
  activeAssistantIndex;
432
443
  nextAssistantIndex = 0;
433
444
  activeThinkingIndex;
@@ -437,10 +448,11 @@ export class RoutedSession {
437
448
  activeMessageFailed = false;
438
449
  providerRecoveryCancelled = false;
439
450
  providerRecoveryAbortController;
451
+ activeCapabilityScope;
440
452
  unsubscribe;
441
453
  recoverySession;
442
454
  isContinuePatched = false;
443
- constructor(piboSessionId, runtime, emit, pluginRegistry, forwardPiEvents, onPiEventTelemetry, initialFastMode, onSessionOperation, onKillChildren, onStateChange, onMessagesInterrupted) {
455
+ constructor(piboSessionId, runtime, emit, pluginRegistry, forwardPiEvents, onPiEventTelemetry, initialFastMode, onSessionOperation, onKillChildren, onStateChange, onMessagesInterrupted, messagePreflight) {
444
456
  this.piboSessionId = piboSessionId;
445
457
  this.runtime = runtime;
446
458
  this.emit = emit;
@@ -451,6 +463,7 @@ export class RoutedSession {
451
463
  this.onKillChildren = onKillChildren;
452
464
  this.onStateChange = onStateChange;
453
465
  this.onMessagesInterrupted = onMessagesInterrupted;
466
+ this.messagePreflight = messagePreflight;
454
467
  this.fastMode = initialFastMode && this.fastModeSupported();
455
468
  this.bindRuntimeSession();
456
469
  this.patchFastModeProviderRequest();
@@ -534,7 +547,7 @@ export class RoutedSession {
534
547
  this.recoverySession = session;
535
548
  claimPiboAssistantContextGuardRecovery(session);
536
549
  this.unsubscribe = session.subscribe((event) => {
537
- this.onPiEventTelemetry?.(this.piboSessionId, event, { status: this.getStatus(), activeEventId: this.activeMessage?.id });
550
+ this.onPiEventTelemetry?.(this.piboSessionId, event, { status: this.getStatus(), activeEventId: this.activeMessage?.id ?? this.activeExecutionEvent?.id });
538
551
  const model = this.runtime.session.model;
539
552
  const normalized = normalizePiEvent(this.piboSessionId, event, { contextWindow: numberValue(model?.contextWindow) });
540
553
  const candidate = event && typeof event === "object" ? event : undefined;
@@ -575,6 +588,11 @@ export class RoutedSession {
575
588
  this.providerRecoveryAbortController?.abort();
576
589
  this.providerRecoveryAbortController = undefined;
577
590
  }
591
+ async waitForPiAgentSettlement(session) {
592
+ const waitForIdle = session.waitForIdle;
593
+ if (waitForIdle)
594
+ await waitForIdle.call(session);
595
+ }
578
596
  async resumeContextGuardRecovery(session) {
579
597
  while (await waitForPiboAssistantContextGuardRecovery(session)) {
580
598
  try {
@@ -583,6 +601,7 @@ export class RoutedSession {
583
601
  content: [{ type: "text", text: PIBO_CONTEXT_GUARD_RESUME_PROMPT }],
584
602
  display: false,
585
603
  }, { triggerTurn: true });
604
+ await this.waitForPiAgentSettlement(session);
586
605
  }
587
606
  catch (error) {
588
607
  const resumeError = error instanceof Error ? error : new Error(String(error));
@@ -647,12 +666,14 @@ export class RoutedSession {
647
666
  if (!event || typeof event !== "object")
648
667
  return;
649
668
  const candidate = event;
669
+ const eventId = this.activeMessage?.id ?? this.activeExecutionEvent?.id;
650
670
  if (candidate.type === "compaction_start") {
651
- this.emit({
671
+ this.emit(this.withActiveMessage({
652
672
  type: "compaction_start",
653
673
  piboSessionId: this.piboSessionId,
674
+ eventId,
654
675
  reason: typeof candidate.reason === "string" ? candidate.reason : "unknown",
655
- });
676
+ }));
656
677
  }
657
678
  if (candidate.type === "compaction_end") {
658
679
  if (candidate.result && candidate.aborted !== true) {
@@ -663,14 +684,15 @@ export class RoutedSession {
663
684
  this.activeThinkingIndex = undefined;
664
685
  this.nextThinkingIndex = 0;
665
686
  }
666
- this.emit({
687
+ this.emit(this.withActiveMessage({
667
688
  type: "compaction_end",
668
689
  piboSessionId: this.piboSessionId,
690
+ eventId,
669
691
  reason: typeof candidate.reason === "string" ? candidate.reason : "unknown",
670
692
  result: candidate.result,
671
693
  aborted: candidate.aborted === true,
672
694
  errorMessage: typeof candidate.errorMessage === "string" ? candidate.errorMessage : undefined,
673
- });
695
+ }));
674
696
  }
675
697
  }
676
698
  enqueueMessage(event) {
@@ -683,6 +705,7 @@ export class RoutedSession {
683
705
  queuedMessages: this.queue.length,
684
706
  text: event.text,
685
707
  source: event.source,
708
+ provenance: event.provenance,
686
709
  };
687
710
  this.emit(output);
688
711
  this.onStateChange?.({ processing: this.processing, queuedMessages: this.queue.length, disposed: this.disposed });
@@ -732,6 +755,11 @@ export class RoutedSession {
732
755
  this.emit(output);
733
756
  return output;
734
757
  }
758
+ getActiveMessage() {
759
+ if (!this.activeMessage)
760
+ return undefined;
761
+ return { id: this.activeMessage.id, source: this.activeMessage.source, provenance: this.activeMessage.provenance };
762
+ }
735
763
  getStatus() {
736
764
  const enabledTools = this.runtime.session.getActiveToolNames();
737
765
  const thinkingLevel = this.runtime.session.thinkingLevel;
@@ -739,8 +767,8 @@ export class RoutedSession {
739
767
  return {
740
768
  piboSessionId: this.piboSessionId,
741
769
  queuedMessages: this.queue.length,
742
- processing: this.processing,
743
- streaming: this.runtime.session.isStreaming,
770
+ processing: this.disposed ? false : this.processing,
771
+ streaming: this.disposed ? false : this.runtime.session.isStreaming,
744
772
  activeTools: enabledTools,
745
773
  enabledTools,
746
774
  cwd: this.runtime.cwd,
@@ -784,6 +812,13 @@ export class RoutedSession {
784
812
  this.notifyMessagesInterrupted(removedMessages, "queued message removed");
785
813
  return removedMessages.length;
786
814
  }
815
+ restoreMessageCapabilityScope() {
816
+ const active = this.activeCapabilityScope;
817
+ if (!active)
818
+ return;
819
+ this.activeCapabilityScope = undefined;
820
+ active.session.setActiveToolsByName(active.restoreToolNames);
821
+ }
787
822
  getCurrentSession() {
788
823
  return this.createSessionSnapshot();
789
824
  }
@@ -908,13 +943,29 @@ export class RoutedSession {
908
943
  this.disposePromise = this.disposeUnsafe();
909
944
  return this.disposePromise;
910
945
  }
911
- async disposeUnsafe() {
912
- if (this.disposed)
946
+ forceDispose(reason = "session disposal timed out") {
947
+ this.transitionToDisposed(reason);
948
+ if (this.forceDisposalStarted)
913
949
  return;
950
+ this.forceDisposalStarted = true;
951
+ const abort = this.runtime.session.abort;
952
+ if (abort) {
953
+ try {
954
+ void Promise.resolve(abort.call(this.runtime.session)).catch(() => { });
955
+ }
956
+ catch {
957
+ // Forced runtime disposal must still run when abort throws synchronously.
958
+ }
959
+ }
960
+ void this.disposeRuntime().catch(() => { });
961
+ }
962
+ transitionToDisposed(reason) {
963
+ if (this.disposed)
964
+ return false;
914
965
  const activeMessage = this.activeMessage;
915
- this.notifyMessagesInterrupted(this.activeAndQueuedMessages(), "session disposed");
966
+ this.notifyMessagesInterrupted(this.activeAndQueuedMessages(), reason);
916
967
  if (activeMessage) {
917
- const error = "Session disposed while a message was active.";
968
+ const error = `Session disposed while a message was active: ${reason}`;
918
969
  this.emit({
919
970
  type: "session_error",
920
971
  piboSessionId: this.piboSessionId,
@@ -925,22 +976,38 @@ export class RoutedSession {
925
976
  }
926
977
  this.cancelProviderRecovery();
927
978
  this.queue.length = 0;
928
- this.onStateChange?.({ processing: this.processing, queuedMessages: this.queue.length, disposed: true });
979
+ this.onStateChange?.({ processing: false, queuedMessages: this.queue.length, disposed: true });
929
980
  this.unsubscribe?.();
930
981
  this.unsubscribe = undefined;
931
982
  if (this.recoverySession) {
932
- this.cancelContextGuardRecovery("Context guard recovery cancelled because the routed session was disposed");
983
+ this.cancelContextGuardRecovery(`Context guard recovery cancelled because ${reason}`);
933
984
  this.recoverySession = undefined;
934
985
  }
935
986
  this.disposed = true;
987
+ return true;
988
+ }
989
+ disposeRuntime() {
990
+ if (!this.runtimeDisposePromise) {
991
+ try {
992
+ this.runtimeDisposePromise = Promise.resolve(this.runtime.dispose());
993
+ }
994
+ catch (error) {
995
+ this.runtimeDisposePromise = Promise.reject(error);
996
+ }
997
+ }
998
+ return this.runtimeDisposePromise;
999
+ }
1000
+ async disposeUnsafe() {
1001
+ if (!this.transitionToDisposed("session disposed"))
1002
+ return;
936
1003
  const abort = this.runtime.session.abort;
937
- if (abort)
938
- await Promise.allSettled([abort.call(this.runtime.session)]);
939
1004
  try {
1005
+ if (abort)
1006
+ await Promise.allSettled([Promise.resolve().then(() => abort.call(this.runtime.session))]);
940
1007
  await this.drainPromise;
941
1008
  }
942
1009
  finally {
943
- await this.runtime.dispose();
1010
+ await this.disposeRuntime();
944
1011
  }
945
1012
  }
946
1013
  async kill() {
@@ -1004,14 +1071,35 @@ export class RoutedSession {
1004
1071
  }
1005
1072
  }
1006
1073
  async processQueuedMessage(event) {
1007
- this.emit({
1008
- type: "message_started",
1009
- piboSessionId: this.piboSessionId,
1010
- eventId: event.id,
1011
- text: event.text,
1012
- source: event.source,
1013
- });
1014
1074
  try {
1075
+ const preflight = await this.messagePreflight?.(event);
1076
+ if (this.disposed)
1077
+ return;
1078
+ if (preflight && !preflight.allowed) {
1079
+ this.emit({
1080
+ type: "session_error",
1081
+ piboSessionId: this.piboSessionId,
1082
+ eventId: event.id,
1083
+ error: preflight.reason ?? "Queued message is no longer authorized",
1084
+ errorDetails: {
1085
+ category: "loop_lifecycle",
1086
+ errorClass: "runtime_abort",
1087
+ code: preflight.code ?? "message_preflight_rejected",
1088
+ origin: "runtime",
1089
+ retryable: false,
1090
+ },
1091
+ provenance: event.provenance,
1092
+ });
1093
+ return;
1094
+ }
1095
+ this.emit({
1096
+ type: "message_started",
1097
+ piboSessionId: this.piboSessionId,
1098
+ eventId: event.id,
1099
+ text: event.text,
1100
+ source: event.source,
1101
+ provenance: event.provenance,
1102
+ });
1015
1103
  this.activeMessage = event;
1016
1104
  this.providerRecoveryCancelled = false;
1017
1105
  this.pendingAssistantError = undefined;
@@ -1022,10 +1110,20 @@ export class RoutedSession {
1022
1110
  this.activeThinkingIndex = undefined;
1023
1111
  this.nextThinkingIndex = 0;
1024
1112
  const session = this.runtime.session;
1113
+ this.applyMessageCapabilityScope(event, session);
1025
1114
  const expandedText = expandInlineSkills(event.text, session.resourceLoader.getSkills().skills);
1026
1115
  await session.prompt(expandedText, { source: promptSource(event.source) });
1116
+ if (this.disposed)
1117
+ return;
1118
+ await this.waitForPiAgentSettlement(session);
1119
+ if (this.disposed)
1120
+ return;
1027
1121
  await this.resumeContextGuardRecovery(session);
1122
+ if (this.disposed)
1123
+ return;
1028
1124
  await this.recoverTransientProviderErrors(session);
1125
+ if (this.disposed)
1126
+ return;
1029
1127
  this.flushPendingAssistantError();
1030
1128
  if (!this.activeMessageFailed) {
1031
1129
  this.emit({
@@ -1033,6 +1131,7 @@ export class RoutedSession {
1033
1131
  piboSessionId: this.piboSessionId,
1034
1132
  eventId: event.id,
1035
1133
  source: event.source,
1134
+ provenance: event.provenance,
1036
1135
  });
1037
1136
  }
1038
1137
  }
@@ -1046,9 +1145,11 @@ export class RoutedSession {
1046
1145
  eventId: event.id,
1047
1146
  error: message,
1048
1147
  errorDetails: runtimeSessionErrorDetails(message),
1148
+ provenance: event.provenance,
1049
1149
  });
1050
1150
  }
1051
1151
  finally {
1152
+ this.restoreMessageCapabilityScope();
1052
1153
  this.activeMessage = undefined;
1053
1154
  this.providerRecoveryCancelled = false;
1054
1155
  this.pendingAssistantError = undefined;
@@ -1060,9 +1161,20 @@ export class RoutedSession {
1060
1161
  this.nextThinkingIndex = 0;
1061
1162
  }
1062
1163
  }
1164
+ applyMessageCapabilityScope(event, session) {
1165
+ if (event.capabilityScope !== "run-reminder")
1166
+ return;
1167
+ const restoreToolNames = session.getActiveToolNames();
1168
+ const scopedToolNames = restoreToolNames.filter((name) => RUN_REMINDER_CAPABILITY_TOOLS.has(name));
1169
+ this.activeCapabilityScope = { session, restoreToolNames };
1170
+ session.setActiveToolsByName(scopedToolNames);
1171
+ }
1063
1172
  async processQueuedCompact(event) {
1173
+ this.activeExecutionEvent = event;
1064
1174
  try {
1065
1175
  const result = await this.runAction(event);
1176
+ if (this.disposed)
1177
+ return;
1066
1178
  this.emit({
1067
1179
  type: "execution_result",
1068
1180
  piboSessionId: this.piboSessionId,
@@ -1072,6 +1184,8 @@ export class RoutedSession {
1072
1184
  });
1073
1185
  }
1074
1186
  catch (error) {
1187
+ if (this.disposed)
1188
+ return;
1075
1189
  const message = errorMessage(error);
1076
1190
  this.emit({
1077
1191
  type: "session_error",
@@ -1081,6 +1195,9 @@ export class RoutedSession {
1081
1195
  errorDetails: runtimeSessionErrorDetails(message),
1082
1196
  });
1083
1197
  }
1198
+ finally {
1199
+ this.activeExecutionEvent = undefined;
1200
+ }
1084
1201
  }
1085
1202
  enqueueCompactAction(event) {
1086
1203
  this.queue.push({ kind: "compact", event });
@@ -1242,6 +1359,8 @@ export class RoutedSession {
1242
1359
  }
1243
1360
  if (this.activeMessage?.id &&
1244
1361
  (event.type === "assistant_usage" ||
1362
+ event.type === "compaction_start" ||
1363
+ event.type === "compaction_end" ||
1245
1364
  event.type === "tool_call" ||
1246
1365
  event.type === "tool_execution_started" ||
1247
1366
  event.type === "tool_execution_updated" ||
@@ -94,6 +94,12 @@ export class PiboRuntimeTelemetryRecorder {
94
94
  this.finishOpenPhasesByName(turn?.turnId, "reasoning", "ok", telemetryTimestamp(context));
95
95
  return;
96
96
  }
97
+ case "compaction_start":
98
+ this.recordCompactionStart(event, context);
99
+ return;
100
+ case "compaction_end":
101
+ this.recordCompactionEnd(event, context);
102
+ return;
97
103
  case "tool_call":
98
104
  this.recordToolArgsPhase(event, context);
99
105
  return;
@@ -249,6 +255,89 @@ export class PiboRuntimeTelemetryRecorder {
249
255
  this.recordTurnTerminal(event, context, "aborted", "abort", `${event.action} requested`);
250
256
  return;
251
257
  }
258
+ if (event.action === "compact") {
259
+ this.recordCompactExecutionResult(event, context);
260
+ }
261
+ }
262
+ recordCompactExecutionResult(event, context) {
263
+ if (!event.eventId)
264
+ return;
265
+ const existing = this.store?.getTurn(turnIdForEvent(event.eventId));
266
+ if (existing && !TERMINAL_TURN_STATUSES.has(existing.status)) {
267
+ this.recordTurnTerminal(event, context, "ok", "finish", "manual compaction finished");
268
+ }
269
+ }
270
+ recordCompactionStart(event, context) {
271
+ const turn = this.turnContextForEvent(event.piboSessionId, event.eventId, undefined, context)
272
+ ?? this.activeTurnContext(event.piboSessionId, context);
273
+ if (!turn)
274
+ return;
275
+ let storedTurn = this.store?.getTurn(turn.turnId);
276
+ if (storedTurn && TERMINAL_TURN_STATUSES.has(storedTurn.status))
277
+ return;
278
+ const now = telemetryTimestamp(context);
279
+ if (!storedTurn && event.eventId) {
280
+ turn.source = "system";
281
+ storedTurn = this.telemetry.upsertTurn({
282
+ turnId: turn.turnId,
283
+ piboSessionId: turn.piboSessionId,
284
+ rootSessionId: turn.rootSessionId,
285
+ roomId: turn.roomId,
286
+ inputEventId: turn.eventId,
287
+ eventId: turn.eventId,
288
+ source: turn.source,
289
+ status: "running",
290
+ currentPhase: "compaction",
291
+ queuedAt: now,
292
+ startedAt: now,
293
+ lastProgressAt: now,
294
+ queueDepth: turn.queueDepth,
295
+ summary: `${event.reason} compaction started`,
296
+ metadata: sessionMetadata(context.session),
297
+ });
298
+ }
299
+ for (const phase of this.store?.listOpenPhasesForTurn(turn.turnId) ?? []) {
300
+ if (phase.name === "compaction")
301
+ continue;
302
+ this.telemetry.finishPhase(phase.phaseId, { status: "ok", endedAt: now, lastProgressAt: now });
303
+ }
304
+ this.startOrProgressPhase(turn, "compaction", now, `${event.reason} compaction started`, {
305
+ updateTurn: true,
306
+ counters: { reason: event.reason },
307
+ });
308
+ }
309
+ recordCompactionEnd(event, context) {
310
+ const turn = this.turnContextForEvent(event.piboSessionId, event.eventId, undefined, context)
311
+ ?? this.activeTurnContext(event.piboSessionId, context);
312
+ if (!turn)
313
+ return;
314
+ const now = telemetryTimestamp(context);
315
+ const status = event.aborted ? "aborted" : event.errorMessage ? "error" : "ok";
316
+ const summary = event.errorMessage
317
+ ? `${event.reason} compaction failed: ${event.errorMessage}`
318
+ : event.aborted
319
+ ? `${event.reason} compaction aborted`
320
+ : `${event.reason} compaction finished`;
321
+ this.finishOpenPhasesByName(turn.turnId, "compaction", status, now, safeSummary(summary));
322
+ const storedTurn = this.store?.getTurn(turn.turnId);
323
+ if (storedTurn?.source === "system" && (status === "error" || status === "aborted")) {
324
+ this.recordTurnTerminal(event, context, status === "aborted" ? "aborted" : "error", status === "aborted" ? "abort" : "error", summary, "compaction");
325
+ return;
326
+ }
327
+ if (storedTurn && !TERMINAL_TURN_STATUSES.has(storedTurn.status)) {
328
+ this.telemetry.upsertTurn({
329
+ turnId: turn.turnId,
330
+ piboSessionId: turn.piboSessionId,
331
+ rootSessionId: turn.rootSessionId,
332
+ roomId: turn.roomId,
333
+ eventId: turn.eventId,
334
+ status: "running",
335
+ currentPhase: "compaction",
336
+ lastProgressAt: now,
337
+ queueDepth: turn.queueDepth,
338
+ summary: safeSummary(summary),
339
+ });
340
+ }
252
341
  }
253
342
  recordProviderStreamProgress(event, context, phaseName, summary, force = false) {
254
343
  const turn = event.eventId
@@ -489,6 +578,7 @@ export class PiboRuntimeTelemetryRecorder {
489
578
  providerRequestId: options.providerRequestId,
490
579
  toolCallId: options.toolCallId,
491
580
  eventId: turn.eventId,
581
+ counters: options.counters,
492
582
  summary,
493
583
  });
494
584
  if (options.updateTurn) {
@@ -287,6 +287,7 @@ export async function createPiboRuntime(options = {}) {
287
287
  piboSessionId: options.sessionContext?.piboSessionId ?? profile.sessionId,
288
288
  piboRoomId: options.sessionContext?.piboRoomId,
289
289
  profileName: profile.profileName,
290
+ getActiveMessage: options.sessionContext?.getActiveMessage,
290
291
  },
291
292
  }, options.subagentRunner, options.runToolController, runtimeToolController);
292
293
  const modelDefaults = options.modelDefaults ?? loadPiboModelDefaults(runtimeCwd);