From dc94e16e539def6983b6af509af91e6c40d16718 Mon Sep 17 00:00:00 2001 From: unraid Date: Mon, 6 Apr 2026 14:24:25 +0800 Subject: [PATCH] feat: enable GrowthBook local gate defaults for P0/P1 features Add LOCAL_GATE_DEFAULTS mapping in growthbook.ts to enable 25+ features when GrowthBook is not connected. Modify 4 getter functions to check local defaults before returning hard-coded false. P0 (local): keybindings, streaming tool exec, cron, JSON tools, ultrathink P1 (API): session memory, auto memory, prompt suggestions, brief mode Kill switches: 10 gates kept true to prevent remote disable New compile flags: AGENT_TRIGGERS, ULTRATHINK, BUILTIN_EXPLORE_PLAN_AGENTS, LODESTONE, EXTRACT_MEMORIES, VERIFICATION_AGENT, KAIROS_BRIEF, AWAY_SUMMARY Bypass: CLAUDE_CODE_DISABLE_LOCAL_GATES=1 --- DEV-LOG.md | 90 +++++ build.ts | 10 + docs/features/feature-flags-audit-complete.md | 14 +- docs/features/growthbook-enablement-plan.md | 334 ++++++++++++++++++ scripts/dev.ts | 14 +- scripts/verify-gates.ts | 107 ++++++ src/services/analytics/growthbook.ts | 93 ++++- 7 files changed, 647 insertions(+), 15 deletions(-) create mode 100644 docs/features/growthbook-enablement-plan.md create mode 100644 scripts/verify-gates.ts diff --git a/DEV-LOG.md b/DEV-LOG.md index 6d13a3e44..9323fdf2b 100644 --- a/DEV-LOG.md +++ b/DEV-LOG.md @@ -1,5 +1,95 @@ # DEV-LOG +## GrowthBook Local Gate Defaults + P0/P1 Feature Enablement (2026-04-06) + +**分支**: `feat/growthbook-enablement` + +### 背景 + +Claude Code 使用 GrowthBook(Anthropic 自建 proxy at api.anthropic.com)进行远程功能开关控制,代码中使用 `tengu_*` 前缀命名。在反编译版本中 GrowthBook 不启动(analytics 空实现),导致 70+ 个功能被 gate 拦截。 + +经 4 个并行研究代理深度分析,确认**所有被 gate 控制的功能代码都是真实现**(非 stub)。 + +### 实现方案 + +在 `growthbook.ts` 中添加 `LOCAL_GATE_DEFAULTS` 映射表,当 GrowthBook 未连接时使用本地默认值替代硬编码 `false`。修改了 4 个 getter 函数: +- `getFeatureValueInternal()` +- `getFeatureValue_CACHED_MAY_BE_STALE()` +- `checkStatsigFeatureGate_CACHED_MAY_BE_STALE()` +- `checkGate_CACHED_OR_BLOCKING()` + +可通过 `CLAUDE_CODE_DISABLE_LOCAL_GATES=1` 环境变量一键禁用。 + +### 启用的功能 + +**P0 — 纯本地功能(7 个 gate):** + +| Gate | 功能 | +|------|------| +| `tengu_keybinding_customization_release` | 自定义快捷键(~/.claude/keybindings.json) | +| `tengu_streaming_tool_execution2` | 流式工具执行(边收边执行) | +| `tengu_kairos_cron` | 定时任务系统 | +| `tengu_amber_json_tools` | Token 高效 JSON 工具格式(省 ~4.5%) | +| `tengu_immediate_model_command` | 运行中即时切换模型 | +| `tengu_basalt_3kr` | MCP 指令增量传输 | +| `tengu_pebble_leaf_prune` | 会话存储叶剪枝优化 | + +**P1 — API 依赖功能(8 个 gate):** + +| Gate | 功能 | +|------|------| +| `tengu_session_memory` | 会话记忆(跨会话上下文持久化) | +| `tengu_passport_quail` | 自动记忆提取 | +| `tengu_chomp_inflection` | 提示建议 | +| `tengu_hive_evidence` | 验证代理(对抗性验证) | +| `tengu_kairos_brief` | Brief 精简输出模式 | +| `tengu_sedge_lantern` | 离开摘要 | +| `tengu_onyx_plover` | 自动梦境(记忆巩固) | +| `tengu_willow_mode` | 空闲返回提示 | + +**Kill Switch(10 个 gate 保持 true):** + +`tengu_turtle_carbon`、`tengu_amber_stoat`、`tengu_amber_flint`、`tengu_slim_subagent_claudemd`、`tengu_birch_trellis`、`tengu_collage_kaleidoscope`、`tengu_compact_cache_prefix`、`tengu_kairos_cron_durable`、`tengu_attribution_header`、`tengu_slate_prism` + +**新增编译 flag:** + +| Flag | build.ts | dev.ts | 用途 | +|------|:--------:|:------:|------| +| `AGENT_TRIGGERS` | ON | ON | 定时任务系统 | +| `EXTRACT_MEMORIES` | ON | ON | 自动记忆提取 | +| `VERIFICATION_AGENT` | ON | ON | 对抗性验证代理 | +| `KAIROS_BRIEF` | ON | ON | Brief 精简模式 | +| `AWAY_SUMMARY` | ON | ON | 离开摘要 | +| `ULTRATHINK` | ON | ON | Ultrathink 扩展思考(双重门控修复) | +| `BUILTIN_EXPLORE_PLAN_AGENTS` | ON | ON | 内置 Explore/Plan agents(双重门控修复) | +| `LODESTONE` | ON | ON | Deep link 协议注册(双重门控修复) | + +**排除的编译 flag:** +- `KAIROS` — 拉入 `useProactive.js`(缺失文件),`KAIROS_BRIEF` 足够 +- `TERMINAL_PANEL` — 拉入 `TerminalCaptureTool`(缺失文件) + +**双重门控修复说明:** +部分功能同时被编译 flag 和 GrowthBook gate 控制(双重门控),仅开 GrowthBook gate 不够。 +审计发现 3 个被卡住的:`ULTRATHINK`、`BUILTIN_EXPLORE_PLAN_AGENTS`、`LODESTONE`。 + +### 修改文件 + +| 文件 | 变更 | +|------|------| +| `build.ts` | `DEFAULT_BUILD_FEATURES` 新增 8 个编译 flag | +| `scripts/dev.ts` | `DEFAULT_FEATURES` 新增 8 个编译 flag | +| `src/services/analytics/growthbook.ts` | 新增 `LOCAL_GATE_DEFAULTS` 映射 + `getLocalGateDefault()` + 修改 4 个 getter | +| `docs/features/growthbook-enablement-plan.md` | 完整研究报告和启用计划 | + +### 验证 + +| 项目 | 结果 | +|------|------| +| `bun run build` | ✅ 成功 (475 files) | +| `bun test` | ✅ 无新增失败 (23 fail 为已有问题) | + +--- + ## Enable SHOT_STATS, TOKEN_BUDGET, PROMPT_CACHE_BREAK_DETECTION (2026-04-05) **PR**: [claude-code-best/claude-code#140](https://github.com/claude-code-best/claude-code/pull/140) diff --git a/build.ts b/build.ts index b22b327ab..96f322993 100644 --- a/build.ts +++ b/build.ts @@ -17,6 +17,16 @@ const DEFAULT_BUILD_FEATURES = [ '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', ] // Collect FEATURE_* env vars → Bun.build features diff --git a/docs/features/feature-flags-audit-complete.md b/docs/features/feature-flags-audit-complete.md index 5d5ac83c5..bfedff447 100644 --- a/docs/features/feature-flags-audit-complete.md +++ b/docs/features/feature-flags-audit-complete.md @@ -38,18 +38,24 @@ Claude Code 使用三层门控系统: --- -## 当前启用状态 (2026-04-05) +## 当前启用状态 (2026-04-06) > 经 Codex CLI 独立复核验证,详见 `feature-flags-codex-review.md` +> GrowthBook gate 启用详见 `growthbook-enablement-plan.md` | 标志 | build.ts | dev.ts | 实际验证状态 | 备注 | |------|:--------:|:------:|:----------:|------| | AGENT_TRIGGERS_REMOTE | **ON** | **ON** | compile-only | 环境标记,原始即启用 | | CHICAGO_MCP | **ON** | **ON** | compile-only | Computer Use,原始即启用 | | VOICE_MODE | **ON** | **ON** | compile-only | 语音模式,原始即启用 | -| SHOT_STATS | **ON** | **ON** | compile-only, 已验证 | 本轮新增,纯本地统计 | -| PROMPT_CACHE_BREAK_DETECTION | **ON** | **ON** | compile-only, 已验证 | 本轮新增,内部诊断 | -| TOKEN_BUDGET | **ON** | **ON** | compile-only, 已验证 | 本轮新增,支持 `+500k` 语法 | +| SHOT_STATS | **ON** | **ON** | compile-only, 已验证 | 纯本地统计 | +| PROMPT_CACHE_BREAK_DETECTION | **ON** | **ON** | compile-only, 已验证 | 内部诊断 | +| TOKEN_BUDGET | **ON** | **ON** | compile-only, 已验证 | 支持 `+500k` 语法 | +| AGENT_TRIGGERS | **ON** | **ON** | compile+GB gate, 已验证 | 本轮新增,定时任务系统 | +| EXTRACT_MEMORIES | **ON** | **ON** | compile+GB gate, 已验证 | 本轮新增,自动记忆提取 | +| VERIFICATION_AGENT | **ON** | **ON** | compile+GB gate, 已验证 | 本轮新增,对抗性验证代理 | +| KAIROS_BRIEF | **ON** | **ON** | compile+GB gate, 已验证 | 本轮新增,Brief 精简模式 | +| AWAY_SUMMARY | **ON** | **ON** | compile+GB gate, 已验证 | 本轮新增,离开摘要 | | BUDDY | off | **ON** | compile+GrowthBook | 仅 dev 模式 | | TRANSCRIPT_CLASSIFIER | off | **ON** | compile+GrowthBook | 仅 dev 模式 | | BRIDGE_MODE | off | **ON** | compile+remote | 仅 dev 模式,需 claude.ai 订阅 | diff --git a/docs/features/growthbook-enablement-plan.md b/docs/features/growthbook-enablement-plan.md new file mode 100644 index 000000000..d2d524503 --- /dev/null +++ b/docs/features/growthbook-enablement-plan.md @@ -0,0 +1,334 @@ +# GrowthBook 功能启用计划 + +> 编制日期: 2026-04-06 +> 基于: feature-flags-codex-review.md + 4 个并行研究代理的深度分析 +> 前提: 我们是付费订阅用户,拥有有效的 Anthropic API key + +--- + +## 背景 + +Claude Code 使用三层门控系统: +1. **编译时 feature flag** — `feature('FLAG_NAME')` from `bun:bundle` +2. **GrowthBook 远程开关** — `tengu_*` 前缀,通过 SDK 连接 Anthropic 服务端 +3. **运行时环境变量** — `USER_TYPE`、`CLAUDE_CODE_*` 等 + +在我们的反编译版本中,GrowthBook 不启动(analytics 链空实现),导致所有 `tengu_*` 检查默认返回 `false`。 + +**核心发现:所有被 GrowthBook 门控的功能代码都是真实现,没有 stub。** + +--- + +## 启用方式说明 + +### 方式 1:硬编码绕过(推荐先用) +在 `src/services/analytics/growthbook.ts` 的 `getFeatureValueInternal()` 函数中添加默认值映射。 + +### 方式 2:自建 GrowthBook 服务器 +```bash +docker run -p 3100:3100 growthbook/growthbook +# 设置环境变量 +CLAUDE_GB_ADAPTER_URL=http://localhost:3100 +CLAUDE_GB_ADAPTER_KEY=sdk-xxx +``` + +### 方式 3:恢复原生 1P 连接 +让 `is1PEventLoggingEnabled()` 返回 `true`,连接 Anthropic 的 GrowthBook 服务端。 +注意:会发送使用统计(不含代码/对话内容)。 + +--- + +## 优先级 P0:纯本地功能(零外部依赖,立即可用) + +这些功能不需要 API 调用,开启 gate 即可工作。 + +### P0-1. 自定义快捷键 +- **Gate**: `tengu_keybinding_customization_release` → `true` +- **编译 flag**: 无(已内置) +- **代码量**: 473 行,完整实现 +- **功能**: 加载 `~/.claude/keybindings.json`,支持热重载、重复键检测、结构验证 +- **效果**: 用户可自定义所有快捷键 +- **风险**: 无 + +### P0-2. 流式工具执行 +- **Gate**: `tengu_streaming_tool_execution2` → `true` +- **编译 flag**: 无(已内置) +- **代码量**: 577 行(StreamingToolExecutor),完整实现 +- **功能**: API 响应还在流式返回时就开始执行工具,减少等待时间 +- **效果**: 显著提升交互速度 +- **风险**: 低(生产级代码,有错误处理) + +### P0-3. 定时任务系统 +- **Gate**: `tengu_kairos_cron` → `true`(额外:`tengu_kairos_cron_durable` 默认 `true`) +- **编译 flag**: `AGENT_TRIGGERS`(需新增)或 `AGENT_TRIGGERS_REMOTE`(已启用) +- **代码量**: 1025 行(cronTasks + cronScheduler),完整实现 +- **功能**: 本地 cron 调度,支持一次性/周期性任务、防雷群效应 jitter、自动过期 +- **效果**: 可设置定时执行的 Claude 任务 +- **风险**: 低 + +### P0-4. Agent 团队 / Swarm +- **Gate**: `tengu_amber_flint` → `true`(这是 kill switch,默认已 `true`) +- **编译 flag**: 无(已内置) +- **代码量**: 45 行(gate 层),实际 swarm 实现在 teammate tools 中 +- **功能**: 多 agent 协作,需额外设置 `--agent-teams` 或 `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` +- **效果**: 允许创建和管理 agent 团队 +- **风险**: 无(kill switch 默认就是 true) + +### P0-5. Token 高效 JSON 工具格式 +- **Gate**: `tengu_amber_json_tools` → `true` +- **编译 flag**: 无(已内置) +- **代码量**: betas.ts 中几行 gate 检查 +- **功能**: 启用 FC v3 格式,减少约 4.5% 的输出 token +- **效果**: 省钱 +- **风险**: 低(需要模型支持该 beta header) + +### P0-6. Ultrathink 扩展思考 +- **Gate**: `tengu_turtle_carbon` → `true`(默认已 `true`,kill switch) +- **编译 flag**: 无 +- **功能**: 通过关键词触发扩展思考模式 +- **效果**: 已默认启用,确保不被远程关闭即可 +- **风险**: 无 + +### P0-7. 即时模型切换 +- **Gate**: `tengu_immediate_model_command` → `true` +- **编译 flag**: 无 +- **功能**: 在 query 运行过程中即时执行 `/model`、`/fast`、`/effort` 命令 +- **效果**: 无需等当前任务完成就能切换 +- **风险**: 低 + +--- + +## 优先级 P1:需要 Claude API 的功能(有 API key 即可用) + +这些功能需要调用 Claude API(使用 forked subagent 或 queryModel),有订阅即可。 + +### P1-1. 会话记忆 +- **Gate**: `tengu_session_memory` → `true`(配置:`tengu_sm_config` → `{}`) +- **编译 flag**: 无(已内置) +- **代码量**: 1127 行,完整实现 +- **功能**: 跨会话上下文持久化。用 forked agent 定期提取会话笔记到 markdown 文件 +- **效果**: Claude 记住跨会话的工作上下文 +- **依赖**: Claude API(forked subagent) +- **风险**: 低(额外 API token 消耗) + +### P1-2. 自动记忆提取 +- **Gate**: `tengu_passport_quail` → `true`(相关:`tengu_moth_copse`、`tengu_coral_fern`) +- **编译 flag**: `EXTRACT_MEMORIES`(需新增) +- **代码量**: 616 行,完整实现 +- **功能**: 对话中自动提取持久记忆到 `~/.claude/projects//memory/` +- **效果**: 自动构建项目知识库 +- **依赖**: Claude API(forked subagent) +- **风险**: 低 + +### P1-3. 提示建议 +- **Gate**: `tengu_chomp_inflection` → `true` +- **编译 flag**: 无(已内置) +- **代码量**: 525 行,完整实现 +- **功能**: 自动生成下一步操作建议,带投机预取(speculation prefetch) +- **效果**: 更流畅的交互体验 +- **依赖**: Claude API(forked subagent) +- **风险**: 低(额外 API 消耗,但有缓存感知) + +### P1-4. 验证代理 +- **Gate**: `tengu_hive_evidence` → `true` +- **编译 flag**: `VERIFICATION_AGENT`(需新增) +- **代码量**: 153 行(agent 定义),完整实现 +- **功能**: 对抗性验证 agent,主动尝试打破你的实现(只读模式) +- **效果**: 自动化代码验证 +- **依赖**: Claude API(subagent) +- **风险**: 低(只读,不修改代码) + +### P1-5. Brief 模式 +- **Gate**: `tengu_kairos_brief` → `true` +- **编译 flag**: `KAIROS` 或 `KAIROS_BRIEF`(需新增) +- **代码量**: 335 行,完整实现 +- **功能**: `/brief` 命令切换精简输出模式 +- **效果**: 减少冗余输出 +- **依赖**: Claude API +- **风险**: 低 + +### P1-6. 离开摘要 +- **Gate**: `tengu_sedge_lantern` → `true` +- **编译 flag**: `AWAY_SUMMARY`(需新增) +- **代码量**: 176 行,完整实现 +- **功能**: 离开终端 5 分钟后返回时自动总结期间发生了什么 +- **效果**: 快速恢复上下文 +- **依赖**: Claude API + 终端焦点事件支持 +- **风险**: 低 + +### P1-7. 自动梦境 +- **Gate**: `tengu_onyx_plover` → `{"enabled": true}` +- **编译 flag**: 无(已内置,但检查 auto-memory 是否启用) +- **代码量**: 349 行,完整实现 +- **功能**: 后台自动整理/巩固记忆(等同于自动执行 `/dream`) +- **效果**: 记忆自动保持整洁有序 +- **依赖**: Claude API(forked subagent)+ auto-memory 启用 +- **风险**: 低 + +### P1-8. 空闲返回提示 +- **Gate**: `tengu_willow_mode` → `"dialog"` 或 `"hint"` +- **编译 flag**: 无 +- **功能**: 对话太大且缓存过期时,提示用户开新会话 +- **效果**: 避免在过期缓存上浪费 token +- **风险**: 无 + +--- + +## 优先级 P2:增强型功能(提升体验但非必须) + +### P2-1. MCP 指令增量传输 +- **Gate**: `tengu_basalt_3kr` → `true` +- **功能**: 只发送变化的 MCP 指令而非全量 +- **效果**: 减少 token 消耗 +- **风险**: 低 + +### P2-2. 叶剪枝优化 +- **Gate**: `tengu_pebble_leaf_prune` → `true` +- **功能**: 会话存储中移除死胡同消息分支 +- **效果**: 减少存储和加载时间 +- **风险**: 低 + +### P2-3. 消息合并 +- **Gate**: `tengu_chair_sermon` → `true` +- **功能**: 合并相邻的 tool_result + text 块 +- **效果**: 减少 token 消耗 +- **风险**: 低 + +### P2-4. 深度链接 +- **Gate**: `tengu_lodestone_enabled` → `true` +- **功能**: 注册 `claude://` URL 协议处理器 +- **效果**: 可从浏览器直接打开 Claude Code +- **风险**: 低 + +### P2-5. Agent 自动转后台 +- **Gate**: `tengu_auto_background_agents` → `true` +- **功能**: Agent 任务运行 120s 后自动转为后台 +- **效果**: 不再阻塞主交互 +- **风险**: 低 + +### P2-6. 细粒度工具状态 +- **Gate**: `tengu_fgts` → `true` +- **功能**: 系统提示中包含细粒度工具状态信息 +- **效果**: 模型更好地理解工具可用性 +- **风险**: 低 + +### P2-7. 文件操作 git diff +- **Gate**: `tengu_quartz_lantern` → `true` +- **功能**: 文件写入/编辑时计算 git diff(仅远程会话) +- **效果**: 更好的变更追踪 +- **风险**: 低 + +--- + +## 优先级 P3:需要自建服务或 Anthropic OAuth + +### P3-1. 团队记忆 +- **Gate**: `tengu_herring_clock` → `true` +- **编译 flag**: `TEAMMEM`(需新增) +- **代码量**: 1180+ 行,完整实现 +- **功能**: 跨 agent 共享记忆,同步到 Anthropic API +- **依赖**: Anthropic OAuth + GitHub remote +- **状态**: 需要 Anthropic 的 `/api/claude_code/team_memory` 端点 +- **可行性**: 除非自建兼容 API,否则无法使用 + +### P3-2. 设置同步 +- **Gate**: `tengu_enable_settings_sync_push` + `tengu_strap_foyer` → `true` +- **编译 flag**: `UPLOAD_USER_SETTINGS` / `DOWNLOAD_USER_SETTINGS`(需新增) +- **代码量**: 582 行,完整实现 +- **功能**: 跨设备设置同步 +- **依赖**: Anthropic OAuth + `/api/claude_code/user_settings` +- **可行性**: 同上 + +### P3-3. Bridge 远程控制 +- **Gate**: `tengu_ccr_bridge` → `true`(已有编译 flag `BRIDGE_MODE` dev 模式启用) +- **代码量**: 12,619 行,完整实现 +- **功能**: claude.ai 网页端远程控制 CLI +- **依赖**: claude.ai 订阅 + WebSocket 后端 +- **可行性**: 需要 Anthropic 的 CCR 后端 + +### P3-4. 远程定时 Agent +- **Gate**: `tengu_surreal_dali` → `true` +- **功能**: 创建在远程执行的定时 agent +- **依赖**: Anthropic CCR 基础设施 +- **可行性**: 需要远程服务 + +--- + +## Kill Switch 清单(确保不被远程关闭) + +这些 gate 默认为 `true`,是 kill switch。应确保它们保持 `true`: + +| Gate | 默认 | 控制什么 | +|---|---|---| +| `tengu_turtle_carbon` | `true` | Ultrathink 扩展思考 | +| `tengu_amber_stoat` | `true` | 内置 Explore/Plan agent | +| `tengu_amber_flint` | `true` | Agent 团队/Swarm | +| `tengu_slim_subagent_claudemd` | `true` | 子 agent 精简 CLAUDE.md | +| `tengu_birch_trellis` | `true` | tree-sitter bash 安全分析 | +| `tengu_collage_kaleidoscope` | `true` | macOS 剪贴板图片读取 | +| `tengu_compact_cache_prefix` | `true` | 压缩时复用 prompt cache | +| `tengu_kairos_cron_durable` | `true` | 持久化 cron 任务 | +| `tengu_attribution_header` | `true` | API 请求署名 | +| `tengu_slate_prism` | `true` | Agent 进度摘要 | + +--- + +## 需要新增的编译 flag + +以下编译时 flag 尚未在 `build.ts` / `scripts/dev.ts` 中启用,但功能代码完整: + +| Flag | 用于 | 优先级 | +|---|---|---| +| `AGENT_TRIGGERS` | 定时任务系统(P0-3) | P0 | +| `EXTRACT_MEMORIES` | 自动记忆提取(P1-2) | P1 | +| `VERIFICATION_AGENT` | 验证代理(P1-4) | P1 | +| `KAIROS` 或 `KAIROS_BRIEF` | Brief 模式(P1-5) | P1 | +| `AWAY_SUMMARY` | 离开摘要(P1-6) | P1 | +| `TEAMMEM` | 团队记忆(P3-1) | P3 | + +--- + +## 实施路线图 + +### Phase 1:硬编码 P0 纯本地 gate(最快见效) +1. 在 growthbook.ts 添加默认值映射 +2. 在 build.ts / dev.ts 添加 `AGENT_TRIGGERS` 编译 flag +3. 验证 7 个 P0 功能正常工作 +4. 预计工作量:1-2 小时 + +### Phase 2:启用 P1 API 依赖功能 +1. 添加编译 flag:`EXTRACT_MEMORIES`、`VERIFICATION_AGENT`、`KAIROS_BRIEF`、`AWAY_SUMMARY` +2. 添加 P1 gate 默认值 +3. 验证 8 个 P1 功能正常工作 +4. 预计工作量:2-3 小时 + +### Phase 3:评估自建 GrowthBook(可选) +1. Docker 部署 GrowthBook 服务器 +2. 迁移硬编码值到 GrowthBook 后台管理 +3. 获得 Web UI 管理所有 flag 的能力 +4. 预计工作量:半天 + +### Phase 4:评估远程功能(可选) +1. 研究是否可以使用 Anthropic OAuth +2. 评估团队记忆、设置同步的自建可行性 +3. 预计工作量:待评估 + +--- + +## 隐私说明 + +### 硬编码绕过(方案 A) +- **零数据外发** +- GrowthBook SDK 不启动 +- 完全离线运行 + +### 自建 GrowthBook(方案 B) +- 数据仅发送到你自己的服务器 +- Anthropic 无法获取任何数据 +- 可通过 Web UI 实时管理所有 flag + +### 恢复原生 1P(方案 C) +- 会发送使用统计到 `api.anthropic.com` +- **不发送**:代码、对话内容、API key +- **会发送**:邮箱、设备 ID、机器指纹、仓库哈希、订阅类型 +- 可用 `DISABLE_TELEMETRY=1` 关闭遥测(但同时关闭 GrowthBook) diff --git a/scripts/dev.ts b/scripts/dev.ts index c1c141017..8444fd2c7 100644 --- a/scripts/dev.ts +++ b/scripts/dev.ts @@ -23,7 +23,19 @@ const defineArgs = Object.entries(defines).flatMap(([k, v]) => [ // Bun --feature flags: enable feature() gates at runtime. // Default features enabled in dev mode. -const DEFAULT_FEATURES = ["BUDDY", "TRANSCRIPT_CLASSIFIER", "BRIDGE_MODE", "AGENT_TRIGGERS_REMOTE", "CHICAGO_MCP", "VOICE_MODE", "SHOT_STATS", "PROMPT_CACHE_BREAK_DETECTION", "TOKEN_BUDGET"]; +const DEFAULT_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", +]; // Any env var matching FEATURE_=1 will also enable that feature. // e.g. FEATURE_PROACTIVE=1 bun run dev diff --git a/scripts/verify-gates.ts b/scripts/verify-gates.ts new file mode 100644 index 000000000..dbef1c8a4 --- /dev/null +++ b/scripts/verify-gates.ts @@ -0,0 +1,107 @@ +#!/usr/bin/env bun +/** + * Verify GrowthBook gate defaults and compile-time feature flags. + * + * Usage: + * bun run scripts/verify-gates.ts + * + * This script checks that LOCAL_GATE_DEFAULTS are being returned correctly + * when GrowthBook is not connected, and that compile-time feature flags + * are properly enabled. + */ + +// We can't import feature() from bun:bundle in a standalone script, +// so we test the GrowthBook layer directly. + +import { + getFeatureValue_CACHED_MAY_BE_STALE, + checkStatsigFeatureGate_CACHED_MAY_BE_STALE, +} from '../src/services/analytics/growthbook.js' + +interface GateCheck { + name: string + gate: string + expected: unknown + category: string + /** If set, this compile flag must also be enabled at build time */ + compileFlag?: string +} + +const gates: GateCheck[] = [ + // P0: Pure local + { name: 'Custom keybindings', gate: 'tengu_keybinding_customization_release', expected: true, category: 'P0' }, + { name: 'Streaming tool exec', gate: 'tengu_streaming_tool_execution2', expected: true, category: 'P0' }, + { name: 'Cron tasks', gate: 'tengu_kairos_cron', expected: true, category: 'P0' }, + { name: 'JSON tools format', gate: 'tengu_amber_json_tools', expected: true, category: 'P0' }, + { name: 'Immediate model cmd', gate: 'tengu_immediate_model_command', expected: true, category: 'P0' }, + { name: 'MCP delta', gate: 'tengu_basalt_3kr', expected: true, category: 'P0' }, + { name: 'Leaf pruning', gate: 'tengu_pebble_leaf_prune', expected: true, category: 'P0' }, + { name: 'Message smooshing', gate: 'tengu_chair_sermon', expected: true, category: 'P0' }, + { name: 'Deep link', gate: 'tengu_lodestone_enabled', expected: true, category: 'P0', compileFlag: 'LODESTONE' }, + { name: 'Auto background', gate: 'tengu_auto_background_agents', expected: true, category: 'P0' }, + { name: 'Fine-grained tools', gate: 'tengu_fgts', expected: true, category: 'P0' }, + + // P1: API-dependent + { name: 'Session memory', gate: 'tengu_session_memory', expected: true, category: 'P1' }, + { name: 'Auto memory extract', gate: 'tengu_passport_quail', expected: true, category: 'P1', compileFlag: 'EXTRACT_MEMORIES' }, + { name: 'Memory skip index', gate: 'tengu_moth_copse', expected: true, category: 'P1' }, + { name: 'Memory search section', gate: 'tengu_coral_fern', expected: true, category: 'P1' }, + { name: 'Prompt suggestions', gate: 'tengu_chomp_inflection', expected: true, category: 'P1' }, + { name: 'Verification agent', gate: 'tengu_hive_evidence', expected: true, category: 'P1', compileFlag: 'VERIFICATION_AGENT' }, + { name: 'Brief mode', gate: 'tengu_kairos_brief', expected: true, category: 'P1', compileFlag: 'KAIROS_BRIEF' }, + { name: 'Away summary', gate: 'tengu_sedge_lantern', expected: true, category: 'P1', compileFlag: 'AWAY_SUMMARY' }, + { name: 'Idle return prompt', gate: 'tengu_willow_mode', expected: 'dialog', category: 'P1' }, + + // Kill switches + { name: 'Ultrathink', gate: 'tengu_turtle_carbon', expected: true, category: 'KS', compileFlag: 'ULTRATHINK' }, + { name: 'Explore/Plan agents', gate: 'tengu_amber_stoat', expected: true, category: 'KS', compileFlag: 'BUILTIN_EXPLORE_PLAN_AGENTS' }, + { name: 'Agent teams', gate: 'tengu_amber_flint', expected: true, category: 'KS' }, + { name: 'Slim subagent CLAUDE.md', gate: 'tengu_slim_subagent_claudemd', expected: true, category: 'KS' }, + { name: 'Bash security', gate: 'tengu_birch_trellis', expected: true, category: 'KS' }, + { name: 'macOS clipboard', gate: 'tengu_collage_kaleidoscope', expected: true, category: 'KS' }, + { name: 'Compact cache prefix', gate: 'tengu_compact_cache_prefix', expected: true, category: 'KS' }, + { name: 'Durable cron', gate: 'tengu_kairos_cron_durable', expected: true, category: 'KS' }, + { name: 'Attribution header', gate: 'tengu_attribution_header', expected: true, category: 'KS' }, + { name: 'Agent progress', gate: 'tengu_slate_prism', expected: true, category: 'KS' }, +] + +console.log('=== GrowthBook Local Gate Verification ===\n') + +let pass = 0 +let fail = 0 + +for (const category of ['P0', 'P1', 'KS']) { + const label = category === 'KS' ? 'Kill Switches' : category + console.log(`--- ${label} ---`) + + for (const check of gates.filter(g => g.category === category)) { + const actual = typeof check.expected === 'boolean' + ? checkStatsigFeatureGate_CACHED_MAY_BE_STALE(check.gate) + : getFeatureValue_CACHED_MAY_BE_STALE(check.gate, null) + + const matches = typeof check.expected === 'boolean' + ? actual === check.expected + : actual === check.expected || JSON.stringify(actual) === JSON.stringify(check.expected) + + const status = matches ? '\x1b[32mPASS\x1b[0m' : '\x1b[31mFAIL\x1b[0m' + const flagNote = check.compileFlag ? ` [needs feature('${check.compileFlag}')]` : '' + + console.log(` ${status} ${check.name}: ${check.gate} = ${JSON.stringify(actual)}${flagNote}`) + + if (matches) pass++ + else fail++ + } + console.log() +} + +console.log(`\nResult: ${pass} passed, ${fail} failed out of ${pass + fail} gates`) + +if (fail > 0) { + console.log('\n\x1b[31mSome gates are not returning expected values!\x1b[0m') + console.log('If CLAUDE_CODE_DISABLE_LOCAL_GATES=1 is set, all gates will return defaults.') + process.exit(1) +} + +console.log('\n\x1b[32mAll GrowthBook gates returning expected local defaults.\x1b[0m') +console.log('\nNote: Compile-time feature() flags cannot be verified in this script.') +console.log('Use "bun run dev" and test manually for features with [needs feature()] markers.') diff --git a/src/services/analytics/growthbook.ts b/src/services/analytics/growthbook.ts index eead6c924..0baccb508 100644 --- a/src/services/analytics/growthbook.ts +++ b/src/services/analytics/growthbook.ts @@ -416,6 +416,71 @@ function syncRemoteEvalToDisk(): void { })) } +/** + * Local default overrides for GrowthBook feature gates. + * + * When GrowthBook is not connected (e.g. no 1P event logging, no adapter), + * these values are used instead of the hard-coded defaults (usually false). + * This allows enabling features that have real implementations without + * requiring a GrowthBook server connection. + * + * Set CLAUDE_CODE_DISABLE_LOCAL_GATES=1 to bypass these defaults. + * + * Categories: + * P0 — Pure local features (no external dependencies) + * P1 — Requires Claude API (works with any valid API key) + * KS — Kill switches (default true, keep them true) + */ +const LOCAL_GATE_DEFAULTS: Record = { + // ── P0: Pure local features ────────────────────────────────────── + tengu_keybinding_customization_release: true, // Custom keybindings + tengu_streaming_tool_execution2: true, // Streaming tool execution + tengu_kairos_cron: true, // Cron/scheduled tasks + tengu_amber_json_tools: true, // Token-efficient JSON tools (~4.5% savings) + tengu_immediate_model_command: true, // Instant /model, /fast, /effort during query + tengu_basalt_3kr: true, // MCP instructions delta (send only changes) + tengu_pebble_leaf_prune: true, // Session storage leaf pruning + tengu_chair_sermon: true, // Message smooshing (merge adjacent blocks) + tengu_lodestone_enabled: true, // Deep link protocol (claude://) + tengu_auto_background_agents: true, // Auto-background agents after 120s + tengu_fgts: true, // Fine-grained tool state in system prompt + + // ── P1: API-dependent features ─────────────────────────────────── + tengu_session_memory: true, // Session memory (cross-session persistence) + tengu_passport_quail: true, // Auto memory extraction + tengu_moth_copse: true, // Skip memory index, use prefetched memories + tengu_coral_fern: true, // "Searching past context" section + tengu_chomp_inflection: true, // Prompt suggestions + tengu_hive_evidence: true, // Verification agent + tengu_kairos_brief: true, // Brief mode + tengu_sedge_lantern: true, // Away summary + tengu_onyx_plover: JSON.stringify({ enabled: true }), // Auto dream (memory consolidation) + tengu_willow_mode: 'dialog', // Idle return prompt + + // ── Kill switches (keep true to prevent remote disable) ────────── + tengu_turtle_carbon: true, // Ultrathink extended thinking + tengu_amber_stoat: true, // Built-in Explore/Plan agents + tengu_amber_flint: true, // Agent teams/swarms + tengu_slim_subagent_claudemd: true, // Slim CLAUDE.md for subagents + tengu_birch_trellis: true, // Tree-sitter bash security analysis + tengu_collage_kaleidoscope: true, // macOS clipboard image reading + tengu_compact_cache_prefix: true, // Reuse prompt cache during compaction + tengu_kairos_cron_durable: true, // Persistent cron tasks + tengu_attribution_header: true, // API request attribution header + tengu_slate_prism: true, // Agent progress summaries +} + +/** + * Look up a local gate default. Returns undefined if not configured, + * allowing the caller to fall through to the original defaultValue. + */ +function getLocalGateDefault(feature: string): unknown | undefined { + if (process.env.CLAUDE_CODE_DISABLE_LOCAL_GATES) { + return undefined + } + return LOCAL_GATE_DEFAULTS[feature] +} + /** * Check if GrowthBook operations should be enabled */ @@ -500,11 +565,13 @@ const getGrowthBookClient = memoize( const attributes = getUserAttributes() const clientKey = getGrowthBookClientKey() const baseUrl = - process.env.CLAUDE_GB_ADAPTER_URL - || (process.env.USER_TYPE === 'ant' - ? process.env.CLAUDE_CODE_GB_BASE_URL || 'https://api.anthropic.com/' - : 'https://api.anthropic.com/') - const isAdapterMode = !!(process.env.CLAUDE_GB_ADAPTER_URL && process.env.CLAUDE_GB_ADAPTER_KEY) + process.env.CLAUDE_GB_ADAPTER_URL || + (process.env.USER_TYPE === 'ant' + ? process.env.CLAUDE_CODE_GB_BASE_URL || 'https://api.anthropic.com/' + : 'https://api.anthropic.com/') + const isAdapterMode = !!( + process.env.CLAUDE_GB_ADAPTER_URL && process.env.CLAUDE_GB_ADAPTER_KEY + ) if (process.env.USER_TYPE === 'ant') { logForDebugging( `GrowthBook: Creating client with clientKey=${clientKey}, attributes: ${jsonStringify(attributes)}`, @@ -537,7 +604,9 @@ const getGrowthBookClient = memoize( // remoteEval only works with Anthropic internal API, GrowthBook Cloud doesn't support it remoteEval: !isAdapterMode, // cacheKeyAttributes only valid with remoteEval - ...(!isAdapterMode ? { cacheKeyAttributes: ['id', 'organizationUUID'] } : {}), + ...(!isAdapterMode + ? { cacheKeyAttributes: ['id', 'organizationUUID'] } + : {}), // Add auth headers if available ...(authHeaders.error ? {} @@ -691,7 +760,8 @@ async function getFeatureValueInternal( } if (!isGrowthBookEnabled()) { - return defaultValue + const localDefault = getLocalGateDefault(feature) + return localDefault !== undefined ? (localDefault as T) : defaultValue } const growthBookClient = await initializeGrowthBook() @@ -754,7 +824,8 @@ export function getFeatureValue_CACHED_MAY_BE_STALE( } if (!isGrowthBookEnabled()) { - return defaultValue + const localDefault = getLocalGateDefault(feature) + return localDefault !== undefined ? (localDefault as T) : defaultValue } // Log experiment exposure if data is available, otherwise defer until after init @@ -823,7 +894,8 @@ export function checkStatsigFeatureGate_CACHED_MAY_BE_STALE( } if (!isGrowthBookEnabled()) { - return false + const localDefault = getLocalGateDefault(gate) + return localDefault !== undefined ? Boolean(localDefault) : false } // Log experiment exposure if data is available, otherwise defer until after init @@ -923,7 +995,8 @@ export async function checkGate_CACHED_OR_BLOCKING( } if (!isGrowthBookEnabled()) { - return false + const localDefault = getLocalGateDefault(gate) + return localDefault !== undefined ? Boolean(localDefault) : false } // Fast path: disk cache already says true — trust it