@yeaft/webchat-agent 0.1.631 → 0.1.633

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": "@yeaft/webchat-agent",
3
- "version": "0.1.631",
3
+ "version": "0.1.633",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/unify/config.js CHANGED
@@ -217,8 +217,8 @@ function loadLegacyConfig(dir, overrides) {
217
217
  maxContinueTurns: overrides.maxContinueTurns ?? fileConfig.maxContinueTurns ?? DEFAULTS.maxContinueTurns,
218
218
  // task-318: legacy path never had the `unify` section — defaults.
219
219
  unify: normaliseUnifySection(null),
220
- // DESIGN-v2 feature flag opt-in via override only on legacy path.
221
- memoryV2: !!overrides.memoryV2,
220
+ // DESIGN-v2 feature flag. Default true (PR-E flipped). Override wins.
221
+ memoryV2: overrides.memoryV2 !== undefined ? !!overrides.memoryV2 : true,
222
222
  providers: null,
223
223
  primaryModel: null,
224
224
  fastModel: null,
@@ -316,11 +316,12 @@ export function loadConfig(overrides = {}) {
316
316
  unify: normaliseUnifySection(jsonConfig.unify),
317
317
 
318
318
  // DESIGN-v2 feature flag. When true the engine routes recall through
319
- // memory/recall-v2.js (per-scope memory.md + summary.md) and reads from
320
- // the v2 store layout. Defaults to false during the rollout; PR-E flips
321
- // the default and deletes the legacy paths.
319
+ // memory/recall-v2.js (per-scope memory.md + summary.md) and the
320
+ // session wires the v2 dream pipeline (dream-v2/runner.js). PR-E
321
+ // flipped the default to true; users who need the legacy R6 paths
322
+ // can opt out via `"memoryV2": false` in ~/.yeaft/config.json.
322
323
  memoryV2: overrides.memoryV2 !== undefined ? !!overrides.memoryV2
323
- : (jsonConfig.memoryV2 !== undefined ? !!jsonConfig.memoryV2 : false),
324
+ : (jsonConfig.memoryV2 !== undefined ? !!jsonConfig.memoryV2 : true),
324
325
 
325
326
  // Legacy fields (null when using config.json)
326
327
  apiKey: overrides.apiKey || null,
@@ -0,0 +1,171 @@
1
+ /**
2
+ * dream-v2/session-wiring.js — DESIGN-v2 §13 wire-up for session.js.
3
+ *
4
+ * Bridges the framework-agnostic `runDream` orchestrator (dream-v2/runner.js)
5
+ * to the live yeaft session: groups store, conversation log, LLM adapter,
6
+ * and the engine's progress event sink.
7
+ *
8
+ * The dream pipeline only activates when `config.memoryV2 === true`. When
9
+ * the flag is off, this module is a no-op — the legacy R6 dream-scheduler
10
+ * keeps running as before (session.js still wires it).
11
+ */
12
+
13
+ import { join } from 'path';
14
+ import { runDream } from './runner.js';
15
+ import { createDreamScheduler } from './schedule.js';
16
+ import { listGroups, openGroup } from '../groups/group-store.js';
17
+
18
+ /**
19
+ * Build the per-call options for runDream. Pure: takes a session and returns
20
+ * the closures runDream needs (listGroups, countMessages, loadGroupDiff, etc.).
21
+ *
22
+ * @param {Object} session — the live Session object from loadSession()
23
+ * @param {(event: object) => void} [onProgress]
24
+ * @returns {Object}
25
+ */
26
+ export function buildRunDreamOpts(session, onProgress) {
27
+ const yeaftDir = session.yeaftDir;
28
+ const memoryRoot = join(yeaftDir, 'memory');
29
+ const groupsRoot = join(yeaftDir, 'groups');
30
+
31
+ return {
32
+ root: memoryRoot,
33
+ llm: makeLlm(session),
34
+ listGroups: async () => {
35
+ try { return listGroups(groupsRoot).map(g => g.id); }
36
+ catch { return []; }
37
+ },
38
+ countMessages: async (gid) => {
39
+ try {
40
+ const h = openGroup(groupsRoot, gid);
41
+ let n = 0;
42
+ for (const _m of h.streamMessages()) n += 1;
43
+ return n;
44
+ } catch { return 0; }
45
+ },
46
+ loadGroupDiff: async (gid, sinceId) => {
47
+ try {
48
+ const h = openGroup(groupsRoot, gid);
49
+ const out = [];
50
+ let started = !sinceId;
51
+ for (const m of h.streamMessages()) {
52
+ if (!started) {
53
+ if (m.id === sinceId) started = true;
54
+ continue;
55
+ }
56
+ out.push(translateGroupMessage(m));
57
+ }
58
+ return out;
59
+ } catch { return []; }
60
+ },
61
+ loadOverlapPreamble: async (gid, beforeId, n) => {
62
+ try {
63
+ const h = openGroup(groupsRoot, gid);
64
+ const buf = [];
65
+ for (const m of h.streamMessages()) {
66
+ if (m.id === beforeId) break;
67
+ buf.push(m);
68
+ }
69
+ return buf.slice(-n).map(translateGroupMessage);
70
+ } catch { return []; }
71
+ },
72
+ onProgress,
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Translate a group-store message record (id, from, role, text, ...) into
78
+ * the shape runDream expects (id, role, body, vpId, author, featureId).
79
+ *
80
+ * @param {Object} m
81
+ */
82
+ function translateGroupMessage(m) {
83
+ const role = m.role || (m.from === 'user' ? 'user' : 'assistant');
84
+ const out = {
85
+ id: m.id,
86
+ role,
87
+ body: m.text || '',
88
+ };
89
+ if (role === 'assistant' && m.from && m.from !== 'user') {
90
+ out.vpId = m.from;
91
+ }
92
+ if (m.meta && typeof m.meta === 'object' && m.meta.featureId) {
93
+ out.featureId = m.meta.featureId;
94
+ }
95
+ return out;
96
+ }
97
+
98
+ /**
99
+ * Build the LLM callable that runDream's triage/apply prompts use.
100
+ *
101
+ * @param {Object} session
102
+ */
103
+ function makeLlm(session) {
104
+ return async ({ pass, prompt, system }) => {
105
+ const adapter = session.adapter;
106
+ const model = session.config?.fastModelId || session.config?.model;
107
+ if (!adapter || typeof adapter.call !== 'function') {
108
+ throw new Error(`dream-v2: no adapter.call available (pass=${pass})`);
109
+ }
110
+ const r = await adapter.call({
111
+ model,
112
+ system: system || `You are the dream pipeline — pass: ${pass}.`,
113
+ messages: [{ role: 'user', content: prompt }],
114
+ maxTokens: 2048,
115
+ });
116
+ return (r && r.text) ? r.text : '';
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Create a v2 dream scheduler bound to a session and wire its progress
122
+ * events into the engine's sub-agent event sink (web-bridge translates
123
+ * `dream_progress` into `unify_output` for the debug panel).
124
+ *
125
+ * @param {Object} session
126
+ * @returns {Object} — scheduler (start/stop/triggerNow)
127
+ */
128
+ export function createV2DreamScheduler(session) {
129
+ const onProgress = (evt) => {
130
+ try {
131
+ const sink = session.engine?.subAgentEventSink || null;
132
+ // We don't have a sub-agent id; emit on the engine's standard
133
+ // event channel instead. session.engine exposes setSubAgentEventSink
134
+ // for nested events; for top-level dream, we route through trace.
135
+ if (typeof session.trace?.event === 'function') {
136
+ session.trace.event('dream_progress', evt);
137
+ }
138
+ if (typeof session._dreamProgressSink === 'function') {
139
+ session._dreamProgressSink(evt);
140
+ }
141
+ // Best-effort console for debug builds.
142
+ if (session.config?.debug) {
143
+ // eslint-disable-next-line no-console
144
+ console.log('[dream-v2]', evt);
145
+ }
146
+ } catch { /* never let progress reporting kill the run */ }
147
+ };
148
+
149
+ const run = (opts = {}) => runDream({
150
+ ...buildRunDreamOpts(session, onProgress),
151
+ manual: !!opts.manual,
152
+ scopeFilter: Array.isArray(opts.scopeFilter) ? opts.scopeFilter : undefined,
153
+ });
154
+
155
+ const v2 = createDreamScheduler({
156
+ run,
157
+ logger: session.config?.debug ? console : undefined,
158
+ });
159
+ // Auto-start the timer.
160
+ v2.start();
161
+ // Adapter shim: legacy callers (web-bridge) call .noteUserMessage() and
162
+ // .triggerDreamNow() / .shutdown(). Map them onto the v2 API.
163
+ return {
164
+ noteUserMessage() { /* v2 doesn't gate on user-message count */ },
165
+ triggerDreamNow() { return v2.triggerNow(); },
166
+ shutdown() { v2.stop(); },
167
+ get isRunning() { return v2.isRunning(); },
168
+ // Preserve direct access for tests.
169
+ _v2: v2,
170
+ };
171
+ }
package/unify/session.js CHANGED
@@ -35,6 +35,7 @@ import { createDispatcher } from './pipeline/dispatcher.js';
35
35
  import { ensureDefaultGroupIfEmpty } from './groups/group-crud.js';
36
36
  import { seedDefaultVps } from './vp/seed-defaults.js';
37
37
  import { createDreamScheduler } from './memory/dream-scheduler.js';
38
+ import { createV2DreamScheduler } from './dream-v2/session-wiring.js';
38
39
  import { getUserMemoryStore } from './memory/user-memory-store.js';
39
40
  import { join } from 'path';
40
41
  import { existsSync as existsSyncSafe, readFileSync as readFileSyncSafe } from 'fs';
@@ -246,29 +247,47 @@ export async function loadSession(options = {}) {
246
247
  yeaftDir,
247
248
  });
248
249
 
249
- // ─── 9a. Create dream scheduler (wave-6b) ─────────────
250
- const dreamScheduler = createDreamScheduler({
251
- memoryShardStore,
252
- userMemoryStore: getUserMemoryStore(),
253
- conversationStore,
254
- adapter,
255
- config,
256
- onDreamStart: (vpId) => {
257
- if (config.debug) console.log(`[Yeaft] Dream started for VP ${vpId}`);
258
- },
259
- onDreamEnd: (vpId, result) => {
260
- if (config.debug) console.log(`[Yeaft] Dream ended for VP ${vpId}:`, JSON.stringify({
261
- trigger: result.trigger,
262
- entriesMerged: result.entriesMerged,
263
- entriesPruned: result.entriesPruned,
264
- bytesReclaimed: result.bytesReclaimed,
265
- errors: result.errors?.length || 0,
266
- }));
267
- },
268
- onError: (vpId, err) => {
269
- console.warn(`[Yeaft] Dream error for VP ${vpId}:`, err?.message || err);
270
- },
271
- });
250
+ // ─── 9a. Create dream scheduler (wave-6b / DESIGN-v2) ──
251
+ // When config.memoryV2 is on, route through the v2 pipeline (per-scope
252
+ // memory.md + summary.md). Otherwise keep the legacy R6 dream-scheduler.
253
+ let dreamScheduler;
254
+ if (config.memoryV2) {
255
+ // Build a partial session reference so the v2 wiring can see adapter,
256
+ // config, yeaftDir, engine, and trace. The scheduler's `run` closure
257
+ // dereferences these lazily, so mutating the object after this line
258
+ // (e.g. attaching engine) is safe.
259
+ const partialSession = {
260
+ yeaftDir,
261
+ adapter,
262
+ config,
263
+ engine,
264
+ trace,
265
+ };
266
+ dreamScheduler = createV2DreamScheduler(partialSession);
267
+ } else {
268
+ dreamScheduler = createDreamScheduler({
269
+ memoryShardStore,
270
+ userMemoryStore: getUserMemoryStore(),
271
+ conversationStore,
272
+ adapter,
273
+ config,
274
+ onDreamStart: (vpId) => {
275
+ if (config.debug) console.log(`[Yeaft] Dream started for VP ${vpId}`);
276
+ },
277
+ onDreamEnd: (vpId, result) => {
278
+ if (config.debug) console.log(`[Yeaft] Dream ended for VP ${vpId}:`, JSON.stringify({
279
+ trigger: result.trigger,
280
+ entriesMerged: result.entriesMerged,
281
+ entriesPruned: result.entriesPruned,
282
+ bytesReclaimed: result.bytesReclaimed,
283
+ errors: result.errors?.length || 0,
284
+ }));
285
+ },
286
+ onError: (vpId, err) => {
287
+ console.warn(`[Yeaft] Dream error for VP ${vpId}:`, err?.message || err);
288
+ },
289
+ });
290
+ }
272
291
 
273
292
  // task-308 Phase 2: thread-aware engine registry.
274
293
  // Each thread gets its own EngineInstance (lazy-created) that owns its
@@ -532,6 +532,17 @@ export function installUnifyRuntimeBridge(s) {
532
532
  if (!s) return;
533
533
  const initialMax = s.engineRegistry?.maxConcurrent ?? null;
534
534
  const initialIdle = s.threadStore?.idleArchiveDays ?? 0;
535
+
536
+ // DESIGN-v2 §19.4: forward dream pipeline progress events to the web
537
+ // client so the debug panel can render live state. Events flow through
538
+ // the same `unify_output` channel; no new WebSocket message type is
539
+ // introduced.
540
+ s._dreamProgressSink = (evt) => {
541
+ try {
542
+ sendUnifyEvent({ type: 'dream_progress', ...evt });
543
+ } catch { /* never let event delivery throw */ }
544
+ };
545
+
535
546
  ctx.unifyRuntimeSettings = {
536
547
  get maxConcurrentThreads() { return s.engineRegistry?.maxConcurrent ?? initialMax; },
537
548
  set maxConcurrentThreads(v) {