gemcode 0.3.66__tar.gz → 0.3.67__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gemcode-0.3.66/src/gemcode.egg-info → gemcode-0.3.67}/PKG-INFO +1 -1
- {gemcode-0.3.66 → gemcode-0.3.67}/pyproject.toml +1 -1
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/agent.py +4 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/callbacks.py +20 -4
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/config.py +6 -0
- gemcode-0.3.67/src/gemcode/dynamic_policy.py +117 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/__init__.py +6 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/bash.py +15 -2
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/filesystem.py +9 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/search.py +9 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/shell.py +10 -2
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/web.py +12 -0
- {gemcode-0.3.66 → gemcode-0.3.67/src/gemcode.egg-info}/PKG-INFO +1 -1
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode.egg-info/SOURCES.txt +1 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/LICENSE +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/MANIFEST.in +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/README.md +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/setup.cfg +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/__init__.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/__main__.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/audit.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/autocompact.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/capability_routing.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/cli.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/compaction.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/computer_use/__init__.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/computer_use/browser_computer.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/context_budget.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/context_warning.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/credentials.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/hitl_session.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/hooks.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/intent_classifier.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/interactions.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/invoke.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/kairos_daemon.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/limits.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/live_audio_engine.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/logging_config.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/mcp_loader.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/memory/__init__.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/memory/embedding_memory_service.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/memory/file_memory_service.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/modality_tools.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/model_errors.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/model_routing.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/openapi_loader.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/paths.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/permissions.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/plugins/__init__.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/pricing.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/prompt_suggestions.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/query/__init__.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/query/config.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/query/deps.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/query/engine.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/query/stop_hooks.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/query/token_budget.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/query/transitions.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/refine.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/repl_commands.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/repl_slash.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/review_agent.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/session_runtime.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/session_store.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/slash_commands.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/thinking.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tool_prompt_manifest.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tool_registry.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tool_result_store.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/browser.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/edit.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/notebook.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/notes.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/repo_map.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/shell_gate.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/subtask.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/tasks.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/think.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/todo.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools/web_search.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tools_inspector.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/trust.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tui/input_handler.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tui/scrollback.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tui/spinner.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tui/welcome_banner.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/tui/welcome_rich.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/version.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/vertex.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/web/__init__.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/web/claude_sse_adapter.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/web/terminal_repl.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode/workspace_hints.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode.egg-info/dependency_links.txt +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode.egg-info/entry_points.txt +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode.egg-info/requires.txt +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/src/gemcode.egg-info/top_level.txt +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_agent_instruction.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_autocompact.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_capability_routing.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_claude_web_adapter_sse.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_cli_init.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_computer_use_permissions.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_context_budget.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_context_warning.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_credentials.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_interactive_permission_ask.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_kairos_scheduler.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_modality_tools.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_model_error_retry.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_model_errors.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_model_routing.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_paths.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_permissions.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_prompt_suggestions.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_repl_commands.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_repl_slash.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_slash_commands.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_thinking_config.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_token_budget.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_tool_context_circulation.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_tools.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_tools_inspector.py +0 -0
- {gemcode-0.3.66 → gemcode-0.3.67}/tests/test_workspace_hints.py +0 -0
|
@@ -517,6 +517,10 @@ You have native deep thinking capability — use it actively:
|
|
|
517
517
|
- When something fails, diagnose (re-read the error, check assumptions) before switching strategy. Do not repeat the same failed call.
|
|
518
518
|
- When asked to analyse or explain something: read the actual files, produce concrete findings, not hypotheses.
|
|
519
519
|
|
|
520
|
+
## Token efficiency without losing intelligence
|
|
521
|
+
- Prefer **small, targeted tool outputs** by default (saves context, improves accuracy).
|
|
522
|
+
- If a tool output was **offloaded** (you see a `tool_result:<sha>` reference), and you need details, call `load_tool_result(ref)` and extract only the relevant slice.
|
|
523
|
+
|
|
520
524
|
## Tool selection guide (only when needed)
|
|
521
525
|
|
|
522
526
|
Keep tool usage minimal. Prefer short, targeted calls and keep tool outputs small.
|
|
@@ -315,10 +315,18 @@ def make_after_tool_callback(cfg: GemCodeConfig):
|
|
|
315
315
|
name = getattr(tool, "name", None) or ""
|
|
316
316
|
|
|
317
317
|
# Offload oversized tool outputs to disk (stable refs) before truncation.
|
|
318
|
+
# Dynamic caps for tool inline payload size.
|
|
319
|
+
effective_tool_chars = int(getattr(cfg, "tool_result_max_chars", 0) or 0)
|
|
320
|
+
try:
|
|
321
|
+
from gemcode.dynamic_policy import get_dynamic_caps
|
|
322
|
+
effective_tool_chars = get_dynamic_caps(cfg).tool_inline_chars
|
|
323
|
+
except Exception:
|
|
324
|
+
pass
|
|
325
|
+
|
|
318
326
|
if (
|
|
319
327
|
isinstance(tool_response, dict)
|
|
320
328
|
and getattr(cfg, "tool_result_offload_enabled", False)
|
|
321
|
-
and
|
|
329
|
+
and effective_tool_chars > 0
|
|
322
330
|
):
|
|
323
331
|
try:
|
|
324
332
|
from gemcode.tool_result_store import maybe_offload_tool_result
|
|
@@ -326,7 +334,7 @@ def make_after_tool_callback(cfg: GemCodeConfig):
|
|
|
326
334
|
project_root=cfg.project_root,
|
|
327
335
|
tool_name=name,
|
|
328
336
|
payload=tool_response,
|
|
329
|
-
max_inline_chars=int(
|
|
337
|
+
max_inline_chars=int(effective_tool_chars),
|
|
330
338
|
)
|
|
331
339
|
if did and isinstance(new_payload, dict):
|
|
332
340
|
tool_response = new_payload
|
|
@@ -334,9 +342,9 @@ def make_after_tool_callback(cfg: GemCodeConfig):
|
|
|
334
342
|
except Exception:
|
|
335
343
|
pass
|
|
336
344
|
|
|
337
|
-
if isinstance(tool_response, dict) and
|
|
345
|
+
if isinstance(tool_response, dict) and effective_tool_chars > 0:
|
|
338
346
|
new_d, did = truncate_tool_result_dict(
|
|
339
|
-
tool_response, int(
|
|
347
|
+
tool_response, int(effective_tool_chars)
|
|
340
348
|
)
|
|
341
349
|
if did:
|
|
342
350
|
tool_response = new_d
|
|
@@ -532,6 +540,14 @@ def make_after_model_callback(cfg: GemCodeConfig):
|
|
|
532
540
|
st[_LAST_PROMPT_TOKENS] = pt
|
|
533
541
|
st[_LAST_CONTEXT_PCT] = cw.get("percent_left")
|
|
534
542
|
st[_LAST_CONTEXT_LEVEL] = level
|
|
543
|
+
# Expose to tool layer (dynamic token policy).
|
|
544
|
+
try:
|
|
545
|
+
pct = cw.get("percent_left")
|
|
546
|
+
if isinstance(pct, int):
|
|
547
|
+
object.__setattr__(cfg, "_context_percent_left", pct)
|
|
548
|
+
object.__setattr__(cfg, "_context_alert_level", int(level))
|
|
549
|
+
except Exception:
|
|
550
|
+
pass
|
|
535
551
|
append_audit(
|
|
536
552
|
cfg.project_root,
|
|
537
553
|
{
|
|
@@ -137,6 +137,12 @@ class GemCodeConfig:
|
|
|
137
137
|
tool_result_offload_enabled: bool = field(
|
|
138
138
|
default_factory=lambda: _truthy_env("GEMCODE_TOOL_RESULT_OFFLOAD", default=True)
|
|
139
139
|
)
|
|
140
|
+
|
|
141
|
+
# Dynamic token policy: adapt tool output caps to context pressure so we stay
|
|
142
|
+
# cheap when context is tight, but remain evidence-rich when there's room.
|
|
143
|
+
dynamic_token_policy: bool = field(
|
|
144
|
+
default_factory=lambda: _truthy_env("GEMCODE_DYNAMIC_TOKEN_POLICY", default=True)
|
|
145
|
+
)
|
|
140
146
|
# Trim oldest text in llm_request.contents when over budget (see context_budget.py).
|
|
141
147
|
context_shrink_enabled: bool = field(
|
|
142
148
|
default_factory=lambda: _truthy_env("GEMCODE_CONTEXT_SHRINK", default=True)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dynamic token budgeting / caps.
|
|
3
|
+
|
|
4
|
+
Optimization must not make the agent dumb:
|
|
5
|
+
- When context pressure is low, allow richer tool outputs and wider reads.
|
|
6
|
+
- When context pressure is high, tighten caps and offload aggressively.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _truthy(v: Any, *, default: bool = False) -> bool:
|
|
16
|
+
if v is None:
|
|
17
|
+
return default
|
|
18
|
+
if isinstance(v, bool):
|
|
19
|
+
return v
|
|
20
|
+
if isinstance(v, str):
|
|
21
|
+
return v.lower() in ("1", "true", "yes", "on")
|
|
22
|
+
return bool(v)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _pct_left(cfg) -> int | None:
|
|
26
|
+
try:
|
|
27
|
+
v = getattr(cfg, "_context_percent_left", None)
|
|
28
|
+
if isinstance(v, int):
|
|
29
|
+
return v
|
|
30
|
+
except Exception:
|
|
31
|
+
return None
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class DynamicCaps:
|
|
37
|
+
tool_inline_chars: int
|
|
38
|
+
read_file_max_bytes: int
|
|
39
|
+
web_fetch_max_chars: int
|
|
40
|
+
bash_stdout_chars: int
|
|
41
|
+
bash_stderr_chars: int
|
|
42
|
+
run_stdout_chars: int
|
|
43
|
+
run_stderr_chars: int
|
|
44
|
+
grep_max_matches: int
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_dynamic_caps(cfg) -> DynamicCaps:
|
|
48
|
+
"""
|
|
49
|
+
Compute caps based on current context pressure.
|
|
50
|
+
|
|
51
|
+
Policy:
|
|
52
|
+
- Healthy (>=45% left): generous caps (better evidence, less re-asking).
|
|
53
|
+
- Warning (20-44%): moderate caps.
|
|
54
|
+
- Tight (<20%): strict caps + prefer offload.
|
|
55
|
+
"""
|
|
56
|
+
# cfg can be None in some tool contexts; treat as enabled with defaults.
|
|
57
|
+
enabled = _truthy(getattr(cfg, "dynamic_token_policy", True) if cfg is not None else True, default=True)
|
|
58
|
+
if not enabled:
|
|
59
|
+
# Essentially "no-op" high caps; tools still apply their explicit maxes.
|
|
60
|
+
return DynamicCaps(
|
|
61
|
+
tool_inline_chars=int(getattr(cfg, "tool_result_max_chars", 12000) or 12000),
|
|
62
|
+
read_file_max_bytes=200_000,
|
|
63
|
+
web_fetch_max_chars=40_000,
|
|
64
|
+
bash_stdout_chars=80_000,
|
|
65
|
+
bash_stderr_chars=20_000,
|
|
66
|
+
run_stdout_chars=50_000,
|
|
67
|
+
run_stderr_chars=50_000,
|
|
68
|
+
grep_max_matches=80,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
pct = _pct_left(cfg) if cfg is not None else None
|
|
72
|
+
if pct is None:
|
|
73
|
+
pct = 35
|
|
74
|
+
|
|
75
|
+
# Base knobs from config (so users can still tune globally).
|
|
76
|
+
base_tool = int(getattr(cfg, "tool_result_max_chars", 12000) or 12000) if cfg is not None else 12000
|
|
77
|
+
base_tool = max(1000, base_tool)
|
|
78
|
+
|
|
79
|
+
if pct >= 45:
|
|
80
|
+
mult = 1.4
|
|
81
|
+
return DynamicCaps(
|
|
82
|
+
tool_inline_chars=min(24_000, int(base_tool * mult)),
|
|
83
|
+
read_file_max_bytes=140_000,
|
|
84
|
+
web_fetch_max_chars=30_000,
|
|
85
|
+
bash_stdout_chars=30_000,
|
|
86
|
+
bash_stderr_chars=15_000,
|
|
87
|
+
run_stdout_chars=30_000,
|
|
88
|
+
run_stderr_chars=30_000,
|
|
89
|
+
grep_max_matches=60,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if pct >= 20:
|
|
93
|
+
mult = 1.0
|
|
94
|
+
return DynamicCaps(
|
|
95
|
+
tool_inline_chars=min(18_000, int(base_tool * mult)),
|
|
96
|
+
read_file_max_bytes=80_000,
|
|
97
|
+
web_fetch_max_chars=20_000,
|
|
98
|
+
bash_stdout_chars=20_000,
|
|
99
|
+
bash_stderr_chars=10_000,
|
|
100
|
+
run_stdout_chars=20_000,
|
|
101
|
+
run_stderr_chars=20_000,
|
|
102
|
+
grep_max_matches=40,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Tight
|
|
106
|
+
mult = 0.6
|
|
107
|
+
return DynamicCaps(
|
|
108
|
+
tool_inline_chars=max(2000, int(base_tool * mult)),
|
|
109
|
+
read_file_max_bytes=35_000,
|
|
110
|
+
web_fetch_max_chars=10_000,
|
|
111
|
+
bash_stdout_chars=10_000,
|
|
112
|
+
bash_stderr_chars=8_000,
|
|
113
|
+
run_stdout_chars=10_000,
|
|
114
|
+
run_stderr_chars=10_000,
|
|
115
|
+
grep_max_matches=20,
|
|
116
|
+
)
|
|
117
|
+
|
|
@@ -84,6 +84,12 @@ def build_function_tools(cfg: GemCodeConfig, *, include_subtask: bool = True) ->
|
|
|
84
84
|
load_tool_result = _make_load_tool_result_tool(cfg)
|
|
85
85
|
repo_map = make_repo_map_tool(cfg)
|
|
86
86
|
|
|
87
|
+
# Attach cfg for dynamic policy inside web_fetch (no cfg param in signature).
|
|
88
|
+
try:
|
|
89
|
+
setattr(web_fetch, "_cfg", cfg)
|
|
90
|
+
except Exception:
|
|
91
|
+
pass
|
|
92
|
+
|
|
87
93
|
# bash and run_command are the most common long-running tools (builds, tests,
|
|
88
94
|
# installs). Wrap them with LongRunningFunctionTool so ADK can handle slow
|
|
89
95
|
# processes without hitting streaming timeouts.
|
|
@@ -182,8 +182,21 @@ def make_bash_tool(cfg: GemCodeConfig):
|
|
|
182
182
|
env=env,
|
|
183
183
|
check=False,
|
|
184
184
|
)
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
try:
|
|
186
|
+
from gemcode.dynamic_policy import get_dynamic_caps
|
|
187
|
+
caps = get_dynamic_caps(cfg)
|
|
188
|
+
stdout_cap = caps.bash_stdout_chars
|
|
189
|
+
stderr_cap = caps.bash_stderr_chars
|
|
190
|
+
except Exception:
|
|
191
|
+
stdout_cap = 20_000
|
|
192
|
+
stderr_cap = 10_000
|
|
193
|
+
|
|
194
|
+
# Keep more stderr when failing; it is usually the most informative.
|
|
195
|
+
if proc.returncode != 0:
|
|
196
|
+
stderr_cap = max(stderr_cap, 12_000)
|
|
197
|
+
|
|
198
|
+
stdout = proc.stdout[:stdout_cap]
|
|
199
|
+
stderr = proc.stderr[:stderr_cap]
|
|
187
200
|
result: dict = {
|
|
188
201
|
"command": command,
|
|
189
202
|
"cwd": str(exec_cwd.relative_to(root)) if exec_cwd != root else ".",
|
|
@@ -43,6 +43,15 @@ def make_filesystem_tools(cfg: GemCodeConfig):
|
|
|
43
43
|
return {"error": str(e)}
|
|
44
44
|
if not p.is_file():
|
|
45
45
|
return {"error": f"Not a file: {path}"}
|
|
46
|
+
|
|
47
|
+
# Dynamic caps: allow bigger reads when context is healthy, tighten when tight.
|
|
48
|
+
try:
|
|
49
|
+
from gemcode.dynamic_policy import get_dynamic_caps
|
|
50
|
+
caps = get_dynamic_caps(cfg)
|
|
51
|
+
if isinstance(max_bytes, int) and max_bytes > caps.read_file_max_bytes:
|
|
52
|
+
max_bytes = caps.read_file_max_bytes
|
|
53
|
+
except Exception:
|
|
54
|
+
pass
|
|
46
55
|
total_bytes = p.stat().st_size
|
|
47
56
|
data = p.read_bytes()
|
|
48
57
|
text_full = data.decode("utf-8", errors="replace")
|
|
@@ -61,6 +61,15 @@ def make_grep_tool(cfg: GemCodeConfig):
|
|
|
61
61
|
Issue multiple grep_content calls in the same turn when searching for
|
|
62
62
|
different patterns — they run in parallel.
|
|
63
63
|
"""
|
|
64
|
+
# Dynamic caps: allow richer search when context is healthy.
|
|
65
|
+
try:
|
|
66
|
+
from gemcode.dynamic_policy import get_dynamic_caps
|
|
67
|
+
caps = get_dynamic_caps(cfg)
|
|
68
|
+
if isinstance(max_matches, int) and max_matches > caps.grep_max_matches:
|
|
69
|
+
max_matches = caps.grep_max_matches
|
|
70
|
+
except Exception:
|
|
71
|
+
pass
|
|
72
|
+
|
|
64
73
|
if max_matches < 1:
|
|
65
74
|
max_matches = 1
|
|
66
75
|
if max_matches > 500:
|
|
@@ -157,12 +157,20 @@ def make_run_command(cfg: GemCodeConfig):
|
|
|
157
157
|
env=child_env,
|
|
158
158
|
check=False,
|
|
159
159
|
)
|
|
160
|
+
try:
|
|
161
|
+
from gemcode.dynamic_policy import get_dynamic_caps
|
|
162
|
+
caps = get_dynamic_caps(cfg)
|
|
163
|
+
out_cap = caps.run_stdout_chars
|
|
164
|
+
err_cap = caps.run_stderr_chars
|
|
165
|
+
except Exception:
|
|
166
|
+
out_cap = 20_000
|
|
167
|
+
err_cap = 20_000
|
|
160
168
|
return {
|
|
161
169
|
"command": [exe, *args],
|
|
162
170
|
"cwd": str(exec_cwd.relative_to(root)) if exec_cwd != root else ".",
|
|
163
171
|
"exit_code": proc.returncode,
|
|
164
|
-
"stdout": proc.stdout[:
|
|
165
|
-
"stderr": proc.stderr[:
|
|
172
|
+
"stdout": proc.stdout[:out_cap],
|
|
173
|
+
"stderr": proc.stderr[:err_cap],
|
|
166
174
|
}
|
|
167
175
|
except subprocess.TimeoutExpired:
|
|
168
176
|
return {"error": f"Timeout after {timeout_seconds}s"}
|
|
@@ -71,6 +71,18 @@ def make_web_fetch_tool():
|
|
|
71
71
|
url = url.strip()
|
|
72
72
|
if not url.startswith(("http://", "https://")):
|
|
73
73
|
return {"error": "Only http:// and https:// URLs are supported"}
|
|
74
|
+
try:
|
|
75
|
+
from gemcode.dynamic_policy import get_dynamic_caps
|
|
76
|
+
caps = get_dynamic_caps(getattr(web_fetch, "_cfg", None) or None) # type: ignore[arg-type]
|
|
77
|
+
except Exception:
|
|
78
|
+
caps = None
|
|
79
|
+
try:
|
|
80
|
+
# make_web_fetch_tool() has no cfg, so we attach one in the builder.
|
|
81
|
+
# If present, apply the dynamic cap.
|
|
82
|
+
if caps is not None and max_chars > getattr(caps, "web_fetch_max_chars", 20_000):
|
|
83
|
+
max_chars = int(getattr(caps, "web_fetch_max_chars", 20_000))
|
|
84
|
+
except Exception:
|
|
85
|
+
pass
|
|
74
86
|
if max_chars < 1000:
|
|
75
87
|
max_chars = 1000
|
|
76
88
|
if max_chars > 200_000:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|