@codex-infinity/pi-infinity 0.63.3 → 0.64.2

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 (112) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +7 -3
  3. package/dist/core/agent-session-runtime.d.ts +134 -0
  4. package/dist/core/agent-session-runtime.d.ts.map +1 -0
  5. package/dist/core/agent-session-runtime.js +262 -0
  6. package/dist/core/agent-session-runtime.js.map +1 -0
  7. package/dist/core/agent-session.d.ts +10 -42
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +56 -236
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/export-html/tool-renderer.d.ts +2 -0
  12. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  13. package/dist/core/export-html/tool-renderer.js +2 -2
  14. package/dist/core/export-html/tool-renderer.js.map +1 -1
  15. package/dist/core/extensions/index.d.ts +1 -1
  16. package/dist/core/extensions/index.d.ts.map +1 -1
  17. package/dist/core/extensions/index.js.map +1 -1
  18. package/dist/core/extensions/runner.d.ts.map +1 -1
  19. package/dist/core/extensions/runner.js +1 -0
  20. package/dist/core/extensions/runner.js.map +1 -1
  21. package/dist/core/extensions/types.d.ts +11 -16
  22. package/dist/core/extensions/types.d.ts.map +1 -1
  23. package/dist/core/extensions/types.js.map +1 -1
  24. package/dist/core/footer-data-provider.d.ts +5 -1
  25. package/dist/core/footer-data-provider.d.ts.map +1 -1
  26. package/dist/core/footer-data-provider.js +69 -8
  27. package/dist/core/footer-data-provider.js.map +1 -1
  28. package/dist/core/index.d.ts +2 -1
  29. package/dist/core/index.d.ts.map +1 -1
  30. package/dist/core/index.js +1 -0
  31. package/dist/core/index.js.map +1 -1
  32. package/dist/core/keybindings.d.ts +10 -0
  33. package/dist/core/keybindings.d.ts.map +1 -1
  34. package/dist/core/keybindings.js +10 -0
  35. package/dist/core/keybindings.js.map +1 -1
  36. package/dist/core/model-registry.d.ts +3 -1
  37. package/dist/core/model-registry.d.ts.map +1 -1
  38. package/dist/core/model-registry.js +7 -1
  39. package/dist/core/model-registry.js.map +1 -1
  40. package/dist/core/sdk.d.ts +5 -2
  41. package/dist/core/sdk.d.ts.map +1 -1
  42. package/dist/core/sdk.js +5 -2
  43. package/dist/core/sdk.js.map +1 -1
  44. package/dist/core/session-manager.d.ts +3 -0
  45. package/dist/core/session-manager.d.ts.map +1 -1
  46. package/dist/core/session-manager.js +13 -6
  47. package/dist/core/session-manager.js.map +1 -1
  48. package/dist/core/tools/edit.d.ts.map +1 -1
  49. package/dist/core/tools/edit.js +15 -4
  50. package/dist/core/tools/edit.js.map +1 -1
  51. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  52. package/dist/core/tools/tool-definition-wrapper.js +2 -0
  53. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  54. package/dist/index.d.ts +2 -2
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +2 -2
  57. package/dist/index.js.map +1 -1
  58. package/dist/main.d.ts.map +1 -1
  59. package/dist/main.js +42 -10
  60. package/dist/main.js.map +1 -1
  61. package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  62. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  63. package/dist/modes/interactive/components/assistant-message.js +13 -3
  64. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  65. package/dist/modes/interactive/components/footer.d.ts +1 -0
  66. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  67. package/dist/modes/interactive/components/footer.js +4 -1
  68. package/dist/modes/interactive/components/footer.js.map +1 -1
  69. package/dist/modes/interactive/components/tree-selector.d.ts +4 -2
  70. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  71. package/dist/modes/interactive/components/tree-selector.js +48 -15
  72. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  73. package/dist/modes/interactive/interactive-mode.d.ts +11 -4
  74. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  75. package/dist/modes/interactive/interactive-mode.js +112 -89
  76. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  77. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  78. package/dist/modes/interactive/theme/theme.js +6 -11
  79. package/dist/modes/interactive/theme/theme.js.map +1 -1
  80. package/dist/modes/print-mode.d.ts +2 -2
  81. package/dist/modes/print-mode.d.ts.map +1 -1
  82. package/dist/modes/print-mode.js +37 -36
  83. package/dist/modes/print-mode.js.map +1 -1
  84. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  85. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  86. package/dist/modes/rpc/rpc-mode.js +72 -49
  87. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  88. package/docs/extensions.md +104 -19
  89. package/docs/json.md +5 -2
  90. package/docs/keybindings.md +2 -0
  91. package/docs/rpc.md +21 -7
  92. package/docs/sdk.md +168 -75
  93. package/docs/tree.md +6 -3
  94. package/examples/extensions/README.md +1 -0
  95. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  96. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  97. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  98. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  99. package/examples/extensions/hidden-thinking-label.ts +53 -0
  100. package/examples/extensions/rpc-demo.ts +3 -9
  101. package/examples/extensions/status-line.ts +0 -8
  102. package/examples/extensions/todo.ts +0 -2
  103. package/examples/extensions/tools.ts +0 -5
  104. package/examples/extensions/widget-placement.ts +4 -12
  105. package/examples/extensions/with-deps/package-lock.json +2 -2
  106. package/examples/extensions/with-deps/package.json +1 -1
  107. package/examples/sdk/02-custom-model.ts +1 -1
  108. package/examples/sdk/09-api-keys-and-oauth.ts +3 -3
  109. package/examples/sdk/12-full-control.ts +1 -1
  110. package/examples/sdk/13-session-runtime.ts +49 -0
  111. package/examples/sdk/README.md +5 -4
  112. package/package.json +4 -4
@@ -54,6 +54,9 @@ function isExpandable(obj) {
54
54
  }
55
55
  export class InteractiveMode {
56
56
  // Convenience accessors
57
+ get session() {
58
+ return this.runtimeHost.session;
59
+ }
57
60
  get agent() {
58
61
  return this.session.agent;
59
62
  }
@@ -63,12 +66,14 @@ export class InteractiveMode {
63
66
  get settingsManager() {
64
67
  return this.session.settingsManager;
65
68
  }
66
- constructor(session, options = {}) {
69
+ constructor(runtimeHost, options = {}) {
67
70
  this.options = options;
68
71
  this.isInitialized = false;
69
72
  this.loadingAnimation = undefined;
70
73
  this.pendingWorkingMessage = undefined;
71
74
  this.defaultWorkingMessage = "Working...";
75
+ this.defaultHiddenThinkingLabel = "Thinking...";
76
+ this.hiddenThinkingLabel = this.defaultHiddenThinkingLabel;
72
77
  this.lastSigintTime = 0;
73
78
  this.lastEscapeTime = 0;
74
79
  this.changelogMarkdown = undefined;
@@ -119,7 +124,7 @@ export class InteractiveMode {
119
124
  * Emits shutdown event to extensions, then exits.
120
125
  */
121
126
  this.isShuttingDown = false;
122
- this.session = session;
127
+ this.runtimeHost = runtimeHost;
123
128
  this.version = VERSION;
124
129
  this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
125
130
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
@@ -140,9 +145,9 @@ export class InteractiveMode {
140
145
  this.editor = this.defaultEditor;
141
146
  this.editorContainer = new Container();
142
147
  this.editorContainer.addChild(this.editor);
143
- this.footerDataProvider = new FooterDataProvider();
144
- this.footer = new FooterComponent(session, this.footerDataProvider);
145
- this.footer.setAutoCompactEnabled(session.autoCompactionEnabled);
148
+ this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
149
+ this.footer = new FooterComponent(this.session, this.footerDataProvider);
150
+ this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
146
151
  // Load hide thinking block setting
147
152
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
148
153
  // Register themes from resource loader and initialize
@@ -249,7 +254,7 @@ export class InteractiveMode {
249
254
  }
250
255
  }
251
256
  // Setup autocomplete
252
- this.autocompleteProvider = new CombinedAutocompleteProvider([...slashCommands, ...templateCommands, ...extensionCommands, ...skillCommandList], process.cwd(), fdPath);
257
+ this.autocompleteProvider = new CombinedAutocompleteProvider([...slashCommands, ...templateCommands, ...extensionCommands, ...skillCommandList], this.sessionManager.getCwd(), fdPath);
253
258
  this.defaultEditor.setAutocompleteProvider(this.autocompleteProvider);
254
259
  if (this.editor !== this.defaultEditor) {
255
260
  this.editor.setAutocompleteProvider?.(this.autocompleteProvider);
@@ -344,7 +349,7 @@ export class InteractiveMode {
344
349
  this.ui.start();
345
350
  this.isInitialized = true;
346
351
  // Initialize extensions first so resources are shown before messages
347
- await this.initExtensions();
352
+ await this.bindCurrentSessionExtensions();
348
353
  // Render initial messages AFTER showing loaded resources
349
354
  this.renderInitialMessages();
350
355
  // Set terminal title
@@ -368,7 +373,7 @@ export class InteractiveMode {
368
373
  * Update terminal title with session name and cwd.
369
374
  */
370
375
  updateTerminalTitle() {
371
- const cwdBasename = path.basename(process.cwd());
376
+ const cwdBasename = path.basename(this.sessionManager.getCwd());
372
377
  const sessionName = this.sessionManager.getSessionName();
373
378
  if (sessionName) {
374
379
  this.ui.terminal.setTitle(`π - ${sessionName} - ${cwdBasename}`);
@@ -475,7 +480,7 @@ export class InteractiveMode {
475
480
  }
476
481
  try {
477
482
  const packageManager = new DefaultPackageManager({
478
- cwd: process.cwd(),
483
+ cwd: this.sessionManager.getCwd(),
479
484
  agentDir: getAgentDir(),
480
485
  settingsManager: this.settingsManager,
481
486
  });
@@ -863,7 +868,7 @@ export class InteractiveMode {
863
868
  /**
864
869
  * Initialize the extension system with TUI-based UI context.
865
870
  */
866
- async initExtensions() {
871
+ async bindCurrentSessionExtensions() {
867
872
  const uiContext = this.createExtensionUIContext();
868
873
  await this.session.bindExtensions({
869
874
  uiContext,
@@ -875,33 +880,23 @@ export class InteractiveMode {
875
880
  this.loadingAnimation = undefined;
876
881
  }
877
882
  this.statusContainer.clear();
878
- // Delegate to AgentSession (handles setup + agent state sync)
879
- const success = await this.session.newSession(options);
880
- if (!success) {
881
- return { cancelled: true };
883
+ const result = await this.runtimeHost.newSession(options);
884
+ if (!result.cancelled) {
885
+ await this.handleRuntimeSessionChange();
886
+ this.renderCurrentSessionState();
887
+ this.ui.requestRender();
882
888
  }
883
- // Clear UI state
884
- this.chatContainer.clear();
885
- this.pendingMessagesContainer.clear();
886
- this.compactionQueuedMessages = [];
887
- this.streamingComponent = undefined;
888
- this.streamingMessage = undefined;
889
- this.pendingTools.clear();
890
- // Render any messages added via setup, or show empty session
891
- this.renderInitialMessages();
892
- this.ui.requestRender();
893
- return { cancelled: false };
889
+ return result;
894
890
  },
895
891
  fork: async (entryId) => {
896
- const result = await this.session.fork(entryId);
897
- if (result.cancelled) {
898
- return { cancelled: true };
892
+ const result = await this.runtimeHost.fork(entryId);
893
+ if (!result.cancelled) {
894
+ await this.handleRuntimeSessionChange();
895
+ this.renderCurrentSessionState();
896
+ this.editor.setText(result.selectedText ?? "");
897
+ this.showStatus("Forked to new session");
899
898
  }
900
- this.chatContainer.clear();
901
- this.renderInitialMessages();
902
- this.editor.setText(result.selectedText);
903
- this.showStatus("Forked to new session");
904
- return { cancelled: false };
899
+ return { cancelled: result.cancelled };
905
900
  },
906
901
  navigateTree: async (targetId, options) => {
907
902
  const result = await this.session.navigateTree(targetId, {
@@ -949,6 +944,42 @@ export class InteractiveMode {
949
944
  this.setupExtensionShortcuts(extensionRunner);
950
945
  this.showLoadedResources({ force: false });
951
946
  }
947
+ applyRuntimeSettings() {
948
+ this.footer.setSession(this.session);
949
+ this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
950
+ this.footerDataProvider.setCwd(this.sessionManager.getCwd());
951
+ this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
952
+ this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor());
953
+ this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
954
+ const editorPaddingX = this.settingsManager.getEditorPaddingX();
955
+ const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
956
+ this.defaultEditor.setPaddingX(editorPaddingX);
957
+ this.defaultEditor.setAutocompleteMaxVisible(autocompleteMaxVisible);
958
+ if (this.editor !== this.defaultEditor) {
959
+ this.editor.setPaddingX?.(editorPaddingX);
960
+ this.editor.setAutocompleteMaxVisible?.(autocompleteMaxVisible);
961
+ }
962
+ }
963
+ async handleRuntimeSessionChange() {
964
+ this.resetExtensionUI();
965
+ this.unsubscribe?.();
966
+ this.unsubscribe = undefined;
967
+ this.applyRuntimeSettings();
968
+ await this.bindCurrentSessionExtensions();
969
+ this.subscribeToAgent();
970
+ await this.updateAvailableProviderCount();
971
+ this.updateEditorBorderColor();
972
+ this.updateTerminalTitle();
973
+ }
974
+ renderCurrentSessionState() {
975
+ this.chatContainer.clear();
976
+ this.pendingMessagesContainer.clear();
977
+ this.compactionQueuedMessages = [];
978
+ this.streamingComponent = undefined;
979
+ this.streamingMessage = undefined;
980
+ this.pendingTools.clear();
981
+ this.renderInitialMessages();
982
+ }
952
983
  /**
953
984
  * Get a registered tool definition by name (for custom rendering).
954
985
  */
@@ -966,7 +997,7 @@ export class InteractiveMode {
966
997
  const createContext = () => ({
967
998
  ui: this.createExtensionUIContext(),
968
999
  hasUI: true,
969
- cwd: process.cwd(),
1000
+ cwd: this.sessionManager.getCwd(),
970
1001
  sessionManager: this.sessionManager,
971
1002
  modelRegistry: this.session.modelRegistry,
972
1003
  model: this.session.model,
@@ -1014,6 +1045,18 @@ export class InteractiveMode {
1014
1045
  this.footerDataProvider.setExtensionStatus(key, text);
1015
1046
  this.ui.requestRender();
1016
1047
  }
1048
+ setHiddenThinkingLabel(label) {
1049
+ this.hiddenThinkingLabel = label ?? this.defaultHiddenThinkingLabel;
1050
+ for (const child of this.chatContainer.children) {
1051
+ if (child instanceof AssistantMessageComponent) {
1052
+ child.setHiddenThinkingLabel(this.hiddenThinkingLabel);
1053
+ }
1054
+ }
1055
+ if (this.streamingComponent) {
1056
+ this.streamingComponent.setHiddenThinkingLabel(this.hiddenThinkingLabel);
1057
+ }
1058
+ this.ui.requestRender();
1059
+ }
1017
1060
  /**
1018
1061
  * Set an extension widget (string array or custom component).
1019
1062
  */
@@ -1085,6 +1128,7 @@ export class InteractiveMode {
1085
1128
  if (this.loadingAnimation) {
1086
1129
  this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${keyText("app.interrupt")} to interrupt)`);
1087
1130
  }
1131
+ this.setHiddenThinkingLabel();
1088
1132
  }
1089
1133
  // Maximum total widget lines to prevent viewport overflow
1090
1134
  static { this.MAX_WIDGET_LINES = 10; }
@@ -1214,6 +1258,7 @@ export class InteractiveMode {
1214
1258
  this.pendingWorkingMessage = message;
1215
1259
  }
1216
1260
  },
1261
+ setHiddenThinkingLabel: (label) => this.setHiddenThinkingLabel(label),
1217
1262
  setWidget: (key, content, options) => this.setExtensionWidget(key, content, options),
1218
1263
  setFooter: (factory) => this.setExtensionFooter(factory),
1219
1264
  setHeader: (factory) => this.setExtensionHeader(factory),
@@ -1817,6 +1862,10 @@ export class InteractiveMode {
1817
1862
  }
1818
1863
  this.ui.requestRender();
1819
1864
  break;
1865
+ case "queue_update":
1866
+ this.updatePendingMessagesDisplay();
1867
+ this.ui.requestRender();
1868
+ break;
1820
1869
  case "message_start":
1821
1870
  if (event.message.role === "custom") {
1822
1871
  this.addMessageToChat(event.message);
@@ -1828,7 +1877,7 @@ export class InteractiveMode {
1828
1877
  this.ui.requestRender();
1829
1878
  }
1830
1879
  else if (event.message.role === "assistant") {
1831
- this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings());
1880
+ this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel);
1832
1881
  this.streamingMessage = event.message;
1833
1882
  this.chatContainer.addChild(this.streamingComponent);
1834
1883
  this.streamingComponent.updateContent(this.streamingMessage);
@@ -1844,7 +1893,7 @@ export class InteractiveMode {
1844
1893
  if (!this.pendingTools.has(content.id)) {
1845
1894
  const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
1846
1895
  showImages: this.settingsManager.getShowImages(),
1847
- }, this.getRegisteredToolDefinition(content.name), this.ui);
1896
+ }, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
1848
1897
  component.setExpanded(this.toolOutputExpanded);
1849
1898
  this.chatContainer.addChild(component);
1850
1899
  this.pendingTools.set(content.id, component);
@@ -1904,7 +1953,7 @@ export class InteractiveMode {
1904
1953
  if (!component) {
1905
1954
  component = new ToolExecutionComponent(event.toolName, event.toolCallId, event.args, {
1906
1955
  showImages: this.settingsManager.getShowImages(),
1907
- }, this.getRegisteredToolDefinition(event.toolName), this.ui);
1956
+ }, this.getRegisteredToolDefinition(event.toolName), this.ui, this.sessionManager.getCwd());
1908
1957
  component.setExpanded(this.toolOutputExpanded);
1909
1958
  this.chatContainer.addChild(component);
1910
1959
  this.pendingTools.set(event.toolCallId, component);
@@ -2126,7 +2175,7 @@ export class InteractiveMode {
2126
2175
  break;
2127
2176
  }
2128
2177
  case "assistant": {
2129
- const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings());
2178
+ const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel);
2130
2179
  this.chatContainer.addChild(assistantComponent);
2131
2180
  break;
2132
2181
  }
@@ -2158,7 +2207,7 @@ export class InteractiveMode {
2158
2207
  // Render tool call components
2159
2208
  for (const content of message.content) {
2160
2209
  if (content.type === "toolCall") {
2161
- const component = new ToolExecutionComponent(content.name, content.id, content.arguments, { showImages: this.settingsManager.getShowImages() }, this.getRegisteredToolDefinition(content.name), this.ui);
2210
+ const component = new ToolExecutionComponent(content.name, content.id, content.arguments, { showImages: this.settingsManager.getShowImages() }, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
2162
2211
  component.setExpanded(this.toolOutputExpanded);
2163
2212
  this.chatContainer.addChild(component);
2164
2213
  if (message.stopReason === "aborted" || message.stopReason === "error") {
@@ -2246,13 +2295,7 @@ export class InteractiveMode {
2246
2295
  if (this.isShuttingDown)
2247
2296
  return;
2248
2297
  this.isShuttingDown = true;
2249
- // Emit shutdown event to extensions
2250
- const extensionRunner = this.session.extensionRunner;
2251
- if (extensionRunner?.hasHandlers("session_shutdown")) {
2252
- await extensionRunner.emit({
2253
- type: "session_shutdown",
2254
- });
2255
- }
2298
+ await this.runtimeHost.dispose();
2256
2299
  // Wait for any pending renders to complete
2257
2300
  // requestRender() uses process.nextTick(), so we wait one tick
2258
2301
  await new Promise((resolve) => process.nextTick(resolve));
@@ -2722,7 +2765,7 @@ export class InteractiveMode {
2722
2765
  },
2723
2766
  onTransportChange: (transport) => {
2724
2767
  this.settingsManager.setTransport(transport);
2725
- this.session.agent.setTransport(transport);
2768
+ this.session.agent.transport = transport;
2726
2769
  },
2727
2770
  onThinkingLevelChange: (level) => {
2728
2771
  this.session.setThinkingLevel(level);
@@ -2978,16 +3021,15 @@ export class InteractiveMode {
2978
3021
  }
2979
3022
  this.showSelector((done) => {
2980
3023
  const selector = new UserMessageSelectorComponent(userMessages.map((m) => ({ id: m.entryId, text: m.text })), async (entryId) => {
2981
- const result = await this.session.fork(entryId);
3024
+ const result = await this.runtimeHost.fork(entryId);
2982
3025
  if (result.cancelled) {
2983
- // Extension cancelled the fork
2984
3026
  done();
2985
3027
  this.ui.requestRender();
2986
3028
  return;
2987
3029
  }
2988
- this.chatContainer.clear();
2989
- this.renderInitialMessages();
2990
- this.editor.setText(result.selectedText);
3030
+ await this.handleRuntimeSessionChange();
3031
+ this.renderCurrentSessionState();
3032
+ this.editor.setText(result.selectedText ?? "");
2991
3033
  done();
2992
3034
  this.showStatus("Branched to new session");
2993
3035
  }, () => {
@@ -3123,23 +3165,17 @@ export class InteractiveMode {
3123
3165
  });
3124
3166
  }
3125
3167
  async handleResumeSession(sessionPath) {
3126
- // Stop loading animation
3127
3168
  if (this.loadingAnimation) {
3128
3169
  this.loadingAnimation.stop();
3129
3170
  this.loadingAnimation = undefined;
3130
3171
  }
3131
3172
  this.statusContainer.clear();
3132
- // Clear UI state
3133
- this.pendingMessagesContainer.clear();
3134
- this.compactionQueuedMessages = [];
3135
- this.streamingComponent = undefined;
3136
- this.streamingMessage = undefined;
3137
- this.pendingTools.clear();
3138
- // Switch session via AgentSession (emits extension session events)
3139
- await this.session.switchSession(sessionPath);
3140
- // Clear and re-render the chat
3141
- this.chatContainer.clear();
3142
- this.renderInitialMessages();
3173
+ const result = await this.runtimeHost.switchSession(sessionPath);
3174
+ if (result.cancelled) {
3175
+ return;
3176
+ }
3177
+ await this.handleRuntimeSessionChange();
3178
+ this.renderCurrentSessionState();
3143
3179
  this.showStatus("Resumed session");
3144
3180
  }
3145
3181
  async showOAuthSelector(mode) {
@@ -3358,26 +3394,18 @@ export class InteractiveMode {
3358
3394
  return;
3359
3395
  }
3360
3396
  try {
3361
- // Stop loading animation
3362
3397
  if (this.loadingAnimation) {
3363
3398
  this.loadingAnimation.stop();
3364
3399
  this.loadingAnimation = undefined;
3365
3400
  }
3366
3401
  this.statusContainer.clear();
3367
- // Clear UI state
3368
- this.pendingMessagesContainer.clear();
3369
- this.compactionQueuedMessages = [];
3370
- this.streamingComponent = undefined;
3371
- this.streamingMessage = undefined;
3372
- this.pendingTools.clear();
3373
- const success = await this.session.importFromJsonl(inputPath);
3374
- if (!success) {
3375
- this.showWarning("Import cancelled");
3402
+ const result = await this.runtimeHost.importFromJsonl(inputPath);
3403
+ if (result.cancelled) {
3404
+ this.showStatus("Import cancelled");
3376
3405
  return;
3377
3406
  }
3378
- // Clear and re-render the chat
3379
- this.chatContainer.clear();
3380
- this.renderInitialMessages();
3407
+ await this.handleRuntimeSessionChange();
3408
+ this.renderCurrentSessionState();
3381
3409
  this.showStatus(`Session imported from: ${inputPath}`);
3382
3410
  }
3383
3411
  catch (error) {
@@ -3691,22 +3719,17 @@ export class InteractiveMode {
3691
3719
  this.ui.requestRender();
3692
3720
  }
3693
3721
  async handleClearCommand() {
3694
- // Stop loading animation
3695
3722
  if (this.loadingAnimation) {
3696
3723
  this.loadingAnimation.stop();
3697
3724
  this.loadingAnimation = undefined;
3698
3725
  }
3699
3726
  this.statusContainer.clear();
3700
- // New session via session (emits extension session events)
3701
- await this.session.newSession();
3702
- // Clear UI state
3703
- this.headerContainer.clear();
3704
- this.chatContainer.clear();
3705
- this.pendingMessagesContainer.clear();
3706
- this.compactionQueuedMessages = [];
3707
- this.streamingComponent = undefined;
3708
- this.streamingMessage = undefined;
3709
- this.pendingTools.clear();
3727
+ const result = await this.runtimeHost.newSession();
3728
+ if (result.cancelled) {
3729
+ return;
3730
+ }
3731
+ await this.handleRuntimeSessionChange();
3732
+ this.renderCurrentSessionState();
3710
3733
  this.chatContainer.addChild(new Spacer(1));
3711
3734
  this.chatContainer.addChild(new Text(`${theme.fg("accent", "✓ New session started")}`, 1, 1));
3712
3735
  this.ui.requestRender();
@@ -3761,7 +3784,7 @@ export class InteractiveMode {
3761
3784
  type: "user_bash",
3762
3785
  command,
3763
3786
  excludeFromContext,
3764
- cwd: process.cwd(),
3787
+ cwd: this.sessionManager.getCwd(),
3765
3788
  })
3766
3789
  : undefined;
3767
3790
  // If extension returned a full result, use it directly