pi-ui-extend 0.1.34 → 0.1.35

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 (58) hide show
  1. package/README.md +20 -0
  2. package/dist/app/app.js +4 -2
  3. package/dist/app/constants.d.ts +2 -1
  4. package/dist/app/constants.js +6 -1
  5. package/dist/app/input/input-controller.d.ts +4 -1
  6. package/dist/app/input/input-controller.js +95 -16
  7. package/dist/app/input/input-paste-handler.js +3 -1
  8. package/dist/app/input/terminal-edit-shortcuts.d.ts +20 -0
  9. package/dist/app/input/terminal-edit-shortcuts.js +50 -16
  10. package/dist/app/rendering/conversation-entry-renderer.d.ts +1 -0
  11. package/dist/app/rendering/conversation-entry-renderer.js +1 -1
  12. package/dist/app/rendering/conversation-tool-renderer.d.ts +1 -0
  13. package/dist/app/rendering/conversation-tool-renderer.js +21 -0
  14. package/dist/app/rendering/conversation-viewport.d.ts +3 -0
  15. package/dist/app/rendering/conversation-viewport.js +41 -5
  16. package/dist/app/rendering/editor-layout-renderer.js +3 -2
  17. package/dist/app/rendering/editor-panels.js +27 -10
  18. package/dist/app/runtime.d.ts +1 -0
  19. package/dist/app/runtime.js +33 -14
  20. package/dist/app/session/session-event-controller.d.ts +7 -0
  21. package/dist/app/session/session-event-controller.js +78 -0
  22. package/dist/app/session/tabs-controller.js +3 -1
  23. package/dist/app/subagents/subagents-widget-controller.d.ts +10 -2
  24. package/dist/app/subagents/subagents-widget-controller.js +141 -70
  25. package/dist/app/terminal/terminal-controller.d.ts +10 -0
  26. package/dist/app/terminal/terminal-controller.js +91 -2
  27. package/dist/app/todo/todo-model.js +2 -0
  28. package/dist/app/todo/todo-widget-controller.d.ts +2 -0
  29. package/dist/app/todo/todo-widget-controller.js +17 -7
  30. package/dist/app/types.d.ts +4 -0
  31. package/dist/bundled-extensions/question/tui.js +8 -1
  32. package/dist/bundled-extensions/session-title/index.js +65 -14
  33. package/dist/input-editor-files.js +23 -4
  34. package/dist/markdown-format.d.ts +4 -1
  35. package/dist/markdown-format.js +76 -9
  36. package/external/pi-tools-suite/README.md +71 -1
  37. package/external/pi-tools-suite/package.json +3 -3
  38. package/external/pi-tools-suite/src/async-subagents/commands.ts +12 -6
  39. package/external/pi-tools-suite/src/async-subagents/index.ts +133 -37
  40. package/external/pi-tools-suite/src/context-usage.ts +6 -1
  41. package/external/pi-tools-suite/src/dcp/commands.ts +3 -2
  42. package/external/pi-tools-suite/src/dcp/compress-tool.ts +9 -4
  43. package/external/pi-tools-suite/src/dcp/config.ts +142 -6
  44. package/external/pi-tools-suite/src/dcp/index.ts +20 -8
  45. package/external/pi-tools-suite/src/dcp/prompts.ts +17 -9
  46. package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +59 -15
  47. package/external/pi-tools-suite/src/dcp/pruner-metadata.ts +6 -8
  48. package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +3 -3
  49. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +51 -1
  50. package/external/pi-tools-suite/src/glm-coding-discipline/index.ts +16 -11
  51. package/external/pi-tools-suite/src/model-tools/index.ts +24 -12
  52. package/external/pi-tools-suite/src/prompt-commands/index.ts +11 -2
  53. package/external/pi-tools-suite/src/telegram-mirror/index.ts +66 -27
  54. package/external/pi-tools-suite/src/todo/index.ts +87 -16
  55. package/external/pi-tools-suite/src/todo/state/store.ts +41 -10
  56. package/external/pi-tools-suite/src/todo/todo.ts +49 -6
  57. package/external/pi-tools-suite/src/tool-descriptions.ts +4 -4
  58. package/package.json +7 -5
@@ -71,30 +71,45 @@ export function renderTodoPanel(details, expanded, width, colors) {
71
71
  export function renderSubagentsPanel(state, expanded, width, colors) {
72
72
  if (!state)
73
73
  return [];
74
- const activeAgents = activeSubagentStates(state.agents);
74
+ const runs = state.runs?.length
75
+ ? state.runs
76
+ : [{ runDir: state.runDir, agents: state.agents, ...(state.tasks === undefined ? {} : { tasks: state.tasks }) }];
77
+ const activeRuns = runs
78
+ .map((run) => ({ ...run, activeAgents: activeSubagentStates(run.agents) }))
79
+ .filter((run) => run.activeAgents.length > 0);
80
+ const activeAgents = activeRuns.flatMap((run) => run.activeAgents);
75
81
  if (activeAgents.length === 0)
76
82
  return [];
77
83
  const target = { kind: "subagents-panel" };
78
- const previewById = taskPreviewMap(state.tasks);
79
- const runName = subagentRunName(state.runDir);
80
84
  const titleSuffix = state.live ? "" : " (snapshot)";
81
85
  const stats = formatSubagentsPanelStats(activeAgents);
82
86
  const headerText = `subagents ${expanded ? "▾" : "▸"}${stats ? ` ${stats}` : ""}${titleSuffix}`;
83
87
  const contentWidth = Math.max(1, width);
84
88
  if (!expanded) {
85
- const collapsedText = `${headerText} — ${runName}`;
89
+ const runSummary = activeRuns.length === 1 ? subagentRunName(activeRuns[0]?.runDir ?? state.runDir) : `${activeRuns.length} runs`;
90
+ const collapsedText = `${headerText} — ${runSummary}`;
86
91
  return [{ text: padOrTrimPlain(ellipsizeDisplay(collapsedText, contentWidth), width), colorOverride: colors.accent, target }];
87
92
  }
88
93
  const lines = [];
89
- const visibleAgents = activeAgents.slice(0, SUBAGENTS_WIDGET_MAX_ROWS);
94
+ const flattenedRuns = activeRuns.flatMap((run) => {
95
+ const previewById = taskPreviewMap(run.tasks);
96
+ return run.activeAgents.map((agent, index) => ({
97
+ runDir: run.runDir,
98
+ agent,
99
+ preview: previewById.get(agent.id),
100
+ showRunLabel: activeRuns.length > 1 && index === 0,
101
+ }));
102
+ });
103
+ const visibleAgents = flattenedRuns.slice(0, SUBAGENTS_WIDGET_MAX_ROWS);
90
104
  const rowWidth = contentWidth;
91
105
  const now = Date.now();
92
- for (const agent of visibleAgents) {
93
- const preview = previewById.get(agent.id);
106
+ for (const visibleRun of visibleAgents) {
107
+ const { agent, preview } = visibleRun;
94
108
  const model = subagentModelThinkingLabel(preview);
95
109
  const task = preview?.task?.trim() || preview?.scope?.trim() || "task unavailable";
96
110
  const icon = subagentStatusIcon(agent.status);
97
- const prefix = `${icon} ${agent.id} ${model} `;
111
+ const runLabel = visibleRun.showRunLabel ? `${subagentRunName(visibleRun.runDir)} ` : "";
112
+ const prefix = `${runLabel}${icon} ${agent.id} ${model} `;
98
113
  const suffix = ` ${formatElapsedSince(agent.startedAt, now)}`;
99
114
  const taskWidth = Math.max(8, rowWidth - stringDisplayWidth(prefix) - stringDisplayWidth(suffix));
100
115
  const taskText = ellipsizeDisplay(task, taskWidth);
@@ -102,22 +117,24 @@ export function renderSubagentsPanel(state, expanded, width, colors) {
102
117
  lines.push({
103
118
  text: padOrTrimPlain(text, width),
104
119
  colorOverride: colors.muted,
105
- segments: subagentPanelLineSegments({ text, icon, agentId: agent.id, model, taskText, prefix, status: agent.status }, colors),
120
+ segments: subagentPanelLineSegments({ text, icon, agentId: agent.id, model, taskText, prefix, runLabel, status: agent.status }, colors),
106
121
  target,
107
122
  });
108
123
  }
109
- const hidden = activeAgents.length - visibleAgents.length;
124
+ const hidden = flattenedRuns.length - visibleAgents.length;
110
125
  if (hidden > 0)
111
126
  lines.push({ text: padOrTrimPlain(`+${hidden} more`, width), variant: "muted", target });
112
127
  return lines;
113
128
  }
114
129
  function subagentPanelLineSegments(input, colors) {
115
130
  const iconStart = input.text.indexOf(input.icon);
131
+ const runLabelStart = input.runLabel ? input.text.indexOf(input.runLabel) : -1;
116
132
  const nameStart = input.text.indexOf(input.agentId, iconStart + input.icon.length);
117
133
  const modelStart = input.text.indexOf(input.model, nameStart + input.agentId.length);
118
134
  const taskStart = input.prefix.length;
119
135
  const suffixStart = taskStart + input.taskText.length;
120
136
  return [
137
+ ...(runLabelStart >= 0 ? [{ start: runLabelStart, end: runLabelStart + input.runLabel.length, foreground: colors.warning }] : []),
121
138
  { start: iconStart, end: iconStart + input.icon.length, foreground: subagentStatusColor(input.status, colors), bold: true },
122
139
  { start: nameStart, end: nameStart + input.agentId.length, foreground: colors.accent, bold: true },
123
140
  { start: modelStart, end: modelStart + input.model.length, foreground: colors.info },
@@ -38,6 +38,7 @@ export declare function prioritizeBundledQuestionExtension(base: LoadExtensionsR
38
38
  export type CreatePixRuntimeOptions = {
39
39
  eventBus?: EventBus;
40
40
  config?: PixConfig;
41
+ reuseServicesFrom?: AgentSessionRuntime;
41
42
  };
42
43
  type RuntimeSessionManagerModelState = Pick<SessionManager, "getEntries" | "getBranch">;
43
44
  export declare function resolvePixRuntimeModelRef(options: Pick<AppOptions, "modelRef">, sessionManager: RuntimeSessionManagerModelState, config?: PixConfig): string | undefined;
@@ -220,26 +220,20 @@ export function resolveSessionModelRefFromTail(entries) {
220
220
  }
221
221
  export async function createPixRuntime(options, runtimeOptions = {}) {
222
222
  const agentDir = getAgentDir();
223
+ const reusableServices = reusableRuntimeServices(runtimeOptions.reuseServicesFrom, options.cwd, agentDir);
223
224
  const createRuntime = async ({ cwd, sessionManager, sessionStartEvent }) => {
224
225
  const config = runtimeOptions.config ?? loadPixConfig(cwd);
225
226
  const effectiveModelRef = resolvePixRuntimeModelRef(options, sessionManager, config);
226
227
  const parsedModel = effectiveModelRef ? parseModelRef(effectiveModelRef) : undefined;
227
228
  const initialThinkingLevel = resolvePixRuntimeInitialThinkingLevel(options, sessionManager, config);
228
- await ensureBundledSkillsInstalledOnce();
229
- await ensurePiToolsSuiteExtensionInstalledOnce({ agentDir });
230
- const bundledExtensionPaths = await getBundledExtensionPathsAsync();
231
- const services = await createAgentSessionServices({
232
- cwd,
233
- agentDir,
234
- resourceLoaderOptions: {
235
- ...(config.ignoreContextFiles ? { noContextFiles: true } : {}),
229
+ const services = reusableServices && sameRuntimeServiceTarget(reusableServices, cwd, agentDir)
230
+ ? reusableServices
231
+ : await createPixRuntimeServices({
232
+ cwd,
233
+ agentDir,
234
+ config,
236
235
  ...(runtimeOptions.eventBus === undefined ? {} : { eventBus: runtimeOptions.eventBus }),
237
- ...(bundledExtensionPaths.length === 0 ? {} : {
238
- additionalExtensionPaths: bundledExtensionPaths,
239
- extensionsOverride: prioritizeBundledQuestionExtension,
240
- }),
241
- },
242
- });
236
+ });
243
237
  services.modelRegistry.refresh();
244
238
  const model = parsedModel ? services.modelRegistry.find(parsedModel.provider, parsedModel.modelId) : undefined;
245
239
  if (parsedModel && !model) {
@@ -285,3 +279,28 @@ export async function createPixRuntime(options, runtimeOptions = {}) {
285
279
  : SessionManager.create(options.cwd),
286
280
  });
287
281
  }
282
+ async function createPixRuntimeServices(options) {
283
+ await ensureBundledSkillsInstalledOnce();
284
+ await ensurePiToolsSuiteExtensionInstalledOnce({ agentDir: options.agentDir });
285
+ const bundledExtensionPaths = await getBundledExtensionPathsAsync();
286
+ return await createAgentSessionServices({
287
+ cwd: options.cwd,
288
+ agentDir: options.agentDir,
289
+ resourceLoaderOptions: {
290
+ ...(options.config.ignoreContextFiles ? { noContextFiles: true } : {}),
291
+ ...(options.eventBus === undefined ? {} : { eventBus: options.eventBus }),
292
+ ...(bundledExtensionPaths.length === 0 ? {} : {
293
+ additionalExtensionPaths: bundledExtensionPaths,
294
+ extensionsOverride: prioritizeBundledQuestionExtension,
295
+ }),
296
+ },
297
+ });
298
+ }
299
+ function reusableRuntimeServices(runtime, cwd, agentDir) {
300
+ const services = runtime?.services;
301
+ return services && sameRuntimeServiceTarget(services, cwd, agentDir) ? services : undefined;
302
+ }
303
+ function sameRuntimeServiceTarget(services, cwd, agentDir) {
304
+ return normalizePathForCompare(services.cwd) === normalizePathForCompare(cwd)
305
+ && normalizePathForCompare(services.agentDir) === normalizePathForCompare(agentDir);
306
+ }
@@ -19,6 +19,7 @@ export type AppSessionEventControllerState = {
19
19
  currentAssistantTextBlockContentIndex: number | undefined;
20
20
  assistantTextBlocksByContentIndex: Map<number, string>;
21
21
  currentThinkingEntryId: string | undefined;
22
+ currentThinkingEntryStartedAt: number | undefined;
22
23
  assistantMessageClosed: boolean;
23
24
  assistantTextBuffer: string;
24
25
  entryRenderVersions: Map<string, number>;
@@ -74,6 +75,8 @@ export declare class AppSessionEventController {
74
75
  private historyEntries;
75
76
  private historyWindowStart;
76
77
  private currentThinkingEntryId;
78
+ private currentThinkingEntryStartedAt;
79
+ private thinkingElapsedRenderTimer;
77
80
  private assistantMessageClosed;
78
81
  private assistantTextBuffer;
79
82
  constructor(host: AppSessionEventControllerHost);
@@ -133,7 +136,11 @@ export declare class AppSessionEventController {
133
136
  private appendThinkingText;
134
137
  private finishCurrentThinkingEntry;
135
138
  private reconcileThinkingText;
139
+ private syncThinkingElapsedRenderTimer;
140
+ private startThinkingElapsedRenderTimer;
141
+ private stopThinkingElapsedRenderTimer;
136
142
  private currentThinkingLevel;
143
+ private reconcileAssistantTextFromFinalMessage;
137
144
  private renderAssistantToolCallsFromMessage;
138
145
  private upsertPendingToolCall;
139
146
  private upsertToolEntry;
@@ -26,6 +26,8 @@ export class AppSessionEventController {
26
26
  historyEntries = [];
27
27
  historyWindowStart = 0;
28
28
  currentThinkingEntryId;
29
+ currentThinkingEntryStartedAt;
30
+ thinkingElapsedRenderTimer;
29
31
  assistantMessageClosed = false;
30
32
  assistantTextBuffer = "";
31
33
  constructor(host) {
@@ -44,6 +46,7 @@ export class AppSessionEventController {
44
46
  currentAssistantTextBlockContentIndex: this.currentAssistantTextBlockContentIndex,
45
47
  assistantTextBlocksByContentIndex: new Map(this.assistantTextBlocksByContentIndex),
46
48
  currentThinkingEntryId: this.currentThinkingEntryId,
49
+ currentThinkingEntryStartedAt: this.currentThinkingEntryStartedAt,
47
50
  assistantMessageClosed: this.assistantMessageClosed,
48
51
  assistantTextBuffer: this.assistantTextBuffer,
49
52
  entryRenderVersions: new Map(this.entryRenderVersions),
@@ -71,6 +74,8 @@ export class AppSessionEventController {
71
74
  for (const [key, value] of state.assistantTextBlocksByContentIndex)
72
75
  this.assistantTextBlocksByContentIndex.set(key, value);
73
76
  this.currentThinkingEntryId = state.currentThinkingEntryId;
77
+ this.currentThinkingEntryStartedAt = state.currentThinkingEntryStartedAt;
78
+ this.syncThinkingElapsedRenderTimer();
74
79
  this.assistantMessageClosed = state.assistantMessageClosed;
75
80
  this.assistantTextBuffer = state.assistantTextBuffer;
76
81
  this.entryRenderVersions.clear();
@@ -94,6 +99,8 @@ export class AppSessionEventController {
94
99
  this.assistantTextBlocksByContentIndex.clear();
95
100
  this.finalizedToolCallContentIndexes.clear();
96
101
  this.currentThinkingEntryId = undefined;
102
+ this.currentThinkingEntryStartedAt = undefined;
103
+ this.stopThinkingElapsedRenderTimer();
97
104
  this.assistantMessageClosed = false;
98
105
  this.assistantTextBuffer = "";
99
106
  this.olderHistoryLoader = undefined;
@@ -563,6 +570,7 @@ export class AppSessionEventController {
563
570
  this.handleToolCallStreamUpdate(assistantEvent.contentIndex, assistantEvent.partial, assistantEvent.toolCall);
564
571
  break;
565
572
  case "done":
573
+ this.reconcileAssistantTextFromFinalMessage(assistantEvent.message);
566
574
  this.renderAssistantToolCallsFromMessage(assistantEvent.message);
567
575
  this.finishCurrentThinkingEntry();
568
576
  this.flushAssistantTextBuffer(true);
@@ -754,12 +762,15 @@ export class AppSessionEventController {
754
762
  : undefined;
755
763
  if (!entry || entry.kind !== "thinking") {
756
764
  const level = this.currentThinkingLevel();
765
+ const startedAt = this.currentThinkingEntryStartedAt ?? Date.now();
766
+ this.currentThinkingEntryStartedAt = startedAt;
757
767
  entry = {
758
768
  id: createId("thinking"),
759
769
  kind: "thinking",
760
770
  text: "",
761
771
  expanded: this.host.toolDefaultExpanded(THINKING_TOOL_NAME),
762
772
  ...(level === undefined ? {} : { level }),
773
+ startedAt,
763
774
  status: "running",
764
775
  };
765
776
  this.addEntry(entry);
@@ -770,17 +781,24 @@ export class AppSessionEventController {
770
781
  delete entry.level;
771
782
  else
772
783
  entry.level = level;
784
+ entry.startedAt ??= this.currentThinkingEntryStartedAt ?? Date.now();
785
+ this.currentThinkingEntryStartedAt = entry.startedAt;
786
+ delete entry.finishedAt;
773
787
  entry.status = "running";
774
788
  entry.text += delta;
789
+ this.startThinkingElapsedRenderTimer();
775
790
  this.touchEntry(entry);
776
791
  }
777
792
  finishCurrentThinkingEntry() {
778
793
  const entry = this.currentThinkingEntryId ? this.findEntry(this.currentThinkingEntryId) : undefined;
779
794
  if (entry?.kind === "thinking" && entry.status !== "done") {
780
795
  entry.status = "done";
796
+ entry.finishedAt ??= Date.now();
781
797
  this.touchEntry(entry);
782
798
  }
783
799
  this.currentThinkingEntryId = undefined;
800
+ this.currentThinkingEntryStartedAt = undefined;
801
+ this.stopThinkingElapsedRenderTimer();
784
802
  }
785
803
  reconcileThinkingText(content) {
786
804
  let entry = this.currentThinkingEntryId
@@ -788,12 +806,15 @@ export class AppSessionEventController {
788
806
  : undefined;
789
807
  if (!entry || entry.kind !== "thinking") {
790
808
  const level = this.currentThinkingLevel();
809
+ const startedAt = this.currentThinkingEntryStartedAt ?? Date.now();
810
+ this.currentThinkingEntryStartedAt = startedAt;
791
811
  entry = {
792
812
  id: createId("thinking"),
793
813
  kind: "thinking",
794
814
  text: "",
795
815
  expanded: this.host.toolDefaultExpanded(THINKING_TOOL_NAME),
796
816
  ...(level === undefined ? {} : { level }),
817
+ startedAt,
797
818
  status: "running",
798
819
  };
799
820
  this.addEntry(entry);
@@ -806,13 +827,63 @@ export class AppSessionEventController {
806
827
  delete entry.level;
807
828
  else
808
829
  entry.level = level;
830
+ entry.startedAt ??= this.currentThinkingEntryStartedAt ?? Date.now();
831
+ this.currentThinkingEntryStartedAt = entry.startedAt;
832
+ delete entry.finishedAt;
809
833
  entry.status = "running";
834
+ this.startThinkingElapsedRenderTimer();
810
835
  this.touchEntry(entry);
811
836
  }
812
837
  }
838
+ syncThinkingElapsedRenderTimer() {
839
+ const entry = this.currentThinkingEntryId ? this.findEntry(this.currentThinkingEntryId) : undefined;
840
+ if (entry?.kind === "thinking" && entry.status === "running" && entry.startedAt !== undefined) {
841
+ this.startThinkingElapsedRenderTimer();
842
+ return;
843
+ }
844
+ this.stopThinkingElapsedRenderTimer();
845
+ }
846
+ startThinkingElapsedRenderTimer() {
847
+ if (this.thinkingElapsedRenderTimer)
848
+ return;
849
+ this.thinkingElapsedRenderTimer = setInterval(() => {
850
+ if (!this.host.isRunning()) {
851
+ this.stopThinkingElapsedRenderTimer();
852
+ return;
853
+ }
854
+ this.host.scheduleRender();
855
+ }, 1000);
856
+ this.thinkingElapsedRenderTimer.unref?.();
857
+ }
858
+ stopThinkingElapsedRenderTimer() {
859
+ if (!this.thinkingElapsedRenderTimer)
860
+ return;
861
+ clearInterval(this.thinkingElapsedRenderTimer);
862
+ this.thinkingElapsedRenderTimer = undefined;
863
+ }
813
864
  currentThinkingLevel() {
814
865
  return this.host.runtime()?.session.thinkingLevel;
815
866
  }
867
+ reconcileAssistantTextFromFinalMessage(message) {
868
+ const openContentIndex = this.currentAssistantTextBlockContentIndex;
869
+ if (openContentIndex !== undefined) {
870
+ const content = assistantTextContentAt(message, openContentIndex);
871
+ if (content !== undefined)
872
+ this.reconcileAssistantTextBlock(content, openContentIndex);
873
+ return;
874
+ }
875
+ const textBlocks = assistantTextContents(message);
876
+ if (textBlocks.length !== 1)
877
+ return;
878
+ const entry = this.currentAssistantEntryId ? this.findEntry(this.currentAssistantEntryId) : undefined;
879
+ if (entry?.kind !== "assistant")
880
+ return;
881
+ const visibleText = assistantStreamVisibleTextForCompleteBlock(textBlocks[0] ?? "", false);
882
+ if (!visibleText || entry.text === visibleText)
883
+ return;
884
+ entry.text = visibleText;
885
+ this.touchEntry(entry);
886
+ }
816
887
  renderAssistantToolCallsFromMessage(message) {
817
888
  if (!isRecord(message) || !Array.isArray(message.content))
818
889
  return;
@@ -893,6 +964,8 @@ export class AppSessionEventController {
893
964
  this.currentAssistantTextBlockStartLength = undefined;
894
965
  this.currentAssistantTextBlockContentIndex = undefined;
895
966
  this.currentThinkingEntryId = undefined;
967
+ this.currentThinkingEntryStartedAt = undefined;
968
+ this.stopThinkingElapsedRenderTimer();
896
969
  this.assistantTextBuffer = "";
897
970
  this.assistantTextBlocksByContentIndex.clear();
898
971
  this.finalizedToolCallContentIndexes.clear();
@@ -916,6 +989,11 @@ function assistantTextContentAt(value, contentIndex) {
916
989
  const block = value.content[contentIndex];
917
990
  return isRecord(block) && block.type === "text" && typeof block.text === "string" ? block.text : undefined;
918
991
  }
992
+ function assistantTextContents(value) {
993
+ if (!isRecord(value) || !Array.isArray(value.content))
994
+ return [];
995
+ return value.content.flatMap((block) => (isRecord(block) && block.type === "text" && typeof block.text === "string" ? [block.text] : []));
996
+ }
919
997
  function assistantStreamVisibleTextForCompleteBlock(text, hasVisibleTextBeforeBlock) {
920
998
  let buffer = text;
921
999
  let visibleText = "";
@@ -641,7 +641,9 @@ export class AppTabsController {
641
641
  void this.saveTabs();
642
642
  this.scheduleTabPrewarm();
643
643
  const cachedView = this.sessionViewsByTabId.get(target.id);
644
- if (cachedView && this.host.restoreSessionView) {
644
+ const cachedViewNeedsHistoryReload = this.tabIdsNeedingHistoryReload.has(target.id)
645
+ && this.sessionActivity(targetRuntime.session) !== "running";
646
+ if (cachedView && this.host.restoreSessionView && !cachedViewNeedsHistoryReload) {
645
647
  this.host.restoreSessionView(cachedView);
646
648
  this.restoreDeferredUserMessages(target.id);
647
649
  this.host.setSessionStatus(targetRuntime.session);
@@ -13,7 +13,9 @@ export declare class AppSubagentsWidgetController {
13
13
  private pollTimer;
14
14
  private pollInFlight;
15
15
  private currentRunDir;
16
+ private currentRunDirs;
16
17
  private state;
18
+ private readonly runFreshnessByRunDir;
17
19
  private readonly taskPreviewsByRunDir;
18
20
  private readonly snapshotByRunDir;
19
21
  private refreshGeneration;
@@ -32,10 +34,16 @@ export declare class AppSubagentsWidgetController {
32
34
  private schedulePoll;
33
35
  private poll;
34
36
  private refreshFromFiles;
35
- private findActiveRegistryRunDirForCurrentSession;
37
+ private findActiveRegistryRunDirsForCurrentSession;
36
38
  private registryRunDirsNewestFirst;
37
- private clearMissingRunAndMaybeSelectReplacement;
38
39
  private clearCachedRun;
40
+ private buildStateFromRuns;
41
+ private orderRuns;
42
+ private orderRunDirs;
43
+ private rememberRunFreshness;
44
+ private runFreshness;
45
+ private runFreshnessFromAgents;
46
+ private parseRunTimestamp;
39
47
  private updateState;
40
48
  private isCurrentGeneration;
41
49
  private shouldContinuePolling;