@yeaft/webchat-agent 1.0.418 → 1.0.421

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.
@@ -12,7 +12,7 @@ import { handleProxyHttpRequest, handleProxyWsOpen, handleProxyWsMessage, handle
12
12
  import {
13
13
  handleReadFile, handleWriteFile, handleListDirectory,
14
14
  handleGitStatus, handleGitDiff, handleGitAdd, handleGitReset, handleGitRestore, handleGitCommit, handleGitPush,
15
- handleFileSearch, handleCreateFile, handleDeleteFiles, handleMoveFiles, handleCopyFiles, handleUploadToDir, handleTransferFiles
15
+ handleFileSearch, handleResolveFileReferences, handleCreateFile, handleDeleteFiles, handleMoveFiles, handleCopyFiles, handleUploadToDir, handleTransferFiles
16
16
  } from '../workbench.js';
17
17
  import { handleListHistorySessions, handleListFolders, handleListModels } from '../history.js';
18
18
  import {
@@ -288,6 +288,10 @@ export async function handleMessage(msg) {
288
288
  await handleFileSearch(msg);
289
289
  break;
290
290
 
291
+ case 'resolve_file_references':
292
+ await handleResolveFileReferences(msg);
293
+ break;
294
+
291
295
  case 'create_file':
292
296
  await handleCreateFile(msg);
293
297
  break;
package/index.js CHANGED
@@ -161,7 +161,7 @@ async function detectCapabilities() {
161
161
  // agent build can speak plaintext WS frames. New servers see this and
162
162
  // flip `agent.encryptOutbound = false`, stopping outbound encryption
163
163
  // to this peer. Old servers ignore the unknown capability token.
164
- const capabilities = ['background_tasks', 'file_editor', 'ping_session', 'plaintext-ok', 'workbench_session_routes', SAFE_REMOTE_UPGRADE_CAPABILITY, 'work_center', 'work_center_message_v2', 'session_history_search', 'session_history_outline', 'session_history_window_prefetch'];
164
+ const capabilities = ['background_tasks', 'file_editor', 'ping_session', 'plaintext-ok', 'workbench_session_routes', SAFE_REMOTE_UPGRADE_CAPABILITY, 'work_center', 'work_center_message_v2', 'session_history_search', 'session_history_outline', 'session_history_window_prefetch', 'file_reference_resolution', 'yeaft_plugins'];
165
165
  if (process.platform === 'linux') capabilities.push('work_item_attachments');
166
166
  const pty = await loadNodePty();
167
167
  if (pty) capabilities.push('terminal');
@@ -169,7 +169,7 @@ export async function handleAgentFileTerminal(agentId, agent, rawMsg) {
169
169
  'terminal_created', 'terminal_output', 'terminal_closed', 'terminal_error',
170
170
  ]);
171
171
  const oneShotTypes = new Set([
172
- 'file_content', 'file_saved', 'directory_listing', 'file_op_result',
172
+ 'file_content', 'file_references_resolved', 'file_saved', 'directory_listing', 'file_op_result',
173
173
  'git_status_result', 'git_diff_result', 'git_op_result', 'file_search_result',
174
174
  ]);
175
175
  if (!terminalTypes.has(msg.type) && !oneShotTypes.has(msg.type)) return false;
@@ -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, {
@@ -40,6 +40,7 @@ async function denyWorkbenchRoute(client, msg) {
40
40
  const RESPONSE_TYPES = Object.freeze({
41
41
  terminal_create: ['terminal_created', 'terminal_error'],
42
42
  read_file: ['file_content'],
43
+ resolve_file_references: ['file_references_resolved'],
43
44
  write_file: ['file_saved'],
44
45
  list_directory: ['directory_listing'],
45
46
  git_status: ['git_status_result'],
@@ -190,6 +191,7 @@ export async function handleClientWorkbench(clientId, client, msg, checkAgentAcc
190
191
  break;
191
192
  }
192
193
 
194
+ case 'resolve_file_references':
193
195
  case 'read_file': {
194
196
  const fileAgentId = msg.agentId || client.currentAgent;
195
197
  if (!fileAgentId) { console.warn('[Server] read_file: no agentId'); return; }
@@ -200,7 +202,7 @@ export async function handleClientWorkbench(clientId, client, msg, checkAgentAcc
200
202
  return;
201
203
  }
202
204
  const fileConvId = resolved.conversationId || msg.conversationId || client.currentConversation || '_explorer';
203
- console.log(`[Server] Forwarding read_file to agent ${fileAgentId}, conv=${fileConvId}, path=${msg.filePath}`);
205
+ console.log(`[Server] Forwarding ${msg.type} to agent ${fileAgentId}, conv=${fileConvId}${msg.filePath ? `, path=${msg.filePath}` : ''}`);
204
206
  await forwardCorrelatedWorkbenchRequest({
205
207
  agentId: fileAgentId,
206
208
  clientId,
@@ -120,7 +120,13 @@ export function handleAgentConnection(ws, url) {
120
120
  return;
121
121
  }
122
122
 
123
- const capabilities = Array.isArray(msg.capabilities) ? msg.capabilities : urlCapabilities;
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,
@@ -228,7 +228,7 @@ export function handleWebConnection(ws, url, req = {}) {
228
228
  const WORKBENCH_TYPES = new Set([
229
229
  ...CLIENT_BROWSER_TYPES,
230
230
  'terminal_create', 'terminal_input', 'terminal_resize', 'terminal_close',
231
- 'read_file', 'write_file', 'create_file', 'delete_files', 'move_files', 'copy_files', 'upload_to_dir', 'file_search',
231
+ 'read_file', 'resolve_file_references', 'write_file', 'create_file', 'delete_files', 'move_files', 'copy_files', 'upload_to_dir', 'file_search',
232
232
  'git_status', 'git_diff', 'git_add', 'git_reset', 'git_restore', 'git_commit', 'git_push',
233
233
  'proxy_update_ports', 'update_file_tabs', 'restore_file_tabs'
234
234
  ]);
@@ -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.418"}
1
+ {"version":"1.0.421"}