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

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
@@ -205,7 +205,9 @@ and can overlay a machine-wide session inventory, pagination state, attention, d
205
205
  errors, and owned/attached identities without reimplementing transcript or capability semantics.
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
- opaque-key callback and retains the reversible locator map.
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
211
 
210
212
  ## Modularity contract
211
213
 
package/activity.mjs CHANGED
@@ -554,6 +554,7 @@ function readSessions(value) {
554
554
  updatedAt: nullableNumber(row.updatedAt),
555
555
  messages: nullableNumber(row.messages),
556
556
  active: row.active === true,
557
+ writable: row.writable === true,
557
558
  live: row.live === true,
558
559
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
559
560
  }];
@@ -783,6 +784,7 @@ function sessionDisplayName(session) {
783
784
  return title && title !== session.name ? title : session.name || "Untitled chat";
784
785
  }
785
786
  function sessionActivity(state, row) {
787
+ if (!row.writable) return "observed";
786
788
  if (state.needsInput && row.active) return "needs-input";
787
789
  if (state.busy && row.active) return "working";
788
790
  if (row.runtimeStatus === "busy") return "working";
@@ -800,6 +802,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
800
802
  finished: 30,
801
803
  running: 20,
802
804
  recent: 10,
805
+ observed: 0,
803
806
  idle: 0
804
807
  });
805
808
  function activityDetail(activity, preview, pill) {
@@ -810,6 +813,7 @@ function activityDetail(activity, preview, pill) {
810
813
  if (activity === "finished") return "Finished";
811
814
  if (activity === "unseen") return "New reply";
812
815
  if (activity === "running") return "Session is running";
816
+ if (activity === "observed") return "Read-only channel";
813
817
  return pill || "Agent chats";
814
818
  }
815
819
  function projectAgentActivity(value) {
package/components.mjs CHANGED
@@ -566,6 +566,7 @@ function readSessions(value) {
566
566
  updatedAt: nullableNumber(row.updatedAt),
567
567
  messages: nullableNumber(row.messages),
568
568
  active: row.active === true,
569
+ writable: row.writable === true,
569
570
  live: row.live === true,
570
571
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
571
572
  }];
@@ -795,6 +796,7 @@ function sessionDisplayName(session) {
795
796
  return title && title !== session.name ? title : session.name || "Untitled chat";
796
797
  }
797
798
  function sessionActivity(state, row) {
799
+ if (!row.writable) return "observed";
798
800
  if (state.needsInput && row.active) return "needs-input";
799
801
  if (state.busy && row.active) return "working";
800
802
  if (row.runtimeStatus === "busy") return "working";
@@ -812,6 +814,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
812
814
  finished: 30,
813
815
  running: 20,
814
816
  recent: 10,
817
+ observed: 0,
815
818
  idle: 0
816
819
  });
817
820
  function activityDetail(activity, preview, pill) {
@@ -822,6 +825,7 @@ function activityDetail(activity, preview, pill) {
822
825
  if (activity === "finished") return "Finished";
823
826
  if (activity === "unseen") return "New reply";
824
827
  if (activity === "running") return "Session is running";
828
+ if (activity === "observed") return "Read-only channel";
825
829
  return pill || "Agent chats";
826
830
  }
827
831
  function projectAgentActivity(value) {
@@ -2299,7 +2303,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2299
2303
  const unreadCount = attention?.unreadCount ?? 0;
2300
2304
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2301
2305
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2302
- return /* @__PURE__ */ jsxs7("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2306
+ return /* @__PURE__ */ jsxs7("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "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: [
2303
2307
  /* @__PURE__ */ jsx8(HarnessLogo, { id: row.harness, activity, size: 34 }),
2304
2308
  /* @__PURE__ */ jsxs7("span", { className: "scui-session-copy", children: [
2305
2309
  /* @__PURE__ */ jsxs7("span", { className: "scui-session-title", children: [
package/composer.mjs CHANGED
@@ -77,6 +77,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
77
77
  finished: 30,
78
78
  running: 20,
79
79
  recent: 10,
80
+ observed: 0,
80
81
  idle: 0
81
82
  });
82
83
  function canContinueHere(state) {
package/controller.d.ts CHANGED
@@ -65,6 +65,8 @@ export interface SessionInventoryProjectionOptions {
65
65
  maxSessions?: number;
66
66
  liveWindowMs?: number;
67
67
  preserveOrder?: boolean;
68
+ /** Capability check performed by the trusted host; omitted rows are read-only observations. */
69
+ isWritable?(descriptor: SessionDescriptor): boolean;
68
70
  }
69
71
 
70
72
  export function sessionConversationUpdatedAt(descriptor: SessionDescriptor): number | null;
package/controller.mjs CHANGED
@@ -72,7 +72,7 @@ function descriptorTitle(descriptor) {
72
72
  return compactTopic(nativeTitle);
73
73
  }
74
74
  if (descriptor?.locator?.harness !== 'claude-code' && descriptor?.locator?.harness !== 'codex') {
75
- return compactTopic(nativeTitle || workspace || descriptor?.locator?.session_id?.slice(0, 8)) || 'Untitled chat';
75
+ return compactTopic(nativeTitle || workspace) || 'Untitled chat';
76
76
  }
77
77
  const openingUserMessages = (descriptor?.preview_candidates ?? []).filter(
78
78
  (candidate) => candidate?.role === undefined || candidate.role === 'user',
@@ -125,6 +125,7 @@ export function projectSessionInventory(descriptors, options) {
125
125
  active: active?.sessionId != null
126
126
  && descriptor.locator.harness === active.harness
127
127
  && descriptor.locator.session_id === active.sessionId,
128
+ writable: options.isWritable?.(descriptor) === true,
128
129
  live: updatedAt !== null && now - updatedAt <= liveWindowMs,
129
130
  runtimeStatus: sessionDescriptorRuntimeStatus(descriptor),
130
131
  });
@@ -378,14 +379,14 @@ function attached(snapshot) {
378
379
  harness: snapshot.activeHarness,
379
380
  name: workspaceName(snapshot.workspace),
380
381
  cwd: snapshot.workspace,
381
- title: snapshot.activeSession?.model ?? snapshot.activeSessionId ?? 'Active session',
382
+ title: 'Untitled chat',
382
383
  } : null;
383
384
  return {
384
385
  key: session.key,
385
386
  harness: session.harness,
386
387
  name: workspaceName(session.cwd),
387
388
  cwd: session.cwd ?? '',
388
- title: session.title ?? session.model ?? session.sessionId,
389
+ title: session.title ?? 'Untitled chat',
389
390
  };
390
391
  }
391
392
 
@@ -442,11 +443,12 @@ function projectClientSnapshotInternal(snapshot, options, imageRegistry) {
442
443
  harness: session.harness,
443
444
  name: workspaceName(session.cwd),
444
445
  cwd: session.cwd ?? '',
445
- title: session.title ?? session.model ?? session.sessionId,
446
+ title: session.title ?? 'Untitled chat',
446
447
  age: relativeAge(session.updatedAt, now),
447
448
  updatedAt: session.updatedAt ?? null,
448
449
  messages: session.messageCount ?? null,
449
450
  active: session.key === snapshot.activeSessionKey,
451
+ writable: session.key === snapshot.activeSessionKey && actions.send === true,
450
452
  live: typeof session.updatedAt === 'number' && now - session.updatedAt <= liveWindowMs,
451
453
  runtimeStatus: session.liveStatus ?? null,
452
454
  }));
package/conversation.mjs CHANGED
@@ -439,6 +439,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
439
439
  finished: 30,
440
440
  running: 20,
441
441
  recent: 10,
442
+ observed: 0,
442
443
  idle: 0
443
444
  });
444
445
  function groupConversation(entries) {
package/core.mjs CHANGED
@@ -562,6 +562,7 @@ function readSessions(value) {
562
562
  updatedAt: nullableNumber(row.updatedAt),
563
563
  messages: nullableNumber(row.messages),
564
564
  active: row.active === true,
565
+ writable: row.writable === true,
565
566
  live: row.live === true,
566
567
  runtimeStatus: row.runtimeStatus === 'running' || row.runtimeStatus === 'busy' || row.runtimeStatus === 'idle' ? row.runtimeStatus : null,
567
568
  }];
@@ -858,6 +859,9 @@ export function sessionDisplayName(session) {
858
859
  }
859
860
 
860
861
  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';
861
865
  if (state.needsInput && row.active) return 'needs-input';
862
866
  if (state.busy && row.active) return 'working';
863
867
  // Unread/finished attention has its own badge. It must not suppress the
@@ -878,6 +882,7 @@ const ACTIVITY_PRIORITY = Object.freeze({
878
882
  finished: 30,
879
883
  running: 20,
880
884
  recent: 10,
885
+ observed: 0,
881
886
  idle: 0,
882
887
  });
883
888
 
@@ -889,6 +894,7 @@ function activityDetail(activity, preview, pill) {
889
894
  if (activity === 'finished') return 'Finished';
890
895
  if (activity === 'unseen') return 'New reply';
891
896
  if (activity === 'running') return 'Session is running';
897
+ if (activity === 'observed') return 'Read-only channel';
892
898
  return pill || 'Agent chats';
893
899
  }
894
900
 
package/embed.mjs CHANGED
@@ -569,6 +569,7 @@ function readSessions(value) {
569
569
  updatedAt: nullableNumber(row.updatedAt),
570
570
  messages: nullableNumber(row.messages),
571
571
  active: row.active === true,
572
+ writable: row.writable === true,
572
573
  live: row.live === true,
573
574
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
574
575
  }];
@@ -798,6 +799,7 @@ function sessionDisplayName(session) {
798
799
  return title && title !== session.name ? title : session.name || "Untitled chat";
799
800
  }
800
801
  function sessionActivity(state, row) {
802
+ if (!row.writable) return "observed";
801
803
  if (state.needsInput && row.active) return "needs-input";
802
804
  if (state.busy && row.active) return "working";
803
805
  if (row.runtimeStatus === "busy") return "working";
@@ -815,6 +817,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
815
817
  finished: 30,
816
818
  running: 20,
817
819
  recent: 10,
820
+ observed: 0,
818
821
  idle: 0
819
822
  });
820
823
  function filterSessions(rows, query) {
@@ -2219,7 +2222,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2219
2222
  const unreadCount = attention?.unreadCount ?? 0;
2220
2223
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2221
2224
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2222
- return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2225
+ return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "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: [
2223
2226
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
2224
2227
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-copy", children: [
2225
2228
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-title", 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' | 'recent' | 'running' | 'working' | 'needs-input' | 'finished' | 'failed' | 'unseen';
11
+ export type SessionActivity = 'idle' | 'observed' | 'recent' | 'running' | 'working' | 'needs-input' | 'finished' | 'failed' | 'unseen';
12
12
 
13
13
  export interface AgentActivityModel {
14
14
  harness: HarnessId | '';
@@ -58,6 +58,8 @@ export interface SessionRowModel {
58
58
  updatedAt: number | null;
59
59
  messages: number | null;
60
60
  active: boolean;
61
+ /** This surface currently has a real send or terminal-control path for the session. */
62
+ writable: boolean;
61
63
  live: boolean;
62
64
  runtimeStatus: 'running' | 'busy' | 'idle' | null;
63
65
  }
package/messenger.mjs CHANGED
@@ -566,6 +566,7 @@ function readSessions(value) {
566
566
  updatedAt: nullableNumber(row.updatedAt),
567
567
  messages: nullableNumber(row.messages),
568
568
  active: row.active === true,
569
+ writable: row.writable === true,
569
570
  live: row.live === true,
570
571
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
571
572
  }];
@@ -795,6 +796,7 @@ function sessionDisplayName(session) {
795
796
  return title && title !== session.name ? title : session.name || "Untitled chat";
796
797
  }
797
798
  function sessionActivity(state, row) {
799
+ if (!row.writable) return "observed";
798
800
  if (state.needsInput && row.active) return "needs-input";
799
801
  if (state.busy && row.active) return "working";
800
802
  if (row.runtimeStatus === "busy") return "working";
@@ -812,6 +814,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
812
814
  finished: 30,
813
815
  running: 20,
814
816
  recent: 10,
817
+ observed: 0,
815
818
  idle: 0
816
819
  });
817
820
  function filterSessions(rows, query) {
@@ -2216,7 +2219,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2216
2219
  const unreadCount = attention?.unreadCount ?? 0;
2217
2220
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2218
2221
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2219
- return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2222
+ return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "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: [
2220
2223
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
2221
2224
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-copy", children: [
2222
2225
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-title", children: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter-ai-dev/supercode-ui",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "type": "module",
5
5
  "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
6
  "exports": {
@@ -554,6 +554,7 @@ function readSessions(value) {
554
554
  updatedAt: nullableNumber(row.updatedAt),
555
555
  messages: nullableNumber(row.messages),
556
556
  active: row.active === true,
557
+ writable: row.writable === true,
557
558
  live: row.live === true,
558
559
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
559
560
  }];
@@ -783,6 +784,7 @@ function sessionDisplayName(session) {
783
784
  return title && title !== session.name ? title : session.name || "Untitled chat";
784
785
  }
785
786
  function sessionActivity(state, row) {
787
+ if (!row.writable) return "observed";
786
788
  if (state.needsInput && row.active) return "needs-input";
787
789
  if (state.busy && row.active) return "working";
788
790
  if (row.runtimeStatus === "busy") return "working";
@@ -800,6 +802,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
800
802
  finished: 30,
801
803
  running: 20,
802
804
  recent: 10,
805
+ observed: 0,
803
806
  idle: 0
804
807
  });
805
808
  function activityDetail(activity, preview, pill) {
@@ -810,6 +813,7 @@ function activityDetail(activity, preview, pill) {
810
813
  if (activity === "finished") return "Finished";
811
814
  if (activity === "unseen") return "New reply";
812
815
  if (activity === "running") return "Session is running";
816
+ if (activity === "observed") return "Read-only channel";
813
817
  return pill || "Agent chats";
814
818
  }
815
819
  function projectAgentActivity(value) {
@@ -566,6 +566,7 @@ function readSessions(value) {
566
566
  updatedAt: nullableNumber(row.updatedAt),
567
567
  messages: nullableNumber(row.messages),
568
568
  active: row.active === true,
569
+ writable: row.writable === true,
569
570
  live: row.live === true,
570
571
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
571
572
  }];
@@ -795,6 +796,7 @@ function sessionDisplayName(session) {
795
796
  return title && title !== session.name ? title : session.name || "Untitled chat";
796
797
  }
797
798
  function sessionActivity(state, row) {
799
+ if (!row.writable) return "observed";
798
800
  if (state.needsInput && row.active) return "needs-input";
799
801
  if (state.busy && row.active) return "working";
800
802
  if (row.runtimeStatus === "busy") return "working";
@@ -812,6 +814,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
812
814
  finished: 30,
813
815
  running: 20,
814
816
  recent: 10,
817
+ observed: 0,
815
818
  idle: 0
816
819
  });
817
820
  function activityDetail(activity, preview, pill) {
@@ -822,6 +825,7 @@ function activityDetail(activity, preview, pill) {
822
825
  if (activity === "finished") return "Finished";
823
826
  if (activity === "unseen") return "New reply";
824
827
  if (activity === "running") return "Session is running";
828
+ if (activity === "observed") return "Read-only channel";
825
829
  return pill || "Agent chats";
826
830
  }
827
831
  function projectAgentActivity(value) {
@@ -2299,7 +2303,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2299
2303
  const unreadCount = attention?.unreadCount ?? 0;
2300
2304
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2301
2305
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2302
- return /* @__PURE__ */ jsxs7("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2306
+ return /* @__PURE__ */ jsxs7("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "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: [
2303
2307
  /* @__PURE__ */ jsx8(HarnessLogo, { id: row.harness, activity, size: 34 }),
2304
2308
  /* @__PURE__ */ jsxs7("span", { className: "scui-session-copy", children: [
2305
2309
  /* @__PURE__ */ jsxs7("span", { className: "scui-session-title", children: [
@@ -77,6 +77,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
77
77
  finished: 30,
78
78
  running: 20,
79
79
  recent: 10,
80
+ observed: 0,
80
81
  idle: 0
81
82
  });
82
83
  function canContinueHere(state) {
@@ -439,6 +439,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
439
439
  finished: 30,
440
440
  running: 20,
441
441
  recent: 10,
442
+ observed: 0,
442
443
  idle: 0
443
444
  });
444
445
  function groupConversation(entries) {
@@ -566,6 +566,7 @@ function readSessions(value) {
566
566
  updatedAt: nullableNumber(row.updatedAt),
567
567
  messages: nullableNumber(row.messages),
568
568
  active: row.active === true,
569
+ writable: row.writable === true,
569
570
  live: row.live === true,
570
571
  runtimeStatus: row.runtimeStatus === "running" || row.runtimeStatus === "busy" || row.runtimeStatus === "idle" ? row.runtimeStatus : null
571
572
  }];
@@ -795,6 +796,7 @@ function sessionDisplayName(session) {
795
796
  return title && title !== session.name ? title : session.name || "Untitled chat";
796
797
  }
797
798
  function sessionActivity(state, row) {
799
+ if (!row.writable) return "observed";
798
800
  if (state.needsInput && row.active) return "needs-input";
799
801
  if (state.busy && row.active) return "working";
800
802
  if (row.runtimeStatus === "busy") return "working";
@@ -812,6 +814,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
812
814
  finished: 30,
813
815
  running: 20,
814
816
  recent: 10,
817
+ observed: 0,
815
818
  idle: 0
816
819
  });
817
820
  function filterSessions(rows, query) {
@@ -2216,7 +2219,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
2216
2219
  const unreadCount = attention?.unreadCount ?? 0;
2217
2220
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
2218
2221
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
2219
- return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
2222
+ return /* @__PURE__ */ jsxs6("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "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: [
2220
2223
  /* @__PURE__ */ jsx7(HarnessLogo, { id: row.harness, activity, size: 34 }),
2221
2224
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-copy", children: [
2222
2225
  /* @__PURE__ */ jsxs6("span", { className: "scui-session-title", children: [
@@ -83,6 +83,7 @@ function sessionDisplayName(session) {
83
83
  return title && title !== session.name ? title : session.name || "Untitled chat";
84
84
  }
85
85
  function sessionActivity(state, row) {
86
+ if (!row.writable) return "observed";
86
87
  if (state.needsInput && row.active) return "needs-input";
87
88
  if (state.busy && row.active) return "working";
88
89
  if (row.runtimeStatus === "busy") return "working";
@@ -100,6 +101,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
100
101
  finished: 30,
101
102
  running: 20,
102
103
  recent: 10,
104
+ observed: 0,
103
105
  idle: 0
104
106
  });
105
107
  function filterSessions(rows, query) {
@@ -332,7 +334,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
332
334
  const unreadCount = attention?.unreadCount ?? 0;
333
335
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
334
336
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
335
- return /* @__PURE__ */ jsxs5("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
337
+ return /* @__PURE__ */ jsxs5("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "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: [
336
338
  /* @__PURE__ */ jsx6(HarnessLogo, { id: row.harness, activity, size: 34 }),
337
339
  /* @__PURE__ */ jsxs5("span", { className: "scui-session-copy", children: [
338
340
  /* @__PURE__ */ jsxs5("span", { className: "scui-session-title", children: [
@@ -77,6 +77,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
77
77
  finished: 30,
78
78
  running: 20,
79
79
  recent: 10,
80
+ observed: 0,
80
81
  idle: 0
81
82
  });
82
83
 
package/sessions.mjs CHANGED
@@ -83,6 +83,7 @@ function sessionDisplayName(session) {
83
83
  return title && title !== session.name ? title : session.name || "Untitled chat";
84
84
  }
85
85
  function sessionActivity(state, row) {
86
+ if (!row.writable) return "observed";
86
87
  if (state.needsInput && row.active) return "needs-input";
87
88
  if (state.busy && row.active) return "working";
88
89
  if (row.runtimeStatus === "busy") return "working";
@@ -100,6 +101,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
100
101
  finished: 30,
101
102
  running: 20,
102
103
  recent: 10,
104
+ observed: 0,
103
105
  idle: 0
104
106
  });
105
107
  function filterSessions(rows, query) {
@@ -332,7 +334,7 @@ function SessionRow({ row, state, onOpen, now = Date.now() }) {
332
334
  const unreadCount = attention?.unreadCount ?? 0;
333
335
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
334
336
  const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
335
- return /* @__PURE__ */ jsxs5("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
337
+ return /* @__PURE__ */ jsxs5("button", { className: "scui-session", "data-active": row.active, "data-activity": activity, "data-writable": row.writable, "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: [
336
338
  /* @__PURE__ */ jsx6(HarnessLogo, { id: row.harness, activity, size: 34 }),
337
339
  /* @__PURE__ */ jsxs5("span", { className: "scui-session-copy", children: [
338
340
  /* @__PURE__ */ jsxs5("span", { className: "scui-session-title", children: [
package/settings.mjs CHANGED
@@ -77,6 +77,7 @@ var ACTIVITY_PRIORITY = Object.freeze({
77
77
  finished: 30,
78
78
  running: 20,
79
79
  recent: 10,
80
+ observed: 0,
80
81
  idle: 0
81
82
  });
82
83
 
package/styles.css CHANGED
@@ -73,6 +73,7 @@
73
73
  .scui-logo[data-harness="pi"] { color:#111; background:#efefeb }
74
74
  .scui-logo[data-harness="supercode"] { background:#111923 }
75
75
  .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) }
76
77
  .scui-logo[data-activity="working"] > i,.scui-logo[data-activity="running"] > i { background:var(--scui-positive) }
77
78
  .scui-logo[data-activity="needs-input"] > i { background:var(--scui-warning) }
78
79
  .scui-logo[data-activity="failed"] > i { background:var(--scui-danger) }