@adhdev/daemon-core 0.9.82-rc.476 → 0.9.82-rc.477
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/dist/index.js +162 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +162 -27
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-event-forwarding.d.ts +8 -0
- package/dist/providers/cli-provider-instance.d.ts +19 -0
- package/dist/providers/native-history/antigravity-claim-registry.d.ts +17 -6
- package/dist/sessions/registry.d.ts +20 -0
- package/package.json +3 -3
- package/src/commands/chat-commands-read.ts +42 -10
- package/src/mesh/mesh-event-forwarding.ts +38 -0
- package/src/providers/cli-provider-instance.ts +148 -5
- package/src/providers/native-history/antigravity-claim-registry.ts +19 -12
- package/src/providers/native-history/dispatcher.ts +39 -12
- package/src/sessions/registry.ts +38 -0
|
@@ -314,24 +314,51 @@ function resolveAntigravityPath(
|
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
// (2) brain/<uuid>/.system_generated/logs/transcript.jsonl (legacy full source).
|
|
317
|
-
// Only bind to a brain transcript that is NON-EMPTY
|
|
318
|
-
//
|
|
319
|
-
// lives in the per-session .db), so an empty transcript here would
|
|
320
|
-
// otherwise shadow the .db fallback below and return no messages. Skip
|
|
321
|
-
// empty transcripts so an unbound read still reaches the .db. Exclude any
|
|
317
|
+
// Only bind to a brain transcript that is NON-EMPTY (antigravity may leave
|
|
318
|
+
// it 0 bytes when the real data lives in the per-session .db). Exclude any
|
|
322
319
|
// brain conversation already claimed by a DIFFERENT live session.
|
|
320
|
+
//
|
|
321
|
+
// Selection MUST mirror pickUnboundConversationDb (step 3): when a spawn
|
|
322
|
+
// floor is known, a brain dir born at/after the floor is THIS session's own,
|
|
323
|
+
// and among those the OLDEST-created wins (the store created first after the
|
|
324
|
+
// session started). The previous newest-by-mtime sort silently mis-bound: in
|
|
325
|
+
// a MAGI panel every co-located antigravity session (coordinator + replicas)
|
|
326
|
+
// has a non-empty brain transcript, and the replica that finished its turn
|
|
327
|
+
// last has the newest mtime — so a coordinator's read grabbed the replica's
|
|
328
|
+
// transcript here, BEFORE step 3's floor-aware pick could run. That is the
|
|
329
|
+
// antigravity coordinator↔replica crosswire, and it lives in THIS step, not
|
|
330
|
+
// step 3. Keep newest-by-mtime only in the floor-less legacy path.
|
|
323
331
|
const brainRoot = path.join(agyRoot, 'brain');
|
|
324
332
|
if (fs.existsSync(brainRoot)) {
|
|
325
333
|
const cutoff = spawnAwareCutoff(sessionStartedAtMs);
|
|
326
|
-
const
|
|
334
|
+
const nonEmptyBrain = (uuid: string, p: string): string | null => {
|
|
335
|
+
const t = path.join(p, '.system_generated', 'logs', 'transcript.jsonl');
|
|
336
|
+
return (fs.existsSync(t) && safeSize(t) > 0) ? t : null;
|
|
337
|
+
};
|
|
338
|
+
const all = fs.readdirSync(brainRoot, { withFileTypes: true })
|
|
327
339
|
.filter(e => e.isDirectory() && isUuidLikeSessionId(e.name))
|
|
328
340
|
.filter(e => !isAntigravityConversationClaimedByOther(e.name, owner))
|
|
329
|
-
.map(e =>
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
341
|
+
.map(e => {
|
|
342
|
+
const p = path.join(brainRoot, e.name);
|
|
343
|
+
return { uuid: e.name, p, mtime: safeMtime(p), birth: safeBirthtime(p) };
|
|
344
|
+
})
|
|
345
|
+
.filter(e => e.mtime >= cutoff);
|
|
346
|
+
let ordered: Array<{ uuid: string; p: string }> = [];
|
|
347
|
+
if (sessionStartedAtMs > 0) {
|
|
348
|
+
// Floor branch: this session's own = born at/after (floor - grace),
|
|
349
|
+
// oldest-birth first. Mirrors pickUnboundConversationDb exactly so the
|
|
350
|
+
// two steps can never resolve DIFFERENT conversations for one session.
|
|
351
|
+
const floor = sessionStartedAtMs - AGY_SPAWN_CLAIM_GRACE_MS;
|
|
352
|
+
ordered = all
|
|
353
|
+
.filter(e => (e.birth > 0 ? e.birth : e.mtime) >= floor)
|
|
354
|
+
.sort((a, b) => (a.birth || a.mtime) - (b.birth || b.mtime));
|
|
355
|
+
} else {
|
|
356
|
+
// Floor-less legacy/unpinned discovery: newest-by-mtime (single-session).
|
|
357
|
+
ordered = [...all].sort((a, b) => b.mtime - a.mtime);
|
|
358
|
+
}
|
|
359
|
+
for (const e of ordered) {
|
|
360
|
+
const t = nonEmptyBrain(e.uuid, e.p);
|
|
361
|
+
if (t) {
|
|
335
362
|
if (owner) claimAntigravityConversation(e.uuid, owner);
|
|
336
363
|
return t;
|
|
337
364
|
}
|
package/src/sessions/registry.ts
CHANGED
|
@@ -14,6 +14,19 @@ export interface SessionRuntimeTarget {
|
|
|
14
14
|
/** Wall clock at register time. native-history readers use it as a
|
|
15
15
|
* cutoff so a fresh session can't show records from a prior one. */
|
|
16
16
|
spawnedAtMs?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Authoritative provider-native conversation id for this session (SSOT).
|
|
19
|
+
* For providers that expose a session id on the CLI (codex/claude/hermes)
|
|
20
|
+
* this equals that id. For antigravity — which takes no --session-id — this
|
|
21
|
+
* is the on-disk conversations/<uuid>.db basename, discovered by the
|
|
22
|
+
* native-history dispatcher and written back here via setProviderSessionId
|
|
23
|
+
* the first time it resolves. Every downstream reader (read_chat, the
|
|
24
|
+
* completion probe, the dashboard) should prefer this over re-deriving the
|
|
25
|
+
* conversation by spawn-floor/mtime heuristics — that re-derivation is the
|
|
26
|
+
* source of the antigravity conversation crosswire/theft class. Empty until
|
|
27
|
+
* the first successful native read binds it.
|
|
28
|
+
*/
|
|
29
|
+
providerSessionId?: string;
|
|
17
30
|
}
|
|
18
31
|
|
|
19
32
|
export class SessionRegistry {
|
|
@@ -23,7 +36,15 @@ export class SessionRegistry {
|
|
|
23
36
|
private readonly byParentSessionId = new Map<string, Set<string>>();
|
|
24
37
|
|
|
25
38
|
register(target: SessionRuntimeTarget): void {
|
|
39
|
+
// Preserve an already-resolved conversation binding across a
|
|
40
|
+
// re-register (attach-restore, meta refresh): the caller rarely knows
|
|
41
|
+
// the antigravity conv uuid at register time, so a plain replace would
|
|
42
|
+
// drop the SSOT binding and force a fresh (crosswire-prone) re-resolve.
|
|
43
|
+
const priorProviderSessionId = this.bySessionId.get(target.sessionId)?.providerSessionId;
|
|
26
44
|
this.unregister(target.sessionId);
|
|
45
|
+
if (priorProviderSessionId && !target.providerSessionId) {
|
|
46
|
+
target = { ...target, providerSessionId: priorProviderSessionId };
|
|
47
|
+
}
|
|
27
48
|
this.bySessionId.set(target.sessionId, target);
|
|
28
49
|
if (target.cdpManagerKey) this.addIndex(this.byManagerKey, target.cdpManagerKey, target.sessionId);
|
|
29
50
|
if (target.instanceKey) this.addIndex(this.byInstanceKey, target.instanceKey, target.sessionId);
|
|
@@ -35,6 +56,23 @@ export class SessionRegistry {
|
|
|
35
56
|
return this.bySessionId.get(sessionId);
|
|
36
57
|
}
|
|
37
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Record the authoritative provider-native conversation id for a session
|
|
61
|
+
* (SSOT). Idempotent; a no-op when the session is unknown or the value is
|
|
62
|
+
* empty or unchanged. Never overwrites a known binding with an empty one.
|
|
63
|
+
* Returns whether the stored value changed.
|
|
64
|
+
*/
|
|
65
|
+
setProviderSessionId(sessionId: string | undefined | null, providerSessionId: string | undefined | null): boolean {
|
|
66
|
+
const sid = typeof sessionId === 'string' ? sessionId.trim() : '';
|
|
67
|
+
const value = typeof providerSessionId === 'string' ? providerSessionId.trim() : '';
|
|
68
|
+
if (!sid || !value) return false;
|
|
69
|
+
const target = this.bySessionId.get(sid);
|
|
70
|
+
if (!target) return false;
|
|
71
|
+
if (target.providerSessionId === value) return false;
|
|
72
|
+
target.providerSessionId = value;
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
38
76
|
unregister(sessionId: string | undefined | null): void {
|
|
39
77
|
if (!sessionId) return;
|
|
40
78
|
const target = this.bySessionId.get(sessionId);
|