From 36bffc1d139291fac2a47086950d11908acf7ded Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 17 Apr 2026 00:59:53 +0200 Subject: [PATCH] fix(coding-agent): restore /import error flow and document throws --- packages/coding-agent/CHANGELOG.md | 1 + .../src/core/agent-session-runtime.ts | 10 ++++++ .../src/modes/interactive/interactive-mode.ts | 36 ++++++++++--------- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 61f5022a..318437c8 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -7,6 +7,7 @@ - Fixed missing root exports for `RpcClient` and RPC protocol types from `@mariozechner/pi-coding-agent`, so ESM consumers can import them from the main package entrypoint ([#3275](https://github.com/badlogic/pi-mono/issues/3275)) - Fixed Bun binary asset path resolution to honor `PI_PACKAGE_DIR` for built-in themes, HTML export templates, and interactive bundled assets ([#3074](https://github.com/badlogic/pi-mono/issues/3074)) - Fixed user-message turn spacing in interactive mode by restoring an inter-message spacer before user turns (except the first user message), preventing assistant and user blocks from rendering flush together. +- Fixed interactive `/import` handling to route missing JSONL files through the non-fatal `SessionImportFileNotFoundError` path, and documented the `importFromJsonl()` exceptions (`SessionImportFileNotFoundError`, `MissingSessionCwdError`). ## [0.67.6] - 2026-04-16 diff --git a/packages/coding-agent/src/core/agent-session-runtime.ts b/packages/coding-agent/src/core/agent-session-runtime.ts index 327140bd..86bae87a 100644 --- a/packages/coding-agent/src/core/agent-session-runtime.ts +++ b/packages/coding-agent/src/core/agent-session-runtime.ts @@ -33,6 +33,9 @@ export type CreateAgentSessionRuntimeFactory = (options: { sessionStartEvent?: SessionStartEvent; }) => Promise; +/** + * Thrown when /import references a JSONL file path that does not exist. + */ export class SessionImportFileNotFoundError extends Error { readonly filePath: string; @@ -258,6 +261,13 @@ export class AgentSessionRuntime { return { cancelled: false, selectedText }; } + /** + * Import a session JSONL file and switch runtime state to the imported session. + * + * @returns `{ cancelled: true }` when cancelled by `session_before_switch`, otherwise `{ cancelled: false }`. + * @throws {SessionImportFileNotFoundError} When the input path does not exist. + * @throws {MissingSessionCwdError} When the imported session cwd cannot be resolved and no override is provided. + */ async importFromJsonl(inputPath: string, cwdOverride?: string): Promise<{ cancelled: boolean }> { const resolvedPath = resolve(inputPath); if (!existsSync(resolvedPath)) { diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index f48604e9..d2ae01de 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -47,7 +47,7 @@ import { VERSION, } from "../../config.js"; import { type AgentSession, type AgentSessionEvent, parseSkillBlock } from "../../core/agent-session.js"; -import type { AgentSessionRuntime } from "../../core/agent-session-runtime.js"; +import { type AgentSessionRuntime, SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js"; import type { ExtensionContext, ExtensionRunner, @@ -4417,20 +4417,6 @@ export class InteractiveMode { return; } - const handleImportResult = async (result: "ok" | "cancelled" | "not_found"): Promise => { - if (result === "cancelled") { - this.showStatus("Import cancelled"); - return; - } - if (result === "not_found") { - this.showError(`Failed to import session: File not found: ${path.resolve(inputPath)}`); - return; - } - await this.handleRuntimeSessionChange(); - this.renderCurrentSessionState(); - this.showStatus(`Session imported from: ${inputPath}`); - }; - try { if (this.loadingAnimation) { this.loadingAnimation.stop(); @@ -4438,7 +4424,13 @@ export class InteractiveMode { } this.statusContainer.clear(); const result = await this.runtimeHost.importFromJsonl(inputPath); - await handleImportResult(result); + if (result.cancelled) { + this.showStatus("Import cancelled"); + return; + } + await this.handleRuntimeSessionChange(); + this.renderCurrentSessionState(); + this.showStatus(`Session imported from: ${inputPath}`); } catch (error: unknown) { if (error instanceof MissingSessionCwdError) { const selectedCwd = await this.promptForMissingSessionCwd(error); @@ -4447,7 +4439,17 @@ export class InteractiveMode { return; } const result = await this.runtimeHost.importFromJsonl(inputPath, selectedCwd); - await handleImportResult(result); + if (result.cancelled) { + this.showStatus("Import cancelled"); + return; + } + await this.handleRuntimeSessionChange(); + this.renderCurrentSessionState(); + this.showStatus(`Session imported from: ${inputPath}`); + return; + } + if (error instanceof SessionImportFileNotFoundError) { + this.showError(`Failed to import session: ${error.message}`); return; } await this.handleFatalRuntimeError("Failed to import session", error);