@hongmaple0820/scale-engine 0.1.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.
Files changed (78) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +64 -0
  3. package/dist/adapters/ClaudeCodeAdapter.d.ts +48 -0
  4. package/dist/adapters/ClaudeCodeAdapter.js +188 -0
  5. package/dist/adapters/ClaudeCodeAdapter.js.map +1 -0
  6. package/dist/adapters/CodexAdapter.d.ts +14 -0
  7. package/dist/adapters/CodexAdapter.js +153 -0
  8. package/dist/adapters/CodexAdapter.js.map +1 -0
  9. package/dist/api/cli.d.ts +2 -0
  10. package/dist/api/cli.js +396 -0
  11. package/dist/api/cli.js.map +1 -0
  12. package/dist/api/doctor.d.ts +28 -0
  13. package/dist/api/doctor.js +182 -0
  14. package/dist/api/doctor.js.map +1 -0
  15. package/dist/api/mcp.d.ts +32 -0
  16. package/dist/api/mcp.js +227 -0
  17. package/dist/api/mcp.js.map +1 -0
  18. package/dist/artifact/fsm.d.ts +36 -0
  19. package/dist/artifact/fsm.js +199 -0
  20. package/dist/artifact/fsm.js.map +1 -0
  21. package/dist/artifact/fsmDefinitions.d.ts +18 -0
  22. package/dist/artifact/fsmDefinitions.js +243 -0
  23. package/dist/artifact/fsmDefinitions.js.map +1 -0
  24. package/dist/artifact/sqliteStore.d.ts +61 -0
  25. package/dist/artifact/sqliteStore.js +394 -0
  26. package/dist/artifact/sqliteStore.js.map +1 -0
  27. package/dist/artifact/store.d.ts +49 -0
  28. package/dist/artifact/store.js +118 -0
  29. package/dist/artifact/store.js.map +1 -0
  30. package/dist/artifact/types.d.ts +333 -0
  31. package/dist/artifact/types.js +50 -0
  32. package/dist/artifact/types.js.map +1 -0
  33. package/dist/context/ContextBuilder.d.ts +36 -0
  34. package/dist/context/ContextBuilder.js +53 -0
  35. package/dist/context/ContextBuilder.js.map +1 -0
  36. package/dist/core/container.d.ts +14 -0
  37. package/dist/core/container.js +33 -0
  38. package/dist/core/container.js.map +1 -0
  39. package/dist/core/eventBus.d.ts +60 -0
  40. package/dist/core/eventBus.js +158 -0
  41. package/dist/core/eventBus.js.map +1 -0
  42. package/dist/core/logger.d.ts +3 -0
  43. package/dist/core/logger.js +12 -0
  44. package/dist/core/logger.js.map +1 -0
  45. package/dist/evolution/BehaviorTracker.d.ts +38 -0
  46. package/dist/evolution/BehaviorTracker.js +52 -0
  47. package/dist/evolution/BehaviorTracker.js.map +1 -0
  48. package/dist/evolution/EvolutionEngine.d.ts +99 -0
  49. package/dist/evolution/EvolutionEngine.js +321 -0
  50. package/dist/evolution/EvolutionEngine.js.map +1 -0
  51. package/dist/guardrails/Gateway.d.ts +26 -0
  52. package/dist/guardrails/Gateway.js +57 -0
  53. package/dist/guardrails/Gateway.js.map +1 -0
  54. package/dist/guardrails/advancedDetectors.d.ts +26 -0
  55. package/dist/guardrails/advancedDetectors.js +138 -0
  56. package/dist/guardrails/advancedDetectors.js.map +1 -0
  57. package/dist/guardrails/detectors.d.ts +25 -0
  58. package/dist/guardrails/detectors.js +170 -0
  59. package/dist/guardrails/detectors.js.map +1 -0
  60. package/dist/guardrails/roles.d.ts +4 -0
  61. package/dist/guardrails/roles.js +54 -0
  62. package/dist/guardrails/roles.js.map +1 -0
  63. package/dist/index.d.ts +22 -0
  64. package/dist/index.js +22 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/knowledge/KnowledgeBase.d.ts +25 -0
  67. package/dist/knowledge/KnowledgeBase.js +81 -0
  68. package/dist/knowledge/KnowledgeBase.js.map +1 -0
  69. package/dist/orchestration/EffectsWiring.d.ts +8 -0
  70. package/dist/orchestration/EffectsWiring.js +87 -0
  71. package/dist/orchestration/EffectsWiring.js.map +1 -0
  72. package/dist/routing/ModelRouter.d.ts +30 -0
  73. package/dist/routing/ModelRouter.js +69 -0
  74. package/dist/routing/ModelRouter.js.map +1 -0
  75. package/dist/tasks/TaskEngine.d.ts +97 -0
  76. package/dist/tasks/TaskEngine.js +269 -0
  77. package/dist/tasks/TaskEngine.js.map +1 -0
  78. package/package.json +48 -0
@@ -0,0 +1,321 @@
1
+ // SCALE Engine — Evolution Layer (W7)
2
+ // 4 级自进化:Defect → Lesson → Rule → Hook
3
+ // 设计参考:docs/01-ARCHITECTURE.md §二 L6
4
+ import { logger } from '../core/logger.js';
5
+ import { writeFileSync, mkdirSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ export class LessonExtractor {
8
+ store;
9
+ kb;
10
+ eventBus;
11
+ constructor(store, kb, eventBus) {
12
+ this.store = store;
13
+ this.kb = kb;
14
+ this.eventBus = eventBus;
15
+ }
16
+ /**
17
+ * 从已关闭的 Defect 中提取 Lesson
18
+ * Gate 1: Defect 必须在 DIAGNOSED/FIXED/CLOSED 状态
19
+ * Gate 2: 必须有 rootCauseCategory
20
+ * Gate 3: 不重复(标题相似度检查)
21
+ */
22
+ async extract(defectId) {
23
+ const defect = await this.store.get(defectId);
24
+ if (!defect)
25
+ return null;
26
+ // Gate 1: 状态检查
27
+ if (!['DIAGNOSED', 'FIXED', 'CLOSED'].includes(defect.status)) {
28
+ logger.debug({ defectId, status: defect.status }, 'Defect not ready for lesson extraction');
29
+ return null;
30
+ }
31
+ // Gate 2: 根因检查
32
+ const payload = defect.payload;
33
+ const rootCause = payload.rootCauseCategory;
34
+ if (!rootCause || rootCause === 'unknown') {
35
+ logger.debug({ defectId }, 'Defect has no root cause');
36
+ return null;
37
+ }
38
+ // Gate 3: 去重
39
+ const title = `[${rootCause}] ${defect.title}`;
40
+ const existing = await this.kb.recall({ tags: [rootCause] });
41
+ const isDupe = existing.some((e) => this.similarity(e.title, title) > 0.85);
42
+ if (isDupe) {
43
+ logger.debug({ defectId, title }, 'Similar lesson already exists — skipping');
44
+ return null;
45
+ }
46
+ // 创建 Lesson
47
+ const entry = await this.kb.add({
48
+ type: 'lesson',
49
+ title,
50
+ tags: [rootCause, defect.type, ...(payload.tags ?? [])],
51
+ contentRef: `lessons/${defectId}.md`,
52
+ verified: false,
53
+ sourceArtifact: defectId,
54
+ });
55
+ this.eventBus.emit('lesson.proposed', {
56
+ lessonId: entry.id,
57
+ defectId,
58
+ rootCause,
59
+ title,
60
+ });
61
+ logger.info({ lessonId: entry.id, defectId }, 'Lesson extracted from defect');
62
+ return entry;
63
+ }
64
+ /**
65
+ * 扫描所有已关闭的 Defect,批量提取 lessons
66
+ */
67
+ async scanForPatterns() {
68
+ const defects = await this.store.query({ type: 'Defect' });
69
+ const closedDefects = defects.filter((d) => ['DIAGNOSED', 'FIXED', 'CLOSED'].includes(d.status));
70
+ const extracted = [];
71
+ for (const defect of closedDefects) {
72
+ const lesson = await this.extract(defect.id);
73
+ if (lesson)
74
+ extracted.push(lesson);
75
+ }
76
+ // 检测重复模式:同类根因出现 ≥3 次 → 标记为高优先
77
+ const causeCount = new Map();
78
+ for (const d of closedDefects) {
79
+ const cause = d.payload.rootCauseCategory;
80
+ if (cause)
81
+ causeCount.set(cause, (causeCount.get(cause) ?? 0) + 1);
82
+ }
83
+ for (const [cause, count] of causeCount) {
84
+ if (count >= 3) {
85
+ logger.warn({ cause, count }, 'Recurring defect pattern detected — consider promoting to Rule');
86
+ }
87
+ }
88
+ return extracted;
89
+ }
90
+ similarity(a, b) {
91
+ const setA = new Set(a.toLowerCase().split(/\s+/));
92
+ const setB = new Set(b.toLowerCase().split(/\s+/));
93
+ const intersection = [...setA].filter((w) => setB.has(w));
94
+ return intersection.length / Math.max(setA.size, setB.size);
95
+ }
96
+ }
97
+ export class RuleProposer {
98
+ kb;
99
+ eventBus;
100
+ rules = new Map();
101
+ seq = 0;
102
+ constructor(kb, eventBus) {
103
+ this.kb = kb;
104
+ this.eventBus = eventBus;
105
+ }
106
+ async proposeFromLesson(lessonId) {
107
+ const lessons = await this.kb.recall({});
108
+ const lesson = lessons.find((l) => l.id === lessonId);
109
+ if (!lesson)
110
+ return null;
111
+ // 只对 verified + active 的 lesson 提议
112
+ if (!lesson.verified) {
113
+ logger.debug({ lessonId }, 'Lesson not verified — cannot propose rule');
114
+ return null;
115
+ }
116
+ const rule = {
117
+ id: `RULE-${Date.now()}-${(++this.seq).toString().padStart(3, '0')}`,
118
+ title: `Rule: ${lesson.title}`,
119
+ description: `Auto-proposed from lesson ${lessonId}. Tags: ${lesson.tags.join(', ')}`,
120
+ sourceLesson: lessonId,
121
+ pattern: lesson.tags[0] ?? 'general',
122
+ enforcement: lesson.accessCount >= 5 ? 'hook' : 'prompt',
123
+ createdAt: Date.now(),
124
+ approved: false,
125
+ };
126
+ this.rules.set(rule.id, rule);
127
+ this.eventBus.emit('rule.proposed', {
128
+ ruleId: rule.id,
129
+ lessonId,
130
+ enforcement: rule.enforcement,
131
+ });
132
+ logger.info({ ruleId: rule.id, lessonId }, 'Rule proposed from lesson');
133
+ return rule;
134
+ }
135
+ async scanAndPropose() {
136
+ const lessons = await this.kb.recall({ verifiedOnly: true });
137
+ const proposed = [];
138
+ for (const lesson of lessons) {
139
+ // 只对 relevance ≥ 0.6 且 accessCount ≥ 3 的 lesson 自动提议
140
+ if (lesson.relevance >= 0.6 && lesson.accessCount >= 3) {
141
+ const existing = [...this.rules.values()].find((r) => r.sourceLesson === lesson.id);
142
+ if (!existing) {
143
+ const rule = await this.proposeFromLesson(lesson.id);
144
+ if (rule)
145
+ proposed.push(rule);
146
+ }
147
+ }
148
+ }
149
+ return proposed;
150
+ }
151
+ async approve(ruleId, approvedBy) {
152
+ const rule = this.rules.get(ruleId);
153
+ if (!rule)
154
+ throw new Error(`Rule not found: ${ruleId}`);
155
+ rule.approved = true;
156
+ rule.approvedBy = approvedBy;
157
+ this.eventBus.emit('rule.enforced', { ruleId, approvedBy });
158
+ logger.info({ ruleId, approvedBy }, 'Rule approved');
159
+ return rule;
160
+ }
161
+ getProposedRules() {
162
+ return [...this.rules.values()];
163
+ }
164
+ writeRuleFile(rule, rulesDir) {
165
+ mkdirSync(rulesDir, { recursive: true });
166
+ const filename = `${rule.id}.md`;
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}\`
182
+ `;
183
+ writeFileSync(path, content, 'utf-8');
184
+ return path;
185
+ }
186
+ }
187
+ export class HookGenerator {
188
+ eventBus;
189
+ hooks = [];
190
+ constructor(eventBus) {
191
+ this.eventBus = eventBus;
192
+ }
193
+ generate(rule, hooksDir) {
194
+ if (!rule.approved) {
195
+ logger.debug({ ruleId: rule.id }, 'Rule not approved — cannot generate hook');
196
+ return null;
197
+ }
198
+ if (rule.enforcement !== 'hook') {
199
+ logger.debug({ ruleId: rule.id }, 'Rule enforcement is prompt, not hook — skipping');
200
+ return null;
201
+ }
202
+ mkdirSync(hooksDir, { recursive: true });
203
+ const hookType = this.inferHookType(rule.pattern);
204
+ const matcher = this.inferMatcher(rule.pattern);
205
+ const scriptName = `${rule.id}.sh`;
206
+ const scriptPath = join(hooksDir, scriptName);
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
225
+ `;
226
+ writeFileSync(scriptPath, script, 'utf-8');
227
+ const hook = {
228
+ id: `HOOK-${Date.now()}`,
229
+ ruleId: rule.id,
230
+ hookType,
231
+ matcher,
232
+ scriptPath,
233
+ createdAt: Date.now(),
234
+ };
235
+ this.hooks.push(hook);
236
+ this.eventBus.emit('hook.generated', {
237
+ hookId: hook.id,
238
+ ruleId: rule.id,
239
+ hookType,
240
+ matcher,
241
+ scriptPath,
242
+ });
243
+ logger.info({ hookId: hook.id, ruleId: rule.id, hookType }, 'Hook generated from rule');
244
+ return hook;
245
+ }
246
+ getGeneratedHooks() {
247
+ return [...this.hooks];
248
+ }
249
+ inferHookType(pattern) {
250
+ if (/test|verify|lint|build/i.test(pattern))
251
+ return 'Stop';
252
+ if (/edit|write|delete/i.test(pattern))
253
+ return 'PreToolUse';
254
+ return 'PostToolUse';
255
+ }
256
+ inferMatcher(pattern) {
257
+ if (/bash|command|shell/i.test(pattern))
258
+ return 'Bash';
259
+ if (/edit|write/i.test(pattern))
260
+ return 'Edit|Write|MultiEdit';
261
+ return '';
262
+ }
263
+ }
264
+ // ============================================================================
265
+ // EvolutionEngine — 编排 4 级进化
266
+ // ============================================================================
267
+ export class EvolutionEngine {
268
+ extractor;
269
+ proposer;
270
+ generator;
271
+ scaleDir;
272
+ eventBus;
273
+ constructor(extractor, proposer, generator, eventBus, scaleDir = '.scale') {
274
+ this.extractor = extractor;
275
+ this.proposer = proposer;
276
+ this.generator = generator;
277
+ this.scaleDir = scaleDir;
278
+ this.eventBus = eventBus;
279
+ }
280
+ /**
281
+ * 完整进化周期:
282
+ * 1. 扫描 Defects → 提取 Lessons
283
+ * 2. 扫描 Lessons → 提议 Rules
284
+ * 3. (人审后) 已批准 Rules → 生成 Hooks
285
+ */
286
+ async runCycle() {
287
+ const stats = {
288
+ lessonsExtracted: 0,
289
+ rulesProposed: 0,
290
+ rulesApproved: 0,
291
+ hooksGenerated: 0,
292
+ };
293
+ // Step 1: Extract lessons
294
+ const lessons = await this.extractor.scanForPatterns();
295
+ stats.lessonsExtracted = lessons.length;
296
+ // Step 2: Propose rules
297
+ const rules = await this.proposer.scanAndPropose();
298
+ stats.rulesProposed = rules.length;
299
+ // Step 3: Generate hooks for approved rules
300
+ const approvedRules = this.proposer.getProposedRules().filter((r) => r.approved && r.enforcement === 'hook');
301
+ stats.rulesApproved = approvedRules.length;
302
+ const hooksDir = join(this.scaleDir, 'hooks');
303
+ for (const rule of approvedRules) {
304
+ const hook = this.generator.generate(rule, hooksDir);
305
+ if (hook)
306
+ stats.hooksGenerated++;
307
+ }
308
+ logger.info(stats, 'Evolution cycle completed');
309
+ this.eventBus.emit('evolution.cycle_completed', stats);
310
+ return stats;
311
+ }
312
+ getStats() {
313
+ return {
314
+ lessonsExtracted: 0, // Would need persistence
315
+ rulesProposed: this.proposer.getProposedRules().length,
316
+ rulesApproved: this.proposer.getProposedRules().filter((r) => r.approved).length,
317
+ hooksGenerated: this.generator.getGeneratedHooks().length,
318
+ };
319
+ }
320
+ }
321
+ //# sourceMappingURL=EvolutionEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EvolutionEngine.js","sourceRoot":"","sources":["../../src/evolution/EvolutionEngine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { ToolUseInput, ToolResultInput, StopInput, GateDecision, DetectorResult } from '../artifact/types.js';
2
+ import type { IEventBus } from '../core/eventBus.js';
3
+ export interface IDetector {
4
+ name: string;
5
+ check(input: ToolUseInput | ToolResultInput | StopInput, context: DetectorContext): Promise<DetectorResult>;
6
+ }
7
+ export interface DetectorContext {
8
+ eventBus: IEventBus;
9
+ cache: Map<string, unknown>;
10
+ }
11
+ export interface IGateway {
12
+ preTool(input: ToolUseInput): Promise<GateDecision>;
13
+ postTool(input: ToolResultInput): Promise<void>;
14
+ beforeStop(input: StopInput): Promise<GateDecision>;
15
+ registerDetector(detector: IDetector, hook: 'preTool' | 'postTool' | 'beforeStop'): void;
16
+ }
17
+ export declare class Gateway implements IGateway {
18
+ private eventBus;
19
+ private cache;
20
+ private detectors;
21
+ constructor(eventBus: IEventBus);
22
+ registerDetector(detector: IDetector, hook: 'preTool' | 'postTool' | 'beforeStop'): void;
23
+ preTool(input: ToolUseInput): Promise<GateDecision>;
24
+ postTool(input: ToolResultInput): Promise<void>;
25
+ beforeStop(input: StopInput): Promise<GateDecision>;
26
+ }
@@ -0,0 +1,57 @@
1
+ // SCALE Engine — Guardrails Gateway (W5 完整实现)
2
+ // Hook 网关 + 5 种懒惰检测器 + Role 权限
3
+ // 设计参考:docs/03-CORE-MODULES.md §3.5
4
+ import { logger } from '../core/logger.js';
5
+ export class Gateway {
6
+ eventBus;
7
+ cache = new Map();
8
+ detectors = {
9
+ preTool: [],
10
+ postTool: [],
11
+ beforeStop: [],
12
+ };
13
+ constructor(eventBus) {
14
+ this.eventBus = eventBus;
15
+ }
16
+ registerDetector(detector, hook) {
17
+ this.detectors[hook].push(detector);
18
+ logger.debug({ name: detector.name, hook }, 'Detector registered');
19
+ }
20
+ async preTool(input) {
21
+ for (const det of this.detectors.preTool) {
22
+ const result = await det.check(input, { eventBus: this.eventBus, cache: this.cache });
23
+ if (result.triggered) {
24
+ if (result.severity === 'deny' || result.severity === 'block') {
25
+ this.eventBus.emit('tool.blocked', { tool: input.tool, detector: det.name, reason: result.reason }, { sessionId: input.sessionId });
26
+ return { allow: false, reason: result.reason, suggestion: result.suggestion };
27
+ }
28
+ if (result.severity === 'warn') {
29
+ return { allow: true, reason: result.reason, injectContext: [result.reason ?? ''] };
30
+ }
31
+ }
32
+ }
33
+ this.eventBus.emit('tool.called', { tool: input.tool, args: input.args }, { sessionId: input.sessionId });
34
+ return { allow: true };
35
+ }
36
+ async postTool(input) {
37
+ if (input.exitCode === 0) {
38
+ this.eventBus.emit('tool.completed', { tool: input.tool, args: input.args, output: input.output }, { sessionId: input.sessionId });
39
+ }
40
+ else {
41
+ this.eventBus.emit('tool.failed', { tool: input.tool, args: input.args, exitCode: input.exitCode, output: input.output }, { sessionId: input.sessionId });
42
+ }
43
+ for (const det of this.detectors.postTool) {
44
+ await det.check(input, { eventBus: this.eventBus, cache: this.cache });
45
+ }
46
+ }
47
+ async beforeStop(input) {
48
+ for (const det of this.detectors.beforeStop) {
49
+ const result = await det.check(input, { eventBus: this.eventBus, cache: this.cache });
50
+ if (result.triggered && (result.severity === 'deny' || result.severity === 'block')) {
51
+ return { allow: false, reason: result.reason, suggestion: result.suggestion };
52
+ }
53
+ }
54
+ return { allow: true };
55
+ }
56
+ }
57
+ //# sourceMappingURL=Gateway.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Gateway.js","sourceRoot":"","sources":["../../src/guardrails/Gateway.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { IDetector, DetectorContext } from './Gateway.js';
2
+ import type { ToolUseInput, DetectorResult } from '../artifact/types.js';
3
+ export declare class DangerousCommandDetector implements IDetector {
4
+ name: string;
5
+ private patterns;
6
+ check(input: ToolUseInput, ctx: DetectorContext): Promise<DetectorResult>;
7
+ }
8
+ export declare class SecretLeakDetector implements IDetector {
9
+ name: string;
10
+ private patterns;
11
+ check(input: ToolUseInput, ctx: DetectorContext): Promise<DetectorResult>;
12
+ }
13
+ export interface RoleDefinition {
14
+ id: string;
15
+ name: string;
16
+ allowedTools: string[];
17
+ deniedTools?: string[];
18
+ }
19
+ export declare const BUILT_IN_ROLES: Record<string, RoleDefinition>;
20
+ export declare class RoleGateDetector implements IDetector {
21
+ name: string;
22
+ private currentRole;
23
+ setRole(role: RoleDefinition): void;
24
+ getRole(): RoleDefinition;
25
+ check(input: ToolUseInput, _ctx: DetectorContext): Promise<DetectorResult>;
26
+ }
@@ -0,0 +1,138 @@
1
+ // SCALE Engine — 危险命令检测 + Role 权限网关
2
+ // W5 补充检测器
3
+ // 设计参考:docs/01-ARCHITECTURE.md §二 L2
4
+ // ============================================================================
5
+ // 6. 危险命令检测器
6
+ // ============================================================================
7
+ export class DangerousCommandDetector {
8
+ name = 'dangerous-command';
9
+ patterns = [
10
+ { pattern: /rm\s+-rf\s+[\/~]/, description: 'rm -rf on root/home' },
11
+ { pattern: /rm\s+-rf\s+\*/, description: 'rm -rf wildcard' },
12
+ { pattern: /DROP\s+(TABLE|DATABASE|SCHEMA)/i, description: 'SQL DROP' },
13
+ { pattern: /TRUNCATE\s+TABLE/i, description: 'SQL TRUNCATE' },
14
+ { pattern: /DELETE\s+FROM\s+\w+\s*;/i, description: 'SQL DELETE without WHERE' },
15
+ { pattern: /ALTER\s+TABLE\s+\w+\s+DROP/i, description: 'SQL ALTER DROP column' },
16
+ { pattern: /curl\s+.*\|\s*(bash|sh)/i, description: 'curl pipe to shell' },
17
+ { pattern: /chmod\s+777/, description: 'chmod 777' },
18
+ { pattern: />\s*\/dev\/sda/, description: 'write to device' },
19
+ { pattern: /mkfs\./, description: 'format filesystem' },
20
+ { pattern: /:(){ :\|:& };:/, description: 'fork bomb' },
21
+ { pattern: /dd\s+if=.*of=\/dev\//, description: 'dd to device' },
22
+ ];
23
+ async check(input, ctx) {
24
+ if (input.tool !== 'Bash')
25
+ return { triggered: false };
26
+ const command = input.args.command ?? '';
27
+ for (const { pattern, description } of this.patterns) {
28
+ if (pattern.test(command)) {
29
+ ctx.eventBus.emit('tool.blocked', {
30
+ tool: input.tool,
31
+ detector: this.name,
32
+ reason: description,
33
+ command,
34
+ }, { sessionId: input.sessionId });
35
+ return {
36
+ triggered: true,
37
+ severity: 'deny',
38
+ reason: `🛑 危险命令被拦截:${description}\n命令: ${command}\n此操作需要人工确认后才能执行。`,
39
+ suggestion: '请使用安全的替代命令,或获取人工授权。',
40
+ };
41
+ }
42
+ }
43
+ return { triggered: false };
44
+ }
45
+ }
46
+ // ============================================================================
47
+ // 7. 密钥泄露检测器
48
+ // ============================================================================
49
+ export class SecretLeakDetector {
50
+ name = 'secret-leak';
51
+ patterns = [
52
+ { pattern: /['"]?[A-Za-z0-9+\/]{40}['"]?/, description: 'possible API key (40 chars)' },
53
+ { pattern: /AKIA[0-9A-Z]{16}/, description: 'AWS Access Key ID' },
54
+ { pattern: /-----BEGIN (RSA |EC |DSA )?PRIVATE KEY-----/, description: 'Private key' },
55
+ { pattern: /sk-[a-zA-Z0-9]{48}/, description: 'OpenAI API key' },
56
+ { pattern: /ghp_[a-zA-Z0-9]{36}/, description: 'GitHub PAT' },
57
+ { pattern: /password\s*[:=]\s*['"][^'"]{8,}['"]/, description: 'hardcoded password' },
58
+ ];
59
+ async check(input, ctx) {
60
+ if (!['Edit', 'Write', 'MultiEdit'].includes(input.tool))
61
+ return { triggered: false };
62
+ const content = JSON.stringify(input.args);
63
+ for (const { pattern, description } of this.patterns) {
64
+ if (pattern.test(content)) {
65
+ ctx.eventBus.emit('tool.blocked', {
66
+ tool: input.tool,
67
+ detector: this.name,
68
+ reason: description,
69
+ }, { sessionId: input.sessionId });
70
+ return {
71
+ triggered: true,
72
+ severity: 'block',
73
+ reason: `🔑 检测到可能的密钥泄露:${description}\n请使用环境变量代替硬编码密钥。`,
74
+ suggestion: '使用 process.env.XXX 或 .env 文件',
75
+ };
76
+ }
77
+ }
78
+ return { triggered: false };
79
+ }
80
+ }
81
+ export const BUILT_IN_ROLES = {
82
+ explorer: {
83
+ id: 'explorer',
84
+ name: 'Explorer',
85
+ allowedTools: ['Read', 'Grep', 'Glob', 'WebSearch', 'Bash'],
86
+ deniedTools: ['Edit', 'Write', 'MultiEdit'],
87
+ },
88
+ planner: {
89
+ id: 'planner',
90
+ name: 'Planner',
91
+ allowedTools: ['Read', 'Grep', 'Glob', 'Write'],
92
+ deniedTools: ['Bash'],
93
+ },
94
+ implementer: {
95
+ id: 'implementer',
96
+ name: 'Implementer',
97
+ allowedTools: ['Read', 'Grep', 'Glob', 'Edit', 'Write', 'MultiEdit', 'Bash'],
98
+ },
99
+ reviewer: {
100
+ id: 'reviewer',
101
+ name: 'Reviewer',
102
+ allowedTools: ['Read', 'Grep', 'Glob', 'Bash'],
103
+ deniedTools: ['Edit', 'Write', 'MultiEdit'],
104
+ },
105
+ };
106
+ export class RoleGateDetector {
107
+ name = 'role-gate';
108
+ currentRole = BUILT_IN_ROLES.implementer;
109
+ setRole(role) {
110
+ this.currentRole = role;
111
+ }
112
+ getRole() {
113
+ return this.currentRole;
114
+ }
115
+ async check(input, _ctx) {
116
+ const role = this.currentRole;
117
+ // Denied tools take priority
118
+ if (role.deniedTools?.includes(input.tool)) {
119
+ return {
120
+ triggered: true,
121
+ severity: 'deny',
122
+ reason: `⛔ Role "${role.name}" 不允许使用 ${input.tool}。请先切换到合适的 Role (如 Implementer)。`,
123
+ suggestion: `切换 Role: scale role activate implementer`,
124
+ };
125
+ }
126
+ // If allowedTools is specified, tool must be in the list
127
+ if (!role.allowedTools.includes(input.tool)) {
128
+ return {
129
+ triggered: true,
130
+ severity: 'deny',
131
+ reason: `⛔ Role "${role.name}" 不允许使用 ${input.tool}。允许的工具: [${role.allowedTools.join(', ')}]`,
132
+ suggestion: `切换到允许 ${input.tool} 的 Role`,
133
+ };
134
+ }
135
+ return { triggered: false };
136
+ }
137
+ }
138
+ //# sourceMappingURL=advancedDetectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advancedDetectors.js","sourceRoot":"","sources":["../../src/guardrails/advancedDetectors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { IDetector, DetectorContext } from './Gateway.js';
2
+ import type { ToolUseInput, ToolResultInput, StopInput, DetectorResult } from '../artifact/types.js';
3
+ export declare class BruteRetryDetector implements IDetector {
4
+ name: string;
5
+ private windowMs;
6
+ private threshold;
7
+ check(input: ToolUseInput, ctx: DetectorContext): Promise<DetectorResult>;
8
+ }
9
+ export declare class IdleToolDetector implements IDetector {
10
+ name: string;
11
+ check(input: ToolUseInput, ctx: DetectorContext): Promise<DetectorResult>;
12
+ }
13
+ export declare class BusyLoopDetector implements IDetector {
14
+ name: string;
15
+ check(input: ToolUseInput, ctx: DetectorContext): Promise<DetectorResult>;
16
+ }
17
+ export declare class PrematureDoneDetector implements IDetector {
18
+ name: string;
19
+ check(input: StopInput, ctx: DetectorContext): Promise<DetectorResult>;
20
+ }
21
+ export declare class BlameShiftDetector implements IDetector {
22
+ name: string;
23
+ private patterns;
24
+ check(input: ToolResultInput, ctx: DetectorContext): Promise<DetectorResult>;
25
+ }