feat: keep managed SSH prompts running on Hosts

Managed SSH prompts previously depended on Desktop-owned provider and transcript state, so accepted work could stop or become uncertain when the Desktop, network, or power disappeared. The Agent now owns accepted OpenCode prompt execution, model and tool rounds, cancellation, and the durable semantic transcript, while Desktop remains authoritative for permanent projects, conversations, tasks, and messages.

Recovered work attaches to the original conversation without replay. The project switcher also groups managed SSH projects by saved Host and shows live Agent connectivity beside each group without opening a connection merely to read status. Remote project subtitles now show only the path, and the Agent lock advances to 0.11.14 with Runtime ACP v4.

Release note: 托管 SSH 任务在桌面端、网络或电源中断后可继续在主机上运行,并能回到原会话精确恢复;项目菜单现按主机分组并显示实时连接状态。
This commit is contained in:
mesalogo
2026-08-31 11:04:41 +08:00
parent f6f8d5e053
commit 561de29f00
75 changed files with 13439 additions and 1327 deletions
+52 -2
View File
@@ -8,7 +8,11 @@ import type {
RuntimeConversationCompactResult,
RuntimeNativeSnapshot
} from '../../shared/contracts'
import type { WorkMode } from '../../shared/assistant-contracts'
import type {
ConversationSubagentActivity,
ConversationToolActivity,
WorkMode
} from '../../shared/assistant-contracts'
export type RuntimeApprovalRequest = {
scopeKey: string
@@ -45,11 +49,33 @@ export type RuntimeModelUsageEvent = {
reportedTotalTokens?: number
}
export type RuntimeEvent =
export type RuntimePublicEvent =
| AgentEvent
| RuntimeGeneratedImageEvent
| RuntimeModelUsageEvent
export type RemoteSemanticEventProvenance = {
source: 'remote-semantic-transcript'
bindingId: string
operationId: string
semanticSequence: string
eventIndex: number
}
export type RemoteSemanticRuntimeEvent = RuntimePublicEvent & {
remoteProvenance: RemoteSemanticEventProvenance
}
export type RemoteSemanticCheckpointEvent = {
requestId: string
type: 'remote-semantic-checkpoint'
remoteProvenance: RemoteSemanticEventProvenance
}
export type RuntimeEvent =
| RuntimePublicEvent
| RemoteSemanticCheckpointEvent
export type RuntimeConversationCompactOutcome = {
result: RuntimeConversationCompactResult
usageEvents?: RuntimeModelUsageEvent[]
@@ -79,6 +105,12 @@ export interface AgentRuntime {
answers?: AgentQuestionAnswer[]
): Promise<void>
releaseConversation?(conversationId: string): Promise<void>
/**
* Stop Main-process admission during a normal application exit. Runtimes
* that implement this hook may intentionally leave remote work owned by
* another process running after Desktop disconnects.
*/
detachForApplicationExit?(): void | Promise<void>
dispose(): Promise<void>
}
@@ -88,6 +120,13 @@ export type AgentImage = {
data: string
}
export type RemoteRecoveredTool = ConversationToolActivity & {
callId: string
}
export type RemoteRecoveredSubagent =
ConversationSubagentActivity
export type AgentExecutionRequest = Omit<AgentRequest, 'workMode'> & {
workMode?: WorkMode
images?: AgentImage[]
@@ -95,4 +134,15 @@ export type AgentExecutionRequest = Omit<AgentRequest, 'workMode'> & {
trustedInstructions?: string
/** Main-process-only request-scoped authorization for built-in data tools. */
knowledgeCapabilityToken?: string
/**
* Main-process recovery cursor for an Agent-owned semantic prompt.
* It is never sent as prompt content and is ignored by local runtimes.
*/
remoteSemanticAfterSequence?: string
/** Recovery may attach an accepted Agent prompt but must never replay it. */
remoteRecoveryOnly?: boolean
/** Main-only durable tool state used to merge partial recovery updates. */
remoteRecoveredTools?: readonly RemoteRecoveredTool[]
/** Main-only durable native subagent state for partial recovery updates. */
remoteRecoveredSubagents?: readonly RemoteRecoveredSubagent[]
}