@hongmaple0820/scale-engine 0.3.0 → 0.6.0
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/LICENSE +15 -15
- package/README.md +1004 -64
- package/dist/adapters/ClaudeCodeAdapter.d.ts +2 -2
- package/dist/adapters/ClaudeCodeAdapter.js +33 -45
- package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
- package/dist/adapters/CodexAdapter.js +28 -28
- package/dist/adapters/CursorAdapter.d.ts +14 -0
- package/dist/adapters/CursorAdapter.js +169 -0
- package/dist/adapters/CursorAdapter.js.map +1 -0
- package/dist/adapters/GeminiAdapter.d.ts +13 -0
- package/dist/adapters/GeminiAdapter.js +157 -0
- package/dist/adapters/GeminiAdapter.js.map +1 -0
- package/dist/adapters/HermesAdapter.d.ts +13 -0
- package/dist/adapters/HermesAdapter.js +157 -0
- package/dist/adapters/HermesAdapter.js.map +1 -0
- package/dist/adapters/OpenClawAdapter.d.ts +13 -0
- package/dist/adapters/OpenClawAdapter.js +157 -0
- package/dist/adapters/OpenClawAdapter.js.map +1 -0
- package/dist/adapters/OpenCodeAdapter.d.ts +14 -0
- package/dist/adapters/OpenCodeAdapter.js +170 -0
- package/dist/adapters/OpenCodeAdapter.js.map +1 -0
- package/dist/adapters/index.d.ts +17 -0
- package/dist/adapters/index.js +42 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/api/cli.js +227 -9
- package/dist/api/cli.js.map +1 -1
- package/dist/artifact/fsm.d.ts +5 -0
- package/dist/artifact/fsm.js +24 -0
- package/dist/artifact/fsm.js.map +1 -1
- package/dist/artifact/fsmDefinitions.js +41 -1
- package/dist/artifact/fsmDefinitions.js.map +1 -1
- package/dist/artifact/sqliteStore.js +89 -89
- package/dist/artifact/types.d.ts +62 -1
- package/dist/artifact/types.js +27 -0
- package/dist/artifact/types.js.map +1 -1
- package/dist/context/ContextBuilder.d.ts +5 -1
- package/dist/context/ContextBuilder.js +90 -5
- package/dist/context/ContextBuilder.js.map +1 -1
- package/dist/evolution/EvolutionEngine.js +31 -31
- package/dist/guardrails/advancedDetectors.d.ts +12 -0
- package/dist/guardrails/advancedDetectors.js +50 -2
- package/dist/guardrails/advancedDetectors.js.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.js +23 -4
- package/dist/index.js.map +1 -1
- package/dist/knowledge/SQLiteKnowledgeBase.d.ts +28 -0
- package/dist/knowledge/SQLiteKnowledgeBase.js +177 -0
- package/dist/knowledge/SQLiteKnowledgeBase.js.map +1 -0
- package/dist/skills/SkillDiscovery.d.ts +28 -0
- package/dist/skills/SkillDiscovery.js +171 -0
- package/dist/skills/SkillDiscovery.js.map +1 -0
- package/dist/tasks/TaskEngine.js +24 -0
- package/dist/tasks/TaskEngine.js.map +1 -1
- package/dist/workflows/presets.d.ts +34 -0
- package/dist/workflows/presets.js +224 -0
- package/dist/workflows/presets.js.map +1 -0
- package/package.json +54 -48
|
@@ -17,67 +17,67 @@ import { dirname, join } from 'node:path';
|
|
|
17
17
|
// ============================================================================
|
|
18
18
|
// Schema DDL
|
|
19
19
|
// ============================================================================
|
|
20
|
-
const SCHEMA_DDL = `
|
|
21
|
-
-- Artifacts 主表
|
|
22
|
-
CREATE TABLE IF NOT EXISTS artifacts (
|
|
23
|
-
id TEXT PRIMARY KEY,
|
|
24
|
-
type TEXT NOT NULL,
|
|
25
|
-
version INTEGER NOT NULL DEFAULT 1,
|
|
26
|
-
status TEXT NOT NULL DEFAULT 'DRAFT',
|
|
27
|
-
status_history TEXT NOT NULL DEFAULT '[]', -- JSON array of StatusChange
|
|
28
|
-
parents TEXT NOT NULL DEFAULT '[]', -- JSON array of ArtifactId
|
|
29
|
-
children TEXT NOT NULL DEFAULT '[]', -- JSON array of ArtifactId
|
|
30
|
-
supersedes TEXT,
|
|
31
|
-
title TEXT NOT NULL,
|
|
32
|
-
content_ref TEXT NOT NULL DEFAULT '',
|
|
33
|
-
payload TEXT NOT NULL DEFAULT '{}', -- JSON
|
|
34
|
-
gates TEXT NOT NULL DEFAULT '[]', -- JSON array of Gate
|
|
35
|
-
created_by TEXT NOT NULL DEFAULT '{}', -- JSON Actor
|
|
36
|
-
created_at INTEGER NOT NULL,
|
|
37
|
-
updated_at INTEGER NOT NULL,
|
|
38
|
-
closed_at INTEGER,
|
|
39
|
-
tags TEXT NOT NULL DEFAULT '[]', -- JSON array
|
|
40
|
-
labels TEXT NOT NULL DEFAULT '{}' -- JSON object
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
CREATE INDEX IF NOT EXISTS idx_artifacts_type ON artifacts(type);
|
|
44
|
-
CREATE INDEX IF NOT EXISTS idx_artifacts_status ON artifacts(status);
|
|
45
|
-
CREATE INDEX IF NOT EXISTS idx_artifacts_created_at ON artifacts(created_at);
|
|
46
|
-
|
|
47
|
-
-- Events 表(事件溯源的持久化副本)
|
|
48
|
-
CREATE TABLE IF NOT EXISTS events (
|
|
49
|
-
id TEXT PRIMARY KEY,
|
|
50
|
-
type TEXT NOT NULL,
|
|
51
|
-
timestamp INTEGER NOT NULL,
|
|
52
|
-
session_id TEXT,
|
|
53
|
-
artifact_id TEXT,
|
|
54
|
-
actor TEXT DEFAULT '{}',
|
|
55
|
-
payload TEXT NOT NULL DEFAULT '{}',
|
|
56
|
-
metadata TEXT DEFAULT '{}'
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
|
|
60
|
-
CREATE INDEX IF NOT EXISTS idx_events_artifact_id ON events(artifact_id);
|
|
61
|
-
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
62
|
-
|
|
63
|
-
-- Artifact 关系边表(加速 parent/child 查询)
|
|
64
|
-
CREATE TABLE IF NOT EXISTS artifact_edges (
|
|
65
|
-
parent_id TEXT NOT NULL,
|
|
66
|
-
child_id TEXT NOT NULL,
|
|
67
|
-
PRIMARY KEY (parent_id, child_id)
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
CREATE INDEX IF NOT EXISTS idx_edges_parent ON artifact_edges(parent_id);
|
|
71
|
-
CREATE INDEX IF NOT EXISTS idx_edges_child ON artifact_edges(child_id);
|
|
72
|
-
|
|
73
|
-
-- 元信息表
|
|
74
|
-
CREATE TABLE IF NOT EXISTS meta (
|
|
75
|
-
key TEXT PRIMARY KEY,
|
|
76
|
-
value TEXT NOT NULL
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
-- 初始化 schema version
|
|
80
|
-
INSERT OR IGNORE INTO meta (key, value) VALUES ('schema_version', '1');
|
|
20
|
+
const SCHEMA_DDL = `
|
|
21
|
+
-- Artifacts 主表
|
|
22
|
+
CREATE TABLE IF NOT EXISTS artifacts (
|
|
23
|
+
id TEXT PRIMARY KEY,
|
|
24
|
+
type TEXT NOT NULL,
|
|
25
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
26
|
+
status TEXT NOT NULL DEFAULT 'DRAFT',
|
|
27
|
+
status_history TEXT NOT NULL DEFAULT '[]', -- JSON array of StatusChange
|
|
28
|
+
parents TEXT NOT NULL DEFAULT '[]', -- JSON array of ArtifactId
|
|
29
|
+
children TEXT NOT NULL DEFAULT '[]', -- JSON array of ArtifactId
|
|
30
|
+
supersedes TEXT,
|
|
31
|
+
title TEXT NOT NULL,
|
|
32
|
+
content_ref TEXT NOT NULL DEFAULT '',
|
|
33
|
+
payload TEXT NOT NULL DEFAULT '{}', -- JSON
|
|
34
|
+
gates TEXT NOT NULL DEFAULT '[]', -- JSON array of Gate
|
|
35
|
+
created_by TEXT NOT NULL DEFAULT '{}', -- JSON Actor
|
|
36
|
+
created_at INTEGER NOT NULL,
|
|
37
|
+
updated_at INTEGER NOT NULL,
|
|
38
|
+
closed_at INTEGER,
|
|
39
|
+
tags TEXT NOT NULL DEFAULT '[]', -- JSON array
|
|
40
|
+
labels TEXT NOT NULL DEFAULT '{}' -- JSON object
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_artifacts_type ON artifacts(type);
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_artifacts_status ON artifacts(status);
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_artifacts_created_at ON artifacts(created_at);
|
|
46
|
+
|
|
47
|
+
-- Events 表(事件溯源的持久化副本)
|
|
48
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
49
|
+
id TEXT PRIMARY KEY,
|
|
50
|
+
type TEXT NOT NULL,
|
|
51
|
+
timestamp INTEGER NOT NULL,
|
|
52
|
+
session_id TEXT,
|
|
53
|
+
artifact_id TEXT,
|
|
54
|
+
actor TEXT DEFAULT '{}',
|
|
55
|
+
payload TEXT NOT NULL DEFAULT '{}',
|
|
56
|
+
metadata TEXT DEFAULT '{}'
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_events_type ON events(type);
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_events_artifact_id ON events(artifact_id);
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
62
|
+
|
|
63
|
+
-- Artifact 关系边表(加速 parent/child 查询)
|
|
64
|
+
CREATE TABLE IF NOT EXISTS artifact_edges (
|
|
65
|
+
parent_id TEXT NOT NULL,
|
|
66
|
+
child_id TEXT NOT NULL,
|
|
67
|
+
PRIMARY KEY (parent_id, child_id)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
CREATE INDEX IF NOT EXISTS idx_edges_parent ON artifact_edges(parent_id);
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_edges_child ON artifact_edges(child_id);
|
|
72
|
+
|
|
73
|
+
-- 元信息表
|
|
74
|
+
CREATE TABLE IF NOT EXISTS meta (
|
|
75
|
+
key TEXT PRIMARY KEY,
|
|
76
|
+
value TEXT NOT NULL
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
-- 初始化 schema version
|
|
80
|
+
INSERT OR IGNORE INTO meta (key, value) VALUES ('schema_version', '1');
|
|
81
81
|
`;
|
|
82
82
|
// ============================================================================
|
|
83
83
|
// SQLiteArtifactStore
|
|
@@ -131,38 +131,38 @@ export class SQLiteArtifactStore {
|
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
133
|
prepareStatements() {
|
|
134
|
-
this.stmtInsert = this.db.prepare(`
|
|
135
|
-
INSERT INTO artifacts (id, type, version, status, status_history, parents, children, supersedes,
|
|
136
|
-
title, content_ref, payload, gates, created_by, created_at, updated_at, closed_at, tags, labels)
|
|
137
|
-
VALUES (@id, @type, @version, @status, @statusHistory, @parents, @children, @supersedes,
|
|
138
|
-
@title, @contentRef, @payload, @gates, @createdBy, @createdAt, @updatedAt, @closedAt, @tags, @labels)
|
|
134
|
+
this.stmtInsert = this.db.prepare(`
|
|
135
|
+
INSERT INTO artifacts (id, type, version, status, status_history, parents, children, supersedes,
|
|
136
|
+
title, content_ref, payload, gates, created_by, created_at, updated_at, closed_at, tags, labels)
|
|
137
|
+
VALUES (@id, @type, @version, @status, @statusHistory, @parents, @children, @supersedes,
|
|
138
|
+
@title, @contentRef, @payload, @gates, @createdBy, @createdAt, @updatedAt, @closedAt, @tags, @labels)
|
|
139
139
|
`);
|
|
140
140
|
this.stmtGet = this.db.prepare(`SELECT * FROM artifacts WHERE id = ?`);
|
|
141
|
-
this.stmtUpdate = this.db.prepare(`
|
|
142
|
-
UPDATE artifacts SET
|
|
143
|
-
version = @version, status = @status, status_history = @statusHistory,
|
|
144
|
-
parents = @parents, children = @children, supersedes = @supersedes,
|
|
145
|
-
title = @title, content_ref = @contentRef, payload = @payload,
|
|
146
|
-
gates = @gates, updated_at = @updatedAt, closed_at = @closedAt,
|
|
147
|
-
tags = @tags, labels = @labels
|
|
148
|
-
WHERE id = @id
|
|
141
|
+
this.stmtUpdate = this.db.prepare(`
|
|
142
|
+
UPDATE artifacts SET
|
|
143
|
+
version = @version, status = @status, status_history = @statusHistory,
|
|
144
|
+
parents = @parents, children = @children, supersedes = @supersedes,
|
|
145
|
+
title = @title, content_ref = @contentRef, payload = @payload,
|
|
146
|
+
gates = @gates, updated_at = @updatedAt, closed_at = @closedAt,
|
|
147
|
+
tags = @tags, labels = @labels
|
|
148
|
+
WHERE id = @id
|
|
149
149
|
`);
|
|
150
150
|
this.stmtDelete = this.db.prepare(`DELETE FROM artifacts WHERE id = ?`);
|
|
151
151
|
this.stmtInsertEdge = this.db.prepare(`INSERT OR IGNORE INTO artifact_edges (parent_id, child_id) VALUES (?, ?)`);
|
|
152
152
|
this.stmtDeleteEdges = this.db.prepare(`DELETE FROM artifact_edges WHERE child_id = ?`);
|
|
153
|
-
this.stmtFindChildren = this.db.prepare(`
|
|
154
|
-
SELECT a.* FROM artifacts a
|
|
155
|
-
INNER JOIN artifact_edges e ON a.id = e.child_id
|
|
156
|
-
WHERE e.parent_id = ?
|
|
153
|
+
this.stmtFindChildren = this.db.prepare(`
|
|
154
|
+
SELECT a.* FROM artifacts a
|
|
155
|
+
INNER JOIN artifact_edges e ON a.id = e.child_id
|
|
156
|
+
WHERE e.parent_id = ?
|
|
157
157
|
`);
|
|
158
|
-
this.stmtFindParents = this.db.prepare(`
|
|
159
|
-
SELECT a.* FROM artifacts a
|
|
160
|
-
INNER JOIN artifact_edges e ON a.id = e.parent_id
|
|
161
|
-
WHERE e.child_id = ?
|
|
158
|
+
this.stmtFindParents = this.db.prepare(`
|
|
159
|
+
SELECT a.* FROM artifacts a
|
|
160
|
+
INNER JOIN artifact_edges e ON a.id = e.parent_id
|
|
161
|
+
WHERE e.child_id = ?
|
|
162
162
|
`);
|
|
163
|
-
this.stmtInsertEvent = this.db.prepare(`
|
|
164
|
-
INSERT OR IGNORE INTO events (id, type, timestamp, session_id, artifact_id, actor, payload, metadata)
|
|
165
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
163
|
+
this.stmtInsertEvent = this.db.prepare(`
|
|
164
|
+
INSERT OR IGNORE INTO events (id, type, timestamp, session_id, artifact_id, actor, payload, metadata)
|
|
165
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
166
166
|
`);
|
|
167
167
|
}
|
|
168
168
|
// ===== CRUD =====
|
|
@@ -276,10 +276,10 @@ export class SQLiteArtifactStore {
|
|
|
276
276
|
}
|
|
277
277
|
async findChildren(parentId, type) {
|
|
278
278
|
if (type) {
|
|
279
|
-
const rows = this.db.prepare(`
|
|
280
|
-
SELECT a.* FROM artifacts a
|
|
281
|
-
INNER JOIN artifact_edges e ON a.id = e.child_id
|
|
282
|
-
WHERE e.parent_id = ? AND a.type = ?
|
|
279
|
+
const rows = this.db.prepare(`
|
|
280
|
+
SELECT a.* FROM artifacts a
|
|
281
|
+
INNER JOIN artifact_edges e ON a.id = e.child_id
|
|
282
|
+
WHERE e.parent_id = ? AND a.type = ?
|
|
283
283
|
`).all(parentId, type);
|
|
284
284
|
return rows.map((r) => this.fromRow(r));
|
|
285
285
|
}
|
package/dist/artifact/types.d.ts
CHANGED
|
@@ -130,6 +130,11 @@ export interface TaskPayload {
|
|
|
130
130
|
dependsOn: ArtifactId[];
|
|
131
131
|
requiredRole: string;
|
|
132
132
|
requiredCapabilities: string[];
|
|
133
|
+
buildStatus?: 'pending' | 'success' | 'failed';
|
|
134
|
+
buildExitCode?: number;
|
|
135
|
+
lintStatus?: 'pending' | 'success' | 'failed';
|
|
136
|
+
testPassed?: boolean;
|
|
137
|
+
testCoverage?: number;
|
|
133
138
|
}
|
|
134
139
|
/** Change —— 实际代码变更 */
|
|
135
140
|
export interface ChangePayload {
|
|
@@ -235,10 +240,11 @@ export interface GuardFailure {
|
|
|
235
240
|
}
|
|
236
241
|
export interface Session {
|
|
237
242
|
id: SessionId;
|
|
238
|
-
agent:
|
|
243
|
+
agent: AgentPlatform | 'unknown';
|
|
239
244
|
startedAt: Timestamp;
|
|
240
245
|
endedAt?: Timestamp;
|
|
241
246
|
activeRole?: string;
|
|
247
|
+
scenarioMode?: ScenarioMode;
|
|
242
248
|
metadata: Record<string, unknown>;
|
|
243
249
|
}
|
|
244
250
|
export interface ToolUseInput {
|
|
@@ -331,3 +337,58 @@ export declare class RoleDeniedError extends ScaleError {
|
|
|
331
337
|
export declare class ArtifactNotFoundError extends ScaleError {
|
|
332
338
|
constructor(id: string);
|
|
333
339
|
}
|
|
340
|
+
/** 场景模式:控制检测器敏感度、上下文规则、权限级别 */
|
|
341
|
+
export type ScenarioMode = 'sandbox' | 'standard' | 'critical';
|
|
342
|
+
/** 场景模式配置 */
|
|
343
|
+
export interface ScenarioModeConfig {
|
|
344
|
+
mode: ScenarioMode;
|
|
345
|
+
detectorSensitivity: 'low' | 'medium' | 'high';
|
|
346
|
+
verificationRequired: boolean;
|
|
347
|
+
humanConfirmationRequired: boolean;
|
|
348
|
+
auditTrail: boolean;
|
|
349
|
+
maxRetries: number;
|
|
350
|
+
}
|
|
351
|
+
/** 场景模式预设配置 */
|
|
352
|
+
export declare const SCENARIO_MODE_CONFIGS: Record<ScenarioMode, ScenarioModeConfig>;
|
|
353
|
+
/** Agent 平台类型 */
|
|
354
|
+
export type AgentPlatform = 'claude-code' | 'codex' | 'opencode' | 'cursor' | 'gemini' | 'openclaw' | 'hermes';
|
|
355
|
+
/** Skill 引用 */
|
|
356
|
+
export interface SkillRef {
|
|
357
|
+
id: string;
|
|
358
|
+
name: string;
|
|
359
|
+
description: string;
|
|
360
|
+
platform: AgentPlatform;
|
|
361
|
+
path: string;
|
|
362
|
+
enabled: boolean;
|
|
363
|
+
}
|
|
364
|
+
/** Skill 目录扫描结果 */
|
|
365
|
+
export interface SkillScanResult {
|
|
366
|
+
platform: AgentPlatform;
|
|
367
|
+
skillsDir: string;
|
|
368
|
+
skills: SkillRef[];
|
|
369
|
+
exists: boolean;
|
|
370
|
+
}
|
|
371
|
+
/** 工作流步骤 */
|
|
372
|
+
export interface WorkflowStep {
|
|
373
|
+
stepId: string;
|
|
374
|
+
skillId?: string;
|
|
375
|
+
action: string;
|
|
376
|
+
verificationGate?: string;
|
|
377
|
+
isMandatory: boolean;
|
|
378
|
+
description?: string;
|
|
379
|
+
}
|
|
380
|
+
/** 工作流预设 */
|
|
381
|
+
export interface WorkflowPreset {
|
|
382
|
+
id: string;
|
|
383
|
+
name: string;
|
|
384
|
+
nameZh: string;
|
|
385
|
+
description: string;
|
|
386
|
+
steps: WorkflowStep[];
|
|
387
|
+
scenarioMode: ScenarioMode;
|
|
388
|
+
requiredArtifacts: Array<{
|
|
389
|
+
type: ArtifactType;
|
|
390
|
+
status?: string;
|
|
391
|
+
}>;
|
|
392
|
+
}
|
|
393
|
+
/** Agent 类型扩展(支持所有 7 种 Agent) */
|
|
394
|
+
export type AgentType = AgentPlatform;
|
package/dist/artifact/types.js
CHANGED
|
@@ -47,4 +47,31 @@ export class ArtifactNotFoundError extends ScaleError {
|
|
|
47
47
|
super(`Artifact '${id}' not found`, 'ARTIFACT_NOT_FOUND', { id });
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
/** 场景模式预设配置 */
|
|
51
|
+
export const SCENARIO_MODE_CONFIGS = {
|
|
52
|
+
sandbox: {
|
|
53
|
+
mode: 'sandbox',
|
|
54
|
+
detectorSensitivity: 'low',
|
|
55
|
+
verificationRequired: false,
|
|
56
|
+
humanConfirmationRequired: false,
|
|
57
|
+
auditTrail: false,
|
|
58
|
+
maxRetries: 10,
|
|
59
|
+
},
|
|
60
|
+
standard: {
|
|
61
|
+
mode: 'standard',
|
|
62
|
+
detectorSensitivity: 'medium',
|
|
63
|
+
verificationRequired: true,
|
|
64
|
+
humanConfirmationRequired: false,
|
|
65
|
+
auditTrail: true,
|
|
66
|
+
maxRetries: 5,
|
|
67
|
+
},
|
|
68
|
+
critical: {
|
|
69
|
+
mode: 'critical',
|
|
70
|
+
detectorSensitivity: 'high',
|
|
71
|
+
verificationRequired: true,
|
|
72
|
+
humanConfirmationRequired: true,
|
|
73
|
+
auditTrail: true,
|
|
74
|
+
maxRetries: 3,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
50
77
|
//# sourceMappingURL=types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/artifact/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/artifact/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAweH,+EAA+E;AAC/E,WAAW;AACX,+EAA+E;AAE/E,MAAM,OAAO,UAAW,SAAQ,KAAK;IACC;IAAqB;IAAzD,YAAY,OAAe,EAAS,IAAY,EAAS,OAAiB;QACxE,KAAK,CAAC,OAAO,CAAC,CAAA;QADoB,SAAI,GAAJ,IAAI,CAAQ;QAAS,YAAO,GAAP,OAAO,CAAU;QAExE,IAAI,CAAC,IAAI,GAAG,YAAY,CAAA;IAC1B,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,UAAU;IACpD,YAAY,IAAY,EAAE,MAAc;QACtC,KAAK,CAAC,UAAU,IAAI,8BAA8B,MAAM,GAAG,EAAE,oBAAoB,EAAE;YACjF,IAAI;YACJ,MAAM;SACP,CAAC,CAAA;IACJ,CAAC;CACF;AAED,MAAM,OAAO,gBAAiB,SAAQ,UAAU;IAC3B;IAAnB,YAAmB,QAAwB;QACzC,KAAK,CAAC,iCAAiC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,cAAc,EAAE;YAChG,QAAQ;SACT,CAAC,CAAA;QAHe,aAAQ,GAAR,QAAQ,CAAgB;IAI3C,CAAC;CACF;AAED,MAAM,OAAO,eAAgB,SAAQ,UAAU;IAC7C,YAAY,IAAY,EAAE,MAAc;QACtC,KAAK,CAAC,SAAS,IAAI,aAAa,MAAM,EAAE,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAA;IAC5E,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,UAAU;IACnD,YAAY,EAAU;QACpB,KAAK,CAAC,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;IACnE,CAAC;CACF;AAmBD,eAAe;AACf,MAAM,CAAC,MAAM,qBAAqB,GAA6C;IAC7E,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,mBAAmB,EAAE,KAAK;QAC1B,oBAAoB,EAAE,KAAK;QAC3B,yBAAyB,EAAE,KAAK;QAChC,UAAU,EAAE,KAAK;QACjB,UAAU,EAAE,EAAE;KACf;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,mBAAmB,EAAE,QAAQ;QAC7B,oBAAoB,EAAE,IAAI;QAC1B,yBAAyB,EAAE,KAAK;QAChC,UAAU,EAAE,IAAI;QAChB,UAAU,EAAE,CAAC;KACd;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,mBAAmB,EAAE,MAAM;QAC3B,oBAAoB,EAAE,IAAI;QAC1B,yBAAyB,EAAE,IAAI;QAC/B,UAAU,EAAE,IAAI;QAChB,UAAU,EAAE,CAAC;KACd;CACF,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ArtifactId, SessionId } from '../artifact/types.js';
|
|
1
|
+
import type { ArtifactId, SessionId, ScenarioMode } from '../artifact/types.js';
|
|
2
2
|
import type { IArtifactStore } from '../artifact/store.js';
|
|
3
3
|
import type { IKnowledgeBase } from '../knowledge/KnowledgeBase.js';
|
|
4
4
|
import type { IEventBus } from '../core/eventBus.js';
|
|
@@ -13,6 +13,7 @@ export interface BuiltContext {
|
|
|
13
13
|
metadata: {
|
|
14
14
|
totalTokens: number;
|
|
15
15
|
layers: string[];
|
|
16
|
+
scenarioMode?: ScenarioMode;
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
export interface ContextStatus {
|
|
@@ -27,12 +28,14 @@ export interface ContextStatus {
|
|
|
27
28
|
current?: boolean;
|
|
28
29
|
}>;
|
|
29
30
|
constraints: string[];
|
|
31
|
+
scenarioMode?: ScenarioMode;
|
|
30
32
|
}
|
|
31
33
|
export interface IContextBuilder {
|
|
32
34
|
build(opts: {
|
|
33
35
|
roleId?: string;
|
|
34
36
|
currentArtifactId?: ArtifactId;
|
|
35
37
|
sessionId: SessionId;
|
|
38
|
+
scenarioMode?: ScenarioMode;
|
|
36
39
|
}): Promise<BuiltContext>;
|
|
37
40
|
getStatus(sessionId: SessionId, roleGate: {
|
|
38
41
|
getRole(): {
|
|
@@ -52,6 +55,7 @@ export declare class ContextBuilder implements IContextBuilder {
|
|
|
52
55
|
roleId?: string;
|
|
53
56
|
currentArtifactId?: ArtifactId;
|
|
54
57
|
sessionId: SessionId;
|
|
58
|
+
scenarioMode?: ScenarioMode;
|
|
55
59
|
}): Promise<BuiltContext>;
|
|
56
60
|
getStatus(sessionId: SessionId, roleGate: {
|
|
57
61
|
getRole(): {
|
|
@@ -1,6 +1,75 @@
|
|
|
1
|
-
// SCALE Engine — Context Builder (
|
|
2
|
-
// 分层上下文加载 + Token 预算
|
|
1
|
+
// SCALE Engine — Context Builder (v0.5.0 完整实现)
|
|
2
|
+
// 分层上下文加载 + Token 预算 + SCALE v10.0 哲学 P1 层 + 场景模式感知
|
|
3
3
|
// 设计参考:docs/03-CORE-MODULES.md §3.6
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// SCALE v10.0 Philosophy — P1 system_rules layer content
|
|
6
|
+
// ============================================================================
|
|
7
|
+
const SCALE_V10_PHILOSOPHY = `## SCALE Engine v10.0 — System Rules
|
|
8
|
+
|
|
9
|
+
You are operating under SCALE Engine governance. These rules are PHYSICALLY ENFORCED — you cannot bypass them by choice.
|
|
10
|
+
|
|
11
|
+
### Core Philosophy
|
|
12
|
+
- **S**caffold — Every action is scaffolded by artifacts (Spec→Plan→Task→Change→Evidence)
|
|
13
|
+
- **C**ontrol — Guardrails physically block dangerous actions; no "soft" suggestions
|
|
14
|
+
- **A**rtifact — All work products are tracked artifacts with FSM state machines
|
|
15
|
+
- **L**earn — Defects auto-extract to Lessons → Rules → Hooks (evolution loop)
|
|
16
|
+
- **E**volve — The system improves itself from mistakes
|
|
17
|
+
|
|
18
|
+
### Physical Constraints (cannot be bypassed)
|
|
19
|
+
🔴 **Dangerous commands** (rm -rf, DROP TABLE, format) are BLOCKED at the gate
|
|
20
|
+
🔴 **Hardcoded secrets** (AWS keys, passwords, tokens) are BLOCKED on Edit/Write
|
|
21
|
+
🔴 **Unapproved code** — Cannot implement until Spec is FROZEN and Plan is APPROVED
|
|
22
|
+
🟡 **Brute retry** — 3+ identical retries detected → forced pause + context injection
|
|
23
|
+
🟡 **Premature completion** — Cannot claim "done" without running tests
|
|
24
|
+
🟡 **Blame shift** — Detected when AI blames environment without evidence
|
|
25
|
+
|
|
26
|
+
### Mandatory Workflow
|
|
27
|
+
1. **Explore** (role: explorer) — Read/Grep only, no edits
|
|
28
|
+
2. **Plan** (role: planner) — Create Spec → refine → approve (guard: ambiguity ≤ 0.2)
|
|
29
|
+
3. **Implement** (role: implementer) — Edit/Write/Bash unlocked only after Plan APPROVED
|
|
30
|
+
4. **Verify** — Must run build+lint+test before marking task complete
|
|
31
|
+
5. **Learn** — Defects auto-extract → lessons → rules → hooks
|
|
32
|
+
|
|
33
|
+
### Artifact Lifecycle
|
|
34
|
+
- Every piece of work is an Artifact with typed FSM transitions
|
|
35
|
+
- Guards check quality gates at each transition (ambiguity score, test coverage, etc.)
|
|
36
|
+
- Artifacts form a DAG: Need→Insight→Spec→Plan→Task→Change→Evidence
|
|
37
|
+
- Challenging a FROZEN Spec invalidates downstream Plans and Tasks
|
|
38
|
+
|
|
39
|
+
### Self-Evolution
|
|
40
|
+
- Defect → Lesson (auto-extracted from root cause)
|
|
41
|
+
- Lesson → Rule (promoted by access count + verification)
|
|
42
|
+
- Rule → Hook (auto-generated enforcement code)
|
|
43
|
+
- The system gets stricter over time, not weaker`;
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Scenario Mode Context Additions
|
|
46
|
+
// ============================================================================
|
|
47
|
+
const SCENARIO_CONTEXT = {
|
|
48
|
+
sandbox: `### Scenario Mode: SANDBOX 🏖️
|
|
49
|
+
- Lower detector sensitivity — exploratory work allowed
|
|
50
|
+
- Verification NOT required before completion
|
|
51
|
+
- Human confirmation NOT required
|
|
52
|
+
- Audit trail disabled (lighter weight)
|
|
53
|
+
- Max retries: 10 (freedom to experiment)
|
|
54
|
+
- Use for: prototyping, exploration, learning`,
|
|
55
|
+
standard: `### Scenario Mode: STANDARD ⚙️
|
|
56
|
+
- Medium detector sensitivity — production-quality required
|
|
57
|
+
- Verification REQUIRED before completion
|
|
58
|
+
- Human confirmation NOT required (trust the process)
|
|
59
|
+
- Audit trail ENABLED
|
|
60
|
+
- Max retries: 5 (balanced)
|
|
61
|
+
- Use for: feature development, bug fixes, regular work`,
|
|
62
|
+
critical: `### Scenario Mode: CRITICAL 🔒
|
|
63
|
+
- Maximum detector sensitivity — zero tolerance for errors
|
|
64
|
+
- Verification REQUIRED before completion
|
|
65
|
+
- Human confirmation REQUIRED for key transitions
|
|
66
|
+
- Audit trail ENABLED (comprehensive)
|
|
67
|
+
- Max retries: 3 (fail fast, escalate)
|
|
68
|
+
- Use for: security changes, production deployments, data migrations`,
|
|
69
|
+
};
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// ContextBuilder
|
|
72
|
+
// ============================================================================
|
|
4
73
|
export class ContextBuilder {
|
|
5
74
|
store;
|
|
6
75
|
kb;
|
|
@@ -12,11 +81,27 @@ export class ContextBuilder {
|
|
|
12
81
|
this.eventBus = eventBus;
|
|
13
82
|
}
|
|
14
83
|
async build(opts) {
|
|
84
|
+
const scenarioMode = opts.scenarioMode ?? 'standard';
|
|
15
85
|
const layers = [];
|
|
16
|
-
|
|
86
|
+
// P1: System Rules — SCALE v10.0 Philosophy (always present, highest priority)
|
|
87
|
+
layers.push({
|
|
88
|
+
name: 'system_rules',
|
|
89
|
+
content: SCALE_V10_PHILOSOPHY,
|
|
90
|
+
priority: 1,
|
|
91
|
+
estimatedTokens: 3500,
|
|
92
|
+
});
|
|
93
|
+
// P1.5: Scenario Mode awareness
|
|
94
|
+
layers.push({
|
|
95
|
+
name: 'scenario_mode',
|
|
96
|
+
content: SCENARIO_CONTEXT[scenarioMode],
|
|
97
|
+
priority: 1,
|
|
98
|
+
estimatedTokens: 800,
|
|
99
|
+
});
|
|
100
|
+
// P2: Role prompt
|
|
17
101
|
if (opts.roleId) {
|
|
18
102
|
layers.push({ name: 'role_prompt', content: `## Active Role: ${opts.roleId}\n...`, priority: 2, estimatedTokens: 1500 });
|
|
19
103
|
}
|
|
104
|
+
// P3: Current artifact context
|
|
20
105
|
if (opts.currentArtifactId) {
|
|
21
106
|
const artifact = await this.store.get(opts.currentArtifactId);
|
|
22
107
|
if (artifact) {
|
|
@@ -43,10 +128,10 @@ export class ContextBuilder {
|
|
|
43
128
|
selected.push(layer);
|
|
44
129
|
used += layer.estimatedTokens;
|
|
45
130
|
}
|
|
46
|
-
this.eventBus.emit('context.built', { layers: selected.map((l) => l.name), totalTokens: used }, { sessionId: opts.sessionId });
|
|
131
|
+
this.eventBus.emit('context.built', { layers: selected.map((l) => l.name), totalTokens: used, scenarioMode }, { sessionId: opts.sessionId });
|
|
47
132
|
return {
|
|
48
133
|
system: selected.map((l) => l.content).join('\n\n---\n\n'),
|
|
49
|
-
metadata: { totalTokens: used, layers: selected.map((l) => l.name) },
|
|
134
|
+
metadata: { totalTokens: used, layers: selected.map((l) => l.name), scenarioMode },
|
|
50
135
|
};
|
|
51
136
|
}
|
|
52
137
|
async getStatus(sessionId, roleGate) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextBuilder.js","sourceRoot":"","sources":["../../src/context/ContextBuilder.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ContextBuilder.js","sourceRoot":"","sources":["../../src/context/ContextBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -165,20 +165,20 @@ export class RuleProposer {
|
|
|
165
165
|
mkdirSync(rulesDir, { recursive: true });
|
|
166
166
|
const filename = `${rule.id}.md`;
|
|
167
167
|
const path = join(rulesDir, filename);
|
|
168
|
-
const content = `# ${rule.title}
|
|
169
|
-
|
|
170
|
-
> Source: ${rule.sourceLesson}
|
|
171
|
-
> Enforcement: ${rule.enforcement}
|
|
172
|
-
> Approved: ${rule.approved ? `Yes (by ${rule.approvedBy})` : 'Pending'}
|
|
173
|
-
> Created: ${new Date(rule.createdAt).toISOString()}
|
|
174
|
-
|
|
175
|
-
## Description
|
|
176
|
-
|
|
177
|
-
${rule.description}
|
|
178
|
-
|
|
179
|
-
## Pattern
|
|
180
|
-
|
|
181
|
-
\`${rule.pattern}\`
|
|
168
|
+
const content = `# ${rule.title}
|
|
169
|
+
|
|
170
|
+
> Source: ${rule.sourceLesson}
|
|
171
|
+
> Enforcement: ${rule.enforcement}
|
|
172
|
+
> Approved: ${rule.approved ? `Yes (by ${rule.approvedBy})` : 'Pending'}
|
|
173
|
+
> Created: ${new Date(rule.createdAt).toISOString()}
|
|
174
|
+
|
|
175
|
+
## Description
|
|
176
|
+
|
|
177
|
+
${rule.description}
|
|
178
|
+
|
|
179
|
+
## Pattern
|
|
180
|
+
|
|
181
|
+
\`${rule.pattern}\`
|
|
182
182
|
`;
|
|
183
183
|
writeFileSync(path, content, 'utf-8');
|
|
184
184
|
return path;
|
|
@@ -205,23 +205,23 @@ export class HookGenerator {
|
|
|
205
205
|
const scriptName = `${rule.id}.sh`;
|
|
206
206
|
const scriptPath = join(hooksDir, scriptName);
|
|
207
207
|
// Generate shell script
|
|
208
|
-
const script = `#!/bin/bash
|
|
209
|
-
# Auto-generated hook from Rule: ${rule.id}
|
|
210
|
-
# Source lesson: ${rule.sourceLesson}
|
|
211
|
-
# Pattern: ${rule.pattern}
|
|
212
|
-
# Hook type: ${hookType} | Matcher: ${matcher}
|
|
213
|
-
#
|
|
214
|
-
# This hook was automatically promoted from a recurring lesson.
|
|
215
|
-
# Edit with caution — it enforces a hard constraint.
|
|
216
|
-
|
|
217
|
-
# Read tool input from stdin
|
|
218
|
-
INPUT=$(cat)
|
|
219
|
-
|
|
220
|
-
# Check condition
|
|
221
|
-
# TODO: Implement specific check for pattern "${rule.pattern}"
|
|
222
|
-
# For now, this is a placeholder that always passes.
|
|
223
|
-
echo "Hook ${rule.id} checked (pattern: ${rule.pattern})" >&2
|
|
224
|
-
exit 0
|
|
208
|
+
const script = `#!/bin/bash
|
|
209
|
+
# Auto-generated hook from Rule: ${rule.id}
|
|
210
|
+
# Source lesson: ${rule.sourceLesson}
|
|
211
|
+
# Pattern: ${rule.pattern}
|
|
212
|
+
# Hook type: ${hookType} | Matcher: ${matcher}
|
|
213
|
+
#
|
|
214
|
+
# This hook was automatically promoted from a recurring lesson.
|
|
215
|
+
# Edit with caution — it enforces a hard constraint.
|
|
216
|
+
|
|
217
|
+
# Read tool input from stdin
|
|
218
|
+
INPUT=$(cat)
|
|
219
|
+
|
|
220
|
+
# Check condition
|
|
221
|
+
# TODO: Implement specific check for pattern "${rule.pattern}"
|
|
222
|
+
# For now, this is a placeholder that always passes.
|
|
223
|
+
echo "Hook ${rule.id} checked (pattern: ${rule.pattern})" >&2
|
|
224
|
+
exit 0
|
|
225
225
|
`;
|
|
226
226
|
writeFileSync(scriptPath, script, 'utf-8');
|
|
227
227
|
const hook = {
|
|
@@ -24,3 +24,15 @@ export declare class RoleGateDetector implements IDetector {
|
|
|
24
24
|
getRole(): RoleDefinition;
|
|
25
25
|
check(input: ToolUseInput, _ctx: DetectorContext): Promise<DetectorResult>;
|
|
26
26
|
}
|
|
27
|
+
export declare class ScopeCreepDetector implements IDetector {
|
|
28
|
+
name: string;
|
|
29
|
+
/** Max distinct files allowed per session before warning */
|
|
30
|
+
private maxFiles;
|
|
31
|
+
/** Window in ms to track file edits */
|
|
32
|
+
private windowMs;
|
|
33
|
+
constructor(opts?: {
|
|
34
|
+
maxFiles?: number;
|
|
35
|
+
windowMs?: number;
|
|
36
|
+
});
|
|
37
|
+
check(input: ToolUseInput, ctx: DetectorContext): Promise<DetectorResult>;
|
|
38
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// SCALE Engine — 危险命令检测 + Role 权限网关
|
|
2
|
-
// W5 补充检测器
|
|
1
|
+
// SCALE Engine — 危险命令检测 + Role 权限网关 + ScopeCreep 检测
|
|
2
|
+
// W5 补充检测器 + 9th detector
|
|
3
3
|
// 设计参考:docs/01-ARCHITECTURE.md §二 L2
|
|
4
4
|
// ============================================================================
|
|
5
5
|
// 6. 危险命令检测器
|
|
@@ -135,4 +135,52 @@ export class RoleGateDetector {
|
|
|
135
135
|
return { triggered: false };
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// 9. ScopeCreep 检测器
|
|
140
|
+
// ============================================================================
|
|
141
|
+
export class ScopeCreepDetector {
|
|
142
|
+
name = 'scope-creep';
|
|
143
|
+
/** Max distinct files allowed per session before warning */
|
|
144
|
+
maxFiles;
|
|
145
|
+
/** Window in ms to track file edits */
|
|
146
|
+
windowMs;
|
|
147
|
+
constructor(opts = {}) {
|
|
148
|
+
this.maxFiles = opts.maxFiles ?? 15;
|
|
149
|
+
this.windowMs = opts.windowMs ?? 10 * 60 * 1000; // 10 minutes
|
|
150
|
+
}
|
|
151
|
+
async check(input, ctx) {
|
|
152
|
+
if (!['Edit', 'Write', 'MultiEdit'].includes(input.tool))
|
|
153
|
+
return { triggered: false };
|
|
154
|
+
const file = input.args.file_path;
|
|
155
|
+
if (!file)
|
|
156
|
+
return { triggered: false };
|
|
157
|
+
const key = `scope-creep:${input.sessionId}`;
|
|
158
|
+
const record = ctx.cache.get(key)
|
|
159
|
+
?? { files: new Set(), timestamps: [] };
|
|
160
|
+
const now = Date.now();
|
|
161
|
+
// Prune old timestamps outside window
|
|
162
|
+
record.timestamps = record.timestamps.filter((t) => now - t < this.windowMs);
|
|
163
|
+
// Track new file
|
|
164
|
+
const isNew = !record.files.has(file);
|
|
165
|
+
record.files.add(file);
|
|
166
|
+
record.timestamps.push(now);
|
|
167
|
+
ctx.cache.set(key, record);
|
|
168
|
+
// Only warn when a NEW file is added and we exceed the threshold
|
|
169
|
+
if (isNew && record.files.size > this.maxFiles) {
|
|
170
|
+
ctx.eventBus.emit('tool.blocked', {
|
|
171
|
+
tool: input.tool,
|
|
172
|
+
detector: this.name,
|
|
173
|
+
reason: `scope_creep:${record.files.size}_files`,
|
|
174
|
+
file,
|
|
175
|
+
}, { sessionId: input.sessionId });
|
|
176
|
+
return {
|
|
177
|
+
triggered: true,
|
|
178
|
+
severity: 'warn',
|
|
179
|
+
reason: `⚠️ 检测到「范围蔓延」:本会话已编辑 ${record.files.size} 个不同文件(阈值 ${this.maxFiles})。请确认这些修改都在当前任务范围内,而非越界修改。`,
|
|
180
|
+
suggestion: '请检查是否偏离了原始任务范围。考虑收窄修改范围或拆分为多个任务。',
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return { triggered: false };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
138
186
|
//# sourceMappingURL=advancedDetectors.js.map
|