@gt-fe/eap-runtime 0.0.8
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.
- package/README.md +323 -0
- package/dist/api/handlers/index.d.ts +3 -0
- package/dist/api/handlers/index.js +4 -0
- package/dist/api/handlers/invoke.handler.d.ts +77 -0
- package/dist/api/handlers/invoke.handler.js +411 -0
- package/dist/api/handlers/messages.handler.d.ts +53 -0
- package/dist/api/handlers/messages.handler.js +80 -0
- package/dist/api/handlers/stream.handler.d.ts +92 -0
- package/dist/api/handlers/stream.handler.js +432 -0
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +4 -0
- package/dist/api/middleware/auth.middleware.d.ts +96 -0
- package/dist/api/middleware/auth.middleware.js +158 -0
- package/dist/api/middleware/index.d.ts +1 -0
- package/dist/api/middleware/index.js +2 -0
- package/dist/api/routes/agent.routes.d.ts +44 -0
- package/dist/api/routes/agent.routes.js +204 -0
- package/dist/api/routes/index.d.ts +3 -0
- package/dist/api/routes/index.js +4 -0
- package/dist/api/routes/session.routes.d.ts +82 -0
- package/dist/api/routes/session.routes.js +169 -0
- package/dist/api/routes/task.routes.d.ts +75 -0
- package/dist/api/routes/task.routes.js +108 -0
- package/dist/checkpoint/checkpoint-serializer.d.ts +58 -0
- package/dist/checkpoint/checkpoint-serializer.js +81 -0
- package/dist/checkpoint/composite-checkpoint-saver.d.ts +42 -0
- package/dist/checkpoint/composite-checkpoint-saver.js +118 -0
- package/dist/checkpoint/contracts/checkpoint-saver.contract.d.ts +65 -0
- package/dist/checkpoint/contracts/checkpoint-saver.contract.js +12 -0
- package/dist/checkpoint/contracts/index.d.ts +1 -0
- package/dist/checkpoint/contracts/index.js +1 -0
- package/dist/checkpoint/file-checkpoint-saver.d.ts +19 -0
- package/dist/checkpoint/file-checkpoint-saver.js +98 -0
- package/dist/checkpoint/hybrid-checkpoint-saver.d.ts +130 -0
- package/dist/checkpoint/hybrid-checkpoint-saver.js +211 -0
- package/dist/checkpoint/in-memory-checkpoint-saver.d.ts +14 -0
- package/dist/checkpoint/in-memory-checkpoint-saver.js +46 -0
- package/dist/checkpoint/index.d.ts +16 -0
- package/dist/checkpoint/index.js +13 -0
- package/dist/checkpoint/mongo-checkpoint-saver.d.ts +38 -0
- package/dist/checkpoint/mongo-checkpoint-saver.js +56 -0
- package/dist/checkpoint/pg-checkpoint-saver.d.ts +59 -0
- package/dist/checkpoint/pg-checkpoint-saver.js +140 -0
- package/dist/checkpoint/redis-checkpoint-saver.d.ts +68 -0
- package/dist/checkpoint/redis-checkpoint-saver.js +173 -0
- package/dist/checkpoint/restore-checkpoint.d.ts +33 -0
- package/dist/checkpoint/restore-checkpoint.js +33 -0
- package/dist/common/agent-code.d.ts +10 -0
- package/dist/common/agent-code.js +12 -0
- package/dist/common/concurrency.d.ts +79 -0
- package/dist/common/concurrency.js +121 -0
- package/dist/common/health.d.ts +40 -0
- package/dist/common/health.js +37 -0
- package/dist/common/index.d.ts +5 -0
- package/dist/common/index.js +5 -0
- package/dist/common/logger.d.ts +34 -0
- package/dist/common/logger.js +38 -0
- package/dist/common/queue-consumer.d.ts +125 -0
- package/dist/common/queue-consumer.js +159 -0
- package/dist/common/trace-id.d.ts +2 -0
- package/dist/common/trace-id.js +8 -0
- package/dist/common/types.d.ts +104 -0
- package/dist/common/types.js +14 -0
- package/dist/contracts/index.d.ts +11 -0
- package/dist/contracts/index.js +10 -0
- package/dist/contracts/permission-checker.d.ts +10 -0
- package/dist/contracts/permission-checker.js +4 -0
- package/dist/contracts/trace-collector.d.ts +211 -0
- package/dist/contracts/trace-collector.js +13 -0
- package/dist/governance/governance-client.d.ts +38 -0
- package/dist/governance/governance-client.js +95 -0
- package/dist/governance/index.d.ts +7 -0
- package/dist/governance/index.js +5 -0
- package/dist/governance/permission-guard.d.ts +19 -0
- package/dist/governance/permission-guard.js +59 -0
- package/dist/graph/adapters/index.d.ts +3 -0
- package/dist/graph/adapters/index.js +3 -0
- package/dist/graph/adapters/langgraph-checkpointer.d.ts +40 -0
- package/dist/graph/adapters/langgraph-checkpointer.js +211 -0
- package/dist/graph/adapters/langgraph.adapter.d.ts +190 -0
- package/dist/graph/adapters/langgraph.adapter.js +1303 -0
- package/dist/graph/adapters/subgraph-compiler.d.ts +39 -0
- package/dist/graph/adapters/subgraph-compiler.js +248 -0
- package/dist/graph/condition-evaluator.d.ts +57 -0
- package/dist/graph/condition-evaluator.js +162 -0
- package/dist/graph/contracts/executable-graph.d.ts +340 -0
- package/dist/graph/contracts/executable-graph.js +10 -0
- package/dist/graph/contracts/index.d.ts +2 -0
- package/dist/graph/contracts/index.js +2 -0
- package/dist/graph/contracts/state-graph.contract.d.ts +76 -0
- package/dist/graph/contracts/state-graph.contract.js +11 -0
- package/dist/graph/hitl/hitl-handler.d.ts +199 -0
- package/dist/graph/hitl/hitl-handler.js +480 -0
- package/dist/graph/hitl/index.d.ts +1 -0
- package/dist/graph/hitl/index.js +1 -0
- package/dist/graph/index.d.ts +7 -0
- package/dist/graph/index.js +7 -0
- package/dist/graph/nodes/index.d.ts +2 -0
- package/dist/graph/nodes/index.js +2 -0
- package/dist/graph/nodes/llm-node.d.ts +182 -0
- package/dist/graph/nodes/llm-node.js +281 -0
- package/dist/graph/nodes/skill-bind-node.d.ts +32 -0
- package/dist/graph/nodes/skill-bind-node.js +116 -0
- package/dist/graph/nodes/tool-node.d.ts +75 -0
- package/dist/graph/nodes/tool-node.js +324 -0
- package/dist/graph/resolve-graph-prompt.d.ts +5 -0
- package/dist/graph/resolve-graph-prompt.js +75 -0
- package/dist/graph/retry/budget-aware-retry.d.ts +91 -0
- package/dist/graph/retry/budget-aware-retry.js +126 -0
- package/dist/graph/retry/index.d.ts +9 -0
- package/dist/graph/retry/index.js +8 -0
- package/dist/graph/retry/node-retry.d.ts +123 -0
- package/dist/graph/retry/node-retry.js +235 -0
- package/dist/graph/skill-audit-recorder.d.ts +126 -0
- package/dist/graph/skill-audit-recorder.js +114 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +32 -0
- package/dist/memory/compression/compression-stats.d.ts +109 -0
- package/dist/memory/compression/compression-stats.js +144 -0
- package/dist/memory/compression/compression.repository.d.ts +62 -0
- package/dist/memory/compression/compression.repository.js +71 -0
- package/dist/memory/compression/index.d.ts +10 -0
- package/dist/memory/compression/index.js +15 -0
- package/dist/memory/compression/llm-summarize.d.ts +58 -0
- package/dist/memory/compression/llm-summarize.js +192 -0
- package/dist/memory/compression/sliding-window.compression.d.ts +60 -0
- package/dist/memory/compression/sliding-window.compression.js +175 -0
- package/dist/memory/index.d.ts +4 -0
- package/dist/memory/index.js +7 -0
- package/dist/memory/long-term-memory.service.d.ts +94 -0
- package/dist/memory/long-term-memory.service.js +120 -0
- package/dist/memory/message-compression.service.d.ts +128 -0
- package/dist/memory/message-compression.service.js +69 -0
- package/dist/memory/pg-memory.repository.d.ts +44 -0
- package/dist/memory/pg-memory.repository.js +108 -0
- package/dist/memory/semantic-retrieval.service.d.ts +148 -0
- package/dist/memory/semantic-retrieval.service.js +238 -0
- package/dist/providers/echo-llm-provider.d.ts +10 -0
- package/dist/providers/echo-llm-provider.js +87 -0
- package/dist/providers/http-llm-provider.d.ts +35 -0
- package/dist/providers/http-llm-provider.js +404 -0
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.js +5 -0
- package/dist/providers/local-llm-provider.d.ts +27 -0
- package/dist/providers/local-llm-provider.js +105 -0
- package/dist/providers/resolve-llm-model.d.ts +13 -0
- package/dist/providers/resolve-llm-model.js +30 -0
- package/dist/providers/routing-llm-provider.d.ts +28 -0
- package/dist/providers/routing-llm-provider.js +79 -0
- package/dist/registry/agent-models.d.ts +1 -0
- package/dist/registry/agent-models.js +1 -0
- package/dist/registry/agent-registry-client.d.ts +56 -0
- package/dist/registry/agent-registry-client.js +36 -0
- package/dist/registry/definition-cache.d.ts +37 -0
- package/dist/registry/definition-cache.js +82 -0
- package/dist/registry/deployment-snapshot.d.ts +99 -0
- package/dist/registry/deployment-snapshot.js +542 -0
- package/dist/registry/graph-loader.d.ts +134 -0
- package/dist/registry/graph-loader.js +985 -0
- package/dist/registry/index.d.ts +9 -0
- package/dist/registry/index.js +9 -0
- package/dist/registry/preload-errors.d.ts +13 -0
- package/dist/registry/preload-errors.js +35 -0
- package/dist/registry/preload-flags.d.ts +22 -0
- package/dist/registry/preload-flags.js +44 -0
- package/dist/registry/skill-io-guard.d.ts +25 -0
- package/dist/registry/skill-io-guard.js +98 -0
- package/dist/registry/skill-tree.d.ts +27 -0
- package/dist/registry/skill-tree.js +183 -0
- package/dist/runtime/agent-runtime.d.ts +106 -0
- package/dist/runtime/agent-runtime.js +777 -0
- package/dist/runtime/dev-runtime.d.ts +69 -0
- package/dist/runtime/dev-runtime.js +199 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/storage-profile.d.ts +50 -0
- package/dist/runtime/storage-profile.js +138 -0
- package/dist/runtime/types.d.ts +120 -0
- package/dist/runtime/types.js +9 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.js +161 -0
- package/dist/session/compression-middleware.d.ts +126 -0
- package/dist/session/compression-middleware.js +230 -0
- package/dist/session/file-message.repository.d.ts +25 -0
- package/dist/session/file-message.repository.js +78 -0
- package/dist/session/file-session.repository.d.ts +72 -0
- package/dist/session/file-session.repository.js +212 -0
- package/dist/session/index.d.ts +13 -0
- package/dist/session/index.js +12 -0
- package/dist/session/message.service.d.ts +80 -0
- package/dist/session/message.service.js +128 -0
- package/dist/session/persist-turn-messages.d.ts +145 -0
- package/dist/session/persist-turn-messages.js +325 -0
- package/dist/session/pg-message.repository.d.ts +49 -0
- package/dist/session/pg-message.repository.js +149 -0
- package/dist/session/pg-session.repository.d.ts +46 -0
- package/dist/session/pg-session.repository.js +117 -0
- package/dist/session/session-context.service.d.ts +49 -0
- package/dist/session/session-context.service.js +66 -0
- package/dist/session/session.service.d.ts +194 -0
- package/dist/session/session.service.js +166 -0
- package/dist/sse/connection-event-logger.d.ts +21 -0
- package/dist/sse/connection-event-logger.js +33 -0
- package/dist/sse/contracts/index.d.ts +1 -0
- package/dist/sse/contracts/index.js +1 -0
- package/dist/sse/contracts/sse-emitter.contract.d.ts +74 -0
- package/dist/sse/contracts/sse-emitter.contract.js +11 -0
- package/dist/sse/event-buffer.d.ts +20 -0
- package/dist/sse/event-buffer.js +45 -0
- package/dist/sse/events/code-events.d.ts +19 -0
- package/dist/sse/events/code-events.js +9 -0
- package/dist/sse/events/collaboration-events.d.ts +37 -0
- package/dist/sse/events/collaboration-events.js +11 -0
- package/dist/sse/events/connection-events.d.ts +13 -0
- package/dist/sse/events/connection-events.js +9 -0
- package/dist/sse/events/file-events.d.ts +29 -0
- package/dist/sse/events/file-events.js +10 -0
- package/dist/sse/events/hitl-events.d.ts +33 -0
- package/dist/sse/events/hitl-events.js +10 -0
- package/dist/sse/events/index.d.ts +107 -0
- package/dist/sse/events/index.js +96 -0
- package/dist/sse/events/plan-events.d.ts +38 -0
- package/dist/sse/events/plan-events.js +11 -0
- package/dist/sse/events/reasoning-events.d.ts +35 -0
- package/dist/sse/events/reasoning-events.js +13 -0
- package/dist/sse/events/response-events.d.ts +25 -0
- package/dist/sse/events/response-events.js +12 -0
- package/dist/sse/events/session-events.d.ts +43 -0
- package/dist/sse/events/session-events.js +12 -0
- package/dist/sse/events/skill-events.d.ts +48 -0
- package/dist/sse/events/skill-events.js +11 -0
- package/dist/sse/events/stats-events.d.ts +44 -0
- package/dist/sse/events/stats-events.js +12 -0
- package/dist/sse/events/tool-events.d.ts +68 -0
- package/dist/sse/events/tool-events.js +14 -0
- package/dist/sse/index.d.ts +8 -0
- package/dist/sse/index.js +8 -0
- package/dist/sse/sse-connection-manager.d.ts +116 -0
- package/dist/sse/sse-connection-manager.js +185 -0
- package/dist/sse/sse-emitter.d.ts +108 -0
- package/dist/sse/sse-emitter.js +210 -0
- package/dist/sse/sse-heartbeat.d.ts +58 -0
- package/dist/sse/sse-heartbeat.js +76 -0
- package/dist/sse/trace-collector.d.ts +60 -0
- package/dist/sse/trace-collector.js +107 -0
- package/dist/task/contracts/index.d.ts +2 -0
- package/dist/task/contracts/index.js +1 -0
- package/dist/task/contracts/task-manager.contract.d.ts +160 -0
- package/dist/task/contracts/task-manager.contract.js +10 -0
- package/dist/task/index.d.ts +7 -0
- package/dist/task/index.js +5 -0
- package/dist/task/pg-task.repository.d.ts +47 -0
- package/dist/task/pg-task.repository.js +131 -0
- package/dist/task/task-progress-tracker.d.ts +67 -0
- package/dist/task/task-progress-tracker.js +80 -0
- package/dist/task/task-timeout.d.ts +70 -0
- package/dist/task/task-timeout.js +116 -0
- package/dist/task/task.service.d.ts +84 -0
- package/dist/task/task.service.js +233 -0
- package/dist/token/compression-budget-integration.d.ts +145 -0
- package/dist/token/compression-budget-integration.js +185 -0
- package/dist/token/context-window-budget.d.ts +113 -0
- package/dist/token/context-window-budget.js +134 -0
- package/dist/token/contracts/index.d.ts +1 -0
- package/dist/token/contracts/index.js +1 -0
- package/dist/token/contracts/token-budget.contract.d.ts +77 -0
- package/dist/token/contracts/token-budget.contract.js +11 -0
- package/dist/token/index.d.ts +12 -0
- package/dist/token/index.js +10 -0
- package/dist/token/pg-token-budget.repository.d.ts +39 -0
- package/dist/token/pg-token-budget.repository.js +116 -0
- package/dist/token/session-budget-checker.d.ts +54 -0
- package/dist/token/session-budget-checker.js +72 -0
- package/dist/token/token-budget-guard.d.ts +66 -0
- package/dist/token/token-budget-guard.js +97 -0
- package/dist/token/token-budget.service.d.ts +86 -0
- package/dist/token/token-budget.service.js +130 -0
- package/dist/token/token-detail-mode.d.ts +73 -0
- package/dist/token/token-detail-mode.js +107 -0
- package/dist/token/token-estimator.d.ts +80 -0
- package/dist/token/token-estimator.js +134 -0
- package/dist/token/token-stats.service.d.ts +88 -0
- package/dist/token/token-stats.service.js +94 -0
- package/dist/tool/create-tool-executor.d.ts +33 -0
- package/dist/tool/create-tool-executor.js +60 -0
- package/dist/tool/dev-http-executor.d.ts +18 -0
- package/dist/tool/dev-http-executor.js +96 -0
- package/dist/tool/dev-mcp-executor.d.ts +29 -0
- package/dist/tool/dev-mcp-executor.js +84 -0
- package/dist/tool/dev-tool-executor.d.ts +23 -0
- package/dist/tool/dev-tool-executor.js +55 -0
- package/dist/tool/file-artifact.d.ts +11 -0
- package/dist/tool/file-artifact.js +90 -0
- package/dist/tool/http-tool-executor.d.ts +17 -0
- package/dist/tool/http-tool-executor.js +158 -0
- package/dist/tool/index.d.ts +18 -0
- package/dist/tool/index.js +17 -0
- package/dist/tool/loaded-tool-definition.d.ts +35 -0
- package/dist/tool/loaded-tool-definition.js +9 -0
- package/dist/tool/local-mcp-client.d.ts +23 -0
- package/dist/tool/local-mcp-client.js +225 -0
- package/dist/tool/local-tool-executor.d.ts +37 -0
- package/dist/tool/local-tool-executor.js +249 -0
- package/dist/tool/mcp-tool-executor.d.ts +23 -0
- package/dist/tool/mcp-tool-executor.js +160 -0
- package/dist/tool/mock-tool-executor.d.ts +28 -0
- package/dist/tool/mock-tool-executor.js +65 -0
- package/dist/tool/system-prompt-builder.d.ts +114 -0
- package/dist/tool/system-prompt-builder.js +213 -0
- package/dist/tool/tool-confirmation.d.ts +29 -0
- package/dist/tool/tool-confirmation.js +81 -0
- package/dist/tool/tool-event-emitter.d.ts +58 -0
- package/dist/tool/tool-event-emitter.js +154 -0
- package/dist/tool/tool-execution-router.d.ts +25 -0
- package/dist/tool/tool-execution-router.js +50 -0
- package/dist/tool/tool-executor.interface.d.ts +85 -0
- package/dist/tool/tool-executor.interface.js +7 -0
- package/dist/tool/tool-orchestrator.d.ts +22 -0
- package/dist/tool/tool-orchestrator.js +104 -0
- package/dist/tool/tool-result-injector.d.ts +64 -0
- package/dist/tool/tool-result-injector.js +65 -0
- package/dist/utils/format-error.d.ts +4 -0
- package/dist/utils/format-error.js +4 -0
- package/dist/utils/http-error.d.ts +11 -0
- package/dist/utils/http-error.js +24 -0
- package/package.json +56 -0
|
@@ -0,0 +1,777 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentRuntime — Runtime SDK 门面
|
|
3
|
+
*
|
|
4
|
+
* Hosting 内嵌调用入口:execute / stream / resumeHitl / fork。
|
|
5
|
+
* 对外 HTTP 由 Hosting 提供;本地 eap dev 亦可直接实例化本类。
|
|
6
|
+
*
|
|
7
|
+
* @see 04-runtime-core/design.md §1.5 / §1.7
|
|
8
|
+
*/
|
|
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';
|
|
12
|
+
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';
|
|
14
|
+
import { resolveExecutionContext } from '../governance/governance-client.js';
|
|
15
|
+
import { resolveAgentCode } from '../common/agent-code.js';
|
|
16
|
+
import { resolveTraceId } from '../common/trace-id.js';
|
|
17
|
+
import { resolveSkillInputFromInvokeInput } from '../registry/skill-io-guard.js';
|
|
18
|
+
const HITL_TERMINAL_STATUSES = [
|
|
19
|
+
'success',
|
|
20
|
+
'failed',
|
|
21
|
+
'timeout',
|
|
22
|
+
'token_exhausted',
|
|
23
|
+
];
|
|
24
|
+
const DEFAULT_GRAPH_DSL = {
|
|
25
|
+
name: 'default-graph',
|
|
26
|
+
version: '1.0.0',
|
|
27
|
+
state: { messages: 'array' },
|
|
28
|
+
nodes: [{ id: 'llm', type: 'llm', config: {} }],
|
|
29
|
+
edges: [
|
|
30
|
+
{ from: '__start__', to: 'llm' },
|
|
31
|
+
{ from: 'llm', to: '__end__' },
|
|
32
|
+
],
|
|
33
|
+
config: { timeout: 60000 },
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* AgentRuntime — 管理 Graph 执行的 SDK 门面
|
|
37
|
+
*/
|
|
38
|
+
export class AgentRuntime {
|
|
39
|
+
deps;
|
|
40
|
+
constructor(deps) {
|
|
41
|
+
this.deps = deps;
|
|
42
|
+
if (typeof this.deps.hitlHandler.setTimeoutListener === 'function') {
|
|
43
|
+
this.deps.hitlHandler.setTimeoutListener((event) => this.handleHitlTimeout(event));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** 从 GraphLoader 解析 tool manifest 映射(invoke/stream/resume/fork 共用) */
|
|
47
|
+
async resolveLoadedTools(agentCode, agentVersion) {
|
|
48
|
+
if (!this.deps.graphLoader)
|
|
49
|
+
return undefined;
|
|
50
|
+
const { toLoadedToolMap } = await import('../tool/loaded-tool-definition.js');
|
|
51
|
+
return toLoadedToolMap(await this.deps.graphLoader.loadToolDefinitions(agentCode, { agentVersion }));
|
|
52
|
+
}
|
|
53
|
+
/** 从预装重载 Skill 定义(HITL/Fork 时 ephemeral 字段可能丢失) */
|
|
54
|
+
async resolveLoadedSkills(agentCode, userId, agentVersion) {
|
|
55
|
+
if (!this.deps.graphLoader)
|
|
56
|
+
return {};
|
|
57
|
+
const loadedSkills = await this.deps.graphLoader.loadSkillDefinitions(agentCode, {
|
|
58
|
+
userId,
|
|
59
|
+
agentVersion,
|
|
60
|
+
});
|
|
61
|
+
return { loadedSkills, skillDefinitions: toLoadedSkillMap(loadedSkills) };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 向会话预置种子消息(开发者 few-shot / 开场白)
|
|
65
|
+
*
|
|
66
|
+
* 专为 SDK `createAgentService({ messages })` 提供:在首条真实用户输入之前
|
|
67
|
+
* 一次性将开发者提供的对话上下文写入 session,后续 execute/stream 直接沿用现有
|
|
68
|
+
* “Session 历史 + 当前输入”途径。
|
|
69
|
+
*
|
|
70
|
+
* 行为:
|
|
71
|
+
* - `role` 仅接受 `user` / `assistant`;`system` 应写入 graph.json LLM 节点的
|
|
72
|
+
* `config.systemPrompt`(由 SystemPromptBuilder 拼平台规则 + 开发者提示词);
|
|
73
|
+
* `tool` 必须与前置 assistant.toolCalls 严格配对,不得单独作为种子。
|
|
74
|
+
* - 幂等语义(exactly-once):当 session 已存在任何历史消息时,直接返回
|
|
75
|
+
* `seeded: false`,不重复写入。
|
|
76
|
+
*
|
|
77
|
+
* @throws PARAMS_INVALID messages 为空、role 不在白名单、content 非字符串或为空
|
|
78
|
+
* @throws PARAMS_INVALID messageService 未注入(无存储无法写入)
|
|
79
|
+
*/
|
|
80
|
+
async seedSessionMessages(params) {
|
|
81
|
+
const agentCode = resolveAgentCode(params);
|
|
82
|
+
const ctx = resolveExecutionContext({
|
|
83
|
+
agentCode,
|
|
84
|
+
sessionId: params.sessionId,
|
|
85
|
+
executionContext: params.executionContext,
|
|
86
|
+
}, 'anonymous');
|
|
87
|
+
const userId = ctx.userId ?? 'anonymous';
|
|
88
|
+
if (!this.deps.messageService) {
|
|
89
|
+
throw new AppError(ErrorCodes.PARAMS_INVALID, 'seedSessionMessages requires messageService to be injected', { exposeMessage: true });
|
|
90
|
+
}
|
|
91
|
+
if (!Array.isArray(params.messages) || params.messages.length === 0) {
|
|
92
|
+
throw new AppError(ErrorCodes.PARAMS_INVALID, 'seedSessionMessages requires a non-empty messages array', { exposeMessage: true });
|
|
93
|
+
}
|
|
94
|
+
// role 白名单 + content 合法性校验(仅允许 user | assistant)
|
|
95
|
+
params.messages.forEach((message, index) => {
|
|
96
|
+
if (!message || typeof message !== 'object') {
|
|
97
|
+
throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}] must be an object`, { exposeMessage: true });
|
|
98
|
+
}
|
|
99
|
+
if (message.role !== 'user' && message.role !== 'assistant') {
|
|
100
|
+
throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}].role "${String(message.role)}" is not allowed; only 'user' and 'assistant' are accepted (system belongs to config.systemPrompt; tool must pair with prior assistant.toolCalls)`, { exposeMessage: true });
|
|
101
|
+
}
|
|
102
|
+
if (typeof message.content !== 'string' || message.content.length === 0) {
|
|
103
|
+
throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}].content must be a non-empty string`, { exposeMessage: true });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// 复用或新建 session(resolveSessionContext 已处理 sessionId 存在/采纳/新建 三种分支)
|
|
107
|
+
const sessionContext = await this.deps.sessionContextService.resolveSessionContext({
|
|
108
|
+
sessionId: params.sessionId,
|
|
109
|
+
userId,
|
|
110
|
+
agentId: agentCode,
|
|
111
|
+
agentVersion: params.agentVersion,
|
|
112
|
+
});
|
|
113
|
+
const sessionId = sessionContext.session.id;
|
|
114
|
+
// 幂等守护:既有历史则不重复写入,保证 exactly-once 语义
|
|
115
|
+
if (sessionContext.messages.length > 0) {
|
|
116
|
+
return { sessionId, seeded: false };
|
|
117
|
+
}
|
|
118
|
+
await this.deps.messageService.addMessages(params.messages.map((message) => ({
|
|
119
|
+
sessionId,
|
|
120
|
+
correlationId: ctx.correlationId,
|
|
121
|
+
role: message.role,
|
|
122
|
+
content: message.content,
|
|
123
|
+
toolCalls: message.toolCalls ?? undefined,
|
|
124
|
+
tokenCount: 0,
|
|
125
|
+
metadata: { seed: true },
|
|
126
|
+
})));
|
|
127
|
+
return { sessionId, seeded: true };
|
|
128
|
+
}
|
|
129
|
+
/** 同步执行 Graph(经 InvokeOrchestrator,内含 loadedTools 注入) */
|
|
130
|
+
async execute(params) {
|
|
131
|
+
const agentCode = resolveAgentCode(params);
|
|
132
|
+
const ctx = resolveExecutionContext({
|
|
133
|
+
agentCode,
|
|
134
|
+
sessionId: params.sessionId,
|
|
135
|
+
executionContext: params.executionContext,
|
|
136
|
+
}, 'anonymous');
|
|
137
|
+
const userId = ctx.userId ?? 'anonymous';
|
|
138
|
+
const response = await this.deps.invokeOrchestrator.execute({
|
|
139
|
+
agentCode,
|
|
140
|
+
input: params.input,
|
|
141
|
+
sessionId: params.sessionId,
|
|
142
|
+
taskId: params.taskId,
|
|
143
|
+
agentVersion: params.agentVersion,
|
|
144
|
+
config: params.config,
|
|
145
|
+
executionContext: ctx,
|
|
146
|
+
userId,
|
|
147
|
+
traceId: resolveTraceId(params),
|
|
148
|
+
}, userId);
|
|
149
|
+
return response.data;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 流式执行 Graph,产出 SSE 事件序列
|
|
153
|
+
*/
|
|
154
|
+
async *stream(params) {
|
|
155
|
+
const agentCode = resolveAgentCode(params);
|
|
156
|
+
const ctx = resolveExecutionContext({
|
|
157
|
+
agentCode,
|
|
158
|
+
sessionId: params.sessionId,
|
|
159
|
+
executionContext: params.executionContext,
|
|
160
|
+
}, 'anonymous');
|
|
161
|
+
const userId = ctx.userId ?? 'anonymous';
|
|
162
|
+
const correlationId = ctx.correlationId;
|
|
163
|
+
const sessionContext = await this.deps.sessionContextService.resolveSessionContext({
|
|
164
|
+
sessionId: params.sessionId,
|
|
165
|
+
userId,
|
|
166
|
+
agentId: agentCode,
|
|
167
|
+
agentVersion: params.agentVersion,
|
|
168
|
+
});
|
|
169
|
+
const sessionId = sessionContext.session.id;
|
|
170
|
+
if (sessionContext.isNew) {
|
|
171
|
+
yield this.toSSE('session.created', correlationId, '', {
|
|
172
|
+
sessionId,
|
|
173
|
+
userId,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
const task = await this.deps.taskService.create({
|
|
177
|
+
id: params.taskId,
|
|
178
|
+
agentId: agentCode,
|
|
179
|
+
sessionId,
|
|
180
|
+
userId,
|
|
181
|
+
correlationId,
|
|
182
|
+
input: params.input,
|
|
183
|
+
timeout: params.config?.timeout ?? 60_000,
|
|
184
|
+
});
|
|
185
|
+
await this.deps.taskService.updateStatus(task.id, 'running');
|
|
186
|
+
await persistUserTurnMessage(this.deps.messageService, {
|
|
187
|
+
sessionId,
|
|
188
|
+
taskId: task.id,
|
|
189
|
+
correlationId,
|
|
190
|
+
content: params.input,
|
|
191
|
+
});
|
|
192
|
+
yield this.toSSE('task.started', correlationId, task.id, { taskId: task.id, sessionId });
|
|
193
|
+
const loadOpts = { agentVersion: params.agentVersion };
|
|
194
|
+
const graphDsl = this.deps.graphLoader
|
|
195
|
+
? await this.deps.graphLoader.loadGraph(agentCode, loadOpts)
|
|
196
|
+
: DEFAULT_GRAPH_DSL;
|
|
197
|
+
const graph = await this.deps.graphEngine.buildGraph(graphDsl);
|
|
198
|
+
const loadedToolsBase = await this.resolveLoadedTools(agentCode, params.agentVersion);
|
|
199
|
+
const loadedSkills = this.deps.graphLoader
|
|
200
|
+
? await this.deps.graphLoader.loadSkillDefinitions(agentCode, {
|
|
201
|
+
...loadOpts,
|
|
202
|
+
userId,
|
|
203
|
+
})
|
|
204
|
+
: undefined;
|
|
205
|
+
const skillDefinitions = toLoadedSkillMap(loadedSkills);
|
|
206
|
+
// Skill scripts 虚拟 Tool 并入 loadedTools(同名不覆盖平台 Tool)
|
|
207
|
+
const loadedTools = mergeSkillScriptTools(loadedToolsBase, loadedSkills);
|
|
208
|
+
const promptRoots = this.deps.graphLoader
|
|
209
|
+
? await this.deps.graphLoader.resolvePromptRoots(agentCode, params.agentVersion)
|
|
210
|
+
: [];
|
|
211
|
+
const history = messageRecordsToGraphMessages(sessionContext.messages);
|
|
212
|
+
const messages = await buildTurnGraphMessages({
|
|
213
|
+
history,
|
|
214
|
+
input: params.input,
|
|
215
|
+
checkpointSaver: this.deps.checkpointSaver,
|
|
216
|
+
threadId: sessionId,
|
|
217
|
+
});
|
|
218
|
+
const startTime = Date.now();
|
|
219
|
+
let assistantText = '';
|
|
220
|
+
let toolCallReasoningContent;
|
|
221
|
+
let assistantReasoningContent;
|
|
222
|
+
const pendingToolCalls = [];
|
|
223
|
+
const toolResults = [];
|
|
224
|
+
try {
|
|
225
|
+
for await (const runtimeEvent of this.deps.graphEngine.stream(graph, {
|
|
226
|
+
messages,
|
|
227
|
+
userId,
|
|
228
|
+
delegationToken: ctx.delegationToken,
|
|
229
|
+
agentCode,
|
|
230
|
+
agentVersion: params.agentVersion,
|
|
231
|
+
correlationId,
|
|
232
|
+
taskId: task.id,
|
|
233
|
+
sessionId,
|
|
234
|
+
executionContext: ctx,
|
|
235
|
+
forceToolServer: params.config?.forceToolServer,
|
|
236
|
+
loadedTools,
|
|
237
|
+
loadedSkills,
|
|
238
|
+
skillDefinitions,
|
|
239
|
+
promptRoots,
|
|
240
|
+
skillInput: resolveSkillInputFromInvokeInput(params.input),
|
|
241
|
+
traceId: resolveTraceId(params),
|
|
242
|
+
traceCollector: this.deps.traceCollector,
|
|
243
|
+
})) {
|
|
244
|
+
if (runtimeEvent.type === 'tool.called') {
|
|
245
|
+
const p = runtimeEvent.payload;
|
|
246
|
+
if (p.toolCallId && p.toolName) {
|
|
247
|
+
pendingToolCalls.push({
|
|
248
|
+
id: p.toolCallId,
|
|
249
|
+
name: p.toolName,
|
|
250
|
+
arguments: p.arguments ?? {},
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
if (typeof p.reasoningContent === 'string' && p.reasoningContent) {
|
|
254
|
+
toolCallReasoningContent = p.reasoningContent;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (runtimeEvent.type === 'tool.result') {
|
|
258
|
+
const p = runtimeEvent.payload;
|
|
259
|
+
if (p.toolCallId && p.toolName) {
|
|
260
|
+
toolResults.push({
|
|
261
|
+
toolCallId: p.toolCallId,
|
|
262
|
+
toolName: p.toolName,
|
|
263
|
+
content: p.content,
|
|
264
|
+
success: p.success !== false,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const streamedReasoning = extractStreamReasoning(runtimeEvent.type, runtimeEvent.payload);
|
|
269
|
+
if (streamedReasoning)
|
|
270
|
+
assistantReasoningContent = streamedReasoning;
|
|
271
|
+
const delta = extractStreamTextDelta(runtimeEvent.type, runtimeEvent.payload);
|
|
272
|
+
if (delta) {
|
|
273
|
+
if (runtimeEvent.type === 'response.final') {
|
|
274
|
+
if (delta.trim())
|
|
275
|
+
assistantText = delta;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
assistantText += delta;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
yield this.runtimeEventToSSE(runtimeEvent, correlationId, task.id);
|
|
282
|
+
}
|
|
283
|
+
await this.deps.taskService.updateStatus(task.id, 'success');
|
|
284
|
+
await persistTurnTranscript(this.deps.messageService, {
|
|
285
|
+
sessionId,
|
|
286
|
+
taskId: task.id,
|
|
287
|
+
correlationId,
|
|
288
|
+
messages: buildTurnTranscriptFromStreamEvents({
|
|
289
|
+
pendingToolCalls,
|
|
290
|
+
toolResults,
|
|
291
|
+
assistantText,
|
|
292
|
+
toolCallReasoningContent,
|
|
293
|
+
assistantReasoningContent,
|
|
294
|
+
}),
|
|
295
|
+
});
|
|
296
|
+
yield this.toSSE('task.completed', correlationId, task.id, {
|
|
297
|
+
taskId: task.id,
|
|
298
|
+
duration: Date.now() - startTime,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
const hitlPaused = unwrapHitlPaused(err);
|
|
303
|
+
if (hitlPaused) {
|
|
304
|
+
await this.deps.taskService.updateStatus(task.id, 'waiting_hitl', buildHitlWaitingTaskOutput({
|
|
305
|
+
threadId: hitlPaused.threadId,
|
|
306
|
+
question: hitlPaused.question,
|
|
307
|
+
options: hitlPaused.options,
|
|
308
|
+
}));
|
|
309
|
+
await persistHitlWaitingPlaceholder(this.deps.messageService, {
|
|
310
|
+
sessionId,
|
|
311
|
+
taskId: task.id,
|
|
312
|
+
correlationId,
|
|
313
|
+
question: hitlPaused.question,
|
|
314
|
+
options: hitlPaused.options,
|
|
315
|
+
threadId: hitlPaused.threadId,
|
|
316
|
+
});
|
|
317
|
+
yield this.toSSE('hitl.waited', correlationId, task.id, buildHitlWaitedPayload(hitlPaused));
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
321
|
+
await this.deps.taskService.updateStatus(task.id, 'failed', message);
|
|
322
|
+
yield this.toSSE('task.error', correlationId, task.id, {
|
|
323
|
+
code: 'runtime.graph.execution_error',
|
|
324
|
+
message,
|
|
325
|
+
recoverable: false,
|
|
326
|
+
});
|
|
327
|
+
throw err;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* HITL 用户响应后继续执行(同步结果)。
|
|
332
|
+
*
|
|
333
|
+
* 内部消费 `streamResumeHitl`:成功为 success;再次确认暂停为 waiting_hitl(不抛错)。
|
|
334
|
+
* 需要过程事件(`hitl.resumed` / `tool.result` / `skill.completed`)时请直接迭代 streamResumeHitl。
|
|
335
|
+
*/
|
|
336
|
+
async resumeHitl(taskId, response) {
|
|
337
|
+
let output = '';
|
|
338
|
+
let waiting;
|
|
339
|
+
let sawWait = false;
|
|
340
|
+
let assistantText = '';
|
|
341
|
+
for await (const event of this.streamResumeHitl(taskId, response)) {
|
|
342
|
+
if (event.type === 'hitl.waited') {
|
|
343
|
+
sawWait = true;
|
|
344
|
+
const data = event.data;
|
|
345
|
+
waiting = {
|
|
346
|
+
question: typeof data.question === 'string' ? data.question : '',
|
|
347
|
+
options: Array.isArray(data.options) ? data.options.map(String) : [],
|
|
348
|
+
threadId: typeof data.threadId === 'string' ? data.threadId : '',
|
|
349
|
+
};
|
|
350
|
+
output = waiting.question;
|
|
351
|
+
}
|
|
352
|
+
const delta = extractStreamTextDelta(event.type, event.data);
|
|
353
|
+
if (delta) {
|
|
354
|
+
if (event.type === 'response.final') {
|
|
355
|
+
if (delta.trim())
|
|
356
|
+
assistantText = delta;
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
assistantText += delta;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
if (sawWait) {
|
|
364
|
+
return { taskId, status: 'waiting_hitl', output, hitl: waiting };
|
|
365
|
+
}
|
|
366
|
+
return { taskId, status: 'success', output: assistantText || output };
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* HITL 恢复的流式入口:新开一条 SSE(第一条 stream 在 `hitl.waited` 后已结束)。
|
|
370
|
+
*
|
|
371
|
+
* 事件顺序:`hitl.resumed` → 图内 `tool.*` / `skill.*` / `response.*` → `task.completed`
|
|
372
|
+
* 或再次 `hitl.waited`。拒绝/失败会发出同 `toolCallId` 的 `tool.error`。
|
|
373
|
+
*
|
|
374
|
+
* 从 GraphLoader `buildGraph` + Checkpoint GraphState 续跑,不用空的 `restore()`。
|
|
375
|
+
*/
|
|
376
|
+
async *streamResumeHitl(taskId, response) {
|
|
377
|
+
const ctx = await this.prepareHitlResume(taskId, response);
|
|
378
|
+
const { task, threadId, sessionId, correlationId } = ctx;
|
|
379
|
+
let resumedState;
|
|
380
|
+
try {
|
|
381
|
+
resumedState = await this.deps.hitlHandler.resume(taskId, response.choice, response.userId, threadId);
|
|
382
|
+
}
|
|
383
|
+
catch (err) {
|
|
384
|
+
if (err instanceof HitlAlreadyTimedOutError) {
|
|
385
|
+
throw new AppError(ErrorCodes.TASK_HITL_TIMEOUT, `Task ${taskId} HITL wait exceeded deadline`, { exposeMessage: true });
|
|
386
|
+
}
|
|
387
|
+
if (err instanceof HitlNotWaitingError) {
|
|
388
|
+
throw new AppError(ErrorCodes.TASK_HITL_INVALID_STATE, `Task ${taskId} is not waiting for HITL`, { exposeMessage: true });
|
|
389
|
+
}
|
|
390
|
+
throw err;
|
|
391
|
+
}
|
|
392
|
+
const claimed = await this.deps.taskService.tryUpdateStatus(taskId, 'waiting_hitl', 'running');
|
|
393
|
+
if (!claimed) {
|
|
394
|
+
throw new AppError(ErrorCodes.TASK_HITL_INVALID_STATE, `Task ${taskId} is no longer waiting for HITL`, { exposeMessage: true });
|
|
395
|
+
}
|
|
396
|
+
yield this.toSSE('hitl.resumed', correlationId, taskId, {
|
|
397
|
+
choice: response.choice,
|
|
398
|
+
userId: response.userId,
|
|
399
|
+
});
|
|
400
|
+
const checkpoint = await this.deps.checkpointSaver.load(threadId);
|
|
401
|
+
if (!checkpoint) {
|
|
402
|
+
throw new AppError(ErrorCodes.HOSTING_CHECKPOINT_NOT_FOUND, `Checkpoint not found for thread: ${threadId}`, { exposeMessage: true });
|
|
403
|
+
}
|
|
404
|
+
const agentCode = task.agentId;
|
|
405
|
+
const agentVersion = (typeof resumedState.agentVersion === 'string' && resumedState.agentVersion) ||
|
|
406
|
+
undefined;
|
|
407
|
+
const graph = await this.rebuildExecutableGraph(agentCode, agentVersion);
|
|
408
|
+
const loadedTools = resumedState.loadedTools ??
|
|
409
|
+
(await this.resolveLoadedTools(agentCode, agentVersion));
|
|
410
|
+
const skillFromState = resumedState.skillDefinitions;
|
|
411
|
+
const skillBundle = skillFromState
|
|
412
|
+
? {
|
|
413
|
+
skillDefinitions: skillFromState,
|
|
414
|
+
loadedSkills: resumedState.loadedSkills,
|
|
415
|
+
}
|
|
416
|
+
: await this.resolveLoadedSkills(agentCode, response.userId, agentVersion);
|
|
417
|
+
const continueState = {
|
|
418
|
+
...resumedState,
|
|
419
|
+
messages: resumedState.messages ?? [],
|
|
420
|
+
replaceCheckpointMessages: true,
|
|
421
|
+
sessionId: resumedState.sessionId ?? sessionId,
|
|
422
|
+
taskId,
|
|
423
|
+
correlationId: resumedState.correlationId ?? correlationId,
|
|
424
|
+
agentVersion: agentVersion ?? resumedState.agentVersion,
|
|
425
|
+
activatedSkill: resumedState.activatedSkill,
|
|
426
|
+
skillRoute: resumedState.skillRoute ??
|
|
427
|
+
(typeof resumedState.activatedSkill === 'string' && resumedState.activatedSkill
|
|
428
|
+
? 'skill'
|
|
429
|
+
: resumedState.skillRoute),
|
|
430
|
+
hitlChoice: resumedState.hitlChoice,
|
|
431
|
+
userId: response.userId,
|
|
432
|
+
delegationToken: response.executionContext?.delegationToken ?? resumedState.delegationToken,
|
|
433
|
+
executionContext: response.executionContext ?? resumedState.executionContext,
|
|
434
|
+
loadedTools,
|
|
435
|
+
...skillBundle,
|
|
436
|
+
traceId: resolveTraceId(resumedState) ?? resolveTraceId(checkpoint.state),
|
|
437
|
+
traceCollector: this.deps.traceCollector,
|
|
438
|
+
};
|
|
439
|
+
const startTime = Date.now();
|
|
440
|
+
let assistantText = '';
|
|
441
|
+
let toolCallReasoningContent;
|
|
442
|
+
let assistantReasoningContent;
|
|
443
|
+
const pendingToolCalls = [];
|
|
444
|
+
const toolResults = [];
|
|
445
|
+
try {
|
|
446
|
+
for await (const runtimeEvent of this.deps.graphEngine.stream(graph, continueState)) {
|
|
447
|
+
if (runtimeEvent.type === 'tool.called') {
|
|
448
|
+
const p = runtimeEvent.payload;
|
|
449
|
+
if (p.toolCallId && p.toolName) {
|
|
450
|
+
pendingToolCalls.push({
|
|
451
|
+
id: p.toolCallId,
|
|
452
|
+
name: p.toolName,
|
|
453
|
+
arguments: p.arguments ?? {},
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
if (typeof p.reasoningContent === 'string' && p.reasoningContent) {
|
|
457
|
+
toolCallReasoningContent = p.reasoningContent;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (runtimeEvent.type === 'tool.result' || runtimeEvent.type === 'tool.error') {
|
|
461
|
+
const p = runtimeEvent.payload;
|
|
462
|
+
if (p.toolCallId && p.toolName) {
|
|
463
|
+
toolResults.push({
|
|
464
|
+
toolCallId: p.toolCallId,
|
|
465
|
+
toolName: p.toolName,
|
|
466
|
+
content: p.content ?? p.error,
|
|
467
|
+
success: runtimeEvent.type === 'tool.result' && p.success !== false,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const streamedReasoning = extractStreamReasoning(runtimeEvent.type, runtimeEvent.payload);
|
|
472
|
+
if (streamedReasoning)
|
|
473
|
+
assistantReasoningContent = streamedReasoning;
|
|
474
|
+
const delta = extractStreamTextDelta(runtimeEvent.type, runtimeEvent.payload);
|
|
475
|
+
if (delta) {
|
|
476
|
+
if (runtimeEvent.type === 'response.final') {
|
|
477
|
+
if (delta.trim())
|
|
478
|
+
assistantText = delta;
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
assistantText += delta;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
yield this.runtimeEventToSSE(runtimeEvent, correlationId, taskId);
|
|
485
|
+
}
|
|
486
|
+
await this.deps.taskService.updateStatus(taskId, 'success', assistantText || undefined);
|
|
487
|
+
if (sessionId) {
|
|
488
|
+
await persistTurnTranscript(this.deps.messageService, {
|
|
489
|
+
sessionId,
|
|
490
|
+
taskId,
|
|
491
|
+
correlationId,
|
|
492
|
+
messages: buildTurnTranscriptFromStreamEvents({
|
|
493
|
+
pendingToolCalls,
|
|
494
|
+
toolResults,
|
|
495
|
+
assistantText,
|
|
496
|
+
toolCallReasoningContent,
|
|
497
|
+
assistantReasoningContent,
|
|
498
|
+
}),
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
yield this.toSSE('task.completed', correlationId, taskId, {
|
|
502
|
+
taskId,
|
|
503
|
+
duration: Date.now() - startTime,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
catch (err) {
|
|
507
|
+
const hitlPaused = unwrapHitlPaused(err);
|
|
508
|
+
if (hitlPaused) {
|
|
509
|
+
await this.deps.taskService.updateStatus(taskId, 'waiting_hitl', buildHitlWaitingTaskOutput({
|
|
510
|
+
threadId: hitlPaused.threadId,
|
|
511
|
+
question: hitlPaused.question,
|
|
512
|
+
options: hitlPaused.options,
|
|
513
|
+
}));
|
|
514
|
+
if (sessionId) {
|
|
515
|
+
await persistHitlWaitingPlaceholder(this.deps.messageService, {
|
|
516
|
+
sessionId,
|
|
517
|
+
taskId,
|
|
518
|
+
correlationId: task.correlationId,
|
|
519
|
+
question: hitlPaused.question,
|
|
520
|
+
options: hitlPaused.options,
|
|
521
|
+
threadId: hitlPaused.threadId,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
yield this.toSSE('hitl.waited', correlationId, taskId, buildHitlWaitedPayload(hitlPaused));
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
const appErr = err instanceof AppError ? err : AppError.fromUnknown(err);
|
|
528
|
+
const failMsg = appErr.exposeMessage ? appErr.message : '系统内部错误';
|
|
529
|
+
await this.deps.taskService.updateStatus(taskId, 'failed', failMsg);
|
|
530
|
+
yield this.toSSE('task.error', correlationId, taskId, {
|
|
531
|
+
code: appErr.code,
|
|
532
|
+
message: failMsg,
|
|
533
|
+
recoverable: false,
|
|
534
|
+
});
|
|
535
|
+
throw err instanceof AppError ? err : appErr;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* 从 Checkpoint Fork 并真实重新执行
|
|
540
|
+
*/
|
|
541
|
+
async fork(checkpointId, overrides = {}) {
|
|
542
|
+
const source = await this.deps.checkpointSaver.loadByCheckpointId(checkpointId);
|
|
543
|
+
if (!source) {
|
|
544
|
+
throw new AppError(ErrorCodes.HOSTING_CHECKPOINT_NOT_FOUND, `Checkpoint not found: ${checkpointId}`, { exposeMessage: true });
|
|
545
|
+
}
|
|
546
|
+
// 优先级:overrides(agentCode > 废弃 agentId)> Checkpoint state
|
|
547
|
+
const agentCode = overrides.agentCode ??
|
|
548
|
+
overrides.agentId ??
|
|
549
|
+
source.state.agentCode ??
|
|
550
|
+
source.state.agentId ??
|
|
551
|
+
'unknown-agent';
|
|
552
|
+
const userId = overrides.userId ?? source.state.userId;
|
|
553
|
+
const sessionId = overrides.sessionId ?? source.state.sessionId ?? randomUUID();
|
|
554
|
+
const originalTraceId = overrides.originalTraceId ??
|
|
555
|
+
source.state.traceId ??
|
|
556
|
+
source.checkpointId;
|
|
557
|
+
const newThreadId = randomUUID();
|
|
558
|
+
const forkedCheckpoint = await this.deps.checkpointSaver.fork(checkpointId, newThreadId, overrides);
|
|
559
|
+
const correlationId = randomUUID();
|
|
560
|
+
const task = await this.deps.taskService.create({
|
|
561
|
+
agentId: agentCode,
|
|
562
|
+
sessionId,
|
|
563
|
+
userId,
|
|
564
|
+
correlationId,
|
|
565
|
+
input: { forkFrom: checkpointId, overrides },
|
|
566
|
+
timeout: 600_000,
|
|
567
|
+
});
|
|
568
|
+
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
|
+
const agentVersion = overrides.agentVersion ??
|
|
583
|
+
(typeof source.state.agentVersion === 'string' ? source.state.agentVersion : undefined);
|
|
584
|
+
const graph = await this.rebuildExecutableGraph(agentCode, agentVersion);
|
|
585
|
+
const loadedToolsBase = await this.resolveLoadedTools(agentCode, agentVersion);
|
|
586
|
+
const skillBundle = await this.resolveLoadedSkills(agentCode, userId, agentVersion);
|
|
587
|
+
const loadedTools = mergeSkillScriptTools(loadedToolsBase, skillBundle.loadedSkills);
|
|
588
|
+
try {
|
|
589
|
+
const result = await this.deps.graphEngine.invoke(graph, {
|
|
590
|
+
...forkedCheckpoint.state,
|
|
591
|
+
agentVersion,
|
|
592
|
+
userId,
|
|
593
|
+
delegationToken: overrides.executionContext?.delegationToken,
|
|
594
|
+
executionContext: overrides.executionContext,
|
|
595
|
+
loadedTools,
|
|
596
|
+
...skillBundle,
|
|
597
|
+
traceId: newTraceId,
|
|
598
|
+
traceCollector: this.deps.traceCollector,
|
|
599
|
+
});
|
|
600
|
+
await this.deps.taskService.updateStatus(task.id, 'success', result.output);
|
|
601
|
+
if (this.deps.recordForkRelation) {
|
|
602
|
+
await this.deps.recordForkRelation({
|
|
603
|
+
originalTraceId,
|
|
604
|
+
forkedTraceId: newTraceId,
|
|
605
|
+
checkpointId,
|
|
606
|
+
overrides,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
return {
|
|
610
|
+
newTraceId,
|
|
611
|
+
originalTraceId,
|
|
612
|
+
newTaskId: task.id,
|
|
613
|
+
checkpointId,
|
|
614
|
+
status: 'completed',
|
|
615
|
+
output: result.output,
|
|
616
|
+
tokenUsage: {
|
|
617
|
+
input_tokens: result.tokenUsage.input,
|
|
618
|
+
output_tokens: result.tokenUsage.output,
|
|
619
|
+
total_tokens: result.tokenUsage.total,
|
|
620
|
+
},
|
|
621
|
+
compareUrl: `/traces/compare?traceId1=${originalTraceId}&traceId2=${newTraceId}`,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
catch (err) {
|
|
625
|
+
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
626
|
+
await this.deps.taskService.updateStatus(task.id, 'failed', message);
|
|
627
|
+
return {
|
|
628
|
+
newTraceId,
|
|
629
|
+
originalTraceId,
|
|
630
|
+
newTaskId: task.id,
|
|
631
|
+
checkpointId,
|
|
632
|
+
status: 'failed',
|
|
633
|
+
output: message,
|
|
634
|
+
tokenUsage: { input_tokens: 0, output_tokens: 0, total_tokens: 0 },
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
async prepareHitlResume(taskId, response) {
|
|
639
|
+
const task = await this.deps.taskService.getTask(taskId);
|
|
640
|
+
if (!task) {
|
|
641
|
+
throw new AppError(ErrorCodes.TASK_NOT_FOUND, `Task not found: ${taskId}`, {
|
|
642
|
+
exposeMessage: true,
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
if (HITL_TERMINAL_STATUSES.includes(task.status)) {
|
|
646
|
+
throw new AppError(ErrorCodes.TASK_HITL_INVALID_STATE, `Task ${taskId} is already ${task.status}`, { exposeMessage: true });
|
|
647
|
+
}
|
|
648
|
+
const waiting = this.deps.hitlHandler.getWaitingState(taskId);
|
|
649
|
+
if (!waiting && task.status !== 'waiting_hitl') {
|
|
650
|
+
throw new AppError(ErrorCodes.TASK_HITL_INVALID_STATE, `Task ${taskId} is not waiting for HITL`, { exposeMessage: true });
|
|
651
|
+
}
|
|
652
|
+
const sessionId = typeof task.sessionId === 'string' && task.sessionId.trim()
|
|
653
|
+
? task.sessionId.trim()
|
|
654
|
+
: undefined;
|
|
655
|
+
const threadId = waiting?.threadId ??
|
|
656
|
+
(typeof response.threadId === 'string' && response.threadId.trim()
|
|
657
|
+
? response.threadId.trim()
|
|
658
|
+
: undefined) ??
|
|
659
|
+
extractHitlThreadId(task.output) ??
|
|
660
|
+
sessionId;
|
|
661
|
+
if (!threadId) {
|
|
662
|
+
throw new AppError(ErrorCodes.TASK_HITL_INVALID_STATE, `Task ${taskId} 缺少 sessionId/hitlThreadId,无法冷恢复 HITL Checkpoint`, { exposeMessage: true });
|
|
663
|
+
}
|
|
664
|
+
const checkpoint = await this.deps.checkpointSaver.load(threadId);
|
|
665
|
+
const persisted = readPersistedHitlWaiting(checkpoint?.state);
|
|
666
|
+
const behavior = persisted?.config.timeoutBehavior ?? 'error';
|
|
667
|
+
if (persisted &&
|
|
668
|
+
isHitlDeadlinePassed(persisted) &&
|
|
669
|
+
behavior !== 'auto-confirm') {
|
|
670
|
+
await this.finalizeHitlTimeout({
|
|
671
|
+
taskId,
|
|
672
|
+
threadId,
|
|
673
|
+
behavior,
|
|
674
|
+
timeoutMs: persisted.config.timeout ?? 0,
|
|
675
|
+
question: persisted.config.question,
|
|
676
|
+
options: persisted.config.options,
|
|
677
|
+
toolCallId: persisted.toolCallId,
|
|
678
|
+
toolName: persisted.toolName,
|
|
679
|
+
toolSpanId: persisted.toolSpanId,
|
|
680
|
+
skillSpanId: persisted.skillSpanId,
|
|
681
|
+
});
|
|
682
|
+
throw new AppError(ErrorCodes.TASK_HITL_TIMEOUT, `Task ${taskId} HITL wait exceeded deadline`, { exposeMessage: true });
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
task,
|
|
686
|
+
threadId,
|
|
687
|
+
sessionId,
|
|
688
|
+
correlationId: task.correlationId || threadId,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
async rebuildExecutableGraph(agentCode, agentVersion) {
|
|
692
|
+
const graphDsl = this.deps.graphLoader && typeof this.deps.graphLoader.loadGraph === 'function'
|
|
693
|
+
? await this.deps.graphLoader.loadGraph(agentCode, { agentVersion })
|
|
694
|
+
: DEFAULT_GRAPH_DSL;
|
|
695
|
+
return this.deps.graphEngine.buildGraph(graphDsl);
|
|
696
|
+
}
|
|
697
|
+
async handleHitlTimeout(event) {
|
|
698
|
+
if (event.behavior === 'auto-confirm') {
|
|
699
|
+
const choice = event.defaultChoice ?? 'confirm';
|
|
700
|
+
try {
|
|
701
|
+
for await (const _ of this.streamResumeHitl(event.taskId, {
|
|
702
|
+
choice,
|
|
703
|
+
userId: '__system_timeout__',
|
|
704
|
+
threadId: event.threadId,
|
|
705
|
+
})) {
|
|
706
|
+
// 系统代确认:无客户端 SSE,只把图跑完并落 Task
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
catch {
|
|
710
|
+
await this.finalizeHitlTimeout({ ...event, behavior: 'error' });
|
|
711
|
+
}
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
await this.finalizeHitlTimeout(event);
|
|
715
|
+
}
|
|
716
|
+
async finalizeHitlTimeout(event) {
|
|
717
|
+
const task = await this.deps.taskService.getTask(event.taskId);
|
|
718
|
+
if (!task || task.status !== 'waiting_hitl') {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
const collector = this.deps.traceCollector;
|
|
722
|
+
const error = event.behavior === 'cancel' ? 'HITL cancelled by timeout' : 'HITL wait timed out';
|
|
723
|
+
if (collector) {
|
|
724
|
+
if (event.toolSpanId) {
|
|
725
|
+
try {
|
|
726
|
+
await collector.completeNode(event.toolSpanId, { status: 'failed', error });
|
|
727
|
+
}
|
|
728
|
+
catch {
|
|
729
|
+
// ignore
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if (event.skillSpanId) {
|
|
733
|
+
try {
|
|
734
|
+
await collector.completeNode(event.skillSpanId, { status: 'failed', error });
|
|
735
|
+
}
|
|
736
|
+
catch {
|
|
737
|
+
// ignore
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
const status = event.behavior === 'cancel' ? 'failed' : 'timeout';
|
|
742
|
+
try {
|
|
743
|
+
await this.deps.taskService.updateStatus(event.taskId, status, {
|
|
744
|
+
hitlTimeout: true,
|
|
745
|
+
timeoutBehavior: event.behavior,
|
|
746
|
+
toolCallId: event.toolCallId,
|
|
747
|
+
toolName: event.toolName,
|
|
748
|
+
error,
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
catch {
|
|
752
|
+
// 可能已终态
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
toSSE(type, correlationId, taskId, data) {
|
|
756
|
+
return {
|
|
757
|
+
type,
|
|
758
|
+
data,
|
|
759
|
+
correlationId,
|
|
760
|
+
taskId,
|
|
761
|
+
timestamp: new Date().toISOString(),
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
runtimeEventToSSE(event, correlationId, taskId) {
|
|
765
|
+
return {
|
|
766
|
+
type: event.type,
|
|
767
|
+
data: event.payload,
|
|
768
|
+
correlationId: event.correlationId ?? correlationId,
|
|
769
|
+
taskId: event.taskId ?? taskId,
|
|
770
|
+
timestamp: event.timestamp,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/** 工厂:从运行时依赖组装 AgentRuntime */
|
|
775
|
+
export function createAgentRuntime(deps) {
|
|
776
|
+
return new AgentRuntime(deps);
|
|
777
|
+
}
|