@gt-fe/eap-runtime 0.0.19 → 0.0.21
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 +3 -2
- package/dist/api/handlers/invoke.handler.d.ts +7 -0
- package/dist/api/handlers/invoke.handler.js +57 -24
- package/dist/api/handlers/stream.handler.d.ts +6 -0
- package/dist/api/handlers/stream.handler.js +59 -39
- package/dist/api/routes/agent.routes.d.ts +8 -1
- package/dist/common/types.d.ts +2 -2
- package/dist/common/types.js +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/trace-collector.d.ts +16 -8
- package/dist/execution/index.d.ts +2 -0
- package/dist/execution/index.js +2 -0
- package/dist/execution/interceptors.d.ts +40 -0
- package/dist/execution/interceptors.js +23 -0
- package/dist/execution/ports.d.ts +18 -0
- package/dist/execution/ports.js +13 -0
- package/dist/governance/audit-reporter.d.ts +14 -0
- package/dist/governance/audit-reporter.js +18 -0
- package/dist/governance/index.d.ts +2 -0
- package/dist/governance/index.js +1 -0
- package/dist/governance/permission-guard.d.ts +6 -2
- package/dist/governance/permission-guard.js +38 -5
- package/dist/graph/adapters/graph-nesting.d.ts +12 -0
- package/dist/graph/adapters/graph-nesting.js +29 -0
- package/dist/graph/adapters/index.d.ts +2 -0
- package/dist/graph/adapters/index.js +2 -0
- package/dist/graph/adapters/langgraph.adapter.d.ts +26 -11
- package/dist/graph/adapters/langgraph.adapter.js +178 -83
- package/dist/graph/adapters/skill-entry-span.d.ts +37 -0
- package/dist/graph/adapters/skill-entry-span.js +78 -0
- package/dist/graph/adapters/skill-isolate.d.ts +17 -0
- package/dist/graph/adapters/skill-isolate.js +50 -0
- package/dist/graph/adapters/skill-read.d.ts +10 -0
- package/dist/graph/adapters/skill-read.js +33 -0
- package/dist/graph/adapters/subgraph-compiler.d.ts +7 -2
- package/dist/graph/adapters/subgraph-compiler.js +81 -35
- package/dist/graph/contracts/executable-graph.d.ts +27 -5
- package/dist/graph/hitl/hitl-handler.js +2 -0
- package/dist/graph/nodes/index.d.ts +1 -0
- package/dist/graph/nodes/index.js +1 -0
- package/dist/graph/nodes/llm-node.d.ts +3 -1
- package/dist/graph/nodes/llm-node.js +26 -2
- package/dist/graph/nodes/rag/dedup.d.ts +20 -0
- package/dist/graph/nodes/rag/dedup.js +77 -0
- package/dist/graph/nodes/rag/denoise.d.ts +58 -0
- package/dist/graph/nodes/rag/denoise.js +220 -0
- package/dist/graph/nodes/rag/fusion/fuse.d.ts +11 -0
- package/dist/graph/nodes/rag/fusion/fuse.js +25 -0
- package/dist/graph/nodes/rag/fusion/normalize.d.ts +9 -0
- package/dist/graph/nodes/rag/fusion/normalize.js +135 -0
- package/dist/graph/nodes/rag/index.d.ts +18 -0
- package/dist/graph/nodes/rag/index.js +17 -0
- package/dist/graph/nodes/rag/prompts.d.ts +12 -0
- package/dist/graph/nodes/rag/prompts.js +40 -0
- package/dist/graph/nodes/rag/rag-node.d.ts +21 -0
- package/dist/graph/nodes/rag/rag-node.js +135 -0
- package/dist/graph/nodes/rag/rag-search-tool.d.ts +48 -0
- package/dist/graph/nodes/rag/rag-search-tool.js +230 -0
- package/dist/graph/nodes/rag/react-loop.d.ts +57 -0
- package/dist/graph/nodes/rag/react-loop.js +467 -0
- package/dist/graph/nodes/rag/retrievers/http-embedder.d.ts +29 -0
- package/dist/graph/nodes/rag/retrievers/http-embedder.js +66 -0
- package/dist/graph/nodes/rag/retrievers/in-memory-retriever.d.ts +17 -0
- package/dist/graph/nodes/rag/retrievers/in-memory-retriever.js +18 -0
- package/dist/graph/nodes/rag/retrievers/local-sqlite-retriever.d.ts +46 -0
- package/dist/graph/nodes/rag/retrievers/local-sqlite-retriever.js +85 -0
- package/dist/graph/nodes/rag/tools.d.ts +90 -0
- package/dist/graph/nodes/rag/tools.js +260 -0
- package/dist/graph/nodes/rag/types.d.ts +226 -0
- package/dist/graph/nodes/rag/types.js +9 -0
- package/dist/graph/nodes/tool-node.d.ts +25 -1
- package/dist/graph/nodes/tool-node.js +193 -35
- package/dist/graph/skill-audit-recorder.d.ts +4 -4
- package/dist/graph/skill-audit-recorder.js +3 -3
- package/dist/graph/trace-span.d.ts +2 -9
- package/dist/graph/trace-span.js +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/memory/semantic-retrieval.service.d.ts +1 -1
- package/dist/memory/semantic-retrieval.service.js +13 -23
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/providers/routing-llm-provider.d.ts +5 -0
- package/dist/providers/routing-llm-provider.js +66 -1
- package/dist/runtime/agent-runtime.d.ts +5 -0
- package/dist/runtime/agent-runtime.js +105 -63
- package/dist/runtime/dev-rag-deps.d.ts +21 -0
- package/dist/runtime/dev-rag-deps.js +186 -0
- package/dist/runtime/dev-runtime.d.ts +16 -1
- package/dist/runtime/dev-runtime.js +59 -5
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/types.d.ts +10 -9
- package/dist/session/apply-session-compression.d.ts +81 -0
- package/dist/session/apply-session-compression.js +262 -0
- package/dist/session/apply-session-memory.d.ts +21 -0
- package/dist/session/apply-session-memory.js +48 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/persist-turn-messages.d.ts +51 -0
- package/dist/session/persist-turn-messages.js +140 -3
- package/dist/sse/events/index.d.ts +13 -4
- package/dist/sse/events/index.js +9 -1
- package/dist/sse/events/rag-events.d.ts +63 -0
- package/dist/sse/events/rag-events.js +9 -0
- package/dist/sse/events/session-events.d.ts +38 -0
- package/dist/sse/events/session-events.js +4 -0
- package/dist/sse/trace-collector.d.ts +2 -9
- package/dist/token/index.d.ts +3 -0
- package/dist/token/index.js +2 -0
- package/dist/token/record-task-token-usage.d.ts +23 -0
- package/dist/token/record-task-token-usage.js +76 -0
- package/dist/token/token-detail-mode.d.ts +4 -4
- package/dist/token/token-detail-mode.js +4 -4
- package/dist/token/token-detail.d.ts +8 -0
- package/dist/token/token-detail.js +22 -0
- package/dist/token/token-stats.service.d.ts +4 -2
- package/dist/token/token-stats.service.js +5 -1
- package/dist/tool/create-tool-executor.d.ts +6 -0
- package/dist/tool/create-tool-executor.js +4 -3
- package/dist/tool/harness-primitives.d.ts +9 -1
- package/dist/tool/harness-primitives.js +43 -12
- package/dist/tool/index.d.ts +3 -2
- package/dist/tool/index.js +3 -2
- package/dist/tool/mcp-list.d.ts +12 -0
- package/dist/tool/mcp-list.js +15 -0
- package/dist/tool/tool-executor.interface.d.ts +27 -0
- package/dist/tool/tool-orchestrator.d.ts +3 -1
- package/dist/tool/tool-orchestrator.js +15 -6
- package/dist/tool/tool-retry.d.ts +15 -0
- package/dist/tool/tool-retry.js +69 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,7 +8,8 @@ Enterprise Agent Platform **执行引擎**(LangGraph.js):Agent Graph 编
|
|
|
8
8
|
|
|
9
9
|
| 日期 | 摘要 |
|
|
10
10
|
|------|------|
|
|
11
|
-
| 2026-08
|
|
11
|
+
| 2026-09-08 | [`docs/TODO.md`](./docs/TODO.md) 改为主路径状态(已落地 / 不在本包),不是排期 |
|
|
12
|
+
| 2026-08-29 | 待办收口:[`docs/TODO.md`](./docs/TODO.md)。此后压缩已挂主路径(默认关);窗口展示不在 Runtime;Gateway 须 `tools/list` |
|
|
12
13
|
| 2026-08-24 | 0.0.10:官方 Skill / Harness、`ForkParams`、对话详情、存储两档。依赖 `@eap/contracts@0.0.9`。详见 [CHANGELOG.md](./CHANGELOG.md) |
|
|
13
14
|
| 2026-08-21 | 0.0.9:Gateway `/mcp` JSON-RPC 补 `Accept`;`tools/call` 透传工具版本。详见 [CHANGELOG.md](./CHANGELOG.md) |
|
|
14
15
|
| 2026-08-19 | 0.0.8:Skill 子图 SSE 闭环;`ExecuteParams.traceId`;`tool.called` 改为 Tool 节点开始执行。依赖 `@eap/contracts@0.0.8`。详见 [CHANGELOG.md](./CHANGELOG.md) |
|
|
@@ -29,7 +30,7 @@ Enterprise Agent Platform **执行引擎**(LangGraph.js):Agent Graph 编
|
|
|
29
30
|
| 环境变量总表 | [`docs/governance/governance-env-config.md`](../docs/governance/governance-env-config.md) |
|
|
30
31
|
| 预装联调契约 | [`docs/contracts/preload-hosting-runtime-contract.md`](../docs/contracts/preload-hosting-runtime-contract.md) |
|
|
31
32
|
| 预装加载与请求流转 | [`docs/contracts/runtime-preinstall-load-flow.md`](../docs/contracts/runtime-preinstall-load-flow.md) |
|
|
32
|
-
| Runtime
|
|
33
|
+
| Runtime 主路径状态 | [`docs/TODO.md`](./docs/TODO.md) |
|
|
33
34
|
| 技能 FS 待办 | [`docs/TODO-skill-fs.md`](./docs/TODO-skill-fs.md) |
|
|
34
35
|
| 本地 Agent 闭环 | [`docs/workflows/local-agent-dev-loop.md`](../docs/workflows/local-agent-dev-loop.md) |
|
|
35
36
|
|
|
@@ -25,6 +25,8 @@ import type { InvokeRequest, InvokeResponse } from '../routes/agent.routes.js';
|
|
|
25
25
|
import { type GraphLoader } from '../../registry/graph-loader.js';
|
|
26
26
|
import type { ICheckpointSaver } from '../../checkpoint/contracts/checkpoint-saver.contract.js';
|
|
27
27
|
import type { ITraceCollector } from '../../contracts/trace-collector.js';
|
|
28
|
+
import { type SessionCompressionOptions } from '../../session/apply-session-compression.js';
|
|
29
|
+
import { type SessionMemoryOptions } from '../../session/apply-session-memory.js';
|
|
28
30
|
/**
|
|
29
31
|
* InvokeOrchestrator 构造器依赖
|
|
30
32
|
*/
|
|
@@ -42,6 +44,8 @@ export interface InvokeOrchestratorDeps {
|
|
|
42
44
|
/** 用于判断 session Checkpoint 是否已有历史,避免双重 append */
|
|
43
45
|
checkpointSaver?: ICheckpointSaver;
|
|
44
46
|
traceCollector?: ITraceCollector;
|
|
47
|
+
sessionCompression?: SessionCompressionOptions;
|
|
48
|
+
sessionMemory?: SessionMemoryOptions;
|
|
45
49
|
}
|
|
46
50
|
/**
|
|
47
51
|
* InvokeOrchestrator — 同步调用全流程编排
|
|
@@ -60,6 +64,8 @@ export declare class InvokeOrchestrator {
|
|
|
60
64
|
private readonly graphLoader?;
|
|
61
65
|
private readonly checkpointSaver?;
|
|
62
66
|
private readonly traceCollector?;
|
|
67
|
+
private readonly sessionCompression?;
|
|
68
|
+
private readonly sessionMemory?;
|
|
63
69
|
constructor(deps: InvokeOrchestratorDeps);
|
|
64
70
|
/**
|
|
65
71
|
* 执行同步调用编排
|
|
@@ -72,6 +78,7 @@ export declare class InvokeOrchestrator {
|
|
|
72
78
|
private buildResponse;
|
|
73
79
|
private buildErrorResponse;
|
|
74
80
|
private isTimeoutError;
|
|
81
|
+
private persistInvokeUsages;
|
|
75
82
|
}
|
|
76
83
|
/** @deprecated 请直接从 `graph/hitl/hitl-handler` 导入;此处保留 re-export 兼容旧测试引用 */
|
|
77
84
|
export { unwrapHitlPaused } from '../../graph/hitl/hitl-handler.js';
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
* @see requirements.md Requirement 11 AC1-AC2
|
|
16
16
|
*/
|
|
17
17
|
import { randomUUID } from 'node:crypto';
|
|
18
|
-
import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, } from '../../session/persist-turn-messages.js';
|
|
18
|
+
import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, messageRecordsToGraphMessages, 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
|
+
import { recordTaskTokenUsages, tokenUsagesFromLlmChannel, } from '../../token/record-task-token-usage.js';
|
|
21
22
|
import { resolveExecutionContext } from '../../governance/governance-client.js';
|
|
22
23
|
import { resolveSkillInputFromInvokeInput } from '../../registry/skill-io-guard.js';
|
|
23
24
|
import { resolveAgentSystemPromptFromLoader } from '../../registry/graph-loader.js';
|
|
@@ -27,6 +28,8 @@ import { assembleLoadedTools } from '../../tool/harness-primitives.js';
|
|
|
27
28
|
import { resolveAgentCode } from '../../common/agent-code.js';
|
|
28
29
|
import { unwrapHitlPaused } from '../../graph/hitl/hitl-handler.js';
|
|
29
30
|
import { resolveTraceId } from '../../common/trace-id.js';
|
|
31
|
+
import { applySessionCompression, } from '../../session/apply-session-compression.js';
|
|
32
|
+
import { applySessionMemory, } from '../../session/apply-session-memory.js';
|
|
30
33
|
// ─────────────────────────────────────────────
|
|
31
34
|
// Default Graph DSL fallback
|
|
32
35
|
// ─────────────────────────────────────────────
|
|
@@ -63,6 +66,8 @@ export class InvokeOrchestrator {
|
|
|
63
66
|
graphLoader;
|
|
64
67
|
checkpointSaver;
|
|
65
68
|
traceCollector;
|
|
69
|
+
sessionCompression;
|
|
70
|
+
sessionMemory;
|
|
66
71
|
constructor(deps) {
|
|
67
72
|
this.sessionContextService = deps.sessionContextService;
|
|
68
73
|
this.taskService = deps.taskService;
|
|
@@ -74,6 +79,8 @@ export class InvokeOrchestrator {
|
|
|
74
79
|
this.graphLoader = deps.graphLoader;
|
|
75
80
|
this.checkpointSaver = deps.checkpointSaver;
|
|
76
81
|
this.traceCollector = deps.traceCollector;
|
|
82
|
+
this.sessionCompression = deps.sessionCompression;
|
|
83
|
+
this.sessionMemory = deps.sessionMemory;
|
|
77
84
|
}
|
|
78
85
|
/**
|
|
79
86
|
* 执行同步调用编排
|
|
@@ -158,6 +165,7 @@ export class InvokeOrchestrator {
|
|
|
158
165
|
});
|
|
159
166
|
// ─── Step 5: 构建/执行 Graph ───
|
|
160
167
|
let graphResult;
|
|
168
|
+
let finishPoints;
|
|
161
169
|
try {
|
|
162
170
|
// 构建图 — 每次请求创建独立 StateGraph 实例,确保请求间无共享可变状态
|
|
163
171
|
const loadOpts = { agentVersion: req.agentVersion };
|
|
@@ -165,6 +173,7 @@ export class InvokeOrchestrator {
|
|
|
165
173
|
? await this.graphLoader.loadGraph(agentCode, loadOpts)
|
|
166
174
|
: DEFAULT_GRAPH_DSL;
|
|
167
175
|
const graph = await this.graphEngine.buildGraph(graphDsl);
|
|
176
|
+
finishPoints = graph.finishPoints;
|
|
168
177
|
const loadedToolsBase = this.graphLoader
|
|
169
178
|
? toLoadedToolMap(await this.graphLoader.loadToolDefinitions(agentCode, loadOpts))
|
|
170
179
|
: undefined;
|
|
@@ -184,7 +193,19 @@ export class InvokeOrchestrator {
|
|
|
184
193
|
: undefined;
|
|
185
194
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.graphLoader, agentCode, req.agentVersion);
|
|
186
195
|
// 组装 GraphState:有 Checkpoint 历史则只追加本轮 user,避免双重 append
|
|
187
|
-
const history =
|
|
196
|
+
const history = await applySessionMemory({
|
|
197
|
+
history: await applySessionCompression({
|
|
198
|
+
history: messageRecordsToGraphMessages(sessionContext.messages),
|
|
199
|
+
sessionId,
|
|
200
|
+
userId: effectiveUserId,
|
|
201
|
+
taskId: task.id,
|
|
202
|
+
options: this.sessionCompression,
|
|
203
|
+
}),
|
|
204
|
+
userId: effectiveUserId,
|
|
205
|
+
agentCode,
|
|
206
|
+
query: typeof req.input === 'string' ? req.input : undefined,
|
|
207
|
+
options: this.sessionMemory,
|
|
208
|
+
});
|
|
188
209
|
const messages = await buildTurnGraphMessages({
|
|
189
210
|
history,
|
|
190
211
|
input: req.input,
|
|
@@ -204,6 +225,8 @@ export class InvokeOrchestrator {
|
|
|
204
225
|
sessionId,
|
|
205
226
|
executionContext: ctx,
|
|
206
227
|
forceToolServer: req.config?.forceToolServer,
|
|
228
|
+
ragScope: req.ragScope,
|
|
229
|
+
ragSources: req.ragSources,
|
|
207
230
|
loadedTools,
|
|
208
231
|
loadedSkills,
|
|
209
232
|
skillDefinitions,
|
|
@@ -216,6 +239,8 @@ export class InvokeOrchestrator {
|
|
|
216
239
|
});
|
|
217
240
|
}
|
|
218
241
|
catch (error) {
|
|
242
|
+
const usages = await this.persistInvokeUsages(task.id, sessionId);
|
|
243
|
+
const tokenUsage = tokenUsageTotals(usages);
|
|
219
244
|
const hitlPaused = unwrapHitlPaused(error);
|
|
220
245
|
if (hitlPaused) {
|
|
221
246
|
await this.taskService.updateStatus(task.id, 'waiting_hitl', buildHitlWaitingTaskOutput({
|
|
@@ -235,7 +260,7 @@ export class InvokeOrchestrator {
|
|
|
235
260
|
taskId: task.id,
|
|
236
261
|
sessionId,
|
|
237
262
|
output: hitlPaused.question,
|
|
238
|
-
tokenUsage
|
|
263
|
+
tokenUsage,
|
|
239
264
|
duration: Date.now() - startTime,
|
|
240
265
|
status: 'waiting_hitl',
|
|
241
266
|
hitl: {
|
|
@@ -252,7 +277,7 @@ export class InvokeOrchestrator {
|
|
|
252
277
|
taskId: task.id,
|
|
253
278
|
sessionId,
|
|
254
279
|
output: error.message,
|
|
255
|
-
tokenUsage
|
|
280
|
+
tokenUsage,
|
|
256
281
|
duration: Date.now() - startTime,
|
|
257
282
|
status: 'token_exhausted',
|
|
258
283
|
});
|
|
@@ -264,7 +289,7 @@ export class InvokeOrchestrator {
|
|
|
264
289
|
taskId: task.id,
|
|
265
290
|
sessionId,
|
|
266
291
|
output: 'Task execution timed out',
|
|
267
|
-
tokenUsage
|
|
292
|
+
tokenUsage,
|
|
268
293
|
duration: Date.now() - startTime,
|
|
269
294
|
status: 'timeout',
|
|
270
295
|
});
|
|
@@ -276,7 +301,7 @@ export class InvokeOrchestrator {
|
|
|
276
301
|
taskId: task.id,
|
|
277
302
|
sessionId,
|
|
278
303
|
output: errorMessage,
|
|
279
|
-
tokenUsage
|
|
304
|
+
tokenUsage,
|
|
280
305
|
duration: Date.now() - startTime,
|
|
281
306
|
status: 'failed',
|
|
282
307
|
});
|
|
@@ -310,33 +335,27 @@ export class InvokeOrchestrator {
|
|
|
310
335
|
};
|
|
311
336
|
// 将 Graph 层面的 token 消耗记录到 Task(按节点明细逐个记录,便于审计)
|
|
312
337
|
if (graphUsage.perNode && graphUsage.perNode.length > 0) {
|
|
313
|
-
|
|
314
|
-
await this.taskService.recordTokenUsage(task.id, {
|
|
315
|
-
nodeId: u.nodeId,
|
|
316
|
-
model: u.model,
|
|
317
|
-
inputTokens: u.inputTokens,
|
|
318
|
-
outputTokens: u.outputTokens,
|
|
319
|
-
costEstimate: 0,
|
|
320
|
-
});
|
|
321
|
-
}
|
|
338
|
+
await recordTaskTokenUsages(this.taskService, task.id, graphUsage.perNode);
|
|
322
339
|
}
|
|
323
340
|
else if (graphUsage.total > 0) {
|
|
324
|
-
await this.taskService
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
341
|
+
await recordTaskTokenUsages(this.taskService, task.id, [
|
|
342
|
+
{
|
|
343
|
+
nodeId: 'graph',
|
|
344
|
+
model: 'unknown',
|
|
345
|
+
inputTokens: graphUsage.input,
|
|
346
|
+
outputTokens: graphUsage.output,
|
|
347
|
+
},
|
|
348
|
+
]);
|
|
331
349
|
}
|
|
332
350
|
// ─── Step 7: 完成 Task (running → success) ───
|
|
333
351
|
await this.taskService.updateStatus(task.id, 'success', graphResult.output);
|
|
334
352
|
// ─── Step 7b: 持久化本轮 transcript(统一顺序:assistant tool_calls → tool → assistant 文本)───
|
|
335
|
-
|
|
353
|
+
// 按 Session 已有行数切 Graph 增量。多段正文收成一条后 Session 比 Graph 短,切点可能提前;finishPoints 只含一个实际节点 id 时靠最后一条纯文本仍落到 merge。
|
|
354
|
+
const initialMessagesCount = sessionContext.messages.length + 1;
|
|
336
355
|
const allMessages = Array.isArray(graphResult.finalState?.messages)
|
|
337
356
|
? graphResult.finalState.messages
|
|
338
357
|
: [];
|
|
339
|
-
const newMessages = allMessages.slice(initialMessagesCount);
|
|
358
|
+
const newMessages = sessionVisibleGraphMessages(allMessages.slice(initialMessagesCount), finishPoints);
|
|
340
359
|
await persistTurnTranscript(this.messageService, {
|
|
341
360
|
sessionId,
|
|
342
361
|
taskId: task.id,
|
|
@@ -375,6 +394,20 @@ export class InvokeOrchestrator {
|
|
|
375
394
|
}
|
|
376
395
|
return false;
|
|
377
396
|
}
|
|
397
|
+
async persistInvokeUsages(taskId, sessionId) {
|
|
398
|
+
if (!this.checkpointSaver)
|
|
399
|
+
return [];
|
|
400
|
+
const threadId = resolveCheckpointThreadId({ sessionId, taskId });
|
|
401
|
+
const ck = await this.checkpointSaver.load(threadId);
|
|
402
|
+
const usages = tokenUsagesFromLlmChannel(ck?.state);
|
|
403
|
+
await recordTaskTokenUsages(this.taskService, taskId, usages);
|
|
404
|
+
return usages;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function tokenUsageTotals(usages) {
|
|
408
|
+
const input = usages.reduce((sum, row) => sum + row.inputTokens, 0);
|
|
409
|
+
const output = usages.reduce((sum, row) => sum + row.outputTokens, 0);
|
|
410
|
+
return { input, output, total: input + output };
|
|
378
411
|
}
|
|
379
412
|
// ─────────────────────────────────────────────
|
|
380
413
|
// Module-level Helpers
|
|
@@ -27,6 +27,8 @@ import type { IStateGraphEngine } from '../../graph/contracts/state-graph.contra
|
|
|
27
27
|
import type { TokenStatsService } from '../../token/token-stats.service.js';
|
|
28
28
|
import type { TokenBudgetGuard } from '../../token/token-budget-guard.js';
|
|
29
29
|
import type { SessionBudgetChecker } from '../../token/session-budget-checker.js';
|
|
30
|
+
import { type SessionCompressionOptions } from '../../session/apply-session-compression.js';
|
|
31
|
+
import { type SessionMemoryOptions } from '../../session/apply-session-memory.js';
|
|
30
32
|
import type { StreamRequest } from '../routes/agent.routes.js';
|
|
31
33
|
import { type GraphLoader } from '../../registry/graph-loader.js';
|
|
32
34
|
import type { ICheckpointSaver } from '../../checkpoint/contracts/checkpoint-saver.contract.js';
|
|
@@ -48,6 +50,8 @@ export interface StreamOrchestratorDeps {
|
|
|
48
50
|
checkpointSaver?: ICheckpointSaver;
|
|
49
51
|
/** Hosting 注入的 Trace 采集器;与 traceId 同时存在时创建 Skill/Tool span */
|
|
50
52
|
traceCollector?: ITraceCollector;
|
|
53
|
+
sessionCompression?: SessionCompressionOptions;
|
|
54
|
+
sessionMemory?: SessionMemoryOptions;
|
|
51
55
|
}
|
|
52
56
|
/**
|
|
53
57
|
* StreamOrchestrator — 流式调用全流程编排
|
|
@@ -66,6 +70,8 @@ export declare class StreamOrchestrator {
|
|
|
66
70
|
private readonly graphLoader?;
|
|
67
71
|
private readonly checkpointSaver?;
|
|
68
72
|
private readonly traceCollector?;
|
|
73
|
+
private readonly sessionCompression?;
|
|
74
|
+
private readonly sessionMemory?;
|
|
69
75
|
constructor(deps: StreamOrchestratorDeps);
|
|
70
76
|
/**
|
|
71
77
|
* 执行流式调用编排
|
|
@@ -19,9 +19,13 @@
|
|
|
19
19
|
* @see design.md §1.5 /stream 内部执行逻辑
|
|
20
20
|
* @see requirements.md Requirement 11 AC2
|
|
21
21
|
*/
|
|
22
|
-
import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, buildTurnTranscriptFromStreamEvents, extractStreamReasoning,
|
|
22
|
+
import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, buildTurnTranscriptFromStreamEvents, 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
|
+
import { recordTaskTokenUsages } from '../../token/record-task-token-usage.js';
|
|
26
|
+
import { iterSessionCompression, } from '../../session/apply-session-compression.js';
|
|
27
|
+
import { applySessionMemory, } from '../../session/apply-session-memory.js';
|
|
28
|
+
import { resolveTokenDetail, shouldEmitTokenSse } from '../../token/token-detail.js';
|
|
25
29
|
import { SSEEmitter } from '../../sse/sse-emitter.js';
|
|
26
30
|
import { resolveExecutionContext } from '../../governance/governance-client.js';
|
|
27
31
|
import { resolveSkillInputFromInvokeInput } from '../../registry/skill-io-guard.js';
|
|
@@ -71,6 +75,8 @@ export class StreamOrchestrator {
|
|
|
71
75
|
graphLoader;
|
|
72
76
|
checkpointSaver;
|
|
73
77
|
traceCollector;
|
|
78
|
+
sessionCompression;
|
|
79
|
+
sessionMemory;
|
|
74
80
|
constructor(deps) {
|
|
75
81
|
this.sessionContextService = deps.sessionContextService;
|
|
76
82
|
this.taskService = deps.taskService;
|
|
@@ -82,6 +88,8 @@ export class StreamOrchestrator {
|
|
|
82
88
|
this.graphLoader = deps.graphLoader;
|
|
83
89
|
this.checkpointSaver = deps.checkpointSaver;
|
|
84
90
|
this.traceCollector = deps.traceCollector;
|
|
91
|
+
this.sessionCompression = deps.sessionCompression;
|
|
92
|
+
this.sessionMemory = deps.sessionMemory;
|
|
85
93
|
}
|
|
86
94
|
/**
|
|
87
95
|
* 执行流式调用编排
|
|
@@ -206,7 +214,28 @@ export class StreamOrchestrator {
|
|
|
206
214
|
: undefined;
|
|
207
215
|
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.graphLoader, agentCode, req.agentVersion);
|
|
208
216
|
// 组装 GraphState:有 Checkpoint 历史则只追加本轮 user,避免双重 append
|
|
209
|
-
|
|
217
|
+
let history = messageRecordsToGraphMessages(sessionContext.messages);
|
|
218
|
+
for await (const step of iterSessionCompression({
|
|
219
|
+
history,
|
|
220
|
+
sessionId,
|
|
221
|
+
userId,
|
|
222
|
+
taskId,
|
|
223
|
+
options: this.sessionCompression,
|
|
224
|
+
})) {
|
|
225
|
+
if (step.kind === 'history') {
|
|
226
|
+
history = step.messages;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
emitter.emit(this.buildSSEEvent(step.kind === 'started' ? 'session.compression.started' : 'session.compression.completed', correlationId, taskId, { ...step.payload }));
|
|
230
|
+
}
|
|
231
|
+
history = await applySessionMemory({
|
|
232
|
+
history,
|
|
233
|
+
userId,
|
|
234
|
+
agentCode,
|
|
235
|
+
query: typeof req.input === 'string' ? req.input : undefined,
|
|
236
|
+
options: this.sessionMemory,
|
|
237
|
+
});
|
|
238
|
+
const tokenDetail = resolveTokenDetail(req.config?.tokenDetail);
|
|
210
239
|
const messages = await buildTurnGraphMessages({
|
|
211
240
|
history,
|
|
212
241
|
input: req.input,
|
|
@@ -214,7 +243,7 @@ export class StreamOrchestrator {
|
|
|
214
243
|
threadId: sessionId,
|
|
215
244
|
});
|
|
216
245
|
const skillInput = resolveSkillInputFromInvokeInput(req.input);
|
|
217
|
-
|
|
246
|
+
const assistantBuckets = createStreamAssistantBuckets();
|
|
218
247
|
let toolCallReasoningContent;
|
|
219
248
|
let assistantReasoningContent;
|
|
220
249
|
const pendingToolCalls = [];
|
|
@@ -232,6 +261,8 @@ export class StreamOrchestrator {
|
|
|
232
261
|
sessionId,
|
|
233
262
|
executionContext,
|
|
234
263
|
forceToolServer: req.config?.forceToolServer,
|
|
264
|
+
ragScope: req.ragScope,
|
|
265
|
+
ragSources: req.ragSources,
|
|
235
266
|
loadedTools,
|
|
236
267
|
loadedSkills,
|
|
237
268
|
skillDefinitions,
|
|
@@ -271,38 +302,20 @@ export class StreamOrchestrator {
|
|
|
271
302
|
toolCallReasoningContent = p.reasoningContent;
|
|
272
303
|
}
|
|
273
304
|
}
|
|
274
|
-
|
|
275
|
-
const p = runtimeEvent.payload;
|
|
276
|
-
if (p.toolCallId && p.toolName) {
|
|
277
|
-
toolResults.push({
|
|
278
|
-
toolCallId: p.toolCallId,
|
|
279
|
-
toolName: p.toolName,
|
|
280
|
-
content: p.content,
|
|
281
|
-
success: p.success !== false,
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
}
|
|
305
|
+
appendTerminalToolResult(runtimeEvent.type, runtimeEvent.payload, toolResults);
|
|
285
306
|
const streamedReasoning = extractStreamReasoning(runtimeEvent.type, runtimeEvent.payload);
|
|
286
307
|
if (streamedReasoning)
|
|
287
308
|
assistantReasoningContent = streamedReasoning;
|
|
288
|
-
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
// final 若带完整文本则覆盖累加,避免与 chunk 重复拼接
|
|
292
|
-
if (delta.trim())
|
|
293
|
-
assistantText = delta;
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
assistantText += delta;
|
|
297
|
-
}
|
|
309
|
+
applyStreamTextDelta(assistantBuckets, runtimeEvent.type, runtimeEvent.payload);
|
|
310
|
+
if (shouldEmitTokenSse(tokenDetail, runtimeEvent.type)) {
|
|
311
|
+
emitter.emit(this.runtimeEventToSSE(runtimeEvent, correlationId, taskId));
|
|
298
312
|
}
|
|
299
|
-
// 将 RuntimeEvent 转为 SSEEvent 并推送
|
|
300
|
-
emitter.emit(this.runtimeEventToSSE(runtimeEvent, correlationId, taskId));
|
|
301
313
|
}
|
|
302
314
|
}
|
|
303
315
|
catch (error) {
|
|
304
316
|
const hitlPaused = unwrapHitlPaused(error);
|
|
305
317
|
if (hitlPaused) {
|
|
318
|
+
await recordTaskTokenUsages(this.taskService, taskId, this.tokenStatsService.list(taskId));
|
|
306
319
|
await this.taskService.updateStatus(taskId, 'waiting_hitl', buildHitlWaitingTaskOutput({
|
|
307
320
|
threadId: hitlPaused.threadId,
|
|
308
321
|
question: hitlPaused.question,
|
|
@@ -323,12 +336,14 @@ export class StreamOrchestrator {
|
|
|
323
336
|
}
|
|
324
337
|
// ─── 错误处理:TokenBudgetExhausted ───
|
|
325
338
|
if (error instanceof TokenBudgetExhaustedError) {
|
|
339
|
+
await recordTaskTokenUsages(this.taskService, taskId, this.tokenStatsService.list(taskId));
|
|
326
340
|
await this.taskService.updateStatus(taskId, 'token_exhausted', error.message);
|
|
327
341
|
this.emitErrorAndClose(emitter, correlationId, taskId, error);
|
|
328
342
|
return;
|
|
329
343
|
}
|
|
330
344
|
// ─── 错误处理:Timeout ───
|
|
331
345
|
if (this.isTimeoutError(error)) {
|
|
346
|
+
await recordTaskTokenUsages(this.taskService, taskId, this.tokenStatsService.list(taskId));
|
|
332
347
|
await this.taskService.updateStatus(taskId, 'timeout');
|
|
333
348
|
emitter.emit(this.buildSSEEvent('task.error', correlationId, taskId, {
|
|
334
349
|
code: ErrorCodes.TASK_TIMEOUT,
|
|
@@ -340,6 +355,7 @@ export class StreamOrchestrator {
|
|
|
340
355
|
}
|
|
341
356
|
// ─── 错误处理:通用错误 ───
|
|
342
357
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
358
|
+
await recordTaskTokenUsages(this.taskService, taskId, this.tokenStatsService.list(taskId));
|
|
343
359
|
await this.taskService.updateStatus(taskId, 'failed', errorMessage);
|
|
344
360
|
this.emitErrorAndClose(emitter, correlationId, taskId, error);
|
|
345
361
|
return;
|
|
@@ -348,21 +364,22 @@ export class StreamOrchestrator {
|
|
|
348
364
|
// Emit: response.final(带完整 assistant 文本,供客户端兜底)
|
|
349
365
|
const tokenStats = this.tokenStatsService.getTaskStats(taskId);
|
|
350
366
|
emitter.emit(this.buildSSEEvent('response.final', correlationId, taskId, {
|
|
351
|
-
content:
|
|
367
|
+
content: visibleStreamAssistantText(assistantBuckets),
|
|
352
368
|
format: 'text',
|
|
353
369
|
}));
|
|
354
|
-
// Emit: stats.token_usage
|
|
355
370
|
const totalTokens = (tokenStats.total.input ?? 0) + (tokenStats.total.output ?? 0);
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
371
|
+
if (shouldEmitTokenSse(tokenDetail, 'stats.token_usage')) {
|
|
372
|
+
emitter.emit(this.buildSSEEvent('stats.token_usage', correlationId, taskId, {
|
|
373
|
+
total: {
|
|
374
|
+
input: tokenStats.total.input,
|
|
375
|
+
output: tokenStats.total.output,
|
|
376
|
+
total: tokenStats.total.total ?? totalTokens,
|
|
377
|
+
},
|
|
378
|
+
byNode: tokenStats.byNode,
|
|
379
|
+
byModel: tokenStats.byModel,
|
|
380
|
+
costEstimate: tokenStats.costEstimate,
|
|
381
|
+
}));
|
|
382
|
+
}
|
|
366
383
|
// ─── Step 8: 记录 Token + 完成 Task + 关闭 SSE ───
|
|
367
384
|
const duration = Date.now() - startTime;
|
|
368
385
|
// Emit: task.completed
|
|
@@ -370,6 +387,7 @@ export class StreamOrchestrator {
|
|
|
370
387
|
taskId,
|
|
371
388
|
duration,
|
|
372
389
|
}));
|
|
390
|
+
await recordTaskTokenUsages(this.taskService, taskId, this.tokenStatsService.list(taskId));
|
|
373
391
|
// Task 状态流转 → success
|
|
374
392
|
await this.taskService.updateStatus(taskId, 'success');
|
|
375
393
|
// ─── Step 8b: 持久化 transcript(与 invoke 同序)───
|
|
@@ -380,7 +398,9 @@ export class StreamOrchestrator {
|
|
|
380
398
|
messages: buildTurnTranscriptFromStreamEvents({
|
|
381
399
|
pendingToolCalls,
|
|
382
400
|
toolResults,
|
|
383
|
-
|
|
401
|
+
assistantTexts: streamAssistantTexts(assistantBuckets),
|
|
402
|
+
assistantText: visibleStreamAssistantText(assistantBuckets),
|
|
403
|
+
finishPoints: graph.finishPoints,
|
|
384
404
|
toolCallReasoningContent,
|
|
385
405
|
assistantReasoningContent,
|
|
386
406
|
}),
|
|
@@ -11,10 +11,17 @@ import type { IStateGraphEngine } from '../../graph/contracts/executable-graph.j
|
|
|
11
11
|
import type { SSEEmitter } from '../../sse/sse-emitter.js';
|
|
12
12
|
export type { InvokeConfig, InvokeResponseData } from '@gt-fe/eap-contracts';
|
|
13
13
|
/**
|
|
14
|
-
* dev HTTP 层 Invoke 请求(扩展 userId,Hosting 从 auth
|
|
14
|
+
* dev HTTP 层 Invoke 请求(扩展 userId,Hosting 从 auth 注入;ragScope 为请求级
|
|
15
|
+
* 检索范围——前端圈定已向量化文件/目录,仅 rag 节点本地检索源生效)
|
|
15
16
|
*/
|
|
16
17
|
export type InvokeRequest = ContractInvokeRequest & {
|
|
17
18
|
userId?: string;
|
|
19
|
+
ragScope?: {
|
|
20
|
+
fileIds?: string[];
|
|
21
|
+
dirPaths?: string[];
|
|
22
|
+
};
|
|
23
|
+
/** 会话级数据源勾选(数据源 id 列表;缺省 = 智能体预定义目录全部启用) */
|
|
24
|
+
ragSources?: string[];
|
|
18
25
|
};
|
|
19
26
|
export type InvokeResponse = ContractInvokeResponse;
|
|
20
27
|
/** 流式调用请求 — 与 InvokeRequest 相同 */
|
package/dist/common/types.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Token 统计输出粒度控制
|
|
10
10
|
* - none: 不输出任何 Token 统计事件
|
|
11
11
|
* - summary: 仅 Task 结束时输出 stats.token_usage 汇总
|
|
12
|
-
* - detail: 每次 LLM 调用输出 stats.
|
|
12
|
+
* - detail: 每次 LLM 调用输出 stats.node_token_usage + 结束时汇总
|
|
13
13
|
*/
|
|
14
14
|
export type TokenDetailLevel = 'none' | 'summary' | 'detail';
|
|
15
15
|
/**
|
|
@@ -79,7 +79,7 @@ export type MessageRole = 'user' | 'assistant' | 'system' | 'tool';
|
|
|
79
79
|
*
|
|
80
80
|
* 与 @gt-fe/eap-contracts.TokenUsage 的区别:
|
|
81
81
|
* - contracts 版本是 Task 级聚合结果(含 byNode/byModel)
|
|
82
|
-
* - 本类型是单次调用粒度的原始记录,用于 Runtime 内部累计和 stats.
|
|
82
|
+
* - 本类型是单次调用粒度的原始记录,用于 Runtime 内部累计和 stats.node_token_usage 事件
|
|
83
83
|
*/
|
|
84
84
|
export interface TokenUsage {
|
|
85
85
|
/** 输入 Token 数 */
|
package/dist/common/types.js
CHANGED
|
@@ -7,5 +7,5 @@
|
|
|
7
7
|
* - LLM 调用:由模型路由模块实现
|
|
8
8
|
* - 存储接口:由各存储后端实现
|
|
9
9
|
*/
|
|
10
|
-
export type { ITraceCollector, TraceEvent, TraceSession, } from './trace-collector.js';
|
|
10
|
+
export type { ITraceCollector, CompleteNodeParams, TraceEvent, TraceSession, } from './trace-collector.js';
|
|
11
11
|
export type { IPermissionChecker } from './permission-checker.js';
|
|
@@ -85,6 +85,8 @@ export interface ExecutionNodeEvent {
|
|
|
85
85
|
type: 'start' | 'llm' | 'tool' | 'skill' | 'condition' | 'hitl' | 'end';
|
|
86
86
|
/** 工具运行环境(type=tool) */
|
|
87
87
|
toolRuntime?: 'local' | 'server' | 'both';
|
|
88
|
+
/** skill/tool 包版本;与 TraceClient RecordSpanParams.version 同义 */
|
|
89
|
+
version?: string;
|
|
88
90
|
/** Agent 对外主键 */
|
|
89
91
|
agentCode?: string;
|
|
90
92
|
/** 输入数据 */
|
|
@@ -92,6 +94,19 @@ export interface ExecutionNodeEvent {
|
|
|
92
94
|
/** 扩展元数据(如 skillId) */
|
|
93
95
|
metadata?: Record<string, unknown>;
|
|
94
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* 完成执行节点参数。llm Span 的 model / token 与 stats.node_token_usage 同源。
|
|
99
|
+
*/
|
|
100
|
+
export interface CompleteNodeParams {
|
|
101
|
+
status: 'completed' | 'failed';
|
|
102
|
+
output?: unknown;
|
|
103
|
+
error?: string;
|
|
104
|
+
inputTokens?: number;
|
|
105
|
+
outputTokens?: number;
|
|
106
|
+
totalTokens?: number;
|
|
107
|
+
/** llm Span:实际模型标识;与 NodeTokenUsage.model 相同 */
|
|
108
|
+
model?: string;
|
|
109
|
+
}
|
|
95
110
|
/**
|
|
96
111
|
* 链路事件
|
|
97
112
|
*
|
|
@@ -176,14 +191,7 @@ export interface ITraceCollector {
|
|
|
176
191
|
* @param nodeId - 节点 ID
|
|
177
192
|
* @param params - 完成参数
|
|
178
193
|
*/
|
|
179
|
-
completeNode(nodeId: string, params:
|
|
180
|
-
status: 'completed' | 'failed';
|
|
181
|
-
output?: unknown;
|
|
182
|
-
error?: string;
|
|
183
|
-
inputTokens?: number;
|
|
184
|
-
outputTokens?: number;
|
|
185
|
-
totalTokens?: number;
|
|
186
|
-
}): Promise<void>;
|
|
194
|
+
completeNode(nodeId: string, params: CompleteNodeParams): Promise<void>;
|
|
187
195
|
/**
|
|
188
196
|
* Checkpoint 已落盘后的引用回调。Hosting 适配 TraceClient.recordCheckpointRef。
|
|
189
197
|
* Runtime 仅在同时有 traceId 与 spanId 时调用;不传 graph state 正文。
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { decideBeforeModel, decideBeforeTool, type AfterModelInterceptor, type AfterToolInterceptor, type BeforeModelInput, type BeforeModelInterceptor, type BeforeToolInterceptor, type ExecutionInterceptors, type ModelInterceptorDecision, type ToolInterceptorDecision, } from './interceptors.js';
|
|
2
|
+
export { sandboxFromJail, type ISandbox, type IApproval } from './ports.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行拦截:Tool / LLM 调用前可拒绝,默认无注入则与未装配时相同。
|
|
3
|
+
* 不是规格 R13 Hook,不做插件发现、不读项目 hook 目录。
|
|
4
|
+
*/
|
|
5
|
+
import type { ExecutionContext } from '@gt-fe/eap-contracts';
|
|
6
|
+
import type { GraphMessage } from '../graph/contracts/executable-graph.js';
|
|
7
|
+
import type { LLMNodeConfig, LLMResponse } from '../graph/nodes/llm-node.js';
|
|
8
|
+
import type { ToolCallRequest, ToolCallResult } from '../tool/tool-executor.interface.js';
|
|
9
|
+
export type ToolInterceptorDecision = {
|
|
10
|
+
action: 'allow';
|
|
11
|
+
} | {
|
|
12
|
+
action: 'deny';
|
|
13
|
+
reason: string;
|
|
14
|
+
} | {
|
|
15
|
+
action: 'ask';
|
|
16
|
+
};
|
|
17
|
+
export type BeforeToolInterceptor = (request: ToolCallRequest, ctx?: ExecutionContext) => Promise<ToolInterceptorDecision | void> | ToolInterceptorDecision | void;
|
|
18
|
+
export type AfterToolInterceptor = (request: ToolCallRequest, result: ToolCallResult) => Promise<void> | void;
|
|
19
|
+
export interface BeforeModelInput {
|
|
20
|
+
messages: GraphMessage[];
|
|
21
|
+
config: LLMNodeConfig;
|
|
22
|
+
nodeId: string;
|
|
23
|
+
}
|
|
24
|
+
export type ModelInterceptorDecision = {
|
|
25
|
+
action: 'allow';
|
|
26
|
+
messages?: GraphMessage[];
|
|
27
|
+
} | {
|
|
28
|
+
action: 'deny';
|
|
29
|
+
reason: string;
|
|
30
|
+
};
|
|
31
|
+
export type BeforeModelInterceptor = (input: BeforeModelInput) => Promise<ModelInterceptorDecision | void> | ModelInterceptorDecision | void;
|
|
32
|
+
export type AfterModelInterceptor = (input: BeforeModelInput, response: LLMResponse) => Promise<void> | void;
|
|
33
|
+
export interface ExecutionInterceptors {
|
|
34
|
+
beforeTool?: BeforeToolInterceptor;
|
|
35
|
+
afterTool?: AfterToolInterceptor;
|
|
36
|
+
beforeModel?: BeforeModelInterceptor;
|
|
37
|
+
afterModel?: AfterModelInterceptor;
|
|
38
|
+
}
|
|
39
|
+
export declare function decideBeforeTool(interceptor: BeforeToolInterceptor | undefined, request: ToolCallRequest, ctx?: ExecutionContext): Promise<ToolInterceptorDecision>;
|
|
40
|
+
export declare function decideBeforeModel(interceptor: BeforeModelInterceptor | undefined, input: BeforeModelInput): Promise<ModelInterceptorDecision>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行拦截:Tool / LLM 调用前可拒绝,默认无注入则与未装配时相同。
|
|
3
|
+
* 不是规格 R13 Hook,不做插件发现、不读项目 hook 目录。
|
|
4
|
+
*/
|
|
5
|
+
export async function decideBeforeTool(interceptor, request, ctx) {
|
|
6
|
+
if (!interceptor)
|
|
7
|
+
return { action: 'allow' };
|
|
8
|
+
const raw = await interceptor(request, ctx);
|
|
9
|
+
if (!raw || raw.action === 'allow')
|
|
10
|
+
return { action: 'allow' };
|
|
11
|
+
if (raw.action === 'ask')
|
|
12
|
+
return { action: 'ask' };
|
|
13
|
+
return { action: 'deny', reason: raw.reason || '拦截拒绝' };
|
|
14
|
+
}
|
|
15
|
+
export async function decideBeforeModel(interceptor, input) {
|
|
16
|
+
if (!interceptor)
|
|
17
|
+
return { action: 'allow' };
|
|
18
|
+
const raw = await interceptor(input);
|
|
19
|
+
if (!raw || raw.action === 'allow') {
|
|
20
|
+
return { action: 'allow', messages: raw && raw.action === 'allow' ? raw.messages : undefined };
|
|
21
|
+
}
|
|
22
|
+
return { action: 'deny', reason: raw.reason || '拦截拒绝' };
|
|
23
|
+
}
|