@yeaft/webchat-agent 0.1.436 → 0.1.438

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.
@@ -56,7 +56,9 @@ export function connect() {
56
56
 
57
57
  const msg = await parseMessage(data);
58
58
  if (msg) {
59
- handleMessage(msg);
59
+ handleMessage(msg).catch(err => {
60
+ console.error('[WS] handleMessage error:', err.message || err);
61
+ });
60
62
  }
61
63
  });
62
64
 
@@ -332,5 +332,12 @@ export async function handleMessage(msg) {
332
332
  case 'unify_reset':
333
333
  await resetUnifySession();
334
334
  break;
335
+
336
+ // Expert roles definition (for ExpertPanel detail view)
337
+ case 'get_expert_roles': {
338
+ const { getExpertRolesDefinition } = await import('../expert-roles.js');
339
+ sendToServer({ type: 'expert_roles_list', roles: getExpertRolesDefinition() });
340
+ break;
341
+ }
335
342
  }
336
343
  }
package/conversation.js CHANGED
@@ -719,7 +719,10 @@ export async function handleUserInput(msg) {
719
719
 
720
720
  // ★ Expert Panel: construct expert message if selections provided
721
721
  const expertSelections = msg.expertSelections;
722
- if (expertSelections?.length > 0) {
722
+ if (msg.expertMessage) {
723
+ // Custom expert role: frontend already built the full prompt
724
+ effectivePrompt = msg.expertMessage;
725
+ } else if (expertSelections?.length > 0) {
723
726
  const { buildExpertMessage } = await import('./expert-roles.js');
724
727
  const expertResult = buildExpertMessage(expertSelections, effectivePrompt, msg.language || 'zh-CN');
725
728
  effectivePrompt = expertResult.effectivePrompt;
package/expert-roles.js CHANGED
@@ -877,4 +877,40 @@ export function buildExpertMessage(selections, userText, language = 'zh-CN') {
877
877
  return buildMultiExpertMessage(selections, userText, isZh);
878
878
  }
879
879
 
880
+ /**
881
+ * Return the full EXPERT_ROLES definition (including prompt content)
882
+ * for the web debug/admin panel.
883
+ *
884
+ * Format per role:
885
+ * {
886
+ * name, messagePrefix, messagePrefixEn,
887
+ * actions: { [actionId]: { name, nameEn, messageTemplate, messageTemplateEn, defaultMessage, defaultMessageEn } }
888
+ * }
889
+ *
890
+ * @returns {Object.<string, object>}
891
+ */
892
+ export function getExpertRolesDefinition() {
893
+ const result = {};
894
+ for (const [roleId, roleDef] of Object.entries(EXPERT_ROLES)) {
895
+ const actions = {};
896
+ for (const [actionId, actionDef] of Object.entries(roleDef.actions)) {
897
+ actions[actionId] = {
898
+ name: actionDef.name,
899
+ nameEn: actionDef.nameEn,
900
+ messageTemplate: actionDef.messageTemplate,
901
+ messageTemplateEn: actionDef.messageTemplateEn,
902
+ defaultMessage: actionDef.defaultMessage,
903
+ defaultMessageEn: actionDef.defaultMessageEn,
904
+ };
905
+ }
906
+ result[roleId] = {
907
+ name: roleDef.name,
908
+ messagePrefix: roleDef.messagePrefix,
909
+ messagePrefixEn: roleDef.messagePrefixEn,
910
+ actions,
911
+ };
912
+ }
913
+ return result;
914
+ }
915
+
880
916
  export { EXPERT_ROLES };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.436",
3
+ "version": "0.1.438",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -23,6 +23,9 @@ let session = null;
23
23
  /** @type {AbortController | null} */
24
24
  let currentAbort = null;
25
25
 
26
+ /** Query timeout in ms — abort if LLM doesn't respond within this window */
27
+ const QUERY_TIMEOUT_MS = 120_000;
28
+
26
29
  /** Virtual conversationId for the Unify session */
27
30
  let unifyConversationId = null;
28
31
 
@@ -100,12 +103,29 @@ export async function handleUnifyChat(msg) {
100
103
 
101
104
  currentAbort = new AbortController();
102
105
 
106
+ // ─── Timeout guard: abort query if LLM hangs beyond threshold ──
107
+ // Resets on every event — fires only after prolonged silence.
108
+ let queryTimer = null;
109
+ const resetQueryTimer = () => {
110
+ if (queryTimer) clearTimeout(queryTimer);
111
+ queryTimer = setTimeout(() => {
112
+ if (currentAbort) {
113
+ console.error(`[Unify] query timeout after ${QUERY_TIMEOUT_MS / 1000}s of silence — aborting`);
114
+ currentAbort.abort();
115
+ }
116
+ }, QUERY_TIMEOUT_MS);
117
+ };
118
+ resetQueryTimer();
119
+
120
+ try {
103
121
  // ─── Stream Engine events → claude_output format ──
104
122
  for await (const event of session.engine.query({
105
123
  prompt,
106
124
  mode: currentMode,
107
125
  signal: currentAbort.signal,
108
126
  })) {
127
+ // Reset timeout on every event — activity means the query is alive
128
+ resetQueryTimer();
109
129
  switch (event.type) {
110
130
  // ── Text streaming ──
111
131
  case 'text_delta':
@@ -261,9 +281,29 @@ export async function handleUnifyChat(msg) {
261
281
  result_text: '',
262
282
  });
263
283
 
284
+ } finally {
285
+ // Always clear the timeout guard
286
+ if (queryTimer) clearTimeout(queryTimer);
287
+ }
288
+
264
289
  } catch (err) {
265
- // Don't report abort errors
266
- if (err.name === 'AbortError') return;
290
+ // Don't report abort errors — but still send result to unblock frontend
291
+ if (err.name === 'AbortError') {
292
+ sendUnifyOutput({
293
+ type: 'assistant',
294
+ message: {
295
+ content: [{
296
+ type: 'text',
297
+ text: '⚠️ Query timed out — no response from LLM. Please try again.',
298
+ }],
299
+ },
300
+ });
301
+ sendUnifyOutput({
302
+ type: 'result',
303
+ result_text: '',
304
+ });
305
+ return;
306
+ }
267
307
 
268
308
  console.error('[Unify] query error:', err.message);
269
309
  sendUnifyOutput({