@gt-fe/eap-runtime 0.0.32 → 0.0.34

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 (45) hide show
  1. package/dist/api/handlers/invoke.handler.d.ts +0 -1
  2. package/dist/api/handlers/invoke.handler.js +25 -6
  3. package/dist/api/handlers/stream.handler.d.ts +0 -1
  4. package/dist/api/handlers/stream.handler.js +22 -18
  5. package/dist/common/types.d.ts +4 -3
  6. package/dist/graph/adapters/langgraph.adapter.d.ts +0 -1
  7. package/dist/graph/adapters/langgraph.adapter.js +117 -82
  8. package/dist/graph/adapters/skill-entry-span.js +4 -1
  9. package/dist/graph/adapters/subgraph-compiler.js +11 -9
  10. package/dist/graph/contracts/executable-graph.d.ts +2 -0
  11. package/dist/graph/execution-trace-context.d.ts +3 -0
  12. package/dist/graph/nodes/llm-node.d.ts +7 -2
  13. package/dist/graph/nodes/llm-node.js +8 -3
  14. package/dist/graph/nodes/skill-bind-node.d.ts +1 -1
  15. package/dist/graph/nodes/skill-bind-node.js +6 -1
  16. package/dist/graph/nodes/tool-node.js +1 -0
  17. package/dist/providers/http-llm-provider.js +27 -10
  18. package/dist/registry/graph-loader.d.ts +11 -0
  19. package/dist/registry/graph-loader.js +28 -4
  20. package/dist/runtime/agent-runtime.d.ts +7 -0
  21. package/dist/runtime/agent-runtime.js +69 -38
  22. package/dist/runtime/types.d.ts +1 -1
  23. package/dist/session/persist-turn-messages.d.ts +10 -2
  24. package/dist/session/persist-turn-messages.js +35 -10
  25. package/dist/sse/events/index.d.ts +6 -4
  26. package/dist/sse/events/index.js +3 -1
  27. package/dist/sse/events/reasoning-events.d.ts +24 -2
  28. package/dist/sse/events/reasoning-events.js +3 -1
  29. package/dist/sse/events/skill-events.d.ts +2 -0
  30. package/dist/task/contracts/task-manager.contract.d.ts +2 -2
  31. package/dist/task/contracts/task-manager.contract.js +1 -1
  32. package/dist/task/index.d.ts +2 -0
  33. package/dist/task/index.js +1 -0
  34. package/dist/task/task-abort.d.ts +14 -0
  35. package/dist/task/task-abort.js +33 -0
  36. package/dist/task/task-timeout.d.ts +11 -0
  37. package/dist/task/task-timeout.js +40 -4
  38. package/dist/task/task.service.js +11 -5
  39. package/dist/tool/index.d.ts +1 -1
  40. package/dist/tool/index.js +1 -1
  41. package/dist/tool/mcp-list.d.ts +3 -1
  42. package/dist/tool/mcp-list.js +4 -1
  43. package/dist/tool/system-prompt-builder.d.ts +10 -8
  44. package/dist/tool/system-prompt-builder.js +36 -26
  45. package/package.json +1 -1
@@ -81,7 +81,6 @@ export declare class InvokeOrchestrator {
81
81
  execute(req: InvokeRequest, userId: string): Promise<InvokeResponse>;
82
82
  private buildResponse;
83
83
  private buildErrorResponse;
84
- private isTimeoutError;
85
84
  private persistInvokeUsages;
86
85
  }
87
86
  /** @deprecated 请直接从 `graph/hitl/hitl-handler` 导入;此处保留 re-export 兼容旧测试引用 */
@@ -26,8 +26,9 @@ import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
26
26
  import { classifyRuntimeError } from '../../utils/http-error.js';
27
27
  import { DEFAULT_GRAPH_TIMEOUT_MS } from '../../common/types.js';
28
28
  import { resolveTaskTimeoutMs } from '../../task/resolve-task-timeout.js';
29
+ import { taskAbortKindOf } from '../../task/task-abort.js';
29
30
  import { runWithExecutionTrace } from '../../graph/execution-trace-context.js';
30
- import { resolveAgentSystemPromptFromLoader } from '../../registry/graph-loader.js';
31
+ import { resolveAgentDisplayNameFromLoader, resolveAgentSystemPromptFromLoader, } from '../../registry/graph-loader.js';
31
32
  import { toLoadedToolMap } from '../../tool/loaded-tool-definition.js';
32
33
  import { toLoadedSkillMap } from '../../tool/system-prompt-builder.js';
33
34
  import { assembleLoadedTools } from '../../tool/harness-primitives.js';
@@ -204,6 +205,7 @@ export class InvokeOrchestrator {
204
205
  ? (await this.graphLoader.resolveAgentDir(agentCode, req.agentVersion)) ?? undefined
205
206
  : undefined;
206
207
  const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.graphLoader, agentCode, req.agentVersion);
208
+ const agentName = await resolveAgentDisplayNameFromLoader(this.graphLoader, agentCode, req.agentVersion);
207
209
  // 模型上下文:Checkpoint 已有 messages 则压它;压成功用 __set 写回最新 Checkpoint。Message 库不改。
208
210
  const facing = await loadModelFacingHistory({
209
211
  sessionHistory: messageRecordsToGraphMessages(sessionContext.messages),
@@ -261,6 +263,7 @@ export class InvokeOrchestrator {
261
263
  promptRoots,
262
264
  agentDir,
263
265
  agentSystemPrompt,
266
+ agentName,
264
267
  traceId: resolveTraceId(req),
265
268
  traceCollector: this.traceCollector,
266
269
  abortSignal,
@@ -311,8 +314,8 @@ export class InvokeOrchestrator {
311
314
  status: 'token_exhausted',
312
315
  });
313
316
  }
314
- // ─── 错误处理:Timeout ───
315
- if (this.isTimeoutError(error)) {
317
+ const abortKind = taskAbortKindOf(error, abortSignal);
318
+ if (abortKind === 'timeout') {
316
319
  await this.taskService.tryUpdateStatus(task.id, 'running', 'timeout');
317
320
  return this.buildResponse(traceId, {
318
321
  taskId: task.id,
@@ -323,6 +326,25 @@ export class InvokeOrchestrator {
323
326
  status: 'timeout',
324
327
  });
325
328
  }
329
+ if (abortKind === 'cancelled') {
330
+ const moved = await this.taskService.tryUpdateStatus(task.id, 'running', 'cancelled');
331
+ if (!moved) {
332
+ await this.taskService.tryUpdateStatus(task.id, 'waiting_hitl', 'cancelled');
333
+ }
334
+ // @gt-fe/eap-contracts 的 InvokeStatus 还没有 cancelled。这次不改包,响应体仍写 cancelled。
335
+ return {
336
+ code: 'runtime.task.cancelled',
337
+ data: {
338
+ taskId: task.id,
339
+ sessionId,
340
+ output: 'Task cancelled',
341
+ tokenUsage,
342
+ duration: Date.now() - startTime,
343
+ status: 'cancelled',
344
+ },
345
+ traceId,
346
+ };
347
+ }
326
348
  // ─── 错误处理:通用错误 ───
327
349
  const classified = classifyRuntimeError(error);
328
350
  const fuse = classified.code === ErrorCodes.GRAPH_MAX_ITERATIONS ? classified : undefined;
@@ -430,9 +452,6 @@ export class InvokeOrchestrator {
430
452
  }
431
453
  return { code: 'runtime.graph.execution_error', data, traceId };
432
454
  }
433
- isTimeoutError(error) {
434
- return error instanceof Error && /timed out/i.test(error.message);
435
- }
436
455
  async persistInvokeUsages(taskId, sessionId) {
437
456
  if (!this.checkpointSaver)
438
457
  return [];
@@ -98,5 +98,4 @@ export declare class StreamOrchestrator {
98
98
  * 发射错误事件并关闭连接
99
99
  */
100
100
  private emitErrorAndClose;
101
- private isTimeoutError;
102
101
  }
@@ -19,7 +19,7 @@
19
19
  * @see design.md §1.5 /stream 内部执行逻辑
20
20
  * @see requirements.md Requirement 11 AC2
21
21
  */
22
- import { buildHitlWaitingTaskOutput, buildTurnTranscriptFromStreamEvents, loadModelFacingHistory, turnGraphMessagesAfterCompression, extractStreamReasoning, applyStreamTextDelta, createStreamAssistantBuckets, visibleStreamAssistantText, streamAssistantTexts, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, appendTerminalToolResult, } from '../../session/persist-turn-messages.js';
22
+ import { buildHitlWaitingTaskOutput, buildTurnTranscriptFromStreamEvents, loadModelFacingHistory, turnGraphMessagesAfterCompression, applyStreamReasoningEvent, applyStreamTextDelta, createStreamAssistantBuckets, createStreamReasoningState, resolvedStreamReasoning, visibleStreamAssistantText, streamAssistantTexts, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, appendTerminalToolResult, } from '../../session/persist-turn-messages.js';
23
23
  import { collectPublicArtifactLinks, collectPublicArtifactLinksFromEvent, injectPublicArtifactLinks, mergeArtifactLinks, } from '../../tool/inject-public-artifact-links.js';
24
24
  import { SessionBudgetExhaustedError } from '../../token/session-budget-checker.js';
25
25
  import { TokenBudgetExhaustedError } from '../../token/token-budget-guard.js';
@@ -31,7 +31,7 @@ import { SSEEmitter } from '../../sse/sse-emitter.js';
31
31
  import { resolveExecutionContext } from '../../governance/governance-client.js';
32
32
  import { resolveSkillInputFromInvokeInput } from '../../registry/skill-io-guard.js';
33
33
  import { ErrorCodes } from '@gt-fe/eap-contracts';
34
- import { resolveAgentSystemPromptFromLoader } from '../../registry/graph-loader.js';
34
+ import { resolveAgentDisplayNameFromLoader, resolveAgentSystemPromptFromLoader, } from '../../registry/graph-loader.js';
35
35
  import { classifyRuntimeError } from '../../utils/http-error.js';
36
36
  import { toLoadedToolMap } from '../../tool/loaded-tool-definition.js';
37
37
  import { toLoadedSkillMap } from '../../tool/system-prompt-builder.js';
@@ -42,6 +42,7 @@ import { buildHitlWaitedPayload } from '../../graph/hitl/hitl-handler.js';
42
42
  import { resolveTraceId } from '../../common/trace-id.js';
43
43
  import { DEFAULT_GRAPH_TIMEOUT_MS } from '../../common/types.js';
44
44
  import { resolveTaskTimeoutMs } from '../../task/resolve-task-timeout.js';
45
+ import { taskAbortKindOf } from '../../task/task-abort.js';
45
46
  import { runWithExecutionTrace } from '../../graph/execution-trace-context.js';
46
47
  // ─────────────────────────────────────────────
47
48
  // Placeholder Graph DSL (Agent Registry 接入前)
@@ -222,6 +223,7 @@ export class StreamOrchestrator {
222
223
  ? (await this.graphLoader.resolveAgentDir(agentCode, req.agentVersion)) ?? undefined
223
224
  : undefined;
224
225
  const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.graphLoader, agentCode, req.agentVersion);
226
+ const agentName = await resolveAgentDisplayNameFromLoader(this.graphLoader, agentCode, req.agentVersion);
225
227
  // 模型上下文:Checkpoint 已有 messages 则压它;压成功用 __set 写回最新 Checkpoint。Message 库不改。
226
228
  const facing = await loadModelFacingHistory({
227
229
  sessionHistory: messageRecordsToGraphMessages(sessionContext.messages),
@@ -265,8 +267,8 @@ export class StreamOrchestrator {
265
267
  });
266
268
  const skillInput = resolveSkillInputFromInvokeInput(req.input);
267
269
  const assistantBuckets = createStreamAssistantBuckets();
270
+ const reasoningState = createStreamReasoningState();
268
271
  let toolCallReasoningContent;
269
- let assistantReasoningContent;
270
272
  const pendingToolCalls = [];
271
273
  const toolResults = [];
272
274
  const artifactLinks = [];
@@ -293,6 +295,7 @@ export class StreamOrchestrator {
293
295
  promptRoots,
294
296
  agentDir,
295
297
  agentSystemPrompt,
298
+ agentName,
296
299
  traceId: resolveTraceId(req),
297
300
  traceCollector: this.traceCollector,
298
301
  abortSignal,
@@ -323,15 +326,11 @@ export class StreamOrchestrator {
323
326
  arguments: p.arguments ?? {},
324
327
  });
325
328
  }
326
- if (typeof p.reasoningContent === 'string' && p.reasoningContent) {
327
- toolCallReasoningContent = p.reasoningContent;
328
- }
329
+ toolCallReasoningContent = resolvedStreamReasoning(reasoningState);
329
330
  }
330
331
  appendTerminalToolResult(runtimeEvent.type, runtimeEvent.payload, toolResults);
331
332
  collectPublicArtifactLinksFromEvent(runtimeEvent.type, runtimeEvent.payload, artifactLinks);
332
- const streamedReasoning = extractStreamReasoning(runtimeEvent.type, runtimeEvent.payload);
333
- if (streamedReasoning)
334
- assistantReasoningContent = streamedReasoning;
333
+ applyStreamReasoningEvent(reasoningState, runtimeEvent.type, runtimeEvent.payload);
335
334
  applyStreamTextDelta(assistantBuckets, runtimeEvent.type, runtimeEvent.payload);
336
335
  if (shouldEmitTokenSse(tokenDetail, runtimeEvent.type)) {
337
336
  emitter.emit(this.runtimeEventToSSE(runtimeEvent, correlationId, taskId));
@@ -367,13 +366,21 @@ export class StreamOrchestrator {
367
366
  this.emitErrorAndClose(emitter, correlationId, taskId, error);
368
367
  return;
369
368
  }
370
- // ─── 错误处理:Timeout ───
371
- if (this.isTimeoutError(error)) {
369
+ const abortKind = taskAbortKindOf(error, abortSignal);
370
+ if (abortKind === 'timeout' || abortKind === 'cancelled') {
372
371
  await recordTaskTokenUsages(this.taskService, taskId, this.tokenStatsService.list(taskId));
373
- await this.taskService.tryUpdateStatus(taskId, 'running', 'timeout');
372
+ if (abortKind === 'timeout') {
373
+ await this.taskService.tryUpdateStatus(taskId, 'running', 'timeout');
374
+ }
375
+ else {
376
+ const moved = await this.taskService.tryUpdateStatus(taskId, 'running', 'cancelled');
377
+ if (!moved) {
378
+ await this.taskService.tryUpdateStatus(taskId, 'waiting_hitl', 'cancelled');
379
+ }
380
+ }
374
381
  emitter.emit(this.buildSSEEvent('task.error', correlationId, taskId, {
375
- code: ErrorCodes.TASK_TIMEOUT,
376
- message: 'Task execution timed out',
382
+ code: abortKind === 'timeout' ? ErrorCodes.TASK_TIMEOUT : 'runtime.task.cancelled',
383
+ message: abortKind === 'timeout' ? 'Task execution timed out' : 'Task cancelled',
377
384
  recoverable: false,
378
385
  }));
379
386
  emitter.close();
@@ -439,7 +446,7 @@ export class StreamOrchestrator {
439
446
  assistantText: injected,
440
447
  finishPoints: graph.finishPoints,
441
448
  toolCallReasoningContent,
442
- assistantReasoningContent,
449
+ assistantReasoningContent: resolvedStreamReasoning(reasoningState),
443
450
  }),
444
451
  assistantTokenCount: tokenStats.total.output ?? 0,
445
452
  assistantDurationMs: duration,
@@ -487,7 +494,4 @@ export class StreamOrchestrator {
487
494
  }));
488
495
  emitter.close();
489
496
  }
490
- isTimeoutError(error) {
491
- return error instanceof Error && /timed out/i.test(error.message);
492
- }
493
497
  }
@@ -58,11 +58,12 @@ export interface RuntimeRunContext {
58
58
  /**
59
59
  * Task 执行状态(Runtime 内部使用)
60
60
  *
61
- * 状态流转:pending → running → success | failed | timeout | token_exhausted | waiting_hitl
61
+ * 状态流转:pending → running → success | failed | timeout | token_exhausted | cancelled | waiting_hitl
62
62
  *
63
- * waiting_hitl:等待人工介入确认,可通过 HITL API 恢复为 running
63
+ * waiting_hitl:等待人工介入确认,可通过 HITL API 恢复为 running。
64
+ * cancelled:调用方停止这一轮(Hosting `POST /tasks/:id/cancel`),不是超时。
64
65
  */
65
- export type TaskStatus = 'pending' | 'running' | 'waiting_hitl' | 'success' | 'failed' | 'timeout' | 'token_exhausted';
66
+ export type TaskStatus = 'pending' | 'running' | 'waiting_hitl' | 'success' | 'failed' | 'timeout' | 'token_exhausted' | 'cancelled';
66
67
  /**
67
68
  * Runtime 执行上下文绑定状态(非平台 Session 生命周期)
68
69
  *
@@ -127,7 +127,6 @@ export declare class LangGraphAdapter implements IStateGraphEngine {
127
127
  private createNodeFunction;
128
128
  private createNodeFunctionBody;
129
129
  private createSkillBindNodeFunction;
130
- private buildSkillCatalog;
131
130
  private createToolNodeFunction;
132
131
  private createLLMNodeFunction;
133
132
  /**
@@ -23,7 +23,7 @@ import { ToolResultInjector } from '../../tool/tool-result-injector.js';
23
23
  import { fileArtifactFromToolOutput } from '../../tool/file-artifact.js';
24
24
  import { EchoLLMProvider } from '../../providers/echo-llm-provider.js';
25
25
  import { ConditionEvaluator } from '../condition-evaluator.js';
26
- import { SystemPromptBuilder, buildToolCatalog, loadedToolsToApiTools, resolveLlmPromptInputs } from '../../tool/system-prompt-builder.js';
26
+ import { SystemPromptBuilder, buildSkillCatalog, loadedToolsToApiTools, resolveLlmPromptInputs, spokenName } from '../../tool/system-prompt-builder.js';
27
27
  import { resolveJailFromState, resolveJailRoots } from '../../tool/harness-primitives.js';
28
28
  import os from 'node:os';
29
29
  import { bindSelectedSkill } from '../nodes/skill-bind-node.js';
@@ -39,6 +39,81 @@ import { recordSkillEntrySpan, skillEntryInvocationFromTool } from './skill-entr
39
39
  import { llmSpanInput, llmSpanOutputFromResult, withGraphSpan } from '../trace-span.js';
40
40
  import { executionTraceFromState, getExecutionStream, getExecutionTrace, runWithExecutionTrace, } from '../execution-trace-context.js';
41
41
  import { isRetryableToolFailure, resolveToolRetryConfig, sleepToolRetry, toolRetryDelayMs, } from '../../tool/tool-retry.js';
42
+ function emitReasoningStartedIfNeeded(live, model, timestamp) {
43
+ if (live.reasoningStartedThisTurn)
44
+ return;
45
+ live.reasoningStartedThisTurn = true;
46
+ live.iteration += 1;
47
+ live.sink({
48
+ type: 'reasoning.started',
49
+ taskId: undefined,
50
+ correlationId: undefined,
51
+ timestamp,
52
+ payload: {
53
+ iterationCount: live.iteration,
54
+ model,
55
+ },
56
+ });
57
+ }
58
+ function nodeGraphExtra(live, nodeId) {
59
+ return {
60
+ node: nodeId,
61
+ ...(live.graphId ? { graphId: live.graphId } : {}),
62
+ };
63
+ }
64
+ function emitLlmStreamDelta(live, nodeId, model, delta) {
65
+ const timestamp = new Date().toISOString();
66
+ const extra = nodeGraphExtra(live, nodeId);
67
+ if (delta.reasoningContent) {
68
+ emitReasoningStartedIfNeeded(live, model, timestamp);
69
+ live.sink({
70
+ type: 'reasoning.chunk',
71
+ taskId: undefined,
72
+ correlationId: undefined,
73
+ timestamp,
74
+ payload: {
75
+ content: delta.reasoningContent,
76
+ index: live.reasoningChunkIndex++,
77
+ ...extra,
78
+ },
79
+ });
80
+ }
81
+ if (delta.content) {
82
+ emitReasoningStartedIfNeeded(live, model, timestamp);
83
+ live.streamedTextThisTurn = true;
84
+ live.sink({
85
+ type: 'response.chunk',
86
+ taskId: undefined,
87
+ correlationId: undefined,
88
+ timestamp,
89
+ payload: {
90
+ content: delta.content,
91
+ index: live.chunkIndex++,
92
+ ...extra,
93
+ },
94
+ });
95
+ }
96
+ }
97
+ function emitReasoningCompletedIfAny(live, nodeId, next) {
98
+ if (!live)
99
+ return;
100
+ const added = Array.isArray(next.messages) ? next.messages[0] : undefined;
101
+ const reasoning = added && typeof added === 'object' && typeof added.reasoningContent === 'string'
102
+ ? added.reasoningContent
103
+ : '';
104
+ if (!reasoning)
105
+ return;
106
+ live.sink({
107
+ type: 'reasoning.completed',
108
+ taskId: undefined,
109
+ correlationId: undefined,
110
+ timestamp: new Date().toISOString(),
111
+ payload: {
112
+ content: reasoning,
113
+ ...nodeGraphExtra(live, nodeId),
114
+ },
115
+ });
116
+ }
42
117
  /**
43
118
  * 传给 LangGraph 的 `recursionLimit`:一轮调度(superstep)+1。
44
119
  * 同一步里并行的多个节点只 +1。ReAct 的 llm 再 tool 大约 +2。
@@ -306,6 +381,7 @@ export class LangGraphAdapter {
306
381
  alreadyInSkillSpan: progress.inSkillSubgraph,
307
382
  });
308
383
  if (catalogSkill) {
384
+ const skillName = spokenName(progress.skillName, catalogSkill.skillId) ?? catalogSkill.skillId;
309
385
  stream.sink({
310
386
  type: 'skill.started',
311
387
  taskId,
@@ -313,7 +389,7 @@ export class LangGraphAdapter {
313
389
  timestamp,
314
390
  payload: {
315
391
  skillId: catalogSkill.skillId,
316
- skillName: progress.skillName || catalogSkill.skillId,
392
+ skillName,
317
393
  path: catalogSkill.path,
318
394
  ...(progress.skillSpace ? { skillSpace: progress.skillSpace } : {}),
319
395
  ...(progress.skillVersion ? { skillVersion: progress.skillVersion } : {}),
@@ -326,7 +402,7 @@ export class LangGraphAdapter {
326
402
  timestamp,
327
403
  payload: {
328
404
  skillId: catalogSkill.skillId,
329
- skillName: catalogSkill.skillId,
405
+ skillName,
330
406
  },
331
407
  });
332
408
  }
@@ -460,6 +536,7 @@ export class LangGraphAdapter {
460
536
  // agentId 已废弃:channel 保留仅为旧 Checkpoint 反序列化兼容,新代码只写 agentCode
461
537
  agentId: ephemeral(''),
462
538
  agentCode: ephemeral(''),
539
+ agentName: ephemeral(undefined),
463
540
  agentVersion: ephemeral(undefined),
464
541
  delegationToken: ephemeral(''),
465
542
  correlationId: ephemeral(''),
@@ -594,15 +671,25 @@ export class LangGraphAdapter {
594
671
  sink: enqueue,
595
672
  eventContext: { taskId: state.taskId, correlationId: state.correlationId },
596
673
  chunkIndex: 0,
674
+ reasoningChunkIndex: 0,
597
675
  iteration: 0,
598
676
  reasoningStartedThisTurn: false,
599
677
  streamedTextThisTurn: false,
678
+ graphId: graph.id,
600
679
  };
601
680
  const traceCtx = executionTraceFromState(state);
602
681
  const graphSignal = graphAbortSignal(state);
682
+ let graphIterator;
683
+ const stopGraphIterator = () => {
684
+ const iterator = graphIterator;
685
+ if (!iterator?.return)
686
+ return;
687
+ void iterator.return().catch(() => undefined);
688
+ };
603
689
  const onGraphAbort = () => {
604
690
  failure = graphSignal ? abortReason(graphSignal) : new Error('aborted');
605
691
  finished = true;
692
+ stopGraphIterator();
606
693
  signal();
607
694
  };
608
695
  graphSignal?.addEventListener('abort', onGraphAbort, { once: true });
@@ -617,9 +704,19 @@ export class LangGraphAdapter {
617
704
  const streamIterator = await withGraphAbort(Promise.resolve(ref.compiled.stream(input, langGraphRunOptions({
618
705
  configurable: this.checkpointConfigurable(state, graph),
619
706
  streamMode: 'updates',
707
+ ...(graphSignal ? { signal: graphSignal } : {}),
620
708
  }))), graphSignal);
621
- for await (const update of streamIterator) {
622
- for (const event of this.convertUpdatesToEvents(update, graph.id, () => streamBag.chunkIndex++, ref.silentNodeIds, ref.subgraphNodeIds)) {
709
+ const iterator = streamIterator[Symbol.asyncIterator]();
710
+ graphIterator = iterator;
711
+ while (true) {
712
+ if (graphSignal?.aborted) {
713
+ failure = failure ?? abortReason(graphSignal);
714
+ break;
715
+ }
716
+ const next = await iterator.next();
717
+ if (next.done)
718
+ break;
719
+ for (const event of this.convertUpdatesToEvents(next.value, graph.id, () => streamBag.chunkIndex++, ref.silentNodeIds, ref.subgraphNodeIds)) {
623
720
  enqueue(event);
624
721
  }
625
722
  }
@@ -665,6 +762,7 @@ export class LangGraphAdapter {
665
762
  abortDeadlineMs,
666
763
  }, () => ref.compiled.invoke(input, langGraphRunOptions({
667
764
  configurable: this.checkpointConfigurable(state, graph),
765
+ ...(abortSignal ? { signal: abortSignal } : {}),
668
766
  }))), abortSignal);
669
767
  }
670
768
  catch (error) {
@@ -856,7 +954,7 @@ export class LangGraphAdapter {
856
954
  correlationId: undefined,
857
955
  timestamp: new Date().toISOString(),
858
956
  payload: {
859
- skillName: patch.skillName || patch.activatedSkill,
957
+ skillName: spokenName(patch.skillName, patch.activatedSkill) ?? patch.activatedSkill,
860
958
  skillId: patch.activatedSkill,
861
959
  confidence: 1,
862
960
  },
@@ -898,20 +996,6 @@ export class LangGraphAdapter {
898
996
  };
899
997
  };
900
998
  }
901
- buildSkillCatalog(defs) {
902
- if (!defs)
903
- return undefined;
904
- const rows = Object.values(defs).map((skill) => {
905
- const desc = (skill.description ?? '').replace(/\s+/g, ' ').trim();
906
- const entry = skill.entryFile ?? (skill.bundlePath ? `${skill.bundlePath}/SKILL.md` : '');
907
- const title = skill.name && skill.name !== skill.skillId ? `${skill.skillId}(${skill.name})` : skill.skillId;
908
- const pathLine = entry ? `\n SKILL.md: ${entry}` : '';
909
- return `- **${title}**${desc ? `:${desc}` : ''}${pathLine}`;
910
- });
911
- if (rows.length === 0)
912
- return undefined;
913
- return `# 可用技能\n要用某个技能时,调用 read_file 读取其 SKILL.md。脚本和参考文档默认在同一目录。\n${rows.join('\n')}`;
914
- }
915
999
  createToolNodeFunction(node) {
916
1000
  const toolName = node.config?.['tool'] ?? node.id;
917
1001
  const defaultArgs = node.config?.['arguments'] ?? {};
@@ -1061,6 +1145,7 @@ export class LangGraphAdapter {
1061
1145
  const resolvedPrompt = resolveGraphPrompt(baseConfig.systemPrompt, promptRoots);
1062
1146
  const loadedToolsBase = state['loadedTools'];
1063
1147
  const agentCode = typeof state['agentCode'] === 'string' ? state['agentCode'] : undefined;
1148
+ const agentName = typeof state['agentName'] === 'string' ? state['agentName'] : undefined;
1064
1149
  const skillDefs = state['skillDefinitions'];
1065
1150
  const execCtx = state['executionContext'];
1066
1151
  const userId = (typeof execCtx?.userId === 'string' && execCtx.userId) || state['userId'];
@@ -1092,8 +1177,7 @@ export class LangGraphAdapter {
1092
1177
  const toolNames = loadedTools ? Object.keys(loadedTools) : [];
1093
1178
  console.log(`[LLM-DEBUG] node=${node.id} loadedTools=${toolNames.length}:`, toolNames);
1094
1179
  }
1095
- const catalog = includeSkillCatalog ? this.buildSkillCatalog(skillDefs) : undefined;
1096
- const toolCatalog = disableTools ? undefined : buildToolCatalog(loadedTools);
1180
+ const catalog = includeSkillCatalog ? buildSkillCatalog(skillDefs) : undefined;
1097
1181
  const jail = resolveJailFromState({
1098
1182
  agentDir: state['agentDir'],
1099
1183
  userId,
@@ -1122,7 +1206,7 @@ export class LangGraphAdapter {
1122
1206
  agentPrompt: promptInputs.agentPrompt,
1123
1207
  nodePrompt: promptInputs.nodePrompt,
1124
1208
  skillCatalog: catalog,
1125
- toolCatalog,
1209
+ agentName,
1126
1210
  agentCode,
1127
1211
  userId,
1128
1212
  workingDirectory,
@@ -1151,39 +1235,14 @@ export class LangGraphAdapter {
1151
1235
  if (stream) {
1152
1236
  stream.reasoningStartedThisTurn = false;
1153
1237
  stream.streamedTextThisTurn = false;
1238
+ stream.reasoningChunkIndex = 0;
1154
1239
  }
1155
1240
  const llmNode = new LLMNode(node.id, finalConfig, this.llmProvider, enableStream, enableStream
1156
- ? (text) => {
1241
+ ? (delta) => {
1157
1242
  const live = getExecutionStream();
1158
- if (!live || !text)
1243
+ if (!live)
1159
1244
  return;
1160
- const timestamp = new Date().toISOString();
1161
- if (!live.reasoningStartedThisTurn) {
1162
- live.reasoningStartedThisTurn = true;
1163
- live.iteration += 1;
1164
- live.sink({
1165
- type: 'reasoning.started',
1166
- taskId: undefined,
1167
- correlationId: undefined,
1168
- timestamp,
1169
- payload: {
1170
- iterationCount: live.iteration,
1171
- model: finalConfig.model,
1172
- },
1173
- });
1174
- }
1175
- live.streamedTextThisTurn = true;
1176
- live.sink({
1177
- type: 'response.chunk',
1178
- taskId: undefined,
1179
- correlationId: undefined,
1180
- timestamp,
1181
- payload: {
1182
- content: text,
1183
- index: live.chunkIndex++,
1184
- node: node.id,
1185
- },
1186
- });
1245
+ emitLlmStreamDelta(live, node.id, finalConfig.model, delta);
1187
1246
  }
1188
1247
  : undefined, this.interceptors);
1189
1248
  const toolsForSpan = disableTools || !loadedTools
@@ -1207,6 +1266,9 @@ export class LangGraphAdapter {
1207
1266
  },
1208
1267
  }, async () => {
1209
1268
  const next = await llmNode.execute(state);
1269
+ if (enableStream) {
1270
+ emitReasoningCompletedIfAny(getExecutionStream(), node.id, next);
1271
+ }
1210
1272
  const hydratedUpdate = loadedTools && loadedTools !== loadedToolsBase ? { loadedTools } : {};
1211
1273
  if (!silent)
1212
1274
  return { ...next, ...hydratedUpdate };
@@ -1552,23 +1614,17 @@ export class LangGraphAdapter {
1552
1614
  continue;
1553
1615
  }
1554
1616
  const hasText = typeof msg.content === 'string' && msg.content.trim().length > 0;
1555
- if ((hasText || msg.reasoningContent) &&
1556
- (!streamedTextThisTurn || msg.reasoningContent)) {
1557
- if (streamedTextThisTurn && !msg.reasoningContent)
1558
- continue;
1617
+ if (hasText && !streamedTextThisTurn) {
1559
1618
  yield {
1560
1619
  type: 'response.chunk',
1561
1620
  taskId: undefined,
1562
1621
  correlationId: undefined,
1563
1622
  timestamp,
1564
1623
  payload: {
1565
- content: streamedTextThisTurn ? '' : (msg.content ?? ''),
1624
+ content: msg.content ?? '',
1566
1625
  index: nextChunkIndex(),
1567
1626
  node: nodeId,
1568
1627
  graphId,
1569
- ...(msg.reasoningContent
1570
- ? { reasoningContent: msg.reasoningContent }
1571
- : {}),
1572
1628
  },
1573
1629
  };
1574
1630
  }
@@ -1579,26 +1635,8 @@ export class LangGraphAdapter {
1579
1635
  continue;
1580
1636
  if (subgraphNodeIds.has(nodeId) && streamedTextThisTurn)
1581
1637
  continue;
1582
- // 已通过 onChunk 推送过 token,避免整段重复
1583
- if (streamedTextThisTurn) {
1584
- // token 已流过:仍补发一条带 reasoning 的标记 chunk(空 content),供落库回传
1585
- if (msg.reasoningContent) {
1586
- yield {
1587
- type: 'response.chunk',
1588
- taskId: undefined,
1589
- correlationId: undefined,
1590
- timestamp,
1591
- payload: {
1592
- content: '',
1593
- index: nextChunkIndex(),
1594
- node: nodeId,
1595
- graphId,
1596
- reasoningContent: msg.reasoningContent,
1597
- },
1598
- };
1599
- }
1638
+ if (streamedTextThisTurn)
1600
1639
  continue;
1601
- }
1602
1640
  yield {
1603
1641
  type: 'response.chunk',
1604
1642
  taskId: undefined,
@@ -1609,9 +1647,6 @@ export class LangGraphAdapter {
1609
1647
  index: nextChunkIndex(),
1610
1648
  node: nodeId,
1611
1649
  graphId,
1612
- ...(msg.reasoningContent
1613
- ? { reasoningContent: msg.reasoningContent }
1614
- : {}),
1615
1650
  },
1616
1651
  };
1617
1652
  continue;
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import { classifySkillFile, classifySkillSpace, isReadTool, } from './skill-read.js';
12
12
  import { completeGraphSpan, startGraphSpan } from '../trace-span.js';
13
+ import { spokenName } from '../../tool/system-prompt-builder.js';
13
14
  function declaredSkillVersion(skillId, version) {
14
15
  const raw = version?.trim();
15
16
  if (!raw || raw === skillId)
@@ -27,9 +28,10 @@ export function skillTraceIdentity(state, skillId, fallbackPath) {
27
28
  const bundle = info?.bundlePath || fallbackPath;
28
29
  const skillSpace = bundle ? classifySkillSpace(bundle, { agentDir, userId }) : undefined;
29
30
  const skillVersion = declaredSkillVersion(skillId, info?.version);
31
+ const skillName = spokenName(info?.name, undefined);
30
32
  return {
31
33
  skillId,
32
- ...(info?.name ? { skillName: info.name } : {}),
34
+ ...(skillName ? { skillName } : {}),
33
35
  ...(skillVersion ? { skillVersion } : {}),
34
36
  ...(skillSpace ? { skillSpace } : {}),
35
37
  };
@@ -39,6 +41,7 @@ export function skillSpanFields(identity, extra) {
39
41
  ...(identity.skillVersion ? { version: identity.skillVersion } : {}),
40
42
  input: {
41
43
  skillId: identity.skillId,
44
+ ...(identity.skillName ? { skillName: identity.skillName } : {}),
42
45
  ...(identity.skillSpace ? { skillSpace: identity.skillSpace } : {}),
43
46
  ...(extra?.path ? { path: extra.path } : {}),
44
47
  ...(extra?.source ? { source: extra.source } : {}),