Merge upstream/main into feat/docker-e2e-wrap-validation

Resolve the PR merge conflict by carrying forward the secure TLS fail-closed rtk download behavior, bring in the latest upstream proxy-handler updates, and fix the OpenClaw linked-install fallback so it copies the required hook-shim directory with regression coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
JerrettDavis 2026-04-07 23:54:05 -05:00
commit addcfcffcd
7 changed files with 107 additions and 8 deletions

View file

@ -436,14 +436,23 @@ def _copy_openclaw_plugin_into_extensions(
raise click.ClickException(
f"Plugin dist folder missing at {dist_dir}. Build the plugin first."
)
hook_shim_dir = plugin_dir / "hook-shim"
if not hook_shim_dir.exists():
raise click.ClickException(
f"Plugin hook-shim folder missing at {hook_shim_dir}. Build the plugin first."
)
extensions_dir = _resolve_openclaw_extensions_dir(openclaw_bin)
target_dir = extensions_dir / "headroom"
target_dist = target_dir / "dist"
target_hook_shim = target_dir / "hook-shim"
target_dir.mkdir(parents=True, exist_ok=True)
if target_dist.exists():
shutil.rmtree(target_dist)
if target_hook_shim.exists():
shutil.rmtree(target_hook_shim)
shutil.copytree(dist_dir, target_dist)
shutil.copytree(hook_shim_dir, target_hook_shim)
for filename in ("openclaw.plugin.json", "package.json", "README.md"):
source = plugin_dir / filename

View file

@ -681,6 +681,17 @@ class AnthropicHandlerMixin:
# Flag compression failure for observability
_compression_failed = True
# Guard: if "optimization" inflated tokens, revert to originals.
# Skip in cache mode where prefix-stability may legitimately shift counts.
if optimized_tokens > original_tokens and not is_cache_mode(self.config.mode):
logger.warning(
f"[{request_id}] Optimization inflated tokens "
f"({original_tokens} -> {optimized_tokens}), reverting to original messages"
)
optimized_messages = original_messages
optimized_tokens = original_tokens
transforms_applied = []
tokens_saved = max(0, original_tokens - optimized_tokens)
optimization_latency = (time.time() - start_time) * 1000
@ -1592,9 +1603,18 @@ class AnthropicHandlerMixin:
# Use pipeline's token counts for consistency with pipeline logs
original_tokens = result.tokens_before
optimized_tokens = result.tokens_after
# Guard: if "optimization" inflated tokens, revert to originals
if optimized_tokens > original_tokens:
logger.warning(
f"[{request_id}] Batch item optimization inflated tokens "
f"({original_tokens} -> {optimized_tokens}), reverting"
)
optimized_messages = messages
optimized_tokens = original_tokens
total_original_tokens += original_tokens
total_optimized_tokens += optimized_tokens
tokens_saved = max(0, original_tokens - optimized_tokens)
tokens_saved = original_tokens - optimized_tokens
total_tokens_saved += tokens_saved
# CCR Tool Injection: Inject retrieval tool if compression occurred

View file

@ -158,9 +158,18 @@ class BatchHandlerMixin:
# Use pipeline's token counts for consistency with pipeline logs
original_tokens = result.tokens_before
optimized_tokens = result.tokens_after
# Guard: if "optimization" inflated tokens, revert to originals
if optimized_tokens > original_tokens:
logger.warning(
f"[{request_id}] Batch item optimization inflated tokens "
f"({original_tokens} -> {optimized_tokens}), reverting"
)
optimized_messages = messages
optimized_tokens = original_tokens
total_original_tokens += original_tokens
total_optimized_tokens += optimized_tokens
tokens_saved = max(0, original_tokens - optimized_tokens)
tokens_saved = original_tokens - optimized_tokens
total_tokens_saved += tokens_saved
# CCR Tool Injection: Inject retrieval tool if compression occurred

View file

@ -265,7 +265,17 @@ class GeminiHandlerMixin:
_compression_failed = True
logger.warning(f"[{request_id}] Gemini optimization failed: {e}")
tokens_saved = max(0, original_tokens - optimized_tokens)
# Guard: if "optimization" inflated tokens, revert to originals
if optimized_tokens > original_tokens:
logger.warning(
f"[{request_id}] Optimization inflated tokens "
f"({original_tokens} -> {optimized_tokens}), reverting to original messages"
)
optimized_messages = messages
optimized_tokens = original_tokens
transforms_applied = []
tokens_saved = original_tokens - optimized_tokens
optimization_latency = (time.time() - start_time) * 1000
# Query Echo: disabled — hurts prefix caching in long conversations.

View file

@ -298,7 +298,17 @@ class OpenAIHandlerMixin:
# Flag compression failure for observability
_compression_failed = True
tokens_saved = max(0, original_tokens - optimized_tokens)
# Guard: if "optimization" inflated tokens, revert to originals
if optimized_tokens > original_tokens:
logger.warning(
f"[{request_id}] Optimization inflated tokens "
f"({original_tokens} -> {optimized_tokens}), reverting to original messages"
)
optimized_messages = original_messages
optimized_tokens = original_tokens
transforms_applied = []
tokens_saved = original_tokens - optimized_tokens
optimization_latency = (time.time() - start_time) * 1000
# Hook: post_compress
@ -795,7 +805,17 @@ class OpenAIHandlerMixin:
except Exception as e:
logger.warning(f"[{request_id}] Responses API optimization failed: {e}")
tokens_saved = max(0, original_tokens - optimized_tokens)
# Guard: if "optimization" inflated tokens, revert to originals
if optimized_tokens > original_tokens:
logger.warning(
f"[{request_id}] Optimization inflated tokens "
f"({original_tokens} -> {optimized_tokens}), reverting to original messages"
)
optimized_messages = messages
optimized_tokens = original_tokens
transforms_applied = []
tokens_saved = original_tokens - optimized_tokens
optimization_latency = (time.time() - start_time) * 1000
# Convert compressed messages back to Responses API items
@ -1060,7 +1080,15 @@ class OpenAIHandlerMixin:
if instructions and opt and opt[0].get("role") == "system":
body["instructions"] = opt[0]["content"]
opt = opt[1:]
body["input"] = messages_to_responses_items(opt, input_data, preserved)
if result.tokens_after <= original_tokens:
body["input"] = messages_to_responses_items(
opt, input_data, preserved
)
else:
logger.warning(
f"[{request_id}] WS optimization inflated tokens "
f"({original_tokens} -> {result.tokens_after}), reverting"
)
tokens_saved = max(0, original_tokens - result.tokens_after)
first_msg_raw = json.dumps(body)
logger.info(

View file

@ -78,8 +78,13 @@ def _setup_file_logging() -> None:
handler.setFormatter(
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
)
# Attach to the headroom root logger so all sub-loggers are captured
logging.getLogger("headroom").addHandler(handler)
# Attach to the headroom root logger so all sub-loggers are captured.
# Disable propagation to root to avoid duplicate writes when
# wrap.py redirects stderr to the same log file.
headroom_logger = logging.getLogger("headroom")
if not any(isinstance(h, RotatingFileHandler) for h in headroom_logger.handlers):
headroom_logger.addHandler(handler)
headroom_logger.propagate = False
except OSError:
# Non-fatal: can't write logs (read-only fs, permissions, etc.)
pass

View file

@ -25,6 +25,9 @@ def plugin_dir(tmp_path: Path) -> Path:
plugin.mkdir(parents=True)
(plugin / "package.json").write_text('{"name":"headroom-openclaw"}\n')
(plugin / "openclaw.plugin.json").write_text('{"id":"headroom"}\n')
hook_shim = plugin / "hook-shim"
hook_shim.mkdir()
(hook_shim / "index.js").write_text("export default {};\n")
return plugin
@ -416,11 +419,24 @@ def test_copy_openclaw_plugin_into_extensions_handles_missing_and_existing_dist(
(plugin / "package.json").write_text("{}\n")
(plugin / "openclaw.plugin.json").write_text("{}\n")
with patch("headroom.cli.wrap._resolve_openclaw_extensions_dir", return_value=tmp_path):
with pytest.raises(Exception, match="Plugin hook-shim folder missing"):
wrap_cli._copy_openclaw_plugin_into_extensions(
plugin_dir=plugin, openclaw_bin="openclaw"
)
hook_shim = plugin / "hook-shim"
hook_shim.mkdir()
(hook_shim / "index.js").write_text("shim\n")
ext_root = tmp_path / ".openclaw" / "extensions"
target_headroom = ext_root / "headroom"
target_dist = target_headroom / "dist"
target_hook_shim = target_headroom / "hook-shim"
target_dist.mkdir(parents=True)
(target_dist / "old.js").write_text("old\n")
target_hook_shim.mkdir(parents=True)
(target_hook_shim / "old.js").write_text("old-shim\n")
with patch("headroom.cli.wrap._resolve_openclaw_extensions_dir", return_value=ext_root):
out = wrap_cli._copy_openclaw_plugin_into_extensions(
@ -430,3 +446,5 @@ def test_copy_openclaw_plugin_into_extensions_handles_missing_and_existing_dist(
assert out == target_headroom
assert (target_dist / "index.js").exists()
assert not (target_dist / "old.js").exists()
assert (target_hook_shim / "index.js").exists()
assert not (target_hook_shim / "old.js").exists()