@gt-fe/eap-runtime 0.0.20 → 0.0.22
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 +4 -3
- package/dist/api/handlers/invoke.handler.d.ts +7 -0
- package/dist/api/handlers/invoke.handler.js +52 -10
- package/dist/api/handlers/stream.handler.d.ts +6 -0
- package/dist/api/handlers/stream.handler.js +60 -39
- package/dist/api/routes/agent.routes.d.ts +8 -1
- package/dist/checkpoint/file-checkpoint-saver.d.ts +4 -0
- package/dist/checkpoint/file-checkpoint-saver.js +51 -22
- package/dist/checkpoint/index.d.ts +1 -1
- package/dist/checkpoint/index.js +1 -1
- package/dist/common/types.d.ts +2 -2
- package/dist/common/types.js +1 -1
- package/dist/execution/index.d.ts +2 -0
- package/dist/execution/index.js +2 -0
- package/dist/execution/interceptors.d.ts +40 -0
- package/dist/execution/interceptors.js +23 -0
- package/dist/execution/ports.d.ts +18 -0
- package/dist/execution/ports.js +13 -0
- package/dist/governance/audit-reporter.d.ts +14 -0
- package/dist/governance/audit-reporter.js +18 -0
- package/dist/governance/index.d.ts +2 -0
- package/dist/governance/index.js +1 -0
- package/dist/governance/permission-guard.d.ts +6 -2
- package/dist/governance/permission-guard.js +38 -5
- package/dist/graph/adapters/graph-nesting.d.ts +12 -0
- package/dist/graph/adapters/graph-nesting.js +29 -0
- package/dist/graph/adapters/index.d.ts +2 -0
- package/dist/graph/adapters/index.js +2 -0
- package/dist/graph/adapters/langgraph-checkpointer.d.ts +11 -0
- package/dist/graph/adapters/langgraph-checkpointer.js +48 -9
- package/dist/graph/adapters/langgraph.adapter.d.ts +33 -11
- package/dist/graph/adapters/langgraph.adapter.js +200 -98
- package/dist/graph/adapters/skill-isolate.d.ts +13 -0
- package/dist/graph/adapters/skill-isolate.js +39 -0
- package/dist/graph/adapters/subgraph-compiler.d.ts +7 -2
- package/dist/graph/adapters/subgraph-compiler.js +63 -30
- package/dist/graph/contracts/executable-graph.d.ts +33 -5
- package/dist/graph/hitl/hitl-handler.d.ts +13 -4
- package/dist/graph/hitl/hitl-handler.js +80 -7
- package/dist/graph/nodes/index.d.ts +1 -0
- package/dist/graph/nodes/index.js +1 -0
- package/dist/graph/nodes/llm-node.d.ts +3 -1
- package/dist/graph/nodes/llm-node.js +26 -2
- package/dist/graph/nodes/rag/dedup.d.ts +20 -0
- package/dist/graph/nodes/rag/dedup.js +77 -0
- package/dist/graph/nodes/rag/denoise.d.ts +58 -0
- package/dist/graph/nodes/rag/denoise.js +220 -0
- package/dist/graph/nodes/rag/fusion/fuse.d.ts +11 -0
- package/dist/graph/nodes/rag/fusion/fuse.js +25 -0
- package/dist/graph/nodes/rag/fusion/normalize.d.ts +9 -0
- package/dist/graph/nodes/rag/fusion/normalize.js +135 -0
- package/dist/graph/nodes/rag/index.d.ts +18 -0
- package/dist/graph/nodes/rag/index.js +17 -0
- package/dist/graph/nodes/rag/prompts.d.ts +12 -0
- package/dist/graph/nodes/rag/prompts.js +40 -0
- package/dist/graph/nodes/rag/rag-node.d.ts +21 -0
- package/dist/graph/nodes/rag/rag-node.js +135 -0
- package/dist/graph/nodes/rag/rag-search-tool.d.ts +48 -0
- package/dist/graph/nodes/rag/rag-search-tool.js +230 -0
- package/dist/graph/nodes/rag/react-loop.d.ts +57 -0
- package/dist/graph/nodes/rag/react-loop.js +467 -0
- package/dist/graph/nodes/rag/retrievers/http-embedder.d.ts +29 -0
- package/dist/graph/nodes/rag/retrievers/http-embedder.js +66 -0
- package/dist/graph/nodes/rag/retrievers/in-memory-retriever.d.ts +17 -0
- package/dist/graph/nodes/rag/retrievers/in-memory-retriever.js +18 -0
- package/dist/graph/nodes/rag/retrievers/local-sqlite-retriever.d.ts +46 -0
- package/dist/graph/nodes/rag/retrievers/local-sqlite-retriever.js +85 -0
- package/dist/graph/nodes/rag/tools.d.ts +90 -0
- package/dist/graph/nodes/rag/tools.js +260 -0
- package/dist/graph/nodes/rag/types.d.ts +226 -0
- package/dist/graph/nodes/rag/types.js +9 -0
- package/dist/graph/nodes/tool-node.d.ts +19 -1
- package/dist/graph/nodes/tool-node.js +158 -33
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/memory/semantic-retrieval.service.d.ts +1 -1
- package/dist/memory/semantic-retrieval.service.js +13 -23
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.js +2 -2
- package/dist/providers/local-llm-provider.d.ts +6 -0
- package/dist/providers/local-llm-provider.js +29 -5
- package/dist/providers/routing-llm-provider.d.ts +7 -2
- package/dist/providers/routing-llm-provider.js +71 -16
- package/dist/registry/deployment-snapshot.js +18 -4
- package/dist/registry/graph-loader.d.ts +2 -0
- package/dist/registry/graph-loader.js +39 -2
- package/dist/registry/preload-flags.d.ts +1 -1
- package/dist/registry/preload-flags.js +1 -1
- package/dist/runtime/agent-runtime.d.ts +5 -0
- package/dist/runtime/agent-runtime.js +107 -70
- package/dist/runtime/dev-rag-deps.d.ts +21 -0
- package/dist/runtime/dev-rag-deps.js +186 -0
- package/dist/runtime/dev-runtime.d.ts +18 -2
- package/dist/runtime/dev-runtime.js +68 -8
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/types.d.ts +10 -9
- package/dist/session/apply-session-compression.d.ts +85 -0
- package/dist/session/apply-session-compression.js +281 -0
- package/dist/session/apply-session-memory.d.ts +21 -0
- package/dist/session/apply-session-memory.js +48 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/persist-turn-messages.d.ts +57 -1
- package/dist/session/persist-turn-messages.js +160 -5
- package/dist/sse/events/index.d.ts +13 -4
- package/dist/sse/events/index.js +9 -1
- package/dist/sse/events/rag-events.d.ts +63 -0
- package/dist/sse/events/rag-events.js +9 -0
- package/dist/sse/events/session-events.d.ts +38 -0
- package/dist/sse/events/session-events.js +4 -0
- package/dist/token/index.d.ts +2 -1
- package/dist/token/index.js +2 -1
- package/dist/token/record-task-token-usage.d.ts +25 -0
- package/dist/token/record-task-token-usage.js +44 -0
- package/dist/token/token-detail-mode.d.ts +4 -4
- package/dist/token/token-detail-mode.js +4 -4
- package/dist/token/token-detail.d.ts +8 -0
- package/dist/token/token-detail.js +22 -0
- package/dist/token/token-stats.service.d.ts +2 -2
- package/dist/token/token-stats.service.js +1 -1
- package/dist/tool/create-tool-executor.d.ts +6 -0
- package/dist/tool/create-tool-executor.js +4 -3
- package/dist/tool/harness-primitives.d.ts +9 -1
- package/dist/tool/harness-primitives.js +43 -12
- package/dist/tool/index.d.ts +2 -1
- package/dist/tool/index.js +2 -1
- package/dist/tool/tool-confirmation.js +8 -5
- package/dist/tool/tool-executor.interface.d.ts +27 -0
- package/dist/tool/tool-orchestrator.d.ts +3 -1
- package/dist/tool/tool-orchestrator.js +15 -6
- package/dist/tool/tool-retry.d.ts +15 -0
- package/dist/tool/tool-retry.js +69 -0
- package/package.json +2 -2
package/dist/common/types.js
CHANGED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { decideBeforeModel, decideBeforeTool, type AfterModelInterceptor, type AfterToolInterceptor, type BeforeModelInput, type BeforeModelInterceptor, type BeforeToolInterceptor, type ExecutionInterceptors, type ModelInterceptorDecision, type ToolInterceptorDecision, } from './interceptors.js';
|
|
2
|
+
export { sandboxFromJail, type ISandbox, type IApproval } from './ports.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行拦截:Tool / LLM 调用前可拒绝,默认无注入则与未装配时相同。
|
|
3
|
+
* 不是规格 R13 Hook,不做插件发现、不读项目 hook 目录。
|
|
4
|
+
*/
|
|
5
|
+
import type { ExecutionContext } from '@gt-fe/eap-contracts';
|
|
6
|
+
import type { GraphMessage } from '../graph/contracts/executable-graph.js';
|
|
7
|
+
import type { LLMNodeConfig, LLMResponse } from '../graph/nodes/llm-node.js';
|
|
8
|
+
import type { ToolCallRequest, ToolCallResult } from '../tool/tool-executor.interface.js';
|
|
9
|
+
export type ToolInterceptorDecision = {
|
|
10
|
+
action: 'allow';
|
|
11
|
+
} | {
|
|
12
|
+
action: 'deny';
|
|
13
|
+
reason: string;
|
|
14
|
+
} | {
|
|
15
|
+
action: 'ask';
|
|
16
|
+
};
|
|
17
|
+
export type BeforeToolInterceptor = (request: ToolCallRequest, ctx?: ExecutionContext) => Promise<ToolInterceptorDecision | void> | ToolInterceptorDecision | void;
|
|
18
|
+
export type AfterToolInterceptor = (request: ToolCallRequest, result: ToolCallResult) => Promise<void> | void;
|
|
19
|
+
export interface BeforeModelInput {
|
|
20
|
+
messages: GraphMessage[];
|
|
21
|
+
config: LLMNodeConfig;
|
|
22
|
+
nodeId: string;
|
|
23
|
+
}
|
|
24
|
+
export type ModelInterceptorDecision = {
|
|
25
|
+
action: 'allow';
|
|
26
|
+
messages?: GraphMessage[];
|
|
27
|
+
} | {
|
|
28
|
+
action: 'deny';
|
|
29
|
+
reason: string;
|
|
30
|
+
};
|
|
31
|
+
export type BeforeModelInterceptor = (input: BeforeModelInput) => Promise<ModelInterceptorDecision | void> | ModelInterceptorDecision | void;
|
|
32
|
+
export type AfterModelInterceptor = (input: BeforeModelInput, response: LLMResponse) => Promise<void> | void;
|
|
33
|
+
export interface ExecutionInterceptors {
|
|
34
|
+
beforeTool?: BeforeToolInterceptor;
|
|
35
|
+
afterTool?: AfterToolInterceptor;
|
|
36
|
+
beforeModel?: BeforeModelInterceptor;
|
|
37
|
+
afterModel?: AfterModelInterceptor;
|
|
38
|
+
}
|
|
39
|
+
export declare function decideBeforeTool(interceptor: BeforeToolInterceptor | undefined, request: ToolCallRequest, ctx?: ExecutionContext): Promise<ToolInterceptorDecision>;
|
|
40
|
+
export declare function decideBeforeModel(interceptor: BeforeModelInterceptor | undefined, input: BeforeModelInput): Promise<ModelInterceptorDecision>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行拦截:Tool / LLM 调用前可拒绝,默认无注入则与未装配时相同。
|
|
3
|
+
* 不是规格 R13 Hook,不做插件发现、不读项目 hook 目录。
|
|
4
|
+
*/
|
|
5
|
+
export async function decideBeforeTool(interceptor, request, ctx) {
|
|
6
|
+
if (!interceptor)
|
|
7
|
+
return { action: 'allow' };
|
|
8
|
+
const raw = await interceptor(request, ctx);
|
|
9
|
+
if (!raw || raw.action === 'allow')
|
|
10
|
+
return { action: 'allow' };
|
|
11
|
+
if (raw.action === 'ask')
|
|
12
|
+
return { action: 'ask' };
|
|
13
|
+
return { action: 'deny', reason: raw.reason || '拦截拒绝' };
|
|
14
|
+
}
|
|
15
|
+
export async function decideBeforeModel(interceptor, input) {
|
|
16
|
+
if (!interceptor)
|
|
17
|
+
return { action: 'allow' };
|
|
18
|
+
const raw = await interceptor(input);
|
|
19
|
+
if (!raw || raw.action === 'allow') {
|
|
20
|
+
return { action: 'allow', messages: raw && raw.action === 'allow' ? raw.messages : undefined };
|
|
21
|
+
}
|
|
22
|
+
return { action: 'deny', reason: raw.reason || '拦截拒绝' };
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行口类型:不另起引擎,也不改现有调用路径。
|
|
3
|
+
* ISandbox 描述 jail 根目录;execute_command 仍走 harness jail,未经过本接口。
|
|
4
|
+
* IApproval 即 HitlHandler;确认仍走 require_confirmation。
|
|
5
|
+
* OS 级沙箱见 TODO-skill-fs FS-2。
|
|
6
|
+
*/
|
|
7
|
+
import type { HitlHandler } from '../graph/hitl/hitl-handler.js';
|
|
8
|
+
/** 命令路径约束。默认:用户目录读写 + 技能包只读。 */
|
|
9
|
+
export interface ISandbox {
|
|
10
|
+
allowedWriteRoots: string[];
|
|
11
|
+
allowedReadRoots: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare function sandboxFromJail(jail: {
|
|
14
|
+
writeRoot: string;
|
|
15
|
+
extraRoots?: string[];
|
|
16
|
+
}): ISandbox;
|
|
17
|
+
/** 人工确认。默认实现是 HitlHandler + require_confirmation。 */
|
|
18
|
+
export type IApproval = HitlHandler;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 执行口类型:不另起引擎,也不改现有调用路径。
|
|
3
|
+
* ISandbox 描述 jail 根目录;execute_command 仍走 harness jail,未经过本接口。
|
|
4
|
+
* IApproval 即 HitlHandler;确认仍走 require_confirmation。
|
|
5
|
+
* OS 级沙箱见 TODO-skill-fs FS-2。
|
|
6
|
+
*/
|
|
7
|
+
export function sandboxFromJail(jail) {
|
|
8
|
+
const extra = jail.extraRoots ?? [];
|
|
9
|
+
return {
|
|
10
|
+
allowedWriteRoots: [jail.writeRoot],
|
|
11
|
+
allowedReadRoots: [jail.writeRoot, ...extra],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 权限拒绝审计。未注入则只抛错(A-4)。上报失败不挡执行。
|
|
3
|
+
*/
|
|
4
|
+
export interface PermissionDenialAudit {
|
|
5
|
+
kind: 'tool' | 'skill';
|
|
6
|
+
resourceId: string;
|
|
7
|
+
reason: string;
|
|
8
|
+
userId?: string | null;
|
|
9
|
+
correlationId?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface IAuditReporter {
|
|
12
|
+
recordDenial(event: PermissionDenialAudit): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare function reportPermissionDenial(reporter: IAuditReporter | undefined, event: PermissionDenialAudit): Promise<void>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 权限拒绝审计。未注入则只抛错(A-4)。上报失败不挡执行。
|
|
3
|
+
*/
|
|
4
|
+
import { emitRuntimeLog } from '../common/logger.js';
|
|
5
|
+
export async function reportPermissionDenial(reporter, event) {
|
|
6
|
+
if (!reporter)
|
|
7
|
+
return;
|
|
8
|
+
try {
|
|
9
|
+
await reporter.recordDenial(event);
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
emitRuntimeLog('warn', 'runtime.audit', 'permission denial audit failed', {
|
|
13
|
+
event: 'runtime.audit.denial_failed',
|
|
14
|
+
resourceId: event.resourceId,
|
|
15
|
+
error: err instanceof Error ? err.message : String(err),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -5,3 +5,5 @@ export type { CallerType, ExecutionContext, GovernancePermissionCheckParams, Gov
|
|
|
5
5
|
export type { IGovernanceClient } from './governance-client.js';
|
|
6
6
|
export { HttpGovernanceClient, AllowAllGovernanceClient, resolveExecutionContext, } from './governance-client.js';
|
|
7
7
|
export { assertToolPermission, assertSkillPermission, permissionMessage, PermissionDeniedError, GovernanceUnavailableError, } from './permission-guard.js';
|
|
8
|
+
export type { IAuditReporter, PermissionDenialAudit } from './audit-reporter.js';
|
|
9
|
+
export { reportPermissionDenial } from './audit-reporter.js';
|
package/dist/governance/index.js
CHANGED
|
@@ -3,3 +3,4 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { HttpGovernanceClient, AllowAllGovernanceClient, resolveExecutionContext, } from './governance-client.js';
|
|
5
5
|
export { assertToolPermission, assertSkillPermission, permissionMessage, PermissionDeniedError, GovernanceUnavailableError, } from './permission-guard.js';
|
|
6
|
+
export { reportPermissionDenial } from './audit-reporter.js';
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* 执行期 Tool 权限守卫
|
|
3
3
|
*/
|
|
4
4
|
import type { ExecutionContext, PermissionDenyReason } from '@gt-fe/eap-contracts';
|
|
5
|
-
import type
|
|
5
|
+
import { type IGovernanceClient } from './governance-client.js';
|
|
6
|
+
import { type IAuditReporter } from './audit-reporter.js';
|
|
6
7
|
export declare class GovernanceUnavailableError extends Error {
|
|
7
8
|
readonly reason: PermissionDenyReason;
|
|
8
9
|
constructor(message?: string);
|
|
@@ -15,6 +16,9 @@ export declare function permissionMessage(kind: 'tool' | 'skill', reason: Permis
|
|
|
15
16
|
export declare function assertToolPermission(client: IGovernanceClient | undefined, ctx: ExecutionContext, toolId: string, options?: {
|
|
16
17
|
dataScope?: 'user-bound' | 'department-bound' | 'global';
|
|
17
18
|
resourceOwnerId?: string;
|
|
19
|
+
auditReporter?: IAuditReporter;
|
|
18
20
|
}): Promise<void>;
|
|
19
21
|
/** Skill 激活守卫(04 R15.3) */
|
|
20
|
-
export declare function assertSkillPermission(client: IGovernanceClient | undefined, ctx: ExecutionContext, skillId: string
|
|
22
|
+
export declare function assertSkillPermission(client: IGovernanceClient | undefined, ctx: ExecutionContext, skillId: string, options?: {
|
|
23
|
+
auditReporter?: IAuditReporter;
|
|
24
|
+
}): Promise<void>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 执行期 Tool 权限守卫
|
|
3
3
|
*/
|
|
4
|
+
import { AllowAllGovernanceClient } from './governance-client.js';
|
|
5
|
+
import { reportPermissionDenial } from './audit-reporter.js';
|
|
4
6
|
export class GovernanceUnavailableError extends Error {
|
|
5
7
|
reason = 'GOVERNANCE_UNAVAILABLE';
|
|
6
8
|
constructor(message = '治理服务不可用') {
|
|
@@ -27,8 +29,17 @@ export async function assertToolPermission(client, ctx, toolId, options) {
|
|
|
27
29
|
if (!client) {
|
|
28
30
|
return;
|
|
29
31
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
// 会向治理服务校验的客户端无 token 即拒绝。AllowAll 不校验,不要求 token。
|
|
33
|
+
if (!ctx.delegationToken && !(client instanceof AllowAllGovernanceClient)) {
|
|
34
|
+
const err = new PermissionDeniedError('NO_PERMISSION', permissionMessage('tool', 'NO_PERMISSION'));
|
|
35
|
+
await reportPermissionDenial(options?.auditReporter, {
|
|
36
|
+
kind: 'tool',
|
|
37
|
+
resourceId: toolId,
|
|
38
|
+
reason: 'NO_PERMISSION',
|
|
39
|
+
userId: ctx.userId,
|
|
40
|
+
correlationId: ctx.correlationId,
|
|
41
|
+
});
|
|
42
|
+
throw err;
|
|
32
43
|
}
|
|
33
44
|
const result = await client.checkPermission(ctx, {
|
|
34
45
|
resourceType: 'tool',
|
|
@@ -42,16 +53,31 @@ export async function assertToolPermission(client, ctx, toolId, options) {
|
|
|
42
53
|
}
|
|
43
54
|
if (!result.allowed) {
|
|
44
55
|
const reason = result.reason ?? 'NO_PERMISSION';
|
|
56
|
+
await reportPermissionDenial(options?.auditReporter, {
|
|
57
|
+
kind: 'tool',
|
|
58
|
+
resourceId: toolId,
|
|
59
|
+
reason,
|
|
60
|
+
userId: ctx.userId,
|
|
61
|
+
correlationId: ctx.correlationId,
|
|
62
|
+
});
|
|
45
63
|
throw new PermissionDeniedError(reason, permissionMessage('tool', reason));
|
|
46
64
|
}
|
|
47
65
|
}
|
|
48
66
|
/** Skill 激活守卫(04 R15.3) */
|
|
49
|
-
export async function assertSkillPermission(client, ctx, skillId) {
|
|
67
|
+
export async function assertSkillPermission(client, ctx, skillId, options) {
|
|
50
68
|
if (!client || !skillId) {
|
|
51
69
|
return;
|
|
52
70
|
}
|
|
53
|
-
if (!ctx.delegationToken &&
|
|
54
|
-
|
|
71
|
+
if (!ctx.delegationToken && !(client instanceof AllowAllGovernanceClient)) {
|
|
72
|
+
const err = new PermissionDeniedError('NO_PERMISSION', permissionMessage('skill', 'NO_PERMISSION'));
|
|
73
|
+
await reportPermissionDenial(options?.auditReporter, {
|
|
74
|
+
kind: 'skill',
|
|
75
|
+
resourceId: skillId,
|
|
76
|
+
reason: 'NO_PERMISSION',
|
|
77
|
+
userId: ctx.userId,
|
|
78
|
+
correlationId: ctx.correlationId,
|
|
79
|
+
});
|
|
80
|
+
throw err;
|
|
55
81
|
}
|
|
56
82
|
const result = await client.checkPermission(ctx, {
|
|
57
83
|
resourceType: 'skill',
|
|
@@ -63,6 +89,13 @@ export async function assertSkillPermission(client, ctx, skillId) {
|
|
|
63
89
|
}
|
|
64
90
|
if (!result.allowed) {
|
|
65
91
|
const reason = result.reason ?? 'NO_PERMISSION';
|
|
92
|
+
await reportPermissionDenial(options?.auditReporter, {
|
|
93
|
+
kind: 'skill',
|
|
94
|
+
resourceId: skillId,
|
|
95
|
+
reason,
|
|
96
|
+
userId: ctx.userId,
|
|
97
|
+
correlationId: ctx.correlationId,
|
|
98
|
+
});
|
|
66
99
|
throw new PermissionDeniedError(reason, permissionMessage('skill', reason));
|
|
67
100
|
}
|
|
68
101
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill 子图只允许一层:Agent 图可以有 type=subgraph,子图 DSL 不能再有。
|
|
3
|
+
* Checkpoint / HITL / Span 未按嵌套设计(B14)。
|
|
4
|
+
*/
|
|
5
|
+
import type { GraphDSL, GraphNode } from '../contracts/executable-graph.js';
|
|
6
|
+
export declare function subgraphNodesOf(dsl: GraphDSL | undefined): GraphNode[];
|
|
7
|
+
/**
|
|
8
|
+
* Skill 子图(compileSubGraph 入参、父节点内嵌 subgraphDsl、运行时 workflow)不得含 type=subgraph。
|
|
9
|
+
*/
|
|
10
|
+
export declare function assertSkillGraphHasNoSubgraphNodes(dsl: GraphDSL, label?: string): void;
|
|
11
|
+
/** 父图 buildGraph:内嵌 subgraphDsl 在编译父图时就拒绝,不等第一次 invoke。 */
|
|
12
|
+
export declare function assertEmbeddedSubgraphsAreFlat(dsl: GraphDSL): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill 子图只允许一层:Agent 图可以有 type=subgraph,子图 DSL 不能再有。
|
|
3
|
+
* Checkpoint / HITL / Span 未按嵌套设计(B14)。
|
|
4
|
+
*/
|
|
5
|
+
import { AppError, ErrorCodes } from '@gt-fe/eap-contracts';
|
|
6
|
+
export function subgraphNodesOf(dsl) {
|
|
7
|
+
return (dsl?.nodes ?? []).filter((node) => node.type === 'subgraph');
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Skill 子图(compileSubGraph 入参、父节点内嵌 subgraphDsl、运行时 workflow)不得含 type=subgraph。
|
|
11
|
+
*/
|
|
12
|
+
export function assertSkillGraphHasNoSubgraphNodes(dsl, label) {
|
|
13
|
+
const nested = subgraphNodesOf(dsl);
|
|
14
|
+
if (nested.length === 0)
|
|
15
|
+
return;
|
|
16
|
+
const where = label ?? dsl.name ?? 'unnamed';
|
|
17
|
+
throw new AppError(ErrorCodes.GRAPH_BUILD_FAILED, `Skill 子图不允许再包含 type=subgraph(不支持子图套子图): ${where} → ${nested.map((n) => n.id).join(', ')}`, { exposeMessage: true });
|
|
18
|
+
}
|
|
19
|
+
/** 父图 buildGraph:内嵌 subgraphDsl 在编译父图时就拒绝,不等第一次 invoke。 */
|
|
20
|
+
export function assertEmbeddedSubgraphsAreFlat(dsl) {
|
|
21
|
+
for (const node of dsl.nodes ?? []) {
|
|
22
|
+
if (node.type !== 'subgraph')
|
|
23
|
+
continue;
|
|
24
|
+
const nested = node.config?.['subgraphDsl'];
|
|
25
|
+
if (nested?.nodes?.length) {
|
|
26
|
+
assertSkillGraphHasNoSubgraphNodes(nested, node.id);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { LangGraphAdapter } from './langgraph.adapter.js';
|
|
2
2
|
export { LangGraphCheckpointer } from './langgraph-checkpointer.js';
|
|
3
3
|
export { SubGraphCompiler } from './subgraph-compiler.js';
|
|
4
|
+
export { assertSkillGraphHasNoSubgraphNodes, assertEmbeddedSubgraphsAreFlat, subgraphNodesOf, } from './graph-nesting.js';
|
|
5
|
+
export { isSkillContextIsolated, isolatedSkillMessages, isolatedSkillOutput, } from './skill-isolate.js';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { LangGraphAdapter } from './langgraph.adapter.js';
|
|
2
2
|
export { LangGraphCheckpointer } from './langgraph-checkpointer.js';
|
|
3
3
|
export { SubGraphCompiler } from './subgraph-compiler.js';
|
|
4
|
+
export { assertSkillGraphHasNoSubgraphNodes, assertEmbeddedSubgraphsAreFlat, subgraphNodesOf, } from './graph-nesting.js';
|
|
5
|
+
export { isSkillContextIsolated, isolatedSkillMessages, isolatedSkillOutput, } from './skill-isolate.js';
|
|
@@ -29,6 +29,7 @@ export declare class LangGraphCheckpointer extends BaseCheckpointSaver {
|
|
|
29
29
|
putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;
|
|
30
30
|
list(config: RunnableConfig, options?: ListOptions): AsyncIterableIterator<CheckpointTuple>;
|
|
31
31
|
private extractThreadId;
|
|
32
|
+
private extractCheckpointNs;
|
|
32
33
|
private checkpointDataToTuple;
|
|
33
34
|
/**
|
|
34
35
|
* 优先还原 put() 时写入的 engineSnapshot;旧数据兜底补齐 pending_sends=[]。
|
|
@@ -37,4 +38,14 @@ export declare class LangGraphCheckpointer extends BaseCheckpointSaver {
|
|
|
37
38
|
private checkpointToData;
|
|
38
39
|
private extractExtraState;
|
|
39
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* ICheckpointSaver 仍按一个 threadId 分槽。
|
|
43
|
+
* 父图 ns 为空,槽位=threadId(HITL/多轮仍等于 sessionId)。
|
|
44
|
+
* 子图 ns 非空,槽位=threadId#ns,不覆盖父 latest。
|
|
45
|
+
*/
|
|
46
|
+
export declare function checkpointStorageThreadId(threadId: string, checkpointNs?: string): string;
|
|
47
|
+
/** 在上一份路径后追加当前节点 id;空 id 或与末项相同则不追加。 */
|
|
48
|
+
export declare function nextHistoryPath(previous: string[] | undefined, currentNodeId: string): string[];
|
|
49
|
+
/** 优先用图状态 channel;旧 checkpoint 没有该 channel 时退回按 currentNode 追加。 */
|
|
50
|
+
export declare function historyPathFromChannel(channel: unknown, previousHistoryPath: string[] | undefined, currentNodeId: string): string[];
|
|
40
51
|
export {};
|
|
@@ -33,7 +33,7 @@ export class LangGraphCheckpointer extends BaseCheckpointSaver {
|
|
|
33
33
|
data = await this.saver.loadByCheckpointId(checkpointId);
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
|
-
data = await this.saver.load(threadId);
|
|
36
|
+
data = await this.saver.load(checkpointStorageThreadId(threadId, this.extractCheckpointNs(config)));
|
|
37
37
|
}
|
|
38
38
|
if (!data) {
|
|
39
39
|
return undefined;
|
|
@@ -42,8 +42,10 @@ export class LangGraphCheckpointer extends BaseCheckpointSaver {
|
|
|
42
42
|
}
|
|
43
43
|
async put(config, checkpoint, metadata, _newVersions) {
|
|
44
44
|
const threadId = this.extractThreadId(config) ?? `thread-${Date.now()}`;
|
|
45
|
-
const
|
|
46
|
-
await this.saver.
|
|
45
|
+
const storageThreadId = checkpointStorageThreadId(threadId, this.extractCheckpointNs(config));
|
|
46
|
+
const previous = await this.saver.load(storageThreadId);
|
|
47
|
+
const data = this.checkpointToData(storageThreadId, checkpoint, previous?.historyPath);
|
|
48
|
+
await this.saver.save(storageThreadId, stripCheckpointCollectors(data));
|
|
47
49
|
await notifyCheckpointRef(data);
|
|
48
50
|
return {
|
|
49
51
|
...config,
|
|
@@ -80,7 +82,7 @@ export class LangGraphCheckpointer extends BaseCheckpointSaver {
|
|
|
80
82
|
if (!threadId) {
|
|
81
83
|
return;
|
|
82
84
|
}
|
|
83
|
-
let checkpoints = await this.saver.list(threadId);
|
|
85
|
+
let checkpoints = await this.saver.list(checkpointStorageThreadId(threadId, this.extractCheckpointNs(config)));
|
|
84
86
|
checkpoints.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
85
87
|
if (options?.before) {
|
|
86
88
|
const beforeId = options.before.configurable?.['checkpoint_id'];
|
|
@@ -101,6 +103,9 @@ export class LangGraphCheckpointer extends BaseCheckpointSaver {
|
|
|
101
103
|
extractThreadId(config) {
|
|
102
104
|
return config.configurable?.['thread_id'];
|
|
103
105
|
}
|
|
106
|
+
extractCheckpointNs(config) {
|
|
107
|
+
return String(config.configurable?.['checkpoint_ns'] ?? '').trim();
|
|
108
|
+
}
|
|
104
109
|
checkpointDataToTuple(data, config) {
|
|
105
110
|
const checkpoint = this.restoreLangGraphCheckpoint(data);
|
|
106
111
|
const metadata = {
|
|
@@ -112,7 +117,7 @@ export class LangGraphCheckpointer extends BaseCheckpointSaver {
|
|
|
112
117
|
...config,
|
|
113
118
|
configurable: {
|
|
114
119
|
...config.configurable,
|
|
115
|
-
thread_id: data.threadId,
|
|
120
|
+
thread_id: this.extractThreadId(config) ?? data.threadId,
|
|
116
121
|
checkpoint_id: data.checkpointId,
|
|
117
122
|
},
|
|
118
123
|
};
|
|
@@ -162,20 +167,20 @@ export class LangGraphCheckpointer extends BaseCheckpointSaver {
|
|
|
162
167
|
channel_values: {
|
|
163
168
|
messages: data.state.messages ?? [],
|
|
164
169
|
currentNode: data.currentNodeId,
|
|
170
|
+
historyPath: data.historyPath ?? [],
|
|
165
171
|
...this.extractExtraState(data),
|
|
166
172
|
},
|
|
167
|
-
channel_versions: { messages: 1, currentNode: 1 },
|
|
173
|
+
channel_versions: { messages: 1, currentNode: 1, historyPath: 1 },
|
|
168
174
|
versions_seen: {},
|
|
169
175
|
pending_sends: [],
|
|
170
176
|
};
|
|
171
177
|
}
|
|
172
|
-
checkpointToData(threadId, checkpoint,
|
|
178
|
+
checkpointToData(threadId, checkpoint, previousHistoryPath) {
|
|
173
179
|
const messages = Array.isArray(checkpoint.channel_values['messages'])
|
|
174
180
|
? checkpoint.channel_values['messages']
|
|
175
181
|
: [];
|
|
176
182
|
const currentNodeId = checkpoint.channel_values['currentNode'] ?? '';
|
|
177
|
-
const
|
|
178
|
-
const historyPath = Array.from({ length: step }, () => 'node');
|
|
183
|
+
const historyPath = historyPathFromChannel(checkpoint.channel_values['historyPath'], previousHistoryPath, currentNodeId);
|
|
179
184
|
const raw = checkpoint;
|
|
180
185
|
return {
|
|
181
186
|
checkpointId: checkpoint.id,
|
|
@@ -211,3 +216,37 @@ export class LangGraphCheckpointer extends BaseCheckpointSaver {
|
|
|
211
216
|
return rest;
|
|
212
217
|
}
|
|
213
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* ICheckpointSaver 仍按一个 threadId 分槽。
|
|
221
|
+
* 父图 ns 为空,槽位=threadId(HITL/多轮仍等于 sessionId)。
|
|
222
|
+
* 子图 ns 非空,槽位=threadId#ns,不覆盖父 latest。
|
|
223
|
+
*/
|
|
224
|
+
export function checkpointStorageThreadId(threadId, checkpointNs) {
|
|
225
|
+
const ns = (checkpointNs ?? '').trim().replace(/[/\\]/g, '_');
|
|
226
|
+
if (!ns)
|
|
227
|
+
return threadId;
|
|
228
|
+
return `${threadId}#${ns}`;
|
|
229
|
+
}
|
|
230
|
+
/** 在上一份路径后追加当前节点 id;空 id 或与末项相同则不追加。 */
|
|
231
|
+
export function nextHistoryPath(previous, currentNodeId) {
|
|
232
|
+
const path = Array.isArray(previous) ? [...previous] : [];
|
|
233
|
+
const id = currentNodeId.trim();
|
|
234
|
+
if (!id)
|
|
235
|
+
return path;
|
|
236
|
+
if (path[path.length - 1] === id)
|
|
237
|
+
return path;
|
|
238
|
+
path.push(id);
|
|
239
|
+
if (path.length > 1000)
|
|
240
|
+
return path.slice(path.length - 1000);
|
|
241
|
+
return path;
|
|
242
|
+
}
|
|
243
|
+
/** 优先用图状态 channel;旧 checkpoint 没有该 channel 时退回按 currentNode 追加。 */
|
|
244
|
+
export function historyPathFromChannel(channel, previousHistoryPath, currentNodeId) {
|
|
245
|
+
if (Array.isArray(channel) && channel.length > 0) {
|
|
246
|
+
const path = channel.filter((id) => typeof id === 'string' && id.trim().length > 0);
|
|
247
|
+
if (path.length > 0) {
|
|
248
|
+
return path.length > 1000 ? path.slice(path.length - 1000) : path;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return nextHistoryPath(previousHistoryPath, currentNodeId);
|
|
252
|
+
}
|
|
@@ -12,11 +12,19 @@ import type { ExecutableGraph, GraphDSL, GraphState, RuntimeEvent, CheckpointDat
|
|
|
12
12
|
import type { ICheckpointSaver } from '../../checkpoint/contracts/checkpoint-saver.contract.js';
|
|
13
13
|
import type { HitlHandler } from '../hitl/hitl-handler.js';
|
|
14
14
|
import { type ILLMProvider } from '../nodes/llm-node.js';
|
|
15
|
+
import { type RagNodeDeps } from '../nodes/rag/index.js';
|
|
15
16
|
import type { IToolExecutor } from '../../tool/tool-executor.interface.js';
|
|
16
17
|
import type { IGovernanceClient } from '../../governance/governance-client.js';
|
|
17
18
|
import { type ConditionEvaluatorFn } from '../condition-evaluator.js';
|
|
19
|
+
import type { ExecutionInterceptors } from '../../execution/interceptors.js';
|
|
18
20
|
/** 图未写 `config.maxIterations` 时的 recursionLimit。`agent ⇄ tools` 一圈约 2,50 ≈ 24 次工具往返。 */
|
|
19
21
|
export declare const DEFAULT_GRAPH_RECURSION_LIMIT = 50;
|
|
22
|
+
/**
|
|
23
|
+
* LangGraph `recursionLimit` 计 superstep(每进一个节点 +1),不是 ReAct 圈数。
|
|
24
|
+
* 图上 `config.maxIterations` 优先;缺省 {@link DEFAULT_GRAPH_RECURSION_LIMIT}。
|
|
25
|
+
*/
|
|
26
|
+
/** 并行节点各自返回 [id] 时合并进同一路径;末项相同不重复。 */
|
|
27
|
+
export declare function mergeHistoryPath(current: unknown, update: unknown): string[];
|
|
20
28
|
/**
|
|
21
29
|
* LangGraphAdapter — 基于 @langchain/langgraph 的 IStateGraphEngine 实现
|
|
22
30
|
*
|
|
@@ -26,6 +34,7 @@ export declare const DEFAULT_GRAPH_RECURSION_LIMIT = 50;
|
|
|
26
34
|
* 3. 提供 Checkpoint 提取与恢复能力
|
|
27
35
|
*/
|
|
28
36
|
export declare class LangGraphAdapter implements IStateGraphEngine {
|
|
37
|
+
private readonly auditReporter?;
|
|
29
38
|
private readonly checkpointer;
|
|
30
39
|
private readonly subGraphCompiler;
|
|
31
40
|
private readonly llmProvider;
|
|
@@ -42,11 +51,17 @@ export declare class LangGraphAdapter implements IStateGraphEngine {
|
|
|
42
51
|
*/
|
|
43
52
|
private readonly governanceClient?;
|
|
44
53
|
private hitlHandler?;
|
|
45
|
-
|
|
54
|
+
private readonly ragNodeDeps?;
|
|
55
|
+
private readonly interceptors?;
|
|
56
|
+
/** 开发 overlay 根;createDevRuntime 在恢复 process.env 之前钉死 */
|
|
57
|
+
private overlayExtraRoots;
|
|
58
|
+
constructor(checkpointSaver?: ICheckpointSaver, llmProvider?: ILLMProvider, toolExecutor?: IToolExecutor, governanceClient?: IGovernanceClient, hitlHandler?: HitlHandler, ragNodeDeps?: RagNodeDeps, interceptors?: ExecutionInterceptors, auditReporter?: import("../../governance/audit-reporter.js").IAuditReporter | undefined);
|
|
46
59
|
/**
|
|
47
60
|
* 注册自定义条件评估函数
|
|
48
61
|
*/
|
|
49
62
|
registerConditionEvaluator(name: string, fn: ConditionEvaluatorFn): void;
|
|
63
|
+
/** SDK 恢复 env 之后仍要把 overlay 算进 LLM jail 根,不能再读 process.env。 */
|
|
64
|
+
bindOverlayExtraRoots(roots: string[] | undefined): void;
|
|
50
65
|
/**
|
|
51
66
|
* 节点旁路推送 RuntimeEvent(子图 Skill 生命周期、Tool 执行)。
|
|
52
67
|
* 无本次执行流式袋时为空操作(纯 invoke)。
|
|
@@ -103,29 +118,25 @@ export declare class LangGraphAdapter implements IStateGraphEngine {
|
|
|
103
118
|
* 本方法无 DSL,无法编译出可用 `_engineInstance`。
|
|
104
119
|
*/
|
|
105
120
|
restore(checkpointData: CheckpointData): Promise<ExecutableGraph>;
|
|
121
|
+
private checkpointConfigurable;
|
|
106
122
|
/**
|
|
107
123
|
* 根据节点类型创建对应的执行函数
|
|
108
124
|
*/
|
|
109
125
|
private createNodeFunction;
|
|
126
|
+
private createNodeFunctionBody;
|
|
110
127
|
private createSkillBindNodeFunction;
|
|
111
128
|
private buildSkillCatalog;
|
|
112
129
|
private createToolNodeFunction;
|
|
113
130
|
private createLLMNodeFunction;
|
|
131
|
+
/**
|
|
132
|
+
* RAG 节点:编译期 fail-fast(deps 缺失),请求级状态经 execute(state) 流转
|
|
133
|
+
*/
|
|
134
|
+
private createRagNodeFunction;
|
|
114
135
|
/**
|
|
115
136
|
* 子图节点:优先 config.subgraphDsl;否则按 skillId 从 state.skillDefinitions.workflow 解析。
|
|
116
137
|
* 技能正文用短目录 + read_file,不灌 system。
|
|
117
138
|
*/
|
|
118
139
|
private createSubGraphNodeFunction;
|
|
119
|
-
/**
|
|
120
|
-
* 并行节点:并行执行多个子任务分支
|
|
121
|
-
*
|
|
122
|
-
* 支持两种并行模式:
|
|
123
|
-
* 1. branches 模式:node.branches 列出要并行执行的节点 ID,每个节点的执行函数由 DSL 定义
|
|
124
|
-
* 2. tasks 模式:node.config.tasks 列出要并行执行的异步任务函数
|
|
125
|
-
*
|
|
126
|
-
* 执行结果合并到 state.__parallelResults 中。
|
|
127
|
-
*/
|
|
128
|
-
private createParallelNodeFunction;
|
|
129
140
|
/**
|
|
130
141
|
* 代码执行节点:在受限沙箱中执行用户代码
|
|
131
142
|
*
|
|
@@ -173,6 +184,17 @@ export declare class LangGraphAdapter implements IStateGraphEngine {
|
|
|
173
184
|
* 否则多轮回放时 DeepSeek thinking 会因缺少 reasoning_content 而 400,
|
|
174
185
|
* tool 消息也会丢掉 tool_call_id。
|
|
175
186
|
*/
|
|
187
|
+
/**
|
|
188
|
+
* 工具形态检索面白名单(图声明即真相):tool 节点 config.retrievalSources 声明的
|
|
189
|
+
* 数据源目录 → 本图工具白名单(声明源解析出的工具 + rag_search)。未声明的检索类
|
|
190
|
+
* 工具对主 LLM 工具表与执行链整体不可见/不可用;图未声明 = 不收敓名单(通用形态)。
|
|
191
|
+
*
|
|
192
|
+
* 约束——单检索面假设:find 只取全图首个带 retrievalSources 的 tool 节点、按它过滤
|
|
193
|
+
* 全图 loadedTools。多 tool 节点各声明检索面属未支持场景:第二面的工具会被当成
|
|
194
|
+
* 未声明而过滤,无法各自收敛;如需支持须改为 per-node 收敛(在节点边界分别过滤
|
|
195
|
+
* loadedTools,而非此处全图一次性收敛)。
|
|
196
|
+
*/
|
|
197
|
+
private applyDeclaredSearchFace;
|
|
176
198
|
private graphStateToLangGraphInput;
|
|
177
199
|
/**
|
|
178
200
|
* 将 LangGraph updates 模式输出转为平台 RuntimeEvent 序列
|