@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
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本地 LLM 回合落盘:每次模型调用写一份 JSON(入参、响应、URL、脱敏密钥、system 段)。
|
|
3
|
+
* 默认 `.eap/llm-turns/{sessionId}/`。EAP_LLM_TURN_LOG=0 关闭。
|
|
4
|
+
*/
|
|
5
|
+
import { mkdir, readdir, readFile, appendFile, writeFile } from 'node:fs/promises';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
export function finalizeLlmTurn(turn) {
|
|
8
|
+
const ts = turn.ts ?? new Date().toISOString();
|
|
9
|
+
const id = turn.id ?? `${ts.replace(/[:.]/g, '-')}-${turn.nodeId ?? 'llm'}`;
|
|
10
|
+
return { ...turn, id, ts };
|
|
11
|
+
}
|
|
12
|
+
export function toLlmTurnIndexEntry(record, file) {
|
|
13
|
+
return {
|
|
14
|
+
id: record.id,
|
|
15
|
+
ts: record.ts,
|
|
16
|
+
sessionId: record.sessionId,
|
|
17
|
+
taskId: record.taskId,
|
|
18
|
+
nodeId: record.nodeId,
|
|
19
|
+
model: record.model,
|
|
20
|
+
url: record.url,
|
|
21
|
+
file: file ?? `${sanitize(record.id)}.json`,
|
|
22
|
+
inputTokens: record.response.inputTokens,
|
|
23
|
+
outputTokens: record.response.outputTokens,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function maskApiKey(key) {
|
|
27
|
+
if (!key)
|
|
28
|
+
return '';
|
|
29
|
+
if (key.length <= 6)
|
|
30
|
+
return '***';
|
|
31
|
+
// 例:sk-proj-… → sk-proj***;sk-abc123xyz → sk-abc1***
|
|
32
|
+
return `${key.slice(0, 7)}***`;
|
|
33
|
+
}
|
|
34
|
+
export function resolveLlmTurnDir(env = process.env, cwd = process.cwd()) {
|
|
35
|
+
return env.EAP_LLM_TURN_DIR?.trim() || path.join(cwd, '.eap', 'llm-turns');
|
|
36
|
+
}
|
|
37
|
+
export function createFileLlmTurnLogFromEnv(env = process.env) {
|
|
38
|
+
if (env.EAP_LLM_TURN_LOG === '0')
|
|
39
|
+
return undefined;
|
|
40
|
+
return new FileLlmTurnLog(resolveLlmTurnDir(env));
|
|
41
|
+
}
|
|
42
|
+
export class InMemoryLlmTurnLog {
|
|
43
|
+
kind = 'memory';
|
|
44
|
+
baseDir = 'memory://llm-turns';
|
|
45
|
+
bySession = new Map();
|
|
46
|
+
async record(turn) {
|
|
47
|
+
const record = finalizeLlmTurn(turn);
|
|
48
|
+
const sessionId = record.sessionId ?? 'no-session';
|
|
49
|
+
let bucket = this.bySession.get(sessionId);
|
|
50
|
+
if (!bucket) {
|
|
51
|
+
bucket = { records: new Map(), index: [] };
|
|
52
|
+
this.bySession.set(sessionId, bucket);
|
|
53
|
+
}
|
|
54
|
+
bucket.records.set(record.id, record);
|
|
55
|
+
bucket.index.push(toLlmTurnIndexEntry(record));
|
|
56
|
+
return record;
|
|
57
|
+
}
|
|
58
|
+
async list(sessionId) {
|
|
59
|
+
return [...(this.bySession.get(sessionId)?.index ?? [])];
|
|
60
|
+
}
|
|
61
|
+
async read(sessionId, turnId) {
|
|
62
|
+
return this.bySession.get(sessionId)?.records.get(turnId) ?? null;
|
|
63
|
+
}
|
|
64
|
+
async listSessions() {
|
|
65
|
+
return [...this.bySession.keys()].sort();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export class FileLlmTurnLog {
|
|
69
|
+
baseDir;
|
|
70
|
+
kind = 'file';
|
|
71
|
+
constructor(baseDir) {
|
|
72
|
+
this.baseDir = baseDir;
|
|
73
|
+
}
|
|
74
|
+
async record(turn) {
|
|
75
|
+
const record = finalizeLlmTurn(turn);
|
|
76
|
+
const sessionId = sanitize(record.sessionId ?? 'no-session');
|
|
77
|
+
const dir = path.join(this.baseDir, sessionId);
|
|
78
|
+
await mkdir(dir, { recursive: true });
|
|
79
|
+
const file = `${sanitize(record.id)}.json`;
|
|
80
|
+
const filePath = path.join(dir, file);
|
|
81
|
+
await writeFile(filePath, JSON.stringify(record, null, 2), 'utf8');
|
|
82
|
+
await appendFile(path.join(dir, 'index.jsonl'), `${JSON.stringify(toLlmTurnIndexEntry(record, file))}\n`, 'utf8');
|
|
83
|
+
return record;
|
|
84
|
+
}
|
|
85
|
+
async list(sessionId) {
|
|
86
|
+
const file = path.join(this.baseDir, sanitize(sessionId), 'index.jsonl');
|
|
87
|
+
try {
|
|
88
|
+
const raw = await readFile(file, 'utf8');
|
|
89
|
+
return raw
|
|
90
|
+
.split('\n')
|
|
91
|
+
.filter((line) => line.trim())
|
|
92
|
+
.map((line) => JSON.parse(line));
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async read(sessionId, turnId) {
|
|
99
|
+
const dir = path.join(this.baseDir, sanitize(sessionId));
|
|
100
|
+
const direct = path.join(dir, `${sanitize(turnId)}.json`);
|
|
101
|
+
try {
|
|
102
|
+
return JSON.parse(await readFile(direct, 'utf8'));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// fall through
|
|
106
|
+
}
|
|
107
|
+
const entries = await this.list(sessionId);
|
|
108
|
+
const hit = entries.find((e) => e.id === turnId || e.file === turnId);
|
|
109
|
+
if (!hit)
|
|
110
|
+
return null;
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(await readFile(path.join(dir, hit.file), 'utf8'));
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async listSessions() {
|
|
119
|
+
try {
|
|
120
|
+
const names = await readdir(this.baseDir);
|
|
121
|
+
return names.sort();
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function sanitize(value) {
|
|
129
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, '_').slice(0, 180);
|
|
130
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM 回合 Postgres 存储。与 FileLlmTurnLog / InMemoryLlmTurnLog 共用 ILlmTurnLog。
|
|
3
|
+
*
|
|
4
|
+
* 表结构见 runtime/docs/ddl.md(`llm_turns`)。启动时仍可 CREATE IF NOT EXISTS。
|
|
5
|
+
*/
|
|
6
|
+
import type { IPgPool } from '../session/pg-message.repository.js';
|
|
7
|
+
import { type ILlmTurnLog, type LlmTurnIndexEntry, type LlmTurnRecord } from './llm-turn-log.js';
|
|
8
|
+
export declare class PostgresLlmTurnLog implements ILlmTurnLog {
|
|
9
|
+
private readonly pool;
|
|
10
|
+
readonly kind: "postgres";
|
|
11
|
+
readonly baseDir = "postgres://llm_turns";
|
|
12
|
+
private ready;
|
|
13
|
+
constructor(pool: IPgPool);
|
|
14
|
+
private ensureTable;
|
|
15
|
+
record(turn: Omit<LlmTurnRecord, 'id' | 'ts'> & {
|
|
16
|
+
id?: string;
|
|
17
|
+
ts?: string;
|
|
18
|
+
}): Promise<LlmTurnRecord>;
|
|
19
|
+
list(sessionId: string): Promise<LlmTurnIndexEntry[]>;
|
|
20
|
+
read(sessionId: string, turnId: string): Promise<LlmTurnRecord | null>;
|
|
21
|
+
listSessions(): Promise<string[]>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM 回合 Postgres 存储。与 FileLlmTurnLog / InMemoryLlmTurnLog 共用 ILlmTurnLog。
|
|
3
|
+
*
|
|
4
|
+
* 表结构见 runtime/docs/ddl.md(`llm_turns`)。启动时仍可 CREATE IF NOT EXISTS。
|
|
5
|
+
*/
|
|
6
|
+
import { finalizeLlmTurn, toLlmTurnIndexEntry, } from './llm-turn-log.js';
|
|
7
|
+
const CREATE_TABLE_SQL = `
|
|
8
|
+
CREATE TABLE IF NOT EXISTS llm_turns (
|
|
9
|
+
id VARCHAR(180) PRIMARY KEY,
|
|
10
|
+
session_id VARCHAR(64) NOT NULL,
|
|
11
|
+
ts TIMESTAMPTZ NOT NULL,
|
|
12
|
+
payload JSONB NOT NULL
|
|
13
|
+
)`;
|
|
14
|
+
const CREATE_INDEX_SQL = 'CREATE INDEX IF NOT EXISTS idx_llm_turns_session_ts ON llm_turns (session_id, ts)';
|
|
15
|
+
export class PostgresLlmTurnLog {
|
|
16
|
+
pool;
|
|
17
|
+
kind = 'postgres';
|
|
18
|
+
baseDir = 'postgres://llm_turns';
|
|
19
|
+
ready;
|
|
20
|
+
constructor(pool) {
|
|
21
|
+
this.pool = pool;
|
|
22
|
+
}
|
|
23
|
+
ensureTable() {
|
|
24
|
+
this.ready ??= this.pool
|
|
25
|
+
.query(CREATE_TABLE_SQL)
|
|
26
|
+
.then(() => this.pool.query(CREATE_INDEX_SQL))
|
|
27
|
+
.then(() => undefined);
|
|
28
|
+
return this.ready;
|
|
29
|
+
}
|
|
30
|
+
async record(turn) {
|
|
31
|
+
await this.ensureTable();
|
|
32
|
+
const record = finalizeLlmTurn(turn);
|
|
33
|
+
await this.pool.query(`INSERT INTO llm_turns (id, session_id, ts, payload)
|
|
34
|
+
VALUES ($1, $2, $3, $4)
|
|
35
|
+
ON CONFLICT (id) DO UPDATE SET payload = EXCLUDED.payload, ts = EXCLUDED.ts, session_id = EXCLUDED.session_id`, [record.id, record.sessionId ?? 'no-session', record.ts, JSON.stringify(record)]);
|
|
36
|
+
return record;
|
|
37
|
+
}
|
|
38
|
+
async list(sessionId) {
|
|
39
|
+
await this.ensureTable();
|
|
40
|
+
const { rows } = await this.pool.query(`SELECT payload FROM llm_turns WHERE session_id = $1 ORDER BY ts ASC`, [sessionId]);
|
|
41
|
+
return rows.map((row) => toLlmTurnIndexEntry(parsePayload(row.payload)));
|
|
42
|
+
}
|
|
43
|
+
async read(sessionId, turnId) {
|
|
44
|
+
await this.ensureTable();
|
|
45
|
+
const { rows } = await this.pool.query(`SELECT payload FROM llm_turns WHERE session_id = $1 AND id = $2`, [sessionId, turnId]);
|
|
46
|
+
const row = rows[0];
|
|
47
|
+
return row ? parsePayload(row.payload) : null;
|
|
48
|
+
}
|
|
49
|
+
async listSessions() {
|
|
50
|
+
await this.ensureTable();
|
|
51
|
+
const { rows } = await this.pool.query(`SELECT DISTINCT session_id FROM llm_turns ORDER BY session_id`);
|
|
52
|
+
return rows.map((row) => row.session_id);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function parsePayload(payload) {
|
|
56
|
+
return typeof payload === 'string' ? JSON.parse(payload) : payload;
|
|
57
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 治理服务 HTTP 客户端(06-governance validateToken / checkPermission)
|
|
3
3
|
*/
|
|
4
|
-
import type { ExecutionContext, GovernancePermissionCheckParams, GovernancePermissionCheckResult } from '@
|
|
4
|
+
import type { ExecutionContext, GovernancePermissionCheckParams, GovernancePermissionCheckResult } from '@eap/contracts';
|
|
5
5
|
export interface HttpGovernanceClientOptions {
|
|
6
6
|
baseUrl: string;
|
|
7
7
|
timeout?: number;
|
|
@@ -40,25 +40,29 @@ export class HttpGovernanceClient {
|
|
|
40
40
|
if (response.status === 503 || response.status >= 500) {
|
|
41
41
|
return { allowed: false, reason: 'GOVERNANCE_UNAVAILABLE' };
|
|
42
42
|
}
|
|
43
|
+
if (response.status === 403) {
|
|
44
|
+
return { allowed: false, reason: await readDenyReason(response, 'NO_PERMISSION') };
|
|
45
|
+
}
|
|
43
46
|
if (!response.ok) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
return { allowed: false, reason: 'NO_PERMISSION' };
|
|
47
|
+
// 401/404/400 等是 Runtime→Foundation hop 失败,不是 C2「没工具权」
|
|
48
|
+
return { allowed: false, reason: 'GOVERNANCE_UNAVAILABLE' };
|
|
49
|
+
}
|
|
50
|
+
let body;
|
|
51
|
+
try {
|
|
52
|
+
body = (await response.json());
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return { allowed: false, reason: 'GOVERNANCE_UNAVAILABLE' };
|
|
55
56
|
}
|
|
56
|
-
const body = (await response.json());
|
|
57
57
|
const data = body.data ?? body;
|
|
58
|
-
|
|
59
|
-
allowed:
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
if (data.allowed === true) {
|
|
59
|
+
return { allowed: true };
|
|
60
|
+
}
|
|
61
|
+
const reason = asDenyReason(data.reason);
|
|
62
|
+
if (data.allowed === false && reason && reason !== 'GOVERNANCE_UNAVAILABLE') {
|
|
63
|
+
return { allowed: false, reason };
|
|
64
|
+
}
|
|
65
|
+
return { allowed: false, reason: 'GOVERNANCE_UNAVAILABLE' };
|
|
62
66
|
}
|
|
63
67
|
catch {
|
|
64
68
|
clearTimeout(timeoutId);
|
|
@@ -66,6 +70,26 @@ export class HttpGovernanceClient {
|
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
72
|
}
|
|
73
|
+
const DENY_REASONS = new Set([
|
|
74
|
+
'NO_PERMISSION',
|
|
75
|
+
'DATA_SCOPE_DENIED',
|
|
76
|
+
'GOVERNANCE_UNAVAILABLE',
|
|
77
|
+
]);
|
|
78
|
+
function asDenyReason(value) {
|
|
79
|
+
return typeof value === 'string' && DENY_REASONS.has(value)
|
|
80
|
+
? value
|
|
81
|
+
: undefined;
|
|
82
|
+
}
|
|
83
|
+
async function readDenyReason(response, fallback) {
|
|
84
|
+
try {
|
|
85
|
+
const errBody = (await response.json());
|
|
86
|
+
const errData = errBody.data ?? errBody;
|
|
87
|
+
return asDenyReason(errData.reason) ?? fallback;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return fallback;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
69
93
|
/** 本地/测试:跳过远程校验 */
|
|
70
94
|
export class AllowAllGovernanceClient {
|
|
71
95
|
async checkPermission(_ctx, _params) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 权限校验 — 对齐 06-governance R16.8
|
|
3
3
|
*/
|
|
4
|
-
export type { CallerType, ExecutionContext, GovernancePermissionCheckParams, GovernancePermissionCheckResult, PermissionAction, PermissionDenyReason, PermissionResourceType, } from '@
|
|
4
|
+
export type { CallerType, ExecutionContext, GovernancePermissionCheckParams, GovernancePermissionCheckResult, PermissionAction, PermissionDenyReason, PermissionResourceType, } from '@eap/contracts';
|
|
5
5
|
export type { IGovernanceClient } from './governance-client.js';
|
|
6
6
|
export { HttpGovernanceClient, AllowAllGovernanceClient, resolveExecutionContext, } from './governance-client.js';
|
|
7
|
-
export { assertToolPermission, assertSkillPermission, PermissionDeniedError, GovernanceUnavailableError, } from './permission-guard.js';
|
|
7
|
+
export { assertToolPermission, assertSkillPermission, permissionMessage, PermissionDeniedError, GovernanceUnavailableError, } from './permission-guard.js';
|
package/dist/governance/index.js
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
* 权限校验 — 对齐 06-governance R16.8
|
|
3
3
|
*/
|
|
4
4
|
export { HttpGovernanceClient, AllowAllGovernanceClient, resolveExecutionContext, } from './governance-client.js';
|
|
5
|
-
export { assertToolPermission, assertSkillPermission, PermissionDeniedError, GovernanceUnavailableError, } from './permission-guard.js';
|
|
5
|
+
export { assertToolPermission, assertSkillPermission, permissionMessage, PermissionDeniedError, GovernanceUnavailableError, } from './permission-guard.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 执行期 Tool 权限守卫
|
|
3
3
|
*/
|
|
4
|
-
import type { ExecutionContext, PermissionDenyReason } from '@
|
|
4
|
+
import type { ExecutionContext, PermissionDenyReason } from '@eap/contracts';
|
|
5
5
|
import type { IGovernanceClient } from './governance-client.js';
|
|
6
6
|
export declare class GovernanceUnavailableError extends Error {
|
|
7
7
|
readonly reason: PermissionDenyReason;
|
|
@@ -11,6 +11,7 @@ export declare class PermissionDeniedError extends Error {
|
|
|
11
11
|
readonly reason: PermissionDenyReason;
|
|
12
12
|
constructor(reason?: PermissionDenyReason, message?: string);
|
|
13
13
|
}
|
|
14
|
+
export declare function permissionMessage(kind: 'tool' | 'skill', reason: PermissionDenyReason): string;
|
|
14
15
|
export declare function assertToolPermission(client: IGovernanceClient | undefined, ctx: ExecutionContext, toolId: string, options?: {
|
|
15
16
|
dataScope?: 'user-bound' | 'department-bound' | 'global';
|
|
16
17
|
resourceOwnerId?: string;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export class GovernanceUnavailableError extends Error {
|
|
5
5
|
reason = 'GOVERNANCE_UNAVAILABLE';
|
|
6
|
-
constructor(message = '
|
|
6
|
+
constructor(message = '治理服务不可用') {
|
|
7
7
|
super(message);
|
|
8
8
|
this.name = 'GovernanceUnavailableError';
|
|
9
9
|
}
|
|
@@ -11,11 +11,18 @@ export class GovernanceUnavailableError extends Error {
|
|
|
11
11
|
export class PermissionDeniedError extends Error {
|
|
12
12
|
reason;
|
|
13
13
|
constructor(reason = 'NO_PERMISSION', message) {
|
|
14
|
-
super(message ?? reason);
|
|
14
|
+
super(message ?? permissionMessage('tool', reason));
|
|
15
15
|
this.name = 'PermissionDeniedError';
|
|
16
16
|
this.reason = reason;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
export function permissionMessage(kind, reason) {
|
|
20
|
+
if (reason === 'DATA_SCOPE_DENIED')
|
|
21
|
+
return '数据范围不足';
|
|
22
|
+
if (reason === 'GOVERNANCE_UNAVAILABLE')
|
|
23
|
+
return '治理服务不可用';
|
|
24
|
+
return kind === 'skill' ? '用户无该 Skill 使用权限' : '用户无该 Tool 执行权限';
|
|
25
|
+
}
|
|
19
26
|
export async function assertToolPermission(client, ctx, toolId, options) {
|
|
20
27
|
if (!client) {
|
|
21
28
|
return;
|
|
@@ -34,7 +41,8 @@ export async function assertToolPermission(client, ctx, toolId, options) {
|
|
|
34
41
|
throw new GovernanceUnavailableError();
|
|
35
42
|
}
|
|
36
43
|
if (!result.allowed) {
|
|
37
|
-
|
|
44
|
+
const reason = result.reason ?? 'NO_PERMISSION';
|
|
45
|
+
throw new PermissionDeniedError(reason, permissionMessage('tool', reason));
|
|
38
46
|
}
|
|
39
47
|
}
|
|
40
48
|
/** Skill 激活守卫(04 R15.3) */
|
|
@@ -54,6 +62,7 @@ export async function assertSkillPermission(client, ctx, skillId) {
|
|
|
54
62
|
throw new GovernanceUnavailableError();
|
|
55
63
|
}
|
|
56
64
|
if (!result.allowed) {
|
|
57
|
-
|
|
65
|
+
const reason = result.reason ?? 'NO_PERMISSION';
|
|
66
|
+
throw new PermissionDeniedError(reason, permissionMessage('skill', reason));
|
|
58
67
|
}
|
|
59
68
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @see tasks.md Task 11.5
|
|
9
9
|
*/
|
|
10
10
|
import { BaseCheckpointSaver, } from '@langchain/langgraph';
|
|
11
|
+
import { notifyCheckpointRef, stripCheckpointCollectors, } from '../../checkpoint/checkpoint-ref-notify.js';
|
|
11
12
|
/**
|
|
12
13
|
* LangGraphCheckpointer — 桥接 LangGraph BaseCheckpointSaver → ICheckpointSaver
|
|
13
14
|
*
|
|
@@ -42,7 +43,8 @@ export class LangGraphCheckpointer extends BaseCheckpointSaver {
|
|
|
42
43
|
async put(config, checkpoint, metadata, _newVersions) {
|
|
43
44
|
const threadId = this.extractThreadId(config) ?? `thread-${Date.now()}`;
|
|
44
45
|
const data = this.checkpointToData(threadId, checkpoint, metadata);
|
|
45
|
-
await this.saver.save(threadId, data);
|
|
46
|
+
await this.saver.save(threadId, stripCheckpointCollectors(data));
|
|
47
|
+
await notifyCheckpointRef(data);
|
|
46
48
|
return {
|
|
47
49
|
...config,
|
|
48
50
|
configurable: {
|
|
@@ -64,6 +64,7 @@ export declare class LangGraphAdapter implements IStateGraphEngine {
|
|
|
64
64
|
/**
|
|
65
65
|
* Tool 节点执行旁路:started → tool.called + tool.progress;
|
|
66
66
|
* completed/error → 对应终态 + tool.progress。同一 toolCallId。
|
|
67
|
+
* 读 SKILL.md / 技能包内文件:成功后再发 skill.loaded / skill.resource_read,同时保留 tool.*。
|
|
67
68
|
*/
|
|
68
69
|
private emitToolExecutionEvents;
|
|
69
70
|
/**
|