code-context-control 2.136.0__py3-none-any.whl → 2.138.0__py3-none-any.whl
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.
- cli/c3.py +1 -1
- cli/guide/tools.html +1 -1
- cli/mcp_server.py +1 -1
- cli/tools/delegate.py +94 -15
- {code_context_control-2.136.0.dist-info → code_context_control-2.138.0.dist-info}/METADATA +1 -1
- {code_context_control-2.136.0.dist-info → code_context_control-2.138.0.dist-info}/RECORD +12 -12
- core/config.py +9 -1
- services/bench/delegate_eval.py +34 -3
- {code_context_control-2.136.0.dist-info → code_context_control-2.138.0.dist-info}/WHEEL +0 -0
- {code_context_control-2.136.0.dist-info → code_context_control-2.138.0.dist-info}/entry_points.txt +0 -0
- {code_context_control-2.136.0.dist-info → code_context_control-2.138.0.dist-info}/licenses/LICENSE +0 -0
- {code_context_control-2.136.0.dist-info → code_context_control-2.138.0.dist-info}/top_level.txt +0 -0
cli/c3.py
CHANGED
cli/guide/tools.html
CHANGED
|
@@ -940,7 +940,7 @@ c3_status(view=<span class="str">'sessions'</span>) <span class="com">#
|
|
|
940
940
|
<td class="param-name">tier</td>
|
|
941
941
|
<td class="param-type">string</td>
|
|
942
942
|
<td class="param-default">"small"</td>
|
|
943
|
-
<td class="param-desc"><code>small</code> · <code>medium</code> · <code>large</code> · <code>default</code>. Claude: Haiku / Sonnet / Opus. Codex and Grok: reasoning effort low / medium / high on the account's model. Gemini: flash-lite / flash / pro.</td>
|
|
943
|
+
<td class="param-desc"><code>small</code> · <code>medium</code> · <code>large</code> · <code>default</code>. Claude: Haiku (thinking capped at 1024 tokens) / Sonnet / Opus; a scout defaults to Sonnet, which finds things in far fewer turns. Codex and Grok: reasoning effort low / medium / high on the account's model. Gemini: flash-lite / flash / pro.</td>
|
|
944
944
|
</tr>
|
|
945
945
|
<tr>
|
|
946
946
|
<td class="param-name">model</td>
|
cli/mcp_server.py
CHANGED
|
@@ -809,7 +809,7 @@ async def c3_delegate(task: str, task_type: str = "ask", context: str = "",
|
|
|
809
809
|
ctx: Context = None) -> str:
|
|
810
810
|
"""OFFLOAD to another model — use when the subtask is local-model-sized or needs a different perspective.
|
|
811
811
|
backend: host (default: your own provider, one tier down — Claude Code->claude, Codex->codex, Grok->grok, Antigravity->gemini) | claude|codex|gemini|grok|ollama|auto. task_type: auto, summarize, explain, review, ask, test, diagnose, ping (live auth check), available, codex_check, gemini_check, grok_check, codex_resume.
|
|
812
|
-
tier: small (default) | medium | large | default. claude: haiku/sonnet/opus; codex/grok: reasoning effort; gemini: flash-lite/flash/pro. model overrides the tier. Context + file_path are packed by C3 under Access Guard; a file over 8k tokens goes as its map, so for line-level questions on a big file paste the excerpt or use scout=true.
|
|
812
|
+
tier: small (default; scout=true defaults to medium) | medium | large | default. claude: haiku/sonnet/opus; codex/grok: reasoning effort; gemini: flash-lite/flash/pro. model overrides the tier. Context + file_path are packed by C3 under Access Guard; a file over 8k tokens goes as its map, so for line-level questions on a big file paste the excerpt or use scout=true.
|
|
813
813
|
scout=true: the delegate may Read/Grep/Glob the project itself (claude: read-only, guard-denied paths refused; codex: its read-only sandbox) — for lookups when you cannot name the files.
|
|
814
814
|
grok runs read-only in a temp dir unless delegate.grok_allow_write=true (then --yolo in the project, loading its trusted .grok MCP servers and hooks).
|
|
815
815
|
allow_write_delegation: explicit user opt-in for write-capable backends (gemini/grok write mode/codex_resume) while Access Guard rules are active; codex is pinned read-only instead."""
|
cli/tools/delegate.py
CHANGED
|
@@ -431,7 +431,56 @@ def scout_settings(project_path) -> dict:
|
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
|
|
434
|
-
|
|
434
|
+
_EFFORT_LEVELS = frozenset({"low", "medium", "high", "xhigh", "max"})
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def claude_effort_for(tier: str, dcfg: dict) -> str:
|
|
438
|
+
"""``--effort`` for a resolved tier: ``claude_effort`` overrides every
|
|
439
|
+
tier, else ``claude_tier_effort[tier]``, else none (the CLI's default).
|
|
440
|
+
An unknown level is ignored rather than passed to argv."""
|
|
441
|
+
value = str(dcfg.get("claude_effort") or "").strip().lower()
|
|
442
|
+
if not value:
|
|
443
|
+
table = dcfg.get("claude_tier_effort") or {}
|
|
444
|
+
value = str(table.get(tier) or "").strip().lower() if isinstance(table, dict) else ""
|
|
445
|
+
return value if value in _EFFORT_LEVELS else ""
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def claude_thinking_cap_for(tier: str, dcfg: dict) -> int | None:
|
|
449
|
+
"""``MAX_THINKING_TOKENS`` for a resolved tier, or None (no cap).
|
|
450
|
+
|
|
451
|
+
``claude_thinking_tokens`` overrides every tier, else
|
|
452
|
+
``claude_tier_thinking_tokens[tier]``. 0 turns thinking off. Unlike
|
|
453
|
+
``--effort`` (no measurable effect on Haiku 4.5), this is what moves a
|
|
454
|
+
delegate's cost: one Haiku review went from 4,831 output tokens and 54 s
|
|
455
|
+
uncapped to 1,186 and 12 s at 1024. A negative or non-integer value is
|
|
456
|
+
ignored.
|
|
457
|
+
"""
|
|
458
|
+
value = dcfg.get("claude_thinking_tokens")
|
|
459
|
+
if value is None or value == "":
|
|
460
|
+
table = dcfg.get("claude_tier_thinking_tokens") or {}
|
|
461
|
+
value = table.get(tier) if isinstance(table, dict) else None
|
|
462
|
+
if value is None or value == "" or isinstance(value, bool):
|
|
463
|
+
return None
|
|
464
|
+
try:
|
|
465
|
+
cap = int(value)
|
|
466
|
+
except (TypeError, ValueError):
|
|
467
|
+
return None
|
|
468
|
+
return cap if cap >= 0 else None
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def claude_default_tier_key(scout: bool) -> str:
|
|
472
|
+
"""Which config key names the default Claude tier.
|
|
473
|
+
|
|
474
|
+
A scout defaults to its own key (medium): measured 2026-09-14, a Haiku
|
|
475
|
+
scout took 28 turns and $0.24 to find one function that Sonnet found in
|
|
476
|
+
8 turns for $0.046; the eval's canary case showed the same (15 turns
|
|
477
|
+
against 2).
|
|
478
|
+
"""
|
|
479
|
+
return "claude_scout_default_tier" if scout else "claude_default_tier"
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def resolve_claude_tier(tier: str, model: str, dcfg: dict,
|
|
483
|
+
scout: bool = False) -> tuple[str, str, str]:
|
|
435
484
|
"""(tier, model_arg, error). An explicit ``model`` wins over the tier.
|
|
436
485
|
|
|
437
486
|
``model_arg`` empty means omit --model. ``error`` is non-empty when the
|
|
@@ -441,7 +490,7 @@ def resolve_claude_tier(tier: str, model: str, dcfg: dict) -> tuple[str, str, st
|
|
|
441
490
|
if not _MODEL_NAME_RE.match(model):
|
|
442
491
|
return "", "", f"model name {model!r} is not a Claude model alias or id"
|
|
443
492
|
return "custom", model, ""
|
|
444
|
-
resolved = normalize_tier(tier, dcfg,
|
|
493
|
+
resolved = normalize_tier(tier, dcfg, claude_default_tier_key(scout))
|
|
445
494
|
if not resolved:
|
|
446
495
|
return "", "", f"unknown tier {tier!r} (use one of {', '.join(CLAUDE_TIERS)})"
|
|
447
496
|
table = {**CLAUDE_TIER_MODELS, **(dcfg.get("claude_tier_models") or {})}
|
|
@@ -483,10 +532,15 @@ def _claude_cmd(exe: str, model: str, system_prompt: str, *, effort: str = "",
|
|
|
483
532
|
return cmd
|
|
484
533
|
|
|
485
534
|
|
|
486
|
-
def _claude_env() -> dict:
|
|
535
|
+
def _claude_env(thinking_cap: int | None = None) -> dict:
|
|
487
536
|
env = _child_host_env("claude-code")
|
|
488
537
|
for name in _CLAUDE_NESTING_VARS:
|
|
489
538
|
env.pop(name, None)
|
|
539
|
+
# The caller's own MAX_THINKING_TOKENS must not leak into a delegate: the
|
|
540
|
+
# cap is decided per tier here, or not set at all.
|
|
541
|
+
env.pop("MAX_THINKING_TOKENS", None)
|
|
542
|
+
if thinking_cap is not None:
|
|
543
|
+
env["MAX_THINKING_TOKENS"] = str(int(thinking_cap))
|
|
490
544
|
return env
|
|
491
545
|
|
|
492
546
|
|
|
@@ -559,7 +613,8 @@ def parse_claude_json(stdout: str, requested_model: str = "") -> tuple[str, bool
|
|
|
559
613
|
def _run_claude(prompt: str, system_prompt: str, model: str = "", *,
|
|
560
614
|
timeout: int = 120, effort: str = "",
|
|
561
615
|
max_budget_usd: float = 0.0, scout_project: str | None = None,
|
|
562
|
-
settings: dict | None = None
|
|
616
|
+
settings: dict | None = None,
|
|
617
|
+
thinking_cap: int | None = None) -> tuple[str, bool, dict]:
|
|
563
618
|
"""Run the locked-down ``claude -p``.
|
|
564
619
|
|
|
565
620
|
Tool-less (default): in a throwaway directory. Scout (``scout_project``
|
|
@@ -580,7 +635,7 @@ def _run_claude(prompt: str, system_prompt: str, model: str = "", *,
|
|
|
580
635
|
harden_win_argv(cmd),
|
|
581
636
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE,
|
|
582
637
|
text=True, encoding="utf-8", errors="replace", cwd=workdir,
|
|
583
|
-
env=_claude_env(), **_popen_kwargs(),
|
|
638
|
+
env=_claude_env(thinking_cap), **_popen_kwargs(),
|
|
584
639
|
)
|
|
585
640
|
stdout, stderr, status = _communicate_with_heartbeat(
|
|
586
641
|
proc, timeout=timeout, idle_timeout=0, stdin_text=prompt)
|
|
@@ -671,7 +726,7 @@ def _handle_claude_delegate(task: str, task_type: str, context: str,
|
|
|
671
726
|
return finalize("c3_delegate", base,
|
|
672
727
|
"[delegate:error] Claude not enabled. Set delegate.claude_enabled=true "
|
|
673
728
|
"in .c3/config.json", "disabled")
|
|
674
|
-
resolved_tier, model_arg, problem = resolve_claude_tier(tier, model, dcfg)
|
|
729
|
+
resolved_tier, model_arg, problem = resolve_claude_tier(tier, model, dcfg, scout=scout)
|
|
675
730
|
if problem:
|
|
676
731
|
return finalize("c3_delegate", base, f"[delegate:error] {problem}", "error")
|
|
677
732
|
base["tier"] = resolved_tier
|
|
@@ -709,14 +764,19 @@ def _handle_claude_delegate(task: str, task_type: str, context: str,
|
|
|
709
764
|
rules = _CLAUDE_SCOUT_RULES if scout else _CLAUDE_DELEGATE_RULES
|
|
710
765
|
system_prompt = f"{tdef['system']} {rules}"
|
|
711
766
|
prompt = tdef["prompt_template"].format(context=enriched or "(none)", task=task)
|
|
712
|
-
effort =
|
|
767
|
+
effort = claude_effort_for(resolved_tier, dcfg)
|
|
768
|
+
if effort:
|
|
769
|
+
base["effort"] = effort
|
|
770
|
+
thinking_cap = claude_thinking_cap_for(resolved_tier, dcfg)
|
|
771
|
+
if thinking_cap is not None:
|
|
772
|
+
base["thinking_cap"] = thinking_cap
|
|
713
773
|
try:
|
|
714
774
|
budget = float(dcfg.get("claude_max_budget_usd") or 0.0)
|
|
715
775
|
except (TypeError, ValueError):
|
|
716
776
|
budget = 0.0
|
|
717
777
|
|
|
718
778
|
ckey = hashlib.md5(
|
|
719
|
-
f"claude|{resolved_tier}|{model_arg}|{effort}|{system_prompt}|{prompt}".encode()
|
|
779
|
+
f"claude|{resolved_tier}|{model_arg}|{effort}|{thinking_cap}|{system_prompt}|{prompt}".encode()
|
|
720
780
|
).hexdigest()
|
|
721
781
|
if not scout and ckey in _delegate_cache:
|
|
722
782
|
cached_resp, _ = _delegate_cache[ckey]
|
|
@@ -733,7 +793,7 @@ def _handle_claude_delegate(task: str, task_type: str, context: str,
|
|
|
733
793
|
output, ok, stats = _run_claude(prompt, system_prompt, model_arg, timeout=timeout,
|
|
734
794
|
effort=effort, max_budget_usd=budget,
|
|
735
795
|
scout_project=str(svc.project_path) if scout else None,
|
|
736
|
-
settings=settings)
|
|
796
|
+
settings=settings, thinking_cap=thinking_cap)
|
|
737
797
|
elapsed = round(time.monotonic() - t0, 1)
|
|
738
798
|
meta = {**base, "model": stats.pop("model", "") or model_arg or "cli-default",
|
|
739
799
|
"elapsed": f"{elapsed}s", **stats}
|
|
@@ -2096,6 +2156,11 @@ def delegate_telemetry_detail(meta: dict | None, status, *, requested_backend: s
|
|
|
2096
2156
|
for key in ("tier", "model", "mode", "effort"):
|
|
2097
2157
|
if meta.get(key):
|
|
2098
2158
|
detail[key] = str(meta[key])
|
|
2159
|
+
if meta.get("thinking_cap") is not None and not isinstance(meta.get("thinking_cap"), bool):
|
|
2160
|
+
try:
|
|
2161
|
+
detail["thinking_cap"] = int(meta["thinking_cap"])
|
|
2162
|
+
except (TypeError, ValueError):
|
|
2163
|
+
pass
|
|
2099
2164
|
if raw_status in _CONFIDENCE:
|
|
2100
2165
|
detail["confidence"] = raw_status
|
|
2101
2166
|
if meta.get("cached"):
|
|
@@ -2167,20 +2232,25 @@ def handle_delegate(task: str, task_type: str, context: str, file_path: str,
|
|
|
2167
2232
|
tier: str = "", model: str = "", scout: bool = False) -> str:
|
|
2168
2233
|
finalize = _telemetry_finalize(finalize, svc, requested_backend=backend,
|
|
2169
2234
|
task_type=task_type)
|
|
2235
|
+
route = {"backend": backend}
|
|
2170
2236
|
try:
|
|
2171
2237
|
return _route_delegate(task, task_type, context, file_path, svc, finalize, backend,
|
|
2172
|
-
allow_write_delegation, tier, model, scout)
|
|
2238
|
+
allow_write_delegation, tier, model, scout, route)
|
|
2173
2239
|
except access_guard.AccessDenied as exc:
|
|
2174
2240
|
# A guard refusal while packing file_path is the answer, not a crash:
|
|
2175
2241
|
# the refusal text goes back as the response (the agent reads the
|
|
2176
|
-
# same S1 line c3_read would give) and telemetry counts it as blocked
|
|
2177
|
-
|
|
2178
|
-
|
|
2242
|
+
# same S1 line c3_read would give) and telemetry counts it as blocked,
|
|
2243
|
+
# under the backend routing had chosen (not 'host' or 'auto').
|
|
2244
|
+
meta = {"task_type": task_type, "backend": route["backend"]}
|
|
2245
|
+
if route.get("tier"):
|
|
2246
|
+
meta["tier"] = route["tier"]
|
|
2247
|
+
return finalize("c3_delegate", meta, exc.message, "blocked")
|
|
2179
2248
|
|
|
2180
2249
|
|
|
2181
2250
|
def _route_delegate(task: str, task_type: str, context: str, file_path: str,
|
|
2182
2251
|
svc, finalize, backend: str, allow_write_delegation: bool,
|
|
2183
|
-
tier: str, model: str, scout: bool) -> str:
|
|
2252
|
+
tier: str, model: str, scout: bool, route: dict | None = None) -> str:
|
|
2253
|
+
route = route if route is not None else {}
|
|
2184
2254
|
if task_type == "ping":
|
|
2185
2255
|
finalize = _ping_finalize(finalize)
|
|
2186
2256
|
task, context, file_path, task_type = PING_TASK, "", "", "ask"
|
|
@@ -2264,6 +2334,8 @@ def _route_delegate(task: str, task_type: str, context: str, file_path: str,
|
|
|
2264
2334
|
provider, mapped = host_backend(svc)
|
|
2265
2335
|
if mapped:
|
|
2266
2336
|
host_tier = normalize_tier("", dcfg, "claude_default_tier" if mapped == "claude" else "")
|
|
2337
|
+
if mapped == "claude":
|
|
2338
|
+
host_tier += f", scout {normalize_tier('', dcfg, claude_default_tier_key(True))}"
|
|
2267
2339
|
lines.append(f" host={provider} -> {mapped} (default tier {host_tier}); "
|
|
2268
2340
|
"task_type='ping' makes a live call")
|
|
2269
2341
|
else:
|
|
@@ -2337,7 +2409,8 @@ def _route_delegate(task: str, task_type: str, context: str, file_path: str,
|
|
|
2337
2409
|
if mapped and not blocked:
|
|
2338
2410
|
backend = mapped
|
|
2339
2411
|
if not tier and not model:
|
|
2340
|
-
route_tier = normalize_tier(
|
|
2412
|
+
route_tier = normalize_tier(
|
|
2413
|
+
"", dcfg, claude_default_tier_key(scout) if mapped == "claude" else "")
|
|
2341
2414
|
else:
|
|
2342
2415
|
why = (f"host {provider} -> {mapped} blocked by Access Guard (write-capable)" if blocked
|
|
2343
2416
|
else f"host {provider or 'unknown'} has no same-provider backend")
|
|
@@ -2394,6 +2467,12 @@ def _route_delegate(task: str, task_type: str, context: str, file_path: str,
|
|
|
2394
2467
|
f"or run {backend} directly.",
|
|
2395
2468
|
"blocked")
|
|
2396
2469
|
|
|
2470
|
+
route["backend"] = backend
|
|
2471
|
+
if backend == "claude" and not model:
|
|
2472
|
+
route["tier"] = normalize_tier(route_tier, dcfg, claude_default_tier_key(scout))
|
|
2473
|
+
elif route_tier:
|
|
2474
|
+
route["tier"] = route_tier
|
|
2475
|
+
|
|
2397
2476
|
# Tier/model reach a handler only when asked for (or set by host routing),
|
|
2398
2477
|
# so an explicit backend with neither keeps its configured behaviour.
|
|
2399
2478
|
tier_kw = {k: v for k, v in (("tier", route_tier), ("model", model)) if v}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-context-control
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.138.0
|
|
4
4
|
Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
|
|
5
5
|
Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
cli/__init__.py,sha256=ec66drCZGNMRU4V6ov0zVhYZph1us12Vn8OvG_LJyRY,22
|
|
2
2
|
cli/_hook_utils.py,sha256=RhfS2WS5VMSi2w2Jxcts1wg5WC-UFp89JOQvcO5Jjb8,23002
|
|
3
3
|
cli/_shell_writes.py,sha256=XzUNlUDljmg-wZxK03xHnRe36WeQxe4tpO9HKVFwP5o,5090
|
|
4
|
-
cli/c3.py,sha256=
|
|
4
|
+
cli/c3.py,sha256=nIkboPZD_O0bgTT1Z1wTjBlYy0pNKeL7jXkYUum-Lr0,424321
|
|
5
5
|
cli/docs.html,sha256=pPxcP5ieY0DcfEDvegwIcRxbz1woLZoKLVSF-4JVy0Q,146784
|
|
6
6
|
cli/edits.html,sha256=UjAhoCmBmQ89cklGvJqzC6eyNP2tc8H6T-e01DVkLvE,43418
|
|
7
7
|
cli/hook_access_guard.py,sha256=nzPvuN1zsTiBmOopoYziXRatCeg_qhlKaQLW5lNyUqg,24729
|
|
@@ -25,7 +25,7 @@ cli/hook_terse_advisor.py,sha256=W_zaTfX2akgX-ifuUTsLu2H1atM8XmWt6jpPoeU7Pm0,625
|
|
|
25
25
|
cli/hub_server.py,sha256=Hr-vqN72FuSUVe4lDBSYHygNJZeQscX7iS5ZGxk-sko,196104
|
|
26
26
|
cli/hub_ui.html,sha256=nnGqWhWGCLfNisGUnoAlQFcBr6wPNsq-AEOR7uFtVkE,2942
|
|
27
27
|
cli/mcp_proxy.py,sha256=r-Qhlie7gCtotex8yQUudCyBLRUK-IW6cNIW5RhBW0o,17648
|
|
28
|
-
cli/mcp_server.py,sha256=
|
|
28
|
+
cli/mcp_server.py,sha256=5mMZqG0cmhbmROWTw-EAzyVMT5sg_lqsHr169ShQvMA,71489
|
|
29
29
|
cli/progress.py,sha256=qTkNy0howOD3hXraMo4ok3dYTpBW5yG_tenKfF4UDM0,1470
|
|
30
30
|
cli/server.py,sha256=vU50-A9PhEFhtcTH8U4VcNlIIglKFyBGk4qbBI_bgWo,173854
|
|
31
31
|
cli/ui.html,sha256=xcdt74nlFEXx-0Bx6-Okw-WSVZPAXL0iukxU0ytI6CA,5694
|
|
@@ -42,7 +42,7 @@ cli/guide/index.html,sha256=nNM_gAi3e8SGEUV7jIsd8N3kmAP35BepwmjZ2HuJoSA,22869
|
|
|
42
42
|
cli/guide/masking.html,sha256=Jz8fEwp5XwudUafn0Ymytofpf4PPWRpyxOHMl09dD5o,19203
|
|
43
43
|
cli/guide/oracle.html,sha256=66utgqoOpHXxZBEopnw-Q-tvhoQOD82YckXW2xDTTJ4,25152
|
|
44
44
|
cli/guide/shared.css,sha256=Mmm7W6aYxrkDq2RPOcyXiQFYGR-yB3auwX1uIWi6C74,15967
|
|
45
|
-
cli/guide/tools.html,sha256=
|
|
45
|
+
cli/guide/tools.html,sha256=38ES1AeC0AuFnxr-sLZNdTaAVyykpY_-W-AxNH2XVhY,95167
|
|
46
46
|
cli/guide/workflow.html,sha256=aN0eB1wMofm_ii02ydDCiR3FTbsEIuSCMCESCO-TEBA,64601
|
|
47
47
|
cli/hub_ui/app.js,sha256=Gbk-2DxqUynrVqJbS8pNUEHmcCpjuUhqeDxWUWa6e_A,6984
|
|
48
48
|
cli/hub_ui/state.js,sha256=QIRs_ooO8ODgdU0mwiwvIAz7IbiTs17fqLyolXbCeCI,7373
|
|
@@ -82,7 +82,7 @@ cli/tools/bitbucket.py,sha256=cni8Y1EoiNm9jcHEpzi2dgKFIT1-4ct0WOhpTs7ac3k,35812
|
|
|
82
82
|
cli/tools/ci.py,sha256=1--Ak18ikHkO7q4HWlDCVac-U969eEFejkd-24T0xuY,20057
|
|
83
83
|
cli/tools/compress.py,sha256=PcgxEw7KfofmJsTLfRf3mjWMxyNlqkcSNzMTlT27SW0,12534
|
|
84
84
|
cli/tools/credentials.py,sha256=R8Errpa41vsq0Z_sb7K-fmJxVovzXDYUyniITLrFC24,19033
|
|
85
|
-
cli/tools/delegate.py,sha256=
|
|
85
|
+
cli/tools/delegate.py,sha256=5a2ZwwNr1w0GfHZaUBMRBtEx2_EWqXKXhsb4GDYOp3Y,112693
|
|
86
86
|
cli/tools/edit.py,sha256=xnRRM2UUybvGvfSzXzIjWmt6_zNaCbkupiK1z-8K4jE,30127
|
|
87
87
|
cli/tools/edit_verify.py,sha256=L1a6U9Z-pAOQz0Ur9SFTNfmCl0OP9_4VdkyxlXOyHDM,11910
|
|
88
88
|
cli/tools/edits.py,sha256=zgVk5FrUmaXK1iJ9sUvz5OfwD0PLeordqnfe6Po7RDs,6271
|
|
@@ -125,10 +125,10 @@ cli/ui/components/sessions.js,sha256=FIKtil76B8tCkAmcFV7hlj6GQ_DCJK2jCzvEmdK7NBE
|
|
|
125
125
|
cli/ui/components/settings.js,sha256=FXcn-UQE2mXC6HFsLeEas-wAUFKt0OJaLySroZQMhGA,84095
|
|
126
126
|
cli/ui/components/sidebar.js,sha256=K2ym2kUgpbyG-EBA_wBIIiqQY8qTatsiBZwzseMWuIQ,9939
|
|
127
127
|
cli/ui/components/tasks.js,sha256=vyKQ3uwoppMwvdEaHlhWXW4oWcAisx4NveqzMhsYqHo,38438
|
|
128
|
-
code_context_control-2.
|
|
128
|
+
code_context_control-2.138.0.dist-info/licenses/LICENSE,sha256=l8Kh5QCNWNvR6kIt8L0BUZvc2LAFiHv2c-FnsGnUZf4,11301
|
|
129
129
|
core/__init__.py,sha256=TSDCEcM4V7gcZVM3w2ykJaqEUch4Dkon-rivV17T73s,2501
|
|
130
130
|
core/codex_patch.py,sha256=CxvT_rQIwPNTRJZj9EHJmx0ZdOeSLxjiixuLI3Z7lPc,2107
|
|
131
|
-
core/config.py,sha256=
|
|
131
|
+
core/config.py,sha256=KRCu5uo_7j-QhMJXXfFwc3f34gVitHv87ngrOqoAcR0,22615
|
|
132
132
|
core/grok_payload.py,sha256=EEwhvkKnCT6Rk9tc-DWDA__toE44244Xr4d6p7SoJwE,5365
|
|
133
133
|
core/hook_command.py,sha256=SvTDUaBLg1Qb4ZdAgpe14YA_8BSnkGJsd3ByPtqBsU0,1032
|
|
134
134
|
core/host.py,sha256=yBzjJjSs2ATyCpGD_IHOJ8n2Pn6RFPhnKHq819CFzC4,1585
|
|
@@ -295,7 +295,7 @@ services/vector_store.py,sha256=satc4MJanC1Wsu_iveMCUXG6K2fd0Ke87JSyy2BMikw,1304
|
|
|
295
295
|
services/watcher.py,sha256=WF6aTIUFzg5B5KepuLyUwmx8WvbmhT9IeenzdK0df3U,10779
|
|
296
296
|
services/win_subprocess.py,sha256=nt8Uwc2PrqsRW3-jcViEsoKj56GudW8TV3CQwryx8y4,4271
|
|
297
297
|
services/bench/__init__.py,sha256=WLEJIWJeaUj6FnH2nDO1qWugJDKfOKeM6WvKLSreYjI,231
|
|
298
|
-
services/bench/delegate_eval.py,sha256=
|
|
298
|
+
services/bench/delegate_eval.py,sha256=gi_cG8yYlYqBr_yOfyTibdeUJujYYFVyjcVqeIMXlUo,26072
|
|
299
299
|
services/bench/map_eval.py,sha256=i0M3hBJQM9wXX4dMB_2LjVKoFtXzFUKapoqjGB_8tcE,43456
|
|
300
300
|
services/bench/search_eval.py,sha256=HjVDoIs_uTvNhbT_YjhNjCPEweUG5goaUDaipOcjuF4,29234
|
|
301
301
|
services/bench/shell_eval.py,sha256=YW4qFO6JwmHuv-7_aaQG4QcDpXqnSEZ5uqFDigzTo_8,26723
|
|
@@ -320,8 +320,8 @@ tui/screens/search_view.py,sha256=MMHjVdlk3HZSuDBSvq8IGrqv_Mh5Us6YqXQ80bcWSMk,19
|
|
|
320
320
|
tui/screens/session_view.py,sha256=eZ1eDwHTvPOck1wCCviixtOaCxIkBT_95ytNNNriGNA,5991
|
|
321
321
|
tui/screens/stats.py,sha256=p81PjzdaIv7hllb8f45-rlVe4lJZwSdIMqu7e86_u5s,6223
|
|
322
322
|
tui/screens/ui_view.py,sha256=1QJCgLh2YfgWIpvzRG1KOGXYEaOYX6ojN61Azjf2oX0,2125
|
|
323
|
-
code_context_control-2.
|
|
324
|
-
code_context_control-2.
|
|
325
|
-
code_context_control-2.
|
|
326
|
-
code_context_control-2.
|
|
327
|
-
code_context_control-2.
|
|
323
|
+
code_context_control-2.138.0.dist-info/METADATA,sha256=338fmGaLU48EzbmUGfRBzGFJdUxfE3uDtCLpsfOIXcE,28433
|
|
324
|
+
code_context_control-2.138.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
325
|
+
code_context_control-2.138.0.dist-info/entry_points.txt,sha256=7kX_WUsDCF2hbXzvbNyscyaBb9AeA-DJY5v_5hN0DlU,93
|
|
326
|
+
code_context_control-2.138.0.dist-info/top_level.txt,sha256=wRt41zBybVF3qAiNXHz9BURbkKvUvfhmWWtKMhaw6eE,29
|
|
327
|
+
code_context_control-2.138.0.dist-info/RECORD,,
|
core/config.py
CHANGED
|
@@ -297,12 +297,20 @@ DELEGATE_DEFAULTS = {
|
|
|
297
297
|
# Claude Code CLI (same-provider tier downshift; locked-down, tool-less `claude -p`)
|
|
298
298
|
"claude_enabled": True, # Master switch — enable Claude as delegate backend
|
|
299
299
|
"claude_default_tier": "", # Empty = default_tier
|
|
300
|
+
"claude_scout_default_tier": "medium", # scout=true without tier/model: Sonnet (Haiku scouts flail: 28 turns vs 8)
|
|
300
301
|
"claude_tier_models": {"small": "haiku", "medium": "sonnet", "large": "opus"}, # CLI aliases, not ids
|
|
301
302
|
"claude_timeout": 120, # Subprocess timeout in seconds (no idle kill: JSON arrives at the end)
|
|
302
303
|
"claude_scout_timeout": 240, # scout=true runs look files up first, so they get longer
|
|
303
304
|
"claude_max_context_tokens": 24000, # Context cap after file packing
|
|
304
305
|
"claude_file_max_tokens": 8000, # A file_path entry over this is sent as its file map
|
|
305
|
-
"claude_effort": "", # --effort
|
|
306
|
+
"claude_effort": "", # --effort for every tier (overrides claude_tier_effort); empty = per tier
|
|
307
|
+
"claude_thinking_tokens": None, # MAX_THINKING_TOKENS for every tier (overrides the table); 0 = off
|
|
308
|
+
"claude_tier_thinking_tokens": {"small": 1024}, # MAX_THINKING_TOKENS per tier; unlisted = no cap.
|
|
309
|
+
# Haiku at 1024: real-file reviews ~55% cheaper, 2.6x faster, no
|
|
310
|
+
# accuracy loss seen. Sonnet 5 ignored nonzero caps, and 0 cost it depth.
|
|
311
|
+
"claude_tier_effort": {}, # --effort per tier, e.g. {"medium": "low"}; unlisted = CLI default
|
|
312
|
+
# (2026-09-14: --effort low changed nothing on Haiku 4.5 — same
|
|
313
|
+
# cost and output tokens over 22 eval cases — so no default)
|
|
306
314
|
"claude_max_budget_usd": 0, # --max-budget-usd per call; 0 = no cap
|
|
307
315
|
}
|
|
308
316
|
|
services/bench/delegate_eval.py
CHANGED
|
@@ -154,7 +154,8 @@ def resolve_suite(name_or_path: str) -> Path:
|
|
|
154
154
|
|
|
155
155
|
|
|
156
156
|
def parse_target(spec: str) -> tuple[str, str]:
|
|
157
|
-
"""``backend
|
|
157
|
+
"""``backend`` or ``backend:tier``, optionally with ``+scout`` / ``+think=N``
|
|
158
|
+
/ ``+effort=X`` flags -> (backend, tier)."""
|
|
158
159
|
head = str(spec).strip().split("+", 1)[0]
|
|
159
160
|
backend, _, tier = head.partition(":")
|
|
160
161
|
if not backend:
|
|
@@ -165,10 +166,18 @@ def parse_target(spec: str) -> tuple[str, str]:
|
|
|
165
166
|
# ── Checks ──────────────────────────────────────────────────────────────────
|
|
166
167
|
|
|
167
168
|
|
|
169
|
+
_EMPHASIS_RE = re.compile(r"\*\*|__")
|
|
170
|
+
|
|
171
|
+
|
|
168
172
|
def grade(case: DelegateCase, answer: str) -> list[str]:
|
|
169
|
-
"""Failed checks for ``answer`` (empty list = pass).
|
|
173
|
+
"""Failed checks for ``answer`` (empty list = pass).
|
|
174
|
+
|
|
175
|
+
Bold markers are removed before matching: "does **not** match" is the
|
|
176
|
+
same answer as "does not match" (a correct Haiku answer failed on
|
|
177
|
+
exactly that). Backticks stay — some checks are about code spans.
|
|
178
|
+
"""
|
|
170
179
|
failures: list[str] = []
|
|
171
|
-
text = answer or ""
|
|
180
|
+
text = _EMPHASIS_RE.sub("", answer or "")
|
|
172
181
|
flags = re.IGNORECASE | re.MULTILINE
|
|
173
182
|
for pattern in case.checks.get("must_match", []):
|
|
174
183
|
if not re.search(pattern, text, flags):
|
|
@@ -280,12 +289,34 @@ def build_eval_svc(project_path: str | Path, delegate_overrides: dict | None = N
|
|
|
280
289
|
)
|
|
281
290
|
|
|
282
291
|
|
|
292
|
+
_TARGET_OVERRIDES = {"think": "claude_thinking_tokens", "effort": "claude_effort"}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def target_overrides(target: str) -> dict:
|
|
296
|
+
"""Delegate-config overrides named in a target: ``+think=N`` sets
|
|
297
|
+
``claude_thinking_tokens``, ``+effort=low`` sets ``claude_effort``. So one
|
|
298
|
+
run can compare ``claude:small``, ``claude:small+think=1024`` and
|
|
299
|
+
``claude:small+think=0`` side by side."""
|
|
300
|
+
out: dict = {}
|
|
301
|
+
for flag in str(target).split("+")[1:]:
|
|
302
|
+
key, sep, value = flag.partition("=")
|
|
303
|
+
key = key.strip().lower()
|
|
304
|
+
if not sep or key not in _TARGET_OVERRIDES:
|
|
305
|
+
continue
|
|
306
|
+
value = value.strip()
|
|
307
|
+
out[_TARGET_OVERRIDES[key]] = int(value) if key == "think" and value.isdigit() else value
|
|
308
|
+
return out
|
|
309
|
+
|
|
310
|
+
|
|
283
311
|
def run_live_case(case: DelegateCase, target: str, svc, *,
|
|
284
312
|
allow_write_delegation: bool = False) -> tuple[CaseResult, dict]:
|
|
285
313
|
"""Send one case through handle_delegate. Returns (result, recording)."""
|
|
286
314
|
from cli.tools.delegate import handle_delegate
|
|
287
315
|
|
|
288
316
|
backend, tier = parse_target(target)
|
|
317
|
+
overrides = target_overrides(target)
|
|
318
|
+
if overrides:
|
|
319
|
+
svc = types.SimpleNamespace(**{**vars(svc), "delegate_config": {**svc.delegate_config, **overrides}})
|
|
289
320
|
captured: dict = {}
|
|
290
321
|
|
|
291
322
|
def finalize(tool, meta, output, status, **kw):
|
|
File without changes
|
{code_context_control-2.136.0.dist-info → code_context_control-2.138.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{code_context_control-2.136.0.dist-info → code_context_control-2.138.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{code_context_control-2.136.0.dist-info → code_context_control-2.138.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|