@volter-ai-dev/supercode-ui 0.1.70 → 0.1.72

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.
Files changed (59) hide show
  1. package/README.md +61 -1
  2. package/activity.mjs +5 -1235
  3. package/chunks/chunk-2FG7VKIW.mjs +201 -0
  4. package/chunks/chunk-4XUMCWPC.mjs +29 -0
  5. package/chunks/chunk-E65I2I2L.mjs +120 -0
  6. package/chunks/chunk-IL5T2AST.mjs +842 -0
  7. package/chunks/chunk-LOP6XOON.mjs +671 -0
  8. package/chunks/chunk-LS6JBXNT.mjs +1237 -0
  9. package/chunks/chunk-SJ3YTA6Z.mjs +316 -0
  10. package/chunks/chunk-TKTOVDXB.mjs +139 -0
  11. package/chunks/chunk-V3UM7H7W.mjs +328 -0
  12. package/chunks/chunk-WRK5TBX3.mjs +224 -0
  13. package/chunks/chunk-ZLYHUYE2.mjs +62 -0
  14. package/components.d.ts +3 -0
  15. package/components.mjs +59 -3766
  16. package/composer.mjs +8 -580
  17. package/conversation.mjs +16 -1373
  18. package/embed.mjs +15 -3681
  19. package/icon.mjs +3 -58
  20. package/index.d.ts +25 -0
  21. package/logo.mjs +5 -87
  22. package/messenger.d.ts +3 -0
  23. package/messenger.mjs +21 -3677
  24. package/package.json +22 -3
  25. package/react/activity.mjs +5 -1235
  26. package/react/chunks/chunk-7ES5FSLG.mjs +316 -0
  27. package/react/chunks/chunk-LS6JBXNT.mjs +1237 -0
  28. package/react/chunks/chunk-LYRIQE5M.mjs +29 -0
  29. package/react/chunks/chunk-NJC6S2AW.mjs +201 -0
  30. package/react/chunks/chunk-OINC7LV7.mjs +62 -0
  31. package/react/chunks/chunk-QL6QEYPK.mjs +842 -0
  32. package/react/chunks/chunk-UGL6AB2K.mjs +120 -0
  33. package/react/chunks/chunk-UKHDCPME.mjs +328 -0
  34. package/react/chunks/chunk-UKMTRNJN.mjs +671 -0
  35. package/react/chunks/chunk-VFOVJ4S3.mjs +139 -0
  36. package/react/chunks/chunk-YK7SJ6EH.mjs +224 -0
  37. package/react/components.mjs +59 -3766
  38. package/react/composer.mjs +8 -580
  39. package/react/conversation.mjs +16 -1373
  40. package/react/icon.mjs +3 -58
  41. package/react/index.d.ts +25 -0
  42. package/react/logo.mjs +5 -87
  43. package/react/messenger.d.ts +3 -0
  44. package/react/messenger.mjs +21 -3677
  45. package/react/sessions.mjs +9 -519
  46. package/react/settings.mjs +9 -425
  47. package/react/subagents.mjs +8 -1546
  48. package/react/supervision-components.mjs +1354 -0
  49. package/react/supervision.d.ts +36 -0
  50. package/sessions.mjs +9 -519
  51. package/settings.mjs +9 -425
  52. package/source-inventory.d.ts +240 -0
  53. package/source-inventory.mjs +367 -0
  54. package/styles.css +104 -0
  55. package/subagents.mjs +8 -1546
  56. package/supervision-components.d.ts +36 -0
  57. package/supervision-components.mjs +1354 -0
  58. package/supervision.d.ts +242 -0
  59. package/supervision.mjs +349 -0
@@ -0,0 +1,842 @@
1
+ import {
2
+ HarnessAdvisory,
3
+ HarnessPicker,
4
+ HarnessReadiness,
5
+ HarnessSettingsPanel
6
+ } from "./chunk-YK7SJ6EH.mjs";
7
+ import {
8
+ SessionList
9
+ } from "./chunk-NJC6S2AW.mjs";
10
+ import {
11
+ SubagentInspector
12
+ } from "./chunk-UGL6AB2K.mjs";
13
+ import {
14
+ HarnessLogo
15
+ } from "./chunk-VFOVJ4S3.mjs";
16
+ import {
17
+ Conversation
18
+ } from "./chunk-UKMTRNJN.mjs";
19
+ import {
20
+ Composer,
21
+ ContinuationBar,
22
+ ExecutionModeSelect,
23
+ dispatchConfirmedIntent,
24
+ useAutosizeTextarea
25
+ } from "./chunk-7ES5FSLG.mjs";
26
+ import {
27
+ ContextCandidates,
28
+ ContextTray,
29
+ ImageTray,
30
+ MAX_CONTEXT_ITEMS,
31
+ MAX_IMAGE_ITEMS,
32
+ boundedSet,
33
+ imageAttachmentsFromFiles,
34
+ mergeContext,
35
+ mergeImages,
36
+ normalizeAttachmentCandidates,
37
+ partitionAttachments,
38
+ uiMemory
39
+ } from "./chunk-UKHDCPME.mjs";
40
+ import {
41
+ DEFAULT_LABELS,
42
+ harnessDisplayName,
43
+ isSendKey,
44
+ normalizeUiState,
45
+ operationLabel,
46
+ sessionDisplayName,
47
+ terminalCommand
48
+ } from "./chunk-LS6JBXNT.mjs";
49
+ import {
50
+ UiIcon
51
+ } from "./chunk-OINC7LV7.mjs";
52
+
53
+ // src/messenger.jsx
54
+ import { createContext } from "react";
55
+ import { useContext, useEffect, useId, useMemo, useRef, useState } from "react";
56
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
57
+ var pendingMessageMemory = uiMemory();
58
+ var messengerViewMemory = uiMemory();
59
+ var newChatMemory = uiMemory();
60
+ var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "steer", "interrupt", "respond", "configureHarness", "refresh", "loadEarlier", "loadSessions"]);
61
+ function UniversalNouns({ state, adapter }) {
62
+ const banners = [];
63
+ if (state.participant && state.participant.kind !== "local-user") {
64
+ banners.push(
65
+ /* @__PURE__ */ jsx("div", { className: "scui-notice scui-noun-banner", "data-noun": "participant", "data-kind": state.participant.kind, children: state.participant.kind === "foreign" ? /* @__PURE__ */ jsxs(Fragment, { children: [
66
+ "This is ",
67
+ /* @__PURE__ */ jsx("strong", { children: state.participant.label ?? "someone else's" }),
68
+ " conversation",
69
+ state.participant.origin ? /* @__PURE__ */ jsxs(Fragment, { children: [
70
+ " via ",
71
+ state.participant.origin
72
+ ] }) : null,
73
+ "."
74
+ ] }) : /* @__PURE__ */ jsx(Fragment, { children: "Unknown participant \u2014 this conversation was not started here." }) }, "participant")
75
+ );
76
+ }
77
+ if (state.mirror) {
78
+ banners.push(
79
+ /* @__PURE__ */ jsxs("div", { className: "scui-notice scui-noun-banner", "data-noun": "mirror", "data-truncated": state.mirror.truncated, children: [
80
+ state.mirror.bounded ? "Bounded mirror" : "Mirror",
81
+ " of a ",
82
+ /* @__PURE__ */ jsx("strong", { children: harnessDisplayName(state.mirror.canonicalHarness) }),
83
+ " session \u2014 the canonical record lives in its own store",
84
+ state.mirror.truncated ? " (truncated here)" : "",
85
+ ".",
86
+ state.mirror.canonicalKey ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => adapter.onIntent({ action: "open_session", key: state.mirror.canonicalKey }), children: "Open canonical session" }) : null
87
+ ] }, "mirror")
88
+ );
89
+ }
90
+ if (state.holder && state.holder.surface !== "supercode") {
91
+ banners.push(
92
+ /* @__PURE__ */ jsxs("div", { className: "scui-notice scui-noun-banner", "data-noun": "holder", "data-surface": state.holder.surface ?? "unheld", children: [
93
+ state.holder.surface ? /* @__PURE__ */ jsxs(Fragment, { children: [
94
+ "Held by ",
95
+ /* @__PURE__ */ jsx("strong", { children: state.holder.surface }),
96
+ " right now."
97
+ ] }) : /* @__PURE__ */ jsx(Fragment, { children: "No surface holds this session right now." }),
98
+ state.holder.canTakeOver ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => dispatchConfirmedIntent(adapter, { action: "take_over" }), children: "Take over" }) : null
99
+ ] }, "holder")
100
+ );
101
+ }
102
+ if (!banners.length) return null;
103
+ return /* @__PURE__ */ jsx("div", { className: "scui-noun-banners", children: banners });
104
+ }
105
+ function Receipt({ state, adapter }) {
106
+ const receipt = state.reductionReceipt;
107
+ if (receipt) return /* @__PURE__ */ jsx("div", { className: "scui-receipt", children: /* @__PURE__ */ jsxs("span", { children: [
108
+ /* @__PURE__ */ jsx("strong", { children: "Reduced and verified" }),
109
+ /* @__PURE__ */ jsxs("small", { children: [
110
+ receipt.sourceTokens.toLocaleString(),
111
+ " \u2192 ",
112
+ receipt.reducedTokens.toLocaleString(),
113
+ " tokens \xB7 ",
114
+ receipt.ratio.toFixed(1),
115
+ "\xD7 \xB7 reversible"
116
+ ] })
117
+ ] }) });
118
+ if (state.exportReceipt) return /* @__PURE__ */ jsxs("div", { className: "scui-receipt", children: [
119
+ /* @__PURE__ */ jsxs("span", { children: [
120
+ /* @__PURE__ */ jsxs("strong", { children: [
121
+ "Lossless export ready \xB7 ",
122
+ harnessDisplayName(state.exportReceipt.targetHarness)
123
+ ] }),
124
+ /* @__PURE__ */ jsxs("small", { children: [
125
+ state.exportReceipt.path,
126
+ " \xB7 ",
127
+ state.exportReceipt.files,
128
+ " files"
129
+ ] })
130
+ ] }),
131
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => adapter.copyText?.(state.exportReceipt.path), children: "Copy path" })
132
+ ] });
133
+ if (state.terminalHandoff) return /* @__PURE__ */ jsxs("div", { className: "scui-receipt", children: [
134
+ /* @__PURE__ */ jsxs("span", { children: [
135
+ /* @__PURE__ */ jsx("strong", { children: "Terminal handoff ready" }),
136
+ /* @__PURE__ */ jsx("small", { children: state.terminalHandoff.cwd })
137
+ ] }),
138
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => adapter.copyText?.(terminalCommand(state.terminalHandoff)), children: "Copy command" })
139
+ ] });
140
+ return null;
141
+ }
142
+ function ConversationActions({ state, adapter, actionPending, onNew, onSettings }) {
143
+ const [open, setOpen] = useState(false);
144
+ const root = useRef(null);
145
+ const panel = useRef(null);
146
+ const trigger = useRef(null);
147
+ const menuId = useId();
148
+ const targets = state.harnesses.filter((item) => item.startable);
149
+ const groups = [
150
+ {
151
+ label: "Session",
152
+ items: [
153
+ { key: "new", label: "New chat", onSelect: onNew },
154
+ state.canDetach ? { key: "detach", label: "Detach to read-only", intent: { action: "detach" } } : null,
155
+ state.canOpenTerminal ? { key: "terminal", label: "Prepare terminal handoff", intent: { action: "terminal" } } : null,
156
+ state.canExport && state.exportBackTarget ? { key: "export", label: `Export back to ${harnessDisplayName(state.exportBackTarget)}`, intent: { action: "export", targetHarness: state.exportBackTarget } } : null,
157
+ state.interopSettings || state.interopSettingsError ? { key: "settings", label: "Interoperability settings", onSelect: onSettings } : null,
158
+ adapter.openKeyboardShortcuts ? { key: "shortcuts", label: "Keyboard shortcuts", onSelect: adapter.openKeyboardShortcuts } : null
159
+ ].filter(Boolean)
160
+ },
161
+ {
162
+ label: "Lower-cost continuation",
163
+ items: state.canReduce ? targets.map((target) => ({ key: `reduce:${target.id}`, label: target.id === state.harness ? `Reduce and continue in ${target.label}` : `Reduce and switch to ${target.label}`, intent: { action: "reduce", targetHarness: target.id } })) : []
164
+ },
165
+ {
166
+ label: "Independent continuation",
167
+ 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 } })) : []
168
+ }
169
+ ].filter((group) => group.items.length);
170
+ useEffect(() => {
171
+ if (!open) return;
172
+ panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
173
+ const dismiss = (event) => {
174
+ if (event.type === "keydown" && event.key === "Escape") {
175
+ event.preventDefault();
176
+ setOpen(false);
177
+ trigger.current?.focus({ preventScroll: true });
178
+ } else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
179
+ setOpen(false);
180
+ }
181
+ };
182
+ document.addEventListener("keydown", dismiss);
183
+ document.addEventListener("pointerdown", dismiss, true);
184
+ return () => {
185
+ document.removeEventListener("keydown", dismiss);
186
+ document.removeEventListener("pointerdown", dismiss, true);
187
+ };
188
+ }, [open]);
189
+ const dispatch = (item) => {
190
+ setOpen(false);
191
+ if (item.onSelect) item.onSelect();
192
+ else dispatchConfirmedIntent(adapter, item.intent);
193
+ };
194
+ const navigate = (event) => {
195
+ if (event.key === "Escape") {
196
+ event.preventDefault();
197
+ event.stopPropagation();
198
+ setOpen(false);
199
+ trigger.current?.focus({ preventScroll: true });
200
+ return;
201
+ }
202
+ if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
203
+ const items = [...panel.current.querySelectorAll("button:not(:disabled)")];
204
+ if (!items.length) return;
205
+ event.preventDefault();
206
+ const current = items.indexOf(document.activeElement);
207
+ 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;
208
+ items[index].focus({ preventScroll: true });
209
+ };
210
+ return /* @__PURE__ */ jsxs("div", { className: "scui-menu", ref: root, onBlur: (event) => {
211
+ if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
212
+ }, children: [
213
+ /* @__PURE__ */ jsx("button", { ref: trigger, className: "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__ */ jsx(UiIcon, { name: "menu", size: 18 }) }),
214
+ open ? /* @__PURE__ */ jsx("div", { ref: panel, id: menuId, className: "scui-menu-panel", role: "menu", "aria-label": "Conversation actions", onKeyDown: navigate, children: groups.map((group) => /* @__PURE__ */ jsxs("section", { role: "group", "aria-label": group.label, children: [
215
+ /* @__PURE__ */ jsx("strong", { children: group.label }),
216
+ group.items.map((item) => /* @__PURE__ */ jsx("button", { role: "menuitem", type: "button", disabled: actionPending, onClick: () => dispatch(item), children: item.label }, item.key))
217
+ ] }, group.label)) }) : null
218
+ ] });
219
+ }
220
+ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose, onSettings, headerActions: HeaderActions }) {
221
+ const back = useRef(null);
222
+ const harness = state.attached?.harness ?? state.harness;
223
+ const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
224
+ const status = state.needsInput ? "Needs input" : pendingStatus === "failed" ? "Send failed" : state.busy ? "Working" : pendingStatus === "sending" ? "Sending" : pendingStatus === "editing" ? "Editing message" : state.messaging === "live_peer" ? "Live" : state.mode === "mirror" ? "Read-only" : "Ready";
225
+ useEffect(() => {
226
+ if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
227
+ }, []);
228
+ return /* @__PURE__ */ jsxs("header", { className: "scui-head scui-chat-head", children: [
229
+ onBack ? /* @__PURE__ */ jsx("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx(UiIcon, { name: "back", size: 17 }) }) : null,
230
+ /* @__PURE__ */ jsx(HarnessLogo, { id: harness, size: 24 }),
231
+ /* @__PURE__ */ jsxs("span", { className: "scui-head-copy", children: [
232
+ /* @__PURE__ */ jsx("strong", { children: title }),
233
+ /* @__PURE__ */ jsxs("small", { children: [
234
+ harnessDisplayName(harness),
235
+ " \xB7 ",
236
+ status
237
+ ] })
238
+ ] }),
239
+ HeaderActions ? /* @__PURE__ */ jsx(HeaderActions, { state, adapter, value: "chat" }) : null,
240
+ /* @__PURE__ */ jsx(ConversationActions, { state, adapter, actionPending, onNew, onSettings }),
241
+ onClose ? /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx(UiIcon, { name: "close", size: 18 }) }) : null
242
+ ] });
243
+ }
244
+ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels, contextCandidates, navigation, composerCommand }) {
245
+ const Header = slots.header;
246
+ const HeaderActions = slots.headerActions;
247
+ const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
248
+ const [pending, setPendingState] = useState(() => pendingMessageMemory.get(memoryKey) ?? null);
249
+ const [pendingAction, setPendingAction] = useState(null);
250
+ const [restoreDraft, setRestoreDraft] = useState(null);
251
+ const [settings, setSettings] = useState(null);
252
+ const restoreSequence = useRef(0);
253
+ const actionSequence = useRef(0);
254
+ const lastNavigation = useRef(null);
255
+ const acknowledged = useRef(/* @__PURE__ */ new Set());
256
+ const setPending = (update) => setPendingState((current) => {
257
+ const next = typeof update === "function" ? update(current) : update;
258
+ if (next) boundedSet(pendingMessageMemory, memoryKey, next);
259
+ else pendingMessageMemory.delete(memoryKey);
260
+ return next;
261
+ });
262
+ useEffect(() => {
263
+ setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
264
+ setPendingAction(null);
265
+ setRestoreDraft(null);
266
+ }, [memoryKey]);
267
+ useEffect(() => {
268
+ if (!navigation || navigation.id === lastNavigation.current) return;
269
+ lastNavigation.current = navigation.id;
270
+ if (navigation.draft === void 0 && !navigation.context?.length && !navigation.images?.length) return;
271
+ setRestoreDraft({
272
+ id: `host:${String(navigation.id)}`,
273
+ text: navigation.draft ?? "",
274
+ context: navigation.context,
275
+ images: navigation.images
276
+ });
277
+ }, [navigation]);
278
+ useEffect(() => {
279
+ if (!pending) return;
280
+ if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
281
+ setPending(null);
282
+ return;
283
+ }
284
+ if (pending.status === "sending" && state.busy && !pending.seenBusy) setPending({ ...pending, seenBusy: true });
285
+ else if (pending.status === "sending" && state.error && !state.busy && !state.operation && (pending.seenBusy || state.error !== pending.initialError)) setPending({ ...pending, status: "failed" });
286
+ }, [pending, state.busy, state.error, state.operation, state.transcript]);
287
+ const beginPending = (text, context = [], images = []) => setPending({
288
+ text,
289
+ context,
290
+ images,
291
+ status: "sending",
292
+ initialError: state.error,
293
+ seenBusy: state.busy,
294
+ baselineIds: state.transcript.filter((entry) => entry.role === "user" && entry.text.trim() === text.trim()).map((entry) => entry.id)
295
+ });
296
+ const retryPending = () => {
297
+ if (!pending) return;
298
+ adapter.onIntent({ action: "send", text: pending.text, ...pending.context?.length ? { context: pending.context } : {}, ...pending.images?.length ? { images: pending.images } : {} });
299
+ setPending({ ...pending, status: "sending", initialError: state.error, seenBusy: state.busy });
300
+ };
301
+ const editPending = () => {
302
+ if (!pending) return;
303
+ restoreSequence.current += 1;
304
+ setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
305
+ setPending({ ...pending, status: "editing" });
306
+ };
307
+ useEffect(() => {
308
+ const key = state.attached?.key;
309
+ if (!key || !state.attention.some((item) => item.key === key)) {
310
+ if (key) acknowledged.current.delete(key);
311
+ return;
312
+ }
313
+ if (!acknowledged.current.has(key)) {
314
+ acknowledged.current.add(key);
315
+ adapter.onIntent({ action: "ack", key });
316
+ }
317
+ }, [adapter, state.attached?.key, state.attention]);
318
+ const trackedAdapter = useMemo(() => ({
319
+ ...adapter,
320
+ onIntent(intent) {
321
+ if (!TRACKED_ACTIONS.has(intent.action)) return adapter.onIntent(intent);
322
+ const id = actionSequence.current + 1;
323
+ actionSequence.current = id;
324
+ const action2 = intent.action;
325
+ setPendingAction({ id, action: action2, initialError: state.error, seenOperation: false });
326
+ let result;
327
+ try {
328
+ result = adapter.onIntent(intent);
329
+ } catch (error) {
330
+ setPendingAction((current) => current?.id === id ? null : current);
331
+ throw error;
332
+ }
333
+ if (result && typeof result.then === "function") {
334
+ Promise.resolve(result).then(
335
+ () => setPendingAction((current) => current?.id === id ? null : current),
336
+ () => setPendingAction((current) => current?.id === id ? null : current)
337
+ );
338
+ }
339
+ return result;
340
+ }
341
+ }), [adapter, state.error]);
342
+ useEffect(() => {
343
+ if (!pendingAction) return;
344
+ if (state.operation && !pendingAction.seenOperation) {
345
+ setPendingAction({ ...pendingAction, seenOperation: true });
346
+ } else if (!state.operation && (pendingAction.seenOperation || state.error !== pendingAction.initialError)) {
347
+ setPendingAction(null);
348
+ }
349
+ }, [pendingAction, state.error, state.operation]);
350
+ const pendingMessage = pending && pending.status !== "editing" ? { text: pending.text, context: pending.context, images: pending.images, status: pending.status, onRetry: retryPending, onEdit: editPending } : null;
351
+ const action = state.operation || pendingAction?.action || null;
352
+ const actionLabel = operationLabel(action);
353
+ const actionState = action ? { ...state, operation: action, canSteer: false, canInterrupt: false, canRespond: false } : state;
354
+ const unreadAfterMessages = state.attention.find((item) => item.key === state.attached?.key)?.afterMessages ?? null;
355
+ const Advisory = components.HarnessAdvisory ?? HarnessAdvisory;
356
+ const SettingsPanel = components.HarnessSettingsPanel ?? HarnessSettingsPanel;
357
+ return /* @__PURE__ */ jsxs("section", { className: "scui-chat", children: [
358
+ Header ? /* @__PURE__ */ jsx(Header, { state: actionState, adapter: trackedAdapter, value: null }) : /* @__PURE__ */ jsx(ChatHeader, { state: actionState, adapter: trackedAdapter, pendingStatus: pending?.status ?? null, actionPending: Boolean(action), onBack, onNew, onClose, onSettings: () => setSettings({ recommendedChange: null }), headerActions: HeaderActions }),
359
+ actionLabel ? /* @__PURE__ */ jsxs("div", { className: "scui-operation", role: "status", children: [
360
+ /* @__PURE__ */ jsx("i", {}),
361
+ actionLabel
362
+ ] }) : null,
363
+ state.error ? /* @__PURE__ */ jsxs("div", { className: "scui-error", role: "alert", children: [
364
+ /* @__PURE__ */ jsx("span", { children: state.error }),
365
+ state.recoverable ? /* @__PURE__ */ jsx("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
366
+ ] }) : null,
367
+ /* @__PURE__ */ jsx(Receipt, { state, adapter }),
368
+ /* @__PURE__ */ jsx(UniversalNouns, { state: actionState, adapter: trackedAdapter }),
369
+ /* @__PURE__ */ jsx(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
370
+ /* @__PURE__ */ jsx(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
371
+ /* @__PURE__ */ jsx(Advisory, { state: actionState, adapter: trackedAdapter, value: state.interopSettings?.advisories[0] ?? null, onReview: (recommendedChange) => setSettings({ recommendedChange }) }),
372
+ state.mode !== "mirror" || state.canSend ? /* @__PURE__ */ jsx(Composer, { state: actionState, adapter: trackedAdapter, labels, memoryKey, pendingStatus: pending?.status ?? null, restoreDraft, command: composerCommand, contextCandidates, components, onDraftRestored: (id) => setRestoreDraft((value) => value?.id === id ? null : value), onPending: beginPending }, `composer:${memoryKey}`) : null,
373
+ settings ? /* @__PURE__ */ jsx(SettingsPanel, { state: actionState, adapter: trackedAdapter, value: state.interopSettings, recommendedChange: settings.recommendedChange, onClose: () => setSettings(null) }) : null
374
+ ] });
375
+ }
376
+ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey, headerActions: HeaderActions, contextCandidates, components, navigation, composerCommand, variant = "default", environmentLabel, suggestions = [], initialDraft = "", onDraftChange }) {
377
+ const startable = state.harnesses.filter((item) => item.startable);
378
+ const startableKey = startable.map((item) => `${item.id}:${item.launchModes?.join(",")}:${item.preferredLaunchMode ?? ""}`).join("\0");
379
+ const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: initialDraft, context: [], images: [], modes: {} };
380
+ const [harness, setHarness] = useState(remembered.harness);
381
+ const [draft, setDraft] = useState(remembered.draft);
382
+ const [context, setContext] = useState(remembered.context);
383
+ const [images, setImages] = useState(remembered.images ?? []);
384
+ const [modes, setModes] = useState(remembered.modes ?? {});
385
+ const [starting, setStarting] = useState(null);
386
+ const [picking, setPicking] = useState(false);
387
+ const [dragging, setDragging] = useState(false);
388
+ const [pickerError, setPickerError] = useState(null);
389
+ const startSequence = useRef(0);
390
+ const lastComposerCommand = useRef(null);
391
+ const textarea = useRef(null);
392
+ const selectedHarness = startable.find((item) => item.id === harness) ?? null;
393
+ const Picker = components.HarnessPicker ?? HarnessPicker;
394
+ const Readiness = components.HarnessReadiness ?? HarnessReadiness;
395
+ const launchModes = selectedHarness?.launchModes?.length ? selectedHarness.launchModes : ["headless"];
396
+ const rememberedMode = modes[harness];
397
+ const requestedMode = launchModes.includes(rememberedMode) ? rememberedMode : launchModes.includes(selectedHarness?.preferredLaunchMode) ? selectedHarness.preferredLaunchMode : launchModes[0];
398
+ const mode = (context.length > 0 || images.length > 0) && launchModes.includes("headless") ? "headless" : requestedMode;
399
+ const terminalAttachments = mode === "terminal" && (context.length > 0 || images.length > 0);
400
+ const remember = (overrides = {}) => {
401
+ boundedSet(newChatMemory, memoryKey, { harness, draft, context, images, modes, ...overrides });
402
+ if (Object.hasOwn(overrides, "draft")) onDraftChange?.(overrides.draft);
403
+ };
404
+ useAutosizeTextarea(textarea, draft);
405
+ useEffect(() => {
406
+ if (startable.some((item) => item.id === harness)) return;
407
+ const next = startable[0]?.id ?? "";
408
+ setHarness(next);
409
+ remember({ harness: next });
410
+ }, [harness, startableKey]);
411
+ useEffect(() => {
412
+ if (!starting || starting.mode === "terminal" || starting.waitForResult || state.error) return;
413
+ const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
414
+ if (!sessionChanged && !state.busy) return;
415
+ newChatMemory.delete(memoryKey);
416
+ onDraftChange?.("");
417
+ onStarted("headless");
418
+ }, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
419
+ useEffect(() => {
420
+ if (starting && state.error && (!starting.waitForResult || state.error !== starting.initialError) && !state.busy && !state.operation) setStarting(null);
421
+ }, [starting, state.busy, state.error, state.operation]);
422
+ useEffect(() => {
423
+ textarea.current?.focus({ preventScroll: true });
424
+ }, []);
425
+ useEffect(() => {
426
+ if (!navigation) return;
427
+ const nextHarness = startable.some((item) => item.id === navigation.harness) ? navigation.harness : harness;
428
+ const nextDraft = typeof navigation.draft === "string" ? navigation.draft : draft;
429
+ const nextContext = mergeContext([], navigation.context ?? []);
430
+ const nextImages = mergeImages([], navigation.images ?? []);
431
+ setHarness(nextHarness);
432
+ setDraft(nextDraft);
433
+ setContext(nextContext);
434
+ setImages(nextImages);
435
+ remember({ harness: nextHarness, draft: nextDraft, context: nextContext, images: nextImages });
436
+ textarea.current?.focus({ preventScroll: true });
437
+ }, [navigation?.id]);
438
+ useEffect(() => {
439
+ if (!composerCommand || composerCommand.id === lastComposerCommand.current) return;
440
+ lastComposerCommand.current = composerCommand.id;
441
+ if (composerCommand.action === "attach") {
442
+ if (mode === "terminal" && !launchModes.includes("headless")) {
443
+ setPickerError("Terminal starts currently accept text only. Choose Headless to attach context or images.");
444
+ } else {
445
+ try {
446
+ const attachments = partitionAttachments(composerCommand.attachments);
447
+ if (context.length + attachments.context.length > MAX_CONTEXT_ITEMS) throw new Error(`Attach at most ${MAX_CONTEXT_ITEMS} text items.`);
448
+ if (images.length + attachments.images.length > MAX_IMAGE_ITEMS) throw new Error(`Attach at most ${MAX_IMAGE_ITEMS} images.`);
449
+ const nextContext = mergeContext(context, attachments.context);
450
+ const nextImages = mergeImages(images, attachments.images);
451
+ setContext(nextContext);
452
+ setImages(nextImages);
453
+ setPickerError(null);
454
+ remember({ context: nextContext, images: nextImages });
455
+ } catch (error) {
456
+ setPickerError(error instanceof Error ? error.message : "Could not attach context.");
457
+ }
458
+ }
459
+ }
460
+ textarea.current?.focus({ preventScroll: true });
461
+ }, [composerCommand?.id]);
462
+ const pickContext = () => {
463
+ if (mode === "terminal" || !adapter.pickContext || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS) return;
464
+ setPicking(true);
465
+ setPickerError(null);
466
+ Promise.resolve().then(() => adapter.pickContext()).then((picked) => {
467
+ const attachments = partitionAttachments(picked);
468
+ if (context.length + attachments.context.length > MAX_CONTEXT_ITEMS) throw new Error(`Attach at most ${MAX_CONTEXT_ITEMS} text items.`);
469
+ if (images.length + attachments.images.length > MAX_IMAGE_ITEMS) throw new Error(`Attach at most ${MAX_IMAGE_ITEMS} images.`);
470
+ setContext((current) => {
471
+ const next = mergeContext(current, attachments.context);
472
+ remember({ context: next });
473
+ return next;
474
+ });
475
+ setImages((current) => {
476
+ const next = mergeImages(current, attachments.images);
477
+ remember({ images: next });
478
+ return next;
479
+ });
480
+ }).catch((error) => setPickerError(error instanceof Error ? error.message : "Could not attach context.")).finally(() => setPicking(false));
481
+ };
482
+ const attachCandidate = (picked) => {
483
+ if (mode === "terminal") return;
484
+ const attachments = partitionAttachments(picked);
485
+ const nextContext = mergeContext(context, attachments.context);
486
+ const nextImages = mergeImages(images, attachments.images);
487
+ setContext(nextContext);
488
+ setImages(nextImages);
489
+ remember({ context: nextContext, images: nextImages });
490
+ };
491
+ const addImageFiles = (value, source) => {
492
+ const allFiles = Array.from(value ?? []);
493
+ if (!allFiles.length) return false;
494
+ if (mode === "terminal") {
495
+ setPickerError("Terminal starts currently accept text only. Choose Headless to attach context or images.");
496
+ return true;
497
+ }
498
+ const files = allFiles.filter((file) => file.type.startsWith("image/"));
499
+ if (files.length !== allFiles.length) {
500
+ setPickerError("Drop or paste PNG, JPEG, GIF, or WebP images.");
501
+ return true;
502
+ }
503
+ if (images.length + files.length > MAX_IMAGE_ITEMS) {
504
+ setPickerError(`Attach at most ${MAX_IMAGE_ITEMS} images.`);
505
+ return true;
506
+ }
507
+ setPicking(true);
508
+ setPickerError(null);
509
+ imageAttachmentsFromFiles(files).then((picked) => setImages((current) => {
510
+ const next = mergeImages(current, picked);
511
+ remember({ images: next });
512
+ return next;
513
+ }), (error) => setPickerError(error instanceof Error ? error.message : `Could not ${source} image.`)).finally(() => setPicking(false));
514
+ return true;
515
+ };
516
+ const pasteImages = (event) => {
517
+ if (addImageFiles(event.clipboardData?.files, "paste")) event.preventDefault();
518
+ };
519
+ const dropImages = (event) => {
520
+ setDragging(false);
521
+ if (addImageFiles(event.dataTransfer?.files, "drop")) event.preventDefault();
522
+ };
523
+ const send = () => {
524
+ const text = draft.trim();
525
+ if (!text && !images.length || !harness || starting || terminalAttachments || mode === "terminal" && !text) return;
526
+ const id = startSequence.current + 1;
527
+ startSequence.current = id;
528
+ setStarting({ id, mode, waitForResult: true, attachedKey: state.attached?.key ?? null, busy: state.busy, initialError: state.error });
529
+ setPickerError(null);
530
+ let result;
531
+ const failed = (error) => {
532
+ setStarting((current) => current?.id === id ? null : current);
533
+ setPickerError(error instanceof Error ? error.message : "Could not start the conversation. Your draft is kept.");
534
+ };
535
+ try {
536
+ result = adapter.onIntent({ action: "new", harness, mode, text, ...context.length ? { context } : {}, ...images.length ? { images } : {} });
537
+ } catch (error) {
538
+ failed(error);
539
+ return;
540
+ }
541
+ if (mode === "terminal") {
542
+ Promise.resolve(result).then(() => {
543
+ newChatMemory.delete(memoryKey);
544
+ onDraftChange?.("");
545
+ onStarted("terminal");
546
+ }, failed);
547
+ } else if (result && typeof result.then === "function") {
548
+ Promise.resolve(result).then(() => setStarting((current) => current?.id === id ? { ...current, waitForResult: false } : current), failed);
549
+ } else {
550
+ setStarting((current) => current?.id === id ? { ...current, waitForResult: false } : current);
551
+ }
552
+ };
553
+ return /* @__PURE__ */ jsxs("section", { className: `scui-chat${variant === "starter" ? " scui-starter" : ""}`, children: [
554
+ state.error ? /* @__PURE__ */ jsx("div", { className: "scui-error", role: "alert", children: state.error }) : null,
555
+ variant !== "starter" ? /* @__PURE__ */ jsxs("header", { className: "scui-head", children: [
556
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Back", onClick: onBack, children: /* @__PURE__ */ jsx(UiIcon, { name: "back", size: 18 }) }),
557
+ /* @__PURE__ */ jsxs("span", { className: "scui-head-copy", children: [
558
+ /* @__PURE__ */ jsx("strong", { children: labels.newChat }),
559
+ /* @__PURE__ */ jsx("small", { children: "No session is created until you send" })
560
+ ] }),
561
+ HeaderActions ? /* @__PURE__ */ jsx(HeaderActions, { state, adapter, value: "new" }) : null,
562
+ onClose ? /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx(UiIcon, { name: "close", size: 18 }) }) : null
563
+ ] }) : null,
564
+ starting ? /* @__PURE__ */ jsxs("div", { className: "scui-operation", role: "status", children: [
565
+ /* @__PURE__ */ jsx("i", {}),
566
+ mode === "terminal" ? "Opening terminal\u2026" : operationLabel("start")
567
+ ] }) : null,
568
+ /* @__PURE__ */ jsxs("div", { className: `scui-new${variant === "starter" ? " scui-new-starter" : ""}`, children: [
569
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2726" }),
570
+ /* @__PURE__ */ jsx("strong", { children: "What should the agent build or fix?" }),
571
+ /* @__PURE__ */ jsx("small", { children: "Choose a coding harness and send the first message." })
572
+ ] }),
573
+ /* @__PURE__ */ jsxs("div", { className: "scui-compose", children: [
574
+ environmentLabel ? /* @__PURE__ */ jsx("span", { className: "scui-environment", children: environmentLabel }) : null,
575
+ /* @__PURE__ */ jsx(Readiness, { harnesses: state.harnesses, state, adapter }),
576
+ mode !== "terminal" ? /* @__PURE__ */ jsx(ContextCandidates, { items: contextCandidates, context, images, state, adapter, onAttach: attachCandidate, component: components.ContextCandidate }) : null,
577
+ /* @__PURE__ */ jsx(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
578
+ const next = items.filter((_, itemIndex) => itemIndex !== index);
579
+ remember({ images: next });
580
+ return next;
581
+ }) }),
582
+ /* @__PURE__ */ jsx(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
583
+ const next = items.filter((_, itemIndex) => itemIndex !== index);
584
+ remember({ context: next });
585
+ return next;
586
+ }) }),
587
+ terminalAttachments ? /* @__PURE__ */ jsx("small", { className: "scui-context-error", role: "alert", children: "Terminal starts currently accept text only. Remove attachments or choose Headless." }) : pickerError ? /* @__PURE__ */ jsx("small", { className: "scui-context-error", role: "alert", children: pickerError }) : null,
588
+ /* @__PURE__ */ jsxs("div", { className: `scui-envelope scui-new-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
589
+ if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
590
+ event.preventDefault();
591
+ setDragging(true);
592
+ }
593
+ }, onDragOver: (event) => {
594
+ if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) event.preventDefault();
595
+ }, onDragLeave: (event) => {
596
+ if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
597
+ }, onDrop: dropImages, children: [
598
+ /* @__PURE__ */ jsx("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) => {
599
+ const value = event.currentTarget.value;
600
+ setDraft(value);
601
+ remember({ draft: value });
602
+ }, onKeyDown: (event) => {
603
+ if (isSendKey(event)) {
604
+ event.preventDefault();
605
+ send();
606
+ }
607
+ } }),
608
+ /* @__PURE__ */ jsxs("footer", { className: "scui-new-envelope-controls", children: [
609
+ adapter.pickContext ? /* @__PURE__ */ jsx("button", { className: "scui-attach", type: "button", "aria-label": labels.attachContext ?? DEFAULT_LABELS.attachContext, disabled: mode === "terminal" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS || Boolean(starting), onClick: pickContext, children: picking ? /* @__PURE__ */ jsx("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx(UiIcon, { name: "attach", size: 17 }) }) : null,
610
+ /* @__PURE__ */ jsx(Picker, { harnesses: state.harnesses, state, value: harness, disabled: Boolean(starting), adapter, logo: components.HarnessLogo, onChange: (value) => {
611
+ setHarness(value);
612
+ remember({ harness: value });
613
+ } }),
614
+ /* @__PURE__ */ jsx(ExecutionModeSelect, { modes: launchModes, value: mode, disabled: Boolean(starting) || mode !== requestedMode, onChange: (item) => {
615
+ const next = { ...modes, [harness]: item };
616
+ setModes(next);
617
+ setPickerError(null);
618
+ remember({ modes: next });
619
+ } }),
620
+ /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx("button", { type: "button", className: "scui-send", "aria-label": mode === "terminal" ? "Start terminal session" : "Start headless session", disabled: !draft.trim() && !images.length || !harness || Boolean(starting) || terminalAttachments || mode === "terminal" && !draft.trim(), onClick: send, children: starting ? /* @__PURE__ */ jsx("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx(UiIcon, { name: "send", size: 17 }) }) })
621
+ ] })
622
+ ] }),
623
+ suggestions.length ? /* @__PURE__ */ jsx("div", { className: "scui-starter-suggestions", children: suggestions.map((text) => /* @__PURE__ */ jsx("button", { type: "button", disabled: Boolean(starting), onClick: () => {
624
+ setDraft(text);
625
+ remember({ draft: text });
626
+ textarea.current?.focus();
627
+ }, children: text }, text)) }) : null
628
+ ] })
629
+ ] });
630
+ }
631
+ function useMessengerModel({ state: stateInput, adapter, initialView, navigation = null, composerCommand = null, onViewChange, contextCandidates: candidatesInput = [], labels, components = {}, slots = {} }) {
632
+ const state = useMemo(() => normalizeUiState(stateInput), [stateInput]);
633
+ const contextCandidates = useMemo(() => normalizeAttachmentCandidates(candidatesInput), [candidatesInput]);
634
+ const copy = { ...DEFAULT_LABELS, ...labels };
635
+ const memoryKey = state.workspace || "@default";
636
+ const [view, setViewState] = useState(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
637
+ const [opening, setOpening] = useState(null);
638
+ const [newNavigation, setNewNavigation] = useState(null);
639
+ const [chatNavigation, setChatNavigation] = useState(null);
640
+ const [listFocus, setListFocus] = useState(null);
641
+ const [inspecting, setInspecting] = useState(null);
642
+ const lastNavigation = useRef(null);
643
+ const setView = (next) => {
644
+ boundedSet(messengerViewMemory, memoryKey, next);
645
+ setViewState(next);
646
+ onViewChange?.(next);
647
+ };
648
+ useEffect(() => {
649
+ if (!navigation || navigation.id === lastNavigation.current) return;
650
+ lastNavigation.current = navigation.id;
651
+ if (navigation.view === "list") {
652
+ setOpening(null);
653
+ setChatNavigation(null);
654
+ setView("list");
655
+ return;
656
+ }
657
+ if (navigation.view === "new") {
658
+ setListFocus("@new");
659
+ setChatNavigation(null);
660
+ setNewNavigation(navigation);
661
+ setView("new");
662
+ return;
663
+ }
664
+ if (navigation.view === "chat" && typeof navigation.sessionKey === "string" && navigation.sessionKey) {
665
+ setListFocus(navigation.sessionKey);
666
+ setNewNavigation(null);
667
+ setChatNavigation(navigation);
668
+ if (state.attached?.key === navigation.sessionKey) {
669
+ setOpening(null);
670
+ setView("chat");
671
+ return;
672
+ }
673
+ const row = state.sessions.find((item) => item.key === navigation.sessionKey);
674
+ setOpening(row ?? { key: navigation.sessionKey, harness: "supercode", name: "Chat", title: "Chat" });
675
+ adapter.onIntent({ action: "attach", key: navigation.sessionKey });
676
+ }
677
+ }, [adapter, navigation, state.attached?.key, state.sessions]);
678
+ useEffect(() => {
679
+ if (!opening) return;
680
+ if (state.attached?.key === opening.key) {
681
+ setOpening(null);
682
+ setView("chat");
683
+ } else if (state.attachError?.key === opening.key || state.error && !state.operation) {
684
+ setOpening(null);
685
+ }
686
+ }, [opening, state.attachError?.key, state.attached?.key, state.error, state.operation]);
687
+ const dismissInspector = () => {
688
+ if (inspecting || state.subagentInspector) {
689
+ setInspecting(null);
690
+ adapter.onIntent({ action: "closeSubagents" });
691
+ }
692
+ };
693
+ const open = (row) => {
694
+ dismissInspector();
695
+ setListFocus(row.key);
696
+ setChatNavigation(null);
697
+ setOpening(row);
698
+ adapter.onIntent({ action: "attach", key: row.key });
699
+ };
700
+ const openSubagents = (row) => {
701
+ setListFocus(row.key);
702
+ setInspecting(row);
703
+ adapter.onIntent({ action: "openSubagents", key: row.key });
704
+ };
705
+ const close = () => adapter.onClose?.();
706
+ const inspectorParent = inspecting ?? (state.subagentInspector ? state.sessions.find((row) => row.key === state.subagentInspector.parentKey) ?? { key: state.subagentInspector.parentKey, title: state.subagentInspector.parentTitle } : null);
707
+ const inspectorState = inspectorParent ? {
708
+ ...state,
709
+ subagentInspector: state.subagentInspector?.parentKey === inspectorParent.key ? state.subagentInspector : { parentKey: inspectorParent.key, parentTitle: sessionDisplayName(inspectorParent), status: state.error ? "error" : "loading", items: [], selectedKey: null, transcript: [], error: state.error }
710
+ } : null;
711
+ const newConversation = () => {
712
+ dismissInspector();
713
+ setListFocus("@new");
714
+ setChatNavigation(null);
715
+ setNewNavigation(null);
716
+ setView("new");
717
+ };
718
+ const back = () => {
719
+ setListFocus(state.attached?.key ?? listFocus);
720
+ setView("list");
721
+ };
722
+ return {
723
+ state,
724
+ adapter,
725
+ view,
726
+ setView,
727
+ open,
728
+ openSubagents,
729
+ newConversation,
730
+ back,
731
+ close,
732
+ opening,
733
+ listFocus,
734
+ memoryKey,
735
+ inspectorState,
736
+ closeInspector: () => {
737
+ setInspecting(null);
738
+ adapter.onIntent({ action: "closeSubagents" });
739
+ },
740
+ newNavigation,
741
+ chatNavigation,
742
+ composerCommand,
743
+ contextCandidates,
744
+ labels: copy,
745
+ components,
746
+ slots
747
+ };
748
+ }
749
+ var MessengerContext = createContext(null);
750
+ function MessengerProvider({ children, ...props }) {
751
+ const model = useMessengerModel(props);
752
+ return /* @__PURE__ */ jsx(MessengerContext.Provider, { value: model, children });
753
+ }
754
+ function useMessenger() {
755
+ const model = useContext(MessengerContext);
756
+ if (!model) throw new Error("Messenger components require a MessengerProvider.");
757
+ return model;
758
+ }
759
+ function MessengerSessions() {
760
+ const m = useMessenger();
761
+ return /* @__PURE__ */ jsx(
762
+ SessionList,
763
+ {
764
+ state: m.state,
765
+ adapter: m.adapter,
766
+ focusKey: m.listFocus,
767
+ onOpen: m.open,
768
+ onOpenSubagents: m.openSubagents,
769
+ onNew: m.newConversation,
770
+ onClose: m.adapter.onClose ? m.close : void 0,
771
+ components: m.components,
772
+ labels: m.labels,
773
+ memoryKey: m.memoryKey,
774
+ slots: m.slots,
775
+ headerActions: m.slots.headerActions
776
+ }
777
+ );
778
+ }
779
+ function MessengerContent({ starter = {}, showBack = true } = {}) {
780
+ const m = useMessenger();
781
+ const common = {
782
+ state: m.state,
783
+ adapter: m.adapter,
784
+ components: m.components,
785
+ labels: m.labels,
786
+ contextCandidates: m.contextCandidates,
787
+ composerCommand: m.composerCommand,
788
+ onClose: m.adapter.onClose ? m.close : void 0,
789
+ onBack: showBack ? m.back : void 0
790
+ };
791
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
792
+ m.inspectorState ? /* @__PURE__ */ jsx(SubagentInspector, { state: m.inspectorState, adapter: m.adapter, onClose: m.closeInspector }) : m.view === "new" || m.view === "list" && !m.state.attached && !m.state.transcript.length ? /* @__PURE__ */ jsx(
793
+ NewChat,
794
+ {
795
+ ...common,
796
+ ...starter,
797
+ memoryKey: m.memoryKey,
798
+ headerActions: m.slots.headerActions,
799
+ navigation: m.newNavigation,
800
+ onStarted: (mode) => m.setView(mode === "terminal" ? "list" : "chat")
801
+ }
802
+ ) : /* @__PURE__ */ jsx(
803
+ Chat,
804
+ {
805
+ ...common,
806
+ slots: m.slots,
807
+ onNew: m.newConversation,
808
+ navigation: m.chatNavigation?.sessionKey === m.state.attached?.key ? m.chatNavigation : null
809
+ }
810
+ ),
811
+ m.opening ? /* @__PURE__ */ jsxs("div", { className: "scui-opening", role: "status", "aria-busy": "true", children: [
812
+ /* @__PURE__ */ jsx(HarnessLogo, { id: m.opening.harness, size: 34 }),
813
+ /* @__PURE__ */ jsxs("span", { children: [
814
+ /* @__PURE__ */ jsxs("strong", { children: [
815
+ "Opening ",
816
+ sessionDisplayName(m.opening)
817
+ ] }),
818
+ /* @__PURE__ */ jsx("small", { children: "Loading the latest transcript window\u2026" })
819
+ ] }),
820
+ /* @__PURE__ */ jsx("i", {})
821
+ ] }) : null
822
+ ] });
823
+ }
824
+ function MessengerSurface({ class: className = "", starter }) {
825
+ const m = useMessenger();
826
+ const Footer = m.slots.footer;
827
+ return /* @__PURE__ */ jsxs("main", { className: `scui-root ${className}`, "data-view": m.inspectorState ? "agents" : m.view, "data-mode": m.state.mode, "aria-label": "Supercode messenger", children: [
828
+ m.view === "list" && !m.inspectorState ? /* @__PURE__ */ jsx(MessengerSessions, {}) : /* @__PURE__ */ jsx(MessengerContent, { starter }),
829
+ Footer ? /* @__PURE__ */ jsx(Footer, { state: m.state, adapter: m.adapter, value: m.labels }) : null
830
+ ] });
831
+ }
832
+ function SupercodeMessenger(props) {
833
+ return /* @__PURE__ */ jsx(MessengerProvider, { ...props, children: /* @__PURE__ */ jsx(MessengerSurface, { class: props.class, starter: props.starter }) });
834
+ }
835
+
836
+ export {
837
+ MessengerProvider,
838
+ useMessenger,
839
+ MessengerSessions,
840
+ MessengerContent,
841
+ SupercodeMessenger
842
+ };