@yeaft/webchat-agent 1.0.224 → 1.0.227
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/agent-file-terminal.js +11 -2
- package/local-runtime/server/handlers/client-workbench.js +6 -1
- package/local-runtime/server/ws-agent.js +2 -1
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +3 -3
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/terminal.js +24 -8
- package/yeaft/conversation/internal-control.js +10 -0
- package/yeaft/conversation/persist.js +14 -6
- package/yeaft/engine.js +8 -2
- package/yeaft/router/continuity.js +3 -2
- package/yeaft/web-bridge.js +9 -3
|
Binary file
|
package/package.json
CHANGED
package/terminal.js
CHANGED
|
@@ -58,11 +58,19 @@ export async function loadNodePty() {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
function terminalRoutingFields(source) {
|
|
62
|
+
return {
|
|
63
|
+
...(source?._requestUserId ? { _requestUserId: source._requestUserId } : {}),
|
|
64
|
+
...(source?._requestClientId ? { _requestClientId: source._requestClientId } : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
61
68
|
export async function handleTerminalCreate(msg) {
|
|
62
69
|
const { conversationId, cols, rows } = msg;
|
|
63
70
|
const terminalId = msg.terminalId || conversationId;
|
|
64
71
|
const conv = ctx.conversations.get(conversationId);
|
|
65
72
|
const workDir = conv?.workDir || ctx.CONFIG.workDir;
|
|
73
|
+
const routingFields = terminalRoutingFields(msg);
|
|
66
74
|
|
|
67
75
|
// 如果已存在终端,先关闭
|
|
68
76
|
if (ctx.terminals.has(terminalId)) {
|
|
@@ -80,7 +88,8 @@ export async function handleTerminalCreate(msg) {
|
|
|
80
88
|
type: 'terminal_error',
|
|
81
89
|
conversationId,
|
|
82
90
|
terminalId,
|
|
83
|
-
message: 'Terminal backend is not installed. Run: npm install'
|
|
91
|
+
message: 'Terminal backend is not installed. Run: npm install',
|
|
92
|
+
...routingFields,
|
|
84
93
|
});
|
|
85
94
|
return;
|
|
86
95
|
}
|
|
@@ -123,7 +132,8 @@ export async function handleTerminalCreate(msg) {
|
|
|
123
132
|
type: 'terminal_output',
|
|
124
133
|
conversationId,
|
|
125
134
|
terminalId,
|
|
126
|
-
data: buffer
|
|
135
|
+
data: buffer,
|
|
136
|
+
...routingFields,
|
|
127
137
|
});
|
|
128
138
|
buffer = '';
|
|
129
139
|
timer = null;
|
|
@@ -138,7 +148,8 @@ export async function handleTerminalCreate(msg) {
|
|
|
138
148
|
type: 'terminal_output',
|
|
139
149
|
conversationId,
|
|
140
150
|
terminalId,
|
|
141
|
-
data: buffer
|
|
151
|
+
data: buffer,
|
|
152
|
+
...routingFields,
|
|
142
153
|
});
|
|
143
154
|
buffer = '';
|
|
144
155
|
}
|
|
@@ -149,7 +160,8 @@ export async function handleTerminalCreate(msg) {
|
|
|
149
160
|
ctx.sendToServer({
|
|
150
161
|
type: 'terminal_closed',
|
|
151
162
|
conversationId,
|
|
152
|
-
terminalId
|
|
163
|
+
terminalId,
|
|
164
|
+
...routingFields,
|
|
153
165
|
});
|
|
154
166
|
});
|
|
155
167
|
|
|
@@ -159,7 +171,8 @@ export async function handleTerminalCreate(msg) {
|
|
|
159
171
|
cols: cols || 80,
|
|
160
172
|
rows: rows || 24,
|
|
161
173
|
buffer: '',
|
|
162
|
-
timer: null
|
|
174
|
+
timer: null,
|
|
175
|
+
...routingFields,
|
|
163
176
|
});
|
|
164
177
|
|
|
165
178
|
console.log(`[PTY] Created terminal ${terminalId} for ${conversationId} in ${workDir}`);
|
|
@@ -167,7 +180,8 @@ export async function handleTerminalCreate(msg) {
|
|
|
167
180
|
type: 'terminal_created',
|
|
168
181
|
conversationId,
|
|
169
182
|
terminalId,
|
|
170
|
-
success: true
|
|
183
|
+
success: true,
|
|
184
|
+
...routingFields,
|
|
171
185
|
});
|
|
172
186
|
} catch (e) {
|
|
173
187
|
console.error(`[PTY] Failed to create terminal:`, e.message);
|
|
@@ -175,7 +189,8 @@ export async function handleTerminalCreate(msg) {
|
|
|
175
189
|
type: 'terminal_error',
|
|
176
190
|
conversationId,
|
|
177
191
|
terminalId,
|
|
178
|
-
message: `Failed to create terminal: ${e.message}
|
|
192
|
+
message: `Failed to create terminal: ${e.message}`,
|
|
193
|
+
...routingFields,
|
|
179
194
|
});
|
|
180
195
|
}
|
|
181
196
|
}
|
|
@@ -220,7 +235,8 @@ export function handleTerminalClose(msg) {
|
|
|
220
235
|
ctx.sendToServer({
|
|
221
236
|
type: 'terminal_closed',
|
|
222
237
|
conversationId: term.conversationId || msg.conversationId,
|
|
223
|
-
terminalId
|
|
238
|
+
terminalId,
|
|
239
|
+
...terminalRoutingFields(term),
|
|
224
240
|
});
|
|
225
241
|
}
|
|
226
242
|
}
|
|
@@ -20,3 +20,13 @@ export function isHiddenConversationRow(row) {
|
|
|
20
20
|
if (row.kind === 'compact_summary' || row._compactSummary) return true;
|
|
21
21
|
return isInternalControlContent(row.content);
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Whether a persisted row may be shown as a human-authored conversation turn.
|
|
26
|
+
* Legacy user rows predate provenance metadata and remain visible; new Engine
|
|
27
|
+
* protocol rows carry `userAuthored: false` and are model-only.
|
|
28
|
+
*/
|
|
29
|
+
export function isVisibleConversationRow(row) {
|
|
30
|
+
if (isHiddenConversationRow(row)) return false;
|
|
31
|
+
return row?.role !== 'user' || row.userAuthored !== false;
|
|
32
|
+
}
|
|
@@ -23,7 +23,7 @@ import { isPermissionError } from '../init.js';
|
|
|
23
23
|
import { writeAtomic } from '../storage/atomic.js';
|
|
24
24
|
import { pairSanitize } from '../pair-sanitize.js';
|
|
25
25
|
import { sliceLastNTurns, stripVpMentionPrefix } from '../turn-utils.js';
|
|
26
|
-
import { isHiddenConversationRow } from './internal-control.js';
|
|
26
|
+
import { isHiddenConversationRow, isVisibleConversationRow } from './internal-control.js';
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Default cold-start "recent window" size, expressed in TURNS (not raw
|
|
@@ -317,6 +317,7 @@ export function projectVisibleSessionMessages(messages) {
|
|
|
317
317
|
const visible = [];
|
|
318
318
|
for (const row of rows) {
|
|
319
319
|
if (!row || (row.role !== 'user' && row.role !== 'assistant')) continue;
|
|
320
|
+
if (!isVisibleConversationRow(row)) continue;
|
|
320
321
|
if (row.role !== 'assistant' || !Array.isArray(row.toolCalls) || row.toolCalls.length === 0) {
|
|
321
322
|
if (row.role === 'assistant' && !row.content && !row.attachments && !row.images
|
|
322
323
|
&& !row.toolSummaryCount && !row.askUserResults) continue;
|
|
@@ -385,6 +386,7 @@ function serializeMessage(msg) {
|
|
|
385
386
|
if (msg.sessionId) fm.push(`sessionId: ${msg.sessionId}`);
|
|
386
387
|
if (msg.chatId) fm.push(`chatId: ${msg.chatId}`);
|
|
387
388
|
if (msg.clientMessageId) fm.push(`clientMessageId: ${msg.clientMessageId}`);
|
|
389
|
+
if (typeof msg.userAuthored === 'boolean') fm.push(`userAuthored: ${msg.userAuthored}`);
|
|
388
390
|
if (msg.incomplete) fm.push('incomplete: true');
|
|
389
391
|
if (msg.stopReason) fm.push(`stopReason: ${msg.stopReason}`);
|
|
390
392
|
// Session attribution: when a VP authors an assistant turn (either
|
|
@@ -516,6 +518,7 @@ export function parseMessage(raw) {
|
|
|
516
518
|
case 'sessionId': msg.sessionId = value; break;
|
|
517
519
|
case 'chatId': msg.chatId = value; break;
|
|
518
520
|
case 'clientMessageId': msg.clientMessageId = value; break;
|
|
521
|
+
case 'userAuthored': msg.userAuthored = value === 'true'; break;
|
|
519
522
|
case 'incomplete': msg.incomplete = value === 'true'; break;
|
|
520
523
|
case 'stopReason': msg.stopReason = value; break;
|
|
521
524
|
case 'speakerVpId': msg.speakerVpId = value; break;
|
|
@@ -1524,7 +1527,7 @@ export class ConversationStore {
|
|
|
1524
1527
|
if (!sessionId) return { messages: [], oldestSeq: null, hasMore: false };
|
|
1525
1528
|
const cutoff = Number.isFinite(beforeSeq) ? beforeSeq : Infinity;
|
|
1526
1529
|
const prefix = this.#readSessionRows(sessionId, { beforeSeq: cutoff })
|
|
1527
|
-
.filter(m => m && m.sessionId === sessionId &&
|
|
1530
|
+
.filter(m => m && m.sessionId === sessionId && isVisibleConversationRow(m));
|
|
1528
1531
|
if (prefix.length === 0) return { messages: [], oldestSeq: null, hasMore: false };
|
|
1529
1532
|
const sliced = pairSanitize(sliceLastNTurns(prefix, turnsLimit));
|
|
1530
1533
|
// Turn-based hasMore: there's an EARLIER turn boundary we didn't keep.
|
|
@@ -1563,6 +1566,7 @@ export class ConversationStore {
|
|
|
1563
1566
|
beforeSeq: cutoff,
|
|
1564
1567
|
roles: null,
|
|
1565
1568
|
stripAssistantToolCalls: false,
|
|
1569
|
+
visibleOnly: true,
|
|
1566
1570
|
});
|
|
1567
1571
|
const messages = projectVisibleSessionMessages(page.messages);
|
|
1568
1572
|
if (messages.length === 0) return { messages: [], oldestSeq: null, hasMore: page.truncated };
|
|
@@ -1638,7 +1642,7 @@ export class ConversationStore {
|
|
|
1638
1642
|
for (const m of this.#iterateSessionRows(sessionId, { afterSeq: cutoff, desc: false })) {
|
|
1639
1643
|
if (!m || m.sessionId !== sessionId) continue;
|
|
1640
1644
|
const seq = parseSeqFromId(m.id);
|
|
1641
|
-
const hidden =
|
|
1645
|
+
const hidden = !isVisibleConversationRow(m);
|
|
1642
1646
|
if (!hidden) {
|
|
1643
1647
|
// Keep every outstanding call open across interleaved VP rows. Session
|
|
1644
1648
|
// persistence is globally sequenced, so a sibling VP may append visible
|
|
@@ -1794,13 +1798,14 @@ export class ConversationStore {
|
|
|
1794
1798
|
beforeSeq: anchorSeq + 1,
|
|
1795
1799
|
roles: null,
|
|
1796
1800
|
stripAssistantToolCalls: false,
|
|
1801
|
+
visibleOnly: true,
|
|
1797
1802
|
});
|
|
1798
1803
|
const messages = beforeRaw.messages.slice();
|
|
1799
1804
|
const seen = new Set(messages.map(message => message?.id).filter(Boolean));
|
|
1800
1805
|
let followingUserTurns = 0;
|
|
1801
1806
|
|
|
1802
1807
|
for (const message of this.#iterateSessionRows(sessionId, { afterSeq: anchorSeq, desc: false })) {
|
|
1803
|
-
if (!message || message.sessionId !== sessionId ||
|
|
1808
|
+
if (!message || message.sessionId !== sessionId || !isVisibleConversationRow(message)) continue;
|
|
1804
1809
|
if (message.role === 'user') {
|
|
1805
1810
|
followingUserTurns += 1;
|
|
1806
1811
|
if (followingUserTurns > afterTurns) break;
|
|
@@ -2452,6 +2457,7 @@ export class ConversationStore {
|
|
|
2452
2457
|
roles = null,
|
|
2453
2458
|
stripAssistantToolCalls = false,
|
|
2454
2459
|
includeReflections = false,
|
|
2460
|
+
visibleOnly = false,
|
|
2455
2461
|
} = {}) {
|
|
2456
2462
|
const kept = [];
|
|
2457
2463
|
const pendingBoundaryRows = [];
|
|
@@ -2498,7 +2504,9 @@ export class ConversationStore {
|
|
|
2498
2504
|
if (!m || m.sessionId !== sessionId) continue;
|
|
2499
2505
|
|
|
2500
2506
|
const boundaryComplete = turnsFromEnd >= turnsLimit;
|
|
2501
|
-
|
|
2507
|
+
const hidden = isHiddenConversationRow(m)
|
|
2508
|
+
|| (visibleOnly && !isVisibleConversationRow(m));
|
|
2509
|
+
if (hidden && !(includeReflections && m._reflection === true)) {
|
|
2502
2510
|
if (boundaryComplete) {
|
|
2503
2511
|
truncated = true;
|
|
2504
2512
|
break;
|
|
@@ -2551,7 +2559,7 @@ export class ConversationStore {
|
|
|
2551
2559
|
let current = null;
|
|
2552
2560
|
|
|
2553
2561
|
const visibleRow = (message) => {
|
|
2554
|
-
if (!message || message.sessionId !== sessionId ||
|
|
2562
|
+
if (!message || message.sessionId !== sessionId || !isVisibleConversationRow(message)) return null;
|
|
2555
2563
|
if (message.role !== 'user' && message.role !== 'assistant') return null;
|
|
2556
2564
|
if (!message.id || seen.has(message.id)) return null;
|
|
2557
2565
|
seen.add(message.id);
|
package/yeaft/engine.js
CHANGED
|
@@ -1401,6 +1401,8 @@ export class Engine {
|
|
|
1401
1401
|
if (message.isError) record.isError = true;
|
|
1402
1402
|
if (message.imageAssetAnchor) record.imageAssetAnchor = true;
|
|
1403
1403
|
if (message._reflection) record._reflection = true;
|
|
1404
|
+
if (message.role === 'user') record.userAuthored = message.userAuthored === true;
|
|
1405
|
+
if (message.internal === true) record.internal = true;
|
|
1404
1406
|
if (Array.isArray(message.foldedMessageIds) && message.foldedMessageIds.length > 0) {
|
|
1405
1407
|
record.foldedMessageIds = [...message.foldedMessageIds];
|
|
1406
1408
|
}
|
|
@@ -1702,7 +1704,11 @@ export class Engine {
|
|
|
1702
1704
|
|
|
1703
1705
|
#persistAppendedUserMessage(item, sessionId) {
|
|
1704
1706
|
if (!item || item.persisted || item.internal) return;
|
|
1705
|
-
this.#persistConversationMessage({
|
|
1707
|
+
this.#persistConversationMessage({
|
|
1708
|
+
role: 'user',
|
|
1709
|
+
content: item.content,
|
|
1710
|
+
userAuthored: true,
|
|
1711
|
+
}, { sessionId });
|
|
1706
1712
|
item.persisted = true;
|
|
1707
1713
|
}
|
|
1708
1714
|
|
|
@@ -1912,7 +1918,7 @@ export class Engine {
|
|
|
1912
1918
|
// already writes one shared user row before multi-VP fan-out, so those
|
|
1913
1919
|
// callers set userAlreadyPersisted and every VP skips this append.
|
|
1914
1920
|
if (!userAlreadyPersisted) {
|
|
1915
|
-
this.#persistConversationMessage({ role: 'user', content: prompt }, {
|
|
1921
|
+
this.#persistConversationMessage({ role: 'user', content: prompt, userAuthored: true }, {
|
|
1916
1922
|
sessionId: runtimeSessionId,
|
|
1917
1923
|
});
|
|
1918
1924
|
}
|
|
@@ -98,9 +98,10 @@ export function stripMetaForWire(messages) {
|
|
|
98
98
|
let mutated = false;
|
|
99
99
|
const out = messages.map(m => {
|
|
100
100
|
if (m && typeof m === 'object'
|
|
101
|
-
&& ('_meta' in m || '_runtimeTurnId' in m || '_partialTurn' in m
|
|
101
|
+
&& ('_meta' in m || '_runtimeTurnId' in m || '_partialTurn' in m
|
|
102
|
+
|| '_persistedMessageId' in m || 'userAuthored' in m)) {
|
|
102
103
|
mutated = true;
|
|
103
|
-
const { _meta, _runtimeTurnId, _partialTurn, _persistedMessageId, ...rest } = m;
|
|
104
|
+
const { _meta, _runtimeTurnId, _partialTurn, _persistedMessageId, userAuthored, ...rest } = m;
|
|
104
105
|
return rest;
|
|
105
106
|
}
|
|
106
107
|
return m;
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -62,7 +62,7 @@ import {
|
|
|
62
62
|
} from './history-compact.js';
|
|
63
63
|
import { persistYeaftAttachments, attachmentsForPersistence, persistedAttachmentPreviewPayload } from './attachments.js';
|
|
64
64
|
import { ConversationStore, parseSeqFromId, projectVisibleSessionMessages } from './conversation/persist.js';
|
|
65
|
-
import { isHiddenConversationRow } from './conversation/internal-control.js';
|
|
65
|
+
import { isHiddenConversationRow, isVisibleConversationRow } from './conversation/internal-control.js';
|
|
66
66
|
import { imageMetadataForPersistence } from './image-assets.js';
|
|
67
67
|
import { sliceLastNTurns } from './turn-utils.js';
|
|
68
68
|
import { pairSanitize } from './pair-sanitize.js';
|
|
@@ -1215,6 +1215,7 @@ function projectPersistedToHistoryEntry(m, { includeReflections = false } = {})
|
|
|
1215
1215
|
}
|
|
1216
1216
|
|
|
1217
1217
|
function projectPersistedToVisibleHistoryEntry(m) {
|
|
1218
|
+
if (!isVisibleConversationRow(m)) return null;
|
|
1218
1219
|
const entry = projectPersistedToHistoryEntry(m);
|
|
1219
1220
|
return entry && (entry.role === 'user' || entry.role === 'assistant') ? entry : null;
|
|
1220
1221
|
}
|
|
@@ -3703,6 +3704,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
3703
3704
|
}
|
|
3704
3705
|
sendSessionOutputFrame({
|
|
3705
3706
|
type: 'user',
|
|
3707
|
+
userAuthored: false,
|
|
3706
3708
|
tool_use_result: [{
|
|
3707
3709
|
type: 'tool_result',
|
|
3708
3710
|
tool_use_id: event.id,
|
|
@@ -3735,6 +3737,7 @@ function handleEngineEvent(event, hctx) {
|
|
|
3735
3737
|
}
|
|
3736
3738
|
sendSessionOutputFrame({
|
|
3737
3739
|
type: 'user',
|
|
3740
|
+
userAuthored: false,
|
|
3738
3741
|
tool_use_result: [{
|
|
3739
3742
|
type: 'tool_result',
|
|
3740
3743
|
tool_use_id: event.toolCallId,
|
|
@@ -5461,8 +5464,11 @@ function persistInboundMessageOnceByMsgId({ msgId, text, sessionId, threadId = '
|
|
|
5461
5464
|
threadId: threadId || 'main',
|
|
5462
5465
|
};
|
|
5463
5466
|
if (sessionId) record.sessionId = sessionId;
|
|
5464
|
-
if (persistRole === 'user'
|
|
5465
|
-
record.
|
|
5467
|
+
if (persistRole === 'user') {
|
|
5468
|
+
record.userAuthored = true;
|
|
5469
|
+
if (clientMessageId && typeof clientMessageId === 'string') {
|
|
5470
|
+
record.clientMessageId = clientMessageId;
|
|
5471
|
+
}
|
|
5466
5472
|
}
|
|
5467
5473
|
// Stamp speakerVpId so the UI's loadHistory replay can route the row
|
|
5468
5474
|
// to the correct VP block. Only meaningful when role='assistant'; for
|