@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.
- package/CHANGELOG.md +21 -0
- package/dist/engine/index.cjs +57 -11
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +1 -1
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/index.js +57 -11
- package/dist/engine/index.js.map +1 -1
- package/dist/{index-29t7n472.d.cts → index-Bbe11HoB.d.cts} +30 -3
- package/dist/{index-29t7n472.d.ts → index-Bbe11HoB.d.ts} +30 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +34 -5
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +15 -1
- package/dist/react/index.d.ts +15 -1
- package/dist/react/index.js +34 -5
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +47 -10
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +47 -10
- package/dist/server/index.js.map +1 -1
- package/dist/sessions/index.cjs +26 -1
- package/dist/sessions/index.cjs.map +1 -1
- package/dist/sessions/index.d.cts +13 -5
- package/dist/sessions/index.d.ts +13 -5
- package/dist/sessions/index.js +26 -1
- package/dist/sessions/index.js.map +1 -1
- package/dist/{titles-eCnA5sY7.d.cts → titles-DjxUb0ji.d.cts} +8 -1
- package/dist/{titles-eCnA5sY7.d.ts → titles-DjxUb0ji.d.ts} +8 -1
- package/dist/ui/assets/index-BqzTnRFe.css +2 -0
- package/dist/ui/assets/{index-SuRr0-D2.js → index-U98AY8r-.js} +17 -17
- package/dist/ui/index.html +2 -2
- package/package.json +4 -4
- 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 >
|
package/dist/engine/index.cjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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]
|
|
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,
|
|
1954
|
-
return (candidate.provider === "claude" ? describeClaude(candidate) : describeCodex(candidate,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
},
|