@volter-ai-dev/supercode-ui 0.1.28 → 0.1.30

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 useEffect7, useId as useId2, useMemo as useMemo2, useRef as useRef6, useState as useState5 } from "preact/hooks";
2
+ import { useEffect as useEffect8, useId as useId3, useMemo as useMemo3, useRef as useRef7, useState as useState6 } from "preact/hooks";
3
3
 
4
4
  // core.mjs
5
5
  var HARNESS_NAMES = Object.freeze({
@@ -40,6 +40,7 @@ var EMPTY_UI_STATE = Object.freeze({
40
40
  canReduce: false,
41
41
  canInterrupt: false,
42
42
  canRespond: false,
43
+ canConfigureSettings: false,
43
44
  messaging: null,
44
45
  workspace: "",
45
46
  taskPlan: Object.freeze({ source: "none", items: Object.freeze([]), residueCount: 0, observedAt: null }),
@@ -48,6 +49,8 @@ var EMPTY_UI_STATE = Object.freeze({
48
49
  exportBackTarget: null,
49
50
  exportReceipt: null,
50
51
  reductionReceipt: null,
52
+ interopSettings: null,
53
+ interopSettingsError: null,
51
54
  error: null,
52
55
  recoverable: false,
53
56
  harnesses: Object.freeze([]),
@@ -81,7 +84,16 @@ function number(value, fallback = 0) {
81
84
  function nullableNumber(value) {
82
85
  return typeof value === "number" && Number.isFinite(value) ? value : null;
83
86
  }
84
- function boundedString(value, max) {
87
+ function relativeAge(updatedAt, now = Date.now()) {
88
+ if (typeof updatedAt !== "number" || !Number.isFinite(updatedAt) || updatedAt <= 0) return "";
89
+ const delta = Math.max(0, now - updatedAt);
90
+ if (delta < 6e4) return "now";
91
+ if (delta < 36e5) return `${Math.floor(delta / 6e4)}m ago`;
92
+ if (delta < 864e5) return `${Math.floor(delta / 36e5)}h ago`;
93
+ if (delta < 6048e5) return `${Math.floor(delta / 864e5)}d ago`;
94
+ return `${Math.floor(delta / 6048e5)}w ago`;
95
+ }
96
+ function boundedString(value, max = 2e3) {
85
97
  if (typeof value !== "string") return "";
86
98
  return value.length <= max ? value : `${value.slice(0, max)}\u2026`;
87
99
  }
@@ -546,11 +558,12 @@ function readSessions(value) {
546
558
  title: string(row.title),
547
559
  preview: string(row.preview),
548
560
  age: string(row.age),
561
+ previewUpdatedAt: nullableNumber(row.previewUpdatedAt),
549
562
  updatedAt: nullableNumber(row.updatedAt),
550
563
  messages: nullableNumber(row.messages),
551
564
  active: row.active === true,
552
565
  live: row.live === true,
553
- runtimeStatus: row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
566
+ runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
554
567
  }];
555
568
  });
556
569
  }
@@ -624,6 +637,68 @@ function readReductionReceipt(value) {
624
637
  targetHarness: receipt.targetHarness
625
638
  };
626
639
  }
640
+ function readInteropSettings(value) {
641
+ const report = record(value);
642
+ if (report?.schema !== "supercode.harness-interop-settings.v1" || typeof report.harness !== "string" || typeof report.revision !== "string" || !Array.isArray(report.controls) || !Array.isArray(report.advisories)) return null;
643
+ const controls = report.controls.slice(0, 20).flatMap((raw) => {
644
+ const control = record(raw);
645
+ if (!control || typeof control.key !== "string" || typeof control.label !== "string") return [];
646
+ return [{
647
+ key: boundedString(control.key, 200),
648
+ nativeKey: boundedString(control.native_key ?? control.nativeKey, 200),
649
+ label: boundedString(control.label, 300),
650
+ description: boundedString(control.description),
651
+ scope: ["user", "project", "managed", "command_line"].includes(control.scope) ? control.scope : "user",
652
+ sourcePath: boundedString(control.source_path ?? control.sourcePath, 4e3),
653
+ configuredValue: typeof (control.configured_value ?? control.configuredValue) === "string" ? boundedString(control.configured_value ?? control.configuredValue, 500) : null,
654
+ effectiveValue: typeof (control.effective_value ?? control.effectiveValue) === "string" ? boundedString(control.effective_value ?? control.effectiveValue, 500) : null,
655
+ effectiveKnown: (control.effective_known ?? control.effectiveKnown) === true,
656
+ effectiveNote: boundedString(control.effective_note ?? control.effectiveNote),
657
+ choices: Array.isArray(control.choices) ? control.choices.slice(0, 20).flatMap((candidate) => {
658
+ const choice = record(candidate);
659
+ return choice && typeof choice.value === "string" && typeof choice.label === "string" ? [{
660
+ value: boundedString(choice.value, 500),
661
+ label: boundedString(choice.label, 300),
662
+ description: boundedString(choice.description),
663
+ ...typeof choice.risk === "string" ? { risk: boundedString(choice.risk) } : {}
664
+ }] : [];
665
+ }) : [],
666
+ writable: control.writable === true,
667
+ resettable: control.resettable === true,
668
+ requiresRestart: (control.requires_restart ?? control.requiresRestart) === true
669
+ }];
670
+ });
671
+ const advisories = report.advisories.slice(0, 20).flatMap((raw) => {
672
+ const advisory = record(raw);
673
+ const recommendation = record(advisory?.recommendation);
674
+ const change = record(recommendation?.change);
675
+ if (!advisory || !recommendation || !change || typeof advisory.code !== "string" || typeof advisory.title !== "string" || typeof advisory.setting !== "string" || typeof change.key !== "string") return [];
676
+ return [{
677
+ code: boundedString(advisory.code, 200),
678
+ severity: ["info", "warning", "error"].includes(advisory.severity) ? advisory.severity : "warning",
679
+ title: boundedString(advisory.title, 500),
680
+ message: boundedString(advisory.message),
681
+ setting: boundedString(advisory.setting, 200),
682
+ recommendation: {
683
+ label: boundedString(recommendation.label, 500),
684
+ description: boundedString(recommendation.description),
685
+ consequence: boundedString(recommendation.consequence),
686
+ change: {
687
+ key: boundedString(change.key, 200),
688
+ value: typeof change.value === "string" ? boundedString(change.value, 500) : null
689
+ },
690
+ command: boundedString(recommendation.command, 4e3)
691
+ }
692
+ }];
693
+ });
694
+ return {
695
+ schema: "supercode.harness-interop-settings.v1",
696
+ harness: report.harness,
697
+ revision: boundedString(report.revision, 500),
698
+ controls,
699
+ advisories
700
+ };
701
+ }
627
702
  function normalizeUiState(value) {
628
703
  const raw = record(value) ?? {};
629
704
  const pill = record(raw.pill);
@@ -649,6 +724,7 @@ function normalizeUiState(value) {
649
724
  canReduce: raw.canReduce === true,
650
725
  canInterrupt: raw.canInterrupt === true,
651
726
  canRespond: raw.canRespond === true,
727
+ canConfigureSettings: raw.canConfigureSettings === true,
652
728
  messaging: raw.messaging === "live_peer" ? "live_peer" : null,
653
729
  workspace: string(raw.workspace),
654
730
  taskPlan: readTaskPlan(raw.taskPlan),
@@ -657,6 +733,8 @@ function normalizeUiState(value) {
657
733
  exportBackTarget: typeof raw.exportBackTarget === "string" ? raw.exportBackTarget : null,
658
734
  exportReceipt: readExportReceipt(raw.exportReceipt),
659
735
  reductionReceipt: readReductionReceipt(raw.reductionReceipt),
736
+ interopSettings: readInteropSettings(raw.interopSettings),
737
+ interopSettingsError: typeof raw.interopSettingsError === "string" ? boundedString(raw.interopSettingsError) : null,
660
738
  error: typeof raw.error === "string" ? raw.error : null,
661
739
  recoverable: raw.recoverable === true,
662
740
  harnesses: Array.isArray(raw.harnesses) ? raw.harnesses.flatMap((candidate) => {
@@ -688,6 +766,7 @@ function sessionActivity(state, row) {
688
766
  const attention = state.attention.find((item) => item.key === row.key)?.kind;
689
767
  if (attention) return attention;
690
768
  if (row.runtimeStatus === "busy") return "working";
769
+ if (row.runtimeStatus === "running") return "running";
691
770
  if (row.live || row.runtimeStatus === "idle") return "recent";
692
771
  return "idle";
693
772
  }
@@ -745,11 +824,11 @@ function activitySummary(entries) {
745
824
  function canContinueHere(state) {
746
825
  if (state.mode !== "mirror" || state.canSend) return false;
747
826
  const row = state.attached?.key ? state.sessions.find((item) => item.key === state.attached.key) : null;
748
- return state.canResume && row?.runtimeStatus !== "busy" && row?.runtimeStatus !== "idle";
827
+ return state.canResume && row?.runtimeStatus !== "running" && row?.runtimeStatus !== "busy" && row?.runtimeStatus !== "idle";
749
828
  }
750
829
  function operationLabel(operation) {
751
830
  if (!operation) return "";
752
- 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" };
831
+ 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", configureHarness: "Updating harness settings\u2026", loadEarlier: "Loading earlier messages\u2026", loadSessions: "Loading more chats\u2026", refresh: "Retrying\u2026" };
753
832
  return labels[operation] ?? `${operation.replaceAll("_", " ")}\u2026`;
754
833
  }
755
834
  function terminalCommand(handoff) {
@@ -1095,7 +1174,7 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
1095
1174
  if (state.mode !== "mirror" || state.canSend) return null;
1096
1175
  const attached = state.attached;
1097
1176
  const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
1098
- const activeElsewhere = row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
1177
+ const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
1099
1178
  const resume = canContinueHere(state);
1100
1179
  const join = state.canAttach;
1101
1180
  const branch = state.canBranch;
@@ -1979,28 +2058,35 @@ function sessionPathParts(value) {
1979
2058
  trailing: complete.slice(boundary + 1)
1980
2059
  };
1981
2060
  }
1982
- function SessionRow({ row, state, onOpen }) {
2061
+ function SessionRow({ row, state, onOpen, now = Date.now() }) {
1983
2062
  const activity = sessionActivity(state, row);
2063
+ const working = activity === "working";
1984
2064
  const attention = state.attention.find((item) => item.key === row.key);
1985
2065
  const title = sessionDisplayName(row);
1986
2066
  const path = sessionPathParts(row.cwd);
1987
2067
  const preview = row.preview || attention?.preview || "";
1988
2068
  const unreadCount = attention?.unreadCount ?? 0;
1989
2069
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
1990
- return /* @__PURE__ */ jsxs6("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)}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${row.age ? ` \xB7 ${row.age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2070
+ const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2071
+ return /* @__PURE__ */ jsxs6("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)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
1991
2072
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
1992
2073
  /* @__PURE__ */ jsxs6("span", { class: "scui-session-copy", children: [
1993
2074
  /* @__PURE__ */ jsxs6("span", { class: "scui-session-title", children: [
1994
2075
  /* @__PURE__ */ jsx7("strong", { children: title }),
1995
- row.age ? /* @__PURE__ */ jsx7("time", { children: row.age }) : null
2076
+ /* @__PURE__ */ jsx7("span", { class: "scui-session-meta", children: age ? /* @__PURE__ */ jsx7("time", { children: age }) : null })
1996
2077
  ] }),
1997
2078
  path.complete ? /* @__PURE__ */ jsxs6("small", { class: "scui-session-path", title: row.cwd, children: [
1998
2079
  /* @__PURE__ */ jsx7("span", { class: "scui-session-path-leading", children: path.leading }),
1999
2080
  path.separator ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-separator", children: path.separator }) : null,
2000
2081
  path.trailing ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-trailing", children: path.trailing }) : null
2001
2082
  ] }) : null,
2002
- preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
2003
- preview ? /* @__PURE__ */ jsx7("small", { children: preview }) : null,
2083
+ working || preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
2084
+ working ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-working", "aria-hidden": "true", children: [
2085
+ /* @__PURE__ */ jsx7("i", {}),
2086
+ /* @__PURE__ */ jsx7("i", {}),
2087
+ /* @__PURE__ */ jsx7("i", {})
2088
+ ] }) : null,
2089
+ preview || working ? /* @__PURE__ */ jsx7("small", { children: preview || "Working\u2026" }) : null,
2004
2090
  unreadCount ? /* @__PURE__ */ jsx7("b", { "aria-label": `${unreadCount} unread messages`, children: unreadLabel }) : null
2005
2091
  ] }) : null,
2006
2092
  state.attachError?.key === row.key ? /* @__PURE__ */ jsx7("em", { children: state.attachError.message }) : null
@@ -2011,6 +2097,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2011
2097
  const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
2012
2098
  const [query, setQuery] = useState4(remembered.query);
2013
2099
  const [loadingMore, setLoadingMore] = useState4(false);
2100
+ const [now, setNow] = useState4(() => Date.now());
2014
2101
  const root = useRef5(null);
2015
2102
  const rowScroller = useRef5(null);
2016
2103
  const rows = filterSessions(state.sessions, query);
@@ -2026,6 +2113,10 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2026
2113
  useEffect6(() => {
2027
2114
  if (loadingMore) setLoadingMore(false);
2028
2115
  }, [state.error, state.history.hasMoreSessions, state.sessions.length]);
2116
+ useEffect6(() => {
2117
+ const timer = setInterval(() => setNow(Date.now()), 1e4);
2118
+ return () => clearInterval(timer);
2119
+ }, []);
2029
2120
  const loadMore = () => {
2030
2121
  if (loadingMore) return;
2031
2122
  setLoadingMore(true);
@@ -2059,23 +2150,120 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2059
2150
  ] }) : null,
2060
2151
  /* @__PURE__ */ jsxs6("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
2061
2152
  !rows.length && state.startup === "ready" ? /* @__PURE__ */ jsx7("div", { class: "scui-empty", children: query ? "No chats match your search." : state.error ?? "No coding chats found." }) : null,
2062
- rows.map((row) => /* @__PURE__ */ jsx7(Row, { value: row, row, state, adapter, onOpen }, row.key)),
2153
+ rows.map((row) => /* @__PURE__ */ jsx7(Row, { value: row, row, state, adapter, onOpen, now }, row.key)),
2063
2154
  state.history.hasMoreSessions ? /* @__PURE__ */ jsx7("button", { class: "scui-load", type: "button", disabled: loadingMore, onClick: loadMore, children: loadingMore ? "Loading older chats\u2026" : "Load older chats" }) : null
2064
2155
  ] })
2065
2156
  ] });
2066
2157
  }
2067
2158
 
2068
- // src/messenger.jsx
2159
+ // src/settings.jsx
2160
+ import { useEffect as useEffect7, useId as useId2, useMemo as useMemo2, useRef as useRef6, useState as useState5 } from "preact/hooks";
2069
2161
  import { jsx as jsx8, jsxs as jsxs7 } from "preact/jsx-runtime";
2162
+ function HarnessAdvisory({ state, onReview }) {
2163
+ const advisory = state.interopSettings?.advisories[0];
2164
+ if (!advisory && !state.interopSettingsError) return null;
2165
+ return /* @__PURE__ */ jsxs7("aside", { class: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" ? "alert" : "status", children: [
2166
+ /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "!" }),
2167
+ /* @__PURE__ */ jsxs7("span", { children: [
2168
+ /* @__PURE__ */ jsx8("strong", { children: advisory?.title ?? "Could not inspect harness settings" }),
2169
+ /* @__PURE__ */ jsx8("small", { children: advisory?.message ?? state.interopSettingsError })
2170
+ ] }),
2171
+ advisory && state.canConfigureSettings ? /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => onReview(advisory.recommendation.change), children: "Review" }) : null
2172
+ ] });
2173
+ }
2174
+ function initialValues(report, recommendedChange) {
2175
+ return Object.fromEntries(report.controls.map((control) => [
2176
+ control.key,
2177
+ recommendedChange?.key === control.key ? recommendedChange.value : control.configuredValue ?? control.effectiveValue ?? control.choices[0]?.value ?? null
2178
+ ]));
2179
+ }
2180
+ function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = null }) {
2181
+ const report = state.interopSettings;
2182
+ const titleId = useId2();
2183
+ const panel = useRef6(null);
2184
+ const valuesKey = `${report?.revision ?? ""}:${recommendedChange?.key ?? ""}:${recommendedChange?.value ?? ""}`;
2185
+ const defaults = useMemo2(() => report ? initialValues(report, recommendedChange) : {}, [valuesKey]);
2186
+ const [values, setValues] = useState5(defaults);
2187
+ useEffect7(() => setValues(defaults), [defaults]);
2188
+ useEffect7(() => {
2189
+ panel.current?.querySelector("select, button")?.focus({ preventScroll: true });
2190
+ const dismiss = (event) => {
2191
+ if (event.key === "Escape") {
2192
+ event.preventDefault();
2193
+ onClose();
2194
+ }
2195
+ };
2196
+ document.addEventListener("keydown", dismiss);
2197
+ return () => document.removeEventListener("keydown", dismiss);
2198
+ }, [onClose]);
2199
+ if (!report) return /* @__PURE__ */ jsx8("section", { ref: panel, class: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: /* @__PURE__ */ jsxs7("header", { children: [
2200
+ /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }),
2201
+ /* @__PURE__ */ jsxs7("span", { children: [
2202
+ /* @__PURE__ */ jsx8("strong", { id: titleId, children: "Harness settings" }),
2203
+ /* @__PURE__ */ jsx8("small", { children: state.interopSettingsError ?? "No interoperability controls are available." })
2204
+ ] })
2205
+ ] }) });
2206
+ const changed = report.controls.flatMap((control) => values[control.key] !== (control.configuredValue ?? control.effectiveValue ?? control.choices[0]?.value ?? null) ? [{ key: control.key, value: values[control.key] ?? null }] : []);
2207
+ const submit = (event) => {
2208
+ event.preventDefault();
2209
+ if (!changed.length || !state.canConfigureSettings) return;
2210
+ adapter.onIntent({ action: "configureHarness", harness: report.harness, changes: changed, expectedRevision: report.revision });
2211
+ };
2212
+ return /* @__PURE__ */ jsxs7("section", { ref: panel, class: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [
2213
+ /* @__PURE__ */ jsxs7("header", { children: [
2214
+ /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }),
2215
+ /* @__PURE__ */ jsxs7("span", { children: [
2216
+ /* @__PURE__ */ jsxs7("strong", { id: titleId, children: [
2217
+ harnessDisplayName(report.harness),
2218
+ " interoperability"
2219
+ ] }),
2220
+ /* @__PURE__ */ jsx8("small", { children: "Native harness settings used by Supercode" })
2221
+ ] })
2222
+ ] }),
2223
+ /* @__PURE__ */ jsxs7("form", { onSubmit: submit, children: [
2224
+ report.controls.map((control) => {
2225
+ const choice = control.choices.find((item) => item.value === values[control.key]);
2226
+ const recommendation = report.advisories.map((advisory) => advisory.recommendation).find((item) => item.change.key === control.key && item.change.value === values[control.key]);
2227
+ const consequence = choice?.risk ?? recommendation?.consequence;
2228
+ return /* @__PURE__ */ jsxs7("fieldset", { disabled: !control.writable || Boolean(state.operation), children: [
2229
+ /* @__PURE__ */ jsxs7("label", { for: `scui-setting-${control.key}`, children: [
2230
+ /* @__PURE__ */ jsx8("strong", { children: control.label }),
2231
+ /* @__PURE__ */ jsx8("small", { children: control.description })
2232
+ ] }),
2233
+ /* @__PURE__ */ jsxs7("select", { id: `scui-setting-${control.key}`, value: values[control.key] ?? "@default", onChange: (event) => setValues((current) => ({ ...current, [control.key]: event.currentTarget.value === "@default" ? null : event.currentTarget.value })), children: [
2234
+ control.resettable ? /* @__PURE__ */ jsx8("option", { value: "@default", children: "Use harness default" }) : null,
2235
+ control.choices.map((item) => /* @__PURE__ */ jsx8("option", { value: item.value, children: item.label }, item.value))
2236
+ ] }),
2237
+ choice ? /* @__PURE__ */ jsx8("p", { children: choice.description }) : null,
2238
+ consequence ? /* @__PURE__ */ jsxs7("p", { class: "scui-setting-risk", children: [
2239
+ /* @__PURE__ */ jsx8("strong", { children: "Security consequence" }),
2240
+ consequence
2241
+ ] }) : null,
2242
+ /* @__PURE__ */ jsxs7("small", { class: "scui-setting-source", children: [
2243
+ control.effectiveNote,
2244
+ control.sourcePath ? ` Source: ${control.sourcePath}` : ""
2245
+ ] })
2246
+ ] }, control.key);
2247
+ }),
2248
+ /* @__PURE__ */ jsxs7("footer", { children: [
2249
+ /* @__PURE__ */ jsx8("button", { type: "button", onClick: onClose, children: "Cancel" }),
2250
+ /* @__PURE__ */ jsx8("button", { type: "submit", disabled: !changed.length || !state.canConfigureSettings || Boolean(state.operation), children: state.operation === "configureHarness" ? "Applying\u2026" : "Apply changes" })
2251
+ ] })
2252
+ ] })
2253
+ ] });
2254
+ }
2255
+
2256
+ // src/messenger.jsx
2257
+ import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
2070
2258
  var pendingMessageMemory = /* @__PURE__ */ new Map();
2071
2259
  var messengerViewMemory = /* @__PURE__ */ new Map();
2072
2260
  var newChatMemory = /* @__PURE__ */ new Map();
2073
- var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "interrupt", "respond", "refresh", "loadEarlier", "loadSessions"]);
2261
+ var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "interrupt", "respond", "configureHarness", "refresh", "loadEarlier", "loadSessions"]);
2074
2262
  function Receipt({ state, adapter }) {
2075
2263
  const receipt = state.reductionReceipt;
2076
- if (receipt) return /* @__PURE__ */ jsx8("div", { class: "scui-receipt", children: /* @__PURE__ */ jsxs7("span", { children: [
2077
- /* @__PURE__ */ jsx8("strong", { children: "Reduced and verified" }),
2078
- /* @__PURE__ */ jsxs7("small", { children: [
2264
+ if (receipt) return /* @__PURE__ */ jsx9("div", { class: "scui-receipt", children: /* @__PURE__ */ jsxs8("span", { children: [
2265
+ /* @__PURE__ */ jsx9("strong", { children: "Reduced and verified" }),
2266
+ /* @__PURE__ */ jsxs8("small", { children: [
2079
2267
  receipt.sourceTokens.toLocaleString(),
2080
2268
  " \u2192 ",
2081
2269
  receipt.reducedTokens.toLocaleString(),
@@ -2084,36 +2272,36 @@ function Receipt({ state, adapter }) {
2084
2272
  "\xD7 \xB7 reversible"
2085
2273
  ] })
2086
2274
  ] }) });
2087
- if (state.exportReceipt) return /* @__PURE__ */ jsxs7("div", { class: "scui-receipt", children: [
2088
- /* @__PURE__ */ jsxs7("span", { children: [
2089
- /* @__PURE__ */ jsxs7("strong", { children: [
2275
+ if (state.exportReceipt) return /* @__PURE__ */ jsxs8("div", { class: "scui-receipt", children: [
2276
+ /* @__PURE__ */ jsxs8("span", { children: [
2277
+ /* @__PURE__ */ jsxs8("strong", { children: [
2090
2278
  "Lossless export ready \xB7 ",
2091
2279
  harnessDisplayName(state.exportReceipt.targetHarness)
2092
2280
  ] }),
2093
- /* @__PURE__ */ jsxs7("small", { children: [
2281
+ /* @__PURE__ */ jsxs8("small", { children: [
2094
2282
  state.exportReceipt.path,
2095
2283
  " \xB7 ",
2096
2284
  state.exportReceipt.files,
2097
2285
  " files"
2098
2286
  ] })
2099
2287
  ] }),
2100
- /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
2288
+ /* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
2101
2289
  ] });
2102
- if (state.terminalHandoff) return /* @__PURE__ */ jsxs7("div", { class: "scui-receipt", children: [
2103
- /* @__PURE__ */ jsxs7("span", { children: [
2104
- /* @__PURE__ */ jsx8("strong", { children: "Terminal handoff ready" }),
2105
- /* @__PURE__ */ jsx8("small", { children: state.terminalHandoff.cwd })
2290
+ if (state.terminalHandoff) return /* @__PURE__ */ jsxs8("div", { class: "scui-receipt", children: [
2291
+ /* @__PURE__ */ jsxs8("span", { children: [
2292
+ /* @__PURE__ */ jsx9("strong", { children: "Terminal handoff ready" }),
2293
+ /* @__PURE__ */ jsx9("small", { children: state.terminalHandoff.cwd })
2106
2294
  ] }),
2107
- /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
2295
+ /* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
2108
2296
  ] });
2109
2297
  return null;
2110
2298
  }
2111
- function ConversationActions({ state, adapter, actionPending }) {
2112
- const [open, setOpen] = useState5(false);
2113
- const root = useRef6(null);
2114
- const panel = useRef6(null);
2115
- const trigger = useRef6(null);
2116
- const menuId = useId2();
2299
+ function ConversationActions({ state, adapter, actionPending, onSettings }) {
2300
+ const [open, setOpen] = useState6(false);
2301
+ const root = useRef7(null);
2302
+ const panel = useRef7(null);
2303
+ const trigger = useRef7(null);
2304
+ const menuId = useId3();
2117
2305
  const targets = state.harnesses.filter((item) => item.startable);
2118
2306
  const groups = [
2119
2307
  {
@@ -2121,7 +2309,8 @@ function ConversationActions({ state, adapter, actionPending }) {
2121
2309
  items: [
2122
2310
  state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
2123
2311
  state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
2124
- state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null
2312
+ state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null,
2313
+ state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null
2125
2314
  ].filter(Boolean)
2126
2315
  },
2127
2316
  {
@@ -2133,7 +2322,7 @@ function ConversationActions({ state, adapter, actionPending }) {
2133
2322
  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 } })) : []
2134
2323
  }
2135
2324
  ].filter((group) => group.items.length);
2136
- useEffect7(() => {
2325
+ useEffect8(() => {
2137
2326
  if (!open) return;
2138
2327
  panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
2139
2328
  const dismiss = (event) => {
@@ -2152,9 +2341,10 @@ function ConversationActions({ state, adapter, actionPending }) {
2152
2341
  document.removeEventListener("pointerdown", dismiss, true);
2153
2342
  };
2154
2343
  }, [open]);
2155
- const dispatch = (intent) => {
2344
+ const dispatch = (item) => {
2156
2345
  setOpen(false);
2157
- adapter.onIntent(intent);
2346
+ if (item.onSelect) item.onSelect();
2347
+ else adapter.onIntent(item.intent);
2158
2348
  };
2159
2349
  const navigate = (event) => {
2160
2350
  if (event.key === "Escape") {
@@ -2172,62 +2362,63 @@ function ConversationActions({ state, adapter, actionPending }) {
2172
2362
  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;
2173
2363
  items[index].focus({ preventScroll: true });
2174
2364
  };
2175
- return /* @__PURE__ */ jsxs7("div", { class: "scui-menu", ref: root, onBlur: (event) => {
2365
+ return /* @__PURE__ */ jsxs8("div", { class: "scui-menu", ref: root, onBlur: (event) => {
2176
2366
  if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
2177
2367
  }, children: [
2178
- /* @__PURE__ */ jsx8("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__ */ jsx8(UiIcon, { name: "menu", size: 18 }) }),
2179
- open ? /* @__PURE__ */ jsx8("div", { ref: panel, id: menuId, class: "scui-menu-panel", role: "menu", "aria-label": "Conversation actions", onKeyDown: navigate, children: groups.map((group) => /* @__PURE__ */ jsxs7("section", { role: "group", "aria-label": group.label, children: [
2180
- /* @__PURE__ */ jsx8("strong", { children: group.label }),
2181
- group.items.map((item) => /* @__PURE__ */ jsx8("button", { role: "menuitem", type: "button", disabled: actionPending, onClick: () => dispatch(item.intent), children: item.label }, item.key))
2368
+ /* @__PURE__ */ jsx9("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__ */ jsx9(UiIcon, { name: "menu", size: 18 }) }),
2369
+ open ? /* @__PURE__ */ jsx9("div", { ref: panel, id: menuId, class: "scui-menu-panel", role: "menu", "aria-label": "Conversation actions", onKeyDown: navigate, children: groups.map((group) => /* @__PURE__ */ jsxs8("section", { role: "group", "aria-label": group.label, children: [
2370
+ /* @__PURE__ */ jsx9("strong", { children: group.label }),
2371
+ group.items.map((item) => /* @__PURE__ */ jsx9("button", { role: "menuitem", type: "button", disabled: actionPending, onClick: () => dispatch(item), children: item.label }, item.key))
2182
2372
  ] }, group.label)) }) : null
2183
2373
  ] });
2184
2374
  }
2185
- function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
2186
- const back = useRef6(null);
2375
+ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose, onSettings }) {
2376
+ const back = useRef7(null);
2187
2377
  const harness = state.attached?.harness ?? state.harness;
2188
2378
  const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
2189
2379
  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";
2190
- const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
2191
- useEffect7(() => {
2380
+ const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce || state.interopSettings || state.interopSettingsError;
2381
+ useEffect8(() => {
2192
2382
  if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
2193
2383
  }, []);
2194
- return /* @__PURE__ */ jsxs7("header", { class: "scui-head scui-chat-head", children: [
2195
- /* @__PURE__ */ jsx8("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx8(UiIcon, { name: "back", size: 18 }) }),
2196
- /* @__PURE__ */ jsx8(HarnessLogo, { id: harness, size: 28 }),
2197
- /* @__PURE__ */ jsxs7("span", { class: "scui-head-copy", children: [
2198
- /* @__PURE__ */ jsx8("strong", { children: title }),
2199
- /* @__PURE__ */ jsxs7("small", { children: [
2384
+ return /* @__PURE__ */ jsxs8("header", { class: "scui-head scui-chat-head", children: [
2385
+ /* @__PURE__ */ jsx9("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
2386
+ /* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 28 }),
2387
+ /* @__PURE__ */ jsxs8("span", { class: "scui-head-copy", children: [
2388
+ /* @__PURE__ */ jsx9("strong", { children: title }),
2389
+ /* @__PURE__ */ jsxs8("small", { children: [
2200
2390
  harnessDisplayName(harness),
2201
2391
  " \xB7 ",
2202
2392
  status
2203
2393
  ] })
2204
2394
  ] }),
2205
- menu ? /* @__PURE__ */ jsx8(ConversationActions, { state, adapter, actionPending }) : null,
2206
- /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx8(UiIcon, { name: "plus", size: 18 }) }),
2207
- onClose ? /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }) : null
2395
+ menu ? /* @__PURE__ */ jsx9(ConversationActions, { state, adapter, actionPending, onSettings }) : null,
2396
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx9(UiIcon, { name: "plus", size: 18 }) }),
2397
+ onClose ? /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(UiIcon, { name: "close", size: 18 }) }) : null
2208
2398
  ] });
2209
2399
  }
2210
2400
  function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
2211
2401
  const Header = slots.header;
2212
2402
  const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
2213
- const [pending, setPendingState] = useState5(() => pendingMessageMemory.get(memoryKey) ?? null);
2214
- const [pendingAction, setPendingAction] = useState5(null);
2215
- const [restoreDraft, setRestoreDraft] = useState5(null);
2216
- const restoreSequence = useRef6(0);
2217
- const actionSequence = useRef6(0);
2218
- const acknowledged = useRef6(/* @__PURE__ */ new Set());
2403
+ const [pending, setPendingState] = useState6(() => pendingMessageMemory.get(memoryKey) ?? null);
2404
+ const [pendingAction, setPendingAction] = useState6(null);
2405
+ const [restoreDraft, setRestoreDraft] = useState6(null);
2406
+ const [settings, setSettings] = useState6(null);
2407
+ const restoreSequence = useRef7(0);
2408
+ const actionSequence = useRef7(0);
2409
+ const acknowledged = useRef7(/* @__PURE__ */ new Set());
2219
2410
  const setPending = (update) => setPendingState((current) => {
2220
2411
  const next = typeof update === "function" ? update(current) : update;
2221
2412
  if (next) boundedSet(pendingMessageMemory, memoryKey, next);
2222
2413
  else pendingMessageMemory.delete(memoryKey);
2223
2414
  return next;
2224
2415
  });
2225
- useEffect7(() => {
2416
+ useEffect8(() => {
2226
2417
  setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
2227
2418
  setPendingAction(null);
2228
2419
  setRestoreDraft(null);
2229
2420
  }, [memoryKey]);
2230
- useEffect7(() => {
2421
+ useEffect8(() => {
2231
2422
  if (!pending) return;
2232
2423
  if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
2233
2424
  setPending(null);
@@ -2256,7 +2447,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2256
2447
  setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
2257
2448
  setPending({ ...pending, status: "editing" });
2258
2449
  };
2259
- useEffect7(() => {
2450
+ useEffect8(() => {
2260
2451
  const key = state.attached?.key;
2261
2452
  if (!key || !state.attention.some((item) => item.key === key)) {
2262
2453
  if (key) acknowledged.current.delete(key);
@@ -2267,7 +2458,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2267
2458
  adapter.onIntent({ action: "ack", key });
2268
2459
  }
2269
2460
  }, [adapter, state.attached?.key, state.attention]);
2270
- const trackedAdapter = useMemo2(() => ({
2461
+ const trackedAdapter = useMemo3(() => ({
2271
2462
  ...adapter,
2272
2463
  onIntent(intent) {
2273
2464
  if (!TRACKED_ACTIONS.has(intent.action)) return adapter.onIntent(intent);
@@ -2291,7 +2482,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2291
2482
  return result;
2292
2483
  }
2293
2484
  }), [adapter, state.error]);
2294
- useEffect7(() => {
2485
+ useEffect8(() => {
2295
2486
  if (!pendingAction) return;
2296
2487
  if (state.operation && !pendingAction.seenOperation) {
2297
2488
  setPendingAction({ ...pendingAction, seenOperation: true });
@@ -2304,44 +2495,48 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2304
2495
  const actionLabel = operationLabel(action);
2305
2496
  const actionState = action ? { ...state, operation: action, canInterrupt: false, canRespond: false } : state;
2306
2497
  const unreadAfterMessages = state.attention.find((item) => item.key === state.attached?.key)?.afterMessages ?? null;
2307
- return /* @__PURE__ */ jsxs7("section", { class: "scui-chat", children: [
2308
- Header ? /* @__PURE__ */ jsx8(Header, { state: actionState, adapter: trackedAdapter, value: null }) : /* @__PURE__ */ jsx8(ChatHeader, { state: actionState, adapter: trackedAdapter, pendingStatus: pending?.status ?? null, actionPending: Boolean(action), onBack, onNew, onClose }),
2309
- actionLabel ? /* @__PURE__ */ jsxs7("div", { class: "scui-operation", role: "status", children: [
2310
- /* @__PURE__ */ jsx8("i", {}),
2498
+ const Advisory = components.HarnessAdvisory ?? HarnessAdvisory;
2499
+ const SettingsPanel = components.HarnessSettingsPanel ?? HarnessSettingsPanel;
2500
+ return /* @__PURE__ */ jsxs8("section", { class: "scui-chat", children: [
2501
+ Header ? /* @__PURE__ */ jsx9(Header, { state: actionState, adapter: trackedAdapter, value: null }) : /* @__PURE__ */ jsx9(ChatHeader, { state: actionState, adapter: trackedAdapter, pendingStatus: pending?.status ?? null, actionPending: Boolean(action), onBack, onNew, onClose, onSettings: () => setSettings({ recommendedChange: null }) }),
2502
+ actionLabel ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
2503
+ /* @__PURE__ */ jsx9("i", {}),
2311
2504
  actionLabel
2312
2505
  ] }) : null,
2313
- state.error ? /* @__PURE__ */ jsxs7("div", { class: "scui-error", role: "alert", children: [
2314
- /* @__PURE__ */ jsx8("span", { children: state.error }),
2315
- state.recoverable ? /* @__PURE__ */ jsx8("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
2506
+ state.error ? /* @__PURE__ */ jsxs8("div", { class: "scui-error", role: "alert", children: [
2507
+ /* @__PURE__ */ jsx9("span", { children: state.error }),
2508
+ state.recoverable ? /* @__PURE__ */ jsx9("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
2316
2509
  ] }) : null,
2317
- /* @__PURE__ */ jsx8(Receipt, { state, adapter }),
2318
- /* @__PURE__ */ jsx8(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
2319
- /* @__PURE__ */ jsx8(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
2320
- state.mode !== "mirror" || state.canSend ? /* @__PURE__ */ jsx8(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
2510
+ /* @__PURE__ */ jsx9(Receipt, { state, adapter }),
2511
+ /* @__PURE__ */ jsx9(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
2512
+ /* @__PURE__ */ jsx9(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
2513
+ /* @__PURE__ */ jsx9(Advisory, { state: actionState, adapter: trackedAdapter, value: state.interopSettings?.advisories[0] ?? null, onReview: (recommendedChange) => setSettings({ recommendedChange }) }),
2514
+ state.mode !== "mirror" || state.canSend ? /* @__PURE__ */ jsx9(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,
2515
+ settings ? /* @__PURE__ */ jsx9(SettingsPanel, { state: actionState, adapter: trackedAdapter, value: state.interopSettings, recommendedChange: settings.recommendedChange, onClose: () => setSettings(null) }) : null
2321
2516
  ] });
2322
2517
  }
2323
2518
  function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey }) {
2324
2519
  const startable = state.harnesses.filter((item) => item.startable);
2325
2520
  const startableKey = startable.map((item) => item.id).join("\0");
2326
2521
  const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "", context: [], images: [] };
2327
- const [harness, setHarness] = useState5(remembered.harness);
2328
- const [draft, setDraft] = useState5(remembered.draft);
2329
- const [context, setContext] = useState5(remembered.context);
2330
- const [images, setImages] = useState5(remembered.images ?? []);
2331
- const [starting, setStarting] = useState5(null);
2332
- const [picking, setPicking] = useState5(false);
2333
- const [dragging, setDragging] = useState5(false);
2334
- const [pickerError, setPickerError] = useState5(null);
2335
- const startSequence = useRef6(0);
2336
- const textarea = useRef6(null);
2522
+ const [harness, setHarness] = useState6(remembered.harness);
2523
+ const [draft, setDraft] = useState6(remembered.draft);
2524
+ const [context, setContext] = useState6(remembered.context);
2525
+ const [images, setImages] = useState6(remembered.images ?? []);
2526
+ const [starting, setStarting] = useState6(null);
2527
+ const [picking, setPicking] = useState6(false);
2528
+ const [dragging, setDragging] = useState6(false);
2529
+ const [pickerError, setPickerError] = useState6(null);
2530
+ const startSequence = useRef7(0);
2531
+ const textarea = useRef7(null);
2337
2532
  useAutosizeTextarea(textarea, draft);
2338
- useEffect7(() => {
2533
+ useEffect8(() => {
2339
2534
  if (startable.some((item) => item.id === harness)) return;
2340
2535
  const next = startable[0]?.id ?? "";
2341
2536
  setHarness(next);
2342
2537
  boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
2343
2538
  }, [harness, startableKey]);
2344
- useEffect7(() => {
2539
+ useEffect8(() => {
2345
2540
  if (!starting) return;
2346
2541
  const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
2347
2542
  const beganWorking = !starting.busy && state.busy;
@@ -2349,10 +2544,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2349
2544
  newChatMemory.delete(memoryKey);
2350
2545
  onStarted();
2351
2546
  }, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
2352
- useEffect7(() => {
2547
+ useEffect8(() => {
2353
2548
  if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
2354
2549
  }, [starting, state.busy, state.error, state.operation]);
2355
- useEffect7(() => {
2550
+ useEffect8(() => {
2356
2551
  textarea.current?.focus({ preventScroll: true });
2357
2552
  }, []);
2358
2553
  const pickContext = () => {
@@ -2414,49 +2609,49 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2414
2609
  Promise.resolve(result).catch(() => setStarting((current) => current?.id === id ? null : current));
2415
2610
  }
2416
2611
  };
2417
- return /* @__PURE__ */ jsxs7("section", { class: "scui-chat", children: [
2418
- /* @__PURE__ */ jsxs7("header", { class: "scui-head", children: [
2419
- /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx8(UiIcon, { name: "back", size: 18 }) }),
2420
- /* @__PURE__ */ jsxs7("span", { class: "scui-head-copy", children: [
2421
- /* @__PURE__ */ jsx8("strong", { children: labels.newChat }),
2422
- /* @__PURE__ */ jsx8("small", { children: "No session is created until you send" })
2612
+ return /* @__PURE__ */ jsxs8("section", { class: "scui-chat", children: [
2613
+ /* @__PURE__ */ jsxs8("header", { class: "scui-head", children: [
2614
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
2615
+ /* @__PURE__ */ jsxs8("span", { class: "scui-head-copy", children: [
2616
+ /* @__PURE__ */ jsx9("strong", { children: labels.newChat }),
2617
+ /* @__PURE__ */ jsx9("small", { children: "No session is created until you send" })
2423
2618
  ] }),
2424
- onClose ? /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }) : null
2619
+ onClose ? /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(UiIcon, { name: "close", size: 18 }) }) : null
2425
2620
  ] }),
2426
- starting ? /* @__PURE__ */ jsxs7("div", { class: "scui-operation", role: "status", children: [
2427
- /* @__PURE__ */ jsx8("i", {}),
2621
+ starting ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
2622
+ /* @__PURE__ */ jsx9("i", {}),
2428
2623
  operationLabel("start")
2429
2624
  ] }) : null,
2430
- /* @__PURE__ */ jsxs7("div", { class: "scui-new", children: [
2431
- /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "\u2726" }),
2432
- /* @__PURE__ */ jsx8("strong", { children: "What should the agent build or fix?" }),
2433
- /* @__PURE__ */ jsx8("small", { children: "Choose a coding harness and send the first message." })
2625
+ /* @__PURE__ */ jsxs8("div", { class: "scui-new", children: [
2626
+ /* @__PURE__ */ jsx9("span", { "aria-hidden": "true", children: "\u2726" }),
2627
+ /* @__PURE__ */ jsx9("strong", { children: "What should the agent build or fix?" }),
2628
+ /* @__PURE__ */ jsx9("small", { children: "Choose a coding harness and send the first message." })
2434
2629
  ] }),
2435
- /* @__PURE__ */ jsxs7("div", { class: "scui-compose", children: [
2436
- /* @__PURE__ */ jsxs7("label", { class: "scui-harness-picker", children: [
2437
- /* @__PURE__ */ jsx8(HarnessLogo, { id: harness, size: 24 }),
2438
- /* @__PURE__ */ jsx8("span", { children: "Coding harness" }),
2439
- /* @__PURE__ */ jsx8("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
2630
+ /* @__PURE__ */ jsxs8("div", { class: "scui-compose", children: [
2631
+ /* @__PURE__ */ jsxs8("label", { class: "scui-harness-picker", children: [
2632
+ /* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 24 }),
2633
+ /* @__PURE__ */ jsx9("span", { children: "Coding harness" }),
2634
+ /* @__PURE__ */ jsx9("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
2440
2635
  const value = event.currentTarget.value;
2441
2636
  setHarness(value);
2442
2637
  boundedSet(newChatMemory, memoryKey, { harness: value, draft, context, images });
2443
- }, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs7("option", { value: item.id, disabled: !item.startable, children: [
2638
+ }, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs8("option", { value: item.id, disabled: !item.startable, children: [
2444
2639
  item.label,
2445
2640
  item.startable ? "" : " \xB7 unavailable"
2446
2641
  ] }, item.id)) })
2447
2642
  ] }),
2448
- /* @__PURE__ */ jsx8(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
2643
+ /* @__PURE__ */ jsx9(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
2449
2644
  const next = items.filter((_, itemIndex) => itemIndex !== index);
2450
2645
  boundedSet(newChatMemory, memoryKey, { harness, draft, context, images: next });
2451
2646
  return next;
2452
2647
  }) }),
2453
- /* @__PURE__ */ jsx8(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
2648
+ /* @__PURE__ */ jsx9(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
2454
2649
  const next = items.filter((_, itemIndex) => itemIndex !== index);
2455
2650
  boundedSet(newChatMemory, memoryKey, { harness, draft, context: next, images });
2456
2651
  return next;
2457
2652
  }) }),
2458
- pickerError ? /* @__PURE__ */ jsx8("small", { class: "scui-context-error", role: "alert", children: pickerError }) : null,
2459
- /* @__PURE__ */ jsxs7("div", { class: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
2653
+ pickerError ? /* @__PURE__ */ jsx9("small", { class: "scui-context-error", role: "alert", children: pickerError }) : null,
2654
+ /* @__PURE__ */ jsxs8("div", { class: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
2460
2655
  if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
2461
2656
  event.preventDefault();
2462
2657
  setDragging(true);
@@ -2466,8 +2661,8 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2466
2661
  }, onDragLeave: (event) => {
2467
2662
  if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
2468
2663
  }, onDrop: dropImages, children: [
2469
- adapter.pickContext ? /* @__PURE__ */ jsx8("button", { class: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx8("i", { class: "scui-control-spinner" }) : /* @__PURE__ */ jsx8(UiIcon, { name: "attach", size: 17 }) }) : null,
2470
- /* @__PURE__ */ jsx8("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), onPaste: pasteImages, onInput: (event) => {
2664
+ adapter.pickContext ? /* @__PURE__ */ jsx9("button", { class: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx9("i", { class: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "attach", size: 17 }) }) : null,
2665
+ /* @__PURE__ */ jsx9("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), onPaste: pasteImages, onInput: (event) => {
2471
2666
  const value = event.currentTarget.value;
2472
2667
  setDraft(value);
2473
2668
  boundedSet(newChatMemory, memoryKey, { harness, draft: value, context, images });
@@ -2477,23 +2672,23 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2477
2672
  send();
2478
2673
  }
2479
2674
  } }),
2480
- /* @__PURE__ */ jsx8("span", { children: /* @__PURE__ */ jsx8("button", { type: "button", class: "scui-send", "aria-label": "Start chat", disabled: !draft.trim() && !images.length || !harness || Boolean(starting), onClick: send, children: starting ? /* @__PURE__ */ jsx8("i", { class: "scui-control-spinner" }) : /* @__PURE__ */ jsx8(UiIcon, { name: "send", size: 17 }) }) })
2675
+ /* @__PURE__ */ jsx9("span", { children: /* @__PURE__ */ jsx9("button", { type: "button", class: "scui-send", "aria-label": "Start chat", disabled: !draft.trim() && !images.length || !harness || Boolean(starting), onClick: send, children: starting ? /* @__PURE__ */ jsx9("i", { class: "scui-control-spinner" }) : /* @__PURE__ */ jsx9(UiIcon, { name: "send", size: 17 }) }) })
2481
2676
  ] })
2482
2677
  ] })
2483
2678
  ] });
2484
2679
  }
2485
2680
  function SupercodeMessenger({ state: stateInput, adapter, class: className = "", initialView, labels, components = {}, slots = {} }) {
2486
- const state = useMemo2(() => normalizeUiState(stateInput), [stateInput]);
2681
+ const state = useMemo3(() => normalizeUiState(stateInput), [stateInput]);
2487
2682
  const copy = { ...DEFAULT_LABELS, ...labels };
2488
2683
  const memoryKey = state.workspace || "@default";
2489
- const [view, setViewState] = useState5(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
2490
- const [opening, setOpening] = useState5(null);
2491
- const [listFocus, setListFocus] = useState5(null);
2684
+ const [view, setViewState] = useState6(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
2685
+ const [opening, setOpening] = useState6(null);
2686
+ const [listFocus, setListFocus] = useState6(null);
2492
2687
  const setView = (next) => {
2493
2688
  boundedSet(messengerViewMemory, memoryKey, next);
2494
2689
  setViewState(next);
2495
2690
  };
2496
- useEffect7(() => {
2691
+ useEffect8(() => {
2497
2692
  if (!opening) return;
2498
2693
  if (state.attached?.key === opening.key) {
2499
2694
  setOpening(null);
@@ -2509,31 +2704,31 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
2509
2704
  };
2510
2705
  const close = () => adapter.onClose?.();
2511
2706
  const Footer = slots.footer;
2512
- return /* @__PURE__ */ jsxs7("main", { class: `scui-root ${className}`, "data-view": view, "data-mode": state.mode, "aria-label": "Supercode messenger", children: [
2513
- view === "list" ? /* @__PURE__ */ jsx8(SessionList, { state, adapter, focusKey: listFocus, onOpen: open, onNew: () => {
2707
+ return /* @__PURE__ */ jsxs8("main", { class: `scui-root ${className}`, "data-view": view, "data-mode": state.mode, "aria-label": "Supercode messenger", children: [
2708
+ view === "list" ? /* @__PURE__ */ jsx9(SessionList, { state, adapter, focusKey: listFocus, onOpen: open, onNew: () => {
2514
2709
  setListFocus("@new");
2515
2710
  setView("new");
2516
2711
  }, onClose: adapter.onClose ? close : void 0, components, labels: copy, memoryKey }) : null,
2517
- view === "new" ? /* @__PURE__ */ jsx8(NewChat, { state, adapter, onBack: () => setView("list"), onClose: adapter.onClose ? close : void 0, onStarted: () => setView("chat"), labels: copy, memoryKey }) : null,
2518
- view === "chat" ? /* @__PURE__ */ jsx8(Chat, { state, adapter, onBack: () => {
2712
+ view === "new" ? /* @__PURE__ */ jsx9(NewChat, { state, adapter, onBack: () => setView("list"), onClose: adapter.onClose ? close : void 0, onStarted: () => setView("chat"), labels: copy, memoryKey }) : null,
2713
+ view === "chat" ? /* @__PURE__ */ jsx9(Chat, { state, adapter, onBack: () => {
2519
2714
  setListFocus(state.attached?.key ?? listFocus);
2520
2715
  setView("list");
2521
2716
  }, onNew: () => {
2522
2717
  setListFocus("@new");
2523
2718
  setView("new");
2524
2719
  }, onClose: adapter.onClose ? close : void 0, components, slots, labels: copy }) : null,
2525
- opening ? /* @__PURE__ */ jsxs7("div", { class: "scui-opening", role: "status", "aria-busy": "true", children: [
2526
- /* @__PURE__ */ jsx8(HarnessLogo, { id: opening.harness, size: 34 }),
2527
- /* @__PURE__ */ jsxs7("span", { children: [
2528
- /* @__PURE__ */ jsxs7("strong", { children: [
2720
+ opening ? /* @__PURE__ */ jsxs8("div", { class: "scui-opening", role: "status", "aria-busy": "true", children: [
2721
+ /* @__PURE__ */ jsx9(HarnessLogo, { id: opening.harness, size: 34 }),
2722
+ /* @__PURE__ */ jsxs8("span", { children: [
2723
+ /* @__PURE__ */ jsxs8("strong", { children: [
2529
2724
  "Opening ",
2530
2725
  sessionDisplayName(opening)
2531
2726
  ] }),
2532
- /* @__PURE__ */ jsx8("small", { children: "Loading the latest transcript window\u2026" })
2727
+ /* @__PURE__ */ jsx9("small", { children: "Loading the latest transcript window\u2026" })
2533
2728
  ] }),
2534
- /* @__PURE__ */ jsx8("i", {})
2729
+ /* @__PURE__ */ jsx9("i", {})
2535
2730
  ] }) : null,
2536
- Footer ? /* @__PURE__ */ jsx8(Footer, { state, adapter, value: copy }) : null
2731
+ Footer ? /* @__PURE__ */ jsx9(Footer, { state, adapter, value: copy }) : null
2537
2732
  ] });
2538
2733
  }
2539
2734
  export {