@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
@@ -14,9 +14,11 @@ import { HitlHandler } from '../graph/hitl/hitl-handler.js';
14
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
+ import type { SessionService, SessionRecord, MessageRecord } from '../session/session.service.js';
18
+ import { type ILlmTurnLog, type LlmTurnIndexEntry, type LlmTurnLogKind, type LlmTurnRecord } from '../debug/llm-turn-log.js';
17
19
  import type { SSEEvent } from '../sse/contracts/sse-emitter.contract.js';
18
20
  import type { TaskService } from '../task/task.service.js';
19
- import type { ExecuteParams, ExecuteResult, ForkOverrides, ForkResult, HitlResponse, ResumeHitlResult, SeedSessionMessagesParams, SeedSessionMessagesResult } from './types.js';
21
+ import type { ExecuteParams, ExecuteResult, ForkParams, ForkOverrides, ForkResult, HitlResponse, ResumeHitlResult, SeedSessionMessagesParams, SeedSessionMessagesResult } from './types.js';
20
22
  export interface ForkRelationRecord {
21
23
  originalTraceId: string;
22
24
  forkedTraceId: string;
@@ -34,6 +36,10 @@ export interface AgentRuntimeDeps {
34
36
  graphLoader?: GraphLoader;
35
37
  /** 多轮短期记忆写回;未注入时 AgentRuntime.stream 跳过持久化 */
36
38
  messageService?: MessageService;
39
+ /** 会话查询(getConversation);未注入时仅返回 messages/llmTurns */
40
+ sessionService?: SessionService;
41
+ /** 每步 LLM 入参/响应落盘;未注入时按环境默认 `.eap/llm-turns` */
42
+ turnLog?: ILlmTurnLog;
37
43
  /**
38
44
  * Hosting 注入的 Trace 采集器。与 ExecuteParams.traceId 同时存在时,
39
45
  * Skill/Tool 节点会 createNode/completeNode。不注入则只发 SSE,不写 Span。
@@ -41,6 +47,27 @@ export interface AgentRuntimeDeps {
41
47
  traceCollector?: ITraceCollector;
42
48
  recordForkRelation?: RecordForkRelation;
43
49
  }
50
+ /** 一次对话详情:消息 + 每步 LLM 入参/响应。File / 内存 / Postgres 同一套结构。 */
51
+ export interface ConversationDetail {
52
+ sessionId: string;
53
+ session: SessionRecord | null;
54
+ messages: MessageRecord[];
55
+ llmTurns: LlmTurnRecord[];
56
+ llmTurnIndex: LlmTurnIndexEntry[];
57
+ tokens: {
58
+ input: number;
59
+ output: number;
60
+ total: number;
61
+ byNode: Record<string, {
62
+ input: number;
63
+ output: number;
64
+ }>;
65
+ };
66
+ storage: {
67
+ llmTurnKind?: LlmTurnLogKind;
68
+ llmTurnDir?: string;
69
+ };
70
+ }
44
71
  /**
45
72
  * AgentRuntime — 管理 Graph 执行的 SDK 门面
46
73
  */
@@ -69,6 +96,17 @@ export declare class AgentRuntime {
69
96
  * @throws PARAMS_INVALID messageService 未注入(无存储无法写入)
70
97
  */
71
98
  seedSessionMessages(params: SeedSessionMessagesParams): Promise<SeedSessionMessagesResult>;
99
+ /**
100
+ * 查询一次对话详情:会话元数据、消息、每步 LLM 落盘(入参、响应、URL、模型、脱敏 sk、提示词)。
101
+ * 本地默认读 `.eap/sessions` + `.eap/llm-turns/{sessionId}/`。
102
+ */
103
+ getConversation(sessionId: string): Promise<ConversationDetail>;
104
+ listLlmTurns(sessionId: string): Promise<LlmTurnIndexEntry[]>;
105
+ getLlmTurn(sessionId: string, turnId: string): Promise<LlmTurnRecord | null>;
106
+ listConversations(options?: {
107
+ userId?: string;
108
+ }): Promise<SessionRecord[]>;
109
+ private resolveTurnLog;
72
110
  /** 同步执行 Graph(经 InvokeOrchestrator,内含 loadedTools 注入) */
73
111
  execute(params: ExecuteParams): Promise<ExecuteResult>;
74
112
  /**
@@ -92,9 +130,10 @@ export declare class AgentRuntime {
92
130
  */
93
131
  streamResumeHitl(taskId: string, response: HitlResponse): AsyncIterable<SSEEvent>;
94
132
  /**
95
- * 从 Checkpoint Fork 并真实重新执行
133
+ * 从 Checkpoint Fork 并真实重新执行。
134
+ * taskId / traceId 由上层发号;Runtime 不 createFlow、不另发 Task 号。
96
135
  */
97
- fork(checkpointId: string, overrides?: ForkOverrides): Promise<ForkResult>;
136
+ fork(params: ForkParams): Promise<ForkResult>;
98
137
  private prepareHitlResume;
99
138
  private rebuildExecutableGraph;
100
139
  private handleHitlTimeout;
@@ -7,10 +7,12 @@
7
7
  * @see 04-runtime-core/design.md §1.5 / §1.7
8
8
  */
9
9
  import { randomUUID } from 'node:crypto';
10
- import { HitlAlreadyTimedOutError, HitlNotWaitingError, buildHitlWaitedPayload, isHitlDeadlinePassed, readPersistedHitlWaiting, unwrapHitlPaused, } from '../graph/hitl/hitl-handler.js';
11
- import { toLoadedSkillMap, mergeSkillScriptTools } from '../tool/system-prompt-builder.js';
10
+ import { HITL_TIMEOUT_ERROR_CODE, HITL_TIMEOUT_MESSAGE, HitlAlreadyTimedOutError, HitlNotWaitingError, buildHitlWaitedPayload, isHitlDeadlinePassed, readPersistedHitlWaiting, unwrapHitlPaused, } from '../graph/hitl/hitl-handler.js';
11
+ import { toLoadedSkillMap } from '../tool/system-prompt-builder.js';
12
+ import { assembleLoadedTools } from '../tool/harness-primitives.js';
13
+ import { createFileLlmTurnLogFromEnv, } from '../debug/llm-turn-log.js';
12
14
  import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, buildTurnTranscriptFromStreamEvents, extractHitlThreadId, extractStreamReasoning, extractStreamTextDelta, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, } from '../session/persist-turn-messages.js';
13
- import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
15
+ import { AppError, ErrorCodes } from '@eap/contracts';
14
16
  import { resolveExecutionContext } from '../governance/governance-client.js';
15
17
  import { resolveAgentCode } from '../common/agent-code.js';
16
18
  import { resolveTraceId } from '../common/trace-id.js';
@@ -42,6 +44,7 @@ export class AgentRuntime {
42
44
  if (typeof this.deps.hitlHandler.setTimeoutListener === 'function') {
43
45
  this.deps.hitlHandler.setTimeoutListener((event) => this.handleHitlTimeout(event));
44
46
  }
47
+ this.deps.hitlHandler.setTraceCollector?.(this.deps.traceCollector);
45
48
  }
46
49
  /** 从 GraphLoader 解析 tool manifest 映射(invoke/stream/resume/fork 共用) */
47
50
  async resolveLoadedTools(agentCode, agentVersion) {
@@ -126,6 +129,64 @@ export class AgentRuntime {
126
129
  })));
127
130
  return { sessionId, seeded: true };
128
131
  }
132
+ /**
133
+ * 查询一次对话详情:会话元数据、消息、每步 LLM 落盘(入参、响应、URL、模型、脱敏 sk、提示词)。
134
+ * 本地默认读 `.eap/sessions` + `.eap/llm-turns/{sessionId}/`。
135
+ */
136
+ async getConversation(sessionId) {
137
+ const turnLog = this.resolveTurnLog();
138
+ const [session, messages, llmTurnIndex] = await Promise.all([
139
+ this.deps.sessionService
140
+ ? this.deps.sessionService.findSession(sessionId).catch(() => null)
141
+ : Promise.resolve(null),
142
+ this.deps.messageService
143
+ ? this.deps.messageService.getMessages(sessionId)
144
+ : Promise.resolve([]),
145
+ turnLog ? turnLog.list(sessionId) : Promise.resolve([]),
146
+ ]);
147
+ const llmTurns = [];
148
+ if (turnLog) {
149
+ for (const entry of llmTurnIndex) {
150
+ const record = await turnLog.read(sessionId, entry.id);
151
+ if (record)
152
+ llmTurns.push(record);
153
+ }
154
+ }
155
+ const tokens = summarizeTurnTokens(llmTurns);
156
+ return {
157
+ sessionId,
158
+ session,
159
+ messages,
160
+ llmTurns,
161
+ llmTurnIndex,
162
+ tokens,
163
+ storage: { llmTurnKind: turnLog?.kind, llmTurnDir: turnLog?.baseDir },
164
+ };
165
+ }
166
+ async listLlmTurns(sessionId) {
167
+ const turnLog = this.resolveTurnLog();
168
+ if (!turnLog)
169
+ return [];
170
+ return turnLog.list(sessionId);
171
+ }
172
+ async getLlmTurn(sessionId, turnId) {
173
+ const turnLog = this.resolveTurnLog();
174
+ if (!turnLog)
175
+ return null;
176
+ return turnLog.read(sessionId, turnId);
177
+ }
178
+ async listConversations(options) {
179
+ if (options?.userId && this.deps.sessionService) {
180
+ return this.deps.sessionService.listSessions(options.userId);
181
+ }
182
+ if (this.deps.sessionService) {
183
+ return this.deps.sessionService.listAllSessions();
184
+ }
185
+ return [];
186
+ }
187
+ resolveTurnLog() {
188
+ return this.deps.turnLog ?? createFileLlmTurnLogFromEnv();
189
+ }
129
190
  /** 同步执行 Graph(经 InvokeOrchestrator,内含 loadedTools 注入) */
130
191
  async execute(params) {
131
192
  const agentCode = resolveAgentCode(params);
@@ -203,11 +264,13 @@ export class AgentRuntime {
203
264
  })
204
265
  : undefined;
205
266
  const skillDefinitions = toLoadedSkillMap(loadedSkills);
206
- // Skill scripts 虚拟 Tool 并入 loadedTools(同名不覆盖平台 Tool)
207
- const loadedTools = mergeSkillScriptTools(loadedToolsBase, loadedSkills);
267
+ const loadedTools = assembleLoadedTools(loadedToolsBase);
208
268
  const promptRoots = this.deps.graphLoader
209
269
  ? await this.deps.graphLoader.resolvePromptRoots(agentCode, params.agentVersion)
210
270
  : [];
271
+ const agentDir = this.deps.graphLoader
272
+ ? (await this.deps.graphLoader.resolveAgentDir(agentCode, params.agentVersion)) ?? undefined
273
+ : undefined;
211
274
  const history = messageRecordsToGraphMessages(sessionContext.messages);
212
275
  const messages = await buildTurnGraphMessages({
213
276
  history,
@@ -237,6 +300,7 @@ export class AgentRuntime {
237
300
  loadedSkills,
238
301
  skillDefinitions,
239
302
  promptRoots,
303
+ agentDir,
240
304
  skillInput: resolveSkillInputFromInvokeInput(params.input),
241
305
  traceId: resolveTraceId(params),
242
306
  traceCollector: this.deps.traceCollector,
@@ -382,7 +446,7 @@ export class AgentRuntime {
382
446
  }
383
447
  catch (err) {
384
448
  if (err instanceof HitlAlreadyTimedOutError) {
385
- throw new AppError(ErrorCodes.TASK_HITL_TIMEOUT, `Task ${taskId} HITL wait exceeded deadline`, { exposeMessage: true });
449
+ throw new AppError(ErrorCodes.TASK_HITL_TIMEOUT, HITL_TIMEOUT_MESSAGE, { exposeMessage: true });
386
450
  }
387
451
  if (err instanceof HitlNotWaitingError) {
388
452
  throw new AppError(ErrorCodes.TASK_HITL_INVALID_STATE, `Task ${taskId} is not waiting for HITL`, { exposeMessage: true });
@@ -405,8 +469,8 @@ export class AgentRuntime {
405
469
  const agentVersion = (typeof resumedState.agentVersion === 'string' && resumedState.agentVersion) ||
406
470
  undefined;
407
471
  const graph = await this.rebuildExecutableGraph(agentCode, agentVersion);
408
- const loadedTools = resumedState.loadedTools ??
409
- (await this.resolveLoadedTools(agentCode, agentVersion));
472
+ const loadedTools = assembleLoadedTools(resumedState.loadedTools ??
473
+ (await this.resolveLoadedTools(agentCode, agentVersion)));
410
474
  const skillFromState = resumedState.skillDefinitions;
411
475
  const skillBundle = skillFromState
412
476
  ? {
@@ -422,6 +486,10 @@ export class AgentRuntime {
422
486
  taskId,
423
487
  correlationId: resumedState.correlationId ?? correlationId,
424
488
  agentVersion: agentVersion ?? resumedState.agentVersion,
489
+ agentDir: (typeof resumedState.agentDir === 'string' && resumedState.agentDir) ||
490
+ (this.deps.graphLoader
491
+ ? (await this.deps.graphLoader.resolveAgentDir(agentCode, agentVersion)) ?? undefined
492
+ : undefined),
425
493
  activatedSkill: resumedState.activatedSkill,
426
494
  skillRoute: resumedState.skillRoute ??
427
495
  (typeof resumedState.activatedSkill === 'string' && resumedState.activatedSkill
@@ -536,16 +604,23 @@ export class AgentRuntime {
536
604
  }
537
605
  }
538
606
  /**
539
- * 从 Checkpoint Fork 并真实重新执行
607
+ * 从 Checkpoint Fork 并真实重新执行。
608
+ * taskId / traceId 由上层发号;Runtime 不 createFlow、不另发 Task 号。
540
609
  */
541
- async fork(checkpointId, overrides = {}) {
610
+ async fork(params) {
611
+ const { checkpointId, taskId, traceId } = params;
612
+ if (!taskId?.trim() || !traceId?.trim()) {
613
+ throw new AppError(ErrorCodes.PARAMS_INVALID, 'fork requires non-empty taskId and traceId from the caller', { exposeMessage: true });
614
+ }
615
+ const overrides = {
616
+ ...params.overrides,
617
+ executionContext: params.executionContext ?? params.overrides?.executionContext,
618
+ };
542
619
  const source = await this.deps.checkpointSaver.loadByCheckpointId(checkpointId);
543
620
  if (!source) {
544
621
  throw new AppError(ErrorCodes.HOSTING_CHECKPOINT_NOT_FOUND, `Checkpoint not found: ${checkpointId}`, { exposeMessage: true });
545
622
  }
546
- // 优先级:overrides(agentCode > 废弃 agentId)> Checkpoint state
547
623
  const agentCode = overrides.agentCode ??
548
- overrides.agentId ??
549
624
  source.state.agentCode ??
550
625
  source.state.agentId ??
551
626
  'unknown-agent';
@@ -556,8 +631,9 @@ export class AgentRuntime {
556
631
  source.checkpointId;
557
632
  const newThreadId = randomUUID();
558
633
  const forkedCheckpoint = await this.deps.checkpointSaver.fork(checkpointId, newThreadId, overrides);
559
- const correlationId = randomUUID();
634
+ const correlationId = overrides.executionContext?.correlationId ?? randomUUID();
560
635
  const task = await this.deps.taskService.create({
636
+ id: taskId,
561
637
  agentId: agentCode,
562
638
  sessionId,
563
639
  userId,
@@ -566,48 +642,46 @@ export class AgentRuntime {
566
642
  timeout: 600_000,
567
643
  });
568
644
  await this.deps.taskService.updateStatus(task.id, 'running');
569
- let newTraceId = randomUUID();
570
- if (this.deps.traceCollector) {
571
- newTraceId = await this.deps.traceCollector.createFlow({
572
- traceId: newTraceId,
573
- agentId: agentCode,
574
- agentCode,
575
- sessionId,
576
- userId,
577
- correlationId,
578
- callType: 'fork',
579
- metadata: { checkpointId, originalTraceId },
580
- });
581
- }
582
645
  const agentVersion = overrides.agentVersion ??
583
646
  (typeof source.state.agentVersion === 'string' ? source.state.agentVersion : undefined);
584
647
  const graph = await this.rebuildExecutableGraph(agentCode, agentVersion);
585
648
  const loadedToolsBase = await this.resolveLoadedTools(agentCode, agentVersion);
586
649
  const skillBundle = await this.resolveLoadedSkills(agentCode, userId, agentVersion);
587
- const loadedTools = mergeSkillScriptTools(loadedToolsBase, skillBundle.loadedSkills);
650
+ const loadedTools = assembleLoadedTools(loadedToolsBase);
651
+ const agentDir = (typeof forkedCheckpoint.state.agentDir === 'string' && forkedCheckpoint.state.agentDir) ||
652
+ (this.deps.graphLoader
653
+ ? (await this.deps.graphLoader.resolveAgentDir(agentCode, agentVersion)) ?? undefined
654
+ : undefined);
588
655
  try {
589
656
  const result = await this.deps.graphEngine.invoke(graph, {
590
657
  ...forkedCheckpoint.state,
591
658
  agentVersion,
659
+ agentDir,
592
660
  userId,
593
661
  delegationToken: overrides.executionContext?.delegationToken,
594
662
  executionContext: overrides.executionContext,
595
663
  loadedTools,
596
664
  ...skillBundle,
597
- traceId: newTraceId,
665
+ taskId: task.id,
666
+ traceId,
598
667
  traceCollector: this.deps.traceCollector,
599
668
  });
600
669
  await this.deps.taskService.updateStatus(task.id, 'success', result.output);
601
670
  if (this.deps.recordForkRelation) {
602
- await this.deps.recordForkRelation({
603
- originalTraceId,
604
- forkedTraceId: newTraceId,
605
- checkpointId,
606
- overrides,
607
- });
671
+ try {
672
+ await this.deps.recordForkRelation({
673
+ originalTraceId,
674
+ forkedTraceId: traceId,
675
+ checkpointId,
676
+ overrides,
677
+ });
678
+ }
679
+ catch {
680
+ // Fork 关系上报失败不阻断执行结果
681
+ }
608
682
  }
609
683
  return {
610
- newTraceId,
684
+ newTraceId: traceId,
611
685
  originalTraceId,
612
686
  newTaskId: task.id,
613
687
  checkpointId,
@@ -618,14 +692,14 @@ export class AgentRuntime {
618
692
  output_tokens: result.tokenUsage.output,
619
693
  total_tokens: result.tokenUsage.total,
620
694
  },
621
- compareUrl: `/traces/compare?traceId1=${originalTraceId}&traceId2=${newTraceId}`,
695
+ compareUrl: `/traces/compare?traceId1=${originalTraceId}&traceId2=${traceId}`,
622
696
  };
623
697
  }
624
698
  catch (err) {
625
699
  const message = err instanceof Error ? err.message : 'Unknown error';
626
700
  await this.deps.taskService.updateStatus(task.id, 'failed', message);
627
701
  return {
628
- newTraceId,
702
+ newTraceId: traceId,
629
703
  originalTraceId,
630
704
  newTaskId: task.id,
631
705
  checkpointId,
@@ -679,7 +753,7 @@ export class AgentRuntime {
679
753
  toolSpanId: persisted.toolSpanId,
680
754
  skillSpanId: persisted.skillSpanId,
681
755
  });
682
- throw new AppError(ErrorCodes.TASK_HITL_TIMEOUT, `Task ${taskId} HITL wait exceeded deadline`, { exposeMessage: true });
756
+ throw new AppError(ErrorCodes.TASK_HITL_TIMEOUT, HITL_TIMEOUT_MESSAGE, { exposeMessage: true });
683
757
  }
684
758
  return {
685
759
  task,
@@ -719,7 +793,7 @@ export class AgentRuntime {
719
793
  return;
720
794
  }
721
795
  const collector = this.deps.traceCollector;
722
- const error = event.behavior === 'cancel' ? 'HITL cancelled by timeout' : 'HITL wait timed out';
796
+ const error = HITL_TIMEOUT_MESSAGE;
723
797
  if (collector) {
724
798
  if (event.toolSpanId) {
725
799
  try {
@@ -746,6 +820,7 @@ export class AgentRuntime {
746
820
  toolCallId: event.toolCallId,
747
821
  toolName: event.toolName,
748
822
  error,
823
+ errorCode: HITL_TIMEOUT_ERROR_CODE,
749
824
  });
750
825
  }
751
826
  catch {
@@ -771,6 +846,21 @@ export class AgentRuntime {
771
846
  };
772
847
  }
773
848
  }
849
+ function summarizeTurnTokens(turns) {
850
+ const byNode = {};
851
+ let input = 0;
852
+ let output = 0;
853
+ for (const turn of turns) {
854
+ input += turn.response.inputTokens ?? 0;
855
+ output += turn.response.outputTokens ?? 0;
856
+ const nodeId = turn.nodeId ?? 'llm';
857
+ const bucket = byNode[nodeId] ?? { input: 0, output: 0 };
858
+ bucket.input += turn.response.inputTokens ?? 0;
859
+ bucket.output += turn.response.outputTokens ?? 0;
860
+ byNode[nodeId] = bucket;
861
+ }
862
+ return { input, output, total: input + output, byNode };
863
+ }
774
864
  /** 工厂:从运行时依赖组装 AgentRuntime */
775
865
  export function createAgentRuntime(deps) {
776
866
  return new AgentRuntime(deps);
@@ -18,7 +18,7 @@
18
18
  *
19
19
  * 装配策略(与 server.ts 一致):
20
20
  * - LLM Provider: Routing > HttpLLMProvider(OpenAI) > Echo 兜底
21
- * - Tool Executor: mock(显式)/ DevToolExecutor(dev+mcp,缺配置失败)/ 通用路由
21
+ * - Tool Executor: DevToolExecutor(dev+mcp,缺配置失败)/ 通用路由
22
22
  * - Governance: AllowAll(本地闭环默认) / HttpGovernance(显式 GOVERNANCE_DISABLED=0)
23
23
  * - 存储: EAP_RUNTIME_STORAGE_PROFILE(默认 dev = File Session/Message/Checkpoint)
24
24
  * - Registry: 本地闭环不装配,由 GraphLoader.deploymentSnapshot 兜底
@@ -27,6 +27,7 @@ import { InvokeOrchestrator, StreamOrchestrator, SessionService, MessageService,
27
27
  import { type AgentRuntime } from './agent-runtime.js';
28
28
  import type { RuntimeStorageProfile } from './storage-profile.js';
29
29
  import type { HitlResponse, ResumeHitlResult, SSEEvent } from './types.js';
30
+ import type { ILlmTurnLog } from '../debug/llm-turn-log.js';
30
31
  /** createDevRuntime 返回值(CLI 嵌入用,可选字段后续扩展) */
31
32
  export interface DevRuntime {
32
33
  invokeOrchestrator: InvokeOrchestrator;
@@ -39,6 +40,7 @@ export interface DevRuntime {
39
40
  streamResumeHitl: (taskId: string, response: HitlResponse) => AsyncIterable<SSEEvent>;
40
41
  sessionService: SessionService;
41
42
  messageService: MessageService;
43
+ turnLog?: ILlmTurnLog;
42
44
  sessionContextService: SessionContextService;
43
45
  taskService: TaskService;
44
46
  tokenStatsService: TokenStatsService;
@@ -53,9 +55,10 @@ export interface DevRuntime {
53
55
  llmDefaultModel: string;
54
56
  governanceKind: 'AllowAll' | 'Http';
55
57
  storageProfile: RuntimeStorageProfile;
56
- sessionStoreKind: 'file' | 'in-memory';
57
- messageStoreKind: 'memory' | 'file' | 'postgres';
58
- checkpointKind: 'memory' | 'file' | 'redis+pg' | 'pg';
58
+ sessionStoreKind: 'file' | 'hosting';
59
+ messageStoreKind: 'file' | 'postgres';
60
+ checkpointKind: 'file' | 'redis+pg';
61
+ llmTurnKind: 'file' | 'postgres';
59
62
  foundationConfigured: boolean;
60
63
  skillHotReload: boolean;
61
64
  };
@@ -18,7 +18,7 @@
18
18
  *
19
19
  * 装配策略(与 server.ts 一致):
20
20
  * - LLM Provider: Routing > HttpLLMProvider(OpenAI) > Echo 兜底
21
- * - Tool Executor: mock(显式)/ DevToolExecutor(dev+mcp,缺配置失败)/ 通用路由
21
+ * - Tool Executor: DevToolExecutor(dev+mcp,缺配置失败)/ 通用路由
22
22
  * - Governance: AllowAll(本地闭环默认) / HttpGovernance(显式 GOVERNANCE_DISABLED=0)
23
23
  * - 存储: EAP_RUNTIME_STORAGE_PROFILE(默认 dev = File Session/Message/Checkpoint)
24
24
  * - Registry: 本地闭环不装配,由 GraphLoader.deploymentSnapshot 兜底
@@ -28,10 +28,10 @@ import { HttpLLMProvider } from '../providers/http-llm-provider.js';
28
28
  import { createLocalLlmProvider, requireLocalLlmConnections } from '../providers/local-llm-provider.js';
29
29
  import { RoutingLLMProvider } from '../providers/routing-llm-provider.js';
30
30
  import { createToolExecutor } from '../tool/create-tool-executor.js';
31
- import { MockToolExecutor } from '../tool/mock-tool-executor.js';
32
31
  import { DevToolExecutor } from '../tool/dev-tool-executor.js';
32
+ import { withHarnessPrimitives } from '../tool/harness-primitives.js';
33
33
  import { AllowAllGovernanceClient, HttpGovernanceClient, } from '../governance/index.js';
34
- import { InvokeOrchestrator, StreamOrchestrator, SessionService, MessageService, SessionContextService, TaskService, LangGraphAdapter, HitlHandler, TokenBudgetService, InMemoryTokenBudgetRepository, TokenStatsService, TokenBudgetGuard, SessionBudgetChecker, createAgentRegistryClient, GraphLoader, } from '../index.js';
34
+ import { InvokeOrchestrator, StreamOrchestrator, SessionService, MessageService, SessionContextService, TaskService, LangGraphAdapter, HitlHandler, TokenBudgetService, TokenStatsService, TokenBudgetGuard, SessionBudgetChecker, createAgentRegistryClient, GraphLoader, } from '../index.js';
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';
@@ -46,43 +46,42 @@ export function createDevRuntime() {
46
46
  const foundationConfigured = Boolean(process.env.FOUNDATION_SERVICE_URL && process.env.FOUNDATION_SERVICE_URL.trim().length > 0);
47
47
  const toolExecutionMode = (process.env.EAP_TOOL_EXECUTION_MODE ?? 'mcp').toLowerCase();
48
48
  const deploymentContext = process.env.EAP_DEPLOYMENT_CONTEXT ?? 'server';
49
- // ── 存储 profile(默认 dev:File Session/Message/Checkpoint)──
50
- // 兼容旧变量:EAP_SESSION_STORE=memory → 强制 test(全内存)
51
- const legacySessionStore = process.env.EAP_SESSION_STORE?.toLowerCase();
49
+ // ── 存储:未配库默认文件;配齐 DATABASE + Redis 则 Redis+PG ──
52
50
  const storage = assembleRuntimeStorage({
53
- defaultProfile: legacySessionStore === 'memory' || legacySessionStore === 'in-memory' ? 'test' : 'dev',
54
- profile: legacySessionStore === 'memory' || legacySessionStore === 'in-memory'
55
- ? 'test'
56
- : undefined,
51
+ defaultProfile: 'dev',
57
52
  });
53
+ const turnLog = process.env.EAP_LLM_TURN_LOG === '0' ? undefined : storage.turnLog;
58
54
  // ── LLM Provider(本地 eap.config runtime.llm 优先,再 Foundation 路由,再环境直连)──
59
55
  let llmProviderKind;
60
56
  let llmProvider;
61
57
  const localLlm = process.env.EAP_LLM_MODE === 'local';
62
58
  if (localLlm || (!foundationConfigured && process.env.OPENAI_API_KEY)) {
63
59
  llmProvider = localLlm
64
- ? createLocalLlmProvider(requireLocalLlmConnections())
60
+ ? createLocalLlmProvider(requireLocalLlmConnections(), turnLog)
65
61
  : new HttpLLMProvider({
66
62
  apiKey: process.env.OPENAI_API_KEY ?? '',
67
63
  baseUrl: process.env.OPENAI_BASE_URL,
68
64
  defaultModel: process.env.EAP_DEFAULT_MODEL,
65
+ turnLog,
69
66
  });
70
67
  llmProviderKind = 'openai-http';
71
68
  }
72
69
  else if (foundationConfigured) {
73
- llmProvider = new RoutingLLMProvider({ foundationUrl });
70
+ llmProvider = new RoutingLLMProvider({ foundationUrl, turnLog });
74
71
  llmProviderKind = 'routing';
75
72
  }
76
73
  else {
77
- llmProvider = createDefaultLLMProvider();
74
+ llmProvider = createDefaultLLMProvider(turnLog);
78
75
  llmProviderKind = 'echo';
79
76
  }
80
- // ── Tool Executor(显式 mock → dev+mcp 直连,缺配置失败 → 通用路由)──
81
- const toolExecutor = toolExecutionMode === 'mock'
82
- ? new MockToolExecutor({ toolsDir: process.env.EAP_LOCAL_TOOLS_DIR })
83
- : toolExecutionMode === 'mcp' && deploymentContext === 'dev'
84
- ? new DevToolExecutor()
85
- : createToolExecutor({ foundationUrl, deploymentContext });
77
+ // ── Tool Executor(dev+mcp 直连,缺配置失败 → 通用路由)──
78
+ const needsHarnessWrap = toolExecutionMode === 'mcp' && deploymentContext === 'dev';
79
+ const innerExecutor = toolExecutionMode === 'mcp' && deploymentContext === 'dev'
80
+ ? new DevToolExecutor()
81
+ : createToolExecutor({ foundationUrl, deploymentContext });
82
+ const toolExecutor = needsHarnessWrap
83
+ ? withHarnessPrimitives(innerExecutor)
84
+ : innerExecutor;
86
85
  // ── Governance ──
87
86
  const governanceClient = process.env.GOVERNANCE_DISABLED === '1' || !foundationConfigured
88
87
  ? new AllowAllGovernanceClient()
@@ -99,7 +98,7 @@ export function createDevRuntime() {
99
98
  const sessionContextService = new SessionContextService(sessionService, messageService);
100
99
  // ── Task / Token ──
101
100
  const taskService = new TaskService();
102
- const budgetService = new TokenBudgetService(new InMemoryTokenBudgetRepository());
101
+ const budgetService = new TokenBudgetService(storage.tokenBudgetRepository);
103
102
  const tokenStatsService = new TokenStatsService();
104
103
  const tokenBudgetGuard = new TokenBudgetGuard(budgetService);
105
104
  const sessionBudgetChecker = new SessionBudgetChecker(budgetService);
@@ -168,6 +167,8 @@ export function createDevRuntime() {
168
167
  hitlHandler,
169
168
  graphLoader,
170
169
  messageService,
170
+ sessionService,
171
+ turnLog,
171
172
  });
172
173
  return {
173
174
  invokeOrchestrator,
@@ -177,6 +178,7 @@ export function createDevRuntime() {
177
178
  streamResumeHitl: (taskId, response) => agentRuntime.streamResumeHitl(taskId, response),
178
179
  sessionService,
179
180
  messageService,
181
+ turnLog,
180
182
  sessionContextService,
181
183
  taskService,
182
184
  tokenStatsService,
@@ -189,9 +191,10 @@ export function createDevRuntime() {
189
191
  llmDefaultModel: process.env.EAP_DEFAULT_MODEL ?? 'DeepSeek-V4-Pro',
190
192
  governanceKind: governanceClient instanceof AllowAllGovernanceClient ? 'AllowAll' : 'Http',
191
193
  storageProfile: storage.profile,
192
- sessionStoreKind: storage.diagnostics.sessionKind === 'file' ? 'file' : 'in-memory',
194
+ sessionStoreKind: storage.diagnostics.sessionKind,
193
195
  messageStoreKind: storage.diagnostics.messageKind,
194
196
  checkpointKind: storage.diagnostics.checkpointKind,
197
+ llmTurnKind: storage.diagnostics.llmTurnKind,
195
198
  foundationConfigured,
196
199
  skillHotReload,
197
200
  },
@@ -1,4 +1,4 @@
1
- export { AgentRuntime, createAgentRuntime, type AgentRuntimeDeps, type ForkRelationRecord, type RecordForkRelation, } from './agent-runtime.js';
2
- export type { ExecuteParams, ExecuteResult, ForkOverrides, ForkResult, HitlResponse, ResumeHitlResult, SSEEvent, } from './types.js';
1
+ export { AgentRuntime, createAgentRuntime, type AgentRuntimeDeps, type ForkRelationRecord, type RecordForkRelation, type ConversationDetail, } from './agent-runtime.js';
2
+ export type { ExecuteParams, ExecuteResult, ForkParams, ForkOverrides, ForkResult, HitlResponse, ResumeHitlResult, SSEEvent, } from './types.js';
3
3
  export { createDevRuntime, type DevRuntime, } from './dev-runtime.js';
4
- export { assembleRuntimeStorage, resolveRuntimeStorageProfile, type RuntimeStorageProfile, type AssembleRuntimeStorageOptions, type AssembledRuntimeStorage, } from './storage-profile.js';
4
+ export { assembleRuntimeStorage, resolveRuntimeStorageProfile, resolveDevStorageDirs, type RuntimeStorageProfile, type AssembleRuntimeStorageOptions, type AssembledRuntimeStorage, } from './storage-profile.js';
@@ -1,3 +1,3 @@
1
1
  export { AgentRuntime, createAgentRuntime, } from './agent-runtime.js';
2
2
  export { createDevRuntime, } from './dev-runtime.js';
3
- export { assembleRuntimeStorage, resolveRuntimeStorageProfile, } from './storage-profile.js';
3
+ export { assembleRuntimeStorage, resolveRuntimeStorageProfile, resolveDevStorageDirs, } from './storage-profile.js';
@@ -9,11 +9,13 @@ import type { ICheckpointSaver } from '../checkpoint/contracts/checkpoint-saver.
9
9
  import { type IPgPool } from '../checkpoint/pg-checkpoint-saver.js';
10
10
  import type { ISessionRepository } from '../session/session.service.js';
11
11
  import type { IFullMessageRepository } from '../session/message.service.js';
12
+ import type { ILlmTurnLog } from '../debug/llm-turn-log.js';
13
+ import { type ITokenBudgetRepository } from '../token/token-budget.service.js';
12
14
  export type RuntimeStorageProfile = 'test' | 'dev' | 'integration' | 'production';
13
15
  export interface AssembleRuntimeStorageOptions {
14
16
  /** 显式 profile;缺省读 EAP_RUNTIME_STORAGE_PROFILE */
15
17
  profile?: RuntimeStorageProfile;
16
- /** 未指定 profile 时的默认值(dev-runtime → dev;embedded → integration) */
18
+ /** 未指定 profile 且未配库时的默认值(本地 CLI → `dev` 文件) */
17
19
  defaultProfile: RuntimeStorageProfile;
18
20
  /** File Checkpoint / Session / Message 根目录 */
19
21
  checkpointDir?: string;
@@ -31,20 +33,27 @@ export interface AssembledRuntimeStorage {
31
33
  /** undefined = SessionService 默认 InMemory */
32
34
  sessionRepository?: ISessionRepository;
33
35
  messageRepository: IFullMessageRepository;
36
+ turnLog: ILlmTurnLog;
37
+ tokenBudgetRepository: ITokenBudgetRepository;
34
38
  diagnostics: {
35
39
  profile: RuntimeStorageProfile;
36
- checkpointKind: 'memory' | 'file' | 'redis+pg' | 'pg';
37
- sessionKind: 'memory' | 'file';
38
- messageKind: 'memory' | 'file' | 'postgres';
40
+ checkpointKind: 'file' | 'redis+pg';
41
+ sessionKind: 'file' | 'hosting';
42
+ messageKind: 'file' | 'postgres';
43
+ llmTurnKind: 'file' | 'postgres';
44
+ tokenBudgetKind: 'process' | 'postgres';
39
45
  };
40
46
  }
41
47
  export declare function resolveRuntimeStorageProfile(explicit: RuntimeStorageProfile | undefined, defaultProfile: RuntimeStorageProfile, env?: NodeJS.ProcessEnv): RuntimeStorageProfile;
48
+ /** 本地 File 存储默认落工程 `.eap/`,可用 EAP_DATA_DIR / EAP_*_DIR 覆盖。 */
49
+ export declare function resolveDevStorageDirs(env?: NodeJS.ProcessEnv, cwd?: string): {
50
+ checkpointDir: string;
51
+ sessionDir: string;
52
+ messageDir: string;
53
+ };
42
54
  /**
43
- * 按 profile 装配 Checkpoint / Session / Message 存储
44
- *
45
- * - test: 全内存
46
- * - dev: File Checkpoint + File Session + File Message
47
- * - integration/production: Message=PG;Checkpoint=Redis 热 + PG 备;
48
- * Session 仓储不落 PG(sessionId 权威在 Hosting.sessions,Runtime 内存采纳)
55
+ * 按 profile 装配 Checkpoint / Message / Token / LLM 回合存储。
56
+ * `dev`:文件。其余 profile:Redis + PostgreSQL(缺配置则抛错)。
57
+ * 未设 profile 时:已配置 DB 或 Redis 则按 Redis+PostgreSQL;否则使用 defaultProfile。
49
58
  */
50
59
  export declare function assembleRuntimeStorage(options: AssembleRuntimeStorageOptions, env?: NodeJS.ProcessEnv): AssembledRuntimeStorage;