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