@yeaft/webchat-agent 0.1.931 → 0.1.933
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 +1 -1
- package/yeaft/dream/output-snapshot.js +59 -0
- package/yeaft/dream/session-wiring.js +6 -1
- package/yeaft/engine.js +32 -1
- package/yeaft/web-bridge.js +51 -0
package/package.json
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dream/output-snapshot.js.
|
|
3
|
+
*
|
|
4
|
+
* Read-only projection of Dream-owned output files for UI observability.
|
|
5
|
+
* The Dream write path remains `runner -> apply -> memory/store`; callers use
|
|
6
|
+
* this module to load the current `memory.md` / `summary.md` contents for a
|
|
7
|
+
* single Yeaft session when emitting status or restoring a switched session.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
|
|
12
|
+
import { readMemory, readSummary } from '../memory/store.js';
|
|
13
|
+
import { readGroupState } from './state.js';
|
|
14
|
+
|
|
15
|
+
export const DREAM_SNAPSHOT_TEXT_LIMIT = 6000;
|
|
16
|
+
|
|
17
|
+
export function truncateDreamText(value, limit = DREAM_SNAPSHOT_TEXT_LIMIT) {
|
|
18
|
+
const text = typeof value === 'string' ? value : '';
|
|
19
|
+
if (text.length <= limit) return { text, truncated: false };
|
|
20
|
+
return { text: text.slice(0, limit), truncated: true };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build the loadable Dream output for one Yeaft session.
|
|
25
|
+
*
|
|
26
|
+
* @param {{ yeaftDir?: string|null }} sessionLike
|
|
27
|
+
* @param {string} sessionId
|
|
28
|
+
* @returns {Promise<object|null>}
|
|
29
|
+
*/
|
|
30
|
+
export async function buildDreamOutputSnapshot(sessionLike, sessionId) {
|
|
31
|
+
if (!sessionId || !sessionLike?.yeaftDir) return null;
|
|
32
|
+
const scope = `group/${sessionId}`;
|
|
33
|
+
const memoryScope = { kind: 'group', id: sessionId };
|
|
34
|
+
const root = join(sessionLike.yeaftDir, 'memory');
|
|
35
|
+
const [memoryRaw, summaryRaw, state] = await Promise.all([
|
|
36
|
+
readMemory(memoryScope, { root }).catch(() => ''),
|
|
37
|
+
readSummary(memoryScope, { root }).catch(() => ''),
|
|
38
|
+
readGroupState(root, sessionId).catch(() => ({
|
|
39
|
+
lastDreamMessageId: null,
|
|
40
|
+
lastDreamAt: null,
|
|
41
|
+
messageCount: 0,
|
|
42
|
+
})),
|
|
43
|
+
]);
|
|
44
|
+
const memory = truncateDreamText(memoryRaw);
|
|
45
|
+
const summary = truncateDreamText(summaryRaw);
|
|
46
|
+
return {
|
|
47
|
+
scope,
|
|
48
|
+
sessionId,
|
|
49
|
+
loadedAt: new Date().toISOString(),
|
|
50
|
+
lastDreamAt: state?.lastDreamAt || null,
|
|
51
|
+
lastDreamMessageId: state?.lastDreamMessageId || null,
|
|
52
|
+
messageCount: Number.isFinite(state?.messageCount) ? state.messageCount : 0,
|
|
53
|
+
hasOutput: !!(memoryRaw || summaryRaw),
|
|
54
|
+
memoryText: memory.text,
|
|
55
|
+
memoryTruncated: memory.truncated,
|
|
56
|
+
summaryText: summary.text,
|
|
57
|
+
summaryTruncated: summary.truncated,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -338,7 +338,7 @@ export function createV2DreamScheduler(session) {
|
|
|
338
338
|
...buildRunDreamOpts(session, onProgress),
|
|
339
339
|
manual: !!opts.manual,
|
|
340
340
|
scopeFilter: Array.isArray(opts.scopeFilter) ? opts.scopeFilter : undefined,
|
|
341
|
-
}).then((result) => {
|
|
341
|
+
}).then(async (result) => {
|
|
342
342
|
// Bug 2: emit turn_close when the dream pass completes.
|
|
343
343
|
result.trigger = opts.manual ? 'manual' : 'auto';
|
|
344
344
|
if (session._dreamActiveGroupId && !result.sessionId) result.sessionId = session._dreamActiveGroupId;
|
|
@@ -370,6 +370,11 @@ export function createV2DreamScheduler(session) {
|
|
|
370
370
|
if (typeof session._dreamProgressSink === 'function') {
|
|
371
371
|
session._dreamProgressSink(turnClose);
|
|
372
372
|
}
|
|
373
|
+
if (typeof session._dreamResultSink === 'function') {
|
|
374
|
+
try {
|
|
375
|
+
await session._dreamResultSink(result);
|
|
376
|
+
} catch { /* dream result visibility must never fail the scheduler */ }
|
|
377
|
+
}
|
|
373
378
|
return result;
|
|
374
379
|
});
|
|
375
380
|
};
|
package/yeaft/engine.js
CHANGED
|
@@ -612,6 +612,7 @@ export class Engine {
|
|
|
612
612
|
* ownVpId: string|null,
|
|
613
613
|
* scopes: string[],
|
|
614
614
|
* snapshotBlock: string,
|
|
615
|
+
* residentEntries: Array<{scope:string, summary:string}>,
|
|
615
616
|
* } | null}
|
|
616
617
|
*/
|
|
617
618
|
#prepareAms(args) {
|
|
@@ -650,7 +651,7 @@ export class Engine {
|
|
|
650
651
|
vpId: ownVpId,
|
|
651
652
|
});
|
|
652
653
|
|
|
653
|
-
return { ams, groupKey, ownVpId, scopes, snapshotBlock };
|
|
654
|
+
return { ams, groupKey, ownVpId, scopes, snapshotBlock, residentEntries };
|
|
654
655
|
}
|
|
655
656
|
|
|
656
657
|
/**
|
|
@@ -1475,6 +1476,25 @@ export class Engine {
|
|
|
1475
1476
|
memoryInjection = amsContext.snapshotBlock;
|
|
1476
1477
|
}
|
|
1477
1478
|
|
|
1479
|
+
// Diagnostic payload for the Dream debug panel. The full AMS Resident
|
|
1480
|
+
// layer can include user and per-VP summaries, but the browser-facing
|
|
1481
|
+
// Dream prompt-load view only needs to prove the active group Dream
|
|
1482
|
+
// summary entered `system_prompt.memory`. Keep the payload scoped to the
|
|
1483
|
+
// exact group resident to avoid leaking unrelated resident summaries into
|
|
1484
|
+
// frontend state. The full system prompt remains visible in the existing
|
|
1485
|
+
// debug-only system-prompt panel.
|
|
1486
|
+
const activeGroupDreamScope = sessionId ? `group/${sessionId}` : null;
|
|
1487
|
+
const dreamResidentLoaded = amsContext && Array.isArray(amsContext.residentEntries)
|
|
1488
|
+
? amsContext.residentEntries
|
|
1489
|
+
.filter(e => e && e.scope === activeGroupDreamScope && e.summary)
|
|
1490
|
+
.map(e => ({
|
|
1491
|
+
scope: e.scope,
|
|
1492
|
+
summary: String(e.summary).slice(0, 4000),
|
|
1493
|
+
truncated: String(e.summary).length > 4000,
|
|
1494
|
+
source: 'resident-summary',
|
|
1495
|
+
}))
|
|
1496
|
+
: [];
|
|
1497
|
+
|
|
1478
1498
|
// ─── Active Scope (DESIGN-PROMPT §3 ④) ──────────────────────
|
|
1479
1499
|
// Structured per-turn scope summary: group + vp + envelope routing
|
|
1480
1500
|
// info. Long-form scope content lives in AMS — this block carries
|
|
@@ -1652,6 +1672,17 @@ export class Engine {
|
|
|
1652
1672
|
};
|
|
1653
1673
|
}
|
|
1654
1674
|
|
|
1675
|
+
if (dreamResidentLoaded.length > 0) {
|
|
1676
|
+
yield {
|
|
1677
|
+
type: 'dream_memory_loaded',
|
|
1678
|
+
turnId: queryTurnId,
|
|
1679
|
+
vpId: queryVpId,
|
|
1680
|
+
sessionId: sessionId || null,
|
|
1681
|
+
loadedInto: 'system_prompt.memory',
|
|
1682
|
+
resident: dreamResidentLoaded,
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1655
1686
|
const toolDefs = this.#getToolDefs();
|
|
1656
1687
|
let turnNumber = 0;
|
|
1657
1688
|
let continueTurns = 0; // auto-continue counter
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { join } from 'node:path';
|
|
22
22
|
import { existsSync } from 'node:fs';
|
|
23
23
|
import { randomUUID } from 'node:crypto';
|
|
24
|
+
import { buildDreamOutputSnapshot } from './dream/output-snapshot.js';
|
|
24
25
|
import { Engine } from './engine.js';
|
|
25
26
|
import { loadSession } from './session.js';
|
|
26
27
|
import { loadConfig } from './config.js';
|
|
@@ -112,6 +113,14 @@ export function __testSetThreadClassifier(fn) {
|
|
|
112
113
|
*/
|
|
113
114
|
const inflightScopedDreamGroups = new Set();
|
|
114
115
|
|
|
116
|
+
async function sendDreamSnapshotForSession(sessionId, extra = {}) {
|
|
117
|
+
const snapshot = await buildDreamOutputSnapshot(session, sessionId);
|
|
118
|
+
if (!snapshot) return null;
|
|
119
|
+
sendYeaftEvent({ type: 'yeaft_dream_snapshot', ...extra, snapshot }, { sessionId });
|
|
120
|
+
return snapshot;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
115
124
|
/**
|
|
116
125
|
* Single in-flight AbortController for legacy 1:1 chat. A new 1:1 user message
|
|
117
126
|
* cancels the prior round (if any).
|
|
@@ -1865,6 +1874,30 @@ export function installYeaftRuntimeBridge(s) {
|
|
|
1865
1874
|
} catch { /* never let event delivery throw */ }
|
|
1866
1875
|
};
|
|
1867
1876
|
|
|
1877
|
+
// Auto dream runs are triggered by the scheduler / nudges, not by the
|
|
1878
|
+
// manual `handleYeaftDreamTrigger` path. Without this terminal sink the UI
|
|
1879
|
+
// only saw progress debug events and could not restore the final dream
|
|
1880
|
+
// output after switching sessions. Manual runs keep using their explicit
|
|
1881
|
+
// handler below to avoid duplicate terminal events.
|
|
1882
|
+
s._dreamResultSink = async (result = {}) => {
|
|
1883
|
+
if (result?.trigger !== 'auto') return;
|
|
1884
|
+
const normalized = normalizeDreamResult(result);
|
|
1885
|
+
const processed = Array.isArray(result.groups)
|
|
1886
|
+
? result.groups.filter(g => g && g.status === 'processed' && g.sessionId)
|
|
1887
|
+
: [];
|
|
1888
|
+
for (const group of processed) {
|
|
1889
|
+
const sessionId = group.sessionId;
|
|
1890
|
+
const snapshot = await buildDreamOutputSnapshot(session, sessionId).catch(() => null);
|
|
1891
|
+
sendToServer({
|
|
1892
|
+
type: 'yeaft_dream_result',
|
|
1893
|
+
sessionId,
|
|
1894
|
+
...result,
|
|
1895
|
+
...normalized,
|
|
1896
|
+
snapshot,
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
};
|
|
1900
|
+
|
|
1868
1901
|
// Wire the post-compact WS sink. Compactor is constructed in
|
|
1869
1902
|
// session.js with a no-op sink; bridge owns `sendYeaftEvent` /
|
|
1870
1903
|
// `yeaftConversationId`, so the sink is wired here once a session is
|
|
@@ -2179,6 +2212,17 @@ function handleEngineEvent(event, hctx) {
|
|
|
2179
2212
|
}, envelope);
|
|
2180
2213
|
break;
|
|
2181
2214
|
|
|
2215
|
+
case 'dream_memory_loaded':
|
|
2216
|
+
sendYeaftEvent({
|
|
2217
|
+
type: 'dream_memory_loaded',
|
|
2218
|
+
turnId: event.turnId,
|
|
2219
|
+
vpId: event.vpId || null,
|
|
2220
|
+
sessionId: event.sessionId || null,
|
|
2221
|
+
loadedInto: event.loadedInto || 'system_prompt.memory',
|
|
2222
|
+
resident: Array.isArray(event.resident) ? event.resident : [],
|
|
2223
|
+
}, envelope);
|
|
2224
|
+
break;
|
|
2225
|
+
|
|
2182
2226
|
case 'memory_adjust':
|
|
2183
2227
|
sendYeaftEvent({
|
|
2184
2228
|
type: 'memory_adjust',
|
|
@@ -3594,6 +3638,9 @@ export async function handleYeaftDreamTrigger(msg = {}) {
|
|
|
3594
3638
|
: await session.dreamScheduler.triggerDreamNow();
|
|
3595
3639
|
|
|
3596
3640
|
const normalized = normalizeDreamResult(result);
|
|
3641
|
+
const snapshot = sessionId
|
|
3642
|
+
? await buildDreamOutputSnapshot(session, sessionId).catch(() => null)
|
|
3643
|
+
: null;
|
|
3597
3644
|
|
|
3598
3645
|
// Spread `result` FIRST so normalized fields (success, skipped,
|
|
3599
3646
|
// skippedReason, groupsProcessed, groupsSkipped, targetsApplied,
|
|
@@ -3616,6 +3663,7 @@ export async function handleYeaftDreamTrigger(msg = {}) {
|
|
|
3616
3663
|
...tag,
|
|
3617
3664
|
...result,
|
|
3618
3665
|
...normalized,
|
|
3666
|
+
...(snapshot ? { snapshot } : {}),
|
|
3619
3667
|
});
|
|
3620
3668
|
} catch (err) {
|
|
3621
3669
|
const error = err?.message || String(err);
|
|
@@ -3829,6 +3877,9 @@ export async function handleYeaftLoadHistory(msg) {
|
|
|
3829
3877
|
yeaftDir: ctx.CONFIG?.yeaftDir || null,
|
|
3830
3878
|
});
|
|
3831
3879
|
sendSessionSnapshotBroadcast();
|
|
3880
|
+
if (sessionId) {
|
|
3881
|
+
await sendDreamSnapshotForSession(sessionId, { trigger: 'load_history' }).catch(() => null);
|
|
3882
|
+
}
|
|
3832
3883
|
// vp-status: replay the authoritative table on reconnect so a refreshed
|
|
3833
3884
|
// frontend doesn't have to wait for the next transition to learn each
|
|
3834
3885
|
// VP's current state.
|