@gt-fe/eap-runtime 0.0.13 → 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 (114) 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 -66
  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
  113. package/dist/registry/mcp-stdio-cwd.d.ts +0 -5
  114. package/dist/registry/mcp-stdio-cwd.js +0 -13
@@ -8,11 +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 { resolveStdioMcpCwd } from './mcp-stdio-cwd.js';
11
+ import { isMcpDiscoverStub } from '../tool/mcp-list.js';
12
12
  import { createDeploymentSnapshotFromEnv, isInlineToolDeclaration, overlayToolRefPolicies, } from './deployment-snapshot.js';
13
13
  import { preloadGraphNotFound, preloadManifestNotFound, preloadSkillNotFound, preloadToolNotFound, } from './preload-errors.js';
14
14
  import { PreinstallDefinitionCache, resolveDefinitionCacheEnabled, } from './definition-cache.js';
15
15
  import { inspectAgentSkillTree, listSkillDirs, readSkillAssembly, resolveSkillHotReload, skillAssemblyStamp, } from './skill-tree.js';
16
+ import { emitRuntimeLog } from '../common/logger.js';
16
17
  function applyDefaultModelToGraph(graph, defaultModel) {
17
18
  if (!defaultModel)
18
19
  return graph;
@@ -57,6 +58,24 @@ function asGraphDsl(value) {
57
58
  }
58
59
  return undefined;
59
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
+ }
60
79
  function extractSkillPrompt(manifest) {
61
80
  const prompt = manifest.prompt;
62
81
  if (typeof prompt === 'string' && prompt.trim())
@@ -96,35 +115,6 @@ async function resolveSkillPrompt(manifest, bundlePath) {
96
115
  }
97
116
  return raw;
98
117
  }
99
- function extractSkillToolsets(manifest) {
100
- const refs = manifest.toolsets;
101
- if (!Array.isArray(refs))
102
- return undefined;
103
- const out = [];
104
- for (const item of refs) {
105
- if (typeof item === 'string') {
106
- out.push(item);
107
- continue;
108
- }
109
- if (!item || typeof item !== 'object')
110
- continue;
111
- const o = item;
112
- const code = o.code != null ? String(o.code) : undefined;
113
- const enabled = Array.isArray(o.enabled)
114
- ? o.enabled.filter((x) => typeof x === 'string')
115
- : undefined;
116
- out.push({
117
- name: o.name != null ? String(o.name) : code,
118
- version: o.version != null ? String(o.version) : undefined,
119
- code,
120
- enabled,
121
- autoApprove: Array.isArray(o.autoApprove)
122
- ? o.autoApprove.filter((x) => typeof x === 'string')
123
- : undefined,
124
- });
125
- }
126
- return out.length > 0 ? out : undefined;
127
- }
128
118
  function extractEmbeddedGraph(manifest) {
129
119
  const candidates = [
130
120
  manifest.graph,
@@ -224,6 +214,67 @@ function toLoadedToolDefinition(record) {
224
214
  inputSchema: m.input,
225
215
  };
226
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
+ }
227
278
  function fromPreinstallTool(pre) {
228
279
  const m = pre.manifest;
229
280
  const permissions = m.permissions;
@@ -255,14 +306,16 @@ function fromPreinstallTool(pre) {
255
306
  };
256
307
  }
257
308
  /**
258
- * 展开预装工具集(toolset.manifest.json,含 tools[])为多个 LoadedToolDefinition。
259
- * 以工具集为管理单元:单个 MCP 服务 / 单个代码包 = 一个工具集,可含多个子工具;
260
- * 子工具继承集级 server/package 配置,合成各自的 execution。
261
- * 无 tools[] 时回退为单工具 manifest(tool.manifest.json 结构)。
309
+ * 展开预装工具集为 LoadedToolDefinition。
310
+ * - type=mcp:忽略清单 tools[],产出一条 discover stub;子工具在执行期 tools/list。
311
+ * - type=code/http:展开 tools[];无 tools[] 时回退单工具 manifest。
262
312
  * enabled 为可选白名单(agent.manifest toolsets[].enabled);缺省启用全部子工具。
263
313
  */
264
314
  function expandPreinstallToolset(pre, options) {
265
315
  const m = pre.manifest;
316
+ if (m.type === 'mcp') {
317
+ return [mcpDiscoverStub(pre, options)];
318
+ }
266
319
  const tools = m.tools;
267
320
  const enabled = options?.enabledTools;
268
321
  const enabledSet = enabled && enabled.length > 0 ? new Set(enabled) : null;
@@ -281,7 +334,6 @@ function expandPreinstallToolset(pre, options) {
281
334
  }
282
335
  const setType = m.type;
283
336
  const toolRuntime = (m.toolRuntime ?? m.runtime);
284
- const server = m.server;
285
337
  const pkg = m.package;
286
338
  const out = [];
287
339
  for (const raw of tools) {
@@ -302,28 +354,10 @@ function expandPreinstallToolset(pre, options) {
302
354
  baseUrl: st.baseUrl ?? gateway.baseUrl,
303
355
  pathTemplate: st.pathTemplate,
304
356
  allowedHosts: gateway.allowedHosts,
357
+ header: gateway.header,
305
358
  timeout: st.timeout ?? gateway.timeout ?? DEFAULT_HTTP_TOOL_TIMEOUT_SEC,
306
359
  };
307
360
  }
308
- else if (setType === 'mcp') {
309
- type = 'mcp';
310
- const transport = server?.transport ?? (server?.url ? 'streamable_http' : 'stdio');
311
- const resolvedServer = server
312
- ? {
313
- ...server,
314
- ...(transport === 'stdio'
315
- ? { cwd: resolveStdioMcpCwd(pre.bundlePath, server.cwd) }
316
- : {}),
317
- }
318
- : server;
319
- execution = {
320
- protocol: 'mcp',
321
- transport,
322
- server: resolvedServer,
323
- toolName: name,
324
- timeout: st.timeout ?? server?.timeout ?? DEFAULT_TOOL_TIMEOUT_SEC,
325
- };
326
- }
327
361
  else {
328
362
  type = 'code';
329
363
  execution = {
@@ -522,28 +556,24 @@ export class GraphLoader {
522
556
  catch (err) {
523
557
  const message = err instanceof Error ? err.message : String(err);
524
558
  skillWarnings.push(message);
525
- console.warn(JSON.stringify({
559
+ emitRuntimeLog('warn', 'graph-loader', message, {
526
560
  event: 'runtime.warmAgent.skills_failed',
527
- level: 'warn',
528
561
  agentCode,
529
562
  agentVersion: version ?? null,
530
- message,
531
- }));
563
+ });
532
564
  }
533
565
  // 预期跳过(无 userId 的 users 层 Skill)用 debug,避免单测/生产噪音
534
566
  if (skippedSkills.length > 0) {
535
- console.debug(JSON.stringify({
567
+ emitRuntimeLog('debug', 'graph-loader', 'warmAgent skipped user skills', {
536
568
  event: 'runtime.warmAgent.skills_skipped',
537
- level: 'debug',
538
569
  agentCode,
539
570
  agentVersion: version ?? null,
540
571
  userId: options?.userId ?? null,
541
572
  skippedSkillIds: skippedSkills,
542
- }));
573
+ });
543
574
  }
544
- console.info(JSON.stringify({
575
+ emitRuntimeLog('info', 'graph-loader', 'warmAgent ok', {
545
576
  event: 'runtime.warmAgent.ok',
546
- level: 'info',
547
577
  agentCode,
548
578
  agentVersion: version ?? null,
549
579
  graph: true,
@@ -551,7 +581,7 @@ export class GraphLoader {
551
581
  skills: skillsOk,
552
582
  skillWarningCount: skillWarnings.length,
553
583
  skillWarnings,
554
- }));
584
+ });
555
585
  return {
556
586
  graph: true,
557
587
  tools: true,
@@ -632,7 +662,6 @@ export class GraphLoader {
632
662
  type: 'llm',
633
663
  config: {
634
664
  model,
635
- systemPrompt: manifest.prompts?.system,
636
665
  },
637
666
  },
638
667
  ],
@@ -642,6 +671,24 @@ export class GraphLoader {
642
671
  },
643
672
  };
644
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
+ }
645
692
  /**
646
693
  * 解析 agent tools[] + graph tool 节点 union;
647
694
  * server+预装:以预装 manifest 为准;desktop 合并 ILocalToolInstallStore。
@@ -753,6 +800,9 @@ export class GraphLoader {
753
800
  continue;
754
801
  const pre = await this.deploymentSnapshot.loadToolManifest({ name: toolName }, agentDir);
755
802
  if (!pre) {
803
+ // MCP 子工具名在 tools/list 后才出现,图内 type:tool 节点不必对应预装目录
804
+ if (definitions.some(isMcpDiscoverStub))
805
+ continue;
756
806
  missing.push(toolName);
757
807
  continue;
758
808
  }
@@ -861,7 +911,6 @@ export class GraphLoader {
861
911
  prompt,
862
912
  bundlePath: pre.bundlePath,
863
913
  entryFile,
864
- toolsets: extractSkillToolsets(pre.manifest),
865
914
  inputDef: pre.manifest.inputDef,
866
915
  outputDef: pre.manifest.outputDef,
867
916
  });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 预装加载错误 — 对齐 runtime.preload.* / 联调契约
3
3
  */
4
- import { AppError, type ErrorCode } from '@gt-fe/eap-contracts';
4
+ import { AppError, type ErrorCode } from '@eap/contracts';
5
5
  export declare function preloadError(code: ErrorCode, message: string, details?: unknown): AppError;
6
6
  export declare function preloadMissing(message: string, details?: unknown): AppError;
7
7
  export declare function preloadManifestNotFound(message: string, details?: unknown): AppError;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 预装加载错误 — 对齐 runtime.preload.* / 联调契约
3
3
  */
4
- import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
4
+ import { AppError, ErrorCodes } from '@eap/contracts';
5
5
  export function preloadError(code, message, details) {
6
6
  return new AppError(code, message, {
7
7
  exposeMessage: true,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Skill inputDef / outputDef 轻量校验(默认开启;EAP_PRELOAD_VALIDATE_SKILL_IO=0 关闭)
3
3
  */
4
- import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
4
+ import { AppError, ErrorCodes } from '@eap/contracts';
5
5
  import { isValidateSkillIo } from './preload-flags.js';
6
6
  function asRecord(value) {
7
7
  if (value && typeof value === 'object' && !Array.isArray(value)) {
@@ -10,7 +10,7 @@ import { readdir, stat, access, readFile } from 'node:fs/promises';
10
10
  import { watch } from 'node:fs';
11
11
  import { constants as fsConstants } from 'node:fs';
12
12
  import path from 'node:path';
13
- import { SKILL_ENTRY_CANDIDATES } from '@gt-fe/eap-contracts';
13
+ import { SKILL_ENTRY_CANDIDATES } from '@eap/contracts';
14
14
  const SKILL_CODE = /^[a-z0-9][a-z0-9-]*$/;
15
15
  async function exists(p) {
16
16
  try {
@@ -11,7 +11,7 @@ import type { ICheckpointSaver } from '../checkpoint/contracts/checkpoint-saver.
11
11
  import type { ITraceCollector } from '../contracts/trace-collector.js';
12
12
  import type { IStateGraphEngine } from '../graph/contracts/state-graph.contract.js';
13
13
  import { HitlHandler } from '../graph/hitl/hitl-handler.js';
14
- import type { GraphLoader } from '../registry/graph-loader.js';
14
+ import { type GraphLoader } from '../registry/graph-loader.js';
15
15
  import type { SessionContextService } from '../session/session-context.service.js';
16
16
  import type { MessageService } from '../session/message.service.js';
17
17
  import type { SessionService, SessionRecord, MessageRecord } from '../session/session.service.js';
@@ -86,8 +86,8 @@ export declare class AgentRuntime {
86
86
  * “Session 历史 + 当前输入”途径。
87
87
  *
88
88
  * 行为:
89
- * - `role` 仅接受 `user` / `assistant`;`system` 应写入 graph.json LLM 节点的
90
- * `config.systemPrompt`(由 SystemPromptBuilder 拼平台规则 + 开发者提示词);
89
+ * - `role` 仅接受 `user` / `assistant`;`system` 由 SystemPromptBuilder 拼
90
+ * (平台规则 + `prompts.system` 基线 + 节点 `systemPrompt`);
91
91
  * `tool` 必须与前置 assistant.toolCalls 严格配对,不得单独作为种子。
92
92
  * - 幂等语义(exactly-once):当 session 已存在任何历史消息时,直接返回
93
93
  * `seeded: false`,不重复写入。
@@ -8,11 +8,12 @@
8
8
  */
9
9
  import { randomUUID } from 'node:crypto';
10
10
  import { HITL_TIMEOUT_ERROR_CODE, HITL_TIMEOUT_MESSAGE, HitlAlreadyTimedOutError, HitlNotWaitingError, buildHitlWaitedPayload, isHitlDeadlinePassed, readPersistedHitlWaiting, unwrapHitlPaused, } from '../graph/hitl/hitl-handler.js';
11
+ import { resolveAgentSystemPromptFromLoader } from '../registry/graph-loader.js';
11
12
  import { toLoadedSkillMap } from '../tool/system-prompt-builder.js';
12
13
  import { assembleLoadedTools } from '../tool/harness-primitives.js';
13
14
  import { createFileLlmTurnLogFromEnv, } from '../debug/llm-turn-log.js';
14
15
  import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, buildTurnTranscriptFromStreamEvents, extractHitlThreadId, extractStreamReasoning, extractStreamTextDelta, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, } from '../session/persist-turn-messages.js';
15
- import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
16
+ import { AppError, ErrorCodes } from '@eap/contracts';
16
17
  import { resolveExecutionContext } from '../governance/governance-client.js';
17
18
  import { resolveAgentCode } from '../common/agent-code.js';
18
19
  import { resolveTraceId } from '../common/trace-id.js';
@@ -71,8 +72,8 @@ export class AgentRuntime {
71
72
  * “Session 历史 + 当前输入”途径。
72
73
  *
73
74
  * 行为:
74
- * - `role` 仅接受 `user` / `assistant`;`system` 应写入 graph.json LLM 节点的
75
- * `config.systemPrompt`(由 SystemPromptBuilder 拼平台规则 + 开发者提示词);
75
+ * - `role` 仅接受 `user` / `assistant`;`system` 由 SystemPromptBuilder 拼
76
+ * (平台规则 + `prompts.system` 基线 + 节点 `systemPrompt`);
76
77
  * `tool` 必须与前置 assistant.toolCalls 严格配对,不得单独作为种子。
77
78
  * - 幂等语义(exactly-once):当 session 已存在任何历史消息时,直接返回
78
79
  * `seeded: false`,不重复写入。
@@ -100,7 +101,7 @@ export class AgentRuntime {
100
101
  throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}] must be an object`, { exposeMessage: true });
101
102
  }
102
103
  if (message.role !== 'user' && message.role !== 'assistant') {
103
- throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}].role "${String(message.role)}" is not allowed; only 'user' and 'assistant' are accepted (system belongs to config.systemPrompt; tool must pair with prior assistant.toolCalls)`, { exposeMessage: true });
104
+ throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}].role "${String(message.role)}" is not allowed; only 'user' and 'assistant' are accepted (system belongs to SystemPromptBuilder: platform rules + prompts.system + node systemPrompt; tool must pair with prior assistant.toolCalls)`, { exposeMessage: true });
104
105
  }
105
106
  if (typeof message.content !== 'string' || message.content.length === 0) {
106
107
  throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}].content must be a non-empty string`, { exposeMessage: true });
@@ -271,6 +272,7 @@ export class AgentRuntime {
271
272
  const agentDir = this.deps.graphLoader
272
273
  ? (await this.deps.graphLoader.resolveAgentDir(agentCode, params.agentVersion)) ?? undefined
273
274
  : undefined;
275
+ const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.deps.graphLoader, agentCode, params.agentVersion);
274
276
  const history = messageRecordsToGraphMessages(sessionContext.messages);
275
277
  const messages = await buildTurnGraphMessages({
276
278
  history,
@@ -301,6 +303,7 @@ export class AgentRuntime {
301
303
  skillDefinitions,
302
304
  promptRoots,
303
305
  agentDir,
306
+ agentSystemPrompt,
304
307
  skillInput: resolveSkillInputFromInvokeInput(params.input),
305
308
  traceId: resolveTraceId(params),
306
309
  traceCollector: this.deps.traceCollector,
@@ -490,6 +493,9 @@ export class AgentRuntime {
490
493
  (this.deps.graphLoader
491
494
  ? (await this.deps.graphLoader.resolveAgentDir(agentCode, agentVersion)) ?? undefined
492
495
  : undefined),
496
+ agentSystemPrompt: (await resolveAgentSystemPromptFromLoader(this.deps.graphLoader, agentCode, agentVersion)) ?? (typeof resumedState.agentSystemPrompt === 'string'
497
+ ? resumedState.agentSystemPrompt
498
+ : undefined),
493
499
  activatedSkill: resumedState.activatedSkill,
494
500
  skillRoute: resumedState.skillRoute ??
495
501
  (typeof resumedState.activatedSkill === 'string' && resumedState.activatedSkill
@@ -652,11 +658,15 @@ export class AgentRuntime {
652
658
  (this.deps.graphLoader
653
659
  ? (await this.deps.graphLoader.resolveAgentDir(agentCode, agentVersion)) ?? undefined
654
660
  : undefined);
661
+ const agentSystemPrompt = (await resolveAgentSystemPromptFromLoader(this.deps.graphLoader, agentCode, agentVersion)) ?? (typeof forkedCheckpoint.state.agentSystemPrompt === 'string'
662
+ ? forkedCheckpoint.state.agentSystemPrompt
663
+ : undefined);
655
664
  try {
656
665
  const result = await this.deps.graphEngine.invoke(graph, {
657
666
  ...forkedCheckpoint.state,
658
667
  agentVersion,
659
668
  agentDir,
669
+ agentSystemPrompt,
660
670
  userId,
661
671
  delegationToken: overrides.executionContext?.delegationToken,
662
672
  executionContext: overrides.executionContext,
@@ -49,6 +49,8 @@ export interface DevRuntime {
49
49
  dispose: () => void;
50
50
  /** 装配期诊断摘要(CLI 启动时打印,便于排查环境问题) */
51
51
  diagnostics: {
52
+ /** 当前加载的 Runtime 包 version */
53
+ packageVersion: string;
52
54
  deploymentContext: string;
53
55
  toolExecutionMode: string;
54
56
  llmProviderKind: 'routing' | 'openai-http' | 'echo';
@@ -35,6 +35,7 @@ import { InvokeOrchestrator, StreamOrchestrator, SessionService, MessageService,
35
35
  import { createAgentRuntime } from './agent-runtime.js';
36
36
  import { assembleRuntimeStorage } from './storage-profile.js';
37
37
  import { resolveSkillHotReload, watchPreinstallSkillRoot, watchSkillRoots } from '../registry/skill-tree.js';
38
+ import { RUNTIME_PACKAGE_VERSION } from '../common/runtime-package-version.js';
38
39
  /**
39
40
  * 装配本地 dev 模式所需的全部 Runtime 依赖
40
41
  *
@@ -185,6 +186,7 @@ export function createDevRuntime() {
185
186
  graphLoader,
186
187
  dispose: stopSkillWatch,
187
188
  diagnostics: {
189
+ packageVersion: RUNTIME_PACKAGE_VERSION,
188
190
  deploymentContext,
189
191
  toolExecutionMode,
190
192
  llmProviderKind,
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @see 04-runtime-core/design.md §1.5
8
8
  */
9
- import type { ExecutionContext, InvokeConfig, ResumeHitlResult } from '@gt-fe/eap-contracts';
9
+ import type { ExecutionContext, InvokeConfig, ResumeHitlResult } from '@eap/contracts';
10
10
  import type { InvokeResponseData } from '../api/routes/agent.routes.js';
11
11
  import type { GraphMessage } from '../graph/contracts/executable-graph.js';
12
12
  import type { SSEEvent } from '../sse/contracts/sse-emitter.contract.js';
@@ -31,7 +31,7 @@ export interface ExecuteParams {
31
31
  }
32
32
  /** 同步执行结果 */
33
33
  export type ExecuteResult = InvokeResponseData;
34
- /** HITL 恢复结果(与 @gt-fe/eap-contracts ResumeHitlResult 对齐) */
34
+ /** HITL 恢复结果(与 @eap/contracts ResumeHitlResult 对齐) */
35
35
  export type { ResumeHitlResult };
36
36
  /** HITL 用户响应 */
37
37
  export interface HitlResponse {
@@ -91,8 +91,8 @@ export interface ForkResult {
91
91
  * 开发者提供的对话上下文(开场白 / few-shot)。
92
92
  *
93
93
  * 约束(AgentRuntime.seedSessionMessages 强制校验):
94
- * - `role` 白名单为 `user` / `assistant`,`system` 走 graph.json 的
95
- * `config.systemPrompt`(由 SystemPromptBuilder 拼平台规则),`tool` 必须与
94
+ * - `role` 白名单为 `user` / `assistant`,`system` 由 SystemPromptBuilder 拼
95
+ * (平台规则 + `prompts.system` + 节点 `systemPrompt`),`tool` 必须与
96
96
  * 前置 assistant.toolCalls 严格配对,两者禁止作为种子;
97
97
  * - 该 session 已有历史时,视作已种入,跳过写入(exactly-once 幂等)。
98
98
  */
package/dist/server.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * TODO: Phase 2 将本文件迁移至 dev-tools/cli(作为 `eap dev` 命令实现),
8
8
  * runtime 包不再包含 HTTP 服务代码,仅作为纯库导出。
9
9
  */
10
- import { ErrorCodes } from '@gt-fe/eap-contracts';
10
+ import { ErrorCodes } from '@eap/contracts';
11
11
  import { createServer } from 'node:http';
12
12
  import { URL } from 'node:url';
13
13
  import { resolveExecutionContext } from './governance/index.js';
@@ -65,11 +65,12 @@ export declare class CompressionMiddleware {
65
65
  sessionId: string;
66
66
  taskId?: string;
67
67
  userId: string;
68
+ maxContextTokens?: number;
68
69
  }): Promise<MessageRecord[]>;
69
70
  /**
70
71
  * 检查是否需要压缩(不执行)
71
72
  */
72
- checkCompression(messages: MessageRecord[]): {
73
+ checkCompression(messages: MessageRecord[], maxContextTokens?: number): {
73
74
  needsCompression: boolean;
74
75
  currentTokens: number;
75
76
  currentRounds: number;
@@ -65,13 +65,16 @@ export class CompressionMiddleware {
65
65
  // 转换为 GraphMessage 格式进行检查
66
66
  const graphMessages = this.toGraphMessages(messages);
67
67
  // 检查是否需要压缩
68
- const checkResult = this.compression.checkCompression(graphMessages);
68
+ const windowOverride = context.maxContextTokens
69
+ ? { maxContextTokens: context.maxContextTokens }
70
+ : undefined;
71
+ const checkResult = this.compression.checkCompression(graphMessages, windowOverride);
69
72
  if (!checkResult.needsCompression) {
70
73
  return messages;
71
74
  }
72
75
  // 执行压缩
73
76
  const startTime = Date.now();
74
- const compressedMessages = await this.compression.compress(graphMessages);
77
+ const compressedMessages = await this.compression.compress(graphMessages, windowOverride);
75
78
  const duration = Date.now() - startTime;
76
79
  // 转换回 MessageRecord 格式
77
80
  const compressedRecords = this.toMessageRecords(compressedMessages, messages);
@@ -106,9 +109,9 @@ export class CompressionMiddleware {
106
109
  /**
107
110
  * 检查是否需要压缩(不执行)
108
111
  */
109
- checkCompression(messages) {
112
+ checkCompression(messages, maxContextTokens) {
110
113
  const graphMessages = this.toGraphMessages(messages);
111
- return this.compression.checkCompression(graphMessages);
114
+ return this.compression.checkCompression(graphMessages, maxContextTokens ? { maxContextTokens } : undefined);
112
115
  }
113
116
  /**
114
117
  * 获取配置
@@ -6,7 +6,7 @@
6
6
  * - skill.started / skill.completed / skill.failed(子图执行生命周期)
7
7
  *
8
8
  * @see design.md §1.6 SSE事件协议定义
9
- * @see @gt-fe/eap-contracts OPTIONAL_SSE_EVENTS
9
+ * @see @eap/contracts OPTIONAL_SSE_EVENTS
10
10
  */
11
11
  /** 技能匹配成功 */
12
12
  export interface SkillMatchedPayload {
@@ -6,6 +6,6 @@
6
6
  * - skill.started / skill.completed / skill.failed(子图执行生命周期)
7
7
  *
8
8
  * @see design.md §1.6 SSE事件协议定义
9
- * @see @gt-fe/eap-contracts OPTIONAL_SSE_EVENTS
9
+ * @see @eap/contracts OPTIONAL_SSE_EVENTS
10
10
  */
11
11
  export {};
@@ -41,4 +41,13 @@ export interface StatsNodeTokenUsagePayload {
41
41
  costEstimate: number;
42
42
  /** 该节点本次调用耗时(毫秒),可选 */
43
43
  durationMs?: number;
44
+ /** 本轮 prompt 占用的上下文窗 used/window;缺 window 则省略 */
45
+ used?: number;
46
+ window?: number;
47
+ /** 按字符比例把 used 拆到 system/messages/tools;合计 = used */
48
+ parts?: {
49
+ system: number;
50
+ messages: number;
51
+ tools: number;
52
+ };
44
53
  }
@@ -18,7 +18,7 @@ import type { IMessageRepository } from '../session/session.service.js';
18
18
  export interface CompressionBudgetConfig {
19
19
  /** 模型 context window 大小 */
20
20
  modelContextWindow: number;
21
- /** 历史消息占比阈值(超过此比例触发压缩,默认 0.8) */
21
+ /** 相对模型窗口的触发系数(默认 0.7,与滑动窗口同一公式) */
22
22
  historyThreshold?: number;
23
23
  /** 压缩后目标占比(默认 0.5) */
24
24
  targetRatio?: number;
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { createContextBudget } from './context-window-budget.js';
11
11
  import { CompressionMiddleware, createCompressionMiddlewareWithLLM, } from '../session/compression-middleware.js';
12
- import { estimateMessagesTokens } from '../memory/message-compression.service.js';
12
+ import { estimateMessagesTokens, resolveCompressionMaxTokens, } from '../memory/message-compression.service.js';
13
13
  // ─────────────────────────────────────────────
14
14
  // CompressionBudgetIntegration
15
15
  // ─────────────────────────────────────────────
@@ -28,7 +28,7 @@ export class CompressionBudgetIntegration {
28
28
  constructor(config, repository, messageRepository) {
29
29
  this.config = {
30
30
  modelContextWindow: config.modelContextWindow,
31
- historyThreshold: config.historyThreshold ?? 0.8,
31
+ historyThreshold: config.historyThreshold ?? 0.7,
32
32
  targetRatio: config.targetRatio ?? 0.5,
33
33
  autoCompress: config.autoCompress ?? true,
34
34
  llmConfig: config.llmConfig,
@@ -36,13 +36,17 @@ export class CompressionBudgetIntegration {
36
36
  // 创建预算管理器
37
37
  this.budget = createContextBudget(this.config.modelContextWindow);
38
38
  // 创建压缩中间件
39
+ const compressionConfig = {
40
+ maxContextTokens: this.config.modelContextWindow,
41
+ };
39
42
  if (this.config.llmConfig?.provider) {
40
43
  this.compression = createCompressionMiddlewareWithLLM(this.config.llmConfig.provider, {
41
44
  llmConfig: this.config.llmConfig,
45
+ compressionConfig,
42
46
  }, repository, messageRepository);
43
47
  }
44
48
  else {
45
- this.compression = new CompressionMiddleware({}, undefined, repository, messageRepository);
49
+ this.compression = new CompressionMiddleware({ compressionConfig }, undefined, repository, messageRepository);
46
50
  }
47
51
  }
48
52
  /**
@@ -55,12 +59,13 @@ export class CompressionBudgetIntegration {
55
59
  // 估算当前 Token 数
56
60
  const graphMessages = this.toGraphMessages(messages);
57
61
  const currentTokens = estimateMessagesTokens(graphMessages);
58
- // 获取历史消息的 Token 上限
59
- const maxTokens = this.budget.getMaxTokens('history');
60
- // 计算使用率
61
- const usageRatio = maxTokens > 0 ? currentTokens / maxTokens : 0;
62
- // 检查是否超过阈值
63
- const needsCompression = usageRatio > this.config.historyThreshold;
62
+ // 与滑动窗口同一阈值:window × 系数(默认 0.7);historyThreshold 作为系数覆盖
63
+ const maxTokens = resolveCompressionMaxTokens(this.config.modelContextWindow, this.config.historyThreshold);
64
+ // 计算使用率(相对窗口,不是相对阈值)
65
+ const usageRatio = this.config.modelContextWindow > 0
66
+ ? currentTokens / this.config.modelContextWindow
67
+ : 0;
68
+ const needsCompression = currentTokens > maxTokens;
64
69
  // 计算压缩目标
65
70
  const targetTokens = needsCompression
66
71
  ? Math.floor(maxTokens * this.config.targetRatio)
@@ -96,7 +101,10 @@ export class CompressionBudgetIntegration {
96
101
  }
97
102
  // 执行压缩
98
103
  const startTime = Date.now();
99
- const compressedMessages = await this.compression.processMessages(messages, context);
104
+ const compressedMessages = await this.compression.processMessages(messages, {
105
+ ...context,
106
+ maxContextTokens: this.config.modelContextWindow,
107
+ });
100
108
  const duration = Date.now() - startTime;
101
109
  // 计算压缩后的 Token 数
102
110
  const compressedGraphMessages = this.toGraphMessages(compressedMessages);