@mariozechner/pi-coding-agent 0.64.0 → 0.65.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 (137) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/README.md +12 -6
  3. package/dist/cli/args.d.ts +7 -4
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +37 -15
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-session-runtime.d.ts +83 -0
  8. package/dist/core/agent-session-runtime.d.ts.map +1 -0
  9. package/dist/core/agent-session-runtime.js +236 -0
  10. package/dist/core/agent-session-runtime.js.map +1 -0
  11. package/dist/core/agent-session-services.d.ts +86 -0
  12. package/dist/core/agent-session-services.d.ts.map +1 -0
  13. package/dist/core/agent-session-services.js +116 -0
  14. package/dist/core/agent-session-services.js.map +1 -0
  15. package/dist/core/agent-session.d.ts +5 -42
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +46 -237
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/bash-executor.d.ts.map +1 -1
  20. package/dist/core/bash-executor.js +19 -7
  21. package/dist/core/bash-executor.js.map +1 -1
  22. package/dist/core/export-html/tool-renderer.d.ts +2 -0
  23. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  24. package/dist/core/export-html/tool-renderer.js +2 -2
  25. package/dist/core/export-html/tool-renderer.js.map +1 -1
  26. package/dist/core/extensions/index.d.ts +2 -2
  27. package/dist/core/extensions/index.d.ts.map +1 -1
  28. package/dist/core/extensions/index.js +1 -1
  29. package/dist/core/extensions/index.js.map +1 -1
  30. package/dist/core/extensions/types.d.ts +16 -28
  31. package/dist/core/extensions/types.d.ts.map +1 -1
  32. package/dist/core/extensions/types.js +10 -0
  33. package/dist/core/extensions/types.js.map +1 -1
  34. package/dist/core/footer-data-provider.d.ts +5 -1
  35. package/dist/core/footer-data-provider.d.ts.map +1 -1
  36. package/dist/core/footer-data-provider.js +70 -8
  37. package/dist/core/footer-data-provider.js.map +1 -1
  38. package/dist/core/index.d.ts +3 -1
  39. package/dist/core/index.d.ts.map +1 -1
  40. package/dist/core/index.js +3 -1
  41. package/dist/core/index.js.map +1 -1
  42. package/dist/core/keybindings.d.ts +14 -1
  43. package/dist/core/keybindings.d.ts.map +1 -1
  44. package/dist/core/keybindings.js +13 -14
  45. package/dist/core/keybindings.js.map +1 -1
  46. package/dist/core/package-manager.d.ts +20 -0
  47. package/dist/core/package-manager.d.ts.map +1 -1
  48. package/dist/core/package-manager.js +55 -9
  49. package/dist/core/package-manager.js.map +1 -1
  50. package/dist/core/resource-loader.d.ts.map +1 -1
  51. package/dist/core/resource-loader.js +25 -3
  52. package/dist/core/resource-loader.js.map +1 -1
  53. package/dist/core/sdk.d.ts +4 -1
  54. package/dist/core/sdk.d.ts.map +1 -1
  55. package/dist/core/sdk.js +4 -1
  56. package/dist/core/sdk.js.map +1 -1
  57. package/dist/core/session-cwd.d.ts +19 -0
  58. package/dist/core/session-cwd.d.ts.map +1 -0
  59. package/dist/core/session-cwd.js +38 -0
  60. package/dist/core/session-cwd.js.map +1 -0
  61. package/dist/core/session-manager.d.ts +5 -1
  62. package/dist/core/session-manager.d.ts.map +1 -1
  63. package/dist/core/session-manager.js +16 -8
  64. package/dist/core/session-manager.js.map +1 -1
  65. package/dist/core/settings-manager.d.ts +1 -1
  66. package/dist/core/settings-manager.d.ts.map +1 -1
  67. package/dist/core/settings-manager.js +2 -1
  68. package/dist/core/settings-manager.js.map +1 -1
  69. package/dist/core/tools/bash.d.ts.map +1 -1
  70. package/dist/core/tools/bash.js +19 -9
  71. package/dist/core/tools/bash.js.map +1 -1
  72. package/dist/index.d.ts +3 -3
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +3 -3
  75. package/dist/index.js.map +1 -1
  76. package/dist/main.d.ts.map +1 -1
  77. package/dist/main.js +245 -426
  78. package/dist/main.js.map +1 -1
  79. package/dist/migrations.d.ts.map +1 -1
  80. package/dist/migrations.js +20 -0
  81. package/dist/migrations.js.map +1 -1
  82. package/dist/modes/interactive/components/footer.d.ts +1 -0
  83. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  84. package/dist/modes/interactive/components/footer.js +4 -1
  85. package/dist/modes/interactive/components/footer.js.map +1 -1
  86. package/dist/modes/interactive/components/tree-selector.d.ts +4 -2
  87. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  88. package/dist/modes/interactive/components/tree-selector.js +48 -15
  89. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  90. package/dist/modes/interactive/interactive-mode.d.ts +10 -4
  91. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  92. package/dist/modes/interactive/interactive-mode.js +160 -94
  93. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  94. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  95. package/dist/modes/interactive/theme/theme.js +15 -11
  96. package/dist/modes/interactive/theme/theme.js.map +1 -1
  97. package/dist/modes/print-mode.d.ts +2 -2
  98. package/dist/modes/print-mode.d.ts.map +1 -1
  99. package/dist/modes/print-mode.js +41 -36
  100. package/dist/modes/print-mode.js.map +1 -1
  101. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  102. package/dist/modes/rpc/rpc-client.js +1 -0
  103. package/dist/modes/rpc/rpc-client.js.map +1 -1
  104. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  105. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  106. package/dist/modes/rpc/rpc-mode.js +92 -64
  107. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  108. package/dist/package-manager-cli.d.ts +4 -0
  109. package/dist/package-manager-cli.d.ts.map +1 -0
  110. package/dist/package-manager-cli.js +234 -0
  111. package/dist/package-manager-cli.js.map +1 -0
  112. package/dist/utils/paths.d.ts +7 -0
  113. package/dist/utils/paths.d.ts.map +1 -0
  114. package/dist/utils/paths.js +19 -0
  115. package/dist/utils/paths.js.map +1 -0
  116. package/docs/extensions.md +72 -40
  117. package/docs/keybindings.md +2 -0
  118. package/docs/sdk.md +227 -74
  119. package/docs/settings.md +1 -1
  120. package/docs/tree.md +6 -3
  121. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  122. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  123. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  124. package/examples/extensions/custom-provider-qwen-cli/package.json +1 -1
  125. package/examples/extensions/doom-overlay/doom/build.sh +2 -2
  126. package/examples/extensions/hello.ts +18 -17
  127. package/examples/extensions/hidden-thinking-label.ts +0 -4
  128. package/examples/extensions/rpc-demo.ts +3 -9
  129. package/examples/extensions/status-line.ts +0 -8
  130. package/examples/extensions/todo.ts +0 -2
  131. package/examples/extensions/tools.ts +0 -5
  132. package/examples/extensions/widget-placement.ts +4 -12
  133. package/examples/extensions/with-deps/package-lock.json +2 -2
  134. package/examples/extensions/with-deps/package.json +1 -1
  135. package/examples/sdk/13-session-runtime.ts +67 -0
  136. package/examples/sdk/README.md +4 -1
  137. package/package.json +4 -4
@@ -15,6 +15,7 @@ import { KeybindingsManager } from "../../core/keybindings.js";
15
15
  import { createCompactionSummaryMessage } from "../../core/messages.js";
16
16
  import { findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.js";
17
17
  import { DefaultPackageManager } from "../../core/package-manager.js";
18
+ import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
18
19
  import { SessionManager } from "../../core/session-manager.js";
19
20
  import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
20
21
  import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
@@ -48,13 +49,13 @@ import { ToolExecutionComponent } from "./components/tool-execution.js";
48
49
  import { TreeSelectorComponent } from "./components/tree-selector.js";
49
50
  import { UserMessageComponent } from "./components/user-message.js";
50
51
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
51
- import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, initTheme, onThemeChange, setRegisteredThemes, setTheme, setThemeInstance, Theme, theme, } from "./theme/theme.js";
52
+ import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, initTheme, onThemeChange, setRegisteredThemes, setTheme, setThemeInstance, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
52
53
  function isExpandable(obj) {
53
54
  return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
54
55
  }
55
56
  export class InteractiveMode {
56
57
  options;
57
- session;
58
+ runtimeHost;
58
59
  ui;
59
60
  chatContainer;
60
61
  pendingMessagesContainer;
@@ -130,6 +131,9 @@ export class InteractiveMode {
130
131
  // Custom header from extension (undefined = use built-in header)
131
132
  customHeader = undefined;
132
133
  // Convenience accessors
134
+ get session() {
135
+ return this.runtimeHost.session;
136
+ }
133
137
  get agent() {
134
138
  return this.session.agent;
135
139
  }
@@ -139,9 +143,9 @@ export class InteractiveMode {
139
143
  get settingsManager() {
140
144
  return this.session.settingsManager;
141
145
  }
142
- constructor(session, options = {}) {
146
+ constructor(runtimeHost, options = {}) {
143
147
  this.options = options;
144
- this.session = session;
148
+ this.runtimeHost = runtimeHost;
145
149
  this.version = VERSION;
146
150
  this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
147
151
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
@@ -162,9 +166,9 @@ export class InteractiveMode {
162
166
  this.editor = this.defaultEditor;
163
167
  this.editorContainer = new Container();
164
168
  this.editorContainer.addChild(this.editor);
165
- this.footerDataProvider = new FooterDataProvider();
166
- this.footer = new FooterComponent(session, this.footerDataProvider);
167
- this.footer.setAutoCompactEnabled(session.autoCompactionEnabled);
169
+ this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
170
+ this.footer = new FooterComponent(this.session, this.footerDataProvider);
171
+ this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
168
172
  // Load hide thinking block setting
169
173
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
170
174
  // Register themes from resource loader and initialize
@@ -271,7 +275,7 @@ export class InteractiveMode {
271
275
  }
272
276
  }
273
277
  // Setup autocomplete
274
- this.autocompleteProvider = new CombinedAutocompleteProvider([...slashCommands, ...templateCommands, ...extensionCommands, ...skillCommandList], process.cwd(), fdPath);
278
+ this.autocompleteProvider = new CombinedAutocompleteProvider([...slashCommands, ...templateCommands, ...extensionCommands, ...skillCommandList], this.sessionManager.getCwd(), fdPath);
275
279
  this.defaultEditor.setAutocompleteProvider(this.autocompleteProvider);
276
280
  if (this.editor !== this.defaultEditor) {
277
281
  this.editor.setAutocompleteProvider?.(this.autocompleteProvider);
@@ -366,7 +370,7 @@ export class InteractiveMode {
366
370
  this.ui.start();
367
371
  this.isInitialized = true;
368
372
  // Initialize extensions first so resources are shown before messages
369
- await this.initExtensions();
373
+ await this.bindCurrentSessionExtensions();
370
374
  // Render initial messages AFTER showing loaded resources
371
375
  this.renderInitialMessages();
372
376
  // Set terminal title
@@ -390,7 +394,7 @@ export class InteractiveMode {
390
394
  * Update terminal title with session name and cwd.
391
395
  */
392
396
  updateTerminalTitle() {
393
- const cwdBasename = path.basename(process.cwd());
397
+ const cwdBasename = path.basename(this.sessionManager.getCwd());
394
398
  const sessionName = this.sessionManager.getSessionName();
395
399
  if (sessionName) {
396
400
  this.ui.terminal.setTitle(`π - ${sessionName} - ${cwdBasename}`);
@@ -497,7 +501,7 @@ export class InteractiveMode {
497
501
  }
498
502
  try {
499
503
  const packageManager = new DefaultPackageManager({
500
- cwd: process.cwd(),
504
+ cwd: this.sessionManager.getCwd(),
501
505
  agentDir: getAgentDir(),
502
506
  settingsManager: this.settingsManager,
503
507
  });
@@ -885,7 +889,7 @@ export class InteractiveMode {
885
889
  /**
886
890
  * Initialize the extension system with TUI-based UI context.
887
891
  */
888
- async initExtensions() {
892
+ async bindCurrentSessionExtensions() {
889
893
  const uiContext = this.createExtensionUIContext();
890
894
  await this.session.bindExtensions({
891
895
  uiContext,
@@ -897,33 +901,33 @@ export class InteractiveMode {
897
901
  this.loadingAnimation = undefined;
898
902
  }
899
903
  this.statusContainer.clear();
900
- // Delegate to AgentSession (handles setup + agent state sync)
901
- const success = await this.session.newSession(options);
902
- if (!success) {
903
- return { cancelled: true };
904
+ try {
905
+ const result = await this.runtimeHost.newSession(options);
906
+ if (!result.cancelled) {
907
+ await this.handleRuntimeSessionChange();
908
+ this.renderCurrentSessionState();
909
+ this.ui.requestRender();
910
+ }
911
+ return result;
912
+ }
913
+ catch (error) {
914
+ return this.handleFatalRuntimeError("Failed to create session", error);
904
915
  }
905
- // Clear UI state
906
- this.chatContainer.clear();
907
- this.pendingMessagesContainer.clear();
908
- this.compactionQueuedMessages = [];
909
- this.streamingComponent = undefined;
910
- this.streamingMessage = undefined;
911
- this.pendingTools.clear();
912
- // Render any messages added via setup, or show empty session
913
- this.renderInitialMessages();
914
- this.ui.requestRender();
915
- return { cancelled: false };
916
916
  },
917
917
  fork: async (entryId) => {
918
- const result = await this.session.fork(entryId);
919
- if (result.cancelled) {
920
- return { cancelled: true };
918
+ try {
919
+ const result = await this.runtimeHost.fork(entryId);
920
+ if (!result.cancelled) {
921
+ await this.handleRuntimeSessionChange();
922
+ this.renderCurrentSessionState();
923
+ this.editor.setText(result.selectedText ?? "");
924
+ this.showStatus("Forked to new session");
925
+ }
926
+ return { cancelled: result.cancelled };
927
+ }
928
+ catch (error) {
929
+ return this.handleFatalRuntimeError("Failed to fork session", error);
921
930
  }
922
- this.chatContainer.clear();
923
- this.renderInitialMessages();
924
- this.editor.setText(result.selectedText);
925
- this.showStatus("Forked to new session");
926
- return { cancelled: false };
927
931
  },
928
932
  navigateTree: async (targetId, options) => {
929
933
  const result = await this.session.navigateTree(targetId, {
@@ -971,6 +975,49 @@ export class InteractiveMode {
971
975
  this.setupExtensionShortcuts(extensionRunner);
972
976
  this.showLoadedResources({ force: false });
973
977
  }
978
+ applyRuntimeSettings() {
979
+ this.footer.setSession(this.session);
980
+ this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
981
+ this.footerDataProvider.setCwd(this.sessionManager.getCwd());
982
+ this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
983
+ this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor());
984
+ this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
985
+ const editorPaddingX = this.settingsManager.getEditorPaddingX();
986
+ const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
987
+ this.defaultEditor.setPaddingX(editorPaddingX);
988
+ this.defaultEditor.setAutocompleteMaxVisible(autocompleteMaxVisible);
989
+ if (this.editor !== this.defaultEditor) {
990
+ this.editor.setPaddingX?.(editorPaddingX);
991
+ this.editor.setAutocompleteMaxVisible?.(autocompleteMaxVisible);
992
+ }
993
+ }
994
+ async handleRuntimeSessionChange() {
995
+ this.resetExtensionUI();
996
+ this.unsubscribe?.();
997
+ this.unsubscribe = undefined;
998
+ this.applyRuntimeSettings();
999
+ await this.bindCurrentSessionExtensions();
1000
+ this.subscribeToAgent();
1001
+ await this.updateAvailableProviderCount();
1002
+ this.updateEditorBorderColor();
1003
+ this.updateTerminalTitle();
1004
+ }
1005
+ async handleFatalRuntimeError(prefix, error) {
1006
+ const message = error instanceof Error ? error.message : String(error);
1007
+ this.showError(`${prefix}: ${message}`);
1008
+ stopThemeWatcher();
1009
+ this.stop();
1010
+ process.exit(1);
1011
+ }
1012
+ renderCurrentSessionState() {
1013
+ this.chatContainer.clear();
1014
+ this.pendingMessagesContainer.clear();
1015
+ this.compactionQueuedMessages = [];
1016
+ this.streamingComponent = undefined;
1017
+ this.streamingMessage = undefined;
1018
+ this.pendingTools.clear();
1019
+ this.renderInitialMessages();
1020
+ }
974
1021
  /**
975
1022
  * Get a registered tool definition by name (for custom rendering).
976
1023
  */
@@ -988,7 +1035,7 @@ export class InteractiveMode {
988
1035
  const createContext = () => ({
989
1036
  ui: this.createExtensionUIContext(),
990
1037
  hasUI: true,
991
- cwd: process.cwd(),
1038
+ cwd: this.sessionManager.getCwd(),
992
1039
  sessionManager: this.sessionManager,
993
1040
  modelRegistry: this.session.modelRegistry,
994
1041
  model: this.session.model,
@@ -1331,6 +1378,10 @@ export class InteractiveMode {
1331
1378
  const result = await this.showExtensionSelector(`${title}\n${message}`, ["Yes", "No"], opts);
1332
1379
  return result === "Yes";
1333
1380
  }
1381
+ async promptForMissingSessionCwd(error) {
1382
+ const confirmed = await this.showExtensionConfirm("Session cwd not found", formatMissingSessionCwdPrompt(error.issue));
1383
+ return confirmed ? error.issue.fallbackCwd : undefined;
1384
+ }
1334
1385
  /**
1335
1386
  * Show a text input for extensions.
1336
1387
  */
@@ -1884,7 +1935,7 @@ export class InteractiveMode {
1884
1935
  if (!this.pendingTools.has(content.id)) {
1885
1936
  const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
1886
1937
  showImages: this.settingsManager.getShowImages(),
1887
- }, this.getRegisteredToolDefinition(content.name), this.ui);
1938
+ }, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
1888
1939
  component.setExpanded(this.toolOutputExpanded);
1889
1940
  this.chatContainer.addChild(component);
1890
1941
  this.pendingTools.set(content.id, component);
@@ -1944,7 +1995,7 @@ export class InteractiveMode {
1944
1995
  if (!component) {
1945
1996
  component = new ToolExecutionComponent(event.toolName, event.toolCallId, event.args, {
1946
1997
  showImages: this.settingsManager.getShowImages(),
1947
- }, this.getRegisteredToolDefinition(event.toolName), this.ui);
1998
+ }, this.getRegisteredToolDefinition(event.toolName), this.ui, this.sessionManager.getCwd());
1948
1999
  component.setExpanded(this.toolOutputExpanded);
1949
2000
  this.chatContainer.addChild(component);
1950
2001
  this.pendingTools.set(event.toolCallId, component);
@@ -2198,7 +2249,7 @@ export class InteractiveMode {
2198
2249
  // Render tool call components
2199
2250
  for (const content of message.content) {
2200
2251
  if (content.type === "toolCall") {
2201
- const component = new ToolExecutionComponent(content.name, content.id, content.arguments, { showImages: this.settingsManager.getShowImages() }, this.getRegisteredToolDefinition(content.name), this.ui);
2252
+ const component = new ToolExecutionComponent(content.name, content.id, content.arguments, { showImages: this.settingsManager.getShowImages() }, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
2202
2253
  component.setExpanded(this.toolOutputExpanded);
2203
2254
  this.chatContainer.addChild(component);
2204
2255
  if (message.stopReason === "aborted" || message.stopReason === "error") {
@@ -2291,13 +2342,7 @@ export class InteractiveMode {
2291
2342
  if (this.isShuttingDown)
2292
2343
  return;
2293
2344
  this.isShuttingDown = true;
2294
- // Emit shutdown event to extensions
2295
- const extensionRunner = this.session.extensionRunner;
2296
- if (extensionRunner?.hasHandlers("session_shutdown")) {
2297
- await extensionRunner.emit({
2298
- type: "session_shutdown",
2299
- });
2300
- }
2345
+ await this.runtimeHost.dispose();
2301
2346
  // Wait for any pending renders to complete
2302
2347
  // requestRender() uses process.nextTick(), so we wait one tick
2303
2348
  await new Promise((resolve) => process.nextTick(resolve));
@@ -2767,7 +2812,7 @@ export class InteractiveMode {
2767
2812
  },
2768
2813
  onTransportChange: (transport) => {
2769
2814
  this.settingsManager.setTransport(transport);
2770
- this.session.agent.setTransport(transport);
2815
+ this.session.agent.transport = transport;
2771
2816
  },
2772
2817
  onThinkingLevelChange: (level) => {
2773
2818
  this.session.setThinkingLevel(level);
@@ -3023,16 +3068,15 @@ export class InteractiveMode {
3023
3068
  }
3024
3069
  this.showSelector((done) => {
3025
3070
  const selector = new UserMessageSelectorComponent(userMessages.map((m) => ({ id: m.entryId, text: m.text })), async (entryId) => {
3026
- const result = await this.session.fork(entryId);
3071
+ const result = await this.runtimeHost.fork(entryId);
3027
3072
  if (result.cancelled) {
3028
- // Extension cancelled the fork
3029
3073
  done();
3030
3074
  this.ui.requestRender();
3031
3075
  return;
3032
3076
  }
3033
- this.chatContainer.clear();
3034
- this.renderInitialMessages();
3035
- this.editor.setText(result.selectedText);
3077
+ await this.handleRuntimeSessionChange();
3078
+ this.renderCurrentSessionState();
3079
+ this.editor.setText(result.selectedText ?? "");
3036
3080
  done();
3037
3081
  this.showStatus("Branched to new session");
3038
3082
  }, () => {
@@ -3168,24 +3212,38 @@ export class InteractiveMode {
3168
3212
  });
3169
3213
  }
3170
3214
  async handleResumeSession(sessionPath) {
3171
- // Stop loading animation
3172
3215
  if (this.loadingAnimation) {
3173
3216
  this.loadingAnimation.stop();
3174
3217
  this.loadingAnimation = undefined;
3175
3218
  }
3176
3219
  this.statusContainer.clear();
3177
- // Clear UI state
3178
- this.pendingMessagesContainer.clear();
3179
- this.compactionQueuedMessages = [];
3180
- this.streamingComponent = undefined;
3181
- this.streamingMessage = undefined;
3182
- this.pendingTools.clear();
3183
- // Switch session via AgentSession (emits extension session events)
3184
- await this.session.switchSession(sessionPath);
3185
- // Clear and re-render the chat
3186
- this.chatContainer.clear();
3187
- this.renderInitialMessages();
3188
- this.showStatus("Resumed session");
3220
+ try {
3221
+ const result = await this.runtimeHost.switchSession(sessionPath);
3222
+ if (result.cancelled) {
3223
+ return;
3224
+ }
3225
+ await this.handleRuntimeSessionChange();
3226
+ this.renderCurrentSessionState();
3227
+ this.showStatus("Resumed session");
3228
+ }
3229
+ catch (error) {
3230
+ if (error instanceof MissingSessionCwdError) {
3231
+ const selectedCwd = await this.promptForMissingSessionCwd(error);
3232
+ if (!selectedCwd) {
3233
+ this.showStatus("Resume cancelled");
3234
+ return;
3235
+ }
3236
+ const result = await this.runtimeHost.switchSession(sessionPath, selectedCwd);
3237
+ if (result.cancelled) {
3238
+ return;
3239
+ }
3240
+ await this.handleRuntimeSessionChange();
3241
+ this.renderCurrentSessionState();
3242
+ this.showStatus("Resumed session in current cwd");
3243
+ return;
3244
+ }
3245
+ await this.handleFatalRuntimeError("Failed to resume session", error);
3246
+ }
3189
3247
  }
3190
3248
  async showOAuthSelector(mode) {
3191
3249
  if (mode === "logout") {
@@ -3403,30 +3461,38 @@ export class InteractiveMode {
3403
3461
  return;
3404
3462
  }
3405
3463
  try {
3406
- // Stop loading animation
3407
3464
  if (this.loadingAnimation) {
3408
3465
  this.loadingAnimation.stop();
3409
3466
  this.loadingAnimation = undefined;
3410
3467
  }
3411
3468
  this.statusContainer.clear();
3412
- // Clear UI state
3413
- this.pendingMessagesContainer.clear();
3414
- this.compactionQueuedMessages = [];
3415
- this.streamingComponent = undefined;
3416
- this.streamingMessage = undefined;
3417
- this.pendingTools.clear();
3418
- const success = await this.session.importFromJsonl(inputPath);
3419
- if (!success) {
3420
- this.showWarning("Import cancelled");
3469
+ const result = await this.runtimeHost.importFromJsonl(inputPath);
3470
+ if (result.cancelled) {
3471
+ this.showStatus("Import cancelled");
3421
3472
  return;
3422
3473
  }
3423
- // Clear and re-render the chat
3424
- this.chatContainer.clear();
3425
- this.renderInitialMessages();
3474
+ await this.handleRuntimeSessionChange();
3475
+ this.renderCurrentSessionState();
3426
3476
  this.showStatus(`Session imported from: ${inputPath}`);
3427
3477
  }
3428
3478
  catch (error) {
3429
- this.showError(`Failed to import session: ${error instanceof Error ? error.message : "Unknown error"}`);
3479
+ if (error instanceof MissingSessionCwdError) {
3480
+ const selectedCwd = await this.promptForMissingSessionCwd(error);
3481
+ if (!selectedCwd) {
3482
+ this.showStatus("Import cancelled");
3483
+ return;
3484
+ }
3485
+ const result = await this.runtimeHost.importFromJsonl(inputPath, selectedCwd);
3486
+ if (result.cancelled) {
3487
+ this.showStatus("Import cancelled");
3488
+ return;
3489
+ }
3490
+ await this.handleRuntimeSessionChange();
3491
+ this.renderCurrentSessionState();
3492
+ this.showStatus(`Session imported from: ${inputPath}`);
3493
+ return;
3494
+ }
3495
+ await this.handleFatalRuntimeError("Failed to import session", error);
3430
3496
  }
3431
3497
  }
3432
3498
  async handleShareCommand() {
@@ -3736,25 +3802,25 @@ export class InteractiveMode {
3736
3802
  this.ui.requestRender();
3737
3803
  }
3738
3804
  async handleClearCommand() {
3739
- // Stop loading animation
3740
3805
  if (this.loadingAnimation) {
3741
3806
  this.loadingAnimation.stop();
3742
3807
  this.loadingAnimation = undefined;
3743
3808
  }
3744
3809
  this.statusContainer.clear();
3745
- // New session via session (emits extension session events)
3746
- await this.session.newSession();
3747
- // Clear UI state
3748
- this.headerContainer.clear();
3749
- this.chatContainer.clear();
3750
- this.pendingMessagesContainer.clear();
3751
- this.compactionQueuedMessages = [];
3752
- this.streamingComponent = undefined;
3753
- this.streamingMessage = undefined;
3754
- this.pendingTools.clear();
3755
- this.chatContainer.addChild(new Spacer(1));
3756
- this.chatContainer.addChild(new Text(`${theme.fg("accent", "✓ New session started")}`, 1, 1));
3757
- this.ui.requestRender();
3810
+ try {
3811
+ const result = await this.runtimeHost.newSession();
3812
+ if (result.cancelled) {
3813
+ return;
3814
+ }
3815
+ await this.handleRuntimeSessionChange();
3816
+ this.renderCurrentSessionState();
3817
+ this.chatContainer.addChild(new Spacer(1));
3818
+ this.chatContainer.addChild(new Text(`${theme.fg("accent", "✓ New session started")}`, 1, 1));
3819
+ this.ui.requestRender();
3820
+ }
3821
+ catch (error) {
3822
+ await this.handleFatalRuntimeError("Failed to create session", error);
3823
+ }
3758
3824
  }
3759
3825
  handleDebugCommand() {
3760
3826
  const width = this.ui.terminal.columns;
@@ -3806,7 +3872,7 @@ export class InteractiveMode {
3806
3872
  type: "user_bash",
3807
3873
  command,
3808
3874
  excludeFromContext,
3809
- cwd: process.cwd(),
3875
+ cwd: this.sessionManager.getCwd(),
3810
3876
  })
3811
3877
  : undefined;
3812
3878
  // If extension returned a full result, use it directly