mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 01:47:24 +00:00
* feat(agent-session): keep the permission mode a Claude session reports Claude names its live permission mode on prompt, tool and Stop hooks. Keep the root agent's latest value on the provider session, next to effort, so a resume without a recorded launch can tell whether the session ran in bypass. Ports the capture from #36359 by Nelef. * fix(agent-launch): resume an unrecorded launch bare unless it reported bypass Without a recorded launch, every restart path now resumes with no settings Arguments, env or permission default; the configured Command still runs. A session whose root agent last reported bypassPermissions keeps its bypass flag, so a hand-typed claude --dangerously-skip-permissions keeps it while a bare hand-typed claude never gains one (#29898). Account-switch restart used the shared rule only for Claude; it now uses it for every agent. Thanks to JustinCMR (#37304) for finding the account-switch and cold-restore gaps. * fix(terminal): keep the launch recipe of a window-started agent in main A window spawn hands main its recipe, but main kept only the token and agent, so restarts that main runs (workspace sleep capture, closed-worker reopen) found no recipe and resumed bare. Keep it with the launch authority, the same gate that admits the token. * fix(orchestration): reopen a closed worker with the recipe it launched with A closed worker's pane record goes away with its tab, so orchestration send reopened it bare (#37693). The worker row now keeps the recipe Orca launched the agent with, keeps it when a later capture of the same launch arrives without one (main loses it on a daemon reattach), and a reopen prefers it over a pane record that may hold another launch of the conversation. Adapted from #37757 by Pr1p, without a new column. * fix(terminal): keep a slept pane's earlier recipe only for the same launch A host sleep capture without a recipe reused the pane's earlier record for the same conversation. That is right after a daemon reattach, when Orca's launch still runs but main lost its recipe, and wrong for an agent typed into the shell that resumed the conversation: it never ran that launch. * fix(agent-session): restore a paired pane with the recipe its host recorded A client restoring a pane on a paired host sends only its recorded Arguments, and the host filled the rest from its current settings. A saved restore now replays the host's own record for that pane, or, with none at all, resumes bare and keeps a bypass only when this host's hooks saw it reported. History and other user-chosen resumes keep current settings. * fix(terminal): replay a saved note only for the launch main still holds A note belongs to the agent in a pane only while main holds that launch's token: main admits it at spawn and retires it when the launch command finishes, and a daemon reattach restores neither. Worker, host sleep and hook/quit captures now take a note only from that attested launch, and no longer copy an earlier record's note by agent or conversation match, which a typed resume in the same shell also satisfies. With no attested launch the unrecorded rule applies. Replaces the inferred orcaLaunched / recipeLostPaneKeys flags and drops the mock-only reopen and fixture-echo worker tests. * fix(agent-status): take a pane's note from the launch main attests The window carried a pane's saved note into each new record by agent and conversation match, so an agent typed into the shell that resumed the same conversation inherited an earlier launch's bypass. Main now sends the note of the launch it still holds with each status, and the window records only that, its own launch registration, or, for a restored replay of the very status a record was written from, that record's note. /clear and compact keep the note because main still holds the same launch. * fix(orchestration): reopen a worker with its own recipe or none A worker without its own note took the note of whichever pane record held the same conversation. Its reopen now always passes a complete recipe: its own note, or the unrecorded rule. The unrecorded bypass is also scoped to Claude, whose hooks are the ones that report a root agent's permission mode. * fix(agent-session): restore a paired pane from its source pane, never current settings A saved restore on a paired host looked up the destination pane, which a Wake opens as a new tab, and fell back to the host's current settings whenever the client sent its partial Arguments. The client now names the source pane (to hosts advertising agent-session.restore-source-pane.v1), and the host replays its note for that pane or, with none, resumes bare keeping only a bypass its own hooks saw there. Older hosts keep today's behavior. * fix(terminal): re-arm a launch across a main restart while its agent still runs The daemon outlives main, so it now keeps main's admitted launch (token and note) and drops it when that launch's command finishes. On reattach main re-arms the launch only when the same agent still runs in the foreground: nothing can be typed into a daemon PTY while main is down, so a running agent there is the one the unretired note launched. A shell foreground clears the daemon's note; an unknown read, including every Windows read, declines. Older daemons keep no note, so restore declines there as before. * fix(terminal): the daemon retires a launch note when its command finishes A launch can finish while main is down, so main's own retirement never sees it and a hand-typed agent in that pane would later pass the reattach foreground check. * fix(agent-status): a fresh Claude start never inherits the last process's permission mode A hand-typed resume of the same conversation may run without the bypass the previous process had, so only /clear and compact carry the mode forward. * fix(agent-session): name a restore's source pane in the host's own keys A mirrored pane's key carries the desktop tab prefix, so the host's exact lookup missed its saved note. * fix(terminal): retire a daemon launch note by its token from the one retirement owner A delayed reattach result or a late retire could clear a newer launch's note. Retires now name the token they retire, the reattach checks its PTY before clearing, and re-arming the same launch keeps the daemon's completion scanner. * fix(agent-status): only the launch owner's statement changes a pane's note Main now states the attested note, or null for none, on every hook status. An OSC or display update makes no statement, so after a completed turn it no longer erases an Orca launch's note; a typed resume still drops it through main's null. * fix(terminal): an attach leaves alone a launch main already holds The renderer attaches a pane the CLI just launched before its agent starts, so the attach saw the shell and retired the new launch's daemon note. * fix(agent-status): a paired client keeps a pane's note only on its host owner's word The host's session-tabs frame now says whether the Orca launch it admitted still runs on the published incarnation, and the client applies that fact whichever writer wins the status. With an owner's word the conversation no longer decides; an older host that says nothing keeps the same-conversation rule. * refactor(agent-status): one decision picks a pane's note for every reader The live entry, quit capture, provider-session updates, account restart and cold restore now ask the same owner-aware decision. An owner's none is final and, when it turns, ends the pane's registry entry and saved note, locally and from a paired host's frame. Cold restore and account restart take that answer for a live invocation and read the saved record only when none is live. Inlines single-use wrappers. * refactor(agent-status): a paired retirement test reaches the cold-restore command; trim comments Replaces the projection and continuity tests with one transition: host frame retires the launch, the next OSC keeps no note, and the cold-restore command has no old bypass. * fix(agent-status): an owner's none retires the pane's matched launch registry entry * chore: satisfy changed-code gates (type guards, SAFETY notes, fixed test clock) * fix(agent-launch): a teams launch keeps its note; a legacy leaf id restores without throwing * refactor(daemon): keep the session launch note on the process base so session.ts stays in budget * fix(agent-launch): a restarted terminal main can't vouch for keeps main's earlier launch rule * fix(agent-launch): read the pane's current terminal; a finished launch settles an undecided pane * fix(agent-launch): undecided and held launches end with their process A finished command, a fresh launch or a replacement process settles an undecided pane; a newer attach or finish supersedes a foreground read still in flight; a legacy relay reattach without an incarnation stays undecided; a held launch applies only to the process it was recorded for. * refactor(terminal): watch and arm a created terminal's launch in one call Keeps terminal-create.ts within the 300-line budget after main's growth. * refactor(terminal): keep main's launch evidence in one immutable record Replace the undecided flag, the reattach check map and the re-arm's writes to main's orchestration launch fields with one launchEvidence object (unknown, gone for an incarnation, or held with its recipe), owned by launch-evidence.ts. - A fresh admitted launch is held; any other fresh process is gone. - A finished command or confirmed exit is gone, sticky for its process: no reattach, legacy or noted, can revive it. - A replacement retires its predecessor's evidence without deciding the newcomer, which may still prove its own launch through a reattach. - A reattach note is held only after a foreground read sees its agent; the evidence object fences that read. - A gone launch clears its recipe and blocks both refill sites; a deliberate sleep snapshot keeps its recipe past the launch it captured. - launchToken, launchIncarnationId and launchAgent writes match main. * fix(terminal): keep launch evidence through a first incarnation and sleep snapshots - Learning a process's first incarnation binds its held or gone evidence to it, so a later attach can neither revive a finished launch nor drop a running one; a known X->Y replacement is fenced as before and now also clears the predecessor's recipe. - A status from no Orca launch leaves a sleep snapshot of its session for the wake and never takes that snapshot's recipe into a live or quit record. * fix(terminal): record a runtime split's fresh shell as running no launch * chore: rerun CI on main with the typecheck fix
157 lines
5.4 KiB
TypeScript
157 lines
5.4 KiB
TypeScript
import type { AgentStatusEntry } from './agent-status/contracts/agent-status-types'
|
||
import type { BrowserCertificateFailure, BrowserLoadError } from './browser-workspace-types'
|
||
import type { RuntimeBrowserPlacement } from './runtime-browser-placement'
|
||
import type { TerminalColorOverrides } from './terminal-themes/terminal-color-overrides'
|
||
import type { TerminalLayoutSnapshot, TerminalTab } from './workspace-layout/terminal-tab-types'
|
||
import type { TuiAgent } from './agent-launch/catalog/tui-agent'
|
||
import type { StructuredAgentId } from './agent-session/lifecycle/agent-session-provider-handle'
|
||
|
||
export type RuntimeMobileSessionTerminalTab = {
|
||
type: 'terminal'
|
||
id: string
|
||
title: string
|
||
/** Host manual title; absent on old hosts preserves the client record, whereas null clears it. */
|
||
customTitle?: string | null
|
||
quickCommandLabel?: string | null
|
||
/** Window-to-main names ranked above live titles; generatedTitle is sent only when enabled. */
|
||
aiVaultTitle?: TerminalTab['aiVaultTitle']
|
||
/** The publishing window resolves this surface's session name, so aiVaultTitle (null clears) is authoritative. Headless and older hosts omit it; their saved names are only cache. */
|
||
resolvesSessionTitle?: true
|
||
generatedTitle?: string | null
|
||
parentTabId: string
|
||
leafId: string
|
||
ptyId?: string | null
|
||
/** Host-owned PTY incarnation used to fence remote identity observations. */
|
||
incarnationId?: string | null
|
||
terminalTheme?: RuntimeMobileTerminalTheme
|
||
agentStatus?: AgentStatusEntry | null
|
||
/** Event-only lead-turn end time for paired clients; never persisted in AgentStatusEntry. */
|
||
turnCompletedAt?: number
|
||
launchAgent?: TuiAgent
|
||
/** Whether the host's launch owner attests that the Orca launch it admitted still runs here; older hosts omit it. */
|
||
launchAttested?: boolean
|
||
startupCwd?: string
|
||
parentLayout?: TerminalLayoutSnapshot
|
||
color?: string | null
|
||
isPinned?: boolean
|
||
viewMode?: 'terminal' | 'chat'
|
||
launchDraft?: string
|
||
launchDraftCreatedAt?: number
|
||
isActive: boolean
|
||
}
|
||
|
||
export type RuntimeMobileTerminalTheme = {
|
||
mode: 'dark' | 'light'
|
||
theme: TerminalColorOverrides
|
||
/** Optional desktop contrast override (#10754); absent uses the client’s background-luminance floor. */
|
||
minimumContrastRatio?: number
|
||
}
|
||
|
||
export type RuntimeMobileSessionMarkdownTab = {
|
||
type: 'markdown'
|
||
id: string
|
||
title: string
|
||
filePath: string
|
||
relativePath: string
|
||
language: 'markdown'
|
||
mode: 'edit' | 'markdown-preview'
|
||
isDirty: boolean
|
||
isActive: boolean
|
||
sourceFileId: string
|
||
sourceFilePath: string
|
||
sourceRelativePath: string
|
||
/** Set by a desktop for a "New Markdown" document not yet named; hosts and phones before it omit it. */
|
||
isUntitled?: true
|
||
documentVersion: string
|
||
color?: string | null
|
||
isPinned?: boolean
|
||
/** The paired server the file lives on. Its publisher sets it; runtimes strip it before serving. */
|
||
hostRuntimeEnvironmentId?: string
|
||
}
|
||
|
||
export type RuntimeMobileSessionFileTab = {
|
||
type: 'file'
|
||
id: string
|
||
title: string
|
||
filePath: string
|
||
relativePath: string
|
||
language: string
|
||
mode?: 'edit' | 'diff'
|
||
diffSource?: 'staged' | 'unstaged'
|
||
oldPath?: string
|
||
isDirty: boolean
|
||
color?: string | null
|
||
isPinned?: boolean
|
||
isActive: boolean
|
||
/** The paired server the file lives on. Its publisher sets it; runtimes strip it before serving. */
|
||
hostRuntimeEnvironmentId?: string
|
||
}
|
||
|
||
export type RuntimeMobileSessionBrowserTab = {
|
||
type: 'browser'
|
||
id: string
|
||
title: string
|
||
browserWorkspaceId: string
|
||
browserPageId: string | null
|
||
browserProfileId?: string
|
||
executionHostKey?: string
|
||
placement?: RuntimeBrowserPlacement
|
||
url: string
|
||
loading: boolean
|
||
canGoBack: boolean
|
||
canGoForward: boolean
|
||
/** From `remoteTabFaviconUrl`; null clears it. Old hosts omit it, so clients keep what they have. */
|
||
faviconUrl?: string | null
|
||
loadError?: BrowserLoadError | null
|
||
certificateFailure?: BrowserCertificateFailure | null
|
||
color?: string | null
|
||
isPinned?: boolean
|
||
isActive: boolean
|
||
/** The paired server the page runs on. Its publisher sets it; runtimes strip it before serving. */
|
||
hostRuntimeEnvironmentId?: string
|
||
}
|
||
|
||
export type RuntimeMobileSessionAgentTab = {
|
||
type: 'agent-session'
|
||
id: string
|
||
title: string
|
||
sessionId: string
|
||
replacesSessionId?: string
|
||
/** Host-registered agent; gate names beyond Claude/Codex on client registered-agents capability. */
|
||
agent: StructuredAgentId
|
||
/** The host's structured status row for this chat. Optional: old hosts never send it. */
|
||
chatStatus?: RuntimeMobileSessionAgentTabStatus
|
||
color?: string | null
|
||
isPinned?: boolean
|
||
isActive: boolean
|
||
}
|
||
|
||
export type RuntimeMobileSessionAgentTabStatus = Pick<
|
||
AgentStatusEntry,
|
||
| 'state'
|
||
| 'workingMode'
|
||
| 'interrupted'
|
||
| 'mainAgent'
|
||
| 'updatedAt'
|
||
| 'evidenceObservedAt'
|
||
| 'structuredHostOwned'
|
||
>
|
||
|
||
export type RuntimeMobileSessionSnapshotTab =
|
||
| RuntimeMobileSessionTerminalTab
|
||
| RuntimeMobileSessionMarkdownTab
|
||
| RuntimeMobileSessionFileTab
|
||
| RuntimeMobileSessionBrowserTab
|
||
| RuntimeMobileSessionAgentTab
|
||
|
||
export type RuntimeMobileSessionTerminalClientTab =
|
||
| (RuntimeMobileSessionTerminalTab & { status: 'pending-handle'; terminal: null })
|
||
| (RuntimeMobileSessionTerminalTab & { status: 'ready'; terminal: string })
|
||
|
||
export type RuntimeMobileSessionClientTab =
|
||
| RuntimeMobileSessionTerminalClientTab
|
||
| RuntimeMobileSessionMarkdownTab
|
||
| RuntimeMobileSessionFileTab
|
||
| RuntimeMobileSessionBrowserTab
|
||
| RuntimeMobileSessionAgentTab
|