feat(client): unified tasks view model + graph layout + api methods
- tasks-model.ts: one pre-order node list for graph AND tree modes — catalog walking, workflow-run attachment with member re-parenting and synthesis, team enrichment, per-parent settled-leaf fold aggregates. - tasks-graph-layout.ts: dependency-free tidy-tree layered layout (bundle-purity: no graph library in the core bundle). - api.ts: workflowsList/teamsView/teamsTaskCreate/teamsTaskUpdate with the wire result types mirrored from the host routes. - 11 unit tests across model derivation, edge classification, fold, and layout geometry.
This commit is contained in:
+48
-1
@@ -9,7 +9,16 @@
|
||||
import { encodeHtmlUrl } from '../html-route.ts'
|
||||
import type { LastActivity } from '../subagent-activity.ts'
|
||||
import type { SidechatLiveEvent, SidechatLogEvent, SidechatThreadInfo } from '../sidechat-core.ts'
|
||||
import type { SidebarJobView, SidebarSessionEvent } from '../context-types.ts'
|
||||
import type {
|
||||
SidebarCreateTeamTaskRequest,
|
||||
SidebarJobView,
|
||||
SidebarSessionEvent,
|
||||
SidebarTeamMemberView,
|
||||
SidebarTeamTaskMutationResult,
|
||||
SidebarTeamTaskView,
|
||||
SidebarUpdateTeamTaskRequest,
|
||||
} from '../context-types.ts'
|
||||
import type { WorkflowRunView } from '../workflow-runs.ts'
|
||||
|
||||
/** One wire failure. */
|
||||
export class SidebarApiError extends Error {
|
||||
@@ -113,6 +122,23 @@ export interface JobOutputResult {
|
||||
/** The `subagents.live` response: running child id → latest activity. */
|
||||
export type SubagentLiveResult = { live: Record<string, LastActivity> }
|
||||
|
||||
/** The `workflows.list` response: the tree's folded workflow runs. */
|
||||
export type WorkflowsListResult = { runs: WorkflowRunView[] }
|
||||
|
||||
/** The `teams.view` response (structural degradation contract). */
|
||||
export type TeamsViewResult =
|
||||
| { available: false }
|
||||
| { available: true; team: { members: SidebarTeamMemberView[]; tasks: SidebarTeamTaskView[] } | null }
|
||||
|
||||
/** The `teams.taskCreate` request payload (minus the rootSessionId). */
|
||||
export type TeamsTaskCreateRequest = SidebarCreateTeamTaskRequest
|
||||
|
||||
/** The `teams.taskUpdate` request payload (minus the rootSessionId). */
|
||||
export type TeamsTaskUpdateRequest = SidebarUpdateTeamTaskRequest
|
||||
|
||||
/** The task-mutation result union (CAS conflict stays distinct). */
|
||||
export type TeamsTaskMutationResult = SidebarTeamTaskMutationResult
|
||||
|
||||
/**
|
||||
* Parse one `/sidebar` JSON response envelope into its value. A non-ok
|
||||
* status, an unparseable body, or any shape other than `{ok: true, value}`
|
||||
@@ -353,6 +379,27 @@ export const api = {
|
||||
*/
|
||||
subagentsLive: (rootSessionId: string, signal?: AbortSignal) =>
|
||||
call<SubagentLiveResult>('subagents.live', { rootSessionId }, signal),
|
||||
/**
|
||||
* The workflow runs of the whole tree, folded host-side from the
|
||||
* `tool-workflow/*` session events (the same four types the official
|
||||
* workflow-run panel folds). Empty when the tree never ran a workflow —
|
||||
* absence is the normal case, never an error.
|
||||
*/
|
||||
workflowsList: (rootSessionId: string, signal?: AbortSignal) =>
|
||||
call<WorkflowsListResult>('workflows.list', { rootSessionId }, signal),
|
||||
/**
|
||||
* The team led by the tree's root agent (experimental Agent Teams layer):
|
||||
* `{available:false}` hides the whole block; `{team:null}` means the root
|
||||
* leads no team. Poll-driven (the official panel is manual-refresh too).
|
||||
*/
|
||||
teamsView: (rootSessionId: string, signal?: AbortSignal) =>
|
||||
call<TeamsViewResult>('teams.view', { rootSessionId }, signal),
|
||||
/** Create one shared task on the root-led team. */
|
||||
teamsTaskCreate: (rootSessionId: string, req: TeamsTaskCreateRequest) =>
|
||||
call<TeamsTaskMutationResult>('teams.taskCreate', { rootSessionId, ...req }),
|
||||
/** CAS-mutate one shared task; conflicts ride the returned union. */
|
||||
teamsTaskUpdate: (rootSessionId: string, req: TeamsTaskUpdateRequest) =>
|
||||
call<TeamsTaskMutationResult>('teams.taskUpdate', { rootSessionId, ...req }),
|
||||
/** Create a Side Chat thread: a child session seeded with the parent's
|
||||
* full log up to now. Empty question = immediate create (Codex-style):
|
||||
* the thread opens empty, the first prompt carries the boundary. */
|
||||
|
||||
Reference in New Issue
Block a user