@gt-fe/eap-runtime 0.0.8 → 0.0.11
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 +15 -20
- package/dist/api/handlers/invoke.handler.js +7 -3
- package/dist/api/handlers/messages.handler.js +0 -1
- package/dist/api/handlers/stream.handler.js +8 -4
- package/dist/api/routes/agent.routes.d.ts +2 -2
- 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/common/types.d.ts +5 -5
- package/dist/common/types.js +2 -2
- package/dist/contracts/permission-checker.d.ts +1 -1
- package/dist/contracts/trace-collector.d.ts +16 -2
- 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/governance/governance-client.d.ts +1 -1
- package/dist/governance/governance-client.js +40 -16
- package/dist/governance/index.d.ts +2 -2
- package/dist/governance/index.js +1 -1
- package/dist/governance/permission-guard.d.ts +2 -1
- package/dist/governance/permission-guard.js +13 -4
- 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 +153 -92
- 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 +11 -9
- package/dist/graph/contracts/executable-graph.js +1 -1
- package/dist/graph/hitl/hitl-handler.d.ts +10 -0
- package/dist/graph/hitl/hitl-handler.js +50 -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 +50 -10
- package/dist/graph/trace-span.d.ts +22 -0
- package/dist/graph/trace-span.js +69 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -1
- 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 +4 -3
- 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 +3 -3
- package/dist/registry/deployment-snapshot.d.ts +17 -5
- package/dist/registry/deployment-snapshot.js +97 -49
- package/dist/registry/graph-loader.d.ts +2 -2
- package/dist/registry/graph-loader.js +43 -80
- package/dist/registry/index.d.ts +2 -2
- package/dist/registry/index.js +2 -2
- 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.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 +129 -39
- package/dist/runtime/dev-runtime.d.ts +7 -4
- package/dist/runtime/dev-runtime.js +24 -21
- 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 +17 -19
- package/dist/server.js +2 -2
- 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 +12 -1
- package/dist/sse/events/skill-events.js +1 -1
- 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.d.ts +1 -1
- package/dist/tool/create-tool-executor.js +3 -2
- package/dist/tool/dev-http-executor.d.ts +1 -1
- package/dist/tool/dev-http-executor.js +10 -7
- package/dist/tool/dev-mcp-executor.js +5 -3
- package/dist/tool/dev-tool-executor.d.ts +1 -1
- package/dist/tool/dev-tool-executor.js +2 -2
- package/dist/tool/harness-command.d.ts +32 -0
- package/dist/tool/harness-command.js +276 -0
- package/dist/tool/harness-primitives.d.ts +54 -0
- package/dist/tool/harness-primitives.js +492 -0
- package/dist/tool/http-tool-executor.js +16 -21
- package/dist/tool/index.d.ts +4 -2
- package/dist/tool/index.js +4 -2
- package/dist/tool/local-mcp-client.d.ts +1 -1
- package/dist/tool/local-mcp-client.js +54 -23
- package/dist/tool/local-tool-executor.d.ts +4 -4
- package/dist/tool/local-tool-executor.js +18 -16
- package/dist/tool/mcp-tool-executor.js +23 -7
- package/dist/tool/skill-script-refs.d.ts +20 -0
- package/dist/tool/skill-script-refs.js +62 -0
- package/dist/tool/system-prompt-builder.d.ts +61 -25
- package/dist/tool/system-prompt-builder.js +198 -92
- package/dist/tool/timeout.d.ts +14 -0
- package/dist/tool/timeout.js +36 -0
- package/dist/tool/tool-confirmation.js +2 -1
- package/dist/tool/tool-execution-router.d.ts +1 -1
- package/dist/tool/tool-execution-router.js +2 -2
- package/dist/tool/tool-executor.interface.d.ts +23 -7
- package/dist/tool/tool-orchestrator.d.ts +1 -1
- package/dist/tool/tool-orchestrator.js +6 -3
- 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 +3 -3
- package/dist/common/concurrency.d.ts +0 -79
- package/dist/common/concurrency.js +0 -121
- package/dist/common/queue-consumer.d.ts +0 -125
- package/dist/common/queue-consumer.js +0 -159
- package/dist/tool/mock-tool-executor.d.ts +0 -28
- package/dist/tool/mock-tool-executor.js +0 -65
|
@@ -18,13 +18,17 @@ import { ToolResultInjector } from '../../tool/tool-result-injector.js';
|
|
|
18
18
|
import { fileArtifactFromToolOutput } from '../../tool/file-artifact.js';
|
|
19
19
|
import { EchoLLMProvider } from '../../providers/echo-llm-provider.js';
|
|
20
20
|
import { ConditionEvaluator } from '../condition-evaluator.js';
|
|
21
|
-
import { SystemPromptBuilder,
|
|
21
|
+
import { SystemPromptBuilder, buildToolCatalog, loadedToolsToApiTools } from '../../tool/system-prompt-builder.js';
|
|
22
|
+
import { resolveJailFromState, resolveJailRoots } from '../../tool/harness-primitives.js';
|
|
23
|
+
import os from 'node:os';
|
|
22
24
|
import { bindSelectedSkill } from '../nodes/skill-bind-node.js';
|
|
23
25
|
import { resolveGraphPrompt } from '../resolve-graph-prompt.js';
|
|
24
26
|
import { SkillAuditRecorder } from '../skill-audit-recorder.js';
|
|
25
27
|
import { preloadSkillNotFound } from '../../registry/preload-errors.js';
|
|
26
|
-
import { AppError, ErrorCodes } from '@
|
|
28
|
+
import { AppError, ErrorCodes } from '@eap/contracts';
|
|
27
29
|
import { isHitlDecision, lastAssistantHasUnansweredToolCalls } from '../hitl/hitl-handler.js';
|
|
30
|
+
import { classifySkillRead } from './skill-read.js';
|
|
31
|
+
import { withGraphSpan } from '../trace-span.js';
|
|
28
32
|
// ─────────────────────────────────────────────
|
|
29
33
|
// LangGraphAdapter
|
|
30
34
|
// ─────────────────────────────────────────────
|
|
@@ -148,6 +152,7 @@ export class LangGraphAdapter {
|
|
|
148
152
|
/**
|
|
149
153
|
* Tool 节点执行旁路:started → tool.called + tool.progress;
|
|
150
154
|
* completed/error → 对应终态 + tool.progress。同一 toolCallId。
|
|
155
|
+
* 读 SKILL.md / 技能包内文件:成功后再发 skill.loaded / skill.resource_read,同时保留 tool.*。
|
|
151
156
|
*/
|
|
152
157
|
emitToolExecutionEvents(progress) {
|
|
153
158
|
if (!this.streamSink)
|
|
@@ -158,6 +163,30 @@ export class LangGraphAdapter {
|
|
|
158
163
|
...(progress.node ? { node: progress.node } : {}),
|
|
159
164
|
...(progress.skillId ? { skillId: progress.skillId } : {}),
|
|
160
165
|
};
|
|
166
|
+
const skillRead = classifySkillRead(progress.toolName, progress.arguments, progress.skillDirs);
|
|
167
|
+
if (skillRead && progress.status === 'completed') {
|
|
168
|
+
if (skillRead.kind === 'loaded') {
|
|
169
|
+
this.streamSink({
|
|
170
|
+
type: 'skill.loaded',
|
|
171
|
+
taskId,
|
|
172
|
+
correlationId,
|
|
173
|
+
timestamp,
|
|
174
|
+
payload: {
|
|
175
|
+
skillId: skillRead.skillId || loc.skillId || skillRead.path,
|
|
176
|
+
path: skillRead.path,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this.streamSink({
|
|
182
|
+
type: 'skill.resource_read',
|
|
183
|
+
taskId,
|
|
184
|
+
correlationId,
|
|
185
|
+
timestamp,
|
|
186
|
+
payload: { skillId: skillRead.skillId || loc.skillId, path: skillRead.path },
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
161
190
|
if (progress.status === 'started') {
|
|
162
191
|
this.streamSink({
|
|
163
192
|
type: 'tool.called',
|
|
@@ -274,14 +303,15 @@ export class LangGraphAdapter {
|
|
|
274
303
|
activeSkillPrompt: ephemeral(undefined),
|
|
275
304
|
activatedSkill: ephemeral(undefined),
|
|
276
305
|
skillRoute: ephemeral(undefined),
|
|
277
|
-
skillSelectRaw: ephemeral(undefined),
|
|
278
306
|
silentOutput: ephemeral(undefined),
|
|
279
307
|
promptRoots: ephemeral(undefined),
|
|
308
|
+
agentDir: ephemeral(undefined),
|
|
280
309
|
executionContext: ephemeral(undefined),
|
|
281
310
|
forceToolServer: ephemeral(undefined),
|
|
282
311
|
traceCollector: ephemeral(undefined),
|
|
283
312
|
traceId: ephemeral(undefined),
|
|
284
313
|
skillSpanId: ephemeral(undefined),
|
|
314
|
+
lastSpanId: ephemeral(undefined),
|
|
285
315
|
hitlChoice: ephemeral(undefined),
|
|
286
316
|
// ── LLM Token 使用量:数组累加器 reducer,每次 LLM 节点追加一条使用明细
|
|
287
317
|
__llmTokenUsage: {
|
|
@@ -546,25 +576,22 @@ export class LangGraphAdapter {
|
|
|
546
576
|
case 'tool':
|
|
547
577
|
return this.createToolNodeFunction(node);
|
|
548
578
|
case 'condition':
|
|
549
|
-
return async (state) => {
|
|
550
|
-
// 条件节点:根据 config 中的 conditionExpr 评估并写入 __conditionResult
|
|
579
|
+
return async (state) => withGraphSpan(state, { name: node.id, type: 'condition' }, async () => {
|
|
551
580
|
const conditionExpr = node.config?.['conditionExpr']
|
|
552
581
|
?? node.config?.['conditionFn'];
|
|
553
582
|
if (conditionExpr) {
|
|
554
583
|
const branchValues = node.config?.['branchValues']
|
|
555
584
|
?? this.extractBranchValuesFromDsl(node.id);
|
|
556
585
|
const result = await this.conditionEvaluator.evaluate(conditionExpr, state, branchValues, node.config?.['fallback'] ?? '__default__');
|
|
557
|
-
return {
|
|
586
|
+
return { currentNode: node.id, __conditionResult: result };
|
|
558
587
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
};
|
|
588
|
+
return { currentNode: node.id };
|
|
589
|
+
});
|
|
562
590
|
case 'subgraph':
|
|
563
591
|
return this.createSubGraphNodeFunction(node);
|
|
564
592
|
case 'human':
|
|
565
593
|
return async (state) => {
|
|
566
|
-
|
|
567
|
-
return { ...state, currentNode: node.id };
|
|
594
|
+
return { currentNode: node.id };
|
|
568
595
|
};
|
|
569
596
|
case 'parallel':
|
|
570
597
|
return this.createParallelNodeFunction(node);
|
|
@@ -639,11 +666,14 @@ export class LangGraphAdapter {
|
|
|
639
666
|
return undefined;
|
|
640
667
|
const rows = Object.values(defs).map((skill) => {
|
|
641
668
|
const desc = (skill.description ?? '').replace(/\s+/g, ' ').trim();
|
|
642
|
-
|
|
669
|
+
const entry = skill.entryFile ?? (skill.bundlePath ? `${skill.bundlePath}/SKILL.md` : '');
|
|
670
|
+
const title = skill.name && skill.name !== skill.skillId ? `${skill.skillId}(${skill.name})` : skill.skillId;
|
|
671
|
+
const pathLine = entry ? `\n SKILL.md: ${entry}` : '';
|
|
672
|
+
return `- **${title}**${desc ? `:${desc}` : ''}${pathLine}`;
|
|
643
673
|
});
|
|
644
674
|
if (rows.length === 0)
|
|
645
675
|
return undefined;
|
|
646
|
-
return `# 可用技能\n
|
|
676
|
+
return `# 可用技能\n要用某个技能时,调用 read_file 读取其 SKILL.md。脚本和参考文档默认在同一目录。\n${rows.join('\n')}`;
|
|
647
677
|
}
|
|
648
678
|
createToolNodeFunction(node) {
|
|
649
679
|
const toolName = node.config?.['tool'] ?? node.id;
|
|
@@ -672,6 +702,10 @@ export class LangGraphAdapter {
|
|
|
672
702
|
arguments: defaultArgs,
|
|
673
703
|
node: node.id,
|
|
674
704
|
});
|
|
705
|
+
const skillDefs = state['skillDefinitions'];
|
|
706
|
+
const jailExtraRoots = Object.values(skillDefs ?? {})
|
|
707
|
+
.map((skill) => skill.bundlePath)
|
|
708
|
+
.filter((p) => Boolean(p));
|
|
675
709
|
const result = await this.toolExecutor.execute({
|
|
676
710
|
toolCallId,
|
|
677
711
|
toolName,
|
|
@@ -679,16 +713,22 @@ export class LangGraphAdapter {
|
|
|
679
713
|
authContext: { userId, delegationToken, agentCode },
|
|
680
714
|
correlationId,
|
|
681
715
|
taskId,
|
|
716
|
+
...(jailExtraRoots.length > 0 ? { jailExtraRoots } : {}),
|
|
682
717
|
});
|
|
683
718
|
const executionTime = Date.now() - startedAt;
|
|
719
|
+
const skillDirs = Object.values(skillDefs ?? {})
|
|
720
|
+
.filter((skill) => Boolean(skill.bundlePath))
|
|
721
|
+
.map((skill) => ({ skillId: skill.skillId, bundlePath: skill.bundlePath }));
|
|
684
722
|
this.emitToolExecutionEvents({
|
|
685
723
|
toolCallId: result.toolCallId,
|
|
686
724
|
toolName: result.toolName,
|
|
687
725
|
status: result.success ? 'completed' : 'error',
|
|
726
|
+
arguments: defaultArgs,
|
|
688
727
|
content: result.content,
|
|
689
728
|
error: result.error,
|
|
690
729
|
executionTime,
|
|
691
730
|
node: node.id,
|
|
731
|
+
...(skillDirs.length > 0 ? { skillDirs } : {}),
|
|
692
732
|
});
|
|
693
733
|
// 重要:messages channel 的 reducer 是 append 语义,只返回新增消息
|
|
694
734
|
return {
|
|
@@ -733,8 +773,8 @@ export class LangGraphAdapter {
|
|
|
733
773
|
toolChoice: node.config?.['toolChoice'],
|
|
734
774
|
};
|
|
735
775
|
const disableTools = node.config?.['disableTools'] === true;
|
|
736
|
-
const includeSkillCatalog = node.config?.['includeSkillCatalog'] === true;
|
|
737
776
|
const silent = node.config?.['silent'] === true;
|
|
777
|
+
const includeSkillCatalog = node.config?.['includeSkillCatalog'] !== false;
|
|
738
778
|
return async (state) => {
|
|
739
779
|
// HITL 续跑:已有未闭合 toolCalls + hitlChoice 时不要再调 LLM(否则会换新 toolCallId)
|
|
740
780
|
const hitlChoice = state['hitlChoice'];
|
|
@@ -745,92 +785,113 @@ export class LangGraphAdapter {
|
|
|
745
785
|
lastAssistantHasUnansweredToolCalls(rawMessages)) {
|
|
746
786
|
return { currentNode: node.id };
|
|
747
787
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
788
|
+
return withGraphSpan(state, { name: node.id, type: 'llm' }, async () => {
|
|
789
|
+
const promptRoots = Array.isArray(state['promptRoots']) ? state['promptRoots'] : [];
|
|
790
|
+
const resolvedPrompt = resolveGraphPrompt(baseConfig.systemPrompt, promptRoots);
|
|
791
|
+
const loadedTools = state['loadedTools'];
|
|
792
|
+
const agentCode = typeof state['agentCode'] === 'string' ? state['agentCode'] : undefined;
|
|
793
|
+
const skillDefs = state['skillDefinitions'];
|
|
794
|
+
// [DEBUG] 打印 loadedTools 实际状态
|
|
795
|
+
if (process.env.EAP_LLM_DEBUG === '1') {
|
|
796
|
+
const toolNames = loadedTools ? Object.keys(loadedTools) : [];
|
|
797
|
+
console.log(`[LLM-DEBUG] node=${node.id} loadedTools=${toolNames.length}:`, toolNames);
|
|
798
|
+
}
|
|
799
|
+
const catalog = includeSkillCatalog ? this.buildSkillCatalog(skillDefs) : undefined;
|
|
800
|
+
const toolCatalog = disableTools ? undefined : buildToolCatalog(loadedTools);
|
|
801
|
+
const execCtx = state['executionContext'];
|
|
802
|
+
const userId = (typeof execCtx?.userId === 'string' && execCtx.userId) || state['userId'];
|
|
803
|
+
const jail = resolveJailFromState({
|
|
804
|
+
agentDir: state['agentDir'],
|
|
805
|
+
userId,
|
|
806
|
+
executionContext: execCtx,
|
|
807
|
+
loadedTools,
|
|
808
|
+
skillDefinitions: skillDefs,
|
|
809
|
+
});
|
|
810
|
+
const workingDirectory = jail.workdir;
|
|
811
|
+
const jailRoots = resolveJailRoots(jail);
|
|
812
|
+
const skillDirs = Object.values(skillDefs ?? {})
|
|
813
|
+
.filter((skill) => Boolean(skill.bundlePath))
|
|
814
|
+
.map((skill) => ({
|
|
815
|
+
skillId: skill.skillId,
|
|
816
|
+
bundlePath: skill.bundlePath,
|
|
817
|
+
entryFile: skill.entryFile,
|
|
818
|
+
}));
|
|
819
|
+
const assembled = this.systemPromptBuilder.build({
|
|
820
|
+
developerPrompt: resolvedPrompt || undefined,
|
|
821
|
+
skillCatalog: catalog,
|
|
822
|
+
toolCatalog,
|
|
823
|
+
agentCode,
|
|
824
|
+
userId,
|
|
825
|
+
workingDirectory,
|
|
826
|
+
os: { platform: os.platform(), release: os.release() },
|
|
827
|
+
jailRoots,
|
|
828
|
+
skillDirs,
|
|
829
|
+
});
|
|
830
|
+
const apiTools = disableTools ? [] : loadedToolsToApiTools(loadedTools);
|
|
831
|
+
const finalConfig = {
|
|
832
|
+
...baseConfig,
|
|
833
|
+
systemPrompt: assembled.text,
|
|
834
|
+
systemSections: assembled.sections,
|
|
835
|
+
turn: {
|
|
836
|
+
sessionId: typeof state['sessionId'] === 'string' ? state['sessionId'] : undefined,
|
|
837
|
+
taskId: typeof state['taskId'] === 'string' ? state['taskId'] : undefined,
|
|
838
|
+
nodeId: node.id,
|
|
839
|
+
agentCode,
|
|
840
|
+
correlationId: typeof state['correlationId'] === 'string' ? state['correlationId'] : undefined,
|
|
841
|
+
},
|
|
842
|
+
tools: disableTools ? undefined : (apiTools.length > 0 ? apiTools : baseConfig.tools),
|
|
843
|
+
toolChoice: disableTools ? 'none' : baseConfig.toolChoice,
|
|
844
|
+
};
|
|
845
|
+
// 流式旁路:有 streamSink 时启用 LLM token 推送;silent 节点不推给用户
|
|
846
|
+
const enableStream = Boolean(this.streamSink) && !silent;
|
|
847
|
+
this.reasoningStartedThisTurn = false;
|
|
848
|
+
this.streamedTextThisTurn = false;
|
|
849
|
+
const llmNode = new LLMNode(node.id, finalConfig, this.llmProvider, enableStream, enableStream
|
|
850
|
+
? (text) => {
|
|
851
|
+
if (!this.streamSink || !text)
|
|
852
|
+
return;
|
|
853
|
+
const timestamp = new Date().toISOString();
|
|
854
|
+
if (!this.reasoningStartedThisTurn) {
|
|
855
|
+
this.reasoningStartedThisTurn = true;
|
|
856
|
+
this.streamIteration += 1;
|
|
857
|
+
this.streamSink({
|
|
858
|
+
type: 'reasoning.started',
|
|
859
|
+
taskId: undefined,
|
|
860
|
+
correlationId: undefined,
|
|
861
|
+
timestamp,
|
|
862
|
+
payload: {
|
|
863
|
+
iterationCount: this.streamIteration,
|
|
864
|
+
model: finalConfig.model,
|
|
865
|
+
},
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
this.streamedTextThisTurn = true;
|
|
796
869
|
this.streamSink({
|
|
797
|
-
type: '
|
|
870
|
+
type: 'response.chunk',
|
|
798
871
|
taskId: undefined,
|
|
799
872
|
correlationId: undefined,
|
|
800
873
|
timestamp,
|
|
801
874
|
payload: {
|
|
802
|
-
|
|
803
|
-
|
|
875
|
+
content: text,
|
|
876
|
+
index: this.streamChunkIndex++,
|
|
877
|
+
node: node.id,
|
|
804
878
|
},
|
|
805
879
|
});
|
|
806
880
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
const next = await llmNode.execute(state);
|
|
822
|
-
if (!silent)
|
|
823
|
-
return next;
|
|
824
|
-
const added = Array.isArray(next.messages) ? next.messages[0] : undefined;
|
|
825
|
-
const raw = added && typeof added === 'object' && typeof added.content === 'string'
|
|
826
|
-
? added.content
|
|
827
|
-
: '';
|
|
828
|
-
return {
|
|
829
|
-
...next,
|
|
830
|
-
messages: [],
|
|
831
|
-
silentOutput: raw,
|
|
832
|
-
skillSelectRaw: raw,
|
|
833
|
-
};
|
|
881
|
+
: undefined);
|
|
882
|
+
const next = await llmNode.execute(state);
|
|
883
|
+
if (!silent)
|
|
884
|
+
return next;
|
|
885
|
+
const added = Array.isArray(next.messages) ? next.messages[0] : undefined;
|
|
886
|
+
const raw = added && typeof added === 'object' && typeof added.content === 'string'
|
|
887
|
+
? added.content
|
|
888
|
+
: '';
|
|
889
|
+
return {
|
|
890
|
+
...next,
|
|
891
|
+
messages: [],
|
|
892
|
+
silentOutput: raw,
|
|
893
|
+
};
|
|
894
|
+
});
|
|
834
895
|
};
|
|
835
896
|
}
|
|
836
897
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface SkillDirRef {
|
|
2
|
+
skillId: string;
|
|
3
|
+
bundlePath: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* 判断 harness 读操作是否命中技能包,并用 bundlePath 最长前缀解析 skillId。
|
|
7
|
+
*/
|
|
8
|
+
export declare function classifySkillRead(toolName: string, args: Record<string, unknown> | undefined, skillDirs?: SkillDirRef[]): {
|
|
9
|
+
kind: 'loaded' | 'resource';
|
|
10
|
+
path: string;
|
|
11
|
+
skillId: string;
|
|
12
|
+
} | null;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
function tryRealpath(p) {
|
|
4
|
+
try {
|
|
5
|
+
return realpathSync(p);
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function normalizeForMatch(p) {
|
|
12
|
+
const resolved = path.resolve(p);
|
|
13
|
+
return (tryRealpath(resolved) ?? resolved).replace(/\\/g, '/').replace(/\/+$/, '');
|
|
14
|
+
}
|
|
15
|
+
function longestSkillMatch(filePath, skillDirs) {
|
|
16
|
+
if (!skillDirs?.length)
|
|
17
|
+
return undefined;
|
|
18
|
+
const target = normalizeForMatch(filePath);
|
|
19
|
+
let best;
|
|
20
|
+
for (const dir of skillDirs) {
|
|
21
|
+
if (!dir.bundlePath)
|
|
22
|
+
continue;
|
|
23
|
+
const root = normalizeForMatch(dir.bundlePath);
|
|
24
|
+
if (target === root || target.startsWith(`${root}/`)) {
|
|
25
|
+
if (!best || root.length > best.len) {
|
|
26
|
+
best = { skillId: dir.skillId, bundlePath: root, len: root.length };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return best;
|
|
31
|
+
}
|
|
32
|
+
function skillIdFromSkillsSegment(norm) {
|
|
33
|
+
const match = norm.match(/(?:^|\/)(?:preinstall\/)?skills\/([^/]+)(?:\/|$)/i);
|
|
34
|
+
return match?.[1] ?? '';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 判断 harness 读操作是否命中技能包,并用 bundlePath 最长前缀解析 skillId。
|
|
38
|
+
*/
|
|
39
|
+
export function classifySkillRead(toolName, args, skillDirs) {
|
|
40
|
+
const n = toolName.toLowerCase().replace(/-/g, '_');
|
|
41
|
+
if (n !== 'read_file' && n !== 'read_skill_file' && n !== 'readfile')
|
|
42
|
+
return null;
|
|
43
|
+
const filePath = String(args?.path ?? args?.file ?? '');
|
|
44
|
+
if (!filePath)
|
|
45
|
+
return null;
|
|
46
|
+
const norm = filePath.replace(/\\/g, '/');
|
|
47
|
+
const matched = longestSkillMatch(filePath, skillDirs);
|
|
48
|
+
const isEntry = /(^|\/)(SKILL\.md|skill\.md)$/i.test(norm);
|
|
49
|
+
if (matched) {
|
|
50
|
+
return {
|
|
51
|
+
kind: isEntry ? 'loaded' : 'resource',
|
|
52
|
+
path: filePath,
|
|
53
|
+
skillId: matched.skillId,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (isEntry) {
|
|
57
|
+
return { kind: 'loaded', path: filePath, skillId: skillIdFromSkillsSegment(norm) };
|
|
58
|
+
}
|
|
59
|
+
if (/(^|\/)(?:preinstall\/)?skills\//i.test(norm)) {
|
|
60
|
+
return { kind: 'resource', path: filePath, skillId: skillIdFromSkillsSegment(norm) };
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
@@ -138,6 +138,7 @@ export class SubGraphCompiler {
|
|
|
138
138
|
traceId,
|
|
139
139
|
traceCollector,
|
|
140
140
|
skillSpanId: skillSpanId ?? undefined,
|
|
141
|
+
lastSpanId: skillSpanId ?? state['lastSpanId'],
|
|
141
142
|
hitlChoice: state['hitlChoice'],
|
|
142
143
|
hitlRespondedBy: state['hitlRespondedBy'],
|
|
143
144
|
};
|
|
@@ -185,9 +186,13 @@ export class SubGraphCompiler {
|
|
|
185
186
|
? [{ role: 'assistant', content: result.output }]
|
|
186
187
|
: [];
|
|
187
188
|
const perNode = result.tokenUsage?.perNode;
|
|
189
|
+
const nestedLastSpanId = (typeof result.finalState?.lastSpanId === 'string' && result.finalState.lastSpanId) ||
|
|
190
|
+
skillSpanId ||
|
|
191
|
+
undefined;
|
|
188
192
|
return {
|
|
189
193
|
messages: outputMessages,
|
|
190
194
|
currentNode: `subgraph:${dsl.name}`,
|
|
195
|
+
...(nestedLastSpanId ? { lastSpanId: nestedLastSpanId } : {}),
|
|
191
196
|
...(Array.isArray(perNode) && perNode.length > 0
|
|
192
197
|
? {
|
|
193
198
|
__llmTokenUsage: perNode.map((u) => ({
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
* Executable Graph 类型定义
|
|
3
3
|
*
|
|
4
4
|
* 定义 Graph 引擎核心的可执行图、DSL、节点、边等类型。
|
|
5
|
-
* 与 @
|
|
5
|
+
* 与 @eap/contracts 的 GraphDSL 基础类型互补,在 Runtime 层提供更丰富的类型支持。
|
|
6
6
|
*
|
|
7
7
|
* @see design.md §1.4 StateGraph Contract
|
|
8
8
|
* @see requirements.md Requirement 1 AC1-AC2
|
|
9
9
|
*/
|
|
10
|
-
import type { GraphDSL as BaseGraphDSL, GraphNode as BaseGraphNode, GraphEdge as BaseGraphEdge, GraphNodeType, GraphConfig } from '@
|
|
10
|
+
import type { GraphDSL as BaseGraphDSL, GraphNode as BaseGraphNode, GraphEdge as BaseGraphEdge, GraphNodeType, GraphConfig } from '@eap/contracts';
|
|
11
11
|
export type { GraphNodeType, GraphConfig };
|
|
12
12
|
/**
|
|
13
13
|
* RuntimeGraphConfig — Runtime 层扩展的 Graph 配置
|
|
14
14
|
*
|
|
15
|
-
* 在 @
|
|
15
|
+
* 在 @eap/contracts GraphConfig 基础上增加 Runtime 执行所需的 maxRetries。
|
|
16
16
|
*/
|
|
17
17
|
export interface RuntimeGraphConfig extends GraphConfig {
|
|
18
18
|
/** Graph 级默认重试次数(节点级 retry 可覆盖) */
|
|
@@ -21,7 +21,7 @@ export interface RuntimeGraphConfig extends GraphConfig {
|
|
|
21
21
|
/**
|
|
22
22
|
* GraphNode — Runtime 层扩展的节点定义
|
|
23
23
|
*
|
|
24
|
-
* 继承 @
|
|
24
|
+
* 继承 @eap/contracts 基础 GraphNode,增加 Runtime 执行所需的字段:
|
|
25
25
|
* - 重试配置(节点级 maxRetries + 退避策略)
|
|
26
26
|
* - HITL 标记(interrupt_before / interrupt_after)
|
|
27
27
|
* - 描述信息(调试/Trace 用)
|
|
@@ -79,7 +79,7 @@ export interface ConditionalBranch {
|
|
|
79
79
|
/**
|
|
80
80
|
* GraphDSL — Runtime 层扩展的声明式工作流定义
|
|
81
81
|
*
|
|
82
|
-
* 扩展 @
|
|
82
|
+
* 扩展 @eap/contracts 的 BaseGraphDSL,增加条件路由、入口/终止节点等。
|
|
83
83
|
*/
|
|
84
84
|
export interface GraphDSL extends Omit<BaseGraphDSL, 'config' | 'nodes' | 'edges'> {
|
|
85
85
|
/** 覆盖节点类型为 Runtime 扩展版本 */
|
|
@@ -163,7 +163,7 @@ export interface ExecutableGraph {
|
|
|
163
163
|
/** 底层引擎内部实例引用(opaque,由 adapter 管理) */
|
|
164
164
|
_engineInstance?: unknown;
|
|
165
165
|
}
|
|
166
|
-
import type { ExecutionContext as PlatformExecutionContext } from '@
|
|
166
|
+
import type { ExecutionContext as PlatformExecutionContext } from '@eap/contracts';
|
|
167
167
|
/**
|
|
168
168
|
* GraphState — 传入 stream/invoke 的上下文数据
|
|
169
169
|
*/
|
|
@@ -185,7 +185,7 @@ export interface GraphState {
|
|
|
185
185
|
forceToolServer?: string[];
|
|
186
186
|
/** GraphLoader 注入:toolName → 运行期 manifest 引用 */
|
|
187
187
|
loadedTools?: import('../../tool/loaded-tool-definition.js').LoadedToolMap;
|
|
188
|
-
/** GraphLoader 注入:已装配 Skill
|
|
188
|
+
/** GraphLoader 注入:已装配 Skill 列表 */
|
|
189
189
|
loadedSkills?: import('../../tool/system-prompt-builder.js').LoadedSkillInfo[];
|
|
190
190
|
/** GraphLoader 注入:skillId → 完整定义(子图解析 / prompt) */
|
|
191
191
|
skillDefinitions?: import('../../tool/system-prompt-builder.js').LoadedSkillMap;
|
|
@@ -193,10 +193,10 @@ export interface GraphState {
|
|
|
193
193
|
activeSkillPrompt?: string;
|
|
194
194
|
/** silent LLM 的原始输出;不入对话 messages */
|
|
195
195
|
silentOutput?: string;
|
|
196
|
-
/** @deprecated 同 silentOutput,兼容旧图 */
|
|
197
|
-
skillSelectRaw?: string;
|
|
198
196
|
/** 解析 graph 内 ./prompts/*.md 的根目录(agentDir + 工程根) */
|
|
199
197
|
promptRoots?: string[];
|
|
198
|
+
/** 预装 Agent 包目录:`{EAP_PREINSTALL_ROOT}/agents/<code>/<ver>` */
|
|
199
|
+
agentDir?: string;
|
|
200
200
|
activatedSkill?: string | null;
|
|
201
201
|
skillRoute?: string;
|
|
202
202
|
/** bind / invoke 解析出的 Skill 入参;子图 IO 校验读取 */
|
|
@@ -207,6 +207,8 @@ export interface GraphState {
|
|
|
207
207
|
traceCollector?: import('../../contracts/trace-collector.js').ITraceCollector;
|
|
208
208
|
/** 当前 Skill 子图 createNode 返回的 span id;子图内 Tool span 的 parentNodeId */
|
|
209
209
|
skillSpanId?: string;
|
|
210
|
+
/** 最近一次 createNode 的 span id;Checkpoint 引用挂在此 Span 上 */
|
|
211
|
+
lastSpanId?: string;
|
|
210
212
|
/**
|
|
211
213
|
* HITL 续跑:入口 messages 以 __set 覆盖 checkpoint 历史,避免 append 重复。
|
|
212
214
|
* 仅 stream/invoke 入口使用,不入持久化语义。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Executable Graph 类型定义
|
|
3
3
|
*
|
|
4
4
|
* 定义 Graph 引擎核心的可执行图、DSL、节点、边等类型。
|
|
5
|
-
* 与 @
|
|
5
|
+
* 与 @eap/contracts 的 GraphDSL 基础类型互补,在 Runtime 层提供更丰富的类型支持。
|
|
6
6
|
*
|
|
7
7
|
* @see design.md §1.4 StateGraph Contract
|
|
8
8
|
* @see requirements.md Requirement 1 AC1-AC2
|
|
@@ -11,10 +11,14 @@
|
|
|
11
11
|
* @see tasks.md Task 14
|
|
12
12
|
*/
|
|
13
13
|
import type { ICheckpointSaver } from '../../checkpoint/contracts/checkpoint-saver.contract.js';
|
|
14
|
+
import type { ITraceCollector } from '../../contracts/trace-collector.js';
|
|
14
15
|
import type { ISSEEmitter } from '../../sse/contracts/sse-emitter.contract.js';
|
|
15
16
|
import type { GraphState } from '../contracts/executable-graph.js';
|
|
16
17
|
/** HITL 默认等待时长(毫秒)= 300 秒。SSE `hitl.waited.timeout` 对外用秒。 */
|
|
17
18
|
export declare const DEFAULT_HITL_TIMEOUT_MS = 300000;
|
|
19
|
+
/** 超时收口对外文案(Task / Trace / AppError);枚举走 `hitl_timeout`。 */
|
|
20
|
+
export declare const HITL_TIMEOUT_MESSAGE = "HITL \u786E\u8BA4\u8D85\u65F6";
|
|
21
|
+
export declare const HITL_TIMEOUT_ERROR_CODE = "hitl_timeout";
|
|
18
22
|
/** HITL 超时行为 */
|
|
19
23
|
export type HitlTimeoutBehavior = 'error' | 'cancel' | 'auto-confirm';
|
|
20
24
|
/** HITL 中断配置 */
|
|
@@ -51,6 +55,7 @@ export interface HitlWaitingState {
|
|
|
51
55
|
toolName?: string;
|
|
52
56
|
toolSpanId?: string;
|
|
53
57
|
skillSpanId?: string;
|
|
58
|
+
hitlSpanId?: string;
|
|
54
59
|
}
|
|
55
60
|
/** HITL 超时错误(保留类型;timer 不再 throw,resume 逾期改走 AppError) */
|
|
56
61
|
export declare class HitlTimeoutError extends Error {
|
|
@@ -112,6 +117,7 @@ export interface PersistedHitlWaiting {
|
|
|
112
117
|
toolName?: string;
|
|
113
118
|
toolSpanId?: string;
|
|
114
119
|
skillSpanId?: string;
|
|
120
|
+
hitlSpanId?: string;
|
|
115
121
|
/** error/cancel 超时已占用此等待;resume 必须拒绝 */
|
|
116
122
|
timedOut?: boolean;
|
|
117
123
|
}
|
|
@@ -162,8 +168,12 @@ export declare class HitlHandler {
|
|
|
162
168
|
private timeoutListener?;
|
|
163
169
|
/** 同一 taskId 上串行化 resume 与 timeout,避免双边占用 */
|
|
164
170
|
private taskLocks;
|
|
171
|
+
private traceCollector?;
|
|
172
|
+
/** 进程内 interrupt 的 collector,不进 Checkpoint */
|
|
173
|
+
private readonly waitingCollectors;
|
|
165
174
|
constructor(checkpointSaver: ICheckpointSaver, sseEmitter: ISSEEmitter, timeoutListener?: HitlTimeoutListener);
|
|
166
175
|
setTimeoutListener(listener: HitlTimeoutListener | undefined): void;
|
|
176
|
+
setTraceCollector(collector: ITraceCollector | undefined): void;
|
|
167
177
|
/**
|
|
168
178
|
* 中断 Graph 执行,等待人工确认
|
|
169
179
|
*
|