skyloom 1.11.0 → 1.13.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 (135) hide show
  1. package/.github/workflows/ci.yml +36 -36
  2. package/README.md +142 -46
  3. package/config/default.yaml +43 -47
  4. package/config/models.yaml +155 -0
  5. package/config/providers.yaml +39 -39
  6. package/config/skills/api_integrator/SKILL.md +15 -15
  7. package/config/skills/arch_designer/SKILL.md +13 -13
  8. package/config/skills/ci_cd_manager/SKILL.md +14 -14
  9. package/config/skills/code_analysis/SKILL.md +13 -13
  10. package/config/skills/code_generator/SKILL.md +12 -12
  11. package/config/skills/code_reviewer/SKILL.md +13 -13
  12. package/config/skills/content_writer/SKILL.md +14 -14
  13. package/config/skills/data_transformer/SKILL.md +15 -15
  14. package/config/skills/document_analysis/SKILL.md +13 -13
  15. package/config/skills/emotional_companion/SKILL.md +15 -15
  16. package/config/skills/performance_checker/SKILL.md +14 -14
  17. package/config/skills/security_auditor/SKILL.md +14 -14
  18. package/config/skills/self_evolve/SKILL.md +13 -13
  19. package/config/skills/sys_operator/SKILL.md +15 -15
  20. package/config/skills/task_planner/SKILL.md +14 -14
  21. package/config/skills/web_research/SKILL.md +14 -14
  22. package/config/skills/workflow_designer/SKILL.md +13 -13
  23. package/dist/agents/dew.js +52 -52
  24. package/dist/agents/fair.js +84 -84
  25. package/dist/agents/fog.js +30 -30
  26. package/dist/agents/frost.js +32 -32
  27. package/dist/agents/rain.js +32 -32
  28. package/dist/agents/snow.js +68 -68
  29. package/dist/cli/main.js +172 -47
  30. package/dist/cli/main.js.map +1 -1
  31. package/dist/cli/tui.d.ts.map +1 -1
  32. package/dist/cli/tui.js +9 -1
  33. package/dist/cli/tui.js.map +1 -1
  34. package/dist/core/agent/task.d.ts +58 -0
  35. package/dist/core/agent/task.d.ts.map +1 -0
  36. package/dist/core/agent/task.js +83 -0
  37. package/dist/core/agent/task.js.map +1 -0
  38. package/dist/core/agent.d.ts +2 -45
  39. package/dist/core/agent.d.ts.map +1 -1
  40. package/dist/core/agent.js +61 -145
  41. package/dist/core/agent.js.map +1 -1
  42. package/dist/core/agent_helpers.d.ts +10 -0
  43. package/dist/core/agent_helpers.d.ts.map +1 -1
  44. package/dist/core/agent_helpers.js +39 -0
  45. package/dist/core/agent_helpers.js.map +1 -1
  46. package/dist/core/catalog.d.ts +71 -0
  47. package/dist/core/catalog.d.ts.map +1 -0
  48. package/dist/core/catalog.js +176 -0
  49. package/dist/core/catalog.js.map +1 -0
  50. package/dist/core/config.d.ts +8 -0
  51. package/dist/core/config.d.ts.map +1 -1
  52. package/dist/core/config.js +12 -4
  53. package/dist/core/config.js.map +1 -1
  54. package/dist/core/factory.js +16 -16
  55. package/dist/core/llm.d.ts +7 -0
  56. package/dist/core/llm.d.ts.map +1 -1
  57. package/dist/core/llm.js +139 -7
  58. package/dist/core/llm.js.map +1 -1
  59. package/dist/core/longdoc.js +5 -5
  60. package/dist/core/memory.d.ts.map +1 -1
  61. package/dist/core/memory.js +69 -62
  62. package/dist/core/memory.js.map +1 -1
  63. package/dist/core/theme.d.ts +46 -0
  64. package/dist/core/theme.d.ts.map +1 -0
  65. package/dist/core/theme.js +42 -0
  66. package/dist/core/theme.js.map +1 -0
  67. package/dist/web/server.js +542 -519
  68. package/dist/web/server.js.map +1 -1
  69. package/docs/AESTHETIC_DESIGN.md +144 -0
  70. package/docs/OPTIMIZATION_PLAN.md +178 -0
  71. package/package.json +60 -60
  72. package/scripts/install.js +48 -48
  73. package/scripts/link.js +10 -10
  74. package/setup.bat +79 -79
  75. package/skill-test-ty2fOA/test.md +10 -10
  76. package/src/agents/dew.ts +70 -70
  77. package/src/agents/fair.ts +102 -102
  78. package/src/agents/fog.ts +48 -48
  79. package/src/agents/frost.ts +50 -50
  80. package/src/agents/rain.ts +50 -50
  81. package/src/agents/snow.ts +239 -239
  82. package/src/cli/main.ts +425 -316
  83. package/src/cli/mode.ts +58 -58
  84. package/src/cli/tui.ts +272 -268
  85. package/src/core/agent/task.ts +100 -0
  86. package/src/core/agent.ts +1446 -1549
  87. package/src/core/agent_helpers.ts +496 -461
  88. package/src/core/arbitrate.ts +162 -162
  89. package/src/core/catalog.ts +178 -0
  90. package/src/core/checkpoint.ts +94 -94
  91. package/src/core/config.ts +20 -4
  92. package/src/core/estimate.ts +104 -104
  93. package/src/core/evolve.ts +191 -191
  94. package/src/core/factory.ts +627 -627
  95. package/src/core/filter.ts +103 -103
  96. package/src/core/graph.ts +156 -156
  97. package/src/core/icons.ts +53 -53
  98. package/src/core/index.ts +37 -37
  99. package/src/core/learn.ts +146 -146
  100. package/src/core/llm.ts +108 -5
  101. package/src/core/longdoc.ts +155 -155
  102. package/src/core/mcp_server.ts +176 -176
  103. package/src/core/memory.ts +1178 -1171
  104. package/src/core/profile.ts +255 -255
  105. package/src/core/router.ts +124 -124
  106. package/src/core/sandbox.ts +142 -142
  107. package/src/core/security.ts +243 -243
  108. package/src/core/skill.ts +342 -342
  109. package/src/core/theme.ts +65 -0
  110. package/src/core/tool_router.ts +193 -193
  111. package/src/core/vector.ts +152 -152
  112. package/src/core/workspace.ts +150 -150
  113. package/src/plugins/loader.ts +66 -66
  114. package/src/skills/loader.ts +46 -46
  115. package/src/sql.js.d.ts +29 -29
  116. package/src/tools/builtin.ts +380 -380
  117. package/src/tools/computer.ts +269 -269
  118. package/src/tools/delegate.ts +49 -49
  119. package/src/web/server.ts +660 -634
  120. package/src/web/tts.ts +93 -93
  121. package/tests/agent_helpers.test.ts +48 -0
  122. package/tests/bus.test.ts +121 -121
  123. package/tests/catalog.test.ts +86 -0
  124. package/tests/config.test.ts +41 -0
  125. package/tests/icons.test.ts +45 -45
  126. package/tests/memory.test.ts +147 -0
  127. package/tests/router.test.ts +86 -86
  128. package/tests/schemas.test.ts +51 -51
  129. package/tests/semantic.test.ts +83 -83
  130. package/tests/setup.ts +10 -10
  131. package/tests/skill.test.ts +172 -172
  132. package/tests/task.test.ts +60 -0
  133. package/tests/tool.test.ts +108 -108
  134. package/tests/tool_router.test.ts +71 -71
  135. package/vitest.config.ts +17 -17
@@ -1,243 +1,243 @@
1
- /**
2
- * 安全与对齐模块 — Security & Alignment
3
- *
4
- * Danger level grading, red-line enforcement, audit trail, human-in-the-loop.
5
- * All security decisions flow through this module before tool execution.
6
- */
7
-
8
- import { getLogger } from "./logger";
9
-
10
- const log = getLogger("security");
11
-
12
- /* ── Danger levels ── */
13
- export enum DangerLevel {
14
- /** Read-only, no side effects — auto-approved */
15
- SAFE = 0,
16
- /** Minor side effects (write single file, git status) — logged */
17
- LOW = 1,
18
- /** Significant side effects (overwrite, delete, git push) — notify */
19
- MEDIUM = 2,
20
- /** Dangerous (sudo, remote deploy, mass delete) — confirm */
21
- HIGH = 3,
22
- /** Red-line — NEVER execute without human-in-the-loop */
23
- CRITICAL = 4,
24
- }
25
-
26
- /* ═══════════════════════════════════════
27
- Red-line list: operations that are NEVER auto-approved
28
- ═══════════════════════════════════════ */
29
- const REDLINE_PATTERNS = [
30
- /rm\s+-rf/, /format\s+\w:/, /dd\s+if=/,
31
- />\s*\/dev\/sd/, /mkfs\./, /:(){ :\|:& };:/,
32
- /sudo\s+rm/, /chmod\s+777\s+\//, /wget.*\|.*sh/,
33
- /curl.*\|.*bash/, /eval\s+\$/, /exec\s+\$/,
34
- /subprocess\.call.*rm/, /os\.system.*rm/,
35
- ];
36
-
37
- const REDLINE_COMMANDS = [
38
- "shutdown", "reboot", "init 0", "init 6",
39
- "del /f /s /q C:\\*", "rd /s /q C:\\",
40
- ];
41
-
42
- /* ═══════════════════════════════════════
43
- Per-tool danger level mapping
44
- ═══════════════════════════════════════ */
45
- const TOOL_DANGER_MAP: Record<string, DangerLevel> = {
46
- read_file: DangerLevel.SAFE,
47
- list_directory: DangerLevel.SAFE,
48
- tree: DangerLevel.SAFE,
49
- file_search: DangerLevel.SAFE,
50
- code_search: DangerLevel.SAFE,
51
- grep: DangerLevel.SAFE,
52
- git_status: DangerLevel.SAFE,
53
- git_diff: DangerLevel.SAFE,
54
- git_log: DangerLevel.SAFE,
55
- system_info: DangerLevel.SAFE,
56
- system_diagnose: DangerLevel.SAFE,
57
- list_processes: DangerLevel.SAFE,
58
- list_installed_apps: DangerLevel.SAFE,
59
- list_skills: DangerLevel.SAFE,
60
- recall_facts: DangerLevel.SAFE,
61
- mcp_list_servers: DangerLevel.SAFE,
62
-
63
- write_file: DangerLevel.LOW,
64
- edit_file: DangerLevel.LOW,
65
- copy_file: DangerLevel.LOW,
66
- move_file: DangerLevel.LOW,
67
- http_get: DangerLevel.LOW,
68
- fetch_page: DangerLevel.LOW,
69
- web_search: DangerLevel.LOW,
70
- remember_fact: DangerLevel.LOW,
71
- use_skill: DangerLevel.LOW,
72
- task_done: DangerLevel.LOW,
73
-
74
- delete_file: DangerLevel.MEDIUM,
75
- git_add: DangerLevel.MEDIUM,
76
- git_commit: DangerLevel.MEDIUM,
77
- git_checkout: DangerLevel.MEDIUM,
78
- http_post: DangerLevel.MEDIUM,
79
- mcp_add_server: DangerLevel.MEDIUM,
80
- mcp_remove_server: DangerLevel.MEDIUM,
81
- launch_app: DangerLevel.MEDIUM,
82
- open_path: DangerLevel.MEDIUM,
83
- browser_open: DangerLevel.MEDIUM,
84
-
85
- run_bash: DangerLevel.HIGH,
86
- shell_exec: DangerLevel.HIGH,
87
- kill_process: DangerLevel.HIGH,
88
- package_manager: DangerLevel.HIGH,
89
- service_control: DangerLevel.HIGH,
90
- delegate_to: DangerLevel.HIGH,
91
- mcp_scaffold_server: DangerLevel.HIGH,
92
- };
93
-
94
- /* ═══════════════════════════════════════
95
- Audit trail entry
96
- ═══════════════════════════════════════ */
97
- export interface AuditEntry {
98
- ts: string;
99
- agent: string;
100
- tool: string;
101
- args: Record<string, any>;
102
- dangerLevel: DangerLevel;
103
- approved: boolean;
104
- result: string;
105
- durationMs: number;
106
- traceId: string;
107
- }
108
-
109
- /* ═══════════════════════════════════════
110
- Security context — per-session security state
111
- ═══════════════════════════════════════ */
112
- export class SecurityContext {
113
- public auditLog: AuditEntry[] = [];
114
- public deniedCount = 0;
115
- public autoApprovedCount = 0;
116
- public manualApprovedCount = 0;
117
- public approvalMode: "auto" | "interactive" | "strict" = "auto";
118
-
119
- private approvalCallback: ((tool: string, args: Record<string, any>, level: DangerLevel) => Promise<boolean>) | null = null;
120
-
121
- constructor(opts?: { mode?: "auto" | "interactive" | "strict"; onApprove?: (tool: string, args: Record<string, any>, level: DangerLevel) => Promise<boolean> }) {
122
- if (opts?.mode) this.approvalMode = opts.mode;
123
- if (opts?.onApprove) this.approvalCallback = opts.onApprove;
124
- }
125
-
126
- /** Get the danger level for a tool. Defaults to SAFE for unknown tools. */
127
- getDangerLevel(toolName: string): DangerLevel {
128
- return TOOL_DANGER_MAP[toolName] ?? DangerLevel.SAFE;
129
- }
130
-
131
- /** Check if arguments contain red-line patterns (critical danger). */
132
- checkRedline(toolName: string, args: Record<string, any>): string | null {
133
- if (toolName !== "run_bash" && toolName !== "shell_exec") return null;
134
- const cmd = String(args.command || args.cmd || "").toLowerCase();
135
- for (const pattern of REDLINE_PATTERNS) {
136
- if (pattern.test(cmd)) return `Red-line pattern detected: ${pattern.source.slice(0, 40)}`;
137
- }
138
- for (const forbidden of REDLINE_COMMANDS) {
139
- if (cmd.includes(forbidden)) return `Red-line command: ${forbidden}`;
140
- }
141
- return null;
142
- }
143
-
144
- /** Determine whether a tool call is permitted. Returns [approved, reason]. */
145
- async checkApproval(toolName: string, args: Record<string, any>, agentName: string): Promise<[boolean, string]> {
146
- const level = this.getDangerLevel(toolName);
147
-
148
- // Red-line check
149
- const redline = this.checkRedline(toolName, args);
150
- if (redline) {
151
- log.warn("redline_blocked", { agent: agentName, tool: toolName, reason: redline });
152
- return [false, redline];
153
- }
154
-
155
- // Safe — always allow
156
- if (level === DangerLevel.SAFE) return [true, "safe"];
157
-
158
- // Strict mode — deny all non-safe
159
- if (this.approvalMode === "strict") {
160
- return [false, `Strict mode: tool '${toolName}' (level ${level}) requires manual approval`];
161
- }
162
-
163
- // Auto mode — allow LOW, prompt for MEDIUM+, deny CRITICAL
164
- if (this.approvalMode === "auto") {
165
- if (level <= DangerLevel.LOW) return [true, "auto-low"];
166
- if (level === DangerLevel.CRITICAL) return [false, `CRITICAL tool '${toolName}' requires explicit human approval`];
167
- // MEDIUM/HIGH with auto mode => need callback
168
- if (this.approvalCallback) {
169
- const approved = await this.approvalCallback(toolName, args, level);
170
- return [approved, approved ? "user-approved" : "user-denied"];
171
- }
172
- return [true, "auto-med"]; // no callback → auto-allow but log
173
- }
174
-
175
- // Interactive mode — prompt for LOW+
176
- if (this.approvalCallback) {
177
- const approved = await this.approvalCallback(toolName, args, level);
178
- return [approved, approved ? "user-approved" : "user-denied"];
179
- }
180
- return [true, "no-callback"];
181
- }
182
-
183
- /** Record an audit entry. */
184
- recordAudit(tool: string, agent: string, args: Record<string, any>, dangerLevel: DangerLevel, approved: boolean, resultPreview: string, durationMs: number, traceId: string): void {
185
- const entry: AuditEntry = {
186
- ts: new Date().toISOString(),
187
- agent, tool, args, dangerLevel, approved,
188
- result: resultPreview.slice(0, 500),
189
- durationMs, traceId,
190
- };
191
- this.auditLog.push(entry);
192
- if (this.auditLog.length > 5000) this.auditLog.shift();
193
-
194
- if (approved) {
195
- if (dangerLevel >= DangerLevel.HIGH) this.manualApprovedCount++;
196
- else this.autoApprovedCount++;
197
- } else {
198
- this.deniedCount++;
199
- }
200
-
201
- log.info(dangerLevel >= DangerLevel.HIGH ? "dangerous_tool_executed" : "tool_executed", {
202
- tool, agent, level: dangerLevel, approved,
203
- });
204
- }
205
-
206
- /** Get summary statistics. */
207
- getStats() {
208
- return {
209
- total: this.auditLog.length,
210
- denied: this.deniedCount,
211
- autoApproved: this.autoApprovedCount,
212
- manualApproved: this.manualApprovedCount,
213
- byLevel: {
214
- safe: this.auditLog.filter(e => e.dangerLevel === DangerLevel.SAFE).length,
215
- low: this.auditLog.filter(e => e.dangerLevel === DangerLevel.LOW).length,
216
- medium: this.auditLog.filter(e => e.dangerLevel === DangerLevel.MEDIUM).length,
217
- high: this.auditLog.filter(e => e.dangerLevel === DangerLevel.HIGH).length,
218
- critical: this.auditLog.filter(e => e.dangerLevel === DangerLevel.CRITICAL).length,
219
- },
220
- lastDenied: this.auditLog.filter(e => !e.approved).slice(-5).map(e => `${e.tool}: ${e.result}`),
221
- };
222
- }
223
-
224
- /** Install approval callback for interactive mode. */
225
- setApprovalCallback(fn: (tool: string, args: Record<string, any>, level: DangerLevel) => Promise<boolean>) {
226
- this.approvalCallback = fn;
227
- }
228
- }
229
-
230
- /* ── Global security context ── */
231
- let globalSecurity: SecurityContext | null = null;
232
-
233
- export function getSecurity(): SecurityContext {
234
- if (!globalSecurity) globalSecurity = new SecurityContext();
235
- return globalSecurity;
236
- }
237
-
238
- export function resetSecurity(): void {
239
- globalSecurity = null;
240
- }
241
-
242
- /** Red-line patterns for reference (used by tools to self-check). */
243
- export { REDLINE_PATTERNS, REDLINE_COMMANDS };
1
+ /**
2
+ * 安全与对齐模块 — Security & Alignment
3
+ *
4
+ * Danger level grading, red-line enforcement, audit trail, human-in-the-loop.
5
+ * All security decisions flow through this module before tool execution.
6
+ */
7
+
8
+ import { getLogger } from "./logger";
9
+
10
+ const log = getLogger("security");
11
+
12
+ /* ── Danger levels ── */
13
+ export enum DangerLevel {
14
+ /** Read-only, no side effects — auto-approved */
15
+ SAFE = 0,
16
+ /** Minor side effects (write single file, git status) — logged */
17
+ LOW = 1,
18
+ /** Significant side effects (overwrite, delete, git push) — notify */
19
+ MEDIUM = 2,
20
+ /** Dangerous (sudo, remote deploy, mass delete) — confirm */
21
+ HIGH = 3,
22
+ /** Red-line — NEVER execute without human-in-the-loop */
23
+ CRITICAL = 4,
24
+ }
25
+
26
+ /* ═══════════════════════════════════════
27
+ Red-line list: operations that are NEVER auto-approved
28
+ ═══════════════════════════════════════ */
29
+ const REDLINE_PATTERNS = [
30
+ /rm\s+-rf/, /format\s+\w:/, /dd\s+if=/,
31
+ />\s*\/dev\/sd/, /mkfs\./, /:(){ :\|:& };:/,
32
+ /sudo\s+rm/, /chmod\s+777\s+\//, /wget.*\|.*sh/,
33
+ /curl.*\|.*bash/, /eval\s+\$/, /exec\s+\$/,
34
+ /subprocess\.call.*rm/, /os\.system.*rm/,
35
+ ];
36
+
37
+ const REDLINE_COMMANDS = [
38
+ "shutdown", "reboot", "init 0", "init 6",
39
+ "del /f /s /q C:\\*", "rd /s /q C:\\",
40
+ ];
41
+
42
+ /* ═══════════════════════════════════════
43
+ Per-tool danger level mapping
44
+ ═══════════════════════════════════════ */
45
+ const TOOL_DANGER_MAP: Record<string, DangerLevel> = {
46
+ read_file: DangerLevel.SAFE,
47
+ list_directory: DangerLevel.SAFE,
48
+ tree: DangerLevel.SAFE,
49
+ file_search: DangerLevel.SAFE,
50
+ code_search: DangerLevel.SAFE,
51
+ grep: DangerLevel.SAFE,
52
+ git_status: DangerLevel.SAFE,
53
+ git_diff: DangerLevel.SAFE,
54
+ git_log: DangerLevel.SAFE,
55
+ system_info: DangerLevel.SAFE,
56
+ system_diagnose: DangerLevel.SAFE,
57
+ list_processes: DangerLevel.SAFE,
58
+ list_installed_apps: DangerLevel.SAFE,
59
+ list_skills: DangerLevel.SAFE,
60
+ recall_facts: DangerLevel.SAFE,
61
+ mcp_list_servers: DangerLevel.SAFE,
62
+
63
+ write_file: DangerLevel.LOW,
64
+ edit_file: DangerLevel.LOW,
65
+ copy_file: DangerLevel.LOW,
66
+ move_file: DangerLevel.LOW,
67
+ http_get: DangerLevel.LOW,
68
+ fetch_page: DangerLevel.LOW,
69
+ web_search: DangerLevel.LOW,
70
+ remember_fact: DangerLevel.LOW,
71
+ use_skill: DangerLevel.LOW,
72
+ task_done: DangerLevel.LOW,
73
+
74
+ delete_file: DangerLevel.MEDIUM,
75
+ git_add: DangerLevel.MEDIUM,
76
+ git_commit: DangerLevel.MEDIUM,
77
+ git_checkout: DangerLevel.MEDIUM,
78
+ http_post: DangerLevel.MEDIUM,
79
+ mcp_add_server: DangerLevel.MEDIUM,
80
+ mcp_remove_server: DangerLevel.MEDIUM,
81
+ launch_app: DangerLevel.MEDIUM,
82
+ open_path: DangerLevel.MEDIUM,
83
+ browser_open: DangerLevel.MEDIUM,
84
+
85
+ run_bash: DangerLevel.HIGH,
86
+ shell_exec: DangerLevel.HIGH,
87
+ kill_process: DangerLevel.HIGH,
88
+ package_manager: DangerLevel.HIGH,
89
+ service_control: DangerLevel.HIGH,
90
+ delegate_to: DangerLevel.HIGH,
91
+ mcp_scaffold_server: DangerLevel.HIGH,
92
+ };
93
+
94
+ /* ═══════════════════════════════════════
95
+ Audit trail entry
96
+ ═══════════════════════════════════════ */
97
+ export interface AuditEntry {
98
+ ts: string;
99
+ agent: string;
100
+ tool: string;
101
+ args: Record<string, any>;
102
+ dangerLevel: DangerLevel;
103
+ approved: boolean;
104
+ result: string;
105
+ durationMs: number;
106
+ traceId: string;
107
+ }
108
+
109
+ /* ═══════════════════════════════════════
110
+ Security context — per-session security state
111
+ ═══════════════════════════════════════ */
112
+ export class SecurityContext {
113
+ public auditLog: AuditEntry[] = [];
114
+ public deniedCount = 0;
115
+ public autoApprovedCount = 0;
116
+ public manualApprovedCount = 0;
117
+ public approvalMode: "auto" | "interactive" | "strict" = "auto";
118
+
119
+ private approvalCallback: ((tool: string, args: Record<string, any>, level: DangerLevel) => Promise<boolean>) | null = null;
120
+
121
+ constructor(opts?: { mode?: "auto" | "interactive" | "strict"; onApprove?: (tool: string, args: Record<string, any>, level: DangerLevel) => Promise<boolean> }) {
122
+ if (opts?.mode) this.approvalMode = opts.mode;
123
+ if (opts?.onApprove) this.approvalCallback = opts.onApprove;
124
+ }
125
+
126
+ /** Get the danger level for a tool. Defaults to SAFE for unknown tools. */
127
+ getDangerLevel(toolName: string): DangerLevel {
128
+ return TOOL_DANGER_MAP[toolName] ?? DangerLevel.SAFE;
129
+ }
130
+
131
+ /** Check if arguments contain red-line patterns (critical danger). */
132
+ checkRedline(toolName: string, args: Record<string, any>): string | null {
133
+ if (toolName !== "run_bash" && toolName !== "shell_exec") return null;
134
+ const cmd = String(args.command || args.cmd || "").toLowerCase();
135
+ for (const pattern of REDLINE_PATTERNS) {
136
+ if (pattern.test(cmd)) return `Red-line pattern detected: ${pattern.source.slice(0, 40)}`;
137
+ }
138
+ for (const forbidden of REDLINE_COMMANDS) {
139
+ if (cmd.includes(forbidden)) return `Red-line command: ${forbidden}`;
140
+ }
141
+ return null;
142
+ }
143
+
144
+ /** Determine whether a tool call is permitted. Returns [approved, reason]. */
145
+ async checkApproval(toolName: string, args: Record<string, any>, agentName: string): Promise<[boolean, string]> {
146
+ const level = this.getDangerLevel(toolName);
147
+
148
+ // Red-line check
149
+ const redline = this.checkRedline(toolName, args);
150
+ if (redline) {
151
+ log.warn("redline_blocked", { agent: agentName, tool: toolName, reason: redline });
152
+ return [false, redline];
153
+ }
154
+
155
+ // Safe — always allow
156
+ if (level === DangerLevel.SAFE) return [true, "safe"];
157
+
158
+ // Strict mode — deny all non-safe
159
+ if (this.approvalMode === "strict") {
160
+ return [false, `Strict mode: tool '${toolName}' (level ${level}) requires manual approval`];
161
+ }
162
+
163
+ // Auto mode — allow LOW, prompt for MEDIUM+, deny CRITICAL
164
+ if (this.approvalMode === "auto") {
165
+ if (level <= DangerLevel.LOW) return [true, "auto-low"];
166
+ if (level === DangerLevel.CRITICAL) return [false, `CRITICAL tool '${toolName}' requires explicit human approval`];
167
+ // MEDIUM/HIGH with auto mode => need callback
168
+ if (this.approvalCallback) {
169
+ const approved = await this.approvalCallback(toolName, args, level);
170
+ return [approved, approved ? "user-approved" : "user-denied"];
171
+ }
172
+ return [true, "auto-med"]; // no callback → auto-allow but log
173
+ }
174
+
175
+ // Interactive mode — prompt for LOW+
176
+ if (this.approvalCallback) {
177
+ const approved = await this.approvalCallback(toolName, args, level);
178
+ return [approved, approved ? "user-approved" : "user-denied"];
179
+ }
180
+ return [true, "no-callback"];
181
+ }
182
+
183
+ /** Record an audit entry. */
184
+ recordAudit(tool: string, agent: string, args: Record<string, any>, dangerLevel: DangerLevel, approved: boolean, resultPreview: string, durationMs: number, traceId: string): void {
185
+ const entry: AuditEntry = {
186
+ ts: new Date().toISOString(),
187
+ agent, tool, args, dangerLevel, approved,
188
+ result: resultPreview.slice(0, 500),
189
+ durationMs, traceId,
190
+ };
191
+ this.auditLog.push(entry);
192
+ if (this.auditLog.length > 5000) this.auditLog.shift();
193
+
194
+ if (approved) {
195
+ if (dangerLevel >= DangerLevel.HIGH) this.manualApprovedCount++;
196
+ else this.autoApprovedCount++;
197
+ } else {
198
+ this.deniedCount++;
199
+ }
200
+
201
+ log.info(dangerLevel >= DangerLevel.HIGH ? "dangerous_tool_executed" : "tool_executed", {
202
+ tool, agent, level: dangerLevel, approved,
203
+ });
204
+ }
205
+
206
+ /** Get summary statistics. */
207
+ getStats() {
208
+ return {
209
+ total: this.auditLog.length,
210
+ denied: this.deniedCount,
211
+ autoApproved: this.autoApprovedCount,
212
+ manualApproved: this.manualApprovedCount,
213
+ byLevel: {
214
+ safe: this.auditLog.filter(e => e.dangerLevel === DangerLevel.SAFE).length,
215
+ low: this.auditLog.filter(e => e.dangerLevel === DangerLevel.LOW).length,
216
+ medium: this.auditLog.filter(e => e.dangerLevel === DangerLevel.MEDIUM).length,
217
+ high: this.auditLog.filter(e => e.dangerLevel === DangerLevel.HIGH).length,
218
+ critical: this.auditLog.filter(e => e.dangerLevel === DangerLevel.CRITICAL).length,
219
+ },
220
+ lastDenied: this.auditLog.filter(e => !e.approved).slice(-5).map(e => `${e.tool}: ${e.result}`),
221
+ };
222
+ }
223
+
224
+ /** Install approval callback for interactive mode. */
225
+ setApprovalCallback(fn: (tool: string, args: Record<string, any>, level: DangerLevel) => Promise<boolean>) {
226
+ this.approvalCallback = fn;
227
+ }
228
+ }
229
+
230
+ /* ── Global security context ── */
231
+ let globalSecurity: SecurityContext | null = null;
232
+
233
+ export function getSecurity(): SecurityContext {
234
+ if (!globalSecurity) globalSecurity = new SecurityContext();
235
+ return globalSecurity;
236
+ }
237
+
238
+ export function resetSecurity(): void {
239
+ globalSecurity = null;
240
+ }
241
+
242
+ /** Red-line patterns for reference (used by tools to self-check). */
243
+ export { REDLINE_PATTERNS, REDLINE_COMMANDS };