@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.
- package/connection/message-router.js +5 -0
- package/index.js +5 -1
- package/local-runtime/server/handlers/client-person.js +115 -0
- package/local-runtime/server/ws-agent.js +2 -0
- package/local-runtime/server/ws-client.js +3 -0
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +263 -104
- 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 +2 -1
- package/yeaft/person/bridge.js +92 -0
- package/yeaft/person/capabilities.js +57 -0
- package/yeaft/person/contracts.js +117 -0
- package/yeaft/person/index.js +1 -0
- package/yeaft/person/provider.js +80 -0
- package/yeaft/person/repository.js +352 -0
- package/yeaft/person/runtime.js +186 -0
- package/yeaft/person/service.js +101 -0
|
@@ -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.
|
|
1
|
+
{"version":"1.0.584"}
|