@ai-matrx/coding-sessions 0.2.0 → 0.3.1

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/engine/index.cjs +57 -11
  3. package/dist/engine/index.cjs.map +1 -1
  4. package/dist/engine/index.d.cts +1 -1
  5. package/dist/engine/index.d.ts +1 -1
  6. package/dist/engine/index.js +57 -11
  7. package/dist/engine/index.js.map +1 -1
  8. package/dist/{index-29t7n472.d.cts → index-Bbe11HoB.d.cts} +30 -3
  9. package/dist/{index-29t7n472.d.ts → index-Bbe11HoB.d.ts} +30 -3
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +12 -3
  12. package/dist/index.d.ts +12 -3
  13. package/dist/index.js.map +1 -1
  14. package/dist/react/index.cjs +34 -5
  15. package/dist/react/index.cjs.map +1 -1
  16. package/dist/react/index.d.cts +15 -1
  17. package/dist/react/index.d.ts +15 -1
  18. package/dist/react/index.js +34 -5
  19. package/dist/react/index.js.map +1 -1
  20. package/dist/server/index.cjs +47 -10
  21. package/dist/server/index.cjs.map +1 -1
  22. package/dist/server/index.d.cts +2 -2
  23. package/dist/server/index.d.ts +2 -2
  24. package/dist/server/index.js +47 -10
  25. package/dist/server/index.js.map +1 -1
  26. package/dist/sessions/index.cjs +26 -1
  27. package/dist/sessions/index.cjs.map +1 -1
  28. package/dist/sessions/index.d.cts +13 -5
  29. package/dist/sessions/index.d.ts +13 -5
  30. package/dist/sessions/index.js +26 -1
  31. package/dist/sessions/index.js.map +1 -1
  32. package/dist/{titles-eCnA5sY7.d.cts → titles-DjxUb0ji.d.cts} +8 -1
  33. package/dist/{titles-eCnA5sY7.d.ts → titles-DjxUb0ji.d.ts} +8 -1
  34. package/dist/ui/assets/index-BqzTnRFe.css +2 -0
  35. package/dist/ui/assets/{index-SuRr0-D2.js → index-U98AY8r-.js} +17 -17
  36. package/dist/ui/index.html +2 -2
  37. package/package.json +4 -4
  38. package/dist/ui/assets/index-BqpVx_My.css +0 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @ai-matrx/coding-sessions
2
2
 
3
+ ## 0.3.1
4
+
5
+ - A long Codex rollout whose first message lies past the head read (a very large `session_meta`) is
6
+ kept as "(untitled)" (source "unknown") until the owner's name for it arrives — never dropped
7
+ (with an owner and no index read, 475 such chats on one Mac were missing from the list).
8
+
9
+ ## 0.3.0
10
+
11
+ - With an owner of the apps' index (`titleHints`), this package reads none of the apps' own files:
12
+ Codex thread names and title search come from the owner (`appTitles`); the standalone host still
13
+ reads `session_index.jsonl` itself.
14
+ - `titleStatus()` / `retryTitles()` (engine + bridge): a failing owner shows "app titles unavailable —
15
+ Retry" beside Your other chats, never a silent fallback. `TitleHints.problem()` / `retry()`.
16
+ - `displayTitle()` (`./sessions`): the list's trim — leading list markers and quotes dropped, cut to
17
+ `DISPLAY_MAX` (100); the stored title stays exact and is the hover text. Vectors in the one file.
18
+ - A held chat whose own file is gone reads its history from the holder's copy
19
+ (`homes.claudeVault`, default `~/.claude/claude-app-vault/<id>/transcript.jsonl`).
20
+ - `engine.setHomes()`: a host that learns the homes later (Matrx 2: Codex homes from its core).
21
+ - `TitleHint.source` / `DiscoveredSession.titleSource` may be `"unknown"` (an owner that did not say).
22
+ - **Consumer action:** none for hosts without an owner; Matrx 2 passes `titleHints` with `problem`/`retry`.
23
+
3
24
  ## 0.2.0
4
25
 
5
26
  - **`./sessions` (new, pure):** the one title rule for a coding chat — `resolveSessionTitle` (override >
@@ -1518,6 +1518,7 @@ var import_node_path5 = __toESM(require("path"), 1);
1518
1518
 
1519
1519
  // src/sessions/titles.ts
1520
1520
  var TITLE_MAX = 300;
1521
+ var UNTITLED = "(untitled)";
1521
1522
  var BRIDGE_PLACEHOLDER = /^AI Matrx · [a-z-]+$/;
1522
1523
  var TITLE_PRECEDENCE = [
1523
1524
  /** The person's rename inside Matrx (Coding Sessions' preference). */
@@ -1663,6 +1664,7 @@ function codexHome(dir) {
1663
1664
  function defaultProviderHomes(home = import_node_os3.default.homedir()) {
1664
1665
  return {
1665
1666
  claudeProjects: import_node_path5.default.join(home, ".claude", "projects"),
1667
+ claudeVault: import_node_path5.default.join(home, ".claude", "claude-app-vault"),
1666
1668
  codex: [codexHome(import_node_path5.default.join(home, ".codex"))]
1667
1669
  };
1668
1670
  }
@@ -1820,7 +1822,8 @@ async function describeCodex(candidate, names) {
1820
1822
  }
1821
1823
  }
1822
1824
  if (isSubAgent) return null;
1823
- const resolved = resolveSessionTitle({ app: names.get(candidate.id), "first-prompt": firstPrompt });
1825
+ const found = resolveSessionTitle({ app: names.get(candidate.id), "first-prompt": firstPrompt });
1826
+ const resolved = found ?? (candidate.size > HEAD_BYTES ? { title: UNTITLED, source: "unknown" } : null);
1824
1827
  if (!resolved) return null;
1825
1828
  return {
1826
1829
  provider: "codex",
@@ -1834,6 +1837,12 @@ async function describeCodex(candidate, names) {
1834
1837
  activeElsewhere: null
1835
1838
  };
1836
1839
  }
1840
+ async function codexNames(homes, appTitles) {
1841
+ if (!appTitles) return readCodexIndex(homes.codex.map((h) => h.index));
1842
+ const out = /* @__PURE__ */ new Map();
1843
+ for (const [key, title] of appTitles) if (key.startsWith("codex:")) out.set(key.slice(6), title);
1844
+ return out;
1845
+ }
1837
1846
  async function readTail(file, size) {
1838
1847
  const start = Math.max(0, size - TAIL_BYTES);
1839
1848
  return parseJsonl(completeLines(await readSlice(file, start, Math.min(size, TAIL_BYTES)), start > 0));
@@ -1874,7 +1883,7 @@ async function discoverLocalSessions(options = {}) {
1874
1883
  const [claude, codex, names] = await Promise.all([
1875
1884
  claudeCandidates(homes.claudeProjects),
1876
1885
  codexCandidates(homes.codex),
1877
- readCodexIndex(homes.codex.map((h) => h.index))
1886
+ codexNames(homes, options.appTitles)
1878
1887
  ]);
1879
1888
  const candidates = [...claude, ...codex].sort((a, b) => b.mtimeMs - a.mtimeMs);
1880
1889
  const processes = await listProcesses();
@@ -1896,7 +1905,12 @@ async function findSessionFile(provider, providerSessionId, homes = defaultProvi
1896
1905
  if (!/^[0-9a-f-]{36}$/i.test(providerSessionId)) return null;
1897
1906
  const candidates = provider === "claude" ? await claudeCandidates(homes.claudeProjects) : await codexCandidates(homes.codex);
1898
1907
  const hits = candidates.filter((c) => c.id === providerSessionId).sort((a, b) => b.mtimeMs - a.mtimeMs);
1899
- return hits[0]?.file ?? null;
1908
+ if (hits[0]) return hits[0].file;
1909
+ if (provider === "claude" && homes.claudeVault) {
1910
+ const copy = import_node_path5.default.join(homes.claudeVault, providerSessionId, "transcript.jsonl");
1911
+ if ((await import_promises4.default.stat(copy).catch(() => null))?.isFile()) return copy;
1912
+ }
1913
+ return null;
1900
1914
  }
1901
1915
  var HISTORY_BUDGET = 24 * 1024 * 1024;
1902
1916
  var MAX_HISTORY_BUDGET = 256 * 1024 * 1024;
@@ -1950,8 +1964,8 @@ async function listSessionFiles(homes = defaultProviderHomes()) {
1950
1964
  ]);
1951
1965
  return [...claude, ...codex].sort((a, b) => b.mtimeMs - a.mtimeMs);
1952
1966
  }
1953
- async function describeSessionFile(candidate, codexNames) {
1954
- return (candidate.provider === "claude" ? describeClaude(candidate) : describeCodex(candidate, codexNames)).catch(
1967
+ async function describeSessionFile(candidate, codexNames2) {
1968
+ return (candidate.provider === "claude" ? describeClaude(candidate) : describeCodex(candidate, codexNames2)).catch(
1955
1969
  () => null
1956
1970
  );
1957
1971
  }
@@ -2146,12 +2160,12 @@ async function searchLocalChats(query, options = {}) {
2146
2160
  const raw = JSON.stringify(needle).slice(1, -1);
2147
2161
  const limit = Math.max(1, Math.min(options.limit ?? 30, 100));
2148
2162
  const deadline = Date.now() + (options.budgetMs ?? 3500);
2149
- const names = await readCodexIndex(homes.codex.map((h) => h.index));
2163
+ const names = await codexNames(homes, options.appTitles);
2150
2164
  const hits = [];
2151
2165
  let i = cursor;
2152
2166
  for (; i < files.length && hits.length < limit && Date.now() < deadline; i += 1) {
2153
2167
  const candidate = files[i];
2154
- const named = candidate.provider === "codex" ? names.get(candidate.id) : void 0;
2168
+ const named = options.appTitles?.get(`${candidate.provider}:${candidate.id}`) ?? (candidate.provider === "codex" ? names.get(candidate.id) : void 0);
2155
2169
  const nameHit = named !== void 0 && named.toLowerCase().includes(needle);
2156
2170
  const scanned = nameHit ? null : await scanFile(candidate.provider, candidate.file, needle, raw, deadline);
2157
2171
  if (scanned === TIMED_OUT) {
@@ -2187,19 +2201,35 @@ function titleHintKey(provider, sessionId) {
2187
2201
  }
2188
2202
  function createTitleHintsStore() {
2189
2203
  let hints = null;
2204
+ let problem = null;
2190
2205
  const listeners = /* @__PURE__ */ new Set();
2206
+ const tell = () => {
2207
+ for (const l of listeners) l();
2208
+ };
2191
2209
  return {
2192
2210
  current: () => hints,
2211
+ problem: () => problem,
2193
2212
  subscribe(listener) {
2194
2213
  listeners.add(listener);
2195
2214
  return () => listeners.delete(listener);
2196
2215
  },
2197
2216
  set(next) {
2198
2217
  hints = next;
2199
- for (const l of listeners) l();
2218
+ problem = null;
2219
+ tell();
2220
+ },
2221
+ setProblem(next) {
2222
+ if (next === problem) return;
2223
+ problem = next;
2224
+ tell();
2200
2225
  }
2201
2226
  };
2202
2227
  }
2228
+ function appTitlesOf(hints) {
2229
+ const out = /* @__PURE__ */ new Map();
2230
+ for (const [key, hint] of hints) out.set(key, hint.title);
2231
+ return out;
2232
+ }
2203
2233
 
2204
2234
  // src/engine/title-hints.ts
2205
2235
  function overlayHint(chat, hints, expected) {
@@ -2510,7 +2540,7 @@ function describeAuthStatus(provider, probe) {
2510
2540
  async function createCodingSessionsEngine(options = {}) {
2511
2541
  const dataDir = options.dataDir ?? import_node_path9.default.join(import_node_os5.default.homedir(), ".matrx", "coding-sessions");
2512
2542
  const stateDir = import_node_path9.default.join(dataDir, "paperclip-state");
2513
- const homes = options.homes ?? defaultProviderHomes();
2543
+ let homes = options.homes ?? defaultProviderHomes();
2514
2544
  const index = new RunIndex(dataDir);
2515
2545
  await index.load();
2516
2546
  const preferences = new PreferenceStore(dataDir);
@@ -2592,6 +2622,11 @@ async function createCodingSessionsEngine(options = {}) {
2592
2622
  }
2593
2623
  const titleHints = options.titleHints;
2594
2624
  const hintsNow = () => titleHints?.current() ?? null;
2625
+ const appTitles = () => {
2626
+ if (!titleHints) return void 0;
2627
+ const hints = hintsNow();
2628
+ return hints ? appTitlesOf(hints) : /* @__PURE__ */ new Map();
2629
+ };
2595
2630
  const unsubscribeHints = titleHints?.subscribe(() => emit({ type: "titles" })) ?? null;
2596
2631
  const withHintTitle = (session) => {
2597
2632
  const hint = session.providerSessionId ? hintsNow()?.get(titleHintKey(session.provider, session.providerSessionId)) : void 0;
@@ -3066,7 +3101,7 @@ async function createCodingSessionsEngine(options = {}) {
3066
3101
  );
3067
3102
  },
3068
3103
  async discoverSessions(opts) {
3069
- const read = await discoverLocalSessions({ limit: opts?.limit, homes, excludePids: ourPids() });
3104
+ const read = await discoverLocalSessions({ limit: opts?.limit, homes, excludePids: ourPids(), appTitles: appTitles() });
3070
3105
  const hints = hintsNow();
3071
3106
  const found = read.map((chat) => overlayHint(chat, hints, titleHints !== void 0));
3072
3107
  if (backups) {
@@ -3104,6 +3139,17 @@ async function createCodingSessionsEngine(options = {}) {
3104
3139
  }))
3105
3140
  );
3106
3141
  },
3142
+ async titleStatus() {
3143
+ if (!titleHints) return { owner: false, pending: false, problem: null };
3144
+ const problem = titleHints.problem?.() ?? null;
3145
+ return { owner: true, pending: hintsNow() === null && problem === null, problem };
3146
+ },
3147
+ async retryTitles() {
3148
+ titleHints?.retry?.();
3149
+ },
3150
+ setHomes(next) {
3151
+ homes = next;
3152
+ },
3107
3153
  async listSessions() {
3108
3154
  return index.sessions().map(withHintTitle);
3109
3155
  },
@@ -3280,7 +3326,7 @@ async function createCodingSessionsEngine(options = {}) {
3280
3326
  return all;
3281
3327
  },
3282
3328
  async searchChats(query, opts) {
3283
- const result = await searchLocalChats(String(query ?? ""), { homes, cursor: opts?.cursor, limit: opts?.limit });
3329
+ const result = await searchLocalChats(String(query ?? ""), { homes, cursor: opts?.cursor, limit: opts?.limit, appTitles: appTitles() });
3284
3330
  const hints = hintsNow();
3285
3331
  return { ...result, hits: result.hits.map((hit) => ({ ...hit, title: hints?.get(titleHintKey(hit.provider, hit.providerSessionId))?.title ?? hit.title })) };
3286
3332
  },