aegiscode 3.1.10 → 3.4.2

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 (209) hide show
  1. package/bin/aegis.js +165 -0
  2. package/package.json +18 -86
  3. package/LICENSE +0 -21
  4. package/README.md +0 -394
  5. package/dist/main.js +0 -2171
  6. package/gui/run-gui.cjs +0 -13
  7. package/src/agent/Agent.ts +0 -903
  8. package/src/agent/SimpleAgent.ts +0 -48
  9. package/src/agent/index.ts +0 -54
  10. package/src/agent/orchestrator/AppBuilder.ts +0 -443
  11. package/src/agent/orchestrator/CouncilAgent.ts +0 -310
  12. package/src/agent/orchestrator/DiscussionRoom.ts +0 -462
  13. package/src/agent/orchestrator/OrchestratorAgent.ts +0 -637
  14. package/src/agent/orchestrator/index.ts +0 -38
  15. package/src/agent/orchestrator/utils.ts +0 -397
  16. package/src/agent/pricing.ts +0 -115
  17. package/src/agent/router.ts +0 -74
  18. package/src/agent/routerStats.ts +0 -121
  19. package/src/agent/types.ts +0 -318
  20. package/src/auth/login.ts +0 -383
  21. package/src/cli/config.ts +0 -189
  22. package/src/cli/index.ts +0 -17
  23. package/src/cli/middleware.ts +0 -119
  24. package/src/cli/types.ts +0 -75
  25. package/src/config/ConfigManager.ts +0 -587
  26. package/src/config/index.ts +0 -7
  27. package/src/config/types.ts +0 -584
  28. package/src/context/CompactionService.ts +0 -300
  29. package/src/context/ContextManager.ts +0 -450
  30. package/src/context/FileAnalyzer.ts +0 -267
  31. package/src/context/TokenCounter.ts +0 -265
  32. package/src/context/index.ts +0 -27
  33. package/src/context/storage/CacheStore.ts +0 -176
  34. package/src/context/storage/JSONLStore.ts +0 -201
  35. package/src/context/storage/MemoryStore.ts +0 -205
  36. package/src/context/storage/PersistentStore.ts +0 -327
  37. package/src/context/storage/index.ts +0 -9
  38. package/src/context/storage/pathUtils.ts +0 -114
  39. package/src/context/test.ts +0 -309
  40. package/src/context/types.ts +0 -268
  41. package/src/hooks/HookExecutor.ts +0 -434
  42. package/src/hooks/HookManager.ts +0 -596
  43. package/src/hooks/HookService.ts +0 -269
  44. package/src/hooks/Matcher.ts +0 -157
  45. package/src/hooks/index.ts +0 -63
  46. package/src/hooks/types.ts +0 -424
  47. package/src/main.tsx +0 -596
  48. package/src/mcp/HealthMonitor.ts +0 -150
  49. package/src/mcp/McpClient.ts +0 -491
  50. package/src/mcp/McpRegistry.ts +0 -321
  51. package/src/mcp/createMcpTool.ts +0 -251
  52. package/src/mcp/index.ts +0 -15
  53. package/src/mcp/server.ts +0 -334
  54. package/src/mcp/test-server.ts +0 -88
  55. package/src/mcp/test.ts +0 -372
  56. package/src/mcp/types.ts +0 -247
  57. package/src/memory/AgentMemoryBus.ts +0 -432
  58. package/src/memory/CloudSync.ts +0 -99
  59. package/src/memory/DriveSync.ts +0 -106
  60. package/src/memory/SharedMemory.ts +0 -951
  61. package/src/memory/index.ts +0 -14
  62. package/src/memory/machineFingerprint.ts +0 -40
  63. package/src/orchestrator/SubAgentMetadata.ts +0 -136
  64. package/src/prompts/builder.ts +0 -213
  65. package/src/prompts/default.ts +0 -144
  66. package/src/prompts/index.ts +0 -16
  67. package/src/prompts/plan.ts +0 -64
  68. package/src/prompts/test.ts +0 -78
  69. package/src/services/AnthropicChatService.ts +0 -341
  70. package/src/services/ChatService.ts +0 -347
  71. package/src/services/ClaudeCliChatService.ts +0 -256
  72. package/src/services/CloudSync.ts +0 -168
  73. package/src/services/CostLedger.ts +0 -211
  74. package/src/services/Heartbeat.ts +0 -135
  75. package/src/services/LearningCollector.ts +0 -291
  76. package/src/services/OllamaInstaller.ts +0 -342
  77. package/src/services/VersionChecker.ts +0 -445
  78. package/src/services/index.ts +0 -57
  79. package/src/services/streaming/OpenAIEventAdapter.ts +0 -126
  80. package/src/services/streaming/RenderingProfile.ts +0 -90
  81. package/src/services/streaming/StreamEventParser.ts +0 -181
  82. package/src/services/streaming/ThrottledRenderer.ts +0 -139
  83. package/src/services/streaming/TranscriptBuffer.ts +0 -574
  84. package/src/services/streaming/eventStatusMap.ts +0 -52
  85. package/src/services/streaming/index.ts +0 -46
  86. package/src/services/streaming/renderFormatting.ts +0 -79
  87. package/src/services/streaming/types.ts +0 -234
  88. package/src/skills/SkillLoader.ts +0 -126
  89. package/src/skills/SkillRegistry.ts +0 -366
  90. package/src/skills/index.ts +0 -48
  91. package/src/skills/types.ts +0 -146
  92. package/src/slash-commands/billing.ts +0 -70
  93. package/src/slash-commands/build.ts +0 -413
  94. package/src/slash-commands/builtinCommands.ts +0 -2733
  95. package/src/slash-commands/clone.ts +0 -242
  96. package/src/slash-commands/council.ts +0 -125
  97. package/src/slash-commands/custom/CustomCommandExecutor.ts +0 -143
  98. package/src/slash-commands/custom/CustomCommandLoader.ts +0 -251
  99. package/src/slash-commands/custom/CustomCommandRegistry.ts +0 -144
  100. package/src/slash-commands/custom/index.ts +0 -7
  101. package/src/slash-commands/debate.ts +0 -254
  102. package/src/slash-commands/gmail.ts +0 -105
  103. package/src/slash-commands/index.ts +0 -388
  104. package/src/slash-commands/mcpCommand.ts +0 -205
  105. package/src/slash-commands/types.ts +0 -201
  106. package/src/store/index.ts +0 -76
  107. package/src/store/selectors.ts +0 -246
  108. package/src/store/slices/appSlice.ts +0 -205
  109. package/src/store/slices/commandSlice.ts +0 -115
  110. package/src/store/slices/configSlice.ts +0 -46
  111. package/src/store/slices/focusSlice.ts +0 -64
  112. package/src/store/slices/index.ts +0 -9
  113. package/src/store/slices/sessionSlice.ts +0 -424
  114. package/src/store/streaming-buffer.ts +0 -425
  115. package/src/store/test.ts +0 -296
  116. package/src/store/types.ts +0 -274
  117. package/src/store/vanilla.ts +0 -186
  118. package/src/tools/builtin/bash.ts +0 -236
  119. package/src/tools/builtin/council.ts +0 -105
  120. package/src/tools/builtin/edit.ts +0 -213
  121. package/src/tools/builtin/glob.ts +0 -136
  122. package/src/tools/builtin/grep.ts +0 -263
  123. package/src/tools/builtin/index.ts +0 -61
  124. package/src/tools/builtin/memory.ts +0 -66
  125. package/src/tools/builtin/read.ts +0 -168
  126. package/src/tools/builtin/skill.ts +0 -97
  127. package/src/tools/builtin/snapshot.ts +0 -40
  128. package/src/tools/builtin/task.ts +0 -106
  129. package/src/tools/builtin/write.ts +0 -134
  130. package/src/tools/createTool.ts +0 -221
  131. package/src/tools/execution/ExecutionPipeline.ts +0 -263
  132. package/src/tools/execution/index.ts +0 -40
  133. package/src/tools/execution/stages/CacheStage.ts +0 -131
  134. package/src/tools/execution/stages/ConfirmationStage.ts +0 -203
  135. package/src/tools/execution/stages/DiscoveryStage.ts +0 -46
  136. package/src/tools/execution/stages/ExecutionStage.ts +0 -48
  137. package/src/tools/execution/stages/FormattingStage.ts +0 -44
  138. package/src/tools/execution/stages/HookStage.ts +0 -72
  139. package/src/tools/execution/stages/PermissionStage.ts +0 -287
  140. package/src/tools/execution/stages/PostHookStage.ts +0 -71
  141. package/src/tools/execution/stages/index.ts +0 -12
  142. package/src/tools/execution/test.ts +0 -266
  143. package/src/tools/execution/types.ts +0 -273
  144. package/src/tools/index.ts +0 -81
  145. package/src/tools/registry.ts +0 -304
  146. package/src/tools/schemas.ts +0 -109
  147. package/src/tools/test.ts +0 -220
  148. package/src/tools/types.ts +0 -175
  149. package/src/tools/validation/PermissionChecker.ts +0 -242
  150. package/src/tools/validation/SensitiveFileDetector.ts +0 -210
  151. package/src/tools/validation/index.ts +0 -11
  152. package/src/ui/App.tsx +0 -166
  153. package/src/ui/components/AegisInterface.tsx +0 -484
  154. package/src/ui/components/common/ChatSearch.tsx +0 -150
  155. package/src/ui/components/common/ErrorBoundary.tsx +0 -82
  156. package/src/ui/components/common/ExitMessage.tsx +0 -120
  157. package/src/ui/components/common/LoadingIndicator.tsx +0 -49
  158. package/src/ui/components/common/index.ts +0 -6
  159. package/src/ui/components/dialog/ConfirmationPrompt.tsx +0 -208
  160. package/src/ui/components/dialog/InteractiveSelector.tsx +0 -149
  161. package/src/ui/components/dialog/SetupWizard.tsx +0 -297
  162. package/src/ui/components/dialog/UpdatePrompt.tsx +0 -155
  163. package/src/ui/components/dialog/index.ts +0 -8
  164. package/src/ui/components/index.ts +0 -28
  165. package/src/ui/components/input/CommandSuggestions.tsx +0 -139
  166. package/src/ui/components/input/CustomTextInput.tsx +0 -220
  167. package/src/ui/components/input/InputArea.tsx +0 -361
  168. package/src/ui/components/input/PromptSuggestions.tsx +0 -66
  169. package/src/ui/components/input/index.ts +0 -6
  170. package/src/ui/components/layout/ChatStatusBar.tsx +0 -90
  171. package/src/ui/components/layout/ContextBar.tsx +0 -79
  172. package/src/ui/components/layout/MessageArea.tsx +0 -96
  173. package/src/ui/components/layout/MessageList.tsx +0 -647
  174. package/src/ui/components/layout/MessageSeparator.tsx +0 -26
  175. package/src/ui/components/layout/WelcomeMessage.tsx +0 -93
  176. package/src/ui/components/layout/index.ts +0 -7
  177. package/src/ui/components/markdown/CodeHighlighter.tsx +0 -292
  178. package/src/ui/components/markdown/MessageRenderer.tsx +0 -1211
  179. package/src/ui/components/markdown/index.ts +0 -8
  180. package/src/ui/components/markdown/parser.ts +0 -336
  181. package/src/ui/components/markdown/types.ts +0 -66
  182. package/src/ui/focus/FocusManager.ts +0 -137
  183. package/src/ui/focus/index.ts +0 -13
  184. package/src/ui/focus/types.ts +0 -54
  185. package/src/ui/focus/useFocus.ts +0 -75
  186. package/src/ui/hooks/index.ts +0 -11
  187. package/src/ui/hooks/useAgent.ts +0 -284
  188. package/src/ui/hooks/useCommandHistory.ts +0 -87
  189. package/src/ui/hooks/useCommandProcessor.ts +0 -443
  190. package/src/ui/hooks/useConfirmation.ts +0 -99
  191. package/src/ui/hooks/useCtrlCHandler.ts +0 -100
  192. package/src/ui/hooks/useInputBuffer.ts +0 -122
  193. package/src/ui/hooks/useTerminalSize.ts +0 -68
  194. package/src/ui/hooks/useTerminalWidth.ts +0 -5
  195. package/src/ui/hooks/useWindowedList.ts +0 -118
  196. package/src/ui/render-debugger.ts +0 -621
  197. package/src/ui/test.ts +0 -189
  198. package/src/ui/themes/ThemeManager.ts +0 -332
  199. package/src/ui/themes/aegisTheme.ts +0 -87
  200. package/src/ui/themes/darkTheme.ts +0 -87
  201. package/src/ui/themes/defaultTheme.ts +0 -85
  202. package/src/ui/themes/index.ts +0 -10
  203. package/src/ui/themes/lightTheme.ts +0 -89
  204. package/src/ui/themes/popularThemes.ts +0 -187
  205. package/src/ui/themes/types.ts +0 -130
  206. package/src/utils/clipboard.ts +0 -48
  207. package/src/utils/debug.ts +0 -43
  208. package/src/utils/environment.ts +0 -68
  209. package/src/utils/index.ts +0 -10
@@ -1,203 +0,0 @@
1
- /**
2
- * Confirmation Stage - 用户确认阶段
3
- *
4
- */
5
-
6
- import type {
7
- PipelineStage,
8
- ToolExecution,
9
- ConfirmationDetails,
10
- } from '../types.js';
11
-
12
- export class ConfirmationStage implements PipelineStage {
13
- readonly name = 'confirmation';
14
-
15
- constructor(
16
- private sessionApprovals: Set<string>,
17
- private sessionDenials?: Set<string>
18
- ) {}
19
-
20
- async process(execution: ToolExecution): Promise<void> {
21
- // 如果不需要确认,直接通
22
- if (!execution._internal.needsConfirmation) {
23
- return;
24
- }
25
-
26
- const forceConfirmation = execution._internal.forceConfirmation === true;
27
-
28
- // 模型级设置:禁用确认提示时直接放行(除非该操作被标记为强制确认
29
- if (!forceConfirmation && execution.context.requireConfirmation === false) {
30
- return;
31
- }
32
-
33
- const tool = execution._internal.tool;
34
- if (!tool) {
35
- execution.abort('Tool not found in execution context');
36
- return;
37
- }
38
-
39
- // 检查是否已在会话中拒绝
40
- const signature = execution._internal.permissionSignature;
41
- if (signature && this.sessionDenials?.has(signature)) {
42
- execution.abort('Not executed: you tried this exact action earlier in this session and the user declined it. Choose a different approach.');
43
- return;
44
- }
45
-
46
- // 检查是否已在会话中批准(强制确认的操作不允许通过"永远允许"跳过
47
- if (!forceConfirmation && signature && this.sessionApprovals.has(signature)) {
48
- return;
49
- }
50
-
51
- // 构建确认详
52
- const confirmationDetails: ConfirmationDetails = {
53
- title: `Permission Required: ${tool.name}`,
54
- message: execution._internal.confirmationReason || 'This operation requires your confirmation',
55
- details: this.generatePreview(execution),
56
- risks: this.extractRisks(execution),
57
- affectedFiles: this.getAffectedPaths(execution),
58
- };
59
-
60
- // 请求用户确
61
- const handler = execution.context.confirmationHandler;
62
- if (!handler) {
63
- // 无确认处理器,默认拒
64
- // Worded to avoid "permission/approval/dialog" vocabulary — this string becomes
65
- // llmContent in a real tool_result, and re-using that vocabulary cluster is what
66
- // lets the model later free-associate a hallucinated "blocked, click Allow" reply
67
- // in an unrelated turn with no tool call at all.
68
- execution.abort('Not executed: this session cannot ask the user a yes/no question right now. Retrying this exact action will not help — explain what you wanted to do in your text response instead, or ask the user directly.');
69
- return;
70
- }
71
-
72
- const response = await handler.requestConfirmation(confirmationDetails);
73
-
74
- if (!response.approved) {
75
- // 如果用户选择"永远拒绝",保存到会话拒绝列
76
- if (response.scope === 'session' && signature && this.sessionDenials) {
77
- this.sessionDenials.add(signature);
78
- }
79
- execution.abort(`Not executed: the user declined this action${response.reason ? ` — ${response.reason}` : ''}. Choose a different approach.`);
80
- return;
81
- }
82
-
83
- // 如果用户选择"永远允许",保存到会话批准列
84
- if (response.scope === 'session' && signature) {
85
- this.sessionApprovals.add(signature);
86
- }
87
- }
88
-
89
- /**
90
- *
91
- */
92
- private generatePreview(execution: ToolExecution): string | undefined {
93
- const { toolName, params } = execution;
94
-
95
- switch (toolName) {
96
- case 'Edit': {
97
- const oldString = params.old_string as string;
98
- const newString = params.new_string as string;
99
- const filePath = params.file_path as string;
100
-
101
- return `**File:** ${filePath}
102
-
103
- **Before:**
104
- \`\`\`
105
- ${this.truncate(oldString, 10)}
106
- \`\`\`
107
-
108
- **After:**
109
- \`\`\`
110
- ${this.truncate(newString, 10)}
111
- \`\`\``;
112
- }
113
-
114
- case 'Write': {
115
- const content = params.contents as string;
116
- const filePath = params.file_path as string;
117
-
118
- return `**File:** ${filePath}
119
-
120
- **Content Preview:**
121
- \`\`\`
122
- ${this.truncate(content, 20)}
123
- \`\`\``;
124
- }
125
-
126
- case 'Bash': {
127
- const command = params.command as string;
128
- const cwd = params.working_directory as string;
129
-
130
- return `**Command:** \`${command}\`${cwd ? `\n**Directory:** ${cwd}` : ''}`;
131
- }
132
-
133
- default:
134
- return undefined;
135
- }
136
- }
137
-
138
- /**
139
- *
140
- */
141
- private extractRisks(execution: ToolExecution): string[] {
142
- const risks: string[] = [];
143
- const { toolName, params } = execution;
144
- const tool = execution._internal.tool;
145
-
146
- // 基于工具类型的风
147
- if (tool?.kind === 'write') {
148
- risks.push('This operation will modify files');
149
- } else if (tool?.kind === 'execute') {
150
- risks.push('This operation will execute system commands');
151
- }
152
-
153
- // 基于参数的风
154
- if (toolName === 'Bash') {
155
- const command = params.command as string;
156
- if (command.includes('rm')) {
157
- risks.push('Command may delete files');
158
- }
159
- if (command.includes('sudo')) {
160
- risks.push('Command requires elevated privileges');
161
- }
162
- if (command.includes('|')) {
163
- risks.push('Command uses piping');
164
- }
165
- }
166
-
167
- // 检查敏感文
168
- const confirmReason = execution._internal.confirmationReason || '';
169
- if (confirmReason.includes('Sensitive file')) {
170
- risks.push('Operation involves sensitive files');
171
- }
172
-
173
- return risks;
174
- }
175
-
176
- /**
177
- *
178
- */
179
- private getAffectedPaths(execution: ToolExecution): string[] {
180
- const params = execution.params;
181
- const pathKeys = ['file_path', 'path', 'target', 'destination'];
182
- const paths: string[] = [];
183
-
184
- for (const key of pathKeys) {
185
- if (typeof params[key] === 'string') {
186
- paths.push(params[key] as string);
187
- }
188
- }
189
-
190
- return paths;
191
- }
192
-
193
- /**
194
- *
195
- */
196
- private truncate(text: string, maxLines: number = 10): string {
197
- const lines = text.split('\n');
198
- if (lines.length <= maxLines) {
199
- return text;
200
- }
201
- return lines.slice(0, maxLines).join('\n') + `\n... (${lines.length - maxLines} more lines)`;
202
- }
203
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * Discovery Stage - 工具发现阶段
3
- *
4
- * Per-model allowedTools/disallowedTools filtering added here.
5
- */
6
-
7
- import type { PipelineStage, ToolExecution } from '../types.js';
8
- import type { ToolRegistry } from '../../registry.js';
9
-
10
- export class DiscoveryStage implements PipelineStage {
11
- readonly name = 'discovery';
12
-
13
- constructor(
14
- private registry: ToolRegistry,
15
- private allowedTools?: string[],
16
- private disallowedTools?: string[],
17
- ) {}
18
-
19
- async process(execution: ToolExecution): Promise<void> {
20
- // 1. Check per-model disallowed tools
21
- if (this.disallowedTools?.includes(execution.toolName)) {
22
- execution.abort(`Tool "${execution.toolName}" is disallowed for the current model`);
23
- return;
24
- }
25
-
26
- // 2. Check per-model allowed tools (if set, only these are allowed)
27
- if (this.allowedTools && this.allowedTools.length > 0) {
28
- if (!this.allowedTools.includes(execution.toolName)) {
29
- execution.abort(
30
- `Tool "${execution.toolName}" is not in the allowed tools list for the current model`
31
- );
32
- return;
33
- }
34
- }
35
-
36
- const tool = this.registry.get(execution.toolName);
37
-
38
- if (!tool) {
39
- execution.abort(`Tool "${execution.toolName}" not found in registry`);
40
- return;
41
- }
42
-
43
- // 将工具实例附加到执行上下
44
- execution._internal.tool = tool;
45
- }
46
- }
@@ -1,48 +0,0 @@
1
- /**
2
- * Execution Stage - 实际执行阶段
3
- *
4
- */
5
-
6
- import type { PipelineStage, ToolExecution } from '../types.js';
7
- import type { ExecutionContext } from '../../types.js';
8
- import type { CacheStage } from './CacheStage.js';
9
-
10
- export class ExecutionStage implements PipelineStage {
11
- readonly name = 'execution';
12
-
13
- constructor(private cacheStage?: CacheStage) {}
14
-
15
- async process(execution: ToolExecution): Promise<void> {
16
- // CacheStage already set the result on a cache hit — nothing to run.
17
- if (execution.getResult()) return;
18
-
19
- const tool = execution._internal.tool;
20
-
21
- if (!tool) {
22
- execution.abort('Tool not found in execution context');
23
- return;
24
- }
25
-
26
- try {
27
- // 构建执行上下
28
- const context: ExecutionContext = {
29
- sessionId: execution.context.sessionId,
30
- signal: execution.context.signal,
31
- cwd: execution.context.workspaceRoot,
32
- };
33
-
34
- // 执行工
35
- const result = await tool.execute(execution.params, context);
36
-
37
- // 设置结
38
- execution.setResult(result);
39
-
40
- const cacheKey = execution._internal.cacheKey;
41
- if (cacheKey) this.cacheStage?.cacheResult(cacheKey, result);
42
- } catch (error) {
43
- // 处理执行错
44
- const errorMessage = error instanceof Error ? error.message : String(error);
45
- execution.abort(`Tool execution failed: ${errorMessage}`);
46
- }
47
- }
48
- }
@@ -1,44 +0,0 @@
1
- /**
2
- * Formatting Stage - 结果格式化阶段
3
- *
4
- */
5
-
6
- import type { PipelineStage, ToolExecution } from '../types.js';
7
-
8
- export class FormattingStage implements PipelineStage {
9
- readonly name = 'formatting';
10
-
11
- async process(execution: ToolExecution): Promise<void> {
12
- const result = execution.getResult();
13
-
14
- if (!result) {
15
- // 没有结果(可能是被中止了),不处
16
- return;
17
- }
18
-
19
- // 确保结果格式正
20
- if (!result.llmContent) {
21
- result.llmContent = result.success
22
- ? 'Execution completed successfully'
23
- : 'Execution failed';
24
- }
25
-
26
- if (!result.displayContent) {
27
- result.displayContent = result.success
28
- ? `✅ ${execution.toolName} completed`
29
- : `❌ ${execution.toolName} failed`;
30
- }
31
-
32
- // 添加执行元数
33
- result.metadata = {
34
- ...result.metadata,
35
- executionId: execution.context.sessionId,
36
- toolName: execution.toolName,
37
- timestamp: Date.now(),
38
- permissionMode: execution.context.permissionMode,
39
- };
40
-
41
- // 更新结
42
- execution.setResult(result);
43
- }
44
- }
@@ -1,72 +0,0 @@
1
- /**
2
- * Hook Stage (Pre) - PreToolUse Hooks 执行阶段
3
- *
4
- */
5
-
6
- import { nanoid } from 'nanoid';
7
- import type { PipelineStage, ToolExecution } from '../types.js';
8
- import { onPreToolUse } from '../../../hooks/index.js';
9
-
10
- export class HookStage implements PipelineStage {
11
- readonly name = 'hook';
12
-
13
- async process(execution: ToolExecution): Promise<void> {
14
- const tool = execution._internal.tool;
15
- if (!tool) {
16
- return;
17
- }
18
-
19
- // 生成唯一的 toolUseId(PostToolUse 阶段复
20
- const toolUseId = execution.context.messageId || `tool_${nanoid()}`;
21
- execution._internal.hookToolUseId = toolUseId;
22
-
23
- // 执行 PreToolUse hooks(通
24
- const result = await onPreToolUse(
25
- tool.name,
26
- toolUseId,
27
- execution.params as Record<string, unknown>,
28
- execution.context.sessionId || 'unknown',
29
- execution.context.workspaceRoot || process.cwd(),
30
- execution.context.permissionMode
31
- );
32
-
33
- // 处理 Hook 决
34
- if (result.decision === 'deny') {
35
- // 直接拒绝,中止执
36
- execution.abort(result.reason || 'Hook blocked execution');
37
- return;
38
- }
39
-
40
- if (result.decision === 'ask') {
41
- // 标记需要用户确认(传递给 Confirmation 阶
42
- execution._internal.needsConfirmation = true;
43
- execution._internal.confirmationReason =
44
- result.reason || 'Hook requires confirmation';
45
- return;
46
- }
47
-
48
- // decision === 'allow':应用修改后的输
49
- if (result.modifiedInput) {
50
- const newParams = { ...execution.params, ...result.modifiedInput };
51
-
52
- // 重新验证修改后的参
53
- if (tool.build) {
54
- try {
55
- tool.build(newParams);
56
- // 更新参
57
- execution.params = newParams;
58
- } catch (err) {
59
- execution.abort(
60
- `Hook modified parameters are invalid: ${err instanceof Error ? err.message : String(err)}`
61
- );
62
- return;
63
- }
64
- }
65
- }
66
-
67
- // 输出警告信
68
- if (result.warning) {
69
- console.warn(`[Hook Warning] ${result.warning}`);
70
- }
71
- }
72
- }
@@ -1,287 +0,0 @@
1
- /**
2
- * Permission Stage - 权限检查阶段
3
- *
4
- */
5
-
6
- import { ToolKind } from '../../types.js';
7
- import {
8
- PermissionMode,
9
- PermissionResult,
10
- type PipelineStage,
11
- type ToolExecution,
12
- type PermissionConfig,
13
- type PermissionCheckResult,
14
- } from '../types.js';
15
- import { PermissionChecker } from '../../validation/PermissionChecker.js';
16
- import { SensitiveFileDetector, SensitivityLevel } from '../../validation/SensitiveFileDetector.js';
17
- import { onPermissionRequest } from '../../../hooks/index.js';
18
-
19
- export class PermissionStage implements PipelineStage {
20
- readonly name = 'permission';
21
- private permissionChecker: PermissionChecker;
22
- private defaultMode: PermissionMode;
23
-
24
- constructor(
25
- config?: Partial<PermissionConfig>,
26
- private sessionApprovals?: Set<string>,
27
- private sessionDenials?: Set<string>,
28
- defaultMode: PermissionMode = PermissionMode.DEFAULT
29
- ) {
30
- this.permissionChecker = new PermissionChecker(config);
31
- this.defaultMode = defaultMode;
32
- }
33
-
34
- async process(execution: ToolExecution): Promise<void> {
35
- const tool = execution._internal.tool;
36
-
37
- if (!tool) {
38
- execution.abort('Tool not found in execution context');
39
- return;
40
- }
41
-
42
- // 1. 创建工具调用实例(含 Zod 验
43
- try {
44
- const invocation = tool.build(execution.params);
45
- execution._internal.invocation = invocation;
46
- } catch (error) {
47
- execution.abort(
48
- `Parameter validation failed: ${error instanceof Error ? error.message : String(error)}`
49
- );
50
- return;
51
- }
52
-
53
- // 2. 构建权限签
54
- const signature = PermissionChecker.buildSignature({
55
- toolName: tool.name,
56
- params: execution.params,
57
- tool,
58
- });
59
- execution._internal.permissionSignature = signature;
60
-
61
- // 3. 检查会话拒绝列
62
- if (this.sessionDenials?.has(signature)) {
63
- // Phrased to avoid "permission/denied" vocabulary: this exact string becomes
64
- // llmContent in a real tool_result, and the model can later free-associate a
65
- // hallucinated continuation of it in an unrelated turn with no tool call.
66
- execution.abort('Not executed: you tried this exact action earlier in this session and the user declined it. Choose a different approach.');
67
- return;
68
- }
69
-
70
- // 4. 检查会话批准列
71
- if (this.sessionApprovals?.has(signature)) {
72
- // 已在会话中批准,跳过权限检
73
- return;
74
- }
75
-
76
- // 5. 执行权限检
77
- let checkResult = this.permissionChecker.check({
78
- toolName: tool.name,
79
- params: execution.params,
80
- tool,
81
- });
82
-
83
- // 6. 应用权限模式覆
84
- const currentMode = execution.context.permissionMode || this.defaultMode;
85
- checkResult = this.applyModeOverrides(tool.kind, checkResult, currentMode);
86
-
87
- // 7. 根据结果采取行
88
- switch (checkResult.result) {
89
- case PermissionResult.DENY:
90
- execution.abort(checkResult.reason || 'Not executed: this action is not allowed under current settings. Choose a different approach.');
91
- return;
92
-
93
- case PermissionResult.ASK:
94
- // 执
95
- const hookDecision = await this.executePermissionHook(execution, tool.name);
96
- if (hookDecision === 'approve') {
97
- // Hook 批准,跳过确
98
- break;
99
- } else if (hookDecision === 'deny') {
100
- // Hook 拒
101
- execution.abort('Not executed: a configured hook excludes this action. Choose a different approach.');
102
- return;
103
- }
104
- // Hook 返回 ask 或无 hook,继续标记需要确
105
- execution._internal.needsConfirmation = true;
106
- execution._internal.confirmationReason =
107
- checkResult.reason || 'This operation requires confirmation';
108
- break;
109
-
110
- case PermissionResult.ALLOW:
111
- // 继续执
112
- break;
113
- }
114
-
115
- // 7. 额外安全检查:敏感文
116
- this.checkSensitiveFiles(execution);
117
-
118
- // 8. 额外安全检查:终端操纵(tmux/screen 注入按键到另一个会话
119
- this.checkTerminalPiloting(execution);
120
- }
121
-
122
- /**
123
- * Detects Bash commands that pilot another terminal session by injecting
124
- * keystrokes (tmux send-keys, screen -X stuff, etc.). This is the same
125
- * vector a model used to mutate ~/.aegiscode/config.json via a second
126
- * live aegis instance while looping unattended — the injected keystrokes
127
- * are indistinguishable from real user input to the receiving process, so
128
- * the only place to catch this is here, before the command runs. Forces
129
- * confirmation regardless of requireConfirmation or session approvals.
130
- */
131
- private checkTerminalPiloting(execution: ToolExecution): void {
132
- const tool = execution._internal.tool;
133
- if (!tool || tool.name !== 'Bash') return;
134
-
135
- const command = execution.params.command;
136
- if (typeof command !== 'string') return;
137
-
138
- const PILOTING_PATTERNS = [
139
- /\btmux\s+send-keys\b/,
140
- /\btmux\s+(?:run-shell|wait-for)\b/,
141
- /\bscreen\s+-S\s+\S+\s+-X\s+stuff\b/,
142
- /\bexpect\s+-c\b/,
143
- ];
144
-
145
- if (PILOTING_PATTERNS.some((re) => re.test(command))) {
146
- execution._internal.needsConfirmation = true;
147
- execution._internal.forceConfirmation = true;
148
- execution._internal.confirmationReason =
149
- 'This command injects input into another terminal session — it can mutate shared ' +
150
- 'config/files exactly like a real user typing, bypassing normal confirmation settings.';
151
- }
152
- }
153
-
154
- /**
155
- *
156
- */
157
- private applyModeOverrides(
158
- toolKind: ToolKind,
159
- checkResult: PermissionCheckResult,
160
- permissionMode: PermissionMode
161
- ): PermissionCheckResult {
162
- // 1. YOLO 模式:批准所有(最高优先
163
- if (permissionMode === PermissionMode.YOLO) {
164
- return {
165
- result: PermissionResult.ALLOW,
166
- matchedRule: 'mode:yolo',
167
- reason: 'YOLO mode: auto-approve all operations',
168
- };
169
- }
170
-
171
- // 2. PLAN 模式:拒绝非只读工
172
- if (permissionMode === PermissionMode.PLAN) {
173
- if (toolKind !== ToolKind.ReadOnly) {
174
- return {
175
- result: PermissionResult.DENY,
176
- matchedRule: 'mode:plan',
177
- reason: 'Plan mode: only read-only tools allowed',
178
- };
179
- }
180
- }
181
-
182
- // 3. 已被 deny 规则拒绝,不覆
183
- if (checkResult.result === PermissionResult.DENY) {
184
- return checkResult;
185
- }
186
-
187
- // 4. 已被 allow 规则批准,不覆
188
- if (checkResult.result === PermissionResult.ALLOW) {
189
- return checkResult;
190
- }
191
-
192
- // 5. 只读工具:所有模式下都批
193
- if (toolKind === ToolKind.ReadOnly) {
194
- return {
195
- result: PermissionResult.ALLOW,
196
- matchedRule: `mode:${permissionMode}:readonly`,
197
- reason: 'Read-only tools are auto-approved',
198
- };
199
- }
200
-
201
- // 6. AUTO_EDIT 模式:批准 Write 工
202
- if (permissionMode === PermissionMode.AUTO_EDIT && toolKind === ToolKind.Write) {
203
- return {
204
- result: PermissionResult.ALLOW,
205
- matchedRule: 'mode:autoEdit:write',
206
- reason: 'AUTO_EDIT mode: auto-approve write tools',
207
- };
208
- }
209
-
210
- // 7. 其他情况:保持原检查结果(通常
211
- return checkResult;
212
- }
213
-
214
- /**
215
- *
216
- */
217
- private checkSensitiveFiles(execution: ToolExecution): void {
218
- const tool = execution._internal.tool;
219
- if (!tool) return;
220
-
221
- // 只检查写入相关工
222
- if (tool.kind === ToolKind.ReadOnly) return;
223
-
224
- // 获取受影响的文件路
225
- const affectedPaths = this.getAffectedPaths(execution);
226
- if (affectedPaths.length === 0) return;
227
-
228
- // 检查敏感文
229
- const sensitiveFiles = SensitiveFileDetector.filterSensitive(
230
- affectedPaths,
231
- SensitivityLevel.MEDIUM
232
- );
233
-
234
- if (sensitiveFiles.length === 0) return;
235
-
236
- // 高敏感文件直接拒
237
- const highSensitive = sensitiveFiles.filter(
238
- f => f.result.level === SensitivityLevel.HIGH
239
- );
240
-
241
- if (highSensitive.length > 0) {
242
- const files = highSensitive.map(f => f.path).join(', ');
243
- execution.abort(`Not executed: these files are excluded for safety — ${files}. Choose a different approach.`);
244
- return;
245
- }
246
-
247
- // 中敏感文件需要确
248
- execution._internal.needsConfirmation = true;
249
- const reasons = sensitiveFiles.map(f => `${f.path}: ${f.result.reason}`);
250
- execution._internal.confirmationReason = `Sensitive file access detected:\n${reasons.join('\n')}`;
251
- }
252
-
253
- /**
254
- *
255
- */
256
- private getAffectedPaths(execution: ToolExecution): string[] {
257
- const params = execution.params;
258
-
259
- // 从常见参数名中提取路
260
- const pathKeys = ['file_path', 'path', 'target', 'destination'];
261
- const paths: string[] = [];
262
-
263
- for (const key of pathKeys) {
264
- if (typeof params[key] === 'string') {
265
- paths.push(params[key] as string);
266
- }
267
- }
268
-
269
- return paths;
270
- }
271
-
272
- /**
273
- *
274
- */
275
- private async executePermissionHook(
276
- execution: ToolExecution,
277
- toolName: string
278
- ): Promise<'approve' | 'deny' | 'ask'> {
279
- return onPermissionRequest(
280
- toolName,
281
- execution.params as Record<string, unknown>,
282
- execution.context.sessionId || 'unknown',
283
- execution.context.workspaceRoot || process.cwd(),
284
- execution.context.permissionMode
285
- );
286
- }
287
- }