@gt-fe/eap-runtime 0.0.39 → 0.0.40

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.
@@ -128,8 +128,8 @@ export declare class GraphLoader {
128
128
  resolveAgentDisplayName(agentCode: string, version?: string): Promise<string | undefined>;
129
129
  private fromAgentManifest;
130
130
  /**
131
- * 解析 agent tools[] + graph tool 节点 union;
132
- * server+预装:以预装 manifest 为准;desktop 合并 ILocalToolInstallStore。
131
+ * 装工具只认登记的工具集(snapshot.tools / 清单 toolsets、toolRefs、tools)和内联 manifest。
132
+ * 图上 config.tool、节点 id 不参与。server 预装优先;dev 再读 ./tools 下与登记 code 同名的目录;desktop 合并本机安装。
133
133
  */
134
134
  loadToolDefinitions(agentCode: string, options?: LoadToolDefinitionsOptions): Promise<LoadedToolDefinition[]>;
135
135
  private loadToolDefinitionsFromSource;
@@ -5,11 +5,10 @@
5
5
  * 开发态:Registry + 可选 ./tools 本地 manifest。
6
6
  */
7
7
  import { DEFAULT_GRAPH_TIMEOUT_MS } from '../common/types.js';
8
- import { readdir, readFile } from 'node:fs/promises';
8
+ import { readFile } from 'node:fs/promises';
9
9
  import path from 'node:path';
10
10
  import { resolveAgentModelAllowlist, resolveAgentDefaultModel } from './agent-models.js';
11
11
  import { DEFAULT_HTTP_TOOL_TIMEOUT_SEC, DEFAULT_TOOL_TIMEOUT_SEC } from '../tool/timeout.js';
12
- import { isMcpDiscoverStub } from '../tool/mcp-list.js';
13
12
  import { createDeploymentSnapshotFromEnv, isInlineToolDeclaration, overlayToolRefPolicies, } from './deployment-snapshot.js';
14
13
  import { preloadGraphNotFound, preloadManifestNotFound, preloadSkillNotFound, preloadToolNotFound, } from './preload-errors.js';
15
14
  import { PreinstallDefinitionCache, resolveDefinitionCacheEnabled, } from './definition-cache.js';
@@ -195,14 +194,39 @@ function yamlGraphToDsl(agentCode, graph) {
195
194
  config: configFromGraphDocument(graph),
196
195
  };
197
196
  }
198
- function collectToolNames(agent, graphDsl) {
199
- const rawTools = agent?.manifest?.tools ?? [];
200
- // 支持 string[] (ID 引用) 和 object[] (完整 ToolManifest 内联声明)
201
- const fromAgent = rawTools.map((t) => typeof t === 'string' ? t : t?.name ?? t?.id);
202
- const fromGraph = graphDsl.nodes
203
- .filter((n) => n.type === 'tool')
204
- .map((n) => n.config?.['tool'] ?? n.id);
205
- return [...new Set([...fromAgent, ...fromGraph].filter(Boolean))];
197
+ /** Registry `manifest.tools` 里的字符串 code。内联对象已在前面转成定义,这里不重复取。 */
198
+ function manifestStringToolIds(raw) {
199
+ if (!Array.isArray(raw))
200
+ return [];
201
+ const out = [];
202
+ for (const item of raw) {
203
+ if (typeof item !== 'string')
204
+ continue;
205
+ const name = item.trim();
206
+ if (name)
207
+ out.push(name);
208
+ }
209
+ return out;
210
+ }
211
+ /**
212
+ * 还要向 Registry / 开发态目录补的登记项。
213
+ * 图上 `config.tool` 与节点 id 不是登记项。
214
+ */
215
+ function pendingToolRefs(snapToolRefs, loadedRefNames, loadedNames, registryTools) {
216
+ const out = [];
217
+ const seen = new Set();
218
+ const push = (ref) => {
219
+ if (!ref.name || seen.has(ref.name) || loadedRefNames.has(ref.name) || loadedNames.has(ref.name)) {
220
+ return;
221
+ }
222
+ seen.add(ref.name);
223
+ out.push(ref);
224
+ };
225
+ for (const ref of snapToolRefs)
226
+ push(ref);
227
+ for (const id of manifestStringToolIds(registryTools))
228
+ push({ name: id });
229
+ return out;
206
230
  }
207
231
  function normalizeDataScope(raw) {
208
232
  if (raw === 'user-bound' || raw === 'department-bound' || raw === 'global')
@@ -437,27 +461,6 @@ function expandPreinstallToolset(pre, options) {
437
461
  }
438
462
  return out;
439
463
  }
440
- function fromLocalManifestFile(toolName, manifest) {
441
- const permissions = manifest.permissions;
442
- const metadata = manifest.metadata;
443
- const manifestRef = {
444
- id: String(manifest.id ?? toolName),
445
- name: String(manifest.name ?? toolName),
446
- version: String(manifest.version ?? '0.0.0'),
447
- type: manifest.type ?? 'code',
448
- status: 'active',
449
- toolRuntime: (manifest.toolRuntime ?? manifest.runtime),
450
- dataScope: normalizeDataScope(permissions?.dataScope),
451
- requireConfirmation: Boolean(metadata?.require_confirmation),
452
- execution: manifest.execution,
453
- };
454
- return {
455
- toolId: String(manifest.id ?? toolName),
456
- name: String(manifest.name ?? toolName),
457
- manifest: manifestRef,
458
- inputSchema: manifest.input,
459
- };
460
- }
461
464
  /**
462
465
  * 从内联 Tool 声明(manifest.tools 中的完整 ToolManifest 对象)
463
466
  * 转为 LoadedToolDefinition。
@@ -505,52 +508,24 @@ async function readJsonObject(filePath) {
505
508
  return null;
506
509
  }
507
510
  }
508
- function loadedToolFromDevDir(dir, toolName, doc, toolsetOnly) {
509
- const tools = doc.tools;
510
- if (Array.isArray(tools) && tools.length > 0 && typeof tools[0] === 'object') {
511
+ /**
512
+ * 开发态只读 `./tools/<code>`,展开整个工具集。不按子工具名扫相邻目录。
513
+ * 找到清单时返回展开结果,白名单把子工具都排除时为空数组。没有清单时返回 null。
514
+ */
515
+ async function loadDevLocalToolset(toolsetCode, toolsDir, options) {
516
+ const dir = path.join(toolsDir, toolsetCode);
517
+ for (const file of DEV_LOCAL_MANIFEST_FILES) {
518
+ const doc = await readJsonObject(path.join(dir, file));
519
+ if (!doc)
520
+ continue;
511
521
  const pre = {
512
- toolId: String(doc.code ?? path.basename(dir)),
513
- name: String(doc.name ?? path.basename(dir)),
522
+ toolId: String(doc.id ?? doc.code ?? toolsetCode),
523
+ name: String(doc.code ?? doc.name ?? toolsetCode),
514
524
  version: String(doc.version ?? '0.0.0'),
515
525
  manifest: doc,
516
526
  bundlePath: dir,
517
527
  };
518
- const expanded = expandPreinstallToolset(pre);
519
- return expanded.find((d) => d.name === toolName) ?? null;
520
- }
521
- if (toolsetOnly)
522
- return null;
523
- return fromLocalManifestFile(toolName, doc);
524
- }
525
- async function loadDevLocalManifestFromDir(dir, toolName, toolsetOnly) {
526
- const files = toolsetOnly ? ['toolset.manifest.json'] : DEV_LOCAL_MANIFEST_FILES;
527
- for (const file of files) {
528
- const doc = await readJsonObject(path.join(dir, file));
529
- if (!doc)
530
- continue;
531
- const hit = loadedToolFromDevDir(dir, toolName, doc, toolsetOnly);
532
- if (hit)
533
- return hit;
534
- }
535
- return null;
536
- }
537
- async function loadDevLocalManifest(toolName, toolsDir) {
538
- const direct = await loadDevLocalManifestFromDir(path.join(toolsDir, toolName), toolName, false);
539
- if (direct)
540
- return direct;
541
- let entries;
542
- try {
543
- entries = await readdir(toolsDir, { withFileTypes: true });
544
- }
545
- catch {
546
- return null;
547
- }
548
- for (const ent of entries) {
549
- if (!ent.isDirectory() || ent.name === toolName)
550
- continue;
551
- const hit = await loadDevLocalManifestFromDir(path.join(toolsDir, ent.name), toolName, true);
552
- if (hit)
553
- return hit;
528
+ return expandPreinstallToolset(pre, options);
554
529
  }
555
530
  return null;
556
531
  }
@@ -816,8 +791,8 @@ export class GraphLoader {
816
791
  return extract(record?.manifest);
817
792
  }
818
793
  /**
819
- * 解析 agent tools[] + graph tool 节点 union;
820
- * server+预装:以预装 manifest 为准;desktop 合并 ILocalToolInstallStore。
794
+ * 装工具只认登记的工具集(snapshot.tools / 清单 toolsets、toolRefs、tools)和内联 manifest。
795
+ * 图上 config.tool、节点 id 不参与。server 预装优先;dev 再读 ./tools 下与登记 code 同名的目录;desktop 合并本机安装。
821
796
  */
822
797
  async loadToolDefinitions(agentCode, options = {}) {
823
798
  const requested = this.resolveVersion(options);
@@ -836,7 +811,8 @@ export class GraphLoader {
836
811
  }
837
812
  async loadToolDefinitionsFromSource(agentCode, options, version) {
838
813
  const activeOnly = options.activeOnly !== false;
839
- const graphDsl = await this.loadGraph(agentCode, {
814
+ // 缺图时与原先一样失败。图上的 tool 名不加入下面的登记列表。
815
+ await this.loadGraph(agentCode, {
840
816
  agentVersion: version,
841
817
  bypassCache: options.bypassCache,
842
818
  });
@@ -889,27 +865,16 @@ export class GraphLoader {
889
865
  }, null, 2));
890
866
  }
891
867
  const snapToolRefs = overlayToolRefPolicies(snap?.tools ?? [], snap?.manifest);
892
- const toolNamesFromSnap = snapToolRefs.map((t) => t.name).filter(Boolean);
893
- const collectedFromGraph = collectToolNames(agent, graphDsl);
894
- const toolNames = [...new Set([...collectedFromGraph, ...toolNamesFromSnap])];
895
- // [DEBUG] 最终 toolNames 决策
896
- if (process.env.EAP_LLM_DEBUG === '1') {
897
- console.log('[LLM-DEBUG] toolNames:', JSON.stringify({
898
- fromGraph: collectedFromGraph,
899
- fromSnap: toolNamesFromSnap,
900
- final: toolNames,
901
- }));
902
- }
903
- if (this.deploymentSnapshot && (snapToolRefs.length > 0 || toolNames.length > 0)) {
868
+ const loadedRefNames = new Set();
869
+ if (this.deploymentSnapshot && snapToolRefs.length > 0) {
904
870
  const missing = [];
905
- const processedToolsetNames = new Set(snapToolRefs.map((r) => r.name));
906
- // 1) snapshot 引用的工具集全部加载并展开(enabledTools 白名单过滤)
907
871
  for (const ref of snapToolRefs) {
908
872
  const pre = await this.deploymentSnapshot.loadToolManifest(ref, agentDir);
909
873
  if (!pre) {
910
874
  missing.push(ref.name);
911
875
  continue;
912
876
  }
877
+ loadedRefNames.add(ref.name);
913
878
  for (const def of expandPreinstallToolset(pre, {
914
879
  enabledTools: ref.enabledTools,
915
880
  autoApprove: ref.autoApprove,
@@ -917,34 +882,37 @@ export class GraphLoader {
917
882
  tryAddLoadedTool(definitions, byName, def);
918
883
  }
919
884
  }
920
- // 2) Graph 声明但尚未命中的工具名:按名直查。
921
- // 已在步骤 1 处理过的工具集名不再二次展开(避免丢掉 enabledTools 白名单)。
922
- for (const toolName of toolNames) {
923
- if (byName.has(toolName))
924
- continue;
925
- if (processedToolsetNames.has(toolName))
926
- continue;
927
- const pre = await this.deploymentSnapshot.loadToolManifest({ name: toolName }, agentDir);
928
- if (!pre) {
929
- // MCP 子工具名在 tools/list 后才出现,图内 type:tool 节点不必对应预装目录
930
- if (definitions.some(isMcpDiscoverStub))
931
- continue;
932
- missing.push(toolName);
885
+ if (this.strictPreinstall && missing.length > 0) {
886
+ throw preloadToolNotFound(`预装快照缺失 Tool: ${missing.join(', ')}(agent=${agentCode})`, { missing, agentCode });
887
+ }
888
+ }
889
+ const pendingRefs = pendingToolRefs(snapToolRefs, loadedRefNames, new Set(byName.keys()), agent?.manifest?.tools);
890
+ if (process.env.EAP_LLM_DEBUG === '1') {
891
+ console.log('[LLM-DEBUG] toolRefs:', JSON.stringify({
892
+ fromSnap: snapToolRefs.map((t) => t.name),
893
+ pending: pendingRefs.map((t) => t.name),
894
+ }));
895
+ }
896
+ if (this.deploymentContext === 'dev') {
897
+ const toolsDir = this.localToolsDir ?? process.env.EAP_LOCAL_TOOLS_DIR ?? './tools';
898
+ for (const ref of pendingRefs) {
899
+ const localDefs = await loadDevLocalToolset(ref.name, toolsDir, {
900
+ enabledTools: ref.enabledTools,
901
+ autoApprove: ref.autoApprove,
902
+ });
903
+ if (!localDefs)
933
904
  continue;
934
- }
935
- for (const def of expandPreinstallToolset(pre)) {
905
+ loadedRefNames.add(ref.name);
906
+ for (const def of localDefs) {
936
907
  tryAddLoadedTool(definitions, byName, def);
937
908
  }
938
909
  }
939
- if (this.strictPreinstall && missing.length > 0) {
940
- throw preloadToolNotFound(`预装快照缺失 Tool: ${missing.join(', ')}(agent=${agentCode})`, { missing, agentCode });
941
- }
942
910
  }
943
911
  if (this.registryClient && !this.blockRegistryDefinitions) {
944
- for (const toolName of toolNames) {
945
- if (byName.has(toolName))
912
+ for (const ref of pendingRefs) {
913
+ if (loadedRefNames.has(ref.name) || byName.has(ref.name))
946
914
  continue;
947
- const record = await this.registryClient.getToolByName(toolName);
915
+ const record = await this.registryClient.getToolByName(ref.name);
948
916
  if (!record)
949
917
  continue;
950
918
  if (activeOnly && record.status !== 'active')
@@ -952,17 +920,6 @@ export class GraphLoader {
952
920
  tryAddLoadedTool(definitions, byName, toLoadedToolDefinition(record));
953
921
  }
954
922
  }
955
- if (this.deploymentContext === 'dev') {
956
- const toolsDir = this.localToolsDir ?? process.env.EAP_LOCAL_TOOLS_DIR ?? './tools';
957
- for (const toolName of toolNames) {
958
- if (byName.has(toolName))
959
- continue;
960
- const localDef = await loadDevLocalManifest(toolName, toolsDir);
961
- if (localDef) {
962
- tryAddLoadedTool(definitions, byName, localDef);
963
- }
964
- }
965
- }
966
923
  if (this.deploymentContext === 'desktop' && this.localInstallStore) {
967
924
  const localRecords = this.localInstallStore.listReady();
968
925
  for (const install of localRecords) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gt-fe/eap-runtime",
3
- "version": "0.0.39",
3
+ "version": "0.0.40",
4
4
  "description": "Enterprise Agent Platform — Runtime核心:Graph执行引擎、Task生命周期、Session管理、Checkpoint持久化",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",