@yeaft/webchat-agent 0.1.854 → 0.1.856
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/bin/yeaft-stats.js +3 -3
- package/connection/message-router.js +73 -44
- package/context.js +4 -4
- package/expert-roles.js +1 -1
- package/index.js +1 -1
- package/package.json +4 -4
- package/service/index.js +1 -1
- package/{unify → yeaft}/attachments.js +7 -7
- package/{unify → yeaft}/cli.js +4 -4
- package/{unify → yeaft}/compact/compactor.js +10 -10
- package/{unify → yeaft}/config-api.js +12 -12
- package/{unify → yeaft}/config.js +19 -19
- package/{unify → yeaft}/conversation/migrate-messages-threadid.js +1 -1
- package/{unify → yeaft}/conversation/persist.js +194 -65
- package/{unify → yeaft}/conversation/search.js +28 -23
- package/{unify → yeaft}/debug-trace.js +1 -1
- package/{unify → yeaft}/dream-v2/limits.js +3 -3
- package/{unify → yeaft}/dream-v2/runner.js +1 -1
- package/{unify → yeaft}/dream-v2/session-wiring.js +2 -2
- package/{unify → yeaft}/effort.js +2 -2
- package/{unify → yeaft}/engine.js +8 -8
- package/{unify → yeaft}/eval/run-eval.js +11 -11
- package/{unify → yeaft}/eval/runner.js +1 -1
- package/{unify → yeaft}/groups/feature-flag.js +2 -2
- package/{unify → yeaft}/groups/index.js +2 -2
- package/{unify → yeaft}/groups/pre-flow.js +1 -1
- package/{unify → yeaft}/groups/project-doc.js +1 -1
- package/{unify → yeaft}/history-compact.js +3 -3
- package/{unify → yeaft}/index.js +1 -1
- package/{unify → yeaft}/init.js +15 -9
- package/{unify → yeaft}/llm/adapter.js +1 -1
- package/{unify → yeaft}/llm/anthropic.js +2 -2
- package/{unify → yeaft}/llm/models-dev.js +10 -3
- package/{unify → yeaft}/llm/openai-responses.js +2 -2
- package/{unify → yeaft}/llm/router.js +1 -1
- package/{unify → yeaft}/mcp.js +1 -1
- package/{unify → yeaft}/memory/ams-registry.js +1 -1
- package/{unify → yeaft}/memory/seed-backfill.js +1 -1
- package/{unify → yeaft}/memory/store-v2.js +1 -1
- package/{unify → yeaft}/models.js +3 -3
- package/{unify → yeaft}/pair-sanitize.js +2 -2
- package/{unify → yeaft}/personas.js +1 -1
- package/{unify → yeaft}/prompts.js +1 -1
- package/{unify → yeaft}/routing/index.js +1 -1
- package/{unify → yeaft}/routing/router.js +1 -1
- package/{unify → yeaft}/session.js +8 -8
- package/{unify → yeaft}/skills.js +2 -2
- package/{unify → yeaft}/stats/format.js +1 -1
- package/{unify → yeaft}/stop-hooks.js +1 -1
- package/{unify → yeaft}/sub-agent/runner.js +1 -1
- package/{unify → yeaft}/tools/ask-user.js +2 -2
- package/{unify → yeaft}/tools/enter-worktree.js +1 -1
- package/{unify → yeaft}/tools/exit-worktree.js +1 -1
- package/{unify → yeaft}/tools/mcp-tools.js +1 -1
- package/{unify → yeaft}/tools/registry.js +2 -2
- package/{unify → yeaft}/tools/skill.js +1 -1
- package/{unify → yeaft}/tools/start-plan.js +1 -1
- package/{unify → yeaft}/tools/types.js +3 -3
- package/{unify → yeaft}/tools/web-search.js +1 -1
- package/{unify → yeaft}/turn-utils.js +1 -1
- package/{unify → yeaft}/vp/index.js +1 -1
- package/{unify → yeaft}/vp/registry.js +1 -1
- package/{unify → yeaft}/vp/vp-bridge.js +8 -8
- package/{unify → yeaft}/web-bridge.js +230 -230
- /package/{unify → yeaft}/archive/tool-results.js +0 -0
- /package/{unify → yeaft}/archive/trace.js +0 -0
- /package/{unify → yeaft}/archive/turn-archive.js +0 -0
- /package/{unify → yeaft}/compact/decisions-log.js +0 -0
- /package/{unify → yeaft}/compact/orchestrator.js +0 -0
- /package/{unify → yeaft}/compact/triggers.js +0 -0
- /package/{unify → yeaft}/compact/turn-group.js +0 -0
- /package/{unify → yeaft}/dream-v2/apply.js +0 -0
- /package/{unify → yeaft}/dream-v2/merge.js +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/create.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/extract-group.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/extract-topic.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/extract-user.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/extract-vp.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/index.js +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/summarize-scope.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/triage-pass1.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/triage-pass2.md +0 -0
- /package/{unify → yeaft}/dream-v2/prompts/update.md +0 -0
- /package/{unify → yeaft}/dream-v2/schedule.js +0 -0
- /package/{unify → yeaft}/dream-v2/segment.js +0 -0
- /package/{unify → yeaft}/dream-v2/snapshot.js +0 -0
- /package/{unify → yeaft}/dream-v2/state.js +0 -0
- /package/{unify → yeaft}/dream-v2/triage.js +0 -0
- /package/{unify → yeaft}/eval/cases/e2e.js +0 -0
- /package/{unify → yeaft}/eval/cases/memory.js +0 -0
- /package/{unify → yeaft}/eval/cases/skills.js +0 -0
- /package/{unify → yeaft}/eval/cases/tool-use.js +0 -0
- /package/{unify → yeaft}/groups/coordinator.js +0 -0
- /package/{unify → yeaft}/groups/group-config.js +0 -0
- /package/{unify → yeaft}/groups/group-crud.js +0 -0
- /package/{unify → yeaft}/groups/group-store.js +0 -0
- /package/{unify → yeaft}/groups/ids.js +0 -0
- /package/{unify → yeaft}/groups/roster.js +0 -0
- /package/{unify → yeaft}/groups/seed-default.js +0 -0
- /package/{unify → yeaft}/llm/credentials/github-copilot.js +0 -0
- /package/{unify → yeaft}/llm/credentials/index.js +0 -0
- /package/{unify → yeaft}/memory/adjust.js +0 -0
- /package/{unify → yeaft}/memory/ams.js +0 -0
- /package/{unify → yeaft}/memory/budget.js +0 -0
- /package/{unify → yeaft}/memory/consolidate.js +0 -0
- /package/{unify → yeaft}/memory/index-db.js +0 -0
- /package/{unify → yeaft}/memory/keywords.js +0 -0
- /package/{unify → yeaft}/memory/preflow.js +0 -0
- /package/{unify → yeaft}/memory/segment-store.js +0 -0
- /package/{unify → yeaft}/memory/segment-sync.js +0 -0
- /package/{unify → yeaft}/memory/segment.js +0 -0
- /package/{unify → yeaft}/memory/summary-store.js +0 -0
- /package/{unify → yeaft}/router/continuity.js +0 -0
- /package/{unify → yeaft}/router/thinking.js +0 -0
- /package/{unify → yeaft}/routing/loop-guard.js +0 -0
- /package/{unify → yeaft}/stats/tool-usage.js +0 -0
- /package/{unify → yeaft}/storage/atomic.js +0 -0
- /package/{unify → yeaft}/storage/compact.js +0 -0
- /package/{unify → yeaft}/storage/index.js +0 -0
- /package/{unify → yeaft}/storage/jsonl-index.js +0 -0
- /package/{unify → yeaft}/storage/jsonl-log.js +0 -0
- /package/{unify → yeaft}/storage/shard-index.js +0 -0
- /package/{unify → yeaft}/storage/shard-store.js +0 -0
- /package/{unify → yeaft}/sub-agent/spawned-prompt.js +0 -0
- /package/{unify → yeaft}/templates/base.md +0 -0
- /package/{unify → yeaft}/templates/common-rules.md +0 -0
- /package/{unify → yeaft}/templates/harness/router-handoff.md +0 -0
- /package/{unify → yeaft}/templates/harness/router-shape.md +0 -0
- /package/{unify → yeaft}/templates/harness/worker-shape.md +0 -0
- /package/{unify → yeaft}/templates/identity-yeaft.md +0 -0
- /package/{unify → yeaft}/templates/mode-dream.md +0 -0
- /package/{unify → yeaft}/templates/mode-unified.md +0 -0
- /package/{unify → yeaft}/templates/personas/explorer.md +0 -0
- /package/{unify → yeaft}/templates/personas/implementer.md +0 -0
- /package/{unify → yeaft}/templates/personas/researcher.md +0 -0
- /package/{unify → yeaft}/templates/personas/reviewer.md +0 -0
- /package/{unify → yeaft}/templates/plan-instruction.md +0 -0
- /package/{unify → yeaft}/templates/tool-guidance.md +0 -0
- /package/{unify → yeaft}/tool-folding/exec-log.js +0 -0
- /package/{unify → yeaft}/tool-folding/fallback-stub.js +0 -0
- /package/{unify → yeaft}/tool-folding/index.js +0 -0
- /package/{unify → yeaft}/tool-folding/reflection-prompt.js +0 -0
- /package/{unify → yeaft}/tool-folding/t1-reflector.js +0 -0
- /package/{unify → yeaft}/tool-folding/t2-reflector.js +0 -0
- /package/{unify → yeaft}/tools/agent.js +0 -0
- /package/{unify → yeaft}/tools/apply-patch.js +0 -0
- /package/{unify → yeaft}/tools/bash.js +0 -0
- /package/{unify → yeaft}/tools/close-agent.js +0 -0
- /package/{unify → yeaft}/tools/file-edit.js +0 -0
- /package/{unify → yeaft}/tools/file-read.js +0 -0
- /package/{unify → yeaft}/tools/file-write.js +0 -0
- /package/{unify → yeaft}/tools/glob.js +0 -0
- /package/{unify → yeaft}/tools/grep.js +0 -0
- /package/{unify → yeaft}/tools/history-search.js +0 -0
- /package/{unify → yeaft}/tools/image-generation.js +0 -0
- /package/{unify → yeaft}/tools/index.js +0 -0
- /package/{unify → yeaft}/tools/js-repl.js +0 -0
- /package/{unify → yeaft}/tools/list-agents.js +0 -0
- /package/{unify → yeaft}/tools/list-dir.js +0 -0
- /package/{unify → yeaft}/tools/notebook-edit.js +0 -0
- /package/{unify → yeaft}/tools/route-forward.js +0 -0
- /package/{unify → yeaft}/tools/send-message.js +0 -0
- /package/{unify → yeaft}/tools/todo-write.js +0 -0
- /package/{unify → yeaft}/tools/view-image.js +0 -0
- /package/{unify → yeaft}/tools/wait-agent.js +0 -0
- /package/{unify → yeaft}/tools/web-fetch.js +0 -0
- /package/{unify → yeaft}/vp/seed-defaults.js +0 -0
- /package/{unify → yeaft}/vp/seed-topup.js +0 -0
- /package/{unify → yeaft}/vp/stock-ids.js +0 -0
- /package/{unify → yeaft}/vp/thread-classifier.js +0 -0
- /package/{unify → yeaft}/vp/vp-crud.js +0 -0
- /package/{unify → yeaft}/vp/vp-loader.js +0 -0
- /package/{unify → yeaft}/vp/vp-store.js +0 -0
- /package/{unify → yeaft}/vp-status-broker.js +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* web-bridge.js — Bridge between web UI and Yeaft
|
|
2
|
+
* web-bridge.js — Bridge between web UI and Yeaft Yeaft Engine.
|
|
3
3
|
*
|
|
4
4
|
* PR #797: group VP runtime is threaded again. Each group VP can own multiple
|
|
5
5
|
* classified threads, keyed by (groupId, vpId, threadId), with separate engine,
|
|
@@ -52,7 +52,7 @@ import { seedDefaultGroup } from './groups/seed-default.js';
|
|
|
52
52
|
import {
|
|
53
53
|
trimSnapshotForBudget,
|
|
54
54
|
} from './history-compact.js';
|
|
55
|
-
import {
|
|
55
|
+
import { persistYeaftAttachments, attachmentsForPersistence, persistedAttachmentPreviewPayload } from './attachments.js';
|
|
56
56
|
import { parseSeqFromId } from './conversation/persist.js';
|
|
57
57
|
import { sliceLastNTurns } from './turn-utils.js';
|
|
58
58
|
import { createVpStatusBroker } from './vp-status-broker.js';
|
|
@@ -70,7 +70,7 @@ export function __testSetThreadClassifier(fn) {
|
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* Tracks scoped-dream triggers that are currently inflight, keyed by
|
|
73
|
-
* groupId. Used by `
|
|
73
|
+
* groupId. Used by `handleYeaftDreamTrigger` to reject any overlapping
|
|
74
74
|
* scoped trigger rather than racing the sink-wrapping logic against
|
|
75
75
|
* itself.
|
|
76
76
|
*
|
|
@@ -103,8 +103,8 @@ let currentAbortCtrl = null;
|
|
|
103
103
|
/**
|
|
104
104
|
* Per-VP-turn AbortControllers. Maps `turnId` → `AbortController`.
|
|
105
105
|
* Each VP-turn in a fan-out gets its own controller so it can be stopped
|
|
106
|
-
* independently (per-VP Stop button). `
|
|
107
|
-
* turnId to abort a single VP. `
|
|
106
|
+
* independently (per-VP Stop button). `handleYeaftAbortTurn` looks up by
|
|
107
|
+
* turnId to abort a single VP. `handleYeaftAbortAll` iterates and aborts all.
|
|
108
108
|
* @type {Map<string, AbortController>}
|
|
109
109
|
*/
|
|
110
110
|
const turnAbortCtrls = new Map();
|
|
@@ -118,7 +118,7 @@ const turnAbortMeta = new Map();
|
|
|
118
118
|
|
|
119
119
|
/**
|
|
120
120
|
* Per-VP status broker — the agent-side authority for VP timeline
|
|
121
|
-
* status. Lazy-initialized on first use because `
|
|
121
|
+
* status. Lazy-initialized on first use because `sendYeaftEvent` is
|
|
122
122
|
* declared below; trying to call it at module top-level would crash
|
|
123
123
|
* with a TDZ error during agent boot.
|
|
124
124
|
*
|
|
@@ -134,8 +134,8 @@ function getVpStatusBroker() {
|
|
|
134
134
|
vpStatusBroker = createVpStatusBroker({
|
|
135
135
|
send: (event) => {
|
|
136
136
|
// The broker emits both `vp_status_changed` and
|
|
137
|
-
// `vp_status_snapshot`. Both ride the standard
|
|
138
|
-
// envelope so the frontend's existing
|
|
137
|
+
// `vp_status_snapshot`. Both ride the standard sendYeaftEvent
|
|
138
|
+
// envelope so the frontend's existing yeaft_output dispatcher
|
|
139
139
|
// sees them. We stamp groupId/vpId on the envelope for
|
|
140
140
|
// events that target a specific VP so the server's per-client
|
|
141
141
|
// routing (groupId scoping, etc.) works the same way as
|
|
@@ -146,7 +146,7 @@ function getVpStatusBroker() {
|
|
|
146
146
|
if (event.vpId) env.vpId = event.vpId;
|
|
147
147
|
if (event.turnId) env.turnId = event.turnId;
|
|
148
148
|
}
|
|
149
|
-
|
|
149
|
+
sendYeaftEvent(event, env);
|
|
150
150
|
},
|
|
151
151
|
});
|
|
152
152
|
}
|
|
@@ -202,7 +202,7 @@ const vpAborts = new Map();
|
|
|
202
202
|
const vpCurrentTodos = new Map();
|
|
203
203
|
/**
|
|
204
204
|
* Per-group cached coordinator + router. Created on first
|
|
205
|
-
* `
|
|
205
|
+
* `handleYeaftGroupChat` for a given groupId; reused across user messages
|
|
206
206
|
* AND across `route_forward` deliveries inside running VP turns (the
|
|
207
207
|
* router is wired into engine ctx; if we recreated coord per turn the
|
|
208
208
|
* route_forward path would deliver into a freshly-created `captured[]`
|
|
@@ -213,9 +213,9 @@ const vpCurrentTodos = new Map();
|
|
|
213
213
|
* handler that mutates roster / meta / lifecycle state on disk
|
|
214
214
|
* (rename, update announcement, archive, delete, add/remove member,
|
|
215
215
|
* set default VP).
|
|
216
|
-
* - `
|
|
216
|
+
* - `handleYeaftGroupChat` — invalidates inline when its own
|
|
217
217
|
* auto-add / default-VP-heal pass mutated the roster.
|
|
218
|
-
* - `
|
|
218
|
+
* - `resetYeaftSession` and `__testResetVpState` clear the whole map.
|
|
219
219
|
*
|
|
220
220
|
* @type {Map<string, { coord: ReturnType<typeof createCoordinator>,
|
|
221
221
|
* router: ReturnType<typeof createRouter>,
|
|
@@ -383,7 +383,7 @@ function invalidateGroupContext(groupId) {
|
|
|
383
383
|
// Engines are NOT torn down here on purpose. They hold subordinate
|
|
384
384
|
// state (AMS adjustments) that should survive a meta change and a
|
|
385
385
|
// closed groupHandle — they don't reach the on-disk group meta
|
|
386
|
-
// directly. They *are* dropped on `
|
|
386
|
+
// directly. They *are* dropped on `resetYeaftSession`.
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
/**
|
|
@@ -449,8 +449,8 @@ const QUERY_TIMEOUT_MS = 120_000;
|
|
|
449
449
|
*/
|
|
450
450
|
const ESCALATE_AFTER_ABORT_MS = 15_000;
|
|
451
451
|
|
|
452
|
-
/** Virtual conversationId for the
|
|
453
|
-
let
|
|
452
|
+
/** Virtual conversationId for the Yeaft session */
|
|
453
|
+
let yeaftConversationId = null;
|
|
454
454
|
|
|
455
455
|
/** task-334-followup-batch-b: stored unsubscribe fn from VP subscribe,
|
|
456
456
|
* called on session reset to prevent stale subscriber leaks. */
|
|
@@ -624,7 +624,7 @@ function loadVisibleGroupHistoryPage(store, groupId, limit, beforeSeq = null) {
|
|
|
624
624
|
rows = store.loadRecentByGroup(groupId, Infinity);
|
|
625
625
|
}
|
|
626
626
|
} catch (err) {
|
|
627
|
-
console.error('[
|
|
627
|
+
console.error('[Yeaft] visible history page load failed:', err?.message || err);
|
|
628
628
|
return { messages: [], oldestSeq: null, hasMore: false };
|
|
629
629
|
}
|
|
630
630
|
|
|
@@ -660,7 +660,7 @@ function hydrateGroupHistory(groupId) {
|
|
|
660
660
|
try {
|
|
661
661
|
recent = session.conversationStore.loadRecentByGroup(groupId);
|
|
662
662
|
} catch (err) {
|
|
663
|
-
console.warn('[
|
|
663
|
+
console.warn('[Yeaft] hydrateGroupHistory failed (groupId=%s):', groupId, err?.message || err);
|
|
664
664
|
return [];
|
|
665
665
|
}
|
|
666
666
|
const out = [];
|
|
@@ -852,7 +852,7 @@ function getOrCreateVpEngine(groupId, vpId, threadId = 'main') {
|
|
|
852
852
|
mcpManager: session.mcpManager,
|
|
853
853
|
yeaftDir: session.yeaftDir,
|
|
854
854
|
// Share the session-shared ToolUsageStats so per-VP tool calls land
|
|
855
|
-
// in the same on-disk snapshot the `
|
|
855
|
+
// in the same on-disk snapshot the `yeaft_fetch_tool_stats` handler
|
|
856
856
|
// reads. Without this, engine's record-on-tool-exec guard
|
|
857
857
|
// (`if (this.#toolStats && ...)`) is false and group VP tool calls
|
|
858
858
|
// are silently dropped.
|
|
@@ -873,7 +873,7 @@ function getOrCreateVpEngine(groupId, vpId, threadId = 'main') {
|
|
|
873
873
|
*
|
|
874
874
|
* The coordinator MUST be reused across user turns AND across in-flight
|
|
875
875
|
* tool calls (route_forward) — its `deliver` callback is the only way
|
|
876
|
-
* envelopes reach `vpInboxes`. If we recreated it per `
|
|
876
|
+
* envelopes reach `vpInboxes`. If we recreated it per `handleYeaftGroupChat`
|
|
877
877
|
* call (the pre-707 design), `route_forward` running mid-turn would
|
|
878
878
|
* deliver into a doomed `captured[]` while the new dispatch ran against
|
|
879
879
|
* a fresh coordinator. The persistent coordinator + module-level inboxes
|
|
@@ -984,7 +984,7 @@ async function routeEnvelopeToVpThread(groupId, vpId, envelope) {
|
|
|
984
984
|
});
|
|
985
985
|
thread.updatedAt = Date.now();
|
|
986
986
|
try {
|
|
987
|
-
|
|
987
|
+
sendYeaftEvent({
|
|
988
988
|
type: 'vp_thread_user_appended',
|
|
989
989
|
groupId,
|
|
990
990
|
vpId,
|
|
@@ -1006,7 +1006,7 @@ async function routeEnvelopeToVpThread(groupId, vpId, envelope) {
|
|
|
1006
1006
|
inbox.push({ envelope, turnId, thread });
|
|
1007
1007
|
|
|
1008
1008
|
try {
|
|
1009
|
-
|
|
1009
|
+
sendYeaftEvent({
|
|
1010
1010
|
type: 'vp_typing_start',
|
|
1011
1011
|
groupId,
|
|
1012
1012
|
vpId,
|
|
@@ -1028,7 +1028,7 @@ async function routeEnvelopeToVpThread(groupId, vpId, envelope) {
|
|
|
1028
1028
|
messageCount: thread.messageIds.length,
|
|
1029
1029
|
});
|
|
1030
1030
|
} catch (err) {
|
|
1031
|
-
console.warn('[
|
|
1031
|
+
console.warn('[Yeaft] vp-status typing transition failed:', err?.message || err);
|
|
1032
1032
|
}
|
|
1033
1033
|
|
|
1034
1034
|
ensureDriverRunning(groupId, vpId, thread.threadId);
|
|
@@ -1085,13 +1085,13 @@ function ensureDriverRunning(groupId, vpId, threadId = 'main') {
|
|
|
1085
1085
|
baseSnapshot,
|
|
1086
1086
|
});
|
|
1087
1087
|
} catch (err) {
|
|
1088
|
-
console.warn('[
|
|
1088
|
+
console.warn('[Yeaft] driveVp: runVpTurn failed', vpId, err?.message || err);
|
|
1089
1089
|
} finally {
|
|
1090
1090
|
turnAbortCtrls.delete(turnId);
|
|
1091
1091
|
turnAbortMeta.delete(turnId);
|
|
1092
1092
|
if (vpAborts.get(key) === vpAbort) vpAborts.delete(key);
|
|
1093
1093
|
try {
|
|
1094
|
-
|
|
1094
|
+
sendYeaftEvent({
|
|
1095
1095
|
type: 'vp_typing_end',
|
|
1096
1096
|
groupId,
|
|
1097
1097
|
vpId,
|
|
@@ -1103,7 +1103,7 @@ function ensureDriverRunning(groupId, vpId, threadId = 'main') {
|
|
|
1103
1103
|
}
|
|
1104
1104
|
try {
|
|
1105
1105
|
if (text && envelope?.msg) {
|
|
1106
|
-
|
|
1106
|
+
sendYeaftEvent({
|
|
1107
1107
|
type: 'group_message',
|
|
1108
1108
|
groupId,
|
|
1109
1109
|
vpId,
|
|
@@ -1168,10 +1168,10 @@ function ensureDriverRunning(groupId, vpId, threadId = 'main') {
|
|
|
1168
1168
|
messageCount: thread.messageIds.length,
|
|
1169
1169
|
});
|
|
1170
1170
|
} catch (err) {
|
|
1171
|
-
console.warn('[
|
|
1171
|
+
console.warn('[Yeaft] vp-status typing transition (rescue) failed:', err?.message || err);
|
|
1172
1172
|
}
|
|
1173
1173
|
try {
|
|
1174
|
-
|
|
1174
|
+
sendYeaftEvent({
|
|
1175
1175
|
type: 'vp_typing_start',
|
|
1176
1176
|
groupId,
|
|
1177
1177
|
vpId,
|
|
@@ -1239,7 +1239,7 @@ export async function __testResetVpState() {
|
|
|
1239
1239
|
|
|
1240
1240
|
|
|
1241
1241
|
/**
|
|
1242
|
-
* Send a
|
|
1242
|
+
* Send a yeaft_output message carrying claude_output-format data.
|
|
1243
1243
|
* Envelope fields: conversationId, groupId, vpId, turnId, threadId let the
|
|
1244
1244
|
* frontend route incremental deltas to the correct per-VP/thread block.
|
|
1245
1245
|
*/
|
|
@@ -1254,11 +1254,11 @@ function resolveGroupDefaultVpId(groupId) {
|
|
|
1254
1254
|
}
|
|
1255
1255
|
}
|
|
1256
1256
|
|
|
1257
|
-
function
|
|
1257
|
+
function sendYeaftOutput(data, { groupId, vpId, turnId, threadId } = {}) {
|
|
1258
1258
|
const resolvedVpId = vpId || resolveGroupDefaultVpId(groupId);
|
|
1259
1259
|
sendToServer({
|
|
1260
|
-
type: '
|
|
1261
|
-
conversationId:
|
|
1260
|
+
type: 'yeaft_output',
|
|
1261
|
+
conversationId: yeaftConversationId,
|
|
1262
1262
|
...(groupId ? { groupId } : {}),
|
|
1263
1263
|
...(resolvedVpId ? { vpId: resolvedVpId } : {}),
|
|
1264
1264
|
...(turnId ? { turnId } : {}),
|
|
@@ -1267,11 +1267,11 @@ function sendUnifyOutput(data, { groupId, vpId, turnId, threadId } = {}) {
|
|
|
1267
1267
|
});
|
|
1268
1268
|
}
|
|
1269
1269
|
|
|
1270
|
-
/** Send a
|
|
1271
|
-
function
|
|
1270
|
+
/** Send a yeaft_output event (non-claude_output metadata). */
|
|
1271
|
+
function sendYeaftEvent(event, { groupId, vpId, turnId, threadId } = {}) {
|
|
1272
1272
|
sendToServer({
|
|
1273
|
-
type: '
|
|
1274
|
-
conversationId:
|
|
1273
|
+
type: 'yeaft_output',
|
|
1274
|
+
conversationId: yeaftConversationId,
|
|
1275
1275
|
...(groupId ? { groupId } : {}),
|
|
1276
1276
|
...(vpId ? { vpId } : {}),
|
|
1277
1277
|
...(turnId ? { turnId } : {}),
|
|
@@ -1290,14 +1290,14 @@ function configuredVpPaths() {
|
|
|
1290
1290
|
};
|
|
1291
1291
|
}
|
|
1292
1292
|
|
|
1293
|
-
export function
|
|
1293
|
+
export function handleYeaftVpSubscribe(_msg) {
|
|
1294
1294
|
if (_vpUnsubscribe) {
|
|
1295
1295
|
try { _vpUnsubscribe(); } catch { /* ignore */ }
|
|
1296
1296
|
_vpUnsubscribe = null;
|
|
1297
1297
|
}
|
|
1298
1298
|
const { libDir } = configuredVpPaths();
|
|
1299
1299
|
_vpUnsubscribe = handleVpSubscribe(
|
|
1300
|
-
|
|
1300
|
+
sendYeaftEvent,
|
|
1301
1301
|
undefined,
|
|
1302
1302
|
libDir ? { dir: libDir } : {},
|
|
1303
1303
|
);
|
|
@@ -1307,10 +1307,10 @@ export function handleUnifyVpSubscribe(_msg) {
|
|
|
1307
1307
|
* VP CRUD from the web client. See historic doc for full message shapes.
|
|
1308
1308
|
*/
|
|
1309
1309
|
function sendVpCrudResult(payload) {
|
|
1310
|
-
|
|
1310
|
+
sendYeaftEvent({ type: 'vp_crud_result', ...payload });
|
|
1311
1311
|
}
|
|
1312
1312
|
|
|
1313
|
-
export function
|
|
1313
|
+
export function handleYeaftVpCreate(msg) {
|
|
1314
1314
|
const requestId = msg && msg.requestId;
|
|
1315
1315
|
const payload = msg && msg.payload;
|
|
1316
1316
|
try {
|
|
@@ -1335,7 +1335,7 @@ export function handleUnifyVpCreate(msg) {
|
|
|
1335
1335
|
}
|
|
1336
1336
|
}
|
|
1337
1337
|
|
|
1338
|
-
export function
|
|
1338
|
+
export function handleYeaftVpUpdate(msg) {
|
|
1339
1339
|
const requestId = msg && msg.requestId;
|
|
1340
1340
|
const payload = msg && msg.payload;
|
|
1341
1341
|
try {
|
|
@@ -1356,7 +1356,7 @@ export function handleUnifyVpUpdate(msg) {
|
|
|
1356
1356
|
}
|
|
1357
1357
|
}
|
|
1358
1358
|
|
|
1359
|
-
export function
|
|
1359
|
+
export function handleYeaftVpDelete(msg) {
|
|
1360
1360
|
const requestId = msg && msg.requestId;
|
|
1361
1361
|
const vpId = msg && msg.vpId;
|
|
1362
1362
|
try {
|
|
@@ -1376,7 +1376,7 @@ export function handleUnifyVpDelete(msg) {
|
|
|
1376
1376
|
if (row.vpId === vpId) broker.forget({ groupId: row.groupId, vpId });
|
|
1377
1377
|
}
|
|
1378
1378
|
} catch (err) {
|
|
1379
|
-
console.warn('[
|
|
1379
|
+
console.warn('[Yeaft] vp-status forget on delete failed:', err?.message || err);
|
|
1380
1380
|
}
|
|
1381
1381
|
sendVpCrudResult({ op: 'delete', requestId, ok: true, vpId });
|
|
1382
1382
|
} catch (err) {
|
|
@@ -1393,7 +1393,7 @@ export function handleUnifyVpDelete(msg) {
|
|
|
1393
1393
|
}
|
|
1394
1394
|
}
|
|
1395
1395
|
|
|
1396
|
-
export function
|
|
1396
|
+
export function handleYeaftVpRead(msg) {
|
|
1397
1397
|
const requestId = msg && msg.requestId;
|
|
1398
1398
|
const vpId = msg && msg.vpId;
|
|
1399
1399
|
const { libDir } = configuredVpPaths();
|
|
@@ -1414,7 +1414,7 @@ export function handleUnifyVpRead(msg) {
|
|
|
1414
1414
|
* Group CRUD wired to WS events.
|
|
1415
1415
|
*/
|
|
1416
1416
|
function sendGroupCrudResult(payload) {
|
|
1417
|
-
|
|
1417
|
+
sendYeaftEvent({ type: 'group_crud_result', ...payload });
|
|
1418
1418
|
}
|
|
1419
1419
|
|
|
1420
1420
|
function sendGroupSnapshotBroadcast() {
|
|
@@ -1422,15 +1422,15 @@ function sendGroupSnapshotBroadcast() {
|
|
|
1422
1422
|
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
1423
1423
|
if (!yeaftDir) return;
|
|
1424
1424
|
const groups = snapshotGroups(yeaftDir);
|
|
1425
|
-
|
|
1425
|
+
sendYeaftEvent({ type: 'group_list_updated', groups });
|
|
1426
1426
|
} catch (err) {
|
|
1427
|
-
console.warn('[
|
|
1427
|
+
console.warn('[Yeaft] sendGroupSnapshotBroadcast failed:', err?.message || err);
|
|
1428
1428
|
}
|
|
1429
1429
|
}
|
|
1430
1430
|
|
|
1431
1431
|
function sendGroupRosterChanged(group) {
|
|
1432
1432
|
if (!group) return;
|
|
1433
|
-
|
|
1433
|
+
sendYeaftEvent({
|
|
1434
1434
|
type: 'group_roster_changed',
|
|
1435
1435
|
groupId: group.id,
|
|
1436
1436
|
name: group.name,
|
|
@@ -1451,7 +1451,7 @@ function groupErrorPayload(err) {
|
|
|
1451
1451
|
};
|
|
1452
1452
|
}
|
|
1453
1453
|
|
|
1454
|
-
export function
|
|
1454
|
+
export function handleYeaftListGroups(msg) {
|
|
1455
1455
|
const requestId = msg && msg.requestId;
|
|
1456
1456
|
try {
|
|
1457
1457
|
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
@@ -1462,7 +1462,7 @@ export function handleUnifyListGroups(msg) {
|
|
|
1462
1462
|
}
|
|
1463
1463
|
}
|
|
1464
1464
|
|
|
1465
|
-
export function
|
|
1465
|
+
export function handleYeaftCreateGroup(msg) {
|
|
1466
1466
|
const requestId = msg && msg.requestId;
|
|
1467
1467
|
const payload = (msg && msg.payload) || {};
|
|
1468
1468
|
try {
|
|
@@ -1475,7 +1475,7 @@ export function handleUnifyCreateGroup(msg) {
|
|
|
1475
1475
|
}
|
|
1476
1476
|
}
|
|
1477
1477
|
|
|
1478
|
-
export function
|
|
1478
|
+
export function handleYeaftRenameGroup(msg) {
|
|
1479
1479
|
const requestId = msg && msg.requestId;
|
|
1480
1480
|
const groupId = msg && msg.groupId;
|
|
1481
1481
|
const name = msg && msg.name;
|
|
@@ -1491,7 +1491,7 @@ export function handleUnifyRenameGroup(msg) {
|
|
|
1491
1491
|
}
|
|
1492
1492
|
|
|
1493
1493
|
/**
|
|
1494
|
-
* `
|
|
1494
|
+
* `yeaft_update_group` — generalised group meta patch. Currently accepts
|
|
1495
1495
|
* `name` and `announcement` keys. Empty patch is rejected; an empty/
|
|
1496
1496
|
* whitespace-only `name` is also rejected up front rather than letting
|
|
1497
1497
|
* `renameGroup` raise a less-specific error deeper in the call stack.
|
|
@@ -1504,7 +1504,7 @@ export function handleUnifyRenameGroup(msg) {
|
|
|
1504
1504
|
* so this is theoretical; readers extending the patch shape should know
|
|
1505
1505
|
* the contract permits half-commits.
|
|
1506
1506
|
*/
|
|
1507
|
-
export function
|
|
1507
|
+
export function handleYeaftUpdateGroup(msg) {
|
|
1508
1508
|
const requestId = msg && msg.requestId;
|
|
1509
1509
|
const groupId = msg && msg.groupId;
|
|
1510
1510
|
const patch = (msg && msg.patch && typeof msg.patch === 'object') ? msg.patch : null;
|
|
@@ -1538,7 +1538,7 @@ export function handleUnifyUpdateGroup(msg) {
|
|
|
1538
1538
|
* Payload: { groupId, requestId, config: { model?: string|null } }
|
|
1539
1539
|
* - `model: ''` or `null` clears the selected group model (falls back to user default).
|
|
1540
1540
|
*/
|
|
1541
|
-
export function
|
|
1541
|
+
export function handleYeaftUpdateGroupConfig(msg) {
|
|
1542
1542
|
const requestId = msg && msg.requestId;
|
|
1543
1543
|
const groupId = msg && msg.groupId;
|
|
1544
1544
|
const partial = (msg && msg.config && typeof msg.config === 'object') ? msg.config : null;
|
|
@@ -1560,7 +1560,7 @@ export function handleUnifyUpdateGroupConfig(msg) {
|
|
|
1560
1560
|
}
|
|
1561
1561
|
}
|
|
1562
1562
|
|
|
1563
|
-
export function
|
|
1563
|
+
export function handleYeaftArchiveGroup(msg) {
|
|
1564
1564
|
const requestId = msg && msg.requestId;
|
|
1565
1565
|
const groupId = msg && msg.groupId;
|
|
1566
1566
|
try {
|
|
@@ -1574,7 +1574,7 @@ export function handleUnifyArchiveGroup(msg) {
|
|
|
1574
1574
|
}
|
|
1575
1575
|
}
|
|
1576
1576
|
|
|
1577
|
-
export function
|
|
1577
|
+
export function handleYeaftDeleteGroup(msg) {
|
|
1578
1578
|
const requestId = msg && msg.requestId;
|
|
1579
1579
|
const groupId = msg && msg.groupId;
|
|
1580
1580
|
try {
|
|
@@ -1614,7 +1614,7 @@ export function handleUnifyDeleteGroup(msg) {
|
|
|
1614
1614
|
}
|
|
1615
1615
|
}
|
|
1616
1616
|
|
|
1617
|
-
export function
|
|
1617
|
+
export function handleYeaftAddMember(msg) {
|
|
1618
1618
|
const requestId = msg && msg.requestId;
|
|
1619
1619
|
const groupId = msg && msg.groupId;
|
|
1620
1620
|
const vpId = msg && msg.vpId;
|
|
@@ -1629,7 +1629,7 @@ export function handleUnifyAddMember(msg) {
|
|
|
1629
1629
|
}
|
|
1630
1630
|
}
|
|
1631
1631
|
|
|
1632
|
-
export function
|
|
1632
|
+
export function handleYeaftRemoveMember(msg) {
|
|
1633
1633
|
const requestId = msg && msg.requestId;
|
|
1634
1634
|
const groupId = msg && msg.groupId;
|
|
1635
1635
|
const vpId = msg && msg.vpId;
|
|
@@ -1650,7 +1650,7 @@ export function handleUnifyRemoveMember(msg) {
|
|
|
1650
1650
|
}
|
|
1651
1651
|
}
|
|
1652
1652
|
|
|
1653
|
-
export function
|
|
1653
|
+
export function handleYeaftSetDefaultVp(msg) {
|
|
1654
1654
|
const requestId = msg && msg.requestId;
|
|
1655
1655
|
const groupId = msg && msg.groupId;
|
|
1656
1656
|
const vpId = msg && msg.vpId;
|
|
@@ -1673,20 +1673,20 @@ export function handleUnifySetDefaultVp(msg) {
|
|
|
1673
1673
|
*
|
|
1674
1674
|
* @param {import('./session.js').Session} s
|
|
1675
1675
|
*/
|
|
1676
|
-
export function
|
|
1676
|
+
export function installYeaftRuntimeBridge(s) {
|
|
1677
1677
|
if (!s) return;
|
|
1678
1678
|
|
|
1679
1679
|
// Forward dream pipeline progress events to the web debug panel.
|
|
1680
1680
|
//
|
|
1681
1681
|
// Group-id stamping is NO LONGER done here. It used to be: this sink
|
|
1682
1682
|
// read a module-level `activeScopedDreamGroupId` that
|
|
1683
|
-
// `
|
|
1683
|
+
// `handleYeaftDreamTrigger({groupId})` parked before awaiting the
|
|
1684
1684
|
// scope-filtered pass. That created a race when two scoped triggers
|
|
1685
1685
|
// overlapped (auto-tick during a manual click; or two manual clicks
|
|
1686
1686
|
// for different groups): the second handler's `finally` could clear
|
|
1687
1687
|
// the module slot while the first run was still emitting events,
|
|
1688
1688
|
// dropping the stamp from the tail of the first pass. The new design:
|
|
1689
|
-
// `
|
|
1689
|
+
// `handleYeaftDreamTrigger` wraps THIS sink for the lifetime of the
|
|
1690
1690
|
// trigger to inject `groupId` per-call (see that function below). The
|
|
1691
1691
|
// base sink is intentionally a pure passthrough.
|
|
1692
1692
|
//
|
|
@@ -1696,25 +1696,25 @@ export function installUnifyRuntimeBridge(s) {
|
|
|
1696
1696
|
try {
|
|
1697
1697
|
if (evt.type === 'turn_open' || evt.type === 'turn_close' || evt.type === 'loop') {
|
|
1698
1698
|
const tag = evt && evt.groupId ? { groupId: evt.groupId } : {};
|
|
1699
|
-
|
|
1699
|
+
sendYeaftEvent(evt, tag);
|
|
1700
1700
|
} else {
|
|
1701
1701
|
const out = { type: 'dream_progress', ...evt };
|
|
1702
1702
|
const tag = evt && evt.groupId ? { groupId: evt.groupId } : {};
|
|
1703
|
-
|
|
1703
|
+
sendYeaftEvent(out, tag);
|
|
1704
1704
|
}
|
|
1705
1705
|
} catch { /* never let event delivery throw */ }
|
|
1706
1706
|
};
|
|
1707
1707
|
|
|
1708
1708
|
// Wire the post-compact WS sink. Compactor is constructed in
|
|
1709
|
-
// session.js with a no-op sink; bridge owns `
|
|
1710
|
-
// `
|
|
1711
|
-
// present. Per-group `
|
|
1709
|
+
// session.js with a no-op sink; bridge owns `sendYeaftEvent` /
|
|
1710
|
+
// `yeaftConversationId`, so the sink is wired here once a session is
|
|
1711
|
+
// present. Per-group `yeaft_history_compacted` events fire after a
|
|
1712
1712
|
// successful summarize+swap.
|
|
1713
1713
|
if (s.compactor && typeof s.compactor.setOnCompacted === 'function') {
|
|
1714
1714
|
s.compactor.setOnCompacted((groupId, result) => {
|
|
1715
1715
|
try {
|
|
1716
|
-
|
|
1717
|
-
type: '
|
|
1716
|
+
sendYeaftEvent({
|
|
1717
|
+
type: 'yeaft_history_compacted',
|
|
1718
1718
|
reason: result?.reason ?? null,
|
|
1719
1719
|
beforeTurns: result?.beforeTurns,
|
|
1720
1720
|
afterTurns: result?.afterTurns,
|
|
@@ -1727,9 +1727,9 @@ export function installUnifyRuntimeBridge(s) {
|
|
|
1727
1727
|
});
|
|
1728
1728
|
}
|
|
1729
1729
|
|
|
1730
|
-
ctx.
|
|
1730
|
+
ctx.yeaftRuntimeSettings = {
|
|
1731
1731
|
// No multi-thread settings to surface anymore. Stub for back-compat
|
|
1732
|
-
// with message-router's
|
|
1732
|
+
// with message-router's update_yeaft_settings branch — assignments are
|
|
1733
1733
|
// accepted but ignored.
|
|
1734
1734
|
get maxConcurrentThreads() { return null; },
|
|
1735
1735
|
set maxConcurrentThreads(_v) { /* deprecated, ignored */ },
|
|
@@ -1764,7 +1764,7 @@ function maybeTransitionVpStatus(hctx, state) {
|
|
|
1764
1764
|
messageCount: hctx.thread?.messageIds?.length || 0,
|
|
1765
1765
|
});
|
|
1766
1766
|
} catch (err) {
|
|
1767
|
-
console.warn(`[
|
|
1767
|
+
console.warn(`[Yeaft] vp-status ${state} transition failed:`, err?.message || err);
|
|
1768
1768
|
}
|
|
1769
1769
|
}
|
|
1770
1770
|
|
|
@@ -1792,7 +1792,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1792
1792
|
switch (event.type) {
|
|
1793
1793
|
case 'text_delta':
|
|
1794
1794
|
hctx.assistantTextParts.push(event.text);
|
|
1795
|
-
|
|
1795
|
+
sendYeaftOutput({
|
|
1796
1796
|
type: 'assistant',
|
|
1797
1797
|
message: { content: [{ type: 'text', text: event.text }] },
|
|
1798
1798
|
}, envelope);
|
|
@@ -1803,7 +1803,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1803
1803
|
break;
|
|
1804
1804
|
|
|
1805
1805
|
case 'thinking_delta':
|
|
1806
|
-
|
|
1806
|
+
sendYeaftEvent({ type: 'thinking_delta', text: event.text }, envelope);
|
|
1807
1807
|
break;
|
|
1808
1808
|
|
|
1809
1809
|
case 'thinking_block_end':
|
|
@@ -1812,7 +1812,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1812
1812
|
// turns / VPs includes it. Without this echo Anthropic 400s the
|
|
1813
1813
|
// next request with "content[].thinking in the thinking mode must
|
|
1814
1814
|
// be passed back to the API". The signature stays server-side
|
|
1815
|
-
// only — wire serializers (stripMetaForWire /
|
|
1815
|
+
// only — wire serializers (stripMetaForWire / sendYeaftOutput)
|
|
1816
1816
|
// never reference thinkingBlocks, so it cannot leak to the UI.
|
|
1817
1817
|
if (hctx.thinkingBlocksAccum && event.signature) {
|
|
1818
1818
|
if (event.redacted) {
|
|
@@ -1842,11 +1842,11 @@ function handleEngineEvent(event, hctx) {
|
|
|
1842
1842
|
});
|
|
1843
1843
|
}
|
|
1844
1844
|
// Finish any in-progress text streaming so UI shows typing dots
|
|
1845
|
-
|
|
1845
|
+
sendYeaftOutput({
|
|
1846
1846
|
type: 'assistant',
|
|
1847
1847
|
message: { content: [] },
|
|
1848
1848
|
}, envelope);
|
|
1849
|
-
|
|
1849
|
+
sendYeaftOutput({
|
|
1850
1850
|
type: 'assistant',
|
|
1851
1851
|
message: {
|
|
1852
1852
|
content: [{
|
|
@@ -1861,7 +1861,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1861
1861
|
break;
|
|
1862
1862
|
|
|
1863
1863
|
case 'tool_start':
|
|
1864
|
-
|
|
1864
|
+
sendYeaftEvent({
|
|
1865
1865
|
type: 'tool_start',
|
|
1866
1866
|
id: event.id,
|
|
1867
1867
|
name: event.name,
|
|
@@ -1877,7 +1877,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1877
1877
|
isError: !!event.isError,
|
|
1878
1878
|
});
|
|
1879
1879
|
}
|
|
1880
|
-
|
|
1880
|
+
sendYeaftOutput({
|
|
1881
1881
|
type: 'user',
|
|
1882
1882
|
tool_use_result: [{
|
|
1883
1883
|
type: 'tool_result',
|
|
@@ -1920,9 +1920,9 @@ function handleEngineEvent(event, hctx) {
|
|
|
1920
1920
|
messageCount: hctx.thread?.messageIds?.length || 0,
|
|
1921
1921
|
});
|
|
1922
1922
|
} catch (err) {
|
|
1923
|
-
console.warn('[
|
|
1923
|
+
console.warn('[Yeaft] vp-status settleIdle (route_forward) failed:', err?.message || err);
|
|
1924
1924
|
}
|
|
1925
|
-
|
|
1925
|
+
sendYeaftEvent({
|
|
1926
1926
|
type: 'vp_turn_end',
|
|
1927
1927
|
groupId: hctx.groupId,
|
|
1928
1928
|
vpId: hctx.vpId,
|
|
@@ -1936,7 +1936,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1936
1936
|
break;
|
|
1937
1937
|
|
|
1938
1938
|
case 'usage':
|
|
1939
|
-
|
|
1939
|
+
sendYeaftEvent({
|
|
1940
1940
|
type: 'context_usage',
|
|
1941
1941
|
inputTokens: event.inputTokens,
|
|
1942
1942
|
outputTokens: event.outputTokens,
|
|
@@ -1944,7 +1944,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1944
1944
|
break;
|
|
1945
1945
|
|
|
1946
1946
|
case 'recall':
|
|
1947
|
-
|
|
1947
|
+
sendYeaftEvent({
|
|
1948
1948
|
type: 'recall',
|
|
1949
1949
|
entryCount: event.entryCount,
|
|
1950
1950
|
cached: event.cached,
|
|
@@ -1955,7 +1955,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1955
1955
|
// Engine compressed the context — clear THIS group's accumulated
|
|
1956
1956
|
// history. Other groups' histories stay intact.
|
|
1957
1957
|
if (hctx.groupId) setGroupHistory(hctx.groupId, []);
|
|
1958
|
-
|
|
1958
|
+
sendYeaftEvent({
|
|
1959
1959
|
type: 'consolidate',
|
|
1960
1960
|
archivedCount: event.archivedCount,
|
|
1961
1961
|
extractedCount: event.extractedCount,
|
|
@@ -1963,7 +1963,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1963
1963
|
break;
|
|
1964
1964
|
|
|
1965
1965
|
case 'fallback':
|
|
1966
|
-
|
|
1966
|
+
sendYeaftEvent({
|
|
1967
1967
|
type: 'fallback',
|
|
1968
1968
|
from: event.from,
|
|
1969
1969
|
to: event.to,
|
|
@@ -1972,7 +1972,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1972
1972
|
break;
|
|
1973
1973
|
|
|
1974
1974
|
case 'reflection':
|
|
1975
|
-
|
|
1975
|
+
sendYeaftEvent({
|
|
1976
1976
|
type: 'reflection',
|
|
1977
1977
|
// feat-6af5f9f1 PR B: stamp turnId/loopNumber so the debug panel
|
|
1978
1978
|
// can attach reflection cards to the matching loop.
|
|
@@ -1989,7 +1989,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
1989
1989
|
break;
|
|
1990
1990
|
|
|
1991
1991
|
case 'turn_open':
|
|
1992
|
-
|
|
1992
|
+
sendYeaftEvent({
|
|
1993
1993
|
type: 'turn_open',
|
|
1994
1994
|
turnId: event.turnId,
|
|
1995
1995
|
userPrompt: event.userPrompt,
|
|
@@ -2000,7 +2000,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
2000
2000
|
break;
|
|
2001
2001
|
|
|
2002
2002
|
case 'turn_close':
|
|
2003
|
-
|
|
2003
|
+
sendYeaftEvent({
|
|
2004
2004
|
type: 'turn_close',
|
|
2005
2005
|
turnId: event.turnId,
|
|
2006
2006
|
totalMs: event.totalMs,
|
|
@@ -2010,7 +2010,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
2010
2010
|
break;
|
|
2011
2011
|
|
|
2012
2012
|
case 'memory_used':
|
|
2013
|
-
|
|
2013
|
+
sendYeaftEvent({
|
|
2014
2014
|
type: 'memory_used',
|
|
2015
2015
|
turnId: event.turnId,
|
|
2016
2016
|
loaded: event.loaded || [],
|
|
@@ -2018,7 +2018,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
2018
2018
|
break;
|
|
2019
2019
|
|
|
2020
2020
|
case 'memory_adjust':
|
|
2021
|
-
|
|
2021
|
+
sendYeaftEvent({
|
|
2022
2022
|
type: 'memory_adjust',
|
|
2023
2023
|
turnId: event.turnId,
|
|
2024
2024
|
groupKey: event.groupKey,
|
|
@@ -2033,7 +2033,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
2033
2033
|
if (hctx && Array.isArray(hctx.appendedUserPrompts) && event.preview) {
|
|
2034
2034
|
hctx.appendedUserPrompts.push(String(event.preview));
|
|
2035
2035
|
}
|
|
2036
|
-
|
|
2036
|
+
sendYeaftEvent({
|
|
2037
2037
|
type: 'vp_thread_user_append_consumed',
|
|
2038
2038
|
turnId: event.turnId,
|
|
2039
2039
|
threadId: event.threadId,
|
|
@@ -2044,7 +2044,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
2044
2044
|
break;
|
|
2045
2045
|
|
|
2046
2046
|
case 'tool_exec':
|
|
2047
|
-
|
|
2047
|
+
sendYeaftEvent({
|
|
2048
2048
|
type: 'tool_exec',
|
|
2049
2049
|
turnId: event.turnId,
|
|
2050
2050
|
loopNumber: event.loopNumber,
|
|
@@ -2058,7 +2058,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
2058
2058
|
case 'loop':
|
|
2059
2059
|
// feat-6af5f9f1 PR B: replaces the old `debug_turn` event. Same
|
|
2060
2060
|
// payload shape plus turnId + loopNumber + usage.totalTokens.
|
|
2061
|
-
|
|
2061
|
+
sendYeaftEvent({
|
|
2062
2062
|
type: 'loop',
|
|
2063
2063
|
turnId: event.turnId,
|
|
2064
2064
|
loopNumber: event.loopNumber,
|
|
@@ -2081,7 +2081,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
2081
2081
|
if (isPermissionErrorMsg(errMsg)) {
|
|
2082
2082
|
if (!_permissionDiagnosticSent) {
|
|
2083
2083
|
_permissionDiagnosticSent = true;
|
|
2084
|
-
|
|
2084
|
+
sendYeaftOutput({
|
|
2085
2085
|
type: 'assistant',
|
|
2086
2086
|
message: {
|
|
2087
2087
|
content: [{
|
|
@@ -2092,7 +2092,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
2092
2092
|
}, envelope);
|
|
2093
2093
|
}
|
|
2094
2094
|
} else {
|
|
2095
|
-
|
|
2095
|
+
sendYeaftOutput({
|
|
2096
2096
|
type: 'assistant',
|
|
2097
2097
|
message: {
|
|
2098
2098
|
content: [{ type: 'text', text: `⚠️ Error: ${errMsg}` }],
|
|
@@ -2109,13 +2109,13 @@ function handleEngineEvent(event, hctx) {
|
|
|
2109
2109
|
}
|
|
2110
2110
|
|
|
2111
2111
|
/**
|
|
2112
|
-
* Handle a
|
|
2112
|
+
* Handle a yeaft_group_chat message from the web UI — the SOLE Yeaft
|
|
2113
2113
|
* conversation entry point.
|
|
2114
2114
|
*
|
|
2115
|
-
* Contract (post-consolidation, was previously split between
|
|
2116
|
-
* and
|
|
2117
|
-
* - Frontend ALWAYS sends `
|
|
2118
|
-
* - `groupId` defaults to `'grp_default'` if missing —
|
|
2115
|
+
* Contract (post-consolidation, was previously split between handleYeaftChat
|
|
2116
|
+
* and handleYeaftGroupChat):
|
|
2117
|
+
* - Frontend ALWAYS sends `yeaft_group_chat`. There is no `yeaft_chat`.
|
|
2118
|
+
* - `groupId` defaults to `'grp_default'` if missing — Yeaft is a single
|
|
2119
2119
|
* conversation backed by the default group; the user is never "outside"
|
|
2120
2120
|
* a group.
|
|
2121
2121
|
* - If the group dir doesn't exist and the resolved id is `'grp_default'`,
|
|
@@ -2125,12 +2125,12 @@ function handleEngineEvent(event, hctx) {
|
|
|
2125
2125
|
* - No legacy "no-group" fallback paths — they were the source of the
|
|
2126
2126
|
* router_unavailable bug fixed in v0.1.671.
|
|
2127
2127
|
*/
|
|
2128
|
-
export async function
|
|
2128
|
+
export async function handleYeaftGroupChat(msg) {
|
|
2129
2129
|
if (!msg || typeof msg !== 'object') return;
|
|
2130
2130
|
const { text } = msg;
|
|
2131
2131
|
// PR #721: image-only send is allowed — text may be empty when the
|
|
2132
2132
|
// user attached files only. The frontend synthesizes a placeholder
|
|
2133
|
-
// string in `
|
|
2133
|
+
// string in `sendYeaftGroupChat`, so by the time we get here `text`
|
|
2134
2134
|
// should always be non-empty; but defend anyway in case an API
|
|
2135
2135
|
// caller sends a bare attachment payload.
|
|
2136
2136
|
const hasFiles = Array.isArray(msg.files) && msg.files.length > 0;
|
|
@@ -2157,11 +2157,11 @@ export async function handleUnifyGroupChat(msg) {
|
|
|
2157
2157
|
// session lying around.
|
|
2158
2158
|
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
2159
2159
|
if (!yeaftDir) {
|
|
2160
|
-
|
|
2160
|
+
sendYeaftOutput({
|
|
2161
2161
|
type: 'assistant',
|
|
2162
|
-
message: { content: [{ type: 'text', text: '⚠️
|
|
2162
|
+
message: { content: [{ type: 'text', text: '⚠️ Yeaft session error: no yeaft directory configured.' }] },
|
|
2163
2163
|
}, { groupId });
|
|
2164
|
-
|
|
2164
|
+
sendYeaftOutput({ type: 'result', result_text: '' }, { groupId });
|
|
2165
2165
|
return;
|
|
2166
2166
|
}
|
|
2167
2167
|
|
|
@@ -2185,24 +2185,24 @@ export async function handleUnifyGroupChat(msg) {
|
|
|
2185
2185
|
groupHandle = seeded.group;
|
|
2186
2186
|
} catch (seedErr) {
|
|
2187
2187
|
seedFailed = true;
|
|
2188
|
-
console.warn('[
|
|
2188
|
+
console.warn('[Yeaft] yeaft_group_chat: seedDefaultGroup failed', seedErr?.message || seedErr);
|
|
2189
2189
|
}
|
|
2190
2190
|
} else {
|
|
2191
|
-
console.warn('[
|
|
2191
|
+
console.warn('[Yeaft] yeaft_group_chat: groupId %s not found', groupId);
|
|
2192
2192
|
}
|
|
2193
2193
|
} catch (err) {
|
|
2194
|
-
console.warn('[
|
|
2194
|
+
console.warn('[Yeaft] yeaft_group_chat: group open failed', err?.message || err);
|
|
2195
2195
|
}
|
|
2196
2196
|
|
|
2197
2197
|
if (!groupHandle) {
|
|
2198
2198
|
const errText = seedFailed
|
|
2199
2199
|
? `⚠️ Failed to seed default group ${groupId} — check group .yeaft permissions.`
|
|
2200
2200
|
: `⚠️ Group ${groupId} not found.`;
|
|
2201
|
-
|
|
2201
|
+
sendYeaftOutput({
|
|
2202
2202
|
type: 'assistant',
|
|
2203
2203
|
message: { content: [{ type: 'text', text: errText }] },
|
|
2204
2204
|
}, { groupId });
|
|
2205
|
-
|
|
2205
|
+
sendYeaftOutput({ type: 'result', result_text: '' }, { groupId });
|
|
2206
2206
|
return;
|
|
2207
2207
|
}
|
|
2208
2208
|
|
|
@@ -2239,7 +2239,7 @@ export async function handleUnifyGroupChat(msg) {
|
|
|
2239
2239
|
} catch { /* best-effort */ }
|
|
2240
2240
|
}
|
|
2241
2241
|
} catch (err) {
|
|
2242
|
-
console.warn('[
|
|
2242
|
+
console.warn('[Yeaft] yeaft_group_chat: auto-roster heal failed', err?.message || err);
|
|
2243
2243
|
}
|
|
2244
2244
|
|
|
2245
2245
|
// task-707: per-group persistent coordinator/router. Created once per
|
|
@@ -2260,7 +2260,7 @@ export async function handleUnifyGroupChat(msg) {
|
|
|
2260
2260
|
// ── Attachments (images + files) ───────────────────────────────
|
|
2261
2261
|
// Server has already resolved fileId → { name, mimeType, data:base64,
|
|
2262
2262
|
// isImage } via the same path crew uses (client-conversation.js relay
|
|
2263
|
-
// for `
|
|
2263
|
+
// for `yeaft_*`). We persist files to disk under the agent's CWD so
|
|
2264
2264
|
// file-tools (file-read / bash) can pick them up with relative paths,
|
|
2265
2265
|
// and we build per-image content blocks for the LLM call. The
|
|
2266
2266
|
// resolved metadata WITHOUT base64 rides on coord.ingest meta so it
|
|
@@ -2270,9 +2270,9 @@ export async function handleUnifyGroupChat(msg) {
|
|
|
2270
2270
|
let attachmentBundle = { promptAttachments: [], promptSuffix: '', promptParts: [], failed: [] };
|
|
2271
2271
|
if (inboundFiles.length > 0) {
|
|
2272
2272
|
try {
|
|
2273
|
-
attachmentBundle =
|
|
2273
|
+
attachmentBundle = persistYeaftAttachments(inboundFiles, { subdir: groupId });
|
|
2274
2274
|
} catch (err) {
|
|
2275
|
-
console.warn('[
|
|
2275
|
+
console.warn('[Yeaft] yeaft_group_chat: attachment persist failed', err?.message || err);
|
|
2276
2276
|
}
|
|
2277
2277
|
}
|
|
2278
2278
|
// Surface partial / total upload failures to the user. We don't abort
|
|
@@ -2282,7 +2282,7 @@ export async function handleUnifyGroupChat(msg) {
|
|
|
2282
2282
|
const detail = attachmentBundle.failed
|
|
2283
2283
|
.map((f) => ` - ${f.name}: ${f.error}`)
|
|
2284
2284
|
.join('\n');
|
|
2285
|
-
|
|
2285
|
+
sendYeaftOutput({
|
|
2286
2286
|
type: 'assistant',
|
|
2287
2287
|
message: { content: [{ type: 'text', text: `⚠️ ${attachmentBundle.failed.length} file(s) could not be attached:\n${detail}` }] },
|
|
2288
2288
|
}, { groupId });
|
|
@@ -2311,12 +2311,12 @@ export async function handleUnifyGroupChat(msg) {
|
|
|
2311
2311
|
_promptSuffix: attachmentBundle.promptSuffix,
|
|
2312
2312
|
});
|
|
2313
2313
|
} catch (err) {
|
|
2314
|
-
console.warn('[
|
|
2315
|
-
|
|
2314
|
+
console.warn('[Yeaft] yeaft_group_chat: coord.ingest failed', err?.message || err);
|
|
2315
|
+
sendYeaftOutput({
|
|
2316
2316
|
type: 'assistant',
|
|
2317
2317
|
message: { content: [{ type: 'text', text: `⚠️ Group dispatch error: ${err?.message || err}` }] },
|
|
2318
2318
|
}, { groupId });
|
|
2319
|
-
|
|
2319
|
+
sendYeaftOutput({ type: 'result', result_text: '' }, { groupId });
|
|
2320
2320
|
return;
|
|
2321
2321
|
}
|
|
2322
2322
|
|
|
@@ -2333,11 +2333,11 @@ export async function handleUnifyGroupChat(msg) {
|
|
|
2333
2333
|
// with a healthy roster. Surface the failure explicitly rather than
|
|
2334
2334
|
// silently retrying as a single-VP turn (the legacy fallback masked
|
|
2335
2335
|
// group-roster bugs).
|
|
2336
|
-
|
|
2336
|
+
sendYeaftOutput({
|
|
2337
2337
|
type: 'assistant',
|
|
2338
2338
|
message: { content: [{ type: 'text', text: '⚠️ No VP available to respond — check the group roster.' }] },
|
|
2339
2339
|
}, { groupId });
|
|
2340
|
-
|
|
2340
|
+
sendYeaftOutput({ type: 'result', result_text: '' }, { groupId });
|
|
2341
2341
|
return;
|
|
2342
2342
|
}
|
|
2343
2343
|
|
|
@@ -2502,18 +2502,18 @@ async function ensureSessionLoaded() {
|
|
|
2502
2502
|
serverMode: true,
|
|
2503
2503
|
});
|
|
2504
2504
|
|
|
2505
|
-
|
|
2505
|
+
installYeaftRuntimeBridge(session);
|
|
2506
2506
|
|
|
2507
2507
|
try {
|
|
2508
2508
|
if (session.engine && typeof session.engine.setSubAgentEventSink === 'function') {
|
|
2509
2509
|
session.engine.setSubAgentEventSink((agentId, evt) => {
|
|
2510
2510
|
try {
|
|
2511
|
-
|
|
2511
|
+
sendYeaftEvent({ type: 'sub_agent_event', agentId, payload: evt });
|
|
2512
2512
|
} catch { /* ignore */ }
|
|
2513
2513
|
});
|
|
2514
2514
|
}
|
|
2515
2515
|
} catch (err) {
|
|
2516
|
-
console.warn('[
|
|
2516
|
+
console.warn('[Yeaft] setSubAgentEventSink wiring failed:', err?.message || err);
|
|
2517
2517
|
}
|
|
2518
2518
|
|
|
2519
2519
|
// Bug 8: clean up legacy `.archived-*` group dirs at boot.
|
|
@@ -2521,21 +2521,21 @@ async function ensureSessionLoaded() {
|
|
|
2521
2521
|
if (yeaftDir) {
|
|
2522
2522
|
const removed = purgeArchivedGroups(yeaftDir);
|
|
2523
2523
|
if (removed && removed.length > 0) {
|
|
2524
|
-
console.log(`[
|
|
2524
|
+
console.log(`[Yeaft] purged ${removed.length} legacy .archived group dir(s)`);
|
|
2525
2525
|
}
|
|
2526
2526
|
}
|
|
2527
2527
|
} catch (err) {
|
|
2528
|
-
console.warn('[
|
|
2528
|
+
console.warn('[Yeaft] purgeArchivedGroups failed:', err?.message || err);
|
|
2529
2529
|
}
|
|
2530
2530
|
|
|
2531
|
-
|
|
2531
|
+
yeaftConversationId = `yeaft-${Date.now()}`;
|
|
2532
2532
|
|
|
2533
2533
|
// Per-group history is hydrated lazily on first `getOrCreateGroupHistory`
|
|
2534
2534
|
// — there's no global "all conversations" tape any more.
|
|
2535
2535
|
|
|
2536
|
-
|
|
2536
|
+
sendYeaftEvent({
|
|
2537
2537
|
type: 'session_ready',
|
|
2538
|
-
conversationId:
|
|
2538
|
+
conversationId: yeaftConversationId,
|
|
2539
2539
|
model: session.config.model,
|
|
2540
2540
|
availableModels: session.config.availableModels || [],
|
|
2541
2541
|
skills: session.status.skills,
|
|
@@ -2551,7 +2551,7 @@ async function ensureSessionLoaded() {
|
|
|
2551
2551
|
try {
|
|
2552
2552
|
getVpStatusBroker().broadcastSnapshot();
|
|
2553
2553
|
} catch (err) {
|
|
2554
|
-
console.warn('[
|
|
2554
|
+
console.warn('[Yeaft] vp-status snapshot broadcast failed:', err?.message || err);
|
|
2555
2555
|
}
|
|
2556
2556
|
}
|
|
2557
2557
|
|
|
@@ -2589,10 +2589,10 @@ async function runVpTurnWithEscalation(args) {
|
|
|
2589
2589
|
deadlineMs,
|
|
2590
2590
|
onEscalate: () => {
|
|
2591
2591
|
console.error(
|
|
2592
|
-
`[
|
|
2592
|
+
`[Yeaft] runVpTurn watchdog escalation: VP ${vpId} did not return ${deadlineMs}ms after enqueue — emitting synthetic stop and unblocking driver`,
|
|
2593
2593
|
);
|
|
2594
2594
|
try {
|
|
2595
|
-
|
|
2595
|
+
sendYeaftOutput(
|
|
2596
2596
|
{ type: 'result', result_text: '', stopped: true },
|
|
2597
2597
|
{ groupId, vpId, turnId, threadId },
|
|
2598
2598
|
);
|
|
@@ -2608,7 +2608,7 @@ async function runVpTurnWithEscalation(args) {
|
|
|
2608
2608
|
try {
|
|
2609
2609
|
getVpStatusBroker().settleIdle({ groupId, vpId, threadId: threadId || 'main', title: thread?.title || '' });
|
|
2610
2610
|
} catch (err) {
|
|
2611
|
-
console.warn('[
|
|
2611
|
+
console.warn('[Yeaft] vp-status settleIdle (escalation) failed:', err?.message || err);
|
|
2612
2612
|
}
|
|
2613
2613
|
},
|
|
2614
2614
|
});
|
|
@@ -2692,7 +2692,7 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2692
2692
|
if (queryTimer) clearTimeout(queryTimer);
|
|
2693
2693
|
queryTimer = setTimeout(() => {
|
|
2694
2694
|
if (!vpAbort.signal.aborted) {
|
|
2695
|
-
console.error(`[
|
|
2695
|
+
console.error(`[Yeaft] query timeout after ${QUERY_TIMEOUT_MS / 1000}s of silence — aborting VP ${vpId}`);
|
|
2696
2696
|
try { vpAbort.abort(); } catch { /* best-effort */ }
|
|
2697
2697
|
}
|
|
2698
2698
|
}, QUERY_TIMEOUT_MS);
|
|
@@ -2700,12 +2700,12 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2700
2700
|
resetQueryTimer();
|
|
2701
2701
|
|
|
2702
2702
|
// Emit turn_start so frontend can create the message block.
|
|
2703
|
-
|
|
2703
|
+
sendYeaftEvent({ type: 'vp_turn_start', vpId, threadId, turnId, groupId, title: thread?.title || '' }, envelope);
|
|
2704
2704
|
// vp-status: LLM call about to start, no text/tool yet → 'thinking'.
|
|
2705
2705
|
try {
|
|
2706
2706
|
getVpStatusBroker().transition({ groupId, vpId, threadId, title: thread?.title || '', state: 'thinking', turnId, messageCount: thread?.messageIds?.length || 0 });
|
|
2707
2707
|
} catch (err) {
|
|
2708
|
-
console.warn('[
|
|
2708
|
+
console.warn('[Yeaft] vp-status thinking transition failed:', err?.message || err);
|
|
2709
2709
|
}
|
|
2710
2710
|
|
|
2711
2711
|
try {
|
|
@@ -2717,7 +2717,7 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2717
2717
|
let vpEngine = null;
|
|
2718
2718
|
|
|
2719
2719
|
// task-707: per-VP engine + persistent group coord. The coord is
|
|
2720
|
-
// created in
|
|
2720
|
+
// created in handleYeaftGroupChat via getOrCreateGroupContext and
|
|
2721
2721
|
// cached on `groupContexts`; we pull it here so route_forward
|
|
2722
2722
|
// (router built from this same coord) lands envelopes back on the
|
|
2723
2723
|
// right inbox set.
|
|
@@ -2760,7 +2760,7 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2760
2760
|
messages: trimmedMessages,
|
|
2761
2761
|
signal: vpAbort.signal,
|
|
2762
2762
|
// Multi-VP fan-out (history-dedup): the user row was persisted
|
|
2763
|
-
// ONCE by
|
|
2763
|
+
// ONCE by handleYeaftGroupChat → persistUserMessageOnce before
|
|
2764
2764
|
// fan-out. Tell the engine's stop-hook to skip the user-row
|
|
2765
2765
|
// append for THIS VP's turn (it still writes assistant + tool
|
|
2766
2766
|
// rows for this VP). Without this the magnet of N engines would
|
|
@@ -2781,11 +2781,11 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2781
2781
|
// Turn completed — atomically append this VP's output to shared history.
|
|
2782
2782
|
appendTurnToGroupHistory(groupId, threadId, [prompt, ...appendedUserPrompts], assistantTextParts, toolCallsAccum, toolResultsAccum, thinkingBlocksAccum);
|
|
2783
2783
|
|
|
2784
|
-
|
|
2784
|
+
sendYeaftOutput({
|
|
2785
2785
|
type: 'assistant',
|
|
2786
2786
|
message: { content: [] },
|
|
2787
2787
|
}, envelope);
|
|
2788
|
-
|
|
2788
|
+
sendYeaftOutput({
|
|
2789
2789
|
type: 'result',
|
|
2790
2790
|
result_text: '',
|
|
2791
2791
|
}, envelope);
|
|
@@ -2795,7 +2795,7 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2795
2795
|
} catch (err) {
|
|
2796
2796
|
const isAbort = err && (err.name === 'AbortError' || err.name === 'LLMAbortError');
|
|
2797
2797
|
if (isAbort) {
|
|
2798
|
-
|
|
2798
|
+
sendYeaftOutput({
|
|
2799
2799
|
type: 'result',
|
|
2800
2800
|
result_text: '',
|
|
2801
2801
|
stopped: true,
|
|
@@ -2803,7 +2803,7 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2803
2803
|
return;
|
|
2804
2804
|
}
|
|
2805
2805
|
|
|
2806
|
-
console.error('[
|
|
2806
|
+
console.error('[Yeaft] query error:', err);
|
|
2807
2807
|
|
|
2808
2808
|
// vp-status: surface a transient `error` state so the row's status
|
|
2809
2809
|
// label flips red for the brief window before the outer finally
|
|
@@ -2813,13 +2813,13 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2813
2813
|
try {
|
|
2814
2814
|
getVpStatusBroker().transition({ groupId, vpId, threadId, title: thread?.title || '', state: 'error', turnId, messageCount: thread?.messageIds?.length || 0 });
|
|
2815
2815
|
} catch (brokerErr) {
|
|
2816
|
-
console.warn('[
|
|
2816
|
+
console.warn('[Yeaft] vp-status error transition failed:', brokerErr?.message || brokerErr);
|
|
2817
2817
|
}
|
|
2818
2818
|
|
|
2819
2819
|
if (isPermissionErrorMsg(err.message)) {
|
|
2820
2820
|
if (!_permissionDiagnosticSent) {
|
|
2821
2821
|
_permissionDiagnosticSent = true;
|
|
2822
|
-
|
|
2822
|
+
sendYeaftOutput({
|
|
2823
2823
|
type: 'assistant',
|
|
2824
2824
|
message: {
|
|
2825
2825
|
content: [{
|
|
@@ -2830,7 +2830,7 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2830
2830
|
}, envelope);
|
|
2831
2831
|
}
|
|
2832
2832
|
} else {
|
|
2833
|
-
|
|
2833
|
+
sendYeaftOutput({
|
|
2834
2834
|
type: 'assistant',
|
|
2835
2835
|
message: {
|
|
2836
2836
|
content: [{
|
|
@@ -2840,7 +2840,7 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2840
2840
|
},
|
|
2841
2841
|
}, envelope);
|
|
2842
2842
|
}
|
|
2843
|
-
|
|
2843
|
+
sendYeaftOutput({
|
|
2844
2844
|
type: 'result',
|
|
2845
2845
|
result_text: '',
|
|
2846
2846
|
}, envelope);
|
|
@@ -2852,7 +2852,7 @@ async function runVpTurn({ prompt, promptParts = null, groupId, vpId, threadId =
|
|
|
2852
2852
|
try {
|
|
2853
2853
|
getVpStatusBroker().settleIdle({ groupId, vpId, threadId: threadId || 'main', title: thread?.title || '' });
|
|
2854
2854
|
} catch (err) {
|
|
2855
|
-
console.warn('[
|
|
2855
|
+
console.warn('[Yeaft] vp-status settleIdle failed:', err?.message || err);
|
|
2856
2856
|
}
|
|
2857
2857
|
// fix-vp-multi-thread (bug 2): the bridge tracks per-thread status
|
|
2858
2858
|
// on `thread.status` separately from the broker. Multiple sites
|
|
@@ -2934,14 +2934,14 @@ function appendTurnToGroupHistory(groupId, threadId, prompts, assistantTextParts
|
|
|
2934
2934
|
/**
|
|
2935
2935
|
* Persist an inbound message row to disk EXACTLY ONCE per
|
|
2936
2936
|
* coordinator-ingest call, keyed by the coordinator-assigned `msgId`.
|
|
2937
|
-
* Both `
|
|
2937
|
+
* Both `handleYeaftGroupChat` (real user input, persists as
|
|
2938
2938
|
* role='user') and `enqueueForVp`'s driver loop (route_forward
|
|
2939
2939
|
* synthetic injections, persists as role='assistant' attributed via
|
|
2940
2940
|
* `speakerVpId`) call this — the Set guard makes either path the
|
|
2941
2941
|
* writer, whichever runs first, while the other becomes a no-op.
|
|
2942
2942
|
*
|
|
2943
2943
|
* Without this dedup, a 2-VP group prompt produces TWO `m{NNNN}.md`
|
|
2944
|
-
* user rows (one per engine) — `
|
|
2944
|
+
* user rows (one per engine) — `handleYeaftLoadHistory` then replays
|
|
2945
2945
|
* the user's prompt twice and sandwiches one VP's reply between two
|
|
2946
2946
|
* copies of the user message. Visually this reads as "messages out of
|
|
2947
2947
|
* order" because the second copy of the user prompt sits BETWEEN the
|
|
@@ -2992,7 +2992,7 @@ function persistInboundMessageOnceByMsgId({ msgId, text, groupId, threadId = 'ma
|
|
|
2992
2992
|
}
|
|
2993
2993
|
}
|
|
2994
2994
|
try {
|
|
2995
|
-
// role defaults to 'user' for back-compat:
|
|
2995
|
+
// role defaults to 'user' for back-compat: handleYeaftGroupChat's
|
|
2996
2996
|
// real-user call site passes no role and gets a user row. The driver
|
|
2997
2997
|
// loop passes role='assistant' + speakerVpId for route_forward
|
|
2998
2998
|
// injections so the on-disk record correctly attributes the text to
|
|
@@ -3018,7 +3018,7 @@ function persistInboundMessageOnceByMsgId({ msgId, text, groupId, threadId = 'ma
|
|
|
3018
3018
|
return true;
|
|
3019
3019
|
} catch (err) {
|
|
3020
3020
|
console.warn(
|
|
3021
|
-
'[
|
|
3021
|
+
'[Yeaft] persistInboundMessageOnceByMsgId failed (non-fatal):',
|
|
3022
3022
|
err?.message || err,
|
|
3023
3023
|
);
|
|
3024
3024
|
return false;
|
|
@@ -3026,24 +3026,24 @@ function persistInboundMessageOnceByMsgId({ msgId, text, groupId, threadId = 'ma
|
|
|
3026
3026
|
}
|
|
3027
3027
|
|
|
3028
3028
|
/**
|
|
3029
|
-
* Cleared on session reset (
|
|
3029
|
+
* Cleared on session reset (resetYeaftSession) so a fresh session
|
|
3030
3030
|
* starts with no stale msg-ids.
|
|
3031
3031
|
*/
|
|
3032
3032
|
const _persistedUserMsgIds = new Set();
|
|
3033
3033
|
|
|
3034
3034
|
// Per-group post-turn compaction lives on `session.compactor`
|
|
3035
|
-
// (`agent/
|
|
3035
|
+
// (`agent/yeaft/compact/compactor.js`), constructed in `session.js`.
|
|
3036
3036
|
// Bridge passes a per-call `historyHandle = { get, set }` and wires the
|
|
3037
|
-
// `
|
|
3038
|
-
// `
|
|
3037
|
+
// `yeaft_history_compacted` WS sink via `compactor.setOnCompacted` from
|
|
3038
|
+
// `installYeaftRuntimeBridge`. The bridge keeps history ownership; the
|
|
3039
3039
|
// Compactor owns single-flight, anti-starvation, race-guard, and the
|
|
3040
3040
|
// LLM summarize call.
|
|
3041
3041
|
|
|
3042
3042
|
/**
|
|
3043
3043
|
* Abort every in-flight VP turn and clear all queued envelopes across
|
|
3044
|
-
* every group/thread runtime. Shared by `
|
|
3045
|
-
* target thread is supplied and by `
|
|
3046
|
-
* strings into the supplied `aborted` array for the
|
|
3044
|
+
* every group/thread runtime. Shared by `handleYeaftAbortThread` when no
|
|
3045
|
+
* target thread is supplied and by `handleYeaftAbortAll`. Pushes `vp:<key>`
|
|
3046
|
+
* strings into the supplied `aborted` array for the yeaft_aborted event.
|
|
3047
3047
|
*
|
|
3048
3048
|
* @param {string[]} aborted — output array, mutated in place
|
|
3049
3049
|
*/
|
|
@@ -3066,7 +3066,7 @@ function abortAllVpRuntime(aborted) {
|
|
|
3066
3066
|
* @param {{ threadId?: string }} _msg
|
|
3067
3067
|
* @returns {{ aborted: string[], all: boolean }}
|
|
3068
3068
|
*/
|
|
3069
|
-
export function
|
|
3069
|
+
export function handleYeaftAbortThread(_msg = {}) {
|
|
3070
3070
|
const aborted = [];
|
|
3071
3071
|
const targetThreadId = _msg && typeof _msg.threadId === 'string' ? _msg.threadId : '';
|
|
3072
3072
|
|
|
@@ -3093,7 +3093,7 @@ export function handleUnifyAbortThread(_msg = {}) {
|
|
|
3093
3093
|
const thread = vpMap.get(targetThreadId);
|
|
3094
3094
|
if (thread) thread.status = 'aborted';
|
|
3095
3095
|
}
|
|
3096
|
-
|
|
3096
|
+
sendYeaftEvent({ type: 'yeaft_aborted', aborted, all: false, threadId: targetThreadId });
|
|
3097
3097
|
return { aborted, all: false };
|
|
3098
3098
|
}
|
|
3099
3099
|
|
|
@@ -3107,15 +3107,15 @@ export function handleUnifyAbortThread(_msg = {}) {
|
|
|
3107
3107
|
turnAbortCtrls.clear();
|
|
3108
3108
|
turnAbortMeta.clear();
|
|
3109
3109
|
abortAllVpRuntime(aborted);
|
|
3110
|
-
|
|
3110
|
+
sendYeaftEvent({ type: 'yeaft_aborted', aborted, all: false });
|
|
3111
3111
|
return { aborted, all: false };
|
|
3112
3112
|
}
|
|
3113
3113
|
|
|
3114
3114
|
/**
|
|
3115
|
-
* Abort all in-flight
|
|
3115
|
+
* Abort all in-flight Yeaft runtime work.
|
|
3116
3116
|
* @returns {{ aborted: string[], all: boolean }}
|
|
3117
3117
|
*/
|
|
3118
|
-
export function
|
|
3118
|
+
export function handleYeaftAbortAll() {
|
|
3119
3119
|
const aborted = [];
|
|
3120
3120
|
if (currentAbortCtrl && !currentAbortCtrl.signal.aborted) {
|
|
3121
3121
|
try { currentAbortCtrl.abort(); aborted.push('main'); } catch { /* best-effort */ }
|
|
@@ -3128,19 +3128,19 @@ export function handleUnifyAbortAll() {
|
|
|
3128
3128
|
turnAbortCtrls.clear();
|
|
3129
3129
|
turnAbortMeta.clear();
|
|
3130
3130
|
abortAllVpRuntime(aborted);
|
|
3131
|
-
|
|
3131
|
+
sendYeaftEvent({ type: 'yeaft_aborted', aborted, all: true });
|
|
3132
3132
|
return { aborted, all: true };
|
|
3133
3133
|
}
|
|
3134
3134
|
|
|
3135
3135
|
/**
|
|
3136
3136
|
* Per-VP abort: stops a single VP turn by turnId without affecting siblings.
|
|
3137
|
-
* Frontend sends `{ type: '
|
|
3137
|
+
* Frontend sends `{ type: 'yeaft_abort_turn', turnId }`.
|
|
3138
3138
|
* @param {{ turnId?: string }} msg
|
|
3139
3139
|
*/
|
|
3140
|
-
export function
|
|
3140
|
+
export function handleYeaftAbortTurn(msg = {}) {
|
|
3141
3141
|
const { turnId } = msg;
|
|
3142
3142
|
if (!turnId) {
|
|
3143
|
-
|
|
3143
|
+
sendYeaftEvent({ type: 'yeaft_turn_aborted', turnId: null, success: false });
|
|
3144
3144
|
return;
|
|
3145
3145
|
}
|
|
3146
3146
|
const ctrl = turnAbortCtrls.get(turnId);
|
|
@@ -3148,11 +3148,11 @@ export function handleUnifyAbortTurn(msg = {}) {
|
|
|
3148
3148
|
try { ctrl.abort(); } catch { /* best-effort */ }
|
|
3149
3149
|
turnAbortCtrls.delete(turnId);
|
|
3150
3150
|
turnAbortMeta.delete(turnId);
|
|
3151
|
-
|
|
3151
|
+
sendYeaftEvent({ type: 'yeaft_turn_aborted', turnId, success: true });
|
|
3152
3152
|
} else {
|
|
3153
3153
|
turnAbortCtrls.delete(turnId);
|
|
3154
3154
|
turnAbortMeta.delete(turnId);
|
|
3155
|
-
|
|
3155
|
+
sendYeaftEvent({ type: 'yeaft_turn_aborted', turnId, success: false });
|
|
3156
3156
|
}
|
|
3157
3157
|
}
|
|
3158
3158
|
|
|
@@ -3160,11 +3160,11 @@ export function handleUnifyAbortTurn(msg = {}) {
|
|
|
3160
3160
|
* Unified abort entry: routes by payload shape.
|
|
3161
3161
|
* @param {{ threadId?: string, all?: boolean }} [opts]
|
|
3162
3162
|
*/
|
|
3163
|
-
export function
|
|
3164
|
-
if (opts && opts.all) return
|
|
3165
|
-
if (opts && opts.threadId) return
|
|
3163
|
+
export function abortYeaftSession(opts = {}) {
|
|
3164
|
+
if (opts && opts.all) return handleYeaftAbortAll();
|
|
3165
|
+
if (opts && opts.threadId) return handleYeaftAbortThread({ threadId: opts.threadId });
|
|
3166
3166
|
// No payload — conservative no-op ack.
|
|
3167
|
-
|
|
3167
|
+
sendYeaftEvent({ type: 'yeaft_aborted', aborted: [], all: false });
|
|
3168
3168
|
return { aborted: [], all: false };
|
|
3169
3169
|
}
|
|
3170
3170
|
|
|
@@ -3206,7 +3206,7 @@ export function __testGetRegisteredThreadIds() {
|
|
|
3206
3206
|
/**
|
|
3207
3207
|
* Test-only: expose the bridge-level escalation helper. Lets tests verify
|
|
3208
3208
|
* the "tool ignored signal → wrapper escalates" contract without booting a
|
|
3209
|
-
* full session. See `test/agent/
|
|
3209
|
+
* full session. See `test/agent/yeaft/web-bridge-escalation.test.js`.
|
|
3210
3210
|
*/
|
|
3211
3211
|
export const __testRaceWithEscalation = raceWithEscalation;
|
|
3212
3212
|
|
|
@@ -3215,11 +3215,11 @@ export const __testRaceWithEscalation = raceWithEscalation;
|
|
|
3215
3215
|
*
|
|
3216
3216
|
* Two call shapes, both routed through this single handler:
|
|
3217
3217
|
*
|
|
3218
|
-
* { type: '
|
|
3218
|
+
* { type: 'yeaft_dream_trigger', vpId } — per-VP trigger (legacy
|
|
3219
3219
|
* VP-detail page button). Fires an unscoped dream pass; the result
|
|
3220
3220
|
* event is tagged with `vpId` so the per-VP store row updates.
|
|
3221
3221
|
*
|
|
3222
|
-
* { type: '
|
|
3222
|
+
* { type: 'yeaft_dream_trigger', groupId } — per-GROUP trigger (new
|
|
3223
3223
|
* in v0.1.754 — added so users can manually kick dream for a group
|
|
3224
3224
|
* after seeing the Resident layer stuck on the bootstrap seed).
|
|
3225
3225
|
* Fires a scope-filtered pass via `triggerDreamForScopes(['group/X'])`
|
|
@@ -3270,7 +3270,7 @@ export function normalizeDreamResult(result) {
|
|
|
3270
3270
|
};
|
|
3271
3271
|
}
|
|
3272
3272
|
|
|
3273
|
-
export async function
|
|
3273
|
+
export async function handleYeaftDreamTrigger(msg = {}) {
|
|
3274
3274
|
// Resolve tag up-front so EVERY outbound envelope (including the
|
|
3275
3275
|
// scheduler-uninitialised early-return below) carries `groupId` /
|
|
3276
3276
|
// `vpId`. Without this the frontend's `applyDreamResult` couldn't
|
|
@@ -3284,7 +3284,7 @@ export async function handleUnifyDreamTrigger(msg = {}) {
|
|
|
3284
3284
|
if (!session?.dreamScheduler) {
|
|
3285
3285
|
const error = 'Dream scheduler not initialized — session not loaded.';
|
|
3286
3286
|
sendToServer({
|
|
3287
|
-
type: '
|
|
3287
|
+
type: 'yeaft_dream_result',
|
|
3288
3288
|
...tag,
|
|
3289
3289
|
...normalizeDreamResult({ error }),
|
|
3290
3290
|
});
|
|
@@ -3310,7 +3310,7 @@ export async function handleUnifyDreamTrigger(msg = {}) {
|
|
|
3310
3310
|
trigger: msg.manual === false ? 'auto' : 'manual',
|
|
3311
3311
|
};
|
|
3312
3312
|
sendToServer({
|
|
3313
|
-
type: '
|
|
3313
|
+
type: 'yeaft_dream_result',
|
|
3314
3314
|
...tag,
|
|
3315
3315
|
...skippedResult,
|
|
3316
3316
|
...normalizeDreamResult(skippedResult),
|
|
@@ -3341,7 +3341,7 @@ export async function handleUnifyDreamTrigger(msg = {}) {
|
|
|
3341
3341
|
|
|
3342
3342
|
try {
|
|
3343
3343
|
sendToServer({
|
|
3344
|
-
type: '
|
|
3344
|
+
type: 'yeaft_dream_status',
|
|
3345
3345
|
...tag,
|
|
3346
3346
|
status: 'running',
|
|
3347
3347
|
});
|
|
@@ -3361,15 +3361,15 @@ export async function handleUnifyDreamTrigger(msg = {}) {
|
|
|
3361
3361
|
// mode of the alternative ordering is silent — review feedback from
|
|
3362
3362
|
// PR #743.
|
|
3363
3363
|
//
|
|
3364
|
-
// This `
|
|
3365
|
-
// a dream pass. The chat-store projects it into BOTH `
|
|
3366
|
-
// (final tally row) AND `
|
|
3364
|
+
// This `yeaft_dream_result` envelope is the SOLE terminal signal for
|
|
3365
|
+
// a dream pass. The chat-store projects it into BOTH `yeaftDreamLatest`
|
|
3366
|
+
// (final tally row) AND `yeaftDreamEvents` (ring-buffer terminal
|
|
3367
3367
|
// marker), so we no longer mirror a synthetic `phase:'result'`
|
|
3368
3368
|
// dream_progress event — that mirror used to race the
|
|
3369
|
-
// `
|
|
3369
|
+
// `yeaftDreamLatest` writer and flip the success row back to
|
|
3370
3370
|
// 'running' (Critical reviewer finding pre-merge).
|
|
3371
3371
|
sendToServer({
|
|
3372
|
-
type: '
|
|
3372
|
+
type: 'yeaft_dream_result',
|
|
3373
3373
|
...tag,
|
|
3374
3374
|
...result,
|
|
3375
3375
|
...normalized,
|
|
@@ -3377,7 +3377,7 @@ export async function handleUnifyDreamTrigger(msg = {}) {
|
|
|
3377
3377
|
} catch (err) {
|
|
3378
3378
|
const error = err?.message || String(err);
|
|
3379
3379
|
sendToServer({
|
|
3380
|
-
type: '
|
|
3380
|
+
type: 'yeaft_dream_result',
|
|
3381
3381
|
...tag,
|
|
3382
3382
|
...normalizeDreamResult({ error }),
|
|
3383
3383
|
});
|
|
@@ -3392,9 +3392,9 @@ export async function handleUnifyDreamTrigger(msg = {}) {
|
|
|
3392
3392
|
}
|
|
3393
3393
|
|
|
3394
3394
|
/**
|
|
3395
|
-
* 2026-05-13: serve the
|
|
3395
|
+
* 2026-05-13: serve the Yeaft debug drawer's "Tool Stats" panel.
|
|
3396
3396
|
*
|
|
3397
|
-
* Replies with `{type: '
|
|
3397
|
+
* Replies with `{type: 'yeaft_tool_stats', snapshot, registered,
|
|
3398
3398
|
* unused}`. `snapshot` is the `ToolUsageStats.snapshot()` keyed by
|
|
3399
3399
|
* tool name (callCount, errorCount, p50Ms, p95Ms, avgMs, lastCalledAt,
|
|
3400
3400
|
* lastError, errorRate). `registered` is the static list of built-in
|
|
@@ -3405,7 +3405,7 @@ export async function handleUnifyDreamTrigger(msg = {}) {
|
|
|
3405
3405
|
* missing, we still reply with an empty snapshot so the UI can render
|
|
3406
3406
|
* a placeholder rather than spin forever.
|
|
3407
3407
|
*/
|
|
3408
|
-
export async function
|
|
3408
|
+
export async function handleYeaftFetchToolStats(_msg = {}) {
|
|
3409
3409
|
let snapshot = {};
|
|
3410
3410
|
let registered = [];
|
|
3411
3411
|
let unused = [];
|
|
@@ -3427,7 +3427,7 @@ export async function handleUnifyFetchToolStats(_msg = {}) {
|
|
|
3427
3427
|
}
|
|
3428
3428
|
} catch (err) {
|
|
3429
3429
|
sendToServer({
|
|
3430
|
-
type: '
|
|
3430
|
+
type: 'yeaft_tool_stats',
|
|
3431
3431
|
snapshot: {},
|
|
3432
3432
|
registered: [],
|
|
3433
3433
|
unused: [],
|
|
@@ -3436,7 +3436,7 @@ export async function handleUnifyFetchToolStats(_msg = {}) {
|
|
|
3436
3436
|
return;
|
|
3437
3437
|
}
|
|
3438
3438
|
sendToServer({
|
|
3439
|
-
type: '
|
|
3439
|
+
type: 'yeaft_tool_stats',
|
|
3440
3440
|
snapshot,
|
|
3441
3441
|
registered,
|
|
3442
3442
|
unused,
|
|
@@ -3444,8 +3444,8 @@ export async function handleUnifyFetchToolStats(_msg = {}) {
|
|
|
3444
3444
|
}
|
|
3445
3445
|
|
|
3446
3446
|
/**
|
|
3447
|
-
* Hydrate the
|
|
3448
|
-
* panel state (`
|
|
3447
|
+
* Hydrate the YeaftDebugPanel from the persistent SQLite trace. The
|
|
3448
|
+
* panel state (`yeaftDebugLoops` / `yeaftDebugTurnsById`) is otherwise
|
|
3449
3449
|
* built ONLY from in-flight `loop` / `turn_open` events on the wire,
|
|
3450
3450
|
* so a panel opened after a turn has finished sees nothing for that
|
|
3451
3451
|
* turn. This handler ships back a frontend-shaped snapshot the store
|
|
@@ -3457,12 +3457,12 @@ export async function handleUnifyFetchToolStats(_msg = {}) {
|
|
|
3457
3457
|
* - `threadId` — narrow by thread
|
|
3458
3458
|
*
|
|
3459
3459
|
* Sends:
|
|
3460
|
-
* { type: '
|
|
3460
|
+
* { type: 'yeaft_debug_history', loops: [...], turns: [...] }
|
|
3461
3461
|
*
|
|
3462
3462
|
* Best-effort: if the session / trace isn't ready, sends an empty
|
|
3463
3463
|
* snapshot so the panel renders a placeholder instead of spinning.
|
|
3464
3464
|
*/
|
|
3465
|
-
export async function
|
|
3465
|
+
export async function handleYeaftFetchDebugHistory(msg = {}) {
|
|
3466
3466
|
const limit = Number.isFinite(msg?.limit) ? Number(msg.limit) : 100;
|
|
3467
3467
|
const dreamLimit = Number.isFinite(msg?.dreamLimit) ? Number(msg.dreamLimit) : 5;
|
|
3468
3468
|
const groupId = typeof msg?.groupId === 'string' && msg.groupId ? msg.groupId : null;
|
|
@@ -3479,7 +3479,7 @@ export async function handleUnifyFetchDebugHistory(msg = {}) {
|
|
|
3479
3479
|
}
|
|
3480
3480
|
} catch (err) {
|
|
3481
3481
|
sendToServer({
|
|
3482
|
-
type: '
|
|
3482
|
+
type: 'yeaft_debug_history',
|
|
3483
3483
|
loops: [],
|
|
3484
3484
|
turns: [],
|
|
3485
3485
|
dreamEvents: [],
|
|
@@ -3488,7 +3488,7 @@ export async function handleUnifyFetchDebugHistory(msg = {}) {
|
|
|
3488
3488
|
return;
|
|
3489
3489
|
}
|
|
3490
3490
|
sendToServer({
|
|
3491
|
-
type: '
|
|
3491
|
+
type: 'yeaft_debug_history',
|
|
3492
3492
|
loops,
|
|
3493
3493
|
turns,
|
|
3494
3494
|
dreamEvents,
|
|
@@ -3497,26 +3497,26 @@ export async function handleUnifyFetchDebugHistory(msg = {}) {
|
|
|
3497
3497
|
});
|
|
3498
3498
|
}
|
|
3499
3499
|
|
|
3500
|
-
/** Deprecated mode switch —
|
|
3501
|
-
export function
|
|
3502
|
-
console.warn('[
|
|
3500
|
+
/** Deprecated mode switch — Yeaft is single-mode. */
|
|
3501
|
+
export function handleYeaftModeSwitch(_msg) {
|
|
3502
|
+
console.warn('[Yeaft] yeaft_mode_switch is deprecated and ignored — Yeaft now runs in a single unified mode.');
|
|
3503
3503
|
}
|
|
3504
3504
|
|
|
3505
3505
|
|
|
3506
3506
|
/** Handle model switch from the web UI. */
|
|
3507
|
-
export function
|
|
3507
|
+
export function handleYeaftModelSwitch(msg) {
|
|
3508
3508
|
if (!session || !msg.model) return;
|
|
3509
3509
|
|
|
3510
3510
|
const available = session.config.availableModels || [];
|
|
3511
3511
|
const found = available.some(m => m.id === msg.model);
|
|
3512
3512
|
if (!found) {
|
|
3513
|
-
console.warn(`[
|
|
3513
|
+
console.warn(`[Yeaft] model switch rejected — "${msg.model}" not in availableModels`);
|
|
3514
3514
|
return;
|
|
3515
3515
|
}
|
|
3516
3516
|
|
|
3517
3517
|
session.config.model = msg.model;
|
|
3518
3518
|
|
|
3519
|
-
|
|
3519
|
+
sendYeaftEvent({
|
|
3520
3520
|
type: 'model_switched',
|
|
3521
3521
|
model: msg.model,
|
|
3522
3522
|
});
|
|
@@ -3532,7 +3532,7 @@ export function handleUnifyModelSwitch(msg) {
|
|
|
3532
3532
|
* groupId at all — are excluded so a stale `grp_default` (or any other
|
|
3533
3533
|
* group) never bleeds into the active group's pane.
|
|
3534
3534
|
*/
|
|
3535
|
-
export async function
|
|
3535
|
+
export async function handleYeaftLoadHistory(msg) {
|
|
3536
3536
|
const groupId = (msg && typeof msg.groupId === 'string' && msg.groupId) || null;
|
|
3537
3537
|
// `lim` is now expressed in TURNS, not raw messages. `loadRecent` and
|
|
3538
3538
|
// `loadRecentByGroup` use turn-based slicing so the cut never lands
|
|
@@ -3549,9 +3549,9 @@ export async function handleUnifyLoadHistory(msg) {
|
|
|
3549
3549
|
skipSkills: false,
|
|
3550
3550
|
serverMode: true,
|
|
3551
3551
|
});
|
|
3552
|
-
|
|
3552
|
+
installYeaftRuntimeBridge(session);
|
|
3553
3553
|
|
|
3554
|
-
|
|
3554
|
+
yeaftConversationId = `yeaft-${Date.now()}`;
|
|
3555
3555
|
|
|
3556
3556
|
// Per-group history hydrates lazily via getOrCreateGroupHistory.
|
|
3557
3557
|
// When the load-history call carries a groupId, force-refresh THAT
|
|
@@ -3567,9 +3567,9 @@ export async function handleUnifyLoadHistory(msg) {
|
|
|
3567
3567
|
}
|
|
3568
3568
|
|
|
3569
3569
|
// Always replay session_ready so refresh / reconnect rebuilds UI state.
|
|
3570
|
-
|
|
3570
|
+
sendYeaftEvent({
|
|
3571
3571
|
type: 'session_ready',
|
|
3572
|
-
conversationId:
|
|
3572
|
+
conversationId: yeaftConversationId,
|
|
3573
3573
|
model: session.config.model,
|
|
3574
3574
|
availableModels: session.config.availableModels || [],
|
|
3575
3575
|
skills: session.status.skills,
|
|
@@ -3584,13 +3584,13 @@ export async function handleUnifyLoadHistory(msg) {
|
|
|
3584
3584
|
try {
|
|
3585
3585
|
getVpStatusBroker().broadcastSnapshot();
|
|
3586
3586
|
} catch (err) {
|
|
3587
|
-
console.warn('[
|
|
3587
|
+
console.warn('[Yeaft] vp-status snapshot broadcast (replay) failed:', err?.message || err);
|
|
3588
3588
|
}
|
|
3589
3589
|
|
|
3590
3590
|
// `msg.limit` is the replay-scrollback request from the frontend (UI
|
|
3591
3591
|
// history pane, not engine context). Keep the bootstrap window small so
|
|
3592
3592
|
// opening a group can paint the latest messages quickly; older rows are
|
|
3593
|
-
// paged via `
|
|
3593
|
+
// paged via `yeaft_load_more_history` when the user scrolls upward.
|
|
3594
3594
|
const limit = (typeof msg.limit === 'number') ? msg.limit : 10;
|
|
3595
3595
|
const visiblePage = groupId
|
|
3596
3596
|
? loadVisibleGroupHistoryPage(session.conversationStore, groupId, limit)
|
|
@@ -3607,7 +3607,7 @@ export async function handleUnifyLoadHistory(msg) {
|
|
|
3607
3607
|
|
|
3608
3608
|
for (const entry of replayEntries) {
|
|
3609
3609
|
if (entry.role === 'user') {
|
|
3610
|
-
|
|
3610
|
+
sendYeaftOutput({
|
|
3611
3611
|
type: 'user',
|
|
3612
3612
|
message: {
|
|
3613
3613
|
content: entry.content,
|
|
@@ -3627,12 +3627,12 @@ export async function handleUnifyLoadHistory(msg) {
|
|
|
3627
3627
|
turnId: entry.turnId || entry.threadId || 'main',
|
|
3628
3628
|
};
|
|
3629
3629
|
if (entry.speakerVpId) envelopeOpts.vpId = entry.speakerVpId;
|
|
3630
|
-
|
|
3630
|
+
sendYeaftOutput({
|
|
3631
3631
|
type: 'assistant',
|
|
3632
3632
|
message: { id: entry.id || null, content: [{ type: 'text', text: entry.content }] },
|
|
3633
3633
|
ts: entry.ts || null,
|
|
3634
3634
|
}, envelopeOpts);
|
|
3635
|
-
|
|
3635
|
+
sendYeaftOutput({ type: 'result', result_text: '' }, envelopeOpts);
|
|
3636
3636
|
}
|
|
3637
3637
|
}
|
|
3638
3638
|
|
|
@@ -3657,7 +3657,7 @@ export async function handleUnifyLoadHistory(msg) {
|
|
|
3657
3657
|
hasCompactSummaryFlag = session.conversationStore.hasAnyCompactSummaryForGroup(groupId);
|
|
3658
3658
|
}
|
|
3659
3659
|
|
|
3660
|
-
|
|
3660
|
+
sendYeaftEvent({
|
|
3661
3661
|
type: 'history_loaded',
|
|
3662
3662
|
count: replayEntries.length,
|
|
3663
3663
|
hasCompactSummary: hasCompactSummaryFlag,
|
|
@@ -3672,21 +3672,21 @@ export async function handleUnifyLoadHistory(msg) {
|
|
|
3672
3672
|
/**
|
|
3673
3673
|
* Handle a "load older messages" pagination request. Reads `turns` more
|
|
3674
3674
|
* turns of history strictly older than `beforeSeq` for `groupId`, and
|
|
3675
|
-
* emits them in a single `
|
|
3676
|
-
* `
|
|
3675
|
+
* emits them in a single `yeaft_history_chunk` envelope (NOT a
|
|
3676
|
+
* `yeaft_output` — that pipeline appends, but the frontend needs to
|
|
3677
3677
|
* PREPEND these older messages above what it already has).
|
|
3678
3678
|
*
|
|
3679
3679
|
* Tool replay is NOT included in this PR — same projection as
|
|
3680
|
-
* `
|
|
3680
|
+
* `handleYeaftLoadHistory` (user / assistant text only). On any internal
|
|
3681
3681
|
* failure we still emit an empty chunk so the spinner clears.
|
|
3682
3682
|
*
|
|
3683
3683
|
* @param {object} msg — { groupId, beforeSeq, turns }
|
|
3684
3684
|
*/
|
|
3685
|
-
export async function
|
|
3685
|
+
export async function handleYeaftLoadMoreHistory(msg) {
|
|
3686
3686
|
const groupId = (msg && typeof msg.groupId === 'string' && msg.groupId) || null;
|
|
3687
3687
|
const emit = (payload) => sendToServer({
|
|
3688
|
-
type: '
|
|
3689
|
-
conversationId:
|
|
3688
|
+
type: 'yeaft_history_chunk',
|
|
3689
|
+
conversationId: yeaftConversationId,
|
|
3690
3690
|
groupId,
|
|
3691
3691
|
...payload,
|
|
3692
3692
|
});
|
|
@@ -3703,11 +3703,11 @@ export async function handleUnifyLoadMoreHistory(msg) {
|
|
|
3703
3703
|
try {
|
|
3704
3704
|
result = loadVisibleGroupHistoryPage(session.conversationStore, groupId, turns, beforeSeq);
|
|
3705
3705
|
} catch (err) {
|
|
3706
|
-
console.error('[
|
|
3706
|
+
console.error('[Yeaft] loadOlderByGroup failed:', err.message);
|
|
3707
3707
|
result = { messages: [], oldestSeq: null, hasMore: false };
|
|
3708
3708
|
}
|
|
3709
3709
|
|
|
3710
|
-
// Wire shape mirrors
|
|
3710
|
+
// Wire shape mirrors handleYeaftLoadHistory's projection: only visible
|
|
3711
3711
|
// user / assistant text rows. Internal reflection/system-only rows stay
|
|
3712
3712
|
// server-side, and stable ids + speaker attribution ride with each row
|
|
3713
3713
|
// so older-history prepend renders exactly like refresh replay.
|
|
@@ -3732,10 +3732,10 @@ export async function handleUnifyLoadMoreHistory(msg) {
|
|
|
3732
3732
|
}
|
|
3733
3733
|
|
|
3734
3734
|
/**
|
|
3735
|
-
* Reset
|
|
3735
|
+
* Reset Yeaft session. Aborts the in-flight controller, tears down the
|
|
3736
3736
|
* session, then re-initialises so the frontend gets fresh config.
|
|
3737
3737
|
*/
|
|
3738
|
-
export async function
|
|
3738
|
+
export async function resetYeaftSession() {
|
|
3739
3739
|
if (currentAbortCtrl && !currentAbortCtrl.signal.aborted) {
|
|
3740
3740
|
try { currentAbortCtrl.abort(); } catch { /* ignore */ }
|
|
3741
3741
|
}
|
|
@@ -3748,7 +3748,7 @@ export async function resetUnifySession() {
|
|
|
3748
3748
|
await session.shutdown();
|
|
3749
3749
|
session = null;
|
|
3750
3750
|
}
|
|
3751
|
-
|
|
3751
|
+
yeaftConversationId = null;
|
|
3752
3752
|
// Per-group histories live on groupContexts entries — clearing the
|
|
3753
3753
|
// map (a few lines below) drops every group's history with it. No
|
|
3754
3754
|
// separate global tape to clear.
|
|
@@ -3783,7 +3783,7 @@ export async function resetUnifySession() {
|
|
|
3783
3783
|
try {
|
|
3784
3784
|
getVpStatusBroker().reset();
|
|
3785
3785
|
} catch (err) {
|
|
3786
|
-
console.warn('[
|
|
3786
|
+
console.warn('[Yeaft] vp-status broker reset failed:', err?.message || err);
|
|
3787
3787
|
}
|
|
3788
3788
|
|
|
3789
3789
|
try {
|
|
@@ -3794,16 +3794,16 @@ export async function resetUnifySession() {
|
|
|
3794
3794
|
skipSkills: false,
|
|
3795
3795
|
serverMode: true,
|
|
3796
3796
|
});
|
|
3797
|
-
|
|
3797
|
+
installYeaftRuntimeBridge(session);
|
|
3798
3798
|
|
|
3799
|
-
|
|
3799
|
+
yeaftConversationId = `yeaft-${Date.now()}`;
|
|
3800
3800
|
|
|
3801
3801
|
// Per-group history hydrates lazily via getOrCreateGroupHistory on
|
|
3802
3802
|
// first read. Nothing to seed here.
|
|
3803
3803
|
|
|
3804
|
-
|
|
3804
|
+
sendYeaftEvent({
|
|
3805
3805
|
type: 'session_ready',
|
|
3806
|
-
conversationId:
|
|
3806
|
+
conversationId: yeaftConversationId,
|
|
3807
3807
|
model: session.config.model,
|
|
3808
3808
|
availableModels: session.config.availableModels || [],
|
|
3809
3809
|
skills: session.status.skills,
|
|
@@ -3817,9 +3817,9 @@ export async function resetUnifySession() {
|
|
|
3817
3817
|
try {
|
|
3818
3818
|
getVpStatusBroker().broadcastSnapshot();
|
|
3819
3819
|
} catch (err) {
|
|
3820
|
-
console.warn('[
|
|
3820
|
+
console.warn('[Yeaft] vp-status snapshot broadcast (reset) failed:', err?.message || err);
|
|
3821
3821
|
}
|
|
3822
3822
|
} catch (err) {
|
|
3823
|
-
console.error('[
|
|
3823
|
+
console.error('[Yeaft] Failed to re-initialize session after reset:', err.message);
|
|
3824
3824
|
}
|
|
3825
3825
|
}
|