@yeaft/webchat-agent 1.0.188 → 1.0.190
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/cli.js +12 -0
- package/index.js +10 -2
- package/local-run.js +218 -0
- package/local-runtime/server/.env.example +54 -0
- package/local-runtime/server/api.js +111 -0
- package/local-runtime/server/auth/aad.js +235 -0
- package/local-runtime/server/auth/login.js +156 -0
- package/local-runtime/server/auth/oauth-flow.js +277 -0
- package/local-runtime/server/auth/password-reset.js +134 -0
- package/local-runtime/server/auth/providers/alipay.js +125 -0
- package/local-runtime/server/auth/providers/github.js +82 -0
- package/local-runtime/server/auth/providers/google.js +60 -0
- package/local-runtime/server/auth/providers/microsoft.js +71 -0
- package/local-runtime/server/auth/providers/types.js +57 -0
- package/local-runtime/server/auth/providers/wechat.js +68 -0
- package/local-runtime/server/auth/register.js +91 -0
- package/local-runtime/server/auth/session-store.js +57 -0
- package/local-runtime/server/auth/token.js +85 -0
- package/local-runtime/server/auth/totp-auth.js +133 -0
- package/local-runtime/server/auth/utils.js +42 -0
- package/local-runtime/server/auth.js +8 -0
- package/local-runtime/server/check-node-version.js +74 -0
- package/local-runtime/server/config.js +298 -0
- package/local-runtime/server/context.js +140 -0
- package/local-runtime/server/create-user.js +59 -0
- package/local-runtime/server/database.js +12 -0
- package/local-runtime/server/db/connection.js +963 -0
- package/local-runtime/server/db/expert-db.js +171 -0
- package/local-runtime/server/db/identity-db.js +92 -0
- package/local-runtime/server/db/invitation-db.js +38 -0
- package/local-runtime/server/db/message-db.js +257 -0
- package/local-runtime/server/db/session-db.js +118 -0
- package/local-runtime/server/db/user-db.js +165 -0
- package/local-runtime/server/db/user-stats-db.js +185 -0
- package/local-runtime/server/db/yeaft-session-db.js +258 -0
- package/local-runtime/server/email.js +96 -0
- package/local-runtime/server/encryption.js +105 -0
- package/local-runtime/server/handlers/agent-conversation.js +347 -0
- package/local-runtime/server/handlers/agent-file-terminal.js +99 -0
- package/local-runtime/server/handlers/agent-output.js +854 -0
- package/local-runtime/server/handlers/agent-sync.js +399 -0
- package/local-runtime/server/handlers/agent-work-center.js +27 -0
- package/local-runtime/server/handlers/client-conversation.js +1182 -0
- package/local-runtime/server/handlers/client-misc.js +254 -0
- package/local-runtime/server/handlers/client-work-center.js +269 -0
- package/local-runtime/server/handlers/client-workbench.js +146 -0
- package/local-runtime/server/handlers/session-pin-router.js +61 -0
- package/local-runtime/server/heartbeat-policy.js +46 -0
- package/local-runtime/server/index.js +275 -0
- package/local-runtime/server/package.json +55 -0
- package/local-runtime/server/perf-trace.js +154 -0
- package/local-runtime/server/proxy.js +273 -0
- package/local-runtime/server/routes/admin-routes.js +207 -0
- package/local-runtime/server/routes/auth-routes.js +322 -0
- package/local-runtime/server/routes/expert-routes.js +117 -0
- package/local-runtime/server/routes/invitation-routes.js +60 -0
- package/local-runtime/server/routes/session-routes.js +112 -0
- package/local-runtime/server/routes/upload-routes.js +109 -0
- package/local-runtime/server/routes/user-routes.js +241 -0
- package/local-runtime/server/totp.js +74 -0
- package/local-runtime/server/work-item-attachment-policy.js +56 -0
- package/local-runtime/server/ws-agent.js +319 -0
- package/local-runtime/server/ws-client.js +214 -0
- package/local-runtime/server/ws-utils.js +394 -0
- package/local-runtime/server/yeaft-asset-store.js +339 -0
- package/local-runtime/version.json +1 -0
- package/local-runtime/web/app.bundle.js +7673 -0
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/assets/avatars/README.md +34 -0
- package/local-runtime/web/assets/avatars/ada.svg +1 -0
- package/local-runtime/web/assets/avatars/alan.svg +1 -0
- package/local-runtime/web/assets/avatars/alice.svg +1 -0
- package/local-runtime/web/assets/avatars/anders.svg +1 -0
- package/local-runtime/web/assets/avatars/bezos.svg +1 -0
- package/local-runtime/web/assets/avatars/borges.svg +1 -0
- package/local-runtime/web/assets/avatars/buffett.svg +1 -0
- package/local-runtime/web/assets/avatars/clausewitz.svg +1 -0
- package/local-runtime/web/assets/avatars/dalio.svg +1 -0
- package/local-runtime/web/assets/avatars/dieter.svg +1 -0
- package/local-runtime/web/assets/avatars/drucker.svg +1 -0
- package/local-runtime/web/assets/avatars/einstein.svg +1 -0
- package/local-runtime/web/assets/avatars/grace.svg +1 -0
- package/local-runtime/web/assets/avatars/harari.svg +1 -0
- package/local-runtime/web/assets/avatars/jung.svg +1 -0
- package/local-runtime/web/assets/avatars/kahneman.svg +1 -0
- package/local-runtime/web/assets/avatars/ken.svg +1 -0
- package/local-runtime/web/assets/avatars/kongzi.svg +1 -0
- package/local-runtime/web/assets/avatars/kubrick.svg +1 -0
- package/local-runtime/web/assets/avatars/linus.svg +1 -0
- package/local-runtime/web/assets/avatars/luxun.svg +1 -0
- package/local-runtime/web/assets/avatars/margaret.svg +1 -0
- package/local-runtime/web/assets/avatars/martin.svg +1 -0
- package/local-runtime/web/assets/avatars/miyazaki.svg +1 -0
- package/local-runtime/web/assets/avatars/munger.svg +1 -0
- package/local-runtime/web/assets/avatars/nietzsche.svg +1 -0
- package/local-runtime/web/assets/avatars/norman.svg +1 -0
- package/local-runtime/web/assets/avatars/shannon.svg +1 -0
- package/local-runtime/web/assets/avatars/simaqian.svg +1 -0
- package/local-runtime/web/assets/avatars/socrates.svg +1 -0
- package/local-runtime/web/assets/avatars/steve.svg +1 -0
- package/local-runtime/web/assets/avatars/sudongpo.svg +1 -0
- package/local-runtime/web/assets/avatars/sunzi.svg +1 -0
- package/local-runtime/web/docx-preview.min.js +2 -0
- package/local-runtime/web/docx-preview.min.js.gz +0 -0
- package/local-runtime/web/html-to-image.min.js +2 -0
- package/local-runtime/web/html-to-image.min.js.gz +0 -0
- package/local-runtime/web/index.html +21 -0
- package/local-runtime/web/jszip.min.js +13 -0
- package/local-runtime/web/jszip.min.js.gz +0 -0
- package/local-runtime/web/mermaid.min.js +2843 -0
- package/local-runtime/web/mermaid.min.js.gz +0 -0
- package/local-runtime/web/msal-browser.min.js +69 -0
- package/local-runtime/web/msal-browser.min.js.gz +0 -0
- package/local-runtime/web/style.bundle.css +10 -0
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/local-runtime/web/vendor.bundle.js +1522 -0
- package/local-runtime/web/vendor.bundle.js.gz +0 -0
- package/local-runtime/web/xlsx.min.js +24 -0
- package/local-runtime/web/xlsx.min.js.gz +0 -0
- package/package.json +13 -3
- package/scripts/prepare-local-runtime.js +25 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { CONFIG } from '../config.js';
|
|
2
|
+
import { sessionDb, userStatsDb } from '../database.js';
|
|
3
|
+
import { agents, webClients } from '../context.js';
|
|
4
|
+
import { sendToWebClient, broadcastAgentList } from '../ws-utils.js';
|
|
5
|
+
import {
|
|
6
|
+
handleProxyResponse, handleProxyResponseChunk, handleProxyResponseEnd,
|
|
7
|
+
handleProxyWsAgentMessage
|
|
8
|
+
} from '../proxy.js';
|
|
9
|
+
|
|
10
|
+
const numberMetric = (value) => {
|
|
11
|
+
const n = Number(value);
|
|
12
|
+
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function normalizeAgentMetrics(metrics = {}) {
|
|
16
|
+
const chatTurns = numberMetric(metrics.chatTurns);
|
|
17
|
+
const yeaftTurns = numberMetric(metrics.yeaftTurns);
|
|
18
|
+
const totalTurns = numberMetric(metrics.totalTurns) || chatTurns + yeaftTurns;
|
|
19
|
+
const inputTokens = numberMetric(metrics.inputTokens);
|
|
20
|
+
const outputTokens = numberMetric(metrics.outputTokens);
|
|
21
|
+
const cacheReadTokens = numberMetric(metrics.cacheReadTokens);
|
|
22
|
+
const cacheWriteTokens = numberMetric(metrics.cacheWriteTokens);
|
|
23
|
+
const totalTokens = numberMetric(metrics.totalTokens) || inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens;
|
|
24
|
+
return {
|
|
25
|
+
metricEpoch: typeof metrics.metricEpoch === 'string' ? metrics.metricEpoch : null,
|
|
26
|
+
chatTurns,
|
|
27
|
+
yeaftTurns,
|
|
28
|
+
totalTurns,
|
|
29
|
+
sessionsCreated: numberMetric(metrics.sessionsCreated),
|
|
30
|
+
inputTokens,
|
|
31
|
+
outputTokens,
|
|
32
|
+
cacheReadTokens,
|
|
33
|
+
cacheWriteTokens,
|
|
34
|
+
totalTokens,
|
|
35
|
+
lastUpdatedAt: numberMetric(metrics.lastUpdatedAt) || null,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function forwardAgentMetrics(agentId, agent) {
|
|
40
|
+
const payload = {
|
|
41
|
+
type: 'agent_metrics',
|
|
42
|
+
agentId,
|
|
43
|
+
metrics: agent.metrics,
|
|
44
|
+
metricsUpdatedAt: agent.metricsUpdatedAt || null,
|
|
45
|
+
};
|
|
46
|
+
for (const [, client] of webClients) {
|
|
47
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
48
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
49
|
+
(!agent.ownerId && client.role === 'admin')
|
|
50
|
+
)) {
|
|
51
|
+
await sendToWebClient(client, payload);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Handle sync, proxy, and agent control messages from agent.
|
|
58
|
+
* Types: agent_sync_complete, sync_sessions, agent_metrics,
|
|
59
|
+
* proxy_response, proxy_response_chunk, proxy_response_end,
|
|
60
|
+
* proxy_ports_update, proxy_ws_opened/message/closed/error,
|
|
61
|
+
* restart_agent_ack, upgrade_agent_ack
|
|
62
|
+
*/
|
|
63
|
+
export async function handleAgentSync(agentId, agent, msg) {
|
|
64
|
+
switch (msg.type) {
|
|
65
|
+
// Phase 1: Agent 同步完成
|
|
66
|
+
case 'agent_sync_complete': {
|
|
67
|
+
agent.status = 'ready';
|
|
68
|
+
if (agent._syncTimeout) {
|
|
69
|
+
clearTimeout(agent._syncTimeout);
|
|
70
|
+
delete agent._syncTimeout;
|
|
71
|
+
}
|
|
72
|
+
console.log(`[Sync] Agent ${agent.name} sync complete, status: ready`);
|
|
73
|
+
await broadcastAgentList();
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
case 'agent_metrics': {
|
|
78
|
+
agent.metrics = normalizeAgentMetrics(msg.metrics || {});
|
|
79
|
+
agent.metricsUpdatedAt = Date.now();
|
|
80
|
+
if (agent.ownerId && agent.metrics.metricEpoch) {
|
|
81
|
+
try {
|
|
82
|
+
userStatsDb.recordAgentTokenSnapshot(
|
|
83
|
+
agent.ownerId,
|
|
84
|
+
agent.instanceId || agentId,
|
|
85
|
+
agent.metrics
|
|
86
|
+
);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error(`[AgentMetrics] Failed to persist token usage for ${agentId}:`, error.message);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
await forwardAgentMetrics(agentId, agent);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Phase 2: Session 同步
|
|
96
|
+
case 'sync_sessions': {
|
|
97
|
+
const sessions = msg.sessions || [];
|
|
98
|
+
// Security: 限制单次同步的 session 数量
|
|
99
|
+
const MAX_SYNC_SESSIONS = 1000;
|
|
100
|
+
if (sessions.length > MAX_SYNC_SESSIONS) {
|
|
101
|
+
console.warn(`[Security] Agent ${agentId} tried to sync ${sessions.length} sessions (limit: ${MAX_SYNC_SESSIONS}), truncating`);
|
|
102
|
+
}
|
|
103
|
+
const safeSessions = sessions.slice(0, MAX_SYNC_SESSIONS);
|
|
104
|
+
console.log(`[Sync] Received ${safeSessions.length} sessions from agent ${agent.name}`);
|
|
105
|
+
let created = 0, updated = 0;
|
|
106
|
+
for (const s of safeSessions) {
|
|
107
|
+
// Security: 校验 sessionId 格式
|
|
108
|
+
if (!s.sessionId || typeof s.sessionId !== 'string' || s.sessionId.length > 200) continue;
|
|
109
|
+
try {
|
|
110
|
+
const existing = sessionDb.get(s.sessionId);
|
|
111
|
+
if (!existing) {
|
|
112
|
+
// Security: 强制使用 agent.ownerId
|
|
113
|
+
sessionDb.create(s.sessionId, agentId, agent.name, s.workDir, s.sessionId, s.title, agent.ownerId || null);
|
|
114
|
+
// Auto-deactivate old sessions synced from disk (not modified in 2 days)
|
|
115
|
+
if (s.lastModified && s.lastModified < Date.now() - 2 * 24 * 60 * 60 * 1000) {
|
|
116
|
+
sessionDb.setActive(s.sessionId, false);
|
|
117
|
+
}
|
|
118
|
+
created++;
|
|
119
|
+
} else {
|
|
120
|
+
// fix-chat-title-sticky: don't let the agent's bulk title
|
|
121
|
+
// sync overwrite a user-renamed session. The title in the
|
|
122
|
+
// DB is sticky once `customTitle` is set; only the
|
|
123
|
+
// agent's claudeSessionId / activity timestamp need to
|
|
124
|
+
// refresh on this path.
|
|
125
|
+
if (s.lastModified > existing.updated_at && !existing.customTitle) {
|
|
126
|
+
sessionDb.update(s.sessionId, { title: s.title });
|
|
127
|
+
}
|
|
128
|
+
updated++;
|
|
129
|
+
}
|
|
130
|
+
} catch (e) {
|
|
131
|
+
console.error(`[Sync] Error syncing session ${s.sessionId}:`, e.message);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
console.log(`[Sync] Sessions synced: ${created} created, ${updated} existing`);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Port proxy responses
|
|
139
|
+
case 'proxy_response':
|
|
140
|
+
handleProxyResponse(msg);
|
|
141
|
+
break;
|
|
142
|
+
|
|
143
|
+
case 'proxy_response_chunk':
|
|
144
|
+
handleProxyResponseChunk(msg);
|
|
145
|
+
break;
|
|
146
|
+
|
|
147
|
+
case 'proxy_response_end':
|
|
148
|
+
handleProxyResponseEnd(msg);
|
|
149
|
+
break;
|
|
150
|
+
|
|
151
|
+
case 'proxy_ports_update': {
|
|
152
|
+
const a = agents.get(agentId);
|
|
153
|
+
if (a) {
|
|
154
|
+
a.proxyPorts = msg.ports || [];
|
|
155
|
+
await broadcastAgentList();
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
case 'restart_agent_ack': {
|
|
161
|
+
// 只通知该 Agent 的 owner
|
|
162
|
+
for (const [, client] of webClients) {
|
|
163
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
164
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
165
|
+
(!agent.ownerId && client.role === 'admin')
|
|
166
|
+
)) {
|
|
167
|
+
await sendToWebClient(client, { type: 'restart_agent_ack', agentId });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
case 'upgrade_agent_ack': {
|
|
174
|
+
for (const [, client] of webClients) {
|
|
175
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
176
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
177
|
+
(!agent.ownerId && client.role === 'admin')
|
|
178
|
+
)) {
|
|
179
|
+
await sendToWebClient(client, { type: 'upgrade_agent_ack', agentId, success: msg.success, error: msg.error, alreadyLatest: msg.alreadyLatest, version: msg.version, reason: msg.reason, currentNode: msg.currentNode, requiredNode: msg.requiredNode });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Proxy WebSocket messages from agent to browser
|
|
186
|
+
case 'proxy_ws_opened':
|
|
187
|
+
case 'proxy_ws_message':
|
|
188
|
+
case 'proxy_ws_closed':
|
|
189
|
+
case 'proxy_ws_error':
|
|
190
|
+
handleProxyWsAgentMessage(msg);
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
// MCP servers list from agent — store on agent and broadcast to owner clients
|
|
194
|
+
case 'mcp_servers_list': {
|
|
195
|
+
agent.mcpServers = msg.servers || [];
|
|
196
|
+
console.log(`[MCP] Agent ${agent.name} reported ${agent.mcpServers.length} MCP servers`);
|
|
197
|
+
for (const [, client] of webClients) {
|
|
198
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
199
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
200
|
+
(!agent.ownerId && client.role === 'admin')
|
|
201
|
+
)) {
|
|
202
|
+
await sendToWebClient(client, {
|
|
203
|
+
type: 'mcp_servers_list',
|
|
204
|
+
agentId,
|
|
205
|
+
servers: agent.mcpServers
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Expert roles definition from agent — forward to owner clients
|
|
213
|
+
case 'expert_roles_list': {
|
|
214
|
+
for (const [, client] of webClients) {
|
|
215
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
216
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
217
|
+
(!agent.ownerId && client.role === 'admin')
|
|
218
|
+
)) {
|
|
219
|
+
await sendToWebClient(client, {
|
|
220
|
+
type: 'expert_roles_list',
|
|
221
|
+
agentId,
|
|
222
|
+
roles: msg.roles
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// MCP config updated acknowledgement from agent
|
|
230
|
+
case 'mcp_config_updated': {
|
|
231
|
+
agent.mcpServers = msg.servers || [];
|
|
232
|
+
for (const [, client] of webClients) {
|
|
233
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
234
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
235
|
+
(!agent.ownerId && client.role === 'admin')
|
|
236
|
+
)) {
|
|
237
|
+
await sendToWebClient(client, {
|
|
238
|
+
type: 'mcp_config_updated',
|
|
239
|
+
agentId,
|
|
240
|
+
servers: agent.mcpServers
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// LLM config response from agent — relay to owner clients
|
|
248
|
+
case 'llm_config': {
|
|
249
|
+
for (const [, client] of webClients) {
|
|
250
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
251
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
252
|
+
(!agent.ownerId && client.role === 'admin')
|
|
253
|
+
)) {
|
|
254
|
+
await sendToWebClient(client, {
|
|
255
|
+
type: 'llm_config',
|
|
256
|
+
agentId,
|
|
257
|
+
providers: msg.providers,
|
|
258
|
+
primaryModel: msg.primaryModel,
|
|
259
|
+
fastModel: msg.fastModel,
|
|
260
|
+
language: msg.language,
|
|
261
|
+
needsSetup: msg.needsSetup,
|
|
262
|
+
agentConfig: msg.agentConfig,
|
|
263
|
+
effectiveConfig: msg.effectiveConfig,
|
|
264
|
+
error: msg.error
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// LLM model discovery response from agent — relay to owner clients
|
|
272
|
+
case 'llm_models_discovered': {
|
|
273
|
+
for (const [, client] of webClients) {
|
|
274
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
275
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
276
|
+
(!agent.ownerId && client.role === 'admin')
|
|
277
|
+
)) {
|
|
278
|
+
await sendToWebClient(client, {
|
|
279
|
+
type: 'llm_models_discovered',
|
|
280
|
+
agentId,
|
|
281
|
+
requestId: msg.requestId,
|
|
282
|
+
providerType: msg.providerType,
|
|
283
|
+
provider: msg.provider,
|
|
284
|
+
models: msg.models || [],
|
|
285
|
+
providerModels: msg.providerModels || [],
|
|
286
|
+
source: msg.source,
|
|
287
|
+
warning: msg.warning,
|
|
288
|
+
error: msg.error,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
case 'llm_config_updated': {
|
|
296
|
+
for (const [, client] of webClients) {
|
|
297
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
298
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
299
|
+
(!agent.ownerId && client.role === 'admin')
|
|
300
|
+
)) {
|
|
301
|
+
await sendToWebClient(client, {
|
|
302
|
+
type: 'llm_config_updated',
|
|
303
|
+
agentId,
|
|
304
|
+
providers: msg.providers,
|
|
305
|
+
primaryModel: msg.primaryModel,
|
|
306
|
+
fastModel: msg.fastModel,
|
|
307
|
+
language: msg.language,
|
|
308
|
+
agentConfig: msg.agentConfig,
|
|
309
|
+
effectiveConfig: msg.effectiveConfig,
|
|
310
|
+
error: msg.error
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// models.dev registry response from agent — relay to owner clients
|
|
318
|
+
case 'models_dev_registry': {
|
|
319
|
+
for (const [, client] of webClients) {
|
|
320
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
321
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
322
|
+
(!agent.ownerId && client.role === 'admin')
|
|
323
|
+
)) {
|
|
324
|
+
await sendToWebClient(client, {
|
|
325
|
+
type: 'models_dev_registry',
|
|
326
|
+
agentId,
|
|
327
|
+
requestId: msg.requestId,
|
|
328
|
+
registry: msg.registry,
|
|
329
|
+
fetchedAt: msg.fetchedAt,
|
|
330
|
+
error: msg.error,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// task-318: Yeaft runtime settings read / update ack — relay to owner
|
|
338
|
+
case 'yeaft_settings':
|
|
339
|
+
case 'yeaft_settings_updated': {
|
|
340
|
+
for (const [, client] of webClients) {
|
|
341
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
342
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
343
|
+
(!agent.ownerId && client.role === 'admin')
|
|
344
|
+
)) {
|
|
345
|
+
await sendToWebClient(client, {
|
|
346
|
+
type: msg.type,
|
|
347
|
+
agentId,
|
|
348
|
+
maxConcurrentThreads: msg.maxConcurrentThreads,
|
|
349
|
+
autoArchiveIdleDays: msg.autoArchiveIdleDays,
|
|
350
|
+
error: msg.error
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Search settings + Tavily usage relays. We pass the whole msg
|
|
358
|
+
// through (minus agentId, which we set ourselves) — the payload
|
|
359
|
+
// shapes differ per type and the front-end already filters on
|
|
360
|
+
// `type`, so a generic forward is simpler than three nearly-
|
|
361
|
+
// identical branches.
|
|
362
|
+
case 'search_settings':
|
|
363
|
+
case 'search_settings_updated':
|
|
364
|
+
case 'tavily_usage': {
|
|
365
|
+
for (const [, client] of webClients) {
|
|
366
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
367
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
368
|
+
(!agent.ownerId && client.role === 'admin')
|
|
369
|
+
)) {
|
|
370
|
+
await sendToWebClient(client, { ...msg, agentId });
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Yeaft MCP CRUD result + live-update broadcast. Same generic pass-
|
|
377
|
+
// through as the search-settings branch above — the payload shape
|
|
378
|
+
// is documented in web-bridge.js handlers (`handleYeaftMcp*`).
|
|
379
|
+
case 'yeaft_mcp_list_result':
|
|
380
|
+
case 'yeaft_mcp_add_result':
|
|
381
|
+
case 'yeaft_mcp_remove_result':
|
|
382
|
+
case 'yeaft_mcp_reload_result':
|
|
383
|
+
case 'yeaft_mcp_updated': {
|
|
384
|
+
for (const [, client] of webClients) {
|
|
385
|
+
if (client.authenticated && (CONFIG.skipAuth ||
|
|
386
|
+
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
387
|
+
(!agent.ownerId && client.role === 'admin')
|
|
388
|
+
)) {
|
|
389
|
+
await sendToWebClient(client, { ...msg, agentId });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
default:
|
|
396
|
+
return false; // Not handled
|
|
397
|
+
}
|
|
398
|
+
return true; // Handled
|
|
399
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { forwardAgentEvent } from '../ws-utils.js';
|
|
2
|
+
import { deliverWorkCenterResponse } from './client-work-center.js';
|
|
3
|
+
|
|
4
|
+
const WORK_CENTER_TYPES = new Set([
|
|
5
|
+
'work_center_response',
|
|
6
|
+
'work_center_event',
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Forward Agent-local Work Center responses and projection events.
|
|
11
|
+
*
|
|
12
|
+
* Responses use a server-owned request map. Events have no requester and are
|
|
13
|
+
* therefore projected only through the authenticated Agent owner boundary.
|
|
14
|
+
*/
|
|
15
|
+
export async function handleAgentWorkCenter(agentId, msg) {
|
|
16
|
+
if (!WORK_CENTER_TYPES.has(msg?.type)) return false;
|
|
17
|
+
|
|
18
|
+
if (msg.type === 'work_center_response') {
|
|
19
|
+
await deliverWorkCenterResponse(agentId, msg);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { agentId: _untrustedAgentId, _requestUserId, ...payload } = msg;
|
|
24
|
+
const outgoing = { ...payload, agentId };
|
|
25
|
+
await forwardAgentEvent(agentId, outgoing);
|
|
26
|
+
return true;
|
|
27
|
+
}
|