@gt-fe/eap-runtime 0.0.8 → 0.0.11
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/README.md +15 -20
- package/dist/api/handlers/invoke.handler.js +7 -3
- package/dist/api/handlers/messages.handler.js +0 -1
- package/dist/api/handlers/stream.handler.js +8 -4
- package/dist/api/routes/agent.routes.d.ts +2 -2
- package/dist/api/routes/agent.routes.js +2 -2
- package/dist/checkpoint/checkpoint-ref-notify.d.ts +10 -0
- package/dist/checkpoint/checkpoint-ref-notify.js +59 -0
- package/dist/checkpoint/index.d.ts +1 -0
- package/dist/checkpoint/index.js +1 -0
- package/dist/checkpoint/pg-checkpoint-saver.js +25 -2
- package/dist/common/agent-code.d.ts +2 -4
- package/dist/common/agent-code.js +4 -5
- package/dist/common/types.d.ts +5 -5
- package/dist/common/types.js +2 -2
- package/dist/contracts/permission-checker.d.ts +1 -1
- package/dist/contracts/trace-collector.d.ts +16 -2
- package/dist/debug/llm-turn-log.d.ts +100 -0
- package/dist/debug/llm-turn-log.js +130 -0
- package/dist/debug/pg-llm-turn-log.d.ts +22 -0
- package/dist/debug/pg-llm-turn-log.js +57 -0
- package/dist/governance/governance-client.d.ts +1 -1
- package/dist/governance/governance-client.js +40 -16
- package/dist/governance/index.d.ts +2 -2
- package/dist/governance/index.js +1 -1
- package/dist/governance/permission-guard.d.ts +2 -1
- package/dist/governance/permission-guard.js +13 -4
- package/dist/graph/adapters/langgraph-checkpointer.js +3 -1
- package/dist/graph/adapters/langgraph.adapter.d.ts +1 -0
- package/dist/graph/adapters/langgraph.adapter.js +153 -92
- package/dist/graph/adapters/skill-read.d.ts +12 -0
- package/dist/graph/adapters/skill-read.js +63 -0
- package/dist/graph/adapters/subgraph-compiler.js +5 -0
- package/dist/graph/contracts/executable-graph.d.ts +11 -9
- package/dist/graph/contracts/executable-graph.js +1 -1
- package/dist/graph/hitl/hitl-handler.d.ts +10 -0
- package/dist/graph/hitl/hitl-handler.js +50 -6
- package/dist/graph/nodes/llm-node.d.ts +14 -0
- package/dist/graph/nodes/skill-bind-node.js +1 -2
- package/dist/graph/nodes/tool-node.d.ts +2 -0
- package/dist/graph/nodes/tool-node.js +50 -10
- package/dist/graph/trace-span.d.ts +22 -0
- package/dist/graph/trace-span.js +69 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -1
- package/dist/memory/compression/compression.repository.d.ts +1 -15
- package/dist/providers/echo-llm-provider.d.ts +1 -1
- package/dist/providers/echo-llm-provider.js +4 -3
- package/dist/providers/http-llm-provider.d.ts +4 -0
- package/dist/providers/http-llm-provider.js +129 -4
- package/dist/providers/local-llm-provider.d.ts +5 -1
- package/dist/providers/local-llm-provider.js +14 -6
- package/dist/providers/routing-llm-provider.d.ts +4 -0
- package/dist/providers/routing-llm-provider.js +12 -2
- package/dist/registry/agent-models.d.ts +1 -1
- package/dist/registry/agent-models.js +1 -1
- package/dist/registry/agent-registry-client.d.ts +3 -3
- package/dist/registry/deployment-snapshot.d.ts +17 -5
- package/dist/registry/deployment-snapshot.js +97 -49
- package/dist/registry/graph-loader.d.ts +2 -2
- package/dist/registry/graph-loader.js +43 -80
- package/dist/registry/index.d.ts +2 -2
- package/dist/registry/index.js +2 -2
- package/dist/registry/preload-errors.d.ts +1 -1
- package/dist/registry/preload-errors.js +1 -1
- package/dist/registry/skill-io-guard.js +1 -1
- package/dist/registry/skill-tree.d.ts +3 -0
- package/dist/registry/skill-tree.js +41 -3
- package/dist/runtime/agent-runtime.d.ts +42 -3
- package/dist/runtime/agent-runtime.js +129 -39
- package/dist/runtime/dev-runtime.d.ts +7 -4
- package/dist/runtime/dev-runtime.js +24 -21
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/storage-profile.d.ts +19 -10
- package/dist/runtime/storage-profile.js +42 -29
- package/dist/runtime/types.d.ts +17 -19
- package/dist/server.js +2 -2
- package/dist/session/session.service.d.ts +5 -0
- package/dist/session/session.service.js +12 -0
- package/dist/sse/events/index.d.ts +5 -3
- package/dist/sse/events/index.js +2 -0
- package/dist/sse/events/skill-events.d.ts +12 -1
- package/dist/sse/events/skill-events.js +1 -1
- package/dist/sse/trace-collector.d.ts +10 -0
- package/dist/sse/trace-collector.js +1 -0
- package/dist/token/token-budget.service.d.ts +2 -2
- package/dist/token/token-budget.service.js +1 -1
- package/dist/tool/create-tool-executor.d.ts +1 -1
- package/dist/tool/create-tool-executor.js +3 -2
- package/dist/tool/dev-http-executor.d.ts +1 -1
- package/dist/tool/dev-http-executor.js +10 -7
- package/dist/tool/dev-mcp-executor.js +5 -3
- package/dist/tool/dev-tool-executor.d.ts +1 -1
- package/dist/tool/dev-tool-executor.js +2 -2
- package/dist/tool/harness-command.d.ts +32 -0
- package/dist/tool/harness-command.js +276 -0
- package/dist/tool/harness-primitives.d.ts +54 -0
- package/dist/tool/harness-primitives.js +492 -0
- package/dist/tool/http-tool-executor.js +16 -21
- package/dist/tool/index.d.ts +4 -2
- package/dist/tool/index.js +4 -2
- package/dist/tool/local-mcp-client.d.ts +1 -1
- package/dist/tool/local-mcp-client.js +54 -23
- package/dist/tool/local-tool-executor.d.ts +4 -4
- package/dist/tool/local-tool-executor.js +18 -16
- package/dist/tool/mcp-tool-executor.js +23 -7
- package/dist/tool/skill-script-refs.d.ts +20 -0
- package/dist/tool/skill-script-refs.js +62 -0
- package/dist/tool/system-prompt-builder.d.ts +61 -25
- package/dist/tool/system-prompt-builder.js +198 -92
- package/dist/tool/timeout.d.ts +14 -0
- package/dist/tool/timeout.js +36 -0
- package/dist/tool/tool-confirmation.js +2 -1
- package/dist/tool/tool-execution-router.d.ts +1 -1
- package/dist/tool/tool-execution-router.js +2 -2
- package/dist/tool/tool-executor.interface.d.ts +23 -7
- package/dist/tool/tool-orchestrator.d.ts +1 -1
- package/dist/tool/tool-orchestrator.js +6 -3
- package/dist/utils/format-error.d.ts +2 -2
- package/dist/utils/format-error.js +2 -2
- package/dist/utils/http-error.d.ts +2 -2
- package/dist/utils/http-error.js +2 -2
- package/package.json +3 -3
- package/dist/common/concurrency.d.ts +0 -79
- package/dist/common/concurrency.js +0 -121
- package/dist/common/queue-consumer.d.ts +0 -125
- package/dist/common/queue-consumer.js +0 -159
- package/dist/tool/mock-tool-executor.d.ts +0 -28
- package/dist/tool/mock-tool-executor.js +0 -65
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import readline from 'node:readline';
|
|
9
|
+
import { isAbortError, resolveToolTimeoutSec, toToolTimeoutMs, toolTimeoutErrorMessage, } from './timeout.js';
|
|
9
10
|
import { fileArtifactFromToolOutput, fileBasesFromEnv } from './file-artifact.js';
|
|
10
11
|
export function formatMcpOutput(result) {
|
|
11
12
|
const r = result;
|
|
@@ -70,10 +71,13 @@ function withUserMeta(params, userId) {
|
|
|
70
71
|
function rpcStdio(session, method, params) {
|
|
71
72
|
const id = session.nextId++;
|
|
72
73
|
const timeoutMs = session.timeoutMs;
|
|
74
|
+
const timeoutSec = Math.max(1, Math.round(timeoutMs / 1000));
|
|
73
75
|
return new Promise((resolve, reject) => {
|
|
74
76
|
const timer = setTimeout(() => {
|
|
75
77
|
session.pending.delete(id);
|
|
76
|
-
|
|
78
|
+
const err = new Error(toolTimeoutErrorMessage(timeoutSec));
|
|
79
|
+
err.name = 'TimeoutError';
|
|
80
|
+
reject(err);
|
|
77
81
|
}, timeoutMs);
|
|
78
82
|
session.pending.set(id, {
|
|
79
83
|
resolve: (v) => { clearTimeout(timer); resolve(v); },
|
|
@@ -86,7 +90,7 @@ async function invokeStdio(execution, args, context) {
|
|
|
86
90
|
const { server } = execution;
|
|
87
91
|
const command = server.command ?? 'node';
|
|
88
92
|
const spawnArgs = server.args ?? [];
|
|
89
|
-
const timeoutMs = execution.timeout
|
|
93
|
+
const timeoutMs = toToolTimeoutMs(execution.timeout);
|
|
90
94
|
const userId = resolveUserId(execution, context);
|
|
91
95
|
const child = spawn(command, spawnArgs, {
|
|
92
96
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -138,12 +142,32 @@ async function invokeStdio(execution, args, context) {
|
|
|
138
142
|
rl.close();
|
|
139
143
|
}
|
|
140
144
|
}
|
|
145
|
+
async function postMcp(url, headers, payload, signal) {
|
|
146
|
+
const response = await fetch(url, {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
headers,
|
|
149
|
+
body: JSON.stringify(payload),
|
|
150
|
+
signal,
|
|
151
|
+
});
|
|
152
|
+
const sessionId = response.headers.get('mcp-session-id') ?? undefined;
|
|
153
|
+
const raw = await response.text();
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
const snippet = raw.replace(/\s+/g, ' ').trim().slice(0, 240);
|
|
156
|
+
throw new Error(`MCP HTTP ${response.status}: ${response.statusText}${snippet ? ` ${snippet}` : ''}`);
|
|
157
|
+
}
|
|
158
|
+
const body = parseMcpHttpBody(raw);
|
|
159
|
+
if (body.error) {
|
|
160
|
+
throw new Error(body.error.message ?? 'MCP HTTP error');
|
|
161
|
+
}
|
|
162
|
+
return { body, sessionId };
|
|
163
|
+
}
|
|
141
164
|
async function invokeStreamableHttp(execution, args, context) {
|
|
142
165
|
const url = expandEnvRefs(execution.server.url);
|
|
143
166
|
if (!url) {
|
|
144
167
|
throw new Error('streamable_http MCP requires server.url');
|
|
145
168
|
}
|
|
146
|
-
const
|
|
169
|
+
const timeoutSec = resolveToolTimeoutSec(execution.timeout);
|
|
170
|
+
const timeoutMs = toToolTimeoutMs(timeoutSec);
|
|
147
171
|
const controller = new AbortController();
|
|
148
172
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
149
173
|
const userId = resolveUserId(execution, context);
|
|
@@ -161,28 +185,35 @@ async function invokeStreamableHttp(execution, args, context) {
|
|
|
161
185
|
if (userId) {
|
|
162
186
|
headers.userId = userId;
|
|
163
187
|
}
|
|
164
|
-
const body = {
|
|
165
|
-
jsonrpc: '2.0',
|
|
166
|
-
id: 1,
|
|
167
|
-
method: 'tools/call',
|
|
168
|
-
params: withUserMeta({ name: execution.toolName, arguments: args }, userId),
|
|
169
|
-
};
|
|
170
188
|
try {
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
if (
|
|
183
|
-
|
|
189
|
+
const init = await postMcp(url, headers, {
|
|
190
|
+
jsonrpc: '2.0',
|
|
191
|
+
id: 1,
|
|
192
|
+
method: 'initialize',
|
|
193
|
+
params: {
|
|
194
|
+
protocolVersion: '2024-11-05',
|
|
195
|
+
capabilities: {},
|
|
196
|
+
clientInfo: { name: 'eap-runtime-local', version: '0.1.0' },
|
|
197
|
+
},
|
|
198
|
+
}, controller.signal);
|
|
199
|
+
const callHeaders = { ...headers };
|
|
200
|
+
if (init.sessionId)
|
|
201
|
+
callHeaders['mcp-session-id'] = init.sessionId;
|
|
202
|
+
const call = await postMcp(url, callHeaders, {
|
|
203
|
+
jsonrpc: '2.0',
|
|
204
|
+
id: 2,
|
|
205
|
+
method: 'tools/call',
|
|
206
|
+
params: withUserMeta({ name: execution.toolName, arguments: args }, userId),
|
|
207
|
+
}, controller.signal);
|
|
208
|
+
return toInvokeResult(call.body.result, execution.server.env);
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
if (isAbortError(err)) {
|
|
212
|
+
const timeoutErr = new Error(toolTimeoutErrorMessage(timeoutSec));
|
|
213
|
+
timeoutErr.name = 'TimeoutError';
|
|
214
|
+
throw timeoutErr;
|
|
184
215
|
}
|
|
185
|
-
|
|
216
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
186
217
|
}
|
|
187
218
|
finally {
|
|
188
219
|
clearTimeout(timer);
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* LocalToolExecutor — 桌面本地 Code / MCP Tool 执行
|
|
3
3
|
*
|
|
4
4
|
* 优先 ILocalToolInstallStore;若无安装记录但 request.execution 带
|
|
5
|
-
*
|
|
5
|
+
* 或 request.execution 带可解析的 code bundlePath+handler,则直接本机执行。
|
|
6
6
|
*/
|
|
7
|
-
import type { ILocalToolInstallStore } from '@
|
|
7
|
+
import type { ILocalToolInstallStore } from '@eap/contracts';
|
|
8
8
|
import type { IToolExecutor, ToolCallRequest, ToolCallResult } from './tool-executor.interface.js';
|
|
9
9
|
import { type ILocalMcpClient } from './local-mcp-client.js';
|
|
10
10
|
export interface LocalToolExecutorOptions {
|
|
@@ -19,10 +19,10 @@ interface CodeExecutionSpec {
|
|
|
19
19
|
args?: string[];
|
|
20
20
|
timeoutMs?: number;
|
|
21
21
|
}
|
|
22
|
-
/** 是否为应本机执行的
|
|
22
|
+
/** 是否为应本机执行的 code(显式 localPreferred,例如 DevToolExecutor) */
|
|
23
23
|
export declare function isLocalPreferredExecution(raw: unknown): boolean;
|
|
24
24
|
/**
|
|
25
|
-
* 从 ToolCallRequest.execution 解析本机 code
|
|
25
|
+
* 从 ToolCallRequest.execution 解析本机 code 执行规格。
|
|
26
26
|
*/
|
|
27
27
|
export declare function resolveCodeExecutionFromRequest(request: ToolCallRequest): CodeExecutionSpec | null;
|
|
28
28
|
export declare class LocalToolExecutor implements IToolExecutor {
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* LocalToolExecutor — 桌面本地 Code / MCP Tool 执行
|
|
3
3
|
*
|
|
4
4
|
* 优先 ILocalToolInstallStore;若无安装记录但 request.execution 带
|
|
5
|
-
*
|
|
5
|
+
* 或 request.execution 带可解析的 code bundlePath+handler,则直接本机执行。
|
|
6
6
|
*/
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import { existsSync } from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
-
import { resolveReadyInstallByToolName, resolveMcpExecutionFromInstall, resolveCodeHandlerFromInstall, } from '@
|
|
10
|
+
import { ErrorCodes, resolveReadyInstallByToolName, resolveMcpExecutionFromInstall, resolveCodeHandlerFromInstall, } from '@eap/contracts';
|
|
11
|
+
import { isAbortError, toToolTimeoutMs, toolTimeoutErrorMessage } from './timeout.js';
|
|
11
12
|
import { fileArtifactFromToolOutput, fileBasesFromEnv } from './file-artifact.js';
|
|
12
13
|
import { LocalMcpClient } from './local-mcp-client.js';
|
|
13
14
|
/**
|
|
@@ -66,7 +67,9 @@ function runHandler(handlerPath, input, timeoutMs, execution) {
|
|
|
66
67
|
};
|
|
67
68
|
const timer = setTimeout(() => {
|
|
68
69
|
child.kill('SIGKILL');
|
|
69
|
-
|
|
70
|
+
const timeoutErr = new Error(toolTimeoutErrorMessage(Math.max(1, Math.round(timeoutMs / 1000))));
|
|
71
|
+
timeoutErr.name = 'TimeoutError';
|
|
72
|
+
finish(() => reject(timeoutErr));
|
|
70
73
|
}, timeoutMs);
|
|
71
74
|
child.stdout.on('data', (d) => { stdout += d.toString(); });
|
|
72
75
|
child.stderr.on('data', (d) => { stderr += d.toString(); });
|
|
@@ -88,19 +91,17 @@ function runHandler(handlerPath, input, timeoutMs, execution) {
|
|
|
88
91
|
child.stdin.end();
|
|
89
92
|
});
|
|
90
93
|
}
|
|
91
|
-
/** 是否为应本机执行的
|
|
94
|
+
/** 是否为应本机执行的 code(显式 localPreferred,例如 DevToolExecutor) */
|
|
92
95
|
export function isLocalPreferredExecution(raw) {
|
|
93
96
|
if (!raw || typeof raw !== 'object')
|
|
94
97
|
return false;
|
|
95
98
|
const e = raw;
|
|
96
99
|
if (e.protocol === 'mcp')
|
|
97
100
|
return false;
|
|
98
|
-
|
|
99
|
-
return true;
|
|
100
|
-
return false;
|
|
101
|
+
return e.localPreferred === true;
|
|
101
102
|
}
|
|
102
103
|
/**
|
|
103
|
-
* 从 ToolCallRequest.execution 解析本机 code
|
|
104
|
+
* 从 ToolCallRequest.execution 解析本机 code 执行规格。
|
|
104
105
|
*/
|
|
105
106
|
export function resolveCodeExecutionFromRequest(request) {
|
|
106
107
|
const raw = request.execution;
|
|
@@ -114,7 +115,7 @@ export function resolveCodeExecutionFromRequest(request) {
|
|
|
114
115
|
const handler = typeof e.handler === 'string' ? e.handler : '';
|
|
115
116
|
const command = typeof e.command === 'string' ? e.command : undefined;
|
|
116
117
|
const args = Array.isArray(e.args) ? e.args.map(String) : undefined;
|
|
117
|
-
const timeoutMs = typeof e.timeout === 'number' && e.timeout > 0 ? e.timeout : undefined;
|
|
118
|
+
const timeoutMs = typeof e.timeout === 'number' && e.timeout > 0 ? toToolTimeoutMs(e.timeout) : undefined;
|
|
118
119
|
if (runtime === 'executable') {
|
|
119
120
|
const handlerPath = handler
|
|
120
121
|
? (path.isAbsolute(handler) || !bundlePath
|
|
@@ -153,7 +154,7 @@ export class LocalToolExecutor {
|
|
|
153
154
|
}
|
|
154
155
|
return this.executeCode(request, install);
|
|
155
156
|
}
|
|
156
|
-
//
|
|
157
|
+
// 无本地安装:仅显式 localPreferred 走 request.execution
|
|
157
158
|
if (isLocalPreferredExecution(request.execution)) {
|
|
158
159
|
const fromReq = resolveCodeExecutionFromRequest(request);
|
|
159
160
|
if (fromReq) {
|
|
@@ -183,14 +184,14 @@ export class LocalToolExecutor {
|
|
|
183
184
|
};
|
|
184
185
|
}
|
|
185
186
|
catch (error) {
|
|
186
|
-
const
|
|
187
|
+
const aborted = isAbortError(error);
|
|
187
188
|
return {
|
|
188
189
|
toolCallId: request.toolCallId,
|
|
189
190
|
toolName: request.toolName,
|
|
190
191
|
content: '',
|
|
191
192
|
success: false,
|
|
192
|
-
error: message,
|
|
193
|
-
errorCode: 'EXECUTION_FAILED',
|
|
193
|
+
error: error instanceof Error ? error.message : String(error),
|
|
194
|
+
errorCode: aborted ? ErrorCodes.TOOL_TIMEOUT : 'EXECUTION_FAILED',
|
|
194
195
|
runtime: 'local',
|
|
195
196
|
};
|
|
196
197
|
}
|
|
@@ -205,6 +206,7 @@ export class LocalToolExecutor {
|
|
|
205
206
|
handlerPath,
|
|
206
207
|
runtime: execution?.runtime,
|
|
207
208
|
args: execution?.args,
|
|
209
|
+
timeoutMs: typeof execution?.timeout === 'number' ? toToolTimeoutMs(execution.timeout) : undefined,
|
|
208
210
|
});
|
|
209
211
|
}
|
|
210
212
|
async executeMcp(request, install) {
|
|
@@ -234,14 +236,14 @@ export class LocalToolExecutor {
|
|
|
234
236
|
};
|
|
235
237
|
}
|
|
236
238
|
catch (error) {
|
|
237
|
-
const
|
|
239
|
+
const aborted = isAbortError(error);
|
|
238
240
|
return {
|
|
239
241
|
toolCallId: request.toolCallId,
|
|
240
242
|
toolName: request.toolName,
|
|
241
243
|
content: '',
|
|
242
244
|
success: false,
|
|
243
|
-
error: message,
|
|
244
|
-
errorCode: 'EXECUTION_FAILED',
|
|
245
|
+
error: error instanceof Error ? error.message : String(error),
|
|
246
|
+
errorCode: aborted ? ErrorCodes.TOOL_TIMEOUT : 'EXECUTION_FAILED',
|
|
245
247
|
runtime: 'local',
|
|
246
248
|
};
|
|
247
249
|
}
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @see docs/contracts/tool-execution.md §2.0
|
|
5
5
|
*/
|
|
6
|
+
import { ErrorCodes } from '@eap/contracts';
|
|
7
|
+
import { abortOrTimeoutMessage, isAbortError } from './timeout.js';
|
|
6
8
|
function resolveAgentCode(request) {
|
|
7
|
-
return request.authContext.agentCode
|
|
9
|
+
return request.authContext.agentCode;
|
|
8
10
|
}
|
|
9
11
|
function formatContent(result) {
|
|
10
12
|
const text = result?.content?.[0]?.text;
|
|
@@ -14,9 +16,14 @@ function formatContent(result) {
|
|
|
14
16
|
}
|
|
15
17
|
function mapMcpError(status, body) {
|
|
16
18
|
const eapCode = body?.error?.data?.eapCode;
|
|
17
|
-
const message = body?.error?.message ?? `HTTP ${status}`;
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
const message = (body?.error?.message ?? '').trim() || `HTTP ${status}`;
|
|
20
|
+
// 仅 HTTP 403 视为权限拒绝。eapCode 含 auth 是 hop 认证失败,不是 C2。
|
|
21
|
+
if (status === 403) {
|
|
22
|
+
return {
|
|
23
|
+
error: message === `HTTP ${status}` ? '用户无该 Tool 执行权限' : message,
|
|
24
|
+
errorCode: eapCode ?? 'NO_PERMISSION',
|
|
25
|
+
retryable: false,
|
|
26
|
+
};
|
|
20
27
|
}
|
|
21
28
|
if (status === 429) {
|
|
22
29
|
return { error: message, errorCode: eapCode ?? 'RATE_LIMITED', retryable: true };
|
|
@@ -57,6 +64,12 @@ export class McpToolExecutor {
|
|
|
57
64
|
params: {
|
|
58
65
|
name: request.toolName,
|
|
59
66
|
arguments: request.arguments,
|
|
67
|
+
...(request.toolVersion
|
|
68
|
+
? {
|
|
69
|
+
version: request.toolVersion,
|
|
70
|
+
_meta: { version: request.toolVersion },
|
|
71
|
+
}
|
|
72
|
+
: {}),
|
|
60
73
|
},
|
|
61
74
|
}, request, agentCode, correlationId);
|
|
62
75
|
if (!response.ok) {
|
|
@@ -98,14 +111,15 @@ export class McpToolExecutor {
|
|
|
98
111
|
};
|
|
99
112
|
}
|
|
100
113
|
catch (err) {
|
|
101
|
-
const
|
|
114
|
+
const timeoutSec = Math.max(1, Math.round(this.timeout / 1000));
|
|
115
|
+
const aborted = isAbortError(err);
|
|
102
116
|
return {
|
|
103
117
|
toolCallId: request.toolCallId,
|
|
104
118
|
toolName: request.toolName,
|
|
105
119
|
content: '',
|
|
106
120
|
success: false,
|
|
107
|
-
error:
|
|
108
|
-
errorCode: 'EXECUTION_FAILED',
|
|
121
|
+
error: abortOrTimeoutMessage(err, timeoutSec),
|
|
122
|
+
errorCode: aborted ? ErrorCodes.TOOL_TIMEOUT : 'EXECUTION_FAILED',
|
|
109
123
|
retryable: false,
|
|
110
124
|
runtime: 'server',
|
|
111
125
|
};
|
|
@@ -140,6 +154,8 @@ export class McpToolExecutor {
|
|
|
140
154
|
method: 'POST',
|
|
141
155
|
headers: {
|
|
142
156
|
'Content-Type': 'application/json',
|
|
157
|
+
// Streamable HTTP:Gateway /mcp 要求同时接受 JSON 与 SSE
|
|
158
|
+
Accept: 'application/json, text/event-stream',
|
|
143
159
|
Authorization: `Bearer ${request.authContext.delegationToken}`,
|
|
144
160
|
'X-Agent-Code': agentCode,
|
|
145
161
|
'X-Agent-Id': agentCode,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 喂给模型的 SKILL.md 正文展开(不改磁盘)。
|
|
3
|
+
* 对标 Claude `${CLAUDE_SKILL_DIR}`:作者仍写包内相对路径。
|
|
4
|
+
* 官方规范不要求脚本必须在 `scripts/`;EAP_SKILL_DIR 是整包绝对根。
|
|
5
|
+
* 展开成绝对路径,避免 Windows cmd 不认 `$VAR`、多技能时 env 未注入。
|
|
6
|
+
*/
|
|
7
|
+
export declare const EAP_SKILL_DIR_ENV = "EAP_SKILL_DIR";
|
|
8
|
+
export interface ExpandSkillRefsOptions {
|
|
9
|
+
platform?: string;
|
|
10
|
+
exists?: (absPath: string) => boolean;
|
|
11
|
+
}
|
|
12
|
+
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;
|
|
20
|
+
export declare function isSkillEntryFile(filePath: string): boolean;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 喂给模型的 SKILL.md 正文展开(不改磁盘)。
|
|
3
|
+
* 对标 Claude `${CLAUDE_SKILL_DIR}`:作者仍写包内相对路径。
|
|
4
|
+
* 官方规范不要求脚本必须在 `scripts/`;EAP_SKILL_DIR 是整包绝对根。
|
|
5
|
+
* 展开成绝对路径,避免 Windows cmd 不认 `$VAR`、多技能时 env 未注入。
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
export const EAP_SKILL_DIR_ENV = 'EAP_SKILL_DIR';
|
|
10
|
+
/** 看起来像可执行/源码的扩展名。不是目录白名单。 */
|
|
11
|
+
const CODE_EXT = 'py|js|mjs|cjs|ts|tsx|jsx|sh|bash|zsh|ps1|bat|cmd|exe|rb|pl|php';
|
|
12
|
+
const RELATIVE_CODE_PATH = new RegExp(`(^|[^$\\w./\\\\:%])(\\.\\/)?((?:[\\w.-]+[\\\\/])*[\\w.-]+\\.(?:${CODE_EXT}))\\b`, 'gi');
|
|
13
|
+
export function normalizeSkillDir(skillDir) {
|
|
14
|
+
return skillDir.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
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
|
+
function defaultExists(absPath) {
|
|
25
|
+
try {
|
|
26
|
+
return existsSync(absPath);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 包内相对脚本路径 → 技能目录绝对路径。
|
|
34
|
+
* 带目录或 `./` 的(`bin/foo.py`、`./main.py`、`scripts/a.py`)一律展开。
|
|
35
|
+
* 根上的裸文件名(`main.py`)仅当包内确实有该文件才展开,以免改写「写到用户目录再跑」的例子。
|
|
36
|
+
*/
|
|
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;
|
|
41
|
+
const dir = normalizeSkillDir(skillDir);
|
|
42
|
+
const expanded = body.replace(RELATIVE_CODE_PATH, (full, prefix, dotSlash, rel) => {
|
|
43
|
+
const relNorm = rel.replace(/\\/g, '/');
|
|
44
|
+
const packageRelative = Boolean(dotSlash) || relNorm.includes('/');
|
|
45
|
+
if (!packageRelative && !exists(path.join(skillDir, relNorm))) {
|
|
46
|
+
return full;
|
|
47
|
+
}
|
|
48
|
+
return `${prefix}${dir}/${relNorm}`;
|
|
49
|
+
});
|
|
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`;
|
|
56
|
+
if (body.includes(`${EAP_SKILL_DIR_ENV}=`))
|
|
57
|
+
return expanded;
|
|
58
|
+
return note + expanded;
|
|
59
|
+
}
|
|
60
|
+
export function isSkillEntryFile(filePath) {
|
|
61
|
+
return /(^|[\\/])(SKILL\.md|skill\.md)$/i.test(filePath);
|
|
62
|
+
}
|
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
* [平台规则常量] ← 代码内置,安全约束 + 行为准则(最高优先级)
|
|
9
9
|
* [当前时间] ← 运行时动态注入(自然周等时间规则的判定基准)
|
|
10
10
|
* [Agent 身份] ← agentName / agentCode
|
|
11
|
+
* [可用工具短目录] ← 名称 + 一句话用途;完整 schema 仍走 API tools
|
|
11
12
|
* [开发者提示词] ← Graph LLM 节点 config.systemPrompt,平台透传
|
|
12
13
|
* (Registry 无内嵌 Graph 的兜底路径读 agent.manifest.json prompts.system)
|
|
13
14
|
* ```
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
+
* 工具 schema 走 API 的 tools 参数(见 loadedToolsToApiTools);system 短目录给模型发现用,不是对用户报菜名。
|
|
16
17
|
*
|
|
17
18
|
* @see docs/status/module-status-and-backlog.md RT-5
|
|
18
19
|
*/
|
|
@@ -28,38 +29,34 @@ export interface LoadedSkillInfo {
|
|
|
28
29
|
name: string;
|
|
29
30
|
description?: string;
|
|
30
31
|
version?: string;
|
|
32
|
+
/** Skill 包目录(含 SKILL.md) */
|
|
33
|
+
bundlePath?: string;
|
|
34
|
+
/** SKILL.md 绝对路径 */
|
|
35
|
+
entryFile?: string;
|
|
31
36
|
/** Skill 工作流(GraphDSL);缺省时子图节点须自带 subgraphDsl */
|
|
32
37
|
workflow?: import('../graph/contracts/executable-graph.js').GraphDSL;
|
|
33
|
-
/**
|
|
38
|
+
/** 子图入口可选上下文;Harness 主路径不灌 system */
|
|
34
39
|
prompt?: string;
|
|
35
|
-
|
|
36
|
-
toolId?: string;
|
|
40
|
+
toolsets?: Array<string | {
|
|
37
41
|
name?: string;
|
|
38
42
|
version?: string;
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
code?: string;
|
|
44
|
+
enabled?: string[];
|
|
41
45
|
autoApprove?: string[];
|
|
42
46
|
}>;
|
|
43
47
|
inputDef?: unknown;
|
|
44
48
|
outputDef?: unknown;
|
|
45
|
-
/** Skill 包 scripts[] 物化出的虚拟 Tool(装配时并入 loadedTools) */
|
|
46
|
-
scriptTools?: import('./loaded-tool-definition.js').LoadedToolDefinition[];
|
|
47
49
|
}
|
|
48
50
|
/** skillId → 完整定义 */
|
|
49
51
|
export type LoadedSkillMap = Record<string, LoadedSkillInfo>;
|
|
50
52
|
export declare function toLoadedSkillMap(skills: LoadedSkillInfo[] | undefined): LoadedSkillMap | undefined;
|
|
51
53
|
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
*
|
|
58
|
-
* - 无 toolRefs / 空数组:不收窄(保持 Agent 全量,兼容旧 Skill)
|
|
59
|
-
* - string / name / toolId:精确匹配工具名
|
|
60
|
-
* - toolset + enabledTools:仅白名单子工具
|
|
61
|
-
* - toolset 无 enabledTools:匹配 toolId 前缀 `<toolset>:` 或 id 等于 toolset
|
|
62
|
-
* - 始终保留该 Skill 的 scriptTools
|
|
54
|
+
* 按 Skill.toolsets 收窄工具集(子图用)。
|
|
55
|
+
* - 无 toolsets / 空数组:不收窄
|
|
56
|
+
* - string / name:精确匹配工具名
|
|
57
|
+
* - code + enabled:仅白名单子工具
|
|
58
|
+
* - code 无 enabled:匹配 toolId 前缀 `<code>:` 或 id 等于 code
|
|
59
|
+
* - 始终保留 Harness 原语
|
|
63
60
|
*/
|
|
64
61
|
export declare function filterLoadedToolsForSkill(loadedTools: LoadedToolMap | undefined, skill: LoadedSkillInfo | undefined): LoadedToolMap | undefined;
|
|
65
62
|
/**
|
|
@@ -78,22 +75,59 @@ export interface PromptBuildContext {
|
|
|
78
75
|
agentCode?: string;
|
|
79
76
|
/** 当前调用用户标识(可选,供「我 / 我的」类指代) */
|
|
80
77
|
userId?: string | null;
|
|
78
|
+
/** 当前用户目录(`users/<userId>`,含用户 skills) */
|
|
79
|
+
workingDirectory?: string;
|
|
80
|
+
os?: {
|
|
81
|
+
platform: string;
|
|
82
|
+
release: string;
|
|
83
|
+
};
|
|
84
|
+
/** 允许 read_file 的根目录(写根 + 已装配 tools/skills) */
|
|
85
|
+
jailRoots?: string[];
|
|
86
|
+
skillDirs?: Array<{
|
|
87
|
+
skillId: string;
|
|
88
|
+
bundlePath: string;
|
|
89
|
+
entryFile?: string;
|
|
90
|
+
}>;
|
|
91
|
+
/** 已装配 Skill 短目录;单独成段,不并进开发者提示词 */
|
|
92
|
+
skillCatalog?: string;
|
|
93
|
+
/** 已装配工具短目录(名称 + 用途);schema 仍走 API tools */
|
|
94
|
+
toolCatalog?: string;
|
|
95
|
+
}
|
|
96
|
+
export interface PromptSection {
|
|
97
|
+
id: string;
|
|
98
|
+
title: string;
|
|
99
|
+
body: string;
|
|
100
|
+
}
|
|
101
|
+
export interface AssembledSystemPrompt {
|
|
102
|
+
text: string;
|
|
103
|
+
sections: PromptSection[];
|
|
81
104
|
}
|
|
82
105
|
/**
|
|
83
106
|
* SystemPromptBuilder — 系统提示词拼接
|
|
84
107
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
108
|
+
* 具名段拼接(无模板引擎)。顺序固定,段标题用于排查。
|
|
109
|
+
* 工具短目录进 system;完整 schema 走 API tools 参数。
|
|
110
|
+
* 开发者提示词整段透传,不做 {{ }} 替换。
|
|
88
111
|
*/
|
|
89
112
|
export declare class SystemPromptBuilder {
|
|
90
113
|
/**
|
|
91
|
-
*
|
|
114
|
+
* 构建完整系统提示词(含具名段,便于对照落盘)。
|
|
92
115
|
*/
|
|
93
|
-
build(context: PromptBuildContext):
|
|
94
|
-
private
|
|
116
|
+
build(context: PromptBuildContext): AssembledSystemPrompt;
|
|
117
|
+
private buildEnvironment;
|
|
95
118
|
private buildAgentIdentity;
|
|
119
|
+
private buildJail;
|
|
96
120
|
}
|
|
121
|
+
/** 自然周(周一为一周第一天)及本月/上月窗口,供时间类推理。 */
|
|
122
|
+
export declare function naturalTimeWindows(now?: Date): {
|
|
123
|
+
now: string;
|
|
124
|
+
weekday: string;
|
|
125
|
+
thisWeek: string;
|
|
126
|
+
lastWeek: string;
|
|
127
|
+
lastWeekend: string;
|
|
128
|
+
thisMonth: string;
|
|
129
|
+
lastMonth: string;
|
|
130
|
+
};
|
|
97
131
|
/**
|
|
98
132
|
* OpenAI function-calling tool definition(API tools 参数格式)
|
|
99
133
|
*
|
|
@@ -111,4 +145,6 @@ export type ApiToolDefinition = LLMToolDefinition;
|
|
|
111
145
|
* @param skillTools - 可选,当前 Skill 所需的工具名称列表(过滤子集)
|
|
112
146
|
* @returns OpenAI API tools 参数数组
|
|
113
147
|
*/
|
|
148
|
+
/** 名称 + 一句话用途,供 system「可用工具」段。schema 仍走 loadedToolsToApiTools。 */
|
|
149
|
+
export declare function buildToolCatalog(loadedTools: LoadedToolMap | undefined): string | undefined;
|
|
114
150
|
export declare function loadedToolsToApiTools(loadedTools: LoadedToolMap | undefined, skillTools?: string[]): ApiToolDefinition[];
|