mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
* fix(agent-status): never drop a running helper for lack of room Helper state (subagents, background tasks and commands) was bounded by counts and byte budgets that refused or evicted live records once hit, so the agent kept working while Orca listed it wrong. Every one of these records already dies with its session, pane or chat, so the count and size caps go; identity-history, transcript and transport bounds stay. * fix(agent-status): keep the status field limit, measured without the subagent roster The previous commit dropped the 256 KiB status check outright; it still guards every other field. Only the roster, whose rows are bounded individually, is left out of the measurement. * fix(agent-status): fit helper previews and walk lineage once * fix(agent-status): skip an oversized Pi row instead of stopping, and parse every envelope the store can write Review follow-up: one long row no longer hides later rows that still fit the request, and the transport envelope's reader no longer refuses a size its writer now produces.
96 lines
4.2 KiB
TypeScript
96 lines
4.2 KiB
TypeScript
// The background-tasks half of the agent-session wire: the per-task row, the
|
|
// roster state that carries it, and the field equality both the client reducer
|
|
// and the host status feed compare with. Split out of `agent-session-wire.ts`
|
|
// when that file reached its line budget; the definitions below are unchanged.
|
|
|
|
import type { AgentChildWorkView } from './agent-status-child-work-view'
|
|
|
|
/** Per-task run state, reusing the agent-state vocabulary the dashboard already
|
|
* renders. Optional on the wire: an old host sends none and clients fall back
|
|
* to kind-derived defaults. */
|
|
export type AgentSessionBackgroundTaskRunState =
|
|
| 'working'
|
|
| 'monitoring'
|
|
| 'waiting'
|
|
| 'blocked'
|
|
| 'done'
|
|
| 'idle'
|
|
| 'unverifiable'
|
|
|
|
export type AgentSessionBackgroundTask = {
|
|
id: string
|
|
kind: 'agent' | 'workflow' | 'command' | 'monitor' | 'unknown'
|
|
description?: string
|
|
/** Provider-reported identity (e.g. a subagent type). `description` stays the
|
|
* display name; this is the fallback when the description is absent. */
|
|
name?: string
|
|
state?: AgentSessionBackgroundTaskRunState
|
|
/** Host epoch ms when the task was first observed, so clients render elapsed. */
|
|
startedAt?: number
|
|
/** Cumulative provider-reported token usage, where the provider supplies it. */
|
|
totalTokens?: number
|
|
/** Whether this row's own stop can act on it. Absent means yes: every host
|
|
* that predates this field published only backgrounded, stoppable rows, and
|
|
* a client that read absence as "not stoppable" would hide a working control
|
|
* on those hosts. A row the host cannot target sends `false`. */
|
|
stoppable?: boolean
|
|
}
|
|
|
|
export type AgentSessionBackgroundTaskState = {
|
|
state: 'monitoring'
|
|
/** Optional so mixed-version clients can consume state-only hosts. */
|
|
tasks?: AgentSessionBackgroundTask[]
|
|
/** Terminal-state siblings of a still-live roster, kept apart from `tasks`
|
|
* so old clients keep rendering exactly the live set they render today. */
|
|
settledTasks?: AgentSessionBackgroundTask[]
|
|
/** Optional so clients only send targeted stops to hosts that accept them. */
|
|
supportsTaskStop?: boolean
|
|
/** Whether an untargeted "stop everything" is available at all. Absent means
|
|
* yes: every host that predates this field accepted one, and a client that
|
|
* read absence as "no stop" would hide a working control on those hosts.
|
|
* A host whose provider exposes no honest stop sends `false`. */
|
|
supportsStopAll?: boolean
|
|
/** The host's child records for this session as views, live and recently settled; `tasks` and
|
|
* `settledTasks` are derived from them. Absent from older hosts. A reader that does not
|
|
* advertise `agent-session.background-task-child-views.v1` never receives a state whose only
|
|
* rows are settled: to it, as before, the roster is live work. */
|
|
children?: AgentChildWorkView[]
|
|
/** Some of this session's running child work has no row (its record was refused, has not landed
|
|
* yet, or a bounded frame dropped it), so what is listed is not all that runs; no row's absence
|
|
* means it ended. A missing child stops counting once a later report of it is admitted or it
|
|
* reports its own end, and the whole flag goes once the provider session ends. */
|
|
incomplete?: true
|
|
}
|
|
|
|
function backgroundTaskFieldsEqual(
|
|
left: AgentSessionBackgroundTask,
|
|
right: AgentSessionBackgroundTask
|
|
): boolean {
|
|
return (
|
|
left.id === right.id &&
|
|
left.kind === right.kind &&
|
|
left.description === right.description &&
|
|
left.name === right.name &&
|
|
left.state === right.state &&
|
|
left.startedAt === right.startedAt &&
|
|
left.totalTokens === right.totalTokens &&
|
|
left.stoppable === right.stoppable
|
|
)
|
|
}
|
|
|
|
/** Field equality for task lists, shared by the client reducer and the host
|
|
* status feed so a publish whose only change is one task's state is never
|
|
* judged equal and dropped. */
|
|
export function agentSessionBackgroundTasksEqual(
|
|
left: AgentSessionBackgroundTask[] | undefined,
|
|
right: AgentSessionBackgroundTask[] | undefined
|
|
): boolean {
|
|
if (left === right) {
|
|
return true
|
|
}
|
|
if (!left || !right || left.length !== right.length) {
|
|
return false
|
|
}
|
|
return left.every((task, index) => backgroundTaskFieldsEqual(task, right[index]))
|
|
}
|