Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
bun-debug.log*
package-lock.json
.DS_Store
*.pem
.cache/
coverage/
.nyc_output/
*.tsbuildinfo
.claude
.logs
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@
### 1. 安装依赖

```bash
# 使用 npm 安装依赖(推荐,Bun 可能因网络问题失败)
npm install

# 或尝试使用 Bun
bun install
```

> **注意**:本项目包含 4 个 Anthropic 私有包的 stubs(位于 `stubs/` 目录),用于解决编译时的依赖缺失问题。这些 stubs 已通过 `bunfig.toml` 配置的路径别名自动映射,无需额外操作。

### 2. 编译

```bash
Expand Down Expand Up @@ -93,7 +99,7 @@ alias = { "src" = "./src", "react/compiler-runtime" = "react-compiler-runtime" }

## 私有包存根说明

以下 4 个 Anthropic 内部私有包在 npm 上不公开,已在 `node_modules/` 中创建功能存根
以下 4 个 Anthropic 内部私有包在 npm 上不公开,已在 `stubs/` 目录中创建功能存根,并通过 `bunfig.toml` 路径别名自动映射

| 包名 | 对应功能 | 影响 |
|------|----------|------|
Expand All @@ -102,7 +108,16 @@ alias = { "src" = "./src", "react/compiler-runtime" = "react-compiler-runtime" }
| `@anthropic-ai/mcpb` | MCP 插件包(.dxt 格式)安装 | 插件市场不可用 |
| `@anthropic-ai/sandbox-runtime` | 沙箱文件/网络权限隔离 | 沙箱模式不可用 |

核心对话、代码编辑、工具调用等主要功能不受影响。
**新增 stubs(位于 `stubs/` 目录):**

| 包名 | 对应功能 | 影响 |
|------|----------|------|
| `@anthropic-ai/bedrock-sdk` | AWS Bedrock 集成 | Bedrock 模式不可用 |
| `@anthropic-ai/foundry-sdk` | Anthropic Foundry 集成 | Foundry 模式不可用 |
| `@anthropic-ai/vertex-sdk` | Google Vertex AI 集成 | Vertex 模式不可用 |
| `@aws-sdk/client-bedrock` | AWS Bedrock 客户端 | Bedrock 模型列表不可用 |

核心对话、代码编辑、工具调用等主要功能不受影响。`npm install` 不会覆盖这些 stubs。

---

Expand Down
12 changes: 11 additions & 1 deletion bunfig.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
[build]
alias = { "src" = "./src", "react/compiler-runtime" = "react-compiler-runtime" }
alias = {
"src" = "./src",
"react/compiler-runtime" = "react-compiler-runtime",
"@anthropic-ai/bedrock-sdk" = "./stubs/@anthropic-ai/bedrock-sdk",
"@anthropic-ai/foundry-sdk" = "./stubs/@anthropic-ai/foundry-sdk",
"@anthropic-ai/vertex-sdk" = "./stubs/@anthropic-ai/vertex-sdk",
"@aws-sdk/client-bedrock" = "./stubs/@aws-sdk/client-bedrock"
}

[install]
registry = "https://registry.npmmirror.com"
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"node": ">=18.0.0"
},
"scripts": {
"build": "bun build src/entrypoints/cli.tsx --outfile cli.js --target bun --define MACRO.VERSION=\"2.1.88\" --define MACRO.BUILD_TIME=\"2025-01-01\" --define MACRO.FEEDBACK_CHANNEL=\"https://github.com/anthropics/claude-code/issues\" --define MACRO.ISSUES_EXPLAINER=\"https://github.com/anthropics/claude-code/issues\" --define MACRO.NATIVE_PACKAGE_URL=\"https://npmjs.com\" --define MACRO.PACKAGE_URL=\"https://npmjs.com\" --define MACRO.VERSION_CHANGELOG=\"\"",
"build": "bun build src/entrypoints/cli.tsx --outfile cli.js --target bun --define MACRO.VERSION='\"2.1.88\"' --define MACRO.BUILD_TIME='\"2025-01-01\"' --define MACRO.FEEDBACK_CHANNEL=\"https://github.com/anthropics/claude-code/issues\" --define MACRO.ISSUES_EXPLAINER=\"https://github.com/anthropics/claude-code/issues\" --define MACRO.NATIVE_PACKAGE_URL=\"https://npmjs.com\" --define MACRO.PACKAGE_URL=\"https://npmjs.com\" --define MACRO.VERSION_CHANGELOG=\"\"",
"build:binary": "bun build src/entrypoints/cli.tsx --compile --outfile claude --define MACRO.VERSION='\"2.1.88\"' --define MACRO.BUILD_TIME='\"2025-01-01\"' --define MACRO.FEEDBACK_CHANNEL=\"https://github.com/anthropics/claude-code/issues\" --define MACRO.ISSUES_EXPLAINER=\"https://github.com/anthropics/claude-code/issues\" --define MACRO.NATIVE_PACKAGE_URL=\"https://npmjs.com\" --define MACRO.PACKAGE_URL=\"https://npmjs.com\" --define MACRO.VERSION_CHANGELOG=\"\"",
"start": "bun cli.js",
"typecheck": "tsc --noEmit"
},
Expand Down
78 changes: 78 additions & 0 deletions src/services/api/claude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ import {
} from '../../utils/systemPromptType.js'
import { tokenCountFromLastAPIResponse } from '../../utils/tokens.js'
import { getDynamicConfig_BLOCKS_ON_INIT } from '../analytics/growthbook.js'
import {
isConversationLoggingEnabled,
logRetry as logConversationRetry,
logTextOutput,
logThinkingOutput,
logToolCall,
logTurnComplete,
logTurnError,
logTurnStart,
setPendingInput,
} from '../conversationLogger.js'
import {
currentLimits,
extractQuotaStatusFromError,
Expand Down Expand Up @@ -1758,6 +1769,22 @@ async function* queryModel(
})
}

// Conversation logging: capture input before API call
if (isConversationLoggingEnabled()) {
setPendingInput(
systemPrompt,
messagesForAPI,
)
logTurnStart(
{
id: options.model,
providerID: getAPIProvider(),
variant: 'default',
},
options.agentId ?? 'default',
)
}

const newMessages: AssistantMessage[] = []
let ttftMs = 0
let partialMessage: BetaMessage | undefined = undefined
Expand Down Expand Up @@ -1999,6 +2026,14 @@ async function* queryModel(
...part.content_block,
input: '',
}
// Conversation logging: log tool call
if (isConversationLoggingEnabled()) {
logToolCall(
part.content_block.id,
part.content_block.name,
{}, // Input will be captured as it streams in
)
}
break
case 'server_tool_use':
contentBlocks[part.index] = {
Expand Down Expand Up @@ -2189,6 +2224,29 @@ async function* queryModel(
})
throw new Error('Message not found')
}
// Conversation logging: capture text and thinking output
if (isConversationLoggingEnabled()) {
if (contentBlock.type === 'text' && 'text' in contentBlock) {
logTextOutput(contentBlock.text as string)
} else if (contentBlock.type === 'thinking' && 'thinking' in contentBlock) {
logThinkingOutput(
part.index.toString(),
contentBlock.thinking as string,
)
} else if (
(contentBlock.type === 'tool_use' || contentBlock.type === 'server_tool_use') &&
'input' in contentBlock &&
'id' in contentBlock &&
'name' in contentBlock
) {
// Update tool call with parsed input
logToolCall(
contentBlock.id as string,
contentBlock.name as string,
contentBlock.input,
)
}
}
const m: AssistantMessage = {
message: {
...partialMessage,
Expand Down Expand Up @@ -2255,6 +2313,21 @@ async function* queryModel(
options.model,
)

// Conversation logging: log turn complete
if (isConversationLoggingEnabled() && lastMsg) {
logTurnComplete(
{
input_tokens: usage.input_tokens ?? 0,
output_tokens: usage.output_tokens ?? 0,
cache_read_input_tokens: usage.cache_read_input_tokens,
cache_creation_input_tokens: usage.cache_creation_input_tokens,
},
stopReason,
costUSD,
lastMsg.message.id,
)
}

const refusalMessage = getErrorMessageIfRefusal(
part.delta.stop_reason,
options.model,
Expand Down Expand Up @@ -2596,6 +2669,11 @@ async function* queryModel(
clearStreamIdleTimers()
}
} catch (errorFromRetry) {
// Conversation logging: log turn error
if (isConversationLoggingEnabled()) {
logTurnError(errorMessage(errorFromRetry))
}

// FallbackTriggeredError must propagate to query.ts, which performs the
// actual model switch. Swallowing it here would turn the fallback into a
// no-op — the user would just see "Model fallback triggered: X -> Y" as
Expand Down
13 changes: 13 additions & 0 deletions src/services/api/withRetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ import {
checkMockRateLimitError,
isMockRateLimitError,
} from '../rateLimitMocking.js'
import {
isConversationLoggingEnabled,
logRetry as logConversationRetry,
} from '../conversationLogger.js'
import { REPEATED_529_ERROR_MESSAGE } from './errors.js'
import { extractConnectionErrorDetails } from './errorUtils.js'

Expand Down Expand Up @@ -258,6 +262,15 @@ export async function* withRetry<T>(
{ level: 'error' },
)

// Conversation logging: log retry event
if (isConversationLoggingEnabled() && error instanceof Error) {
logConversationRetry(
attempt,
error,
error instanceof APIError ? error.status : undefined,
)
}

// Fast mode fallback: on 429/529, either wait and retry (short delays)
// or fall back to standard speed (long delays) to avoid cache thrashing.
// Skip in persistent mode: the short-retry path below loops with fast
Expand Down
7 changes: 7 additions & 0 deletions src/services/compact/compact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ const sessionTranscriptModule = feature('KAIROS')
import { APIUserAbortError } from '@anthropic-ai/sdk'
import { markPostCompaction } from 'src/bootstrap/state.js'
import { getInvokedSkillsForAgent } from '../../bootstrap/state.js'
import {
isConversationLoggingEnabled,
logCompaction,
} from '../conversationLogger.js'
import type { QuerySource } from '../../constants/querySource.js'
import type { CanUseToolFn } from '../../hooks/useCanUseTool.js'
import type { Tool, ToolUseContext } from '../../Tool.js'
Expand Down Expand Up @@ -398,6 +402,9 @@ export async function compactConversation(
throw new Error(ERROR_MESSAGE_NOT_ENOUGH_MESSAGES)
}

// Log compaction event for conversation logging
logCompaction()

const preCompactTokenCount = tokenCountWithEstimation(messages)

const appState = context.getAppState()
Expand Down
Loading