1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! The correction injected when a turn claims work it never executed
//! (#796, #797).
//!
//! The previous wording stated what was missing: "your last response produced
//! ZERO tool_use blocks". That is accurate and argues against a position the
//! model does not hold. It is not withholding a call it knows it skipped; it
//! believes the work already happened, having simulated the call while
//! reasoning and lost the distinction between the simulation and a result.
//! Told it emitted no tool_use blocks, a model in that state reads a
//! formatting complaint and leaves the belief intact.
//!
//! So the correction states the mechanism instead: nothing executes inside
//! reasoning, and the only evidence a tool ran is a result present in the
//! conversation. That is checkable, and it is checkable by the model.
//!
//! Pure so the wording is testable without a provider.
/// The escape hatch every variant must carry.
///
/// Without it the nudge becomes a loop: a model that genuinely finished, and
/// said so, gets told to call tools, calls something pointless to comply, and
/// is nudged again. Real completion has to have an exit that is not a tool
/// call.
const FINISHED_ESCAPE: &str = " If the work is genuinely done and you have already reported it, \
reply with a short confirmation and stop; do not run extra tool calls to re-verify it.";
/// The mechanism, stated once and shared by every variant.
const NO_EXECUTION_WHILE_REASONING: &str = "Tools execute only between turns; nothing runs inside your reasoning. If you saw that \
output while thinking, you imagined it. The only evidence a tool ran is its result \
present in this conversation.";
/// Correction naming the exact commands claimed but never run (#797).
///
/// This is the one correction that does not infer. Every other phantom check
/// reads wording for signals, and wording is arguable; the loop knows what it
/// executed, so "you claimed X ran and X did not run" is a matter of fact and
/// cannot be talked around. Quoting it turns a category into a citation.
/// Correction for a turn that produced no tool calls, when no specific
/// fabricated command was identified.
///
/// `local_model` selects wording for Qwen/Kimi/DeepSeek-class models, which
/// need two things the cloud variant does not. They read "STOP" as "wait for
/// further instruction" and reply with an acknowledgement instead of calling
/// anything, so the word is avoided. And they write `{"tool_call": {...}}` as
/// message text believing that IS the invocation, so the structured API is
/// named explicitly.
// ── Pre-compaction context-pressure warning (#909) ──
//
// Compaction is reactive today: Tier-1 fires at 65% and clips the conversation
// with no prior warning. The model never gets a chance to deliberately finish
// its current sub-task and persist critical state before the clip.
//
// These helpers define a warning band (55-64%) just below the trigger. When
// usage enters the band, a behavioural nudge is appended to the system brain
// telling the model to persist state to disk NOW. It is a nudge, not a number,
// because `context.token_count` is a tiktoken estimate for all providers at
// the point this runs - a precise percentage would repeat the units-confusion
// that caused #896, and the model can't trigger compaction anyway, so the only
// useful signal is the instruction itself.
//
// The nudge is transient: it rides on `system_brain`, which is rebuilt from
// disk every turn, so it never lands in the DB. A per-session throttle flag
// (held on AgentService) suppresses repeat warnings while usage lingers in the
// band and re-arms when it drops below the band floor.
/// Lower edge of the warning band (inclusive). Below this the throttle re-arms
/// so a fresh entry into the band warns again.
pub const PRESSURE_WARN_FLOOR: f64 = 55.0;
/// Upper edge of the warning band (exclusive). At 65% Tier-1 compaction fires
/// (see `compaction.rs`), so the warning band runs right up to the trigger.
pub const PRESSURE_WARN_CEILING: f64 = 65.0;
/// The behavioural nudge text appended to the system brain when usage is in the
/// warning band. Public to the crate so the wiring site and tests can reference
/// the exact wording.
/// Whether the current usage percentage is inside the warning band.
/// Pure - testable without a provider or a full AgentService.
/// Decide whether to emit the pressure warning this turn.
///
/// Returns `Some(warning)` when usage is in the band AND the warning has not
/// already been emitted for this band entry, `None` otherwise. The caller owns
/// the `already_emitted` flag and must set it true on emit, false when usage
/// drops below the floor (so the next band entry warns again).
///
/// Pure - testable in isolation.