@agimon-ai/doompi-task 0.0.1-alpha.87 → 0.0.1-alpha.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/commands.d.mts +1 -1
  2. package/dist/{delegation-BtzyLnYM.cjs → delegation-3hMdTFXF.cjs} +4 -1
  3. package/dist/delegation-3hMdTFXF.cjs.map +1 -0
  4. package/dist/{delegation-CTFGZtwp.mjs → delegation-CkGbRaOl.mjs} +4 -1
  5. package/dist/delegation-CkGbRaOl.mjs.map +1 -0
  6. package/dist/delegationManager.cjs +1 -1
  7. package/dist/delegationManager.d.mts +1 -1
  8. package/dist/delegationManager.mjs +1 -1
  9. package/dist/extensions/mcp-ui.mjs +23 -0
  10. package/dist/extensions/mcp-ui.mjs.map +1 -0
  11. package/dist/extensions/mcp.cjs +11 -1
  12. package/dist/extensions/mcp.cjs.map +1 -1
  13. package/dist/extensions/mcp.d.mts.map +1 -1
  14. package/dist/extensions/mcp.mjs +11 -1
  15. package/dist/extensions/mcp.mjs.map +1 -1
  16. package/dist/extensions/pi.cjs +6 -5
  17. package/dist/extensions/pi.cjs.map +1 -1
  18. package/dist/extensions/pi.mjs +6 -5
  19. package/dist/extensions/pi.mjs.map +1 -1
  20. package/dist/extensions/server.cjs +1 -1
  21. package/dist/extensions/server.mjs +1 -1
  22. package/dist/{format.cli-CKxfl-lR.cjs → format.cli-ChQsP1MQ.cjs} +2 -2
  23. package/dist/{format.cli-CKxfl-lR.cjs.map → format.cli-ChQsP1MQ.cjs.map} +1 -1
  24. package/dist/{format.cli-CuTlvk8J.mjs → format.cli-Cnc591eb.mjs} +2 -2
  25. package/dist/{format.cli-CuTlvk8J.mjs.map → format.cli-Cnc591eb.mjs.map} +1 -1
  26. package/dist/{index-BUmf786V.d.mts → index-3-3ZJlaH.d.mts} +2 -1
  27. package/dist/index-3-3ZJlaH.d.mts.map +1 -0
  28. package/dist/index.cjs +1 -1
  29. package/dist/index.d.mts +2 -2
  30. package/dist/index.mjs +1 -1
  31. package/dist/{root.cli-BfEKqt8s.d.mts → root.cli-swHZqusx.d.mts} +2 -2
  32. package/dist/{root.cli-BfEKqt8s.d.mts.map → root.cli-swHZqusx.d.mts.map} +1 -1
  33. package/dist/{task-space.cli-BdDqn-_h.cjs → task-space.cli-BH4Iy_7m.cjs} +2 -2
  34. package/dist/{task-space.cli-BdDqn-_h.cjs.map → task-space.cli-BH4Iy_7m.cjs.map} +1 -1
  35. package/dist/{task-space.cli-DowNll0k.mjs → task-space.cli-CMYNjgOW.mjs} +2 -2
  36. package/dist/{task-space.cli-DowNll0k.mjs.map → task-space.cli-CMYNjgOW.mjs.map} +1 -1
  37. package/dist/{taskOverlay-BKiu4cVj.cjs → taskOverlay-B5wJHiml.cjs} +2 -2
  38. package/dist/{taskOverlay-BKiu4cVj.cjs.map → taskOverlay-B5wJHiml.cjs.map} +1 -1
  39. package/dist/{taskOverlay-DVvLdTtf.mjs → taskOverlay-CljDoTj_.mjs} +2 -2
  40. package/dist/{taskOverlay-DVvLdTtf.mjs.map → taskOverlay-CljDoTj_.mjs.map} +1 -1
  41. package/dist/toolTaskTool.d.mts +1 -1
  42. package/dist/tuiFormat.cjs +1 -1
  43. package/dist/tuiFormat.d.mts +1 -1
  44. package/dist/tuiFormat.mjs +1 -1
  45. package/dist/tuiTaskOverlay.cjs +1 -1
  46. package/dist/tuiTaskOverlay.d.mts +1 -1
  47. package/dist/tuiTaskOverlay.mjs +1 -1
  48. package/dist/tuiTaskSpace.cjs +1 -1
  49. package/dist/tuiTaskSpace.mjs +1 -1
  50. package/package.json +15 -8
  51. package/dist/delegation-BtzyLnYM.cjs.map +0 -1
  52. package/dist/delegation-CTFGZtwp.mjs.map +0 -1
  53. package/dist/index-BUmf786V.d.mts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"task-space.cli-DowNll0k.mjs","names":[],"sources":["../src/extensions/workspaces/sessions/(frontend)/overlay/_lib/task-space.cli.ts"],"sourcesContent":["/**\n * Task Space: the `SPC t l` overlay.\n *\n * The task list and detail pane render from plain values so the whole surface\n * can be asserted as text without a live terminal.\n *\n * The overlay reads the store on every render rather than caching rows, so an\n * external write by a delegated child shows up without closing the overlay.\n *\n * Every commit it makes is a single-entry `upsert` that carries the selected\n * task's id. That id is what keeps creating a task a tool-only path: an entry\n * without one would create rather than be rejected.\n */\n\nimport { homedir } from 'node:os';\n\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/doomOverlay';\nimport { fitStyledLine } from '@agimon-ai/doompi-ui/rendering';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nimport type { Task, TaskItemMutation } from '../../../../../../models/task';\nimport { isBlocked } from '../../../../../../models/taskGraph';\nimport { MSG_NO_TASKS, TASK_STATUSES } from '../../../../../../schemas/task';\nimport { applyTaskMutation, isCommittingOp, type Op, singleItemOutcome } from '../../../../../../services/reducer';\nimport type { TaskStore } from '../../../../../../services/taskStore';\nimport { overlayStatusGlyph, STATUS_LABEL } from '../../_shared/format.cli';\nimport { countTasks, visibleTasks } from '.././_lib/selectors';\n\nexport const TASK_SPACE_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nexport interface TaskSpaceOptions {\n store: TaskStore;\n}\n\ntype EditMode = 'browse' | 'status' | 'subject';\n\nconst KEY_BACKSPACE = '\\x7f';\nconst KEY_ENTER = '\\r';\nconst KEY_NEWLINE = '\\n';\nconst FIRST_PRINTABLE_CODE_POINT = 0x20;\nconst DELETE_CODE_POINT = 0x7f;\n\n/**\n * Keystrokes that carry no text: escape sequences, control keys, delete.\n * Indexed rather than iterated so surrogate pairs stay intact; both halves sit\n * far above the control range, so no emoji is ever mistaken for one.\n */\nfunction isControlInput(data: string): boolean {\n for (let index = 0; index < data.length; index++) {\n const code = data.charCodeAt(index);\n if (code < FIRST_PRINTABLE_CODE_POINT || code === DELETE_CODE_POINT) return true;\n }\n return false;\n}\n\n/**\n * Store path for the list heading: home collapses to `~`, and an over-long path\n * loses leading segments rather than its tail, because the file name identifies\n * the store while `/var/folders/...` does not.\n */\nfunction storeLabel(storePath: string, width: number): string {\n const home = homedir();\n const abbreviated = home && storePath.startsWith(home) ? `~${storePath.slice(home.length)}` : storePath;\n if (visibleWidth(abbreviated) <= width) return abbreviated;\n\n const segments = abbreviated.split('/');\n let tail = segments.pop() ?? abbreviated;\n while (segments.length > 0) {\n const candidate = `${segments[segments.length - 1]}/${tail}`;\n if (visibleWidth(`…/${candidate}`) > width) break;\n tail = candidate;\n segments.pop();\n }\n return truncateToWidth(`…/${tail}`, width, '');\n}\n\n/** Neither palette name is exported, so take both from the theme's methods. */\ntype ThemeBg = Parameters<Theme['bg']>[0];\ntype ThemeColor = Parameters<Theme['fg']>[0];\n\n/** Each task occupies a heading row and a metadata row beneath it. */\nconst ROWS_PER_TASK = 2;\n/** Hanging indent for the metadata row: clears marker, glyph and id. */\nconst META_INDENT = 6;\n\nconst MIN_WIDTH = 48;\n/** A third for the list, two thirds for the task being read or edited. */\nconst LIST_PANE_RATIO = 1 / 3;\n/** One blank column each side of the pane divider so neither column touches it. */\nconst PANE_GUTTER = 1;\nconst MIN_PANE_WIDTH = 18;\nconst SELECTION_MARKER = '›';\nconst CURRENT_MARKER = '▸';\nconst CURSOR_BLOCK = '█';\nconst ELLIPSIS = '…';\nconst NO_VALUE = 'none';\n\nconst TITLE = 'TASK SPACE';\nconst BREADCRUMB = 'SPC › t › t / tasks';\n/** Key/label pairs rendered as capped keys, matching the mockup's footer. */\nconst LIST_HINTS: readonly (readonly [string, string])[] = [\n ['↑↓', 'select'],\n ['enter', 'edit subject'],\n ['s', 'status'],\n ['esc', 'close'],\n];\nconst LIST_HEADER_RIGHT = 'esc close';\nconst STATUS_HINT = 'STATUS s cycles · enter commits';\nconst SUBJECT_HINT = 'SUBJECT enter commits · esc cancels';\nfunction fitRow(text: string, width: number): string {\n return fitStyledLine(text, width, ELLIPSIS);\n}\n\nfunction rightAligned(left: string, right: string, width: number): string {\n const rightWidth = visibleWidth(right);\n const leftWidth = Math.max(0, width - rightWidth - 1);\n return fitRow(left, leftWidth) + ' '.repeat(Math.max(1, width - leftWidth - rightWidth)) + fitRow(right, rightWidth);\n}\n\nfunction wrap(text: string, width: number): string[] {\n if (width <= 0) return [];\n const lines: string[] = [];\n let current = '';\n for (const word of text.split(/\\s+/).filter(Boolean)) {\n if (current.length === 0) current = word;\n else if (current.length + 1 + word.length <= width) current += ` ${word}`;\n else {\n lines.push(current);\n current = word;\n }\n }\n if (current.length > 0) lines.push(current);\n return lines;\n}\n\n/**\n * Task Space overlay component.\n *\n * Rendering is pure: `render(width)` reads the live store and returns lines with\n * no side effects, so the fidelity job can capture it and unit tests can assert on it.\n */\nexport class TaskSpaceComponent extends DoomOverlay {\n private readonly options: TaskSpaceOptions;\n private readonly done: (result: undefined) => void;\n private readonly unwatch: () => void;\n\n private mode: EditMode = 'browse';\n private selected = 0;\n private statusIndex = 0;\n private draft = '';\n private notice: string | undefined;\n private bodyHeight = 1;\n\n constructor(tui: DoomOverlayTui, theme: Theme, options: TaskSpaceOptions, done: (result: undefined) => void) {\n super(tui, theme);\n this.options = options;\n this.done = done;\n this.unwatch = options.store.onExternalChange(() => this.tui.requestRender());\n }\n\n private tasks(): Task[] {\n return visibleTasks(this.options.store.snapshot.tasks);\n }\n\n private selectedTask(): Task | undefined {\n const tasks = this.tasks();\n if (tasks.length === 0) return undefined;\n return tasks[Math.min(this.selected, tasks.length - 1)];\n }\n\n private move(delta: number): void {\n const tasks = this.tasks();\n if (tasks.length === 0) return;\n this.selected = Math.max(0, Math.min(tasks.length - 1, this.selected + delta));\n this.tui.requestRender();\n }\n\n /** Commits through the same reducer the task tool uses, so both paths share every invariant. */\n private commit(item: TaskItemMutation): void {\n this.options.store\n .mutate<Op>((document) => {\n const result = applyTaskMutation(document, 'upsert', { tasks: [item] });\n // A rejected mutation must not reach the writer: committing it would\n // bump `rev` for an edit that never happened.\n if (!isCommittingOp(result.op)) return { value: result.op };\n return { document: result.document, value: result.op };\n })\n .then((outcome) => {\n // The reducer's message is unprefixed, so the notice reads\n // \"subject must not be blank\" rather than \"item[0] failed: ...\".\n const single = singleItemOutcome(outcome.value);\n this.notice = single?.kind === 'failed' ? single.message : undefined;\n this.tui.requestRender();\n })\n .catch((error: unknown) => {\n // Shown in the overlay rather than swallowed: a rejected write means the\n // edit the operator just made never landed.\n this.notice = error instanceof Error ? error.message : String(error);\n this.tui.requestRender();\n });\n }\n\n private handleStatusInput(data: string): void {\n const task = this.selectedTask();\n if (matchesKey(data, 'up')) this.statusIndex = Math.max(0, this.statusIndex - 1);\n else if (matchesKey(data, 'down')) this.statusIndex = Math.min(TASK_STATUSES.length - 1, this.statusIndex + 1);\n else if (data === 's') this.statusIndex = (this.statusIndex + 1) % TASK_STATUSES.length;\n else if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.mode = 'browse';\n if (task) this.commit({ id: task.id, status: TASK_STATUSES[this.statusIndex] });\n } else if (matchesKey(data, 'escape')) this.mode = 'browse';\n this.tui.requestRender();\n }\n\n private handleSubjectInput(data: string): void {\n const task = this.selectedTask();\n if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.mode = 'browse';\n if (task) this.commit({ id: task.id, subject: this.draft });\n } else if (matchesKey(data, 'escape')) this.mode = 'browse';\n else if (data === KEY_BACKSPACE) this.draft = this.draft.slice(0, -1);\n else if (!isControlInput(data)) this.draft += data;\n this.tui.requestRender();\n }\n\n handleInput(data: string): void {\n if (this.mode === 'status') {\n this.handleStatusInput(data);\n return;\n }\n if (this.mode === 'subject') {\n this.handleSubjectInput(data);\n return;\n }\n\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'up')) {\n this.move(-1);\n return;\n }\n if (matchesKey(data, 'down')) {\n this.move(1);\n return;\n }\n\n const task = this.selectedTask();\n if (!task) return;\n if (data === 's') {\n this.mode = 'status';\n this.statusIndex = Math.max(0, TASK_STATUSES.indexOf(task.status));\n this.tui.requestRender();\n return;\n }\n if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.mode = 'subject';\n this.draft = task.subject;\n this.tui.requestRender();\n }\n }\n\n /**\n * A padded, background-filled chip. Foreground is applied inside the fill so\n * the colour closes with it, rather than a reset stripping the background\n * partway through the label.\n */\n private badge(text: string, background: ThemeBg, colour: ThemeColor = 'text'): string {\n return this.theme.bg(background, this.theme.fg(colour, ` ${text} `));\n }\n\n /** Footer hints as capped keys: `[ enter ] edit subject`, per the mockup. */\n private hintLine(hints: readonly (readonly [string, string])[]): string {\n return hints\n .map(([key, label]) => `${this.badge(key, 'selectedBg')}${this.theme.fg('dim', ` ${label}`)}`)\n .join(this.theme.fg('dim', ' '));\n }\n\n private listLines(width: number): string[] {\n const tasks = this.tasks();\n const label = this.theme.bold('TASKS');\n const pathWidth = Math.max(0, width - visibleWidth('TASKS') - 2);\n const lines = [`${label} ${this.theme.fg('dim', storeLabel(this.options.store.storePath, pathWidth))}`, ''];\n if (tasks.length === 0) {\n lines.push(this.theme.fg('dim', MSG_NO_TASKS));\n return lines;\n }\n\n // Two rows per task: at a third of the width a single row left the subject\n // with a handful of columns once owner and state took their share.\n const budget = Math.max(1, Math.floor((this.bodyHeight - lines.length) / ROWS_PER_TASK));\n const start = Math.max(0, Math.min(this.selected - budget + 1, Math.max(0, tasks.length - budget)));\n for (const [offset, task] of tasks.slice(start, start + budget).entries()) {\n const index = start + offset;\n const active = index === Math.min(this.selected, tasks.length - 1);\n const marker = active ? this.theme.fg('accent', SELECTION_MARKER) : ' ';\n const subject = active ? this.theme.bold(task.subject) : task.subject;\n const glyph = overlayStatusGlyph(task.status, this.theme);\n const owner = task.owner ?? task.delegation?.agent ?? NO_VALUE;\n const blocked = task.status === 'pending' && isBlocked(this.options.store.snapshot.tasks, task);\n const state = blocked\n ? this.theme.fg('warning', `blocked by ${(task.blockedBy ?? []).map((id) => `#${id}`).join(', ')}`)\n : this.theme.fg('dim', STATUS_LABEL[task.status]);\n // Subject owns the first row outright; the metadata hangs under it.\n const heading = `${marker} ${glyph} ${this.theme.fg('dim', `#${task.id}`)} ${subject}`;\n const meta = `${' '.repeat(META_INDENT)}${this.badge(owner, 'userMessageBg', 'muted')} ${state}`;\n // Every task sits on a band, the selected one a shade lighter, as in the\n // mockup; the marker stays so selection survives themes with no background.\n const background: ThemeBg = active ? 'selectedBg' : 'userMessageBg';\n for (const row of [heading, meta]) {\n lines.push(this.theme.bg(background, fitRow(row, width)));\n }\n }\n return lines;\n }\n\n private statusLines(task: Task, width: number): string[] {\n if (this.mode !== 'status') {\n const glyph = overlayStatusGlyph(task.status, this.theme);\n return [this.theme.fg('dim', 'STATUS'), `${glyph} ${STATUS_LABEL[task.status]}`];\n }\n // Editing draws the picker as a filled panel, so the whole option list reads\n // as one surface rather than five loose rows.\n const lines = [this.theme.fg('dim', STATUS_HINT)];\n for (const [index, status] of TASK_STATUSES.entries()) {\n const selected = index === this.statusIndex;\n const marker = selected ? this.theme.fg('accent', CURRENT_MARKER) : ' ';\n const label = selected ? this.theme.bold(this.theme.fg('accent', status)) : this.theme.fg('muted', status);\n const current = status === task.status ? this.theme.fg('dim', 'current') : '';\n const row = rightAligned(` ${marker} ${label}`, current, width);\n lines.push(this.theme.bg(selected ? 'selectedBg' : 'userMessageBg', row));\n }\n return lines;\n }\n\n /** The subject as an input: a filled field with a caret, as in the mockup. */\n private subjectField(task: Task, width: number): string[] {\n const editing = this.mode === 'subject';\n const value = editing ? `${this.draft}${CURSOR_BLOCK}` : task.subject;\n return [\n this.theme.fg('dim', editing ? SUBJECT_HINT : 'SUBJECT'),\n this.theme.bg(editing ? 'selectedBg' : 'userMessageBg', fitRow(` ${value}`, width)),\n ];\n }\n\n private detailLines(width: number): string[] {\n const task = this.selectedTask();\n if (!task) return [this.theme.fg('dim', 'No task selected')];\n\n const editing = this.mode === 'subject' || this.mode === 'status';\n const badge = editing ? this.badge('EDIT', 'toolSuccessBg', 'warning') : '';\n const lines = [rightAligned(this.theme.bold(this.theme.fg('accent', `TASK #${task.id}`)), badge, width), ''];\n lines.push(...this.subjectField(task, width));\n lines.push('', ...this.statusLines(task, width), '');\n\n if (task.activeForm) lines.push(this.theme.fg('dim', 'ACTIVE FORM'), task.activeForm, '');\n const owner = task.owner ?? task.delegation?.agent ?? NO_VALUE;\n const delegated = task.delegation?.runId ? this.theme.fg('success', `delegated run ${task.delegation.runId}`) : '';\n lines.push(\n this.theme.fg('dim', 'OWNER'),\n delegated ? `${owner} ${this.theme.fg('dim', '·')} ${delegated}` : owner,\n '',\n );\n lines.push(\n this.theme.fg('dim', 'BLOCKED BY'),\n task.blockedBy?.length ? task.blockedBy.map((id) => `#${id}`).join(' · ') : NO_VALUE,\n );\n if (task.description) lines.push('', this.theme.fg('dim', 'DESCRIPTION'), ...wrap(task.description, width));\n if (this.notice) lines.push('', this.theme.fg('error', this.notice));\n return lines;\n }\n\n private headerSummary(): string {\n const counts = countTasks(this.options.store.snapshot.tasks);\n const summary = [`${counts.completed}/${counts.total} completed`];\n if (counts.inProgress > 0) summary.push(`${counts.inProgress} in progress`);\n if (counts.blocked > 0) summary.push(`${counts.blocked} blocked`);\n if (counts.failed > 0) summary.push(`${counts.failed} failed`);\n return summary.join(' · ');\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: BREADCRUMB,\n headerRight: `${this.headerSummary()} · ${LIST_HEADER_RIGHT}`,\n footer: this.hintLine(LIST_HINTS),\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n this.bodyHeight = height;\n if (width < MIN_WIDTH) return this.renderStacked(width, height);\n\n const leftWidth = Math.max(MIN_PANE_WIDTH, Math.floor((width - 1) * LIST_PANE_RATIO));\n const rightWidth = Math.max(1, width - leftWidth - 1);\n // Each column gives up one column to the gutter, so content never abuts the\n // divider: a long store path used to run straight into the detail heading.\n const leftContent = Math.max(1, leftWidth - PANE_GUTTER);\n const rightContent = Math.max(1, rightWidth - PANE_GUTTER);\n const left = this.listLines(leftContent);\n const right = this.detailLines(rightContent);\n\n const divider = this.theme.fg('borderMuted', '│');\n return Array.from({ length: height }, (_, index) => {\n const row = `${fitRow(left[index] ?? '', leftContent)} ${divider} ${fitRow(right[index] ?? '', rightContent)}`;\n return truncateToWidth(row, width, ELLIPSIS);\n });\n }\n\n private renderStacked(width: number, height: number): string[] {\n const topHeight = Math.max(1, Math.floor((height - 1) / 2));\n const bottomHeight = Math.max(0, height - topHeight - 1);\n this.bodyHeight = topHeight;\n const top = this.listLines(width);\n const bottom = this.detailLines(width);\n return [\n ...top.slice(0, topHeight),\n ...Array.from({ length: Math.max(0, topHeight - top.length) }, () => ''),\n this.theme.fg('borderMuted', '─'.repeat(width)),\n ...bottom.slice(0, bottomHeight),\n ];\n }\n\n dispose(): void {\n this.unwatch();\n }\n}\n\nexport async function openTaskSpace(ctx: ExtensionContext, options: TaskSpaceOptions): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new TaskSpaceComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n\nexport default openTaskSpace;\n"],"mappings":"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"}
1
+ {"version":3,"file":"task-space.cli-CMYNjgOW.mjs","names":[],"sources":["../src/extensions/workspaces/sessions/(frontend)/overlay/_lib/task-space.cli.ts"],"sourcesContent":["/**\n * Task Space: the `SPC t l` overlay.\n *\n * The task list and detail pane render from plain values so the whole surface\n * can be asserted as text without a live terminal.\n *\n * The overlay reads the store on every render rather than caching rows, so an\n * external write by a delegated child shows up without closing the overlay.\n *\n * Every commit it makes is a single-entry `upsert` that carries the selected\n * task's id. That id is what keeps creating a task a tool-only path: an entry\n * without one would create rather than be rejected.\n */\n\nimport { homedir } from 'node:os';\n\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from '@agimon-ai/doompi-ui/doomOverlay';\nimport { fitStyledLine } from '@agimon-ai/doompi-ui/rendering';\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey, truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';\n\nimport type { Task, TaskItemMutation } from '../../../../../../models/task';\nimport { isBlocked } from '../../../../../../models/taskGraph';\nimport { MSG_NO_TASKS, TASK_STATUSES } from '../../../../../../schemas/task';\nimport { applyTaskMutation, isCommittingOp, type Op, singleItemOutcome } from '../../../../../../services/reducer';\nimport type { TaskStore } from '../../../../../../services/taskStore';\nimport { overlayStatusGlyph, STATUS_LABEL } from '../../_shared/format.cli';\nimport { countTasks, visibleTasks } from '.././_lib/selectors';\n\nexport const TASK_SPACE_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nexport interface TaskSpaceOptions {\n store: TaskStore;\n}\n\ntype EditMode = 'browse' | 'status' | 'subject';\n\nconst KEY_BACKSPACE = '\\x7f';\nconst KEY_ENTER = '\\r';\nconst KEY_NEWLINE = '\\n';\nconst FIRST_PRINTABLE_CODE_POINT = 0x20;\nconst DELETE_CODE_POINT = 0x7f;\n\n/**\n * Keystrokes that carry no text: escape sequences, control keys, delete.\n * Indexed rather than iterated so surrogate pairs stay intact; both halves sit\n * far above the control range, so no emoji is ever mistaken for one.\n */\nfunction isControlInput(data: string): boolean {\n for (let index = 0; index < data.length; index++) {\n const code = data.charCodeAt(index);\n if (code < FIRST_PRINTABLE_CODE_POINT || code === DELETE_CODE_POINT) return true;\n }\n return false;\n}\n\n/**\n * Store path for the list heading: home collapses to `~`, and an over-long path\n * loses leading segments rather than its tail, because the file name identifies\n * the store while `/var/folders/...` does not.\n */\nfunction storeLabel(storePath: string, width: number): string {\n const home = homedir();\n const abbreviated = home && storePath.startsWith(home) ? `~${storePath.slice(home.length)}` : storePath;\n if (visibleWidth(abbreviated) <= width) return abbreviated;\n\n const segments = abbreviated.split('/');\n let tail = segments.pop() ?? abbreviated;\n while (segments.length > 0) {\n const candidate = `${segments[segments.length - 1]}/${tail}`;\n if (visibleWidth(`…/${candidate}`) > width) break;\n tail = candidate;\n segments.pop();\n }\n return truncateToWidth(`…/${tail}`, width, '');\n}\n\n/** Neither palette name is exported, so take both from the theme's methods. */\ntype ThemeBg = Parameters<Theme['bg']>[0];\ntype ThemeColor = Parameters<Theme['fg']>[0];\n\n/** Each task occupies a heading row and a metadata row beneath it. */\nconst ROWS_PER_TASK = 2;\n/** Hanging indent for the metadata row: clears marker, glyph and id. */\nconst META_INDENT = 6;\n\nconst MIN_WIDTH = 48;\n/** A third for the list, two thirds for the task being read or edited. */\nconst LIST_PANE_RATIO = 1 / 3;\n/** One blank column each side of the pane divider so neither column touches it. */\nconst PANE_GUTTER = 1;\nconst MIN_PANE_WIDTH = 18;\nconst SELECTION_MARKER = '›';\nconst CURRENT_MARKER = '▸';\nconst CURSOR_BLOCK = '█';\nconst ELLIPSIS = '…';\nconst NO_VALUE = 'none';\n\nconst TITLE = 'TASK SPACE';\nconst BREADCRUMB = 'SPC › t › t / tasks';\n/** Key/label pairs rendered as capped keys, matching the mockup's footer. */\nconst LIST_HINTS: readonly (readonly [string, string])[] = [\n ['↑↓', 'select'],\n ['enter', 'edit subject'],\n ['s', 'status'],\n ['esc', 'close'],\n];\nconst LIST_HEADER_RIGHT = 'esc close';\nconst STATUS_HINT = 'STATUS s cycles · enter commits';\nconst SUBJECT_HINT = 'SUBJECT enter commits · esc cancels';\nfunction fitRow(text: string, width: number): string {\n return fitStyledLine(text, width, ELLIPSIS);\n}\n\nfunction rightAligned(left: string, right: string, width: number): string {\n const rightWidth = visibleWidth(right);\n const leftWidth = Math.max(0, width - rightWidth - 1);\n return fitRow(left, leftWidth) + ' '.repeat(Math.max(1, width - leftWidth - rightWidth)) + fitRow(right, rightWidth);\n}\n\nfunction wrap(text: string, width: number): string[] {\n if (width <= 0) return [];\n const lines: string[] = [];\n let current = '';\n for (const word of text.split(/\\s+/).filter(Boolean)) {\n if (current.length === 0) current = word;\n else if (current.length + 1 + word.length <= width) current += ` ${word}`;\n else {\n lines.push(current);\n current = word;\n }\n }\n if (current.length > 0) lines.push(current);\n return lines;\n}\n\n/**\n * Task Space overlay component.\n *\n * Rendering is pure: `render(width)` reads the live store and returns lines with\n * no side effects, so the fidelity job can capture it and unit tests can assert on it.\n */\nexport class TaskSpaceComponent extends DoomOverlay {\n private readonly options: TaskSpaceOptions;\n private readonly done: (result: undefined) => void;\n private readonly unwatch: () => void;\n\n private mode: EditMode = 'browse';\n private selected = 0;\n private statusIndex = 0;\n private draft = '';\n private notice: string | undefined;\n private bodyHeight = 1;\n\n constructor(tui: DoomOverlayTui, theme: Theme, options: TaskSpaceOptions, done: (result: undefined) => void) {\n super(tui, theme);\n this.options = options;\n this.done = done;\n this.unwatch = options.store.onExternalChange(() => this.tui.requestRender());\n }\n\n private tasks(): Task[] {\n return visibleTasks(this.options.store.snapshot.tasks);\n }\n\n private selectedTask(): Task | undefined {\n const tasks = this.tasks();\n if (tasks.length === 0) return undefined;\n return tasks[Math.min(this.selected, tasks.length - 1)];\n }\n\n private move(delta: number): void {\n const tasks = this.tasks();\n if (tasks.length === 0) return;\n this.selected = Math.max(0, Math.min(tasks.length - 1, this.selected + delta));\n this.tui.requestRender();\n }\n\n /** Commits through the same reducer the task tool uses, so both paths share every invariant. */\n private commit(item: TaskItemMutation): void {\n this.options.store\n .mutate<Op>((document) => {\n const result = applyTaskMutation(document, 'upsert', { tasks: [item] });\n // A rejected mutation must not reach the writer: committing it would\n // bump `rev` for an edit that never happened.\n if (!isCommittingOp(result.op)) return { value: result.op };\n return { document: result.document, value: result.op };\n })\n .then((outcome) => {\n // The reducer's message is unprefixed, so the notice reads\n // \"subject must not be blank\" rather than \"item[0] failed: ...\".\n const single = singleItemOutcome(outcome.value);\n this.notice = single?.kind === 'failed' ? single.message : undefined;\n this.tui.requestRender();\n })\n .catch((error: unknown) => {\n // Shown in the overlay rather than swallowed: a rejected write means the\n // edit the operator just made never landed.\n this.notice = error instanceof Error ? error.message : String(error);\n this.tui.requestRender();\n });\n }\n\n private handleStatusInput(data: string): void {\n const task = this.selectedTask();\n if (matchesKey(data, 'up')) this.statusIndex = Math.max(0, this.statusIndex - 1);\n else if (matchesKey(data, 'down')) this.statusIndex = Math.min(TASK_STATUSES.length - 1, this.statusIndex + 1);\n else if (data === 's') this.statusIndex = (this.statusIndex + 1) % TASK_STATUSES.length;\n else if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.mode = 'browse';\n if (task) this.commit({ id: task.id, status: TASK_STATUSES[this.statusIndex] });\n } else if (matchesKey(data, 'escape')) this.mode = 'browse';\n this.tui.requestRender();\n }\n\n private handleSubjectInput(data: string): void {\n const task = this.selectedTask();\n if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.mode = 'browse';\n if (task) this.commit({ id: task.id, subject: this.draft });\n } else if (matchesKey(data, 'escape')) this.mode = 'browse';\n else if (data === KEY_BACKSPACE) this.draft = this.draft.slice(0, -1);\n else if (!isControlInput(data)) this.draft += data;\n this.tui.requestRender();\n }\n\n handleInput(data: string): void {\n if (this.mode === 'status') {\n this.handleStatusInput(data);\n return;\n }\n if (this.mode === 'subject') {\n this.handleSubjectInput(data);\n return;\n }\n\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.done(undefined);\n return;\n }\n if (matchesKey(data, 'up')) {\n this.move(-1);\n return;\n }\n if (matchesKey(data, 'down')) {\n this.move(1);\n return;\n }\n\n const task = this.selectedTask();\n if (!task) return;\n if (data === 's') {\n this.mode = 'status';\n this.statusIndex = Math.max(0, TASK_STATUSES.indexOf(task.status));\n this.tui.requestRender();\n return;\n }\n if (data === KEY_ENTER || data === KEY_NEWLINE) {\n this.mode = 'subject';\n this.draft = task.subject;\n this.tui.requestRender();\n }\n }\n\n /**\n * A padded, background-filled chip. Foreground is applied inside the fill so\n * the colour closes with it, rather than a reset stripping the background\n * partway through the label.\n */\n private badge(text: string, background: ThemeBg, colour: ThemeColor = 'text'): string {\n return this.theme.bg(background, this.theme.fg(colour, ` ${text} `));\n }\n\n /** Footer hints as capped keys: `[ enter ] edit subject`, per the mockup. */\n private hintLine(hints: readonly (readonly [string, string])[]): string {\n return hints\n .map(([key, label]) => `${this.badge(key, 'selectedBg')}${this.theme.fg('dim', ` ${label}`)}`)\n .join(this.theme.fg('dim', ' '));\n }\n\n private listLines(width: number): string[] {\n const tasks = this.tasks();\n const label = this.theme.bold('TASKS');\n const pathWidth = Math.max(0, width - visibleWidth('TASKS') - 2);\n const lines = [`${label} ${this.theme.fg('dim', storeLabel(this.options.store.storePath, pathWidth))}`, ''];\n if (tasks.length === 0) {\n lines.push(this.theme.fg('dim', MSG_NO_TASKS));\n return lines;\n }\n\n // Two rows per task: at a third of the width a single row left the subject\n // with a handful of columns once owner and state took their share.\n const budget = Math.max(1, Math.floor((this.bodyHeight - lines.length) / ROWS_PER_TASK));\n const start = Math.max(0, Math.min(this.selected - budget + 1, Math.max(0, tasks.length - budget)));\n for (const [offset, task] of tasks.slice(start, start + budget).entries()) {\n const index = start + offset;\n const active = index === Math.min(this.selected, tasks.length - 1);\n const marker = active ? this.theme.fg('accent', SELECTION_MARKER) : ' ';\n const subject = active ? this.theme.bold(task.subject) : task.subject;\n const glyph = overlayStatusGlyph(task.status, this.theme);\n const owner = task.owner ?? task.delegation?.agent ?? NO_VALUE;\n const blocked = task.status === 'pending' && isBlocked(this.options.store.snapshot.tasks, task);\n const state = blocked\n ? this.theme.fg('warning', `blocked by ${(task.blockedBy ?? []).map((id) => `#${id}`).join(', ')}`)\n : this.theme.fg('dim', STATUS_LABEL[task.status]);\n // Subject owns the first row outright; the metadata hangs under it.\n const heading = `${marker} ${glyph} ${this.theme.fg('dim', `#${task.id}`)} ${subject}`;\n const meta = `${' '.repeat(META_INDENT)}${this.badge(owner, 'userMessageBg', 'muted')} ${state}`;\n // Every task sits on a band, the selected one a shade lighter, as in the\n // mockup; the marker stays so selection survives themes with no background.\n const background: ThemeBg = active ? 'selectedBg' : 'userMessageBg';\n for (const row of [heading, meta]) {\n lines.push(this.theme.bg(background, fitRow(row, width)));\n }\n }\n return lines;\n }\n\n private statusLines(task: Task, width: number): string[] {\n if (this.mode !== 'status') {\n const glyph = overlayStatusGlyph(task.status, this.theme);\n return [this.theme.fg('dim', 'STATUS'), `${glyph} ${STATUS_LABEL[task.status]}`];\n }\n // Editing draws the picker as a filled panel, so the whole option list reads\n // as one surface rather than five loose rows.\n const lines = [this.theme.fg('dim', STATUS_HINT)];\n for (const [index, status] of TASK_STATUSES.entries()) {\n const selected = index === this.statusIndex;\n const marker = selected ? this.theme.fg('accent', CURRENT_MARKER) : ' ';\n const label = selected ? this.theme.bold(this.theme.fg('accent', status)) : this.theme.fg('muted', status);\n const current = status === task.status ? this.theme.fg('dim', 'current') : '';\n const row = rightAligned(` ${marker} ${label}`, current, width);\n lines.push(this.theme.bg(selected ? 'selectedBg' : 'userMessageBg', row));\n }\n return lines;\n }\n\n /** The subject as an input: a filled field with a caret, as in the mockup. */\n private subjectField(task: Task, width: number): string[] {\n const editing = this.mode === 'subject';\n const value = editing ? `${this.draft}${CURSOR_BLOCK}` : task.subject;\n return [\n this.theme.fg('dim', editing ? SUBJECT_HINT : 'SUBJECT'),\n this.theme.bg(editing ? 'selectedBg' : 'userMessageBg', fitRow(` ${value}`, width)),\n ];\n }\n\n private detailLines(width: number): string[] {\n const task = this.selectedTask();\n if (!task) return [this.theme.fg('dim', 'No task selected')];\n\n const editing = this.mode === 'subject' || this.mode === 'status';\n const badge = editing ? this.badge('EDIT', 'toolSuccessBg', 'warning') : '';\n const lines = [rightAligned(this.theme.bold(this.theme.fg('accent', `TASK #${task.id}`)), badge, width), ''];\n lines.push(...this.subjectField(task, width));\n lines.push('', ...this.statusLines(task, width), '');\n\n if (task.activeForm) lines.push(this.theme.fg('dim', 'ACTIVE FORM'), task.activeForm, '');\n const owner = task.owner ?? task.delegation?.agent ?? NO_VALUE;\n const delegated = task.delegation?.runId ? this.theme.fg('success', `delegated run ${task.delegation.runId}`) : '';\n lines.push(\n this.theme.fg('dim', 'OWNER'),\n delegated ? `${owner} ${this.theme.fg('dim', '·')} ${delegated}` : owner,\n '',\n );\n lines.push(\n this.theme.fg('dim', 'BLOCKED BY'),\n task.blockedBy?.length ? task.blockedBy.map((id) => `#${id}`).join(' · ') : NO_VALUE,\n );\n if (task.description) lines.push('', this.theme.fg('dim', 'DESCRIPTION'), ...wrap(task.description, width));\n if (this.notice) lines.push('', this.theme.fg('error', this.notice));\n return lines;\n }\n\n private headerSummary(): string {\n const counts = countTasks(this.options.store.snapshot.tasks);\n const summary = [`${counts.completed}/${counts.total} completed`];\n if (counts.inProgress > 0) summary.push(`${counts.inProgress} in progress`);\n if (counts.blocked > 0) summary.push(`${counts.blocked} blocked`);\n if (counts.failed > 0) summary.push(`${counts.failed} failed`);\n return summary.join(' · ');\n }\n\n protected getChrome(): DoomOverlayChrome {\n return {\n title: TITLE,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: BREADCRUMB,\n headerRight: `${this.headerSummary()} · ${LIST_HEADER_RIGHT}`,\n footer: this.hintLine(LIST_HINTS),\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n this.bodyHeight = height;\n if (width < MIN_WIDTH) return this.renderStacked(width, height);\n\n const leftWidth = Math.max(MIN_PANE_WIDTH, Math.floor((width - 1) * LIST_PANE_RATIO));\n const rightWidth = Math.max(1, width - leftWidth - 1);\n // Each column gives up one column to the gutter, so content never abuts the\n // divider: a long store path used to run straight into the detail heading.\n const leftContent = Math.max(1, leftWidth - PANE_GUTTER);\n const rightContent = Math.max(1, rightWidth - PANE_GUTTER);\n const left = this.listLines(leftContent);\n const right = this.detailLines(rightContent);\n\n const divider = this.theme.fg('borderMuted', '│');\n return Array.from({ length: height }, (_, index) => {\n const row = `${fitRow(left[index] ?? '', leftContent)} ${divider} ${fitRow(right[index] ?? '', rightContent)}`;\n return truncateToWidth(row, width, ELLIPSIS);\n });\n }\n\n private renderStacked(width: number, height: number): string[] {\n const topHeight = Math.max(1, Math.floor((height - 1) / 2));\n const bottomHeight = Math.max(0, height - topHeight - 1);\n this.bodyHeight = topHeight;\n const top = this.listLines(width);\n const bottom = this.detailLines(width);\n return [\n ...top.slice(0, topHeight),\n ...Array.from({ length: Math.max(0, topHeight - top.length) }, () => ''),\n this.theme.fg('borderMuted', '─'.repeat(width)),\n ...bottom.slice(0, bottomHeight),\n ];\n }\n\n dispose(): void {\n this.unwatch();\n }\n}\n\nexport async function openTaskSpace(ctx: ExtensionContext, options: TaskSpaceOptions): Promise<void> {\n await ctx.ui.custom<undefined>(\n (tui, theme, _keybindings, done) => new TaskSpaceComponent(tui, theme, options, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n\nexport default openTaskSpace;\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  const require_config = require("./config-BPNE_wEU.cjs");
2
2
  const require_selectors = require("./selectors-b-69vb4W.cjs");
3
- const require_format_cli = require("./format.cli-CKxfl-lR.cjs");
3
+ const require_format_cli = require("./format.cli-ChQsP1MQ.cjs");
4
4
  let _agimon_ai_doompi_ui_theme = require("@agimon-ai/doompi-ui/theme");
5
5
  let _earendil_works_pi_tui = require("@earendil-works/pi-tui");
6
6
  //#region src/extensions/workspaces/sessions/(frontend)/overlay/_lib/taskOverlay.ts
@@ -172,4 +172,4 @@ Object.defineProperty(exports, "registerTaskCollapseShortcut", {
172
172
  }
173
173
  });
174
174
 
175
- //# sourceMappingURL=taskOverlay-BKiu4cVj.cjs.map
175
+ //# sourceMappingURL=taskOverlay-B5wJHiml.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"taskOverlay-BKiu4cVj.cjs","names":["resolveCollapseKey","visibleTasks","deriveTaskProjection","truncateToWidth","selectOverlayLayoutFromProjection","getMaxWidgetLines","allocateAgentIdentityColor","formatOverlayTaskLine"],"sources":["../src/extensions/workspaces/sessions/(frontend)/overlay/_lib/taskOverlay.ts"],"sourcesContent":["import { allocateAgentIdentityColor } from '@agimon-ai/doompi-ui/theme';\nimport type { ExtensionAPI, ExtensionContext, ExtensionUIContext, Theme } from '@earendil-works/pi-coding-agent';\nimport type { KeyId } from '@earendil-works/pi-tui';\nimport { type TUI, truncateToWidth } from '@earendil-works/pi-tui';\n\nimport type { Task } from '../../../../../../models/task';\nimport { COLLAPSE_KEY_OFF, getMaxWidgetLines, resolveCollapseKey } from '../../../../../../services/config';\nimport type { DelegationManager } from '../../../../../../services/delegation';\nimport { formatOverlayTaskLine } from '../../_shared/format.cli';\nimport { deriveTaskProjection, selectOverlayLayoutFromProjection, visibleTasks } from './selectors';\n\nconst WIDGET_KEY = 'doom-tasks';\nconst OVERLAY_HEADING = 'Tasks';\nconst PROGRESS_TICK_MS = 1000;\n\nexport interface TaskOverlayOptions {\n getTasks: () => readonly Task[];\n delegation: DelegationManager;\n}\n\n/** The widget owns its collapse key, while the session root owns its lifetime. */\nexport function registerTaskCollapseShortcut(\n pi: Pick<ExtensionAPI, 'registerShortcut'>,\n overlay: TaskOverlay,\n waitUntilReady: (context: ExtensionContext) => Promise<void>,\n isActive: () => boolean,\n): void {\n const key = resolveCollapseKey();\n if (key === COLLAPSE_KEY_OFF) return;\n pi.registerShortcut(key as KeyId, {\n description: 'Collapse or expand the task overlay',\n handler: async (context) => {\n if (!isActive() || !context.hasUI) return;\n await waitUntilReady(context);\n if (!isActive() || !overlay.isRegistered()) return;\n overlay.toggleCollapse();\n },\n });\n}\n\n/**\n * Persistent widget listing tasks above the editor.\n *\n * Registered once per UI context and refreshed with `requestRender()`; it\n * auto-hides when there is nothing to show so an idle session keeps a clean\n * prompt.\n */\nexport class TaskOverlay {\n private readonly options: TaskOverlayOptions;\n private uiCtx: ExtensionUIContext | undefined;\n private widgetRegistered = false;\n private tui: TUI | undefined;\n private progressTimer: NodeJS.Timeout | undefined;\n private collapsed = false;\n\n constructor(options: TaskOverlayOptions) {\n this.options = options;\n }\n\n /** Identity-compare so repeated session_start handlers stay idempotent. */\n setUICtx(ctx: ExtensionUIContext): void {\n if (ctx === this.uiCtx) return;\n if (this.uiCtx && this.widgetRegistered) this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.stopProgressTimer();\n this.uiCtx = ctx;\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n\n update(): void {\n if (!this.uiCtx) {\n this.stopProgressTimer();\n return;\n }\n const tasks = visibleTasks(this.options.getTasks());\n\n if (tasks.length === 0) {\n if (this.widgetRegistered) {\n this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n this.stopProgressTimer();\n return;\n }\n\n if (this.widgetRegistered) {\n this.syncProgressTimer(tasks);\n this.tui?.requestRender();\n return;\n }\n\n this.uiCtx.setWidget(\n WIDGET_KEY,\n (tui, factoryTheme) => {\n this.tui = tui;\n return {\n render: (width: number) => this.renderWidget(this.uiCtx?.theme ?? factoryTheme, width),\n invalidate: () => {\n // Nothing is cached: the next render reads the live theme and store.\n },\n };\n },\n { placement: 'aboveEditor' },\n );\n this.widgetRegistered = true;\n this.syncProgressTimer(tasks);\n }\n\n private syncProgressTimer(tasks: readonly Task[]): void {\n const hasRunningDelegation = tasks.some(\n (task) => task.status !== 'deleted' && task.delegation?.state === 'running',\n );\n if (this.collapsed || !this.widgetRegistered || !hasRunningDelegation) {\n this.stopProgressTimer();\n return;\n }\n if (this.progressTimer) return;\n this.progressTimer = setInterval(() => this.tui?.requestRender(), PROGRESS_TICK_MS);\n this.progressTimer.unref?.();\n }\n\n private stopProgressTimer(): void {\n if (this.progressTimer) clearInterval(this.progressTimer);\n this.progressTimer = undefined;\n }\n\n toggleCollapse(): void {\n this.collapsed = !this.collapsed;\n this.syncProgressTimer(this.options.getTasks());\n // Forced redraw: collapsing changes the widget height, not just its content.\n this.tui?.requestRender(true);\n }\n\n isRegistered(): boolean {\n return this.widgetRegistered;\n }\n\n private renderWidget(theme: Theme, width: number): string[] {\n const projection = deriveTaskProjection(this.options.getTasks());\n if (projection.visible.length === 0) return [];\n\n const truncate = (line: string): string => truncateToWidth(line, width, '…');\n const headingColor = projection.hasActiveWork ? 'accent' : 'dim';\n const headingIcon = projection.hasActiveWork ? '●' : '○';\n let headingText = `${OVERLAY_HEADING} (${projection.counts.completed}/${projection.counts.total})`;\n if (projection.counts.failed > 0) headingText += ` · ${projection.counts.failed} failed`;\n const heading = truncate(`${theme.fg(headingColor, headingIcon)} ${theme.fg(headingColor, headingText)}`);\n\n if (this.collapsed) {\n const key = resolveCollapseKey();\n const hint = key === COLLAPSE_KEY_OFF ? 'collapsed' : `${key} to expand`;\n return this.withTrailingSpacer([heading, truncate(`${theme.fg('dim', '└─')} ${theme.fg('dim', hint)}`)]);\n }\n\n const layout = selectOverlayLayoutFromProjection(projection, getMaxWidgetLines() - 1);\n const occupiedColors = new Set<ReturnType<typeof allocateAgentIdentityColor>>();\n const agentColors = new Map<string, ReturnType<typeof allocateAgentIdentityColor>>();\n for (const task of layout.visible) {\n if (task.delegation?.state !== 'running') continue;\n const identity = task.delegation.runId ?? task.delegation.requestId;\n const color = allocateAgentIdentityColor(identity, occupiedColors);\n occupiedColors.add(color);\n agentColors.set(identity, color);\n }\n\n const nowMs = Date.now();\n const lines = [heading];\n for (const task of layout.visible) {\n const progress = this.options.delegation.progressFor(task);\n const identity = task.delegation?.runId ?? task.delegation?.requestId;\n lines.push(\n truncate(\n `${theme.fg('dim', '├─')} ${formatOverlayTaskLine(\n task,\n theme,\n projection.showIds,\n progress,\n nowMs,\n identity ? agentColors.get(identity) : undefined,\n )}`,\n ),\n );\n }\n\n const hidden = layout.hiddenCompleted + layout.truncatedTail;\n if (hidden === 0) {\n const last = lines.length - 1;\n lines[last] = lines[last].replace('├─', '└─');\n return this.withTrailingSpacer(lines);\n }\n\n const parts: string[] = [];\n if (layout.hiddenCompleted > 0) parts.push(`${layout.hiddenCompleted} completed`);\n if (layout.truncatedTail > 0) parts.push(`${layout.truncatedTail} pending`);\n const summary = parts.length > 0 ? `+${hidden} more (${parts.join(', ')})` : `+${hidden} more`;\n lines.push(truncate(`${theme.fg('dim', '└─')} ${theme.fg('dim', summary)}`));\n return this.withTrailingSpacer(lines);\n }\n\n /** Pi adds a spacer above the widget but none below, which glues the last row to the input box. */\n private withTrailingSpacer(lines: string[]): string[] {\n if (lines.length === 0) return lines;\n lines.push('');\n return lines;\n }\n\n dispose(): void {\n this.stopProgressTimer();\n if (this.uiCtx) this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.widgetRegistered = false;\n this.tui = undefined;\n this.uiCtx = undefined;\n this.collapsed = false;\n }\n}\n"],"mappings":";;;;;;AAWA,MAAM,aAAa;AACnB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;;AAQzB,SAAgB,6BACd,IACA,SACA,gBACA,UACM;CACN,MAAM,MAAMA,eAAAA,mBAAmB;CAC/B,IAAI,QAAA,OAA0B;CAC9B,GAAG,iBAAiB,KAAc;EAChC,aAAa;EACb,SAAS,OAAO,YAAY;GAC1B,IAAI,CAAC,SAAS,KAAK,CAAC,QAAQ,OAAO;GACnC,MAAM,eAAe,OAAO;GAC5B,IAAI,CAAC,SAAS,KAAK,CAAC,QAAQ,aAAa,GAAG;GAC5C,QAAQ,eAAe;EACzB;CACF,CAAC;AACH;;;;;;;;AASA,IAAa,cAAb,MAAyB;CACvB;CACA;CACA,mBAA2B;CAC3B;CACA;CACA,YAAoB;CAEpB,YAAY,SAA6B;EACvC,KAAK,UAAU;CACjB;;CAGA,SAAS,KAA+B;EACtC,IAAI,QAAQ,KAAK,OAAO;EACxB,IAAI,KAAK,SAAS,KAAK,kBAAkB,KAAK,MAAM,UAAU,YAAY,KAAA,CAAS;EACnF,KAAK,kBAAkB;EACvB,KAAK,QAAQ;EACb,KAAK,mBAAmB;EACxB,KAAK,MAAM,KAAA;CACb;CAEA,SAAe;EACb,IAAI,CAAC,KAAK,OAAO;GACf,KAAK,kBAAkB;GACvB;EACF;EACA,MAAM,QAAQC,kBAAAA,aAAa,KAAK,QAAQ,SAAS,CAAC;EAElD,IAAI,MAAM,WAAW,GAAG;GACtB,IAAI,KAAK,kBAAkB;IACzB,KAAK,MAAM,UAAU,YAAY,KAAA,CAAS;IAC1C,KAAK,mBAAmB;IACxB,KAAK,MAAM,KAAA;GACb;GACA,KAAK,kBAAkB;GACvB;EACF;EAEA,IAAI,KAAK,kBAAkB;GACzB,KAAK,kBAAkB,KAAK;GAC5B,KAAK,KAAK,cAAc;GACxB;EACF;EAEA,KAAK,MAAM,UACT,aACC,KAAK,iBAAiB;GACrB,KAAK,MAAM;GACX,OAAO;IACL,SAAS,UAAkB,KAAK,aAAa,KAAK,OAAO,SAAS,cAAc,KAAK;IACrF,kBAAkB,CAElB;GACF;EACF,GACA,EAAE,WAAW,cAAc,CAC7B;EACA,KAAK,mBAAmB;EACxB,KAAK,kBAAkB,KAAK;CAC9B;CAEA,kBAA0B,OAA8B;EACtD,MAAM,uBAAuB,MAAM,MAChC,SAAS,KAAK,WAAW,aAAa,KAAK,YAAY,UAAU,SACpE;EACA,IAAI,KAAK,aAAa,CAAC,KAAK,oBAAoB,CAAC,sBAAsB;GACrE,KAAK,kBAAkB;GACvB;EACF;EACA,IAAI,KAAK,eAAe;EACxB,KAAK,gBAAgB,kBAAkB,KAAK,KAAK,cAAc,GAAG,gBAAgB;EAClF,KAAK,cAAc,QAAQ;CAC7B;CAEA,oBAAkC;EAChC,IAAI,KAAK,eAAe,cAAc,KAAK,aAAa;EACxD,KAAK,gBAAgB,KAAA;CACvB;CAEA,iBAAuB;EACrB,KAAK,YAAY,CAAC,KAAK;EACvB,KAAK,kBAAkB,KAAK,QAAQ,SAAS,CAAC;EAE9C,KAAK,KAAK,cAAc,IAAI;CAC9B;CAEA,eAAwB;EACtB,OAAO,KAAK;CACd;CAEA,aAAqB,OAAc,OAAyB;EAC1D,MAAM,aAAaC,kBAAAA,qBAAqB,KAAK,QAAQ,SAAS,CAAC;EAC/D,IAAI,WAAW,QAAQ,WAAW,GAAG,OAAO,CAAC;EAE7C,MAAM,YAAY,UAAA,GAAyBC,uBAAAA,gBAAAA,CAAgB,MAAM,OAAO,GAAG;EAC3E,MAAM,eAAe,WAAW,gBAAgB,WAAW;EAC3D,MAAM,cAAc,WAAW,gBAAgB,MAAM;EACrD,IAAI,cAAc,GAAG,gBAAgB,IAAI,WAAW,OAAO,UAAU,GAAG,WAAW,OAAO,MAAM;EAChG,IAAI,WAAW,OAAO,SAAS,GAAG,eAAe,MAAM,WAAW,OAAO,OAAO;EAChF,MAAM,UAAU,SAAS,GAAG,MAAM,GAAG,cAAc,WAAW,EAAE,GAAG,MAAM,GAAG,cAAc,WAAW,GAAG;EAExG,IAAI,KAAK,WAAW;GAClB,MAAM,MAAMH,eAAAA,mBAAmB;GAC/B,MAAM,OAAO,QAAA,QAA2B,cAAc,GAAG,IAAI;GAC7D,OAAO,KAAK,mBAAmB,CAAC,SAAS,SAAS,GAAG,MAAM,GAAG,OAAO,IAAI,EAAE,GAAG,MAAM,GAAG,OAAO,IAAI,GAAG,CAAC,CAAC;EACzG;EAEA,MAAM,SAASI,kBAAAA,kCAAkC,YAAYC,eAAAA,kBAAkB,IAAI,CAAC;EACpF,MAAM,iCAAiB,IAAI,IAAmD;EAC9E,MAAM,8BAAc,IAAI,IAA2D;EACnF,KAAK,MAAM,QAAQ,OAAO,SAAS;GACjC,IAAI,KAAK,YAAY,UAAU,WAAW;GAC1C,MAAM,WAAW,KAAK,WAAW,SAAS,KAAK,WAAW;GAC1D,MAAM,SAAA,GAAQC,2BAAAA,2BAAAA,CAA2B,UAAU,cAAc;GACjE,eAAe,IAAI,KAAK;GACxB,YAAY,IAAI,UAAU,KAAK;EACjC;EAEA,MAAM,QAAQ,KAAK,IAAI;EACvB,MAAM,QAAQ,CAAC,OAAO;EACtB,KAAK,MAAM,QAAQ,OAAO,SAAS;GACjC,MAAM,WAAW,KAAK,QAAQ,WAAW,YAAY,IAAI;GACzD,MAAM,WAAW,KAAK,YAAY,SAAS,KAAK,YAAY;GAC5D,MAAM,KACJ,SACE,GAAG,MAAM,GAAG,OAAO,IAAI,EAAE,GAAGC,mBAAAA,sBAC1B,MACA,OACA,WAAW,SACX,UACA,OACA,WAAW,YAAY,IAAI,QAAQ,IAAI,KAAA,CACzC,GACF,CACF;EACF;EAEA,MAAM,SAAS,OAAO,kBAAkB,OAAO;EAC/C,IAAI,WAAW,GAAG;GAChB,MAAM,OAAO,MAAM,SAAS;GAC5B,MAAM,QAAQ,MAAM,KAAK,CAAC,QAAQ,MAAM,IAAI;GAC5C,OAAO,KAAK,mBAAmB,KAAK;EACtC;EAEA,MAAM,QAAkB,CAAC;EACzB,IAAI,OAAO,kBAAkB,GAAG,MAAM,KAAK,GAAG,OAAO,gBAAgB,WAAW;EAChF,IAAI,OAAO,gBAAgB,GAAG,MAAM,KAAK,GAAG,OAAO,cAAc,SAAS;EAC1E,MAAM,UAAU,MAAM,SAAS,IAAI,IAAI,OAAO,SAAS,MAAM,KAAK,IAAI,EAAE,KAAK,IAAI,OAAO;EACxF,MAAM,KAAK,SAAS,GAAG,MAAM,GAAG,OAAO,IAAI,EAAE,GAAG,MAAM,GAAG,OAAO,OAAO,GAAG,CAAC;EAC3E,OAAO,KAAK,mBAAmB,KAAK;CACtC;;CAGA,mBAA2B,OAA2B;EACpD,IAAI,MAAM,WAAW,GAAG,OAAO;EAC/B,MAAM,KAAK,EAAE;EACb,OAAO;CACT;CAEA,UAAgB;EACd,KAAK,kBAAkB;EACvB,IAAI,KAAK,OAAO,KAAK,MAAM,UAAU,YAAY,KAAA,CAAS;EAC1D,KAAK,mBAAmB;EACxB,KAAK,MAAM,KAAA;EACX,KAAK,QAAQ,KAAA;EACb,KAAK,YAAY;CACnB;AACF"}
1
+ {"version":3,"file":"taskOverlay-B5wJHiml.cjs","names":["resolveCollapseKey","visibleTasks","deriveTaskProjection","truncateToWidth","selectOverlayLayoutFromProjection","getMaxWidgetLines","allocateAgentIdentityColor","formatOverlayTaskLine"],"sources":["../src/extensions/workspaces/sessions/(frontend)/overlay/_lib/taskOverlay.ts"],"sourcesContent":["import { allocateAgentIdentityColor } from '@agimon-ai/doompi-ui/theme';\nimport type { ExtensionAPI, ExtensionContext, ExtensionUIContext, Theme } from '@earendil-works/pi-coding-agent';\nimport type { KeyId } from '@earendil-works/pi-tui';\nimport { type TUI, truncateToWidth } from '@earendil-works/pi-tui';\n\nimport type { Task } from '../../../../../../models/task';\nimport { COLLAPSE_KEY_OFF, getMaxWidgetLines, resolveCollapseKey } from '../../../../../../services/config';\nimport type { DelegationManager } from '../../../../../../services/delegation';\nimport { formatOverlayTaskLine } from '../../_shared/format.cli';\nimport { deriveTaskProjection, selectOverlayLayoutFromProjection, visibleTasks } from './selectors';\n\nconst WIDGET_KEY = 'doom-tasks';\nconst OVERLAY_HEADING = 'Tasks';\nconst PROGRESS_TICK_MS = 1000;\n\nexport interface TaskOverlayOptions {\n getTasks: () => readonly Task[];\n delegation: DelegationManager;\n}\n\n/** The widget owns its collapse key, while the session root owns its lifetime. */\nexport function registerTaskCollapseShortcut(\n pi: Pick<ExtensionAPI, 'registerShortcut'>,\n overlay: TaskOverlay,\n waitUntilReady: (context: ExtensionContext) => Promise<void>,\n isActive: () => boolean,\n): void {\n const key = resolveCollapseKey();\n if (key === COLLAPSE_KEY_OFF) return;\n pi.registerShortcut(key as KeyId, {\n description: 'Collapse or expand the task overlay',\n handler: async (context) => {\n if (!isActive() || !context.hasUI) return;\n await waitUntilReady(context);\n if (!isActive() || !overlay.isRegistered()) return;\n overlay.toggleCollapse();\n },\n });\n}\n\n/**\n * Persistent widget listing tasks above the editor.\n *\n * Registered once per UI context and refreshed with `requestRender()`; it\n * auto-hides when there is nothing to show so an idle session keeps a clean\n * prompt.\n */\nexport class TaskOverlay {\n private readonly options: TaskOverlayOptions;\n private uiCtx: ExtensionUIContext | undefined;\n private widgetRegistered = false;\n private tui: TUI | undefined;\n private progressTimer: NodeJS.Timeout | undefined;\n private collapsed = false;\n\n constructor(options: TaskOverlayOptions) {\n this.options = options;\n }\n\n /** Identity-compare so repeated session_start handlers stay idempotent. */\n setUICtx(ctx: ExtensionUIContext): void {\n if (ctx === this.uiCtx) return;\n if (this.uiCtx && this.widgetRegistered) this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.stopProgressTimer();\n this.uiCtx = ctx;\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n\n update(): void {\n if (!this.uiCtx) {\n this.stopProgressTimer();\n return;\n }\n const tasks = visibleTasks(this.options.getTasks());\n\n if (tasks.length === 0) {\n if (this.widgetRegistered) {\n this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n this.stopProgressTimer();\n return;\n }\n\n if (this.widgetRegistered) {\n this.syncProgressTimer(tasks);\n this.tui?.requestRender();\n return;\n }\n\n this.uiCtx.setWidget(\n WIDGET_KEY,\n (tui, factoryTheme) => {\n this.tui = tui;\n return {\n render: (width: number) => this.renderWidget(this.uiCtx?.theme ?? factoryTheme, width),\n invalidate: () => {\n // Nothing is cached: the next render reads the live theme and store.\n },\n };\n },\n { placement: 'aboveEditor' },\n );\n this.widgetRegistered = true;\n this.syncProgressTimer(tasks);\n }\n\n private syncProgressTimer(tasks: readonly Task[]): void {\n const hasRunningDelegation = tasks.some(\n (task) => task.status !== 'deleted' && task.delegation?.state === 'running',\n );\n if (this.collapsed || !this.widgetRegistered || !hasRunningDelegation) {\n this.stopProgressTimer();\n return;\n }\n if (this.progressTimer) return;\n this.progressTimer = setInterval(() => this.tui?.requestRender(), PROGRESS_TICK_MS);\n this.progressTimer.unref?.();\n }\n\n private stopProgressTimer(): void {\n if (this.progressTimer) clearInterval(this.progressTimer);\n this.progressTimer = undefined;\n }\n\n toggleCollapse(): void {\n this.collapsed = !this.collapsed;\n this.syncProgressTimer(this.options.getTasks());\n // Forced redraw: collapsing changes the widget height, not just its content.\n this.tui?.requestRender(true);\n }\n\n isRegistered(): boolean {\n return this.widgetRegistered;\n }\n\n private renderWidget(theme: Theme, width: number): string[] {\n const projection = deriveTaskProjection(this.options.getTasks());\n if (projection.visible.length === 0) return [];\n\n const truncate = (line: string): string => truncateToWidth(line, width, '…');\n const headingColor = projection.hasActiveWork ? 'accent' : 'dim';\n const headingIcon = projection.hasActiveWork ? '●' : '○';\n let headingText = `${OVERLAY_HEADING} (${projection.counts.completed}/${projection.counts.total})`;\n if (projection.counts.failed > 0) headingText += ` · ${projection.counts.failed} failed`;\n const heading = truncate(`${theme.fg(headingColor, headingIcon)} ${theme.fg(headingColor, headingText)}`);\n\n if (this.collapsed) {\n const key = resolveCollapseKey();\n const hint = key === COLLAPSE_KEY_OFF ? 'collapsed' : `${key} to expand`;\n return this.withTrailingSpacer([heading, truncate(`${theme.fg('dim', '└─')} ${theme.fg('dim', hint)}`)]);\n }\n\n const layout = selectOverlayLayoutFromProjection(projection, getMaxWidgetLines() - 1);\n const occupiedColors = new Set<ReturnType<typeof allocateAgentIdentityColor>>();\n const agentColors = new Map<string, ReturnType<typeof allocateAgentIdentityColor>>();\n for (const task of layout.visible) {\n if (task.delegation?.state !== 'running') continue;\n const identity = task.delegation.runId ?? task.delegation.requestId;\n const color = allocateAgentIdentityColor(identity, occupiedColors);\n occupiedColors.add(color);\n agentColors.set(identity, color);\n }\n\n const nowMs = Date.now();\n const lines = [heading];\n for (const task of layout.visible) {\n const progress = this.options.delegation.progressFor(task);\n const identity = task.delegation?.runId ?? task.delegation?.requestId;\n lines.push(\n truncate(\n `${theme.fg('dim', '├─')} ${formatOverlayTaskLine(\n task,\n theme,\n projection.showIds,\n progress,\n nowMs,\n identity ? agentColors.get(identity) : undefined,\n )}`,\n ),\n );\n }\n\n const hidden = layout.hiddenCompleted + layout.truncatedTail;\n if (hidden === 0) {\n const last = lines.length - 1;\n lines[last] = lines[last].replace('├─', '└─');\n return this.withTrailingSpacer(lines);\n }\n\n const parts: string[] = [];\n if (layout.hiddenCompleted > 0) parts.push(`${layout.hiddenCompleted} completed`);\n if (layout.truncatedTail > 0) parts.push(`${layout.truncatedTail} pending`);\n const summary = parts.length > 0 ? `+${hidden} more (${parts.join(', ')})` : `+${hidden} more`;\n lines.push(truncate(`${theme.fg('dim', '└─')} ${theme.fg('dim', summary)}`));\n return this.withTrailingSpacer(lines);\n }\n\n /** Pi adds a spacer above the widget but none below, which glues the last row to the input box. */\n private withTrailingSpacer(lines: string[]): string[] {\n if (lines.length === 0) return lines;\n lines.push('');\n return lines;\n }\n\n dispose(): void {\n this.stopProgressTimer();\n if (this.uiCtx) this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.widgetRegistered = false;\n this.tui = undefined;\n this.uiCtx = undefined;\n this.collapsed = false;\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { _ as resolveCollapseKey, m as getMaxWidgetLines } from "./config-DmsCPDuF.mjs";
2
2
  import { c as visibleTasks, n as deriveTaskProjection, o as selectOverlayLayoutFromProjection } from "./selectors-BaS6dObz.mjs";
3
- import { c as formatOverlayTaskLine } from "./format.cli-CuTlvk8J.mjs";
3
+ import { c as formatOverlayTaskLine } from "./format.cli-Cnc591eb.mjs";
4
4
  import { allocateAgentIdentityColor } from "@agimon-ai/doompi-ui/theme";
5
5
  import { truncateToWidth } from "@earendil-works/pi-tui";
6
6
  //#region src/extensions/workspaces/sessions/(frontend)/overlay/_lib/taskOverlay.ts
@@ -161,4 +161,4 @@ var TaskOverlay = class {
161
161
  //#endregion
162
162
  export { registerTaskCollapseShortcut as n, TaskOverlay as t };
163
163
 
164
- //# sourceMappingURL=taskOverlay-DVvLdTtf.mjs.map
164
+ //# sourceMappingURL=taskOverlay-CljDoTj_.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"taskOverlay-DVvLdTtf.mjs","names":[],"sources":["../src/extensions/workspaces/sessions/(frontend)/overlay/_lib/taskOverlay.ts"],"sourcesContent":["import { allocateAgentIdentityColor } from '@agimon-ai/doompi-ui/theme';\nimport type { ExtensionAPI, ExtensionContext, ExtensionUIContext, Theme } from '@earendil-works/pi-coding-agent';\nimport type { KeyId } from '@earendil-works/pi-tui';\nimport { type TUI, truncateToWidth } from '@earendil-works/pi-tui';\n\nimport type { Task } from '../../../../../../models/task';\nimport { COLLAPSE_KEY_OFF, getMaxWidgetLines, resolveCollapseKey } from '../../../../../../services/config';\nimport type { DelegationManager } from '../../../../../../services/delegation';\nimport { formatOverlayTaskLine } from '../../_shared/format.cli';\nimport { deriveTaskProjection, selectOverlayLayoutFromProjection, visibleTasks } from './selectors';\n\nconst WIDGET_KEY = 'doom-tasks';\nconst OVERLAY_HEADING = 'Tasks';\nconst PROGRESS_TICK_MS = 1000;\n\nexport interface TaskOverlayOptions {\n getTasks: () => readonly Task[];\n delegation: DelegationManager;\n}\n\n/** The widget owns its collapse key, while the session root owns its lifetime. */\nexport function registerTaskCollapseShortcut(\n pi: Pick<ExtensionAPI, 'registerShortcut'>,\n overlay: TaskOverlay,\n waitUntilReady: (context: ExtensionContext) => Promise<void>,\n isActive: () => boolean,\n): void {\n const key = resolveCollapseKey();\n if (key === COLLAPSE_KEY_OFF) return;\n pi.registerShortcut(key as KeyId, {\n description: 'Collapse or expand the task overlay',\n handler: async (context) => {\n if (!isActive() || !context.hasUI) return;\n await waitUntilReady(context);\n if (!isActive() || !overlay.isRegistered()) return;\n overlay.toggleCollapse();\n },\n });\n}\n\n/**\n * Persistent widget listing tasks above the editor.\n *\n * Registered once per UI context and refreshed with `requestRender()`; it\n * auto-hides when there is nothing to show so an idle session keeps a clean\n * prompt.\n */\nexport class TaskOverlay {\n private readonly options: TaskOverlayOptions;\n private uiCtx: ExtensionUIContext | undefined;\n private widgetRegistered = false;\n private tui: TUI | undefined;\n private progressTimer: NodeJS.Timeout | undefined;\n private collapsed = false;\n\n constructor(options: TaskOverlayOptions) {\n this.options = options;\n }\n\n /** Identity-compare so repeated session_start handlers stay idempotent. */\n setUICtx(ctx: ExtensionUIContext): void {\n if (ctx === this.uiCtx) return;\n if (this.uiCtx && this.widgetRegistered) this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.stopProgressTimer();\n this.uiCtx = ctx;\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n\n update(): void {\n if (!this.uiCtx) {\n this.stopProgressTimer();\n return;\n }\n const tasks = visibleTasks(this.options.getTasks());\n\n if (tasks.length === 0) {\n if (this.widgetRegistered) {\n this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n this.stopProgressTimer();\n return;\n }\n\n if (this.widgetRegistered) {\n this.syncProgressTimer(tasks);\n this.tui?.requestRender();\n return;\n }\n\n this.uiCtx.setWidget(\n WIDGET_KEY,\n (tui, factoryTheme) => {\n this.tui = tui;\n return {\n render: (width: number) => this.renderWidget(this.uiCtx?.theme ?? factoryTheme, width),\n invalidate: () => {\n // Nothing is cached: the next render reads the live theme and store.\n },\n };\n },\n { placement: 'aboveEditor' },\n );\n this.widgetRegistered = true;\n this.syncProgressTimer(tasks);\n }\n\n private syncProgressTimer(tasks: readonly Task[]): void {\n const hasRunningDelegation = tasks.some(\n (task) => task.status !== 'deleted' && task.delegation?.state === 'running',\n );\n if (this.collapsed || !this.widgetRegistered || !hasRunningDelegation) {\n this.stopProgressTimer();\n return;\n }\n if (this.progressTimer) return;\n this.progressTimer = setInterval(() => this.tui?.requestRender(), PROGRESS_TICK_MS);\n this.progressTimer.unref?.();\n }\n\n private stopProgressTimer(): void {\n if (this.progressTimer) clearInterval(this.progressTimer);\n this.progressTimer = undefined;\n }\n\n toggleCollapse(): void {\n this.collapsed = !this.collapsed;\n this.syncProgressTimer(this.options.getTasks());\n // Forced redraw: collapsing changes the widget height, not just its content.\n this.tui?.requestRender(true);\n }\n\n isRegistered(): boolean {\n return this.widgetRegistered;\n }\n\n private renderWidget(theme: Theme, width: number): string[] {\n const projection = deriveTaskProjection(this.options.getTasks());\n if (projection.visible.length === 0) return [];\n\n const truncate = (line: string): string => truncateToWidth(line, width, '…');\n const headingColor = projection.hasActiveWork ? 'accent' : 'dim';\n const headingIcon = projection.hasActiveWork ? '●' : '○';\n let headingText = `${OVERLAY_HEADING} (${projection.counts.completed}/${projection.counts.total})`;\n if (projection.counts.failed > 0) headingText += ` · ${projection.counts.failed} failed`;\n const heading = truncate(`${theme.fg(headingColor, headingIcon)} ${theme.fg(headingColor, headingText)}`);\n\n if (this.collapsed) {\n const key = resolveCollapseKey();\n const hint = key === COLLAPSE_KEY_OFF ? 'collapsed' : `${key} to expand`;\n return this.withTrailingSpacer([heading, truncate(`${theme.fg('dim', '└─')} ${theme.fg('dim', hint)}`)]);\n }\n\n const layout = selectOverlayLayoutFromProjection(projection, getMaxWidgetLines() - 1);\n const occupiedColors = new Set<ReturnType<typeof allocateAgentIdentityColor>>();\n const agentColors = new Map<string, ReturnType<typeof allocateAgentIdentityColor>>();\n for (const task of layout.visible) {\n if (task.delegation?.state !== 'running') continue;\n const identity = task.delegation.runId ?? task.delegation.requestId;\n const color = allocateAgentIdentityColor(identity, occupiedColors);\n occupiedColors.add(color);\n agentColors.set(identity, color);\n }\n\n const nowMs = Date.now();\n const lines = [heading];\n for (const task of layout.visible) {\n const progress = this.options.delegation.progressFor(task);\n const identity = task.delegation?.runId ?? task.delegation?.requestId;\n lines.push(\n truncate(\n `${theme.fg('dim', '├─')} ${formatOverlayTaskLine(\n task,\n theme,\n projection.showIds,\n progress,\n nowMs,\n identity ? agentColors.get(identity) : undefined,\n )}`,\n ),\n );\n }\n\n const hidden = layout.hiddenCompleted + layout.truncatedTail;\n if (hidden === 0) {\n const last = lines.length - 1;\n lines[last] = lines[last].replace('├─', '└─');\n return this.withTrailingSpacer(lines);\n }\n\n const parts: string[] = [];\n if (layout.hiddenCompleted > 0) parts.push(`${layout.hiddenCompleted} completed`);\n if (layout.truncatedTail > 0) parts.push(`${layout.truncatedTail} pending`);\n const summary = parts.length > 0 ? `+${hidden} more (${parts.join(', ')})` : `+${hidden} more`;\n lines.push(truncate(`${theme.fg('dim', '└─')} ${theme.fg('dim', summary)}`));\n return this.withTrailingSpacer(lines);\n }\n\n /** Pi adds a spacer above the widget but none below, which glues the last row to the input box. */\n private withTrailingSpacer(lines: string[]): string[] {\n if (lines.length === 0) return lines;\n lines.push('');\n return lines;\n }\n\n dispose(): void {\n this.stopProgressTimer();\n if (this.uiCtx) this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.widgetRegistered = false;\n this.tui = undefined;\n this.uiCtx = undefined;\n this.collapsed = false;\n }\n}\n"],"mappings":";;;;;;AAWA,MAAM,aAAa;AACnB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;;AAQzB,SAAgB,6BACd,IACA,SACA,gBACA,UACM;CACN,MAAM,MAAM,mBAAmB;CAC/B,IAAI,QAAA,OAA0B;CAC9B,GAAG,iBAAiB,KAAc;EAChC,aAAa;EACb,SAAS,OAAO,YAAY;GAC1B,IAAI,CAAC,SAAS,KAAK,CAAC,QAAQ,OAAO;GACnC,MAAM,eAAe,OAAO;GAC5B,IAAI,CAAC,SAAS,KAAK,CAAC,QAAQ,aAAa,GAAG;GAC5C,QAAQ,eAAe;EACzB;CACF,CAAC;AACH;;;;;;;;AASA,IAAa,cAAb,MAAyB;CACvB;CACA;CACA,mBAA2B;CAC3B;CACA;CACA,YAAoB;CAEpB,YAAY,SAA6B;EACvC,KAAK,UAAU;CACjB;;CAGA,SAAS,KAA+B;EACtC,IAAI,QAAQ,KAAK,OAAO;EACxB,IAAI,KAAK,SAAS,KAAK,kBAAkB,KAAK,MAAM,UAAU,YAAY,KAAA,CAAS;EACnF,KAAK,kBAAkB;EACvB,KAAK,QAAQ;EACb,KAAK,mBAAmB;EACxB,KAAK,MAAM,KAAA;CACb;CAEA,SAAe;EACb,IAAI,CAAC,KAAK,OAAO;GACf,KAAK,kBAAkB;GACvB;EACF;EACA,MAAM,QAAQ,aAAa,KAAK,QAAQ,SAAS,CAAC;EAElD,IAAI,MAAM,WAAW,GAAG;GACtB,IAAI,KAAK,kBAAkB;IACzB,KAAK,MAAM,UAAU,YAAY,KAAA,CAAS;IAC1C,KAAK,mBAAmB;IACxB,KAAK,MAAM,KAAA;GACb;GACA,KAAK,kBAAkB;GACvB;EACF;EAEA,IAAI,KAAK,kBAAkB;GACzB,KAAK,kBAAkB,KAAK;GAC5B,KAAK,KAAK,cAAc;GACxB;EACF;EAEA,KAAK,MAAM,UACT,aACC,KAAK,iBAAiB;GACrB,KAAK,MAAM;GACX,OAAO;IACL,SAAS,UAAkB,KAAK,aAAa,KAAK,OAAO,SAAS,cAAc,KAAK;IACrF,kBAAkB,CAElB;GACF;EACF,GACA,EAAE,WAAW,cAAc,CAC7B;EACA,KAAK,mBAAmB;EACxB,KAAK,kBAAkB,KAAK;CAC9B;CAEA,kBAA0B,OAA8B;EACtD,MAAM,uBAAuB,MAAM,MAChC,SAAS,KAAK,WAAW,aAAa,KAAK,YAAY,UAAU,SACpE;EACA,IAAI,KAAK,aAAa,CAAC,KAAK,oBAAoB,CAAC,sBAAsB;GACrE,KAAK,kBAAkB;GACvB;EACF;EACA,IAAI,KAAK,eAAe;EACxB,KAAK,gBAAgB,kBAAkB,KAAK,KAAK,cAAc,GAAG,gBAAgB;EAClF,KAAK,cAAc,QAAQ;CAC7B;CAEA,oBAAkC;EAChC,IAAI,KAAK,eAAe,cAAc,KAAK,aAAa;EACxD,KAAK,gBAAgB,KAAA;CACvB;CAEA,iBAAuB;EACrB,KAAK,YAAY,CAAC,KAAK;EACvB,KAAK,kBAAkB,KAAK,QAAQ,SAAS,CAAC;EAE9C,KAAK,KAAK,cAAc,IAAI;CAC9B;CAEA,eAAwB;EACtB,OAAO,KAAK;CACd;CAEA,aAAqB,OAAc,OAAyB;EAC1D,MAAM,aAAa,qBAAqB,KAAK,QAAQ,SAAS,CAAC;EAC/D,IAAI,WAAW,QAAQ,WAAW,GAAG,OAAO,CAAC;EAE7C,MAAM,YAAY,SAAyB,gBAAgB,MAAM,OAAO,GAAG;EAC3E,MAAM,eAAe,WAAW,gBAAgB,WAAW;EAC3D,MAAM,cAAc,WAAW,gBAAgB,MAAM;EACrD,IAAI,cAAc,GAAG,gBAAgB,IAAI,WAAW,OAAO,UAAU,GAAG,WAAW,OAAO,MAAM;EAChG,IAAI,WAAW,OAAO,SAAS,GAAG,eAAe,MAAM,WAAW,OAAO,OAAO;EAChF,MAAM,UAAU,SAAS,GAAG,MAAM,GAAG,cAAc,WAAW,EAAE,GAAG,MAAM,GAAG,cAAc,WAAW,GAAG;EAExG,IAAI,KAAK,WAAW;GAClB,MAAM,MAAM,mBAAmB;GAC/B,MAAM,OAAO,QAAA,QAA2B,cAAc,GAAG,IAAI;GAC7D,OAAO,KAAK,mBAAmB,CAAC,SAAS,SAAS,GAAG,MAAM,GAAG,OAAO,IAAI,EAAE,GAAG,MAAM,GAAG,OAAO,IAAI,GAAG,CAAC,CAAC;EACzG;EAEA,MAAM,SAAS,kCAAkC,YAAY,kBAAkB,IAAI,CAAC;EACpF,MAAM,iCAAiB,IAAI,IAAmD;EAC9E,MAAM,8BAAc,IAAI,IAA2D;EACnF,KAAK,MAAM,QAAQ,OAAO,SAAS;GACjC,IAAI,KAAK,YAAY,UAAU,WAAW;GAC1C,MAAM,WAAW,KAAK,WAAW,SAAS,KAAK,WAAW;GAC1D,MAAM,QAAQ,2BAA2B,UAAU,cAAc;GACjE,eAAe,IAAI,KAAK;GACxB,YAAY,IAAI,UAAU,KAAK;EACjC;EAEA,MAAM,QAAQ,KAAK,IAAI;EACvB,MAAM,QAAQ,CAAC,OAAO;EACtB,KAAK,MAAM,QAAQ,OAAO,SAAS;GACjC,MAAM,WAAW,KAAK,QAAQ,WAAW,YAAY,IAAI;GACzD,MAAM,WAAW,KAAK,YAAY,SAAS,KAAK,YAAY;GAC5D,MAAM,KACJ,SACE,GAAG,MAAM,GAAG,OAAO,IAAI,EAAE,GAAG,sBAC1B,MACA,OACA,WAAW,SACX,UACA,OACA,WAAW,YAAY,IAAI,QAAQ,IAAI,KAAA,CACzC,GACF,CACF;EACF;EAEA,MAAM,SAAS,OAAO,kBAAkB,OAAO;EAC/C,IAAI,WAAW,GAAG;GAChB,MAAM,OAAO,MAAM,SAAS;GAC5B,MAAM,QAAQ,MAAM,KAAK,CAAC,QAAQ,MAAM,IAAI;GAC5C,OAAO,KAAK,mBAAmB,KAAK;EACtC;EAEA,MAAM,QAAkB,CAAC;EACzB,IAAI,OAAO,kBAAkB,GAAG,MAAM,KAAK,GAAG,OAAO,gBAAgB,WAAW;EAChF,IAAI,OAAO,gBAAgB,GAAG,MAAM,KAAK,GAAG,OAAO,cAAc,SAAS;EAC1E,MAAM,UAAU,MAAM,SAAS,IAAI,IAAI,OAAO,SAAS,MAAM,KAAK,IAAI,EAAE,KAAK,IAAI,OAAO;EACxF,MAAM,KAAK,SAAS,GAAG,MAAM,GAAG,OAAO,IAAI,EAAE,GAAG,MAAM,GAAG,OAAO,OAAO,GAAG,CAAC;EAC3E,OAAO,KAAK,mBAAmB,KAAK;CACtC;;CAGA,mBAA2B,OAA2B;EACpD,IAAI,MAAM,WAAW,GAAG,OAAO;EAC/B,MAAM,KAAK,EAAE;EACb,OAAO;CACT;CAEA,UAAgB;EACd,KAAK,kBAAkB;EACvB,IAAI,KAAK,OAAO,KAAK,MAAM,UAAU,YAAY,KAAA,CAAS;EAC1D,KAAK,mBAAmB;EACxB,KAAK,MAAM,KAAA;EACX,KAAK,QAAQ,KAAA;EACb,KAAK,YAAY;CACnB;AACF"}
1
+ {"version":3,"file":"taskOverlay-CljDoTj_.mjs","names":[],"sources":["../src/extensions/workspaces/sessions/(frontend)/overlay/_lib/taskOverlay.ts"],"sourcesContent":["import { allocateAgentIdentityColor } from '@agimon-ai/doompi-ui/theme';\nimport type { ExtensionAPI, ExtensionContext, ExtensionUIContext, Theme } from '@earendil-works/pi-coding-agent';\nimport type { KeyId } from '@earendil-works/pi-tui';\nimport { type TUI, truncateToWidth } from '@earendil-works/pi-tui';\n\nimport type { Task } from '../../../../../../models/task';\nimport { COLLAPSE_KEY_OFF, getMaxWidgetLines, resolveCollapseKey } from '../../../../../../services/config';\nimport type { DelegationManager } from '../../../../../../services/delegation';\nimport { formatOverlayTaskLine } from '../../_shared/format.cli';\nimport { deriveTaskProjection, selectOverlayLayoutFromProjection, visibleTasks } from './selectors';\n\nconst WIDGET_KEY = 'doom-tasks';\nconst OVERLAY_HEADING = 'Tasks';\nconst PROGRESS_TICK_MS = 1000;\n\nexport interface TaskOverlayOptions {\n getTasks: () => readonly Task[];\n delegation: DelegationManager;\n}\n\n/** The widget owns its collapse key, while the session root owns its lifetime. */\nexport function registerTaskCollapseShortcut(\n pi: Pick<ExtensionAPI, 'registerShortcut'>,\n overlay: TaskOverlay,\n waitUntilReady: (context: ExtensionContext) => Promise<void>,\n isActive: () => boolean,\n): void {\n const key = resolveCollapseKey();\n if (key === COLLAPSE_KEY_OFF) return;\n pi.registerShortcut(key as KeyId, {\n description: 'Collapse or expand the task overlay',\n handler: async (context) => {\n if (!isActive() || !context.hasUI) return;\n await waitUntilReady(context);\n if (!isActive() || !overlay.isRegistered()) return;\n overlay.toggleCollapse();\n },\n });\n}\n\n/**\n * Persistent widget listing tasks above the editor.\n *\n * Registered once per UI context and refreshed with `requestRender()`; it\n * auto-hides when there is nothing to show so an idle session keeps a clean\n * prompt.\n */\nexport class TaskOverlay {\n private readonly options: TaskOverlayOptions;\n private uiCtx: ExtensionUIContext | undefined;\n private widgetRegistered = false;\n private tui: TUI | undefined;\n private progressTimer: NodeJS.Timeout | undefined;\n private collapsed = false;\n\n constructor(options: TaskOverlayOptions) {\n this.options = options;\n }\n\n /** Identity-compare so repeated session_start handlers stay idempotent. */\n setUICtx(ctx: ExtensionUIContext): void {\n if (ctx === this.uiCtx) return;\n if (this.uiCtx && this.widgetRegistered) this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.stopProgressTimer();\n this.uiCtx = ctx;\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n\n update(): void {\n if (!this.uiCtx) {\n this.stopProgressTimer();\n return;\n }\n const tasks = visibleTasks(this.options.getTasks());\n\n if (tasks.length === 0) {\n if (this.widgetRegistered) {\n this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.widgetRegistered = false;\n this.tui = undefined;\n }\n this.stopProgressTimer();\n return;\n }\n\n if (this.widgetRegistered) {\n this.syncProgressTimer(tasks);\n this.tui?.requestRender();\n return;\n }\n\n this.uiCtx.setWidget(\n WIDGET_KEY,\n (tui, factoryTheme) => {\n this.tui = tui;\n return {\n render: (width: number) => this.renderWidget(this.uiCtx?.theme ?? factoryTheme, width),\n invalidate: () => {\n // Nothing is cached: the next render reads the live theme and store.\n },\n };\n },\n { placement: 'aboveEditor' },\n );\n this.widgetRegistered = true;\n this.syncProgressTimer(tasks);\n }\n\n private syncProgressTimer(tasks: readonly Task[]): void {\n const hasRunningDelegation = tasks.some(\n (task) => task.status !== 'deleted' && task.delegation?.state === 'running',\n );\n if (this.collapsed || !this.widgetRegistered || !hasRunningDelegation) {\n this.stopProgressTimer();\n return;\n }\n if (this.progressTimer) return;\n this.progressTimer = setInterval(() => this.tui?.requestRender(), PROGRESS_TICK_MS);\n this.progressTimer.unref?.();\n }\n\n private stopProgressTimer(): void {\n if (this.progressTimer) clearInterval(this.progressTimer);\n this.progressTimer = undefined;\n }\n\n toggleCollapse(): void {\n this.collapsed = !this.collapsed;\n this.syncProgressTimer(this.options.getTasks());\n // Forced redraw: collapsing changes the widget height, not just its content.\n this.tui?.requestRender(true);\n }\n\n isRegistered(): boolean {\n return this.widgetRegistered;\n }\n\n private renderWidget(theme: Theme, width: number): string[] {\n const projection = deriveTaskProjection(this.options.getTasks());\n if (projection.visible.length === 0) return [];\n\n const truncate = (line: string): string => truncateToWidth(line, width, '…');\n const headingColor = projection.hasActiveWork ? 'accent' : 'dim';\n const headingIcon = projection.hasActiveWork ? '●' : '○';\n let headingText = `${OVERLAY_HEADING} (${projection.counts.completed}/${projection.counts.total})`;\n if (projection.counts.failed > 0) headingText += ` · ${projection.counts.failed} failed`;\n const heading = truncate(`${theme.fg(headingColor, headingIcon)} ${theme.fg(headingColor, headingText)}`);\n\n if (this.collapsed) {\n const key = resolveCollapseKey();\n const hint = key === COLLAPSE_KEY_OFF ? 'collapsed' : `${key} to expand`;\n return this.withTrailingSpacer([heading, truncate(`${theme.fg('dim', '└─')} ${theme.fg('dim', hint)}`)]);\n }\n\n const layout = selectOverlayLayoutFromProjection(projection, getMaxWidgetLines() - 1);\n const occupiedColors = new Set<ReturnType<typeof allocateAgentIdentityColor>>();\n const agentColors = new Map<string, ReturnType<typeof allocateAgentIdentityColor>>();\n for (const task of layout.visible) {\n if (task.delegation?.state !== 'running') continue;\n const identity = task.delegation.runId ?? task.delegation.requestId;\n const color = allocateAgentIdentityColor(identity, occupiedColors);\n occupiedColors.add(color);\n agentColors.set(identity, color);\n }\n\n const nowMs = Date.now();\n const lines = [heading];\n for (const task of layout.visible) {\n const progress = this.options.delegation.progressFor(task);\n const identity = task.delegation?.runId ?? task.delegation?.requestId;\n lines.push(\n truncate(\n `${theme.fg('dim', '├─')} ${formatOverlayTaskLine(\n task,\n theme,\n projection.showIds,\n progress,\n nowMs,\n identity ? agentColors.get(identity) : undefined,\n )}`,\n ),\n );\n }\n\n const hidden = layout.hiddenCompleted + layout.truncatedTail;\n if (hidden === 0) {\n const last = lines.length - 1;\n lines[last] = lines[last].replace('├─', '└─');\n return this.withTrailingSpacer(lines);\n }\n\n const parts: string[] = [];\n if (layout.hiddenCompleted > 0) parts.push(`${layout.hiddenCompleted} completed`);\n if (layout.truncatedTail > 0) parts.push(`${layout.truncatedTail} pending`);\n const summary = parts.length > 0 ? `+${hidden} more (${parts.join(', ')})` : `+${hidden} more`;\n lines.push(truncate(`${theme.fg('dim', '└─')} ${theme.fg('dim', summary)}`));\n return this.withTrailingSpacer(lines);\n }\n\n /** Pi adds a spacer above the widget but none below, which glues the last row to the input box. */\n private withTrailingSpacer(lines: string[]): string[] {\n if (lines.length === 0) return lines;\n lines.push('');\n return lines;\n }\n\n dispose(): void {\n this.stopProgressTimer();\n if (this.uiCtx) this.uiCtx.setWidget(WIDGET_KEY, undefined);\n this.widgetRegistered = false;\n this.tui = undefined;\n this.uiCtx = undefined;\n this.collapsed = false;\n }\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import { t as DEFAULT_PROMPT_GUIDELINES } from "./promptGuidelines-DaxWrqud.mjs";
2
- import { i as createTaskTool, n as DEFAULT_PROMPT_SNIPPET, r as TaskToolDependencies } from "./root.cli-BfEKqt8s.mjs";
2
+ import { i as createTaskTool, n as DEFAULT_PROMPT_SNIPPET, r as TaskToolDependencies } from "./root.cli-swHZqusx.mjs";
3
3
  export { DEFAULT_PROMPT_GUIDELINES, DEFAULT_PROMPT_SNIPPET, TaskToolDependencies, createTaskTool };
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_format_cli = require("./format.cli-CKxfl-lR.cjs");
2
+ const require_format_cli = require("./format.cli-ChQsP1MQ.cjs");
3
3
  exports.ACTION_GLYPH = require_format_cli.ACTION_GLYPH;
4
4
  exports.JustifiedRows = require_format_cli.JustifiedRows;
5
5
  exports.STATUS_COLOR = require_format_cli.STATUS_COLOR;
@@ -1,5 +1,5 @@
1
1
  import { c as TaskAction, h as TaskStatus, m as TaskMutationParams, s as Task } from "./task-hLh4BOO7.mjs";
2
- import { c as DelegationProgress } from "./index-BUmf786V.mjs";
2
+ import { c as DelegationProgress } from "./index-3-3ZJlaH.mjs";
3
3
  import { Component } from "@earendil-works/pi-tui";
4
4
  import { Theme } from "@earendil-works/pi-coding-agent";
5
5
  //#region src/extensions/workspaces/sessions/(frontend)/_shared/format.cli.d.ts
@@ -1,2 +1,2 @@
1
- import { a as STATUS_LABEL, c as formatOverlayTaskLine, d as renderTaskCall, f as renderTaskResult, i as STATUS_GLYPH, l as formatTokenCount, n as JustifiedRows, o as formatCommandTaskLine, r as STATUS_COLOR, s as formatElapsedMs, t as ACTION_GLYPH, u as overlayStatusGlyph } from "./format.cli-CuTlvk8J.mjs";
1
+ import { a as STATUS_LABEL, c as formatOverlayTaskLine, d as renderTaskCall, f as renderTaskResult, i as STATUS_GLYPH, l as formatTokenCount, n as JustifiedRows, o as formatCommandTaskLine, r as STATUS_COLOR, s as formatElapsedMs, t as ACTION_GLYPH, u as overlayStatusGlyph } from "./format.cli-Cnc591eb.mjs";
2
2
  export { ACTION_GLYPH, JustifiedRows, STATUS_COLOR, STATUS_GLYPH, STATUS_LABEL, formatCommandTaskLine, formatElapsedMs, formatOverlayTaskLine, formatTokenCount, overlayStatusGlyph, renderTaskCall, renderTaskResult };
@@ -1,4 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_taskOverlay = require("./taskOverlay-BKiu4cVj.cjs");
2
+ const require_taskOverlay = require("./taskOverlay-B5wJHiml.cjs");
3
3
  exports.TaskOverlay = require_taskOverlay.TaskOverlay;
4
4
  exports.registerTaskCollapseShortcut = require_taskOverlay.registerTaskCollapseShortcut;
@@ -1,5 +1,5 @@
1
1
  import { s as Task } from "./task-hLh4BOO7.mjs";
2
- import { r as DelegationManager } from "./index-BUmf786V.mjs";
2
+ import { r as DelegationManager } from "./index-3-3ZJlaH.mjs";
3
3
  import { ExtensionAPI, ExtensionContext, ExtensionUIContext } from "@earendil-works/pi-coding-agent";
4
4
  //#region src/extensions/workspaces/sessions/(frontend)/overlay/_lib/taskOverlay.d.ts
5
5
  export interface TaskOverlayOptions {
@@ -1,2 +1,2 @@
1
- import { n as registerTaskCollapseShortcut, t as TaskOverlay } from "./taskOverlay-DVvLdTtf.mjs";
1
+ import { n as registerTaskCollapseShortcut, t as TaskOverlay } from "./taskOverlay-CljDoTj_.mjs";
2
2
  export { TaskOverlay, registerTaskCollapseShortcut };
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_task_space_cli = require("./task-space.cli-BdDqn-_h.cjs");
2
+ const require_task_space_cli = require("./task-space.cli-BH4Iy_7m.cjs");
3
3
  exports.TASK_SPACE_OVERLAY_OPTIONS = require_task_space_cli.TASK_SPACE_OVERLAY_OPTIONS;
4
4
  exports.TaskSpaceComponent = require_task_space_cli.TaskSpaceComponent;
5
5
  exports.openTaskSpace = require_task_space_cli.openTaskSpace;
@@ -1,2 +1,2 @@
1
- import { n as TaskSpaceComponent, r as openTaskSpace, t as TASK_SPACE_OVERLAY_OPTIONS } from "./task-space.cli-DowNll0k.mjs";
1
+ import { n as TaskSpaceComponent, r as openTaskSpace, t as TASK_SPACE_OVERLAY_OPTIONS } from "./task-space.cli-CMYNjgOW.mjs";
2
2
  export { TASK_SPACE_OVERLAY_OPTIONS, TaskSpaceComponent, openTaskSpace };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-task",
3
- "version": "0.0.1-alpha.87",
3
+ "version": "0.0.1-alpha.89",
4
4
  "description": "Persistent, dependency-aware task graphs and subagent delegation for Pi coding sessions.",
5
5
  "keywords": [
6
6
  "ai",
@@ -161,15 +161,15 @@
161
161
  "registry": "https://registry.npmjs.org/"
162
162
  },
163
163
  "dependencies": {
164
- "@agimon-ai/doompi-core": "0.0.1-alpha.87",
165
- "@agimon-ai/doompi-telemetry": "0.0.1-alpha.81",
166
- "@agimon-ai/doompi-ui": "0.0.1-alpha.87",
167
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.45",
164
+ "@agimon-ai/doompi-core": "0.0.1-alpha.89",
165
+ "@agimon-ai/doompi-telemetry": "0.0.1-alpha.83",
166
+ "@agimon-ai/doompi-ui": "0.0.1-alpha.89",
167
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.47",
168
168
  "@deepseek-ai/cordis": "4.0.2",
169
169
  "typebox": "1.3.34"
170
170
  },
171
171
  "devDependencies": {
172
- "@agimon-ai/doompi-build": "0.0.1-alpha.13",
172
+ "@agimon-ai/doompi-build": "0.0.1-alpha.15",
173
173
  "@earendil-works/pi-coding-agent": "0.87.1",
174
174
  "@earendil-works/pi-tui": "0.87.1",
175
175
  "@tanstack/react-store": "0.11.1",
@@ -203,7 +203,14 @@
203
203
  "dist": "./dist/extensions/mcp.mjs",
204
204
  "scopes": [
205
205
  "session"
206
- ]
206
+ ],
207
+ "ui": {
208
+ "entry": "./generated/mcp-ui.ts",
209
+ "dist": "./dist/extensions/mcp-ui.mjs",
210
+ "widgets": [
211
+ "@agimon-ai/doompi-task/task"
212
+ ]
213
+ }
207
214
  },
208
215
  "doompiServer": {
209
216
  "entry": "./generated/server.ts",
@@ -237,7 +244,7 @@
237
244
  "build": "tsdown && tsdown --config tsdown.mcp.config.ts",
238
245
  "build:mcp": "tsdown --config tsdown.mcp.config.ts",
239
246
  "test": "vitest --run",
240
- "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.web.json",
247
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.web.json && tsc --noEmit -p tsconfig.mcp.json",
241
248
  "lint": "oxlint . && oxfmt . --check",
242
249
  "fixcode": "oxlint . --fix && oxfmt ."
243
250
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"delegation-BtzyLnYM.cjs","names":["DOOM_DELEGATION_ACCEPTED_EVENT","DOOM_DELEGATION_STARTED_EVENT","DOOM_DELEGATION_UPDATED_EVENT","DOOM_DELEGATION_FINISHED_EVENT","TASK_EVENT","reconcileOrphanedDelegations","isDelegationActive","isBlocked","isTaskListComplete"],"sources":["../src/services/delegation/index.ts"],"sourcesContent":["import {\n type DelegationAccepted as DelegationAcceptedPayload,\n DOOM_DELEGATION_ACCEPTED_EVENT,\n DOOM_DELEGATION_FINISHED_EVENT,\n DOOM_DELEGATION_STARTED_EVENT,\n DOOM_DELEGATION_UPDATED_EVENT,\n type DoomDelegationService,\n type DelegationResult,\n type DelegationStarted as DelegationStartedPayload,\n type DelegationUpdate,\n type InlineAgent,\n} from '@agimon-ai/doompi-core/delegation';\nimport type { Context } from '@deepseek-ai/cordis';\n\nimport { isBlocked, isTaskListComplete } from '../../models/taskGraph';\nimport { MAX_BRIEF_FILES } from '../../types/delegation';\nimport { TASK_EVENT, type TaskEventName, type TaskFailureReporter } from '../../types/telemetry';\nimport { reconcileOrphanedDelegations } from '../reconcile';\nimport type { TaskStore } from '../taskStore';\n\nexport { MAX_BRIEF_FILES };\nimport { isDelegationActive, type Task, type TaskDelegation, type TaskDocument } from '../../models/task';\n\nexport const NOTIFY_CUSTOM_TYPE = 'doom-task-notify';\nexport const BACKGROUND_WORK_PROVIDER = 'doom-task';\n\nconst DEFAULT_STARTED_TIMEOUT_MS = 5000;\nconst DEFAULT_RUN_TIMEOUT_MS = 20 * 60 * 1000;\nconst DEFAULT_CANCEL_TIMEOUT_MS = 10_000;\nconst MAX_RUN_TIMEOUT_GRACE_MS = 60_000;\nconst MAX_STORED_OUTPUT = 4000;\n/**\n * Cap on files rendered into a brief. Exported so the tool schema can state the\n * same number to the model rather than restating it and drifting.\n */\nconst MAX_BRIEF_NOTES = 1500;\nconst COMPLETED_STATE = 'completed';\nconst FAILED_STATE = 'failed';\nconst CANCELLED_STATE = 'cancelled';\nconst TIMED_OUT_STATUS = 'timed_out';\nconst DELETED_STATUS: Task['status'] = 'deleted';\n\nconst TASK_ID_ATTRIBUTE = 'task.id';\nconst REQUEST_ID_ATTRIBUTE = 'delegation.request_id';\n// `_name` suffix is load-bearing: the telemetry redactor drops string values\n// whose key has no safe meaning, and a bare `delegation.agent` is dropped.\nconst AGENT_ATTRIBUTE = 'delegation.agent_name';\nconst PID_ATTRIBUTE = 'delegation.pid';\nconst CONTEXT_PRESENT_ATTRIBUTE = 'delegation.context_present';\nconst CONTEXT_FILE_COUNT_ATTRIBUTE = 'delegation.context_file_count';\nconst CONTEXT_NOTES_LENGTH_ATTRIBUTE = 'delegation.context_notes_length';\nconst BRIEF_LENGTH_ATTRIBUTE = 'delegation.brief_length';\nconst TOOL_COUNT_ATTRIBUTE = 'delegation.tool_count';\nconst DURATION_MS_ATTRIBUTE = 'delegation.duration_ms';\nconst OUTCOME_ATTRIBUTE = 'delegation.outcome';\n\ntype SettlementState = typeof COMPLETED_STATE | typeof FAILED_STATE | typeof CANCELLED_STATE;\n\nexport const ERR_NO_RUNTIME =\n 'No subagent runtime responded. Delegation needs the subagents extension — relaunch doom-pi with agents enabled.';\nexport const ERR_CHILD_SESSION =\n 'assign is only available in the main session. Update this task directly instead of re-delegating it.';\nexport const ERR_CANCEL_UNACKNOWLEDGED = 'Subagent did not acknowledge the cancel request';\n\n/** Live progress from a running subagent. Kept in memory: persisting per-tool\n * ticks would rewrite the store file every second for no durable benefit. */\nexport interface DelegationProgress {\n agent: string;\n currentTool?: string;\n toolCount?: number;\n tokens?: number;\n /** Latest child-runtime duration baseline, excluding assignment wait. */\n durationMs?: number;\n /** Wall-clock time at which durationMs was observed or rebased. */\n durationObservedAt?: number;\n /** Assign-time context-pack shape, carried so the completion event can be\n * grouped by it without joining back to the assignment event. */\n contextFileCount?: number;\n contextNotesLength?: number;\n}\n\nexport type DelegationNotifier = (\n message: { customType: string; content: string; display: boolean },\n options?: { triggerTurn?: boolean; deliverAs?: 'steer' | 'followUp' | 'nextTurn' },\n) => void;\n\nexport interface DelegationPlatform {\n readonly environment: Readonly<Record<string, string | undefined>>;\n readonly processId: number;\n readonly createRequestId: () => string;\n readonly formatBriefPath: (entry: string, cwd: string) => string;\n}\n\nexport interface DelegationManagerOptions {\n store: TaskStore;\n cwd: string;\n platform: DelegationPlatform;\n notify?: DelegationNotifier;\n getSessionId?: () => string | undefined;\n onChange?: () => void;\n startedTimeoutMs?: number;\n /** How long a started run may go without reporting a result. */\n runTimeoutMs?: number;\n /** How long to wait for a cancelled run to report back before forcing it. */\n cancelTimeoutMs?: number;\n now?: () => string;\n nowMs?: () => number;\n /** Called when a completion notification could not be delivered. */\n onNotifyError: (error: unknown, taskId: number) => void;\n /** Where other swallowed delegation failures go. */\n report?: TaskFailureReporter;\n}\n\nexport interface AssignOptions {\n agent?: string;\n inlineAgent?: InlineAgent;\n instructions?: string;\n /** Paths the parent already located, rendered into the brief so the child\n * skips rediscovering them. */\n relevantFiles?: string[];\n /** Facts the parent already established, rendered into the brief so the child\n * does not re-derive them. */\n priorFindings?: string;\n model?: string;\n context?: 'fresh' | 'fork';\n signal?: AbortSignal;\n}\n\nexport interface DelegationOutcome {\n ok: boolean;\n message: string;\n}\n\n/** Result of the guarded store mutation that claims a task for delegation. */\ninterface AssignAttempt extends DelegationOutcome {\n task?: Task;\n}\n\ninterface PendingTask {\n readonly taskId: number;\n readonly sessionId?: string;\n}\n\nfunction truncate(value: string | undefined, limit = MAX_STORED_OUTPUT): string | undefined {\n if (!value) return undefined;\n return value.length <= limit ? value : `${value.slice(0, limit)}\\n… (truncated)`;\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** The assign-time context pack, normalized once for the brief and the telemetry. */\ninterface BriefContext {\n files: string[];\n notes?: string;\n}\n\n/** Normalize the parent context with host-owned path semantics. */\nfunction briefContext(options: AssignOptions, cwd: string, platform: DelegationPlatform): BriefContext {\n const files = new Set<string>();\n for (const entry of options.relevantFiles ?? []) {\n if (files.size >= MAX_BRIEF_FILES) break;\n const normalized = platform.formatBriefPath(entry.trim(), cwd);\n if (normalized && normalized !== '.') files.add(normalized);\n }\n return { files: [...files], notes: truncate(options.priorFindings?.trim(), MAX_BRIEF_NOTES) };\n}\n\n/**\n * Build the brief handed to the subagent.\n *\n * Doom Team owns the child tool surface and the parent owns the task record.\n * Keeping those responsibilities explicit prevents a child from attempting to\n * call a task tool that is not loaded in its runtime, while still giving it a\n * reliable path to ask the parent for a decision.\n *\n * The context pack is the parent's verified discovery handed over. The whole\n * block is omitted when nothing was supplied: this brief is the child's whole\n * starting context, so an empty section header is pure token cost.\n */\nfunction buildBrief(task: Task, instructions: string | undefined, context: BriefContext): string {\n const sections = [`Task #${task.id}: ${task.subject}`];\n if (task.description) sections.push(task.description);\n if (instructions) sections.push(instructions);\n if (context.files.length > 0 || context.notes) {\n const contextLines = ['Parent context — consume before repository discovery:'];\n if (context.files.length > 0) {\n contextLines.push(`- Parent-verified files (read these first with direct reads): ${context.files.join(', ')}`);\n }\n if (context.notes) {\n contextLines.push(\n `- Established facts (do not re-derive unless direct evidence contradicts them): ${context.notes}`,\n );\n }\n contextLines.push(\n 'This pack satisfies initial repository/context exploration. Do not begin with repository-wide listing, find, or grep. Expand only after consuming the pack and naming a concrete missing dependency or invalid path; search narrowly for that item and state why.',\n );\n sections.push(contextLines.join('\\n'));\n }\n sections.push(\n 'Coordination: work directly without changing the task record; ask main through intercom only for blockers or decisions; report changed files and verification when done.',\n );\n return sections.join('\\n\\n');\n}\n\n/**\n * Owns the lifecycle of tasks delegated to subagents.\n *\n * Delegation rides Team's injected Cordis service rather than spawning\n * processes directly, so doom-task stays decoupled from the subagent runtime\n * and degrades to a clear error when that service is not loaded.\n *\n * Liveness rule: every entry in `pendingTasks` has a timer armed against it, so\n * a delegation always has a deadline no matter which phase it is in. Losing\n * that pairing is what previously left tasks running forever.\n */\nexport class DelegationManager {\n private readonly options: DelegationManagerOptions;\n private readonly progress = new Map<string, DelegationProgress>();\n private readonly pendingTasks = new Map<string, PendingTask>();\n private readonly settlingRequests = new Set<string>();\n private readonly timers = new Map<string, ReturnType<typeof setTimeout>>();\n private readonly subscriptions: Array<() => void> = [];\n private service: DoomDelegationService | undefined;\n\n constructor(options: DelegationManagerOptions) {\n this.options = options;\n }\n\n private now(): string {\n return this.options.now?.() ?? new Date().toISOString();\n }\n\n private nowMs(): number {\n return this.options.nowMs?.() ?? Date.now();\n }\n\n private get runTimeoutMs(): number {\n return this.options.runTimeoutMs ?? DEFAULT_RUN_TIMEOUT_MS;\n }\n\n private report(event: TaskEventName, error: unknown, attributes?: Record<string, string | number | boolean>): void {\n this.options.report?.error(event, error, attributes);\n }\n\n /** True in a session that is itself a subagent child. */\n get isChildSession(): boolean {\n return Boolean(this.options.platform.environment.PI_SUBAGENT_CHILD);\n }\n\n /** Live progress for a task, for the overlay to render. */\n progressFor(task: Task): DelegationProgress | undefined {\n const requestId = task.delegation?.requestId;\n return requestId ? this.progress.get(requestId) : undefined;\n }\n\n /** Displayed runtime duration, rebased from the latest child observation. */\n static elapsedMs(progress: DelegationProgress, nowMs = Date.now()): number {\n const baseline = progress.durationMs ?? 0;\n const observedAt = progress.durationObservedAt ?? nowMs;\n return baseline + Math.max(0, nowMs - observedAt);\n }\n\n /** Rebind to the active Team service without stacking lifecycle listeners. */\n bind(ctx: Context, service: DoomDelegationService): () => void {\n this.unbind();\n this.service = service;\n this.subscriptions.push(\n ctx.on(DOOM_DELEGATION_ACCEPTED_EVENT, (event) => {\n this.handleAccepted(event);\n }),\n ctx.on(DOOM_DELEGATION_STARTED_EVENT, (event) => {\n this.handleStarted(event);\n }),\n ctx.on(DOOM_DELEGATION_UPDATED_EVENT, (event) => {\n this.handleUpdate(event);\n }),\n ctx.on(DOOM_DELEGATION_FINISHED_EVENT, (event) => {\n void this.handleResponse(event).catch((error: unknown) => {\n this.report(TASK_EVENT.delegationResponseFailed, error, { [REQUEST_ID_ATTRIBUTE]: event.requestId });\n });\n }),\n );\n return () => {\n if (this.service === service) this.unbind();\n };\n }\n\n /** Remove only the cross-package binding; task state and watchdogs remain live. */\n unbind(): void {\n for (const unsubscribe of this.subscriptions.splice(0)) unsubscribe();\n this.service = undefined;\n }\n\n /** Items still in flight, surfaced so auto-stop will not kill the harness. */\n listActiveWork(): Array<{ id: string; sessionId: string }> {\n return [...this.pendingTasks.entries()].flatMap(([requestId, pending]) =>\n pending.sessionId ? [{ id: `task-${pending.taskId}:${requestId}`, sessionId: pending.sessionId }] : [],\n );\n }\n\n /**\n * Return tasks nothing can finish to `pending`.\n *\n * The live request set is read inside the mutation so a delegation assigned\n * while the lock was contended is not mistaken for a leftover.\n */\n async reconcile(isCurrent: () => boolean = () => true): Promise<Task[]> {\n const { value } = await this.options.store.mutate((document) => {\n if (!isCurrent()) return { value: [] as Task[] };\n const result = reconcileOrphanedDelegations(document, this.now(), undefined, {\n pid: this.options.platform.processId,\n liveRequestIds: new Set(this.pendingTasks.keys()),\n });\n if (result.orphaned.length === 0) return { value: [] as Task[] };\n return { document: result.document, value: result.orphaned };\n });\n\n if (!isCurrent()) return [];\n for (const task of value) {\n this.options.report?.warn(TASK_EVENT.delegationOrphaned, new Error(task.delegation?.result?.error ?? ''), {\n [TASK_ID_ATTRIBUTE]: task.id,\n ...(task.delegation?.agent ? { [AGENT_ATTRIBUTE]: task.delegation.agent } : {}),\n ...(task.delegation?.pid === undefined ? {} : { [PID_ATTRIBUTE]: task.delegation.pid }),\n });\n }\n\n if (value.length > 0) this.options.onChange?.();\n return value;\n }\n\n async assign(taskId: number, options: AssignOptions): Promise<DelegationOutcome> {\n const agent = options.agent?.trim();\n if (!agent) return { ok: false, message: 'agent required for assign' };\n if (this.isChildSession) return { ok: false, message: ERR_CHILD_SESSION };\n if (options.signal?.aborted) return { ok: false, message: 'delegation cancelled before it started' };\n\n const requestId = this.options.platform.createRequestId();\n const startedAt = this.now();\n const sessionId = this.options.getSessionId?.();\n const context = briefContext(options, this.options.cwd, this.options.platform);\n\n const { value: outcome } = await this.options.store.mutate<AssignAttempt>((current) => {\n const index = current.tasks.findIndex((task) => task.id === taskId);\n if (index === -1) return { value: { ok: false, message: `#${taskId} not found` } };\n\n const task = current.tasks[index];\n if (task.status === DELETED_STATUS) return { value: { ok: false, message: `#${taskId} is deleted` } };\n if (task.status === COMPLETED_STATE) {\n return { value: { ok: false, message: `#${taskId} is already completed` } };\n }\n if (isDelegationActive(task)) {\n return { value: { ok: false, message: `#${taskId} is already delegated to ${task.delegation?.agent}` } };\n }\n if (isBlocked(current.tasks, task)) {\n const blockers = (task.blockedBy ?? []).map((id) => `#${id}`).join(', ');\n return { value: { ok: false, message: `#${taskId} is blocked by ${blockers}` } };\n }\n\n const delegation: TaskDelegation = {\n requestId,\n agent,\n state: 'requested',\n pid: this.options.platform.processId,\n startedAt,\n ...(sessionId ? { sessionId } : {}),\n ...(options.model ? { model: options.model } : {}),\n };\n\n const tasks = [...current.tasks];\n tasks[index] = { ...task, owner: agent, updatedAt: startedAt, delegation };\n return {\n document: { ...current, tasks },\n value: { ok: true, message: '', task: tasks[index] },\n };\n });\n\n if (!outcome.ok || !outcome.task) return { ok: false, message: outcome.message };\n\n const contextNotesLength = context.notes?.length ?? 0;\n this.pendingTasks.set(requestId, { taskId, sessionId });\n this.progress.set(requestId, { agent, contextFileCount: context.files.length, contextNotesLength });\n this.armStartedTimeout(requestId);\n this.options.onChange?.();\n const request = {\n requestId,\n taskId,\n agent,\n ...(options.inlineAgent ? { inlineAgent: options.inlineAgent } : {}),\n prompt: buildBrief(outcome.task, options.instructions, context),\n ...(options.context ? { context: options.context } : {}),\n cwd: this.options.cwd,\n artifacts: true,\n timeoutMs: this.runTimeoutMs,\n runMode: 'detached' as const,\n teamTask: { id: String(outcome.task.id), subject: outcome.task.subject },\n ...(options.model ? { model: options.model } : {}),\n };\n\n try {\n const service = this.service;\n if (!service) throw new Error(ERR_NO_RUNTIME);\n void service.request(request).catch((error: unknown) => {\n this.report(TASK_EVENT.delegationRequestFailed, error, {\n [TASK_ID_ATTRIBUTE]: taskId,\n [REQUEST_ID_ATTRIBUTE]: requestId,\n [AGENT_ATTRIBUTE]: agent,\n });\n this.settleDetached(requestId, FAILED_STATE, {\n status: FAILED_STATE,\n error: `Delegation request failed: ${messageOf(error)}`,\n });\n });\n } catch (error) {\n this.report(TASK_EVENT.delegationRequestFailed, error, {\n [TASK_ID_ATTRIBUTE]: taskId,\n [REQUEST_ID_ATTRIBUTE]: requestId,\n [AGENT_ATTRIBUTE]: agent,\n });\n const detail = messageOf(error);\n await this.settle(requestId, FAILED_STATE, {\n status: FAILED_STATE,\n error: `Delegation request failed: ${detail}`,\n });\n return { ok: false, message: `Could not dispatch #${taskId} to ${agent}: ${detail}` };\n }\n\n // Recorded only after the service accepted the request call, so it means a\n // brief actually entered Team's state machine rather than merely being assembled.\n this.options.report?.event(TASK_EVENT.delegationAssigned, {\n [TASK_ID_ATTRIBUTE]: taskId,\n [REQUEST_ID_ATTRIBUTE]: requestId,\n [AGENT_ATTRIBUTE]: agent,\n [CONTEXT_PRESENT_ATTRIBUTE]: context.files.length > 0 || contextNotesLength > 0,\n [CONTEXT_FILE_COUNT_ATTRIBUTE]: context.files.length,\n [CONTEXT_NOTES_LENGTH_ATTRIBUTE]: contextNotesLength,\n [BRIEF_LENGTH_ATTRIBUTE]: request.prompt.length,\n });\n\n return {\n ok: true,\n message: `Delegated #${taskId} to ${agent} in the background. It runs independently of this turn and will notify you when it finishes. Continue non-overlapping work, or end your turn.`,\n };\n }\n\n async cancel(taskId: number): Promise<DelegationOutcome> {\n const document = this.options.store.read();\n const task = document.tasks.find((candidate) => candidate.id === taskId);\n if (!task) return { ok: false, message: `#${taskId} not found` };\n if (!task.delegation || !isDelegationActive(task)) {\n return { ok: false, message: `#${taskId} has no running delegation` };\n }\n\n const { requestId } = task.delegation;\n this.service?.cancel({ requestId });\n // Only this session can force the outcome; a delegation owned elsewhere\n // gets the request and nothing more.\n this.armCancelTimeout(requestId);\n return { ok: true, message: `Cancelling delegation for #${taskId} (${task.delegation.agent})` };\n }\n\n /** Settle without awaiting, reporting rather than dropping a failure. */\n private settleDetached(\n requestId: string,\n state: SettlementState,\n result: TaskDelegation['result'],\n event: TaskEventName = TASK_EVENT.delegationSettleFailed,\n ): void {\n void this.settle(requestId, state, result).catch((error: unknown) => {\n this.report(event, error, { [REQUEST_ID_ATTRIBUTE]: requestId });\n });\n }\n\n /**\n * Fail a delegation that nobody acknowledged.\n *\n * Provider loss or a stalled Team runtime can still leave a dispatched\n * request silent. Without this acknowledgement deadline the task would sit\n * in `requested` forever.\n */\n private armStartedTimeout(requestId: string): void {\n this.armTimer(requestId, this.options.startedTimeoutMs ?? DEFAULT_STARTED_TIMEOUT_MS, () => {\n this.settleDetached(requestId, FAILED_STATE, { status: FAILED_STATE, error: ERR_NO_RUNTIME });\n });\n }\n\n /**\n * Bound a run that already started.\n *\n * The request carries the same budget, so in the normal case the runtime ends\n * the run itself and reports a richer result. The grace period biases toward\n * that answer; this timer only covers a runtime that goes silent, which is\n * precisely the case where no response event will ever arrive.\n */\n private armRunTimeout(requestId: string): void {\n const grace = Math.min(MAX_RUN_TIMEOUT_GRACE_MS, this.runTimeoutMs);\n this.armTimer(requestId, this.runTimeoutMs + grace, () => {\n const taskId = this.pendingTasks.get(requestId)?.taskId;\n const agent = this.progress.get(requestId)?.agent;\n const error = new Error(`Delegation produced no result within ${this.runTimeoutMs}ms`);\n this.options.report?.warn(TASK_EVENT.delegationTimedOut, error, {\n ...(taskId === undefined ? {} : { [TASK_ID_ATTRIBUTE]: taskId }),\n [REQUEST_ID_ATTRIBUTE]: requestId,\n ...(agent ? { [AGENT_ATTRIBUTE]: agent } : {}),\n });\n this.service?.cancel({ requestId, reason: error.message });\n this.settleDetached(requestId, FAILED_STATE, { status: TIMED_OUT_STATUS, error: error.message });\n });\n }\n\n /**\n * Guarantee a cancel reaches a terminal state.\n *\n * Calling the service is a request, not an outcome: a runtime that has\n * already forgotten the request answers nothing at all. The window still\n * favours a real response, which carries the run's partial output.\n */\n private armCancelTimeout(requestId: string): void {\n if (!this.pendingTasks.has(requestId)) return;\n this.armTimer(requestId, this.options.cancelTimeoutMs ?? DEFAULT_CANCEL_TIMEOUT_MS, () => {\n this.settleDetached(requestId, CANCELLED_STATE, {\n status: CANCELLED_STATE,\n error: ERR_CANCEL_UNACKNOWLEDGED,\n });\n });\n }\n\n private armTimer(requestId: string, delayMs: number, onFire: () => void): void {\n this.clearTimer(requestId);\n const timer = setTimeout(onFire, delayMs);\n timer.unref?.();\n this.timers.set(requestId, timer);\n }\n\n private clearTimer(requestId: string): void {\n const timer = this.timers.get(requestId);\n if (timer) clearTimeout(timer);\n this.timers.delete(requestId);\n }\n\n /** Runtime acknowledgement moves the request off the short extension-presence watchdog while launch is in flight. */\n private handleAccepted(event: DelegationAcceptedPayload): void {\n if (!this.pendingTasks.has(event.requestId)) return;\n this.armRunTimeout(event.requestId);\n }\n\n private handleStarted(event: DelegationStartedPayload): void {\n const { runId } = event;\n const taskId = this.pendingTasks.get(event.requestId)?.taskId;\n if (taskId === undefined) return;\n this.armRunTimeout(event.requestId);\n const existing = this.progress.get(event.requestId);\n if (existing) {\n this.progress.set(event.requestId, {\n ...existing,\n durationMs: existing.durationMs ?? 0,\n durationObservedAt: existing.durationObservedAt ?? this.nowMs(),\n });\n }\n\n void this.options.store\n .mutate((document) =>\n this.patchDelegation(document, event.requestId, (task, delegation) => ({\n ...task,\n status: task.status === COMPLETED_STATE ? task.status : 'in_progress',\n delegation: {\n ...delegation,\n state: 'running',\n startedAt: delegation.startedAt ?? this.now(),\n ...(runId ? { runId } : {}),\n },\n })),\n )\n .then(() => this.options.onChange?.())\n .catch((error: unknown) => {\n this.report(TASK_EVENT.delegationStartFailed, error, {\n [TASK_ID_ATTRIBUTE]: taskId,\n [REQUEST_ID_ATTRIBUTE]: event.requestId,\n });\n });\n }\n\n private handleUpdate(event: DelegationUpdate): void {\n const existing = this.progress.get(event.requestId);\n if (!existing) return;\n\n const observedAt = this.nowMs();\n const currentElapsed = DelegationManager.elapsedMs(existing, observedAt);\n const hasDuration = event.durationMs !== undefined;\n this.progress.set(event.requestId, {\n // Spread, not field-by-field: anything carried on progress that this\n // event does not report (the assign-time context shape) must survive the\n // first update, or the completion event reports it as absent.\n ...existing,\n currentTool: event.currentTool ?? existing.currentTool,\n toolCount:\n event.toolCount === undefined ? existing.toolCount : Math.max(existing.toolCount ?? 0, event.toolCount),\n tokens: event.tokens === undefined ? existing.tokens : Math.max(existing.tokens ?? 0, event.tokens),\n durationMs: hasDuration ? Math.max(currentElapsed, event.durationMs!) : existing.durationMs,\n durationObservedAt: hasDuration ? observedAt : existing.durationObservedAt,\n });\n this.options.onChange?.();\n }\n\n private async handleResponse(event: DelegationResult): Promise<void> {\n if (!this.pendingTasks.has(event.requestId)) return;\n\n const succeeded = event.status === COMPLETED_STATE && !event.error;\n const state = succeeded ? COMPLETED_STATE : event.status === CANCELLED_STATE ? CANCELLED_STATE : FAILED_STATE;\n await this.settle(event.requestId, state, {\n status: event.status,\n error: event.error,\n output: truncate(event.output),\n outputPath: event.outputPath,\n sessionFile: event.sessionFile,\n durationMs: event.durationMs,\n toolCount: event.toolCount,\n });\n }\n\n /**\n * Apply the terminal state of a delegation and tell the model about it.\n *\n * A cancelled run returns the task to `pending` (the work is still wanted,\n * just not by that agent); a failure marks it `failed` so it stays visible\n * rather than silently rejoining the backlog.\n *\n * The request is claimed in `settlingRequests` while it remains published as\n * active work. It is removed only after the terminal state and model notification\n * have been handed off, so an auto-stop observer cannot end the session between\n * receiving the result and delivering it to the model.\n */\n private async settle(requestId: string, state: SettlementState, result: TaskDelegation['result']): Promise<void> {\n const pending = this.pendingTasks.get(requestId);\n if (!pending || this.settlingRequests.has(requestId)) return;\n this.settlingRequests.add(requestId);\n this.clearTimer(requestId);\n const { taskId } = pending;\n const progress = this.progress.get(requestId);\n\n try {\n const endedAt = this.now();\n let task: Task | undefined;\n try {\n const { value } = await this.options.store.mutate((document) =>\n this.patchDelegation(document, requestId, (current, delegation) => ({\n ...current,\n status: state === COMPLETED_STATE ? COMPLETED_STATE : state === CANCELLED_STATE ? 'pending' : FAILED_STATE,\n updatedAt: endedAt,\n delegation: { ...delegation, state, endedAt, ...(result ? { result } : {}) },\n })),\n );\n task = value;\n } catch (error) {\n this.report(TASK_EVENT.delegationSettleFailed, error, {\n [TASK_ID_ATTRIBUTE]: taskId,\n [REQUEST_ID_ATTRIBUTE]: requestId,\n });\n }\n\n const agent = task?.delegation?.agent ?? progress?.agent ?? 'subagent';\n const subject = task?.subject ?? `#${taskId}`;\n\n // Repeats the assign-time context shape so the cost question (\"did packs\n // reduce child tool calls?\") is one grouping rather than a self-join.\n // A run that never started reports neither metric, keeping the medians clean.\n this.options.report?.event(TASK_EVENT.delegationCompleted, {\n [TASK_ID_ATTRIBUTE]: taskId,\n [REQUEST_ID_ATTRIBUTE]: requestId,\n [AGENT_ATTRIBUTE]: agent,\n [OUTCOME_ATTRIBUTE]: state,\n [CONTEXT_PRESENT_ATTRIBUTE]: (progress?.contextFileCount ?? 0) > 0 || (progress?.contextNotesLength ?? 0) > 0,\n [CONTEXT_FILE_COUNT_ATTRIBUTE]: progress?.contextFileCount ?? 0,\n [CONTEXT_NOTES_LENGTH_ATTRIBUTE]: progress?.contextNotesLength ?? 0,\n ...(result?.toolCount === undefined ? {} : { [TOOL_COUNT_ATTRIBUTE]: result.toolCount }),\n ...(result?.durationMs === undefined ? {} : { [DURATION_MS_ATTRIBUTE]: result.durationMs }),\n });\n const listComplete =\n state === COMPLETED_STATE && task !== undefined && isTaskListComplete(this.options.store.snapshot.tasks);\n this.notifyModel(state, taskId, subject, agent, result, listComplete);\n } finally {\n this.pendingTasks.delete(requestId);\n this.progress.delete(requestId);\n this.settlingRequests.delete(requestId);\n this.options.onChange?.();\n }\n }\n\n private notifyModel(\n state: SettlementState,\n taskId: number,\n subject: string,\n agent: string,\n result: TaskDelegation['result'],\n listComplete: boolean,\n ): void {\n const notify = this.options.notify;\n if (!notify) return;\n\n const headline =\n state === COMPLETED_STATE\n ? `Subagent ${agent} completed task #${taskId}: ${subject}`\n : state === CANCELLED_STATE\n ? `Subagent ${agent} was cancelled on task #${taskId}: ${subject} (returned to pending)`\n : `Subagent ${agent} failed task #${taskId}: ${subject}`;\n\n const lines = [headline];\n if (listComplete) {\n lines.push(\n '',\n 'All tasks are completed. Review the full task list once more, then close it with task {\"action\":\"clear\"}.',\n );\n }\n if (result?.error) lines.push(`Error: ${result.error}`);\n if (result?.output) lines.push('', result.output);\n if (result?.outputPath) lines.push('', `Full output: ${result.outputPath}`);\n\n try {\n notify(\n { customType: NOTIFY_CUSTOM_TYPE, content: lines.join('\\n'), display: true },\n { triggerTurn: true, deliverAs: 'steer' },\n );\n } catch (error) {\n // The delegation state is already committed to the store, so a failed\n // notification costs the model its wake-up, not the result. Report it\n // rather than rethrowing, which would strand the settled run.\n this.options.onNotifyError(error, taskId);\n }\n }\n\n /**\n * Rewrite the task carrying `requestId`, returning the updated task as the mutation value.\n *\n * Terminal delegations are left alone. The document here is the freshest one,\n * read under the store lock, which is the only place the check is meaningful:\n * a `started` event can land after a cancel already settled the run, and\n * without this guard its write would resurrect a delegation that nothing is\n * tracking any more.\n */\n private patchDelegation(\n document: TaskDocument,\n requestId: string,\n patch: (task: Task, delegation: TaskDelegation) => Task,\n ): { document?: TaskDocument; value: Task | undefined } {\n const index = document.tasks.findIndex((task) => task.delegation?.requestId === requestId);\n if (index === -1) return { value: undefined };\n\n const task = document.tasks[index];\n if (!isDelegationActive(task)) return { value: task };\n\n const updated = patch(task, task.delegation!);\n if (updated === task) return { value: task };\n const tasks = [...document.tasks];\n tasks[index] = updated;\n return { document: { ...document, tasks }, value: updated };\n }\n\n dispose(): void {\n this.unbind();\n this.reset();\n }\n\n /** Clear one Pi session's transient delegation state without losing service injection. */\n reset(): void {\n const changed = this.pendingTasks.size > 0 || this.progress.size > 0;\n for (const timer of this.timers.values()) clearTimeout(timer);\n this.timers.clear();\n this.pendingTasks.clear();\n this.settlingRequests.clear();\n this.progress.clear();\n if (changed) this.options.onChange?.();\n }\n}\n"],"mappings":"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"}