@gt-fe/eap-runtime 0.0.20 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +4 -3
  2. package/dist/api/handlers/invoke.handler.d.ts +7 -0
  3. package/dist/api/handlers/invoke.handler.js +52 -10
  4. package/dist/api/handlers/stream.handler.d.ts +6 -0
  5. package/dist/api/handlers/stream.handler.js +60 -39
  6. package/dist/api/routes/agent.routes.d.ts +8 -1
  7. package/dist/checkpoint/file-checkpoint-saver.d.ts +4 -0
  8. package/dist/checkpoint/file-checkpoint-saver.js +51 -22
  9. package/dist/checkpoint/index.d.ts +1 -1
  10. package/dist/checkpoint/index.js +1 -1
  11. package/dist/common/types.d.ts +2 -2
  12. package/dist/common/types.js +1 -1
  13. package/dist/execution/index.d.ts +2 -0
  14. package/dist/execution/index.js +2 -0
  15. package/dist/execution/interceptors.d.ts +40 -0
  16. package/dist/execution/interceptors.js +23 -0
  17. package/dist/execution/ports.d.ts +18 -0
  18. package/dist/execution/ports.js +13 -0
  19. package/dist/governance/audit-reporter.d.ts +14 -0
  20. package/dist/governance/audit-reporter.js +18 -0
  21. package/dist/governance/index.d.ts +2 -0
  22. package/dist/governance/index.js +1 -0
  23. package/dist/governance/permission-guard.d.ts +6 -2
  24. package/dist/governance/permission-guard.js +38 -5
  25. package/dist/graph/adapters/graph-nesting.d.ts +12 -0
  26. package/dist/graph/adapters/graph-nesting.js +29 -0
  27. package/dist/graph/adapters/index.d.ts +2 -0
  28. package/dist/graph/adapters/index.js +2 -0
  29. package/dist/graph/adapters/langgraph-checkpointer.d.ts +11 -0
  30. package/dist/graph/adapters/langgraph-checkpointer.js +48 -9
  31. package/dist/graph/adapters/langgraph.adapter.d.ts +33 -11
  32. package/dist/graph/adapters/langgraph.adapter.js +200 -98
  33. package/dist/graph/adapters/skill-isolate.d.ts +13 -0
  34. package/dist/graph/adapters/skill-isolate.js +39 -0
  35. package/dist/graph/adapters/subgraph-compiler.d.ts +7 -2
  36. package/dist/graph/adapters/subgraph-compiler.js +63 -30
  37. package/dist/graph/contracts/executable-graph.d.ts +33 -5
  38. package/dist/graph/hitl/hitl-handler.d.ts +13 -4
  39. package/dist/graph/hitl/hitl-handler.js +80 -7
  40. package/dist/graph/nodes/index.d.ts +1 -0
  41. package/dist/graph/nodes/index.js +1 -0
  42. package/dist/graph/nodes/llm-node.d.ts +3 -1
  43. package/dist/graph/nodes/llm-node.js +26 -2
  44. package/dist/graph/nodes/rag/dedup.d.ts +20 -0
  45. package/dist/graph/nodes/rag/dedup.js +77 -0
  46. package/dist/graph/nodes/rag/denoise.d.ts +58 -0
  47. package/dist/graph/nodes/rag/denoise.js +220 -0
  48. package/dist/graph/nodes/rag/fusion/fuse.d.ts +11 -0
  49. package/dist/graph/nodes/rag/fusion/fuse.js +25 -0
  50. package/dist/graph/nodes/rag/fusion/normalize.d.ts +9 -0
  51. package/dist/graph/nodes/rag/fusion/normalize.js +135 -0
  52. package/dist/graph/nodes/rag/index.d.ts +18 -0
  53. package/dist/graph/nodes/rag/index.js +17 -0
  54. package/dist/graph/nodes/rag/prompts.d.ts +12 -0
  55. package/dist/graph/nodes/rag/prompts.js +40 -0
  56. package/dist/graph/nodes/rag/rag-node.d.ts +21 -0
  57. package/dist/graph/nodes/rag/rag-node.js +135 -0
  58. package/dist/graph/nodes/rag/rag-search-tool.d.ts +48 -0
  59. package/dist/graph/nodes/rag/rag-search-tool.js +230 -0
  60. package/dist/graph/nodes/rag/react-loop.d.ts +57 -0
  61. package/dist/graph/nodes/rag/react-loop.js +467 -0
  62. package/dist/graph/nodes/rag/retrievers/http-embedder.d.ts +29 -0
  63. package/dist/graph/nodes/rag/retrievers/http-embedder.js +66 -0
  64. package/dist/graph/nodes/rag/retrievers/in-memory-retriever.d.ts +17 -0
  65. package/dist/graph/nodes/rag/retrievers/in-memory-retriever.js +18 -0
  66. package/dist/graph/nodes/rag/retrievers/local-sqlite-retriever.d.ts +46 -0
  67. package/dist/graph/nodes/rag/retrievers/local-sqlite-retriever.js +85 -0
  68. package/dist/graph/nodes/rag/tools.d.ts +90 -0
  69. package/dist/graph/nodes/rag/tools.js +260 -0
  70. package/dist/graph/nodes/rag/types.d.ts +226 -0
  71. package/dist/graph/nodes/rag/types.js +9 -0
  72. package/dist/graph/nodes/tool-node.d.ts +19 -1
  73. package/dist/graph/nodes/tool-node.js +158 -33
  74. package/dist/index.d.ts +2 -0
  75. package/dist/index.js +1 -0
  76. package/dist/memory/semantic-retrieval.service.d.ts +1 -1
  77. package/dist/memory/semantic-retrieval.service.js +13 -23
  78. package/dist/providers/index.d.ts +2 -2
  79. package/dist/providers/index.js +2 -2
  80. package/dist/providers/local-llm-provider.d.ts +6 -0
  81. package/dist/providers/local-llm-provider.js +29 -5
  82. package/dist/providers/routing-llm-provider.d.ts +7 -2
  83. package/dist/providers/routing-llm-provider.js +71 -16
  84. package/dist/registry/deployment-snapshot.js +18 -4
  85. package/dist/registry/graph-loader.d.ts +2 -0
  86. package/dist/registry/graph-loader.js +39 -2
  87. package/dist/registry/preload-flags.d.ts +1 -1
  88. package/dist/registry/preload-flags.js +1 -1
  89. package/dist/runtime/agent-runtime.d.ts +5 -0
  90. package/dist/runtime/agent-runtime.js +107 -70
  91. package/dist/runtime/dev-rag-deps.d.ts +21 -0
  92. package/dist/runtime/dev-rag-deps.js +186 -0
  93. package/dist/runtime/dev-runtime.d.ts +18 -2
  94. package/dist/runtime/dev-runtime.js +68 -8
  95. package/dist/runtime/index.d.ts +1 -1
  96. package/dist/runtime/types.d.ts +10 -9
  97. package/dist/session/apply-session-compression.d.ts +85 -0
  98. package/dist/session/apply-session-compression.js +281 -0
  99. package/dist/session/apply-session-memory.d.ts +21 -0
  100. package/dist/session/apply-session-memory.js +48 -0
  101. package/dist/session/index.d.ts +2 -0
  102. package/dist/session/index.js +2 -0
  103. package/dist/session/persist-turn-messages.d.ts +57 -1
  104. package/dist/session/persist-turn-messages.js +160 -5
  105. package/dist/sse/events/index.d.ts +13 -4
  106. package/dist/sse/events/index.js +9 -1
  107. package/dist/sse/events/rag-events.d.ts +63 -0
  108. package/dist/sse/events/rag-events.js +9 -0
  109. package/dist/sse/events/session-events.d.ts +38 -0
  110. package/dist/sse/events/session-events.js +4 -0
  111. package/dist/token/index.d.ts +2 -1
  112. package/dist/token/index.js +2 -1
  113. package/dist/token/record-task-token-usage.d.ts +25 -0
  114. package/dist/token/record-task-token-usage.js +44 -0
  115. package/dist/token/token-detail-mode.d.ts +4 -4
  116. package/dist/token/token-detail-mode.js +4 -4
  117. package/dist/token/token-detail.d.ts +8 -0
  118. package/dist/token/token-detail.js +22 -0
  119. package/dist/token/token-stats.service.d.ts +2 -2
  120. package/dist/token/token-stats.service.js +1 -1
  121. package/dist/tool/create-tool-executor.d.ts +6 -0
  122. package/dist/tool/create-tool-executor.js +4 -3
  123. package/dist/tool/harness-primitives.d.ts +9 -1
  124. package/dist/tool/harness-primitives.js +43 -12
  125. package/dist/tool/index.d.ts +2 -1
  126. package/dist/tool/index.js +2 -1
  127. package/dist/tool/tool-confirmation.js +8 -5
  128. package/dist/tool/tool-executor.interface.d.ts +27 -0
  129. package/dist/tool/tool-orchestrator.d.ts +3 -1
  130. package/dist/tool/tool-orchestrator.js +15 -6
  131. package/dist/tool/tool-retry.d.ts +15 -0
  132. package/dist/tool/tool-retry.js +69 -0
  133. package/package.json +2 -2
@@ -0,0 +1,281 @@
1
+ /**
2
+ * execute/stream 装载历史后的压缩入口。默认关。
3
+ *
4
+ * C-1a 开关:options.enabled 或 EAP_RUNTIME_COMPRESSION=1
5
+ * C-1b 有窗口:用上一轮 llm_turns 的 used/window,不用字符估窗
6
+ * C-1c 无窗口:只按轮次上限,不按窗压
7
+ * C-1d 摘要只走 ILLMProvider;失败则不压缩
8
+ * C-1e 默认只改本轮内存历史;persistCompressed 时必须能写 compression record
9
+ * C-1f 流式路径在摘要前产出 session.compression.started,结束后产出 completed。
10
+ * 不新开「占用 ≥ 80% 再 compact」链;触发仍是 C-1b/c。
11
+ */
12
+ import { EchoLLMProvider } from '../providers/echo-llm-provider.js';
13
+ import { COMPRESSION_WINDOW_FACTOR, countRounds, DEFAULT_COMPRESSION_CONFIG, } from '../memory/message-compression.service.js';
14
+ import { SlidingWindowCompression } from '../memory/compression/sliding-window.compression.js';
15
+ import { emitRuntimeLog } from '../common/logger.js';
16
+ export function isRuntimeCompressionEnabled(explicit, env = process.env) {
17
+ if (explicit === true)
18
+ return true;
19
+ if (explicit === false)
20
+ return false;
21
+ const raw = env.EAP_RUNTIME_COMPRESSION ?? env.EAP_COMPRESSION_ENABLED;
22
+ return raw === '1' || raw === 'true';
23
+ }
24
+ export function isRuntimeCompressionPersist(explicit, env = process.env) {
25
+ if (explicit === true)
26
+ return true;
27
+ if (explicit === false)
28
+ return false;
29
+ return env.EAP_RUNTIME_COMPRESSION_PERSIST === '1' || env.EAP_RUNTIME_COMPRESSION_PERSIST === 'true';
30
+ }
31
+ export function formatCompressionStartedMessage(input) {
32
+ if (typeof input.percent === 'number' && Number.isFinite(input.percent)) {
33
+ return `Context ${input.percent}% full. Compacting...`;
34
+ }
35
+ if (typeof input.threshold === 'number' && Number.isFinite(input.threshold)) {
36
+ return `Conversation exceeded ${input.threshold} rounds. Compacting...`;
37
+ }
38
+ return 'Compacting conversation history...';
39
+ }
40
+ export function formatCompressionCompletedMessage(input) {
41
+ if (!input.success) {
42
+ return input.error
43
+ ? 'Compression failed; using original history.'
44
+ : 'Compression skipped; using original history.';
45
+ }
46
+ return `Compressed ${input.originalMessageCount} → ${input.compressedMessageCount} messages.`;
47
+ }
48
+ export async function applySessionCompression(params) {
49
+ const result = await runSessionCompression(params);
50
+ return result.messages;
51
+ }
52
+ /** 未显式 summaryModel 时写入 Agent `models.default`。压缩未开则不解析。 */
53
+ export async function withCompressionSummaryModel(options, resolveDefaultModel, env = process.env) {
54
+ if (!options)
55
+ return options;
56
+ if (!isRuntimeCompressionEnabled(options.enabled, env))
57
+ return options;
58
+ if (options.summaryModel?.trim())
59
+ return options;
60
+ const summaryModel = (await resolveDefaultModel())?.trim();
61
+ if (!summaryModel || summaryModel.toLowerCase() === 'default')
62
+ return options;
63
+ return { ...options, summaryModel };
64
+ }
65
+ /**
66
+ * 流式入口用:先产出 started,再跑摘要,再产出 completed,最后给出本轮历史。
67
+ * SDK `stream` 可在 started 之后才继续 next(),HTTP SSE 可立刻 emit。
68
+ */
69
+ export async function* iterSessionCompression(params) {
70
+ const history = params.history ?? [];
71
+ const decision = await decideSessionCompression(params);
72
+ if (!decision.needed || !decision.started) {
73
+ yield { kind: 'history', messages: history };
74
+ return;
75
+ }
76
+ yield { kind: 'started', payload: decision.started };
77
+ const result = await runDecidedSessionCompression(params, decision);
78
+ if (result.completed) {
79
+ yield { kind: 'completed', payload: result.completed };
80
+ }
81
+ yield { kind: 'history', messages: result.messages };
82
+ }
83
+ export async function runSessionCompression(params) {
84
+ const decision = await decideSessionCompression(params);
85
+ if (!decision.needed) {
86
+ return { messages: params.history ?? [], compressed: false };
87
+ }
88
+ return runDecidedSessionCompression(params, decision);
89
+ }
90
+ export async function decideSessionCompression(params) {
91
+ const history = params.history ?? [];
92
+ const originalMessageCount = history.length;
93
+ const options = params.options ?? {};
94
+ const env = params.env ?? process.env;
95
+ const enabled = isRuntimeCompressionEnabled(options.enabled, env);
96
+ if (!enabled || history.length === 0) {
97
+ return { needed: false, originalMessageCount };
98
+ }
99
+ const provider = options.provider;
100
+ if (!provider || provider instanceof EchoLLMProvider) {
101
+ return { needed: false, originalMessageCount };
102
+ }
103
+ const maxRounds = options.maxRounds ?? DEFAULT_COMPRESSION_CONFIG.maxRounds;
104
+ const last = await lastTurnUsage(options.turnLog, params.sessionId);
105
+ const needed = last?.window
106
+ ? last.used >= Math.floor(last.window * COMPRESSION_WINDOW_FACTOR)
107
+ : countRounds(history) > maxRounds;
108
+ if (!needed) {
109
+ return { needed: false, originalMessageCount };
110
+ }
111
+ const reason = last?.window ? 'window' : 'max_rounds';
112
+ const percent = last?.window && last.window > 0 ? Math.round((last.used / last.window) * 100) : undefined;
113
+ const threshold = last?.window
114
+ ? Math.floor(last.window * COMPRESSION_WINDOW_FACTOR)
115
+ : maxRounds;
116
+ const started = {
117
+ sessionId: params.sessionId,
118
+ ...(params.taskId ? { taskId: params.taskId } : {}),
119
+ reason,
120
+ originalMessageCount,
121
+ ...(last ? { used: last.used } : {}),
122
+ ...(last?.window ? { window: last.window } : {}),
123
+ ...(percent !== undefined ? { percent } : {}),
124
+ threshold,
125
+ displayMessage: formatCompressionStartedMessage({ percent, reason, threshold }),
126
+ };
127
+ return {
128
+ needed: true,
129
+ originalMessageCount,
130
+ reason,
131
+ used: last?.used,
132
+ window: last?.window,
133
+ percent,
134
+ threshold,
135
+ started,
136
+ };
137
+ }
138
+ async function runDecidedSessionCompression(params, decision) {
139
+ const history = params.history ?? [];
140
+ const options = params.options ?? {};
141
+ const env = params.env ?? process.env;
142
+ const provider = options.provider;
143
+ if (!provider || provider instanceof EchoLLMProvider || !decision.needed) {
144
+ return {
145
+ messages: history,
146
+ compressed: false,
147
+ completed: completedPayload(params, decision, {
148
+ success: false,
149
+ compressedMessageCount: history.length,
150
+ persisted: false,
151
+ }),
152
+ };
153
+ }
154
+ const persist = isRuntimeCompressionPersist(options.persistCompressed, env);
155
+ if (persist && !options.compressionRepository) {
156
+ emitRuntimeLog('warn', 'runtime.compression', 'persist requested without repository; memory only', {
157
+ event: 'runtime.compression.persist_skipped',
158
+ sessionId: params.sessionId,
159
+ });
160
+ }
161
+ const maxRounds = options.maxRounds ?? DEFAULT_COMPRESSION_CONFIG.maxRounds;
162
+ try {
163
+ const compression = new SlidingWindowCompression({
164
+ maxRounds,
165
+ preserveRecentRounds: options.preserveRecentRounds ?? DEFAULT_COMPRESSION_CONFIG.preserveRecentRounds,
166
+ ...(decision.window ? { maxContextTokens: decision.window } : {}),
167
+ }, async (msgs) => summarizeWithProvider(provider, msgs, options.summaryModel));
168
+ const compressed = await compression.compress(history);
169
+ if (compressed === history) {
170
+ return {
171
+ messages: history,
172
+ compressed: false,
173
+ completed: completedPayload(params, decision, {
174
+ success: false,
175
+ compressedMessageCount: history.length,
176
+ persisted: false,
177
+ }),
178
+ };
179
+ }
180
+ const record = {
181
+ sessionId: params.sessionId,
182
+ taskId: params.taskId ?? null,
183
+ originalMessageCount: history.length,
184
+ compressedMessageCount: compressed.length,
185
+ originalTokens: decision.used ?? 0,
186
+ compressedTokens: 0,
187
+ strategy: 'sliding-window',
188
+ summaryContent: compressed[0]?.content ?? '',
189
+ createdBy: params.userId ?? 'system',
190
+ createdAt: new Date().toISOString(),
191
+ };
192
+ let persisted = false;
193
+ if (persist && options.compressionRepository) {
194
+ await options.compressionRepository.save(record);
195
+ persisted = true;
196
+ }
197
+ return {
198
+ messages: compressed,
199
+ compressed: true,
200
+ record,
201
+ completed: completedPayload(params, decision, {
202
+ success: true,
203
+ compressedMessageCount: compressed.length,
204
+ persisted,
205
+ }),
206
+ };
207
+ }
208
+ catch (err) {
209
+ const error = err instanceof Error ? err.message : String(err);
210
+ emitRuntimeLog('warn', 'runtime.compression', 'compress failed; using original history', {
211
+ event: 'runtime.compression.failed',
212
+ sessionId: params.sessionId,
213
+ error,
214
+ });
215
+ return {
216
+ messages: history,
217
+ compressed: false,
218
+ completed: completedPayload(params, decision, {
219
+ success: false,
220
+ compressedMessageCount: history.length,
221
+ persisted: false,
222
+ error,
223
+ }),
224
+ };
225
+ }
226
+ }
227
+ function completedPayload(params, decision, outcome) {
228
+ const reason = decision.reason ?? 'max_rounds';
229
+ return {
230
+ sessionId: params.sessionId,
231
+ ...(params.taskId ? { taskId: params.taskId } : {}),
232
+ reason,
233
+ success: outcome.success,
234
+ originalMessageCount: decision.originalMessageCount,
235
+ compressedMessageCount: outcome.compressedMessageCount,
236
+ ...(decision.used !== undefined ? { used: decision.used } : {}),
237
+ ...(decision.window !== undefined ? { window: decision.window } : {}),
238
+ ...(decision.percent !== undefined ? { percent: decision.percent } : {}),
239
+ persisted: outcome.persisted,
240
+ ...(outcome.error ? { error: outcome.error } : {}),
241
+ displayMessage: formatCompressionCompletedMessage({
242
+ success: outcome.success,
243
+ originalMessageCount: decision.originalMessageCount,
244
+ compressedMessageCount: outcome.compressedMessageCount,
245
+ error: outcome.error,
246
+ }),
247
+ };
248
+ }
249
+ async function lastTurnUsage(turnLog, sessionId) {
250
+ if (!turnLog || !sessionId)
251
+ return undefined;
252
+ const index = await turnLog.list(sessionId);
253
+ const last = index[index.length - 1];
254
+ if (!last || typeof last.used !== 'number')
255
+ return undefined;
256
+ return { used: last.used, window: last.window };
257
+ }
258
+ async function summarizeWithProvider(provider, messages, summaryModel) {
259
+ const body = messages
260
+ .map((m) => `${m.role}: ${m.content}`)
261
+ .join('\n')
262
+ .slice(0, 12000);
263
+ const raw = summaryModel?.trim();
264
+ const model = raw && raw.toLowerCase() !== 'default' ? raw : '';
265
+ const response = await provider.invoke([
266
+ {
267
+ role: 'system',
268
+ content: '将对话历史压成短摘要,保留用户需求、结论和未完成事项。',
269
+ },
270
+ { role: 'user', content: body },
271
+ ], {
272
+ model,
273
+ temperature: 0.2,
274
+ maxTokens: 1024,
275
+ });
276
+ const text = response.content?.trim();
277
+ if (!text) {
278
+ throw new Error('empty compression summary');
279
+ }
280
+ return text;
281
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * execute/stream 装载历史后注入长期记忆。默认关。
3
+ * 只用关键词匹配;未注入真实 embedding 不做向量检索。
4
+ */
5
+ import type { GraphMessage } from '../graph/contracts/executable-graph.js';
6
+ import { LongTermMemoryService, type MemoryRecord } from '../memory/long-term-memory.service.js';
7
+ export interface SessionMemoryOptions {
8
+ enabled?: boolean;
9
+ service?: LongTermMemoryService;
10
+ limit?: number;
11
+ }
12
+ export declare function isRuntimeMemoryEnabled(explicit?: boolean, env?: NodeJS.ProcessEnv): boolean;
13
+ export declare function applySessionMemory(params: {
14
+ history: GraphMessage[];
15
+ userId?: string;
16
+ agentCode?: string;
17
+ query?: string;
18
+ options?: SessionMemoryOptions;
19
+ env?: NodeJS.ProcessEnv;
20
+ }): Promise<GraphMessage[]>;
21
+ export declare function injectMemoryHits(history: GraphMessage[], memories: MemoryRecord[]): GraphMessage[];
@@ -0,0 +1,48 @@
1
+ /**
2
+ * execute/stream 装载历史后注入长期记忆。默认关。
3
+ * 只用关键词匹配;未注入真实 embedding 不做向量检索。
4
+ */
5
+ import { SemanticRetrievalService } from '../memory/semantic-retrieval.service.js';
6
+ export function isRuntimeMemoryEnabled(explicit, env = process.env) {
7
+ if (explicit === true)
8
+ return true;
9
+ if (explicit === false)
10
+ return false;
11
+ const raw = env.EAP_RUNTIME_MEMORY;
12
+ return raw === '1' || raw === 'true';
13
+ }
14
+ export async function applySessionMemory(params) {
15
+ const history = params.history ?? [];
16
+ const options = params.options ?? {};
17
+ const env = params.env ?? process.env;
18
+ if (!isRuntimeMemoryEnabled(options.enabled, env))
19
+ return history;
20
+ const service = options.service;
21
+ if (!service || !params.userId || !params.agentCode)
22
+ return history;
23
+ const query = params.query?.trim() ||
24
+ [...history].reverse().find((m) => m.role === 'user')?.content?.trim() ||
25
+ '';
26
+ if (!query)
27
+ return history;
28
+ const stored = await service.retrieve(params.userId, params.agentCode, 50);
29
+ if (stored.length === 0)
30
+ return history;
31
+ const retrieval = new SemanticRetrievalService({
32
+ strategy: 'keyword',
33
+ limit: options.limit ?? 10,
34
+ });
35
+ const hits = await retrieval.retrieve(query, stored);
36
+ if (hits.length === 0)
37
+ return history;
38
+ return injectMemoryHits(history, hits.map((h) => h.record));
39
+ }
40
+ export function injectMemoryHits(history, memories) {
41
+ if (memories.length === 0)
42
+ return history;
43
+ const memoryContent = memories.map((m) => `[${m.type}] ${m.content}`).join('\n');
44
+ return [
45
+ { role: 'system', content: `[Long-term Memory]\n${memoryContent}` },
46
+ ...history,
47
+ ];
48
+ }
@@ -10,4 +10,6 @@ export type { FileMessageRepositoryOptions } from './file-message.repository.js'
10
10
  export { SessionContextService } from './session-context.service.js';
11
11
  export type { ResolveSessionParams, SessionContext, } from './session-context.service.js';
12
12
  export { CompressionMiddleware, createCompressionMiddleware } from './compression-middleware.js';
13
+ export { applySessionCompression, withCompressionSummaryModel, runSessionCompression, decideSessionCompression, iterSessionCompression, formatCompressionStartedMessage, formatCompressionCompletedMessage, isRuntimeCompressionEnabled, isRuntimeCompressionPersist, type SessionCompressionOptions, type RunSessionCompressionParams, type RunSessionCompressionResult, type SessionCompressionDecision, type SessionCompressionStep, } from './apply-session-compression.js';
13
14
  export type { CompressionMiddlewareConfig } from './compression-middleware.js';
15
+ export { applySessionMemory, isRuntimeMemoryEnabled, type SessionMemoryOptions, } from './apply-session-memory.js';
@@ -10,3 +10,5 @@ export { FileMessageRepository } from './file-message.repository.js';
10
10
  export { SessionContextService } from './session-context.service.js';
11
11
  // 压缩中间件
12
12
  export { CompressionMiddleware, createCompressionMiddleware } from './compression-middleware.js';
13
+ export { applySessionCompression, withCompressionSummaryModel, runSessionCompression, decideSessionCompression, iterSessionCompression, formatCompressionStartedMessage, formatCompressionCompletedMessage, isRuntimeCompressionEnabled, isRuntimeCompressionPersist, } from './apply-session-compression.js';
14
+ export { applySessionMemory, isRuntimeMemoryEnabled, } from './apply-session-memory.js';
@@ -21,6 +21,7 @@ export declare function persistAssistantTurnMessage(messageService: MessageServi
21
21
  toolCalls?: unknown[];
22
22
  /** DeepSeek thinking:多轮回传用 */
23
23
  reasoningContent?: string;
24
+ durationMs?: number | null;
24
25
  }): Promise<void>;
25
26
  /**
26
27
  * HITL 等待态占位消息:避免早退时本轮只有 user、历史偏薄
@@ -46,11 +47,56 @@ export declare function persistToolTurnMessage(messageService: MessageService |
46
47
  }): Promise<void>;
47
48
  /** 从流式 RuntimeEvent payload 抽取文本增量 */
48
49
  export declare function extractStreamTextDelta(eventType: string, payload: unknown): string;
50
+ /** 按节点分开累加 stream 文本,避免并行 LLM 的 token 交错拼成一条消息 */
51
+ export type StreamAssistantBuckets = {
52
+ order: string[];
53
+ text: Map<string, string>;
54
+ };
55
+ export declare function createStreamAssistantBuckets(): StreamAssistantBuckets;
56
+ export declare function applyStreamTextDelta(buckets: StreamAssistantBuckets, eventType: string, payload: unknown): void;
57
+ export declare function streamAssistantTexts(buckets: StreamAssistantBuckets): Array<{
58
+ nodeId: string;
59
+ text: string;
60
+ }>;
61
+ /** 最后一个有文本的节点(有汇合节点时通常是它) */
62
+ export declare function lastStreamAssistantText(buckets: StreamAssistantBuckets): string;
63
+ /** 用户可见兜底输出:各节点文本都保留,避免无汇合时只剩后出现的一路 */
64
+ export declare function visibleStreamAssistantText(buckets: StreamAssistantBuckets): string;
65
+ /** `finishPoints` 里的实际节点 id;`__end__` / `__start__` 不算。空则 Session 不按结束节点筛选。 */
66
+ export declare function namedFinishPoints(finishPoints?: string[]): string[];
67
+ /**
68
+ * Session 里的助手正文。
69
+ * - `finishPoints` 只含一个实际节点 id(DAG `merge`):只留该节点
70
+ * - 没有实际节点 id,或含多个:全部保留,由调用方拼成一条消息(规划执行的计划+结果)
71
+ * 不把 planner 写进 finishPoints:那会变成 LangGraph 终止点,计划跑完图就结束。
72
+ */
73
+ export declare function sessionVisibleAssistantTexts(texts: Array<{
74
+ nodeId?: string;
75
+ text: string;
76
+ }>, finishPoints?: string[]): Array<{
77
+ nodeId?: string;
78
+ text: string;
79
+ }>;
80
+ /**
81
+ * invoke 路径图增量。tool_calls / tool 行原样保留,纯文本助手收成一条放在末尾(与 stream 落库顺序一致)。
82
+ * GraphMessage 无 nodeId:`finishPoints` 只含一个实际节点 id 时取最后一条纯文本,不是按 id 筛选(官方 DAG 的 `merge` 在末尾)。
83
+ * 没有实际节点 id 或仅为 `__end__` 时把多段正文用空行拼进这一条。
84
+ * 无 toolCallId 的 tool 行不进 Session(code 节点结果)。
85
+ */
86
+ export declare function sessionVisibleGraphMessages(messages: GraphMessage[], finishPoints?: string[]): GraphMessage[];
49
87
  /**
50
88
  * 从流式事件抽取 assistant reasoning。
51
89
  * 子图在 skill.completed 上回传(父图不再为 subgraph 节点发空 response.chunk)。
52
90
  */
53
91
  export declare function extractStreamReasoning(eventType: string, payload: unknown): string | undefined;
92
+ /** 落盘 / 回填历史只收终态:result,或不会再试的 error。willRetry 的 error 只给直播 UI。 */
93
+ export declare function isTerminalToolStreamEvent(eventType: string, payload: unknown): boolean;
94
+ export declare function appendTerminalToolResult(eventType: string, payload: unknown, bucket: Array<{
95
+ toolCallId: string;
96
+ toolName: string;
97
+ content: unknown;
98
+ success: boolean;
99
+ }>): void;
54
100
  /** 历史 MessageRecord → GraphMessage(保留 tool 元数据与 reasoning) */
55
101
  export declare function messageRecordsToGraphMessages(records: MessageRecord[]): GraphMessage[];
56
102
  /**
@@ -94,8 +140,10 @@ export declare function buildHitlWaitingTaskOutput(params: {
94
140
  /** 从 Task.output 解析 Checkpoint thread_id(含嵌套 hitl.threadId) */
95
141
  export declare function extractHitlThreadId(output: unknown): string | undefined;
96
142
  /**
97
- * Checkpoint thread_id 契约:HITL/多轮优先且应唯一等于 sessionId;
143
+ * Checkpoint 父图 thread_id:HITL/多轮优先且应唯一等于 sessionId;
98
144
  * 无 session 时退到 `task-${taskId}`。勿用 correlationId 作为 Checkpoint key。
145
+ * 子图槽位再经 `checkpointStorageThreadId`(`thread_id#父节点id`),
146
+ * 与 LangGraph put / 子图内 HITL interrupt 同一把钥匙。
99
147
  *
100
148
  * 注意:session 级 thread + 全量 Session 历史同时注入会导致 messages 双重 append。
101
149
  * 组装本轮 Graph 输入请用 {@link buildTurnGraphMessages}(有 Checkpoint 历史则只追加 user)。
@@ -124,6 +172,13 @@ export declare function buildTurnTranscriptFromStreamEvents(params: {
124
172
  pendingToolCalls?: StreamToolCall[];
125
173
  toolResults?: StreamToolResult[];
126
174
  assistantText?: string;
175
+ /** 按节点分开的助手文本;有值时优先于 assistantText */
176
+ assistantTexts?: Array<{
177
+ nodeId?: string;
178
+ text: string;
179
+ }>;
180
+ /** 图 finishPoints。只含一个实际节点 id 时按该 id 只留该节点正文;仅为 `__end__` 或含多个实际节点 id 时全部拼接 */
181
+ finishPoints?: string[];
127
182
  /** 带 tool_calls 的那条 assistant 的 reasoning(thinking 回传) */
128
183
  toolCallReasoningContent?: string;
129
184
  /** 最终文本 assistant 的 reasoning */
@@ -142,4 +197,5 @@ export declare function persistTurnTranscript(messageService: MessageService | u
142
197
  /** 无 assistant 文本消息时的兜底(invoke 的 graphResult.output) */
143
198
  fallbackAssistantContent?: string;
144
199
  assistantTokenCount?: number;
200
+ assistantDurationMs?: number | null;
145
201
  }): Promise<void>;