@yeaft/webchat-agent 1.0.458 → 1.0.460
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/message-router.js +6 -6
- package/connection/upgrade.js +20 -16
- package/index.js +1 -1
- package/local-runtime/server/context.js +101 -3
- package/local-runtime/server/handlers/agent-sync.js +37 -36
- package/local-runtime/server/handlers/client-conversation.js +10 -3
- package/local-runtime/server/handlers/client-misc.js +62 -19
- package/local-runtime/server/ws-agent.js +59 -5
- package/local-runtime/server/ws-client.js +7 -1
- package/local-runtime/server/ws-utils.js +7 -6
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +228 -116
- 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
|
@@ -350,7 +350,7 @@ export async function handleMessage(msg) {
|
|
|
350
350
|
break;
|
|
351
351
|
|
|
352
352
|
case 'restart_agent':
|
|
353
|
-
await handleRestartAgent();
|
|
353
|
+
await handleRestartAgent({ requestId: msg.requestId, clientId: msg.clientId });
|
|
354
354
|
break;
|
|
355
355
|
|
|
356
356
|
case 'set_dream_enabled': {
|
|
@@ -363,15 +363,15 @@ export async function handleMessage(msg) {
|
|
|
363
363
|
const runtime = await ensureSessionLoaded();
|
|
364
364
|
runtime.config.dream = { ...(runtime.config.dream || {}), enabled };
|
|
365
365
|
runtime.dreamScheduler?.setEnabled?.(enabled);
|
|
366
|
-
sendToServer({ type: 'dream_enabled_changed', enabled });
|
|
366
|
+
sendToServer({ type: 'dream_enabled_changed', enabled, requestId: msg.requestId, clientId: msg.clientId });
|
|
367
367
|
} catch (error) {
|
|
368
|
-
sendToServer({ type: 'dream_enabled_changed', enabled: !enabled, error: error?.message || String(error) });
|
|
368
|
+
sendToServer({ type: 'dream_enabled_changed', enabled: !enabled, error: error?.message || String(error), requestId: msg.requestId, clientId: msg.clientId });
|
|
369
369
|
}
|
|
370
370
|
break;
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
case 'upgrade_agent':
|
|
374
|
-
await handleUpgradeAgent();
|
|
374
|
+
await handleUpgradeAgent({ requestId: msg.requestId, clientId: msg.clientId });
|
|
375
375
|
break;
|
|
376
376
|
|
|
377
377
|
// MCP configuration
|
|
@@ -495,7 +495,7 @@ export async function handleMessage(msg) {
|
|
|
495
495
|
// section of config.json. This does not expose trace payloads.
|
|
496
496
|
case 'get_telemetry_settings': {
|
|
497
497
|
const settings = getTelemetrySettings(ctx.CONFIG?.yeaftDir);
|
|
498
|
-
sendToServer({ type: 'telemetry_settings', ...settings });
|
|
498
|
+
sendToServer({ type: 'telemetry_settings', requestId: msg.requestId, clientId: msg.clientId, ...settings });
|
|
499
499
|
break;
|
|
500
500
|
}
|
|
501
501
|
|
|
@@ -515,7 +515,7 @@ export async function handleMessage(msg) {
|
|
|
515
515
|
result.runtimeRefreshError = error?.message || String(error);
|
|
516
516
|
}
|
|
517
517
|
}
|
|
518
|
-
sendToServer({ type: 'telemetry_settings_updated', ...result });
|
|
518
|
+
sendToServer({ type: 'telemetry_settings_updated', requestId: msg.requestId, clientId: msg.clientId, ...result });
|
|
519
519
|
break;
|
|
520
520
|
}
|
|
521
521
|
|
package/connection/upgrade.js
CHANGED
|
@@ -92,9 +92,9 @@ function cleanupAndExit(exitCode) {
|
|
|
92
92
|
}, 500);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
export async function handleRestartAgent() {
|
|
95
|
+
export async function handleRestartAgent(correlation = {}) {
|
|
96
96
|
console.log('[Agent] Restart requested, shutting down for PM2/systemd restart...');
|
|
97
|
-
await sendToServer({ type: 'restart_agent_ack' });
|
|
97
|
+
await sendToServer({ type: 'restart_agent_ack', requestId: correlation.requestId, clientId: correlation.clientId });
|
|
98
98
|
cleanupAndExit(1);
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -178,13 +178,18 @@ function compareCmp(cur, cmp) {
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
export async function handleUpgradeAgent() {
|
|
181
|
+
export async function handleUpgradeAgent(correlation = {}) {
|
|
182
|
+
const sendUpgradeAck = (payload) => sendToServer({
|
|
183
|
+
type: 'upgrade_agent_ack',
|
|
184
|
+
...payload,
|
|
185
|
+
requestId: correlation.requestId,
|
|
186
|
+
clientId: correlation.clientId,
|
|
187
|
+
});
|
|
182
188
|
if (isContainerAgentRuntime() || ctx.agentCapabilities?.includes(CONTAINER_AGENT_CAPABILITY)) {
|
|
183
189
|
const version = ctx.agentVersion || null;
|
|
184
190
|
const error = buildContainerImageUpgradeMessage(version);
|
|
185
191
|
console.warn(`[Agent] Upgrade rejected (${CONTAINER_IMAGE_UPGRADE_REASON}): ${error}`);
|
|
186
|
-
await
|
|
187
|
-
type: 'upgrade_agent_ack',
|
|
192
|
+
await sendUpgradeAck({
|
|
188
193
|
success: false,
|
|
189
194
|
reason: CONTAINER_IMAGE_UPGRADE_REASON,
|
|
190
195
|
error,
|
|
@@ -205,7 +210,7 @@ export async function handleUpgradeAgent() {
|
|
|
205
210
|
});
|
|
206
211
|
if (!isUpgradeAvailable(ctx.agentVersion, latestVersion)) {
|
|
207
212
|
console.log(`[Agent] No newer version than ${ctx.agentVersion} is available (registry: ${latestVersion}), skipping upgrade.`);
|
|
208
|
-
await
|
|
213
|
+
await sendUpgradeAck({ success: true, alreadyLatest: true, version: ctx.agentVersion });
|
|
209
214
|
return;
|
|
210
215
|
}
|
|
211
216
|
|
|
@@ -218,8 +223,7 @@ export async function handleUpgradeAgent() {
|
|
|
218
223
|
if (requiredNode && !nodeRangeSatisfied(currentNode, requiredNode)) {
|
|
219
224
|
const msg = `Node ${currentNode} does not satisfy required ${requiredNode} for ${pkgName}@${latestVersion}`;
|
|
220
225
|
console.warn(`[Agent] Upgrade aborted: ${msg}`);
|
|
221
|
-
await
|
|
222
|
-
type: 'upgrade_agent_ack',
|
|
226
|
+
await sendUpgradeAck({
|
|
223
227
|
success: false,
|
|
224
228
|
reason: 'node_incompatible',
|
|
225
229
|
error: msg,
|
|
@@ -240,7 +244,7 @@ export async function handleUpgradeAgent() {
|
|
|
240
244
|
if (!isNpmInstall) {
|
|
241
245
|
// 源码运行不支持远程升级(代码在 git repo 中,需要手动 git pull)
|
|
242
246
|
console.log('[Agent] Source-based install detected, remote upgrade not supported.');
|
|
243
|
-
await
|
|
247
|
+
await sendUpgradeAck({ success: false, error: 'Source-based install: please use git pull to upgrade' });
|
|
244
248
|
return;
|
|
245
249
|
}
|
|
246
250
|
|
|
@@ -260,9 +264,9 @@ export async function handleUpgradeAgent() {
|
|
|
260
264
|
const instanceId = resolveInstanceId();
|
|
261
265
|
|
|
262
266
|
if (isWindows) {
|
|
263
|
-
await spawnWindowsUpgradeScript(pkgName, installDir, isGlobalInstall, latestVersion, instanceId);
|
|
267
|
+
await spawnWindowsUpgradeScript(pkgName, installDir, isGlobalInstall, latestVersion, instanceId, sendUpgradeAck);
|
|
264
268
|
} else {
|
|
265
|
-
await spawnUnixUpgradeScript(pkgName, installDir, isGlobalInstall, latestVersion, instanceId);
|
|
269
|
+
await spawnUnixUpgradeScript(pkgName, installDir, isGlobalInstall, latestVersion, instanceId, sendUpgradeAck);
|
|
266
270
|
}
|
|
267
271
|
|
|
268
272
|
// On Windows the detached runner removes the PM2 app only after this
|
|
@@ -281,11 +285,11 @@ export async function handleUpgradeAgent() {
|
|
|
281
285
|
cleanupAndExit(0);
|
|
282
286
|
} catch (e) {
|
|
283
287
|
console.error('[Agent] Upgrade failed:', e.message);
|
|
284
|
-
await
|
|
288
|
+
await sendUpgradeAck({ success: false, error: e.message });
|
|
285
289
|
}
|
|
286
290
|
}
|
|
287
291
|
|
|
288
|
-
async function spawnWindowsUpgradeScript(pkgName, installDir, isGlobalInstall, latestVersion, instanceId = DEFAULT_INSTANCE_ID) {
|
|
292
|
+
async function spawnWindowsUpgradeScript(pkgName, installDir, isGlobalInstall, latestVersion, instanceId = DEFAULT_INSTANCE_ID, sendUpgradeAck = (payload) => sendToServer({ type: 'upgrade_agent_ack', ...payload })) {
|
|
289
293
|
const configDir = getConfigDir(instanceId);
|
|
290
294
|
const upgradeDir = join(configDir, 'upgrade-runtime');
|
|
291
295
|
const logDir = join(configDir, 'logs');
|
|
@@ -364,7 +368,7 @@ async function spawnWindowsUpgradeScript(pkgName, installDir, isGlobalInstall, l
|
|
|
364
368
|
spawnProcess: spawn,
|
|
365
369
|
});
|
|
366
370
|
console.log(`[Agent] Spawned Windows upgrade runner via ${launcher} (pm2=${isPm2}, dir=${installDir})`);
|
|
367
|
-
await
|
|
371
|
+
await sendUpgradeAck({ success: true, version: latestVersion, pendingRestart: true });
|
|
368
372
|
}
|
|
369
373
|
|
|
370
374
|
/**
|
|
@@ -529,7 +533,7 @@ export function buildUnixUpgradeScript({
|
|
|
529
533
|
return shLines.join('\n');
|
|
530
534
|
}
|
|
531
535
|
|
|
532
|
-
async function spawnUnixUpgradeScript(pkgName, installDir, isGlobalInstall, latestVersion, instanceId = DEFAULT_INSTANCE_ID) {
|
|
536
|
+
async function spawnUnixUpgradeScript(pkgName, installDir, isGlobalInstall, latestVersion, instanceId = DEFAULT_INSTANCE_ID, sendUpgradeAck = (payload) => sendToServer({ type: 'upgrade_agent_ack', ...payload })) {
|
|
533
537
|
const configDir = getConfigDir(instanceId);
|
|
534
538
|
// Create logs/ too: the generated script's `exec > "$LOGFILE" 2>&1` aborts
|
|
535
539
|
// the whole upgrade silently if that directory is missing.
|
|
@@ -555,5 +559,5 @@ async function spawnUnixUpgradeScript(pkgName, installDir, isGlobalInstall, late
|
|
|
555
559
|
});
|
|
556
560
|
child.unref();
|
|
557
561
|
console.log(`[Agent] Spawned upgrade script: ${shPath}`);
|
|
558
|
-
await
|
|
562
|
+
await sendUpgradeAck({ success: true, version: latestVersion, pendingRestart: true });
|
|
559
563
|
}
|
package/index.js
CHANGED
|
@@ -175,7 +175,7 @@ async function detectCapabilities() {
|
|
|
175
175
|
// agent build can speak plaintext WS frames. New servers see this and
|
|
176
176
|
// flip `agent.encryptOutbound = false`, stopping outbound encryption
|
|
177
177
|
// to this peer. Old servers ignore the unknown capability token.
|
|
178
|
-
const capabilities = ['background_tasks', 'file_editor', 'ping_session', 'plaintext-ok', 'workbench_session_routes', 'work_center', 'work_center_message_v2', 'session_history_search', 'session_history_outline', 'session_history_window_prefetch', 'file_reference_resolution', 'yeaft_plugins', 'yeaft_managed_skills'];
|
|
178
|
+
const capabilities = ['background_tasks', 'file_editor', 'ping_session', 'plaintext-ok', 'workbench_session_routes', 'work_center', 'work_center_message_v2', 'session_history_search', 'session_history_outline', 'session_history_window_prefetch', 'file_reference_resolution', 'yeaft_plugins', 'yeaft_managed_skills', 'settings_request_correlation'];
|
|
179
179
|
capabilities.push(getAgentUpgradeCapability());
|
|
180
180
|
if (process.platform === 'linux') capabilities.push('work_item_attachments');
|
|
181
181
|
const pty = await loadNodePty();
|
|
@@ -43,8 +43,104 @@ export const previewFiles = new Map();
|
|
|
43
43
|
// original Agent/Session ownership check happened, and consume it exactly once.
|
|
44
44
|
const YEAFT_DEBUG_REQUEST_TTL_MS = 30_000;
|
|
45
45
|
const YEAFT_DEBUG_REQUEST_MAX_PENDING = 2048;
|
|
46
|
+
const YEAFT_DEBUG_REQUEST_MAX_PENDING_PER_CLIENT = 256;
|
|
46
47
|
export const pendingYeaftDebugRequests = new Map();
|
|
47
48
|
|
|
49
|
+
// Agent settings replies share one Agent connection across browser clients.
|
|
50
|
+
// Correlation-capable Agents require exact requestId matching. Legacy Agents are
|
|
51
|
+
// allowed one in-flight request per Agent/operation so an identity-less reply has
|
|
52
|
+
// exactly one possible owner.
|
|
53
|
+
const AGENT_SETTINGS_REQUEST_TTL_MS = 120_000;
|
|
54
|
+
const AGENT_SETTINGS_REQUEST_MAX_PENDING = 2048;
|
|
55
|
+
const AGENT_SETTINGS_REQUEST_MAX_PENDING_PER_CLIENT = 256;
|
|
56
|
+
export const pendingAgentSettingsRequests = new Map();
|
|
57
|
+
|
|
58
|
+
function agentSettingsRequestKey(agentId, requestId) {
|
|
59
|
+
return `${String(agentId || '')}\u0000${String(requestId || '')}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function pruneAgentSettingsRequests(now = Date.now()) {
|
|
63
|
+
for (const [key, pending] of pendingAgentSettingsRequests) {
|
|
64
|
+
if (!pending || pending.expiresAt <= now || !webClients.has(pending.clientId)) {
|
|
65
|
+
pendingAgentSettingsRequests.delete(key);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Register an owner-checked settings request before it is sent to the Agent. */
|
|
71
|
+
export function registerAgentSettingsRequest({ agentId, operation, requestId, clientId, allowLegacyReply = false }) {
|
|
72
|
+
if (!agentId || !operation || !requestId || !clientId) return false;
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
pruneAgentSettingsRequests(now);
|
|
75
|
+
const key = agentSettingsRequestKey(agentId, requestId);
|
|
76
|
+
if (pendingAgentSettingsRequests.has(key)) return false;
|
|
77
|
+
let clientPending = 0;
|
|
78
|
+
for (const pending of pendingAgentSettingsRequests.values()) {
|
|
79
|
+
if (pending?.clientId === clientId) clientPending++;
|
|
80
|
+
if (allowLegacyReply && pending?.agentId === agentId && pending?.operation === operation) return false;
|
|
81
|
+
}
|
|
82
|
+
if (clientPending >= AGENT_SETTINGS_REQUEST_MAX_PENDING_PER_CLIENT
|
|
83
|
+
|| pendingAgentSettingsRequests.size >= AGENT_SETTINGS_REQUEST_MAX_PENDING) return false;
|
|
84
|
+
pendingAgentSettingsRequests.set(key, {
|
|
85
|
+
agentId,
|
|
86
|
+
operation,
|
|
87
|
+
requestId,
|
|
88
|
+
clientId,
|
|
89
|
+
allowLegacyReply,
|
|
90
|
+
expiresAt: now + AGENT_SETTINGS_REQUEST_TTL_MS,
|
|
91
|
+
});
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Consume an exact response, or the sole explicitly legacy-compatible request. */
|
|
96
|
+
export function consumeAgentSettingsRequest({ agentId, operation, requestId }) {
|
|
97
|
+
if (!agentId || !operation) return null;
|
|
98
|
+
pruneAgentSettingsRequests();
|
|
99
|
+
if (requestId) {
|
|
100
|
+
const key = agentSettingsRequestKey(agentId, requestId);
|
|
101
|
+
const pending = pendingAgentSettingsRequests.get(key);
|
|
102
|
+
if (!pending || pending.operation !== operation) return null;
|
|
103
|
+
pendingAgentSettingsRequests.delete(key);
|
|
104
|
+
return pending;
|
|
105
|
+
}
|
|
106
|
+
let matchKey = null;
|
|
107
|
+
let match = null;
|
|
108
|
+
for (const [key, pending] of pendingAgentSettingsRequests) {
|
|
109
|
+
if (pending?.agentId !== agentId || pending?.operation !== operation || !pending.allowLegacyReply) continue;
|
|
110
|
+
if (match) return null;
|
|
111
|
+
matchKey = key;
|
|
112
|
+
match = pending;
|
|
113
|
+
}
|
|
114
|
+
if (!match) return null;
|
|
115
|
+
pendingAgentSettingsRequests.delete(matchKey);
|
|
116
|
+
return match;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function deleteAgentSettingsRequest({ agentId, requestId, clientId = null }) {
|
|
120
|
+
const key = agentSettingsRequestKey(agentId, requestId);
|
|
121
|
+
const pending = pendingAgentSettingsRequests.get(key);
|
|
122
|
+
if (!pending || (clientId && pending.clientId !== clientId)) return false;
|
|
123
|
+
return pendingAgentSettingsRequests.delete(key);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function clearAgentSettingsRequestsForClient(clientId) {
|
|
127
|
+
if (!clientId) return;
|
|
128
|
+
for (const [key, pending] of pendingAgentSettingsRequests) {
|
|
129
|
+
if (pending?.clientId === clientId) pendingAgentSettingsRequests.delete(key);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function takeAgentSettingsRequestsForAgent(agentId) {
|
|
134
|
+
const removed = [];
|
|
135
|
+
if (!agentId) return removed;
|
|
136
|
+
for (const [key, pending] of pendingAgentSettingsRequests) {
|
|
137
|
+
if (pending?.agentId !== agentId) continue;
|
|
138
|
+
pendingAgentSettingsRequests.delete(key);
|
|
139
|
+
removed.push(pending);
|
|
140
|
+
}
|
|
141
|
+
return removed;
|
|
142
|
+
}
|
|
143
|
+
|
|
48
144
|
function yeaftDebugRequestKey(agentId, requestId) {
|
|
49
145
|
return `${String(agentId || '')}\u0000${String(requestId || '')}`;
|
|
50
146
|
}
|
|
@@ -68,10 +164,12 @@ export function registerYeaftDebugRequest({ agentId, requestId, sessionId, clien
|
|
|
68
164
|
pruneYeaftDebugRequests(now);
|
|
69
165
|
const key = yeaftDebugRequestKey(agentId, requestId);
|
|
70
166
|
if (pendingYeaftDebugRequests.has(key)) return false;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (
|
|
167
|
+
let clientPending = 0;
|
|
168
|
+
for (const pending of pendingYeaftDebugRequests.values()) {
|
|
169
|
+
if (pending?.clientId === clientId) clientPending++;
|
|
74
170
|
}
|
|
171
|
+
if (clientPending >= YEAFT_DEBUG_REQUEST_MAX_PENDING_PER_CLIENT
|
|
172
|
+
|| pendingYeaftDebugRequests.size >= YEAFT_DEBUG_REQUEST_MAX_PENDING) return false;
|
|
75
173
|
pendingYeaftDebugRequests.set(key, {
|
|
76
174
|
agentId,
|
|
77
175
|
requestId,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { CONFIG } from '../config.js';
|
|
2
2
|
import { agentInventoryDb, sessionDb, userStatsDb } from '../database.js';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
agents,
|
|
5
|
+
consumeAgentSettingsRequest,
|
|
6
|
+
webClients,
|
|
7
|
+
} from '../context.js';
|
|
4
8
|
import { sendToWebClient, broadcastAgentList } from '../ws-utils.js';
|
|
5
9
|
import {
|
|
6
10
|
handleProxyResponse, handleProxyResponseChunk, handleProxyResponseEnd,
|
|
@@ -12,6 +16,18 @@ const numberMetric = (value) => {
|
|
|
12
16
|
return Number.isFinite(n) && n >= 0 ? n : 0;
|
|
13
17
|
};
|
|
14
18
|
|
|
19
|
+
async function sendAgentSettingsReply(agentId, agent, operation, msg, payload) {
|
|
20
|
+
const pending = consumeAgentSettingsRequest({ agentId, operation, requestId: msg.requestId });
|
|
21
|
+
if (!pending) return false;
|
|
22
|
+
if (!msg.requestId) payload.requestId = pending.requestId;
|
|
23
|
+
const client = webClients.get(pending.clientId);
|
|
24
|
+
if (!client?.authenticated || (!CONFIG.skipAuth
|
|
25
|
+
&& !((agent.ownerId && client.userId === agent.ownerId)
|
|
26
|
+
|| (!agent.ownerId && client.role === 'admin')))) return false;
|
|
27
|
+
await sendToWebClient(client, payload);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
15
31
|
function normalizeAgentMetrics(metrics = {}) {
|
|
16
32
|
const chatTurns = numberMetric(metrics.chatTurns);
|
|
17
33
|
const yeaftTurns = numberMetric(metrics.yeaftTurns);
|
|
@@ -80,16 +96,14 @@ export async function handleAgentSync(agentId, agent, msg) {
|
|
|
80
96
|
agent.dreamEnabled = msg.enabled !== false;
|
|
81
97
|
await broadcastAgentList();
|
|
82
98
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
}
|
|
99
|
+
const payload = {
|
|
100
|
+
type: 'dream_enabled_changed',
|
|
101
|
+
agentId,
|
|
102
|
+
requestId: msg.requestId,
|
|
103
|
+
enabled: msg.error ? agent.dreamEnabled !== false : msg.enabled !== false,
|
|
104
|
+
...(msg.error ? { error: msg.error } : {}),
|
|
105
|
+
};
|
|
106
|
+
await sendAgentSettingsReply(agentId, agent, 'dream', msg, payload);
|
|
93
107
|
break;
|
|
94
108
|
}
|
|
95
109
|
|
|
@@ -202,27 +216,14 @@ export async function handleAgentSync(agentId, agent, msg) {
|
|
|
202
216
|
}
|
|
203
217
|
|
|
204
218
|
case 'restart_agent_ack': {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (client.authenticated && (CONFIG.skipAuth ||
|
|
208
|
-
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
209
|
-
(!agent.ownerId && client.role === 'admin')
|
|
210
|
-
)) {
|
|
211
|
-
await sendToWebClient(client, { type: 'restart_agent_ack', agentId });
|
|
212
|
-
}
|
|
213
|
-
}
|
|
219
|
+
const payload = { type: 'restart_agent_ack', agentId, requestId: msg.requestId };
|
|
220
|
+
await sendAgentSettingsReply(agentId, agent, 'restart', msg, payload);
|
|
214
221
|
break;
|
|
215
222
|
}
|
|
216
223
|
|
|
217
224
|
case 'upgrade_agent_ack': {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
(agent.ownerId && client.userId === agent.ownerId) ||
|
|
221
|
-
(!agent.ownerId && client.role === 'admin')
|
|
222
|
-
)) {
|
|
223
|
-
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, requiredCapability: msg.requiredCapability });
|
|
224
|
-
}
|
|
225
|
-
}
|
|
225
|
+
const payload = { type: 'upgrade_agent_ack', agentId, requestId: msg.requestId, success: msg.success, error: msg.error, alreadyLatest: msg.alreadyLatest, version: msg.version, reason: msg.reason, currentNode: msg.currentNode, requiredNode: msg.requiredNode, requiredCapability: msg.requiredCapability };
|
|
226
|
+
await sendAgentSettingsReply(agentId, agent, 'upgrade', msg, payload);
|
|
226
227
|
break;
|
|
227
228
|
}
|
|
228
229
|
|
|
@@ -408,14 +409,14 @@ export async function handleAgentSync(agentId, agent, msg) {
|
|
|
408
409
|
// trace payloads stay on the Agent.
|
|
409
410
|
case 'telemetry_settings':
|
|
410
411
|
case 'telemetry_settings_updated': {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
412
|
+
const operation = msg.type === 'yeaft_plugins_updated'
|
|
413
|
+
? 'plugins:update'
|
|
414
|
+
: msg.type === 'yeaft_plugins'
|
|
415
|
+
? 'plugins:load'
|
|
416
|
+
: msg.type === 'telemetry_settings_updated'
|
|
417
|
+
? 'telemetry:update'
|
|
418
|
+
: 'telemetry:load';
|
|
419
|
+
await sendAgentSettingsReply(agentId, agent, operation, msg, { ...msg, agentId });
|
|
419
420
|
break;
|
|
420
421
|
}
|
|
421
422
|
|
|
@@ -1339,9 +1339,12 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1339
1339
|
clientId,
|
|
1340
1340
|
userId: client.userId,
|
|
1341
1341
|
});
|
|
1342
|
-
if (!registered)
|
|
1342
|
+
if (!registered) {
|
|
1343
|
+
await sendToWebClient(client, { type: 'yeaft_debug_history', agentId: debugAgentId, sessionId: debugSessionId, requestId: debugRequestId, error: 'Request rejected: too many pending requests or duplicate requestId.' });
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1343
1346
|
try {
|
|
1344
|
-
await forwardToAgent(debugAgentId, {
|
|
1347
|
+
const dispatched = await forwardToAgent(debugAgentId, {
|
|
1345
1348
|
type: 'yeaft_fetch_debug_history',
|
|
1346
1349
|
sessionId: debugSessionId,
|
|
1347
1350
|
requestId: debugRequestId,
|
|
@@ -1353,9 +1356,13 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1353
1356
|
search: typeof msg.search === 'string' ? msg.search.slice(0, 500) : '',
|
|
1354
1357
|
_requestClientId: clientId,
|
|
1355
1358
|
});
|
|
1359
|
+
if (!dispatched) {
|
|
1360
|
+
deleteYeaftDebugRequest({ agentId: debugAgentId, requestId: debugRequestId, clientId });
|
|
1361
|
+
await sendToWebClient(client, { type: 'yeaft_debug_history', agentId: debugAgentId, sessionId: debugSessionId, requestId: debugRequestId, error: 'Agent is unavailable.' });
|
|
1362
|
+
}
|
|
1356
1363
|
} catch (err) {
|
|
1357
1364
|
deleteYeaftDebugRequest({ agentId: debugAgentId, requestId: debugRequestId, clientId });
|
|
1358
|
-
|
|
1365
|
+
await sendToWebClient(client, { type: 'yeaft_debug_history', agentId: debugAgentId, sessionId: debugSessionId, requestId: debugRequestId, error: `Failed to send request to Agent: ${err.message}` });
|
|
1359
1366
|
}
|
|
1360
1367
|
break;
|
|
1361
1368
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
agents,
|
|
3
|
+
deleteAgentSettingsRequest,
|
|
4
|
+
registerAgentSettingsRequest,
|
|
5
|
+
userFileTabs,
|
|
6
|
+
} from '../context.js';
|
|
2
7
|
import {
|
|
3
8
|
sendToWebClient, forwardToAgent, broadcastAgentList
|
|
4
9
|
} from '../ws-utils.js';
|
|
@@ -27,6 +32,26 @@ async function rejectUnsupportedYeaftPlugins(client, msg, agentId) {
|
|
|
27
32
|
});
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
async function forwardRegisteredAgentRequest({ client, clientId, agentId, operation, requestId, message, responseType }) {
|
|
36
|
+
const agent = agents.get(agentId);
|
|
37
|
+
const allowLegacyReply = !Array.isArray(agent?.capabilities)
|
|
38
|
+
|| !agent.capabilities.includes('settings_request_correlation');
|
|
39
|
+
if (!registerAgentSettingsRequest({ agentId, operation, requestId, clientId, allowLegacyReply })) {
|
|
40
|
+
await sendToWebClient(client, { type: responseType, agentId, requestId, error: 'Request rejected: too many pending requests or duplicate requestId.' });
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
if (await forwardToAgent(agentId, message)) return true;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
deleteAgentSettingsRequest({ agentId, requestId, clientId });
|
|
47
|
+
await sendToWebClient(client, { type: responseType, agentId, requestId, error: `Failed to send request to Agent: ${error.message}` });
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
deleteAgentSettingsRequest({ agentId, requestId, clientId });
|
|
51
|
+
await sendToWebClient(client, { type: responseType, agentId, requestId, error: 'Agent is unavailable.' });
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
30
55
|
export function requiresManualUpgradeBridge(capabilities, platform = null) {
|
|
31
56
|
if (Array.isArray(capabilities) && capabilities.includes(SAFE_REMOTE_UPGRADE_CAPABILITY)) return false;
|
|
32
57
|
const normalizedPlatform = typeof platform === 'string' ? platform.trim().toLowerCase() : '';
|
|
@@ -52,7 +77,12 @@ export async function handleClientMisc(clientId, client, msg, checkAgentAccess)
|
|
|
52
77
|
const restartAgentId = msg.agentId;
|
|
53
78
|
if (!restartAgentId) break;
|
|
54
79
|
if (!await checkAgentAccess(restartAgentId)) break;
|
|
55
|
-
|
|
80
|
+
if (msg.requestId) {
|
|
81
|
+
await forwardRegisteredAgentRequest({ client, clientId, agentId: restartAgentId, operation: 'restart', requestId: msg.requestId,
|
|
82
|
+
message: { type: 'restart_agent', requestId: msg.requestId, clientId }, responseType: 'restart_agent_ack' });
|
|
83
|
+
} else {
|
|
84
|
+
await forwardToAgent(restartAgentId, { type: 'restart_agent' });
|
|
85
|
+
}
|
|
56
86
|
break;
|
|
57
87
|
}
|
|
58
88
|
|
|
@@ -60,7 +90,12 @@ export async function handleClientMisc(clientId, client, msg, checkAgentAccess)
|
|
|
60
90
|
const agentId = msg.agentId;
|
|
61
91
|
if (!agentId) break;
|
|
62
92
|
if (!await checkAgentAccess(agentId)) break;
|
|
63
|
-
|
|
93
|
+
if (msg.requestId) {
|
|
94
|
+
await forwardRegisteredAgentRequest({ client, clientId, agentId, operation: 'dream', requestId: msg.requestId,
|
|
95
|
+
message: { type: 'set_dream_enabled', enabled: msg.enabled !== false, requestId: msg.requestId, clientId }, responseType: 'dream_enabled_changed' });
|
|
96
|
+
} else {
|
|
97
|
+
await forwardToAgent(agentId, { type: 'set_dream_enabled', enabled: msg.enabled !== false });
|
|
98
|
+
}
|
|
64
99
|
break;
|
|
65
100
|
}
|
|
66
101
|
|
|
@@ -79,6 +114,7 @@ export async function handleClientMisc(clientId, client, msg, checkAgentAccess)
|
|
|
79
114
|
version: upgradeAgent?.version || null,
|
|
80
115
|
requiredCapability: CONTAINER_AGENT_CAPABILITY,
|
|
81
116
|
error: 'Container Agent is managed as a Docker image. Pull the configured image and recreate the same container through the Server/Sandbox lifecycle while keeping its persistent volumes and host-side agent-secret file.',
|
|
117
|
+
requestId: msg.requestId,
|
|
82
118
|
});
|
|
83
119
|
break;
|
|
84
120
|
}
|
|
@@ -91,10 +127,16 @@ export async function handleClientMisc(clientId, client, msg, checkAgentAccess)
|
|
|
91
127
|
version: upgradeAgent?.version || null,
|
|
92
128
|
requiredCapability: SAFE_REMOTE_UPGRADE_CAPABILITY,
|
|
93
129
|
error: `Agent ${upgradeAgent?.version || 'unknown'} does not advertise the safe remote-upgrade contract. First stop the selected Agent/service on that machine: if it runs under PM2 or another service manager, stop that exact instance there; if it runs in a foreground terminal, terminate that process. Confirm that process has exited, then run "npm install -g @yeaft/webchat-agent@latest --registry=https://pkg.yeaft.com/". Finally, restart the same Agent instance with its original configuration.`,
|
|
130
|
+
requestId: msg.requestId,
|
|
94
131
|
});
|
|
95
132
|
break;
|
|
96
133
|
}
|
|
97
|
-
|
|
134
|
+
if (msg.requestId) {
|
|
135
|
+
await forwardRegisteredAgentRequest({ client, clientId, agentId: upgradeAgentId, operation: 'upgrade', requestId: msg.requestId,
|
|
136
|
+
message: { type: 'upgrade_agent', requestId: msg.requestId, clientId }, responseType: 'upgrade_agent_ack' });
|
|
137
|
+
} else {
|
|
138
|
+
await forwardToAgent(upgradeAgentId, { type: 'upgrade_agent' });
|
|
139
|
+
}
|
|
98
140
|
break;
|
|
99
141
|
}
|
|
100
142
|
|
|
@@ -247,10 +289,8 @@ export async function handleClientMisc(clientId, client, msg, checkAgentAccess)
|
|
|
247
289
|
await rejectUnsupportedYeaftPlugins(client, msg, targetAgentId);
|
|
248
290
|
break;
|
|
249
291
|
}
|
|
250
|
-
await
|
|
251
|
-
type: 'get_yeaft_plugins',
|
|
252
|
-
requestId: msg.requestId || null,
|
|
253
|
-
});
|
|
292
|
+
await forwardRegisteredAgentRequest({ client, clientId, agentId: targetAgentId, operation: 'plugins:load', requestId: msg.requestId,
|
|
293
|
+
message: { type: 'get_yeaft_plugins', requestId: msg.requestId }, responseType: 'yeaft_plugins' });
|
|
254
294
|
break;
|
|
255
295
|
}
|
|
256
296
|
|
|
@@ -264,12 +304,15 @@ export async function handleClientMisc(clientId, client, msg, checkAgentAccess)
|
|
|
264
304
|
}
|
|
265
305
|
const hasPlugins = Object.prototype.hasOwnProperty.call(msg, 'plugins');
|
|
266
306
|
const hasConfig = Object.prototype.hasOwnProperty.call(msg, 'config');
|
|
267
|
-
await
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
307
|
+
await forwardRegisteredAgentRequest({ client, clientId, agentId: targetAgentId, operation: 'plugins:update', requestId: msg.requestId,
|
|
308
|
+
message: {
|
|
309
|
+
type: 'update_yeaft_plugins',
|
|
310
|
+
requestId: msg.requestId,
|
|
311
|
+
// Preserve explicit falsy values so Agent-side schema validation can
|
|
312
|
+
// reject them. Only an absent payload is the legacy empty selection.
|
|
313
|
+
plugins: hasPlugins ? msg.plugins : (hasConfig ? msg.config : {}),
|
|
314
|
+
},
|
|
315
|
+
responseType: 'yeaft_plugins_updated',
|
|
273
316
|
});
|
|
274
317
|
break;
|
|
275
318
|
}
|
|
@@ -291,7 +334,8 @@ export async function handleClientMisc(clientId, client, msg, checkAgentAccess)
|
|
|
291
334
|
const a = msg.agentId || client.currentAgent;
|
|
292
335
|
if (!a) break;
|
|
293
336
|
if (!await checkAgentAccess(a)) break;
|
|
294
|
-
await
|
|
337
|
+
await forwardRegisteredAgentRequest({ client, clientId, agentId: a, operation: 'telemetry:load', requestId: msg.requestId,
|
|
338
|
+
message: { type: 'get_telemetry_settings', requestId: msg.requestId, clientId }, responseType: 'telemetry_settings' });
|
|
295
339
|
break;
|
|
296
340
|
}
|
|
297
341
|
|
|
@@ -299,10 +343,9 @@ export async function handleClientMisc(clientId, client, msg, checkAgentAccess)
|
|
|
299
343
|
const a = msg.agentId || client.currentAgent;
|
|
300
344
|
if (!a) break;
|
|
301
345
|
if (!await checkAgentAccess(a)) break;
|
|
302
|
-
await
|
|
303
|
-
type: 'update_telemetry_settings',
|
|
304
|
-
|
|
305
|
-
});
|
|
346
|
+
await forwardRegisteredAgentRequest({ client, clientId, agentId: a, operation: 'telemetry:update', requestId: msg.requestId,
|
|
347
|
+
message: { type: 'update_telemetry_settings', requestId: msg.requestId, clientId, settings: msg.settings || msg.config || {} },
|
|
348
|
+
responseType: 'telemetry_settings_updated' });
|
|
306
349
|
break;
|
|
307
350
|
}
|
|
308
351
|
|