@gt-fe/eap-runtime 0.0.12 → 0.0.14

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 (112) hide show
  1. package/README.md +3 -1
  2. package/dist/api/handlers/invoke.handler.d.ts +1 -1
  3. package/dist/api/handlers/invoke.handler.js +3 -0
  4. package/dist/api/handlers/stream.handler.d.ts +1 -1
  5. package/dist/api/handlers/stream.handler.js +4 -1
  6. package/dist/api/routes/agent.routes.d.ts +2 -2
  7. package/dist/common/index.d.ts +1 -0
  8. package/dist/common/index.js +1 -0
  9. package/dist/common/logger.d.ts +16 -4
  10. package/dist/common/logger.js +82 -16
  11. package/dist/common/runtime-package-version.d.ts +2 -0
  12. package/dist/common/runtime-package-version.js +5 -0
  13. package/dist/common/types.d.ts +5 -5
  14. package/dist/common/types.js +2 -2
  15. package/dist/contracts/permission-checker.d.ts +1 -1
  16. package/dist/debug/llm-turn-log.d.ts +12 -0
  17. package/dist/debug/llm-turn-log.js +1 -0
  18. package/dist/governance/governance-client.d.ts +1 -1
  19. package/dist/governance/index.d.ts +1 -1
  20. package/dist/governance/permission-guard.d.ts +1 -1
  21. package/dist/graph/adapters/langgraph.adapter.d.ts +3 -1
  22. package/dist/graph/adapters/langgraph.adapter.js +67 -14
  23. package/dist/graph/adapters/subgraph-compiler.js +5 -2
  24. package/dist/graph/contracts/executable-graph.d.ts +19 -8
  25. package/dist/graph/contracts/executable-graph.js +1 -1
  26. package/dist/graph/nodes/llm-node.d.ts +3 -0
  27. package/dist/graph/nodes/llm-node.js +8 -0
  28. package/dist/graph/nodes/skill-bind-node.d.ts +3 -4
  29. package/dist/graph/nodes/skill-bind-node.js +1 -20
  30. package/dist/graph/nodes/tool-node.d.ts +2 -0
  31. package/dist/graph/nodes/tool-node.js +20 -14
  32. package/dist/index.d.ts +1 -1
  33. package/dist/memory/compression/index.d.ts +1 -1
  34. package/dist/memory/compression/index.js +1 -1
  35. package/dist/memory/compression/sliding-window.compression.js +7 -7
  36. package/dist/memory/index.d.ts +1 -1
  37. package/dist/memory/index.js +1 -1
  38. package/dist/memory/message-compression.service.d.ts +12 -1
  39. package/dist/memory/message-compression.service.js +29 -1
  40. package/dist/providers/http-llm-provider.d.ts +1 -0
  41. package/dist/providers/http-llm-provider.js +4 -1
  42. package/dist/providers/index.d.ts +1 -1
  43. package/dist/providers/index.js +1 -1
  44. package/dist/providers/local-llm-provider.d.ts +4 -1
  45. package/dist/providers/local-llm-provider.js +15 -3
  46. package/dist/providers/routing-llm-provider.js +13 -4
  47. package/dist/registry/agent-models.d.ts +1 -1
  48. package/dist/registry/agent-models.js +1 -1
  49. package/dist/registry/agent-registry-client.d.ts +1 -1
  50. package/dist/registry/deployment-snapshot.js +19 -17
  51. package/dist/registry/graph-loader.d.ts +11 -1
  52. package/dist/registry/graph-loader.js +115 -56
  53. package/dist/registry/preload-errors.d.ts +1 -1
  54. package/dist/registry/preload-errors.js +1 -1
  55. package/dist/registry/skill-io-guard.js +1 -1
  56. package/dist/registry/skill-tree.js +1 -1
  57. package/dist/runtime/agent-runtime.d.ts +3 -3
  58. package/dist/runtime/agent-runtime.js +14 -4
  59. package/dist/runtime/dev-runtime.d.ts +2 -0
  60. package/dist/runtime/dev-runtime.js +2 -0
  61. package/dist/runtime/types.d.ts +4 -4
  62. package/dist/server.js +1 -1
  63. package/dist/session/compression-middleware.d.ts +2 -1
  64. package/dist/session/compression-middleware.js +7 -4
  65. package/dist/sse/events/skill-events.d.ts +1 -1
  66. package/dist/sse/events/skill-events.js +1 -1
  67. package/dist/sse/events/stats-events.d.ts +9 -0
  68. package/dist/token/compression-budget-integration.d.ts +1 -1
  69. package/dist/token/compression-budget-integration.js +18 -10
  70. package/dist/token/context-window.d.ts +42 -0
  71. package/dist/token/context-window.js +73 -0
  72. package/dist/token/index.d.ts +1 -0
  73. package/dist/token/index.js +1 -0
  74. package/dist/tool/create-tool-executor.d.ts +1 -1
  75. package/dist/tool/dev-http-executor.d.ts +2 -1
  76. package/dist/tool/dev-http-executor.js +14 -2
  77. package/dist/tool/dev-mcp-executor.d.ts +11 -1
  78. package/dist/tool/dev-mcp-executor.js +27 -1
  79. package/dist/tool/dev-tool-executor.d.ts +2 -1
  80. package/dist/tool/dev-tool-executor.js +6 -0
  81. package/dist/tool/harness-command.js +1 -1
  82. package/dist/tool/harness-primitives.d.ts +7 -0
  83. package/dist/tool/harness-primitives.js +57 -6
  84. package/dist/tool/http-tool-executor.js +1 -1
  85. package/dist/tool/index.d.ts +2 -1
  86. package/dist/tool/index.js +1 -0
  87. package/dist/tool/local-mcp-client.d.ts +5 -2
  88. package/dist/tool/local-mcp-client.js +130 -3
  89. package/dist/tool/local-tool-executor.d.ts +3 -2
  90. package/dist/tool/local-tool-executor.js +16 -1
  91. package/dist/tool/mcp-list.d.ts +53 -0
  92. package/dist/tool/mcp-list.js +199 -0
  93. package/dist/tool/mcp-tool-executor.d.ts +6 -1
  94. package/dist/tool/mcp-tool-executor.js +53 -1
  95. package/dist/tool/skill-dir-infer.d.ts +17 -0
  96. package/dist/tool/skill-dir-infer.js +81 -0
  97. package/dist/tool/skill-script-refs.d.ts +1 -8
  98. package/dist/tool/skill-script-refs.js +32 -20
  99. package/dist/tool/system-prompt-builder.d.ts +35 -16
  100. package/dist/tool/system-prompt-builder.js +30 -58
  101. package/dist/tool/timeout.d.ts +2 -2
  102. package/dist/tool/timeout.js +6 -3
  103. package/dist/tool/tool-execution-router.d.ts +3 -2
  104. package/dist/tool/tool-execution-router.js +8 -1
  105. package/dist/tool/tool-executor.interface.d.ts +11 -0
  106. package/dist/tool/tool-orchestrator.d.ts +1 -1
  107. package/dist/tool/tool-orchestrator.js +1 -1
  108. package/dist/utils/format-error.d.ts +2 -2
  109. package/dist/utils/format-error.js +2 -2
  110. package/dist/utils/http-error.d.ts +2 -2
  111. package/dist/utils/http-error.js +2 -2
  112. package/package.json +6 -33
@@ -1,4 +1,4 @@
1
1
  export { LongTermMemoryService, InMemoryMemoryRepository, type IMemoryRepository, type MemoryRecord, type MemoryType, type CompressionResult, } from './long-term-memory.service.js';
2
2
  export { PostgresMemoryRepository } from './pg-memory.repository.js';
3
- export { SlidingWindowCompression, TruncateCompression, type IMessageCompressionService, type CompressionConfig, type SummarizeFunction, DEFAULT_COMPRESSION_CONFIG, estimateMessagesTokens, countRounds, } from './compression/index.js';
3
+ export { SlidingWindowCompression, TruncateCompression, type IMessageCompressionService, type CompressionConfig, type SummarizeFunction, DEFAULT_COMPRESSION_CONFIG, COMPRESSION_WINDOW_FACTOR, resolveCompressionMaxTokens, estimateMessagesTokens, countRounds, } from './compression/index.js';
4
4
  export { SemanticRetrievalService, InMemoryVectorStore, type RetrievalConfig, type RetrievalStrategy, type RetrievalResult, type IVectorStore, type VectorResult, type EmbeddingFunction, DEFAULT_RETRIEVAL_CONFIG, } from './semantic-retrieval.service.js';
@@ -2,6 +2,6 @@
2
2
  export { LongTermMemoryService, InMemoryMemoryRepository, } from './long-term-memory.service.js';
3
3
  export { PostgresMemoryRepository } from './pg-memory.repository.js';
4
4
  // 消息压缩模块
5
- export { SlidingWindowCompression, TruncateCompression, DEFAULT_COMPRESSION_CONFIG, estimateMessagesTokens, countRounds, } from './compression/index.js';
5
+ export { SlidingWindowCompression, TruncateCompression, DEFAULT_COMPRESSION_CONFIG, COMPRESSION_WINDOW_FACTOR, resolveCompressionMaxTokens, estimateMessagesTokens, countRounds, } from './compression/index.js';
6
6
  // 语义检索模块
7
7
  export { SemanticRetrievalService, InMemoryVectorStore, DEFAULT_RETRIEVAL_CONFIG, } from './semantic-retrieval.service.js';
@@ -18,8 +18,10 @@ export type CompressionStrategy = 'sliding-window' | 'truncate' | 'headroom';
18
18
  export interface CompressionConfig {
19
19
  /** 压缩策略(默认 sliding-window) */
20
20
  strategy: CompressionStrategy;
21
- /** 触发压缩的 Token 上限(默认模型 context window 的 70%) */
21
+ /** 触发压缩的 Token 上限。有 maxContextTokens 且未显式传 maxTokens 时按 window×系数 */
22
22
  maxTokens: number;
23
+ /** 模型上下文窗。有则 maxTokens = floor(window × COMPRESSION_WINDOW_FACTOR) */
24
+ maxContextTokens?: number;
23
25
  /** 备用:按轮次触发(默认 20 轮) */
24
26
  maxRounds: number;
25
27
  /** 压缩后保留的最近轮次(默认 5 轮) */
@@ -29,6 +31,15 @@ export interface CompressionConfig {
29
31
  /** 用于摘要的模型(可选,默认使用当前 Agent 的模型) */
30
32
  summaryModel?: string;
31
33
  }
34
+ /** 触发阈值 = 模型窗口 × 此系数(与原先 100000/128000 一致) */
35
+ export declare const COMPRESSION_WINDOW_FACTOR = 0.7;
36
+ /** 无模型窗口时的触发阈值(旧默认,按 128k×0.7) */
37
+ export declare const COMPRESSION_MAX_TOKENS_FALLBACK = 100000;
38
+ export declare function resolveCompressionMaxTokens(window?: number, factor?: number): number;
39
+ /**
40
+ * 合并压缩配置。未显式传 maxTokens 且有 maxContextTokens 时,按窗口×系数。
41
+ */
42
+ export declare function mergeCompressionConfig(base: CompressionConfig, override?: Partial<CompressionConfig>): CompressionConfig;
32
43
  /**
33
44
  * 默认压缩配置
34
45
  */
@@ -7,12 +7,40 @@
7
7
  * @see design.md §1.1 记忆管理
8
8
  * @see requirements.md Requirement 4.3
9
9
  */
10
+ /** 触发阈值 = 模型窗口 × 此系数(与原先 100000/128000 一致) */
11
+ export const COMPRESSION_WINDOW_FACTOR = 0.7;
12
+ /** 无模型窗口时的触发阈值(旧默认,按 128k×0.7) */
13
+ export const COMPRESSION_MAX_TOKENS_FALLBACK = 100000;
14
+ export function resolveCompressionMaxTokens(window, factor = COMPRESSION_WINDOW_FACTOR) {
15
+ if (typeof window === 'number' && Number.isFinite(window) && window > 0) {
16
+ const f = typeof factor === 'number' && Number.isFinite(factor) && factor > 0 && factor <= 1
17
+ ? factor
18
+ : COMPRESSION_WINDOW_FACTOR;
19
+ return Math.max(1, Math.floor(window * f));
20
+ }
21
+ return COMPRESSION_MAX_TOKENS_FALLBACK;
22
+ }
23
+ /**
24
+ * 合并压缩配置。未显式传 maxTokens 且有 maxContextTokens 时,按窗口×系数。
25
+ */
26
+ export function mergeCompressionConfig(base, override) {
27
+ if (!override)
28
+ return { ...base };
29
+ const merged = { ...base, ...override };
30
+ if (Object.prototype.hasOwnProperty.call(override, 'maxTokens')) {
31
+ return merged;
32
+ }
33
+ if (override.maxContextTokens) {
34
+ merged.maxTokens = resolveCompressionMaxTokens(override.maxContextTokens);
35
+ }
36
+ return merged;
37
+ }
10
38
  /**
11
39
  * 默认压缩配置
12
40
  */
13
41
  export const DEFAULT_COMPRESSION_CONFIG = {
14
42
  strategy: 'sliding-window',
15
- maxTokens: 100000, // 约 70% of 128k context
43
+ maxTokens: COMPRESSION_MAX_TOKENS_FALLBACK,
16
44
  maxRounds: 20,
17
45
  preserveRecentRounds: 5,
18
46
  compressionRatio: 0.3,
@@ -9,6 +9,7 @@
9
9
  import type { GraphMessage } from '../graph/contracts/executable-graph.js';
10
10
  import type { ILLMProvider, LLMNodeConfig, LLMResponse, LLMChunk } from '../graph/nodes/llm-node.js';
11
11
  import { type ILlmTurnLog } from '../debug/llm-turn-log.js';
12
+ export declare const DEFAULT_LLM_HTTP_TIMEOUT_MS = 180000;
12
13
  export interface HttpLLMProviderConfig {
13
14
  apiKey: string;
14
15
  baseUrl?: string;
@@ -8,6 +8,8 @@
8
8
  */
9
9
  import { resolveLlmModelName } from './resolve-llm-model.js';
10
10
  import { createFileLlmTurnLogFromEnv, maskApiKey } from '../debug/llm-turn-log.js';
11
+ import { buildContextUsage, turnSectionChars } from '../token/context-window.js';
12
+ export const DEFAULT_LLM_HTTP_TIMEOUT_MS = 180_000;
11
13
  export class HttpLLMProvider {
12
14
  apiKey;
13
15
  baseUrl;
@@ -18,7 +20,7 @@ export class HttpLLMProvider {
18
20
  this.apiKey = config.apiKey;
19
21
  this.baseUrl = (config.baseUrl ?? 'https://api.openai.com/v1').replace(/\/$/, '');
20
22
  this.defaultModel = config.defaultModel ?? 'DeepSeek-V4-Pro';
21
- this.timeoutMs = config.timeoutMs ?? 60000;
23
+ this.timeoutMs = config.timeoutMs ?? DEFAULT_LLM_HTTP_TIMEOUT_MS;
22
24
  this.turnLog = 'turnLog' in config ? config.turnLog : createFileLlmTurnLogFromEnv();
23
25
  }
24
26
  async writeTurn(args) {
@@ -57,6 +59,7 @@ export class HttpLLMProvider {
57
59
  error: args.response.error,
58
60
  },
59
61
  latencyMs: Date.now() - args.startedAt,
62
+ context: buildContextUsage(args.response.inputTokens, args.config.maxContextTokens, turnSectionChars(args.config, args.messages)),
60
63
  });
61
64
  }
62
65
  catch (err) {
@@ -1,5 +1,5 @@
1
1
  export { EchoLLMProvider, createDefaultLLMProvider } from './echo-llm-provider.js';
2
- export { HttpLLMProvider, type HttpLLMProviderConfig } from './http-llm-provider.js';
2
+ export { HttpLLMProvider, DEFAULT_LLM_HTTP_TIMEOUT_MS, type HttpLLMProviderConfig } from './http-llm-provider.js';
3
3
  export { RoutingLLMProvider, type RoutingLLMProviderConfig } from './routing-llm-provider.js';
4
4
  export { resolveLlmModelName, canOverrideGraphModel } from './resolve-llm-model.js';
5
5
  export { createLocalLlmProvider, requireLocalLlmConnections, resolveLocalLlmConnection, parseLocalLlmConnectionRefs, hydrateLocalLlmConnections, LLM_CONNECTIONS_ENV, } from './local-llm-provider.js';
@@ -1,5 +1,5 @@
1
1
  export { EchoLLMProvider, createDefaultLLMProvider } from './echo-llm-provider.js';
2
- export { HttpLLMProvider } from './http-llm-provider.js';
2
+ export { HttpLLMProvider, DEFAULT_LLM_HTTP_TIMEOUT_MS } from './http-llm-provider.js';
3
3
  export { RoutingLLMProvider } from './routing-llm-provider.js';
4
4
  export { resolveLlmModelName, canOverrideGraphModel } from './resolve-llm-model.js';
5
5
  export { createLocalLlmProvider, requireLocalLlmConnections, resolveLocalLlmConnection, parseLocalLlmConnectionRefs, hydrateLocalLlmConnections, LLM_CONNECTIONS_ENV, } from './local-llm-provider.js';
@@ -4,16 +4,19 @@
4
4
  */
5
5
  import type { ILLMProvider } from '../graph/nodes/llm-node.js';
6
6
  import type { ILlmTurnLog } from '../debug/llm-turn-log.js';
7
- import { type ModelCallDefaults } from '@gt-fe/eap-contracts';
7
+ import { type ModelCallDefaults } from '@eap/contracts';
8
8
  export interface LocalLlmConnection {
9
9
  baseUrl: string;
10
10
  apiKey: string;
11
11
  defaults?: ModelCallDefaults;
12
+ /** HttpLLMProvider 超时(毫秒);缺省用 DEFAULT_LLM_HTTP_TIMEOUT_MS */
13
+ timeoutMs?: number;
12
14
  }
13
15
  export interface LocalLlmConnectionRef {
14
16
  baseUrl: string;
15
17
  apiKeyEnv: string;
16
18
  defaults?: ModelCallDefaults;
19
+ timeoutMs?: number;
17
20
  }
18
21
  export interface LocalLlmConnectionRefs {
19
22
  default?: LocalLlmConnectionRef;
@@ -2,8 +2,13 @@
2
2
  * 本地多模型 LLM:按节点 model 选网关。仅 dev。
3
3
  * 连接表里只带 apiKeyEnv,密钥从 process.env 读取,不进 JSON。
4
4
  */
5
- import { mergeModelCallParams } from '@gt-fe/eap-contracts';
5
+ import { mergeModelCallParams } from '@eap/contracts';
6
6
  import { HttpLLMProvider } from './http-llm-provider.js';
7
+ function parseTimeoutMs(value) {
8
+ if (typeof value !== 'number' || !Number.isFinite(value) || value < 1000)
9
+ return undefined;
10
+ return Math.min(3_600_000, Math.trunc(value));
11
+ }
7
12
  export const LLM_CONNECTIONS_ENV = 'EAP_LLM_CONNECTIONS';
8
13
  export function parseLocalLlmConnectionRefs(env = process.env) {
9
14
  const raw = env[LLM_CONNECTIONS_ENV]?.trim();
@@ -42,7 +47,13 @@ export function hydrateLocalLlmConnections(refs, env = process.env) {
42
47
  const apiKey = env[ref.apiKeyEnv]?.trim();
43
48
  if (!apiKey)
44
49
  return undefined;
45
- return { baseUrl: ref.baseUrl, apiKey, defaults: ref.defaults };
50
+ const timeoutMs = parseTimeoutMs(ref.timeoutMs);
51
+ return {
52
+ baseUrl: ref.baseUrl,
53
+ apiKey,
54
+ defaults: ref.defaults,
55
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
56
+ };
46
57
  };
47
58
  const byModel = {};
48
59
  for (const [name, ref] of Object.entries(refs.byModel ?? {})) {
@@ -97,7 +108,7 @@ class LocalLlmProvider {
97
108
  }
98
109
  providerFor(model) {
99
110
  const connection = resolveLocalLlmConnection(this.connections, model);
100
- const cacheKey = `${connection.baseUrl}\0${connection.apiKey}\0${model ?? ''}`;
111
+ const cacheKey = `${connection.baseUrl}\0${connection.apiKey}\0${model ?? ''}\0${connection.timeoutMs ?? ''}`;
101
112
  const cached = this.cache.get(cacheKey);
102
113
  if (cached)
103
114
  return cached;
@@ -105,6 +116,7 @@ class LocalLlmProvider {
105
116
  apiKey: connection.apiKey,
106
117
  baseUrl: connection.baseUrl,
107
118
  defaultModel: model,
119
+ timeoutMs: connection.timeoutMs,
108
120
  turnLog: this.turnLog,
109
121
  });
110
122
  this.cache.set(cacheKey, provider);
@@ -11,9 +11,9 @@
11
11
  * - 支持任意 OpenAI 兼容 API(OpenAI / DeepSeek / 本地 Ollama 等)
12
12
  * - 连接配置由 Foundation 统一管理,Runtime 无感知
13
13
  */
14
- import { mergeModelCallParams } from '@gt-fe/eap-contracts';
14
+ import { mergeModelCallParams } from '@eap/contracts';
15
15
  import { EchoLLMProvider } from './echo-llm-provider.js';
16
- import { HttpLLMProvider } from './http-llm-provider.js';
16
+ import { DEFAULT_LLM_HTTP_TIMEOUT_MS, HttpLLMProvider } from './http-llm-provider.js';
17
17
  export class RoutingLLMProvider {
18
18
  foundationUrl;
19
19
  fallback;
@@ -35,7 +35,12 @@ export class RoutingLLMProvider {
35
35
  }
36
36
  withCallDefaults(config, selected) {
37
37
  const merged = mergeModelCallParams(config, selected.defaults);
38
- return { ...config, ...merged, model: selected.name };
38
+ return {
39
+ ...config,
40
+ ...merged,
41
+ model: selected.name,
42
+ maxContextTokens: selected.maxContextTokens ?? config.maxContextTokens,
43
+ };
39
44
  }
40
45
  async selectModel(config) {
41
46
  try {
@@ -50,11 +55,14 @@ export class RoutingLLMProvider {
50
55
  if (res.ok) {
51
56
  const body = await res.json();
52
57
  if (body.data?.name) {
58
+ const window = body.data.maxContextTokens
59
+ ?? body.data.capabilities?.maxContextTokens;
53
60
  return {
54
61
  name: body.data.name,
55
62
  provider: body.data.provider,
56
63
  connection: body.data.connection ?? null,
57
64
  defaults: body.data.defaults ?? null,
65
+ ...(typeof window === 'number' && window > 0 ? { maxContextTokens: window } : {}),
58
66
  };
59
67
  }
60
68
  }
@@ -71,7 +79,7 @@ export class RoutingLLMProvider {
71
79
  apiKey: selected.connection.apiKey ?? '',
72
80
  baseUrl: selected.connection.baseUrl,
73
81
  defaultModel: selected.name,
74
- timeoutMs: selected.connection.timeoutMs,
82
+ timeoutMs: selected.connection.timeoutMs ?? DEFAULT_LLM_HTTP_TIMEOUT_MS,
75
83
  turnLog: this.turnLog,
76
84
  });
77
85
  }
@@ -81,6 +89,7 @@ export class RoutingLLMProvider {
81
89
  apiKey: process.env.OPENAI_API_KEY,
82
90
  baseUrl: process.env.OPENAI_BASE_URL,
83
91
  defaultModel: selected.name,
92
+ timeoutMs: DEFAULT_LLM_HTTP_TIMEOUT_MS,
84
93
  turnLog: this.turnLog,
85
94
  });
86
95
  }
@@ -1 +1 @@
1
- export { resolveDeclaredModels, resolveAgentDefaultModel, resolveAgentModelAllowlist, } from '@gt-fe/eap-contracts';
1
+ export { resolveDeclaredModels, resolveAgentDefaultModel, resolveAgentModelAllowlist, } from '@eap/contracts';
@@ -1 +1 @@
1
- export { resolveDeclaredModels, resolveAgentDefaultModel, resolveAgentModelAllowlist, } from '@gt-fe/eap-contracts';
1
+ export { resolveDeclaredModels, resolveAgentDefaultModel, resolveAgentModelAllowlist, } from '@eap/contracts';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Agent Registry 客户端 — 从 foundation-service 拉取 Agent 定义
3
3
  */
4
- /** Agent 清单(与 @gt-fe/eap-contracts AgentManifest 对齐) */
4
+ /** Agent 清单(与 @eap/contracts AgentManifest 对齐) */
5
5
  export interface AgentManifest {
6
6
  id: string;
7
7
  name: string;
@@ -13,7 +13,7 @@ import { readdir, readFile, access } from 'node:fs/promises';
13
13
  import { createHash } from 'node:crypto';
14
14
  import path from 'node:path';
15
15
  import { constants as fsConstants } from 'node:fs';
16
- import { parseSkillMd } from '@gt-fe/eap-contracts';
16
+ import { parseSkillMd } from '@eap/contracts';
17
17
  import { findSkillEntryFile } from './skill-tree.js';
18
18
  import { preloadMissing, preloadPackageCorrupt, } from './preload-errors.js';
19
19
  import { isRequireChecksum, resolvePreloadBehaviorFlags, } from './preload-flags.js';
@@ -538,35 +538,37 @@ export class FileSystemDeploymentSnapshot {
538
538
  break;
539
539
  }
540
540
  }
541
- if (!manifest && entryFile) {
542
- const parsed = parseSkillMd(await readFile(entryFile, 'utf8'));
543
- if (!parsed)
541
+ const parsedMd = entryFile
542
+ ? parseSkillMd(await readFile(entryFile, 'utf8'))
543
+ : null;
544
+ if (!manifest) {
545
+ if (!parsedMd)
544
546
  continue;
545
547
  manifest = {
546
- code: parsed.frontmatter.name,
547
- name: parsed.frontmatter.name,
548
- description: parsed.frontmatter.description,
549
- ...(parsed.frontmatter.license ? { license: parsed.frontmatter.license } : {}),
548
+ code: ref.skillId,
549
+ name: parsedMd.frontmatter.name,
550
+ description: parsedMd.frontmatter.description,
551
+ ...(parsedMd.frontmatter.license ? { license: parsedMd.frontmatter.license } : {}),
550
552
  };
551
553
  manifestPath = entryFile;
552
554
  }
555
+ else if (parsedMd) {
556
+ manifest = {
557
+ ...manifest,
558
+ name: parsedMd.frontmatter.name,
559
+ description: parsedMd.frontmatter.description,
560
+ ...(parsedMd.frontmatter.license ? { license: parsedMd.frontmatter.license } : {}),
561
+ };
562
+ }
553
563
  if (!manifest || !manifestPath)
554
564
  continue;
555
- if (!manifest.description && entryFile) {
556
- const parsed = parseSkillMd(await readFile(entryFile, 'utf8'));
557
- if (parsed?.frontmatter.description) {
558
- manifest = { ...manifest, description: parsed.frontmatter.description };
559
- if (!manifest.name)
560
- manifest.name = parsed.frontmatter.name;
561
- }
562
- }
563
565
  const isUserOverridePath = bundlePath.includes('/users/') || bundlePath.includes('\\users\\');
564
566
  const isAuthorOverlay = extraRoots.some((root) => root && isPathInside(root, bundlePath));
565
567
  if (ref.sha256 && !isUserOverridePath && !isAuthorOverlay && manifestPath.endsWith('.json')) {
566
568
  const actual = await sha256OfFile(manifestPath);
567
569
  await assertDigestMatch(ref.sha256, actual, `skill ${ref.skillId}`, { path: manifestPath });
568
570
  }
569
- const skillId = String(manifest.code ?? ref.skillId);
571
+ const skillId = String(ref.skillId);
570
572
  return {
571
573
  skillId,
572
574
  name: String(manifest.name ?? ref.name ?? skillId),
@@ -7,7 +7,7 @@
7
7
  import type { GraphDSL } from '../graph/contracts/executable-graph.js';
8
8
  import type { AgentRegistryClient } from './agent-registry-client.js';
9
9
  import type { DeploymentContext } from '../tool/tool-execution-router.js';
10
- import type { ILocalToolInstallStore } from '@gt-fe/eap-contracts';
10
+ import type { ILocalToolInstallStore } from '@eap/contracts';
11
11
  import { type LoadedToolDefinition } from '../tool/loaded-tool-definition.js';
12
12
  import type { LoadedSkillInfo } from '../tool/system-prompt-builder.js';
13
13
  import { type IDeploymentSnapshot } from './deployment-snapshot.js';
@@ -17,6 +17,11 @@ export interface LoadAgentOptions {
17
17
  /** 跳过读写进程内定义缓存 */
18
18
  bypassCache?: boolean;
19
19
  }
20
+ export declare function resolveAgentSystemPromptFromLoader(loader: {
21
+ resolveAgentSystemPrompt?: (agentCode: string, version?: string) => Promise<string | undefined>;
22
+ } | undefined, agentCode: string, version?: string): Promise<string | undefined>;
23
+ /** manifest.prompts.system;空白视为缺省。 */
24
+ export declare function extractAgentSystemPrompt(manifest: unknown): string | undefined;
20
25
  export interface GraphLoaderConfig {
21
26
  registryClient?: AgentRegistryClient;
22
27
  deploymentContext?: DeploymentContext;
@@ -104,6 +109,11 @@ export declare class GraphLoader {
104
109
  private cloneCached;
105
110
  loadGraph(agentCode: string, options?: LoadAgentOptions): Promise<GraphDSL>;
106
111
  private loadGraphFromSource;
112
+ /**
113
+ * Agent 基线系统提示词(manifest.prompts.system)。
114
+ * 每轮 execute/stream 注入 GraphState.agentSystemPrompt;不写进节点 config。
115
+ */
116
+ resolveAgentSystemPrompt(agentCode: string, version?: string): Promise<string | undefined>;
107
117
  /**
108
118
  * 解析 agent tools[] + graph tool 节点 union;
109
119
  * server+预装:以预装 manifest 为准;desktop 合并 ILocalToolInstallStore。
@@ -8,10 +8,12 @@ import { readFile } from 'node:fs/promises';
8
8
  import path from 'node:path';
9
9
  import { resolveAgentModelAllowlist, resolveAgentDefaultModel } from './agent-models.js';
10
10
  import { DEFAULT_HTTP_TOOL_TIMEOUT_SEC, DEFAULT_TOOL_TIMEOUT_SEC } from '../tool/timeout.js';
11
+ import { isMcpDiscoverStub } from '../tool/mcp-list.js';
11
12
  import { createDeploymentSnapshotFromEnv, isInlineToolDeclaration, overlayToolRefPolicies, } from './deployment-snapshot.js';
12
13
  import { preloadGraphNotFound, preloadManifestNotFound, preloadSkillNotFound, preloadToolNotFound, } from './preload-errors.js';
13
14
  import { PreinstallDefinitionCache, resolveDefinitionCacheEnabled, } from './definition-cache.js';
14
15
  import { inspectAgentSkillTree, listSkillDirs, readSkillAssembly, resolveSkillHotReload, skillAssemblyStamp, } from './skill-tree.js';
16
+ import { emitRuntimeLog } from '../common/logger.js';
15
17
  function applyDefaultModelToGraph(graph, defaultModel) {
16
18
  if (!defaultModel)
17
19
  return graph;
@@ -56,6 +58,24 @@ function asGraphDsl(value) {
56
58
  }
57
59
  return undefined;
58
60
  }
61
+ export async function resolveAgentSystemPromptFromLoader(loader, agentCode, version) {
62
+ if (!loader || typeof loader.resolveAgentSystemPrompt !== 'function')
63
+ return undefined;
64
+ return loader.resolveAgentSystemPrompt(agentCode, version);
65
+ }
66
+ /** manifest.prompts.system;空白视为缺省。 */
67
+ export function extractAgentSystemPrompt(manifest) {
68
+ if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest))
69
+ return undefined;
70
+ const prompts = manifest.prompts;
71
+ if (!prompts || typeof prompts !== 'object' || Array.isArray(prompts))
72
+ return undefined;
73
+ const system = prompts.system;
74
+ if (typeof system !== 'string')
75
+ return undefined;
76
+ const trimmed = system.trim();
77
+ return trimmed ? trimmed : undefined;
78
+ }
59
79
  function extractSkillPrompt(manifest) {
60
80
  const prompt = manifest.prompt;
61
81
  if (typeof prompt === 'string' && prompt.trim())
@@ -95,35 +115,6 @@ async function resolveSkillPrompt(manifest, bundlePath) {
95
115
  }
96
116
  return raw;
97
117
  }
98
- function extractSkillToolsets(manifest) {
99
- const refs = manifest.toolsets;
100
- if (!Array.isArray(refs))
101
- return undefined;
102
- const out = [];
103
- for (const item of refs) {
104
- if (typeof item === 'string') {
105
- out.push(item);
106
- continue;
107
- }
108
- if (!item || typeof item !== 'object')
109
- continue;
110
- const o = item;
111
- const code = o.code != null ? String(o.code) : undefined;
112
- const enabled = Array.isArray(o.enabled)
113
- ? o.enabled.filter((x) => typeof x === 'string')
114
- : undefined;
115
- out.push({
116
- name: o.name != null ? String(o.name) : code,
117
- version: o.version != null ? String(o.version) : undefined,
118
- code,
119
- enabled,
120
- autoApprove: Array.isArray(o.autoApprove)
121
- ? o.autoApprove.filter((x) => typeof x === 'string')
122
- : undefined,
123
- });
124
- }
125
- return out.length > 0 ? out : undefined;
126
- }
127
118
  function extractEmbeddedGraph(manifest) {
128
119
  const candidates = [
129
120
  manifest.graph,
@@ -223,6 +214,67 @@ function toLoadedToolDefinition(record) {
223
214
  inputSchema: m.input,
224
215
  };
225
216
  }
217
+ function resolveMcpServer(manifest) {
218
+ if (manifest.server && typeof manifest.server === 'object' && !Array.isArray(manifest.server)) {
219
+ return manifest.server;
220
+ }
221
+ const execution = manifest.execution;
222
+ if (execution && typeof execution === 'object' && !Array.isArray(execution)) {
223
+ const exec = execution;
224
+ if (exec.server && typeof exec.server === 'object' && !Array.isArray(exec.server)) {
225
+ return exec.server;
226
+ }
227
+ const mcp = exec.mcp;
228
+ if (mcp && typeof mcp === 'object' && !Array.isArray(mcp)) {
229
+ const nested = mcp.server;
230
+ if (nested && typeof nested === 'object' && !Array.isArray(nested)) {
231
+ return nested;
232
+ }
233
+ }
234
+ }
235
+ return undefined;
236
+ }
237
+ function mcpDiscoverStub(pre, options) {
238
+ const m = pre.manifest;
239
+ const server = resolveMcpServer(m);
240
+ const toolRuntime = (m.toolRuntime ?? m.runtime);
241
+ const timeout = (typeof server?.timeout === 'number' ? server.timeout : undefined)
242
+ ?? DEFAULT_TOOL_TIMEOUT_SEC;
243
+ const transport = (typeof server?.transport === 'string' && server.transport)
244
+ || (server?.url ? 'streamable_http' : 'stdio');
245
+ const execution = {
246
+ protocol: 'mcp',
247
+ transport,
248
+ server,
249
+ timeout,
250
+ discover: true,
251
+ bundlePath: pre.bundlePath,
252
+ };
253
+ if (options?.enabledTools && options.enabledTools.length > 0) {
254
+ execution.enabledTools = options.enabledTools;
255
+ }
256
+ if (options?.autoApprove && options.autoApprove.length > 0) {
257
+ execution.autoApprove = options.autoApprove;
258
+ }
259
+ return {
260
+ toolId: pre.toolId,
261
+ name: pre.name,
262
+ manifest: {
263
+ id: pre.toolId,
264
+ name: pre.name,
265
+ version: pre.version,
266
+ type: 'mcp',
267
+ status: 'active',
268
+ description: m.description,
269
+ toolRuntime,
270
+ dataScope: normalizeDataScope(m.permissions && typeof m.permissions === 'object' && !Array.isArray(m.permissions)
271
+ ? m.permissions.dataScope
272
+ : m.dataScope),
273
+ requireConfirmation: false,
274
+ execution,
275
+ },
276
+ };
277
+ }
226
278
  function fromPreinstallTool(pre) {
227
279
  const m = pre.manifest;
228
280
  const permissions = m.permissions;
@@ -254,14 +306,16 @@ function fromPreinstallTool(pre) {
254
306
  };
255
307
  }
256
308
  /**
257
- * 展开预装工具集(toolset.manifest.json,含 tools[])为多个 LoadedToolDefinition。
258
- * 以工具集为管理单元:单个 MCP 服务 / 单个代码包 = 一个工具集,可含多个子工具;
259
- * 子工具继承集级 server/package 配置,合成各自的 execution。
260
- * 无 tools[] 时回退为单工具 manifest(tool.manifest.json 结构)。
309
+ * 展开预装工具集为 LoadedToolDefinition。
310
+ * - type=mcp:忽略清单 tools[],产出一条 discover stub;子工具在执行期 tools/list。
311
+ * - type=code/http:展开 tools[];无 tools[] 时回退单工具 manifest。
261
312
  * enabled 为可选白名单(agent.manifest toolsets[].enabled);缺省启用全部子工具。
262
313
  */
263
314
  function expandPreinstallToolset(pre, options) {
264
315
  const m = pre.manifest;
316
+ if (m.type === 'mcp') {
317
+ return [mcpDiscoverStub(pre, options)];
318
+ }
265
319
  const tools = m.tools;
266
320
  const enabled = options?.enabledTools;
267
321
  const enabledSet = enabled && enabled.length > 0 ? new Set(enabled) : null;
@@ -280,7 +334,6 @@ function expandPreinstallToolset(pre, options) {
280
334
  }
281
335
  const setType = m.type;
282
336
  const toolRuntime = (m.toolRuntime ?? m.runtime);
283
- const server = m.server;
284
337
  const pkg = m.package;
285
338
  const out = [];
286
339
  for (const raw of tools) {
@@ -301,19 +354,10 @@ function expandPreinstallToolset(pre, options) {
301
354
  baseUrl: st.baseUrl ?? gateway.baseUrl,
302
355
  pathTemplate: st.pathTemplate,
303
356
  allowedHosts: gateway.allowedHosts,
357
+ header: gateway.header,
304
358
  timeout: st.timeout ?? gateway.timeout ?? DEFAULT_HTTP_TOOL_TIMEOUT_SEC,
305
359
  };
306
360
  }
307
- else if (setType === 'mcp') {
308
- type = 'mcp';
309
- execution = {
310
- protocol: 'mcp',
311
- transport: server?.transport ?? (server?.url ? 'streamable_http' : 'stdio'),
312
- server,
313
- toolName: name,
314
- timeout: st.timeout ?? server?.timeout ?? DEFAULT_TOOL_TIMEOUT_SEC,
315
- };
316
- }
317
361
  else {
318
362
  type = 'code';
319
363
  execution = {
@@ -512,28 +556,24 @@ export class GraphLoader {
512
556
  catch (err) {
513
557
  const message = err instanceof Error ? err.message : String(err);
514
558
  skillWarnings.push(message);
515
- console.warn(JSON.stringify({
559
+ emitRuntimeLog('warn', 'graph-loader', message, {
516
560
  event: 'runtime.warmAgent.skills_failed',
517
- level: 'warn',
518
561
  agentCode,
519
562
  agentVersion: version ?? null,
520
- message,
521
- }));
563
+ });
522
564
  }
523
565
  // 预期跳过(无 userId 的 users 层 Skill)用 debug,避免单测/生产噪音
524
566
  if (skippedSkills.length > 0) {
525
- console.debug(JSON.stringify({
567
+ emitRuntimeLog('debug', 'graph-loader', 'warmAgent skipped user skills', {
526
568
  event: 'runtime.warmAgent.skills_skipped',
527
- level: 'debug',
528
569
  agentCode,
529
570
  agentVersion: version ?? null,
530
571
  userId: options?.userId ?? null,
531
572
  skippedSkillIds: skippedSkills,
532
- }));
573
+ });
533
574
  }
534
- console.info(JSON.stringify({
575
+ emitRuntimeLog('info', 'graph-loader', 'warmAgent ok', {
535
576
  event: 'runtime.warmAgent.ok',
536
- level: 'info',
537
577
  agentCode,
538
578
  agentVersion: version ?? null,
539
579
  graph: true,
@@ -541,7 +581,7 @@ export class GraphLoader {
541
581
  skills: skillsOk,
542
582
  skillWarningCount: skillWarnings.length,
543
583
  skillWarnings,
544
- }));
584
+ });
545
585
  return {
546
586
  graph: true,
547
587
  tools: true,
@@ -622,7 +662,6 @@ export class GraphLoader {
622
662
  type: 'llm',
623
663
  config: {
624
664
  model,
625
- systemPrompt: manifest.prompts?.system,
626
665
  },
627
666
  },
628
667
  ],
@@ -632,6 +671,24 @@ export class GraphLoader {
632
671
  },
633
672
  };
634
673
  }
674
+ /**
675
+ * Agent 基线系统提示词(manifest.prompts.system)。
676
+ * 每轮 execute/stream 注入 GraphState.agentSystemPrompt;不写进节点 config。
677
+ */
678
+ async resolveAgentSystemPrompt(agentCode, version) {
679
+ if (this.deploymentSnapshot) {
680
+ const snap = await this.deploymentSnapshot.loadAgentSnapshot(agentCode, version);
681
+ if (snap) {
682
+ return extractAgentSystemPrompt(snap.manifest);
683
+ }
684
+ if (this.blockRegistryDefinitions)
685
+ return undefined;
686
+ }
687
+ if (this.blockRegistryDefinitions || !this.registryClient)
688
+ return undefined;
689
+ const record = await this.registryClient.getAgent(agentCode);
690
+ return extractAgentSystemPrompt(record?.manifest);
691
+ }
635
692
  /**
636
693
  * 解析 agent tools[] + graph tool 节点 union;
637
694
  * server+预装:以预装 manifest 为准;desktop 合并 ILocalToolInstallStore。
@@ -743,6 +800,9 @@ export class GraphLoader {
743
800
  continue;
744
801
  const pre = await this.deploymentSnapshot.loadToolManifest({ name: toolName }, agentDir);
745
802
  if (!pre) {
803
+ // MCP 子工具名在 tools/list 后才出现,图内 type:tool 节点不必对应预装目录
804
+ if (definitions.some(isMcpDiscoverStub))
805
+ continue;
746
806
  missing.push(toolName);
747
807
  continue;
748
808
  }
@@ -851,7 +911,6 @@ export class GraphLoader {
851
911
  prompt,
852
912
  bundlePath: pre.bundlePath,
853
913
  entryFile,
854
- toolsets: extractSkillToolsets(pre.manifest),
855
914
  inputDef: pre.manifest.inputDef,
856
915
  outputDef: pre.manifest.outputDef,
857
916
  });