@yeaft/webchat-agent 1.0.44 → 1.0.46

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "1.0.44",
3
+ "version": "1.0.46",
4
4
  "description": "Remote worker agent for Yeaft Web Code Agent — connects the native Yeaft engine, CLI providers, and workbench tools",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -12,24 +12,16 @@
12
12
  import { formatSize } from '../archive/tool-results.js';
13
13
 
14
14
  /**
15
- * Collaboration tools are mutually exclusive per Yeaft group shape:
16
- * single-VP groups use sub-agents; multi-VP groups use VP-to-VP forwarding.
17
- * Keep the policy names close to the tool registry so LLM exposure and
18
- * execution gating use the same source of truth.
15
+ * Collaboration tools are gated only where the session shape makes a tool
16
+ * invalid. Single-VP sessions cannot forward to peers because no peers exist.
17
+ * Multi-VP sessions keep the full tool surface, including background tasks and
18
+ * sub-agents, and additionally expose VP-to-VP forwarding.
19
19
  */
20
20
  export const COLLAB_TOOL_POLICY = Object.freeze({
21
21
  SINGLE_VP: 'single-vp',
22
22
  MULTI_VP: 'multi-vp',
23
23
  });
24
24
 
25
- export const SUB_AGENT_TOOL_NAMES = Object.freeze([
26
- 'SpawnAgent',
27
- 'PromptAgent',
28
- 'WaitAgent',
29
- 'CloseAgent',
30
- 'ListAgents',
31
- ]);
32
-
33
25
  export const FORWARD_TOOL_NAMES = Object.freeze(['RouteForward']);
34
26
 
35
27
  /**
@@ -261,8 +253,7 @@ export function normalizeCollabToolPolicy(policy) {
261
253
  export function isToolHiddenByCollabPolicy(toolName, policy) {
262
254
  const normalized = normalizeCollabToolPolicy(policy);
263
255
  if (!normalized) return false;
264
- if (normalized === COLLAB_TOOL_POLICY.SINGLE_VP) return FORWARD_TOOL_NAMES.includes(toolName);
265
- return SUB_AGENT_TOOL_NAMES.includes(toolName);
256
+ return normalized === COLLAB_TOOL_POLICY.SINGLE_VP && FORWARD_TOOL_NAMES.includes(toolName);
266
257
  }
267
258
 
268
259
  export class ToolRegistry {
@@ -350,8 +341,9 @@ export class ToolRegistry {
350
341
 
351
342
  /**
352
343
  * Get tool definitions for the LLM adapter.
353
- * Returns all registered tools unless a collaboration policy hides one of
354
- * the mutually-exclusive orchestration tool families.
344
+ * Returns all registered tools unless the current session shape makes a
345
+ * specific collaboration tool invalid, such as RouteForward in single-VP
346
+ * sessions.
355
347
  * @param {string} [language='en']
356
348
  * @param {{ collabToolPolicy?: string }} [opts]
357
349
  * @returns {{ name: string, description: string, parameters: object }[]}
@@ -164,6 +164,41 @@ async function sendDreamSnapshotForSession(sessionId, extra = {}) {
164
164
  return snapshot;
165
165
  }
166
166
 
167
+ function scheduleYeaftLoadHistoryMetadataReplay(sessionId) {
168
+ const replaySession = session;
169
+ const replayConversationId = yeaftConversationId;
170
+ setTimeout(() => {
171
+ try {
172
+ if (!replaySession) return;
173
+ refreshLiveSessionConfig();
174
+ hydrateYeaftStatusFromSession(replaySession, { reason: 'history_load', emitEvent: true });
175
+ sendSessionEvent({
176
+ type: 'session_ready',
177
+ conversationId: replayConversationId,
178
+ model: replaySession.config.primaryModel || replaySession.config.model,
179
+ modelEffort: replaySession.config.modelEffort || null,
180
+ availableModels: replaySession.config.availableModels || [],
181
+ skills: replaySession.status.skills,
182
+ mcpServers: replaySession.status.mcpServers,
183
+ tools: replaySession.status.tools,
184
+ yeaftDir: ctx.CONFIG?.yeaftDir || null,
185
+ tasks: replaySession.taskManager ? replaySession.taskManager.listActiveTasks() : [],
186
+ });
187
+ sendSessionSnapshotBroadcast();
188
+ if (sessionId && session === replaySession) {
189
+ sendDreamSnapshotForSession(sessionId, { trigger: 'load_history' }).catch(() => null);
190
+ }
191
+ try {
192
+ getVpStatusBroker().broadcastSnapshot();
193
+ } catch (err) {
194
+ console.warn('[Yeaft] vp-status snapshot broadcast (replay) failed:', err?.message || err);
195
+ }
196
+ } catch (err) {
197
+ console.warn('[Yeaft] load-history metadata replay failed:', err?.message || err);
198
+ }
199
+ }, 0);
200
+ }
201
+
167
202
 
168
203
  /**
169
204
  * Single in-flight AbortController for legacy 1:1 chat. A new 1:1 user message
@@ -4819,6 +4854,7 @@ export function handleYeaftModelSwitch(msg) {
4819
4854
  */
4820
4855
  export async function handleYeaftLoadHistory(msg) {
4821
4856
  const sessionId = (msg && typeof msg.sessionId === 'string' && msg.sessionId) || null;
4857
+ const metadataOnly = msg && Number.isFinite(msg.limit) && msg.limit <= 0;
4822
4858
  // `lim` is now expressed in TURNS, not raw messages. `loadRecent` and
4823
4859
  // `loadRecentBySession` use turn-based slicing so the cut never lands
4824
4860
  // mid-tool-arc. Pass `undefined` to use the persistence-layer default
@@ -4828,6 +4864,7 @@ export async function handleYeaftLoadHistory(msg) {
4828
4864
  let historyAlreadyReplayed = false;
4829
4865
 
4830
4866
  const replayHistoryFromStore = () => {
4867
+ if (metadataOnly) return;
4831
4868
  // Delta path: caller knows the latest seq (or message id) it has cached
4832
4869
  // and wants only the messages that arrived after that cursor. Returns
4833
4870
  // mode:'delta' so the frontend can append+dedupe instead of replacing
@@ -4959,7 +4996,7 @@ export async function handleYeaftLoadHistory(msg) {
4959
4996
  afterSeq,
4960
4997
  });
4961
4998
  sendSessionEvent({ type: 'history_loaded', mode: 'delta', count: projectedMessages.length, sessionId, latestSeq: delta.latestSeq, afterSeq });
4962
- } else {
4999
+ } else if (!metadataOnly) {
4963
5000
  emitVisibleHistoryReplay({ store: coldStore, sessionId, limit, mode: 'recent' });
4964
5001
  }
4965
5002
  historyAlreadyReplayed = true;
@@ -4972,9 +5009,6 @@ export async function handleYeaftLoadHistory(msg) {
4972
5009
  });
4973
5010
  installYeaftRuntimeBridge(session);
4974
5011
 
4975
- refreshLiveSessionConfig();
4976
- hydrateYeaftStatusFromSession(session, { reason: 'history_load', emitEvent: true });
4977
-
4978
5012
  // Per-group history hydrates lazily via getOrCreateSessionHistory.
4979
5013
  // When the load-history call carries a sessionId, force-refresh THAT
4980
5014
  // group's tape so the next user message sees on-disk state. When
@@ -4984,8 +5018,6 @@ export async function handleYeaftLoadHistory(msg) {
4984
5018
  } else {
4985
5019
  replayHistoryFromStore();
4986
5020
  historyAlreadyReplayed = true;
4987
- refreshLiveSessionConfig();
4988
- hydrateYeaftStatusFromSession(session, { reason: 'history_load', emitEvent: true });
4989
5021
  }
4990
5022
 
4991
5023
  if (sessionId) {
@@ -4995,31 +5027,11 @@ export async function handleYeaftLoadHistory(msg) {
4995
5027
  setGroupHistory(sessionId, hydrateGroupHistory(sessionId));
4996
5028
  }
4997
5029
 
4998
- // Always replay session_ready so refresh / reconnect rebuilds UI state.
4999
- sendSessionEvent({
5000
- type: 'session_ready',
5001
- conversationId: yeaftConversationId,
5002
- model: session.config.primaryModel || session.config.model,
5003
- modelEffort: session.config.modelEffort || null,
5004
- availableModels: session.config.availableModels || [],
5005
- skills: session.status.skills,
5006
- mcpServers: session.status.mcpServers,
5007
- tools: session.status.tools,
5008
- yeaftDir: ctx.CONFIG?.yeaftDir || null,
5009
- tasks: session.taskManager ? session.taskManager.listActiveTasks() : [],
5010
- });
5011
- sendSessionSnapshotBroadcast();
5012
- if (sessionId) {
5013
- await sendDreamSnapshotForSession(sessionId, { trigger: 'load_history' }).catch(() => null);
5014
- }
5015
- // vp-status: replay the authoritative table on reconnect so a refreshed
5016
- // frontend doesn't have to wait for the next transition to learn each
5017
- // VP's current state.
5018
- try {
5019
- getVpStatusBroker().broadcastSnapshot();
5020
- } catch (err) {
5021
- console.warn('[Yeaft] vp-status snapshot broadcast (replay) failed:', err?.message || err);
5022
- }
5030
+ // Always replay session_ready so refresh / reconnect rebuilds UI state, but
5031
+ // never make the history response wait for bulky metadata snapshots. The
5032
+ // first visible chunk has already been sent above; defer metadata to the next
5033
+ // tick so the browser can paint messages before VP/session/dream snapshots.
5034
+ scheduleYeaftLoadHistoryMetadataReplay(sessionId);
5023
5035
 
5024
5036
  if (historyAlreadyReplayed) return;
5025
5037