@adhdev/daemon-core 0.9.82-rc.441 → 0.9.82-rc.442

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.441",
3
+ "version": "0.9.82-rc.442",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -46,7 +46,7 @@
46
46
  "author": "vilmire",
47
47
  "license": "AGPL-3.0-or-later",
48
48
  "dependencies": {
49
- "@adhdev/mesh-shared": "0.9.82-rc.441",
49
+ "@adhdev/mesh-shared": "0.9.82-rc.442",
50
50
  "@adhdev/session-host-core": "*",
51
51
  "@agentclientprotocol/sdk": "^0.16.1",
52
52
  "ajv": "^8.20.0",
@@ -264,22 +264,30 @@ function executeSqlite(src: NativeHistorySqliteSource, input: NativeHistoryInput
264
264
 
265
265
  try {
266
266
  const requested = input.providerSessionId || '';
267
- let sessionId: string;
268
- if (requested) {
269
- // Session pin: the caller already bound this instance to a
270
- // specific provider session, so read THAT session directly and
271
- // skip the newest-wins `session_query` entirely. hermes ≥0.14
272
- // spawns a fresh `sessions` row per internal sub-session, so the
273
- // `ORDER BY started_at DESC LIMIT 1` pick drifts to a different
274
- // id on every read. Left unpinned that churns the bound session
275
- // (each re-bind re-hydrates unbounded history daemon
276
- // saturation) and reads completion evidence from the wrong
277
- // session (turn never finalizes). Binding straight to the
278
- // requested id fixes both. Existence is validated below by the
279
- // spec's own `message_query` returning rows for this id, so we
280
- // don't hardcode any schema here.
281
- sessionId = requested;
282
- } else {
267
+ // Resolve the session id the message query runs against. The `requested`
268
+ // pin path is tried first, but a pinned id that has NO rows in the store
269
+ // is not a real session fall back to the newest-session `session_query`
270
+ // instead of returning empty. This is the hermes read_chat gap: hermes
271
+ // never surfaces its own provider session id to the daemon (the spec
272
+ // declares no session-id extraction and the adapter's screen-scrape is
273
+ // codex-only), so the read pipeline falls back to threading the mesh
274
+ // RUNTIME session id through as `providerSessionId`. That runtime id does
275
+ // not exist in ~/.hermes/state.db, so the old unconditional pin path ran
276
+ // `message_query WHERE session_id = '<runtime id>'` 0 rows → null, and
277
+ // the answer (physically present under the real cli session) was never
278
+ // returned. Validating the pin by the spec's own `message_query` keeps
279
+ // this schema-agnostic and only rescues the mis-bound-id case: a genuine
280
+ // discovered pin (codex/claude use jsonl sources and never reach here;
281
+ // any real sqlite pin has rows) still short-circuits on its own rows.
282
+ const resolveMessagesFor = (sessionId: string): any[] | null => {
283
+ if (!sessionId) return null;
284
+ let rows: any[];
285
+ try { rows = db.prepare(src.message_query).all(sessionId); }
286
+ catch { return null; }
287
+ return rows && rows.length > 0 ? rows : null;
288
+ };
289
+
290
+ const resolveNewestSessionId = (): string => {
283
291
  let sessionRow: any;
284
292
  try {
285
293
  // session_query may reference `?` to receive the session's
@@ -299,15 +307,38 @@ function executeSqlite(src: NativeHistorySqliteSource, input: NativeHistoryInput
299
307
  } catch {
300
308
  sessionRow = stmt.get();
301
309
  }
302
- } catch { return null; }
303
- if (!sessionRow) return null;
310
+ } catch { return ''; }
311
+ if (!sessionRow) return '';
304
312
  // First column of the first row is the session id.
305
313
  const sessionIdRaw = Object.values(sessionRow)[0];
306
- sessionId = sessionIdRaw == null ? '' : String(sessionIdRaw);
314
+ return sessionIdRaw == null ? '' : String(sessionIdRaw);
315
+ };
316
+
317
+ let sessionId: string;
318
+ let messageRows: any[] | null;
319
+ if (requested) {
320
+ // Pin path: read the requested session directly and skip the
321
+ // newest-wins `session_query`. hermes ≥0.14 spawns a fresh
322
+ // `sessions` row per internal sub-session, so an unpinned
323
+ // `ORDER BY started_at DESC LIMIT 1` pick drifts to a different id
324
+ // on every read (re-bind churn + reading completion evidence from
325
+ // the wrong session). A pin that resolves rows is authoritative.
326
+ messageRows = resolveMessagesFor(requested);
327
+ if (messageRows) {
328
+ sessionId = requested;
329
+ } else {
330
+ // The pinned id has no rows — it is not a real session in this
331
+ // store (the mis-bound mesh runtime-id case). Recover by letting
332
+ // the spec's own newest-session query self-resolve instead of
333
+ // returning empty.
334
+ sessionId = resolveNewestSessionId();
335
+ messageRows = resolveMessagesFor(sessionId);
336
+ }
337
+ } else {
338
+ sessionId = resolveNewestSessionId();
339
+ messageRows = resolveMessagesFor(sessionId);
307
340
  }
308
341
  if (!sessionId) return null;
309
-
310
- const messageRows: any[] = db.prepare(src.message_query).all(sessionId);
311
342
  if (!messageRows || messageRows.length === 0) return null;
312
343
 
313
344
  const mtime = safeMtimeMs(resolved);