@volter-ai-dev/supercode-ui 0.1.13 → 0.1.15

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/components.mjs CHANGED
@@ -292,7 +292,8 @@ function readTranscript(value) {
292
292
  role: item.role,
293
293
  text: item.text,
294
294
  ts: nullableNumber(item.ts),
295
- truncated: item.truncated === true
295
+ truncated: item.truncated === true,
296
+ ...Number.isSafeInteger(item.messageIndex) && item.messageIndex > 0 ? { messageIndex: item.messageIndex } : {}
296
297
  };
297
298
  for (const key of ["label", "arguments", "resultText", "code"]) {
298
299
  if (typeof item[key] === "string") entry[key] = item[key];
@@ -341,6 +342,7 @@ function readSessions(value) {
341
342
  name: string(row.name),
342
343
  cwd: string(row.cwd),
343
344
  title: string(row.title),
345
+ preview: string(row.preview),
344
346
  age: string(row.age),
345
347
  updatedAt: nullableNumber(row.updatedAt),
346
348
  messages: nullableNumber(row.messages),
@@ -463,7 +465,7 @@ function normalizeUiState(value) {
463
465
  savedDraft: string(raw.savedDraft),
464
466
  attention: Array.isArray(raw.attention) ? raw.attention.flatMap((candidate) => {
465
467
  const item = record(candidate);
466
- return item && typeof item.key === "string" && ["unseen", "finished", "failed"].includes(item.kind) ? [{ key: item.key, kind: item.kind, ...typeof item.preview === "string" ? { preview: item.preview } : {} }] : [];
468
+ return item && typeof item.key === "string" && ["unseen", "finished", "failed"].includes(item.kind) ? [{ key: item.key, kind: item.kind, ...typeof item.preview === "string" ? { preview: item.preview } : {}, ...Number.isSafeInteger(item.afterMessages) && item.afterMessages >= 0 ? { afterMessages: item.afterMessages } : {} }] : [];
467
469
  }) : [],
468
470
  sessions: readSessions(raw.sessions),
469
471
  attached: readAttached(raw.attached),
@@ -545,7 +547,7 @@ function canContinueHere(state) {
545
547
  }
546
548
  function operationLabel(operation) {
547
549
  if (!operation) return "";
548
- const labels = { discover: "Refreshing chats\u2026", observe: "Loading recent messages\u2026", attach: "Opening chat\u2026", resume: "Continuing here\u2026", branch: "Starting continuation\u2026", reduce: "Reducing context and verifying reversibility\u2026", terminal: "Preparing terminal handoff\u2026", export: "Exporting losslessly\u2026", refresh: "Retrying\u2026" };
550
+ const labels = { discover: "Refreshing chats\u2026", observe: "Loading recent messages\u2026", attach: "Opening chat\u2026", start: "Starting new chat\u2026", resume: "Continuing here\u2026", join: "Joining live session\u2026", detach: "Detaching to read-only\u2026", branch: "Starting continuation\u2026", reduce: "Reducing context and verifying reversibility\u2026", terminal: "Preparing terminal handoff\u2026", export: "Exporting losslessly\u2026", interrupt: "Stopping agent\u2026", respond: "Sending response\u2026", loadEarlier: "Loading earlier messages\u2026", loadSessions: "Loading more chats\u2026", refresh: "Retrying\u2026" };
549
551
  return labels[operation] ?? `${operation.replaceAll("_", " ")}\u2026`;
550
552
  }
551
553
  function terminalCommand(handoff) {
@@ -564,8 +566,65 @@ function boundedSet(map, key, value) {
564
566
  while (map.size > MEMORY_LIMIT) map.delete(map.keys().next().value);
565
567
  }
566
568
 
569
+ // src/icon.jsx
570
+ import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
571
+ var ICONS = {
572
+ back: () => /* @__PURE__ */ jsx("path", { d: "m11.5 4.5-4.5 4.5 4.5 4.5" }),
573
+ check: () => /* @__PURE__ */ jsx("path", { d: "m4 9 3.25 3.25L14 5.5" }),
574
+ chevron: () => /* @__PURE__ */ jsx("path", { d: "m7 4.5 4.5 4.5L7 13.5" }),
575
+ close: () => /* @__PURE__ */ jsxs(Fragment, { children: [
576
+ /* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
577
+ /* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
578
+ ] }),
579
+ copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
580
+ /* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
581
+ /* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
582
+ ] }),
583
+ down: () => /* @__PURE__ */ jsxs(Fragment, { children: [
584
+ /* @__PURE__ */ jsx("path", { d: "M9 3.5v10" }),
585
+ /* @__PURE__ */ jsx("path", { d: "m4.75 9.5 4.25 4 4.25-4" })
586
+ ] }),
587
+ menu: () => /* @__PURE__ */ jsxs(Fragment, { children: [
588
+ /* @__PURE__ */ jsx("circle", { cx: "4", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
589
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
590
+ /* @__PURE__ */ jsx("circle", { cx: "14", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" })
591
+ ] }),
592
+ plus: () => /* @__PURE__ */ jsxs(Fragment, { children: [
593
+ /* @__PURE__ */ jsx("path", { d: "M9 3.5v11" }),
594
+ /* @__PURE__ */ jsx("path", { d: "M3.5 9h11" })
595
+ ] }),
596
+ search: () => /* @__PURE__ */ jsxs(Fragment, { children: [
597
+ /* @__PURE__ */ jsx("circle", { cx: "7.75", cy: "7.75", r: "4.25" }),
598
+ /* @__PURE__ */ jsx("path", { d: "m11 11 3.5 3.5" })
599
+ ] }),
600
+ send: () => /* @__PURE__ */ jsxs(Fragment, { children: [
601
+ /* @__PURE__ */ jsx("path", { d: "M9 14.5v-11" }),
602
+ /* @__PURE__ */ jsx("path", { d: "m4.75 7.75 4.25-4.25 4.25 4.25" })
603
+ ] }),
604
+ stop: () => /* @__PURE__ */ jsx("rect", { x: "4.5", y: "4.5", width: "9", height: "9", rx: "1.5", fill: "currentColor", stroke: "none" })
605
+ };
606
+ function UiIcon({ name, size = 16, class: className = "" }) {
607
+ const Glyph = ICONS[name];
608
+ if (!Glyph) return null;
609
+ return /* @__PURE__ */ jsx("svg", { class: `scui-icon ${className}`, style: { "--scui-icon-size": `${size}px` }, viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Glyph, {}) });
610
+ }
611
+
612
+ // src/textarea.js
613
+ import { useLayoutEffect } from "preact/hooks";
614
+ function useAutosizeTextarea(ref, value) {
615
+ useLayoutEffect(() => {
616
+ const element = ref.current;
617
+ if (!element) return;
618
+ element.style.height = "auto";
619
+ const maxHeight = Number.parseFloat(getComputedStyle(element).maxHeight) || 150;
620
+ const height = Math.min(element.scrollHeight, maxHeight);
621
+ element.style.height = `${height}px`;
622
+ element.style.overflowY = element.scrollHeight > maxHeight ? "auto" : "hidden";
623
+ }, [ref, value]);
624
+ }
625
+
567
626
  // src/composer.jsx
568
- import { jsx, jsxs } from "preact/jsx-runtime";
627
+ import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
569
628
  var composerMemory = /* @__PURE__ */ new Map();
570
629
  function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
571
630
  if (state.mode !== "mirror" || state.canSend) return null;
@@ -575,16 +634,16 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
575
634
  const resume = canContinueHere(state);
576
635
  const join = state.canAttach;
577
636
  const branch = state.canBranch;
578
- if (!resume && !join && !branch) return /* @__PURE__ */ jsx("div", { class: "scui-continuation", children: /* @__PURE__ */ jsxs("span", { children: [
579
- /* @__PURE__ */ jsx("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
580
- /* @__PURE__ */ jsx("small", { children: "This session cannot be continued by an available harness." })
637
+ if (!resume && !join && !branch) return /* @__PURE__ */ jsx2("div", { class: "scui-continuation", children: /* @__PURE__ */ jsxs2("span", { children: [
638
+ /* @__PURE__ */ jsx2("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
639
+ /* @__PURE__ */ jsx2("small", { children: "This session cannot be continued by an available harness." })
581
640
  ] }) });
582
- return /* @__PURE__ */ jsxs("div", { class: "scui-continuation", children: [
583
- /* @__PURE__ */ jsxs("span", { children: [
584
- /* @__PURE__ */ jsx("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
585
- /* @__PURE__ */ jsx("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
641
+ return /* @__PURE__ */ jsxs2("div", { class: "scui-continuation", children: [
642
+ /* @__PURE__ */ jsxs2("span", { children: [
643
+ /* @__PURE__ */ jsx2("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
644
+ /* @__PURE__ */ jsx2("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
586
645
  ] }),
587
- /* @__PURE__ */ jsx("button", { type: "button", disabled: Boolean(state.operation), onClick: () => adapter.onIntent(join ? { action: "join" } : resume ? { action: "resume" } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
646
+ /* @__PURE__ */ jsx2("button", { type: "button", disabled: Boolean(state.operation), onClick: () => adapter.onIntent(join ? { action: "join" } : resume ? { action: "resume" } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
588
647
  ] });
589
648
  }
590
649
  function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
@@ -593,6 +652,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
593
652
  const [queue, setQueue] = useState(remembered.queue);
594
653
  const [dispatching, setDispatching] = useState(false);
595
654
  const textarea = useRef(null);
655
+ useAutosizeTextarea(textarea, draft);
596
656
  const remember = (nextDraft, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, queue: nextQueue });
597
657
  const updateQueue = (update) => setQueue((items) => {
598
658
  const next = update(items);
@@ -640,19 +700,19 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
640
700
  setDraft("");
641
701
  remember("", queuesNewMessage ? [...queue, text] : queue);
642
702
  };
643
- return /* @__PURE__ */ jsxs("div", { class: "scui-compose", children: [
644
- queue.length ? /* @__PURE__ */ jsxs("div", { class: "scui-queue", children: [
645
- /* @__PURE__ */ jsxs("strong", { children: [
703
+ return /* @__PURE__ */ jsxs2("div", { class: "scui-compose", children: [
704
+ queue.length ? /* @__PURE__ */ jsxs2("div", { class: "scui-queue", children: [
705
+ /* @__PURE__ */ jsxs2("strong", { children: [
646
706
  queue.length,
647
707
  " queued"
648
708
  ] }),
649
- queue.map((item, index) => /* @__PURE__ */ jsxs("span", { children: [
709
+ queue.map((item, index) => /* @__PURE__ */ jsxs2("span", { children: [
650
710
  item,
651
- /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `Remove queued message ${index + 1}`, onClick: () => updateQueue((items) => items.filter((_, itemIndex) => itemIndex !== index)), children: "\xD7" })
711
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Remove queued message ${index + 1}`, onClick: () => updateQueue((items) => items.filter((_, itemIndex) => itemIndex !== index)), children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 13 }) })
652
712
  ] }, `${index}:${item}`))
653
713
  ] }) : null,
654
- /* @__PURE__ */ jsxs("div", { class: "scui-envelope", children: [
655
- /* @__PURE__ */ jsx("textarea", { ref: textarea, rows: 1, "aria-label": `Message ${harnessDisplayName(state.harness) || "agent"}`, placeholder: state.startup !== "ready" ? "Connecting\u2026" : pendingStatus === "failed" ? "Retry or edit the unsent message\u2026" : pendingStatus === "editing" ? "Edit and resend\u2026" : queueBlocked ? "Queue a follow-up\u2026" : labels.askAgent, value: draft, disabled: state.mode !== "control" && !state.canSend, onInput: (event) => {
714
+ /* @__PURE__ */ jsxs2("div", { class: "scui-envelope", children: [
715
+ /* @__PURE__ */ jsx2("textarea", { ref: textarea, rows: 1, "aria-label": `Message ${harnessDisplayName(state.harness) || "agent"}`, placeholder: state.startup !== "ready" ? "Connecting\u2026" : pendingStatus === "failed" ? "Retry or edit the unsent message\u2026" : pendingStatus === "editing" ? "Edit and resend\u2026" : queueBlocked ? "Queue a follow-up\u2026" : labels.askAgent, value: draft, disabled: state.mode !== "control" && !state.canSend, onInput: (event) => {
656
716
  const value = event.currentTarget.value;
657
717
  setDraft(value);
658
718
  remember(value, queue);
@@ -662,35 +722,113 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
662
722
  send();
663
723
  }
664
724
  } }),
665
- /* @__PURE__ */ jsxs("span", { children: [
666
- state.busy ? /* @__PURE__ */ jsx("button", { class: "scui-stop", type: "button", "aria-label": "Stop agent", disabled: !state.canInterrupt, onClick: () => adapter.onIntent({ action: "interrupt" }), children: "\u25A0" }) : null,
667
- /* @__PURE__ */ jsx("button", { class: "scui-send", type: "button", "aria-label": queuesNewMessage ? "Queue message" : "Send message", disabled: !draft.trim() || !queuesNewMessage && !state.canSend, onClick: send, children: queuesNewMessage ? "+" : "\u2191" })
725
+ /* @__PURE__ */ jsxs2("span", { children: [
726
+ state.busy ? /* @__PURE__ */ jsx2("button", { class: "scui-stop", type: "button", "aria-label": "Stop agent", disabled: !state.canInterrupt, onClick: () => adapter.onIntent({ action: "interrupt" }), children: /* @__PURE__ */ jsx2(UiIcon, { name: "stop", size: 15 }) }) : null,
727
+ /* @__PURE__ */ jsx2("button", { class: "scui-send", type: "button", "aria-label": queuesNewMessage ? "Queue message" : "Send message", disabled: !draft.trim() || !queuesNewMessage && !state.canSend, onClick: send, children: /* @__PURE__ */ jsx2(UiIcon, { name: queuesNewMessage ? "plus" : "send", size: 17 }) })
668
728
  ] })
669
729
  ] })
670
730
  ] });
671
731
  }
672
732
 
673
733
  // src/conversation.jsx
674
- import { useEffect as useEffect2, useId, useLayoutEffect, useRef as useRef2, useState as useState2 } from "preact/hooks";
734
+ import { Fragment as Fragment2 } from "preact";
735
+ import { useEffect as useEffect3, useId, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState2 } from "preact/hooks";
675
736
 
676
737
  // src/markdown.jsx
677
738
  import MarkdownIt from "markdown-it";
678
- import { useMemo } from "preact/hooks";
679
- import { jsx as jsx2 } from "preact/jsx-runtime";
739
+ import { useEffect as useEffect2, useMemo, useRef as useRef2 } from "preact/hooks";
740
+ import { jsx as jsx3 } from "preact/jsx-runtime";
680
741
  var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
742
+ var LANGUAGE_LABELS = {
743
+ bash: "Shell",
744
+ css: "CSS",
745
+ html: "HTML",
746
+ js: "JavaScript",
747
+ javascript: "JavaScript",
748
+ jsx: "JSX",
749
+ md: "Markdown",
750
+ markdown: "Markdown",
751
+ py: "Python",
752
+ python: "Python",
753
+ rs: "Rust",
754
+ rust: "Rust",
755
+ sh: "Shell",
756
+ shell: "Shell",
757
+ ts: "TypeScript",
758
+ tsx: "TSX",
759
+ yaml: "YAML",
760
+ yml: "YAML",
761
+ json: "JSON",
762
+ jsonc: "JSONC",
763
+ sql: "SQL",
764
+ xml: "XML"
765
+ };
766
+ var COPY_ICON = '<svg viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5" y="5" width="8" height="8" rx="1.5"></rect><path d="M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25"></path></svg>';
767
+ function languageLabel(info) {
768
+ const language = info.trim().split(/\s+/, 1)[0]?.toLocaleLowerCase() ?? "";
769
+ if (!language) return "Code";
770
+ return LANGUAGE_LABELS[language] ?? language.toLocaleUpperCase();
771
+ }
772
+ function frameCode(render, tokens, index, options, env, self) {
773
+ const label = markdown.utils.escapeHtml(languageLabel(tokens[index]?.info ?? ""));
774
+ return `<div class="scui-code-block"><div class="scui-code-head"><span>${label}</span><button class="scui-code-copy" type="button" aria-label="Copy code" title="Copy code">${COPY_ICON}<span>Copy</span></button></div>${render(tokens, index, options, env, self)}</div>`;
775
+ }
776
+ for (const kind of ["fence", "code_block"]) {
777
+ const render = markdown.renderer.rules[kind];
778
+ markdown.renderer.rules[kind] = (tokens, index, options, env, self) => frameCode(render, tokens, index, options, env, self);
779
+ }
681
780
  var defaultLinkOpen = markdown.renderer.rules.link_open;
682
781
  markdown.renderer.rules.link_open = (tokens, index, options, env, self) => {
683
782
  tokens[index]?.attrSet("target", "_blank");
684
783
  tokens[index]?.attrSet("rel", "noreferrer noopener");
685
784
  return defaultLinkOpen ? defaultLinkOpen(tokens, index, options, env, self) : self.renderToken(tokens, index, options);
686
785
  };
687
- function Markdown({ value }) {
786
+ function setCopyState(button, status) {
787
+ const labels = {
788
+ idle: ["Copy code", "Copy"],
789
+ copied: ["Code copied", "Copied"],
790
+ failed: ["Copy failed \xB7 retry", "Retry"]
791
+ };
792
+ const [label, visible] = labels[status];
793
+ button.dataset.status = status;
794
+ button.setAttribute("aria-label", label);
795
+ button.setAttribute("title", label);
796
+ const text = button.querySelector("span");
797
+ if (text) text.textContent = visible;
798
+ }
799
+ function Markdown({ value, copyText }) {
688
800
  const html = useMemo(() => markdown.render(value), [value]);
689
- return /* @__PURE__ */ jsx2("div", { class: "scui-markdown", dangerouslySetInnerHTML: { __html: html } });
801
+ const resets = useRef2(/* @__PURE__ */ new Map());
802
+ useEffect2(() => () => {
803
+ for (const timer of resets.current.values()) clearTimeout(timer);
804
+ resets.current.clear();
805
+ }, []);
806
+ const copyCode = async (event) => {
807
+ const button = event.target.closest?.(".scui-code-copy");
808
+ if (!button || !event.currentTarget.contains(button) || !copyText) return;
809
+ const code = button.closest(".scui-code-block")?.querySelector("pre code");
810
+ if (!code) return;
811
+ button.disabled = true;
812
+ try {
813
+ await copyText(code.textContent ?? "");
814
+ setCopyState(button, "copied");
815
+ } catch {
816
+ setCopyState(button, "failed");
817
+ } finally {
818
+ button.disabled = false;
819
+ }
820
+ clearTimeout(resets.current.get(button));
821
+ const timer = setTimeout(() => {
822
+ if (button.isConnected) setCopyState(button, "idle");
823
+ resets.current.delete(button);
824
+ }, 1500);
825
+ resets.current.set(button, timer);
826
+ };
827
+ return /* @__PURE__ */ jsx3("div", { class: "scui-markdown", "data-copyable": Boolean(copyText), onClick: copyCode, dangerouslySetInnerHTML: { __html: html } });
690
828
  }
691
829
 
692
830
  // src/conversation.jsx
693
- import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
831
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs3 } from "preact/jsx-runtime";
694
832
  function LoadingStatus({ state, compact = false }) {
695
833
  const copy = {
696
834
  connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
@@ -698,81 +836,104 @@ function LoadingStatus({ state, compact = false }) {
698
836
  discovering: ["Loading recent sessions", "Scanning native session stores without loading full transcripts.", 2],
699
837
  ready: ["Ready", "Coding sessions are up to date.", 3]
700
838
  }[state.startup];
701
- return /* @__PURE__ */ jsxs2("div", { class: `scui-loading${compact ? " scui-loading-compact" : ""}`, role: "status", "aria-busy": state.startup !== "ready", children: [
702
- /* @__PURE__ */ jsx3("span", { class: "scui-orbit", "aria-hidden": "true", children: /* @__PURE__ */ jsx3("i", {}) }),
703
- /* @__PURE__ */ jsxs2("span", { class: "scui-loading-copy", children: [
704
- /* @__PURE__ */ jsx3("strong", { children: copy[0] }),
705
- /* @__PURE__ */ jsx3("small", { children: copy[1] })
839
+ return /* @__PURE__ */ jsxs3("div", { class: `scui-loading${compact ? " scui-loading-compact" : ""}`, role: "status", "aria-busy": state.startup !== "ready", children: [
840
+ /* @__PURE__ */ jsx4("span", { class: "scui-orbit", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("i", {}) }),
841
+ /* @__PURE__ */ jsxs3("span", { class: "scui-loading-copy", children: [
842
+ /* @__PURE__ */ jsx4("strong", { children: copy[0] }),
843
+ /* @__PURE__ */ jsx4("small", { children: copy[1] })
706
844
  ] }),
707
- /* @__PURE__ */ jsx3("span", { class: "scui-progress", "aria-hidden": "true", children: [1, 2, 3].map((step) => /* @__PURE__ */ jsx3("i", { "data-progress": step <= copy[2] ? "done" : step === copy[2] + 1 ? "current" : "waiting" }, step)) })
845
+ /* @__PURE__ */ jsx4("span", { class: "scui-progress", "aria-hidden": "true", children: [1, 2, 3].map((step) => /* @__PURE__ */ jsx4("i", { "data-progress": step <= copy[2] ? "done" : step === copy[2] + 1 ? "current" : "waiting" }, step)) })
708
846
  ] });
709
847
  }
710
848
  function RequestCard({ entry, adapter, canRespond }) {
711
849
  const request = entry.request;
712
850
  if (!request) return null;
713
851
  if (request.status === "responded") {
714
- return /* @__PURE__ */ jsxs2("div", { class: "scui-request-done", children: [
852
+ return /* @__PURE__ */ jsxs3("div", { class: "scui-request-done", children: [
715
853
  "\u2713 Request answered \xB7 ",
716
854
  request.resolution?.name ?? request.requestKind
717
855
  ] });
718
856
  }
719
- return /* @__PURE__ */ jsxs2("section", { class: "scui-request", "aria-label": `${request.requestKind} needs input`, children: [
720
- /* @__PURE__ */ jsx3("strong", { children: "Agent needs input" }),
721
- /* @__PURE__ */ jsx3(Markdown, { value: request.payloadText || entry.text }),
722
- /* @__PURE__ */ jsxs2("div", { class: "scui-request-actions", children: [
723
- request.options.map((option) => /* @__PURE__ */ jsx3("button", { type: "button", disabled: !canRespond, onClick: () => adapter.onIntent({ action: "respond", requestId: request.requestId, optionId: option.optionId }), children: option.name }, option.optionId)),
724
- request.cancellable ? /* @__PURE__ */ jsx3("button", { type: "button", disabled: !canRespond, onClick: () => adapter.onIntent({ action: "respond", requestId: request.requestId, optionId: null }), children: "Cancel" }) : null
857
+ return /* @__PURE__ */ jsxs3("section", { class: "scui-request", role: "alert", "aria-label": `${request.requestKind} needs input`, children: [
858
+ /* @__PURE__ */ jsx4("strong", { children: "Agent needs input" }),
859
+ /* @__PURE__ */ jsx4(Markdown, { value: request.payloadText || entry.text, copyText: adapter?.copyText }),
860
+ /* @__PURE__ */ jsxs3("div", { class: "scui-request-actions", children: [
861
+ request.options.map((option) => /* @__PURE__ */ jsx4("button", { type: "button", disabled: !canRespond, onClick: () => adapter.onIntent({ action: "respond", requestId: request.requestId, optionId: option.optionId }), children: option.name }, option.optionId)),
862
+ request.cancellable ? /* @__PURE__ */ jsx4("button", { type: "button", disabled: !canRespond, onClick: () => adapter.onIntent({ action: "respond", requestId: request.requestId, optionId: null }), children: "Cancel" }) : null
725
863
  ] })
726
864
  ] });
727
865
  }
728
866
  function ContextDisclosure({ context }) {
729
867
  if (!context?.length) return null;
730
- return /* @__PURE__ */ jsxs2("details", { class: "scui-context", children: [
731
- /* @__PURE__ */ jsxs2("summary", { children: [
868
+ return /* @__PURE__ */ jsxs3("details", { class: "scui-context", children: [
869
+ /* @__PURE__ */ jsxs3("summary", { children: [
732
870
  "Context \xB7 ",
733
871
  context.length
734
872
  ] }),
735
- /* @__PURE__ */ jsx3("div", { children: context.map((item, index) => /* @__PURE__ */ jsxs2("p", { children: [
736
- /* @__PURE__ */ jsx3("strong", { children: item.label }),
737
- /* @__PURE__ */ jsx3("span", { children: item.detail })
873
+ /* @__PURE__ */ jsx4("div", { children: context.map((item, index) => /* @__PURE__ */ jsxs3("p", { children: [
874
+ /* @__PURE__ */ jsx4("strong", { children: item.label }),
875
+ /* @__PURE__ */ jsx4("span", { children: item.detail })
738
876
  ] }, item.id ?? index)) })
739
877
  ] });
740
878
  }
879
+ function MessageMeta({ entry, adapter }) {
880
+ const [copyState, setCopyState2] = useState2("idle");
881
+ const reset = useRef3(null);
882
+ useEffect3(() => () => clearTimeout(reset.current), []);
883
+ const date = entry.ts === null ? null : new Date(entry.ts);
884
+ const validDate = date && Number.isFinite(date.valueOf()) ? date : null;
885
+ if (!validDate && (!adapter?.copyText || !entry.text)) return null;
886
+ const copy = async () => {
887
+ try {
888
+ await adapter.copyText(entry.text);
889
+ setCopyState2("copied");
890
+ } catch {
891
+ setCopyState2("failed");
892
+ }
893
+ clearTimeout(reset.current);
894
+ reset.current = setTimeout(() => setCopyState2("idle"), 1500);
895
+ };
896
+ const copyLabel = copyState === "copied" ? "Message copied" : copyState === "failed" ? "Copy failed \xB7 retry" : "Copy message";
897
+ return /* @__PURE__ */ jsxs3("footer", { class: "scui-message-meta", children: [
898
+ validDate ? /* @__PURE__ */ jsx4("time", { dateTime: validDate.toISOString(), title: validDate.toLocaleString(), children: validDate.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) }) : null,
899
+ adapter?.copyText && entry.text ? /* @__PURE__ */ jsx4("button", { type: "button", "aria-label": copyLabel, title: copyLabel, onClick: copy, "data-status": copyState, children: /* @__PURE__ */ jsx4(UiIcon, { name: "copy", size: 13 }) }) : null
900
+ ] });
901
+ }
741
902
  var TOOL_ICONS = {
742
- read: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
743
- /* @__PURE__ */ jsx3("path", { d: "M5 2.75h7.25L15 5.5v7.75A1.75 1.75 0 0 1 13.25 15h-8.5A1.75 1.75 0 0 1 3 13.25v-8.5A2 2 0 0 1 5 2.75Z" }),
744
- /* @__PURE__ */ jsx3("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
903
+ read: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
904
+ /* @__PURE__ */ jsx4("path", { d: "M5 2.75h7.25L15 5.5v7.75A1.75 1.75 0 0 1 13.25 15h-8.5A1.75 1.75 0 0 1 3 13.25v-8.5A2 2 0 0 1 5 2.75Z" }),
905
+ /* @__PURE__ */ jsx4("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
745
906
  ] }),
746
- search: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
747
- /* @__PURE__ */ jsx3("circle", { cx: "8", cy: "8", r: "4.5" }),
748
- /* @__PURE__ */ jsx3("path", { d: "m11.5 11.5 3 3" })
907
+ search: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
908
+ /* @__PURE__ */ jsx4("circle", { cx: "8", cy: "8", r: "4.5" }),
909
+ /* @__PURE__ */ jsx4("path", { d: "m11.5 11.5 3 3" })
749
910
  ] }),
750
- edit: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
751
- /* @__PURE__ */ jsx3("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
752
- /* @__PURE__ */ jsx3("path", { d: "m10 5 3 3" })
911
+ edit: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
912
+ /* @__PURE__ */ jsx4("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
913
+ /* @__PURE__ */ jsx4("path", { d: "m10 5 3 3" })
753
914
  ] }),
754
- command: () => /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
755
- test: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
756
- /* @__PURE__ */ jsx3("path", { d: "M6 2.5v3L3 12a2 2 0 0 0 1.8 3h8.4a2 2 0 0 0 1.8-3l-3-6.5v-3M5 9h8" }),
757
- /* @__PURE__ */ jsx3("path", { d: "M5 2.5h8" })
915
+ command: () => /* @__PURE__ */ jsx4(Fragment3, { children: /* @__PURE__ */ jsx4("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
916
+ test: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
917
+ /* @__PURE__ */ jsx4("path", { d: "M6 2.5v3L3 12a2 2 0 0 0 1.8 3h8.4a2 2 0 0 0 1.8-3l-3-6.5v-3M5 9h8" }),
918
+ /* @__PURE__ */ jsx4("path", { d: "M5 2.5h8" })
758
919
  ] }),
759
- web: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
760
- /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "6.5" }),
761
- /* @__PURE__ */ jsx3("path", { d: "M2.75 9h12.5M9 2.5c2 1.8 3 4 3 6.5s-1 4.7-3 6.5c-2-1.8-3-4-3-6.5s1-4.7 3-6.5Z" })
920
+ web: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
921
+ /* @__PURE__ */ jsx4("circle", { cx: "9", cy: "9", r: "6.5" }),
922
+ /* @__PURE__ */ jsx4("path", { d: "M2.75 9h12.5M9 2.5c2 1.8 3 4 3 6.5s-1 4.7-3 6.5c-2-1.8-3-4-3-6.5s1-4.7 3-6.5Z" })
762
923
  ] }),
763
- agent: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
764
- /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "6", r: "2.5" }),
765
- /* @__PURE__ */ jsx3("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
924
+ agent: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
925
+ /* @__PURE__ */ jsx4("circle", { cx: "9", cy: "6", r: "2.5" }),
926
+ /* @__PURE__ */ jsx4("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
766
927
  ] }),
767
- plan: () => /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3("path", { d: "m3 5 1 1 2-2M3 9l1 1 2-2M3 13l1 1 2-2M8 5h7M8 9h7M8 13h7" }) }),
768
- other: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
769
- /* @__PURE__ */ jsx3("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
770
- /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "3.5" })
928
+ plan: () => /* @__PURE__ */ jsx4(Fragment3, { children: /* @__PURE__ */ jsx4("path", { d: "m3 5 1 1 2-2M3 9l1 1 2-2M3 13l1 1 2-2M8 5h7M8 9h7M8 13h7" }) }),
929
+ other: () => /* @__PURE__ */ jsxs3(Fragment3, { children: [
930
+ /* @__PURE__ */ jsx4("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
931
+ /* @__PURE__ */ jsx4("circle", { cx: "9", cy: "9", r: "3.5" })
771
932
  ] })
772
933
  };
773
934
  function ToolIcon({ category }) {
774
935
  const Glyph = TOOL_ICONS[category] ?? TOOL_ICONS.other;
775
- return /* @__PURE__ */ jsx3("svg", { class: "scui-tool-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.35", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(Glyph, {}) });
936
+ return /* @__PURE__ */ jsx4("svg", { class: "scui-tool-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.35", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx4(Glyph, {}) });
776
937
  }
777
938
  function toolAction2(entry, category, presentation) {
778
939
  if (presentation?.action) return presentation.action;
@@ -827,86 +988,86 @@ function ToolMetrics({ presentation }) {
827
988
  presentation.exitCode === null ? "" : `exit ${presentation.exitCode}`,
828
989
  formatDuration(presentation.durationMs)
829
990
  ].filter(Boolean);
830
- return metrics.length ? /* @__PURE__ */ jsx3("div", { class: "scui-tool-metrics", children: metrics.map((metric) => /* @__PURE__ */ jsx3("span", { children: metric }, metric)) }) : null;
991
+ return metrics.length ? /* @__PURE__ */ jsx4("div", { class: "scui-tool-metrics", children: metrics.map((metric) => /* @__PURE__ */ jsx4("span", { children: metric }, metric)) }) : null;
831
992
  }
832
993
  function PendingElapsed({ now }) {
833
994
  const clock = now ?? Date.now;
834
- const started = useRef2(clock());
995
+ const started = useRef3(clock());
835
996
  const [elapsed, setElapsed] = useState2(0);
836
- useEffect2(() => {
997
+ useEffect3(() => {
837
998
  const timer = setInterval(() => setElapsed(Math.max(0, clock() - started.current)), 1e3);
838
999
  return () => clearInterval(timer);
839
1000
  }, [clock]);
840
- return /* @__PURE__ */ jsx3("small", { class: "scui-tool-elapsed", children: elapsed < 1e3 ? "now" : formatDuration(elapsed) });
1001
+ return /* @__PURE__ */ jsx4("small", { class: "scui-tool-elapsed", children: elapsed < 1e3 ? "now" : formatDuration(elapsed) });
841
1002
  }
842
1003
  function LinePreview({ value, kind }) {
843
1004
  if (!value) return null;
844
- return /* @__PURE__ */ jsx3("ol", { class: "scui-code-preview", "data-kind": kind, children: stripAnsi(value).split("\n").map((line, index) => {
1005
+ return /* @__PURE__ */ jsx4("ol", { class: "scui-code-preview", "data-kind": kind, children: stripAnsi(value).split("\n").map((line, index) => {
845
1006
  const tone = kind === "diff" ? line.startsWith("+") && !line.startsWith("+++") ? "add" : line.startsWith("-") && !line.startsWith("---") ? "remove" : line.startsWith("@@") ? "hunk" : "plain" : "plain";
846
- return /* @__PURE__ */ jsxs2("li", { "data-tone": tone, children: [
847
- /* @__PURE__ */ jsx3("span", { children: index + 1 }),
848
- /* @__PURE__ */ jsx3("code", { children: line || " " })
1007
+ return /* @__PURE__ */ jsxs3("li", { "data-tone": tone, children: [
1008
+ /* @__PURE__ */ jsx4("span", { children: index + 1 }),
1009
+ /* @__PURE__ */ jsx4("code", { children: line || " " })
849
1010
  ] }, index);
850
1011
  }) });
851
1012
  }
852
1013
  function TerminalPreview({ presentation, pending, failed }) {
853
- return /* @__PURE__ */ jsxs2("section", { class: "scui-terminal", children: [
854
- /* @__PURE__ */ jsxs2("header", { children: [
855
- /* @__PURE__ */ jsxs2("span", { "aria-hidden": "true", children: [
856
- /* @__PURE__ */ jsx3("i", {}),
857
- /* @__PURE__ */ jsx3("i", {}),
858
- /* @__PURE__ */ jsx3("i", {})
1014
+ return /* @__PURE__ */ jsxs3("section", { class: "scui-terminal", children: [
1015
+ /* @__PURE__ */ jsxs3("header", { children: [
1016
+ /* @__PURE__ */ jsxs3("span", { "aria-hidden": "true", children: [
1017
+ /* @__PURE__ */ jsx4("i", {}),
1018
+ /* @__PURE__ */ jsx4("i", {}),
1019
+ /* @__PURE__ */ jsx4("i", {})
859
1020
  ] }),
860
- /* @__PURE__ */ jsx3("code", { children: presentation.command ? `$ ${presentation.command}` : "Terminal" })
1021
+ /* @__PURE__ */ jsx4("code", { children: presentation.command ? `$ ${presentation.command}` : "Terminal" })
861
1022
  ] }),
862
- presentation.preview ? /* @__PURE__ */ jsx3("pre", { "data-error": failed, children: stripAnsi(presentation.preview) }) : pending ? /* @__PURE__ */ jsxs2("div", { class: "scui-terminal-wait", children: [
863
- /* @__PURE__ */ jsx3("i", {}),
1023
+ presentation.preview ? /* @__PURE__ */ jsx4("pre", { "data-error": failed, children: stripAnsi(presentation.preview) }) : pending ? /* @__PURE__ */ jsxs3("div", { class: "scui-terminal-wait", children: [
1024
+ /* @__PURE__ */ jsx4("i", {}),
864
1025
  " Waiting for output"
865
- ] }) : /* @__PURE__ */ jsx3("div", { class: "scui-terminal-empty", children: "No output" })
1026
+ ] }) : /* @__PURE__ */ jsx4("div", { class: "scui-terminal-empty", children: "No output" })
866
1027
  ] });
867
1028
  }
868
1029
  function SearchPreview({ presentation }) {
869
1030
  const lines = stripAnsi(presentation.preview).split("\n").filter(Boolean);
870
- return /* @__PURE__ */ jsxs2("section", { class: "scui-search-preview", children: [
871
- presentation.query ? /* @__PURE__ */ jsxs2("header", { children: [
872
- /* @__PURE__ */ jsx3("span", { children: "Search" }),
873
- /* @__PURE__ */ jsx3("code", { children: presentation.query })
1031
+ return /* @__PURE__ */ jsxs3("section", { class: "scui-search-preview", children: [
1032
+ presentation.query ? /* @__PURE__ */ jsxs3("header", { children: [
1033
+ /* @__PURE__ */ jsx4("span", { children: "Search" }),
1034
+ /* @__PURE__ */ jsx4("code", { children: presentation.query })
874
1035
  ] }) : null,
875
- lines.length ? /* @__PURE__ */ jsx3("ol", { children: lines.map((line, index) => {
1036
+ lines.length ? /* @__PURE__ */ jsx4("ol", { children: lines.map((line, index) => {
876
1037
  const match = /^(.*?):(\d+)(?::(\d+))?:(.*)$/.exec(line);
877
- return /* @__PURE__ */ jsx3("li", { children: match ? /* @__PURE__ */ jsxs2(Fragment, { children: [
878
- /* @__PURE__ */ jsx3("code", { children: match[1] }),
879
- /* @__PURE__ */ jsxs2("small", { children: [
1038
+ return /* @__PURE__ */ jsx4("li", { children: match ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
1039
+ /* @__PURE__ */ jsx4("code", { children: match[1] }),
1040
+ /* @__PURE__ */ jsxs3("small", { children: [
880
1041
  match[2],
881
1042
  match[3] ? `:${match[3]}` : ""
882
1043
  ] }),
883
- /* @__PURE__ */ jsx3("span", { children: match[4] })
884
- ] }) : /* @__PURE__ */ jsx3("span", { children: line }) }, index);
885
- }) }) : /* @__PURE__ */ jsx3("p", { children: "No textual results" })
1044
+ /* @__PURE__ */ jsx4("span", { children: match[4] })
1045
+ ] }) : /* @__PURE__ */ jsx4("span", { children: line }) }, index);
1046
+ }) }) : /* @__PURE__ */ jsx4("p", { children: "No textual results" })
886
1047
  ] });
887
1048
  }
888
1049
  function ToolPreview({ presentation, entry }) {
889
1050
  const pending = entry.status === "pending";
890
1051
  const failed = entry.status === "error";
891
- if (presentation.detail === "terminal") return /* @__PURE__ */ jsx3(TerminalPreview, { presentation, pending, failed });
892
- if (presentation.detail === "diff") return presentation.preview ? /* @__PURE__ */ jsx3(LinePreview, { value: presentation.preview, kind: "diff" }) : /* @__PURE__ */ jsx3("div", { class: "scui-tool-empty", children: "Edit completed without a textual diff" });
893
- if (presentation.detail === "file") return presentation.preview ? /* @__PURE__ */ jsx3(LinePreview, { value: presentation.preview, kind: "file" }) : /* @__PURE__ */ jsx3("div", { class: "scui-tool-empty", children: "File contents were not included in this event" });
894
- if (presentation.detail === "matches") return /* @__PURE__ */ jsx3(SearchPreview, { presentation });
895
- if (presentation.detail === "web") return /* @__PURE__ */ jsxs2("section", { class: "scui-web-preview", children: [
896
- presentation.url ? /* @__PURE__ */ jsx3("code", { children: presentation.url }) : null,
897
- presentation.preview ? /* @__PURE__ */ jsx3("p", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx3("small", { children: pending ? "Waiting for the page" : "No page summary returned" })
1052
+ if (presentation.detail === "terminal") return /* @__PURE__ */ jsx4(TerminalPreview, { presentation, pending, failed });
1053
+ if (presentation.detail === "diff") return presentation.preview ? /* @__PURE__ */ jsx4(LinePreview, { value: presentation.preview, kind: "diff" }) : /* @__PURE__ */ jsx4("div", { class: "scui-tool-empty", children: "Edit completed without a textual diff" });
1054
+ if (presentation.detail === "file") return presentation.preview ? /* @__PURE__ */ jsx4(LinePreview, { value: presentation.preview, kind: "file" }) : /* @__PURE__ */ jsx4("div", { class: "scui-tool-empty", children: "File contents were not included in this event" });
1055
+ if (presentation.detail === "matches") return /* @__PURE__ */ jsx4(SearchPreview, { presentation });
1056
+ if (presentation.detail === "web") return /* @__PURE__ */ jsxs3("section", { class: "scui-web-preview", children: [
1057
+ presentation.url ? /* @__PURE__ */ jsx4("code", { children: presentation.url }) : null,
1058
+ presentation.preview ? /* @__PURE__ */ jsx4("p", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx4("small", { children: pending ? "Waiting for the page" : "No page summary returned" })
898
1059
  ] });
899
- if (presentation.detail === "agent") return /* @__PURE__ */ jsx3("section", { class: "scui-agent-preview", children: presentation.preview ? /* @__PURE__ */ jsx3("pre", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx3("small", { children: pending ? "Agent is working" : "No textual handoff returned" }) });
900
- if (presentation.detail === "plan") return /* @__PURE__ */ jsx3("ol", { class: "scui-plan-preview", children: presentation.items?.map((item, index) => /* @__PURE__ */ jsxs2("li", { "data-status": item.status, children: [
901
- /* @__PURE__ */ jsx3("i", { "aria-hidden": "true" }),
902
- /* @__PURE__ */ jsx3("span", { children: item.label })
1060
+ if (presentation.detail === "agent") return /* @__PURE__ */ jsx4("section", { class: "scui-agent-preview", children: presentation.preview ? /* @__PURE__ */ jsx4("pre", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx4("small", { children: pending ? "Agent is working" : "No textual handoff returned" }) });
1061
+ if (presentation.detail === "plan") return /* @__PURE__ */ jsx4("ol", { class: "scui-plan-preview", children: presentation.items?.map((item, index) => /* @__PURE__ */ jsxs3("li", { "data-status": item.status, children: [
1062
+ /* @__PURE__ */ jsx4("i", { "aria-hidden": "true" }),
1063
+ /* @__PURE__ */ jsx4("span", { children: item.label })
903
1064
  ] }, `${item.label}:${index}`)) });
904
- return presentation.preview ? /* @__PURE__ */ jsx3("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
1065
+ return presentation.preview ? /* @__PURE__ */ jsx4("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
905
1066
  }
906
1067
  function ToolActions({ presentation, adapter }) {
907
1068
  const [copied, setCopied] = useState2(false);
908
- const reset = useRef2(null);
909
- useEffect2(() => () => clearTimeout(reset.current), []);
1069
+ const reset = useRef3(null);
1070
+ useEffect3(() => () => clearTimeout(reset.current), []);
910
1071
  if (!adapter?.copyText) return null;
911
1072
  const action = presentation.command ? ["Copy command", presentation.command] : presentation.path ? ["Copy path", presentation.path] : presentation.url ? ["Copy URL", presentation.url] : presentation.query ? ["Copy query", presentation.query] : null;
912
1073
  const copy = async () => {
@@ -915,27 +1076,27 @@ function ToolActions({ presentation, adapter }) {
915
1076
  clearTimeout(reset.current);
916
1077
  reset.current = setTimeout(() => setCopied(false), 1500);
917
1078
  };
918
- return action ? /* @__PURE__ */ jsx3("div", { class: "scui-tool-actions", children: /* @__PURE__ */ jsx3("button", { type: "button", onClick: copy, children: copied ? "Copied" : action[0] }) }) : null;
1079
+ return action ? /* @__PURE__ */ jsx4("div", { class: "scui-tool-actions", children: /* @__PURE__ */ jsx4("button", { type: "button", onClick: copy, children: copied ? "Copied" : action[0] }) }) : null;
919
1080
  }
920
1081
  function ToolStack({ tools }) {
921
1082
  if (tools.length < 2) return null;
922
- return /* @__PURE__ */ jsx3("div", { class: "scui-tool-stack", "aria-label": "Coordinated tools", children: tools.map((tool) => /* @__PURE__ */ jsx3("span", { children: tool.replaceAll("__", " \xB7 ").replaceAll("_", " ") }, tool)) });
1083
+ return /* @__PURE__ */ jsx4("div", { class: "scui-tool-stack", "aria-label": "Coordinated tools", children: tools.map((tool) => /* @__PURE__ */ jsx4("span", { children: tool.replaceAll("__", " \xB7 ").replaceAll("_", " ") }, tool)) });
923
1084
  }
924
1085
  function TechnicalDetails({ entry }) {
925
1086
  if (!entry.arguments && !entry.resultText) return null;
926
- return /* @__PURE__ */ jsxs2("details", { class: "scui-tool-technical", children: [
927
- /* @__PURE__ */ jsx3("summary", { children: "Technical details" }),
928
- /* @__PURE__ */ jsxs2("div", { children: [
929
- entry.arguments ? /* @__PURE__ */ jsxs2("section", { children: [
930
- /* @__PURE__ */ jsx3("strong", { children: "Native arguments" }),
931
- /* @__PURE__ */ jsx3("dl", { children: argumentRows(entry.arguments).map((row) => /* @__PURE__ */ jsxs2("div", { children: [
932
- /* @__PURE__ */ jsx3("dt", { children: row.label }),
933
- /* @__PURE__ */ jsx3("dd", { children: /* @__PURE__ */ jsx3("pre", { children: row.value }) })
1087
+ return /* @__PURE__ */ jsxs3("details", { class: "scui-tool-technical", children: [
1088
+ /* @__PURE__ */ jsx4("summary", { children: "Technical details" }),
1089
+ /* @__PURE__ */ jsxs3("div", { children: [
1090
+ entry.arguments ? /* @__PURE__ */ jsxs3("section", { children: [
1091
+ /* @__PURE__ */ jsx4("strong", { children: "Native arguments" }),
1092
+ /* @__PURE__ */ jsx4("dl", { children: argumentRows(entry.arguments).map((row) => /* @__PURE__ */ jsxs3("div", { children: [
1093
+ /* @__PURE__ */ jsx4("dt", { children: row.label }),
1094
+ /* @__PURE__ */ jsx4("dd", { children: /* @__PURE__ */ jsx4("pre", { children: row.value }) })
934
1095
  ] }, row.key)) })
935
1096
  ] }) : null,
936
- entry.resultText ? /* @__PURE__ */ jsxs2("section", { children: [
937
- /* @__PURE__ */ jsx3("strong", { children: "Native result" }),
938
- /* @__PURE__ */ jsxs2("pre", { "data-error": entry.status === "error", children: [
1097
+ entry.resultText ? /* @__PURE__ */ jsxs3("section", { children: [
1098
+ /* @__PURE__ */ jsx4("strong", { children: "Native result" }),
1099
+ /* @__PURE__ */ jsxs3("pre", { "data-error": entry.status === "error", children: [
939
1100
  entry.resultText,
940
1101
  entry.truncated ? "\n[truncated]" : ""
941
1102
  ] })
@@ -944,125 +1105,126 @@ function TechnicalDetails({ entry }) {
944
1105
  ] });
945
1106
  }
946
1107
  function TranscriptEntry({ entry, state, adapter }) {
947
- if (entry.role === "request") return /* @__PURE__ */ jsx3(RequestCard, { entry, adapter, canRespond: state.canRespond });
1108
+ if (entry.role === "request") return /* @__PURE__ */ jsx4(RequestCard, { entry, adapter, canRespond: state.canRespond });
948
1109
  if (entry.role === "reasoning") {
949
- return /* @__PURE__ */ jsxs2("details", { class: "scui-reasoning", open: entry.streaming, children: [
950
- /* @__PURE__ */ jsx3("summary", { children: entry.streaming ? "Reasoning\u2026" : "Reasoning" }),
951
- /* @__PURE__ */ jsx3(Markdown, { value: entry.text })
1110
+ return /* @__PURE__ */ jsxs3("details", { class: "scui-reasoning", open: entry.streaming, children: [
1111
+ /* @__PURE__ */ jsx4("summary", { children: entry.streaming ? "Reasoning\u2026" : "Reasoning" }),
1112
+ /* @__PURE__ */ jsx4(Markdown, { value: entry.text, copyText: adapter?.copyText })
952
1113
  ] });
953
1114
  }
954
- if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */ jsx3("div", { class: "scui-notice", "data-code": entry.code, children: entry.text });
955
- return /* @__PURE__ */ jsxs2("article", { class: "scui-message", "data-role": entry.role, children: [
956
- /* @__PURE__ */ jsx3(Markdown, { value: entry.text }),
957
- /* @__PURE__ */ jsx3(ContextDisclosure, { context: entry.context }),
958
- entry.truncated ? /* @__PURE__ */ jsx3("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null
1115
+ if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */ jsx4("div", { class: "scui-notice", "data-code": entry.code, children: entry.text });
1116
+ return /* @__PURE__ */ jsxs3("article", { class: "scui-message", "data-role": entry.role, "aria-label": `${entry.role === "user" ? "Your" : "Assistant"} message`, children: [
1117
+ /* @__PURE__ */ jsx4(Markdown, { value: entry.text, copyText: adapter?.copyText }),
1118
+ /* @__PURE__ */ jsx4(ContextDisclosure, { context: entry.context }),
1119
+ entry.truncated ? /* @__PURE__ */ jsx4("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null,
1120
+ /* @__PURE__ */ jsx4(MessageMeta, { entry, adapter })
959
1121
  ] });
960
1122
  }
961
1123
  function ToolRow({ entry, workspace, open = false, adapter }) {
962
1124
  const presentation = entry.presentation ?? createToolPresentation(entry);
963
1125
  const [expanded, setExpanded] = useState2(open || entry.status === "pending");
964
- useEffect2(() => {
1126
+ useEffect3(() => {
965
1127
  if (entry.status === "pending") setExpanded(true);
966
1128
  }, [entry.status]);
967
1129
  const target = compactToolTarget(presentation.target, workspace);
968
1130
  const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
969
1131
  const category = presentation.category ?? toolCategory(entry);
970
- const summary = /* @__PURE__ */ jsxs2(Fragment, { children: [
971
- /* @__PURE__ */ jsx3(ToolIcon, { category }),
972
- /* @__PURE__ */ jsx3("strong", { children: toolAction2(entry, category, presentation) }),
973
- target ? /* @__PURE__ */ jsx3("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
974
- /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
975
- entry.status === "pending" ? /* @__PURE__ */ jsx3(PendingElapsed, { now: adapter?.now }) : null,
976
- /* @__PURE__ */ jsx3("span", { class: "scui-tool-status", role: "status", "data-status": entry.status ?? "completed", "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? /* @__PURE__ */ jsx3("i", {}) : entry.status === "error" ? "\xD7" : "\u2713" }),
977
- hasDetail ? /* @__PURE__ */ jsx3("span", { class: "scui-tool-chevron", "aria-hidden": "true", children: "\u203A" }) : null
1132
+ const summary = /* @__PURE__ */ jsxs3(Fragment3, { children: [
1133
+ /* @__PURE__ */ jsx4(ToolIcon, { category }),
1134
+ /* @__PURE__ */ jsx4("strong", { children: toolAction2(entry, category, presentation) }),
1135
+ target ? /* @__PURE__ */ jsx4("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
1136
+ /* @__PURE__ */ jsx4("span", { class: "scui-spacer" }),
1137
+ entry.status === "pending" ? /* @__PURE__ */ jsx4(PendingElapsed, { now: adapter?.now }) : null,
1138
+ /* @__PURE__ */ jsx4("span", { class: "scui-tool-status", role: "status", "data-status": entry.status ?? "completed", "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? /* @__PURE__ */ jsx4("i", {}) : /* @__PURE__ */ jsx4(UiIcon, { name: entry.status === "error" ? "close" : "check", size: 12 }) }),
1139
+ hasDetail ? /* @__PURE__ */ jsx4(UiIcon, { name: "chevron", size: 14, class: "scui-tool-chevron" }) : null
978
1140
  ] });
979
- if (!hasDetail) return /* @__PURE__ */ jsx3("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: /* @__PURE__ */ jsx3("div", { class: "scui-tool-head", children: summary }) });
980
- return /* @__PURE__ */ jsxs2("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, open: expanded, onToggle: (event) => setExpanded(event.currentTarget.open), children: [
981
- /* @__PURE__ */ jsx3("summary", { class: "scui-tool-head", children: summary }),
982
- /* @__PURE__ */ jsxs2("div", { class: "scui-tool-detail", children: [
983
- /* @__PURE__ */ jsx3(ToolMetrics, { presentation }),
984
- /* @__PURE__ */ jsx3(ToolStack, { tools: presentation.tools ?? [] }),
985
- /* @__PURE__ */ jsx3(ToolPreview, { presentation, entry }),
986
- presentation.fields.length ? /* @__PURE__ */ jsx3("dl", { class: "scui-tool-fields", children: presentation.fields.map((field) => /* @__PURE__ */ jsxs2("div", { children: [
987
- /* @__PURE__ */ jsx3("dt", { children: field.label }),
988
- /* @__PURE__ */ jsx3("dd", { children: field.value })
1141
+ if (!hasDetail) return /* @__PURE__ */ jsx4("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: /* @__PURE__ */ jsx4("div", { class: "scui-tool-head", children: summary }) });
1142
+ return /* @__PURE__ */ jsxs3("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, open: expanded, onToggle: (event) => setExpanded(event.currentTarget.open), children: [
1143
+ /* @__PURE__ */ jsx4("summary", { class: "scui-tool-head", children: summary }),
1144
+ /* @__PURE__ */ jsxs3("div", { class: "scui-tool-detail", children: [
1145
+ /* @__PURE__ */ jsx4(ToolMetrics, { presentation }),
1146
+ /* @__PURE__ */ jsx4(ToolStack, { tools: presentation.tools ?? [] }),
1147
+ /* @__PURE__ */ jsx4(ToolPreview, { presentation, entry }),
1148
+ presentation.fields.length ? /* @__PURE__ */ jsx4("dl", { class: "scui-tool-fields", children: presentation.fields.map((field) => /* @__PURE__ */ jsxs3("div", { children: [
1149
+ /* @__PURE__ */ jsx4("dt", { children: field.label }),
1150
+ /* @__PURE__ */ jsx4("dd", { children: field.value })
989
1151
  ] }, field.label)) }) : null,
990
- /* @__PURE__ */ jsx3(ToolActions, { presentation, adapter }),
991
- /* @__PURE__ */ jsx3(TechnicalDetails, { entry })
1152
+ /* @__PURE__ */ jsx4(ToolActions, { presentation, adapter }),
1153
+ /* @__PURE__ */ jsx4(TechnicalDetails, { entry })
992
1154
  ] })
993
1155
  ] });
994
1156
  }
995
1157
  function ActivityGroup({ entries, state, adapter }) {
996
- if (entries.length === 1) return /* @__PURE__ */ jsx3("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx3(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1158
+ if (entries.length === 1) return /* @__PURE__ */ jsx4("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx4(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
997
1159
  const active = entries.some((entry) => entry.status === "pending");
998
1160
  const [open, setOpen] = useState2(active);
999
1161
  const id = useId();
1000
- useEffect2(() => {
1162
+ useEffect3(() => {
1001
1163
  if (active) setOpen(true);
1002
1164
  }, [active]);
1003
- return /* @__PURE__ */ jsxs2("section", { class: "scui-activity", children: [
1004
- /* @__PURE__ */ jsxs2("button", { class: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
1005
- /* @__PURE__ */ jsx3("span", { class: "scui-fold", "data-open": open, children: "\u203A" }),
1006
- /* @__PURE__ */ jsx3("strong", { children: activitySummary(entries) }),
1007
- /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
1008
- /* @__PURE__ */ jsxs2("small", { children: [
1165
+ return /* @__PURE__ */ jsxs3("section", { class: "scui-activity", children: [
1166
+ /* @__PURE__ */ jsxs3("button", { class: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
1167
+ /* @__PURE__ */ jsx4("span", { class: "scui-fold", "data-open": open, children: /* @__PURE__ */ jsx4(UiIcon, { name: "chevron", size: 14 }) }),
1168
+ /* @__PURE__ */ jsx4("strong", { children: activitySummary(entries) }),
1169
+ /* @__PURE__ */ jsx4("span", { class: "scui-spacer" }),
1170
+ /* @__PURE__ */ jsxs3("small", { children: [
1009
1171
  entries.filter((entry) => entry.status !== "pending").length,
1010
1172
  "/",
1011
1173
  entries.length
1012
1174
  ] })
1013
1175
  ] }),
1014
- open ? /* @__PURE__ */ jsx3("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx3(ToolRow, { entry, workspace: state.workspace, adapter }, entry.id)) }) : null
1176
+ open ? /* @__PURE__ */ jsx4("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx4(ToolRow, { entry, workspace: state.workspace, adapter }, entry.id)) }) : null
1015
1177
  ] });
1016
1178
  }
1017
1179
  function TaskPlan({ plan }) {
1018
1180
  if (!plan.items.length) return null;
1019
1181
  const complete = plan.items.filter((item) => item.status === "completed" || item.status === "cancelled").length;
1020
- return /* @__PURE__ */ jsxs2("details", { class: "scui-plan", children: [
1021
- /* @__PURE__ */ jsxs2("summary", { children: [
1022
- /* @__PURE__ */ jsx3("span", { children: "Plan" }),
1023
- /* @__PURE__ */ jsxs2("small", { children: [
1182
+ return /* @__PURE__ */ jsxs3("details", { class: "scui-plan", children: [
1183
+ /* @__PURE__ */ jsxs3("summary", { children: [
1184
+ /* @__PURE__ */ jsx4("span", { children: "Plan" }),
1185
+ /* @__PURE__ */ jsxs3("small", { children: [
1024
1186
  complete,
1025
1187
  "/",
1026
1188
  plan.items.length
1027
1189
  ] })
1028
1190
  ] }),
1029
- /* @__PURE__ */ jsx3("ol", { tabIndex: 0, "aria-label": "Task plan steps", children: plan.items.map((item) => /* @__PURE__ */ jsxs2("li", { "data-status": item.status, children: [
1030
- /* @__PURE__ */ jsx3("i", { "aria-hidden": "true" }),
1191
+ /* @__PURE__ */ jsx4("ol", { tabIndex: 0, "aria-label": "Task plan steps", children: plan.items.map((item) => /* @__PURE__ */ jsxs3("li", { "data-status": item.status, children: [
1192
+ /* @__PURE__ */ jsx4("i", { "aria-hidden": "true" }),
1031
1193
  " ",
1032
- /* @__PURE__ */ jsx3("span", { children: item.title })
1194
+ /* @__PURE__ */ jsx4("span", { children: item.title })
1033
1195
  ] }, item.id)) })
1034
1196
  ] });
1035
1197
  }
1036
1198
  function SessionDetails({ semantics }) {
1037
1199
  if (!semantics.fidelity && !semantics.residueCount && !semantics.parseErrors && !semantics.subagents.length) return null;
1038
- return /* @__PURE__ */ jsxs2("details", { class: "scui-details", children: [
1039
- /* @__PURE__ */ jsx3("summary", { children: "Session details" }),
1040
- /* @__PURE__ */ jsxs2("div", { children: [
1041
- semantics.fidelity ? /* @__PURE__ */ jsxs2("p", { children: [
1042
- /* @__PURE__ */ jsx3("strong", { children: "Fidelity" }),
1043
- /* @__PURE__ */ jsx3("span", { children: semantics.fidelity.replaceAll("_", " ") })
1200
+ return /* @__PURE__ */ jsxs3("details", { class: "scui-details", children: [
1201
+ /* @__PURE__ */ jsx4("summary", { children: "Session details" }),
1202
+ /* @__PURE__ */ jsxs3("div", { children: [
1203
+ semantics.fidelity ? /* @__PURE__ */ jsxs3("p", { children: [
1204
+ /* @__PURE__ */ jsx4("strong", { children: "Fidelity" }),
1205
+ /* @__PURE__ */ jsx4("span", { children: semantics.fidelity.replaceAll("_", " ") })
1044
1206
  ] }) : null,
1045
- /* @__PURE__ */ jsxs2("p", { children: [
1046
- /* @__PURE__ */ jsx3("strong", { children: "Native records" }),
1047
- /* @__PURE__ */ jsx3("span", { children: semantics.rawRecords })
1207
+ /* @__PURE__ */ jsxs3("p", { children: [
1208
+ /* @__PURE__ */ jsx4("strong", { children: "Native records" }),
1209
+ /* @__PURE__ */ jsx4("span", { children: semantics.rawRecords })
1048
1210
  ] }),
1049
- semantics.residueCount ? /* @__PURE__ */ jsxs2("p", { children: [
1050
- /* @__PURE__ */ jsx3("strong", { children: "Residue" }),
1051
- /* @__PURE__ */ jsxs2("span", { children: [
1211
+ semantics.residueCount ? /* @__PURE__ */ jsxs3("p", { children: [
1212
+ /* @__PURE__ */ jsx4("strong", { children: "Residue" }),
1213
+ /* @__PURE__ */ jsxs3("span", { children: [
1052
1214
  semantics.residueCount,
1053
1215
  " retained"
1054
1216
  ] })
1055
1217
  ] }) : null,
1056
- semantics.parseErrors ? /* @__PURE__ */ jsxs2("p", { children: [
1057
- /* @__PURE__ */ jsx3("strong", { children: "Parse diagnostics" }),
1058
- /* @__PURE__ */ jsx3("span", { children: semantics.parseErrors })
1218
+ semantics.parseErrors ? /* @__PURE__ */ jsxs3("p", { children: [
1219
+ /* @__PURE__ */ jsx4("strong", { children: "Parse diagnostics" }),
1220
+ /* @__PURE__ */ jsx4("span", { children: semantics.parseErrors })
1059
1221
  ] }) : null,
1060
- semantics.subagents.map((agent) => /* @__PURE__ */ jsxs2("p", { children: [
1061
- /* @__PURE__ */ jsxs2("strong", { children: [
1222
+ semantics.subagents.map((agent) => /* @__PURE__ */ jsxs3("p", { children: [
1223
+ /* @__PURE__ */ jsxs3("strong", { children: [
1062
1224
  agent.source,
1063
1225
  " subagent"
1064
1226
  ] }),
1065
- /* @__PURE__ */ jsxs2("span", { children: [
1227
+ /* @__PURE__ */ jsxs3("span", { children: [
1066
1228
  agent.messages,
1067
1229
  " messages \xB7 ",
1068
1230
  agent.fidelity.replaceAll("_", " ")
@@ -1072,13 +1234,29 @@ function SessionDetails({ semantics }) {
1072
1234
  ] });
1073
1235
  }
1074
1236
  var conversationMemory = /* @__PURE__ */ new Map();
1075
- function Conversation({ state, adapter, components = {}, slots = {}, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pending = null }) {
1076
- const scroller = useRef2(null);
1237
+ function ConversationAnnouncements({ state }) {
1238
+ const previousBusy = useRef3(state.busy);
1239
+ const [announcement, setAnnouncement] = useState2("");
1240
+ useEffect3(() => {
1241
+ if (previousBusy.current && !state.busy && !state.error) {
1242
+ setAnnouncement(`${harnessDisplayName(state.harness) || "Coding agent"} finished working`);
1243
+ }
1244
+ previousBusy.current = state.busy;
1245
+ }, [state.busy, state.error, state.harness]);
1246
+ return /* @__PURE__ */ jsx4("span", { class: "scui-sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement });
1247
+ }
1248
+ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pending = null, unreadAfterMessages = null }) {
1249
+ const scroller = useRef3(null);
1077
1250
  const remembered = conversationMemory.get(memoryKey) ?? { top: null, atBottom: true };
1078
1251
  const [atBottom, setAtBottom] = useState2(remembered.atBottom);
1079
- const earlierAnchor = useRef2(null);
1080
- const restored = useRef2(false);
1252
+ const earlierAnchor = useRef3(null);
1253
+ const restored = useRef3(false);
1081
1254
  const blocks = groupConversation(state.transcript);
1255
+ const unreadBoundary = useRef3(Number.isSafeInteger(unreadAfterMessages) && unreadAfterMessages >= 0 ? unreadAfterMessages : null);
1256
+ const unreadBlock = unreadBoundary.current === null ? -1 : blocks.findIndex((block) => {
1257
+ const entries = block.kind === "activity" ? block.entries : [block.entry];
1258
+ return entries.some((entry) => Number.isSafeInteger(entry.messageIndex) && entry.messageIndex > unreadBoundary.current);
1259
+ });
1082
1260
  const Entry = components.TranscriptEntry ?? TranscriptEntry;
1083
1261
  const Group = components.ActivityGroup ?? ActivityGroup;
1084
1262
  const Plan = components.TaskPlan ?? TaskPlan;
@@ -1093,7 +1271,7 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
1093
1271
  setAtBottom(true);
1094
1272
  remember({ top: scroller.current.scrollTop, atBottom: true });
1095
1273
  };
1096
- useLayoutEffect(() => {
1274
+ useLayoutEffect2(() => {
1097
1275
  const element = scroller.current;
1098
1276
  if (!element) return;
1099
1277
  const anchor = earlierAnchor.current;
@@ -1109,54 +1287,61 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
1109
1287
  else pin();
1110
1288
  } else if (atBottom) pin();
1111
1289
  }, [memoryKey, state.transcript, state.busy, state.operation, pendingMessage?.text, pendingMessage?.status]);
1112
- return /* @__PURE__ */ jsxs2("div", { class: "scui-conversation-wrap", children: [
1113
- /* @__PURE__ */ jsx3("div", { class: "scui-conversation", ref: scroller, tabIndex: 0, "aria-label": "Conversation", onScroll: (event) => {
1290
+ return /* @__PURE__ */ jsxs3("div", { class: "scui-conversation-wrap", children: [
1291
+ /* @__PURE__ */ jsx4(ConversationAnnouncements, { state }),
1292
+ /* @__PURE__ */ jsx4("div", { class: "scui-conversation", ref: scroller, tabIndex: 0, "aria-label": "Conversation", onScroll: (event) => {
1114
1293
  const element = event.currentTarget;
1115
1294
  const bottom = element.scrollHeight - element.scrollTop - element.clientHeight <= 64;
1116
1295
  setAtBottom(bottom);
1117
1296
  remember({ top: element.scrollTop, atBottom: bottom });
1118
- }, children: /* @__PURE__ */ jsxs2("div", { children: [
1119
- Before ? /* @__PURE__ */ jsx3(Before, { state, adapter, value: null }) : null,
1120
- /* @__PURE__ */ jsx3(Plan, { plan: state.taskPlan, value: state.taskPlan, state, adapter }),
1121
- /* @__PURE__ */ jsx3(SessionDetails, { semantics: state.semantics }),
1122
- state.history.hasEarlier ? /* @__PURE__ */ jsx3("button", { class: "scui-load", type: "button", disabled: Boolean(state.operation), onClick: () => {
1297
+ }, children: /* @__PURE__ */ jsxs3("div", { children: [
1298
+ Before ? /* @__PURE__ */ jsx4(Before, { state, adapter, value: null }) : null,
1299
+ /* @__PURE__ */ jsx4(Plan, { plan: state.taskPlan, value: state.taskPlan, state, adapter }),
1300
+ /* @__PURE__ */ jsx4(SessionDetails, { semantics: state.semantics }),
1301
+ state.history.hasEarlier ? /* @__PURE__ */ jsx4("button", { class: "scui-load", type: "button", disabled: Boolean(state.operation), onClick: () => {
1123
1302
  const element = scroller.current;
1124
1303
  if (element) earlierAnchor.current = { height: element.scrollHeight, top: element.scrollTop, entries: state.transcript.length };
1125
1304
  setAtBottom(false);
1126
1305
  adapter.onIntent({ action: "loadEarlier" });
1127
1306
  }, children: "Load earlier messages" }) : null,
1128
- !blocks.length && state.startup !== "ready" ? /* @__PURE__ */ jsx3(LoadingStatus, { state }) : null,
1129
- !blocks.length && state.startup === "ready" ? Empty ? /* @__PURE__ */ jsx3(Empty, { state, adapter, value: null }) : /* @__PURE__ */ jsx3("div", { class: "scui-empty", children: state.error ?? (state.harness ? `${harnessDisplayName(state.harness)} is listening. Say something.` : "No transcript yet.") }) : null,
1130
- blocks.map((block) => block.kind === "activity" ? /* @__PURE__ */ jsx3(Group, { value: block.entries, entries: block.entries, state, adapter }, block.id) : /* @__PURE__ */ jsx3(Entry, { value: block.entry, entry: block.entry, state, adapter }, block.id)),
1131
- pendingMessage ? /* @__PURE__ */ jsxs2("article", { class: "scui-message scui-pending", "data-role": "user", "data-status": pendingMessage.status, children: [
1132
- /* @__PURE__ */ jsx3(Markdown, { value: pendingMessage.text }),
1133
- /* @__PURE__ */ jsxs2("footer", { children: [
1134
- /* @__PURE__ */ jsx3("small", { children: pendingMessage.status === "failed" ? "Not sent" : "Sending\u2026" }),
1135
- pendingMessage.status === "failed" ? /* @__PURE__ */ jsxs2("span", { children: [
1136
- /* @__PURE__ */ jsx3("button", { type: "button", onClick: pendingMessage.onRetry, children: "Retry" }),
1137
- /* @__PURE__ */ jsx3("button", { type: "button", onClick: pendingMessage.onEdit, children: "Edit" })
1307
+ !blocks.length && state.startup !== "ready" ? /* @__PURE__ */ jsx4(LoadingStatus, { state }) : null,
1308
+ !blocks.length && state.startup === "ready" ? Empty ? /* @__PURE__ */ jsx4(Empty, { state, adapter, value: null }) : /* @__PURE__ */ jsx4("div", { class: "scui-empty", children: state.error ?? (state.harness ? `${harnessDisplayName(state.harness)} is listening. Say something.` : "No transcript yet.") }) : null,
1309
+ blocks.map((block, index) => /* @__PURE__ */ jsxs3(Fragment2, { children: [
1310
+ index === unreadBlock ? /* @__PURE__ */ jsx4("div", { class: "scui-unread-divider", role: "separator", "aria-label": "New messages", children: /* @__PURE__ */ jsx4("span", { children: "New" }) }) : null,
1311
+ block.kind === "activity" ? /* @__PURE__ */ jsx4(Group, { value: block.entries, entries: block.entries, state, adapter }) : /* @__PURE__ */ jsx4(Entry, { value: block.entry, entry: block.entry, state, adapter })
1312
+ ] }, block.id)),
1313
+ pendingMessage ? /* @__PURE__ */ jsxs3("article", { class: "scui-message scui-pending", "data-role": "user", "data-status": pendingMessage.status, "aria-label": "Your pending message", children: [
1314
+ /* @__PURE__ */ jsx4(Markdown, { value: pendingMessage.text, copyText: adapter?.copyText }),
1315
+ /* @__PURE__ */ jsxs3("footer", { children: [
1316
+ /* @__PURE__ */ jsx4("small", { children: pendingMessage.status === "failed" ? "Not sent" : "Sending\u2026" }),
1317
+ pendingMessage.status === "failed" ? /* @__PURE__ */ jsxs3("span", { children: [
1318
+ /* @__PURE__ */ jsx4("button", { type: "button", onClick: pendingMessage.onRetry, children: "Retry" }),
1319
+ /* @__PURE__ */ jsx4("button", { type: "button", onClick: pendingMessage.onEdit, children: "Edit" })
1138
1320
  ] }) : null
1139
1321
  ] })
1140
1322
  ] }) : null,
1141
- state.busy ? /* @__PURE__ */ jsxs2("div", { class: "scui-working", role: "status", children: [
1142
- /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", children: "\u2726" }),
1143
- /* @__PURE__ */ jsx3("i", {}),
1144
- /* @__PURE__ */ jsx3("i", {}),
1145
- /* @__PURE__ */ jsx3("i", {}),
1146
- /* @__PURE__ */ jsxs2("small", { children: [
1323
+ state.busy ? /* @__PURE__ */ jsxs3("div", { class: "scui-working", role: "status", children: [
1324
+ /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: "\u2726" }),
1325
+ /* @__PURE__ */ jsx4("i", {}),
1326
+ /* @__PURE__ */ jsx4("i", {}),
1327
+ /* @__PURE__ */ jsx4("i", {}),
1328
+ /* @__PURE__ */ jsxs3("small", { children: [
1147
1329
  harnessDisplayName(state.harness),
1148
1330
  " is working"
1149
1331
  ] })
1150
1332
  ] }) : null,
1151
- After ? /* @__PURE__ */ jsx3(After, { state, adapter, value: null }) : null
1333
+ After ? /* @__PURE__ */ jsx4(After, { state, adapter, value: null }) : null
1152
1334
  ] }) }),
1153
- !atBottom ? /* @__PURE__ */ jsx3("button", { class: "scui-latest", type: "button", onClick: pin, children: "\u2193 Latest" }) : null
1335
+ !atBottom ? /* @__PURE__ */ jsxs3("button", { class: "scui-latest", type: "button", onClick: pin, children: [
1336
+ /* @__PURE__ */ jsx4(UiIcon, { name: "down", size: 13 }),
1337
+ " Latest"
1338
+ ] }) : null
1154
1339
  ] });
1155
1340
  }
1156
1341
 
1157
1342
  // src/logo.jsx
1158
- import { useEffect as useEffect3 } from "preact/hooks";
1159
- import { jsx as jsx4, jsxs as jsxs3 } from "preact/jsx-runtime";
1343
+ import { useEffect as useEffect4 } from "preact/hooks";
1344
+ import { jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
1160
1345
  var LOGOS = {
1161
1346
  "claude-code": {
1162
1347
  viewBox: "-1 3.5 26 18",
@@ -1206,84 +1391,109 @@ function hasHarnessLogo(id) {
1206
1391
  }
1207
1392
  function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
1208
1393
  const logo = LOGOS[id];
1209
- useEffect3(() => {
1394
+ useEffect4(() => {
1210
1395
  if (!logo) onMissingLogo?.(id);
1211
1396
  }, [id, logo, onMissingLogo]);
1212
1397
  if (!logo) return null;
1213
- return /* @__PURE__ */ jsxs3("span", { class: "scui-logo", "data-harness": id, "data-activity": activity, style: `--scui-logo-size:${size}px`, "aria-hidden": "true", children: [
1214
- /* @__PURE__ */ jsx4("svg", { viewBox: logo.viewBox, preserveAspectRatio: "xMidYMid meet", focusable: "false", children: logo.paths.map(([Tag, props], index) => /* @__PURE__ */ jsx4(Tag, { ...props }, index)) }),
1215
- activity && activity !== "idle" ? /* @__PURE__ */ jsx4("i", {}) : null
1398
+ return /* @__PURE__ */ jsxs4("span", { class: "scui-logo", "data-harness": id, "data-activity": activity, style: `--scui-logo-size:${size}px`, "aria-hidden": "true", children: [
1399
+ /* @__PURE__ */ jsx5("svg", { viewBox: logo.viewBox, preserveAspectRatio: "xMidYMid meet", focusable: "false", children: logo.paths.map(([Tag, props], index) => /* @__PURE__ */ jsx5(Tag, { ...props }, index)) }),
1400
+ activity && activity !== "idle" ? /* @__PURE__ */ jsx5("i", {}) : null
1216
1401
  ] });
1217
1402
  }
1218
1403
 
1219
1404
  // src/messenger.jsx
1220
- import { useEffect as useEffect5, useMemo as useMemo2, useRef as useRef4, useState as useState4 } from "preact/hooks";
1405
+ import { useEffect as useEffect6, useId as useId2, useMemo as useMemo2, useRef as useRef5, useState as useState4 } from "preact/hooks";
1221
1406
 
1222
1407
  // src/sessions.jsx
1223
- import { useEffect as useEffect4, useRef as useRef3, useState as useState3 } from "preact/hooks";
1224
- import { jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
1408
+ import { useEffect as useEffect5, useLayoutEffect as useLayoutEffect3, useRef as useRef4, useState as useState3 } from "preact/hooks";
1409
+ import { jsx as jsx6, jsxs as jsxs5 } from "preact/jsx-runtime";
1410
+ var sessionListMemory = /* @__PURE__ */ new Map();
1225
1411
  function SessionRow({ row, state, onOpen }) {
1226
1412
  const activity = sessionActivity(state, row);
1227
- const preview = state.attention.find((item) => item.key === row.key)?.preview;
1228
- return /* @__PURE__ */ jsxs4("button", { class: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", onClick: () => onOpen(row), children: [
1229
- /* @__PURE__ */ jsx5(HarnessLogo, { id: row.harness, activity, size: 34 }),
1230
- /* @__PURE__ */ jsxs4("span", { class: "scui-session-copy", children: [
1231
- /* @__PURE__ */ jsxs4("span", { children: [
1232
- /* @__PURE__ */ jsx5("strong", { children: sessionDisplayName(row) }),
1233
- /* @__PURE__ */ jsx5("small", { children: row.age })
1234
- ] }),
1235
- /* @__PURE__ */ jsxs4("span", { children: [
1236
- /* @__PURE__ */ jsx5("b", { children: harnessDisplayName(row.harness) }),
1237
- /* @__PURE__ */ jsx5("small", { children: preview || row.cwd || row.name })
1413
+ const attentionPreview = state.attention.find((item) => item.key === row.key)?.preview;
1414
+ const title = sessionDisplayName(row);
1415
+ const detail = attentionPreview || row.preview || (row.name && row.name !== title ? row.name : row.cwd);
1416
+ return /* @__PURE__ */ jsxs5("button", { class: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${row.age ? ` \xB7 ${row.age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
1417
+ /* @__PURE__ */ jsx6(HarnessLogo, { id: row.harness, activity, size: 34 }),
1418
+ /* @__PURE__ */ jsxs5("span", { class: "scui-session-copy", children: [
1419
+ /* @__PURE__ */ jsxs5("span", { children: [
1420
+ /* @__PURE__ */ jsx6("strong", { children: title }),
1421
+ /* @__PURE__ */ jsx6("small", { children: row.age })
1238
1422
  ] }),
1239
- state.attachError?.key === row.key ? /* @__PURE__ */ jsx5("em", { children: state.attachError.message }) : null
1423
+ detail ? /* @__PURE__ */ jsx6("span", { children: /* @__PURE__ */ jsx6("small", { title: row.cwd, children: detail }) }) : null,
1424
+ state.attachError?.key === row.key ? /* @__PURE__ */ jsx6("em", { children: state.attachError.message }) : null
1240
1425
  ] })
1241
1426
  ] });
1242
1427
  }
1243
- function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS, focusKey = null }) {
1244
- const [query, setQuery] = useState3("");
1245
- const root = useRef3(null);
1428
+ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS, focusKey = null, memoryKey = state.workspace || "@default" }) {
1429
+ const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
1430
+ const [query, setQuery] = useState3(remembered.query);
1431
+ const [loadingMore, setLoadingMore] = useState3(false);
1432
+ const root = useRef4(null);
1433
+ const rowScroller = useRef4(null);
1246
1434
  const rows = filterSessions(state.sessions, query);
1247
1435
  const Row = components.SessionRow ?? SessionRow;
1248
- useEffect4(() => {
1436
+ useLayoutEffect3(() => {
1437
+ if (rowScroller.current) rowScroller.current.scrollTop = remembered.top;
1438
+ }, [memoryKey]);
1439
+ useEffect5(() => {
1249
1440
  if (!focusKey || !root.current) return;
1250
1441
  const target = focusKey === "@new" ? root.current.querySelector('[data-list-focus="new"]') : [...root.current.querySelectorAll("[data-session-key]")].find((element) => element.dataset.sessionKey === focusKey);
1251
1442
  (target ?? root.current.querySelector("input,button"))?.focus({ preventScroll: true });
1252
1443
  }, [focusKey, rows.length]);
1253
- return /* @__PURE__ */ jsxs4("section", { class: "scui-list", ref: root, children: [
1254
- /* @__PURE__ */ jsxs4("header", { class: "scui-head", children: [
1255
- /* @__PURE__ */ jsxs4("span", { class: "scui-head-copy", children: [
1256
- /* @__PURE__ */ jsx5("strong", { children: labels.chats }),
1257
- /* @__PURE__ */ jsxs4("small", { children: [
1444
+ useEffect5(() => {
1445
+ if (loadingMore) setLoadingMore(false);
1446
+ }, [state.error, state.history.hasMoreSessions, state.sessions.length]);
1447
+ const loadMore = () => {
1448
+ if (loadingMore) return;
1449
+ setLoadingMore(true);
1450
+ const result = adapter.onIntent({ action: "loadSessions" });
1451
+ if (result && typeof result.then === "function") {
1452
+ Promise.resolve(result).then(() => setLoadingMore(false), () => setLoadingMore(false));
1453
+ }
1454
+ };
1455
+ return /* @__PURE__ */ jsxs5("section", { class: "scui-list", ref: root, children: [
1456
+ /* @__PURE__ */ jsxs5("header", { class: "scui-head", children: [
1457
+ /* @__PURE__ */ jsxs5("span", { class: "scui-head-copy", children: [
1458
+ /* @__PURE__ */ jsx6("strong", { children: labels.chats }),
1459
+ /* @__PURE__ */ jsxs5("small", { children: [
1258
1460
  state.sessions.length,
1259
1461
  " recent conversations"
1260
1462
  ] })
1261
1463
  ] }),
1262
- /* @__PURE__ */ jsx5("button", { type: "button", "data-list-focus": "new", "aria-label": labels.newChat, disabled: !state.harnesses.some((item) => item.startable), onClick: onNew, children: "\uFF0B" }),
1263
- onClose ? /* @__PURE__ */ jsx5("button", { type: "button", "aria-label": "Close", onClick: onClose, children: "\xD7" }) : null
1464
+ /* @__PURE__ */ jsx6("button", { type: "button", "data-list-focus": "new", "aria-label": labels.newChat, disabled: !state.harnesses.some((item) => item.startable), onClick: onNew, children: /* @__PURE__ */ jsx6(UiIcon, { name: "plus", size: 18 }) }),
1465
+ onClose ? /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx6(UiIcon, { name: "close", size: 18 }) }) : null
1264
1466
  ] }),
1265
- state.startup !== "ready" ? /* @__PURE__ */ jsx5(LoadingStatus, { state, compact: rows.length > 0 }) : null,
1266
- state.sessions.length > 4 ? /* @__PURE__ */ jsxs4("label", { class: "scui-search", children: [
1267
- /* @__PURE__ */ jsx5("span", { "aria-hidden": "true", children: "\u2315" }),
1268
- /* @__PURE__ */ jsx5("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => setQuery(event.currentTarget.value) }),
1269
- /* @__PURE__ */ jsx5("small", { children: rows.length })
1467
+ state.startup !== "ready" ? /* @__PURE__ */ jsx6(LoadingStatus, { state, compact: rows.length > 0 }) : null,
1468
+ state.sessions.length > 4 ? /* @__PURE__ */ jsxs5("label", { class: "scui-search", children: [
1469
+ /* @__PURE__ */ jsx6(UiIcon, { name: "search", size: 15 }),
1470
+ /* @__PURE__ */ jsx6("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
1471
+ const value = event.currentTarget.value;
1472
+ setQuery(value);
1473
+ boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
1474
+ if (rowScroller.current) rowScroller.current.scrollTop = 0;
1475
+ } }),
1476
+ /* @__PURE__ */ jsx6("small", { children: rows.length })
1270
1477
  ] }) : null,
1271
- /* @__PURE__ */ jsxs4("div", { class: "scui-session-rows", children: [
1272
- !rows.length && state.startup === "ready" ? /* @__PURE__ */ jsx5("div", { class: "scui-empty", children: query ? "No chats match your search." : state.error ?? "No coding chats found." }) : null,
1273
- rows.map((row) => /* @__PURE__ */ jsx5(Row, { value: row, row, state, adapter, onOpen }, row.key)),
1274
- state.history.hasMoreSessions ? /* @__PURE__ */ jsx5("button", { class: "scui-load", type: "button", onClick: () => adapter.onIntent({ action: "loadSessions" }), children: "Load older chats" }) : null
1478
+ /* @__PURE__ */ jsxs5("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
1479
+ !rows.length && state.startup === "ready" ? /* @__PURE__ */ jsx6("div", { class: "scui-empty", children: query ? "No chats match your search." : state.error ?? "No coding chats found." }) : null,
1480
+ rows.map((row) => /* @__PURE__ */ jsx6(Row, { value: row, row, state, adapter, onOpen }, row.key)),
1481
+ state.history.hasMoreSessions ? /* @__PURE__ */ jsx6("button", { class: "scui-load", type: "button", disabled: loadingMore, onClick: loadMore, children: loadingMore ? "Loading older chats\u2026" : "Load older chats" }) : null
1275
1482
  ] })
1276
1483
  ] });
1277
1484
  }
1278
1485
 
1279
1486
  // src/messenger.jsx
1280
- import { jsx as jsx6, jsxs as jsxs5 } from "preact/jsx-runtime";
1487
+ import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
1281
1488
  var pendingMessageMemory = /* @__PURE__ */ new Map();
1489
+ var messengerViewMemory = /* @__PURE__ */ new Map();
1490
+ var newChatMemory = /* @__PURE__ */ new Map();
1491
+ var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "interrupt", "respond", "refresh", "loadEarlier", "loadSessions"]);
1282
1492
  function Receipt({ state, adapter }) {
1283
1493
  const receipt = state.reductionReceipt;
1284
- if (receipt) return /* @__PURE__ */ jsx6("div", { class: "scui-receipt", children: /* @__PURE__ */ jsxs5("span", { children: [
1285
- /* @__PURE__ */ jsx6("strong", { children: "Reduced and verified" }),
1286
- /* @__PURE__ */ jsxs5("small", { children: [
1494
+ if (receipt) return /* @__PURE__ */ jsx7("div", { class: "scui-receipt", children: /* @__PURE__ */ jsxs6("span", { children: [
1495
+ /* @__PURE__ */ jsx7("strong", { children: "Reduced and verified" }),
1496
+ /* @__PURE__ */ jsxs6("small", { children: [
1287
1497
  receipt.sourceTokens.toLocaleString(),
1288
1498
  " \u2192 ",
1289
1499
  receipt.reducedTokens.toLocaleString(),
@@ -1292,82 +1502,150 @@ function Receipt({ state, adapter }) {
1292
1502
  "\xD7 \xB7 reversible"
1293
1503
  ] })
1294
1504
  ] }) });
1295
- if (state.exportReceipt) return /* @__PURE__ */ jsxs5("div", { class: "scui-receipt", children: [
1296
- /* @__PURE__ */ jsxs5("span", { children: [
1297
- /* @__PURE__ */ jsxs5("strong", { children: [
1505
+ if (state.exportReceipt) return /* @__PURE__ */ jsxs6("div", { class: "scui-receipt", children: [
1506
+ /* @__PURE__ */ jsxs6("span", { children: [
1507
+ /* @__PURE__ */ jsxs6("strong", { children: [
1298
1508
  "Lossless export ready \xB7 ",
1299
1509
  harnessDisplayName(state.exportReceipt.targetHarness)
1300
1510
  ] }),
1301
- /* @__PURE__ */ jsxs5("small", { children: [
1511
+ /* @__PURE__ */ jsxs6("small", { children: [
1302
1512
  state.exportReceipt.path,
1303
1513
  " \xB7 ",
1304
1514
  state.exportReceipt.files,
1305
1515
  " files"
1306
1516
  ] })
1307
1517
  ] }),
1308
- /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
1518
+ /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
1309
1519
  ] });
1310
- if (state.terminalHandoff) return /* @__PURE__ */ jsxs5("div", { class: "scui-receipt", children: [
1311
- /* @__PURE__ */ jsxs5("span", { children: [
1312
- /* @__PURE__ */ jsx6("strong", { children: "Terminal handoff ready" }),
1313
- /* @__PURE__ */ jsx6("small", { children: state.terminalHandoff.cwd })
1520
+ if (state.terminalHandoff) return /* @__PURE__ */ jsxs6("div", { class: "scui-receipt", children: [
1521
+ /* @__PURE__ */ jsxs6("span", { children: [
1522
+ /* @__PURE__ */ jsx7("strong", { children: "Terminal handoff ready" }),
1523
+ /* @__PURE__ */ jsx7("small", { children: state.terminalHandoff.cwd })
1314
1524
  ] }),
1315
- /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
1525
+ /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
1316
1526
  ] });
1317
1527
  return null;
1318
1528
  }
1319
- function ChatHeader({ state, adapter, pendingStatus, onBack, onNew, onClose }) {
1320
- const harness = state.attached?.harness ?? state.harness;
1529
+ function ConversationActions({ state, adapter, actionPending }) {
1530
+ const [open, setOpen] = useState4(false);
1531
+ const root = useRef5(null);
1532
+ const panel = useRef5(null);
1533
+ const trigger = useRef5(null);
1534
+ const menuId = useId2();
1321
1535
  const targets = state.harnesses.filter((item) => item.startable);
1536
+ const groups = [
1537
+ {
1538
+ label: "Session",
1539
+ items: [
1540
+ state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
1541
+ state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
1542
+ state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null
1543
+ ].filter(Boolean)
1544
+ },
1545
+ {
1546
+ label: "Lower-cost continuation",
1547
+ items: state.canReduce ? targets.map((target) => ({ key: `reduce:${target.id}`, label: target.id === state.harness ? `Reduce and continue in ${target.label}` : `Reduce and switch to ${target.label}`, intent: { action: "reduce", targetHarness: target.id } })) : []
1548
+ },
1549
+ {
1550
+ label: "Independent continuation",
1551
+ items: state.canBranch ? targets.map((target) => ({ key: `branch:${target.id}`, label: target.id === state.harness ? `Fork in ${target.label}` : `Continue with ${target.label}`, intent: { action: "branch", targetHarness: target.id } })) : []
1552
+ }
1553
+ ].filter((group) => group.items.length);
1554
+ useEffect6(() => {
1555
+ if (!open) return;
1556
+ panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
1557
+ const dismiss = (event) => {
1558
+ if (event.type === "keydown" && event.key === "Escape") {
1559
+ event.preventDefault();
1560
+ setOpen(false);
1561
+ trigger.current?.focus({ preventScroll: true });
1562
+ } else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
1563
+ setOpen(false);
1564
+ }
1565
+ };
1566
+ document.addEventListener("keydown", dismiss);
1567
+ document.addEventListener("pointerdown", dismiss, true);
1568
+ return () => {
1569
+ document.removeEventListener("keydown", dismiss);
1570
+ document.removeEventListener("pointerdown", dismiss, true);
1571
+ };
1572
+ }, [open]);
1573
+ const dispatch = (intent) => {
1574
+ setOpen(false);
1575
+ adapter.onIntent(intent);
1576
+ };
1577
+ const navigate = (event) => {
1578
+ if (event.key === "Escape") {
1579
+ event.preventDefault();
1580
+ event.stopPropagation();
1581
+ setOpen(false);
1582
+ trigger.current?.focus({ preventScroll: true });
1583
+ return;
1584
+ }
1585
+ if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
1586
+ const items = [...panel.current.querySelectorAll("button:not(:disabled)")];
1587
+ if (!items.length) return;
1588
+ event.preventDefault();
1589
+ const current = items.indexOf(document.activeElement);
1590
+ const index = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : event.key === "ArrowDown" ? (current + 1) % items.length : (current <= 0 ? items.length : current) - 1;
1591
+ items[index].focus({ preventScroll: true });
1592
+ };
1593
+ return /* @__PURE__ */ jsxs6("div", { class: "scui-menu", ref: root, onBlur: (event) => {
1594
+ if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
1595
+ }, children: [
1596
+ /* @__PURE__ */ jsx7("button", { ref: trigger, class: "scui-menu-trigger", type: "button", "aria-label": "Conversation actions", "aria-haspopup": "menu", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((value) => !value), children: /* @__PURE__ */ jsx7(UiIcon, { name: "menu", size: 18 }) }),
1597
+ open ? /* @__PURE__ */ jsx7("div", { ref: panel, id: menuId, class: "scui-menu-panel", role: "menu", "aria-label": "Conversation actions", onKeyDown: navigate, children: groups.map((group) => /* @__PURE__ */ jsxs6("section", { role: "group", "aria-label": group.label, children: [
1598
+ /* @__PURE__ */ jsx7("strong", { children: group.label }),
1599
+ group.items.map((item) => /* @__PURE__ */ jsx7("button", { role: "menuitem", type: "button", disabled: actionPending, onClick: () => dispatch(item.intent), children: item.label }, item.key))
1600
+ ] }, group.label)) }) : null
1601
+ ] });
1602
+ }
1603
+ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
1604
+ const back = useRef5(null);
1605
+ const harness = state.attached?.harness ?? state.harness;
1322
1606
  const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
1323
1607
  const status = state.needsInput ? "Needs input" : pendingStatus === "failed" ? "Send failed" : state.busy ? "Working" : pendingStatus === "sending" ? "Sending" : pendingStatus === "editing" ? "Editing message" : state.mode === "mirror" ? "Read-only" : "Ready";
1324
1608
  const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
1325
- return /* @__PURE__ */ jsxs5("header", { class: "scui-head scui-chat-head", children: [
1326
- /* @__PURE__ */ jsx6("button", { type: "button", autofocus: state.mode === "mirror" && !state.canSend, "aria-label": "Back to chats", onClick: onBack, children: "\u2039" }),
1327
- /* @__PURE__ */ jsx6(HarnessLogo, { id: harness, size: 28 }),
1328
- /* @__PURE__ */ jsxs5("span", { class: "scui-head-copy", children: [
1329
- /* @__PURE__ */ jsx6("strong", { children: title }),
1330
- /* @__PURE__ */ jsxs5("small", { children: [
1609
+ useEffect6(() => {
1610
+ if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
1611
+ }, []);
1612
+ return /* @__PURE__ */ jsxs6("header", { class: "scui-head scui-chat-head", children: [
1613
+ /* @__PURE__ */ jsx7("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx7(UiIcon, { name: "back", size: 18 }) }),
1614
+ /* @__PURE__ */ jsx7(HarnessLogo, { id: harness, size: 28 }),
1615
+ /* @__PURE__ */ jsxs6("span", { class: "scui-head-copy", children: [
1616
+ /* @__PURE__ */ jsx7("strong", { children: title }),
1617
+ /* @__PURE__ */ jsxs6("small", { children: [
1331
1618
  harnessDisplayName(harness),
1332
1619
  " \xB7 ",
1333
1620
  status
1334
1621
  ] })
1335
1622
  ] }),
1336
- menu ? /* @__PURE__ */ jsxs5("details", { class: "scui-menu", children: [
1337
- /* @__PURE__ */ jsx6("summary", { "aria-label": "Conversation actions", children: "\u2022\u2022\u2022" }),
1338
- /* @__PURE__ */ jsxs5("div", { children: [
1339
- state.canDetach ? /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.onIntent({ action: "detach" }), children: "Detach to read-only" }) : null,
1340
- state.canOpenTerminal ? /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.onIntent({ action: "terminal" }), children: "Prepare terminal handoff" }) : null,
1341
- state.canReduce ? targets.map((target) => /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.onIntent({ action: "reduce", targetHarness: target.id }), children: target.id === state.harness ? `Reduce and continue in ${target.label}` : `Reduce and switch to ${target.label}` }, `reduce:${target.id}`)) : null,
1342
- state.canBranch ? targets.map((target) => /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.onIntent({ action: "branch", targetHarness: target.id }), children: target.id === state.harness ? `Fork in ${target.label}` : `Continue with ${target.label}` }, `branch:${target.id}`)) : null,
1343
- state.canExport && state.exportBackTarget ? /* @__PURE__ */ jsxs5("button", { type: "button", onClick: () => adapter.onIntent({ action: "export", targetHarness: state.exportBackTarget }), children: [
1344
- "Export back to ",
1345
- harnessDisplayName(state.exportBackTarget)
1346
- ] }) : null
1347
- ] })
1348
- ] }) : null,
1349
- /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: "\uFF0B" }),
1350
- onClose ? /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Close", onClick: onClose, children: "\xD7" }) : null
1623
+ menu ? /* @__PURE__ */ jsx7(ConversationActions, { state, adapter, actionPending }) : null,
1624
+ /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx7(UiIcon, { name: "plus", size: 18 }) }),
1625
+ onClose ? /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx7(UiIcon, { name: "close", size: 18 }) }) : null
1351
1626
  ] });
1352
1627
  }
1353
1628
  function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
1354
1629
  const Header = slots.header;
1355
1630
  const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
1356
1631
  const [pending, setPendingState] = useState4(() => pendingMessageMemory.get(memoryKey) ?? null);
1632
+ const [pendingAction, setPendingAction] = useState4(null);
1357
1633
  const [restoreDraft, setRestoreDraft] = useState4(null);
1358
- const restoreSequence = useRef4(0);
1359
- const acknowledged = useRef4(/* @__PURE__ */ new Set());
1634
+ const restoreSequence = useRef5(0);
1635
+ const actionSequence = useRef5(0);
1636
+ const acknowledged = useRef5(/* @__PURE__ */ new Set());
1360
1637
  const setPending = (update) => setPendingState((current) => {
1361
1638
  const next = typeof update === "function" ? update(current) : update;
1362
1639
  if (next) boundedSet(pendingMessageMemory, memoryKey, next);
1363
1640
  else pendingMessageMemory.delete(memoryKey);
1364
1641
  return next;
1365
1642
  });
1366
- useEffect5(() => {
1643
+ useEffect6(() => {
1367
1644
  setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
1645
+ setPendingAction(null);
1368
1646
  setRestoreDraft(null);
1369
1647
  }, [memoryKey]);
1370
- useEffect5(() => {
1648
+ useEffect6(() => {
1371
1649
  if (!pending) return;
1372
1650
  if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
1373
1651
  setPending(null);
@@ -1394,7 +1672,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
1394
1672
  setRestoreDraft({ id: restoreSequence.current, text: pending.text });
1395
1673
  setPending({ ...pending, status: "editing" });
1396
1674
  };
1397
- useEffect5(() => {
1675
+ useEffect6(() => {
1398
1676
  const key = state.attached?.key;
1399
1677
  if (!key || !state.attention.some((item) => item.key === key)) {
1400
1678
  if (key) acknowledged.current.delete(key);
@@ -1405,75 +1683,143 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
1405
1683
  adapter.onIntent({ action: "ack", key });
1406
1684
  }
1407
1685
  }, [adapter, state.attached?.key, state.attention]);
1686
+ const trackedAdapter = useMemo2(() => ({
1687
+ ...adapter,
1688
+ onIntent(intent) {
1689
+ if (!TRACKED_ACTIONS.has(intent.action)) return adapter.onIntent(intent);
1690
+ const id = actionSequence.current + 1;
1691
+ actionSequence.current = id;
1692
+ const action2 = intent.action;
1693
+ setPendingAction({ id, action: action2, initialError: state.error, seenOperation: false });
1694
+ let result;
1695
+ try {
1696
+ result = adapter.onIntent(intent);
1697
+ } catch (error) {
1698
+ setPendingAction((current) => current?.id === id ? null : current);
1699
+ throw error;
1700
+ }
1701
+ if (result && typeof result.then === "function") {
1702
+ Promise.resolve(result).then(
1703
+ () => setPendingAction((current) => current?.id === id ? null : current),
1704
+ () => setPendingAction((current) => current?.id === id ? null : current)
1705
+ );
1706
+ }
1707
+ return result;
1708
+ }
1709
+ }), [adapter, state.error]);
1710
+ useEffect6(() => {
1711
+ if (!pendingAction) return;
1712
+ if (state.operation && !pendingAction.seenOperation) {
1713
+ setPendingAction({ ...pendingAction, seenOperation: true });
1714
+ } else if (!state.operation && (pendingAction.seenOperation || state.error !== pendingAction.initialError)) {
1715
+ setPendingAction(null);
1716
+ }
1717
+ }, [pendingAction, state.error, state.operation]);
1408
1718
  const pendingMessage = pending && pending.status !== "editing" ? { text: pending.text, status: pending.status, onRetry: retryPending, onEdit: editPending } : null;
1409
- return /* @__PURE__ */ jsxs5("section", { class: "scui-chat", children: [
1410
- Header ? /* @__PURE__ */ jsx6(Header, { state, adapter, value: null }) : /* @__PURE__ */ jsx6(ChatHeader, { state, adapter, pendingStatus: pending?.status ?? null, onBack, onNew, onClose }),
1411
- operationLabel(state.operation) ? /* @__PURE__ */ jsxs5("div", { class: "scui-operation", role: "status", children: [
1412
- /* @__PURE__ */ jsx6("i", {}),
1413
- operationLabel(state.operation)
1719
+ const action = state.operation || pendingAction?.action || null;
1720
+ const actionLabel = operationLabel(action);
1721
+ const actionState = action ? { ...state, operation: action, canInterrupt: false, canRespond: false } : state;
1722
+ const unreadAfterMessages = state.attention.find((item) => item.key === state.attached?.key)?.afterMessages ?? null;
1723
+ return /* @__PURE__ */ jsxs6("section", { class: "scui-chat", children: [
1724
+ Header ? /* @__PURE__ */ jsx7(Header, { state: actionState, adapter: trackedAdapter, value: null }) : /* @__PURE__ */ jsx7(ChatHeader, { state: actionState, adapter: trackedAdapter, pendingStatus: pending?.status ?? null, actionPending: Boolean(action), onBack, onNew, onClose }),
1725
+ actionLabel ? /* @__PURE__ */ jsxs6("div", { class: "scui-operation", role: "status", children: [
1726
+ /* @__PURE__ */ jsx7("i", {}),
1727
+ actionLabel
1414
1728
  ] }) : null,
1415
- state.error ? /* @__PURE__ */ jsxs5("div", { class: "scui-error", role: "alert", children: [
1416
- /* @__PURE__ */ jsx6("span", { children: state.error }),
1417
- state.recoverable ? /* @__PURE__ */ jsx6("button", { type: "button", onClick: () => adapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
1729
+ state.error ? /* @__PURE__ */ jsxs6("div", { class: "scui-error", role: "alert", children: [
1730
+ /* @__PURE__ */ jsx7("span", { children: state.error }),
1731
+ state.recoverable ? /* @__PURE__ */ jsx7("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
1418
1732
  ] }) : null,
1419
- /* @__PURE__ */ jsx6(Receipt, { state, adapter }),
1420
- /* @__PURE__ */ jsx6(Conversation, { state, adapter, components, slots, memoryKey, pending: pendingMessage }, memoryKey),
1421
- /* @__PURE__ */ jsx6(ContinuationBar, { state, adapter, labels }),
1422
- state.mode !== "mirror" || state.canSend ? /* @__PURE__ */ jsx6(Composer, { state, adapter, labels, memoryKey, pendingStatus: pending?.status ?? null, restoreDraft, onDraftRestored: (id) => setRestoreDraft((value) => value?.id === id ? null : value), onPending: beginPending }, memoryKey) : null
1733
+ /* @__PURE__ */ jsx7(Receipt, { state, adapter }),
1734
+ /* @__PURE__ */ jsx7(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
1735
+ /* @__PURE__ */ jsx7(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
1736
+ state.mode !== "mirror" || state.canSend ? /* @__PURE__ */ jsx7(Composer, { state: actionState, adapter: trackedAdapter, labels, memoryKey, pendingStatus: pending?.status ?? null, restoreDraft, onDraftRestored: (id) => setRestoreDraft((value) => value?.id === id ? null : value), onPending: beginPending }, memoryKey) : null
1423
1737
  ] });
1424
1738
  }
1425
- function NewChat({ state, adapter, onBack, onClose, onStarted, labels }) {
1739
+ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey }) {
1426
1740
  const startable = state.harnesses.filter((item) => item.startable);
1427
1741
  const startableKey = startable.map((item) => item.id).join("\0");
1428
- const [harness, setHarness] = useState4(startable[0]?.id ?? "");
1429
- const [draft, setDraft] = useState4("");
1430
- const [starting, setStarting] = useState4(false);
1431
- useEffect5(() => {
1432
- if (!startable.some((item) => item.id === harness)) setHarness(startable[0]?.id ?? "");
1742
+ const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "" };
1743
+ const [harness, setHarness] = useState4(remembered.harness);
1744
+ const [draft, setDraft] = useState4(remembered.draft);
1745
+ const [starting, setStarting] = useState4(null);
1746
+ const startSequence = useRef5(0);
1747
+ const textarea = useRef5(null);
1748
+ useAutosizeTextarea(textarea, draft);
1749
+ useEffect6(() => {
1750
+ if (startable.some((item) => item.id === harness)) return;
1751
+ const next = startable[0]?.id ?? "";
1752
+ setHarness(next);
1753
+ boundedSet(newChatMemory, memoryKey, { harness: next, draft });
1433
1754
  }, [harness, startableKey]);
1434
- useEffect5(() => {
1435
- if (starting && (state.busy || state.transcript.length)) onStarted();
1436
- }, [onStarted, starting, state.busy, state.transcript.length]);
1437
- useEffect5(() => {
1438
- if (starting && state.error && !state.busy && !state.operation) setStarting(false);
1755
+ useEffect6(() => {
1756
+ if (!starting) return;
1757
+ const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
1758
+ const beganWorking = !starting.busy && state.busy;
1759
+ if (!sessionChanged && !beganWorking) return;
1760
+ newChatMemory.delete(memoryKey);
1761
+ onStarted();
1762
+ }, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
1763
+ useEffect6(() => {
1764
+ if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
1439
1765
  }, [starting, state.busy, state.error, state.operation]);
1766
+ useEffect6(() => {
1767
+ textarea.current?.focus({ preventScroll: true });
1768
+ }, []);
1440
1769
  const send = () => {
1441
1770
  const text = draft.trim();
1442
1771
  if (!text || !harness || starting) return;
1443
- setStarting(true);
1444
- adapter.onIntent({ action: "new", harness, text });
1772
+ const id = startSequence.current + 1;
1773
+ startSequence.current = id;
1774
+ setStarting({ id, attachedKey: state.attached?.key ?? null, busy: state.busy, initialError: state.error });
1775
+ const result = adapter.onIntent({ action: "new", harness, text });
1776
+ if (result && typeof result.then === "function") {
1777
+ Promise.resolve(result).catch(() => setStarting((current) => current?.id === id ? null : current));
1778
+ }
1445
1779
  };
1446
- return /* @__PURE__ */ jsxs5("section", { class: "scui-chat", children: [
1447
- /* @__PURE__ */ jsxs5("header", { class: "scui-head", children: [
1448
- /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Back", onClick: onBack, children: "\u2039" }),
1449
- /* @__PURE__ */ jsxs5("span", { class: "scui-head-copy", children: [
1450
- /* @__PURE__ */ jsx6("strong", { children: labels.newChat }),
1451
- /* @__PURE__ */ jsx6("small", { children: "No session is created until you send" })
1780
+ return /* @__PURE__ */ jsxs6("section", { class: "scui-chat", children: [
1781
+ /* @__PURE__ */ jsxs6("header", { class: "scui-head", children: [
1782
+ /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx7(UiIcon, { name: "back", size: 18 }) }),
1783
+ /* @__PURE__ */ jsxs6("span", { class: "scui-head-copy", children: [
1784
+ /* @__PURE__ */ jsx7("strong", { children: labels.newChat }),
1785
+ /* @__PURE__ */ jsx7("small", { children: "No session is created until you send" })
1452
1786
  ] }),
1453
- onClose ? /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Close", onClick: onClose, children: "\xD7" }) : null
1787
+ onClose ? /* @__PURE__ */ jsx7("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx7(UiIcon, { name: "close", size: 18 }) }) : null
1454
1788
  ] }),
1455
- /* @__PURE__ */ jsxs5("div", { class: "scui-new", children: [
1456
- /* @__PURE__ */ jsx6("span", { "aria-hidden": "true", children: "\u2726" }),
1457
- /* @__PURE__ */ jsx6("strong", { children: "What should the agent build or fix?" }),
1458
- /* @__PURE__ */ jsx6("small", { children: "Choose a coding harness and send the first message." })
1789
+ starting ? /* @__PURE__ */ jsxs6("div", { class: "scui-operation", role: "status", children: [
1790
+ /* @__PURE__ */ jsx7("i", {}),
1791
+ operationLabel("start")
1792
+ ] }) : null,
1793
+ /* @__PURE__ */ jsxs6("div", { class: "scui-new", children: [
1794
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", children: "\u2726" }),
1795
+ /* @__PURE__ */ jsx7("strong", { children: "What should the agent build or fix?" }),
1796
+ /* @__PURE__ */ jsx7("small", { children: "Choose a coding harness and send the first message." })
1459
1797
  ] }),
1460
- /* @__PURE__ */ jsxs5("div", { class: "scui-compose", children: [
1461
- /* @__PURE__ */ jsxs5("label", { class: "scui-harness-picker", children: [
1462
- /* @__PURE__ */ jsx6(HarnessLogo, { id: harness, size: 24 }),
1463
- /* @__PURE__ */ jsx6("span", { children: "Coding harness" }),
1464
- /* @__PURE__ */ jsx6("select", { value: harness, disabled: starting, onChange: (event) => setHarness(event.currentTarget.value), children: state.harnesses.map((item) => /* @__PURE__ */ jsxs5("option", { value: item.id, disabled: !item.startable, children: [
1798
+ /* @__PURE__ */ jsxs6("div", { class: "scui-compose", children: [
1799
+ /* @__PURE__ */ jsxs6("label", { class: "scui-harness-picker", children: [
1800
+ /* @__PURE__ */ jsx7(HarnessLogo, { id: harness, size: 24 }),
1801
+ /* @__PURE__ */ jsx7("span", { children: "Coding harness" }),
1802
+ /* @__PURE__ */ jsx7("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
1803
+ const value = event.currentTarget.value;
1804
+ setHarness(value);
1805
+ boundedSet(newChatMemory, memoryKey, { harness: value, draft });
1806
+ }, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs6("option", { value: item.id, disabled: !item.startable, children: [
1465
1807
  item.label,
1466
1808
  item.startable ? "" : " \xB7 unavailable"
1467
1809
  ] }, item.id)) })
1468
1810
  ] }),
1469
- /* @__PURE__ */ jsxs5("div", { class: "scui-envelope", children: [
1470
- /* @__PURE__ */ jsx6("textarea", { autofocus: true, rows: 3, "aria-label": "Message coding agent", placeholder: startable.length ? "What should the agent do?" : "No coding harness is available", value: draft, disabled: !startable.length || starting, onInput: (event) => setDraft(event.currentTarget.value), onKeyDown: (event) => {
1811
+ /* @__PURE__ */ jsxs6("div", { class: "scui-envelope", children: [
1812
+ /* @__PURE__ */ jsx7("textarea", { ref: textarea, rows: 3, "aria-label": "Message coding agent", placeholder: startable.length ? "What should the agent do?" : "No coding harness is available", value: draft, disabled: !startable.length || Boolean(starting), onInput: (event) => {
1813
+ const value = event.currentTarget.value;
1814
+ setDraft(value);
1815
+ boundedSet(newChatMemory, memoryKey, { harness, draft: value });
1816
+ }, onKeyDown: (event) => {
1471
1817
  if (isSendKey(event)) {
1472
1818
  event.preventDefault();
1473
1819
  send();
1474
1820
  }
1475
1821
  } }),
1476
- /* @__PURE__ */ jsx6("span", { children: /* @__PURE__ */ jsx6("button", { type: "button", class: "scui-send", "aria-label": "Start chat", disabled: !draft.trim() || !harness || starting, onClick: send, children: starting ? "\u25CC" : "\u2191" }) })
1822
+ /* @__PURE__ */ jsx7("span", { children: /* @__PURE__ */ jsx7("button", { type: "button", class: "scui-send", "aria-label": "Start chat", disabled: !draft.trim() || !harness || Boolean(starting), onClick: send, children: starting ? /* @__PURE__ */ jsx7("i", { class: "scui-control-spinner" }) : /* @__PURE__ */ jsx7(UiIcon, { name: "send", size: 17 }) }) })
1477
1823
  ] })
1478
1824
  ] })
1479
1825
  ] });
@@ -1481,10 +1827,15 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels }) {
1481
1827
  function SupercodeMessenger({ state: stateInput, adapter, class: className = "", initialView, labels, components = {}, slots = {} }) {
1482
1828
  const state = useMemo2(() => normalizeUiState(stateInput), [stateInput]);
1483
1829
  const copy = { ...DEFAULT_LABELS, ...labels };
1484
- const [view, setView] = useState4(initialView ?? (state.attention.length ? "list" : state.attached || state.transcript.length ? "chat" : "list"));
1830
+ const memoryKey = state.workspace || "@default";
1831
+ const [view, setViewState] = useState4(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
1485
1832
  const [opening, setOpening] = useState4(null);
1486
1833
  const [listFocus, setListFocus] = useState4(null);
1487
- useEffect5(() => {
1834
+ const setView = (next) => {
1835
+ boundedSet(messengerViewMemory, memoryKey, next);
1836
+ setViewState(next);
1837
+ };
1838
+ useEffect6(() => {
1488
1839
  if (!opening) return;
1489
1840
  if (state.attached?.key === opening.key) {
1490
1841
  setOpening(null);
@@ -1496,36 +1847,35 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
1496
1847
  const open = (row) => {
1497
1848
  setListFocus(row.key);
1498
1849
  setOpening(row);
1499
- adapter.onIntent({ action: "ack", key: row.key });
1500
1850
  adapter.onIntent({ action: "attach", key: row.key });
1501
1851
  };
1502
1852
  const close = () => adapter.onClose?.();
1503
1853
  const Footer = slots.footer;
1504
- return /* @__PURE__ */ jsxs5("main", { class: `scui-root ${className}`, "data-view": view, "data-mode": state.mode, "aria-label": "Supercode messenger", children: [
1505
- view === "list" ? /* @__PURE__ */ jsx6(SessionList, { state, adapter, focusKey: listFocus, onOpen: open, onNew: () => {
1854
+ return /* @__PURE__ */ jsxs6("main", { class: `scui-root ${className}`, "data-view": view, "data-mode": state.mode, "aria-label": "Supercode messenger", children: [
1855
+ view === "list" ? /* @__PURE__ */ jsx7(SessionList, { state, adapter, focusKey: listFocus, onOpen: open, onNew: () => {
1506
1856
  setListFocus("@new");
1507
1857
  setView("new");
1508
- }, onClose: adapter.onClose ? close : void 0, components, labels: copy }) : null,
1509
- view === "new" ? /* @__PURE__ */ jsx6(NewChat, { state, adapter, onBack: () => setView("list"), onClose: adapter.onClose ? close : void 0, onStarted: () => setView("chat"), labels: copy }) : null,
1510
- view === "chat" ? /* @__PURE__ */ jsx6(Chat, { state, adapter, onBack: () => {
1858
+ }, onClose: adapter.onClose ? close : void 0, components, labels: copy, memoryKey }) : null,
1859
+ view === "new" ? /* @__PURE__ */ jsx7(NewChat, { state, adapter, onBack: () => setView("list"), onClose: adapter.onClose ? close : void 0, onStarted: () => setView("chat"), labels: copy, memoryKey }) : null,
1860
+ view === "chat" ? /* @__PURE__ */ jsx7(Chat, { state, adapter, onBack: () => {
1511
1861
  setListFocus(state.attached?.key ?? listFocus);
1512
1862
  setView("list");
1513
1863
  }, onNew: () => {
1514
1864
  setListFocus("@new");
1515
1865
  setView("new");
1516
1866
  }, onClose: adapter.onClose ? close : void 0, components, slots, labels: copy }) : null,
1517
- opening ? /* @__PURE__ */ jsxs5("div", { class: "scui-opening", role: "status", "aria-busy": "true", children: [
1518
- /* @__PURE__ */ jsx6(HarnessLogo, { id: opening.harness, size: 34 }),
1519
- /* @__PURE__ */ jsxs5("span", { children: [
1520
- /* @__PURE__ */ jsxs5("strong", { children: [
1867
+ opening ? /* @__PURE__ */ jsxs6("div", { class: "scui-opening", role: "status", "aria-busy": "true", children: [
1868
+ /* @__PURE__ */ jsx7(HarnessLogo, { id: opening.harness, size: 34 }),
1869
+ /* @__PURE__ */ jsxs6("span", { children: [
1870
+ /* @__PURE__ */ jsxs6("strong", { children: [
1521
1871
  "Opening ",
1522
1872
  sessionDisplayName(opening)
1523
1873
  ] }),
1524
- /* @__PURE__ */ jsx6("small", { children: "Loading the latest transcript window\u2026" })
1874
+ /* @__PURE__ */ jsx7("small", { children: "Loading the latest transcript window\u2026" })
1525
1875
  ] }),
1526
- /* @__PURE__ */ jsx6("i", {})
1876
+ /* @__PURE__ */ jsx7("i", {})
1527
1877
  ] }) : null,
1528
- Footer ? /* @__PURE__ */ jsx6(Footer, { state, adapter, value: copy }) : null
1878
+ Footer ? /* @__PURE__ */ jsx7(Footer, { state, adapter, value: copy }) : null
1529
1879
  ] });
1530
1880
  }
1531
1881
  export {
@@ -1543,5 +1893,6 @@ export {
1543
1893
  TaskPlan,
1544
1894
  ToolRow,
1545
1895
  TranscriptEntry,
1896
+ UiIcon,
1546
1897
  hasHarnessLogo
1547
1898
  };