mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-15 21:05:51 +00:00
Compare commits
197 Commits
v1.2.0
...
codex/code
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3b16ae040 | ||
|
|
a90c16431b | ||
|
|
a5ede237f0 | ||
|
|
85dc1b9462 | ||
|
|
b47731a3f3 | ||
|
|
a65df4a102 | ||
|
|
52b61c2c06 | ||
|
|
3cb4828de6 | ||
|
|
f5c3ee5b5d | ||
|
|
c2ac9a74c1 | ||
|
|
fc438bd222 | ||
|
|
4591432a1d | ||
|
|
901628b4d9 | ||
|
|
cf33c06021 | ||
|
|
e0ca1d054c | ||
|
|
6585d0f67c | ||
|
|
e4403ff010 | ||
|
|
9e61e7a90d | ||
|
|
d03af7bd4e | ||
|
|
e8ef955ff9 | ||
|
|
a8ed0cdce5 | ||
|
|
1c3b280c6a | ||
|
|
7a3cc24a00 | ||
|
|
2e7fc428cd | ||
|
|
ad09f38fd1 | ||
|
|
b0a3ef90dc | ||
|
|
c07ad4c738 | ||
|
|
e38d45460e | ||
|
|
e0c8e9dafc | ||
|
|
047c85fcbf | ||
|
|
da6d06365d | ||
|
|
8613d558a8 | ||
|
|
017c251f78 | ||
|
|
d4223abc34 | ||
|
|
5125a159d2 | ||
|
|
d09f363414 | ||
|
|
9d35f98ec7 | ||
|
|
eb833da33b | ||
|
|
eadd32ae47 | ||
|
|
3c55a8c83f | ||
|
|
5582bb47ef | ||
|
|
95bb191977 | ||
|
|
03811f973b | ||
|
|
02ab1a0307 | ||
|
|
2a5b263641 | ||
|
|
f2dd5142b3 | ||
|
|
4dcbaf1e66 | ||
|
|
0b304730d8 | ||
|
|
7a0dd3057e | ||
|
|
ca1c87f460 | ||
|
|
fc7a85f5c7 | ||
|
|
5bc12b00b2 | ||
|
|
792777d68c | ||
|
|
047634afe6 | ||
|
|
a92af99448 | ||
|
|
cfe1552ec9 | ||
|
|
9624f880e0 | ||
|
|
85e5a8cffb | ||
|
|
299953b0ee | ||
|
|
7a3fdf6e67 | ||
|
|
b642977afe | ||
|
|
781188862e | ||
|
|
b966eef5a9 | ||
|
|
c3d63c8fe2 | ||
|
|
7d4c4278c0 | ||
|
|
93bfdabff1 | ||
|
|
1173a62301 | ||
|
|
7ea69ca279 | ||
|
|
4e82fb5974 | ||
|
|
f43350e600 | ||
|
|
23fcbf9004 | ||
|
|
23bb09d240 | ||
|
|
d208855f07 | ||
|
|
7881cc617c | ||
|
|
c7e1c50b86 | ||
|
|
2247026bd5 | ||
|
|
eec961352b | ||
|
|
fb41513b32 | ||
|
|
94c4b37eed | ||
|
|
6c5df395c3 | ||
|
|
be97a0b010 | ||
|
|
59f8675fa3 | ||
|
|
c4775fff58 | ||
|
|
31b2fdd97a | ||
|
|
1837df5f88 | ||
|
|
04c7ed4250 | ||
|
|
711927f01b | ||
|
|
956e98a445 | ||
|
|
cee62bc654 | ||
|
|
5fc7c8e13d | ||
|
|
300faa18d0 | ||
|
|
96ec96c720 | ||
|
|
13a0bfc479 | ||
|
|
84f0271813 | ||
|
|
ed4bdb9338 | ||
|
|
e4ce08fe39 | ||
|
|
92f8a92fbb | ||
|
|
a67e2d0e97 | ||
|
|
8c629858ab | ||
|
|
494eab7204 | ||
|
|
b83c3008d0 | ||
|
|
66d2671c98 | ||
|
|
c7bc8c8636 | ||
|
|
673ccd1800 | ||
|
|
d1ab38c089 | ||
|
|
f9d011164a | ||
|
|
481e2a58a9 | ||
|
|
c5edee431f | ||
|
|
a57ca08566 | ||
|
|
6536757428 | ||
|
|
a0dc4540ca | ||
|
|
7e4df5c3e9 | ||
|
|
4d939e5722 | ||
|
|
2e9aaf4993 | ||
|
|
34154ee3f5 | ||
|
|
29cc74a170 | ||
|
|
d2b66d9d2c | ||
|
|
d70e7f7f05 | ||
|
|
72a2093cd6 | ||
|
|
b5c299f5d2 | ||
|
|
ac42ce2d67 | ||
|
|
c659912517 | ||
|
|
a14b7f352b | ||
|
|
c5ab83a3fc | ||
|
|
03b7f9b453 | ||
|
|
bddd146f25 | ||
|
|
c8d08d235b | ||
|
|
a02dc0bded | ||
|
|
3cb1e50b25 | ||
|
|
cfab161e28 | ||
|
|
90027279e6 | ||
|
|
3470783ced | ||
|
|
8169b96250 | ||
|
|
fe08cacf8d | ||
|
|
5a4c820e1d | ||
|
|
1a4e9702c2 | ||
|
|
2273a0bcfe | ||
|
|
b80483c23e | ||
|
|
8442aaadd2 | ||
|
|
dad3ad2b8d | ||
|
|
b5b81dfe49 | ||
|
|
ecbd5a93e4 | ||
|
|
be80da4ce0 | ||
|
|
fce40fed1f | ||
|
|
a7e03a5b30 | ||
|
|
05cabbbd73 | ||
|
|
d4b30d32c3 | ||
|
|
e0484e2817 | ||
|
|
2fb1c9dcd8 | ||
|
|
bbb8b613a9 | ||
|
|
c63b875ae3 | ||
|
|
9b8503d13d | ||
|
|
3cf94fbda0 | ||
|
|
9a3081dff6 | ||
|
|
bd6448ecda | ||
|
|
1071270ce3 | ||
|
|
711440474c | ||
|
|
8399d9ed20 | ||
|
|
513ccc3003 | ||
|
|
e770f1ef9d | ||
|
|
227083d31f | ||
|
|
14c46df881 | ||
|
|
e0e4ee41c2 | ||
|
|
e9861415c0 | ||
|
|
423f114db6 | ||
|
|
c8a502f81f | ||
|
|
09fc515edb | ||
|
|
2fea429dc6 | ||
|
|
6a9da9d546 | ||
|
|
d27c6cbc64 | ||
|
|
ffd1c366eb | ||
|
|
5beeebad59 | ||
|
|
c676ac4693 | ||
|
|
eeb0f2776e | ||
|
|
6a70056910 | ||
|
|
7088fe3c8b | ||
|
|
b060eabda9 | ||
|
|
9da7345f8e | ||
|
|
8137b66a46 | ||
|
|
b681139b63 | ||
|
|
0b1e678fb7 | ||
|
|
81073135e2 | ||
|
|
ff03fe7fcb | ||
|
|
c82f59943c | ||
|
|
e70319e8f5 | ||
|
|
609e91143f | ||
|
|
637531f81f | ||
|
|
875510e1eb | ||
|
|
34bbc1d403 | ||
|
|
a14d3dc8f0 | ||
|
|
ab3d8ef87e | ||
|
|
dfce6d02f9 | ||
|
|
01cf45f4ac | ||
|
|
e6affc7053 | ||
|
|
bb07836231 | ||
|
|
87230cf3bf | ||
|
|
8c619a215c |
@@ -41,7 +41,8 @@ All teach-me data is stored under `.claude/skills/teach-me/records/`:
|
|||||||
.claude/skills/teach-me/records/
|
.claude/skills/teach-me/records/
|
||||||
├── learner-profile.md # Cross-topic notes (created on first session)
|
├── learner-profile.md # Cross-topic notes (created on first session)
|
||||||
└── {topic-slug}/
|
└── {topic-slug}/
|
||||||
└── session.md # Learning state: concepts, status, notes
|
├── session.md # Learning state: concepts, status, notes
|
||||||
|
└── {topic-slug}-notes.md # Learner-facing summary notes (generated at session end)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Slug**: Topic in kebab-case, 2-5 words. Example: "Python decorators" → `python-decorators`
|
**Slug**: Topic in kebab-case, 2-5 words. Example: "Python decorators" → `python-decorators`
|
||||||
@@ -275,7 +276,8 @@ Update `session.md` after each round:
|
|||||||
When all concepts mastered or user ends session:
|
When all concepts mastered or user ends session:
|
||||||
|
|
||||||
1. Update `session.md` with final state.
|
1. Update `session.md` with final state.
|
||||||
2. Update `.claude/skills/teach-me/records/learner-profile.md` (keep under 30 lines):
|
2. **Generate learner-facing notes** — write `{topic-slug}-notes.md` in the topic directory. This is a standalone reference document the learner can review later. See "Notes Generation" below for format.
|
||||||
|
3. Update `.claude/skills/teach-me/records/learner-profile.md` (keep under 30 lines):
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
# Learner Profile
|
# Learner Profile
|
||||||
@@ -293,7 +295,48 @@ Updated: {timestamp}
|
|||||||
- Python decorators (8/10 concepts, 2025-01-15)
|
- Python decorators (8/10 concepts, 2025-01-15)
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Give a brief text summary of what was covered, key insights, and areas for further study.
|
4. Give a brief text summary of what was covered, key insights, and areas for further study.
|
||||||
|
|
||||||
|
## Notes Generation
|
||||||
|
|
||||||
|
At session end, generate a learner-facing notes file at `{topic-slug}/{topic-slug}-notes.md`. This file is **written for the learner to review later**, not for the tutor. It should be self-contained and organized as a quick-reference.
|
||||||
|
|
||||||
|
### Notes Structure
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# {Topic} 核心笔记
|
||||||
|
|
||||||
|
## 1. {Section Name}
|
||||||
|
{Key concept, mechanism, or principle}
|
||||||
|
* **One-line summary**: {what it does / why it matters}
|
||||||
|
* **Detail**: {brief explanation, 2-4 sentences max}
|
||||||
|
* **Example** (if applicable): {code snippet, command, or concrete scenario}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. {Section Name}
|
||||||
|
...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## n. 实战参数 / Cheat Sheet (if applicable)
|
||||||
|
{Practical commands, config, or quick-reference table}
|
||||||
|
|
||||||
|
| Parameter / Concept | What it does | Tuning tip |
|
||||||
|
|---------------------|-------------|------------|
|
||||||
|
| ... | ... | ... |
|
||||||
|
```
|
||||||
|
|
||||||
|
### Notes Writing Rules
|
||||||
|
|
||||||
|
1. **Start with "what & why"** before "how". Each section should answer: what is this, why does it exist, what problem does it solve.
|
||||||
|
2. **Use analogies sparingly but effectively**. Only include an analogy if it clarifies a non-obvious mechanism (e.g., "PagedAttention is like OS virtual memory paging").
|
||||||
|
3. **Include trade-offs**. Every optimization or design choice has a cost. Always state it (e.g., "TP improves throughput but increases communication latency").
|
||||||
|
4. **Code / command examples should be minimal**. Under 10 lines, self-contained, with comments explaining the key flags.
|
||||||
|
5. **Organize by concept dependency**, not by chronological teaching order. Foundation concepts first, advanced ones last.
|
||||||
|
6. **No quiz questions, no misconceptions, no tutor-side notes**. This is a clean reference document.
|
||||||
|
7. **Language matches the session**. If the session was in Chinese, notes are in Chinese (technical terms can stay in English).
|
||||||
|
8. **Keep it under 150 lines**. If it gets too long, the learner won't review it. Be ruthless about cutting fluff.
|
||||||
|
|
||||||
## Resuming Sessions
|
## Resuming Sessions
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
# pre-commit hook: 对暂存的文件运行 Biome 检查
|
|
||||||
# 仅检查 src/ 下的 .ts/.tsx/.js/.jsx 文件
|
|
||||||
|
|
||||||
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '^src/.*\.(ts|tsx|js|jsx)$')
|
|
||||||
|
|
||||||
if [ -z "$STAGED_FILES" ]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Running Biome lint on staged files..."
|
|
||||||
|
|
||||||
# 使用 biome lint 对暂存文件进行检查(仅 lint,不格式化,不自动修复)
|
|
||||||
echo "$STAGED_FILES" | xargs bunx biome lint --no-errors-on-unmatched
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo ""
|
|
||||||
echo "Biome lint failed. Fix errors or use --no-verify to bypass."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
39
.github/workflows/ci.yml
vendored
39
.github/workflows/ci.yml
vendored
@@ -6,22 +6,49 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
runs-on: macos-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, 2026-04-25
|
||||||
|
env:
|
||||||
|
GIT_CONFIG_COUNT: 2
|
||||||
|
GIT_CONFIG_KEY_0: init.defaultBranch
|
||||||
|
GIT_CONFIG_VALUE_0: main
|
||||||
|
GIT_CONFIG_KEY_1: advice.defaultBranchName
|
||||||
|
GIT_CONFIG_VALUE_1: "false"
|
||||||
|
|
||||||
- uses: oven-sh/setup-bun@v2
|
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2, 2026-04-25
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
env:
|
||||||
|
CLAUDE_CODE_SKIP_CHROME_MCP_SETUP: "1"
|
||||||
run: bun install --frozen-lockfile
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
- name: Test
|
- name: Type check
|
||||||
run: bun test
|
run: bun run typecheck
|
||||||
|
|
||||||
|
- name: Test with Coverage
|
||||||
|
run: |
|
||||||
|
set -o pipefail
|
||||||
|
bun test --coverage --coverage-reporter lcov --coverage-dir coverage 2>&1 | grep -vE '^\s*(\(pass\)|\(skip\))' | sed '/^.*\/__tests__\/.*:$/d' | cat -s
|
||||||
|
test -s coverage/lcov.info
|
||||||
|
grep -q '^SF:' coverage/lcov.info
|
||||||
|
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
|
||||||
|
uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5, 2026-04-25
|
||||||
|
with:
|
||||||
|
fail_ci_if_error: true
|
||||||
|
files: ./coverage/lcov.info
|
||||||
|
disable_search: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: bun run build
|
run: bun run build:vite
|
||||||
|
|||||||
79
.github/workflows/publish-npm.yml
vendored
Normal file
79
.github/workflows/publish-npm.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
name: Publish to npm
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: '版本号 (例如: v1.9.0)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, 2026-04-25
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.inputs.version || github.ref }}
|
||||||
|
|
||||||
|
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6, 2026-04-25
|
||||||
|
with:
|
||||||
|
node-version: "24"
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2, 2026-04-25
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install --frozen-lockfile
|
||||||
|
- name: Type check
|
||||||
|
run: bun run typecheck
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: bun test
|
||||||
|
|
||||||
|
- name: Publish to npm
|
||||||
|
run: npm publish --provenance --access public
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate changelog
|
||||||
|
id: changelog
|
||||||
|
run: |
|
||||||
|
VERSION="${{ github.event.inputs.version || github.ref_name }}"
|
||||||
|
PREV_TAG=$(git tag --sort=-version:refname | grep -v "^${VERSION#v}$" | head -1)
|
||||||
|
|
||||||
|
if [ -n "$PREV_TAG" ]; then
|
||||||
|
COMMITS=$(git log "${PREV_TAG}..${VERSION}" --pretty=format:"- %s (%h)" --no-merges)
|
||||||
|
else
|
||||||
|
COMMITS=$(git log --pretty=format:"- %s (%h)" --no-merges -20)
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "commits<<EOF"
|
||||||
|
echo "$COMMITS"
|
||||||
|
echo "EOF"
|
||||||
|
} >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2, 2026-04-25
|
||||||
|
with:
|
||||||
|
name: ${{ github.event.inputs.version || github.ref_name }}
|
||||||
|
body: |
|
||||||
|
## What's Changed
|
||||||
|
|
||||||
|
${{ steps.changelog.outputs.commits }}
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ github.event.inputs.version || github.ref_name }}^...${{ github.event.inputs.version || github.ref_name }}
|
||||||
|
draft: false
|
||||||
|
prerelease: ${{ contains(github.event.inputs.version || github.ref_name, 'rc') || contains(github.event.inputs.version || github.ref_name, 'beta') || contains(github.event.inputs.version || github.ref_name, 'alpha') }}
|
||||||
8
.github/workflows/release-rcs.yml
vendored
8
.github/workflows/release-rcs.yml
vendored
@@ -17,17 +17,17 @@ jobs:
|
|||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, 2026-04-25
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3, 2026-04-25
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3, 2026-04-25
|
||||||
|
|
||||||
- name: Extract version
|
- name: Extract version
|
||||||
id: version
|
id: version
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
echo "tags=$TAGS" >> "$GITHUB_OUTPUT"
|
echo "tags=$TAGS" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5, 2026-04-25
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: packages/remote-control-server/Dockerfile
|
file: packages/remote-control-server/Dockerfile
|
||||||
|
|||||||
11
.github/workflows/update-contributors.yml
vendored
11
.github/workflows/update-contributors.yml
vendored
@@ -1,11 +1,8 @@
|
|||||||
name: Update Contributors
|
name: Update Contributors
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *' # 每天更新一次
|
- cron: '0 0 * * 1' # 每周一更新一次
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -14,17 +11,17 @@ jobs:
|
|||||||
update:
|
update:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2, 2026-04-25
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- uses: jaywcjlove/github-action-contributors@main
|
- uses: jaywcjlove/github-action-contributors@86707f6d4c2469ce6b46bc3367253ebd41ee242c # main, 2026-04-25
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
output: "contributors.svg"
|
output: "contributors.svg"
|
||||||
repository: ${{ github.repository }}
|
repository: ${{ github.repository }}
|
||||||
|
|
||||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
- uses: stefanzweifel/git-auto-commit-action@b863ae1933cb653a53c021fe36dbb774e1fb9403 # v5, 2026-04-25
|
||||||
with:
|
with:
|
||||||
commit_message: "docs: update contributors"
|
commit_message: "docs: update contributors"
|
||||||
file_pattern: "contributors.svg"
|
file_pattern: "contributors.svg"
|
||||||
|
|||||||
20
.gitignore
vendored
20
.gitignore
vendored
@@ -12,13 +12,18 @@ src/utils/vendor/
|
|||||||
|
|
||||||
# AI tool runtime directories
|
# AI tool runtime directories
|
||||||
.agents/
|
.agents/
|
||||||
.codex/
|
.claude/
|
||||||
.omx/
|
.omx/
|
||||||
|
.docs/task/
|
||||||
# Binary / screenshot files (root only)
|
# Binary / screenshot files (root only)
|
||||||
/*.png
|
/*.png
|
||||||
*.bmp
|
*.bmp
|
||||||
|
|
||||||
|
# Internal system prompt documents
|
||||||
|
Claude-Opus-*.txt
|
||||||
|
Claude-Sonnet-*.txt
|
||||||
|
Claude-Haiku-*.txt
|
||||||
|
|
||||||
# Agent / tool state dirs
|
# Agent / tool state dirs
|
||||||
.swarm/
|
.swarm/
|
||||||
.agents/__pycache__/
|
.agents/__pycache__/
|
||||||
@@ -29,3 +34,14 @@ __pycache__/
|
|||||||
logs
|
logs
|
||||||
|
|
||||||
data
|
data
|
||||||
|
.omc
|
||||||
|
.codex/*
|
||||||
|
!.codex/agents/
|
||||||
|
!.codex/agents/**
|
||||||
|
!.codex/skills/
|
||||||
|
!.codex/skills/**
|
||||||
|
.codex/skills/.system/**
|
||||||
|
!.codex/prompts/
|
||||||
|
!.codex/prompts/**
|
||||||
|
teach-me
|
||||||
|
credentials.json
|
||||||
|
|||||||
78
.impeccable.md
Normal file
78
.impeccable.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# Impeccable Design Context
|
||||||
|
|
||||||
|
## Users
|
||||||
|
|
||||||
|
**Primary**: Technical teams and enterprises using AI-assisted coding in production workflows.
|
||||||
|
- DevOps engineers managing remote agents via RCS dashboard
|
||||||
|
- Development teams collaborating through shared sessions
|
||||||
|
- Individual developers using terminal CLI daily
|
||||||
|
|
||||||
|
**Context**: Used during focused work sessions — debugging, code review, agent orchestration. Users are in "get things done" mode, not browsing. They value efficiency but also appreciate warmth and personality.
|
||||||
|
|
||||||
|
**Job to be done**: Make advanced AI coding tools accessible and controllable, especially features that normally require enterprise accounts or Anthropic OAuth.
|
||||||
|
|
||||||
|
## Brand Personality
|
||||||
|
|
||||||
|
**3 words**: Warm, Considered, Human
|
||||||
|
|
||||||
|
**Voice**: Like a knowledgeable colleague who's genuinely enthusiastic about the craft — not a corporate product manager. Community-first, open, slightly playful. Chinese developer community culture (贴吧/discord 温暖氛围).
|
||||||
|
|
||||||
|
**Emotional goals**: Confidence (this tool is solid), Warmth (this community is welcoming), Delight (small moments of personality make the difference).
|
||||||
|
|
||||||
|
**References**:
|
||||||
|
- **Anthropic's own design language** — their clean, considered aesthetic with warm undertones. The terra cotta/burnt orange as a human accent. Lots of breathing room. Typography-forward.
|
||||||
|
- **NOT**: Generic AI product (no ChatGPT blue, no gradient text, no "AI slop"). NOT corporate SaaS (no Salesforce-blue dashboards, no enterprise sterility).
|
||||||
|
|
||||||
|
**Anti-references**: Corporate enterprise dashboards, generic AI product pages, anything that looks like it was "designed by committee."
|
||||||
|
|
||||||
|
## Aesthetic Direction
|
||||||
|
|
||||||
|
**Theme**: Light + Dark dual mode (user/system preference switch)
|
||||||
|
|
||||||
|
**Tone**: Anthropomorphic warmth meets terminal precision. The brand orange (Claude's terra cotta) is the thread that ties everything together — it's the human element in a technical world.
|
||||||
|
|
||||||
|
**Typography**: Clean, considered, with good hierarchy. Terminal-native for CLI; modern web fonts for Web UI (RCS dashboard, docs). Favor readability and personality.
|
||||||
|
|
||||||
|
**Color**:
|
||||||
|
- Primary: Claude orange family (`#D77757` / terra cotta)
|
||||||
|
- Accent: Warm neutrals tinted toward orange
|
||||||
|
- Semantic: Success/Error/Warning following Anthropic's established palette
|
||||||
|
- Dark mode: Warm dark surfaces (not cold blue-black)
|
||||||
|
|
||||||
|
**Differentiation**: The CCB brand sits at the intersection of "serious tool" and "community project." It should feel like Anthropic's design principles applied to an open-source context — less corporate polish, more human craft. The mascot "Clawd" and the playful "踩踩背" naming hint at personality that the design should honor.
|
||||||
|
|
||||||
|
**Scope**: All Web UI — RCS control panel, documentation site, landing pages.
|
||||||
|
|
||||||
|
## Design Principles
|
||||||
|
|
||||||
|
1. **Considered over clever** — Every design choice should feel intentional, not trendy. If it doesn't serve the user, it doesn't ship.
|
||||||
|
2. **Warmth through subtlety** — Orange tints on neutrals, breathing room in layouts, personality in copy. Not giant emoji or aggressive color.
|
||||||
|
3. **Density with clarity** — Technical users need information density, but not chaos. Every pixel earns its place.
|
||||||
|
4. **Community voice** — The design should feel like it was made by people who use it, not by a distant design team. Slightly rough edges are fine if they're honest.
|
||||||
|
5. **Anthropic's shadow** — When in doubt, follow Anthropic's design instincts — the clean layouts, the generous spacing, the warm color temperature. Then add the community touch.
|
||||||
|
|
||||||
|
## Existing Design Assets
|
||||||
|
|
||||||
|
### Brand Colors (from theme system)
|
||||||
|
- Claude Orange: `rgb(215,119,87)` / `#D77757`
|
||||||
|
- Claude Blue: `rgb(87,105,247)` / `#5769F7`
|
||||||
|
- Permission Blue: `rgb(87,105,247)`
|
||||||
|
- Auto Accept Violet: `rgb(135,0,255)`
|
||||||
|
- Plan Mode Teal: `rgb(0,102,102)`
|
||||||
|
- Success: `rgb(78,186,101)`
|
||||||
|
- Error: `rgb(255,107,128)`
|
||||||
|
- Warning: `rgb(255,193,7)`
|
||||||
|
|
||||||
|
### Logo
|
||||||
|
- CCB text + orange play button icon
|
||||||
|
- Dark/Light SVG variants in `docs/logo/`
|
||||||
|
- Favicon: Orange circle `#D97706` with white play triangle
|
||||||
|
|
||||||
|
### Mascot
|
||||||
|
- "Clawd" — terminal-art character with multiple poses
|
||||||
|
- Theme-aware coloring
|
||||||
|
|
||||||
|
### Theme System
|
||||||
|
- 7 variants: dark, light, dark-ansi, light-ansi, dark-daltonized, light-daltonized, auto
|
||||||
|
- 89+ semantic color tokens
|
||||||
|
- Full documentation in `packages/@ant/ink/docs/04-theme-system.md`
|
||||||
2
.mintignore
Normal file
2
.mintignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
src/
|
||||||
|
packages/
|
||||||
357
AGENTS.md
Normal file
357
AGENTS.md
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) and other AI coding agents when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This is a **reverse-engineered / decompiled** version of Anthropic's official Claude Code CLI tool. The goal is to restore core functionality while trimming secondary capabilities. Many modules are stubbed or feature-flagged off. TypeScript strict mode is enforced — **`bunx tsc --noEmit` must pass with zero errors**.
|
||||||
|
|
||||||
|
## Git Commit Message Convention
|
||||||
|
|
||||||
|
使用 **Conventional Commits** 规范:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>: <描述>
|
||||||
|
```
|
||||||
|
|
||||||
|
常见 type:`feat`、`fix`、`docs`、`chore`、`refactor`
|
||||||
|
|
||||||
|
示例:
|
||||||
|
- `feat: 添加模型 1M 上下文切换`
|
||||||
|
- `fix: 修复初次登陆的校验问题`
|
||||||
|
- `chore: remove prefetchOfficialMcpUrls call on startup`
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
bun install
|
||||||
|
|
||||||
|
# Dev mode (runs cli.tsx with MACRO defines injected via -d flags)
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# Dev mode with debugger (set BUN_INSPECT=9229 to pick port)
|
||||||
|
bun run dev:inspect
|
||||||
|
|
||||||
|
# Pipe mode
|
||||||
|
echo "say hello" | bun run src/entrypoints/cli.tsx -p
|
||||||
|
|
||||||
|
# Build (code splitting, outputs dist/cli.js + chunk files)
|
||||||
|
bun run build
|
||||||
|
|
||||||
|
# Build with Vite (alternative build pipeline)
|
||||||
|
bun run build:vite
|
||||||
|
|
||||||
|
# Test
|
||||||
|
bun test # run all tests
|
||||||
|
bun test src/utils/__tests__/hash.test.ts # run single file
|
||||||
|
bun test --coverage # with coverage report
|
||||||
|
|
||||||
|
# Lint & Format (Biome)
|
||||||
|
bun run lint # check only
|
||||||
|
bun run lint:fix # auto-fix
|
||||||
|
bun run format # format all src/
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
bun run health
|
||||||
|
|
||||||
|
# Check unused exports
|
||||||
|
bun run check:unused
|
||||||
|
|
||||||
|
# Full check (typecheck + lint + test) — run after completing any task
|
||||||
|
bun run test:all
|
||||||
|
bun run typecheck
|
||||||
|
|
||||||
|
# Remote Control Server
|
||||||
|
bun run rcs
|
||||||
|
|
||||||
|
# Docs dev server (Mintlify)
|
||||||
|
bun run docs:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
详细的测试规范、覆盖状态和改进计划见 `docs/testing-spec.md`。
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Runtime & Build
|
||||||
|
|
||||||
|
- **Runtime**: Bun (not Node.js). All imports, builds, and execution use Bun APIs.
|
||||||
|
- **Build**: `build.ts` 执行 `Bun.build()` with `splitting: true`,入口 `src/entrypoints/cli.tsx`,输出 `dist/cli.js` + chunk files。Build 默认启用 19 个 feature(见下方 Feature Flag 段)。构建后自动替换 `import.meta.require` 为 Node.js 兼容版本(产物 bun/node 都可运行)。
|
||||||
|
- **Dev mode**: `scripts/dev.ts` 通过 Bun `-d` flag 注入 `MACRO.*` defines,运行 `src/entrypoints/cli.tsx`。默认启用全部 feature。
|
||||||
|
- **Module system**: ESM (`"type": "module"`), TSX with `react-jsx` transform.
|
||||||
|
- **Monorepo**: Bun workspaces — 15 个 workspace packages + 若干辅助目录 in `packages/` resolved via `workspace:*`。
|
||||||
|
- **Lint/Format**: Biome (`biome.json`)。`bun run lint` / `bun run lint:fix` / `bun run format`。
|
||||||
|
- **Defines**: 集中管理在 `scripts/defines.ts`。当前版本 `2.1.888`。
|
||||||
|
- **CI**: GitHub Actions — `ci.yml`(构建+测试)、`release-rcs.yml`(RCS 发布)、`update-contributors.yml`(自动更新贡献者)。
|
||||||
|
|
||||||
|
### Entry & Bootstrap
|
||||||
|
|
||||||
|
1. **`src/entrypoints/cli.tsx`** — True entrypoint。`main()` 函数按优先级处理多条快速路径:
|
||||||
|
- `--version` / `-v` — 零模块加载
|
||||||
|
- `--dump-system-prompt` — feature-gated (DUMP_SYSTEM_PROMPT)
|
||||||
|
- `--claude-in-chrome-mcp` / `--chrome-native-host`
|
||||||
|
- `--computer-use-mcp` — 独立 MCP server 模式
|
||||||
|
- `--daemon-worker=<kind>` — feature-gated (DAEMON)
|
||||||
|
- `remote-control` / `rc` / `remote` / `sync` / `bridge` — feature-gated (BRIDGE_MODE)
|
||||||
|
- `daemon` [subcommand] — feature-gated (DAEMON)
|
||||||
|
- `ps` / `logs` / `attach` / `kill` / `--bg` — feature-gated (BG_SESSIONS)
|
||||||
|
- `new` / `list` / `reply` — Template job commands
|
||||||
|
- `environment-runner` / `self-hosted-runner` — BYOC runner
|
||||||
|
- `--tmux` + `--worktree` 组合
|
||||||
|
- 默认路径:加载 `main.tsx` 启动完整 CLI
|
||||||
|
2. **`src/main.tsx`** (~6981 行) — Commander.js CLI definition。注册大量 subcommands:`mcp` (serve/add/remove/list...)、`server`、`ssh`、`open`、`auth`、`plugin`、`agents`、`auto-mode`、`doctor`、`update` 等。主 `.action()` 处理器负责权限、MCP、会话恢复、REPL/Headless 模式分发。
|
||||||
|
3. **`src/entrypoints/init.ts`** — One-time initialization (telemetry, config, trust dialog)。
|
||||||
|
|
||||||
|
### Core Loop
|
||||||
|
|
||||||
|
- **`src/query.ts`** — The main API query function. Sends messages to Claude API, handles streaming responses, processes tool calls, and manages the conversation turn loop.
|
||||||
|
- **`src/QueryEngine.ts`** — Higher-level orchestrator wrapping `query()`. Manages conversation state, compaction, file history snapshots, attribution, and turn-level bookkeeping. Used by the REPL screen.
|
||||||
|
- **`src/screens/REPL.tsx`** — The interactive REPL screen (React/Ink component). Handles user input, message display, tool permission prompts, and keyboard shortcuts.
|
||||||
|
|
||||||
|
### API Layer
|
||||||
|
|
||||||
|
- **`src/services/api/claude.ts`** — Core API client. Builds request params (system prompt, messages, tools, betas), calls the Anthropic SDK streaming endpoint, and processes `BetaRawMessageStreamEvent` events.
|
||||||
|
- **7 providers**: `firstParty` (Anthropic direct), `bedrock` (AWS), `vertex` (Google Cloud), `foundry`, `openai`, `gemini`, `grok` (xAI)。
|
||||||
|
- Provider selection in `src/utils/model/providers.ts`。优先级:modelType 参数 > 环境变量 > 默认 firstParty。
|
||||||
|
|
||||||
|
### Tool System
|
||||||
|
|
||||||
|
- **`src/Tool.ts`** — Tool interface definition (`Tool` type) and utilities (`findToolByName`, `toolMatchesName`).
|
||||||
|
- **`src/tools.ts`** — Tool registry. Assembles the tool list; tools are imported from `@claude-code-best/builtin-tools` package. Some tools are conditionally loaded via `feature()` flags or `process.env.USER_TYPE`.
|
||||||
|
- **`packages/builtin-tools/src/tools/`** — 59 个子目录(含 shared/testing 等工具目录),通过 `@claude-code-best/builtin-tools` 包导出。主要分类:
|
||||||
|
- **文件操作**: FileEditTool, FileReadTool, FileWriteTool, GlobTool, GrepTool
|
||||||
|
- **Shell/执行**: BashTool, PowerShellTool, REPLTool
|
||||||
|
- **Agent 系统**: AgentTool, TaskCreateTool, TaskUpdateTool, TaskListTool, TaskGetTool
|
||||||
|
- **规划**: EnterPlanModeTool, ExitPlanModeV2Tool, VerifyPlanExecutionTool
|
||||||
|
- **Web/MCP**: WebFetchTool, WebSearchTool, MCPTool, McpAuthTool
|
||||||
|
- **调度**: CronCreateTool, CronDeleteTool, CronListTool
|
||||||
|
- **其他**: LSPTool, ConfigTool, SkillTool, EnterWorktreeTool, ExitWorktreeTool 等
|
||||||
|
- **`src/tools/shared/`** / **`packages/builtin-tools/src/tools/shared/`** — Tool 共享工具函数。
|
||||||
|
|
||||||
|
### UI Layer (Ink)
|
||||||
|
|
||||||
|
- **`src/ink.ts`** — Ink render wrapper with ThemeProvider injection.
|
||||||
|
- **`packages/@ant/ink/`** — Custom Ink framework(forked/internal),包含 components、core、hooks、keybindings、theme、utils。注意:不是 `src/ink/`。
|
||||||
|
- **`src/components/`** — 149 个组件目录/文件,渲染于终端 Ink 环境中。关键组件:
|
||||||
|
- `App.tsx` — Root provider (AppState, Stats, FpsMetrics)
|
||||||
|
- `Messages.tsx` / `MessageRow.tsx` — Conversation message rendering
|
||||||
|
- `PromptInput/` — User input handling
|
||||||
|
- `permissions/` — Tool permission approval UI
|
||||||
|
- `design-system/` — 复用 UI 组件(Dialog, FuzzyPicker, ProgressBar, ThemeProvider 等)
|
||||||
|
- Components use React Compiler runtime (`react/compiler-runtime`) — decompiled output has `_c()` memoization calls throughout.
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
|
||||||
|
- **`src/state/AppState.tsx`** — Central app state type and context provider. Contains messages, tools, permissions, MCP connections, etc.
|
||||||
|
- **`src/state/AppStateStore.ts`** — Default state and store factory.
|
||||||
|
- **`src/state/store.ts`** — Zustand-style store for AppState (`createStore`).
|
||||||
|
- **`src/state/selectors.ts`** — State selectors.
|
||||||
|
- **`src/bootstrap/state.ts`** — Module-level singletons for session-global state (session ID, CWD, project root, token counts, model overrides, client type, permission mode).
|
||||||
|
|
||||||
|
### Workspace Packages
|
||||||
|
|
||||||
|
| Package | 说明 |
|
||||||
|
|---------|------|
|
||||||
|
| `packages/@ant/ink/` | Forked Ink 框架(components、hooks、keybindings、theme) |
|
||||||
|
| `packages/@ant/computer-use-mcp/` | Computer Use MCP server(截图/键鼠/剪贴板/应用管理) |
|
||||||
|
| `packages/@ant/computer-use-input/` | 键鼠模拟(dispatcher + darwin/win32/linux backend) |
|
||||||
|
| `packages/@ant/computer-use-swift/` | 截图 + 应用管理(dispatcher + per-platform backend) |
|
||||||
|
| `packages/@ant/claude-for-chrome-mcp/` | Chrome 浏览器控制(通过 `--chrome` 启用) |
|
||||||
|
| `packages/@ant/model-provider/` | Model provider 抽象层 |
|
||||||
|
| `packages/builtin-tools/` | 内置工具集(60 个 tool 实现,通过 `@claude-code-best/builtin-tools` 导出) |
|
||||||
|
| `packages/agent-tools/` | Agent 工具集 |
|
||||||
|
| `packages/acp-link/` | ACP 代理服务器(WebSocket → ACP agent 桥接) |
|
||||||
|
| `packages/cc-knowledge/` | Claude Code 知识库(非 workspace 包) |
|
||||||
|
| `packages/langfuse-dashboard/` | Langfuse 可观测性面板(非 workspace 包) |
|
||||||
|
| `packages/mcp-client/` | MCP 客户端库 |
|
||||||
|
| `packages/mcp-server/` | MCP 服务端库(非 workspace 包) |
|
||||||
|
| `packages/remote-control-server/` | 自托管 Remote Control Server(Docker 部署,含 Web UI)— Web UI 已重构为 React + Vite + Radix UI,支持 ACP agent 接入 |
|
||||||
|
| `packages/swarm/` | Swarm 解耦模块(非 workspace 包) |
|
||||||
|
| `packages/shell/` | Shell 抽象(非 workspace 包) |
|
||||||
|
| `packages/audio-capture-napi/` | 原生音频捕获(已恢复) |
|
||||||
|
| `packages/color-diff-napi/` | 颜色差异计算(完整实现,11 tests) |
|
||||||
|
| `packages/image-processor-napi/` | 图像处理(已恢复) |
|
||||||
|
| `packages/modifiers-napi/` | 键盘修饰键检测(macOS FFI 实现) |
|
||||||
|
| `packages/url-handler-napi/` | URL scheme 处理(环境变量 + CLI 参数读取) |
|
||||||
|
|
||||||
|
### Bridge / Remote Control
|
||||||
|
|
||||||
|
- **`src/bridge/`** — Remote Control / Bridge 模式。feature-gated by `BRIDGE_MODE`。包含 bridge API、会话管理、JWT 认证、消息传输、权限回调等。Entry: `bridgeMain.ts`。
|
||||||
|
- **`packages/remote-control-server/`** — 自托管 RCS,支持 Docker 部署,含 Web UI 控制面板(React 19 + Vite + Radix UI)。支持 ACP agent 通过 acp-link 接入(ACP WebSocket handler、relay handler、SSE event stream)。通过 `bun run rcs` 启动。
|
||||||
|
- CLI 快速路径: `claude remote-control` / `claude rc` / `claude bridge`。
|
||||||
|
- 详见 `docs/features/remote-control-self-hosting.md`。
|
||||||
|
|
||||||
|
### ACP Protocol (Agent Client Protocol)
|
||||||
|
|
||||||
|
- **`src/services/acp/`** — ACP agent 实现,包含 `agent.ts`(AcpAgent 类)、`bridge.ts`(Claude Code ↔ ACP 桥接)、`permissions.ts`(权限处理)、`entry.ts`(入口)。
|
||||||
|
- **`packages/acp-link/`** — ACP 代理服务器,将 WebSocket 客户端桥接到 ACP agent。提供 `acp-link` CLI 命令,支持自定义端口/HTTPS/认证/会话管理、RCS 集成(REST 注册 + WS identify 两步流程)、权限模式透传(fallback: 客户端传值 > config > `ACP_PERMISSION_MODE` 环境变量)。
|
||||||
|
- ACP 权限管道改进:`createAcpCanUseTool` 统一权限流水线,`applySessionMode` 模式同步,`bypassPermissions` 可用性检测(非 root/sandbox 环境)。
|
||||||
|
- ACP Plan 可视化已支持 `session/update plan` 类型的消息展示(PlanView 组件,含进度条/状态图标/优先级标签)。
|
||||||
|
|
||||||
|
### Daemon Mode
|
||||||
|
|
||||||
|
- **`src/daemon/`** — Daemon 模式(长驻 supervisor)。feature-gated by `DAEMON`。包含 `main.ts`(entry)和 `workerRegistry.ts`(worker 管理)。
|
||||||
|
|
||||||
|
### Context & System Prompt
|
||||||
|
|
||||||
|
- **`src/context.ts`** — Builds system/user context for the API call (git status, date, CLAUDE.md contents, memory files).
|
||||||
|
- **`src/utils/claudemd.ts`** — Discovers and loads CLAUDE.md files from project hierarchy.
|
||||||
|
|
||||||
|
### Feature Flag System
|
||||||
|
|
||||||
|
Feature flags control which functionality is enabled at runtime. 代码中统一通过 `import { feature } from 'bun:bundle'` 导入,调用 `feature('FLAG_NAME')` 返回 `boolean`。
|
||||||
|
|
||||||
|
**启用方式**: 环境变量 `FEATURE_<FLAG_NAME>=1`。例如 `FEATURE_BUDDY=1 bun run dev`。
|
||||||
|
|
||||||
|
**Build 默认 features**(19 个,见 `build.ts`):
|
||||||
|
- 基础: `BUDDY`, `TRANSCRIPT_CLASSIFIER`, `BRIDGE_MODE`, `AGENT_TRIGGERS_REMOTE`, `CHICAGO_MCP`, `VOICE_MODE`
|
||||||
|
- 统计/缓存: `SHOT_STATS`, `PROMPT_CACHE_BREAK_DETECTION`, `TOKEN_BUDGET`
|
||||||
|
- P0 本地: `AGENT_TRIGGERS`, `ULTRATHINK`, `BUILTIN_EXPLORE_PLAN_AGENTS`, `LODESTONE`
|
||||||
|
- P1 API 依赖: `EXTRACT_MEMORIES`, `VERIFICATION_AGENT`, `KAIROS_BRIEF`, `AWAY_SUMMARY`, `ULTRAPLAN`
|
||||||
|
- P2: `DAEMON`
|
||||||
|
|
||||||
|
**Dev mode 默认**: 全部启用(见 `scripts/dev.ts`)。
|
||||||
|
|
||||||
|
**类型声明**: `src/types/internal-modules.d.ts` 中声明了 `bun:bundle` 模块的 `feature` 函数签名。
|
||||||
|
|
||||||
|
**新增功能的正确做法**: 保留 `import { feature } from 'bun:bundle'` + `feature('FLAG_NAME')` 的标准模式,在运行时通过环境变量或配置控制,不要绕过 feature flag 直接 import。
|
||||||
|
|
||||||
|
### Multi-API 兼容层
|
||||||
|
|
||||||
|
所有兼容层均采用流适配器模式:将第三方 API 格式转为 Anthropic 内部格式,下游代码完全不改。通过 `/login` 命令配置。
|
||||||
|
|
||||||
|
#### OpenAI 兼容层
|
||||||
|
|
||||||
|
通过 `CLAUDE_CODE_USE_OPENAI=1` 启用,支持 Ollama/DeepSeek/vLLM 等任意 OpenAI Chat Completions 协议端点。含 DeepSeek thinking mode 支持。
|
||||||
|
|
||||||
|
- **`src/services/api/openai/`** — client、消息/工具转换、流适配、模型映射
|
||||||
|
- 关键环境变量:`CLAUDE_CODE_USE_OPENAI`、`OPENAI_API_KEY`、`OPENAI_BASE_URL`、`OPENAI_MODEL`
|
||||||
|
|
||||||
|
#### Gemini 兼容层
|
||||||
|
|
||||||
|
通过 `CLAUDE_CODE_USE_GEMINI=1` 启用。独立环境变量体系。
|
||||||
|
|
||||||
|
- **`src/services/api/gemini/`** — client、模型映射、类型定义
|
||||||
|
- 关键环境变量:`GEMINI_API_KEY`(必填)、`GEMINI_MODEL`(直接指定)、`GEMINI_DEFAULT_SONNET_MODEL`/`GEMINI_DEFAULT_OPUS_MODEL`(按能力映射)
|
||||||
|
- 模型映射优先级:`GEMINI_MODEL` > `GEMINI_DEFAULT_*_MODEL` > `ANTHROPIC_DEFAULT_*_MODEL`(已废弃) > 原样返回
|
||||||
|
|
||||||
|
#### Grok 兼容层
|
||||||
|
|
||||||
|
通过 `CLAUDE_CODE_USE_GROK=1` 启用。自定义模型映射支持 xAI Grok API。
|
||||||
|
|
||||||
|
- **`src/services/api/grok/`** — client、模型映射
|
||||||
|
|
||||||
|
详见各兼容层的 docs 文档。
|
||||||
|
|
||||||
|
### 穷鬼模式(Budget Mode)
|
||||||
|
|
||||||
|
- 通过 `/poor` 命令切换,持久化到 `settings.json`。
|
||||||
|
- 启用后跳过 `extract_memories`、`prompt_suggestion` 和 `verification_agent`,显著减少 token 消耗。
|
||||||
|
- 实现在 `src/commands/poor/poorMode.ts`。
|
||||||
|
|
||||||
|
### Stubbed/Deleted Modules
|
||||||
|
|
||||||
|
| Module | Status |
|
||||||
|
|--------|--------|
|
||||||
|
| Computer Use (`@ant/*`) | Restored — macOS + Windows + Linux(后端完整度不一) |
|
||||||
|
| `*-napi` packages | 全部已恢复/实现:`audio-capture-napi`、`image-processor-napi` 已恢复;`color-diff-napi` 完整;`modifiers-napi`(macOS FFI);`url-handler-napi`(环境变量+CLI) |
|
||||||
|
| Voice Mode | Restored — Push-to-Talk 语音输入(需 Anthropic OAuth) |
|
||||||
|
| OpenAI/Gemini/Grok 兼容层 | Restored |
|
||||||
|
| Remote Control Server | Restored — 自托管 RCS + Web UI |
|
||||||
|
| Analytics / GrowthBook / Sentry | Empty implementations |
|
||||||
|
| Magic Docs / LSP Server | Restored — Magic Docs 自动更新 + LSP 服务器管理器 |
|
||||||
|
| Plugins / Marketplace | Restored — 插件安装/卸载/启用/禁用 + Marketplace 浏览 |
|
||||||
|
| MCP OAuth | Simplified |
|
||||||
|
|
||||||
|
### Key Type Files
|
||||||
|
|
||||||
|
- **`src/types/global.d.ts`** — Declares `MACRO`, `BUILD_TARGET`, `BUILD_ENV` and internal Anthropic-only identifiers.
|
||||||
|
- **`src/types/internal-modules.d.ts`** — Type declarations for `bun:bundle`, `bun:ffi`, `@anthropic-ai/mcpb`.
|
||||||
|
- **`src/types/message.ts`** — Message type hierarchy (UserMessage, AssistantMessage, SystemMessage, etc.).
|
||||||
|
- **`src/types/permissions.ts`** — Permission mode and result types.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- **框架**: `bun:test`(内置断言 + mock)
|
||||||
|
- **单元测试**: 就近放置于 `src/**/__tests__/`,文件名 `<module>.test.ts`
|
||||||
|
- **集成测试**: `tests/integration/` — 4 个文件(cli-arguments, context-build, message-pipeline, tool-chain)
|
||||||
|
- **共享 mock/fixture**: `tests/mocks/`(api-responses, file-system, fixtures/)
|
||||||
|
- **命名**: `describe("functionName")` + `test("behavior description")`,英文
|
||||||
|
- **包测试**: `packages/` 下各包也有独立测试(如 `color-diff-napi` 11 tests)
|
||||||
|
|
||||||
|
### Mock 使用规范
|
||||||
|
|
||||||
|
**只 mock 有副作用的依赖链,不 mock 纯函数/纯数据模块。**
|
||||||
|
|
||||||
|
被迫 mock 的根源:`log.ts` / `debug.ts` → `bootstrap/state.ts`(模块级 `realpathSync` / `randomUUID` 副作用)。必须 mock 的模块:`log.ts`、`debug.ts`、`bun:bundle`、`settings/settings.js`、`config.ts`、`auth.ts`、第三方网络库。
|
||||||
|
|
||||||
|
**`log.ts` 和 `debug.ts` 使用共享 mock**(`tests/mocks/log.ts` / `tests/mocks/debug.ts`),不要在测试文件中内联 mock 定义。使用方式:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { logMock } from "../../../tests/mocks/log";
|
||||||
|
mock.module("src/utils/log.ts", logMock);
|
||||||
|
|
||||||
|
import { debugMock } from "../../../../tests/mocks/debug";
|
||||||
|
mock.module("src/utils/debug.ts", debugMock);
|
||||||
|
```
|
||||||
|
|
||||||
|
源文件导出变更时只需更新 `tests/mocks/` 下的对应文件,不需要逐个修改测试。
|
||||||
|
|
||||||
|
不要 mock:纯函数模块(`errors.ts`、`stringUtils.js`)、mock 值与真实实现相同的模块、mock 路径与实际 import 不匹配的模块。
|
||||||
|
|
||||||
|
路径规则:统一用 `.ts` 扩展名 + `src/*` 别名路径,禁止双重 mock 同一模块。
|
||||||
|
|
||||||
|
### 类型检查
|
||||||
|
|
||||||
|
项目使用 TypeScript strict 模式,**tsc 必须零错误**。每次修改后运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run typecheck
|
||||||
|
```
|
||||||
|
|
||||||
|
**类型规范**:
|
||||||
|
- 生产代码禁止 `as any`;测试文件中 mock 数据可用 `as any`
|
||||||
|
- 类型不匹配优先用 `as unknown as SpecificType` 双重断言,或补充 interface
|
||||||
|
- 未知结构对象用 `Record<string, unknown>` 替代 `any`
|
||||||
|
- 联合类型用类型守卫(type guard)收窄,不要强转
|
||||||
|
- `msg.request` 属性访问:`const req = msg.request as Record<string, unknown>`
|
||||||
|
- Ink `color` prop:用 `as keyof Theme` 而非 `as any`
|
||||||
|
|
||||||
|
## Working with This Codebase
|
||||||
|
|
||||||
|
- **tsc must pass** — `bun run typecheck` 必须零错误,任何修改都不能引入新的类型错误。
|
||||||
|
- **Feature flags** — 默认全部关闭(`feature()` 返回 `false`)。Dev/build 各有自己的默认启用列表。不要在 `cli.tsx` 中重定义 `feature` 函数。
|
||||||
|
- **React Compiler output** — Components have decompiled memoization boilerplate (`const $ = _c(N)`). This is normal.
|
||||||
|
- **`bun:bundle` import** — `import { feature } from 'bun:bundle'` 是 Bun 内置模块,由运行时/构建器解析。不要用自定义函数替代它。**`feature()` 只能直接用在 `if` 语句或三元表达式的条件位置**(Bun 编译器限制),不能赋值给变量、不能放在箭头函数体里、不能作为 `&&` 链的一部分。正确:`if (feature('X')) {}` 或 `feature('X') ? a : b`。
|
||||||
|
- **`src/` path alias** — tsconfig maps `src/*` to `./src/*`. Imports like `import { ... } from 'src/utils/...'` are valid.
|
||||||
|
- **MACRO defines** — 集中管理在 `scripts/defines.ts`。Dev mode 通过 `bun -d` 注入,build 通过 `Bun.build({ define })` 注入。修改版本号等常量只改这个文件。
|
||||||
|
- **构建产物兼容 Node.js** — `build.ts` 会自动后处理 `import.meta.require`,产物可直接用 `node dist/cli.js` 运行。
|
||||||
|
- **Biome 配置** — 大量 lint 规则被关闭(decompiled 代码不适合严格 lint)。`.tsx` 文件用 120 行宽 + 强制分号;其他文件 80 行宽 + 按需分号。
|
||||||
|
- **Ink 框架在 `packages/@ant/ink/`** — 不是 `src/ink/`(该目录不存在)。Ink 相关的组件、hooks、keybindings 都在 packages 中。
|
||||||
|
- **Provider 优先级** — `modelType` 参数 > 环境变量 > 默认 `firstParty`。新增 provider 需在 `src/utils/model/providers.ts` 注册。
|
||||||
|
|
||||||
|
## Design Context
|
||||||
|
|
||||||
|
Impeccable 设计上下文保存在 `.impeccable.md` 中。设计 Web UI(RCS 控制面板、文档站、着陆页)时必须参考该文件。
|
||||||
|
|
||||||
|
### 核心设计原则
|
||||||
|
|
||||||
|
1. **Considered over clever** — 每个设计选择都应感觉有意为之,而非追逐潮流
|
||||||
|
2. **Warmth through subtlety** — 通过橙色色调的中性色、留白布局、有温度的文案来传达温暖
|
||||||
|
3. **Density with clarity** — 技术用户需要信息密度,但不能混乱
|
||||||
|
4. **Community voice** — 设计应感觉是由使用者创造的,而非遥远的设计团队
|
||||||
|
5. **Anthropic's shadow** — 遵循 Anthropic 的设计直觉:干净的布局、充足的间距、温暖的色温
|
||||||
|
|
||||||
|
### 品牌色
|
||||||
|
|
||||||
|
- 主色:Claude Orange `#D77757`(terra cotta)
|
||||||
|
- 辅色:Claude Blue `#5769F7`
|
||||||
|
- 暗色模式使用温暖的深色表面(非冷蓝黑色)
|
||||||
|
|
||||||
|
### 目标用户
|
||||||
|
|
||||||
|
技术团队/企业,在专业工作流中使用 AI 辅助编程。友好的开源社区氛围,非企业 SaaS 风格。
|
||||||
|
|
||||||
|
### 视觉参考
|
||||||
|
|
||||||
|
Anthropic 公司的设计风格 — 干净、考究、温暖的底色。大量留白,以排版为核心。避免 AI 产品常见的设计套路(渐变文字、玻璃态、霓虹色)。
|
||||||
311
CLAUDE.md
311
CLAUDE.md
@@ -1,10 +1,25 @@
|
|||||||
# CLAUDE.md
|
# CLAUDE.md
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
This file provides guidance to Claude Code (claude.ai/code) and other AI coding agents when working with code in this repository.
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
This is a **reverse-engineered / decompiled** version of Anthropic's official Claude Code CLI tool. The goal is to restore core functionality while trimming secondary capabilities. Many modules are stubbed or feature-flagged off. The codebase has ~1341 tsc errors from decompilation (mostly `unknown`/`never`/`{}` types) — these do **not** block Bun runtime execution.
|
This is a **reverse-engineered / decompiled** version of Anthropic's official Claude Code CLI tool. The goal is to restore core functionality while trimming secondary capabilities. Many modules are stubbed or feature-flagged off. TypeScript strict mode is enforced — **`bunx tsc --noEmit` must pass with zero errors**.
|
||||||
|
|
||||||
|
## Git Commit Message Convention
|
||||||
|
|
||||||
|
使用 **Conventional Commits** 规范:
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>: <描述>
|
||||||
|
```
|
||||||
|
|
||||||
|
常见 type:`feat`、`fix`、`docs`、`chore`、`refactor`
|
||||||
|
|
||||||
|
示例:
|
||||||
|
- `feat: 添加模型 1M 上下文切换`
|
||||||
|
- `fix: 修复初次登陆的校验问题`
|
||||||
|
- `chore: remove prefetchOfficialMcpUrls call on startup`
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
@@ -21,13 +36,16 @@ bun run dev:inspect
|
|||||||
# Pipe mode
|
# Pipe mode
|
||||||
echo "say hello" | bun run src/entrypoints/cli.tsx -p
|
echo "say hello" | bun run src/entrypoints/cli.tsx -p
|
||||||
|
|
||||||
# Build (code splitting, outputs dist/cli.js + ~450 chunk files)
|
# Build (code splitting, outputs dist/cli.js + chunk files)
|
||||||
bun run build
|
bun run build
|
||||||
|
|
||||||
|
# Build with Vite (alternative build pipeline)
|
||||||
|
bun run build:vite
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
bun test # run all tests
|
bun test # run all tests
|
||||||
bun test src/utils/__tests__/hash.test.ts # run single file
|
bun test src/utils/__tests__/hash.test.ts # run single file
|
||||||
bun test --coverage # with coverage report
|
bun test --coverage # with coverage report
|
||||||
|
|
||||||
# Lint & Format (Biome)
|
# Lint & Format (Biome)
|
||||||
bun run lint # check only
|
bun run lint # check only
|
||||||
@@ -40,6 +58,13 @@ bun run health
|
|||||||
# Check unused exports
|
# Check unused exports
|
||||||
bun run check:unused
|
bun run check:unused
|
||||||
|
|
||||||
|
# Full check (typecheck + lint + test) — run after completing any task
|
||||||
|
bun run test:all
|
||||||
|
bun run typecheck
|
||||||
|
|
||||||
|
# Remote Control Server
|
||||||
|
bun run rcs
|
||||||
|
|
||||||
# Docs dev server (Mintlify)
|
# Docs dev server (Mintlify)
|
||||||
bun run docs:dev
|
bun run docs:dev
|
||||||
```
|
```
|
||||||
@@ -51,12 +76,15 @@ bun run docs:dev
|
|||||||
### Runtime & Build
|
### Runtime & Build
|
||||||
|
|
||||||
- **Runtime**: Bun (not Node.js). All imports, builds, and execution use Bun APIs.
|
- **Runtime**: Bun (not Node.js). All imports, builds, and execution use Bun APIs.
|
||||||
- **Build**: `build.ts` 执行 `Bun.build()` with `splitting: true`,入口 `src/entrypoints/cli.tsx`,输出 `dist/cli.js` + chunk files。默认启用 `AGENT_TRIGGERS_REMOTE`、`CHICAGO_MCP`、`VOICE_MODE` feature。构建后自动替换 `import.meta.require` 为 Node.js 兼容版本(产物 bun/node 都可运行)。
|
- **Build**: `build.ts` 执行 `Bun.build()` with `splitting: true`,入口 `src/entrypoints/cli.tsx`,输出 `dist/cli.js` + chunk files。Build 默认启用 19 个 feature(见下方 Feature Flag 段)。构建后自动替换 `import.meta.require` 为 Node.js 兼容版本(产物 bun/node 都可运行)。构建时会将 `vendor/audio-capture/` 和 `src/utils/vendor/ripgrep/` 复制到 `dist/vendor/` 下。
|
||||||
- **Dev mode**: `scripts/dev.ts` 通过 Bun `-d` flag 注入 `MACRO.*` defines,运行 `src/entrypoints/cli.tsx`。默认启用 `BUDDY`、`TRANSCRIPT_CLASSIFIER`、`BRIDGE_MODE`、`AGENT_TRIGGERS_REMOTE`、`CHICAGO_MCP`、`VOICE_MODE` 六个 feature。
|
- **Build (Vite)**: `vite.config.ts` + `scripts/post-build.ts`,chunk 输出到 `dist/chunks/`。post-build 同样复制 vendor 文件到 `dist/vendor/`。
|
||||||
|
- **Vendor 路径解析**: 构建后 chunk 文件位于 `dist/` 或 `dist/chunks/` 下,vendor 二进制在 `dist/vendor/`。`src/utils/ripgrep.ts` 和 `packages/audio-capture-napi/src/index.ts` 均通过 `import.meta.url` 路径中 `lastIndexOf('dist')` 定位 dist 根目录,再拼接 `vendor/` 子路径,确保不同构建产物层级下路径一致。
|
||||||
|
- **Dev mode**: `scripts/dev.ts` 通过 Bun `-d` flag 注入 `MACRO.*` defines,运行 `src/entrypoints/cli.tsx`。默认启用全部 feature。
|
||||||
- **Module system**: ESM (`"type": "module"`), TSX with `react-jsx` transform.
|
- **Module system**: ESM (`"type": "module"`), TSX with `react-jsx` transform.
|
||||||
- **Monorepo**: Bun workspaces — internal packages live in `packages/` resolved via `workspace:*`.
|
- **Monorepo**: Bun workspaces — 15 个 workspace packages + 若干辅助目录 in `packages/` resolved via `workspace:*`。
|
||||||
- **Lint/Format**: Biome (`biome.json`)。`bun run lint` / `bun run lint:fix` / `bun run format`。
|
- **Lint/Format**: Biome (`biome.json`)。`bun run lint` / `bun run lint:fix` / `bun run format`。
|
||||||
- **Defines**: 集中管理在 `scripts/defines.ts`。当前版本 `2.1.888`。
|
- **Defines**: 集中管理在 `scripts/defines.ts`。当前版本 `2.1.888`。
|
||||||
|
- **CI**: GitHub Actions — `ci.yml`(构建+测试)、`release-rcs.yml`(RCS 发布)、`update-contributors.yml`(自动更新贡献者)。
|
||||||
|
|
||||||
### Entry & Bootstrap
|
### Entry & Bootstrap
|
||||||
|
|
||||||
@@ -64,13 +92,16 @@ bun run docs:dev
|
|||||||
- `--version` / `-v` — 零模块加载
|
- `--version` / `-v` — 零模块加载
|
||||||
- `--dump-system-prompt` — feature-gated (DUMP_SYSTEM_PROMPT)
|
- `--dump-system-prompt` — feature-gated (DUMP_SYSTEM_PROMPT)
|
||||||
- `--claude-in-chrome-mcp` / `--chrome-native-host`
|
- `--claude-in-chrome-mcp` / `--chrome-native-host`
|
||||||
|
- `--computer-use-mcp` — 独立 MCP server 模式
|
||||||
- `--daemon-worker=<kind>` — feature-gated (DAEMON)
|
- `--daemon-worker=<kind>` — feature-gated (DAEMON)
|
||||||
- `remote-control` / `rc` / `bridge` — feature-gated (BRIDGE_MODE)
|
- `remote-control` / `rc` / `remote` / `sync` / `bridge` — feature-gated (BRIDGE_MODE)
|
||||||
- `daemon` — feature-gated (DAEMON)
|
- `daemon` [subcommand] — feature-gated (DAEMON)
|
||||||
- `ps` / `logs` / `attach` / `kill` / `--bg` — feature-gated (BG_SESSIONS)
|
- `ps` / `logs` / `attach` / `kill` / `--bg` — feature-gated (BG_SESSIONS)
|
||||||
|
- `new` / `list` / `reply` — Template job commands
|
||||||
|
- `environment-runner` / `self-hosted-runner` — BYOC runner
|
||||||
- `--tmux` + `--worktree` 组合
|
- `--tmux` + `--worktree` 组合
|
||||||
- 默认路径:加载 `main.tsx` 启动完整 CLI
|
- 默认路径:加载 `main.tsx` 启动完整 CLI
|
||||||
2. **`src/main.tsx`** (~4680 行) — Commander.js CLI definition。注册大量 subcommands:`mcp` (serve/add/remove/list...)、`server`、`ssh`、`open`、`auth`、`plugin`、`agents`、`auto-mode`、`doctor`、`update` 等。主 `.action()` 处理器负责权限、MCP、会话恢复、REPL/Headless 模式分发。
|
2. **`src/main.tsx`** (~6981 行) — Commander.js CLI definition。注册大量 subcommands:`mcp` (serve/add/remove/list...)、`server`、`ssh`、`open`、`auth`、`plugin`、`agents`、`auto-mode`、`doctor`、`update` 等。主 `.action()` 处理器负责权限、MCP、会话恢复、REPL/Headless 模式分发。
|
||||||
3. **`src/entrypoints/init.ts`** — One-time initialization (telemetry, config, trust dialog)。
|
3. **`src/entrypoints/init.ts`** — One-time initialization (telemetry, config, trust dialog)。
|
||||||
|
|
||||||
### Core Loop
|
### Core Loop
|
||||||
@@ -82,21 +113,28 @@ bun run docs:dev
|
|||||||
### API Layer
|
### API Layer
|
||||||
|
|
||||||
- **`src/services/api/claude.ts`** — Core API client. Builds request params (system prompt, messages, tools, betas), calls the Anthropic SDK streaming endpoint, and processes `BetaRawMessageStreamEvent` events.
|
- **`src/services/api/claude.ts`** — Core API client. Builds request params (system prompt, messages, tools, betas), calls the Anthropic SDK streaming endpoint, and processes `BetaRawMessageStreamEvent` events.
|
||||||
- Supports multiple providers: Anthropic direct, AWS Bedrock, Google Vertex, Azure.
|
- **7 providers**: `firstParty` (Anthropic direct), `bedrock` (AWS), `vertex` (Google Cloud), `foundry`, `openai`, `gemini`, `grok` (xAI)。
|
||||||
- Provider selection in `src/utils/model/providers.ts`.
|
- Provider selection in `src/utils/model/providers.ts`。优先级:modelType 参数 > 环境变量 > 默认 firstParty。
|
||||||
|
|
||||||
### Tool System
|
### Tool System
|
||||||
|
|
||||||
- **`src/Tool.ts`** — Tool interface definition (`Tool` type) and utilities (`findToolByName`, `toolMatchesName`).
|
- **`src/Tool.ts`** — Tool interface definition (`Tool` type) and utilities (`findToolByName`, `toolMatchesName`).
|
||||||
- **`src/tools.ts`** — Tool registry. Assembles the tool list; some tools are conditionally loaded via `feature()` flags or `process.env.USER_TYPE`.
|
- **`src/tools.ts`** — Tool registry. Assembles the tool list; tools are imported from `@claude-code-best/builtin-tools` package. Some tools are conditionally loaded via `feature()` flags or `process.env.USER_TYPE`.
|
||||||
- **`src/tools/<ToolName>/`** — 61 个 tool 目录(如 BashTool, FileEditTool, GrepTool, AgentTool, WebFetchTool, LSPTool, MCPTool 等)。每个 tool 包含 `name`、`description`、`inputSchema`、`call()` 及可选的 React 渲染组件。
|
- **`packages/builtin-tools/src/tools/`** — 59 个子目录(含 shared/testing 等工具目录),通过 `@claude-code-best/builtin-tools` 包导出。主要分类:
|
||||||
- **`src/tools/shared/`** — Tool 共享工具函数。
|
- **文件操作**: FileEditTool, FileReadTool, FileWriteTool, GlobTool, GrepTool
|
||||||
|
- **Shell/执行**: BashTool, PowerShellTool, REPLTool
|
||||||
|
- **Agent 系统**: AgentTool, TaskCreateTool, TaskUpdateTool, TaskListTool, TaskGetTool
|
||||||
|
- **规划**: EnterPlanModeTool, ExitPlanModeV2Tool, VerifyPlanExecutionTool
|
||||||
|
- **Web/MCP**: WebFetchTool, WebSearchTool, MCPTool, McpAuthTool
|
||||||
|
- **调度**: CronCreateTool, CronDeleteTool, CronListTool
|
||||||
|
- **其他**: LSPTool, ConfigTool, SkillTool, EnterWorktreeTool, ExitWorktreeTool 等
|
||||||
|
- **`src/tools/shared/`** / **`packages/builtin-tools/src/tools/shared/`** — Tool 共享工具函数。
|
||||||
|
|
||||||
### UI Layer (Ink)
|
### UI Layer (Ink)
|
||||||
|
|
||||||
- **`src/ink.ts`** — Ink render wrapper with ThemeProvider injection.
|
- **`src/ink.ts`** — Ink render wrapper with ThemeProvider injection.
|
||||||
- **`src/ink/`** — Custom Ink framework (forked/internal): custom reconciler, hooks (`useInput`, `useTerminalSize`, `useSearchHighlight`), virtual list rendering.
|
- **`packages/@ant/ink/`** — Custom Ink framework(forked/internal),包含 components、core、hooks、keybindings、theme、utils。注意:不是 `src/ink/`。
|
||||||
- **`src/components/`** — 大量 React 组件(170+ 项),渲染于终端 Ink 环境中。关键组件:
|
- **`src/components/`** — 149 个组件目录/文件,渲染于终端 Ink 环境中。关键组件:
|
||||||
- `App.tsx` — Root provider (AppState, Stats, FpsMetrics)
|
- `App.tsx` — Root provider (AppState, Stats, FpsMetrics)
|
||||||
- `Messages.tsx` / `MessageRow.tsx` — Conversation message rendering
|
- `Messages.tsx` / `MessageRow.tsx` — Conversation message rendering
|
||||||
- `PromptInput/` — User input handling
|
- `PromptInput/` — User input handling
|
||||||
@@ -112,10 +150,45 @@ bun run docs:dev
|
|||||||
- **`src/state/selectors.ts`** — State selectors.
|
- **`src/state/selectors.ts`** — State selectors.
|
||||||
- **`src/bootstrap/state.ts`** — Module-level singletons for session-global state (session ID, CWD, project root, token counts, model overrides, client type, permission mode).
|
- **`src/bootstrap/state.ts`** — Module-level singletons for session-global state (session ID, CWD, project root, token counts, model overrides, client type, permission mode).
|
||||||
|
|
||||||
|
### Workspace Packages
|
||||||
|
|
||||||
|
| Package | 说明 |
|
||||||
|
|---------|------|
|
||||||
|
| `packages/@ant/ink/` | Forked Ink 框架(components、hooks、keybindings、theme) |
|
||||||
|
| `packages/@ant/computer-use-mcp/` | Computer Use MCP server(截图/键鼠/剪贴板/应用管理) |
|
||||||
|
| `packages/@ant/computer-use-input/` | 键鼠模拟(dispatcher + darwin/win32/linux backend) |
|
||||||
|
| `packages/@ant/computer-use-swift/` | 截图 + 应用管理(dispatcher + per-platform backend) |
|
||||||
|
| `packages/@ant/claude-for-chrome-mcp/` | Chrome 浏览器控制(通过 `--chrome` 启用) |
|
||||||
|
| `packages/@ant/model-provider/` | Model provider 抽象层 |
|
||||||
|
| `packages/builtin-tools/` | 内置工具集(60 个 tool 实现,通过 `@claude-code-best/builtin-tools` 导出) |
|
||||||
|
| `packages/agent-tools/` | Agent 工具集 |
|
||||||
|
| `packages/acp-link/` | ACP 代理服务器(WebSocket → ACP agent 桥接) |
|
||||||
|
| `packages/cc-knowledge/` | Claude Code 知识库(非 workspace 包) |
|
||||||
|
| `packages/langfuse-dashboard/` | Langfuse 可观测性面板(非 workspace 包) |
|
||||||
|
| `packages/mcp-client/` | MCP 客户端库 |
|
||||||
|
| `packages/mcp-server/` | MCP 服务端库(非 workspace 包) |
|
||||||
|
| `packages/remote-control-server/` | 自托管 Remote Control Server(Docker 部署,含 Web UI)— Web UI 已重构为 React + Vite + Radix UI,支持 ACP agent 接入 |
|
||||||
|
| `packages/swarm/` | Swarm 解耦模块(非 workspace 包) |
|
||||||
|
| `packages/shell/` | Shell 抽象(非 workspace 包) |
|
||||||
|
| `packages/audio-capture-napi/` | 原生音频捕获(已恢复) |
|
||||||
|
| `packages/color-diff-napi/` | 颜色差异计算(完整实现,11 tests) |
|
||||||
|
| `packages/image-processor-napi/` | 图像处理(已恢复) |
|
||||||
|
| `packages/modifiers-napi/` | 键盘修饰键检测(macOS FFI 实现) |
|
||||||
|
| `packages/url-handler-napi/` | URL scheme 处理(环境变量 + CLI 参数读取) |
|
||||||
|
|
||||||
### Bridge / Remote Control
|
### Bridge / Remote Control
|
||||||
|
|
||||||
- **`src/bridge/`** (~35 files) — Remote Control / Bridge 模式。feature-gated by `BRIDGE_MODE`。包含 bridge API、会话管理、JWT 认证、消息传输、权限回调等。Entry: `bridgeMain.ts`。
|
- **`src/bridge/`** — Remote Control / Bridge 模式。feature-gated by `BRIDGE_MODE`。包含 bridge API、会话管理、JWT 认证、消息传输、权限回调等。Entry: `bridgeMain.ts`。
|
||||||
|
- **`packages/remote-control-server/`** — 自托管 RCS,支持 Docker 部署,含 Web UI 控制面板(React 19 + Vite + Radix UI)。支持 ACP agent 通过 acp-link 接入(ACP WebSocket handler、relay handler、SSE event stream)。通过 `bun run rcs` 启动。
|
||||||
- CLI 快速路径: `claude remote-control` / `claude rc` / `claude bridge`。
|
- CLI 快速路径: `claude remote-control` / `claude rc` / `claude bridge`。
|
||||||
|
- 详见 `docs/features/remote-control-self-hosting.md`。
|
||||||
|
|
||||||
|
### ACP Protocol (Agent Client Protocol)
|
||||||
|
|
||||||
|
- **`src/services/acp/`** — ACP agent 实现,包含 `agent.ts`(AcpAgent 类)、`bridge.ts`(Claude Code ↔ ACP 桥接)、`permissions.ts`(权限处理)、`entry.ts`(入口)。
|
||||||
|
- **`packages/acp-link/`** — ACP 代理服务器,将 WebSocket 客户端桥接到 ACP agent。提供 `acp-link` CLI 命令,支持自定义端口/HTTPS/认证/会话管理、RCS 集成(REST 注册 + WS identify 两步流程)、权限模式透传(fallback: 客户端传值 > config > `ACP_PERMISSION_MODE` 环境变量)。
|
||||||
|
- ACP 权限管道改进:`createAcpCanUseTool` 统一权限流水线,`applySessionMode` 模式同步,`bypassPermissions` 可用性检测(非 root/sandbox 环境)。
|
||||||
|
- ACP Plan 可视化已支持 `session/update plan` 类型的消息展示(PlanView 组件,含进度条/状态图标/优先级标签)。
|
||||||
|
|
||||||
### Daemon Mode
|
### Daemon Mode
|
||||||
|
|
||||||
@@ -128,91 +201,70 @@ bun run docs:dev
|
|||||||
|
|
||||||
### Feature Flag System
|
### Feature Flag System
|
||||||
|
|
||||||
Feature flags control which functionality is enabled at runtime:
|
Feature flags control which functionality is enabled at runtime. 代码中统一通过 `import { feature } from 'bun:bundle'` 导入,调用 `feature('FLAG_NAME')` 返回 `boolean`。
|
||||||
|
|
||||||
- **在代码中使用**: 统一通过 `import { feature } from 'bun:bundle'` 导入,调用 `feature('FLAG_NAME')` 返回 `boolean`。**不要**在 `cli.tsx` 或其他文件里自己定义 `feature` 函数或覆盖这个 import。
|
**启用方式**: 环境变量 `FEATURE_<FLAG_NAME>=1`。例如 `FEATURE_BUDDY=1 bun run dev`。
|
||||||
- **启用方式**: 通过环境变量 `FEATURE_<FLAG_NAME>=1`。例如 `FEATURE_BUDDY=1 bun run dev` 启用 BUDDY 功能。
|
|
||||||
- **Dev 默认 features**: `BUDDY`、`TRANSCRIPT_CLASSIFIER`、`BRIDGE_MODE`、`AGENT_TRIGGERS_REMOTE`、`CHICAGO_MCP`、`VOICE_MODE`(见 `scripts/dev.ts`)。
|
**Build 默认 features**(19 个,见 `build.ts`):
|
||||||
- **Build 默认 features**: `AGENT_TRIGGERS_REMOTE`、`CHICAGO_MCP`、`VOICE_MODE`(见 `build.ts`)。
|
- 基础: `BUDDY`, `TRANSCRIPT_CLASSIFIER`, `BRIDGE_MODE`, `AGENT_TRIGGERS_REMOTE`, `CHICAGO_MCP`, `VOICE_MODE`
|
||||||
- **常见 flag**: `BUDDY`, `DAEMON`, `BRIDGE_MODE`, `BG_SESSIONS`, `PROACTIVE`, `KAIROS`, `VOICE_MODE`, `FORK_SUBAGENT`, `SSH_REMOTE`, `DIRECT_CONNECT`, `TEMPLATES`, `CHICAGO_MCP`, `BYOC_ENVIRONMENT_RUNNER`, `SELF_HOSTED_RUNNER`, `COORDINATOR_MODE`, `UDS_INBOX`, `LODESTONE`, `ABLATION_BASELINE` 等。
|
- 统计/缓存: `SHOT_STATS`, `PROMPT_CACHE_BREAK_DETECTION`, `TOKEN_BUDGET`
|
||||||
- **类型声明**: `src/types/internal-modules.d.ts` 中声明了 `bun:bundle` 模块的 `feature` 函数签名。
|
- P0 本地: `AGENT_TRIGGERS`, `ULTRATHINK`, `BUILTIN_EXPLORE_PLAN_AGENTS`, `LODESTONE`
|
||||||
|
- P1 API 依赖: `EXTRACT_MEMORIES`, `VERIFICATION_AGENT`, `KAIROS_BRIEF`, `AWAY_SUMMARY`, `ULTRAPLAN`
|
||||||
|
- P2: `DAEMON`
|
||||||
|
|
||||||
|
**Dev mode 默认**: 全部启用(见 `scripts/dev.ts`)。
|
||||||
|
|
||||||
|
**类型声明**: `src/types/internal-modules.d.ts` 中声明了 `bun:bundle` 模块的 `feature` 函数签名。
|
||||||
|
|
||||||
**新增功能的正确做法**: 保留 `import { feature } from 'bun:bundle'` + `feature('FLAG_NAME')` 的标准模式,在运行时通过环境变量或配置控制,不要绕过 feature flag 直接 import。
|
**新增功能的正确做法**: 保留 `import { feature } from 'bun:bundle'` + `feature('FLAG_NAME')` 的标准模式,在运行时通过环境变量或配置控制,不要绕过 feature flag 直接 import。
|
||||||
|
|
||||||
|
### Multi-API 兼容层
|
||||||
|
|
||||||
|
所有兼容层均采用流适配器模式:将第三方 API 格式转为 Anthropic 内部格式,下游代码完全不改。通过 `/login` 命令配置。
|
||||||
|
|
||||||
|
#### OpenAI 兼容层
|
||||||
|
|
||||||
|
通过 `CLAUDE_CODE_USE_OPENAI=1` 启用,支持 Ollama/DeepSeek/vLLM 等任意 OpenAI Chat Completions 协议端点。含 DeepSeek thinking mode 支持。
|
||||||
|
|
||||||
|
- **`src/services/api/openai/`** — client、消息/工具转换、流适配、模型映射
|
||||||
|
- 关键环境变量:`CLAUDE_CODE_USE_OPENAI`、`OPENAI_API_KEY`、`OPENAI_BASE_URL`、`OPENAI_MODEL`
|
||||||
|
|
||||||
|
#### Gemini 兼容层
|
||||||
|
|
||||||
|
通过 `CLAUDE_CODE_USE_GEMINI=1` 启用。独立环境变量体系。
|
||||||
|
|
||||||
|
- **`src/services/api/gemini/`** — client、模型映射、类型定义
|
||||||
|
- 关键环境变量:`GEMINI_API_KEY`(必填)、`GEMINI_MODEL`(直接指定)、`GEMINI_DEFAULT_SONNET_MODEL`/`GEMINI_DEFAULT_OPUS_MODEL`(按能力映射)
|
||||||
|
- 模型映射优先级:`GEMINI_MODEL` > `GEMINI_DEFAULT_*_MODEL` > `ANTHROPIC_DEFAULT_*_MODEL`(已废弃) > 原样返回
|
||||||
|
|
||||||
|
#### Grok 兼容层
|
||||||
|
|
||||||
|
通过 `CLAUDE_CODE_USE_GROK=1` 启用。自定义模型映射支持 xAI Grok API。
|
||||||
|
|
||||||
|
- **`src/services/api/grok/`** — client、模型映射
|
||||||
|
|
||||||
|
详见各兼容层的 docs 文档。
|
||||||
|
|
||||||
|
### 穷鬼模式(Budget Mode)
|
||||||
|
|
||||||
|
- 通过 `/poor` 命令切换,持久化到 `settings.json`。
|
||||||
|
- 启用后跳过 `extract_memories`、`prompt_suggestion` 和 `verification_agent`,显著减少 token 消耗。
|
||||||
|
- 实现在 `src/commands/poor/poorMode.ts`。
|
||||||
|
|
||||||
### Stubbed/Deleted Modules
|
### Stubbed/Deleted Modules
|
||||||
|
|
||||||
| Module | Status |
|
| Module | Status |
|
||||||
|--------|--------|
|
|--------|--------|
|
||||||
| Computer Use (`@ant/*`) | Restored — `computer-use-swift`, `computer-use-input`, `computer-use-mcp`, `claude-for-chrome-mcp` 均有完整实现,macOS + Windows 可用,Linux 后端待完成 |
|
| Computer Use (`@ant/*`) | Restored — macOS + Windows + Linux(后端完整度不一) |
|
||||||
| `*-napi` packages | `audio-capture-napi`、`image-processor-napi` 已恢复实现;`color-diff-napi` 完整实现;`url-handler-napi`、`modifiers-napi` 仍为 stub |
|
| `*-napi` packages | 全部已恢复/实现:`audio-capture-napi`、`image-processor-napi` 已恢复;`color-diff-napi` 完整;`modifiers-napi`(macOS FFI);`url-handler-napi`(环境变量+CLI) |
|
||||||
| Voice Mode | Restored — `src/voice/`、`src/hooks/useVoiceIntegration.tsx`、`src/services/voiceStreamSTT.ts` 等,Push-to-Talk 语音输入(需 Anthropic OAuth) |
|
| Voice Mode | Restored — Push-to-Talk 语音输入(需 Anthropic OAuth) |
|
||||||
| OpenAI 兼容层 | Restored — `src/services/api/openai/`,支持 Ollama/DeepSeek/vLLM 等任意 OpenAI 协议端点,通过 `CLAUDE_CODE_USE_OPENAI=1` 启用 |
|
| OpenAI/Gemini/Grok 兼容层 | Restored |
|
||||||
|
| Remote Control Server | Restored — 自托管 RCS + Web UI |
|
||||||
| Analytics / GrowthBook / Sentry | Empty implementations |
|
| Analytics / GrowthBook / Sentry | Empty implementations |
|
||||||
| Magic Docs / LSP Server | Removed |
|
| Magic Docs / LSP Server | Restored — Magic Docs 自动更新 + LSP 服务器管理器 |
|
||||||
| Plugins / Marketplace | Removed |
|
| Plugins / Marketplace | Restored — 插件安装/卸载/启用/禁用 + Marketplace 浏览 |
|
||||||
| MCP OAuth | Simplified |
|
| MCP OAuth | Simplified |
|
||||||
|
|
||||||
### Computer Use
|
|
||||||
|
|
||||||
Feature flag `CHICAGO_MCP`,dev/build 默认启用。实现跨平台屏幕操控(macOS + Windows 可用,Linux 待完成)。
|
|
||||||
|
|
||||||
- **`packages/@ant/computer-use-mcp/`** — MCP server,注册截图/键鼠/剪贴板/应用管理工具
|
|
||||||
- **`packages/@ant/computer-use-input/`** — 键鼠模拟,dispatcher + per-platform backend(`backends/darwin.ts`、`win32.ts`、`linux.ts`)
|
|
||||||
- **`packages/@ant/computer-use-swift/`** — 截图 + 应用管理,同样 dispatcher + per-platform backend
|
|
||||||
- **`packages/@ant/claude-for-chrome-mcp/`** — Chrome 浏览器控制(独立于 Computer Use,通过 `--chrome` CLI 参数启用)
|
|
||||||
|
|
||||||
详见 `docs/features/computer-use.md`。
|
|
||||||
|
|
||||||
### Voice Mode
|
|
||||||
|
|
||||||
Feature flag `VOICE_MODE`,dev/build 默认启用。Push-to-Talk 语音输入,音频通过 WebSocket 流式传输到 Anthropic STT(Nova 3)。需要 Anthropic OAuth(非 API key)。
|
|
||||||
|
|
||||||
- **`src/voice/voiceModeEnabled.ts`** — 三层门控(feature flag + GrowthBook + OAuth auth)
|
|
||||||
- **`src/hooks/useVoice.ts`** — React hook 管理录音状态和 WebSocket 连接
|
|
||||||
- **`src/services/voiceStreamSTT.ts`** — STT WebSocket 流式传输
|
|
||||||
|
|
||||||
详见 `docs/features/voice-mode.md`。
|
|
||||||
|
|
||||||
### OpenAI 兼容层
|
|
||||||
|
|
||||||
通过 `CLAUDE_CODE_USE_OPENAI=1` 环境变量启用,支持任意 OpenAI Chat Completions 协议端点(Ollama、DeepSeek、vLLM 等)。流适配器模式:在 `queryModel()` 中将 Anthropic 格式请求转为 OpenAI 格式,再将 SSE 流转换回 `BetaRawMessageStreamEvent`,下游代码完全不改。
|
|
||||||
|
|
||||||
- **`src/services/api/openai/`** — client、消息/工具转换、流适配、模型映射
|
|
||||||
- **`src/utils/model/providers.ts`** — 添加 `'openai'` provider 类型(最高优先级)
|
|
||||||
|
|
||||||
关键环境变量:`CLAUDE_CODE_USE_OPENAI`、`OPENAI_API_KEY`、`OPENAI_BASE_URL`、`OPENAI_MODEL`、`OPENAI_DEFAULT_OPUS_MODEL`、`OPENAI_DEFAULT_SONNET_MODEL`、`OPENAI_DEFAULT_HAIKU_MODEL`。详见 `docs/plans/openai-compatibility.md`。
|
|
||||||
|
|
||||||
### Gemini 兼容层
|
|
||||||
|
|
||||||
通过 `CLAUDE_CODE_USE_GEMINI=1` 环境变量或 `modelType: "gemini"` 设置启用,支持 Google Gemini API。独立的环境变量体系,不与 OpenAI 或 Anthropic 配置混杂。
|
|
||||||
|
|
||||||
- **`src/services/api/gemini/`** — client、模型映射、类型定义
|
|
||||||
- **`src/utils/model/providers.ts`** — 添加 `'gemini'` provider 类型
|
|
||||||
- **`src/utils/managedEnvConstants.ts`** — Gemini 专用的 managed env vars
|
|
||||||
|
|
||||||
关键环境变量:
|
|
||||||
- `CLAUDE_CODE_USE_GEMINI` - 启用 Gemini provider
|
|
||||||
- `GEMINI_API_KEY` - API 密钥(必填)
|
|
||||||
- `GEMINI_BASE_URL` - API 端点(可选,默认 `https://generativelanguage.googleapis.com/v1beta`)
|
|
||||||
- `GEMINI_MODEL` - 直接指定模型(最高优先级)
|
|
||||||
- `GEMINI_DEFAULT_HAIKU_MODEL` / `GEMINI_DEFAULT_SONNET_MODEL` / `GEMINI_DEFAULT_OPUS_MODEL` - 按能力级别映射
|
|
||||||
- `GEMINI_DEFAULT_HAIKU_MODEL_NAME` / `DESCRIPTION` / `SUPPORTED_CAPABILITIES` - 显示名称和描述
|
|
||||||
- `GEMINI_SMALL_FAST_MODEL` - 快速任务使用的模型(可选)
|
|
||||||
|
|
||||||
模型映射优先级(`src/services/api/gemini/modelMapping.ts`):
|
|
||||||
1. `GEMINI_MODEL` - 直接覆盖
|
|
||||||
2. `GEMINI_DEFAULT_*_MODEL` - 独立配置(推荐)
|
|
||||||
3. `ANTHROPIC_DEFAULT_*_MODEL` - 向后兼容 fallback(已废弃)
|
|
||||||
4. 原样返回 Anthropic 模型名
|
|
||||||
|
|
||||||
使用示例:
|
|
||||||
```bash
|
|
||||||
export CLAUDE_CODE_USE_GEMINI=1
|
|
||||||
export GEMINI_API_KEY="your-api-key"
|
|
||||||
export GEMINI_DEFAULT_SONNET_MODEL="gemini-2.5-flash"
|
|
||||||
export GEMINI_DEFAULT_OPUS_MODEL="gemini-2.5-pro"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Key Type Files
|
### Key Type Files
|
||||||
|
|
||||||
- **`src/types/global.d.ts`** — Declares `MACRO`, `BUILD_TARGET`, `BUILD_ENV` and internal Anthropic-only identifiers.
|
- **`src/types/global.d.ts`** — Declares `MACRO`, `BUILD_TARGET`, `BUILD_ENV` and internal Anthropic-only identifiers.
|
||||||
@@ -227,16 +279,81 @@ export GEMINI_DEFAULT_OPUS_MODEL="gemini-2.5-pro"
|
|||||||
- **集成测试**: `tests/integration/` — 4 个文件(cli-arguments, context-build, message-pipeline, tool-chain)
|
- **集成测试**: `tests/integration/` — 4 个文件(cli-arguments, context-build, message-pipeline, tool-chain)
|
||||||
- **共享 mock/fixture**: `tests/mocks/`(api-responses, file-system, fixtures/)
|
- **共享 mock/fixture**: `tests/mocks/`(api-responses, file-system, fixtures/)
|
||||||
- **命名**: `describe("functionName")` + `test("behavior description")`,英文
|
- **命名**: `describe("functionName")` + `test("behavior description")`,英文
|
||||||
- **Mock 模式**: 对重依赖模块使用 `mock.module()` + `await import()` 解锁(必须内联在测试文件中,不能从共享 helper 导入)
|
- **包测试**: `packages/` 下各包也有独立测试(如 `color-diff-napi` 11 tests)
|
||||||
- **当前状态**: ~1623 tests / 114 files (110 unit + 4 integration) / 0 fail(详见 `docs/testing-spec.md`)
|
|
||||||
|
### Mock 使用规范
|
||||||
|
|
||||||
|
**只 mock 有副作用的依赖链,不 mock 纯函数/纯数据模块。**
|
||||||
|
|
||||||
|
被迫 mock 的根源:`log.ts` / `debug.ts` → `bootstrap/state.ts`(模块级 `realpathSync` / `randomUUID` 副作用)。必须 mock 的模块:`log.ts`、`debug.ts`、`bun:bundle`、`settings/settings.js`、`config.ts`、`auth.ts`、第三方网络库。
|
||||||
|
|
||||||
|
**`log.ts` 和 `debug.ts` 使用共享 mock**(`tests/mocks/log.ts` / `tests/mocks/debug.ts`),不要在测试文件中内联 mock 定义。使用方式:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { logMock } from "../../../tests/mocks/log";
|
||||||
|
mock.module("src/utils/log.ts", logMock);
|
||||||
|
|
||||||
|
import { debugMock } from "../../../../tests/mocks/debug";
|
||||||
|
mock.module("src/utils/debug.ts", debugMock);
|
||||||
|
```
|
||||||
|
|
||||||
|
源文件导出变更时只需更新 `tests/mocks/` 下的对应文件,不需要逐个修改测试。
|
||||||
|
|
||||||
|
不要 mock:纯函数模块(`errors.ts`、`stringUtils.js`)、mock 值与真实实现相同的模块、mock 路径与实际 import 不匹配的模块。
|
||||||
|
|
||||||
|
路径规则:统一用 `.ts` 扩展名 + `src/*` 别名路径,禁止双重 mock 同一模块。
|
||||||
|
|
||||||
|
### 类型检查
|
||||||
|
|
||||||
|
项目使用 TypeScript strict 模式,**tsc 必须零错误**。每次修改后运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run typecheck
|
||||||
|
```
|
||||||
|
|
||||||
|
**类型规范**:
|
||||||
|
- 生产代码禁止 `as any`;测试文件中 mock 数据可用 `as any`
|
||||||
|
- 类型不匹配优先用 `as unknown as SpecificType` 双重断言,或补充 interface
|
||||||
|
- 未知结构对象用 `Record<string, unknown>` 替代 `any`
|
||||||
|
- 联合类型用类型守卫(type guard)收窄,不要强转
|
||||||
|
- `msg.request` 属性访问:`const req = msg.request as Record<string, unknown>`
|
||||||
|
- Ink `color` prop:用 `as keyof Theme` 而非 `as any`
|
||||||
|
|
||||||
## Working with This Codebase
|
## Working with This Codebase
|
||||||
|
|
||||||
- **Don't try to fix all tsc errors** — they're from decompilation and don't affect runtime.
|
- **tsc must pass** — `bun run typecheck` 必须零错误,任何修改都不能引入新的类型错误。
|
||||||
- **Feature flags** — 默认全部关闭(`feature()` 返回 `false`)。Dev/build 各有自己的默认启用列表。不要在 `cli.tsx` 中重定义 `feature` 函数。
|
- **Feature flags** — 默认全部关闭(`feature()` 返回 `false`)。Dev/build 各有自己的默认启用列表。不要在 `cli.tsx` 中重定义 `feature` 函数。
|
||||||
- **React Compiler output** — Components have decompiled memoization boilerplate (`const $ = _c(N)`). This is normal.
|
- **React Compiler output** — Components have decompiled memoization boilerplate (`const $ = _c(N)`). This is normal.
|
||||||
- **`bun:bundle` import** — `import { feature } from 'bun:bundle'` 是 Bun 内置模块,由运行时/构建器解析。不要用自定义函数替代它。
|
- **`bun:bundle` import** — `import { feature } from 'bun:bundle'` 是 Bun 内置模块,由运行时/构建器解析。不要用自定义函数替代它。**`feature()` 只能直接用在 `if` 语句或三元表达式的条件位置**(Bun 编译器限制),不能赋值给变量、不能放在箭头函数体里、不能作为 `&&` 链的一部分。正确:`if (feature('X')) {}` 或 `feature('X') ? a : b`。
|
||||||
- **`src/` path alias** — tsconfig maps `src/*` to `./src/*`. Imports like `import { ... } from 'src/utils/...'` are valid.
|
- **`src/` path alias** — tsconfig maps `src/*` to `./src/*`. Imports like `import { ... } from 'src/utils/...'` are valid.
|
||||||
- **MACRO defines** — 集中管理在 `scripts/defines.ts`。Dev mode 通过 `bun -d` 注入,build 通过 `Bun.build({ define })` 注入。修改版本号等常量只改这个文件。
|
- **MACRO defines** — 集中管理在 `scripts/defines.ts`。Dev mode 通过 `bun -d` 注入,build 通过 `Bun.build({ define })` 注入。修改版本号等常量只改这个文件。
|
||||||
- **构建产物兼容 Node.js** — `build.ts` 会自动后处理 `import.meta.require`,产物可直接用 `node dist/cli.js` 运行。
|
- **构建产物兼容 Node.js** — `build.ts` 会自动后处理 `import.meta.require`,产物可直接用 `node dist/cli.js` 运行。
|
||||||
- **Biome 配置** — 大量 lint 规则被关闭(decompiled 代码不适合严格 lint)。`.tsx` 文件用 120 行宽 + 强制分号;其他文件 80 行宽 + 按需分号。
|
- **Biome 配置** — 大量 lint 规则被关闭(decompiled 代码不适合严格 lint)。`.tsx` 文件用 120 行宽 + 强制分号;其他文件 80 行宽 + 按需分号。
|
||||||
|
- **Ink 框架在 `packages/@ant/ink/`** — 不是 `src/ink/`(该目录不存在)。Ink 相关的组件、hooks、keybindings 都在 packages 中。
|
||||||
|
- **Provider 优先级** — `modelType` 参数 > 环境变量 > 默认 `firstParty`。新增 provider 需在 `src/utils/model/providers.ts` 注册。
|
||||||
|
|
||||||
|
## Design Context
|
||||||
|
|
||||||
|
Impeccable 设计上下文保存在 `.impeccable.md` 中。设计 Web UI(RCS 控制面板、文档站、着陆页)时必须参考该文件。
|
||||||
|
|
||||||
|
### 核心设计原则
|
||||||
|
|
||||||
|
1. **Considered over clever** — 每个设计选择都应感觉有意为之,而非追逐潮流
|
||||||
|
2. **Warmth through subtlety** — 通过橙色色调的中性色、留白布局、有温度的文案来传达温暖
|
||||||
|
3. **Density with clarity** — 技术用户需要信息密度,但不能混乱
|
||||||
|
4. **Community voice** — 设计应感觉是由使用者创造的,而非遥远的设计团队
|
||||||
|
5. **Anthropic's shadow** — 遵循 Anthropic 的设计直觉:干净的布局、充足的间距、温暖的色温
|
||||||
|
|
||||||
|
### 品牌色
|
||||||
|
|
||||||
|
- 主色:Claude Orange `#D77757`(terra cotta)
|
||||||
|
- 辅色:Claude Blue `#5769F7`
|
||||||
|
- 暗色模式使用温暖的深色表面(非冷蓝黑色)
|
||||||
|
|
||||||
|
### 目标用户
|
||||||
|
|
||||||
|
技术团队/企业,在专业工作流中使用 AI 辅助编程。友好的开源社区氛围,非企业 SaaS 风格。
|
||||||
|
|
||||||
|
### 视觉参考
|
||||||
|
|
||||||
|
Anthropic 公司的设计风格 — 干净、考究、温暖的底色。大量留白,以排版为核心。避免 AI 产品常见的设计套路(渐变文字、玻璃态、霓虹色)。
|
||||||
|
|||||||
189
DEV-LOG.md
189
DEV-LOG.md
@@ -1,5 +1,194 @@
|
|||||||
# DEV-LOG
|
# DEV-LOG
|
||||||
|
|
||||||
|
## /poor 省流模式 (2026-04-11)
|
||||||
|
|
||||||
|
新增 `/poor` 命令,toggle 关闭 `extract_memories` 和 `prompt_suggestion`,省 token。
|
||||||
|
|
||||||
|
- 新增 `POOR` feature flag(build.ts + dev.ts)
|
||||||
|
- `src/commands/poor/` — 命令定义 + toggle 实现 + 状态管理
|
||||||
|
- `src/query/stopHooks.ts` — POOR 模式激活时跳过 extract_memories 和 prompt_suggestion
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pipe IPC + LAN Pipes + Monitor Tool + 工具恢复 (2026-04-08 ~ 2026-04-11)
|
||||||
|
|
||||||
|
**分支**: `feat/pr-package-adapt`
|
||||||
|
|
||||||
|
### 背景
|
||||||
|
|
||||||
|
从 decompiled 代码恢复大量 stub 为完整实现,同时新增 LAN 跨机器通讯能力。本次 PR 覆盖:Pipe IPC 系统、LAN Pipes、Monitor Tool、20+ 工具/组件<E7BB84><E4BBB6><EFBFBD>复、REPL hook 架构重构。
|
||||||
|
|
||||||
|
### 实现
|
||||||
|
|
||||||
|
#### 1. PipeServer TCP 双模式(`src/utils/pipeTransport.ts`)
|
||||||
|
|
||||||
|
从原始的纯 UDS 服务器扩展为 UDS + TCP 双模式:
|
||||||
|
|
||||||
|
- 提取 `setupSocket()` 共享方法,UDS 和 TCP 的 socket 处理逻辑完全一致
|
||||||
|
- `start(options?: PipeServerOptions)` 新增可选参数 `{ enableTcp, tcpPort }`
|
||||||
|
- 内部维护两个 `net.Server`(UDS + TCP),共享同一组 `clients: Set<Socket>` 和 `handlers`
|
||||||
|
- TCP server 绑定 `0.0.0.0` + 动态端口(port=0 由 OS 分配)
|
||||||
|
- `tcpAddress` getter 暴露 TCP 端口信息
|
||||||
|
- `close()` 同时关闭两个 server
|
||||||
|
- 新增类型:`PipeTransportMode`、`TcpEndpoint`、`PipeServerOptions`
|
||||||
|
|
||||||
|
PipeClient 对应扩展:
|
||||||
|
- 构造函数新增可选 `TcpEndpoint` 参数
|
||||||
|
- `connect()` 根据是否有 TCP endpoint 分派到 `connectTcp()` 或 `connectUds()`
|
||||||
|
- TCP 连接不需要文件存在轮询,直接建立连接
|
||||||
|
|
||||||
|
#### 2. LAN Beacon — UDP Multicast 发现(`src/utils/lanBeacon.ts`,新文件)
|
||||||
|
|
||||||
|
零配置局域网 peer 发现:
|
||||||
|
|
||||||
|
- **协议**:UDP multicast 组 `224.0.71.67`("CC" ASCII),端口 `7101`,TTL=1
|
||||||
|
- **Announce 包**:JSON `{ proto, pipeName, machineId, hostname, ip, tcpPort, role, ts }`
|
||||||
|
- **广播间隔**:3 秒,首次在 socket bind 完成后立即发送
|
||||||
|
- **Peer 超时**:15 秒无 announce 视为 lost
|
||||||
|
- **事件**:`peer-discovered`、`peer-lost`
|
||||||
|
- **存储**:module-level singleton `getLanBeacon()`/`setLanBeacon()`,不挂在 Zustand state 上
|
||||||
|
|
||||||
|
关键修复:
|
||||||
|
- `addMembership(group, localIp)` + `setMulticastInterface(localIp)` 指定 LAN 网卡,解决 Windows 上 WSL/Docker 虚拟网卡劫持 multicast 的问题
|
||||||
|
- announce/cleanup 定时器移入 `bind()` 回调内,修复 socket 未就绪时发送的竞态
|
||||||
|
|
||||||
|
#### 3. Registry 扩展(`src/utils/pipeRegistry.ts`)
|
||||||
|
|
||||||
|
- `PipeRegistryEntry` 新增 `tcpPort?` 和 `lanVisible?` 字段
|
||||||
|
- `mergeWithLanPeers(registry, lanPeers)` 合并本地 registry 和 LAN beacon peers,本地优先
|
||||||
|
|
||||||
|
#### 4. Peer Address 扩展(`src/utils/peerAddress.ts`)
|
||||||
|
|
||||||
|
- `parseAddress()` 新增 `tcp` scheme:`tcp:192.168.1.20:7100`
|
||||||
|
- 新增 `parseTcpTarget()` 解析 `host:port` 字符串
|
||||||
|
|
||||||
|
#### 5. REPL 集成(`src/screens/REPL.tsx`)
|
||||||
|
|
||||||
|
三个阶段的改动:
|
||||||
|
|
||||||
|
**Bootstrap**:`createPipeServer()` 时根据 `feature('LAN_PIPES')` 传入 TCP 选项 → 启动 `LanBeacon` → 注册 entry 携带 tcpPort
|
||||||
|
|
||||||
|
**Heartbeat**(每 5 秒):
|
||||||
|
- `refreshDiscoveredPipes()` 同时包含本地 subs 和 LAN beacon peers,防止 LAN peer 状态被覆盖
|
||||||
|
- auto-attach 循环统一遍历本地 subs + LAN peers,LAN peers 通过 TCP endpoint 连接
|
||||||
|
- cleanup 检查 LAN beacon peers 列表,避免误删存活的 LAN 连接
|
||||||
|
- attach 请求携带 `machineId`,接收方区分 LAN peer(不要求 sub 角色)
|
||||||
|
|
||||||
|
**Cleanup**:通过 `getLanBeacon()` 获取并 `stop()`,`setLanBeacon(null)` 清除
|
||||||
|
|
||||||
|
#### 6. 命令更新
|
||||||
|
|
||||||
|
- `/pipes`(`src/commands/pipes/pipes.ts`):显示 `[LAN]` 标记的远端实例
|
||||||
|
- `/attach`(`src/commands/attach/attach.ts`):自动查找 LAN beacon 获取 TCP endpoint
|
||||||
|
- `SendMessageTool`(`src/tools/SendMessageTool/SendMessageTool.ts`):支持 `tcp:` scheme,权限检查要求用户确认
|
||||||
|
|
||||||
|
#### 7. Feature Flag
|
||||||
|
|
||||||
|
`LAN_PIPES` — 在 `scripts/dev.ts` 和 `build.ts` 的默认 features 列表中启用。所有 LAN 代码路径均通过 `feature('LAN_PIPES')` 门控。
|
||||||
|
|
||||||
|
#### 8. Pipe IPC 基础系统(`UDS_INBOX` feature)
|
||||||
|
|
||||||
|
- `PipeServer`/`PipeClient`:UDS 传输,NDJSON 协议(共享 `ndjsonFramer.ts`)
|
||||||
|
- `PipeRegistry`:machineId 绑定的角色分配(main/sub),文件锁,并行探测
|
||||||
|
- Master/slave attach 流程、prompt 转发、permission 转发
|
||||||
|
- Heartbeat 生命周期(5s 间隔,stale entry 清理,busy flag 防重叠)
|
||||||
|
- 命令:`/pipes`、`/attach`、`/detach`、`/send`、`/claim-main`、`/pipe-status`
|
||||||
|
|
||||||
|
#### 9. Monitor Tool(`MONITOR_TOOL` feature)
|
||||||
|
|
||||||
|
- `MonitorTool`:AI 可调用的后台 shell 监控工具
|
||||||
|
- `/monitor` 命令:用户快捷入口,Windows 兼容(watch → PowerShell 循环)
|
||||||
|
- `MonitorMcpTask`:从 stub 恢复完整生命周期(register/complete/fail/kill)
|
||||||
|
- `MonitorPermissionRequest`:React 权限确认 UI
|
||||||
|
- `MonitorMcpDetailDialog`:Shift+Down 详情面板
|
||||||
|
|
||||||
|
#### 10. 工具恢复(stub → 实现)
|
||||||
|
|
||||||
|
- SnipTool、SleepTool、ListPeersTool、SendUserFileTool
|
||||||
|
- WebBrowserTool、SubscribePRTool、PushNotificationTool
|
||||||
|
- CtxInspectTool、TerminalCaptureTool、WorkflowTool
|
||||||
|
- REPLTool (.js → .ts)、VerifyPlanExecutionTool (.js → .ts)、SuggestBackgroundPRTool (.js → .ts)
|
||||||
|
- 组件 .ts → .tsx 重写:MonitorPermissionRequest、ReviewArtifactPermissionRequest、MonitorMcpDetailDialog、WorkflowDetailDialog、WorkflowPermissionRequest
|
||||||
|
|
||||||
|
#### 11. REPL Hook 架构重构
|
||||||
|
|
||||||
|
从 REPL.tsx 提取 ~830 行 Pipe IPC 内联代码为 4 个独立 hook:
|
||||||
|
|
||||||
|
| Hook | 行数 | 职责 |
|
||||||
|
|------|------|------|
|
||||||
|
| `usePipeIpc` | 623 | 生命周期:bootstrap、handlers、heartbeat、cleanup |
|
||||||
|
| `usePipeRelay` | 38 | slave→master 消息回传(通过 `setPipeRelay` singleton) |
|
||||||
|
| `usePipePermissionForward` | 159 | 权限请求转发 + 流式通知显示 |
|
||||||
|
| `usePipeRouter` | 130 | selected pipe 输入路由 + role/IP 标签显示 |
|
||||||
|
|
||||||
|
共享工具:`ndjsonFramer.ts` 替换 3 份重复的 NDJSON 解析。
|
||||||
|
|
||||||
|
#### 12. Feature Flags 新增启用
|
||||||
|
|
||||||
|
UDS_INBOX、LAN_PIPES、MONITOR_TOOL、FORK_SUBAGENT、KAIROS、COORDINATOR_MODE、WORKFLOW_SCRIPTS、HISTORY_SNIP、CONTEXT_COLLAPSE
|
||||||
|
|
||||||
|
### 踩坑记录
|
||||||
|
|
||||||
|
1. **Multicast 绑错网卡**:Windows 上 `addMembership(group)` 不指定本地接口时,默认绑到 WSL/Docker 虚拟网卡(`172.19.112.1`),LAN 上的真实机器收不到。必须 `addMembership(group, localIp)` + `setMulticastInterface(localIp)`。
|
||||||
|
|
||||||
|
2. **Beacon ref 丢失**:最初用 `(store.getState() as any)._lanBeacon` 挂载 beacon 引用,但 Zustand `setState` 展开 `prev` 时不包含 `_lanBeacon` 属性,下次读取就是 `undefined`。改为 module-level singleton 解决。
|
||||||
|
|
||||||
|
3. **Heartbeat 清洗 LAN 连接**:`refreshDiscoveredPipes()` 每 5 秒用仅含本地 registry subs 的列表完全覆盖 `discoveredPipes` + `selectedPipes`,LAN peer 的发现和选择状态被持续清空。必须在 refresh 中同时包含 beacon peers。
|
||||||
|
|
||||||
|
4. **Heartbeat cleanup 误删**:`!aliveSubNames.has(slaveName)` 导致 LAN peer(不在本地 registry)被判定为死连接每 5 秒清除一次。需要同时检查 beacon peers 列表。
|
||||||
|
|
||||||
|
5. **跨机器 attach 被拒**:两台机器各自为 `main`,attach handler 硬编码 `role !== 'sub'` 拒绝。通过 attach_request 携带 `machineId`,接收方对不同 machineId 的请求放行。
|
||||||
|
|
||||||
|
6. **`feature()` 使用约束**:Bun 的 `feature()` 是编译时常量,只能在 `if` 语句或三元条件中直接使用,不能赋值给变量(如 `const x = feature('...')`),否则构建报错。
|
||||||
|
|
||||||
|
### 已知限制
|
||||||
|
|
||||||
|
- TCP 无认证:同 LAN 内任何设备知道端口号即可连接
|
||||||
|
- JSON.parse 无 schema 验证:code review 建议增加 Zod 校验
|
||||||
|
- Beacon 明文广播 IP/hostname/machineId:建议后续 hash 处理
|
||||||
|
- `getLocalIp()` 可能返回 VPN 地址:多网卡环境需更精确的接口选择
|
||||||
|
|
||||||
|
### 测试
|
||||||
|
|
||||||
|
- `src/utils/__tests__/lanBeacon.test.ts`:7 个测试(mock dgram)
|
||||||
|
- `src/utils/__tests__/peerAddress.test.ts`:8 个测试(纯函数)
|
||||||
|
- 全量:2190 pass / 0 fail
|
||||||
|
|
||||||
|
### 防火墙配置
|
||||||
|
|
||||||
|
**Windows**(管理员 PowerShell):
|
||||||
|
```powershell
|
||||||
|
New-NetFirewallRule -DisplayName "Claude Code LAN Beacon (UDP)" -Direction Inbound -Protocol UDP -LocalPort 7101 -Action Allow -Profile Private
|
||||||
|
New-NetFirewallRule -DisplayName "Claude Code LAN Pipes (TCP)" -Direction Inbound -Protocol TCP -LocalPort 1024-65535 -Program (Get-Command bun).Source -Action Allow -Profile Private
|
||||||
|
New-NetFirewallRule -DisplayName "Claude Code LAN Beacon Out (UDP)" -Direction Outbound -Protocol UDP -RemotePort 7101 -Action Allow -Profile Private
|
||||||
|
```
|
||||||
|
|
||||||
|
**macOS**(首次运行时系统会弹出"允许接受传入连接"对话框,点击允许即可。手动放行):
|
||||||
|
```bash
|
||||||
|
# 如果使用 pf <20><><EFBFBD>火墙,添加规则:
|
||||||
|
echo "pass in proto udp from any to any port 7101" | sudo pfctl -ef -
|
||||||
|
# 或<><E68896>接在 System Settings → Network → Firewall 中允许 bun 进程
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux**(firewalld):
|
||||||
|
```bash
|
||||||
|
sudo firewall-cmd --zone=trusted --add-port=7101/udp --permanent
|
||||||
|
sudo firewall-cmd --zone=trusted --add-port=1024-65535/tcp --permanent
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux**(iptables):
|
||||||
|
```bash
|
||||||
|
sudo iptables -A INPUT -p udp --dport 7101 -j ACCEPT
|
||||||
|
sudo iptables -A INPUT -p tcp --dport 1024:65535 -m owner --uid-owner $(id -u) -j ACCEPT
|
||||||
|
sudo iptables-save | sudo tee /etc/iptables/rules.v4
|
||||||
|
```
|
||||||
|
|
||||||
|
**通用验证**:确认网络为局域网(非公共 WiFi),路<EFBC8C><E8B7AF><EFBFBD>器未开启 AP 隔离。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
## Daemon + Remote Control Server 还原 (2026-04-07)
|
## Daemon + Remote Control Server 还原 (2026-04-07)
|
||||||
|
|
||||||
**分支**: `feat/daemon-remote-control-server`
|
**分支**: `feat/daemon-remote-control-server`
|
||||||
|
|||||||
132
README.md
132
README.md
@@ -6,37 +6,53 @@
|
|||||||
[](https://github.com/claude-code-best/claude-code/blob/main/LICENSE)
|
[](https://github.com/claude-code-best/claude-code/blob/main/LICENSE)
|
||||||
[](https://github.com/claude-code-best/claude-code/commits/main)
|
[](https://github.com/claude-code-best/claude-code/commits/main)
|
||||||
[](https://bun.sh/)
|
[](https://bun.sh/)
|
||||||
[](https://discord.gg/qZU6zS7Q)
|
[](https://discord.gg/uApuzJWGKX)
|
||||||
|
|
||||||
> Which Claude do you like? The open source one is the best.
|
> Which Claude do you like? The open source one is the best.
|
||||||
|
|
||||||
牢 A (Anthropic) 官方 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI 工具的源码反编译/逆向还原项目。目标是将 Claude Code 大部分功能及工程化能力复现 (问就是老佛爷已经付过钱了)。虽然很难绷, 但是它叫做 CCB(踩踩背)...
|
牢 A (Anthropic) 官方 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI 工具的源码反编译/逆向还原项目。目标是将 Claude Code 大部分功能及工程化能力复现 (问就是老佛爷已经付过钱了)。虽然很难绷, 但是它叫做 CCB(踩踩背)... 而且, 我们实现了企业版或者需要登陆 Claude 账号才能使用的特性, 实现技术普惠
|
||||||
|
|
||||||
[文档在这里, 支持投稿 PR](https://ccb.agent-aura.top/) | [留影文档在这里](./Friends.md) | [Discord 群组](https://discord.gg/qZU6zS7Q)
|
> 我们将会在五一期间进行整个代码仓库的 lint 规范化, 这个期间提交的 PR 可能会有非常多的冲突, 所以大的功能请尽量在这之前提交哈
|
||||||
|
|
||||||
- ✅ [x] V4 — 测试补全、[Buddy](https://ccb.agent-aura.top/docs/features/buddy)、[Auto Mode](https://ccb.agent-aura.top/docs/safety/auto-mode)、环境变量 Feature 开关
|
[文档在这里, 支持投稿 PR](https://ccb.agent-aura.top/) | [留影文档在这里](./Friends.md) | [Discord 群组](https://discord.gg/uApuzJWGKX)
|
||||||
- ✅ [x] V5 — [Sentry](https://ccb.agent-aura.top/docs/internals/sentry-setup) / [GrowthBook](https://ccb.agent-aura.top/docs/internals/growthbook-adapter) 企业监控、[自定义 Login](https://ccb.agent-aura.top/docs/features/custom-platform-login)、[OpenAI 兼容](https://ccb.agent-aura.top/docs/plans/openai-compatibility)、[Web Search](https://ccb.agent-aura.top/docs/features/web-browser-tool)、[Computer Use](https://ccb.agent-aura.top/docs/features/computer-use) / [Chrome Use](https://ccb.agent-aura.top/docs/features/claude-in-chrome-mcp)、[Voice Mode](https://ccb.agent-aura.top/docs/features/voice-mode)、[Bridge Mode](https://ccb.agent-aura.top/docs/features/bridge-mode)、[/dream 记忆整理](https://ccb.agent-aura.top/docs/features/auto-dream)
|
|
||||||
- 🔮 [ ] V6 — 大规模重构石山代码,全面模块分包(全新分支,main 封存为历史版本)
|
|
||||||
|
| 特性 | 说明 | 文档 |
|
||||||
|
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| **Claude 群控技术** | Pipe IPC 多实例协作:同机 main/sub 自动编排 + LAN 跨机器零配置发现与通讯,`/pipes` 选择面板 + `Shift+↓` 交互 + 消息广播路由 | [Pipe IPC](https://ccb.agent-aura.top/docs/features/uds-inbox) / [LAN](https://ccb.agent-aura.top/docs/features/lan-pipes) |
|
||||||
|
| **ACP 协议一等一支持** | 支持接入 Zed、Cursor 等 IDE,支持会话恢复、Skills、权限桥接 | [文档](https://ccb.agent-aura.top/docs/features/acp-zed) |
|
||||||
|
| **Remote Control 私有部署** | Docker 自托管远程界面, 可以手机上看 CC | [文档](https://ccb.agent-aura.top/docs/features/remote-control-self-hosting) |
|
||||||
|
| **Langfuse 监控** | 企业级 Agent 监控, 可以清晰看到每次 agent loop 细节, 可以一键转化为数据集 | [文档](https://ccb.agent-aura.top/docs/features/langfuse-monitoring) |
|
||||||
|
| **Web Search** | 内置网页搜索工具, 支持 bing 和 brave 搜索 | [文档](https://ccb.agent-aura.top/docs/features/web-browser-tool) |
|
||||||
|
| **Poor Mode** | 穷鬼模式,关闭记忆提取和键入建议,大幅度减少并发请求 | /poor 可以开关 |
|
||||||
|
| **Channels 频道通知** | MCP 服务器推送外部消息到会话(飞书/Slack/Discord/微信等),`--channels plugin:name@marketplace` 启用 | [文档](https://ccb.agent-aura.top/docs/features/channels) |
|
||||||
|
| **自定义模型供应商** | OpenAI/Anthropic/Gemini/Grok 兼容 (`/login`) | [文档](https://ccb.agent-aura.top/docs/features/all-features-guide) |
|
||||||
|
| Voice Mode | 语音输入,支持豆包语言输入(`/voice doubao`) | [文档](https://ccb.agent-aura.top/docs/features/voice-mode) |
|
||||||
|
| Computer Use | 屏幕截图、键鼠控制 | [文档](https://ccb.agent-aura.top/docs/features/computer-use) |
|
||||||
|
| Chrome Use | 浏览器自动化、表单填写、数据抓取 | [自托管](https://ccb.agent-aura.top/docs/features/chrome-use-mcp) [原生版](https://ccb.agent-aura.top/docs/features/claude-in-chrome-mcp) |
|
||||||
|
| Sentry | 企业级错误追踪 | [文档](https://ccb.agent-aura.top/docs/internals/sentry-setup) |
|
||||||
|
| GrowthBook | 企业级特性开关 | [文档](https://ccb.agent-aura.top/docs/internals/growthbook-adapter) |
|
||||||
|
| /dream 记忆整理 | 自动整理和优化记忆文件 | [文档](https://ccb.agent-aura.top/docs/features/auto-dream) |
|
||||||
|
|
||||||
- 🚀 [想要启动项目](#快速开始源码版)
|
- 🚀 [想要启动项目](#快速开始源码版)
|
||||||
- 🐛 [想要调试项目](#vs-code-调试)
|
- 🐛 [想要调试项目](#vs-code-调试)
|
||||||
- 📖 [想要学习项目](#teach-me-学习项目)
|
- 📖 [想要学习项目](#teach-me-学习项目)
|
||||||
|
|
||||||
|
|
||||||
## ⚡ 快速开始(安装版)
|
## ⚡ 快速开始(安装版)
|
||||||
|
|
||||||
不用克隆仓库, 从 NPM 下载后, 直接使用
|
不用克隆仓库, 从 NPM 下载后, 直接使用
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun i -g claude-code-best
|
npm i -g claude-code-best
|
||||||
bun pm -g trust claude-code-best
|
|
||||||
ccb # 直接打开 claude code
|
|
||||||
```
|
|
||||||
|
|
||||||
⚠️ 国内对 github 网络较差的, 需要先设置这个环境变量
|
# bun 安装比较多问题, 推荐 npm 装
|
||||||
|
# bun i -g claude-code-best
|
||||||
|
# bun pm -g trust claude-code-best @claude-code-best/mcp-chrome-bridge
|
||||||
|
|
||||||
```bash
|
ccb # 以 nodejs 打开 claude code
|
||||||
DEFAULT_RELEASE_BASE=https://ghproxy.net/https://github.com/microsoft/ripgrep-prebuilt/releases/download/v15.0.1
|
ccb-bun # 以 bun 形态打开
|
||||||
|
ccb update # 更新到最新版本
|
||||||
|
CLAUDE_BRIDGE_BASE_URL=https://remote-control.claude-code-best.win/ CLAUDE_BRIDGE_OAUTH_TOKEN=test-my-key ccb --remote-control # 我们有自部署的远程控制
|
||||||
```
|
```
|
||||||
|
|
||||||
## ⚡ 快速开始(源码版)
|
## ⚡ 快速开始(源码版)
|
||||||
@@ -46,20 +62,69 @@ DEFAULT_RELEASE_BASE=https://ghproxy.net/https://github.com/microsoft/ripgrep-pr
|
|||||||
一定要最新版本的 bun 啊, 不然一堆奇奇怪怪的 BUG!!! bun upgrade!!!
|
一定要最新版本的 bun 啊, 不然一堆奇奇怪怪的 BUG!!! bun upgrade!!!
|
||||||
|
|
||||||
- 📦 [Bun](https://bun.sh/) >= 1.3.11
|
- 📦 [Bun](https://bun.sh/) >= 1.3.11
|
||||||
|
|
||||||
|
**安装 Bun:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux 和 macOS
|
||||||
|
curl -fsSL https://bun.sh/install | bash
|
||||||
|
|
||||||
|
# Windows (PowerShell)
|
||||||
|
powershell -c "irm bun.sh/install.ps1 | iex"
|
||||||
|
```
|
||||||
|
|
||||||
|
**安装后的操作:**
|
||||||
|
|
||||||
|
1. **让当前终端识别 `bun` 命令**
|
||||||
|
|
||||||
|
安装脚本会把 `~/.bun/bin` 写入对应的 shell 配置文件。macOS 默认 zsh 环境通常会看到:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Added "~/.bun/bin" to $PATH in "~/.zshrc"
|
||||||
|
```
|
||||||
|
|
||||||
|
可以按安装脚本提示重启当前 shell:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exec /bin/zsh
|
||||||
|
```
|
||||||
|
|
||||||
|
如果你使用 bash,重新加载 bash 配置:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
Windows PowerShell 用户关闭并重新打开 PowerShell 即可。
|
||||||
|
|
||||||
|
2. **验证 Bun 是否可用**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun --help
|
||||||
|
bun --version
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **如果已经安装过 Bun,更新到最新版本**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun upgrade
|
||||||
|
```
|
||||||
|
|
||||||
- ⚙️ 常规的配置 CC 的方式, 各大提供商都有自己的配置方式
|
- ⚙️ 常规的配置 CC 的方式, 各大提供商都有自己的配置方式
|
||||||
|
|
||||||
|
### 📍 命令执行位置
|
||||||
|
|
||||||
|
- 安装或检查 Bun 的命令可以在任意目录执行:
|
||||||
|
`curl -fsSL https://bun.sh/install | bash`、`bun --help`、`bun --version`、`bun upgrade`
|
||||||
|
- 安装本项目依赖、启动开发模式、构建项目时,必须先进入本仓库根目录,也就是包含 `package.json` 的目录。
|
||||||
|
|
||||||
### 📥 安装
|
### 📥 安装
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
cd /path/to/claude-code
|
||||||
bun install
|
bun install
|
||||||
```
|
```
|
||||||
|
|
||||||
⚠️ 国内对 github 网络较差的,可以使用这个环境变量
|
|
||||||
|
|
||||||
```bash
|
|
||||||
DEFAULT_RELEASE_BASE=https://ghproxy.net/https://github.com/microsoft/ripgrep-prebuilt/releases/download/v15.0.1
|
|
||||||
```
|
|
||||||
|
|
||||||
### ▶️ 运行
|
### ▶️ 运行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -83,17 +148,17 @@ bun run build
|
|||||||
|
|
||||||
需要填写的字段:
|
需要填写的字段:
|
||||||
|
|
||||||
| 📌 字段 | 📝 说明 | 💡 示例 |
|
|
||||||
|------|------|------|
|
| 📌 字段 | 📝 说明 | 💡 示例 |
|
||||||
| Base URL | API 服务地址 | `https://api.example.com/v1` |
|
| ------------ | ------------- | ---------------------------- |
|
||||||
| API Key | 认证密钥 | `sk-xxx` |
|
| Base URL | API 服务地址 | `https://api.example.com/v1` |
|
||||||
| Haiku Model | 快速模型 ID | `claude-haiku-4-5-20251001` |
|
| API Key | 认证密钥 | `sk-xxx` |
|
||||||
| Sonnet Model | 均衡模型 ID | `claude-sonnet-4-6` |
|
| Haiku Model | 快速模型 ID | `claude-haiku-4-5-20251001` |
|
||||||
| Opus Model | 高性能模型 ID | `claude-opus-4-6` |
|
| Sonnet Model | 均衡模型 ID | `claude-sonnet-4-6` |
|
||||||
|
| Opus Model | 高性能模型 ID | `claude-opus-4-6` |
|
||||||
|
|
||||||
- ⌨️ **Tab / Shift+Tab** 切换字段,**Enter** 确认并跳到下一个,最后一个字段按 Enter 保存
|
- ⌨️ **Tab / Shift+Tab** 切换字段,**Enter** 确认并跳到下一个,最后一个字段按 Enter 保存
|
||||||
|
|
||||||
|
|
||||||
> ℹ️ 支持所有 Anthropic API 兼容服务(如 OpenRouter、AWS Bedrock 代理等),只要接口兼容 Messages API 即可。
|
> ℹ️ 支持所有 Anthropic API 兼容服务(如 OpenRouter、AWS Bedrock 代理等),只要接口兼容 Messages API 即可。
|
||||||
|
|
||||||
## Feature Flags
|
## Feature Flags
|
||||||
@@ -113,16 +178,17 @@ TUI (REPL) 模式需要真实终端,无法直接通过 VS Code launch 启动
|
|||||||
### 步骤
|
### 步骤
|
||||||
|
|
||||||
1. **终端启动 inspect 服务**:
|
1. **终端启动 inspect 服务**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run dev:inspect
|
bun run dev:inspect
|
||||||
```
|
```
|
||||||
会输出类似 `ws://localhost:8888/xxxxxxxx` 的地址。
|
|
||||||
|
|
||||||
|
会输出类似 `ws://localhost:8888/xxxxxxxx` 的地址。
|
||||||
2. **VS Code 附着调试器**:
|
2. **VS Code 附着调试器**:
|
||||||
|
|
||||||
- 在 `src/` 文件中打断点
|
- 在 `src/` 文件中打断点
|
||||||
- F5 → 选择 **"Attach to Bun (TUI debug)"**
|
- F5 → 选择 **"Attach to Bun (TUI debug)"**
|
||||||
|
|
||||||
|
|
||||||
## Teach Me 学习项目
|
## Teach Me 学习项目
|
||||||
|
|
||||||
我们新加了一个 teach-me skills, 通过问答式引导帮你理解这个项目的任何模块。(调整 [sigma skill 而来](https://github.com/sanyuan0704/sanyuan-skills))
|
我们新加了一个 teach-me skills, 通过问答式引导帮你理解这个项目的任何模块。(调整 [sigma skill 而来](https://github.com/sanyuan0704/sanyuan-skills))
|
||||||
@@ -149,7 +215,7 @@ TUI (REPL) 模式需要真实终端,无法直接通过 VS Code launch 启动
|
|||||||
## 相关文档及网站
|
## 相关文档及网站
|
||||||
|
|
||||||
- **在线文档(Mintlify)**: [ccb.agent-aura.top](https://ccb.agent-aura.top/) — 文档源码位于 [`docs/`](docs/) 目录,欢迎投稿 PR
|
- **在线文档(Mintlify)**: [ccb.agent-aura.top](https://ccb.agent-aura.top/) — 文档源码位于 [`docs/`](docs/) 目录,欢迎投稿 PR
|
||||||
- **DeepWiki**: <https://deepwiki.com/claude-code-best/claude-code>
|
- **DeepWiki**: [https://deepwiki.com/claude-code-best/claude-code](https://deepwiki.com/claude-code-best/claude-code)
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
@@ -167,6 +233,10 @@ TUI (REPL) 模式需要真实终端,无法直接通过 VS Code launch 启动
|
|||||||
</picture>
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
## 致谢
|
||||||
|
|
||||||
|
- [doubaoime-asr](https://github.com/starccy/doubaoime-asr) — 豆包 ASR 语音识别 SDK,为 Voice Mode 提供无需 Anthropic OAuth 的语音输入方案
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
本项目仅供学习研究用途。Claude Code 的所有权利归 [Anthropic](https://www.anthropic.com/) 所有。
|
本项目仅供学习研究用途。Claude Code 的所有权利归 [Anthropic](https://www.anthropic.com/) 所有。
|
||||||
|
|||||||
55
README_EN.md
55
README_EN.md
@@ -48,11 +48,64 @@ Sponsor placeholder.
|
|||||||
Make sure you're on the latest version of Bun, otherwise you'll run into all sorts of weird bugs. Run `bun upgrade`!
|
Make sure you're on the latest version of Bun, otherwise you'll run into all sorts of weird bugs. Run `bun upgrade`!
|
||||||
|
|
||||||
- [Bun](https://bun.sh/) >= 1.3.11
|
- [Bun](https://bun.sh/) >= 1.3.11
|
||||||
|
|
||||||
|
**Install Bun:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux and macOS
|
||||||
|
curl -fsSL https://bun.sh/install | bash
|
||||||
|
|
||||||
|
# Windows (PowerShell)
|
||||||
|
powershell -c "irm bun.sh/install.ps1 | iex"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Post-installation steps:**
|
||||||
|
|
||||||
|
1. **Make `bun` available in the current terminal**
|
||||||
|
|
||||||
|
The installer adds `~/.bun/bin` to the matching shell configuration file. On macOS with the default zsh shell, you may see:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Added "~/.bun/bin" to $PATH in "~/.zshrc"
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart the current shell as the installer suggests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
exec /bin/zsh
|
||||||
|
```
|
||||||
|
|
||||||
|
If you use bash, reload the bash configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
Windows PowerShell users can close and reopen PowerShell.
|
||||||
|
|
||||||
|
2. **Verify that Bun is available:**
|
||||||
|
```bash
|
||||||
|
bun --help
|
||||||
|
bun --version
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Update to latest version (if already installed):**
|
||||||
|
```bash
|
||||||
|
bun upgrade
|
||||||
|
```
|
||||||
|
|
||||||
- Standard Claude Code configuration — each provider has its own setup method
|
- Standard Claude Code configuration — each provider has its own setup method
|
||||||
|
|
||||||
|
### Command Execution Location
|
||||||
|
|
||||||
|
- Bun installation and checking commands can be run from any directory:
|
||||||
|
`curl -fsSL https://bun.sh/install | bash`, `bun --help`, `bun --version`, `bun upgrade`
|
||||||
|
- Project dependency installation, development mode, and builds must be run from this repository root, the directory containing `package.json`.
|
||||||
|
|
||||||
### Install
|
### Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
cd /path/to/claude-code
|
||||||
bun install
|
bun install
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -135,7 +188,7 @@ The TUI (REPL) mode requires a real terminal and cannot be launched directly via
|
|||||||
## Documentation & Links
|
## Documentation & Links
|
||||||
|
|
||||||
- **Online docs (Mintlify)**: [ccb.agent-aura.top](https://ccb.agent-aura.top/) — source in [`docs/`](docs/), PR contributions welcome
|
- **Online docs (Mintlify)**: [ccb.agent-aura.top](https://ccb.agent-aura.top/) — source in [`docs/`](docs/), PR contributions welcome
|
||||||
- **DeepWiki**: <https://deepwiki.com/claude-code-best/claude-code>
|
- **DeepWiki**: https://deepwiki.com/claude-code-best/claude-code
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
|
|||||||
90
build.ts
90
build.ts
@@ -1,6 +1,7 @@
|
|||||||
import { readdir, readFile, writeFile, cp } from 'fs/promises'
|
import { readdir, readFile, writeFile, cp } from 'fs/promises'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { getMacroDefines } from './scripts/defines.ts'
|
import { getMacroDefines } from './scripts/defines.ts'
|
||||||
|
import { DEFAULT_BUILD_FEATURES } from './scripts/defines.ts'
|
||||||
|
|
||||||
const outdir = 'dist'
|
const outdir = 'dist'
|
||||||
|
|
||||||
@@ -8,33 +9,6 @@ const outdir = 'dist'
|
|||||||
const { rmSync } = await import('fs')
|
const { rmSync } = await import('fs')
|
||||||
rmSync(outdir, { recursive: true, force: true })
|
rmSync(outdir, { recursive: true, force: true })
|
||||||
|
|
||||||
// Default features that match the official CLI build.
|
|
||||||
// Additional features can be enabled via FEATURE_<NAME>=1 env vars.
|
|
||||||
const DEFAULT_BUILD_FEATURES = [
|
|
||||||
'BUDDY',
|
|
||||||
'TRANSCRIPT_CLASSIFIER',
|
|
||||||
'BRIDGE_MODE',
|
|
||||||
'AGENT_TRIGGERS_REMOTE',
|
|
||||||
'CHICAGO_MCP',
|
|
||||||
'VOICE_MODE',
|
|
||||||
'SHOT_STATS',
|
|
||||||
'PROMPT_CACHE_BREAK_DETECTION',
|
|
||||||
'TOKEN_BUDGET',
|
|
||||||
// P0: local features
|
|
||||||
'AGENT_TRIGGERS',
|
|
||||||
'ULTRATHINK',
|
|
||||||
'BUILTIN_EXPLORE_PLAN_AGENTS',
|
|
||||||
'LODESTONE',
|
|
||||||
// P1: API-dependent features
|
|
||||||
'EXTRACT_MEMORIES',
|
|
||||||
'VERIFICATION_AGENT',
|
|
||||||
'KAIROS_BRIEF',
|
|
||||||
'AWAY_SUMMARY',
|
|
||||||
'ULTRAPLAN',
|
|
||||||
// P2: daemon + remote control server
|
|
||||||
'DAEMON',
|
|
||||||
]
|
|
||||||
|
|
||||||
// Collect FEATURE_* env vars → Bun.build features
|
// Collect FEATURE_* env vars → Bun.build features
|
||||||
const envFeatures = Object.keys(process.env)
|
const envFeatures = Object.keys(process.env)
|
||||||
.filter(k => k.startsWith('FEATURE_'))
|
.filter(k => k.startsWith('FEATURE_'))
|
||||||
@@ -78,27 +52,49 @@ for (const file of files) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also patch unguarded globalThis.Bun destructuring from third-party deps
|
||||||
|
// (e.g. @anthropic-ai/sandbox-runtime) so Node.js doesn't crash at import time.
|
||||||
|
let bunPatched = 0
|
||||||
|
const BUN_DESTRUCTURE = /var \{([^}]+)\} = globalThis\.Bun;?/g
|
||||||
|
const BUN_DESTRUCTURE_SAFE = 'var {$1} = typeof globalThis.Bun !== "undefined" ? globalThis.Bun : {};'
|
||||||
|
for (const file of files) {
|
||||||
|
if (!file.endsWith('.js')) continue
|
||||||
|
const filePath = join(outdir, file)
|
||||||
|
const content = await readFile(filePath, 'utf-8')
|
||||||
|
if (BUN_DESTRUCTURE.test(content)) {
|
||||||
|
await writeFile(
|
||||||
|
filePath,
|
||||||
|
content.replace(BUN_DESTRUCTURE, BUN_DESTRUCTURE_SAFE),
|
||||||
|
)
|
||||||
|
bunPatched++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BUN_DESTRUCTURE.lastIndex = 0
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`Bundled ${result.outputs.length} files to ${outdir}/ (patched ${patched} for Node.js compat)`,
|
`Bundled ${result.outputs.length} files to ${outdir}/ (patched ${patched} for import.meta.require, ${bunPatched} for Bun destructure)`,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Step 4: Copy native .node addon files (audio-capture)
|
// Step 4: Copy native .node addon files (audio-capture) and vendored binaries (ripgrep)
|
||||||
const vendorDir = join(outdir, 'vendor', 'audio-capture')
|
const audioCaptureDir = join(outdir, 'vendor', 'audio-capture')
|
||||||
await cp('vendor/audio-capture', vendorDir, { recursive: true })
|
await cp('vendor/audio-capture', audioCaptureDir, { recursive: true })
|
||||||
console.log(`Copied vendor/audio-capture/ → ${vendorDir}/`)
|
console.log(`Copied vendor/audio-capture/ → ${audioCaptureDir}/`)
|
||||||
|
|
||||||
// Step 5: Bundle download-ripgrep script as standalone JS for postinstall
|
const ripgrepDir = join(outdir, 'vendor', 'ripgrep')
|
||||||
const rgScript = await Bun.build({
|
await cp('src/utils/vendor/ripgrep', ripgrepDir, { recursive: true })
|
||||||
entrypoints: ['scripts/download-ripgrep.ts'],
|
console.log(`Copied src/utils/vendor/ripgrep/ → ${ripgrepDir}/`)
|
||||||
outdir,
|
|
||||||
target: 'node',
|
// Step 5: Generate cli-bun and cli-node executable entry points
|
||||||
})
|
const cliBun = join(outdir, 'cli-bun.js')
|
||||||
if (!rgScript.success) {
|
const cliNode = join(outdir, 'cli-node.js')
|
||||||
console.error('Failed to bundle download-ripgrep script:')
|
|
||||||
for (const log of rgScript.logs) {
|
await writeFile(cliBun, '#!/usr/bin/env bun\nimport "./cli.js"\n')
|
||||||
console.error(log)
|
|
||||||
}
|
await writeFile(cliNode, '#!/usr/bin/env node\nimport "./cli.js"\n')
|
||||||
// Non-fatal — postinstall fallback to bun run scripts/download-ripgrep.ts
|
|
||||||
} else {
|
// Make both executable
|
||||||
console.log(`Bundled download-ripgrep script to ${outdir}/`)
|
const { chmodSync } = await import('fs')
|
||||||
}
|
chmodSync(cliBun, 0o755)
|
||||||
|
chmodSync(cliNode, 0o755)
|
||||||
|
|
||||||
|
console.log(`Generated ${cliBun} (shebang: bun) and ${cliNode} (shebang: node)`)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.7 MiB |
188
docs.json
Normal file
188
docs.json
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://mintlify.com/docs.json",
|
||||||
|
"theme": "mint",
|
||||||
|
"name": "Claude Code Architecture",
|
||||||
|
"colors": {
|
||||||
|
"primary": "#D97706",
|
||||||
|
"light": "#F59E0B",
|
||||||
|
"dark": "#B45309"
|
||||||
|
},
|
||||||
|
"favicon": "/docs/favicon.svg",
|
||||||
|
"navigation": {
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"group": "开始",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"group": "介绍",
|
||||||
|
"pages": [
|
||||||
|
"docs/introduction/what-is-claude-code",
|
||||||
|
"docs/introduction/why-this-whitepaper",
|
||||||
|
"docs/introduction/architecture-overview"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "对话是如何运转的",
|
||||||
|
"pages": [
|
||||||
|
"docs/conversation/the-loop",
|
||||||
|
"docs/conversation/streaming",
|
||||||
|
"docs/conversation/multi-turn"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "工具:AI 的双手",
|
||||||
|
"pages": [
|
||||||
|
"docs/tools/what-are-tools",
|
||||||
|
"docs/tools/file-operations",
|
||||||
|
"docs/tools/shell-execution",
|
||||||
|
"docs/tools/search-and-navigation",
|
||||||
|
"docs/tools/task-management"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "上下文工程",
|
||||||
|
"pages": [
|
||||||
|
"docs/context/system-prompt",
|
||||||
|
"docs/context/project-memory",
|
||||||
|
"docs/context/compaction",
|
||||||
|
"docs/context/token-budget"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "多 Agent 协作",
|
||||||
|
"pages": [
|
||||||
|
"docs/agent/sub-agents",
|
||||||
|
"docs/agent/worktree-isolation",
|
||||||
|
"docs/agent/coordinator-and-swarm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "可扩展性",
|
||||||
|
"pages": [
|
||||||
|
"docs/extensibility/mcp-protocol",
|
||||||
|
"docs/extensibility/hooks",
|
||||||
|
"docs/extensibility/skills",
|
||||||
|
"docs/extensibility/custom-agents"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "安全与权限",
|
||||||
|
"pages": [
|
||||||
|
"docs/safety/why-safety-matters",
|
||||||
|
"docs/safety/permission-model",
|
||||||
|
"docs/safety/sandbox",
|
||||||
|
"docs/safety/plan-mode",
|
||||||
|
"docs/safety/auto-mode"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "揭秘:隐藏功能与内部机制",
|
||||||
|
"pages": [
|
||||||
|
"docs/internals/three-tier-gating",
|
||||||
|
"docs/internals/feature-flags",
|
||||||
|
"docs/internals/growthbook-ab-testing",
|
||||||
|
"docs/internals/growthbook-adapter",
|
||||||
|
"docs/internals/sentry-setup",
|
||||||
|
"docs/internals/hidden-features",
|
||||||
|
"docs/internals/ant-only-world",
|
||||||
|
"docs/features/debug-mode",
|
||||||
|
"docs/features/buddy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "隐藏功能详解",
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"group": "Agent 与协作",
|
||||||
|
"pages": [
|
||||||
|
"docs/features/coordinator-mode",
|
||||||
|
"docs/features/fork-subagent",
|
||||||
|
"docs/features/daemon",
|
||||||
|
"docs/features/teammem"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "运行模式",
|
||||||
|
"pages": [
|
||||||
|
"docs/features/kairos",
|
||||||
|
"docs/features/voice-mode",
|
||||||
|
"docs/features/bridge-mode",
|
||||||
|
"docs/features/remote-control-self-hosting",
|
||||||
|
"docs/features/proactive",
|
||||||
|
"docs/features/ultraplan"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "工具增强",
|
||||||
|
"pages": [
|
||||||
|
"docs/features/mcp-skills",
|
||||||
|
"docs/features/tree-sitter-bash",
|
||||||
|
"docs/features/bash-classifier",
|
||||||
|
"docs/features/web-browser-tool",
|
||||||
|
"docs/features/experimental-skill-search"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "上下文与自动化",
|
||||||
|
"pages": [
|
||||||
|
"docs/features/token-budget",
|
||||||
|
"docs/features/context-collapse",
|
||||||
|
"docs/features/workflow-scripts",
|
||||||
|
"docs/features/auto-dream"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"docs/features/tier3-stubs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "基础设施与依赖",
|
||||||
|
"pages": [
|
||||||
|
"docs/auto-updater",
|
||||||
|
"docs/lsp-integration",
|
||||||
|
"docs/external-dependencies",
|
||||||
|
"docs/telemetry-remote-config-audit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"logo": {
|
||||||
|
"light": "/docs/logo/light.svg",
|
||||||
|
"dark": "/docs/logo/dark.svg"
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"color": {
|
||||||
|
"light": "#FFFFFF",
|
||||||
|
"dark": "#0F172A"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"navbar": {
|
||||||
|
"primary": {
|
||||||
|
"type": "github",
|
||||||
|
"href": "https://github.com/claude-code-best/claude-code"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"prompt": "搜索 Claude Code 架构文档..."
|
||||||
|
},
|
||||||
|
"seo": {
|
||||||
|
"metatags": {
|
||||||
|
"og:image": "https://ccb.agent-aura.top/docs/images/og-cover.png",
|
||||||
|
"twitter:image": "https://ccb.agent-aura.top/docs/images/og-cover.png",
|
||||||
|
"twitter:card": "summary_large_image"
|
||||||
|
},
|
||||||
|
"indexing": "navigable"
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"socials": {
|
||||||
|
"github": "https://github.com/anthropics/claude-code"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"redirects": [
|
||||||
|
{
|
||||||
|
"source": "/docs/introduction",
|
||||||
|
"destination": "/docs/introduction/what-is-claude-code"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
# 文档修正计划
|
|
||||||
|
|
||||||
> 目标:补充源码级洞察,让每篇文档从"概念科普"升级为"逆向工程白皮书"水准。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 第一梯队:空壳页,需要大幅重写
|
|
||||||
|
|
||||||
### 1. `safety/sandbox.mdx` — 沙箱机制 ✅ DONE
|
|
||||||
|
|
||||||
**现状**:35 行,只列了"文件系统/网络/进程/时间"四个维度,没有任何实现细节。
|
|
||||||
|
|
||||||
**修正方向**:
|
|
||||||
- 补充 macOS `sandbox-exec` 的实际调用方式,展示沙箱 profile 的关键片段
|
|
||||||
- 说明 `getSandboxConfig()` 的判定逻辑:哪些命令走沙箱、哪些跳过
|
|
||||||
- 补充 `dangerouslyDisableSandbox` 参数的设计权衡
|
|
||||||
- 加入 Linux 平台的沙箱差异对比(seatbelt vs namespace)
|
|
||||||
- 展示一次命令执行从权限检查→沙箱包裹→实际执行的完整链路
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. `introduction/what-is-claude-code.mdx` — 什么是 Claude Code ✅ DONE
|
|
||||||
|
|
||||||
**现状**:39 行,纯营销文案,和"普通聊天 AI"的对比表太低级。
|
|
||||||
|
|
||||||
**修正方向**:
|
|
||||||
- 砍掉"能做什么"的泛泛列表,改为一个具体的端到端示例(从用户输入→系统处理→最终输出)
|
|
||||||
- 用一张简化架构图替代文字描述,让读者 30 秒建立直觉
|
|
||||||
- 补充 Claude Code 的技术定位:不是 IDE 插件、不是 Web Chat,而是 terminal-native agentic system
|
|
||||||
- 加入与 Cursor / Copilot / Aider 等工具的定位差异(架构层面而非功能清单)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. `introduction/why-this-whitepaper.mdx` — 为什么写这份白皮书 ✅ DONE
|
|
||||||
|
|
||||||
**现状**:40 行,全是空话,四张 Card 只是后续章节标题的预告。
|
|
||||||
|
|
||||||
**修正方向**:
|
|
||||||
- 明确定位:这是对 Anthropic 官方 CLI 的逆向工程分析,不是官方文档
|
|
||||||
- 列出逆向过程中发现的 3-5 个最意外/最精妙的设计决策(吊住读者胃口)
|
|
||||||
- 说明白皮书的阅读路线图:推荐的阅读顺序和每个章节解决什么问题
|
|
||||||
- 补充"这份白皮书不是什么"——不是使用教程,不是 API 文档
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 4. `safety/why-safety-matters.mdx` — 为什么安全至关重要 ✅ DONE
|
|
||||||
|
|
||||||
**现状**:40 行,只列了显而易见的风险,"安全 vs 效率的平衡"只有 3 个 bullet。
|
|
||||||
|
|
||||||
**修正方向**:
|
|
||||||
- 从源码角度展示安全体系的全景图:权限规则 → 沙箱 → Plan Mode → 预算上限 → Hooks 的纵深防御链
|
|
||||||
- 补充 Claude 自身 System Prompt 中的安全指令("执行前确认"、"优先可逆操作"等),展示 AI 端的安全约束
|
|
||||||
- 用真实场景说明"安全 vs 效率"的工程权衡:比如 Read 工具为什么免审批、Bash 工具为什么要逐条确认
|
|
||||||
- 加入 Prompt Injection 防御的简要说明(tool result 中的恶意内容如何被系统标记)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 第二梯队:有骨架但太浅,需要补肉
|
|
||||||
|
|
||||||
### 5. `conversation/streaming.mdx` — 流式响应 ✅ DONE
|
|
||||||
|
|
||||||
**现状**:43 行,只说了"流式好"和 3 行 provider 表。
|
|
||||||
|
|
||||||
**修正方向**:
|
|
||||||
- 补充 `BetaRawMessageStreamEvent` 的核心事件类型及其含义
|
|
||||||
- 展示文本 chunk 和 tool_use block 交织的状态机流转
|
|
||||||
- 说明流式中的错误处理:网络断开、API 限流、token 超限时的重试/降级策略
|
|
||||||
- 补充 `processStreamEvents()` 的核心逻辑:如何从事件流中分离出文本、工具调用、usage 统计
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 6. `tools/search-and-navigation.mdx` — 搜索与导航 ✅ DONE
|
|
||||||
|
|
||||||
**现状**:43 行,只说 Glob 和 Grep 存在。
|
|
||||||
|
|
||||||
**修正方向**:
|
|
||||||
- 补充 ripgrep 二进制的内嵌方式(vendor 目录、平台适配)
|
|
||||||
- 说明搜索结果的 head_limit 默认 250 的设计原因(token 预算)
|
|
||||||
- 展示 ToolSearch 的实现:如何用语义匹配在 50+ 工具(含 MCP)中找到最相关的
|
|
||||||
- 补充 Glob 按修改时间排序的意义:最近修改的文件最可能与当前任务相关
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 7. `tools/task-management.mdx` — 任务管理 ✅ DONE
|
|
||||||
|
|
||||||
**现状**:50 行,只有流程 Steps 和状态展示的 4 个 bullet。
|
|
||||||
|
|
||||||
**修正方向**:
|
|
||||||
- 补充任务的数据模型:id / subject / description / status / blockedBy / blocks / owner
|
|
||||||
- 说明依赖管理的实现:blockedBy 如何阻止任务被认领、完成一个任务后如何自动解锁下游
|
|
||||||
- 展示任务与 Agent 工具的联动:子 Agent 如何认领任务、报告进度
|
|
||||||
- 补充 activeForm 字段的 UX 设计:进行中任务的 spinner 动画文案
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 8. `context/token-budget.mdx` — Token 预算管理 ✅ DONE
|
|
||||||
|
|
||||||
**现状**:55 行,预算控制只有 3 张 Card 各一句话。
|
|
||||||
|
|
||||||
**修正方向**:
|
|
||||||
- 补充 `contextWindowTokens` 和 `maxOutputTokens` 的动态计算逻辑
|
|
||||||
- 说明缓存 breakpoint 的放置策略:System Prompt 中不变内容在前、变化内容在后的原因
|
|
||||||
- 展示工具输出截断的具体机制:超长结果如何被 truncate、何时触发 micro-compact
|
|
||||||
- 补充 token 计数的实现:`countTokens` 的调用时机和近似 vs 精确计数的权衡
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 9. `agent/worktree-isolation.mdx` — Worktree 隔离 ✅ DONE
|
|
||||||
|
|
||||||
**现状**:55 行,只描述了 git worktree 的概念。
|
|
||||||
|
|
||||||
**修正方向**:
|
|
||||||
- 展示 `.claude/worktrees/` 的目录结构和分支命名规则
|
|
||||||
- 说明 worktree 的生命周期:创建时机(`isolation: "worktree"`)→ 子 Agent 执行 → 完成/放弃 → 自动清理
|
|
||||||
- 补充 worktree 与子 Agent 的绑定关系:Agent 结束时如何判断 keep or remove
|
|
||||||
- 加入 EnterWorktree / ExitWorktree 工具的交互设计
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 10. `extensibility/custom-agents.mdx` — 自定义 Agent ✅ DONE
|
|
||||||
|
|
||||||
**现状**:56 行,只有配置表和示例表。
|
|
||||||
|
|
||||||
**修正方向**:
|
|
||||||
- 展示 agent markdown 文件的完整 frontmatter 格式(name / description / model / allowedTools 等)
|
|
||||||
- 说明 agent 如何被加载和注入 System Prompt:`loadAgentDefinitions()` 的发现和合并逻辑
|
|
||||||
- 展示工具限制的实现:allowedTools 如何过滤工具列表
|
|
||||||
- 补充 agent 与 subagent_type 参数的关联:Agent 工具如何指定使用自定义 Agent
|
|
||||||
@@ -10,13 +10,13 @@ keywords: ["协调者模式", "蜂群模式", "Agent Swarm", "多 Agent 协作",
|
|||||||
|
|
||||||
| 维度 | Coordinator Mode | Agent Swarms |
|
| 维度 | Coordinator Mode | Agent Swarms |
|
||||||
|------|-----------------|--------------|
|
|------|-----------------|--------------|
|
||||||
| **门控** | `feature('COORDINATOR_MODE')` + `CLAUDE_CODE_COORDINATOR_MODE=1` | 任务系统 V2(默认启用) |
|
| **门控** | `feature('COORDINATOR_MODE')` + `CLAUDE_CODE_COORDINATOR_MODE=1` | `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` 环境变量 |
|
||||||
| **拓扑** | 星型:Coordinator 居中,Worker 外围 | 网状:对等 Agent 共享任务列表 |
|
| **拓扑** | 星型:Coordinator 居中,Worker 外围 | 星型+P2P 混合:Team Lead 协调,Teammate 间可直接通信 |
|
||||||
| **角色** | 明确分工:Coordinator 编排、Worker 执行 | 模糊:每个 Agent 自主认领任务 |
|
| **角色** | 明确分工:Coordinator 编排、Worker 执行 | Team Lead 协调 + Teammate 自主认领任务 |
|
||||||
| **通信** | `SendMessage` 定向通信 + `<task-notification>` | 任务文件系统 + 邮箱广播 |
|
| **通信** | `SendMessage` 定向通信 + `<task-notification>` | Mailbox 消息系统(message / broadcast) |
|
||||||
| **适用** | 需要集中决策的复杂任务 | 并行度高的独立子任务 |
|
| **适用** | 需要集中决策的复杂任务 | 并行度高、需要 Teammate 间直接协作的任务 |
|
||||||
|
|
||||||
两者不是互斥的——Coordinator Mode 可以在 Swarm 架构之上运行,将 Coordinator 作为特殊的 Leader Agent。
|
两者不是互斥的——理论上 Coordinator Mode 可以在 Agent Teams 架构之上运行(概念层叠加,非嵌套团队),将 Coordinator 作为特殊的 Team Lead,但这部分集成(`workerAgent.ts` 中的 `getCoordinatorAgents`)目前为 stub 实现,尚未完整落地。
|
||||||
|
|
||||||
## Coordinator Mode:星型编排架构
|
## Coordinator Mode:星型编排架构
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ Coordinator 被剥夺了所有"动手"工具,只保留编排能力:
|
|||||||
| **TaskStop** | 中途停止走错方向的 Worker |
|
| **TaskStop** | 中途停止走错方向的 Worker |
|
||||||
| **subscribe_pr_activity** | 订阅 GitHub PR 事件(review comments、CI 结果) |
|
| **subscribe_pr_activity** | 订阅 GitHub PR 事件(review comments、CI 结果) |
|
||||||
|
|
||||||
Coordinator **不写代码、不读文件、不执行命令**——它只做三件事:理解需求、分配任务、综合结果。
|
Coordinator **不写代码、不读文件、不执行命令**——它的核心职责是:理解需求、分配任务、综合结果,以及在无需工具时直接回答用户问题。
|
||||||
|
|
||||||
### Worker 的工具权限
|
### Worker 的工具权限
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ Worker 的可用工具由 `getCoordinatorUserContext()`(`coordinatorMode.ts:80
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// 简化模式下:只有 Bash + Read + Edit
|
// 简化模式下:只有 Bash + Read + Edit
|
||||||
const workerTools = isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE')
|
const workerTools = isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)
|
||||||
? [BASH_TOOL_NAME, FILE_READ_TOOL_NAME, FILE_EDIT_TOOL_NAME]
|
? [BASH_TOOL_NAME, FILE_READ_TOOL_NAME, FILE_EDIT_TOOL_NAME]
|
||||||
: Array.from(ASYNC_AGENT_ALLOWED_TOOLS)
|
: Array.from(ASYNC_AGENT_ALLOWED_TOOLS)
|
||||||
.filter(name => !INTERNAL_WORKER_TOOLS.has(name))
|
.filter(name => !INTERNAL_WORKER_TOOLS.has(name))
|
||||||
@@ -63,7 +63,7 @@ const workerTools = isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE')
|
|||||||
|
|
||||||
### Scratchpad:跨 Worker 的共享知识库
|
### Scratchpad:跨 Worker 的共享知识库
|
||||||
|
|
||||||
当 `tengu_scratch` feature flag 启用时,Coordinator 拥有一个 Scratchpad 目录:
|
当 `isScratchpadGateEnabled()`(内部检查 `tengu_scratch` feature gate)启用时,Workers 获得一个 Scratchpad 目录,Coordinator 通过其系统上下文知晓该目录的存在:
|
||||||
|
|
||||||
```
|
```
|
||||||
Scratchpad 目录:
|
Scratchpad 目录:
|
||||||
@@ -113,32 +113,84 @@ Coordinator System Prompt(`coordinatorMode.ts:111-369`,约 260 行)明确
|
|||||||
|
|
||||||
这是 Coordinator Mode 最核心的设计约束:Coordinator 必须先理解,再分配。
|
这是 Coordinator Mode 最核心的设计约束:Coordinator 必须先理解,再分配。
|
||||||
|
|
||||||
## Agent Swarms:蜂群式协作
|
## Agent Teams (Swarm):蜂群式协作
|
||||||
|
|
||||||
Swarm 模式基于任务系统 V2(详见[任务管理](../tools/task-management.mdx)),核心机制是**共享任务列表 + 竞争认领**:
|
Swarm 模式基于任务系统 V2(详见[任务管理](../tools/task-management.mdx)),核心机制是**共享任务列表 + 竞争认领 + Mailbox 消息系统**:
|
||||||
|
|
||||||
### 团队初始化
|
### 团队初始化
|
||||||
|
|
||||||
```
|
```
|
||||||
Leader 创建团队(TeamCreateTool)
|
Team Lead 创建团队(TeamCreateTool)
|
||||||
↓
|
↓
|
||||||
设置 teamName → setLeaderTeamName()
|
设置 teamName → setLeaderTeamName()
|
||||||
↓
|
↓
|
||||||
所有 teammate 自动获得相同的 taskListId
|
所有 Teammate 自动获得相同的 taskListId
|
||||||
↓
|
↓
|
||||||
teammate 启动时:
|
Teammate 启动时:
|
||||||
1. CLAUDE_CODE_TASK_LIST_ID 环境变量(显式覆盖)
|
1. CLAUDE_CODE_TASK_LIST_ID 环境变量(显式覆盖)
|
||||||
2. teammate 上下文的 teamName(共享 leader 的任务列表)
|
2. Teammate 上下文的 teamName(共享 Lead 的任务列表)
|
||||||
3. CLAUDE_CODE_TEAM_NAME 环境变量
|
3. CLAUDE_CODE_TEAM_NAME 环境变量
|
||||||
4. leader 设置的 teamName
|
4. Lead 设置的 teamName
|
||||||
5. getSessionId()(兜底)
|
5. getSessionId()(兜底)
|
||||||
```
|
```
|
||||||
|
|
||||||
多级优先级确保了 Leader 和所有 Teammate 指向同一个任务列表,无需额外协调。
|
多级优先级确保了 Team Lead 和所有 Teammate 指向同一个任务列表,无需额外协调。
|
||||||
|
|
||||||
|
### 架构组件
|
||||||
|
|
||||||
|
官方 Agent Teams 架构定义了四个核心组件:
|
||||||
|
|
||||||
|
| 组件 | 角色 |
|
||||||
|
|------|------|
|
||||||
|
| **Team Lead** | 创建团队、分配任务、综合结果的主 Claude Code 会话 |
|
||||||
|
| **Teammate** | 独立的 Claude Code 实例,各自拥有独立的上下文窗口 |
|
||||||
|
| **Task List** | 共享的任务列表,Teammate 竞争认领和完成 |
|
||||||
|
| **Mailbox** | 消息系统,支持 Teammate 间直接通信 |
|
||||||
|
|
||||||
|
### Mailbox 消息系统
|
||||||
|
|
||||||
|
官方架构中的 Mailbox 是 Teammate 间通信的核心原语,支持两种消息模式(`broadcast` 模式来自源码推断,官方文档未明确细分):
|
||||||
|
|
||||||
|
| 模式 | 作用 | 场景 |
|
||||||
|
|------|------|------|
|
||||||
|
| **message** | 定向发送给指定 Teammate | 传递具体指令、请求协作 |
|
||||||
|
| **broadcast** | 广播给所有 Teammate | 全局通知、状态同步 |
|
||||||
|
|
||||||
|
Mailbox 的关键特性:
|
||||||
|
- **自动投递**:消息自动送达目标 Teammate 的对话上下文
|
||||||
|
- **空闲通知**(TeammateIdle):Teammate 完成当前任务进入空闲时,自动通过 Mailbox 通知 Team Lead
|
||||||
|
- **直接通信**:与 Coordinator Mode 不同,Teammate 之间可以直接通信,无需经过 Lead 中转
|
||||||
|
|
||||||
|
### Hook 事件
|
||||||
|
|
||||||
|
Agent Teams 提供三个关键 Hook 事件,用于在团队生命周期中注入自定义逻辑:
|
||||||
|
|
||||||
|
| Hook | 触发时机 | 典型用途 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| **TaskCreated** | 新任务添加到任务列表时 | 自动分配、优先级排序 |
|
||||||
|
| **TaskCompleted** | 任务标记为完成时 | 结果通知、依赖解锁 |
|
||||||
|
| **TeammateIdle** | Teammate 完成所有任务进入空闲时 | Lead 重新分配、动态扩缩容 |
|
||||||
|
|
||||||
|
### 限制
|
||||||
|
|
||||||
|
当前 Agent Teams 实现的限制:
|
||||||
|
- **不支持嵌套团队**:Teammate 不能再创建子团队
|
||||||
|
- **每 session 一个团队**:一个会话只能属于一个团队
|
||||||
|
- **Lead 固定**:Team Lead 创建后不可更换
|
||||||
|
- **不支持 in-process Teammate 的会话恢复**:进程重启后 in-process 类型 Teammate 的状态丢失
|
||||||
|
|
||||||
|
### 持久化存储
|
||||||
|
|
||||||
|
团队状态通过文件系统持久化,确保进程重启后可恢复:
|
||||||
|
|
||||||
|
```
|
||||||
|
~/.claude/teams/{team-name}/config.json ← 团队配置
|
||||||
|
~/.claude/tasks/{team-name}/ ← 共享任务列表(文件锁保护)
|
||||||
|
```
|
||||||
|
|
||||||
### 任务认领与竞争
|
### 任务认领与竞争
|
||||||
|
|
||||||
`claimTask()` 是 Swarm 的核心并发原语:
|
`claimTask()` 是 Agent Teams 的核心并发原语:
|
||||||
|
|
||||||
```
|
```
|
||||||
Teammate A 调用 TaskList → 发现 task #3 是 pending
|
Teammate A 调用 TaskList → 发现 task #3 是 pending
|
||||||
@@ -146,7 +198,7 @@ Teammate B 同时发现 task #3 是 pending
|
|||||||
↓
|
↓
|
||||||
两者同时尝试 TaskUpdate(task #3, {status: "in_progress"})
|
两者同时尝试 TaskUpdate(task #3, {status: "in_progress"})
|
||||||
↓
|
↓
|
||||||
文件锁 + 高水位标记保证原子性:
|
文件锁保证原子性:
|
||||||
- 第一个写入者获得 owner 锁定
|
- 第一个写入者获得 owner 锁定
|
||||||
- 第二个写入者收到 already_claimed 错误
|
- 第二个写入者收到 already_claimed 错误
|
||||||
↓
|
↓
|
||||||
@@ -166,8 +218,11 @@ unassignTeammateTasks()
|
|||||||
→ 扫描任务列表,找到 owner === teammateName 的未完成任务
|
→ 扫描任务列表,找到 owner === teammateName 的未完成任务
|
||||||
→ 重置为 pending + owner=undefined
|
→ 重置为 pending + owner=undefined
|
||||||
↓
|
↓
|
||||||
Leader 通过 mailbox 收到通知
|
Team Lead 感知途径:
|
||||||
→ 重新分配或创建新 Teammate
|
1. 任务状态变化(pending 重置)—— 通过共享任务列表
|
||||||
|
2. Mailbox 空闲通知(TeammateIdle hook)—— Teammate 停止时自动通知 Lead
|
||||||
|
↓
|
||||||
|
Team Lead 重新分配任务或创建新 Teammate
|
||||||
```
|
```
|
||||||
|
|
||||||
## 任务类型全景
|
## 任务类型全景
|
||||||
@@ -186,11 +241,11 @@ Leader 通过 mailbox 收到通知
|
|||||||
|
|
||||||
`InProcessTeammateTask` 与 `LocalAgentTask` 的关键差异:前者共享进程的内存空间和基础设施状态(如 MCP 连接池),但有独立的对话上下文和工具权限;后者是完全隔离的子进程,启动开销更大但更安全。
|
`InProcessTeammateTask` 与 `LocalAgentTask` 的关键差异:前者共享进程的内存空间和基础设施状态(如 MCP 连接池),但有独立的对话上下文和工具权限;后者是完全隔离的子进程,启动开销更大但更安全。
|
||||||
|
|
||||||
## Coordinator vs Swarm 的选择
|
## Coordinator vs Agent Teams 的选择
|
||||||
|
|
||||||
| 场景 | 推荐模式 | 原因 |
|
| 场景 | 推荐模式 | 原因 |
|
||||||
|------|---------|------|
|
|------|---------|------|
|
||||||
| "重构认证系统,需要多模块协调" | Coordinator | 需要集中决策,Worker 间有依赖 |
|
| "重构认证系统,需要多模块协调" | Coordinator | 需要集中决策,Worker 间有依赖 |
|
||||||
| "修复 10 个独立的 lint 警告" | Swarm | 任务独立,可完全并行 |
|
| "修复 10 个独立的 lint 警告" | Agent Teams | 任务独立,Teammate 可完全并行 |
|
||||||
| "研究方案 A 和方案 B,然后选一个实现" | Coordinator | 先并行研究,再集中决策 |
|
| "研究方案 A 和方案 B,然后选一个实现" | Coordinator | 先并行研究,再集中决策 |
|
||||||
| "在大仓库中搜索所有 TODO 并分类" | Swarm | 无依赖,各自领任务即可 |
|
| "在大仓库中搜索所有 TODO 并分类" | Agent Teams | 无依赖,各自领任务即可 |
|
||||||
|
|||||||
@@ -13,44 +13,48 @@ keywords: ["子 Agent", "AgentTool", "任务委派", "forkSubagent", "子进程
|
|||||||
```
|
```
|
||||||
AI 生成 tool_use: { prompt: "修复 bug", subagent_type: "Explore" }
|
AI 生成 tool_use: { prompt: "修复 bug", subagent_type: "Explore" }
|
||||||
↓
|
↓
|
||||||
AgentTool.call() ← 入口(AgentTool.tsx:239)
|
AgentTool.call() ← 入口(AgentTool.tsx:387)
|
||||||
├── 解析 effectiveType(fork vs 命名 agent)
|
├── 解析 effectiveType(fork vs 命名 agent vs GP 回退)
|
||||||
├── filterDeniedAgents() ← 权限过滤
|
├── filterDeniedAgents() ← 仅命名 Agent 路径执行:权限过滤
|
||||||
├── 检查 requiredMcpServers ← MCP 依赖验证(最长等 30s)
|
├── 检查 requiredMcpServers ← MCP 依赖验证(最长等 30s)
|
||||||
├── assembleToolPool(workerPermissionContext) ← 独立组装工具池
|
├── assembleToolPool(workerPermissionContext) ← 独立组装工具池
|
||||||
├── createAgentWorktree() ← 可选 worktree 隔离
|
├── createAgentWorktree() ← 可选 worktree 隔离
|
||||||
↓
|
↓
|
||||||
runAgent() ← 核心执行(runAgent.ts:248)
|
runAgent() ← 核心执行(runAgent.ts)
|
||||||
├── getAgentSystemPrompt() ← 构建 agent 专属 system prompt
|
├── getAgentSystemPrompt() ← 构建 agent 专属 system prompt
|
||||||
├── initializeAgentMcpServers() ← agent 级 MCP 服务器
|
├── initializeAgentMcpServers() ← agent 级 MCP 服务器
|
||||||
├── executeSubagentStartHooks() ← Hook 注入
|
├── executeSubagentStartHooks() ← Hook 注入
|
||||||
├── query() ← 进入标准 agentic loop
|
├── query() ← 进入标准 agentic loop
|
||||||
│ ├── 消息流逐条 yield
|
│ ├── 消息流逐条 yield
|
||||||
│ └── recordSidechainTranscript() ← JSONL 持久化
|
│ └── recordSidechainTranscript() ← JSONL 持久化(~/.claude/projects/{project}/{session}/subagents/)
|
||||||
↓
|
↓
|
||||||
finalizeAgentTool() ← 结果汇总
|
finalizeAgentTool() ← 结果汇总
|
||||||
├── 提取文本内容 + usage 统计
|
├── 提取文本内容 + usage 统计
|
||||||
└── mapToolResultToToolResultBlockParam() ← 格式化为 tool_result
|
└── mapToolResultToToolResultBlockParam() ← 格式化为 tool_result
|
||||||
```
|
```
|
||||||
|
|
||||||
## 两种子 Agent 路径:命名 Agent vs Fork
|
## 子 Agent 的三种路径
|
||||||
|
|
||||||
`AgentTool.call()` 根据是否提供 `subagent_type` 走两条完全不同的路径(`AgentTool.tsx:322-356`):
|
`AgentTool.call()` 根据 `subagent_type` 参数和 Fork 实验开关,走三条不同的路径:
|
||||||
|
|
||||||
| 维度 | 命名 Agent(`subagent_type` 指定) | Fork 子进程(`subagent_type` 省略) |
|
| 维度 | 命名 Agent(`subagent_type` 指定) | Fork 子进程(Fork 启用 + 类型省略) | General-purpose 回退(Fork 关闭 + 类型省略) |
|
||||||
|------|-------------------------------------|--------------------------------------|
|
|------|-------------------------------------|--------------------------------------|---------------------------------------------|
|
||||||
| **触发条件** | `subagent_type` 有值 | `isForkSubagentEnabled()` && 未指定类型 |
|
| **触发条件** | `subagent_type` 有值 | `isForkSubagentEnabled() === true` 且未指定类型 | `isForkSubagentEnabled() === false` 且未指定类型 |
|
||||||
| **System Prompt** | Agent 自身的 `getSystemPrompt()` | 继承父 Agent 的完整 System Prompt |
|
| **System Prompt** | Agent 自身的 `getSystemPrompt()` | 继承父 Agent 的完整 System Prompt | General-purpose Agent 的 `getSystemPrompt()` |
|
||||||
| **工具池** | `assembleToolPool()` 独立组装 | 父 Agent 的原始工具池(`useExactTools: true`) |
|
| **工具池** | `assembleToolPool()` 独立组装 | 父 Agent 的原始工具池(`useExactTools: true`) | `assembleToolPool()` 独立组装 |
|
||||||
| **上下文** | 仅任务描述 | 父 Agent 的完整对话历史(`forkContextMessages`) |
|
| **上下文** | 仅任务描述 | 父 Agent 的完整对话历史(`forkContextMessages`) | 仅任务描述 |
|
||||||
| **模型** | 可独立指定 | 继承父模型(`model: 'inherit'`) |
|
| **模型** | 可独立指定 | 继承父模型(`model: 'inherit'`) | 可独立指定 |
|
||||||
| **权限模式** | Agent 定义的 `permissionMode` | `'bubble'`(上浮到父终端) |
|
| **权限模式** | Agent 定义的 `permissionMode` | `'bubble'`(上浮到父终端) | Agent 定义的 `permissionMode` |
|
||||||
| **目的** | 专业任务委派 | Prompt Cache 命中率优化 |
|
| **目的** | 专业任务委派 | Prompt Cache 命中率优化 | 通用任务处理 |
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Fork 实验的门控函数 `isForkSubagentEnabled()` 需要同时满足三个前提:`FORK_SUBAGENT` feature flag 已启用、当前不在 Coordinator 模式中、且不是非交互式会话。任一条件不满足时,省略 `subagent_type` 会静默降级为 General-purpose Agent,而非触发 Fork。
|
||||||
|
</Note>
|
||||||
|
|
||||||
Fork 路径的设计核心是 **Prompt Cache 共享**:所有 fork 子进程共享父 Agent 的完整 `assistant` 消息(所有 `tool_use` 块),用相同的占位符 `tool_result` 填充,只有最后一个 `text` 块包含各自的指令。这使得 API 请求前缀字节完全一致,最大化缓存命中。
|
Fork 路径的设计核心是 **Prompt Cache 共享**:所有 fork 子进程共享父 Agent 的完整 `assistant` 消息(所有 `tool_use` 块),用相同的占位符 `tool_result` 填充,只有最后一个 `text` 块包含各自的指令。这使得 API 请求前缀字节完全一致,最大化缓存命中。
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// forkSubagent.ts:142 — 所有 fork 子进程的占位结果
|
// forkSubagent.ts:93 — 所有 fork 子进程的占位结果
|
||||||
const FORK_PLACEHOLDER_RESULT = 'Fork started — processing in background'
|
const FORK_PLACEHOLDER_RESULT = 'Fork started — processing in background'
|
||||||
|
|
||||||
// buildForkedMessages() 构建:
|
// buildForkedMessages() 构建:
|
||||||
@@ -59,14 +63,46 @@ const FORK_PLACEHOLDER_RESULT = 'Fork started — processing in background'
|
|||||||
|
|
||||||
### Fork 递归防护
|
### Fork 递归防护
|
||||||
|
|
||||||
Fork 子进程保留 Agent 工具(为了 cache-identical tool defs),但通过两道防线防止递归 fork(`AgentTool.tsx:332`):
|
Fork 子进程保留 Agent 工具(为了 cache-identical tool defs),但通过两道防线防止递归 fork:
|
||||||
|
|
||||||
1. **`querySource` 检查**(压缩安全):`context.options.querySource === 'agent:builtin:fork'`
|
1. **`querySource` 检查**(压缩安全):`context.options.querySource === 'agent:builtin:fork'`
|
||||||
2. **消息扫描**(降级兜底):检测 `<fork-boilerplate>` 标签
|
2. **消息扫描**(降级兜底):检测 `<fork-boilerplate>` 标签
|
||||||
|
|
||||||
## 工具池的独立组装
|
### 模型解析优先级
|
||||||
|
|
||||||
子 Agent 不继承父 Agent 的工具限制——它的工具池完全独立组装(`AgentTool.tsx:573-577`):
|
子 Agent 的模型选择遵循严格的优先级链(`src/utils/model/agent.ts`):
|
||||||
|
|
||||||
|
```
|
||||||
|
1. CLAUDE_CODE_SUBAGENT_MODEL 环境变量 ← 全局覆盖
|
||||||
|
↓(未设置时)
|
||||||
|
2. 每次调用的 model 参数 ← AgentTool 入参
|
||||||
|
↓(未指定时)
|
||||||
|
3. Agent 定义的 model frontmatter ← 如 "sonnet", "haiku", "inherit"
|
||||||
|
↓(未定义时)
|
||||||
|
4. 继承父对话模型(conversation model) ← getDefaultSubagentModel() 返回 "inherit"
|
||||||
|
```
|
||||||
|
|
||||||
|
其中 `inherit` 不是简单的模型传递——它经过 `getRuntimeMainLoopModel()` 解析,确保 plan mode 下的 `opusplan→Opus` 等运行时映射正确生效。当 Agent 指定的模型族(如 `haiku`)与父模型同族时,直接复用父模型的精确 ID,避免跨 provider 降级。
|
||||||
|
|
||||||
|
## 命名 Agent 的工具池独立组装
|
||||||
|
|
||||||
|
### 内置 Agent
|
||||||
|
|
||||||
|
系统预定义了几个内置 Agent(`packages/builtin-tools/src/tools/AgentTool/builtInAgents.ts`),各有明确的职责和模型配置:
|
||||||
|
|
||||||
|
| Agent | 模型 | 权限 | 用途 |
|
||||||
|
|-------|------|------|------|
|
||||||
|
| **Explore** | Haiku(轻量快速) | 只读(Read/Grep/Glob) | 代码库搜索与探索 |
|
||||||
|
| **Plan** | 继承父模型 | 只读 | 为 Plan Mode 收集研究信息 |
|
||||||
|
| **General-purpose** | 继承父模型 | 全部工具 | 复杂的通用任务处理 |
|
||||||
|
| **statusline-setup** | 继承父模型 | 受限 | 配置状态栏设置 |
|
||||||
|
| **claude-code-guide** | 继承父模型 | 受限 | 解答 Claude Code 使用问题 |
|
||||||
|
|
||||||
|
用户还可通过 `.claude/agents/` 目录或 settings 定义自定义 Agent,作用域优先级为:managed settings > CLI `--agents` > 项目级 `.claude/agents/` > 用户级 `~/.claude/agents/` > plugin。
|
||||||
|
|
||||||
|
命名 Agent(包括 General-purpose 回退)不继承父 Agent 的工具限制——它的工具池完全独立组装。Fork 子进程则通过 `useExactTools: true` 直接继承父 Agent 的原始工具池,以保持 Prompt Cache 中工具定义的字节一致性。
|
||||||
|
|
||||||
|
命名 Agent 的工具池组装逻辑:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const workerPermissionContext = {
|
const workerPermissionContext = {
|
||||||
@@ -83,7 +119,7 @@ const workerTools = assembleToolPool(workerPermissionContext, appState.mcp.tools
|
|||||||
|
|
||||||
### 工具过滤的 resolveAgentTools
|
### 工具过滤的 resolveAgentTools
|
||||||
|
|
||||||
`runAgent.ts:500-502` 在工具组装后进一步过滤:
|
`runAgent.ts:508` 在工具组装后进一步过滤:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const resolvedTools = useExactTools
|
const resolvedTools = useExactTools
|
||||||
@@ -93,9 +129,20 @@ const resolvedTools = useExactTools
|
|||||||
|
|
||||||
`resolveAgentTools()` 会根据 Agent 定义中的 `tools` 字段过滤可用工具,将 `['*']` 映射为全量工具。
|
`resolveAgentTools()` 会根据 Agent 定义中的 `tools` 字段过滤可用工具,将 `['*']` 映射为全量工具。
|
||||||
|
|
||||||
|
### Hook 事件
|
||||||
|
|
||||||
|
子 Agent 支持 Agent 定义 frontmatter 和全局 settings.json 两种级别的 Hook:
|
||||||
|
|
||||||
|
| 来源 | 事件 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| Agent frontmatter `hooks` | `PreToolUse` / `PostToolUse` | 工具调用前后拦截 |
|
||||||
|
| Agent frontmatter `hooks` | `Stop` | 自动转换为 `SubagentStop`(`registerFrontmatterHooks` 传入 `isAgent=true`) |
|
||||||
|
| settings.json | `SubagentStart` | 子 Agent 启动时触发(`executeSubagentStartHooks()`) |
|
||||||
|
| settings.json | `SubagentStop` | 子 Agent 停止时触发 |
|
||||||
|
|
||||||
## Worktree 隔离机制
|
## Worktree 隔离机制
|
||||||
|
|
||||||
`isolation: "worktree"` 参数让子 Agent 在独立的 git worktree 中工作(`AgentTool.tsx:590-593`):
|
`isolation: "worktree"` 参数让子 Agent 在独立的 git worktree 中工作(`AgentTool.tsx:863`):
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const slug = `agent-${earlyAgentId.slice(0, 8)}`
|
const slug = `agent-${earlyAgentId.slice(0, 8)}`
|
||||||
@@ -115,24 +162,28 @@ Worktree 生命周期:
|
|||||||
|
|
||||||
### 异步 Agent(后台运行)
|
### 异步 Agent(后台运行)
|
||||||
|
|
||||||
当 `run_in_background=true` 或 `selectedAgent.background=true` 时,Agent 立即返回 `async_launched` 状态(`AgentTool.tsx:686-764`):
|
当 `run_in_background=true`、`selectedAgent.background=true`、或系统判定应强制异步(如 `assistantForceAsync`、`proactiveModule` 激活)时,Agent 立即返回 `async_launched` 状态:
|
||||||
|
|
||||||
```
|
```
|
||||||
registerAsyncAgent(agentId, ...) ← 注册到 AppState.tasks
|
registerAsyncAgent(agentId, ...) ← 注册到 AppState.tasks
|
||||||
↓ (void — 火后不管)
|
↓ (void — 火后不管)
|
||||||
runAsyncAgentLifecycle() ← 后台执行
|
runAsyncAgentLifecycle() ← 后台执行(agentToolUtils.ts)
|
||||||
├── runAgent().onCacheSafeParams ← 进度摘要初始化
|
├── runAgent().onCacheSafeParams ← 进度摘要初始化
|
||||||
├── 消息流迭代
|
├── 消息流迭代
|
||||||
├── completeAsyncAgent() ← 标记完成
|
├── finalizeAgentTool() ← 结果汇总(提取文本 + usage 统计)
|
||||||
├── classifyHandoffIfNeeded() ← 安全检查
|
├── completeAsyncAgent() ← 标记完成(先于通知,确保 TaskOutput 尽快解除阻塞)
|
||||||
|
├── classifyHandoffIfNeeded() ← 安全分类(需 TRANSCRIPT_CLASSIFIER feature)
|
||||||
|
├── getWorktreeResult() ← Worktree 清理(如有隔离)
|
||||||
└── enqueueAgentNotification() ← 通知主 Agent
|
└── enqueueAgentNotification() ← 通知主 Agent
|
||||||
```
|
```
|
||||||
|
|
||||||
|
如果异步 Agent 提供了 `name` 参数,还会注册到 `agentNameRegistry`,支撑 `SendMessage` 工具通过名称路由到该 Agent。
|
||||||
|
|
||||||
异步 Agent 获得独立的 `AbortController`,不与父 Agent 共享——用户按 ESC 取消主线程不会杀掉后台 Agent。
|
异步 Agent 获得独立的 `AbortController`,不与父 Agent 共享——用户按 ESC 取消主线程不会杀掉后台 Agent。
|
||||||
|
|
||||||
### 同步 Agent(前台运行)
|
### 同步 Agent(前台运行)
|
||||||
|
|
||||||
同步 Agent 的关键特性是 **可后台化**(`AgentTool.tsx:818-833`):
|
同步 Agent 的关键特性是 **可后台化**(`AgentTool.tsx:1107`):
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const registration = registerAgentForeground({
|
const registration = registerAgentForeground({
|
||||||
@@ -154,27 +205,27 @@ const raceResult = await Promise.race([
|
|||||||
|
|
||||||
## 结果回传格式
|
## 结果回传格式
|
||||||
|
|
||||||
`mapToolResultToToolResultBlockParam()` 根据状态返回不同格式(`AgentTool.tsx:1298-1375`):
|
`mapToolResultToToolResultBlockParam()` 根据状态返回不同格式:
|
||||||
|
|
||||||
| 状态 | 返回内容 |
|
| 状态 | 返回内容 |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| `completed` | 内容 + `<usage>` 块(token/tool_calls/duration) |
|
| `completed` | 内容 + `<usage>` 块(token/tool_calls/duration);无内容时插入占位文本 `"(Subagent completed but returned no output.)"` 防止模型误判为空 |
|
||||||
| `async_launched` | agentId + outputFile 路径 + 操作指引 |
|
| `async_launched` | agentId + outputFile 路径 + 操作指引(指引内容取决于 `canReadOutputFile`:有读取权限时提示通过 Read/Bash 查看进度,否则仅告知已启动) |
|
||||||
| `teammate_spawned` | agent_id + name + team_name |
|
| `teammate_spawned` | agent_id + name + team_name |
|
||||||
| `remote_launched` | taskId + sessionUrl + outputFile |
|
| `remote_launched` | taskId + sessionUrl + outputFile |
|
||||||
|
|
||||||
对于一次性内置 Agent(Explore、Plan),`<usage>` 块被省略——每周节省约 1-2 Gtok 的上下文窗口。
|
对于一次性内置 Agent(Explore、Plan),当**不存在** worktree 隔离时,`<usage>` 块和 agentId 尾部被省略——每周节省约 1-2 Gtok 的上下文窗口。存在 worktree 时仍需返回 `worktreePath` 和 `worktreeBranch` 信息。
|
||||||
|
|
||||||
## MCP 依赖的等待机制
|
## MCP 依赖的等待机制
|
||||||
|
|
||||||
如果 Agent 声明了 `requiredMcpServers`,`call()` 会等待这些服务器连接完成(`AgentTool.tsx:371-410`):
|
如果 Agent 声明了 `requiredMcpServers`,`call()` 会等待这些服务器连接完成(`AgentTool.tsx:576`):
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const MAX_WAIT_MS = 30_000 // 最长等 30 秒
|
const MAX_WAIT_MS = 30_000 // 最长等 30 秒
|
||||||
const POLL_INTERVAL_MS = 500 // 每 500ms 轮询
|
const POLL_INTERVAL_MS = 500 // 每 500ms 轮询
|
||||||
```
|
```
|
||||||
|
|
||||||
早期退出条件:任何必需服务器进入 `failed` 状态时立即停止等待。工具可用性通过 `mcp__` 前缀工具名解析(`mcp__serverName__toolName`)判断。
|
早期退出条件:任何必需服务器进入 `failed` 状态时立即停止等待。工具可用性通过 `mcp__` 前缀工具名解析(`mcp__serverName__toolName`)判断。等待结束后如果仍有必需服务器未就绪,`call()` 会抛出错误并明确列出缺失的服务器名称。
|
||||||
|
|
||||||
## 适用场景
|
## 适用场景
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ Worktree 文件统一存放在仓库根目录下的 `.claude/worktrees/`:
|
|||||||
|
|
||||||
## 创建流程:EnterWorktreeTool
|
## 创建流程:EnterWorktreeTool
|
||||||
|
|
||||||
`EnterWorktreeTool`(`src/tools/EnterWorktreeTool/EnterWorktreeTool.ts`)的执行链路:
|
`EnterWorktreeTool`(`packages/builtin-tools/src/tools/EnterWorktreeTool/EnterWorktreeTool.ts`)的执行链路:
|
||||||
|
|
||||||
```
|
```
|
||||||
EnterWorktreeTool.call({ name? })
|
EnterWorktreeTool.call({ name? })
|
||||||
@@ -83,7 +83,7 @@ EnterWorktreeTool.call({ name? })
|
|||||||
|
|
||||||
## 退出流程:ExitWorktreeTool
|
## 退出流程:ExitWorktreeTool
|
||||||
|
|
||||||
`ExitWorktreeTool`(`src/tools/ExitWorktreeTool/ExitWorktreeTool.ts`)支持两种退出策略:
|
`ExitWorktreeTool`(`packages/builtin-tools/src/tools/ExitWorktreeTool/ExitWorktreeTool.ts`)支持两种退出策略:
|
||||||
|
|
||||||
### keep:保留 worktree
|
### keep:保留 worktree
|
||||||
|
|
||||||
@@ -143,14 +143,18 @@ call() — 实际执行
|
|||||||
|
|
||||||
## 与 Agent 工具的联动
|
## 与 Agent 工具的联动
|
||||||
|
|
||||||
Agent 工具(`AgentTool`)的 `isolation` 参数决定子 Agent 是否在 worktree 中运行:
|
Agent 工具(`AgentTool`)的 `isolation` 参数决定子 Agent 是否在 worktree 中运行。注意 Agent 工具使用**专用的** `createAgentWorktree()`(`src/utils/worktree.ts`),而非用户会话用的 `createWorktreeForSession()`,两者有关键差异:
|
||||||
|
|
||||||
- `isolation: "worktree"` → 调用 `createWorktreeForSession()`,子 Agent 在独立 worktree 中执行
|
| 维度 | `createWorktreeForSession`(用户会话) | `createAgentWorktree`(子 Agent) |
|
||||||
- 无 isolation → 子 Agent 共享主工作目录
|
|------|---------------------------------------|----------------------------------|
|
||||||
|
| 调用者 | EnterWorktreeTool | AgentTool |
|
||||||
|
| Session 管理 | 设置 `currentWorktreeSession` | **不设置** `currentWorktreeSession` |
|
||||||
|
| 恢复已有 worktree | 直接复用 | 复用并 bump mtime(防止被周期性清理误删) |
|
||||||
|
|
||||||
子 Agent 结束时的处理:
|
子 Agent 结束时的处理由 `cleanupWorktreeIfNeeded()` 自动完成——它不走 `ExitWorktreeTool`(因为 Agent worktree 没有会话状态,`ExitWorktreeTool` 的 `validateInput` 会拒绝):
|
||||||
- **成功**:主 Agent 通过 `ExitWorktreeTool(action: "keep")` 保留 worktree,然后手动合并
|
- **有变更** → 保留 worktree,返回 `worktreePath` 供主 Agent 后续合并
|
||||||
- **失败/放弃**:主 Agent 通过 `ExitWorktreeTool(action: "remove", discard_changes: true)` 清理
|
- **无变更** → 自动删除
|
||||||
|
- **Hook-based** → 始终保留
|
||||||
|
|
||||||
## Session 状态持久化
|
## Session 状态持久化
|
||||||
|
|
||||||
@@ -168,6 +172,7 @@ Agent 工具(`AgentTool`)的 `isolation` 参数决定子 Agent 是否在 wor
|
|||||||
tmuxSessionName?: string, // 关联的 tmux session
|
tmuxSessionName?: string, // 关联的 tmux session
|
||||||
hookBased?: boolean, // 是否由 hook 创建
|
hookBased?: boolean, // 是否由 hook 创建
|
||||||
creationDurationMs?: number, // 创建耗时(分析用)
|
creationDurationMs?: number, // 创建耗时(分析用)
|
||||||
|
usedSparsePaths?: boolean, // 是否使用了 sparse checkout
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ useInterval(checkForUpdates, 30 * 60 * 1000); // 每 30 分钟
|
|||||||
|
|
||||||
任何更新尝试之前,系统会依次检查:
|
任何更新尝试之前,系统会依次检查:
|
||||||
|
|
||||||
1. **自动更新是否被禁用?** — `getAutoUpdaterDisabledReason()`(`src/utils/config.ts:1735`)
|
1. **自动更新是否被禁用?** — `getAutoUpdaterDisabledReason()`(`src/utils/config.ts:1737`)
|
||||||
- `NODE_ENV === 'development'`
|
- `NODE_ENV === 'development'`
|
||||||
- 设置了 `DISABLE_AUTOUPDATER` 环境变量
|
- 设置了 `DISABLE_AUTOUPDATER` 环境变量
|
||||||
- 仅限必要流量模式
|
- 仅限必要流量模式
|
||||||
@@ -81,7 +81,7 @@ useInterval(checkForUpdates, 30 * 60 * 1000); // 每 30 分钟
|
|||||||
|
|
||||||
`src/utils/autoUpdater.ts:70` — `assertMinVersion()`
|
`src/utils/autoUpdater.ts:70` — `assertMinVersion()`
|
||||||
|
|
||||||
从 `src/main.tsx:1775` 在启动时调用:
|
定义于 `src/utils/autoUpdater.ts:70`,设计上在启动时调用(当前未接入启动流程):
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
void assertMinVersion();
|
void assertMinVersion();
|
||||||
@@ -200,7 +200,7 @@ Windows 系统使用文件复制而非符号链接。
|
|||||||
|
|
||||||
**文件**: `src/migrations/migrateAutoUpdatesToSettings.ts`
|
**文件**: `src/migrations/migrateAutoUpdatesToSettings.ts`
|
||||||
|
|
||||||
一次性将旧版 `globalConfig.autoUpdates = false` 迁移为 settings 中的 `DISABLE_AUTOUPDATER=1` 环境变量。从 `src/main.tsx:325` 在启动时调用。
|
一次性将旧版 `globalConfig.autoUpdates = false` 迁移为 settings 中的 `DISABLE_AUTOUPDATER=1` 环境变量。定义于 `src/migrations/migrateAutoUpdatesToSettings.ts`(当前未接入启动流程)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -270,7 +270,7 @@ React hook `useUpdateNotification(updatedVersion)` — 确保每次 semver 变
|
|||||||
| `src/utils/releaseNotes.ts` | Changelog 获取、缓存与展示 |
|
| `src/utils/releaseNotes.ts` | Changelog 获取、缓存与展示 |
|
||||||
| `src/utils/semver.ts` | Semver 版本比较(Bun 原生 + npm 回退) |
|
| `src/utils/semver.ts` | Semver 版本比较(Bun 原生 + npm 回退) |
|
||||||
| `src/utils/doctorDiagnostic.ts` | 安装类型检测与健康诊断 |
|
| `src/utils/doctorDiagnostic.ts` | 安装类型检测与健康诊断 |
|
||||||
| `src/utils/config.ts:1735` | `getAutoUpdaterDisabledReason()` — 禁用检查逻辑 |
|
| `src/utils/config.ts:1737` | `getAutoUpdaterDisabledReason()` — 禁用检查逻辑 |
|
||||||
| `src/migrations/migrateAutoUpdatesToSettings.ts` | 旧版配置迁移 |
|
| `src/migrations/migrateAutoUpdatesToSettings.ts` | 旧版配置迁移 |
|
||||||
| `src/screens/Doctor.tsx` | Doctor 命令 UI,展示自动更新状态 |
|
| `src/screens/Doctor.tsx` | Doctor 命令 UI,展示自动更新状态 |
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const messagesForCompact = microcompactResult.messages
|
|||||||
MicroCompact 不压缩整个对话,而是**清除旧工具输出的内容**。它维护一个白名单:
|
MicroCompact 不压缩整个对话,而是**清除旧工具输出的内容**。它维护一个白名单:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// src/services/compact/microCompact.ts:41-48
|
// src/services/compact/microCompact.ts:41-50
|
||||||
const COMPACTABLE_TOOLS = new Set([
|
const COMPACTABLE_TOOLS = new Set([
|
||||||
FILE_READ_TOOL_NAME, // 'Read' - 文件读取
|
FILE_READ_TOOL_NAME, // 'Read' - 文件读取
|
||||||
...SHELL_TOOL_NAMES, // 'Bash' - 命令输出
|
...SHELL_TOOL_NAMES, // 'Bash' - 命令输出
|
||||||
@@ -143,7 +143,7 @@ const stripped2 = stripReinjectedAttachments(stripped) // 移除会被重新注
|
|||||||
压缩后,系统会从摘要中**重新注入关键上下文**:
|
压缩后,系统会从摘要中**重新注入关键上下文**:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// compact.ts:124-132
|
// compact.ts:126-134
|
||||||
export const POST_COMPACT_TOKEN_BUDGET = 50_000 // 总预算
|
export const POST_COMPACT_TOKEN_BUDGET = 50_000 // 总预算
|
||||||
export const POST_COMPACT_MAX_FILES_TO_RESTORE = 5 // 最多恢复 5 个文件
|
export const POST_COMPACT_MAX_FILES_TO_RESTORE = 5 // 最多恢复 5 个文件
|
||||||
export const POST_COMPACT_MAX_TOKENS_PER_FILE = 5_000 // 每文件 5K token
|
export const POST_COMPACT_MAX_TOKENS_PER_FILE = 5_000 // 每文件 5K token
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ Claude Code 的记忆系统是**纯文件**的——没有数据库、没有向
|
|||||||
`MEMORY.md` 是记忆的入口索引,每次对话都完整加载到上下文中:
|
`MEMORY.md` 是记忆的入口索引,每次对话都完整加载到上下文中:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// memdir.ts:35-38
|
// memdir.ts:34-38
|
||||||
export const ENTRYPOINT_NAME = 'MEMORY.md'
|
export const ENTRYPOINT_NAME = 'MEMORY.md'
|
||||||
export const MAX_ENTRYPOINT_LINES = 200
|
export const MAX_ENTRYPOINT_LINES = 200
|
||||||
export const MAX_ENTRYPOINT_BYTES = 25_000
|
export const MAX_ENTRYPOINT_BYTES = 25_000
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ buildSystemPromptBlocks() → TextBlockParam[] (分块 + cache_control 标
|
|||||||
|
|
||||||
1. **`getSystemPrompt()`**(`src/constants/prompts.ts:444`)—— 收集静态段 + 动态段,插入 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` 分界标记
|
1. **`getSystemPrompt()`**(`src/constants/prompts.ts:444`)—— 收集静态段 + 动态段,插入 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` 分界标记
|
||||||
2. **`buildEffectiveSystemPrompt()`**(`src/utils/systemPrompt.ts:41`)—— 按 Override > Coordinator > Agent > Custom > Default 优先级选择
|
2. **`buildEffectiveSystemPrompt()`**(`src/utils/systemPrompt.ts:41`)—— 按 Override > Coordinator > Agent > Custom > Default 优先级选择
|
||||||
3. **`buildSystemPromptBlocks()`**(`src/services/api/claude.ts:3214`)—— 调用 `splitSysPromptPrefix()` 分块,为每个块附加 `cache_control`
|
3. **`buildSystemPromptBlocks()`**(`src/services/api/claude.ts:3279`)—— 调用 `splitSysPromptPrefix()` 分块,为每个块附加 `cache_control`
|
||||||
|
|
||||||
## SystemPrompt 品牌类型
|
## SystemPrompt 品牌类型
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// src/utils/systemPromptType.ts:8
|
// packages/@ant/model-provider/src/types/systemPrompt.ts:4
|
||||||
export type SystemPrompt = readonly string[] & {
|
export type SystemPrompt = readonly string[] & {
|
||||||
readonly __brand: 'SystemPrompt'
|
readonly __brand: 'SystemPrompt'
|
||||||
}
|
}
|
||||||
@@ -185,7 +185,7 @@ export function shouldUseGlobalCacheScope(): boolean {
|
|||||||
|
|
||||||
### `getCacheControl()`:TTL 决策
|
### `getCacheControl()`:TTL 决策
|
||||||
|
|
||||||
`src/services/api/claude.ts:359` 生成的 `cache_control` 对象:
|
`src/services/api/claude.ts:348` 生成的 `cache_control` 对象:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
{
|
{
|
||||||
@@ -195,14 +195,14 @@ export function shouldUseGlobalCacheScope(): boolean {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
1 小时 TTL 的判定逻辑(`should1hCacheTTL()`,第 394 行):
|
1 小时 TTL 的判定逻辑(`should1hCacheTTL()`,第 383 行):
|
||||||
- **Bedrock 用户**:通过环境变量 `ENABLE_PROMPT_CACHING_1H_BEDROCK` 启用
|
- **Bedrock 用户**:通过环境变量 `ENABLE_PROMPT_CACHING_1H_BEDROCK` 启用
|
||||||
- **1P 用户**:通过 GrowthBook 配置的 `allowlist` 数组匹配 `querySource`,支持前缀通配符(如 `"repl_main_thread*"`)
|
- **1P 用户**:通过 GrowthBook 配置的 `allowlist` 数组匹配 `querySource`,支持前缀通配符(如 `"repl_main_thread*"`)
|
||||||
- **会话级锁定**:资格判定结果在 bootstrap state 中缓存,防止 GrowthBook 配置中途变化导致同一会话内 TTL 不一致
|
- **会话级锁定**:资格判定结果在 bootstrap state 中缓存,防止 GrowthBook 配置中途变化导致同一会话内 TTL 不一致
|
||||||
|
|
||||||
### 缓存破坏:Session-Specific Guidance 的放置
|
### 缓存破坏:Session-Specific Guidance 的放置
|
||||||
|
|
||||||
`getSessionSpecificGuidanceSection()`(`src/constants/prompts.ts:352`)的内容必须放在 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` **之后**。因为它包含:
|
`getSessionSpecificGuidanceSection()`(`src/constants/prompts.ts:354`)的内容必须放在 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` **之后**。因为它包含:
|
||||||
- 当前会话的 enabledTools 集合
|
- 当前会话的 enabledTools 集合
|
||||||
- `isForkSubagentEnabled()` 的运行时判定
|
- `isForkSubagentEnabled()` 的运行时判定
|
||||||
- `getIsNonInteractiveSession()` 的结果
|
- `getIsNonInteractiveSession()` 的结果
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ message_stop ← 消息结束
|
|||||||
|
|
||||||
### 事件处理状态机
|
### 事件处理状态机
|
||||||
|
|
||||||
`src/services/api/claude.ts` 中 `queryStreamRaw()` 函数的事件处理循环实现了一个基于 `switch(part.type)` 的状态机:
|
`src/services/api/claude.ts` 中 `queryModelWithStreaming()` 函数的事件处理循环实现了一个基于 `switch(part.type)` 的状态机:
|
||||||
|
|
||||||
| 事件类型 | 处理逻辑 | 状态变更 |
|
| 事件类型 | 处理逻辑 | 状态变更 |
|
||||||
|----------|----------|----------|
|
|----------|----------|----------|
|
||||||
@@ -167,10 +167,13 @@ UI 层通过 `useToolCallProgress` hook 实时展示命令输出,而不是等
|
|||||||
|
|
||||||
| Provider | 流式协议 | 特殊处理 |
|
| Provider | 流式协议 | 特殊处理 |
|
||||||
|----------|----------|----------|
|
|----------|----------|----------|
|
||||||
| **Anthropic Direct** | 原生 SSE | 延迟最低,TTFT 最快 |
|
| **firstParty** (Anthropic Direct) | 原生 SSE | 延迟最低,TTFT 最快 |
|
||||||
| **AWS Bedrock** | AWS SDK 流式接口 | 需要额外的 beta header 和认证 |
|
| **AWS Bedrock** | AWS SDK 流式接口 | 需要额外的 beta header 和认证 |
|
||||||
| **Google Vertex** | gRPC → 事件流 | 通过 `getMergedBetas()` 适配 |
|
| **Google Vertex** | gRPC → 事件流 | 通过 `getMergedBetas()` 适配 |
|
||||||
| **Azure** | Anthropic 兼容 API | 自定义 base URL |
|
| **foundry** | Anthropic 兼容 API | 内部部署 |
|
||||||
|
| **openai** | OpenAI 流式适配器 | 转换为 Anthropic 内部格式 |
|
||||||
|
| **gemini** | Gemini 流式适配器 | 转换为 Anthropic 内部格式 |
|
||||||
|
| **grok** (xAI) | Grok 流式适配器 | 转换为 Anthropic 内部格式 |
|
||||||
|
|
||||||
所有 Provider 通过统一的 `Stream<BetaRawMessageStreamEvent>` 抽象层屏蔽差异。上层代码(QueryEngine、REPL)不需要关心底层用的是哪个 Provider。
|
所有 Provider 通过统一的 `Stream<BetaRawMessageStreamEvent>` 抽象层屏蔽差异。上层代码(QueryEngine、REPL)不需要关心底层用的是哪个 Provider。
|
||||||
|
|
||||||
|
|||||||
@@ -74,17 +74,17 @@ const toolUpdates = streamingToolExecutor
|
|||||||
|
|
||||||
| 终止原因 | 触发位置 | 机制 |
|
| 终止原因 | 触发位置 | 机制 |
|
||||||
|----------|---------|------|
|
|----------|---------|------|
|
||||||
| **blocking_limit** | 第 646 行 | Token 计数超过硬限制(非 autocompact 模式)→ 生成 PTL 错误消息 → 返回 |
|
| **blocking_limit** | 第 686 行 | Token 计数超过硬限制(非 autocompact 模式)→ 生成 PTL 错误消息 → 返回 |
|
||||||
| **image_error** | 第 980 行 | `ImageSizeError` / `ImageResizeError` 异常 → 直接返回 |
|
| **image_error** | 第 1021 行 | `ImageSizeError` / `ImageResizeError` 异常 → 直接返回 |
|
||||||
| **model_error** | 第 999 行 | `callModel()` 抛出不可恢复异常 → 生成错误消息 → 返回 |
|
| **model_error** | 第 1040 行 | `callModel()` 抛出不可恢复异常 → 生成错误消息 → 返回 |
|
||||||
| **aborted_streaming** | 第 1054 行 | `abortController.signal.aborted`(流式阶段)→ 为未完成的 tool_use 生成合成 tool_result → 返回 |
|
| **aborted_streaming** | 第 1095 行 | `abortController.signal.aborted`(流式阶段)→ 为未完成的 tool_use 生成合成 tool_result → 返回 |
|
||||||
| **prompt_too_long** | 第 1178/1185 行 | 413 错误且 reactive compact 无法恢复 → 暂扣的错误消息被释放 → 返回 |
|
| **prompt_too_long** | 第 1219/1226 行 | 413 错误且 reactive compact 无法恢复 → 暂扣的错误消息被释放 → 返回 |
|
||||||
| **completed** | 第 1267 行 | API 错误(限流、认证失败等)导致无法继续 → 返回 |
|
| **completed** | 第 1308 行 | API 错误(限流、认证失败等)导致无法继续 → 返回 |
|
||||||
| **stop_hook_prevented** | 第 1282 行 | Stop hook 返回 `preventContinuation: true` → 返回 |
|
| **stop_hook_prevented** | 第 1323 行 | Stop hook 返回 `preventContinuation: true` → 返回 |
|
||||||
| **completed** | 第 1360 行 | 正常完成:AI 未发出 tool_use → `needsFollowUp = false` → 经过 stop hooks → 返回 |
|
| **completed** | 第 1401 行 | 正常完成:AI 未发出 tool_use → `needsFollowUp = false` → 经过 stop hooks → 返回 |
|
||||||
| **aborted_tools** | 第 1518 行 | `abortController.signal.aborted`(工具执行阶段)→ 返回 |
|
| **aborted_tools** | 第 1559 行 | `abortController.signal.aborted`(工具执行阶段)→ 返回 |
|
||||||
| **hook_stopped** | 第 1523 行 | 工具执行期间 hook 返回 `shouldPreventContinuation` → 返回 |
|
| **hook_stopped** | 第 1564 行 | 工具执行期间 hook 返回 `shouldPreventContinuation` → 返回 |
|
||||||
| **max_turns** | 第 1714 行 | 轮次计数超过 `maxTurns` 限制 → 返回 |
|
| **max_turns** | 第 1755 行 | 轮次计数超过 `maxTurns` 限制 → 返回 |
|
||||||
|
|
||||||
## 继续条件(恢复路径)
|
## 继续条件(恢复路径)
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ type State = {
|
|||||||
- **每一步都产生真实信息**:`runTools()` 返回的 `toolResults` 是 API 不可能预知的——命令输出、文件内容、错误信息
|
- **每一步都产生真实信息**:`runTools()` 返回的 `toolResults` 是 API 不可能预知的——命令输出、文件内容、错误信息
|
||||||
- **动态上下文管理**:每轮迭代前都重新评估压缩需求(autocompact → microcompact → snip),基于最新的 token 计数
|
- **动态上下文管理**:每轮迭代前都重新评估压缩需求(autocompact → microcompact → snip),基于最新的 token 计数
|
||||||
- **错误即时恢复**:工具失败不需要推倒重来——stop hook 可以注入阻塞错误让 AI 修正策略
|
- **错误即时恢复**:工具失败不需要推倒重来——stop hook 可以注入阻塞错误让 AI 修正策略
|
||||||
- **用户可控**:`abortController.signal` 在循环的多个检查点被检测(第 1018、1048、1488 行),用户按 ESC 可以优雅中断
|
- **用户可控**:`abortController.signal` 在循环的多个检查点被检测(第 1059、1095、1529 行),用户按 ESC 可以优雅中断
|
||||||
- **成本控制**:Token Budget 在每轮终止前检查,防止 AI 无效循环
|
- **成本控制**:Token Budget 在每轮终止前检查,防止 AI 无效循环
|
||||||
|
|
||||||
## 一个完整的迭代示例
|
## 一个完整的迭代示例
|
||||||
|
|||||||
17
docs/diagrams/agent-loop-simple.mmd
Normal file
17
docs/diagrams/agent-loop-simple.mmd
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
flowchart TB
|
||||||
|
START((输入)) --> CTX["Context 管理"]
|
||||||
|
CTX --> LLM["LLM 流式输出"]
|
||||||
|
LLM --> TC{tool_use?}
|
||||||
|
|
||||||
|
TC --> |是| EXEC["执行工具"]
|
||||||
|
EXEC --> CTX
|
||||||
|
|
||||||
|
TC --> |否| DONE((完成))
|
||||||
|
|
||||||
|
classDef proc fill:#eef,stroke:#66c,color:#224
|
||||||
|
classDef decision fill:#fee,stroke:#c66,color:#422
|
||||||
|
classDef io fill:#eff,stroke:#6cc,color:#244
|
||||||
|
|
||||||
|
class CTX,LLM,EXEC proc
|
||||||
|
class TC decision
|
||||||
|
class START,DONE io
|
||||||
40
docs/diagrams/agent-loop.mmd
Normal file
40
docs/diagrams/agent-loop.mmd
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
flowchart TB
|
||||||
|
START((输入)) --> CTX["Context 管理"]
|
||||||
|
CTX --> PRE["Pre-sampling Hook"]
|
||||||
|
PRE --> LLM["LLM 流式输出"]
|
||||||
|
LLM --> TC{tool_use?}
|
||||||
|
|
||||||
|
TC --> |是| PERM{需权限?}
|
||||||
|
PERM --> |是| USER["👤 用户审批"]
|
||||||
|
USER --> |allow| TOOL_PRE
|
||||||
|
USER --> |deny| DENIED["拒绝"]
|
||||||
|
PERM --> |否| TOOL_PRE["Pre-tool Hook"]
|
||||||
|
TOOL_PRE --> EXEC["并发执行工具"]
|
||||||
|
EXEC --> TOOL_POST["Post-tool Hook"]
|
||||||
|
TOOL_POST --> CTX
|
||||||
|
DENIED --> CTX
|
||||||
|
|
||||||
|
TC --> |否| POST["Post-sampling Hook"]
|
||||||
|
POST --> STOP{"Stop Hook"}
|
||||||
|
STOP --> |不通过| CTX
|
||||||
|
STOP --> |通过| BUDGET{"Token Budget"}
|
||||||
|
BUDGET --> |继续| CTX
|
||||||
|
BUDGET --> |完成| DONE((完成))
|
||||||
|
|
||||||
|
subgraph SUB["子 Agent"]
|
||||||
|
FORK["AgentTool"] --> RECURSE["递归调用"]
|
||||||
|
end
|
||||||
|
|
||||||
|
EXEC -.-> FORK
|
||||||
|
|
||||||
|
classDef proc fill:#eef,stroke:#66c,color:#224
|
||||||
|
classDef decision fill:#fee,stroke:#c66,color:#422
|
||||||
|
classDef hook fill:#ffe,stroke:#cc6,color:#442
|
||||||
|
classDef io fill:#eff,stroke:#6cc,color:#244
|
||||||
|
classDef sub fill:#efe,stroke:#6a6,color:#242
|
||||||
|
|
||||||
|
class CTX,LLM,EXEC proc
|
||||||
|
class TC,PERM,STOP,BUDGET decision
|
||||||
|
class PRE,TOOL_PRE,TOOL_POST,POST hook
|
||||||
|
class START,DONE,USER,DENIED io
|
||||||
|
class FORK,RECURSE sub
|
||||||
@@ -12,7 +12,7 @@ Claude Code 的 Agent 不仅仅来自用户自定义——系统有三类来源
|
|||||||
|
|
||||||
| 来源 | 位置 | 优先级 |
|
| 来源 | 位置 | 优先级 |
|
||||||
|------|------|--------|
|
|------|------|--------|
|
||||||
| **Built-in** | `src/tools/AgentTool/built-in/` 硬编码 | 最低(可被覆盖) |
|
| **Built-in** | `packages/builtin-tools/src/tools/AgentTool/built-in/` 硬编码 | 最低(可被覆盖) |
|
||||||
| **Plugin** | 通过插件系统注册 | 中 |
|
| **Plugin** | 通过插件系统注册 | 中 |
|
||||||
| **User/Project/Policy** | `.claude/agents/*.md` 或 settings.json | 最高 |
|
| **User/Project/Policy** | `.claude/agents/*.md` 或 settings.json | 最高 |
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ color: "blue" # 终端中的 Agent 颜色标识
|
|||||||
以内置 Explore Agent 为例:
|
以内置 Explore Agent 为例:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// src/tools/AgentTool/built-in/exploreAgent.ts
|
// packages/builtin-tools/src/tools/AgentTool/built-in/exploreAgent.ts
|
||||||
disallowedTools: [
|
disallowedTools: [
|
||||||
'Agent', // 不能嵌套调用 Agent
|
'Agent', // 不能嵌套调用 Agent
|
||||||
'ExitPlanMode', // 不需要 plan mode
|
'ExitPlanMode', // 不需要 plan mode
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ SDK 非交互模式下信任是隐式的(`getIsNonInteractiveSession()` 为 tr
|
|||||||
|
|
||||||
## Session Hook 的生命周期
|
## Session Hook 的生命周期
|
||||||
|
|
||||||
Agent 和 Skill 的前置 Hook 通过 `registerFrontmatterHooks()` 注册(调用位置:`src/tools/AgentTool/runAgent.ts`;定义位置:`src/utils/hooks/registerFrontmatterHooks.ts`),绑定到 agent 的 session ID。Agent 结束时通过 `clearSessionHooks()`(定义位置:`src/utils/hooks/sessionHooks.ts`)清理。
|
Agent 和 Skill 的前置 Hook 通过 `registerFrontmatterHooks()` 注册(调用位置:`packages/builtin-tools/src/tools/AgentTool/runAgent.ts`;定义位置:`src/utils/hooks/registerFrontmatterHooks.ts`),绑定到 agent 的 session ID。Agent 结束时通过 `clearSessionHooks()`(定义位置:`src/utils/hooks/sessionHooks.ts`)清理。
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// runAgent.ts — 注册 agent 的前置 Hook
|
// runAgent.ts — 注册 agent 的前置 Hook
|
||||||
|
|||||||
346
docs/extensibility/mcp-configuration.mdx
Normal file
346
docs/extensibility/mcp-configuration.mdx
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
---
|
||||||
|
title: "MCP 配置 - 多来源合并、作用域与策略管控"
|
||||||
|
description: "详细说明 Claude Code MCP 配置的来源层次、合并优先级、传输类型、企业策略管控、插件集成和保留名称机制。"
|
||||||
|
keywords: ["MCP", "配置", "settings.json", ".mcp.json", "企业策略", "插件"]
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置来源与作用域
|
||||||
|
|
||||||
|
Claude Code 的 MCP 配置来自多个来源,每个来源对应一个 `scope`(作用域)。配置按优先级合并,高优先级来源的同名配置覆盖低优先级。
|
||||||
|
|
||||||
|
### 来源列表
|
||||||
|
|
||||||
|
| 来源 | Scope | 文件/接口 | 说明 |
|
||||||
|
|------|-------|----------|------|
|
||||||
|
| 企业管控 | `enterprise` | 系统管理路径 `managed-mcp.json` | **排他模式**:存在时忽略所有其他来源 |
|
||||||
|
| 本地项目 | `local` | `<project>/.claude/settings.local.json` | 项目级私有配置(不提交到 VCS) |
|
||||||
|
| 项目配置 | `project` | `<project>/.mcp.json` | 项目级共享配置(可提交到 VCS) |
|
||||||
|
| 用户全局 | `user` | `~/.claude/settings.json` | 用户级配置,所有项目共享 |
|
||||||
|
| 插件 | `dynamic` | 插件 manifest 中 `.mcp.json` / `.mcpb` | 插件提供的 MCP 服务器 |
|
||||||
|
| claude.ai | `claudeai` | 通过 API 获取 | claude.ai 网页端配置的连接器 |
|
||||||
|
| 内置动态 | `dynamic` | 代码中注册 | Computer Use / Chrome 等内置服务器 |
|
||||||
|
| IDE SDK | `sdk` | IDE 传入 | VS Code / JetBrains 嵌入模式 |
|
||||||
|
|
||||||
|
### 合并优先级(从低到高)
|
||||||
|
|
||||||
|
```
|
||||||
|
claude.ai 连接器 ← 最低优先级
|
||||||
|
↓ 去重
|
||||||
|
插件服务器
|
||||||
|
↓ 去重
|
||||||
|
用户全局配置
|
||||||
|
↓
|
||||||
|
项目配置(.mcp.json) ← 需要用户审批
|
||||||
|
↓
|
||||||
|
本地项目配置
|
||||||
|
↓
|
||||||
|
动态配置(内置 MCP) ← 最高优先级
|
||||||
|
```
|
||||||
|
|
||||||
|
`Object.assign({}, dedupedPluginServers, userServers, approvedProjectServers, localServers)` 实现合并——后出现的同名键覆盖前者。
|
||||||
|
|
||||||
|
## 企业管控模式
|
||||||
|
|
||||||
|
当 `managed-mcp.json` 文件存在时,进入 **排他模式**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// config.ts:1084
|
||||||
|
if (doesEnterpriseMcpConfigExist()) {
|
||||||
|
// 只返回企业配置,忽略所有用户/项目/插件/claude.ai 配置
|
||||||
|
return { servers: filtered, errors: [] }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
特性:
|
||||||
|
- 路径由系统管理决定(`getManagedFilePath()` + `managed-mcp.json`)
|
||||||
|
- 覆盖所有用户级、项目级、插件和 claude.ai 配置
|
||||||
|
- 仍然应用策略过滤(allowlist/denylist)
|
||||||
|
- 无法通过 CLI 添加新服务器(`addMcpConfig` 会拒绝)
|
||||||
|
|
||||||
|
## 传输类型与配置 Schema
|
||||||
|
|
||||||
|
### stdio(默认)
|
||||||
|
|
||||||
|
启动子进程,通过 stdin/stdout JSON-RPC 通信。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"my-server": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["-y", "@my-org/mcp-server"],
|
||||||
|
"env": { "API_KEY": "..." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`type` 字段可省略(默认为 `stdio`)。环境变量通过 `env` 传递给子进程,会与当前进程环境合并。
|
||||||
|
|
||||||
|
**Windows 注意**:使用 `npx` 需要包装为 `cmd /c npx`,否则会报错。
|
||||||
|
|
||||||
|
### SSE(Server-Sent Events)
|
||||||
|
|
||||||
|
通过 HTTP SSE 连接远程 MCP 服务器。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"my-remote": {
|
||||||
|
"type": "sse",
|
||||||
|
"url": "https://mcp.example.com/sse",
|
||||||
|
"headers": { "Authorization": "Bearer ..." },
|
||||||
|
"oauth": {
|
||||||
|
"clientId": "...",
|
||||||
|
"authServerMetadataUrl": "https://auth.example.com/.well-known/oauth-authorization-server"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
支持 OAuth 认证流程。认证失败时进入 `needs-auth` 状态,15 分钟 TTL 缓存避免重复提示。
|
||||||
|
|
||||||
|
### HTTP(Streamable HTTP)
|
||||||
|
|
||||||
|
HTTP 流式传输。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"my-http": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "https://mcp.example.com/mcp",
|
||||||
|
"headers": { "X-API-Key": "..." }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
支持与 SSE 相同的 OAuth 配置。
|
||||||
|
|
||||||
|
### WebSocket
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"my-ws": {
|
||||||
|
"type": "ws",
|
||||||
|
"url": "wss://mcp.example.com/ws"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### IDE 专用类型(内部)
|
||||||
|
|
||||||
|
`sse-ide` 和 `ws-ide` 是 IDE 扩展专用类型,不由用户直接配置。
|
||||||
|
|
||||||
|
- `sse-ide`:使用 lockfile token 认证
|
||||||
|
- `ws-ide`:使用 `X-Claude-Code-Ide-Authorization` header
|
||||||
|
|
||||||
|
### SDK 类型(内部)
|
||||||
|
|
||||||
|
`type: "sdk"` 由 IDE 嵌入模式传入,不经过保留名称检查和企业管控排他限制。
|
||||||
|
|
||||||
|
### claude.ai 代理类型(内部)
|
||||||
|
|
||||||
|
`type: "claudeai-proxy"` 由 claude.ai 网页端配置的连接器使用,通过 OAuth bearer token 认证并支持 401 重试。
|
||||||
|
|
||||||
|
## 配置操作
|
||||||
|
|
||||||
|
### 添加 MCP 服务器
|
||||||
|
|
||||||
|
通过 CLI 命令 `claude mcp add` 或 API 调用 `addMcpConfig()`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 添加到用户配置
|
||||||
|
claude mcp add my-server -s user -- npx @my-org/mcp-server
|
||||||
|
|
||||||
|
# 添加到项目配置
|
||||||
|
claude mcp add my-server -s project -- npx @my-org/mcp-server
|
||||||
|
|
||||||
|
# 添加 HTTP 类型
|
||||||
|
claude mcp add my-remote -s user -t http -u https://mcp.example.com/mcp
|
||||||
|
```
|
||||||
|
|
||||||
|
添加时的验证流程:
|
||||||
|
|
||||||
|
1. **名称校验**:只允许字母、数字、连字符和下划线
|
||||||
|
2. **保留名检查**:`claude-in-chrome` 和 `computer-use` 被保留
|
||||||
|
3. **企业管控检查**:企业模式下拒绝添加
|
||||||
|
4. **Schema 验证**:Zod 校验配置格式
|
||||||
|
5. **策略检查**:denylist 拒绝、allowlist 验证
|
||||||
|
|
||||||
|
### 移除 MCP 服务器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
claude mcp remove my-server -s user
|
||||||
|
```
|
||||||
|
|
||||||
|
### 列出 MCP 服务器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
claude mcp list
|
||||||
|
```
|
||||||
|
|
||||||
|
## 项目配置审批
|
||||||
|
|
||||||
|
`.mcp.json` 中的项目配置需要用户显式审批才能生效:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// config.ts:1166
|
||||||
|
const approvedProjectServers: Record<string, ScopedMcpServerConfig> = {}
|
||||||
|
for (const [name, config] of Object.entries(projectServers)) {
|
||||||
|
if (getProjectMcpServerStatus(name) === 'approved') {
|
||||||
|
approvedProjectServers[name] = config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
首次打开项目时,Claude Code 会提示用户审批 `.mcp.json` 中的每个服务器。审批状态持久化在本地配置中。
|
||||||
|
|
||||||
|
## 插件 MCP 集成
|
||||||
|
|
||||||
|
插件通过 manifest 中的 `.mcp.json` 或 `.mcpb` 文件声明 MCP 服务器:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 插件 MCP 加载流程
|
||||||
|
const pluginResult = await loadAllPluginsCacheOnly()
|
||||||
|
const pluginServerResults = await Promise.all(
|
||||||
|
pluginResult.enabled.map(plugin => getPluginMcpServers(plugin, mcpErrors))
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 插件命名空间
|
||||||
|
|
||||||
|
插件 MCP 服务器名格式为 `plugin:<pluginName>:<serverName>`,不会与手动配置的名称冲突。
|
||||||
|
|
||||||
|
### 去重机制
|
||||||
|
|
||||||
|
插件服务器通过内容签名去重(`dedupPluginMcpServers`):
|
||||||
|
|
||||||
|
- **stdio 类型**:签名 = `stdio:` + JSON.stringify([command, ...args])
|
||||||
|
- **URL 类型**:签名 = `url:` + 原始 URL(unwrap CCR proxy URL)
|
||||||
|
- **sdk 类型**:签名为 null,不去重
|
||||||
|
|
||||||
|
去重规则:
|
||||||
|
1. 手动配置优先于插件配置
|
||||||
|
2. 先加载的插件优先于后加载的
|
||||||
|
3. 被抑制的插件服务器在 `/plugin` UI 中显示提示
|
||||||
|
|
||||||
|
### claude.ai 连接器去重
|
||||||
|
|
||||||
|
claude.ai 连接器使用相同的内容签名机制去重(`dedupClaudeAiMcpServers`):
|
||||||
|
- 仅启用的手动配置参与去重(禁用的手动配置不应抑制连接器)
|
||||||
|
- 连接器名格式为 `claude.ai <DisplayName>`
|
||||||
|
|
||||||
|
## 策略管控
|
||||||
|
|
||||||
|
### Allowlist / Denylist
|
||||||
|
|
||||||
|
企业策略通过 allowlist 和 denylist 控制可用的 MCP 服务器:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// config.ts:1243 - 最终策略过滤
|
||||||
|
for (const [name, serverConfig] of Object.entries(configs)) {
|
||||||
|
if (!isMcpServerAllowedByPolicy(name, serverConfig)) {
|
||||||
|
continue // 跳过策略禁止的服务器
|
||||||
|
}
|
||||||
|
filtered[name] = serverConfig
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
策略检查考虑:
|
||||||
|
- 服务器名称匹配
|
||||||
|
- stdio 类型的 command + args 匹配
|
||||||
|
- URL 类型的 URL 模式匹配(支持通配符)
|
||||||
|
|
||||||
|
### 插件专用模式
|
||||||
|
|
||||||
|
`isRestrictedToPluginOnly('mcp')` 启用时,只允许插件提供的 MCP 服务器——用户/项目级配置被忽略。
|
||||||
|
|
||||||
|
## 环境变量展开
|
||||||
|
|
||||||
|
MCP 配置中的环境变量支持 `$VAR` 和 `${VAR}` 语法展开:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"my-server": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["@my-org/mcp-server"],
|
||||||
|
"env": {
|
||||||
|
"API_KEY": "$MY_API_KEY",
|
||||||
|
"DB_URL": "${DATABASE_URL}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
展开时缺失的变量会生成警告信息,但不阻止配置加载。
|
||||||
|
|
||||||
|
## 内置 MCP 动态注册
|
||||||
|
|
||||||
|
内置 MCP 服务器在 `main.tsx` 启动流程中动态注入配置:
|
||||||
|
|
||||||
|
### Computer Use MCP
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/utils/computerUse/setup.ts
|
||||||
|
export function setupComputerUseMCP(): {
|
||||||
|
mcpConfig: Record<string, ScopedMcpServerConfig>
|
||||||
|
allowedTools: string[]
|
||||||
|
} {
|
||||||
|
return {
|
||||||
|
mcpConfig: {
|
||||||
|
"computer-use": {
|
||||||
|
type: "stdio",
|
||||||
|
command: process.execPath,
|
||||||
|
args: ["--computer-use-mcp"],
|
||||||
|
scope: "dynamic",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
allowedTools: ["mcp__computer-use__screenshot", ...]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
启用条件:
|
||||||
|
- Feature flag `CHICAGO_MCP` 开启
|
||||||
|
- `getPlatform() !== "unknown"`(macOS/Windows/Linux)
|
||||||
|
- 非非交互式会话
|
||||||
|
- GrowthBook gate `getChicagoEnabled()` 返回 true
|
||||||
|
|
||||||
|
### Claude in Chrome MCP
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 类似 Computer Use,在 main.tsx 中注册
|
||||||
|
const { mcpConfig, allowedTools, systemPrompt } = setupClaudeInChrome()
|
||||||
|
dynamicMcpConfig = { ...dynamicMcpConfig, ...mcpConfig }
|
||||||
|
```
|
||||||
|
|
||||||
|
启用条件:
|
||||||
|
- `--chrome` 参数或 `claudeInChromeDefaultEnabled` 配置
|
||||||
|
- Chrome 扩展已安装
|
||||||
|
|
||||||
|
### VSCode SDK MCP
|
||||||
|
|
||||||
|
IDE 嵌入模式通过初始化消息传入 `type:'sdk'` 的配置,由 `setupVscodeSdkMcp()` 设置双向通知。
|
||||||
|
|
||||||
|
## 保留名称
|
||||||
|
|
||||||
|
以下 MCP 服务器名称被保留,用户无法手动配置同名服务器:
|
||||||
|
|
||||||
|
| 名称 | 用途 | 检查条件 |
|
||||||
|
|------|------|---------|
|
||||||
|
| `claude-in-chrome` | Chrome 浏览器控制 | 始终检查 |
|
||||||
|
| `computer-use` | 桌面自动化 | `CHICAGO_MCP` feature flag 开启时检查 |
|
||||||
|
| `claude-vscode` | VSCode IDE 集成 | 由 SDK 传入,不经过名称检查 |
|
||||||
|
|
||||||
|
保留名检查在两个位置:
|
||||||
|
1. `addMcpConfig()`(`config.ts:636-648`)— 运行时拒绝
|
||||||
|
2. `main.tsx` 启动检查(`main.tsx:2351-2368`)— 启动时退出
|
||||||
|
|
||||||
|
## 关键源文件索引
|
||||||
|
|
||||||
|
| 文件 | 职责 |
|
||||||
|
|------|------|
|
||||||
|
| `src/services/mcp/config.ts` | 配置管理核心:合并、去重、策略、添加/删除 |
|
||||||
|
| `src/services/mcp/types.ts` | Zod Schema 定义、类型声明 |
|
||||||
|
| `src/services/mcp/client.ts` | 连接管理、传输层选择 |
|
||||||
|
| `src/utils/plugins/mcpPluginIntegration.ts` | 插件 MCP 配置加载 |
|
||||||
|
| `src/utils/computerUse/setup.ts` | Computer Use 动态注册 |
|
||||||
|
| `src/utils/claudeInChrome/common.ts` | Chrome MCP 保留名与工具名 |
|
||||||
|
| `src/services/mcp/vscodeSdkMcp.ts` | VSCode SDK 双向通知 |
|
||||||
@@ -1,25 +1,32 @@
|
|||||||
---
|
---
|
||||||
title: "MCP 协议 - 连接管理、工具发现与执行链路"
|
title: "MCP 协议 - 连接管理、工具发现与执行链路"
|
||||||
description: "从源码角度解析 Claude Code 的 MCP 集成:7 种传输层实现、connectToServer 的 memoize 缓存、工具发现的 LRU 策略、认证状态机、以及 MCP 工具如何进入权限检查链路。"
|
description: "从源码角度解析 Claude Code 的 MCP 集成:内置 MCP 与外部 MCP 的区别、7 种传输层实现、connectToServer 的 memoize 缓存、工具发现的 LRU 策略、认证状态机、以及 MCP 工具如何进入权限检查链路。"
|
||||||
keywords: ["MCP", "Model Context Protocol", "工具扩展", "MCP 客户端", "工具发现"]
|
keywords: ["MCP", "Model Context Protocol", "工具扩展", "MCP 客户端", "工具发现", "内置 MCP", "外部 MCP"]
|
||||||
---
|
---
|
||||||
|
|
||||||
{/* 本章目标:从源码角度揭示 MCP 客户端的连接管理、工具发现协议和执行链路 */}
|
{/* 本章目标:从源码角度揭示 MCP 客户端的两种运行模式(内置/外部)、连接管理、工具发现协议和执行链路 */}
|
||||||
|
|
||||||
## 架构总览:从配置到可用工具
|
## 架构总览:从配置到可用工具
|
||||||
|
|
||||||
```
|
```
|
||||||
settings.json: { mcpServers: { "my-db": { command: "npx", args: [...] } } }
|
配置层(多来源合并)
|
||||||
|
├── settings.json: { mcpServers: { "my-db": { command: "npx", args: [...] } } } ← 外部
|
||||||
|
├── .mcp.json: 项目级 MCP 配置 ← 外部
|
||||||
|
├── 插件 manifest (.mcp.json / .mcpb) ← 外部(插件)
|
||||||
|
├── claude.ai connectors ← 外部(远程)
|
||||||
|
├── enterprise managed-mcp.json ← 外部(企业管控)
|
||||||
|
├── setupComputerUseMCP() / setupClaudeInChrome() ← 内置(动态注册)
|
||||||
|
└── SDK 传入 (type:'sdk') ← 内置(IDE 嵌入)
|
||||||
↓
|
↓
|
||||||
getAllMcpConfigs() ← enterprise 独占或合并 user/project/local + plugin + claude.ai
|
getAllMcpConfigs() ← enterprise 独占 或 合并 user/project/local + plugin + claude.ai
|
||||||
↓
|
↓
|
||||||
useManageMCPConnections() ← React Hook 管理连接生命周期
|
useManageMCPConnections() ← React Hook 管理连接生命周期
|
||||||
↓
|
↓
|
||||||
connectToServer(name, config) ← memoize 缓存(lodash memoize)
|
connectToServer(name, config) ← memoize 缓存(lodash memoize)
|
||||||
├── 创建 Transport(stdio/sse/http/...)
|
├── 判断:内置 MCP → InProcessTransport(同进程)
|
||||||
├── new Client() ← @modelcontextprotocol/sdk
|
├── 判断:外部 stdio → StdioClientTransport(子进程)
|
||||||
├── client.connect(transport) ← 超时控制(MCP_TIMEOUT, 默认 30s)
|
├── 判断:远程 SSE/HTTP/WS → 网络传输
|
||||||
└── 返回 MCPServerConnection ← { connected | failed | needs-auth | pending }
|
└── 返回 MCPServerConnection ← { connected | failed | needs-auth | pending | disabled }
|
||||||
↓
|
↓
|
||||||
fetchToolsForClient(client) ← LRU(20) 缓存
|
fetchToolsForClient(client) ← LRU(20) 缓存
|
||||||
├── client.request({ method: 'tools/list' })
|
├── client.request({ method: 'tools/list' })
|
||||||
@@ -30,19 +37,208 @@ assembleToolPool() ← 合并内置工具 + MCP 工具
|
|||||||
工具名格式: mcp__<serverName>__<toolName> ← buildMcpToolName()
|
工具名格式: mcp__<serverName>__<toolName> ← buildMcpToolName()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 两种 MCP 模式:内置 vs 外部
|
||||||
|
|
||||||
|
Claude Code 的 MCP 实现区分 **内置 MCP 服务器** 和 **外部 MCP 服务器**。两者使用相同的客户端协议和工具发现机制,但在连接方式、生命周期管理和配置来源上完全不同。
|
||||||
|
|
||||||
|
### 内置 MCP 服务器
|
||||||
|
|
||||||
|
内置 MCP 服务器由 Claude Code 自身提供,无需用户手动配置。它们在启动时自动注册为 `dynamic` scope 的配置,并在同进程内运行。
|
||||||
|
|
||||||
|
| 服务器 | 名称 | 包路径 | Feature Flag | 启用方式 |
|
||||||
|
|--------|------|--------|-------------|---------|
|
||||||
|
| Computer Use | `computer-use` | `@ant/computer-use-mcp` | `CHICAGO_MCP` | GrowthBook gate + macOS + interactive |
|
||||||
|
| Claude in Chrome | `claude-in-chrome` | `@ant/claude-for-chrome-mcp` | — | `--chrome` 参数或 `claudeInChromeDefaultEnabled` 配置 |
|
||||||
|
| VSCode SDK | `claude-vscode` | — | — | IDE 嵌入模式 (type:`sdk`) |
|
||||||
|
|
||||||
|
#### InProcessTransport:零开销同进程通信
|
||||||
|
|
||||||
|
内置服务器通过 `InProcessTransport`(`src/services/mcp/InProcessTransport.ts`)运行,**不启动子进程**:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 创建一对 linked transport —— 消息在两端之间直接传递
|
||||||
|
const [clientTransport, serverTransport] = createLinkedTransportPair()
|
||||||
|
|
||||||
|
// server 端连接到 serverTransport
|
||||||
|
inProcessServer = createComputerUseMcpServerForCli()
|
||||||
|
await inProcessServer.connect(serverTransport)
|
||||||
|
|
||||||
|
// client 端使用 clientTransport(与外部 MCP 的 Client 相同接口)
|
||||||
|
transport = clientTransport
|
||||||
|
```
|
||||||
|
|
||||||
|
`InProcessTransport` 的核心设计:
|
||||||
|
- `send()` 通过 `queueMicrotask()` 异步投递消息到对端,避免同步请求/响应的栈深度问题
|
||||||
|
- `close()` 双向关闭,任一端关闭都会触发两端的 `onclose` 回调
|
||||||
|
- 无网络开销、无 IPC 序列化、无进程启动时间
|
||||||
|
|
||||||
|
#### 动态注册流程
|
||||||
|
|
||||||
|
内置服务器在 `main.tsx` 的启动流程中注册,注入 `dynamicMcpConfig`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// main.tsx: Computer Use MCP 动态注册
|
||||||
|
if (feature("CHICAGO_MCP") && getPlatform() !== "unknown" && !getIsNonInteractiveSession()) {
|
||||||
|
const { getChicagoEnabled } = await import("src/utils/computerUse/gates.js")
|
||||||
|
if (getChicagoEnabled()) {
|
||||||
|
const { setupComputerUseMCP } = await import("src/utils/computerUse/setup.js")
|
||||||
|
const { mcpConfig, allowedTools } = setupComputerUseMCP()
|
||||||
|
dynamicMcpConfig = { ...dynamicMcpConfig, ...mcpConfig }
|
||||||
|
allowedTools.push(...cuTools)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`setupComputerUseMCP()` 返回的配置(`src/utils/computerUse/setup.ts`):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
"computer-use": {
|
||||||
|
type: "stdio", // 类型标记为 stdio(但 client.ts 会拦截为 InProcessTransport)
|
||||||
|
command: process.execPath,
|
||||||
|
args: ["--computer-use-mcp"],
|
||||||
|
scope: "dynamic", // 动态作用域,不持久化
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 连接时拦截
|
||||||
|
|
||||||
|
`connectToServer()` 在 `client.ts:906-944` 中根据服务器名拦截内置服务器:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Chrome MCP — 在 process 内运行,避免 ~325MB 子进程
|
||||||
|
if (isClaudeInChromeMCPServer(name)) {
|
||||||
|
const { createChromeContext } = await import('../../utils/claudeInChrome/mcpServer.js')
|
||||||
|
const { createClaudeForChromeMcpServer } = await import('@ant/claude-for-chrome-mcp')
|
||||||
|
const { createLinkedTransportPair } = await import('./InProcessTransport.js')
|
||||||
|
const context = createChromeContext(config.env)
|
||||||
|
inProcessServer = createClaudeForChromeMcpServer(context)
|
||||||
|
const [clientTransport, serverTransport] = createLinkedTransportPair()
|
||||||
|
await inProcessServer.connect(serverTransport)
|
||||||
|
transport = clientTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computer Use MCP — 同理
|
||||||
|
if (feature('CHICAGO_MCP') && isComputerUseMCPServer(name)) {
|
||||||
|
const { createComputerUseMcpServerForCli } = await import('../../utils/computerUse/mcpServer.js')
|
||||||
|
const { createLinkedTransportPair } = await import('./InProcessTransport.js')
|
||||||
|
inProcessServer = await createComputerUseMcpServerForCli()
|
||||||
|
const [clientTransport, serverTransport] = createLinkedTransportPair()
|
||||||
|
await inProcessServer.connect(serverTransport)
|
||||||
|
transport = clientTransport
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 保留名称保护
|
||||||
|
|
||||||
|
内置服务器的名称被保留,用户无法手动添加同名配置(`config.ts:636-648`):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 添加 MCP 配置时检查保留名
|
||||||
|
if (isClaudeInChromeMCPServer(name)) {
|
||||||
|
throw new Error(`Cannot add MCP server "${name}": this name is reserved.`)
|
||||||
|
}
|
||||||
|
if (feature('CHICAGO_MCP') && isComputerUseMCPServer(name)) {
|
||||||
|
throw new Error(`Cannot add MCP server "${name}": this name is reserved.`)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
启动时也有全局检查(`main.tsx:2351-2368`):如果用户配置中包含保留名(非 `type:'sdk'`),直接 `process.exit(1)`。
|
||||||
|
|
||||||
|
#### VSCode SDK MCP
|
||||||
|
|
||||||
|
VSCode SDK MCP 是特殊的内置模式。IDE(如 VS Code、JetBrains)通过嵌入方式启动 Claude Code,并传入 `type:'sdk'` 的 MCP 配置。这类配置:
|
||||||
|
- 不经过保留名称检查(IDE 可以使用任意名称)
|
||||||
|
- 不参与 enterprise MCP 的排他控制
|
||||||
|
- 通过 VSCode SDK transport 连接
|
||||||
|
- 支持双向通知(如 `file_updated`、`experiment_gates`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/services/mcp/vscodeSdkMcp.ts
|
||||||
|
export function setupVscodeSdkMcp(sdkClients: MCPServerConnection[]): void {
|
||||||
|
const client = sdkClients.find(client => client.name === 'claude-vscode')
|
||||||
|
if (client && client.type === 'connected') {
|
||||||
|
// 注册 log_event 通知处理器
|
||||||
|
client.client.setNotificationHandler(LogEventNotificationSchema(), ...)
|
||||||
|
// 发送实验门控到 VSCode
|
||||||
|
client.client.notification({ method: 'experiment_gates', params: { gates } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 外部 MCP 服务器
|
||||||
|
|
||||||
|
外部 MCP 服务器由用户在配置文件中声明,通过子进程或网络连接运行。
|
||||||
|
|
||||||
|
#### 配置来源
|
||||||
|
|
||||||
|
| 来源 | Scope | 文件位置 | 优先级 |
|
||||||
|
|------|-------|---------|--------|
|
||||||
|
| 项目配置 | `project` | `<project>/.mcp.json` | 最高(同名覆盖) |
|
||||||
|
| 本地配置 | `local` | `<project>/.claude/settings.local.json` | 高 |
|
||||||
|
| 用户配置 | `user` | `~/.claude/settings.json` | 中 |
|
||||||
|
| 插件 | `dynamic` | 插件 manifest 中 `.mcp.json` | 中 |
|
||||||
|
| claude.ai | `claudeai` | 通过 API 获取 | 低 |
|
||||||
|
| 企业管控 | `enterprise` | 系统管理路径 `managed-mcp.json` | 排他(存在时覆盖全部) |
|
||||||
|
|
||||||
|
#### 配置示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
// settings.json / .mcp.json 中的 MCP 配置
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
// stdio 类型 — 启动子进程
|
||||||
|
"my-database": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["@my-org/db-mcp-server"],
|
||||||
|
"env": { "DB_URL": "postgres://..." }
|
||||||
|
},
|
||||||
|
|
||||||
|
// HTTP 流类型 — 远程服务器
|
||||||
|
"remote-api": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "https://api.example.com/mcp"
|
||||||
|
},
|
||||||
|
|
||||||
|
// SSE 类型 — Server-Sent Events
|
||||||
|
"realtime-feed": {
|
||||||
|
"type": "sse",
|
||||||
|
"url": "https://feed.example.com/sse"
|
||||||
|
},
|
||||||
|
|
||||||
|
// WebSocket 类型
|
||||||
|
"ws-service": {
|
||||||
|
"type": "ws",
|
||||||
|
"url": "wss://ws.example.com/mcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 配置合并与去重
|
||||||
|
|
||||||
|
`getAllMcpConfigs()`(`config.ts`)按优先级合并多个来源的配置:
|
||||||
|
|
||||||
|
1. 企业管控配置存在时,**独占返回**(忽略所有其他来源)
|
||||||
|
2. 否则合并:user → project → local → plugin → claude.ai
|
||||||
|
3. 插件与手动配置去重:通过 `getMcpServerSignature()` 生成内容签名(基于 command/args/url),插件配置被同名手动配置抑制
|
||||||
|
4. `addScopeToServers()` 为每个配置项标注来源 scope
|
||||||
|
|
||||||
## 7 种传输层实现
|
## 7 种传输层实现
|
||||||
|
|
||||||
`connectToServer()`(`client.ts:596-1643`)根据 `config.type` 分发到不同的 Transport 实现:
|
`connectToServer()`(`client.ts:596-1643`)根据 `config.type` 分发到不同的 Transport 实现:
|
||||||
|
|
||||||
| 传输类型 | Transport 类 | 适用场景 | 认证方式 |
|
| 传输类型 | Transport 类 | 适用场景 | 认证方式 |
|
||||||
|----------|-------------|---------|---------|
|
|----------|-------------|---------|---------|
|
||||||
| `stdio`(默认) | `StdioClientTransport` | 本地子进程 | 无 |
|
| `stdio`(默认) | `StdioClientTransport` | 外部本地子进程 | 无 |
|
||||||
| `sse` | `SSEClientTransport` | 远程 SSE 服务 | `ClaudeAuthProvider` + OAuth |
|
| `sse` | `SSEClientTransport` | 远程 SSE 服务 | `ClaudeAuthProvider` + OAuth |
|
||||||
| `http` | `StreamableHTTPClientTransport` | HTTP 流 | `ClaudeAuthProvider` + OAuth |
|
| `http` | `StreamableHTTPClientTransport` | HTTP 流 | `ClaudeAuthProvider` + OAuth |
|
||||||
| `sse-ide` | `SSEClientTransport` | IDE 集成 | lockfile token |
|
| `sse-ide` | `SSEClientTransport` | IDE 集成 | lockfile token |
|
||||||
| `ws-ide` | `WebSocketTransport` | IDE WebSocket | `X-Claude-Code-Ide-Authorization` |
|
| `ws-ide` | `WebSocketTransport` | IDE WebSocket | `X-Claude-Code-Ide-Authorization` |
|
||||||
| `ws` | `WebSocketTransport` | WebSocket 服务 | session ingress token |
|
| `ws` | `WebSocketTransport` | WebSocket 服务 | session ingress token |
|
||||||
| `claudeai-proxy` | `StreamableHTTPClientTransport` | claude.ai 代理 | OAuth bearer + 401 重试 |
|
| `claudeai-proxy` | `StreamableHTTPClientTransport` | claude.ai 代理 | OAuth bearer + 401 重试 |
|
||||||
|
| InProcess(内置) | `InProcessTransport` | Computer Use / Chrome | 无(同进程) |
|
||||||
|
|
||||||
### stdio 传输的进程管理
|
### stdio 传输的进程管理
|
||||||
|
|
||||||
@@ -108,13 +304,21 @@ timer.unref?.() // 不阻止进程退出
|
|||||||
|
|
||||||
## 工具发现:从 MCP 到 Tool 接口
|
## 工具发现:从 MCP 到 Tool 接口
|
||||||
|
|
||||||
`fetchToolsForClient()`(`client.ts:1745-2000`)使用 `memoizeWithLRU` 缓存(上限 20),将 MCP 工具转换为 Claude Code 的统一 Tool 接口:
|
`fetchToolsForClient()`(`client.ts:1744-2000`)使用 `memoizeWithLRU` 缓存(上限 100),将 MCP 工具转换为 Claude Code 的统一 Tool 接口:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const fullyQualifiedName = buildMcpToolName(client.name, tool.name)
|
const fullyQualifiedName = buildMcpToolName(client.name, tool.name)
|
||||||
// 结果: "mcp__my-db__query"
|
// 结果: "mcp__my-database__query"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 内置 MCP 的工具发现
|
||||||
|
|
||||||
|
内置 MCP 服务器虽然使用 InProcessTransport,但工具发现流程与外部服务器完全一致:
|
||||||
|
|
||||||
|
- **Computer Use**:`createComputerUseMcpServerForCli()` 在 `src/utils/computerUse/mcpServer.ts` 中构建 MCP Server 对象,注册 `ListToolsRequestSchema` handler。工具描述包含平台特定的已安装应用列表(1s 超时枚举)。
|
||||||
|
- **Claude in Chrome**:`createClaudeForChromeMcpServer()` 在 `@ant/claude-for-chrome-mcp` 包中构建 Server,提供 17+ 个浏览器控制工具。
|
||||||
|
- **VSCode SDK**:由 IDE 端提供工具列表,通过 SDK transport 传递。
|
||||||
|
|
||||||
### 工具描述截断
|
### 工具描述截断
|
||||||
|
|
||||||
MCP 工具描述上限 2048 字符(`MAX_MCP_DESCRIPTION_LENGTH`)。OpenAPI 生成的 MCP 服务器曾观察到 15-60KB 的描述文档。
|
MCP 工具描述上限 2048 字符(`MAX_MCP_DESCRIPTION_LENGTH`)。OpenAPI 生成的 MCP 服务器曾观察到 15-60KB 的描述文档。
|
||||||
@@ -134,6 +338,8 @@ MCP 工具描述上限 2048 字符(`MAX_MCP_DESCRIPTION_LENGTH`)。OpenAPI
|
|||||||
|
|
||||||
MCP 工具默认返回 `{ behavior: 'passthrough' }`(`client.ts:1816-1834`),意味着它们始终进入权限确认流程。工具名使用 `mcp__` 前缀精确匹配权限规则。
|
MCP 工具默认返回 `{ behavior: 'passthrough' }`(`client.ts:1816-1834`),意味着它们始终进入权限确认流程。工具名使用 `mcp__` 前缀精确匹配权限规则。
|
||||||
|
|
||||||
|
内置 MCP 服务器的工具通过 `allowedTools` 列表自动授权——在 `main.tsx` 启动时加入,绕过普通权限提示。例如 Computer Use 工具的 `request_access` 自行处理会话级审批。
|
||||||
|
|
||||||
## MCP 工具的执行链路
|
## MCP 工具的执行链路
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -169,23 +375,33 @@ getRemoteMcpServerConnectionBatchSize() // 默认 20
|
|||||||
|
|
||||||
本地 MCP 服务器(stdio)是重量级的子进程,默认限制 3 个并发连接。远程服务器是轻量级 HTTP 请求,允许 20 个并发。
|
本地 MCP 服务器(stdio)是重量级的子进程,默认限制 3 个并发连接。远程服务器是轻量级 HTTP 请求,允许 20 个并发。
|
||||||
|
|
||||||
## 实际配置示例
|
## 内置 vs 外部 MCP 对比总结
|
||||||
|
|
||||||
```json
|
| 维度 | 内置 MCP | 外部 MCP |
|
||||||
// settings.json 中的 MCP 配置
|
|------|---------|---------|
|
||||||
{
|
| **Transport** | `InProcessTransport`(同进程) | stdio / SSE / HTTP / WebSocket |
|
||||||
"mcpServers": {
|
| **配置来源** | `setupComputerUseMCP()` / `setupClaudeInChrome()` 等动态注册 | settings.json / .mcp.json / 插件 / claude.ai |
|
||||||
"my-database": {
|
| **Scope** | `dynamic` | `user` / `project` / `local` / `enterprise` / `claudeai` |
|
||||||
"command": "npx",
|
| **进程模型** | 同进程,零开销 | 子进程(stdio)或网络连接 |
|
||||||
"args": ["@my-org/db-mcp-server"],
|
| **名称保护** | 保留名,用户不可添加同名 | 自由命名(字母数字 + `-_`) |
|
||||||
"env": { "DB_URL": "postgres://..." }
|
| **生命周期** | 随 CLI 启停 | 连接缓存 + 按需重连 |
|
||||||
},
|
| **权限** | `allowedTools` 自动授权 | `passthrough` 进入权限确认 |
|
||||||
"remote-api": {
|
| **Feature Flag** | `CHICAGO_MCP`(Computer Use)等 | 无(始终可用) |
|
||||||
"type": "http",
|
| **工具发现** | 与外部相同(MCP 协议) | 标准 MCP `tools/list` |
|
||||||
"url": "https://api.example.com/mcp"
|
| **清理** | `inProcessServer.close()` | 信号升级策略 SIGINT→SIGTERM→SIGKILL |
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
配置后,AI 的工具列表中会出现 `mcp__my-database__query` 和 `mcp__remote-api__*` 工具——与内置工具使用相同的权限检查链路和 UI 渲染。
|
## 关键源文件索引
|
||||||
|
|
||||||
|
| 文件 | 职责 |
|
||||||
|
|------|------|
|
||||||
|
| `src/services/mcp/client.ts` | 核心客户端:connectToServer、fetchToolsForClient、MCPTool.call |
|
||||||
|
| `src/services/mcp/config.ts` | 配置管理:getAllMcpConfigs、addMcpConfig、removeMcpConfig |
|
||||||
|
| `src/services/mcp/types.ts` | 类型定义:配置 Schema、连接状态类型 |
|
||||||
|
| `src/services/mcp/InProcessTransport.ts` | 内置 MCP 传输层:linked transport pair |
|
||||||
|
| `src/services/mcp/vscodeSdkMcp.ts` | VSCode SDK MCP:双向通知、实验门控 |
|
||||||
|
| `src/services/mcp/useManageMCPConnections.ts` | React Hook:连接生命周期、重连 |
|
||||||
|
| `src/utils/computerUse/mcpServer.ts` | Computer Use MCP Server 构建 |
|
||||||
|
| `src/utils/computerUse/setup.ts` | Computer Use 动态注册 |
|
||||||
|
| `src/utils/claudeInChrome/mcpServer.ts` | Chrome MCP Server 构建 + Bridge 配置 |
|
||||||
|
| `src/tools/MCPTool/MCPTool.ts` | MCP 工具包装:统一 Tool 接口 |
|
||||||
|
| `src/entrypoints/mcp.ts` | MCP server 入口(Claude Code 作为 MCP server) |
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Skill 的核心洞见:**复杂任务的关键不在代码逻辑,而在 Promp
|
|||||||
|
|
||||||
### 1. 内置命令(Built-in Commands)
|
### 1. 内置命令(Built-in Commands)
|
||||||
|
|
||||||
硬编码在 `src/commands.ts:258` 的 `COMMANDS` memoize 数组中,包含 70+ 条命令(`/commit`、`/review`、`/compact` 等)。这些是 TypeScript 模块而非 Markdown,但实现了相同的 `Command` 接口(`src/types/command.ts`)。
|
硬编码在 `src/commands.ts:299` 的 `COMMANDS` memoize 数组中,包含 70+ 条命令(`/commit`、`/review`、`/compact` 等)。这些是 TypeScript 模块而非 Markdown,但实现了相同的 `Command` 接口(`src/types/command.ts`)。
|
||||||
|
|
||||||
### 2. Bundled Skills(编译时打包)
|
### 2. Bundled Skills(编译时打包)
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ shell: ["bash"] # Shell 执行环境
|
|||||||
|
|
||||||
## 两条执行路径:Inline vs Fork
|
## 两条执行路径:Inline vs Fork
|
||||||
|
|
||||||
SkillTool(`src/tools/SkillTool/SkillTool.ts:332`)在 `call()` 中根据 `command.context` 分流:
|
SkillTool(`packages/builtin-tools/src/tools/SkillTool/SkillTool.ts:332`)在 `call()` 中根据 `command.context` 分流:
|
||||||
|
|
||||||
### Inline 模式(默认)
|
### Inline 模式(默认)
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
| 11 | BigQuery Metrics | `api.anthropic.com/api/claude_code/metrics` | HTTPS | 默认启用 |
|
| 11 | BigQuery Metrics | `api.anthropic.com/api/claude_code/metrics` | HTTPS | 默认启用 |
|
||||||
| 12 | MCP Proxy | `mcp-proxy.anthropic.com` | HTTPS+WS | 使用 MCP 工具时 |
|
| 12 | MCP Proxy | `mcp-proxy.anthropic.com` | HTTPS+WS | 使用 MCP 工具时 |
|
||||||
| 13 | MCP Registry | `api.anthropic.com/mcp-registry` | HTTPS | 查询 MCP 服务器时 |
|
| 13 | MCP Registry | `api.anthropic.com/mcp-registry` | HTTPS | 查询 MCP 服务器时 |
|
||||||
| 14 | Bing Search | `www.bing.com` | HTTPS | WebSearch 工具 |
|
| 14 | Web Search Pages | `www.bing.com`, `search.brave.com` | HTTPS | WebSearch 工具,可通过 `WEB_SEARCH_ADAPTER=bing|brave` 切换 |
|
||||||
| 15 | Google Cloud Storage (更新) | `storage.googleapis.com` | HTTPS | 版本检查 |
|
| 15 | Google Cloud Storage (更新) | `storage.googleapis.com` | HTTPS | 版本检查 |
|
||||||
| 16 | GitHub Raw (Changelog/Stats) | `raw.githubusercontent.com` | HTTPS | 更新提示 |
|
| 16 | GitHub Raw (Changelog/Stats) | `raw.githubusercontent.com` | HTTPS | 更新提示 |
|
||||||
| 17 | Claude in Chrome Bridge | `bridge.claudeusercontent.com` | WSS | Chrome 集成 |
|
| 17 | Claude in Chrome Bridge | `bridge.claudeusercontent.com` | WSS | Chrome 集成 |
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
- **端点**: `{region}-aiplatform.googleapis.com`
|
- **端点**: `{region}-aiplatform.googleapis.com`
|
||||||
- **认证**: `GoogleAuth` + `cloud-platform` scope
|
- **认证**: `GoogleAuth` + `cloud-platform` scope
|
||||||
- **文件**: `src/services/api/client.ts:228-298`
|
- **文件**: `src/services/api/client.ts:221-298`
|
||||||
|
|
||||||
### 4. Azure Foundry
|
### 4. Azure Foundry
|
||||||
|
|
||||||
@@ -121,16 +121,20 @@ Anthropic 托管的 MCP 服务器代理。
|
|||||||
- **端点**: `https://api.anthropic.com/mcp-registry/v0/servers?version=latest&visibility=commercial`
|
- **端点**: `https://api.anthropic.com/mcp-registry/v0/servers?version=latest&visibility=commercial`
|
||||||
- **文件**: `src/services/mcp/officialRegistry.ts`
|
- **文件**: `src/services/mcp/officialRegistry.ts`
|
||||||
|
|
||||||
### 14. Bing Search
|
### 14. Web Search Pages
|
||||||
|
|
||||||
WebSearch 工具的默认适配器,抓取 Bing 搜索结果。
|
WebSearch 工具支持直接抓取 Bing 搜索结果页面,也支持通过 Brave 的 LLM Context API
|
||||||
|
获取搜索上下文;可通过 `WEB_SEARCH_ADAPTER=bing|brave` 显式切换后端。
|
||||||
|
|
||||||
- **端点**: `https://www.bing.com/search?q={query}&setmkt=en-US`
|
- **Bing 端点**: `https://www.bing.com/search?q={query}&setmkt=en-US`
|
||||||
- **文件**: `src/tools/WebSearchTool/adapters/bingAdapter.ts`
|
- **Brave 端点**: `https://api.search.brave.com/res/v1/llm/context?q={query}`
|
||||||
|
- **文件**:
|
||||||
|
- `packages/builtin-tools/src/tools/WebSearchTool/adapters/bingAdapter.ts`
|
||||||
|
- `packages/builtin-tools/src/tools/WebSearchTool/adapters/braveAdapter.ts`
|
||||||
|
|
||||||
另外还有 Domain Blocklist 查询:
|
另外还有 Domain Blocklist 查询:
|
||||||
- **端点**: `https://api.anthropic.com/api/web/domain_info?domain={domain}`
|
- **端点**: `https://api.anthropic.com/api/web/domain_info?domain={domain}`
|
||||||
- **文件**: `src/tools/WebFetchTool/utils.ts`
|
- **文件**: `packages/builtin-tools/src/tools/WebFetchTool/utils.ts`
|
||||||
|
|
||||||
### 15. Google Cloud Storage (自动更新)
|
### 15. Google Cloud Storage (自动更新)
|
||||||
|
|
||||||
@@ -201,6 +205,7 @@ WebSearch 工具的默认适配器,抓取 Bing 搜索结果。
|
|||||||
| `{region}-aiplatform.googleapis.com` | Google Vertex AI | HTTPS |
|
| `{region}-aiplatform.googleapis.com` | Google Vertex AI | HTTPS |
|
||||||
| `{resource}.services.ai.azure.com` | Azure Foundry | HTTPS |
|
| `{resource}.services.ai.azure.com` | Azure Foundry | HTTPS |
|
||||||
| `www.bing.com` | Bing 搜索 | HTTPS |
|
| `www.bing.com` | Bing 搜索 | HTTPS |
|
||||||
|
| `search.brave.com` | Brave 搜索 | HTTPS |
|
||||||
| `storage.googleapis.com` | 自动更新 | HTTPS |
|
| `storage.googleapis.com` | 自动更新 | HTTPS |
|
||||||
| `raw.githubusercontent.com` | Changelog / 插件统计 | HTTPS |
|
| `raw.githubusercontent.com` | Changelog / 插件统计 | HTTPS |
|
||||||
| `bridge.claudeusercontent.com` | Chrome Bridge | WSS |
|
| `bridge.claudeusercontent.com` | Chrome Bridge | WSS |
|
||||||
|
|||||||
@@ -1,457 +0,0 @@
|
|||||||
# Feature 探索计划书
|
|
||||||
|
|
||||||
> 生成日期:2026-04-02
|
|
||||||
> 代码库中已识别 89 个 feature flag,本文档按实现完整度和探索价值分级,制定探索优先级和路线图。
|
|
||||||
>
|
|
||||||
> **已完成**:BUDDY(✅ 2026-04-02)、TRANSCRIPT_CLASSIFIER / Auto Mode(✅ 2026-04-02)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 一、总览
|
|
||||||
|
|
||||||
### 按实现状态分类
|
|
||||||
|
|
||||||
| 状态 | 数量 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| 已实现/可用 | 11 | 代码完整,开启 feature 后可运行(可能需要 OAuth 等外部依赖) |
|
|
||||||
| 部分实现 | 8 | 核心逻辑存在但关键模块为 stub,需要补全 |
|
|
||||||
| 纯 Stub | 15 | 所有函数/工具返回空值,需要从零实现 |
|
|
||||||
| N/A | 55+ | 内部基础设施、低引用量辅助功能,或反编译丢失过多 |
|
|
||||||
|
|
||||||
### 启用方式
|
|
||||||
|
|
||||||
所有 feature 通过环境变量启用:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 单个 feature
|
|
||||||
FEATURE_BUDDY=1 bun run dev
|
|
||||||
|
|
||||||
# 多个 feature 组合
|
|
||||||
FEATURE_KAIROS=1 FEATURE_PROACTIVE=1 FEATURE_FORK_SUBAGENT=1 bun run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 二、Tier 1 — 已实现/可用(优先探索)
|
|
||||||
|
|
||||||
### 2.1 KAIROS(常驻助手模式)⭐ 最高优先级
|
|
||||||
|
|
||||||
- **引用数**:154(全库最大)
|
|
||||||
- **功能**:将 CLI 变为常驻后台助手,支持:
|
|
||||||
- 持久化 bridge 会话(跨重启复用 session)
|
|
||||||
- 后台执行任务(用户离开终端时继续工作)
|
|
||||||
- 推送通知到移动端(任务完成/需要输入时)
|
|
||||||
- 每日记忆日志 + `/dream` 知识蒸馏
|
|
||||||
- 外部频道消息接入(Slack/Discord/Telegram)
|
|
||||||
- **子 Feature**:
|
|
||||||
|
|
||||||
| 子 Feature | 引用 | 功能 |
|
|
||||||
|-----------|------|------|
|
|
||||||
| `KAIROS_BRIEF` | 39 | Brief 工具(`SendUserMessage`),结构化消息输出 |
|
|
||||||
| `KAIROS_CHANNELS` | 19 | 外部频道消息接入 |
|
|
||||||
| `KAIROS_PUSH_NOTIFICATION` | 4 | 移动端推送通知 |
|
|
||||||
| `KAIROS_GITHUB_WEBHOOKS` | 3 | GitHub PR webhook 订阅 |
|
|
||||||
| `KAIROS_DREAM` | 1 | 夜间记忆蒸馏 |
|
|
||||||
|
|
||||||
- **关键文件**:`src/assistant/`、`src/tools/BriefTool/`、`src/services/mcp/channelNotification.ts`、`src/memdir/memdir.ts`
|
|
||||||
- **外部依赖**:Anthropic OAuth(claude.ai 订阅)、GrowthBook 特性门控
|
|
||||||
- **探索命令**:`FEATURE_KAIROS=1 FEATURE_KAIROS_BRIEF=1 FEATURE_PROACTIVE=1 bun run dev`
|
|
||||||
|
|
||||||
**探索步骤**:
|
|
||||||
1. 开启 feature,观察启动行为变化
|
|
||||||
2. 测试 `/assistant`、`/brief` 命令
|
|
||||||
3. 验证 BriefTool 输出模式
|
|
||||||
4. 尝试频道消息接入
|
|
||||||
5. 测试 `/dream` 记忆蒸馏
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### ~~2.2 TRANSCRIPT_CLASSIFIER(Auto Mode 分类器)~~ ✅ 已完成
|
|
||||||
|
|
||||||
- **引用数**:108
|
|
||||||
- **功能**:使用 LLM 对用户意图进行分类,实现 auto mode(自动决定工具权限)
|
|
||||||
- **状态**:✅ prompt 模板已重建,功能完整可用(2026-04-02 完成)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2.3 VOICE_MODE(语音输入)
|
|
||||||
|
|
||||||
- **引用数**:46
|
|
||||||
- **功能**:按键说话(Push-to-Talk),音频流式传输到 Anthropic STT 端点(Nova 3),实时转录显示
|
|
||||||
- **当前状态**:**完整实现**,包括录音、WebSocket 流、转录插入
|
|
||||||
- **关键文件**:`src/voice/voiceModeEnabled.ts`、`src/hooks/useVoice.ts`、`src/services/voiceStreamSTT.ts`
|
|
||||||
- **外部依赖**:Anthropic OAuth(非 API key)、macOS 原生音频或 SoX
|
|
||||||
- **探索命令**:`FEATURE_VOICE_MODE=1 bun run dev`
|
|
||||||
- **默认快捷键**:长按空格键录音
|
|
||||||
|
|
||||||
**探索步骤**:
|
|
||||||
1. 确认 OAuth token 可用
|
|
||||||
2. 测试按住空格录音 → 释放后转录
|
|
||||||
3. 验证实时中间转录显示
|
|
||||||
4. 测试 `/voice` 命令切换
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2.4 TEAMMEM(团队共享记忆)
|
|
||||||
|
|
||||||
- **引用数**:51
|
|
||||||
- **功能**:基于 GitHub 仓库的团队共享记忆系统,`memory/team/` 目录双向同步到 Anthropic 服务器
|
|
||||||
- **当前状态**:**完整实现**,包括增量同步、冲突解决、密钥扫描、路径穿越防护
|
|
||||||
- **关键文件**:`src/services/teamMemorySync/`(index、watcher、secretScanner)、`src/memdir/teamMemPaths.ts`
|
|
||||||
- **外部依赖**:Anthropic OAuth + GitHub remote(`getGithubRepo()`)
|
|
||||||
- **探索命令**:`FEATURE_TEAMMEM=1 bun run dev`
|
|
||||||
|
|
||||||
**探索步骤**:
|
|
||||||
1. 确认项目有 GitHub remote
|
|
||||||
2. 开启后观察 `memory/team/` 目录创建
|
|
||||||
3. 测试团队记忆写入和同步
|
|
||||||
4. 验证密钥扫描防护
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2.5 COORDINATOR_MODE(多 Agent 编排)
|
|
||||||
|
|
||||||
- **引用数**:32
|
|
||||||
- **功能**:CLI 变为编排者,通过 AgentTool 派发任务给多个 worker 并行执行
|
|
||||||
- **当前状态**:核心逻辑实现,worker agent 模块为 stub
|
|
||||||
- **关键文件**:`src/coordinator/coordinatorMode.ts`(系统 prompt 完整)、`src/coordinator/workerAgent.ts`(stub)
|
|
||||||
- **限制**:编排者只能使用 AgentTool/TaskStop/SendMessage,不能直接操作文件
|
|
||||||
- **探索命令**:`FEATURE_COORDINATOR_MODE=1 CLAUDE_CODE_COORDINATOR_MODE=1 bun run dev`
|
|
||||||
|
|
||||||
**探索步骤**:
|
|
||||||
1. 补全 `workerAgent.ts` stub
|
|
||||||
2. 测试多 worker 并行任务派发
|
|
||||||
3. 验证 worker 结果汇总
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2.6 BRIDGE_MODE(远程控制)
|
|
||||||
|
|
||||||
- **引用数**:28
|
|
||||||
- **功能**:本地 CLI 注册为 bridge 环境,可从 claude.ai 或其他控制面远程驱动
|
|
||||||
- **当前状态**:v1(env-based)和 v2(env-less)实现均存在
|
|
||||||
- **关键文件**:`src/bridge/bridgeEnabled.ts`、`src/bridge/replBridge.ts`(v1)、`src/bridge/remoteBridgeCore.ts`(v2)
|
|
||||||
- **外部依赖**:claude.ai OAuth、GrowthBook 门控 `tengu_ccr_bridge`
|
|
||||||
- **探索命令**:`FEATURE_BRIDGE_MODE=1 bun run dev`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2.7 FORK_SUBAGENT(上下文继承子 Agent)
|
|
||||||
|
|
||||||
- **引用数**:4
|
|
||||||
- **功能**:AgentTool 生成 fork 子 agent,继承父级完整对话上下文,优化 prompt cache
|
|
||||||
- **当前状态**:**完整实现**(`forkSubagent.ts`),支持 worktree 隔离通知、递归防护
|
|
||||||
- **关键文件**:`src/tools/AgentTool/forkSubagent.ts`
|
|
||||||
- **探索命令**:`FEATURE_FORK_SUBAGENT=1 bun run dev`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2.8 TOKEN_BUDGET(Token 预算控制)
|
|
||||||
|
|
||||||
- **引用数**:9
|
|
||||||
- **功能**:解析用户指定的 token 预算(如 "spend 2M tokens"),自动持续工作直到达到目标
|
|
||||||
- **当前状态**:解析器**完整实现**,支持简写和详细语法;QueryEngine 中的周转逻辑已连接
|
|
||||||
- **关键文件**:`src/utils/tokenBudget.ts`、`src/QueryEngine.ts`
|
|
||||||
- **探索命令**:`FEATURE_TOKEN_BUDGET=1 bun run dev`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2.9 MCP_SKILLS(MCP 技能发现)
|
|
||||||
|
|
||||||
- **引用数**:9
|
|
||||||
- **功能**:将 MCP 服务器提供的 prompt 类型命令筛选为可调用技能
|
|
||||||
- **当前状态**:**功能性实现**(config 门控筛选器)
|
|
||||||
- **关键文件**:`src/commands.ts`(`getMcpSkillCommands()`)
|
|
||||||
- **探索命令**:`FEATURE_MCP_SKILLS=1 bun run dev`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2.10 TREE_SITTER_BASH(Bash AST 解析)
|
|
||||||
|
|
||||||
- **引用数**:3
|
|
||||||
- **功能**:纯 TypeScript bash 命令 AST 解析器,用于 fail-closed 权限匹配
|
|
||||||
- **当前状态**:**完整实现**(`bashParser.ts` ~2000行 + `ast.ts` ~400行)
|
|
||||||
- **关键文件**:`src/utils/vendor/tree-sitter-bash/`
|
|
||||||
- **探索命令**:`FEATURE_TREE_SITTER_BASH=1 bun run dev`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### ~~2.11 BUDDY(虚拟伙伴)~~ ✅ 已完成
|
|
||||||
|
|
||||||
- **引用数**:16
|
|
||||||
- **功能**:`/buddy` 命令,支持 hatch/rehatch/pet/mute/unmute
|
|
||||||
- **状态**:✅ 已合入,功能完整可用(2026-04-02 完成)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 三、Tier 2 — 部分实现(需要补全)
|
|
||||||
|
|
||||||
### 3.1 PROACTIVE(主动模式)
|
|
||||||
|
|
||||||
- **引用数**:37
|
|
||||||
- **功能**:Tick 驱动的自主代理,定时唤醒执行工作,配合 SleepTool 控制节奏
|
|
||||||
- **当前状态**:核心模块 `src/proactive/index.ts` **全部 stub**(activate/deactivate/pause 返回 false 或空操作)
|
|
||||||
- **依赖**:与 KAIROS 强绑定(所有检查都是 `feature('PROACTIVE') || feature('KAIROS')`)
|
|
||||||
- **补全工作量**:中等 — 需要实现 tick 生成、SleepTool 集成、暂停/恢复逻辑
|
|
||||||
|
|
||||||
### 3.2 BASH_CLASSIFIER(Bash 命令分类器)
|
|
||||||
|
|
||||||
- **引用数**:45
|
|
||||||
- **功能**:LLM 驱动的 bash 命令意图分类(允许/拒绝/询问)
|
|
||||||
- **当前状态**:`bashClassifier.ts` **全部 stub**(`matches: false`)
|
|
||||||
- **补全工作量**:大 — 需要 LLM 调用实现、prompt 设计
|
|
||||||
|
|
||||||
### 3.3 ULTRAPLAN(增强规划)
|
|
||||||
|
|
||||||
- **引用数**:10
|
|
||||||
- **功能**:关键字触发增强计划模式,输入 "ultraplan" 自动转为 plan
|
|
||||||
- **当前状态**:关键字检测**完整实现**,`/ultraplan` 命令**为 stub**
|
|
||||||
- **补全工作量**:小 — 只需实现命令处理逻辑
|
|
||||||
|
|
||||||
### 3.4 EXPERIMENTAL_SKILL_SEARCH(技能语义搜索)
|
|
||||||
|
|
||||||
- **引用数**:21
|
|
||||||
- **功能**:DiscoverSkills 工具,根据当前任务语义搜索可用技能
|
|
||||||
- **当前状态**:布线完整,核心搜索逻辑 stub
|
|
||||||
- **补全工作量**:中等 — 需要实现搜索引擎和索引
|
|
||||||
|
|
||||||
### 3.5 CONTEXT_COLLAPSE(上下文折叠)
|
|
||||||
|
|
||||||
- **引用数**:20
|
|
||||||
- **功能**:CtxInspectTool 让模型内省上下文窗口大小,优化压缩决策
|
|
||||||
- **当前状态**:工具 stub,HISTORY_SNIP 子功能也 stub
|
|
||||||
- **补全工作量**:中等
|
|
||||||
|
|
||||||
### 3.6 WORKFLOW_SCRIPTS(工作流自动化)
|
|
||||||
|
|
||||||
- **引用数**:10
|
|
||||||
- **功能**:基于文件的自动化工作流 + `/workflows` 命令
|
|
||||||
- **当前状态**:WorkflowTool、命令、加载器全部 stub
|
|
||||||
- **补全工作量**:大 — 需要从零设计工作流 DSL
|
|
||||||
|
|
||||||
### 3.7 WEB_BROWSER_TOOL(浏览器工具)
|
|
||||||
|
|
||||||
- **引用数**:4
|
|
||||||
- **功能**:模型可调用浏览器工具导航和交互网页
|
|
||||||
- **当前状态**:工具注册存在,实现 stub
|
|
||||||
- **补全工作量**:大
|
|
||||||
|
|
||||||
### 3.8 DAEMON(后台守护进程)
|
|
||||||
|
|
||||||
- **引用数**:3
|
|
||||||
- **功能**:后台守护进程 + 远程控制服务器
|
|
||||||
- **当前状态**:只有条件导入布线,无实现
|
|
||||||
- **补全工作量**:极大
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 四、Tier 3 — 纯 Stub / N/A(低优先级)
|
|
||||||
|
|
||||||
| Feature | 引用 | 状态 | 说明 |
|
|
||||||
|---------|------|------|------|
|
|
||||||
| CHICAGO_MCP | 16 | N/A | Anthropic 内部 MCP 基础设施 |
|
|
||||||
| UDS_INBOX | 17 | Stub | Unix 域套接字对等消息 |
|
|
||||||
| MONITOR_TOOL | 13 | Stub | 文件/进程监控工具 |
|
|
||||||
| BG_SESSIONS | 11 | Stub | 后台会话管理 |
|
|
||||||
| SHOT_STATS | 10 | 无实现 | 逐 prompt 统计 |
|
|
||||||
| EXTRACT_MEMORIES | 7 | 无实现 | 自动记忆提取 |
|
|
||||||
| TEMPLATES | 6 | Stub | 项目/提示模板 |
|
|
||||||
| LODESTONE | 6 | N/A | 内部基础设施 |
|
|
||||||
| STREAMLINED_OUTPUT | 1 | — | 精简输出模式 |
|
|
||||||
| HOOK_PROMPTS | 1 | — | Hook 提示词 |
|
|
||||||
| CCR_AUTO_CONNECT | 3 | — | CCR 自动连接 |
|
|
||||||
| CCR_MIRROR | 4 | — | CCR 镜像模式 |
|
|
||||||
| CCR_REMOTE_SETUP | 1 | — | CCR 远程设置 |
|
|
||||||
| NATIVE_CLIPBOARD_IMAGE | 2 | — | 原生剪贴板图片 |
|
|
||||||
| CONNECTOR_TEXT | 7 | — | 连接器文本 |
|
|
||||||
|
|
||||||
以及其余 40+ 个低引用量 feature。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 五、探索路线图
|
|
||||||
|
|
||||||
### Phase 1:快速验证(无外部依赖)
|
|
||||||
|
|
||||||
> 目标:确认代码可以正常运行,体验基本功能
|
|
||||||
|
|
||||||
| 优先级 | Feature | 命令 | 预期效果 |
|
|
||||||
|--------|---------|------|----------|
|
|
||||||
| 1 | BUDDY | `FEATURE_BUDDY=1 bun run dev` | `/buddy hatch` 生成伙伴 |
|
|
||||||
| 2 | FORK_SUBAGENT | `FEATURE_FORK_SUBAGENT=1 bun run dev` | Agent 可生成上下文继承的子任务 |
|
|
||||||
| 3 | TOKEN_BUDGET | `FEATURE_TOKEN_BUDGET=1 bun run dev` | 输入 "spend 500k tokens" 测试自动持续 |
|
|
||||||
| 4 | TREE_SITTER_BASH | `FEATURE_TREE_SITTER_BASH=1 bun run dev` | 更精确的 bash 权限匹配 |
|
|
||||||
| 5 | MCP_SKILLS | `FEATURE_MCP_SKILLS=1 bun run dev` | MCP 服务器 prompt 提升为技能 |
|
|
||||||
|
|
||||||
### Phase 2:核心功能探索(需要 OAuth)
|
|
||||||
|
|
||||||
> 目标:体验 KAIROS 全套能力
|
|
||||||
|
|
||||||
| 优先级 | Feature | 命令 | 预期效果 |
|
|
||||||
|--------|---------|------|----------|
|
|
||||||
| 1 | TRANSCRIPT_CLASSIFIER | `FEATURE_TRANSCRIPT_CLASSIFIER=1 bun run dev` | Auto mode 自动激活 |
|
|
||||||
| 2 | KAIROS 全套 | `FEATURE_KAIROS=1 FEATURE_KAIROS_BRIEF=1 FEATURE_KAIROS_CHANNELS=1 FEATURE_PROACTIVE=1 bun run dev` | 常驻助手 + Brief 输出 + 频道消息 |
|
|
||||||
| 3 | VOICE_MODE | `FEATURE_VOICE_MODE=1 bun run dev` | 按空格说话 |
|
|
||||||
| 4 | TEAMMEM | `FEATURE_TEAMMEM=1 bun run dev` | 团队记忆同步 |
|
|
||||||
| 5 | COORDINATOR_MODE | `FEATURE_COORDINATOR_MODE=1 CLAUDE_CODE_COORDINATOR_MODE=1 bun run dev` | 多 agent 编排 |
|
|
||||||
|
|
||||||
### Phase 3:Stub 补全开发
|
|
||||||
|
|
||||||
> 目标:将高价值 stub 实现为可用功能
|
|
||||||
|
|
||||||
| 优先级 | Feature | 补全难度 | 价值 |
|
|
||||||
|--------|---------|----------|------|
|
|
||||||
| 1 | PROACTIVE | 中 | 自主工作能力 |
|
|
||||||
| 2 | ULTRAPLAN | 小 | 增强规划 |
|
|
||||||
| 3 | CONTEXT_COLLAPSE | 中 | 长对话优化 |
|
|
||||||
| 4 | EXPERIMENTAL_SKILL_SEARCH | 中 | 技能发现 |
|
|
||||||
| 5 | BASH_CLASSIFIER | 大 | 安全增强 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 六、推荐组合方案
|
|
||||||
|
|
||||||
### "全功能助手"组合
|
|
||||||
|
|
||||||
```bash
|
|
||||||
FEATURE_KAIROS=1 \
|
|
||||||
FEATURE_KAIROS_BRIEF=1 \
|
|
||||||
FEATURE_KAIROS_CHANNELS=1 \
|
|
||||||
FEATURE_KAIROS_PUSH_NOTIFICATION=1 \
|
|
||||||
FEATURE_PROACTIVE=1 \
|
|
||||||
FEATURE_FORK_SUBAGENT=1 \
|
|
||||||
FEATURE_TOKEN_BUDGET=1 \
|
|
||||||
FEATURE_TRANSCRIPT_CLASSIFIER=1 \
|
|
||||||
FEATURE_BUDDY=1 \
|
|
||||||
bun run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### "多 Agent 协作"组合
|
|
||||||
|
|
||||||
```bash
|
|
||||||
FEATURE_COORDINATOR_MODE=1 \
|
|
||||||
FEATURE_FORK_SUBAGENT=1 \
|
|
||||||
FEATURE_BRIDGE_MODE=1 \
|
|
||||||
FEATURE_BG_SESSIONS=1 \
|
|
||||||
CLAUDE_CODE_COORDINATOR_MODE=1 \
|
|
||||||
bun run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### "开发者增强"组合
|
|
||||||
|
|
||||||
```bash
|
|
||||||
FEATURE_TRANSCRIPT_CLASSIFIER=1 \
|
|
||||||
FEATURE_TREE_SITTER_BASH=1 \
|
|
||||||
FEATURE_TOKEN_BUDGET=1 \
|
|
||||||
FEATURE_MCP_SKILLS=1 \
|
|
||||||
FEATURE_CONTEXT_COLLAPSE=1 \
|
|
||||||
bun run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 七、风险与注意事项
|
|
||||||
|
|
||||||
1. **OAuth 依赖**:KAIROS、VOICE_MODE、TEAMMEM、BRIDGE_MODE 需要 Anthropic OAuth 认证(claude.ai 订阅),API key 用户无法使用
|
|
||||||
2. **GrowthBook 门控**:部分功能(VOICE_MODE 的 `tengu_cobalt_frost`、TEAMMEM 的 `tengu_herring_clock`)即使 feature flag 开启,还需要服务端 GrowthBook 开关
|
|
||||||
3. **反编译不完整**:所有"已实现"功能均为反编译产物,可能存在运行时错误,需要逐个验证
|
|
||||||
4. **Proactive stub**:KAIROS 的自主工作能力依赖 PROACTIVE,但 PROACTIVE 核心是 stub,需先补全
|
|
||||||
5. **tsc 错误**:代码库有 ~1341 个 TypeScript 编译错误(来自反编译),不影响 Bun 运行时但在 IDE 中会有大量红线
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 附录:Feature Flag 完整列表
|
|
||||||
|
|
||||||
共 89 个 feature flag(按引用数降序):
|
|
||||||
|
|
||||||
| Feature | 引用 | Tier |
|
|
||||||
|---------|------|------|
|
|
||||||
| KAIROS | 154 | 1 |
|
|
||||||
| TRANSCRIPT_CLASSIFIER | 108 | 1 |
|
|
||||||
| TEAMMEM | 51 | 1 |
|
|
||||||
| VOICE_MODE | 46 | 1 |
|
|
||||||
| BASH_CLASSIFIER | 45 | 2 |
|
|
||||||
| KAIROS_BRIEF | 39 | 1 |
|
|
||||||
| PROACTIVE | 37 | 2 |
|
|
||||||
| COORDINATOR_MODE | 32 | 1 |
|
|
||||||
| BRIDGE_MODE | 28 | 1 |
|
|
||||||
| EXPERIMENTAL_SKILL_SEARCH | 21 | 2 |
|
|
||||||
| CONTEXT_COLLAPSE | 20 | 2 |
|
|
||||||
| KAIROS_CHANNELS | 19 | 1 |
|
|
||||||
| UDS_INBOX | 17 | 3 |
|
|
||||||
| CHICAGO_MCP | 16 | 3 |
|
|
||||||
| BUDDY | 16 | 1 |
|
|
||||||
| HISTORY_SNIP | 15 | 2 |
|
|
||||||
| MONITOR_TOOL | 13 | 3 |
|
|
||||||
| COMMIT_ATTRIBUTION | 12 | — |
|
|
||||||
| CACHED_MICROCOMPACT | 12 | — |
|
|
||||||
| BG_SESSIONS | 11 | 3 |
|
|
||||||
| WORKFLOW_SCRIPTS | 10 | 2 |
|
|
||||||
| ULTRAPLAN | 10 | 2 |
|
|
||||||
| SHOT_STATS | 10 | 3 |
|
|
||||||
| TOKEN_BUDGET | 9 | 1 |
|
|
||||||
| PROMPT_CACHE_BREAK_DETECTION | 9 | — |
|
|
||||||
| MCP_SKILLS | 9 | 1 |
|
|
||||||
| EXTRACT_MEMORIES | 7 | 3 |
|
|
||||||
| CONNECTOR_TEXT | 7 | — |
|
|
||||||
| TEMPLATES | 6 | 3 |
|
|
||||||
| LODESTONE | 6 | 3 |
|
|
||||||
| TREE_SITTER_BASH_SHADOW | 5 | — |
|
|
||||||
| QUICK_SEARCH | 5 | — |
|
|
||||||
| MESSAGE_ACTIONS | 5 | — |
|
|
||||||
| DOWNLOAD_USER_SETTINGS | 5 | — |
|
|
||||||
| DIRECT_CONNECT | 5 | — |
|
|
||||||
| WEB_BROWSER_TOOL | 4 | 2 |
|
|
||||||
| VERIFICATION_AGENT | 4 | — |
|
|
||||||
| TERMINAL_PANEL | 4 | — |
|
|
||||||
| SSH_REMOTE | 4 | — |
|
|
||||||
| REVIEW_ARTIFACT | 4 | — |
|
|
||||||
| REACTIVE_COMPACT | 4 | — |
|
|
||||||
| KAIROS_PUSH_NOTIFICATION | 4 | 1 |
|
|
||||||
| HISTORY_PICKER | 4 | — |
|
|
||||||
| FORK_SUBAGENT | 4 | 1 |
|
|
||||||
| CCR_MIRROR | 4 | — |
|
|
||||||
| TREE_SITTER_BASH | 3 | 1 |
|
|
||||||
| MEMORY_SHAPE_TELEMETRY | 3 | — |
|
|
||||||
| MCP_RICH_OUTPUT | 3 | — |
|
|
||||||
| KAIROS_GITHUB_WEBHOOKS | 3 | 1 |
|
|
||||||
| FILE_PERSISTENCE | 3 | — |
|
|
||||||
| DAEMON | 3 | 2 |
|
|
||||||
| CCR_AUTO_CONNECT | 3 | — |
|
|
||||||
| UPLOAD_USER_SETTINGS | 2 | — |
|
|
||||||
| POWERSHELL_AUTO_MODE | 2 | — |
|
|
||||||
| OVERFLOW_TEST_TOOL | 2 | — |
|
|
||||||
| NEW_INIT | 2 | — |
|
|
||||||
| NATIVE_CLIPBOARD_IMAGE | 2 | — |
|
|
||||||
| HARD_FAIL | 2 | — |
|
|
||||||
| ENHANCED_TELEMETRY_BETA | 2 | — |
|
|
||||||
| COWORKER_TYPE_TELEMETRY | 2 | — |
|
|
||||||
| BREAK_CACHE_COMMAND | 2 | — |
|
|
||||||
| AWAY_SUMMARY | 2 | — |
|
|
||||||
| AUTO_THEME | 2 | — |
|
|
||||||
| ALLOW_TEST_VERSIONS | 2 | — |
|
|
||||||
| AGENT_TRIGGERS_REMOTE | 2 | — |
|
|
||||||
| AGENT_MEMORY_SNAPSHOT | 2 | — |
|
|
||||||
| UNATTENDED_RETRY | 1 | — |
|
|
||||||
| ULTRATHINK | 1 | — |
|
|
||||||
| TORCH | 1 | — |
|
|
||||||
| STREAMLINED_OUTPUT | 1 | — |
|
|
||||||
| SLOW_OPERATION_LOGGING | 1 | — |
|
|
||||||
| SKILL_IMPROVEMENT | 1 | — |
|
|
||||||
| SELF_HOSTED_RUNNER | 1 | — |
|
|
||||||
| RUN_SKILL_GENERATOR | 1 | — |
|
|
||||||
| PERFETTO_TRACING | 1 | — |
|
|
||||||
| NATIVE_CLIENT_ATTESTATION | 1 | — |
|
|
||||||
| KAIROS_DREAM | 1 | 1 |
|
|
||||||
| IS_LIBC_MUSL | 1 | — |
|
|
||||||
| IS_LIBC_GLIBC | 1 | — |
|
|
||||||
| HOOK_PROMPTS | 1 | — |
|
|
||||||
| DUMP_SYSTEM_PROMPT | 1 | — |
|
|
||||||
| COMPACTION_REMINDERS | 1 | — |
|
|
||||||
| CCR_REMOTE_SETUP | 1 | — |
|
|
||||||
| BYOC_ENVIRONMENT_RUNNER | 1 | — |
|
|
||||||
| BUILTIN_EXPLORE_PLAN_AGENTS | 1 | — |
|
|
||||||
| BUILDING_CLAUDE_APPS | 1 | — |
|
|
||||||
| ANTI_DISTILLATION_CC | 1 | — |
|
|
||||||
| AGENT_TRIGGERS | 1 | — |
|
|
||||||
| ABLATION_BASELINE | 1 | — |
|
|
||||||
File diff suppressed because it is too large
Load Diff
207
docs/features/acp-link.md
Normal file
207
docs/features/acp-link.md
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
# acp-link — ACP 代理服务器
|
||||||
|
|
||||||
|
> 源码目录:`packages/acp-link/`
|
||||||
|
> PR: #292
|
||||||
|
> 新增时间:2026-04-18
|
||||||
|
|
||||||
|
## 一、功能概述
|
||||||
|
|
||||||
|
`acp-link` 是一个 ACP (Agent Client Protocol) 代理服务器,将 WebSocket 客户端桥接到 ACP agent 的 stdio 接口。它让 ACP agent(如 Claude Code)可以通过 WebSocket 远程访问,而不仅限于本地 stdio。
|
||||||
|
|
||||||
|
### 核心特性
|
||||||
|
|
||||||
|
- **WebSocket → stdio 桥接**:将浏览器/远程客户端的 WebSocket 连接转换为 ACP agent 的 stdin/stdout NDJSON 流
|
||||||
|
- **会话管理**:创建、加载、恢复、列出、关闭会话
|
||||||
|
- **权限审批流程**:客户端可远程审批 agent 的工具权限请求
|
||||||
|
- **RCS 集成**:可与 Remote Control Server (RCS) 连接,将 ACP agent 注册到 RCS 并通过 Web UI 交互
|
||||||
|
- **HTTPS 支持**:内置自签名证书生成,支持安全连接
|
||||||
|
- **Token 认证**:自动生成或通过环境变量配置认证 token
|
||||||
|
|
||||||
|
## 二、架构
|
||||||
|
|
||||||
|
### 独立模式
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────┐ WebSocket ┌──────────────────┐ stdio/NDJSON ┌──────────────┐
|
||||||
|
│ 浏览器/客户端 │ ◄──────────────►│ acp-link │ ◄────────────────►│ ACP Agent │
|
||||||
|
│ (WS Client) │ ws://host:port │ (Proxy Server) │ spawn subprocess │ (Claude等) │
|
||||||
|
└──────────────────┘ └──────────────────┘ └──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### RCS 集成模式
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────┐ WebSocket ┌──────────────────┐ stdio/NDJSON ┌──────────────┐
|
||||||
|
│ RCS Web UI │ ◄──────────────►│ Remote Control │ ◄─────────────────►│ acp-link │
|
||||||
|
│ (/code/*) │ ACP Relay WS │ Server (RCS) │ ACP events │ + Agent │
|
||||||
|
└──────────────┘ └──────────────────┘ └──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
packages/acp-link/
|
||||||
|
├── src/
|
||||||
|
│ ├── server.ts # 主服务器:WS 连接管理、会话管理、权限处理、消息桥接
|
||||||
|
│ ├── rcs-upstream.ts # RCS 上游客户端:REST 注册 + WS identify 两步流程
|
||||||
|
│ ├── cert.ts # TLS 证书生成(自签名)
|
||||||
|
│ ├── logger.ts # 日志模块
|
||||||
|
│ ├── types.ts # JSON-RPC 和 ACP 协议类型定义
|
||||||
|
│ ├── cli/
|
||||||
|
│ │ ├── bin.ts # CLI 入口
|
||||||
|
│ │ ├── command.ts # 命令行参数解析
|
||||||
|
│ │ ├── app.ts # 应用启动
|
||||||
|
│ │ └── context.ts # 上下文配置
|
||||||
|
│ └── __tests__/ # 测试(cert, server, types)
|
||||||
|
├── package.json
|
||||||
|
└── tsconfig.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三、安装与使用
|
||||||
|
|
||||||
|
### 基本用法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 直接运行(在 monorepo 中)
|
||||||
|
# 注意:claude 本身不支持 ACP,需要用 ccb-bun --acp 启动 ACP agent
|
||||||
|
bun packages/acp-link/src/cli/bin.ts ccb-bun -- --acp
|
||||||
|
|
||||||
|
# 指定端口和主机
|
||||||
|
acp-link --port 9000 --host 0.0.0.0 ccb-bun -- --acp
|
||||||
|
|
||||||
|
# 启用 HTTPS(自签名证书)
|
||||||
|
acp-link --https ccb-bun -- --acp
|
||||||
|
|
||||||
|
# 调试模式
|
||||||
|
acp-link --debug ccb-bun -- --acp
|
||||||
|
```
|
||||||
|
|
||||||
|
### CLI 参考
|
||||||
|
|
||||||
|
```
|
||||||
|
USAGE
|
||||||
|
acp-link [--port value] [--host value] [--debug] [--no-auth] [--https] <command>...
|
||||||
|
acp-link --help
|
||||||
|
acp-link --version
|
||||||
|
|
||||||
|
FLAGS
|
||||||
|
[--port] Port to listen on [default = 9315]
|
||||||
|
[--host] Host to bind to [default = localhost]
|
||||||
|
[--debug] Enable debug logging to file
|
||||||
|
[--no-auth] Disable authentication (dangerous)
|
||||||
|
[--https] Enable HTTPS with self-signed cert
|
||||||
|
-h --help Print help information and exit
|
||||||
|
-v --version Print version information and exit
|
||||||
|
|
||||||
|
ARGUMENTS
|
||||||
|
command... Agent command followed by its arguments (e.g. "ccb-bun -- --acp")
|
||||||
|
```
|
||||||
|
|
||||||
|
## 四、认证
|
||||||
|
|
||||||
|
默认启动时自动生成随机 token。客户端连接时不要把 token 放在 URL 中:
|
||||||
|
|
||||||
|
```
|
||||||
|
ws://localhost:9315/ws
|
||||||
|
```
|
||||||
|
|
||||||
|
无法发送 `Authorization` header 的 WebSocket 客户端需要使用
|
||||||
|
`rcs.auth.<base64url-token>` 子协议传递 token。
|
||||||
|
|
||||||
|
配置固定 token:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ACP_AUTH_TOKEN=my-fixed-token acp-link ccb-bun -- --acp
|
||||||
|
```
|
||||||
|
|
||||||
|
禁用认证(不推荐,仅用于开发):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
acp-link --no-auth ccb-bun -- --acp
|
||||||
|
```
|
||||||
|
|
||||||
|
## 五、RCS 集成
|
||||||
|
|
||||||
|
acp-link 支持将 ACP agent 注册到 Remote Control Server,通过 Web UI 远程操控。
|
||||||
|
|
||||||
|
### 连接方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 通过环境变量配置 RCS 连接
|
||||||
|
ACP_RCS_URL=http://localhost:3000 \
|
||||||
|
ACP_RCS_TOKEN=sk-rcs-your-key \
|
||||||
|
acp-link ccb-bun -- --acp
|
||||||
|
```
|
||||||
|
|
||||||
|
### 注册流程(两步)
|
||||||
|
|
||||||
|
1. **REST 注册**:通过 `POST /v1/environments/bridge` 向 RCS 注册环境
|
||||||
|
2. **WS identify**:建立 WebSocket 连接后发送 `identify` 消息(携带 agentId),替代完整 `register`
|
||||||
|
|
||||||
|
RCS 的 ACP WebSocket 连接不接受 URL query token。acp-link 会通过
|
||||||
|
`rcs.auth.<base64url-token>` WebSocket 子协议发送 `ACP_RCS_TOKEN`。
|
||||||
|
|
||||||
|
```
|
||||||
|
acp-link RCS
|
||||||
|
│ │
|
||||||
|
│── POST /v1/environments/bridge ──►│ (REST 注册)
|
||||||
|
│◄── { agentId, sessionId } ───────│
|
||||||
|
│ │
|
||||||
|
│── WS connect ─────────────────►│ (WebSocket)
|
||||||
|
│── identify { agentId } ────────►│ (WS 标识)
|
||||||
|
│◄── identified ─────────────────│
|
||||||
|
│ │
|
||||||
|
│── ACP events ─────────────────►│ (双向消息转发)
|
||||||
|
│◄── user prompts/permissions ───│
|
||||||
|
```
|
||||||
|
|
||||||
|
## 六、权限模式
|
||||||
|
|
||||||
|
### permissionMode 传递链
|
||||||
|
|
||||||
|
权限模式通过整条链路传递:Web UI → RCS → acp-link → ACP agent。
|
||||||
|
|
||||||
|
支持的权限模式:
|
||||||
|
- `default` — 每次请求权限确认
|
||||||
|
- `auto` — 自动判断
|
||||||
|
- `acceptEdits` — 自动接受编辑
|
||||||
|
- `plan` — 规划模式
|
||||||
|
- `dontAsk` — 不询问
|
||||||
|
- `bypassPermissions` — 绕过权限(需 sandbox 环境)
|
||||||
|
|
||||||
|
### fallback 链
|
||||||
|
|
||||||
|
当客户端未显式传递 permissionMode 时,使用以下 fallback 链:
|
||||||
|
|
||||||
|
```
|
||||||
|
客户端传值 > config.permissionMode > ACP_PERMISSION_MODE 环境变量
|
||||||
|
```
|
||||||
|
|
||||||
|
示例:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ACP_PERMISSION_MODE=auto acp-link ccb-bun -- --acp
|
||||||
|
```
|
||||||
|
|
||||||
|
## 七、权限管道(2026-04-18 改进)
|
||||||
|
|
||||||
|
### 模式同步
|
||||||
|
|
||||||
|
`applySessionMode` 在 agent 切换权限模式时同步 `appState.toolPermissionContext.mode`,确保内部权限上下文与 ACP 客户端状态一致。
|
||||||
|
|
||||||
|
### 统一权限流水线
|
||||||
|
|
||||||
|
`createAcpCanUseTool` 接入 `hasPermissionsToUseTool` 统一权限流水线,替代原来分散的处理逻辑。支持 `onModeChange` 回调,模式变更时实时同步。
|
||||||
|
|
||||||
|
### bypass 检测
|
||||||
|
|
||||||
|
`bypassPermissions` 模式增加可用性检测 — 仅在非 root 或 sandbox 环境中允许启用,防止权限绕过的安全风险。
|
||||||
|
|
||||||
|
## 八、环境变量
|
||||||
|
|
||||||
|
| 变量 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `ACP_AUTH_TOKEN` | 固定认证 token(默认自动生成) |
|
||||||
|
| `ACP_PERMISSION_MODE` | 默认权限模式 fallback |
|
||||||
|
| `ACP_RCS_URL` | RCS 服务器地址(启用 RCS 集成) |
|
||||||
|
| `ACP_RCS_TOKEN` | RCS API token |
|
||||||
189
docs/features/acp-zed.md
Normal file
189
docs/features/acp-zed.md
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
# ACP (Agent Client Protocol) — Zed / IDE 集成
|
||||||
|
|
||||||
|
> Feature Flag: `FEATURE_ACP=1`(build 和 dev 模式默认启用)
|
||||||
|
> 实现状态:可用(支持 Zed、Cursor 等 ACP 客户端)
|
||||||
|
> 源码目录:`src/services/acp/`
|
||||||
|
|
||||||
|
## 一、功能概述
|
||||||
|
|
||||||
|
ACP (Agent Client Protocol) 是一种标准化的 stdio 协议,允许 IDE 和编辑器通过 stdin/stdout 的 NDJSON 流驱动 AI Agent。CCB 实现了完整的 ACP agent 端,可以被 Zed、Cursor 等支持 ACP 的客户端直接调用。
|
||||||
|
|
||||||
|
### 核心特性
|
||||||
|
|
||||||
|
- **会话管理**:新建 / 恢复 / 加载 / 分叉 / 关闭会话
|
||||||
|
- **历史回放**:恢复会话时自动加载并回放对话历史
|
||||||
|
- **权限桥接**:ACP 客户端的权限决策映射到 CCB 的工具权限系统
|
||||||
|
- **斜杠命令 & Skills**:加载真实命令列表,支持 `/commit`、`/review` 等 prompt 型 skill
|
||||||
|
- **Context Window 跟踪**:精确的 usage_update,含 model prefix matching
|
||||||
|
- **Prompt 排队**:支持连续发送多条 prompt,自动排队处理
|
||||||
|
- **模式切换**:auto / default / acceptEdits / plan / dontAsk / bypassPermissions
|
||||||
|
- **模型切换**:运行时切换 AI 模型
|
||||||
|
|
||||||
|
## 二、架构
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────┐ NDJSON/stdio ┌──────────────────┐
|
||||||
|
│ Zed / IDE │ ◄────────────────► │ CCB ACP Agent │
|
||||||
|
│ (Client) │ stdin / stdout │ (Agent) │
|
||||||
|
└──────────────┘ │ │
|
||||||
|
│ entry.ts │ ← stdio → NDJSON stream
|
||||||
|
│ agent.ts │ ← ACP protocol handler
|
||||||
|
│ bridge.ts │ ← SDKMessage → ACP SessionUpdate
|
||||||
|
│ permissions.ts │ ← 权限桥接
|
||||||
|
│ utils.ts │ ← 通用工具
|
||||||
|
│ │
|
||||||
|
│ QueryEngine │ ← 内部查询引擎
|
||||||
|
└──────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文件职责
|
||||||
|
|
||||||
|
| 文件 | 职责 |
|
||||||
|
|------|------|
|
||||||
|
| `entry.ts` | 入口,创建 stdio → NDJSON stream,启动 `AgentSideConnection` |
|
||||||
|
| `agent.ts` | 实现 ACP `Agent` 接口:会话 CRUD、prompt、cancel、模式/模型切换 |
|
||||||
|
| `bridge.ts` | `SDKMessage` → ACP `SessionUpdate` 转换:文本/思考/工具/用量/编辑 diff |
|
||||||
|
| `permissions.ts` | ACP `requestPermission()` → CCB `CanUseToolFn` 桥接 |
|
||||||
|
| `utils.ts` | Pushable、流转换、权限模式解析、session fingerprint、路径显示 |
|
||||||
|
|
||||||
|
## 三、配置 Zed 编辑器
|
||||||
|
|
||||||
|
### 3.1 Zed settings.json 配置
|
||||||
|
|
||||||
|
打开 Zed 的 `settings.json`(`Cmd+,` → Open Settings),添加 `agent_servers` 配置:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"agent_servers": {
|
||||||
|
"ccb": {
|
||||||
|
"type": "custom",
|
||||||
|
"command": "ccb",
|
||||||
|
"args": ["--acp"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 API 认证配置
|
||||||
|
|
||||||
|
CCB 的 ACP agent 在启动时会自动加载 `settings.json` 中的环境变量(`ANTHROPIC_BASE_URL`、`ANTHROPIC_AUTH_TOKEN` 等)。确保已通过 `/login` 配置好 API 供应商。
|
||||||
|
|
||||||
|
也可通过环境变量传入:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"agent_servers": {
|
||||||
|
"claude-code": {
|
||||||
|
"command": "ccb",
|
||||||
|
"args": ["--acp"],
|
||||||
|
"env": {
|
||||||
|
"ANTHROPIC_BASE_URL": "https://api.example.com/v1",
|
||||||
|
"ANTHROPIC_AUTH_TOKEN": "sk-xxx"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 在 Zed 中使用
|
||||||
|
|
||||||
|
1. 配置完成后重启 Zed
|
||||||
|
2. 打开任意项目目录
|
||||||
|
3. 按 `Cmd+'`(macOS)或 `Ctrl+'`(Linux)打开 Agent Panel
|
||||||
|
4. 在 Agent Panel 顶部的下拉菜单中选择 **claude-code**
|
||||||
|
5. 开始对话
|
||||||
|
|
||||||
|
### 3.5 功能说明
|
||||||
|
|
||||||
|
| 功能 | 操作 |
|
||||||
|
|------|------|
|
||||||
|
| 对话 | 在 Agent Panel 中直接输入消息 |
|
||||||
|
| 斜杠命令 | 输入 `/` 查看可用 skills 列表(如 `/commit`、`/review`) |
|
||||||
|
| 工具权限 | 弹出权限请求时选择 Allow / Reject / Always Allow |
|
||||||
|
| 模式切换 | 通过 Agent Panel 的设置菜单切换 auto/default/plan 等模式 |
|
||||||
|
| 模型切换 | 通过 Agent Panel 的设置菜单切换 AI 模型 |
|
||||||
|
| 会话恢复 | 关闭重开 Zed 后,之前的会话可自动恢复(含历史消息) |
|
||||||
|
|
||||||
|
## 四、配置其他 ACP 客户端
|
||||||
|
|
||||||
|
ACP 是开放协议,任何支持 ACP 的客户端都可以连接 CCB。通用配置模式:
|
||||||
|
|
||||||
|
```
|
||||||
|
命令: ccb --acp
|
||||||
|
参数: ["--acp"]
|
||||||
|
通信: stdin/stdout NDJSON
|
||||||
|
协议版本: ACP v1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.1 Cursor
|
||||||
|
|
||||||
|
在 Cursor 的设置中配置 MCP / Agent Server,使用同样的 `ccb --acp` 命令。
|
||||||
|
|
||||||
|
### 4.2 自定义客户端
|
||||||
|
|
||||||
|
使用 `@agentclientprotocol/sdk` 可以快速构建 ACP 客户端:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ClientSideConnection, ndJsonStream } from '@agentclientprotocol/sdk'
|
||||||
|
|
||||||
|
// 创建连接(将 ccb --acp 作为子进程启动)
|
||||||
|
const child = spawn('ccb', ['--acp'])
|
||||||
|
const stream = ndJsonStream(
|
||||||
|
Writable.toWeb(child.stdin),
|
||||||
|
Readable.toWeb(child.stdout),
|
||||||
|
)
|
||||||
|
|
||||||
|
const client = new ClientSideConnection(stream)
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
await client.initialize({ clientCapabilities: {} })
|
||||||
|
|
||||||
|
// 创建会话
|
||||||
|
const { sessionId } = await client.newSession({
|
||||||
|
cwd: '/path/to/project',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 发送 prompt
|
||||||
|
const response = await client.prompt({
|
||||||
|
sessionId,
|
||||||
|
prompt: [{ type: 'text', text: 'Hello, explain this project' }],
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听 session 更新
|
||||||
|
client.on('sessionUpdate', (update) => {
|
||||||
|
console.log('Update:', update)
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## 五、ACP 协议支持矩阵
|
||||||
|
|
||||||
|
| 方法 | 状态 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `initialize` | ✅ | 返回 agent 信息和能力 |
|
||||||
|
| `authenticate` | ✅ | 无需认证(自托管) |
|
||||||
|
| `newSession` | ✅ | 创建新会话 |
|
||||||
|
| `resumeSession` | ✅ | 恢复已有会话(含历史回放) |
|
||||||
|
| `loadSession` | ✅ | 加载指定会话(含历史回放) |
|
||||||
|
| `listSessions` | ✅ | 列出可用会话 |
|
||||||
|
| `forkSession` | ✅ | 分叉会话 |
|
||||||
|
| `closeSession` | ✅ | 关闭会话 |
|
||||||
|
| `prompt` | ✅ | 发送消息,支持排队 |
|
||||||
|
| `cancel` | ✅ | 取消当前/排队的 prompt |
|
||||||
|
| `setSessionMode` | ✅ | 切换权限模式 |
|
||||||
|
| `setSessionModel` | ✅ | 切换 AI 模型 |
|
||||||
|
| `setSessionConfigOption` | ✅ | 动态修改配置 |
|
||||||
|
|
||||||
|
### SessionUpdate 类型
|
||||||
|
|
||||||
|
| 类型 | 状态 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `agent_message_chunk` | ✅ | 助手文本消息 |
|
||||||
|
| `agent_thought_chunk` | ✅ | 思考/推理内容 |
|
||||||
|
| `user_message_chunk` | ✅ | 用户消息(历史回放) |
|
||||||
|
| `tool_call` | ✅ | 工具调用开始 |
|
||||||
|
| `tool_call_update` | ✅ | 工具调用结果/状态更新 |
|
||||||
|
| `usage_update` | ✅ | token 用量 + context window |
|
||||||
|
| `plan` | ✅ | TodoWrite → plan entries |
|
||||||
|
| `available_commands_update` | ✅ | 斜杠命令 & skills 列表 |
|
||||||
|
| `current_mode_update` | ✅ | 模式切换通知 |
|
||||||
|
| `config_option_update` | ✅ | 配置更新通知 |
|
||||||
574
docs/features/all-features-guide.md
Normal file
574
docs/features/all-features-guide.md
Normal file
@@ -0,0 +1,574 @@
|
|||||||
|
# Claude Code Best (CCB) — 全功能使用指南
|
||||||
|
|
||||||
|
本文档覆盖我们通过 13 个 PR 为 CCB 恢复/新增的**全部功能**,按类别组织,每个功能包含说明、使用方法和示例。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
1. [Buddy 伴侣系统](#1-buddy-伴侣系统)
|
||||||
|
2. [Remote Control 远程控制](#2-remote-control-远程控制)
|
||||||
|
3. [定时任务 /schedule](#3-定时任务-schedule)
|
||||||
|
4. [Voice Mode 语音模式](#4-voice-mode-语音模式)
|
||||||
|
5. [Chrome 浏览器控制](#5-chrome-浏览器控制)
|
||||||
|
6. [Computer Use 屏幕操控](#6-computer-use-屏幕操控)
|
||||||
|
7. [Feature Flags 与 GrowthBook](#7-feature-flags-与-growthbook)
|
||||||
|
8. [/ultraplan 高级规划](#8-ultraplan-高级规划)
|
||||||
|
9. [Daemon 后台守护](#9-daemon-后台守护)
|
||||||
|
10. [Pipe IPC 多实例协作](#10-pipe-ipc-多实例协作)
|
||||||
|
11. [LAN Pipes 局域网群控](#11-lan-pipes-局域网群控)
|
||||||
|
12. [Monitor 后台监控](#12-monitor-后台监控)
|
||||||
|
13. [Workflow 工作流脚本](#13-workflow-工作流脚本)
|
||||||
|
14. [Coordinator 多Worker协调](#14-coordinator-多worker协调)
|
||||||
|
15. [Proactive 自主模式](#15-proactive-自主模式)
|
||||||
|
16. [History / Snip 历史管理](#16-history--snip-历史管理)
|
||||||
|
17. [Fork 子Agent](#17-fork-子agent)
|
||||||
|
18. [其他恢复的工具](#18-其他恢复的工具)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Buddy 伴侣系统
|
||||||
|
|
||||||
|
**PR**: #82 `refactor(buddy): align companion system with official CLI`
|
||||||
|
**Feature Flag**: `BUDDY`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
Buddy 是一个后台运行的伴侣 AI,在你主对话进行的同时,异步观察会话内容并提供建议。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```bash
|
||||||
|
# 启动时自动加载(feature 默认开启)
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# 在对话中,Buddy 会在适当时机自动提供建议
|
||||||
|
# 例如当你在调试时,Buddy 可能提示你检查日志
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Remote Control 远程控制
|
||||||
|
|
||||||
|
**PR**: #60 `feat: enable Remote Control (BRIDGE_MODE)` + #170 `feat: restore daemon supervisor`
|
||||||
|
**Feature Flag**: `BRIDGE_MODE`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
通过 WebSocket 远程控制 Claude Code 会话。支持自托管私有部署。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```bash
|
||||||
|
# 启动远程控制模式
|
||||||
|
bun run dev -- remote-control
|
||||||
|
|
||||||
|
# 使用自托管服务器
|
||||||
|
CLAUDE_BRIDGE_BASE_URL=https://your-server.com CLAUDE_BRIDGE_OAUTH_TOKEN=your-token bun run dev --remote-control
|
||||||
|
|
||||||
|
# 或通过 /remote-control 命令在会话中启动
|
||||||
|
/remote-control
|
||||||
|
```
|
||||||
|
|
||||||
|
### 命令
|
||||||
|
- `claude remote-control` / `claude rc` — 启动远程控制客户端
|
||||||
|
- `claude bridge` — 同上(别名)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 定时任务 /schedule
|
||||||
|
|
||||||
|
**PR**: #88 `feat: enable /schedule by adding AGENT_TRIGGERS_REMOTE`
|
||||||
|
**Feature Flag**: `AGENT_TRIGGERS_REMOTE`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
创建定时执行的远程 agent 任务,支持 cron 表达式。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```
|
||||||
|
/schedule create "每天检查依赖更新" --cron "0 9 * * *" --prompt "检查 package.json 中的过期依赖并创建更新 PR"
|
||||||
|
/schedule list — 列出所有定时任务
|
||||||
|
/schedule delete <id> — 删除指定任务
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Voice Mode 语音模式
|
||||||
|
|
||||||
|
**PR**: #92 `feat: enable /voice mode with native audio binaries`
|
||||||
|
**Feature Flag**: `VOICE_MODE`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
Push-to-Talk 语音输入,音频通过 WebSocket 流式传输到 Anthropic STT(Nova 3)。需要 Anthropic OAuth 认证(非 API key)。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```bash
|
||||||
|
# 确保已通过 OAuth 登录
|
||||||
|
claude auth login
|
||||||
|
|
||||||
|
# 在会话中按住指定键说话
|
||||||
|
# 松开后自动转写为文字输入
|
||||||
|
```
|
||||||
|
|
||||||
|
### 前提条件
|
||||||
|
- Anthropic OAuth 认证(不支持 API key 模式)
|
||||||
|
- 系统麦克风权限
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Chrome 浏览器控制
|
||||||
|
|
||||||
|
**PR**: #93 `feat: enable Claude in Chrome MCP with full browser control`
|
||||||
|
**Feature Flag**: `CHICAGO_MCP`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
通过 Chrome 扩展控制浏览器:导航、点击、填表、截图、执行 JS。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```bash
|
||||||
|
# 启动带 Chrome 控制的模式
|
||||||
|
bun run dev -- --chrome
|
||||||
|
|
||||||
|
# 安装 Chrome 扩展后,AI 可以:
|
||||||
|
# - 打开网页、点击按钮
|
||||||
|
# - 填写表单
|
||||||
|
# - 截取页面内容
|
||||||
|
# - 执行 JavaScript
|
||||||
|
```
|
||||||
|
|
||||||
|
### AI 可用工具
|
||||||
|
- `navigate` — 导航到 URL
|
||||||
|
- `click` / `find` / `form_input` — 页面交互
|
||||||
|
- `get_page_text` / `read_page` — 读取内容
|
||||||
|
- `javascript_tool` — 执行 JS
|
||||||
|
- `gif_creator` — 录制操作 GIF
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Computer Use 屏幕操控
|
||||||
|
|
||||||
|
**PR**: #98 + #137 `feat: Computer Use — 跨平台 Executor + Python Bridge + GUI 无障碍`
|
||||||
|
**Feature Flag**: `CHICAGO_MCP`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
跨平台屏幕操控:截图、键鼠模拟、应用管理。支持 macOS + Windows,Linux 后端待完成。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```bash
|
||||||
|
# 启动后 AI 可自动调用屏幕操控工具
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# AI 可以:
|
||||||
|
# - 截取屏幕/窗口截图
|
||||||
|
# - 模拟键盘输入和鼠标操作
|
||||||
|
# - 列出运行的应用
|
||||||
|
# - 使用剪贴板
|
||||||
|
```
|
||||||
|
|
||||||
|
### 平台支持
|
||||||
|
| 平台 | 截图 | 键鼠 | 应用管理 |
|
||||||
|
|------|------|------|----------|
|
||||||
|
| macOS | ✅ | ✅ | ✅ |
|
||||||
|
| Windows | ✅ | ✅ | ✅ |
|
||||||
|
| Linux | ⏳ | ⏳ | ⏳ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Feature Flags 与 GrowthBook
|
||||||
|
|
||||||
|
**PR**: #140 + #153 `feat: enable GrowthBook local gate defaults`
|
||||||
|
**Feature Flags**: `SHOT_STATS`, `PROMPT_CACHE_BREAK_DETECTION`, `TOKEN_BUDGET`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
本地 GrowthBook gate defaults 机制,绕过远程 feature flag 服务,确保功能在无网络时也可使用。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```bash
|
||||||
|
# 通过环境变量启用任意 feature
|
||||||
|
FEATURE_PROACTIVE=1 bun run dev
|
||||||
|
|
||||||
|
# dev/build 模式有各自的默认启用列表
|
||||||
|
# 查看 scripts/dev.ts 中的 DEFAULT_FEATURES
|
||||||
|
```
|
||||||
|
|
||||||
|
### 关键 feature flags
|
||||||
|
| Flag | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `SHOT_STATS` | API 调用统计 |
|
||||||
|
| `TOKEN_BUDGET` | Token 预算控制 |
|
||||||
|
| `PROMPT_CACHE_BREAK_DETECTION` | Prompt 缓存命中检测 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. /ultraplan 高级规划
|
||||||
|
|
||||||
|
**PR**: #156 `feat: enable /ultraplan and harden GrowthBook fallback chain`
|
||||||
|
**Feature Flag**: `ULTRAPLAN`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
高级多 agent 规划模式。将复杂任务分解为多个阶段,每阶段可分配给不同 agent 并行执行。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```
|
||||||
|
/ultraplan 实现一个完整的用户认证系统,包括注册、登录、密码重置、OAuth 集成
|
||||||
|
```
|
||||||
|
|
||||||
|
AI 会生成:
|
||||||
|
1. 任务分解(多阶段)
|
||||||
|
2. 每阶段的 agent 分配
|
||||||
|
3. 依赖关系图
|
||||||
|
4. 并行执行计划
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Daemon 后台守护
|
||||||
|
|
||||||
|
**PR**: #170 `feat: restore daemon supervisor and remoteControlServer command`
|
||||||
|
**Feature Flag**: `DAEMON`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
Daemon 模式允许 Claude Code 作为后台长驻进程运行,管理多个 worker。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```bash
|
||||||
|
# 启动 daemon
|
||||||
|
claude daemon start
|
||||||
|
|
||||||
|
# 查看状态
|
||||||
|
claude daemon status
|
||||||
|
|
||||||
|
# 停止
|
||||||
|
claude daemon stop
|
||||||
|
|
||||||
|
# 启动远程控制服务器
|
||||||
|
bun run rcs
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Pipe IPC 多实例协作
|
||||||
|
|
||||||
|
**PR**: #241 `feat: restore pipe IPC, LAN pipes, monitor tool`
|
||||||
|
**Feature Flag**: `UDS_INBOX`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
同一台机器上的多个 Claude Code 实例通过 UDS(Unix Domain Socket / Windows Named Pipe)自动发现并协作。首个启动的实例成为 main,后续自动注册为 sub。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
|
||||||
|
**启动多实例**:
|
||||||
|
```bash
|
||||||
|
# 终端 1
|
||||||
|
bun run dev
|
||||||
|
# → 自动成为 main
|
||||||
|
|
||||||
|
# 终端 2
|
||||||
|
bun run dev
|
||||||
|
# → 自动成为 sub-1,被 main attach
|
||||||
|
```
|
||||||
|
|
||||||
|
**管理实例**:
|
||||||
|
```
|
||||||
|
/pipes — 显示所有实例,Shift+↓ 展开选择面板
|
||||||
|
/pipes select <name> — 选中实例
|
||||||
|
/pipes all — 全选
|
||||||
|
/pipes none — 取消全选
|
||||||
|
/attach <name> — 手动 attach 某实例
|
||||||
|
/detach <name> — 断开连接
|
||||||
|
/send <name> <msg> — 向指定实例发送消息
|
||||||
|
/claim-main — 强制声明为 main
|
||||||
|
/pipe-status — 显示详细状态
|
||||||
|
/peers — 列出所有已发现的 peer
|
||||||
|
```
|
||||||
|
|
||||||
|
**选择面板操作**:
|
||||||
|
1. 按 `Shift+↓` 展开面板
|
||||||
|
2. `↑/↓` 移动光标
|
||||||
|
3. `Space` 选中/取消 pipe
|
||||||
|
4. `Enter` 确认关闭
|
||||||
|
5. `←/→` 切换路由模式(selected pipes ↔ local main)
|
||||||
|
|
||||||
|
**消息广播**:
|
||||||
|
选中 pipe 后,输入的消息自动路由到所有选中的 slave 执行,结果流式回传到 main。
|
||||||
|
|
||||||
|
**权限转发**:
|
||||||
|
slave 执行需要权限的工具时(如 BashTool),权限请求自动转发到 main 的确认队列。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. LAN Pipes 局域网群控
|
||||||
|
|
||||||
|
**PR**: #241(同上)
|
||||||
|
**Feature Flag**: `LAN_PIPES`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
在 Pipe IPC 基础上增加 TCP 传输层和 UDP Multicast 发现,实现跨机器零配置协作。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
|
||||||
|
**局域网多机器**:
|
||||||
|
```bash
|
||||||
|
# 机器 A (192.168.50.22)
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# 机器 B (192.168.50.27)
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# 两边启动后 3-5 秒自动发现和 attach
|
||||||
|
# /pipes 显示 [LAN] 标记的远端实例
|
||||||
|
```
|
||||||
|
|
||||||
|
**防火墙配置**(每台机器都需要):
|
||||||
|
|
||||||
|
Windows(管理员 PowerShell):
|
||||||
|
```powershell
|
||||||
|
New-NetFirewallRule -DisplayName "CCB LAN Beacon (UDP)" -Direction Inbound -Protocol UDP -LocalPort 7101 -Action Allow -Profile Private
|
||||||
|
New-NetFirewallRule -DisplayName "CCB LAN Pipes (TCP)" -Direction Inbound -Protocol TCP -LocalPort 1024-65535 -Program (Get-Command bun).Source -Action Allow -Profile Private
|
||||||
|
New-NetFirewallRule -DisplayName "CCB LAN Beacon Out (UDP)" -Direction Outbound -Protocol UDP -RemotePort 7101 -Action Allow -Profile Private
|
||||||
|
```
|
||||||
|
|
||||||
|
macOS:
|
||||||
|
```bash
|
||||||
|
# 首次运行时系统弹对话框,点"允许"即可
|
||||||
|
```
|
||||||
|
|
||||||
|
Linux:
|
||||||
|
```bash
|
||||||
|
sudo firewall-cmd --zone=trusted --add-port=7101/udp --permanent
|
||||||
|
sudo firewall-cmd --zone=trusted --add-port=1024-65535/tcp --permanent
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
**通知显示格式**:
|
||||||
|
```
|
||||||
|
# 本机 sub
|
||||||
|
Routed to [sub-1]; main can continue other tasks
|
||||||
|
|
||||||
|
# LAN peer
|
||||||
|
Routed to [main] vmwin11/192.168.50.27; main can continue other tasks
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Monitor 后台监控
|
||||||
|
|
||||||
|
**PR**: #241(同上)
|
||||||
|
**Feature Flag**: `MONITOR_TOOL`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
在后台运行 shell 命令持续监控输出(类似 `watch` 命令)。AI 也可自主调用 MonitorTool。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
|
||||||
|
**用户命令**:
|
||||||
|
```
|
||||||
|
/monitor tail -f /var/log/syslog
|
||||||
|
/monitor watch -n 5 docker ps
|
||||||
|
/monitor "while true; do curl -s localhost:3000/health; sleep 10; done"
|
||||||
|
```
|
||||||
|
|
||||||
|
**查看监控**:
|
||||||
|
- 按 `Shift+Down` 展开后台任务面板
|
||||||
|
- 查看监控输出和状态
|
||||||
|
|
||||||
|
**Windows 兼容**:
|
||||||
|
`watch -n <sec> <cmd>` 自动转为 PowerShell 循环:
|
||||||
|
```powershell
|
||||||
|
while($true){ <cmd>; Start-Sleep -Seconds <sec> }
|
||||||
|
```
|
||||||
|
|
||||||
|
**AI 调用**:
|
||||||
|
AI 可在对话中自动调用 `MonitorTool` 监控日志、构建输出等。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Workflow 工作流脚本
|
||||||
|
|
||||||
|
**PR**: #241(同上)
|
||||||
|
**Feature Flag**: `WORKFLOW_SCRIPTS`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
执行 `.claude/workflows/` 目录下的用户定义工作流脚本。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
|
||||||
|
**创建工作流**:
|
||||||
|
```bash
|
||||||
|
mkdir -p .claude/workflows
|
||||||
|
cat > .claude/workflows/deploy.sh << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
echo "Running tests..."
|
||||||
|
bun test
|
||||||
|
echo "Building..."
|
||||||
|
bun run build
|
||||||
|
echo "Deploying..."
|
||||||
|
EOF
|
||||||
|
chmod +x .claude/workflows/deploy.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**列出可用工作流**:
|
||||||
|
```
|
||||||
|
/workflows
|
||||||
|
```
|
||||||
|
|
||||||
|
**AI 调用**:
|
||||||
|
AI 可通过 `WorkflowTool` 自动执行工作流:
|
||||||
|
```
|
||||||
|
请执行 deploy 工作流
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Coordinator 多Worker协调
|
||||||
|
|
||||||
|
**PR**: #241(同上)
|
||||||
|
**Feature Flag**: `COORDINATOR_MODE`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
启用 coordinator 模式后,AI 可自动将任务分配给多个 worker 并行执行。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```
|
||||||
|
/coordinator — 切换 coordinator 模式开/关
|
||||||
|
```
|
||||||
|
|
||||||
|
启用后,AI 在处理复杂任务时会:
|
||||||
|
1. 分析任务可并行的部分
|
||||||
|
2. 自动创建 worker 分支
|
||||||
|
3. 分配子任务
|
||||||
|
4. 汇总结果
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Proactive 自主模式
|
||||||
|
|
||||||
|
**PR**: #241(同上)
|
||||||
|
**Feature Flag**: `PROACTIVE` / `KAIROS`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
启用后 AI 会主动发起操作(而不仅回应用户输入),例如自动检测文件变更、主动提出优化建议。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```
|
||||||
|
/proactive — 切换 proactive 模式开/关
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. History / Snip 历史管理
|
||||||
|
|
||||||
|
**PR**: #241(同上)
|
||||||
|
**Feature Flag**: `HISTORY_SNIP`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
查看和管理对话历史,支持手动截断以释放上下文窗口空间。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```
|
||||||
|
/history — 显示对话历史摘要
|
||||||
|
/force-snip — 强制在当前位置截断历史
|
||||||
|
```
|
||||||
|
|
||||||
|
AI 也可通过 `SnipTool` 自动截断过长的对话:
|
||||||
|
```
|
||||||
|
对话太长了,请帮我截断历史
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. Fork 子Agent
|
||||||
|
|
||||||
|
**PR**: #241(同上)
|
||||||
|
**Feature Flag**: `FORK_SUBAGENT`
|
||||||
|
|
||||||
|
### 说明
|
||||||
|
在当前对话上下文中 fork 一个独立的子 agent,继承完整会话状态独立执行。
|
||||||
|
|
||||||
|
### 使用
|
||||||
|
```
|
||||||
|
/fork — 基于当前上下文 fork 子 agent
|
||||||
|
```
|
||||||
|
|
||||||
|
子 agent 会:
|
||||||
|
- 继承当前的全部对话历史
|
||||||
|
- 在独立的执行环境中运行
|
||||||
|
- 不影响主会话状态
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 18. 其他恢复的工具
|
||||||
|
|
||||||
|
以下工具从 stub 恢复为完整实现:
|
||||||
|
|
||||||
|
| 工具 | 说明 | 使用 |
|
||||||
|
|------|------|------|
|
||||||
|
| `SleepTool` | 暂停执行指定时间 | AI 在轮询场景自动调用 |
|
||||||
|
| `WebBrowserTool` | 终端内网页交互 | AI 需要查看网页时调用 |
|
||||||
|
| `SubscribePRTool` | 订阅 GitHub PR 变更 | `/subscribe-pr` 或 AI 调用 |
|
||||||
|
| `PushNotificationTool` | 推送桌面通知 | AI 在长任务完成时调用 |
|
||||||
|
| `CtxInspectTool` | 检查上下文窗口使用 | AI 判断上下文剩余空间 |
|
||||||
|
| `TerminalCaptureTool` | 截取终端屏幕 | AI 需要看终端输出时调用 |
|
||||||
|
| `SendUserFileTool` | 向用户发送文件 | AI 导出文件时调用 |
|
||||||
|
| `REPLTool` | 启动子 REPL 会话 | AI 需要独立交互环境时调用 |
|
||||||
|
| `VerifyPlanExecutionTool` | 验证执行计划完成度 | AI 完成计划后自动验证 |
|
||||||
|
| `SuggestBackgroundPRTool` | 建议创建后台 PR | AI 发现可独立的变更时提议 |
|
||||||
|
| `ListPeersTool` | 列出已发现的 peer | AI 查询多实例状态时调用 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录:全部 Feature Flags
|
||||||
|
|
||||||
|
| Flag | 默认 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `BUDDY` | ✅ dev only | 伴侣系统 |
|
||||||
|
| `BRIDGE_MODE` | ✅ dev only | 远程控制 |
|
||||||
|
| `VOICE_MODE` | ✅ dev+build | 语音模式 |
|
||||||
|
| `CHICAGO_MCP` | ✅ dev+build | Computer Use + Chrome |
|
||||||
|
| `AGENT_TRIGGERS_REMOTE` | ✅ dev+build | 定时任务 |
|
||||||
|
| `SHOT_STATS` | ✅ dev+build | API 统计 |
|
||||||
|
| `TOKEN_BUDGET` | ✅ dev+build | Token 预算 |
|
||||||
|
| `PROMPT_CACHE_BREAK_DETECTION` | ✅ dev+build | 缓存检测 |
|
||||||
|
| `ULTRAPLAN` | ✅ dev+build | 高级规划 |
|
||||||
|
| `DAEMON` | ✅ dev+build | 后台守护 |
|
||||||
|
| `UDS_INBOX` | ✅ dev only | Pipe IPC |
|
||||||
|
| `LAN_PIPES` | ✅ dev only | LAN 群控 |
|
||||||
|
| `MONITOR_TOOL` | ✅ dev+build | 后台监控 |
|
||||||
|
| `WORKFLOW_SCRIPTS` | ✅ dev+build | 工作流脚本 |
|
||||||
|
| `FORK_SUBAGENT` | ✅ dev+build | 子 Agent |
|
||||||
|
| `KAIROS` | ✅ dev+build | Kairos 调度 |
|
||||||
|
| `COORDINATOR_MODE` | ✅ dev+build | 多 Worker |
|
||||||
|
| `HISTORY_SNIP` | ✅ dev+build | 历史管理 |
|
||||||
|
| `CONTEXT_COLLAPSE` | ✅ dev+build | 上下文折叠 |
|
||||||
|
| `ULTRATHINK` | ✅ dev+build | 扩展思考 |
|
||||||
|
| `EXTRACT_MEMORIES` | ✅ dev+build | 自动记忆提取 |
|
||||||
|
| `VERIFICATION_AGENT` | ✅ dev+build | 验证 Agent |
|
||||||
|
| `KAIROS_BRIEF` | ✅ dev+build | Brief 模式 |
|
||||||
|
| `AWAY_SUMMARY` | ✅ dev+build | 离开摘要 |
|
||||||
|
| `ACP` | ✅ dev+build | ACP 协议 |
|
||||||
|
| `LODESTONE` | ✅ dev+build | 深度链接 |
|
||||||
|
| `BUILTIN_EXPLORE_PLAN_AGENTS` | ✅ dev+build | 内置 Explore/Plan agent |
|
||||||
|
| `AGENT_TRIGGERS` | ✅ dev+build | 本地定时任务 |
|
||||||
|
| `BG_SESSIONS` | ✅ dev only | 后台会话 |
|
||||||
|
| `TEMPLATES` | ✅ dev only | 模板系统 |
|
||||||
|
| `TRANSCRIPT_CLASSIFIER` | ✅ dev only | 对话分类 |
|
||||||
|
|
||||||
|
手动启用任意 flag:
|
||||||
|
```bash
|
||||||
|
FEATURE_FLAG_NAME=1 bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录:PR 列表
|
||||||
|
|
||||||
|
| PR | 日期 | 标题 |
|
||||||
|
|----|------|------|
|
||||||
|
| #60 | 2026-04-02 | feat: enable Remote Control (BRIDGE_MODE) |
|
||||||
|
| #82 | 2026-04-03 | refactor(buddy): align companion system |
|
||||||
|
| #88 | 2026-04-03 | feat: enable /schedule (AGENT_TRIGGERS_REMOTE) |
|
||||||
|
| #89 | 2026-04-03 | feat: built-in status line |
|
||||||
|
| #92 | 2026-04-03 | feat: enable /voice mode |
|
||||||
|
| #93 | 2026-04-03 | feat: enable Chrome MCP |
|
||||||
|
| #98 | 2026-04-03 | feat: enable Computer Use (macOS + Windows + Linux) |
|
||||||
|
| #137 | 2026-04-05 | feat: Computer Use v2 — 跨平台 Executor |
|
||||||
|
| #140 | 2026-04-05 | feat: enable SHOT_STATS, TOKEN_BUDGET |
|
||||||
|
| #153 | 2026-04-06 | feat: enable GrowthBook local gate defaults |
|
||||||
|
| #156 | 2026-04-06 | feat: enable /ultraplan |
|
||||||
|
| #170 | 2026-04-07 | feat: restore daemon supervisor |
|
||||||
|
| #241 | 2026-04-11 | feat: restore pipe IPC, LAN pipes, monitor tool |
|
||||||
@@ -102,6 +102,6 @@ FEATURE_BASH_CLASSIFIER=1 FEATURE_TREE_SITTER_BASH=1 bun run dev
|
|||||||
| `src/utils/permissions/bashClassifier.ts` | — | Bash 分类器(stub,ANT-ONLY) |
|
| `src/utils/permissions/bashClassifier.ts` | — | Bash 分类器(stub,ANT-ONLY) |
|
||||||
| `src/utils/permissions/yoloClassifier.ts` | 1496 | YOLO 分类器(完整参考实现) |
|
| `src/utils/permissions/yoloClassifier.ts` | 1496 | YOLO 分类器(完整参考实现) |
|
||||||
| `src/utils/classifierApprovals.ts` | — | 分类器审批信号管理 |
|
| `src/utils/classifierApprovals.ts` | — | 分类器审批信号管理 |
|
||||||
| `src/components/permissions/BashPermissionRequest.tsx:261-469` | — | 分类器 UI |
|
| `src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx` | — | 分类器 UI |
|
||||||
| `src/hooks/toolPermission/handlers/interactiveHandler.ts` | — | 交互式权限处理 |
|
| `src/hooks/toolPermission/handlers/interactiveHandler.ts` | — | 交互式权限处理 |
|
||||||
| `src/services/api/withRetry.ts:81` | — | API beta 标头 |
|
| `src/services/api/withRetry.ts` | — | API beta 标头 |
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ BRIDGE_MODE 将本地 CLI 注册为"bridge 环境",可从 claude.ai 或其他
|
|||||||
|
|
||||||
文件:`src/bridge/bridgeApi.ts`
|
文件:`src/bridge/bridgeApi.ts`
|
||||||
|
|
||||||
Bridge API Client 提供 7 个核心操作:
|
Bridge API Client 提供 9 个核心操作:
|
||||||
|
|
||||||
| 操作 | HTTP | 说明 |
|
| 操作 | HTTP | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
@@ -137,7 +137,7 @@ FEATURE_BRIDGE_MODE=1 FEATURE_DAEMON=1 bun run dev
|
|||||||
|
|
||||||
| 文件 | 行数 | 职责 |
|
| 文件 | 行数 | 职责 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `src/bridge/bridgeApi.ts` | 540 | API Client(核心) |
|
| `src/bridge/bridgeApi.ts` | 541 | API Client(核心) |
|
||||||
| `src/bridge/sessionRunner.ts` | — | 会话运行器 |
|
| `src/bridge/sessionRunner.ts` | — | 会话运行器 |
|
||||||
| `src/bridge/bridgeConfig.ts` | — | 配置管理 |
|
| `src/bridge/bridgeConfig.ts` | — | 配置管理 |
|
||||||
| `src/bridge/replBridgeTransport.ts` | — | 传输层 |
|
| `src/bridge/replBridgeTransport.ts` | — | 传输层 |
|
||||||
|
|||||||
@@ -78,10 +78,13 @@ FEATURE_BUDDY=1 bun run dev
|
|||||||
|
|
||||||
| 文件 | 说明 |
|
| 文件 | 说明 |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
| `src/commands/buddy/index.ts` | `/buddy` 命令注册 |
|
||||||
| `src/commands/buddy/buddy.ts` | `/buddy` 命令处理 |
|
| `src/commands/buddy/buddy.ts` | `/buddy` 命令处理 |
|
||||||
| `src/buddy/companion.ts` | 宠物生成与加载 |
|
| `src/buddy/companion.ts` | 宠物生成与加载 |
|
||||||
|
| `src/buddy/companionReact.ts` | 宠物反应系统(REPL 每轮查询后触发) |
|
||||||
| `src/buddy/types.ts` | 类型定义(物种、稀有度、属性) |
|
| `src/buddy/types.ts` | 类型定义(物种、稀有度、属性) |
|
||||||
| `src/buddy/sprites.ts` | 终端像素画渲染 |
|
| `src/buddy/sprites.ts` | 终端像素画渲染 |
|
||||||
| `src/buddy/CompanionSprite.tsx` | React 组件(输入框旁显示) |
|
| `src/buddy/CompanionSprite.tsx` | React 组件(输入框旁显示) |
|
||||||
|
| `src/buddy/CompanionCard.tsx` | 宠物信息卡片(`/buddy` 无参数时展示) |
|
||||||
| `src/buddy/useBuddyNotification.tsx` | 启动提示通知 |
|
| `src/buddy/useBuddyNotification.tsx` | 启动提示通知 |
|
||||||
| `src/buddy/prompt.ts` | 宠物相关 prompt 模板 |
|
| `src/buddy/prompt.ts` | 宠物相关 prompt 模板 |
|
||||||
|
|||||||
89
docs/features/channels.md
Normal file
89
docs/features/channels.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# Channels — 外部频道消息接入
|
||||||
|
|
||||||
|
> 启动参数:`--channels` / `--dangerously-load-development-channels`
|
||||||
|
> 状态:已解除 feature flag 和 OAuth 限制,可直接使用
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
Channel 是一个 MCP 服务器,它将外部事件推送到你运行中的 Claude Code 会话中,以便 Claude 可以在你不在终端时做出反应。详细使用说明请参考以下文档:
|
||||||
|
|
||||||
|
- **官方文档**:[使用 channels 将事件推送到运行中的会话](https://code.claude.com/docs/zh-CN/channels)
|
||||||
|
- **飞书插件**:[claude-code-feishu-channel](https://github.com/whobot-ai/claude-code-feishu-channel) — 社区首个飞书 Channel 插件,支持双向消息、配对认证、群组聊天、文件附件
|
||||||
|
|
||||||
|
本仓库现在内置了 **微信 WeChat channel**,不需要单独安装外部 marketplace 插件。
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 启用频道监听(plugin 格式)
|
||||||
|
ccb --channels plugin:feishu@claude-code-feishu-channel
|
||||||
|
|
||||||
|
# 启用内置微信 channel
|
||||||
|
ccb weixin login
|
||||||
|
ccb --channels plugin:weixin@builtin
|
||||||
|
|
||||||
|
# 启用频道监听(server 格式)
|
||||||
|
ccb --channels server:my-slack-bridge
|
||||||
|
|
||||||
|
# 同时启用多个频道
|
||||||
|
ccb --channels plugin:feishu@claude-code-feishu-channel --channels server:discord-bot
|
||||||
|
|
||||||
|
# 开发模式(跳过 allowlist 检查,用于测试自定义 channel)
|
||||||
|
ccb --dangerously-load-development-channels server:my-custom-channel
|
||||||
|
```
|
||||||
|
|
||||||
|
## 支持的 Channel
|
||||||
|
|
||||||
|
| Channel | 说明 | 来源 |
|
||||||
|
|---------|------|------|
|
||||||
|
| **Telegram** | 官方 Telegram Bot 集成 | `/plugin install telegram@claude-plugins-official` |
|
||||||
|
| **Discord** | 官方 Discord Bot 集成 | `/plugin install discord@claude-plugins-official` |
|
||||||
|
| **iMessage** | macOS 原生消息 | `/plugin install imessage@claude-plugins-official` |
|
||||||
|
| **飞书 (Feishu/Lark)** | 双向消息、群组聊天、文件附件 | `/plugin install feishu@claude-code-feishu-channel` |
|
||||||
|
| **微信 (WeChat)** | 内置 channel,支持扫码登录、双向消息、附件透传 | `ccb weixin login` + `ccb --channels plugin:weixin@builtin` |
|
||||||
|
|
||||||
|
## 微信内置 Channel
|
||||||
|
|
||||||
|
### 登录
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ccb weixin login
|
||||||
|
```
|
||||||
|
|
||||||
|
已登录状态可清除:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ccb weixin login clear
|
||||||
|
```
|
||||||
|
|
||||||
|
### 会话启用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ccb --channels plugin:weixin@builtin
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配对授权
|
||||||
|
|
||||||
|
首次收到未授权微信用户消息时,weixin channel 会回一条 6 位 pairing code。运营侧可在终端执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ccb weixin access pair <code>
|
||||||
|
```
|
||||||
|
|
||||||
|
确认后,该微信用户后续消息才会进入 Claude Code 会话。
|
||||||
|
|
||||||
|
## 相关文件
|
||||||
|
|
||||||
|
| 文件 | 职责 |
|
||||||
|
|------|------|
|
||||||
|
| `src/services/mcp/channelNotification.ts` | 频道 gate 逻辑、消息包装 |
|
||||||
|
| `src/services/mcp/channelAllowlist.ts` | 频道开关(已默认开启) |
|
||||||
|
| `src/services/mcp/useManageMCPConnections.ts` | MCP 连接管理中的频道注册 |
|
||||||
|
| `src/components/LogoV2/ChannelsNotice.tsx` | 启动时频道状态提示 |
|
||||||
|
| `src/main.tsx` | `--channels` 参数解析 |
|
||||||
|
| `src/interactiveHelpers.tsx` | Dev channels 确认对话框 |
|
||||||
|
|
||||||
|
## 参考链接
|
||||||
|
|
||||||
|
- [官方 Channels 文档](https://code.claude.com/docs/zh-CN/channels) — 完整使用说明、安全性、Enterprise 控制
|
||||||
|
- [飞书 Channel 插件](https://github.com/whobot-ai/claude-code-feishu-channel) — 安装配置教程、MCP 工具、Skill 命令参考
|
||||||
30
docs/features/chrome-use-mcp.md
Normal file
30
docs/features/chrome-use-mcp.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Chrome Use — 浏览器自动化快速指南
|
||||||
|
|
||||||
|
让 Claude Code 直接控制你的 Chrome 浏览器,用自然语言完成网页操作。
|
||||||
|
|
||||||
|
## 快速开始(3 分钟)
|
||||||
|
|
||||||
|
### 第一步:安装 Chrome 扩展
|
||||||
|
|
||||||
|
1. 下载扩展:https://github.com/hangwin/mcp-chrome/releases
|
||||||
|
2. 解压 zip 文件
|
||||||
|
3. 打开 Chrome 访问 `chrome://extensions/`
|
||||||
|
4. 开启右上角「开发者模式」
|
||||||
|
5. 点击「加载已解压的扩展程序」,选择解压后的文件夹
|
||||||
|
|
||||||
|
### 第二步:启动 Claude Code
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run dev
|
||||||
|
ccb # 或者 ccb 安装版也行
|
||||||
|
```
|
||||||
|
|
||||||
|
### 第三步:启用 Chrome MCP
|
||||||
|
|
||||||
|
1. 在 REPL 中输入 `/mcp` 打开 MCP 面板
|
||||||
|
2. 找到 `mcp-chrome`,按空格键启用
|
||||||
|
3. 按 Enter 确认
|
||||||
|
|
||||||
|
## 相关文档
|
||||||
|
|
||||||
|
- GitHub 仓库:https://github.com/hangwin/mcp-chrome
|
||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
## 概览
|
## 概览
|
||||||
|
|
||||||
Computer Use 提供 37 个工具,分为三类:
|
Computer Use 提供 38 个工具,分为三类:
|
||||||
|
|
||||||
| 分类 | 平台 | 工具数 | 说明 |
|
| 分类 | 平台 | 工具数 | 说明 |
|
||||||
|------|------|--------|------|
|
|------|------|--------|------|
|
||||||
| 通用工具 | 全平台 | 24 | 官方 Computer Use 标准能力 |
|
| 通用工具 | 全平台 | 24 | 官方 Computer Use 标准能力 |
|
||||||
| Windows 专属工具 | Win32 | 10 | 绑定窗口模式下的增强能力 |
|
| Windows 专属工具 | Win32 | 11 | 绑定窗口模式下的增强能力 |
|
||||||
| 教学工具 | 全平台 | 3 | 分步引导模式(需 teachMode 开启) |
|
| 教学工具 | 全平台 | 3 | 分步引导模式(需 teachMode 开启) |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -82,7 +82,7 @@ Computer Use 提供 37 个工具,分为三类:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 二、Windows 专属工具(10 个)
|
## 二、Windows 专属工具(12 个)
|
||||||
|
|
||||||
仅 Windows 平台可见。核心能力:**绑定窗口后的独立操作——不抢占用户鼠标键盘**。
|
仅 Windows 平台可见。核心能力:**绑定窗口后的独立操作——不抢占用户鼠标键盘**。
|
||||||
|
|
||||||
@@ -235,8 +235,19 @@ Computer Use 提供 37 个工具,分为三类:
|
|||||||
|
|
||||||
| 工具 | 参数 | 说明 |
|
| 工具 | 参数 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
|
| `open_terminal` | `agent`, `command?` | 打开新终端窗口并启动 AI agent(claude/codex/gemini/custom)。自动绑定窗口并截图验证 |
|
||||||
|
| `activate_window` | `click_x?`, `click_y?` | 激活绑定窗口:SetForegroundWindow + BringWindowToTop + 点击确保焦点 |
|
||||||
| `prompt_respond` | `response_type`, `arrow_direction?`, `arrow_count?`, `text?` | 处理终端 Yes/No/选择提示 |
|
| `prompt_respond` | `response_type`, `arrow_direction?`, `arrow_count?`, `text?` | 处理终端 Yes/No/选择提示 |
|
||||||
|
|
||||||
|
**open_terminal agent 类型:**
|
||||||
|
|
||||||
|
| agent | 命令 | 说明 |
|
||||||
|
|-------|------|------|
|
||||||
|
| `claude` | `claude` | 启动 Claude Code |
|
||||||
|
| `codex` | `codex` | 启动 Codex |
|
||||||
|
| `gemini` | `gemini` | 启动 Gemini |
|
||||||
|
| `custom` | 用户指定 | 自定义命令 |
|
||||||
|
|
||||||
**response_type 详情:**
|
**response_type 详情:**
|
||||||
|
|
||||||
| response_type | 操作 | 场景 |
|
| response_type | 操作 | 场景 |
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
- ✅ `@ant/computer-use-input` 拆为 dispatcher + backends(darwin + win32)
|
- ✅ `@ant/computer-use-input` 拆为 dispatcher + backends(darwin + win32)
|
||||||
- ✅ `@ant/computer-use-swift` 拆为 dispatcher + backends(darwin + win32)
|
- ✅ `@ant/computer-use-swift` 拆为 dispatcher + backends(darwin + win32)
|
||||||
- ✅ `CHICAGO_MCP` 编译开关已开
|
- ✅ `CHICAGO_MCP` 编译开关已开
|
||||||
- ❌ `src/` 层有 6 处 macOS 硬编码阻塞
|
- ✅ `src/` 层 macOS 硬编码已移除(Phase 2 已完成)
|
||||||
|
|
||||||
## 2. 阻塞点全景
|
## 2. 阻塞点全景
|
||||||
|
|
||||||
@@ -19,25 +19,25 @@
|
|||||||
|
|
||||||
| # | 文件:行号 | 阻塞代码 | 影响 |
|
| # | 文件:行号 | 阻塞代码 | 影响 |
|
||||||
|---|----------|---------|------|
|
|---|----------|---------|------|
|
||||||
| 1 | `src/main.tsx:1605` | `getPlatform() === 'macos'` | 整个 CU 初始化被跳过 |
|
| 1 | `src/main.tsx:2366` | `feature("CHICAGO_MCP")` 门控 | CU 初始化入口 |
|
||||||
|
|
||||||
### 2.2 加载层
|
### 2.2 加载层
|
||||||
|
|
||||||
| # | 文件:行号 | 阻塞代码 | 影响 |
|
| # | 文件:行号 | 阻塞代码 | 影响 |
|
||||||
|---|----------|---------|------|
|
|---|----------|---------|------|
|
||||||
| 2 | `src/utils/computerUse/swiftLoader.ts:16` | `process.platform !== 'darwin'` → throw | 截图、应用管理全部不可用 |
|
| 2 | `src/utils/computerUse/swiftLoader.ts` | macOS-only loader(已改为仅 darwin 加载) | 非 darwin 使用 platforms/ 替代 |
|
||||||
| 3 | `src/utils/computerUse/executor.ts:263` | `process.platform !== 'darwin'` → throw | 整个 executor 工厂函数不可用 |
|
| 3 | `src/utils/computerUse/executor.ts:302` | `process.platform !== 'darwin'` → cross-platform executor | 非 darwin 走跨平台路径 |
|
||||||
|
|
||||||
### 2.3 macOS 特有依赖
|
### 2.3 macOS 特有依赖
|
||||||
|
|
||||||
| # | 文件:行号 | 依赖 | macOS 实现 | 需要替代方案 |
|
| # | 文件:行号 | 依赖 | macOS 实现 | 需要替代方案 |
|
||||||
|---|----------|------|-----------|------------|
|
|---|----------|------|-----------|------------|
|
||||||
| 4 | `executor.ts:70-88` | 剪贴板 | `pbcopy`/`pbpaste` | Win: PowerShell `Get/Set-Clipboard`;Linux: `xclip`/`wl-copy` |
|
| 4 | `executor.ts:72-96` | 剪贴板 | `pbcopy`/`pbpaste` / PowerShell / xclip | Win: PowerShell `Get/Set-Clipboard`;Linux: `xclip`/`wl-copy` |
|
||||||
| 5 | `drainRunLoop.ts:21` | CFRunLoop pump | `cu._drainMainRunLoop()` | 非 darwin:直接执行 fn(),不需要 pump |
|
| 5 | `drainRunLoop.ts` | CFRunLoop pump | `cu._drainMainRunLoop()` | 非 darwin:直接执行 fn(),不需要 pump |
|
||||||
| 6 | `escHotkey.ts:28` | ESC 热键 | CGEventTap | 非 darwin:返回 false(已有 Ctrl+C fallback) |
|
| 6 | `escHotkey.ts` | ESC 热键 | CGEventTap | 非 darwin:返回 false(已有 Ctrl+C fallback) |
|
||||||
| 7 | `hostAdapter.ts:48-54` | 系统权限 | TCC accessibility + screenRecording | Win:直接 granted;Linux:检查 xdotool |
|
| 7 | `hostAdapter.ts` | 系统权限 | TCC accessibility + screenRecording | Win:直接 granted;Linux:检查 xdotool |
|
||||||
| 8 | `common.ts:56` | 平台标识 | `platform: 'darwin'` 硬编码 | 动态获取 |
|
| 8 | `common.ts:55-58` | 平台标识 | 动态获取 | 已改为 `process.platform` 分发 |
|
||||||
| 9 | `executor.ts:180` | 粘贴快捷键 | `command+v` | Win/Linux:`ctrl+v` |
|
| 9 | `executor.ts:232` | 粘贴快捷键 | `command`/`ctrl` 分发 | 已按平台分发粘贴快捷键 |
|
||||||
|
|
||||||
### 2.4 缺失的 Linux 后端
|
### 2.4 缺失的 Linux 后端
|
||||||
|
|
||||||
@@ -100,19 +100,19 @@
|
|||||||
|
|
||||||
| 步骤 | 文件 | 改动 |
|
| 步骤 | 文件 | 改动 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| 2.1 | `src/main.tsx:1605` | `getPlatform() === 'macos'` → 去掉平台限制,或改为 `!== 'unknown'` |
|
| 2.1 | `src/main.tsx:2366` | `feature("CHICAGO_MCP")` → 已为跨平台入口 |
|
||||||
| 2.2 | `src/utils/computerUse/swiftLoader.ts:16-18` | 移除 `process.platform !== 'darwin'` throw。`@ant/computer-use-swift/index.ts` 已有跨平台 dispatch |
|
| 2.2 | `src/utils/computerUse/swiftLoader.ts` | 已改为仅 darwin 加载,非 darwin 使用 platforms/ |
|
||||||
| 2.3 | `src/utils/computerUse/executor.ts:263-267` | 移除 `process.platform !== 'darwin'` throw。改为检查 input/swift isSupported |
|
| 2.3 | `src/utils/computerUse/executor.ts:302-309` | 已改为 cross-platform dispatch(非 darwin → createCrossPlatformExecutor) |
|
||||||
| 2.4 | `src/utils/computerUse/executor.ts:70-88` | 剪贴板函数按平台分发:darwin→pbcopy/pbpaste,win32→PowerShell Get/Set-Clipboard,linux→xclip |
|
| 2.4 | `src/utils/computerUse/executor.ts:72-96` | 剪贴板已按平台分发:darwin→pbcopy/pbpaste,win32→PowerShell,linux→xclip |
|
||||||
| 2.5 | `src/utils/computerUse/executor.ts:180` | `typeViaClipboard` 中 `command+v` → 非 darwin 时用 `ctrl+v` |
|
| 2.5 | `src/utils/computerUse/executor.ts:232` | 粘贴快捷键已按平台分发:darwin→command,其他→ctrl |
|
||||||
| 2.6 | `src/utils/computerUse/executor.ts:273` | `const cu = requireComputerUseSwift()` → 改为 `new ComputerUseAPI()`(从 package 直接实例化,不走 swiftLoader throw) |
|
| 2.6 | `src/utils/computerUse/executor.ts:302-309` | 非 darwin 已改为 `createCrossPlatformExecutor()` |
|
||||||
| 2.7 | `src/utils/computerUse/drainRunLoop.ts` | 开头加 `if (process.platform !== 'darwin') return fn()` |
|
| 2.7 | `src/utils/computerUse/drainRunLoop.ts` | 非 darwin 无需 pump(直接执行 fn) |
|
||||||
| 2.8 | `src/utils/computerUse/escHotkey.ts` | `registerEscHotkey` 非 darwin 返回 false(已有 Ctrl+C fallback) |
|
| 2.8 | `src/utils/computerUse/escHotkey.ts` | 非 darwin 返回 false(已有 Ctrl+C fallback) |
|
||||||
| 2.9 | `src/utils/computerUse/hostAdapter.ts:48-54` | `ensureOsPermissions` 非 darwin 返回 `{ granted: true }` |
|
| 2.9 | `src/utils/computerUse/hostAdapter.ts` | 非 darwin 权限检查逻辑已实现 |
|
||||||
| 2.10 | `src/utils/computerUse/common.ts:56` | `platform: 'darwin'` → `platform: process.platform === 'win32' ? 'windows' : process.platform === 'linux' ? 'linux' : 'darwin'` |
|
| 2.10 | `src/utils/computerUse/common.ts:58` | 已改为动态 `process.platform` 分发 |
|
||||||
| 2.11 | `src/utils/computerUse/common.ts:55` | `screenshotFiltering: 'native'` → 非 darwin 时 `'none'`(Windows/Linux 截图不支持 per-app 过滤) |
|
| 2.11 | `src/utils/computerUse/common.ts:55` | 已改为 darwin→'native',其他→'none' |
|
||||||
| 2.12 | `src/utils/computerUse/gates.ts:13` | `enabled: false` → `enabled: true`(无 GrowthBook 时默认可用) |
|
| 2.12 | `src/utils/computerUse/gates.ts:55` | 已更新(需验证 enabled 默认值) |
|
||||||
| 2.13 | `src/utils/computerUse/gates.ts:39-43` | `hasRequiredSubscription()` → 直接返回 `true` |
|
| 2.13 | `src/utils/computerUse/gates.ts:39` | `hasRequiredSubscription()` 已更新 |
|
||||||
|
|
||||||
### Phase 3:新增 Linux 后端
|
### Phase 3:新增 Linux 后端
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ CONTEXT_COLLAPSE 让模型内省上下文窗口使用情况,并智能压缩旧
|
|||||||
| 折叠核心 | `src/services/contextCollapse/index.ts` | **Stub** — 接口完整(`ContextCollapseStats`、`CollapseResult`、`DrainResult`),函数全部空操作 |
|
| 折叠核心 | `src/services/contextCollapse/index.ts` | **Stub** — 接口完整(`ContextCollapseStats`、`CollapseResult`、`DrainResult`),函数全部空操作 |
|
||||||
| 折叠操作 | `src/services/contextCollapse/operations.ts` | **Stub** — `projectView` 为恒等函数 |
|
| 折叠操作 | `src/services/contextCollapse/operations.ts` | **Stub** — `projectView` 为恒等函数 |
|
||||||
| 折叠持久化 | `src/services/contextCollapse/persist.ts` | **Stub** — `restoreFromEntries` 为空操作 |
|
| 折叠持久化 | `src/services/contextCollapse/persist.ts` | **Stub** — `restoreFromEntries` 为空操作 |
|
||||||
| CtxInspectTool | `src/tools/CtxInspectTool/` | **缺失** — 目录不存在 |
|
| CtxInspectTool | `packages/builtin-tools/src/tools/CtxInspectTool/CtxInspectTool.ts` | **实现** — 上下文内省工具 |
|
||||||
| SnipTool 提示 | `src/tools/SnipTool/prompt.ts` | **Stub** — 空工具名 |
|
| SnipTool 提示 | `src/tools/SnipTool/prompt.ts` | **Stub** — 空工具名 |
|
||||||
| SnipTool 实现 | `src/tools/SnipTool/SnipTool.ts` | **缺失** |
|
| SnipTool 实现 | `src/tools/SnipTool/SnipTool.ts` | **缺失** |
|
||||||
| force-snip 命令 | `src/commands/force-snip.js` | **缺失** |
|
| force-snip 命令 | `src/commands/force-snip.js` | **缺失** |
|
||||||
@@ -106,7 +106,7 @@ SnipTool 提供手动折叠能力:
|
|||||||
| 1 | `services/contextCollapse/index.ts` | 大 | 折叠状态机、LLM 调用、消息压缩 |
|
| 1 | `services/contextCollapse/index.ts` | 大 | 折叠状态机、LLM 调用、消息压缩 |
|
||||||
| 2 | `services/contextCollapse/operations.ts` | 中 | `projectView()` 消息过滤 |
|
| 2 | `services/contextCollapse/operations.ts` | 中 | `projectView()` 消息过滤 |
|
||||||
| 3 | `services/contextCollapse/persist.ts` | 小 | `restoreFromEntries()` 磁盘持久化 |
|
| 3 | `services/contextCollapse/persist.ts` | 小 | `restoreFromEntries()` 磁盘持久化 |
|
||||||
| 4 | `tools/CtxInspectTool/` | 中 | 上下文内省工具(token 计数、已折叠范围) |
|
| 4 | `tools/CtxInspectTool/` | 已完成 | 上下文内省工具已实现(`packages/builtin-tools/src/tools/CtxInspectTool/`) |
|
||||||
| 5 | `tools/SnipTool/SnipTool.ts` | 中 | Snip 工具实现 |
|
| 5 | `tools/SnipTool/SnipTool.ts` | 中 | Snip 工具实现 |
|
||||||
| 6 | `commands/force-snip.js` | 小 | `/force-snip` 命令 |
|
| 6 | `commands/force-snip.js` | 小 | `/force-snip` 命令 |
|
||||||
|
|
||||||
|
|||||||
318
docs/features/daemon-restructure-design.md
Normal file
318
docs/features/daemon-restructure-design.md
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
# Daemon 重构设计方案
|
||||||
|
|
||||||
|
> 分支: `feat/integrate-5-branches`
|
||||||
|
> 基于: `f41745cb` (= main `11bb3f62` 内容)
|
||||||
|
> 日期: 2026-04-13
|
||||||
|
|
||||||
|
## 一、问题概述
|
||||||
|
|
||||||
|
### 1.1 命令结构散乱
|
||||||
|
|
||||||
|
当前后台进程相关的命令分布在三个不同的位置,没有统一的命名空间:
|
||||||
|
|
||||||
|
| 命令 | 注册位置 | 入口 |
|
||||||
|
|------|---------|------|
|
||||||
|
| `claude daemon start/status/stop` | `cli.tsx` 快速路径 L203 | `daemon/main.ts` |
|
||||||
|
| `claude ps` | `cli.tsx` 快速路径 L220 | `cli/bg.ts` |
|
||||||
|
| `claude logs <x>` | `cli.tsx` 快速路径 L232 | `cli/bg.ts` |
|
||||||
|
| `claude attach <x>` | `cli.tsx` 快速路径 L236 | `cli/bg.ts` |
|
||||||
|
| `claude kill <x>` | `cli.tsx` 快速路径 L238 | `cli/bg.ts` |
|
||||||
|
| `claude --bg` | `cli.tsx` 快速路径 L244 | `cli/bg.ts` |
|
||||||
|
| `claude new/list/reply` | `cli.tsx` 快速路径 L250 | `cli/handlers/templateJobs.ts` |
|
||||||
|
| `claude rollback` | `main.tsx` Commander.js L6525 | `cli/rollback.ts` |
|
||||||
|
| `claude up` | `main.tsx` Commander.js L6511 | `cli/up.ts` |
|
||||||
|
|
||||||
|
**问题**:
|
||||||
|
- `ps/logs/attach/kill` 与 `daemon` 逻辑上都是后台进程管理,但互不关联
|
||||||
|
- 这些命令都**只有 CLI 入口**,REPL 里输入 `/daemon` 或 `/ps` 不存在
|
||||||
|
- `new/list/reply` 是模板任务系统的顶级命令,容易与其他命令冲突(特别是 `list`)
|
||||||
|
|
||||||
|
### 1.2 Windows 不支持
|
||||||
|
|
||||||
|
`--bg` 和 `attach` 硬依赖 tmux:
|
||||||
|
- `bg.ts:handleBgFlag()` 第一步就检查 tmux,不可用直接报错退出
|
||||||
|
- `bg.ts:attachHandler()` 用 `tmux attach-session`,无 tmux 替代方案
|
||||||
|
- Windows (包括 VS Code 终端) 完全无法使用后台会话功能
|
||||||
|
|
||||||
|
### 1.3 无 REPL 入口
|
||||||
|
|
||||||
|
对比 `/mcp` 的双注册模式:
|
||||||
|
- **CLI**: `claude mcp serve/add/remove/list` (Commander.js, `main.tsx:5760`)
|
||||||
|
- **REPL**: `/mcp enable/disable/reconnect` (slash command, `commands/mcp/index.ts`)
|
||||||
|
|
||||||
|
`daemon`/`bg`/`job` 系列只有 CLI 快速路径,REPL 中完全不可用。
|
||||||
|
|
||||||
|
## 二、目标
|
||||||
|
|
||||||
|
1. **层级化命令结构**: 参照 `/mcp` 模式,将后台管理收归 `/daemon`,模板任务收归 `/job`
|
||||||
|
2. **跨平台后台会话**: Windows / macOS / Linux 都能启动、附着、终止后台会话
|
||||||
|
3. **双注册**: CLI (`claude daemon ...`) + REPL (`/daemon ...`) 同时可用
|
||||||
|
4. **向后兼容**: 旧命令保留但输出 deprecation 提示
|
||||||
|
|
||||||
|
## 三、命令结构设计
|
||||||
|
|
||||||
|
### 3.1 `/daemon` — 后台进程管理
|
||||||
|
|
||||||
|
合并 daemon supervisor + bg sessions 为统一命名空间:
|
||||||
|
|
||||||
|
```
|
||||||
|
claude daemon <subcommand> ← CLI 入口 (cli.tsx 快速路径)
|
||||||
|
/daemon <subcommand> ← REPL 入口 (slash command, local-jsx)
|
||||||
|
|
||||||
|
子命令:
|
||||||
|
status 综合状态面板 (daemon + 所有会话)
|
||||||
|
start [--dir <path>] 启动 daemon supervisor
|
||||||
|
stop 停止 daemon
|
||||||
|
bg [args...] 启动后台会话
|
||||||
|
attach [target] 附着到后台会话
|
||||||
|
logs [target] 查看会话日志
|
||||||
|
kill [target] 终止会话
|
||||||
|
(无参数) 等同于 status
|
||||||
|
```
|
||||||
|
|
||||||
|
**CLI 快速路径路由** (`cli.tsx`):
|
||||||
|
```typescript
|
||||||
|
// 新: 统一入口
|
||||||
|
if (feature('DAEMON') && args[0] === 'daemon') {
|
||||||
|
const sub = args[1] || 'status'
|
||||||
|
switch (sub) {
|
||||||
|
case 'start': case 'stop': case 'status':
|
||||||
|
await daemonMain([sub, ...args.slice(2)])
|
||||||
|
break
|
||||||
|
case 'bg':
|
||||||
|
await bg.handleBgStart(args.slice(2))
|
||||||
|
break
|
||||||
|
case 'attach': case 'logs': case 'kill':
|
||||||
|
await bg[`${sub}Handler`](args[2])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向后兼容 (deprecated)
|
||||||
|
if (feature('BG_SESSIONS') && ['ps','logs','attach','kill'].includes(args[0])) {
|
||||||
|
console.warn(`[deprecated] Use: claude daemon ${args[0] === 'ps' ? 'status' : args[0]}`)
|
||||||
|
// ... delegate to daemon subcommand
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**REPL 斜杠命令** (`commands/daemon/index.ts`):
|
||||||
|
```typescript
|
||||||
|
const daemon = {
|
||||||
|
type: 'local-jsx',
|
||||||
|
name: 'daemon',
|
||||||
|
description: 'Manage background sessions and daemon',
|
||||||
|
argumentHint: '[status|start|stop|bg|attach|logs|kill]',
|
||||||
|
isEnabled: () => feature('DAEMON') || feature('BG_SESSIONS'),
|
||||||
|
load: () => import('./daemon.js'),
|
||||||
|
} satisfies Command
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 `/job` — 模板任务管理
|
||||||
|
|
||||||
|
```
|
||||||
|
claude job <subcommand> ← CLI 入口
|
||||||
|
/job <subcommand> ← REPL 入口
|
||||||
|
|
||||||
|
子命令:
|
||||||
|
list 列出模板和活跃任务
|
||||||
|
new <template> [args] 从模板创建任务
|
||||||
|
reply <id> <text> 回复任务
|
||||||
|
status <id> 查看任务状态
|
||||||
|
(无参数) 等同于 list
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 独立命令 (不变)
|
||||||
|
|
||||||
|
```
|
||||||
|
claude up 保持顶级 (简短的 bootstrap 命令)
|
||||||
|
claude rollback [target] 保持顶级 (低频运维命令)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 四、跨平台后台引擎
|
||||||
|
|
||||||
|
### 4.1 引擎抽象
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/cli/bg/engine.ts
|
||||||
|
export interface BgEngine {
|
||||||
|
readonly name: string
|
||||||
|
|
||||||
|
/** 当前平台是否可用 */
|
||||||
|
available(): Promise<boolean>
|
||||||
|
|
||||||
|
/** 启动后台会话 */
|
||||||
|
start(opts: BgStartOptions): Promise<BgStartResult>
|
||||||
|
|
||||||
|
/** 附着到后台会话(blocking) */
|
||||||
|
attach(session: SessionEntry): Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BgStartOptions {
|
||||||
|
sessionName: string
|
||||||
|
args: string[]
|
||||||
|
env: Record<string, string | undefined>
|
||||||
|
logPath: string
|
||||||
|
cwd: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BgStartResult {
|
||||||
|
pid: number
|
||||||
|
sessionName: string
|
||||||
|
logPath: string
|
||||||
|
engineUsed: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 三种引擎实现
|
||||||
|
|
||||||
|
| 引擎 | 平台 | 启动方式 | attach 方式 |
|
||||||
|
|------|------|---------|------------|
|
||||||
|
| TmuxEngine | macOS/Linux (有 tmux) | `tmux new-session -d` | `tmux attach-session` |
|
||||||
|
| DetachedEngine | Windows / 无 tmux 的 macOS/Linux | `spawn({ detached, stdio→logFile })` | `tail -f` 日志文件 |
|
||||||
|
|
||||||
|
#### DetachedEngine 详细设计
|
||||||
|
|
||||||
|
**启动 (`start`)**:
|
||||||
|
```typescript
|
||||||
|
// 1. 打开日志文件 fd
|
||||||
|
const logFd = fs.openSync(logPath, 'a')
|
||||||
|
// 2. detached spawn, stdout/stderr 重定向到日志
|
||||||
|
const child = spawn(process.execPath, execArgs, {
|
||||||
|
detached: true,
|
||||||
|
stdio: ['ignore', logFd, logFd],
|
||||||
|
env,
|
||||||
|
cwd,
|
||||||
|
})
|
||||||
|
child.unref()
|
||||||
|
fs.closeSync(logFd)
|
||||||
|
// 3. 写 sessions/<PID>.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**附着 (`attach`)**:
|
||||||
|
```typescript
|
||||||
|
// 跨平台 tail -f 实现
|
||||||
|
// 1. 读取已有日志内容输出到 stdout
|
||||||
|
// 2. fs.watch(logPath) 监听变化
|
||||||
|
// 3. 每次变化读取新增内容
|
||||||
|
// 4. Ctrl+C 退出 tail(不杀后台进程)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 引擎选择逻辑
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/cli/bg/engines/index.ts
|
||||||
|
export async function selectEngine(): Promise<BgEngine> {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
return new DetachedEngine()
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmux = new TmuxEngine()
|
||||||
|
if (await tmux.available()) {
|
||||||
|
return tmux
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DetachedEngine()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 SessionEntry 扩展
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface SessionEntry {
|
||||||
|
// ... 现有字段
|
||||||
|
engine: 'tmux' | 'detached' // 新增: 记录使用的引擎
|
||||||
|
tmuxSessionName?: string // tmux 引擎才有
|
||||||
|
logPath?: string // 两种引擎都有
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`attach` 时根据 `session.engine` 选择对应的 attach 策略。
|
||||||
|
|
||||||
|
## 五、文件变更清单
|
||||||
|
|
||||||
|
### 新增文件 (10 个)
|
||||||
|
|
||||||
|
```
|
||||||
|
src/cli/bg/engine.ts BgEngine 接口定义
|
||||||
|
src/cli/bg/engines/tmux.ts TmuxEngine (从 bg.ts 提取)
|
||||||
|
src/cli/bg/engines/detached.ts DetachedEngine (新实现)
|
||||||
|
src/cli/bg/engines/index.ts 引擎选择 + re-export
|
||||||
|
src/cli/bg/tail.ts 跨平台日志 tail (用于 detached attach)
|
||||||
|
src/commands/daemon/index.ts /daemon REPL 斜杠命令注册
|
||||||
|
src/commands/daemon/daemon.tsx /daemon 子命令路由 + status UI
|
||||||
|
src/commands/job/index.ts /job REPL 斜杠命令注册
|
||||||
|
src/commands/job/job.tsx /job 子命令路由 + UI
|
||||||
|
docs/features/daemon-restructure-design.md 本设计文档
|
||||||
|
```
|
||||||
|
|
||||||
|
### 修改文件 (6 个)
|
||||||
|
|
||||||
|
```
|
||||||
|
src/cli/bg.ts 重构: handler 函数改为调用 BgEngine
|
||||||
|
src/entrypoints/cli.tsx 快速路径: daemon 统一入口 + 向后兼容
|
||||||
|
src/commands.ts 注册 /daemon 和 /job 斜杠命令
|
||||||
|
src/daemon/main.ts daemonMain() 增加 bg/ps/logs 子命令分发
|
||||||
|
src/main.tsx Commander.js: 可选注册 daemon/job 子命令
|
||||||
|
src/cli/handlers/templateJobs.ts 适配 /job 入口 (可能不需改)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 不动的文件
|
||||||
|
|
||||||
|
```
|
||||||
|
src/daemon/state.ts daemon PID 状态管理 (无需改)
|
||||||
|
src/jobs/state.ts job 状态管理 (无需改)
|
||||||
|
src/jobs/templates.ts 模板发现 (无需改)
|
||||||
|
src/jobs/classifier.ts 任务分类器 (无需改)
|
||||||
|
src/cli/rollback.ts 保持顶级命令 (无需改)
|
||||||
|
src/cli/up.ts 保持顶级命令 (无需改)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 六、可行性分析
|
||||||
|
|
||||||
|
### 6.1 风险评估
|
||||||
|
|
||||||
|
| 风险 | 级别 | 缓解措施 |
|
||||||
|
|------|------|---------|
|
||||||
|
| cli.tsx 快速路径修改影响启动性能 | 低 | 仅改路由逻辑,import 仍然 lazy |
|
||||||
|
| DetachedEngine 的 attach 在 Windows 上 fs.watch 不可靠 | 中 | 使用轮询 fallback (setInterval + fs.stat) |
|
||||||
|
| 向后兼容的 deprecation 可能破坏脚本 | 低 | 旧命令保持可用,仅输出 stderr 警告 |
|
||||||
|
| REPL 中 /daemon bg 需要 spawn 子进程 | 中 | 参考 /assistant 的 NewInstallWizard (已有 spawn 先例) |
|
||||||
|
| tsc 类型兼容 | 低 | 接口定义清晰,不引入 any |
|
||||||
|
|
||||||
|
### 6.2 工作量估计
|
||||||
|
|
||||||
|
| Task | 文件数 | 复杂度 |
|
||||||
|
|------|--------|--------|
|
||||||
|
| Task 013: BgEngine 抽象 + 引擎实现 | 5 新增 + 1 修改 | 中 |
|
||||||
|
| Task 014: /daemon 命令层级化 | 3 新增 + 3 修改 | 中 |
|
||||||
|
| Task 015: /job 命令层级化 | 2 新增 + 2 修改 | 低 |
|
||||||
|
| Task 016: 向后兼容 + 测试 | 0 新增 + 2 修改 | 低 |
|
||||||
|
|
||||||
|
### 6.3 依赖关系
|
||||||
|
|
||||||
|
```
|
||||||
|
Task 013 (BgEngine) ← 无依赖,可独立开发
|
||||||
|
Task 014 (/daemon) ← 依赖 Task 013 (引擎选择)
|
||||||
|
Task 015 (/job) ← 无依赖,可与 013 并行
|
||||||
|
Task 016 (兼容) ← 依赖 Task 014 + 015
|
||||||
|
```
|
||||||
|
|
||||||
|
## 七、设计决策记录
|
||||||
|
|
||||||
|
### D1: 为什么 daemon + bg sessions 合为一个命名空间?
|
||||||
|
|
||||||
|
用户视角:都是"后台运行的东西"。分开会导致 `claude daemon status` 看 supervisor + `claude ps` 看会话,割裂感强。合并后 `claude daemon status` 一次性展示 supervisor 状态 + 所有会话列表。
|
||||||
|
|
||||||
|
### D2: 为什么 rollback/up 不收入 daemon?
|
||||||
|
|
||||||
|
它们本质是**版本管理/环境初始化**,不是后台进程管理。`claude up` 是同步阻塞的 setup 脚本,不涉及 daemon 或后台会话。保持顶级更直观。
|
||||||
|
|
||||||
|
### D3: 为什么 DetachedEngine 的 attach 用 tail 而不是 IPC?
|
||||||
|
|
||||||
|
1. 日志文件是最简单的跨平台方案,无需额外依赖
|
||||||
|
2. UDS Pipe IPC 系统 (usePipeIpc) 设计用于实例间通信,不是终端附着
|
||||||
|
3. tmux attach 的体验(完整 PTY)无法在纯 detached 模式下复制,tail 是最诚实的替代
|
||||||
|
|
||||||
|
### D4: 为什么不用 Windows Terminal 的 tab/pane API?
|
||||||
|
|
||||||
|
Windows Terminal 的 `wt.exe` 新窗口/标签功能不够通用——用户可能在 VS Code、ConEmu、cmder 等终端中。detached + log 是唯一跨终端方案。
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
# DAEMON — 后台守护进程
|
# DAEMON — 后台守护进程
|
||||||
|
|
||||||
> Feature Flag: `FEATURE_DAEMON=1`
|
> Feature Flag: `FEATURE_DAEMON=1`
|
||||||
> 实现状态:主进程和 worker 注册为 Stub,CLI 路由完整
|
> 实现状态:Supervisor 和 remoteControl Worker 已实现
|
||||||
> 引用数:3
|
> 引用数:3
|
||||||
|
|
||||||
## 一、功能概述
|
## 一、功能概述
|
||||||
|
|
||||||
DAEMON 将 Claude Code 变为后台守护进程。主进程(supervisor)管理多个 worker 进程的生命周期,通过 Unix 域套接字进行 IPC。适用于持续运行的后台服务场景(如配合 BRIDGE_MODE 提供远程控制服务)。
|
DAEMON 将 Claude Code 变为后台守护进程。主进程(supervisor)管理多个 worker 子进程的生命周期,通过文件系统状态文件进行通信。适用于持续运行的后台服务场景(如配合 BRIDGE_MODE 提供远程控制服务)。
|
||||||
|
|
||||||
## 二、实现架构
|
## 二、实现架构
|
||||||
|
|
||||||
@@ -14,8 +14,9 @@ DAEMON 将 Claude Code 变为后台守护进程。主进程(supervisor)管
|
|||||||
|
|
||||||
| 模块 | 文件 | 状态 |
|
| 模块 | 文件 | 状态 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| 守护主进程 | `src/daemon/main.ts` | **Stub** — `daemonMain: () => Promise.resolve()` |
|
| 守护主进程 | `src/daemon/main.ts` | **已实现** — Supervisor 含子命令、Worker 生命周期管理、指数退避重启 |
|
||||||
| Worker 注册 | `src/daemon/workerRegistry.ts` | **Stub** — `runDaemonWorker: () => Promise.resolve()` |
|
| Worker 注册 | `src/daemon/workerRegistry.ts` | **已实现** — remoteControl Worker(headless bridge) |
|
||||||
|
| Daemon 状态 | `src/daemon/state.ts` | **已实现** — PID/状态文件的读写与查询 |
|
||||||
| CLI 路由 | `src/entrypoints/cli.tsx` | **布线** — `--daemon-worker` 和 `daemon` 子命令 |
|
| CLI 路由 | `src/entrypoints/cli.tsx` | **布线** — `--daemon-worker` 和 `daemon` 子命令 |
|
||||||
| 命令注册 | `src/commands.ts` | **布线** — DAEMON + BRIDGE_MODE 门控 |
|
| 命令注册 | `src/commands.ts` | **布线** — DAEMON + BRIDGE_MODE 门控 |
|
||||||
|
|
||||||
@@ -23,34 +24,49 @@ DAEMON 将 Claude Code 变为后台守护进程。主进程(supervisor)管
|
|||||||
|
|
||||||
```
|
```
|
||||||
# 启动守护进程
|
# 启动守护进程
|
||||||
claude daemon
|
claude daemon start
|
||||||
|
|
||||||
# 以 worker 身份启动
|
# 查看状态(默认子命令)
|
||||||
claude --daemon-worker=<kind>
|
claude daemon status
|
||||||
|
claude daemon ps
|
||||||
|
|
||||||
|
# 停止守护进程
|
||||||
|
claude daemon stop
|
||||||
|
|
||||||
|
# 以 worker 身份启动(由 supervisor 自动调用)
|
||||||
|
claude --daemon-worker=remoteControl
|
||||||
|
|
||||||
|
# 后台会话管理
|
||||||
|
claude daemon bg
|
||||||
|
claude daemon attach <session>
|
||||||
|
claude daemon logs <session>
|
||||||
|
claude daemon kill <session>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.3 预期架构
|
### 2.3 架构
|
||||||
|
|
||||||
```
|
```
|
||||||
Supervisor (daemonMain)
|
Supervisor (daemonMain)
|
||||||
│
|
│
|
||||||
├── Worker 1: assistant-mode
|
├── Worker: remoteControl
|
||||||
│ └── 接收和处理 assistant 会话
|
│ └── runBridgeHeadless() — 远程控制 headless 模式
|
||||||
│
|
│ 接收远程会话、处理消息、权限审批
|
||||||
├── Worker 2: bridge-sync
|
|
||||||
│ └── bridge 消息同步
|
|
||||||
│
|
|
||||||
└── Worker 3: proactive
|
|
||||||
└── 主动任务执行
|
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
IPC via Unix Domain Sockets
|
文件系统状态文件 (daemon-state.json)
|
||||||
- 生命周期管理(启动、停止、重启)
|
- PID、CWD、启动时间、Worker 类型
|
||||||
- 工作分发
|
- queryDaemonStatus() / stopDaemonByPid()
|
||||||
- 状态报告
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.4 与 BRIDGE_MODE 的关系
|
### 2.4 Worker 生命周期管理
|
||||||
|
|
||||||
|
Supervisor 为每个 worker 实现:
|
||||||
|
- **指数退避重启**:初始 2s,上限 120s,倍数 ×2
|
||||||
|
- **快速失败检测**:10s 内连续崩溃 5 次则 parking(不再重启)
|
||||||
|
- **永久错误退出码**:78 (EXIT_CODE_PERMANENT) 导致直接 parking
|
||||||
|
- **优雅关闭**:SIGTERM/SIGINT → abort signal → 30s 强制 SIGKILL
|
||||||
|
|
||||||
|
### 2.5 与 BRIDGE_MODE 的关系
|
||||||
|
|
||||||
DAEMON 和 BRIDGE_MODE 常组合使用:
|
DAEMON 和 BRIDGE_MODE 常组合使用:
|
||||||
|
|
||||||
@@ -63,40 +79,39 @@ if (feature('DAEMON') && feature('BRIDGE_MODE')) {
|
|||||||
|
|
||||||
双重门控:两个 feature 都需要开启才能使用远程控制服务器。
|
双重门控:两个 feature 都需要开启才能使用远程控制服务器。
|
||||||
|
|
||||||
## 三、需要补全的内容
|
## 三、关键设计决策
|
||||||
|
|
||||||
| 模块 | 工作量 | 说明 |
|
|
||||||
|------|--------|------|
|
|
||||||
| `daemon/main.ts` | 大 | Supervisor 主进程:启动 worker、生命周期管理、IPC |
|
|
||||||
| `daemon/workerRegistry.ts` | 中 | Worker 类型分发(assistant/bridge-sync/proactive) |
|
|
||||||
| Worker 实现 | 大 | 各类型 worker 的具体实现 |
|
|
||||||
| IPC 协议 | 中 | Supervisor-Worker 通信层 |
|
|
||||||
|
|
||||||
## 四、关键设计决策
|
|
||||||
|
|
||||||
1. **多进程架构**:一个 supervisor + 多个 worker,进程隔离
|
1. **多进程架构**:一个 supervisor + 多个 worker,进程隔离
|
||||||
2. **Unix 域套接字 IPC**:本地进程间通信,低延迟
|
2. **文件系统状态通信**:通过 `daemon-state.json` 文件进行状态共享(非 Unix 域套接字)
|
||||||
3. **与 BRIDGE_MODE 强绑定**:守护进程最常见的用途是提供远程控制服务
|
3. **与 BRIDGE_MODE 强绑定**:守护进程最常见的用途是提供远程控制服务
|
||||||
4. **CLI 子命令路由**:`daemon` 子命令和 `--daemon-worker` 参数在 `cli.tsx` 中路由
|
4. **CLI 子命令路由**:`daemon` 子命令和 `--daemon-worker` 参数在 `cli.tsx` 中路由
|
||||||
|
5. **Worker 环境变量**:supervisor 通过环境变量(`DAEMON_WORKER_*`)向 worker 传递配置
|
||||||
|
|
||||||
## 五、使用方式
|
## 四、使用方式
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 启用守护进程模式
|
# 启用守护进程模式
|
||||||
FEATURE_DAEMON=1 FEATURE_BRIDGE_MODE=1 bun run dev
|
FEATURE_DAEMON=1 FEATURE_BRIDGE_MODE=1 bun run dev
|
||||||
|
|
||||||
# 启动守护进程
|
# 启动守护进程
|
||||||
claude daemon
|
claude daemon start
|
||||||
|
|
||||||
# 以特定 worker 启动
|
# 查看状态
|
||||||
claude --daemon-worker=assistant
|
claude daemon status
|
||||||
|
|
||||||
|
# 停止守护进程
|
||||||
|
claude daemon stop
|
||||||
|
|
||||||
|
# 以特定 worker 启动(通常由 supervisor 自动调用)
|
||||||
|
claude --daemon-worker=remoteControl
|
||||||
```
|
```
|
||||||
|
|
||||||
## 六、文件索引
|
## 五、文件索引
|
||||||
|
|
||||||
| 文件 | 职责 |
|
| 文件 | 职责 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `src/daemon/main.ts` | Supervisor 主进程(stub) |
|
| `src/daemon/main.ts` | Supervisor 主进程:子命令分发、Worker 生命周期管理、退避重启 |
|
||||||
| `src/daemon/workerRegistry.ts` | Worker 注册(stub) |
|
| `src/daemon/workerRegistry.ts` | Worker 入口:remoteControl worker 实现 |
|
||||||
| `src/entrypoints/cli.tsx:95,149` | CLI 路由 |
|
| `src/daemon/state.ts` | Daemon 状态管理:PID 文件读写、状态查询 |
|
||||||
| `src/commands.ts:77` | 命令注册(双重门控) |
|
| `src/entrypoints/cli.tsx` | CLI 路由 |
|
||||||
|
| `src/commands.ts` | 命令注册(双重门控) |
|
||||||
|
|||||||
@@ -27,13 +27,15 @@ bun run dev:inspect
|
|||||||
|
|
||||||
## 原理
|
## 原理
|
||||||
|
|
||||||
`dev:inspect` 脚本实际执行的是:
|
`dev:inspect` 脚本实际执行的是 `scripts/dev-debug.ts`:
|
||||||
|
|
||||||
```bash
|
```typescript
|
||||||
bun --inspect-wait=localhost:8888/<token> run scripts/dev.ts
|
// scripts/dev-debug.ts
|
||||||
|
process.env.BUN_INSPECT = "localhost:8888/<token>"
|
||||||
|
await import("./dev")
|
||||||
```
|
```
|
||||||
|
|
||||||
Bun 的 `--inspect-wait` 参数启动一个 Chrome DevTools Protocol 兼容的 inspect 服务,等待调试器连接后才开始执行。VS Code 的 `bun` 扩展通过 WebSocket 连接到这个地址实现 attach。
|
通过设置 `BUN_INSPECT` 环境变量启动一个 Chrome DevTools Protocol 兼容的 inspect 服务,然后导入 dev 模式入口。VS Code 的 `bun` 扩展通过 WebSocket 连接到输出的地址实现 attach。
|
||||||
|
|
||||||
## JetBrains IDE
|
## JetBrains IDE
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,160 +0,0 @@
|
|||||||
# Feature Flags 审查报告 — Codex 复核
|
|
||||||
|
|
||||||
> 审查日期: 2026-04-05
|
|
||||||
> 审查工具: Codex CLI v0.118.0 (本地, full-auto mode)
|
|
||||||
> 消耗 tokens: 240,306
|
|
||||||
> 审查范围: docs/feature-flags-audit-complete.md 中标记为 COMPLETE 的 22 个编译时 feature flag
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 审查背景
|
|
||||||
|
|
||||||
原始审计报告 (`docs/feature-flags-audit-complete.md`) 声称 22 个 feature flag 被标记为 "COMPLETE",只需在 `build.ts` / `scripts/dev.ts` 中启用即可工作。
|
|
||||||
|
|
||||||
Claude Code 团队通过 6 个并行子代理实际读取源码后初步发现大量误判,随后将分析结果传递给 Codex CLI 进行独立二次验证。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Codex 发现摘要
|
|
||||||
|
|
||||||
### High 级发现
|
|
||||||
|
|
||||||
1. **`CONTEXT_COLLAPSE` 不是 COMPLETE**
|
|
||||||
- `src/services/contextCollapse/index.ts:43` — `isContextCollapseEnabled()` 硬编码为 `false`
|
|
||||||
- `src/services/contextCollapse/index.ts:47` — `applyCollapsesIfNeeded()` 只是原样返回消息
|
|
||||||
- `src/services/contextCollapse/index.ts:59` — `recoverFromOverflow()` 也是 no-op
|
|
||||||
- `src/services/contextCollapse/operations.ts:3` 和 `persist.ts:3` 同样是 stub
|
|
||||||
- 审计报告把 UI/命令文件算进去了,但真正被查询循环消费的是 stub 后端
|
|
||||||
|
|
||||||
2. **原分类"真正只需编译开关"的 7 个 flag,只有 3 个准确**
|
|
||||||
- ✅ `SHOT_STATS` — 零额外门控,compile-only
|
|
||||||
- ✅ `PROMPT_CACHE_BREAK_DETECTION` — 有 try-catch 兜底,compile-only
|
|
||||||
- ✅ `TOKEN_BUDGET` — 纯本地计算,compile-only
|
|
||||||
- ❌ `TEAMMEM` — 还要求 AutoMem + GrowthBook `tengu_herring_clock` + GitHub repo (`teamMemPaths.ts:73`, `watcher.ts:256`, `watcher.ts:259`)
|
|
||||||
- ❌ `AGENT_TRIGGERS` — 受 `isKairosCronEnabled()` GrowthBook 控制 (`useScheduledTasks.ts:61`, `useScheduledTasks.ts:119`)
|
|
||||||
- ❌ `EXTRACT_MEMORIES` — 受 `tengu_passport_quail` + AutoMem + 非 remote 限制 (`extractMemories.ts:536`, `:545`, `:550`)
|
|
||||||
- ❌ `KAIROS_BRIEF` — 受 `tengu_kairos_brief` + opt-in/kairosActive 限制 (`BriefTool.ts:95`, `:126`, `:132`)
|
|
||||||
|
|
||||||
### Medium 级发现
|
|
||||||
|
|
||||||
3. **`BG_SESSIONS` 和 `BASH_CLASSIFIER` 不适合简单归为"全 stub"**
|
|
||||||
- `BG_SESSIONS` — 会话注册/清理是真实现 (`concurrentSessions.ts:44`, `:55`),但任务摘要核心是 stub (`taskSummary.ts:2`)
|
|
||||||
- `BASH_CLASSIFIER` — 权限编排很大一块是真实现 (`bashPermissions.ts` 2621行),但分类后端 `bashClassifier.ts:24` 永远返回 disabled
|
|
||||||
|
|
||||||
4. **审计口径问题**
|
|
||||||
- 把"代码量/周边 UI 很多"误当成"可独立启用"
|
|
||||||
- `PROACTIVE` — `index.ts:3` 只有 state stub,`commands.ts:64` 和 `REPL.tsx:415` 引用缺失文件
|
|
||||||
- `REACTIVE_COMPACT` — `reactiveCompact.ts:13` 整块是 stub
|
|
||||||
- `CACHED_MICROCOMPACT` — `cachedMicrocompact.ts:22` 全部 stub
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Codex 修正后的分类
|
|
||||||
|
|
||||||
### 第一类:真正 compile-only(3 个)
|
|
||||||
|
|
||||||
| Flag | 说明 | Crash 风险 |
|
|
||||||
|------|------|-----------|
|
|
||||||
| **SHOT_STATS** | 纯本地 shot 分布统计,ant-only 数据路径 | 低 |
|
|
||||||
| **PROMPT_CACHE_BREAK_DETECTION** | 本地 cache key 变化检测,写 diff 有兜底 | 低 |
|
|
||||||
| **TOKEN_BUDGET** | 本地 token 预算追踪,纯计算逻辑 | 低 |
|
|
||||||
|
|
||||||
### 第二类:compile + 运行时条件(7 个)
|
|
||||||
|
|
||||||
| Flag | 条件 | Crash 风险 |
|
|
||||||
|------|------|-----------|
|
|
||||||
| **TEAMMEM** | AutoMem + GrowthBook `tengu_herring_clock` + GitHub repo | 低 (clean no-op) |
|
|
||||||
| **AGENT_TRIGGERS** | GrowthBook `isKairosCronEnabled()` | 低 (clean no-op) |
|
|
||||||
| **EXTRACT_MEMORIES** | `tengu_passport_quail` + AutoMem + 非 remote | 低 (clean no-op) |
|
|
||||||
| **KAIROS_BRIEF** | `tengu_kairos_brief` + opt-in/kairosActive,可用 `CLAUDE_CODE_BRIEF=1` 绕过 | 低 |
|
|
||||||
| **COORDINATOR_MODE** | 需 `CLAUDE_CODE_COORDINATOR_MODE=1`,`workerAgent.ts` 是 stub 但不阻塞 | 低 |
|
|
||||||
| **COMMIT_ATTRIBUTION** | 仅对 `isInternal=true` 的 repo 生效 | 低 |
|
|
||||||
| **VERIFICATION_AGENT** | 受 GrowthBook `tengu_hive_evidence` 双重门控 | 低 |
|
|
||||||
|
|
||||||
### 第三类:混合型 — 部分实现 + stub 核心(5 个)
|
|
||||||
|
|
||||||
| Flag | 真实现部分 | Stub 核心 |
|
|
||||||
|------|-----------|----------|
|
|
||||||
| **BG_SESSIONS** | 会话注册/清理 (`concurrentSessions.ts`) | `bg.ts`/`taskSummary.ts`/`udsClient.ts` 全 stub + 依赖 tmux |
|
|
||||||
| **BASH_CLASSIFIER** | 权限编排 (`bashPermissions.ts` 2621行) | `bashClassifier.ts` 分类后端 stub + 需 API beta |
|
|
||||||
| **PROACTIVE** | REPL/命令注册框架 | `index.ts` stub + 3 文件缺失 |
|
|
||||||
| **REACTIVE_COMPACT** | 调用点已在主查询环路 | `reactiveCompact.ts` 22行全 no-op |
|
|
||||||
| **CACHED_MICROCOMPACT** | 调用点已布线 | `cachedMicrocompact.ts` 全 stub + 需未公开 API |
|
|
||||||
|
|
||||||
### 第四类:纯 stub(1 个)
|
|
||||||
|
|
||||||
| Flag | 问题 |
|
|
||||||
|------|------|
|
|
||||||
| **CONTEXT_COLLAPSE** | 3 核心文件全 stub + CtxInspectTool 目录不存在 |
|
|
||||||
|
|
||||||
### 第五类:依赖远程服务(3 个)
|
|
||||||
|
|
||||||
| Flag | 依赖 |
|
|
||||||
|------|------|
|
|
||||||
| **ULTRAPLAN** | CCR 远程 agent 基础设施 + OAuth |
|
|
||||||
| **CCR_REMOTE_SETUP** | claude.ai OAuth + GitHub CLI + CCR 后端 |
|
|
||||||
| **BRIDGE_MODE** (build端) | claude.ai 订阅 + GrowthBook + WebSocket 后端 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 第三类恢复优先级建议
|
|
||||||
|
|
||||||
Codex 推荐的恢复顺序:
|
|
||||||
|
|
||||||
1. **REACTIVE_COMPACT** — 收益最直接,调用点在主查询环路,改完最容易立刻见效
|
|
||||||
2. **BG_SESSIONS** — 已有会话注册基础,补齐摘要和后台运行链路的 ROI 高
|
|
||||||
3. **PROACTIVE** — 产品面大,但缺文件比 stub 更严重,范围比前两项大
|
|
||||||
4. **CONTEXT_COLLAPSE** — collapse engine 全 stub,恢复成本和设计不确定性都高
|
|
||||||
5. **BASH_CLASSIFIER** — 若无 API beta 能力不值得优先;若有则升到第 2
|
|
||||||
6. **CACHED_MICROCOMPACT** — 受未公开 API 约束,最后做
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 审计报告分类标准修正建议
|
|
||||||
|
|
||||||
Codex 建议将原来的单轴分类(COMPLETE/PARTIAL/STUB)改为**三轴**:
|
|
||||||
|
|
||||||
| 轴 | 取值 | 说明 |
|
|
||||||
|----|------|------|
|
|
||||||
| **实现完整度** | `full` / `mixed` / `stub` | 活跃调用链上的核心模块是否有真实现 |
|
|
||||||
| **激活条件** | `compile-only` / `compile+env` / `compile+GrowthBook` / `compile+remote` / `compile+private API` | 启用需要什么 |
|
|
||||||
| **运行风险** | `safe no-op` / `background IO` / `startup critical` | 启用后条件不满足时的行为 |
|
|
||||||
|
|
||||||
**COMPLETE 的最低标准应满足:**
|
|
||||||
1. 活跃调用链上的核心模块不能是 stub
|
|
||||||
2. "可启用"不能只看编译 flag,还要单列运行时 gate
|
|
||||||
|
|
||||||
按此标准,`CONTEXT_COLLAPSE`、`BG_SESSIONS`、`BASH_CLASSIFIER`、`PROACTIVE`、`REACTIVE_COMPACT`、`CACHED_MICROCOMPACT` 都应从 COMPLETE 降级。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 已采取的行动
|
|
||||||
|
|
||||||
基于审查结果,已将以下 3 个确认安全的 flag 加入默认构建:
|
|
||||||
|
|
||||||
**build.ts:**
|
|
||||||
```typescript
|
|
||||||
const DEFAULT_BUILD_FEATURES = [
|
|
||||||
"AGENT_TRIGGERS_REMOTE", "CHICAGO_MCP", "VOICE_MODE",
|
|
||||||
"SHOT_STATS", "PROMPT_CACHE_BREAK_DETECTION", "TOKEN_BUDGET"
|
|
||||||
];
|
|
||||||
```
|
|
||||||
|
|
||||||
**scripts/dev.ts:**
|
|
||||||
```typescript
|
|
||||||
const DEFAULT_FEATURES = [
|
|
||||||
"BUDDY", "TRANSCRIPT_CLASSIFIER", "BRIDGE_MODE",
|
|
||||||
"AGENT_TRIGGERS_REMOTE", "CHICAGO_MCP", "VOICE_MODE",
|
|
||||||
"SHOT_STATS", "PROMPT_CACHE_BREAK_DETECTION", "TOKEN_BUDGET"
|
|
||||||
];
|
|
||||||
```
|
|
||||||
|
|
||||||
### 验证结果
|
|
||||||
|
|
||||||
| 项目 | 结果 |
|
|
||||||
|------|------|
|
|
||||||
| `bun run build` | ✅ 成功 (475 files) |
|
|
||||||
| `bun test` | ✅ 无新增失败 (23 fail 为已有问题) |
|
|
||||||
| SHOT_STATS 代码路径 | ✅ 完整 — stats 面板显示 shot 分布 |
|
|
||||||
| TOKEN_BUDGET 代码路径 | ✅ 完整 — 支持 `+500k` 语法,带进度条 |
|
|
||||||
| PROMPT_CACHE_BREAK_DETECTION 代码路径 | ✅ 完整 — 内部诊断,debug 模式可见 |
|
|
||||||
@@ -37,7 +37,7 @@ Agent({ subagent_type: "general-purpose", prompt: "..." })
|
|||||||
|
|
||||||
### 3.1 门控与互斥
|
### 3.1 门控与互斥
|
||||||
|
|
||||||
文件:`src/tools/AgentTool/forkSubagent.ts:32-39`
|
文件:`packages/builtin-tools/src/tools/AgentTool/forkSubagent.ts:32-39`
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export function isForkSubagentEnabled(): boolean {
|
export function isForkSubagentEnabled(): boolean {
|
||||||
@@ -105,7 +105,7 @@ isForkSubagentEnabled() && !subagent_type?
|
|||||||
|
|
||||||
### 3.4 消息构建:buildForkedMessages
|
### 3.4 消息构建:buildForkedMessages
|
||||||
|
|
||||||
文件:`src/tools/AgentTool/forkSubagent.ts:107-169`
|
文件:`packages/builtin-tools/src/tools/AgentTool/forkSubagent.ts:107-169`
|
||||||
|
|
||||||
构建的消息结构:
|
构建的消息结构:
|
||||||
|
|
||||||
@@ -185,11 +185,11 @@ FEATURE_FORK_SUBAGENT=1 bun run dev
|
|||||||
|
|
||||||
| 文件 | 行数 | 职责 |
|
| 文件 | 行数 | 职责 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `src/tools/AgentTool/forkSubagent.ts` | ~210 | 核心定义 + 消息构建 + 递归防护 |
|
| `packages/builtin-tools/src/tools/AgentTool/forkSubagent.ts` | ~210 | 核心定义 + 消息构建 + 递归防护 |
|
||||||
| `src/tools/AgentTool/AgentTool.tsx` | — | Fork 路由 + 强制异步 |
|
| `packages/builtin-tools/src/tools/AgentTool/AgentTool.tsx` | — | Fork 路由 + 强制异步 |
|
||||||
| `src/tools/AgentTool/prompt.ts` | — | "When to Fork" 提示词段落 |
|
| `packages/builtin-tools/src/tools/AgentTool/prompt.ts` | — | "When to Fork" 提示词段落 |
|
||||||
| `src/tools/AgentTool/runAgent.ts` | — | useExactTools 路径 |
|
| `packages/builtin-tools/src/tools/AgentTool/runAgent.ts` | — | useExactTools 路径 |
|
||||||
| `src/tools/AgentTool/resumeAgent.ts` | — | Fork agent 恢复 |
|
| `packages/builtin-tools/src/tools/AgentTool/resumeAgent.ts` | — | Fork agent 恢复 |
|
||||||
| `src/constants/xml.ts` | — | XML 标签常量 |
|
| `src/constants/xml.ts` | — | XML 标签常量 |
|
||||||
| `src/utils/forkedAgent.ts` | — | CacheSafeParams + ContentReplacementState 克隆 |
|
| `src/utils/forkedAgent.ts` | — | CacheSafeParams + ContentReplacementState 克隆 |
|
||||||
| `src/commands/fork/index.ts` | — | /fork 命令(stub) |
|
| `src/commands/fork/index.ts` | — | /fork 命令(stub) |
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# KAIROS — 常驻助手模式
|
# KAIROS — 常驻助手模式
|
||||||
|
|
||||||
> Feature Flag: `FEATURE_KAIROS=1`(及子 Feature)
|
> Feature Flag: `FEATURE_KAIROS=1`(及子 Feature)
|
||||||
> 实现状态:核心框架完整,部分子模块为 stub
|
> 实现状态:核心框架完整,部分子模块为 stub;proactive/sleep 节奏控制已可用
|
||||||
> 引用数:154(全库最大)
|
> 引用数:154(全库最大)
|
||||||
|
|
||||||
## 一、功能概述
|
## 一、功能概述
|
||||||
@@ -34,13 +34,13 @@ KAIROS 在系统提示中注入两大段落:
|
|||||||
|
|
||||||
### 2.1 Brief 段落 (`getBriefSection`)
|
### 2.1 Brief 段落 (`getBriefSection`)
|
||||||
|
|
||||||
文件:`src/constants/prompts.ts:843-858`
|
文件:`src/constants/prompts.ts:847-858`
|
||||||
|
|
||||||
当 `feature('KAIROS') || feature('KAIROS_BRIEF')` 时注入。Brief 工具(`SendUserMessage`)的结构化消息输出指令。`/brief` toggle 和 `--brief` flag 只控制显示过滤,不影响模型行为。
|
当 `feature('KAIROS') || feature('KAIROS_BRIEF')` 时注入。Brief 工具(`SendUserMessage`)的结构化消息输出指令。`/brief` toggle 和 `--brief` flag 只控制显示过滤,不影响模型行为。
|
||||||
|
|
||||||
### 2.2 Proactive/Autonomous Work 段落 (`getProactiveSection`)
|
### 2.2 Proactive/Autonomous Work 段落 (`getProactiveSection`)
|
||||||
|
|
||||||
文件:`src/constants/prompts.ts:860-914`
|
文件:`src/constants/prompts.ts:864-918`
|
||||||
|
|
||||||
当 `feature('PROACTIVE') || feature('KAIROS')` 且 `isProactiveActive()` 时注入。核心行为指令:
|
当 `feature('PROACTIVE') || feature('KAIROS')` 且 `isProactiveActive()` 时注入。核心行为指令:
|
||||||
|
|
||||||
@@ -74,8 +74,9 @@ KAIROS 在系统提示中注入两大段落:
|
|||||||
|
|
||||||
SleepTool 是 KAIROS/Proactive 的节奏控制核心。工具描述让模型理解"休眠"概念:
|
SleepTool 是 KAIROS/Proactive 的节奏控制核心。工具描述让模型理解"休眠"概念:
|
||||||
- 工具名:`Sleep`
|
- 工具名:`Sleep`
|
||||||
- 功能:等待指定时间后响应 tick prompt
|
- 功能:等待指定时间后响应 tick prompt;若队列出现新工作或 proactive 被关闭,会提前唤醒
|
||||||
- 与 `<tick_tag>` 配合实现心跳式自主工作
|
- 与 `<tick_tag>` 配合实现心跳式自主工作
|
||||||
|
- 远程控制 surfaces 可通过 `automation_state` 看到 `standby` / `sleeping` 两种状态
|
||||||
|
|
||||||
### 3.3 Bridge 集成
|
### 3.3 Bridge 集成
|
||||||
|
|
||||||
@@ -172,8 +173,10 @@ FEATURE_KAIROS=1 FEATURE_TOKEN_BUDGET=1 bun run dev
|
|||||||
| `src/assistant/AssistantSessionChooser.ts` | — | Session 选择 UI(stub) |
|
| `src/assistant/AssistantSessionChooser.ts` | — | Session 选择 UI(stub) |
|
||||||
| `src/tools/BriefTool/` | — | BriefTool 实现(stub) |
|
| `src/tools/BriefTool/` | — | BriefTool 实现(stub) |
|
||||||
| `src/tools/SleepTool/prompt.ts` | ~30 | SleepTool 工具提示 |
|
| `src/tools/SleepTool/prompt.ts` | ~30 | SleepTool 工具提示 |
|
||||||
|
| `src/tools/SleepTool/SleepTool.ts` | ~200 | 休眠/唤醒与 automation metadata |
|
||||||
| `src/services/mcp/channelNotification.ts` | 5 | 频道消息接入(stub) |
|
| `src/services/mcp/channelNotification.ts` | 5 | 频道消息接入(stub) |
|
||||||
| `src/memdir/memdir.ts` | — | 记忆目录管理(stub) |
|
| `src/memdir/memdir.ts` | — | 记忆目录管理(stub) |
|
||||||
| `src/constants/prompts.ts:552-554,843-914` | 72 | 系统提示注入 |
|
| `src/constants/prompts.ts:557,847-918` | 72 | 系统提示注入 |
|
||||||
| `src/components/tasks/src/tasks/DreamTask/` | 3 | Dream 任务(stub) |
|
| `src/components/tasks/src/tasks/DreamTask/` | 3 | Dream 任务(stub) |
|
||||||
| `src/proactive/index.ts` | — | Proactive 核心(stub,KAIROS 共享) |
|
| `src/proactive/index.ts` | — | Proactive 核心(KAIROS 共享) |
|
||||||
|
| `src/utils/sessionState.ts` | — | 向 bridge/CCR 暴露 automation 状态 |
|
||||||
|
|||||||
321
docs/features/lan-pipes-implementation.md
Normal file
321
docs/features/lan-pipes-implementation.md
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
# LAN Pipes — 技术实现文档
|
||||||
|
|
||||||
|
面向开发者的实现细节。用户指南见 [lan-pipes.md](./lan-pipes.md)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
```
|
||||||
|
Machine A (192.168.50.22) Machine B (192.168.50.27)
|
||||||
|
┌───────────────────────────┐ ┌───────────────────────────┐
|
||||||
|
│ PipeServer │ │ PipeServer │
|
||||||
|
│ UDS: ~/.claude/pipes/ │ │ UDS: ~/.claude/pipes/ │
|
||||||
|
│ cli-abc.sock │ │ cli-def.sock │
|
||||||
|
│ TCP: 0.0.0.0:<random> │◄──TCP───►│ TCP: 0.0.0.0:<random> │
|
||||||
|
├───────────────────────────┤ ├───────────────────────────┤
|
||||||
|
│ LanBeacon │ │ LanBeacon │
|
||||||
|
│ UDP 224.0.71.67:7101 │◄──UDP───►│ UDP 224.0.71.67:7101 │
|
||||||
|
├───────────────────────────┤ ├───────────────────────────┤
|
||||||
|
│ usePipeIpc (hook) │ │ usePipeIpc (hook) │
|
||||||
|
│ initPipeServer │ │ initPipeServer │
|
||||||
|
│ registerMessageHandlers │ │ registerMessageHandlers │
|
||||||
|
│ runMainHeartbeat │ │ runSubHeartbeat │
|
||||||
|
│ cleanupPipeIpc │ │ cleanupPipeIpc │
|
||||||
|
└───────────────────────────┘ └───────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Feature Flag
|
||||||
|
|
||||||
|
`LAN_PIPES` — 在 `scripts/dev.ts` 和 `build.ts` 的 `DEFAULT_FEATURES` 中启用。
|
||||||
|
|
||||||
|
所有 LAN 代码路径通过 `feature('LAN_PIPES')` 编译时门控。`feature()` 只能在 `if` 或三元中使用(Bun 编译时常量约束)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 核心文件
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `src/utils/pipeTransport.ts` | PipeServer/PipeClient(UDS + TCP 双模式) |
|
||||||
|
| `src/utils/lanBeacon.ts` | UDP multicast beacon + module singleton |
|
||||||
|
| `src/utils/ndjsonFramer.ts` | 共享 NDJSON socket 帧解析 |
|
||||||
|
| `src/utils/pipeRegistry.ts` | 文件注册表 + `mergeWithLanPeers()` |
|
||||||
|
| `src/utils/peerAddress.ts` | 地址解析(uds/bridge/tcp scheme) |
|
||||||
|
| `src/utils/pipePermissionRelay.ts` | 权限转发 + `setPipeRelay`/`getPipeRelay` singleton |
|
||||||
|
| `src/hooks/usePipeIpc.ts` | 生命周期 hook(从 REPL.tsx 提取) |
|
||||||
|
| `src/hooks/usePipeRelay.ts` | 消息回传 hook |
|
||||||
|
| `src/hooks/usePipePermissionForward.ts` | 权限转发 hook |
|
||||||
|
| `src/hooks/usePipeRouter.ts` | 输入路由 hook |
|
||||||
|
| `src/hooks/useMasterMonitor.ts` | slave 注册表 + 消息订阅 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PipeServer TCP 扩展
|
||||||
|
|
||||||
|
`src/utils/pipeTransport.ts`
|
||||||
|
|
||||||
|
### 类型
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export type PipeTransportMode = 'uds' | 'tcp'
|
||||||
|
export type TcpEndpoint = { host: string; port: number }
|
||||||
|
export type PipeServerOptions = { enableTcp?: boolean; tcpPort?: number }
|
||||||
|
```
|
||||||
|
|
||||||
|
### PipeServer 变更
|
||||||
|
|
||||||
|
- `setupSocket(socket)` — 从 start() 提取的共享方法,UDS 和 TCP 共用
|
||||||
|
- `start(options?)` — 可选启用 TCP,port=0 让 OS 分配
|
||||||
|
- 内部维护两个 `net.Server`,共享同一组 `clients: Set<Socket>` 和 `handlers`
|
||||||
|
- `tcpAddress` getter 暴露 TCP 端口
|
||||||
|
- `close()` 同时关闭两个 server
|
||||||
|
|
||||||
|
socket 帧解析使用 `attachNdjsonFramer()` from `ndjsonFramer.ts`(替代原先 3 份重复代码)。
|
||||||
|
|
||||||
|
### PipeClient 变更
|
||||||
|
|
||||||
|
- 构造函数新增可选 `TcpEndpoint` 参数
|
||||||
|
- `connect()` 根据 tcpEndpoint 分派到 `connectTcp()` 或 `connectUds()`
|
||||||
|
- TCP 不需要文件存在轮询,直接建连
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## LAN Beacon
|
||||||
|
|
||||||
|
`src/utils/lanBeacon.ts`
|
||||||
|
|
||||||
|
### 协议参数
|
||||||
|
|
||||||
|
| 参数 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| Multicast 组 | `224.0.71.67` |
|
||||||
|
| 端口 | `7101` |
|
||||||
|
| 广播间隔 | `3000ms` |
|
||||||
|
| Peer 超时 | `15000ms` |
|
||||||
|
| TTL | `1` |
|
||||||
|
|
||||||
|
### Announce 包
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type LanAnnounce = {
|
||||||
|
proto: 'claude-pipe-v1'
|
||||||
|
pipeName: string
|
||||||
|
machineId: string
|
||||||
|
hostname: string
|
||||||
|
ip: string
|
||||||
|
tcpPort: number
|
||||||
|
role: 'main' | 'sub'
|
||||||
|
ts: number
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class LanBeacon extends EventEmitter {
|
||||||
|
constructor(announce: Omit<LanAnnounce, 'proto' | 'ts'>)
|
||||||
|
start(): void
|
||||||
|
stop(): void
|
||||||
|
getPeers(): Map<string, LanAnnounce> // 防御性拷贝
|
||||||
|
updateAnnounce(partial): void // 使用 spread(不可变更新)
|
||||||
|
|
||||||
|
on('peer-discovered', (peer: LanAnnounce) => void)
|
||||||
|
on('peer-lost', (pipeName: string) => void)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 存储
|
||||||
|
|
||||||
|
module-level singleton:`getLanBeacon()` / `setLanBeacon()`。不挂在 Zustand state 上(避免 `setState` 展开时丢失引用)。
|
||||||
|
|
||||||
|
### 网卡绑定
|
||||||
|
|
||||||
|
`addMembership(group, localIp)` + `setMulticastInterface(localIp)` 指定 LAN 网卡。解决 Windows 上 WSL/Docker 虚拟网卡劫持 multicast 的问题。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Hook 架构
|
||||||
|
|
||||||
|
从 REPL.tsx 提取的 ~830 行 Pipe IPC 代码:
|
||||||
|
|
||||||
|
### usePipeIpc(生命周期)
|
||||||
|
|
||||||
|
`src/hooks/usePipeIpc.ts`(623 行)
|
||||||
|
|
||||||
|
在 REPL.tsx 顶层通过 feature-gated require 加载:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const usePipeIpc = feature('UDS_INBOX')
|
||||||
|
? require('../hooks/usePipeIpc.js').usePipeIpc
|
||||||
|
: () => undefined;
|
||||||
|
|
||||||
|
// 组件内
|
||||||
|
usePipeIpc({ store, handleIncomingPrompt });
|
||||||
|
```
|
||||||
|
|
||||||
|
内部使用 **lazy getter** 函数加载依赖(避免循环依赖导致 Bun 运行时崩溃):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const pt = () => require('../utils/pipeTransport.js')
|
||||||
|
const pr = () => require('../utils/pipeRegistry.js')
|
||||||
|
const mm = () => require('./useMasterMonitor.js')
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
`import type` 用于静态类型(不会触发模块加载)。
|
||||||
|
|
||||||
|
### 四个阶段函数
|
||||||
|
|
||||||
|
| 函数 | 职责 |
|
||||||
|
|------|------|
|
||||||
|
| `initPipeServer` | 角色判定 + server 创建 + beacon 启动 |
|
||||||
|
| `registerMessageHandlers` | ping、attach、prompt、permission、detach 五个 handler |
|
||||||
|
| `runMainHeartbeat` | cleanup + 发现 + auto-attach + 清理死连接 |
|
||||||
|
| `runSubHeartbeat` | 检测 main 是否存活,死亡则接管或独立 |
|
||||||
|
|
||||||
|
### usePipeRelay(消息回传)
|
||||||
|
|
||||||
|
`src/hooks/usePipeRelay.ts`(38 行)
|
||||||
|
|
||||||
|
提供 `relayPipeMessage()` 和 `pipeReturnHadErrorRef`。relay 函数通过 `getPipeRelay()` module singleton 读取(替代 `globalThis.__pipeSendToMaster`)。
|
||||||
|
|
||||||
|
### usePipePermissionForward(权限转发)
|
||||||
|
|
||||||
|
`src/hooks/usePipePermissionForward.ts`(159 行)
|
||||||
|
|
||||||
|
订阅 `subscribePipeEntries()`,处理:
|
||||||
|
- `permission_request` → 解析 payload → 查找 tool → 加入确认队列
|
||||||
|
- `permission_cancel` → 从队列移除
|
||||||
|
- `stream/error/done` → 转为系统消息显示(含 role + IP 标签)
|
||||||
|
|
||||||
|
### usePipeRouter(输入路由)
|
||||||
|
|
||||||
|
`src/hooks/usePipeRouter.ts`(130 行)
|
||||||
|
|
||||||
|
提供 `routeToSelectedPipes(input): boolean`。读取 `selectedPipes` + `routeMode`,逐个发送到已连接目标。通知显示 `[role] hostname/ip`(LAN peer)或 `[role]`(本机)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Registry 并行探测
|
||||||
|
|
||||||
|
`src/utils/pipeRegistry.ts`
|
||||||
|
|
||||||
|
### getAliveSubs()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export async function getAliveSubs(): Promise<PipeRegistrySub[]> {
|
||||||
|
const registry = await readRegistry()
|
||||||
|
const results = await Promise.all(
|
||||||
|
registry.subs.map(sub =>
|
||||||
|
isPipeAlive(sub.pipeName, 1000).then(alive => alive ? sub : null)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return results.filter(Boolean)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### cleanupStaleEntries()
|
||||||
|
|
||||||
|
两阶段:
|
||||||
|
1. **无锁并行探测**:`Promise.all` 探测 main + 所有 subs
|
||||||
|
2. **短暂持锁写入**:`acquireLock()` → 重新读取 → 应用变更 → 写入 → `releaseLock()`
|
||||||
|
|
||||||
|
持锁时间从 N 秒降至 ~10ms。
|
||||||
|
|
||||||
|
### getMachineId()
|
||||||
|
|
||||||
|
Windows/macOS 使用 `execFile`(异步),不阻塞主线程。结果缓存,仅首次调用执行。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NDJSON 协议
|
||||||
|
|
||||||
|
### 消息类型
|
||||||
|
|
||||||
|
| 类型 | 方向 | 数据 |
|
||||||
|
|------|------|------|
|
||||||
|
| `ping` / `pong` | 双向 | 无 |
|
||||||
|
| `attach_request` | M→S | `meta: { machineId }` |
|
||||||
|
| `attach_accept` / `attach_reject` | S→M | `data: reason` |
|
||||||
|
| `detach` | M→S | 无 |
|
||||||
|
| `prompt` | M→S | `data: prompt_text` |
|
||||||
|
| `prompt_ack` | S→M | `data: 'accepted'` |
|
||||||
|
| `stream` | S→M | `data: partial_text` |
|
||||||
|
| `done` | S→M | 无 |
|
||||||
|
| `error` | 双向 | `data: error_message` |
|
||||||
|
| `permission_request` | S→M | `data: JSON(PipePermissionRequestPayload)` |
|
||||||
|
| `permission_response` | M→S | `data: JSON(PipePermissionResponsePayload)` |
|
||||||
|
| `permission_cancel` | M→S | `data: JSON({ requestId, reason })` |
|
||||||
|
|
||||||
|
### 帧格式
|
||||||
|
|
||||||
|
每行一个 JSON 对象,`\n` 分隔:
|
||||||
|
```
|
||||||
|
{"type":"ping","from":"cli-abc","ts":"2026-04-11T00:00:00.000Z"}\n
|
||||||
|
{"type":"prompt","data":"检查 git status","from":"cli-abc"}\n
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 跨机器 Attach 流程
|
||||||
|
|
||||||
|
```
|
||||||
|
CLI-B (192.168.50.27) 心跳循环
|
||||||
|
→ beacon.getPeers() 发现 CLI-A (192.168.50.22)
|
||||||
|
→ connectToPipe(pName, myName, 3000, { host: '192.168.50.22', port: 58853 })
|
||||||
|
→ PipeClient.connectTcp() → net.createConnection({ host, port })
|
||||||
|
→ client.send({ type: 'attach_request', meta: { machineId } })
|
||||||
|
→ CLI-A 收到:
|
||||||
|
isLanPeer = (msg.meta.machineId !== myMachineId) → true
|
||||||
|
→ 不检查 role,直接 reply({ type: 'attach_accept' })
|
||||||
|
→ setPipeRelay(socket.write)
|
||||||
|
→ CLI-B 收到 attach_accept
|
||||||
|
→ addSlaveClient(pName, client)
|
||||||
|
→ store.setState: role='master', slaves[pName] = { status: 'idle' }
|
||||||
|
```
|
||||||
|
|
||||||
|
关键:跨机器 attach 不要求对方是 sub 角色。通过 `machineId` 区分 LAN peer。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SendMessageTool TCP 支持
|
||||||
|
|
||||||
|
`packages/builtin-tools/src/tools/SendMessageTool/SendMessageTool.ts`
|
||||||
|
|
||||||
|
- `to` 字段支持 `tcp:host:port` 格式
|
||||||
|
- `checkPermissions`:`tcp:` scheme 返回 `behavior: 'ask'`,`classifierApprovable: false`
|
||||||
|
- `call()`:创建临时 `PipeClient` → connect → send → disconnect
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
| 文件 | 测试数 | 覆盖 |
|
||||||
|
|------|--------|------|
|
||||||
|
| `lanBeacon.test.ts` | 7 | socket 初始化、announce、peer 发现/过滤/清理 |
|
||||||
|
| `peerAddress.test.ts` | 8 | scheme 解析、parseTcpTarget、端口范围验证 |
|
||||||
|
| `pipePermissionRelay.test.ts` | 2 | setPipeRelay singleton、权限请求/响应 |
|
||||||
|
| `pipeTransport.test.ts` | 2 | UDS 基础行为 |
|
||||||
|
| `useMasterMonitor.test.ts` | 5 | slave 注册/移除、事件发射 |
|
||||||
|
|
||||||
|
全量:2190 pass / 0 fail
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 已知限制
|
||||||
|
|
||||||
|
1. **TCP 无认证** — 同 LAN 内知道端口号即可连接
|
||||||
|
2. **Beacon 明文广播** — IP/hostname/machineId 未 hash
|
||||||
|
3. **单网卡选择** — `getLocalIp()` 返回首个非内部 IPv4,可能选到 VPN
|
||||||
|
4. **端口随机** — 每次启动不同端口,依赖 beacon 发现
|
||||||
|
5. **SendMessageTool 每次创建新连接** — 未复用已有 slave client
|
||||||
|
|
||||||
|
## 后续改进方向
|
||||||
|
|
||||||
|
1. HMAC-SHA256 TCP 握手认证
|
||||||
|
2. machineId hash 后再广播
|
||||||
|
3. 多网卡选择(优先 RFC 1918 地址)
|
||||||
|
4. 固定端口范围配置
|
||||||
|
5. TLS 加密传输
|
||||||
|
6. SendMessageTool 复用已连接的 slave client
|
||||||
193
docs/features/lan-pipes.md
Normal file
193
docs/features/lan-pipes.md
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
# LAN Pipes — 局域网多机器群控指南
|
||||||
|
|
||||||
|
## 什么是 LAN Pipes
|
||||||
|
|
||||||
|
LAN Pipes 让多台机器上的 Claude Code 实例通过局域网自动发现并协作。你可以在一台机器(main)上操控其他机器(sub)上的 Claude Code,发送 prompt、查看执行结果、审批权限请求——全程零配置。
|
||||||
|
|
||||||
|
基于本机 Pipe IPC(`UDS_INBOX`)扩展,新增 TCP 传输层 + UDP Multicast 发现。
|
||||||
|
|
||||||
|
## 前置条件
|
||||||
|
|
||||||
|
- 两台或以上机器在同一局域网
|
||||||
|
- 每台机器安装了 CCB 并能 `bun run dev`
|
||||||
|
- Feature flag `LAN_PIPES`(dev/build 默认开启)
|
||||||
|
- 防火墙允许 UDP 7101 + TCP 动态端口(见下方配置)
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 第一步:配置防火墙
|
||||||
|
|
||||||
|
**每台机器都需要执行。**
|
||||||
|
|
||||||
|
**Windows**(管理员 PowerShell):
|
||||||
|
```powershell
|
||||||
|
New-NetFirewallRule -DisplayName "CCB LAN Beacon (UDP)" -Direction Inbound -Protocol UDP -LocalPort 7101 -Action Allow -Profile Private
|
||||||
|
New-NetFirewallRule -DisplayName "CCB LAN Pipes (TCP)" -Direction Inbound -Protocol TCP -LocalPort 1024-65535 -Program (Get-Command bun).Source -Action Allow -Profile Private
|
||||||
|
New-NetFirewallRule -DisplayName "CCB LAN Beacon Out (UDP)" -Direction Outbound -Protocol UDP -RemotePort 7101 -Action Allow -Profile Private
|
||||||
|
```
|
||||||
|
|
||||||
|
验证网络为"专用"(非公共):`Get-NetConnectionProfile`
|
||||||
|
|
||||||
|
**macOS**:
|
||||||
|
首次运行时系统弹出"允许接受传入连接"对话框,点击"允许"。
|
||||||
|
|
||||||
|
如果使用 pf 防火墙:
|
||||||
|
```bash
|
||||||
|
echo "pass in proto udp from any to any port 7101" | sudo pfctl -ef -
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux**(firewalld):
|
||||||
|
```bash
|
||||||
|
sudo firewall-cmd --zone=trusted --add-port=7101/udp --permanent
|
||||||
|
sudo firewall-cmd --zone=trusted --add-port=1024-65535/tcp --permanent
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux**(iptables):
|
||||||
|
```bash
|
||||||
|
sudo iptables -A INPUT -p udp --dport 7101 -j ACCEPT
|
||||||
|
sudo iptables -A INPUT -p tcp --dport 1024:65535 -m owner --uid-owner $(id -u) -j ACCEPT
|
||||||
|
```
|
||||||
|
|
||||||
|
### 第二步:启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 机器 A(例如 192.168.50.22)
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# 机器 B(例如 192.168.50.27)
|
||||||
|
bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
启动后等待 3-5 秒(beacon 广播间隔),两边自动发现并连接。
|
||||||
|
|
||||||
|
### 第三步:查看和操作
|
||||||
|
|
||||||
|
在任一台机器上:
|
||||||
|
```
|
||||||
|
/pipes
|
||||||
|
```
|
||||||
|
|
||||||
|
输出示例:
|
||||||
|
```
|
||||||
|
pipe: cli-a91bad56 (main) 192.168.50.22 2/3 selected
|
||||||
|
|
||||||
|
Main machine: 205d6c3a... (this machine)
|
||||||
|
[main] cli-a91bad56 XC/192.168.50.22 [alive] (you)
|
||||||
|
☑ [sub-1] cli-da029538 XC/192.168.50.22 [alive] [connected]
|
||||||
|
|
||||||
|
LAN Peers:
|
||||||
|
☐ [main] cli-04d67950 vmwin11/192.168.50.27 tcp:192.168.50.27:58853 [LAN]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 第四步:选中目标并发送任务
|
||||||
|
|
||||||
|
1. 按 `Shift+↓` 展开选择面板
|
||||||
|
2. `↑↓` 移动到 LAN peer
|
||||||
|
3. `Space` 选中
|
||||||
|
4. `Enter` 确认
|
||||||
|
5. 输入 prompt,自动路由到远端执行
|
||||||
|
|
||||||
|
远端执行结果会流式回传到你的消息列表:
|
||||||
|
```
|
||||||
|
[main vmwin11/192.168.50.27 / cli-04d67950] 正在检查 git status...
|
||||||
|
[main vmwin11/192.168.50.27 / cli-04d67950] Completed
|
||||||
|
```
|
||||||
|
|
||||||
|
## 完整命令参考
|
||||||
|
|
||||||
|
| 命令 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `/pipes` | 显示所有实例(本机 + LAN),Shift+↓ 展开选择面板 |
|
||||||
|
| `/pipes select <name>` | 选中某实例 |
|
||||||
|
| `/pipes all` | 全选 |
|
||||||
|
| `/pipes none` | 取消全选 |
|
||||||
|
| `/attach <name>` | 手动 attach(自动识别 LAN peer 并通过 TCP 连接) |
|
||||||
|
| `/detach <name>` | 断开连接 |
|
||||||
|
| `/send <name> <msg>` | 向指定 pipe 发送消息 |
|
||||||
|
| `/send tcp:host:port <msg>` | 直接通过 TCP 地址发送 |
|
||||||
|
| `/claim-main` | 强制声明为 main |
|
||||||
|
| `/pipe-status` | 显示详细状态 |
|
||||||
|
| `/peers` | 列出所有已发现的 peer |
|
||||||
|
|
||||||
|
## 快捷键
|
||||||
|
|
||||||
|
| 快捷键 | 场景 | 作用 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `Shift+↓` | 状态栏可见时 | 展开/收起选择面板 |
|
||||||
|
| `↑ / ↓` | 面板展开时 | 移动光标 |
|
||||||
|
| `Space` | 面板展开时 | 选中/取消 |
|
||||||
|
| `Enter` | 面板展开时 | 确认关闭 |
|
||||||
|
| `Esc` | 面板展开时 | 取消关闭 |
|
||||||
|
| `← / →` | 有选中 pipe 时 | 切换路由模式 |
|
||||||
|
| `M` | 面板展开时 | 同 ←/→ 切换路由模式 |
|
||||||
|
|
||||||
|
## 路由模式
|
||||||
|
|
||||||
|
| 模式 | 显示 | 行为 |
|
||||||
|
|------|------|------|
|
||||||
|
| `selected pipes only` | 绿色 | prompt 仅发送到选中的 pipe,本地不执行 |
|
||||||
|
| `local main` | 灰色 | prompt 仅在本地执行,不转发 |
|
||||||
|
|
||||||
|
切换路由模式不会清空选择。
|
||||||
|
|
||||||
|
## 权限转发
|
||||||
|
|
||||||
|
当远端 slave 执行需要权限的工具(如 BashTool)时:
|
||||||
|
1. slave 发送 `permission_request` 到 main
|
||||||
|
2. main 弹出权限确认对话框,显示 `[role hostname/ip / pipeName]`
|
||||||
|
3. 用户确认/拒绝
|
||||||
|
4. 结果发回 slave,继续或中断
|
||||||
|
|
||||||
|
## 工作原理
|
||||||
|
|
||||||
|
### 发现机制
|
||||||
|
|
||||||
|
- 每台机器启动时创建 UDP multicast beacon
|
||||||
|
- 组地址 `224.0.71.67`,端口 `7101`,TTL=1(不跨路由器)
|
||||||
|
- 每 3 秒广播一次自身信息(pipeName、IP、TCP 端口、角色)
|
||||||
|
- 15 秒未收到广播则标记 peer 丢失
|
||||||
|
|
||||||
|
### 通信机制
|
||||||
|
|
||||||
|
- 本机实例:UDS(Unix Domain Socket / Named Pipe)
|
||||||
|
- 跨机器:TCP(动态端口,通过 beacon 发现)
|
||||||
|
- 协议:NDJSON(每行一个 JSON 对象)
|
||||||
|
- 消息类型:ping/pong、attach/detach、prompt/stream/done/error、permission
|
||||||
|
|
||||||
|
### 角色模型
|
||||||
|
|
||||||
|
| 角色 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `main` | 首个启动的实例 |
|
||||||
|
| `sub` | 同机后续启动的实例 |
|
||||||
|
| `master` | attach 了至少一个 slave 的实例 |
|
||||||
|
| `slave` | 被 master attach 的实例 |
|
||||||
|
|
||||||
|
跨机器 attach 时,两边都可以是 main——不要求对方必须是 sub。
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### 看不到 LAN peer
|
||||||
|
|
||||||
|
1. 检查防火墙是否放行 UDP 7101
|
||||||
|
2. `Get-NetConnectionProfile`(Windows)确认网络为"专用"
|
||||||
|
3. 确认两台机器在同一子网(`ping` 能通)
|
||||||
|
4. 路由器未开启 AP 隔离
|
||||||
|
|
||||||
|
### 连接超时
|
||||||
|
|
||||||
|
1. 检查 TCP 入站防火墙规则
|
||||||
|
2. 确认没有 VPN 劫持流量
|
||||||
|
3. 尝试 `/send tcp:ip:port hello` 直接测试
|
||||||
|
|
||||||
|
### beacon 绑到了错误网卡
|
||||||
|
|
||||||
|
Windows 上 WSL/Docker 虚拟网卡可能劫持 multicast。beacon 会自动选择非内部 IPv4 接口。如果选错,检查 `getLocalIp()` 返回值。
|
||||||
|
|
||||||
|
## 安全说明
|
||||||
|
|
||||||
|
- TCP 连接当前**无认证**——同 LAN 内知道端口号即可连接
|
||||||
|
- Multicast TTL=1,不跨路由器
|
||||||
|
- AI 通过 `SendMessageTool` 发送 `tcp:` 消息时需**用户显式确认**
|
||||||
|
- 建议仅在信任的局域网中使用
|
||||||
205
docs/features/langfuse-monitoring.md
Normal file
205
docs/features/langfuse-monitoring.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# Langfuse 监控集成
|
||||||
|
|
||||||
|
> 实现状态:已完成,通过环境变量启用
|
||||||
|
> 依赖:`@langfuse/otel`、`@langfuse/tracing`、`@opentelemetry/sdk-trace-base`
|
||||||
|
|
||||||
|
## 一、功能概述
|
||||||
|
|
||||||
|
Langfuse 是一个开源的 LLM 可观测性平台,用于追踪、监控和调试 AI 应用的请求链路。CCB 通过 OpenTelemetry (OTel) 桥接层将 Langfuse 集成到查询流程中,实现:
|
||||||
|
|
||||||
|
- **LLM 调用追踪** — 记录每次 API 请求的模型、Provider、输入/输出、Token 用量
|
||||||
|
- **工具执行追踪** — 记录每个工具调用的名称、输入、输出、耗时和错误
|
||||||
|
- **多 Agent 追踪** — 主 Agent 和子 Agent 各自独立的 Trace 链路
|
||||||
|
- **数据脱敏** — 自动遮蔽敏感信息(API Key、文件内容、Shell 输出等)
|
||||||
|
|
||||||
|
## 二、启用方式
|
||||||
|
|
||||||
|
Langfuse 是开源项目,你可以 **自部署**(Docker / Kubernetes),也可以使用官方提供的 **[Langfuse Cloud](https://cloud.langfuse.com)** 免费测试。注册后在 Project Settings → API Keys 页面获取密钥。
|
||||||
|
|
||||||
|
核心只需要三个环境变量:
|
||||||
|
|
||||||
|
| 环境变量 | 说明 |
|
||||||
|
|---------|------|
|
||||||
|
| `LANGFUSE_PUBLIC_KEY` | Langfuse 公钥(必填) |
|
||||||
|
| `LANGFUSE_SECRET_KEY` | Langfuse 密钥(必填) |
|
||||||
|
| `LANGFUSE_BASE_URL` | 服务地址,默认 `https://cloud.langfuse.com`;自部署时改为你的地址(必填) |
|
||||||
|
|
||||||
|
未配置时所有追踪函数为 no-op,零开销。
|
||||||
|
|
||||||
|
### 通过 settings.json 配置(推荐)
|
||||||
|
|
||||||
|
在 `.claude/settings.json` 的 `env` 字段中添加,这样每次启动自动生效:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"LANGFUSE_PUBLIC_KEY": "pk-xxx",
|
||||||
|
"LANGFUSE_SECRET_KEY": "sk-xxx",
|
||||||
|
"LANGFUSE_BASE_URL": "https://cloud.langfuse.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 其他可选参数
|
||||||
|
|
||||||
|
| 环境变量 | 默认值 | 说明 |
|
||||||
|
|---------|--------|------|
|
||||||
|
| `LANGFUSE_TRACING_ENVIRONMENT` | `development` | 环境标签,用于 Langfuse 面板筛选 |
|
||||||
|
| `LANGFUSE_FLUSH_AT` | `20` | 批量发送的 span 数量阈值 |
|
||||||
|
| `LANGFUSE_FLUSH_INTERVAL` | `10` | 定时刷新间隔(秒) |
|
||||||
|
| `LANGFUSE_EXPORT_MODE` | `batched` | 导出模式:`batched`(批量)或 `immediate`(即时) |
|
||||||
|
| `LANGFUSE_TIMEOUT` | `5` | 请求超时(秒) |
|
||||||
|
|
||||||
|
## 四、架构
|
||||||
|
|
||||||
|
### 4.1 模块结构
|
||||||
|
|
||||||
|
```
|
||||||
|
src/services/langfuse/
|
||||||
|
├── index.ts # 统一导出
|
||||||
|
├── client.ts # OTel Provider + LangfuseSpanProcessor 初始化
|
||||||
|
├── tracing.ts # Trace/Span 创建、LLM 和工具观察记录
|
||||||
|
├── convert.ts # 内部 Message 类型 → Langfuse OpenAI 兼容格式转换
|
||||||
|
└── sanitize.ts # 数据脱敏(敏感字段、文件路径、工具输出)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 追踪层级
|
||||||
|
|
||||||
|
```
|
||||||
|
Trace (Agent Span) ← createTrace() / createSubagentTrace()
|
||||||
|
├── Generation (LLM 调用) ← recordLLMObservation()
|
||||||
|
├── Tool Observation (工具调用) ← recordToolObservation()
|
||||||
|
├── Tool Observation (工具调用) ← recordToolObservation()
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 数据流
|
||||||
|
|
||||||
|
```
|
||||||
|
query.ts ──→ createTrace() # 每个 query turn 创建根 trace
|
||||||
|
│
|
||||||
|
├── claude.ts ──→ recordLLMObservation() # API 调用完成后记录 LLM 观察
|
||||||
|
│
|
||||||
|
├── toolExecution.ts ──→ recordToolObservation() # 每个工具执行记录
|
||||||
|
│
|
||||||
|
└── query.ts ──→ endTrace() # turn 结束时关闭 trace
|
||||||
|
|
||||||
|
runAgent.ts ──→ createSubagentTrace() # 子 Agent 有独立 trace
|
||||||
|
```
|
||||||
|
|
||||||
|
## 五、追踪详情
|
||||||
|
|
||||||
|
### 5.1 主 Agent Trace
|
||||||
|
|
||||||
|
每次 `query()` 调用(即用户一次对话 turn)创建一个类型为 `agent` 的根 Span:
|
||||||
|
|
||||||
|
- **名称**: `agent-run` 或 `agent-run:<querySource>`
|
||||||
|
- **元数据**: `provider`、`model`、`agentType: "main"`
|
||||||
|
- **Session ID**: 关联到 Langfuse 的 Session 功能,支持按会话聚合
|
||||||
|
|
||||||
|
### 5.2 子 Agent Trace
|
||||||
|
|
||||||
|
通过 `AgentTool` 启动的子 Agent 创建独立 Trace:
|
||||||
|
|
||||||
|
- **名称**: `agent:<agentType>`
|
||||||
|
- **元数据**: `provider`、`model`、`agentType`、`agentId`
|
||||||
|
- 独立于主 Trace,有自己的 Session 关联
|
||||||
|
|
||||||
|
### 5.3 LLM Generation
|
||||||
|
|
||||||
|
每次 API 调用记录为一个 `generation` 类型的 Span:
|
||||||
|
|
||||||
|
- **名称**: 按 Provider 映射(如 `ChatAnthropic`、`ChatOpenAI`、`ChatBedrockAnthropic` 等)
|
||||||
|
- **记录内容**: 输入消息、输出消息、Token 用量(input/output)
|
||||||
|
- **时间**: 精确记录 `startTime`、`endTime`、`completionStartTime`(TTFT 指标)
|
||||||
|
|
||||||
|
Provider 名称映射:
|
||||||
|
|
||||||
|
| Provider | Generation 名称 |
|
||||||
|
|----------|-----------------|
|
||||||
|
| `firstParty` | `ChatAnthropic` |
|
||||||
|
| `bedrock` | `ChatBedrockAnthropic` |
|
||||||
|
| `vertex` | `ChatVertexAnthropic` |
|
||||||
|
| `foundry` | `ChatFoundry` |
|
||||||
|
| `openai` | `ChatOpenAI` |
|
||||||
|
| `gemini` | `ChatGoogleGenerativeAI` |
|
||||||
|
| `grok` | `ChatXAI` |
|
||||||
|
|
||||||
|
### 5.4 工具执行
|
||||||
|
|
||||||
|
每个工具调用记录为一个 `tool` 类型的 Span:
|
||||||
|
|
||||||
|
- **名称**: 工具名(如 `FileEditTool`、`BashTool`)
|
||||||
|
- **记录内容**: 输入(经脱敏)、输出(经脱敏)、`toolUseId`
|
||||||
|
- **错误标记**: `isError` 标志 + `level: ERROR`
|
||||||
|
|
||||||
|
## 六、数据脱敏
|
||||||
|
|
||||||
|
所有上传到 Langfuse 的数据都会经过脱敏处理(`sanitize.ts`),确保敏感信息不会泄露:
|
||||||
|
|
||||||
|
### 6.1 全局脱敏(`sanitizeGlobal`)
|
||||||
|
|
||||||
|
- **Home 路径替换** — `/Users/xxx` → `~`
|
||||||
|
- **敏感字段遮蔽** — 匹配 `api_key`、`token`、`secret`、`password`、`credential`、`auth_header` 等关键字的字段值替换为 `[REDACTED]`
|
||||||
|
|
||||||
|
### 6.2 工具输入脱敏(`sanitizeToolInput`)
|
||||||
|
|
||||||
|
- 敏感字段遮蔽(同全局)
|
||||||
|
- `file_path`、`path`、`directory` 路径中的 Home 目录替换
|
||||||
|
|
||||||
|
### 6.3 工具输出脱敏(`sanitizeToolOutput`)
|
||||||
|
|
||||||
|
| 工具 | 脱敏策略 |
|
||||||
|
|------|---------|
|
||||||
|
| `FileReadTool`、`FileWriteTool`、`FileEditTool` | 完全遮蔽,仅保留字符数:`[file content redacted, N chars]` |
|
||||||
|
| `BashTool`、`PowerShellTool` | 截断至 500 字符 |
|
||||||
|
| `ConfigTool`、`MCPTool` | 完全遮蔽 |
|
||||||
|
| 其他工具 | 原样保留 |
|
||||||
|
|
||||||
|
## 七、消息格式转换
|
||||||
|
|
||||||
|
`convert.ts` 将 CCB 内部的 Message 类型转换为 Langfuse 期望的 OpenAI 兼容格式:
|
||||||
|
|
||||||
|
- **输入**: `UserMessage | AssistantMessage[]` + 可选 system prompt → `{ role, content }[]`
|
||||||
|
- **输出**: `AssistantMessage[]` → `{ role: 'assistant', content }`
|
||||||
|
- **Content Block 映射**:
|
||||||
|
- `text` → `{ type: 'text', text }`
|
||||||
|
- `thinking` / `redacted_thinking` → `{ type: 'thinking', thinking }`
|
||||||
|
- `tool_use` → `{ type: 'tool_use', id, name, input }`
|
||||||
|
- `tool_result` → `{ type: 'tool_result', tool_use_id, content }`
|
||||||
|
- `image` / `document` → 占位标记 `[image]` / `[document: name]`
|
||||||
|
|
||||||
|
## 八、生命周期
|
||||||
|
|
||||||
|
1. **初始化** — `initLangfuse()` 在 `src/entrypoints/init.ts` 启动时调用,创建 `LangfuseSpanProcessor` 和 `BasicTracerProvider`
|
||||||
|
2. **运行时** — 各追踪函数通过 `isLangfuseEnabled()` 检查,未配置时直接返回 `null`/跳过
|
||||||
|
3. **关闭** — `shutdownLangfuse()` 在进程退出时调用,强制 flush 并关闭 Processor
|
||||||
|
|
||||||
|
## 九、自部署 Langfuse
|
||||||
|
|
||||||
|
Langfuse 是开源项目,支持 Docker / Kubernetes 自部署:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name langfuse \
|
||||||
|
-p 3000:3000 \
|
||||||
|
-e DATABASE_URL=postgresql://... \
|
||||||
|
langfuse/langfuse:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
自部署后,将 `LANGFUSE_BASE_URL` 指向你的实例地址即可。详见 [Langfuse 自部署文档](https://langfuse.com/docs/deployment/self-host)。
|
||||||
|
|
||||||
|
如果没有自部署需求,可以直接使用 [Langfuse Cloud](https://cloud.langfuse.com),提供免费额度可用于测试。
|
||||||
|
|
||||||
|
## 十、相关文件
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `src/services/langfuse/client.ts` | OTel Provider 初始化、生命周期管理 |
|
||||||
|
| `src/services/langfuse/tracing.ts` | Trace/Span 创建和观察记录 |
|
||||||
|
| `src/services/langfuse/convert.ts` | Message 格式转换 |
|
||||||
|
| `src/services/langfuse/sanitize.ts` | 数据脱敏 |
|
||||||
|
| `src/services/langfuse/__tests__/langfuse.test.ts` | 测试(568 行) |
|
||||||
|
| `src/query.ts` | 主查询流程中的 Trace 集成 |
|
||||||
|
| `src/services/tools/toolExecution.ts` | 工具执行中的观察记录 |
|
||||||
|
| `packages/builtin-tools/src/tools/AgentTool/runAgent.ts` | 子 Agent Trace 创建 |
|
||||||
@@ -41,7 +41,7 @@ getMcpSkillCommands() 过滤 → SkillTool 调用
|
|||||||
|
|
||||||
### 2.2 技能筛选
|
### 2.2 技能筛选
|
||||||
|
|
||||||
文件:`src/commands.ts:547-558`
|
文件:`src/commands.ts:604-616`
|
||||||
|
|
||||||
`getMcpSkillCommands(mcpCommands)` 过滤条件:
|
`getMcpSkillCommands(mcpCommands)` 过滤条件:
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ feature('MCP_SKILLS') // feature flag 必须开启
|
|||||||
|
|
||||||
### 2.3 条件加载
|
### 2.3 条件加载
|
||||||
|
|
||||||
文件:`src/services/mcp/client.ts:117-121`
|
文件:`src/services/mcp/client.ts:129-133`
|
||||||
|
|
||||||
`fetchMcpSkillsForClient` 通过 `require()` 条件加载,feature flag 关闭时不加载任何模块:
|
`fetchMcpSkillsForClient` 通过 `require()` 条件加载,feature flag 关闭时不加载任何模块:
|
||||||
|
|
||||||
@@ -79,8 +79,8 @@ const fetchMcpSkillsForClient = feature('MCP_SKILLS')
|
|||||||
|
|
||||||
| 文件 | 行 | 说明 |
|
| 文件 | 行 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `src/commands.ts` | 547-558, 561-608 | 命令过滤和 SkillTool 命令收集 |
|
| `src/commands.ts` | 604-616, 620-633 | 命令过滤和 SkillTool 命令收集 |
|
||||||
| `src/services/mcp/client.ts` | 117-121, 1394, 1672, 2173-2181, 2346-2358 | 技能获取、缓存清除、连接时获取 |
|
| `src/services/mcp/client.ts` | 129-133, 1394, 1672, 2176 | 技能获取、缓存清除、连接时获取 |
|
||||||
| `src/services/mcp/useManageMCPConnections.ts` | 22-26, 682-740 | 实时刷新(prompts/resources 变化) |
|
| `src/services/mcp/useManageMCPConnections.ts` | 22-26, 682-740 | 实时刷新(prompts/resources 变化) |
|
||||||
|
|
||||||
## 三、关键设计决策
|
## 三、关键设计决策
|
||||||
|
|||||||
342
docs/features/pipes-and-lan.md
Normal file
342
docs/features/pipes-and-lan.md
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
# Pipes + LAN Pipes 完整功能指南
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
Pipes 系统提供 Claude Code CLI 实例之间的通讯能力,分两层:
|
||||||
|
|
||||||
|
1. **Pipes(本机)**:同一台机器上的多个 CLI 实例通过 UDS(Unix Domain Socket / Windows Named Pipe)协作
|
||||||
|
2. **LAN Pipes(局域网)**:不同机器上的 CLI 实例通过 TCP + UDP Multicast 协作
|
||||||
|
|
||||||
|
两层使用同一套协议(NDJSON)和同一套命令(`/pipes`、`/attach`、`/send` 等),对用户透明。
|
||||||
|
|
||||||
|
## Feature Flags
|
||||||
|
|
||||||
|
| Flag | 控制范围 | 默认 |
|
||||||
|
|------|----------|------|
|
||||||
|
| `UDS_INBOX` | 本机 Pipe IPC 全部功能 | dev/build 启用 |
|
||||||
|
| `LAN_PIPES` | 局域网 TCP + beacon 扩展 | dev/build 启用 |
|
||||||
|
|
||||||
|
手动启用:`FEATURE_UDS_INBOX=1 FEATURE_LAN_PIPES=1 bun run dev`
|
||||||
|
|
||||||
|
## 快速上手
|
||||||
|
|
||||||
|
### 本机多实例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 终端 1
|
||||||
|
bun run dev
|
||||||
|
# 启动后自动注册为 main
|
||||||
|
|
||||||
|
# 终端 2
|
||||||
|
bun run dev
|
||||||
|
# 自动注册为 sub-1,被 main 自动 attach
|
||||||
|
```
|
||||||
|
|
||||||
|
在终端 1 中输入 `/pipes`,可以看到两个实例。选中 sub-1 后,输入的消息会自动转发到 sub-1 执行。
|
||||||
|
|
||||||
|
### 局域网多机器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 机器 A (192.168.50.22)
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# 机器 B (192.168.50.27)
|
||||||
|
bun run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
两边启动后等 3-5 秒(beacon 广播间隔),LAN peers 会自动发现并 attach。输入 `/pipes` 可看到标记 `[LAN]` 的远端实例。
|
||||||
|
|
||||||
|
### 防火墙配置(两台机器都需要)
|
||||||
|
|
||||||
|
**Windows**(管理员 PowerShell):
|
||||||
|
```powershell
|
||||||
|
New-NetFirewallRule -DisplayName "Claude Code LAN Beacon (UDP)" -Direction Inbound -Protocol UDP -LocalPort 7101 -Action Allow -Profile Private
|
||||||
|
New-NetFirewallRule -DisplayName "Claude Code LAN Pipes (TCP)" -Direction Inbound -Protocol TCP -LocalPort 1024-65535 -Program (Get-Command bun).Source -Action Allow -Profile Private
|
||||||
|
New-NetFirewallRule -DisplayName "Claude Code LAN Beacon Out (UDP)" -Direction Outbound -Protocol UDP -RemotePort 7101 -Action Allow -Profile Private
|
||||||
|
# 确认网络为"专用":Get-NetConnectionProfile
|
||||||
|
```
|
||||||
|
|
||||||
|
**macOS**(首次运行时系统弹出对话框,点击"允许"即可):
|
||||||
|
```bash
|
||||||
|
# 如果需要手动放行 pf 防火墙:
|
||||||
|
echo "pass in proto udp from any to any port 7101" | sudo pfctl -ef -
|
||||||
|
```
|
||||||
|
|
||||||
|
**Linux**(firewalld / iptables):
|
||||||
|
```bash
|
||||||
|
# firewalld
|
||||||
|
sudo firewall-cmd --zone=trusted --add-port=7101/udp --permanent
|
||||||
|
sudo firewall-cmd --zone=trusted --add-port=1024-65535/tcp --permanent
|
||||||
|
sudo firewall-cmd --reload
|
||||||
|
|
||||||
|
# 或 iptables
|
||||||
|
sudo iptables -A INPUT -p udp --dport 7101 -j ACCEPT
|
||||||
|
sudo iptables -A INPUT -p tcp --dport 1024:65535 -m owner --uid-owner $(id -u) -j ACCEPT
|
||||||
|
```
|
||||||
|
|
||||||
|
确认:网络为局域网(非公共 WiFi),路由器未开启 AP 隔离。
|
||||||
|
|
||||||
|
## 交互面板与快捷键
|
||||||
|
|
||||||
|
### 状态栏
|
||||||
|
|
||||||
|
执行 `/pipes` 后,输入框底部出现 pipe 状态栏(单行):
|
||||||
|
|
||||||
|
```
|
||||||
|
pipe: cli-a91bad56 (main) 192.168.50.22 2/3 selected selected pipes only · ←/→ or m switch · Shift+↓ edit
|
||||||
|
```
|
||||||
|
|
||||||
|
状态栏始终可见(直到会话结束),显示:当前 pipe 名、角色、IP、已选数/总数、路由模式。
|
||||||
|
|
||||||
|
### 展开选择面板
|
||||||
|
|
||||||
|
按 **Shift+↓**(Shift + 下箭头)展开选择面板:
|
||||||
|
|
||||||
|
```
|
||||||
|
pipe: cli-a91bad56 (main) 192.168.50.22 ↑↓ move Space select ←/→ or m route Enter/Esc close Shift+↓ toggle
|
||||||
|
当前普通 prompt 走 已选 sub;切换不会清空选择
|
||||||
|
☑ cli-da029538 (sub-1 XC/192.168.50.22)
|
||||||
|
☐ cli-04d67950 (main vmwin11/192.168.50.27)
|
||||||
|
☑ cli-893747d3 [offline] (sub-2 vmwin11/192.168.50.27)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 面板内快捷键
|
||||||
|
|
||||||
|
| 快捷键 | 场景 | 作用 |
|
||||||
|
|--------|------|------|
|
||||||
|
| **Shift+↓** | 状态栏可见时 | 展开/收起选择面板 |
|
||||||
|
| **↑ / ↓** | 面板展开时 | 上下移动光标 |
|
||||||
|
| **Space** | 面板展开时 | 切换当前光标所在 pipe 的选中状态(☑ ↔ ☐) |
|
||||||
|
| **Enter** | 面板展开时 | 确认并关闭面板 |
|
||||||
|
| **Esc** | 面板展开时 | 取消并关闭面板 |
|
||||||
|
| **← / → 或 M** | 状态栏可见且有选中 pipe 时 | 切换路由模式(`selected pipes only` ↔ `local main`) |
|
||||||
|
|
||||||
|
### M 键 — 路由模式切换
|
||||||
|
|
||||||
|
M 键(或 ← / →)用于在两种路由模式之间切换,**无需展开面板**:
|
||||||
|
|
||||||
|
| 模式 | 状态栏显示 | 行为 |
|
||||||
|
|------|-----------|------|
|
||||||
|
| `selected pipes only` | 绿色高亮 | 输入的 prompt **仅**发送到选中的 pipe,本地不执行 |
|
||||||
|
| `local main` | 灰色 | 输入的 prompt 在**本地 main** 执行,不转发到任何 pipe |
|
||||||
|
|
||||||
|
切换路由模式**不会清空选择**。你可以在 `local main` 模式下保持选择,随时按 M 切回 `selected pipes only` 继续向远端发送。
|
||||||
|
|
||||||
|
### 完整操作流程示例
|
||||||
|
|
||||||
|
```
|
||||||
|
1. 输入 /pipes → 状态栏出现,显示发现的实例
|
||||||
|
2. 按 Shift+↓ → 展开选择面板
|
||||||
|
3. 按 ↓ 移动到目标 pipe → 光标移到 cli-04d67950
|
||||||
|
4. 按 Space → 选中 ☑ cli-04d67950
|
||||||
|
5. 按 Enter → 确认,面板收起
|
||||||
|
6. 输入 "帮我检查 git status" → prompt 自动发送到 cli-04d67950 执行
|
||||||
|
7. 按 M → 切换到 local main 模式
|
||||||
|
8. 输入 "本地做点什么" → 仅在本地执行
|
||||||
|
9. 按 M → 切回 selected pipes only
|
||||||
|
10. 输入 "继续远端任务" → 又发送到 cli-04d67950
|
||||||
|
```
|
||||||
|
|
||||||
|
## 命令参考
|
||||||
|
|
||||||
|
### /pipes
|
||||||
|
|
||||||
|
显示所有发现的实例,管理选择状态。再次执行 `/pipes` 切换面板展开/收起。
|
||||||
|
|
||||||
|
```
|
||||||
|
/pipes — 显示所有实例 + 切换选择面板
|
||||||
|
/pipes select <name> — 选中某实例(消息会广播到它)
|
||||||
|
/pipes deselect <name> — 取消选中
|
||||||
|
/pipes all — 全选
|
||||||
|
/pipes none — 全部取消
|
||||||
|
```
|
||||||
|
|
||||||
|
输出示例:
|
||||||
|
```
|
||||||
|
Your pipe: cli-a91bad56
|
||||||
|
Role: main
|
||||||
|
Machine ID: 205d6c3a...
|
||||||
|
IP: 192.168.50.22
|
||||||
|
Host: XC
|
||||||
|
|
||||||
|
Main machine: 205d6c3a... (this machine)
|
||||||
|
[main] cli-a91bad56 XC/192.168.50.22 [alive] (you)
|
||||||
|
☑ [sub-1] cli-da029538 XC/192.168.50.22 [alive] [connected]
|
||||||
|
|
||||||
|
LAN Peers:
|
||||||
|
☐ [main] cli-04d67950 vmwin11/192.168.50.27 tcp:192.168.50.27:58853 [LAN]
|
||||||
|
|
||||||
|
Selected: cli-da029538
|
||||||
|
```
|
||||||
|
|
||||||
|
### /attach <name>
|
||||||
|
|
||||||
|
手动 attach 到一个实例,使其成为你的 slave。
|
||||||
|
|
||||||
|
```
|
||||||
|
/attach cli-04d67950 — 连接到指定 pipe(自动解析 LAN TCP 端点)
|
||||||
|
```
|
||||||
|
|
||||||
|
attach 后,对方变为 slave,你变为 master。可以向它发送 prompt。通常不需要手动 attach——heartbeat 会自动发现并连接。
|
||||||
|
|
||||||
|
### /detach <name>
|
||||||
|
|
||||||
|
断开与某个 slave 的连接。
|
||||||
|
|
||||||
|
```
|
||||||
|
/detach cli-04d67950
|
||||||
|
```
|
||||||
|
|
||||||
|
### /send <name> <message>
|
||||||
|
|
||||||
|
向指定 pipe 发送消息(不依赖选择状态,直接指定目标)。
|
||||||
|
|
||||||
|
```
|
||||||
|
/send cli-04d67950 请帮我检查一下日志
|
||||||
|
/send tcp:192.168.50.27:58853 hello — 直接通过 TCP 地址发送
|
||||||
|
```
|
||||||
|
|
||||||
|
### /claim-main
|
||||||
|
|
||||||
|
强制声明当前机器为 main(用于 main 意外退出后的恢复)。
|
||||||
|
|
||||||
|
## 消息路由
|
||||||
|
|
||||||
|
### 选中 pipe 后的自动路由
|
||||||
|
|
||||||
|
1. 通过 `/pipes select` 或 Shift+Down 面板选中一个或多个 pipe
|
||||||
|
2. 在输入框中正常输入消息
|
||||||
|
3. 消息自动发送到所有选中的已连接 pipe
|
||||||
|
4. 每个 pipe 独立执行,结果流式回传到 main 的消息列表
|
||||||
|
|
||||||
|
### 路由模式
|
||||||
|
|
||||||
|
| 模式 | 行为 |
|
||||||
|
|------|------|
|
||||||
|
| `selected`(默认) | 消息发送到选中的 pipe |
|
||||||
|
| `local` | 消息仅在本地执行,不转发 |
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
### 通信协议
|
||||||
|
|
||||||
|
所有通讯使用 NDJSON(Newline-Delimited JSON),每行一个消息:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"type":"ping","from":"cli-abc","ts":"2026-04-11T00:00:00.000Z"}
|
||||||
|
{"type":"prompt","data":"帮我查看 git status","from":"cli-abc","ts":"..."}
|
||||||
|
{"type":"stream","data":"正在执行...","from":"cli-def","ts":"..."}
|
||||||
|
{"type":"done","data":"","from":"cli-def","ts":"..."}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 消息类型
|
||||||
|
|
||||||
|
| 类型 | 方向 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `ping`/`pong` | 双向 | 健康检查 |
|
||||||
|
| `attach_request`/`accept`/`reject` | M→S/S→M | 连接控制 |
|
||||||
|
| `detach` | M→S | 断开连接 |
|
||||||
|
| `prompt` | M→S | 主向从发送 prompt |
|
||||||
|
| `prompt_ack` | S→M | 从确认接收 |
|
||||||
|
| `stream` | S→M | 从流式回传 AI 输出 |
|
||||||
|
| `tool_start`/`tool_result` | S→M | 工具执行通知 |
|
||||||
|
| `done` | S→M | 本轮完成 |
|
||||||
|
| `error` | 双向 | 错误通知 |
|
||||||
|
| `permission_request`/`response`/`cancel` | 双向 | 权限审批转发 |
|
||||||
|
|
||||||
|
### 传输层
|
||||||
|
|
||||||
|
```
|
||||||
|
本机 LAN
|
||||||
|
┌──────────────┐ ┌──────────────┐
|
||||||
|
│ PipeServer │ │ PipeServer │
|
||||||
|
│ UDS sock │ │ UDS sock │
|
||||||
|
│ TCP :rand │◄───TCP───►│ TCP :rand │
|
||||||
|
├──────────────┤ ├──────────────┤
|
||||||
|
│ LanBeacon │◄──UDP────►│ LanBeacon │
|
||||||
|
│ 224.0.71.67 │ mcast │ 224.0.71.67 │
|
||||||
|
└──────────────┘ └──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
- **UDS**:本机实例间通讯,通过文件系统路径寻址(`~/.claude/pipes/cli-xxx.sock`)
|
||||||
|
- **TCP**:LAN 实例间通讯,动态端口,通过 beacon 发现
|
||||||
|
- **UDP Multicast**:peer 发现,3 秒广播一次 announce 包
|
||||||
|
|
||||||
|
### 角色模型
|
||||||
|
|
||||||
|
| 角色 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `main` | 首个启动的实例,管理 registry |
|
||||||
|
| `sub` | 后续启动的同机实例(或被 attach 的 LAN 实例) |
|
||||||
|
| `master` | attach 了至少一个 slave 的实例 |
|
||||||
|
| `slave` | 被 master attach 控制的实例 |
|
||||||
|
|
||||||
|
角色转换:
|
||||||
|
- 首个启动 → `main`
|
||||||
|
- 同机后续启动 → `sub`(自动被 main attach → `slave`)
|
||||||
|
- LAN 发现 → 两边都是 `main`,heartbeat 自动互相 attach
|
||||||
|
- 被 attach → 变为 `slave`(可通过 `/detach` 恢复)
|
||||||
|
|
||||||
|
### 发现机制
|
||||||
|
|
||||||
|
**本机**:通过 `~/.claude/pipes/registry.json` 文件(带文件锁),`machineId` 绑定主机身份。
|
||||||
|
|
||||||
|
**LAN**:通过 UDP multicast beacon:
|
||||||
|
1. 每 3 秒广播 `{ proto, pipeName, machineId, ip, tcpPort, role }`
|
||||||
|
2. 收到其他实例的 announce → 记入 peers Map
|
||||||
|
3. 15 秒未收到 → 标记 peer lost
|
||||||
|
4. Heartbeat 合并 local registry + beacon peers → 统一 attach 目标列表
|
||||||
|
|
||||||
|
### Heartbeat 循环(5 秒间隔)
|
||||||
|
|
||||||
|
```
|
||||||
|
main/master 角色:
|
||||||
|
1. cleanupStaleEntries() — 清理 registry 中死掉的条目
|
||||||
|
2. getAliveSubs() — 获取存活的本地 subs
|
||||||
|
3. refreshDiscoveredPipes() — 刷新 discoveredPipes(包含 LAN peers)
|
||||||
|
4. 合并 LAN peers 到 state
|
||||||
|
5. 构建统一 attach 目标列表 — 本地 subs + LAN peers
|
||||||
|
6. 遍历未连接的目标 → 自动 attach
|
||||||
|
7. 清理断开的 slave 连接 — 同时检查 local registry 和 beacon
|
||||||
|
|
||||||
|
sub 角色:
|
||||||
|
1. 检测 main 是否存活
|
||||||
|
2. main 死亡 → 同机则接管 main 角色,跨机则独立
|
||||||
|
```
|
||||||
|
|
||||||
|
## 关键文件
|
||||||
|
|
||||||
|
| 文件 | 职责 |
|
||||||
|
|------|------|
|
||||||
|
| `src/utils/pipeTransport.ts` | PipeServer(双模 UDS+TCP)、PipeClient、类型定义 |
|
||||||
|
| `src/utils/lanBeacon.ts` | UDP multicast beacon、singleton 管理 |
|
||||||
|
| `src/utils/pipeRegistry.ts` | Registry CRUD、角色判定、machineId、LAN merge |
|
||||||
|
| `src/utils/peerAddress.ts` | 地址解析(uds:/bridge:/tcp: scheme) |
|
||||||
|
| `src/screens/REPL.tsx` | Bootstrap、heartbeat、cleanup、prompt 路由 |
|
||||||
|
| `src/hooks/useMasterMonitor.ts` | Slave client registry、消息订阅 |
|
||||||
|
| `src/hooks/useSlaveNotifications.ts` | Slave 端通知处理 |
|
||||||
|
| `src/commands/pipes/pipes.ts` | /pipes 命令 |
|
||||||
|
| `src/commands/attach/attach.ts` | /attach 命令 |
|
||||||
|
| `src/commands/send/send.ts` | /send 命令 |
|
||||||
|
| `packages/builtin-tools/src/tools/SendMessageTool/SendMessageTool.ts` | AI 发消息工具(含 tcp: 支持) |
|
||||||
|
|
||||||
|
## 后续优化方向
|
||||||
|
|
||||||
|
### 安全(P0)
|
||||||
|
|
||||||
|
1. **TCP 认证**:首次连接时交换 HMAC-SHA256 token(基于 machineId + session secret),防止未授权设备连接
|
||||||
|
2. **JSON schema 验证**:在所有 `JSON.parse` 入口点增加 Zod 校验,防止 prototype pollution
|
||||||
|
3. **Beacon 信息脱敏**:hash machineId 后再广播,不暴露硬件序列号
|
||||||
|
|
||||||
|
### 可靠性(P1)
|
||||||
|
|
||||||
|
4. **多网卡选择**:`getLocalIp()` 应优先选择 RFC 1918 地址,排除 VPN/Docker 接口
|
||||||
|
5. **TCP target 验证**:`parseTcpTarget()` 应限制目标为已知 beacon peers 或 RFC 1918 范围
|
||||||
|
6. **PipeServer close()**:改为 `Promise.allSettled` 并行关闭 UDS + TCP,加 `_closing` guard
|
||||||
|
|
||||||
|
### 功能(P2)
|
||||||
|
|
||||||
|
7. **mDNS/DNS-SD**:作为 multicast 受限环境下的 beacon 替代方案
|
||||||
|
8. **固定端口配置**:允许用户指定 TCP 端口范围,便于防火墙精确配置
|
||||||
|
9. **TLS 加密**:TCP 传输加密,防中间人窃听
|
||||||
|
10. **双向 prompt**:当前只有 master → slave 方向,可考虑 slave 主动向 master 发送结果/请求
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# PROACTIVE — 主动模式
|
# PROACTIVE — 主动模式
|
||||||
|
|
||||||
> Feature Flag: `FEATURE_PROACTIVE=1`(与 `FEATURE_KAIROS=1` 共享功能)
|
> Feature Flag: `FEATURE_PROACTIVE=1`(与 `FEATURE_KAIROS=1` 共享功能)
|
||||||
> 实现状态:核心模块全部 Stub,布线完整
|
> 实现状态:核心循环与 SleepTool 已落地,部分外围文档仍在补齐
|
||||||
> 引用数:37
|
> 引用数:37
|
||||||
|
|
||||||
## 一、功能概述
|
## 一、功能概述
|
||||||
@@ -21,13 +21,13 @@ PROACTIVE 实现 Tick 驱动的自主代理。CLI 在用户不输入时也能持
|
|||||||
|
|
||||||
| 模块 | 文件 | 状态 | 说明 |
|
| 模块 | 文件 | 状态 | 说明 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| 核心逻辑 | `src/proactive/index.ts` | **Stub** | `activateProactive()`、`deactivateProactive()`、`isProactiveActive() => false` |
|
| 核心逻辑 | `src/proactive/index.ts` | **已实现** | `activateProactive()`、`deactivateProactive()`、`pause/resume`、`nextTickAt` 调度状态 |
|
||||||
| SleepTool 提示 | `src/tools/SleepTool/prompt.ts` | **完整** | 工具提示定义(工具名:`Sleep`) |
|
| SleepTool 提示 | `src/tools/SleepTool/prompt.ts` | **完整** | 工具提示定义(工具名:`Sleep`) |
|
||||||
| 命令注册 | `src/commands.ts:62-65` | **布线** | 动态加载 `./commands/proactive.js` |
|
| 命令注册 | `src/commands.ts:62-65` | **布线** | 动态加载 `./commands/proactive.js` |
|
||||||
| 工具注册 | `src/tools.ts:26-28` | **布线** | SleepTool 动态加载 |
|
| 工具注册 | `src/tools.ts:26-28` | **布线** | SleepTool 动态加载 |
|
||||||
| REPL 集成 | `src/screens/REPL.tsx` | **布线** | tick 驱动逻辑、占位符、页脚 UI |
|
| REPL 集成 | `src/screens/REPL.tsx` | **已实现** | tick 驱动、standby/sleeping 状态、页脚与 bridge automation metadata 上报 |
|
||||||
| 系统提示 | `src/constants/prompts.ts:860-914` | **完整** | 自主工作行为指令(~55 行详细 prompt) |
|
| 系统提示 | `src/constants/prompts.ts:864-918` | **完整** | 自主工作行为指令(~55 行详细 prompt) |
|
||||||
| 会话存储 | `src/utils/sessionStorage.ts:4892-4912` | **布线** | tick 消息注入对话流 |
|
| 远控状态镜像 | `src/utils/sessionState.ts` | **已实现** | 向 remote-control/CCR 暴露 `automation_state` 元数据 |
|
||||||
|
|
||||||
### 2.2 系统提示内容
|
### 2.2 系统提示内容
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ PROACTIVE 实现 Tick 驱动的自主代理。CLI 在用户不输入时也能持
|
|||||||
### 2.3 数据流
|
### 2.3 数据流
|
||||||
|
|
||||||
```
|
```
|
||||||
activateProactive() [需要实现]
|
activateProactive()
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
Tick 调度器启动
|
Tick 调度器启动
|
||||||
@@ -62,20 +62,22 @@ Tick 调度器启动
|
|||||||
└── 无事可做 → 必须调用 SleepTool
|
└── 无事可做 → 必须调用 SleepTool
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
SleepTool 等待 [需要实现]
|
SleepTool 等待
|
||||||
|
│
|
||||||
|
├── 用户插入新工作 / 队列中有命令 → 立即唤醒
|
||||||
|
├── proactive 被关闭 → 立即中断
|
||||||
|
└── 进入休眠时向远端 surfaces 上报 `automation_state = sleeping`
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
下一个 tick 到达
|
下一个 tick 到达
|
||||||
```
|
```
|
||||||
|
|
||||||
## 三、需要补全的内容
|
## 三、当前行为补充
|
||||||
|
|
||||||
| 优先级 | 模块 | 工作量 | 说明 |
|
- `standby`:proactive 已开启,当前没有执行中的 turn,且已调度下一个 tick。
|
||||||
|--------|------|--------|------|
|
- `sleeping`:模型显式调用 `SleepTool` 进入等待窗口。
|
||||||
| 1 | `src/proactive/index.ts` | 中 | Tick 调度器、activate/deactivate 状态机、pause/resume |
|
- remote-control/CCR 通过 `external_metadata.automation_state` 接收这两个状态,用于 Web UI 的 Autopilot 状态显示。
|
||||||
| 2 | `src/tools/SleepTool/SleepTool.ts` | 小 | 工具执行(等待指定时间后触发 tick) |
|
- `SleepTool` 现在不是纯定时器;它会在共享命令队列出现新工作时提前醒来。
|
||||||
| 3 | `src/commands/proactive.js` | 小 | `/proactive` 斜杠命令处理器 |
|
|
||||||
| 4 | `src/hooks/useProactive.ts` | 中 | React hook(REPL 引用但不存在) |
|
|
||||||
|
|
||||||
## 四、关键设计决策
|
## 四、关键设计决策
|
||||||
|
|
||||||
@@ -101,9 +103,11 @@ FEATURE_PROACTIVE=1 FEATURE_KAIROS=1 FEATURE_KAIROS_BRIEF=1 bun run dev
|
|||||||
|
|
||||||
| 文件 | 职责 |
|
| 文件 | 职责 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `src/proactive/index.ts` | 核心逻辑(stub) |
|
| `src/proactive/index.ts` | 核心逻辑与 next-tick 状态 |
|
||||||
| `src/tools/SleepTool/prompt.ts` | SleepTool 工具提示 |
|
| `src/tools/SleepTool/prompt.ts` | SleepTool 工具提示 |
|
||||||
| `src/constants/prompts.ts:860-914` | 自主工作系统提示 |
|
| `src/tools/SleepTool/SleepTool.ts` | 休眠/唤醒执行逻辑 |
|
||||||
| `src/screens/REPL.tsx` | REPL tick 集成 |
|
| `src/constants/prompts.ts:864-918` | 自主工作系统提示 |
|
||||||
|
| `src/screens/REPL.tsx` | REPL tick 集成与 automation 状态上报 |
|
||||||
| `src/utils/sessionStorage.ts:4892-4912` | Tick 消息注入 |
|
| `src/utils/sessionStorage.ts:4892-4912` | Tick 消息注入 |
|
||||||
|
| `src/utils/sessionState.ts` | bridge/CCR metadata 镜像 |
|
||||||
| `src/components/PromptInput/PromptInputFooterLeftSide.tsx` | 页脚 UI 状态 |
|
| `src/components/PromptInput/PromptInputFooterLeftSide.tsx` | 页脚 UI 状态 |
|
||||||
|
|||||||
357
docs/features/remote-control-self-hosting.md
Normal file
357
docs/features/remote-control-self-hosting.md
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
# Remote Control Server 私有化部署指南
|
||||||
|
|
||||||
|
本指南说明如何将 Remote Control Server (RCS) 部署到私有环境,并通过 Claude Code CLI 连接使用。
|
||||||
|
|
||||||
|
## 架构概览
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────┐ ┌──────────────────────┐
|
||||||
|
│ Claude Code CLI │ ◄── HTTP/SSE/WS ─►│ Remote Control │
|
||||||
|
│ (Bridge Worker) │ 长轮询 + 心跳 │ Server (RCS) │
|
||||||
|
└──────────────────┘ │ │
|
||||||
|
│ ┌──────────────┐ │
|
||||||
|
┌──────────────────┐ HTTP/SSE │ │ In-Memory │ │
|
||||||
|
│ Web UI 控制面板 │ ◄─────────────── │ │ Store │ │
|
||||||
|
│ (/code/*) │ │ └──────────────┘ │
|
||||||
|
│ (React + Vite) │ │ ┌──────────────┐ │
|
||||||
|
└──────────────────┘ │ │ JWT Auth │ │
|
||||||
|
│ └──────────────┘ │
|
||||||
|
┌──────────────────┐ │ ┌──────────────┐ │
|
||||||
|
│ acp-link │ ◄── ACP Relay ─── │ │ ACP Handler │ │
|
||||||
|
│ + ACP Agent │ WebSocket │ └──────────────┘ │
|
||||||
|
└──────────────────┘ └──────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**RCS 是一个纯内存的中间服务**,它的职责是:
|
||||||
|
- 接收 Claude Code CLI 的环境注册和工作轮询
|
||||||
|
- 接收 acp-link 的 ACP agent 注册,支持 WebSocket relay 桥接
|
||||||
|
- 提供 Web UI 供操作者远程监控和审批
|
||||||
|
- 通过 WebSocket/SSE 双向传输消息
|
||||||
|
- 管理会话、环境、权限请求
|
||||||
|
- 提供 ACP SSE event stream 供外部消费者订阅 channel group 事件
|
||||||
|
|
||||||
|
## 前置条件
|
||||||
|
|
||||||
|
- 一台可被 Claude Code CLI 和 Web 浏览器同时访问的服务器(物理机、VM、容器均可)
|
||||||
|
- [Docker](https://www.docker.com/)
|
||||||
|
- 启用 `BRIDGE_MODE` feature flag 的 Claude Code 构建
|
||||||
|
|
||||||
|
## 部署
|
||||||
|
|
||||||
|
### 构建 Docker 镜像
|
||||||
|
|
||||||
|
在项目根目录执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t rcs:latest -f packages/remote-control-server/Dockerfile .
|
||||||
|
```
|
||||||
|
|
||||||
|
### 启动容器
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name rcs \
|
||||||
|
-p 3000:3000 \
|
||||||
|
-e RCS_API_KEYS=sk-rcs-your-secret-key-here \
|
||||||
|
-e RCS_BASE_URL=https://rcs.example.com \
|
||||||
|
-v rcs-data:/app/data \
|
||||||
|
--restart unless-stopped \
|
||||||
|
rcs:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Compose
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
rcs:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: packages/remote-control-server/Dockerfile
|
||||||
|
args:
|
||||||
|
VERSION: "0.1.0"
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- RCS_API_KEYS=sk-rcs-your-secret-key-here
|
||||||
|
- RCS_BASE_URL=https://rcs.example.com
|
||||||
|
volumes:
|
||||||
|
- rcs-data:/app/data
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
rcs-data:
|
||||||
|
```
|
||||||
|
|
||||||
|
启动:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 环境变量参考
|
||||||
|
|
||||||
|
### 服务器端
|
||||||
|
|
||||||
|
| 变量 | 必填 | 默认值 | 说明 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| `RCS_API_KEYS` | **是** | _(空)_ | API 密钥列表,逗号分隔。用于客户端认证和 JWT 签名。**务必设置强密钥** |
|
||||||
|
| `RCS_PORT` | 否 | `3000` | 服务监听端口 |
|
||||||
|
| `RCS_HOST` | 否 | `0.0.0.0` | 服务监听地址 |
|
||||||
|
| `RCS_BASE_URL` | 否 | `http://localhost:3000` | 外部访问 URL。用于生成 WebSocket 连接地址,必须与客户端实际访问的地址一致 |
|
||||||
|
| `RCS_VERSION` | 否 | `0.1.0` | 版本号,显示在 `/health` 响应中 |
|
||||||
|
| `RCS_POLL_TIMEOUT` | 否 | `8` | V1 工作轮询超时(秒) |
|
||||||
|
| `RCS_HEARTBEAT_INTERVAL` | 否 | `20` | 心跳间隔(秒) |
|
||||||
|
| `RCS_JWT_EXPIRES_IN` | 否 | `3600` | JWT 令牌有效期(秒) |
|
||||||
|
| `RCS_DISCONNECT_TIMEOUT` | 否 | `300` | 断线判定超时(秒) |
|
||||||
|
| `RCS_WS_IDLE_TIMEOUT` | 否 | `30` | WebSocket 空闲超时(秒),Bun 发送协议级 ping |
|
||||||
|
| `RCS_WS_KEEPALIVE_INTERVAL` | 否 | `20` | 服务端→客户端 keep_alive 帧间隔(秒),防止反向代理关闭空闲连接 |
|
||||||
|
|
||||||
|
### 客户端(Claude Code CLI)
|
||||||
|
|
||||||
|
| 变量 | 必填 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `CLAUDE_BRIDGE_BASE_URL` | **是** | RCS 服务器地址,例如 `https://rcs.example.com`。设置此变量即启用自托管模式,跳过 GrowthBook 门控 |
|
||||||
|
| `CLAUDE_BRIDGE_OAUTH_TOKEN` | **是** | 认证令牌,必须与服务器端 `RCS_API_KEYS` 中的某个值匹配 |
|
||||||
|
| `CLAUDE_BRIDGE_SESSION_INGRESS_URL` | 否 | WebSocket 入口地址(默认与 `CLAUDE_BRIDGE_BASE_URL` 相同) |
|
||||||
|
| `CLAUDE_CODE_REMOTE` | 否 | 设为 `1` 时标记为远程执行模式 |
|
||||||
|
|
||||||
|
## Claude Code 客户端连接
|
||||||
|
|
||||||
|
### 1. 设置环境变量
|
||||||
|
|
||||||
|
在运行 Claude Code 的机器上设置:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export CLAUDE_BRIDGE_BASE_URL="https://rcs.example.com"
|
||||||
|
export CLAUDE_BRIDGE_OAUTH_TOKEN="sk-rcs-your-secret-key-here"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 启动 Claude Code
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 dev 模式(BRIDGE_MODE 默认启用)
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# 或使用构建产物
|
||||||
|
bun run dist/cli.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 执行 /remote-control 命令
|
||||||
|
|
||||||
|
在 Claude Code 的 REPL 中输入:
|
||||||
|
|
||||||
|
```
|
||||||
|
/remote-control
|
||||||
|
```
|
||||||
|
|
||||||
|
环境型 Remote Control(例如 `claude remote-control` 子命令)会向 RCS 注册环境,注册成功后在终端显示连接 URL:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://rcs.example.com/code?bridge=<environmentId>
|
||||||
|
```
|
||||||
|
|
||||||
|
交互式 REPL 方式(`--remote-control` 或 `/remote-control`)在某些桥接模式下也可能直接给出会话 URL:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://rcs.example.com/code/session_<id>
|
||||||
|
```
|
||||||
|
|
||||||
|
两种 URL 都可以直接在浏览器打开并远程操控当前会话;只有 environment 模式才会出现在 Web UI 的环境列表中。
|
||||||
|
|
||||||
|
若已连接,再次执行 `/remote-control` 会显示对话框,包含以下选项:
|
||||||
|
- **Disconnect this session** — 断开远程连接
|
||||||
|
- **Show QR code** — 显示/隐藏二维码
|
||||||
|
- **Continue** — 保持连接,继续使用
|
||||||
|
|
||||||
|
也可通过 CLI 参数直接启动:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
claude remote-control
|
||||||
|
# 或简写
|
||||||
|
claude rc
|
||||||
|
# 或
|
||||||
|
claude bridge
|
||||||
|
```
|
||||||
|
|
||||||
|
## Web UI 控制面板
|
||||||
|
|
||||||
|
通过 `/remote-control` 命令获取 URL 后,在浏览器打开即可使用。
|
||||||
|
|
||||||
|
### 技术栈(v2,2026-04-18 重构)
|
||||||
|
|
||||||
|
Web UI 已从原生 JS 重构为 **React + Vite + Radix UI**:
|
||||||
|
|
||||||
|
- **框架**: React 19 + Vite 构建,TypeScript
|
||||||
|
- **UI 组件**: Radix UI primitives(Dialog、Tabs、Select、Popover 等)
|
||||||
|
- **聊天组件**: 完整的 ACP 聊天界面,支持 Plan 可视化、工具调用展示、权限审批
|
||||||
|
- **AI Elements**: 独立的 AI 交互组件库(message、reasoning、tool、code-block、prompt-input 等)
|
||||||
|
- **ACP 直连**: 支持 QR 码扫描自动跳转 ACP 直连视图(`ACPDirectView`)
|
||||||
|
- **主题系统**: 暗色/亮色主题切换,遵循 Impeccable 设计系统
|
||||||
|
|
||||||
|
### 功能
|
||||||
|
|
||||||
|
- 查看已注册的运行环境(environment 模式),区分 ACP Agent 和 Claude Code 类型
|
||||||
|
- 创建和管理会话
|
||||||
|
- 实时查看对话消息和工具调用
|
||||||
|
- 查看 Autopilot 状态(`standby` / `sleeping`)和自动运行指示
|
||||||
|
- 查看 authoritative task snapshots 驱动的 Tasks 面板
|
||||||
|
- 审批 Claude Code 的工具权限请求
|
||||||
|
- 权限模式选择器(6 种模式:默认/自动接受编辑/跳过权限/规划/不询问/自动判断)
|
||||||
|
- 模型选择器(可选可用模型)
|
||||||
|
- Plan 可视化(进度条、状态图标、优先级标签)
|
||||||
|
- ACP QR 扫描自动跳转到 ACP 聊天界面
|
||||||
|
|
||||||
|
Web UI 使用 UUID 认证(无需用户账户),适合受信任网络环境。
|
||||||
|
|
||||||
|
## ACP 支持
|
||||||
|
|
||||||
|
RCS 支持 ACP (Agent Client Protocol) agent 通过 `acp-link` 包接入。
|
||||||
|
|
||||||
|
### 架构
|
||||||
|
|
||||||
|
```
|
||||||
|
acp-link ──REST注册──► RCS POST /v1/environments/bridge
|
||||||
|
acp-link ──WS identify──► RCS WebSocket (携带 agentId)
|
||||||
|
acp-link ◄──ACP relay──► RCS ◄──Web UI WS──► 浏览器
|
||||||
|
```
|
||||||
|
|
||||||
|
### 后端组件
|
||||||
|
|
||||||
|
| 文件 | 职责 |
|
||||||
|
|------|------|
|
||||||
|
| `src/routes/acp/index.ts` | ACP REST 路由:agents 列表、channel groups、relay |
|
||||||
|
| `src/transport/acp-ws-handler.ts` | ACP WebSocket 处理:agent 注册、心跳、消息转发 |
|
||||||
|
| `src/transport/acp-relay-handler.ts` | 前端 WS → acp-link 透传 + EventBus inbound 转发 |
|
||||||
|
| `src/transport/acp-sse-writer.ts` | SSE event stream 供外部消费者订阅 |
|
||||||
|
|
||||||
|
ACP 的 agents、channel groups、relay 和 channel-group SSE 端点都要求有效
|
||||||
|
API key。浏览器 `EventSource` 不能发送 `Authorization` header,外部订阅
|
||||||
|
`/acp/channel-groups/:id/events` 时需要使用 `fetch` + `ReadableStream` 并带
|
||||||
|
`Authorization: Bearer <api-key>`。
|
||||||
|
|
||||||
|
### acp-link 连接
|
||||||
|
|
||||||
|
详见 [acp-link 文档](./acp-link.md)。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 在 RCS 环境中启动 acp-link
|
||||||
|
# 注意:claude 本身不支持 ACP,需要用 ccb-bun --acp
|
||||||
|
ACP_RCS_URL=http://localhost:3000 \
|
||||||
|
ACP_RCS_TOKEN=sk-rcs-your-key \
|
||||||
|
acp-link ccb-bun -- --acp
|
||||||
|
```
|
||||||
|
|
||||||
|
ACP session 在 Web UI 中显示品牌色标签,与普通 Claude Code session 区分。
|
||||||
|
|
||||||
|
## 工作流程详解
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────────────┐
|
||||||
|
│ 完整工作流程 │
|
||||||
|
└──────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
1. Claude Code CLI 启动,设置环境变量指向自托管 RCS
|
||||||
|
|
||||||
|
2. 用户执行 /remote-control 命令
|
||||||
|
|
||||||
|
3. 注册环境
|
||||||
|
CLI ──POST /v1/environments/bridge──► RCS
|
||||||
|
CLI ◄── { environment_id, environment_secret } ── RCS
|
||||||
|
|
||||||
|
4. 终端显示连接 URL
|
||||||
|
https://rcs.example.com/code?bridge=<environmentId>
|
||||||
|
|
||||||
|
5. 开始工作轮询(循环)
|
||||||
|
CLI ──GET /v1/environments/:id/work/poll──► RCS
|
||||||
|
(长轮询,等待任务分配,超时 8 秒后重试)
|
||||||
|
|
||||||
|
6. 浏览器打开 URL → Web UI 创建任务
|
||||||
|
Browser ──POST /web/sessions──► RCS
|
||||||
|
RCS 分配 work 给正在轮询的 CLI
|
||||||
|
|
||||||
|
7. CLI 收到任务并确认
|
||||||
|
CLI ◄── { id, data: { type, sessionId } } ── RCS
|
||||||
|
CLI ──POST /v1/environments/:id/work/:workId/ack──► RCS
|
||||||
|
|
||||||
|
8. 建立会话连接
|
||||||
|
CLI ──WebSocket /v1/session_ingress──► RCS
|
||||||
|
(或使用 V2 的 SSE + HTTP POST)
|
||||||
|
|
||||||
|
9. 双向通信
|
||||||
|
CLI ──消息/工具调用结果──► RCS ──► Browser
|
||||||
|
CLI ◄──权限审批/指令───── RCS ◄──── Browser
|
||||||
|
CLI ──automation_state / task_state──► RCS ──► Browser
|
||||||
|
|
||||||
|
10. 心跳保活(每 20 秒)
|
||||||
|
CLI ──POST /v1/environments/:id/work/:workId/heartbeat──► RCS
|
||||||
|
|
||||||
|
11. 任务完成 → 归档会话 → 注销环境
|
||||||
|
```
|
||||||
|
|
||||||
|
## 故障排查
|
||||||
|
|
||||||
|
### Web UI 看不到当前 Autopilot 状态
|
||||||
|
|
||||||
|
- `standby`:proactive 已开启,正在等待下一个 tick
|
||||||
|
- `sleeping`:模型正在 `SleepTool` 等待窗口中
|
||||||
|
|
||||||
|
这两个状态通过 worker `external_metadata.automation_state` 上报。如果页面只显示普通 working spinner,优先检查 CLI 和 RCS 之间的 worker metadata PUT 是否成功。
|
||||||
|
|
||||||
|
### CLI 无法连接
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: Remote Control is not available in this build.
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因**:`BRIDGE_MODE` feature flag 未启用。
|
||||||
|
|
||||||
|
**解决**:使用 dev 模式(默认启用)或确保构建时包含 `BRIDGE_MODE` flag。
|
||||||
|
|
||||||
|
### 认证失败 (401)
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: Unauthorized
|
||||||
|
```
|
||||||
|
|
||||||
|
**检查项**:
|
||||||
|
1. `CLAUDE_BRIDGE_OAUTH_TOKEN` 是否与 `RCS_API_KEYS` 中的值匹配
|
||||||
|
2. API Key 是否包含多余的空格或换行
|
||||||
|
3. 两个环境变量是否都已正确设置
|
||||||
|
|
||||||
|
### WebSocket 连接中断
|
||||||
|
|
||||||
|
**检查项**:
|
||||||
|
1. 如果使用反向代理,确认已正确配置 WebSocket 升级(`Upgrade` / `Connection` 头)
|
||||||
|
2. 代理的 `proxy_read_timeout` 是否足够大(建议 86400 秒)
|
||||||
|
3. 网络防火墙是否允许 WebSocket 流量
|
||||||
|
|
||||||
|
### 健康检查
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://rcs.example.com/health
|
||||||
|
# 预期: {"status":"ok","version":"0.1.0"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 限制与注意事项
|
||||||
|
|
||||||
|
| 项目 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| 存储 | 纯内存存储(Map),服务器重启后所有会话和环境数据丢失 |
|
||||||
|
| 扩展 | 不支持水平扩展(无共享状态),单实例部署 |
|
||||||
|
| 并发 | 适合中小规模使用,大量并发会话可能需要性能调优 |
|
||||||
|
| 数据持久化 | `/app/data` 卷已预留但当前未使用,未来可能用于持久化 |
|
||||||
|
| Web UI 认证 | 基于 UUID,无用户账户系统,适合受信任网络环境 |
|
||||||
|
|
||||||
|
## 与云端模式对比
|
||||||
|
|
||||||
|
| 特性 | 云端 (Anthropic CCR) | 自托管 (RCS) |
|
||||||
|
|------|---------------------|--------------|
|
||||||
|
| 认证方式 | claude.ai OAuth 订阅 | API Key |
|
||||||
|
| GrowthBook 门控 | 需要 `tengu_ccr_bridge` 通过 | 自动跳过 |
|
||||||
|
| 功能标志 | 需要 `BRIDGE_MODE=1` | 同样需要 |
|
||||||
|
| 部署位置 | Anthropic 云端 | 用户自有服务器 |
|
||||||
|
| 数据流经 | Anthropic 基础设施 | 用户私有网络 |
|
||||||
|
| 依赖 | claude.ai 订阅 + OAuth | 仅需 API Key |
|
||||||
|
|
||||||
|
自托管模式的核心优势是:设置 `CLAUDE_BRIDGE_BASE_URL` 后,代码自动调用 `isSelfHostedBridge()` 返回 `true`,跳过所有 GrowthBook 和订阅检查,无需 claude.ai 账户即可使用。
|
||||||
426
docs/features/ssh-remote.md
Normal file
426
docs/features/ssh-remote.md
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
# SSH Remote — 远程主机运行 Claude Code
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
SSH Remote 提供两种方式在远程 Linux 主机上运行 Claude Code:
|
||||||
|
|
||||||
|
1. **SSH Remote 模块**(`ccb ssh <host>`)— 本地 REPL + 远程工具执行,自动部署二进制 + 认证隧道
|
||||||
|
2. **直接 SSH 运行**(`ssh <host> -t ccb`)— 远程已安装 ccb,直接启动交互式会话
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
### 方式一:SSH Remote 模块(完整模式)
|
||||||
|
|
||||||
|
适用场景:远端没有 API 凭据或没有安装 ccb。
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────── 本地 Windows/Mac/Linux ───────────┐
|
||||||
|
│ │
|
||||||
|
│ ccb ssh <host> [dir] │
|
||||||
|
│ │ │
|
||||||
|
│ ├── 1. SSHProbe: 探测远端平台/架构/已有二进制 │
|
||||||
|
│ ├── 2. SSHDeploy: 部署 dist/ 到远端 │
|
||||||
|
│ ├── 3. SSHAuthProxy: 启动本地认证代理 │
|
||||||
|
│ │ ├─ Unix Socket (Linux/Mac) │
|
||||||
|
│ │ └─ TCP 127.0.0.1:<port> (Windows) │
|
||||||
|
│ │ │
|
||||||
|
│ └── 4. SSH -R 反向隧道 + 启动远端 CLI │
|
||||||
|
│ ssh -R <remote>:<local> <host> \ │
|
||||||
|
│ ANTHROPIC_BASE_URL=... \ │
|
||||||
|
│ ANTHROPIC_AUTH_NONCE=... \ │
|
||||||
|
│ ccb --output-format stream-json │
|
||||||
|
│ │
|
||||||
|
│ ┌─────── 本地 REPL (Ink TUI) ───────┐ │
|
||||||
|
│ │ 用户输入 → NDJSON → SSH stdin │ │
|
||||||
|
│ │ SSH stdout → NDJSON → 渲染消息 │ │
|
||||||
|
│ │ 工具权限请求 → 本地审批 → 回传 │ │
|
||||||
|
│ └────────────────────────────────────┘ │
|
||||||
|
└────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
│ SSH 连接 (加密通道)
|
||||||
|
│
|
||||||
|
┌───────────────── 远端 Linux ──────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ ccb (自动部署或已存在) │
|
||||||
|
│ ├── --output-format stream-json │
|
||||||
|
│ ├── --input-format stream-json │
|
||||||
|
│ ├── --verbose -p │
|
||||||
|
│ │ │
|
||||||
|
│ ├── API 请求 → ANTHROPIC_BASE_URL │
|
||||||
|
│ │ → SSH 反向隧道 → 本地 AuthProxy │
|
||||||
|
│ │ → 注入真实凭据 → api.anthropic.com │
|
||||||
|
│ │ │
|
||||||
|
│ └── 工具执行 (Bash/Read/Write/...) │
|
||||||
|
│ 直接在远端文件系统上操作 │
|
||||||
|
└────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:直接 SSH 运行(简单模式)
|
||||||
|
|
||||||
|
适用场景:远端已安装 ccb 且已有 API 凭据(订阅或 API Key)。
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────── 本地终端 ───────┐ ┌──────── 远端 Linux ────────┐
|
||||||
|
│ │ SSH │ │
|
||||||
|
│ ssh <host> -t ccb │ ──────→ │ ccb (全局安装) │
|
||||||
|
│ │ │ ├── 使用远端自身凭据 │
|
||||||
|
│ 终端直接显示远端 TUI │ ←────── │ ├── 远端文件系统操作 │
|
||||||
|
│ │ TTY │ └── API 直连 Anthropic │
|
||||||
|
└─────────────────────────┘ └─────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 适用场景对比
|
||||||
|
|
||||||
|
| | SSH Remote 模块 | 直接 SSH 运行 |
|
||||||
|
|---|---|---|
|
||||||
|
| 远端需要安装 ccb | 不需要(自动部署) | 需要 |
|
||||||
|
| 远端需要 API 凭据 | 不需要(本地隧道) | 需要 |
|
||||||
|
| 本地需要安装 ccb | 需要 | 不需要(任何终端) |
|
||||||
|
| 斜杠命令 | 本地处理 | 远端处理 |
|
||||||
|
| 网络延迟敏感 | 高(NDJSON 双向) | 低(仅 TTY) |
|
||||||
|
| 推荐场景 | 远端无凭据/无安装 | 远端已配置完整 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 前置准备:SSH 密钥配置
|
||||||
|
|
||||||
|
两种方式都依赖 SSH 免密连接。以下是完整的密钥配置步骤。
|
||||||
|
|
||||||
|
### 1. 生成 SSH 密钥对(本地)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 生成 Ed25519 密钥(推荐)
|
||||||
|
ssh-keygen -t ed25519 -C "your-email@example.com" -f ~/.ssh/id_remote
|
||||||
|
|
||||||
|
# 或 RSA 4096 位
|
||||||
|
ssh-keygen -t rsa -b 4096 -C "your-email@example.com" -f ~/.ssh/id_remote
|
||||||
|
```
|
||||||
|
|
||||||
|
生成两个文件:
|
||||||
|
- `~/.ssh/id_remote` — 私钥(不可泄露)
|
||||||
|
- `~/.ssh/id_remote.pub` — 公钥(部署到远端)
|
||||||
|
|
||||||
|
### 2. 将公钥部署到远端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 方式 A:ssh-copy-id(推荐)
|
||||||
|
ssh-copy-id -i ~/.ssh/id_remote.pub user@remote-host
|
||||||
|
|
||||||
|
# 方式 B:手动复制
|
||||||
|
cat ~/.ssh/id_remote.pub | ssh user@remote-host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 配置 SSH Config(本地)
|
||||||
|
|
||||||
|
编辑 `~/.ssh/config`(不存在则创建):
|
||||||
|
|
||||||
|
```
|
||||||
|
Host my-server
|
||||||
|
HostName 192.168.1.100 # 远端 IP 或域名
|
||||||
|
User root # 远端用户名
|
||||||
|
IdentityFile ~/.ssh/id_remote # 私钥路径
|
||||||
|
ServerAliveInterval 60 # 防止连接超时断开
|
||||||
|
ServerAliveCountMax 3
|
||||||
|
```
|
||||||
|
|
||||||
|
配置后可直接用别名连接:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh my-server # 等同于 ssh -i ~/.ssh/id_remote root@192.168.1.100
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 文件权限设置
|
||||||
|
|
||||||
|
#### Linux / macOS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod 700 ~/.ssh
|
||||||
|
chmod 600 ~/.ssh/config
|
||||||
|
chmod 600 ~/.ssh/id_remote
|
||||||
|
chmod 644 ~/.ssh/id_remote.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Windows(OpenSSH 强制 ACL 检查)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 重置 .ssh 目录权限:仅允许当前用户 + SYSTEM
|
||||||
|
icacls "$env:USERPROFILE\.ssh" /inheritance:r /grant:r "$($env:USERNAME):(OI)(CI)F" /grant "SYSTEM:(OI)(CI)F"
|
||||||
|
|
||||||
|
# 修复 config 文件权限
|
||||||
|
icacls "$env:USERPROFILE\.ssh\config" /inheritance:r /grant:r "$($env:USERNAME):F" /grant "SYSTEM:F"
|
||||||
|
|
||||||
|
# 修复私钥权限
|
||||||
|
icacls "$env:USERPROFILE\.ssh\id_remote" /inheritance:r /grant:r "$($env:USERNAME):F" /grant "SYSTEM:F"
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Windows 常见错误**:如果 `icacls` 显示 `UNKNOWN\UNKNOWN` ACL 条目,需要先移除再重新授权。权限错误会导致 SSH 拒绝使用密钥。
|
||||||
|
|
||||||
|
### 5. 验证免密连接
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh my-server "echo 'SSH connection OK'"
|
||||||
|
# 应直接输出 "SSH connection OK",不要求输入密码
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
|
||||||
|
### 方式一:SSH Remote 模块
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 基本用法 — 自动探测、部署、启动
|
||||||
|
ccb ssh user@remote-host
|
||||||
|
|
||||||
|
# 使用 SSH Config 别名
|
||||||
|
ccb ssh my-server
|
||||||
|
|
||||||
|
# 指定远端工作目录
|
||||||
|
ccb ssh my-server /home/user/project
|
||||||
|
|
||||||
|
# 使用自定义远端二进制(跳过探测/部署)
|
||||||
|
ccb ssh my-server --remote-bin "bun /opt/ccb/dist/cli.js"
|
||||||
|
|
||||||
|
# 权限控制
|
||||||
|
ccb ssh my-server --permission-mode auto
|
||||||
|
ccb ssh my-server --dangerously-skip-permissions
|
||||||
|
|
||||||
|
# 恢复远端会话
|
||||||
|
ccb ssh my-server --continue
|
||||||
|
ccb ssh my-server --resume <session-uuid>
|
||||||
|
|
||||||
|
# 选择模型
|
||||||
|
ccb ssh my-server --model claude-sonnet-4-6-20250514
|
||||||
|
|
||||||
|
# 本地测试模式(不连接远端,测试 auth proxy 管道)
|
||||||
|
ccb ssh localhost --local
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:直接 SSH 运行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 启动交互式会话
|
||||||
|
ssh my-server -t ccb
|
||||||
|
|
||||||
|
# 指定工作目录
|
||||||
|
ssh my-server -t "ccb --cwd /home/user/project"
|
||||||
|
|
||||||
|
# 使用特定模型
|
||||||
|
ssh my-server -t "ccb --model claude-sonnet-4-6-20250514"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 构建与部署
|
||||||
|
|
||||||
|
### 构建产物
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装依赖
|
||||||
|
bun install
|
||||||
|
|
||||||
|
# 构建(输出到 dist/)
|
||||||
|
bun run build
|
||||||
|
```
|
||||||
|
|
||||||
|
产物说明:
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `dist/cli.js` | Bun 入口(`#!/usr/bin/env bun`) |
|
||||||
|
| `dist/cli-node.js` | Node.js 入口(`#!/usr/bin/env node` → `import ./cli.js`) |
|
||||||
|
| `dist/cli-bun.js` | Bun 专用入口 |
|
||||||
|
| `dist/chunk-*.js` | 代码分割 chunk 文件(约 668 个) |
|
||||||
|
|
||||||
|
### 运行方式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 方式 A:通过 bun 直接运行(开发/调试)
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# 方式 B:运行构建产物(bun 运行时)
|
||||||
|
bun dist/cli.js
|
||||||
|
|
||||||
|
# 方式 C:运行构建产物(node 运行时)
|
||||||
|
node dist/cli-node.js
|
||||||
|
|
||||||
|
# 方式 D:全局安装后使用命令名
|
||||||
|
ccb
|
||||||
|
```
|
||||||
|
|
||||||
|
### 全局安装
|
||||||
|
|
||||||
|
在项目根目录执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# bun 全局安装(推荐)
|
||||||
|
bun install -g .
|
||||||
|
|
||||||
|
# 创建的命令:
|
||||||
|
# ccb → dist/cli-node.js
|
||||||
|
# ccb-bun → dist/cli-bun.js
|
||||||
|
# claude-code-best → dist/cli-node.js
|
||||||
|
|
||||||
|
# 安装位置:~/.bun/bin/ccb
|
||||||
|
```
|
||||||
|
|
||||||
|
或使用 npm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g .
|
||||||
|
```
|
||||||
|
|
||||||
|
验证:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ccb --version
|
||||||
|
# → x.x.x (Claude Code)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 远端部署(全流程)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 登录远端
|
||||||
|
ssh my-server
|
||||||
|
|
||||||
|
# 2. 克隆或同步项目代码
|
||||||
|
git clone <repo-url> ~/ccb-project
|
||||||
|
cd ~/ccb-project
|
||||||
|
|
||||||
|
# 3. 安装运行时(如果没有 bun)
|
||||||
|
curl -fsSL https://bun.sh/install | bash
|
||||||
|
source ~/.bashrc
|
||||||
|
|
||||||
|
# 4. 安装依赖 + 构建
|
||||||
|
bun install
|
||||||
|
bun run build
|
||||||
|
|
||||||
|
# 5. 全局安装
|
||||||
|
bun install -g .
|
||||||
|
|
||||||
|
# 6. 确保非交互式 SSH 可访问 ccb 命令
|
||||||
|
# bun install -g 安装到 ~/.bun/bin/,但非交互式 SSH 不加载 .bashrc,
|
||||||
|
# 所以 PATH 中不包含 ~/.bun/bin/
|
||||||
|
# 解决方式(任选其一):
|
||||||
|
|
||||||
|
# 方式 A:符号链接到系统 PATH(推荐)
|
||||||
|
ln -sf ~/.bun/bin/ccb /usr/local/bin/ccb
|
||||||
|
|
||||||
|
# 方式 B:添加到 /etc/profile.d/(所有用户生效)
|
||||||
|
echo 'export PATH="$HOME/.bun/bin:$PATH"' > /etc/profile.d/bun-path.sh
|
||||||
|
|
||||||
|
# 方式 C:添加到 ~/.bash_profile(当前用户,ssh -t 时生效)
|
||||||
|
echo 'export PATH="$HOME/.bun/bin:$PATH"' >> ~/.bash_profile
|
||||||
|
|
||||||
|
# 7. 验证
|
||||||
|
ccb --version
|
||||||
|
|
||||||
|
# 8. 从本地测试
|
||||||
|
# (在本地终端)
|
||||||
|
ssh my-server -t ccb
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSH Remote 自动部署
|
||||||
|
|
||||||
|
使用 `ccb ssh <host>` 时,模块自动处理:
|
||||||
|
|
||||||
|
1. **SSHProbe** 探测远端 `~/.local/bin/claude` 或 `command -v claude`
|
||||||
|
2. 若二进制不存在或版本不匹配,**SSHDeploy** 通过 `scp` 传输 `dist/` 目录
|
||||||
|
3. 在远端创建 wrapper 脚本(`~/.local/bin/claude`)
|
||||||
|
4. 无需手动安装
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 模块结构
|
||||||
|
|
||||||
|
```
|
||||||
|
src/ssh/
|
||||||
|
├── createSSHSession.ts — 会话工厂:编排 probe → deploy → proxy → spawn
|
||||||
|
├── SSHSessionManager.ts — 双向 NDJSON 通信管理 + 权限转发 + 重连
|
||||||
|
├── SSHAuthProxy.ts — 本地认证代理(API 凭据隧道)
|
||||||
|
├── SSHProbe.ts — 远端主机探测(平台/架构/已有二进制)
|
||||||
|
├── SSHDeploy.ts — 远端二进制部署(scp + wrapper 脚本)
|
||||||
|
└── __tests__/
|
||||||
|
└── SSHSessionManager.test.ts — 17 个单元测试
|
||||||
|
```
|
||||||
|
|
||||||
|
## 关键技术细节
|
||||||
|
|
||||||
|
### 认证隧道
|
||||||
|
|
||||||
|
- **AuthProxy** 在本地监听(Unix socket 或 TCP),接收远端 CLI 的 API 请求
|
||||||
|
- 通过 SSH `-R` 反向端口转发隧道到远端
|
||||||
|
- AuthProxy 注入本地真实凭据(API key 或 OAuth token),转发到 `api.anthropic.com`
|
||||||
|
- `ANTHROPIC_AUTH_NONCE` header 防止未授权访问(nonce 通过环境变量传递给远端 CLI,远端 CLI 在每个 API 请求中携带此 header)
|
||||||
|
|
||||||
|
### waitForInit vs 存活检查
|
||||||
|
|
||||||
|
- **标准模式**:`waitForInit` 等待远端 CLI 发送 `{type:'system', subtype:'init'}` JSON 消息
|
||||||
|
- **`--remote-bin` 模式**:跳过 `waitForInit`(print+stream-json 模式下 init 只在首次查询后发送),改用 3 秒进程存活检查
|
||||||
|
|
||||||
|
### 重连机制
|
||||||
|
|
||||||
|
- `SSHSessionManager` 检测 SSH 连接断开后自动重连
|
||||||
|
- 重连时在远端 CLI 命令中追加 `--continue` 恢复会话
|
||||||
|
- 指数退避重试(最多 5 次,间隔 1s → 2s → 4s → 8s → 16s)
|
||||||
|
|
||||||
|
## Feature Flag
|
||||||
|
|
||||||
|
SSH Remote 功能受 `SSH_REMOTE` feature flag 控制:
|
||||||
|
|
||||||
|
- **Dev 模式**:默认启用
|
||||||
|
- **Build 模式**:需在 `build.ts` 的 `DEFAULT_BUILD_FEATURES` 中添加 `'SSH_REMOTE'`
|
||||||
|
- **运行时**:`FEATURE_SSH_REMOTE=1` 环境变量
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### `ccb: command not found`(SSH 远程执行时)
|
||||||
|
|
||||||
|
非交互式 SSH 不加载 `.bashrc`,`~/.bun/bin` 不在 PATH 中。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 解决:创建符号链接
|
||||||
|
ln -sf ~/.bun/bin/ccb /usr/local/bin/ccb
|
||||||
|
```
|
||||||
|
|
||||||
|
### SSH 密钥被拒绝
|
||||||
|
|
||||||
|
```
|
||||||
|
Permission denied (publickey)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 确认公钥已添加到远端 `~/.ssh/authorized_keys`
|
||||||
|
2. 确认本地私钥文件权限正确(`chmod 600`)
|
||||||
|
3. 确认 `~/.ssh/config` 中 `IdentityFile` 路径正确
|
||||||
|
4. Windows 用户检查 ACL 权限(见上方 Windows 权限设置)
|
||||||
|
|
||||||
|
### SSH 连接超时
|
||||||
|
|
||||||
|
```
|
||||||
|
ssh: connect to host x.x.x.x port 22: Connection timed out
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 确认远端 SSH 服务正在运行:`systemctl status sshd`
|
||||||
|
2. 确认防火墙允许 22 端口
|
||||||
|
3. 确认 IP 地址/域名正确
|
||||||
|
4. 在 `~/.ssh/config` 中添加 `ConnectTimeout 10`
|
||||||
|
|
||||||
|
### 403 Forbidden(SSH Remote 模块)
|
||||||
|
|
||||||
|
AuthProxy 的 nonce 验证失败。确认:
|
||||||
|
1. 远端 CLI 版本包含 nonce header 注入修复
|
||||||
|
2. `ANTHROPIC_AUTH_NONCE` 环境变量正确传递到远端
|
||||||
|
3. `src/services/api/client.ts` 中 `x-auth-nonce` header 已启用
|
||||||
|
|
||||||
|
### 远端 CLI 启动后立即退出
|
||||||
|
|
||||||
|
```
|
||||||
|
Remote process exited immediately (code 1)
|
||||||
|
```
|
||||||
|
|
||||||
|
1. 确认远端 `bun` / `node` 运行时可用
|
||||||
|
2. 手动在远端执行 `ccb --version` 验证安装
|
||||||
|
3. 检查 `--remote-bin` 路径是否正确
|
||||||
|
4. 查看 stderr 输出获取详细错误信息
|
||||||
310
docs/features/stub-recovery-design-1-4.md
Normal file
310
docs/features/stub-recovery-design-1-4.md
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
# Stub 恢复设计 1-4
|
||||||
|
|
||||||
|
> 日期:2026-04-12
|
||||||
|
> 目标:基于当前代码边界,为下一阶段 4 个 stub/半 stub 命令面给出可实施的设计方案。
|
||||||
|
> 排序原则:按建议实施顺序排序,不按问题严重性排序。
|
||||||
|
|
||||||
|
## 设计原则
|
||||||
|
|
||||||
|
- 先做能独立闭环、收益明确、改动边界清晰的项。
|
||||||
|
- 大项拆成 `MVP` 和 `Phase 2+`,避免一次性掉进大范围恢复。
|
||||||
|
- 优先复用已有状态、传输层、日志与配置能力,不重造协议。
|
||||||
|
- 设计以当前仓库实际代码为准,不以旧文档的理想状态为准。
|
||||||
|
|
||||||
|
## 1. `claude daemon status` / `claude daemon stop`
|
||||||
|
|
||||||
|
### 现状
|
||||||
|
|
||||||
|
- `start` 路径已有完整 supervisor + worker 生命周期:
|
||||||
|
`src/daemon/main.ts`
|
||||||
|
`src/daemon/workerRegistry.ts`
|
||||||
|
- `status` / `stop` 目前只是占位输出:
|
||||||
|
`src/daemon/main.ts`
|
||||||
|
- `/remote-control-server` 有自己的命令内 UI 状态,但只维护当前进程内的 `daemonProcess`,并不适合作为跨进程 CLI 管理基础:
|
||||||
|
`src/commands/remoteControlServer/remoteControlServer.tsx`
|
||||||
|
|
||||||
|
### 目标
|
||||||
|
|
||||||
|
- 让 `claude daemon status` 和 `claude daemon stop` 在另一个 CLI 进程中也能正确工作。
|
||||||
|
- 不依赖 TUI 内存态,不要求当前命令进程就是启动 daemon 的那个进程。
|
||||||
|
|
||||||
|
### MVP 方案
|
||||||
|
|
||||||
|
- 新增 daemon 状态文件,例如:
|
||||||
|
`~/.claude/daemon/remote-control.json`
|
||||||
|
- `start` 时写入:
|
||||||
|
- supervisor pid
|
||||||
|
- cwd
|
||||||
|
- startedAt
|
||||||
|
- worker kinds
|
||||||
|
- 最近状态
|
||||||
|
- `status`:
|
||||||
|
- 读取状态文件
|
||||||
|
- 用现有进程探测能力验证 pid 是否存活
|
||||||
|
- 输出 `running / stopped / stale`
|
||||||
|
- stale 时自动清理状态文件
|
||||||
|
- `stop`:
|
||||||
|
- 读取 pid
|
||||||
|
- 发送 `SIGTERM`
|
||||||
|
- 等待退出
|
||||||
|
- 超时后 `SIGKILL`
|
||||||
|
- 清理状态文件
|
||||||
|
|
||||||
|
### 代码范围
|
||||||
|
|
||||||
|
- 新增 `src/daemon/state.ts`
|
||||||
|
- 修改 `src/daemon/main.ts`
|
||||||
|
- 轻量修改 `src/commands/remoteControlServer/remoteControlServer.tsx`,让 UI 尽量读取同一份状态文件
|
||||||
|
|
||||||
|
### 验证
|
||||||
|
|
||||||
|
1. `claude daemon start`
|
||||||
|
2. 新开终端执行 `claude daemon status`
|
||||||
|
3. 执行 `claude daemon stop`
|
||||||
|
4. 再次执行 `claude daemon status`,确认返回 `stopped` 或清晰的 `stale cleaned`
|
||||||
|
|
||||||
|
### 风险
|
||||||
|
|
||||||
|
- Windows 信号模型和 Unix 不同,`stop` 需要超时兜底。
|
||||||
|
- 当前设计默认单 supervisor,不处理多实例并发。
|
||||||
|
|
||||||
|
### 工作量判断
|
||||||
|
|
||||||
|
- 小
|
||||||
|
- 适合作为下一步的首选实现项
|
||||||
|
|
||||||
|
## 2. `BG_SESSIONS`
|
||||||
|
|
||||||
|
### 现状
|
||||||
|
|
||||||
|
- fast-path 已接好:
|
||||||
|
`src/entrypoints/cli.tsx`
|
||||||
|
- session registry 已有真实实现:
|
||||||
|
`src/utils/concurrentSessions.ts`
|
||||||
|
- `exit` 在 bg session 内已会 `tmux detach-client`:
|
||||||
|
`src/commands/exit/exit.tsx`
|
||||||
|
- 但 CLI handler 仍全空:
|
||||||
|
`src/cli/bg.ts`
|
||||||
|
- task summary 仍然是 stub:
|
||||||
|
`src/utils/taskSummary.ts`
|
||||||
|
|
||||||
|
### 目标
|
||||||
|
|
||||||
|
- 先把 `ps` / `logs` / `kill` 做成真正有用的 session 管理命令。
|
||||||
|
- 不在第一阶段就强行补完 `attach` / `--bg`。
|
||||||
|
|
||||||
|
### Phase 2A:MVP
|
||||||
|
|
||||||
|
- 实现 `ps`
|
||||||
|
- 从 registry 读取 live sessions
|
||||||
|
- 展示 pid、kind、sessionId、cwd、name、startedAt、bridgeSessionId
|
||||||
|
- 如果有 activity/status,则一并展示
|
||||||
|
- 实现 `logs`
|
||||||
|
- 支持按 `sessionId / pid / name` 查找
|
||||||
|
- 优先复用本地 transcript/log 读取能力
|
||||||
|
- 如果 registry 里存在 `logPath`,支持 tail 文件
|
||||||
|
- 实现 `kill`
|
||||||
|
- 解析目标 session
|
||||||
|
- 发退出信号
|
||||||
|
- 清理 stale registry
|
||||||
|
|
||||||
|
### Phase 2B:后续
|
||||||
|
|
||||||
|
- 实现 `attach`
|
||||||
|
- 实现 `--bg`
|
||||||
|
- 实现 `taskSummary` 的中途状态更新
|
||||||
|
|
||||||
|
### 为什么要拆
|
||||||
|
|
||||||
|
- 现有 registry 记录了 `pid / sessionId / name / logPath`
|
||||||
|
- 但没有可靠的 tmux attach target
|
||||||
|
- 所以 `attach` 和 `--bg` 不是简单补 handler,而是需要补启动/附着元数据设计
|
||||||
|
|
||||||
|
### 代码范围
|
||||||
|
|
||||||
|
- 修改 `src/cli/bg.ts`
|
||||||
|
- 修改 `src/utils/concurrentSessions.ts` 以便后续 attach/--bg 扩展
|
||||||
|
- 修改 `src/utils/taskSummary.ts`
|
||||||
|
- 复用:
|
||||||
|
`src/utils/sessionStorage.ts`
|
||||||
|
`src/utils/udsClient.ts`
|
||||||
|
|
||||||
|
### 验证
|
||||||
|
|
||||||
|
1. `ps` 能列出 live sessions
|
||||||
|
2. `logs <sessionId|pid|name>` 能输出对应日志
|
||||||
|
3. `kill <sessionId|pid|name>` 能结束目标 session
|
||||||
|
|
||||||
|
### 风险
|
||||||
|
|
||||||
|
- `attach` / `--bg` 第二阶段需要 tmux 元数据设计
|
||||||
|
- Windows 下 tmux 路径需要明确降级策略
|
||||||
|
|
||||||
|
### 工作量判断
|
||||||
|
|
||||||
|
- `ps/logs/kill` 中等
|
||||||
|
- `attach/--bg` 明显更大,应分阶段
|
||||||
|
|
||||||
|
## 3. `TEMPLATES`
|
||||||
|
|
||||||
|
### 现状
|
||||||
|
|
||||||
|
- 命令入口只有 fast-path:
|
||||||
|
`src/entrypoints/cli.tsx`
|
||||||
|
- handler 是空的:
|
||||||
|
`src/cli/handlers/templateJobs.ts`
|
||||||
|
- `markdownConfigLoader` 已把 `templates` 纳入配置目录:
|
||||||
|
`src/utils/markdownConfigLoader.ts`
|
||||||
|
- `query / stopHooks` 已预留 job classifier 链路:
|
||||||
|
`src/query/stopHooks.ts`
|
||||||
|
- `jobs/classifier.ts` 仍是 stub:
|
||||||
|
`src/jobs/classifier.ts`
|
||||||
|
|
||||||
|
### 目标
|
||||||
|
|
||||||
|
- 把 `new / list / reply` 做成可用的模板任务系统。
|
||||||
|
- 第一阶段不碰复杂的自动分类与自动执行。
|
||||||
|
|
||||||
|
### MVP 方案
|
||||||
|
|
||||||
|
- 模板来源:
|
||||||
|
`.claude/templates/*.md`
|
||||||
|
- 模板格式:
|
||||||
|
复用现有 markdown + frontmatter 解析,不另外设计 DSL
|
||||||
|
- `list`
|
||||||
|
- 列出所有模板
|
||||||
|
- 显示模板名、description、路径
|
||||||
|
- `new <template> [args...]`
|
||||||
|
- 解析模板
|
||||||
|
- 在 `~/.claude/jobs/<job-id>/` 下创建 job 目录
|
||||||
|
- 写入 `template.md`、`input.txt`、`state.json`
|
||||||
|
- 返回 job id 与目录
|
||||||
|
- `reply <job-id> <text>`
|
||||||
|
- 将回复写入 `replies.jsonl` 或 `input.txt`
|
||||||
|
- 更新 `state.json`
|
||||||
|
|
||||||
|
### Phase 2
|
||||||
|
|
||||||
|
- 恢复 `src/jobs/classifier.ts`
|
||||||
|
- 让带 `CLAUDE_JOB_DIR` 的 job session 在 turn 完成后自动更新 `state.json`
|
||||||
|
- 再决定是否补自动 job runner
|
||||||
|
|
||||||
|
### 为什么要拆
|
||||||
|
|
||||||
|
- 当前证据表明这是“template job commands”,不是单纯模板列表
|
||||||
|
- 但自动 job 运行链路没有足够现成实现,先做文件系统 job lifecycle 更稳
|
||||||
|
|
||||||
|
### 代码范围
|
||||||
|
|
||||||
|
- 修改 [src/cli/handlers/templateJobs.ts](</e:/Source_code/Claude-code-bast/src/cli/handlers/templateJobs.ts:1>)
|
||||||
|
- 新增 `src/jobs/state.ts`
|
||||||
|
- 新增 `src/jobs/templates.ts`
|
||||||
|
- Phase 2 再改 [src/jobs/classifier.ts](</e:/Source_code/Claude-code-bast/src/jobs/classifier.ts:1>)
|
||||||
|
|
||||||
|
### 验证
|
||||||
|
|
||||||
|
1. `list` 能列出 `.claude/templates`
|
||||||
|
2. `new` 能创建 job 目录和状态文件
|
||||||
|
3. `reply` 能更新 job 内容和状态
|
||||||
|
4. Phase 2 再验证 classifier 写状态
|
||||||
|
|
||||||
|
### 风险
|
||||||
|
|
||||||
|
- frontmatter schema 需要先定义最小字段集
|
||||||
|
- 一旦扩展到“自动运行 job”,范围会明显膨胀
|
||||||
|
|
||||||
|
### 工作量判断
|
||||||
|
|
||||||
|
- MVP 中等
|
||||||
|
- 完整 job 系统偏大
|
||||||
|
|
||||||
|
## 4. `assistant [sessionId]`
|
||||||
|
|
||||||
|
### 现状
|
||||||
|
|
||||||
|
- attach 主流程其实已经存在:
|
||||||
|
[src/main.tsx](</e:/Source_code/Claude-code-bast/src/main.tsx:4708>)
|
||||||
|
- 远端 viewer 所需基础模块已存在:
|
||||||
|
[src/remote/RemoteSessionManager.ts](</e:/Source_code/Claude-code-bast/src/remote/RemoteSessionManager.ts:1>)
|
||||||
|
[src/hooks/useAssistantHistory.ts](</e:/Source_code/Claude-code-bast/src/hooks/useAssistantHistory.ts:1>)
|
||||||
|
[src/assistant/sessionHistory.ts](</e:/Source_code/Claude-code-bast/src/assistant/sessionHistory.ts:1>)
|
||||||
|
- 真正 stub 的主要是:
|
||||||
|
[src/assistant/sessionDiscovery.ts](</e:/Source_code/Claude-code-bast/src/assistant/sessionDiscovery.ts:1>)
|
||||||
|
[src/assistant/AssistantSessionChooser.ts](</e:/Source_code/Claude-code-bast/src/assistant/AssistantSessionChooser.ts:1>)
|
||||||
|
[src/commands/assistant/assistant.ts](</e:/Source_code/Claude-code-bast/src/commands/assistant/assistant.ts:7>)
|
||||||
|
[src/assistant/index.ts](</e:/Source_code/Claude-code-bast/src/assistant/index.ts:1>)
|
||||||
|
|
||||||
|
### 目标
|
||||||
|
|
||||||
|
- 不一次性恢复整个 KAIROS 助手系统。
|
||||||
|
- 先做“明确 sessionId 的 viewer attach 可用”,再逐步补 discovery / chooser / install。
|
||||||
|
|
||||||
|
### Phase 4A:MVP
|
||||||
|
|
||||||
|
- 只支持 `claude assistant <sessionId>`
|
||||||
|
- 对 `claude assistant` 无参数模式,先返回明确提示:
|
||||||
|
- 当前版本需要显式 `sessionId`
|
||||||
|
- discovery 尚未启用
|
||||||
|
- 这样可以直接复用现有 attach 分支,不必先恢复 chooser/install wizard
|
||||||
|
|
||||||
|
### Phase 4B
|
||||||
|
|
||||||
|
- 恢复 `discoverAssistantSessions()`
|
||||||
|
- 数据来源优先复用现有 sessions / bridge / teleport API,而不是新协议
|
||||||
|
- 让 `claude assistant` 无参数时能拿到候选 session 列表
|
||||||
|
|
||||||
|
### Phase 4C
|
||||||
|
|
||||||
|
- 恢复 `AssistantSessionChooser`
|
||||||
|
- 多 session 时可交互选择
|
||||||
|
|
||||||
|
### Phase 4D
|
||||||
|
|
||||||
|
- 最后考虑 install wizard 辅助函数
|
||||||
|
- 这部分属于“没有 session 时如何引导”,不是 attach 核心路径
|
||||||
|
|
||||||
|
### 为什么要拆
|
||||||
|
|
||||||
|
- attach 渲染层与远端消息通道大部分已经在
|
||||||
|
- 真正缺的是“如何发现目标 session”和“如何交互选择”
|
||||||
|
- 如果把 `src/assistant/index.ts` 的整套 KAIROS 正常模式也一起拉进来,范围会失控
|
||||||
|
|
||||||
|
### 代码范围
|
||||||
|
|
||||||
|
- Phase 4A:
|
||||||
|
- [src/main.tsx](</e:/Source_code/Claude-code-bast/src/main.tsx:4708>)
|
||||||
|
- [src/commands/assistant/index.ts](</e:/Source_code/Claude-code-bast/src/commands/assistant/index.ts:1>)
|
||||||
|
- Phase 4B:
|
||||||
|
- [src/assistant/sessionDiscovery.ts](</e:/Source_code/Claude-code-bast/src/assistant/sessionDiscovery.ts:1>)
|
||||||
|
- Phase 4C:
|
||||||
|
- [src/assistant/AssistantSessionChooser.ts](</e:/Source_code/Claude-code-bast/src/assistant/AssistantSessionChooser.ts:1>)
|
||||||
|
- Phase 4D:
|
||||||
|
- [src/commands/assistant/assistant.ts](</e:/Source_code/Claude-code-bast/src/commands/assistant/assistant.ts:7>)
|
||||||
|
|
||||||
|
### 验证
|
||||||
|
|
||||||
|
1. `claude assistant <sessionId>` 能进入 remote viewer
|
||||||
|
2. 历史懒加载工作正常
|
||||||
|
3. 无参数模式先给出明确提示
|
||||||
|
4. 后续阶段再分别验证 discovery / chooser / install
|
||||||
|
|
||||||
|
### 风险
|
||||||
|
|
||||||
|
- 这是四项里范围最大的
|
||||||
|
- 一旦把 KAIROS 正常模式整体拉入,会从“viewer attach”膨胀成“完整 assistant mode 恢复”
|
||||||
|
|
||||||
|
### 工作量判断
|
||||||
|
|
||||||
|
- Phase 4A 中等
|
||||||
|
- 4A-4D 全做完很大
|
||||||
|
|
||||||
|
## 建议执行顺序
|
||||||
|
|
||||||
|
1. `claude daemon status` / `claude daemon stop`
|
||||||
|
2. `BG_SESSIONS` 先做 `ps/logs/kill`
|
||||||
|
3. `TEMPLATES` 先做 job 文件系统 MVP
|
||||||
|
4. `assistant [sessionId]` 先做显式 sessionId attach,再补 discovery/chooser/install
|
||||||
|
|
||||||
|
## 简短结论
|
||||||
|
|
||||||
|
这四项里,最适合立刻实现的是 `daemon status/stop`。`BG_SESSIONS` 和 `TEMPLATES` 适合按 MVP 先补 handler 与文件系统闭环。`assistant [sessionId]` 不能整块硬上,应该按“attach → discovery → chooser → install”拆开恢复。
|
||||||
@@ -7,51 +7,13 @@
|
|||||||
|
|
||||||
| Feature | 引用 | 状态 | 类别 | 简要说明 |
|
| Feature | 引用 | 状态 | 类别 | 简要说明 |
|
||||||
|---------|------|------|------|---------|
|
|---------|------|------|------|---------|
|
||||||
| CHICAGO_MCP | 16 | N/A | 内部基础设施 | Anthropic 内部 MCP 基础设施,非外部可用 |
|
| CHICAGO_MCP | 16 | 已实现 | 工具 | Computer Use + Chrome MCP 控制(build 默认启用) |
|
||||||
| UDS_INBOX | 17 | Stub | 消息通信 | Unix 域套接字对等消息,进程间消息传递 |
|
| MONITOR_TOOL | 13 | 已实现 | 工具 | 后台监控工具,持续监视 shell 输出(build 默认启用) |
|
||||||
| MONITOR_TOOL | 13 | Stub | 工具 | 文件/进程监控工具,检测变更并通知 |
|
| BG_SESSIONS | 11 | 部分实现 | 会话管理 | 后台会话注册/清理已实现,任务摘要是 stub(dev 默认启用) |
|
||||||
| BG_SESSIONS | 11 | Stub | 会话管理 | 后台会话管理,支持多会话并行 |
|
| SHOT_STATS | 10 | 已实现 | 统计 | API 调用统计面板(build 默认启用) |
|
||||||
| SHOT_STATS | 10 | 无实现 | 统计 | 逐 prompt 统计信息收集 |
|
| EXTRACT_MEMORIES | 7 | 已实现 | 记忆 | 自动记忆提取(build 默认启用,受 GrowthBook 门控) |
|
||||||
| EXTRACT_MEMORIES | 7 | 无实现 | 记忆 | 自动从对话中提取重要信息作为记忆 |
|
| TEMPLATES | 6 | 部分实现 | 项目管理 | 项目/提示模板系统(dev 默认启用) |
|
||||||
| TEMPLATES | 6 | Stub | 项目管理 | 项目/提示模板系统 |
|
| LODESTONE | 6 | 已实现 | 深度链接 | URL 协议处理器(build 默认启用) |
|
||||||
| LODESTONE | 6 | N/A | 内部基础设施 | 内部基础设施模块 |
|
|
||||||
| STREAMLINED_OUTPUT | 1 | — | 输出 | 精简输出模式,减少终端输出量 |
|
|
||||||
| HOOK_PROMPTS | 1 | — | 钩子 | Hook 提示词,自定义钩子的提示注入 |
|
|
||||||
| CCR_AUTO_CONNECT | 3 | — | 远程控制 | CCR 自动连接,自动建立远程控制会话 |
|
|
||||||
| CCR_MIRROR | 4 | — | 远程控制 | CCR 镜像模式,会话状态同步 |
|
|
||||||
| CCR_REMOTE_SETUP | 1 | — | 远程控制 | CCR 远程设置,初始化远程控制配置 |
|
|
||||||
| NATIVE_CLIPBOARD_IMAGE | 2 | — | 系统集成 | 原生剪贴板图片,从剪贴板读取图片 |
|
|
||||||
| CONNECTOR_TEXT | 7 | — | 连接器 | 连接器文本,外部系统文本适配 |
|
|
||||||
| COMMIT_ATTRIBUTION | 12 | — | Git | Commit 归因,标记 commit 来源 |
|
|
||||||
| CACHED_MICROCOMPACT | 12 | — | 压缩 | 缓存微压缩,优化 compaction 性能 |
|
|
||||||
| PROMPT_CACHE_BREAK_DETECTION | 9 | — | 性能 | Prompt cache 中断检测,监控 cache miss |
|
|
||||||
| MEMORY_SHAPE_TELEMETRY | 3 | — | 遥测 | 记忆形态遥测,记忆使用模式追踪 |
|
|
||||||
| MCP_RICH_OUTPUT | 3 | — | MCP | MCP 富输出,增强 MCP 工具输出格式 |
|
|
||||||
| FILE_PERSISTENCE | 3 | — | 持久化 | 文件持久化,跨会话保持状态 |
|
|
||||||
| TREE_SITTER_BASH_SHADOW | 5 | Shadow | 安全 | Bash AST Shadow 模式(见 tree-sitter-bash.md) |
|
|
||||||
| QUICK_SEARCH | 5 | — | 搜索 | 快速搜索,优化的文件/内容搜索 |
|
|
||||||
| MESSAGE_ACTIONS | 5 | — | UI | 消息操作,对消息执行后处理动作 |
|
|
||||||
| DOWNLOAD_USER_SETTINGS | 5 | — | 配置 | 下载用户设置,从服务端同步配置 |
|
|
||||||
| DIRECT_CONNECT | 5 | — | 网络 | 直连模式,绕过代理直接连接 API |
|
|
||||||
| VERIFICATION_AGENT | 4 | — | Agent | 验证 Agent,专门用于验证代码变更 |
|
|
||||||
| TERMINAL_PANEL | 4 | — | UI | 终端面板,嵌入式终端输出显示 |
|
|
||||||
| SSH_REMOTE | 4 | — | 远程 | SSH 远程,通过 SSH 连接远程 Claude |
|
|
||||||
| REVIEW_ARTIFACT | 4 | — | 审查 | Review Artifact,代码审查产出物 |
|
|
||||||
| REACTIVE_COMPACT | 4 | — | 压缩 | 响应式压缩,基于上下文变化触发 compaction |
|
|
||||||
| HISTORY_PICKER | 4 | — | UI | 历史选择器,浏览和选择历史对话 |
|
|
||||||
| UPLOAD_USER_SETTINGS | 2 | — | 配置 | 上传用户设置,同步配置到服务端 |
|
|
||||||
| POWERSHELL_AUTO_MODE | 2 | — | 平台 | PowerShell 自动模式,Windows 权限自动化 |
|
|
||||||
| OVERFLOW_TEST_TOOL | 2 | — | 测试 | 溢出测试工具,测试上下文溢出处理 |
|
|
||||||
| NEW_INIT | 2 | — | 初始化 | 新版初始化流程 |
|
|
||||||
| HARD_FAIL | 2 | — | 错误处理 | 硬失败模式,不可恢复错误直接终止 |
|
|
||||||
| ENHANCED_TELEMETRY_BETA | 2 | — | 遥测 | 增强遥测 Beta,详细的性能指标收集 |
|
|
||||||
| COWORKER_TYPE_TELEMETRY | 2 | — | 遥测 | 协作者类型遥测,追踪协作模式 |
|
|
||||||
| BREAK_CACHE_COMMAND | 2 | — | 缓存 | 中断缓存命令,强制刷新 prompt cache |
|
|
||||||
| AWAY_SUMMARY | 2 | — | 摘要 | 离开摘要,用户返回时总结期间工作 |
|
|
||||||
| AUTO_THEME | 2 | — | UI | 自动主题,根据终端设置切换主题 |
|
|
||||||
| ALLOW_TEST_VERSIONS | 2 | — | 版本 | 允许测试版本,跳过版本检查 |
|
|
||||||
| AGENT_TRIGGERS_REMOTE | 2 | — | Agent | Agent 远程触发,从远程触发 Agent 任务 |
|
|
||||||
| AGENT_MEMORY_SNAPSHOT | 2 | — | Agent | Agent 记忆快照,保存/恢复 Agent 状态 |
|
|
||||||
|
|
||||||
## 单引用 Feature(40+ 个)
|
## 单引用 Feature(40+ 个)
|
||||||
|
|
||||||
@@ -67,10 +29,9 @@ BUILDING_CLAUDE_APPS, ANTI_DISTILLATION_CC, AGENT_TRIGGERS, ABLATION_BASELINE
|
|||||||
|
|
||||||
这些 feature 被列为 Tier 3 的原因:
|
这些 feature 被列为 Tier 3 的原因:
|
||||||
|
|
||||||
1. **内部基础设施**(CHICAGO_MCP, LODESTONE):Anthropic 内部使用,外部无法运行
|
1. **已实现但影响范围小**(CHICAGO_MCP, LODESTONE, SHOT_STATS, EXTRACT_MEMORIES, MONITOR_TOOL):已在 build/dev 默认启用,主要作为其他功能的基础设施
|
||||||
2. **纯 Stub 且引用低**(UDS_INBOX, MONITOR_TOOL, BG_SESSIONS):需要大量工作才能实现
|
2. **部分实现**(BG_SESSIONS, TEMPLATES):核心注册已实现,但部分功能如任务摘要仍是 stub
|
||||||
3. **实验性功能**(SHOT_STATS, EXTRACT_MEMORIES):尚在概念阶段
|
3. **辅助功能**(STREAMLINED_OUTPUT, HOOK_PROMPTS):影响范围小
|
||||||
4. **辅助功能**(STREAMLINED_OUTPUT, HOOK_PROMPTS):影响范围小
|
4. **CCR 系列**:依赖远程控制基础设施,需要 BRIDGE_MODE 先完善
|
||||||
5. **CCR 系列**:依赖远程控制基础设施,需要 BRIDGE_MODE 先完善
|
|
||||||
|
|
||||||
如需深入了解某个 Tier 3 feature,可以在代码库中搜索 `feature('FEATURE_NAME')` 查看具体使用场景。
|
如需深入了解某个 Tier 3 feature,可以在代码库中搜索 `feature('FEATURE_NAME')` 查看具体使用场景。
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ FEATURE_TOKEN_BUDGET=1 bun run dev
|
|||||||
| `src/query/tokenBudget.ts` | 93 | 预算追踪器 + continue/stop 决策 |
|
| `src/query/tokenBudget.ts` | 93 | 预算追踪器 + continue/stop 决策 |
|
||||||
| `src/bootstrap/state.ts:724-743` | 20 | turn 级 token 快照状态 |
|
| `src/bootstrap/state.ts:724-743` | 20 | turn 级 token 快照状态 |
|
||||||
| `src/constants/prompts.ts:538-551` | 14 | 系统提示注入 |
|
| `src/constants/prompts.ts:538-551` | 14 | 系统提示注入 |
|
||||||
| `src/utils/attachments.ts:3829-3845` | 17 | API attachment 附加 |
|
| `src/utils/attachments.ts:3830-3844` | 17 | API attachment 附加 |
|
||||||
| `src/query.ts:280,1311-1358` | 48 | 主循环集成 |
|
| `src/query.ts:280,1311-1358` | 48 | 主循环集成 |
|
||||||
| `src/screens/REPL.tsx:2897,2963,2138` | 20 | REPL 提交/完成/取消处理 |
|
| `src/screens/REPL.tsx:2897,2963,2138` | 20 | REPL 提交/完成/取消处理 |
|
||||||
| `src/components/Spinner.tsx:319-338` | 20 | 进度条 UI |
|
| `src/components/Spinner.tsx:319-338` | 20 | 进度条 UI |
|
||||||
|
|||||||
@@ -158,4 +158,4 @@ FEATURE_TREE_SITTER_BASH_SHADOW=1 bun run dev
|
|||||||
| `src/utils/bash/bashParser.ts` | 4437 | 纯 TS bash 解析器 |
|
| `src/utils/bash/bashParser.ts` | 4437 | 纯 TS bash 解析器 |
|
||||||
| `src/utils/bash/ast.ts` | 2680 | 安全分析器(核心) |
|
| `src/utils/bash/ast.ts` | 2680 | 安全分析器(核心) |
|
||||||
| `src/utils/bash/treeSitterAnalysis.ts` | 507 | AST 分析辅助 |
|
| `src/utils/bash/treeSitterAnalysis.ts` | 507 | AST 分析辅助 |
|
||||||
| `src/tools/BashTool/bashPermissions.ts:1670-1810` | ~140 | 权限集成 + Shadow 遥测 |
|
| `packages/builtin-tools/src/tools/BashTool/bashPermissions.ts` | ~140 | 权限集成 + Shadow 遥测 |
|
||||||
|
|||||||
114
docs/features/uds-inbox.md
Normal file
114
docs/features/uds-inbox.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# UDS_INBOX / pipes
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
`UDS_INBOX` 现在不是一个“空壳 flag”,而是一套已经落地的本机 IPC 能力。但它同时承载了两层不同目标,必须拆开理解:
|
||||||
|
|
||||||
|
1. **UDS peer messaging**
|
||||||
|
- 面向任意 Claude Code 进程。
|
||||||
|
- 使用 `src/utils/udsMessaging.ts` 和 `src/utils/udsClient.ts`。
|
||||||
|
- 对外入口是 `/peers` 和 `SendMessageTool` 的 `uds:<socket-path>` 地址。
|
||||||
|
2. **pipes control plane**
|
||||||
|
- 面向交互式 REPL 会话之间的主从协作。
|
||||||
|
- 使用 `src/utils/pipeTransport.ts`、`src/utils/pipeRegistry.ts` 和 `src/screens/REPL.tsx` 中的内联 bootstrap。
|
||||||
|
- 对外入口是 `/pipes`、`/attach`、`/detach`、`/send`、`/pipe-status`、`/history`、`/claim-main`。
|
||||||
|
|
||||||
|
这两层都依赖本机 socket,但职责不同。`/peers` 解决“找到其他会话并发消息”,`/pipes` 解决“把一个 REPL 变成另一个 REPL 的受控 worker”。
|
||||||
|
|
||||||
|
## 为什么要有单独的 `pipes`
|
||||||
|
|
||||||
|
单独的 `pipes` 层有三个实际理由:
|
||||||
|
|
||||||
|
1. **命名与角色模型不同**
|
||||||
|
- UDS peer 层按 `messagingSocketPath` 寻址。
|
||||||
|
- pipes 层按 `cli-xxxxxxxx` 会话名、`main/sub/master/slave` 角色和 `machineId` 注册表工作。
|
||||||
|
2. **交互语义不同**
|
||||||
|
- peer 层是通用消息投递。
|
||||||
|
- pipes 层需要 attach、detach、历史收集、选择性广播、状态栏和 REPL 快捷键。
|
||||||
|
3. **UI 集成不同**
|
||||||
|
- peer 层主要服务工具调用。
|
||||||
|
- pipes 层直接影响 REPL 提交路径和 PromptInput 页脚。
|
||||||
|
|
||||||
|
如果把两者硬合并,`SendMessageTool` 的通用寻址和 REPL 的主从控制会互相污染,命令语义也会变得混乱。
|
||||||
|
|
||||||
|
## 当前通信模型
|
||||||
|
|
||||||
|
### 1. UDS peer messaging
|
||||||
|
|
||||||
|
- 服务端:`src/utils/udsMessaging.ts`
|
||||||
|
- 客户端:`src/utils/udsClient.ts`
|
||||||
|
- 发现方式:读取 `~/.claude/sessions/*.json`
|
||||||
|
- 地址方式:`uds:<socket-path>`
|
||||||
|
- 传输方式:**本机 Unix socket / Windows named pipe**
|
||||||
|
|
||||||
|
这层是真正的“通用收件箱”。
|
||||||
|
|
||||||
|
### 2. pipes control plane
|
||||||
|
|
||||||
|
- 服务端/客户端:`src/utils/pipeTransport.ts`
|
||||||
|
- 注册表:`src/utils/pipeRegistry.ts`
|
||||||
|
- 生效入口:`src/screens/REPL.tsx`
|
||||||
|
- 发现方式:扫描 `~/.claude/pipes/` + `registry.json`
|
||||||
|
- 会话名:`cli-${sessionId.slice(0, 8)}`
|
||||||
|
- 传输方式:**本机 Unix socket / Windows named pipe**
|
||||||
|
|
||||||
|
这层是真正的“主从 REPL 协调平面”。
|
||||||
|
|
||||||
|
## 关于“局域网通信”的事实
|
||||||
|
|
||||||
|
当前实现**不是**真正的局域网传输。
|
||||||
|
|
||||||
|
代码里虽然保存了这些字段:
|
||||||
|
|
||||||
|
- `localIp`
|
||||||
|
- `hostname`
|
||||||
|
- `machineId`
|
||||||
|
- `mac`
|
||||||
|
|
||||||
|
但这些字段当前只用于:
|
||||||
|
|
||||||
|
1. 注册表展示
|
||||||
|
2. main/sub 身份判定
|
||||||
|
3. `claim-main` 的机器级归属切换
|
||||||
|
4. 状态输出与排障信息
|
||||||
|
|
||||||
|
它们**没有**被用于创建 TCP/WebSocket 连接。真正的传输仍然是 `getPipePath(name)` 返回的本机 socket 路径。
|
||||||
|
|
||||||
|
所以目前更准确的描述应该是:
|
||||||
|
|
||||||
|
- `pipes` 支持 **本机多实例协作**
|
||||||
|
- `registry` 带有 **机器身份元数据**
|
||||||
|
- 但 **尚未实现跨机器局域网 transport**
|
||||||
|
|
||||||
|
如果未来要做真局域网版本,至少还需要:
|
||||||
|
|
||||||
|
1. TCP/WebSocket transport
|
||||||
|
2. 认证与会话授权
|
||||||
|
3. 发现与地址交换
|
||||||
|
4. 超时、重连和安全边界
|
||||||
|
|
||||||
|
## 当前 REPL 行为
|
||||||
|
|
||||||
|
当前线上行为由 `src/screens/REPL.tsx` 的内联实现负责:
|
||||||
|
|
||||||
|
1. 启动时创建当前 REPL 的 pipe server
|
||||||
|
2. 通过 `pipeRegistry` 判定 `main` / `sub`
|
||||||
|
3. 处理 `attach_request` / `detach` / `prompt`
|
||||||
|
4. 主实例心跳探测并维护 `slaves`
|
||||||
|
5. `/pipes` 打开状态栏并维护选择器
|
||||||
|
6. 提交普通消息时,仅向**已连接**的 selected pipes 广播
|
||||||
|
|
||||||
|
最近的收敛点:
|
||||||
|
|
||||||
|
- 过去遗留了一套未接线的 hook 方案
|
||||||
|
- 当前已明确以 `REPL.tsx` 内联 bootstrap 为唯一生效实现
|
||||||
|
- 选中但未连接的 pipe 不再导致本地处理被错误跳过
|
||||||
|
|
||||||
|
## 文档与代码对齐约定
|
||||||
|
|
||||||
|
后续关于 `UDS_INBOX` / `pipes` 的说明应遵守以下表述:
|
||||||
|
|
||||||
|
1. 默认称为“本机 IPC / 本机多实例协作”
|
||||||
|
2. 不把 `localIp` / `hostname` 元数据表述成已完成的 LAN transport
|
||||||
|
3. 明确区分 `/peers` 和 `/pipes` 的两层职责
|
||||||
|
4. 以 `src/screens/REPL.tsx`、`src/utils/pipeTransport.ts`、`src/utils/pipeRegistry.ts` 为事实来源
|
||||||
@@ -22,16 +22,16 @@ ULTRAPLAN 在用户输入中检测 "ultraplan" 关键字时,自动进入增强
|
|||||||
|
|
||||||
| 模块 | 文件 | 行数 | 状态 |
|
| 模块 | 文件 | 行数 | 状态 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| 命令处理器 | `src/commands/ultraplan.tsx` | 472 | **完整** |
|
| 命令处理器 | `src/commands/ultraplan.tsx` | 525 | **完整** |
|
||||||
| CCR 会话 | `src/utils/ultraplan/ccrSession.ts` | 350 | **完整** |
|
| CCR 会话 | `src/utils/ultraplan/ccrSession.ts` | 349 | **完整** |
|
||||||
| 关键字检测 | `src/utils/ultraplan/keyword.ts` | 128 | **完整** |
|
| 关键字检测 | `src/utils/ultraplan/keyword.ts` | 127 | **完整** |
|
||||||
| 嵌入式提示 | `src/utils/ultraplan/prompt.txt` | 1 | **完整** |
|
| 嵌入式提示 | `src/utils/ultraplan/prompt.txt` | 1 | **完整** |
|
||||||
| REPL 对话框 | `src/screens/REPL.tsx` | — | **布线** |
|
| REPL 对话框 | `src/screens/REPL.tsx` | — | **布线** |
|
||||||
| 关键字高亮 | `src/components/PromptInput/PromptInput.tsx` | — | **布线** |
|
| 关键字高亮 | `src/components/PromptInput/PromptInput.tsx` | — | **布线** |
|
||||||
|
|
||||||
### 2.2 关键字检测
|
### 2.2 关键字检测
|
||||||
|
|
||||||
文件:`src/utils/ultraplan/keyword.ts`(128 行)
|
文件:`src/utils/ultraplan/keyword.ts`(127 行)
|
||||||
|
|
||||||
`findUltraplanTriggerPositions(text)` 智能过滤:
|
`findUltraplanTriggerPositions(text)` 智能过滤:
|
||||||
- 排除引号内的 "ultraplan"
|
- 排除引号内的 "ultraplan"
|
||||||
@@ -41,7 +41,7 @@ ULTRAPLAN 在用户输入中检测 "ultraplan" 关键字时,自动进入增强
|
|||||||
|
|
||||||
### 2.3 CCR 远程会话
|
### 2.3 CCR 远程会话
|
||||||
|
|
||||||
文件:`src/utils/ultraplan/ccrSession.ts`(350 行)
|
文件:`src/utils/ultraplan/ccrSession.ts`(349 行)
|
||||||
|
|
||||||
`ExitPlanModeScanner` 类实现完整的事件状态机:
|
`ExitPlanModeScanner` 类实现完整的事件状态机:
|
||||||
- `pollForApprovedExitPlanMode()` — 3 秒轮询间隔
|
- `pollForApprovedExitPlanMode()` — 3 秒轮询间隔
|
||||||
@@ -99,9 +99,9 @@ FEATURE_ULTRAPLAN=1 bun run dev
|
|||||||
|
|
||||||
| 文件 | 行数 | 职责 |
|
| 文件 | 行数 | 职责 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `src/commands/ultraplan.tsx` | 472 | 斜杠命令处理器 |
|
| `src/commands/ultraplan.tsx` | 525 | 斜杠命令处理器 |
|
||||||
| `src/utils/ultraplan/ccrSession.ts` | 350 | CCR 远程会话管理 |
|
| `src/utils/ultraplan/ccrSession.ts` | 349 | CCR 远程会话管理 |
|
||||||
| `src/utils/ultraplan/keyword.ts` | 128 | 关键字检测和替换 |
|
| `src/utils/ultraplan/keyword.ts` | 127 | 关键字检测和替换 |
|
||||||
| `src/utils/ultraplan/prompt.txt` | 1 | 嵌入式提示 |
|
| `src/utils/ultraplan/prompt.txt` | 1 | 嵌入式提示 |
|
||||||
| `src/utils/processUserInput/processUserInput.ts:468` | — | 关键字重定向 |
|
| `src/utils/processUserInput/processUserInput.ts:468` | — | 关键字重定向 |
|
||||||
| `src/components/PromptInput/PromptInput.tsx` | — | 彩虹高亮 |
|
| `src/components/PromptInput/PromptInput.tsx` | — | 彩虹高亮 |
|
||||||
|
|||||||
@@ -1,27 +1,32 @@
|
|||||||
# VOICE_MODE — 语音输入
|
# VOICE_MODE — 语音输入
|
||||||
|
|
||||||
> Feature Flag: `FEATURE_VOICE_MODE=1`
|
> Feature Flag: `FEATURE_VOICE_MODE=1`
|
||||||
> 实现状态:完整可用(需要 Anthropic OAuth)
|
> 实现状态:完整可用(双后端:Anthropic OAuth / 豆包 ASR)
|
||||||
> 引用数:46
|
> 引用数:46
|
||||||
|
|
||||||
## 一、功能概述
|
## 一、功能概述
|
||||||
|
|
||||||
VOICE_MODE 实现"按键说话"(Push-to-Talk)语音输入。用户按住空格键录音,音频通过 WebSocket 流式传输到 Anthropic STT 端点(Nova 3),实时转录显示在终端中。
|
VOICE_MODE 实现"按键说话"(Push-to-Talk)语音输入。用户按住空格键录音,音频流式传输到 STT 后端,实时转录显示在终端中。支持两个后端:
|
||||||
|
|
||||||
|
- **Anthropic STT(默认)**:通过 WebSocket 流式传输到 Nova 3 端点,需要 Anthropic OAuth
|
||||||
|
- **豆包 ASR(Doubao)**:通过 `doubaoime-asr` 包的 AsyncGenerator 协议流式识别,使用独立凭证文件,无需 Anthropic OAuth
|
||||||
|
|
||||||
### 核心特性
|
### 核心特性
|
||||||
|
|
||||||
- **Push-to-Talk**:长按空格键录音,释放后自动发送
|
- **Push-to-Talk**:长按空格键录音,释放后自动发送
|
||||||
- **流式转录**:录音过程中实时显示中间转录结果
|
- **流式转录**:录音过程中实时显示中间转录结果
|
||||||
- **无缝集成**:转录文本直接作为用户消息提交到对话
|
- **无缝集成**:转录文本直接作为用户消息提交到对话
|
||||||
|
- **双后端切换**:通过 `/voice` 命令参数选择 STT 后端,持久化到 settings.json
|
||||||
|
|
||||||
## 二、用户交互
|
## 二、用户交互
|
||||||
|
|
||||||
| 操作 | 行为 |
|
| 操作 | 行为 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 长按空格 | 开始录音,显示录音状态 |
|
| 长按空格 | 开始录音,显示录音状态 |
|
||||||
| 释放空格 | 停止录音,等待最终转录 |
|
| 释放空格 | 停止录音,转录结果自动提交 |
|
||||||
| 转录完成 | 自动插入到输入框并提交 |
|
| `/voice` | 切换语音模式开关(默认使用 Anthropic 后端) |
|
||||||
| `/voice` 命令 | 切换语音模式开关 |
|
| `/voice doubao` | 启用语音模式并使用豆包 ASR 后端 |
|
||||||
|
| `/voice anthropic` | 切换回 Anthropic STT 后端 |
|
||||||
|
|
||||||
### UI 反馈
|
### UI 反馈
|
||||||
|
|
||||||
@@ -35,26 +40,37 @@ VOICE_MODE 实现"按键说话"(Push-to-Talk)语音输入。用户按住空
|
|||||||
|
|
||||||
文件:`src/voice/voiceModeEnabled.ts`
|
文件:`src/voice/voiceModeEnabled.ts`
|
||||||
|
|
||||||
三层检查:
|
两层检查函数:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
// Anthropic 后端(需要 OAuth)
|
||||||
isVoiceModeEnabled() = hasVoiceAuth() && isVoiceGrowthBookEnabled()
|
isVoiceModeEnabled() = hasVoiceAuth() && isVoiceGrowthBookEnabled()
|
||||||
|
|
||||||
|
// 豆包后端 / 通用可用性检查(不需要 OAuth)
|
||||||
|
isVoiceAvailable() = isVoiceGrowthBookEnabled()
|
||||||
```
|
```
|
||||||
|
|
||||||
1. **Feature Flag**:`feature('VOICE_MODE')` — 编译时/运行时开关
|
1. **Feature Flag**:`feature('VOICE_MODE')` — 编译时/运行时开关
|
||||||
2. **GrowthBook Kill-Switch**:`!getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_quartz_disabled', false)` — 紧急关闭开关(默认 false = 未禁用)
|
2. **GrowthBook Kill-Switch**:`!getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_quartz_disabled', false)` — 紧急关闭开关(默认 false = 未禁用)
|
||||||
3. **Auth 检查**:`hasVoiceAuth()` — 需要 Anthropic OAuth token(非 API key)
|
3. **Auth 检查(仅 Anthropic)**:`hasVoiceAuth()` — 需要 Anthropic OAuth token(非 API key)
|
||||||
|
4. **Provider 检查**:`voiceProvider` 设置决定使用哪个后端,豆包后端跳过 OAuth 检查
|
||||||
|
|
||||||
### 3.2 核心模块
|
### 3.2 核心模块
|
||||||
|
|
||||||
| 模块 | 职责 |
|
| 模块 | 职责 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `src/voice/voiceModeEnabled.ts` | Feature flag + GrowthBook + Auth 三层门控 |
|
| `src/voice/voiceModeEnabled.ts` | Feature flag + GrowthBook + Auth 三层门控 |
|
||||||
| `src/hooks/useVoice.ts` | React hook 管理录音状态和 WebSocket 连接 |
|
| `src/hooks/useVoice.ts` | React hook 管理录音状态和后端连接 |
|
||||||
| `src/services/voiceStreamSTT.ts` | WebSocket 流式传输到 Anthropic STT |
|
| `src/services/voiceStreamSTT.ts` | Anthropic WebSocket 流式 STT |
|
||||||
|
| `src/services/doubaoSTT.ts` | 豆包 ASR 适配器(AsyncGenerator → VoiceStreamConnection) |
|
||||||
|
| `src/commands/voice/voice.ts` | `/voice` 命令实现,处理后端选择和持久化 |
|
||||||
|
| `src/hooks/useVoiceEnabled.ts` | 语音启用状态 hook,根据 provider 决定是否跳过 OAuth |
|
||||||
|
| `src/utils/settings/types.ts` | `voiceProvider: 'anthropic' | 'doubao'` 设置类型定义 |
|
||||||
|
|
||||||
### 3.3 数据流
|
### 3.3 数据流
|
||||||
|
|
||||||
|
#### Anthropic 后端
|
||||||
|
|
||||||
```
|
```
|
||||||
用户按下空格键
|
用户按下空格键
|
||||||
│
|
│
|
||||||
@@ -79,20 +95,108 @@ WebSocket 连接到 Anthropic STT 端点
|
|||||||
转录文本 → 插入输入框 → 自动提交
|
转录文本 → 插入输入框 → 自动提交
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### 豆包 ASR 后端
|
||||||
|
|
||||||
|
```
|
||||||
|
用户按下空格键
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
useVoice hook 激活(检测到 voiceProvider === 'doubao')
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
macOS 原生音频 / SoX 开始录音
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
connectDoubaoStream() 创建 AudioChunkQueue + VoiceStreamConnection
|
||||||
|
│
|
||||||
|
├──→ onReady 立即触发(无需等待握手)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
音频数据通过 AudioChunkQueue 传入 transcribeRealtime()
|
||||||
|
│
|
||||||
|
├──→ INTERIM_RESULT → 实时显示中间转录
|
||||||
|
├──→ FINAL_RESULT → 显示最终转录
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
用户释放空格键
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
finalize() 立即返回(豆包在录音过程中已返回结果,无需等待)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
转录文本 → 插入输入框 → 自动提交
|
||||||
|
```
|
||||||
|
|
||||||
### 3.4 音频录制
|
### 3.4 音频录制
|
||||||
|
|
||||||
支持两种音频后端:
|
支持两种音频后端(两个 STT 后端共享):
|
||||||
- **macOS 原生音频**:优先使用,低延迟
|
- **macOS 原生音频**:优先使用,低延迟
|
||||||
- **SoX(Sound eXchange)**:回退方案,跨平台
|
- **SoX(Sound eXchange)**:回退方案,跨平台
|
||||||
|
|
||||||
音频流通过 WebSocket 发送到 Anthropic 的 Nova 3 STT 模型。
|
### 3.5 豆包 ASR 适配器设计
|
||||||
|
|
||||||
|
文件:`src/services/doubaoSTT.ts`
|
||||||
|
|
||||||
|
豆包后端使用适配器模式,将 `doubaoime-asr` 的 AsyncGenerator 协议桥接到 `VoiceStreamConnection` 接口:
|
||||||
|
|
||||||
|
**AudioChunkQueue** — push 式异步队列:
|
||||||
|
- 实现 `AsyncIterable<Uint8Array>` 接口
|
||||||
|
- `push(chunk)` 将音频数据入队,`push(null)` 发送结束信号
|
||||||
|
- 内部维护等待者(waiting)和缓冲队列(chunks)两个状态
|
||||||
|
|
||||||
|
**connectDoubaoStream()** — 连接入口:
|
||||||
|
- 动态导入 `doubaoime-asr`(optionalDependencies)
|
||||||
|
- 从 `~/.claude/tts/doubao/credentials.json` 加载凭证
|
||||||
|
- 创建 AudioChunkQueue 和 VoiceStreamConnection
|
||||||
|
- 立即触发 `onReady`(避免与 useVoice 的音频缓冲死锁)
|
||||||
|
- `finalize()` 立即返回(豆包在录音过程中已返回结果)
|
||||||
|
- 后台 async IIFE 消费 `transcribeRealtime` generator,映射响应类型到回调
|
||||||
|
|
||||||
|
**响应类型映射**:
|
||||||
|
|
||||||
|
| doubaoime-asr ResponseType | 回调映射 |
|
||||||
|
|----------------------------|----------|
|
||||||
|
| SESSION_STARTED | 日志记录 |
|
||||||
|
| VAD_START | 日志记录 |
|
||||||
|
| INTERIM_RESULT | `onTranscript(text, false)` |
|
||||||
|
| FINAL_RESULT | `onTranscript(text, true)` |
|
||||||
|
| ERROR | `onError(errorMsg)` |
|
||||||
|
| SESSION_FINISHED | 日志记录 |
|
||||||
|
|
||||||
|
### 3.6 后端选择逻辑
|
||||||
|
|
||||||
|
文件:`src/hooks/useVoice.ts`
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// 判断当前 provider
|
||||||
|
isDoubaoProvider() → 读取 settings.voiceProvider
|
||||||
|
|
||||||
|
// handleKeyEvent 中的可用性检查
|
||||||
|
const sttAvailable = isDoubaoProvider()
|
||||||
|
? isDoubaoAvailableSync() // 乐观检查(首次返回 true)
|
||||||
|
: isVoiceStreamAvailable() // Anthropic WebSocket 检查
|
||||||
|
|
||||||
|
// attemptConnect 中的连接函数选择
|
||||||
|
const connectFn = isDoubaoProvider()
|
||||||
|
? connectDoubaoStream
|
||||||
|
: connectVoiceStream
|
||||||
|
```
|
||||||
|
|
||||||
|
豆包后端的特殊处理:
|
||||||
|
- 跳过 `getVoiceKeyterms()` 调用(豆包无需关键词提示)
|
||||||
|
- 跳过 Focus Mode(`if (!enabled || !focusMode || isDoubaoProvider())`)
|
||||||
|
|
||||||
## 四、关键设计决策
|
## 四、关键设计决策
|
||||||
|
|
||||||
1. **OAuth 独占**:语音模式使用 `voice_stream` 端点(claude.ai),仅 Anthropic OAuth 用户可用。API key、Bedrock、Vertex 用户无法使用
|
1. **双后端共存**:豆包后端作为独立适配器与 Anthropic 后端并存,不替换原有流程,通过 `voiceProvider` 设置切换
|
||||||
2. **GrowthBook 负向门控**:`tengu_amber_quartz_disabled` 默认 `false`,新安装自动可用(无需等 GrowthBook 初始化)
|
2. **设置持久化**:`voiceProvider` 存储在 `settings.json`,通过 `/voice` 命令修改,跨会话生效
|
||||||
3. **Keychain 缓存**:`getClaudeAIOAuthTokens()` 首次调用访问 macOS keychain(~20-50ms),后续缓存命中
|
3. **OAuth 独占(Anthropic)**:Anthropic 后端使用 `voice_stream` 端点(claude.ai),仅 OAuth 用户可用
|
||||||
4. **独立于主 feature flag**:`isVoiceGrowthBookEnabled()` 在 feature flag 关闭时短路返回 `false`,不触发任何模块加载
|
4. **豆包无需 OAuth**:豆包后端使用独立凭证文件,不依赖 Anthropic 认证,通过 `isVoiceAvailable()` 放宽门控
|
||||||
|
5. **GrowthBook 负向门控**:`tengu_amber_quartz_disabled` 默认 `false`,新安装自动可用
|
||||||
|
6. **onReady 立即触发**:豆包后端在连接建立后立即触发 `onReady`,避免与 useVoice 音频缓冲的时序死锁(Anthropic 需要等待 WebSocket 握手)
|
||||||
|
7. **finalize() 立即返回**:豆包在录音过程中已返回所有结果,用户抬手时无需等待处理
|
||||||
|
8. **乐观可用性检查**:`isDoubaoAvailableSync()` 在首次调用时返回 `true`,实际导入错误在 `connectDoubaoStream` 中处理
|
||||||
|
9. **optionalDependencies**:`doubaoime-asr` 作为可选依赖,安装失败不影响 Anthropic 后端
|
||||||
|
|
||||||
## 五、使用方式
|
## 五、使用方式
|
||||||
|
|
||||||
@@ -100,26 +204,60 @@ WebSocket 连接到 Anthropic STT 端点
|
|||||||
# 启用 feature
|
# 启用 feature
|
||||||
FEATURE_VOICE_MODE=1 bun run dev
|
FEATURE_VOICE_MODE=1 bun run dev
|
||||||
|
|
||||||
# 在 REPL 中使用
|
# 在 REPL 中使用 Anthropic 后端
|
||||||
# 1. 确保已通过 OAuth 登录(claude.ai 订阅)
|
# 1. 确保已通过 OAuth 登录(claude.ai 订阅)
|
||||||
# 2. 按住空格键说话
|
# 2. 输入 /voice 启用
|
||||||
# 3. 释放空格键等待转录
|
# 3. 按住空格键说话
|
||||||
# 4. 或使用 /voice 命令切换开关
|
# 4. 释放空格键等待转录
|
||||||
|
|
||||||
|
# 在 REPL 中使用豆包 ASR 后端
|
||||||
|
# 1. 确保 doubaoime-asr 已安装(bun add doubaoime-asr)
|
||||||
|
# 2. 配置凭证文件:~/.claude/tts/doubao/credentials.json
|
||||||
|
# 3. 输入 /voice doubao 启用
|
||||||
|
# 4. 按住空格键说话
|
||||||
|
# 5. 释放空格键,转录结果即刻显示
|
||||||
|
|
||||||
|
# 切换后端
|
||||||
|
/voice doubao # 切换到豆包 ASR
|
||||||
|
/voice anthropic # 切换回 Anthropic STT
|
||||||
|
/voice # 关闭语音模式
|
||||||
|
```
|
||||||
|
|
||||||
|
### 豆包凭证配置
|
||||||
|
|
||||||
|
凭证文件路径:`~/.claude/tts/doubao/credentials.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"deviceId": "...",
|
||||||
|
"installId": "...",
|
||||||
|
"cdid": "...",
|
||||||
|
"openudid": "...",
|
||||||
|
"clientudid": "...",
|
||||||
|
"token": "..."
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 六、外部依赖
|
## 六、外部依赖
|
||||||
|
|
||||||
| 依赖 | 说明 |
|
| 依赖 | 说明 | 适用后端 |
|
||||||
|------|------|
|
|------|------|----------|
|
||||||
| Anthropic OAuth | claude.ai 订阅登录,非 API key |
|
| Anthropic OAuth | claude.ai 订阅登录,非 API key | Anthropic |
|
||||||
| GrowthBook | `tengu_amber_quartz_disabled` 紧急关闭 |
|
| GrowthBook | `tengu_amber_quartz_disabled` 紧急关闭 | 通用 |
|
||||||
| macOS 原生音频 或 SoX | 音频录制 |
|
| macOS 原生音频 或 SoX | 音频录制 | 通用 |
|
||||||
| Nova 3 STT | 语音转文本模型 |
|
| Nova 3 STT | Anthropic 语音转文本模型 | Anthropic |
|
||||||
|
| doubaoime-asr | 豆包 ASR SDK(optionalDependencies) | 豆包 |
|
||||||
|
| 凭证文件 | `~/.claude/tts/doubao/credentials.json` | 豆包 |
|
||||||
|
|
||||||
## 七、文件索引
|
## 七、文件索引
|
||||||
|
|
||||||
| 文件 | 行数 | 职责 |
|
| 文件 | 职责 |
|
||||||
|------|------|------|
|
|------|------|
|
||||||
| `src/voice/voiceModeEnabled.ts` | 55 | 三层门控逻辑 |
|
| `src/voice/voiceModeEnabled.ts` | 三层门控逻辑 + `isVoiceAvailable()` |
|
||||||
| `src/hooks/useVoice.ts` | — | React hook(录音状态 + WebSocket) |
|
| `src/hooks/useVoice.ts` | React hook(录音状态 + 后端选择 + 连接管理) |
|
||||||
| `src/services/voiceStreamSTT.ts` | — | STT WebSocket 流式传输 |
|
| `src/hooks/useVoiceEnabled.ts` | 语音启用状态 hook(按 provider 决定 OAuth 检查) |
|
||||||
|
| `src/services/voiceStreamSTT.ts` | Anthropic STT WebSocket 流式传输 |
|
||||||
|
| `src/services/doubaoSTT.ts` | 豆包 ASR 适配器(AudioChunkQueue + connectDoubaoStream) |
|
||||||
|
| `src/commands/voice/voice.ts` | `/voice` 命令(开关 + 后端选择) |
|
||||||
|
| `src/commands/voice/index.ts` | 命令注册(去除 availability 限制) |
|
||||||
|
| `src/utils/settings/types.ts` | `voiceProvider` 类型定义 |
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# WEB_BROWSER_TOOL — 浏览器工具
|
# WEB_BROWSER_TOOL — 浏览器工具
|
||||||
|
|
||||||
> Feature Flag: `FEATURE_WEB_BROWSER_TOOL=1`
|
> Feature Flag: `FEATURE_WEB_BROWSER_TOOL=1`
|
||||||
> 实现状态:核心实现缺失,面板为 Stub,布线完整
|
> 实现状态:核心工具已实现,面板为 Stub,布线完整
|
||||||
> 引用数:4
|
> 引用数:4
|
||||||
|
|
||||||
## 一、功能概述
|
## 一、功能概述
|
||||||
@@ -14,8 +14,8 @@ WEB_BROWSER_TOOL 让模型可以启动浏览器实例、导航网页、与页面
|
|||||||
|
|
||||||
| 模块 | 文件 | 状态 |
|
| 模块 | 文件 | 状态 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| 浏览器面板 | `src/tools/WebBrowserTool/WebBrowserPanel.ts` | **Stub** — 返回 null |
|
| 浏览器面板 | `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserPanel.ts` | **Stub** — 返回 null |
|
||||||
| 浏览器工具 | `src/tools/WebBrowserTool/WebBrowserTool.ts` | **缺失** |
|
| 浏览器工具 | `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserTool.ts` | **已实现** |
|
||||||
| REPL 集成 | `src/screens/REPL.tsx` | **布线** — 渲染 WebBrowserPanel |
|
| REPL 集成 | `src/screens/REPL.tsx` | **布线** — 渲染 WebBrowserPanel |
|
||||||
| 工具注册 | `src/tools.ts` | **布线** — 动态加载 |
|
| 工具注册 | `src/tools.ts` | **布线** — 动态加载 |
|
||||||
| WebView 检测 | `src/main.tsx` | **布线** — `'WebView' in Bun` 检测 |
|
| WebView 检测 | `src/main.tsx` | **布线** — `'WebView' in Bun` 检测 |
|
||||||
@@ -44,8 +44,8 @@ WebBrowserPanel 在 REPL 侧边显示浏览器状态
|
|||||||
|
|
||||||
| 模块 | 工作量 | 说明 |
|
| 模块 | 工作量 | 说明 |
|
||||||
|------|--------|------|
|
|------|--------|------|
|
||||||
| `WebBrowserTool.ts` | 大 | 工具 schema + Bun WebView API 执行 |
|
| `WebBrowserTool.ts` | ✅ 已实现 | 工具 schema + Bun WebView API 执行 |
|
||||||
| `WebBrowserPanel.tsx` | 中 | REPL 侧边栏浏览器状态面板 |
|
| `WebBrowserPanel.tsx` | 中 | REPL 侧边栏浏览器状态面板(仍为 Stub) |
|
||||||
|
|
||||||
## 四、关键设计决策
|
## 四、关键设计决策
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ FEATURE_WEB_BROWSER_TOOL=1 bun run dev
|
|||||||
|
|
||||||
| 文件 | 职责 |
|
| 文件 | 职责 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `src/tools/WebBrowserTool/WebBrowserPanel.ts` | 面板组件(stub) |
|
| `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserPanel.ts` | 面板组件(stub) |
|
||||||
| `src/tools/WebBrowserTool/WebBrowserTool.ts` | 工具实现(缺失) |
|
| `packages/builtin-tools/src/tools/WebBrowserTool/WebBrowserTool.ts` | 工具实现(已实现) |
|
||||||
| `src/screens/REPL.tsx:273,4582` | 面板渲染 |
|
| `src/screens/REPL.tsx:471,5676` | 面板渲染 |
|
||||||
| `src/tools.ts:115-116` | 工具注册 |
|
| `src/tools.ts:115-116` | 工具注册 |
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# WEB_SEARCH_TOOL — 网页搜索工具
|
# WEB_SEARCH_TOOL — 网页搜索工具
|
||||||
|
|
||||||
> 实现状态:适配器架构完成,Bing 适配器为当前默认后端
|
> 实现状态:适配器架构完成,支持 API / Bing / Brave 三种后端
|
||||||
> 引用数:核心工具,无 feature flag 门控(始终启用)
|
> 引用数:核心工具,无 feature flag 门控(始终启用)
|
||||||
|
|
||||||
## 一、功能概述
|
## 一、功能概述
|
||||||
|
|
||||||
WebSearchTool 让模型可以搜索互联网获取最新信息。原始实现仅支持 Anthropic API 服务端搜索(`web_search_20250305` server tool),在第三方代理端点下不可用。现已重构为适配器架构,新增 Bing 搜索页面解析作为 fallback,确保任何 API 端点都能使用搜索功能。
|
WebSearchTool 让模型可以搜索互联网获取最新信息。原始实现仅支持 Anthropic API 服务端搜索(`web_search_20250305` server tool),在第三方代理端点下不可用。现已重构为适配器架构,支持 API 服务端搜索,以及 Bing / Brave 两个 HTML 解析后端,确保任何 API 端点都能使用搜索功能。
|
||||||
|
|
||||||
## 二、实现架构
|
## 二、实现架构
|
||||||
|
|
||||||
@@ -21,24 +21,29 @@ WebSearchTool.call()
|
|||||||
│ └── 使用 web_search_20250305 server tool
|
│ └── 使用 web_search_20250305 server tool
|
||||||
│ 通过 queryModelWithStreaming 二次调用 API
|
│ 通过 queryModelWithStreaming 二次调用 API
|
||||||
│
|
│
|
||||||
└── BingSearchAdapter — Bing HTML 抓取 + 正则提取(当前默认)
|
├── BingSearchAdapter — Bing HTML 抓取 + 正则提取
|
||||||
└── 直接抓取 Bing 搜索页 HTML
|
│ └── 直接抓取 Bing 搜索页 HTML
|
||||||
正则提取 b_algo 块中的标题/URL/摘要
|
│ 正则提取 b_algo 块中的标题/URL/摘要
|
||||||
|
│
|
||||||
|
└── BraveSearchAdapter — Brave LLM Context API
|
||||||
|
└── 调用 Brave HTTPS GET 接口
|
||||||
|
将 grounding payload 映射为标题/URL/摘要
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.2 模块结构
|
### 2.2 模块结构
|
||||||
|
|
||||||
| 模块 | 文件 | 说明 |
|
| 模块 | 文件 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| 工具入口 | `src/tools/WebSearchTool/WebSearchTool.ts` | `buildTool()` 定义:schema、权限、执行、输出格式化 |
|
| 工具入口 | `packages/builtin-tools/src/tools/WebSearchTool/WebSearchTool.ts` | `buildTool()` 定义:schema、权限、执行、输出格式化 |
|
||||||
| 工具 prompt | `src/tools/WebSearchTool/prompt.ts` | 搜索工具的系统提示词 |
|
| 工具 prompt | `packages/builtin-tools/src/tools/WebSearchTool/prompt.ts` | 搜索工具的系统提示词 |
|
||||||
| UI 渲染 | `src/tools/WebSearchTool/UI.tsx` | 搜索结果的终端渲染组件 |
|
| UI 渲染 | `packages/builtin-tools/src/tools/WebSearchTool/UI.tsx` | 搜索结果的终端渲染组件 |
|
||||||
| 适配器接口 | `src/tools/WebSearchTool/adapters/types.ts` | `WebSearchAdapter` 接口、`SearchResult`/`SearchOptions`/`SearchProgress` 类型 |
|
| 适配器接口 | `packages/builtin-tools/src/tools/WebSearchTool/adapters/types.ts` | `WebSearchAdapter` 接口、`SearchResult`/`SearchOptions`/`SearchProgress` 类型 |
|
||||||
| 适配器工厂 | `src/tools/WebSearchTool/adapters/index.ts` | `createAdapter()` 工厂函数,选择后端 |
|
| 适配器工厂 | `packages/builtin-tools/src/tools/WebSearchTool/adapters/index.ts` | `createAdapter()` 工厂函数,选择后端 |
|
||||||
| API 适配器 | `src/tools/WebSearchTool/adapters/apiAdapter.ts` | 封装原有 `queryModelWithStreaming` 逻辑,使用 server tool |
|
| API 适配器 | `packages/builtin-tools/src/tools/WebSearchTool/adapters/apiAdapter.ts` | 封装原有 `queryModelWithStreaming` 逻辑,使用 server tool |
|
||||||
| Bing 适配器 | `src/tools/WebSearchTool/adapters/bingAdapter.ts` | Bing HTML 抓取 + 正则解析 |
|
| Bing 适配器 | `packages/builtin-tools/src/tools/WebSearchTool/adapters/bingAdapter.ts` | Bing HTML 抓取 + 正则解析 |
|
||||||
| 单元测试 | `src/tools/WebSearchTool/__tests__/bingAdapter.test.ts` | 32 个测试用例 |
|
| Brave 适配器 | `packages/builtin-tools/src/tools/WebSearchTool/adapters/braveAdapter.ts` | Brave LLM Context API 适配与结果映射 |
|
||||||
| 集成测试 | `src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts` | 真实网络请求验证 |
|
| 单元测试 | `packages/builtin-tools/src/tools/WebSearchTool/__tests__/bingAdapter.test.ts`, `packages/builtin-tools/src/tools/WebSearchTool/__tests__/braveAdapter*.test.ts`, `packages/builtin-tools/src/tools/WebSearchTool/__tests__/adapterFactory.test.ts` | Bing / Brave 解析与工厂逻辑测试 |
|
||||||
|
| 集成测试 | `packages/builtin-tools/src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts`, `packages/builtin-tools/src/tools/WebSearchTool/__tests__/braveAdapter.integration.ts` | 真实网络请求验证 |
|
||||||
|
|
||||||
### 2.3 数据流
|
### 2.3 数据流
|
||||||
|
|
||||||
@@ -49,20 +54,18 @@ WebSearchTool.call()
|
|||||||
validateInput() — 校验 query 非空、allowed/block 不共存
|
validateInput() — 校验 query 非空、allowed/block 不共存
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
createAdapter() → BingSearchAdapter(当前硬编码)
|
createAdapter() → ApiSearchAdapter | BingSearchAdapter | BraveSearchAdapter
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
adapter.search(query, { allowedDomains, blockedDomains, signal, onProgress })
|
adapter.search(query, { allowedDomains, blockedDomains, signal, onProgress })
|
||||||
│
|
│
|
||||||
├── onProgress({ type: 'query_update', query })
|
├── onProgress({ type: 'query_update', query })
|
||||||
│
|
│
|
||||||
├── axios.get(bing.com/search?q=...&setmkt=en-US)
|
├── axios.get(search-engine-url)
|
||||||
│ └── 13 个 Edge 浏览器请求头
|
│ └── API 鉴权请求头
|
||||||
│
|
│
|
||||||
├── extractBingResults(html) — 正则提取 <li class="b_algo"> 块
|
├── extractResults(payload) — 按后端提取结果
|
||||||
│ ├── resolveBingUrl() — 解码 base64 重定向 URL
|
│ └── grounding → SearchResult[] 映射
|
||||||
│ ├── extractSnippet() — 三级降级摘要提取
|
|
||||||
│ └── decodeHtmlEntities() — he.decode
|
|
||||||
│
|
│
|
||||||
├── 客户端域名过滤 (allowedDomains / blockedDomains)
|
├── 客户端域名过滤 (allowedDomains / blockedDomains)
|
||||||
│
|
│
|
||||||
@@ -117,19 +120,18 @@ Bing 返回的重定向 URL 格式:`bing.com/ck/a?...&u=a1aHR0cHM6Ly9...`
|
|||||||
|
|
||||||
## 四、适配器选择逻辑
|
## 四、适配器选择逻辑
|
||||||
|
|
||||||
当前 `createAdapter()` 硬编码返回 `BingSearchAdapter`,原逻辑已注释保留:
|
`createAdapter()` 按以下优先级选择后端,并按选中的后端 key 缓存适配器实例:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
export function createAdapter(): WebSearchAdapter {
|
export function createAdapter(): WebSearchAdapter {
|
||||||
return new BingSearchAdapter()
|
// 1. WEB_SEARCH_ADAPTER=api|bing|brave 显式指定
|
||||||
// 注释保留的选择逻辑:
|
// 2. Anthropic 官方 API Base URL → ApiSearchAdapter
|
||||||
// 1. WEB_SEARCH_ADAPTER 环境变量强制指定 api|bing
|
// 3. 第三方代理 / 非官方端点 → BingSearchAdapter
|
||||||
// 2. isFirstPartyAnthropicBaseUrl() → API 适配器
|
|
||||||
// 3. 第三方端点 → Bing 适配器
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
恢复自动选择:取消 `index.ts` 中的注释即可。
|
显式指定 `WEB_SEARCH_ADAPTER=brave` 时,会改用 Brave LLM Context API 后端,并要求
|
||||||
|
`BRAVE_SEARCH_API_KEY` 或 `BRAVE_API_KEY`。
|
||||||
|
|
||||||
## 五、接口定义
|
## 五、接口定义
|
||||||
|
|
||||||
@@ -174,13 +176,13 @@ interface SearchProgress {
|
|||||||
|
|
||||||
| 文件 | 职责 |
|
| 文件 | 职责 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `src/tools/WebSearchTool/WebSearchTool.ts` | 工具定义入口 |
|
| `packages/builtin-tools/src/tools/WebSearchTool/WebSearchTool.ts` | 工具定义入口 |
|
||||||
| `src/tools/WebSearchTool/prompt.ts` | 搜索工具 prompt |
|
| `packages/builtin-tools/src/tools/WebSearchTool/prompt.ts` | 搜索工具 prompt |
|
||||||
| `src/tools/WebSearchTool/UI.tsx` | 终端 UI 渲染 |
|
| `packages/builtin-tools/src/tools/WebSearchTool/UI.tsx` | 终端 UI 渲染 |
|
||||||
| `src/tools/WebSearchTool/adapters/types.ts` | 适配器接口 |
|
| `packages/builtin-tools/src/tools/WebSearchTool/adapters/types.ts` | 适配器接口 |
|
||||||
| `src/tools/WebSearchTool/adapters/index.ts` | 适配器工厂 |
|
| `packages/builtin-tools/src/tools/WebSearchTool/adapters/index.ts` | 适配器工厂 |
|
||||||
| `src/tools/WebSearchTool/adapters/apiAdapter.ts` | API 服务端搜索适配器 |
|
| `packages/builtin-tools/src/tools/WebSearchTool/adapters/apiAdapter.ts` | API 服务端搜索适配器 |
|
||||||
| `src/tools/WebSearchTool/adapters/bingAdapter.ts` | Bing HTML 解析适配器 |
|
| `packages/builtin-tools/src/tools/WebSearchTool/adapters/bingAdapter.ts` | Bing HTML 解析适配器 |
|
||||||
| `src/tools/WebSearchTool/__tests__/bingAdapter.test.ts` | 单元测试 (32 cases) |
|
| `packages/builtin-tools/src/tools/WebSearchTool/__tests__/bingAdapter.test.ts` | 单元测试 (32 cases) |
|
||||||
| `src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts` | 集成测试 |
|
| `packages/builtin-tools/src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts` | 集成测试 |
|
||||||
| `src/tools.ts` | 工具注册 |
|
| `src/tools.ts` | 工具注册 |
|
||||||
|
|||||||
@@ -14,17 +14,17 @@ WORKFLOW_SCRIPTS 实现基于文件的多步自动化工作流。用户可以定
|
|||||||
|
|
||||||
| 模块 | 文件 | 状态 |
|
| 模块 | 文件 | 状态 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| WorkflowTool | `src/tools/WorkflowTool/WorkflowTool.ts` | **Stub** — 空对象 |
|
| WorkflowTool | `packages/builtin-tools/src/tools/WorkflowTool/WorkflowTool.ts` | **部分实现** — tool schema + 渲染完整,call 返回运行时缺失提示 |
|
||||||
| Workflow 权限 | `src/tools/WorkflowTool/WorkflowPermissionRequest.ts` | **Stub** — 返回 null |
|
| Workflow 权限 | `packages/builtin-tools/src/tools/WorkflowTool/WorkflowPermissionRequest.tsx` | **部分实现** — 权限请求组件 |
|
||||||
| 常量 | `src/tools/WorkflowTool/constants.ts` | **Stub** — 空工具名 |
|
| 常量 | `packages/builtin-tools/src/tools/WorkflowTool/constants.ts` | **实现** — 工具名 + 目录名 + 文件扩展名常量 |
|
||||||
| 命令创建 | `src/tools/WorkflowTool/createWorkflowCommand.ts` | **Stub** — 空操作 |
|
| 命令创建 | `packages/builtin-tools/src/tools/WorkflowTool/createWorkflowCommand.ts` | **实现** — 扫描 .claude/workflows/ 目录创建 Command 对象 |
|
||||||
| 捆绑工作流 | `src/tools/WorkflowTool/bundled/` | **缺失** — 目录不存在 |
|
| 捆绑工作流 | `packages/builtin-tools/src/tools/WorkflowTool/bundled/index.ts` | **实现** — 内置工作流初始化 |
|
||||||
| 本地工作流任务 | `src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts` | **Stub** — 类型 + 空操作 |
|
| 本地工作流任务 | `src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts` | **Stub** — 类型 + 空操作 |
|
||||||
| UI 任务组件 | `src/components/tasks/src/tasks/LocalWorkflowTask/` | **Stub** — 空导出 |
|
| UI 任务组件 | `src/components/tasks/src/tasks/LocalWorkflowTask/` | **Stub** — 空导出 |
|
||||||
| 详情对话框 | `src/components/tasks/WorkflowDetailDialog.ts` | **Stub** — 返回 null |
|
| 详情对话框 | `src/components/tasks/WorkflowDetailDialog.ts` | **Stub** — 返回 null |
|
||||||
| 任务注册 | `src/tasks.ts` | **布线** — 动态加载 |
|
| 任务注册 | `src/tasks.ts` | **布线** — 动态加载 |
|
||||||
| 工具注册 | `src/tools.ts` | **布线** — 包含 bundled 工作流初始化 |
|
| 工具注册 | `src/tools.ts` | **布线** — 动态加载 + bundled 工作流初始化 (行 131-134,235) |
|
||||||
| 命令注册 | `src/commands.ts` | **布线** — `/workflows` 命令 |
|
| 命令注册 | `src/commands.ts` | **布线** — `/workflows` 命令 (行 93-95,395,460) |
|
||||||
|
|
||||||
### 2.2 预期数据流
|
### 2.2 预期数据流
|
||||||
|
|
||||||
@@ -69,13 +69,9 @@ steps:
|
|||||||
|
|
||||||
| 优先级 | 模块 | 工作量 | 说明 |
|
| 优先级 | 模块 | 工作量 | 说明 |
|
||||||
|--------|------|--------|------|
|
|--------|------|--------|------|
|
||||||
| 1 | `WorkflowTool.ts` | 大 | Schema 定义 + 多步执行引擎 |
|
| 1 | `WorkflowTool.ts` call 方法 | 中 | 实际工作流执行逻辑(当前返回运行时缺失提示) |
|
||||||
| 2 | `bundled/index.js` | 中 | 内置工作流定义(initBundledWorkflows) |
|
| 2 | `LocalWorkflowTask.ts` | 大 | 步骤协调、kill/skip/retry |
|
||||||
| 3 | `createWorkflowCommand.ts` | 中 | 从文件解析创建命令对象 |
|
| 3 | `WorkflowDetailDialog.ts` | 中 | 进度详情 UI |
|
||||||
| 4 | `LocalWorkflowTask.ts` | 大 | 步骤协调、kill/skip/retry |
|
|
||||||
| 5 | `WorkflowDetailDialog.ts` | 中 | 进度详情 UI |
|
|
||||||
| 6 | `WorkflowPermissionRequest.ts` | 小 | 权限对话框 |
|
|
||||||
| 7 | `constants.ts` | 小 | 工具名常量 |
|
|
||||||
|
|
||||||
## 四、关键设计决策
|
## 四、关键设计决策
|
||||||
|
|
||||||
@@ -95,11 +91,12 @@ FEATURE_WORKFLOW_SCRIPTS=1 bun run dev
|
|||||||
|
|
||||||
| 文件 | 职责 |
|
| 文件 | 职责 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `src/tools/WorkflowTool/WorkflowTool.ts` | 工具定义(stub) |
|
| `packages/builtin-tools/src/tools/WorkflowTool/WorkflowTool.ts` | 工具定义(部分实现) |
|
||||||
| `src/tools/WorkflowTool/WorkflowPermissionRequest.ts` | 权限对话框(stub) |
|
| `packages/builtin-tools/src/tools/WorkflowTool/WorkflowPermissionRequest.tsx` | 权限请求组件 |
|
||||||
| `src/tools/WorkflowTool/constants.ts` | 常量(stub) |
|
| `packages/builtin-tools/src/tools/WorkflowTool/constants.ts` | 常量定义 |
|
||||||
| `src/tools/WorkflowTool/createWorkflowCommand.ts` | 命令创建(stub) |
|
| `packages/builtin-tools/src/tools/WorkflowTool/createWorkflowCommand.ts` | 命令创建(已实现) |
|
||||||
|
| `packages/builtin-tools/src/tools/WorkflowTool/bundled/index.ts` | 内置工作流初始化 |
|
||||||
| `src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts` | 任务协调(stub) |
|
| `src/tasks/LocalWorkflowTask/LocalWorkflowTask.ts` | 任务协调(stub) |
|
||||||
| `src/components/tasks/WorkflowDetailDialog.ts` | 详情对话框(stub) |
|
| `src/components/tasks/WorkflowDetailDialog.ts` | 详情对话框(stub) |
|
||||||
| `src/tools.ts:127-132` | 工具注册 |
|
| `src/tools.ts:131-134,235` | 工具注册 |
|
||||||
| `src/commands.ts:86-89` | 命令注册 |
|
| `src/commands.ts:93-95,395,460` | 命令注册 |
|
||||||
|
|||||||
564
docs/internals/agent-comm-fix-jira-tasks.md
Normal file
564
docs/internals/agent-comm-fix-jira-tasks.md
Normal file
@@ -0,0 +1,564 @@
|
|||||||
|
# Agent 通讯修复 Jira Task
|
||||||
|
|
||||||
|
- 版本:v1.0
|
||||||
|
- 生成日期:2026-04-25
|
||||||
|
- 来源:由按文件执行清单、Claude 交叉验证意见整理合并
|
||||||
|
- 范围:ACP Agent / Bridge / Remote Control Server / REPL Hook 生命周期
|
||||||
|
- 使用方式:这是唯一执行任务文档;每个 `JIRA-*` 小节可直接拆成一个 Jira issue,字段保持统一,便于复制或二次导入。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方案性质
|
||||||
|
|
||||||
|
本文档是目标状态式执行方案,不是临时补丁清单。每张 ticket 必须交付明确的代码终态、测试覆盖和回归边界;不得只用局部 workaround 掩盖问题。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 执行总则
|
||||||
|
|
||||||
|
1. 先边界安全,后内部优化:先修 WS 入站大小与输入校验,避免线上风险扩大。
|
||||||
|
2. 单文件可回滚:每个文件内修改保持内聚,便于回滚与 bisect。
|
||||||
|
3. 不改协议语义,只修实现缺陷:除 `resource_link` 表达形式统一外,不改变主流程契约。
|
||||||
|
4. 每个文件必须有验收输出:要么测试用例,要么日志/指标验证。
|
||||||
|
5. 发布前必须确认协议层行为无回归:`stopReason` 决策与 `sessionUpdate` 发送顺序保持稳定。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Epic
|
||||||
|
|
||||||
|
### JIRA-EPIC-001:提升 Agent 通讯链路稳定性与边界安全
|
||||||
|
|
||||||
|
- Issue Type:Epic
|
||||||
|
- Priority:P0
|
||||||
|
- Owner:核心通讯 / 后端网关 / QA
|
||||||
|
- Scope:ACP Agent、ACP Bridge、Remote Control Server、REPL 初始化生命周期
|
||||||
|
- Goal:修复长会话资源泄漏、补齐 WebSocket 入站边界、统一 prompt 转换、收敛类型风险,并补充关键回归测试。
|
||||||
|
|
||||||
|
#### Epic 验收标准
|
||||||
|
|
||||||
|
- `bun run typecheck` 0 error。
|
||||||
|
- P0 WebSocket 超大消息拒绝逻辑已实现并覆盖测试。
|
||||||
|
- ACP bridge abort listener 生命周期无累积。
|
||||||
|
- prompt 转换实现单源化。
|
||||||
|
- settings/defaultMode 能真实影响 ACP permission mode,且 `_meta.permissionMode` 保持最高优先级。
|
||||||
|
- REPL 目标 hook suppress 清理完成,timer cleanup 完整。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P0 Tickets
|
||||||
|
|
||||||
|
### JIRA-001:为 session ingress WebSocket 补齐消息大小限制
|
||||||
|
|
||||||
|
- Issue Type:Bug
|
||||||
|
- Priority:P0
|
||||||
|
- Story Points:3
|
||||||
|
- Owner:后端/网关
|
||||||
|
- Files:
|
||||||
|
- `packages/remote-control-server/src/routes/v1/session-ingress.ts`
|
||||||
|
- 后续票:JIRA-008(同文件 P1 类型与 decode path 收尾)
|
||||||
|
|
||||||
|
#### 参考代码位置
|
||||||
|
|
||||||
|
- `packages/remote-control-server/src/routes/v1/session-ingress.ts:100-106`
|
||||||
|
|
||||||
|
#### 背景
|
||||||
|
|
||||||
|
`session-ingress` 当前缺少 WebSocket message size limit。ACP 路由已有类似限制,两个入口边界不一致,可能导致大包占用内存或绕过入口保护。
|
||||||
|
|
||||||
|
#### 实施要求
|
||||||
|
|
||||||
|
- 新增 `MAX_WS_MESSAGE_SIZE = 10 * 1024 * 1024`,与 ACP 路由的 10MB 上限保持一致。
|
||||||
|
- 在 `onMessage` decode 后优先检查 payload size。
|
||||||
|
- 超限时执行 `ws.close(1009, "message too large")`。
|
||||||
|
- 日志记录 `sessionId`、payload size、limit。
|
||||||
|
- 对 `string`、`ArrayBuffer`、`Uint8Array` 进行统一 decode 分流。
|
||||||
|
- 非支持类型直接拒绝并记录,不进入业务 handler。
|
||||||
|
|
||||||
|
#### 验收标准
|
||||||
|
|
||||||
|
- 11MB payload 被 1009 close。
|
||||||
|
- 1KB 合法 payload 仍正常进入 handler。
|
||||||
|
- 非支持类型 payload 不进入 handler。
|
||||||
|
- 不改变 URL、auth、session 解析逻辑。
|
||||||
|
|
||||||
|
#### 回归范围
|
||||||
|
|
||||||
|
- Remote Control Server session ingress WebSocket。
|
||||||
|
- 正常会话消息转发。
|
||||||
|
- WebSocket close code 行为。
|
||||||
|
|
||||||
|
#### 风险等级
|
||||||
|
|
||||||
|
- 中。入口逻辑变更可能影响特殊客户端 payload 类型。
|
||||||
|
|
||||||
|
#### 必须验证
|
||||||
|
|
||||||
|
- 在 `packages/remote-control-server/src/__tests__/routes.test.ts` 增加 session-ingress WebSocket 大包、小包、坏类型 payload 用例。
|
||||||
|
- 运行 `bun run typecheck`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JIRA-002:修复 ACP bridge abort listener 生命周期泄漏
|
||||||
|
|
||||||
|
- Issue Type:Bug
|
||||||
|
- Priority:P0
|
||||||
|
- Story Points:3
|
||||||
|
- Owner:核心通讯
|
||||||
|
- Files:
|
||||||
|
- `src/services/acp/bridge.ts`
|
||||||
|
|
||||||
|
#### 参考代码位置
|
||||||
|
|
||||||
|
- `src/services/acp/bridge.ts:576-585`
|
||||||
|
|
||||||
|
#### 背景
|
||||||
|
|
||||||
|
ACP bridge 的 `Promise.race` abort 分支注册 listener 后缺少完整 cleanup。长会话或高频 next 场景可能出现 listener 累积。
|
||||||
|
|
||||||
|
#### 实施要求
|
||||||
|
|
||||||
|
- 将 abort race 改为可清理监听器写法。
|
||||||
|
- 注册 listener 后保留 handler 引用。
|
||||||
|
- `sdkMessages.next()` 先返回时必须 `removeEventListener`。
|
||||||
|
- abort、throw、return 等路径都在 `finally` 中清理。
|
||||||
|
- 不改变 `stopReason` 决策逻辑。
|
||||||
|
- 不改变 `sessionUpdate` 发送顺序。
|
||||||
|
|
||||||
|
#### 验收标准
|
||||||
|
|
||||||
|
- 模拟 10k 次 next 且不 abort,listener 不增长。
|
||||||
|
- abort 场景仍返回 `cancelled`。
|
||||||
|
- 原有 streaming/session update 行为无回归。
|
||||||
|
|
||||||
|
#### 回归范围
|
||||||
|
|
||||||
|
- ACP bridge streaming loop。
|
||||||
|
- 用户取消请求。
|
||||||
|
- SDK generator 异常路径。
|
||||||
|
|
||||||
|
#### 风险等级
|
||||||
|
|
||||||
|
- 中。异步控制流变更需要覆盖取消与异常路径。
|
||||||
|
|
||||||
|
#### 必须验证
|
||||||
|
|
||||||
|
- 新增 listener cleanup 单元测试。
|
||||||
|
- 运行 `bun run typecheck`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P1 Tickets
|
||||||
|
|
||||||
|
### JIRA-003:优化 ACP agent pending prompt 队列为 O(1) 出队
|
||||||
|
|
||||||
|
- Issue Type:Task
|
||||||
|
- Priority:P1
|
||||||
|
- Story Points:5
|
||||||
|
- Owner:核心通讯
|
||||||
|
- Files:
|
||||||
|
- `src/services/acp/agent.ts`
|
||||||
|
|
||||||
|
#### 参考代码位置
|
||||||
|
|
||||||
|
- `src/services/acp/agent.ts:332-339`
|
||||||
|
|
||||||
|
#### 背景
|
||||||
|
|
||||||
|
当前 pending prompt 队列使用 `Map + sort` 获取下一项,排队量上升时会带来不必要的排序成本。
|
||||||
|
|
||||||
|
#### 实施要求
|
||||||
|
|
||||||
|
- 改为 `queue: string[]` + `pendingMap: Map<string, PendingPrompt>` 组合。
|
||||||
|
- 入队执行 `queue.push(id)` 与 `pendingMap.set(id, prompt)`。
|
||||||
|
- 出队从队首惰性跳过已取消项。
|
||||||
|
- 取消只从 `pendingMap` 删除,不做数组中间删除。
|
||||||
|
- 保持现有取消语义和出队顺序。
|
||||||
|
|
||||||
|
#### 验收标准
|
||||||
|
|
||||||
|
- 1000 pending prompt 场景下出队顺序正确。
|
||||||
|
- 已取消 prompt 不会被 resolve。
|
||||||
|
- 出队不再依赖全量 sort。
|
||||||
|
- 1000 排队场景下出队耗时低于旧实现;测试记录旧实现复杂度风险和新实现 O(1) 出队路径。
|
||||||
|
- 行为与旧实现兼容。
|
||||||
|
|
||||||
|
#### 回归范围
|
||||||
|
|
||||||
|
- ACP prompt queue。
|
||||||
|
- 并发 prompt 请求。
|
||||||
|
- prompt cancel / resolve 边界。
|
||||||
|
|
||||||
|
#### 风险等级
|
||||||
|
|
||||||
|
- 中。队列结构变更可能引入取消边界问题。
|
||||||
|
|
||||||
|
#### 必须验证
|
||||||
|
|
||||||
|
- 新增 queue 顺序与取消测试。
|
||||||
|
- 对 1000 prompt 场景做性能断言或日志记录。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JIRA-004:接入真实 settings 读取并校验 ACP permission mode
|
||||||
|
|
||||||
|
- Issue Type:Bug
|
||||||
|
- Priority:P1
|
||||||
|
- Story Points:3
|
||||||
|
- Owner:核心通讯
|
||||||
|
- Files:
|
||||||
|
- `src/services/acp/agent.ts`
|
||||||
|
|
||||||
|
#### 参考代码位置
|
||||||
|
|
||||||
|
- `src/services/acp/agent.ts:465-467`
|
||||||
|
|
||||||
|
#### 背景
|
||||||
|
|
||||||
|
`getSetting()` 当前未真正接入项目配置,导致默认 permission mode 配置无法按预期生效。
|
||||||
|
|
||||||
|
#### 实施要求
|
||||||
|
|
||||||
|
- 接入项目现有 settings/config 读取逻辑。
|
||||||
|
- 仅接受合法 permission mode 枚举值。
|
||||||
|
- 非法值 fallback 到 `default`。
|
||||||
|
- `_meta.permissionMode` 继续保持最高优先级。
|
||||||
|
- 不改变外部协议字段。
|
||||||
|
|
||||||
|
#### 验收标准
|
||||||
|
|
||||||
|
- settings/defaultMode 能影响默认 permission mode。
|
||||||
|
- `_meta.permissionMode` 能覆盖 settings。
|
||||||
|
- 非法 settings 值不会传播到运行时。
|
||||||
|
- 类型检查通过。
|
||||||
|
|
||||||
|
#### 回归范围
|
||||||
|
|
||||||
|
- ACP agent session 初始化。
|
||||||
|
- 权限模式同步。
|
||||||
|
- 客户端 `_meta` 覆盖逻辑。
|
||||||
|
|
||||||
|
#### 风险等级
|
||||||
|
|
||||||
|
- 中。配置优先级错误会影响权限行为。
|
||||||
|
|
||||||
|
#### 必须验证
|
||||||
|
|
||||||
|
- 新增 defaultMode / `_meta.permissionMode` 优先级测试。
|
||||||
|
- 运行 `bun run typecheck`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JIRA-005:单源化 ACP prompt 转换逻辑
|
||||||
|
|
||||||
|
- Issue Type:Refactor
|
||||||
|
- Priority:P1
|
||||||
|
- Story Points:5
|
||||||
|
- Owner:核心通讯
|
||||||
|
- Files:
|
||||||
|
- `src/services/acp/agent.ts`
|
||||||
|
- `src/services/acp/bridge.ts`
|
||||||
|
- `src/services/acp/promptConversion.ts`(新增)
|
||||||
|
|
||||||
|
#### 参考代码位置
|
||||||
|
|
||||||
|
- `src/services/acp/agent.ts:754-758`
|
||||||
|
- `src/services/acp/agent.ts:764-785`
|
||||||
|
- `src/services/acp/bridge.ts:522-537`
|
||||||
|
|
||||||
|
#### 背景
|
||||||
|
|
||||||
|
ACP agent 与 bridge 存在重复 prompt 转换逻辑,`resource_link` 等 block 的输出策略容易分叉。
|
||||||
|
|
||||||
|
#### 实施要求
|
||||||
|
|
||||||
|
- 新增共享转换模块 `src/services/acp/promptConversion.ts`。
|
||||||
|
- `agent.ts` 与 `bridge.ts` 改为调用共享转换函数。
|
||||||
|
- 删除 `bridge.ts` 中 `promptToQueryContent` 的真实实现;如导出仍需保留,则只允许保留调用共享函数的 wrapper。
|
||||||
|
- `resource_link` 输出改为稳定纯文本元信息,禁止 markdown link。
|
||||||
|
- 保持其他 block 转换语义不变。
|
||||||
|
|
||||||
|
#### 验收标准
|
||||||
|
|
||||||
|
- 全仓库仅保留一个真实 prompt 转换实现。
|
||||||
|
- 相同 input block 在 agent/bridge 输出一致。
|
||||||
|
- `resource_link` 不再输出 `[name](uri)` 形式。
|
||||||
|
- 相关测试覆盖转换一致性。
|
||||||
|
|
||||||
|
#### 回归范围
|
||||||
|
|
||||||
|
- ACP prompt input。
|
||||||
|
- bridge query content。
|
||||||
|
- resource link prompt 表达。
|
||||||
|
|
||||||
|
#### 风险等级
|
||||||
|
|
||||||
|
- 中。文本格式变化可能影响下游 prompt 快照或断言。
|
||||||
|
|
||||||
|
#### 必须验证
|
||||||
|
|
||||||
|
- 新增 shared conversion 单元测试。
|
||||||
|
- 全仓库搜索重复转换函数。
|
||||||
|
- 运行 `bun run typecheck`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JIRA-006:治理 REPL onInit effect 依赖并补齐 timer cleanup
|
||||||
|
|
||||||
|
- Issue Type:Task
|
||||||
|
- Priority:P1
|
||||||
|
- Story Points:3
|
||||||
|
- Owner:终端 UI
|
||||||
|
- Files:
|
||||||
|
- `src/screens/REPL.tsx`
|
||||||
|
|
||||||
|
#### 参考代码位置
|
||||||
|
|
||||||
|
- `src/screens/REPL.tsx:654-662`
|
||||||
|
- `src/screens/REPL.tsx:4996-5005`
|
||||||
|
|
||||||
|
#### 背景
|
||||||
|
|
||||||
|
REPL 中目标初始化 effect 存在 hook dependency suppress,warm-up timer 也需要显式 cleanup,避免频繁挂载/卸载时留下悬挂任务。
|
||||||
|
|
||||||
|
#### 实施要求
|
||||||
|
|
||||||
|
- 整理 `onInit` 生命周期,使用稳定引用或 effect 内联。
|
||||||
|
- 移除目标段 `exhaustive-deps` suppress。
|
||||||
|
- 保持 unmount cleanup 行为不变。
|
||||||
|
- warm-up effect 中记录 timeout id。
|
||||||
|
- cleanup 中执行 `clearTimeout(timeoutId)`。
|
||||||
|
- 保留 `alive` 判定作为并发保护。
|
||||||
|
|
||||||
|
#### 验收标准
|
||||||
|
|
||||||
|
- 目标段不再需要 hooks lint suppress。
|
||||||
|
- 高频打开/关闭搜索栏无悬挂 timer 增长。
|
||||||
|
- REPL 初始化行为无回归。
|
||||||
|
|
||||||
|
#### 回归范围
|
||||||
|
|
||||||
|
- REPL 初始化。
|
||||||
|
- 搜索栏 warm-up。
|
||||||
|
- 组件卸载 cleanup。
|
||||||
|
|
||||||
|
#### 风险等级
|
||||||
|
|
||||||
|
- 中。React effect 依赖治理可能改变初始化时机。
|
||||||
|
|
||||||
|
#### 必须验证
|
||||||
|
|
||||||
|
- 运行 lint/typecheck。
|
||||||
|
- 手动或测试覆盖 REPL mount/unmount。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JIRA-007:收敛 ACP route WebSocket 事件 any 类型
|
||||||
|
|
||||||
|
- Issue Type:Task
|
||||||
|
- Priority:P1
|
||||||
|
- Story Points:2
|
||||||
|
- Owner:后端/网关
|
||||||
|
- Files:
|
||||||
|
- `packages/remote-control-server/src/routes/acp/index.ts`
|
||||||
|
|
||||||
|
#### 参考代码位置
|
||||||
|
|
||||||
|
- `packages/remote-control-server/src/routes/acp/index.ts:108-146`
|
||||||
|
|
||||||
|
#### 背景
|
||||||
|
|
||||||
|
ACP route 中 WebSocket 事件和 socket 参数存在 `any`,降低编译期保护。
|
||||||
|
|
||||||
|
#### 实施要求
|
||||||
|
|
||||||
|
- 定义最小 WebSocket 事件类型:open/message/close/error。
|
||||||
|
- 将 `_evt: any`、`evt: any`、`ws: any` 替换为窄类型。
|
||||||
|
- 不改变 payload decode 与大小检查策略。
|
||||||
|
- 不改变现有 handler 行为。
|
||||||
|
|
||||||
|
#### 验收标准
|
||||||
|
|
||||||
|
- 编译期能捕获错误事件字段访问。
|
||||||
|
- 现有 WebSocket 行为不变。
|
||||||
|
- `bun run typecheck` 通过。
|
||||||
|
|
||||||
|
#### 回归范围
|
||||||
|
|
||||||
|
- ACP WebSocket route。
|
||||||
|
- message decode。
|
||||||
|
- close/error handler。
|
||||||
|
|
||||||
|
#### 风险等级
|
||||||
|
|
||||||
|
- 低。类型收敛为主。
|
||||||
|
|
||||||
|
#### 必须验证
|
||||||
|
|
||||||
|
- 运行 `bun run typecheck`。
|
||||||
|
- 保留现有测试通过。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JIRA-008:收敛 session ingress WebSocket 事件类型与 decode path
|
||||||
|
|
||||||
|
- Issue Type:Task
|
||||||
|
- Priority:P1
|
||||||
|
- Story Points:3
|
||||||
|
- Owner:后端/网关
|
||||||
|
- Files:
|
||||||
|
- `packages/remote-control-server/src/routes/v1/session-ingress.ts`
|
||||||
|
- 前置依赖:JIRA-001 已合并
|
||||||
|
|
||||||
|
#### 参考代码位置
|
||||||
|
|
||||||
|
- `packages/remote-control-server/src/routes/v1/session-ingress.ts:100-106`
|
||||||
|
|
||||||
|
#### 背景
|
||||||
|
|
||||||
|
在完成 P0 size guard 后,session ingress 仍需要进一步收敛事件类型与 decode path,减少隐式类型风险。
|
||||||
|
|
||||||
|
#### 实施要求
|
||||||
|
|
||||||
|
- 定义或复用最小 WebSocket message event 类型。
|
||||||
|
- 将 message decode 分支集中到一个小函数。
|
||||||
|
- 保持 P0 size guard 与 close code 语义。
|
||||||
|
- 不改变 auth/session 解析。
|
||||||
|
|
||||||
|
#### 验收标准
|
||||||
|
|
||||||
|
- decode path 单一清晰。
|
||||||
|
- 不支持 payload 类型有明确拒绝路径。
|
||||||
|
- `bun run typecheck` 通过。
|
||||||
|
|
||||||
|
#### 回归范围
|
||||||
|
|
||||||
|
- Session ingress WebSocket message handling。
|
||||||
|
- P0 大包拒绝逻辑。
|
||||||
|
|
||||||
|
#### 风险等级
|
||||||
|
|
||||||
|
- 低到中。与 P0 同文件,注意避免重复改动冲突。
|
||||||
|
|
||||||
|
#### 必须验证
|
||||||
|
|
||||||
|
- 与 JIRA-001 同批测试。
|
||||||
|
- 运行 `bun run typecheck`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## QA Tickets
|
||||||
|
|
||||||
|
### JIRA-009:补充 ACP 通讯回归测试
|
||||||
|
|
||||||
|
- Issue Type:Test
|
||||||
|
- Priority:P1
|
||||||
|
- Story Points:5
|
||||||
|
- Owner:QA/核心通讯
|
||||||
|
- Files:
|
||||||
|
- `src/services/acp/agent.ts`
|
||||||
|
- `src/services/acp/bridge.ts`
|
||||||
|
- `src/services/acp/promptConversion.ts`
|
||||||
|
- `src/services/acp/__tests__/agent.test.ts`
|
||||||
|
- `src/services/acp/__tests__/bridge.test.ts`
|
||||||
|
- `src/services/acp/__tests__/promptConversion.test.ts`
|
||||||
|
|
||||||
|
#### 覆盖场景
|
||||||
|
|
||||||
|
- 长会话 10k turn,无 abort listener 累积。
|
||||||
|
- prompt queue 1000 并发排队,取消/出队顺序正确。
|
||||||
|
- settings/defaultMode 与 `_meta.permissionMode` 优先级正确。
|
||||||
|
- `resource_link` 转换在 agent 与 bridge 输出一致。
|
||||||
|
|
||||||
|
#### 验收标准
|
||||||
|
|
||||||
|
- 新增测试在本地稳定通过。
|
||||||
|
- 不依赖真实网络或外部服务。
|
||||||
|
- 测试 mock 遵守仓库规范,只 mock 有副作用链路。
|
||||||
|
|
||||||
|
#### 回归范围
|
||||||
|
|
||||||
|
- ACP bridge。
|
||||||
|
- ACP agent。
|
||||||
|
- prompt conversion。
|
||||||
|
- permission mode resolution。
|
||||||
|
|
||||||
|
#### 风险等级
|
||||||
|
|
||||||
|
- 中。异步测试可能有稳定性问题,需要避免时间敏感断言。
|
||||||
|
|
||||||
|
#### 必须验证
|
||||||
|
|
||||||
|
- 运行相关 `bun test`。
|
||||||
|
- 运行 `bun run typecheck`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JIRA-010:补充 Remote Control Server WebSocket 入站回归测试
|
||||||
|
|
||||||
|
- Issue Type:Test
|
||||||
|
- Priority:P1
|
||||||
|
- Story Points:3
|
||||||
|
- Owner:QA/后端
|
||||||
|
- Files:
|
||||||
|
- `packages/remote-control-server/src/__tests__/routes.test.ts`
|
||||||
|
- `packages/remote-control-server/src/routes/v1/session-ingress.ts`
|
||||||
|
|
||||||
|
#### 覆盖场景
|
||||||
|
|
||||||
|
- 11MB session ingress payload 被 1009 close(与 10MB 上限对齐)。
|
||||||
|
- 合法小 payload 正常进入 handler。
|
||||||
|
- 非支持 payload 类型被拒绝。
|
||||||
|
- 日志或可观测输出包含 sessionId、payload size、limit。
|
||||||
|
|
||||||
|
#### 验收标准
|
||||||
|
|
||||||
|
- 11MB payload 被 1009 close(与 10MB 上限对齐)。
|
||||||
|
- 新增测试稳定通过。
|
||||||
|
- 不启动真实外部服务。
|
||||||
|
- 不改变现有 route public contract。
|
||||||
|
|
||||||
|
#### 回归范围
|
||||||
|
|
||||||
|
- RCS session ingress route。
|
||||||
|
- WebSocket message handling。
|
||||||
|
- close code 行为。
|
||||||
|
|
||||||
|
#### 风险等级
|
||||||
|
|
||||||
|
- 中。测试需要适配现有 WebSocket/mock 基础设施。
|
||||||
|
|
||||||
|
#### 必须验证
|
||||||
|
|
||||||
|
- 运行 RCS package 相关测试。
|
||||||
|
- 运行 `bun run typecheck`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 推荐执行顺序
|
||||||
|
|
||||||
|
执行节奏与原计划保持一致:先完成 P0 全部改动和冒烟验证,再启动 P1 改造;测试票可穿插执行,但不得绕过 P0 gate。
|
||||||
|
|
||||||
|
1. JIRA-001:先封入口大包风险。
|
||||||
|
2. JIRA-002:修长会话 listener 生命周期。
|
||||||
|
3. JIRA-010:补 RCS 入站测试,锁住 P0 行为。
|
||||||
|
4. JIRA-003:优化 pending prompt queue。
|
||||||
|
5. JIRA-004:接入 settings/defaultMode。
|
||||||
|
6. JIRA-005:单源化 prompt 转换。
|
||||||
|
7. JIRA-009:补 ACP 回归测试。
|
||||||
|
8. JIRA-006:治理 REPL effect/timer。
|
||||||
|
9. JIRA-007:收敛 ACP route 类型。
|
||||||
|
10. JIRA-008:收敛 session ingress 类型与 decode path。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Release Checklist
|
||||||
|
|
||||||
|
- [ ] `bun run typecheck` 0 error
|
||||||
|
- [ ] P0 tickets 已合并并测试通过
|
||||||
|
- [ ] ACP 回归测试通过
|
||||||
|
- [ ] RCS WebSocket 入站测试通过
|
||||||
|
- [ ] prompt conversion 单源化已通过代码搜索确认
|
||||||
|
- [ ] permission mode 优先级测试通过
|
||||||
|
- [ ] 协议层行为无回归(stopReason 决策、sessionUpdate 发送顺序)
|
||||||
|
- [ ] REPL hook/timer 改动通过 lint/typecheck
|
||||||
|
- [ ] 最终变更说明包含风险与未覆盖项
|
||||||
74
docs/internals/agent-comm-fix-questions.md
Normal file
74
docs/internals/agent-comm-fix-questions.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# Agent 通讯修复问题文档
|
||||||
|
|
||||||
|
- 版本:v1.0
|
||||||
|
- 生成日期:2026-04-25
|
||||||
|
- 范围:ACP Agent / Bridge / Remote Control Server / REPL Hook 生命周期
|
||||||
|
- 配套执行文档:`docs/internals/agent-comm-fix-jira-tasks.md`
|
||||||
|
- 目的:保留决策前要问的问题、交叉验证提示词和已确认结论;不要在这里写 Jira 执行步骤。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 当前已确认结论
|
||||||
|
|
||||||
|
- 只保留两份交付文档:本问题文档 + Jira Task 文档。
|
||||||
|
- Jira Task 文档是唯一执行入口,包含 Owner、优先级、文件范围、验收标准、风险和验证建议。
|
||||||
|
- Claude 交叉验证结论:整体通过,无 blocking findings;建议补充协议回归 gate、JIRA-001/008 依赖、代码参考位置和阈值一致性,这些建议已合并到 Jira Task 文档。
|
||||||
|
- 本次已进入业务代码修复阶段,必须运行 `bun run typecheck` 和相关回归测试。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 执行前必须问清的问题
|
||||||
|
|
||||||
|
1. `session-ingress` 的 WebSocket 上限是否固定为 10MB,并与 ACP route 保持一致?
|
||||||
|
2. 超限 close code 是否统一使用 `1009`,close reason 是否固定为 `message too large`?
|
||||||
|
3. `resource_link` 的纯文本格式是否已有下游依赖,能否替代当前 markdown link 表达?
|
||||||
|
4. ACP permission mode 的真实 settings key 是哪个,非法值 fallback 是否统一为 `default`?
|
||||||
|
5. `_meta.permissionMode` 是否必须始终覆盖 settings/defaultMode?
|
||||||
|
6. abort listener 测试中,是否能通过 mock signal 或计数器稳定证明 10k next 后无 listener 累积?
|
||||||
|
7. pending prompt queue 的取消语义是否允许惰性清理,而不是立刻从数组中删除?
|
||||||
|
8. REPL hook suppress 的清理范围是否只限目标段,不顺手改其他 decompiled React Compiler 结构?
|
||||||
|
9. RCS WebSocket 测试应放在现有哪个 `__tests__` 布局下,是否已有 route/mock 基础设施可复用?
|
||||||
|
10. 发布 gate 是否必须包含 `stopReason` 决策与 `sessionUpdate` 发送顺序不回归?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 给 Claude 或 Reviewer 的复核问题
|
||||||
|
|
||||||
|
```text
|
||||||
|
请作为外部审查者,复核 docs/internals/agent-comm-fix-jira-tasks.md。
|
||||||
|
|
||||||
|
请检查:
|
||||||
|
1. 是否仍满足“按文件分工的执行清单”和“Jira task 文档”要求。
|
||||||
|
2. 是否存在遗漏的文件、验收标准、风险或前置依赖。
|
||||||
|
3. 是否有重复、误导执行者、优先级不合理或测试不可落地的问题。
|
||||||
|
4. 是否还有必须阻断实施的 finding。
|
||||||
|
|
||||||
|
请用中文输出:
|
||||||
|
- Verdict
|
||||||
|
- Blocking Findings
|
||||||
|
- Non-blocking Findings
|
||||||
|
- Suggested Edits
|
||||||
|
- Final Recommendation
|
||||||
|
|
||||||
|
不要修改文件,只输出审查意见。
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 已处理的复核建议
|
||||||
|
|
||||||
|
- Release Checklist 已补充协议层行为无回归 gate。
|
||||||
|
- JIRA-001 与 JIRA-008 已明确同文件前后置关系。
|
||||||
|
- JIRA-001 到 JIRA-008 已补充参考代码位置。
|
||||||
|
- JIRA-003 已补回 1000 排队场景下的出队耗时验收。
|
||||||
|
- JIRA-008 story points 已从 2 调整为 3。
|
||||||
|
- JIRA-010 已明确 11MB payload 对齐 10MB 上限并触发 1009 close。
|
||||||
|
- 推荐执行顺序已明确 P0 gate:P0 全部改动和冒烟验证完成后,再启动 P1 改造。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 不在本文档维护的内容
|
||||||
|
|
||||||
|
- 不维护 Jira ticket 正文;统一在 `docs/internals/agent-comm-fix-jira-tasks.md` 修改。
|
||||||
|
- 不维护业务代码实现方案;实现时按具体 ticket 读取对应文件。
|
||||||
|
- 不维护历史中间稿;旧执行清单已合并进 Jira Task 文档。
|
||||||
@@ -17,7 +17,7 @@ keywords: ["Ant 特权", "USER_TYPE", "身份门控", "内部功能", "Anthropic
|
|||||||
|
|
||||||
`BUILD_TARGET` 等构建时常量在反编译版本中已被移除。`USER_TYPE` 通过 Bun 的 `--define` 或环境变量注入,Bun 会进行**常量折叠**——所有 `process.env.USER_TYPE === 'ant'` 在外部构建中直接变为 `false`,后续代码被 DCE 移除。但在反编译版本中,这些代码保留完整。
|
`BUILD_TARGET` 等构建时常量在反编译版本中已被移除。`USER_TYPE` 通过 Bun 的 `--define` 或环境变量注入,Bun 会进行**常量折叠**——所有 `process.env.USER_TYPE === 'ant'` 在外部构建中直接变为 `false`,后续代码被 DCE 移除。但在反编译版本中,这些代码保留完整。
|
||||||
|
|
||||||
`USER_TYPE === 'ant'` 在代码库中出现 **377+ 次**(含 `=== 'ant'` 291 次、`(process.env.USER_TYPE) === 'ant'` 86 次),另有 `!== 'ant'` 53 次、其他引用约 35 次,总计 **465 处引用**,控制着工具、命令、API、UI 等方方面面。
|
`USER_TYPE === 'ant'` 在代码库中出现 **351+ 次**(跨 163 个文件),另有 `!== 'ant'` 59 次(跨 38 个文件),总计 **410+ 处引用**,控制着工具、命令、API、UI 等方方面面。
|
||||||
|
|
||||||
## Ant-Only 工具
|
## Ant-Only 工具
|
||||||
|
|
||||||
@@ -25,10 +25,10 @@ keywords: ["Ant 特权", "USER_TYPE", "身份门控", "内部功能", "Anthropic
|
|||||||
|
|
||||||
| 工具 | 代码位置 | 用途 |
|
| 工具 | 代码位置 | 用途 |
|
||||||
|------|---------|------|
|
|------|---------|------|
|
||||||
| **REPLTool** | `src/tools/REPLTool/` | 高级 REPL 模式——在 VM 中包装 Bash/Read/Edit/Glob/Grep/Agent 等工具 |
|
| **REPLTool** | `packages/builtin-tools/src/tools/REPLTool/` | 高级 REPL 模式——在 VM 中包装 Bash/Read/Edit/Glob/Grep/Agent 等工具 |
|
||||||
| **SuggestBackgroundPRTool** | `src/tools/SuggestBackgroundPRTool/` | 建议在后台创建 PR |
|
| **SuggestBackgroundPRTool** | `packages/builtin-tools/src/tools/SuggestBackgroundPRTool/` | 建议在后台创建 PR |
|
||||||
| **ConfigTool** | `src/tools/ConfigTool/` | 交互式配置编辑器,包含 Gates 标签页用于覆盖 GrowthBook flags |
|
| **ConfigTool** | `packages/builtin-tools/src/tools/ConfigTool/` | 交互式配置编辑器,包含 Gates 标签页用于覆盖 GrowthBook flags |
|
||||||
| **TungstenTool** | `src/tools/TungstenTool/` | 基于 tmux 的终端面板工具(反编译版中已 stub) |
|
| **TungstenTool** | `packages/builtin-tools/src/tools/TungstenTool/` | 基于 tmux 的终端面板工具(反编译版中已 stub) |
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// src/tools.ts 第 14-24 行——条件导入 + Dead Code Elimination 标记
|
// src/tools.ts 第 14-24 行——条件导入 + Dead Code Elimination 标记
|
||||||
@@ -36,18 +36,18 @@ keywords: ["Ant 特权", "USER_TYPE", "身份门控", "内部功能", "Anthropic
|
|||||||
/* eslint-disable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
|
/* eslint-disable custom-rules/no-process-env-top-level, @typescript-eslint/no-require-imports */
|
||||||
const REPLTool =
|
const REPLTool =
|
||||||
process.env.USER_TYPE === 'ant'
|
process.env.USER_TYPE === 'ant'
|
||||||
? require('./tools/REPLTool/REPLTool.js').REPLTool
|
? require('@claude-code-best/builtin-tools/tools/REPLTool/REPLTool.js').REPLTool
|
||||||
: null
|
: null
|
||||||
const SuggestBackgroundPRTool =
|
const SuggestBackgroundPRTool =
|
||||||
process.env.USER_TYPE === 'ant'
|
process.env.USER_TYPE === 'ant'
|
||||||
? require('./tools/SuggestBackgroundPRTool/SuggestBackgroundPRTool.js')
|
? require('@claude-code-best/builtin-tools/tools/SuggestBackgroundPRTool/SuggestBackgroundPRTool.js')
|
||||||
.SuggestBackgroundPRTool
|
.SuggestBackgroundPRTool
|
||||||
: null
|
: null
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ant-Only 命令
|
## Ant-Only 命令
|
||||||
|
|
||||||
`src/commands.ts` 注册了 **28** 个仅在内部构建中可用的斜杠命令(`INTERNAL_ONLY_COMMANDS`,lines 225-254),在 `USER_TYPE === 'ant' && !IS_DEMO` 时才加载(line 343-345):
|
`src/commands.ts` 注册了 **24+** 个仅在内部构建中可用的斜杠命令(`INTERNAL_ONLY_COMMANDS`,lines 267-295),在 `USER_TYPE === 'ant' && !IS_DEMO` 时才加载(line 400-401):
|
||||||
|
|
||||||
<AccordionGroup>
|
<AccordionGroup>
|
||||||
<Accordion title="调试类">
|
<Accordion title="调试类">
|
||||||
@@ -74,7 +74,7 @@ const SuggestBackgroundPRTool =
|
|||||||
- `summary` — 生成摘要
|
- `summary` — 生成摘要
|
||||||
- `subscribePr` — 订阅 PR(需要 `KAIROS_GITHUB_WEBHOOKS` feature flag)
|
- `subscribePr` — 订阅 PR(需要 `KAIROS_GITHUB_WEBHOOKS` feature flag)
|
||||||
- `forceSnip` — 强制截断历史(需要 `HISTORY_SNIP` feature flag)
|
- `forceSnip` — 强制截断历史(需要 `HISTORY_SNIP` feature flag)
|
||||||
- `ultraplan` — 超级规划(需要 `ULTRAPLAN` feature flag)
|
- `ultraplan` — 超级规划(需要 `ULTRAPLAN` feature flag,单独注册于 `commands.ts:396`)
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Accordion title="基础设施类">
|
<Accordion title="基础设施类">
|
||||||
- `backfillSessions` — 回填会话数据
|
- `backfillSessions` — 回填会话数据
|
||||||
|
|||||||
@@ -15,17 +15,19 @@ Claude Code 使用 Bun 打包器的 `bun:bundle` 模块提供编译时特性门
|
|||||||
import { feature } from 'bun:bundle'
|
import { feature } from 'bun:bundle'
|
||||||
|
|
||||||
const SleepTool = feature('PROACTIVE') || feature('KAIROS')
|
const SleepTool = feature('PROACTIVE') || feature('KAIROS')
|
||||||
? require('./tools/SleepTool/SleepTool.js').SleepTool
|
? require('@claude-code-best/builtin-tools/tools/SleepTool/SleepTool.js').SleepTool
|
||||||
: null
|
: null
|
||||||
```
|
```
|
||||||
|
|
||||||
在 Anthropic 的内部构建中,`feature()` 在打包时被求值——返回 `true` 的代码会被保留,返回 `false` 的代码会被 **Dead Code Elimination (DCE)** 彻底移除。
|
在 Anthropic 的内部构建中,`feature()` 在打包时被求值——返回 `true` 的代码会被保留,返回 `false` 的代码会被 **Dead Code Elimination (DCE)** 彻底移除。
|
||||||
|
|
||||||
在我们的反编译版本中,这个函数被兜底为:
|
在我们的反编译版本中,`feature` 从 `bun:bundle` 导入(声明在 `src/types/internal-modules.d.ts`),在运行时始终返回 `false`:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// src/entrypoints/cli.tsx 第 3 行
|
// src/types/internal-modules.d.ts
|
||||||
const feature = (_name: string) => false;
|
declare module 'bun:bundle' {
|
||||||
|
export function feature(name: string): boolean;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
这意味着所有 88+ 个 feature flag 后的代码**在运行时永远不会执行**,但代码本身完整保留,可以阅读和分析。
|
这意味着所有 88+ 个 feature flag 后的代码**在运行时永远不会执行**,但代码本身完整保留,可以阅读和分析。
|
||||||
@@ -79,7 +81,7 @@ Feature flags 在代码中主要有三种使用模式:
|
|||||||
```typescript
|
```typescript
|
||||||
// src/tools.ts — 最常见的模式
|
// src/tools.ts — 最常见的模式
|
||||||
const MonitorTool = feature('MONITOR_TOOL')
|
const MonitorTool = feature('MONITOR_TOOL')
|
||||||
? require('./tools/MonitorTool/MonitorTool.js').MonitorTool
|
? require('@claude-code-best/builtin-tools/tools/MonitorTool/MonitorTool.js').MonitorTool
|
||||||
: null
|
: null
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ keywords: ["GrowthBook", "A/B 测试", "运行时门控", "tengu", "渐进式发
|
|||||||
|
|
||||||
## 集成架构
|
## 集成架构
|
||||||
|
|
||||||
GrowthBook 的完整实现位于 `src/services/analytics/growthbook.ts`(1156 行),工作流程如下:
|
GrowthBook 的完整实现位于 `src/services/analytics/growthbook.ts`(1258 行),工作流程如下:
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="启动时获取远程配置">
|
<Step title="启动时获取远程配置">
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ keywords: ["隐藏功能", "未公开功能", "秘密功能", "Claude Code 彩
|
|||||||
<Accordion title="VOICE_MODE:语音交互">
|
<Accordion title="VOICE_MODE:语音交互">
|
||||||
**门控**: `feature('VOICE_MODE')`
|
**门控**: `feature('VOICE_MODE')`
|
||||||
|
|
||||||
代码中存在语音输入模式的注册点,但核心实现依赖于 `audio-napi` 包(在反编译版本中已 stub):
|
代码中存在语音输入模式的注册点,核心实现依赖 `audio-capture-napi` 包(已恢复):
|
||||||
|
|
||||||
- 通过 `/voice` 命令激活
|
- 通过 `/voice` 命令激活
|
||||||
- "按住说话"(hold-to-talk)交互模式
|
- "按住说话"(hold-to-talk)交互模式
|
||||||
|
|||||||
@@ -64,24 +64,27 @@ Claude Code 从上到下分为五个层次,每一层职责清晰、边界分
|
|||||||
needsFollowUp ? continue : return { reason }
|
needsFollowUp ? continue : return { reason }
|
||||||
```
|
```
|
||||||
|
|
||||||
完整的状态机通过 `State` 类型(`src/query.ts:204`)在迭代间传递,包含 10 个字段(messages、autoCompactTracking、maxOutputTokensRecoveryCount 等)。
|
完整的状态机通过 `State` 类型(`src/query.ts:207`)在迭代间传递,包含 10 个字段(messages、autoCompactTracking、maxOutputTokensRecoveryCount 等)。
|
||||||
|
|
||||||
### 4. 工具层(`src/tools.ts` → `src/Tool.ts`)
|
### 4. 工具层(`src/tools.ts` → `src/Tool.ts`)
|
||||||
|
|
||||||
`getAllBaseTools()`(`src/tools.ts:191`)组装 50+ 工具列表,经过 `filterToolsByDenyRules()` 权限过滤后传给 API。
|
`getAllBaseTools()`(`src/tools.ts:195`)组装 50+ 工具列表,经过 `filterToolsByDenyRules()` 权限过滤后传给 API。
|
||||||
|
|
||||||
每个工具实现 `Tool<Input, Output, Progress>` 接口(`src/Tool.ts:362`),核心方法链:
|
每个工具实现 `Tool<Input, Output, Progress>` 接口(`src/Tool.ts:368`),核心方法链:
|
||||||
```
|
```
|
||||||
validateInput() → canUseTool()(UI 层)→ checkPermissions() → call() → ToolResult
|
validateInput() → canUseTool()(UI 层)→ checkPermissions() → call() → ToolResult
|
||||||
```
|
```
|
||||||
|
|
||||||
### 5. 通信层(`src/services/api/claude.ts`)
|
### 5. 通信层(`src/services/api/claude.ts`)
|
||||||
|
|
||||||
API 客户端支持 4 种 Provider:
|
API 客户端支持 7 种 Provider:
|
||||||
- **Anthropic Direct**:默认
|
- **Anthropic Direct (firstParty)**:默认
|
||||||
- **AWS Bedrock**:`ANTHROPIC_BEDROCK_BASE_URL`
|
- **AWS Bedrock**:`ANTHROPIC_BEDROCK_BASE_URL`
|
||||||
- **Google Vertex**:`ANTHROPIC_VERTEX_PROJECT_ID`
|
- **Google Vertex**:`ANTHROPIC_VERTEX_PROJECT_ID`
|
||||||
- **Azure**:通过自定义 base URL
|
- **Foundry**:`ANTHROPIC_CODE_USE_FOUNDRY`
|
||||||
|
- **OpenAI**:兼容层
|
||||||
|
- **Gemini**:兼容层
|
||||||
|
- **Grok (xAI)**:兼容层
|
||||||
|
|
||||||
`deps.callModel()` 发起流式请求,返回 `BetaRawMessageStreamEvent` 事件流。支持 Prompt Cache(`cache_control`)、thinking blocks、multi-turn tool use。
|
`deps.callModel()` 发起流式请求,返回 `BetaRawMessageStreamEvent` 事件流。支持 Prompt Cache(`cache_control`)、thinking blocks、multi-turn tool use。
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ Claude Code 是一个**运行在本地终端中的 agentic coding system**。它
|
|||||||
│ 实际执行: 读文件、运行命令、搜索代码... │
|
│ 实际执行: 读文件、运行命令、搜索代码... │
|
||||||
├─────────────────────────────────────────────────────────┤
|
├─────────────────────────────────────────────────────────┤
|
||||||
│ 6. 通信层 (claude.ts → Anthropic API) │
|
│ 6. 通信层 (claude.ts → Anthropic API) │
|
||||||
│ 流式 HTTP, 支持 Bedrock/Vertex/Azure 多 provider │
|
│ 流式 HTTP, 支持 Bedrock/Vertex/Foundry 等 7 种 provider │
|
||||||
└─────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ AI 没有真正的"记忆",Claude Code 通过精心分层营造了这个幻觉
|
|||||||
|
|
||||||
### 3. 工具系统的权限双轨制
|
### 3. 工具系统的权限双轨制
|
||||||
|
|
||||||
`src/tools/BashTool/shouldUseSandbox.ts` 展示了一个精巧的双重安全模型:
|
`packages/builtin-tools/src/tools/BashTool/shouldUseSandbox.ts` 展示了一个精巧的双重安全模型:
|
||||||
|
|
||||||
- **应用层**:权限规则决定"能不能执行"(白名单/黑名单/用户确认)
|
- **应用层**:权限规则决定"能不能执行"(白名单/黑名单/用户确认)
|
||||||
- **OS 层**:沙箱决定"执行时能做什么"(文件系统/网络/进程隔离)
|
- **OS 层**:沙箱决定"执行时能做什么"(文件系统/网络/进程隔离)
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ ENABLE_LSP_TOOL=1 bun run dev
|
|||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────┐
|
||||||
│ LSP Tool │
|
│ LSP Tool │
|
||||||
│ src/tools/LSPTool/LSPTool.ts │
|
│ packages/builtin-tools/src/tools/LSPTool/LSPTool.ts│
|
||||||
│ (Claude 可调用的工具,9 种操作) │
|
│ (Claude 可调用的工具,9 种操作) │
|
||||||
└──────────────────────┬──────────────────────────────┘
|
└──────────────────────┬──────────────────────────────┘
|
||||||
│
|
│
|
||||||
@@ -128,10 +128,10 @@ LSP 服务器会异步推送 `textDocument/publishDiagnostics` 通知,经去
|
|||||||
| `src/services/lsp/config.ts` | 从插件加载 LSP 服务器配置 |
|
| `src/services/lsp/config.ts` | 从插件加载 LSP 服务器配置 |
|
||||||
| `src/services/lsp/LSPDiagnosticRegistry.ts` | 诊断信息注册、去重、容量限制 |
|
| `src/services/lsp/LSPDiagnosticRegistry.ts` | 诊断信息注册、去重、容量限制 |
|
||||||
| `src/services/lsp/passiveFeedback.ts` | 注册 `publishDiagnostics` 通知处理器 |
|
| `src/services/lsp/passiveFeedback.ts` | 注册 `publishDiagnostics` 通知处理器 |
|
||||||
| `src/tools/LSPTool/LSPTool.ts` | LSP Tool 实现(暴露给 Claude) |
|
| `packages/builtin-tools/src/tools/LSPTool/LSPTool.ts` | LSP Tool 实现(暴露给 Claude) |
|
||||||
| `src/tools/LSPTool/schemas.ts` | 输入 schema(9 种操作的 discriminated union) |
|
| `packages/builtin-tools/src/tools/LSPTool/schemas.ts` | 输入 schema(9 种操作的 discriminated union) |
|
||||||
| `src/tools/LSPTool/formatters.ts` | 各操作结果的格式化 |
|
| `packages/builtin-tools/src/tools/LSPTool/formatters.ts` | 各操作结果的格式化 |
|
||||||
| `src/tools/LSPTool/prompt.ts` | Tool 描述文本 |
|
| `packages/builtin-tools/src/tools/LSPTool/prompt.ts` | Tool 描述文本 |
|
||||||
| `src/utils/plugins/lspPluginIntegration.ts` | 从插件加载、验证、环境变量解析、作用域管理 |
|
| `src/utils/plugins/lspPluginIntegration.ts` | 从插件加载、验证、环境变量解析、作用域管理 |
|
||||||
|
|
||||||
## LSP Tool 支持的操作
|
## LSP Tool 支持的操作
|
||||||
@@ -200,9 +200,9 @@ LSP 服务器通过插件提供。插件的 `manifest.json` 中可以声明 LSP
|
|||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| `command` | string | 是 | LSP 服务器可执行命令(不含空格) |
|
| `command` | string | 是 | LSP 服务器可执行命令(不含空格) |
|
||||||
| `args` | string[] | 否 | 命令行参数 |
|
| `args` | string[] | 否 | 命令行参数 |
|
||||||
| `extensionToLanguage` | Record<string, string> | 是 | 文件扩展名到语言 ID 的映射(至少一个) |
|
| `extensionToLanguage` | `Record<string, string>` | 是 | 文件扩展名到语言 ID 的映射(至少一个) |
|
||||||
| `transport` | `"stdio"` \| `"socket"` | 否 | 通信方式,默认 `stdio` |
|
| `transport` | `"stdio"` \| `"socket"` | 否 | 通信方式,默认 `stdio` |
|
||||||
| `env` | Record<string, string> | 否 | 启动服务器时设置的环境变量 |
|
| `env` | `Record<string, string>` | 否 | 启动服务器时设置的环境变量 |
|
||||||
| `initializationOptions` | unknown | 否 | 传给服务器的初始化选项 |
|
| `initializationOptions` | unknown | 否 | 传给服务器的初始化选项 |
|
||||||
| `settings` | unknown | 否 | 通过 `workspace/didChangeConfiguration` 传递的设置 |
|
| `settings` | unknown | 否 | 通过 `workspace/didChangeConfiguration` 传递的设置 |
|
||||||
| `workspaceFolder` | string | 否 | 工作区目录路径 |
|
| `workspaceFolder` | string | 否 | 工作区目录路径 |
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
# OpenAI兼容模型中task工具使用指南
|
|
||||||
|
|
||||||
## 问题描述
|
|
||||||
|
|
||||||
当使用OpenAI兼容模型(如DeepSeek、Ollama、vLLM等)时,调用task工具(TaskGet、TaskCreate、TaskUpdate、TaskList)可能会出现以下错误:
|
|
||||||
|
|
||||||
```
|
|
||||||
Error: InputValidationError: TaskGet failed due to the following issues:
|
|
||||||
The required parameter `taskId` is missing
|
|
||||||
An unexpected parameter `task_id` was provided
|
|
||||||
|
|
||||||
This tool's schema was not sent to the API — it was not in the discovered-tool set derived from message history. Without the schema in your prompt, typed parameters (arrays, numbers, booleans) get emitted as strings and the client-side parser rejects them. Load the tool first: call ToolSearch with query "select:TaskGet", then retry this call.
|
|
||||||
```
|
|
||||||
|
|
||||||
## 问题原因
|
|
||||||
|
|
||||||
### 1. 延迟加载工具(Deferred Tools)
|
|
||||||
task工具都是延迟加载的(`shouldDefer: true`),这意味着:
|
|
||||||
- 工具的模式(schema)不会在初始API调用中发送
|
|
||||||
- 需要先通过`ToolSearch`工具发现
|
|
||||||
- 只有在被发现后,工具模式才会被发送给API
|
|
||||||
|
|
||||||
### 2. 参数名转换问题
|
|
||||||
- task工具使用驼峰命名:`taskId`
|
|
||||||
- OpenAI兼容模型可能输出蛇形命名:`task_id`
|
|
||||||
- 当工具模式没有被发送时,模型会猜测参数名,可能导致不匹配
|
|
||||||
|
|
||||||
## 解决方案
|
|
||||||
|
|
||||||
### 方案1:先使用ToolSearch(推荐)
|
|
||||||
在使用task工具之前,先调用`ToolSearch`工具:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 第一步:发现task工具
|
|
||||||
ToolSearch("select:TaskGet,TaskCreate,TaskUpdate,TaskList")
|
|
||||||
|
|
||||||
// 第二步:正常使用task工具
|
|
||||||
TaskCreate({ subject: "任务标题", description: "任务描述" })
|
|
||||||
TaskGet({ taskId: "1" })
|
|
||||||
TaskUpdate({ taskId: "1", status: "completed" })
|
|
||||||
TaskList()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 方案2:批量发现所有task工具
|
|
||||||
```javascript
|
|
||||||
// 一次性发现所有task工具
|
|
||||||
ToolSearch("select:TaskGet,TaskCreate,TaskUpdate,TaskList")
|
|
||||||
|
|
||||||
// 然后可以任意使用task工具
|
|
||||||
const task = await TaskCreate({ subject: "新任务", description: "任务描述" })
|
|
||||||
console.log(`创建的任务ID: ${task.id}`)
|
|
||||||
|
|
||||||
const taskList = await TaskList()
|
|
||||||
console.log(`当前有 ${taskList.tasks.length} 个任务`)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 方案3:单独发现特定工具
|
|
||||||
```javascript
|
|
||||||
// 只发现需要的工具
|
|
||||||
ToolSearch("select:TaskGet")
|
|
||||||
|
|
||||||
// 然后使用该工具
|
|
||||||
TaskGet({ taskId: "1" })
|
|
||||||
```
|
|
||||||
|
|
||||||
## 参数名注意事项
|
|
||||||
|
|
||||||
在使用OpenAI兼容模型时,请注意参数名格式:
|
|
||||||
|
|
||||||
### ✅ 正确(驼峰命名)
|
|
||||||
```javascript
|
|
||||||
TaskGet({ taskId: "1" })
|
|
||||||
TaskCreate({ subject: "标题", description: "描述" })
|
|
||||||
TaskUpdate({ taskId: "1", status: "completed" })
|
|
||||||
```
|
|
||||||
|
|
||||||
### ❌ 错误(蛇形命名)
|
|
||||||
```javascript
|
|
||||||
TaskGet({ task_id: "1" }) // 错误:应该使用taskId
|
|
||||||
TaskCreate({ subject: "标题", description: "描述" }) // 正确
|
|
||||||
TaskUpdate({ task_id: "1", status: "completed" }) // 错误:应该使用taskId
|
|
||||||
```
|
|
||||||
|
|
||||||
## 常见问题解答
|
|
||||||
|
|
||||||
### Q1: 为什么需要先使用ToolSearch?
|
|
||||||
A: task工具是延迟加载的,它们的模式只有在被`ToolSearch`工具发现后才会发送给API。没有工具模式,模型无法知道正确的参数名和类型。
|
|
||||||
|
|
||||||
### Q2: 每次会话都需要使用ToolSearch吗?
|
|
||||||
A: 是的,每次新的会话都需要先使用ToolSearch发现工具。工具发现状态不会在会话之间保留。
|
|
||||||
|
|
||||||
### Q3: 使用Anthropic官方模型也需要这样吗?
|
|
||||||
A: 通常不需要。Anthropic官方模型对延迟加载工具的处理更智能,但为了兼容性,建议在使用task工具前都先使用ToolSearch。
|
|
||||||
|
|
||||||
### Q4: 可以一次性发现所有工具吗?
|
|
||||||
A: 可以,使用`ToolSearch("select:TaskGet,TaskCreate,TaskUpdate,TaskList")`可以一次性发现所有task工具。
|
|
||||||
|
|
||||||
### Q5: 如果忘记使用ToolSearch会怎样?
|
|
||||||
A: 会收到参数验证错误,提示需要先使用ToolSearch。按照错误信息的指导操作即可。
|
|
||||||
|
|
||||||
## 最佳实践
|
|
||||||
|
|
||||||
1. **会话开始时发现工具**:在开始使用task工具前,先调用ToolSearch
|
|
||||||
2. **批量发现**:一次性发现所有需要的task工具
|
|
||||||
3. **检查参数名**:确保使用正确的驼峰命名参数
|
|
||||||
4. **查看错误信息**:如果遇到错误,仔细阅读错误信息中的指导
|
|
||||||
|
|
||||||
## 示例工作流
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// 1. 开始新会话
|
|
||||||
// 2. 发现task工具
|
|
||||||
ToolSearch("select:TaskGet,TaskCreate,TaskUpdate,TaskList")
|
|
||||||
|
|
||||||
// 3. 创建任务
|
|
||||||
const newTask = await TaskCreate({
|
|
||||||
subject: "修复OpenAI兼容性问题",
|
|
||||||
description: "解决task工具在OpenAI兼容模型下的参数名问题"
|
|
||||||
})
|
|
||||||
|
|
||||||
// 4. 获取任务详情
|
|
||||||
const taskDetails = await TaskGet({ taskId: newTask.id })
|
|
||||||
|
|
||||||
// 5. 更新任务状态
|
|
||||||
await TaskUpdate({
|
|
||||||
taskId: newTask.id,
|
|
||||||
status: "in_progress",
|
|
||||||
activeForm: "修复OpenAI兼容性问题"
|
|
||||||
})
|
|
||||||
|
|
||||||
// 6. 查看所有任务
|
|
||||||
const allTasks = await TaskList()
|
|
||||||
console.log(`当前有 ${allTasks.tasks.length} 个任务`)
|
|
||||||
|
|
||||||
// 7. 完成任务
|
|
||||||
await TaskUpdate({
|
|
||||||
taskId: newTask.id,
|
|
||||||
status: "completed"
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## 故障排除
|
|
||||||
|
|
||||||
### 错误:参数名不匹配
|
|
||||||
**症状**:`taskId`参数缺失,发现`task_id`参数
|
|
||||||
**解决**:确保使用驼峰命名的`taskId`,而不是蛇形命名的`task_id`
|
|
||||||
|
|
||||||
### 错误:工具模式未发送
|
|
||||||
**症状**:`This tool's schema was not sent to the API`
|
|
||||||
**解决**:先使用`ToolSearch("select:工具名")`发现工具
|
|
||||||
|
|
||||||
### 错误:工具不可用
|
|
||||||
**症状**:工具调用失败,没有具体错误信息
|
|
||||||
**解决**:检查工具是否启用(通过`isTodoV2Enabled()`),确保环境变量设置正确
|
|
||||||
|
|
||||||
## 相关配置
|
|
||||||
|
|
||||||
### 环境变量
|
|
||||||
```bash
|
|
||||||
# 启用OpenAI兼容模式
|
|
||||||
export CLAUDE_CODE_USE_OPENAI=1
|
|
||||||
export OPENAI_API_KEY=your-api-key
|
|
||||||
export OPENAI_BASE_URL=https://api.deepseek.com
|
|
||||||
|
|
||||||
# 配置模型映射
|
|
||||||
export OPENAI_DEFAULT_SONNET_MODEL=deepseek-chat
|
|
||||||
export OPENAI_DEFAULT_OPUS_MODEL=deepseek-chat
|
|
||||||
export OPENAI_DEFAULT_HAIKU_MODEL=deepseek-chat
|
|
||||||
```
|
|
||||||
|
|
||||||
### 设置文件
|
|
||||||
通过`/login`命令配置OpenAI兼容模式后,设置会保存在`~/.claude/settings.json`:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"modelType": "openai",
|
|
||||||
"openai": {
|
|
||||||
"baseURL": "https://api.deepseek.com",
|
|
||||||
"apiKey": "your-api-key",
|
|
||||||
"models": {
|
|
||||||
"haiku": "deepseek-chat",
|
|
||||||
"sonnet": "deepseek-chat",
|
|
||||||
"opus": "deepseek-chat"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
在使用OpenAI兼容模型时,task工具需要先通过`ToolSearch`发现才能正常使用。遵循"先发现,后使用"的原则,并注意参数名的正确格式(驼峰命名),可以确保task工具在OpenAI兼容模型下正常工作。
|
|
||||||
@@ -1,425 +0,0 @@
|
|||||||
# OpenAI 协议兼容层
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
claude-code 支持通过 OpenAI Chat Completions API(`/v1/chat/completions`)兼容任意 OpenAI 协议端点,包括 Ollama、DeepSeek、vLLM、One API、LiteLLM 等。
|
|
||||||
|
|
||||||
核心策略为**流适配器模式**:在 `queryModel()` 中插入提前返回分支,将 Anthropic 格式请求转为 OpenAI 格式,调用 OpenAI SDK,再将 SSE 流转换回 `BetaRawMessageStreamEvent` 格式。下游代码(流处理循环、query.ts、QueryEngine.ts、REPL)**完全不改**。
|
|
||||||
|
|
||||||
## 环境变量
|
|
||||||
|
|
||||||
| 变量 | 必需 | 说明 |
|
|
||||||
|---|---|---|
|
|
||||||
| `CLAUDE_CODE_USE_OPENAI` | 是 | 设为 `1` 启用 OpenAI 后端 |
|
|
||||||
| `OPENAI_API_KEY` | 是 | API key(Ollama 等可设为任意值) |
|
|
||||||
| `OPENAI_BASE_URL` | 推荐 | 端点 URL(如 `http://localhost:11434/v1`) |
|
|
||||||
| `OPENAI_MODEL` | 可选 | 覆盖所有请求的模型名(跳过映射) |
|
|
||||||
| `OPENAI_DEFAULT_OPUS_MODEL` | 可选 | 覆盖 opus 家族对应的模型(如 `o3`, `o3-mini`, `o1-pro`) |
|
|
||||||
| `OPENAI_DEFAULT_SONNET_MODEL` | 可选 | 覆盖 sonnet 家族对应的模型(如 `gpt-4o`, `gpt-4.1`) |
|
|
||||||
| `OPENAI_DEFAULT_HAIKU_MODEL` | 可选 | 覆盖 haiku 家族对应的模型(如 `gpt-4o-mini`, `gpt-4.0-mini`) |
|
|
||||||
| `OPENAI_ORG_ID` | 可选 | Organization ID |
|
|
||||||
| `OPENAI_PROJECT_ID` | 可选 | Project ID |
|
|
||||||
|
|
||||||
### 使用示例
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Ollama
|
|
||||||
CLAUDE_CODE_USE_OPENAI=1 \
|
|
||||||
OPENAI_API_KEY=ollama \
|
|
||||||
OPENAI_BASE_URL=http://localhost:11434/v1 \
|
|
||||||
OPENAI_MODEL=qwen2.5-coder-32b \
|
|
||||||
bun run dev
|
|
||||||
|
|
||||||
# DeepSeek(自动支持 Thinking)
|
|
||||||
CLAUDE_CODE_USE_OPENAI=1 \
|
|
||||||
OPENAI_API_KEY=sk-xxx \
|
|
||||||
OPENAI_BASE_URL=https://api.deepseek.com/v1 \
|
|
||||||
OPENAI_MODEL=deepseek-chat \
|
|
||||||
bun run dev
|
|
||||||
|
|
||||||
# vLLM
|
|
||||||
CLAUDE_CODE_USE_OPENAI=1 \
|
|
||||||
OPENAI_API_KEY=token-abc123 \
|
|
||||||
OPENAI_BASE_URL=http://localhost:8000/v1 \
|
|
||||||
OPENAI_MODEL=Qwen/Qwen2.5-Coder-32B-Instruct \
|
|
||||||
bun run dev
|
|
||||||
|
|
||||||
# One API / LiteLLM
|
|
||||||
CLAUDE_CODE_USE_OPENAI=1 \
|
|
||||||
OPENAI_API_KEY=sk-your-key \
|
|
||||||
OPENAI_BASE_URL=https://your-one-api.example.com/v1 \
|
|
||||||
OPENAI_MODEL=gpt-4o \
|
|
||||||
bun run dev
|
|
||||||
|
|
||||||
# 自定义模型映射(使用家族变量)
|
|
||||||
CLAUDE_CODE_USE_OPENAI=1 \
|
|
||||||
OPENAI_API_KEY=sk-xxx \
|
|
||||||
OPENAI_BASE_URL=https://my-gateway.example.com/v1 \
|
|
||||||
OPENAI_DEFAULT_SONNET_MODEL="gpt-4o-2024-11-20" \
|
|
||||||
OPENAI_DEFAULT_HAIKU_MODEL="gpt-4o-mini" \
|
|
||||||
bun run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## 架构
|
|
||||||
|
|
||||||
### 请求流程
|
|
||||||
|
|
||||||
```
|
|
||||||
queryModel() [claude.ts]
|
|
||||||
├── 共享预处理(消息归一化、工具过滤、媒体裁剪)
|
|
||||||
└── if (getAPIProvider() === 'openai')
|
|
||||||
└── queryModelOpenAI() [openai/index.ts]
|
|
||||||
├── resolveOpenAIModel() → 解析模型名
|
|
||||||
├── normalizeMessagesForAPI() → 共享消息预处理
|
|
||||||
├── toolToAPISchema() → 构建工具 schema
|
|
||||||
├── anthropicMessagesToOpenAI() → 消息格式转换
|
|
||||||
├── anthropicToolsToOpenAI() → 工具格式转换
|
|
||||||
├── openai.chat.completions.create({ stream: true })
|
|
||||||
└── adaptOpenAIStreamToAnthropic() → 流格式转换
|
|
||||||
├── delta.reasoning_content → thinking 块
|
|
||||||
├── delta.content → text 块
|
|
||||||
├── delta.tool_calls → tool_use 块
|
|
||||||
├── usage.cached_tokens → cache_read_input_tokens
|
|
||||||
└── yield BetaRawMessageStreamEvent
|
|
||||||
```
|
|
||||||
|
|
||||||
### 模型名解析优先级
|
|
||||||
|
|
||||||
`resolveOpenAIModel()` 的解析顺序:
|
|
||||||
|
|
||||||
1. `OPENAI_MODEL` 环境变量 → 直接使用,覆盖所有
|
|
||||||
2. `OPENAI_DEFAULT_{FAMILY}_MODEL` 变量(如 `OPENAI_DEFAULT_SONNET_MODEL`)→ 按模型家族覆盖
|
|
||||||
3. `ANTHROPIC_DEFAULT_{FAMILY}_MODEL` 变量(向后兼容)
|
|
||||||
4. 内置默认映射(见下表)
|
|
||||||
5. 以上都不匹配 → 原名透传
|
|
||||||
|
|
||||||
### 内置模型映射
|
|
||||||
|
|
||||||
| Anthropic 模型 | OpenAI 映射 |
|
|
||||||
|---|---|
|
|
||||||
| `claude-sonnet-4-6` | `gpt-4o` |
|
|
||||||
| `claude-sonnet-4-5-20250929` | `gpt-4o` |
|
|
||||||
| `claude-sonnet-4-20250514` | `gpt-4o` |
|
|
||||||
| `claude-3-7-sonnet-20250219` | `gpt-4o` |
|
|
||||||
| `claude-3-5-sonnet-20241022` | `gpt-4o` |
|
|
||||||
| `claude-opus-4-6` | `o3` |
|
|
||||||
| `claude-opus-4-5-20251101` | `o3` |
|
|
||||||
| `claude-opus-4-1-20250805` | `o3` |
|
|
||||||
| `claude-opus-4-20250514` | `o3` |
|
|
||||||
| `claude-haiku-4-5-20251001` | `gpt-4o-mini` |
|
|
||||||
| `claude-3-5-haiku-20241022` | `gpt-4o-mini` |
|
|
||||||
|
|
||||||
同时会自动剥离 `[1m]` 后缀(Claude 特有的 modifier)。
|
|
||||||
|
|
||||||
## 文件结构
|
|
||||||
|
|
||||||
### 新增文件
|
|
||||||
|
|
||||||
```
|
|
||||||
src/services/api/openai/
|
|
||||||
├── client.ts # OpenAI SDK 客户端工厂(~50 行)
|
|
||||||
├── convertMessages.ts # Anthropic → OpenAI 消息格式转换(~190 行)
|
|
||||||
├── convertTools.ts # Anthropic → OpenAI 工具格式转换(~70 行)
|
|
||||||
├── streamAdapter.ts # SSE 流转换核心,含 thinking + caching(~270 行)
|
|
||||||
├── modelMapping.ts # 模型名解析(~60 行)
|
|
||||||
├── index.ts # 公共入口 queryModelOpenAI()(~110 行)
|
|
||||||
└── __tests__/
|
|
||||||
├── convertMessages.test.ts # 10 个测试
|
|
||||||
├── convertTools.test.ts # 7 个测试
|
|
||||||
├── modelMapping.test.ts # 6 个测试
|
|
||||||
└── streamAdapter.test.ts # 14 个测试(含 thinking + caching)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 修改文件
|
|
||||||
|
|
||||||
| 文件 | 改动 |
|
|
||||||
|---|---|
|
|
||||||
| `src/utils/model/providers.ts` | 添加 `'openai'` provider 类型 + `CLAUDE_CODE_USE_OPENAI` 检查(最高优先级) |
|
|
||||||
| `src/utils/model/configs.ts` | 每个 ModelConfig 添加 `openai` 键 |
|
|
||||||
| `src/services/api/claude.ts` | 在 `stripExcessMediaItems()` 后插入 OpenAI 提前返回分支(~8 行) |
|
|
||||||
| `package.json` | 添加 `"openai": "^4.73.0"` 依赖 |
|
|
||||||
|
|
||||||
## 消息转换规则
|
|
||||||
|
|
||||||
### Anthropic → OpenAI
|
|
||||||
|
|
||||||
| Anthropic | OpenAI |
|
|
||||||
|---|---|
|
|
||||||
| `system` prompt(`string[]`) | `role: "system"` 消息(`\n\n` 拼接) |
|
|
||||||
| `user` + `text` 块 | `role: "user"` 消息 |
|
|
||||||
| `assistant` + `text` 块 | `role: "assistant"` + `content` |
|
|
||||||
| `assistant` + `tool_use` 块 | `role: "assistant"` + `tool_calls[]` |
|
|
||||||
| `user` + `tool_result` 块 | `role: "tool"` + `tool_call_id` |
|
|
||||||
| `thinking` 块 | 静默丢弃(请求侧) |
|
|
||||||
|
|
||||||
### 工具转换
|
|
||||||
|
|
||||||
| Anthropic | OpenAI |
|
|
||||||
|---|---|
|
|
||||||
| `{ name, description, input_schema }` | `{ type: "function", function: { name, description, parameters } }` |
|
|
||||||
| `cache_control`, `defer_loading` 等字段 | 剥离 |
|
|
||||||
| `tool_choice: { type: "auto" }` | `"auto"` |
|
|
||||||
| `tool_choice: { type: "any" }` | `"required"` |
|
|
||||||
| `tool_choice: { type: "tool", name }` | `{ type: "function", function: { name } }` |
|
|
||||||
|
|
||||||
### 消息转换示例
|
|
||||||
|
|
||||||
```
|
|
||||||
Anthropic: OpenAI:
|
|
||||||
[
|
|
||||||
system: ["You are helpful."], [
|
|
||||||
{ role: "system",
|
|
||||||
{ role: "user", content: "You are helpful." },
|
|
||||||
content: [ { role: "user",
|
|
||||||
{ type: "text", text: "Run ls" } content: "Run ls"
|
|
||||||
] },
|
|
||||||
}, { role: "assistant",
|
|
||||||
{ role: "assistant", content: "I'll check.",
|
|
||||||
content: [ tool_calls: [{
|
|
||||||
{ type: "text", text: "I'll check."}, id: "tu_123",
|
|
||||||
{ type: "tool_use", type: "function",
|
|
||||||
id: "tu_123", name: "bash", function: {
|
|
||||||
input: { command: "ls" } } name: "bash",
|
|
||||||
] arguments: '{"command":"ls"}'
|
|
||||||
}, }] }
|
|
||||||
{ role: "user", { role: "tool",
|
|
||||||
content: [ tool_call_id: "tu_123",
|
|
||||||
{ type: "tool_result", content: "file1\nfile2"
|
|
||||||
tool_use_id: "tu_123", }
|
|
||||||
content: "file1\nfile2" ]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## 流转换规则
|
|
||||||
|
|
||||||
### SSE Chunk → Anthropic Event 映射
|
|
||||||
|
|
||||||
| OpenAI Chunk | Anthropic Event |
|
|
||||||
|---|---|
|
|
||||||
| 首个 chunk | `message_start`(含 usage) |
|
|
||||||
| `delta.reasoning_content` | `content_block_start(thinking)` + `thinking_delta` |
|
|
||||||
| `delta.content` | `content_block_start(text)` + `text_delta` |
|
|
||||||
| `delta.tool_calls` | `content_block_start(tool_use)` + `input_json_delta` |
|
|
||||||
| `finish_reason: "stop"` | `message_delta(stop_reason: "end_turn")` |
|
|
||||||
| `finish_reason: "tool_calls"` | `message_delta(stop_reason: "tool_use")` |
|
|
||||||
| `finish_reason: "length"` | `message_delta(stop_reason: "max_tokens")` |
|
|
||||||
|
|
||||||
### 块顺序
|
|
||||||
|
|
||||||
当模型返回 `reasoning_content` 时(如 DeepSeek),块顺序与 Anthropic 一致:
|
|
||||||
|
|
||||||
```
|
|
||||||
thinking block (index 0) ← delta.reasoning_content
|
|
||||||
text block (index 1) ← delta.content
|
|
||||||
```
|
|
||||||
|
|
||||||
或:
|
|
||||||
|
|
||||||
```
|
|
||||||
thinking block (index 0) ← delta.reasoning_content
|
|
||||||
tool_use block (index 1) ← delta.tool_calls
|
|
||||||
```
|
|
||||||
|
|
||||||
无 `reasoning_content` 时:
|
|
||||||
|
|
||||||
```
|
|
||||||
text block (index 0) ← delta.content
|
|
||||||
tool_use block (index 1) ← delta.tool_calls(如果有)
|
|
||||||
```
|
|
||||||
|
|
||||||
### finish_reason 映射
|
|
||||||
|
|
||||||
| OpenAI | Anthropic |
|
|
||||||
|---|---|
|
|
||||||
| `stop` | `end_turn` |
|
|
||||||
| `tool_calls` | `tool_use` |
|
|
||||||
| `length` | `max_tokens` |
|
|
||||||
| `content_filter` | `end_turn` |
|
|
||||||
|
|
||||||
### 事件序列示例
|
|
||||||
|
|
||||||
**纯文本响应**:
|
|
||||||
```
|
|
||||||
OpenAI chunks:
|
|
||||||
delta.content = "Hello"
|
|
||||||
delta.content = " world"
|
|
||||||
finish_reason = "stop"
|
|
||||||
|
|
||||||
→ Anthropic events:
|
|
||||||
message_start { message: { id, role: 'assistant', usage: {...} } }
|
|
||||||
content_block_start { index: 0, content_block: { type: 'text' } }
|
|
||||||
content_block_delta { index: 0, delta: { type: 'text_delta', text: 'Hello' } }
|
|
||||||
content_block_delta { index: 0, delta: { type: 'text_delta', text: ' world' } }
|
|
||||||
content_block_stop { index: 0 }
|
|
||||||
message_delta { delta: { stop_reason: 'end_turn' } }
|
|
||||||
message_stop
|
|
||||||
```
|
|
||||||
|
|
||||||
**Thinking + 文本(DeepSeek 风格)**:
|
|
||||||
```
|
|
||||||
OpenAI chunks:
|
|
||||||
delta.reasoning_content = "Let me think..."
|
|
||||||
delta.reasoning_content = " step by step."
|
|
||||||
delta.content = "The answer is 42."
|
|
||||||
finish_reason = "stop"
|
|
||||||
|
|
||||||
→ Anthropic events:
|
|
||||||
message_start { ... }
|
|
||||||
content_block_start { index: 0, content_block: { type: 'thinking', signature: '' } }
|
|
||||||
content_block_delta { index: 0, delta: { type: 'thinking_delta', thinking: 'Let me think...' } }
|
|
||||||
content_block_delta { index: 0, delta: { type: 'thinking_delta', thinking: ' step by step.' } }
|
|
||||||
content_block_stop { index: 0 }
|
|
||||||
content_block_start { index: 1, content_block: { type: 'text' } }
|
|
||||||
content_block_delta { index: 1, delta: { type: 'text_delta', text: 'The answer is 42.' } }
|
|
||||||
content_block_stop { index: 1 }
|
|
||||||
message_delta { delta: { stop_reason: 'end_turn' } }
|
|
||||||
message_stop
|
|
||||||
```
|
|
||||||
|
|
||||||
**工具调用**:
|
|
||||||
```
|
|
||||||
OpenAI chunks:
|
|
||||||
delta.tool_calls[0] = { id: 'call_xxx', function: { name: 'bash', arguments: '' } }
|
|
||||||
delta.tool_calls[0].function.arguments = '{"comm'
|
|
||||||
delta.tool_calls[0].function.arguments = 'and":"ls"}'
|
|
||||||
finish_reason = "tool_calls"
|
|
||||||
|
|
||||||
→ Anthropic events:
|
|
||||||
message_start { ... }
|
|
||||||
content_block_start { index: 0, content_block: { type: 'tool_use', id: 'call_xxx', name: 'bash' } }
|
|
||||||
content_block_delta { index: 0, delta: { type: 'input_json_delta', partial_json: '{"comm' } }
|
|
||||||
content_block_delta { index: 0, delta: { type: 'input_json_delta', partial_json: 'and":"ls"}' } }
|
|
||||||
content_block_stop { index: 0 }
|
|
||||||
message_delta { delta: { stop_reason: 'tool_use' } }
|
|
||||||
message_stop
|
|
||||||
```
|
|
||||||
|
|
||||||
## 功能支持
|
|
||||||
|
|
||||||
### Thinking(思维链)
|
|
||||||
|
|
||||||
**请求侧**:不需要显式配置。支持思维链的模型(DeepSeek 等)会自动返回 `delta.reasoning_content`。
|
|
||||||
|
|
||||||
**响应侧**:`delta.reasoning_content` 被转换为 Anthropic `thinking` content block:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// content_block_start
|
|
||||||
{ type: 'content_block_start', index: 0,
|
|
||||||
content_block: { type: 'thinking', thinking: '', signature: '' } }
|
|
||||||
|
|
||||||
// content_block_delta
|
|
||||||
{ type: 'content_block_delta', index: 0,
|
|
||||||
delta: { type: 'thinking_delta', thinking: 'Let me analyze...' } }
|
|
||||||
```
|
|
||||||
|
|
||||||
thinking block 在 text/tool_use block 之前自动关闭,保持 Anthropic 的块顺序。
|
|
||||||
|
|
||||||
### Prompt Caching
|
|
||||||
|
|
||||||
**请求侧**:OpenAI 端点使用自动缓存,无需显式设置 `cache_control`。
|
|
||||||
|
|
||||||
**响应侧**:OpenAI 的 `usage.prompt_tokens_details.cached_tokens` 被映射到 Anthropic 的 `cache_read_input_tokens`:
|
|
||||||
|
|
||||||
```
|
|
||||||
OpenAI: usage.prompt_tokens_details.cached_tokens = 800
|
|
||||||
↓
|
|
||||||
Anthropic: message_start.message.usage.cache_read_input_tokens = 800
|
|
||||||
```
|
|
||||||
|
|
||||||
在 `message_start` 的 usage 中报告缓存命中量。
|
|
||||||
|
|
||||||
### 工具调用(Tool Use)
|
|
||||||
|
|
||||||
完整支持 OpenAI function calling 格式。所有本地工具(Bash、FileEdit、Grep、Glob、Agent 等)透明工作——它们通过 JSON 输入输出通信,格式无关。
|
|
||||||
|
|
||||||
工具参数以 `input_json_delta` 形式流式传输,由下游代码拼接解析。
|
|
||||||
|
|
||||||
### 不支持的功能
|
|
||||||
|
|
||||||
| 功能 | 策略 |
|
|
||||||
|---|---|
|
|
||||||
| Beta Headers | 不发送 |
|
|
||||||
| Server Tools (advisor) | 不发送 |
|
|
||||||
| Structured Output | 不发送 |
|
|
||||||
| Fast Mode / Effort | 不发送 |
|
|
||||||
| Tool Search / defer_loading | 不启用,所有工具直接发送 |
|
|
||||||
| Anthropic Signature | thinking block 的 `signature` 字段为空字符串 |
|
|
||||||
| cache_creation_input_tokens | 始终为 0(OpenAI 不区分创建/读取) |
|
|
||||||
|
|
||||||
## 测试
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 运行所有 OpenAI 适配层测试
|
|
||||||
bun test src/services/api/openai/__tests__/
|
|
||||||
|
|
||||||
# 单独运行
|
|
||||||
bun test src/services/api/openai/__tests__/streamAdapter.test.ts # 14 tests(含 thinking + caching)
|
|
||||||
bun test src/services/api/openai/__tests__/convertMessages.test.ts # 10 tests
|
|
||||||
bun test src/services/api/openai/__tests__/convertTools.test.ts # 7 tests
|
|
||||||
bun test src/services/api/openai/__tests__/modelMapping.test.ts # 6 tests
|
|
||||||
```
|
|
||||||
|
|
||||||
当前测试覆盖:**39 tests / 73 assertions / 0 fail**。
|
|
||||||
|
|
||||||
### 测试覆盖矩阵
|
|
||||||
|
|
||||||
| 功能 | convertMessages | convertTools | streamAdapter | modelMapping |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| 文本消息转换 | ✅ | | | |
|
|
||||||
| tool_use 转换 | ✅ | | | |
|
|
||||||
| tool_result 转换 | ✅ | | | |
|
|
||||||
| thinking 剥离 | ✅ | | | |
|
|
||||||
| 完整对话流程 | ✅ | | | |
|
|
||||||
| 工具 schema 转换 | | ✅ | | |
|
|
||||||
| tool_choice 映射 | | ✅ | | |
|
|
||||||
| 纯文本流 | | | ✅ | |
|
|
||||||
| 工具调用流 | | | ✅ | |
|
|
||||||
| 混合文本+工具 | | | ✅ | |
|
|
||||||
| finish_reason 映射 | | | ✅ | |
|
|
||||||
| thinking 流 | | | ✅ | |
|
|
||||||
| thinking+text 切换 | | | ✅ | |
|
|
||||||
| thinking+tool_use 切换 | | | ✅ | |
|
|
||||||
| 块索引正确性 | | | ✅ | |
|
|
||||||
| cached_tokens 映射 | | | ✅ | |
|
|
||||||
| OPENAI_MODEL 覆盖 | | | | ✅ |
|
|
||||||
| 默认模型映射 | | | | ✅ |
|
|
||||||
| 未知模型透传 | | | | ✅ |
|
|
||||||
| [1m] 后缀剥离 | | | | ✅ |
|
|
||||||
|
|
||||||
## 端到端验证
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. 安装依赖
|
|
||||||
bun install
|
|
||||||
|
|
||||||
# 2. 运行单元测试
|
|
||||||
bun test src/services/api/openai/__tests__/
|
|
||||||
|
|
||||||
# 3. 连接实际端点(以 Ollama 为例)
|
|
||||||
CLAUDE_CODE_USE_OPENAI=1 \
|
|
||||||
OPENAI_API_KEY=ollama \
|
|
||||||
OPENAI_BASE_URL=http://localhost:11434/v1 \
|
|
||||||
OPENAI_MODEL=qwen2.5-coder-32b \
|
|
||||||
bun run dev
|
|
||||||
|
|
||||||
# 4. 连接 DeepSeek(测试 thinking 支持)
|
|
||||||
CLAUDE_CODE_USE_OPENAI=1 \
|
|
||||||
OPENAI_API_KEY=sk-xxx \
|
|
||||||
OPENAI_BASE_URL=https://api.deepseek.com/v1 \
|
|
||||||
OPENAI_MODEL=deepseek-reasoner \
|
|
||||||
bun run dev
|
|
||||||
|
|
||||||
# 5. 确认现有测试不受影响
|
|
||||||
bun test # 无 CLAUDE_CODE_USE_OPENAI 时走原有路径
|
|
||||||
```
|
|
||||||
|
|
||||||
## 代码统计
|
|
||||||
|
|
||||||
| 类别 | 行数 |
|
|
||||||
|---|---|
|
|
||||||
| 新增源码 | ~620 行 |
|
|
||||||
| 新增测试 | ~450 行 |
|
|
||||||
| 改动现有代码 | ~25 行 |
|
|
||||||
| **总计** | **~1100 行** |
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# 社区项目 & Blog 合集
|
|
||||||
|
|
||||||
> 每日更新,欢迎自荐!
|
|
||||||
|
|
||||||
## 工具 & 应用
|
|
||||||
|
|
||||||
| 项目 | 描述 | 作者 |
|
|
||||||
|------|------|------|
|
|
||||||
| [4qtask.vercel.app](https://4qtask.vercel.app/) | 免费四象限时间管理工具 | @kevinhuky |
|
|
||||||
| [kaying.studio](https://kaying.studio/) | 个人 AI 工具箱 | @kayingai |
|
|
||||||
| [supsub.ai](https://supsub.ai/) | 高效阅读工具 | @hidumou |
|
|
||||||
| [x-video-download.net](https://x-video-download.net/) | 视频下载工具 | @syakadou |
|
|
||||||
| [1openapi.com](https://1openapi.com/) | API 中转站 | @thinker007 |
|
|
||||||
| [claw-z.com](https://claw-z.com/) | 一键部署 OpenClaw AI Agent(场景驱动、全面管理) | @uhhc |
|
|
||||||
| [gemini-watermark-remover.net](https://gemini-watermark-remover.net/) | Gemini 水印移除工具 | @syakadou |
|
|
||||||
|
|
||||||
## GitHub 开源项目
|
|
||||||
|
|
||||||
| 项目 | 描述 | 作者 |
|
|
||||||
|------|------|------|
|
|
||||||
| [VersperClaw](https://github.com/versperai/VersperClaw) | 全自动科研流 | @versperai |
|
|
||||||
| [claude-reviews-claude](https://github.com/openedclaude/claude-reviews-claude) | 原汤化原食——Claude 如何看待眼中的老己 | @openedclaude |
|
|
||||||
| [agentica](https://github.com/shibing624/agentica) | 自研 Agent 框架,借鉴 claude-code 多 Agent 处理 | @shibing624 |
|
|
||||||
| [macman](https://github.com/tonngw/macman) | Mac 从 0 到 1 保姆级配置教程 | @tonngw |
|
|
||||||
| [SuperSpec](https://github.com/asasugar/SuperSpec) | SDD / Spec-Driven Development | @asasugar |
|
|
||||||
| [adnify](https://github.com/adnaan-worker/adnify) | 高颜值高定制化 AI 编辑器 | @adnaan-worker |
|
|
||||||
| [another-rule-engine](https://github.com/eatmoreduck/another-rule-engine) | 基于 Groovy 的开源多功能决策引擎 | @eatmoreduck |
|
|
||||||
| [creative_master](https://github.com/chatabc/creative_master) | AI 驱动的创意灵感管理工具 | @chatabc |
|
|
||||||
| [RapidDoc](https://github.com/RapidAI/RapidDoc) | Office 文件解析工具转 Markdown(支持 PDF/Image/Word/PPT/Excel) | @hzkitt |
|
|
||||||
|
|
||||||
## Blog
|
|
||||||
|
|
||||||
| 链接 | 作者 |
|
|
||||||
|------|------|
|
|
||||||
| [blog.xiaohuangyu.space](https://blog.xiaohuangyu.space/) | @eatmoreduck |
|
|
||||||
@@ -137,7 +137,7 @@ Auto mode 可通过以下方式激活:
|
|||||||
|
|
||||||
### 进入时(Full Instructions)
|
### 进入时(Full Instructions)
|
||||||
|
|
||||||
注入到对话中的指令(`messages.ts:3464`):
|
注入到对话中的指令(`messages.ts:3481`):
|
||||||
|
|
||||||
> Auto mode is active. The user chose continuous, autonomous execution. You should:
|
> Auto mode is active. The user chose continuous, autonomous execution. You should:
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -18,17 +18,19 @@ keywords: ["权限模型", "Allow Ask Deny", "PermissionRule", "checkPermissions
|
|||||||
|
|
||||||
这些行为由 `PermissionResult` 类型定义(`src/utils/permissions/PermissionResult.ts`)。
|
这些行为由 `PermissionResult` 类型定义(`src/utils/permissions/PermissionResult.ts`)。
|
||||||
|
|
||||||
## 权限规则的五层来源
|
## 权限规则的来源
|
||||||
|
|
||||||
规则从 5 个来源汇聚(`PERMISSION_RULE_SOURCES`,`permissions.ts:109`),优先级从高到低:
|
规则从 8 个来源汇聚(`PERMISSION_RULE_SOURCES`,`permissions.ts:109`),优先级从低到高(后者覆盖前者):
|
||||||
|
|
||||||
```
|
```
|
||||||
1. session — 用户在当前对话中手动授权("Always allow")
|
1. userSettings — ~/.claude/settings.json(跨项目)
|
||||||
2. cliArg — 命令行 --allow/--deny 参数
|
2. projectSettings — .claude/settings.json(团队共享)
|
||||||
3. command — Skill 工具的 allowedTools 白名单
|
3. localSettings — .claude/settings.local.json(gitignored,个人覆盖)
|
||||||
4. projectSettings — .claude/settings.json(团队共享)
|
4. flagSettings — --settings 命令行参数
|
||||||
5. userSettings — ~/.claude/settings.json(跨项目)
|
5. policySettings — 企业管理员下发的策略(用户不可覆盖)
|
||||||
6. policySettings — 企业管理员下发的策略(用户不可覆盖)
|
6. cliArg — 命令行 --allow/--deny 参数
|
||||||
|
7. command — Skill 工具的 allowedTools 白名单
|
||||||
|
8. session — 用户在当前对话中手动授权("Always allow")
|
||||||
```
|
```
|
||||||
|
|
||||||
每个来源维护三个数组:`alwaysAllowRules[source]`、`alwaysAskRules[source]`、`alwaysDenyRules[source]`。
|
每个来源维护三个数组:`alwaysAllowRules[source]`、`alwaysAskRules[source]`、`alwaysDenyRules[source]`。
|
||||||
@@ -65,7 +67,7 @@ MCP 工具使用 `getToolNameForPermissionCheck()` 获取匹配名称,支持
|
|||||||
|
|
||||||
**2. 命令模式匹配**(BashTool 的 `checkPermissions()`)
|
**2. 命令模式匹配**(BashTool 的 `checkPermissions()`)
|
||||||
|
|
||||||
BashTool 通过 `preparePermissionMatcher()`(`Tool.ts:514`)解析命令模式:
|
BashTool 通过 `preparePermissionMatcher()`(`Tool.ts:520`)解析命令模式:
|
||||||
```json
|
```json
|
||||||
{"tool": "Bash", "ruleContent": "git *"} → 匹配 "git commit -m 'fix'"
|
{"tool": "Bash", "ruleContent": "git *"} → 匹配 "git commit -m 'fix'"
|
||||||
```
|
```
|
||||||
@@ -120,7 +122,9 @@ Read/Edit/Write 工具通过 `getPath()` 提取文件路径,与 `ruleContent`
|
|||||||
|------|---------------------|---------|------|
|
|------|---------------------|---------|------|
|
||||||
| **Default** | `'default'` | 日常使用 | 敏感操作逐一确认 |
|
| **Default** | `'default'` | 日常使用 | 敏感操作逐一确认 |
|
||||||
| **Plan Mode** | `'plan'` | 探索阶段 | 只能读不能写(`isReadOnly()` 检查) |
|
| **Plan Mode** | `'plan'` | 探索阶段 | 只能读不能写(`isReadOnly()` 检查) |
|
||||||
| **Auto** | `'auto'` | 信任 AI | 通过 transcript classifier 自动决策 |
|
| **Accept Edits** | `'acceptEdits'` | 快速迭代 | 工作区内文件编辑自动放行,其他操作仍需确认 |
|
||||||
|
| **Don't Ask** | `'dontAsk'` | 减少打断 | 尽量自动决策,减少确认弹窗 |
|
||||||
|
| **Auto** | `'auto'` | 信任 AI | 通过 transcript classifier 自动决策(需 `TRANSCRIPT_CLASSIFIER` feature flag) |
|
||||||
| **Bypass** | `'bypassPermissions'` | 完全信任 | 所有操作自动放行(需显式 `--dangerously-skip-permissions`) |
|
| **Bypass** | `'bypassPermissions'` | 完全信任 | 所有操作自动放行(需显式 `--dangerously-skip-permissions`) |
|
||||||
|
|
||||||
Plan Mode 切换由 `EnterPlanModeTool.call()` 触发:
|
Plan Mode 切换由 `EnterPlanModeTool.call()` 触发:
|
||||||
@@ -143,8 +147,8 @@ context.setAppState(prev => ({
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const DENIAL_LIMITS = {
|
const DENIAL_LIMITS = {
|
||||||
maxDenialsPerTool: 3, // 同一工具连续拒绝上限
|
maxConsecutive: 3, // 同一工具连续拒绝上限
|
||||||
cooldownPeriodMs: 30_000, // 冷却期 30 秒
|
maxTotal: 20, // 总拒绝上限
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -162,9 +166,12 @@ const DENIAL_LIMITS = {
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
type PermissionUpdate =
|
type PermissionUpdate =
|
||||||
| { type: 'addRule', behavior, rule, destination }
|
| { type: 'addRules', destination, rules, behavior }
|
||||||
| { type: 'removeRule', behavior, rule, destination }
|
| { type: 'replaceRules', destination, rules, behavior }
|
||||||
| { type: 'setMode', mode, destination }
|
| { type: 'removeRules', destination, rules, behavior }
|
||||||
|
| { type: 'setMode', destination, mode }
|
||||||
|
| { type: 'addDirectories', destination, directories }
|
||||||
|
| { type: 'removeDirectories', destination, directories }
|
||||||
```
|
```
|
||||||
|
|
||||||
当用户在 Ask 对话框中选择 "Always allow",系统调用 `persistPermissionUpdates()` 将规则写入对应层级的 settings 文件(project/user/managed),同时更新内存中的 `toolPermissionContext`。
|
当用户在 Ask 对话框中选择 "Always allow",系统调用 `persistPermissionUpdates()` 将规则写入对应层级的 settings 文件(project/user/managed),同时更新内存中的 `toolPermissionContext`。
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ keywords: ["Plan Mode", "计划模式", "EnterPlanMode", "ExitPlanMode", "prepar
|
|||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="EnterPlanMode — 进入计划模式">
|
<Step title="EnterPlanMode — 进入计划模式">
|
||||||
AI 自主判断(或用户触发)任务需要规划,调用 `EnterPlanModeTool`(`src/tools/EnterPlanModeTool/EnterPlanModeTool.ts:36`)。该工具需要**用户审批**(`checkPermissions` 返回 `ask`)。
|
AI 自主判断(或用户触发)任务需要规划,调用 `EnterPlanModeTool`(`packages/builtin-tools/src/tools/EnterPlanModeTool/EnterPlanModeTool.ts:36`)。该工具需要**用户审批**(`checkPermissions` 返回 `ask`)。
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="探索阶段 — 只读工具集">
|
<Step title="探索阶段 — 只读工具集">
|
||||||
权限模式切换为 `'plan'`,AI 只能使用 `isReadOnly()` 为 true 的工具(Read、Grep、Glob、Agent 等)。写操作被自动拒绝。
|
权限模式切换为 `'plan'`,AI 只能使用 `isReadOnly()` 为 true 的工具(Read、Grep、Glob、Agent 等)。写操作被自动拒绝。
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="ExitPlanMode — 提交方案审批">
|
<Step title="ExitPlanMode — 提交方案审批">
|
||||||
AI 完成探索后,调用 `ExitPlanModeV2Tool`(`src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts:147`),将计划文件提交给用户审阅。这是第二个**需要用户审批**的节点。
|
AI 完成探索后,调用 `ExitPlanModeV2Tool`(`packages/builtin-tools/src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts:147`),将计划文件提交给用户审阅。这是第二个**需要用户审批**的节点。
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="恢复执行 — 全部工具权限">
|
<Step title="恢复执行 — 全部工具权限">
|
||||||
用户批准后,权限模式恢复为进入前的状态,AI 按计划执行。
|
用户批准后,权限模式恢复为进入前的状态,AI 按计划执行。
|
||||||
@@ -107,7 +107,7 @@ if (isTeammate()) {
|
|||||||
|
|
||||||
## 什么时候该用计划模式
|
## 什么时候该用计划模式
|
||||||
|
|
||||||
`EnterPlanModeTool` 的 Prompt(`src/tools/EnterPlanModeTool/prompt.ts`)定义了两套触发标准——外部版本更积极(鼓励规划),内部版本更克制(仅在真正模糊时使用):
|
`EnterPlanModeTool` 的 Prompt(`packages/builtin-tools/src/tools/EnterPlanModeTool/prompt.ts`)定义了两套触发标准——外部版本更积极(鼓励规划),内部版本更克制(仅在真正模糊时使用):
|
||||||
|
|
||||||
| 场景 | 外部版本 | 内部版本 |
|
| 场景 | 外部版本 | 内部版本 |
|
||||||
|------|---------|---------|
|
|------|---------|---------|
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ keywords: ["沙箱", "sandbox", "权限", "Bash", "PowerShell", "bubblewrap", "s
|
|||||||
5. 这条命令没有被显式排除
|
5. 这条命令没有被显式排除
|
||||||
6. 这次调用没有被允许以 `dangerouslyDisableSandbox` 绕过
|
6. 这次调用没有被允许以 `dangerouslyDisableSandbox` 绕过
|
||||||
|
|
||||||
对应入口在 `src/tools/BashTool/shouldUseSandbox.ts` 和 `src/utils/sandbox/sandbox-adapter.ts`。
|
对应入口在 `packages/builtin-tools/src/tools/BashTool/shouldUseSandbox.ts` 和 `src/utils/sandbox/sandbox-adapter.ts`。
|
||||||
|
|
||||||
### 3. PowerShell 只在支持平台上走
|
### 3. PowerShell 只在支持平台上走
|
||||||
|
|
||||||
@@ -518,11 +518,11 @@ REPL / CLI 启动
|
|||||||
|
|
||||||
如果你想继续顺着源码深入,推荐按下面顺序看:
|
如果你想继续顺着源码深入,推荐按下面顺序看:
|
||||||
|
|
||||||
1. `src/tools/BashTool/shouldUseSandbox.ts`
|
1. `packages/builtin-tools/src/tools/BashTool/shouldUseSandbox.ts`
|
||||||
2. `src/utils/Shell.ts`
|
2. `src/utils/Shell.ts`
|
||||||
3. `src/utils/sandbox/sandbox-adapter.ts`
|
3. `src/utils/sandbox/sandbox-adapter.ts`
|
||||||
4. `src/utils/permissions/permissions.ts`
|
4. `src/utils/permissions/permissions.ts`
|
||||||
5. `src/tools/BashTool/bashPermissions.ts`
|
5. `packages/builtin-tools/src/tools/BashTool/bashPermissions.ts`
|
||||||
6. `src/utils/permissions/pathValidation.ts`
|
6. `src/utils/permissions/pathValidation.ts`
|
||||||
7. `src/utils/permissions/filesystem.ts`
|
7. `src/utils/permissions/filesystem.ts`
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ Claude 的 System Prompt 中包含安全指令——这是"软性"约束,依
|
|||||||
| `deny` | 直接拒绝 | 匹配 deny 规则 |
|
| `deny` | 直接拒绝 | 匹配 deny 规则 |
|
||||||
| `ask` | 弹窗确认 | 未匹配任何规则 或 匹配 ask 规则 |
|
| `ask` | 弹窗确认 | 未匹配任何规则 或 匹配 ask 规则 |
|
||||||
|
|
||||||
以 BashTool 为例(`src/tools/BashTool/bashPermissions.ts`),`bashToolHasPermission()` 执行了极其细致的检查链:
|
以 BashTool 为例(`packages/builtin-tools/src/tools/BashTool/bashPermissions.ts`),`bashToolHasPermission()` 执行了极其细致的检查链:
|
||||||
|
|
||||||
1. **AST 安全解析**:用 tree-sitter 解析 bash AST,检测命令注入(`$()`、反引号等)
|
1. **AST 安全解析**:用 tree-sitter 解析 bash AST,检测命令注入(`$()`、反引号等)
|
||||||
2. **语义检查**:识别危险命令(`eval`、`exec`、`source` 等)
|
2. **语义检查**:识别危险命令(`eval`、`exec`、`source` 等)
|
||||||
@@ -169,7 +169,7 @@ Bash("rm -rf node_modules") → ⚠️ 需确认(不可逆)
|
|||||||
攻击:cd /malicious/dir && git status
|
攻击:cd /malicious/dir && git status
|
||||||
/malicious/dir 包含 bare repo + 恶意钩子
|
/malicious/dir 包含 bare repo + 恶意钩子
|
||||||
防御:bashToolHasPermission() 检测 cd + git 组合
|
防御:bashToolHasPermission() 检测 cd + git 组合
|
||||||
强制 require approval(src/tools/BashTool/bashPermissions.ts:2209)
|
强制 require approval(packages/builtin-tools/src/tools/BashTool/bashPermissions.ts:2209)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 场景3:管道注入
|
### 场景3:管道注入
|
||||||
@@ -178,5 +178,5 @@ Bash("rm -rf node_modules") → ⚠️ 需确认(不可逆)
|
|||||||
攻击:echo 'x' | xargs printf '%s' >> /etc/passwd
|
攻击:echo 'x' | xargs printf '%s' >> /etc/passwd
|
||||||
splitCommand 会剥离重定向,导致路径检查遗漏
|
splitCommand 会剥离重定向,导致路径检查遗漏
|
||||||
防御:即使管道段独立检查通过,仍对原始命令重新验证路径约束
|
防御:即使管道段独立检查通过,仍对原始命令重新验证路径约束
|
||||||
检查重定向目标中的危险模式(反引号、$())(bashPermissions.ts:1992-2056)
|
检查重定向目标中的危险模式(反引号、$())(packages/builtin-tools/src/tools/BashTool/bashPermissions.ts:1992-2056)
|
||||||
```
|
```
|
||||||
|
|||||||
77
docs/task/task-001-daemon-status-stop.md
Normal file
77
docs/task/task-001-daemon-status-stop.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# Task 001: daemon status / stop
|
||||||
|
|
||||||
|
> 来源: [stub-recovery-design-1-4.md](../features/stub-recovery-design-1-4.md) 第 1 项
|
||||||
|
> 优先级: P0 (首选实现项)
|
||||||
|
> 工作量: 小
|
||||||
|
> 状态: DONE
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
让 `claude daemon status` 和 `claude daemon stop` 在任意 CLI 进程中都能正确工作,不依赖 TUI 内存态。
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
- `start` 路径已有完整 supervisor + worker 生命周期 (`src/daemon/main.ts`, `src/daemon/workerRegistry.ts`)
|
||||||
|
- `status` / `stop` 目前只是占位输出 (`src/daemon/main.ts:49`)
|
||||||
|
- `/remote-control-server` 有自己的命令内 UI 状态,但只维护当前进程内的 `daemonProcess`,不适合跨进程管理
|
||||||
|
|
||||||
|
## 实现方案
|
||||||
|
|
||||||
|
### 新增文件
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `src/daemon/state.ts` | daemon 状态文件读写模块 |
|
||||||
|
|
||||||
|
### 修改文件
|
||||||
|
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `src/daemon/main.ts` | `start` 写入状态文件;`status`/`stop` 调用 state 模块 |
|
||||||
|
| `src/commands/remoteControlServer/remoteControlServer.tsx` | 读取同一份状态文件(轻量改动) |
|
||||||
|
|
||||||
|
### 状态文件
|
||||||
|
|
||||||
|
路径: `~/.claude/daemon/remote-control.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pid": 12345,
|
||||||
|
"cwd": "/path/to/project",
|
||||||
|
"startedAt": "2026-04-12T10:00:00Z",
|
||||||
|
"workerKinds": ["bridge", "rcs"],
|
||||||
|
"lastStatus": "running"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### status 逻辑
|
||||||
|
|
||||||
|
1. 读取状态文件
|
||||||
|
2. 用进程探测验证 pid 是否存活
|
||||||
|
3. 输出 `running` / `stopped` / `stale`
|
||||||
|
4. stale 时自动清理状态文件
|
||||||
|
|
||||||
|
### stop 逻辑
|
||||||
|
|
||||||
|
1. 读取 pid
|
||||||
|
2. 发送 `SIGTERM`
|
||||||
|
3. 等待退出(超时兜底)
|
||||||
|
4. 超时后 `SIGKILL`
|
||||||
|
5. 清理状态文件
|
||||||
|
|
||||||
|
## 验证步骤
|
||||||
|
|
||||||
|
- [ ] `claude daemon start` 正常启动并写入状态文件
|
||||||
|
- [ ] 新开终端执行 `claude daemon status`,显示 `running`
|
||||||
|
- [ ] 执行 `claude daemon stop`,daemon 正常退出
|
||||||
|
- [ ] 再次执行 `claude daemon status`,返回 `stopped` 或 `stale cleaned`
|
||||||
|
- [ ] Windows 下 stop 超时兜底正常工作
|
||||||
|
|
||||||
|
## 风险
|
||||||
|
|
||||||
|
- Windows 信号模型和 Unix 不同,`stop` 需要超时兜底
|
||||||
|
- 当前设计默认单 supervisor,不处理多实例并发
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
无外部依赖,可独立实施。
|
||||||
80
docs/task/task-002-bg-sessions-ps-logs-kill.md
Normal file
80
docs/task/task-002-bg-sessions-ps-logs-kill.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Task 002: BG_SESSIONS — ps / logs / kill
|
||||||
|
|
||||||
|
> 来源: [stub-recovery-design-1-4.md](../features/stub-recovery-design-1-4.md) 第 2 项
|
||||||
|
> 优先级: P1
|
||||||
|
> 工作量: 中等
|
||||||
|
> 状态: DONE
|
||||||
|
> 阶段: Phase 2A (MVP)
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
把 `ps` / `logs` / `kill` 做成真正有用的 session 管理命令。不在第一阶段补完 `attach` / `--bg`。
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
- fast-path 已接好 (`src/entrypoints/cli.tsx:218`)
|
||||||
|
- session registry 已有真实实现 (`src/utils/concurrentSessions.ts`)
|
||||||
|
- `exit` 在 bg session 内已会 `tmux detach-client` (`src/commands/exit/exit.tsx:20`)
|
||||||
|
- CLI handler 仍全空 (`src/cli/bg.ts`)
|
||||||
|
- task summary 仍然是 stub (`src/utils/taskSummary.ts`)
|
||||||
|
|
||||||
|
## 实现方案
|
||||||
|
|
||||||
|
### 修改文件
|
||||||
|
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `src/cli/bg.ts` | 实现 `ps` / `logs` / `kill` handler |
|
||||||
|
| `src/utils/concurrentSessions.ts` | 扩展以便后续 attach/--bg 使用 |
|
||||||
|
| `src/utils/taskSummary.ts` | 补充基础实现 |
|
||||||
|
|
||||||
|
### 复用模块
|
||||||
|
|
||||||
|
- `src/utils/sessionStorage.ts` — session 存储
|
||||||
|
- `src/utils/udsClient.ts` — UDS 通信
|
||||||
|
|
||||||
|
### ps 命令
|
||||||
|
|
||||||
|
- 从 registry 读取 live sessions
|
||||||
|
- 展示: pid, kind, sessionId, cwd, name, startedAt, bridgeSessionId
|
||||||
|
- 如果有 activity/status,一并展示
|
||||||
|
|
||||||
|
### logs 命令
|
||||||
|
|
||||||
|
- 支持按 `sessionId` / `pid` / `name` 查找
|
||||||
|
- 优先复用本地 transcript/log 读取能力
|
||||||
|
- 如果 registry 里存在 `logPath`,支持 tail 文件
|
||||||
|
|
||||||
|
### kill 命令
|
||||||
|
|
||||||
|
- 解析目标 session
|
||||||
|
- 发退出信号
|
||||||
|
- 清理 stale registry
|
||||||
|
|
||||||
|
## 验证步骤
|
||||||
|
|
||||||
|
- [ ] `ps` 能列出当前 live sessions
|
||||||
|
- [ ] `logs <sessionId|pid|name>` 能输出对应日志
|
||||||
|
- [ ] `kill <sessionId|pid|name>` 能结束目标 session 并清理 registry
|
||||||
|
- [ ] 无 live session 时各命令有明确提示
|
||||||
|
|
||||||
|
## Phase 2B (后续)
|
||||||
|
|
||||||
|
- [ ] 实现 `attach`
|
||||||
|
- [ ] 实现 `--bg`
|
||||||
|
- [ ] 实现 `taskSummary` 的中途状态更新
|
||||||
|
|
||||||
|
### 为什么拆分
|
||||||
|
|
||||||
|
- 现有 registry 记录了 `pid / sessionId / name / logPath`
|
||||||
|
- 但没有可靠的 tmux attach target
|
||||||
|
- `attach` 和 `--bg` 需要补启动/附着元数据设计,不是简单补 handler
|
||||||
|
|
||||||
|
## 风险
|
||||||
|
|
||||||
|
- `attach` / `--bg` 第二阶段需要 tmux 元数据设计
|
||||||
|
- Windows 下 tmux 路径需要明确降级策略
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
- Task 001 (daemon 状态管理可复用模式,但非硬性依赖)
|
||||||
87
docs/task/task-003-templates-job-mvp.md
Normal file
87
docs/task/task-003-templates-job-mvp.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# Task 003: TEMPLATES — job 文件系统 MVP
|
||||||
|
|
||||||
|
> 来源: [stub-recovery-design-1-4.md](../features/stub-recovery-design-1-4.md) 第 3 项
|
||||||
|
> 优先级: P2
|
||||||
|
> 工作量: 中等
|
||||||
|
> 状态: DONE
|
||||||
|
> 阶段: MVP
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
把 `new` / `list` / `reply` 做成可用的模板任务系统。第一阶段不碰复杂的自动分类与自动执行。
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
- 命令入口只有 fast-path (`src/entrypoints/cli.tsx:272`)
|
||||||
|
- handler 是空的 (`src/cli/handlers/templateJobs.ts`)
|
||||||
|
- `markdownConfigLoader` 已把 `templates` 纳入配置目录 (`src/utils/markdownConfigLoader.ts:35`)
|
||||||
|
- `query/stopHooks` 已预留 job classifier 链路 (`src/query/stopHooks.ts:103`)
|
||||||
|
- `jobs/classifier.ts` 仍是 stub (`src/jobs/classifier.ts`)
|
||||||
|
|
||||||
|
## 实现方案
|
||||||
|
|
||||||
|
### 新增文件
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `src/jobs/state.ts` | job 状态管理 |
|
||||||
|
| `src/jobs/templates.ts` | 模板解析与列表 |
|
||||||
|
|
||||||
|
### 修改文件
|
||||||
|
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `src/cli/handlers/templateJobs.ts` | 实现 `new` / `list` / `reply` handler |
|
||||||
|
|
||||||
|
### 模板来源
|
||||||
|
|
||||||
|
`.claude/templates/*.md`
|
||||||
|
|
||||||
|
### 模板格式
|
||||||
|
|
||||||
|
复用现有 markdown + frontmatter 解析,不另外设计 DSL。
|
||||||
|
|
||||||
|
### list 命令
|
||||||
|
|
||||||
|
- 列出所有模板
|
||||||
|
- 显示: 模板名, description, 路径
|
||||||
|
|
||||||
|
### new 命令
|
||||||
|
|
||||||
|
- 解析模板
|
||||||
|
- 在 `~/.claude/jobs/<job-id>/` 下创建 job 目录
|
||||||
|
- 写入 `template.md`, `input.txt`, `state.json`
|
||||||
|
- 返回 job id 与目录路径
|
||||||
|
|
||||||
|
### reply 命令
|
||||||
|
|
||||||
|
- 将回复写入 `replies.jsonl` 或 `input.txt`
|
||||||
|
- 更新 `state.json`
|
||||||
|
|
||||||
|
## 验证步骤
|
||||||
|
|
||||||
|
- [ ] `list` 能列出 `.claude/templates` 下的所有模板
|
||||||
|
- [ ] `new <template> [args...]` 能创建 job 目录和状态文件
|
||||||
|
- [ ] `reply <job-id> <text>` 能更新 job 内容和状态
|
||||||
|
- [ ] frontmatter schema 最小字段集已定义
|
||||||
|
|
||||||
|
## Phase 2 (后续)
|
||||||
|
|
||||||
|
- [ ] 恢复 `src/jobs/classifier.ts`
|
||||||
|
- [ ] 让带 `CLAUDE_JOB_DIR` 的 job session 在 turn 完成后自动更新 `state.json`
|
||||||
|
- [ ] 再决定是否补自动 job runner
|
||||||
|
|
||||||
|
### 为什么拆分
|
||||||
|
|
||||||
|
- 当前是 "template job commands",不是单纯模板列表
|
||||||
|
- 自动 job 运行链路没有足够现成实现
|
||||||
|
- 先做文件系统 job lifecycle 更稳
|
||||||
|
|
||||||
|
## 风险
|
||||||
|
|
||||||
|
- frontmatter schema 需要先定义最小字段集
|
||||||
|
- 一旦扩展到"自动运行 job",范围会明显膨胀
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
无硬性依赖,可独立实施。
|
||||||
103
docs/task/task-004-assistant-session-attach.md
Normal file
103
docs/task/task-004-assistant-session-attach.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# Task 004: assistant [sessionId] — 分阶段恢复
|
||||||
|
|
||||||
|
> 来源: [stub-recovery-design-1-4.md](../features/stub-recovery-design-1-4.md) 第 4 项
|
||||||
|
> 优先级: P3
|
||||||
|
> 工作量: Phase 4A 中等,4A-4D 全做完很大
|
||||||
|
> 状态: Phase 4A DONE, 4B-4D TODO
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
不一次性恢复整个 KAIROS 助手系统。先做"明确 sessionId 的 viewer attach 可用",再逐步补 discovery / chooser / install。
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
- attach 主流程已存在 (`src/main.tsx:4708`)
|
||||||
|
- 远端 viewer 所需基础模块已存在:
|
||||||
|
- `src/remote/RemoteSessionManager.ts`
|
||||||
|
- `src/hooks/useAssistantHistory.ts`
|
||||||
|
- `src/assistant/sessionHistory.ts`
|
||||||
|
- 真正 stub 的主要是:
|
||||||
|
- `src/assistant/sessionDiscovery.ts`
|
||||||
|
- `src/assistant/AssistantSessionChooser.tsx`
|
||||||
|
- `src/commands/assistant/assistant.tsx:7`
|
||||||
|
- `src/assistant/index.ts`
|
||||||
|
|
||||||
|
## 分阶段实现
|
||||||
|
|
||||||
|
### Phase 4A: MVP — 显式 sessionId attach
|
||||||
|
|
||||||
|
**修改文件:**
|
||||||
|
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `src/main.tsx` | 确保 attach 分支可用 |
|
||||||
|
| `src/commands/assistant/index.ts` | 实现显式 sessionId 参数入口 |
|
||||||
|
|
||||||
|
**行为:**
|
||||||
|
- `claude assistant <sessionId>` — 进入 remote viewer
|
||||||
|
- `claude assistant` (无参数) — 返回明确提示: 当前版本需要显式 sessionId,discovery 尚未启用
|
||||||
|
|
||||||
|
**验证:**
|
||||||
|
- [ ] `claude assistant <sessionId>` 能进入 remote viewer
|
||||||
|
- [ ] 历史懒加载工作正常
|
||||||
|
- [ ] 无参数模式给出明确提示
|
||||||
|
|
||||||
|
### Phase 4B: session discovery
|
||||||
|
|
||||||
|
**修改文件:**
|
||||||
|
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `src/assistant/sessionDiscovery.ts` | 恢复 `discoverAssistantSessions()` |
|
||||||
|
|
||||||
|
**行为:**
|
||||||
|
- 数据来源优先复用现有 sessions / bridge / teleport API,不新增协议
|
||||||
|
- `claude assistant` 无参数时能拿到候选 session 列表
|
||||||
|
|
||||||
|
**验证:**
|
||||||
|
- [ ] 无参数调用能列出可用 sessions
|
||||||
|
- [ ] 数据来源复用现有通道
|
||||||
|
|
||||||
|
### Phase 4C: session chooser
|
||||||
|
|
||||||
|
**修改文件:**
|
||||||
|
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `src/assistant/AssistantSessionChooser.ts` | 恢复交互式选择器 |
|
||||||
|
|
||||||
|
**行为:**
|
||||||
|
- 多 session 时可交互选择
|
||||||
|
|
||||||
|
**验证:**
|
||||||
|
- [ ] 多个 session 时弹出选择器
|
||||||
|
- [ ] 选择后正确 attach
|
||||||
|
|
||||||
|
### Phase 4D: install wizard
|
||||||
|
|
||||||
|
**修改文件:**
|
||||||
|
|
||||||
|
| 文件 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `src/commands/assistant/assistant.ts` | 恢复 install wizard 辅助函数 |
|
||||||
|
|
||||||
|
**行为:**
|
||||||
|
- 没有 session 时如何引导用户
|
||||||
|
|
||||||
|
**验证:**
|
||||||
|
- [ ] 无可用 session 时引导用户创建/连接
|
||||||
|
|
||||||
|
## 为什么拆分
|
||||||
|
|
||||||
|
- attach 渲染层与远端消息通道大部分已在
|
||||||
|
- 真正缺的是"如何发现目标 session"和"如何交互选择"
|
||||||
|
- 如果把 `src/assistant/index.ts` 的整套 KAIROS 正常模式也一起拉进来,范围会失控
|
||||||
|
|
||||||
|
## 风险
|
||||||
|
|
||||||
|
- 这是四项里范围最大的
|
||||||
|
- 一旦把 KAIROS 正常模式整体拉入,会从"viewer attach"膨胀成"完整 assistant mode 恢复"
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
- Task 002 的 session registry 模式可复用
|
||||||
196
docs/task/task-013-bg-engine-abstraction.md
Normal file
196
docs/task/task-013-bg-engine-abstraction.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
# Task 013: BgEngine 跨平台后台引擎抽象
|
||||||
|
|
||||||
|
> 设计文档: [daemon-restructure-design.md](../features/daemon-restructure-design.md) § 四
|
||||||
|
> 依赖: 无
|
||||||
|
> 分支: `feat/integrate-5-branches`
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
将 `src/cli/bg.ts` 中硬编码的 tmux 逻辑提取为引擎抽象层,实现 TmuxEngine + DetachedEngine,使后台会话功能在 Windows / macOS / Linux 上都能工作。
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
当前 `bg.ts` 中 `handleBgFlag()` 和 `attachHandler()` 直接调用 tmux 命令。Windows 上 `--bg` 直接报错退出。需要一个引擎抽象层,根据平台和可用工具自动选择最佳方案。
|
||||||
|
|
||||||
|
## 文件清单
|
||||||
|
|
||||||
|
### 新增
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `src/cli/bg/engine.ts` | BgEngine 接口 + BgStartOptions/BgStartResult 类型 |
|
||||||
|
| `src/cli/bg/engines/tmux.ts` | TmuxEngine: 从 `bg.ts` 提取 tmux 相关逻辑 |
|
||||||
|
| `src/cli/bg/engines/detached.ts` | DetachedEngine: spawn({ detached }) + logFile 重定向 |
|
||||||
|
| `src/cli/bg/engines/index.ts` | selectEngine() 自动选择 + re-export |
|
||||||
|
| `src/cli/bg/tail.ts` | 跨平台日志 tail: fs.watch + 轮询 fallback |
|
||||||
|
|
||||||
|
### 修改
|
||||||
|
|
||||||
|
| 文件 | 变更 |
|
||||||
|
|------|------|
|
||||||
|
| `src/cli/bg.ts` | `handleBgFlag()` 改为调用 `selectEngine().start()`;`attachHandler()` 改为调用 `engine.attach()` |
|
||||||
|
|
||||||
|
## 实现方案
|
||||||
|
|
||||||
|
### 1. BgEngine 接口 (`src/cli/bg/engine.ts`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export interface BgEngine {
|
||||||
|
readonly name: string
|
||||||
|
available(): Promise<boolean>
|
||||||
|
start(opts: BgStartOptions): Promise<BgStartResult>
|
||||||
|
attach(session: SessionEntry): Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BgStartOptions {
|
||||||
|
sessionName: string
|
||||||
|
args: string[] // CLI args (去除 --bg)
|
||||||
|
env: Record<string, string | undefined>
|
||||||
|
logPath: string
|
||||||
|
cwd: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BgStartResult {
|
||||||
|
pid: number
|
||||||
|
sessionName: string
|
||||||
|
logPath: string
|
||||||
|
engineUsed: 'tmux' | 'detached'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. TmuxEngine (`src/cli/bg/engines/tmux.ts`)
|
||||||
|
|
||||||
|
从 `bg.ts:handleBgFlag()` 和 `bg.ts:attachHandler()` 提取:
|
||||||
|
- `available()`: `execFileNoThrow('tmux', ['-V'])` 返回 code === 0
|
||||||
|
- `start()`: `tmux new-session -d -s <name> <cmd>`
|
||||||
|
- `attach()`: `tmux attach-session -t <session.tmuxSessionName>`
|
||||||
|
|
||||||
|
### 3. DetachedEngine (`src/cli/bg/engines/detached.ts`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export class DetachedEngine implements BgEngine {
|
||||||
|
readonly name = 'detached'
|
||||||
|
|
||||||
|
async available(): Promise<boolean> {
|
||||||
|
return true // 总是可用
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(opts: BgStartOptions): Promise<BgStartResult> {
|
||||||
|
const logFd = openSync(opts.logPath, 'a')
|
||||||
|
const child = spawn(process.execPath, [process.argv[1]!, ...opts.args], {
|
||||||
|
detached: true,
|
||||||
|
stdio: ['ignore', logFd, logFd],
|
||||||
|
env: opts.env,
|
||||||
|
cwd: opts.cwd,
|
||||||
|
})
|
||||||
|
child.unref()
|
||||||
|
closeSync(logFd)
|
||||||
|
|
||||||
|
return {
|
||||||
|
pid: child.pid!,
|
||||||
|
sessionName: opts.sessionName,
|
||||||
|
logPath: opts.logPath,
|
||||||
|
engineUsed: 'detached',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async attach(session: SessionEntry): Promise<void> {
|
||||||
|
// 委托给 tail.ts
|
||||||
|
await tailLog(session.logPath!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 日志 Tail (`src/cli/bg/tail.ts`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* 跨平台实时日志输出。Ctrl+C 退出,不杀后台进程。
|
||||||
|
*
|
||||||
|
* 策略:
|
||||||
|
* 1. 读取已有内容输出
|
||||||
|
* 2. fs.watch() 监听文件变化 (主方案)
|
||||||
|
* 3. 如果 fs.watch 不可靠 (某些 Windows 网络驱动器),fallback 到 500ms 轮询
|
||||||
|
*/
|
||||||
|
export async function tailLog(logPath: string): Promise<void>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 引擎选择 (`src/cli/bg/engines/index.ts`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export async function selectEngine(): Promise<BgEngine> {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
return new DetachedEngine()
|
||||||
|
}
|
||||||
|
const tmux = new TmuxEngine()
|
||||||
|
if (await tmux.available()) {
|
||||||
|
return tmux
|
||||||
|
}
|
||||||
|
return new DetachedEngine()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. bg.ts 重构
|
||||||
|
|
||||||
|
`handleBgFlag()` 改名为 `handleBgStart()`,内部逻辑:
|
||||||
|
```typescript
|
||||||
|
export async function handleBgStart(args: string[]): Promise<void> {
|
||||||
|
const engine = await selectEngine()
|
||||||
|
const sessionName = `claude-bg-${randomUUID().slice(0, 8)}`
|
||||||
|
const logPath = join(getClaudeConfigHomeDir(), 'sessions', 'logs', `${sessionName}.log`)
|
||||||
|
|
||||||
|
const result = await engine.start({
|
||||||
|
sessionName,
|
||||||
|
args: filteredArgs,
|
||||||
|
env: { ...process.env, CLAUDE_CODE_SESSION_KIND: 'bg', ... },
|
||||||
|
logPath,
|
||||||
|
cwd: process.cwd(),
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(`Background session started: ${result.sessionName}`)
|
||||||
|
console.log(` Engine: ${result.engineUsed}`)
|
||||||
|
console.log(` Log: ${result.logPath}`)
|
||||||
|
console.log(` Use \`claude daemon attach ${result.sessionName}\` to reconnect.`)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`attachHandler()` 根据 `session.engine` 字段选择引擎:
|
||||||
|
```typescript
|
||||||
|
export async function attachHandler(target: string | undefined): Promise<void> {
|
||||||
|
// ... 找到 session
|
||||||
|
if (session.engine === 'tmux' && session.tmuxSessionName) {
|
||||||
|
const tmux = new TmuxEngine()
|
||||||
|
await tmux.attach(session)
|
||||||
|
} else {
|
||||||
|
const detached = new DetachedEngine()
|
||||||
|
await detached.attach(session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## SessionEntry 扩展
|
||||||
|
|
||||||
|
`sessions/<PID>.json` 新增 `engine` 字段:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pid": 12345,
|
||||||
|
"engine": "detached",
|
||||||
|
"logPath": "~/.claude/sessions/logs/claude-bg-a1b2c3d4.log",
|
||||||
|
"sessionId": "...",
|
||||||
|
"cwd": "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
兼容旧格式: 如果 `engine` 字段缺失,检查 `tmuxSessionName` 存在则为 `tmux`,否则为 `detached`。
|
||||||
|
|
||||||
|
## 验证清单
|
||||||
|
|
||||||
|
- [ ] Windows: `claude daemon bg` 启动后台会话,无 tmux 依赖
|
||||||
|
- [ ] Windows: `claude daemon attach <name>` 以 tail 模式附着,Ctrl+C 退出不杀进程
|
||||||
|
- [ ] macOS/Linux (有 tmux): 行为与当前一致
|
||||||
|
- [ ] macOS/Linux (无 tmux): 自动 fallback 到 detached 引擎
|
||||||
|
- [ ] `claude daemon status` 正确显示 engine 类型
|
||||||
|
- [ ] 旧格式 session JSON (无 engine 字段) 兼容
|
||||||
|
- [ ] tsc --noEmit 零错误
|
||||||
|
- [ ] bun test 通过
|
||||||
275
docs/task/task-014-daemon-command-hierarchy.md
Normal file
275
docs/task/task-014-daemon-command-hierarchy.md
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
# Task 014: /daemon 命令层级化
|
||||||
|
|
||||||
|
> 设计文档: [daemon-restructure-design.md](../features/daemon-restructure-design.md) § 三.1
|
||||||
|
> 依赖: Task 013 (BgEngine 抽象)
|
||||||
|
> 分支: `feat/integrate-5-branches`
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
将散落的 `daemon start/stop/status` + `ps/logs/attach/kill` + `--bg` 统一收归 `/daemon` 命名空间,实现 CLI + REPL 双注册。
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
当前这些命令注册在两个互不关联的位置:
|
||||||
|
- `cli.tsx:203-212`: `daemon [start|status|stop]` → `daemon/main.ts`
|
||||||
|
- `cli.tsx:217-246`: `ps|logs|attach|kill|--bg` → `cli/bg.ts`
|
||||||
|
|
||||||
|
需要合并为统一的 `claude daemon <subcommand>` 入口,并新增 REPL `/daemon` 斜杠命令。
|
||||||
|
|
||||||
|
## 文件清单
|
||||||
|
|
||||||
|
### 新增
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `src/commands/daemon/index.ts` | `/daemon` REPL 斜杠命令注册 (type: local-jsx) |
|
||||||
|
| `src/commands/daemon/daemon.tsx` | `/daemon` 子命令路由 + status UI 组件 |
|
||||||
|
|
||||||
|
### 修改
|
||||||
|
|
||||||
|
| 文件 | 变更 |
|
||||||
|
|------|------|
|
||||||
|
| `src/entrypoints/cli.tsx` | 统一 daemon 快速路径: `daemon <sub>` 路由到对应 handler。旧命令 `ps/logs/attach/kill` 保留但输出 deprecation 警告后代理 |
|
||||||
|
| `src/commands.ts` | 注册 `/daemon` 斜杠命令 (feature-gated: DAEMON \|\| BG_SESSIONS) |
|
||||||
|
| `src/daemon/main.ts` | `daemonMain()` 扩展: 支持 `bg/attach/logs/kill/ps` 子命令 (委托给 bg.ts handlers) |
|
||||||
|
|
||||||
|
## 实现方案
|
||||||
|
|
||||||
|
### 1. CLI 快速路径统一 (`cli.tsx`)
|
||||||
|
|
||||||
|
**改前** (两段独立路由):
|
||||||
|
```typescript
|
||||||
|
// 段 1: daemon
|
||||||
|
if (feature('DAEMON') && args[0] === 'daemon') {
|
||||||
|
await daemonMain(args.slice(1))
|
||||||
|
}
|
||||||
|
// 段 2: bg sessions
|
||||||
|
if (feature('BG_SESSIONS') && ['ps','logs','attach','kill'].includes(args[0])) {
|
||||||
|
// ...switch/case
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**改后** (统一入口):
|
||||||
|
```typescript
|
||||||
|
// 统一 daemon 入口 — 合并 daemon supervisor + bg sessions
|
||||||
|
if (
|
||||||
|
(feature('DAEMON') || feature('BG_SESSIONS')) &&
|
||||||
|
args[0] === 'daemon'
|
||||||
|
) {
|
||||||
|
profileCheckpoint('cli_daemon_path')
|
||||||
|
const { enableConfigs } = await import('../utils/config.js')
|
||||||
|
enableConfigs()
|
||||||
|
const { initSinks } = await import('../utils/sinks.js')
|
||||||
|
initSinks()
|
||||||
|
const { daemonMain } = await import('../daemon/main.js')
|
||||||
|
await daemonMain(args.slice(1))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// --bg 快捷方式 → daemon bg
|
||||||
|
if (
|
||||||
|
feature('BG_SESSIONS') &&
|
||||||
|
(args.includes('--bg') || args.includes('--background'))
|
||||||
|
) {
|
||||||
|
profileCheckpoint('cli_daemon_path')
|
||||||
|
const { enableConfigs } = await import('../utils/config.js')
|
||||||
|
enableConfigs()
|
||||||
|
const bg = await import('../cli/bg.js')
|
||||||
|
await bg.handleBgStart(args.filter(a => a !== '--bg' && a !== '--background'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向后兼容: ps/logs/attach/kill → daemon <sub> (deprecated)
|
||||||
|
if (
|
||||||
|
feature('BG_SESSIONS') &&
|
||||||
|
['ps', 'logs', 'attach', 'kill'].includes(args[0] ?? '')
|
||||||
|
) {
|
||||||
|
const mapped = args[0] === 'ps' ? 'status' : args[0]
|
||||||
|
console.error(`[deprecated] Use: claude daemon ${mapped} ${args.slice(1).join(' ')}`.trim())
|
||||||
|
const { enableConfigs } = await import('../utils/config.js')
|
||||||
|
enableConfigs()
|
||||||
|
const { daemonMain } = await import('../daemon/main.js')
|
||||||
|
await daemonMain([args[0]!, ...args.slice(1)])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. daemonMain 扩展 (`daemon/main.ts`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export async function daemonMain(args: string[]): Promise<void> {
|
||||||
|
const subcommand = args[0] || 'status'
|
||||||
|
|
||||||
|
switch (subcommand) {
|
||||||
|
// --- Supervisor 管理 ---
|
||||||
|
case 'start':
|
||||||
|
await runSupervisor(args.slice(1))
|
||||||
|
break
|
||||||
|
case 'stop':
|
||||||
|
await handleDaemonStop()
|
||||||
|
break
|
||||||
|
|
||||||
|
// --- 会话管理 (委托给 bg.ts) ---
|
||||||
|
case 'status':
|
||||||
|
case 'ps':
|
||||||
|
await showUnifiedStatus() // 新: daemon 状态 + 会话列表
|
||||||
|
break
|
||||||
|
case 'bg':
|
||||||
|
const bg = await import('../cli/bg.js')
|
||||||
|
await bg.handleBgStart(args.slice(1))
|
||||||
|
break
|
||||||
|
case 'attach':
|
||||||
|
const bg2 = await import('../cli/bg.js')
|
||||||
|
await bg2.attachHandler(args[1])
|
||||||
|
break
|
||||||
|
case 'logs':
|
||||||
|
const bg3 = await import('../cli/bg.js')
|
||||||
|
await bg3.logsHandler(args[1])
|
||||||
|
break
|
||||||
|
case 'kill':
|
||||||
|
const bg4 = await import('../cli/bg.js')
|
||||||
|
await bg4.killHandler(args[1])
|
||||||
|
break
|
||||||
|
|
||||||
|
case '--help': case '-h': case 'help':
|
||||||
|
printHelp()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
console.error(`Unknown daemon subcommand: ${subcommand}`)
|
||||||
|
printHelp()
|
||||||
|
process.exitCode = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 统一状态面板 (`showUnifiedStatus`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
async function showUnifiedStatus(): Promise<void> {
|
||||||
|
// 1. Daemon supervisor 状态
|
||||||
|
const daemonResult = queryDaemonStatus()
|
||||||
|
console.log('=== Daemon Supervisor ===')
|
||||||
|
switch (daemonResult.status) {
|
||||||
|
case 'running':
|
||||||
|
console.log(` Status: running (PID: ${daemonResult.state!.pid})`)
|
||||||
|
console.log(` Workers: ${daemonResult.state!.workerKinds.join(', ')}`)
|
||||||
|
break
|
||||||
|
case 'stopped':
|
||||||
|
console.log(' Status: stopped')
|
||||||
|
break
|
||||||
|
case 'stale':
|
||||||
|
console.log(' Status: stale (cleaned up)')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 后台会话列表
|
||||||
|
console.log('\n=== Background Sessions ===')
|
||||||
|
const bg = await import('../cli/bg.js')
|
||||||
|
await bg.psHandler([])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. REPL 斜杠命令注册
|
||||||
|
|
||||||
|
**`src/commands/daemon/index.ts`**:
|
||||||
|
```typescript
|
||||||
|
import type { Command } from '../../commands.js'
|
||||||
|
import { feature } from 'bun:bundle'
|
||||||
|
|
||||||
|
const daemon = {
|
||||||
|
type: 'local-jsx',
|
||||||
|
name: 'daemon',
|
||||||
|
description: 'Manage background sessions and daemon',
|
||||||
|
argumentHint: '[status|start|stop|bg|attach|logs|kill]',
|
||||||
|
isEnabled: () => {
|
||||||
|
if (feature('DAEMON')) return true
|
||||||
|
if (feature('BG_SESSIONS')) return true
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
load: () => import('./daemon.js'),
|
||||||
|
} satisfies Command
|
||||||
|
|
||||||
|
export default daemon
|
||||||
|
```
|
||||||
|
|
||||||
|
**`src/commands/daemon/daemon.tsx`**:
|
||||||
|
```typescript
|
||||||
|
export async function call(
|
||||||
|
onDone: LocalJSXCommandOnDone,
|
||||||
|
context: LocalJSXCommandContext,
|
||||||
|
args: string,
|
||||||
|
): Promise<React.ReactNode> {
|
||||||
|
const parts = args.trim().split(/\s+/)
|
||||||
|
const sub = parts[0] || 'status'
|
||||||
|
|
||||||
|
switch (sub) {
|
||||||
|
case 'status':
|
||||||
|
case 'ps':
|
||||||
|
// 调用 showUnifiedStatus,捕获输出
|
||||||
|
// 返回文本结果
|
||||||
|
break
|
||||||
|
case 'bg':
|
||||||
|
// REPL 中启动后台会话
|
||||||
|
break
|
||||||
|
case 'start':
|
||||||
|
case 'stop':
|
||||||
|
case 'attach':
|
||||||
|
case 'logs':
|
||||||
|
case 'kill':
|
||||||
|
// 委托给对应 handler
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
onDone(`Unknown: ${sub}. Use: status|start|stop|bg|attach|logs|kill`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**`src/commands.ts`** 添加:
|
||||||
|
```typescript
|
||||||
|
// 条件导入
|
||||||
|
const daemonCmd =
|
||||||
|
feature('DAEMON') || feature('BG_SESSIONS')
|
||||||
|
? require('./commands/daemon/index.js').default
|
||||||
|
: null
|
||||||
|
|
||||||
|
// COMMANDS 数组中添加
|
||||||
|
...(daemonCmd ? [daemonCmd] : []),
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 更新 help 文本 (`daemon/main.ts`)
|
||||||
|
|
||||||
|
```
|
||||||
|
Claude Code Daemon — background process management
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
claude daemon [subcommand]
|
||||||
|
|
||||||
|
SUBCOMMANDS
|
||||||
|
status Show daemon and session status (default)
|
||||||
|
start Start the daemon supervisor
|
||||||
|
stop Stop the daemon
|
||||||
|
bg Start a background session
|
||||||
|
attach Attach to a background session
|
||||||
|
logs Show session logs
|
||||||
|
kill Kill a session
|
||||||
|
help Show this help
|
||||||
|
|
||||||
|
REPL
|
||||||
|
/daemon [subcommand] Same commands available in interactive mode
|
||||||
|
```
|
||||||
|
|
||||||
|
## 验证清单
|
||||||
|
|
||||||
|
- [ ] `claude daemon` (无参数) 显示统一状态面板
|
||||||
|
- [ ] `claude daemon status` 显示 supervisor + 会话列表
|
||||||
|
- [ ] `claude daemon start/stop` 与当前行为一致
|
||||||
|
- [ ] `claude daemon bg` 启动后台会话 (调用 BgEngine)
|
||||||
|
- [ ] `claude daemon attach/logs/kill <target>` 功能正常
|
||||||
|
- [ ] `claude ps` 输出 deprecation 警告 + 正常工作
|
||||||
|
- [ ] `claude logs/attach/kill` 同上
|
||||||
|
- [ ] `claude --bg` 快捷方式正常
|
||||||
|
- [ ] REPL 中 `/daemon` 可用,tab 补全显示
|
||||||
|
- [ ] REPL 中 `/daemon status` 显示状态信息
|
||||||
|
- [ ] tsc --noEmit 零错误
|
||||||
|
- [ ] bun test 通过
|
||||||
177
docs/task/task-015-job-command-hierarchy.md
Normal file
177
docs/task/task-015-job-command-hierarchy.md
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
# Task 015: /job 命令层级化
|
||||||
|
|
||||||
|
> 设计文档: [daemon-restructure-design.md](../features/daemon-restructure-design.md) § 三.2
|
||||||
|
> 依赖: 无 (可与 Task 013 并行)
|
||||||
|
> 分支: `feat/integrate-5-branches`
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
将 `claude new/list/reply` 收归 `/job` 命名空间,实现 CLI + REPL 双注册。
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
当前 `new`, `list`, `reply` 是顶级 CLI 命令 (`cli.tsx:250-261`),容易与其他命令冲突(特别是 `list` 这种通用词)。需要收归 `claude job <subcommand>` 并新增 REPL `/job` 入口。
|
||||||
|
|
||||||
|
## 文件清单
|
||||||
|
|
||||||
|
### 新增
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `src/commands/job/index.ts` | `/job` REPL 斜杠命令注册 |
|
||||||
|
| `src/commands/job/job.tsx` | `/job` 子命令路由 |
|
||||||
|
|
||||||
|
### 修改
|
||||||
|
|
||||||
|
| 文件 | 变更 |
|
||||||
|
|------|------|
|
||||||
|
| `src/entrypoints/cli.tsx` | 新增 `job` 快速路径 + 旧 `new/list/reply` deprecation 代理 |
|
||||||
|
| `src/commands.ts` | 注册 `/job` 斜杠命令 |
|
||||||
|
|
||||||
|
### 不动
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `src/cli/handlers/templateJobs.ts` | 内部 handler 不变,只是被调用方式变了 |
|
||||||
|
| `src/jobs/state.ts` | job 状态管理不变 |
|
||||||
|
| `src/jobs/templates.ts` | 模板发现不变 |
|
||||||
|
| `src/jobs/classifier.ts` | 任务分类器不变 |
|
||||||
|
|
||||||
|
## 实现方案
|
||||||
|
|
||||||
|
### 1. CLI 快速路径 (`cli.tsx`)
|
||||||
|
|
||||||
|
**改后**:
|
||||||
|
```typescript
|
||||||
|
// 新: claude job <subcommand>
|
||||||
|
if (
|
||||||
|
feature('TEMPLATES') &&
|
||||||
|
args[0] === 'job'
|
||||||
|
) {
|
||||||
|
profileCheckpoint('cli_templates_path')
|
||||||
|
const { templatesMain } = await import('../cli/handlers/templateJobs.js')
|
||||||
|
await templatesMain(args.slice(1))
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 向后兼容 (deprecated)
|
||||||
|
if (
|
||||||
|
feature('TEMPLATES') &&
|
||||||
|
(args[0] === 'new' || args[0] === 'list' || args[0] === 'reply')
|
||||||
|
) {
|
||||||
|
console.error(`[deprecated] Use: claude job ${args[0]} ${args.slice(1).join(' ')}`.trim())
|
||||||
|
profileCheckpoint('cli_templates_path')
|
||||||
|
const { templatesMain } = await import('../cli/handlers/templateJobs.js')
|
||||||
|
await templatesMain(args)
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. templateJobs.ts 新增 status 子命令
|
||||||
|
|
||||||
|
在现有 `switch` 中增加:
|
||||||
|
```typescript
|
||||||
|
case 'status':
|
||||||
|
handleStatus(args.slice(1))
|
||||||
|
break
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function handleStatus(args: string[]): void {
|
||||||
|
const jobId = args[0]
|
||||||
|
if (!jobId) {
|
||||||
|
console.error('Usage: claude job status <job-id>')
|
||||||
|
process.exitCode = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const state = readJobState(jobId)
|
||||||
|
if (!state) {
|
||||||
|
console.error(`Job not found: ${jobId}`)
|
||||||
|
process.exitCode = 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(`Job: ${state.jobId}`)
|
||||||
|
console.log(` Template: ${state.templateName}`)
|
||||||
|
console.log(` Status: ${state.status}`)
|
||||||
|
console.log(` Created: ${state.createdAt}`)
|
||||||
|
console.log(` Updated: ${state.updatedAt}`)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. REPL 斜杠命令
|
||||||
|
|
||||||
|
**`src/commands/job/index.ts`**:
|
||||||
|
```typescript
|
||||||
|
import type { Command } from '../../commands.js'
|
||||||
|
import { feature } from 'bun:bundle'
|
||||||
|
|
||||||
|
const job = {
|
||||||
|
type: 'local-jsx',
|
||||||
|
name: 'job',
|
||||||
|
description: 'Manage template jobs',
|
||||||
|
argumentHint: '[list|new|reply|status]',
|
||||||
|
isEnabled: () => {
|
||||||
|
if (feature('TEMPLATES')) return true
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
load: () => import('./job.js'),
|
||||||
|
} satisfies Command
|
||||||
|
|
||||||
|
export default job
|
||||||
|
```
|
||||||
|
|
||||||
|
**`src/commands/job/job.tsx`**:
|
||||||
|
```typescript
|
||||||
|
export async function call(
|
||||||
|
onDone: LocalJSXCommandOnDone,
|
||||||
|
_context: LocalJSXCommandContext,
|
||||||
|
args: string,
|
||||||
|
): Promise<React.ReactNode> {
|
||||||
|
const parts = args.trim().split(/\s+/)
|
||||||
|
const sub = parts[0] || 'list'
|
||||||
|
|
||||||
|
// 委托给 templatesMain
|
||||||
|
const { templatesMain } = await import('../../cli/handlers/templateJobs.js')
|
||||||
|
|
||||||
|
// 捕获 console.log 输出作为结果返回给 REPL
|
||||||
|
const lines: string[] = []
|
||||||
|
const origLog = console.log
|
||||||
|
const origError = console.error
|
||||||
|
console.log = (...a: unknown[]) => lines.push(a.join(' '))
|
||||||
|
console.error = (...a: unknown[]) => lines.push(a.join(' '))
|
||||||
|
|
||||||
|
try {
|
||||||
|
await templatesMain([sub, ...parts.slice(1)])
|
||||||
|
} finally {
|
||||||
|
console.log = origLog
|
||||||
|
console.error = origError
|
||||||
|
}
|
||||||
|
|
||||||
|
onDone(lines.join('\n') || 'Done.', { display: 'system' })
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. commands.ts 注册
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const jobCmd = feature('TEMPLATES')
|
||||||
|
? require('./commands/job/index.js').default
|
||||||
|
: null
|
||||||
|
|
||||||
|
// COMMANDS 数组:
|
||||||
|
...(jobCmd ? [jobCmd] : []),
|
||||||
|
```
|
||||||
|
|
||||||
|
## 验证清单
|
||||||
|
|
||||||
|
- [ ] `claude job list` 列出模板
|
||||||
|
- [ ] `claude job new <template>` 创建任务
|
||||||
|
- [ ] `claude job reply <id> <text>` 回复任务
|
||||||
|
- [ ] `claude job status <id>` 显示任务状态
|
||||||
|
- [ ] `claude job` (无参数) 等同于 `claude job list`
|
||||||
|
- [ ] `claude new/list/reply` 输出 deprecation 警告 + 正常工作
|
||||||
|
- [ ] REPL 中 `/job` 可用
|
||||||
|
- [ ] REPL 中 `/job list` 显示模板列表
|
||||||
|
- [ ] tsc --noEmit 零错误
|
||||||
|
- [ ] bun test 通过
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user