@yeaft/webchat-agent 1.0.582 → 1.0.584

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.
@@ -33,6 +33,7 @@ import { fetchModelsDev } from '../yeaft/llm/models-dev.js';
33
33
  import { handleYeaftSessionSend, handleYeaftAskUserAnswer, handleYeaftSubAgentPrompt, handleYeaftTaskCancel, handleYeaftModeSwitch, handleYeaftModelSwitch, resetYeaftSession, refreshLiveSessionConfig, handleYeaftLoadHistory, handleYeaftLoadHistoryOutline, handleYeaftSearchHistory, handleYeaftLoadHistoryWindow, handleYeaftLoadMoreHistory, handleYeaftAbortThread, handleYeaftAbortAll, handleYeaftAbortTurn, handleYeaftVpSubscribe, handleYeaftVpCreate, handleYeaftVpUpdate, handleYeaftVpDelete, handleYeaftVpRead, handleYeaftListSessions, handleYeaftProjectContextSync, handleYeaftProjectMutation, handleYeaftCreateSession, handleYeaftCopySession, handleYeaftRenameSession, handleYeaftUpdateSession, handleYeaftUpdateSessionConfig, handleYeaftArchiveSession, handleYeaftDeleteSession, handleYeaftSessionAddMember, handleYeaftSessionRemoveMember, handleYeaftSessionSetDefaultVp, handleYeaftScanWorkdirSessions, handleYeaftRestoreSession, handleYeaftDreamTrigger, handleYeaftFetchToolStats, handleYeaftFetchDebugHistory, handleYeaftMcpList, handleYeaftMcpAdd, handleYeaftMcpRemove, handleYeaftMcpReload, handleYeaftPluginCatalog, handleYeaftManagedSkill, ensureSessionLoaded, broadcastLanguageChange, broadcastYeaftSessionSnapshotEager, broadcastYeaftVpSnapshotEager, preloadYeaftSkillSlashCommands } from '../yeaft/web-bridge.js';
34
34
  import { startYeaftStatusRefresh, forceRefreshYeaftStatus } from '../yeaft/status-cache.js';
35
35
  import { handleWorkCenterRequest } from '../yeaft/work-center/bridge.js';
36
+ import { handlePersonRequest } from '../yeaft/person/bridge.js';
36
37
  import { handleBrowserRuntimeMessage } from '../browser-runtime/messages.js';
37
38
 
38
39
  export async function applyLlmConfigUpdate(msg, dependencies = {}) {
@@ -873,6 +874,10 @@ export async function handleMessage(msg) {
873
874
  await handleYeaftFetchDebugHistory(msg);
874
875
  break;
875
876
 
877
+ case 'person_request':
878
+ await handlePersonRequest(msg);
879
+ break;
880
+
876
881
  case 'work_center_request':
877
882
  if (ctx.CONFIG?.workCenterEnabled === true) {
878
883
  await handleWorkCenterRequest(msg);
package/index.js CHANGED
@@ -179,7 +179,7 @@ async function detectCapabilities() {
179
179
  // flip `agent.encryptOutbound = false`, stopping outbound encryption
180
180
  // to this peer. Old servers ignore the unknown capability token.
181
181
  const capabilities = ['background_tasks', 'file_editor', 'ping_session', 'plaintext-ok', 'workbench_session_routes', 'workbench_request_correlation', 'workbench_terminal_cleanup_fence', 'workbench_file_content_chunks', 'workbench_video_stream', 'session_history_search', 'session_history_outline', 'session_history_window_prefetch', 'session_fork_from_turn', 'file_reference_resolution', 'response_image_preview', 'yeaft_plugins', 'yeaft_managed_skills', 'settings_request_correlation', 'work_center_feature_settings'];
182
- capabilities.push(getAgentUpgradeCapability());
182
+ capabilities.push('digital_person', getAgentUpgradeCapability());
183
183
  if (ctx.CONFIG?.workCenterEnabled === true) {
184
184
  capabilities.push('work_center', 'work_center_message_v2', 'work_center_workbench');
185
185
  if (supportsWorkItemAttachments()) capabilities.push('work_item_attachments');
@@ -401,6 +401,10 @@ function cleanup() {
401
401
  const { shutdownWorkCenter } = await import('./yeaft/work-center/bridge.js');
402
402
  await shutdownWorkCenter();
403
403
  } catch {}
404
+ try {
405
+ const { shutdownPerson } = await import('./yeaft/person/bridge.js');
406
+ await shutdownPerson();
407
+ } catch {}
404
408
  try { await shutdownBrowserRuntime(); } catch {}
405
409
  if (ctx.ws) ctx.ws.close();
406
410
  });
@@ -0,0 +1,115 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { CONFIG } from '../config.js';
3
+ import { agents } from '../context.js';
4
+ import { forwardToAgent, resolveAgentAccessError, sendToWebClient } from '../ws-utils.js';
5
+
6
+ const FIELDS = Object.freeze({
7
+ status: [], open: [], snapshot: [],
8
+ send: ['text', 'clientMessageId'], think: ['text', 'clientMessageId'],
9
+ dream: ['clientMessageId'], cancel: ['episodeId'],
10
+ messages: ['cursor', 'limit'], traces: ['cursor', 'limit'],
11
+ settings: ['autonomyEnabled'],
12
+ });
13
+
14
+ /** Request-only relay: identity is supplied by the authenticated Server, never the browser.
15
+ * Person data stays on the Agent. No Person response is broadcast to other clients.
16
+ */
17
+ export function createPersonRelay({
18
+ agentMap = agents, send = sendToWebClient, forward = forwardToAgent,
19
+ accessError = resolveAgentAccessError, skipAuth = () => CONFIG.skipAuth,
20
+ timeoutMs = 30_000,
21
+ } = {}) {
22
+ const pending = new Map();
23
+ function forget(id) {
24
+ const row = pending.get(id);
25
+ if (row) clearTimeout(row.timer);
26
+ pending.delete(id);
27
+ return row;
28
+ }
29
+ async function reply(client, envelope, value) {
30
+ await send(client, { type: 'person_response', ...envelope, ...value });
31
+ }
32
+ return {
33
+ async request(client, msg) {
34
+ if (msg?.type !== 'person_request') return false;
35
+ const agentId = typeof msg.agentId === 'string' ? msg.agentId : '';
36
+ const requestId = typeof msg.requestId === 'string' && msg.requestId.length <= 128 ? msg.requestId : null;
37
+ const op = typeof msg.op === 'string' ? msg.op : '';
38
+ const envelope = { agentId, requestId, op };
39
+ const error = !client.authenticated ? 'Authentication required'
40
+ : accessError(agentId, client.userId, client.role);
41
+ if (error) {
42
+ await reply(client, envelope, { ok: false, error });
43
+ return true;
44
+ }
45
+ const ownerId = client.userId || (skipAuth() ? 'local-development' : null);
46
+ if (!ownerId || !requestId || !Object.hasOwn(FIELDS, op)) {
47
+ await reply(client, envelope, { ok: false, error: 'Invalid digital person request' });
48
+ return true;
49
+ }
50
+ const agent = agentMap.get(agentId);
51
+ if (!agent?.capabilities?.includes('digital_person')) {
52
+ await reply(client, envelope, { ok: false, error: 'This Agent does not support digital persons; upgrade it first' });
53
+ return true;
54
+ }
55
+ if (pending.size >= 1024 || [...pending.values()].filter(row => row.client === client).length >= 32) {
56
+ await reply(client, envelope, { ok: false, error: 'Too many pending digital person requests' });
57
+ return true;
58
+ }
59
+ const source = msg.payload;
60
+ if (source != null && (typeof source !== 'object' || Array.isArray(source))) {
61
+ await reply(client, envelope, { ok: false, error: 'Invalid digital person payload' });
62
+ return true;
63
+ }
64
+ const payload = Object.fromEntries(FIELDS[op].filter(key => Object.hasOwn(source || {}, key)).map(key => [key, source[key]]));
65
+ if (JSON.stringify(payload).length > 40_000) {
66
+ await reply(client, envelope, { ok: false, error: 'Digital person input is too large' });
67
+ return true;
68
+ }
69
+ const relayId = randomUUID();
70
+ const row = { client, agent, ownerId, agentId, envelope };
71
+ row.timer = setTimeout(() => {
72
+ if (!forget(relayId)) return;
73
+ void reply(client, envelope, { ok: false, errorCode: 'timeout', error: 'Digital person request timed out; refresh before retrying' }).catch(() => {});
74
+ }, timeoutMs);
75
+ row.timer.unref?.();
76
+ pending.set(relayId, row);
77
+ try {
78
+ const sent = await forward(agentId, {
79
+ type: 'person_request', requestId: relayId, op, payload,
80
+ // Only server-authenticated identity crosses the relay boundary.
81
+ ownerId,
82
+ });
83
+ if (sent === false && forget(relayId)) await reply(client, envelope, { ok: false, errorCode: 'offline', error: 'Agent is offline; refresh before retrying' });
84
+ } catch {
85
+ // A thrown transport error does not prove the request was never delivered.
86
+ if (forget(relayId)) await reply(client, envelope, { ok: false, errorCode: 'outcome_unknown', error: 'Agent delivery could not be confirmed; refresh before retrying' });
87
+ }
88
+ return true;
89
+ },
90
+ async response(agentId, msg) {
91
+ if (msg?.type !== 'person_response') return false;
92
+ const row = pending.get(msg.requestId);
93
+ if (!row || row.agentId !== agentId || row.agent !== agentMap.get(agentId)) return true;
94
+ forget(msg.requestId);
95
+ if (!row.client.authenticated || (row.client.userId || (skipAuth() ? 'local-development' : null)) !== row.ownerId
96
+ || accessError(agentId, row.client.userId, row.client.role)) return true;
97
+ // Whitelist the response. Echoed owner/agent/op/correlation fields are not trusted.
98
+ await reply(row.client, row.envelope, msg.ok === true
99
+ ? { ok: true, data: msg.data }
100
+ : { ok: false,
101
+ errorCode: ['outcome_unknown', 'invalid_request', 'busy', 'unsupported', 'not_configured', 'not_open', 'stale', 'idempotency_conflict'].includes(msg.errorCode) ? msg.errorCode : 'requestFailed',
102
+ error: typeof msg.error === 'string' ? msg.error.slice(0, 500) : 'Digital person request failed' });
103
+ return true;
104
+ },
105
+ clearClient(client) {
106
+ for (const [id, row] of pending) if (row.client === client) forget(id);
107
+ },
108
+ close() { for (const id of pending.keys()) forget(id); },
109
+ };
110
+ }
111
+
112
+ const relay = createPersonRelay();
113
+ export const handleClientPerson = (client, msg) => relay.request(client, msg);
114
+ export const handleAgentPerson = (agentId, msg) => relay.response(agentId, msg);
115
+ export const clearPersonRequestsForClient = client => relay.clearClient(client);
@@ -12,6 +12,7 @@ import {
12
12
  import { handleAgentConversation } from './handlers/agent-conversation.js';
13
13
  import { handleAgentOutput } from './handlers/agent-output.js';
14
14
  import { handleAgentWorkCenter } from './handlers/agent-work-center.js';
15
+ import { handleAgentPerson } from './handlers/client-person.js';
15
16
  import { handleAgentFileTerminal } from './handlers/agent-file-terminal.js';
16
17
  import { handleAgentSync } from './handlers/agent-sync.js';
17
18
  import { recordPerfTraceEvent } from './perf-trace.js';
@@ -474,6 +475,7 @@ async function handleAgentMessage(agentId, msg, ws) {
474
475
  if (await handleAgentConversation(agentId, agent, msg)) return;
475
476
  if (await handleAgentBrowser(agentId, agent, msg)) return;
476
477
  if (await handleAgentWorkCenter(agentId, msg)) return;
478
+ if (await handleAgentPerson(agentId, msg)) return;
477
479
  if (await handleAgentOutput(agentId, agent, msg)) return;
478
480
  if (await handleAgentFileTerminal(agentId, agent, msg)) return;
479
481
  if (await handleAgentSync(agentId, agent, msg)) return;
@@ -33,6 +33,7 @@ import { handleClientWorkbench } from './handlers/client-workbench.js';
33
33
  import { CLIENT_BROWSER_TYPES, handleClientBrowser } from './handlers/client-browser.js';
34
34
  import { handleClientMisc } from './handlers/client-misc.js';
35
35
  import { clearWorkCenterRequestsForClient, handleClientWorkCenter } from './handlers/client-work-center.js';
36
+ import { clearPersonRequestsForClient, handleClientPerson } from './handlers/client-person.js';
36
37
  import { recordPerfTraceEvent } from './perf-trace.js';
37
38
 
38
39
  export function cleanupWorkbenchForDisconnectedClient(clientId) {
@@ -201,6 +202,7 @@ export function handleWebConnection(ws, url, req = {}) {
201
202
  }
202
203
  }
203
204
  clearWorkCenterRequestsForClient(client);
205
+ clearPersonRequestsForClient(client);
204
206
  clearAgentSettingsRequestsForClient(clientId);
205
207
  clearYeaftDebugRequestsForClient(clientId);
206
208
  const browserPeers = clearBrowserRuntimeForClient(client);
@@ -295,5 +297,6 @@ async function handleWebMessage(clientId, msg) {
295
297
  if (await handleClientBrowser(client, msg, checkAgentAccess)) return;
296
298
  if (await handleClientWorkbench(clientId, client, msg, checkAgentAccess)) return;
297
299
  if (await handleClientWorkCenter(client, msg, checkAgentAccess)) return;
300
+ if (await handleClientPerson(client, msg)) return;
298
301
  if (await handleClientMisc(clientId, client, msg, checkAgentAccess)) return;
299
302
  }
@@ -1 +1 @@
1
- {"version":"1.0.582"}
1
+ {"version":"1.0.584"}