@yeaft/webchat-agent 1.0.299 → 1.0.301

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.299",
3
+ "version": "1.0.301",
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",
@@ -23,7 +23,7 @@ function buildVpPersona(vpId, loaded) {
23
23
  };
24
24
  }
25
25
 
26
- function createEngine(loaded, sessionId, vpId) {
26
+ export function createCliVpEngine(loaded, sessionId, vpId) {
27
27
  const effectiveConfig = resolveSessionConfig(
28
28
  loaded.config,
29
29
  loadSessionConfig(loaded.yeaftDir, sessionId),
@@ -41,6 +41,7 @@ function createEngine(loaded, sessionId, vpId) {
41
41
  yeaftDir: loaded.yeaftDir,
42
42
  toolStats: loaded.toolStats || null,
43
43
  taskManager: loaded.taskManager || null,
44
+ managedCliReady: loaded.managedCliReady || null,
44
45
  sessionId,
45
46
  vpId,
46
47
  });
@@ -58,7 +59,7 @@ export function createCliSessionRunner({
58
59
  loaded,
59
60
  sessionId,
60
61
  workDir = process.cwd(),
61
- engineFactory = createEngine,
62
+ engineFactory = createCliVpEngine,
62
63
  personaFactory = buildVpPersona,
63
64
  } = {}) {
64
65
  if (!loaded || !sessionId) return null;
package/yeaft/cli.js CHANGED
@@ -40,6 +40,7 @@ import { loadSessionConfig, resolveSessionConfig } from './sessions/session-conf
40
40
  import { validateSessionId } from './sessions/ids.js';
41
41
  import { createJsonlWriter, JsonlInput, runStreamTurn, runStreamSessionTurn } from './stdio-protocol.js';
42
42
  import { createCliSessionRunner } from './cli-session-runner.js';
43
+ import { ensureManagedCliTools, summarizeManagedCliResults } from './managed-cli.js';
43
44
 
44
45
  // ─── Argument parsing ──────────────────────────────────────────
45
46
 
@@ -65,6 +66,7 @@ export function parseArgs(argv) {
65
66
  outputFormat: 'text',
66
67
  print: false,
67
68
  prompt: null,
69
+ managedCliReady: Promise.resolve([]),
68
70
  };
69
71
 
70
72
  const rest = argv.slice(2);
@@ -346,6 +348,7 @@ async function runREPL(config, args) {
346
348
  skipMCP: args.skipMCP,
347
349
  skipSkills: args.skipSkills,
348
350
  configOverrides: effectiveConfig,
351
+ managedCliReady: args.managedCliReady,
349
352
  });
350
353
 
351
354
  const { engine, conversationStore, trace, skillManager, mcpManager, toolRegistry } = session;
@@ -797,6 +800,7 @@ async function runStreamJson(config, args) {
797
800
  ...effectiveConfig,
798
801
  ...(effectiveConfig.modelEffort ? { modelEffort: effectiveConfig.modelEffort } : {}),
799
802
  },
803
+ managedCliReady: args.managedCliReady,
800
804
  });
801
805
  const { engine, conversationStore, skillManager, toolRegistry } = loaded;
802
806
  const sessionRunner = createCliSessionRunner({ loaded, sessionId, workDir });
@@ -885,6 +889,7 @@ async function runOnce(config, args) {
885
889
  skipMCP: args.skipMCP,
886
890
  skipSkills: args.skipSkills,
887
891
  configOverrides: effectiveConfig,
892
+ managedCliReady: args.managedCliReady,
888
893
  });
889
894
 
890
895
  const { engine, conversationStore } = session;
@@ -993,7 +998,6 @@ async function main() {
993
998
  language: args.language,
994
999
  debug: args.debug || undefined,
995
1000
  });
996
-
997
1001
  // Handle --trace queries (no LLM needed, no session needed)
998
1002
  if (args.trace) {
999
1003
  await handleTraceQuery(args, config);
@@ -1010,8 +1014,20 @@ async function main() {
1010
1014
  return;
1011
1015
  }
1012
1016
 
1017
+ const prepareManagedCli = () => {
1018
+ args.managedCliReady = ensureManagedCliTools({ yeaftDir: config.dir });
1019
+ args.managedCliReady.then(results => {
1020
+ if (results.some(result => result.status === 'installed')) {
1021
+ console.error(`[Yeaft] managed CLI tools: ${summarizeManagedCliResults(results)}`);
1022
+ }
1023
+ }).catch(error => {
1024
+ console.error(`[Yeaft] managed CLI setup failed; using built-in fallbacks: ${error?.message || error}`);
1025
+ });
1026
+ };
1027
+
1013
1028
  // Handle interactive mode
1014
1029
  if (args.interactive) {
1030
+ prepareManagedCli();
1015
1031
  await runREPL(config, args);
1016
1032
  return;
1017
1033
  }
@@ -1023,6 +1039,7 @@ async function main() {
1023
1039
  if (!args.prompt && args.inputFormat !== 'stream-json' && process.stdin.isTTY) {
1024
1040
  throw new Error('stream-json output requires a prompt, piped stdin, or --input-format stream-json');
1025
1041
  }
1042
+ prepareManagedCli();
1026
1043
  await runStreamJson(config, args);
1027
1044
  return;
1028
1045
  }
@@ -1032,6 +1049,7 @@ async function main() {
1032
1049
 
1033
1050
  // Handle prompt (from args or stdin)
1034
1051
  if (args.prompt) {
1052
+ prepareManagedCli();
1035
1053
  await runOnce(config, args);
1036
1054
  return;
1037
1055
  }
@@ -1044,6 +1062,7 @@ async function main() {
1044
1062
  }
1045
1063
  args.prompt = input.trim();
1046
1064
  if (args.prompt) {
1065
+ prepareManagedCli();
1047
1066
  await runOnce(config, args);
1048
1067
  return;
1049
1068
  }
@@ -152,7 +152,7 @@ function sessionConversationDirs(dir) {
152
152
  * @param {string} dir — Yeaft root directory (e.g. ~/.yeaft)
153
153
  * @param {string} keyword — search terms
154
154
  * @param {number} [limit=10] — max results
155
- * @param {{telemetry?: {scannedFiles?: number, scannedBytes?: number, scannedMessages?: number}}} [options]
155
+ * @param {{telemetry?: {scannedFiles?: number, scannedBytes?: number, scannedMessages?: number}, sessionIds?: string[]}} [options]
156
156
  * @returns {object[]} — matching messages, newest first
157
157
  */
158
158
  export function searchMessages(dir, keyword, limit = 10, options = {}) {
@@ -167,18 +167,24 @@ export function searchMessages(dir, keyword, limit = 10, options = {}) {
167
167
  telemetry.scannedMessages = 0;
168
168
  }
169
169
 
170
- const conversationDirs = [
171
- { dir: join(dir, 'chat'), sessionId: null, kind: 'chat' },
172
- ...sessionConversationDirs(dir),
173
- ];
170
+ const requestedSessionIds = Array.isArray(options.sessionIds)
171
+ ? new Set(options.sessionIds.filter(id => typeof id === 'string' && id.trim()).map(id => id.trim()))
172
+ : null;
173
+ const scopedSessionDirs = sessionConversationDirs(dir)
174
+ .filter(source => !requestedSessionIds || requestedSessionIds.has(source.sessionId));
175
+ const conversationDirs = requestedSessionIds
176
+ ? scopedSessionDirs
177
+ : [{ dir: join(dir, 'chat'), sessionId: null, kind: 'chat' }, ...scopedSessionDirs];
174
178
 
175
179
  const markdownDirs = [
176
180
  ...conversationDirs.flatMap(source => [
177
181
  { ...source, dir: join(source.dir, 'messages') },
178
182
  { ...source, dir: join(source.dir, 'cold') },
179
183
  ]),
180
- { dir: join(dir, 'conversation', 'messages'), sessionId: null, kind: 'legacy-conversation' },
181
- { dir: join(dir, 'conversation', 'cold'), sessionId: null, kind: 'legacy-conversation' },
184
+ ...(!requestedSessionIds ? [
185
+ { dir: join(dir, 'conversation', 'messages'), sessionId: null, kind: 'legacy-conversation' },
186
+ { dir: join(dir, 'conversation', 'cold'), sessionId: null, kind: 'legacy-conversation' },
187
+ ] : []),
182
188
  ];
183
189
  const segmentDirs = conversationDirs.map(source => ({ ...source, dir: join(source.dir, 'segments') }));
184
190
 
package/yeaft/engine.js CHANGED
@@ -524,6 +524,9 @@ export class Engine {
524
524
 
525
525
  /** @type {string|null} */
526
526
  #yeaftDir;
527
+
528
+ /** @type {Promise<Array>|null} */
529
+ #managedCliReady;
527
530
  /** @type {string|null} — set when this engine is bound to a specific group (per-VP fan-out path). */
528
531
  #sessionId = null;
529
532
  /** @type {string|null} — set when this engine is bound to a specific VP (per-VP fan-out path). */
@@ -716,9 +719,10 @@ export class Engine {
716
719
  * mcpManager?: import('./mcp.js').MCPManager,
717
720
  * yeaftDir?: string,
718
721
  * toolStats?: import('./stats/tool-usage.js').ToolUsageStats,
722
+ * managedCliReady?: Promise<Array>,
719
723
  * }} params
720
724
  */
721
- constructor({ adapter, trace, config, conversationStore, memoryIndex, amsRegistry, toolRegistry, skillManager, mcpManager, yeaftDir, toolStats = null, taskManager = null, sessionId = null, vpId = null, chatId = null }) {
725
+ constructor({ adapter, trace, config, conversationStore, memoryIndex, amsRegistry, toolRegistry, skillManager, mcpManager, yeaftDir, toolStats = null, taskManager = null, sessionId = null, vpId = null, chatId = null, managedCliReady = null }) {
722
726
  this.#adapter = adapter;
723
727
  this.#trace = trace;
724
728
  this.#config = config;
@@ -732,6 +736,7 @@ export class Engine {
732
736
  this.#skillManager = skillManager || null;
733
737
  this.#mcpManager = mcpManager || null;
734
738
  this.#yeaftDir = yeaftDir || null;
739
+ this.#managedCliReady = managedCliReady || null;
735
740
  this.#toolStats = toolStats || null;
736
741
  // Per-VP fan-out (2026-06-01): engine instances in the group path are
737
742
  // keyed by ${sessionId}::${vpId}::${threadId}, so binding the engine to
@@ -1260,6 +1265,7 @@ export class Engine {
1260
1265
  return {
1261
1266
  signal,
1262
1267
  yeaftDir: this.#yeaftDir,
1268
+ managedCliReady: this.#managedCliReady,
1263
1269
  runtimePlatform: getRuntimePlatformInfo(),
1264
1270
  // Group-scoped working directory. Threaded from #runQuery({ workDir })
1265
1271
  // → set by web-bridge runVpTurn from sessionMeta.workDir. Tools read
@@ -1279,6 +1285,9 @@ export class Engine {
1279
1285
  config: this.#config,
1280
1286
  taskManager: this.#taskManager,
1281
1287
  sessionId: vpCtx?.sessionId || this.#sessionId || null,
1288
+ projectSessionIds: Array.isArray(vpCtx?.projectSessionIds)
1289
+ ? vpCtx.projectSessionIds.slice()
1290
+ : [],
1282
1291
  threadId: vpCtx?.threadId || this.#currentThreadId || MAIN_THREAD_ID,
1283
1292
  currentVpId: vpCtx?.senderVpId || this.#vpId || null,
1284
1293
  // task-704b: per-tool-result hard cap derives from this. Threaded
@@ -1338,10 +1347,14 @@ export class Engine {
1338
1347
  skillManager: this.#skillManager,
1339
1348
  mcpManager: this.#mcpManager,
1340
1349
  yeaftDir: this.#yeaftDir,
1350
+ managedCliReady: this.#managedCliReady,
1341
1351
  parentName: vpCtx?.senderVpId || 'parent',
1342
1352
  parentVpId: vpCtx?.senderVpId || null,
1343
1353
  parentVpPersona: vpCtx?.vpPersona || null,
1344
1354
  parentSessionId: vpCtx?.sessionId || null,
1355
+ projectSessionIds: Array.isArray(vpCtx?.projectSessionIds)
1356
+ ? vpCtx.projectSessionIds.slice()
1357
+ : [],
1345
1358
  parentThreadId: vpCtx?.threadId || this.#currentThreadId || MAIN_THREAD_ID,
1346
1359
  onEvent: this.#subAgentEventSink || null,
1347
1360
  language: this.#config?.language || 'en',
@@ -1838,7 +1851,7 @@ export class Engine {
1838
1851
  * string-prompt shape (no regression for existing callers).
1839
1852
  * @yields {EngineEvent}
1840
1853
  */
1841
- async *query({ prompt, promptParts = null, messages = [], signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, sessionTopics = null, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, askUser = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, prepareProviderRequest = null, startProviderRequest = null, finishProviderRequest = null, failProviderRequest = null, closePendingUserInput = null, collabToolPolicy = null } = {}) {
1854
+ async *query({ prompt, promptParts = null, messages = [], signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, projectSessionIds = null, sessionTopics = null, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, askUser = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, prepareProviderRequest = null, startProviderRequest = null, finishProviderRequest = null, failProviderRequest = null, closePendingUserInput = null, collabToolPolicy = null } = {}) {
1842
1855
  if (!prompt || typeof prompt !== 'string' || !prompt.trim()) {
1843
1856
  yield {
1844
1857
  type: 'error',
@@ -1915,7 +1928,7 @@ export class Engine {
1915
1928
  };
1916
1929
  try {
1917
1930
  this.#currentThreadId = threadId || MAIN_THREAD_ID;
1918
- yield* this.#runQuery({ prompt: effectivePrompt, promptParts: effectivePromptParts, messages, signal: runSignal, userEffort: effectiveUserEffort, scenario, vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, sessionTopics, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted, getCurrentTodos, setCurrentTodos, askUser, threadId: this.#currentThreadId, vpTurnId, drainPendingUserMessages, prepareProviderRequest, startProviderRequest, finishProviderRequest, failProviderRequest, closePendingUserInput, collabToolPolicy: effectiveCollabToolPolicy, explicitSkillName: parsedSkill.skillName, retryLifecycle });
1931
+ yield* this.#runQuery({ prompt: effectivePrompt, promptParts: effectivePromptParts, messages, signal: runSignal, userEffort: effectiveUserEffort, scenario, vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, projectSessionIds, sessionTopics, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted, getCurrentTodos, setCurrentTodos, askUser, threadId: this.#currentThreadId, vpTurnId, drainPendingUserMessages, prepareProviderRequest, startProviderRequest, finishProviderRequest, failProviderRequest, closePendingUserInput, collabToolPolicy: effectiveCollabToolPolicy, explicitSkillName: parsedSkill.skillName, retryLifecycle });
1919
1932
  } finally {
1920
1933
  // Closing the async generator at a visible retry boundary means the
1921
1934
  // continuation never reached a provider. Keep it out of history and
@@ -1963,7 +1976,7 @@ export class Engine {
1963
1976
  * in a try/finally without indenting the whole loop.
1964
1977
  * @private
1965
1978
  */
1966
- async *#runQuery({ prompt, promptParts = null, messages, signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, sessionTopics = null, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, askUser = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, prepareProviderRequest = null, startProviderRequest = null, finishProviderRequest = null, failProviderRequest = null, closePendingUserInput = null, collabToolPolicy = null, explicitSkillName = null, retryLifecycle }) {
1979
+ async *#runQuery({ prompt, promptParts = null, messages, signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, projectSessionIds = null, sessionTopics = null, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, askUser = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, prepareProviderRequest = null, startProviderRequest = null, finishProviderRequest = null, failProviderRequest = null, closePendingUserInput = null, collabToolPolicy = null, explicitSkillName = null, retryLifecycle }) {
1967
1980
 
1968
1981
  const effectiveCollabToolPolicy = collabToolPolicy === COLLAB_TOOL_POLICY.SINGLE_VP || collabToolPolicy === COLLAB_TOOL_POLICY.MULTI_VP
1969
1982
  ? collabToolPolicy
@@ -2022,12 +2035,22 @@ export class Engine {
2022
2035
  let recallEntryCount = 0;
2023
2036
 
2024
2037
  const topicScopesForMemory = await this.#loadSessionTopicScopes(sessionId);
2038
+ const projectScopesForMemory = Array.isArray(projectSessionIds)
2039
+ ? projectSessionIds.flatMap(id => [
2040
+ `sessions/${id}`,
2041
+ `sessions/${id}/user`,
2042
+ `session/${id}`,
2043
+ `session/${id}/user`,
2044
+ `group/${id}`,
2045
+ `group/${id}/user`,
2046
+ ])
2047
+ : [];
2025
2048
  const recallResult = await this.#recallMemory(prompt, {
2026
2049
  sessionId,
2027
2050
  vpId: vpPersona && typeof vpPersona === 'object' && typeof vpPersona.vpId === 'string'
2028
2051
  ? vpPersona.vpId
2029
2052
  : (typeof senderVpId === 'string' ? senderVpId : undefined),
2030
- extraScopes: topicScopesForMemory,
2053
+ extraScopes: [...topicScopesForMemory, ...projectScopesForMemory],
2031
2054
  });
2032
2055
  recallEntryCount = recallResult && Array.isArray(recallResult.entries)
2033
2056
  ? recallResult.entries.length
@@ -3521,6 +3544,7 @@ export class Engine {
3521
3544
  router,
3522
3545
  senderVpId,
3523
3546
  sessionId: runtimeSessionId,
3547
+ projectSessionIds,
3524
3548
  threadId: runtimeThreadId,
3525
3549
  inboundEnvelope,
3526
3550
  taskId,