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

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