@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
@@ -1468,6 +1468,7 @@ var import_node_path5 = __toESM(require("path"), 1);
1468
1468
 
1469
1469
  // src/sessions/titles.ts
1470
1470
  var TITLE_MAX = 300;
1471
+ var UNTITLED = "(untitled)";
1471
1472
  var BRIDGE_PLACEHOLDER = /^AI Matrx · [a-z-]+$/;
1472
1473
  var TITLE_PRECEDENCE = [
1473
1474
  /** The person's rename inside Matrx (Coding Sessions' preference). */
@@ -1613,6 +1614,7 @@ function codexHome(dir) {
1613
1614
  function defaultProviderHomes(home = import_node_os3.default.homedir()) {
1614
1615
  return {
1615
1616
  claudeProjects: import_node_path5.default.join(home, ".claude", "projects"),
1617
+ claudeVault: import_node_path5.default.join(home, ".claude", "claude-app-vault"),
1616
1618
  codex: [codexHome(import_node_path5.default.join(home, ".codex"))]
1617
1619
  };
1618
1620
  }
@@ -1770,7 +1772,8 @@ async function describeCodex(candidate, names) {
1770
1772
  }
1771
1773
  }
1772
1774
  if (isSubAgent) return null;
1773
- const resolved = resolveSessionTitle({ app: names.get(candidate.id), "first-prompt": firstPrompt });
1775
+ const found = resolveSessionTitle({ app: names.get(candidate.id), "first-prompt": firstPrompt });
1776
+ const resolved = found ?? (candidate.size > HEAD_BYTES ? { title: UNTITLED, source: "unknown" } : null);
1774
1777
  if (!resolved) return null;
1775
1778
  return {
1776
1779
  provider: "codex",
@@ -1784,6 +1787,12 @@ async function describeCodex(candidate, names) {
1784
1787
  activeElsewhere: null
1785
1788
  };
1786
1789
  }
1790
+ async function codexNames(homes, appTitles) {
1791
+ if (!appTitles) return readCodexIndex(homes.codex.map((h) => h.index));
1792
+ const out = /* @__PURE__ */ new Map();
1793
+ for (const [key, title] of appTitles) if (key.startsWith("codex:")) out.set(key.slice(6), title);
1794
+ return out;
1795
+ }
1787
1796
  async function readTail(file, size) {
1788
1797
  const start = Math.max(0, size - TAIL_BYTES);
1789
1798
  return parseJsonl(completeLines(await readSlice(file, start, Math.min(size, TAIL_BYTES)), start > 0));
@@ -1824,7 +1833,7 @@ async function discoverLocalSessions(options = {}) {
1824
1833
  const [claude, codex, names] = await Promise.all([
1825
1834
  claudeCandidates(homes.claudeProjects),
1826
1835
  codexCandidates(homes.codex),
1827
- readCodexIndex(homes.codex.map((h) => h.index))
1836
+ codexNames(homes, options.appTitles)
1828
1837
  ]);
1829
1838
  const candidates = [...claude, ...codex].sort((a, b) => b.mtimeMs - a.mtimeMs);
1830
1839
  const processes = await listProcesses();
@@ -1846,7 +1855,12 @@ async function findSessionFile(provider, providerSessionId, homes = defaultProvi
1846
1855
  if (!/^[0-9a-f-]{36}$/i.test(providerSessionId)) return null;
1847
1856
  const candidates = provider === "claude" ? await claudeCandidates(homes.claudeProjects) : await codexCandidates(homes.codex);
1848
1857
  const hits = candidates.filter((c) => c.id === providerSessionId).sort((a, b) => b.mtimeMs - a.mtimeMs);
1849
- return hits[0]?.file ?? null;
1858
+ if (hits[0]) return hits[0].file;
1859
+ if (provider === "claude" && homes.claudeVault) {
1860
+ const copy = import_node_path5.default.join(homes.claudeVault, providerSessionId, "transcript.jsonl");
1861
+ if ((await import_promises4.default.stat(copy).catch(() => null))?.isFile()) return copy;
1862
+ }
1863
+ return null;
1850
1864
  }
1851
1865
  var HISTORY_BUDGET = 24 * 1024 * 1024;
1852
1866
  var MAX_HISTORY_BUDGET = 256 * 1024 * 1024;
@@ -1900,8 +1914,8 @@ async function listSessionFiles(homes = defaultProviderHomes()) {
1900
1914
  ]);
1901
1915
  return [...claude, ...codex].sort((a, b) => b.mtimeMs - a.mtimeMs);
1902
1916
  }
1903
- async function describeSessionFile(candidate, codexNames) {
1904
- return (candidate.provider === "claude" ? describeClaude(candidate) : describeCodex(candidate, codexNames)).catch(
1917
+ async function describeSessionFile(candidate, codexNames2) {
1918
+ return (candidate.provider === "claude" ? describeClaude(candidate) : describeCodex(candidate, codexNames2)).catch(
1905
1919
  () => null
1906
1920
  );
1907
1921
  }
@@ -2096,12 +2110,12 @@ async function searchLocalChats(query, options = {}) {
2096
2110
  const raw = JSON.stringify(needle).slice(1, -1);
2097
2111
  const limit = Math.max(1, Math.min(options.limit ?? 30, 100));
2098
2112
  const deadline = Date.now() + (options.budgetMs ?? 3500);
2099
- const names = await readCodexIndex(homes.codex.map((h) => h.index));
2113
+ const names = await codexNames(homes, options.appTitles);
2100
2114
  const hits = [];
2101
2115
  let i = cursor;
2102
2116
  for (; i < files.length && hits.length < limit && Date.now() < deadline; i += 1) {
2103
2117
  const candidate = files[i];
2104
- const named = candidate.provider === "codex" ? names.get(candidate.id) : void 0;
2118
+ const named = options.appTitles?.get(`${candidate.provider}:${candidate.id}`) ?? (candidate.provider === "codex" ? names.get(candidate.id) : void 0);
2105
2119
  const nameHit = named !== void 0 && named.toLowerCase().includes(needle);
2106
2120
  const scanned = nameHit ? null : await scanFile(candidate.provider, candidate.file, needle, raw, deadline);
2107
2121
  if (scanned === TIMED_OUT) {
@@ -2135,6 +2149,11 @@ async function searchLocalChats(query, options = {}) {
2135
2149
  function titleHintKey(provider, sessionId) {
2136
2150
  return `${provider}:${sessionId}`;
2137
2151
  }
2152
+ function appTitlesOf(hints) {
2153
+ const out = /* @__PURE__ */ new Map();
2154
+ for (const [key, hint] of hints) out.set(key, hint.title);
2155
+ return out;
2156
+ }
2138
2157
 
2139
2158
  // src/engine/title-hints.ts
2140
2159
  function overlayHint(chat, hints, expected) {
@@ -2445,7 +2464,7 @@ function describeAuthStatus(provider, probe) {
2445
2464
  async function createCodingSessionsEngine(options = {}) {
2446
2465
  const dataDir = options.dataDir ?? import_node_path9.default.join(import_node_os5.default.homedir(), ".matrx", "coding-sessions");
2447
2466
  const stateDir = import_node_path9.default.join(dataDir, "paperclip-state");
2448
- const homes = options.homes ?? defaultProviderHomes();
2467
+ let homes = options.homes ?? defaultProviderHomes();
2449
2468
  const index = new RunIndex(dataDir);
2450
2469
  await index.load();
2451
2470
  const preferences = new PreferenceStore(dataDir);
@@ -2527,6 +2546,11 @@ async function createCodingSessionsEngine(options = {}) {
2527
2546
  }
2528
2547
  const titleHints = options.titleHints;
2529
2548
  const hintsNow = () => titleHints?.current() ?? null;
2549
+ const appTitles = () => {
2550
+ if (!titleHints) return void 0;
2551
+ const hints = hintsNow();
2552
+ return hints ? appTitlesOf(hints) : /* @__PURE__ */ new Map();
2553
+ };
2530
2554
  const unsubscribeHints = titleHints?.subscribe(() => emit({ type: "titles" })) ?? null;
2531
2555
  const withHintTitle = (session) => {
2532
2556
  const hint = session.providerSessionId ? hintsNow()?.get(titleHintKey(session.provider, session.providerSessionId)) : void 0;
@@ -3001,7 +3025,7 @@ async function createCodingSessionsEngine(options = {}) {
3001
3025
  );
3002
3026
  },
3003
3027
  async discoverSessions(opts) {
3004
- const read = await discoverLocalSessions({ limit: opts?.limit, homes, excludePids: ourPids() });
3028
+ const read = await discoverLocalSessions({ limit: opts?.limit, homes, excludePids: ourPids(), appTitles: appTitles() });
3005
3029
  const hints = hintsNow();
3006
3030
  const found = read.map((chat) => overlayHint(chat, hints, titleHints !== void 0));
3007
3031
  if (backups) {
@@ -3039,6 +3063,17 @@ async function createCodingSessionsEngine(options = {}) {
3039
3063
  }))
3040
3064
  );
3041
3065
  },
3066
+ async titleStatus() {
3067
+ if (!titleHints) return { owner: false, pending: false, problem: null };
3068
+ const problem = titleHints.problem?.() ?? null;
3069
+ return { owner: true, pending: hintsNow() === null && problem === null, problem };
3070
+ },
3071
+ async retryTitles() {
3072
+ titleHints?.retry?.();
3073
+ },
3074
+ setHomes(next) {
3075
+ homes = next;
3076
+ },
3042
3077
  async listSessions() {
3043
3078
  return index.sessions().map(withHintTitle);
3044
3079
  },
@@ -3215,7 +3250,7 @@ async function createCodingSessionsEngine(options = {}) {
3215
3250
  return all;
3216
3251
  },
3217
3252
  async searchChats(query, opts) {
3218
- const result = await searchLocalChats(String(query ?? ""), { homes, cursor: opts?.cursor, limit: opts?.limit });
3253
+ const result = await searchLocalChats(String(query ?? ""), { homes, cursor: opts?.cursor, limit: opts?.limit, appTitles: appTitles() });
3219
3254
  const hints = hintsNow();
3220
3255
  return { ...result, hits: result.hits.map((hit) => ({ ...hit, title: hints?.get(titleHintKey(hit.provider, hit.providerSessionId))?.title ?? hit.title })) };
3221
3256
  },
@@ -3299,6 +3334,8 @@ function openFolderIn(folder, target) {
3299
3334
  var BRIDGE_METHODS = [
3300
3335
  "providers",
3301
3336
  "discoverSessions",
3337
+ "titleStatus",
3338
+ "retryTitles",
3302
3339
  "recentFolders",
3303
3340
  "listSessions",
3304
3341
  "listRuns",