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