aegiscode 3.1.7 → 3.1.10

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 (206) hide show
  1. package/README.md +23 -15
  2. package/dist/main.js +555 -556
  3. package/package.json +4 -2
  4. package/src/agent/Agent.ts +903 -0
  5. package/src/agent/SimpleAgent.ts +48 -0
  6. package/src/agent/index.ts +54 -0
  7. package/src/agent/orchestrator/AppBuilder.ts +443 -0
  8. package/src/agent/orchestrator/CouncilAgent.ts +310 -0
  9. package/src/agent/orchestrator/DiscussionRoom.ts +462 -0
  10. package/src/agent/orchestrator/OrchestratorAgent.ts +637 -0
  11. package/src/agent/orchestrator/index.ts +38 -0
  12. package/src/agent/orchestrator/utils.ts +397 -0
  13. package/src/agent/pricing.ts +115 -0
  14. package/src/agent/router.ts +74 -0
  15. package/src/agent/routerStats.ts +121 -0
  16. package/src/agent/types.ts +318 -0
  17. package/src/auth/login.ts +383 -0
  18. package/src/cli/config.ts +189 -0
  19. package/src/cli/index.ts +17 -0
  20. package/src/cli/middleware.ts +119 -0
  21. package/src/cli/types.ts +75 -0
  22. package/src/config/ConfigManager.ts +587 -0
  23. package/src/config/index.ts +7 -0
  24. package/src/config/types.ts +584 -0
  25. package/src/context/CompactionService.ts +300 -0
  26. package/src/context/ContextManager.ts +450 -0
  27. package/src/context/FileAnalyzer.ts +267 -0
  28. package/src/context/TokenCounter.ts +265 -0
  29. package/src/context/index.ts +27 -0
  30. package/src/context/storage/CacheStore.ts +176 -0
  31. package/src/context/storage/JSONLStore.ts +201 -0
  32. package/src/context/storage/MemoryStore.ts +205 -0
  33. package/src/context/storage/PersistentStore.ts +327 -0
  34. package/src/context/storage/index.ts +9 -0
  35. package/src/context/storage/pathUtils.ts +114 -0
  36. package/src/context/test.ts +309 -0
  37. package/src/context/types.ts +268 -0
  38. package/src/hooks/HookExecutor.ts +434 -0
  39. package/src/hooks/HookManager.ts +596 -0
  40. package/src/hooks/HookService.ts +269 -0
  41. package/src/hooks/Matcher.ts +157 -0
  42. package/src/hooks/index.ts +63 -0
  43. package/src/hooks/types.ts +424 -0
  44. package/src/main.tsx +596 -0
  45. package/src/mcp/HealthMonitor.ts +150 -0
  46. package/src/mcp/McpClient.ts +491 -0
  47. package/src/mcp/McpRegistry.ts +321 -0
  48. package/src/mcp/createMcpTool.ts +251 -0
  49. package/src/mcp/index.ts +15 -0
  50. package/src/mcp/server.ts +334 -0
  51. package/src/mcp/test-server.ts +88 -0
  52. package/src/mcp/test.ts +372 -0
  53. package/src/mcp/types.ts +247 -0
  54. package/src/memory/AgentMemoryBus.ts +432 -0
  55. package/src/memory/CloudSync.ts +99 -0
  56. package/src/memory/DriveSync.ts +106 -0
  57. package/src/memory/SharedMemory.ts +951 -0
  58. package/src/memory/index.ts +14 -0
  59. package/src/memory/machineFingerprint.ts +40 -0
  60. package/src/orchestrator/SubAgentMetadata.ts +136 -0
  61. package/src/prompts/builder.ts +213 -0
  62. package/src/prompts/default.ts +144 -0
  63. package/src/prompts/index.ts +16 -0
  64. package/src/prompts/plan.ts +64 -0
  65. package/src/prompts/test.ts +78 -0
  66. package/src/services/AnthropicChatService.ts +341 -0
  67. package/src/services/ChatService.ts +347 -0
  68. package/src/services/ClaudeCliChatService.ts +256 -0
  69. package/src/services/CloudSync.ts +168 -0
  70. package/src/services/CostLedger.ts +211 -0
  71. package/src/services/Heartbeat.ts +135 -0
  72. package/src/services/LearningCollector.ts +291 -0
  73. package/src/services/OllamaInstaller.ts +342 -0
  74. package/src/services/VersionChecker.ts +445 -0
  75. package/src/services/index.ts +57 -0
  76. package/src/services/streaming/OpenAIEventAdapter.ts +126 -0
  77. package/src/services/streaming/RenderingProfile.ts +90 -0
  78. package/src/services/streaming/StreamEventParser.ts +181 -0
  79. package/src/services/streaming/ThrottledRenderer.ts +139 -0
  80. package/src/services/streaming/TranscriptBuffer.ts +574 -0
  81. package/src/services/streaming/eventStatusMap.ts +52 -0
  82. package/src/services/streaming/index.ts +46 -0
  83. package/src/services/streaming/renderFormatting.ts +79 -0
  84. package/src/services/streaming/types.ts +234 -0
  85. package/src/skills/SkillLoader.ts +126 -0
  86. package/src/skills/SkillRegistry.ts +366 -0
  87. package/src/skills/index.ts +48 -0
  88. package/src/skills/types.ts +146 -0
  89. package/src/slash-commands/billing.ts +70 -0
  90. package/src/slash-commands/build.ts +413 -0
  91. package/src/slash-commands/builtinCommands.ts +2733 -0
  92. package/src/slash-commands/clone.ts +242 -0
  93. package/src/slash-commands/council.ts +125 -0
  94. package/src/slash-commands/custom/CustomCommandExecutor.ts +143 -0
  95. package/src/slash-commands/custom/CustomCommandLoader.ts +251 -0
  96. package/src/slash-commands/custom/CustomCommandRegistry.ts +144 -0
  97. package/src/slash-commands/custom/index.ts +7 -0
  98. package/src/slash-commands/debate.ts +254 -0
  99. package/src/slash-commands/gmail.ts +105 -0
  100. package/src/slash-commands/index.ts +388 -0
  101. package/src/slash-commands/mcpCommand.ts +205 -0
  102. package/src/slash-commands/types.ts +201 -0
  103. package/src/store/index.ts +76 -0
  104. package/src/store/selectors.ts +246 -0
  105. package/src/store/slices/appSlice.ts +205 -0
  106. package/src/store/slices/commandSlice.ts +115 -0
  107. package/src/store/slices/configSlice.ts +46 -0
  108. package/src/store/slices/focusSlice.ts +64 -0
  109. package/src/store/slices/index.ts +9 -0
  110. package/src/store/slices/sessionSlice.ts +424 -0
  111. package/src/store/streaming-buffer.ts +425 -0
  112. package/src/store/test.ts +296 -0
  113. package/src/store/types.ts +274 -0
  114. package/src/store/vanilla.ts +186 -0
  115. package/src/tools/builtin/bash.ts +236 -0
  116. package/src/tools/builtin/council.ts +105 -0
  117. package/src/tools/builtin/edit.ts +213 -0
  118. package/src/tools/builtin/glob.ts +136 -0
  119. package/src/tools/builtin/grep.ts +263 -0
  120. package/src/tools/builtin/index.ts +61 -0
  121. package/src/tools/builtin/memory.ts +66 -0
  122. package/src/tools/builtin/read.ts +168 -0
  123. package/src/tools/builtin/skill.ts +97 -0
  124. package/src/tools/builtin/snapshot.ts +40 -0
  125. package/src/tools/builtin/task.ts +106 -0
  126. package/src/tools/builtin/write.ts +134 -0
  127. package/src/tools/createTool.ts +221 -0
  128. package/src/tools/execution/ExecutionPipeline.ts +263 -0
  129. package/src/tools/execution/index.ts +40 -0
  130. package/src/tools/execution/stages/CacheStage.ts +131 -0
  131. package/src/tools/execution/stages/ConfirmationStage.ts +203 -0
  132. package/src/tools/execution/stages/DiscoveryStage.ts +46 -0
  133. package/src/tools/execution/stages/ExecutionStage.ts +48 -0
  134. package/src/tools/execution/stages/FormattingStage.ts +44 -0
  135. package/src/tools/execution/stages/HookStage.ts +72 -0
  136. package/src/tools/execution/stages/PermissionStage.ts +287 -0
  137. package/src/tools/execution/stages/PostHookStage.ts +71 -0
  138. package/src/tools/execution/stages/index.ts +12 -0
  139. package/src/tools/execution/test.ts +266 -0
  140. package/src/tools/execution/types.ts +273 -0
  141. package/src/tools/index.ts +81 -0
  142. package/src/tools/registry.ts +304 -0
  143. package/src/tools/schemas.ts +109 -0
  144. package/src/tools/test.ts +220 -0
  145. package/src/tools/types.ts +175 -0
  146. package/src/tools/validation/PermissionChecker.ts +242 -0
  147. package/src/tools/validation/SensitiveFileDetector.ts +210 -0
  148. package/src/tools/validation/index.ts +11 -0
  149. package/src/ui/App.tsx +166 -0
  150. package/src/ui/components/AegisInterface.tsx +484 -0
  151. package/src/ui/components/common/ChatSearch.tsx +150 -0
  152. package/src/ui/components/common/ErrorBoundary.tsx +82 -0
  153. package/src/ui/components/common/ExitMessage.tsx +120 -0
  154. package/src/ui/components/common/LoadingIndicator.tsx +49 -0
  155. package/src/ui/components/common/index.ts +6 -0
  156. package/src/ui/components/dialog/ConfirmationPrompt.tsx +208 -0
  157. package/src/ui/components/dialog/InteractiveSelector.tsx +149 -0
  158. package/src/ui/components/dialog/SetupWizard.tsx +297 -0
  159. package/src/ui/components/dialog/UpdatePrompt.tsx +155 -0
  160. package/src/ui/components/dialog/index.ts +8 -0
  161. package/src/ui/components/index.ts +28 -0
  162. package/src/ui/components/input/CommandSuggestions.tsx +139 -0
  163. package/src/ui/components/input/CustomTextInput.tsx +220 -0
  164. package/src/ui/components/input/InputArea.tsx +361 -0
  165. package/src/ui/components/input/PromptSuggestions.tsx +66 -0
  166. package/src/ui/components/input/index.ts +6 -0
  167. package/src/ui/components/layout/ChatStatusBar.tsx +90 -0
  168. package/src/ui/components/layout/ContextBar.tsx +79 -0
  169. package/src/ui/components/layout/MessageArea.tsx +96 -0
  170. package/src/ui/components/layout/MessageList.tsx +647 -0
  171. package/src/ui/components/layout/MessageSeparator.tsx +26 -0
  172. package/src/ui/components/layout/WelcomeMessage.tsx +93 -0
  173. package/src/ui/components/layout/index.ts +7 -0
  174. package/src/ui/components/markdown/CodeHighlighter.tsx +292 -0
  175. package/src/ui/components/markdown/MessageRenderer.tsx +1211 -0
  176. package/src/ui/components/markdown/index.ts +8 -0
  177. package/src/ui/components/markdown/parser.ts +336 -0
  178. package/src/ui/components/markdown/types.ts +66 -0
  179. package/src/ui/focus/FocusManager.ts +137 -0
  180. package/src/ui/focus/index.ts +13 -0
  181. package/src/ui/focus/types.ts +54 -0
  182. package/src/ui/focus/useFocus.ts +75 -0
  183. package/src/ui/hooks/index.ts +11 -0
  184. package/src/ui/hooks/useAgent.ts +284 -0
  185. package/src/ui/hooks/useCommandHistory.ts +87 -0
  186. package/src/ui/hooks/useCommandProcessor.ts +443 -0
  187. package/src/ui/hooks/useConfirmation.ts +99 -0
  188. package/src/ui/hooks/useCtrlCHandler.ts +100 -0
  189. package/src/ui/hooks/useInputBuffer.ts +122 -0
  190. package/src/ui/hooks/useTerminalSize.ts +68 -0
  191. package/src/ui/hooks/useTerminalWidth.ts +5 -0
  192. package/src/ui/hooks/useWindowedList.ts +118 -0
  193. package/src/ui/render-debugger.ts +621 -0
  194. package/src/ui/test.ts +189 -0
  195. package/src/ui/themes/ThemeManager.ts +332 -0
  196. package/src/ui/themes/aegisTheme.ts +87 -0
  197. package/src/ui/themes/darkTheme.ts +87 -0
  198. package/src/ui/themes/defaultTheme.ts +85 -0
  199. package/src/ui/themes/index.ts +10 -0
  200. package/src/ui/themes/lightTheme.ts +89 -0
  201. package/src/ui/themes/popularThemes.ts +187 -0
  202. package/src/ui/themes/types.ts +130 -0
  203. package/src/utils/clipboard.ts +48 -0
  204. package/src/utils/debug.ts +43 -0
  205. package/src/utils/environment.ts +68 -0
  206. package/src/utils/index.ts +10 -0
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Task tool — lets the running agent delegate read-only investigation
3
+ * work to parallel sub-agents, mid-conversation, without the user typing
4
+ * a slash command. Same machinery as /multi, restricted to Read/Grep/Glob
5
+ * (see plan: no confirmationHandler reaches a tool's execute(), so only
6
+ * ReadOnly-kind work is safe to trigger from inside a tool call).
7
+ */
8
+
9
+ import { z } from 'zod';
10
+ import { createTool } from '../createTool.js';
11
+ import { ToolKind } from '../types.js';
12
+ import { OrchestratorAgent, type SubAgentConfig } from '../../agent/orchestrator/OrchestratorAgent.js';
13
+ import { requireModelConfig, buildSourceContext } from '../../agent/orchestrator/utils.js';
14
+
15
+ const TaskSchema = z.object({
16
+ tasks: z.array(z.object({
17
+ description: z.string().describe('Short label for this investigation, e.g. "find auth bugs"'),
18
+ prompt: z.string().describe('Detailed instructions for the sub-agent'),
19
+ })).min(1).max(5).describe('One or more independent investigations to run in parallel'),
20
+ });
21
+
22
+ export const taskTool = createTool({
23
+ name: 'Task',
24
+ displayName: 'Delegate Task',
25
+ kind: ToolKind.ReadOnly,
26
+ schema: TaskSchema,
27
+
28
+ description: {
29
+ short: 'Delegate read-only investigation work to parallel sub-agents',
30
+ long: `Spin up one or more read-only sub-agents (Read/Grep/Glob only) to investigate
31
+ the codebase in parallel, then return their findings.
32
+
33
+ Use this when:
34
+ - A question has multiple independent angles worth exploring at once
35
+ - You want a focused sub-agent to dig into one area without cluttering the main conversation
36
+
37
+ Sub-agents cannot write files or run commands — they only read and report back.
38
+ You're responsible for synthesizing their findings into your final answer.`,
39
+ },
40
+
41
+ execute: async ({ tasks }, context) => {
42
+ let modelConfig;
43
+ try {
44
+ modelConfig = requireModelConfig();
45
+ } catch (e) {
46
+ const msg = (e as Error).message;
47
+ return { success: false, llmContent: msg, displayContent: msg };
48
+ }
49
+
50
+ const agentConfig = {
51
+ apiKey: modelConfig.apiKey,
52
+ baseURL: modelConfig.baseURL,
53
+ model: modelConfig.model,
54
+ timeout: modelConfig.timeout,
55
+ };
56
+
57
+ const cwd = context?.cwd || process.cwd();
58
+ const sourceCtx = buildSourceContext(cwd);
59
+ const codeContext = sourceCtx
60
+ ? `\n\nWorkspace context (project metadata + file tree + structure summaries + git changes):\n${sourceCtx}\n\nUse Read / Grep / Glob to examine these.`
61
+ : '\n\nUse Read / Grep / Glob to explore the codebase before responding.';
62
+
63
+ const orchestrator = new OrchestratorAgent(
64
+ 'Task-Orchestrator',
65
+ 'You coordinate read-only investigation sub-agents.',
66
+ );
67
+
68
+ tasks.forEach((task, i) => {
69
+ const agentCfg: SubAgentConfig = {
70
+ name: `agent-${i}`,
71
+ role: 'Investigator',
72
+ systemPrompt: `You are a focused investigation sub-agent. ${task.prompt}${codeContext}`,
73
+ config: agentConfig,
74
+ tools: ['Read', 'Grep', 'Glob'],
75
+ };
76
+ orchestrator.registerAgent(agentCfg);
77
+ });
78
+
79
+ try {
80
+ const responses = await orchestrator.delegateParallel(
81
+ tasks.map((task, i) => ({ agentName: `agent-${i}`, task: task.prompt })),
82
+ tasks.length,
83
+ context?.sessionId,
84
+ );
85
+
86
+ const lines: string[] = [];
87
+ responses.forEach((res, i) => {
88
+ lines.push(`### ${tasks[i]?.description || res.agentName}`);
89
+ lines.push(res.content || '*No response*');
90
+ lines.push('');
91
+ });
92
+
93
+ const formatted = lines.join('\n').trim();
94
+ return {
95
+ success: true,
96
+ llmContent: formatted,
97
+ displayContent: `✓ Ran ${tasks.length} investigation${tasks.length > 1 ? 's' : ''} in parallel`,
98
+ };
99
+ } catch (e) {
100
+ const msg = `Task delegation failed: ${(e as Error).message}`;
101
+ return { success: false, llmContent: msg, displayContent: msg };
102
+ }
103
+ },
104
+ });
105
+
106
+ export default taskTool;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Write 工具
3
+ *
4
+ *
5
+ */
6
+
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
9
+ import { z } from 'zod';
10
+ import { createTool } from '../createTool.js';
11
+ import { ToolKind, ToolErrorType } from '../types.js';
12
+ import { createSnapshot } from './snapshot.js';
13
+
14
+ // ========== Schema 定
15
+
16
+ const WriteSchema = z.object({
17
+ file_path: z.string()
18
+ .min(1, '文件路径不能为空')
19
+ .describe('The absolute path to the file to write'),
20
+ contents: z.string()
21
+ .describe('The contents to write to the file'),
22
+ });
23
+
24
+ // ========== Write 工
25
+
26
+ export const writeTool = createTool({
27
+ name: 'Write',
28
+ displayName: 'File Write',
29
+ kind: ToolKind.Write,
30
+ schema: WriteSchema,
31
+
32
+ description: {
33
+ short: 'Writes a file to the local filesystem',
34
+ long: 'Creates a new file or overwrites an existing file with the specified contents.',
35
+ usageNotes: [
36
+ 'This tool will overwrite the existing file if there is one at the provided path',
37
+ 'ALWAYS prefer editing existing files in the codebase using Edit tool',
38
+ 'NEVER write new files unless explicitly required',
39
+ 'NEVER proactively create documentation files (*.md) or README files',
40
+ 'Parent directories will be created automatically if they do not exist',
41
+ ],
42
+ examples: [
43
+ {
44
+ description: 'Create a new TypeScript file',
45
+ params: {
46
+ file_path: '/path/to/new-file.ts',
47
+ contents: 'export const hello = "world";',
48
+ },
49
+ },
50
+ ],
51
+ important: [
52
+ 'Only create files that are absolutely necessary',
53
+ 'Do not create README or documentation files unless explicitly requested',
54
+ ],
55
+ },
56
+
57
+ category: '文件操作',
58
+ tags: ['file', 'io', 'write', 'create'],
59
+
60
+ // 提取签名内容(用于权限规
61
+ extractSignatureContent: (params: unknown) => {
62
+ const p = params as { file_path: string };
63
+ return p.file_path;
64
+ },
65
+
66
+ // 抽象权限规
67
+ abstractPermissionRule: (params: unknown) => {
68
+ const p = params as { file_path: string };
69
+ const dir = path.dirname(p.file_path);
70
+ return `Write:${dir}/*`;
71
+ },
72
+
73
+ async execute(params, context) {
74
+ const { file_path, contents } = params;
75
+
76
+ try {
77
+ // 1. 检查目标是否是目
78
+ try {
79
+ const stat = await fs.stat(file_path);
80
+ if (stat.isDirectory()) {
81
+ return {
82
+ success: false,
83
+ llmContent: `Error: ${file_path} is a directory, cannot write to it`,
84
+ displayContent: `error: ${file_path} is a directory`,
85
+ error: {
86
+ type: ToolErrorType.VALIDATION_ERROR,
87
+ message: 'Path is a directory',
88
+ },
89
+ };
90
+ }
91
+ } catch {
92
+ // 文件不存在,这是允许
93
+ }
94
+
95
+ // 2. 确保父目录存
96
+ const dir = path.dirname(file_path);
97
+ await fs.mkdir(dir, { recursive: true });
98
+
99
+ // 3. 快照原始文件(如果已存在)
100
+ let snapshotPath: string | null = null;
101
+ try { await fs.access(file_path); snapshotPath = await createSnapshot(file_path); } catch { /* new file */ }
102
+
103
+ // 4. 写入文
104
+ await fs.writeFile(file_path, contents, 'utf8');
105
+
106
+ // 5. 计算写入信
107
+ const lines = contents.split('\n').length;
108
+ const bytes = Buffer.byteLength(contents, 'utf8');
109
+
110
+ return {
111
+ success: true,
112
+ llmContent: `Successfully wrote to ${file_path} (${lines} lines, ${bytes} bytes)`,
113
+ displayContent: `${path.basename(file_path)} ${lines} lines`,
114
+ metadata: {
115
+ file_path,
116
+ lines,
117
+ bytes,
118
+ snapshot: snapshotPath,
119
+ },
120
+ };
121
+ } catch (error) {
122
+ const errorMessage = error instanceof Error ? error.message : 'unknown error';
123
+ return {
124
+ success: false,
125
+ llmContent: `Error writing file: ${errorMessage}`,
126
+ displayContent: `error: ${errorMessage}`,
127
+ error: {
128
+ type: ToolErrorType.EXECUTION_ERROR,
129
+ message: errorMessage,
130
+ },
131
+ };
132
+ }
133
+ },
134
+ });
@@ -0,0 +1,221 @@
1
+ /**
2
+ *
3
+ *
4
+ *
5
+ */
6
+
7
+ import { z } from 'zod';
8
+ import { zodToJsonSchema } from 'zod-to-json-schema';
9
+ import {
10
+ ToolErrorType,
11
+ type Tool,
12
+ type ToolKind,
13
+ type ToolDescription,
14
+ type ToolResult,
15
+ type ExecutionContext,
16
+ type FunctionDeclaration,
17
+ type ToolInvocation,
18
+ } from './types.js';
19
+
20
+ // ========== 配置类
21
+
22
+ /**
23
+ *
24
+ */
25
+ export interface ToolConfig<TSchema extends z.ZodType> {
26
+ /** 工具唯一名称 */
27
+ name: string;
28
+ /** 显示名称 */
29
+ displayName?: string;
30
+ /** 工具类型 */
31
+ kind: ToolKind;
32
+ /** 参数 Schema */
33
+ schema: TSchema;
34
+ /** 工具描述 */
35
+ description: ToolDescription;
36
+ /** 执行函数 */
37
+ execute: (
38
+ params: z.infer<TSchema>,
39
+ context?: ExecutionContext
40
+ ) => Promise<ToolResult>;
41
+ /** 版本 */
42
+ version?: string;
43
+ /** 分类 */
44
+ category?: string;
45
+ /** 标签 */
46
+ tags?: string[];
47
+ /** 是否只读(默认根据 kind 推断) */
48
+ isReadOnly?: boolean;
49
+ /** 是否并发安全(默认 true) */
50
+ isConcurrencySafe?: boolean;
51
+ /** 是否启用结构化输出(默认 false) */
52
+ strict?: boolean;
53
+ /** 提取签名内容(用于权限规则) */
54
+ extractSignatureContent?: (params: unknown) => string;
55
+ /** 抽象权限规则(用于权限匹配) */
56
+ abstractPermissionRule?: (params: unknown) => string;
57
+ }
58
+
59
+ // ========== 工厂函
60
+
61
+ /**
62
+ *
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const readTool = createTool({
67
+ * name: 'Read',
68
+ * kind: ToolKind.ReadOnly,
69
+ * schema: z.object({
70
+ * file_path: z.string(),
71
+ * }),
72
+ * description: { short: 'Read files' },
73
+ * execute: async (params) => {
74
+ * // ...
75
+ * },
76
+ * });
77
+ * ```
78
+ */
79
+ export function createTool<TSchema extends z.ZodType>(
80
+ config: ToolConfig<TSchema>
81
+ ): Tool<z.infer<TSchema>> {
82
+ const {
83
+ name,
84
+ displayName,
85
+ kind,
86
+ schema,
87
+ description,
88
+ execute,
89
+ version = '1.0.0',
90
+ category,
91
+ tags = [],
92
+ isReadOnly,
93
+ isConcurrencySafe = true,
94
+ strict = false,
95
+ extractSignatureContent,
96
+ abstractPermissionRule,
97
+ } = config;
98
+
99
+ // 从 Zod Schema 生
100
+ const jsonSchema = zodToJsonSchema(schema, {
101
+ $refStrategy: 'none',
102
+ target: 'openApi3',
103
+ });
104
+
105
+ // 提取 properties
106
+ const schemaObj = jsonSchema as {
107
+ type?: string;
108
+ properties?: Record<string, unknown>;
109
+ required?: string[];
110
+ };
111
+
112
+ return {
113
+ name,
114
+ displayName: displayName || name,
115
+ kind,
116
+ isReadOnly: isReadOnly ?? kind === 'readonly',
117
+ isConcurrencySafe,
118
+ strict,
119
+ description,
120
+ version,
121
+ category,
122
+ tags,
123
+
124
+ /**
125
+ *
126
+ */
127
+ getFunctionDeclaration(): FunctionDeclaration {
128
+ return {
129
+ name,
130
+ description: buildFullDescription(description),
131
+ parameters: {
132
+ type: 'object',
133
+ properties: schemaObj.properties || {},
134
+ required: schemaObj.required,
135
+ },
136
+ };
137
+ },
138
+
139
+ /**
140
+ *
141
+ */
142
+ build(params: z.infer<TSchema>): ToolInvocation<z.infer<TSchema>> {
143
+ return {
144
+ toolName: name,
145
+ params,
146
+ };
147
+ },
148
+
149
+ /**
150
+ *
151
+ */
152
+ async execute(
153
+ params: z.infer<TSchema>,
154
+ context?: ExecutionContext
155
+ ): Promise<ToolResult> {
156
+ try {
157
+ // 验证参
158
+ const validated = schema.parse(params);
159
+ // 执行工
160
+ return await execute(validated, context);
161
+ } catch (error) {
162
+ // 处理 Zod 验证错
163
+ if (error instanceof z.ZodError) {
164
+ const messages = error.errors.map(e =>
165
+ `${e.path.join('.')}: ${e.message}`
166
+ ).join('; ');
167
+
168
+ return {
169
+ success: false,
170
+ llmContent: `Parameter validation failed: ${messages}`,
171
+ displayContent: `❌ 参数验证失败: ${messages}`,
172
+ error: {
173
+ type: ToolErrorType.VALIDATION_ERROR,
174
+ message: messages,
175
+ details: error.errors,
176
+ },
177
+ };
178
+ }
179
+
180
+ // 处理其他错
181
+ const errorMessage = error instanceof Error ? error.message : '未知错误';
182
+ return {
183
+ success: false,
184
+ llmContent: `Tool execution failed: ${errorMessage}`,
185
+ displayContent: `❌ 执行失败: ${errorMessage}`,
186
+ error: {
187
+ type: ToolErrorType.EXECUTION_ERROR,
188
+ message: errorMessage,
189
+ },
190
+ };
191
+ }
192
+ },
193
+
194
+ // 可选方
195
+ extractSignatureContent,
196
+ abstractPermissionRule,
197
+ };
198
+ }
199
+
200
+ /**
201
+ *
202
+ */
203
+ function buildFullDescription(desc: ToolDescription): string {
204
+ const parts: string[] = [desc.short];
205
+
206
+ if (desc.long) {
207
+ parts.push(desc.long);
208
+ }
209
+
210
+ if (desc.usageNotes && desc.usageNotes.length > 0) {
211
+ parts.push('\nUsage notes:');
212
+ parts.push(...desc.usageNotes.map(note => `- ${note}`));
213
+ }
214
+
215
+ if (desc.important && desc.important.length > 0) {
216
+ parts.push('\nIMPORTANT:');
217
+ parts.push(...desc.important.map(note => `- ${note}`));
218
+ }
219
+
220
+ return parts.join('\n');
221
+ }
@@ -0,0 +1,263 @@
1
+ /**
2
+ * ExecutionPipeline - 执行管道
3
+ *
4
+ */
5
+
6
+ import {
7
+ ToolExecution,
8
+ PermissionMode,
9
+ type PipelineStage,
10
+ type PipelineExecutionContext,
11
+ type ExecutionPipelineConfig,
12
+ type ExecutionHistoryEntry,
13
+ type StageStartEvent,
14
+ type StageCompleteEvent,
15
+ } from './types.js';
16
+ import type { ToolResult, ToolErrorType } from '../types.js';
17
+ import type { ToolRegistry } from '../registry.js';
18
+ import {
19
+ DiscoveryStage,
20
+ CacheStage,
21
+ PermissionStage,
22
+ HookStage,
23
+ ConfirmationStage,
24
+ ExecutionStage,
25
+ PostHookStage,
26
+ FormattingStage,
27
+ } from './stages/index.js';
28
+
29
+ /**
30
+ *
31
+ */
32
+ export interface ExecutionPipelineEvents {
33
+ stageStart: (event: StageStartEvent) => void;
34
+ stageComplete: (event: StageCompleteEvent) => void;
35
+ executionStart: (execution: ToolExecution) => void;
36
+ executionComplete: (execution: ToolExecution, result: ToolResult) => void;
37
+ executionError: (execution: ToolExecution, error: Error) => void;
38
+ }
39
+
40
+ /**
41
+ *
42
+ */
43
+ export class ExecutionPipeline {
44
+ private stages: PipelineStage[];
45
+ private cacheStage: CacheStage;
46
+ private executionHistory: ExecutionHistoryEntry[] = [];
47
+ private readonly sessionApprovals = new Set<string>();
48
+ private readonly sessionDenials = new Set<string>();
49
+ private readonly maxHistorySize = 1000;
50
+
51
+ constructor(
52
+ private registry: ToolRegistry,
53
+ config: ExecutionPipelineConfig = {}
54
+ ) {
55
+
56
+ const defaultMode = config.defaultMode || PermissionMode.DEFAULT;
57
+ this.cacheStage = new CacheStage();
58
+
59
+ // 初始化八个执行阶
60
+ this.stages = [
61
+ new DiscoveryStage(this.registry, config.allowedTools, config.disallowedTools),
62
+ this.cacheStage,
63
+ new PermissionStage(config.permissions, this.sessionApprovals, this.sessionDenials, defaultMode),
64
+ new HookStage(),
65
+ new ConfirmationStage(this.sessionApprovals, this.sessionDenials),
66
+ new ExecutionStage(this.cacheStage),
67
+ new PostHookStage(),
68
+ new FormattingStage(),
69
+ ];
70
+ }
71
+
72
+ /**
73
+ *
74
+ */
75
+ async execute(
76
+ toolName: string,
77
+ params: Record<string, unknown>,
78
+ context: PipelineExecutionContext
79
+ ): Promise<ToolResult> {
80
+ const startTime = Date.now();
81
+ const executedStages: string[] = [];
82
+
83
+ // 创建执行实
84
+ const execution = new ToolExecution(toolName, params, context);
85
+
86
+ // 发出执行开始事
87
+
88
+ try {
89
+ // 依次执行各阶
90
+ for (const stage of this.stages) {
91
+ if (execution.isAborted()) {
92
+ break;
93
+ }
94
+
95
+ // 发出阶段开始事
96
+
97
+ // 执行阶
98
+ await stage.process(execution);
99
+
100
+ // 记录已执行的阶
101
+ executedStages.push(stage.name);
102
+
103
+ // 发出阶段完成事
104
+ }
105
+
106
+ // 获取或创建结
107
+ const result = execution.getResult() || this.createErrorResult(execution);
108
+
109
+ // 记录执行历
110
+ this.recordExecution({
111
+ toolName,
112
+ params,
113
+ result,
114
+ timestamp: startTime,
115
+ duration: Date.now() - startTime,
116
+ permissionMode: context.permissionMode,
117
+ stages: executedStages,
118
+ });
119
+
120
+ // 发出执行完成事
121
+
122
+ return result;
123
+ } catch (error) {
124
+ // 发出执行错误事
125
+ const err = error instanceof Error ? error : new Error(String(error));
126
+
127
+ // 返回错误结
128
+ return {
129
+ success: false,
130
+ llmContent: `Pipeline execution error: ${err.message}`,
131
+ displayContent: `❌ Pipeline error: ${err.message}`,
132
+ error: {
133
+ type: 'execution_error' as ToolErrorType,
134
+ message: err.message,
135
+ },
136
+ };
137
+ }
138
+ }
139
+
140
+ /**
141
+ *
142
+ */
143
+ private createErrorResult(execution: ToolExecution): ToolResult {
144
+ const reason = execution.getAbortReason() || 'Unknown error';
145
+ return {
146
+ success: false,
147
+ llmContent: `Tool execution aborted: ${reason}`,
148
+ displayContent: `❌ ${execution.toolName}: ${reason}`,
149
+ error: {
150
+ type: 'execution_error' as ToolErrorType,
151
+ message: reason,
152
+ },
153
+ };
154
+ }
155
+
156
+ /**
157
+ *
158
+ */
159
+ private recordExecution(entry: ExecutionHistoryEntry): void {
160
+ this.executionHistory.push(entry);
161
+
162
+ // 限制历史大
163
+ if (this.executionHistory.length > this.maxHistorySize) {
164
+ this.executionHistory.shift();
165
+ }
166
+ }
167
+
168
+ /**
169
+ *
170
+ */
171
+ getRegistry(): ToolRegistry {
172
+ return this.registry;
173
+ }
174
+
175
+ /**
176
+ *
177
+ */
178
+ getHistory(): ExecutionHistoryEntry[] {
179
+ return [...this.executionHistory];
180
+ }
181
+
182
+ /**
183
+ *
184
+ */
185
+ clearHistory(): void {
186
+ this.executionHistory = [];
187
+ }
188
+
189
+ /**
190
+ *
191
+ */
192
+ getSessionApprovals(): Set<string> {
193
+ return new Set(this.sessionApprovals);
194
+ }
195
+
196
+ /**
197
+ *
198
+ */
199
+ clearSessionApprovals(): void {
200
+ this.sessionApprovals.clear();
201
+ }
202
+
203
+ /**
204
+ *
205
+ */
206
+ addSessionApproval(signature: string): void {
207
+ this.sessionApprovals.add(signature);
208
+ }
209
+
210
+ /**
211
+ *
212
+ */
213
+ hasSessionApproval(signature: string): boolean {
214
+ return this.sessionApprovals.has(signature);
215
+ }
216
+
217
+ // ==================== Session Denials ====================
218
+
219
+ /**
220
+ *
221
+ */
222
+ getSessionDenials(): Set<string> {
223
+ return new Set(this.sessionDenials);
224
+ }
225
+
226
+ /**
227
+ *
228
+ */
229
+ clearSessionDenials(): void {
230
+ this.sessionDenials.clear();
231
+ }
232
+
233
+ /**
234
+ *
235
+ */
236
+ addSessionDenial(signature: string): void {
237
+ this.sessionDenials.add(signature);
238
+ }
239
+
240
+ /**
241
+ *
242
+ */
243
+ hasSessionDenial(signature: string): boolean {
244
+ return this.sessionDenials.has(signature);
245
+ }
246
+
247
+ /**
248
+ *
249
+ */
250
+ getStageNames(): string[] {
251
+ return this.stages.map(s => s.name);
252
+ }
253
+
254
+ /** Stats for the session-scoped Read/Grep/Glob result cache */
255
+ getCacheStats(): { size: number; maxSize: number } {
256
+ return this.cacheStage.stats();
257
+ }
258
+
259
+ /** Clear cached entries for a specific session */
260
+ clearSessionCache(sessionId: string): void {
261
+ this.cacheStage.clearSession(sessionId);
262
+ }
263
+ }