@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,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionContextService — 会话上下文解析服务
|
|
3
|
+
*
|
|
4
|
+
* Agent Invoke API 调用入口的编排逻辑:
|
|
5
|
+
* - sessionId 已存在且 Runtime 有壳 → 加载 Session + 历史消息
|
|
6
|
+
* - sessionId 由上游分配但 Runtime 无壳 → 采纳该 ID 建壳,并仍按 sessionId 加载 Message
|
|
7
|
+
* - sessionId 为空 → 创建新 Session,返回空历史
|
|
8
|
+
*
|
|
9
|
+
* @see requirements.md Requirement 11 AC9, AC10
|
|
10
|
+
*/
|
|
11
|
+
import type { SessionRecord, MessageRecord } from './session.service.js';
|
|
12
|
+
import { SessionService } from './session.service.js';
|
|
13
|
+
import { MessageService } from './message.service.js';
|
|
14
|
+
/**
|
|
15
|
+
* 解析会话上下文的输入参数
|
|
16
|
+
*/
|
|
17
|
+
export interface ResolveSessionParams {
|
|
18
|
+
/** 会话 ID(可选,不传则创建新会话) */
|
|
19
|
+
sessionId?: string;
|
|
20
|
+
/** 用户 ID */
|
|
21
|
+
userId: string;
|
|
22
|
+
/** Agent ID */
|
|
23
|
+
agentId: string;
|
|
24
|
+
/** 预装版本,写入 session.metadata.agentVersion(续聊回退) */
|
|
25
|
+
agentVersion?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 解析后的会话上下文
|
|
29
|
+
*/
|
|
30
|
+
export interface SessionContext {
|
|
31
|
+
/** Session 记录 */
|
|
32
|
+
session: SessionRecord;
|
|
33
|
+
/** 历史消息列表(新会话为空数组) */
|
|
34
|
+
messages: MessageRecord[];
|
|
35
|
+
/** 是否为新创建的会话 */
|
|
36
|
+
isNew: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare class SessionContextService {
|
|
39
|
+
private readonly sessionService;
|
|
40
|
+
private readonly messageService;
|
|
41
|
+
constructor(sessionService: SessionService, messageService: MessageService);
|
|
42
|
+
/**
|
|
43
|
+
* 解析会话上下文
|
|
44
|
+
*
|
|
45
|
+
* - sessionId 存在:加载 Session 及其历史消息
|
|
46
|
+
* - sessionId 为空:创建新 Session,返回空消息列表
|
|
47
|
+
*/
|
|
48
|
+
resolveSessionContext(params: ResolveSessionParams): Promise<SessionContext>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionContextService — 会话上下文解析服务
|
|
3
|
+
*
|
|
4
|
+
* Agent Invoke API 调用入口的编排逻辑:
|
|
5
|
+
* - sessionId 已存在且 Runtime 有壳 → 加载 Session + 历史消息
|
|
6
|
+
* - sessionId 由上游分配但 Runtime 无壳 → 采纳该 ID 建壳,并仍按 sessionId 加载 Message
|
|
7
|
+
* - sessionId 为空 → 创建新 Session,返回空历史
|
|
8
|
+
*
|
|
9
|
+
* @see requirements.md Requirement 11 AC9, AC10
|
|
10
|
+
*/
|
|
11
|
+
// ─────────────────────────────────────────────
|
|
12
|
+
// SessionContextService Implementation
|
|
13
|
+
// ─────────────────────────────────────────────
|
|
14
|
+
export class SessionContextService {
|
|
15
|
+
sessionService;
|
|
16
|
+
messageService;
|
|
17
|
+
constructor(sessionService, messageService) {
|
|
18
|
+
this.sessionService = sessionService;
|
|
19
|
+
this.messageService = messageService;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 解析会话上下文
|
|
23
|
+
*
|
|
24
|
+
* - sessionId 存在:加载 Session 及其历史消息
|
|
25
|
+
* - sessionId 为空:创建新 Session,返回空消息列表
|
|
26
|
+
*/
|
|
27
|
+
async resolveSessionContext(params) {
|
|
28
|
+
if (params.sessionId) {
|
|
29
|
+
const existing = await this.sessionService.findSession(params.sessionId);
|
|
30
|
+
if (existing) {
|
|
31
|
+
const messages = await this.messageService.getMessages(params.sessionId);
|
|
32
|
+
return {
|
|
33
|
+
session: existing,
|
|
34
|
+
messages,
|
|
35
|
+
isNew: false,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// Hosting 等上游已分配 sessionId,Runtime 侧尚未落库时采纳该 ID。
|
|
39
|
+
// Message 权威可能已在 File/PG:必须加载,禁止因壳缺失而清空历史。
|
|
40
|
+
const session = await this.sessionService.createSession({
|
|
41
|
+
id: params.sessionId,
|
|
42
|
+
userId: params.userId,
|
|
43
|
+
agentId: params.agentId,
|
|
44
|
+
metadata: params.agentVersion ? { agentVersion: params.agentVersion } : undefined,
|
|
45
|
+
});
|
|
46
|
+
const messages = await this.messageService.getMessages(params.sessionId);
|
|
47
|
+
return {
|
|
48
|
+
session,
|
|
49
|
+
messages,
|
|
50
|
+
// 有历史则不当作新会话(避免重启后误发 session.created)
|
|
51
|
+
isNew: messages.length === 0,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// 创建新 Session
|
|
55
|
+
const session = await this.sessionService.createSession({
|
|
56
|
+
userId: params.userId,
|
|
57
|
+
agentId: params.agentId,
|
|
58
|
+
metadata: params.agentVersion ? { agentVersion: params.agentVersion } : undefined,
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
session,
|
|
62
|
+
messages: [],
|
|
63
|
+
isNew: true,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionService — 会话管理服务
|
|
3
|
+
*
|
|
4
|
+
* 管理 Session 生命周期:创建、查询、归档、加载历史消息。
|
|
5
|
+
* v1 使用 InMemorySessionRepository 存储,生产由 PostgresSessionRepository 替换。
|
|
6
|
+
*
|
|
7
|
+
* @see requirements.md Requirement 10 AC1-AC4
|
|
8
|
+
*/
|
|
9
|
+
import type { ExecutionSessionBindingStatus } from '../common/types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Session 记录(对应 sessions 表)
|
|
12
|
+
*/
|
|
13
|
+
export interface SessionRecord {
|
|
14
|
+
/** 会话唯一 ID */
|
|
15
|
+
id: string;
|
|
16
|
+
/** 所属用户 ID */
|
|
17
|
+
userId: string;
|
|
18
|
+
/** 关联 Agent(值为 agentCode;字段名保留 agentId 对齐存储列 agent_id) */
|
|
19
|
+
agentId: string;
|
|
20
|
+
/** 会话标题(可选) */
|
|
21
|
+
title: string | null;
|
|
22
|
+
/** 会话状态:active | archived */
|
|
23
|
+
status: ExecutionSessionBindingStatus;
|
|
24
|
+
/** 扩展元数据 */
|
|
25
|
+
metadata: Record<string, unknown>;
|
|
26
|
+
/** 创建人 */
|
|
27
|
+
createdBy: string;
|
|
28
|
+
/** 创建时间 (ISO 8601) */
|
|
29
|
+
createdAt: string;
|
|
30
|
+
/** 最后更新人 */
|
|
31
|
+
updatedBy: string;
|
|
32
|
+
/** 最后更新时间 (ISO 8601) */
|
|
33
|
+
updatedAt: string;
|
|
34
|
+
/** 是否删除(软删除标记) */
|
|
35
|
+
isDeleted: boolean;
|
|
36
|
+
/** 删除人 */
|
|
37
|
+
deletedBy?: string | null;
|
|
38
|
+
/** 删除时间 (ISO 8601) */
|
|
39
|
+
deletedAt?: string | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 创建 Session 参数
|
|
43
|
+
*/
|
|
44
|
+
export interface CreateSessionParams {
|
|
45
|
+
/** 会话 ID(可选;Hosting 下发时用于采纳外部 ID) */
|
|
46
|
+
id?: string;
|
|
47
|
+
/** 用户 ID */
|
|
48
|
+
userId: string;
|
|
49
|
+
/** Agent(值为 agentCode;字段名保留 agentId 对齐存储列 agent_id) */
|
|
50
|
+
agentId: string;
|
|
51
|
+
/** 会话标题(可选) */
|
|
52
|
+
title?: string;
|
|
53
|
+
/** 扩展元数据(可选) */
|
|
54
|
+
metadata?: Record<string, unknown>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 加载历史消息的分页选项
|
|
58
|
+
*/
|
|
59
|
+
export interface LoadHistoryOptions {
|
|
60
|
+
/** 返回条数上限 */
|
|
61
|
+
limit?: number;
|
|
62
|
+
/** 偏移量 */
|
|
63
|
+
offset?: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 消息记录(对应 messages 表)
|
|
67
|
+
*
|
|
68
|
+
* 完整实现在 message.service.ts (Task 4.3);
|
|
69
|
+
* 此处仅定义接口类型供 loadHistory 返回值使用。
|
|
70
|
+
*/
|
|
71
|
+
export interface MessageRecord {
|
|
72
|
+
id: string;
|
|
73
|
+
sessionId: string;
|
|
74
|
+
taskId: string | null;
|
|
75
|
+
correlationId: string | null;
|
|
76
|
+
role: 'user' | 'assistant' | 'system' | 'tool';
|
|
77
|
+
content: string;
|
|
78
|
+
toolCalls: unknown[] | null;
|
|
79
|
+
tokenCount: number;
|
|
80
|
+
/** 可选:模型 reasoning / 思考过程 */
|
|
81
|
+
reasoning?: unknown | null;
|
|
82
|
+
/** 可选:生成或工具耗时(毫秒) */
|
|
83
|
+
durationMs?: number | null;
|
|
84
|
+
/** 元数据(扩展字段) */
|
|
85
|
+
metadata?: Record<string, unknown>;
|
|
86
|
+
createdBy: string;
|
|
87
|
+
createdAt: string;
|
|
88
|
+
updatedBy: string;
|
|
89
|
+
updatedAt: string;
|
|
90
|
+
isDeleted: boolean;
|
|
91
|
+
deletedBy?: string | null;
|
|
92
|
+
deletedAt?: string | null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* ISessionRepository — Session 持久化接口
|
|
96
|
+
*
|
|
97
|
+
* SessionService 通过此接口访问存储层,默认使用 InMemorySessionRepository。
|
|
98
|
+
* Task 4.2 将提供 MySQL 实现。
|
|
99
|
+
*/
|
|
100
|
+
export interface ISessionRepository {
|
|
101
|
+
save(session: SessionRecord): Promise<void>;
|
|
102
|
+
findById(id: string): Promise<SessionRecord | null>;
|
|
103
|
+
findByUserId(userId: string, limit?: number, offset?: number): Promise<SessionRecord[]>;
|
|
104
|
+
update(session: SessionRecord): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* IMessageRepository — Message 查询接口(最小定义)
|
|
108
|
+
*
|
|
109
|
+
* SessionService.loadHistory 委托此接口查询消息。
|
|
110
|
+
* 完整实现由 Task 4.3/4.4 提供。
|
|
111
|
+
*/
|
|
112
|
+
export interface IMessageRepository {
|
|
113
|
+
findBySessionId(sessionId: string, limit?: number, offset?: number): Promise<MessageRecord[]>;
|
|
114
|
+
/**
|
|
115
|
+
* 保存单条消息
|
|
116
|
+
*
|
|
117
|
+
* @param message - 消息记录
|
|
118
|
+
*/
|
|
119
|
+
save(message: MessageRecord): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* 批量保存消息
|
|
122
|
+
*
|
|
123
|
+
* @param messages - 消息记录列表
|
|
124
|
+
*/
|
|
125
|
+
saveMany(messages: MessageRecord[]): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* 标记消息为已压缩
|
|
128
|
+
*
|
|
129
|
+
* @param messageIds - 消息 ID 列表
|
|
130
|
+
* @param compressedBy - 操作人
|
|
131
|
+
* @param compressedAt - 压缩时间
|
|
132
|
+
*/
|
|
133
|
+
markAsCompressed(messageIds: string[], compressedBy: string, compressedAt: string): Promise<void>;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* InMemorySessionRepository — 内存存储实现
|
|
137
|
+
*
|
|
138
|
+
* 用于开发/测试阶段,后续替换为 MySQL 实现。
|
|
139
|
+
*/
|
|
140
|
+
export declare class InMemorySessionRepository implements ISessionRepository {
|
|
141
|
+
private readonly store;
|
|
142
|
+
save(session: SessionRecord): Promise<void>;
|
|
143
|
+
findById(id: string): Promise<SessionRecord | null>;
|
|
144
|
+
findByUserId(userId: string, limit?: number, offset?: number): Promise<SessionRecord[]>;
|
|
145
|
+
update(session: SessionRecord): Promise<void>;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* InMemoryMessageRepository — 内存 Message 存储(stub)
|
|
149
|
+
*
|
|
150
|
+
* 供 SessionService 开发/测试使用,Task 4.3/4.4 将提供完整实现。
|
|
151
|
+
*/
|
|
152
|
+
export declare class InMemoryMessageRepository implements IMessageRepository {
|
|
153
|
+
private readonly store;
|
|
154
|
+
findBySessionId(sessionId: string, limit?: number, offset?: number): Promise<MessageRecord[]>;
|
|
155
|
+
save(message: MessageRecord): Promise<void>;
|
|
156
|
+
saveMany(messages: MessageRecord[]): Promise<void>;
|
|
157
|
+
markAsCompressed(messageIds: string[], compressedBy: string, compressedAt: string): Promise<void>;
|
|
158
|
+
/** 辅助方法:添加消息(测试用) */
|
|
159
|
+
addMessage(message: MessageRecord): Promise<void>;
|
|
160
|
+
}
|
|
161
|
+
export declare class SessionService {
|
|
162
|
+
private readonly sessionRepository;
|
|
163
|
+
private readonly messageRepository;
|
|
164
|
+
constructor(sessionRepository?: ISessionRepository, messageRepository?: IMessageRepository);
|
|
165
|
+
/**
|
|
166
|
+
* 创建新 Session,初始状态为 active
|
|
167
|
+
*/
|
|
168
|
+
createSession(params: CreateSessionParams): Promise<SessionRecord>;
|
|
169
|
+
/**
|
|
170
|
+
* 按 ID 查找 Session(不存在返回 null)
|
|
171
|
+
*/
|
|
172
|
+
findSession(sessionId: string): Promise<SessionRecord | null>;
|
|
173
|
+
/**
|
|
174
|
+
* 按 ID 获取 Session(不存在则抛错)
|
|
175
|
+
*/
|
|
176
|
+
getSession(sessionId: string): Promise<SessionRecord>;
|
|
177
|
+
/**
|
|
178
|
+
* 列出用户的 Session
|
|
179
|
+
*/
|
|
180
|
+
listSessions(userId: string, options?: {
|
|
181
|
+
limit?: number;
|
|
182
|
+
offset?: number;
|
|
183
|
+
}): Promise<SessionRecord[]>;
|
|
184
|
+
/**
|
|
185
|
+
* 归档 Session(标记为 archived)
|
|
186
|
+
*/
|
|
187
|
+
archiveSession(sessionId: string): Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* 加载 Session 的消息历史(分页)
|
|
190
|
+
*
|
|
191
|
+
* 委托 IMessageRepository 查询,返回按时间排序的消息列表。
|
|
192
|
+
*/
|
|
193
|
+
loadHistory(sessionId: string, options?: LoadHistoryOptions): Promise<MessageRecord[]>;
|
|
194
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionService — 会话管理服务
|
|
3
|
+
*
|
|
4
|
+
* 管理 Session 生命周期:创建、查询、归档、加载历史消息。
|
|
5
|
+
* v1 使用 InMemorySessionRepository 存储,生产由 PostgresSessionRepository 替换。
|
|
6
|
+
*
|
|
7
|
+
* @see requirements.md Requirement 10 AC1-AC4
|
|
8
|
+
*/
|
|
9
|
+
import { randomUUID } from 'node:crypto';
|
|
10
|
+
// ─────────────────────────────────────────────
|
|
11
|
+
// In-Memory Repository (Default)
|
|
12
|
+
// ─────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* InMemorySessionRepository — 内存存储实现
|
|
15
|
+
*
|
|
16
|
+
* 用于开发/测试阶段,后续替换为 MySQL 实现。
|
|
17
|
+
*/
|
|
18
|
+
export class InMemorySessionRepository {
|
|
19
|
+
store = new Map();
|
|
20
|
+
async save(session) {
|
|
21
|
+
this.store.set(session.id, { ...session });
|
|
22
|
+
}
|
|
23
|
+
async findById(id) {
|
|
24
|
+
const record = this.store.get(id);
|
|
25
|
+
return record ? { ...record } : null;
|
|
26
|
+
}
|
|
27
|
+
async findByUserId(userId, limit, offset) {
|
|
28
|
+
const filtered = Array.from(this.store.values())
|
|
29
|
+
.filter((s) => s.userId === userId && !s.isDeleted)
|
|
30
|
+
.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
31
|
+
const start = offset ?? 0;
|
|
32
|
+
const end = limit !== undefined ? start + limit : undefined;
|
|
33
|
+
return filtered.slice(start, end);
|
|
34
|
+
}
|
|
35
|
+
async update(session) {
|
|
36
|
+
this.store.set(session.id, { ...session });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* InMemoryMessageRepository — 内存 Message 存储(stub)
|
|
41
|
+
*
|
|
42
|
+
* 供 SessionService 开发/测试使用,Task 4.3/4.4 将提供完整实现。
|
|
43
|
+
*/
|
|
44
|
+
export class InMemoryMessageRepository {
|
|
45
|
+
store = [];
|
|
46
|
+
async findBySessionId(sessionId, limit, offset) {
|
|
47
|
+
const filtered = this.store.filter((m) => m.sessionId === sessionId);
|
|
48
|
+
const start = offset ?? 0;
|
|
49
|
+
const end = limit !== undefined ? start + limit : undefined;
|
|
50
|
+
return filtered.slice(start, end);
|
|
51
|
+
}
|
|
52
|
+
async save(message) {
|
|
53
|
+
// 检查是否已存在,存在则更新,不存在则添加
|
|
54
|
+
const existingIndex = this.store.findIndex((m) => m.id === message.id);
|
|
55
|
+
if (existingIndex >= 0) {
|
|
56
|
+
this.store[existingIndex] = { ...message };
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
this.store.push({ ...message });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async saveMany(messages) {
|
|
63
|
+
for (const message of messages) {
|
|
64
|
+
await this.save(message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async markAsCompressed(messageIds, compressedBy, compressedAt) {
|
|
68
|
+
for (const id of messageIds) {
|
|
69
|
+
const message = this.store.find((m) => m.id === id);
|
|
70
|
+
if (message) {
|
|
71
|
+
// 在 metadata 中添加压缩标记
|
|
72
|
+
if (!message.metadata) {
|
|
73
|
+
message.metadata = {};
|
|
74
|
+
}
|
|
75
|
+
message.metadata.compressed = true;
|
|
76
|
+
message.metadata.compressedBy = compressedBy;
|
|
77
|
+
message.metadata.compressedAt = compressedAt;
|
|
78
|
+
message.updatedBy = compressedBy;
|
|
79
|
+
message.updatedAt = compressedAt;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** 辅助方法:添加消息(测试用) */
|
|
84
|
+
async addMessage(message) {
|
|
85
|
+
this.store.push({ ...message });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// ─────────────────────────────────────────────
|
|
89
|
+
// SessionService Implementation
|
|
90
|
+
// ─────────────────────────────────────────────
|
|
91
|
+
export class SessionService {
|
|
92
|
+
sessionRepository;
|
|
93
|
+
messageRepository;
|
|
94
|
+
constructor(sessionRepository, messageRepository) {
|
|
95
|
+
this.sessionRepository = sessionRepository ?? new InMemorySessionRepository();
|
|
96
|
+
this.messageRepository = messageRepository ?? new InMemoryMessageRepository();
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 创建新 Session,初始状态为 active
|
|
100
|
+
*/
|
|
101
|
+
async createSession(params) {
|
|
102
|
+
const now = new Date().toISOString();
|
|
103
|
+
const session = {
|
|
104
|
+
id: params.id ?? randomUUID(),
|
|
105
|
+
userId: params.userId,
|
|
106
|
+
agentId: params.agentId,
|
|
107
|
+
title: params.title ?? null,
|
|
108
|
+
status: 'active',
|
|
109
|
+
metadata: params.metadata ?? {},
|
|
110
|
+
createdBy: params.userId,
|
|
111
|
+
createdAt: now,
|
|
112
|
+
updatedBy: params.userId,
|
|
113
|
+
updatedAt: now,
|
|
114
|
+
isDeleted: false,
|
|
115
|
+
};
|
|
116
|
+
await this.sessionRepository.save(session);
|
|
117
|
+
return session;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 按 ID 查找 Session(不存在返回 null)
|
|
121
|
+
*/
|
|
122
|
+
async findSession(sessionId) {
|
|
123
|
+
return this.sessionRepository.findById(sessionId);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 按 ID 获取 Session(不存在则抛错)
|
|
127
|
+
*/
|
|
128
|
+
async getSession(sessionId) {
|
|
129
|
+
const session = await this.sessionRepository.findById(sessionId);
|
|
130
|
+
if (!session) {
|
|
131
|
+
throw new Error(`Session not found: '${sessionId}'`);
|
|
132
|
+
}
|
|
133
|
+
return session;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* 列出用户的 Session
|
|
137
|
+
*/
|
|
138
|
+
async listSessions(userId, options) {
|
|
139
|
+
return this.sessionRepository.findByUserId(userId, options?.limit, options?.offset);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* 归档 Session(标记为 archived)
|
|
143
|
+
*/
|
|
144
|
+
async archiveSession(sessionId) {
|
|
145
|
+
const session = await this.sessionRepository.findById(sessionId);
|
|
146
|
+
if (!session) {
|
|
147
|
+
throw new Error(`Session not found: '${sessionId}'`);
|
|
148
|
+
}
|
|
149
|
+
if (session.status === 'archived') {
|
|
150
|
+
return; // 幂等:已归档则无操作
|
|
151
|
+
}
|
|
152
|
+
session.status = 'archived';
|
|
153
|
+
session.updatedAt = new Date().toISOString();
|
|
154
|
+
await this.sessionRepository.update(session);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 加载 Session 的消息历史(分页)
|
|
158
|
+
*
|
|
159
|
+
* 委托 IMessageRepository 查询,返回按时间排序的消息列表。
|
|
160
|
+
*/
|
|
161
|
+
async loadHistory(sessionId, options) {
|
|
162
|
+
// 先验证 Session 存在
|
|
163
|
+
await this.getSession(sessionId);
|
|
164
|
+
return this.messageRepository.findBySessionId(sessionId, options?.limit, options?.offset);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE 连接异常状态记录 — A3
|
|
3
|
+
*/
|
|
4
|
+
import type { ILogger } from '../common/logger.js';
|
|
5
|
+
export type DisconnectReason = 'network_error' | 'client_close' | 'server_timeout' | 'unknown';
|
|
6
|
+
export interface ConnectionEvent {
|
|
7
|
+
taskId: string;
|
|
8
|
+
sessionId: string;
|
|
9
|
+
reason: DisconnectReason;
|
|
10
|
+
timestamp: string;
|
|
11
|
+
lastEventId?: string;
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
export declare class ConnectionEventLogger {
|
|
15
|
+
private readonly logger;
|
|
16
|
+
private readonly history;
|
|
17
|
+
constructor(logger: ILogger);
|
|
18
|
+
logDisconnect(event: ConnectionEvent): void;
|
|
19
|
+
logReconnect(taskId: string, sessionId: string, lastEventId?: string): void;
|
|
20
|
+
getHistory(taskId: string): ConnectionEvent[];
|
|
21
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE 连接异常状态记录 — A3
|
|
3
|
+
*/
|
|
4
|
+
export class ConnectionEventLogger {
|
|
5
|
+
logger;
|
|
6
|
+
history = new Map();
|
|
7
|
+
constructor(logger) {
|
|
8
|
+
this.logger = logger;
|
|
9
|
+
}
|
|
10
|
+
logDisconnect(event) {
|
|
11
|
+
this.logger.warn('SSE disconnected', { taskId: event.taskId, reason: event.reason, sessionId: event.sessionId });
|
|
12
|
+
const list = this.history.get(event.taskId) ?? [];
|
|
13
|
+
list.push(event);
|
|
14
|
+
this.history.set(event.taskId, list);
|
|
15
|
+
}
|
|
16
|
+
logReconnect(taskId, sessionId, lastEventId) {
|
|
17
|
+
this.logger.info('SSE reconnected', { taskId, sessionId, lastEventId });
|
|
18
|
+
const event = {
|
|
19
|
+
taskId,
|
|
20
|
+
sessionId,
|
|
21
|
+
reason: 'unknown', // reconnect 不是 disconnect,reason 不适用
|
|
22
|
+
timestamp: new Date().toISOString(),
|
|
23
|
+
lastEventId,
|
|
24
|
+
metadata: { type: 'reconnect' },
|
|
25
|
+
};
|
|
26
|
+
const list = this.history.get(taskId) ?? [];
|
|
27
|
+
list.push(event);
|
|
28
|
+
this.history.set(taskId, list);
|
|
29
|
+
}
|
|
30
|
+
getHistory(taskId) {
|
|
31
|
+
return this.history.get(taskId) ?? [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './sse-emitter.contract.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './sse-emitter.contract.js';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE Emitter Contract — 服务端推送事件发射器接口
|
|
3
|
+
*
|
|
4
|
+
* 定义 SSE 连接生命周期管理和事件发射能力。
|
|
5
|
+
* 将 RuntimeEvent 包装为 SSE 协议格式推送给客户端。
|
|
6
|
+
*
|
|
7
|
+
* @see design.md §1.6 SSE事件协议定义
|
|
8
|
+
* @see requirements.md Requirement 5 AC1-AC5
|
|
9
|
+
* @see requirements.md Requirement 9 AC1-AC4
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* SSEEvent — 面向 SSE 协议的事件封装
|
|
13
|
+
*
|
|
14
|
+
* 将 RuntimeEvent 包装为 SSE 传输格式:
|
|
15
|
+
* ```
|
|
16
|
+
* event: {type}
|
|
17
|
+
* data: {"correlationId":"xxx", "taskId":"xxx", "timestamp":"xxx", ...payload}
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export interface SSEEvent {
|
|
21
|
+
/** 事件类型名称(如 response.chunk, tool.called 等) */
|
|
22
|
+
type: string;
|
|
23
|
+
/** JSON 序列化后的事件数据负载 */
|
|
24
|
+
data: Record<string, unknown>;
|
|
25
|
+
/** 请求关联 ID */
|
|
26
|
+
correlationId: string;
|
|
27
|
+
/** 关联的 Task ID */
|
|
28
|
+
taskId: string;
|
|
29
|
+
/** 事件时间戳 (ISO 8601) */
|
|
30
|
+
timestamp: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* SSEConnectionState — SSE 连接生命周期状态
|
|
34
|
+
*/
|
|
35
|
+
export type SSEConnectionState = 'connected' | 'disconnected' | 'reconnecting';
|
|
36
|
+
/**
|
|
37
|
+
* ISSEEmitter — SSE 事件发射器接口
|
|
38
|
+
*
|
|
39
|
+
* 负责将 Graph 执行产生的 RuntimeEvent 转换为 SSE 格式推送给客户端。
|
|
40
|
+
* 管理连接生命周期,支持心跳保活。
|
|
41
|
+
*
|
|
42
|
+
* 使用方式:
|
|
43
|
+
* 1. 创建 Emitter 实例绑定到 HTTP Response
|
|
44
|
+
* 2. Graph.stream() 每产出一个 RuntimeEvent → 调用 emit() 推送
|
|
45
|
+
* 3. 定时调用 heartbeat() 保活连接(默认 30s 间隔)
|
|
46
|
+
* 4. Task 完成或异常时调用 close() 关闭连接
|
|
47
|
+
*/
|
|
48
|
+
export interface ISSEEmitter {
|
|
49
|
+
/**
|
|
50
|
+
* 发射单个 SSE 事件到客户端
|
|
51
|
+
*
|
|
52
|
+
* 将 SSEEvent 序列化为 SSE 协议格式:
|
|
53
|
+
* ```
|
|
54
|
+
* event: {event.type}
|
|
55
|
+
* data: {"correlationId":"...", "taskId":"...", "timestamp":"...", ...data}
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @param event - 要发送的 SSE 事件
|
|
59
|
+
*/
|
|
60
|
+
emit(event: SSEEvent): void;
|
|
61
|
+
/**
|
|
62
|
+
* 关闭 SSE 连接
|
|
63
|
+
*
|
|
64
|
+
* 终止事件流,释放底层资源。调用后不可再 emit。
|
|
65
|
+
*/
|
|
66
|
+
close(): void;
|
|
67
|
+
/**
|
|
68
|
+
* 发送心跳事件保持连接活跃
|
|
69
|
+
*
|
|
70
|
+
* 推送 `connection.heartbeat` 事件,防止代理/LB 因空闲超时断开连接。
|
|
71
|
+
* 默认心跳间隔 30s,可通过配置调整。
|
|
72
|
+
*/
|
|
73
|
+
heartbeat(): void;
|
|
74
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE Emitter Contract — 服务端推送事件发射器接口
|
|
3
|
+
*
|
|
4
|
+
* 定义 SSE 连接生命周期管理和事件发射能力。
|
|
5
|
+
* 将 RuntimeEvent 包装为 SSE 协议格式推送给客户端。
|
|
6
|
+
*
|
|
7
|
+
* @see design.md §1.6 SSE事件协议定义
|
|
8
|
+
* @see requirements.md Requirement 5 AC1-AC5
|
|
9
|
+
* @see requirements.md Requirement 9 AC1-AC4
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSEEventBuffer — 环形缓冲区,支持断线重连事件回放
|
|
3
|
+
*/
|
|
4
|
+
import type { SSEEvent } from './contracts/index.js';
|
|
5
|
+
export declare class SSEEventBuffer {
|
|
6
|
+
private readonly maxSize;
|
|
7
|
+
private readonly buffer;
|
|
8
|
+
private head;
|
|
9
|
+
private count;
|
|
10
|
+
private seq;
|
|
11
|
+
constructor(maxSize?: number);
|
|
12
|
+
/** 缓存事件,eventId 由外部传入 */
|
|
13
|
+
push(event: SSEEvent, eventId: string): void;
|
|
14
|
+
/** 获取 lastEventId 之后的所有事件 */
|
|
15
|
+
getEventsSince(lastEventId: string): SSEEvent[];
|
|
16
|
+
/** 生成自增事件 ID */
|
|
17
|
+
generateId(): string;
|
|
18
|
+
/** 按时间顺序返回所有有效 entries */
|
|
19
|
+
private entries;
|
|
20
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSEEventBuffer — 环形缓冲区,支持断线重连事件回放
|
|
3
|
+
*/
|
|
4
|
+
export class SSEEventBuffer {
|
|
5
|
+
maxSize;
|
|
6
|
+
buffer;
|
|
7
|
+
head = 0;
|
|
8
|
+
count = 0;
|
|
9
|
+
seq = 0;
|
|
10
|
+
constructor(maxSize = 100) {
|
|
11
|
+
this.maxSize = maxSize;
|
|
12
|
+
this.buffer = new Array(maxSize);
|
|
13
|
+
}
|
|
14
|
+
/** 缓存事件,eventId 由外部传入 */
|
|
15
|
+
push(event, eventId) {
|
|
16
|
+
this.buffer[this.head] = { event, eventId };
|
|
17
|
+
this.head = (this.head + 1) % this.maxSize;
|
|
18
|
+
if (this.count < this.maxSize)
|
|
19
|
+
this.count++;
|
|
20
|
+
}
|
|
21
|
+
/** 获取 lastEventId 之后的所有事件 */
|
|
22
|
+
getEventsSince(lastEventId) {
|
|
23
|
+
const entries = this.entries();
|
|
24
|
+
const idx = entries.findIndex(e => e.eventId === lastEventId);
|
|
25
|
+
if (idx === -1)
|
|
26
|
+
return entries.map(e => e.event); // 找不到,返回全部缓冲
|
|
27
|
+
return entries.slice(idx + 1).map(e => e.event);
|
|
28
|
+
}
|
|
29
|
+
/** 生成自增事件 ID */
|
|
30
|
+
generateId() {
|
|
31
|
+
return `evt-${++this.seq}`;
|
|
32
|
+
}
|
|
33
|
+
/** 按时间顺序返回所有有效 entries */
|
|
34
|
+
entries() {
|
|
35
|
+
const result = [];
|
|
36
|
+
const start = this.count < this.maxSize ? 0 : this.head;
|
|
37
|
+
for (let i = 0; i < this.count; i++) {
|
|
38
|
+
const idx = (start + i) % this.maxSize;
|
|
39
|
+
const entry = this.buffer[idx];
|
|
40
|
+
if (entry)
|
|
41
|
+
result.push(entry);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
}
|