@volter-ai-dev/supercode-ui 0.1.29 → 0.1.31

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/components.mjs CHANGED
@@ -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) {
@@ -1987,7 +2065,7 @@ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
1987
2065
  }
1988
2066
 
1989
2067
  // src/messenger.jsx
1990
- import { useEffect as useEffect7, useId as useId2, useMemo as useMemo2, useRef as useRef6, useState as useState5 } from "preact/hooks";
2068
+ import { useEffect as useEffect8, useId as useId3, useMemo as useMemo3, useRef as useRef7, useState as useState6 } from "preact/hooks";
1991
2069
 
1992
2070
  // src/sessions.jsx
1993
2071
  import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState4 } from "preact/hooks";
@@ -2004,28 +2082,35 @@ function sessionPathParts(value) {
2004
2082
  trailing: complete.slice(boundary + 1)
2005
2083
  };
2006
2084
  }
2007
- function SessionRow({ row, state, onOpen }) {
2085
+ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2008
2086
  const activity = sessionActivity(state, row);
2087
+ const working = activity === "working";
2009
2088
  const attention = state.attention.find((item) => item.key === row.key);
2010
2089
  const title = sessionDisplayName(row);
2011
2090
  const path = sessionPathParts(row.cwd);
2012
2091
  const preview = row.preview || attention?.preview || "";
2013
2092
  const unreadCount = attention?.unreadCount ?? 0;
2014
2093
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2015
- 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: [
2094
+ const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2095
+ 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: [
2016
2096
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
2017
2097
  /* @__PURE__ */ jsxs6("span", { class: "scui-session-copy", children: [
2018
2098
  /* @__PURE__ */ jsxs6("span", { class: "scui-session-title", children: [
2019
2099
  /* @__PURE__ */ jsx7("strong", { children: title }),
2020
- row.age ? /* @__PURE__ */ jsx7("time", { children: row.age }) : null
2100
+ /* @__PURE__ */ jsx7("span", { class: "scui-session-meta", children: age ? /* @__PURE__ */ jsx7("time", { children: age }) : null })
2021
2101
  ] }),
2022
2102
  path.complete ? /* @__PURE__ */ jsxs6("small", { class: "scui-session-path", title: row.cwd, children: [
2023
2103
  /* @__PURE__ */ jsx7("span", { class: "scui-session-path-leading", children: path.leading }),
2024
2104
  path.separator ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-separator", children: path.separator }) : null,
2025
2105
  path.trailing ? /* @__PURE__ */ jsx7("span", { class: "scui-session-path-trailing", children: path.trailing }) : null
2026
2106
  ] }) : null,
2027
- preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
2028
- preview ? /* @__PURE__ */ jsx7("small", { children: preview }) : null,
2107
+ working || preview || unreadCount ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-preview", children: [
2108
+ working ? /* @__PURE__ */ jsxs6("span", { class: "scui-session-working", "aria-hidden": "true", children: [
2109
+ /* @__PURE__ */ jsx7("i", {}),
2110
+ /* @__PURE__ */ jsx7("i", {}),
2111
+ /* @__PURE__ */ jsx7("i", {})
2112
+ ] }) : null,
2113
+ preview || working ? /* @__PURE__ */ jsx7("small", { children: preview || "Working\u2026" }) : null,
2029
2114
  unreadCount ? /* @__PURE__ */ jsx7("b", { "aria-label": `${unreadCount} unread messages`, children: unreadLabel }) : null
2030
2115
  ] }) : null,
2031
2116
  state.attachError?.key === row.key ? /* @__PURE__ */ jsx7("em", { children: state.attachError.message }) : null
@@ -2036,6 +2121,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2036
2121
  const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
2037
2122
  const [query, setQuery] = useState4(remembered.query);
2038
2123
  const [loadingMore, setLoadingMore] = useState4(false);
2124
+ const [now, setNow] = useState4(() => Date.now());
2039
2125
  const root = useRef5(null);
2040
2126
  const rowScroller = useRef5(null);
2041
2127
  const rows = filterSessions(state.sessions, query);
@@ -2051,6 +2137,10 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2051
2137
  useEffect6(() => {
2052
2138
  if (loadingMore) setLoadingMore(false);
2053
2139
  }, [state.error, state.history.hasMoreSessions, state.sessions.length]);
2140
+ useEffect6(() => {
2141
+ const timer = setInterval(() => setNow(Date.now()), 1e4);
2142
+ return () => clearInterval(timer);
2143
+ }, []);
2054
2144
  const loadMore = () => {
2055
2145
  if (loadingMore) return;
2056
2146
  setLoadingMore(true);
@@ -2084,23 +2174,120 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
2084
2174
  ] }) : null,
2085
2175
  /* @__PURE__ */ jsxs6("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
2086
2176
  !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,
2087
- rows.map((row) => /* @__PURE__ */ jsx7(Row, { value: row, row, state, adapter, onOpen }, row.key)),
2177
+ rows.map((row) => /* @__PURE__ */ jsx7(Row, { value: row, row, state, adapter, onOpen, now }, row.key)),
2088
2178
  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
2089
2179
  ] })
2090
2180
  ] });
2091
2181
  }
2092
2182
 
2093
- // src/messenger.jsx
2183
+ // src/settings.jsx
2184
+ import { useEffect as useEffect7, useId as useId2, useMemo as useMemo2, useRef as useRef6, useState as useState5 } from "preact/hooks";
2094
2185
  import { jsx as jsx8, jsxs as jsxs7 } from "preact/jsx-runtime";
2186
+ function HarnessAdvisory({ state, onReview }) {
2187
+ const advisory = state.interopSettings?.advisories[0];
2188
+ if (!advisory && !state.interopSettingsError) return null;
2189
+ return /* @__PURE__ */ jsxs7("div", { class: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" || !advisory ? "alert" : "status", children: [
2190
+ /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "!" }),
2191
+ /* @__PURE__ */ jsxs7("span", { children: [
2192
+ /* @__PURE__ */ jsx8("strong", { children: advisory?.title ?? "Could not inspect harness settings" }),
2193
+ /* @__PURE__ */ jsx8("small", { children: advisory?.message ?? state.interopSettingsError })
2194
+ ] }),
2195
+ advisory && state.canConfigureSettings ? /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => onReview(advisory.recommendation.change), children: "Review" }) : null
2196
+ ] });
2197
+ }
2198
+ function initialValues(report, recommendedChange) {
2199
+ return Object.fromEntries(report.controls.map((control) => [
2200
+ control.key,
2201
+ recommendedChange?.key === control.key ? recommendedChange.value : control.configuredValue ?? control.effectiveValue ?? control.choices[0]?.value ?? null
2202
+ ]));
2203
+ }
2204
+ function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = null }) {
2205
+ const report = state.interopSettings;
2206
+ const titleId = useId2();
2207
+ const panel = useRef6(null);
2208
+ const valuesKey = `${report?.revision ?? ""}:${recommendedChange?.key ?? ""}:${recommendedChange?.value ?? ""}`;
2209
+ const defaults = useMemo2(() => report ? initialValues(report, recommendedChange) : {}, [valuesKey]);
2210
+ const [values, setValues] = useState5(defaults);
2211
+ useEffect7(() => setValues(defaults), [defaults]);
2212
+ useEffect7(() => {
2213
+ panel.current?.querySelector("select, button")?.focus({ preventScroll: true });
2214
+ const dismiss = (event) => {
2215
+ if (event.key === "Escape") {
2216
+ event.preventDefault();
2217
+ onClose();
2218
+ }
2219
+ };
2220
+ document.addEventListener("keydown", dismiss);
2221
+ return () => document.removeEventListener("keydown", dismiss);
2222
+ }, [onClose]);
2223
+ if (!report) return /* @__PURE__ */ jsx8("section", { ref: panel, class: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: /* @__PURE__ */ jsxs7("header", { children: [
2224
+ /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }),
2225
+ /* @__PURE__ */ jsxs7("span", { children: [
2226
+ /* @__PURE__ */ jsx8("strong", { id: titleId, children: "Harness settings" }),
2227
+ /* @__PURE__ */ jsx8("small", { children: state.interopSettingsError ?? "No interoperability controls are available." })
2228
+ ] })
2229
+ ] }) });
2230
+ 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 }] : []);
2231
+ const submit = (event) => {
2232
+ event.preventDefault();
2233
+ if (!changed.length || !state.canConfigureSettings) return;
2234
+ adapter.onIntent({ action: "configureHarness", harness: report.harness, changes: changed, expectedRevision: report.revision });
2235
+ };
2236
+ return /* @__PURE__ */ jsxs7("section", { ref: panel, class: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [
2237
+ /* @__PURE__ */ jsxs7("header", { children: [
2238
+ /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }),
2239
+ /* @__PURE__ */ jsxs7("span", { children: [
2240
+ /* @__PURE__ */ jsxs7("strong", { id: titleId, children: [
2241
+ harnessDisplayName(report.harness),
2242
+ " interoperability"
2243
+ ] }),
2244
+ /* @__PURE__ */ jsx8("small", { children: "Native harness settings used by Supercode" })
2245
+ ] })
2246
+ ] }),
2247
+ /* @__PURE__ */ jsxs7("form", { onSubmit: submit, children: [
2248
+ report.controls.map((control) => {
2249
+ const choice = control.choices.find((item) => item.value === values[control.key]);
2250
+ const recommendation = report.advisories.map((advisory) => advisory.recommendation).find((item) => item.change.key === control.key && item.change.value === values[control.key]);
2251
+ const consequence = choice?.risk ?? recommendation?.consequence;
2252
+ return /* @__PURE__ */ jsxs7("fieldset", { disabled: !control.writable || Boolean(state.operation), children: [
2253
+ /* @__PURE__ */ jsxs7("label", { for: `scui-setting-${control.key}`, children: [
2254
+ /* @__PURE__ */ jsx8("strong", { children: control.label }),
2255
+ /* @__PURE__ */ jsx8("small", { children: control.description })
2256
+ ] }),
2257
+ /* @__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: [
2258
+ control.resettable ? /* @__PURE__ */ jsx8("option", { value: "@default", children: "Use harness default" }) : null,
2259
+ control.choices.map((item) => /* @__PURE__ */ jsx8("option", { value: item.value, children: item.label }, item.value))
2260
+ ] }),
2261
+ choice ? /* @__PURE__ */ jsx8("p", { children: choice.description }) : null,
2262
+ consequence ? /* @__PURE__ */ jsxs7("p", { class: "scui-setting-risk", children: [
2263
+ /* @__PURE__ */ jsx8("strong", { children: "Security consequence" }),
2264
+ consequence
2265
+ ] }) : null,
2266
+ /* @__PURE__ */ jsxs7("small", { class: "scui-setting-source", children: [
2267
+ control.effectiveNote,
2268
+ control.sourcePath ? ` Source: ${control.sourcePath}` : ""
2269
+ ] })
2270
+ ] }, control.key);
2271
+ }),
2272
+ /* @__PURE__ */ jsxs7("footer", { children: [
2273
+ /* @__PURE__ */ jsx8("button", { type: "button", onClick: onClose, children: "Cancel" }),
2274
+ /* @__PURE__ */ jsx8("button", { type: "submit", disabled: !changed.length || !state.canConfigureSettings || Boolean(state.operation), children: state.operation === "configureHarness" ? "Applying\u2026" : "Apply changes" })
2275
+ ] })
2276
+ ] })
2277
+ ] });
2278
+ }
2279
+
2280
+ // src/messenger.jsx
2281
+ import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
2095
2282
  var pendingMessageMemory = /* @__PURE__ */ new Map();
2096
2283
  var messengerViewMemory = /* @__PURE__ */ new Map();
2097
2284
  var newChatMemory = /* @__PURE__ */ new Map();
2098
- var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "interrupt", "respond", "refresh", "loadEarlier", "loadSessions"]);
2285
+ var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "interrupt", "respond", "configureHarness", "refresh", "loadEarlier", "loadSessions"]);
2099
2286
  function Receipt({ state, adapter }) {
2100
2287
  const receipt = state.reductionReceipt;
2101
- if (receipt) return /* @__PURE__ */ jsx8("div", { class: "scui-receipt", children: /* @__PURE__ */ jsxs7("span", { children: [
2102
- /* @__PURE__ */ jsx8("strong", { children: "Reduced and verified" }),
2103
- /* @__PURE__ */ jsxs7("small", { children: [
2288
+ if (receipt) return /* @__PURE__ */ jsx9("div", { class: "scui-receipt", children: /* @__PURE__ */ jsxs8("span", { children: [
2289
+ /* @__PURE__ */ jsx9("strong", { children: "Reduced and verified" }),
2290
+ /* @__PURE__ */ jsxs8("small", { children: [
2104
2291
  receipt.sourceTokens.toLocaleString(),
2105
2292
  " \u2192 ",
2106
2293
  receipt.reducedTokens.toLocaleString(),
@@ -2109,36 +2296,36 @@ function Receipt({ state, adapter }) {
2109
2296
  "\xD7 \xB7 reversible"
2110
2297
  ] })
2111
2298
  ] }) });
2112
- if (state.exportReceipt) return /* @__PURE__ */ jsxs7("div", { class: "scui-receipt", children: [
2113
- /* @__PURE__ */ jsxs7("span", { children: [
2114
- /* @__PURE__ */ jsxs7("strong", { children: [
2299
+ if (state.exportReceipt) return /* @__PURE__ */ jsxs8("div", { class: "scui-receipt", children: [
2300
+ /* @__PURE__ */ jsxs8("span", { children: [
2301
+ /* @__PURE__ */ jsxs8("strong", { children: [
2115
2302
  "Lossless export ready \xB7 ",
2116
2303
  harnessDisplayName(state.exportReceipt.targetHarness)
2117
2304
  ] }),
2118
- /* @__PURE__ */ jsxs7("small", { children: [
2305
+ /* @__PURE__ */ jsxs8("small", { children: [
2119
2306
  state.exportReceipt.path,
2120
2307
  " \xB7 ",
2121
2308
  state.exportReceipt.files,
2122
2309
  " files"
2123
2310
  ] })
2124
2311
  ] }),
2125
- /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
2312
+ /* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
2126
2313
  ] });
2127
- if (state.terminalHandoff) return /* @__PURE__ */ jsxs7("div", { class: "scui-receipt", children: [
2128
- /* @__PURE__ */ jsxs7("span", { children: [
2129
- /* @__PURE__ */ jsx8("strong", { children: "Terminal handoff ready" }),
2130
- /* @__PURE__ */ jsx8("small", { children: state.terminalHandoff.cwd })
2314
+ if (state.terminalHandoff) return /* @__PURE__ */ jsxs8("div", { class: "scui-receipt", children: [
2315
+ /* @__PURE__ */ jsxs8("span", { children: [
2316
+ /* @__PURE__ */ jsx9("strong", { children: "Terminal handoff ready" }),
2317
+ /* @__PURE__ */ jsx9("small", { children: state.terminalHandoff.cwd })
2131
2318
  ] }),
2132
- /* @__PURE__ */ jsx8("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
2319
+ /* @__PURE__ */ jsx9("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
2133
2320
  ] });
2134
2321
  return null;
2135
2322
  }
2136
- function ConversationActions({ state, adapter, actionPending }) {
2137
- const [open, setOpen] = useState5(false);
2138
- const root = useRef6(null);
2139
- const panel = useRef6(null);
2140
- const trigger = useRef6(null);
2141
- const menuId = useId2();
2323
+ function ConversationActions({ state, adapter, actionPending, onSettings }) {
2324
+ const [open, setOpen] = useState6(false);
2325
+ const root = useRef7(null);
2326
+ const panel = useRef7(null);
2327
+ const trigger = useRef7(null);
2328
+ const menuId = useId3();
2142
2329
  const targets = state.harnesses.filter((item) => item.startable);
2143
2330
  const groups = [
2144
2331
  {
@@ -2146,7 +2333,8 @@ function ConversationActions({ state, adapter, actionPending }) {
2146
2333
  items: [
2147
2334
  state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
2148
2335
  state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
2149
- state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null
2336
+ state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null,
2337
+ state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null
2150
2338
  ].filter(Boolean)
2151
2339
  },
2152
2340
  {
@@ -2158,7 +2346,7 @@ function ConversationActions({ state, adapter, actionPending }) {
2158
2346
  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 } })) : []
2159
2347
  }
2160
2348
  ].filter((group) => group.items.length);
2161
- useEffect7(() => {
2349
+ useEffect8(() => {
2162
2350
  if (!open) return;
2163
2351
  panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
2164
2352
  const dismiss = (event) => {
@@ -2177,9 +2365,10 @@ function ConversationActions({ state, adapter, actionPending }) {
2177
2365
  document.removeEventListener("pointerdown", dismiss, true);
2178
2366
  };
2179
2367
  }, [open]);
2180
- const dispatch = (intent) => {
2368
+ const dispatch = (item) => {
2181
2369
  setOpen(false);
2182
- adapter.onIntent(intent);
2370
+ if (item.onSelect) item.onSelect();
2371
+ else adapter.onIntent(item.intent);
2183
2372
  };
2184
2373
  const navigate = (event) => {
2185
2374
  if (event.key === "Escape") {
@@ -2197,62 +2386,63 @@ function ConversationActions({ state, adapter, actionPending }) {
2197
2386
  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;
2198
2387
  items[index].focus({ preventScroll: true });
2199
2388
  };
2200
- return /* @__PURE__ */ jsxs7("div", { class: "scui-menu", ref: root, onBlur: (event) => {
2389
+ return /* @__PURE__ */ jsxs8("div", { class: "scui-menu", ref: root, onBlur: (event) => {
2201
2390
  if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
2202
2391
  }, children: [
2203
- /* @__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 }) }),
2204
- 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: [
2205
- /* @__PURE__ */ jsx8("strong", { children: group.label }),
2206
- group.items.map((item) => /* @__PURE__ */ jsx8("button", { role: "menuitem", type: "button", disabled: actionPending, onClick: () => dispatch(item.intent), children: item.label }, item.key))
2392
+ /* @__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 }) }),
2393
+ 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: [
2394
+ /* @__PURE__ */ jsx9("strong", { children: group.label }),
2395
+ group.items.map((item) => /* @__PURE__ */ jsx9("button", { role: "menuitem", type: "button", disabled: actionPending, onClick: () => dispatch(item), children: item.label }, item.key))
2207
2396
  ] }, group.label)) }) : null
2208
2397
  ] });
2209
2398
  }
2210
- function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
2211
- const back = useRef6(null);
2399
+ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose, onSettings }) {
2400
+ const back = useRef7(null);
2212
2401
  const harness = state.attached?.harness ?? state.harness;
2213
2402
  const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
2214
2403
  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";
2215
- const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
2216
- useEffect7(() => {
2404
+ const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce || state.interopSettings || state.interopSettingsError;
2405
+ useEffect8(() => {
2217
2406
  if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
2218
2407
  }, []);
2219
- return /* @__PURE__ */ jsxs7("header", { class: "scui-head scui-chat-head", children: [
2220
- /* @__PURE__ */ jsx8("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx8(UiIcon, { name: "back", size: 18 }) }),
2221
- /* @__PURE__ */ jsx8(HarnessLogo, { id: harness, size: 28 }),
2222
- /* @__PURE__ */ jsxs7("span", { class: "scui-head-copy", children: [
2223
- /* @__PURE__ */ jsx8("strong", { children: title }),
2224
- /* @__PURE__ */ jsxs7("small", { children: [
2408
+ return /* @__PURE__ */ jsxs8("header", { class: "scui-head scui-chat-head", children: [
2409
+ /* @__PURE__ */ jsx9("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
2410
+ /* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 28 }),
2411
+ /* @__PURE__ */ jsxs8("span", { class: "scui-head-copy", children: [
2412
+ /* @__PURE__ */ jsx9("strong", { children: title }),
2413
+ /* @__PURE__ */ jsxs8("small", { children: [
2225
2414
  harnessDisplayName(harness),
2226
2415
  " \xB7 ",
2227
2416
  status
2228
2417
  ] })
2229
2418
  ] }),
2230
- menu ? /* @__PURE__ */ jsx8(ConversationActions, { state, adapter, actionPending }) : null,
2231
- /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx8(UiIcon, { name: "plus", size: 18 }) }),
2232
- onClose ? /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }) : null
2419
+ menu ? /* @__PURE__ */ jsx9(ConversationActions, { state, adapter, actionPending, onSettings }) : null,
2420
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx9(UiIcon, { name: "plus", size: 18 }) }),
2421
+ onClose ? /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(UiIcon, { name: "close", size: 18 }) }) : null
2233
2422
  ] });
2234
2423
  }
2235
2424
  function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
2236
2425
  const Header = slots.header;
2237
2426
  const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
2238
- const [pending, setPendingState] = useState5(() => pendingMessageMemory.get(memoryKey) ?? null);
2239
- const [pendingAction, setPendingAction] = useState5(null);
2240
- const [restoreDraft, setRestoreDraft] = useState5(null);
2241
- const restoreSequence = useRef6(0);
2242
- const actionSequence = useRef6(0);
2243
- const acknowledged = useRef6(/* @__PURE__ */ new Set());
2427
+ const [pending, setPendingState] = useState6(() => pendingMessageMemory.get(memoryKey) ?? null);
2428
+ const [pendingAction, setPendingAction] = useState6(null);
2429
+ const [restoreDraft, setRestoreDraft] = useState6(null);
2430
+ const [settings, setSettings] = useState6(null);
2431
+ const restoreSequence = useRef7(0);
2432
+ const actionSequence = useRef7(0);
2433
+ const acknowledged = useRef7(/* @__PURE__ */ new Set());
2244
2434
  const setPending = (update) => setPendingState((current) => {
2245
2435
  const next = typeof update === "function" ? update(current) : update;
2246
2436
  if (next) boundedSet(pendingMessageMemory, memoryKey, next);
2247
2437
  else pendingMessageMemory.delete(memoryKey);
2248
2438
  return next;
2249
2439
  });
2250
- useEffect7(() => {
2440
+ useEffect8(() => {
2251
2441
  setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
2252
2442
  setPendingAction(null);
2253
2443
  setRestoreDraft(null);
2254
2444
  }, [memoryKey]);
2255
- useEffect7(() => {
2445
+ useEffect8(() => {
2256
2446
  if (!pending) return;
2257
2447
  if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
2258
2448
  setPending(null);
@@ -2281,7 +2471,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2281
2471
  setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
2282
2472
  setPending({ ...pending, status: "editing" });
2283
2473
  };
2284
- useEffect7(() => {
2474
+ useEffect8(() => {
2285
2475
  const key = state.attached?.key;
2286
2476
  if (!key || !state.attention.some((item) => item.key === key)) {
2287
2477
  if (key) acknowledged.current.delete(key);
@@ -2292,7 +2482,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2292
2482
  adapter.onIntent({ action: "ack", key });
2293
2483
  }
2294
2484
  }, [adapter, state.attached?.key, state.attention]);
2295
- const trackedAdapter = useMemo2(() => ({
2485
+ const trackedAdapter = useMemo3(() => ({
2296
2486
  ...adapter,
2297
2487
  onIntent(intent) {
2298
2488
  if (!TRACKED_ACTIONS.has(intent.action)) return adapter.onIntent(intent);
@@ -2316,7 +2506,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2316
2506
  return result;
2317
2507
  }
2318
2508
  }), [adapter, state.error]);
2319
- useEffect7(() => {
2509
+ useEffect8(() => {
2320
2510
  if (!pendingAction) return;
2321
2511
  if (state.operation && !pendingAction.seenOperation) {
2322
2512
  setPendingAction({ ...pendingAction, seenOperation: true });
@@ -2329,44 +2519,48 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2329
2519
  const actionLabel = operationLabel(action);
2330
2520
  const actionState = action ? { ...state, operation: action, canInterrupt: false, canRespond: false } : state;
2331
2521
  const unreadAfterMessages = state.attention.find((item) => item.key === state.attached?.key)?.afterMessages ?? null;
2332
- return /* @__PURE__ */ jsxs7("section", { class: "scui-chat", children: [
2333
- 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 }),
2334
- actionLabel ? /* @__PURE__ */ jsxs7("div", { class: "scui-operation", role: "status", children: [
2335
- /* @__PURE__ */ jsx8("i", {}),
2522
+ const Advisory = components.HarnessAdvisory ?? HarnessAdvisory;
2523
+ const SettingsPanel = components.HarnessSettingsPanel ?? HarnessSettingsPanel;
2524
+ return /* @__PURE__ */ jsxs8("section", { class: "scui-chat", children: [
2525
+ 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 }) }),
2526
+ actionLabel ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
2527
+ /* @__PURE__ */ jsx9("i", {}),
2336
2528
  actionLabel
2337
2529
  ] }) : null,
2338
- state.error ? /* @__PURE__ */ jsxs7("div", { class: "scui-error", role: "alert", children: [
2339
- /* @__PURE__ */ jsx8("span", { children: state.error }),
2340
- state.recoverable ? /* @__PURE__ */ jsx8("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
2530
+ state.error ? /* @__PURE__ */ jsxs8("div", { class: "scui-error", role: "alert", children: [
2531
+ /* @__PURE__ */ jsx9("span", { children: state.error }),
2532
+ state.recoverable ? /* @__PURE__ */ jsx9("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
2341
2533
  ] }) : null,
2342
- /* @__PURE__ */ jsx8(Receipt, { state, adapter }),
2343
- /* @__PURE__ */ jsx8(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
2344
- /* @__PURE__ */ jsx8(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
2345
- 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
2534
+ /* @__PURE__ */ jsx9(Receipt, { state, adapter }),
2535
+ /* @__PURE__ */ jsx9(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
2536
+ /* @__PURE__ */ jsx9(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
2537
+ /* @__PURE__ */ jsx9(Advisory, { state: actionState, adapter: trackedAdapter, value: state.interopSettings?.advisories[0] ?? null, onReview: (recommendedChange) => setSettings({ recommendedChange }) }),
2538
+ 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,
2539
+ settings ? /* @__PURE__ */ jsx9(SettingsPanel, { state: actionState, adapter: trackedAdapter, value: state.interopSettings, recommendedChange: settings.recommendedChange, onClose: () => setSettings(null) }) : null
2346
2540
  ] });
2347
2541
  }
2348
2542
  function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey }) {
2349
2543
  const startable = state.harnesses.filter((item) => item.startable);
2350
2544
  const startableKey = startable.map((item) => item.id).join("\0");
2351
2545
  const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "", context: [], images: [] };
2352
- const [harness, setHarness] = useState5(remembered.harness);
2353
- const [draft, setDraft] = useState5(remembered.draft);
2354
- const [context, setContext] = useState5(remembered.context);
2355
- const [images, setImages] = useState5(remembered.images ?? []);
2356
- const [starting, setStarting] = useState5(null);
2357
- const [picking, setPicking] = useState5(false);
2358
- const [dragging, setDragging] = useState5(false);
2359
- const [pickerError, setPickerError] = useState5(null);
2360
- const startSequence = useRef6(0);
2361
- const textarea = useRef6(null);
2546
+ const [harness, setHarness] = useState6(remembered.harness);
2547
+ const [draft, setDraft] = useState6(remembered.draft);
2548
+ const [context, setContext] = useState6(remembered.context);
2549
+ const [images, setImages] = useState6(remembered.images ?? []);
2550
+ const [starting, setStarting] = useState6(null);
2551
+ const [picking, setPicking] = useState6(false);
2552
+ const [dragging, setDragging] = useState6(false);
2553
+ const [pickerError, setPickerError] = useState6(null);
2554
+ const startSequence = useRef7(0);
2555
+ const textarea = useRef7(null);
2362
2556
  useAutosizeTextarea(textarea, draft);
2363
- useEffect7(() => {
2557
+ useEffect8(() => {
2364
2558
  if (startable.some((item) => item.id === harness)) return;
2365
2559
  const next = startable[0]?.id ?? "";
2366
2560
  setHarness(next);
2367
2561
  boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
2368
2562
  }, [harness, startableKey]);
2369
- useEffect7(() => {
2563
+ useEffect8(() => {
2370
2564
  if (!starting) return;
2371
2565
  const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
2372
2566
  const beganWorking = !starting.busy && state.busy;
@@ -2374,10 +2568,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2374
2568
  newChatMemory.delete(memoryKey);
2375
2569
  onStarted();
2376
2570
  }, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
2377
- useEffect7(() => {
2571
+ useEffect8(() => {
2378
2572
  if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
2379
2573
  }, [starting, state.busy, state.error, state.operation]);
2380
- useEffect7(() => {
2574
+ useEffect8(() => {
2381
2575
  textarea.current?.focus({ preventScroll: true });
2382
2576
  }, []);
2383
2577
  const pickContext = () => {
@@ -2439,49 +2633,49 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2439
2633
  Promise.resolve(result).catch(() => setStarting((current) => current?.id === id ? null : current));
2440
2634
  }
2441
2635
  };
2442
- return /* @__PURE__ */ jsxs7("section", { class: "scui-chat", children: [
2443
- /* @__PURE__ */ jsxs7("header", { class: "scui-head", children: [
2444
- /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx8(UiIcon, { name: "back", size: 18 }) }),
2445
- /* @__PURE__ */ jsxs7("span", { class: "scui-head-copy", children: [
2446
- /* @__PURE__ */ jsx8("strong", { children: labels.newChat }),
2447
- /* @__PURE__ */ jsx8("small", { children: "No session is created until you send" })
2636
+ return /* @__PURE__ */ jsxs8("section", { class: "scui-chat", children: [
2637
+ /* @__PURE__ */ jsxs8("header", { class: "scui-head", children: [
2638
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
2639
+ /* @__PURE__ */ jsxs8("span", { class: "scui-head-copy", children: [
2640
+ /* @__PURE__ */ jsx9("strong", { children: labels.newChat }),
2641
+ /* @__PURE__ */ jsx9("small", { children: "No session is created until you send" })
2448
2642
  ] }),
2449
- onClose ? /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx8(UiIcon, { name: "close", size: 18 }) }) : null
2643
+ onClose ? /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx9(UiIcon, { name: "close", size: 18 }) }) : null
2450
2644
  ] }),
2451
- starting ? /* @__PURE__ */ jsxs7("div", { class: "scui-operation", role: "status", children: [
2452
- /* @__PURE__ */ jsx8("i", {}),
2645
+ starting ? /* @__PURE__ */ jsxs8("div", { class: "scui-operation", role: "status", children: [
2646
+ /* @__PURE__ */ jsx9("i", {}),
2453
2647
  operationLabel("start")
2454
2648
  ] }) : null,
2455
- /* @__PURE__ */ jsxs7("div", { class: "scui-new", children: [
2456
- /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "\u2726" }),
2457
- /* @__PURE__ */ jsx8("strong", { children: "What should the agent build or fix?" }),
2458
- /* @__PURE__ */ jsx8("small", { children: "Choose a coding harness and send the first message." })
2649
+ /* @__PURE__ */ jsxs8("div", { class: "scui-new", children: [
2650
+ /* @__PURE__ */ jsx9("span", { "aria-hidden": "true", children: "\u2726" }),
2651
+ /* @__PURE__ */ jsx9("strong", { children: "What should the agent build or fix?" }),
2652
+ /* @__PURE__ */ jsx9("small", { children: "Choose a coding harness and send the first message." })
2459
2653
  ] }),
2460
- /* @__PURE__ */ jsxs7("div", { class: "scui-compose", children: [
2461
- /* @__PURE__ */ jsxs7("label", { class: "scui-harness-picker", children: [
2462
- /* @__PURE__ */ jsx8(HarnessLogo, { id: harness, size: 24 }),
2463
- /* @__PURE__ */ jsx8("span", { children: "Coding harness" }),
2464
- /* @__PURE__ */ jsx8("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
2654
+ /* @__PURE__ */ jsxs8("div", { class: "scui-compose", children: [
2655
+ /* @__PURE__ */ jsxs8("label", { class: "scui-harness-picker", children: [
2656
+ /* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 24 }),
2657
+ /* @__PURE__ */ jsx9("span", { children: "Coding harness" }),
2658
+ /* @__PURE__ */ jsx9("select", { value: harness, disabled: Boolean(starting), onChange: (event) => {
2465
2659
  const value = event.currentTarget.value;
2466
2660
  setHarness(value);
2467
2661
  boundedSet(newChatMemory, memoryKey, { harness: value, draft, context, images });
2468
- }, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs7("option", { value: item.id, disabled: !item.startable, children: [
2662
+ }, children: state.harnesses.map((item) => /* @__PURE__ */ jsxs8("option", { value: item.id, disabled: !item.startable, children: [
2469
2663
  item.label,
2470
2664
  item.startable ? "" : " \xB7 unavailable"
2471
2665
  ] }, item.id)) })
2472
2666
  ] }),
2473
- /* @__PURE__ */ jsx8(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
2667
+ /* @__PURE__ */ jsx9(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
2474
2668
  const next = items.filter((_, itemIndex) => itemIndex !== index);
2475
2669
  boundedSet(newChatMemory, memoryKey, { harness, draft, context, images: next });
2476
2670
  return next;
2477
2671
  }) }),
2478
- /* @__PURE__ */ jsx8(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
2672
+ /* @__PURE__ */ jsx9(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
2479
2673
  const next = items.filter((_, itemIndex) => itemIndex !== index);
2480
2674
  boundedSet(newChatMemory, memoryKey, { harness, draft, context: next, images });
2481
2675
  return next;
2482
2676
  }) }),
2483
- pickerError ? /* @__PURE__ */ jsx8("small", { class: "scui-context-error", role: "alert", children: pickerError }) : null,
2484
- /* @__PURE__ */ jsxs7("div", { class: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
2677
+ pickerError ? /* @__PURE__ */ jsx9("small", { class: "scui-context-error", role: "alert", children: pickerError }) : null,
2678
+ /* @__PURE__ */ jsxs8("div", { class: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
2485
2679
  if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
2486
2680
  event.preventDefault();
2487
2681
  setDragging(true);
@@ -2491,8 +2685,8 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2491
2685
  }, onDragLeave: (event) => {
2492
2686
  if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
2493
2687
  }, onDrop: dropImages, children: [
2494
- 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,
2495
- /* @__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) => {
2688
+ 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,
2689
+ /* @__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) => {
2496
2690
  const value = event.currentTarget.value;
2497
2691
  setDraft(value);
2498
2692
  boundedSet(newChatMemory, memoryKey, { harness, draft: value, context, images });
@@ -2502,23 +2696,23 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2502
2696
  send();
2503
2697
  }
2504
2698
  } }),
2505
- /* @__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 }) }) })
2699
+ /* @__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 }) }) })
2506
2700
  ] })
2507
2701
  ] })
2508
2702
  ] });
2509
2703
  }
2510
2704
  function SupercodeMessenger({ state: stateInput, adapter, class: className = "", initialView, labels, components = {}, slots = {} }) {
2511
- const state = useMemo2(() => normalizeUiState(stateInput), [stateInput]);
2705
+ const state = useMemo3(() => normalizeUiState(stateInput), [stateInput]);
2512
2706
  const copy = { ...DEFAULT_LABELS, ...labels };
2513
2707
  const memoryKey = state.workspace || "@default";
2514
- const [view, setViewState] = useState5(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
2515
- const [opening, setOpening] = useState5(null);
2516
- const [listFocus, setListFocus] = useState5(null);
2708
+ const [view, setViewState] = useState6(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
2709
+ const [opening, setOpening] = useState6(null);
2710
+ const [listFocus, setListFocus] = useState6(null);
2517
2711
  const setView = (next) => {
2518
2712
  boundedSet(messengerViewMemory, memoryKey, next);
2519
2713
  setViewState(next);
2520
2714
  };
2521
- useEffect7(() => {
2715
+ useEffect8(() => {
2522
2716
  if (!opening) return;
2523
2717
  if (state.attached?.key === opening.key) {
2524
2718
  setOpening(null);
@@ -2534,31 +2728,31 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
2534
2728
  };
2535
2729
  const close = () => adapter.onClose?.();
2536
2730
  const Footer = slots.footer;
2537
- return /* @__PURE__ */ jsxs7("main", { class: `scui-root ${className}`, "data-view": view, "data-mode": state.mode, "aria-label": "Supercode messenger", children: [
2538
- view === "list" ? /* @__PURE__ */ jsx8(SessionList, { state, adapter, focusKey: listFocus, onOpen: open, onNew: () => {
2731
+ return /* @__PURE__ */ jsxs8("main", { class: `scui-root ${className}`, "data-view": view, "data-mode": state.mode, "aria-label": "Supercode messenger", children: [
2732
+ view === "list" ? /* @__PURE__ */ jsx9(SessionList, { state, adapter, focusKey: listFocus, onOpen: open, onNew: () => {
2539
2733
  setListFocus("@new");
2540
2734
  setView("new");
2541
2735
  }, onClose: adapter.onClose ? close : void 0, components, labels: copy, memoryKey }) : null,
2542
- view === "new" ? /* @__PURE__ */ jsx8(NewChat, { state, adapter, onBack: () => setView("list"), onClose: adapter.onClose ? close : void 0, onStarted: () => setView("chat"), labels: copy, memoryKey }) : null,
2543
- view === "chat" ? /* @__PURE__ */ jsx8(Chat, { state, adapter, onBack: () => {
2736
+ view === "new" ? /* @__PURE__ */ jsx9(NewChat, { state, adapter, onBack: () => setView("list"), onClose: adapter.onClose ? close : void 0, onStarted: () => setView("chat"), labels: copy, memoryKey }) : null,
2737
+ view === "chat" ? /* @__PURE__ */ jsx9(Chat, { state, adapter, onBack: () => {
2544
2738
  setListFocus(state.attached?.key ?? listFocus);
2545
2739
  setView("list");
2546
2740
  }, onNew: () => {
2547
2741
  setListFocus("@new");
2548
2742
  setView("new");
2549
2743
  }, onClose: adapter.onClose ? close : void 0, components, slots, labels: copy }) : null,
2550
- opening ? /* @__PURE__ */ jsxs7("div", { class: "scui-opening", role: "status", "aria-busy": "true", children: [
2551
- /* @__PURE__ */ jsx8(HarnessLogo, { id: opening.harness, size: 34 }),
2552
- /* @__PURE__ */ jsxs7("span", { children: [
2553
- /* @__PURE__ */ jsxs7("strong", { children: [
2744
+ opening ? /* @__PURE__ */ jsxs8("div", { class: "scui-opening", role: "status", "aria-busy": "true", children: [
2745
+ /* @__PURE__ */ jsx9(HarnessLogo, { id: opening.harness, size: 34 }),
2746
+ /* @__PURE__ */ jsxs8("span", { children: [
2747
+ /* @__PURE__ */ jsxs8("strong", { children: [
2554
2748
  "Opening ",
2555
2749
  sessionDisplayName(opening)
2556
2750
  ] }),
2557
- /* @__PURE__ */ jsx8("small", { children: "Loading the latest transcript window\u2026" })
2751
+ /* @__PURE__ */ jsx9("small", { children: "Loading the latest transcript window\u2026" })
2558
2752
  ] }),
2559
- /* @__PURE__ */ jsx8("i", {})
2753
+ /* @__PURE__ */ jsx9("i", {})
2560
2754
  ] }) : null,
2561
- Footer ? /* @__PURE__ */ jsx8(Footer, { state, adapter, value: copy }) : null
2755
+ Footer ? /* @__PURE__ */ jsx9(Footer, { state, adapter, value: copy }) : null
2562
2756
  ] });
2563
2757
  }
2564
2758
  export {
@@ -2566,7 +2760,9 @@ export {
2566
2760
  Composer,
2567
2761
  ContinuationBar,
2568
2762
  Conversation,
2763
+ HarnessAdvisory,
2569
2764
  HarnessLogo,
2765
+ HarnessSettingsPanel,
2570
2766
  ImageViewer,
2571
2767
  LoadingStatus,
2572
2768
  MessageImages,