@gt-fe/eap-runtime 0.0.12 → 0.0.14
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 -1
- package/dist/api/handlers/invoke.handler.d.ts +1 -1
- package/dist/api/handlers/invoke.handler.js +3 -0
- package/dist/api/handlers/stream.handler.d.ts +1 -1
- package/dist/api/handlers/stream.handler.js +4 -1
- package/dist/api/routes/agent.routes.d.ts +2 -2
- package/dist/common/index.d.ts +1 -0
- package/dist/common/index.js +1 -0
- package/dist/common/logger.d.ts +16 -4
- package/dist/common/logger.js +82 -16
- package/dist/common/runtime-package-version.d.ts +2 -0
- package/dist/common/runtime-package-version.js +5 -0
- package/dist/common/types.d.ts +5 -5
- package/dist/common/types.js +2 -2
- package/dist/contracts/permission-checker.d.ts +1 -1
- package/dist/debug/llm-turn-log.d.ts +12 -0
- package/dist/debug/llm-turn-log.js +1 -0
- package/dist/governance/governance-client.d.ts +1 -1
- package/dist/governance/index.d.ts +1 -1
- package/dist/governance/permission-guard.d.ts +1 -1
- package/dist/graph/adapters/langgraph.adapter.d.ts +3 -1
- package/dist/graph/adapters/langgraph.adapter.js +67 -14
- package/dist/graph/adapters/subgraph-compiler.js +5 -2
- package/dist/graph/contracts/executable-graph.d.ts +19 -8
- package/dist/graph/contracts/executable-graph.js +1 -1
- package/dist/graph/nodes/llm-node.d.ts +3 -0
- package/dist/graph/nodes/llm-node.js +8 -0
- package/dist/graph/nodes/skill-bind-node.d.ts +3 -4
- package/dist/graph/nodes/skill-bind-node.js +1 -20
- package/dist/graph/nodes/tool-node.d.ts +2 -0
- package/dist/graph/nodes/tool-node.js +20 -14
- package/dist/index.d.ts +1 -1
- package/dist/memory/compression/index.d.ts +1 -1
- package/dist/memory/compression/index.js +1 -1
- package/dist/memory/compression/sliding-window.compression.js +7 -7
- package/dist/memory/index.d.ts +1 -1
- package/dist/memory/index.js +1 -1
- package/dist/memory/message-compression.service.d.ts +12 -1
- package/dist/memory/message-compression.service.js +29 -1
- package/dist/providers/http-llm-provider.d.ts +1 -0
- package/dist/providers/http-llm-provider.js +4 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/providers/local-llm-provider.d.ts +4 -1
- package/dist/providers/local-llm-provider.js +15 -3
- package/dist/providers/routing-llm-provider.js +13 -4
- package/dist/registry/agent-models.d.ts +1 -1
- package/dist/registry/agent-models.js +1 -1
- package/dist/registry/agent-registry-client.d.ts +1 -1
- package/dist/registry/deployment-snapshot.js +19 -17
- package/dist/registry/graph-loader.d.ts +11 -1
- package/dist/registry/graph-loader.js +115 -56
- package/dist/registry/preload-errors.d.ts +1 -1
- package/dist/registry/preload-errors.js +1 -1
- package/dist/registry/skill-io-guard.js +1 -1
- package/dist/registry/skill-tree.js +1 -1
- package/dist/runtime/agent-runtime.d.ts +3 -3
- package/dist/runtime/agent-runtime.js +14 -4
- package/dist/runtime/dev-runtime.d.ts +2 -0
- package/dist/runtime/dev-runtime.js +2 -0
- package/dist/runtime/types.d.ts +4 -4
- package/dist/server.js +1 -1
- package/dist/session/compression-middleware.d.ts +2 -1
- package/dist/session/compression-middleware.js +7 -4
- package/dist/sse/events/skill-events.d.ts +1 -1
- package/dist/sse/events/skill-events.js +1 -1
- package/dist/sse/events/stats-events.d.ts +9 -0
- package/dist/token/compression-budget-integration.d.ts +1 -1
- package/dist/token/compression-budget-integration.js +18 -10
- package/dist/token/context-window.d.ts +42 -0
- package/dist/token/context-window.js +73 -0
- package/dist/token/index.d.ts +1 -0
- package/dist/token/index.js +1 -0
- package/dist/tool/create-tool-executor.d.ts +1 -1
- package/dist/tool/dev-http-executor.d.ts +2 -1
- package/dist/tool/dev-http-executor.js +14 -2
- package/dist/tool/dev-mcp-executor.d.ts +11 -1
- package/dist/tool/dev-mcp-executor.js +27 -1
- package/dist/tool/dev-tool-executor.d.ts +2 -1
- package/dist/tool/dev-tool-executor.js +6 -0
- package/dist/tool/harness-command.js +1 -1
- package/dist/tool/harness-primitives.d.ts +7 -0
- package/dist/tool/harness-primitives.js +57 -6
- package/dist/tool/http-tool-executor.js +1 -1
- package/dist/tool/index.d.ts +2 -1
- package/dist/tool/index.js +1 -0
- package/dist/tool/local-mcp-client.d.ts +5 -2
- package/dist/tool/local-mcp-client.js +130 -3
- package/dist/tool/local-tool-executor.d.ts +3 -2
- package/dist/tool/local-tool-executor.js +16 -1
- package/dist/tool/mcp-list.d.ts +53 -0
- package/dist/tool/mcp-list.js +199 -0
- package/dist/tool/mcp-tool-executor.d.ts +6 -1
- package/dist/tool/mcp-tool-executor.js +53 -1
- package/dist/tool/skill-dir-infer.d.ts +17 -0
- package/dist/tool/skill-dir-infer.js +81 -0
- package/dist/tool/skill-script-refs.d.ts +1 -8
- package/dist/tool/skill-script-refs.js +32 -20
- package/dist/tool/system-prompt-builder.d.ts +35 -16
- package/dist/tool/system-prompt-builder.js +30 -58
- package/dist/tool/timeout.d.ts +2 -2
- package/dist/tool/timeout.js +6 -3
- package/dist/tool/tool-execution-router.d.ts +3 -2
- package/dist/tool/tool-execution-router.js +8 -1
- package/dist/tool/tool-executor.interface.d.ts +11 -0
- package/dist/tool/tool-orchestrator.d.ts +1 -1
- package/dist/tool/tool-orchestrator.js +1 -1
- package/dist/utils/format-error.d.ts +2 -2
- package/dist/utils/format-error.js +2 -2
- package/dist/utils/http-error.d.ts +2 -2
- package/dist/utils/http-error.js +2 -2
- package/package.json +6 -33
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 预装加载错误 — 对齐 runtime.preload.* / 联调契约
|
|
3
3
|
*/
|
|
4
|
-
import { AppError, type ErrorCode } from '@
|
|
4
|
+
import { AppError, type ErrorCode } from '@eap/contracts';
|
|
5
5
|
export declare function preloadError(code: ErrorCode, message: string, details?: unknown): AppError;
|
|
6
6
|
export declare function preloadMissing(message: string, details?: unknown): AppError;
|
|
7
7
|
export declare function preloadManifestNotFound(message: string, details?: unknown): AppError;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 预装加载错误 — 对齐 runtime.preload.* / 联调契约
|
|
3
3
|
*/
|
|
4
|
-
import { AppError, ErrorCodes } from '@
|
|
4
|
+
import { AppError, ErrorCodes } from '@eap/contracts';
|
|
5
5
|
export function preloadError(code, message, details) {
|
|
6
6
|
return new AppError(code, message, {
|
|
7
7
|
exposeMessage: true,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Skill inputDef / outputDef 轻量校验(默认开启;EAP_PRELOAD_VALIDATE_SKILL_IO=0 关闭)
|
|
3
3
|
*/
|
|
4
|
-
import { AppError, ErrorCodes } from '@
|
|
4
|
+
import { AppError, ErrorCodes } from '@eap/contracts';
|
|
5
5
|
import { isValidateSkillIo } from './preload-flags.js';
|
|
6
6
|
function asRecord(value) {
|
|
7
7
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
@@ -10,7 +10,7 @@ import { readdir, stat, access, readFile } from 'node:fs/promises';
|
|
|
10
10
|
import { watch } from 'node:fs';
|
|
11
11
|
import { constants as fsConstants } from 'node:fs';
|
|
12
12
|
import path from 'node:path';
|
|
13
|
-
import { SKILL_ENTRY_CANDIDATES } from '@
|
|
13
|
+
import { SKILL_ENTRY_CANDIDATES } from '@eap/contracts';
|
|
14
14
|
const SKILL_CODE = /^[a-z0-9][a-z0-9-]*$/;
|
|
15
15
|
async function exists(p) {
|
|
16
16
|
try {
|
|
@@ -11,7 +11,7 @@ import type { ICheckpointSaver } from '../checkpoint/contracts/checkpoint-saver.
|
|
|
11
11
|
import type { ITraceCollector } from '../contracts/trace-collector.js';
|
|
12
12
|
import type { IStateGraphEngine } from '../graph/contracts/state-graph.contract.js';
|
|
13
13
|
import { HitlHandler } from '../graph/hitl/hitl-handler.js';
|
|
14
|
-
import type
|
|
14
|
+
import { type GraphLoader } from '../registry/graph-loader.js';
|
|
15
15
|
import type { SessionContextService } from '../session/session-context.service.js';
|
|
16
16
|
import type { MessageService } from '../session/message.service.js';
|
|
17
17
|
import type { SessionService, SessionRecord, MessageRecord } from '../session/session.service.js';
|
|
@@ -86,8 +86,8 @@ export declare class AgentRuntime {
|
|
|
86
86
|
* “Session 历史 + 当前输入”途径。
|
|
87
87
|
*
|
|
88
88
|
* 行为:
|
|
89
|
-
* - `role` 仅接受 `user` / `assistant`;`system`
|
|
90
|
-
* `
|
|
89
|
+
* - `role` 仅接受 `user` / `assistant`;`system` 由 SystemPromptBuilder 拼
|
|
90
|
+
* (平台规则 + `prompts.system` 基线 + 节点 `systemPrompt`);
|
|
91
91
|
* `tool` 必须与前置 assistant.toolCalls 严格配对,不得单独作为种子。
|
|
92
92
|
* - 幂等语义(exactly-once):当 session 已存在任何历史消息时,直接返回
|
|
93
93
|
* `seeded: false`,不重复写入。
|
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { randomUUID } from 'node:crypto';
|
|
10
10
|
import { HITL_TIMEOUT_ERROR_CODE, HITL_TIMEOUT_MESSAGE, HitlAlreadyTimedOutError, HitlNotWaitingError, buildHitlWaitedPayload, isHitlDeadlinePassed, readPersistedHitlWaiting, unwrapHitlPaused, } from '../graph/hitl/hitl-handler.js';
|
|
11
|
+
import { resolveAgentSystemPromptFromLoader } from '../registry/graph-loader.js';
|
|
11
12
|
import { toLoadedSkillMap } from '../tool/system-prompt-builder.js';
|
|
12
13
|
import { assembleLoadedTools } from '../tool/harness-primitives.js';
|
|
13
14
|
import { createFileLlmTurnLogFromEnv, } from '../debug/llm-turn-log.js';
|
|
14
15
|
import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, buildTurnTranscriptFromStreamEvents, extractHitlThreadId, extractStreamReasoning, extractStreamTextDelta, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, } from '../session/persist-turn-messages.js';
|
|
15
|
-
import { AppError, ErrorCodes } from '@
|
|
16
|
+
import { AppError, ErrorCodes } from '@eap/contracts';
|
|
16
17
|
import { resolveExecutionContext } from '../governance/governance-client.js';
|
|
17
18
|
import { resolveAgentCode } from '../common/agent-code.js';
|
|
18
19
|
import { resolveTraceId } from '../common/trace-id.js';
|
|
@@ -71,8 +72,8 @@ export class AgentRuntime {
|
|
|
71
72
|
* “Session 历史 + 当前输入”途径。
|
|
72
73
|
*
|
|
73
74
|
* 行为:
|
|
74
|
-
* - `role` 仅接受 `user` / `assistant`;`system`
|
|
75
|
-
* `
|
|
75
|
+
* - `role` 仅接受 `user` / `assistant`;`system` 由 SystemPromptBuilder 拼
|
|
76
|
+
* (平台规则 + `prompts.system` 基线 + 节点 `systemPrompt`);
|
|
76
77
|
* `tool` 必须与前置 assistant.toolCalls 严格配对,不得单独作为种子。
|
|
77
78
|
* - 幂等语义(exactly-once):当 session 已存在任何历史消息时,直接返回
|
|
78
79
|
* `seeded: false`,不重复写入。
|
|
@@ -100,7 +101,7 @@ export class AgentRuntime {
|
|
|
100
101
|
throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}] must be an object`, { exposeMessage: true });
|
|
101
102
|
}
|
|
102
103
|
if (message.role !== 'user' && message.role !== 'assistant') {
|
|
103
|
-
throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}].role "${String(message.role)}" is not allowed; only 'user' and 'assistant' are accepted (system belongs to
|
|
104
|
+
throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}].role "${String(message.role)}" is not allowed; only 'user' and 'assistant' are accepted (system belongs to SystemPromptBuilder: platform rules + prompts.system + node systemPrompt; tool must pair with prior assistant.toolCalls)`, { exposeMessage: true });
|
|
104
105
|
}
|
|
105
106
|
if (typeof message.content !== 'string' || message.content.length === 0) {
|
|
106
107
|
throw new AppError(ErrorCodes.PARAMS_INVALID, `seedSessionMessages messages[${index}].content must be a non-empty string`, { exposeMessage: true });
|
|
@@ -271,6 +272,7 @@ export class AgentRuntime {
|
|
|
271
272
|
const agentDir = this.deps.graphLoader
|
|
272
273
|
? (await this.deps.graphLoader.resolveAgentDir(agentCode, params.agentVersion)) ?? undefined
|
|
273
274
|
: undefined;
|
|
275
|
+
const agentSystemPrompt = await resolveAgentSystemPromptFromLoader(this.deps.graphLoader, agentCode, params.agentVersion);
|
|
274
276
|
const history = messageRecordsToGraphMessages(sessionContext.messages);
|
|
275
277
|
const messages = await buildTurnGraphMessages({
|
|
276
278
|
history,
|
|
@@ -301,6 +303,7 @@ export class AgentRuntime {
|
|
|
301
303
|
skillDefinitions,
|
|
302
304
|
promptRoots,
|
|
303
305
|
agentDir,
|
|
306
|
+
agentSystemPrompt,
|
|
304
307
|
skillInput: resolveSkillInputFromInvokeInput(params.input),
|
|
305
308
|
traceId: resolveTraceId(params),
|
|
306
309
|
traceCollector: this.deps.traceCollector,
|
|
@@ -490,6 +493,9 @@ export class AgentRuntime {
|
|
|
490
493
|
(this.deps.graphLoader
|
|
491
494
|
? (await this.deps.graphLoader.resolveAgentDir(agentCode, agentVersion)) ?? undefined
|
|
492
495
|
: undefined),
|
|
496
|
+
agentSystemPrompt: (await resolveAgentSystemPromptFromLoader(this.deps.graphLoader, agentCode, agentVersion)) ?? (typeof resumedState.agentSystemPrompt === 'string'
|
|
497
|
+
? resumedState.agentSystemPrompt
|
|
498
|
+
: undefined),
|
|
493
499
|
activatedSkill: resumedState.activatedSkill,
|
|
494
500
|
skillRoute: resumedState.skillRoute ??
|
|
495
501
|
(typeof resumedState.activatedSkill === 'string' && resumedState.activatedSkill
|
|
@@ -652,11 +658,15 @@ export class AgentRuntime {
|
|
|
652
658
|
(this.deps.graphLoader
|
|
653
659
|
? (await this.deps.graphLoader.resolveAgentDir(agentCode, agentVersion)) ?? undefined
|
|
654
660
|
: undefined);
|
|
661
|
+
const agentSystemPrompt = (await resolveAgentSystemPromptFromLoader(this.deps.graphLoader, agentCode, agentVersion)) ?? (typeof forkedCheckpoint.state.agentSystemPrompt === 'string'
|
|
662
|
+
? forkedCheckpoint.state.agentSystemPrompt
|
|
663
|
+
: undefined);
|
|
655
664
|
try {
|
|
656
665
|
const result = await this.deps.graphEngine.invoke(graph, {
|
|
657
666
|
...forkedCheckpoint.state,
|
|
658
667
|
agentVersion,
|
|
659
668
|
agentDir,
|
|
669
|
+
agentSystemPrompt,
|
|
660
670
|
userId,
|
|
661
671
|
delegationToken: overrides.executionContext?.delegationToken,
|
|
662
672
|
executionContext: overrides.executionContext,
|
|
@@ -49,6 +49,8 @@ export interface DevRuntime {
|
|
|
49
49
|
dispose: () => void;
|
|
50
50
|
/** 装配期诊断摘要(CLI 启动时打印,便于排查环境问题) */
|
|
51
51
|
diagnostics: {
|
|
52
|
+
/** 当前加载的 Runtime 包 version */
|
|
53
|
+
packageVersion: string;
|
|
52
54
|
deploymentContext: string;
|
|
53
55
|
toolExecutionMode: string;
|
|
54
56
|
llmProviderKind: 'routing' | 'openai-http' | 'echo';
|
|
@@ -35,6 +35,7 @@ import { InvokeOrchestrator, StreamOrchestrator, SessionService, MessageService,
|
|
|
35
35
|
import { createAgentRuntime } from './agent-runtime.js';
|
|
36
36
|
import { assembleRuntimeStorage } from './storage-profile.js';
|
|
37
37
|
import { resolveSkillHotReload, watchPreinstallSkillRoot, watchSkillRoots } from '../registry/skill-tree.js';
|
|
38
|
+
import { RUNTIME_PACKAGE_VERSION } from '../common/runtime-package-version.js';
|
|
38
39
|
/**
|
|
39
40
|
* 装配本地 dev 模式所需的全部 Runtime 依赖
|
|
40
41
|
*
|
|
@@ -185,6 +186,7 @@ export function createDevRuntime() {
|
|
|
185
186
|
graphLoader,
|
|
186
187
|
dispose: stopSkillWatch,
|
|
187
188
|
diagnostics: {
|
|
189
|
+
packageVersion: RUNTIME_PACKAGE_VERSION,
|
|
188
190
|
deploymentContext,
|
|
189
191
|
toolExecutionMode,
|
|
190
192
|
llmProviderKind,
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @see 04-runtime-core/design.md §1.5
|
|
8
8
|
*/
|
|
9
|
-
import type { ExecutionContext, InvokeConfig, ResumeHitlResult } from '@
|
|
9
|
+
import type { ExecutionContext, InvokeConfig, ResumeHitlResult } from '@eap/contracts';
|
|
10
10
|
import type { InvokeResponseData } from '../api/routes/agent.routes.js';
|
|
11
11
|
import type { GraphMessage } from '../graph/contracts/executable-graph.js';
|
|
12
12
|
import type { SSEEvent } from '../sse/contracts/sse-emitter.contract.js';
|
|
@@ -31,7 +31,7 @@ export interface ExecuteParams {
|
|
|
31
31
|
}
|
|
32
32
|
/** 同步执行结果 */
|
|
33
33
|
export type ExecuteResult = InvokeResponseData;
|
|
34
|
-
/** HITL 恢复结果(与 @
|
|
34
|
+
/** HITL 恢复结果(与 @eap/contracts ResumeHitlResult 对齐) */
|
|
35
35
|
export type { ResumeHitlResult };
|
|
36
36
|
/** HITL 用户响应 */
|
|
37
37
|
export interface HitlResponse {
|
|
@@ -91,8 +91,8 @@ export interface ForkResult {
|
|
|
91
91
|
* 开发者提供的对话上下文(开场白 / few-shot)。
|
|
92
92
|
*
|
|
93
93
|
* 约束(AgentRuntime.seedSessionMessages 强制校验):
|
|
94
|
-
* - `role` 白名单为 `user` / `assistant`,`system`
|
|
95
|
-
* `
|
|
94
|
+
* - `role` 白名单为 `user` / `assistant`,`system` 由 SystemPromptBuilder 拼
|
|
95
|
+
* (平台规则 + `prompts.system` + 节点 `systemPrompt`),`tool` 必须与
|
|
96
96
|
* 前置 assistant.toolCalls 严格配对,两者禁止作为种子;
|
|
97
97
|
* - 该 session 已有历史时,视作已种入,跳过写入(exactly-once 幂等)。
|
|
98
98
|
*/
|
package/dist/server.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* TODO: Phase 2 将本文件迁移至 dev-tools/cli(作为 `eap dev` 命令实现),
|
|
8
8
|
* runtime 包不再包含 HTTP 服务代码,仅作为纯库导出。
|
|
9
9
|
*/
|
|
10
|
-
import { ErrorCodes } from '@
|
|
10
|
+
import { ErrorCodes } from '@eap/contracts';
|
|
11
11
|
import { createServer } from 'node:http';
|
|
12
12
|
import { URL } from 'node:url';
|
|
13
13
|
import { resolveExecutionContext } from './governance/index.js';
|
|
@@ -65,11 +65,12 @@ export declare class CompressionMiddleware {
|
|
|
65
65
|
sessionId: string;
|
|
66
66
|
taskId?: string;
|
|
67
67
|
userId: string;
|
|
68
|
+
maxContextTokens?: number;
|
|
68
69
|
}): Promise<MessageRecord[]>;
|
|
69
70
|
/**
|
|
70
71
|
* 检查是否需要压缩(不执行)
|
|
71
72
|
*/
|
|
72
|
-
checkCompression(messages: MessageRecord[]): {
|
|
73
|
+
checkCompression(messages: MessageRecord[], maxContextTokens?: number): {
|
|
73
74
|
needsCompression: boolean;
|
|
74
75
|
currentTokens: number;
|
|
75
76
|
currentRounds: number;
|
|
@@ -65,13 +65,16 @@ export class CompressionMiddleware {
|
|
|
65
65
|
// 转换为 GraphMessage 格式进行检查
|
|
66
66
|
const graphMessages = this.toGraphMessages(messages);
|
|
67
67
|
// 检查是否需要压缩
|
|
68
|
-
const
|
|
68
|
+
const windowOverride = context.maxContextTokens
|
|
69
|
+
? { maxContextTokens: context.maxContextTokens }
|
|
70
|
+
: undefined;
|
|
71
|
+
const checkResult = this.compression.checkCompression(graphMessages, windowOverride);
|
|
69
72
|
if (!checkResult.needsCompression) {
|
|
70
73
|
return messages;
|
|
71
74
|
}
|
|
72
75
|
// 执行压缩
|
|
73
76
|
const startTime = Date.now();
|
|
74
|
-
const compressedMessages = await this.compression.compress(graphMessages);
|
|
77
|
+
const compressedMessages = await this.compression.compress(graphMessages, windowOverride);
|
|
75
78
|
const duration = Date.now() - startTime;
|
|
76
79
|
// 转换回 MessageRecord 格式
|
|
77
80
|
const compressedRecords = this.toMessageRecords(compressedMessages, messages);
|
|
@@ -106,9 +109,9 @@ export class CompressionMiddleware {
|
|
|
106
109
|
/**
|
|
107
110
|
* 检查是否需要压缩(不执行)
|
|
108
111
|
*/
|
|
109
|
-
checkCompression(messages) {
|
|
112
|
+
checkCompression(messages, maxContextTokens) {
|
|
110
113
|
const graphMessages = this.toGraphMessages(messages);
|
|
111
|
-
return this.compression.checkCompression(graphMessages);
|
|
114
|
+
return this.compression.checkCompression(graphMessages, maxContextTokens ? { maxContextTokens } : undefined);
|
|
112
115
|
}
|
|
113
116
|
/**
|
|
114
117
|
* 获取配置
|
|
@@ -41,4 +41,13 @@ export interface StatsNodeTokenUsagePayload {
|
|
|
41
41
|
costEstimate: number;
|
|
42
42
|
/** 该节点本次调用耗时(毫秒),可选 */
|
|
43
43
|
durationMs?: number;
|
|
44
|
+
/** 本轮 prompt 占用的上下文窗 used/window;缺 window 则省略 */
|
|
45
|
+
used?: number;
|
|
46
|
+
window?: number;
|
|
47
|
+
/** 按字符比例把 used 拆到 system/messages/tools;合计 = used */
|
|
48
|
+
parts?: {
|
|
49
|
+
system: number;
|
|
50
|
+
messages: number;
|
|
51
|
+
tools: number;
|
|
52
|
+
};
|
|
44
53
|
}
|
|
@@ -18,7 +18,7 @@ import type { IMessageRepository } from '../session/session.service.js';
|
|
|
18
18
|
export interface CompressionBudgetConfig {
|
|
19
19
|
/** 模型 context window 大小 */
|
|
20
20
|
modelContextWindow: number;
|
|
21
|
-
/**
|
|
21
|
+
/** 相对模型窗口的触发系数(默认 0.7,与滑动窗口同一公式) */
|
|
22
22
|
historyThreshold?: number;
|
|
23
23
|
/** 压缩后目标占比(默认 0.5) */
|
|
24
24
|
targetRatio?: number;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { createContextBudget } from './context-window-budget.js';
|
|
11
11
|
import { CompressionMiddleware, createCompressionMiddlewareWithLLM, } from '../session/compression-middleware.js';
|
|
12
|
-
import { estimateMessagesTokens } from '../memory/message-compression.service.js';
|
|
12
|
+
import { estimateMessagesTokens, resolveCompressionMaxTokens, } from '../memory/message-compression.service.js';
|
|
13
13
|
// ─────────────────────────────────────────────
|
|
14
14
|
// CompressionBudgetIntegration
|
|
15
15
|
// ─────────────────────────────────────────────
|
|
@@ -28,7 +28,7 @@ export class CompressionBudgetIntegration {
|
|
|
28
28
|
constructor(config, repository, messageRepository) {
|
|
29
29
|
this.config = {
|
|
30
30
|
modelContextWindow: config.modelContextWindow,
|
|
31
|
-
historyThreshold: config.historyThreshold ?? 0.
|
|
31
|
+
historyThreshold: config.historyThreshold ?? 0.7,
|
|
32
32
|
targetRatio: config.targetRatio ?? 0.5,
|
|
33
33
|
autoCompress: config.autoCompress ?? true,
|
|
34
34
|
llmConfig: config.llmConfig,
|
|
@@ -36,13 +36,17 @@ export class CompressionBudgetIntegration {
|
|
|
36
36
|
// 创建预算管理器
|
|
37
37
|
this.budget = createContextBudget(this.config.modelContextWindow);
|
|
38
38
|
// 创建压缩中间件
|
|
39
|
+
const compressionConfig = {
|
|
40
|
+
maxContextTokens: this.config.modelContextWindow,
|
|
41
|
+
};
|
|
39
42
|
if (this.config.llmConfig?.provider) {
|
|
40
43
|
this.compression = createCompressionMiddlewareWithLLM(this.config.llmConfig.provider, {
|
|
41
44
|
llmConfig: this.config.llmConfig,
|
|
45
|
+
compressionConfig,
|
|
42
46
|
}, repository, messageRepository);
|
|
43
47
|
}
|
|
44
48
|
else {
|
|
45
|
-
this.compression = new CompressionMiddleware({}, undefined, repository, messageRepository);
|
|
49
|
+
this.compression = new CompressionMiddleware({ compressionConfig }, undefined, repository, messageRepository);
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
52
|
/**
|
|
@@ -55,12 +59,13 @@ export class CompressionBudgetIntegration {
|
|
|
55
59
|
// 估算当前 Token 数
|
|
56
60
|
const graphMessages = this.toGraphMessages(messages);
|
|
57
61
|
const currentTokens = estimateMessagesTokens(graphMessages);
|
|
58
|
-
//
|
|
59
|
-
const maxTokens = this.
|
|
60
|
-
//
|
|
61
|
-
const usageRatio =
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
// 与滑动窗口同一阈值:window × 系数(默认 0.7);historyThreshold 作为系数覆盖
|
|
63
|
+
const maxTokens = resolveCompressionMaxTokens(this.config.modelContextWindow, this.config.historyThreshold);
|
|
64
|
+
// 计算使用率(相对窗口,不是相对阈值)
|
|
65
|
+
const usageRatio = this.config.modelContextWindow > 0
|
|
66
|
+
? currentTokens / this.config.modelContextWindow
|
|
67
|
+
: 0;
|
|
68
|
+
const needsCompression = currentTokens > maxTokens;
|
|
64
69
|
// 计算压缩目标
|
|
65
70
|
const targetTokens = needsCompression
|
|
66
71
|
? Math.floor(maxTokens * this.config.targetRatio)
|
|
@@ -96,7 +101,10 @@ export class CompressionBudgetIntegration {
|
|
|
96
101
|
}
|
|
97
102
|
// 执行压缩
|
|
98
103
|
const startTime = Date.now();
|
|
99
|
-
const compressedMessages = await this.compression.processMessages(messages,
|
|
104
|
+
const compressedMessages = await this.compression.processMessages(messages, {
|
|
105
|
+
...context,
|
|
106
|
+
maxContextTokens: this.config.modelContextWindow,
|
|
107
|
+
});
|
|
100
108
|
const duration = Date.now() - startTime;
|
|
101
109
|
// 计算压缩后的 Token 数
|
|
102
110
|
const compressedGraphMessages = this.toGraphMessages(compressedMessages);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本轮 prompt 占用的上下文窗:used = 本轮 inputTokens,window = ModelRecord.capabilities.maxContextTokens。
|
|
3
|
+
* 不是账单合计,也不是 max_tokens。
|
|
4
|
+
* parts 按本轮 system / messages / tools 字符比例把 used 拆开(无独立 tokenizer)。
|
|
5
|
+
*/
|
|
6
|
+
export interface ContextUsageParts {
|
|
7
|
+
system: number;
|
|
8
|
+
messages: number;
|
|
9
|
+
tools: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ContextWindowUsage {
|
|
12
|
+
used: number;
|
|
13
|
+
window?: number;
|
|
14
|
+
parts?: ContextUsageParts;
|
|
15
|
+
}
|
|
16
|
+
export declare function contextWindowUsage(used: number, window: number | undefined): ContextWindowUsage | undefined;
|
|
17
|
+
export declare function splitContextUsage(used: number, sections: {
|
|
18
|
+
systemChars: number;
|
|
19
|
+
messageChars: number;
|
|
20
|
+
toolChars: number;
|
|
21
|
+
}): ContextUsageParts | undefined;
|
|
22
|
+
export declare function turnSectionChars(config: {
|
|
23
|
+
systemPrompt?: string;
|
|
24
|
+
tools?: unknown[];
|
|
25
|
+
}, messages: Array<{
|
|
26
|
+
content?: string;
|
|
27
|
+
toolCalls?: unknown;
|
|
28
|
+
}>): {
|
|
29
|
+
systemChars: number;
|
|
30
|
+
messageChars: number;
|
|
31
|
+
toolChars: number;
|
|
32
|
+
};
|
|
33
|
+
export declare function buildContextUsage(used: number, window: number | undefined, sections?: {
|
|
34
|
+
systemChars: number;
|
|
35
|
+
messageChars: number;
|
|
36
|
+
toolChars: number;
|
|
37
|
+
}): ContextWindowUsage | undefined;
|
|
38
|
+
export declare function pickContextFields(raw: {
|
|
39
|
+
used?: unknown;
|
|
40
|
+
window?: unknown;
|
|
41
|
+
parts?: unknown;
|
|
42
|
+
}): Partial<ContextWindowUsage>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本轮 prompt 占用的上下文窗:used = 本轮 inputTokens,window = ModelRecord.capabilities.maxContextTokens。
|
|
3
|
+
* 不是账单合计,也不是 max_tokens。
|
|
4
|
+
* parts 按本轮 system / messages / tools 字符比例把 used 拆开(无独立 tokenizer)。
|
|
5
|
+
*/
|
|
6
|
+
export function contextWindowUsage(used, window) {
|
|
7
|
+
return buildContextUsage(used, window);
|
|
8
|
+
}
|
|
9
|
+
export function splitContextUsage(used, sections) {
|
|
10
|
+
if (!(used > 0))
|
|
11
|
+
return undefined;
|
|
12
|
+
const systemChars = Math.max(0, sections.systemChars);
|
|
13
|
+
const messageChars = Math.max(0, sections.messageChars);
|
|
14
|
+
const toolChars = Math.max(0, sections.toolChars);
|
|
15
|
+
const total = systemChars + messageChars + toolChars;
|
|
16
|
+
if (total <= 0)
|
|
17
|
+
return undefined;
|
|
18
|
+
const system = Math.floor((used * systemChars) / total);
|
|
19
|
+
const tools = Math.floor((used * toolChars) / total);
|
|
20
|
+
const messages = used - system - tools;
|
|
21
|
+
return { system, messages, tools };
|
|
22
|
+
}
|
|
23
|
+
export function turnSectionChars(config, messages) {
|
|
24
|
+
const systemChars = (config.systemPrompt ?? '').length;
|
|
25
|
+
let messageChars = 0;
|
|
26
|
+
for (const message of messages) {
|
|
27
|
+
if (message.content)
|
|
28
|
+
messageChars += message.content.length;
|
|
29
|
+
if (message.toolCalls)
|
|
30
|
+
messageChars += jsonChars(message.toolCalls);
|
|
31
|
+
}
|
|
32
|
+
const toolChars = config.tools?.length ? jsonChars(config.tools) : 0;
|
|
33
|
+
return { systemChars, messageChars, toolChars };
|
|
34
|
+
}
|
|
35
|
+
export function buildContextUsage(used, window, sections) {
|
|
36
|
+
const safeUsed = typeof used === 'number' && Number.isFinite(used) && used >= 0 ? Math.floor(used) : 0;
|
|
37
|
+
const win = typeof window === 'number' && Number.isFinite(window) && window > 0
|
|
38
|
+
? Math.floor(window)
|
|
39
|
+
: undefined;
|
|
40
|
+
const parts = sections ? splitContextUsage(safeUsed, sections) : undefined;
|
|
41
|
+
if (safeUsed === 0 && win == null && !parts)
|
|
42
|
+
return undefined;
|
|
43
|
+
return {
|
|
44
|
+
used: safeUsed,
|
|
45
|
+
...(win != null ? { window: win } : {}),
|
|
46
|
+
...(parts ? { parts } : {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function pickContextFields(raw) {
|
|
50
|
+
const out = {};
|
|
51
|
+
if (typeof raw.used === 'number' && Number.isFinite(raw.used))
|
|
52
|
+
out.used = raw.used;
|
|
53
|
+
if (typeof raw.window === 'number' && Number.isFinite(raw.window) && raw.window > 0) {
|
|
54
|
+
out.window = raw.window;
|
|
55
|
+
}
|
|
56
|
+
if (raw.parts && typeof raw.parts === 'object' && !Array.isArray(raw.parts)) {
|
|
57
|
+
const p = raw.parts;
|
|
58
|
+
if (typeof p.system === 'number'
|
|
59
|
+
&& typeof p.messages === 'number'
|
|
60
|
+
&& typeof p.tools === 'number') {
|
|
61
|
+
out.parts = { system: p.system, messages: p.messages, tools: p.tools };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
function jsonChars(value) {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.stringify(value).length;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
}
|
package/dist/token/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { SessionBudgetChecker, SessionBudgetExhaustedError, } from './session-bu
|
|
|
7
7
|
export type { NodeTokenUsage, TokenBreakdown, TaskTokenSummary, } from './token-stats.service.js';
|
|
8
8
|
export { TokenStatsService } from './token-stats.service.js';
|
|
9
9
|
export type { TokenDetailMode } from './token-detail-mode.js';
|
|
10
|
+
export { contextWindowUsage, buildContextUsage, splitContextUsage, turnSectionChars, pickContextFields, type ContextWindowUsage, type ContextUsageParts, } from './context-window.js';
|
|
10
11
|
export { TokenStatsEmitter } from './token-detail-mode.js';
|
|
11
12
|
export { ContextWindowBudget, createContextBudget, type ContextWindowBudgetConfig, type ContextSource, type SourceAllocation, type BudgetAllocation, DEFAULT_CONTEXT_BUDGET_CONFIG, } from './context-window-budget.js';
|
|
12
13
|
export { CharBasedTokenEstimator, ModelBasedTokenEstimator, createTokenEstimator, type ITokenEstimator, } from './token-estimator.js';
|
package/dist/token/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { PostgresTokenBudgetRepository } from './pg-token-budget.repository.js';
|
|
|
3
3
|
export { TokenBudgetGuard, TokenBudgetExhaustedError, } from './token-budget-guard.js';
|
|
4
4
|
export { SessionBudgetChecker, SessionBudgetExhaustedError, } from './session-budget-checker.js';
|
|
5
5
|
export { TokenStatsService } from './token-stats.service.js';
|
|
6
|
+
export { contextWindowUsage, buildContextUsage, splitContextUsage, turnSectionChars, pickContextFields, } from './context-window.js';
|
|
6
7
|
export { TokenStatsEmitter } from './token-detail-mode.js';
|
|
7
8
|
// 上下文窗口预算分配
|
|
8
9
|
export { ContextWindowBudget, createContextBudget, DEFAULT_CONTEXT_BUDGET_CONFIG, } from './context-window-budget.js';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `EAP_TOOL_GATEWAY_TRANSPORT=http` 时用 HttpToolExecutor(仅调试)。
|
|
6
6
|
* desktop 经 ToolExecutionRouter 分流 Local / Gateway。
|
|
7
7
|
*/
|
|
8
|
-
import type { ILocalToolInstallStore, LocalToolInstallRecord } from '@
|
|
8
|
+
import type { ILocalToolInstallStore, LocalToolInstallRecord } from '@eap/contracts';
|
|
9
9
|
import { type DeploymentContext } from './tool-execution-router.js';
|
|
10
10
|
import type { IToolExecutor } from './tool-executor.interface.js';
|
|
11
11
|
export declare class EmptyInstallStore implements ILocalToolInstallStore {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dev 下按 type:http 清单直连出站(gateway.baseUrl + pathTemplate)。
|
|
3
|
-
* URL 拼接与 host 白名单与 Tool Gateway 共用 @
|
|
3
|
+
* URL 拼接与 host 白名单与 Tool Gateway 共用 @eap/contracts http-outbound。
|
|
4
4
|
*/
|
|
5
5
|
import type { IToolExecutor, ToolCallRequest, ToolCallResult } from './tool-executor.interface.js';
|
|
6
6
|
export interface DevHttpExecution {
|
|
@@ -9,6 +9,7 @@ export interface DevHttpExecution {
|
|
|
9
9
|
baseUrl?: string;
|
|
10
10
|
pathTemplate?: string;
|
|
11
11
|
allowedHosts?: string[];
|
|
12
|
+
header?: Record<string, string>;
|
|
12
13
|
timeout?: number;
|
|
13
14
|
}
|
|
14
15
|
export declare function resolveHttpExecutionFromRequest(request: ToolCallRequest): DevHttpExecution | null;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dev 下按 type:http 清单直连出站(gateway.baseUrl + pathTemplate)。
|
|
3
|
-
* URL 拼接与 host 白名单与 Tool Gateway 共用 @
|
|
3
|
+
* URL 拼接与 host 白名单与 Tool Gateway 共用 @eap/contracts http-outbound。
|
|
4
4
|
*/
|
|
5
|
-
import { assertHttpHostAllowlisted, buildHttpToolUrl as buildSharedHttpToolUrl, ErrorCodes } from '@
|
|
5
|
+
import { assertHttpHostAllowlisted, buildHttpToolUrl as buildSharedHttpToolUrl, ErrorCodes } from '@eap/contracts';
|
|
6
6
|
import { abortOrTimeoutMessage, DEFAULT_HTTP_TOOL_TIMEOUT_SEC, isAbortError, resolveToolTimeoutSec, toToolTimeoutMs, } from './timeout.js';
|
|
7
7
|
export function resolveHttpExecutionFromRequest(request) {
|
|
8
8
|
const raw = request.execution;
|
|
@@ -23,9 +23,20 @@ export function resolveHttpExecutionFromRequest(request) {
|
|
|
23
23
|
allowedHosts: Array.isArray(e.allowedHosts)
|
|
24
24
|
? e.allowedHosts.filter((item) => typeof item === 'string')
|
|
25
25
|
: [],
|
|
26
|
+
header: readHeaderMap(e.header),
|
|
26
27
|
timeout: resolveToolTimeoutSec(e.timeout, DEFAULT_HTTP_TOOL_TIMEOUT_SEC),
|
|
27
28
|
};
|
|
28
29
|
}
|
|
30
|
+
function readHeaderMap(raw) {
|
|
31
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
32
|
+
return undefined;
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
35
|
+
if (typeof value === 'string')
|
|
36
|
+
out[key] = value;
|
|
37
|
+
}
|
|
38
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
39
|
+
}
|
|
29
40
|
export function buildHttpToolUrl(spec, args) {
|
|
30
41
|
const url = buildSharedHttpToolUrl(spec.baseUrl ?? '', spec.pathTemplate ?? '', args);
|
|
31
42
|
assertHttpHostAllowlisted(url, spec.allowedHosts ?? []);
|
|
@@ -54,6 +65,7 @@ export class DevHttpExecutor {
|
|
|
54
65
|
const timer = setTimeout(() => controller.abort(), toToolTimeoutMs(timeoutSec, DEFAULT_HTTP_TOOL_TIMEOUT_SEC));
|
|
55
66
|
const headers = {
|
|
56
67
|
Accept: 'application/json, text/plain;q=0.9',
|
|
68
|
+
...(spec.header ?? {}),
|
|
57
69
|
};
|
|
58
70
|
const init = { method, headers, signal: controller.signal };
|
|
59
71
|
if (method !== 'GET' && method !== 'HEAD') {
|
|
@@ -17,13 +17,23 @@
|
|
|
17
17
|
* @see docs/workflows/local-agent-dev-loop.md
|
|
18
18
|
* @see runtime/src/tool/local-mcp-client.ts
|
|
19
19
|
*/
|
|
20
|
+
import { type McpRuntimeExecution } from '@eap/contracts';
|
|
20
21
|
import { type ILocalMcpClient } from './local-mcp-client.js';
|
|
21
|
-
import type { IToolExecutor, ToolCallRequest, ToolCallResult } from './tool-executor.interface.js';
|
|
22
|
+
import type { IToolExecutor, McpListedTool, ToolCallRequest, ToolCallResult } from './tool-executor.interface.js';
|
|
22
23
|
export interface DevMcpExecutorOptions {
|
|
23
24
|
client?: ILocalMcpClient;
|
|
24
25
|
}
|
|
25
26
|
export declare class DevMcpExecutor implements IToolExecutor {
|
|
26
27
|
private readonly client;
|
|
28
|
+
private readonly listCache;
|
|
27
29
|
constructor(options?: DevMcpExecutorOptions);
|
|
28
30
|
execute(request: ToolCallRequest): Promise<ToolCallResult>;
|
|
31
|
+
listTools(request: ToolCallRequest): Promise<McpListedTool[]>;
|
|
29
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* 把 tool.manifest.execution(McpExecution 结构,transport 可为 stdio/sse/streamable_http)
|
|
35
|
+
* 规范化为 LocalMcpClient 消费的 McpRuntimeExecution(transport 只支持 stdio/streamable_http)。
|
|
36
|
+
*
|
|
37
|
+
* sse 归并到 streamable_http(LocalMcpClient 的 http 分支已能解析 SSE 响应)。
|
|
38
|
+
*/
|
|
39
|
+
export declare function normalizeMcpExecution(raw: unknown, fallbackToolName: string): McpRuntimeExecution | null;
|
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
* @see docs/workflows/local-agent-dev-loop.md
|
|
18
18
|
* @see runtime/src/tool/local-mcp-client.ts
|
|
19
19
|
*/
|
|
20
|
-
import { ErrorCodes } from '@
|
|
20
|
+
import { ErrorCodes } from '@eap/contracts';
|
|
21
21
|
import { isAbortError } from './timeout.js';
|
|
22
22
|
import { LocalMcpClient } from './local-mcp-client.js';
|
|
23
23
|
export class DevMcpExecutor {
|
|
24
24
|
client;
|
|
25
|
+
listCache = new Map();
|
|
25
26
|
constructor(options = {}) {
|
|
26
27
|
this.client = options.client ?? new LocalMcpClient();
|
|
27
28
|
}
|
|
@@ -64,6 +65,28 @@ export class DevMcpExecutor {
|
|
|
64
65
|
};
|
|
65
66
|
}
|
|
66
67
|
}
|
|
68
|
+
async listTools(request) {
|
|
69
|
+
const execution = normalizeExecution(request.execution, request.toolName);
|
|
70
|
+
if (!execution) {
|
|
71
|
+
throw new Error(`Dev MCP: tool "${request.toolName}" has no valid mcp execution config (expect manifest.execution.protocol="mcp")`);
|
|
72
|
+
}
|
|
73
|
+
const url = execution.server?.url ?? '';
|
|
74
|
+
const cmd = execution.server?.command ?? '';
|
|
75
|
+
const args = (execution.server?.args ?? []).join('\0');
|
|
76
|
+
const key = `${url || `stdio:${cmd}:${args}`}\0${request.authContext?.userId ?? ''}\0${request.authContext?.agentCode ?? ''}`;
|
|
77
|
+
const cached = this.listCache.get(key);
|
|
78
|
+
if (cached)
|
|
79
|
+
return cached;
|
|
80
|
+
const pending = this.client.list(execution, { userId: request.authContext?.userId });
|
|
81
|
+
this.listCache.set(key, pending);
|
|
82
|
+
try {
|
|
83
|
+
return await pending;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
this.listCache.delete(key);
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
67
90
|
}
|
|
68
91
|
/**
|
|
69
92
|
* 把 tool.manifest.execution(McpExecution 结构,transport 可为 stdio/sse/streamable_http)
|
|
@@ -71,6 +94,9 @@ export class DevMcpExecutor {
|
|
|
71
94
|
*
|
|
72
95
|
* sse 归并到 streamable_http(LocalMcpClient 的 http 分支已能解析 SSE 响应)。
|
|
73
96
|
*/
|
|
97
|
+
export function normalizeMcpExecution(raw, fallbackToolName) {
|
|
98
|
+
return normalizeExecution(raw, fallbackToolName);
|
|
99
|
+
}
|
|
74
100
|
function normalizeExecution(raw, fallbackToolName) {
|
|
75
101
|
if (!raw || typeof raw !== 'object')
|
|
76
102
|
return null;
|