aegiscode 3.1.10 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/aegis-cli-linux-x64 +0 -0
- package/bin/cli.js +68 -0
- package/package.json +11 -48
- package/scripts/download-binary.mjs +155 -0
- package/dist/main.js +0 -2171
- package/gui/run-gui.cjs +0 -13
- package/src/agent/Agent.ts +0 -903
- package/src/agent/SimpleAgent.ts +0 -48
- package/src/agent/index.ts +0 -54
- package/src/agent/orchestrator/AppBuilder.ts +0 -443
- package/src/agent/orchestrator/CouncilAgent.ts +0 -310
- package/src/agent/orchestrator/DiscussionRoom.ts +0 -462
- package/src/agent/orchestrator/OrchestratorAgent.ts +0 -637
- package/src/agent/orchestrator/index.ts +0 -38
- package/src/agent/orchestrator/utils.ts +0 -397
- package/src/agent/pricing.ts +0 -115
- package/src/agent/router.ts +0 -74
- package/src/agent/routerStats.ts +0 -121
- package/src/agent/types.ts +0 -318
- package/src/auth/login.ts +0 -383
- package/src/cli/config.ts +0 -189
- package/src/cli/index.ts +0 -17
- package/src/cli/middleware.ts +0 -119
- package/src/cli/types.ts +0 -75
- package/src/config/ConfigManager.ts +0 -587
- package/src/config/index.ts +0 -7
- package/src/config/types.ts +0 -584
- package/src/context/CompactionService.ts +0 -300
- package/src/context/ContextManager.ts +0 -450
- package/src/context/FileAnalyzer.ts +0 -267
- package/src/context/TokenCounter.ts +0 -265
- package/src/context/index.ts +0 -27
- package/src/context/storage/CacheStore.ts +0 -176
- package/src/context/storage/JSONLStore.ts +0 -201
- package/src/context/storage/MemoryStore.ts +0 -205
- package/src/context/storage/PersistentStore.ts +0 -327
- package/src/context/storage/index.ts +0 -9
- package/src/context/storage/pathUtils.ts +0 -114
- package/src/context/test.ts +0 -309
- package/src/context/types.ts +0 -268
- package/src/hooks/HookExecutor.ts +0 -434
- package/src/hooks/HookManager.ts +0 -596
- package/src/hooks/HookService.ts +0 -269
- package/src/hooks/Matcher.ts +0 -157
- package/src/hooks/index.ts +0 -63
- package/src/hooks/types.ts +0 -424
- package/src/main.tsx +0 -596
- package/src/mcp/HealthMonitor.ts +0 -150
- package/src/mcp/McpClient.ts +0 -491
- package/src/mcp/McpRegistry.ts +0 -321
- package/src/mcp/createMcpTool.ts +0 -251
- package/src/mcp/index.ts +0 -15
- package/src/mcp/server.ts +0 -334
- package/src/mcp/test-server.ts +0 -88
- package/src/mcp/test.ts +0 -372
- package/src/mcp/types.ts +0 -247
- package/src/memory/AgentMemoryBus.ts +0 -432
- package/src/memory/CloudSync.ts +0 -99
- package/src/memory/DriveSync.ts +0 -106
- package/src/memory/SharedMemory.ts +0 -951
- package/src/memory/index.ts +0 -14
- package/src/memory/machineFingerprint.ts +0 -40
- package/src/orchestrator/SubAgentMetadata.ts +0 -136
- package/src/prompts/builder.ts +0 -213
- package/src/prompts/default.ts +0 -144
- package/src/prompts/index.ts +0 -16
- package/src/prompts/plan.ts +0 -64
- package/src/prompts/test.ts +0 -78
- package/src/services/AnthropicChatService.ts +0 -341
- package/src/services/ChatService.ts +0 -347
- package/src/services/ClaudeCliChatService.ts +0 -256
- package/src/services/CloudSync.ts +0 -168
- package/src/services/CostLedger.ts +0 -211
- package/src/services/Heartbeat.ts +0 -135
- package/src/services/LearningCollector.ts +0 -291
- package/src/services/OllamaInstaller.ts +0 -342
- package/src/services/VersionChecker.ts +0 -445
- package/src/services/index.ts +0 -57
- package/src/services/streaming/OpenAIEventAdapter.ts +0 -126
- package/src/services/streaming/RenderingProfile.ts +0 -90
- package/src/services/streaming/StreamEventParser.ts +0 -181
- package/src/services/streaming/ThrottledRenderer.ts +0 -139
- package/src/services/streaming/TranscriptBuffer.ts +0 -574
- package/src/services/streaming/eventStatusMap.ts +0 -52
- package/src/services/streaming/index.ts +0 -46
- package/src/services/streaming/renderFormatting.ts +0 -79
- package/src/services/streaming/types.ts +0 -234
- package/src/skills/SkillLoader.ts +0 -126
- package/src/skills/SkillRegistry.ts +0 -366
- package/src/skills/index.ts +0 -48
- package/src/skills/types.ts +0 -146
- package/src/slash-commands/billing.ts +0 -70
- package/src/slash-commands/build.ts +0 -413
- package/src/slash-commands/builtinCommands.ts +0 -2733
- package/src/slash-commands/clone.ts +0 -242
- package/src/slash-commands/council.ts +0 -125
- package/src/slash-commands/custom/CustomCommandExecutor.ts +0 -143
- package/src/slash-commands/custom/CustomCommandLoader.ts +0 -251
- package/src/slash-commands/custom/CustomCommandRegistry.ts +0 -144
- package/src/slash-commands/custom/index.ts +0 -7
- package/src/slash-commands/debate.ts +0 -254
- package/src/slash-commands/gmail.ts +0 -105
- package/src/slash-commands/index.ts +0 -388
- package/src/slash-commands/mcpCommand.ts +0 -205
- package/src/slash-commands/types.ts +0 -201
- package/src/store/index.ts +0 -76
- package/src/store/selectors.ts +0 -246
- package/src/store/slices/appSlice.ts +0 -205
- package/src/store/slices/commandSlice.ts +0 -115
- package/src/store/slices/configSlice.ts +0 -46
- package/src/store/slices/focusSlice.ts +0 -64
- package/src/store/slices/index.ts +0 -9
- package/src/store/slices/sessionSlice.ts +0 -424
- package/src/store/streaming-buffer.ts +0 -425
- package/src/store/test.ts +0 -296
- package/src/store/types.ts +0 -274
- package/src/store/vanilla.ts +0 -186
- package/src/tools/builtin/bash.ts +0 -236
- package/src/tools/builtin/council.ts +0 -105
- package/src/tools/builtin/edit.ts +0 -213
- package/src/tools/builtin/glob.ts +0 -136
- package/src/tools/builtin/grep.ts +0 -263
- package/src/tools/builtin/index.ts +0 -61
- package/src/tools/builtin/memory.ts +0 -66
- package/src/tools/builtin/read.ts +0 -168
- package/src/tools/builtin/skill.ts +0 -97
- package/src/tools/builtin/snapshot.ts +0 -40
- package/src/tools/builtin/task.ts +0 -106
- package/src/tools/builtin/write.ts +0 -134
- package/src/tools/createTool.ts +0 -221
- package/src/tools/execution/ExecutionPipeline.ts +0 -263
- package/src/tools/execution/index.ts +0 -40
- package/src/tools/execution/stages/CacheStage.ts +0 -131
- package/src/tools/execution/stages/ConfirmationStage.ts +0 -203
- package/src/tools/execution/stages/DiscoveryStage.ts +0 -46
- package/src/tools/execution/stages/ExecutionStage.ts +0 -48
- package/src/tools/execution/stages/FormattingStage.ts +0 -44
- package/src/tools/execution/stages/HookStage.ts +0 -72
- package/src/tools/execution/stages/PermissionStage.ts +0 -287
- package/src/tools/execution/stages/PostHookStage.ts +0 -71
- package/src/tools/execution/stages/index.ts +0 -12
- package/src/tools/execution/test.ts +0 -266
- package/src/tools/execution/types.ts +0 -273
- package/src/tools/index.ts +0 -81
- package/src/tools/registry.ts +0 -304
- package/src/tools/schemas.ts +0 -109
- package/src/tools/test.ts +0 -220
- package/src/tools/types.ts +0 -175
- package/src/tools/validation/PermissionChecker.ts +0 -242
- package/src/tools/validation/SensitiveFileDetector.ts +0 -210
- package/src/tools/validation/index.ts +0 -11
- package/src/ui/App.tsx +0 -166
- package/src/ui/components/AegisInterface.tsx +0 -484
- package/src/ui/components/common/ChatSearch.tsx +0 -150
- package/src/ui/components/common/ErrorBoundary.tsx +0 -82
- package/src/ui/components/common/ExitMessage.tsx +0 -120
- package/src/ui/components/common/LoadingIndicator.tsx +0 -49
- package/src/ui/components/common/index.ts +0 -6
- package/src/ui/components/dialog/ConfirmationPrompt.tsx +0 -208
- package/src/ui/components/dialog/InteractiveSelector.tsx +0 -149
- package/src/ui/components/dialog/SetupWizard.tsx +0 -297
- package/src/ui/components/dialog/UpdatePrompt.tsx +0 -155
- package/src/ui/components/dialog/index.ts +0 -8
- package/src/ui/components/index.ts +0 -28
- package/src/ui/components/input/CommandSuggestions.tsx +0 -139
- package/src/ui/components/input/CustomTextInput.tsx +0 -220
- package/src/ui/components/input/InputArea.tsx +0 -361
- package/src/ui/components/input/PromptSuggestions.tsx +0 -66
- package/src/ui/components/input/index.ts +0 -6
- package/src/ui/components/layout/ChatStatusBar.tsx +0 -90
- package/src/ui/components/layout/ContextBar.tsx +0 -79
- package/src/ui/components/layout/MessageArea.tsx +0 -96
- package/src/ui/components/layout/MessageList.tsx +0 -647
- package/src/ui/components/layout/MessageSeparator.tsx +0 -26
- package/src/ui/components/layout/WelcomeMessage.tsx +0 -93
- package/src/ui/components/layout/index.ts +0 -7
- package/src/ui/components/markdown/CodeHighlighter.tsx +0 -292
- package/src/ui/components/markdown/MessageRenderer.tsx +0 -1211
- package/src/ui/components/markdown/index.ts +0 -8
- package/src/ui/components/markdown/parser.ts +0 -336
- package/src/ui/components/markdown/types.ts +0 -66
- package/src/ui/focus/FocusManager.ts +0 -137
- package/src/ui/focus/index.ts +0 -13
- package/src/ui/focus/types.ts +0 -54
- package/src/ui/focus/useFocus.ts +0 -75
- package/src/ui/hooks/index.ts +0 -11
- package/src/ui/hooks/useAgent.ts +0 -284
- package/src/ui/hooks/useCommandHistory.ts +0 -87
- package/src/ui/hooks/useCommandProcessor.ts +0 -443
- package/src/ui/hooks/useConfirmation.ts +0 -99
- package/src/ui/hooks/useCtrlCHandler.ts +0 -100
- package/src/ui/hooks/useInputBuffer.ts +0 -122
- package/src/ui/hooks/useTerminalSize.ts +0 -68
- package/src/ui/hooks/useTerminalWidth.ts +0 -5
- package/src/ui/hooks/useWindowedList.ts +0 -118
- package/src/ui/render-debugger.ts +0 -621
- package/src/ui/test.ts +0 -189
- package/src/ui/themes/ThemeManager.ts +0 -332
- package/src/ui/themes/aegisTheme.ts +0 -87
- package/src/ui/themes/darkTheme.ts +0 -87
- package/src/ui/themes/defaultTheme.ts +0 -85
- package/src/ui/themes/index.ts +0 -10
- package/src/ui/themes/lightTheme.ts +0 -89
- package/src/ui/themes/popularThemes.ts +0 -187
- package/src/ui/themes/types.ts +0 -130
- package/src/utils/clipboard.ts +0 -48
- package/src/utils/debug.ts +0 -43
- package/src/utils/environment.ts +0 -68
- package/src/utils/index.ts +0 -10
package/src/prompts/test.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { buildSystemPrompt, getPromptStats } from './builder.js';
|
|
8
|
-
import { getEnvironmentContext, getEnvironmentInfo } from '../utils/environment.js';
|
|
9
|
-
import { DEFAULT_SYSTEM_PROMPT } from './default.js';
|
|
10
|
-
import { PLAN_MODE_SYSTEM_PROMPT, createPlanModeReminder } from './plan.js';
|
|
11
|
-
|
|
12
|
-
async function testPromptSystem() {
|
|
13
|
-
console.log('=== 提示词系统测试 ===\n');
|
|
14
|
-
|
|
15
|
-
// 1. 测试环境上下
|
|
16
|
-
console.log('1. 环境信息:');
|
|
17
|
-
const envInfo = getEnvironmentInfo();
|
|
18
|
-
console.log(` 工作目录: ${envInfo.workingDirectory}`);
|
|
19
|
-
console.log(` 平台: ${envInfo.platform}`);
|
|
20
|
-
console.log(` 日期: ${envInfo.currentDate}`);
|
|
21
|
-
console.log();
|
|
22
|
-
|
|
23
|
-
// 2. 测试环境上下文生
|
|
24
|
-
console.log('2. 环境上下文 (前 300 字符):');
|
|
25
|
-
const envContext = getEnvironmentContext();
|
|
26
|
-
console.log(envContext.slice(0, 300) + '...\n');
|
|
27
|
-
|
|
28
|
-
// 3. 测试默认提示
|
|
29
|
-
console.log('3. 默认提示词长度:', DEFAULT_SYSTEM_PROMPT.length, '字符');
|
|
30
|
-
console.log(' 包含关键词:');
|
|
31
|
-
console.log(' - AEGIS:', DEFAULT_SYSTEM_PROMPT.includes('AEGIS') ? '✅' : '❌');
|
|
32
|
-
console.log(' - 4 lines:', DEFAULT_SYSTEM_PROMPT.includes('4 lines') ? '✅' : '❌');
|
|
33
|
-
console.log(' - Chinese:', DEFAULT_SYSTEM_PROMPT.includes('Chinese') ? '✅' : '❌');
|
|
34
|
-
console.log();
|
|
35
|
-
|
|
36
|
-
// 4. 测试 Plan 模式提示
|
|
37
|
-
console.log('4. Plan 模式提示词长度:', PLAN_MODE_SYSTEM_PROMPT.length, '字符');
|
|
38
|
-
console.log(' 包含关键词:');
|
|
39
|
-
console.log(' - PLAN MODE:', PLAN_MODE_SYSTEM_PROMPT.includes('PLAN MODE') ? '✅' : '❌');
|
|
40
|
-
console.log(' - Read-only:', PLAN_MODE_SYSTEM_PROMPT.includes('Read-only') ? '✅' : '❌');
|
|
41
|
-
console.log();
|
|
42
|
-
|
|
43
|
-
// 5. 测试消息提醒注
|
|
44
|
-
console.log('5. Plan 模式消息注入:');
|
|
45
|
-
const reminder = createPlanModeReminder('帮我分析这段代码');
|
|
46
|
-
console.log(' ' + reminder.slice(0, 100) + '...\n');
|
|
47
|
-
|
|
48
|
-
// 6. 测试完整提示词构建(默认模
|
|
49
|
-
console.log('6. 构建完整提示词(默认模式):');
|
|
50
|
-
const defaultResult = await buildSystemPrompt({
|
|
51
|
-
projectPath: process.cwd(),
|
|
52
|
-
includeEnvironment: true,
|
|
53
|
-
});
|
|
54
|
-
console.log(getPromptStats(defaultResult));
|
|
55
|
-
console.log();
|
|
56
|
-
|
|
57
|
-
// 7. 测试 Plan 模式提示词构
|
|
58
|
-
console.log('7. 构建完整提示词(Plan 模式):');
|
|
59
|
-
const planResult = await buildSystemPrompt({
|
|
60
|
-
projectPath: process.cwd(),
|
|
61
|
-
mode: 'plan',
|
|
62
|
-
includeEnvironment: true,
|
|
63
|
-
});
|
|
64
|
-
console.log(getPromptStats(planResult));
|
|
65
|
-
console.log();
|
|
66
|
-
|
|
67
|
-
// 8. 测试自定义追加内
|
|
68
|
-
console.log('8. 构建带追加内容的提示词:');
|
|
69
|
-
const appendResult = await buildSystemPrompt({
|
|
70
|
-
append: '额外指令:始终使用 TypeScript',
|
|
71
|
-
});
|
|
72
|
-
console.log(getPromptStats(appendResult));
|
|
73
|
-
console.log();
|
|
74
|
-
|
|
75
|
-
console.log('=== 测试完成 ===');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
testPromptSystem().catch(console.error);
|
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AnthropicChatService — native Anthropic Messages API transport.
|
|
3
|
-
*
|
|
4
|
-
* The OpenAI-compatible shim (ChatService.ts) cannot carry `cache_control`,
|
|
5
|
-
* `thinking`, or citations — Anthropic's OpenAI-compatibility layer silently
|
|
6
|
-
* drops all three. This talks directly to /v1/messages over raw SSE so those
|
|
7
|
-
* features actually work. Used only when the model is Anthropic and the key
|
|
8
|
-
* isn't a Claude-CLI OAuth token (that path stays on ClaudeCliChatService).
|
|
9
|
-
*
|
|
10
|
-
* The wire-level stream events (content_block_start/delta/stop, message_*)
|
|
11
|
-
* are forwarded as-is via onStreamEvent — AnthropicStreamEvent already
|
|
12
|
-
* mirrors this exact shape, so no adapter layer is needed (contrast with
|
|
13
|
-
* OpenAIEventAdapter, which exists only to translate other providers' shapes
|
|
14
|
-
* into this one).
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import type {
|
|
18
|
-
Message,
|
|
19
|
-
ToolDefinition,
|
|
20
|
-
ToolCall,
|
|
21
|
-
ChatResponse,
|
|
22
|
-
IChatService,
|
|
23
|
-
StreamCallbacks,
|
|
24
|
-
} from '../agent/types.js';
|
|
25
|
-
import type { ChatServiceConfig } from './ChatService.js';
|
|
26
|
-
import type { AnthropicStreamEvent } from './streaming/types.js';
|
|
27
|
-
|
|
28
|
-
const EFFORT_LEVELS = new Set(['low', 'medium', 'high']);
|
|
29
|
-
/** 'max' maps to a fixed budget_tokens ceiling when sent via extended thinking. */
|
|
30
|
-
const MAX_BUDGET_TOKENS = 128000;
|
|
31
|
-
|
|
32
|
-
/** Effort + adaptive thinking are unsupported on Haiku (errors per Anthropic API). */
|
|
33
|
-
function modelSupportsThinking(model: string): boolean {
|
|
34
|
-
return !/haiku/i.test(model);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
type AnthropicContentBlock = Record<string, unknown>;
|
|
38
|
-
type AnthropicMessage = { role: 'user' | 'assistant'; content: AnthropicContentBlock[] };
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Convert aegiscode's flat Message[] (OpenAI-shaped: system/user/assistant/tool
|
|
42
|
-
* roles, tool_calls, tool_call_id) into Anthropic's content-block format:
|
|
43
|
-
* system goes to a top-level field, tool results fold into 'user' messages,
|
|
44
|
-
* and tool_use blocks live inside 'assistant' content.
|
|
45
|
-
*/
|
|
46
|
-
function buildAnthropicMessages(messages: Message[]): { systemText: string; anthropicMessages: AnthropicMessage[] } {
|
|
47
|
-
let systemText = '';
|
|
48
|
-
const anthropicMessages: AnthropicMessage[] = [];
|
|
49
|
-
|
|
50
|
-
// Tool names by call id — needed to mark Read-tool results as citable documents.
|
|
51
|
-
const toolNameById = new Map<string, string>();
|
|
52
|
-
|
|
53
|
-
// Consecutive 'tool' messages (parallel tool calls) collapse into one user message.
|
|
54
|
-
let pendingToolResults: AnthropicContentBlock[] | null = null;
|
|
55
|
-
const flushToolResults = () => {
|
|
56
|
-
if (pendingToolResults && pendingToolResults.length > 0) {
|
|
57
|
-
anthropicMessages.push({ role: 'user', content: pendingToolResults });
|
|
58
|
-
}
|
|
59
|
-
pendingToolResults = null;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
for (const msg of messages) {
|
|
63
|
-
if (msg.role === 'system') {
|
|
64
|
-
systemText += (systemText ? '\n\n' : '') + msg.content;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (msg.role === 'tool') {
|
|
69
|
-
if (!pendingToolResults) pendingToolResults = [];
|
|
70
|
-
const toolName = msg.name || toolNameById.get(msg.tool_call_id || '') || '';
|
|
71
|
-
const resultContent: AnthropicContentBlock = toolName === 'Read'
|
|
72
|
-
? {
|
|
73
|
-
type: 'document',
|
|
74
|
-
source: { type: 'text', media_type: 'text/plain', data: msg.content },
|
|
75
|
-
citations: { enabled: true },
|
|
76
|
-
}
|
|
77
|
-
: { type: 'text', text: msg.content };
|
|
78
|
-
pendingToolResults.push({
|
|
79
|
-
type: 'tool_result',
|
|
80
|
-
tool_use_id: msg.tool_call_id,
|
|
81
|
-
content: [resultContent],
|
|
82
|
-
});
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
flushToolResults();
|
|
87
|
-
|
|
88
|
-
if (msg.role === 'user') {
|
|
89
|
-
anthropicMessages.push({ role: 'user', content: [{ type: 'text', text: msg.content }] });
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (msg.role === 'assistant') {
|
|
94
|
-
const content: AnthropicContentBlock[] = [];
|
|
95
|
-
if (msg.content) content.push({ type: 'text', text: msg.content });
|
|
96
|
-
for (const tc of msg.tool_calls || []) {
|
|
97
|
-
toolNameById.set(tc.id, tc.function.name);
|
|
98
|
-
let input: unknown = {};
|
|
99
|
-
try {
|
|
100
|
-
input = tc.function.arguments ? JSON.parse(tc.function.arguments) : {};
|
|
101
|
-
} catch {
|
|
102
|
-
input = {};
|
|
103
|
-
}
|
|
104
|
-
content.push({ type: 'tool_use', id: tc.id, name: tc.function.name, input });
|
|
105
|
-
}
|
|
106
|
-
anthropicMessages.push({ role: 'assistant', content });
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
flushToolResults();
|
|
111
|
-
|
|
112
|
-
return { systemText, anthropicMessages };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export class AnthropicChatService implements IChatService {
|
|
116
|
-
private apiKey: string;
|
|
117
|
-
private baseURL: string;
|
|
118
|
-
private model: string;
|
|
119
|
-
private timeout: number;
|
|
120
|
-
private maxRetries: number;
|
|
121
|
-
private thinkingBudget: 'off' | 'low' | 'medium' | 'high' | 'max';
|
|
122
|
-
private maxOutputTokens: number;
|
|
123
|
-
|
|
124
|
-
constructor(config: ChatServiceConfig) {
|
|
125
|
-
this.apiKey = config.apiKey;
|
|
126
|
-
this.baseURL = (config.baseURL || 'https://api.anthropic.com/v1').replace(/\/$/, '');
|
|
127
|
-
this.model = config.model || 'claude-sonnet-4-6';
|
|
128
|
-
this.timeout = config.timeout ?? 60000;
|
|
129
|
-
this.maxRetries = config.maxRetries ?? 2;
|
|
130
|
-
this.thinkingBudget = config.thinkingBudget || 'off';
|
|
131
|
-
this.maxOutputTokens = config.maxOutputTokens || 16384;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async chat(
|
|
135
|
-
messages: Message[],
|
|
136
|
-
tools?: ToolDefinition[],
|
|
137
|
-
signal?: AbortSignal,
|
|
138
|
-
streamCallbacks?: StreamCallbacks,
|
|
139
|
-
attempt = 0,
|
|
140
|
-
): Promise<ChatResponse> {
|
|
141
|
-
const { systemText, anthropicMessages } = buildAnthropicMessages(messages);
|
|
142
|
-
|
|
143
|
-
// Cache the conversation-history prefix: mark the last content block of the
|
|
144
|
-
// second-to-last message so everything before the newest turn can hit cache
|
|
145
|
-
// instead of being reprocessed at full price/latency every turn.
|
|
146
|
-
if (anthropicMessages.length >= 2) {
|
|
147
|
-
const prefixMessage = anthropicMessages[anthropicMessages.length - 2];
|
|
148
|
-
const lastBlock = prefixMessage.content[prefixMessage.content.length - 1];
|
|
149
|
-
if (lastBlock) {
|
|
150
|
-
(lastBlock as Record<string, unknown>).cache_control = { type: 'ephemeral' };
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const body: Record<string, unknown> = {
|
|
155
|
-
model: this.model,
|
|
156
|
-
max_tokens: this.maxOutputTokens,
|
|
157
|
-
system: [{ type: 'text', text: systemText, cache_control: { type: 'ephemeral' } }],
|
|
158
|
-
messages: anthropicMessages,
|
|
159
|
-
stream: true,
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
if (tools && tools.length > 0) {
|
|
163
|
-
const anthropicTools = tools.map(t => ({
|
|
164
|
-
name: t.function.name,
|
|
165
|
-
description: t.function.description,
|
|
166
|
-
input_schema: t.function.parameters,
|
|
167
|
-
})) as Array<Record<string, unknown>>;
|
|
168
|
-
(anthropicTools[anthropicTools.length - 1] as Record<string, unknown>).cache_control = { type: 'ephemeral' };
|
|
169
|
-
body.tools = anthropicTools;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (this.thinkingBudget !== 'off' && modelSupportsThinking(this.model)) {
|
|
173
|
-
if (this.thinkingBudget === 'max') {
|
|
174
|
-
// 'max' is not a valid Anthropic effort value — use token-based approach instead
|
|
175
|
-
body.thinking = { type: 'enabled', budget_tokens: MAX_BUDGET_TOKENS };
|
|
176
|
-
} else if (EFFORT_LEVELS.has(this.thinkingBudget)) {
|
|
177
|
-
body.thinking = { type: 'adaptive' };
|
|
178
|
-
body.output_config = { effort: this.thinkingBudget };
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
let content = '';
|
|
183
|
-
let reasoningContent = '';
|
|
184
|
-
let usage: ChatResponse['usage'] | undefined;
|
|
185
|
-
|
|
186
|
-
try {
|
|
187
|
-
const response = await fetch(`${this.baseURL}/messages`, {
|
|
188
|
-
method: 'POST',
|
|
189
|
-
signal,
|
|
190
|
-
headers: {
|
|
191
|
-
'x-api-key': this.apiKey,
|
|
192
|
-
'anthropic-version': '2023-06-01',
|
|
193
|
-
'content-type': 'application/json',
|
|
194
|
-
},
|
|
195
|
-
body: JSON.stringify(body),
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
if (!response.ok || !response.body) {
|
|
199
|
-
const text = await response.text().catch(() => '');
|
|
200
|
-
const err: Error & { status?: number } = new Error(
|
|
201
|
-
`Anthropic API error (${response.status}): ${text || response.statusText}`,
|
|
202
|
-
);
|
|
203
|
-
err.status = response.status;
|
|
204
|
-
throw err;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const toolBlocks = new Map<number, { id: string; name: string; arguments: string }>();
|
|
208
|
-
const citations: Array<{ text: string; title: string }> = [];
|
|
209
|
-
|
|
210
|
-
const reader = response.body.getReader();
|
|
211
|
-
const decoder = new TextDecoder();
|
|
212
|
-
let buffer = '';
|
|
213
|
-
|
|
214
|
-
while (true) {
|
|
215
|
-
const { done, value } = await reader.read();
|
|
216
|
-
if (done) break;
|
|
217
|
-
buffer += decoder.decode(value, { stream: true });
|
|
218
|
-
|
|
219
|
-
let sepIdx: number;
|
|
220
|
-
while ((sepIdx = buffer.indexOf('\n\n')) !== -1) {
|
|
221
|
-
const rawEvent = buffer.slice(0, sepIdx);
|
|
222
|
-
buffer = buffer.slice(sepIdx + 2);
|
|
223
|
-
|
|
224
|
-
const dataLine = rawEvent.split('\n').find(l => l.startsWith('data:'));
|
|
225
|
-
if (!dataLine) continue;
|
|
226
|
-
const json = dataLine.slice(5).trim();
|
|
227
|
-
if (!json) continue;
|
|
228
|
-
|
|
229
|
-
let event: AnthropicStreamEvent;
|
|
230
|
-
try {
|
|
231
|
-
event = JSON.parse(json);
|
|
232
|
-
} catch {
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
streamCallbacks?.onStreamEvent?.(event);
|
|
237
|
-
|
|
238
|
-
if (event.type === 'content_block_start') {
|
|
239
|
-
const block = event.content_block;
|
|
240
|
-
if (block.type === 'tool_use') {
|
|
241
|
-
toolBlocks.set(event.index, { id: block.id, name: block.name, arguments: '' });
|
|
242
|
-
streamCallbacks?.onToolCallStart?.({
|
|
243
|
-
id: block.id,
|
|
244
|
-
type: 'function',
|
|
245
|
-
function: { name: block.name, arguments: '' },
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
} else if (event.type === 'content_block_delta') {
|
|
249
|
-
const delta = event.delta;
|
|
250
|
-
if (delta.type === 'text_delta') {
|
|
251
|
-
content += delta.text;
|
|
252
|
-
streamCallbacks?.onContentDelta?.(delta.text);
|
|
253
|
-
} else if (delta.type === 'thinking_delta') {
|
|
254
|
-
reasoningContent += delta.thinking;
|
|
255
|
-
streamCallbacks?.onThinkingDelta?.(delta.thinking);
|
|
256
|
-
} else if (delta.type === 'input_json_delta') {
|
|
257
|
-
const tb = toolBlocks.get(event.index);
|
|
258
|
-
if (tb) {
|
|
259
|
-
tb.arguments += delta.partial_json;
|
|
260
|
-
streamCallbacks?.onToolCallDelta?.(tb.id, delta.partial_json);
|
|
261
|
-
}
|
|
262
|
-
} else if (delta.type === 'citations_delta') {
|
|
263
|
-
citations.push({ text: delta.citation.cited_text, title: delta.citation.document_title || 'source' });
|
|
264
|
-
}
|
|
265
|
-
} else if (event.type === 'message_start') {
|
|
266
|
-
const u = event.message.usage as { input_tokens?: number } | undefined;
|
|
267
|
-
if (u) {
|
|
268
|
-
usage = {
|
|
269
|
-
promptTokens: u.input_tokens || 0,
|
|
270
|
-
completionTokens: 0,
|
|
271
|
-
totalTokens: u.input_tokens || 0,
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
} else if (event.type === 'message_delta') {
|
|
275
|
-
const u = event.usage as { output_tokens?: number } | undefined;
|
|
276
|
-
if (u && usage) {
|
|
277
|
-
usage.completionTokens = u.output_tokens || 0;
|
|
278
|
-
usage.totalTokens = usage.promptTokens + usage.completionTokens;
|
|
279
|
-
}
|
|
280
|
-
} else if (event.type === 'error') {
|
|
281
|
-
throw new Error(event.error?.message || 'Anthropic stream error');
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Surface citations as a compact footer — reuses the existing text-rendering
|
|
287
|
-
// path instead of requiring new UI plumbing through the transcript buffer.
|
|
288
|
-
if (citations.length > 0) {
|
|
289
|
-
const seen = new Set<string>();
|
|
290
|
-
const lines: string[] = [];
|
|
291
|
-
for (const c of citations) {
|
|
292
|
-
const key = `${c.title}:${c.text}`;
|
|
293
|
-
if (seen.has(key)) continue;
|
|
294
|
-
seen.add(key);
|
|
295
|
-
const snippet = c.text.length > 80 ? c.text.slice(0, 77) + '...' : c.text;
|
|
296
|
-
lines.push(` ${c.title}: "${snippet}"`);
|
|
297
|
-
}
|
|
298
|
-
const footer = `\n\n— Cited:\n${lines.join('\n')}`;
|
|
299
|
-
content += footer;
|
|
300
|
-
streamCallbacks?.onContentDelta?.(footer);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const result: ChatResponse = { content, reasoningContent: reasoningContent || undefined, usage };
|
|
304
|
-
const toolCalls: ToolCall[] = Array.from(toolBlocks.values()).map(tb => ({
|
|
305
|
-
id: tb.id,
|
|
306
|
-
type: 'function' as const,
|
|
307
|
-
function: { name: tb.name, arguments: tb.arguments },
|
|
308
|
-
}));
|
|
309
|
-
if (toolCalls.length > 0) result.toolCalls = toolCalls;
|
|
310
|
-
return result;
|
|
311
|
-
} catch (error) {
|
|
312
|
-
if (error instanceof Error && error.name === 'AbortError') throw error;
|
|
313
|
-
|
|
314
|
-
const status = (error as { status?: number }).status;
|
|
315
|
-
const isTransient = status !== undefined && [429, 500, 502, 503, 504].includes(status);
|
|
316
|
-
|
|
317
|
-
if (isTransient) {
|
|
318
|
-
if (content.length > 0) {
|
|
319
|
-
return { content, reasoningContent: reasoningContent || undefined };
|
|
320
|
-
}
|
|
321
|
-
const MAX_RETRIES = 5;
|
|
322
|
-
if (attempt >= MAX_RETRIES) {
|
|
323
|
-
const reason = status === 429 ? 'Rate limited' : `Server error (${status})`;
|
|
324
|
-
throw new Error(
|
|
325
|
-
`${reason} — gave up after ${MAX_RETRIES} retries.\n` +
|
|
326
|
-
(status === 429
|
|
327
|
-
? `Your account hit Anthropic's rate limit. Wait a few minutes and try again,\n` +
|
|
328
|
-
`or check usage at https://claude.ai/settings/usage`
|
|
329
|
-
: (error as Error).message),
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
const delay = Math.min(1000 * 2 ** attempt, 16000);
|
|
333
|
-
process.stderr.write(`[RETRY] attempt ${attempt + 1}/${MAX_RETRIES} after ${delay}ms\n`);
|
|
334
|
-
await new Promise(r => setTimeout(r, delay));
|
|
335
|
-
return this.chat(messages, tools, signal, streamCallbacks, attempt + 1);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
throw error;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|