Skip to content

Commit 5aba36b

Browse files
author
IronRod Ops
committed
Merge remote-tracking branch 'origin/main' into ironrod-local-patches
2 parents 02308f6 + 48c0c3a commit 5aba36b

59 files changed

Lines changed: 5239 additions & 572 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

agent/chat_completion_helpers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3270,6 +3270,10 @@ def _managed_summary_call(request, callback, *, retry_count: int):
32703270
# tool_call was summarized away; Responses API rejects that as
32713271
# "No tool call found for function call output".
32723272
api_messages = agent._sanitize_api_messages(api_messages)
3273+
# Same send-path vision eviction as the main loop (#89296).
3274+
from agent.context_compressor import evict_stale_outbound_tool_images
3275+
3276+
evict_stale_outbound_tool_images(api_messages)
32733277

32743278
# Same safety net as the main loop: drop thinking-only assistant
32753279
# turns so Anthropic-family providers don't 400 the summary call.

agent/context_compressor.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,6 +1772,23 @@ def _retire_stale_tool_result_images(
17721772
return pruned
17731773

17741774

1775+
def evict_stale_outbound_tool_images(
1776+
api_messages: List[Dict[str, Any]],
1777+
keep_newest: int = _MAX_KEEP_TOOL_IMAGES,
1778+
) -> int:
1779+
"""Drop stale screenshot/vision payloads from the per-call API copy.
1780+
1781+
Compression's keep-newest pass only runs when prune/compress fires, and
1782+
the Anthropic adapter's screenshot eviction only sees nested
1783+
``tool_result`` blocks. OpenAI-style ``image_url`` tool results
1784+
otherwise ride every subsequent request until a 413 forces the reactive
1785+
strip (#89286). Call this on the cloned ``api_messages`` list after
1786+
sanitization so older frames never leave the box (#89296). Do not pass
1787+
persisted history — the rewrite is send-path only.
1788+
"""
1789+
return _retire_stale_tool_result_images(api_messages, keep_newest=keep_newest)
1790+
1791+
17751792
def _truncate_tool_call_args_json(args: str, head_chars: int = 200) -> str:
17761793
"""Shrink long string values inside a tool-call arguments JSON blob while
17771794
preserving JSON validity.

agent/conversation_loop.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2743,6 +2743,13 @@ def run_conversation(
27432743
# gated on context_compressor — so orphans from session loading or
27442744
# manual message manipulation are always caught.
27452745
api_messages = agent._sanitize_api_messages(api_messages)
2746+
# Send-path vision eviction (#89296): compression only strips stale
2747+
# screenshots when prune fires, and the Anthropic adapter's keep-window
2748+
# never sees OpenAI-style tool-result image_url parts. The per-call
2749+
# clone is rewritten in place; persisted history is untouched.
2750+
from agent.context_compressor import evict_stale_outbound_tool_images
2751+
2752+
evict_stale_outbound_tool_images(api_messages)
27462753

27472754
# One-time repeated-heal escalation notice (#96870): if the sanitizer
27482755
# above just crossed the per-session heal threshold, deliver the

agent/relay_llm.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,19 @@ def _provider_request(
11191119
for key, value in headers.items()
11201120
if str(key).lower() not in _RELAY_INTERNAL_PROVIDER_HEADERS
11211121
}
1122+
# Relay's managed-call trace header maps to ``extra_headers`` for known SDK
1123+
# adapters and custom requests that already use that container. Other
1124+
# native transports receive protocol kwargs directly and may reject a new
1125+
# SDK-only argument. Preserve non-trace middleware headers as before.
1126+
supports_extra_headers = (
1127+
_relay_protocol(metadata) is not None or "extra_headers" in original
1128+
)
1129+
if headers and not supports_extra_headers:
1130+
headers = {
1131+
key: value
1132+
for key, value in headers.items()
1133+
if str(key).lower() != "traceparent"
1134+
}
11221135
if headers:
11231136
final["extra_headers"] = {
11241137
**dict(final.get("extra_headers") or {}),

agent/relay_runtime.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,8 @@ def acquire(
343343

344344
if self._activation is None:
345345
# Hermes only enters Relay's initialization path after an
346-
# explicit opt-in. Relay currently owns any subsequent ambient
347-
# layering; a future discovery=False API can make this exact.
346+
# explicit opt-in. Relay 0.8 no longer layers repository-local
347+
# configuration onto this explicitly selected payload.
348348
_resolve_plugin_awaitable(relay.plugin.initialize(plugin_config))
349349
except Exception as exc:
350350
self._activation = None

agent/relay_tools.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def execute(
2121
callback: Callable[[dict[str, Any]], Any],
2222
*,
2323
session_id: str,
24+
tool_call_id: str | None = None,
2425
metadata: dict[str, Any] | None = None,
2526
) -> tuple[Any, dict[str, Any]]:
2627
"""Run one tool call through Relay and return its final arguments."""
@@ -52,7 +53,7 @@ def guarded(final_args: dict[str, Any]) -> Any:
5253
raise
5354
raw_result["value"] = result
5455
raw_result["json"] = _jsonable(result)
55-
return raw_result["json"]
56+
return runtime.relay.ToolExecutionResult(raw_result["json"])
5657

5758
try:
5859
managed = _run_awaitable(
@@ -64,6 +65,7 @@ def guarded(final_args: dict[str, Any]) -> Any:
6465
invoke,
6566
handle=parent,
6667
metadata=_jsonable(metadata or {}),
68+
tool_call_id=tool_call_id or None,
6769
)
6870
)
6971
except BaseException as exc:
@@ -85,11 +87,12 @@ def guarded(final_args: dict[str, Any]) -> Any:
8587
return raw_result["value"], observed_args
8688
raise
8789

88-
if "value" in raw_result and _json_equal(managed, raw_result["json"]):
90+
managed_result = managed.result
91+
if "value" in raw_result and _json_equal(managed_result, raw_result["json"]):
8992
return raw_result["value"], observed_args
90-
if isinstance(managed, str):
91-
return managed, observed_args
92-
return json.dumps(_jsonable(managed), ensure_ascii=False), observed_args
93+
if isinstance(managed_result, str):
94+
return managed_result, observed_args
95+
return json.dumps(_jsonable(managed_result), ensure_ascii=False), observed_args
9396

9497

9598
def _jsonable(value: Any) -> Any:

agent/search_policy.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Shared directory pruning policy for broad recursive scans.
2+
3+
These names identify version-control internals, dependency trees, generated
4+
artifacts, caches, and backup copies that are not useful results for broad
5+
agent-facing discovery. Ordinary search callers may still target an explicit
6+
path; broad diagnostic probes should apply this policy to recursive walks.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
12+
# Keep this policy conservative and name-based so it works for local and remote
13+
# shell backends alike. The same set is used by context discovery and search
14+
# probes; adding a directory here protects every broad recursive consumer.
15+
SEARCH_PRUNE_DIR_NAMES = frozenset({
16+
# Version-control internals.
17+
".git", ".hg", ".svn",
18+
# Dependency and vendored trees.
19+
"node_modules", "venv", ".venv", "site-packages", "dist-packages",
20+
"vendor", "third_party",
21+
# Generated/build output.
22+
"build", "dist", "target", "out", "coverage",
23+
".next", ".turbo", ".parcel-cache", ".nuxt", ".svelte-kit",
24+
# Python and package-manager caches.
25+
"__pycache__", ".cache", ".Trash", ".tox", ".nox", ".mypy_cache",
26+
".pytest_cache", ".ruff_cache", ".npm", ".yarn", ".pnpm-store",
27+
".gradle", ".m2", ".nuget",
28+
# Backup copies.
29+
"backups", "backup", ".backups",
30+
})

agent/subdirectory_hints.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from typing import Dict, Any, Optional, Set
2222

2323
from agent.prompt_builder import _read_text_with_timeout, _scan_context_content
24+
from agent.search_policy import SEARCH_PRUNE_DIR_NAMES
2425

2526
logger = logging.getLogger(__name__)
2627

@@ -47,17 +48,9 @@
4748
# Prevents scanning all the way to / for deeply nested paths.
4849
_MAX_ANCESTOR_WALK = 5
4950

50-
# Directory names that never contain authoritative project context.
51-
# Backups, vendored deps, VCS internals, and caches routinely hold *copies* of
52-
# AGENTS.md; loading those duplicates real context and inflates the prompt.
53-
_EXCLUDED_DIR_NAMES = frozenset({
54-
"node_modules", "venv", ".venv", "__pycache__",
55-
".git", ".hg", ".svn",
56-
".Trash", ".cache", ".tox", ".mypy_cache", ".pytest_cache",
57-
"site-packages", "dist-packages",
58-
"backups", "backup", ".backups",
59-
"vendor", "third_party",
60-
})
51+
# Shared with broad recursive search probes so context discovery and search do
52+
# not drift into different dependency/cache/build trees.
53+
_EXCLUDED_DIR_NAMES = SEARCH_PRUNE_DIR_NAMES
6154

6255

6356
def _is_ancestor_or_same(a: Path, b: Path) -> bool:

agent/tool_executor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ def _hermes_pipeline(relay_args: dict[str, Any]) -> Any:
771771
function_args,
772772
_hermes_pipeline,
773773
session_id=str(getattr(agent, "session_id", "") or ""),
774+
tool_call_id=tool_call_id or None,
774775
metadata={
775776
"task_id": effective_task_id or "",
776777
"turn_id": getattr(agent, "_current_turn_id", "") or "",

agent/usage_pricing.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,10 @@ def get_pricing_entry(
12781278
)
12791279
if route.provider == "openrouter":
12801280
return _openrouter_pricing_entry(route)
1281+
1282+
bundled_entry = _lookup_official_docs_pricing(route)
1283+
if bundled_entry:
1284+
return bundled_entry
12811285
if route.base_url:
12821286
entry = _pricing_entry_from_metadata(
12831287
fetch_endpoint_model_metadata(route.base_url, api_key=api_key or ""),
@@ -1287,7 +1291,7 @@ def get_pricing_entry(
12871291
)
12881292
if entry:
12891293
return entry
1290-
return _lookup_official_docs_pricing(route)
1294+
return None
12911295

12921296

12931297
def normalize_usage(

0 commit comments

Comments
 (0)