@yeaft/webchat-agent 1.0.506 → 1.0.508
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/local-runtime/server/handlers/client-workbench.js +7 -1
- package/local-runtime/server/workbench-route.js +11 -4
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +88 -88
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +1 -1
- package/package.json +1 -1
- package/yeaft/engine.js +9 -4
- package/yeaft/history-window.js +76 -25
- package/yeaft/prompts.js +3 -0
- package/yeaft/session.js +16 -45
- package/yeaft/sub-agent/runner.js +6 -4
|
Binary file
|
package/package.json
CHANGED
package/yeaft/engine.js
CHANGED
|
@@ -2103,11 +2103,16 @@ export class Engine {
|
|
|
2103
2103
|
...(internalTrigger ? { internal: true } : { userAuthored: true }),
|
|
2104
2104
|
}, { sessionId: runtimeSessionId });
|
|
2105
2105
|
}
|
|
2106
|
-
// Native Session history, including internal wakeups,
|
|
2107
|
-
//
|
|
2106
|
+
// Native Session history, including internal wakeups, comes from the
|
|
2107
|
+
// canonical message transcript. Dream memory loading is temporarily
|
|
2108
|
+
// disabled for every scenario (including Work Center and child agents)
|
|
2109
|
+
// while message-history recall is evaluated as its replacement. Keep the
|
|
2110
|
+
// downstream memory pipeline intact behind this single switch so it can be
|
|
2111
|
+
// restored without migrating or deleting persisted memory data.
|
|
2108
2112
|
const useMessageHistory = scenario !== 'work-item' && !!runtimeSessionId && !vpPersona?.subAgent;
|
|
2109
|
-
const useDreamMemory = scenario === 'work-item' || !!vpPersona?.subAgent
|
|
2110
|
-
|
|
2113
|
+
// const useDreamMemory = scenario === 'work-item' || !!vpPersona?.subAgent
|
|
2114
|
+
// || (!runtimeSessionId && !internalTrigger);
|
|
2115
|
+
const useDreamMemory = false;
|
|
2111
2116
|
const recentTurnCap = this.#config.yeaft?.recentTurnsLimit ?? 20;
|
|
2112
2117
|
const relatedTurnCap = this.#config.yeaft?.relatedTurnsLimit ?? 8;
|
|
2113
2118
|
let relatedHistoryTurns = [];
|
package/yeaft/history-window.js
CHANGED
|
@@ -27,6 +27,7 @@ export const DEFAULT_RUNTIME_CACHE_TURN_CAP = 25;
|
|
|
27
27
|
export const DEFAULT_RUNTIME_CACHE_TOKEN_BUDGET = 32768;
|
|
28
28
|
export const DEFAULT_RUNTIME_CACHE_MESSAGE_CAP = 256;
|
|
29
29
|
|
|
30
|
+
const MINIMUM_RECENT_PROVIDER_TURNS = 20;
|
|
30
31
|
const IMAGE_PART_TOKEN_COST = 1024;
|
|
31
32
|
const DOCUMENT_PART_TOKEN_COST = 2048;
|
|
32
33
|
const CONTENT_PART_FRAME_TOKENS = 2;
|
|
@@ -565,10 +566,11 @@ function truncateToolResultsForModel(messages, options = {}) {
|
|
|
565
566
|
|
|
566
567
|
const HISTORY_SOURCE_INDEX = Symbol('historySourceIndex');
|
|
567
568
|
|
|
568
|
-
function withHistorySourceIndexes(messages) {
|
|
569
|
+
function withHistorySourceIndexes(messages, offset = 0) {
|
|
569
570
|
return messages.map((message, index) => (
|
|
570
571
|
message && typeof message === 'object'
|
|
571
|
-
? { ...message, [HISTORY_SOURCE_INDEX]:
|
|
572
|
+
? { ...message, [HISTORY_SOURCE_INDEX]: Number.isInteger(message[HISTORY_SOURCE_INDEX])
|
|
573
|
+
? message[HISTORY_SOURCE_INDEX] : offset + index }
|
|
572
574
|
: message
|
|
573
575
|
));
|
|
574
576
|
}
|
|
@@ -795,12 +797,13 @@ function bucketTextMessages(messages) {
|
|
|
795
797
|
}
|
|
796
798
|
|
|
797
799
|
function bucketTurn(messages, index, supplied = {}) {
|
|
798
|
-
const
|
|
799
|
-
const
|
|
800
|
-
const
|
|
800
|
+
const indexedMessages = withHistorySourceIndexes(messages, index ?? 0);
|
|
801
|
+
const user = indexedMessages.find(bucketUserBoundary);
|
|
802
|
+
const sourceIds = [...new Set(indexedMessages.flatMap(bucketSourceIds))];
|
|
803
|
+
const keys = [...new Set(indexedMessages.filter(bucketUserBoundary).flatMap(bucketUserKeys))];
|
|
801
804
|
if (supplied.id) keys.push(`turn:${supplied.id}`);
|
|
802
805
|
if (keys.length === 0 && index != null) keys.push(`snapshot:${index}`);
|
|
803
|
-
const text = bucketTextMessages(
|
|
806
|
+
const text = bucketTextMessages(indexedMessages);
|
|
804
807
|
return {
|
|
805
808
|
...supplied,
|
|
806
809
|
id: supplied.id || keys[0] || `snapshot:${index}`,
|
|
@@ -808,7 +811,7 @@ function bucketTurn(messages, index, supplied = {}) {
|
|
|
808
811
|
sourceIds,
|
|
809
812
|
userSeq: Number.isFinite(supplied.userSeq) ? supplied.userSeq : bucketSequence(user),
|
|
810
813
|
index,
|
|
811
|
-
messages,
|
|
814
|
+
messages: indexedMessages,
|
|
812
815
|
text,
|
|
813
816
|
tokens: estimateMessagesTokens(text),
|
|
814
817
|
};
|
|
@@ -893,19 +896,20 @@ function describeBucket(turns, messages = turns.flatMap(turn => turn.text)) {
|
|
|
893
896
|
|
|
894
897
|
/**
|
|
895
898
|
* Recompute provider history from untrimmed candidates; never mutate/cache the
|
|
896
|
-
* result in the transcript. Past human
|
|
897
|
-
*
|
|
899
|
+
* result in the transcript. Past human turn boundaries are retained when the
|
|
900
|
+
* configured recent window fits; tools are optional enrichment, newest first.
|
|
898
901
|
*
|
|
899
902
|
* The active turn is outside both buckets and consumes the global budget first.
|
|
900
|
-
* Its opening user row is protected
|
|
901
|
-
*
|
|
902
|
-
*
|
|
903
|
+
* Its opening user row is protected. Oversized recent text is fitted only in the
|
|
904
|
+
* disposable provider copy so one large turn cannot evict the rest of the recent
|
|
905
|
+
* window. Related recall remains optional and complete. External recall must have
|
|
906
|
+
* comparable userSeq/source identities to establish
|
|
903
907
|
* that it predates recent/current history; unknown chronology fails closed.
|
|
904
908
|
*
|
|
905
909
|
* @param {Array<object>} snapshot Untrimmed history plus the active execution.
|
|
906
910
|
* @param {{ prompt?: string, relatedTurns?: Array<object>, recentTurnCap?: number,
|
|
907
911
|
* relatedTurnCap?: number, messageTokenBudget?: number, maxMessageCount?: number,
|
|
908
|
-
* language?: string, currentTurnStartIndex?: number,
|
|
912
|
+
* keepToolTurns?: number, language?: string, currentTurnStartIndex?: number,
|
|
909
913
|
* scoreTurn?: (turn: object, prompt: string) => (number|object) }} [options]
|
|
910
914
|
* @returns {{messages: Array<object>, meta: object}}
|
|
911
915
|
*/
|
|
@@ -915,6 +919,7 @@ export function buildHistoryBuckets(snapshot, options = {}) {
|
|
|
915
919
|
const messageCap = bucketCap(options.maxMessageCount, DEFAULT_RUNTIME_CACHE_MESSAGE_CAP);
|
|
916
920
|
const recentCap = bucketCap(options.recentTurnCap, 20);
|
|
917
921
|
const relatedCap = bucketCap(options.relatedTurnCap, 8, 10);
|
|
922
|
+
const keepToolTurns = bucketCap(options.keepToolTurns, DEFAULT_KEEP_TOOL_TURNS);
|
|
918
923
|
const allTurns = splitBucketTurns(source);
|
|
919
924
|
const currentStart = Number.isInteger(options.currentTurnStartIndex)
|
|
920
925
|
? Math.max(0, Math.min(source.length, options.currentTurnStartIndex))
|
|
@@ -1025,16 +1030,53 @@ export function buildHistoryBuckets(snapshot, options = {}) {
|
|
|
1025
1030
|
const reserve = reservable.length ? Math.floor(availableTokens * 0.25) : 0;
|
|
1026
1031
|
const reservedRows = reservable.length
|
|
1027
1032
|
? Math.max(Math.floor(availableRows * 0.25), Math.min(...reservable.map(turn => turn.text.length))) : 0;
|
|
1033
|
+
function fitRecentTurn(turn, tokenLimit) {
|
|
1034
|
+
if (turn.tokens <= tokenLimit) return turn.text;
|
|
1035
|
+
const fitted = [];
|
|
1036
|
+
let tokens = tokenLimit;
|
|
1037
|
+
for (let index = 0; index < turn.text.length; index += 1) {
|
|
1038
|
+
const remainingRows = turn.text.length - index;
|
|
1039
|
+
const allowance = Math.max(2, Math.floor(tokens / remainingRows));
|
|
1040
|
+
const message = shrinkMessageToBudget(turn.text[index], allowance);
|
|
1041
|
+
const rows = dropEmptyAssistantRows([message]);
|
|
1042
|
+
fitted.push(...rows);
|
|
1043
|
+
tokens -= estimateMessagesTokens(rows);
|
|
1044
|
+
}
|
|
1045
|
+
return fitted;
|
|
1046
|
+
}
|
|
1028
1047
|
function selectRecent(limit, rowLimit = availableRows) {
|
|
1048
|
+
const candidates = past.slice(-recentCap);
|
|
1049
|
+
if (!candidates.length || limit <= 0 || rowLimit <= 0) return [];
|
|
1050
|
+
const preserveBoundaries = candidates.length >= MINIMUM_RECENT_PROVIDER_TURNS
|
|
1051
|
+
&& rowLimit >= candidates.reduce((sum, turn) => sum + turn.text.length, 0)
|
|
1052
|
+
&& limit >= candidates.length * 2;
|
|
1053
|
+
if (!preserveBoundaries) {
|
|
1054
|
+
const selected = [];
|
|
1055
|
+
let tokens = 0;
|
|
1056
|
+
let rows = 0;
|
|
1057
|
+
for (let index = candidates.length - 1; index >= 0; index -= 1) {
|
|
1058
|
+
const turn = candidates[index];
|
|
1059
|
+
if (tokens + turn.tokens > limit || rows + turn.text.length > rowLimit) break;
|
|
1060
|
+
selected.unshift(turn);
|
|
1061
|
+
tokens += turn.tokens;
|
|
1062
|
+
rows += turn.text.length;
|
|
1063
|
+
}
|
|
1064
|
+
return selected;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1029
1067
|
const selected = [];
|
|
1030
|
-
let tokens =
|
|
1031
|
-
let rows =
|
|
1032
|
-
for (
|
|
1033
|
-
const
|
|
1034
|
-
if (
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1068
|
+
let tokens = Math.floor(limit);
|
|
1069
|
+
let rows = Math.floor(rowLimit);
|
|
1070
|
+
for (const turn of candidates) {
|
|
1071
|
+
const remainingTurns = candidates.length - selected.length;
|
|
1072
|
+
if (rows < remainingTurns) break;
|
|
1073
|
+
const allowance = Math.max(2, Math.floor(tokens / remainingTurns));
|
|
1074
|
+
const fitted = fitRecentTurn(turn, allowance);
|
|
1075
|
+
if (!fitted.length || fitted.length > rows - (remainingTurns - 1)) continue;
|
|
1076
|
+
const used = estimateMessagesTokens(fitted);
|
|
1077
|
+
selected.push({ ...turn, text: fitted, tokens: used });
|
|
1078
|
+
tokens -= used;
|
|
1079
|
+
rows -= fitted.length;
|
|
1038
1080
|
}
|
|
1039
1081
|
return selected;
|
|
1040
1082
|
}
|
|
@@ -1072,13 +1114,22 @@ export function buildHistoryBuckets(snapshot, options = {}) {
|
|
|
1072
1114
|
related.sort((a, b) => bucketBefore(a, b) ? -1 : bucketBefore(b, a) ? 1 : 0);
|
|
1073
1115
|
|
|
1074
1116
|
const relatedMessages = related.flatMap(turn => turn.text);
|
|
1075
|
-
const recentText =
|
|
1076
|
-
.filter(isVisibleConversationRow)
|
|
1077
|
-
const
|
|
1117
|
+
const recentText = recent.flatMap(turn => turn.messages)
|
|
1118
|
+
.filter(isVisibleConversationRow);
|
|
1119
|
+
const recentWasFitted = recent.some(turn => turn.text !== turn.messages
|
|
1120
|
+
&& (turn.text.length !== bucketTextMessages(turn.messages).length
|
|
1121
|
+
|| turn.tokens !== estimateMessagesTokens(bucketTextMessages(turn.messages))));
|
|
1122
|
+
const recentBaseline = recent.flatMap(turn => recentWasFitted ? turn.text : bucketTextMessages(turn.messages));
|
|
1078
1123
|
// Enrich only after both complete-text buckets and the active turn are paid.
|
|
1124
|
+
// Tool protocol is useful only for immediate continuity; unlike visible text,
|
|
1125
|
+
// it never reaches farther back than the configured recent tool window.
|
|
1126
|
+
const toolCutIndex = indexOfNthTurnFromEnd(recentText, keepToolTurns);
|
|
1127
|
+
const recentToolSource = keepToolTurns > 0
|
|
1128
|
+
? recentText.slice(toolCutIndex < 0 ? 0 : toolCutIndex)
|
|
1129
|
+
: [];
|
|
1079
1130
|
const recentMessages = withoutHistorySourceIndexes(addOptionalRecentToolPairs(
|
|
1080
1131
|
recentBaseline,
|
|
1081
|
-
truncateToolResultsForModel(
|
|
1132
|
+
truncateToolResultsForModel(recentToolSource, { language: options.language }),
|
|
1082
1133
|
{
|
|
1083
1134
|
messageTokenBudget: availableTokens - estimateMessagesTokens(relatedMessages),
|
|
1084
1135
|
maxMessageCount: availableRows - relatedMessages.length,
|
package/yeaft/prompts.js
CHANGED
|
@@ -532,6 +532,8 @@ function renderVpPersona(vpPersona, lang, effectiveLang = 'en') {
|
|
|
532
532
|
const name = selectVpPersonaName(vpPersona, effectiveLang);
|
|
533
533
|
if (!name) return '';
|
|
534
534
|
const body = selectVpPersonaBody(vpPersona, effectiveLang);
|
|
535
|
+
const runtimePreamble = typeof vpPersona.runtimePreamble === 'string'
|
|
536
|
+
? vpPersona.runtimePreamble.trim() : '';
|
|
535
537
|
|
|
536
538
|
// Persona is the IDENTITY layer (not an overlay). Do not prepend a
|
|
537
539
|
// generic assistant identity here: the VP soul body is the source of truth.
|
|
@@ -540,6 +542,7 @@ function renderVpPersona(vpPersona, lang, effectiveLang = 'en') {
|
|
|
540
542
|
const soulHeading = effectiveLang === 'zh' ? '## 灵魂' : '## Soul';
|
|
541
543
|
const lines = [`# ${name}`, '', soulHeading];
|
|
542
544
|
if (body) lines.push('', body);
|
|
545
|
+
if (runtimePreamble) lines.push('', runtimePreamble);
|
|
543
546
|
return lines.join('\n');
|
|
544
547
|
}
|
|
545
548
|
|
package/yeaft/session.js
CHANGED
|
@@ -49,7 +49,8 @@ import { ensureDefaultSessionIfEmpty, migrateRegisteredWorkDirSessions } from '.
|
|
|
49
49
|
import { seedDefaultVps } from './vp/seed-defaults.js';
|
|
50
50
|
import { topUpDefaultVps } from './vp/seed-topup.js';
|
|
51
51
|
import { archiveLegacyScopes } from './memory/seed-backfill.js';
|
|
52
|
-
|
|
52
|
+
// Dream scheduler wiring is intentionally not imported while the runtime path is disabled.
|
|
53
|
+
// import { createV2DreamScheduler, bootInitEmptyGroups, bootCatchUpStaleDream } from './dream/session-wiring.js';
|
|
53
54
|
import { isWorkCenterEnabled } from './work-center/feature.js';
|
|
54
55
|
import { openSegmentIndex } from './memory/index-db.js';
|
|
55
56
|
import { syncAll as syncSegmentIndex } from './memory/segment-sync.js';
|
|
@@ -473,50 +474,20 @@ export async function loadSession(options = {}) {
|
|
|
473
474
|
});
|
|
474
475
|
|
|
475
476
|
|
|
476
|
-
// ─── 9a.
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
//
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
const dreamScheduler = createV2DreamScheduler(partialSession);
|
|
491
|
-
|
|
492
|
-
// task-710: kick a dream pass at boot for any group that has user
|
|
493
|
-
// messages but zero memory segments in the FTS index. Without this a
|
|
494
|
-
// freshly opened agent had to wait an hour (or for the nudge counter
|
|
495
|
-
// to cross 50) before the first segment landed and recall could find
|
|
496
|
-
// anything. Fire-and-forget; failure logs at debug only.
|
|
497
|
-
if (memoryIndex && !config._readOnly) {
|
|
498
|
-
bootInitEmptyGroups({
|
|
499
|
-
yeaftDir,
|
|
500
|
-
memoryIndex,
|
|
501
|
-
dreamScheduler,
|
|
502
|
-
config,
|
|
503
|
-
}).catch(() => { /* best-effort boot init */ });
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// fix/dream-cadence-and-ui-trigger: stale-cadence catch-up. If the
|
|
507
|
-
// newest per-group lastDreamAt across all groups is older than
|
|
508
|
-
// DREAM_INTERVAL_HOURS (or absent and there's user traffic), fire a
|
|
509
|
-
// single non-manual tick now. Independent of the interval timer —
|
|
510
|
-
// necessary because production observed 12 days between scheduled
|
|
511
|
-
// ticks (the unref'd interval did not fire reliably on long-lived
|
|
512
|
-
// server processes).
|
|
513
|
-
if (!config._readOnly) {
|
|
514
|
-
bootCatchUpStaleDream({
|
|
515
|
-
yeaftDir,
|
|
516
|
-
dreamScheduler,
|
|
517
|
-
config,
|
|
518
|
-
}).catch(() => { /* best-effort catch-up */ });
|
|
519
|
-
}
|
|
477
|
+
// ─── 9a. Dream runtime temporarily disabled ────────────
|
|
478
|
+
// Message history now supplies turn context. Keep the Dream implementation
|
|
479
|
+
// and persisted data intact, but do not create a scheduler or run boot-time
|
|
480
|
+
// initialization/catch-up while the replacement is evaluated.
|
|
481
|
+
//
|
|
482
|
+
// const partialSession = { yeaftDir, adapter, config, engine, trace };
|
|
483
|
+
// const dreamScheduler = createV2DreamScheduler(partialSession);
|
|
484
|
+
// if (memoryIndex && !config._readOnly) {
|
|
485
|
+
// bootInitEmptyGroups({ yeaftDir, memoryIndex, dreamScheduler, config }).catch(() => {});
|
|
486
|
+
// }
|
|
487
|
+
// if (!config._readOnly) {
|
|
488
|
+
// bootCatchUpStaleDream({ yeaftDir, dreamScheduler, config }).catch(() => {});
|
|
489
|
+
// }
|
|
490
|
+
const dreamScheduler = null;
|
|
520
491
|
|
|
521
492
|
// H2.f.5 retired the old session-level thread engine registry, input queue,
|
|
522
493
|
// and dispatcher. The session exposes a default `engine`; PR #797 keeps
|
|
@@ -221,10 +221,12 @@ export function startSubAgent(agent, deps = {}) {
|
|
|
221
221
|
deps.parentVpPersona && typeof deps.parentVpPersona === 'object'
|
|
222
222
|
? { ...deps.parentVpPersona }
|
|
223
223
|
: {};
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
224
|
+
// Keep the spawned-agent contract outside the inherited VP soul. Stock
|
|
225
|
+
// souls can contain bilingual section markers, and persona rendering selects
|
|
226
|
+
// one language section before provider dispatch; appending the preamble to
|
|
227
|
+
// that source can therefore discard the child identity and mission. The
|
|
228
|
+
// prompt renderer appends this runtime-only block after soul selection.
|
|
229
|
+
baseVpPersona.runtimePreamble = preamble.trim();
|
|
228
230
|
if (!baseVpPersona.displayName || !String(baseVpPersona.displayName).trim()) {
|
|
229
231
|
baseVpPersona.displayName = `${deps.parentName || 'Parent'}/${agent.name || 'sub-agent'}`;
|
|
230
232
|
}
|