@yeaft/webchat-agent 1.0.590 → 1.0.591
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/local-runtime/server/handlers/client-conversation.js +76 -11
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +72 -72
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +1 -1
- package/package.json +1 -1
- package/yeaft/attachments.js +190 -46
- package/yeaft/conversation/persist.js +3 -1
- package/yeaft/engine.js +39 -4
- package/yeaft/llm/adapter.js +2 -2
- package/yeaft/llm/openai-responses.js +4 -1
- package/yeaft/tool-folding/index.js +12 -2
- package/yeaft/tools/registry.js +11 -0
- package/yeaft/tools/view-image.js +5 -4
- package/yeaft/web-bridge.js +12 -8
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import {
|
|
20
20
|
sendToWebClient, forwardToAgent,
|
|
21
21
|
broadcastAgentList, broadcastSessionCatalog, buildSessionCatalog, buildHiddenSessionCatalog,
|
|
22
|
-
verifyConversationOwnership, verifyAgentOwnership
|
|
22
|
+
verifyConversationOwnership, verifyAgentOwnership, resolveAgentAccessError
|
|
23
23
|
} from '../ws-utils.js';
|
|
24
24
|
import { routeSessionPin } from './session-pin-router.js';
|
|
25
25
|
import { recordPerfTraceEvent } from '../perf-trace.js';
|
|
@@ -87,6 +87,26 @@ async function sendAskUserAgentUnavailable(client, msg, agentId) {
|
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
async function sendYeaftSessionSendError(client, msg, agentId, code = 'agent_unavailable') {
|
|
91
|
+
// Keep the top-level error contract understood by existing Web clients.
|
|
92
|
+
// Correlation identifies the rejected send, not an Agent acknowledgement.
|
|
93
|
+
await sendToWebClient(client, {
|
|
94
|
+
type: 'error',
|
|
95
|
+
agentId: agentId || null,
|
|
96
|
+
sessionId: msg.sessionId ?? null,
|
|
97
|
+
conversationId: msg.conversationId ?? null,
|
|
98
|
+
requestId: msg.requestId ?? null,
|
|
99
|
+
id: msg.id ?? null,
|
|
100
|
+
clientMessageId: msg.id ?? null,
|
|
101
|
+
code,
|
|
102
|
+
message: code === 'invalid_attachment'
|
|
103
|
+
? 'Session message was not sent: an attachment is missing, expired, invalid, or unavailable. Please upload the files again.'
|
|
104
|
+
: code === 'agent_access_denied'
|
|
105
|
+
? 'Agent access denied; Session message was not sent.'
|
|
106
|
+
: 'No agent available; Session message was not sent. Please retry.',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
90
110
|
async function sendVpSnapshotError(client, msg, error) {
|
|
91
111
|
await sendToWebClient(client, {
|
|
92
112
|
type: 'yeaft_output',
|
|
@@ -1669,6 +1689,7 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1669
1689
|
if (typeof msg.type === 'string' && (msg.type.startsWith('yeaft_') || msg.type.startsWith('unify_'))) {
|
|
1670
1690
|
const relayType = msg.type.startsWith('unify_') ? `yeaft_${msg.type.slice('unify_'.length)}` : msg.type;
|
|
1671
1691
|
const relayAgentId = msg.agentId || client.currentAgent;
|
|
1692
|
+
const isSessionSend = relayType === 'yeaft_session_send' || relayType === 'yeaft_session_chat';
|
|
1672
1693
|
if (msg.perfTraceId) {
|
|
1673
1694
|
recordPerfTraceEvent({
|
|
1674
1695
|
traceId: msg.perfTraceId,
|
|
@@ -1682,7 +1703,9 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1682
1703
|
});
|
|
1683
1704
|
}
|
|
1684
1705
|
if (!relayAgentId) {
|
|
1685
|
-
if (
|
|
1706
|
+
if (isSessionSend) {
|
|
1707
|
+
await sendYeaftSessionSendError(client, msg, relayAgentId);
|
|
1708
|
+
} else if (relayType === 'yeaft_ask_user_answer') {
|
|
1686
1709
|
await sendAskUserAgentUnavailable(client, msg, relayAgentId);
|
|
1687
1710
|
} else if (relayType === 'yeaft_fetch_tool_stats') {
|
|
1688
1711
|
await sendToWebClient(client, emptyYeaftToolStats('No agent selected.'));
|
|
@@ -1698,8 +1721,16 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1698
1721
|
}
|
|
1699
1722
|
return true; // swallow silently for legacy fire-and-forget messages
|
|
1700
1723
|
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1724
|
+
// The shared checker emits an uncorrelated error. Session sends use the
|
|
1725
|
+
// same access policy directly so rejection emits exactly one error.
|
|
1726
|
+
const sessionAccessError = isSessionSend
|
|
1727
|
+
? resolveAgentAccessError(relayAgentId, client.userId, client.role)
|
|
1728
|
+
: null;
|
|
1729
|
+
if (isSessionSend ? sessionAccessError : !await checkAgentAccess(relayAgentId)) {
|
|
1730
|
+
if (isSessionSend) {
|
|
1731
|
+
await sendYeaftSessionSendError(client, msg, relayAgentId,
|
|
1732
|
+
sessionAccessError === 'Agent access denied' ? 'agent_access_denied' : 'agent_unavailable');
|
|
1733
|
+
} else if (relayType === 'yeaft_ask_user_answer') {
|
|
1703
1734
|
await sendAskUserAgentUnavailable(client, msg, relayAgentId);
|
|
1704
1735
|
} else if (relayType === 'yeaft_fetch_tool_stats') {
|
|
1705
1736
|
await sendToWebClient(client, emptyYeaftToolStats('Agent is not available.'));
|
|
@@ -1748,6 +1779,10 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1748
1779
|
}
|
|
1749
1780
|
const relayAgent = agents.get(relayAgentId);
|
|
1750
1781
|
if (!relayAgent || relayAgent.ws?.readyState !== 1) {
|
|
1782
|
+
if (isSessionSend) {
|
|
1783
|
+
await sendYeaftSessionSendError(client, msg, relayAgentId);
|
|
1784
|
+
return true;
|
|
1785
|
+
}
|
|
1751
1786
|
if (relayType === 'yeaft_ask_user_answer') {
|
|
1752
1787
|
await sendAskUserAgentUnavailable(client, msg, relayAgentId);
|
|
1753
1788
|
return true;
|
|
@@ -1810,9 +1845,28 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1810
1845
|
rest.id = `u_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1811
1846
|
}
|
|
1812
1847
|
|
|
1813
|
-
//
|
|
1814
|
-
//
|
|
1815
|
-
|
|
1848
|
+
// Validate the entire Session upload set before resolving or consuming
|
|
1849
|
+
// any item. An invalid reference must never downgrade a send to text-only.
|
|
1850
|
+
if (isSessionSend && rest.attachments !== undefined) {
|
|
1851
|
+
const now = Date.now();
|
|
1852
|
+
const valid = Array.isArray(rest.attachments) && rest.attachments.every(att => {
|
|
1853
|
+
if (typeof att?.fileId !== 'string' || !att.fileId.trim()) return false;
|
|
1854
|
+
const file = pendingFiles.get(att.fileId);
|
|
1855
|
+
return file && Buffer.isBuffer(file.buffer)
|
|
1856
|
+
&& typeof file.mimeType === 'string'
|
|
1857
|
+
&& Number.isFinite(file.uploadedAt)
|
|
1858
|
+
&& now - file.uploadedAt <= CONFIG.fileCleanupInterval
|
|
1859
|
+
&& (CONFIG.skipAuth || (file.userId && file.userId === client.userId));
|
|
1860
|
+
});
|
|
1861
|
+
if (!valid) {
|
|
1862
|
+
await sendYeaftSessionSendError(client, msg, relayAgentId, 'invalid_attachment');
|
|
1863
|
+
return true;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
// The Agent sees resolved bytes, never upload references. Session files
|
|
1868
|
+
// remain retryable until local dispatch succeeds (not an Agent ack).
|
|
1869
|
+
const sessionFileIds = new Set();
|
|
1816
1870
|
if (Array.isArray(rest.attachments) && rest.attachments.length > 0) {
|
|
1817
1871
|
const resolvedFiles = [];
|
|
1818
1872
|
for (const att of rest.attachments) {
|
|
@@ -1825,7 +1879,8 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1825
1879
|
data: file.buffer.toString('base64'),
|
|
1826
1880
|
isImage: !!att.isImage || (file.mimeType || '').startsWith('image/'),
|
|
1827
1881
|
});
|
|
1828
|
-
|
|
1882
|
+
if (isSessionSend) sessionFileIds.add(att.fileId);
|
|
1883
|
+
else pendingFiles.delete(att.fileId);
|
|
1829
1884
|
} else if (file && file.userId !== client.userId) {
|
|
1830
1885
|
console.warn(`[Security] User ${client.userId} attempted to use yeaft file ${att.fileId} owned by ${file.userId}`);
|
|
1831
1886
|
}
|
|
@@ -1833,8 +1888,6 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1833
1888
|
if (resolvedFiles.length > 0) {
|
|
1834
1889
|
rest.files = resolvedFiles;
|
|
1835
1890
|
}
|
|
1836
|
-
// Drop the fileId-bearing array so the agent only sees the
|
|
1837
|
-
// resolved form on `files`.
|
|
1838
1891
|
delete rest.attachments;
|
|
1839
1892
|
}
|
|
1840
1893
|
|
|
@@ -1851,7 +1904,19 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1851
1904
|
bytes: Buffer.byteLength(JSON.stringify(rest)),
|
|
1852
1905
|
});
|
|
1853
1906
|
}
|
|
1854
|
-
|
|
1907
|
+
if (isSessionSend) {
|
|
1908
|
+
let dispatched = false;
|
|
1909
|
+
try {
|
|
1910
|
+
dispatched = await forwardToAgent(relayAgentId, rest);
|
|
1911
|
+
} catch { /* Preserve uploads and report a correlated failure below. */ }
|
|
1912
|
+
if (!dispatched) {
|
|
1913
|
+
await sendYeaftSessionSendError(client, msg, relayAgentId);
|
|
1914
|
+
} else {
|
|
1915
|
+
for (const fileId of sessionFileIds) pendingFiles.delete(fileId);
|
|
1916
|
+
}
|
|
1917
|
+
} else {
|
|
1918
|
+
await forwardToAgent(relayAgentId, rest);
|
|
1919
|
+
}
|
|
1855
1920
|
return true;
|
|
1856
1921
|
}
|
|
1857
1922
|
return false; // Not handled
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.591"}
|