@volter-ai-dev/supercode-ui 0.1.29 → 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,6 +558,7 @@ 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,
@@ -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) => {
@@ -750,7 +828,7 @@ function canContinueHere(state) {
750
828
  }
751
829
  function operationLabel(operation) {
752
830
  if (!operation) return "";
753
- 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" };
754
832
  return labels[operation] ?? `${operation.replaceAll("_", " ")}\u2026`;
755
833
  }
756
834
  function terminalCommand(handoff) {
@@ -1980,28 +2058,35 @@ function sessionPathParts(value) {
1980
2058
  trailing: complete.slice(boundary + 1)
1981
2059
  };
1982
2060
  }
1983
- function SessionRow({ row, state, onOpen }) {
2061
+ function SessionRow({ row, state, onOpen, now = Date.now() }) {
1984
2062
  const activity = sessionActivity(state, row);
2063
+ const working = activity === "working";
1985
2064
  const attention = state.attention.find((item) => item.key === row.key);
1986
2065
  const title = sessionDisplayName(row);
1987
2066
  const path = sessionPathParts(row.cwd);
1988
2067
  const preview = row.preview || attention?.preview || "";
1989
2068
  const unreadCount = attention?.unreadCount ?? 0;
1990
2069
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
1991
- 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: [
1992
2072
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
1993
2073
  /* @__PURE__ */ jsxs6("span", { class: "scui-session-copy", children: [
1994
2074
  /* @__PURE__ */ jsxs6("span", { class: "scui-session-title", children: [
1995
2075
  /* @__PURE__ */ jsx7("strong", { children: title }),
1996
- 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 })
1997
2077
  ] }),
1998
2078
  path.complete ? /* @__PURE__ */ jsxs6("small", { class: "scui-session-path", title: row.cwd, children: [
1999
2079
  /* @__PURE__ */ jsx7("span", { class: "scui-session-path-leading", children: path.leading }),
2000
2080
  path.separator ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-separator", children: path.separator }) : null,
2001
2081
  path.trailing ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-trailing", children: path.trailing }) : null
2002
2082
  ] }) : null,
2003
- preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
2004
- 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,
2005
2090
  unreadCount ? /* @__PURE__ */ jsx7("b", { "aria-label": `${unreadCount} unread messages`, children: unreadLabel }) : null
2006
2091
  ] }) : null,
2007
2092
  state.attachError?.key === row.key ? /* @__PURE__ */ jsx7("em", { children: state.attachError.message }) : null
@@ -2012,6 +2097,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2012
2097
  const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
2013
2098
  const [query, setQuery] = useState4(remembered.query);
2014
2099
  const [loadingMore, setLoadingMore] = useState4(false);
2100
+ const [now, setNow] = useState4(() => Date.now());
2015
2101
  const root = useRef5(null);
2016
2102
  const rowScroller = useRef5(null);
2017
2103
  const rows = filterSessions(state.sessions, query);
@@ -2027,6 +2113,10 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2027
2113
  useEffect6(() => {
2028
2114
  if (loadingMore) setLoadingMore(false);
2029
2115
  }, [state.error, state.history.hasMoreSessions, state.sessions.length]);
2116
+ useEffect6(() => {
2117
+ const timer = setInterval(() => setNow(Date.now()), 1e4);
2118
+ return () => clearInterval(timer);
2119
+ }, []);
2030
2120
  const loadMore = () => {
2031
2121
  if (loadingMore) return;
2032
2122
  setLoadingMore(true);
@@ -2060,23 +2150,120 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2060
2150
  ] }) : null,
2061
2151
  /* @__PURE__ */ jsxs6("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
2062
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,
2063
- 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)),
2064
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
2065
2155
  ] })
2066
2156
  ] });
2067
2157
  }
2068
2158
 
2069
- // 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";
2070
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";
2071
2258
  var pendingMessageMemory = /* @__PURE__ */ new Map();
2072
2259
  var messengerViewMemory = /* @__PURE__ */ new Map();
2073
2260
  var newChatMemory = /* @__PURE__ */ new Map();
2074
- 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"]);
2075
2262
  function Receipt({ state, adapter }) {
2076
2263
  const receipt = state.reductionReceipt;
2077
- if (receipt) return /* @__PURE__ */ jsx8("div", { class: "scui-receipt", children: /* @__PURE__ */ jsxs7("span", { children: [
2078
- /* @__PURE__ */ jsx8("strong", { children: "Reduced and verified" }),
2079
- /* @__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: [
2080
2267
  receipt.sourceTokens.toLocaleString(),
2081
2268
  " \u2192 ",
2082
2269
  receipt.reducedTokens.toLocaleString(),
@@ -2085,36 +2272,36 @@ function Receipt({ state, adapter }) {
2085
2272
  "\xD7 \xB7 reversible"
2086
2273
  ] })
2087
2274
  ] }) });
2088
- if (state.exportReceipt) return /* @__PURE__ */ jsxs7("div", { class: "scui-receipt", children: [
2089
- /* @__PURE__ */ jsxs7("span", { children: [
2090
- /* @__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: [
2091
2278
  "Lossless export ready \xB7 ",
2092
2279
  harnessDisplayName(state.exportReceipt.targetHarness)
2093
2280
  ] }),
2094
- /* @__PURE__ */ jsxs7("small", { children: [
2281
+ /* @__PURE__ */ jsxs8("small", { children: [
2095
2282
  state.exportReceipt.path,
2096
2283
  " \xB7 ",
2097
2284
  state.exportReceipt.files,
2098
2285
  " files"
2099
2286
  ] })
2100
2287
  ] }),
2101
- /* @__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" })
2102
2289
  ] });
2103
- if (state.terminalHandoff) return /* @__PURE__ */ jsxs7("div", { class: "scui-receipt", children: [
2104
- /* @__PURE__ */ jsxs7("span", { children: [
2105
- /* @__PURE__ */ jsx8("strong", { children: "Terminal handoff ready" }),
2106
- /* @__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 })
2107
2294
  ] }),
2108
- /* @__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" })
2109
2296
  ] });
2110
2297
  return null;
2111
2298
  }
2112
- function ConversationActions({ state, adapter, actionPending }) {
2113
- const [open, setOpen] = useState5(false);
2114
- const root = useRef6(null);
2115
- const panel = useRef6(null);
2116
- const trigger = useRef6(null);
2117
- 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();
2118
2305
  const targets = state.harnesses.filter((item) => item.startable);
2119
2306
  const groups = [
2120
2307
  {
@@ -2122,7 +2309,8 @@ function ConversationActions({ state, adapter, actionPending }) {
2122
2309
  items: [
2123
2310
  state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
2124
2311
  state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
2125
- 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
2126
2314
  ].filter(Boolean)
2127
2315
  },
2128
2316
  {
@@ -2134,7 +2322,7 @@ function ConversationActions({ state, adapter, actionPending }) {
2134
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 } })) : []
2135
2323
  }
2136
2324
  ].filter((group) => group.items.length);
2137
- useEffect7(() => {
2325
+ useEffect8(() => {
2138
2326
  if (!open) return;
2139
2327
  panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
2140
2328
  const dismiss = (event) => {
@@ -2153,9 +2341,10 @@ function ConversationActions({ state, adapter, actionPending }) {
2153
2341
  document.removeEventListener("pointerdown", dismiss, true);
2154
2342
  };
2155
2343
  }, [open]);
2156
- const dispatch = (intent) => {
2344
+ const dispatch = (item) => {
2157
2345
  setOpen(false);
2158
- adapter.onIntent(intent);
2346
+ if (item.onSelect) item.onSelect();
2347
+ else adapter.onIntent(item.intent);
2159
2348
  };
2160
2349
  const navigate = (event) => {
2161
2350
  if (event.key === "Escape") {
@@ -2173,62 +2362,63 @@ function ConversationActions({ state, adapter, actionPending }) {
2173
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;
2174
2363
  items[index].focus({ preventScroll: true });
2175
2364
  };
2176
- return /* @__PURE__ */ jsxs7("div", { class: "scui-menu", ref: root, onBlur: (event) => {
2365
+ return /* @__PURE__ */ jsxs8("div", { class: "scui-menu", ref: root, onBlur: (event) => {
2177
2366
  if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
2178
2367
  }, children: [
2179
- /* @__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 }) }),
2180
- 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: [
2181
- /* @__PURE__ */ jsx8("strong", { children: group.label }),
2182
- 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))
2183
2372
  ] }, group.label)) }) : null
2184
2373
  ] });
2185
2374
  }
2186
- function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
2187
- const back = useRef6(null);
2375
+ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose, onSettings }) {
2376
+ const back = useRef7(null);
2188
2377
  const harness = state.attached?.harness ?? state.harness;
2189
2378
  const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
2190
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";
2191
- const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
2192
- useEffect7(() => {
2380
+ const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce || state.interopSettings || state.interopSettingsError;
2381
+ useEffect8(() => {
2193
2382
  if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
2194
2383
  }, []);
2195
- return /* @__PURE__ */ jsxs7("header", { class: "scui-head scui-chat-head", children: [
2196
- /* @__PURE__ */ jsx8("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx8(UiIcon, { name: "back", size: 18 }) }),
2197
- /* @__PURE__ */ jsx8(HarnessLogo, { id: harness, size: 28 }),
2198
- /* @__PURE__ */ jsxs7("span", { class: "scui-head-copy", children: [
2199
- /* @__PURE__ */ jsx8("strong", { children: title }),
2200
- /* @__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: [
2201
2390
  harnessDisplayName(harness),
2202
2391
  " \xB7 ",
2203
2392
  status
2204
2393
  ] })
2205
2394
  ] }),
2206
- menu ? /* @__PURE__ */ jsx8(ConversationActions, { state, adapter, actionPending }) : null,
2207
- /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx8(UiIcon, { name: "plus", size: 18 }) }),
2208
- 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
2209
2398
  ] });
2210
2399
  }
2211
2400
  function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
2212
2401
  const Header = slots.header;
2213
2402
  const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
2214
- const [pending, setPendingState] = useState5(() => pendingMessageMemory.get(memoryKey) ?? null);
2215
- const [pendingAction, setPendingAction] = useState5(null);
2216
- const [restoreDraft, setRestoreDraft] = useState5(null);
2217
- const restoreSequence = useRef6(0);
2218
- const actionSequence = useRef6(0);
2219
- 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());
2220
2410
  const setPending = (update) => setPendingState((current) => {
2221
2411
  const next = typeof update === "function" ? update(current) : update;
2222
2412
  if (next) boundedSet(pendingMessageMemory, memoryKey, next);
2223
2413
  else pendingMessageMemory.delete(memoryKey);
2224
2414
  return next;
2225
2415
  });
2226
- useEffect7(() => {
2416
+ useEffect8(() => {
2227
2417
  setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
2228
2418
  setPendingAction(null);
2229
2419
  setRestoreDraft(null);
2230
2420
  }, [memoryKey]);
2231
- useEffect7(() => {
2421
+ useEffect8(() => {
2232
2422
  if (!pending) return;
2233
2423
  if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
2234
2424
  setPending(null);
@@ -2257,7 +2447,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2257
2447
  setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
2258
2448
  setPending({ ...pending, status: "editing" });
2259
2449
  };
2260
- useEffect7(() => {
2450
+ useEffect8(() => {
2261
2451
  const key = state.attached?.key;
2262
2452
  if (!key || !state.attention.some((item) => item.key === key)) {
2263
2453
  if (key) acknowledged.current.delete(key);
@@ -2268,7 +2458,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2268
2458
  adapter.onIntent({ action: "ack", key });
2269
2459
  }
2270
2460
  }, [adapter, state.attached?.key, state.attention]);
2271
- const trackedAdapter = useMemo2(() => ({
2461
+ const trackedAdapter = useMemo3(() => ({
2272
2462
  ...adapter,
2273
2463
  onIntent(intent) {
2274
2464
  if (!TRACKED_ACTIONS.has(intent.action)) return adapter.onIntent(intent);
@@ -2292,7 +2482,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2292
2482
  return result;
2293
2483
  }
2294
2484
  }), [adapter, state.error]);
2295
- useEffect7(() => {
2485
+ useEffect8(() => {
2296
2486
  if (!pendingAction) return;
2297
2487
  if (state.operation && !pendingAction.seenOperation) {
2298
2488
  setPendingAction({ ...pendingAction, seenOperation: true });
@@ -2305,44 +2495,48 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2305
2495
  const actionLabel = operationLabel(action);
2306
2496
  const actionState = action ? { ...state, operation: action, canInterrupt: false, canRespond: false } : state;
2307
2497
  const unreadAfterMessages = state.attention.find((item) => item.key === state.attached?.key)?.afterMessages ?? null;
2308
- return /* @__PURE__ */ jsxs7("section", { class: "scui-chat", children: [
2309
- 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 }),
2310
- actionLabel ? /* @__PURE__ */ jsxs7("div", { class: "scui-operation", role: "status", children: [
2311
- /* @__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", {}),
2312
2504
  actionLabel
2313
2505
  ] }) : null,
2314
- state.error ? /* @__PURE__ */ jsxs7("div", { class: "scui-error", role: "alert", children: [
2315
- /* @__PURE__ */ jsx8("span", { children: state.error }),
2316
- 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
2317
2509
  ] }) : null,
2318
- /* @__PURE__ */ jsx8(Receipt, { state, adapter }),
2319
- /* @__PURE__ */ jsx8(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
2320
- /* @__PURE__ */ jsx8(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
2321
- 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
2322
2516
  ] });
2323
2517
  }
2324
2518
  function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey }) {
2325
2519
  const startable = state.harnesses.filter((item) => item.startable);
2326
2520
  const startableKey = startable.map((item) => item.id).join("\0");
2327
2521
  const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "", context: [], images: [] };
2328
- const [harness, setHarness] = useState5(remembered.harness);
2329
- const [draft, setDraft] = useState5(remembered.draft);
2330
- const [context, setContext] = useState5(remembered.context);
2331
- const [images, setImages] = useState5(remembered.images ?? []);
2332
- const [starting, setStarting] = useState5(null);
2333
- const [picking, setPicking] = useState5(false);
2334
- const [dragging, setDragging] = useState5(false);
2335
- const [pickerError, setPickerError] = useState5(null);
2336
- const startSequence = useRef6(0);
2337
- 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);
2338
2532
  useAutosizeTextarea(textarea, draft);
2339
- useEffect7(() => {
2533
+ useEffect8(() => {
2340
2534
  if (startable.some((item) => item.id === harness)) return;
2341
2535
  const next = startable[0]?.id ?? "";
2342
2536
  setHarness(next);
2343
2537
  boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
2344
2538
  }, [harness, startableKey]);
2345
- useEffect7(() => {
2539
+ useEffect8(() => {
2346
2540
  if (!starting) return;
2347
2541
  const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
2348
2542
  const beganWorking = !starting.busy && state.busy;
@@ -2350,10 +2544,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2350
2544
  newChatMemory.delete(memoryKey);
2351
2545
  onStarted();
2352
2546
  }, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
2353
- useEffect7(() => {
2547
+ useEffect8(() => {
2354
2548
  if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
2355
2549
  }, [starting, state.busy, state.error, state.operation]);
2356
- useEffect7(() => {
2550
+ useEffect8(() => {
2357
2551
  textarea.current?.focus({ preventScroll: true });
2358
2552
  }, []);
2359
2553
  const pickContext = () => {
@@ -2415,49 +2609,49 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2415
2609
  Promise.resolve(result).catch(() => setStarting((current) => current?.id === id ? null : current));
2416
2610
  }
2417
2611
  };
2418
- return /* @__PURE__ */ jsxs7("section", { class: "scui-chat", children: [
2419
- /* @__PURE__ */ jsxs7("header", { class: "scui-head", children: [
2420
- /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx8(UiIcon, { name: "back", size: 18 }) }),
2421
- /* @__PURE__ */ jsxs7("span", { class: "scui-head-copy", children: [
2422
- /* @__PURE__ */ jsx8("strong", { children: labels.newChat }),
2423
- /* @__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" })
2424
2618
  ] }),
2425
- 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
2426
2620
  ] }),
2427
- starting ? /* @__PURE__ */ jsxs7("div", { class: "scui-operation", role: "status", children: [
2428
- /* @__PURE__ */ jsx8("i", {}),
2621
+ starting ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
2622
+ /* @__PURE__ */ jsx9("i", {}),
2429
2623
  operationLabel("start")
2430
2624
  ] }) : null,
2431
- /* @__PURE__ */ jsxs7("div", { class: "scui-new", children: [
2432
- /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "\u2726" }),
2433
- /* @__PURE__ */ jsx8("strong", { children: "What should the agent build or fix?" }),
2434
- /* @__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." })
2435
2629
  ] }),
2436
- /* @__PURE__ */ jsxs7("div", { class: "scui-compose", children: [
2437
- /* @__PURE__ */ jsxs7("label", { class: "scui-harness-picker", children: [
2438
- /* @__PURE__ */ jsx8(HarnessLogo, { id: harness, size: 24 }),
2439
- /* @__PURE__ */ jsx8("span", { children: "Coding harness" }),
2440
- /* @__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) => {
2441
2635
  const value = event.currentTarget.value;
2442
2636
  setHarness(value);
2443
2637
  boundedSet(newChatMemory, memoryKey, { harness: value, draft, context, images });
2444
- }, 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: [
2445
2639
  item.label,
2446
2640
  item.startable ? "" : " \xB7 unavailable"
2447
2641
  ] }, item.id)) })
2448
2642
  ] }),
2449
- /* @__PURE__ */ jsx8(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
2643
+ /* @__PURE__ */ jsx9(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
2450
2644
  const next = items.filter((_, itemIndex) => itemIndex !== index);
2451
2645
  boundedSet(newChatMemory, memoryKey, { harness, draft, context, images: next });
2452
2646
  return next;
2453
2647
  }) }),
2454
- /* @__PURE__ */ jsx8(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
2648
+ /* @__PURE__ */ jsx9(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
2455
2649
  const next = items.filter((_, itemIndex) => itemIndex !== index);
2456
2650
  boundedSet(newChatMemory, memoryKey, { harness, draft, context: next, images });
2457
2651
  return next;
2458
2652
  }) }),
2459
- pickerError ? /* @__PURE__ */ jsx8("small", { class: "scui-context-error", role: "alert", children: pickerError }) : null,
2460
- /* @__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) => {
2461
2655
  if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
2462
2656
  event.preventDefault();
2463
2657
  setDragging(true);
@@ -2467,8 +2661,8 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2467
2661
  }, onDragLeave: (event) => {
2468
2662
  if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
2469
2663
  }, onDrop: dropImages, children: [
2470
- 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,
2471
- /* @__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) => {
2472
2666
  const value = event.currentTarget.value;
2473
2667
  setDraft(value);
2474
2668
  boundedSet(newChatMemory, memoryKey, { harness, draft: value, context, images });
@@ -2478,23 +2672,23 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2478
2672
  send();
2479
2673
  }
2480
2674
  } }),
2481
- /* @__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 }) }) })
2482
2676
  ] })
2483
2677
  ] })
2484
2678
  ] });
2485
2679
  }
2486
2680
  function SupercodeMessenger({ state: stateInput, adapter, class: className = "", initialView, labels, components = {}, slots = {} }) {
2487
- const state = useMemo2(() => normalizeUiState(stateInput), [stateInput]);
2681
+ const state = useMemo3(() => normalizeUiState(stateInput), [stateInput]);
2488
2682
  const copy = { ...DEFAULT_LABELS, ...labels };
2489
2683
  const memoryKey = state.workspace || "@default";
2490
- const [view, setViewState] = useState5(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
2491
- const [opening, setOpening] = useState5(null);
2492
- 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);
2493
2687
  const setView = (next) => {
2494
2688
  boundedSet(messengerViewMemory, memoryKey, next);
2495
2689
  setViewState(next);
2496
2690
  };
2497
- useEffect7(() => {
2691
+ useEffect8(() => {
2498
2692
  if (!opening) return;
2499
2693
  if (state.attached?.key === opening.key) {
2500
2694
  setOpening(null);
@@ -2510,31 +2704,31 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
2510
2704
  };
2511
2705
  const close = () => adapter.onClose?.();
2512
2706
  const Footer = slots.footer;
2513
- return /* @__PURE__ */ jsxs7("main", { class: `scui-root ${className}`, "data-view": view, "data-mode": state.mode, "aria-label": "Supercode messenger", children: [
2514
- 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: () => {
2515
2709
  setListFocus("@new");
2516
2710
  setView("new");
2517
2711
  }, onClose: adapter.onClose ? close : void 0, components, labels: copy, memoryKey }) : null,
2518
- view === "new" ? /* @__PURE__ */ jsx8(NewChat, { state, adapter, onBack: () => setView("list"), onClose: adapter.onClose ? close : void 0, onStarted: () => setView("chat"), labels: copy, memoryKey }) : null,
2519
- 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: () => {
2520
2714
  setListFocus(state.attached?.key ?? listFocus);
2521
2715
  setView("list");
2522
2716
  }, onNew: () => {
2523
2717
  setListFocus("@new");
2524
2718
  setView("new");
2525
2719
  }, onClose: adapter.onClose ? close : void 0, components, slots, labels: copy }) : null,
2526
- opening ? /* @__PURE__ */ jsxs7("div", { class: "scui-opening", role: "status", "aria-busy": "true", children: [
2527
- /* @__PURE__ */ jsx8(HarnessLogo, { id: opening.harness, size: 34 }),
2528
- /* @__PURE__ */ jsxs7("span", { children: [
2529
- /* @__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: [
2530
2724
  "Opening ",
2531
2725
  sessionDisplayName(opening)
2532
2726
  ] }),
2533
- /* @__PURE__ */ jsx8("small", { children: "Loading the latest transcript window\u2026" })
2727
+ /* @__PURE__ */ jsx9("small", { children: "Loading the latest transcript window\u2026" })
2534
2728
  ] }),
2535
- /* @__PURE__ */ jsx8("i", {})
2729
+ /* @__PURE__ */ jsx9("i", {})
2536
2730
  ] }) : null,
2537
- Footer ? /* @__PURE__ */ jsx8(Footer, { state, adapter, value: copy }) : null
2731
+ Footer ? /* @__PURE__ */ jsx9(Footer, { state, adapter, value: copy }) : null
2538
2732
  ] });
2539
2733
  }
2540
2734
  export {