@gt-fe/eap-runtime 0.0.23 → 0.0.25
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.d.ts +4 -0
- package/dist/api/handlers/invoke.handler.js +69 -37
- package/dist/api/handlers/stream.handler.d.ts +4 -0
- package/dist/api/handlers/stream.handler.js +40 -21
- package/dist/api/routes/agent.routes.js +6 -5
- package/dist/checkpoint/checkpoint-ref-notify.js +1 -1
- package/dist/common/types.d.ts +6 -2
- package/dist/common/types.js +3 -1
- package/dist/graph/adapters/langgraph.adapter.d.ts +6 -4
- package/dist/graph/adapters/langgraph.adapter.js +103 -18
- package/dist/graph/contracts/executable-graph.d.ts +11 -2
- package/dist/graph/execution-trace-context.d.ts +4 -0
- package/dist/graph/execution-trace-context.js +3 -1
- package/dist/graph/nodes/rag/types.d.ts +4 -1
- package/dist/providers/http-llm-provider.js +25 -0
- package/dist/registry/graph-loader.js +5 -4
- package/dist/runtime/agent-runtime.d.ts +4 -1
- package/dist/runtime/agent-runtime.js +110 -20
- package/dist/runtime/dev-runtime.d.ts +1 -0
- package/dist/runtime/dev-runtime.js +9 -2
- package/dist/runtime/types.d.ts +1 -1
- 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/sse/events/response-events.d.ts +2 -2
- package/dist/sse/events/response-events.js +1 -1
- package/dist/task/contracts/task-manager.contract.d.ts +2 -0
- package/dist/task/index.d.ts +1 -0
- package/dist/task/index.js +1 -0
- package/dist/task/resolve-task-timeout.d.ts +6 -0
- package/dist/task/resolve-task-timeout.js +16 -0
- package/dist/task/task-timeout.d.ts +1 -1
- package/dist/task/task-timeout.js +3 -2
- package/dist/task/task.service.js +1 -0
- package/package.json +2 -2
|
@@ -7,12 +7,15 @@
|
|
|
7
7
|
* @see 04-runtime-core/design.md §1.5 / §1.7
|
|
8
8
|
*/
|
|
9
9
|
import { randomUUID } from 'node:crypto';
|
|
10
|
+
import { DEFAULT_GRAPH_TIMEOUT_MS } from '../common/types.js';
|
|
11
|
+
import { resolveTaskTimeoutMs } from '../task/resolve-task-timeout.js';
|
|
12
|
+
import { runWithExecutionTrace } from '../graph/execution-trace-context.js';
|
|
10
13
|
import { HITL_TIMEOUT_ERROR_CODE, HITL_TIMEOUT_MESSAGE, HitlAlreadyTimedOutError, HitlNotWaitingError, buildHitlWaitedPayload, isHitlDeadlinePassed, loadHitlCheckpoint, readPersistedHitlWaiting, unwrapHitlPaused, } from '../graph/hitl/hitl-handler.js';
|
|
11
14
|
import { resolveAgentSystemPromptFromLoader } from '../registry/graph-loader.js';
|
|
12
15
|
import { toLoadedSkillMap } from '../tool/system-prompt-builder.js';
|
|
13
16
|
import { assembleLoadedTools } from '../tool/harness-primitives.js';
|
|
14
17
|
import { createFileLlmTurnLogFromEnv, } from '../debug/llm-turn-log.js';
|
|
15
|
-
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';
|
|
16
19
|
import { iterSessionCompression, withCompressionSummaryModel, } from '../session/apply-session-compression.js';
|
|
17
20
|
import { applySessionMemory } from '../session/apply-session-memory.js';
|
|
18
21
|
import { resolveTokenDetail, shouldEmitTokenSse } from '../token/token-detail.js';
|
|
@@ -37,7 +40,7 @@ const DEFAULT_GRAPH_DSL = {
|
|
|
37
40
|
{ from: '__start__', to: 'llm' },
|
|
38
41
|
{ from: 'llm', to: '__end__' },
|
|
39
42
|
],
|
|
40
|
-
config: { timeout:
|
|
43
|
+
config: { timeout: DEFAULT_GRAPH_TIMEOUT_MS },
|
|
41
44
|
};
|
|
42
45
|
/**
|
|
43
46
|
* AgentRuntime — 管理 Graph 执行的 SDK 门面
|
|
@@ -214,6 +217,14 @@ export class AgentRuntime {
|
|
|
214
217
|
ragScope: params.ragScope,
|
|
215
218
|
ragSources: params.ragSources,
|
|
216
219
|
}, userId);
|
|
220
|
+
if (response.code !== 'ok') {
|
|
221
|
+
const output = typeof response.data?.output === 'string' && response.data.output
|
|
222
|
+
? response.data.output
|
|
223
|
+
: response.code;
|
|
224
|
+
throw new AppError(response.code, output, {
|
|
225
|
+
exposeMessage: true,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
217
228
|
return response.data;
|
|
218
229
|
}
|
|
219
230
|
/**
|
|
@@ -241,6 +252,11 @@ export class AgentRuntime {
|
|
|
241
252
|
userId,
|
|
242
253
|
});
|
|
243
254
|
}
|
|
255
|
+
const loadOpts = { agentVersion: params.agentVersion };
|
|
256
|
+
const graphDsl = this.deps.graphLoader
|
|
257
|
+
? await this.deps.graphLoader.loadGraph(agentCode, loadOpts)
|
|
258
|
+
: DEFAULT_GRAPH_DSL;
|
|
259
|
+
const timeout = resolveTaskTimeoutMs(params.config?.timeout, graphDsl.config?.timeout);
|
|
244
260
|
const task = await this.deps.taskService.create({
|
|
245
261
|
id: params.taskId,
|
|
246
262
|
agentId: agentCode,
|
|
@@ -248,9 +264,12 @@ export class AgentRuntime {
|
|
|
248
264
|
userId,
|
|
249
265
|
correlationId,
|
|
250
266
|
input: params.input,
|
|
251
|
-
timeout
|
|
267
|
+
timeout,
|
|
252
268
|
});
|
|
253
269
|
await this.deps.taskService.updateStatus(task.id, 'running');
|
|
270
|
+
const abortSignal = this.deps.timeoutManager?.startTimer(task.id, timeout, async () => {
|
|
271
|
+
await this.deps.taskService.tryUpdateStatus(task.id, 'running', 'timeout', 'Task execution timed out');
|
|
272
|
+
});
|
|
254
273
|
await persistUserTurnMessage(this.deps.messageService, {
|
|
255
274
|
sessionId,
|
|
256
275
|
taskId: task.id,
|
|
@@ -258,10 +277,6 @@ export class AgentRuntime {
|
|
|
258
277
|
content: params.input,
|
|
259
278
|
});
|
|
260
279
|
yield this.toSSE('task.started', correlationId, task.id, { taskId: task.id, sessionId });
|
|
261
|
-
const loadOpts = { agentVersion: params.agentVersion };
|
|
262
|
-
const graphDsl = this.deps.graphLoader
|
|
263
|
-
? await this.deps.graphLoader.loadGraph(agentCode, loadOpts)
|
|
264
|
-
: DEFAULT_GRAPH_DSL;
|
|
265
280
|
const graph = await this.deps.graphEngine.buildGraph(graphDsl);
|
|
266
281
|
const loadedToolsBase = await this.resolveLoadedTools(agentCode, params.agentVersion);
|
|
267
282
|
const loadedSkills = this.deps.graphLoader
|
|
@@ -279,7 +294,13 @@ export class AgentRuntime {
|
|
|
279
294
|
? (await this.deps.graphLoader.resolveAgentDir(agentCode, params.agentVersion)) ?? undefined
|
|
280
295
|
: undefined;
|
|
281
296
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.deps.graphLoader, agentCode, params.agentVersion);
|
|
282
|
-
|
|
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;
|
|
283
304
|
const compressionOptions = await withCompressionSummaryModel({
|
|
284
305
|
...this.deps.sessionCompression,
|
|
285
306
|
turnLog: this.deps.sessionCompression?.turnLog ?? this.deps.turnLog,
|
|
@@ -297,6 +318,9 @@ export class AgentRuntime {
|
|
|
297
318
|
history = step.messages;
|
|
298
319
|
continue;
|
|
299
320
|
}
|
|
321
|
+
if (step.kind === 'completed') {
|
|
322
|
+
compressed = step.payload.success === true;
|
|
323
|
+
}
|
|
300
324
|
yield this.toSSE(step.kind === 'started' ? 'session.compression.started' : 'session.compression.completed', correlationId, task.id, { ...step.payload });
|
|
301
325
|
}
|
|
302
326
|
history = await applySessionMemory({
|
|
@@ -307,11 +331,11 @@ export class AgentRuntime {
|
|
|
307
331
|
options: this.deps.sessionMemory,
|
|
308
332
|
});
|
|
309
333
|
const tokenDetail = resolveTokenDetail(params.config?.tokenDetail);
|
|
310
|
-
const messages =
|
|
334
|
+
const { messages, replaceCheckpointMessages } = turnGraphMessagesAfterCompression({
|
|
311
335
|
history,
|
|
312
336
|
input: params.input,
|
|
313
|
-
|
|
314
|
-
|
|
337
|
+
fromCheckpoint: facing.fromCheckpoint,
|
|
338
|
+
compressed,
|
|
315
339
|
});
|
|
316
340
|
const startTime = Date.now();
|
|
317
341
|
const assistantBuckets = createStreamAssistantBuckets();
|
|
@@ -321,8 +345,9 @@ export class AgentRuntime {
|
|
|
321
345
|
const toolResults = [];
|
|
322
346
|
const nodeTokenUsages = [];
|
|
323
347
|
try {
|
|
324
|
-
for await (const runtimeEvent of this.deps.graphEngine.stream(graph, {
|
|
348
|
+
for await (const runtimeEvent of runWithExecutionTrace({ abortSignal }, () => this.deps.graphEngine.stream(graph, {
|
|
325
349
|
messages,
|
|
350
|
+
replaceCheckpointMessages,
|
|
326
351
|
userId,
|
|
327
352
|
delegationToken: ctx.delegationToken,
|
|
328
353
|
agentCode,
|
|
@@ -343,7 +368,9 @@ export class AgentRuntime {
|
|
|
343
368
|
skillInput: resolveSkillInputFromInvokeInput(params.input),
|
|
344
369
|
traceId: resolveTraceId(params),
|
|
345
370
|
traceCollector: this.deps.traceCollector,
|
|
346
|
-
|
|
371
|
+
abortSignal,
|
|
372
|
+
abortDeadlineMs: Date.now() + timeout,
|
|
373
|
+
}))) {
|
|
347
374
|
if (runtimeEvent.type === 'tool.called') {
|
|
348
375
|
const p = runtimeEvent.payload;
|
|
349
376
|
if (p.toolCallId && p.toolName) {
|
|
@@ -415,6 +442,17 @@ export class AgentRuntime {
|
|
|
415
442
|
yield this.toSSE('hitl.waited', correlationId, task.id, buildHitlWaitedPayload(hitlPaused));
|
|
416
443
|
return;
|
|
417
444
|
}
|
|
445
|
+
const timedOut = err instanceof Error && /timed out/i.test(err.message);
|
|
446
|
+
if (timedOut) {
|
|
447
|
+
await recordTaskTokenUsages(this.deps.taskService, task.id, nodeTokenUsages);
|
|
448
|
+
await this.deps.taskService.tryUpdateStatus(task.id, 'running', 'timeout');
|
|
449
|
+
yield this.toSSE('task.error', correlationId, task.id, {
|
|
450
|
+
code: ErrorCodes.TASK_TIMEOUT,
|
|
451
|
+
message: 'Task execution timed out',
|
|
452
|
+
recoverable: false,
|
|
453
|
+
});
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
418
456
|
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
419
457
|
await recordTaskTokenUsages(this.deps.taskService, task.id, nodeTokenUsages);
|
|
420
458
|
await this.deps.taskService.updateStatus(task.id, 'failed', message);
|
|
@@ -425,6 +463,9 @@ export class AgentRuntime {
|
|
|
425
463
|
});
|
|
426
464
|
throw err;
|
|
427
465
|
}
|
|
466
|
+
finally {
|
|
467
|
+
this.deps.timeoutManager?.clearTimer(task.id);
|
|
468
|
+
}
|
|
428
469
|
}
|
|
429
470
|
/**
|
|
430
471
|
* HITL 用户响应后继续执行(同步结果)。
|
|
@@ -483,6 +524,9 @@ export class AgentRuntime {
|
|
|
483
524
|
if (!claimed) {
|
|
484
525
|
throw new AppError(ErrorCodes.TASK_HITL_INVALID_STATE, `Task ${taskId} is no longer waiting for HITL`, { exposeMessage: true });
|
|
485
526
|
}
|
|
527
|
+
const abortSignal = this.deps.timeoutManager?.startTimer(taskId, resolveTaskTimeoutMs(task.timeout), async () => {
|
|
528
|
+
await this.deps.taskService.tryUpdateStatus(taskId, 'running', 'timeout', 'Task execution timed out');
|
|
529
|
+
});
|
|
486
530
|
const tokenDetail = resolveTokenDetail(response.tokenDetail);
|
|
487
531
|
yield this.toSSE('hitl.resumed', correlationId, taskId, {
|
|
488
532
|
choice: response.choice,
|
|
@@ -507,6 +551,8 @@ export class AgentRuntime {
|
|
|
507
551
|
: await this.resolveLoadedSkills(agentCode, response.userId, agentVersion);
|
|
508
552
|
const continueState = {
|
|
509
553
|
...resumedState,
|
|
554
|
+
abortSignal,
|
|
555
|
+
abortDeadlineMs: Date.now() + resolveTaskTimeoutMs(task.timeout),
|
|
510
556
|
messages: resumedState.messages ?? [],
|
|
511
557
|
replaceCheckpointMessages: true,
|
|
512
558
|
sessionId: resumedState.sessionId ?? sessionId,
|
|
@@ -542,7 +588,7 @@ export class AgentRuntime {
|
|
|
542
588
|
const toolResults = [];
|
|
543
589
|
const nodeTokenUsages = [];
|
|
544
590
|
try {
|
|
545
|
-
for await (const runtimeEvent of this.deps.graphEngine.stream(graph, continueState)) {
|
|
591
|
+
for await (const runtimeEvent of runWithExecutionTrace({ abortSignal, abortDeadlineMs: continueState.abortDeadlineMs }, () => this.deps.graphEngine.stream(graph, continueState))) {
|
|
546
592
|
if (runtimeEvent.type === 'tool.called') {
|
|
547
593
|
const p = runtimeEvent.payload;
|
|
548
594
|
if (p.toolCallId && p.toolName) {
|
|
@@ -617,6 +663,17 @@ export class AgentRuntime {
|
|
|
617
663
|
yield this.toSSE('hitl.waited', correlationId, taskId, buildHitlWaitedPayload(hitlPaused));
|
|
618
664
|
return;
|
|
619
665
|
}
|
|
666
|
+
const timedOut = err instanceof Error && /timed out/i.test(err.message);
|
|
667
|
+
if (timedOut) {
|
|
668
|
+
await recordTaskTokenUsages(this.deps.taskService, taskId, nodeTokenUsages);
|
|
669
|
+
await this.deps.taskService.tryUpdateStatus(taskId, 'running', 'timeout');
|
|
670
|
+
yield this.toSSE('task.error', correlationId, taskId, {
|
|
671
|
+
code: ErrorCodes.TASK_TIMEOUT,
|
|
672
|
+
message: 'Task execution timed out',
|
|
673
|
+
recoverable: false,
|
|
674
|
+
});
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
620
677
|
const appErr = err instanceof AppError ? err : AppError.fromUnknown(err);
|
|
621
678
|
const failMsg = appErr.exposeMessage ? appErr.message : '系统内部错误';
|
|
622
679
|
await recordTaskTokenUsages(this.deps.taskService, taskId, nodeTokenUsages);
|
|
@@ -628,6 +685,9 @@ export class AgentRuntime {
|
|
|
628
685
|
});
|
|
629
686
|
throw err instanceof AppError ? err : appErr;
|
|
630
687
|
}
|
|
688
|
+
finally {
|
|
689
|
+
this.deps.timeoutManager?.clearTimer(taskId);
|
|
690
|
+
}
|
|
631
691
|
}
|
|
632
692
|
/**
|
|
633
693
|
* 从 Checkpoint Fork 并真实重新执行。
|
|
@@ -658,6 +718,12 @@ export class AgentRuntime {
|
|
|
658
718
|
const newThreadId = randomUUID();
|
|
659
719
|
const forkedCheckpoint = await this.deps.checkpointSaver.fork(checkpointId, newThreadId, overrides);
|
|
660
720
|
const correlationId = overrides.executionContext?.correlationId ?? randomUUID();
|
|
721
|
+
const agentVersion = overrides.agentVersion ??
|
|
722
|
+
(typeof source.state.agentVersion === 'string' ? source.state.agentVersion : undefined);
|
|
723
|
+
const graphDsl = this.deps.graphLoader && typeof this.deps.graphLoader.loadGraph === 'function'
|
|
724
|
+
? await this.deps.graphLoader.loadGraph(agentCode, { agentVersion })
|
|
725
|
+
: DEFAULT_GRAPH_DSL;
|
|
726
|
+
const timeout = resolveTaskTimeoutMs(undefined, graphDsl.config?.timeout);
|
|
661
727
|
const task = await this.deps.taskService.create({
|
|
662
728
|
id: taskId,
|
|
663
729
|
agentId: agentCode,
|
|
@@ -665,12 +731,13 @@ export class AgentRuntime {
|
|
|
665
731
|
userId,
|
|
666
732
|
correlationId,
|
|
667
733
|
input: { forkFrom: checkpointId, overrides },
|
|
668
|
-
timeout
|
|
734
|
+
timeout,
|
|
669
735
|
});
|
|
670
736
|
await this.deps.taskService.updateStatus(task.id, 'running');
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
737
|
+
const abortSignal = this.deps.timeoutManager?.startTimer(task.id, timeout, async () => {
|
|
738
|
+
await this.deps.taskService.tryUpdateStatus(task.id, 'running', 'timeout', 'Task execution timed out');
|
|
739
|
+
});
|
|
740
|
+
const graph = await this.deps.graphEngine.buildGraph(graphDsl);
|
|
674
741
|
const loadedToolsBase = await this.resolveLoadedTools(agentCode, agentVersion);
|
|
675
742
|
const skillBundle = await this.resolveLoadedSkills(agentCode, userId, agentVersion);
|
|
676
743
|
const loadedTools = assembleLoadedTools(loadedToolsBase);
|
|
@@ -681,11 +748,12 @@ export class AgentRuntime {
|
|
|
681
748
|
const agentSystemPrompt = (await resolveAgentSystemPromptFromLoader(this.deps.graphLoader, agentCode, agentVersion)) ?? (typeof forkedCheckpoint.state.agentSystemPrompt === 'string'
|
|
682
749
|
? forkedCheckpoint.state.agentSystemPrompt
|
|
683
750
|
: undefined);
|
|
751
|
+
const abortDeadlineMs = Date.now() + timeout;
|
|
684
752
|
try {
|
|
685
753
|
const forkState = { ...forkedCheckpoint.state };
|
|
686
754
|
delete forkState.toolSpanId;
|
|
687
755
|
delete forkState.hitlToolCallId;
|
|
688
|
-
const result = await this.deps.graphEngine.invoke(graph, {
|
|
756
|
+
const result = await runWithExecutionTrace({ abortSignal, abortDeadlineMs }, () => this.deps.graphEngine.invoke(graph, {
|
|
689
757
|
messages: Array.isArray(forkState.messages)
|
|
690
758
|
? forkState.messages
|
|
691
759
|
: [],
|
|
@@ -701,7 +769,9 @@ export class AgentRuntime {
|
|
|
701
769
|
taskId: task.id,
|
|
702
770
|
traceId,
|
|
703
771
|
traceCollector: this.deps.traceCollector,
|
|
704
|
-
|
|
772
|
+
abortSignal,
|
|
773
|
+
abortDeadlineMs,
|
|
774
|
+
}));
|
|
705
775
|
if (result.tokenUsage.perNode && result.tokenUsage.perNode.length > 0) {
|
|
706
776
|
await recordTaskTokenUsages(this.deps.taskService, task.id, result.tokenUsage.perNode);
|
|
707
777
|
}
|
|
@@ -760,6 +830,23 @@ export class AgentRuntime {
|
|
|
760
830
|
await recordTaskTokenUsages(this.deps.taskService, task.id, usages);
|
|
761
831
|
const input_tokens = usages.reduce((sum, row) => sum + row.inputTokens, 0);
|
|
762
832
|
const output_tokens = usages.reduce((sum, row) => sum + row.outputTokens, 0);
|
|
833
|
+
const timedOut = err instanceof Error && /timed out/i.test(err.message);
|
|
834
|
+
if (timedOut) {
|
|
835
|
+
await this.deps.taskService.tryUpdateStatus(task.id, 'running', 'timeout');
|
|
836
|
+
return {
|
|
837
|
+
newTraceId: traceId,
|
|
838
|
+
originalTraceId,
|
|
839
|
+
newTaskId: task.id,
|
|
840
|
+
checkpointId,
|
|
841
|
+
status: 'timeout',
|
|
842
|
+
output: message,
|
|
843
|
+
tokenUsage: {
|
|
844
|
+
input_tokens,
|
|
845
|
+
output_tokens,
|
|
846
|
+
total_tokens: input_tokens + output_tokens,
|
|
847
|
+
},
|
|
848
|
+
};
|
|
849
|
+
}
|
|
763
850
|
await this.deps.taskService.updateStatus(task.id, 'failed', message);
|
|
764
851
|
return {
|
|
765
852
|
newTraceId: traceId,
|
|
@@ -775,6 +862,9 @@ export class AgentRuntime {
|
|
|
775
862
|
},
|
|
776
863
|
};
|
|
777
864
|
}
|
|
865
|
+
finally {
|
|
866
|
+
this.deps.timeoutManager?.clearTimer(task.id);
|
|
867
|
+
}
|
|
778
868
|
}
|
|
779
869
|
async prepareHitlResume(taskId, response) {
|
|
780
870
|
const task = await this.deps.taskService.getTask(taskId);
|
|
@@ -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
|
};
|
|
@@ -31,7 +31,7 @@ import { createToolExecutor } from '../tool/create-tool-executor.js';
|
|
|
31
31
|
import { DevToolExecutor } from '../tool/dev-tool-executor.js';
|
|
32
32
|
import { captureOverlayJailOptions, withHarnessPrimitives } from '../tool/harness-primitives.js';
|
|
33
33
|
import { AllowAllGovernanceClient, HttpGovernanceClient, } from '../governance/index.js';
|
|
34
|
-
import { InvokeOrchestrator, StreamOrchestrator, SessionService, MessageService, SessionContextService, TaskService, LangGraphAdapter, HitlHandler, TokenBudgetService, TokenStatsService, TokenBudgetGuard, SessionBudgetChecker, createAgentRegistryClient, GraphLoader, } from '../index.js';
|
|
34
|
+
import { InvokeOrchestrator, StreamOrchestrator, SessionService, MessageService, SessionContextService, TaskService, TaskTimeoutManager, LangGraphAdapter, HitlHandler, TokenBudgetService, TokenStatsService, TokenBudgetGuard, SessionBudgetChecker, createAgentRegistryClient, GraphLoader, } from '../index.js';
|
|
35
35
|
import { createAgentRuntime } from './agent-runtime.js';
|
|
36
36
|
import { assembleRuntimeStorage } from './storage-profile.js';
|
|
37
37
|
import { createDevRagNodeDeps } from './dev-rag-deps.js';
|
|
@@ -139,6 +139,7 @@ export function createDevRuntime(options) {
|
|
|
139
139
|
const sessionContextService = new SessionContextService(sessionService, messageService);
|
|
140
140
|
// ── Task / Token ──
|
|
141
141
|
const taskService = new TaskService();
|
|
142
|
+
const timeoutManager = new TaskTimeoutManager();
|
|
142
143
|
const budgetService = new TokenBudgetService(storage.tokenBudgetRepository);
|
|
143
144
|
const tokenStatsService = new TokenStatsService();
|
|
144
145
|
const tokenBudgetGuard = new TokenBudgetGuard(budgetService);
|
|
@@ -202,6 +203,7 @@ export function createDevRuntime(options) {
|
|
|
202
203
|
checkpointSaver: storage.checkpointSaver,
|
|
203
204
|
sessionCompression,
|
|
204
205
|
sessionMemory,
|
|
206
|
+
timeoutManager,
|
|
205
207
|
});
|
|
206
208
|
const streamOrchestrator = new StreamOrchestrator({
|
|
207
209
|
sessionContextService,
|
|
@@ -215,6 +217,7 @@ export function createDevRuntime(options) {
|
|
|
215
217
|
checkpointSaver: storage.checkpointSaver,
|
|
216
218
|
sessionCompression,
|
|
217
219
|
sessionMemory,
|
|
220
|
+
timeoutManager,
|
|
218
221
|
});
|
|
219
222
|
const agentRuntime = createAgentRuntime({
|
|
220
223
|
invokeOrchestrator,
|
|
@@ -229,6 +232,7 @@ export function createDevRuntime(options) {
|
|
|
229
232
|
turnLog,
|
|
230
233
|
sessionCompression,
|
|
231
234
|
sessionMemory,
|
|
235
|
+
timeoutManager,
|
|
232
236
|
});
|
|
233
237
|
return {
|
|
234
238
|
invokeOrchestrator,
|
|
@@ -243,7 +247,10 @@ export function createDevRuntime(options) {
|
|
|
243
247
|
taskService,
|
|
244
248
|
tokenStatsService,
|
|
245
249
|
graphLoader,
|
|
246
|
-
dispose:
|
|
250
|
+
dispose: () => {
|
|
251
|
+
stopSkillWatch();
|
|
252
|
+
timeoutManager.dispose();
|
|
253
|
+
},
|
|
247
254
|
diagnostics: {
|
|
248
255
|
packageVersion: RUNTIME_PACKAGE_VERSION,
|
|
249
256
|
deploymentContext,
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -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 {};
|