feat(runtime): add user-controlled turn recovery

This commit is contained in:
Xubin Ren
2026-08-24 00:58:04 +08:00
parent ffa58aa5ef
commit 12029f8812
60 changed files with 4027 additions and 169 deletions
+3
View File
@@ -22,6 +22,7 @@ import type {
ProviderOAuthCompletionResult,
ProviderOAuthLoginResult,
ProviderSettingsUpdate,
RecoveryState,
SessionDeleteResult,
SessionHandle,
SessionAutomationsPayload,
@@ -192,6 +193,7 @@ export async function listSessions(
preview?: string;
model_preset?: string | null;
run_started_at?: number | null;
recovery_state?: RecoveryState | null;
workspace_scope?: WorkspaceScopePayload | null;
handle?: SessionHandle | null;
};
@@ -212,6 +214,7 @@ export async function listSessions(
preview: s.preview ?? "",
modelPreset: s.model_preset ?? null,
runStartedAt: s.run_started_at ?? null,
recoveryState: s.recovery_state ?? null,
workspaceScope: s.workspace_scope ?? null,
handle,
};
+3
View File
@@ -7,6 +7,7 @@ export interface LocalPreferences {
activityMode: LocalActivityMode;
codeWrap: boolean;
brandLogos: boolean;
browserNotifications: boolean;
fileEditDisplayMode: FileEditDisplayMode;
}
@@ -18,6 +19,7 @@ export const DEFAULT_LOCAL_PREFS: LocalPreferences = {
activityMode: "auto",
codeWrap: true,
brandLogos: false,
browserNotifications: false,
fileEditDisplayMode: "summary",
};
@@ -35,6 +37,7 @@ export function readLocalPreferences(): LocalPreferences {
activityMode: parsed.activityMode === "expanded" ? "expanded" : "auto",
codeWrap: parsed.codeWrap !== false,
brandLogos: parsed.brandLogos === true,
browserNotifications: parsed.browserNotifications === true,
fileEditDisplayMode: normalizeFileEditDisplayMode(parsed.fileEditDisplayMode),
};
} catch {
+17
View File
@@ -49,6 +49,16 @@ export interface TurnUsage {
[key: string]: number | undefined;
}
export type RecoveryStatus = "resuming" | "awaiting_user" | "recovered" | "failed";
export interface RecoveryState {
status: RecoveryStatus;
recovery_id: string;
reason?: string;
attempts?: number;
can_continue?: boolean;
}
export interface UIMessage {
id: string;
role: Role;
@@ -368,6 +378,8 @@ export interface ChatSummary {
modelPreset?: string | null;
/** Unix epoch seconds when this session currently has a turn in flight. */
runStartedAt?: number | null;
/** Durable recovery state that needs attention after an interrupted turn. */
recoveryState?: RecoveryState | null;
workspaceScope?: WorkspaceScopePayload | null;
/** Stable, server-owned @handle for this session. */
handle?: SessionHandle | null;
@@ -1246,6 +1258,7 @@ export type InboundEvent =
event: "attached";
chat_id: string;
temporary?: boolean;
recovery_state?: RecoveryState;
usage?: TurnUsage;
}
| {
@@ -1289,6 +1302,10 @@ export type InboundEvent =
/** Optional structured payload on progress frames (channel-specific). */
agent_ui?: AgentUIBlob;
} & InboundTurnMetadata)
| ({
event: "recovery_state";
chat_id: string;
} & RecoveryState)
| ({
event: "file_edit";
chat_id: string;