@yeaft/webchat-agent 1.0.417 → 1.0.419
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/browser-runtime/browser-install.js +39 -0
- package/browser-runtime/messages.js +52 -2
- package/browser-runtime/service.js +221 -5
- package/index.js +13 -2
- package/local-runtime/server/browser-runtime-routes.js +22 -3
- package/local-runtime/server/client-protocol.js +4 -0
- package/local-runtime/server/handlers/agent-browser.js +88 -3
- package/local-runtime/server/handlers/agent-sync.js +14 -0
- package/local-runtime/server/handlers/client-browser.js +50 -3
- package/local-runtime/server/handlers/client-conversation.js +22 -0
- package/local-runtime/server/handlers/client-misc.js +24 -0
- package/local-runtime/server/ws-agent.js +13 -2
- package/local-runtime/server/ws-client.js +9 -1
- package/local-runtime/server/ws-utils.js +1 -0
- package/local-runtime/server/yeaft-plugin-capability.js +11 -0
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +162 -94
- 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
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
browserRoutes,
|
|
6
6
|
completeBrowserRequest,
|
|
7
7
|
deleteBrowserPeer,
|
|
8
|
+
findBrowserRequest,
|
|
8
9
|
deleteBrowserRoute,
|
|
9
10
|
getBrowserPeer,
|
|
10
11
|
getBrowserRoute,
|
|
@@ -12,6 +13,9 @@ import {
|
|
|
12
13
|
} from '../browser-runtime-routes.js';
|
|
13
14
|
|
|
14
15
|
const AGENT_BROWSER_TYPES = new Set([
|
|
16
|
+
'browser_runtime_status_result',
|
|
17
|
+
'browser_runtime_install_progress',
|
|
18
|
+
'browser_runtime_error',
|
|
15
19
|
'browser_session_created',
|
|
16
20
|
'browser_session_error',
|
|
17
21
|
'browser_session_snapshot',
|
|
@@ -56,6 +60,50 @@ function sessionSnapshot(msg) {
|
|
|
56
60
|
};
|
|
57
61
|
}
|
|
58
62
|
|
|
63
|
+
function safeByteCount(value) {
|
|
64
|
+
const number = Number(value);
|
|
65
|
+
if (!Number.isSafeInteger(number) || number < 0) return 0;
|
|
66
|
+
return Math.min(number, 4 * 1024 * 1024 * 1024);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function publicRuntimeMessage(agentId, requestId, msg) {
|
|
70
|
+
if (msg?.type === 'browser_runtime_error') {
|
|
71
|
+
return {
|
|
72
|
+
type: msg.type,
|
|
73
|
+
agentId,
|
|
74
|
+
requestId,
|
|
75
|
+
code: clean(msg.code, 128) || 'browser_runtime_error',
|
|
76
|
+
safeError: clean(msg.safeError, 500) || null,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (msg?.type === 'browser_runtime_install_progress') {
|
|
80
|
+
return {
|
|
81
|
+
type: msg.type,
|
|
82
|
+
agentId,
|
|
83
|
+
requestId,
|
|
84
|
+
downloadedBytes: safeByteCount(msg.downloadedBytes),
|
|
85
|
+
totalBytes: safeByteCount(msg.totalBytes),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
type: 'browser_runtime_status_result',
|
|
90
|
+
agentId,
|
|
91
|
+
requestId,
|
|
92
|
+
supported: msg?.supported === true,
|
|
93
|
+
state: clean(msg?.state, 32) || 'unknown',
|
|
94
|
+
installed: msg?.installed === true,
|
|
95
|
+
enabled: msg?.enabled === true,
|
|
96
|
+
ready: msg?.ready === true,
|
|
97
|
+
buildId: clean(msg?.buildId, 128) || null,
|
|
98
|
+
platform: clean(msg?.platform, 32) || null,
|
|
99
|
+
downloadBytes: safeByteCount(msg?.downloadBytes),
|
|
100
|
+
downloadedBytes: safeByteCount(msg?.downloadedBytes),
|
|
101
|
+
totalBytes: safeByteCount(msg?.totalBytes),
|
|
102
|
+
probeCode: clean(msg?.probeCode, 128) || null,
|
|
103
|
+
safeError: clean(msg?.safeError, 500) || null,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
59
107
|
function publicSessionMessage(agentId, msg) {
|
|
60
108
|
if (msg?.type === 'browser_session_error') {
|
|
61
109
|
return {
|
|
@@ -84,7 +132,7 @@ function publicSessionMessage(agentId, msg) {
|
|
|
84
132
|
}
|
|
85
133
|
|
|
86
134
|
async function sendCreateResponse(agentId, msg) {
|
|
87
|
-
const request = completeBrowserRequest(agentId, msg);
|
|
135
|
+
const request = completeBrowserRequest(agentId, msg, { kinds: 'browser_session_create' });
|
|
88
136
|
if (!request) return true;
|
|
89
137
|
const client = browserClientForPeer({
|
|
90
138
|
clientId: request.clientId,
|
|
@@ -130,13 +178,47 @@ async function sendCreateResponse(agentId, msg) {
|
|
|
130
178
|
/** Agent-authenticated Browser events routed only through Server-owned ledgers. */
|
|
131
179
|
export async function handleAgentBrowser(agentId, agent, msg) {
|
|
132
180
|
if (!AGENT_BROWSER_TYPES.has(msg?.type)) return false;
|
|
181
|
+
if (msg.type === 'browser_runtime_install_progress') {
|
|
182
|
+
const request = findBrowserRequest(agentId, msg.requestId);
|
|
183
|
+
if (!request || request.state !== 'pending' || request.kind !== 'browser_runtime_install') return true;
|
|
184
|
+
const client = browserClientForPeer({
|
|
185
|
+
clientId: request.clientId,
|
|
186
|
+
webConnectionId: request.webConnectionId,
|
|
187
|
+
webConnectionGeneration: request.webConnectionGeneration,
|
|
188
|
+
});
|
|
189
|
+
if (client?.userId === request.ownerUserId) {
|
|
190
|
+
await sendToWebClient(client, publicRuntimeMessage(agentId, request.requestId, msg));
|
|
191
|
+
}
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
if (msg.type === 'browser_runtime_status_result' || msg.type === 'browser_runtime_error') {
|
|
195
|
+
const pendingRequest = findBrowserRequest(agentId, msg.requestId);
|
|
196
|
+
const request = completeBrowserRequest(agentId, msg, {
|
|
197
|
+
consume: pendingRequest?.kind === 'browser_runtime_status',
|
|
198
|
+
kinds: ['browser_runtime_status', 'browser_runtime_install', 'browser_runtime_enable'],
|
|
199
|
+
});
|
|
200
|
+
if (!request) return true;
|
|
201
|
+
const client = browserClientForPeer({
|
|
202
|
+
clientId: request.clientId,
|
|
203
|
+
webConnectionId: request.webConnectionId,
|
|
204
|
+
webConnectionGeneration: request.webConnectionGeneration,
|
|
205
|
+
});
|
|
206
|
+
if (client?.userId !== request.ownerUserId) return true;
|
|
207
|
+
const response = publicRuntimeMessage(agentId, request.requestId, msg);
|
|
208
|
+
request.response = response;
|
|
209
|
+
await sendToWebClient(client, response);
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
133
212
|
if (msg.type === 'browser_session_created'
|
|
134
213
|
|| (msg.type === 'browser_session_error' && msg.requestId)) {
|
|
135
214
|
return sendCreateResponse(agentId, msg);
|
|
136
215
|
}
|
|
137
216
|
|
|
138
217
|
if (msg.type === 'browser_session_list_result') {
|
|
139
|
-
const request = completeBrowserRequest(agentId, msg, {
|
|
218
|
+
const request = completeBrowserRequest(agentId, msg, {
|
|
219
|
+
consume: true,
|
|
220
|
+
kinds: 'browser_session_list',
|
|
221
|
+
});
|
|
140
222
|
if (!request) return true;
|
|
141
223
|
for (const snapshot of Array.isArray(msg.sessions) ? msg.sessions : []) {
|
|
142
224
|
if (!snapshot?.browserSessionId) continue;
|
|
@@ -154,7 +236,10 @@ export async function handleAgentBrowser(agentId, agent, msg) {
|
|
|
154
236
|
}
|
|
155
237
|
|
|
156
238
|
if (msg.type === 'browser_session_snapshot' && msg.requestId) {
|
|
157
|
-
const request = completeBrowserRequest(agentId, msg, {
|
|
239
|
+
const request = completeBrowserRequest(agentId, msg, {
|
|
240
|
+
consume: true,
|
|
241
|
+
kinds: ['browser_session_get', 'browser_session_close'],
|
|
242
|
+
});
|
|
158
243
|
if (!request) return true;
|
|
159
244
|
const client = browserClientForPeer({
|
|
160
245
|
clientId: request.clientId,
|
|
@@ -74,6 +74,20 @@ export async function handleAgentSync(agentId, agent, msg) {
|
|
|
74
74
|
break;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
case 'agent_capabilities_updated': {
|
|
78
|
+
const capabilities = Array.isArray(msg.capabilities)
|
|
79
|
+
? [...new Set(msg.capabilities.filter(value => (
|
|
80
|
+
typeof value === 'string' && value.length > 0 && value.length <= 128
|
|
81
|
+
)).slice(0, 128))]
|
|
82
|
+
: null;
|
|
83
|
+
if (!capabilities || capabilities.length === 0) break;
|
|
84
|
+
agent.capabilities = capabilities;
|
|
85
|
+
// Transport encryption negotiation is connection-scoped and immutable.
|
|
86
|
+
// A runtime capability refresh must never flip framing mid-connection.
|
|
87
|
+
await broadcastAgentList();
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
|
|
77
91
|
case 'agent_metrics': {
|
|
78
92
|
agent.metrics = normalizeAgentMetrics(msg.metrics || {});
|
|
79
93
|
agent.metricsUpdatedAt = Date.now();
|
|
@@ -15,6 +15,9 @@ import {
|
|
|
15
15
|
} from '../browser-runtime-routes.js';
|
|
16
16
|
|
|
17
17
|
const CLIENT_BROWSER_TYPES = new Set([
|
|
18
|
+
'browser_runtime_status',
|
|
19
|
+
'browser_runtime_install',
|
|
20
|
+
'browser_runtime_enable',
|
|
18
21
|
'browser_session_create',
|
|
19
22
|
'browser_session_get',
|
|
20
23
|
'browser_session_list',
|
|
@@ -34,8 +37,11 @@ function digest(value) {
|
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
async function fail(client, msg, code, safeError = code) {
|
|
40
|
+
const type = String(msg.type || '');
|
|
37
41
|
await sendToWebClient(client, {
|
|
38
|
-
type:
|
|
42
|
+
type: type.startsWith('browser_peer_') ? 'browser_peer_error'
|
|
43
|
+
: type.startsWith('browser_runtime_') ? 'browser_runtime_error'
|
|
44
|
+
: 'browser_session_error',
|
|
39
45
|
requestId: clean(msg.requestId) || null,
|
|
40
46
|
browserSessionId: clean(msg.browserSessionId) || null,
|
|
41
47
|
peerId: clean(msg.peerId) || null,
|
|
@@ -46,6 +52,10 @@ async function fail(client, msg, code, safeError = code) {
|
|
|
46
52
|
return true;
|
|
47
53
|
}
|
|
48
54
|
|
|
55
|
+
function agentSupportsBrowserSetup(agent) {
|
|
56
|
+
return new Set(agent?.capabilities || []).has('browser_runtime_setup');
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
function agentSupportsBrowser(agent) {
|
|
50
60
|
const capabilities = new Set(agent?.capabilities || []);
|
|
51
61
|
return capabilities.has('browser_runtime')
|
|
@@ -98,17 +108,54 @@ function ownerRoute(client, msg) {
|
|
|
98
108
|
export async function handleClientBrowser(client, msg, checkAgentAccess) {
|
|
99
109
|
if (!CLIENT_BROWSER_TYPES.has(msg?.type)) return false;
|
|
100
110
|
if (!CONFIG.browserRuntime.enabled) return fail(client, msg, 'browser_runtime_disabled');
|
|
101
|
-
|
|
111
|
+
const setupRequest = String(msg.type || '').startsWith('browser_runtime_');
|
|
112
|
+
if (setupRequest) {
|
|
113
|
+
if (client.browserRuntimeSetupProtocol !== 1) {
|
|
114
|
+
return fail(client, msg, 'browser_setup_protocol_required');
|
|
115
|
+
}
|
|
116
|
+
} else if (client.browserRuntimeProtocol !== 1) {
|
|
117
|
+
return fail(client, msg, 'browser_protocol_required');
|
|
118
|
+
}
|
|
102
119
|
const agentId = clean(msg.agentId);
|
|
103
120
|
if (!await checkAgentAccess(agentId)) return true;
|
|
104
121
|
const agent = agents.get(agentId);
|
|
105
|
-
if (!
|
|
122
|
+
if (setupRequest ? !agentSupportsBrowserSetup(agent) : !agentSupportsBrowser(agent)) {
|
|
123
|
+
return fail(client, msg, 'browser_runtime_unavailable');
|
|
124
|
+
}
|
|
106
125
|
const requestId = clean(msg.requestId);
|
|
107
126
|
const requestRequired = msg.type !== 'browser_peer_answer'
|
|
108
127
|
&& msg.type !== 'browser_peer_ice_candidate';
|
|
109
128
|
if (requestRequired && !requestId) return fail(client, msg, 'browser_request_id_required');
|
|
110
129
|
const identity = browserServerIdentity(client);
|
|
111
130
|
|
|
131
|
+
if (setupRequest) {
|
|
132
|
+
const canonical = msg.type === 'browser_runtime_install' ? {
|
|
133
|
+
confirmedBuildId: clean(msg.confirmedBuildId, 128),
|
|
134
|
+
confirmedDownloadBytes: Number(msg.confirmedDownloadBytes) || 0,
|
|
135
|
+
} : {};
|
|
136
|
+
const registration = registerBrowserCreateRequest({
|
|
137
|
+
agentId,
|
|
138
|
+
client,
|
|
139
|
+
requestId,
|
|
140
|
+
digest: digest({ type: msg.type, ...canonical }),
|
|
141
|
+
kind: msg.type,
|
|
142
|
+
});
|
|
143
|
+
if (registration.conflict) return fail(client, msg, 'browser_request_conflict');
|
|
144
|
+
if (registration.capacity) return fail(client, msg, 'browser_request_capacity');
|
|
145
|
+
if (registration.duplicate) {
|
|
146
|
+
if (registration.request.response) await sendToWebClient(client, registration.request.response);
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
await sendToAgent(agent, {
|
|
150
|
+
type: msg.type,
|
|
151
|
+
agentId,
|
|
152
|
+
requestId: registration.request.serverRequestId,
|
|
153
|
+
...canonical,
|
|
154
|
+
serverIdentity: identity,
|
|
155
|
+
});
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
112
159
|
if (msg.type === 'browser_session_create') {
|
|
113
160
|
const canonical = canonicalCreate(msg);
|
|
114
161
|
if (!canonical) return fail(client, msg, 'browser_url_invalid');
|
|
@@ -27,6 +27,10 @@ import {
|
|
|
27
27
|
chatCatalogKey,
|
|
28
28
|
yeaftCatalogKey,
|
|
29
29
|
} from '../session-catalog.js';
|
|
30
|
+
import {
|
|
31
|
+
agentSupportsYeaftPlugins,
|
|
32
|
+
YEAFT_PLUGINS_UNSUPPORTED_ERROR,
|
|
33
|
+
} from '../yeaft-plugin-capability.js';
|
|
30
34
|
|
|
31
35
|
|
|
32
36
|
function isRetiredCollabSessionId(id) {
|
|
@@ -44,6 +48,14 @@ function emptyYeaftToolStats(reason = '') {
|
|
|
44
48
|
return payload;
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
function emptyYeaftPluginCatalog(error = null) {
|
|
52
|
+
return {
|
|
53
|
+
type: 'yeaft_plugin_catalog_result',
|
|
54
|
+
catalog: { tools: [], skills: [], mcpServers: [] },
|
|
55
|
+
...(error ? { error } : {}),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
47
59
|
async function sendVpSnapshotError(client, msg, error) {
|
|
48
60
|
await sendToWebClient(client, {
|
|
49
61
|
type: 'yeaft_output',
|
|
@@ -420,6 +432,7 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
420
432
|
agentName: agent.name,
|
|
421
433
|
workDir: agent.workDir,
|
|
422
434
|
capabilities: agent.capabilities || ['terminal', 'file_editor', 'background_tasks'],
|
|
435
|
+
...(agent.capabilityMetadataProvided === true ? { capabilityMetadataProvided: true } : {}),
|
|
423
436
|
version: agent.version || null,
|
|
424
437
|
conversations: filteredConvs,
|
|
425
438
|
slashCommands: agent.slashCommands || [],
|
|
@@ -1628,6 +1641,15 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1628
1641
|
}
|
|
1629
1642
|
return true;
|
|
1630
1643
|
}
|
|
1644
|
+
if (relayType === 'yeaft_plugin_catalog'
|
|
1645
|
+
&& !agentSupportsYeaftPlugins(agents.get(relayAgentId))) {
|
|
1646
|
+
await sendToWebClient(client, {
|
|
1647
|
+
...emptyYeaftPluginCatalog(YEAFT_PLUGINS_UNSUPPORTED_ERROR),
|
|
1648
|
+
agentId: relayAgentId,
|
|
1649
|
+
requestId: msg.requestId || null,
|
|
1650
|
+
});
|
|
1651
|
+
return true;
|
|
1652
|
+
}
|
|
1631
1653
|
const relayAgent = agents.get(relayAgentId);
|
|
1632
1654
|
if (!relayAgent || relayAgent.ws?.readyState !== 1) {
|
|
1633
1655
|
if (relayType === 'yeaft_fetch_tool_stats') {
|
|
@@ -3,11 +3,27 @@ import {
|
|
|
3
3
|
sendToWebClient, forwardToAgent, broadcastAgentList
|
|
4
4
|
} from '../ws-utils.js';
|
|
5
5
|
import { resolveWorkbenchRequest } from '../workbench-route.js';
|
|
6
|
+
import {
|
|
7
|
+
agentSupportsYeaftPlugins,
|
|
8
|
+
YEAFT_PLUGINS_CAPABILITY,
|
|
9
|
+
YEAFT_PLUGINS_UNSUPPORTED_ERROR,
|
|
10
|
+
} from '../yeaft-plugin-capability.js';
|
|
6
11
|
|
|
7
12
|
// Only Agents that explicitly advertise the package-replacement-safe updater
|
|
8
13
|
// may receive remote upgrade commands. Version thresholds are insufficient:
|
|
9
14
|
// builds without this capability may still inherit the installed package cwd.
|
|
10
15
|
export const SAFE_REMOTE_UPGRADE_CAPABILITY = 'remote_upgrade_safe';
|
|
16
|
+
export { YEAFT_PLUGINS_CAPABILITY, YEAFT_PLUGINS_UNSUPPORTED_ERROR };
|
|
17
|
+
|
|
18
|
+
async function rejectUnsupportedYeaftPlugins(client, msg, agentId) {
|
|
19
|
+
await sendToWebClient(client, {
|
|
20
|
+
type: msg.type === 'update_yeaft_plugins' ? 'yeaft_plugins_updated' : 'yeaft_plugins',
|
|
21
|
+
agentId,
|
|
22
|
+
requestId: msg.requestId || null,
|
|
23
|
+
plugins: {},
|
|
24
|
+
error: YEAFT_PLUGINS_UNSUPPORTED_ERROR,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
11
27
|
|
|
12
28
|
export function requiresManualUpgradeBridge(capabilities, platform = null) {
|
|
13
29
|
if (Array.isArray(capabilities) && capabilities.includes(SAFE_REMOTE_UPGRADE_CAPABILITY)) return false;
|
|
@@ -204,6 +220,10 @@ export async function handleClientMisc(clientId, client, msg, checkAgentAccess)
|
|
|
204
220
|
const targetAgentId = msg.agentId || client.currentAgent;
|
|
205
221
|
if (!targetAgentId) break;
|
|
206
222
|
if (!await checkAgentAccess(targetAgentId)) break;
|
|
223
|
+
if (!agentSupportsYeaftPlugins(agents.get(targetAgentId))) {
|
|
224
|
+
await rejectUnsupportedYeaftPlugins(client, msg, targetAgentId);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
207
227
|
await forwardToAgent(targetAgentId, {
|
|
208
228
|
type: 'get_yeaft_plugins',
|
|
209
229
|
requestId: msg.requestId || null,
|
|
@@ -215,6 +235,10 @@ export async function handleClientMisc(clientId, client, msg, checkAgentAccess)
|
|
|
215
235
|
const targetAgentId = msg.agentId || client.currentAgent;
|
|
216
236
|
if (!targetAgentId) break;
|
|
217
237
|
if (!await checkAgentAccess(targetAgentId)) break;
|
|
238
|
+
if (!agentSupportsYeaftPlugins(agents.get(targetAgentId))) {
|
|
239
|
+
await rejectUnsupportedYeaftPlugins(client, msg, targetAgentId);
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
218
242
|
const hasPlugins = Object.prototype.hasOwnProperty.call(msg, 'plugins');
|
|
219
243
|
const hasConfig = Object.prototype.hasOwnProperty.call(msg, 'config');
|
|
220
244
|
await forwardToAgent(targetAgentId, {
|
|
@@ -120,7 +120,13 @@ export function handleAgentConnection(ws, url) {
|
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
const
|
|
123
|
+
const authCapabilitiesProvided = Array.isArray(msg.capabilities);
|
|
124
|
+
// Modern Agents always include this query key, including for an
|
|
125
|
+
// explicit empty list. The key's presence is the metadata signal;
|
|
126
|
+
// its value only determines the effective capability list.
|
|
127
|
+
const urlCapabilitiesProvided = url.searchParams.has('capabilities');
|
|
128
|
+
const capabilityMetadataProvided = authCapabilitiesProvided || urlCapabilitiesProvided;
|
|
129
|
+
const capabilities = authCapabilitiesProvided ? msg.capabilities : urlCapabilities;
|
|
124
130
|
const agentVersion = msg.version || null;
|
|
125
131
|
const agentPlatform = typeof msg.platform === 'string' && msg.platform
|
|
126
132
|
? msg.platform
|
|
@@ -152,6 +158,7 @@ export function handleAgentConnection(ws, url) {
|
|
|
152
158
|
pending.workDir,
|
|
153
159
|
authResult.sessionKey,
|
|
154
160
|
capabilities,
|
|
161
|
+
capabilityMetadataProvided,
|
|
155
162
|
ownerId,
|
|
156
163
|
ownerUsername,
|
|
157
164
|
agentVersion,
|
|
@@ -243,7 +250,7 @@ function handleAgentDisconnect(agentId, agentName, ws) {
|
|
|
243
250
|
broadcastAgentList();
|
|
244
251
|
}
|
|
245
252
|
|
|
246
|
-
function completeAgentRegistration(ws, agentId, agentName, workDir, sessionKey, capabilities = [], ownerId = null, ownerUsername = null, agentVersion = null, instanceId = null, agentPlatform = null) {
|
|
253
|
+
function completeAgentRegistration(ws, agentId, agentName, workDir, sessionKey, capabilities = [], capabilityMetadataProvided = false, ownerId = null, ownerUsername = null, agentVersion = null, instanceId = null, agentPlatform = null) {
|
|
247
254
|
// 如果是重连,保留 conversations;否则(server 重启)创建空 Map
|
|
248
255
|
const existingAgent = agents.get(agentId);
|
|
249
256
|
const conversations = existingAgent?.conversations || new Map();
|
|
@@ -274,6 +281,9 @@ function completeAgentRegistration(ws, agentId, agentName, workDir, sessionKey,
|
|
|
274
281
|
isAlive: true,
|
|
275
282
|
lastSeenAt: Date.now(),
|
|
276
283
|
capabilities: effectiveCapabilities,
|
|
284
|
+
// The fallback list supports old UI features but cannot prove that an
|
|
285
|
+
// Agent made an explicit capability claim.
|
|
286
|
+
capabilityMetadataProvided,
|
|
277
287
|
proxyPorts,
|
|
278
288
|
slashCommands,
|
|
279
289
|
slashCommandDescriptions,
|
|
@@ -352,6 +362,7 @@ async function handleAgentMessage(agentId, msg, ws) {
|
|
|
352
362
|
'file_content', 'file_saved', 'file_op_result', 'file_search_result',
|
|
353
363
|
'git_status_result', 'git_diff_result', 'git_op_result',
|
|
354
364
|
'terminal_created', 'terminal_output', 'terminal_closed', 'terminal_error',
|
|
365
|
+
'agent_capabilities_updated', 'browser_runtime_status_result', 'browser_runtime_install_progress', 'browser_runtime_error',
|
|
355
366
|
'browser_session_created', 'browser_session_error', 'browser_session_snapshot', 'browser_session_list_result',
|
|
356
367
|
'browser_peer_prepared', 'browser_peer_offer', 'browser_peer_ice_candidate', 'browser_peer_state', 'browser_peer_error'
|
|
357
368
|
]);
|
|
@@ -6,7 +6,12 @@ import { authenticateRequest } from './auth/request-auth.js';
|
|
|
6
6
|
import { encodeKey } from './encryption.js';
|
|
7
7
|
import { userDb } from './database.js';
|
|
8
8
|
import { agents, clearYeaftDebugRequestsForClient, webClients, isHeartbeatMessageType, trackRequest } from './context.js';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
applyClientHello,
|
|
11
|
+
BROWSER_RUNTIME_PROTOCOL,
|
|
12
|
+
BROWSER_RUNTIME_SETUP_PROTOCOL,
|
|
13
|
+
WORKBENCH_ROUTE_PROTOCOL,
|
|
14
|
+
} from './client-protocol.js';
|
|
10
15
|
import { clearWorkbenchCorrelationsForClient } from './workbench-correlation.js';
|
|
11
16
|
import { clearBrowserRuntimeForClient } from './browser-runtime-routes.js';
|
|
12
17
|
import {
|
|
@@ -88,6 +93,7 @@ export function handleWebConnection(ws, url, req = {}) {
|
|
|
88
93
|
// Explicit protocols have no omission-based downgrade for security fields.
|
|
89
94
|
workbenchRouteProtocol: 0,
|
|
90
95
|
browserRuntimeProtocol: 0,
|
|
96
|
+
browserRuntimeSetupProtocol: 0,
|
|
91
97
|
});
|
|
92
98
|
|
|
93
99
|
// 心跳响应处理
|
|
@@ -116,6 +122,7 @@ export function handleWebConnection(ws, url, req = {}) {
|
|
|
116
122
|
yeaftSessionInventoryComplete: true,
|
|
117
123
|
workbenchRouteProtocol: WORKBENCH_ROUTE_PROTOCOL,
|
|
118
124
|
browserRuntimeProtocol: BROWSER_RUNTIME_PROTOCOL,
|
|
125
|
+
browserRuntimeSetupProtocol: BROWSER_RUNTIME_SETUP_PROTOCOL,
|
|
119
126
|
browserRuntimeEnabled: CONFIG.browserRuntime.enabled,
|
|
120
127
|
}));
|
|
121
128
|
setTimeout(() => broadcastAgentList(), 100);
|
|
@@ -249,6 +256,7 @@ async function handleWebMessage(clientId, msg) {
|
|
|
249
256
|
type: 'client_hello_ack',
|
|
250
257
|
workbenchRouteProtocol: client.workbenchRouteProtocol,
|
|
251
258
|
browserRuntimeProtocol: client.browserRuntimeProtocol,
|
|
259
|
+
browserRuntimeSetupProtocol: client.browserRuntimeSetupProtocol,
|
|
252
260
|
browserRuntimeEnabled: CONFIG.browserRuntime.enabled,
|
|
253
261
|
});
|
|
254
262
|
return;
|
|
@@ -169,6 +169,7 @@ export async function broadcastAgentList() {
|
|
|
169
169
|
status: agent.status || 'ready',
|
|
170
170
|
latency: agent.latency || null,
|
|
171
171
|
capabilities: agent.capabilities || ['terminal', 'file_editor', 'background_tasks'],
|
|
172
|
+
...(agent.capabilityMetadataProvided === true ? { capabilityMetadataProvided: true } : {}),
|
|
172
173
|
version: agent.version || null,
|
|
173
174
|
yeaftStatus: agent.yeaftStatus || null,
|
|
174
175
|
proxyPorts: agent.proxyPorts || [],
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Plugin protocol support is omission-compatible: old Agents that never
|
|
2
|
+
// advertised capability metadata keep the legacy relay path. Once an Agent
|
|
3
|
+
// explicitly provides metadata, omitting this token is an explicit refusal.
|
|
4
|
+
export const YEAFT_PLUGINS_CAPABILITY = 'yeaft_plugins';
|
|
5
|
+
export const YEAFT_PLUGINS_UNSUPPORTED_ERROR = 'The selected Agent does not support Plugins; upgrade and restart the Agent';
|
|
6
|
+
|
|
7
|
+
export function agentSupportsYeaftPlugins(agent) {
|
|
8
|
+
if (agent?.capabilityMetadataProvided !== true) return true;
|
|
9
|
+
return Array.isArray(agent.capabilities)
|
|
10
|
+
&& agent.capabilities.includes(YEAFT_PLUGINS_CAPABILITY);
|
|
11
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.419"}
|