@yeaft/webchat-agent 1.0.267 → 1.0.269
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/connection/index.js +12 -2
- package/connection/message-router.js +15 -13
- package/local-runtime/server/database.js +1 -0
- package/local-runtime/server/db/connection.js +48 -0
- package/local-runtime/server/db/session-db.js +5 -0
- package/local-runtime/server/db/session-ui-metadata-db.js +78 -0
- package/local-runtime/server/db/yeaft-session-db.js +22 -11
- package/local-runtime/server/handlers/agent-output.js +7 -1
- package/local-runtime/server/handlers/client-conversation.js +232 -58
- package/local-runtime/server/handlers/client-workbench.js +2 -2
- package/local-runtime/server/handlers/session-pin-router.js +7 -6
- package/local-runtime/server/session-catalog.js +82 -0
- package/local-runtime/server/ws-utils.js +78 -5
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +177 -85
- 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 +19 -21
- package/yeaft/sub-agent/runner.js +3 -3
- package/yeaft/tasks/manager.js +35 -6
- package/yeaft/tasks/store.js +31 -2
- package/yeaft/tools/agent.js +2 -0
- package/yeaft/tools/bash.js +1 -8
- package/yeaft/web-bridge.js +9 -7
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { WebSocket } from 'ws';
|
|
2
2
|
import { CONFIG } from './config.js';
|
|
3
3
|
import { encrypt, decrypt, isEncrypted, encodeKey } from './encryption.js';
|
|
4
|
-
import { sessionDb } from './database.js';
|
|
4
|
+
import { sessionDb, yeaftSessionDb, sessionUiMetadataDb } from './database.js';
|
|
5
|
+
import { projectSessionCatalog } from './session-catalog.js';
|
|
5
6
|
import { agents, webClients, directoryCache, DIR_CACHE_TTL, DIR_CACHE_MAX_SIZE, trackMessageBytesSent } from './context.js';
|
|
6
7
|
|
|
7
8
|
// Send message to web client.
|
|
@@ -75,6 +76,63 @@ export async function parseMessage(data, sessionKey) {
|
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
function onlineAgentIdsForUser(userId, role = null) {
|
|
80
|
+
return new Set(
|
|
81
|
+
Array.from(agents.entries())
|
|
82
|
+
.filter(([, agent]) => agent?.ws?.readyState === WebSocket.OPEN)
|
|
83
|
+
.filter(([, agent]) => CONFIG.skipAuth
|
|
84
|
+
|| agent.ownerId === userId
|
|
85
|
+
|| (!agent.ownerId && role === 'admin'))
|
|
86
|
+
.map(([agentId]) => agentId),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function verifyPersistedAgentOwnership(agentId, userId, role = null) {
|
|
91
|
+
if (CONFIG.skipAuth) return true;
|
|
92
|
+
if (!agentId || !userId) return false;
|
|
93
|
+
const agent = agents.get(agentId);
|
|
94
|
+
if (agent) {
|
|
95
|
+
if (agent.ownerId === userId) return true;
|
|
96
|
+
return !agent.ownerId && role === 'admin';
|
|
97
|
+
}
|
|
98
|
+
return sessionDb.hasOwnedRoute(userId, agentId)
|
|
99
|
+
|| sessionDb.getByUser(userId).some(row => row.user_id === userId && row.agent_id === agentId)
|
|
100
|
+
|| yeaftSessionDb.getByUser(userId).some(row => row.agentId === agentId);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function buildSessionCatalog(userId, role = null) {
|
|
104
|
+
const chatSessions = sessionDb.getActiveByUser(userId).filter((session) => {
|
|
105
|
+
if (CONFIG.skipAuth || session.user_id === userId) return true;
|
|
106
|
+
if (session.user_id != null) return false;
|
|
107
|
+
// Legacy NULL-owner rows require a durable route proof when their Agent is
|
|
108
|
+
// offline. This same predicate protects catalog mutations.
|
|
109
|
+
return verifyPersistedAgentOwnership(session.agent_id, userId, role);
|
|
110
|
+
});
|
|
111
|
+
return projectSessionCatalog({
|
|
112
|
+
chatSessions,
|
|
113
|
+
yeaftSessions: yeaftSessionDb.getByUser(userId),
|
|
114
|
+
metadata: sessionUiMetadataDb.getByUser(userId),
|
|
115
|
+
onlineAgentIds: onlineAgentIdsForUser(userId, role),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Broadcast the owner-scoped read model after canonical Session state changes.
|
|
120
|
+
export async function broadcastSessionCatalog(userId) {
|
|
121
|
+
if (!userId) return;
|
|
122
|
+
for (const [, client] of webClients) {
|
|
123
|
+
if (!client?.authenticated) continue;
|
|
124
|
+
if (client.userId !== userId) continue;
|
|
125
|
+
try {
|
|
126
|
+
await sendToWebClient(client, {
|
|
127
|
+
type: 'session_catalog_snapshot',
|
|
128
|
+
catalog: buildSessionCatalog(userId, client.role),
|
|
129
|
+
});
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.warn('[Server] session catalog projection failed:', e?.message || e);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
78
136
|
// 广播 agent 列表给所有已认证的 web 客户端(按 owner 过滤)
|
|
79
137
|
export async function broadcastAgentList() {
|
|
80
138
|
for (const [, client] of webClients) {
|
|
@@ -125,6 +183,14 @@ export async function broadcastAgentList() {
|
|
|
125
183
|
type: 'agent_list',
|
|
126
184
|
agents: agentList
|
|
127
185
|
});
|
|
186
|
+
try {
|
|
187
|
+
await sendToWebClient(client, {
|
|
188
|
+
type: 'session_catalog_snapshot',
|
|
189
|
+
catalog: buildSessionCatalog(client.userId, client.role),
|
|
190
|
+
});
|
|
191
|
+
} catch (e) {
|
|
192
|
+
console.warn('[Server] session catalog projection failed:', e?.message || e);
|
|
193
|
+
}
|
|
128
194
|
}
|
|
129
195
|
}
|
|
130
196
|
}
|
|
@@ -305,7 +371,7 @@ export function findAgentByName(agentName) {
|
|
|
305
371
|
/**
|
|
306
372
|
* 检查用户是否拥有指定的会话
|
|
307
373
|
*/
|
|
308
|
-
export function verifyConversationOwnership(conversationId, userId) {
|
|
374
|
+
export function verifyConversationOwnership(conversationId, userId, role = null) {
|
|
309
375
|
if (!conversationId || !userId) {
|
|
310
376
|
console.warn(`[Ownership] Check failed: conversationId=${conversationId}, userId=${userId} (missing parameter)`);
|
|
311
377
|
return false;
|
|
@@ -315,8 +381,9 @@ export function verifyConversationOwnership(conversationId, userId) {
|
|
|
315
381
|
for (const [agentId, agent] of agents) {
|
|
316
382
|
const conv = agent.conversations.get(conversationId);
|
|
317
383
|
if (conv) {
|
|
318
|
-
//
|
|
319
|
-
|
|
384
|
+
// Legacy ownerless conversations inherit the Agent route ACL; they are
|
|
385
|
+
// not public merely because the old row lacks user_id.
|
|
386
|
+
if (!conv.userId) return verifyPersistedAgentOwnership(agentId, userId, role);
|
|
320
387
|
const match = conv.userId === userId;
|
|
321
388
|
if (!match) {
|
|
322
389
|
console.warn(`[Ownership] Mismatch for ${conversationId}: conv.userId=${conv.userId}, client.userId=${userId}, agent=${agentId}`);
|
|
@@ -329,7 +396,13 @@ export function verifyConversationOwnership(conversationId, userId) {
|
|
|
329
396
|
try {
|
|
330
397
|
const session = sessionDb.get(conversationId);
|
|
331
398
|
if (session) {
|
|
332
|
-
if (!session.user_id)
|
|
399
|
+
if (!session.user_id) {
|
|
400
|
+
const allowed = verifyPersistedAgentOwnership(session.agent_id, userId, role);
|
|
401
|
+
if (!allowed) {
|
|
402
|
+
console.warn(`[Ownership] Legacy Session ${conversationId} has no durable owner proof for userId=${userId}`);
|
|
403
|
+
}
|
|
404
|
+
return allowed;
|
|
405
|
+
}
|
|
333
406
|
const match = session.user_id === userId;
|
|
334
407
|
if (!match) {
|
|
335
408
|
console.warn(`[Ownership] DB mismatch for ${conversationId}: session.user_id=${session.user_id}, client.userId=${userId}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.269"}
|