@yeaft/webchat-agent 1.0.444 → 1.0.445

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.
@@ -30,7 +30,7 @@ import { getLlmConfig, updateLlmConfig, getYeaftSettings, updateYeaftSettings, g
30
30
  import { loadConfig } from '../yeaft/config.js';
31
31
  import { discoverLlmModels } from '../llm-model-discovery.js';
32
32
  import { fetchModelsDev } from '../yeaft/llm/models-dev.js';
33
- import { handleYeaftSessionSend, handleYeaftAskUserAnswer, handleYeaftSubAgentPrompt, handleYeaftTaskCancel, handleYeaftModeSwitch, handleYeaftModelSwitch, resetYeaftSession, refreshLiveSessionConfig, handleYeaftLoadHistory, handleYeaftLoadHistoryOutline, handleYeaftSearchHistory, handleYeaftLoadHistoryWindow, handleYeaftLoadMoreHistory, handleYeaftAbortThread, handleYeaftAbortAll, handleYeaftAbortTurn, handleYeaftVpSubscribe, handleYeaftVpCreate, handleYeaftVpUpdate, handleYeaftVpDelete, handleYeaftVpRead, handleYeaftListSessions, handleYeaftProjectContextSync, handleYeaftProjectMutation, handleYeaftCreateSession, handleYeaftRenameSession, handleYeaftUpdateSession, handleYeaftUpdateSessionConfig, handleYeaftArchiveSession, handleYeaftDeleteSession, handleYeaftSessionAddMember, handleYeaftSessionRemoveMember, handleYeaftSessionSetDefaultVp, handleYeaftScanWorkdirSessions, handleYeaftRestoreSession, handleYeaftDreamTrigger, handleYeaftFetchToolStats, handleYeaftFetchDebugHistory, handleYeaftMcpList, handleYeaftMcpAdd, handleYeaftMcpRemove, handleYeaftMcpReload, handleYeaftPluginCatalog, broadcastLanguageChange, broadcastYeaftSessionSnapshotEager, broadcastYeaftVpSnapshotEager, preloadYeaftSkillSlashCommands } from '../yeaft/web-bridge.js';
33
+ import { handleYeaftSessionSend, handleYeaftAskUserAnswer, handleYeaftSubAgentPrompt, handleYeaftTaskCancel, handleYeaftModeSwitch, handleYeaftModelSwitch, resetYeaftSession, refreshLiveSessionConfig, handleYeaftLoadHistory, handleYeaftLoadHistoryOutline, handleYeaftSearchHistory, handleYeaftLoadHistoryWindow, handleYeaftLoadMoreHistory, handleYeaftAbortThread, handleYeaftAbortAll, handleYeaftAbortTurn, handleYeaftVpSubscribe, handleYeaftVpCreate, handleYeaftVpUpdate, handleYeaftVpDelete, handleYeaftVpRead, handleYeaftListSessions, handleYeaftProjectContextSync, handleYeaftProjectMutation, handleYeaftCreateSession, handleYeaftRenameSession, handleYeaftUpdateSession, handleYeaftUpdateSessionConfig, handleYeaftArchiveSession, handleYeaftDeleteSession, handleYeaftSessionAddMember, handleYeaftSessionRemoveMember, handleYeaftSessionSetDefaultVp, handleYeaftScanWorkdirSessions, handleYeaftRestoreSession, handleYeaftDreamTrigger, handleYeaftFetchToolStats, handleYeaftFetchDebugHistory, handleYeaftMcpList, handleYeaftMcpAdd, handleYeaftMcpRemove, handleYeaftMcpReload, handleYeaftPluginCatalog, handleYeaftManagedSkill, broadcastLanguageChange, broadcastYeaftSessionSnapshotEager, broadcastYeaftVpSnapshotEager, preloadYeaftSkillSlashCommands } from '../yeaft/web-bridge.js';
34
34
  import { startYeaftStatusRefresh, forceRefreshYeaftStatus } from '../yeaft/status-cache.js';
35
35
  import { handleWorkCenterRequest } from '../yeaft/work-center/bridge.js';
36
36
  import { handleBrowserRuntimeMessage } from '../browser-runtime/messages.js';
@@ -639,6 +639,10 @@ export async function handleMessage(msg) {
639
639
  handleYeaftPluginCatalog(msg);
640
640
  break;
641
641
 
642
+ case 'yeaft_managed_skill':
643
+ handleYeaftManagedSkill(msg);
644
+ break;
645
+
642
646
  case 'yeaft_vp_create':
643
647
  case 'unify_vp_create':
644
648
  handleYeaftVpCreate(msg);
package/index.js CHANGED
@@ -6,8 +6,6 @@ import { homedir } from 'os';
6
6
  import { createAssetOutbox } from './yeaft/asset-outbox.js';
7
7
  import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, chmodSync, readdirSync } from 'fs';
8
8
  import { join, dirname } from 'path';
9
- import { exec } from 'child_process';
10
- import { promisify } from 'util';
11
9
  import { fileURLToPath } from 'url';
12
10
  import ctx from './context.js';
13
11
  import {
@@ -33,14 +31,12 @@ import {
33
31
  runAfterManagedCliRuntimeCleanup,
34
32
  summarizeManagedCliResults,
35
33
  } from './yeaft/managed-cli.js';
36
-
37
- const execAsync = promisify(exec);
38
-
39
- // Startup maintenance is non-interactive. Without windowsHide, each exec()
40
- // creates a visible cmd.exe window on Windows before the Agent connects.
41
- function execHiddenAsync(command, options = {}) {
42
- return execAsync(command, { ...options, windowsHide: true });
43
- }
34
+ import {
35
+ execHiddenFileAsync,
36
+ resolveNpmInvocation,
37
+ shouldRunPeriodicCheck,
38
+ writePeriodicCheckMarker,
39
+ } from './startup-maintenance.js';
44
40
 
45
41
  const __dirname = dirname(fileURLToPath(import.meta.url));
46
42
 
@@ -177,7 +173,7 @@ async function detectCapabilities() {
177
173
  // agent build can speak plaintext WS frames. New servers see this and
178
174
  // flip `agent.encryptOutbound = false`, stopping outbound encryption
179
175
  // to this peer. Old servers ignore the unknown capability token.
180
- const capabilities = ['background_tasks', 'file_editor', 'ping_session', 'plaintext-ok', 'workbench_session_routes', SAFE_REMOTE_UPGRADE_CAPABILITY, 'work_center', 'work_center_message_v2', 'session_history_search', 'session_history_outline', 'session_history_window_prefetch', 'file_reference_resolution', 'yeaft_plugins'];
176
+ const capabilities = ['background_tasks', 'file_editor', 'ping_session', 'plaintext-ok', 'workbench_session_routes', SAFE_REMOTE_UPGRADE_CAPABILITY, 'work_center', 'work_center_message_v2', 'session_history_search', 'session_history_outline', 'session_history_window_prefetch', 'file_reference_resolution', 'yeaft_plugins', 'yeaft_managed_skills'];
181
177
  if (process.platform === 'linux') capabilities.push('work_item_attachments');
182
178
  const pty = await loadNodePty();
183
179
  if (pty) capabilities.push('terminal');
@@ -199,7 +195,8 @@ async function ensureDependencies() {
199
195
  if (!existsSync(nodeModulesPath)) {
200
196
  console.log('[Startup] node_modules not found, running npm install...');
201
197
  try {
202
- await execHiddenAsync('npm install', { cwd: agentDir, timeout: 120000 });
198
+ const npm = resolveNpmInvocation();
199
+ await execHiddenFileAsync(npm.command, [...npm.argsPrefix, 'install'], { cwd: agentDir, timeout: 120000 });
203
200
  console.log('[Startup] npm install completed');
204
201
  } catch (e) {
205
202
  console.warn('[Startup] npm install failed:', e.message);
@@ -222,6 +219,7 @@ async function ensureYeaftSkills() {
222
219
  const installedFile = join(pluginsDir, 'installed_plugins.json');
223
220
  const settingsFile = join(claudeDir, 'settings.json');
224
221
  const cacheDir = join(pluginsDir, 'cache', MARKETPLACE_NAME, PLUGIN_NAME, PLUGIN_VERSION);
222
+ const updateCheckMarker = join(pluginsDir, '.yeaft-skills-update-check.json');
225
223
 
226
224
  try {
227
225
  // --- Layer 1: Clone or update the marketplace repo ---
@@ -230,38 +228,49 @@ async function ensureYeaftSkills() {
230
228
  if (!existsSync(installDir)) {
231
229
  console.log('[Startup] yeaft-skills not found, installing as marketplace plugin...');
232
230
  mkdirSync(marketplacesDir, { recursive: true });
233
- await execHiddenAsync(`git clone ${REPO_URL} "${installDir}"`, { timeout: 60000 });
231
+ await execHiddenFileAsync('git', ['clone', REPO_URL, installDir], { timeout: 60000 });
232
+ writePeriodicCheckMarker(updateCheckMarker);
234
233
  needsCacheUpdate = true;
235
234
  console.log('[Startup] yeaft-skills installed');
236
235
  } else {
237
- console.log('[Startup] yeaft-skills found, checking for updates...');
238
- // Record HEAD before pull to detect changes
239
- let headBefore = '';
240
- try {
241
- const { stdout: h } = await execHiddenAsync('git rev-parse HEAD', { cwd: installDir, timeout: 5000 });
242
- headBefore = h.trim();
243
- } catch { /* ignore */ }
244
- const { stdout } = await execHiddenAsync('git pull --ff-only', {
245
- cwd: installDir,
246
- timeout: 30000
247
- });
248
- if (stdout.includes('Already up to date')) {
249
- console.log('[Startup] yeaft-skills is up to date');
250
- } else {
251
- needsCacheUpdate = true;
252
- console.log('[Startup] yeaft-skills updated');
253
- }
254
- // Double-check: compare HEAD after pull
255
- if (!needsCacheUpdate && headBefore) {
236
+ if (shouldRunPeriodicCheck(updateCheckMarker)) {
237
+ console.log('[Startup] yeaft-skills found, checking for updates...');
238
+ // Record HEAD before pull to detect changes
239
+ let headBefore = '';
256
240
  try {
257
- const { stdout: h2 } = await execHiddenAsync('git rev-parse HEAD', { cwd: installDir, timeout: 5000 });
258
- if (h2.trim() !== headBefore) needsCacheUpdate = true;
241
+ const { stdout: h } = await execHiddenFileAsync('git', ['rev-parse', 'HEAD'], { cwd: installDir, timeout: 5000 });
242
+ headBefore = h.trim();
259
243
  } catch { /* ignore */ }
244
+ try {
245
+ const { stdout } = await execHiddenFileAsync('git', ['pull', '--ff-only'], {
246
+ cwd: installDir,
247
+ timeout: 30000
248
+ });
249
+ if (stdout.includes('Already up to date')) {
250
+ console.log('[Startup] yeaft-skills is up to date');
251
+ } else {
252
+ needsCacheUpdate = true;
253
+ console.log('[Startup] yeaft-skills updated');
254
+ }
255
+ writePeriodicCheckMarker(updateCheckMarker);
256
+ } catch (e) {
257
+ writePeriodicCheckMarker(updateCheckMarker);
258
+ console.warn('[Startup] yeaft-skills update check failed; using cached checkout:', e.message);
259
+ }
260
+ // Double-check: compare HEAD after pull
261
+ if (!needsCacheUpdate && headBefore) {
262
+ try {
263
+ const { stdout: h2 } = await execHiddenFileAsync('git', ['rev-parse', 'HEAD'], { cwd: installDir, timeout: 5000 });
264
+ if (h2.trim() !== headBefore) needsCacheUpdate = true;
265
+ } catch { /* ignore */ }
266
+ }
267
+ } else {
268
+ console.log('[Startup] yeaft-skills update check skipped; recently checked');
260
269
  }
261
270
  }
262
271
  // Get current commit SHA for installed_plugins.json
263
272
  try {
264
- const { stdout: sha } = await execHiddenAsync('git rev-parse HEAD', { cwd: installDir, timeout: 5000 });
273
+ const { stdout: sha } = await execHiddenFileAsync('git', ['rev-parse', 'HEAD'], { cwd: installDir, timeout: 5000 });
265
274
  gitSha = sha.trim();
266
275
  } catch { /* non-critical */ }
267
276
 
@@ -882,6 +882,23 @@ export async function handleAgentOutput(agentId, agent, msg) {
882
882
  break;
883
883
  }
884
884
 
885
+ case 'yeaft_managed_skill_result': {
886
+ const targetClient = msg._requestClientId ? webClients.get(msg._requestClientId) : null;
887
+ if (targetClient?.authenticated && (CONFIG.skipAuth || targetClient.userId === agent.ownerId)) {
888
+ await sendToWebClient(targetClient, {
889
+ type: 'yeaft_managed_skill_result',
890
+ agentId,
891
+ requestId: msg.requestId || null,
892
+ scope: msg.scope || null,
893
+ sessionId: msg.sessionId || null,
894
+ result: msg.result || null,
895
+ catalog: msg.catalog || { tools: [], skills: [], skillSources: [], mcpServers: [] },
896
+ error: msg.error || null,
897
+ });
898
+ }
899
+ break;
900
+ }
901
+
885
902
  case 'yeaft_tool_stats':
886
903
  // 2026-05-13: relay tool-call counters from the agent to the web
887
904
  // client that requested them. Whitelist `snapshot`/`registered`/
@@ -31,6 +31,10 @@ import {
31
31
  agentSupportsYeaftPlugins,
32
32
  YEAFT_PLUGINS_UNSUPPORTED_ERROR,
33
33
  } from '../yeaft-plugin-capability.js';
34
+ import {
35
+ agentSupportsYeaftManagedSkills,
36
+ YEAFT_MANAGED_SKILLS_UNSUPPORTED_ERROR,
37
+ } from '../yeaft-managed-skill-capability.js';
34
38
 
35
39
 
36
40
  function isRetiredCollabSessionId(id) {
@@ -56,6 +60,14 @@ function emptyYeaftPluginCatalog(error = null) {
56
60
  };
57
61
  }
58
62
 
63
+ function emptyYeaftManagedSkillResult(error = null) {
64
+ return {
65
+ type: 'yeaft_managed_skill_result',
66
+ catalog: { tools: [], skills: [], skillSources: [], mcpServers: [] },
67
+ ...(error ? { error } : {}),
68
+ };
69
+ }
70
+
59
71
  async function sendVpSnapshotError(client, msg, error) {
60
72
  await sendToWebClient(client, {
61
73
  type: 'yeaft_output',
@@ -1650,6 +1662,15 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
1650
1662
  });
1651
1663
  return true;
1652
1664
  }
1665
+ if (relayType === 'yeaft_managed_skill'
1666
+ && !agentSupportsYeaftManagedSkills(agents.get(relayAgentId))) {
1667
+ await sendToWebClient(client, {
1668
+ ...emptyYeaftManagedSkillResult(YEAFT_MANAGED_SKILLS_UNSUPPORTED_ERROR),
1669
+ agentId: relayAgentId,
1670
+ requestId: msg.requestId || null,
1671
+ });
1672
+ return true;
1673
+ }
1653
1674
  const relayAgent = agents.get(relayAgentId);
1654
1675
  if (!relayAgent || relayAgent.ws?.readyState !== 1) {
1655
1676
  if (relayType === 'yeaft_fetch_tool_stats') {
@@ -1676,7 +1697,9 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
1676
1697
  // Direct catalog replies are request-scoped. Carry the browser client
1677
1698
  // identity through the Agent so another owner tab cannot accidentally
1678
1699
  // consume a response for this picker request.
1679
- if (relayType === 'yeaft_plugin_catalog') rest._requestClientId = clientId;
1700
+ if (relayType === 'yeaft_plugin_catalog' || relayType === 'yeaft_managed_skill') {
1701
+ rest._requestClientId = clientId;
1702
+ }
1680
1703
  if (rest.type === 'yeaft_session_send' || rest.type === 'yeaft_session_chat') {
1681
1704
  trackUserTurn(client.userId, Buffer.byteLength(JSON.stringify(msg)));
1682
1705
  const sessionId = typeof rest.sessionId === 'string' ? rest.sessionId.trim() : '';
@@ -0,0 +1,11 @@
1
+ import { YEAFT_PLUGINS_CAPABILITY } from './yeaft-plugin-capability.js';
2
+
3
+ export const YEAFT_MANAGED_SKILLS_CAPABILITY = 'yeaft_managed_skills';
4
+ export const YEAFT_MANAGED_SKILLS_UNSUPPORTED_ERROR = 'The selected Agent does not support Skill management; upgrade and restart the Agent';
5
+
6
+ export function agentSupportsYeaftManagedSkills(agent) {
7
+ if (agent?.capabilityMetadataProvided !== true) return false;
8
+ const capabilities = Array.isArray(agent.capabilities) ? agent.capabilities : [];
9
+ return capabilities.includes(YEAFT_PLUGINS_CAPABILITY)
10
+ && capabilities.includes(YEAFT_MANAGED_SKILLS_CAPABILITY);
11
+ }
@@ -1 +1 @@
1
- {"version":"1.0.444"}
1
+ {"version":"1.0.445"}