@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.
Files changed (130) hide show
  1. package/README.md +15 -20
  2. package/dist/api/handlers/invoke.handler.js +7 -3
  3. package/dist/api/handlers/messages.handler.js +0 -1
  4. package/dist/api/handlers/stream.handler.js +8 -4
  5. package/dist/api/routes/agent.routes.d.ts +2 -2
  6. package/dist/api/routes/agent.routes.js +2 -2
  7. package/dist/checkpoint/checkpoint-ref-notify.d.ts +10 -0
  8. package/dist/checkpoint/checkpoint-ref-notify.js +59 -0
  9. package/dist/checkpoint/index.d.ts +1 -0
  10. package/dist/checkpoint/index.js +1 -0
  11. package/dist/checkpoint/pg-checkpoint-saver.js +25 -2
  12. package/dist/common/agent-code.d.ts +2 -4
  13. package/dist/common/agent-code.js +4 -5
  14. package/dist/common/types.d.ts +5 -5
  15. package/dist/common/types.js +2 -2
  16. package/dist/contracts/permission-checker.d.ts +1 -1
  17. package/dist/contracts/trace-collector.d.ts +16 -2
  18. package/dist/debug/llm-turn-log.d.ts +100 -0
  19. package/dist/debug/llm-turn-log.js +130 -0
  20. package/dist/debug/pg-llm-turn-log.d.ts +22 -0
  21. package/dist/debug/pg-llm-turn-log.js +57 -0
  22. package/dist/governance/governance-client.d.ts +1 -1
  23. package/dist/governance/governance-client.js +40 -16
  24. package/dist/governance/index.d.ts +2 -2
  25. package/dist/governance/index.js +1 -1
  26. package/dist/governance/permission-guard.d.ts +2 -1
  27. package/dist/governance/permission-guard.js +13 -4
  28. package/dist/graph/adapters/langgraph-checkpointer.js +3 -1
  29. package/dist/graph/adapters/langgraph.adapter.d.ts +1 -0
  30. package/dist/graph/adapters/langgraph.adapter.js +153 -92
  31. package/dist/graph/adapters/skill-read.d.ts +12 -0
  32. package/dist/graph/adapters/skill-read.js +63 -0
  33. package/dist/graph/adapters/subgraph-compiler.js +5 -0
  34. package/dist/graph/contracts/executable-graph.d.ts +11 -9
  35. package/dist/graph/contracts/executable-graph.js +1 -1
  36. package/dist/graph/hitl/hitl-handler.d.ts +10 -0
  37. package/dist/graph/hitl/hitl-handler.js +50 -6
  38. package/dist/graph/nodes/llm-node.d.ts +14 -0
  39. package/dist/graph/nodes/skill-bind-node.js +1 -2
  40. package/dist/graph/nodes/tool-node.d.ts +2 -0
  41. package/dist/graph/nodes/tool-node.js +50 -10
  42. package/dist/graph/trace-span.d.ts +22 -0
  43. package/dist/graph/trace-span.js +69 -0
  44. package/dist/index.d.ts +5 -2
  45. package/dist/index.js +3 -1
  46. package/dist/memory/compression/compression.repository.d.ts +1 -15
  47. package/dist/providers/echo-llm-provider.d.ts +1 -1
  48. package/dist/providers/echo-llm-provider.js +4 -3
  49. package/dist/providers/http-llm-provider.d.ts +4 -0
  50. package/dist/providers/http-llm-provider.js +129 -4
  51. package/dist/providers/local-llm-provider.d.ts +5 -1
  52. package/dist/providers/local-llm-provider.js +14 -6
  53. package/dist/providers/routing-llm-provider.d.ts +4 -0
  54. package/dist/providers/routing-llm-provider.js +12 -2
  55. package/dist/registry/agent-models.d.ts +1 -1
  56. package/dist/registry/agent-models.js +1 -1
  57. package/dist/registry/agent-registry-client.d.ts +3 -3
  58. package/dist/registry/deployment-snapshot.d.ts +17 -5
  59. package/dist/registry/deployment-snapshot.js +97 -49
  60. package/dist/registry/graph-loader.d.ts +2 -2
  61. package/dist/registry/graph-loader.js +43 -80
  62. package/dist/registry/index.d.ts +2 -2
  63. package/dist/registry/index.js +2 -2
  64. package/dist/registry/preload-errors.d.ts +1 -1
  65. package/dist/registry/preload-errors.js +1 -1
  66. package/dist/registry/skill-io-guard.js +1 -1
  67. package/dist/registry/skill-tree.d.ts +3 -0
  68. package/dist/registry/skill-tree.js +41 -3
  69. package/dist/runtime/agent-runtime.d.ts +42 -3
  70. package/dist/runtime/agent-runtime.js +129 -39
  71. package/dist/runtime/dev-runtime.d.ts +7 -4
  72. package/dist/runtime/dev-runtime.js +24 -21
  73. package/dist/runtime/index.d.ts +3 -3
  74. package/dist/runtime/index.js +1 -1
  75. package/dist/runtime/storage-profile.d.ts +19 -10
  76. package/dist/runtime/storage-profile.js +42 -29
  77. package/dist/runtime/types.d.ts +17 -19
  78. package/dist/server.js +2 -2
  79. package/dist/session/session.service.d.ts +5 -0
  80. package/dist/session/session.service.js +12 -0
  81. package/dist/sse/events/index.d.ts +5 -3
  82. package/dist/sse/events/index.js +2 -0
  83. package/dist/sse/events/skill-events.d.ts +12 -1
  84. package/dist/sse/events/skill-events.js +1 -1
  85. package/dist/sse/trace-collector.d.ts +10 -0
  86. package/dist/sse/trace-collector.js +1 -0
  87. package/dist/token/token-budget.service.d.ts +2 -2
  88. package/dist/token/token-budget.service.js +1 -1
  89. package/dist/tool/create-tool-executor.d.ts +1 -1
  90. package/dist/tool/create-tool-executor.js +3 -2
  91. package/dist/tool/dev-http-executor.d.ts +1 -1
  92. package/dist/tool/dev-http-executor.js +10 -7
  93. package/dist/tool/dev-mcp-executor.js +5 -3
  94. package/dist/tool/dev-tool-executor.d.ts +1 -1
  95. package/dist/tool/dev-tool-executor.js +2 -2
  96. package/dist/tool/harness-command.d.ts +32 -0
  97. package/dist/tool/harness-command.js +276 -0
  98. package/dist/tool/harness-primitives.d.ts +54 -0
  99. package/dist/tool/harness-primitives.js +492 -0
  100. package/dist/tool/http-tool-executor.js +16 -21
  101. package/dist/tool/index.d.ts +4 -2
  102. package/dist/tool/index.js +4 -2
  103. package/dist/tool/local-mcp-client.d.ts +1 -1
  104. package/dist/tool/local-mcp-client.js +54 -23
  105. package/dist/tool/local-tool-executor.d.ts +4 -4
  106. package/dist/tool/local-tool-executor.js +18 -16
  107. package/dist/tool/mcp-tool-executor.js +23 -7
  108. package/dist/tool/skill-script-refs.d.ts +20 -0
  109. package/dist/tool/skill-script-refs.js +62 -0
  110. package/dist/tool/system-prompt-builder.d.ts +61 -25
  111. package/dist/tool/system-prompt-builder.js +198 -92
  112. package/dist/tool/timeout.d.ts +14 -0
  113. package/dist/tool/timeout.js +36 -0
  114. package/dist/tool/tool-confirmation.js +2 -1
  115. package/dist/tool/tool-execution-router.d.ts +1 -1
  116. package/dist/tool/tool-execution-router.js +2 -2
  117. package/dist/tool/tool-executor.interface.d.ts +23 -7
  118. package/dist/tool/tool-orchestrator.d.ts +1 -1
  119. package/dist/tool/tool-orchestrator.js +6 -3
  120. package/dist/utils/format-error.d.ts +2 -2
  121. package/dist/utils/format-error.js +2 -2
  122. package/dist/utils/http-error.d.ts +2 -2
  123. package/dist/utils/http-error.js +2 -2
  124. package/package.json +3 -3
  125. package/dist/common/concurrency.d.ts +0 -79
  126. package/dist/common/concurrency.js +0 -121
  127. package/dist/common/queue-consumer.d.ts +0 -125
  128. package/dist/common/queue-consumer.js +0 -159
  129. package/dist/tool/mock-tool-executor.d.ts +0 -28
  130. 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
- reject(new Error(`MCP RPC timeout: ${method}`));
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 ?? 30_000;
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 timeoutMs = execution.timeout ?? 30_000;
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 response = await fetch(url, {
172
- method: 'POST',
173
- headers,
174
- body: JSON.stringify(body),
175
- signal: controller.signal,
176
- });
177
- if (!response.ok) {
178
- throw new Error(`MCP HTTP ${response.status}: ${response.statusText}`);
179
- }
180
- const raw = await response.text();
181
- const payload = parseMcpHttpBody(raw);
182
- if (payload.error) {
183
- throw new Error(payload.error.message ?? 'MCP HTTP error');
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
- return toInvokeResult(payload.result, execution.server.env);
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
- * localPreferred / skill-script(或可解析的 code bundlePath+handler),则直接本机执行。
5
+ * 或 request.execution 带可解析的 code bundlePath+handler,则直接本机执行。
6
6
  */
7
- import type { ILocalToolInstallStore } from '@gt-fe/eap-contracts';
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
- /** 是否为应本机执行的 Skill script / 显式 localPreferred code execution */
22
+ /** 是否为应本机执行的 code(显式 localPreferred,例如 DevToolExecutor) */
23
23
  export declare function isLocalPreferredExecution(raw: unknown): boolean;
24
24
  /**
25
- * 从 ToolCallRequest.execution 解析本机 code 执行规格(Skill scripts 等)。
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
- * localPreferred / skill-script(或可解析的 code bundlePath+handler),则直接本机执行。
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 '@gt-fe/eap-contracts';
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
- finish(() => reject(new Error(`Local tool timeout after ${timeoutMs}ms`)));
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
- /** 是否为应本机执行的 Skill script / 显式 localPreferred code execution */
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
- if (e.localPreferred === true || e.source === 'skill-script')
99
- return true;
100
- return false;
101
+ return e.localPreferred === true;
101
102
  }
102
103
  /**
103
- * 从 ToolCallRequest.execution 解析本机 code 执行规格(Skill scripts 等)。
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
- // 无本地安装:仅 Skill scripts / localPreferred 走 request.execution
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 message = error instanceof Error ? error.message : String(error);
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 message = error instanceof Error ? error.message : String(error);
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 ?? request.authContext.agentId;
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
- if (status === 403 || eapCode?.includes('forbidden') || eapCode?.includes('auth')) {
19
- return { error: message, errorCode: eapCode ?? 'NO_PERMISSION', retryable: false };
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 message = err instanceof Error ? err.message : String(err);
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: message,
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
- * Tools 不经过本类,走 API 的 tools 参数(见 loadedToolsToApiTools)。
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
- /** 注入子图入口 LLM 系统上下文 */
38
+ /** 子图入口可选上下文;Harness 主路径不灌 system */
34
39
  prompt?: string;
35
- toolRefs?: Array<string | {
36
- toolId?: string;
40
+ toolsets?: Array<string | {
37
41
  name?: string;
38
42
  version?: string;
39
- toolset?: string;
40
- enabledTools?: string[];
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
- * 将各 Skill 的 scripts 虚拟 Tool 并入 loadedTools(同名不覆盖平台 Tool)。
53
- * 装配点(invoke/stream/fork)在构造 GraphState 前调用。
54
- */
55
- export declare function mergeSkillScriptTools(loadedTools: LoadedToolMap | undefined, skills: LoadedSkillInfo[] | undefined): LoadedToolMap | undefined;
56
- /**
57
- * 按 Skill.toolRefs 收窄工具集。
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
- * 四段式拼接:平台规则 + 当前时间 + Agent 身份 + 开发者提示词。
86
- * 不注入工具列表(工具走 API tools 参数)。
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): string;
94
- private buildCurrentTime;
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[];