@invergent/agent-chat-react 2.5.8 → 2.6.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.
package/dist/index.cjs CHANGED
@@ -18,7 +18,7 @@ var streamdown = require('streamdown');
18
18
  var reactUseControllableState = require('@radix-ui/react-use-controllable-state');
19
19
  var shiki = require('shiki');
20
20
  var diff = require('diff');
21
- var Ansi = require('ansi-to-react');
21
+ var AnsiImport = require('ansi-to-react');
22
22
  var cmdk = require('cmdk');
23
23
  var nanoid = require('nanoid');
24
24
  var tokenlens = require('tokenlens');
@@ -28,7 +28,7 @@ var dateFns = require('date-fns');
28
28
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
29
29
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
30
30
 
31
- var Ansi__default = /*#__PURE__*/_interopDefault(Ansi);
31
+ var AnsiImport__default = /*#__PURE__*/_interopDefault(AnsiImport);
32
32
 
33
33
  var __defProp = Object.defineProperty;
34
34
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -1025,6 +1025,11 @@ var MessageResponse = react.memo(
1025
1025
  {
1026
1026
  className: cn(
1027
1027
  "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
1028
+ // Streamdown emits ``list-inside`` lists and preflight strips
1029
+ // the UA list padding, so bullet markers hug the container
1030
+ // edge and wrapped lines slide under them. Hanging-indent
1031
+ // lists instead, everywhere markdown renders.
1032
+ "[&_ol]:list-outside [&_ol]:pl-5 [&_ul]:list-outside [&_ul]:pl-5",
1028
1033
  className
1029
1034
  ),
1030
1035
  plugins: streamdownPlugins,
@@ -1215,7 +1220,14 @@ var ReasoningContent = react.memo(
1215
1220
  className
1216
1221
  ),
1217
1222
  ...props,
1218
- children: /* @__PURE__ */ jsxRuntime.jsx(streamdown.Streamdown, { plugins: streamdownPlugins2, children })
1223
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1224
+ streamdown.Streamdown,
1225
+ {
1226
+ className: "[&_ol]:list-outside [&_ol]:pl-5 [&_ul]:list-outside [&_ul]:pl-5",
1227
+ plugins: streamdownPlugins2,
1228
+ children
1229
+ }
1230
+ )
1219
1231
  }
1220
1232
  )
1221
1233
  );
@@ -3309,7 +3321,7 @@ var TerminalContent = ({
3309
3321
  ref: containerRef,
3310
3322
  ...props,
3311
3323
  children: children ?? /* @__PURE__ */ jsxRuntime.jsxs("pre", { className: "whitespace-pre-wrap break-words", children: [
3312
- /* @__PURE__ */ jsxRuntime.jsx(Ansi__default.default, { children: output }),
3324
+ /* @__PURE__ */ jsxRuntime.jsx(AnsiImport__default.default, { children: output }),
3313
3325
  isStreaming && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-0.5 inline-block h-4 w-2 animate-pulse bg-foreground" })
3314
3326
  ] })
3315
3327
  }
@@ -4573,6 +4585,108 @@ function ResearchOutlineBlock({ tc }) {
4573
4585
  outline ? /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[11px] leading-snug", children: outline }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "updated" })
4574
4586
  ] });
4575
4587
  }
4588
+ init_utils();
4589
+ var Ansi2 = AnsiImport__default.default.default ?? AnsiImport__default.default;
4590
+ function parseCodeRun(result, args) {
4591
+ const fallback = {
4592
+ agent: "",
4593
+ provider: "",
4594
+ prompt: "",
4595
+ output: "",
4596
+ finalMessage: "",
4597
+ error: null,
4598
+ inputTokens: 0,
4599
+ outputTokens: 0
4600
+ };
4601
+ if (result) {
4602
+ try {
4603
+ const parsed = JSON.parse(result);
4604
+ return { ...fallback, ...parsed, error: parsed.error ?? null };
4605
+ } catch {
4606
+ }
4607
+ }
4608
+ try {
4609
+ const parsedArgs = JSON.parse(args);
4610
+ return { ...fallback, ...parsedArgs, error: null };
4611
+ } catch {
4612
+ return fallback;
4613
+ }
4614
+ }
4615
+ function agentLabel(state) {
4616
+ if (state.agent) return state.agent;
4617
+ if (state.provider === "anthropic") return "claude";
4618
+ if (state.provider === "openai") return "codex";
4619
+ return "coding agent";
4620
+ }
4621
+ function CodeRunToolBlock({
4622
+ tc,
4623
+ defaultOpen = false
4624
+ }) {
4625
+ const [isOpen, setIsOpen] = react.useState(defaultOpen);
4626
+ const isRunning = tc.status === "running";
4627
+ const state = parseCodeRun(tc.result, tc.args);
4628
+ const label = agentLabel(state);
4629
+ const hasError = tc.status === "error" || Boolean(state.error);
4630
+ return /* @__PURE__ */ jsxRuntime.jsxs(
4631
+ Collapsible,
4632
+ {
4633
+ open: isOpen,
4634
+ onOpenChange: setIsOpen,
4635
+ className: "not-prose w-full",
4636
+ children: [
4637
+ /* @__PURE__ */ jsxRuntime.jsxs(CollapsibleTrigger, { className: "group/trigger flex w-fit items-center gap-2 text-sm transition-colors", children: [
4638
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-left", children: isRunning ? /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { as: "span", duration: 1, children: `Running ${label}...` }) : hasError ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-destructive", children: `${label} failed` }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: `Ran ${label}` }) }),
4639
+ /* @__PURE__ */ jsxRuntime.jsx(
4640
+ lucideReact.ChevronDownIcon,
4641
+ {
4642
+ className: cn(
4643
+ "size-4 shrink-0 transition-transform",
4644
+ isOpen ? "rotate-180" : "rotate-0"
4645
+ )
4646
+ }
4647
+ )
4648
+ ] }),
4649
+ /* @__PURE__ */ jsxRuntime.jsx(
4650
+ CollapsibleContent,
4651
+ {
4652
+ className: cn(
4653
+ "mt-2 overflow-hidden",
4654
+ "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 outline-none data-[state=closed]:animate-out data-[state=open]:animate-in"
4655
+ ),
4656
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-border overflow-hidden", children: [
4657
+ state.prompt && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 bg-background text-muted-foreground px-3 py-2 font-mono text-sm leading-relaxed", children: [
4658
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 select-none text-emerald-600", children: "IN" }),
4659
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap wrap-break-word text-foreground/90 flex-1", children: state.prompt })
4660
+ ] }),
4661
+ /* @__PURE__ */ jsxRuntime.jsxs(
4662
+ "div",
4663
+ {
4664
+ className: cn(
4665
+ "px-3 py-2 bg-background font-mono text-sm leading-relaxed max-h-96 overflow-auto",
4666
+ state.prompt && "border-t border-border"
4667
+ ),
4668
+ children: [
4669
+ (state.output || !isRunning) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 text-foreground/90", children: [
4670
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 select-none text-sky-600", children: "OUT" }),
4671
+ /* @__PURE__ */ jsxRuntime.jsxs("pre", { className: "whitespace-pre-wrap wrap-break-word flex-1", children: [
4672
+ state.output ? /* @__PURE__ */ jsxRuntime.jsx(Ansi2, { children: state.output }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/60", children: "(no output)" }),
4673
+ isRunning && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-0.5 inline-block h-3.5 w-1.5 animate-pulse bg-foreground" })
4674
+ ] })
4675
+ ] }),
4676
+ isRunning && !state.output && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-0.5 inline-block h-3.5 w-1.5 animate-pulse bg-foreground" })
4677
+ ]
4678
+ }
4679
+ ),
4680
+ !isRunning && hasError && state.error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border bg-destructive/5 px-3 py-2 text-sm text-destructive", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap wrap-break-word", children: state.error }) }),
4681
+ !isRunning && !hasError && state.finalMessage && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border bg-background px-3 py-2 text-sm text-foreground/90", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap wrap-break-word", children: state.finalMessage }) }),
4682
+ !isRunning && (state.inputTokens > 0 || state.outputTokens > 0) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border bg-background px-3 py-1.5 text-xs text-muted-foreground", children: `${state.inputTokens.toLocaleString()} in \xB7 ${state.outputTokens.toLocaleString()} out tokens` })
4683
+ ] })
4684
+ }
4685
+ )
4686
+ ]
4687
+ }
4688
+ );
4689
+ }
4576
4690
  function ToolCallBlock({
4577
4691
  tc,
4578
4692
  resolvedArtifactName,
@@ -4629,6 +4743,8 @@ function ToolCallBlock({
4629
4743
  return /* @__PURE__ */ jsxRuntime.jsx(ResearchOutlineBlock, { tc });
4630
4744
  case "research_memory":
4631
4745
  return /* @__PURE__ */ jsxRuntime.jsx(ResearchMemoryBlock, { tc });
4746
+ case "code_run":
4747
+ return /* @__PURE__ */ jsxRuntime.jsx(CodeRunToolBlock, { tc });
4632
4748
  case "spawn_worker":
4633
4749
  case "send_worker_message":
4634
4750
  case "stop_worker":
@@ -6222,6 +6338,7 @@ function ChatComposerInner({
6222
6338
  canShowBrowser = false,
6223
6339
  canShowWorkspace = false,
6224
6340
  deepResearchEnabled = false,
6341
+ codeAgentsEnabled = false,
6225
6342
  viewMode = "simple",
6226
6343
  onViewModeChange
6227
6344
  }) {
@@ -6276,9 +6393,18 @@ function ChatComposerInner({
6276
6393
  description: "Delegate a topic to the deep-research workflow"
6277
6394
  });
6278
6395
  }
6396
+ if (codeAgentsEnabled) {
6397
+ base.push(
6398
+ { value: "/code claude ", label: "/code claude", description: "Run Claude Code on the workspace (your plan)" },
6399
+ { value: "/code codex ", label: "/code codex", description: "Run Codex on the workspace (your plan)" },
6400
+ { value: "/code status", label: "/code status", description: "Show connected coding agents" },
6401
+ { value: "/code login claude", label: "/code login claude", description: "Connect your Claude plan" },
6402
+ { value: "/code login codex", label: "/code login codex", description: "Connect your ChatGPT plan" }
6403
+ );
6404
+ }
6279
6405
  return base;
6280
6406
  },
6281
- [deepResearchEnabled]
6407
+ [deepResearchEnabled, codeAgentsEnabled]
6282
6408
  );
6283
6409
  const scheduledExamples = react.useMemo(
6284
6410
  () => [
@@ -7503,6 +7629,54 @@ function ArtifactSvg({ spec }) {
7503
7629
  spec.caption && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: spec.caption })
7504
7630
  ] });
7505
7631
  }
7632
+ init_utils();
7633
+ function useMediaSrc(sessionId, path) {
7634
+ const { adapter } = useAgentChatAdapterContext();
7635
+ return react.useMemo(
7636
+ () => adapter.getWorkspaceDownloadUrl({ sessionId, path }),
7637
+ [adapter, sessionId, path]
7638
+ );
7639
+ }
7640
+ function MediaCaption({ caption }) {
7641
+ if (!caption) return null;
7642
+ return /* @__PURE__ */ jsxRuntime.jsx("figcaption", { className: "border-t border-border px-3 py-2 text-xs leading-snug text-muted-foreground", children: caption });
7643
+ }
7644
+ function ArtifactImage({ sessionId, spec, fill = false }) {
7645
+ const src = useMediaSrc(sessionId, spec.path);
7646
+ return /* @__PURE__ */ jsxRuntime.jsxs("figure", { className: "m-0", children: [
7647
+ /* @__PURE__ */ jsxRuntime.jsx(
7648
+ "img",
7649
+ {
7650
+ src,
7651
+ alt: spec.caption ?? spec.path,
7652
+ loading: "lazy",
7653
+ className: cn(
7654
+ "block w-full bg-muted/30 object-contain",
7655
+ fill ? "max-h-full" : "max-h-[480px]"
7656
+ )
7657
+ }
7658
+ ),
7659
+ /* @__PURE__ */ jsxRuntime.jsx(MediaCaption, { caption: spec.caption })
7660
+ ] });
7661
+ }
7662
+ function ArtifactVideo({ sessionId, spec, fill = false }) {
7663
+ const src = useMediaSrc(sessionId, spec.path);
7664
+ return /* @__PURE__ */ jsxRuntime.jsxs("figure", { className: "m-0", children: [
7665
+ /* @__PURE__ */ jsxRuntime.jsx(
7666
+ "video",
7667
+ {
7668
+ src,
7669
+ controls: true,
7670
+ preload: "metadata",
7671
+ className: cn(
7672
+ "block w-full bg-black object-contain",
7673
+ fill ? "max-h-full" : "max-h-[480px]"
7674
+ )
7675
+ }
7676
+ ),
7677
+ /* @__PURE__ */ jsxRuntime.jsx(MediaCaption, { caption: spec.caption })
7678
+ ] });
7679
+ }
7506
7680
 
7507
7681
  // src/components/chat/artifacts/artifact-export.ts
7508
7682
  function exportArtifact(payload) {
@@ -7537,6 +7711,17 @@ function exportArtifact(payload) {
7537
7711
  mime: "image/svg+xml",
7538
7712
  extension: "svg"
7539
7713
  };
7714
+ // Media artifacts are file references — the bytes live in the
7715
+ // workspace. Copy yields the path; the download action in
7716
+ // ArtifactBlock bypasses this serialisation and uses the
7717
+ // authenticated workspace download URL directly.
7718
+ case "image":
7719
+ case "video":
7720
+ return {
7721
+ text: payload.spec.path,
7722
+ mime: "text/plain",
7723
+ extension: "txt"
7724
+ };
7540
7725
  }
7541
7726
  }
7542
7727
  function tableToCsv(columns, rows) {
@@ -7589,7 +7774,9 @@ var KIND_LABEL = {
7589
7774
  table: "Table",
7590
7775
  chart: "Chart",
7591
7776
  html: "HTML preview",
7592
- svg: "SVG"
7777
+ svg: "SVG",
7778
+ image: "Generated image",
7779
+ video: "Generated video"
7593
7780
  };
7594
7781
  var COPY_FEEDBACK_MS = 1500;
7595
7782
  function ArtifactBlock({
@@ -7630,6 +7817,18 @@ function ArtifactBlock({
7630
7817
  };
7631
7818
  const handleDownload = () => {
7632
7819
  if (!payload) return;
7820
+ if (payload.kind === "image" || payload.kind === "video") {
7821
+ const a = document.createElement("a");
7822
+ a.href = adapter.getWorkspaceDownloadUrl({
7823
+ sessionId,
7824
+ path: payload.spec.path
7825
+ });
7826
+ a.download = "";
7827
+ document.body.appendChild(a);
7828
+ a.click();
7829
+ document.body.removeChild(a);
7830
+ return;
7831
+ }
7633
7832
  const { text, mime, extension } = exportArtifact(payload);
7634
7833
  downloadText(`${safeFilename(name)}.${extension}`, text, mime);
7635
7834
  };
@@ -7675,20 +7874,21 @@ function ArtifactBlock({
7675
7874
  )
7676
7875
  ] })
7677
7876
  ] }),
7678
- /* @__PURE__ */ jsxRuntime.jsx(ArtifactContent, { className: "p-0 overflow-visible", children: /* @__PURE__ */ jsxRuntime.jsx(ArtifactBody, { error, payload }) })
7877
+ /* @__PURE__ */ jsxRuntime.jsx(ArtifactContent, { className: "p-0 overflow-visible", children: /* @__PURE__ */ jsxRuntime.jsx(ArtifactBody, { error, payload, sessionId }) })
7679
7878
  ] }),
7680
7879
  /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: expanded, onOpenChange: setExpanded, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "flex h-[95vh] w-[95vw] max-w-[95vw] flex-col gap-4 overflow-hidden p-6 sm:max-w-[95vw]", children: [
7681
7880
  /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
7682
7881
  /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { className: "truncate pr-10", children: name }),
7683
7882
  /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: description })
7684
7883
  ] }),
7685
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ArtifactBody, { error, payload, fill: true }) })
7884
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ArtifactBody, { error, payload, sessionId, fill: true }) })
7686
7885
  ] }) })
7687
7886
  ] });
7688
7887
  }
7689
7888
  function ArtifactBody({
7690
7889
  error,
7691
7890
  payload,
7891
+ sessionId,
7692
7892
  fill = false
7693
7893
  }) {
7694
7894
  if (error) {
@@ -7714,6 +7914,10 @@ function ArtifactBody({
7714
7914
  return /* @__PURE__ */ jsxRuntime.jsx(ArtifactHtml, { spec: payload.spec, fill });
7715
7915
  case "svg":
7716
7916
  return /* @__PURE__ */ jsxRuntime.jsx(ArtifactSvg, { spec: payload.spec });
7917
+ case "image":
7918
+ return /* @__PURE__ */ jsxRuntime.jsx(ArtifactImage, { sessionId, spec: payload.spec, fill });
7919
+ case "video":
7920
+ return /* @__PURE__ */ jsxRuntime.jsx(ArtifactVideo, { sessionId, spec: payload.spec, fill });
7717
7921
  }
7718
7922
  }
7719
7923
  init_utils();
@@ -8820,13 +9024,17 @@ function cancelledToolLabel(toolName) {
8820
9024
  ask_user_question: "Ask User Question",
8821
9025
  process: "Process",
8822
9026
  create_artifact: "Create artifact",
9027
+ run_coding_agent: "Coding agent",
9028
+ code_run: "Coding agent",
8823
9029
  // ``Research memory`` reads as a noun ("memory of research") and
8824
9030
  // makes the shimmer awkward ("Running Research memory…"). The
8825
9031
  // one-liner renderer shows just ``Research`` as the label, so
8826
9032
  // mirror that here so the live shimmer matches the row that
8827
9033
  // replaces it on completion.
8828
9034
  research_memory: "Research",
8829
- research_outline: "Research"
9035
+ research_outline: "Research",
9036
+ generate_image: "image generation",
9037
+ generate_video: "video generation"
8830
9038
  };
8831
9039
  if (map[toolName]) return map[toolName];
8832
9040
  if (toolName.startsWith("mcp__")) return formatMcpToolLabel(toolName);
@@ -9114,6 +9322,11 @@ function extractToolDetail(tc) {
9114
9322
  case "consult_expert":
9115
9323
  case "delegate_task":
9116
9324
  return stringArg("name") ?? stringArg("task") ?? stringArg("title");
9325
+ case "run_coding_agent":
9326
+ case "code_run": {
9327
+ const prompt = stringArg("prompt");
9328
+ return prompt ? truncate3(prompt, 60) : null;
9329
+ }
9117
9330
  case "memory":
9118
9331
  return stringArg("action") ?? stringArg("key");
9119
9332
  case "research_memory": {
@@ -9237,7 +9450,9 @@ var _TOOL_ROW_ICON = {
9237
9450
  memory: lucideReact.PenLineIcon,
9238
9451
  todo: lucideReact.ListIcon,
9239
9452
  research_memory: lucideReact.BookOpenIcon,
9240
- research_outline: lucideReact.ListIcon
9453
+ research_outline: lucideReact.ListIcon,
9454
+ run_coding_agent: lucideReact.BotIcon,
9455
+ code_run: lucideReact.BotIcon
9241
9456
  };
9242
9457
  function _toolRowIcon(toolName) {
9243
9458
  return _TOOL_ROW_ICON[toolName] ?? lucideReact.WrenchIcon;
@@ -9283,6 +9498,13 @@ function _toolRowLabel(tc) {
9283
9498
  return detail ? `Memory ${detail}` : "Updated memory";
9284
9499
  case "todo":
9285
9500
  return detail ? `Todo ${detail}` : "Updated todo list";
9501
+ case "run_coding_agent":
9502
+ case "code_run": {
9503
+ const a = parseArgs(tc.args);
9504
+ const agent = a?.agent ?? (a?.provider === "openai" ? "codex" : a?.provider === "anthropic" ? "claude" : void 0);
9505
+ const name = agent === "codex" ? "Codex" : agent === "claude" ? "Claude Code" : "coding agent";
9506
+ return detail ? `${name}: ${detail}` : `Ran ${name}`;
9507
+ }
9286
9508
  case "research_memory": {
9287
9509
  const args = parseArgs(tc.args);
9288
9510
  const action = args?.action;
@@ -9366,6 +9588,9 @@ function IterationExpanded({
9366
9588
  if (tc.toolName === "todo") {
9367
9589
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pr-1", children: /* @__PURE__ */ jsxRuntime.jsx(TodoToolBlock, { tc }) }, tc.id);
9368
9590
  }
9591
+ if (tc.toolName === "code_run") {
9592
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pr-1", children: /* @__PURE__ */ jsxRuntime.jsx(CodeRunToolBlock, { tc, defaultOpen: true }) }, tc.id);
9593
+ }
9369
9594
  return /* @__PURE__ */ jsxRuntime.jsx(SimpleDetailRow, { icon: _toolRowIcon(tc.toolName), children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: _toolRowLabel(tc) }) }, tc.id);
9370
9595
  }),
9371
9596
  showDone && /* @__PURE__ */ jsxRuntime.jsx(SimpleDetailRow, { icon: lucideReact.CircleCheckIcon, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground", children: "Done" }) })
@@ -9692,6 +9917,7 @@ function ChatThread({
9692
9917
  viewMode = "simple",
9693
9918
  onViewModeChange,
9694
9919
  deepResearchEnabled = false,
9920
+ codeAgentsEnabled = false,
9695
9921
  researchSources = [],
9696
9922
  hideTurnSummary = false,
9697
9923
  agentId,
@@ -9756,7 +9982,8 @@ function ChatThread({
9756
9982
  canShowWorkspace,
9757
9983
  viewMode,
9758
9984
  onViewModeChange,
9759
- deepResearchEnabled
9985
+ deepResearchEnabled,
9986
+ codeAgentsEnabled
9760
9987
  }
9761
9988
  ),
9762
9989
  onOpenIntegrations && /* @__PURE__ */ jsxRuntime.jsx(
@@ -10585,6 +10812,9 @@ var AGENT_CHAT_LISTENED_EVENTS = [
10585
10812
  "expert.failure",
10586
10813
  "expert.endorse",
10587
10814
  "expert.override",
10815
+ "code.run_started",
10816
+ "code.run_progress",
10817
+ "code.run_result",
10588
10818
  "user.feedback",
10589
10819
  "artifact.created",
10590
10820
  "artifact.updated",
@@ -10764,6 +10994,21 @@ function applyAgentChatEvent(state, event) {
10764
10994
  nextState,
10765
10995
  applyExpertFeedback(nextState.messages, event.type, event.data)
10766
10996
  );
10997
+ case "code.run_started":
10998
+ return applyCodeRunStarted(nextState, event);
10999
+ case "code.run_progress":
11000
+ return withMessages(
11001
+ nextState,
11002
+ applyCodeRunProgress(nextState.messages, event.data)
11003
+ );
11004
+ case "code.run_result":
11005
+ return {
11006
+ ...withMessages(
11007
+ nextState,
11008
+ applyCodeRunResult(nextState.messages, event.data)
11009
+ ),
11010
+ isRunning: false
11011
+ };
10767
11012
  case "user.feedback":
10768
11013
  return withMessages(
10769
11014
  nextState,
@@ -11314,6 +11559,141 @@ function applyExpertDelegation(state, event) {
11314
11559
  isRunning: true
11315
11560
  };
11316
11561
  }
11562
+ var CODE_RUN_TOOL = "code_run";
11563
+ function codeRunFrameId(runId) {
11564
+ return `code-run-${runId}`;
11565
+ }
11566
+ function parseCodeRunState(raw) {
11567
+ if (raw) {
11568
+ try {
11569
+ const parsed = JSON.parse(raw);
11570
+ return {
11571
+ agent: parsed.agent ?? "",
11572
+ provider: parsed.provider ?? "",
11573
+ prompt: parsed.prompt ?? "",
11574
+ output: parsed.output ?? "",
11575
+ finalMessage: parsed.finalMessage ?? "",
11576
+ error: parsed.error ?? null,
11577
+ inputTokens: parsed.inputTokens ?? 0,
11578
+ outputTokens: parsed.outputTokens ?? 0
11579
+ };
11580
+ } catch {
11581
+ }
11582
+ }
11583
+ return {
11584
+ agent: "",
11585
+ provider: "",
11586
+ prompt: "",
11587
+ output: "",
11588
+ finalMessage: "",
11589
+ error: null,
11590
+ inputTokens: 0,
11591
+ outputTokens: 0
11592
+ };
11593
+ }
11594
+ function findCodeRunFrame(messages, runId) {
11595
+ const toolId = codeRunFrameId(runId);
11596
+ for (let i = messages.length - 1; i >= 0; i--) {
11597
+ const tc = messages[i]?.toolCalls?.find((c) => c.id === toolId);
11598
+ if (tc) return { msgIdx: i, toolId };
11599
+ }
11600
+ return null;
11601
+ }
11602
+ function applyCodeRunStarted(state, event) {
11603
+ const runId = stringValue2(event.data.run_id);
11604
+ if (!runId) return state;
11605
+ if (findCodeRunFrame(state.messages, runId)) {
11606
+ return { ...state, terminal: false, isRunning: true };
11607
+ }
11608
+ const agent = stringValue2(event.data.agent);
11609
+ const provider = stringValue2(event.data.provider);
11610
+ const prompt = stringValue2(event.data.prompt);
11611
+ const messages = [...state.messages];
11612
+ const assistantIdx = findLastAssistantIndex(messages);
11613
+ let assistant = assistantIdx >= 0 ? messages[assistantIdx] : null;
11614
+ const userAfterAssistant = assistantIdx >= 0 && hasUserAfterIndex(messages, assistantIdx);
11615
+ if (!assistant || assistant.status === "complete" || userAfterAssistant) {
11616
+ assistant = {
11617
+ id: `evt-${event.eventId}-code-run`,
11618
+ role: "assistant",
11619
+ content: "",
11620
+ createdAt: /* @__PURE__ */ new Date(),
11621
+ status: "streaming"
11622
+ };
11623
+ messages.push(assistant);
11624
+ } else {
11625
+ assistant = { ...assistant };
11626
+ messages[assistantIdx] = assistant;
11627
+ }
11628
+ const frameState = {
11629
+ agent,
11630
+ provider,
11631
+ prompt,
11632
+ output: "",
11633
+ finalMessage: "",
11634
+ error: null,
11635
+ inputTokens: 0,
11636
+ outputTokens: 0
11637
+ };
11638
+ const toolCall = {
11639
+ id: codeRunFrameId(runId),
11640
+ toolName: CODE_RUN_TOOL,
11641
+ args: JSON.stringify({ agent, provider, prompt }),
11642
+ status: "running",
11643
+ result: JSON.stringify(frameState)
11644
+ };
11645
+ assistant.toolCalls = [...assistant.toolCalls ?? [], toolCall];
11646
+ return { ...state, messages, terminal: false, isRunning: true };
11647
+ }
11648
+ function applyCodeRunProgress(messages, data) {
11649
+ const runId = stringValue2(data.run_id);
11650
+ if (!runId) return messages;
11651
+ const match = findCodeRunFrame(messages, runId);
11652
+ if (!match) return messages;
11653
+ const chunk = stringValue2(data.chunk);
11654
+ if (!chunk) return messages;
11655
+ const next = [...messages];
11656
+ const msg = next[match.msgIdx];
11657
+ next[match.msgIdx] = {
11658
+ ...msg,
11659
+ toolCalls: msg.toolCalls?.map((tc) => {
11660
+ if (tc.id !== match.toolId) return tc;
11661
+ const frame = parseCodeRunState(tc.result);
11662
+ frame.output += chunk;
11663
+ return { ...tc, result: JSON.stringify(frame) };
11664
+ })
11665
+ };
11666
+ return next;
11667
+ }
11668
+ function applyCodeRunResult(messages, data) {
11669
+ const runId = stringValue2(data.run_id);
11670
+ if (!runId) return messages;
11671
+ const match = findCodeRunFrame(messages, runId);
11672
+ if (!match) return messages;
11673
+ const error = optionalStringValue(data.error) ?? null;
11674
+ const finalMessage = stringValue2(data.final_message);
11675
+ const inputTokens = numberValue(data.input_tokens);
11676
+ const outputTokens = numberValue(data.output_tokens);
11677
+ const next = [...messages];
11678
+ const msg = next[match.msgIdx];
11679
+ next[match.msgIdx] = {
11680
+ ...msg,
11681
+ toolCalls: msg.toolCalls?.map((tc) => {
11682
+ if (tc.id !== match.toolId) return tc;
11683
+ const frame = parseCodeRunState(tc.result);
11684
+ frame.finalMessage = finalMessage;
11685
+ frame.error = error;
11686
+ frame.inputTokens = inputTokens;
11687
+ frame.outputTokens = outputTokens;
11688
+ return {
11689
+ ...tc,
11690
+ status: error ? "error" : "complete",
11691
+ result: JSON.stringify(frame)
11692
+ };
11693
+ })
11694
+ };
11695
+ return next;
11696
+ }
11317
11697
  function applyExpertFailure(messages, eventId, data) {
11318
11698
  const match = findLatestConsultExpertCall(messages);
11319
11699
  if (!match) return messages;
@@ -11987,6 +12367,7 @@ function AgentChat({
11987
12367
  disabled,
11988
12368
  onComposerError,
11989
12369
  deepResearchEnabled = false,
12370
+ codeAgentsEnabled = false,
11990
12371
  onOpenIntegrations
11991
12372
  }) {
11992
12373
  const [workspacePath, setWorkspacePath] = react.useState(null);
@@ -12136,6 +12517,7 @@ function AgentChat({
12136
12517
  viewMode: runtime.viewMode,
12137
12518
  onViewModeChange: runtime.setViewMode,
12138
12519
  deepResearchEnabled,
12520
+ codeAgentsEnabled,
12139
12521
  researchSources: runtime.researchSources,
12140
12522
  hideTurnSummary,
12141
12523
  agentId,
@@ -12422,74 +12804,297 @@ function ToolkitRow({
12422
12804
  ] })
12423
12805
  ] });
12424
12806
  }
12425
- init_utils();
12426
- var DEFAULT_LIMIT = 50;
12427
- function requireInboxAdapter(adapter) {
12428
- if (!adapter.listInbox || !adapter.getInboxItem || !adapter.markInboxItemRead || !adapter.acknowledgeInboxItem || !adapter.respondGovernanceInboxItem || !adapter.openInboxStream) {
12429
- throw new Error("Inbox is not supported by this adapter.");
12807
+ var PROVIDERS = [
12808
+ {
12809
+ key: "claude",
12810
+ provider: "anthropic",
12811
+ name: "Claude Code",
12812
+ oauthInstructions: "Run `claude setup-token` locally, then paste the `sk-ant-oat...` token it prints to connect your Claude subscription.",
12813
+ apiKeyInstructions: "Paste an Anthropic API key (`sk-ant-...`) to bill usage to your API account instead of a subscription.",
12814
+ oauthPlaceholder: "sk-ant-oat...",
12815
+ apiKeyPlaceholder: "sk-ant-...",
12816
+ validateOauth: (value) => value.startsWith("sk-ant-oat") ? null : "That doesn't look like a setup token \u2014 it should start with `sk-ant-oat`."
12817
+ },
12818
+ {
12819
+ key: "codex",
12820
+ provider: "openai",
12821
+ name: "Codex",
12822
+ oauthInstructions: "Run `codex login` locally, then paste the contents of `~/.codex/auth.json` to connect your ChatGPT plan.",
12823
+ apiKeyInstructions: "Paste an OpenAI API key (`sk-...`) to bill usage to your API account instead of a ChatGPT plan.",
12824
+ oauthPlaceholder: '{"OPENAI_API_KEY": ..., "tokens": { ... }}',
12825
+ apiKeyPlaceholder: "sk-...",
12826
+ validateOauth: (value) => {
12827
+ try {
12828
+ JSON.parse(value);
12829
+ return null;
12830
+ } catch {
12831
+ return "That doesn't look like valid JSON \u2014 paste the whole `~/.codex/auth.json` file.";
12832
+ }
12833
+ }
12430
12834
  }
12431
- return adapter;
12432
- }
12433
- var KIND_LABEL2 = {
12434
- input_required: "Input needed",
12435
- action_required: "Action needed",
12436
- task_complete: "Task complete",
12437
- governance_gate: "Approval",
12438
- progress_checkin: "Progress"
12439
- };
12440
- function kindLabel(kind) {
12441
- return KIND_LABEL2[kind] ?? kind.replace(/[_-]+/g, " ");
12442
- }
12443
- function statusLabel(status) {
12444
- return status.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
12445
- }
12446
- function formatRelative(value) {
12447
- const date = new Date(value);
12448
- if (Number.isNaN(date.getTime())) return "";
12449
- return dateFns.formatDistanceToNow(date, { addSuffix: true });
12450
- }
12451
- function kindIcon(kind) {
12452
- if (kind === "input_required") return lucideReact.MessageSquareIcon;
12453
- if (kind === "action_required") return lucideReact.ExternalLinkIcon;
12454
- if (kind === "task_complete") return lucideReact.ClipboardCheckIcon;
12455
- if (kind === "governance_gate") return lucideReact.ShieldAlertIcon;
12456
- if (kind === "progress_checkin") return lucideReact.TimerIcon;
12457
- return lucideReact.CircleDotIcon;
12458
- }
12459
- function sortItems(items) {
12460
- return [...items].sort((a, b) => {
12461
- if (a.createdAt !== b.createdAt) return a.createdAt > b.createdAt ? -1 : 1;
12462
- return b.id - a.id;
12463
- });
12464
- }
12465
- function QuestionInput({
12466
- prompt,
12467
- choices,
12468
- disabled,
12469
- value,
12470
- onChange
12471
- }) {
12472
- if (choices && choices.length > 0) {
12473
- return /* @__PURE__ */ jsxRuntime.jsxs(
12474
- "select",
12835
+ ];
12836
+ function CodingAgentsPanel({ agentId, adapter, onBack }) {
12837
+ const supported = !!adapter.listCodingAgentConnections && !!adapter.submitCodingAgentCredential && !!adapter.disconnectCodingAgentProvider;
12838
+ const [connections, setConnections] = react.useState([]);
12839
+ const refresh = react.useCallback(async () => {
12840
+ if (!adapter.listCodingAgentConnections) return;
12841
+ const res = await adapter.listCodingAgentConnections({ agentId });
12842
+ setConnections(res.connections);
12843
+ }, [adapter, agentId]);
12844
+ react.useEffect(() => {
12845
+ if (!supported) return;
12846
+ void refresh();
12847
+ }, [supported, refresh]);
12848
+ const byProvider = react.useMemo(() => {
12849
+ const map = /* @__PURE__ */ new Map();
12850
+ for (const c of connections) map.set(c.provider, c);
12851
+ return map;
12852
+ }, [connections]);
12853
+ if (!supported) return null;
12854
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-6 p-6", children: [
12855
+ onBack && /* @__PURE__ */ jsxRuntime.jsxs(
12856
+ "button",
12475
12857
  {
12476
- "aria-label": prompt,
12477
- className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
12478
- disabled,
12479
- value,
12480
- onChange: (event) => onChange(event.target.value),
12858
+ type: "button",
12859
+ onClick: onBack,
12860
+ className: "flex w-fit items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground",
12481
12861
  children: [
12482
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select" }),
12483
- choices.map((choice) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: choice.label, children: choice.label }, choice.label))
12862
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeftIcon, { className: "h-4 w-4" }),
12863
+ "Back"
12484
12864
  ]
12485
12865
  }
12486
- );
12487
- }
12488
- return /* @__PURE__ */ jsxRuntime.jsx(
12489
- "input",
12490
- {
12491
- "aria-label": prompt,
12492
- className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
12866
+ ),
12867
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
12868
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold text-foreground", children: "Coding agents" }),
12869
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Connect your Claude or ChatGPT plan so the agent can run `/code` on your behalf without separate API billing." })
12870
+ ] }),
12871
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-4", children: PROVIDERS.map((meta) => /* @__PURE__ */ jsxRuntime.jsx(
12872
+ ProviderCard,
12873
+ {
12874
+ meta,
12875
+ connection: byProvider.get(meta.provider) ?? null,
12876
+ adapter,
12877
+ agentId,
12878
+ onChanged: refresh
12879
+ },
12880
+ meta.key
12881
+ )) })
12882
+ ] });
12883
+ }
12884
+ function ProviderCard({
12885
+ meta,
12886
+ connection,
12887
+ adapter,
12888
+ agentId,
12889
+ onChanged
12890
+ }) {
12891
+ const [mode, setMode] = react.useState("oauth");
12892
+ const [value, setValue] = react.useState("");
12893
+ const [busy, setBusy] = react.useState(false);
12894
+ const [error, setError] = react.useState(null);
12895
+ const connected = connection?.connected ?? false;
12896
+ const instructions = mode === "oauth" ? meta.oauthInstructions : meta.apiKeyInstructions;
12897
+ const placeholder = mode === "oauth" ? meta.oauthPlaceholder : meta.apiKeyPlaceholder;
12898
+ const submit = async () => {
12899
+ if (!adapter.submitCodingAgentCredential) return;
12900
+ const trimmed = value.trim();
12901
+ setError(null);
12902
+ if (!trimmed) {
12903
+ setError("Paste a value first.");
12904
+ return;
12905
+ }
12906
+ if (mode === "oauth") {
12907
+ const hint = meta.validateOauth(trimmed);
12908
+ if (hint) {
12909
+ setError(hint);
12910
+ return;
12911
+ }
12912
+ }
12913
+ setBusy(true);
12914
+ try {
12915
+ await adapter.submitCodingAgentCredential({
12916
+ agentId,
12917
+ provider: meta.provider,
12918
+ mode,
12919
+ value: trimmed
12920
+ });
12921
+ setValue("");
12922
+ await onChanged();
12923
+ } catch (e) {
12924
+ setError(e instanceof Error ? e.message : "Failed to save the credential");
12925
+ } finally {
12926
+ setBusy(false);
12927
+ }
12928
+ };
12929
+ const disconnect = async () => {
12930
+ if (!adapter.disconnectCodingAgentProvider) return;
12931
+ setBusy(true);
12932
+ setError(null);
12933
+ try {
12934
+ await adapter.disconnectCodingAgentProvider({ agentId, provider: meta.provider });
12935
+ await onChanged();
12936
+ } catch (e) {
12937
+ setError(e instanceof Error ? e.message : "Failed to disconnect");
12938
+ } finally {
12939
+ setBusy(false);
12940
+ }
12941
+ };
12942
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "flex flex-col gap-3 rounded-lg border border-border p-4", children: [
12943
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
12944
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-semibold text-foreground", children: meta.name }),
12945
+ /* @__PURE__ */ jsxRuntime.jsx(
12946
+ "span",
12947
+ {
12948
+ className: `inline-flex items-center rounded px-1.5 py-0.5 text-xs font-semibold ${connected ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"}`,
12949
+ children: connected ? `Connected${connection?.auth_mode === "api_key" ? " (API key)" : ""}` : "Not connected"
12950
+ }
12951
+ )
12952
+ ] }),
12953
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1 text-xs", children: [
12954
+ /* @__PURE__ */ jsxRuntime.jsx(
12955
+ "button",
12956
+ {
12957
+ type: "button",
12958
+ onClick: () => {
12959
+ setMode("oauth");
12960
+ setError(null);
12961
+ },
12962
+ className: `rounded-md border px-2 py-1 ${mode === "oauth" ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:bg-accent"}`,
12963
+ children: "Subscription"
12964
+ }
12965
+ ),
12966
+ /* @__PURE__ */ jsxRuntime.jsx(
12967
+ "button",
12968
+ {
12969
+ type: "button",
12970
+ onClick: () => {
12971
+ setMode("api_key");
12972
+ setError(null);
12973
+ },
12974
+ className: `rounded-md border px-2 py-1 ${mode === "api_key" ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground hover:bg-accent"}`,
12975
+ children: "API key"
12976
+ }
12977
+ )
12978
+ ] }),
12979
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: instructions }),
12980
+ /* @__PURE__ */ jsxRuntime.jsx(
12981
+ "textarea",
12982
+ {
12983
+ value,
12984
+ onChange: (e) => setValue(e.target.value),
12985
+ placeholder,
12986
+ spellCheck: false,
12987
+ autoComplete: "off",
12988
+ rows: mode === "oauth" && meta.provider === "openai" ? 4 : 2,
12989
+ "aria-label": `${meta.name} credential`,
12990
+ className: "w-full resize-y rounded-md border border-border bg-background px-3 py-2 font-mono text-xs [-webkit-text-security:disc]"
12991
+ }
12992
+ ),
12993
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", className: "text-xs text-red-500", children: error }),
12994
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
12995
+ /* @__PURE__ */ jsxRuntime.jsxs(
12996
+ "button",
12997
+ {
12998
+ type: "button",
12999
+ disabled: busy,
13000
+ onClick: submit,
13001
+ className: "inline-flex items-center justify-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-xs text-foreground hover:bg-accent disabled:opacity-60",
13002
+ children: [
13003
+ busy && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2Icon, { className: "h-3.5 w-3.5 animate-spin" }),
13004
+ connected ? "Update" : "Connect"
13005
+ ]
13006
+ }
13007
+ ),
13008
+ connected && /* @__PURE__ */ jsxRuntime.jsx(
13009
+ "button",
13010
+ {
13011
+ type: "button",
13012
+ disabled: busy,
13013
+ onClick: disconnect,
13014
+ className: "inline-flex items-center justify-center rounded-md border border-border px-3 py-1.5 text-xs text-muted-foreground hover:bg-accent disabled:opacity-60",
13015
+ children: "Disconnect"
13016
+ }
13017
+ )
13018
+ ] })
13019
+ ] });
13020
+ }
13021
+ init_utils();
13022
+ var DEFAULT_LIMIT = 50;
13023
+ function requireInboxAdapter(adapter) {
13024
+ if (!adapter.listInbox || !adapter.getInboxItem || !adapter.markInboxItemRead || !adapter.acknowledgeInboxItem || !adapter.respondGovernanceInboxItem || !adapter.openInboxStream) {
13025
+ throw new Error("Inbox is not supported by this adapter.");
13026
+ }
13027
+ return adapter;
13028
+ }
13029
+ var KIND_LABEL2 = {
13030
+ input_required: "Input needed",
13031
+ action_required: "Action needed",
13032
+ task_complete: "Task complete",
13033
+ governance_gate: "Approval",
13034
+ progress_checkin: "Progress"
13035
+ };
13036
+ function kindLabel(kind) {
13037
+ return KIND_LABEL2[kind] ?? kind.replace(/[_-]+/g, " ");
13038
+ }
13039
+ function statusLabel(status) {
13040
+ return status.split(/[_-]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
13041
+ }
13042
+ function formatRelative(value) {
13043
+ const date = new Date(value);
13044
+ if (Number.isNaN(date.getTime())) return "";
13045
+ return dateFns.formatDistanceToNow(date, { addSuffix: true });
13046
+ }
13047
+ function kindIcon(kind) {
13048
+ if (kind === "input_required") return lucideReact.MessageSquareIcon;
13049
+ if (kind === "action_required") return lucideReact.ExternalLinkIcon;
13050
+ if (kind === "task_complete") return lucideReact.ClipboardCheckIcon;
13051
+ if (kind === "governance_gate") return lucideReact.ShieldAlertIcon;
13052
+ if (kind === "progress_checkin") return lucideReact.TimerIcon;
13053
+ return lucideReact.CircleDotIcon;
13054
+ }
13055
+ function sortItems(items) {
13056
+ return [...items].sort((a, b) => {
13057
+ if (a.createdAt !== b.createdAt) return a.createdAt > b.createdAt ? -1 : 1;
13058
+ return b.id - a.id;
13059
+ });
13060
+ }
13061
+ function InboxBody({ body, muted }) {
13062
+ return /* @__PURE__ */ jsxRuntime.jsx(
13063
+ MessageResponse,
13064
+ {
13065
+ className: cn("text-sm", muted && "text-muted-foreground"),
13066
+ children: body
13067
+ }
13068
+ );
13069
+ }
13070
+ function QuestionInput({
13071
+ prompt,
13072
+ choices,
13073
+ disabled,
13074
+ value,
13075
+ onChange
13076
+ }) {
13077
+ if (choices && choices.length > 0) {
13078
+ return /* @__PURE__ */ jsxRuntime.jsxs(
13079
+ "select",
13080
+ {
13081
+ "aria-label": prompt,
13082
+ className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
13083
+ disabled,
13084
+ value,
13085
+ onChange: (event) => onChange(event.target.value),
13086
+ children: [
13087
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select" }),
13088
+ choices.map((choice) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: choice.label, children: choice.label }, choice.label))
13089
+ ]
13090
+ }
13091
+ );
13092
+ }
13093
+ return /* @__PURE__ */ jsxRuntime.jsx(
13094
+ "input",
13095
+ {
13096
+ "aria-label": prompt,
13097
+ className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
12493
13098
  disabled,
12494
13099
  value,
12495
13100
  onChange: (event) => onChange(event.target.value)
@@ -12585,7 +13190,7 @@ function InputRequiredDetail({
12585
13190
  }
12586
13191
  }
12587
13192
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
12588
- item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: item.body }),
13193
+ item.body && /* @__PURE__ */ jsxRuntime.jsx(InboxBody, { body: item.body, muted: true }),
12589
13194
  questions.map((question) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block space-y-1.5", children: [
12590
13195
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-foreground", children: question.prompt }),
12591
13196
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -12638,7 +13243,7 @@ function AckDetail({
12638
13243
  const duration = typeof item.payload.duration_seconds === "number" ? item.payload.duration_seconds : null;
12639
13244
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
12640
13245
  outcome && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", children: outcome }),
12641
- item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
13246
+ item.body && /* @__PURE__ */ jsxRuntime.jsx(InboxBody, { body: item.body }),
12642
13247
  typeof item.payload.error === "string" && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-x-auto bg-destructive/10 p-3 text-xs text-destructive", children: item.payload.error }),
12643
13248
  duration !== null && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
12644
13249
  "Duration: ",
@@ -12752,7 +13357,7 @@ function ActionRequiredDetail({
12752
13357
  }
12753
13358
  }
12754
13359
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
12755
- item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
13360
+ item.body && /* @__PURE__ */ jsxRuntime.jsx(InboxBody, { body: item.body }),
12756
13361
  context && context !== item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: context }),
12757
13362
  actionType && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", children: actionType }),
12758
13363
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -12801,7 +13406,7 @@ function ProgressDetail({
12801
13406
  }
12802
13407
  }
12803
13408
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
12804
- item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm", children: item.body }),
13409
+ item.body && /* @__PURE__ */ jsxRuntime.jsx(InboxBody, { body: item.body }),
12805
13410
  rows.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "space-y-1 text-xs text-muted-foreground", children: rows.map(([label, value]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
12806
13411
  /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "w-20 shrink-0", children: String(label) }),
12807
13412
  /* @__PURE__ */ jsxRuntime.jsx("dd", { className: "min-w-0", children: String(value) })
@@ -13122,6 +13727,45 @@ function Card({
13122
13727
  }
13123
13728
  );
13124
13729
  }
13730
+ function CardHeader({ className, ...props }) {
13731
+ return /* @__PURE__ */ jsxRuntime.jsx(
13732
+ "div",
13733
+ {
13734
+ "data-slot": "card-header",
13735
+ className: cn(
13736
+ "group/card-header @container/card-header grid auto-rows-min items-start gap-1.5 rounded-none px-8 group-data-[size=sm]/card:px-5 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-8 group-data-[size=sm]/card:[.border-b]:pb-5",
13737
+ className
13738
+ ),
13739
+ ...props
13740
+ }
13741
+ );
13742
+ }
13743
+ function CardTitle({ className, ...props }) {
13744
+ return /* @__PURE__ */ jsxRuntime.jsx(
13745
+ "div",
13746
+ {
13747
+ "data-slot": "card-title",
13748
+ className: cn(
13749
+ "font-heading text-lg font-semibold tracking-wider uppercase",
13750
+ className
13751
+ ),
13752
+ ...props
13753
+ }
13754
+ );
13755
+ }
13756
+ function CardAction({ className, ...props }) {
13757
+ return /* @__PURE__ */ jsxRuntime.jsx(
13758
+ "div",
13759
+ {
13760
+ "data-slot": "card-action",
13761
+ className: cn(
13762
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
13763
+ className
13764
+ ),
13765
+ ...props
13766
+ }
13767
+ );
13768
+ }
13125
13769
  function CardContent({ className, ...props }) {
13126
13770
  return /* @__PURE__ */ jsxRuntime.jsx(
13127
13771
  "div",
@@ -13178,111 +13822,793 @@ function truncate4(s, max) {
13178
13822
  if (s.length <= max) return s;
13179
13823
  return `${s.slice(0, max - 1)}\u2026`;
13180
13824
  }
13181
- var DEFAULT_POLL_INTERVAL_MS = 5e3;
13182
- var MISSION_WORKER_KIND_BADGE_CLASS = {
13183
- task: "border-primary/40 bg-primary/10 text-primary uppercase tracking-wide text-[10px]",
13184
- worker: "border-foreground/30 bg-foreground/5 text-foreground/80 uppercase tracking-wide text-[10px]",
13185
- delegation: "border-foreground/20 bg-foreground/[0.03] text-foreground/60 uppercase tracking-wide text-[10px]"
13186
- };
13187
- var MISSION_WORKER_KIND_TOOLTIP = {
13188
- task: "Durable Task row created by spawn_task. Retried by the dispatcher; survives across coordinator wakes.",
13189
- worker: "Async one-shot session spawned by spawn_worker. Durable session, no retry/DAG.",
13190
- delegation: "Sync fork-join child spawned by delegate_task. Coordinator's wake blocked until it finished."
13191
- };
13192
- function CountBadge({ n }) {
13193
- if (n === 0) return null;
13194
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 rounded bg-muted px-1 py-px text-[9px] text-muted-foreground/70", children: n });
13195
- }
13196
- function firstLine3(text, max = 140) {
13197
- const trimmed = text.replace(/\s+/g, " ").trim();
13198
- if (trimmed.length <= max) return trimmed;
13199
- return `${trimmed.slice(0, max).trimEnd()}\u2026`;
13200
- }
13201
- var ACTIVITY_KIND_TONE = {
13202
- "mission.defined": "bg-primary/10 text-primary border-primary/30",
13203
- "mission.paused": "bg-amber-500/10 text-amber-600 border-amber-500/30",
13204
- "mission.cancelled": "bg-destructive/10 text-destructive border-destructive/30",
13205
- "task.spawned": "bg-foreground/5 text-foreground/80 border-foreground/20",
13206
- "task.completed": "bg-emerald-500/10 text-emerald-600 border-emerald-500/30",
13207
- "task.failed": "bg-destructive/10 text-destructive border-destructive/30",
13208
- "task.cancelled": "bg-muted text-muted-foreground border-border",
13209
- "worker.latest": "bg-foreground/5 text-foreground/70 border-foreground/15",
13210
- "evaluator.verdict": "bg-primary/10 text-primary border-primary/30"
13211
- };
13212
- function buildMissionActivity(mission, tasks, workers) {
13213
- const entries = [];
13214
- entries.push({
13215
- at: mission.createdAt,
13216
- kind: "mission.defined",
13217
- label: "Mission defined",
13218
- detail: mission.description
13219
- });
13220
- if (mission.status === "paused" && mission.pausedReason) {
13221
- entries.push({
13222
- at: mission.updatedAt,
13223
- kind: "mission.paused",
13224
- label: "Paused",
13225
- detail: mission.pausedReason
13226
- });
13227
- }
13228
- if (mission.status === "cancelled" && mission.cancelledReason) {
13229
- entries.push({
13230
- at: mission.updatedAt,
13231
- kind: "mission.cancelled",
13232
- label: "Cancelled",
13233
- detail: mission.cancelledReason
13234
- });
13825
+ var MISSION_EVENT_CATEGORIES = [
13826
+ "spawn",
13827
+ "output",
13828
+ "done",
13829
+ "verdict",
13830
+ "system"
13831
+ ];
13832
+ function missionEventCategory(type) {
13833
+ switch (type) {
13834
+ case "worker.spawned":
13835
+ return "spawn";
13836
+ case "iteration.summary":
13837
+ return "output";
13838
+ case "worker.complete":
13839
+ return "done";
13840
+ case "mission.evaluation.end":
13841
+ return "verdict";
13842
+ default:
13843
+ return "system";
13235
13844
  }
13236
- for (const task of tasks) {
13237
- if (task.createdAt) {
13238
- entries.push({
13239
- at: task.createdAt,
13240
- kind: "task.spawned",
13241
- label: `Spawn \xB7 ${task.agentDefName ?? "task"}`,
13242
- detail: firstLine3(task.goal, 200)
13243
- });
13244
- }
13245
- if (task.completedAt) {
13246
- const kind = task.status === "done" ? "task.completed" : task.status === "failed" ? "task.failed" : task.status === "cancelled" ? "task.cancelled" : "task.completed";
13247
- entries.push({
13248
- at: task.completedAt,
13249
- kind,
13250
- label: `${task.status} \xB7 ${task.agentDefName ?? "task"}`,
13251
- detail: task.result ? firstLine3(task.result, 200) : void 0
13252
- });
13845
+ }
13846
+ function dataString(data, key) {
13847
+ const v = data?.[key];
13848
+ return typeof v === "string" && v.trim().length > 0 ? v.trim() : null;
13849
+ }
13850
+ var CONTROL_MARKUP_RE = /<next_action\b[^>]*(?:\/>|>[\s\S]*?<\/next_action>)/gi;
13851
+ function stripMissionControlMarkup(text) {
13852
+ return text.replace(CONTROL_MARKUP_RE, "").trim();
13853
+ }
13854
+ function missionEventSummary(event) {
13855
+ return stripMissionControlMarkup(rawMissionEventSummary(event));
13856
+ }
13857
+ function rawMissionEventSummary(event) {
13858
+ const d = event.data ?? null;
13859
+ switch (event.type) {
13860
+ case "worker.spawned":
13861
+ return dataString(d, "goal") ?? "Task spawned";
13862
+ case "worker.complete":
13863
+ return dataString(d, "result") ?? "Worker complete";
13864
+ case "iteration.summary":
13865
+ return dataString(d, "summary") ?? "";
13866
+ case "mission.evaluation.end": {
13867
+ const result = dataString(d, "result");
13868
+ const feedback = dataString(d, "feedback") ?? dataString(d, "explanation");
13869
+ if (result && feedback) return `${result} \u2014 ${feedback}`;
13870
+ return result ?? feedback ?? "Evaluation finished";
13871
+ }
13872
+ case "task.blocked":
13873
+ return dataString(d, "reason") ?? "Task blocked";
13874
+ case "task.failed":
13875
+ return "Task failed";
13876
+ default: {
13877
+ const text = dataString(d, "summary") ?? dataString(d, "text") ?? dataString(d, "message") ?? dataString(d, "reason");
13878
+ if (text) return text;
13879
+ if (!d) return "";
13880
+ try {
13881
+ return truncate4(JSON.stringify(d), 160);
13882
+ } catch {
13883
+ return "";
13884
+ }
13253
13885
  }
13254
13886
  }
13255
- for (const worker of workers) {
13256
- if (worker.latestEventAt && worker.latestEventKind) {
13257
- entries.push({
13258
- at: worker.latestEventAt,
13259
- kind: "worker.latest",
13260
- label: `${worker.kind} \xB7 ${worker.latestEventKind}`,
13261
- detail: deriveMissionWorkerActivityLabel(worker)
13262
- });
13887
+ }
13888
+ function missionEventTaskId(event, sessions) {
13889
+ return dataString(event.data ?? null, "task_id") ?? sessions[event.sessionId]?.taskId ?? null;
13890
+ }
13891
+ function missionEventActorLabel(event, sessions) {
13892
+ const meta = sessions[event.sessionId];
13893
+ if (!meta) return event.sessionId.slice(0, 8);
13894
+ if (meta.kind === "coordinator") return "orchestrator";
13895
+ return meta.agentDefName ?? "worker";
13896
+ }
13897
+ function missionTaskBlocks(tasks) {
13898
+ const blocks = {};
13899
+ for (const t of tasks) {
13900
+ for (const parent of t.parentIds) {
13901
+ (blocks[parent] ?? (blocks[parent] = [])).push(t.id);
13263
13902
  }
13264
13903
  }
13265
- if (mission.lastEvaluationAt && mission.lastEvaluationResult) {
13266
- entries.push({
13267
- at: mission.lastEvaluationAt,
13268
- kind: "evaluator.verdict",
13269
- label: `Verdict \xB7 ${mission.lastEvaluationResult}`,
13270
- detail: mission.lastEvaluationFeedback ?? void 0
13271
- });
13272
- }
13273
- entries.sort((a, b) => b.at.localeCompare(a.at));
13274
- return entries;
13904
+ return blocks;
13275
13905
  }
13276
- function formatTimestamp(iso) {
13277
- try {
13278
- return new Date(iso).toLocaleTimeString();
13279
- } catch {
13280
- return iso;
13906
+ function missionTaskTitle(goal, max = 80) {
13907
+ const line = goal.split("\n").find((l) => l.trim().length > 0)?.trim() ?? goal.trim();
13908
+ return truncate4(line.replace(/\s+/g, " "), max);
13909
+ }
13910
+ var RAIL_GROUP_DEFS = [
13911
+ { key: "running", label: "Running", match: (s) => s === "running" },
13912
+ { key: "blocked", label: "Blocked", match: (s) => s === "blocked" },
13913
+ {
13914
+ key: "queued",
13915
+ label: "Queued",
13916
+ match: (s) => s === "ready" || s === "todo"
13917
+ },
13918
+ { key: "done", label: "Done", match: (s) => s === "done" },
13919
+ {
13920
+ key: "failed",
13921
+ label: "Failed / cancelled",
13922
+ match: (s) => s === "failed" || s === "cancelled"
13923
+ }
13924
+ ];
13925
+ function missionTaskRailGroups(tasks) {
13926
+ const grouped = /* @__PURE__ */ new Set();
13927
+ const groups = RAIL_GROUP_DEFS.map((def) => ({
13928
+ key: def.key,
13929
+ label: def.label,
13930
+ tasks: tasks.filter((t) => {
13931
+ const hit = def.match(t.status);
13932
+ if (hit) grouped.add(t.id);
13933
+ return hit;
13934
+ })
13935
+ }));
13936
+ const stray = tasks.filter((t) => !grouped.has(t.id));
13937
+ if (stray.length > 0) {
13938
+ const queued = groups.find((g) => g.key === "queued");
13939
+ if (queued) queued.tasks.push(...stray);
13281
13940
  }
13941
+ return groups.filter((g) => g.tasks.length > 0);
13282
13942
  }
13283
- var INITIAL_STATE = {
13284
- mission: null,
13285
- tasks: [],
13943
+ var TERMINAL_TASK_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
13944
+ function defaultSelectedMissionTaskId(tasks) {
13945
+ if (tasks.length === 0) return null;
13946
+ const ordered = missionTaskRailGroups(tasks).flatMap((g) => g.tasks);
13947
+ const firstLive = ordered.find(
13948
+ (t) => !TERMINAL_TASK_STATUSES.has(t.status)
13949
+ );
13950
+ return (firstLive ?? ordered[0] ?? tasks[0]).id;
13951
+ }
13952
+ function mergeMissionEvents(existing, incoming) {
13953
+ if (incoming.length === 0) return existing;
13954
+ const byId = /* @__PURE__ */ new Map();
13955
+ for (const e of existing) byId.set(e.id, e);
13956
+ for (const e of incoming) byId.set(e.id, e);
13957
+ return [...byId.values()].sort((a, b) => a.id - b.id);
13958
+ }
13959
+ function missionTaskStatusDotClass(status) {
13960
+ switch (status) {
13961
+ case "running":
13962
+ return "bg-primary";
13963
+ case "blocked":
13964
+ return "bg-amber-500";
13965
+ case "done":
13966
+ return "bg-emerald-500";
13967
+ case "failed":
13968
+ return "bg-destructive";
13969
+ case "cancelled":
13970
+ return "bg-muted-foreground";
13971
+ default:
13972
+ return "bg-foreground/30";
13973
+ }
13974
+ }
13975
+ function missionWorkerTaskCounts(worker, tasks) {
13976
+ if (worker.kind !== "task") return null;
13977
+ const mine = tasks.filter((t) => t.agentDefName === worker.agentDefName);
13978
+ return {
13979
+ completed: mine.filter((t) => t.status === "done").length,
13980
+ inFlight: mine.filter(
13981
+ (t) => t.status === "running" || t.status === "blocked"
13982
+ ).length
13983
+ };
13984
+ }
13985
+ function formatMissionTimestamp(iso) {
13986
+ if (!iso) return "";
13987
+ const parsed = new Date(iso);
13988
+ if (Number.isNaN(parsed.getTime())) return iso;
13989
+ return parsed.toLocaleTimeString();
13990
+ }
13991
+ var CATEGORY_BADGE_CLASS = {
13992
+ spawn: "border-primary/30 bg-primary/10 text-primary",
13993
+ output: "border-foreground/20 bg-foreground/5 text-foreground/80",
13994
+ done: "border-emerald-500/30 bg-emerald-500/10 text-emerald-600",
13995
+ verdict: "border-amber-500/30 bg-amber-500/10 text-amber-600",
13996
+ system: "border-border bg-muted text-muted-foreground"
13997
+ };
13998
+ function MissionActivityTab({ feed }) {
13999
+ const [filter, setFilter] = react.useState("all");
14000
+ const rows = react.useMemo(() => {
14001
+ const newestFirst = [...feed.events].reverse();
14002
+ if (filter === "all") return newestFirst;
14003
+ return newestFirst.filter((e) => missionEventCategory(e.type) === filter);
14004
+ }, [feed.events, filter]);
14005
+ if (feed.events.length === 0) {
14006
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-8 text-center text-sm text-muted-foreground/60", children: "No mission activity yet." });
14007
+ }
14008
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
14009
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-1.5", children: ["all", ...MISSION_EVENT_CATEGORIES].map((cat) => /* @__PURE__ */ jsxRuntime.jsx(
14010
+ "button",
14011
+ {
14012
+ type: "button",
14013
+ onClick: () => setFilter(cat),
14014
+ className: `rounded-full border px-2.5 py-0.5 font-mono text-[10px] uppercase tracking-wide transition-colors ${filter === cat ? "border-foreground bg-foreground text-background" : "border-border/60 bg-background text-muted-foreground hover:bg-muted/40"}`,
14015
+ children: cat
14016
+ },
14017
+ cat
14018
+ )) }),
14019
+ /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "space-y-1", children: rows.map((e) => /* @__PURE__ */ jsxRuntime.jsx(ActivityRow, { event: e, feed }, e.id)) })
14020
+ ] });
14021
+ }
14022
+ function ActivityRow({
14023
+ event,
14024
+ feed
14025
+ }) {
14026
+ const category = missionEventCategory(event.type);
14027
+ const taskId = missionEventTaskId(event, feed.sessions);
14028
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-start gap-3 rounded px-2 py-1.5 text-sm hover:bg-muted/30", children: [
14029
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-20 shrink-0 font-mono text-[10px] text-muted-foreground/60", children: formatMissionTimestamp(event.createdAt) }),
14030
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
14031
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-center", children: [
14032
+ /* @__PURE__ */ jsxRuntime.jsx(
14033
+ "span",
14034
+ {
14035
+ className: `shrink-0 rounded border px-1.5 font-mono uppercase ${CATEGORY_BADGE_CLASS[category]}`,
14036
+ children: category
14037
+ }
14038
+ ),
14039
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 text-xs text-foreground font-mono", children: missionEventActorLabel(event, feed.sessions) }),
14040
+ taskId ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 rounded bg-muted px-1.5 py-0.5 font-mono text-[9px] text-muted-foreground/70", children: taskId.slice(0, 8) }) : null
14041
+ ] }),
14042
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "min-w-0 flex-1 truncate text-foreground/80", children: missionEventSummary(event) }) })
14043
+ ] })
14044
+ ] });
14045
+ }
14046
+ function verdictToneClass(result) {
14047
+ if (result === "satisfied") {
14048
+ return "border-emerald-500/30 bg-emerald-500/5 text-emerald-700 dark:text-emerald-400";
14049
+ }
14050
+ if (result === "failed" || result === "blocked") {
14051
+ return "border-destructive/30 bg-destructive/5 text-destructive";
14052
+ }
14053
+ return "border-amber-500/30 bg-amber-500/5 text-amber-700 dark:text-amber-400";
14054
+ }
14055
+ function MissionMetadataTab({ mission }) {
14056
+ const rows = [
14057
+ {
14058
+ label: "Mission ID",
14059
+ value: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-[11px]", children: mission.id })
14060
+ },
14061
+ {
14062
+ label: "Session ID",
14063
+ value: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-[11px]", children: mission.sessionId })
14064
+ },
14065
+ {
14066
+ label: "Agent ID",
14067
+ value: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-[11px]", children: mission.agentId })
14068
+ },
14069
+ { label: "Status", value: mission.status },
14070
+ {
14071
+ label: "Iteration",
14072
+ value: `${mission.iteration} / ${mission.maxIterations}`
14073
+ },
14074
+ {
14075
+ label: "Owner",
14076
+ value: mission.userId ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
14077
+ "user ",
14078
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-[11px]", children: mission.userId })
14079
+ ] }) : mission.serviceAccountId ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
14080
+ "service account",
14081
+ " ",
14082
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-[11px]", children: mission.serviceAccountId })
14083
+ ] }) : "\u2014"
14084
+ },
14085
+ {
14086
+ label: "Created",
14087
+ value: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[11px]", children: new Date(mission.createdAt).toLocaleString() })
14088
+ },
14089
+ {
14090
+ label: "Updated",
14091
+ value: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[11px]", children: new Date(mission.updatedAt).toLocaleString() })
14092
+ }
14093
+ ];
14094
+ if (mission.pausedReason) {
14095
+ rows.push({ label: "Paused reason", value: mission.pausedReason });
14096
+ }
14097
+ if (mission.cancelledReason) {
14098
+ rows.push({ label: "Cancelled reason", value: mission.cancelledReason });
14099
+ }
14100
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
14101
+ mission.lastEvaluationResult ? /* @__PURE__ */ jsxRuntime.jsxs(
14102
+ "section",
14103
+ {
14104
+ className: `space-y-2 rounded-md border p-4 ${verdictToneClass(mission.lastEvaluationResult)}`,
14105
+ children: [
14106
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-mono text-[10px] uppercase tracking-widest", children: [
14107
+ "Last verdict \u2014 ",
14108
+ mission.lastEvaluationResult
14109
+ ] }),
14110
+ mission.lastEvaluationFeedback ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm font-medium text-foreground/90", children: mission.lastEvaluationFeedback }) : null,
14111
+ mission.lastEvaluationExplanation ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-xs text-muted-foreground/80", children: mission.lastEvaluationExplanation }) : null,
14112
+ mission.lastEvaluationAt ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-mono text-[10px] text-muted-foreground/60", children: new Date(mission.lastEvaluationAt).toLocaleString() }) : null
14113
+ ]
14114
+ }
14115
+ ) : null,
14116
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
14117
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2 font-mono text-[10px] uppercase tracking-widest text-muted-foreground/70", children: "Identifiers" }),
14118
+ /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "grid grid-cols-[max-content_1fr] gap-x-4 gap-y-1 text-xs", children: rows.map((r) => /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
14119
+ /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground/70", children: r.label }),
14120
+ /* @__PURE__ */ jsxRuntime.jsx("dd", { className: "break-all text-foreground/90", children: r.value })
14121
+ ] }, r.label)) })
14122
+ ] })
14123
+ ] });
14124
+ }
14125
+ var TASK_STATUS_BADGE_CLASS = {
14126
+ running: "border-primary/30 bg-primary/10 text-primary",
14127
+ blocked: "border-amber-500/30 bg-amber-500/10 text-amber-600",
14128
+ done: "border-emerald-500/30 bg-emerald-500/10 text-emerald-600",
14129
+ failed: "border-destructive/30 bg-destructive/10 text-destructive",
14130
+ cancelled: "border-border bg-muted text-muted-foreground"
14131
+ };
14132
+ function statusBadgeClass(status) {
14133
+ return TASK_STATUS_BADGE_CLASS[status] ?? "border-foreground/20 bg-foreground/5 text-foreground/70";
14134
+ }
14135
+ function TaskChip({
14136
+ task,
14137
+ onSelect
14138
+ }) {
14139
+ return /* @__PURE__ */ jsxRuntime.jsxs(
14140
+ "button",
14141
+ {
14142
+ type: "button",
14143
+ "data-task-chip": true,
14144
+ onClick: () => onSelect(task.id),
14145
+ className: "inline-flex max-w-full items-center gap-1.5 rounded-full border border-border/60 bg-background px-2.5 py-1 text-xs hover:bg-muted/40",
14146
+ children: [
14147
+ /* @__PURE__ */ jsxRuntime.jsx(
14148
+ "span",
14149
+ {
14150
+ className: `size-1.5 shrink-0 rounded-full ${missionTaskStatusDotClass(task.status)}`
14151
+ }
14152
+ ),
14153
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-foreground/85", children: missionTaskTitle(task.goal, 40) }),
14154
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-mono text-[9px] text-muted-foreground/60", children: task.id.slice(0, 8) })
14155
+ ]
14156
+ }
14157
+ );
14158
+ }
14159
+ function TaskRailRow({
14160
+ task,
14161
+ selected,
14162
+ onSelect
14163
+ }) {
14164
+ const preview = stripMissionControlMarkup(
14165
+ task.status === "done" && task.result ? task.result : task.goal
14166
+ );
14167
+ return /* @__PURE__ */ jsxRuntime.jsxs(
14168
+ "button",
14169
+ {
14170
+ type: "button",
14171
+ onClick: () => onSelect(task.id),
14172
+ className: `w-full rounded-md border px-3 py-2 text-left text-sm transition-colors ${selected ? "border-primary/40 bg-primary/5" : "border-transparent hover:bg-muted/40"}`,
14173
+ children: [
14174
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
14175
+ /* @__PURE__ */ jsxRuntime.jsx(
14176
+ "span",
14177
+ {
14178
+ className: `size-2 shrink-0 rounded-full ${missionTaskStatusDotClass(task.status)}`
14179
+ }
14180
+ ),
14181
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate font-medium text-foreground/90", children: task.goal })
14182
+ ] }),
14183
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex flex-col gap-2 pl-4", children: [
14184
+ task.agentDefName ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs font-mono text-muted-foreground", children: task.agentDefName }) : null,
14185
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-2 min-w-0 flex-1 text-xs text-muted-foreground/70", children: preview })
14186
+ ] })
14187
+ ]
14188
+ }
14189
+ );
14190
+ }
14191
+ function MissionTasksTab({
14192
+ tasks,
14193
+ feed,
14194
+ onOpenTranscript
14195
+ }) {
14196
+ const [selectedId, setSelectedId] = react.useState(null);
14197
+ const groups = react.useMemo(() => missionTaskRailGroups(tasks), [tasks]);
14198
+ const blocks = react.useMemo(() => missionTaskBlocks(tasks), [tasks]);
14199
+ const byId = react.useMemo(() => new Map(tasks.map((t) => [t.id, t])), [tasks]);
14200
+ const effectiveId = selectedId && byId.has(selectedId) ? selectedId : defaultSelectedMissionTaskId(tasks);
14201
+ const selected = effectiveId ? byId.get(effectiveId) ?? null : null;
14202
+ const taskEvents = react.useMemo(() => {
14203
+ if (!selected || !feed.supported) return [];
14204
+ return feed.events.filter(
14205
+ (e) => missionEventTaskId(e, feed.sessions) === selected.id
14206
+ );
14207
+ }, [selected, feed]);
14208
+ const liveOutput = react.useMemo(() => {
14209
+ if (!selected || selected.status !== "running") return null;
14210
+ const outputs = taskEvents.filter(
14211
+ (e) => missionEventCategory(e.type) === "output"
14212
+ );
14213
+ return outputs.length > 0 ? outputs[outputs.length - 1] : null;
14214
+ }, [selected, taskEvents]);
14215
+ if (tasks.length === 0) {
14216
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-8 text-center text-sm text-muted-foreground/60", children: "No tasks spawned yet." });
14217
+ }
14218
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 gap-4", children: [
14219
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-72 shrink-0 space-y-4 overflow-y-auto pr-1", children: groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
14220
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 px-1 font-mono text-[10px] uppercase tracking-widest text-muted-foreground/70", children: [
14221
+ group.label,
14222
+ " (",
14223
+ group.tasks.length,
14224
+ ")"
14225
+ ] }),
14226
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1", children: group.tasks.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
14227
+ TaskRailRow,
14228
+ {
14229
+ task: t,
14230
+ selected: t.id === effectiveId,
14231
+ onSelect: setSelectedId
14232
+ },
14233
+ t.id
14234
+ )) })
14235
+ ] }, group.key)) }),
14236
+ /* @__PURE__ */ jsxRuntime.jsx(
14237
+ "div",
14238
+ {
14239
+ "data-testid": "task-detail",
14240
+ className: "min-w-0 flex-1 space-y-4 overflow-y-auto bg-card px-8 py-5",
14241
+ children: selected ? /* @__PURE__ */ jsxRuntime.jsx(
14242
+ TaskDetail,
14243
+ {
14244
+ task: selected,
14245
+ allTasks: byId,
14246
+ blockedTaskIds: blocks[selected.id] ?? [],
14247
+ taskEvents,
14248
+ liveOutput,
14249
+ eventsSupported: feed.supported,
14250
+ onSelect: setSelectedId,
14251
+ onOpenTranscript
14252
+ }
14253
+ ) : null
14254
+ }
14255
+ )
14256
+ ] });
14257
+ }
14258
+ function TaskDetail({
14259
+ task,
14260
+ allTasks,
14261
+ blockedTaskIds,
14262
+ taskEvents,
14263
+ liveOutput,
14264
+ eventsSupported,
14265
+ onSelect,
14266
+ onOpenTranscript
14267
+ }) {
14268
+ const parents = task.parentIds.map((id) => allTasks.get(id)).filter((t) => Boolean(t));
14269
+ const children = blockedTaskIds.map((id) => allTasks.get(id)).filter((t) => Boolean(t));
14270
+ const timeLabel = task.startedAt && task.completedAt ? `${formatMissionTimestamp(task.startedAt)} \u2192 ${formatMissionTimestamp(task.completedAt)}` : task.startedAt ? `started ${formatMissionTimestamp(task.startedAt)}` : "not started";
14271
+ const outcome = deriveTaskOutcome({ task, taskEvents, liveOutput, parents });
14272
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-5", children: [
14273
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
14274
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
14275
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "min-w-0 flex-1 truncate text-lg font-semibold text-foreground", children: missionTaskTitle(task.goal) }),
14276
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-mono text-[10px] text-muted-foreground/60", children: task.id.slice(0, 8) })
14277
+ ] }),
14278
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 text-xs", children: [
14279
+ /* @__PURE__ */ jsxRuntime.jsx(
14280
+ Badge,
14281
+ {
14282
+ variant: "destructive",
14283
+ className: `font-mono text-sm uppercase tracking-wide ${statusBadgeClass(task.status)}`,
14284
+ children: task.status
14285
+ }
14286
+ ),
14287
+ task.agentDefName ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: task.agentDefName }) : null,
14288
+ task.attemptCount > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
14289
+ "attempts ",
14290
+ task.attemptCount,
14291
+ "/",
14292
+ task.maxAttempts
14293
+ ] }) : null,
14294
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-auto inline-flex items-center gap-1 text-muted-foreground", children: [
14295
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "size-3" }),
14296
+ timeLabel
14297
+ ] })
14298
+ ] })
14299
+ ] }),
14300
+ /* @__PURE__ */ jsxRuntime.jsx(OutcomeCard, { outcome }),
14301
+ parents.length > 0 || children.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
14302
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-[10px] uppercase tracking-widest text-muted-foreground/70", children: "Dependencies" }),
14303
+ parents.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
14304
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-1 text-[10px] uppercase text-muted-foreground/50", children: "Depends on" }),
14305
+ parents.map((p) => /* @__PURE__ */ jsxRuntime.jsx(TaskChip, { task: p, onSelect }, p.id))
14306
+ ] }) : null,
14307
+ children.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [
14308
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-1 text-[10px] uppercase text-muted-foreground/50", children: "Blocks" }),
14309
+ children.map((c) => /* @__PURE__ */ jsxRuntime.jsx(TaskChip, { task: c, onSelect }, c.id))
14310
+ ] }) : null
14311
+ ] }) : null,
14312
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
14313
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1 font-mono text-[10px] uppercase tracking-widest text-muted-foreground/70", children: "Brief" }),
14314
+ /* @__PURE__ */ jsxRuntime.jsx(MessageResponse, { className: "text-sm leading-relaxed text-foreground/90", children: task.goal })
14315
+ ] }),
14316
+ task.resultMetadata ? /* @__PURE__ */ jsxRuntime.jsxs("details", { children: [
14317
+ /* @__PURE__ */ jsxRuntime.jsx("summary", { className: "cursor-pointer font-mono text-[10px] uppercase tracking-widest text-muted-foreground/70", children: "Result metadata" }),
14318
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-1 max-h-64 overflow-auto whitespace-pre-wrap rounded bg-muted/30 px-2 py-1.5 text-[11px] text-foreground/90", children: JSON.stringify(task.resultMetadata, null, 2) })
14319
+ ] }) : null,
14320
+ eventsSupported && taskEvents.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
14321
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 font-mono font-semibold uppercase text-muted-foreground", children: [
14322
+ "History (",
14323
+ taskEvents.length,
14324
+ ")"
14325
+ ] }),
14326
+ /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "space-y-3", children: taskEvents.map((e) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-start gap-3 text-xs", children: [
14327
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-16 shrink-0 font-mono text-[10px] text-muted-foreground/60", children: formatMissionTimestamp(e.createdAt) }),
14328
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "", children: [
14329
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 font-mono uppercase text-muted-foreground", children: missionEventCategory(e.type) }),
14330
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "min-w-0 flex-1 text-foreground/80", children: missionEventSummary(e) })
14331
+ ] })
14332
+ ] }, e.id)) })
14333
+ ] }) : null,
14334
+ task.currentSessionId && onOpenTranscript ? /* @__PURE__ */ jsxRuntime.jsxs(
14335
+ Button,
14336
+ {
14337
+ "data-testid": "view-session",
14338
+ variant: "outline",
14339
+ size: "sm",
14340
+ onClick: () => onOpenTranscript(task.currentSessionId),
14341
+ children: [
14342
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "size-4" }),
14343
+ "View ",
14344
+ task.agentDefName ?? "worker",
14345
+ " session"
14346
+ ]
14347
+ }
14348
+ ) : null
14349
+ ] });
14350
+ }
14351
+ function deriveTaskOutcome({
14352
+ task,
14353
+ taskEvents,
14354
+ liveOutput,
14355
+ parents
14356
+ }) {
14357
+ const result = task.result ? stripMissionControlMarkup(task.result) : "";
14358
+ switch (task.status) {
14359
+ case "done":
14360
+ return {
14361
+ label: "Result",
14362
+ tone: "bg-emerald-500/5 ring-emerald-500/25",
14363
+ labelTone: "text-emerald-600",
14364
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "size-3" }),
14365
+ timestamp: task.completedAt,
14366
+ markdown: result || void 0,
14367
+ text: result ? void 0 : "Completed."
14368
+ };
14369
+ case "failed":
14370
+ return {
14371
+ label: "Failed",
14372
+ tone: "bg-destructive/5 ring-destructive/25",
14373
+ labelTone: "text-destructive",
14374
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "size-3" }),
14375
+ timestamp: task.completedAt,
14376
+ markdown: result || void 0,
14377
+ text: result ? void 0 : `Failed after ${task.attemptCount}/${task.maxAttempts} attempts.`
14378
+ };
14379
+ case "cancelled":
14380
+ return {
14381
+ label: "Cancelled",
14382
+ tone: "bg-muted/40 ring-border",
14383
+ labelTone: "text-muted-foreground",
14384
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ban, { className: "size-3" }),
14385
+ timestamp: task.completedAt,
14386
+ text: "Task cancelled."
14387
+ };
14388
+ case "running":
14389
+ return {
14390
+ label: "Live output",
14391
+ tone: "bg-amber-500/5 ring-amber-500/25",
14392
+ labelTone: "text-amber-600",
14393
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleDot, { className: "size-3 animate-pulse" }),
14394
+ timestamp: liveOutput?.createdAt ?? null,
14395
+ text: liveOutput ? missionEventSummary(liveOutput) : "Working \u2014 no output yet."
14396
+ };
14397
+ case "blocked": {
14398
+ const blockedEvent = [...taskEvents].reverse().find((e) => e.type === "task.blocked");
14399
+ return {
14400
+ label: "Waiting",
14401
+ tone: "bg-amber-500/5 ring-amber-500/20",
14402
+ labelTone: "text-amber-700 dark:text-amber-500",
14403
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "size-3" }),
14404
+ timestamp: blockedEvent?.createdAt ?? null,
14405
+ text: blockedEvent ? missionEventSummary(blockedEvent) : "Blocked \u2014 awaiting input."
14406
+ };
14407
+ }
14408
+ default: {
14409
+ const pending = parents.filter((p) => p.status !== "done");
14410
+ const text = pending.length > 0 ? `Waiting on ${pending.map((p) => missionTaskTitle(p.goal, 40)).join(" \xB7 ")} to complete.` : "Queued \u2014 waiting for a worker slot.";
14411
+ return {
14412
+ label: "Waiting",
14413
+ tone: "bg-muted/30 ring-border",
14414
+ labelTone: "text-muted-foreground",
14415
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "size-3" }),
14416
+ timestamp: null,
14417
+ text
14418
+ };
14419
+ }
14420
+ }
14421
+ }
14422
+ function OutcomeCard({ outcome }) {
14423
+ return /* @__PURE__ */ jsxRuntime.jsxs(
14424
+ Card,
14425
+ {
14426
+ "data-testid": "task-outcome",
14427
+ size: "sm",
14428
+ className: `mx-1 gap-3 rounded-lg py-4 shadow-none ${outcome.tone}`,
14429
+ children: [
14430
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { children: [
14431
+ /* @__PURE__ */ jsxRuntime.jsxs(
14432
+ CardTitle,
14433
+ {
14434
+ className: `flex items-center gap-1.5 font-mono text-[11px] font-semibold tracking-widest ${outcome.labelTone}`,
14435
+ children: [
14436
+ outcome.icon,
14437
+ outcome.label
14438
+ ]
14439
+ }
14440
+ ),
14441
+ outcome.timestamp ? /* @__PURE__ */ jsxRuntime.jsx(CardAction, { className: "font-mono text-[10px] text-muted-foreground/60", children: formatMissionTimestamp(outcome.timestamp) }) : null
14442
+ ] }),
14443
+ /* @__PURE__ */ jsxRuntime.jsx(CardContent, { children: outcome.markdown ? /* @__PURE__ */ jsxRuntime.jsx(MessageResponse, { className: "font-mono text-xs leading-relaxed text-foreground/90", children: outcome.markdown }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap font-mono text-xs leading-relaxed text-foreground/90", children: outcome.text }) })
14444
+ ]
14445
+ }
14446
+ );
14447
+ }
14448
+ var MISSION_WORKER_KIND_BADGE_CLASS = {
14449
+ task: "border-primary/40 bg-primary/10 text-primary uppercase tracking-wide text-[10px]",
14450
+ worker: "border-foreground/30 bg-foreground/5 text-foreground/80 uppercase tracking-wide text-[10px]",
14451
+ delegation: "border-foreground/20 bg-foreground/[0.03] text-foreground/60 uppercase tracking-wide text-[10px]"
14452
+ };
14453
+ var MISSION_WORKER_KIND_TOOLTIP = {
14454
+ task: "Durable Task row created by spawn_task. Retried by the dispatcher; survives across coordinator wakes.",
14455
+ worker: "Async one-shot session spawned by spawn_worker. Durable session, no retry/DAG.",
14456
+ delegation: "Sync fork-join child spawned by delegate_task. Coordinator's wake blocked until it finished."
14457
+ };
14458
+ function MissionWorkersTab({
14459
+ workers,
14460
+ tasks,
14461
+ onOpenTranscript
14462
+ }) {
14463
+ if (workers.length === 0) {
14464
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-8 text-center text-sm text-muted-foreground/60", children: "No workers attached to this mission right now." });
14465
+ }
14466
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-3 sm:grid-cols-2", children: workers.map((w) => /* @__PURE__ */ jsxRuntime.jsx(
14467
+ WorkerCard,
14468
+ {
14469
+ worker: w,
14470
+ tasks,
14471
+ onOpenTranscript
14472
+ },
14473
+ w.workerSessionId
14474
+ )) });
14475
+ }
14476
+ function WorkerCard({
14477
+ worker,
14478
+ tasks,
14479
+ onOpenTranscript
14480
+ }) {
14481
+ const counts = missionWorkerTaskCounts(worker, tasks);
14482
+ const statusLabel2 = worker.kind === "task" ? worker.taskStatus ?? "\u2014" : worker.sessionStatus;
14483
+ const onTranscriptClick = (e) => {
14484
+ if (onOpenTranscript) {
14485
+ e.preventDefault();
14486
+ onOpenTranscript(worker.workerSessionId);
14487
+ }
14488
+ };
14489
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 rounded-md border border-border/60 bg-card p-4 shadow-sm", children: [
14490
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
14491
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
14492
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-sm font-semibold text-foreground", children: worker.agentDefName ?? "worker" }),
14493
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
14494
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
14495
+ Badge,
14496
+ {
14497
+ variant: "outline",
14498
+ className: `mt-1 ${MISSION_WORKER_KIND_BADGE_CLASS[worker.kind]}`,
14499
+ children: worker.kind
14500
+ }
14501
+ ) }),
14502
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { className: "max-w-xs", children: MISSION_WORKER_KIND_TOOLTIP[worker.kind] })
14503
+ ] })
14504
+ ] }),
14505
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-mono text-[10px] uppercase tracking-wider text-muted-foreground/70", children: statusLabel2 })
14506
+ ] }),
14507
+ counts ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-6", children: [
14508
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
14509
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl font-bold tabular-nums text-foreground", children: counts.completed }),
14510
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-[9px] uppercase tracking-widest text-muted-foreground/60", children: "Completed" })
14511
+ ] }),
14512
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
14513
+ /* @__PURE__ */ jsxRuntime.jsx(
14514
+ "div",
14515
+ {
14516
+ className: `text-xl font-bold tabular-nums ${counts.inFlight > 0 ? "text-amber-600" : "text-foreground"}`,
14517
+ children: counts.inFlight
14518
+ }
14519
+ ),
14520
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-[9px] uppercase tracking-widest text-muted-foreground/60", children: "In flight" })
14521
+ ] })
14522
+ ] }) : null,
14523
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto space-y-1 border-t border-border/40 pt-2", children: [
14524
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
14525
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-mono text-[10px] text-muted-foreground/70", children: worker.latestEventKind ?? "no events yet" }),
14526
+ worker.latestEventAt ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-mono text-[10px] text-muted-foreground/60", children: formatMissionTimestamp(worker.latestEventAt) }) : null
14527
+ ] }),
14528
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-xs text-foreground/80", children: deriveMissionWorkerActivityLabel(worker) }),
14529
+ /* @__PURE__ */ jsxRuntime.jsx(
14530
+ "a",
14531
+ {
14532
+ href: worker.transcriptUrl,
14533
+ target: "_blank",
14534
+ rel: "noreferrer",
14535
+ onClick: onTranscriptClick,
14536
+ className: "inline-block text-xs text-primary hover:underline",
14537
+ children: "View session"
14538
+ }
14539
+ )
14540
+ ] })
14541
+ ] });
14542
+ }
14543
+ var PAGE_LIMIT = 500;
14544
+ var MAX_PAGES_PER_PUMP = 20;
14545
+ function useMissionEvents(input) {
14546
+ const { adapter, missionId, missionStatus, isTerminal, pollIntervalMs } = input;
14547
+ const supported = typeof adapter.listMissionEvents === "function";
14548
+ const [events, setEvents] = react.useState([]);
14549
+ const [sessions, setSessions] = react.useState({});
14550
+ const lastIdRef = react.useRef(null);
14551
+ const pumpingRef = react.useRef(false);
14552
+ const mountedRef = react.useRef(true);
14553
+ react.useEffect(() => {
14554
+ mountedRef.current = true;
14555
+ return () => {
14556
+ mountedRef.current = false;
14557
+ };
14558
+ }, []);
14559
+ react.useEffect(() => {
14560
+ lastIdRef.current = null;
14561
+ setEvents([]);
14562
+ setSessions({});
14563
+ }, [missionId]);
14564
+ const pump = react.useCallback(async () => {
14565
+ const fn = adapter.listMissionEvents;
14566
+ if (typeof fn !== "function" || pumpingRef.current) return;
14567
+ pumpingRef.current = true;
14568
+ try {
14569
+ for (let page = 0; page < MAX_PAGES_PER_PUMP; page += 1) {
14570
+ const resp = await fn.call(adapter, {
14571
+ missionId,
14572
+ afterId: lastIdRef.current ?? void 0,
14573
+ limit: PAGE_LIMIT
14574
+ });
14575
+ if (!mountedRef.current) return;
14576
+ if (resp.events.length > 0) {
14577
+ lastIdRef.current = resp.events[resp.events.length - 1].id;
14578
+ setEvents((prev) => mergeMissionEvents(prev, resp.events));
14579
+ }
14580
+ if (Object.keys(resp.sessions).length > 0) {
14581
+ setSessions((prev) => ({ ...prev, ...resp.sessions }));
14582
+ }
14583
+ if (resp.events.length < PAGE_LIMIT) break;
14584
+ }
14585
+ } catch {
14586
+ } finally {
14587
+ pumpingRef.current = false;
14588
+ }
14589
+ }, [adapter, missionId]);
14590
+ react.useEffect(() => {
14591
+ void pump();
14592
+ }, [pump]);
14593
+ react.useEffect(() => {
14594
+ if (!supported || missionStatus === null || isTerminal) return;
14595
+ const id = window.setInterval(() => {
14596
+ void pump();
14597
+ }, pollIntervalMs);
14598
+ return () => {
14599
+ window.clearInterval(id);
14600
+ };
14601
+ }, [supported, missionStatus, isTerminal, pollIntervalMs, pump]);
14602
+ return { supported, events, sessions };
14603
+ }
14604
+ var DEFAULT_POLL_INTERVAL_MS = 5e3;
14605
+ function CountBadge({ n }) {
14606
+ if (n === 0) return null;
14607
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 rounded bg-muted px-1 py-px text-[9px] text-muted-foreground/70", children: n });
14608
+ }
14609
+ var INITIAL_STATE = {
14610
+ mission: null,
14611
+ tasks: [],
13286
14612
  workers: [],
13287
14613
  loading: true,
13288
14614
  error: null
@@ -13361,10 +14687,13 @@ function MissionDashboard({
13361
14687
  window.clearInterval(id);
13362
14688
  };
13363
14689
  }, [refresh, state.mission, pollIntervalMs]);
13364
- const taskBuckets = react.useMemo(
13365
- () => groupMissionTasksByBucket(state.tasks),
13366
- [state.tasks]
13367
- );
14690
+ const feed = useMissionEvents({
14691
+ adapter,
14692
+ missionId,
14693
+ missionStatus: state.mission?.status ?? null,
14694
+ isTerminal: state.mission ? isTerminalMissionStatus(state.mission.status) : false,
14695
+ pollIntervalMs
14696
+ });
13368
14697
  const runningWorkerCount = react.useMemo(
13369
14698
  // "running" means the child is still consuming compute right now.
13370
14699
  // For task-backed children that's ``taskStatus === "running"``; for
@@ -13433,7 +14762,6 @@ function MissionDashboard({
13433
14762
  }
13434
14763
  };
13435
14764
  const statusTone = mission.status === "satisfied" ? "border-emerald-500/40 bg-emerald-500/10 text-emerald-700 dark:text-emerald-400" : mission.status === "failed" || mission.status === "cancelled" ? "border-destructive/40 bg-destructive/10 text-destructive" : mission.status === "paused" ? "border-amber-500/40 bg-amber-500/10 text-amber-700 dark:text-amber-400" : "border-primary/40 bg-primary/10 text-primary";
13436
- const activity = buildMissionActivity(mission, state.tasks, state.workers);
13437
14765
  const isActive = ACTIVE_MISSION_STATUSES.has(mission.status);
13438
14766
  const iterationPct = mission.maxIterations > 0 ? Math.min(100, mission.iteration / mission.maxIterations * 100) : 0;
13439
14767
  return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full flex-1 flex-col overflow-hidden bg-muted/30", children: [
@@ -13540,18 +14868,18 @@ function MissionDashboard({
13540
14868
  /* @__PURE__ */ jsxRuntime.jsxs(
13541
14869
  Tabs,
13542
14870
  {
13543
- defaultValue: "activity",
14871
+ defaultValue: "tasks",
13544
14872
  className: "mt-5 flex flex-1 flex-col overflow-hidden sm:mt-6",
13545
14873
  children: [
13546
14874
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 border-b border-border bg-background px-5 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsxs(TabsList, { variant: "line", children: [
13547
- /* @__PURE__ */ jsxRuntime.jsxs(TabsTrigger, { value: "activity", children: [
13548
- "Activity",
13549
- /* @__PURE__ */ jsxRuntime.jsx(CountBadge, { n: activity.length })
13550
- ] }),
13551
14875
  /* @__PURE__ */ jsxRuntime.jsxs(TabsTrigger, { value: "tasks", children: [
13552
14876
  "Tasks",
13553
14877
  /* @__PURE__ */ jsxRuntime.jsx(CountBadge, { n: state.tasks.length })
13554
14878
  ] }),
14879
+ feed.supported ? /* @__PURE__ */ jsxRuntime.jsxs(TabsTrigger, { value: "activity", children: [
14880
+ "Activity",
14881
+ /* @__PURE__ */ jsxRuntime.jsx(CountBadge, { n: feed.events.length })
14882
+ ] }) : null,
13555
14883
  /* @__PURE__ */ jsxRuntime.jsxs(TabsTrigger, { value: "workers", children: [
13556
14884
  "Workers",
13557
14885
  /* @__PURE__ */ jsxRuntime.jsx(CountBadge, { n: state.workers.length })
@@ -13559,22 +14887,24 @@ function MissionDashboard({
13559
14887
  /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { value: "metadata", children: "Metadata" })
13560
14888
  ] }) }),
13561
14889
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto bg-background px-5 py-4 sm:px-6", children: [
13562
- /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "activity", className: "mt-0", children: /* @__PURE__ */ jsxRuntime.jsx(ActivityTimeline, { entries: activity }) }),
13563
- /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "tasks", className: "mt-0", children: /* @__PURE__ */ jsxRuntime.jsx(
13564
- TasksList,
14890
+ /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "tasks", className: "mt-0 h-full", children: /* @__PURE__ */ jsxRuntime.jsx(
14891
+ MissionTasksTab,
13565
14892
  {
13566
- taskBuckets,
13567
- totalTasks: state.tasks.length
14893
+ tasks: state.tasks,
14894
+ feed,
14895
+ onOpenTranscript
13568
14896
  }
13569
14897
  ) }),
14898
+ feed.supported ? /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "activity", className: "mt-0", children: /* @__PURE__ */ jsxRuntime.jsx(MissionActivityTab, { feed }) }) : null,
13570
14899
  /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "workers", className: "mt-0", children: /* @__PURE__ */ jsxRuntime.jsx(
13571
- WorkersList,
14900
+ MissionWorkersTab,
13572
14901
  {
13573
14902
  workers: state.workers,
14903
+ tasks: state.tasks,
13574
14904
  onOpenTranscript
13575
14905
  }
13576
14906
  ) }),
13577
- /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "metadata", className: "mt-0", children: /* @__PURE__ */ jsxRuntime.jsx(MetadataPane, { mission }) })
14907
+ /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "metadata", className: "mt-0", children: /* @__PURE__ */ jsxRuntime.jsx(MissionMetadataTab, { mission }) })
13578
14908
  ] })
13579
14909
  ]
13580
14910
  }
@@ -13638,234 +14968,6 @@ function MissionDashboard({
13638
14968
  )
13639
14969
  ] }) });
13640
14970
  }
13641
- function ActivityTimeline({ entries }) {
13642
- if (entries.length === 0) {
13643
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-8 text-center text-sm text-muted-foreground/60", children: "No mission activity yet." });
13644
- }
13645
- return /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "space-y-1", children: entries.map((e, i) => /* @__PURE__ */ jsxRuntime.jsxs(
13646
- "li",
13647
- {
13648
- className: "flex items-start gap-3 rounded px-2 py-1.5 text-sm hover:bg-muted/30",
13649
- children: [
13650
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-20 shrink-0 font-mono text-[10px] text-muted-foreground/60", children: formatTimestamp(e.at) }),
13651
- /* @__PURE__ */ jsxRuntime.jsx(
13652
- "span",
13653
- {
13654
- className: `shrink-0 rounded border px-1.5 py-0.5 font-mono text-[9px] uppercase tracking-wide ${ACTIVITY_KIND_TONE[e.kind]}`,
13655
- children: e.label
13656
- }
13657
- ),
13658
- e.detail ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-foreground/80", children: e.detail }) : null
13659
- ]
13660
- },
13661
- `${e.kind}-${e.at}-${i}`
13662
- )) });
13663
- }
13664
- function TaskRow({ task }) {
13665
- const [open, setOpen] = react.useState(false);
13666
- const goalPreview = firstLine3(task.goal, 160);
13667
- const hasMore = task.goal.length > 160 || Boolean(task.result) || Boolean(task.resultMetadata);
13668
- const statusTone = task.status === "done" ? "border-emerald-500/30 bg-emerald-500/10 text-emerald-600" : task.status === "failed" ? "border-destructive/30 bg-destructive/10 text-destructive" : task.status === "cancelled" ? "border-border bg-muted text-muted-foreground" : task.status === "running" ? "border-primary/30 bg-primary/10 text-primary" : "border-foreground/20 bg-foreground/5 text-foreground/70";
13669
- return /* @__PURE__ */ jsxRuntime.jsx(Collapsible, { open, onOpenChange: setOpen, asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "rounded border border-border/40 text-sm", children: [
13670
- /* @__PURE__ */ jsxRuntime.jsx(
13671
- CollapsibleTrigger,
13672
- {
13673
- asChild: true,
13674
- disabled: !hasMore,
13675
- children: /* @__PURE__ */ jsxRuntime.jsxs(
13676
- "button",
13677
- {
13678
- type: "button",
13679
- className: "flex w-full items-start gap-3 px-3 py-2 text-left hover:bg-muted/30 disabled:hover:bg-transparent",
13680
- children: [
13681
- /* @__PURE__ */ jsxRuntime.jsx(
13682
- lucideReact.ChevronRight,
13683
- {
13684
- className: `mt-0.5 size-3.5 shrink-0 text-muted-foreground/60 transition-transform ${open ? "rotate-90" : ""} ${hasMore ? "" : "opacity-0"}`
13685
- }
13686
- ),
13687
- /* @__PURE__ */ jsxRuntime.jsx(
13688
- "span",
13689
- {
13690
- className: `shrink-0 rounded border px-1.5 py-0.5 font-mono text-[9px] uppercase tracking-wide ${statusTone}`,
13691
- children: task.status
13692
- }
13693
- ),
13694
- task.agentDefName ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground/70", children: task.agentDefName }) : null,
13695
- task.attemptCount > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-[10px] text-muted-foreground/60", children: [
13696
- "(",
13697
- task.attemptCount,
13698
- "/",
13699
- task.maxAttempts,
13700
- ")"
13701
- ] }) : null,
13702
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-foreground/80", children: goalPreview })
13703
- ]
13704
- }
13705
- )
13706
- }
13707
- ),
13708
- /* @__PURE__ */ jsxRuntime.jsxs(CollapsibleContent, { className: "border-t border-border/40 px-3 py-2 text-xs", children: [
13709
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2", children: [
13710
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1 font-mono text-[9px] uppercase tracking-wide text-muted-foreground/60", children: "Goal" }),
13711
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "max-h-64 overflow-auto whitespace-pre-wrap rounded bg-muted/30 px-2 py-1.5 text-[11px] text-foreground/90", children: task.goal })
13712
- ] }),
13713
- task.parentIds.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 text-[10px] text-muted-foreground/70", children: [
13714
- "after: ",
13715
- task.parentIds.map((p) => p.slice(0, 8)).join(", ")
13716
- ] }) : null,
13717
- task.result ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2", children: [
13718
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1 font-mono text-[9px] uppercase tracking-wide text-muted-foreground/60", children: "Result" }),
13719
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "max-h-64 overflow-auto whitespace-pre-wrap rounded bg-muted/30 px-2 py-1.5 text-[11px] text-foreground/90", children: task.result })
13720
- ] }) : null,
13721
- task.resultMetadata ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
13722
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1 font-mono text-[9px] uppercase tracking-wide text-muted-foreground/60", children: "Result metadata" }),
13723
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "max-h-64 overflow-auto whitespace-pre-wrap rounded bg-muted/30 px-2 py-1.5 text-[11px] text-foreground/90", children: JSON.stringify(task.resultMetadata, null, 2) })
13724
- ] }) : null
13725
- ] })
13726
- ] }) });
13727
- }
13728
- function TasksList({
13729
- taskBuckets,
13730
- totalTasks
13731
- }) {
13732
- if (totalTasks === 0) {
13733
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-8 text-center text-sm text-muted-foreground/60", children: "No tasks spawned yet." });
13734
- }
13735
- const headings = {
13736
- in_flight: "In flight",
13737
- blocked: "Blocked",
13738
- done: "Done",
13739
- failed_or_cancelled: "Failed / cancelled"
13740
- };
13741
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: Object.keys(headings).map((bucket) => {
13742
- const rows = taskBuckets[bucket];
13743
- if (rows.length === 0) return null;
13744
- return /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
13745
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-mono text-[10px] uppercase tracking-widest text-muted-foreground/70", children: [
13746
- headings[bucket],
13747
- " (",
13748
- rows.length,
13749
- ")"
13750
- ] }),
13751
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1", children: rows.map((task) => /* @__PURE__ */ jsxRuntime.jsx(TaskRow, { task }, task.id)) })
13752
- ] }, bucket);
13753
- }) });
13754
- }
13755
- function WorkersList({
13756
- workers,
13757
- onOpenTranscript
13758
- }) {
13759
- if (workers.length === 0) {
13760
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-8 text-center text-sm text-muted-foreground/60", children: "No workers attached to this mission right now." });
13761
- }
13762
- return /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1", children: workers.map((w) => {
13763
- const onTranscriptClick = (e) => {
13764
- if (onOpenTranscript) {
13765
- e.preventDefault();
13766
- onOpenTranscript(w.workerSessionId);
13767
- }
13768
- };
13769
- return /* @__PURE__ */ jsxRuntime.jsxs(
13770
- "li",
13771
- {
13772
- className: "flex flex-col gap-2 rounded border border-border/40 px-3 py-2 text-sm sm:flex-row sm:items-start sm:justify-between",
13773
- children: [
13774
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
13775
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
13776
- /* @__PURE__ */ jsxRuntime.jsx(
13777
- Badge,
13778
- {
13779
- variant: "outline",
13780
- className: MISSION_WORKER_KIND_BADGE_CLASS[w.kind],
13781
- title: MISSION_WORKER_KIND_TOOLTIP[w.kind],
13782
- children: w.kind
13783
- }
13784
- ),
13785
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { className: "text-foreground/70", children: w.kind === "task" ? w.taskStatus ?? "\u2014" : w.sessionStatus }),
13786
- w.agentDefName ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground/70", children: w.agentDefName }) : null,
13787
- w.latestEventAt ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto font-mono text-[10px] text-muted-foreground/60", children: formatTimestamp(w.latestEventAt) }) : null
13788
- ] }),
13789
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-sm text-foreground/80", children: deriveMissionWorkerActivityLabel(w) })
13790
- ] }),
13791
- /* @__PURE__ */ jsxRuntime.jsx(
13792
- "a",
13793
- {
13794
- href: w.transcriptUrl,
13795
- target: "_blank",
13796
- rel: "noreferrer",
13797
- onClick: onTranscriptClick,
13798
- className: "self-start text-xs text-primary hover:underline",
13799
- children: "View session"
13800
- }
13801
- )
13802
- ]
13803
- },
13804
- w.workerSessionId
13805
- );
13806
- }) });
13807
- }
13808
- function MetadataPane({ mission }) {
13809
- const rows = [];
13810
- rows.push({
13811
- label: "Mission ID",
13812
- value: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-[11px]", children: mission.id })
13813
- });
13814
- rows.push({
13815
- label: "Session ID",
13816
- value: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-[11px]", children: mission.sessionId })
13817
- });
13818
- rows.push({
13819
- label: "Agent ID",
13820
- value: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-[11px]", children: mission.agentId })
13821
- });
13822
- rows.push({ label: "Status", value: mission.status });
13823
- rows.push({
13824
- label: "Iteration",
13825
- value: `${mission.iteration} / ${mission.maxIterations}`
13826
- });
13827
- rows.push({
13828
- label: "Owner",
13829
- value: mission.userId ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13830
- "user ",
13831
- /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-[11px]", children: mission.userId })
13832
- ] }) : mission.serviceAccountId ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13833
- "service account ",
13834
- /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-[11px]", children: mission.serviceAccountId })
13835
- ] }) : "\u2014"
13836
- });
13837
- rows.push({
13838
- label: "Created",
13839
- value: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[11px]", children: new Date(mission.createdAt).toLocaleString() })
13840
- });
13841
- rows.push({
13842
- label: "Updated",
13843
- value: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[11px]", children: new Date(mission.updatedAt).toLocaleString() })
13844
- });
13845
- if (mission.pausedReason) {
13846
- rows.push({ label: "Paused reason", value: mission.pausedReason });
13847
- }
13848
- if (mission.cancelledReason) {
13849
- rows.push({ label: "Cancelled reason", value: mission.cancelledReason });
13850
- }
13851
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
13852
- mission.lastEvaluationResult ? /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-2", children: [
13853
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-mono text-[10px] uppercase tracking-widest text-muted-foreground/70", children: [
13854
- "Last verdict \u2014 ",
13855
- mission.lastEvaluationResult
13856
- ] }),
13857
- mission.lastEvaluationFeedback ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm text-foreground/90", children: mission.lastEvaluationFeedback }) : null,
13858
- mission.lastEvaluationExplanation ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-xs text-muted-foreground/70", children: mission.lastEvaluationExplanation }) : null
13859
- ] }) : null,
13860
- /* @__PURE__ */ jsxRuntime.jsxs("section", { children: [
13861
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2 font-mono text-[10px] uppercase tracking-widest text-muted-foreground/70", children: "Identifiers" }),
13862
- /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "grid grid-cols-[max-content_1fr] gap-x-4 gap-y-1 text-xs", children: rows.map((r) => /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
13863
- /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "text-muted-foreground/70", children: r.label }),
13864
- /* @__PURE__ */ jsxRuntime.jsx("dd", { className: "break-all text-foreground/90", children: r.value })
13865
- ] }, r.label)) })
13866
- ] })
13867
- ] });
13868
- }
13869
14971
  init_utils();
13870
14972
  var DEFAULT_POLL_INTERVAL_MS2 = 3e4;
13871
14973
  function fingerprint(items) {
@@ -14105,7 +15207,7 @@ function MissionsPanel({
14105
15207
  if (!adapter.cancelMission || busyMissionId) return;
14106
15208
  setBusyMissionId(missionId);
14107
15209
  try {
14108
- await adapter.cancelMission({ missionId });
15210
+ await adapter.cancelMission({ missionId, cascadeToWorkers: true });
14109
15211
  setItems((current) => {
14110
15212
  const next = current.filter((m) => m.id !== missionId);
14111
15213
  lastFingerprint.current = fingerprint(next);
@@ -14897,24 +15999,40 @@ exports.ACTIVE_MISSION_STATUSES = ACTIVE_MISSION_STATUSES;
14897
15999
  exports.AgentChat = AgentChat;
14898
16000
  exports.AgentChatAdapterProvider = AgentChatAdapterProvider;
14899
16001
  exports.CitationText = CitationText;
16002
+ exports.CodingAgentsPanel = CodingAgentsPanel;
14900
16003
  exports.ComposioConnectCard = ComposioConnectCard;
14901
16004
  exports.InboxPanel = InboxPanel;
14902
16005
  exports.IntegrationsBand = IntegrationsBand;
14903
16006
  exports.IntegrationsPage = IntegrationsPage;
16007
+ exports.MISSION_EVENT_CATEGORIES = MISSION_EVENT_CATEGORIES;
14904
16008
  exports.MessageResponse = MessageResponse;
14905
16009
  exports.MissionDashboard = MissionDashboard;
14906
16010
  exports.MissionsPanel = MissionsPanel;
14907
16011
  exports.ResearchSourcesPanel = ResearchSourcesPanel;
14908
16012
  exports.ScheduledWorkPanel = ScheduledWorkPanel;
14909
16013
  exports.SessionTreePanel = SessionTreePanel;
16014
+ exports.defaultSelectedMissionTaskId = defaultSelectedMissionTaskId;
14910
16015
  exports.deriveMissionWorkerActivityLabel = deriveMissionWorkerActivityLabel;
16016
+ exports.formatMissionTimestamp = formatMissionTimestamp;
14911
16017
  exports.groupMissionTasksByBucket = groupMissionTasksByBucket;
14912
16018
  exports.isTerminalMissionStatus = isTerminalMissionStatus;
16019
+ exports.mergeMissionEvents = mergeMissionEvents;
16020
+ exports.missionEventActorLabel = missionEventActorLabel;
16021
+ exports.missionEventCategory = missionEventCategory;
16022
+ exports.missionEventSummary = missionEventSummary;
16023
+ exports.missionEventTaskId = missionEventTaskId;
16024
+ exports.missionTaskBlocks = missionTaskBlocks;
14913
16025
  exports.missionTaskBucket = missionTaskBucket;
16026
+ exports.missionTaskRailGroups = missionTaskRailGroups;
16027
+ exports.missionTaskStatusDotClass = missionTaskStatusDotClass;
16028
+ exports.missionTaskTitle = missionTaskTitle;
16029
+ exports.missionWorkerTaskCounts = missionWorkerTaskCounts;
14914
16030
  exports.splitCitations = splitCitations;
16031
+ exports.stripMissionControlMarkup = stripMissionControlMarkup;
14915
16032
  exports.useAgentChatAdapterContext = useAgentChatAdapterContext;
14916
16033
  exports.useAgentChatRuntime = useAgentChatRuntime;
14917
16034
  exports.useChatViewMode = useChatViewMode;
14918
16035
  exports.useInboxUnreadCount = useInboxUnreadCount;
16036
+ exports.useMissionEvents = useMissionEvents;
14919
16037
  //# sourceMappingURL=index.cjs.map
14920
16038
  //# sourceMappingURL=index.cjs.map