fix(electrobun): session resume uses pending mode — user types prompt first, no empty string to SDK

This commit is contained in:
Hibryda 2026-03-27 03:35:02 +01:00
parent 31a3335651
commit 18b9c7c3b5
3 changed files with 49 additions and 6 deletions

View file

@ -3,6 +3,7 @@
import ChatInput from "./ChatInput.svelte";
import SessionPicker from "./SessionPicker.svelte";
import type { AgentMessage, AgentStatus } from "./agent-store.svelte.ts";
import { getPendingResume } from "./agent-store.svelte.ts";
import { t } from "./i18n.svelte.ts";
interface Props {
@ -55,6 +56,8 @@
return () => observer.disconnect();
});
function getPendingResumeForProject() { return projectId ? getPendingResume(projectId) : undefined; }
function handleSend() {
const text = promptText.trim();
if (!text) return;
@ -236,7 +239,7 @@
{model}
{provider}
{contextPct}
placeholder={t("agent.prompt.placeholder")}
placeholder={getPendingResumeForProject() ? 'Type to resume session...' : t("agent.prompt.placeholder")}
onSend={handleSend}
onInput={(v) => (promptText = v)}
/>

View file

@ -1,7 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import type { ClaudeSessionInfo } from './agent-store.svelte.ts';
import { listProjectSessions, resumeSession, continueLastSession, startAgent } from './agent-store.svelte.ts';
import { listProjectSessions, setPendingResume, getPendingResume, clearPendingResume } from './agent-store.svelte.ts';
import { t } from './i18n.svelte.ts';
interface Props {
@ -72,18 +72,21 @@
return s.length > max ? s.slice(0, max - 3) + '...' : s;
}
async function handleContinue() {
function handleContinue() {
open = false;
await continueLastSession(projectId, provider, '', cwd);
setPendingResume(projectId, 'continue');
// User types their next prompt and hits Send — startAgent reads the pending resume
}
async function handleResume(sdkSessionId: string) {
function handleResume(sdkSessionId: string) {
open = false;
await resumeSession(projectId, provider, sdkSessionId, '', cwd);
setPendingResume(projectId, 'resume', sdkSessionId);
// User types their next prompt and hits Send — startAgent reads the pending resume
}
function handleNew() {
open = false;
clearPendingResume(projectId);
onNewSession?.('');
}
</script>

View file

@ -127,6 +127,33 @@ function validateExtraEnv(env: Record<string, string> | undefined): Record<strin
// Map projectId -> sessionId for lookup
const projectSessionMap = new Map<string, string>();
// Pending resume: when the user selects a session to resume, we store the
// resume options here. The next startAgent() call reads and clears them.
// This avoids sending an empty prompt — the user types their message first.
interface PendingResume {
mode: 'continue' | 'resume';
sdkSessionId?: string;
}
const pendingResumes = new Map<string, PendingResume>();
/** Set a pending resume for a project — next startAgent will use these options. */
export function setPendingResume(projectId: string, mode: 'continue' | 'resume', sdkSessionId?: string): void {
pendingResumes.set(projectId, { mode, sdkSessionId });
bump();
}
/** Check if a project has a pending resume. */
export function getPendingResume(projectId: string): PendingResume | undefined {
void _v;
return pendingResumes.get(projectId);
}
/** Clear pending resume (called after startAgent consumes it). */
export function clearPendingResume(projectId: string): void {
pendingResumes.delete(projectId);
bump();
}
// Map sessionId -> reactive session state
let sessions = $state<Record<string, AgentSession>>({});
@ -522,6 +549,16 @@ async function _startAgentInner(
// If there's an existing done/error session for this project, clear it first
clearSession(projectId);
// Check for pending resume (user selected a session in the picker)
const pending = pendingResumes.get(projectId);
if (pending) {
pendingResumes.delete(projectId);
if (!options.resumeMode) {
options.resumeMode = pending.mode;
options.resumeSessionId = pending.sdkSessionId;
}
}
const sessionId = `${projectId}-${Date.now()}`;
// Read settings defaults if not explicitly provided (Fix #5)