mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
354 lines
13 KiB
TypeScript
354 lines
13 KiB
TypeScript
/** Share child labels/details across sidebar and chat. Prefer host children views; retain subagents/task-roster builders until no supported publisher/paired host requires each legacy shape. Delete each legacy builder when its last publisher is gone. */
|
|
import type { AgentSessionBackgroundTask } from './agent-session-background-task-wire'
|
|
import type { AgentChildWorkKind } from './agent-status-child-work'
|
|
import { resolveAgentChildWorkFreshness } from './agent-status-child-work-freshness'
|
|
import {
|
|
agentChildWorkOwnedLiveness,
|
|
deriveAgentChildDisplayState,
|
|
type AgentChildDisplayState
|
|
} from './agent-status-child-work-display'
|
|
import { agentChildWorkViewOffersStop } from './agent-child-work-stop-targets'
|
|
import type { AgentChildWorkView } from './agent-status-child-work-view'
|
|
import {
|
|
agentStatusAuthorityObservedAt,
|
|
agentStatusEvidenceObservedAt
|
|
} from './agent-status-freshness'
|
|
import type { AgentStatusEntry, AgentSubagentSnapshot } from './agent-status-types'
|
|
|
|
/** What a child row says beside its name. Surfaces format it; they never decide it. */
|
|
export type AgentChildRowDetail =
|
|
/** The tool the child is running now, as `tool: input` — the line a CLI agent row shows. */
|
|
| { kind: 'operation'; toolName: string; input?: string }
|
|
/** Its own work is over and only work it owns still runs; the tool line is stale. */
|
|
| { kind: 'monitoring' }
|
|
| { kind: 'message'; text: string }
|
|
/** Settled, and the lane cannot say how. */
|
|
| { kind: 'ended' }
|
|
/** How long since the last evidence for this child (see `recencyAt`). */
|
|
| { kind: 'no-update' }
|
|
/** The child's role, when nothing more specific is known. */
|
|
| { kind: 'role'; agentType: string }
|
|
/** A host that publishes only a run state: that state's reason word. */
|
|
| { kind: 'reason'; state: 'waiting' | 'blocked' | 'unverifiable' }
|
|
|
|
/** One child row, for every surface that lists child work (sidebar and chat strip). */
|
|
export type AgentChildRowModel = {
|
|
/** Row identity: the host's child id, or the provider id an old host names it by. */
|
|
id: string
|
|
/** The id a targeted stop names; absent when the host holds none. */
|
|
providerId?: string
|
|
kind: AgentChildWorkKind
|
|
displayState: AgentChildDisplayState
|
|
/** '' when the child reported no label; a surface then names the row by its state. */
|
|
name: string
|
|
agentType?: string
|
|
model?: string
|
|
detail: AgentChildRowDetail | null
|
|
/** When the child was first seen: the elapsed anchor. */
|
|
firstObservedAt: number
|
|
/** Last evidence for THIS child. Absent from a host that reports none. */
|
|
observedAt?: number
|
|
/** Reader-clock anchor for no-update age: child evidence, otherwise parent evidence. */
|
|
recencyAt: number
|
|
settledAt?: number
|
|
totalTokens?: number
|
|
canStop: boolean
|
|
settled: boolean
|
|
/** Work this child owns (a nested agent, a shell it launched), in host order. */
|
|
owned: AgentChildRowModel[]
|
|
}
|
|
|
|
export type AgentChildRowContext = {
|
|
/** A stale parent makes every live claim beneath it unverifiable. */
|
|
parentEvidenceFresh: boolean
|
|
transportObservation: 'live' | 'unverifiable'
|
|
/** Parent evidence on the reader clock; fallback recency when the child has no clock. */
|
|
parentObservedAt: number
|
|
/** Reader minus host clock, or zero locally; convert child stamps before computing age. */
|
|
hostClockOffsetMs: number
|
|
}
|
|
|
|
/** Derive every surface's child context from the same parent evidence. */
|
|
export function agentChildRowContextForParent(
|
|
parent: Pick<
|
|
AgentStatusEntry,
|
|
'updatedAt' | 'evidenceObservedAt' | 'mirroredEvidenceReceivedAt' | 'subagentObservation'
|
|
>,
|
|
parentEvidenceFresh: boolean
|
|
): AgentChildRowContext {
|
|
// The parent row decays on this same clock, so a child's silence reads the same as its parent's.
|
|
const parentObservedAt = agentStatusEvidenceObservedAt(parent)
|
|
return {
|
|
parentEvidenceFresh,
|
|
transportObservation: parent.subagentObservation ?? 'live',
|
|
parentObservedAt,
|
|
hostClockOffsetMs: parentObservedAt - agentStatusAuthorityObservedAt(parent)
|
|
}
|
|
}
|
|
|
|
/** Provider strings that carry no identity; the next label wins. */
|
|
const PLACEHOLDER_LABELS = new Set(['unknown', 'untitled', 'task', 'subagent'])
|
|
|
|
/** A provider label that names the child, or undefined for an empty or placeholder one. */
|
|
export function usableAgentChildLabel(value: string | undefined): string | undefined {
|
|
const trimmed = value?.trim()
|
|
return trimmed && !PLACEHOLDER_LABELS.has(trimmed.toLowerCase()) ? trimmed : undefined
|
|
}
|
|
|
|
// A live claim beneath a parent Orca no longer hears from asserts nothing; settled history stands.
|
|
function withFreshness(
|
|
displayState: AgentChildDisplayState,
|
|
context: AgentChildRowContext
|
|
): AgentChildDisplayState {
|
|
if (
|
|
displayState === 'done' ||
|
|
displayState === 'failed' ||
|
|
displayState === 'interrupted' ||
|
|
displayState === 'idle'
|
|
) {
|
|
return displayState
|
|
}
|
|
return resolveAgentChildWorkFreshness({
|
|
state: displayState,
|
|
membership: 'live',
|
|
parentEvidenceFresh: context.parentEvidenceFresh,
|
|
transportObservation: context.transportObservation
|
|
})
|
|
}
|
|
|
|
type AgentChildRowDetailSource = {
|
|
/** `run-state`: the host reported only the child's run state (the legacy task roster). */
|
|
evidence: 'child' | 'run-state'
|
|
kind: AgentChildWorkKind
|
|
name: string
|
|
agentType?: string
|
|
operation?: { toolName: string; input?: string }
|
|
lastMessage?: string
|
|
settled: boolean
|
|
}
|
|
|
|
// A default role or one that restates the name adds nothing.
|
|
function roleDetail(source: AgentChildRowDetailSource): AgentChildRowDetail | null {
|
|
if (source.kind !== 'agent') {
|
|
return null
|
|
}
|
|
const agentType = source.agentType ?? 'unknown'
|
|
return agentType.trim() === 'default' || source.name.trim() === agentType.trim()
|
|
? null
|
|
: { kind: 'role', agentType }
|
|
}
|
|
|
|
function messageOrRole(source: AgentChildRowDetailSource): AgentChildRowDetail | null {
|
|
const text = source.lastMessage?.trim()
|
|
return text ? { kind: 'message', text } : roleDetail(source)
|
|
}
|
|
|
|
/** The one rule for what a child row says, in the order a CLI agent row decides it. */
|
|
function agentChildRowDetail(
|
|
source: AgentChildRowDetailSource,
|
|
displayState: AgentChildDisplayState
|
|
): AgentChildRowDetail | null {
|
|
if (source.evidence === 'run-state') {
|
|
return displayState === 'waiting' ||
|
|
displayState === 'blocked' ||
|
|
displayState === 'unverifiable'
|
|
? { kind: 'reason', state: displayState }
|
|
: null
|
|
}
|
|
switch (displayState) {
|
|
case 'unverifiable':
|
|
return { kind: 'no-update' }
|
|
case 'monitoring':
|
|
// A shell or monitor names itself; only an agent's own tool line goes stale.
|
|
return source.kind === 'agent' ? { kind: 'monitoring' } : null
|
|
case 'working':
|
|
case 'waiting':
|
|
return source.operation
|
|
? {
|
|
kind: 'operation',
|
|
toolName: source.operation.toolName,
|
|
...(source.operation.input !== undefined ? { input: source.operation.input } : {})
|
|
}
|
|
: messageOrRole(source)
|
|
case 'blocked':
|
|
case 'done':
|
|
case 'failed':
|
|
return messageOrRole(source)
|
|
case 'interrupted':
|
|
return roleDetail(source)
|
|
case 'idle':
|
|
return source.settled ? { kind: 'ended' } : roleDetail(source)
|
|
}
|
|
}
|
|
|
|
function viewName(view: AgentChildWorkView): string {
|
|
return (
|
|
usableAgentChildLabel(view.description) ??
|
|
usableAgentChildLabel(view.name) ??
|
|
usableAgentChildLabel(view.agentType) ??
|
|
''
|
|
)
|
|
}
|
|
|
|
function rowFromView(
|
|
view: AgentChildWorkView,
|
|
views: readonly AgentChildWorkView[],
|
|
ownedByOwner: ReadonlyMap<string, AgentChildWorkView[]>,
|
|
context: AgentChildRowContext,
|
|
path: ReadonlySet<string>
|
|
): AgentChildRowModel {
|
|
const displayState = withFreshness(
|
|
deriveAgentChildDisplayState(view, agentChildWorkOwnedLiveness(views, view.id)),
|
|
context
|
|
)
|
|
const settled = view.membership === 'settled'
|
|
const name = viewName(view)
|
|
const nextPath = new Set(path).add(view.id)
|
|
return {
|
|
id: view.id,
|
|
...(view.providerId !== undefined ? { providerId: view.providerId } : {}),
|
|
kind: view.kind,
|
|
displayState,
|
|
name,
|
|
...(view.agentType !== undefined ? { agentType: view.agentType } : {}),
|
|
...(view.model !== undefined ? { model: view.model } : {}),
|
|
detail: agentChildRowDetail(
|
|
{
|
|
evidence: 'child',
|
|
kind: view.kind,
|
|
name,
|
|
agentType: view.agentType,
|
|
operation: view.operation,
|
|
lastMessage: view.lastMessage,
|
|
settled
|
|
},
|
|
displayState
|
|
),
|
|
firstObservedAt: view.firstObservedAt,
|
|
observedAt: view.observedAt,
|
|
recencyAt: view.observedAt + context.hostClockOffsetMs,
|
|
...(view.settledAt !== undefined ? { settledAt: view.settledAt } : {}),
|
|
...(view.totalTokens !== undefined ? { totalTokens: view.totalTokens } : {}),
|
|
canStop: agentChildWorkViewOffersStop(view),
|
|
settled,
|
|
owned: (ownedByOwner.get(view.id) ?? [])
|
|
.filter((owned) => !nextPath.has(owned.id))
|
|
.map((owned) => rowFromView(owned, views, ownedByOwner, context, nextPath))
|
|
}
|
|
}
|
|
|
|
/** Nest host views by ownership; the shared fold keeps finished agents monitoring their live work. */
|
|
export function buildAgentChildRowModels(
|
|
views: readonly AgentChildWorkView[],
|
|
context: AgentChildRowContext
|
|
): AgentChildRowModel[] {
|
|
const ownedByOwner = new Map<string, AgentChildWorkView[]>()
|
|
for (const view of views) {
|
|
if (view.parentChildWorkId !== undefined) {
|
|
ownedByOwner.set(view.parentChildWorkId, [
|
|
...(ownedByOwner.get(view.parentChildWorkId) ?? []),
|
|
view
|
|
])
|
|
}
|
|
}
|
|
return views
|
|
.filter((view) => view.parentChildWorkId === undefined)
|
|
.map((view) => rowFromView(view, views, ownedByOwner, context, new Set()))
|
|
}
|
|
|
|
/** Legacy subagents from older hosts/CLI panes have lifecycle/spawn times but no child evidence clock. */
|
|
export function buildLegacyAgentChildRowModels(
|
|
subagents: readonly AgentSubagentSnapshot[],
|
|
context: AgentChildRowContext
|
|
): AgentChildRowModel[] {
|
|
return subagents.map((subagent) => {
|
|
const displayState = withFreshness(
|
|
deriveAgentChildDisplayState({ state: subagent.state, membership: 'live' }, null),
|
|
context
|
|
)
|
|
const name =
|
|
usableAgentChildLabel(subagent.description) ?? usableAgentChildLabel(subagent.agentType) ?? ''
|
|
return {
|
|
id: subagent.id,
|
|
providerId: subagent.id,
|
|
kind: 'agent',
|
|
displayState,
|
|
name,
|
|
...(subagent.agentType !== undefined ? { agentType: subagent.agentType } : {}),
|
|
...(subagent.model !== undefined ? { model: subagent.model } : {}),
|
|
detail: agentChildRowDetail(
|
|
{ evidence: 'child', kind: 'agent', name, agentType: subagent.agentType, settled: false },
|
|
displayState
|
|
),
|
|
firstObservedAt: subagent.startedAt,
|
|
recencyAt: context.parentObservedAt,
|
|
canStop: false,
|
|
settled: false,
|
|
owned: []
|
|
}
|
|
})
|
|
}
|
|
|
|
function legacyTaskDisplayState(
|
|
task: AgentSessionBackgroundTask,
|
|
settled: boolean
|
|
): AgentChildDisplayState {
|
|
if (task.state) {
|
|
return task.state
|
|
}
|
|
if (settled) {
|
|
return 'done'
|
|
}
|
|
return task.kind === 'monitor' ? 'monitoring' : 'working'
|
|
}
|
|
|
|
// The host decided each row's state; the row keeps it.
|
|
function legacyTaskRow(task: AgentSessionBackgroundTask, settled: boolean): AgentChildRowModel {
|
|
const displayState = legacyTaskDisplayState(task, settled)
|
|
const name = usableAgentChildLabel(task.description) ?? usableAgentChildLabel(task.name) ?? ''
|
|
const startedAt = task.startedAt ?? 0
|
|
return {
|
|
id: task.id,
|
|
providerId: task.id,
|
|
kind: task.kind,
|
|
displayState,
|
|
name,
|
|
detail: agentChildRowDetail(
|
|
{ evidence: 'run-state', kind: task.kind, name, settled },
|
|
displayState
|
|
),
|
|
firstObservedAt: startedAt,
|
|
recencyAt: startedAt,
|
|
...(task.totalTokens !== undefined ? { totalTokens: task.totalTokens } : {}),
|
|
// Absent means stoppable: a host predating the field published only rows its stop could act on.
|
|
canStop: task.stoppable !== false,
|
|
settled,
|
|
owned: []
|
|
}
|
|
}
|
|
|
|
/** Legacy session task rosters are flat, retain host-decided state and have only start times. */
|
|
export function buildLegacyTaskRowModels(
|
|
tasks: readonly AgentSessionBackgroundTask[],
|
|
settledTasks: readonly AgentSessionBackgroundTask[]
|
|
): AgentChildRowModel[] {
|
|
// Older hosts can retain a previous turn beside its resumed live task; the live row wins.
|
|
const rows = new Map<string, AgentChildRowModel>()
|
|
for (const [roster, settled] of [
|
|
[settledTasks, true],
|
|
[tasks, false]
|
|
] as const) {
|
|
for (const task of roster) {
|
|
rows.set(task.id, legacyTaskRow(task, settled))
|
|
}
|
|
}
|
|
return [...rows.values()]
|
|
}
|
|
|
|
/** Every row of a tree, owners before what they own. */
|
|
export function flattenAgentChildRowModels(
|
|
rows: readonly AgentChildRowModel[]
|
|
): AgentChildRowModel[] {
|
|
return rows.flatMap((row) => [row, ...flattenAgentChildRowModels(row.owned)])
|
|
}
|