@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
@@ -0,0 +1,199 @@
1
+ /**
2
+ * MCP tools/list 解析、discover stub 判定、hydrate。
3
+ *
4
+ * type: mcp 的预装工具集不展开清单 tools[],先产出 discover stub;
5
+ * 有 ExecutionContext 后由同一执行器 tools/list,再写入 LLM tools。
6
+ */
7
+ import { emitRuntimeLog } from '../common/logger.js';
8
+ export function parseListedTools(result) {
9
+ const tools = result?.tools;
10
+ if (!Array.isArray(tools))
11
+ return [];
12
+ const out = [];
13
+ for (const raw of tools) {
14
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
15
+ continue;
16
+ const t = raw;
17
+ if (typeof t.name !== 'string' || !t.name.trim())
18
+ continue;
19
+ const inputSchema = t.inputSchema && typeof t.inputSchema === 'object' && !Array.isArray(t.inputSchema)
20
+ ? t.inputSchema
21
+ : undefined;
22
+ out.push({
23
+ name: t.name,
24
+ description: typeof t.description === 'string' ? t.description : undefined,
25
+ inputSchema,
26
+ });
27
+ }
28
+ return out;
29
+ }
30
+ export function nextListCursor(result) {
31
+ const cursor = result?.nextCursor;
32
+ return typeof cursor === 'string' && cursor.trim() ? cursor : undefined;
33
+ }
34
+ export function readMcpDiscoverExecution(raw) {
35
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw))
36
+ return null;
37
+ const e = raw;
38
+ if (e.protocol !== 'mcp' || e.discover !== true)
39
+ return null;
40
+ return e;
41
+ }
42
+ export function isMcpDiscoverStub(def) {
43
+ return def.manifest.type === 'mcp' && readMcpDiscoverExecution(def.manifest.execution) != null;
44
+ }
45
+ export function mcpEndpointKey(execution, fallback = 'gateway') {
46
+ if (!execution || typeof execution !== 'object' || Array.isArray(execution))
47
+ return fallback;
48
+ const e = execution;
49
+ const server = e.server;
50
+ if (server && typeof server === 'object') {
51
+ if (typeof server.url === 'string' && server.url.trim()) {
52
+ return `http:${server.url.trim()}`;
53
+ }
54
+ if (typeof server.command === 'string' && server.command.trim()) {
55
+ const args = Array.isArray(server.args)
56
+ ? server.args.map((a) => String(a)).join('\0')
57
+ : '';
58
+ return `stdio:${server.command.trim()}:${args}`;
59
+ }
60
+ }
61
+ return fallback;
62
+ }
63
+ export const MCP_LIST_TOOL_NAME = 'tools/list';
64
+ /** 展示用:`tl-data-035/get_dataset_by_code`;无名工具集则只返回工具名。 */
65
+ export function formatToolsetToolName(toolset, toolName) {
66
+ const set = toolset?.trim() ?? '';
67
+ const name = toolName.trim() || 'tool';
68
+ if (!set || set === name)
69
+ return name;
70
+ if (name.startsWith(`${set}/`))
71
+ return name;
72
+ return `${set}/${name}`;
73
+ }
74
+ /** 从 hydrate 后的 toolId(`toolset:toolName`)取出工具集编码。 */
75
+ export function toolsetCodeFromLoaded(loaded) {
76
+ if (!loaded)
77
+ return undefined;
78
+ const id = loaded.toolId || loaded.manifest?.id || '';
79
+ const sep = id.indexOf(':');
80
+ if (sep > 0) {
81
+ const prefix = id.slice(0, sep);
82
+ if (prefix && prefix !== loaded.name)
83
+ return prefix;
84
+ }
85
+ return undefined;
86
+ }
87
+ /**
88
+ * 将 MCP discover stub 替换为 tools/list 结果。
89
+ * 无 stub 时原样返回(同一引用)。按 endpoint+user+agent 在执行器内缓存 list。
90
+ * enabledTools 为可选白名单;autoApprove 的子工具 requireConfirmation=false。
91
+ * 单个 endpoint 的 list 失败只跳过该工具集;全部失败才抛错(错误含工具集名)。
92
+ */
93
+ export async function hydrateMcpTools(loadedTools, executor, auth) {
94
+ if (!loadedTools)
95
+ return loadedTools;
96
+ const stubs = Object.values(loadedTools).filter(isMcpDiscoverStub);
97
+ if (stubs.length === 0)
98
+ return loadedTools;
99
+ if (typeof executor?.listTools !== 'function') {
100
+ throw new Error('MCP 工具集需要 tools/list,当前执行器不支持 listTools');
101
+ }
102
+ const result = { ...loadedTools };
103
+ for (const stub of stubs) {
104
+ delete result[stub.name];
105
+ }
106
+ const groups = new Map();
107
+ for (const stub of stubs) {
108
+ const key = mcpEndpointKey(stub.manifest.execution);
109
+ const list = groups.get(key);
110
+ if (list)
111
+ list.push(stub);
112
+ else
113
+ groups.set(key, [stub]);
114
+ }
115
+ const emitted = new Set();
116
+ const failures = [];
117
+ for (const [, group] of groups) {
118
+ const primary = group[0];
119
+ const endpoint = mcpEndpointKey(primary.manifest.execution, primary.name);
120
+ let listed;
121
+ try {
122
+ listed = await executor.listTools({
123
+ toolCallId: `mcp-list:${primary.toolId}`,
124
+ toolName: primary.name,
125
+ arguments: {},
126
+ authContext: auth.authContext,
127
+ correlationId: auth.correlationId,
128
+ taskId: auth.taskId,
129
+ toolType: 'mcp',
130
+ toolRuntime: primary.manifest.toolRuntime,
131
+ execution: primary.manifest.execution,
132
+ });
133
+ }
134
+ catch (err) {
135
+ const detail = err instanceof Error ? err.message : String(err);
136
+ const ref = formatToolsetToolName(primary.name, MCP_LIST_TOOL_NAME);
137
+ const message = `MCP ${ref} 失败 (${endpoint}): ${detail}`;
138
+ failures.push(message);
139
+ emitRuntimeLog('warn', 'tool', message, {
140
+ agentCode: auth.authContext.agentCode,
141
+ taskId: auth.taskId,
142
+ correlationId: auth.correlationId,
143
+ });
144
+ auth.onListFailure?.({ toolset: primary.name, endpoint, error: detail });
145
+ continue;
146
+ }
147
+ for (const stub of group) {
148
+ const exec = readMcpDiscoverExecution(stub.manifest.execution);
149
+ const enabled = exec?.enabledTools;
150
+ const enabledSet = enabled && enabled.length > 0 ? new Set(enabled) : null;
151
+ const autoApprove = new Set(exec?.autoApprove ?? []);
152
+ const picked = enabledSet ? listed.filter((t) => enabledSet.has(t.name)) : listed;
153
+ for (const tool of picked) {
154
+ if (emitted.has(tool.name))
155
+ continue;
156
+ emitted.add(tool.name);
157
+ result[tool.name] = listedToolToDefinition(tool, stub, autoApprove.has(tool.name));
158
+ }
159
+ }
160
+ }
161
+ if (emitted.size === 0 && failures.length > 0) {
162
+ throw new Error(failures.join('\n'));
163
+ }
164
+ return result;
165
+ }
166
+ function listedToolToDefinition(tool, stub, autoApprove) {
167
+ const exec = { ...(readMcpDiscoverExecution(stub.manifest.execution) ?? {}) };
168
+ delete exec.discover;
169
+ delete exec.enabledTools;
170
+ delete exec.autoApprove;
171
+ const rest = exec;
172
+ return {
173
+ toolId: `${stub.toolId}:${tool.name}`,
174
+ name: tool.name,
175
+ manifest: {
176
+ id: `${stub.manifest.id}:${tool.name}`,
177
+ name: tool.name,
178
+ version: stub.manifest.version,
179
+ type: 'mcp',
180
+ status: 'active',
181
+ description: tool.description,
182
+ toolRuntime: stub.manifest.toolRuntime,
183
+ dataScope: stub.manifest.dataScope,
184
+ requireConfirmation: autoApprove ? false : Boolean(stub.manifest.requireConfirmation),
185
+ execution: {
186
+ ...rest,
187
+ protocol: 'mcp',
188
+ toolName: tool.name,
189
+ },
190
+ },
191
+ inputSchema: tool.inputSchema ?? { type: 'object', properties: {} },
192
+ };
193
+ }
194
+ /** 测试与 HITL 续跑:从已 hydrate 的 map 判断是否还要 list */
195
+ export function needsMcpHydration(loadedTools) {
196
+ if (!loadedTools)
197
+ return false;
198
+ return Object.values(loadedTools).some(isMcpDiscoverStub);
199
+ }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @see docs/contracts/tool-execution.md §2.0
5
5
  */
6
- import type { IToolExecutor, ToolCallRequest, ToolCallResult } from './tool-executor.interface.js';
6
+ import type { IToolExecutor, McpListedTool, ToolCallRequest, ToolCallResult } from './tool-executor.interface.js';
7
7
  export interface McpToolExecutorOptions {
8
8
  baseUrl: string;
9
9
  timeout?: number;
@@ -16,8 +16,13 @@ export declare class McpToolExecutor implements IToolExecutor {
16
16
  private readonly mcpUrl;
17
17
  private rpcId;
18
18
  private initialized;
19
+ /** endpoint+user+agent → in-flight / completed tools/list */
20
+ private readonly listCache;
19
21
  constructor(options: McpToolExecutorOptions);
20
22
  execute(request: ToolCallRequest): Promise<ToolCallResult>;
23
+ listTools(request: ToolCallRequest): Promise<McpListedTool[]>;
24
+ private listAllTools;
25
+ private rpcResult;
21
26
  private ensureInitialized;
22
27
  private postRpc;
23
28
  }
@@ -3,8 +3,9 @@
3
3
  *
4
4
  * @see docs/contracts/tool-execution.md §2.0
5
5
  */
6
- import { ErrorCodes } from '@gt-fe/eap-contracts';
6
+ import { ErrorCodes } from '@eap/contracts';
7
7
  import { abortOrTimeoutMessage, isAbortError } from './timeout.js';
8
+ import { nextListCursor, parseListedTools } from './mcp-list.js';
8
9
  function resolveAgentCode(request) {
9
10
  return request.authContext.agentCode;
10
11
  }
@@ -45,6 +46,8 @@ export class McpToolExecutor {
45
46
  mcpUrl;
46
47
  rpcId = 1;
47
48
  initialized = false;
49
+ /** endpoint+user+agent → in-flight / completed tools/list */
50
+ listCache = new Map();
48
51
  constructor(options) {
49
52
  this.baseUrl = options.baseUrl.replace(/\/+$/, '');
50
53
  this.timeout = options.timeout ?? 30_000;
@@ -125,6 +128,55 @@ export class McpToolExecutor {
125
128
  };
126
129
  }
127
130
  }
131
+ async listTools(request) {
132
+ const key = `${this.mcpUrl}\0${request.authContext.userId}\0${request.authContext.agentCode}`;
133
+ const cached = this.listCache.get(key);
134
+ if (cached)
135
+ return cached;
136
+ const pending = this.listAllTools(request);
137
+ this.listCache.set(key, pending);
138
+ try {
139
+ return await pending;
140
+ }
141
+ catch (err) {
142
+ this.listCache.delete(key);
143
+ throw err;
144
+ }
145
+ }
146
+ async listAllTools(request) {
147
+ await this.ensureInitialized(request);
148
+ const tools = [];
149
+ let cursor;
150
+ do {
151
+ const result = await this.rpcResult(request, 'tools/list', cursor ? { cursor } : {});
152
+ tools.push(...parseListedTools(result));
153
+ cursor = nextListCursor(result);
154
+ } while (cursor);
155
+ return tools;
156
+ }
157
+ async rpcResult(request, method, params) {
158
+ const agentCode = resolveAgentCode(request);
159
+ const correlationId = request.correlationId ?? request.toolCallId;
160
+ const id = this.rpcId++;
161
+ const response = await this.postRpc({ jsonrpc: '2.0', id, method, params }, request, agentCode, correlationId);
162
+ if (!response.ok) {
163
+ let body = null;
164
+ try {
165
+ body = (await response.json());
166
+ }
167
+ catch {
168
+ body = null;
169
+ }
170
+ const mapped = mapMcpError(response.status, body);
171
+ throw new Error(mapped.error ?? `MCP ${method} failed: HTTP ${response.status}`);
172
+ }
173
+ const body = (await response.json());
174
+ if (body.error) {
175
+ const mapped = mapMcpError(500, body);
176
+ throw new Error(mapped.error ?? `MCP ${method} failed`);
177
+ }
178
+ return body.result;
179
+ }
128
180
  async ensureInitialized(request) {
129
181
  if (this.initialized)
130
182
  return;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 推断 harness 用的技能包根:命令里的绝对路径、本批/历史 read_file、bind、唯一技能。
3
+ * 不写 GraphState.activatedSkill。
4
+ */
5
+ import { type SkillDirRef } from '../graph/adapters/skill-read.js';
6
+ import type { GraphMessage } from '../graph/contracts/executable-graph.js';
7
+ export declare function longestSkillDirInText(text: string, skillDirs: SkillDirRef[]): string | undefined;
8
+ export declare function inferHarnessSkillDir(opts: {
9
+ command?: string;
10
+ toolCalls?: Array<{
11
+ name?: string;
12
+ arguments?: Record<string, unknown>;
13
+ }>;
14
+ messages?: GraphMessage[];
15
+ skillDirs: SkillDirRef[];
16
+ activatedSkill?: string | null;
17
+ }): string | undefined;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * 推断 harness 用的技能包根:命令里的绝对路径、本批/历史 read_file、bind、唯一技能。
3
+ * 不写 GraphState.activatedSkill。
4
+ */
5
+ import { classifySkillRead } from '../graph/adapters/skill-read.js';
6
+ import { normalizeSkillDir } from './skill-script-refs.js';
7
+ export function longestSkillDirInText(text, skillDirs) {
8
+ const hay = text.replace(/\\/g, '/');
9
+ let best;
10
+ for (const dir of skillDirs) {
11
+ if (!dir.bundlePath)
12
+ continue;
13
+ const root = normalizeSkillDir(dir.bundlePath);
14
+ if (root && hay.includes(root) && (!best || root.length > best.len)) {
15
+ best = { bundlePath: dir.bundlePath, len: root.length };
16
+ }
17
+ }
18
+ return best?.bundlePath;
19
+ }
20
+ function bundleForSkillId(skillId, skillDirs) {
21
+ return skillDirs.find((dir) => dir.skillId === skillId)?.bundlePath;
22
+ }
23
+ function lastReadSkillDir(toolCalls, skillDirs) {
24
+ if (!toolCalls?.length)
25
+ return undefined;
26
+ let found;
27
+ for (const tc of toolCalls) {
28
+ const hit = classifySkillRead(tc.name ?? '', tc.arguments, skillDirs);
29
+ if (hit?.skillId) {
30
+ const bundle = bundleForSkillId(hit.skillId, skillDirs);
31
+ if (bundle)
32
+ found = bundle;
33
+ }
34
+ }
35
+ return found;
36
+ }
37
+ function inferFromMessages(messages, skillDirs) {
38
+ if (!messages?.length)
39
+ return undefined;
40
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
41
+ const msg = messages[i];
42
+ if (msg.role === 'assistant' && msg.toolCalls?.length) {
43
+ const found = lastReadSkillDir(msg.toolCalls.map((tc) => ({ name: tc.name, arguments: tc.arguments })), skillDirs);
44
+ if (found)
45
+ return found;
46
+ }
47
+ if (msg.role === 'tool' && typeof msg.content === 'string') {
48
+ const tagged = msg.content.match(/技能包根(只读)([^\s。]+)|EAP_SKILL_DIR=([^\s。]+)/);
49
+ const dir = tagged?.[1] ?? tagged?.[2];
50
+ if (dir) {
51
+ const norm = normalizeSkillDir(dir);
52
+ const hit = skillDirs.find((d) => d.bundlePath && normalizeSkillDir(d.bundlePath) === norm);
53
+ if (hit?.bundlePath)
54
+ return hit.bundlePath;
55
+ }
56
+ const fromBody = longestSkillDirInText(msg.content, skillDirs);
57
+ if (fromBody)
58
+ return fromBody;
59
+ }
60
+ }
61
+ return undefined;
62
+ }
63
+ export function inferHarnessSkillDir(opts) {
64
+ const dirs = opts.skillDirs.filter((dir) => Boolean(dir.bundlePath));
65
+ if (opts.command) {
66
+ const fromCommand = longestSkillDirInText(opts.command, dirs);
67
+ if (fromCommand)
68
+ return fromCommand;
69
+ }
70
+ const fromBatch = lastReadSkillDir(opts.toolCalls, dirs);
71
+ if (fromBatch)
72
+ return fromBatch;
73
+ const fromHistory = inferFromMessages(opts.messages, dirs);
74
+ if (fromHistory)
75
+ return fromHistory;
76
+ if (dirs.length === 1)
77
+ return dirs[0].bundlePath;
78
+ if (opts.activatedSkill)
79
+ return bundleForSkillId(opts.activatedSkill, dirs);
80
+ return undefined;
81
+ }
@@ -6,15 +6,8 @@
6
6
  */
7
7
  export declare const EAP_SKILL_DIR_ENV = "EAP_SKILL_DIR";
8
8
  export interface ExpandSkillRefsOptions {
9
- platform?: string;
10
9
  exists?: (absPath: string) => boolean;
11
10
  }
12
11
  export declare function normalizeSkillDir(skillDir: string): string;
13
- export declare function skillEnvRef(platform?: string): string;
14
- /**
15
- * 包内相对脚本路径 → 技能目录绝对路径。
16
- * 带目录或 `./` 的(`bin/foo.py`、`./main.py`、`scripts/a.py`)一律展开。
17
- * 根上的裸文件名(`main.py`)仅当包内确实有该文件才展开,以免改写「写到用户目录再跑」的例子。
18
- */
19
- export declare function expandSkillScriptRefs(body: string, skillDir: string, platformOrOpts?: string | ExpandSkillRefsOptions): string;
12
+ export declare function expandSkillScriptRefs(body: string, skillDir: string, opts?: ExpandSkillRefsOptions): string;
20
13
  export declare function isSkillEntryFile(filePath: string): boolean;
@@ -13,14 +13,6 @@ const RELATIVE_CODE_PATH = new RegExp(`(^|[^$\\w./\\\\:%])(\\.\\/)?((?:[\\w.-]+[
13
13
  export function normalizeSkillDir(skillDir) {
14
14
  return skillDir.replace(/\\/g, '/').replace(/\/+$/, '');
15
15
  }
16
- export function skillEnvRef(platform = process.platform) {
17
- return platform === 'win32' ? `%${EAP_SKILL_DIR_ENV}%` : `$${EAP_SKILL_DIR_ENV}`;
18
- }
19
- function asOptions(platformOrOpts) {
20
- if (typeof platformOrOpts === 'string')
21
- return { platform: platformOrOpts };
22
- return platformOrOpts ?? {};
23
- }
24
16
  function defaultExists(absPath) {
25
17
  try {
26
18
  return existsSync(absPath);
@@ -34,12 +26,37 @@ function defaultExists(absPath) {
34
26
  * 带目录或 `./` 的(`bin/foo.py`、`./main.py`、`scripts/a.py`)一律展开。
35
27
  * 根上的裸文件名(`main.py`)仅当包内确实有该文件才展开,以免改写「写到用户目录再跑」的例子。
36
28
  */
37
- export function expandSkillScriptRefs(body, skillDir, platformOrOpts) {
38
- const opts = asOptions(platformOrOpts);
39
- const platform = opts.platform ?? process.platform;
40
- const exists = opts.exists ?? defaultExists;
29
+ function resolvePackageRelativePath(rel, skillDir, exists = defaultExists) {
30
+ const raw = rel.trim();
31
+ if (!raw || raw.startsWith('#') || raw.includes('://'))
32
+ return undefined;
33
+ if (path.isAbsolute(raw) || /^[A-Za-z]:[\\/]/.test(raw))
34
+ return undefined;
35
+ if (raw.startsWith('$') || raw.startsWith('%'))
36
+ return undefined;
37
+ const relNorm = raw.replace(/\\/g, '/').replace(/^\.\//, '');
38
+ if (!relNorm || !/\.[A-Za-z0-9]{1,8}$/.test(relNorm))
39
+ return undefined;
40
+ const abs = path.resolve(skillDir, relNorm);
41
+ if (!exists(abs))
42
+ return undefined;
43
+ return normalizeSkillDir(abs);
44
+ }
45
+ function expandPackageFileRefs(body, skillDir, exists) {
46
+ return body.replace(/\]\(([^)]+)\)|`([^`\n]+)`/g, (full, linkDest, tickDest) => {
47
+ const raw = (linkDest ?? tickDest ?? '').trim();
48
+ const abs = resolvePackageRelativePath(raw, skillDir, exists);
49
+ if (!abs)
50
+ return full;
51
+ if (linkDest != null)
52
+ return `](${abs})`;
53
+ return `\`${abs}\``;
54
+ });
55
+ }
56
+ export function expandSkillScriptRefs(body, skillDir, opts) {
57
+ const exists = opts?.exists ?? defaultExists;
41
58
  const dir = normalizeSkillDir(skillDir);
42
- const expanded = body.replace(RELATIVE_CODE_PATH, (full, prefix, dotSlash, rel) => {
59
+ let expanded = body.replace(RELATIVE_CODE_PATH, (full, prefix, dotSlash, rel) => {
43
60
  const relNorm = rel.replace(/\\/g, '/');
44
61
  const packageRelative = Boolean(dotSlash) || relNorm.includes('/');
45
62
  if (!packageRelative && !exists(path.join(skillDir, relNorm))) {
@@ -47,15 +64,10 @@ export function expandSkillScriptRefs(body, skillDir, platformOrOpts) {
47
64
  }
48
65
  return `${prefix}${dir}/${relNorm}`;
49
66
  });
50
- const envRef = skillEnvRef(platform);
51
- const envPath = platform === 'win32' ? `${envRef}\\<相对路径>` : `${envRef}/<相对路径>`;
52
- const note = `技能目录(只读)环境变量 ${EAP_SKILL_DIR_ENV}=${dir}。` +
53
- `包内任意文件用 ${envPath}(不限 scripts/)。` +
54
- `读入正文里像包内脚本的相对路径已展开为绝对路径。` +
55
- `当前工作目录是用户目录,相对路径写出在用户目录。\n\n`;
67
+ expanded = expandPackageFileRefs(expanded, skillDir, exists);
56
68
  if (body.includes(`${EAP_SKILL_DIR_ENV}=`))
57
69
  return expanded;
58
- return note + expanded;
70
+ return `技能包根(只读)${dir}。\n\n` + expanded;
59
71
  }
60
72
  export function isSkillEntryFile(filePath) {
61
73
  return /(^|[\\/])(SKILL\.md|skill\.md)$/i.test(filePath);
@@ -7,12 +7,15 @@
7
7
  * ```
8
8
  * [平台规则常量] ← 代码内置,安全约束 + 行为准则(最高优先级)
9
9
  * [当前时间] ← 运行时动态注入(自然周等时间规则的判定基准)
10
- * [Agent 身份] ← agentName / agentCode
10
+ * [Agent 身份] ← agentName / agentCode(目录身份,不是 prompts.system)
11
11
  * [可用工具短目录] ← 名称 + 一句话用途;完整 schema 仍走 API tools
12
- * [开发者提示词] ← Graph LLM 节点 config.systemPrompt,平台透传
13
- * (Registry 无内嵌 Graph 的兜底路径读 agent.manifest.json prompts.system)
12
+ * [Agent 基线] ← agent.manifest.json prompts.system;非 silent LLM
13
+ * [节点提示词] ← Graph LLM 节点 config.systemPrompt / systemPromptFile
14
14
  * ```
15
15
  *
16
+ * 基线只走 GraphState.agentSystemPrompt,不写进节点 config。无图时节点无增量。
17
+ * silent 节点不带基线。技能正文不灌 system。
18
+ *
16
19
  * 工具 schema 走 API 的 tools 参数(见 loadedToolsToApiTools);system 短目录给模型发现用,不是对用户报菜名。
17
20
  *
18
21
  * @see docs/status/module-status-and-backlog.md RT-5
@@ -37,6 +40,10 @@ export interface LoadedSkillInfo {
37
40
  workflow?: import('../graph/contracts/executable-graph.js').GraphDSL;
38
41
  /** 子图入口可选上下文;Harness 主路径不灌 system */
39
42
  prompt?: string;
43
+ /**
44
+ * 已废弃:Skill 不再声明/装配 Tool。
45
+ * 工具只来自 Agent `toolsets`;本字段忽略。
46
+ */
40
47
  toolsets?: Array<string | {
41
48
  name?: string;
42
49
  version?: string;
@@ -51,24 +58,24 @@ export interface LoadedSkillInfo {
51
58
  export type LoadedSkillMap = Record<string, LoadedSkillInfo>;
52
59
  export declare function toLoadedSkillMap(skills: LoadedSkillInfo[] | undefined): LoadedSkillMap | undefined;
53
60
  /**
54
- * 按 Skill.toolsets 收窄工具集(子图用)。
55
- * - 无 toolsets / 空数组:不收窄
56
- * - string / name:精确匹配工具名
57
- * - code + enabled:仅白名单子工具
58
- * - code 无 enabled:匹配 toolId 前缀 `<code>:` 或 id 等于 code
59
- * - 始终保留 Harness 原语
61
+ * 进入 Skill 子图时的工具集。
62
+ * 工具只来自 Agent 装配;不读 Skill `toolsets` / `toolRefs` / SKILL.md `allowed-tools`。
60
63
  */
61
- export declare function filterLoadedToolsForSkill(loadedTools: LoadedToolMap | undefined, skill: LoadedSkillInfo | undefined): LoadedToolMap | undefined;
64
+ export declare function filterLoadedToolsForSkill(loadedTools: LoadedToolMap | undefined, _skill?: LoadedSkillInfo): LoadedToolMap | undefined;
62
65
  /**
63
66
  * 提示词构建上下文
64
67
  */
65
68
  export interface PromptBuildContext {
66
69
  /**
67
- * 开发者编写的系统提示词。
68
- * 来源:Graph LLM 节点 config.systemPrompt(主路径);
69
- * Registry 无内嵌 Graph 的兜底路径为 agent.manifest.json prompts.system。
70
+ * Agent 基线系统提示词(manifest.prompts.system)。
71
+ * 非 silent LLM 才传入;空则省略本段。
72
+ */
73
+ agentPrompt?: string;
74
+ /**
75
+ * 本节点增量。来源:Graph LLM 节点 config.systemPrompt / systemPromptFile。
76
+ * 空或空白则省略。不再承担 Agent 基线。
70
77
  */
71
- developerPrompt?: string;
78
+ nodePrompt?: string;
72
79
  /** Agent 名称 */
73
80
  agentName?: string;
74
81
  /** Agent 代码标识 */
@@ -88,7 +95,7 @@ export interface PromptBuildContext {
88
95
  bundlePath: string;
89
96
  entryFile?: string;
90
97
  }>;
91
- /** 已装配 Skill 短目录;单独成段,不并进开发者提示词 */
98
+ /** 已装配 Skill 短目录;单独成段,不并进 Agent 基线 / 节点提示词 */
92
99
  skillCatalog?: string;
93
100
  /** 已装配工具短目录(名称 + 用途);schema 仍走 API tools */
94
101
  toolCatalog?: string;
@@ -102,12 +109,24 @@ export interface AssembledSystemPrompt {
102
109
  text: string;
103
110
  sections: PromptSection[];
104
111
  }
112
+ /**
113
+ * 非 silent LLM 才带 Agent 基线;节点提示词空则省略。
114
+ * silent 只保留节点增量(选技能 / JSON)。
115
+ */
116
+ export declare function resolveLlmPromptInputs(input: {
117
+ silent: boolean;
118
+ agentSystemPrompt?: unknown;
119
+ nodePrompt?: unknown;
120
+ }): {
121
+ agentPrompt?: string;
122
+ nodePrompt?: string;
123
+ };
105
124
  /**
106
125
  * SystemPromptBuilder — 系统提示词拼接
107
126
  *
108
127
  * 具名段拼接(无模板引擎)。顺序固定,段标题用于排查。
109
128
  * 工具短目录进 system;完整 schema 走 API tools 参数。
110
- * 开发者提示词整段透传,不做 {{ }} 替换。
129
+ * Agent 基线与节点提示词整段透传,不做 {{ }} 替换。
111
130
  */
112
131
  export declare class SystemPromptBuilder {
113
132
  /**