chad-code 1.0.8__tar.gz → 1.11.0__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.
- {chad_code-1.0.8 → chad_code-1.11.0}/PKG-INFO +1 -1
- {chad_code-1.0.8 → chad_code-1.11.0}/pyproject.toml +5 -5
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/__init__.py +1 -1
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/agent.py +50 -5
- chad_code-1.11.0/src/chad/ambient.py +453 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/cli.py +5 -4
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/compaction.py +4 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/guardrails.py +38 -7
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/levers.py +136 -30
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/mlx_fastpath.py +55 -4
- chad_code-1.11.0/src/chad/mlx_moe_fused.py +657 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/profiles.py +1 -5
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/prompt.py +20 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/render.py +3 -1
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/repomap.py +100 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/symbols.py +4 -1
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/syntaxgate.py +7 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/tools.py +41 -3
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad_code.egg-info/PKG-INFO +1 -1
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad_code.egg-info/SOURCES.txt +5 -0
- chad_code-1.11.0/tests/test_ambient.py +277 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_lever_bite.py +47 -0
- chad_code-1.11.0/tests/test_lever_instrumentation.py +95 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_levers.py +37 -11
- chad_code-1.11.0/tests/test_mlx_moe_fused.py +354 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_repomap.py +87 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_repomap_polyglot.py +31 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/LICENSE +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/README.md +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/setup.cfg +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/atif.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/base_engine.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/bench.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/completion_engine.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/config.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/diag.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/engine.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/ignore.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/lsp.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/lspclient.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/lspservers.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/mcp.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/mcp_oauth.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/mlx_qsdpa.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/LICENSE +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/__init__.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/alignment.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/attention.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/audio.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/cache.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/conformer.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/ctc.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/parakeet.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/rnnt.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/tokenizer.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/parakeet/utils.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/prove.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/serve.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/session.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/skills.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/speech.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/toolcall_parse.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/tui.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad/validate.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad_code.egg-info/dependency_links.txt +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad_code.egg-info/entry_points.txt +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad_code.egg-info/requires.txt +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/src/chad_code.egg-info/top_level.txt +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_agent.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_agent_e2e.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_agent_guards.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_atif.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_bench.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_cli.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_compact_notice.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_compaction.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_completion_engine.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_config.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_confirm_preview.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_done_audit.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_drift_warn.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_edit.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_edit_corruption.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_engine.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_engine_kvquant.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_engine_pld_hybrid.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_feel_pack.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_garble_invariant.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_gate.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_ignore.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_intent.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_log_redaction.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_lsp.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_lsp_live.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_lspclient.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_mcp.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_mcp_oauth.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_mlx_fastpath.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_mlx_qsdpa.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_plan_review.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_prove.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_render.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_replace_lines.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_serve.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_session.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_skills.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_speech.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_speech_tui.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_subagent.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_symbols.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_syntaxgate.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_toolcall_parse.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_tools.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_tui.py +0 -0
- {chad_code-1.0.8 → chad_code-1.11.0}/tests/test_validate.py +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# import name, and command name are independent. `uvx chad-code` runs the alias
|
|
5
5
|
# script added under [project.scripts].
|
|
6
6
|
name = "chad-code"
|
|
7
|
-
version = "1.0
|
|
7
|
+
version = "1.11.0"
|
|
8
8
|
description = "Local MLX-backed, Claude-Code-style coding agent (Apple Silicon, Ornith 35B/9B)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -174,10 +174,10 @@ module = "chad.parakeet.*"
|
|
|
174
174
|
ignore_errors = true
|
|
175
175
|
|
|
176
176
|
[[tool.mypy.overrides]]
|
|
177
|
-
#
|
|
178
|
-
# (dev Macs, the macos CI jobs)
|
|
177
|
+
# These modules patch mlx_lm classes that only exist on macOS: with mlx installed
|
|
178
|
+
# (dev Macs, the macos CI jobs) their `type: ignore[method-assign]` comments are load-
|
|
179
179
|
# bearing; on the Linux lint runner mlx_lm is absent, the patched classes type as Any,
|
|
180
180
|
# and warn_unused_ignores flags those same comments — unfixable in the code for both
|
|
181
|
-
# platforms at once, so the unused-ignore warning alone is relaxed for
|
|
182
|
-
module = "chad.mlx_fastpath"
|
|
181
|
+
# platforms at once, so the unused-ignore warning alone is relaxed for them.
|
|
182
|
+
module = ["chad.mlx_fastpath", "chad.mlx_moe_fused"]
|
|
183
183
|
warn_unused_ignores = false
|
|
@@ -4,7 +4,7 @@ A flat collection of cooperating modules behind one console script (``chad``):
|
|
|
4
4
|
the inference engine, the tool layer, the agent loop, and the terminal UI.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "1.0
|
|
7
|
+
__version__ = "1.11.0"
|
|
8
8
|
|
|
9
9
|
# chad sets no MLX_* runtime vars. MLX_METAL_FAST_SYNCH, MLX_MAX_OPS_PER_BUFFER
|
|
10
10
|
# and MLX_MAX_MB_PER_BUFFER were each measured end-to-end on the 35B and every
|
|
@@ -14,7 +14,7 @@ import re
|
|
|
14
14
|
import sys
|
|
15
15
|
import time
|
|
16
16
|
|
|
17
|
-
from . import atif, compaction, config, guardrails, levers, session, syntaxgate
|
|
17
|
+
from . import ambient, atif, compaction, config, guardrails, levers, session, syntaxgate
|
|
18
18
|
from .base_engine import BackendError, BaseEngine
|
|
19
19
|
from .diag import args_preview, log, redact, result_preview
|
|
20
20
|
from .prompt import build_subagent_prompt, build_system_prompt, classify_intent
|
|
@@ -47,7 +47,7 @@ SUBAGENT_MAX_STEPS = 24
|
|
|
47
47
|
SUBAGENT_CTX_LIMIT = 32000
|
|
48
48
|
SUBAGENT_READ_ONLY = {
|
|
49
49
|
"read", "grep", "glob", "repo_map", "overview", "view_symbol",
|
|
50
|
-
"find_symbol", "find_refs", "done", "finish", "stop",
|
|
50
|
+
"find_symbol", "definition", "find_refs", "done", "finish", "stop",
|
|
51
51
|
}
|
|
52
52
|
# `write_todos` is deliberately absent: a sub-agent that plans its own work would mutate
|
|
53
53
|
# the process-global `_TODOS` and clobber the parent's pinned todo panel (and `_sub_emit`
|
|
@@ -346,6 +346,8 @@ class Agent:
|
|
|
346
346
|
skills.reset_session()
|
|
347
347
|
from . import mcp
|
|
348
348
|
mcp.reset_session()
|
|
349
|
+
ambient.reset() # fresh session-state ledger; sub-agents share the
|
|
350
|
+
# parent's and never feed it (see run_turn)
|
|
349
351
|
self.mode = mode or ("yolo" if yolo else "normal")
|
|
350
352
|
self.thinking = thinking # Ornith is a reasoning model; toggles <think> blocks
|
|
351
353
|
# Steps per WINDOW, not a hard kill: a window that landed+verified a change
|
|
@@ -625,6 +627,7 @@ class Agent:
|
|
|
625
627
|
# measured trace: a model re-phrasing the same delete 30 times). The
|
|
626
628
|
# guard names itself and the fix — narrow the target.
|
|
627
629
|
if levers.enabled("scoped_destructive_guard"):
|
|
630
|
+
levers.fired("scoped_destructive_guard", deny_text=True)
|
|
628
631
|
self._deny_reason = (
|
|
629
632
|
"[blocked by the destructive-command guard, not by a person: "
|
|
630
633
|
"the command matches a catastrophic pattern (recursive delete "
|
|
@@ -722,6 +725,7 @@ class Agent:
|
|
|
722
725
|
note = sub.budget_note or guardrails.progress_note(sub.messages)
|
|
723
726
|
if not note:
|
|
724
727
|
return res
|
|
728
|
+
levers.fired("subagent_budget_note")
|
|
725
729
|
return (res.rstrip() + "\n[sub-agent progress before it "
|
|
726
730
|
f"stopped: {note}]").strip()
|
|
727
731
|
if sub.interrupted:
|
|
@@ -1196,6 +1200,7 @@ class Agent:
|
|
|
1196
1200
|
or self._backend_retries >= _MAX_BACKEND_RETRIES):
|
|
1197
1201
|
raise
|
|
1198
1202
|
self._backend_retries += 1
|
|
1203
|
+
levers.fired("backend_retry", attempt=self._backend_retries)
|
|
1199
1204
|
log.warning("backend error (retry %d/%d): %s",
|
|
1200
1205
|
self._backend_retries, _MAX_BACKEND_RETRIES, e)
|
|
1201
1206
|
self._emit("status", f"Backend error; retrying "
|
|
@@ -1285,6 +1290,8 @@ class Agent:
|
|
|
1285
1290
|
# the first branch already credits; an unclosed generation that
|
|
1286
1291
|
# stopped SHORT of the cap is a truncation of some other kind, not
|
|
1287
1292
|
# the reasoning overspend this counts.
|
|
1293
|
+
levers.fired("capped_think_credit", step=step,
|
|
1294
|
+
tokens=stats.generated_tokens)
|
|
1288
1295
|
_think_delta = stats.generated_tokens
|
|
1289
1296
|
else:
|
|
1290
1297
|
_think_delta = 0
|
|
@@ -1312,6 +1319,7 @@ class Agent:
|
|
|
1312
1319
|
if _tt_decision == "half":
|
|
1313
1320
|
log.info("THINK-BUDGET half at step %d: %d/%d cumulative think tok "
|
|
1314
1321
|
"this turn", step, turn_think_tokens, _tt_budget)
|
|
1322
|
+
levers.fired("turn_think_budget", step=step, decision="half")
|
|
1315
1323
|
self.messages.append({"role": "tool", "name": "steer",
|
|
1316
1324
|
"content": guardrails.TURN_THINK_BUDGET_STEER})
|
|
1317
1325
|
elif _tt_decision == "exhausted":
|
|
@@ -1319,6 +1327,7 @@ class Agent:
|
|
|
1319
1327
|
"tok this turn — throttling <think> (one action step per "
|
|
1320
1328
|
"%d further think tok)", step, turn_think_tokens,
|
|
1321
1329
|
_tt_budget, guardrails.TURN_THINK_REARM_TOK)
|
|
1330
|
+
levers.fired("turn_think_budget", step=step, decision="exhausted")
|
|
1322
1331
|
self._emit("info", " [reasoning budget exhausted for this turn — "
|
|
1323
1332
|
"throttling further <think>]")
|
|
1324
1333
|
|
|
@@ -1388,6 +1397,8 @@ class Agent:
|
|
|
1388
1397
|
hard_wrapup_fired = True
|
|
1389
1398
|
landing_no_think = True
|
|
1390
1399
|
_remaining = self._turn_budget_s - (time.monotonic() - turn_start)
|
|
1400
|
+
levers.fired("hard_wrapup", step=step,
|
|
1401
|
+
remaining_s=int(_remaining))
|
|
1391
1402
|
log.info("HARD-WRAPUP abort at step %d: %.0fs left, gen was %d tok",
|
|
1392
1403
|
step, _remaining, stats.generated_tokens)
|
|
1393
1404
|
self._emit("info", " [wall deadline reached — landing the best answer now]")
|
|
@@ -1452,6 +1463,7 @@ class Agent:
|
|
|
1452
1463
|
capped_stall_streak += 1
|
|
1453
1464
|
if (self.think_ceiling and self.thinking and capped_stall_streak >= 2
|
|
1454
1465
|
and levers.enabled("no_think_escalation")):
|
|
1466
|
+
levers.fired("no_think_escalation", step=step)
|
|
1455
1467
|
no_think_next = True
|
|
1456
1468
|
else:
|
|
1457
1469
|
capped_stall_streak = 0
|
|
@@ -1481,6 +1493,7 @@ class Agent:
|
|
|
1481
1493
|
# last). Costs one prefix-cache invalidation on this rare path.
|
|
1482
1494
|
if (levers.enabled("garble_never_final")
|
|
1483
1495
|
and consecutive_garbles >= 2 and last_garble_idx is not None):
|
|
1496
|
+
levers.fired("garble_never_final", step=step, action="scrub")
|
|
1484
1497
|
self.messages[last_garble_idx]["content"] = \
|
|
1485
1498
|
guardrails.GARBLE_SCRUBBED
|
|
1486
1499
|
log.info("GARBLE scrub at step %d: previous garbled message "
|
|
@@ -1516,6 +1529,7 @@ class Agent:
|
|
|
1516
1529
|
# the audit latch, and the last one ships as the answer with most of the
|
|
1517
1530
|
# wall budget still unspent).
|
|
1518
1531
|
if garbled and levers.enabled("garble_never_final"):
|
|
1532
|
+
levers.fired("garble_never_final", step=step, action="hard-stop")
|
|
1519
1533
|
self.budget_note = guardrails.progress_note(self.messages)
|
|
1520
1534
|
log.info("END step %d: GARBLE hard-stop (%d garble nudges spent) — "
|
|
1521
1535
|
"a garbled tool call is never a final answer",
|
|
@@ -1558,6 +1572,8 @@ class Agent:
|
|
|
1558
1572
|
if audit:
|
|
1559
1573
|
done_audit_fired = True
|
|
1560
1574
|
done_audit_bounces += 1
|
|
1575
|
+
levers.fired("done_audit", step=step, entry="churn-handoff")
|
|
1576
|
+
levers.fired("audit_churn_handoff", step=step)
|
|
1561
1577
|
audit_absent_list = guardrails.audit_absent_paths(audit_task)
|
|
1562
1578
|
_runway = ((self._turn_budget_s
|
|
1563
1579
|
- (time.monotonic() - turn_start))
|
|
@@ -1607,6 +1623,7 @@ class Agent:
|
|
|
1607
1623
|
if audit:
|
|
1608
1624
|
done_audit_fired = True
|
|
1609
1625
|
done_audit_bounces += 1
|
|
1626
|
+
levers.fired("done_audit", step=step, entry="final-answer")
|
|
1610
1627
|
audit_absent_list = guardrails.audit_absent_paths(audit_task)
|
|
1611
1628
|
_runway = ((self._turn_budget_s
|
|
1612
1629
|
- (time.monotonic() - turn_start))
|
|
@@ -1681,10 +1698,15 @@ class Agent:
|
|
|
1681
1698
|
log.info("DONE rejected: edits not verified -> nudge #%d", verify_nudges)
|
|
1682
1699
|
self.messages.append({
|
|
1683
1700
|
"role": "tool", "name": "done",
|
|
1701
|
+
# The session-ledger facts (what changed, what last ran) ride
|
|
1702
|
+
# the bounce when the lever is on — the model's stale state
|
|
1703
|
+
# model IS the wrong-verify bug.
|
|
1684
1704
|
"content": "[not done yet: you changed files but have not run anything "
|
|
1685
1705
|
"to verify them. Run the project's tests (or the code) with "
|
|
1686
1706
|
"bash, check the output is correct, then call done. If a test "
|
|
1687
|
-
"fails, fix the code first.]"
|
|
1707
|
+
"fails, fix the code first.]"
|
|
1708
|
+
+ ("" if self._subagent else
|
|
1709
|
+
ambient.ledger_suffix(step=step, force=True)),
|
|
1688
1710
|
})
|
|
1689
1711
|
continue
|
|
1690
1712
|
if action_task and not read_only_intent and self.mode != "plan" \
|
|
@@ -1706,6 +1728,9 @@ class Agent:
|
|
|
1706
1728
|
if audit:
|
|
1707
1729
|
done_audit_fired = True
|
|
1708
1730
|
done_audit_bounces += 1
|
|
1731
|
+
levers.fired("done_audit", step=step,
|
|
1732
|
+
entry="churn-handoff-done")
|
|
1733
|
+
levers.fired("audit_churn_handoff", step=step)
|
|
1709
1734
|
audit_absent_list = guardrails.audit_absent_paths(audit_task)
|
|
1710
1735
|
_runway = ((self._turn_budget_s
|
|
1711
1736
|
- (time.monotonic() - turn_start))
|
|
@@ -1768,14 +1793,18 @@ class Agent:
|
|
|
1768
1793
|
if audit:
|
|
1769
1794
|
done_audit_fired = True
|
|
1770
1795
|
done_audit_bounces += 1
|
|
1796
|
+
levers.fired("done_audit", step=step, entry="done")
|
|
1771
1797
|
audit_absent_list = guardrails.audit_absent_paths(audit_task)
|
|
1772
1798
|
_runway = ((self._turn_budget_s
|
|
1773
1799
|
- (time.monotonic() - turn_start))
|
|
1774
1800
|
if self._turn_budget_s else float("inf"))
|
|
1775
1801
|
log.info("DONE-AUDIT bounce: paths=%s runway=%.0fs",
|
|
1776
1802
|
guardrails.audit_extract_paths(audit_task), _runway)
|
|
1777
|
-
self.messages.append({
|
|
1778
|
-
|
|
1803
|
+
self.messages.append({
|
|
1804
|
+
"role": "tool", "name": "done",
|
|
1805
|
+
# Session-ledger facts ride the audit bounce
|
|
1806
|
+
"content": audit
|
|
1807
|
+
+ ambient.ledger_suffix(step=step, force=True)})
|
|
1779
1808
|
continue
|
|
1780
1809
|
# Absent-path re-bounce (see the final-answer twin above): a
|
|
1781
1810
|
# still-absent task-named path at accept time gets ONE more bounce,
|
|
@@ -1802,6 +1831,7 @@ class Agent:
|
|
|
1802
1831
|
and guardrails.done_spec_recheck(
|
|
1803
1832
|
did_work, unverified_edit, done_recheck_done, read_only_intent):
|
|
1804
1833
|
done_recheck_done = True
|
|
1834
|
+
levers.fired("done_spec_recheck", step=step)
|
|
1805
1835
|
log.info("DONE deferred for deliverable recheck (step %d)", step)
|
|
1806
1836
|
self.messages.append({"role": "tool", "name": "done",
|
|
1807
1837
|
"content": guardrails.DONE_SPEC_RECHECK})
|
|
@@ -1897,6 +1927,7 @@ class Agent:
|
|
|
1897
1927
|
_sub_sig = (str(args.get("description", "")).strip(),
|
|
1898
1928
|
str(args.get("prompt", "")).strip())
|
|
1899
1929
|
if _sub_sig in subagent_sigs and levers.enabled("subagent_no_respawn"):
|
|
1930
|
+
levers.fired("subagent_no_respawn", step=step)
|
|
1900
1931
|
result = ("[you already ran this exact sub-agent this turn — do NOT "
|
|
1901
1932
|
"re-run it. Use what it returned, or do the work yourself "
|
|
1902
1933
|
"now with grep/read to locate the code and edit to change "
|
|
@@ -1960,6 +1991,16 @@ class Agent:
|
|
|
1960
1991
|
log.info("TOOL %s duplicate result elided (%d chars)",
|
|
1961
1992
|
name, len(result))
|
|
1962
1993
|
result = _elided
|
|
1994
|
+
# Ambient state: fold this result into the session
|
|
1995
|
+
# ledger and append whatever fact lines the enabled levers owe it
|
|
1996
|
+
# (session_ledger on landed mutations, bash_read_skeleton on file
|
|
1997
|
+
# content surfacing through bash/read). After the clip cap on
|
|
1998
|
+
# purpose — a clipped result must still carry its facts; the
|
|
1999
|
+
# appended text is bounded in ambient.py. Main agent only: a
|
|
2000
|
+
# sub-agent's transcript folds away and must not pollute the
|
|
2001
|
+
# parent's ledger.
|
|
2002
|
+
if not self._subagent:
|
|
2003
|
+
result = ambient.annotate(name, args, result, step=step)
|
|
1963
2004
|
step_tool_chars += len(result)
|
|
1964
2005
|
if _PREFILL_TRACE:
|
|
1965
2006
|
self._trace_tools_pending.append([name, round(_tool_s, 4)])
|
|
@@ -1977,6 +2018,7 @@ class Agent:
|
|
|
1977
2018
|
if (plan_write and result.startswith("[wrote") and not plan_reviews
|
|
1978
2019
|
and levers.enabled("plan_review")):
|
|
1979
2020
|
plan_reviews += 1
|
|
2021
|
+
levers.fired("plan_review", step=step)
|
|
1980
2022
|
log.info("PLAN REVIEW nudge for %s", self.last_plan_path)
|
|
1981
2023
|
self.messages.append({"role": "tool", "name": "read", "content": (
|
|
1982
2024
|
f"[plan written to {self.last_plan_path}. Before you call `done`, "
|
|
@@ -2087,6 +2129,9 @@ class Agent:
|
|
|
2087
2129
|
n == "bash"
|
|
2088
2130
|
and not guardrails.is_readonly_bash(str(a.get("command", "")))
|
|
2089
2131
|
for n, a in calls):
|
|
2132
|
+
if readonly_streak: # the exemption actually reset a live streak
|
|
2133
|
+
levers.fired("gate_ops_exempt", step=step,
|
|
2134
|
+
streak=readonly_streak)
|
|
2090
2135
|
readonly_streak = 0
|
|
2091
2136
|
else:
|
|
2092
2137
|
readonly_streak += 1
|