@volter-ai-dev/supercode-ui 0.1.66 → 0.1.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/embed.mjs CHANGED
@@ -35,6 +35,10 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  harness: "",
36
36
  mode: "none",
37
37
  strategy: null,
38
+ participant: Object.freeze({ kind: "local-user", label: null, origin: null }),
39
+ workspaceRef: Object.freeze({ kind: "none", value: null }),
40
+ mirror: null,
41
+ holder: null,
38
42
  canSend: false,
39
43
  canSteer: false,
40
44
  canResume: false,
@@ -72,7 +76,12 @@ var EMPTY_UI_STATE = Object.freeze({
72
76
  subagentInspector: null,
73
77
  attached: null,
74
78
  owned: null,
75
- attachError: null
79
+ attachError: null,
80
+ agentPackage: null,
81
+ contributions: Object.freeze([]),
82
+ agentPackageError: null,
83
+ agentPackageCapabilityState: null,
84
+ agentPackageCapabilityError: null
76
85
  });
77
86
  var ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool", "reasoning", "request", "notice"]);
78
87
  var MODES = /* @__PURE__ */ new Set(["none", "control", "mirror"]);
@@ -85,6 +94,10 @@ var AUTHENTICATION_PHASES = /* @__PURE__ */ new Set(["idle", "checking", "requir
85
94
  var MAX_TOOL_FIELDS = 8;
86
95
  var MAX_TOOL_FIELD_CHARS = 800;
87
96
  var MAX_TOOL_PREVIEW_CHARS = 4e3;
97
+ var MAX_CONTRIBUTIONS = 64;
98
+ var MAX_CONTRIBUTION_JSON_DEPTH = 12;
99
+ var MAX_CONTRIBUTION_COLLECTION = 100;
100
+ var MAX_CONTRIBUTION_STRING_CHARS = 16e3;
88
101
  function record(value) {
89
102
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
90
103
  }
@@ -117,6 +130,126 @@ function number(value, fallback = 0) {
117
130
  function nullableNumber(value) {
118
131
  return typeof value === "number" && Number.isFinite(value) ? value : null;
119
132
  }
133
+ function relativeContributionPath(value) {
134
+ if (typeof value !== "string" || value.startsWith("/") || /^[a-z]:[\\/]/i.test(value)) return null;
135
+ return value.replaceAll("\\", "/").split("/").some((segment) => segment === "..") ? null : boundedString(value, 1e3);
136
+ }
137
+ function contributionJson(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
138
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
139
+ return typeof value === "string" ? boundedString(value, MAX_CONTRIBUTION_STRING_CHARS) : value;
140
+ }
141
+ if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
142
+ if (depth >= MAX_CONTRIBUTION_JSON_DEPTH || !value || typeof value !== "object" || seen.has(value)) {
143
+ return void 0;
144
+ }
145
+ seen.add(value);
146
+ if (Array.isArray(value)) {
147
+ const result2 = [];
148
+ for (const item of value.slice(0, MAX_CONTRIBUTION_COLLECTION)) {
149
+ const normalized = contributionJson(item, depth + 1, seen);
150
+ if (normalized !== void 0) result2.push(normalized);
151
+ }
152
+ seen.delete(value);
153
+ return result2;
154
+ }
155
+ const source = record(value);
156
+ if (!source) return void 0;
157
+ const result = {};
158
+ for (const [key, item] of Object.entries(source).slice(0, MAX_CONTRIBUTION_COLLECTION)) {
159
+ const normalized = contributionJson(item, depth + 1, seen);
160
+ if (normalized !== void 0) result[boundedString(key, 200)] = normalized;
161
+ }
162
+ seen.delete(value);
163
+ return result;
164
+ }
165
+ function normalizeContributions(value) {
166
+ if (!Array.isArray(value)) return [];
167
+ return value.slice(0, MAX_CONTRIBUTIONS).flatMap((candidate) => {
168
+ const item = record(candidate);
169
+ const placement = record(item?.placement);
170
+ const source = relativeContributionPath(item?.source);
171
+ if (item?.schema !== "supercode/contribution-v1" || typeof item.id !== "string" || !item.id.trim() || typeof item.kind !== "string" || !item.kind.trim() || source === null) return [];
172
+ const data = contributionJson(item.data);
173
+ if (data === void 0) return [];
174
+ return [{
175
+ schema: "supercode/contribution-v1",
176
+ id: boundedString(item.id, 200),
177
+ kind: boundedString(item.kind, 100),
178
+ ...typeof item.title === "string" ? { title: boundedString(item.title, 500) } : {},
179
+ ...placement ? { placement: {
180
+ ...typeof placement.surface === "string" ? { surface: boundedString(placement.surface, 100) } : {},
181
+ ...typeof placement.region === "string" ? { region: boundedString(placement.region, 100) } : {}
182
+ } } : {},
183
+ data,
184
+ source
185
+ }];
186
+ });
187
+ }
188
+ function readAgentPackage(value) {
189
+ const agentPackage = record(value);
190
+ const capabilities = record(agentPackage?.capabilities);
191
+ const storage = record(agentPackage?.storage);
192
+ const relativePath = relativeContributionPath(storage?.relativePath);
193
+ if (agentPackage?.schemaVersion !== 1 || typeof agentPackage.id !== "string" || typeof agentPackage.name !== "string" || typeof agentPackage.version !== "string" || !capabilities || !Array.isArray(capabilities.required) || !Array.isArray(capabilities.optional) || !storage || relativePath === null) return null;
194
+ const resources = record(agentPackage.resources);
195
+ return {
196
+ id: boundedString(agentPackage.id, 200),
197
+ name: boundedString(agentPackage.name, 500),
198
+ version: boundedString(agentPackage.version, 100),
199
+ description: typeof agentPackage.description === "string" ? boundedString(agentPackage.description, 2e3) : null,
200
+ capabilities: {
201
+ required: capabilities.required.filter((item) => typeof item === "string").slice(0, 100).map((item) => boundedString(item, 200)),
202
+ optional: capabilities.optional.filter((item) => typeof item === "string").slice(0, 100).map((item) => boundedString(item, 200))
203
+ },
204
+ storage: { relativePath },
205
+ resources: resources ? Object.fromEntries(Object.entries(resources).flatMap(([id, candidate]) => {
206
+ const state = record(candidate);
207
+ if (state?.schema !== "supercode/package-resource-state-v1") return [];
208
+ if (state.status === "absent" && typeof state.contributionId === "string") {
209
+ return [[boundedString(id, 200), {
210
+ schema: "supercode/package-resource-state-v1",
211
+ status: "absent",
212
+ contributionId: boundedString(state.contributionId, 200)
213
+ }]];
214
+ }
215
+ if (state.status === "error" && typeof state.contributionId === "string" && typeof state.message === "string") {
216
+ return [[boundedString(id, 200), {
217
+ schema: "supercode/package-resource-state-v1",
218
+ status: "error",
219
+ contributionId: boundedString(state.contributionId, 200),
220
+ message: boundedString(state.message)
221
+ }]];
222
+ }
223
+ const resource = record(state?.resource);
224
+ const data = contributionJson(resource?.data);
225
+ if (state?.status !== "ready" || resource?.schema !== "supercode/package-resource-v1" || typeof resource.contributionId !== "string" || !["json", "text"].includes(resource.format) || typeof resource.mediaType !== "string" || data === void 0) return [];
226
+ return [[boundedString(id, 200), {
227
+ schema: "supercode/package-resource-state-v1",
228
+ status: "ready",
229
+ resource: {
230
+ schema: "supercode/package-resource-v1",
231
+ contributionId: boundedString(resource.contributionId, 200),
232
+ format: resource.format,
233
+ mediaType: boundedString(resource.mediaType, 200),
234
+ data
235
+ }
236
+ }]];
237
+ })) : {}
238
+ };
239
+ }
240
+ function readAgentPackageCapabilityState(value) {
241
+ const state = record(value);
242
+ if (!state) return null;
243
+ const fields = ["availableRequired", "missingRequired", "enabledOptional", "unavailableOptional"];
244
+ if (!fields.every((field) => Array.isArray(state[field]))) return null;
245
+ return {
246
+ ready: state.ready === true,
247
+ ...Object.fromEntries(fields.map((field) => [
248
+ field,
249
+ state[field].filter((item) => typeof item === "string").slice(0, 100).map((item) => boundedString(item, 200))
250
+ ]))
251
+ };
252
+ }
120
253
  function relativeAge(updatedAt, now = Date.now()) {
121
254
  if (typeof updatedAt !== "number" || !Number.isFinite(updatedAt) || updatedAt <= 0) return "";
122
255
  const delta = Math.max(0, now - updatedAt);
@@ -515,12 +648,40 @@ function readToolPresentation(value, entry) {
515
648
  matches: nullableNumber(item.matches) ?? generated.matches
516
649
  };
517
650
  }
651
+ var OPAQUE_RAW_CHARS = 4e3;
652
+ function opaqueEntry(item) {
653
+ const previouslyOpaque = item.role === "opaque";
654
+ let raw;
655
+ if (previouslyOpaque && typeof item.raw === "string") {
656
+ raw = item.raw;
657
+ } else {
658
+ try {
659
+ raw = JSON.stringify(item, null, 2) ?? "";
660
+ } catch {
661
+ raw = "[unserializable entry payload]";
662
+ }
663
+ }
664
+ const kind = previouslyOpaque && typeof item.kind === "string" ? item.kind : typeof item.role === "string" ? item.role : "";
665
+ return {
666
+ id: item.id,
667
+ role: "opaque",
668
+ kind: boundedString(kind, 120) || "unknown",
669
+ text: typeof item.text === "string" ? boundedString(item.text, OPAQUE_RAW_CHARS) : "",
670
+ ts: nullableNumber(item.ts),
671
+ truncated: item.truncated === true || raw.length > OPAQUE_RAW_CHARS,
672
+ raw: boundedString(raw, OPAQUE_RAW_CHARS)
673
+ };
674
+ }
518
675
  function readTranscript(value) {
519
676
  if (!Array.isArray(value)) return [];
520
677
  const result = [];
521
678
  for (const candidate of value) {
522
679
  const item = record(candidate);
523
- if (!item || typeof item.id !== "string" || typeof item.text !== "string" || !ROLES.has(item.role)) continue;
680
+ if (!item || typeof item.id !== "string") continue;
681
+ if (typeof item.text !== "string" || !ROLES.has(item.role)) {
682
+ result.push(opaqueEntry(item));
683
+ continue;
684
+ }
524
685
  const entry = {
525
686
  id: item.id,
526
687
  role: item.role,
@@ -578,6 +739,75 @@ function readTranscript(value) {
578
739
  }
579
740
  return result;
580
741
  }
742
+ function readParticipant(value) {
743
+ const participant = record(value);
744
+ if (!participant) return { kind: "local-user", label: null, origin: null };
745
+ return {
746
+ kind: ["local-user", "foreign", "unknown"].includes(participant.kind) ? participant.kind : "unknown",
747
+ label: typeof participant.label === "string" && participant.label ? boundedString(participant.label, 200) : null,
748
+ origin: typeof participant.origin === "string" && participant.origin ? boundedString(participant.origin, 100) : null
749
+ };
750
+ }
751
+ function readWorkspaceRef(value, fallbackPath = "") {
752
+ const ref = record(value);
753
+ if (ref && ["repo", "none", "channel"].includes(ref.kind)) {
754
+ return {
755
+ kind: ref.kind,
756
+ value: ref.kind === "none" ? null : typeof ref.value === "string" && ref.value ? boundedString(ref.value, 500) : null
757
+ };
758
+ }
759
+ return fallbackPath ? { kind: "repo", value: boundedString(fallbackPath, 500) } : { kind: "none", value: null };
760
+ }
761
+ function readMirror(value) {
762
+ const mirror = record(value);
763
+ if (!mirror || typeof mirror.canonicalHarness !== "string" || !mirror.canonicalHarness) return null;
764
+ return {
765
+ canonicalHarness: boundedString(mirror.canonicalHarness, 100),
766
+ canonicalKey: typeof mirror.canonicalKey === "string" && mirror.canonicalKey ? boundedString(mirror.canonicalKey, 300) : null,
767
+ bounded: mirror.bounded === true,
768
+ truncated: mirror.truncated === true,
769
+ origin: typeof mirror.origin === "string" && mirror.origin ? boundedString(mirror.origin, 200) : null
770
+ };
771
+ }
772
+ function readHolder(value) {
773
+ const holder = record(value);
774
+ if (!holder) return null;
775
+ const surface = typeof holder.surface === "string" && holder.surface ? boundedString(holder.surface, 100) : null;
776
+ return {
777
+ surface,
778
+ canTakeOver: holder.canTakeOver === true && surface !== null && surface !== "supercode"
779
+ };
780
+ }
781
+ var TRIGGER_KINDS = ["human", "channel", "cron", "heartbeat", "webhook", "parent", "api", "unknown"];
782
+ function readTrigger(value) {
783
+ const trigger = record(value);
784
+ if (!trigger) return null;
785
+ const kind = trigger.kind === "manual" ? "human" : trigger.kind;
786
+ return {
787
+ kind: TRIGGER_KINDS.includes(kind) ? kind : "unknown",
788
+ label: typeof trigger.label === "string" && trigger.label ? boundedString(trigger.label, 200) : null,
789
+ surface: typeof trigger.surface === "string" && trigger.surface ? boundedString(trigger.surface, 300) : null
790
+ };
791
+ }
792
+ function readCrossSurface(value) {
793
+ const moved = record(value);
794
+ if (!moved || typeof moved.state !== "string" || !moved.state) return null;
795
+ return {
796
+ state: boundedString(moved.state, 100),
797
+ platform: typeof moved.platform === "string" && moved.platform ? boundedString(moved.platform, 100) : null,
798
+ error: typeof moved.error === "string" && moved.error ? boundedString(moved.error, 500) : null
799
+ };
800
+ }
801
+ function readRecurring(value) {
802
+ const recurring = record(value);
803
+ if (!recurring || typeof recurring.groupKey !== "string" || !recurring.groupKey) return null;
804
+ const runs = Number.isSafeInteger(recurring.runs) && recurring.runs > 0 ? recurring.runs : 1;
805
+ return {
806
+ groupKey: boundedString(recurring.groupKey, 300),
807
+ runs,
808
+ lastStatus: ["ok", "failed", "mixed"].includes(recurring.lastStatus) ? recurring.lastStatus : null
809
+ };
810
+ }
581
811
  function readSessions(value) {
582
812
  if (!Array.isArray(value)) return [];
583
813
  return value.flatMap((raw) => {
@@ -599,7 +829,13 @@ function readSessions(value) {
599
829
  live: row.live === true,
600
830
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null,
601
831
  ...Number.isSafeInteger(row.subagentCount) && row.subagentCount > 0 ? { subagentCount: row.subagentCount } : {},
602
- activity: ACTIVITY_PRIORITY[row.activity] !== void 0 ? row.activity : void 0
832
+ activity: ACTIVITY_PRIORITY[row.activity] !== void 0 ? row.activity : void 0,
833
+ ...row.participant !== void 0 ? { participant: readParticipant(row.participant) } : {},
834
+ workspaceRef: readWorkspaceRef(row.workspaceRef, string(row.cwd)),
835
+ ...readMirror(row.mirror) ? { mirror: readMirror(row.mirror) } : {},
836
+ ...readTrigger(row.trigger) ? { trigger: readTrigger(row.trigger) } : {},
837
+ ...readRecurring(row.recurring) ? { recurring: readRecurring(row.recurring) } : {},
838
+ ...readCrossSurface(row.crossSurface) ? { crossSurface: readCrossSurface(row.crossSurface) } : {}
603
839
  }];
604
840
  });
605
841
  }
@@ -783,6 +1019,10 @@ function normalizeUiState(value) {
783
1019
  canConfigureSettings: raw.canConfigureSettings === true,
784
1020
  messaging: raw.messaging === "live_peer" ? "live_peer" : null,
785
1021
  workspace: string(raw.workspace),
1022
+ workspaceRef: readWorkspaceRef(raw.workspaceRef, string(raw.workspace)),
1023
+ participant: readParticipant(raw.participant),
1024
+ mirror: readMirror(raw.mirror),
1025
+ holder: readHolder(raw.holder),
786
1026
  taskPlan: readTaskPlan(raw.taskPlan),
787
1027
  semantics: readSemantics(raw.semantics),
788
1028
  terminalHandoff: readTerminalHandoff(raw.terminalHandoff),
@@ -834,7 +1074,12 @@ function normalizeUiState(value) {
834
1074
  subagentInspector: readSubagentInspector(raw.subagentInspector),
835
1075
  attached: readAttached(raw.attached),
836
1076
  owned: readAttached(raw.owned),
837
- attachError: attachError && typeof attachError.key === "string" && typeof attachError.message === "string" ? { key: attachError.key, message: attachError.message } : null
1077
+ attachError: attachError && typeof attachError.key === "string" && typeof attachError.message === "string" ? { key: attachError.key, message: attachError.message } : null,
1078
+ agentPackage: readAgentPackage(raw.agentPackage),
1079
+ contributions: normalizeContributions(raw.contributions),
1080
+ agentPackageError: typeof raw.agentPackageError === "string" ? boundedString(raw.agentPackageError) : null,
1081
+ agentPackageCapabilityState: readAgentPackageCapabilityState(raw.agentPackageCapabilityState),
1082
+ agentPackageCapabilityError: typeof raw.agentPackageCapabilityError === "string" ? boundedString(raw.agentPackageCapabilityError) : null
838
1083
  };
839
1084
  }
840
1085
  function harnessDisplayName(id) {
@@ -939,6 +1184,12 @@ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } fro
939
1184
 
940
1185
  // src/memory.js
941
1186
  var MEMORY_LIMIT = 100;
1187
+ var registry = /* @__PURE__ */ new Set();
1188
+ function uiMemory() {
1189
+ const map = /* @__PURE__ */ new Map();
1190
+ registry.add(map);
1191
+ return map;
1192
+ }
942
1193
  function boundedSet(map, key, value) {
943
1194
  map.delete(key);
944
1195
  map.set(key, value);
@@ -1292,7 +1543,7 @@ function MessageImages({ items, adapter }) {
1292
1543
  }
1293
1544
 
1294
1545
  // src/intent.js
1295
- var CONFIRMABLE_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "branch", "reduce", "export"]);
1546
+ var CONFIRMABLE_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "branch", "reduce", "export", "take_over"]);
1296
1547
  async function dispatchConfirmedIntent(adapter, intent) {
1297
1548
  if (CONFIRMABLE_ACTIONS.has(intent.action) && adapter.confirmIntent) {
1298
1549
  const confirmed = await adapter.confirmIntent(intent);
@@ -1317,7 +1568,7 @@ function useAutosizeTextarea(ref, value) {
1317
1568
 
1318
1569
  // src/composer.jsx
1319
1570
  import { jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
1320
- var composerMemory = /* @__PURE__ */ new Map();
1571
+ var composerMemory = uiMemory();
1321
1572
  function ExecutionModeSelect({ modes = [], value, onChange, disabled = false, label = "Run in" }) {
1322
1573
  if (modes.length < 2) return null;
1323
1574
  return /* @__PURE__ */ jsxs3("label", { className: "scui-execution-mode", title: disabled ? void 0 : `${label}: ${value}`, children: [
@@ -1613,7 +1864,11 @@ function languageLabel(info) {
1613
1864
  }
1614
1865
  function frameCode(render2, tokens, index, options, env, self) {
1615
1866
  const label = markdown.utils.escapeHtml(languageLabel(tokens[index]?.info ?? ""));
1616
- return `<div class="scui-code-block"><div class="scui-code-head"><span>${label}</span><button class="scui-code-copy" type="button" aria-label="Copy code" title="Copy code">${COPY_ICON}<span>Copy</span></button></div>${render2(tokens, index, options, env, self)}</div>`;
1867
+ const body = render2(tokens, index, options, env, self).replace(
1868
+ "<pre>",
1869
+ `<pre tabindex="0" role="region" aria-label="${label} code">`
1870
+ );
1871
+ return `<div class="scui-code-block"><div class="scui-code-head"><span>${label}</span><button class="scui-code-copy" type="button" aria-label="Copy code" title="Copy code">${COPY_ICON}<span>Copy</span></button></div>${body}</div>`;
1617
1872
  }
1618
1873
  for (const kind of ["fence", "code_block"]) {
1619
1874
  const render2 = markdown.renderer.rules[kind];
@@ -1950,6 +2205,26 @@ function TechnicalDetails({ entry }) {
1950
2205
  ] });
1951
2206
  }
1952
2207
  function TranscriptEntry({ entry, state, adapter }) {
2208
+ if (entry.role === "opaque") {
2209
+ return /* @__PURE__ */ jsxs4("article", { className: "scui-message scui-opaque", "data-role": "opaque", "data-kind": entry.kind, "aria-label": "Unrecognized entry", children: [
2210
+ /* @__PURE__ */ jsxs4("div", { className: "scui-notice", "data-code": "opaque-entry", children: [
2211
+ "Unrecognized entry kind ",
2212
+ /* @__PURE__ */ jsx5("code", { children: entry.kind }),
2213
+ " \u2014 kept as-is, nothing dropped"
2214
+ ] }),
2215
+ entry.text ? /* @__PURE__ */ jsx5(Markdown, { value: entry.text, copyText: adapter?.copyText }) : null,
2216
+ entry.raw ? /* @__PURE__ */ jsxs4("details", { className: "scui-tool-technical", children: [
2217
+ /* @__PURE__ */ jsx5("summary", { children: "Technical details" }),
2218
+ /* @__PURE__ */ jsx5("div", { children: /* @__PURE__ */ jsxs4("section", { children: [
2219
+ /* @__PURE__ */ jsx5("strong", { children: "Raw entry" }),
2220
+ /* @__PURE__ */ jsxs4("pre", { children: [
2221
+ entry.raw,
2222
+ entry.truncated ? "\n[truncated]" : ""
2223
+ ] })
2224
+ ] }) })
2225
+ ] }) : null
2226
+ ] });
2227
+ }
1953
2228
  if (entry.role === "request") return /* @__PURE__ */ jsx5(RequestCard, { entry, adapter, canRespond: state.canRespond });
1954
2229
  if (entry.role === "reasoning") {
1955
2230
  return /* @__PURE__ */ jsxs4("details", { className: "scui-reasoning", open: entry.streaming, children: [
@@ -2079,7 +2354,7 @@ function SessionDetails({ semantics }) {
2079
2354
  ] })
2080
2355
  ] });
2081
2356
  }
2082
- var conversationMemory = /* @__PURE__ */ new Map();
2357
+ var conversationMemory = uiMemory();
2083
2358
  function ConversationAnnouncements({ state }) {
2084
2359
  const previousBusy = useRef4(state.busy);
2085
2360
  const [announcement, setAnnouncement] = useState3("");
@@ -2259,7 +2534,7 @@ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
2259
2534
  // src/sessions.jsx
2260
2535
  import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState4 } from "preact/hooks";
2261
2536
  import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
2262
- var sessionListMemory = /* @__PURE__ */ new Map();
2537
+ var sessionListMemory = uiMemory();
2263
2538
  function sessionPathParts(value) {
2264
2539
  const complete = String(value ?? "").replaceAll("\\", "/");
2265
2540
  const boundary = complete.lastIndexOf("/");
@@ -2281,11 +2556,55 @@ function SessionRow({ row, state, onOpen, onOpenSubagents, now = Date.now() }) {
2281
2556
  const unreadCount = attention?.unreadCount ?? 0;
2282
2557
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2283
2558
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2284
- return /* @__PURE__ */ jsxs6("article", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, children: [
2559
+ const nounBadges = [];
2560
+ if (row.trigger && row.trigger.kind !== "manual" && row.trigger.kind !== "human") {
2561
+ const runs = row.recurring?.runs;
2562
+ const detail = row.trigger.label || row.trigger.surface || "";
2563
+ nounBadges.push(
2564
+ /* @__PURE__ */ jsxs6("small", { className: "scui-session-noun", "data-noun": "trigger", "data-kind": row.trigger.kind, "data-surface": row.trigger.surface ?? void 0, "data-status": row.recurring?.lastStatus ?? void 0, children: [
2565
+ row.trigger.kind,
2566
+ detail ? ` \xB7 ${detail}` : "",
2567
+ runs && runs > 1 ? ` \xB7 \xD7${runs}` : ""
2568
+ ] }, "trigger")
2569
+ );
2570
+ }
2571
+ if (row.crossSurface) {
2572
+ nounBadges.push(
2573
+ /* @__PURE__ */ jsxs6("small", { className: "scui-session-noun", "data-noun": "cross-surface", "data-state": row.crossSurface.state, title: row.crossSurface.error ?? void 0, children: [
2574
+ "\u2192",
2575
+ row.crossSurface.platform ?? "elsewhere",
2576
+ " \xB7 ",
2577
+ row.crossSurface.state
2578
+ ] }, "cross-surface")
2579
+ );
2580
+ }
2581
+ if (row.participant && row.participant.kind !== "local-user") {
2582
+ nounBadges.push(
2583
+ /* @__PURE__ */ jsx7("small", { className: "scui-session-noun", "data-noun": "participant", "data-kind": row.participant.kind, children: row.participant.kind === "foreign" ? `${row.participant.label ?? "someone else"}${row.participant.origin ? ` via ${row.participant.origin}` : ""}` : "unknown participant" }, "participant")
2584
+ );
2585
+ }
2586
+ if (row.mirror) {
2587
+ nounBadges.push(
2588
+ /* @__PURE__ */ jsxs6("small", { className: "scui-session-noun", "data-noun": "mirror", "data-truncated": row.mirror.truncated, children: [
2589
+ "mirror of ",
2590
+ harnessDisplayName(row.mirror.canonicalHarness)
2591
+ ] }, "mirror")
2592
+ );
2593
+ }
2594
+ if (row.workspaceRef?.kind === "channel") {
2595
+ nounBadges.push(
2596
+ /* @__PURE__ */ jsxs6("small", { className: "scui-session-noun", "data-noun": "workspace", "data-kind": "channel", children: [
2597
+ "#",
2598
+ row.workspaceRef.value ?? "channel"
2599
+ ] }, "workspace")
2600
+ );
2601
+ }
2602
+ return /* @__PURE__ */ jsxs6("article", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "data-workspace-kind": row.workspaceRef?.kind, children: [
2285
2603
  /* @__PURE__ */ jsxs6("button", { className: "scui-session-main", "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${row.writable ? "" : " \xB7 Read-only"}${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: [
2286
2604
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
2287
2605
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-copy", children: [
2288
2606
  /* @__PURE__ */ jsx7("span", { className: "scui-session-title", children: /* @__PURE__ */ jsx7("strong", { children: title }) }),
2607
+ nounBadges.length ? /* @__PURE__ */ jsx7("span", { className: "scui-session-nouns", children: nounBadges }) : null,
2289
2608
  path.complete ? /* @__PURE__ */ jsxs6("small", { className: "scui-session-path", title: row.cwd, children: [
2290
2609
  /* @__PURE__ */ jsx7("span", { className: "scui-session-path-leading", children: path.leading }),
2291
2610
  path.separator ? /* @__PURE__ */ jsx7("span", { className: "scui-session-path-separator", children: path.separator }) : null,
@@ -2700,10 +3019,54 @@ function HarnessPicker({ harnesses, state, value, disabled = false, adapter, onC
2700
3019
 
2701
3020
  // src/messenger.jsx
2702
3021
  import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs9 } from "preact/jsx-runtime";
2703
- var pendingMessageMemory = /* @__PURE__ */ new Map();
2704
- var messengerViewMemory = /* @__PURE__ */ new Map();
2705
- var newChatMemory = /* @__PURE__ */ new Map();
3022
+ var pendingMessageMemory = uiMemory();
3023
+ var messengerViewMemory = uiMemory();
3024
+ var newChatMemory = uiMemory();
2706
3025
  var TRACKED_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "detach", "branch", "reduce", "terminal", "export", "steer", "interrupt", "respond", "configureHarness", "refresh", "loadEarlier", "loadSessions"]);
3026
+ function UniversalNouns({ state, adapter }) {
3027
+ const banners = [];
3028
+ if (state.participant && state.participant.kind !== "local-user") {
3029
+ banners.push(
3030
+ /* @__PURE__ */ jsx10("div", { className: "scui-notice scui-noun-banner", "data-noun": "participant", "data-kind": state.participant.kind, children: state.participant.kind === "foreign" ? /* @__PURE__ */ jsxs9(Fragment5, { children: [
3031
+ "This is ",
3032
+ /* @__PURE__ */ jsx10("strong", { children: state.participant.label ?? "someone else's" }),
3033
+ " conversation",
3034
+ state.participant.origin ? /* @__PURE__ */ jsxs9(Fragment5, { children: [
3035
+ " via ",
3036
+ state.participant.origin
3037
+ ] }) : null,
3038
+ "."
3039
+ ] }) : /* @__PURE__ */ jsx10(Fragment5, { children: "Unknown participant \u2014 this conversation was not started here." }) }, "participant")
3040
+ );
3041
+ }
3042
+ if (state.mirror) {
3043
+ banners.push(
3044
+ /* @__PURE__ */ jsxs9("div", { className: "scui-notice scui-noun-banner", "data-noun": "mirror", "data-truncated": state.mirror.truncated, children: [
3045
+ state.mirror.bounded ? "Bounded mirror" : "Mirror",
3046
+ " of a ",
3047
+ /* @__PURE__ */ jsx10("strong", { children: harnessDisplayName(state.mirror.canonicalHarness) }),
3048
+ " session \u2014 the canonical record lives in its own store",
3049
+ state.mirror.truncated ? " (truncated here)" : "",
3050
+ ".",
3051
+ state.mirror.canonicalKey ? /* @__PURE__ */ jsx10("button", { type: "button", onClick: () => adapter.onIntent({ action: "open_session", key: state.mirror.canonicalKey }), children: "Open canonical session" }) : null
3052
+ ] }, "mirror")
3053
+ );
3054
+ }
3055
+ if (state.holder && state.holder.surface !== "supercode") {
3056
+ banners.push(
3057
+ /* @__PURE__ */ jsxs9("div", { className: "scui-notice scui-noun-banner", "data-noun": "holder", "data-surface": state.holder.surface ?? "unheld", children: [
3058
+ state.holder.surface ? /* @__PURE__ */ jsxs9(Fragment5, { children: [
3059
+ "Held by ",
3060
+ /* @__PURE__ */ jsx10("strong", { children: state.holder.surface }),
3061
+ " right now."
3062
+ ] }) : /* @__PURE__ */ jsx10(Fragment5, { children: "No surface holds this session right now." }),
3063
+ state.holder.canTakeOver ? /* @__PURE__ */ jsx10("button", { type: "button", onClick: () => dispatchConfirmedIntent(adapter, { action: "take_over" }), children: "Take over" }) : null
3064
+ ] }, "holder")
3065
+ );
3066
+ }
3067
+ if (!banners.length) return null;
3068
+ return /* @__PURE__ */ jsx10("div", { className: "scui-noun-banners", children: banners });
3069
+ }
2707
3070
  function Receipt({ state, adapter }) {
2708
3071
  const receipt = state.reductionReceipt;
2709
3072
  if (receipt) return /* @__PURE__ */ jsx10("div", { className: "scui-receipt", children: /* @__PURE__ */ jsxs9("span", { children: [
@@ -2967,6 +3330,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2967
3330
  state.recoverable ? /* @__PURE__ */ jsx10("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
2968
3331
  ] }) : null,
2969
3332
  /* @__PURE__ */ jsx10(Receipt, { state, adapter }),
3333
+ /* @__PURE__ */ jsx10(UniversalNouns, { state: actionState, adapter: trackedAdapter }),
2970
3334
  /* @__PURE__ */ jsx10(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
2971
3335
  /* @__PURE__ */ jsx10(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
2972
3336
  /* @__PURE__ */ jsx10(Advisory, { state: actionState, adapter: trackedAdapter, value: state.interopSettings?.advisories[0] ?? null, onReview: (recommendedChange) => setSettings({ recommendedChange }) }),