@gt-fe/eap-runtime 0.0.8 → 0.0.10
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 +12 -17
- package/dist/api/handlers/invoke.handler.js +3 -3
- package/dist/api/handlers/messages.handler.js +0 -1
- package/dist/api/handlers/stream.handler.js +3 -3
- package/dist/api/routes/agent.routes.js +2 -2
- package/dist/checkpoint/checkpoint-ref-notify.d.ts +10 -0
- package/dist/checkpoint/checkpoint-ref-notify.js +59 -0
- package/dist/checkpoint/index.d.ts +1 -0
- package/dist/checkpoint/index.js +1 -0
- package/dist/checkpoint/pg-checkpoint-saver.js +25 -2
- package/dist/common/agent-code.d.ts +2 -4
- package/dist/common/agent-code.js +4 -5
- package/dist/contracts/trace-collector.d.ts +15 -1
- package/dist/debug/llm-turn-log.d.ts +100 -0
- package/dist/debug/llm-turn-log.js +130 -0
- package/dist/debug/pg-llm-turn-log.d.ts +22 -0
- package/dist/debug/pg-llm-turn-log.js +57 -0
- package/dist/graph/adapters/langgraph-checkpointer.js +3 -1
- package/dist/graph/adapters/langgraph.adapter.d.ts +1 -0
- package/dist/graph/adapters/langgraph.adapter.js +147 -91
- package/dist/graph/adapters/skill-read.d.ts +12 -0
- package/dist/graph/adapters/skill-read.js +63 -0
- package/dist/graph/adapters/subgraph-compiler.js +5 -0
- package/dist/graph/contracts/executable-graph.d.ts +3 -3
- package/dist/graph/hitl/hitl-handler.d.ts +7 -0
- package/dist/graph/hitl/hitl-handler.js +47 -6
- package/dist/graph/nodes/llm-node.d.ts +14 -0
- package/dist/graph/nodes/skill-bind-node.js +1 -2
- package/dist/graph/nodes/tool-node.d.ts +2 -0
- package/dist/graph/nodes/tool-node.js +24 -9
- package/dist/graph/trace-span.d.ts +22 -0
- package/dist/graph/trace-span.js +69 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/memory/compression/compression.repository.d.ts +1 -15
- package/dist/providers/echo-llm-provider.d.ts +1 -1
- package/dist/providers/echo-llm-provider.js +2 -1
- package/dist/providers/http-llm-provider.d.ts +4 -0
- package/dist/providers/http-llm-provider.js +129 -4
- package/dist/providers/local-llm-provider.d.ts +5 -1
- package/dist/providers/local-llm-provider.js +14 -6
- package/dist/providers/routing-llm-provider.d.ts +4 -0
- package/dist/providers/routing-llm-provider.js +12 -2
- 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 +2 -2
- package/dist/registry/deployment-snapshot.d.ts +4 -4
- package/dist/registry/deployment-snapshot.js +50 -28
- package/dist/registry/graph-loader.d.ts +1 -1
- package/dist/registry/graph-loader.js +39 -76
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +1 -1
- package/dist/registry/skill-tree.d.ts +3 -0
- package/dist/registry/skill-tree.js +41 -3
- package/dist/runtime/agent-runtime.d.ts +42 -3
- package/dist/runtime/agent-runtime.js +110 -34
- package/dist/runtime/dev-runtime.d.ts +6 -3
- package/dist/runtime/dev-runtime.js +21 -14
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/storage-profile.d.ts +19 -10
- package/dist/runtime/storage-profile.js +42 -29
- package/dist/runtime/types.d.ts +15 -17
- package/dist/server.js +1 -1
- package/dist/session/session.service.d.ts +5 -0
- package/dist/session/session.service.js +12 -0
- package/dist/sse/events/index.d.ts +5 -3
- package/dist/sse/events/index.js +2 -0
- package/dist/sse/events/skill-events.d.ts +11 -0
- package/dist/sse/trace-collector.d.ts +10 -0
- package/dist/sse/trace-collector.js +1 -0
- package/dist/token/token-budget.service.d.ts +2 -2
- package/dist/token/token-budget.service.js +1 -1
- package/dist/tool/create-tool-executor.js +3 -2
- package/dist/tool/harness-primitives.d.ts +32 -0
- package/dist/tool/harness-primitives.js +385 -0
- package/dist/tool/http-tool-executor.js +1 -1
- package/dist/tool/index.d.ts +2 -1
- package/dist/tool/index.js +2 -1
- package/dist/tool/local-tool-executor.d.ts +3 -3
- package/dist/tool/local-tool-executor.js +5 -7
- package/dist/tool/mcp-tool-executor.js +9 -1
- package/dist/tool/system-prompt-builder.d.ts +61 -25
- package/dist/tool/system-prompt-builder.js +195 -92
- package/dist/tool/tool-execution-router.js +1 -1
- package/dist/tool/tool-executor.interface.d.ts +12 -6
- package/package.json +2 -2
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { readFile } from 'node:fs/promises';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
-
import { resolveAgentModelAllowlist,
|
|
9
|
+
import { resolveAgentModelAllowlist, resolveAgentDefaultModel } from './agent-models.js';
|
|
10
10
|
import { createDeploymentSnapshotFromEnv, isInlineToolDeclaration, overlayToolRefPolicies, } from './deployment-snapshot.js';
|
|
11
11
|
import { preloadGraphNotFound, preloadManifestNotFound, preloadSkillNotFound, preloadToolNotFound, } from './preload-errors.js';
|
|
12
12
|
import { PreinstallDefinitionCache, resolveDefinitionCacheEnabled, } from './definition-cache.js';
|
|
13
|
-
import { inspectAgentSkillTree, readSkillAssembly, resolveSkillHotReload, skillAssemblyStamp, } from './skill-tree.js';
|
|
14
|
-
function
|
|
15
|
-
if (!
|
|
13
|
+
import { inspectAgentSkillTree, listSkillDirs, readSkillAssembly, resolveSkillHotReload, skillAssemblyStamp, } from './skill-tree.js';
|
|
14
|
+
function applyDefaultModelToGraph(graph, defaultModel) {
|
|
15
|
+
if (!defaultModel)
|
|
16
16
|
return graph;
|
|
17
17
|
let changed = false;
|
|
18
18
|
const nodes = graph.nodes.map((node) => {
|
|
@@ -22,7 +22,7 @@ function applyPrimaryModelToGraph(graph, primary) {
|
|
|
22
22
|
if (current && current.toLowerCase() !== 'default')
|
|
23
23
|
return node;
|
|
24
24
|
changed = true;
|
|
25
|
-
return { ...node, config: { ...node.config, model:
|
|
25
|
+
return { ...node, config: { ...node.config, model: defaultModel } };
|
|
26
26
|
});
|
|
27
27
|
return changed ? { ...graph, nodes } : graph;
|
|
28
28
|
}
|
|
@@ -41,7 +41,7 @@ function assertGraphModelsAllowed(graph, manifest) {
|
|
|
41
41
|
return graph;
|
|
42
42
|
}
|
|
43
43
|
function finalizeLoadedGraph(graph, manifest) {
|
|
44
|
-
return assertGraphModelsAllowed(
|
|
44
|
+
return assertGraphModelsAllowed(applyDefaultModelToGraph(graph, resolveAgentDefaultModel((manifest ?? {}))), manifest);
|
|
45
45
|
}
|
|
46
46
|
function asGraphDsl(value) {
|
|
47
47
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
@@ -94,57 +94,8 @@ async function resolveSkillPrompt(manifest, bundlePath) {
|
|
|
94
94
|
}
|
|
95
95
|
return raw;
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
* scripts 为可选/非标字段:Skill 包仅强制要求 skill.md,其余文件结构不限;
|
|
100
|
-
* 存在 scripts 声明时才物化为可调用的本地 Tool。
|
|
101
|
-
*/
|
|
102
|
-
function resolveSkillScripts(manifest, bundlePath) {
|
|
103
|
-
const scripts = manifest.scripts;
|
|
104
|
-
if (!Array.isArray(scripts))
|
|
105
|
-
return [];
|
|
106
|
-
const tools = [];
|
|
107
|
-
for (const script of scripts) {
|
|
108
|
-
if (!script || typeof script !== 'object')
|
|
109
|
-
continue;
|
|
110
|
-
const s = script;
|
|
111
|
-
const name = s.name;
|
|
112
|
-
const command = s.command;
|
|
113
|
-
if (!name || !command)
|
|
114
|
-
continue;
|
|
115
|
-
const args = Array.isArray(s.args) ? s.args.map(String) : [];
|
|
116
|
-
tools.push({
|
|
117
|
-
toolId: `script:${name}`,
|
|
118
|
-
name,
|
|
119
|
-
manifest: {
|
|
120
|
-
id: `script:${name}`,
|
|
121
|
-
name,
|
|
122
|
-
version: '0.0.0',
|
|
123
|
-
type: 'code',
|
|
124
|
-
status: 'active',
|
|
125
|
-
description: s.description ?? `Skill script: ${name}`,
|
|
126
|
-
execution: {
|
|
127
|
-
runtime: command === 'python3' || command === 'python' ? 'python'
|
|
128
|
-
: command === 'node' ? 'node'
|
|
129
|
-
: 'executable',
|
|
130
|
-
handler: args[0] ?? command,
|
|
131
|
-
args: args.slice(1),
|
|
132
|
-
timeout: 30000,
|
|
133
|
-
sandbox: false,
|
|
134
|
-
bundlePath,
|
|
135
|
-
command,
|
|
136
|
-
/** 标记为 Skill 虚拟脚本:Router 优先本机执行,不经 Gateway */
|
|
137
|
-
localPreferred: true,
|
|
138
|
-
source: 'skill-script',
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
inputSchema: { type: 'object', properties: {}, additionalProperties: true },
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
return tools;
|
|
145
|
-
}
|
|
146
|
-
function extractSkillToolRefs(manifest) {
|
|
147
|
-
const refs = manifest.toolRefs ?? manifest.tools;
|
|
97
|
+
function extractSkillToolsets(manifest) {
|
|
98
|
+
const refs = manifest.toolsets;
|
|
148
99
|
if (!Array.isArray(refs))
|
|
149
100
|
return undefined;
|
|
150
101
|
const out = [];
|
|
@@ -156,14 +107,15 @@ function extractSkillToolRefs(manifest) {
|
|
|
156
107
|
if (!item || typeof item !== 'object')
|
|
157
108
|
continue;
|
|
158
109
|
const o = item;
|
|
110
|
+
const code = o.code != null ? String(o.code) : undefined;
|
|
111
|
+
const enabled = Array.isArray(o.enabled)
|
|
112
|
+
? o.enabled.filter((x) => typeof x === 'string')
|
|
113
|
+
: undefined;
|
|
159
114
|
out.push({
|
|
160
|
-
|
|
161
|
-
name: o.name != null ? String(o.name) : o.toolset != null ? String(o.toolset) : undefined,
|
|
115
|
+
name: o.name != null ? String(o.name) : code,
|
|
162
116
|
version: o.version != null ? String(o.version) : undefined,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
? o.enabledTools.filter((x) => typeof x === 'string')
|
|
166
|
-
: undefined,
|
|
117
|
+
code,
|
|
118
|
+
enabled,
|
|
167
119
|
autoApprove: Array.isArray(o.autoApprove)
|
|
168
120
|
? o.autoApprove.filter((x) => typeof x === 'string')
|
|
169
121
|
: undefined,
|
|
@@ -305,7 +257,7 @@ function fromPreinstallTool(pre) {
|
|
|
305
257
|
* 以工具集为管理单元:单个 MCP 服务 / 单个代码包 = 一个工具集,可含多个子工具;
|
|
306
258
|
* 子工具继承集级 server/package 配置,合成各自的 execution。
|
|
307
259
|
* 无 tools[] 时回退为单工具 manifest(tool.manifest.json 结构)。
|
|
308
|
-
*
|
|
260
|
+
* enabled 为可选白名单(agent.manifest toolsets[].enabled);缺省启用全部子工具。
|
|
309
261
|
*/
|
|
310
262
|
function expandPreinstallToolset(pre, options) {
|
|
311
263
|
const m = pre.manifest;
|
|
@@ -659,7 +611,7 @@ export class GraphLoader {
|
|
|
659
611
|
if (graphFromManifest && typeof graphFromManifest === 'object') {
|
|
660
612
|
return finalizeLoadedGraph(yamlGraphToDsl(agentCode, graphFromManifest), manifest);
|
|
661
613
|
}
|
|
662
|
-
const model =
|
|
614
|
+
const model = resolveAgentDefaultModel(manifest) ?? 'default';
|
|
663
615
|
return {
|
|
664
616
|
...DEFAULT_GRAPH_DSL,
|
|
665
617
|
name: agentCode,
|
|
@@ -851,7 +803,7 @@ export class GraphLoader {
|
|
|
851
803
|
return definitions;
|
|
852
804
|
}
|
|
853
805
|
/**
|
|
854
|
-
*
|
|
806
|
+
* 加载已装配 Skill:SKILL.md 目录(Agent Skills)或可选 skill.manifest.json。
|
|
855
807
|
* @param options.userId 用于解析 users/<userId>/skills(优先于 preinstall/skills)
|
|
856
808
|
* @param options.allowMissingSkills 缺失时跳过而非抛错(warm 预热用)
|
|
857
809
|
*/
|
|
@@ -889,8 +841,7 @@ export class GraphLoader {
|
|
|
889
841
|
asGraphDsl(pre.manifest.definitionSnapshot);
|
|
890
842
|
// 解析 prompt:支持文件路径引用(./ 开头)
|
|
891
843
|
const prompt = await resolveSkillPrompt(pre.manifest, pre.bundlePath);
|
|
892
|
-
|
|
893
|
-
const scriptTools = resolveSkillScripts(pre.manifest, pre.bundlePath);
|
|
844
|
+
const entryFile = typeof pre.manifest.skillEntryFile === 'string' ? pre.manifest.skillEntryFile : undefined;
|
|
894
845
|
skills.push({
|
|
895
846
|
skillId: pre.skillId,
|
|
896
847
|
name: pre.name,
|
|
@@ -898,10 +849,11 @@ export class GraphLoader {
|
|
|
898
849
|
version: pre.version,
|
|
899
850
|
workflow,
|
|
900
851
|
prompt,
|
|
901
|
-
|
|
852
|
+
bundlePath: pre.bundlePath,
|
|
853
|
+
entryFile,
|
|
854
|
+
toolsets: extractSkillToolsets(pre.manifest),
|
|
902
855
|
inputDef: pre.manifest.inputDef,
|
|
903
856
|
outputDef: pre.manifest.outputDef,
|
|
904
|
-
scriptTools: scriptTools.length > 0 ? scriptTools : undefined,
|
|
905
857
|
});
|
|
906
858
|
}
|
|
907
859
|
else if (!this.strictPreinstall) {
|
|
@@ -940,16 +892,25 @@ export class GraphLoader {
|
|
|
940
892
|
* 磁盘多出来的未声明目录不进选择清单;正文热加载仍看已装配目录的 mtime。
|
|
941
893
|
*/
|
|
942
894
|
async resolveSkillRefs(snap, agentDir, userId) {
|
|
895
|
+
const byId = new Map();
|
|
896
|
+
const add = (ref) => {
|
|
897
|
+
if (ref.skillId && !byId.has(ref.skillId))
|
|
898
|
+
byId.set(ref.skillId, ref);
|
|
899
|
+
};
|
|
943
900
|
if (agentDir) {
|
|
944
901
|
const assembly = await readSkillAssembly(agentDir, userId);
|
|
945
902
|
if (assembly && assembly.skills.length > 0) {
|
|
946
|
-
|
|
903
|
+
for (const skillId of assembly.skills)
|
|
904
|
+
add({ skillId });
|
|
947
905
|
}
|
|
948
906
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
907
|
+
if (byId.size === 0) {
|
|
908
|
+
for (const ref of snap?.skills ?? [])
|
|
909
|
+
add(ref);
|
|
910
|
+
}
|
|
911
|
+
for (const root of this.localSkillRoots()) {
|
|
912
|
+
for (const skillId of await listSkillDirs(root))
|
|
913
|
+
add({ skillId });
|
|
953
914
|
}
|
|
954
915
|
return [...byId.values()];
|
|
955
916
|
}
|
|
@@ -981,5 +942,7 @@ export class GraphLoader {
|
|
|
981
942
|
}
|
|
982
943
|
function resolveLocalSkillsDir(explicit) {
|
|
983
944
|
const candidate = explicit?.trim() || process.env.EAP_LOCAL_SKILLS_DIR?.trim();
|
|
984
|
-
|
|
945
|
+
if (candidate)
|
|
946
|
+
return path.resolve(candidate);
|
|
947
|
+
return path.resolve(process.cwd(), 'skills');
|
|
985
948
|
}
|
package/dist/registry/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { resolveAgentDefaultModel, resolveAgentModelAllowlist, resolveDeclaredModels } from './agent-models.js';
|
|
2
2
|
export { createAgentRegistryClient, type AgentRegistryClient, type AgentRegistryRecord, type AgentManifest } from './agent-registry-client.js';
|
|
3
3
|
export { GraphLoader, type GraphLoaderConfig, type LoadToolDefinitionsOptions, type LoadAgentOptions } from './graph-loader.js';
|
|
4
4
|
export { PreinstallDefinitionCache, resolveDefinitionCacheEnabled, type CachedAgentDefinitions, } from './definition-cache.js';
|
package/dist/registry/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { resolveAgentDefaultModel, resolveAgentModelAllowlist, resolveDeclaredModels } from './agent-models.js';
|
|
2
2
|
export { createAgentRegistryClient } from './agent-registry-client.js';
|
|
3
3
|
export { GraphLoader } from './graph-loader.js';
|
|
4
4
|
export { PreinstallDefinitionCache, resolveDefinitionCacheEnabled, } from './definition-cache.js';
|
|
@@ -3,6 +3,9 @@ export interface AgentSkillTree {
|
|
|
3
3
|
/** `${count}:${maxMtimeMs}`;增删改任一 skill 文件都会变 */
|
|
4
4
|
stamp: string;
|
|
5
5
|
}
|
|
6
|
+
/** Agent Skills 入口:目录内的 SKILL.md(规范名优先)。 */
|
|
7
|
+
export declare function findSkillEntryFile(bundlePath: string): Promise<string | null>;
|
|
8
|
+
export declare function listSkillDirs(root: string): Promise<string[]>;
|
|
6
9
|
export interface SkillAssembly {
|
|
7
10
|
skills: string[];
|
|
8
11
|
source: string;
|
|
@@ -10,6 +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 '@gt-fe/eap-contracts';
|
|
13
14
|
const SKILL_CODE = /^[a-z0-9][a-z0-9-]*$/;
|
|
14
15
|
async function exists(p) {
|
|
15
16
|
try {
|
|
@@ -20,6 +21,43 @@ async function exists(p) {
|
|
|
20
21
|
return false;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
24
|
+
/** Agent Skills 入口:目录内的 SKILL.md(规范名优先)。 */
|
|
25
|
+
export async function findSkillEntryFile(bundlePath) {
|
|
26
|
+
for (const name of SKILL_ENTRY_CANDIDATES) {
|
|
27
|
+
const candidate = path.join(bundlePath, name);
|
|
28
|
+
if (await exists(candidate))
|
|
29
|
+
return candidate;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const files = await readdir(bundlePath);
|
|
33
|
+
const hit = files.find((file) => file.toLowerCase() === 'skill.md');
|
|
34
|
+
return hit ? path.join(bundlePath, hit) : null;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function listSkillDirs(root) {
|
|
41
|
+
let names = [];
|
|
42
|
+
try {
|
|
43
|
+
names = await readdir(root);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
const codes = [];
|
|
49
|
+
for (const name of names) {
|
|
50
|
+
if (!SKILL_CODE.test(name))
|
|
51
|
+
continue;
|
|
52
|
+
const bundle = path.join(root, name);
|
|
53
|
+
const info = await stat(bundle).catch(() => undefined);
|
|
54
|
+
if (!info?.isDirectory())
|
|
55
|
+
continue;
|
|
56
|
+
if (await findSkillEntryFile(bundle))
|
|
57
|
+
codes.push(name);
|
|
58
|
+
}
|
|
59
|
+
return codes.sort();
|
|
60
|
+
}
|
|
23
61
|
async function maxMtime(filePath, fallback = 0) {
|
|
24
62
|
try {
|
|
25
63
|
const info = await stat(filePath);
|
|
@@ -101,9 +139,9 @@ export async function inspectAgentSkillTree(agentDir, userId, extraRoots = []) {
|
|
|
101
139
|
const bundleStat = await stat(bundle).catch(() => undefined);
|
|
102
140
|
if (!bundleStat?.isDirectory())
|
|
103
141
|
continue;
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
if (!
|
|
142
|
+
const hasEntry = await findSkillEntryFile(bundle);
|
|
143
|
+
const hasOverlay = await exists(path.join(bundle, 'skill.manifest.json'));
|
|
144
|
+
if (!hasEntry && !hasOverlay)
|
|
107
145
|
continue;
|
|
108
146
|
codes.add(name);
|
|
109
147
|
latest = Math.max(latest, bundleStat.mtimeMs);
|
|
@@ -14,9 +14,11 @@ import { HitlHandler } from '../graph/hitl/hitl-handler.js';
|
|
|
14
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
|
+
import type { SessionService, SessionRecord, MessageRecord } from '../session/session.service.js';
|
|
18
|
+
import { type ILlmTurnLog, type LlmTurnIndexEntry, type LlmTurnLogKind, type LlmTurnRecord } from '../debug/llm-turn-log.js';
|
|
17
19
|
import type { SSEEvent } from '../sse/contracts/sse-emitter.contract.js';
|
|
18
20
|
import type { TaskService } from '../task/task.service.js';
|
|
19
|
-
import type { ExecuteParams, ExecuteResult, ForkOverrides, ForkResult, HitlResponse, ResumeHitlResult, SeedSessionMessagesParams, SeedSessionMessagesResult } from './types.js';
|
|
21
|
+
import type { ExecuteParams, ExecuteResult, ForkParams, ForkOverrides, ForkResult, HitlResponse, ResumeHitlResult, SeedSessionMessagesParams, SeedSessionMessagesResult } from './types.js';
|
|
20
22
|
export interface ForkRelationRecord {
|
|
21
23
|
originalTraceId: string;
|
|
22
24
|
forkedTraceId: string;
|
|
@@ -34,6 +36,10 @@ export interface AgentRuntimeDeps {
|
|
|
34
36
|
graphLoader?: GraphLoader;
|
|
35
37
|
/** 多轮短期记忆写回;未注入时 AgentRuntime.stream 跳过持久化 */
|
|
36
38
|
messageService?: MessageService;
|
|
39
|
+
/** 会话查询(getConversation);未注入时仅返回 messages/llmTurns */
|
|
40
|
+
sessionService?: SessionService;
|
|
41
|
+
/** 每步 LLM 入参/响应落盘;未注入时按环境默认 `.eap/llm-turns` */
|
|
42
|
+
turnLog?: ILlmTurnLog;
|
|
37
43
|
/**
|
|
38
44
|
* Hosting 注入的 Trace 采集器。与 ExecuteParams.traceId 同时存在时,
|
|
39
45
|
* Skill/Tool 节点会 createNode/completeNode。不注入则只发 SSE,不写 Span。
|
|
@@ -41,6 +47,27 @@ export interface AgentRuntimeDeps {
|
|
|
41
47
|
traceCollector?: ITraceCollector;
|
|
42
48
|
recordForkRelation?: RecordForkRelation;
|
|
43
49
|
}
|
|
50
|
+
/** 一次对话详情:消息 + 每步 LLM 入参/响应。File / 内存 / Postgres 同一套结构。 */
|
|
51
|
+
export interface ConversationDetail {
|
|
52
|
+
sessionId: string;
|
|
53
|
+
session: SessionRecord | null;
|
|
54
|
+
messages: MessageRecord[];
|
|
55
|
+
llmTurns: LlmTurnRecord[];
|
|
56
|
+
llmTurnIndex: LlmTurnIndexEntry[];
|
|
57
|
+
tokens: {
|
|
58
|
+
input: number;
|
|
59
|
+
output: number;
|
|
60
|
+
total: number;
|
|
61
|
+
byNode: Record<string, {
|
|
62
|
+
input: number;
|
|
63
|
+
output: number;
|
|
64
|
+
}>;
|
|
65
|
+
};
|
|
66
|
+
storage: {
|
|
67
|
+
llmTurnKind?: LlmTurnLogKind;
|
|
68
|
+
llmTurnDir?: string;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
44
71
|
/**
|
|
45
72
|
* AgentRuntime — 管理 Graph 执行的 SDK 门面
|
|
46
73
|
*/
|
|
@@ -69,6 +96,17 @@ export declare class AgentRuntime {
|
|
|
69
96
|
* @throws PARAMS_INVALID messageService 未注入(无存储无法写入)
|
|
70
97
|
*/
|
|
71
98
|
seedSessionMessages(params: SeedSessionMessagesParams): Promise<SeedSessionMessagesResult>;
|
|
99
|
+
/**
|
|
100
|
+
* 查询一次对话详情:会话元数据、消息、每步 LLM 落盘(入参、响应、URL、模型、脱敏 sk、提示词)。
|
|
101
|
+
* 本地默认读 `.eap/sessions` + `.eap/llm-turns/{sessionId}/`。
|
|
102
|
+
*/
|
|
103
|
+
getConversation(sessionId: string): Promise<ConversationDetail>;
|
|
104
|
+
listLlmTurns(sessionId: string): Promise<LlmTurnIndexEntry[]>;
|
|
105
|
+
getLlmTurn(sessionId: string, turnId: string): Promise<LlmTurnRecord | null>;
|
|
106
|
+
listConversations(options?: {
|
|
107
|
+
userId?: string;
|
|
108
|
+
}): Promise<SessionRecord[]>;
|
|
109
|
+
private resolveTurnLog;
|
|
72
110
|
/** 同步执行 Graph(经 InvokeOrchestrator,内含 loadedTools 注入) */
|
|
73
111
|
execute(params: ExecuteParams): Promise<ExecuteResult>;
|
|
74
112
|
/**
|
|
@@ -92,9 +130,10 @@ export declare class AgentRuntime {
|
|
|
92
130
|
*/
|
|
93
131
|
streamResumeHitl(taskId: string, response: HitlResponse): AsyncIterable<SSEEvent>;
|
|
94
132
|
/**
|
|
95
|
-
* 从 Checkpoint Fork
|
|
133
|
+
* 从 Checkpoint Fork 并真实重新执行。
|
|
134
|
+
* taskId / traceId 由上层发号;Runtime 不 createFlow、不另发 Task 号。
|
|
96
135
|
*/
|
|
97
|
-
fork(
|
|
136
|
+
fork(params: ForkParams): Promise<ForkResult>;
|
|
98
137
|
private prepareHitlResume;
|
|
99
138
|
private rebuildExecutableGraph;
|
|
100
139
|
private handleHitlTimeout;
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { randomUUID } from 'node:crypto';
|
|
10
10
|
import { HitlAlreadyTimedOutError, HitlNotWaitingError, buildHitlWaitedPayload, isHitlDeadlinePassed, readPersistedHitlWaiting, unwrapHitlPaused, } from '../graph/hitl/hitl-handler.js';
|
|
11
|
-
import { toLoadedSkillMap
|
|
11
|
+
import { toLoadedSkillMap } from '../tool/system-prompt-builder.js';
|
|
12
|
+
import { assembleLoadedTools } from '../tool/harness-primitives.js';
|
|
13
|
+
import { createFileLlmTurnLogFromEnv, } from '../debug/llm-turn-log.js';
|
|
12
14
|
import { buildHitlWaitingTaskOutput, buildTurnGraphMessages, buildTurnTranscriptFromStreamEvents, extractHitlThreadId, extractStreamReasoning, extractStreamTextDelta, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, } from '../session/persist-turn-messages.js';
|
|
13
15
|
import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
|
|
14
16
|
import { resolveExecutionContext } from '../governance/governance-client.js';
|
|
@@ -42,6 +44,7 @@ export class AgentRuntime {
|
|
|
42
44
|
if (typeof this.deps.hitlHandler.setTimeoutListener === 'function') {
|
|
43
45
|
this.deps.hitlHandler.setTimeoutListener((event) => this.handleHitlTimeout(event));
|
|
44
46
|
}
|
|
47
|
+
this.deps.hitlHandler.setTraceCollector?.(this.deps.traceCollector);
|
|
45
48
|
}
|
|
46
49
|
/** 从 GraphLoader 解析 tool manifest 映射(invoke/stream/resume/fork 共用) */
|
|
47
50
|
async resolveLoadedTools(agentCode, agentVersion) {
|
|
@@ -126,6 +129,64 @@ export class AgentRuntime {
|
|
|
126
129
|
})));
|
|
127
130
|
return { sessionId, seeded: true };
|
|
128
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* 查询一次对话详情:会话元数据、消息、每步 LLM 落盘(入参、响应、URL、模型、脱敏 sk、提示词)。
|
|
134
|
+
* 本地默认读 `.eap/sessions` + `.eap/llm-turns/{sessionId}/`。
|
|
135
|
+
*/
|
|
136
|
+
async getConversation(sessionId) {
|
|
137
|
+
const turnLog = this.resolveTurnLog();
|
|
138
|
+
const [session, messages, llmTurnIndex] = await Promise.all([
|
|
139
|
+
this.deps.sessionService
|
|
140
|
+
? this.deps.sessionService.findSession(sessionId).catch(() => null)
|
|
141
|
+
: Promise.resolve(null),
|
|
142
|
+
this.deps.messageService
|
|
143
|
+
? this.deps.messageService.getMessages(sessionId)
|
|
144
|
+
: Promise.resolve([]),
|
|
145
|
+
turnLog ? turnLog.list(sessionId) : Promise.resolve([]),
|
|
146
|
+
]);
|
|
147
|
+
const llmTurns = [];
|
|
148
|
+
if (turnLog) {
|
|
149
|
+
for (const entry of llmTurnIndex) {
|
|
150
|
+
const record = await turnLog.read(sessionId, entry.id);
|
|
151
|
+
if (record)
|
|
152
|
+
llmTurns.push(record);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const tokens = summarizeTurnTokens(llmTurns);
|
|
156
|
+
return {
|
|
157
|
+
sessionId,
|
|
158
|
+
session,
|
|
159
|
+
messages,
|
|
160
|
+
llmTurns,
|
|
161
|
+
llmTurnIndex,
|
|
162
|
+
tokens,
|
|
163
|
+
storage: { llmTurnKind: turnLog?.kind, llmTurnDir: turnLog?.baseDir },
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
async listLlmTurns(sessionId) {
|
|
167
|
+
const turnLog = this.resolveTurnLog();
|
|
168
|
+
if (!turnLog)
|
|
169
|
+
return [];
|
|
170
|
+
return turnLog.list(sessionId);
|
|
171
|
+
}
|
|
172
|
+
async getLlmTurn(sessionId, turnId) {
|
|
173
|
+
const turnLog = this.resolveTurnLog();
|
|
174
|
+
if (!turnLog)
|
|
175
|
+
return null;
|
|
176
|
+
return turnLog.read(sessionId, turnId);
|
|
177
|
+
}
|
|
178
|
+
async listConversations(options) {
|
|
179
|
+
if (options?.userId && this.deps.sessionService) {
|
|
180
|
+
return this.deps.sessionService.listSessions(options.userId);
|
|
181
|
+
}
|
|
182
|
+
if (this.deps.sessionService) {
|
|
183
|
+
return this.deps.sessionService.listAllSessions();
|
|
184
|
+
}
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
resolveTurnLog() {
|
|
188
|
+
return this.deps.turnLog ?? createFileLlmTurnLogFromEnv();
|
|
189
|
+
}
|
|
129
190
|
/** 同步执行 Graph(经 InvokeOrchestrator,内含 loadedTools 注入) */
|
|
130
191
|
async execute(params) {
|
|
131
192
|
const agentCode = resolveAgentCode(params);
|
|
@@ -203,8 +264,7 @@ export class AgentRuntime {
|
|
|
203
264
|
})
|
|
204
265
|
: undefined;
|
|
205
266
|
const skillDefinitions = toLoadedSkillMap(loadedSkills);
|
|
206
|
-
|
|
207
|
-
const loadedTools = mergeSkillScriptTools(loadedToolsBase, loadedSkills);
|
|
267
|
+
const loadedTools = assembleLoadedTools(loadedToolsBase);
|
|
208
268
|
const promptRoots = this.deps.graphLoader
|
|
209
269
|
? await this.deps.graphLoader.resolvePromptRoots(agentCode, params.agentVersion)
|
|
210
270
|
: [];
|
|
@@ -405,8 +465,8 @@ export class AgentRuntime {
|
|
|
405
465
|
const agentVersion = (typeof resumedState.agentVersion === 'string' && resumedState.agentVersion) ||
|
|
406
466
|
undefined;
|
|
407
467
|
const graph = await this.rebuildExecutableGraph(agentCode, agentVersion);
|
|
408
|
-
const loadedTools = resumedState.loadedTools ??
|
|
409
|
-
(await this.resolveLoadedTools(agentCode, agentVersion));
|
|
468
|
+
const loadedTools = assembleLoadedTools(resumedState.loadedTools ??
|
|
469
|
+
(await this.resolveLoadedTools(agentCode, agentVersion)));
|
|
410
470
|
const skillFromState = resumedState.skillDefinitions;
|
|
411
471
|
const skillBundle = skillFromState
|
|
412
472
|
? {
|
|
@@ -536,16 +596,23 @@ export class AgentRuntime {
|
|
|
536
596
|
}
|
|
537
597
|
}
|
|
538
598
|
/**
|
|
539
|
-
* 从 Checkpoint Fork
|
|
599
|
+
* 从 Checkpoint Fork 并真实重新执行。
|
|
600
|
+
* taskId / traceId 由上层发号;Runtime 不 createFlow、不另发 Task 号。
|
|
540
601
|
*/
|
|
541
|
-
async fork(
|
|
602
|
+
async fork(params) {
|
|
603
|
+
const { checkpointId, taskId, traceId } = params;
|
|
604
|
+
if (!taskId?.trim() || !traceId?.trim()) {
|
|
605
|
+
throw new AppError(ErrorCodes.PARAMS_INVALID, 'fork requires non-empty taskId and traceId from the caller', { exposeMessage: true });
|
|
606
|
+
}
|
|
607
|
+
const overrides = {
|
|
608
|
+
...params.overrides,
|
|
609
|
+
executionContext: params.executionContext ?? params.overrides?.executionContext,
|
|
610
|
+
};
|
|
542
611
|
const source = await this.deps.checkpointSaver.loadByCheckpointId(checkpointId);
|
|
543
612
|
if (!source) {
|
|
544
613
|
throw new AppError(ErrorCodes.HOSTING_CHECKPOINT_NOT_FOUND, `Checkpoint not found: ${checkpointId}`, { exposeMessage: true });
|
|
545
614
|
}
|
|
546
|
-
// 优先级:overrides(agentCode > 废弃 agentId)> Checkpoint state
|
|
547
615
|
const agentCode = overrides.agentCode ??
|
|
548
|
-
overrides.agentId ??
|
|
549
616
|
source.state.agentCode ??
|
|
550
617
|
source.state.agentId ??
|
|
551
618
|
'unknown-agent';
|
|
@@ -556,8 +623,9 @@ export class AgentRuntime {
|
|
|
556
623
|
source.checkpointId;
|
|
557
624
|
const newThreadId = randomUUID();
|
|
558
625
|
const forkedCheckpoint = await this.deps.checkpointSaver.fork(checkpointId, newThreadId, overrides);
|
|
559
|
-
const correlationId = randomUUID();
|
|
626
|
+
const correlationId = overrides.executionContext?.correlationId ?? randomUUID();
|
|
560
627
|
const task = await this.deps.taskService.create({
|
|
628
|
+
id: taskId,
|
|
561
629
|
agentId: agentCode,
|
|
562
630
|
sessionId,
|
|
563
631
|
userId,
|
|
@@ -566,25 +634,12 @@ export class AgentRuntime {
|
|
|
566
634
|
timeout: 600_000,
|
|
567
635
|
});
|
|
568
636
|
await this.deps.taskService.updateStatus(task.id, 'running');
|
|
569
|
-
let newTraceId = randomUUID();
|
|
570
|
-
if (this.deps.traceCollector) {
|
|
571
|
-
newTraceId = await this.deps.traceCollector.createFlow({
|
|
572
|
-
traceId: newTraceId,
|
|
573
|
-
agentId: agentCode,
|
|
574
|
-
agentCode,
|
|
575
|
-
sessionId,
|
|
576
|
-
userId,
|
|
577
|
-
correlationId,
|
|
578
|
-
callType: 'fork',
|
|
579
|
-
metadata: { checkpointId, originalTraceId },
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
637
|
const agentVersion = overrides.agentVersion ??
|
|
583
638
|
(typeof source.state.agentVersion === 'string' ? source.state.agentVersion : undefined);
|
|
584
639
|
const graph = await this.rebuildExecutableGraph(agentCode, agentVersion);
|
|
585
640
|
const loadedToolsBase = await this.resolveLoadedTools(agentCode, agentVersion);
|
|
586
641
|
const skillBundle = await this.resolveLoadedSkills(agentCode, userId, agentVersion);
|
|
587
|
-
const loadedTools =
|
|
642
|
+
const loadedTools = assembleLoadedTools(loadedToolsBase);
|
|
588
643
|
try {
|
|
589
644
|
const result = await this.deps.graphEngine.invoke(graph, {
|
|
590
645
|
...forkedCheckpoint.state,
|
|
@@ -594,20 +649,26 @@ export class AgentRuntime {
|
|
|
594
649
|
executionContext: overrides.executionContext,
|
|
595
650
|
loadedTools,
|
|
596
651
|
...skillBundle,
|
|
597
|
-
|
|
652
|
+
taskId: task.id,
|
|
653
|
+
traceId,
|
|
598
654
|
traceCollector: this.deps.traceCollector,
|
|
599
655
|
});
|
|
600
656
|
await this.deps.taskService.updateStatus(task.id, 'success', result.output);
|
|
601
657
|
if (this.deps.recordForkRelation) {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
658
|
+
try {
|
|
659
|
+
await this.deps.recordForkRelation({
|
|
660
|
+
originalTraceId,
|
|
661
|
+
forkedTraceId: traceId,
|
|
662
|
+
checkpointId,
|
|
663
|
+
overrides,
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
catch {
|
|
667
|
+
// Fork 关系上报失败不阻断执行结果
|
|
668
|
+
}
|
|
608
669
|
}
|
|
609
670
|
return {
|
|
610
|
-
newTraceId,
|
|
671
|
+
newTraceId: traceId,
|
|
611
672
|
originalTraceId,
|
|
612
673
|
newTaskId: task.id,
|
|
613
674
|
checkpointId,
|
|
@@ -618,14 +679,14 @@ export class AgentRuntime {
|
|
|
618
679
|
output_tokens: result.tokenUsage.output,
|
|
619
680
|
total_tokens: result.tokenUsage.total,
|
|
620
681
|
},
|
|
621
|
-
compareUrl: `/traces/compare?traceId1=${originalTraceId}&traceId2=${
|
|
682
|
+
compareUrl: `/traces/compare?traceId1=${originalTraceId}&traceId2=${traceId}`,
|
|
622
683
|
};
|
|
623
684
|
}
|
|
624
685
|
catch (err) {
|
|
625
686
|
const message = err instanceof Error ? err.message : 'Unknown error';
|
|
626
687
|
await this.deps.taskService.updateStatus(task.id, 'failed', message);
|
|
627
688
|
return {
|
|
628
|
-
newTraceId,
|
|
689
|
+
newTraceId: traceId,
|
|
629
690
|
originalTraceId,
|
|
630
691
|
newTaskId: task.id,
|
|
631
692
|
checkpointId,
|
|
@@ -771,6 +832,21 @@ export class AgentRuntime {
|
|
|
771
832
|
};
|
|
772
833
|
}
|
|
773
834
|
}
|
|
835
|
+
function summarizeTurnTokens(turns) {
|
|
836
|
+
const byNode = {};
|
|
837
|
+
let input = 0;
|
|
838
|
+
let output = 0;
|
|
839
|
+
for (const turn of turns) {
|
|
840
|
+
input += turn.response.inputTokens ?? 0;
|
|
841
|
+
output += turn.response.outputTokens ?? 0;
|
|
842
|
+
const nodeId = turn.nodeId ?? 'llm';
|
|
843
|
+
const bucket = byNode[nodeId] ?? { input: 0, output: 0 };
|
|
844
|
+
bucket.input += turn.response.inputTokens ?? 0;
|
|
845
|
+
bucket.output += turn.response.outputTokens ?? 0;
|
|
846
|
+
byNode[nodeId] = bucket;
|
|
847
|
+
}
|
|
848
|
+
return { input, output, total: input + output, byNode };
|
|
849
|
+
}
|
|
774
850
|
/** 工厂:从运行时依赖组装 AgentRuntime */
|
|
775
851
|
export function createAgentRuntime(deps) {
|
|
776
852
|
return new AgentRuntime(deps);
|
|
@@ -27,6 +27,7 @@ import { InvokeOrchestrator, StreamOrchestrator, SessionService, MessageService,
|
|
|
27
27
|
import { type AgentRuntime } from './agent-runtime.js';
|
|
28
28
|
import type { RuntimeStorageProfile } from './storage-profile.js';
|
|
29
29
|
import type { HitlResponse, ResumeHitlResult, SSEEvent } from './types.js';
|
|
30
|
+
import type { ILlmTurnLog } from '../debug/llm-turn-log.js';
|
|
30
31
|
/** createDevRuntime 返回值(CLI 嵌入用,可选字段后续扩展) */
|
|
31
32
|
export interface DevRuntime {
|
|
32
33
|
invokeOrchestrator: InvokeOrchestrator;
|
|
@@ -39,6 +40,7 @@ export interface DevRuntime {
|
|
|
39
40
|
streamResumeHitl: (taskId: string, response: HitlResponse) => AsyncIterable<SSEEvent>;
|
|
40
41
|
sessionService: SessionService;
|
|
41
42
|
messageService: MessageService;
|
|
43
|
+
turnLog?: ILlmTurnLog;
|
|
42
44
|
sessionContextService: SessionContextService;
|
|
43
45
|
taskService: TaskService;
|
|
44
46
|
tokenStatsService: TokenStatsService;
|
|
@@ -53,9 +55,10 @@ export interface DevRuntime {
|
|
|
53
55
|
llmDefaultModel: string;
|
|
54
56
|
governanceKind: 'AllowAll' | 'Http';
|
|
55
57
|
storageProfile: RuntimeStorageProfile;
|
|
56
|
-
sessionStoreKind: 'file' | '
|
|
57
|
-
messageStoreKind: '
|
|
58
|
-
checkpointKind: '
|
|
58
|
+
sessionStoreKind: 'file' | 'hosting';
|
|
59
|
+
messageStoreKind: 'file' | 'postgres';
|
|
60
|
+
checkpointKind: 'file' | 'redis+pg';
|
|
61
|
+
llmTurnKind: 'file' | 'postgres';
|
|
59
62
|
foundationConfigured: boolean;
|
|
60
63
|
skillHotReload: boolean;
|
|
61
64
|
};
|