feat(ch16/phase5): CCR upstream proxy (CONNECT-over-WS relay + 6-step setup) - #42
Merged
ericleepi314 merged 1 commit intoMay 10, 2026
Conversation
… setup)
Phase 5 of the Ch16 port — credential injection for CCR sessions
running inside containers. Self-contained; depends only on Phase 2's
NO_PROXY allowlist.
* src/upstreamproxy/protobuf_codec.py — hand-encoded
UpstreamProxyChunk codec (tag 0x0a + varint + payload). 16 LOC of
bit manipulation replaces a protobuf runtime dep.
* src/upstreamproxy/ptrace_guard.py — set_non_dumpable() calls
libc.prctl(PR_SET_DUMPABLE, 0, ...) via ctypes on Linux to block
same-UID ptrace of the agent's heap (chapter §"Apply This" rule
#4 — "Keep secrets heap-only in adversarial environments").
No-op on macOS/Windows. Never raises.
* src/upstreamproxy/ca_bundle.py — is_valid_pem_content (regex match
for >=1 well-formed PEM block; security guard against compromised
server returning HTML/JSON) + download_ca_bundle (5 s timeout +
PEM validation + atomic write via tempfile + os.replace).
* src/upstreamproxy/relay.py — single-asyncio CONNECT-over-WS relay
(no Bun/Node fork). asyncio.start_server listens on 127.0.0.1:0;
per-connection: parse CRLFCRLF (8 KB cap) -> websockets.connect
upgrade with auth header -> bidirectional pump (encode_chunk on
write, decode_chunk on read) + 30 s app-level keepalive.
asyncio.wait(FIRST_COMPLETED) instead of TaskGroup so one pump
exiting tears down the others (caught a hang during dev).
* src/upstreamproxy/upstream_proxy.py — init_upstream_proxy 6-step
ordered setup: env-var gates (CLAUDE_CODE_REMOTE +
CCR_UPSTREAM_PROXY_ENABLED + CLAUDE_CODE_REMOTE_SESSION_ID) →
read /run/ccr/session_token → set_non_dumpable → download CA
bundle → start relay → unlink token file (only AFTER relay up).
Fail-open semantics throughout (chapter §"Apply This" rule #5).
get_upstream_proxy_env returns the 9-var dict for child
subprocesses, with parent-env-inheritance fallback.
64 tests pass + 1 Linux-only skip (prctl). 87% module coverage. Real
WS round-trip e2e test against an in-process echo server. Atomic-write
test simulates os.replace failure mid-write and verifies no leftover
.tmp file.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This was referenced May 10, 2026
ericleepi314
marked this pull request as ready for review
May 10, 2026 18:38
ericleepi314
added a commit
that referenced
this pull request
Jun 28, 2026
Re-examined the "external" classification: PrBadge is buildable via the gh CLI (already used by /pr-comments), not external. On startup, `gh pr view --json number,state` populates a footer badge (e.g. "⊟ #42", or "⊟ #42 merged"); absent when there's no PR or gh isn't available. Verified: StatusBar renders "⊟ #42" when a PR badge is set. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
peroxider
pushed a commit
to peroxider/clawcodex
that referenced
this pull request
Jul 7, 2026
…r-0573f4c
F-121: PR 代码检视意见规则回灌 — LLM Judge 架构升级
Created-by: evanTang
Commit-by: evanTang
Merged-by: chadwweng
Description: 背景
原有 ConflictJudge 协议采用逐对 LLM 判定:每对 candidate-existing 规则单独调用一次 LLM,耗时随规则数 O(n²) 增长。同时缺少统一的轻量 LLM 直连接口,各模块依赖 QueryR
unner / BaseProvider 构造。
变更概要
新增 2 文件,修改 3 文件,+682 / −215。
1. clawcodex_ext/llm.py — 轻量 LLM 直连接口
为所有组件提供统一的 LLM 调用入口,避免各模块重复构造 provider。
from clawcodex_ext.llm import llm_complete
answer = await llm_complete("prompt", model="deepseek-v4-flash")
特性:
•复用现有 build_provider_from_config() + BaseProvider.chat_async()
•支持 provider_name/model/temperature/max_tokens/system_prompt
•模块级缓存 default provider/model 名称(不缓存 provider 实例,配置变更即时生效)
•异常抛 RuntimeError,调用方自行决定降级策略
2. BatchedLLMJudge — 批量 LLM 判定(替换 ConflictJudge)
核心变更:将逐对判定改造为一次 batched LLM 调用,TF-IDF 保留为降级兜底。
新增组件:
┌────────────────────────┬───────────────────────────────────┐
│ 组件 │ 说明 │
├────────────────────────┼───────────────────────────────────┤
│ JudgeResult dataclass │ 判定结果(action + target_idx) │
├────────────────────────┼───────────────────────────────────┤
│ RuleJudge Protocol │ 批量判定接口 │
├────────────────────────┼───────────────────────────────────┤
│ BatchedLLMJudge │ 生产实现,调用 llm_complete │
├────────────────────────┼───────────────────────────────────┤
│ _apply_judge_results() │ 将 JudgeResult 回写到 merged 列表 │
└────────────────────────┴───────────────────────────────────┘
流水线变化:
改造前: extract → ConflictJudge(逐对LLM) → TF-IDF dedup+merge → 评分/修剪
改造后: extract → BatchedLLMJudge(一次LLM) → _apply_judge_results → 评分/修剪
↑ 失败降级
TF-IDF 兜底
删除的废弃代码:
•ConflictJudge 协议 + _DEFAULT_CONFLICT_JUDGE
•_find_conflict_pairs() 方法
•_detect_conflicts() 方法(上一轮误报源)
其他改进:
•apply() 新增 source 溯源参数(如 "PR agentforce314#42" → rule.source)
•extract() 补全 source: '' 默认字段
•is_user_managed() 改为仅检测首行头注释,兼容多行 header
测试覆盖
┌──────────────────────────────────────────┬─────────────────────────────┐
│ 文件 │ 数量 │
├──────────────────────────────────────────┼─────────────────────────────┤
│ tests/test_llm.py │ 11 │
├──────────────────────────────────────────┼─────────────────────────────┤
│ tests/orchestrator/test_rules_learner.py │ 77(含 LLM judge 路径测试) │
└──────────────────────────────────────────┴─────────────────────────────┘
合计 88 个测试覆盖正常调用、参数转发、异常降级、LLM 判定解析、各分支回写、TF-IDF 兜底。


See merge request: chadwweng/clawcodex!80
singlaamitesh
pushed a commit
to singlaamitesh/clawcodex
that referenced
this pull request
Jul 7, 2026
…e5-upstream-proxy feat(ch16/phase5): CCR upstream proxy (CONNECT-over-WS relay + 6-step setup)
singlaamitesh
pushed a commit
to singlaamitesh/clawcodex
that referenced
this pull request
Jul 7, 2026
…force314#550) Re-examined the "external" classification: PrBadge is buildable via the gh CLI (already used by /pr-comments), not external. On startup, `gh pr view --json number,state` populates a footer badge (e.g. "⊟ agentforce314#42", or "⊟ agentforce314#42 merged"); absent when there's no PR or gh isn't available. Verified: StatusBar renders "⊟ agentforce314#42" when a PR badge is set. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
ericleepi314
added a commit
that referenced
this pull request
Jul 21, 2026
Re-examined the "external" classification: PrBadge is buildable via the gh CLI (already used by /pr-comments), not external. On startup, `gh pr view --json number,state` populates a footer badge (e.g. "⊟ #42", or "⊟ #42 merged"); absent when there's no PR or gh isn't available. Verified: StatusBar renders "⊟ #42" when a PR badge is set. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR 3 of 6 in the Ch16 stack. Stacked on #41 (Phase 2). Rebase to
mainonce #40 + #41 land.Phase 5 — credential injection for CCR sessions running inside containers. Self-contained (depends only on Phase 2's
NO_PROXYallowlist); can ship in parallel with Phase 1, 3, 4.Modules added (
src/upstreamproxy/)protobuf_codec.pyUpstreamProxyChunkcodec (tag0x0a+ varint + payload). 16 LOC of bit manipulation replaces a protobuf runtime dep.ptrace_guard.pyset_non_dumpable()callslibc.prctl(PR_SET_DUMPABLE, 0, ...)viactypeson Linux to block same-UID ptrace of the agent's heap (chapter §"Apply This" rule #4). No-op on macOS/Windows. Never raises.ca_bundle.pyis_valid_pem_content(regex match for ≥1 well-formed PEM block — security guard against compromised server returning HTML/JSON) +download_ca_bundle(5 s timeout, PEM validation, atomic write via tempfile +os.replace).relay.pyasyncio.start_serverlistens on 127.0.0.1:0; per-connection: parse CRLFCRLF (8 KB cap) →websockets.connectupgrade → bidirectional pump + 30 s app-level keepalive.asyncio.wait(FIRST_COMPLETED)instead of TaskGroup so one pump exiting tears down the others (caught a hang during dev).upstream_proxy.pyinit_upstream_proxy6-step ordered setup: env-var gates (CLAUDE_CODE_REMOTE+CCR_UPSTREAM_PROXY_ENABLED+CLAUDE_CODE_REMOTE_SESSION_ID) → read/run/ccr/session_token→set_non_dumpable→ download CA bundle → start relay → unlink token file (only AFTER relay up).get_upstream_proxy_envreturns the 9-var dict for child subprocesses with parent-env-inheritance fallback.Notable correctness invariants
anthropic.com,.anthropic.com,*.anthropic.com) — different runtimes parse NO_PROXY differently; missing any one would MITM the model API itself.tempfile.mkstemp+os.replace; test verifies no leftover.tmpon simulated rename failure._DISABLEDsentinel; never raises (chapter §"Apply This" rule Add Adopt Me-style pet game demo #5).Test plan
pytest tests/upstreamproxy/— 64 pass + 1 skipped (Linux-onlyprctl)tests/upstreamproxy/test_relay_e2e.py(3 tests, markedintegration) — real WS round-trip against in-process echo servertests/upstreamproxy/test_ca_bundle.py::test_atomic_write_no_partial_file_on_crash— simulatesos.replacefailure mid-write, asserts no leftover.tmptests/upstreamproxy/test_upstream_proxy.py— every gate combination returns disabled state; relay-failure-is-fail-open verifies token file is preserved when relay startup raisesprctlpaths and rare WS-during-handshake errors🤖 Generated with Claude Code