@athenaintel/react 0.50.0 → 0.53.0

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 (112) hide show
  1. package/README.md +24 -0
  2. package/dist/agent-runtime/src/desktop-handoff.d.ts +10 -0
  3. package/dist/agent-runtime/src/edit-command.cjs +21 -0
  4. package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
  5. package/dist/agent-runtime/src/edit-command.d.ts +8 -0
  6. package/dist/agent-runtime/src/edit-command.js +21 -0
  7. package/dist/agent-runtime/src/edit-command.js.map +1 -1
  8. package/dist/agent-runtime/src/handoff-arrival.cjs +19 -0
  9. package/dist/agent-runtime/src/handoff-arrival.cjs.map +1 -1
  10. package/dist/agent-runtime/src/handoff-arrival.d.ts +18 -3
  11. package/dist/agent-runtime/src/handoff-arrival.js +20 -1
  12. package/dist/agent-runtime/src/handoff-arrival.js.map +1 -1
  13. package/dist/agent-runtime/src/handoff-cursor-id.cjs +6 -0
  14. package/dist/agent-runtime/src/handoff-cursor-id.cjs.map +1 -0
  15. package/dist/agent-runtime/src/handoff-cursor-id.d.ts +12 -0
  16. package/dist/agent-runtime/src/handoff-cursor-id.js +6 -0
  17. package/dist/agent-runtime/src/handoff-cursor-id.js.map +1 -0
  18. package/dist/agent-runtime/src/handoff-cursor.cjs +88 -0
  19. package/dist/agent-runtime/src/handoff-cursor.cjs.map +1 -0
  20. package/dist/agent-runtime/src/handoff-cursor.d.ts +120 -0
  21. package/dist/agent-runtime/src/handoff-cursor.js +88 -0
  22. package/dist/agent-runtime/src/handoff-cursor.js.map +1 -0
  23. package/dist/agent-runtime/src/index.d.ts +4 -3
  24. package/dist/agent-runtime/src/thread-handoff-flow-machine.d.ts +6 -0
  25. package/dist/agent-runtime/src/thread-handoff-protocol.d.ts +114 -4
  26. package/dist/agent-runtime/src/thread-handoff.d.ts +1 -1
  27. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs +8 -3
  28. package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
  29. package/dist/agent-runtime/src/use-deep-agent-window-sync.d.ts +7 -0
  30. package/dist/agent-runtime/src/use-deep-agent-window-sync.js +9 -4
  31. package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
  32. package/dist/agent-runtime/src/use-thread-handoff-flow.d.ts +5 -0
  33. package/dist/chat/AthenaChat.cjs +27 -12
  34. package/dist/chat/AthenaChat.cjs.map +1 -1
  35. package/dist/chat/AthenaChat.d.ts +13 -0
  36. package/dist/chat/AthenaChat.js +27 -12
  37. package/dist/chat/AthenaChat.js.map +1 -1
  38. package/dist/chat/AthenaPlanStrip.cjs +97 -0
  39. package/dist/chat/AthenaPlanStrip.cjs.map +1 -0
  40. package/dist/chat/AthenaPlanStrip.d.ts +15 -0
  41. package/dist/chat/AthenaPlanStrip.js +97 -0
  42. package/dist/chat/AthenaPlanStrip.js.map +1 -0
  43. package/dist/chat/super-grouping/SuperGroupingCard.cjs +4 -2
  44. package/dist/chat/super-grouping/SuperGroupingCard.cjs.map +1 -1
  45. package/dist/chat/super-grouping/SuperGroupingCard.js +4 -2
  46. package/dist/chat/super-grouping/SuperGroupingCard.js.map +1 -1
  47. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs +4 -2
  48. package/dist/chat/super-grouping/SuperGroupingTranscript.cjs.map +1 -1
  49. package/dist/chat/super-grouping/SuperGroupingTranscript.js +4 -2
  50. package/dist/chat/super-grouping/SuperGroupingTranscript.js.map +1 -1
  51. package/dist/chat/super-grouping/activity-timeline.cjs +6 -4
  52. package/dist/chat/super-grouping/activity-timeline.cjs.map +1 -1
  53. package/dist/chat/super-grouping/activity-timeline.d.ts +9 -1
  54. package/dist/chat/super-grouping/activity-timeline.js +6 -4
  55. package/dist/chat/super-grouping/activity-timeline.js.map +1 -1
  56. package/dist/chat/super-grouping/index.d.ts +1 -1
  57. package/dist/chat/super-grouping/use-earlier-steps-unloaded.cjs +17 -0
  58. package/dist/chat/super-grouping/use-earlier-steps-unloaded.cjs.map +1 -0
  59. package/dist/chat/super-grouping/use-earlier-steps-unloaded.d.ts +8 -0
  60. package/dist/chat/super-grouping/use-earlier-steps-unloaded.js +17 -0
  61. package/dist/chat/super-grouping/use-earlier-steps-unloaded.js.map +1 -0
  62. package/dist/chat-ui/index.cjs +8 -0
  63. package/dist/chat-ui/index.cjs.map +1 -1
  64. package/dist/chat-ui/index.d.ts +2 -1
  65. package/dist/chat-ui/index.js +9 -1
  66. package/dist/chat-ui/index.js.map +1 -1
  67. package/dist/chat-ui/mentions/mention-chip.cjs +1 -12
  68. package/dist/chat-ui/mentions/mention-chip.cjs.map +1 -1
  69. package/dist/chat-ui/mentions/mention-chip.js +1 -12
  70. package/dist/chat-ui/mentions/mention-chip.js.map +1 -1
  71. package/dist/chat-ui/mentions/use-mention-asset.cjs +18 -3
  72. package/dist/chat-ui/mentions/use-mention-asset.cjs.map +1 -1
  73. package/dist/chat-ui/mentions/use-mention-asset.js +19 -4
  74. package/dist/chat-ui/mentions/use-mention-asset.js.map +1 -1
  75. package/dist/chat-ui/thread/chat-thread-shell.cjs +2 -0
  76. package/dist/chat-ui/thread/chat-thread-shell.cjs.map +1 -1
  77. package/dist/chat-ui/thread/chat-thread-shell.d.ts +4 -0
  78. package/dist/chat-ui/thread/chat-thread-shell.js +2 -0
  79. package/dist/chat-ui/thread/chat-thread-shell.js.map +1 -1
  80. package/dist/chat-ui/thread/handoff-cursor.cjs +216 -0
  81. package/dist/chat-ui/thread/handoff-cursor.cjs.map +1 -0
  82. package/dist/chat-ui/thread/handoff-cursor.d.ts +57 -0
  83. package/dist/chat-ui/thread/handoff-cursor.js +216 -0
  84. package/dist/chat-ui/thread/handoff-cursor.js.map +1 -0
  85. package/dist/chat-ui/thread/statewire-history-edge.cjs +2 -0
  86. package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
  87. package/dist/chat-ui/thread/statewire-history-edge.d.ts +2 -0
  88. package/dist/chat-ui/thread/statewire-history-edge.js +3 -1
  89. package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
  90. package/dist/index.cjs +2 -0
  91. package/dist/index.cjs.map +1 -1
  92. package/dist/index.d.ts +2 -1
  93. package/dist/index.js +2 -0
  94. package/dist/index.js.map +1 -1
  95. package/dist/keryx-http/src/unconfirmed-update-attributions.cjs +20 -3
  96. package/dist/keryx-http/src/unconfirmed-update-attributions.cjs.map +1 -1
  97. package/dist/keryx-http/src/unconfirmed-update-attributions.d.ts +11 -2
  98. package/dist/keryx-http/src/unconfirmed-update-attributions.js +21 -4
  99. package/dist/keryx-http/src/unconfirmed-update-attributions.js.map +1 -1
  100. package/dist/react/package.json.cjs +1 -1
  101. package/dist/react/package.json.js +1 -1
  102. package/dist/styles.css +1 -1
  103. package/dist/tools/write-todos-tool-ui.cjs +8 -0
  104. package/dist/tools/write-todos-tool-ui.cjs.map +1 -1
  105. package/dist/tools/write-todos-tool-ui.d.ts +2 -0
  106. package/dist/tools/write-todos-tool-ui.js +8 -0
  107. package/dist/tools/write-todos-tool-ui.js.map +1 -1
  108. package/dist/workbench/session-workbench-panes.cjs +1 -8
  109. package/dist/workbench/session-workbench-panes.cjs.map +1 -1
  110. package/dist/workbench/session-workbench-panes.js +2 -9
  111. package/dist/workbench/session-workbench-panes.js.map +1 -1
  112. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"write-todos-tool-ui.cjs","sources":["../../src/tools/write-todos-tool-ui.tsx"],"sourcesContent":["\"use client\";\n\nimport { memo, useEffect, useMemo, useState } from \"react\";\nimport type { ToolCallMessagePartComponent } from \"@assistant-ui/react\";\nimport {\n CheckIcon,\n ChevronDownIcon,\n ListChecksIcon,\n LoaderIcon,\n XIcon,\n} from \"lucide-react\";\nimport { cn } from \"../lib/utils\";\nimport {\n allocatePlanSeq,\n registerPlanCard,\n unregisterPlanCard,\n updatePlanCard,\n useIsLatestPlanCard,\n usePlanScope,\n} from \"./todo-plan-store\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport type PlanStepStatus =\n | \"pending\"\n | \"in_progress\"\n | \"completed\"\n | \"cancelled\"\n | \"failed\";\n\nexport type PlanStep = {\n id?: string;\n content: string;\n status: PlanStepStatus;\n};\n\nexport type TodoPlan = {\n steps: PlanStep[];\n changeDescription?: string;\n};\n\nconst STEP_STATUSES: readonly PlanStepStatus[] = [\n \"pending\",\n \"in_progress\",\n \"completed\",\n \"cancelled\",\n \"failed\",\n];\n\n// ─── Parsing ─────────────────────────────────────────────────────────\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return typeof value === \"object\" && value !== null\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction asStatus(value: unknown): PlanStepStatus {\n return typeof value === \"string\" &&\n (STEP_STATUSES as readonly string[]).includes(value)\n ? (value as PlanStepStatus)\n : \"pending\";\n}\n\nfunction parseSteps(value: unknown): PlanStep[] {\n if (!Array.isArray(value)) return [];\n const steps: PlanStep[] = [];\n for (const raw of value) {\n const record = asRecord(raw);\n if (!record) continue;\n const content = record.content ?? record.task ?? record.title;\n if (typeof content !== \"string\" || content.trim() === \"\") continue;\n steps.push({\n id: typeof record.id === \"string\" ? record.id : undefined,\n content: content.trim(),\n status: asStatus(record.status),\n });\n }\n return steps;\n}\n\nfunction asDescription(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() !== \"\"\n ? value.trim()\n : undefined;\n}\n\nfunction parsePartialSteps(argsText: string | undefined): PlanStep[] {\n if (!argsText) return [];\n const steps: PlanStep[] = [];\n const objectPattern = /\\{(?:[^{}\\\\\"]|\"(?:[^\"\\\\]|\\\\.)*\")*\\}?/g;\n for (const [chunk] of argsText.matchAll(objectPattern)) {\n const contentMatch = /\"(?:content|task|title)\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/.exec(\n chunk,\n );\n if (!contentMatch) continue;\n let content: string;\n try {\n content = JSON.parse(`\"${contentMatch[1]}\"`) as string;\n } catch {\n content = contentMatch[1];\n }\n if (content.trim() === \"\") continue;\n const statusMatch = /\"status\"\\s*:\\s*\"([a-z_]+)\"/.exec(chunk);\n steps.push({\n content: content.trim(),\n status: asStatus(statusMatch?.[1]),\n });\n }\n return steps;\n}\n\nexport function parseTodoPlan({\n args,\n argsText,\n result,\n}: {\n args?: unknown;\n argsText?: string;\n result?: unknown;\n}): TodoPlan {\n const argsRecord = asRecord(args);\n const argSteps = parseSteps(argsRecord?.todos);\n\n let resultRecord = asRecord(result);\n if (!resultRecord && typeof result === \"string\") {\n try {\n resultRecord = asRecord(JSON.parse(result));\n } catch {\n resultRecord = null;\n }\n }\n\n const hasResultTodos = Array.isArray(resultRecord?.todos);\n const steps = hasResultTodos\n ? parseSteps(resultRecord?.todos)\n : argSteps.length > 0\n ? argSteps\n : parsePartialSteps(argsText);\n\n return {\n steps,\n changeDescription:\n asDescription(resultRecord?.description) ??\n asDescription(resultRecord?.todoChangeDescription),\n };\n}\n\n// ─── Presentation ────────────────────────────────────────────────────\n\n/** A plan step's status glyph (its number while pending) — shared with the workbench's Tasks pane. */\nexport function PlanStepMarker({\n status,\n index,\n}: {\n status: PlanStepStatus;\n index: number;\n}) {\n if (status === \"completed\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-emerald-500 text-white\">\n <CheckIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"in_progress\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary\">\n <LoaderIcon className=\"size-3 animate-spin\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"failed\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-destructive/10 text-destructive\">\n <XIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"cancelled\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full border border-border text-muted-foreground\">\n <XIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full border border-dashed border-border text-[9px] font-medium text-muted-foreground\">\n {index + 1}\n </span>\n );\n}\n\nfunction PlanBadge({\n resolved,\n total,\n}: {\n resolved: number;\n total: number;\n}) {\n const isDone = total > 0 && resolved === total;\n\n return (\n <span\n className={cn(\n \"flex size-8 shrink-0 items-center justify-center rounded-lg\",\n isDone && \"bg-emerald-50 text-emerald-600\",\n !isDone && \"bg-primary/10 text-primary\",\n )}\n >\n {isDone ? (\n <CheckIcon className=\"size-4\" strokeWidth={3} />\n ) : (\n <ListChecksIcon className=\"size-4\" />\n )}\n </span>\n );\n}\n\n/**\n * Register this card in its chat's plan registry and report whether it is the\n * newest mounted one. Shared by the visible card (which hides superseded\n * instances) and {@link HiddenWriteTodosToolUI} (which renders nothing but\n * keeps the registry — and so `useLatestTodoPlan` — fed).\n */\nfunction usePlanCardRegistration(plan: TodoPlan, isWriting: boolean): boolean {\n const scope = usePlanScope();\n const [seq] = useState(allocatePlanSeq);\n useEffect(() => {\n registerPlanCard(scope, seq, plan, isWriting);\n return () => unregisterPlanCard(scope, seq);\n // Registration is keyed to this instance's lifetime (and its chat's\n // scope); content updates ride the effect below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [scope, seq]);\n useEffect(() => {\n updatePlanCard(scope, seq, plan, isWriting);\n }, [scope, seq, plan, isWriting]);\n return useIsLatestPlanCard(scope, seq);\n}\n\n/**\n * Inline-invisible `write_todos` for hosts that pin the plan elsewhere:\n * feeds the plan registry exactly like the visible card, renders nothing.\n * Register it via `toolUIs={{ write_todos: HiddenWriteTodosToolUI }}` and\n * read the plan with `useLatestTodoPlan()` — a bare `() => null` override\n * would leave the registry (and the hook) permanently empty.\n */\nconst HiddenWriteTodosToolUIImpl: ToolCallMessagePartComponent = ({\n args,\n argsText,\n result,\n status,\n}) => {\n const plan = useMemo(\n () => parseTodoPlan({ args, argsText, result }),\n [args, argsText, result],\n );\n usePlanCardRegistration(plan, status?.type === \"running\");\n return null;\n};\n\nexport const HiddenWriteTodosToolUI = memo(\n HiddenWriteTodosToolUIImpl,\n) as unknown as ToolCallMessagePartComponent;\nHiddenWriteTodosToolUI.displayName = \"HiddenWriteTodosToolUI\";\n\nconst WriteTodosToolUIImpl: ToolCallMessagePartComponent = ({\n args,\n argsText,\n result,\n status,\n}) => {\n const plan = useMemo(\n () => parseTodoPlan({ args, argsText, result }),\n [args, argsText, result],\n );\n const { steps, changeDescription } = plan;\n const [collapsed, setCollapsed] = useState(false);\n\n // Newest-plan dedupe: a run updates its plan by calling write_todos again,\n // so a conversation stacks cards that all describe the same plan. Only the\n // newest mounted card in this chat's scope renders (see todo-plan-store.ts).\n const isWriting = status?.type === \"running\";\n const isLatest = usePlanCardRegistration(plan, isWriting);\n if (!isLatest) return null;\n\n const completed = steps.filter((s) => s.status === \"completed\").length;\n const stopped = steps.filter(\n (s) => s.status === \"cancelled\" || s.status === \"failed\",\n ).length;\n const current = steps.find((s) => s.status === \"in_progress\");\n const total = steps.length;\n const resolved = completed + stopped;\n const isDone = total > 0 && resolved === total;\n\n if (total === 0) {\n return isWriting ? (\n <div className=\"my-3 flex w-full items-center gap-2 rounded-xl border border-border/60 bg-background px-4 py-3 shadow-sm\">\n <LoaderIcon className=\"size-3.5 animate-spin text-primary\" />\n <span className=\"text-[13px] font-medium text-foreground\">\n Planning...\n </span>\n </div>\n ) : null;\n }\n\n const heading = isWriting\n ? \"Updating plan\"\n : isDone\n ? \"Plan complete\"\n : completed === 0 && !current\n ? \"Plan\"\n : \"Plan in progress\";\n\n const subtitle =\n changeDescription ?? (current ? current.content : undefined);\n\n return (\n <div className=\"my-3 w-full rounded-xl border border-border/60 bg-background shadow-sm\">\n <div className=\"sticky top-0 z-20 overflow-hidden rounded-t-xl bg-background\">\n <button\n type=\"button\"\n onClick={() => setCollapsed((prev) => !prev)}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/30\"\n aria-expanded={!collapsed}\n >\n <PlanBadge resolved={resolved} total={total} />\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex items-baseline gap-2\">\n <span className=\"text-[13px] font-medium text-foreground\">\n {heading}\n </span>\n <span className=\"text-[11px] tabular-nums text-muted-foreground\">\n {resolved}/{total} steps\n </span>\n </span>\n {subtitle && (\n <span className=\"block truncate text-[12px] text-muted-foreground\">\n {subtitle}\n </span>\n )}\n </span>\n <ChevronDownIcon\n className={cn(\n \"size-4 shrink-0 text-muted-foreground transition-transform duration-200\",\n collapsed && \"-rotate-90\",\n )}\n />\n </button>\n\n <div className=\"h-0.5 w-full bg-muted\">\n <div\n className={cn(\n \"h-full transition-all duration-500\",\n isDone ? \"bg-emerald-500\" : \"bg-primary\",\n )}\n style={{ width: `${total > 0 ? (resolved / total) * 100 : 0}%` }}\n />\n </div>\n </div>\n\n {!collapsed && (\n <ol className=\"flex max-h-72 flex-col overflow-y-auto rounded-b-xl border-t border-border/40 py-1\">\n {steps.map((step, index) => (\n <li\n key={step.id ?? `${index}-${step.content}`}\n className={cn(\n \"relative flex items-start gap-2.5 px-4 py-1.5\",\n step.status === \"in_progress\" && \"bg-primary/5\",\n )}\n >\n {index < steps.length - 1 && (\n <span\n aria-hidden\n className=\"absolute left-[calc(1rem+8px)] top-[26px] h-[calc(100%-18px)] w-px bg-border/60\"\n />\n )}\n <span className=\"relative z-10 mt-[3px]\">\n <PlanStepMarker status={step.status} index={index} />\n </span>\n <span\n className={cn(\n \"text-[12.5px] leading-relaxed text-foreground\",\n step.status === \"completed\" &&\n \"text-muted-foreground line-through decoration-muted-foreground/40\",\n step.status === \"cancelled\" &&\n \"text-muted-foreground/70 line-through decoration-muted-foreground/40\",\n step.status === \"failed\" && \"text-destructive\",\n step.status === \"in_progress\" && \"font-medium text-primary\",\n step.status === \"pending\" && \"text-muted-foreground\",\n )}\n >\n {step.content}\n </span>\n </li>\n ))}\n </ol>\n )}\n </div>\n );\n};\n\nexport const WriteTodosToolUI = memo(\n WriteTodosToolUIImpl,\n) as unknown as ToolCallMessagePartComponent;\nWriteTodosToolUI.displayName = \"WriteTodosToolUI\";\n"],"names":["jsx","CheckIcon","LoaderIcon","XIcon","cn","ListChecksIcon","usePlanScope","useState","allocatePlanSeq","useEffect","registerPlanCard","unregisterPlanCard","updatePlanCard","useIsLatestPlanCard","useMemo","memo","jsxs","ChevronDownIcon"],"mappings":"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"}
1
+ {"version":3,"file":"write-todos-tool-ui.cjs","sources":["../../src/tools/write-todos-tool-ui.tsx"],"sourcesContent":["\"use client\";\n\nimport { memo, useEffect, useMemo, useState } from \"react\";\nimport type { ToolCallMessagePartComponent } from \"@assistant-ui/react\";\nimport {\n CheckIcon,\n ChevronDownIcon,\n ListChecksIcon,\n LoaderIcon,\n XIcon,\n} from \"lucide-react\";\nimport { cn } from \"../lib/utils\";\nimport {\n allocatePlanSeq,\n registerPlanCard,\n unregisterPlanCard,\n updatePlanCard,\n useIsLatestPlanCard,\n usePlanScope,\n} from \"./todo-plan-store\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport type PlanStepStatus =\n | \"pending\"\n | \"in_progress\"\n | \"completed\"\n | \"cancelled\"\n | \"failed\";\n\nexport type PlanStep = {\n id?: string;\n content: string;\n status: PlanStepStatus;\n};\n\nexport type TodoPlan = {\n steps: PlanStep[];\n changeDescription?: string;\n};\n\nconst STEP_STATUSES: readonly PlanStepStatus[] = [\n \"pending\",\n \"in_progress\",\n \"completed\",\n \"cancelled\",\n \"failed\",\n];\n\n// ─── Parsing ─────────────────────────────────────────────────────────\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return typeof value === \"object\" && value !== null\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction asStatus(value: unknown): PlanStepStatus {\n return typeof value === \"string\" &&\n (STEP_STATUSES as readonly string[]).includes(value)\n ? (value as PlanStepStatus)\n : \"pending\";\n}\n\nfunction parseSteps(value: unknown): PlanStep[] {\n if (!Array.isArray(value)) return [];\n const steps: PlanStep[] = [];\n for (const raw of value) {\n const record = asRecord(raw);\n if (!record) continue;\n const content = record.content ?? record.task ?? record.title;\n if (typeof content !== \"string\" || content.trim() === \"\") continue;\n steps.push({\n id: typeof record.id === \"string\" ? record.id : undefined,\n content: content.trim(),\n status: asStatus(record.status),\n });\n }\n return steps;\n}\n\nfunction asDescription(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() !== \"\"\n ? value.trim()\n : undefined;\n}\n\nfunction parsePartialSteps(argsText: string | undefined): PlanStep[] {\n if (!argsText) return [];\n const steps: PlanStep[] = [];\n const objectPattern = /\\{(?:[^{}\\\\\"]|\"(?:[^\"\\\\]|\\\\.)*\")*\\}?/g;\n for (const [chunk] of argsText.matchAll(objectPattern)) {\n const contentMatch = /\"(?:content|task|title)\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/.exec(\n chunk,\n );\n if (!contentMatch) continue;\n let content: string;\n try {\n content = JSON.parse(`\"${contentMatch[1]}\"`) as string;\n } catch {\n content = contentMatch[1];\n }\n if (content.trim() === \"\") continue;\n const statusMatch = /\"status\"\\s*:\\s*\"([a-z_]+)\"/.exec(chunk);\n steps.push({\n content: content.trim(),\n status: asStatus(statusMatch?.[1]),\n });\n }\n return steps;\n}\n\nexport function parseTodoPlan({\n args,\n argsText,\n result,\n}: {\n args?: unknown;\n argsText?: string;\n result?: unknown;\n}): TodoPlan {\n const argsRecord = asRecord(args);\n const argSteps = parseSteps(argsRecord?.todos);\n\n let resultRecord = asRecord(result);\n if (!resultRecord && typeof result === \"string\") {\n try {\n resultRecord = asRecord(JSON.parse(result));\n } catch {\n resultRecord = null;\n }\n }\n\n const hasResultTodos = Array.isArray(resultRecord?.todos);\n const steps = hasResultTodos\n ? parseSteps(resultRecord?.todos)\n : argSteps.length > 0\n ? argSteps\n : parsePartialSteps(argsText);\n\n return {\n steps,\n changeDescription:\n asDescription(resultRecord?.description) ??\n asDescription(resultRecord?.todoChangeDescription),\n };\n}\n\n// ─── Presentation ────────────────────────────────────────────────────\n\n/** A plan step's status in words, for screen readers beside its glyph (the Tasks pane, the plan strip). */\nexport const PLAN_STEP_STATUS_LABEL: Record<PlanStepStatus, string> = {\n pending: \"To do\",\n in_progress: \"In progress\",\n completed: \"Done\",\n failed: \"Failed\",\n cancelled: \"Cancelled\",\n};\n\n/** A plan step's status glyph (its number while pending) — shared with the workbench's Tasks pane. */\nexport function PlanStepMarker({\n status,\n index,\n}: {\n status: PlanStepStatus;\n index: number;\n}) {\n if (status === \"completed\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-emerald-500 text-white\">\n <CheckIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"in_progress\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary\">\n <LoaderIcon className=\"size-3 animate-spin\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"failed\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-destructive/10 text-destructive\">\n <XIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"cancelled\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full border border-border text-muted-foreground\">\n <XIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full border border-dashed border-border text-[9px] font-medium text-muted-foreground\">\n {index + 1}\n </span>\n );\n}\n\nfunction PlanBadge({\n resolved,\n total,\n}: {\n resolved: number;\n total: number;\n}) {\n const isDone = total > 0 && resolved === total;\n\n return (\n <span\n className={cn(\n \"flex size-8 shrink-0 items-center justify-center rounded-lg\",\n isDone && \"bg-emerald-50 text-emerald-600\",\n !isDone && \"bg-primary/10 text-primary\",\n )}\n >\n {isDone ? (\n <CheckIcon className=\"size-4\" strokeWidth={3} />\n ) : (\n <ListChecksIcon className=\"size-4\" />\n )}\n </span>\n );\n}\n\n/**\n * Register this card in its chat's plan registry and report whether it is the\n * newest mounted one. Shared by the visible card (which hides superseded\n * instances) and {@link HiddenWriteTodosToolUI} (which renders nothing but\n * keeps the registry — and so `useLatestTodoPlan` — fed).\n */\nfunction usePlanCardRegistration(plan: TodoPlan, isWriting: boolean): boolean {\n const scope = usePlanScope();\n const [seq] = useState(allocatePlanSeq);\n useEffect(() => {\n registerPlanCard(scope, seq, plan, isWriting);\n return () => unregisterPlanCard(scope, seq);\n // Registration is keyed to this instance's lifetime (and its chat's\n // scope); content updates ride the effect below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [scope, seq]);\n useEffect(() => {\n updatePlanCard(scope, seq, plan, isWriting);\n }, [scope, seq, plan, isWriting]);\n return useIsLatestPlanCard(scope, seq);\n}\n\n/**\n * Inline-invisible `write_todos` for hosts that pin the plan elsewhere:\n * feeds the plan registry exactly like the visible card, renders nothing.\n * Register it via `toolUIs={{ write_todos: HiddenWriteTodosToolUI }}` and\n * read the plan with `useLatestTodoPlan()` — a bare `() => null` override\n * would leave the registry (and the hook) permanently empty.\n */\nconst HiddenWriteTodosToolUIImpl: ToolCallMessagePartComponent = ({\n args,\n argsText,\n result,\n status,\n}) => {\n const plan = useMemo(\n () => parseTodoPlan({ args, argsText, result }),\n [args, argsText, result],\n );\n usePlanCardRegistration(plan, status?.type === \"running\");\n return null;\n};\n\nexport const HiddenWriteTodosToolUI = memo(\n HiddenWriteTodosToolUIImpl,\n) as unknown as ToolCallMessagePartComponent;\nHiddenWriteTodosToolUI.displayName = \"HiddenWriteTodosToolUI\";\n\nconst WriteTodosToolUIImpl: ToolCallMessagePartComponent = ({\n args,\n argsText,\n result,\n status,\n}) => {\n const plan = useMemo(\n () => parseTodoPlan({ args, argsText, result }),\n [args, argsText, result],\n );\n const { steps, changeDescription } = plan;\n const [collapsed, setCollapsed] = useState(false);\n\n // Newest-plan dedupe: a run updates its plan by calling write_todos again,\n // so a conversation stacks cards that all describe the same plan. Only the\n // newest mounted card in this chat's scope renders (see todo-plan-store.ts).\n const isWriting = status?.type === \"running\";\n const isLatest = usePlanCardRegistration(plan, isWriting);\n if (!isLatest) return null;\n\n const completed = steps.filter((s) => s.status === \"completed\").length;\n const stopped = steps.filter(\n (s) => s.status === \"cancelled\" || s.status === \"failed\",\n ).length;\n const current = steps.find((s) => s.status === \"in_progress\");\n const total = steps.length;\n const resolved = completed + stopped;\n const isDone = total > 0 && resolved === total;\n\n if (total === 0) {\n return isWriting ? (\n <div className=\"my-3 flex w-full items-center gap-2 rounded-xl border border-border/60 bg-background px-4 py-3 shadow-sm\">\n <LoaderIcon className=\"size-3.5 animate-spin text-primary\" />\n <span className=\"text-[13px] font-medium text-foreground\">\n Planning...\n </span>\n </div>\n ) : null;\n }\n\n const heading = isWriting\n ? \"Updating plan\"\n : isDone\n ? \"Plan complete\"\n : completed === 0 && !current\n ? \"Plan\"\n : \"Plan in progress\";\n\n const subtitle =\n changeDescription ?? (current ? current.content : undefined);\n\n return (\n <div className=\"my-3 w-full rounded-xl border border-border/60 bg-background shadow-sm\">\n <div className=\"sticky top-0 z-20 overflow-hidden rounded-t-xl bg-background\">\n <button\n type=\"button\"\n onClick={() => setCollapsed((prev) => !prev)}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/30\"\n aria-expanded={!collapsed}\n >\n <PlanBadge resolved={resolved} total={total} />\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex items-baseline gap-2\">\n <span className=\"text-[13px] font-medium text-foreground\">\n {heading}\n </span>\n <span className=\"text-[11px] tabular-nums text-muted-foreground\">\n {resolved}/{total} steps\n </span>\n </span>\n {subtitle && (\n <span className=\"block truncate text-[12px] text-muted-foreground\">\n {subtitle}\n </span>\n )}\n </span>\n <ChevronDownIcon\n className={cn(\n \"size-4 shrink-0 text-muted-foreground transition-transform duration-200\",\n collapsed && \"-rotate-90\",\n )}\n />\n </button>\n\n <div className=\"h-0.5 w-full bg-muted\">\n <div\n className={cn(\n \"h-full transition-all duration-500\",\n isDone ? \"bg-emerald-500\" : \"bg-primary\",\n )}\n style={{ width: `${total > 0 ? (resolved / total) * 100 : 0}%` }}\n />\n </div>\n </div>\n\n {!collapsed && (\n <ol className=\"flex max-h-72 flex-col overflow-y-auto rounded-b-xl border-t border-border/40 py-1\">\n {steps.map((step, index) => (\n <li\n key={step.id ?? `${index}-${step.content}`}\n className={cn(\n \"relative flex items-start gap-2.5 px-4 py-1.5\",\n step.status === \"in_progress\" && \"bg-primary/5\",\n )}\n >\n {index < steps.length - 1 && (\n <span\n aria-hidden\n className=\"absolute left-[calc(1rem+8px)] top-[26px] h-[calc(100%-18px)] w-px bg-border/60\"\n />\n )}\n <span className=\"relative z-10 mt-[3px]\">\n <PlanStepMarker status={step.status} index={index} />\n </span>\n <span\n className={cn(\n \"text-[12.5px] leading-relaxed text-foreground\",\n step.status === \"completed\" &&\n \"text-muted-foreground line-through decoration-muted-foreground/40\",\n step.status === \"cancelled\" &&\n \"text-muted-foreground/70 line-through decoration-muted-foreground/40\",\n step.status === \"failed\" && \"text-destructive\",\n step.status === \"in_progress\" && \"font-medium text-primary\",\n step.status === \"pending\" && \"text-muted-foreground\",\n )}\n >\n {step.content}\n </span>\n </li>\n ))}\n </ol>\n )}\n </div>\n );\n};\n\nexport const WriteTodosToolUI = memo(\n WriteTodosToolUIImpl,\n) as unknown as ToolCallMessagePartComponent;\nWriteTodosToolUI.displayName = \"WriteTodosToolUI\";\n"],"names":["jsx","CheckIcon","LoaderIcon","XIcon","cn","ListChecksIcon","usePlanScope","useState","allocatePlanSeq","useEffect","registerPlanCard","unregisterPlanCard","updatePlanCard","useIsLatestPlanCard","useMemo","memo","jsxs","ChevronDownIcon"],"mappings":"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"}
@@ -14,6 +14,8 @@ export declare function parseTodoPlan({ args, argsText, result, }: {
14
14
  argsText?: string;
15
15
  result?: unknown;
16
16
  }): TodoPlan;
17
+ /** A plan step's status in words, for screen readers beside its glyph (the Tasks pane, the plan strip). */
18
+ export declare const PLAN_STEP_STATUS_LABEL: Record<PlanStepStatus, string>;
17
19
  /** A plan step's status glyph (its number while pending) — shared with the workbench's Tasks pane. */
18
20
  export declare function PlanStepMarker({ status, index, }: {
19
21
  status: PlanStepStatus;
@@ -81,6 +81,13 @@ function parseTodoPlan({
81
81
  changeDescription: asDescription(resultRecord == null ? void 0 : resultRecord.description) ?? asDescription(resultRecord == null ? void 0 : resultRecord.todoChangeDescription)
82
82
  };
83
83
  }
84
+ const PLAN_STEP_STATUS_LABEL = {
85
+ pending: "To do",
86
+ in_progress: "In progress",
87
+ completed: "Done",
88
+ failed: "Failed",
89
+ cancelled: "Cancelled"
90
+ };
84
91
  function PlanStepMarker({
85
92
  status,
86
93
  index
@@ -264,6 +271,7 @@ const WriteTodosToolUI = memo(
264
271
  WriteTodosToolUI.displayName = "WriteTodosToolUI";
265
272
  export {
266
273
  HiddenWriteTodosToolUI,
274
+ PLAN_STEP_STATUS_LABEL,
267
275
  PlanStepMarker,
268
276
  WriteTodosToolUI,
269
277
  parseTodoPlan
@@ -1 +1 @@
1
- {"version":3,"file":"write-todos-tool-ui.js","sources":["../../src/tools/write-todos-tool-ui.tsx"],"sourcesContent":["\"use client\";\n\nimport { memo, useEffect, useMemo, useState } from \"react\";\nimport type { ToolCallMessagePartComponent } from \"@assistant-ui/react\";\nimport {\n CheckIcon,\n ChevronDownIcon,\n ListChecksIcon,\n LoaderIcon,\n XIcon,\n} from \"lucide-react\";\nimport { cn } from \"../lib/utils\";\nimport {\n allocatePlanSeq,\n registerPlanCard,\n unregisterPlanCard,\n updatePlanCard,\n useIsLatestPlanCard,\n usePlanScope,\n} from \"./todo-plan-store\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport type PlanStepStatus =\n | \"pending\"\n | \"in_progress\"\n | \"completed\"\n | \"cancelled\"\n | \"failed\";\n\nexport type PlanStep = {\n id?: string;\n content: string;\n status: PlanStepStatus;\n};\n\nexport type TodoPlan = {\n steps: PlanStep[];\n changeDescription?: string;\n};\n\nconst STEP_STATUSES: readonly PlanStepStatus[] = [\n \"pending\",\n \"in_progress\",\n \"completed\",\n \"cancelled\",\n \"failed\",\n];\n\n// ─── Parsing ─────────────────────────────────────────────────────────\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return typeof value === \"object\" && value !== null\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction asStatus(value: unknown): PlanStepStatus {\n return typeof value === \"string\" &&\n (STEP_STATUSES as readonly string[]).includes(value)\n ? (value as PlanStepStatus)\n : \"pending\";\n}\n\nfunction parseSteps(value: unknown): PlanStep[] {\n if (!Array.isArray(value)) return [];\n const steps: PlanStep[] = [];\n for (const raw of value) {\n const record = asRecord(raw);\n if (!record) continue;\n const content = record.content ?? record.task ?? record.title;\n if (typeof content !== \"string\" || content.trim() === \"\") continue;\n steps.push({\n id: typeof record.id === \"string\" ? record.id : undefined,\n content: content.trim(),\n status: asStatus(record.status),\n });\n }\n return steps;\n}\n\nfunction asDescription(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() !== \"\"\n ? value.trim()\n : undefined;\n}\n\nfunction parsePartialSteps(argsText: string | undefined): PlanStep[] {\n if (!argsText) return [];\n const steps: PlanStep[] = [];\n const objectPattern = /\\{(?:[^{}\\\\\"]|\"(?:[^\"\\\\]|\\\\.)*\")*\\}?/g;\n for (const [chunk] of argsText.matchAll(objectPattern)) {\n const contentMatch = /\"(?:content|task|title)\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/.exec(\n chunk,\n );\n if (!contentMatch) continue;\n let content: string;\n try {\n content = JSON.parse(`\"${contentMatch[1]}\"`) as string;\n } catch {\n content = contentMatch[1];\n }\n if (content.trim() === \"\") continue;\n const statusMatch = /\"status\"\\s*:\\s*\"([a-z_]+)\"/.exec(chunk);\n steps.push({\n content: content.trim(),\n status: asStatus(statusMatch?.[1]),\n });\n }\n return steps;\n}\n\nexport function parseTodoPlan({\n args,\n argsText,\n result,\n}: {\n args?: unknown;\n argsText?: string;\n result?: unknown;\n}): TodoPlan {\n const argsRecord = asRecord(args);\n const argSteps = parseSteps(argsRecord?.todos);\n\n let resultRecord = asRecord(result);\n if (!resultRecord && typeof result === \"string\") {\n try {\n resultRecord = asRecord(JSON.parse(result));\n } catch {\n resultRecord = null;\n }\n }\n\n const hasResultTodos = Array.isArray(resultRecord?.todos);\n const steps = hasResultTodos\n ? parseSteps(resultRecord?.todos)\n : argSteps.length > 0\n ? argSteps\n : parsePartialSteps(argsText);\n\n return {\n steps,\n changeDescription:\n asDescription(resultRecord?.description) ??\n asDescription(resultRecord?.todoChangeDescription),\n };\n}\n\n// ─── Presentation ────────────────────────────────────────────────────\n\n/** A plan step's status glyph (its number while pending) — shared with the workbench's Tasks pane. */\nexport function PlanStepMarker({\n status,\n index,\n}: {\n status: PlanStepStatus;\n index: number;\n}) {\n if (status === \"completed\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-emerald-500 text-white\">\n <CheckIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"in_progress\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary\">\n <LoaderIcon className=\"size-3 animate-spin\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"failed\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-destructive/10 text-destructive\">\n <XIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"cancelled\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full border border-border text-muted-foreground\">\n <XIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full border border-dashed border-border text-[9px] font-medium text-muted-foreground\">\n {index + 1}\n </span>\n );\n}\n\nfunction PlanBadge({\n resolved,\n total,\n}: {\n resolved: number;\n total: number;\n}) {\n const isDone = total > 0 && resolved === total;\n\n return (\n <span\n className={cn(\n \"flex size-8 shrink-0 items-center justify-center rounded-lg\",\n isDone && \"bg-emerald-50 text-emerald-600\",\n !isDone && \"bg-primary/10 text-primary\",\n )}\n >\n {isDone ? (\n <CheckIcon className=\"size-4\" strokeWidth={3} />\n ) : (\n <ListChecksIcon className=\"size-4\" />\n )}\n </span>\n );\n}\n\n/**\n * Register this card in its chat's plan registry and report whether it is the\n * newest mounted one. Shared by the visible card (which hides superseded\n * instances) and {@link HiddenWriteTodosToolUI} (which renders nothing but\n * keeps the registry — and so `useLatestTodoPlan` — fed).\n */\nfunction usePlanCardRegistration(plan: TodoPlan, isWriting: boolean): boolean {\n const scope = usePlanScope();\n const [seq] = useState(allocatePlanSeq);\n useEffect(() => {\n registerPlanCard(scope, seq, plan, isWriting);\n return () => unregisterPlanCard(scope, seq);\n // Registration is keyed to this instance's lifetime (and its chat's\n // scope); content updates ride the effect below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [scope, seq]);\n useEffect(() => {\n updatePlanCard(scope, seq, plan, isWriting);\n }, [scope, seq, plan, isWriting]);\n return useIsLatestPlanCard(scope, seq);\n}\n\n/**\n * Inline-invisible `write_todos` for hosts that pin the plan elsewhere:\n * feeds the plan registry exactly like the visible card, renders nothing.\n * Register it via `toolUIs={{ write_todos: HiddenWriteTodosToolUI }}` and\n * read the plan with `useLatestTodoPlan()` — a bare `() => null` override\n * would leave the registry (and the hook) permanently empty.\n */\nconst HiddenWriteTodosToolUIImpl: ToolCallMessagePartComponent = ({\n args,\n argsText,\n result,\n status,\n}) => {\n const plan = useMemo(\n () => parseTodoPlan({ args, argsText, result }),\n [args, argsText, result],\n );\n usePlanCardRegistration(plan, status?.type === \"running\");\n return null;\n};\n\nexport const HiddenWriteTodosToolUI = memo(\n HiddenWriteTodosToolUIImpl,\n) as unknown as ToolCallMessagePartComponent;\nHiddenWriteTodosToolUI.displayName = \"HiddenWriteTodosToolUI\";\n\nconst WriteTodosToolUIImpl: ToolCallMessagePartComponent = ({\n args,\n argsText,\n result,\n status,\n}) => {\n const plan = useMemo(\n () => parseTodoPlan({ args, argsText, result }),\n [args, argsText, result],\n );\n const { steps, changeDescription } = plan;\n const [collapsed, setCollapsed] = useState(false);\n\n // Newest-plan dedupe: a run updates its plan by calling write_todos again,\n // so a conversation stacks cards that all describe the same plan. Only the\n // newest mounted card in this chat's scope renders (see todo-plan-store.ts).\n const isWriting = status?.type === \"running\";\n const isLatest = usePlanCardRegistration(plan, isWriting);\n if (!isLatest) return null;\n\n const completed = steps.filter((s) => s.status === \"completed\").length;\n const stopped = steps.filter(\n (s) => s.status === \"cancelled\" || s.status === \"failed\",\n ).length;\n const current = steps.find((s) => s.status === \"in_progress\");\n const total = steps.length;\n const resolved = completed + stopped;\n const isDone = total > 0 && resolved === total;\n\n if (total === 0) {\n return isWriting ? (\n <div className=\"my-3 flex w-full items-center gap-2 rounded-xl border border-border/60 bg-background px-4 py-3 shadow-sm\">\n <LoaderIcon className=\"size-3.5 animate-spin text-primary\" />\n <span className=\"text-[13px] font-medium text-foreground\">\n Planning...\n </span>\n </div>\n ) : null;\n }\n\n const heading = isWriting\n ? \"Updating plan\"\n : isDone\n ? \"Plan complete\"\n : completed === 0 && !current\n ? \"Plan\"\n : \"Plan in progress\";\n\n const subtitle =\n changeDescription ?? (current ? current.content : undefined);\n\n return (\n <div className=\"my-3 w-full rounded-xl border border-border/60 bg-background shadow-sm\">\n <div className=\"sticky top-0 z-20 overflow-hidden rounded-t-xl bg-background\">\n <button\n type=\"button\"\n onClick={() => setCollapsed((prev) => !prev)}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/30\"\n aria-expanded={!collapsed}\n >\n <PlanBadge resolved={resolved} total={total} />\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex items-baseline gap-2\">\n <span className=\"text-[13px] font-medium text-foreground\">\n {heading}\n </span>\n <span className=\"text-[11px] tabular-nums text-muted-foreground\">\n {resolved}/{total} steps\n </span>\n </span>\n {subtitle && (\n <span className=\"block truncate text-[12px] text-muted-foreground\">\n {subtitle}\n </span>\n )}\n </span>\n <ChevronDownIcon\n className={cn(\n \"size-4 shrink-0 text-muted-foreground transition-transform duration-200\",\n collapsed && \"-rotate-90\",\n )}\n />\n </button>\n\n <div className=\"h-0.5 w-full bg-muted\">\n <div\n className={cn(\n \"h-full transition-all duration-500\",\n isDone ? \"bg-emerald-500\" : \"bg-primary\",\n )}\n style={{ width: `${total > 0 ? (resolved / total) * 100 : 0}%` }}\n />\n </div>\n </div>\n\n {!collapsed && (\n <ol className=\"flex max-h-72 flex-col overflow-y-auto rounded-b-xl border-t border-border/40 py-1\">\n {steps.map((step, index) => (\n <li\n key={step.id ?? `${index}-${step.content}`}\n className={cn(\n \"relative flex items-start gap-2.5 px-4 py-1.5\",\n step.status === \"in_progress\" && \"bg-primary/5\",\n )}\n >\n {index < steps.length - 1 && (\n <span\n aria-hidden\n className=\"absolute left-[calc(1rem+8px)] top-[26px] h-[calc(100%-18px)] w-px bg-border/60\"\n />\n )}\n <span className=\"relative z-10 mt-[3px]\">\n <PlanStepMarker status={step.status} index={index} />\n </span>\n <span\n className={cn(\n \"text-[12.5px] leading-relaxed text-foreground\",\n step.status === \"completed\" &&\n \"text-muted-foreground line-through decoration-muted-foreground/40\",\n step.status === \"cancelled\" &&\n \"text-muted-foreground/70 line-through decoration-muted-foreground/40\",\n step.status === \"failed\" && \"text-destructive\",\n step.status === \"in_progress\" && \"font-medium text-primary\",\n step.status === \"pending\" && \"text-muted-foreground\",\n )}\n >\n {step.content}\n </span>\n </li>\n ))}\n </ol>\n )}\n </div>\n );\n};\n\nexport const WriteTodosToolUI = memo(\n WriteTodosToolUIImpl,\n) as unknown as ToolCallMessagePartComponent;\nWriteTodosToolUI.displayName = \"WriteTodosToolUI\";\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"write-todos-tool-ui.js","sources":["../../src/tools/write-todos-tool-ui.tsx"],"sourcesContent":["\"use client\";\n\nimport { memo, useEffect, useMemo, useState } from \"react\";\nimport type { ToolCallMessagePartComponent } from \"@assistant-ui/react\";\nimport {\n CheckIcon,\n ChevronDownIcon,\n ListChecksIcon,\n LoaderIcon,\n XIcon,\n} from \"lucide-react\";\nimport { cn } from \"../lib/utils\";\nimport {\n allocatePlanSeq,\n registerPlanCard,\n unregisterPlanCard,\n updatePlanCard,\n useIsLatestPlanCard,\n usePlanScope,\n} from \"./todo-plan-store\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\nexport type PlanStepStatus =\n | \"pending\"\n | \"in_progress\"\n | \"completed\"\n | \"cancelled\"\n | \"failed\";\n\nexport type PlanStep = {\n id?: string;\n content: string;\n status: PlanStepStatus;\n};\n\nexport type TodoPlan = {\n steps: PlanStep[];\n changeDescription?: string;\n};\n\nconst STEP_STATUSES: readonly PlanStepStatus[] = [\n \"pending\",\n \"in_progress\",\n \"completed\",\n \"cancelled\",\n \"failed\",\n];\n\n// ─── Parsing ─────────────────────────────────────────────────────────\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return typeof value === \"object\" && value !== null\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction asStatus(value: unknown): PlanStepStatus {\n return typeof value === \"string\" &&\n (STEP_STATUSES as readonly string[]).includes(value)\n ? (value as PlanStepStatus)\n : \"pending\";\n}\n\nfunction parseSteps(value: unknown): PlanStep[] {\n if (!Array.isArray(value)) return [];\n const steps: PlanStep[] = [];\n for (const raw of value) {\n const record = asRecord(raw);\n if (!record) continue;\n const content = record.content ?? record.task ?? record.title;\n if (typeof content !== \"string\" || content.trim() === \"\") continue;\n steps.push({\n id: typeof record.id === \"string\" ? record.id : undefined,\n content: content.trim(),\n status: asStatus(record.status),\n });\n }\n return steps;\n}\n\nfunction asDescription(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() !== \"\"\n ? value.trim()\n : undefined;\n}\n\nfunction parsePartialSteps(argsText: string | undefined): PlanStep[] {\n if (!argsText) return [];\n const steps: PlanStep[] = [];\n const objectPattern = /\\{(?:[^{}\\\\\"]|\"(?:[^\"\\\\]|\\\\.)*\")*\\}?/g;\n for (const [chunk] of argsText.matchAll(objectPattern)) {\n const contentMatch = /\"(?:content|task|title)\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/.exec(\n chunk,\n );\n if (!contentMatch) continue;\n let content: string;\n try {\n content = JSON.parse(`\"${contentMatch[1]}\"`) as string;\n } catch {\n content = contentMatch[1];\n }\n if (content.trim() === \"\") continue;\n const statusMatch = /\"status\"\\s*:\\s*\"([a-z_]+)\"/.exec(chunk);\n steps.push({\n content: content.trim(),\n status: asStatus(statusMatch?.[1]),\n });\n }\n return steps;\n}\n\nexport function parseTodoPlan({\n args,\n argsText,\n result,\n}: {\n args?: unknown;\n argsText?: string;\n result?: unknown;\n}): TodoPlan {\n const argsRecord = asRecord(args);\n const argSteps = parseSteps(argsRecord?.todos);\n\n let resultRecord = asRecord(result);\n if (!resultRecord && typeof result === \"string\") {\n try {\n resultRecord = asRecord(JSON.parse(result));\n } catch {\n resultRecord = null;\n }\n }\n\n const hasResultTodos = Array.isArray(resultRecord?.todos);\n const steps = hasResultTodos\n ? parseSteps(resultRecord?.todos)\n : argSteps.length > 0\n ? argSteps\n : parsePartialSteps(argsText);\n\n return {\n steps,\n changeDescription:\n asDescription(resultRecord?.description) ??\n asDescription(resultRecord?.todoChangeDescription),\n };\n}\n\n// ─── Presentation ────────────────────────────────────────────────────\n\n/** A plan step's status in words, for screen readers beside its glyph (the Tasks pane, the plan strip). */\nexport const PLAN_STEP_STATUS_LABEL: Record<PlanStepStatus, string> = {\n pending: \"To do\",\n in_progress: \"In progress\",\n completed: \"Done\",\n failed: \"Failed\",\n cancelled: \"Cancelled\",\n};\n\n/** A plan step's status glyph (its number while pending) — shared with the workbench's Tasks pane. */\nexport function PlanStepMarker({\n status,\n index,\n}: {\n status: PlanStepStatus;\n index: number;\n}) {\n if (status === \"completed\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-emerald-500 text-white\">\n <CheckIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"in_progress\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary\">\n <LoaderIcon className=\"size-3 animate-spin\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"failed\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full bg-destructive/10 text-destructive\">\n <XIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n if (status === \"cancelled\") {\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full border border-border text-muted-foreground\">\n <XIcon className=\"size-3\" strokeWidth={3} />\n </span>\n );\n }\n return (\n <span className=\"flex size-[18px] shrink-0 items-center justify-center rounded-full border border-dashed border-border text-[9px] font-medium text-muted-foreground\">\n {index + 1}\n </span>\n );\n}\n\nfunction PlanBadge({\n resolved,\n total,\n}: {\n resolved: number;\n total: number;\n}) {\n const isDone = total > 0 && resolved === total;\n\n return (\n <span\n className={cn(\n \"flex size-8 shrink-0 items-center justify-center rounded-lg\",\n isDone && \"bg-emerald-50 text-emerald-600\",\n !isDone && \"bg-primary/10 text-primary\",\n )}\n >\n {isDone ? (\n <CheckIcon className=\"size-4\" strokeWidth={3} />\n ) : (\n <ListChecksIcon className=\"size-4\" />\n )}\n </span>\n );\n}\n\n/**\n * Register this card in its chat's plan registry and report whether it is the\n * newest mounted one. Shared by the visible card (which hides superseded\n * instances) and {@link HiddenWriteTodosToolUI} (which renders nothing but\n * keeps the registry — and so `useLatestTodoPlan` — fed).\n */\nfunction usePlanCardRegistration(plan: TodoPlan, isWriting: boolean): boolean {\n const scope = usePlanScope();\n const [seq] = useState(allocatePlanSeq);\n useEffect(() => {\n registerPlanCard(scope, seq, plan, isWriting);\n return () => unregisterPlanCard(scope, seq);\n // Registration is keyed to this instance's lifetime (and its chat's\n // scope); content updates ride the effect below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [scope, seq]);\n useEffect(() => {\n updatePlanCard(scope, seq, plan, isWriting);\n }, [scope, seq, plan, isWriting]);\n return useIsLatestPlanCard(scope, seq);\n}\n\n/**\n * Inline-invisible `write_todos` for hosts that pin the plan elsewhere:\n * feeds the plan registry exactly like the visible card, renders nothing.\n * Register it via `toolUIs={{ write_todos: HiddenWriteTodosToolUI }}` and\n * read the plan with `useLatestTodoPlan()` — a bare `() => null` override\n * would leave the registry (and the hook) permanently empty.\n */\nconst HiddenWriteTodosToolUIImpl: ToolCallMessagePartComponent = ({\n args,\n argsText,\n result,\n status,\n}) => {\n const plan = useMemo(\n () => parseTodoPlan({ args, argsText, result }),\n [args, argsText, result],\n );\n usePlanCardRegistration(plan, status?.type === \"running\");\n return null;\n};\n\nexport const HiddenWriteTodosToolUI = memo(\n HiddenWriteTodosToolUIImpl,\n) as unknown as ToolCallMessagePartComponent;\nHiddenWriteTodosToolUI.displayName = \"HiddenWriteTodosToolUI\";\n\nconst WriteTodosToolUIImpl: ToolCallMessagePartComponent = ({\n args,\n argsText,\n result,\n status,\n}) => {\n const plan = useMemo(\n () => parseTodoPlan({ args, argsText, result }),\n [args, argsText, result],\n );\n const { steps, changeDescription } = plan;\n const [collapsed, setCollapsed] = useState(false);\n\n // Newest-plan dedupe: a run updates its plan by calling write_todos again,\n // so a conversation stacks cards that all describe the same plan. Only the\n // newest mounted card in this chat's scope renders (see todo-plan-store.ts).\n const isWriting = status?.type === \"running\";\n const isLatest = usePlanCardRegistration(plan, isWriting);\n if (!isLatest) return null;\n\n const completed = steps.filter((s) => s.status === \"completed\").length;\n const stopped = steps.filter(\n (s) => s.status === \"cancelled\" || s.status === \"failed\",\n ).length;\n const current = steps.find((s) => s.status === \"in_progress\");\n const total = steps.length;\n const resolved = completed + stopped;\n const isDone = total > 0 && resolved === total;\n\n if (total === 0) {\n return isWriting ? (\n <div className=\"my-3 flex w-full items-center gap-2 rounded-xl border border-border/60 bg-background px-4 py-3 shadow-sm\">\n <LoaderIcon className=\"size-3.5 animate-spin text-primary\" />\n <span className=\"text-[13px] font-medium text-foreground\">\n Planning...\n </span>\n </div>\n ) : null;\n }\n\n const heading = isWriting\n ? \"Updating plan\"\n : isDone\n ? \"Plan complete\"\n : completed === 0 && !current\n ? \"Plan\"\n : \"Plan in progress\";\n\n const subtitle =\n changeDescription ?? (current ? current.content : undefined);\n\n return (\n <div className=\"my-3 w-full rounded-xl border border-border/60 bg-background shadow-sm\">\n <div className=\"sticky top-0 z-20 overflow-hidden rounded-t-xl bg-background\">\n <button\n type=\"button\"\n onClick={() => setCollapsed((prev) => !prev)}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/30\"\n aria-expanded={!collapsed}\n >\n <PlanBadge resolved={resolved} total={total} />\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex items-baseline gap-2\">\n <span className=\"text-[13px] font-medium text-foreground\">\n {heading}\n </span>\n <span className=\"text-[11px] tabular-nums text-muted-foreground\">\n {resolved}/{total} steps\n </span>\n </span>\n {subtitle && (\n <span className=\"block truncate text-[12px] text-muted-foreground\">\n {subtitle}\n </span>\n )}\n </span>\n <ChevronDownIcon\n className={cn(\n \"size-4 shrink-0 text-muted-foreground transition-transform duration-200\",\n collapsed && \"-rotate-90\",\n )}\n />\n </button>\n\n <div className=\"h-0.5 w-full bg-muted\">\n <div\n className={cn(\n \"h-full transition-all duration-500\",\n isDone ? \"bg-emerald-500\" : \"bg-primary\",\n )}\n style={{ width: `${total > 0 ? (resolved / total) * 100 : 0}%` }}\n />\n </div>\n </div>\n\n {!collapsed && (\n <ol className=\"flex max-h-72 flex-col overflow-y-auto rounded-b-xl border-t border-border/40 py-1\">\n {steps.map((step, index) => (\n <li\n key={step.id ?? `${index}-${step.content}`}\n className={cn(\n \"relative flex items-start gap-2.5 px-4 py-1.5\",\n step.status === \"in_progress\" && \"bg-primary/5\",\n )}\n >\n {index < steps.length - 1 && (\n <span\n aria-hidden\n className=\"absolute left-[calc(1rem+8px)] top-[26px] h-[calc(100%-18px)] w-px bg-border/60\"\n />\n )}\n <span className=\"relative z-10 mt-[3px]\">\n <PlanStepMarker status={step.status} index={index} />\n </span>\n <span\n className={cn(\n \"text-[12.5px] leading-relaxed text-foreground\",\n step.status === \"completed\" &&\n \"text-muted-foreground line-through decoration-muted-foreground/40\",\n step.status === \"cancelled\" &&\n \"text-muted-foreground/70 line-through decoration-muted-foreground/40\",\n step.status === \"failed\" && \"text-destructive\",\n step.status === \"in_progress\" && \"font-medium text-primary\",\n step.status === \"pending\" && \"text-muted-foreground\",\n )}\n >\n {step.content}\n </span>\n </li>\n ))}\n </ol>\n )}\n </div>\n );\n};\n\nexport const WriteTodosToolUI = memo(\n WriteTodosToolUIImpl,\n) as unknown as ToolCallMessagePartComponent;\nWriteTodosToolUI.displayName = \"WriteTodosToolUI\";\n"],"names":[],"mappings":"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"}
@@ -51,13 +51,6 @@ const RequiresStatewireThread = ({
51
51
  }
52
52
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
53
53
  };
54
- const TASK_STATUS_LABEL = {
55
- pending: "To do",
56
- in_progress: "In progress",
57
- completed: "Done",
58
- failed: "Failed",
59
- cancelled: "Cancelled"
60
- };
61
54
  const TasksPaneContent = () => {
62
55
  const stateTodos = reactStatewire.useStatewireState((state) => statewireState.readDeepAgentStreamValues(state).todos);
63
56
  const messages = react.useAuiState((state) => state.thread.messages);
@@ -94,7 +87,7 @@ const TasksPaneContent = () => {
94
87
  /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "size-[18px] shrink-0 rounded-full border border-border border-dashed" })
95
88
  ) : /* @__PURE__ */ jsxRuntime.jsx(writeTodosToolUi.PlanStepMarker, { status: step.status, index }),
96
89
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sr-only", children: [
97
- TASK_STATUS_LABEL[step.status],
90
+ writeTodosToolUi.PLAN_STEP_STATUS_LABEL[step.status],
98
91
  ": "
99
92
  ] }),
100
93
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1 +1 @@
1
- {"version":3,"file":"session-workbench-panes.cjs","sources":["../../src/workbench/session-workbench-panes.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * The built-in panes of `SessionWorkbench` — Tasks, Shell, Computer, Pull\n * requests and Attachments. Each reads one source:\n *\n * - Tasks: the thread state's `todos` channel (the deepagents plan), else the\n * newest `write_todos` call in the loaded transcript.\n * - Shell: every `execute` call in the loaded transcript; while the thread has\n * older history the chat has not loaded, it says so and loads the next page\n * on request (`StatewireHistoryContext`).\n * - Computer / Attachments: the session's workspace\n * (`GET /api/conversations/threads/{id}/workspace`), plus the computer's\n * status and SSH target; the Computer pane also shows the computer's screen\n * live (`ComputerLiveDesktop`).\n * - Pull requests: the thread list's live pull-request state for the session.\n */\nimport { useAuiState } from '@assistant-ui/react';\nimport { useStatewireState } from '@assistant-ui/react-statewire';\nimport {\n type ConversationPullRequest,\n hasStatewireThreadExtras,\n readDeepAgentStreamValues,\n} from '@athenaintel/agent-runtime';\nimport {\n AlertCircleIcon,\n BookOpenIcon,\n CheckIcon,\n CircleDashedIcon,\n CopyIcon,\n ExternalLinkIcon,\n FileIcon,\n FileSpreadsheetIcon,\n FileTextIcon,\n FolderIcon,\n GitPullRequestIcon,\n ImageIcon,\n ListChecksIcon,\n LoaderIcon,\n MonitorIcon,\n PaperclipIcon,\n PresentationIcon,\n SearchIcon,\n TerminalIcon,\n XIcon,\n} from 'lucide-react';\nimport { type FC, type ReactNode, useMemo, useState } from 'react';\nimport { type AssetType as AssetPanelType, useAssetPanelStore } from '../assets/asset-panel-store';\nimport { athenaLinkAppUrl, createAthenaSpacesUrl } from '../lib/athena-urls';\nimport { cn } from '../lib/utils';\nimport { useAthenaConfig } from '../provider/AthenaContext';\nimport {\n type StatewireHistoryState,\n useStatewireHistory,\n} from '../provider/statewire-history-context';\nimport { PR_CHECKS_LABEL, PR_STATE_CLASS, PR_STATE_ICON } from '../threads/pull-request-display';\nimport {\n orderedPullRequests,\n pullRequestChipState,\n relativeTime,\n} from '../threads/session-list-model';\nimport { useAthenaStatewireThreadList } from '../threads/statewire-thread-list';\nimport { useAthenaSessionAttention } from '../threads/use-athena-session-attention';\nimport { PlanStepMarker, type PlanStepStatus } from '../tools/write-todos-tool-ui';\nimport {\n type SessionWorkbenchPane,\n useSessionWorkbench,\n useSessionWorkbenchWorkspace,\n} from './session-workbench-context';\nimport {\n assetPanelTypeOf,\n assetTypeLabel,\n computerStateBadge,\n deriveShellRows,\n deriveTaskSteps,\n filterShellRows,\n reviewDecisionLabel,\n type ShellRow,\n type ShellRowStatus,\n taskProgress,\n workspaceComputers,\n} from './session-workbench-model';\nimport { ComputerLiveDesktop } from './ComputerLiveDesktop';\nimport type { SessionWorkspaceAsset } from './session-workspace-api';\nimport { useAthenaComputerSshAccess, useAthenaComputerStatus } from './use-session-workspace';\n\n// ─── Shared pieces ───────────────────────────────────────────────────\n\nconst FOCUS_RING_CLASS =\n 'outline-none focus-visible:ring-2 focus-visible:ring-ring/60 focus-visible:ring-offset-0';\n\nconst PaneEmpty: FC<{\n icon: FC<{ className?: string }>;\n title: string;\n children?: ReactNode;\n}> = ({ icon: Icon, title, children }) => (\n <div className=\"flex h-full min-h-40 flex-col items-center justify-center gap-2 px-6 py-10 text-center\">\n <Icon className=\"size-5 text-muted-foreground/70\" />\n <p className=\"font-medium text-foreground text-sm\">{title}</p>\n {children ? <p className=\"max-w-64 text-muted-foreground text-xs leading-snug\">{children}</p> : null}\n </div>\n);\n\nconst PaneError: FC<{ error: Error }> = ({ error }) => (\n <div role=\"alert\" className=\"m-3 flex items-start gap-2 rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-red-900 text-xs\">\n <AlertCircleIcon className=\"mt-0.5 size-3.5 shrink-0\" aria-hidden />\n <span className=\"min-w-0 flex-1 break-words\">{error.message}</span>\n </div>\n);\n\nconst PaneSkeleton: FC = () => (\n <div className=\"flex flex-col gap-2 p-3\" aria-busy=\"true\" aria-label=\"Loading\">\n {[0, 1, 2].map((row) => (\n <div key={row} className=\"h-10 animate-pulse rounded-lg bg-muted\" />\n ))}\n </div>\n);\n\nconst SectionHeading: FC<{ children: ReactNode; trailing?: ReactNode }> = ({ children, trailing }) => (\n <div className=\"flex items-center justify-between gap-2 px-3 pt-3 pb-1.5\">\n <h3 className=\"font-medium text-[11px] text-muted-foreground uppercase tracking-wide\">{children}</h3>\n {trailing}\n </div>\n);\n\n/** The `title` tooltip for an ISO time: the full local date and time. */\nfunction fullTime(iso: string | null): string | undefined {\n if (!iso) return undefined;\n const time = Date.parse(iso);\n return Number.isNaN(time) ? undefined : new Date(time).toLocaleString();\n}\n\n/** Statewire-only panes read the transcript and thread state; elsewhere they say so. */\nconst RequiresStatewireThread: FC<{ children: ReactNode; icon: FC<{ className?: string }> }> = ({\n children,\n icon,\n}) => {\n const extras = useAuiState((state) => state.thread.extras);\n if (!hasStatewireThreadExtras(extras)) {\n return <PaneEmpty icon={icon} title=\"No live session\">Open a session to see this pane.</PaneEmpty>;\n }\n return <>{children}</>;\n};\n\n// ─── Tasks ───────────────────────────────────────────────────────────\n\nconst TASK_STATUS_LABEL: Record<PlanStepStatus, string> = {\n pending: 'To do',\n in_progress: 'In progress',\n completed: 'Done',\n failed: 'Failed',\n cancelled: 'Cancelled',\n};\n\nconst TasksPaneContent: FC = () => {\n const stateTodos = useStatewireState((state) => readDeepAgentStreamValues(state).todos);\n const messages = useAuiState((state) => state.thread.messages);\n const progress = useMemo(\n () => taskProgress(deriveTaskSteps({ stateTodos, messages })),\n [stateTodos, messages],\n );\n if (progress.total === 0) {\n return (\n <PaneEmpty icon={ListChecksIcon} title=\"No plan yet\">\n When the agent writes a plan, its steps show here as it works through them.\n </PaneEmpty>\n );\n }\n const percent = Math.round((progress.done / progress.total) * 100);\n return (\n <div className=\"flex flex-col\">\n <SectionHeading\n trailing={\n <span className=\"font-medium text-foreground text-xs tabular-nums\">\n {progress.done}/{progress.total} done\n </span>\n }\n >\n Plan\n </SectionHeading>\n <div className=\"mx-3 mb-2 h-1 overflow-hidden rounded-full bg-muted\">\n {/* A runtime percentage is the one width a static utility cannot express. */}\n <div className=\"h-full rounded-full bg-primary transition-all\" style={{ width: `${percent}%` }} />\n </div>\n <ol className=\"flex flex-col pb-3\" aria-label=\"Plan steps\">\n {progress.steps.map((step, index) => (\n <li\n key={step.id ?? `${index}:${step.content}`}\n data-status={step.status}\n className=\"flex items-start gap-2.5 px-3 py-1.5\"\n >\n {step.status === 'pending' ? (\n // The row already carries its number; a pending marker repeating it reads \"11 11.\".\n <span aria-hidden className=\"size-[18px] shrink-0 rounded-full border border-border border-dashed\" />\n ) : (\n <PlanStepMarker status={step.status} index={index} />\n )}\n <span className=\"sr-only\">{TASK_STATUS_LABEL[step.status]}: </span>\n <span\n className={cn(\n 'min-w-0 flex-1 text-sm leading-[18px]',\n step.status === 'completed' && 'text-muted-foreground line-through',\n step.status === 'in_progress' && 'font-medium text-foreground',\n step.status === 'failed' && 'text-destructive',\n step.status === 'cancelled' && 'text-muted-foreground line-through',\n )}\n >\n <span className=\"mr-1.5 text-muted-foreground tabular-nums\">{index + 1}.</span>\n {step.content}\n </span>\n </li>\n ))}\n </ol>\n </div>\n );\n};\n\nexport const SessionTasksPane: FC = () => (\n <RequiresStatewireThread icon={ListChecksIcon}>\n <TasksPaneContent />\n </RequiresStatewireThread>\n);\n\n// ─── Shell ───────────────────────────────────────────────────────────\n\nconst SHELL_STATUS_LABEL: Record<ShellRowStatus, string> = {\n running: 'Running',\n succeeded: 'Succeeded',\n failed: 'Failed',\n unknown: 'Finished',\n};\n\nconst ShellStatusIcon: FC<{ status: ShellRowStatus }> = ({ status }) => {\n const label = SHELL_STATUS_LABEL[status];\n if (status === 'running') {\n return <LoaderIcon className=\"mt-0.5 size-3.5 shrink-0 animate-spin text-primary\" aria-label={label} />;\n }\n if (status === 'succeeded') {\n return <CheckIcon className=\"mt-0.5 size-3.5 shrink-0 text-emerald-600\" aria-label={label} />;\n }\n if (status === 'failed') {\n return <XIcon className=\"mt-0.5 size-3.5 shrink-0 text-red-600\" aria-label={label} />;\n }\n return <CircleDashedIcon className=\"mt-0.5 size-3.5 shrink-0 text-muted-foreground\" aria-label={label} />;\n};\n\nfunction shellMeta(row: ShellRow): string {\n const parts = [row.machine.label];\n if (row.exitCode !== null) parts.push(`exit ${row.exitCode}`);\n if (row.createdAt) parts.push(relativeTime(row.createdAt));\n return parts.join(' · ');\n}\n\nconst ShellRowItem: FC<{ row: ShellRow; expanded: boolean; onToggle: () => void }> = ({\n row,\n expanded,\n onToggle,\n}) => {\n const panelId = `athena-shell-${row.id}`;\n return (\n <li data-status={row.status} className=\"border-border/60 border-b last:border-b-0\">\n <button\n type=\"button\"\n aria-expanded={expanded}\n aria-controls={panelId}\n onClick={onToggle}\n className={cn(\n 'flex w-full items-start gap-2 px-3 py-2 text-left transition-colors hover:bg-muted/60',\n FOCUS_RING_CLASS,\n )}\n >\n <ShellStatusIcon status={row.status} />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate font-mono text-foreground text-xs\">\n <span className=\"text-muted-foreground\">$ </span>\n {row.firstLine || '(empty command)'}\n {row.extraLines > 0 ? (\n <span className=\"text-muted-foreground\"> (+{row.extraLines} lines)</span>\n ) : null}\n </span>\n <span className=\"block truncate text-[11px] text-muted-foreground\" title={fullTime(row.createdAt)}>\n {shellMeta(row)}\n </span>\n </span>\n </button>\n {expanded ? (\n <div id={panelId} className=\"px-3 pb-2.5\">\n <pre\n aria-label=\"Command\"\n className=\"mb-1.5 max-h-40 overflow-auto whitespace-pre-wrap break-all rounded-md border border-border/60 px-2 py-1.5 font-mono text-[11px] text-foreground\"\n >\n {row.command}\n </pre>\n <pre className=\"max-h-72 overflow-auto whitespace-pre-wrap break-all rounded-md bg-muted px-2 py-1.5 font-mono text-[11px] text-foreground\">\n {row.status === 'running' ? 'Running…' : row.output || '(no output)'}\n </pre>\n {row.outputPartial ? (\n <p className=\"pt-1 text-[11px] text-muted-foreground\">\n Output shortened — the full output is on the command's card in the chat.\n </p>\n ) : null}\n </div>\n ) : null}\n </li>\n );\n};\n\n/**\n * The commands below come from the part of the transcript the chat has\n * loaded; earlier ones arrive a page at a time, through the chat's own history\n * edge (the same page it loads when the reader scrolls up).\n */\nconst ShellHistoryFooter: FC<{ history: StatewireHistoryState; empty: boolean }> = ({\n history,\n empty,\n}) => {\n const { canLoadOlder, loading, loadOlder } = history;\n return (\n <div className=\"flex flex-col items-center gap-2 px-3 py-4 text-center\">\n <p className=\"text-[11px] text-muted-foreground\">\n {empty\n ? 'No commands in the part of this session loaded so far.'\n : 'Earlier commands are in the part of this session not loaded yet.'}\n </p>\n {canLoadOlder && loadOlder ? (\n <button\n type=\"button\"\n onClick={loadOlder}\n disabled={loading}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border border-border px-2 py-1 font-medium text-xs transition-colors hover:bg-muted disabled:opacity-60',\n FOCUS_RING_CLASS,\n )}\n >\n {loading ? <LoaderIcon className=\"size-3.5 animate-spin\" aria-hidden /> : null}\n {loading ? 'Loading earlier commands…' : 'Load earlier commands'}\n </button>\n ) : (\n <p className=\"text-[11px] text-muted-foreground\">\n The rest is further back than one view of the conversation loads.\n </p>\n )}\n </div>\n );\n};\n\nconst ShellPaneContent: FC = () => {\n const { threadId } = useSessionWorkbench();\n const messages = useAuiState((state) => state.thread.messages);\n const workspace = useSessionWorkbenchWorkspace().data ?? null;\n const history = useStatewireHistory(threadId);\n const partial = history?.hasOlder === true;\n const rows = useMemo(() => deriveShellRows({ messages, workspace }), [messages, workspace]);\n const [query, setQuery] = useState('');\n const [expanded, setExpanded] = useState<ReadonlySet<string>>(() => new Set());\n const visible = useMemo(() => filterShellRows(rows, query), [rows, query]);\n const toggle = (id: string) =>\n setExpanded((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n\n if (rows.length === 0) {\n if (partial && history) return <ShellHistoryFooter history={history} empty />;\n return (\n <PaneEmpty icon={TerminalIcon} title=\"No commands yet\">\n Every shell command the agent runs shows here with where it ran and how it ended.\n </PaneEmpty>\n );\n }\n return (\n <div className=\"flex flex-col\">\n <div className=\"sticky top-0 z-10 border-border/60 border-b bg-background px-3 py-2\">\n <label className=\"flex items-center gap-2 rounded-md border border-border bg-background px-2 py-1 focus-within:ring-2 focus-within:ring-ring/40\">\n <SearchIcon className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n <input\n type=\"search\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder=\"Search commands\"\n aria-label=\"Search commands\"\n className=\"min-w-0 flex-1 bg-transparent text-xs outline-none placeholder:text-muted-foreground\"\n />\n </label>\n <p className=\"pt-1.5 text-[11px] text-muted-foreground\" aria-live=\"polite\">\n {query.trim()\n ? `${visible.length} of ${rows.length} commands`\n : `${rows.length} command${rows.length === 1 ? '' : 's'}`}\n {partial ? ' so far' : ''}\n </p>\n </div>\n {visible.length === 0 ? (\n <p className=\"px-3 py-6 text-center text-muted-foreground text-xs\">No command matches.</p>\n ) : (\n <ul aria-label=\"Shell commands\">\n {visible.map((row) => (\n <ShellRowItem\n key={row.id}\n row={row}\n expanded={expanded.has(row.id)}\n onToggle={() => toggle(row.id)}\n />\n ))}\n </ul>\n )}\n {partial && history ? <ShellHistoryFooter history={history} empty={false} /> : null}\n </div>\n );\n};\n\nexport const SessionShellPane: FC = () => (\n <RequiresStatewireThread icon={TerminalIcon}>\n <ShellPaneContent />\n </RequiresStatewireThread>\n);\n\n// ─── Computer ────────────────────────────────────────────────────────\n\nconst ComputerStateBadge: FC<{ assetId: string }> = ({ assetId }) => {\n const status = useAthenaComputerStatus({ assetId });\n const badge = status.data ? computerStateBadge(status.data) : null;\n if (!badge) return null;\n return (\n <span\n data-state={status.data?.state}\n className=\"inline-flex items-center gap-1.5 rounded-full border border-border px-2 py-0.5 text-[11px] text-muted-foreground\"\n >\n <span\n aria-hidden\n className={cn(\n 'size-1.5 rounded-full',\n badge.tone === 'running' && 'bg-emerald-500',\n badge.tone === 'transition' && 'animate-pulse bg-amber-500',\n badge.tone === 'stopped' && 'bg-muted-foreground/50',\n badge.tone === 'error' && 'bg-red-500',\n )}\n />\n {badge.label}\n </span>\n );\n};\n\nconst CopySshCommand: FC<{ assetId: string }> = ({ assetId }) => {\n const ssh = useAthenaComputerSshAccess({ assetId });\n const [copied, setCopied] = useState(false);\n const command = ssh.data?.command;\n if (!command) return null;\n const copy = () => {\n void navigator.clipboard?.writeText(command).then(() => {\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n });\n };\n return (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex items-center gap-1.5\">\n <code className=\"min-w-0 flex-1 truncate rounded-md bg-muted px-2 py-1 font-mono text-[11px] text-foreground\" title={command}>\n {command}\n </code>\n <button\n type=\"button\"\n onClick={copy}\n className={cn(\n 'inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2 py-1 font-medium text-xs transition-colors hover:bg-muted',\n FOCUS_RING_CLASS,\n )}\n >\n {copied ? <CheckIcon className=\"size-3.5\" aria-hidden /> : <CopyIcon className=\"size-3.5\" aria-hidden />}\n {copied ? 'Copied' : 'Copy SSH command'}\n </button>\n </div>\n {ssh.data?.keyAuthEnabled === false ? (\n <p className=\"text-[11px] text-muted-foreground\">\n This environment's SSH gateway takes temporary tokens only; create one from the computer in Athena.\n </p>\n ) : null}\n </div>\n );\n};\n\nfunction useAssetHref(assetId: string): string | null {\n const { appUrl, backendUrl } = useAthenaConfig();\n return useMemo(() => {\n const pageOrigin = typeof window === 'undefined' ? null : window.location.origin;\n try {\n return createAthenaSpacesUrl({\n appUrl: athenaLinkAppUrl({ appUrl, backendUrl, pageOrigin }),\n assetIds: assetId,\n });\n } catch {\n return null;\n }\n }, [appUrl, backendUrl, assetId]);\n}\n\nconst OpenInAthenaLink: FC<{ assetId: string; label?: string; compact?: boolean }> = ({\n assetId,\n label = 'Open in Athena',\n compact = false,\n}) => {\n const href = useAssetHref(assetId);\n if (!href) return null;\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={compact ? label : undefined}\n title={compact ? label : undefined}\n className={cn(\n 'inline-flex shrink-0 items-center gap-1 rounded-md font-medium text-xs transition-colors hover:bg-muted',\n compact ? 'p-1 text-muted-foreground hover:text-foreground' : 'border border-border px-2 py-1',\n FOCUS_RING_CLASS,\n )}\n >\n <ExternalLinkIcon className=\"size-3.5\" aria-hidden />\n {compact ? null : label}\n </a>\n );\n};\n\nconst ComputerCard: FC<{ computer: SessionWorkspaceAsset }> = ({ computer }) => (\n <article\n aria-label={computer.title ?? computer.assetId}\n className=\"mx-3 mt-3 flex flex-col gap-3 rounded-xl border border-border p-3\"\n >\n <div className=\"flex items-start gap-2.5\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground\">\n <MonitorIcon className=\"size-4\" aria-hidden />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-medium text-foreground text-sm\">{computer.title ?? computer.assetId}</p>\n <p className=\"truncate text-[11px] text-muted-foreground\">\n {computer.role === 'session_computer' ? 'Session computer' : 'Used by this session'}\n {computer.updatedAt ? ` · updated ${relativeTime(computer.updatedAt)}` : ''}\n </p>\n </div>\n <ComputerStateBadge assetId={computer.assetId} />\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <OpenInAthenaLink assetId={computer.assetId} />\n </div>\n <CopySshCommand assetId={computer.assetId} />\n </article>\n);\n\nexport const SessionComputerPane: FC = () => {\n const { isPersisted } = useSessionWorkbench();\n const workspace = useSessionWorkbenchWorkspace();\n if (!isPersisted) {\n return (\n <PaneEmpty icon={MonitorIcon} title=\"No computer yet\">\n The computer this session works on shows here once it starts.\n </PaneEmpty>\n );\n }\n if (workspace.isError) return <PaneError error={workspace.error} />;\n if (!workspace.data) return <PaneSkeleton />;\n const { primary, others } = workspaceComputers(workspace.data);\n if (!primary) {\n return (\n <PaneEmpty icon={MonitorIcon} title=\"No computer used yet\">\n When the agent runs commands on a computer, it shows here with a link and its SSH command.\n </PaneEmpty>\n );\n }\n return (\n <div className=\"flex flex-col pb-3\">\n <ComputerLiveDesktop assetId={primary.assetId} className=\"mx-3 mt-3\" />\n <ComputerCard computer={primary} />\n {others.length > 0 ? (\n <>\n <SectionHeading>Also used</SectionHeading>\n <ul aria-label=\"Other computers\">\n {others.map((computer) => (\n <li key={computer.assetId} className=\"flex items-center gap-2 px-3 py-1.5\">\n <MonitorIcon className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 flex-1 truncate text-foreground text-sm\">\n {computer.title ?? computer.assetId}\n </span>\n <OpenInAthenaLink assetId={computer.assetId} compact label={`Open ${computer.title ?? 'computer'} in Athena`} />\n </li>\n ))}\n </ul>\n </>\n ) : null}\n </div>\n );\n};\n\n// ─── Pull requests ───────────────────────────────────────────────────\n\nconst PullRequestRow: FC<{ pullRequest: ConversationPullRequest }> = ({ pullRequest }) => {\n const state = pullRequestChipState(pullRequest);\n const Icon = PR_STATE_ICON[state];\n const meta = [\n `${pullRequest.repoFullName}#${pullRequest.number}`,\n state,\n PR_CHECKS_LABEL[pullRequest.checksState],\n reviewDecisionLabel(pullRequest.reviewDecision),\n ]\n .filter(Boolean)\n .join(' · ');\n return (\n <li>\n <a\n href={pullRequest.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-pr-state={state}\n className={cn('flex items-start gap-2.5 px-3 py-2 transition-colors hover:bg-muted/60', FOCUS_RING_CLASS)}\n >\n <Icon className={cn('mt-0.5 size-4 shrink-0', PR_STATE_CLASS[state])} aria-hidden />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block text-foreground text-sm leading-snug\">\n {pullRequest.title || `#${pullRequest.number}`}\n </span>\n <span className=\"block truncate text-[11px] text-muted-foreground\">{meta}</span>\n </span>\n <ExternalLinkIcon className=\"mt-0.5 size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n </a>\n </li>\n );\n};\n\nexport const SessionPullRequestsPane: FC = () => {\n const { threadId, isPersisted } = useSessionWorkbench();\n const list = useAthenaStatewireThreadList();\n const attention = useAthenaSessionAttention(threadId ?? undefined);\n if (!list) {\n return (\n <PaneEmpty icon={GitPullRequestIcon} title=\"Pull requests need the session list\">\n Mount the provider with enableThreadList to see the pull requests a session opened.\n </PaneEmpty>\n );\n }\n const pullRequests = isPersisted ? (attention?.pullRequests ?? []) : [];\n if (attention?.pullRequests === null && isPersisted) {\n return (\n <PaneEmpty icon={GitPullRequestIcon} title=\"Pull requests unavailable\">\n GitHub could not be read for this session right now.\n </PaneEmpty>\n );\n }\n if (pullRequests.length === 0) {\n return (\n <PaneEmpty icon={GitPullRequestIcon} title=\"No pull requests\">\n Pull requests the agent opens show here with their checks and review state.\n </PaneEmpty>\n );\n }\n const ordered = orderedPullRequests(pullRequests);\n return (\n <div className=\"flex flex-col pb-2\">\n <SectionHeading\n trailing={<span className=\"text-muted-foreground text-xs tabular-nums\">{ordered.length}</span>}\n >\n Pull requests\n </SectionHeading>\n <ul aria-label=\"Pull requests\">\n {ordered.map((pullRequest) => (\n <PullRequestRow key={pullRequest.url} pullRequest={pullRequest} />\n ))}\n </ul>\n </div>\n );\n};\n\n// ─── Attachments ─────────────────────────────────────────────────────\n\nconst ASSET_ICON: Record<string, FC<{ className?: string }>> = {\n spreadsheet: FileSpreadsheetIcon,\n document: FileTextIcon,\n presentation: PresentationIcon,\n presentation_pptx_studio: PresentationIcon,\n pptx: PresentationIcon,\n notebook: BookOpenIcon,\n image: ImageIcon,\n folder: FolderIcon,\n markdown: FileTextIcon,\n pdf: FileTextIcon,\n txt: FileTextIcon,\n};\n\nconst AttachmentRow: FC<{ asset: SessionWorkspaceAsset; canOpenHere: boolean }> = ({\n asset,\n canOpenHere,\n}) => {\n const openAsset = useAssetPanelStore((state) => state.openAsset);\n const Icon = ASSET_ICON[asset.assetType] ?? FileIcon;\n const panelType: AssetPanelType | null = assetPanelTypeOf(asset.assetType);\n const title = asset.title ?? asset.assetId;\n const meta = [\n assetTypeLabel(asset.assetType),\n asset.updatedAt ? `updated ${relativeTime(asset.updatedAt)}` : null,\n ]\n .filter(Boolean)\n .join(' · ');\n const body = (\n <>\n <Icon className=\"mt-0.5 size-4 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-foreground text-sm\">{title}</span>\n <span className=\"block truncate text-[11px] text-muted-foreground\" title={fullTime(asset.updatedAt)}>\n {meta}\n </span>\n </span>\n </>\n );\n return (\n <li className=\"flex items-start gap-1 pr-2\">\n {canOpenHere && panelType ? (\n <button\n type=\"button\"\n onClick={() => openAsset(asset.assetId, { name: title, type: panelType })}\n className={cn(\n 'flex min-w-0 flex-1 items-start gap-2.5 px-3 py-2 text-left transition-colors hover:bg-muted/60',\n FOCUS_RING_CLASS,\n )}\n >\n {body}\n </button>\n ) : (\n <div className=\"flex min-w-0 flex-1 items-start gap-2.5 px-3 py-2\">{body}</div>\n )}\n <span className=\"pt-1.5\">\n <OpenInAthenaLink assetId={asset.assetId} compact label={`Open ${title} in Athena`} />\n </span>\n </li>\n );\n};\n\nexport const SessionAttachmentsPane: FC = () => {\n const { isPersisted } = useSessionWorkbench();\n const workspace = useSessionWorkbenchWorkspace();\n const canOpenHere = useAssetPanelStore((state) => state.assetPanelHostCount > 0);\n if (!isPersisted) {\n return (\n <PaneEmpty icon={PaperclipIcon} title=\"No files yet\">\n Documents, sheets and decks the agent creates or edits show here.\n </PaneEmpty>\n );\n }\n if (workspace.isError) return <PaneError error={workspace.error} />;\n if (!workspace.data) return <PaneSkeleton />;\n const { assets } = workspace.data;\n if (assets.length === 0) {\n return (\n <PaneEmpty icon={PaperclipIcon} title=\"No files yet\">\n Documents, sheets and decks the agent creates or edits show here.\n </PaneEmpty>\n );\n }\n return (\n <div className=\"flex flex-col pb-2\">\n <SectionHeading\n trailing={<span className=\"text-muted-foreground text-xs tabular-nums\">{assets.length}</span>}\n >\n Created or edited\n </SectionHeading>\n <ul aria-label=\"Session files\">\n {assets.map((asset) => (\n <AttachmentRow key={asset.assetId} asset={asset} canOpenHere={canOpenHere} />\n ))}\n </ul>\n </div>\n );\n};\n\n// ─── Registry ────────────────────────────────────────────────────────\n\nexport const SESSION_WORKBENCH_TASKS_PANE: SessionWorkbenchPane = {\n id: 'tasks',\n title: 'Tasks',\n description: \"Follow the agent's plan step by step\",\n icon: ListChecksIcon,\n render: () => <SessionTasksPane />,\n};\n\nexport const SESSION_WORKBENCH_SHELL_PANE: SessionWorkbenchPane = {\n id: 'shell',\n title: 'Shell',\n description: 'Commands the agent ran and their output',\n icon: TerminalIcon,\n render: () => <SessionShellPane />,\n};\n\nexport const SESSION_WORKBENCH_COMPUTER_PANE: SessionWorkbenchPane = {\n id: 'computer',\n title: 'Computer',\n description: 'The machine this session works on, and how to reach it',\n icon: MonitorIcon,\n render: () => <SessionComputerPane />,\n};\n\nexport const SESSION_WORKBENCH_PULL_REQUESTS_PANE: SessionWorkbenchPane = {\n id: 'pull-requests',\n title: 'Pull requests',\n description: 'Pull requests this session opened, with checks and reviews',\n icon: GitPullRequestIcon,\n render: () => <SessionPullRequestsPane />,\n};\n\nexport const SESSION_WORKBENCH_ATTACHMENTS_PANE: SessionWorkbenchPane = {\n id: 'attachments',\n title: 'Attachments',\n description: 'Files and assets this session created or edited',\n icon: PaperclipIcon,\n render: () => <SessionAttachmentsPane />,\n};\n\n/** The built-in panes, in \"+\" menu order. Spread it to add your own. */\nexport const DEFAULT_SESSION_WORKBENCH_PANES: readonly SessionWorkbenchPane[] = [\n SESSION_WORKBENCH_TASKS_PANE,\n SESSION_WORKBENCH_SHELL_PANE,\n SESSION_WORKBENCH_COMPUTER_PANE,\n SESSION_WORKBENCH_PULL_REQUESTS_PANE,\n SESSION_WORKBENCH_ATTACHMENTS_PANE,\n];\n\n/** The panes a thread opens with until the person changes its tabs. */\nexport const DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS: readonly string[] = [\n SESSION_WORKBENCH_TASKS_PANE.id,\n SESSION_WORKBENCH_SHELL_PANE.id,\n SESSION_WORKBENCH_COMPUTER_PANE.id,\n];\n"],"names":["jsxs","jsx","AlertCircleIcon","useAuiState","hasStatewireThreadExtras","useStatewireState","readDeepAgentStreamValues","useMemo","taskProgress","deriveTaskSteps","ListChecksIcon","PlanStepMarker","cn","LoaderIcon","CheckIcon","XIcon","CircleDashedIcon","relativeTime","useSessionWorkbench","useSessionWorkbenchWorkspace","useStatewireHistory","deriveShellRows","useState","filterShellRows","TerminalIcon","SearchIcon","useAthenaComputerStatus","computerStateBadge","useAthenaComputerSshAccess","_a","CopyIcon","useAthenaConfig","createAthenaSpacesUrl","athenaLinkAppUrl","ExternalLinkIcon","MonitorIcon","workspaceComputers","ComputerLiveDesktop","Fragment","pullRequestChipState","PR_STATE_ICON","PR_CHECKS_LABEL","reviewDecisionLabel","PR_STATE_CLASS","useAthenaStatewireThreadList","useAthenaSessionAttention","GitPullRequestIcon","orderedPullRequests","FileSpreadsheetIcon","FileTextIcon","PresentationIcon","BookOpenIcon","ImageIcon","FolderIcon","useAssetPanelStore","FileIcon","assetPanelTypeOf","assetTypeLabel","PaperclipIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAwFA,MAAM,mBACJ;AAEF,MAAM,YAID,CAAC,EAAE,MAAM,MAAM,OAAO,eACzBA,2BAAAA,KAAC,OAAA,EAAI,WAAU,0FACb,UAAA;AAAA,EAAAC,2BAAAA,IAAC,MAAA,EAAK,WAAU,kCAAA,CAAkC;AAAA,EAClDA,2BAAAA,IAAC,KAAA,EAAE,WAAU,uCAAuC,UAAA,OAAM;AAAA,EACzD,WAAWA,2BAAAA,IAAC,KAAA,EAAE,WAAU,uDAAuD,UAAS,IAAO;AAAA,GAClG;AAGF,MAAM,YAAkC,CAAC,EAAE,MAAA,sCACxC,OAAA,EAAI,MAAK,SAAQ,WAAU,wGAC1B,UAAA;AAAA,EAAAA,2BAAAA,IAACC,YAAAA,iBAAA,EAAgB,WAAU,4BAA2B,eAAW,MAAC;AAAA,EAClED,2BAAAA,IAAC,QAAA,EAAK,WAAU,8BAA8B,gBAAM,QAAA,CAAQ;AAAA,GAC9D;AAGF,MAAM,eAAmB,MACvBA,2BAAAA,IAAC,OAAA,EAAI,WAAU,2BAA0B,aAAU,QAAO,cAAW,WAClE,UAAA,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,QACdA,2BAAAA,IAAC,SAAc,WAAU,yCAAA,GAAf,GAAwD,CACnE,EAAA,CACH;AAGF,MAAM,iBAAoE,CAAC,EAAE,UAAU,eACrFD,gCAAC,OAAA,EAAI,WAAU,4DACb,UAAA;AAAA,EAAAC,2BAAAA,IAAC,MAAA,EAAG,WAAU,yEAAyE,SAAA,CAAS;AAAA,EAC/F;AAAA,GACH;AAIF,SAAS,SAAS,KAAwC;AACxD,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,SAAO,OAAO,MAAM,IAAI,IAAI,SAAY,IAAI,KAAK,IAAI,EAAE,eAAA;AACzD;AAGA,MAAM,0BAAyF,CAAC;AAAA,EAC9F;AAAA,EACA;AACF,MAAM;AACJ,QAAM,SAASE,MAAAA,YAAY,CAAC,UAAU,MAAM,OAAO,MAAM;AACzD,MAAI,CAACC,aAAAA,yBAAyB,MAAM,GAAG;AACrC,WAAOH,2BAAAA,IAAC,WAAA,EAAU,MAAY,OAAM,mBAAkB,UAAA,oCAAgC;AAAA,EACxF;AACA,+DAAU,UAAS;AACrB;AAIA,MAAM,oBAAoD;AAAA,EACxD,SAAS;AAAA,EACT,aAAa;AAAA,EACb,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,WAAW;AACb;AAEA,MAAM,mBAAuB,MAAM;AACjC,QAAM,aAAaI,eAAAA,kBAAkB,CAAC,UAAUC,eAAAA,0BAA0B,KAAK,EAAE,KAAK;AACtF,QAAM,WAAWH,MAAAA,YAAY,CAAC,UAAU,MAAM,OAAO,QAAQ;AAC7D,QAAM,WAAWI,WAAAA;AAAAA,IACf,MAAMC,sBAAAA,aAAaC,sBAAAA,gBAAgB,EAAE,YAAY,SAAA,CAAU,CAAC;AAAA,IAC5D,CAAC,YAAY,QAAQ;AAAA,EAAA;AAEvB,MAAI,SAAS,UAAU,GAAG;AACxB,0CACG,WAAA,EAAU,MAAMC,YAAAA,gBAAgB,OAAM,eAAc,UAAA,+EAErD;AAAA,EAEJ;AACA,QAAM,UAAU,KAAK,MAAO,SAAS,OAAO,SAAS,QAAS,GAAG;AACjE,SACEV,2BAAAA,KAAC,OAAA,EAAI,WAAU,iBACb,UAAA;AAAA,IAAAC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,UACED,2BAAAA,KAAC,QAAA,EAAK,WAAU,oDACb,UAAA;AAAA,UAAA,SAAS;AAAA,UAAK;AAAA,UAAE,SAAS;AAAA,UAAM;AAAA,QAAA,GAClC;AAAA,QAEH,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAGDC,2BAAAA,IAAC,OAAA,EAAI,WAAU,uDAEb,yCAAC,OAAA,EAAI,WAAU,iDAAgD,OAAO,EAAE,OAAO,GAAG,OAAO,IAAA,GAAO,GAClG;AAAA,IACAA,2BAAAA,IAAC,MAAA,EAAG,WAAU,sBAAqB,cAAW,cAC3C,UAAA,SAAS,MAAM,IAAI,CAAC,MAAM,UACzBD,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QAEC,eAAa,KAAK;AAAA,QAClB,WAAU;AAAA,QAET,UAAA;AAAA,UAAA,KAAK,WAAW;AAAA;AAAA,YAEfC,2BAAAA,IAAC,QAAA,EAAK,eAAW,MAAC,WAAU,uEAAA,CAAuE;AAAA,cAEnGA,2BAAAA,IAACU,iBAAAA,gBAAA,EAAe,QAAQ,KAAK,QAAQ,OAAc;AAAA,UAErDX,2BAAAA,KAAC,QAAA,EAAK,WAAU,WAAW,UAAA;AAAA,YAAA,kBAAkB,KAAK,MAAM;AAAA,YAAE;AAAA,UAAA,GAAE;AAAA,UAC5DA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAWY,OAAAA;AAAAA,gBACT;AAAA,gBACA,KAAK,WAAW,eAAe;AAAA,gBAC/B,KAAK,WAAW,iBAAiB;AAAA,gBACjC,KAAK,WAAW,YAAY;AAAA,gBAC5B,KAAK,WAAW,eAAe;AAAA,cAAA;AAAA,cAGjC,UAAA;AAAA,gBAAAZ,2BAAAA,KAAC,QAAA,EAAK,WAAU,6CAA6C,UAAA;AAAA,kBAAA,QAAQ;AAAA,kBAAE;AAAA,gBAAA,GAAC;AAAA,gBACvE,KAAK;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACR;AAAA,MAAA;AAAA,MAtBK,KAAK,MAAM,GAAG,KAAK,IAAI,KAAK,OAAO;AAAA,IAAA,CAwB3C,EAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAEO,MAAM,mBAAuB,MAClCC,2BAAAA,IAAC,yBAAA,EAAwB,MAAMS,4BAC7B,UAAAT,2BAAAA,IAAC,oBAAiB,EAAA,CACpB;AAKF,MAAM,qBAAqD;AAAA,EACzD,SAAS;AAAA,EACT,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,SAAS;AACX;AAEA,MAAM,kBAAkD,CAAC,EAAE,aAAa;AACtE,QAAM,QAAQ,mBAAmB,MAAM;AACvC,MAAI,WAAW,WAAW;AACxB,WAAOA,2BAAAA,IAACY,YAAAA,YAAA,EAAW,WAAU,sDAAqD,cAAY,OAAO;AAAA,EACvG;AACA,MAAI,WAAW,aAAa;AAC1B,WAAOZ,2BAAAA,IAACa,YAAAA,WAAA,EAAU,WAAU,6CAA4C,cAAY,OAAO;AAAA,EAC7F;AACA,MAAI,WAAW,UAAU;AACvB,WAAOb,2BAAAA,IAACc,YAAAA,OAAA,EAAM,WAAU,yCAAwC,cAAY,OAAO;AAAA,EACrF;AACA,SAAOd,2BAAAA,IAACe,YAAAA,kBAAA,EAAiB,WAAU,kDAAiD,cAAY,OAAO;AACzG;AAEA,SAAS,UAAU,KAAuB;AACxC,QAAM,QAAQ,CAAC,IAAI,QAAQ,KAAK;AAChC,MAAI,IAAI,aAAa,KAAM,OAAM,KAAK,QAAQ,IAAI,QAAQ,EAAE;AAC5D,MAAI,IAAI,UAAW,OAAM,KAAKC,8BAAa,IAAI,SAAS,CAAC;AACzD,SAAO,MAAM,KAAK,KAAK;AACzB;AAEA,MAAM,eAA+E,CAAC;AAAA,EACpF;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,UAAU,gBAAgB,IAAI,EAAE;AACtC,yCACG,MAAA,EAAG,eAAa,IAAI,QAAQ,WAAU,6CACrC,UAAA;AAAA,IAAAjB,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,iBAAe;AAAA,QACf,iBAAe;AAAA,QACf,SAAS;AAAA,QACT,WAAWY,OAAAA;AAAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAGF,UAAA;AAAA,UAAAX,2BAAAA,IAAC,iBAAA,EAAgB,QAAQ,IAAI,OAAA,CAAQ;AAAA,UACrCD,2BAAAA,KAAC,QAAA,EAAK,WAAU,kBACd,UAAA;AAAA,YAAAA,2BAAAA,KAAC,QAAA,EAAK,WAAU,oDACd,UAAA;AAAA,cAAAC,2BAAAA,IAAC,QAAA,EAAK,WAAU,yBAAwB,UAAA,MAAE;AAAA,cACzC,IAAI,aAAa;AAAA,cACjB,IAAI,aAAa,IAChBD,2BAAAA,KAAC,QAAA,EAAK,WAAU,yBAAwB,UAAA;AAAA,gBAAA;AAAA,gBAAI,IAAI;AAAA,gBAAW;AAAA,cAAA,EAAA,CAAO,IAChE;AAAA,YAAA,GACN;AAAA,YACAC,2BAAAA,IAAC,QAAA,EAAK,WAAU,oDAAmD,OAAO,SAAS,IAAI,SAAS,GAC7F,UAAA,UAAU,GAAG,EAAA,CAChB;AAAA,UAAA,EAAA,CACF;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAED,WACCD,2BAAAA,KAAC,OAAA,EAAI,IAAI,SAAS,WAAU,eAC1B,UAAA;AAAA,MAAAC,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,cAAW;AAAA,UACX,WAAU;AAAA,UAET,UAAA,IAAI;AAAA,QAAA;AAAA,MAAA;AAAA,MAEPA,2BAAAA,IAAC,OAAA,EAAI,WAAU,8HACZ,UAAA,IAAI,WAAW,YAAY,aAAa,IAAI,UAAU,cAAA,CACzD;AAAA,MACC,IAAI,gBACHA,2BAAAA,IAAC,OAAE,WAAU,0CAAyC,sFAEtD,IACE;AAAA,IAAA,EAAA,CACN,IACE;AAAA,EAAA,GACN;AAEJ;AAOA,MAAM,qBAA6E,CAAC;AAAA,EAClF;AAAA,EACA;AACF,MAAM;AACJ,QAAM,EAAE,cAAc,SAAS,UAAA,IAAc;AAC7C,SACED,2BAAAA,KAAC,OAAA,EAAI,WAAU,0DACb,UAAA;AAAA,IAAAC,+BAAC,KAAA,EAAE,WAAU,qCACV,UAAA,QACG,2DACA,oEACN;AAAA,IACC,gBAAgB,YACfD,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAWY,OAAAA;AAAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAGD,UAAA;AAAA,UAAA,yCAAWC,YAAAA,YAAA,EAAW,WAAU,yBAAwB,eAAW,MAAC,IAAK;AAAA,UACzE,UAAU,8BAA8B;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,IAG3CZ,2BAAAA,IAAC,KAAA,EAAE,WAAU,qCAAoC,UAAA,oEAAA,CAEjD;AAAA,EAAA,GAEJ;AAEJ;AAEA,MAAM,mBAAuB,MAAM;AACjC,QAAM,EAAE,SAAA,IAAaiB,4CAAA;AACrB,QAAM,WAAWf,MAAAA,YAAY,CAAC,UAAU,MAAM,OAAO,QAAQ;AAC7D,QAAM,YAAYgB,wBAAAA,+BAA+B,QAAQ;AACzD,QAAM,UAAUC,wBAAAA,oBAAoB,QAAQ;AAC5C,QAAM,WAAU,mCAAS,cAAa;AACtC,QAAM,OAAOb,WAAAA,QAAQ,MAAMc,sBAAAA,gBAAgB,EAAE,UAAU,UAAA,CAAW,GAAG,CAAC,UAAU,SAAS,CAAC;AAC1F,QAAM,CAAC,OAAO,QAAQ,IAAIC,WAAAA,SAAS,EAAE;AACrC,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAAA,SAA8B,MAAM,oBAAI,KAAK;AAC7E,QAAM,UAAUf,mBAAQ,MAAMgB,sCAAgB,MAAM,KAAK,GAAG,CAAC,MAAM,KAAK,CAAC;AACzE,QAAM,SAAS,CAAC,OACd,YAAY,CAAC,YAAY;AACvB,UAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,QAAI,KAAK,IAAI,EAAE,EAAG,MAAK,OAAO,EAAE;AAAA,QAC3B,MAAK,IAAI,EAAE;AAChB,WAAO;AAAA,EACT,CAAC;AAEH,MAAI,KAAK,WAAW,GAAG;AACrB,QAAI,WAAW,QAAS,uCAAQ,oBAAA,EAAmB,SAAkB,OAAK,MAAC;AAC3E,0CACG,WAAA,EAAU,MAAMC,YAAAA,cAAc,OAAM,mBAAkB,UAAA,qFAEvD;AAAA,EAEJ;AACA,SACExB,2BAAAA,KAAC,OAAA,EAAI,WAAU,iBACb,UAAA;AAAA,IAAAA,2BAAAA,KAAC,OAAA,EAAI,WAAU,uEACb,UAAA;AAAA,MAAAA,2BAAAA,KAAC,SAAA,EAAM,WAAU,iIACf,UAAA;AAAA,QAAAC,2BAAAA,IAACwB,YAAAA,YAAA,EAAW,WAAU,2CAA0C,eAAW,MAAC;AAAA,QAC5ExB,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,OAAO;AAAA,YACP,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,YAChD,aAAY;AAAA,YACZ,cAAW;AAAA,YACX,WAAU;AAAA,UAAA;AAAA,QAAA;AAAA,MACZ,GACF;AAAA,MACAD,2BAAAA,KAAC,KAAA,EAAE,WAAU,4CAA2C,aAAU,UAC/D,UAAA;AAAA,QAAA,MAAM,SACH,GAAG,QAAQ,MAAM,OAAO,KAAK,MAAM,cACnC,GAAG,KAAK,MAAM,WAAW,KAAK,WAAW,IAAI,KAAK,GAAG;AAAA,QACxD,UAAU,YAAY;AAAA,MAAA,EAAA,CACzB;AAAA,IAAA,GACF;AAAA,IACC,QAAQ,WAAW,IAClBC,2BAAAA,IAAC,KAAA,EAAE,WAAU,uDAAsD,UAAA,sBAAA,CAAmB,IAEtFA,+BAAC,QAAG,cAAW,kBACZ,UAAA,QAAQ,IAAI,CAAC,QACZA,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QAEC;AAAA,QACA,UAAU,SAAS,IAAI,IAAI,EAAE;AAAA,QAC7B,UAAU,MAAM,OAAO,IAAI,EAAE;AAAA,MAAA;AAAA,MAHxB,IAAI;AAAA,IAAA,CAKZ,GACH;AAAA,IAED,WAAW,UAAUA,2BAAAA,IAAC,sBAAmB,SAAkB,OAAO,OAAO,IAAK;AAAA,EAAA,GACjF;AAEJ;AAEO,MAAM,mBAAuB,MAClCA,2BAAAA,IAAC,yBAAA,EAAwB,MAAMuB,0BAC7B,UAAAvB,2BAAAA,IAAC,oBAAiB,EAAA,CACpB;AAKF,MAAM,qBAA8C,CAAC,EAAE,cAAc;;AACnE,QAAM,SAASyB,oBAAAA,wBAAwB,EAAE,SAAS;AAClD,QAAM,QAAQ,OAAO,OAAOC,sBAAAA,mBAAmB,OAAO,IAAI,IAAI;AAC9D,MAAI,CAAC,MAAO,QAAO;AACnB,SACE3B,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAY,YAAO,SAAP,mBAAa;AAAA,MACzB,WAAU;AAAA,MAEV,UAAA;AAAA,QAAAC,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,eAAW;AAAA,YACX,WAAWW,OAAAA;AAAAA,cACT;AAAA,cACA,MAAM,SAAS,aAAa;AAAA,cAC5B,MAAM,SAAS,gBAAgB;AAAA,cAC/B,MAAM,SAAS,aAAa;AAAA,cAC5B,MAAM,SAAS,WAAW;AAAA,YAAA;AAAA,UAC5B;AAAA,QAAA;AAAA,QAED,MAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGb;AAEA,MAAM,iBAA0C,CAAC,EAAE,cAAc;;AAC/D,QAAM,MAAMgB,oBAAAA,2BAA2B,EAAE,SAAS;AAClD,QAAM,CAAC,QAAQ,SAAS,IAAIN,WAAAA,SAAS,KAAK;AAC1C,QAAM,WAAU,SAAI,SAAJ,mBAAU;AAC1B,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,OAAO,MAAM;;AACjB,WAAKO,MAAA,UAAU,cAAV,gBAAAA,IAAqB,UAAU,SAAS,KAAK,MAAM;AACtD,gBAAU,IAAI;AACd,iBAAW,MAAM,UAAU,KAAK,GAAG,GAAI;AAAA,IACzC;AAAA,EACF;AACA,SACE7B,2BAAAA,KAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,IAAAA,2BAAAA,KAAC,OAAA,EAAI,WAAU,6BACb,UAAA;AAAA,MAAAC,+BAAC,QAAA,EAAK,WAAU,+FAA8F,OAAO,SAClH,UAAA,SACH;AAAA,MACAD,2BAAAA;AAAAA,QAAC;AAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,WAAWY,OAAAA;AAAAA,YACT;AAAA,YACA;AAAA,UAAA;AAAA,UAGD,UAAA;AAAA,YAAA,SAASX,2BAAAA,IAACa,YAAAA,WAAA,EAAU,WAAU,YAAW,eAAW,KAAA,CAAC,IAAKb,2BAAAA,IAAC6B,YAAAA,UAAA,EAAS,WAAU,YAAW,eAAW,MAAC;AAAA,YACrG,SAAS,WAAW;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IACvB,GACF;AAAA,MACC,SAAI,SAAJ,mBAAU,oBAAmB,uCAC3B,KAAA,EAAE,WAAU,qCAAoC,UAAA,sGAAA,CAEjD,IACE;AAAA,EAAA,GACN;AAEJ;AAEA,SAAS,aAAa,SAAgC;AACpD,QAAM,EAAE,QAAQ,WAAA,IAAeC,8BAAA;AAC/B,SAAOxB,WAAAA,QAAQ,MAAM;AACnB,UAAM,aAAa,OAAO,WAAW,cAAc,OAAO,OAAO,SAAS;AAC1E,QAAI;AACF,aAAOyB,iCAAsB;AAAA,QAC3B,QAAQC,WAAAA,iBAAiB,EAAE,QAAQ,YAAY,YAAY;AAAA,QAC3D,UAAU;AAAA,MAAA,CACX;AAAA,IACH,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,QAAQ,YAAY,OAAO,CAAC;AAClC;AAEA,MAAM,mBAA+E,CAAC;AAAA,EACpF;AAAA,EACA,QAAQ;AAAA,EACR,UAAU;AACZ,MAAM;AACJ,QAAM,OAAO,aAAa,OAAO;AACjC,MAAI,CAAC,KAAM,QAAO;AAClB,SACEjC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,QAAO;AAAA,MACP,KAAI;AAAA,MACJ,cAAY,UAAU,QAAQ;AAAA,MAC9B,OAAO,UAAU,QAAQ;AAAA,MACzB,WAAWY,OAAAA;AAAAA,QACT;AAAA,QACA,UAAU,oDAAoD;AAAA,QAC9D;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAAX,2BAAAA,IAACiC,YAAAA,kBAAA,EAAiB,WAAU,YAAW,eAAW,MAAC;AAAA,QAClD,UAAU,OAAO;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGxB;AAEA,MAAM,eAAwD,CAAC,EAAE,SAAA,MAC/DlC,2BAAAA;AAAAA,EAAC;AAAA,EAAA;AAAA,IACC,cAAY,SAAS,SAAS,SAAS;AAAA,IACvC,WAAU;AAAA,IAEV,UAAA;AAAA,MAAAA,2BAAAA,KAAC,OAAA,EAAI,WAAU,4BACb,UAAA;AAAA,QAAAC,2BAAAA,IAAC,QAAA,EAAK,WAAU,wFACd,UAAAA,2BAAAA,IAACkC,2BAAY,WAAU,UAAS,eAAW,KAAA,CAAC,EAAA,CAC9C;AAAA,QACAnC,2BAAAA,KAAC,OAAA,EAAI,WAAU,kBACb,UAAA;AAAA,UAAAC,+BAAC,OAAE,WAAU,gDAAgD,UAAA,SAAS,SAAS,SAAS,SAAQ;AAAA,UAChGD,2BAAAA,KAAC,KAAA,EAAE,WAAU,8CACV,UAAA;AAAA,YAAA,SAAS,SAAS,qBAAqB,qBAAqB;AAAA,YAC5D,SAAS,YAAY,cAAciB,iBAAAA,aAAa,SAAS,SAAS,CAAC,KAAK;AAAA,UAAA,EAAA,CAC3E;AAAA,QAAA,GACF;AAAA,QACAhB,2BAAAA,IAAC,oBAAA,EAAmB,SAAS,SAAS,QAAA,CAAS;AAAA,MAAA,GACjD;AAAA,MACAA,2BAAAA,IAAC,SAAI,WAAU,qCACb,yCAAC,kBAAA,EAAiB,SAAS,SAAS,QAAA,CAAS,EAAA,CAC/C;AAAA,MACAA,2BAAAA,IAAC,gBAAA,EAAe,SAAS,SAAS,QAAA,CAAS;AAAA,IAAA;AAAA,EAAA;AAC7C;AAGK,MAAM,sBAA0B,MAAM;AAC3C,QAAM,EAAE,YAAA,IAAgBiB,4CAAA;AACxB,QAAM,YAAYC,wBAAAA,6BAAA;AAClB,MAAI,CAAC,aAAa;AAChB,0CACG,WAAA,EAAU,MAAMgB,YAAAA,aAAa,OAAM,mBAAkB,UAAA,iEAEtD;AAAA,EAEJ;AACA,MAAI,UAAU,QAAS,uCAAQ,WAAA,EAAU,OAAO,UAAU,OAAO;AACjE,MAAI,CAAC,UAAU,KAAM,uCAAQ,cAAA,CAAA,CAAa;AAC1C,QAAM,EAAE,SAAS,OAAA,IAAWC,sBAAAA,mBAAmB,UAAU,IAAI;AAC7D,MAAI,CAAC,SAAS;AACZ,0CACG,WAAA,EAAU,MAAMD,YAAAA,aAAa,OAAM,wBAAuB,UAAA,8FAE3D;AAAA,EAEJ;AACA,SACEnC,2BAAAA,KAAC,OAAA,EAAI,WAAU,sBACb,UAAA;AAAA,IAAAC,2BAAAA,IAACoC,oBAAAA,qBAAA,EAAoB,SAAS,QAAQ,SAAS,WAAU,aAAY;AAAA,IACrEpC,2BAAAA,IAAC,cAAA,EAAa,UAAU,QAAA,CAAS;AAAA,IAChC,OAAO,SAAS,IACfD,2BAAAA,KAAAsC,WAAAA,UAAA,EACE,UAAA;AAAA,MAAArC,2BAAAA,IAAC,kBAAe,UAAA,YAAA,CAAS;AAAA,MACzBA,2BAAAA,IAAC,MAAA,EAAG,cAAW,mBACZ,UAAA,OAAO,IAAI,CAAC,aACXD,2BAAAA,KAAC,MAAA,EAA0B,WAAU,uCACnC,UAAA;AAAA,QAAAC,2BAAAA,IAACkC,YAAAA,aAAA,EAAY,WAAU,2CAA0C,eAAW,MAAC;AAAA,uCAC5E,QAAA,EAAK,WAAU,mDACb,UAAA,SAAS,SAAS,SAAS,SAC9B;AAAA,QACAlC,2BAAAA,IAAC,kBAAA,EAAiB,SAAS,SAAS,SAAS,SAAO,MAAC,OAAO,QAAQ,SAAS,SAAS,UAAU,aAAA,CAAc;AAAA,MAAA,KALvG,SAAS,OAMlB,CACD,EAAA,CACH;AAAA,IAAA,EAAA,CACF,IACE;AAAA,EAAA,GACN;AAEJ;AAIA,MAAM,iBAA+D,CAAC,EAAE,kBAAkB;AACxF,QAAM,QAAQsC,iBAAAA,qBAAqB,WAAW;AAC9C,QAAM,OAAOC,mBAAAA,cAAc,KAAK;AAChC,QAAM,OAAO;AAAA,IACX,GAAG,YAAY,YAAY,IAAI,YAAY,MAAM;AAAA,IACjD;AAAA,IACAC,mBAAAA,gBAAgB,YAAY,WAAW;AAAA,IACvCC,sBAAAA,oBAAoB,YAAY,cAAc;AAAA,EAAA,EAE7C,OAAO,OAAO,EACd,KAAK,KAAK;AACb,wCACG,MAAA,EACC,UAAA1C,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAM,YAAY;AAAA,MAClB,QAAO;AAAA,MACP,KAAI;AAAA,MACJ,iBAAe;AAAA,MACf,WAAWY,OAAAA,GAAG,0EAA0E,gBAAgB;AAAA,MAExG,UAAA;AAAA,QAAAX,2BAAAA,IAAC,MAAA,EAAK,WAAWW,UAAG,0BAA0B+B,mBAAAA,eAAe,KAAK,CAAC,GAAG,eAAW,KAAA,CAAC;AAAA,QAClF3C,2BAAAA,KAAC,QAAA,EAAK,WAAU,kBACd,UAAA;AAAA,UAAAC,2BAAAA,IAAC,QAAA,EAAK,WAAU,8CACb,UAAA,YAAY,SAAS,IAAI,YAAY,MAAM,GAAA,CAC9C;AAAA,UACAA,2BAAAA,IAAC,QAAA,EAAK,WAAU,oDAAoD,UAAA,KAAA,CAAK;AAAA,QAAA,GAC3E;AAAA,QACAA,2BAAAA,IAACiC,YAAAA,kBAAA,EAAiB,WAAU,kDAAiD,eAAW,KAAA,CAAC;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAE7F;AAEJ;AAEO,MAAM,0BAA8B,MAAM;AAC/C,QAAM,EAAE,UAAU,YAAA,IAAgBhB,4CAAA;AAClC,QAAM,OAAO0B,oBAAAA,6BAAA;AACb,QAAM,YAAYC,0BAAAA,0BAA0B,YAAY,MAAS;AACjE,MAAI,CAAC,MAAM;AACT,0CACG,WAAA,EAAU,MAAMC,YAAAA,oBAAoB,OAAM,uCAAsC,UAAA,uFAEjF;AAAA,EAEJ;AACA,QAAM,eAAe,eAAe,uCAAW,iBAAgB,CAAA,IAAM,CAAA;AACrE,OAAI,uCAAW,kBAAiB,QAAQ,aAAa;AACnD,0CACG,WAAA,EAAU,MAAMA,YAAAA,oBAAoB,OAAM,6BAA4B,UAAA,wDAEvE;AAAA,EAEJ;AACA,MAAI,aAAa,WAAW,GAAG;AAC7B,0CACG,WAAA,EAAU,MAAMA,YAAAA,oBAAoB,OAAM,oBAAmB,UAAA,+EAE9D;AAAA,EAEJ;AACA,QAAM,UAAUC,iBAAAA,oBAAoB,YAAY;AAChD,SACE/C,2BAAAA,KAAC,OAAA,EAAI,WAAU,sBACb,UAAA;AAAA,IAAAC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,UAAUA,2BAAAA,IAAC,QAAA,EAAK,WAAU,8CAA8C,kBAAQ,QAAO;AAAA,QACxF,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAGDA,2BAAAA,IAAC,MAAA,EAAG,cAAW,iBACZ,kBAAQ,IAAI,CAAC,gBACZA,2BAAAA,IAAC,gBAAA,EAAqC,YAAA,GAAjB,YAAY,GAA+B,CACjE,EAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAIA,MAAM,aAAyD;AAAA,EAC7D,aAAa+C,YAAAA;AAAAA,EACb,UAAUC,YAAAA;AAAAA,EACV,cAAcC,YAAAA;AAAAA,EACd,0BAA0BA,YAAAA;AAAAA,EAC1B,MAAMA,YAAAA;AAAAA,EACN,UAAUC,YAAAA;AAAAA,EACV,OAAOC,YAAAA;AAAAA,EACP,QAAQC,YAAAA;AAAAA,EACR,UAAUJ,YAAAA;AAAAA,EACV,KAAKA,YAAAA;AAAAA,EACL,KAAKA,YAAAA;AACP;AAEA,MAAM,gBAA4E,CAAC;AAAA,EACjF;AAAA,EACA;AACF,MAAM;AACJ,QAAM,YAAYK,gBAAAA,mBAAmB,CAAC,UAAU,MAAM,SAAS;AAC/D,QAAM,OAAO,WAAW,MAAM,SAAS,KAAKC,YAAAA;AAC5C,QAAM,YAAmCC,sBAAAA,iBAAiB,MAAM,SAAS;AACzE,QAAM,QAAQ,MAAM,SAAS,MAAM;AACnC,QAAM,OAAO;AAAA,IACXC,sBAAAA,eAAe,MAAM,SAAS;AAAA,IAC9B,MAAM,YAAY,WAAWxC,iBAAAA,aAAa,MAAM,SAAS,CAAC,KAAK;AAAA,EAAA,EAE9D,OAAO,OAAO,EACd,KAAK,KAAK;AACb,QAAM,OACJjB,2BAAAA,KAAAsC,WAAAA,UAAA,EACE,UAAA;AAAA,IAAArC,2BAAAA,IAAC,MAAA,EAAK,WAAU,+CAAA,CAA+C;AAAA,IAC/DD,2BAAAA,KAAC,QAAA,EAAK,WAAU,kBACd,UAAA;AAAA,MAAAC,2BAAAA,IAAC,QAAA,EAAK,WAAU,0CAA0C,UAAA,OAAM;AAAA,MAChEA,2BAAAA,IAAC,UAAK,WAAU,oDAAmD,OAAO,SAAS,MAAM,SAAS,GAC/F,UAAA,KAAA,CACH;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GACF;AAEF,SACED,2BAAAA,KAAC,MAAA,EAAG,WAAU,+BACX,UAAA;AAAA,IAAA,eAAe,YACdC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,UAAU,MAAM,SAAS,EAAE,MAAM,OAAO,MAAM,WAAW;AAAA,QACxE,WAAWW,OAAAA;AAAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAGD,UAAA;AAAA,MAAA;AAAA,IAAA,IAGHX,2BAAAA,IAAC,OAAA,EAAI,WAAU,qDAAqD,UAAA,MAAK;AAAA,IAE3EA,+BAAC,QAAA,EAAK,WAAU,UACd,yCAAC,kBAAA,EAAiB,SAAS,MAAM,SAAS,SAAO,MAAC,OAAO,QAAQ,KAAK,cAAc,EAAA,CACtF;AAAA,EAAA,GACF;AAEJ;AAEO,MAAM,yBAA6B,MAAM;AAC9C,QAAM,EAAE,YAAA,IAAgBiB,4CAAA;AACxB,QAAM,YAAYC,wBAAAA,6BAAA;AAClB,QAAM,cAAcmC,gBAAAA,mBAAmB,CAAC,UAAU,MAAM,sBAAsB,CAAC;AAC/E,MAAI,CAAC,aAAa;AAChB,0CACG,WAAA,EAAU,MAAMI,YAAAA,eAAe,OAAM,gBAAe,UAAA,qEAErD;AAAA,EAEJ;AACA,MAAI,UAAU,QAAS,uCAAQ,WAAA,EAAU,OAAO,UAAU,OAAO;AACjE,MAAI,CAAC,UAAU,KAAM,uCAAQ,cAAA,CAAA,CAAa;AAC1C,QAAM,EAAE,WAAW,UAAU;AAC7B,MAAI,OAAO,WAAW,GAAG;AACvB,0CACG,WAAA,EAAU,MAAMA,YAAAA,eAAe,OAAM,gBAAe,UAAA,qEAErD;AAAA,EAEJ;AACA,SACE1D,2BAAAA,KAAC,OAAA,EAAI,WAAU,sBACb,UAAA;AAAA,IAAAC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,UAAUA,2BAAAA,IAAC,QAAA,EAAK,WAAU,8CAA8C,iBAAO,QAAO;AAAA,QACvF,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAGDA,2BAAAA,IAAC,MAAA,EAAG,cAAW,iBACZ,iBAAO,IAAI,CAAC,UACXA,2BAAAA,IAAC,iBAAkC,OAAc,YAAA,GAA7B,MAAM,OAAiD,CAC5E,EAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAIO,MAAM,+BAAqD;AAAA,EAChE,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAMS,YAAAA;AAAAA,EACN,QAAQ,MAAMT,+BAAC,kBAAA,CAAA,CAAiB;AAClC;AAEO,MAAM,+BAAqD;AAAA,EAChE,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAMuB,YAAAA;AAAAA,EACN,QAAQ,MAAMvB,+BAAC,kBAAA,CAAA,CAAiB;AAClC;AAEO,MAAM,kCAAwD;AAAA,EACnE,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAMkC,YAAAA;AAAAA,EACN,QAAQ,MAAMlC,+BAAC,qBAAA,CAAA,CAAoB;AACrC;AAEO,MAAM,uCAA6D;AAAA,EACxE,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAM6C,YAAAA;AAAAA,EACN,QAAQ,MAAM7C,+BAAC,yBAAA,CAAA,CAAwB;AACzC;AAEO,MAAM,qCAA2D;AAAA,EACtE,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAMyD,YAAAA;AAAAA,EACN,QAAQ,MAAMzD,+BAAC,wBAAA,CAAA,CAAuB;AACxC;AAGO,MAAM,kCAAmE;AAAA,EAC9E;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,MAAM,0CAA6D;AAAA,EACxE,6BAA6B;AAAA,EAC7B,6BAA6B;AAAA,EAC7B,gCAAgC;AAClC;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"session-workbench-panes.cjs","sources":["../../src/workbench/session-workbench-panes.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * The built-in panes of `SessionWorkbench` — Tasks, Shell, Computer, Pull\n * requests and Attachments. Each reads one source:\n *\n * - Tasks: the thread state's `todos` channel (the deepagents plan), else the\n * newest `write_todos` call in the loaded transcript.\n * - Shell: every `execute` call in the loaded transcript; while the thread has\n * older history the chat has not loaded, it says so and loads the next page\n * on request (`StatewireHistoryContext`).\n * - Computer / Attachments: the session's workspace\n * (`GET /api/conversations/threads/{id}/workspace`), plus the computer's\n * status and SSH target; the Computer pane also shows the computer's screen\n * live (`ComputerLiveDesktop`).\n * - Pull requests: the thread list's live pull-request state for the session.\n */\nimport { useAuiState } from '@assistant-ui/react';\nimport { useStatewireState } from '@assistant-ui/react-statewire';\nimport {\n type ConversationPullRequest,\n hasStatewireThreadExtras,\n readDeepAgentStreamValues,\n} from '@athenaintel/agent-runtime';\nimport {\n AlertCircleIcon,\n BookOpenIcon,\n CheckIcon,\n CircleDashedIcon,\n CopyIcon,\n ExternalLinkIcon,\n FileIcon,\n FileSpreadsheetIcon,\n FileTextIcon,\n FolderIcon,\n GitPullRequestIcon,\n ImageIcon,\n ListChecksIcon,\n LoaderIcon,\n MonitorIcon,\n PaperclipIcon,\n PresentationIcon,\n SearchIcon,\n TerminalIcon,\n XIcon,\n} from 'lucide-react';\nimport { type FC, type ReactNode, useMemo, useState } from 'react';\nimport { type AssetType as AssetPanelType, useAssetPanelStore } from '../assets/asset-panel-store';\nimport { athenaLinkAppUrl, createAthenaSpacesUrl } from '../lib/athena-urls';\nimport { cn } from '../lib/utils';\nimport { useAthenaConfig } from '../provider/AthenaContext';\nimport {\n type StatewireHistoryState,\n useStatewireHistory,\n} from '../provider/statewire-history-context';\nimport { PR_CHECKS_LABEL, PR_STATE_CLASS, PR_STATE_ICON } from '../threads/pull-request-display';\nimport {\n orderedPullRequests,\n pullRequestChipState,\n relativeTime,\n} from '../threads/session-list-model';\nimport { useAthenaStatewireThreadList } from '../threads/statewire-thread-list';\nimport { useAthenaSessionAttention } from '../threads/use-athena-session-attention';\nimport { PLAN_STEP_STATUS_LABEL, PlanStepMarker } from '../tools/write-todos-tool-ui';\nimport {\n type SessionWorkbenchPane,\n useSessionWorkbench,\n useSessionWorkbenchWorkspace,\n} from './session-workbench-context';\nimport {\n assetPanelTypeOf,\n assetTypeLabel,\n computerStateBadge,\n deriveShellRows,\n deriveTaskSteps,\n filterShellRows,\n reviewDecisionLabel,\n type ShellRow,\n type ShellRowStatus,\n taskProgress,\n workspaceComputers,\n} from './session-workbench-model';\nimport { ComputerLiveDesktop } from './ComputerLiveDesktop';\nimport type { SessionWorkspaceAsset } from './session-workspace-api';\nimport { useAthenaComputerSshAccess, useAthenaComputerStatus } from './use-session-workspace';\n\n// ─── Shared pieces ───────────────────────────────────────────────────\n\nconst FOCUS_RING_CLASS =\n 'outline-none focus-visible:ring-2 focus-visible:ring-ring/60 focus-visible:ring-offset-0';\n\nconst PaneEmpty: FC<{\n icon: FC<{ className?: string }>;\n title: string;\n children?: ReactNode;\n}> = ({ icon: Icon, title, children }) => (\n <div className=\"flex h-full min-h-40 flex-col items-center justify-center gap-2 px-6 py-10 text-center\">\n <Icon className=\"size-5 text-muted-foreground/70\" />\n <p className=\"font-medium text-foreground text-sm\">{title}</p>\n {children ? <p className=\"max-w-64 text-muted-foreground text-xs leading-snug\">{children}</p> : null}\n </div>\n);\n\nconst PaneError: FC<{ error: Error }> = ({ error }) => (\n <div role=\"alert\" className=\"m-3 flex items-start gap-2 rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-red-900 text-xs\">\n <AlertCircleIcon className=\"mt-0.5 size-3.5 shrink-0\" aria-hidden />\n <span className=\"min-w-0 flex-1 break-words\">{error.message}</span>\n </div>\n);\n\nconst PaneSkeleton: FC = () => (\n <div className=\"flex flex-col gap-2 p-3\" aria-busy=\"true\" aria-label=\"Loading\">\n {[0, 1, 2].map((row) => (\n <div key={row} className=\"h-10 animate-pulse rounded-lg bg-muted\" />\n ))}\n </div>\n);\n\nconst SectionHeading: FC<{ children: ReactNode; trailing?: ReactNode }> = ({ children, trailing }) => (\n <div className=\"flex items-center justify-between gap-2 px-3 pt-3 pb-1.5\">\n <h3 className=\"font-medium text-[11px] text-muted-foreground uppercase tracking-wide\">{children}</h3>\n {trailing}\n </div>\n);\n\n/** The `title` tooltip for an ISO time: the full local date and time. */\nfunction fullTime(iso: string | null): string | undefined {\n if (!iso) return undefined;\n const time = Date.parse(iso);\n return Number.isNaN(time) ? undefined : new Date(time).toLocaleString();\n}\n\n/** Statewire-only panes read the transcript and thread state; elsewhere they say so. */\nconst RequiresStatewireThread: FC<{ children: ReactNode; icon: FC<{ className?: string }> }> = ({\n children,\n icon,\n}) => {\n const extras = useAuiState((state) => state.thread.extras);\n if (!hasStatewireThreadExtras(extras)) {\n return <PaneEmpty icon={icon} title=\"No live session\">Open a session to see this pane.</PaneEmpty>;\n }\n return <>{children}</>;\n};\n\n// ─── Tasks ───────────────────────────────────────────────────────────\n\nconst TasksPaneContent: FC = () => {\n const stateTodos = useStatewireState((state) => readDeepAgentStreamValues(state).todos);\n const messages = useAuiState((state) => state.thread.messages);\n const progress = useMemo(\n () => taskProgress(deriveTaskSteps({ stateTodos, messages })),\n [stateTodos, messages],\n );\n if (progress.total === 0) {\n return (\n <PaneEmpty icon={ListChecksIcon} title=\"No plan yet\">\n When the agent writes a plan, its steps show here as it works through them.\n </PaneEmpty>\n );\n }\n const percent = Math.round((progress.done / progress.total) * 100);\n return (\n <div className=\"flex flex-col\">\n <SectionHeading\n trailing={\n <span className=\"font-medium text-foreground text-xs tabular-nums\">\n {progress.done}/{progress.total} done\n </span>\n }\n >\n Plan\n </SectionHeading>\n <div className=\"mx-3 mb-2 h-1 overflow-hidden rounded-full bg-muted\">\n {/* A runtime percentage is the one width a static utility cannot express. */}\n <div className=\"h-full rounded-full bg-primary transition-all\" style={{ width: `${percent}%` }} />\n </div>\n <ol className=\"flex flex-col pb-3\" aria-label=\"Plan steps\">\n {progress.steps.map((step, index) => (\n <li\n key={step.id ?? `${index}:${step.content}`}\n data-status={step.status}\n className=\"flex items-start gap-2.5 px-3 py-1.5\"\n >\n {step.status === 'pending' ? (\n // The row already carries its number; a pending marker repeating it reads \"11 11.\".\n <span aria-hidden className=\"size-[18px] shrink-0 rounded-full border border-border border-dashed\" />\n ) : (\n <PlanStepMarker status={step.status} index={index} />\n )}\n <span className=\"sr-only\">{PLAN_STEP_STATUS_LABEL[step.status]}: </span>\n <span\n className={cn(\n 'min-w-0 flex-1 text-sm leading-[18px]',\n step.status === 'completed' && 'text-muted-foreground line-through',\n step.status === 'in_progress' && 'font-medium text-foreground',\n step.status === 'failed' && 'text-destructive',\n step.status === 'cancelled' && 'text-muted-foreground line-through',\n )}\n >\n <span className=\"mr-1.5 text-muted-foreground tabular-nums\">{index + 1}.</span>\n {step.content}\n </span>\n </li>\n ))}\n </ol>\n </div>\n );\n};\n\nexport const SessionTasksPane: FC = () => (\n <RequiresStatewireThread icon={ListChecksIcon}>\n <TasksPaneContent />\n </RequiresStatewireThread>\n);\n\n// ─── Shell ───────────────────────────────────────────────────────────\n\nconst SHELL_STATUS_LABEL: Record<ShellRowStatus, string> = {\n running: 'Running',\n succeeded: 'Succeeded',\n failed: 'Failed',\n unknown: 'Finished',\n};\n\nconst ShellStatusIcon: FC<{ status: ShellRowStatus }> = ({ status }) => {\n const label = SHELL_STATUS_LABEL[status];\n if (status === 'running') {\n return <LoaderIcon className=\"mt-0.5 size-3.5 shrink-0 animate-spin text-primary\" aria-label={label} />;\n }\n if (status === 'succeeded') {\n return <CheckIcon className=\"mt-0.5 size-3.5 shrink-0 text-emerald-600\" aria-label={label} />;\n }\n if (status === 'failed') {\n return <XIcon className=\"mt-0.5 size-3.5 shrink-0 text-red-600\" aria-label={label} />;\n }\n return <CircleDashedIcon className=\"mt-0.5 size-3.5 shrink-0 text-muted-foreground\" aria-label={label} />;\n};\n\nfunction shellMeta(row: ShellRow): string {\n const parts = [row.machine.label];\n if (row.exitCode !== null) parts.push(`exit ${row.exitCode}`);\n if (row.createdAt) parts.push(relativeTime(row.createdAt));\n return parts.join(' · ');\n}\n\nconst ShellRowItem: FC<{ row: ShellRow; expanded: boolean; onToggle: () => void }> = ({\n row,\n expanded,\n onToggle,\n}) => {\n const panelId = `athena-shell-${row.id}`;\n return (\n <li data-status={row.status} className=\"border-border/60 border-b last:border-b-0\">\n <button\n type=\"button\"\n aria-expanded={expanded}\n aria-controls={panelId}\n onClick={onToggle}\n className={cn(\n 'flex w-full items-start gap-2 px-3 py-2 text-left transition-colors hover:bg-muted/60',\n FOCUS_RING_CLASS,\n )}\n >\n <ShellStatusIcon status={row.status} />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate font-mono text-foreground text-xs\">\n <span className=\"text-muted-foreground\">$ </span>\n {row.firstLine || '(empty command)'}\n {row.extraLines > 0 ? (\n <span className=\"text-muted-foreground\"> (+{row.extraLines} lines)</span>\n ) : null}\n </span>\n <span className=\"block truncate text-[11px] text-muted-foreground\" title={fullTime(row.createdAt)}>\n {shellMeta(row)}\n </span>\n </span>\n </button>\n {expanded ? (\n <div id={panelId} className=\"px-3 pb-2.5\">\n <pre\n aria-label=\"Command\"\n className=\"mb-1.5 max-h-40 overflow-auto whitespace-pre-wrap break-all rounded-md border border-border/60 px-2 py-1.5 font-mono text-[11px] text-foreground\"\n >\n {row.command}\n </pre>\n <pre className=\"max-h-72 overflow-auto whitespace-pre-wrap break-all rounded-md bg-muted px-2 py-1.5 font-mono text-[11px] text-foreground\">\n {row.status === 'running' ? 'Running…' : row.output || '(no output)'}\n </pre>\n {row.outputPartial ? (\n <p className=\"pt-1 text-[11px] text-muted-foreground\">\n Output shortened — the full output is on the command's card in the chat.\n </p>\n ) : null}\n </div>\n ) : null}\n </li>\n );\n};\n\n/**\n * The commands below come from the part of the transcript the chat has\n * loaded; earlier ones arrive a page at a time, through the chat's own history\n * edge (the same page it loads when the reader scrolls up).\n */\nconst ShellHistoryFooter: FC<{ history: StatewireHistoryState; empty: boolean }> = ({\n history,\n empty,\n}) => {\n const { canLoadOlder, loading, loadOlder } = history;\n return (\n <div className=\"flex flex-col items-center gap-2 px-3 py-4 text-center\">\n <p className=\"text-[11px] text-muted-foreground\">\n {empty\n ? 'No commands in the part of this session loaded so far.'\n : 'Earlier commands are in the part of this session not loaded yet.'}\n </p>\n {canLoadOlder && loadOlder ? (\n <button\n type=\"button\"\n onClick={loadOlder}\n disabled={loading}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border border-border px-2 py-1 font-medium text-xs transition-colors hover:bg-muted disabled:opacity-60',\n FOCUS_RING_CLASS,\n )}\n >\n {loading ? <LoaderIcon className=\"size-3.5 animate-spin\" aria-hidden /> : null}\n {loading ? 'Loading earlier commands…' : 'Load earlier commands'}\n </button>\n ) : (\n <p className=\"text-[11px] text-muted-foreground\">\n The rest is further back than one view of the conversation loads.\n </p>\n )}\n </div>\n );\n};\n\nconst ShellPaneContent: FC = () => {\n const { threadId } = useSessionWorkbench();\n const messages = useAuiState((state) => state.thread.messages);\n const workspace = useSessionWorkbenchWorkspace().data ?? null;\n const history = useStatewireHistory(threadId);\n const partial = history?.hasOlder === true;\n const rows = useMemo(() => deriveShellRows({ messages, workspace }), [messages, workspace]);\n const [query, setQuery] = useState('');\n const [expanded, setExpanded] = useState<ReadonlySet<string>>(() => new Set());\n const visible = useMemo(() => filterShellRows(rows, query), [rows, query]);\n const toggle = (id: string) =>\n setExpanded((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n\n if (rows.length === 0) {\n if (partial && history) return <ShellHistoryFooter history={history} empty />;\n return (\n <PaneEmpty icon={TerminalIcon} title=\"No commands yet\">\n Every shell command the agent runs shows here with where it ran and how it ended.\n </PaneEmpty>\n );\n }\n return (\n <div className=\"flex flex-col\">\n <div className=\"sticky top-0 z-10 border-border/60 border-b bg-background px-3 py-2\">\n <label className=\"flex items-center gap-2 rounded-md border border-border bg-background px-2 py-1 focus-within:ring-2 focus-within:ring-ring/40\">\n <SearchIcon className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n <input\n type=\"search\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder=\"Search commands\"\n aria-label=\"Search commands\"\n className=\"min-w-0 flex-1 bg-transparent text-xs outline-none placeholder:text-muted-foreground\"\n />\n </label>\n <p className=\"pt-1.5 text-[11px] text-muted-foreground\" aria-live=\"polite\">\n {query.trim()\n ? `${visible.length} of ${rows.length} commands`\n : `${rows.length} command${rows.length === 1 ? '' : 's'}`}\n {partial ? ' so far' : ''}\n </p>\n </div>\n {visible.length === 0 ? (\n <p className=\"px-3 py-6 text-center text-muted-foreground text-xs\">No command matches.</p>\n ) : (\n <ul aria-label=\"Shell commands\">\n {visible.map((row) => (\n <ShellRowItem\n key={row.id}\n row={row}\n expanded={expanded.has(row.id)}\n onToggle={() => toggle(row.id)}\n />\n ))}\n </ul>\n )}\n {partial && history ? <ShellHistoryFooter history={history} empty={false} /> : null}\n </div>\n );\n};\n\nexport const SessionShellPane: FC = () => (\n <RequiresStatewireThread icon={TerminalIcon}>\n <ShellPaneContent />\n </RequiresStatewireThread>\n);\n\n// ─── Computer ────────────────────────────────────────────────────────\n\nconst ComputerStateBadge: FC<{ assetId: string }> = ({ assetId }) => {\n const status = useAthenaComputerStatus({ assetId });\n const badge = status.data ? computerStateBadge(status.data) : null;\n if (!badge) return null;\n return (\n <span\n data-state={status.data?.state}\n className=\"inline-flex items-center gap-1.5 rounded-full border border-border px-2 py-0.5 text-[11px] text-muted-foreground\"\n >\n <span\n aria-hidden\n className={cn(\n 'size-1.5 rounded-full',\n badge.tone === 'running' && 'bg-emerald-500',\n badge.tone === 'transition' && 'animate-pulse bg-amber-500',\n badge.tone === 'stopped' && 'bg-muted-foreground/50',\n badge.tone === 'error' && 'bg-red-500',\n )}\n />\n {badge.label}\n </span>\n );\n};\n\nconst CopySshCommand: FC<{ assetId: string }> = ({ assetId }) => {\n const ssh = useAthenaComputerSshAccess({ assetId });\n const [copied, setCopied] = useState(false);\n const command = ssh.data?.command;\n if (!command) return null;\n const copy = () => {\n void navigator.clipboard?.writeText(command).then(() => {\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n });\n };\n return (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex items-center gap-1.5\">\n <code className=\"min-w-0 flex-1 truncate rounded-md bg-muted px-2 py-1 font-mono text-[11px] text-foreground\" title={command}>\n {command}\n </code>\n <button\n type=\"button\"\n onClick={copy}\n className={cn(\n 'inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2 py-1 font-medium text-xs transition-colors hover:bg-muted',\n FOCUS_RING_CLASS,\n )}\n >\n {copied ? <CheckIcon className=\"size-3.5\" aria-hidden /> : <CopyIcon className=\"size-3.5\" aria-hidden />}\n {copied ? 'Copied' : 'Copy SSH command'}\n </button>\n </div>\n {ssh.data?.keyAuthEnabled === false ? (\n <p className=\"text-[11px] text-muted-foreground\">\n This environment's SSH gateway takes temporary tokens only; create one from the computer in Athena.\n </p>\n ) : null}\n </div>\n );\n};\n\nfunction useAssetHref(assetId: string): string | null {\n const { appUrl, backendUrl } = useAthenaConfig();\n return useMemo(() => {\n const pageOrigin = typeof window === 'undefined' ? null : window.location.origin;\n try {\n return createAthenaSpacesUrl({\n appUrl: athenaLinkAppUrl({ appUrl, backendUrl, pageOrigin }),\n assetIds: assetId,\n });\n } catch {\n return null;\n }\n }, [appUrl, backendUrl, assetId]);\n}\n\nconst OpenInAthenaLink: FC<{ assetId: string; label?: string; compact?: boolean }> = ({\n assetId,\n label = 'Open in Athena',\n compact = false,\n}) => {\n const href = useAssetHref(assetId);\n if (!href) return null;\n return (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={compact ? label : undefined}\n title={compact ? label : undefined}\n className={cn(\n 'inline-flex shrink-0 items-center gap-1 rounded-md font-medium text-xs transition-colors hover:bg-muted',\n compact ? 'p-1 text-muted-foreground hover:text-foreground' : 'border border-border px-2 py-1',\n FOCUS_RING_CLASS,\n )}\n >\n <ExternalLinkIcon className=\"size-3.5\" aria-hidden />\n {compact ? null : label}\n </a>\n );\n};\n\nconst ComputerCard: FC<{ computer: SessionWorkspaceAsset }> = ({ computer }) => (\n <article\n aria-label={computer.title ?? computer.assetId}\n className=\"mx-3 mt-3 flex flex-col gap-3 rounded-xl border border-border p-3\"\n >\n <div className=\"flex items-start gap-2.5\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground\">\n <MonitorIcon className=\"size-4\" aria-hidden />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-medium text-foreground text-sm\">{computer.title ?? computer.assetId}</p>\n <p className=\"truncate text-[11px] text-muted-foreground\">\n {computer.role === 'session_computer' ? 'Session computer' : 'Used by this session'}\n {computer.updatedAt ? ` · updated ${relativeTime(computer.updatedAt)}` : ''}\n </p>\n </div>\n <ComputerStateBadge assetId={computer.assetId} />\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <OpenInAthenaLink assetId={computer.assetId} />\n </div>\n <CopySshCommand assetId={computer.assetId} />\n </article>\n);\n\nexport const SessionComputerPane: FC = () => {\n const { isPersisted } = useSessionWorkbench();\n const workspace = useSessionWorkbenchWorkspace();\n if (!isPersisted) {\n return (\n <PaneEmpty icon={MonitorIcon} title=\"No computer yet\">\n The computer this session works on shows here once it starts.\n </PaneEmpty>\n );\n }\n if (workspace.isError) return <PaneError error={workspace.error} />;\n if (!workspace.data) return <PaneSkeleton />;\n const { primary, others } = workspaceComputers(workspace.data);\n if (!primary) {\n return (\n <PaneEmpty icon={MonitorIcon} title=\"No computer used yet\">\n When the agent runs commands on a computer, it shows here with a link and its SSH command.\n </PaneEmpty>\n );\n }\n return (\n <div className=\"flex flex-col pb-3\">\n <ComputerLiveDesktop assetId={primary.assetId} className=\"mx-3 mt-3\" />\n <ComputerCard computer={primary} />\n {others.length > 0 ? (\n <>\n <SectionHeading>Also used</SectionHeading>\n <ul aria-label=\"Other computers\">\n {others.map((computer) => (\n <li key={computer.assetId} className=\"flex items-center gap-2 px-3 py-1.5\">\n <MonitorIcon className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 flex-1 truncate text-foreground text-sm\">\n {computer.title ?? computer.assetId}\n </span>\n <OpenInAthenaLink assetId={computer.assetId} compact label={`Open ${computer.title ?? 'computer'} in Athena`} />\n </li>\n ))}\n </ul>\n </>\n ) : null}\n </div>\n );\n};\n\n// ─── Pull requests ───────────────────────────────────────────────────\n\nconst PullRequestRow: FC<{ pullRequest: ConversationPullRequest }> = ({ pullRequest }) => {\n const state = pullRequestChipState(pullRequest);\n const Icon = PR_STATE_ICON[state];\n const meta = [\n `${pullRequest.repoFullName}#${pullRequest.number}`,\n state,\n PR_CHECKS_LABEL[pullRequest.checksState],\n reviewDecisionLabel(pullRequest.reviewDecision),\n ]\n .filter(Boolean)\n .join(' · ');\n return (\n <li>\n <a\n href={pullRequest.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-pr-state={state}\n className={cn('flex items-start gap-2.5 px-3 py-2 transition-colors hover:bg-muted/60', FOCUS_RING_CLASS)}\n >\n <Icon className={cn('mt-0.5 size-4 shrink-0', PR_STATE_CLASS[state])} aria-hidden />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block text-foreground text-sm leading-snug\">\n {pullRequest.title || `#${pullRequest.number}`}\n </span>\n <span className=\"block truncate text-[11px] text-muted-foreground\">{meta}</span>\n </span>\n <ExternalLinkIcon className=\"mt-0.5 size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n </a>\n </li>\n );\n};\n\nexport const SessionPullRequestsPane: FC = () => {\n const { threadId, isPersisted } = useSessionWorkbench();\n const list = useAthenaStatewireThreadList();\n const attention = useAthenaSessionAttention(threadId ?? undefined);\n if (!list) {\n return (\n <PaneEmpty icon={GitPullRequestIcon} title=\"Pull requests need the session list\">\n Mount the provider with enableThreadList to see the pull requests a session opened.\n </PaneEmpty>\n );\n }\n const pullRequests = isPersisted ? (attention?.pullRequests ?? []) : [];\n if (attention?.pullRequests === null && isPersisted) {\n return (\n <PaneEmpty icon={GitPullRequestIcon} title=\"Pull requests unavailable\">\n GitHub could not be read for this session right now.\n </PaneEmpty>\n );\n }\n if (pullRequests.length === 0) {\n return (\n <PaneEmpty icon={GitPullRequestIcon} title=\"No pull requests\">\n Pull requests the agent opens show here with their checks and review state.\n </PaneEmpty>\n );\n }\n const ordered = orderedPullRequests(pullRequests);\n return (\n <div className=\"flex flex-col pb-2\">\n <SectionHeading\n trailing={<span className=\"text-muted-foreground text-xs tabular-nums\">{ordered.length}</span>}\n >\n Pull requests\n </SectionHeading>\n <ul aria-label=\"Pull requests\">\n {ordered.map((pullRequest) => (\n <PullRequestRow key={pullRequest.url} pullRequest={pullRequest} />\n ))}\n </ul>\n </div>\n );\n};\n\n// ─── Attachments ─────────────────────────────────────────────────────\n\nconst ASSET_ICON: Record<string, FC<{ className?: string }>> = {\n spreadsheet: FileSpreadsheetIcon,\n document: FileTextIcon,\n presentation: PresentationIcon,\n presentation_pptx_studio: PresentationIcon,\n pptx: PresentationIcon,\n notebook: BookOpenIcon,\n image: ImageIcon,\n folder: FolderIcon,\n markdown: FileTextIcon,\n pdf: FileTextIcon,\n txt: FileTextIcon,\n};\n\nconst AttachmentRow: FC<{ asset: SessionWorkspaceAsset; canOpenHere: boolean }> = ({\n asset,\n canOpenHere,\n}) => {\n const openAsset = useAssetPanelStore((state) => state.openAsset);\n const Icon = ASSET_ICON[asset.assetType] ?? FileIcon;\n const panelType: AssetPanelType | null = assetPanelTypeOf(asset.assetType);\n const title = asset.title ?? asset.assetId;\n const meta = [\n assetTypeLabel(asset.assetType),\n asset.updatedAt ? `updated ${relativeTime(asset.updatedAt)}` : null,\n ]\n .filter(Boolean)\n .join(' · ');\n const body = (\n <>\n <Icon className=\"mt-0.5 size-4 shrink-0 text-muted-foreground\" />\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-foreground text-sm\">{title}</span>\n <span className=\"block truncate text-[11px] text-muted-foreground\" title={fullTime(asset.updatedAt)}>\n {meta}\n </span>\n </span>\n </>\n );\n return (\n <li className=\"flex items-start gap-1 pr-2\">\n {canOpenHere && panelType ? (\n <button\n type=\"button\"\n onClick={() => openAsset(asset.assetId, { name: title, type: panelType })}\n className={cn(\n 'flex min-w-0 flex-1 items-start gap-2.5 px-3 py-2 text-left transition-colors hover:bg-muted/60',\n FOCUS_RING_CLASS,\n )}\n >\n {body}\n </button>\n ) : (\n <div className=\"flex min-w-0 flex-1 items-start gap-2.5 px-3 py-2\">{body}</div>\n )}\n <span className=\"pt-1.5\">\n <OpenInAthenaLink assetId={asset.assetId} compact label={`Open ${title} in Athena`} />\n </span>\n </li>\n );\n};\n\nexport const SessionAttachmentsPane: FC = () => {\n const { isPersisted } = useSessionWorkbench();\n const workspace = useSessionWorkbenchWorkspace();\n const canOpenHere = useAssetPanelStore((state) => state.assetPanelHostCount > 0);\n if (!isPersisted) {\n return (\n <PaneEmpty icon={PaperclipIcon} title=\"No files yet\">\n Documents, sheets and decks the agent creates or edits show here.\n </PaneEmpty>\n );\n }\n if (workspace.isError) return <PaneError error={workspace.error} />;\n if (!workspace.data) return <PaneSkeleton />;\n const { assets } = workspace.data;\n if (assets.length === 0) {\n return (\n <PaneEmpty icon={PaperclipIcon} title=\"No files yet\">\n Documents, sheets and decks the agent creates or edits show here.\n </PaneEmpty>\n );\n }\n return (\n <div className=\"flex flex-col pb-2\">\n <SectionHeading\n trailing={<span className=\"text-muted-foreground text-xs tabular-nums\">{assets.length}</span>}\n >\n Created or edited\n </SectionHeading>\n <ul aria-label=\"Session files\">\n {assets.map((asset) => (\n <AttachmentRow key={asset.assetId} asset={asset} canOpenHere={canOpenHere} />\n ))}\n </ul>\n </div>\n );\n};\n\n// ─── Registry ────────────────────────────────────────────────────────\n\nexport const SESSION_WORKBENCH_TASKS_PANE: SessionWorkbenchPane = {\n id: 'tasks',\n title: 'Tasks',\n description: \"Follow the agent's plan step by step\",\n icon: ListChecksIcon,\n render: () => <SessionTasksPane />,\n};\n\nexport const SESSION_WORKBENCH_SHELL_PANE: SessionWorkbenchPane = {\n id: 'shell',\n title: 'Shell',\n description: 'Commands the agent ran and their output',\n icon: TerminalIcon,\n render: () => <SessionShellPane />,\n};\n\nexport const SESSION_WORKBENCH_COMPUTER_PANE: SessionWorkbenchPane = {\n id: 'computer',\n title: 'Computer',\n description: 'The machine this session works on, and how to reach it',\n icon: MonitorIcon,\n render: () => <SessionComputerPane />,\n};\n\nexport const SESSION_WORKBENCH_PULL_REQUESTS_PANE: SessionWorkbenchPane = {\n id: 'pull-requests',\n title: 'Pull requests',\n description: 'Pull requests this session opened, with checks and reviews',\n icon: GitPullRequestIcon,\n render: () => <SessionPullRequestsPane />,\n};\n\nexport const SESSION_WORKBENCH_ATTACHMENTS_PANE: SessionWorkbenchPane = {\n id: 'attachments',\n title: 'Attachments',\n description: 'Files and assets this session created or edited',\n icon: PaperclipIcon,\n render: () => <SessionAttachmentsPane />,\n};\n\n/** The built-in panes, in \"+\" menu order. Spread it to add your own. */\nexport const DEFAULT_SESSION_WORKBENCH_PANES: readonly SessionWorkbenchPane[] = [\n SESSION_WORKBENCH_TASKS_PANE,\n SESSION_WORKBENCH_SHELL_PANE,\n SESSION_WORKBENCH_COMPUTER_PANE,\n SESSION_WORKBENCH_PULL_REQUESTS_PANE,\n SESSION_WORKBENCH_ATTACHMENTS_PANE,\n];\n\n/** The panes a thread opens with until the person changes its tabs. */\nexport const DEFAULT_SESSION_WORKBENCH_OPEN_PANE_IDS: readonly string[] = [\n SESSION_WORKBENCH_TASKS_PANE.id,\n SESSION_WORKBENCH_SHELL_PANE.id,\n SESSION_WORKBENCH_COMPUTER_PANE.id,\n];\n"],"names":["jsxs","jsx","AlertCircleIcon","useAuiState","hasStatewireThreadExtras","useStatewireState","readDeepAgentStreamValues","useMemo","taskProgress","deriveTaskSteps","ListChecksIcon","PlanStepMarker","PLAN_STEP_STATUS_LABEL","cn","LoaderIcon","CheckIcon","XIcon","CircleDashedIcon","relativeTime","useSessionWorkbench","useSessionWorkbenchWorkspace","useStatewireHistory","deriveShellRows","useState","filterShellRows","TerminalIcon","SearchIcon","useAthenaComputerStatus","computerStateBadge","useAthenaComputerSshAccess","_a","CopyIcon","useAthenaConfig","createAthenaSpacesUrl","athenaLinkAppUrl","ExternalLinkIcon","MonitorIcon","workspaceComputers","ComputerLiveDesktop","Fragment","pullRequestChipState","PR_STATE_ICON","PR_CHECKS_LABEL","reviewDecisionLabel","PR_STATE_CLASS","useAthenaStatewireThreadList","useAthenaSessionAttention","GitPullRequestIcon","orderedPullRequests","FileSpreadsheetIcon","FileTextIcon","PresentationIcon","BookOpenIcon","ImageIcon","FolderIcon","useAssetPanelStore","FileIcon","assetPanelTypeOf","assetTypeLabel","PaperclipIcon"],"mappings":"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"}
@@ -11,7 +11,7 @@ import { PR_CHECKS_LABEL, PR_STATE_ICON, PR_STATE_CLASS } from "../threads/pull-
11
11
  import { orderedPullRequests, relativeTime, pullRequestChipState } from "../threads/session-list-model.js";
12
12
  import { useAthenaStatewireThreadList } from "../threads/statewire-thread-list.js";
13
13
  import { useAthenaSessionAttention } from "../threads/use-athena-session-attention.js";
14
- import { PlanStepMarker } from "../tools/write-todos-tool-ui.js";
14
+ import { PlanStepMarker, PLAN_STEP_STATUS_LABEL } from "../tools/write-todos-tool-ui.js";
15
15
  import { useSessionWorkbench, useSessionWorkbenchWorkspace } from "./session-workbench-context.js";
16
16
  import { workspaceComputers, assetPanelTypeOf, assetTypeLabel, reviewDecisionLabel, deriveShellRows, filterShellRows, taskProgress, deriveTaskSteps, computerStateBadge } from "./session-workbench-model.js";
17
17
  import { ComputerLiveDesktop } from "./ComputerLiveDesktop.js";
@@ -49,13 +49,6 @@ const RequiresStatewireThread = ({
49
49
  }
50
50
  return /* @__PURE__ */ jsx(Fragment, { children });
51
51
  };
52
- const TASK_STATUS_LABEL = {
53
- pending: "To do",
54
- in_progress: "In progress",
55
- completed: "Done",
56
- failed: "Failed",
57
- cancelled: "Cancelled"
58
- };
59
52
  const TasksPaneContent = () => {
60
53
  const stateTodos = useStatewireState((state) => readDeepAgentStreamValues(state).todos);
61
54
  const messages = useAuiState((state) => state.thread.messages);
@@ -92,7 +85,7 @@ const TasksPaneContent = () => {
92
85
  /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "size-[18px] shrink-0 rounded-full border border-border border-dashed" })
93
86
  ) : /* @__PURE__ */ jsx(PlanStepMarker, { status: step.status, index }),
94
87
  /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
95
- TASK_STATUS_LABEL[step.status],
88
+ PLAN_STEP_STATUS_LABEL[step.status],
96
89
  ": "
97
90
  ] }),
98
91
  /* @__PURE__ */ jsxs(