@yeaft/webchat-agent 0.1.654 → 0.1.655

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.
@@ -36,7 +36,7 @@ import { sendToServer, flushMessageBuffer } from './buffer.js';
36
36
  import { handleRestartAgent, handleUpgradeAgent } from './upgrade.js';
37
37
  import { loadMcpServers, updateMcpConfig } from '../mcp.js';
38
38
  import { getLlmConfig, updateLlmConfig, getUnifySettings, updateUnifySettings } from '../unify/config-api.js';
39
- import { handleUnifyChat, handleUnifyGroupChat, handleUnifyModeSwitch, handleUnifyModelSwitch, resetUnifySession, handleUnifyLoadHistory, handleUnifyMergeThread, handleUnifyForkThread, handleUnifyAbortThread, handleUnifyAbortAll, handleUnifyVpSubscribe, handleUnifyVpCreate, handleUnifyVpUpdate, handleUnifyVpDelete, handleUnifyVpRead, handleUnifyFeatureMessage, handleUnifyUserMemoryWrite, handleUnifyUserMemoryRemove, handleUnifyMemoryScopeList, handleUnifyMemoryQuery, handleUnifyMemoryTrace, handleUnifyFetchSummaryHistory, handleUnifyFeatureCrud, handleUnifyListGroups, handleUnifyCreateGroup, handleUnifyRenameGroup, handleUnifyArchiveGroup, handleUnifyDeleteGroup, handleUnifyAddMember, handleUnifyRemoveMember, handleUnifySetDefaultVp, handleUnifyDreamTrigger } from '../unify/web-bridge.js';
39
+ import { handleUnifyChat, handleUnifyGroupChat, handleUnifyModeSwitch, handleUnifyModelSwitch, resetUnifySession, handleUnifyLoadHistory, handleUnifyAbortThread, handleUnifyAbortAll, handleUnifyVpSubscribe, handleUnifyVpCreate, handleUnifyVpUpdate, handleUnifyVpDelete, handleUnifyVpRead, handleUnifyFeatureMessage, handleUnifyUserMemoryWrite, handleUnifyUserMemoryRemove, handleUnifyMemoryScopeList, handleUnifyMemoryQuery, handleUnifyMemoryTrace, handleUnifyFetchSummaryHistory, handleUnifyFeatureCrud, handleUnifyListGroups, handleUnifyCreateGroup, handleUnifyRenameGroup, handleUnifyArchiveGroup, handleUnifyDeleteGroup, handleUnifyAddMember, handleUnifyRemoveMember, handleUnifySetDefaultVp, handleUnifyDreamTrigger } from '../unify/web-bridge.js';
40
40
 
41
41
  export async function handleMessage(msg) {
42
42
  switch (msg.type) {
@@ -381,18 +381,10 @@ export async function handleMessage(msg) {
381
381
  await resetUnifySession();
382
382
  break;
383
383
 
384
- case 'unify_merge_thread':
385
- handleUnifyMergeThread(msg);
386
- break;
387
-
388
- case 'unify_fork_thread':
389
- handleUnifyForkThread(msg);
390
- break;
391
-
392
384
  case 'unify_abort_thread':
393
- // task-325c: user-initiated abort of a single thread's in-flight
394
- // query. Payload `{ threadId }`. Silent no-op when the thread has
395
- // no in-flight controller.
385
+ // task-325c: user-initiated abort of an in-flight query. The
386
+ // legacy `threadId` field on the payload is accepted but ignored
387
+ // (H2.f.5: single-conversation model).
396
388
  handleUnifyAbortThread(msg);
397
389
  break;
398
390
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.654",
3
+ "version": "0.1.655",
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/engine.js CHANGED
@@ -32,7 +32,10 @@ import { buildMemoryInjection } from './memory/layout.js';
32
32
  import { buildUserProfile } from './memory/user-memory-store.js';
33
33
  import { readSummary as readScopeSummary } from './memory/store-v2.js';
34
34
  import { runStopHooks } from './stop-hooks.js';
35
- import { getThreadStore, MAIN_THREAD_ID } from './threads/store.js';
35
+ // H2.f.5: threads/ retired. Persisted messages still carry a `threadId`
36
+ // field for back-compat with old conversation files; new writes always use
37
+ // the constant 'main'.
38
+ const MAIN_THREAD_ID = 'main';
36
39
  import { pickEffort, parseEffortPrefix } from './effort.js';
37
40
  import { normalizeEffort } from './models.js';
38
41
  import { attachRouterPlan, extractPriorPlan, stripMetaForWire } from './router/continuity.js';
@@ -581,17 +584,9 @@ export class Engine {
581
584
  if (!this.#conversationStore) return;
582
585
  if (this.#config._readOnly) return;
583
586
 
584
- // task-299 Phase 1: tag persisted messages with the current thread.
585
- // getThreadStore() lazily seeds a default 'main' thread if not yet init'd.
586
- let threadId = MAIN_THREAD_ID;
587
- let threadStore = null;
588
- try {
589
- threadStore = getThreadStore();
590
- threadId = threadStore.currentId || MAIN_THREAD_ID;
591
- } catch {
592
- // Defensive: any store failure falls back to 'main' so persistence
593
- // never breaks because of thread bookkeeping.
594
- }
587
+ // H2.f.5: threads retired. Persisted messages still carry threadId
588
+ // for back-compat with old conversation files; new writes always use 'main'.
589
+ const threadId = MAIN_THREAD_ID;
595
590
 
596
591
  // Persist user message
597
592
  this.#conversationStore.append({
@@ -614,18 +609,6 @@ export class Engine {
614
609
  assistantMsg.toolCalls = toolCalls;
615
610
  }
616
611
  this.#conversationStore.append(assistantMsg);
617
-
618
- // task-299 Phase 1 cached-field update: bump thread counters twice
619
- // (once for user, once for assistant). Any exception is swallowed so
620
- // bookkeeping never blocks the main persist path.
621
- try {
622
- if (threadStore) {
623
- threadStore.noteMessage(threadId);
624
- threadStore.noteMessage(threadId);
625
- }
626
- } catch {
627
- // Non-critical; counters can be rebuilt via rebuildFromMessages().
628
- }
629
612
  }
630
613
 
631
614
  /**
@@ -1709,11 +1692,7 @@ export class Engine {
1709
1692
  * @returns {string}
1710
1693
  */
1711
1694
  get currentThreadId() {
1712
- try {
1713
- return getThreadStore().currentId || MAIN_THREAD_ID;
1714
- } catch {
1715
- return MAIN_THREAD_ID;
1716
- }
1695
+ return MAIN_THREAD_ID;
1717
1696
  }
1718
1697
 
1719
1698
  /** @returns {string|null} */
package/unify/session.js CHANGED
@@ -24,17 +24,10 @@ import { SkillManager, createSkillManager } from './skills.js';
24
24
  import { MCPManager } from './mcp.js';
25
25
  import { createFullRegistry } from './tools/index.js';
26
26
  import { initFeatureStore } from './tools/feature-tools.js';
27
- import { initThreadStore } from './threads/store.js';
28
27
  import { Engine } from './engine.js';
29
- import { createThreadEngineRegistry } from './threads/engine-registry.js';
30
- import { MAIN_THREAD_ID } from './threads/store.js';
31
- import { getThreadStore } from './threads/store.js';
32
- // H2.f.1: intent-classifier (LLM router) is retired. Memory recall now
33
- // runs through pre-flow (memory/preflow.js) + post-turn adjustMemory
34
- // (memory/adjust.js); the dispatcher routes every input to the single
35
- // MAIN_THREAD_ID engine instance.
36
- import { initInputQueueStore } from './input-queue/store.js';
37
- import { createDispatcher } from './pipeline/dispatcher.js';
28
+ // H2.f.5: threads/, pipeline/dispatcher and input-queue retired. The
29
+ // session now exposes a single Engine. Memory recall runs through
30
+ // pre-flow (memory/preflow.js) + post-turn adjustMemory (memory/adjust.js).
38
31
  import { ensureDefaultGroupIfEmpty } from './groups/group-crud.js';
39
32
  import { seedDefaultVps } from './vp/seed-defaults.js';
40
33
  import { createDreamScheduler } from './memory/dream-scheduler.js';
@@ -223,16 +216,7 @@ export async function loadSession(options = {}) {
223
216
  // ─── 5a. Initialize feature store ──────────────────────
224
217
  initFeatureStore(yeaftDir, { readOnly: config._readOnly || false });
225
218
 
226
- // ─── 5b. Initialize thread store (task-299 Phase 1) ────
227
- // task-307a: now file-backed under ~/.yeaft/threads/. Passing the
228
- // yeaftDir switches on disk persistence; read-only mode is honoured.
229
- // task-318: forward the Unify autoArchiveIdleDays knob so the
230
- // archive pass (owned by task-317) can read it off the store.
231
- initThreadStore(yeaftDir, {
232
- readOnly: config._readOnly || false,
233
- force: true,
234
- idleArchiveDays: config.unify?.autoArchiveIdleDays ?? 0,
235
- });
219
+ // ─── 5b. (H2.f.5) thread store retired. Single conversation. ───
236
220
 
237
221
  // ─── 5c. D1 first-boot seed (task-334m) ─────────────────
238
222
  // When no groups exist on disk AND we're not in read-only mode,
@@ -341,46 +325,10 @@ export async function loadSession(options = {}) {
341
325
  });
342
326
  }
343
327
 
344
- // task-308 Phase 2: thread-aware engine registry.
345
- // Each thread gets its own EngineInstance (lazy-created) that owns its
346
- // messages array and tags all events with the bound threadId. Legacy
347
- // single-engine callers keep working via `session.engine`; multi-thread
348
- // callers use `session.engineRegistry.ensure(threadId)`.
349
- const engineRegistry = createThreadEngineRegistry({
350
- adapter,
351
- trace,
352
- config,
353
- conversationStore,
354
- memoryStore,
355
- memoryShardStore,
356
- toolRegistry,
357
- skillManager,
358
- mcpManager,
359
- yeaftDir,
360
- // task-318: concurrent-thread cap (UI-adjustable via Settings).
361
- maxConcurrent: config.unify?.maxConcurrentThreads ?? null,
362
- });
363
- // Seed the main-thread instance so listActive() is non-empty from T=0.
364
- engineRegistry.ensure(MAIN_THREAD_ID);
365
-
366
- // H2.f.1: the LLM intent-classifier is retired. The dispatcher now
367
- // unconditionally routes every input to the MAIN_THREAD_ID engine
368
- // instance. Memory recall happens via memory/preflow.js (pre-turn)
369
- // and memory/adjust.js (post-turn).
370
-
371
- // task-310 Phase 2 integration: wire InputQueue + Dispatcher so the
372
- // web-bridge can submit `unify_chat` inputs through the unified pipeline
373
- // (queue → engineRegistry → EngineInstance). In read-only mode the
374
- // queue is memory-only (no disk writes).
375
- const inputQueue = initInputQueueStore({
376
- yeaftDir: config._readOnly ? null : yeaftDir,
377
- force: true,
378
- });
379
- const dispatcher = createDispatcher({
380
- inputQueue,
381
- engineRegistry,
382
- trace,
383
- });
328
+ // H2.f.5: thread engine registry, input queue, and dispatcher retired.
329
+ // The session exposes a single `engine`; web-bridge calls engine.query()
330
+ // directly. Memory recall happens via memory/preflow.js (pre-turn) and
331
+ // memory/adjust.js (post-turn).
384
332
 
385
333
  // ─── 10. Build session ─────────────────────────────────
386
334
  const status = {
@@ -397,11 +345,6 @@ export async function loadSession(options = {}) {
397
345
  } catch {
398
346
  // Best-effort cleanup
399
347
  }
400
- try {
401
- engineRegistry.terminateAll();
402
- } catch {
403
- // Best-effort cleanup
404
- }
405
348
  try {
406
349
  await mcpManager.disconnectAll();
407
350
  } catch {
@@ -416,13 +359,6 @@ export async function loadSession(options = {}) {
416
359
 
417
360
  return {
418
361
  engine,
419
- engineRegistry,
420
- // H2.f.1: `router` removed (intent classifier retired). Kept the
421
- // property as `null` for any caller doing back-compat existence
422
- // checks; the dispatcher now always routes to MAIN_THREAD_ID.
423
- router: null,
424
- inputQueue,
425
- dispatcher,
426
362
  adapter,
427
363
  config,
428
364
  conversationStore,
@@ -434,17 +370,12 @@ export async function loadSession(options = {}) {
434
370
  toolRegistry,
435
371
  trace,
436
372
  yeaftDir,
437
- // task-318 rev-1 fix: expose the live ThreadStore handle so callers
438
- // (web-bridge, message-router via ctx) can invoke setIdleArchiveDays()
439
- // on the exact instance that's wired into the dispatcher. Without this
440
- // export the setter was effectively dead code.
441
- threadStore: getThreadStore(),
442
373
  status,
443
374
  shutdown,
444
375
  // task-325c: user-initiated abort API. Delegates to web-bridge which
445
- // owns the per-thread AbortController registry (`abortByThread`).
446
- // Lazy-imported to avoid a hard cycle with web-bridge.js (which already
447
- // imports this module to call loadSession).
376
+ // owns the single AbortController. Lazy-imported to avoid a hard cycle
377
+ // with web-bridge.js (which already imports this module to call
378
+ // loadSession).
448
379
  async abort(opts = {}) {
449
380
  const { abortUnifySession } = await import('./web-bridge.js');
450
381
  return abortUnifySession(opts);
@@ -426,47 +426,6 @@ export function installUnifyRuntimeBridge(s) {
426
426
  };
427
427
  }
428
428
 
429
- /**
430
- * Translate a pipeline event (from Dispatcher) into web-bridge outputs.
431
- * Pipeline events are distinct from engine events — they carry queue /
432
- * routing state for the UI. Engine events are unwrapped and forwarded.
433
- */
434
- function forwardPipelineEvent(ev, pctx) {
435
- if (!ev || typeof ev !== 'object') return false;
436
- const gid = pctx && pctx.groupId;
437
- switch (ev.type) {
438
- case 'input_queue_updated':
439
- sendUnifyEvent({
440
- type: 'input_queue_updated',
441
- total: ev.total,
442
- pending: ev.pending,
443
- routing: ev.routing,
444
- dispatched: ev.dispatched,
445
- head: ev.head,
446
- }, gid);
447
- return false;
448
- case 'routing_decision':
449
- // H2.f.2: still forwarded for wire compat, but frontend treats it as
450
- // a no-op marker; targetThreadId is always 'main'.
451
- sendUnifyEvent({
452
- type: 'routing_decision',
453
- entryId: ev.entryId,
454
- action: ev.action,
455
- source: ev.source,
456
- reason: ev.reason,
457
- }, gid);
458
- return false;
459
- case 'engine_event':
460
- pctx.onEngineEvent(ev.event);
461
- return false;
462
- case 'error':
463
- pctx.onError(ev.error);
464
- return true;
465
- default:
466
- return false;
467
- }
468
- }
469
-
470
429
  /**
471
430
  * Handle a single engine event unwrapped from an `engine_event` envelope.
472
431
  * H2.f.2: no longer stamps a threadId on outgoing claude_output frames.
@@ -957,34 +916,24 @@ export async function handleUnifyChat(msg) {
957
916
  const toolCallsAccum = [];
958
917
  const toolResultsAccum = [];
959
918
 
960
- const { entry } = session.dispatcher.submit(prompt, {
961
- messageId: msg.messageId,
962
- queryOpts: buildVpQueryOpts({ vpId, groupCoordinator, groupId }),
963
- });
964
- sendUnifyEvent({
965
- type: 'input_queue_updated',
966
- total: 1,
967
- pending: 1,
968
- routing: 0,
969
- dispatched: 0,
970
- head: { id: entry.id, status: entry.status, text: entry.text.slice(0, 80) },
971
- }, groupId);
972
-
973
- const pipelineCtx = {
919
+ // H2.f.5: dispatcher + InputQueue retired. Call engine.query() directly,
920
+ // passing the flat conversation history as `messages` for context continuity.
921
+ const queryOpts = buildVpQueryOpts({ vpId, groupCoordinator, groupId });
922
+ const handlerCtx = {
923
+ assistantTextParts,
924
+ toolCallsAccum,
925
+ toolResultsAccum,
926
+ resetQueryTimer,
974
927
  groupId,
975
- onEngineEvent: (event) => handleEngineEvent(event, {
976
- assistantTextParts,
977
- toolCallsAccum,
978
- toolResultsAccum,
979
- resetQueryTimer,
980
- groupId,
981
- }),
982
- onError: (err) => { throw err; },
983
928
  };
984
-
985
- for await (const pev of session.dispatcher.drain({ signal: abortCtrl.signal })) {
929
+ for await (const event of session.engine.query({
930
+ prompt,
931
+ messages: [...conversationMessages],
932
+ signal: abortCtrl.signal,
933
+ ...queryOpts,
934
+ })) {
986
935
  resetQueryTimer();
987
- forwardPipelineEvent(pev, pipelineCtx);
936
+ handleEngineEvent(event, handlerCtx);
988
937
  }
989
938
 
990
939
  // Accumulate messages for context continuity.
@@ -1376,33 +1325,6 @@ export async function handleUnifyFeatureCrud(msg = {}) {
1376
1325
  }
1377
1326
  }
1378
1327
 
1379
- /**
1380
- * H2.f.2 stub: thread merge no longer exists. Kept for back-compat with
1381
- * older message-router cases — emits a failed-ack.
1382
- */
1383
- export function handleUnifyMergeThread(msg) {
1384
- const { sourceId, targetId } = msg || {};
1385
- sendUnifyEvent({
1386
- type: 'thread_merge_failed',
1387
- sourceId,
1388
- targetId,
1389
- error: 'thread merge is no longer supported (H2 single-conversation)',
1390
- });
1391
- }
1392
-
1393
- /**
1394
- * H2.f.2 stub: thread fork no longer exists.
1395
- */
1396
- export function handleUnifyForkThread(msg) {
1397
- const { sourceThreadId, atMessageId } = msg || {};
1398
- sendUnifyEvent({
1399
- type: 'thread_fork_failed',
1400
- sourceThreadId,
1401
- atMessageId,
1402
- error: 'thread fork is no longer supported (H2 single-conversation)',
1403
- });
1404
- }
1405
-
1406
1328
  /** Handle model switch from the web UI. */
1407
1329
  export function handleUnifyModelSwitch(msg) {
1408
1330
  if (!session || !msg.model) return;