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

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