@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
@@ -10,11 +10,16 @@
10
10
  * @see design.md §1.6 hitl.waiting / hitl.resumed 事件
11
11
  * @see tasks.md Task 14
12
12
  */
13
+ import { notifyCheckpointRef, stripCheckpointCollectors, } from '../../checkpoint/checkpoint-ref-notify.js';
14
+ import { startGraphSpan, completeGraphSpan } from '../trace-span.js';
13
15
  // ─────────────────────────────────────────────
14
16
  // Types
15
17
  // ─────────────────────────────────────────────
16
18
  /** HITL 默认等待时长(毫秒)= 300 秒。SSE `hitl.waited.timeout` 对外用秒。 */
17
19
  export const DEFAULT_HITL_TIMEOUT_MS = 300_000;
20
+ /** 超时收口对外文案(Task / Trace / AppError);枚举走 `hitl_timeout`。 */
21
+ export const HITL_TIMEOUT_MESSAGE = 'HITL 确认超时';
22
+ export const HITL_TIMEOUT_ERROR_CODE = 'hitl_timeout';
18
23
  /** HITL 超时错误(保留类型;timer 不再 throw,resume 逾期改走 AppError) */
19
24
  export class HitlTimeoutError extends Error {
20
25
  taskId;
@@ -151,8 +156,9 @@ export function buildHitlWaitedPayload(paused) {
151
156
  */
152
157
  function prepareHitlCheckpointState(state) {
153
158
  const raw = state;
159
+ const { traceCollector: _drop, ...rest } = raw;
154
160
  return {
155
- ...state,
161
+ ...rest,
156
162
  userId: state.userId ?? raw.userId ?? '',
157
163
  loadedTools: state.loadedTools ?? raw.loadedTools,
158
164
  forceToolServer: state.forceToolServer ?? raw.forceToolServer,
@@ -162,6 +168,7 @@ function prepareHitlCheckpointState(state) {
162
168
  delegationToken: state.delegationToken ?? raw.delegationToken,
163
169
  skillSpanId: state.skillSpanId ?? raw.skillSpanId,
164
170
  traceId: state.traceId ?? (typeof raw.traceId === 'string' ? raw.traceId : undefined),
171
+ lastSpanId: state.lastSpanId ?? (typeof raw.lastSpanId === 'string' ? raw.lastSpanId : undefined),
165
172
  };
166
173
  }
167
174
  function toTimeoutEvent(waiting) {
@@ -193,6 +200,7 @@ function waitingFromPersisted(persisted, taskId) {
193
200
  toolName: persisted.toolName,
194
201
  toolSpanId: persisted.toolSpanId,
195
202
  skillSpanId: persisted.skillSpanId,
203
+ hitlSpanId: persisted.hitlSpanId,
196
204
  };
197
205
  }
198
206
  // ─────────────────────────────────────────────
@@ -216,6 +224,9 @@ export class HitlHandler {
216
224
  timeoutListener;
217
225
  /** 同一 taskId 上串行化 resume 与 timeout,避免双边占用 */
218
226
  taskLocks = new Map();
227
+ traceCollector;
228
+ /** 进程内 interrupt 的 collector,不进 Checkpoint */
229
+ waitingCollectors = new Map();
219
230
  constructor(checkpointSaver, sseEmitter, timeoutListener) {
220
231
  this.checkpointSaver = checkpointSaver;
221
232
  this.sseEmitter = sseEmitter;
@@ -224,6 +235,9 @@ export class HitlHandler {
224
235
  setTimeoutListener(listener) {
225
236
  this.timeoutListener = listener;
226
237
  }
238
+ setTraceCollector(collector) {
239
+ this.traceCollector = collector;
240
+ }
227
241
  /**
228
242
  * 中断 Graph 执行,等待人工确认
229
243
  *
@@ -237,6 +251,11 @@ export class HitlHandler {
237
251
  const deadlineAt = new Date(Date.now() + timeoutMs).toISOString();
238
252
  const skillSpanId = extras.skillSpanId ??
239
253
  (typeof state.skillSpanId === 'string' ? state.skillSpanId : undefined);
254
+ const hitlSpanId = await startGraphSpan(state, {
255
+ name: extras.toolName ?? 'hitl',
256
+ type: 'hitl',
257
+ parentSpanId: extras.toolSpanId ?? skillSpanId,
258
+ });
240
259
  const persisted = {
241
260
  taskId,
242
261
  threadId,
@@ -247,11 +266,16 @@ export class HitlHandler {
247
266
  toolName: extras.toolName,
248
267
  toolSpanId: extras.toolSpanId,
249
268
  skillSpanId,
269
+ hitlSpanId,
250
270
  };
251
271
  const existing = await this.checkpointSaver.load(threadId);
272
+ const lastSpanId = hitlSpanId ??
273
+ extras.toolSpanId ??
274
+ (typeof state.lastSpanId === 'string' ? state.lastSpanId : undefined);
252
275
  const persistedState = {
253
276
  ...(existing?.state ?? {}),
254
277
  ...prepareHitlCheckpointState(state),
278
+ ...(lastSpanId ? { lastSpanId } : {}),
255
279
  [HITL_WAITING_STATE_KEY]: persisted,
256
280
  };
257
281
  const checkpointData = {
@@ -266,7 +290,13 @@ export class HitlHandler {
266
290
  updatedAt: now,
267
291
  isDeleted: false,
268
292
  };
269
- await this.checkpointSaver.save(threadId, checkpointData);
293
+ const persistedCheckpoint = stripCheckpointCollectors(checkpointData);
294
+ await this.checkpointSaver.save(threadId, persistedCheckpoint);
295
+ const liveCollector = state.traceCollector;
296
+ if (liveCollector) {
297
+ this.waitingCollectors.set(taskId, liveCollector);
298
+ }
299
+ await notifyCheckpointRef(persistedCheckpoint, liveCollector);
270
300
  const event = {
271
301
  type: 'hitl.waiting',
272
302
  data: {
@@ -300,6 +330,7 @@ export class HitlHandler {
300
330
  toolName: extras.toolName,
301
331
  toolSpanId: extras.toolSpanId,
302
332
  skillSpanId,
333
+ hitlSpanId,
303
334
  });
304
335
  }
305
336
  /**
@@ -335,6 +366,7 @@ export class HitlHandler {
335
366
  if (!waiting) {
336
367
  throw new HitlNotWaitingError(taskId);
337
368
  }
369
+ const liveCollector = this.waitingCollectors.get(taskId) ?? this.traceCollector;
338
370
  this.clearWaiting(taskId);
339
371
  this.resolvedChoices.set(taskId, choice);
340
372
  const baseState = prepareHitlCheckpointState(checkpoint.state);
@@ -346,13 +378,18 @@ export class HitlHandler {
346
378
  hitlRespondedBy: userId,
347
379
  };
348
380
  const now = new Date().toISOString();
349
- await this.checkpointSaver.save(waiting.threadId, {
381
+ await this.checkpointSaver.save(waiting.threadId, stripCheckpointCollectors({
350
382
  ...checkpoint,
351
383
  engineSnapshot: undefined,
352
384
  state: resumedState,
353
385
  updatedBy: userId,
354
386
  updatedAt: now,
355
- });
387
+ }));
388
+ const hitlSpanId = waiting.hitlSpanId ?? persisted?.hitlSpanId;
389
+ await completeGraphSpan(resumedState, hitlSpanId, {
390
+ status: 'completed',
391
+ output: { choice, userId },
392
+ }, liveCollector);
356
393
  const event = {
357
394
  type: 'hitl.resumed',
358
395
  data: { choice, userId },
@@ -390,6 +427,7 @@ export class HitlHandler {
390
427
  toolSpanId: waiting.toolSpanId ?? persisted.toolSpanId,
391
428
  skillSpanId: waiting.skillSpanId ?? persisted.skillSpanId,
392
429
  });
430
+ const liveCollector = this.waitingCollectors.get(taskId) ?? this.traceCollector;
393
431
  this.clearWaiting(taskId);
394
432
  if (timeoutEvent.behavior !== 'auto-confirm' && checkpoint) {
395
433
  const marked = {
@@ -400,12 +438,17 @@ export class HitlHandler {
400
438
  timeoutBehavior: timeoutEvent.behavior,
401
439
  },
402
440
  };
403
- await this.checkpointSaver.save(waiting.threadId, {
441
+ await this.checkpointSaver.save(waiting.threadId, stripCheckpointCollectors({
404
442
  ...checkpoint,
405
443
  engineSnapshot: undefined,
406
444
  state: marked,
407
445
  updatedAt: new Date().toISOString(),
408
- });
446
+ }));
447
+ const hitlSpanId = waiting.hitlSpanId ?? persisted.hitlSpanId;
448
+ await completeGraphSpan(checkpoint.state, hitlSpanId, {
449
+ status: 'failed',
450
+ error: HITL_TIMEOUT_MESSAGE,
451
+ }, liveCollector);
409
452
  }
410
453
  return timeoutEvent;
411
454
  });
@@ -439,6 +482,7 @@ export class HitlHandler {
439
482
  clearTimeout(waiting.timer);
440
483
  }
441
484
  this.waitingTasks.delete(taskId);
485
+ this.waitingCollectors.delete(taskId);
442
486
  }
443
487
  async withTaskLock(taskId, fn) {
444
488
  const prev = this.taskLocks.get(taskId) ?? Promise.resolve();
@@ -44,6 +44,20 @@ export interface LLMNodeConfig {
44
44
  name: string;
45
45
  };
46
46
  };
47
+ /** 具名 system 段(落盘排查,不另发给模型) */
48
+ systemSections?: Array<{
49
+ id: string;
50
+ title: string;
51
+ body: string;
52
+ }>;
53
+ /** 本轮调用定位(session / task / node) */
54
+ turn?: {
55
+ sessionId?: string;
56
+ taskId?: string;
57
+ nodeId?: string;
58
+ agentCode?: string;
59
+ correlationId?: string;
60
+ };
47
61
  }
48
62
  /**
49
63
  * LLMResponse — LLM 完整响应
@@ -62,8 +62,7 @@ function buildSkillPrompt(skill, params) {
62
62
  */
63
63
  export function bindSelectedSkill(state) {
64
64
  const incoming = Array.isArray(state.messages) ? [...state.messages] : [];
65
- const fromState = (typeof state.silentOutput === 'string' && state.silentOutput)
66
- || (typeof state.skillSelectRaw === 'string' ? state.skillSelectRaw : '');
65
+ const fromState = typeof state.silentOutput === 'string' ? state.silentOutput : '';
67
66
  const lastText = lastAssistantText(incoming);
68
67
  const lastIsSelector = extractJsonObject(lastText) !== null;
69
68
  const parsed = extractJsonObject(fromState) ?? extractJsonObject(lastText);
@@ -12,6 +12,7 @@
12
12
  import type { AuthContext } from '../../tool/tool-executor.interface.js';
13
13
  import type { ToolOrchestrator } from '../../tool/tool-orchestrator.js';
14
14
  import type { ToolResultInjector } from '../../tool/tool-result-injector.js';
15
+ import type { SkillDirRef } from '../adapters/skill-read.js';
15
16
  import type { HitlHandler } from '../hitl/hitl-handler.js';
16
17
  /**
17
18
  * LangGraphState — LangGraph 内部状态(与 LLMNode 对齐)
@@ -39,6 +40,7 @@ export interface ToolNodeProgress {
39
40
  executionTime?: number;
40
41
  node?: string;
41
42
  skillId?: string;
43
+ skillDirs?: SkillDirRef[];
42
44
  }
43
45
  export declare class ToolNode {
44
46
  private readonly orchestrator;
@@ -9,10 +9,30 @@
9
9
  * @see design.md §1.4 ToolNode: LangGraph的ToolNode封装
10
10
  * @see requirements.md Requirement 8 AC1, AC3, AC5
11
11
  */
12
+ import { resolveJailFromState } from '../../tool/harness-primitives.js';
12
13
  import { isHitlDecision, unwrapHitlPaused } from '../hitl/hitl-handler.js';
13
14
  import { ToolConfirmationInterceptor } from '../../tool/tool-confirmation.js';
14
15
  import { resolveCheckpointThreadId } from '../../session/persist-turn-messages.js';
15
16
  import { resolveTraceId } from '../../common/trace-id.js';
17
+ function skillDirsFromState(state) {
18
+ const defs = state['skillDefinitions'];
19
+ return Object.values(defs ?? {})
20
+ .filter((skill) => Boolean(skill.bundlePath))
21
+ .map((skill) => ({ skillId: skill.skillId, bundlePath: skill.bundlePath }));
22
+ }
23
+ function resolveActiveSkillDir(state) {
24
+ const defs = state['skillDefinitions'];
25
+ if (!defs)
26
+ return undefined;
27
+ const activated = state['activatedSkill'];
28
+ if (typeof activated === 'string' && defs[activated]?.bundlePath) {
29
+ return defs[activated].bundlePath;
30
+ }
31
+ const paths = Object.values(defs)
32
+ .map((skill) => skill.bundlePath)
33
+ .filter((p) => Boolean(p));
34
+ return paths.length === 1 ? paths[0] : undefined;
35
+ }
16
36
  export class ToolNode {
17
37
  orchestrator;
18
38
  injector;
@@ -46,6 +66,16 @@ export class ToolNode {
46
66
  return state;
47
67
  }
48
68
  const loadedTools = state['loadedTools'];
69
+ const skillDirs = skillDirsFromState(state);
70
+ const jail = resolveJailFromState({
71
+ agentDir: state['agentDir'],
72
+ userId: state['userId'],
73
+ executionContext: state['executionContext'],
74
+ loadedTools,
75
+ skillDefinitions: state['skillDefinitions'],
76
+ });
77
+ const jailExtraRoots = jail.extraRoots ?? [];
78
+ const skillDir = resolveActiveSkillDir(state);
49
79
  // 4. 转换为 ToolCallRequest[](附带 manifest 路由字段)
50
80
  const requests = lastAssistantMessage.toolCalls.map((tc) => {
51
81
  const loaded = loadedTools?.[tc.name];
@@ -62,20 +92,29 @@ export class ToolNode {
62
92
  dataScope: loaded?.manifest.dataScope,
63
93
  // dev 上下文 DevMcpExecutor 直连所需;server 上下文会忽略
64
94
  execution: loaded?.manifest.execution,
95
+ ...(loaded?.manifest.version ? { toolVersion: loaded.manifest.version } : {}),
65
96
  correlationId,
66
97
  taskId,
98
+ ...(jail.workdir ? { jailWorkdir: jail.workdir } : {}),
99
+ ...(jailExtraRoots.length > 0 ? { jailExtraRoots } : {}),
100
+ ...(skillDir ? { skillDir } : {}),
67
101
  };
68
102
  });
69
103
  const executionContext = state['executionContext'];
70
104
  const traceCollector = state['traceCollector'];
71
105
  const traceId = resolveTraceId(state);
72
- const agentCode = String(state['agentCode'] ?? state['agentId'] ?? '');
106
+ const agentCode = String(state['agentCode'] ?? '');
73
107
  const nodeId = this.nodeId ?? 'tool-node';
74
108
  const skillId = typeof state['activatedSkill'] === 'string' && state['activatedSkill']
75
109
  ? state['activatedSkill']
76
110
  : undefined;
77
111
  const emitProgress = (data) => {
78
- this.onProgress?.({ ...data, node: nodeId, ...(skillId ? { skillId } : {}) });
112
+ this.onProgress?.({
113
+ ...data,
114
+ node: nodeId,
115
+ ...(skillId ? { skillId } : {}),
116
+ ...(skillDirs.length > 0 ? { skillDirs } : {}),
117
+ });
79
118
  };
80
119
  // 5. 并行执行所有 Tool 调用(附 Trace span)
81
120
  const spanIds = await Promise.all(requests.map(async (req) => {
@@ -146,7 +185,8 @@ export class ToolNode {
146
185
  toolName: req.toolName,
147
186
  content: `用户拒绝执行工具 ${req.toolName}`,
148
187
  success: false,
149
- error: 'hitl_rejected',
188
+ error: `用户拒绝执行工具 ${req.toolName}`,
189
+ errorCode: 'hitl_rejected',
150
190
  };
151
191
  }
152
192
  else {
@@ -158,6 +198,7 @@ export class ToolNode {
158
198
  toolCallId: result.toolCallId,
159
199
  toolName: result.toolName,
160
200
  status: 'completed',
201
+ arguments: req.arguments,
161
202
  content: result.content,
162
203
  executionTime,
163
204
  });
@@ -167,6 +208,7 @@ export class ToolNode {
167
208
  toolCallId: result.toolCallId,
168
209
  toolName: result.toolName,
169
210
  status: 'error',
211
+ arguments: req.arguments,
170
212
  error: result.error ?? 'unknown error',
171
213
  executionTime,
172
214
  });
@@ -202,6 +244,7 @@ export class ToolNode {
202
244
  toolCallId: req.toolCallId,
203
245
  toolName: req.toolName,
204
246
  status: 'error',
247
+ arguments: req.arguments,
205
248
  error: message,
206
249
  executionTime,
207
250
  });
@@ -240,10 +283,11 @@ export class ToolNode {
240
283
  success: result.success,
241
284
  ...('file' in result && result.file ? { file: result.file } : {}),
242
285
  }));
243
- // 7. 返回更新后的 state(仅含新增 messages + currentNode)
286
+ const lastSpanId = [...spanIds].reverse().find((id) => Boolean(id));
244
287
  return {
245
288
  messages: toolMessages,
246
289
  currentNode: nodeId,
290
+ ...(lastSpanId ? { lastSpanId } : {}),
247
291
  };
248
292
  }
249
293
  // ─────────────────────────────────────────────
@@ -282,11 +326,7 @@ export class ToolNode {
282
326
  }
283
327
  buildAuthContext(state) {
284
328
  const executionContext = state['executionContext'];
285
- // 旧 Checkpoint / 历史调用方兼容:agentId 仅作读兜底
286
- const agentCode = String(state['agentCode'] ??
287
- state['agentId'] ??
288
- this.defaultAuthContext.agentCode ??
289
- this.defaultAuthContext.agentId);
329
+ const agentCode = String(state['agentCode'] ?? this.defaultAuthContext.agentCode ?? '');
290
330
  // 优先 ExecutionContext(invoke Token.sub),保证所有 Tool/MCP 出站同一身份
291
331
  const userId = String(executionContext?.userId ?? state['userId'] ?? this.defaultAuthContext.userId);
292
332
  const delegationToken = String(executionContext?.delegationToken ??
@@ -315,7 +355,7 @@ export class ToolNode {
315
355
  loadedTools: state['loadedTools'],
316
356
  forceToolServer: state['forceToolServer'],
317
357
  executionContext: state['executionContext'],
318
- agentCode: (state['agentCode'] ?? state['agentId']),
358
+ agentCode: state['agentCode'],
319
359
  correlationId: state['correlationId'],
320
360
  taskId: state['taskId'],
321
361
  sessionId: state['sessionId'],
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Graph 节点 Span 辅助:createNode / completeNode 失败不挡主路径。
3
+ */
4
+ import type { ITraceCollector, ExecutionNodeEvent } from '../contracts/trace-collector.js';
5
+ export declare function startGraphSpan(state: Record<string, unknown>, input: {
6
+ name: string;
7
+ type: ExecutionNodeEvent['type'];
8
+ parentSpanId?: string;
9
+ extra?: Partial<Omit<ExecutionNodeEvent, 'traceId' | 'name' | 'type' | 'parentNodeId'>>;
10
+ }): Promise<string | undefined>;
11
+ export declare function completeGraphSpan(state: Record<string, unknown>, spanId: string | undefined, params: {
12
+ status: 'completed' | 'failed';
13
+ output?: unknown;
14
+ error?: string;
15
+ inputTokens?: number;
16
+ outputTokens?: number;
17
+ totalTokens?: number;
18
+ }, collectorOverride?: ITraceCollector): Promise<void>;
19
+ export declare function withGraphSpan(state: Record<string, unknown>, spec: {
20
+ name: string;
21
+ type: ExecutionNodeEvent['type'];
22
+ }, run: () => Promise<Record<string, unknown>>): Promise<Record<string, unknown>>;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Graph 节点 Span 辅助:createNode / completeNode 失败不挡主路径。
3
+ */
4
+ import { resolveTraceId } from '../common/trace-id.js';
5
+ export async function startGraphSpan(state, input) {
6
+ const collector = state['traceCollector'];
7
+ const traceId = resolveTraceId(state);
8
+ if (!collector || !traceId)
9
+ return undefined;
10
+ const parentNodeId = input.parentSpanId ??
11
+ (typeof state['skillSpanId'] === 'string' && state['skillSpanId']
12
+ ? state['skillSpanId']
13
+ : undefined);
14
+ try {
15
+ return await collector.createNode({
16
+ traceId,
17
+ name: input.name,
18
+ type: input.type,
19
+ parentNodeId,
20
+ agentCode: (typeof state['agentCode'] === 'string' && state['agentCode']) ||
21
+ (typeof state['agentId'] === 'string' ? state['agentId'] : undefined),
22
+ ...input.extra,
23
+ metadata: {
24
+ taskId: state['taskId'],
25
+ correlationId: state['correlationId'],
26
+ ...(input.extra?.metadata ?? {}),
27
+ },
28
+ });
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ export async function completeGraphSpan(state, spanId, params, collectorOverride) {
35
+ const collector = collectorOverride ?? state['traceCollector'];
36
+ if (!collector?.completeNode || !spanId)
37
+ return;
38
+ try {
39
+ await collector.completeNode(spanId, params);
40
+ }
41
+ catch {
42
+ // Trace 失败不影响主路径
43
+ }
44
+ }
45
+ export async function withGraphSpan(state, spec, run) {
46
+ const spanId = await startGraphSpan(state, spec);
47
+ try {
48
+ const result = await run();
49
+ const usage = Array.isArray(result['__llmTokenUsage'])
50
+ ? result['__llmTokenUsage'].at(-1)
51
+ : undefined;
52
+ await completeGraphSpan(state, spanId, {
53
+ status: 'completed',
54
+ inputTokens: usage?.inputTokens,
55
+ outputTokens: usage?.outputTokens,
56
+ totalTokens: usage?.inputTokens != null || usage?.outputTokens != null
57
+ ? (usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0)
58
+ : undefined,
59
+ });
60
+ return spanId ? { ...result, lastSpanId: spanId } : result;
61
+ }
62
+ catch (err) {
63
+ await completeGraphSpan(state, spanId, {
64
+ status: 'failed',
65
+ error: err instanceof Error ? err.message : String(err),
66
+ });
67
+ throw err;
68
+ }
69
+ }
package/dist/index.d.ts CHANGED
@@ -16,6 +16,9 @@ export * from './common/index.js';
16
16
  export * from './registry/index.js';
17
17
  export * from './providers/index.js';
18
18
  export type { ITraceCollector, TraceEvent, TraceSession, ExecutionFlowEvent, ExecutionNodeEvent, TraceLinkEvent, TraceLinkNodeEvent, } from './contracts/trace-collector.js';
19
- export type { ExecutionContext, CallerType, GovernancePermissionCheckParams, GovernancePermissionCheckResult, PermissionDenyReason, } from '@gt-fe/eap-contracts';
20
- export { HttpGovernanceClient, AllowAllGovernanceClient, resolveExecutionContext, assertToolPermission, assertSkillPermission, PermissionDeniedError, GovernanceUnavailableError, } from './governance/index.js';
19
+ export type { ExecutionContext, CallerType, GovernancePermissionCheckParams, GovernancePermissionCheckResult, PermissionDenyReason, } from '@eap/contracts';
20
+ export { HttpGovernanceClient, AllowAllGovernanceClient, resolveExecutionContext, assertToolPermission, assertSkillPermission, permissionMessage, PermissionDeniedError, GovernanceUnavailableError, } from './governance/index.js';
21
21
  export type { IGovernanceClient } from './governance/index.js';
22
+ export { FileLlmTurnLog, InMemoryLlmTurnLog, createFileLlmTurnLogFromEnv, maskApiKey, resolveLlmTurnDir, } from './debug/llm-turn-log.js';
23
+ export { PostgresLlmTurnLog } from './debug/pg-llm-turn-log.js';
24
+ export type { ILlmTurnLog, LlmTurnRecord, LlmTurnIndexEntry, LlmTurnContext, LlmTurnLogKind, } from './debug/llm-turn-log.js';
package/dist/index.js CHANGED
@@ -29,4 +29,6 @@ export * from './common/index.js';
29
29
  export * from './registry/index.js';
30
30
  // LLM Providers
31
31
  export * from './providers/index.js';
32
- export { HttpGovernanceClient, AllowAllGovernanceClient, resolveExecutionContext, assertToolPermission, assertSkillPermission, PermissionDeniedError, GovernanceUnavailableError, } from './governance/index.js';
32
+ export { HttpGovernanceClient, AllowAllGovernanceClient, resolveExecutionContext, assertToolPermission, assertSkillPermission, permissionMessage, PermissionDeniedError, GovernanceUnavailableError, } from './governance/index.js';
33
+ export { FileLlmTurnLog, InMemoryLlmTurnLog, createFileLlmTurnLogFromEnv, maskApiKey, resolveLlmTurnDir, } from './debug/llm-turn-log.js';
34
+ export { PostgresLlmTurnLog } from './debug/pg-llm-turn-log.js';
@@ -13,21 +13,7 @@ export interface IPgPool {
13
13
  }>;
14
14
  }
15
15
  /**
16
- * CREATE TABLE compression_records (
17
- * id VARCHAR(36) PRIMARY KEY,
18
- * session_id VARCHAR(36) NOT NULL,
19
- * task_id VARCHAR(36) NULL,
20
- * original_message_count INT NOT NULL,
21
- * compressed_message_count INT NOT NULL,
22
- * original_tokens INT NOT NULL,
23
- * compressed_tokens INT NOT NULL,
24
- * strategy VARCHAR(20) NOT NULL,
25
- * summary_content TEXT NOT NULL,
26
- * created_by VARCHAR(36) NOT NULL DEFAULT 'system',
27
- * created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
28
- * INDEX idx_compression_session (session_id),
29
- * INDEX idx_compression_task (task_id)
30
- * );
16
+ * `compression_records` 不在现行 Runtime DDL 内。见 runtime/docs/ddl.md。
31
17
  */
32
18
  /**
33
19
  * ICompressionRepository — 压缩记录持久化接口
@@ -7,4 +7,4 @@ export declare class EchoLLMProvider implements ILLMProvider {
7
7
  invoke(messages: GraphMessage[], config: LLMNodeConfig): Promise<LLMResponse>;
8
8
  stream(messages: GraphMessage[], config: LLMNodeConfig): AsyncIterableIterator<LLMChunk>;
9
9
  }
10
- export declare function createDefaultLLMProvider(): ILLMProvider;
10
+ export declare function createDefaultLLMProvider(turnLog?: import('../debug/llm-turn-log.js').ILlmTurnLog): ILLMProvider;
@@ -7,8 +7,8 @@
7
7
  * - `call:echo-tool {"text":"hi"}` → arguments 使用解析后的 JSON
8
8
  * - `call:echo-tool hi` → arguments 兜底为 `{ text: 'hi' }`
9
9
  *
10
- * 仅用于本地闭环 (`EAP_TOOL_EXECUTION_MODE=mock`) 下驱动 ToolNode,
11
- * 让开发者无需接入真实 LLM 即可完成 tool 调用链路验证。
10
+ * 仅用于本地闭环(无真实 LLM)下驱动 ToolNode,
11
+ * 让开发者无需接入模型即可完成 tool 调用链路验证。
12
12
  */
13
13
  function parseSyntheticToolCall(content) {
14
14
  if (!content)
@@ -75,12 +75,13 @@ export class EchoLLMProvider {
75
75
  * - 否则 → EchoLLMProvider
76
76
  */
77
77
  import { HttpLLMProvider } from './http-llm-provider.js';
78
- export function createDefaultLLMProvider() {
78
+ export function createDefaultLLMProvider(turnLog) {
79
79
  if (process.env.OPENAI_API_KEY) {
80
80
  return new HttpLLMProvider({
81
81
  apiKey: process.env.OPENAI_API_KEY,
82
82
  baseUrl: process.env.OPENAI_BASE_URL ?? 'https://api.openai.com/v1',
83
83
  defaultModel: process.env.EAP_DEFAULT_MODEL ?? 'DeepSeek-V4-Pro',
84
+ turnLog,
84
85
  });
85
86
  }
86
87
  return new EchoLLMProvider();
@@ -8,18 +8,22 @@
8
8
  */
9
9
  import type { GraphMessage } from '../graph/contracts/executable-graph.js';
10
10
  import type { ILLMProvider, LLMNodeConfig, LLMResponse, LLMChunk } from '../graph/nodes/llm-node.js';
11
+ import { type ILlmTurnLog } from '../debug/llm-turn-log.js';
11
12
  export interface HttpLLMProviderConfig {
12
13
  apiKey: string;
13
14
  baseUrl?: string;
14
15
  defaultModel?: string;
15
16
  timeoutMs?: number;
17
+ turnLog?: ILlmTurnLog;
16
18
  }
17
19
  export declare class HttpLLMProvider implements ILLMProvider {
18
20
  private readonly apiKey;
19
21
  private readonly baseUrl;
20
22
  private readonly defaultModel;
21
23
  private readonly timeoutMs;
24
+ private readonly turnLog?;
22
25
  constructor(config: HttpLLMProviderConfig);
26
+ private writeTurn;
23
27
  invoke(messages: GraphMessage[], config: LLMNodeConfig): Promise<LLMResponse>;
24
28
  stream(messages: GraphMessage[], config: LLMNodeConfig): AsyncIterableIterator<LLMChunk>;
25
29
  private buildRequestBody;