@gt-fe/eap-runtime 0.0.24 → 0.0.26
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 +1 -0
- package/dist/api/handlers/invoke.handler.js +31 -18
- package/dist/api/handlers/stream.handler.js +16 -6
- package/dist/graph/contracts/executable-graph.d.ts +2 -2
- package/dist/runtime/agent-runtime.d.ts +1 -1
- package/dist/runtime/agent-runtime.js +15 -5
- package/dist/runtime/dev-runtime.d.ts +1 -0
- package/dist/session/apply-session-compression.d.ts +6 -4
- package/dist/session/apply-session-compression.js +24 -11
- package/dist/session/index.d.ts +1 -1
- package/dist/session/index.js +1 -1
- package/dist/session/persist-turn-messages.d.ts +50 -9
- package/dist/session/persist-turn-messages.js +133 -15
- package/dist/tool/harness-primitives.js +29 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Enterprise Agent Platform **执行引擎**(LangGraph.js):Agent Graph 编
|
|
|
8
8
|
|
|
9
9
|
| 日期 | 摘要 |
|
|
10
10
|
|------|------|
|
|
11
|
+
| 2026-09-14 | 会话滑动窗口压缩默认开;`EAP_RUNTIME_COMPRESSION=0` 关闭 |
|
|
11
12
|
| 2026-09-08 | [`docs/TODO.md`](./docs/TODO.md) 改为主路径状态(已落地 / 不在本包),不是排期 |
|
|
12
13
|
| 2026-08-29 | 待办收口:[`docs/TODO.md`](./docs/TODO.md)。此后压缩已挂主路径(默认关);窗口展示不在 Runtime;Gateway 须 `tools/list` |
|
|
13
14
|
| 2026-08-24 | 0.0.10:官方 Skill / Harness、`ForkParams`、对话详情、存储两档。依赖 `@eap/contracts@0.0.9`。详见 [CHANGELOG.md](./CHANGELOG.md) |
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* @see requirements.md Requirement 11 AC1-AC2
|
|
16
16
|
*/
|
|
17
17
|
import { randomUUID } from 'node:crypto';
|
|
18
|
-
import { buildHitlWaitingTaskOutput,
|
|
18
|
+
import { buildHitlWaitingTaskOutput, loadModelFacingHistory, messageRecordsToGraphMessages, persistGraphMessageBaseline, turnGraphMessagesAfterCompression, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, resolveCheckpointThreadId, sessionVisibleGraphMessages, } from '../../session/persist-turn-messages.js';
|
|
19
19
|
import { SessionBudgetExhaustedError } from '../../token/session-budget-checker.js';
|
|
20
20
|
import { TokenBudgetExhaustedError } from '../../token/token-budget-guard.js';
|
|
21
21
|
import { recordTaskTokenUsages, tokenUsagesFromLlmChannel, } from '../../token/record-task-token-usage.js';
|
|
@@ -33,7 +33,7 @@ import { assembleLoadedTools } from '../../tool/harness-primitives.js';
|
|
|
33
33
|
import { resolveAgentCode } from '../../common/agent-code.js';
|
|
34
34
|
import { unwrapHitlPaused } from '../../graph/hitl/hitl-handler.js';
|
|
35
35
|
import { resolveTraceId } from '../../common/trace-id.js';
|
|
36
|
-
import {
|
|
36
|
+
import { runSessionCompression, withCompressionSummaryModel, } from '../../session/apply-session-compression.js';
|
|
37
37
|
import { applySessionMemory, } from '../../session/apply-session-memory.js';
|
|
38
38
|
// ─────────────────────────────────────────────
|
|
39
39
|
// Default Graph DSL fallback
|
|
@@ -180,6 +180,7 @@ export class InvokeOrchestrator {
|
|
|
180
180
|
// ─── Step 5: 构建/执行 Graph ───
|
|
181
181
|
let graphResult;
|
|
182
182
|
let finishPoints;
|
|
183
|
+
let persistBaseline = 0;
|
|
183
184
|
try {
|
|
184
185
|
// 构建图 — 每次请求创建独立 StateGraph 实例,确保请求间无共享可变状态
|
|
185
186
|
const graph = await this.graphEngine.buildGraph(graphDsl);
|
|
@@ -202,32 +203,45 @@ export class InvokeOrchestrator {
|
|
|
202
203
|
? (await this.graphLoader.resolveAgentDir(agentCode, req.agentVersion)) ?? undefined
|
|
203
204
|
: undefined;
|
|
204
205
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.graphLoader, agentCode, req.agentVersion);
|
|
205
|
-
//
|
|
206
|
+
// 模型上下文:Checkpoint 已有 messages 则压它;压成功用 __set 写回最新 Checkpoint。Message 库不改。
|
|
207
|
+
const facing = await loadModelFacingHistory({
|
|
208
|
+
sessionHistory: messageRecordsToGraphMessages(sessionContext.messages),
|
|
209
|
+
checkpointSaver: this.checkpointSaver,
|
|
210
|
+
threadId: sessionId,
|
|
211
|
+
});
|
|
212
|
+
const compression = await runSessionCompression({
|
|
213
|
+
history: facing.messages,
|
|
214
|
+
sessionId,
|
|
215
|
+
userId: effectiveUserId,
|
|
216
|
+
taskId: task.id,
|
|
217
|
+
options: await withCompressionSummaryModel(this.sessionCompression, () => this.graphLoader
|
|
218
|
+
? this.graphLoader.resolveDefaultModel(agentCode, req.agentVersion)
|
|
219
|
+
: Promise.resolve(undefined)),
|
|
220
|
+
});
|
|
206
221
|
const history = await applySessionMemory({
|
|
207
|
-
history:
|
|
208
|
-
history: messageRecordsToGraphMessages(sessionContext.messages),
|
|
209
|
-
sessionId,
|
|
210
|
-
userId: effectiveUserId,
|
|
211
|
-
taskId: task.id,
|
|
212
|
-
options: await withCompressionSummaryModel(this.sessionCompression, () => this.graphLoader
|
|
213
|
-
? this.graphLoader.resolveDefaultModel(agentCode, req.agentVersion)
|
|
214
|
-
: Promise.resolve(undefined)),
|
|
215
|
-
}),
|
|
222
|
+
history: compression.messages,
|
|
216
223
|
userId: effectiveUserId,
|
|
217
224
|
agentCode,
|
|
218
225
|
query: typeof req.input === 'string' ? req.input : undefined,
|
|
219
226
|
options: this.sessionMemory,
|
|
220
227
|
});
|
|
221
|
-
const messages =
|
|
228
|
+
const { messages, replaceCheckpointMessages } = turnGraphMessagesAfterCompression({
|
|
222
229
|
history,
|
|
223
230
|
input: req.input,
|
|
224
|
-
|
|
225
|
-
|
|
231
|
+
fromCheckpoint: facing.fromCheckpoint,
|
|
232
|
+
compressed: compression.compressed,
|
|
233
|
+
});
|
|
234
|
+
persistBaseline = persistGraphMessageBaseline({
|
|
235
|
+
fromCheckpoint: facing.fromCheckpoint,
|
|
236
|
+
replaceCheckpointMessages,
|
|
237
|
+
graphInputCount: messages.length,
|
|
238
|
+
modelFacingCount: facing.messages.length,
|
|
226
239
|
});
|
|
227
240
|
const skillInput = resolveSkillInputFromInvokeInput(req.input);
|
|
228
241
|
// 同步执行图
|
|
229
242
|
graphResult = await runWithExecutionTrace({ abortSignal }, () => this.graphEngine.invoke(graph, {
|
|
230
243
|
messages,
|
|
244
|
+
replaceCheckpointMessages,
|
|
231
245
|
userId: effectiveUserId,
|
|
232
246
|
delegationToken: ctx.delegationToken,
|
|
233
247
|
agentCode,
|
|
@@ -369,12 +383,11 @@ export class InvokeOrchestrator {
|
|
|
369
383
|
// ─── Step 7: 完成 Task (running → success) ───
|
|
370
384
|
await this.taskService.updateStatus(task.id, 'success', graphResult.output);
|
|
371
385
|
// ─── Step 7b: 持久化本轮 transcript(统一顺序:assistant tool_calls → tool → assistant 文本)───
|
|
372
|
-
//
|
|
373
|
-
const initialMessagesCount = sessionContext.messages.length + 1;
|
|
386
|
+
// 切点是入图 messages 基线(压缩 `__set` 后 Graph 短于 Session 全文)。user 已在 4b 写入。
|
|
374
387
|
const allMessages = Array.isArray(graphResult.finalState?.messages)
|
|
375
388
|
? graphResult.finalState.messages
|
|
376
389
|
: [];
|
|
377
|
-
const newMessages = sessionVisibleGraphMessages(allMessages.slice(
|
|
390
|
+
const newMessages = sessionVisibleGraphMessages(allMessages.slice(persistBaseline), finishPoints);
|
|
378
391
|
const duration = Date.now() - startTime;
|
|
379
392
|
await persistTurnTranscript(this.messageService, {
|
|
380
393
|
sessionId,
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* @see design.md §1.5 /stream 内部执行逻辑
|
|
20
20
|
* @see requirements.md Requirement 11 AC2
|
|
21
21
|
*/
|
|
22
|
-
import { buildHitlWaitingTaskOutput,
|
|
22
|
+
import { buildHitlWaitingTaskOutput, buildTurnTranscriptFromStreamEvents, loadModelFacingHistory, turnGraphMessagesAfterCompression, extractStreamReasoning, applyStreamTextDelta, createStreamAssistantBuckets, visibleStreamAssistantText, streamAssistantTexts, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, appendTerminalToolResult, } from '../../session/persist-turn-messages.js';
|
|
23
23
|
import { SessionBudgetExhaustedError } from '../../token/session-budget-checker.js';
|
|
24
24
|
import { TokenBudgetExhaustedError } from '../../token/token-budget-guard.js';
|
|
25
25
|
import { recordTaskTokenUsages } from '../../token/record-task-token-usage.js';
|
|
@@ -221,8 +221,14 @@ export class StreamOrchestrator {
|
|
|
221
221
|
? (await this.graphLoader.resolveAgentDir(agentCode, req.agentVersion)) ?? undefined
|
|
222
222
|
: undefined;
|
|
223
223
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.graphLoader, agentCode, req.agentVersion);
|
|
224
|
-
//
|
|
225
|
-
|
|
224
|
+
// 模型上下文:Checkpoint 已有 messages 则压它;压成功用 __set 写回最新 Checkpoint。Message 库不改。
|
|
225
|
+
const facing = await loadModelFacingHistory({
|
|
226
|
+
sessionHistory: messageRecordsToGraphMessages(sessionContext.messages),
|
|
227
|
+
checkpointSaver: this.checkpointSaver,
|
|
228
|
+
threadId: sessionId,
|
|
229
|
+
});
|
|
230
|
+
let history = facing.messages;
|
|
231
|
+
let compressed = false;
|
|
226
232
|
const compressionOptions = await withCompressionSummaryModel(this.sessionCompression, () => this.graphLoader
|
|
227
233
|
? this.graphLoader.resolveDefaultModel(agentCode, req.agentVersion)
|
|
228
234
|
: Promise.resolve(undefined));
|
|
@@ -237,6 +243,9 @@ export class StreamOrchestrator {
|
|
|
237
243
|
history = step.messages;
|
|
238
244
|
continue;
|
|
239
245
|
}
|
|
246
|
+
if (step.kind === 'completed') {
|
|
247
|
+
compressed = step.payload.success === true;
|
|
248
|
+
}
|
|
240
249
|
emitter.emit(this.buildSSEEvent(step.kind === 'started' ? 'session.compression.started' : 'session.compression.completed', correlationId, taskId, { ...step.payload }));
|
|
241
250
|
}
|
|
242
251
|
history = await applySessionMemory({
|
|
@@ -247,11 +256,11 @@ export class StreamOrchestrator {
|
|
|
247
256
|
options: this.sessionMemory,
|
|
248
257
|
});
|
|
249
258
|
const tokenDetail = resolveTokenDetail(req.config?.tokenDetail);
|
|
250
|
-
const messages =
|
|
259
|
+
const { messages, replaceCheckpointMessages } = turnGraphMessagesAfterCompression({
|
|
251
260
|
history,
|
|
252
261
|
input: req.input,
|
|
253
|
-
|
|
254
|
-
|
|
262
|
+
fromCheckpoint: facing.fromCheckpoint,
|
|
263
|
+
compressed,
|
|
255
264
|
});
|
|
256
265
|
const skillInput = resolveSkillInputFromInvokeInput(req.input);
|
|
257
266
|
const assistantBuckets = createStreamAssistantBuckets();
|
|
@@ -263,6 +272,7 @@ export class StreamOrchestrator {
|
|
|
263
272
|
try {
|
|
264
273
|
const eventStream = runWithExecutionTrace({ abortSignal }, () => this.graphEngine.stream(graph, {
|
|
265
274
|
messages,
|
|
275
|
+
replaceCheckpointMessages,
|
|
266
276
|
userId,
|
|
267
277
|
delegationToken: executionContext.delegationToken,
|
|
268
278
|
agentCode,
|
|
@@ -239,8 +239,8 @@ export interface GraphState {
|
|
|
239
239
|
/** 最近一次 createNode 的 span id;Checkpoint 引用挂在此 Span 上 */
|
|
240
240
|
lastSpanId?: string;
|
|
241
241
|
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
242
|
+
* 入口 messages 以 __set 覆盖 checkpoint 历史,避免 append 重复。
|
|
243
|
+
* HITL 续跑与会话压缩写回最新 Checkpoint 使用。不入持久化语义。
|
|
244
244
|
*/
|
|
245
245
|
replaceCheckpointMessages?: boolean;
|
|
246
246
|
/**
|
|
@@ -48,7 +48,7 @@ export interface AgentRuntimeDeps {
|
|
|
48
48
|
*/
|
|
49
49
|
traceCollector?: ITraceCollector;
|
|
50
50
|
recordForkRelation?: RecordForkRelation;
|
|
51
|
-
/**
|
|
51
|
+
/** 历史压缩;默认开。EAP_RUNTIME_COMPRESSION=0 关闭 */
|
|
52
52
|
sessionCompression?: SessionCompressionOptions;
|
|
53
53
|
/** 长期记忆注入;默认关。见 EAP_RUNTIME_MEMORY */
|
|
54
54
|
sessionMemory?: import('../session/apply-session-memory.js').SessionMemoryOptions;
|
|
@@ -15,7 +15,7 @@ import { resolveAgentSystemPromptFromLoader } from '../registry/graph-loader.js'
|
|
|
15
15
|
import { toLoadedSkillMap } from '../tool/system-prompt-builder.js';
|
|
16
16
|
import { assembleLoadedTools } from '../tool/harness-primitives.js';
|
|
17
17
|
import { createFileLlmTurnLogFromEnv, } from '../debug/llm-turn-log.js';
|
|
18
|
-
import { buildHitlWaitingTaskOutput,
|
|
18
|
+
import { buildHitlWaitingTaskOutput, buildTurnTranscriptFromStreamEvents, loadModelFacingHistory, turnGraphMessagesAfterCompression, extractHitlThreadId, extractStreamReasoning, applyStreamTextDelta, createStreamAssistantBuckets, visibleStreamAssistantText, streamAssistantTexts, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, resolveCheckpointThreadId, appendTerminalToolResult, } from '../session/persist-turn-messages.js';
|
|
19
19
|
import { iterSessionCompression, withCompressionSummaryModel, } from '../session/apply-session-compression.js';
|
|
20
20
|
import { applySessionMemory } from '../session/apply-session-memory.js';
|
|
21
21
|
import { resolveTokenDetail, shouldEmitTokenSse } from '../token/token-detail.js';
|
|
@@ -294,7 +294,13 @@ export class AgentRuntime {
|
|
|
294
294
|
? (await this.deps.graphLoader.resolveAgentDir(agentCode, params.agentVersion)) ?? undefined
|
|
295
295
|
: undefined;
|
|
296
296
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.deps.graphLoader, agentCode, params.agentVersion);
|
|
297
|
-
|
|
297
|
+
const facing = await loadModelFacingHistory({
|
|
298
|
+
sessionHistory: messageRecordsToGraphMessages(sessionContext.messages),
|
|
299
|
+
checkpointSaver: this.deps.checkpointSaver,
|
|
300
|
+
threadId: sessionId,
|
|
301
|
+
});
|
|
302
|
+
let history = facing.messages;
|
|
303
|
+
let compressed = false;
|
|
298
304
|
const compressionOptions = await withCompressionSummaryModel({
|
|
299
305
|
...this.deps.sessionCompression,
|
|
300
306
|
turnLog: this.deps.sessionCompression?.turnLog ?? this.deps.turnLog,
|
|
@@ -312,6 +318,9 @@ export class AgentRuntime {
|
|
|
312
318
|
history = step.messages;
|
|
313
319
|
continue;
|
|
314
320
|
}
|
|
321
|
+
if (step.kind === 'completed') {
|
|
322
|
+
compressed = step.payload.success === true;
|
|
323
|
+
}
|
|
315
324
|
yield this.toSSE(step.kind === 'started' ? 'session.compression.started' : 'session.compression.completed', correlationId, task.id, { ...step.payload });
|
|
316
325
|
}
|
|
317
326
|
history = await applySessionMemory({
|
|
@@ -322,11 +331,11 @@ export class AgentRuntime {
|
|
|
322
331
|
options: this.deps.sessionMemory,
|
|
323
332
|
});
|
|
324
333
|
const tokenDetail = resolveTokenDetail(params.config?.tokenDetail);
|
|
325
|
-
const messages =
|
|
334
|
+
const { messages, replaceCheckpointMessages } = turnGraphMessagesAfterCompression({
|
|
326
335
|
history,
|
|
327
336
|
input: params.input,
|
|
328
|
-
|
|
329
|
-
|
|
337
|
+
fromCheckpoint: facing.fromCheckpoint,
|
|
338
|
+
compressed,
|
|
330
339
|
});
|
|
331
340
|
const startTime = Date.now();
|
|
332
341
|
const assistantBuckets = createStreamAssistantBuckets();
|
|
@@ -338,6 +347,7 @@ export class AgentRuntime {
|
|
|
338
347
|
try {
|
|
339
348
|
for await (const runtimeEvent of runWithExecutionTrace({ abortSignal }, () => this.deps.graphEngine.stream(graph, {
|
|
340
349
|
messages,
|
|
350
|
+
replaceCheckpointMessages,
|
|
341
351
|
userId,
|
|
342
352
|
delegationToken: ctx.delegationToken,
|
|
343
353
|
agentCode,
|
|
@@ -33,6 +33,7 @@ import { LongTermMemoryService } from '../memory/long-term-memory.service.js';
|
|
|
33
33
|
export interface CreateDevRuntimeOptions {
|
|
34
34
|
interceptors?: ExecutionInterceptors;
|
|
35
35
|
compression?: {
|
|
36
|
+
/** 默认开。false 或 EAP_RUNTIME_COMPRESSION=0 关闭 */
|
|
36
37
|
enabled?: boolean;
|
|
37
38
|
persistCompressed?: boolean;
|
|
38
39
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* execute/stream
|
|
2
|
+
* execute/stream 装载历史后的压缩入口。默认开。
|
|
3
3
|
*
|
|
4
|
-
* C-1a
|
|
4
|
+
* C-1a 开关:默认开。options.enabled=false 或 EAP_RUNTIME_COMPRESSION=0 / false 关闭
|
|
5
5
|
* C-1b 有窗口:用上一轮 llm_turns 的 used/window,不用字符估窗
|
|
6
6
|
* C-1c 无窗口:只按轮次上限,不按窗压
|
|
7
|
-
* C-1d 摘要只走 ILLMProvider
|
|
8
|
-
* C-1e
|
|
7
|
+
* C-1d 摘要只走 ILLMProvider;Echo 或未注入 provider 则不压;失败则不压缩
|
|
8
|
+
* C-1e 压成功且来自 Checkpoint 时,入图 `__set` 覆盖最新 Checkpoint messages;不回写 Message 库。persistCompressed 只写审计 record
|
|
9
9
|
* C-1f 流式路径在摘要前产出 session.compression.started,结束后产出 completed。
|
|
10
10
|
* 不新开「占用 ≥ 80% 再 compact」链;触发仍是 C-1b/c。
|
|
11
11
|
*/
|
|
@@ -15,6 +15,8 @@ import type { ILlmTurnLog } from '../debug/llm-turn-log.js';
|
|
|
15
15
|
import { type CompressionRecord } from '../memory/message-compression.service.js';
|
|
16
16
|
import type { ICompressionRepository } from '../memory/compression/compression.repository.js';
|
|
17
17
|
import type { SessionCompressionCompletedPayload, SessionCompressionReason, SessionCompressionStartedPayload } from '../sse/events/session-events.js';
|
|
18
|
+
/** 摘要 LLM 写入 llm_turns 的 nodeId。lastTurnUsage 跳过,避免用摘要占用触发下一轮压缩。 */
|
|
19
|
+
export declare const COMPRESSION_TURN_NODE_ID = "session-compression";
|
|
18
20
|
export interface SessionCompressionOptions {
|
|
19
21
|
enabled?: boolean;
|
|
20
22
|
persistCompressed?: boolean;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* execute/stream
|
|
2
|
+
* execute/stream 装载历史后的压缩入口。默认开。
|
|
3
3
|
*
|
|
4
|
-
* C-1a
|
|
4
|
+
* C-1a 开关:默认开。options.enabled=false 或 EAP_RUNTIME_COMPRESSION=0 / false 关闭
|
|
5
5
|
* C-1b 有窗口:用上一轮 llm_turns 的 used/window,不用字符估窗
|
|
6
6
|
* C-1c 无窗口:只按轮次上限,不按窗压
|
|
7
|
-
* C-1d 摘要只走 ILLMProvider
|
|
8
|
-
* C-1e
|
|
7
|
+
* C-1d 摘要只走 ILLMProvider;Echo 或未注入 provider 则不压;失败则不压缩
|
|
8
|
+
* C-1e 压成功且来自 Checkpoint 时,入图 `__set` 覆盖最新 Checkpoint messages;不回写 Message 库。persistCompressed 只写审计 record
|
|
9
9
|
* C-1f 流式路径在摘要前产出 session.compression.started,结束后产出 completed。
|
|
10
10
|
* 不新开「占用 ≥ 80% 再 compact」链;触发仍是 C-1b/c。
|
|
11
11
|
*/
|
|
@@ -13,13 +13,17 @@ import { EchoLLMProvider } from '../providers/echo-llm-provider.js';
|
|
|
13
13
|
import { COMPRESSION_WINDOW_FACTOR, countRounds, DEFAULT_COMPRESSION_CONFIG, } from '../memory/message-compression.service.js';
|
|
14
14
|
import { SlidingWindowCompression } from '../memory/compression/sliding-window.compression.js';
|
|
15
15
|
import { emitRuntimeLog } from '../common/logger.js';
|
|
16
|
+
/** 摘要 LLM 写入 llm_turns 的 nodeId。lastTurnUsage 跳过,避免用摘要占用触发下一轮压缩。 */
|
|
17
|
+
export const COMPRESSION_TURN_NODE_ID = 'session-compression';
|
|
16
18
|
export function isRuntimeCompressionEnabled(explicit, env = process.env) {
|
|
17
19
|
if (explicit === true)
|
|
18
20
|
return true;
|
|
19
21
|
if (explicit === false)
|
|
20
22
|
return false;
|
|
21
23
|
const raw = env.EAP_RUNTIME_COMPRESSION ?? env.EAP_COMPRESSION_ENABLED;
|
|
22
|
-
|
|
24
|
+
if (raw === '0' || raw === 'false')
|
|
25
|
+
return false;
|
|
26
|
+
return true;
|
|
23
27
|
}
|
|
24
28
|
export function isRuntimeCompressionPersist(explicit, env = process.env) {
|
|
25
29
|
if (explicit === true)
|
|
@@ -164,7 +168,7 @@ async function runDecidedSessionCompression(params, decision) {
|
|
|
164
168
|
maxRounds,
|
|
165
169
|
preserveRecentRounds: options.preserveRecentRounds ?? DEFAULT_COMPRESSION_CONFIG.preserveRecentRounds,
|
|
166
170
|
...(decision.window ? { maxContextTokens: decision.window } : {}),
|
|
167
|
-
}, async (msgs) => summarizeWithProvider(provider, msgs, options.summaryModel));
|
|
171
|
+
}, async (msgs) => summarizeWithProvider(provider, msgs, options.summaryModel, params.sessionId));
|
|
168
172
|
const compressed = await compression.compress(history);
|
|
169
173
|
if (compressed === history) {
|
|
170
174
|
return {
|
|
@@ -250,12 +254,17 @@ async function lastTurnUsage(turnLog, sessionId) {
|
|
|
250
254
|
if (!turnLog || !sessionId)
|
|
251
255
|
return undefined;
|
|
252
256
|
const index = await turnLog.list(sessionId);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
+
for (let i = index.length - 1; i >= 0; i--) {
|
|
258
|
+
const entry = index[i];
|
|
259
|
+
if (!entry || entry.nodeId === COMPRESSION_TURN_NODE_ID)
|
|
260
|
+
continue;
|
|
261
|
+
if (typeof entry.used !== 'number')
|
|
262
|
+
continue;
|
|
263
|
+
return { used: entry.used, window: entry.window };
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
257
266
|
}
|
|
258
|
-
async function summarizeWithProvider(provider, messages, summaryModel) {
|
|
267
|
+
async function summarizeWithProvider(provider, messages, summaryModel, sessionId) {
|
|
259
268
|
const body = messages
|
|
260
269
|
.map((m) => `${m.role}: ${m.content}`)
|
|
261
270
|
.join('\n')
|
|
@@ -272,6 +281,10 @@ async function summarizeWithProvider(provider, messages, summaryModel) {
|
|
|
272
281
|
model,
|
|
273
282
|
temperature: 0.2,
|
|
274
283
|
maxTokens: 1024,
|
|
284
|
+
turn: {
|
|
285
|
+
sessionId,
|
|
286
|
+
nodeId: COMPRESSION_TURN_NODE_ID,
|
|
287
|
+
},
|
|
275
288
|
});
|
|
276
289
|
const text = response.content?.trim();
|
|
277
290
|
if (!text) {
|
package/dist/session/index.d.ts
CHANGED
|
@@ -10,6 +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
|
+
export { applySessionCompression, COMPRESSION_TURN_NODE_ID, withCompressionSummaryModel, runSessionCompression, decideSessionCompression, iterSessionCompression, formatCompressionStartedMessage, formatCompressionCompletedMessage, isRuntimeCompressionEnabled, isRuntimeCompressionPersist, type SessionCompressionOptions, type RunSessionCompressionParams, type RunSessionCompressionResult, type SessionCompressionDecision, type SessionCompressionStep, } from './apply-session-compression.js';
|
|
14
14
|
export type { CompressionMiddlewareConfig } from './compression-middleware.js';
|
|
15
15
|
export { applySessionMemory, isRuntimeMemoryEnabled, type SessionMemoryOptions, } from './apply-session-memory.js';
|
package/dist/session/index.js
CHANGED
|
@@ -10,5 +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';
|
|
13
|
+
export { applySessionCompression, COMPRESSION_TURN_NODE_ID, withCompressionSummaryModel, runSessionCompression, decideSessionCompression, iterSessionCompression, formatCompressionStartedMessage, formatCompressionCompletedMessage, isRuntimeCompressionEnabled, isRuntimeCompressionPersist, } from './apply-session-compression.js';
|
|
14
14
|
export { applySessionMemory, isRuntimeMemoryEnabled, } from './apply-session-memory.js';
|
|
@@ -99,6 +99,15 @@ export declare function appendTerminalToolResult(eventType: string, payload: unk
|
|
|
99
99
|
}>): void;
|
|
100
100
|
/** 历史 MessageRecord → GraphMessage(保留 tool 元数据与 reasoning) */
|
|
101
101
|
export declare function messageRecordsToGraphMessages(records: MessageRecord[]): GraphMessage[];
|
|
102
|
+
type CheckpointLoad = {
|
|
103
|
+
state?: {
|
|
104
|
+
messages?: unknown;
|
|
105
|
+
};
|
|
106
|
+
engineSnapshot?: Record<string, unknown>;
|
|
107
|
+
} | null;
|
|
108
|
+
type CheckpointLoader = {
|
|
109
|
+
load(threadId: string): Promise<CheckpointLoad>;
|
|
110
|
+
};
|
|
102
111
|
/**
|
|
103
112
|
* 组装本轮送入 Graph 的 messages。
|
|
104
113
|
*
|
|
@@ -107,21 +116,52 @@ export declare function messageRecordsToGraphMessages(records: MessageRecord[]):
|
|
|
107
116
|
* - Session 级 Checkpoint 持有图内 messages(含 reasoning)
|
|
108
117
|
* - 当 Checkpoint 已有历史时,只追加本轮 user,避免双重 append
|
|
109
118
|
* - Checkpoint 缺失/无 messages 时,回退 Session 全量历史
|
|
119
|
+
* - 压缩针对 Checkpoint(无则 Session)中的模型上下文;压成功且来自 Checkpoint 时由调用方设 `replaceCheckpointMessages`
|
|
110
120
|
*/
|
|
111
121
|
export declare function buildTurnGraphMessages(params: {
|
|
112
122
|
history: GraphMessage[];
|
|
113
123
|
input: string;
|
|
114
|
-
checkpointSaver?:
|
|
115
|
-
load(threadId: string): Promise<{
|
|
116
|
-
state?: {
|
|
117
|
-
messages?: unknown;
|
|
118
|
-
};
|
|
119
|
-
engineSnapshot?: Record<string, unknown>;
|
|
120
|
-
} | null>;
|
|
121
|
-
} | null;
|
|
124
|
+
checkpointSaver?: CheckpointLoader | null;
|
|
122
125
|
/** 与 LangGraph configurable.thread_id 一致(通常为 sessionId) */
|
|
123
126
|
threadId?: string;
|
|
124
127
|
}): Promise<GraphMessage[]>;
|
|
128
|
+
/**
|
|
129
|
+
* 模型上下文:Checkpoint 已有 messages 用它,否则用 Session 历史。
|
|
130
|
+
* 压缩必须压这份列表,不能只压 Session 再在有 Checkpoint 时丢掉。
|
|
131
|
+
*/
|
|
132
|
+
export declare function loadModelFacingHistory(params: {
|
|
133
|
+
sessionHistory: GraphMessage[];
|
|
134
|
+
checkpointSaver?: CheckpointLoader | null;
|
|
135
|
+
threadId?: string;
|
|
136
|
+
}): Promise<{
|
|
137
|
+
messages: GraphMessage[];
|
|
138
|
+
fromCheckpoint: boolean;
|
|
139
|
+
}>;
|
|
140
|
+
/**
|
|
141
|
+
* 压缩后的入图列表。来自 Checkpoint 且本轮压成功:整表 `__set` 覆盖,写入最新 Checkpoint。
|
|
142
|
+
* 未压且来自 Checkpoint:只追加本轮 user。无 Checkpoint:历史 + 本轮 user。
|
|
143
|
+
*/
|
|
144
|
+
export declare function turnGraphMessagesAfterCompression(params: {
|
|
145
|
+
history: GraphMessage[];
|
|
146
|
+
input: string;
|
|
147
|
+
fromCheckpoint: boolean;
|
|
148
|
+
compressed: boolean;
|
|
149
|
+
}): {
|
|
150
|
+
messages: GraphMessage[];
|
|
151
|
+
replaceCheckpointMessages: boolean;
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* invoke 从 Graph 最终 messages 切本轮增量的起点。
|
|
155
|
+
* 覆盖(`__set`)或无 Checkpoint:入图数组长度(已含本轮 user)。
|
|
156
|
+
* 追加到已有 Checkpoint:模型上下文条数 + 入图条数(通常 1 条 user)。
|
|
157
|
+
*/
|
|
158
|
+
export declare function persistGraphMessageBaseline(params: {
|
|
159
|
+
fromCheckpoint: boolean;
|
|
160
|
+
replaceCheckpointMessages: boolean;
|
|
161
|
+
graphInputCount: number;
|
|
162
|
+
modelFacingCount: number;
|
|
163
|
+
}): number;
|
|
164
|
+
export declare function graphMessagesFromCheckpoint(cp: CheckpointLoad): GraphMessage[];
|
|
125
165
|
/**
|
|
126
166
|
* HITL 等待态写入 Task.output 的结构化载荷。
|
|
127
167
|
* 冷恢复时优先读 `hitlThreadId`,避免误用 correlationId。
|
|
@@ -146,7 +186,7 @@ export declare function extractHitlThreadId(output: unknown): string | undefined
|
|
|
146
186
|
* 与 LangGraph put / 子图内 HITL interrupt 同一把钥匙。
|
|
147
187
|
*
|
|
148
188
|
* 注意:session 级 thread + 全量 Session 历史同时注入会导致 messages 双重 append。
|
|
149
|
-
* 组装本轮 Graph 输入请用 {@link
|
|
189
|
+
* 组装本轮 Graph 输入请用 {@link loadModelFacingHistory} 与 {@link turnGraphMessagesAfterCompression}。
|
|
150
190
|
*/
|
|
151
191
|
export declare function resolveCheckpointThreadId(params: {
|
|
152
192
|
sessionId?: string;
|
|
@@ -199,3 +239,4 @@ export declare function persistTurnTranscript(messageService: MessageService | u
|
|
|
199
239
|
assistantTokenCount?: number;
|
|
200
240
|
assistantDurationMs?: number | null;
|
|
201
241
|
}): Promise<void>;
|
|
242
|
+
export {};
|
|
@@ -305,31 +305,149 @@ function extractReasoningContent(reasoning) {
|
|
|
305
305
|
* - Session 级 Checkpoint 持有图内 messages(含 reasoning)
|
|
306
306
|
* - 当 Checkpoint 已有历史时,只追加本轮 user,避免双重 append
|
|
307
307
|
* - Checkpoint 缺失/无 messages 时,回退 Session 全量历史
|
|
308
|
+
* - 压缩针对 Checkpoint(无则 Session)中的模型上下文;压成功且来自 Checkpoint 时由调用方设 `replaceCheckpointMessages`
|
|
308
309
|
*/
|
|
309
310
|
export async function buildTurnGraphMessages(params) {
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
311
|
+
const facing = await loadModelFacingHistory({
|
|
312
|
+
sessionHistory: params.history,
|
|
313
|
+
checkpointSaver: params.checkpointSaver,
|
|
314
|
+
threadId: params.threadId,
|
|
315
|
+
});
|
|
316
|
+
return turnGraphMessagesAfterCompression({
|
|
317
|
+
history: facing.messages,
|
|
318
|
+
input: params.input,
|
|
319
|
+
fromCheckpoint: facing.fromCheckpoint,
|
|
320
|
+
compressed: false,
|
|
321
|
+
}).messages;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* 模型上下文:Checkpoint 已有 messages 用它,否则用 Session 历史。
|
|
325
|
+
* 压缩必须压这份列表,不能只压 Session 再在有 Checkpoint 时丢掉。
|
|
326
|
+
*/
|
|
327
|
+
export async function loadModelFacingHistory(params) {
|
|
328
|
+
const sessionHistory = params.sessionHistory ?? [];
|
|
329
|
+
if (!params.checkpointSaver || !params.threadId) {
|
|
330
|
+
return { messages: sessionHistory, fromCheckpoint: false };
|
|
314
331
|
}
|
|
315
332
|
const cp = await params.checkpointSaver.load(params.threadId);
|
|
316
|
-
|
|
317
|
-
|
|
333
|
+
const checkpointMessages = graphMessagesFromCheckpoint(cp);
|
|
334
|
+
if (checkpointMessages.length > 0) {
|
|
335
|
+
return { messages: checkpointMessages, fromCheckpoint: true };
|
|
318
336
|
}
|
|
319
|
-
return
|
|
337
|
+
return { messages: sessionHistory, fromCheckpoint: false };
|
|
320
338
|
}
|
|
321
|
-
|
|
339
|
+
/**
|
|
340
|
+
* 压缩后的入图列表。来自 Checkpoint 且本轮压成功:整表 `__set` 覆盖,写入最新 Checkpoint。
|
|
341
|
+
* 未压且来自 Checkpoint:只追加本轮 user。无 Checkpoint:历史 + 本轮 user。
|
|
342
|
+
*/
|
|
343
|
+
export function turnGraphMessagesAfterCompression(params) {
|
|
344
|
+
const newUser = { role: 'user', content: params.input };
|
|
345
|
+
if (params.fromCheckpoint && params.compressed) {
|
|
346
|
+
return {
|
|
347
|
+
messages: [...params.history, newUser],
|
|
348
|
+
replaceCheckpointMessages: true,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
if (params.fromCheckpoint) {
|
|
352
|
+
return { messages: [newUser], replaceCheckpointMessages: false };
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
messages: [...params.history, newUser],
|
|
356
|
+
replaceCheckpointMessages: false,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* invoke 从 Graph 最终 messages 切本轮增量的起点。
|
|
361
|
+
* 覆盖(`__set`)或无 Checkpoint:入图数组长度(已含本轮 user)。
|
|
362
|
+
* 追加到已有 Checkpoint:模型上下文条数 + 入图条数(通常 1 条 user)。
|
|
363
|
+
*/
|
|
364
|
+
export function persistGraphMessageBaseline(params) {
|
|
365
|
+
if (params.replaceCheckpointMessages || !params.fromCheckpoint) {
|
|
366
|
+
return params.graphInputCount;
|
|
367
|
+
}
|
|
368
|
+
return params.modelFacingCount + params.graphInputCount;
|
|
369
|
+
}
|
|
370
|
+
export function graphMessagesFromCheckpoint(cp) {
|
|
322
371
|
if (!cp)
|
|
323
|
-
return
|
|
324
|
-
if (Array.isArray(cp.state?.messages) && cp.state.messages.length > 0)
|
|
325
|
-
return true;
|
|
372
|
+
return [];
|
|
326
373
|
const channelValues = cp.engineSnapshot?.['channel_values'];
|
|
327
374
|
if (channelValues && typeof channelValues === 'object') {
|
|
328
375
|
const messages = channelValues['messages'];
|
|
329
|
-
if (Array.isArray(messages) && messages.length > 0)
|
|
330
|
-
return
|
|
376
|
+
if (Array.isArray(messages) && messages.length > 0) {
|
|
377
|
+
return unknownListToGraphMessages(messages);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (Array.isArray(cp.state?.messages) && cp.state.messages.length > 0) {
|
|
381
|
+
return unknownListToGraphMessages(cp.state.messages);
|
|
382
|
+
}
|
|
383
|
+
return [];
|
|
384
|
+
}
|
|
385
|
+
function unknownListToGraphMessages(raw) {
|
|
386
|
+
return raw.map((msg) => unknownToGraphMessage(msg));
|
|
387
|
+
}
|
|
388
|
+
function unknownToGraphMessage(msg) {
|
|
389
|
+
if (typeof msg !== 'object' || msg === null) {
|
|
390
|
+
return { role: 'user', content: String(msg) };
|
|
331
391
|
}
|
|
332
|
-
|
|
392
|
+
const m = msg;
|
|
393
|
+
const nested = m.kwargs && typeof m.kwargs === 'object' ? m.kwargs : undefined;
|
|
394
|
+
const lcName = Array.isArray(m.id) ? m.id[m.id.length - 1] : undefined;
|
|
395
|
+
const typeHint = m.type === 'constructor' ? undefined : m.type;
|
|
396
|
+
const role = graphRoleFromUnknown(m.role ?? nested?.role ?? nested?.type ?? typeHint ?? lcName);
|
|
397
|
+
const content = messageContentToString(m.content ?? nested?.content);
|
|
398
|
+
const toolCalls = (m.toolCalls ?? m.tool_calls ?? nested?.tool_calls);
|
|
399
|
+
const toolCallId = (typeof m.toolCallId === 'string' && m.toolCallId) ||
|
|
400
|
+
(typeof m.tool_call_id === 'string' && m.tool_call_id) ||
|
|
401
|
+
(typeof nested?.tool_call_id === 'string' && nested.tool_call_id) ||
|
|
402
|
+
undefined;
|
|
403
|
+
const toolName = (typeof m.toolName === 'string' && m.toolName) ||
|
|
404
|
+
(typeof nested?.name === 'string' && nested.name) ||
|
|
405
|
+
undefined;
|
|
406
|
+
const success = typeof m.success === 'boolean' ? m.success : undefined;
|
|
407
|
+
const reasoningContent = (typeof m.reasoningContent === 'string' && m.reasoningContent) ||
|
|
408
|
+
(typeof m.reasoning_content === 'string' && m.reasoning_content) ||
|
|
409
|
+
undefined;
|
|
410
|
+
return {
|
|
411
|
+
role,
|
|
412
|
+
content,
|
|
413
|
+
...(toolCalls ? { toolCalls } : {}),
|
|
414
|
+
...(toolCallId ? { toolCallId } : {}),
|
|
415
|
+
...(toolName ? { toolName } : {}),
|
|
416
|
+
...(success !== undefined ? { success } : {}),
|
|
417
|
+
...(reasoningContent ? { reasoningContent } : {}),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function graphRoleFromUnknown(raw) {
|
|
421
|
+
if (raw === 'user' || raw === 'assistant' || raw === 'system' || raw === 'tool')
|
|
422
|
+
return raw;
|
|
423
|
+
if (raw === 'human' || raw === 'HumanMessage')
|
|
424
|
+
return 'user';
|
|
425
|
+
if (raw === 'ai' || raw === 'AIMessage')
|
|
426
|
+
return 'assistant';
|
|
427
|
+
if (raw === 'system' || raw === 'SystemMessage')
|
|
428
|
+
return 'system';
|
|
429
|
+
if (raw === 'tool' || raw === 'ToolMessage')
|
|
430
|
+
return 'tool';
|
|
431
|
+
return 'user';
|
|
432
|
+
}
|
|
433
|
+
function messageContentToString(content) {
|
|
434
|
+
if (typeof content === 'string')
|
|
435
|
+
return content;
|
|
436
|
+
if (Array.isArray(content)) {
|
|
437
|
+
return content
|
|
438
|
+
.map((part) => {
|
|
439
|
+
if (typeof part === 'string')
|
|
440
|
+
return part;
|
|
441
|
+
if (part && typeof part === 'object' && typeof part.text === 'string') {
|
|
442
|
+
return part.text;
|
|
443
|
+
}
|
|
444
|
+
return '';
|
|
445
|
+
})
|
|
446
|
+
.join('');
|
|
447
|
+
}
|
|
448
|
+
if (content == null)
|
|
449
|
+
return '';
|
|
450
|
+
return String(content);
|
|
333
451
|
}
|
|
334
452
|
export function buildHitlWaitingTaskOutput(params) {
|
|
335
453
|
return {
|
|
@@ -362,7 +480,7 @@ export function extractHitlThreadId(output) {
|
|
|
362
480
|
* 与 LangGraph put / 子图内 HITL interrupt 同一把钥匙。
|
|
363
481
|
*
|
|
364
482
|
* 注意:session 级 thread + 全量 Session 历史同时注入会导致 messages 双重 append。
|
|
365
|
-
* 组装本轮 Graph 输入请用 {@link
|
|
483
|
+
* 组装本轮 Graph 输入请用 {@link loadModelFacingHistory} 与 {@link turnGraphMessagesAfterCompression}。
|
|
366
484
|
*/
|
|
367
485
|
export function resolveCheckpointThreadId(params) {
|
|
368
486
|
if (params.sessionId)
|
|
@@ -53,7 +53,25 @@ function harnessDefinitions() {
|
|
|
53
53
|
manifest: {
|
|
54
54
|
id: 'builtin:write_file',
|
|
55
55
|
name: 'write_file',
|
|
56
|
-
description: '写入当前用户目录(含用户自己的 skills)。预装 tools / skills
|
|
56
|
+
description: '写入当前用户目录(含用户自己的 skills)。预装 tools / skills 只读。覆盖已有文件。',
|
|
57
|
+
...common,
|
|
58
|
+
},
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: 'object',
|
|
61
|
+
required: ['path', 'content'],
|
|
62
|
+
properties: {
|
|
63
|
+
path: { type: 'string' },
|
|
64
|
+
content: { type: 'string' },
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
toolId: 'builtin:append_to_file',
|
|
70
|
+
name: 'append_to_file',
|
|
71
|
+
manifest: {
|
|
72
|
+
id: 'builtin:append_to_file',
|
|
73
|
+
name: 'append_to_file',
|
|
74
|
+
description: '向当前用户目录内的文件追加文本。文件不存在则创建。预装 tools / skills 只读。',
|
|
57
75
|
...common,
|
|
58
76
|
},
|
|
59
77
|
inputSchema: {
|
|
@@ -199,10 +217,10 @@ export function resolveJailRoots(options) {
|
|
|
199
217
|
}
|
|
200
218
|
return roots;
|
|
201
219
|
}
|
|
202
|
-
async function writeFileNoFollow(target, content) {
|
|
220
|
+
async function writeFileNoFollow(target, content, append = false) {
|
|
203
221
|
const flags = fsConstants.O_WRONLY |
|
|
204
222
|
fsConstants.O_CREAT |
|
|
205
|
-
fsConstants.O_TRUNC |
|
|
223
|
+
(append ? fsConstants.O_APPEND : fsConstants.O_TRUNC) |
|
|
206
224
|
(fsConstants.O_NOFOLLOW ?? 0);
|
|
207
225
|
let handle;
|
|
208
226
|
try {
|
|
@@ -216,7 +234,8 @@ async function writeFileNoFollow(target, content) {
|
|
|
216
234
|
throw err;
|
|
217
235
|
}
|
|
218
236
|
try {
|
|
219
|
-
await handle.
|
|
237
|
+
const { bytesWritten } = await handle.write(content, undefined, 'utf8');
|
|
238
|
+
return bytesWritten;
|
|
220
239
|
}
|
|
221
240
|
finally {
|
|
222
241
|
await handle.close();
|
|
@@ -408,7 +427,9 @@ export class HarnessPrimitiveExecutor {
|
|
|
408
427
|
case 'read_file':
|
|
409
428
|
return await this.readFile(request);
|
|
410
429
|
case 'write_file':
|
|
411
|
-
return await this.writeFile(request);
|
|
430
|
+
return await this.writeFile(request, false);
|
|
431
|
+
case 'append_to_file':
|
|
432
|
+
return await this.writeFile(request, true);
|
|
412
433
|
case 'execute_command':
|
|
413
434
|
return await this.executeCommand(request);
|
|
414
435
|
default:
|
|
@@ -459,7 +480,7 @@ export class HarnessPrimitiveExecutor {
|
|
|
459
480
|
}
|
|
460
481
|
return ok(request, text);
|
|
461
482
|
}
|
|
462
|
-
async writeFile(request) {
|
|
483
|
+
async writeFile(request, append) {
|
|
463
484
|
const filePath = String(request.arguments.path ?? '');
|
|
464
485
|
const content = String(request.arguments.content ?? '');
|
|
465
486
|
const workdir = this.requireWorkdir(request);
|
|
@@ -474,8 +495,8 @@ export class HarnessPrimitiveExecutor {
|
|
|
474
495
|
if (PROTECTED_WRITE_NAMES.has(path.basename(recast))) {
|
|
475
496
|
throw new Error(`禁止写入平台文件:${path.basename(recast)}`);
|
|
476
497
|
}
|
|
477
|
-
await writeFileNoFollow(recast, content);
|
|
478
|
-
return ok(request, JSON.stringify({ ok: true, path: path.relative(workdir, recast) }));
|
|
498
|
+
const bytesWritten = await writeFileNoFollow(recast, content, append);
|
|
499
|
+
return ok(request, JSON.stringify({ ok: true, path: path.relative(workdir, recast), bytesWritten }));
|
|
479
500
|
}
|
|
480
501
|
async executeCommand(request) {
|
|
481
502
|
const command = substituteSkillDir(String(request.arguments.command ?? '').trim(), request.skillDir);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gt-fe/eap-runtime",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "Enterprise Agent Platform — Runtime核心:Graph执行引擎、Task生命周期、Session管理、Checkpoint持久化",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"ioredis": "^5.4.0",
|
|
21
21
|
"pg": "^8.13.0",
|
|
22
22
|
"yaml": "^2.6.1",
|
|
23
|
-
"@gt-fe/eap-contracts": "0.0.
|
|
23
|
+
"@gt-fe/eap-contracts": "0.0.15"
|
|
24
24
|
},
|
|
25
25
|
"publishConfig": {
|
|
26
26
|
"registry": "https://registry.npmjs.org/",
|