@volter-ai-dev/supercode-ui 0.1.46 → 0.1.48

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/README.md CHANGED
@@ -206,8 +206,9 @@ errors, and owned/attached identities without reimplementing transcript or capab
206
206
  `projectSessionInventory` performs the corresponding title, latest-preview, activity, path, age,
207
207
  sorting, and row projection for raw machine-wide descriptors; the trusted host supplies only its
208
208
  opaque-key callback and retains the reversible locator map. Its `isWritable` capability callback
209
- is fail-closed: observed sessions remain neutral read-only channels until the host proves a real
210
- send or terminal-control path.
209
+ is fail-closed: unread badges remain neutral until the host proves a real send or terminal-control
210
+ path. Runtime activity remains independent, so a read-only channel can still truthfully show that
211
+ its external agent is working or needs input.
211
212
 
212
213
  ## Modularity contract
213
214
 
package/activity.mjs CHANGED
@@ -32,9 +32,12 @@ var EMPTY_UI_STATE = Object.freeze({
32
32
  canSend: false,
33
33
  canSteer: false,
34
34
  canResume: false,
35
+ supportsResume: false,
35
36
  continuationModes: Object.freeze([]),
36
37
  canBranch: false,
38
+ supportsBranch: false,
37
39
  canAttach: false,
40
+ supportsAttach: false,
38
41
  canDetach: false,
39
42
  canOpenTerminal: false,
40
43
  canExport: false,
@@ -712,9 +715,12 @@ function normalizeUiState(value) {
712
715
  canSend: raw.canSend === true,
713
716
  canSteer: raw.canSteer === true,
714
717
  canResume,
718
+ supportsResume: raw.supportsResume === true,
715
719
  continuationModes,
716
720
  canBranch: raw.canBranch === true,
721
+ supportsBranch: raw.supportsBranch === true,
717
722
  canAttach: raw.canAttach === true,
723
+ supportsAttach: raw.supportsAttach === true,
718
724
  canDetach: raw.canDetach === true,
719
725
  canOpenTerminal: raw.canOpenTerminal === true,
720
726
  canExport: raw.canExport === true,
@@ -784,7 +790,6 @@ function sessionDisplayName(session) {
784
790
  return title && title !== session.name ? title : session.name || "Untitled chat";
785
791
  }
786
792
  function sessionActivity(state, row) {
787
- if (!row.writable) return "observed";
788
793
  if (state.needsInput && row.active) return "needs-input";
789
794
  if (state.busy && row.active) return "working";
790
795
  if (row.runtimeStatus === "busy") return "working";
@@ -802,7 +807,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
802
807
  finished: 30,
803
808
  running: 20,
804
809
  recent: 10,
805
- observed: 0,
806
810
  idle: 0
807
811
  });
808
812
  function activityDetail(activity, preview, pill) {
@@ -813,13 +817,14 @@ function activityDetail(activity, preview, pill) {
813
817
  if (activity === "finished") return "Finished";
814
818
  if (activity === "unseen") return "New reply";
815
819
  if (activity === "running") return "Session is running";
816
- if (activity === "observed") return "Read-only channel";
817
820
  return pill || "Agent chats";
818
821
  }
819
822
  function projectAgentActivity(value) {
820
823
  const state = normalizeUiState(value);
821
824
  const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
825
+ const sessionsByKey = new Map(state.sessions.map((row) => [row.key, row]));
822
826
  const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
827
+ const unreadTone = state.attention.length > 0 && state.attention.every((item) => sessionsByKey.get(item.key)?.writable) ? "attention" : "neutral";
823
828
  const ranked = state.sessions.map((row, index) => {
824
829
  const activity2 = sessionActivity(state, row);
825
830
  return {
@@ -843,7 +848,8 @@ function projectAgentActivity(value) {
843
848
  title: selected ? sessionDisplayName(selected.row) : fallback ? sessionDisplayName(fallback) : "Agent chats",
844
849
  detail: activityDetail(activity, preview, state.pill.label),
845
850
  activity,
846
- unreadCount
851
+ unreadCount,
852
+ unreadTone
847
853
  };
848
854
  }
849
855
  function toolTarget(argumentsText) {
@@ -930,7 +936,7 @@ function AgentActivityLauncher({ state, onOpen, class: className = "", showSumma
930
936
  const Logo = components.HarnessLogo ?? HarnessLogo;
931
937
  const badge = value.unreadCount > 99 ? "99+" : String(value.unreadCount);
932
938
  const label = [value.title, value.detail, value.unreadCount ? `${value.unreadCount} unread` : ""].filter(Boolean).join(" \xB7 ");
933
- return /* @__PURE__ */ jsxs2("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
939
+ return /* @__PURE__ */ jsxs2("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, "data-unread-tone": value.unreadTone, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
934
940
  /* @__PURE__ */ jsxs2("span", { className: "scui-launcher-logo", children: [
935
941
  /* @__PURE__ */ jsx2(Logo, { id: value.harness || "supercode", activity: value.activity, size: 36 }),
936
942
  value.unreadCount ? /* @__PURE__ */ jsx2("b", { "aria-hidden": "true", children: badge }) : null
package/components.mjs CHANGED
@@ -35,9 +35,12 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  canSend: false,
36
36
  canSteer: false,
37
37
  canResume: false,
38
+ supportsResume: false,
38
39
  continuationModes: Object.freeze([]),
39
40
  canBranch: false,
41
+ supportsBranch: false,
40
42
  canAttach: false,
43
+ supportsAttach: false,
41
44
  canDetach: false,
42
45
  canOpenTerminal: false,
43
46
  canExport: false,
@@ -724,9 +727,12 @@ function normalizeUiState(value) {
724
727
  canSend: raw.canSend === true,
725
728
  canSteer: raw.canSteer === true,
726
729
  canResume,
730
+ supportsResume: raw.supportsResume === true,
727
731
  continuationModes,
728
732
  canBranch: raw.canBranch === true,
733
+ supportsBranch: raw.supportsBranch === true,
729
734
  canAttach: raw.canAttach === true,
735
+ supportsAttach: raw.supportsAttach === true,
730
736
  canDetach: raw.canDetach === true,
731
737
  canOpenTerminal: raw.canOpenTerminal === true,
732
738
  canExport: raw.canExport === true,
@@ -796,7 +802,6 @@ function sessionDisplayName(session) {
796
802
  return title && title !== session.name ? title : session.name || "Untitled chat";
797
803
  }
798
804
  function sessionActivity(state, row) {
799
- if (!row.writable) return "observed";
800
805
  if (state.needsInput && row.active) return "needs-input";
801
806
  if (state.busy && row.active) return "working";
802
807
  if (row.runtimeStatus === "busy") return "working";
@@ -814,7 +819,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
814
819
  finished: 30,
815
820
  running: 20,
816
821
  recent: 10,
817
- observed: 0,
818
822
  idle: 0
819
823
  });
820
824
  function activityDetail(activity, preview, pill) {
@@ -825,13 +829,14 @@ function activityDetail(activity, preview, pill) {
825
829
  if (activity === "finished") return "Finished";
826
830
  if (activity === "unseen") return "New reply";
827
831
  if (activity === "running") return "Session is running";
828
- if (activity === "observed") return "Read-only channel";
829
832
  return pill || "Agent chats";
830
833
  }
831
834
  function projectAgentActivity(value) {
832
835
  const state = normalizeUiState(value);
833
836
  const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
837
+ const sessionsByKey = new Map(state.sessions.map((row) => [row.key, row]));
834
838
  const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
839
+ const unreadTone = state.attention.length > 0 && state.attention.every((item) => sessionsByKey.get(item.key)?.writable) ? "attention" : "neutral";
835
840
  const ranked = state.sessions.map((row, index) => {
836
841
  const activity2 = sessionActivity(state, row);
837
842
  return {
@@ -855,7 +860,8 @@ function projectAgentActivity(value) {
855
860
  title: selected ? sessionDisplayName(selected.row) : fallback ? sessionDisplayName(fallback) : "Agent chats",
856
861
  detail: activityDetail(activity, preview, state.pill.label),
857
862
  activity,
858
- unreadCount
863
+ unreadCount,
864
+ unreadTone
859
865
  };
860
866
  }
861
867
  function filterSessions(rows, query) {
@@ -1314,21 +1320,25 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
1314
1320
  const attached = state.attached;
1315
1321
  const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
1316
1322
  const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
1317
- const resume = canContinueHere(state);
1318
- const join = state.canAttach;
1319
- const branch = state.canBranch;
1320
- if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
1323
+ const available = {
1324
+ resume: canContinueHere(state),
1325
+ join: state.canAttach,
1326
+ branch: state.canBranch
1327
+ };
1328
+ const continuation = available.join ? "join" : available.resume ? "resume" : available.branch ? "branch" : state.supportsAttach ? "join" : state.supportsResume ? "resume" : state.supportsBranch ? "branch" : null;
1329
+ if (!continuation) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
1321
1330
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
1322
1331
  /* @__PURE__ */ jsx3("small", { children: "This session cannot be continued by an available harness." })
1323
1332
  ] }) });
1333
+ const enabled = available[continuation];
1324
1334
  return /* @__PURE__ */ jsxs3("div", { className: "scui-continuation", children: [
1325
1335
  /* @__PURE__ */ jsxs3("span", { children: [
1326
1336
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
1327
- /* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
1337
+ /* @__PURE__ */ jsx3("small", { children: continuation === "join" ? "Join the proven live runtime without taking it over." : continuation === "resume" ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
1328
1338
  ] }),
1329
1339
  /* @__PURE__ */ jsxs3("span", { className: "scui-continuation-actions", children: [
1330
- !join && resume ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation), onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
1331
- /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => dispatchConfirmedIntent(adapter, join ? { action: "join" } : resume ? { action: "resume", mode: executionMode } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
1340
+ continuation === "resume" ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation) || !enabled, onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
1341
+ /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation) || !enabled, onClick: () => dispatchConfirmedIntent(adapter, continuation === "join" ? { action: "join" } : continuation === "resume" ? { action: "resume", mode: executionMode } : { action: "branch" }), children: continuation === "join" ? labels.joinLive : continuation === "resume" ? labels.continueHere : labels.forkHere })
1332
1342
  ] })
1333
1343
  ] });
1334
1344
  }
@@ -1643,7 +1653,7 @@ function AgentActivityLauncher({ state, onOpen, class: className = "", showSumma
1643
1653
  const Logo = components.HarnessLogo ?? HarnessLogo;
1644
1654
  const badge = value.unreadCount > 99 ? "99+" : String(value.unreadCount);
1645
1655
  const label = [value.title, value.detail, value.unreadCount ? `${value.unreadCount} unread` : ""].filter(Boolean).join(" \xB7 ");
1646
- return /* @__PURE__ */ jsxs5("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
1656
+ return /* @__PURE__ */ jsxs5("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, "data-unread-tone": value.unreadTone, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
1647
1657
  /* @__PURE__ */ jsxs5("span", { className: "scui-launcher-logo", children: [
1648
1658
  /* @__PURE__ */ jsx5(Logo, { id: value.harness || "supercode", activity: value.activity, size: 36 }),
1649
1659
  value.unreadCount ? /* @__PURE__ */ jsx5("b", { "aria-hidden": "true", children: badge }) : null
@@ -2716,8 +2726,8 @@ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNe
2716
2726
  if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
2717
2727
  }, []);
2718
2728
  return /* @__PURE__ */ jsxs9("header", { className: "scui-head scui-chat-head", children: [
2719
- /* @__PURE__ */ jsx10("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx10(UiIcon, { name: "back", size: 18 }) }),
2720
- /* @__PURE__ */ jsx10(HarnessLogo, { id: harness, size: 28 }),
2729
+ /* @__PURE__ */ jsx10("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx10(UiIcon, { name: "back", size: 17 }) }),
2730
+ /* @__PURE__ */ jsx10(HarnessLogo, { id: harness, size: 24 }),
2721
2731
  /* @__PURE__ */ jsxs9("span", { className: "scui-head-copy", children: [
2722
2732
  /* @__PURE__ */ jsx10("strong", { children: title }),
2723
2733
  /* @__PURE__ */ jsxs9("small", { children: [
package/composer.mjs CHANGED
@@ -35,9 +35,12 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  canSend: false,
36
36
  canSteer: false,
37
37
  canResume: false,
38
+ supportsResume: false,
38
39
  continuationModes: Object.freeze([]),
39
40
  canBranch: false,
41
+ supportsBranch: false,
40
42
  canAttach: false,
43
+ supportsAttach: false,
41
44
  canDetach: false,
42
45
  canOpenTerminal: false,
43
46
  canExport: false,
@@ -77,7 +80,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
77
80
  finished: 30,
78
81
  running: 20,
79
82
  recent: 10,
80
- observed: 0,
81
83
  idle: 0
82
84
  });
83
85
  function canContinueHere(state) {
@@ -312,21 +314,25 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
312
314
  const attached = state.attached;
313
315
  const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
314
316
  const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
315
- const resume = canContinueHere(state);
316
- const join = state.canAttach;
317
- const branch = state.canBranch;
318
- if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
317
+ const available = {
318
+ resume: canContinueHere(state),
319
+ join: state.canAttach,
320
+ branch: state.canBranch
321
+ };
322
+ const continuation = available.join ? "join" : available.resume ? "resume" : available.branch ? "branch" : state.supportsAttach ? "join" : state.supportsResume ? "resume" : state.supportsBranch ? "branch" : null;
323
+ if (!continuation) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
319
324
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
320
325
  /* @__PURE__ */ jsx3("small", { children: "This session cannot be continued by an available harness." })
321
326
  ] }) });
327
+ const enabled = available[continuation];
322
328
  return /* @__PURE__ */ jsxs3("div", { className: "scui-continuation", children: [
323
329
  /* @__PURE__ */ jsxs3("span", { children: [
324
330
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
325
- /* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
331
+ /* @__PURE__ */ jsx3("small", { children: continuation === "join" ? "Join the proven live runtime without taking it over." : continuation === "resume" ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
326
332
  ] }),
327
333
  /* @__PURE__ */ jsxs3("span", { className: "scui-continuation-actions", children: [
328
- !join && resume ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation), onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
329
- /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => dispatchConfirmedIntent(adapter, join ? { action: "join" } : resume ? { action: "resume", mode: executionMode } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
334
+ continuation === "resume" ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation) || !enabled, onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
335
+ /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation) || !enabled, onClick: () => dispatchConfirmedIntent(adapter, continuation === "join" ? { action: "join" } : continuation === "resume" ? { action: "resume", mode: executionMode } : { action: "branch" }), children: continuation === "join" ? labels.joinLive : continuation === "resume" ? labels.continueHere : labels.forkHere })
330
336
  ] })
331
337
  ] });
332
338
  }
package/controller.mjs CHANGED
@@ -436,6 +436,10 @@ function projectClientSnapshotInternal(snapshot, options, imageRegistry) {
436
436
  const now = Number.isFinite(options.now) ? options.now : Date.now();
437
437
  const busy = ['running', 'interrupting', 'reconciling'].includes(snapshot.turn?.state);
438
438
  const actions = snapshot.availableActions ?? {};
439
+ // Older clients did not expose operation-independent eligibility. Falling
440
+ // back preserves compatibility; current clients keep stable UI capability
441
+ // copy while an action temporarily makes every executable action false.
442
+ const eligibility = snapshot.actionEligibility ?? actions;
439
443
  const maxSessions = positiveInteger(options.maxSessions, DEFAULT_MAX_SESSIONS);
440
444
  const liveWindowMs = positiveInteger(options.liveWindowMs, DEFAULT_LIVE_WINDOW_MS);
441
445
  const snapshotSessions = (snapshot.sessions ?? []).slice(0, maxSessions).map((session) => ({
@@ -475,9 +479,12 @@ function projectClientSnapshotInternal(snapshot, options, imageRegistry) {
475
479
  canSend: actions.send === true,
476
480
  canSteer: actions.steer === true,
477
481
  canResume: actions.resume === true,
478
- continuationModes: options.continuationModes ?? (actions.resume === true ? ['headless'] : []),
482
+ supportsResume: eligibility.resume === true,
483
+ continuationModes: options.continuationModes ?? (eligibility.resume === true ? ['headless'] : []),
479
484
  canBranch: actions.branch === true,
485
+ supportsBranch: eligibility.branch === true,
480
486
  canAttach: actions.attach === true,
487
+ supportsAttach: eligibility.attach === true,
481
488
  canDetach: actions.detach === true,
482
489
  canOpenTerminal: actions.openTerminal === true,
483
490
  canExport: Boolean(options.exportBackTarget && snapshot.activeSessionKey),
@@ -517,13 +524,13 @@ function projectClientSnapshotInternal(snapshot, options, imageRegistry) {
517
524
  protocol: harness.protocol,
518
525
  repair: harness.repair ?? null,
519
526
  capabilities: {
520
- start: harness.availableActions?.start === true,
521
- resume: harness.availableActions?.resume === true,
522
- attach: harness.availableActions?.attach === true,
523
- send: harness.availableActions?.send === true,
524
- interrupt: harness.availableActions?.interrupt === true,
525
- steer: harness.availableActions?.steer === true,
526
- respond: harness.availableActions?.respond === true,
527
+ start: (harness.effective_capabilities ?? harness.capabilities)?.start_session === true,
528
+ resume: (harness.effective_capabilities ?? harness.capabilities)?.resume_session === true,
529
+ attach: (harness.effective_capabilities ?? harness.capabilities)?.attach_existing_process === true,
530
+ send: (harness.effective_capabilities ?? harness.capabilities)?.send_input === true,
531
+ interrupt: (harness.effective_capabilities ?? harness.capabilities)?.interrupt === true,
532
+ steer: (harness.effective_capabilities ?? harness.capabilities)?.steer === true,
533
+ respond: (harness.effective_capabilities ?? harness.capabilities)?.respond_to_requests === true,
527
534
  },
528
535
  })),
529
536
  history: {
package/conversation.mjs CHANGED
@@ -36,9 +36,12 @@ var EMPTY_UI_STATE = Object.freeze({
36
36
  canSend: false,
37
37
  canSteer: false,
38
38
  canResume: false,
39
+ supportsResume: false,
39
40
  continuationModes: Object.freeze([]),
40
41
  canBranch: false,
42
+ supportsBranch: false,
41
43
  canAttach: false,
44
+ supportsAttach: false,
42
45
  canDetach: false,
43
46
  canOpenTerminal: false,
44
47
  canExport: false,
@@ -439,7 +442,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
439
442
  finished: 30,
440
443
  running: 20,
441
444
  recent: 10,
442
- observed: 0,
443
445
  idle: 0
444
446
  });
445
447
  function groupConversation(entries) {
package/core.mjs CHANGED
@@ -33,9 +33,12 @@ export const EMPTY_UI_STATE = Object.freeze({
33
33
  canSend: false,
34
34
  canSteer: false,
35
35
  canResume: false,
36
+ supportsResume: false,
36
37
  continuationModes: Object.freeze([]),
37
38
  canBranch: false,
39
+ supportsBranch: false,
38
40
  canAttach: false,
41
+ supportsAttach: false,
39
42
  canDetach: false,
40
43
  canOpenTerminal: false,
41
44
  canExport: false,
@@ -776,9 +779,12 @@ export function normalizeUiState(value) {
776
779
  canSend: raw.canSend === true,
777
780
  canSteer: raw.canSteer === true,
778
781
  canResume,
782
+ supportsResume: raw.supportsResume === true,
779
783
  continuationModes,
780
784
  canBranch: raw.canBranch === true,
785
+ supportsBranch: raw.supportsBranch === true,
781
786
  canAttach: raw.canAttach === true,
787
+ supportsAttach: raw.supportsAttach === true,
782
788
  canDetach: raw.canDetach === true,
783
789
  canOpenTerminal: raw.canOpenTerminal === true,
784
790
  canExport: raw.canExport === true,
@@ -859,9 +865,6 @@ export function sessionDisplayName(session) {
859
865
  }
860
866
 
861
867
  export function sessionActivity(state, row) {
862
- // Runtime activity and interaction authority are deliberately independent. A process may be
863
- // busy in another terminal, but presenting it as this surface's active agent is misleading.
864
- if (!row.writable) return 'observed';
865
868
  if (state.needsInput && row.active) return 'needs-input';
866
869
  if (state.busy && row.active) return 'working';
867
870
  // Unread/finished attention has its own badge. It must not suppress the
@@ -882,7 +885,6 @@ const ACTIVITY_PRIORITY = Object.freeze({
882
885
  finished: 30,
883
886
  running: 20,
884
887
  recent: 10,
885
- observed: 0,
886
888
  idle: 0,
887
889
  });
888
890
 
@@ -894,7 +896,6 @@ function activityDetail(activity, preview, pill) {
894
896
  if (activity === 'finished') return 'Finished';
895
897
  if (activity === 'unseen') return 'New reply';
896
898
  if (activity === 'running') return 'Session is running';
897
- if (activity === 'observed') return 'Read-only channel';
898
899
  return pill || 'Agent chats';
899
900
  }
900
901
 
@@ -902,7 +903,12 @@ function activityDetail(activity, preview, pill) {
902
903
  export function projectAgentActivity(value) {
903
904
  const state = normalizeUiState(value);
904
905
  const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
906
+ const sessionsByKey = new Map(state.sessions.map((row) => [row.key, row]));
905
907
  const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
908
+ const unreadTone = state.attention.length > 0
909
+ && state.attention.every((item) => sessionsByKey.get(item.key)?.writable)
910
+ ? 'attention'
911
+ : 'neutral';
906
912
  const ranked = state.sessions.map((row, index) => {
907
913
  const activity = sessionActivity(state, row);
908
914
  return {
@@ -927,6 +933,7 @@ export function projectAgentActivity(value) {
927
933
  detail: activityDetail(activity, preview, state.pill.label),
928
934
  activity,
929
935
  unreadCount,
936
+ unreadTone,
930
937
  };
931
938
  }
932
939
 
package/embed.mjs CHANGED
@@ -38,9 +38,12 @@ var EMPTY_UI_STATE = Object.freeze({
38
38
  canSend: false,
39
39
  canSteer: false,
40
40
  canResume: false,
41
+ supportsResume: false,
41
42
  continuationModes: Object.freeze([]),
42
43
  canBranch: false,
44
+ supportsBranch: false,
43
45
  canAttach: false,
46
+ supportsAttach: false,
44
47
  canDetach: false,
45
48
  canOpenTerminal: false,
46
49
  canExport: false,
@@ -727,9 +730,12 @@ function normalizeUiState(value) {
727
730
  canSend: raw.canSend === true,
728
731
  canSteer: raw.canSteer === true,
729
732
  canResume,
733
+ supportsResume: raw.supportsResume === true,
730
734
  continuationModes,
731
735
  canBranch: raw.canBranch === true,
736
+ supportsBranch: raw.supportsBranch === true,
732
737
  canAttach: raw.canAttach === true,
738
+ supportsAttach: raw.supportsAttach === true,
733
739
  canDetach: raw.canDetach === true,
734
740
  canOpenTerminal: raw.canOpenTerminal === true,
735
741
  canExport: raw.canExport === true,
@@ -799,7 +805,6 @@ function sessionDisplayName(session) {
799
805
  return title && title !== session.name ? title : session.name || "Untitled chat";
800
806
  }
801
807
  function sessionActivity(state, row) {
802
- if (!row.writable) return "observed";
803
808
  if (state.needsInput && row.active) return "needs-input";
804
809
  if (state.busy && row.active) return "working";
805
810
  if (row.runtimeStatus === "busy") return "working";
@@ -817,7 +822,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
817
822
  finished: 30,
818
823
  running: 20,
819
824
  recent: 10,
820
- observed: 0,
821
825
  idle: 0
822
826
  });
823
827
  function filterSessions(rows, query) {
@@ -1279,21 +1283,25 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
1279
1283
  const attached = state.attached;
1280
1284
  const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
1281
1285
  const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
1282
- const resume = canContinueHere(state);
1283
- const join = state.canAttach;
1284
- const branch = state.canBranch;
1285
- if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
1286
+ const available = {
1287
+ resume: canContinueHere(state),
1288
+ join: state.canAttach,
1289
+ branch: state.canBranch
1290
+ };
1291
+ const continuation = available.join ? "join" : available.resume ? "resume" : available.branch ? "branch" : state.supportsAttach ? "join" : state.supportsResume ? "resume" : state.supportsBranch ? "branch" : null;
1292
+ if (!continuation) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
1286
1293
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
1287
1294
  /* @__PURE__ */ jsx3("small", { children: "This session cannot be continued by an available harness." })
1288
1295
  ] }) });
1296
+ const enabled = available[continuation];
1289
1297
  return /* @__PURE__ */ jsxs3("div", { className: "scui-continuation", children: [
1290
1298
  /* @__PURE__ */ jsxs3("span", { children: [
1291
1299
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
1292
- /* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
1300
+ /* @__PURE__ */ jsx3("small", { children: continuation === "join" ? "Join the proven live runtime without taking it over." : continuation === "resume" ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
1293
1301
  ] }),
1294
1302
  /* @__PURE__ */ jsxs3("span", { className: "scui-continuation-actions", children: [
1295
- !join && resume ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation), onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
1296
- /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => dispatchConfirmedIntent(adapter, join ? { action: "join" } : resume ? { action: "resume", mode: executionMode } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
1303
+ continuation === "resume" ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation) || !enabled, onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
1304
+ /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation) || !enabled, onClick: () => dispatchConfirmedIntent(adapter, continuation === "join" ? { action: "join" } : continuation === "resume" ? { action: "resume", mode: executionMode } : { action: "branch" }), children: continuation === "join" ? labels.joinLive : continuation === "resume" ? labels.continueHere : labels.forkHere })
1297
1305
  ] })
1298
1306
  ] });
1299
1307
  }
@@ -2635,8 +2643,8 @@ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNe
2635
2643
  if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
2636
2644
  }, []);
2637
2645
  return /* @__PURE__ */ jsxs8("header", { className: "scui-head scui-chat-head", children: [
2638
- /* @__PURE__ */ jsx9("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
2639
- /* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 28 }),
2646
+ /* @__PURE__ */ jsx9("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 17 }) }),
2647
+ /* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 24 }),
2640
2648
  /* @__PURE__ */ jsxs8("span", { className: "scui-head-copy", children: [
2641
2649
  /* @__PURE__ */ jsx9("strong", { children: title }),
2642
2650
  /* @__PURE__ */ jsxs8("small", { children: [
package/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export type ControlStrategy = 'start' | 'resume' | 'attach' | 'branch' | 'reduce
8
8
  export type ExecutionMode = 'headless' | 'terminal';
9
9
  /** @deprecated Use ExecutionMode. */
10
10
  export type ContinuationMode = ExecutionMode;
11
- export type SessionActivity = 'idle' | 'observed' | 'recent' | 'running' | 'working' | 'needs-input' | 'finished' | 'failed' | 'unseen';
11
+ export type SessionActivity = 'idle' | 'recent' | 'running' | 'working' | 'needs-input' | 'finished' | 'failed' | 'unseen';
12
12
 
13
13
  export interface AgentActivityModel {
14
14
  harness: HarnessId | '';
@@ -17,6 +17,7 @@ export interface AgentActivityModel {
17
17
  detail: string;
18
18
  activity: SessionActivity;
19
19
  unreadCount: number;
20
+ unreadTone: 'attention' | 'neutral';
20
21
  }
21
22
 
22
23
  export interface HarnessOption {
@@ -266,10 +267,16 @@ export interface SupercodeUiState {
266
267
  canSend: boolean;
267
268
  canSteer: boolean;
268
269
  canResume: boolean;
270
+ /** Stable continuation eligibility while another controller action is in flight. */
271
+ supportsResume: boolean;
269
272
  /** Execution transports the current host can actually provide. */
270
273
  continuationModes: ContinuationMode[];
271
274
  canBranch: boolean;
275
+ /** Stable continuation eligibility while another controller action is in flight. */
276
+ supportsBranch: boolean;
272
277
  canAttach: boolean;
278
+ /** Stable continuation eligibility while another controller action is in flight. */
279
+ supportsAttach: boolean;
273
280
  canDetach: boolean;
274
281
  canOpenTerminal: boolean;
275
282
  canExport: boolean;
package/messenger.mjs CHANGED
@@ -35,9 +35,12 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  canSend: false,
36
36
  canSteer: false,
37
37
  canResume: false,
38
+ supportsResume: false,
38
39
  continuationModes: Object.freeze([]),
39
40
  canBranch: false,
41
+ supportsBranch: false,
40
42
  canAttach: false,
43
+ supportsAttach: false,
41
44
  canDetach: false,
42
45
  canOpenTerminal: false,
43
46
  canExport: false,
@@ -724,9 +727,12 @@ function normalizeUiState(value) {
724
727
  canSend: raw.canSend === true,
725
728
  canSteer: raw.canSteer === true,
726
729
  canResume,
730
+ supportsResume: raw.supportsResume === true,
727
731
  continuationModes,
728
732
  canBranch: raw.canBranch === true,
733
+ supportsBranch: raw.supportsBranch === true,
729
734
  canAttach: raw.canAttach === true,
735
+ supportsAttach: raw.supportsAttach === true,
730
736
  canDetach: raw.canDetach === true,
731
737
  canOpenTerminal: raw.canOpenTerminal === true,
732
738
  canExport: raw.canExport === true,
@@ -796,7 +802,6 @@ function sessionDisplayName(session) {
796
802
  return title && title !== session.name ? title : session.name || "Untitled chat";
797
803
  }
798
804
  function sessionActivity(state, row) {
799
- if (!row.writable) return "observed";
800
805
  if (state.needsInput && row.active) return "needs-input";
801
806
  if (state.busy && row.active) return "working";
802
807
  if (row.runtimeStatus === "busy") return "working";
@@ -814,7 +819,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
814
819
  finished: 30,
815
820
  running: 20,
816
821
  recent: 10,
817
- observed: 0,
818
822
  idle: 0
819
823
  });
820
824
  function filterSessions(rows, query) {
@@ -1276,21 +1280,25 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
1276
1280
  const attached = state.attached;
1277
1281
  const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
1278
1282
  const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
1279
- const resume = canContinueHere(state);
1280
- const join = state.canAttach;
1281
- const branch = state.canBranch;
1282
- if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
1283
+ const available = {
1284
+ resume: canContinueHere(state),
1285
+ join: state.canAttach,
1286
+ branch: state.canBranch
1287
+ };
1288
+ const continuation = available.join ? "join" : available.resume ? "resume" : available.branch ? "branch" : state.supportsAttach ? "join" : state.supportsResume ? "resume" : state.supportsBranch ? "branch" : null;
1289
+ if (!continuation) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
1283
1290
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
1284
1291
  /* @__PURE__ */ jsx3("small", { children: "This session cannot be continued by an available harness." })
1285
1292
  ] }) });
1293
+ const enabled = available[continuation];
1286
1294
  return /* @__PURE__ */ jsxs3("div", { className: "scui-continuation", children: [
1287
1295
  /* @__PURE__ */ jsxs3("span", { children: [
1288
1296
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
1289
- /* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
1297
+ /* @__PURE__ */ jsx3("small", { children: continuation === "join" ? "Join the proven live runtime without taking it over." : continuation === "resume" ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
1290
1298
  ] }),
1291
1299
  /* @__PURE__ */ jsxs3("span", { className: "scui-continuation-actions", children: [
1292
- !join && resume ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation), onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
1293
- /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => dispatchConfirmedIntent(adapter, join ? { action: "join" } : resume ? { action: "resume", mode: executionMode } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
1300
+ continuation === "resume" ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation) || !enabled, onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
1301
+ /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation) || !enabled, onClick: () => dispatchConfirmedIntent(adapter, continuation === "join" ? { action: "join" } : continuation === "resume" ? { action: "resume", mode: executionMode } : { action: "branch" }), children: continuation === "join" ? labels.joinLive : continuation === "resume" ? labels.continueHere : labels.forkHere })
1294
1302
  ] })
1295
1303
  ] });
1296
1304
  }
@@ -2632,8 +2640,8 @@ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNe
2632
2640
  if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
2633
2641
  }, []);
2634
2642
  return /* @__PURE__ */ jsxs8("header", { className: "scui-head scui-chat-head", children: [
2635
- /* @__PURE__ */ jsx9("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
2636
- /* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 28 }),
2643
+ /* @__PURE__ */ jsx9("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 17 }) }),
2644
+ /* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 24 }),
2637
2645
  /* @__PURE__ */ jsxs8("span", { className: "scui-head-copy", children: [
2638
2646
  /* @__PURE__ */ jsx9("strong", { children: title }),
2639
2647
  /* @__PURE__ */ jsxs8("small", { children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter-ai-dev/supercode-ui",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
4
4
  "type": "module",
5
5
  "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
6
  "exports": {
@@ -32,9 +32,12 @@ var EMPTY_UI_STATE = Object.freeze({
32
32
  canSend: false,
33
33
  canSteer: false,
34
34
  canResume: false,
35
+ supportsResume: false,
35
36
  continuationModes: Object.freeze([]),
36
37
  canBranch: false,
38
+ supportsBranch: false,
37
39
  canAttach: false,
40
+ supportsAttach: false,
38
41
  canDetach: false,
39
42
  canOpenTerminal: false,
40
43
  canExport: false,
@@ -712,9 +715,12 @@ function normalizeUiState(value) {
712
715
  canSend: raw.canSend === true,
713
716
  canSteer: raw.canSteer === true,
714
717
  canResume,
718
+ supportsResume: raw.supportsResume === true,
715
719
  continuationModes,
716
720
  canBranch: raw.canBranch === true,
721
+ supportsBranch: raw.supportsBranch === true,
717
722
  canAttach: raw.canAttach === true,
723
+ supportsAttach: raw.supportsAttach === true,
718
724
  canDetach: raw.canDetach === true,
719
725
  canOpenTerminal: raw.canOpenTerminal === true,
720
726
  canExport: raw.canExport === true,
@@ -784,7 +790,6 @@ function sessionDisplayName(session) {
784
790
  return title && title !== session.name ? title : session.name || "Untitled chat";
785
791
  }
786
792
  function sessionActivity(state, row) {
787
- if (!row.writable) return "observed";
788
793
  if (state.needsInput && row.active) return "needs-input";
789
794
  if (state.busy && row.active) return "working";
790
795
  if (row.runtimeStatus === "busy") return "working";
@@ -802,7 +807,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
802
807
  finished: 30,
803
808
  running: 20,
804
809
  recent: 10,
805
- observed: 0,
806
810
  idle: 0
807
811
  });
808
812
  function activityDetail(activity, preview, pill) {
@@ -813,13 +817,14 @@ function activityDetail(activity, preview, pill) {
813
817
  if (activity === "finished") return "Finished";
814
818
  if (activity === "unseen") return "New reply";
815
819
  if (activity === "running") return "Session is running";
816
- if (activity === "observed") return "Read-only channel";
817
820
  return pill || "Agent chats";
818
821
  }
819
822
  function projectAgentActivity(value) {
820
823
  const state = normalizeUiState(value);
821
824
  const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
825
+ const sessionsByKey = new Map(state.sessions.map((row) => [row.key, row]));
822
826
  const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
827
+ const unreadTone = state.attention.length > 0 && state.attention.every((item) => sessionsByKey.get(item.key)?.writable) ? "attention" : "neutral";
823
828
  const ranked = state.sessions.map((row, index) => {
824
829
  const activity2 = sessionActivity(state, row);
825
830
  return {
@@ -843,7 +848,8 @@ function projectAgentActivity(value) {
843
848
  title: selected ? sessionDisplayName(selected.row) : fallback ? sessionDisplayName(fallback) : "Agent chats",
844
849
  detail: activityDetail(activity, preview, state.pill.label),
845
850
  activity,
846
- unreadCount
851
+ unreadCount,
852
+ unreadTone
847
853
  };
848
854
  }
849
855
  function toolTarget(argumentsText) {
@@ -930,7 +936,7 @@ function AgentActivityLauncher({ state, onOpen, class: className = "", showSumma
930
936
  const Logo = components.HarnessLogo ?? HarnessLogo;
931
937
  const badge = value.unreadCount > 99 ? "99+" : String(value.unreadCount);
932
938
  const label = [value.title, value.detail, value.unreadCount ? `${value.unreadCount} unread` : ""].filter(Boolean).join(" \xB7 ");
933
- return /* @__PURE__ */ jsxs2("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
939
+ return /* @__PURE__ */ jsxs2("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, "data-unread-tone": value.unreadTone, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
934
940
  /* @__PURE__ */ jsxs2("span", { className: "scui-launcher-logo", children: [
935
941
  /* @__PURE__ */ jsx2(Logo, { id: value.harness || "supercode", activity: value.activity, size: 36 }),
936
942
  value.unreadCount ? /* @__PURE__ */ jsx2("b", { "aria-hidden": "true", children: badge }) : null
@@ -35,9 +35,12 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  canSend: false,
36
36
  canSteer: false,
37
37
  canResume: false,
38
+ supportsResume: false,
38
39
  continuationModes: Object.freeze([]),
39
40
  canBranch: false,
41
+ supportsBranch: false,
40
42
  canAttach: false,
43
+ supportsAttach: false,
41
44
  canDetach: false,
42
45
  canOpenTerminal: false,
43
46
  canExport: false,
@@ -724,9 +727,12 @@ function normalizeUiState(value) {
724
727
  canSend: raw.canSend === true,
725
728
  canSteer: raw.canSteer === true,
726
729
  canResume,
730
+ supportsResume: raw.supportsResume === true,
727
731
  continuationModes,
728
732
  canBranch: raw.canBranch === true,
733
+ supportsBranch: raw.supportsBranch === true,
729
734
  canAttach: raw.canAttach === true,
735
+ supportsAttach: raw.supportsAttach === true,
730
736
  canDetach: raw.canDetach === true,
731
737
  canOpenTerminal: raw.canOpenTerminal === true,
732
738
  canExport: raw.canExport === true,
@@ -796,7 +802,6 @@ function sessionDisplayName(session) {
796
802
  return title && title !== session.name ? title : session.name || "Untitled chat";
797
803
  }
798
804
  function sessionActivity(state, row) {
799
- if (!row.writable) return "observed";
800
805
  if (state.needsInput && row.active) return "needs-input";
801
806
  if (state.busy && row.active) return "working";
802
807
  if (row.runtimeStatus === "busy") return "working";
@@ -814,7 +819,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
814
819
  finished: 30,
815
820
  running: 20,
816
821
  recent: 10,
817
- observed: 0,
818
822
  idle: 0
819
823
  });
820
824
  function activityDetail(activity, preview, pill) {
@@ -825,13 +829,14 @@ function activityDetail(activity, preview, pill) {
825
829
  if (activity === "finished") return "Finished";
826
830
  if (activity === "unseen") return "New reply";
827
831
  if (activity === "running") return "Session is running";
828
- if (activity === "observed") return "Read-only channel";
829
832
  return pill || "Agent chats";
830
833
  }
831
834
  function projectAgentActivity(value) {
832
835
  const state = normalizeUiState(value);
833
836
  const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
837
+ const sessionsByKey = new Map(state.sessions.map((row) => [row.key, row]));
834
838
  const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
839
+ const unreadTone = state.attention.length > 0 && state.attention.every((item) => sessionsByKey.get(item.key)?.writable) ? "attention" : "neutral";
835
840
  const ranked = state.sessions.map((row, index) => {
836
841
  const activity2 = sessionActivity(state, row);
837
842
  return {
@@ -855,7 +860,8 @@ function projectAgentActivity(value) {
855
860
  title: selected ? sessionDisplayName(selected.row) : fallback ? sessionDisplayName(fallback) : "Agent chats",
856
861
  detail: activityDetail(activity, preview, state.pill.label),
857
862
  activity,
858
- unreadCount
863
+ unreadCount,
864
+ unreadTone
859
865
  };
860
866
  }
861
867
  function filterSessions(rows, query) {
@@ -1314,21 +1320,25 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
1314
1320
  const attached = state.attached;
1315
1321
  const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
1316
1322
  const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
1317
- const resume = canContinueHere(state);
1318
- const join = state.canAttach;
1319
- const branch = state.canBranch;
1320
- if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
1323
+ const available = {
1324
+ resume: canContinueHere(state),
1325
+ join: state.canAttach,
1326
+ branch: state.canBranch
1327
+ };
1328
+ const continuation = available.join ? "join" : available.resume ? "resume" : available.branch ? "branch" : state.supportsAttach ? "join" : state.supportsResume ? "resume" : state.supportsBranch ? "branch" : null;
1329
+ if (!continuation) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
1321
1330
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
1322
1331
  /* @__PURE__ */ jsx3("small", { children: "This session cannot be continued by an available harness." })
1323
1332
  ] }) });
1333
+ const enabled = available[continuation];
1324
1334
  return /* @__PURE__ */ jsxs3("div", { className: "scui-continuation", children: [
1325
1335
  /* @__PURE__ */ jsxs3("span", { children: [
1326
1336
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
1327
- /* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
1337
+ /* @__PURE__ */ jsx3("small", { children: continuation === "join" ? "Join the proven live runtime without taking it over." : continuation === "resume" ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
1328
1338
  ] }),
1329
1339
  /* @__PURE__ */ jsxs3("span", { className: "scui-continuation-actions", children: [
1330
- !join && resume ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation), onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
1331
- /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => dispatchConfirmedIntent(adapter, join ? { action: "join" } : resume ? { action: "resume", mode: executionMode } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
1340
+ continuation === "resume" ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation) || !enabled, onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
1341
+ /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation) || !enabled, onClick: () => dispatchConfirmedIntent(adapter, continuation === "join" ? { action: "join" } : continuation === "resume" ? { action: "resume", mode: executionMode } : { action: "branch" }), children: continuation === "join" ? labels.joinLive : continuation === "resume" ? labels.continueHere : labels.forkHere })
1332
1342
  ] })
1333
1343
  ] });
1334
1344
  }
@@ -1643,7 +1653,7 @@ function AgentActivityLauncher({ state, onOpen, class: className = "", showSumma
1643
1653
  const Logo = components.HarnessLogo ?? HarnessLogo;
1644
1654
  const badge = value.unreadCount > 99 ? "99+" : String(value.unreadCount);
1645
1655
  const label = [value.title, value.detail, value.unreadCount ? `${value.unreadCount} unread` : ""].filter(Boolean).join(" \xB7 ");
1646
- return /* @__PURE__ */ jsxs5("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
1656
+ return /* @__PURE__ */ jsxs5("button", { className: `scui-launcher ${className}`, "data-activity": value.activity, "data-unread-tone": value.unreadTone, type: "button", "aria-label": `Open ${label}`, onClick: () => onOpen?.(value), children: [
1647
1657
  /* @__PURE__ */ jsxs5("span", { className: "scui-launcher-logo", children: [
1648
1658
  /* @__PURE__ */ jsx5(Logo, { id: value.harness || "supercode", activity: value.activity, size: 36 }),
1649
1659
  value.unreadCount ? /* @__PURE__ */ jsx5("b", { "aria-hidden": "true", children: badge }) : null
@@ -2716,8 +2726,8 @@ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNe
2716
2726
  if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
2717
2727
  }, []);
2718
2728
  return /* @__PURE__ */ jsxs9("header", { className: "scui-head scui-chat-head", children: [
2719
- /* @__PURE__ */ jsx10("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx10(UiIcon, { name: "back", size: 18 }) }),
2720
- /* @__PURE__ */ jsx10(HarnessLogo, { id: harness, size: 28 }),
2729
+ /* @__PURE__ */ jsx10("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx10(UiIcon, { name: "back", size: 17 }) }),
2730
+ /* @__PURE__ */ jsx10(HarnessLogo, { id: harness, size: 24 }),
2721
2731
  /* @__PURE__ */ jsxs9("span", { className: "scui-head-copy", children: [
2722
2732
  /* @__PURE__ */ jsx10("strong", { children: title }),
2723
2733
  /* @__PURE__ */ jsxs9("small", { children: [
@@ -35,9 +35,12 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  canSend: false,
36
36
  canSteer: false,
37
37
  canResume: false,
38
+ supportsResume: false,
38
39
  continuationModes: Object.freeze([]),
39
40
  canBranch: false,
41
+ supportsBranch: false,
40
42
  canAttach: false,
43
+ supportsAttach: false,
41
44
  canDetach: false,
42
45
  canOpenTerminal: false,
43
46
  canExport: false,
@@ -77,7 +80,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
77
80
  finished: 30,
78
81
  running: 20,
79
82
  recent: 10,
80
- observed: 0,
81
83
  idle: 0
82
84
  });
83
85
  function canContinueHere(state) {
@@ -312,21 +314,25 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
312
314
  const attached = state.attached;
313
315
  const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
314
316
  const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
315
- const resume = canContinueHere(state);
316
- const join = state.canAttach;
317
- const branch = state.canBranch;
318
- if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
317
+ const available = {
318
+ resume: canContinueHere(state),
319
+ join: state.canAttach,
320
+ branch: state.canBranch
321
+ };
322
+ const continuation = available.join ? "join" : available.resume ? "resume" : available.branch ? "branch" : state.supportsAttach ? "join" : state.supportsResume ? "resume" : state.supportsBranch ? "branch" : null;
323
+ if (!continuation) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
319
324
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
320
325
  /* @__PURE__ */ jsx3("small", { children: "This session cannot be continued by an available harness." })
321
326
  ] }) });
327
+ const enabled = available[continuation];
322
328
  return /* @__PURE__ */ jsxs3("div", { className: "scui-continuation", children: [
323
329
  /* @__PURE__ */ jsxs3("span", { children: [
324
330
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
325
- /* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
331
+ /* @__PURE__ */ jsx3("small", { children: continuation === "join" ? "Join the proven live runtime without taking it over." : continuation === "resume" ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
326
332
  ] }),
327
333
  /* @__PURE__ */ jsxs3("span", { className: "scui-continuation-actions", children: [
328
- !join && resume ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation), onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
329
- /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => dispatchConfirmedIntent(adapter, join ? { action: "join" } : resume ? { action: "resume", mode: executionMode } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
334
+ continuation === "resume" ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation) || !enabled, onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
335
+ /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation) || !enabled, onClick: () => dispatchConfirmedIntent(adapter, continuation === "join" ? { action: "join" } : continuation === "resume" ? { action: "resume", mode: executionMode } : { action: "branch" }), children: continuation === "join" ? labels.joinLive : continuation === "resume" ? labels.continueHere : labels.forkHere })
330
336
  ] })
331
337
  ] });
332
338
  }
@@ -36,9 +36,12 @@ var EMPTY_UI_STATE = Object.freeze({
36
36
  canSend: false,
37
37
  canSteer: false,
38
38
  canResume: false,
39
+ supportsResume: false,
39
40
  continuationModes: Object.freeze([]),
40
41
  canBranch: false,
42
+ supportsBranch: false,
41
43
  canAttach: false,
44
+ supportsAttach: false,
42
45
  canDetach: false,
43
46
  canOpenTerminal: false,
44
47
  canExport: false,
@@ -439,7 +442,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
439
442
  finished: 30,
440
443
  running: 20,
441
444
  recent: 10,
442
- observed: 0,
443
445
  idle: 0
444
446
  });
445
447
  function groupConversation(entries) {
@@ -35,9 +35,12 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  canSend: false,
36
36
  canSteer: false,
37
37
  canResume: false,
38
+ supportsResume: false,
38
39
  continuationModes: Object.freeze([]),
39
40
  canBranch: false,
41
+ supportsBranch: false,
40
42
  canAttach: false,
43
+ supportsAttach: false,
41
44
  canDetach: false,
42
45
  canOpenTerminal: false,
43
46
  canExport: false,
@@ -724,9 +727,12 @@ function normalizeUiState(value) {
724
727
  canSend: raw.canSend === true,
725
728
  canSteer: raw.canSteer === true,
726
729
  canResume,
730
+ supportsResume: raw.supportsResume === true,
727
731
  continuationModes,
728
732
  canBranch: raw.canBranch === true,
733
+ supportsBranch: raw.supportsBranch === true,
729
734
  canAttach: raw.canAttach === true,
735
+ supportsAttach: raw.supportsAttach === true,
730
736
  canDetach: raw.canDetach === true,
731
737
  canOpenTerminal: raw.canOpenTerminal === true,
732
738
  canExport: raw.canExport === true,
@@ -796,7 +802,6 @@ function sessionDisplayName(session) {
796
802
  return title && title !== session.name ? title : session.name || "Untitled chat";
797
803
  }
798
804
  function sessionActivity(state, row) {
799
- if (!row.writable) return "observed";
800
805
  if (state.needsInput && row.active) return "needs-input";
801
806
  if (state.busy && row.active) return "working";
802
807
  if (row.runtimeStatus === "busy") return "working";
@@ -814,7 +819,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
814
819
  finished: 30,
815
820
  running: 20,
816
821
  recent: 10,
817
- observed: 0,
818
822
  idle: 0
819
823
  });
820
824
  function filterSessions(rows, query) {
@@ -1276,21 +1280,25 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
1276
1280
  const attached = state.attached;
1277
1281
  const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
1278
1282
  const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
1279
- const resume = canContinueHere(state);
1280
- const join = state.canAttach;
1281
- const branch = state.canBranch;
1282
- if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
1283
+ const available = {
1284
+ resume: canContinueHere(state),
1285
+ join: state.canAttach,
1286
+ branch: state.canBranch
1287
+ };
1288
+ const continuation = available.join ? "join" : available.resume ? "resume" : available.branch ? "branch" : state.supportsAttach ? "join" : state.supportsResume ? "resume" : state.supportsBranch ? "branch" : null;
1289
+ if (!continuation) return /* @__PURE__ */ jsx3("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
1283
1290
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
1284
1291
  /* @__PURE__ */ jsx3("small", { children: "This session cannot be continued by an available harness." })
1285
1292
  ] }) });
1293
+ const enabled = available[continuation];
1286
1294
  return /* @__PURE__ */ jsxs3("div", { className: "scui-continuation", children: [
1287
1295
  /* @__PURE__ */ jsxs3("span", { children: [
1288
1296
  /* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
1289
- /* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
1297
+ /* @__PURE__ */ jsx3("small", { children: continuation === "join" ? "Join the proven live runtime without taking it over." : continuation === "resume" ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
1290
1298
  ] }),
1291
1299
  /* @__PURE__ */ jsxs3("span", { className: "scui-continuation-actions", children: [
1292
- !join && resume ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation), onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
1293
- /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => dispatchConfirmedIntent(adapter, join ? { action: "join" } : resume ? { action: "resume", mode: executionMode } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere })
1300
+ continuation === "resume" ? /* @__PURE__ */ jsx3(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation) || !enabled, onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
1301
+ /* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation) || !enabled, onClick: () => dispatchConfirmedIntent(adapter, continuation === "join" ? { action: "join" } : continuation === "resume" ? { action: "resume", mode: executionMode } : { action: "branch" }), children: continuation === "join" ? labels.joinLive : continuation === "resume" ? labels.continueHere : labels.forkHere })
1294
1302
  ] })
1295
1303
  ] });
1296
1304
  }
@@ -2632,8 +2640,8 @@ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNe
2632
2640
  if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
2633
2641
  }, []);
2634
2642
  return /* @__PURE__ */ jsxs8("header", { className: "scui-head scui-chat-head", children: [
2635
- /* @__PURE__ */ jsx9("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 18 }) }),
2636
- /* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 28 }),
2643
+ /* @__PURE__ */ jsx9("button", { ref: back, type: "button", "aria-label": "Back to chats", onClick: onBack, children: /* @__PURE__ */ jsx9(UiIcon, { name: "back", size: 17 }) }),
2644
+ /* @__PURE__ */ jsx9(HarnessLogo, { id: harness, size: 24 }),
2637
2645
  /* @__PURE__ */ jsxs8("span", { className: "scui-head-copy", children: [
2638
2646
  /* @__PURE__ */ jsx9("strong", { children: title }),
2639
2647
  /* @__PURE__ */ jsxs8("small", { children: [
@@ -35,9 +35,12 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  canSend: false,
36
36
  canSteer: false,
37
37
  canResume: false,
38
+ supportsResume: false,
38
39
  continuationModes: Object.freeze([]),
39
40
  canBranch: false,
41
+ supportsBranch: false,
40
42
  canAttach: false,
43
+ supportsAttach: false,
41
44
  canDetach: false,
42
45
  canOpenTerminal: false,
43
46
  canExport: false,
@@ -83,7 +86,6 @@ function sessionDisplayName(session) {
83
86
  return title && title !== session.name ? title : session.name || "Untitled chat";
84
87
  }
85
88
  function sessionActivity(state, row) {
86
- if (!row.writable) return "observed";
87
89
  if (state.needsInput && row.active) return "needs-input";
88
90
  if (state.busy && row.active) return "working";
89
91
  if (row.runtimeStatus === "busy") return "working";
@@ -101,7 +103,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
101
103
  finished: 30,
102
104
  running: 20,
103
105
  recent: 10,
104
- observed: 0,
105
106
  idle: 0
106
107
  });
107
108
  function filterSessions(rows, query) {
@@ -35,9 +35,12 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  canSend: false,
36
36
  canSteer: false,
37
37
  canResume: false,
38
+ supportsResume: false,
38
39
  continuationModes: Object.freeze([]),
39
40
  canBranch: false,
41
+ supportsBranch: false,
40
42
  canAttach: false,
43
+ supportsAttach: false,
41
44
  canDetach: false,
42
45
  canOpenTerminal: false,
43
46
  canExport: false,
@@ -77,7 +80,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
77
80
  finished: 30,
78
81
  running: 20,
79
82
  recent: 10,
80
- observed: 0,
81
83
  idle: 0
82
84
  });
83
85
 
package/sessions.mjs CHANGED
@@ -35,9 +35,12 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  canSend: false,
36
36
  canSteer: false,
37
37
  canResume: false,
38
+ supportsResume: false,
38
39
  continuationModes: Object.freeze([]),
39
40
  canBranch: false,
41
+ supportsBranch: false,
40
42
  canAttach: false,
43
+ supportsAttach: false,
41
44
  canDetach: false,
42
45
  canOpenTerminal: false,
43
46
  canExport: false,
@@ -83,7 +86,6 @@ function sessionDisplayName(session) {
83
86
  return title && title !== session.name ? title : session.name || "Untitled chat";
84
87
  }
85
88
  function sessionActivity(state, row) {
86
- if (!row.writable) return "observed";
87
89
  if (state.needsInput && row.active) return "needs-input";
88
90
  if (state.busy && row.active) return "working";
89
91
  if (row.runtimeStatus === "busy") return "working";
@@ -101,7 +103,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
101
103
  finished: 30,
102
104
  running: 20,
103
105
  recent: 10,
104
- observed: 0,
105
106
  idle: 0
106
107
  });
107
108
  function filterSessions(rows, query) {
package/settings.mjs CHANGED
@@ -35,9 +35,12 @@ var EMPTY_UI_STATE = Object.freeze({
35
35
  canSend: false,
36
36
  canSteer: false,
37
37
  canResume: false,
38
+ supportsResume: false,
38
39
  continuationModes: Object.freeze([]),
39
40
  canBranch: false,
41
+ supportsBranch: false,
40
42
  canAttach: false,
43
+ supportsAttach: false,
41
44
  canDetach: false,
42
45
  canOpenTerminal: false,
43
46
  canExport: false,
@@ -77,7 +80,6 @@ var ACTIVITY_PRIORITY = Object.freeze({
77
80
  finished: 30,
78
81
  running: 20,
79
82
  recent: 10,
80
- observed: 0,
81
83
  idle: 0
82
84
  });
83
85
 
package/styles.css CHANGED
@@ -13,6 +13,7 @@
13
13
  --scui-warning: #b26a00;
14
14
  --scui-danger: #b4232e;
15
15
  --scui-radius: 10px;
16
+ --scui-head-height: 42px;
16
17
  --scui-font: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
17
18
  box-sizing: border-box;
18
19
  display: flex;
@@ -53,11 +54,11 @@
53
54
  .scui-sr-only { position:absolute; width:1px; height:1px; padding:0; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0 }
54
55
 
55
56
  .scui-list,.scui-chat { display:flex; flex:1; min-width:0; min-height:0; flex-direction:column }
56
- .scui-head { display:flex; flex:none; align-items:center; gap:8px; min-height:49px; padding:7px 8px 7px 12px; border-bottom:1px solid var(--scui-border); background:var(--scui-bg-raised) }
57
+ .scui-head { display:flex; flex:none; align-items:center; gap:5px; min-height:var(--scui-head-height); padding:4px 6px 4px 8px; border-bottom:1px solid var(--scui-border); background:var(--scui-bg-raised) }
57
58
  .scui-head > .scui-head-copy { display:grid; flex:1; min-width:0 }
58
- .scui-head strong { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px }
59
- .scui-head small { overflow:hidden; color:var(--scui-muted); font-size:10.5px; text-overflow:ellipsis; white-space:nowrap }
60
- .scui-head > button,.scui-menu-trigger { display:grid; place-items:center; width:30px; height:30px; padding:0; border:0; border-radius:8px; background:transparent; color:inherit; cursor:pointer; font-size:18px }
59
+ .scui-head strong { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:12px }
60
+ .scui-head small { overflow:hidden; color:var(--scui-muted); font-size:9.5px; text-overflow:ellipsis; white-space:nowrap }
61
+ .scui-head > button,.scui-menu-trigger { display:grid; place-items:center; width:28px; height:28px; padding:0; border:0; border-radius:7px; background:transparent; color:inherit; cursor:pointer; font-size:16px }
61
62
  .scui-head > button:hover,.scui-menu-trigger:hover,.scui-menu-trigger[aria-expanded="true"] { background:var(--scui-fill) }
62
63
  .scui-menu { position:relative }
63
64
  .scui-menu-panel { position:absolute; z-index:20; top:36px; right:0; display:grid; width:250px; max-height:300px; padding:5px; overflow:auto; border:1px solid var(--scui-border-strong); border-radius:10px; background:var(--scui-bg); box-shadow:0 10px 28px rgba(0,0,0,.22) }
@@ -73,7 +74,6 @@
73
74
  .scui-logo[data-harness="pi"] { color:#111; background:#efefeb }
74
75
  .scui-logo[data-harness="supercode"] { background:#111923 }
75
76
  .scui-logo > i { position:absolute; right:-5%; bottom:-5%; width:32%; height:32%; border:2px solid var(--scui-bg); border-radius:50%; background:var(--scui-muted) }
76
- .scui-logo[data-activity="observed"] > i { background:var(--scui-muted) }
77
77
  .scui-logo[data-activity="working"] > i,.scui-logo[data-activity="running"] > i { background:var(--scui-positive) }
78
78
  .scui-logo[data-activity="needs-input"] > i { background:var(--scui-warning) }
79
79
  .scui-logo[data-activity="failed"] > i { background:var(--scui-danger) }
@@ -81,6 +81,7 @@
81
81
 
82
82
  .scui-launcher { --scui-bg:#fff; --scui-fg:#191918; --scui-muted:#5f5f5a; --scui-border:#deded9; --scui-positive:#22863a; --scui-warning:#b26a00; --scui-danger:#b4232e; display:inline-flex; min-width:44px; min-height:44px; align-items:center; gap:8px; padding:4px; overflow:visible; border:1px solid var(--scui-border); border-radius:999px; background:color-mix(in srgb,var(--scui-bg) 88%,transparent); color:var(--scui-fg); box-shadow:0 5px 18px rgba(0,0,0,.14); cursor:pointer; font:12px/1.25 var(--scui-font,Inter,ui-sans-serif,system-ui,sans-serif); backdrop-filter:blur(10px) }
83
83
  .scui-launcher-logo { position:relative; display:grid; flex:none; place-items:center }.scui-launcher-logo > b { position:absolute; z-index:1; top:-6px; right:-8px; display:grid; min-width:18px; height:18px; padding:0 4px; place-items:center; border:2px solid var(--scui-bg); border-radius:999px; background:var(--scui-danger); color:#fff; font-size:9px; line-height:1 }
84
+ .scui-launcher[data-unread-tone="neutral"] .scui-launcher-logo > b { background:var(--scui-muted) }
84
85
  .scui-launcher-copy { display:grid; min-width:0; max-width:220px; padding-right:8px; text-align:left }.scui-launcher-copy strong,.scui-launcher-copy small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-launcher-copy small { color:var(--scui-muted); font-size:10px }
85
86
  .scui-launcher:hover { border-color:color-mix(in srgb,var(--scui-fg) 28%,var(--scui-border)); box-shadow:0 7px 22px rgba(0,0,0,.18) }.scui-launcher:focus-visible { outline:2px solid currentColor; outline-offset:2px }
86
87
  @media (prefers-color-scheme:dark) { .scui-launcher { --scui-bg:#151515; --scui-fg:#f3f3ef; --scui-muted:#a3a39d; --scui-border:#353532; --scui-positive:#57c26e; --scui-warning:#e8a23a; --scui-danger:#ff747d } }
@@ -108,6 +109,7 @@
108
109
  .scui-session-path { display:flex; min-width:0; overflow:hidden; color:var(--scui-muted); font-size:9.5px; white-space:nowrap }
109
110
  .scui-session-path-leading { flex:0 1 auto; overflow:hidden; text-overflow:ellipsis }.scui-session-path-separator { flex:none }.scui-session-path-trailing { flex:none; max-width:58%; overflow:hidden; direction:rtl; text-align:left; text-overflow:ellipsis; unicode-bidi:plaintext }
110
111
  .scui-session-preview small { flex:1; min-width:0; color:var(--scui-muted); font-size:10.5px }.scui-session-preview b { display:grid; flex:none; min-width:16px; height:16px; padding:0 4px; place-items:center; border-radius:8px; background:var(--scui-accent); color:var(--scui-bg); font-size:9px; line-height:16px }
112
+ .scui-session[data-writable="false"] .scui-session-preview b { background:var(--scui-muted) }
111
113
  .scui-session-copy em { overflow:hidden; color:var(--scui-danger); font-size:10px; font-style:normal; text-overflow:ellipsis; white-space:nowrap }
112
114
  .scui-load { display:block; margin:7px auto; padding:5px 10px; border:1px solid var(--scui-border); border-radius:8px; background:var(--scui-bg-raised); color:var(--scui-muted); cursor:pointer }.scui-load:disabled { opacity:.65; cursor:default }
113
115
 
@@ -206,7 +208,7 @@
206
208
  .scui-harness-manage { margin-top:4px; padding-top:4px; border-top:1px solid var(--scui-border) }.scui-harness-manage > summary { padding:6px 7px; color:var(--scui-muted); cursor:pointer; font-size:9.5px }.scui-harness-manage > div { display:grid; gap:1px }.scui-harness-manage section { display:grid; grid-template-columns:auto minmax(0,1fr) auto; min-height:36px; align-items:center; gap:8px; padding:5px 7px }.scui-harness-manage section > span { display:grid; min-width:0 }.scui-harness-manage section strong,.scui-harness-manage section small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-harness-manage section strong { font-size:10.5px; font-weight:600 }.scui-harness-manage section small { color:var(--scui-muted); font-size:8.5px }.scui-harness-manage section > button { padding:4px 6px; border:1px solid var(--scui-border); border-radius:6px; background:transparent; color:var(--scui-muted); cursor:pointer; font-size:8.5px }.scui-harness-manage section > button:hover { border-color:var(--scui-border-strong); color:var(--scui-fg) }
207
209
  .scui-readiness { margin:0 0 7px; border:1px solid var(--scui-border); border-radius:7px; background:var(--scui-bg) }.scui-readiness > summary { padding:6px 8px; color:var(--scui-muted); cursor:pointer; font-size:10px }.scui-readiness > div { display:grid; border-top:1px solid var(--scui-border) }.scui-readiness section { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:7px; padding:7px 8px }.scui-readiness section + section { border-top:1px solid var(--scui-border) }.scui-readiness section > span { display:grid; min-width:0 }.scui-readiness section strong,.scui-readiness section small,.scui-readiness section em { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-readiness section small { color:var(--scui-muted); font-size:9px }.scui-readiness section em { color:var(--scui-muted); font-size:8px; font-style:normal }.scui-readiness section button { padding:4px 6px; border:1px solid var(--scui-border); border-radius:6px; background:transparent; cursor:pointer; font-size:9px }
208
210
  .scui-new { display:grid; justify-items:center; gap:5px; margin:auto; padding:20px; color:var(--scui-muted); text-align:center }.scui-new > span { color:var(--scui-accent); font-size:28px }.scui-new strong { color:var(--scui-fg) }
209
- .scui-opening { position:absolute; z-index:30; inset:49px 0 0; display:flex; align-items:center; justify-content:center; gap:10px; padding:20px; background:var(--scui-bg) }.scui-opening > span { display:grid }.scui-opening small { color:var(--scui-muted) }.scui-opening > i { width:16px; height:16px; border:2px solid var(--scui-border); border-top-color:var(--scui-accent); border-radius:50%; animation:scui-spin .8s linear infinite }
211
+ .scui-opening { position:absolute; z-index:30; inset:var(--scui-head-height) 0 0; display:flex; align-items:center; justify-content:center; gap:10px; padding:20px; background:var(--scui-bg) }.scui-opening > span { display:grid }.scui-opening small { color:var(--scui-muted) }.scui-opening > i { width:16px; height:16px; border:2px solid var(--scui-border); border-top-color:var(--scui-accent); border-radius:50%; animation:scui-spin .8s linear infinite }
210
212
 
211
213
  @keyframes scui-spin { to { transform:rotate(360deg) } }
212
214
  @keyframes scui-breathe { 50% { transform:scale(.72); opacity:.5 } }
@@ -215,8 +217,8 @@
215
217
  @keyframes scui-session-dots { 50% { transform:translateY(-1px); opacity:.55 } }
216
218
  @media (prefers-reduced-motion:reduce) { .scui-root * { animation-duration:.001ms !important; animation-iteration-count:1 !important; transition:none !important } }
217
219
  @media (pointer:coarse) {
218
- .scui-head > button,.scui-menu-trigger { width:40px; height:40px }
219
- .scui-head { min-height:52px }
220
+ :where(.scui-root) { --scui-head-height:44px }
221
+ .scui-head > button,.scui-menu-trigger { width:36px; height:36px }
220
222
  .scui-menu-panel button,.scui-request-actions button,.scui-load { min-height:40px }
221
223
  .scui-send,.scui-stop { width:40px; height:40px }
222
224
  .scui-new-envelope-controls .scui-attach,.scui-harness-trigger { min-height:40px }