@yeaft/webchat-agent 1.0.387 → 1.0.388
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/context.js +78 -0
- package/local-runtime/server/handlers/agent-output.js +36 -25
- package/local-runtime/server/handlers/client-conversation.js +47 -1
- package/local-runtime/server/ws-client.js +2 -1
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +95 -104
- 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/yeaft/debug-trace.js +168 -1
- package/yeaft/web-bridge.js +7 -0
|
@@ -37,6 +37,84 @@ export const userFileTabs = new Map();
|
|
|
37
37
|
// fileId → { buffer, mimeType, filename, createdAt, token }
|
|
38
38
|
export const previewFiles = new Map();
|
|
39
39
|
|
|
40
|
+
// Debug trace replies may come from an older Agent that does not echo the
|
|
41
|
+
// private browser client id. Keep the correlation on the Server, where the
|
|
42
|
+
// original Agent/Session ownership check happened, and consume it exactly once.
|
|
43
|
+
const YEAFT_DEBUG_REQUEST_TTL_MS = 30_000;
|
|
44
|
+
const YEAFT_DEBUG_REQUEST_MAX_PENDING = 2048;
|
|
45
|
+
export const pendingYeaftDebugRequests = new Map();
|
|
46
|
+
|
|
47
|
+
function yeaftDebugRequestKey(agentId, requestId) {
|
|
48
|
+
return `${String(agentId || '')}\u0000${String(requestId || '')}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function pruneYeaftDebugRequests(now = Date.now()) {
|
|
52
|
+
for (const [key, pending] of pendingYeaftDebugRequests) {
|
|
53
|
+
if (!pending || pending.expiresAt <= now || !webClients.has(pending.clientId)) {
|
|
54
|
+
pendingYeaftDebugRequests.delete(key);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Register one owner-checked browser request before forwarding it to an Agent.
|
|
61
|
+
* @param {{agentId:string, requestId:string, sessionId:string, clientId:string, userId:string}} request
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
export function registerYeaftDebugRequest({ agentId, requestId, sessionId, clientId, userId }) {
|
|
65
|
+
if (!agentId || !requestId || !sessionId || !clientId || !userId) return false;
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
pruneYeaftDebugRequests(now);
|
|
68
|
+
const key = yeaftDebugRequestKey(agentId, requestId);
|
|
69
|
+
if (pendingYeaftDebugRequests.has(key)) return false;
|
|
70
|
+
if (pendingYeaftDebugRequests.size >= YEAFT_DEBUG_REQUEST_MAX_PENDING) {
|
|
71
|
+
const oldestKey = pendingYeaftDebugRequests.keys().next().value;
|
|
72
|
+
if (oldestKey != null) pendingYeaftDebugRequests.delete(oldestKey);
|
|
73
|
+
}
|
|
74
|
+
pendingYeaftDebugRequests.set(key, {
|
|
75
|
+
agentId,
|
|
76
|
+
requestId,
|
|
77
|
+
sessionId,
|
|
78
|
+
clientId,
|
|
79
|
+
userId,
|
|
80
|
+
expiresAt: now + YEAFT_DEBUG_REQUEST_TTL_MS,
|
|
81
|
+
});
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Consume a matching, unexpired correlation exactly once.
|
|
87
|
+
* @param {{agentId:string, requestId:string, sessionId?:string|null}} response
|
|
88
|
+
* @returns {{agentId:string, requestId:string, sessionId:string, clientId:string, userId:string, expiresAt:number}|null}
|
|
89
|
+
*/
|
|
90
|
+
export function consumeYeaftDebugRequest({ agentId, requestId, sessionId }) {
|
|
91
|
+
if (!agentId || !requestId) return null;
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
pruneYeaftDebugRequests(now);
|
|
94
|
+
const key = yeaftDebugRequestKey(agentId, requestId);
|
|
95
|
+
const pending = pendingYeaftDebugRequests.get(key);
|
|
96
|
+
if (!pending) return null;
|
|
97
|
+
if (sessionId && pending.sessionId !== sessionId) return null;
|
|
98
|
+
pendingYeaftDebugRequests.delete(key);
|
|
99
|
+
return pending;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Remove one correlation after forwarding fails. */
|
|
103
|
+
export function deleteYeaftDebugRequest({ agentId, requestId, clientId = null }) {
|
|
104
|
+
const key = yeaftDebugRequestKey(agentId, requestId);
|
|
105
|
+
const pending = pendingYeaftDebugRequests.get(key);
|
|
106
|
+
if (!pending || (clientId && pending.clientId !== clientId)) return false;
|
|
107
|
+
return pendingYeaftDebugRequests.delete(key);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Remove all correlations owned by a disconnected browser client. */
|
|
111
|
+
export function clearYeaftDebugRequestsForClient(clientId) {
|
|
112
|
+
if (!clientId) return;
|
|
113
|
+
for (const [key, pending] of pendingYeaftDebugRequests) {
|
|
114
|
+
if (pending?.clientId === clientId) pendingYeaftDebugRequests.delete(key);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
40
118
|
// Admin dashboard usage stats.
|
|
41
119
|
// userId → { requests, bytesSent, bytesReceived, messages, sessions }
|
|
42
120
|
// `messages` is user turn count. bytesSent/bytesReceived are message traffic
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from 'crypto';
|
|
|
2
2
|
import { messageDb, sessionUiMetadataDb, yeaftProjectDb, yeaftSessionDb } from '../database.js';
|
|
3
3
|
import { transaction } from '../db/connection.js';
|
|
4
4
|
import { broadcastAgentList, broadcastSessionCatalog, forwardToClients, sendToAgent, sendToWebClient } from '../ws-utils.js';
|
|
5
|
-
import { webClients, previewFiles } from '../context.js';
|
|
5
|
+
import { consumeYeaftDebugRequest, webClients, previewFiles } from '../context.js';
|
|
6
6
|
import { CONFIG } from '../config.js';
|
|
7
7
|
import { yeaftAssetStore } from '../yeaft-asset-store.js';
|
|
8
8
|
import { recordPerfTraceEvent } from '../perf-trace.js';
|
|
@@ -987,32 +987,43 @@ export async function handleAgentOutput(agentId, agent, msg) {
|
|
|
987
987
|
break;
|
|
988
988
|
}
|
|
989
989
|
|
|
990
|
-
case 'yeaft_debug_history':
|
|
991
|
-
//
|
|
992
|
-
//
|
|
993
|
-
//
|
|
994
|
-
//
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
})
|
|
1013
|
-
|
|
990
|
+
case 'yeaft_debug_history': {
|
|
991
|
+
// Debug history is request/Turn scoped and may contain raw provider or
|
|
992
|
+
// tool payloads. The Server registered this Agent + requestId after the
|
|
993
|
+
// compound Session ownership check, so rolling upgrades remain safe even
|
|
994
|
+
// when an older Agent does not echo the private browser client id.
|
|
995
|
+
const pending = consumeYeaftDebugRequest({
|
|
996
|
+
agentId,
|
|
997
|
+
requestId: msg.requestId,
|
|
998
|
+
sessionId: msg.sessionId,
|
|
999
|
+
});
|
|
1000
|
+
const targetClient = pending ? webClients.get(pending.clientId) : null;
|
|
1001
|
+
const ownerMatches = CONFIG.skipAuth || (
|
|
1002
|
+
pending?.userId === agent.ownerId
|
|
1003
|
+
&& targetClient?.userId === pending.userId
|
|
1004
|
+
);
|
|
1005
|
+
if (targetClient?.authenticated && ownerMatches) {
|
|
1006
|
+
await sendToWebClient(targetClient, {
|
|
1007
|
+
type: 'yeaft_debug_history',
|
|
1008
|
+
agentId,
|
|
1009
|
+
loops: Array.isArray(msg.loops) ? msg.loops : [],
|
|
1010
|
+
turns: Array.isArray(msg.turns) ? msg.turns : [],
|
|
1011
|
+
dreamEvents: Array.isArray(msg.dreamEvents) ? msg.dreamEvents : [],
|
|
1012
|
+
...(msg.projection && typeof msg.projection === 'object' ? { projection: msg.projection } : {}),
|
|
1013
|
+
...(msg.sessionId != null ? { sessionId: msg.sessionId } : {}),
|
|
1014
|
+
...(msg.threadId != null ? { threadId: msg.threadId } : {}),
|
|
1015
|
+
...(msg.requestId != null ? { requestId: msg.requestId } : {}),
|
|
1016
|
+
...(msg.requestKind != null ? { requestKind: msg.requestKind } : {}),
|
|
1017
|
+
...(msg.search != null ? { search: msg.search } : {}),
|
|
1018
|
+
...(msg.hasMore != null ? { hasMore: !!msg.hasMore } : {}),
|
|
1019
|
+
...(msg.limit != null ? { limit: msg.limit } : {}),
|
|
1020
|
+
...(msg.indexOnly != null ? { indexOnly: !!msg.indexOnly } : {}),
|
|
1021
|
+
...(msg.detailTurnId != null ? { detailTurnId: msg.detailTurnId } : {}),
|
|
1022
|
+
...(msg.error != null ? { error: msg.error } : {}),
|
|
1023
|
+
});
|
|
1014
1024
|
}
|
|
1015
1025
|
break;
|
|
1026
|
+
}
|
|
1016
1027
|
|
|
1017
1028
|
default:
|
|
1018
1029
|
return false; // Not handled
|
|
@@ -8,7 +8,14 @@ import {
|
|
|
8
8
|
yeaftSessionDb,
|
|
9
9
|
sessionUiMetadataDb,
|
|
10
10
|
} from '../database.js';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
agents,
|
|
13
|
+
deleteYeaftDebugRequest,
|
|
14
|
+
pendingFiles,
|
|
15
|
+
registerYeaftDebugRequest,
|
|
16
|
+
trackUserTurn,
|
|
17
|
+
webClients,
|
|
18
|
+
} from '../context.js';
|
|
12
19
|
import {
|
|
13
20
|
sendToWebClient, forwardToAgent,
|
|
14
21
|
broadcastAgentList, broadcastSessionCatalog, buildSessionCatalog, buildHiddenSessionCatalog,
|
|
@@ -1288,6 +1295,45 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1288
1295
|
break;
|
|
1289
1296
|
}
|
|
1290
1297
|
|
|
1298
|
+
case 'yeaft_fetch_debug_history': {
|
|
1299
|
+
// Debug traces can contain raw prompts, provider payloads, and tool
|
|
1300
|
+
// output. Treat this as a precise Session-owned request instead of the
|
|
1301
|
+
// generic Yeaft relay: require the compound Agent + Session identity and
|
|
1302
|
+
// correlate the response back to the requesting browser tab.
|
|
1303
|
+
const debugAgentId = msg.agentId;
|
|
1304
|
+
const debugSessionId = typeof msg.sessionId === 'string' ? msg.sessionId : '';
|
|
1305
|
+
const debugRequestId = typeof msg.requestId === 'string' ? msg.requestId : '';
|
|
1306
|
+
if (!debugAgentId || !debugSessionId || !debugRequestId) return;
|
|
1307
|
+
if (!await checkAgentAccess(debugAgentId)) return;
|
|
1308
|
+
if (!CONFIG.skipAuth && !yeaftSessionDb.getForAgent(client.userId, debugAgentId, debugSessionId)) return;
|
|
1309
|
+
const registered = registerYeaftDebugRequest({
|
|
1310
|
+
agentId: debugAgentId,
|
|
1311
|
+
requestId: debugRequestId,
|
|
1312
|
+
sessionId: debugSessionId,
|
|
1313
|
+
clientId,
|
|
1314
|
+
userId: client.userId,
|
|
1315
|
+
});
|
|
1316
|
+
if (!registered) return;
|
|
1317
|
+
try {
|
|
1318
|
+
await forwardToAgent(debugAgentId, {
|
|
1319
|
+
type: 'yeaft_fetch_debug_history',
|
|
1320
|
+
sessionId: debugSessionId,
|
|
1321
|
+
requestId: debugRequestId,
|
|
1322
|
+
requestKind: msg.requestKind === 'detail' ? 'detail' : 'list',
|
|
1323
|
+
limit: typeof msg.limit === 'number' ? msg.limit : 10,
|
|
1324
|
+
dreamLimit: typeof msg.dreamLimit === 'number' ? msg.dreamLimit : 5,
|
|
1325
|
+
indexOnly: msg.indexOnly === true,
|
|
1326
|
+
detailTurnId: typeof msg.detailTurnId === 'string' ? msg.detailTurnId : null,
|
|
1327
|
+
search: typeof msg.search === 'string' ? msg.search.slice(0, 500) : '',
|
|
1328
|
+
_requestClientId: clientId,
|
|
1329
|
+
});
|
|
1330
|
+
} catch (err) {
|
|
1331
|
+
deleteYeaftDebugRequest({ agentId: debugAgentId, requestId: debugRequestId, clientId });
|
|
1332
|
+
throw err;
|
|
1333
|
+
}
|
|
1334
|
+
break;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1291
1337
|
case 'yeaft_load_history_outline': {
|
|
1292
1338
|
const outlineAgentId = msg.agentId;
|
|
1293
1339
|
const outlineSessionId = typeof msg.sessionId === 'string' ? msg.sessionId : '';
|
|
@@ -5,7 +5,7 @@ import { generateSkipAuthSession } from './auth.js';
|
|
|
5
5
|
import { authenticateRequest } from './auth/request-auth.js';
|
|
6
6
|
import { encodeKey } from './encryption.js';
|
|
7
7
|
import { userDb } from './database.js';
|
|
8
|
-
import { agents, webClients, isHeartbeatMessageType, trackRequest } from './context.js';
|
|
8
|
+
import { agents, clearYeaftDebugRequestsForClient, webClients, isHeartbeatMessageType, trackRequest } from './context.js';
|
|
9
9
|
import {
|
|
10
10
|
parseMessage, sendToWebClient, sendToAgent,
|
|
11
11
|
broadcastAgentList, resolveAgentAccessError
|
|
@@ -163,6 +163,7 @@ export function handleWebConnection(ws, url, req = {}) {
|
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
clearWorkCenterRequestsForClient(client);
|
|
166
|
+
clearYeaftDebugRequestsForClient(clientId);
|
|
166
167
|
webClients.delete(clientId);
|
|
167
168
|
console.log(`Web client disconnected: ${clientId}`);
|
|
168
169
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.388"}
|