@ai-matrx/coding-sessions 0.1.165 → 0.2.0

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.
@@ -2,6 +2,10 @@ import { ReactNode } from 'react';
2
2
  import { JsonValue } from '@ai-matrx/agents/portable';
3
3
  import { ChatThreadStore, ChatMessage } from '@ai-matrx/chat';
4
4
 
5
+ /** Where a title came from, strongest first. */
6
+ declare const TITLE_PRECEDENCE: readonly ["override", "app", "app-remembered", "custom-title", "ai-title", "agent-name", "summary", "first-prompt", "last-prompt"];
7
+ type TitleSource = (typeof TITLE_PRECEDENCE)[number];
8
+
5
9
  /**
6
10
  * The public contract of `@ai-matrx/coding-sessions`, shared by the engine
7
11
  * (Node), the local host (Node) and the UI (browser). Pure types — nothing here
@@ -129,7 +133,22 @@ interface DiscoveredSession {
129
133
  provider: CodingProvider;
130
134
  providerSessionId: string;
131
135
  cwd: string | null;
136
+ /**
137
+ * The chat's own title by the one rule (`./sessions`): the coding app's title when the owner of
138
+ * the apps' index supplied it (title hints), else the transcript's. Never the person's rename —
139
+ * that is applied once, by the screen, from the preferences.
140
+ */
132
141
  title: string;
142
+ /** Where `title` came from (`TITLE_PRECEDENCE`). */
143
+ titleSource?: TitleSource | undefined;
144
+ /** The app's title is still on its way (hints not loaded yet); `title` is the transcript's for now. */
145
+ titlePending?: boolean | undefined;
146
+ /** Archived in the app (hidden by default, like Code › Chats). */
147
+ archived?: boolean | undefined;
148
+ /** A scheduled or automated run (hidden by default). */
149
+ scheduled?: boolean | undefined;
150
+ /** The app lists it but its conversation file is gone: why. Shown, cannot be taken over. */
151
+ unavailable?: string | null | undefined;
133
152
  updatedAt: string;
134
153
  /** Where it was started, when the provider records it ("cli", "vscode", "exec", …). */
135
154
  source: string | null;
@@ -254,6 +273,10 @@ type CodingEvent = {
254
273
  } | {
255
274
  type: "preferences";
256
275
  preferences: Record<string, ChatPreference>;
276
+ }
277
+ /** The apps' titles changed (or finished loading): re-read the discovered chats. */
278
+ | {
279
+ type: "titles";
257
280
  } | {
258
281
  type: "item";
259
282
  sessionId: string;
@@ -2,6 +2,10 @@ import { ReactNode } from 'react';
2
2
  import { JsonValue } from '@ai-matrx/agents/portable';
3
3
  import { ChatThreadStore, ChatMessage } from '@ai-matrx/chat';
4
4
 
5
+ /** Where a title came from, strongest first. */
6
+ declare const TITLE_PRECEDENCE: readonly ["override", "app", "app-remembered", "custom-title", "ai-title", "agent-name", "summary", "first-prompt", "last-prompt"];
7
+ type TitleSource = (typeof TITLE_PRECEDENCE)[number];
8
+
5
9
  /**
6
10
  * The public contract of `@ai-matrx/coding-sessions`, shared by the engine
7
11
  * (Node), the local host (Node) and the UI (browser). Pure types — nothing here
@@ -129,7 +133,22 @@ interface DiscoveredSession {
129
133
  provider: CodingProvider;
130
134
  providerSessionId: string;
131
135
  cwd: string | null;
136
+ /**
137
+ * The chat's own title by the one rule (`./sessions`): the coding app's title when the owner of
138
+ * the apps' index supplied it (title hints), else the transcript's. Never the person's rename —
139
+ * that is applied once, by the screen, from the preferences.
140
+ */
132
141
  title: string;
142
+ /** Where `title` came from (`TITLE_PRECEDENCE`). */
143
+ titleSource?: TitleSource | undefined;
144
+ /** The app's title is still on its way (hints not loaded yet); `title` is the transcript's for now. */
145
+ titlePending?: boolean | undefined;
146
+ /** Archived in the app (hidden by default, like Code › Chats). */
147
+ archived?: boolean | undefined;
148
+ /** A scheduled or automated run (hidden by default). */
149
+ scheduled?: boolean | undefined;
150
+ /** The app lists it but its conversation file is gone: why. Shown, cannot be taken over. */
151
+ unavailable?: string | null | undefined;
133
152
  updatedAt: string;
134
153
  /** Where it was started, when the provider records it ("cli", "vscode", "exec", …). */
135
154
  source: string | null;
@@ -254,6 +273,10 @@ type CodingEvent = {
254
273
  } | {
255
274
  type: "preferences";
256
275
  preferences: Record<string, ChatPreference>;
276
+ }
277
+ /** The apps' titles changed (or finished loading): re-read the discovered chats. */
278
+ | {
279
+ type: "titles";
257
280
  } | {
258
281
  type: "item";
259
282
  sessionId: string;
@@ -305,6 +305,7 @@ function sessionEntry(session, prefs) {
305
305
  updatedAt: session.updatedAt,
306
306
  pinned: pref.pinned === true,
307
307
  hidden: pref.hidden === true,
308
+ hiddenBy: pref.hidden === true ? "you" : null,
308
309
  session,
309
310
  discovered: null
310
311
  };
@@ -322,7 +323,9 @@ function discoveredEntry(d, prefs) {
322
323
  cwd: d.cwd,
323
324
  updatedAt: d.updatedAt,
324
325
  pinned: pref.pinned === true,
325
- hidden: pref.hidden === true,
326
+ // Archived chats and scheduled runs are hidden by default, as in Code › Chats; a pin shows them.
327
+ hidden: pref.hidden === true || pref.pinned !== true && (d.archived === true || d.scheduled === true),
328
+ hiddenBy: pref.hidden === true ? "you" : pref.pinned === true ? null : d.archived ? "archived" : d.scheduled ? "scheduled" : null,
326
329
  session: null,
327
330
  discovered: d
328
331
  };
@@ -1483,6 +1486,7 @@ function Sidebar(props) {
1483
1486
  const [search, searchMore] = useContentSearch(query);
1484
1487
  const [groupMode, setGroupMode] = useState5(() => readLocal("coding-sessions.group", ["date", "folder"], "date"));
1485
1488
  const [showHidden, setShowHidden] = useState5(false);
1489
+ const titlesUpdating = state.discovered.data.some((d) => d.titlePending === true);
1486
1490
  const [takingOver, setTakingOver] = useState5(null);
1487
1491
  const [takeoverError, setTakeoverError] = useState5(null);
1488
1492
  const prefs = state.preferences;
@@ -1536,6 +1540,7 @@ function Sidebar(props) {
1536
1540
  "Running in ",
1537
1541
  PROVIDER_LABEL[entry.provider]
1538
1542
  ] });
1543
+ else if (d?.unavailable) meta = `${PROVIDER_LABEL[entry.provider]} \xB7 file missing \xB7 ${relativeTime(entry.updatedAt, now)}`;
1539
1544
  else {
1540
1545
  const status = run ? run.status === "failed" ? "Failed \xB7 " : run.status === "stopped" ? "Stopped \xB7 " : "" : "";
1541
1546
  const where = entry.cwd ? prettyPath2(entry.cwd) : "unknown folder";
@@ -1625,7 +1630,7 @@ function Sidebar(props) {
1625
1630
  ] }) : null,
1626
1631
  /* @__PURE__ */ jsx6(SectionLabel, { children: "Recent" }),
1627
1632
  state.sessions.loading && state.sessions.loadedAt === null ? /* @__PURE__ */ jsx6(SidebarSkeleton, {}) : state.sessions.error ? /* @__PURE__ */ jsx6("p", { className: "px-4 py-1 text-[12px] text-destructive", children: state.sessions.error }) : recent.length === 0 ? running.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "px-4 py-1 text-[12px] leading-5 text-muted-foreground", children: "Sessions you start or take over here will appear here." }) : null : recent.map((e) => renderEntry(e)),
1628
- /* @__PURE__ */ jsx6(
1633
+ /* @__PURE__ */ jsxs6(
1629
1634
  SectionLabel,
1630
1635
  {
1631
1636
  action: /* @__PURE__ */ jsxs6("span", { className: "flex items-center gap-0.5", children: [
@@ -1653,7 +1658,10 @@ function Sidebar(props) {
1653
1658
  }
1654
1659
  )
1655
1660
  ] }),
1656
- children: "Your other chats"
1661
+ children: [
1662
+ "Your other chats",
1663
+ titlesUpdating ? /* @__PURE__ */ jsx6("span", { className: "ml-1 font-normal normal-case text-muted-foreground", "data-cs-titles": "updating", children: "\xB7 titles updating" }) : null
1664
+ ]
1657
1665
  }
1658
1666
  ),
1659
1667
  state.discovered.loading && state.discovered.loadedAt === null ? /* @__PURE__ */ jsx6(SidebarSkeleton, {}) : state.discovered.error ? /* @__PURE__ */ jsx6("p", { className: "px-4 py-1 text-[12px] text-destructive", children: state.discovered.error }) : otherList.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "px-4 py-1 text-[12px] leading-5 text-muted-foreground", children: others.length > 0 ? "Every chat found here is pinned or hidden." : "No other Claude Code or Codex chats were found on this computer." }) : /* @__PURE__ */ jsxs6(Fragment4, { children: [
@@ -2171,6 +2179,10 @@ var CodingSessionsClient = class {
2171
2179
  } else if (event.type === "preferences") {
2172
2180
  this.set({ preferences: event.preferences });
2173
2181
  return;
2182
+ } else if (event.type === "titles") {
2183
+ void this.refreshDiscovered();
2184
+ void this.refreshSessions();
2185
+ return;
2174
2186
  }
2175
2187
  const sessionId = event.type === "session" ? event.session.id : event.type === "run" ? event.run.sessionId : event.sessionId;
2176
2188
  this.transcripts.get(sessionId)?.onEvent(event);