@volter-ai-dev/supercode-ui 0.1.67 → 0.1.69

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,
@@ -644,12 +648,40 @@ function readToolPresentation(value, entry) {
644
648
  matches: nullableNumber(item.matches) ?? generated.matches
645
649
  };
646
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
+ }
647
675
  function readTranscript(value) {
648
676
  if (!Array.isArray(value)) return [];
649
677
  const result = [];
650
678
  for (const candidate of value) {
651
679
  const item = record(candidate);
652
- 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
+ }
653
685
  const entry = {
654
686
  id: item.id,
655
687
  role: item.role,
@@ -707,6 +739,75 @@ function readTranscript(value) {
707
739
  }
708
740
  return result;
709
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
+ }
710
811
  function readSessions(value) {
711
812
  if (!Array.isArray(value)) return [];
712
813
  return value.flatMap((raw) => {
@@ -728,7 +829,13 @@ function readSessions(value) {
728
829
  live: row.live === true,
729
830
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null,
730
831
  ...Number.isSafeInteger(row.subagentCount) && row.subagentCount > 0 ? { subagentCount: row.subagentCount } : {},
731
- 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) } : {}
732
839
  }];
733
840
  });
734
841
  }
@@ -912,6 +1019,10 @@ function normalizeUiState(value) {
912
1019
  canConfigureSettings: raw.canConfigureSettings === true,
913
1020
  messaging: raw.messaging === "live_peer" ? "live_peer" : null,
914
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),
915
1026
  taskPlan: readTaskPlan(raw.taskPlan),
916
1027
  semantics: readSemantics(raw.semantics),
917
1028
  terminalHandoff: readTerminalHandoff(raw.terminalHandoff),
@@ -1073,6 +1184,12 @@ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } fro
1073
1184
 
1074
1185
  // src/memory.js
1075
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
+ }
1076
1193
  function boundedSet(map, key, value) {
1077
1194
  map.delete(key);
1078
1195
  map.set(key, value);
@@ -1426,7 +1543,7 @@ function MessageImages({ items, adapter }) {
1426
1543
  }
1427
1544
 
1428
1545
  // src/intent.js
1429
- 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"]);
1430
1547
  async function dispatchConfirmedIntent(adapter, intent) {
1431
1548
  if (CONFIRMABLE_ACTIONS.has(intent.action) && adapter.confirmIntent) {
1432
1549
  const confirmed = await adapter.confirmIntent(intent);
@@ -1451,7 +1568,7 @@ function useAutosizeTextarea(ref, value) {
1451
1568
 
1452
1569
  // src/composer.jsx
1453
1570
  import { jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
1454
- var composerMemory = /* @__PURE__ */ new Map();
1571
+ var composerMemory = uiMemory();
1455
1572
  function ExecutionModeSelect({ modes = [], value, onChange, disabled = false, label = "Run in" }) {
1456
1573
  if (modes.length < 2) return null;
1457
1574
  return /* @__PURE__ */ jsxs3("label", { className: "scui-execution-mode", title: disabled ? void 0 : `${label}: ${value}`, children: [
@@ -1747,7 +1864,11 @@ function languageLabel(info) {
1747
1864
  }
1748
1865
  function frameCode(render2, tokens, index, options, env, self) {
1749
1866
  const label = markdown.utils.escapeHtml(languageLabel(tokens[index]?.info ?? ""));
1750
- 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>`;
1751
1872
  }
1752
1873
  for (const kind of ["fence", "code_block"]) {
1753
1874
  const render2 = markdown.renderer.rules[kind];
@@ -2084,6 +2205,26 @@ function TechnicalDetails({ entry }) {
2084
2205
  ] });
2085
2206
  }
2086
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
+ }
2087
2228
  if (entry.role === "request") return /* @__PURE__ */ jsx5(RequestCard, { entry, adapter, canRespond: state.canRespond });
2088
2229
  if (entry.role === "reasoning") {
2089
2230
  return /* @__PURE__ */ jsxs4("details", { className: "scui-reasoning", open: entry.streaming, children: [
@@ -2213,7 +2354,7 @@ function SessionDetails({ semantics }) {
2213
2354
  ] })
2214
2355
  ] });
2215
2356
  }
2216
- var conversationMemory = /* @__PURE__ */ new Map();
2357
+ var conversationMemory = uiMemory();
2217
2358
  function ConversationAnnouncements({ state }) {
2218
2359
  const previousBusy = useRef4(state.busy);
2219
2360
  const [announcement, setAnnouncement] = useState3("");
@@ -2393,7 +2534,7 @@ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
2393
2534
  // src/sessions.jsx
2394
2535
  import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState4 } from "preact/hooks";
2395
2536
  import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
2396
- var sessionListMemory = /* @__PURE__ */ new Map();
2537
+ var sessionListMemory = uiMemory();
2397
2538
  function sessionPathParts(value) {
2398
2539
  const complete = String(value ?? "").replaceAll("\\", "/");
2399
2540
  const boundary = complete.lastIndexOf("/");
@@ -2415,11 +2556,55 @@ function SessionRow({ row, state, onOpen, onOpenSubagents, now = Date.now() }) {
2415
2556
  const unreadCount = attention?.unreadCount ?? 0;
2416
2557
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2417
2558
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2418
- 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: [
2419
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: [
2420
2604
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
2421
2605
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-copy", children: [
2422
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,
2423
2608
  path.complete ? /* @__PURE__ */ jsxs6("small", { className: "scui-session-path", title: row.cwd, children: [
2424
2609
  /* @__PURE__ */ jsx7("span", { className: "scui-session-path-leading", children: path.leading }),
2425
2610
  path.separator ? /* @__PURE__ */ jsx7("span", { className: "scui-session-path-separator", children: path.separator }) : null,
@@ -2834,10 +3019,54 @@ function HarnessPicker({ harnesses, state, value, disabled = false, adapter, onC
2834
3019
 
2835
3020
  // src/messenger.jsx
2836
3021
  import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs9 } from "preact/jsx-runtime";
2837
- var pendingMessageMemory = /* @__PURE__ */ new Map();
2838
- var messengerViewMemory = /* @__PURE__ */ new Map();
2839
- var newChatMemory = /* @__PURE__ */ new Map();
3022
+ var pendingMessageMemory = uiMemory();
3023
+ var messengerViewMemory = uiMemory();
3024
+ var newChatMemory = uiMemory();
2840
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
+ }
2841
3070
  function Receipt({ state, adapter }) {
2842
3071
  const receipt = state.reductionReceipt;
2843
3072
  if (receipt) return /* @__PURE__ */ jsx10("div", { className: "scui-receipt", children: /* @__PURE__ */ jsxs9("span", { children: [
@@ -3101,6 +3330,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
3101
3330
  state.recoverable ? /* @__PURE__ */ jsx10("button", { type: "button", disabled: Boolean(action), onClick: () => trackedAdapter.onIntent({ action: "refresh" }), children: "Retry" }) : null
3102
3331
  ] }) : null,
3103
3332
  /* @__PURE__ */ jsx10(Receipt, { state, adapter }),
3333
+ /* @__PURE__ */ jsx10(UniversalNouns, { state: actionState, adapter: trackedAdapter }),
3104
3334
  /* @__PURE__ */ jsx10(Conversation, { state: actionState, adapter: trackedAdapter, components, slots, memoryKey, pending: pendingMessage, unreadAfterMessages }, memoryKey),
3105
3335
  /* @__PURE__ */ jsx10(ContinuationBar, { state: actionState, adapter: trackedAdapter, labels }),
3106
3336
  /* @__PURE__ */ jsx10(Advisory, { state: actionState, adapter: trackedAdapter, value: state.interopSettings?.advisories[0] ?? null, onReview: (recommendedChange) => setSettings({ recommendedChange }) }),
package/host.d.ts CHANGED
@@ -24,7 +24,7 @@ export class RemoteControllerHost {
24
24
  dispatch(intent: unknown): Promise<RemoteUiFrame>;
25
25
  getFrame(): RemoteUiFrame;
26
26
  refreshProjection(): RemoteUiFrame;
27
- restore(identity: string, connection: 'observe' | 'attach'): Promise<RemoteUiFrame>;
27
+ restore(identity: string, connection: 'observe' | 'attach' | 'resume'): Promise<RemoteUiFrame>;
28
28
  subscribe(listener: (frame: RemoteUiFrame) => void): () => void;
29
29
  }
30
30
 
package/host.mjs CHANGED
@@ -119,8 +119,8 @@ export class RemoteControllerHost {
119
119
  if (typeof identity !== 'string' || !identity) {
120
120
  throw new TypeError('RemoteControllerHost restore identity must be a non-empty string.');
121
121
  }
122
- if (connection !== 'observe' && connection !== 'attach') {
123
- throw new TypeError('RemoteControllerHost restore connection must be observe or attach.');
122
+ if (connection !== 'observe' && connection !== 'attach' && connection !== 'resume') {
123
+ throw new TypeError('RemoteControllerHost restore connection must be observe, attach, or resume.');
124
124
  }
125
125
  const sequence = this.#sequence;
126
126
  await this.#controller.dispatch({ type: 'restore', identity, connection });
package/index.d.ts CHANGED
@@ -90,6 +90,66 @@ export interface HarnessOption {
90
90
  preferredLaunchMode?: ExecutionMode | null;
91
91
  }
92
92
 
93
+ /** UNI-8: whose conversation a session is. */
94
+ export interface ParticipantModel {
95
+ kind: 'local-user' | 'foreign' | 'unknown';
96
+ /** The human ("@jane"), when known. */
97
+ label: string | null;
98
+ /** The surface the participant reached the agent through ("slack", "acp", ...). */
99
+ origin: string | null;
100
+ }
101
+
102
+ /** UNI-9: a typed workspace. `none` is a first-class value, not an empty path. */
103
+ export interface WorkspaceRefModel {
104
+ kind: 'repo' | 'none' | 'channel';
105
+ /** Path for `repo`, channel label for `channel`, null for `none`. */
106
+ value: string | null;
107
+ }
108
+
109
+ /** UNI-10: a bounded/truncated mirror of a session canonical in another store. */
110
+ export interface MirrorModel {
111
+ canonicalHarness: HarnessId;
112
+ /** Session key to cross-link to, when the host can resolve it. */
113
+ canonicalKey: string | null;
114
+ bounded: boolean;
115
+ truncated: boolean;
116
+ origin: string | null;
117
+ }
118
+
119
+ /** UNI-11: which surface holds this session now; take-over is the transition. */
120
+ export interface HolderModel {
121
+ /** 'supercode' = held here; any other string = that surface; null = unheld. */
122
+ surface: string | null;
123
+ /** Take-over offered (flows through confirmIntent with action 'take_over'). */
124
+ canTakeOver: boolean;
125
+ }
126
+
127
+ /** UNI-12/ORCH-6: why a session exists — the server's own `Trigger` enum.
128
+ * `manual` is the pre-ORCH-6 spelling of `human`, accepted on input and
129
+ * normalized to `human`. */
130
+ export type TriggerKind = 'human' | 'channel' | 'cron' | 'heartbeat' | 'webhook' | 'parent' | 'api' | 'unknown';
131
+ export interface TriggerModel {
132
+ kind: TriggerKind;
133
+ label: string | null;
134
+ /** ORCH-6: the compact surface key this conversation is reached on
135
+ * (`telegram:dm:123456`), when it has one. */
136
+ surface?: string | null;
137
+ }
138
+
139
+ /** ORCH-6: a conversation that moved to another surface (Hermes `handoff_*`). */
140
+ export interface CrossSurfaceModel {
141
+ state: string;
142
+ platform: string | null;
143
+ error: string | null;
144
+ }
145
+
146
+ /** UNI-12: one row representing N runs of a recurring trigger. */
147
+ export interface RecurringModel {
148
+ groupKey: string;
149
+ runs: number;
150
+ lastStatus: 'ok' | 'failed' | 'mixed' | null;
151
+ }
152
+
93
153
  export interface SessionRowModel {
94
154
  key: string;
95
155
  harness: HarnessId;
@@ -112,6 +172,18 @@ export interface SessionRowModel {
112
172
  subagentCount?: number;
113
173
  /** Explicit activity used for child rows in the on-demand inspector. */
114
174
  activity?: SessionActivity;
175
+ /** UNI-8: whose conversation this is (absent = local user). */
176
+ participant?: ParticipantModel;
177
+ /** UNI-9: typed workspace (derived from `cwd` when a host omits it). */
178
+ workspaceRef?: WorkspaceRefModel;
179
+ /** UNI-10: canonical-elsewhere provenance. */
180
+ mirror?: MirrorModel;
181
+ /** UNI-12: trigger provenance. */
182
+ trigger?: TriggerModel;
183
+ /** UNI-12: recurring-run grouping. */
184
+ recurring?: RecurringModel;
185
+ /** ORCH-6: moved-to-another-surface state, when the harness publishes one. */
186
+ crossSurface?: CrossSurfaceModel;
115
187
  }
116
188
 
117
189
  export interface SubagentInspectorModel {
@@ -360,6 +432,14 @@ export interface SupercodeUiState {
360
432
  canConfigureSettings: boolean;
361
433
  messaging: 'live_peer' | null;
362
434
  workspace: string;
435
+ /** UNI-9: typed workspace for the attached conversation (always present after normalization). */
436
+ workspaceRef?: WorkspaceRefModel;
437
+ /** UNI-8: whose conversation the attached session is (defaults to local-user). */
438
+ participant?: ParticipantModel;
439
+ /** UNI-10: set when the attached session is a mirror. */
440
+ mirror?: MirrorModel | null;
441
+ /** UNI-11: which surface holds the attached session now. */
442
+ holder?: HolderModel | null;
363
443
  taskPlan: TaskPlanModel;
364
444
  semantics: SessionSemanticsModel;
365
445
  terminalHandoff: { program: string; arguments: string[]; cwd: string } | null;
@@ -619,6 +699,29 @@ export function selectContributions(
619
699
  ): AgentContributionModel[];
620
700
  export function harnessDisplayName(id: string): string;
621
701
  export function sessionDisplayName(session: Pick<SessionRowModel, 'name' | 'title'>): string;
702
+ /** ORCH-6: one `harness.v1.sessions.discover` / `sessions.load` row, as the
703
+ * service publishes it. Structurally the SDK's `SessionDescriptor`; declared
704
+ * here so the UI package stays dependency-free. */
705
+ export interface HarnessSessionDescriptorModel {
706
+ locator: { harness: string; session_id: string; storage?: unknown };
707
+ cwd?: string | null;
708
+ title?: string | null;
709
+ updated_at_ms?: number | null;
710
+ message_count?: number | null;
711
+ model?: string | null;
712
+ child_session_count?: number;
713
+ trigger?: TriggerKind | 'manual';
714
+ surface?: { key?: string; platform?: string; kind?: string; chat_id?: string; thread_id?: string; participant_id?: string };
715
+ profile?: string;
716
+ recurrence?: { job_id: string; kind: string };
717
+ cross_surface?: { state: string; platform?: string; error?: string };
718
+ workspace?: WorkspaceRefModel | { kind: 'repo' | 'channel' | 'none'; value?: string };
719
+ }
720
+ /** ORCH-6: `telegram:dm:123456` — the compact surface key a person reads. */
721
+ export function surfaceLabel(surface: unknown): string;
722
+ /** ORCH-6: project service rows into list rows. The conversation nouns are
723
+ * read off the server's derivation, never re-derived on the client. */
724
+ export function sessionRowsFromDescriptors(descriptors: readonly HarnessSessionDescriptorModel[] | unknown): SessionRowModel[];
622
725
  export function relativeAge(updatedAt: number | null | undefined, now?: number): string;
623
726
  export function sessionActivity(state: SupercodeUiState, row: SessionRowModel): SessionActivity;
624
727
  export function projectAgentActivity(state: SupercodeUiState | unknown): AgentActivityModel;