@yeaft/webchat-agent 0.1.556 → 0.1.557

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/unify/web-bridge.js +61 -35
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.556",
3
+ "version": "0.1.557",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -963,76 +963,102 @@ export async function handleUnifyGroupChat(msg) {
963
963
  if (!text?.trim()) return;
964
964
  const mentions = Array.isArray(msg.mentions) ? msg.mentions : [];
965
965
 
966
- // Resolve the group meta. If the group is missing / not found, fall back
967
- // to legacy single-agent behavior so the send never silently drops.
968
- let meta = null;
966
+ // Fallback path #1 (PM red-line): no groupId on payload skip group
967
+ // resolution entirely and hand the text to the legacy single-agent
968
+ // dispatcher. Ensures backward-compat with old clients that never learned
969
+ // about groups.
970
+ if (!groupId) {
971
+ await handleUnifyChat({ ...msg, prompt: text });
972
+ return;
973
+ }
974
+
975
+ // Open the group handle (meta + jsonl log). Unresolvable groups take the
976
+ // legacy fallback — never silently drop the send.
977
+ let groupHandle = null;
969
978
  try {
970
979
  const yeaftDir = ctx.CONFIG?.yeaftDir;
971
- if (yeaftDir && groupId) {
980
+ if (yeaftDir) {
972
981
  const { openGroup, loadGroupMeta } = await import('./groups/group-store.js');
973
982
  const { join } = await import('node:path');
974
983
  const { existsSync } = await import('node:fs');
975
984
  const root = join(yeaftDir, 'groups');
976
985
  const dir = join(root, groupId);
977
986
  if (existsSync(dir) && loadGroupMeta(dir)) {
978
- const handle = openGroup(root, groupId);
979
- meta = handle.getMeta();
987
+ groupHandle = openGroup(root, groupId);
980
988
  }
981
989
  }
982
990
  } catch (err) {
983
- console.warn('[Unify] unify_group_chat: group resolve failed', err?.message || err);
991
+ console.warn('[Unify] unify_group_chat: group open failed', err?.message || err);
984
992
  }
985
993
 
986
- // Resolve target vpIds.
987
- const roster = Array.isArray(meta?.roster) ? meta.roster : [];
988
- let targets = [];
989
- if (mentions.length > 0) {
990
- // Intersect mentions with roster. Unknown mentions are ignored (not
991
- // dispatched) — coordinator §6 "not_in_roster" semantics.
992
- targets = mentions.filter((v) => roster.includes(v));
993
- }
994
- if (targets.length === 0 && meta?.defaultVpId && roster.includes(meta.defaultVpId)) {
995
- targets = [meta.defaultVpId];
994
+ if (!groupHandle) {
995
+ await handleUnifyChat({ ...msg, prompt: text });
996
+ return;
996
997
  }
997
998
 
998
- // Fallback: no target resolvable behave as the legacy single-agent path
999
- // so that the user's text still lands on the Engine.
1000
- if (targets.length === 0) {
1001
- await handleUnifyChat({
1002
- ...msg,
1003
- prompt: text,
999
+ // Adapter layer (PM red-line: do NOT modify coordinator to fit this
1000
+ // consumer). We drive `createCoordinator()` with a capturing `deliver`
1001
+ // callback, collect its dispatched/fallback report, then translate each
1002
+ // target into (a) a per-VP `group_message` event for the UI and (b) a
1003
+ // per-VP prompt dispatched through the legacy `handleUnifyChat` path.
1004
+ //
1005
+ // Source of truth for mentions is the PAYLOAD (ChatInput parsed them
1006
+ // once). We pass them through the coordinator's `input.meta` so the
1007
+ // appended log carries the authoritative set — the coordinator will also
1008
+ // run its own parse over `text` for routing, which matches the payload by
1009
+ // construction (ChatInput's `parseMentions` is the same regex).
1010
+ const { createCoordinator } = await import('./groups/coordinator.js');
1011
+ const captured = [];
1012
+ const coord = createCoordinator(groupHandle, {
1013
+ deliver: (vpId, envelope) => { captured.push({ vpId, envelope }); },
1014
+ });
1015
+
1016
+ let report;
1017
+ try {
1018
+ report = coord.ingest({
1019
+ from: 'user',
1020
+ role: 'user',
1021
+ text,
1022
+ meta: { mentions },
1004
1023
  });
1024
+ } catch (err) {
1025
+ console.warn('[Unify] unify_group_chat: coord.ingest failed', err?.message || err);
1026
+ await handleUnifyChat({ ...msg, prompt: text });
1005
1027
  return;
1006
1028
  }
1007
1029
 
1008
- // Tag outbound events with vpId so the UI can attribute them per VP. One
1009
- // `group_message` event per target (mirrors coordinator.deliver() calls).
1010
- for (const vpId of targets) {
1030
+ // Fallback path #2: coordinator resolved no targets and no default VP.
1031
+ // Hand off to the legacy single-agent path so the text still runs.
1032
+ const dispatchedIds = Array.isArray(report?.dispatched) ? report.dispatched : [];
1033
+ if (dispatchedIds.length === 0 && !report?.fallback) {
1034
+ await handleUnifyChat({ ...msg, prompt: text });
1035
+ return;
1036
+ }
1037
+
1038
+ // Per target: emit `group_message` tagged with `speakerVpId` (the VP
1039
+ // being addressed — F3's GroupSelector binding consumes this) + dispatch
1040
+ // through the Engine via handleUnifyChat with an `@vp-<id>` prompt prefix.
1041
+ for (const { vpId, envelope } of captured) {
1011
1042
  try {
1012
1043
  sendUnifyEvent({
1013
1044
  type: 'group_message',
1014
1045
  groupId,
1015
1046
  vpId,
1047
+ speakerVpId: vpId,
1016
1048
  text,
1017
1049
  mentions,
1018
- trigger: mentions.includes(vpId) ? 'mention' : 'fallback',
1050
+ trigger: envelope?.trigger || 'fallback',
1019
1051
  ts: Date.now(),
1020
1052
  });
1021
1053
  } catch { /* never crash WS pipeline */ }
1022
- }
1023
1054
 
1024
- // For each target, dispatch into the engine via handleUnifyChat. We prepend
1025
- // an `@vp-<id>` tag on the prompt so the Dispatcher can route per-VP when
1026
- // VP-bound Engine wiring lands (task-338-F2/F5); today handleUnifyChat
1027
- // still runs a single shared Engine, which preserves legacy behaviour.
1028
- for (const vpId of targets) {
1029
- const scoped = `@vp-${vpId} ${text}`;
1030
1055
  try {
1031
1056
  await handleUnifyChat({
1032
1057
  ...msg,
1033
- prompt: scoped,
1058
+ prompt: `@vp-${vpId} ${text}`,
1034
1059
  groupId,
1035
1060
  vpId,
1061
+ speakerVpId: vpId,
1036
1062
  });
1037
1063
  } catch (err) {
1038
1064
  console.warn('[Unify] unify_group_chat: per-vp dispatch failed', vpId, err?.message || err);