fix(tui): trim drifting turn metadata (#5024)
Remove stale duplicated turn metadata and keep the runtime state authoritative. All substantive CI and Buildkite gates passed; the only failed check was the unavailable Claude review infrastructure.
This commit is contained in:
+72
-100
@@ -41,7 +41,6 @@ use crate::models::{
|
||||
};
|
||||
use crate::prompts;
|
||||
use crate::purge::{emit_purge_completed, emit_purge_failed, emit_purge_started, run_purge};
|
||||
use crate::resource_telemetry::ResourceTelemetry;
|
||||
#[cfg(test)]
|
||||
use crate::route_runtime::resolve_runtime_route;
|
||||
use crate::route_runtime::{
|
||||
@@ -88,6 +87,20 @@ use super::turn::{TurnContext, post_turn_snapshot, pre_turn_snapshot};
|
||||
const ENGINE_OP_CHANNEL_CAPACITY: usize = 32;
|
||||
const GOAL_CONTINUATION_FAILURE_DETAIL_MAX_BYTES: usize = 512;
|
||||
|
||||
fn context_pressure_message(usage_percent: f64) -> Option<&'static str> {
|
||||
if usage_percent >= crate::tui::context_inspector::CONTEXT_CRITICAL_THRESHOLD_PERCENT {
|
||||
Some(
|
||||
"Context pressure: critical — CRITICAL: stop expanding scope; run /compact immediately or finish the current task",
|
||||
)
|
||||
} else if usage_percent >= crate::tui::context_inspector::CONTEXT_WARNING_THRESHOLD_PERCENT {
|
||||
Some(
|
||||
"Context pressure: warning — ESCALATED: prefer /compact, narrow scope, or finish the current task",
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn agent_list_event(manager: &SubAgentManager) -> Event {
|
||||
Event::AgentList {
|
||||
agents: manager.list(),
|
||||
@@ -2483,90 +2496,55 @@ impl Engine {
|
||||
prompt_context: &NextTurnPromptContext,
|
||||
system_prompt: Option<&SystemPrompt>,
|
||||
) {
|
||||
if let Some(line) = self.context_pressure_line(current_text, prompt_context, system_prompt)
|
||||
{
|
||||
lines.push(line);
|
||||
}
|
||||
if let Some(line) = self.active_goal_token_budget_line(prompt_context) {
|
||||
lines.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
/// One-line context-pressure signal, emitted **only** while the input
|
||||
/// estimate sits at or above the warning/critical thresholds. No token
|
||||
/// counts, percentages, or headroom figures: the model only learns that
|
||||
/// the pressure band it is in has crossed a threshold. Between crossings
|
||||
/// the line is byte-stable, so ordinary turns do not bust the prefix
|
||||
/// cache.
|
||||
fn context_pressure_line(
|
||||
&self,
|
||||
current_text: &str,
|
||||
prompt_context: &NextTurnPromptContext,
|
||||
system_prompt: Option<&SystemPrompt>,
|
||||
) -> Option<String> {
|
||||
let input_tokens = self.active_input_tokens_with_current_text(current_text, system_prompt);
|
||||
if let Some(budget) = route_context_budget_for_route(
|
||||
let budget = route_context_budget_for_route(
|
||||
prompt_context.provider,
|
||||
&prompt_context.model,
|
||||
prompt_context.route_limits,
|
||||
input_tokens,
|
||||
) {
|
||||
let usage_percent = budget.usage_percent();
|
||||
let escalation = if usage_percent
|
||||
>= crate::tui::context_inspector::CONTEXT_CRITICAL_THRESHOLD_PERCENT
|
||||
{
|
||||
" — CRITICAL: stop expanding scope; run /compact immediately or finish the current task"
|
||||
} else if usage_percent
|
||||
>= crate::tui::context_inspector::CONTEXT_WARNING_THRESHOLD_PERCENT
|
||||
{
|
||||
" — ESCALATED: prefer /compact, narrow scope, or finish the current task"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
lines.push(format!(
|
||||
"Context pressure: {} ({usage_percent:.1}% used, {} / {} tokens; {} input tokens available){escalation}",
|
||||
budget.pressure.label(),
|
||||
budget.input_tokens,
|
||||
budget.window_tokens,
|
||||
budget.available_input_tokens,
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(line) = self.session_token_usage_line() {
|
||||
lines.push(line);
|
||||
}
|
||||
if let Some(line) = self.active_goal_resource_line(prompt_context) {
|
||||
lines.push(line);
|
||||
}
|
||||
)?;
|
||||
context_pressure_message(budget.usage_percent()).map(str::to_string)
|
||||
}
|
||||
|
||||
fn session_token_usage_line(&self) -> Option<String> {
|
||||
let usage = &self.session.total_usage;
|
||||
let total = usage.input_tokens.saturating_add(usage.output_tokens);
|
||||
if total == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut line = format!(
|
||||
"Session token usage: {total} total ({} input, {} output)",
|
||||
usage.input_tokens, usage.output_tokens,
|
||||
);
|
||||
if let Some(hit_tokens) = usage.cache_read_input_tokens {
|
||||
line.push_str(&format!(", cache hits {hit_tokens}"));
|
||||
}
|
||||
if let Some(write_tokens) = usage.cache_creation_input_tokens {
|
||||
line.push_str(&format!(", cache writes {write_tokens}"));
|
||||
}
|
||||
Some(line)
|
||||
}
|
||||
|
||||
fn active_goal_resource_line(&self, prompt_context: &NextTurnPromptContext) -> Option<String> {
|
||||
/// Goal pacing for the model: the budget figure only, and only while a
|
||||
/// goal is actually active. Usage/time deltas, rates, and continuation
|
||||
/// counts are UI telemetry — they changed every turn and invalidated the
|
||||
/// prefix cache without adding model-steering signal.
|
||||
fn active_goal_token_budget_line(
|
||||
&self,
|
||||
prompt_context: &NextTurnPromptContext,
|
||||
) -> Option<String> {
|
||||
let objective = prompt_context.goal_objective.as_deref()?;
|
||||
let snapshot = self.config.goal_state.lock().ok()?.snapshot();
|
||||
let same_goal =
|
||||
normalized_goal_objective(snapshot.objective.as_deref()).as_deref() == Some(objective);
|
||||
let (tokens_used, time_used_seconds, continuation_count, token_budget) = if same_goal {
|
||||
(
|
||||
snapshot.tokens_used,
|
||||
snapshot.time_used_seconds,
|
||||
snapshot.continuation_count,
|
||||
snapshot.token_budget,
|
||||
)
|
||||
let token_budget = if same_goal {
|
||||
snapshot.token_budget
|
||||
} else {
|
||||
(0, 0, 0, prompt_context.goal_token_budget)
|
||||
};
|
||||
|
||||
let mut telemetry = ResourceTelemetry::new(tokens_used, time_used_seconds);
|
||||
if let Some(token_budget) = token_budget {
|
||||
telemetry = telemetry.with_token_budget(u64::from(token_budget));
|
||||
}
|
||||
|
||||
let mut line = format!("Active goal resource usage: {}", telemetry.human_summary());
|
||||
if tokens_used > 0 && time_used_seconds > 0 {
|
||||
let rate = tokens_used as f64 / time_used_seconds as f64;
|
||||
line.push_str(&format!("; {rate:.1} tok/s"));
|
||||
}
|
||||
line.push_str(&format!("; {continuation_count} continuations"));
|
||||
Some(line)
|
||||
prompt_context.goal_token_budget
|
||||
}?;
|
||||
Some(format!("Active goal token budget: {token_budget}"))
|
||||
}
|
||||
|
||||
async fn add_session_message(&mut self, message: Message) {
|
||||
@@ -2606,7 +2584,7 @@ impl Engine {
|
||||
/// Build `<turn_meta>` from an explicit snapshot of the session state a
|
||||
/// turn installs *before* it writes the block.
|
||||
///
|
||||
/// Production installs mode, approval posture, policy narrowing, and the
|
||||
/// Production installs approval posture, policy narrowing, and the
|
||||
/// observed working set on `self`, then reads them back here.
|
||||
/// `/preview-request` cannot install any of that — it describes a turn
|
||||
/// that has not started — so it passes the values it would have installed,
|
||||
@@ -2617,9 +2595,9 @@ impl Engine {
|
||||
fn turn_metadata_block_from_snapshot(
|
||||
&self,
|
||||
_routed_model: &str,
|
||||
auto_model: bool,
|
||||
reasoning_effort: Option<&str>,
|
||||
reasoning_effort_auto: bool,
|
||||
_auto_model: bool,
|
||||
_reasoning_effort: Option<&str>,
|
||||
_reasoning_effort_auto: bool,
|
||||
provenance: UserInputProvenance,
|
||||
current_text: &str,
|
||||
snapshot: TurnMetadataSnapshot<'_>,
|
||||
@@ -2637,42 +2615,36 @@ impl Engine {
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
// Facts only (#4780). Mode doctrine and permission-question discipline
|
||||
// ship once in the stable system prefix (mode/approval overlays); re-
|
||||
// asserting hundreds of tokens of doctrine here out-shouts the
|
||||
// constitution by salience every user message.
|
||||
// Facts only (#4780 + turn-meta diet). Mode doctrine ships once in the
|
||||
// stable system prefix. The permission posture does not: preserve its
|
||||
// compact label so the model can distinguish Ask, Auto-Review, Full
|
||||
// Access, and Never without repeating question-discipline prose.
|
||||
// Route/effort/model lines are telemetry the model cannot act on.
|
||||
let mut lines = vec![
|
||||
format!("Current local date: {today}"),
|
||||
// Workspace path moved here from the static `## Environment` block so
|
||||
// the static system prefix stays byte-stable across sessions (see
|
||||
// `render_environment_block` for the prefix-cache rationale).
|
||||
format!("Current workspace: {}", self.config.workspace.display()),
|
||||
format!("Current model: {}", prompt_context.model),
|
||||
format!("Current mode: {}", prompt_context.mode.as_setting()),
|
||||
format!(
|
||||
"Current permission posture: {}",
|
||||
approval_mode.permission_chip_label()
|
||||
),
|
||||
format!("Input provenance: {}", provenance.as_str()),
|
||||
format!(
|
||||
"Input authority: {}",
|
||||
if provenance.can_authorize_work() {
|
||||
"external_current_turn"
|
||||
} else {
|
||||
"non_authoritative"
|
||||
}
|
||||
),
|
||||
];
|
||||
if auto_model {
|
||||
lines.push(format!("Auto model route: {}", prompt_context.model));
|
||||
}
|
||||
if reasoning_effort_auto && let Some(reasoning_effort) = reasoning_effort {
|
||||
lines.push(format!("Auto reasoning effort: {reasoning_effort}"));
|
||||
// On ordinary external turns the user's own message is authoritative by
|
||||
// construction, so provenance is redundant. On non-external turns
|
||||
// (sub-agent handoff, runtime events) the *reduced* authority is the
|
||||
// sole signal, so surface it as one condensed line.
|
||||
if !provenance.can_authorize_work() {
|
||||
lines.push(format!(
|
||||
"Input provenance: {} (non-authoritative)",
|
||||
provenance.as_str()
|
||||
));
|
||||
}
|
||||
// #3947: when runtime policy narrowed this turn's authority, the model
|
||||
// learns that it happened, why, and the exact sentence the user saw —
|
||||
// not merely the already-narrowed posture above. Emitted only on a
|
||||
// narrowed turn, so the ordinary turn's metadata stays byte-stable.
|
||||
// learns that it happened, why, and the exact sentence the user saw.
|
||||
// Emitted only on a narrowed turn, so the ordinary turn's metadata
|
||||
// stays byte-stable.
|
||||
if let Some(event) = policy_narrowing {
|
||||
lines.push(format!("Authority narrowing: {}", event.reason().as_str()));
|
||||
lines.push(format!("Authority transition: {}", event.transition()));
|
||||
|
||||
@@ -1047,10 +1047,20 @@ mod tests {
|
||||
let config = deepseek_config();
|
||||
let (mut engine, _handle, _tmp) = preview_engine(&config);
|
||||
engine.api_provider = ApiProvider::Deepseek;
|
||||
engine.active_route_limits = Some(codewhale_config::route::RouteLimits {
|
||||
let installed_limits = codewhale_config::route::RouteLimits {
|
||||
context_tokens: Some(4_096),
|
||||
input_tokens: None,
|
||||
output_tokens: Some(512),
|
||||
};
|
||||
engine.active_route_limits = Some(installed_limits);
|
||||
// Large enough to be critical for the installed 4K route, but safely
|
||||
// below the warning threshold for the planned 123K route.
|
||||
engine.session.messages.push(Message {
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentBlock::Text {
|
||||
text: "x".repeat(20_000),
|
||||
cache_control: None,
|
||||
}],
|
||||
});
|
||||
let prompt_context = NextTurnPromptContext::for_planned_turn(
|
||||
ApiProvider::Openrouter,
|
||||
@@ -1068,6 +1078,35 @@ mod tests {
|
||||
None,
|
||||
);
|
||||
let system_prompt = engine.compose_stable_system_prompt(&prompt_context);
|
||||
assert_eq!(
|
||||
engine.context_pressure_line(
|
||||
"cross-route budget",
|
||||
&prompt_context,
|
||||
system_prompt.as_ref()
|
||||
),
|
||||
None,
|
||||
"the planned 123K route must not inherit the installed route's pressure"
|
||||
);
|
||||
let installed_context = NextTurnPromptContext::for_planned_turn(
|
||||
ApiProvider::Deepseek,
|
||||
"deepseek-v4-flash".to_string(),
|
||||
Some(installed_limits),
|
||||
AppMode::Agent,
|
||||
None,
|
||||
GoalStatus::Active,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
);
|
||||
assert_eq!(
|
||||
engine
|
||||
.context_pressure_line("cross-route budget", &installed_context, None)
|
||||
.as_deref(),
|
||||
Some(
|
||||
"Context pressure: critical — CRITICAL: stop expanding scope; run /compact immediately or finish the current task"
|
||||
),
|
||||
"control fixture must be critical under the installed 4K limits"
|
||||
);
|
||||
let message = engine.user_text_message_from_snapshot(
|
||||
"cross-route budget".to_string(),
|
||||
&prompt_context.model,
|
||||
@@ -1092,8 +1131,12 @@ mod tests {
|
||||
})
|
||||
.next_back()
|
||||
.expect("turn metadata text");
|
||||
assert!(metadata.contains("123456 tokens"), "{metadata}");
|
||||
assert!(!metadata.contains(" / 4096 tokens"), "{metadata}");
|
||||
assert!(
|
||||
!metadata.contains("Context pressure:"),
|
||||
"planned route metadata must remain below warning: {metadata}"
|
||||
);
|
||||
assert!(!metadata.contains("123456 tokens"), "{metadata}");
|
||||
assert!(!metadata.contains("4096 tokens"), "{metadata}");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -6535,8 +6535,8 @@ fn normalize_representative_prompt(text: &str, workspace: &Path, home: &Path) ->
|
||||
.fold(text.to_string(), |normalized, (path, replacement)| {
|
||||
normalized.replace(path.to_string_lossy().as_ref(), replacement)
|
||||
});
|
||||
// The environment block truthfully reports the host OS per render; the
|
||||
// contract tracks content stability modulo host facts, so pin it here.
|
||||
// Platform remains an actionable host fact in the stable environment
|
||||
// block. Pin it so this fixture measures prompt structure across hosts.
|
||||
normalized.replace(
|
||||
&format!("- platform: {}", std::env::consts::OS),
|
||||
"- platform: <PLATFORM>",
|
||||
@@ -9271,7 +9271,6 @@ fn mode_invariant_matrix_covers_context_catalog_subagents_and_prompt_metadata()
|
||||
struct ModeCase {
|
||||
name: &'static str,
|
||||
mode: AppMode,
|
||||
setting: &'static str,
|
||||
prompt_marker: &'static str,
|
||||
shell_policy: ShellPolicy,
|
||||
sandbox: ExpectedSandbox,
|
||||
@@ -9286,7 +9285,6 @@ fn mode_invariant_matrix_covers_context_catalog_subagents_and_prompt_metadata()
|
||||
ModeCase {
|
||||
name: "plan",
|
||||
mode: AppMode::Plan,
|
||||
setting: "plan",
|
||||
prompt_marker: "##### Mode: Plan",
|
||||
shell_policy: ShellPolicy::None,
|
||||
sandbox: ExpectedSandbox::ReadOnly,
|
||||
@@ -9299,7 +9297,6 @@ fn mode_invariant_matrix_covers_context_catalog_subagents_and_prompt_metadata()
|
||||
ModeCase {
|
||||
name: "agent",
|
||||
mode: AppMode::Agent,
|
||||
setting: "agent",
|
||||
prompt_marker: "##### Mode: Agent",
|
||||
shell_policy: ShellPolicy::Full,
|
||||
sandbox: ExpectedSandbox::WorkspaceWrite,
|
||||
@@ -9312,7 +9309,6 @@ fn mode_invariant_matrix_covers_context_catalog_subagents_and_prompt_metadata()
|
||||
ModeCase {
|
||||
name: "agent-full-access",
|
||||
mode: AppMode::Agent,
|
||||
setting: "agent",
|
||||
prompt_marker: "##### Mode: Agent",
|
||||
shell_policy: ShellPolicy::Full,
|
||||
sandbox: ExpectedSandbox::DangerFullAccess,
|
||||
@@ -9325,7 +9321,6 @@ fn mode_invariant_matrix_covers_context_catalog_subagents_and_prompt_metadata()
|
||||
ModeCase {
|
||||
name: "auto-compat",
|
||||
mode: AppMode::Auto,
|
||||
setting: "agent",
|
||||
prompt_marker: "##### Mode: Agent",
|
||||
shell_policy: ShellPolicy::Full,
|
||||
sandbox: ExpectedSandbox::WorkspaceWrite,
|
||||
@@ -9338,7 +9333,6 @@ fn mode_invariant_matrix_covers_context_catalog_subagents_and_prompt_metadata()
|
||||
ModeCase {
|
||||
name: "operate",
|
||||
mode: AppMode::Operate,
|
||||
setting: "operate",
|
||||
prompt_marker: "##### Mode: Operate",
|
||||
shell_policy: ShellPolicy::Full,
|
||||
sandbox: ExpectedSandbox::WorkspaceWrite,
|
||||
@@ -9353,7 +9347,6 @@ fn mode_invariant_matrix_covers_context_catalog_subagents_and_prompt_metadata()
|
||||
// surfaces now speak Act (invisible one-way permission shorthand).
|
||||
name: "yolo",
|
||||
mode: AppMode::Yolo,
|
||||
setting: "agent",
|
||||
prompt_marker: "##### Mode: Agent",
|
||||
shell_policy: ShellPolicy::Full,
|
||||
sandbox: ExpectedSandbox::DangerFullAccess,
|
||||
@@ -9487,13 +9480,20 @@ fn mode_invariant_matrix_covers_context_catalog_subagents_and_prompt_metadata()
|
||||
panic!("{}: expected text metadata block", case.name);
|
||||
};
|
||||
assert!(
|
||||
text.contains(&format!("Current mode: {}", case.setting)),
|
||||
text.contains(&format!(
|
||||
"Current permission posture: {}",
|
||||
case.approval_mode.permission_chip_label()
|
||||
)),
|
||||
"{}: {text}",
|
||||
case.name
|
||||
);
|
||||
// turn_meta carries the mode as a *fact*; the doctrine ships once in
|
||||
// the stable prefix (#4780). Assert both halves so neither can be
|
||||
// dropped silently the way the overlay was.
|
||||
// Mode doctrine ships once in the stable prefix (#4780). The turn
|
||||
// block carries only the independently actionable permission posture.
|
||||
assert!(
|
||||
!text.contains("Current mode:"),
|
||||
"{}: turn metadata must not repeat the mode: {text}",
|
||||
case.name
|
||||
);
|
||||
assert!(
|
||||
!text.contains(case.prompt_marker),
|
||||
"{}: turn metadata must not re-embed mode doctrine: {text}",
|
||||
@@ -11606,13 +11606,35 @@ fn turn_metadata_includes_current_local_date_without_working_set() {
|
||||
let today = chrono::Local::now().format("%Y-%m-%d").to_string();
|
||||
assert!(text.starts_with("<turn_meta>\n"));
|
||||
assert!(text.contains(&format!("Current local date: {today}")));
|
||||
assert!(text.contains("Current model: deepseek-v4-flash"));
|
||||
assert!(text.contains("Input provenance: external_user"));
|
||||
assert!(text.contains("Input authority: external_current_turn"));
|
||||
assert!(
|
||||
text.contains(&format!("Current workspace: {}", tmp.path().display())),
|
||||
"workspace must remain in the block: {text}"
|
||||
);
|
||||
assert!(
|
||||
text.contains("Current permission posture: Ask"),
|
||||
"the active posture must remain model-visible: {text}"
|
||||
);
|
||||
// Turn-meta diet: no telemetry may re-enter the per-turn block.
|
||||
for telemetry in [
|
||||
"Current model:",
|
||||
"Current mode:",
|
||||
"Input provenance:",
|
||||
"Input authority:",
|
||||
"Auto model route:",
|
||||
"Auto reasoning effort:",
|
||||
"Session token usage:",
|
||||
"Active goal resource usage:",
|
||||
"Active goal token budget:",
|
||||
] {
|
||||
assert!(
|
||||
!text.contains(telemetry),
|
||||
"{telemetry} leaked into turn_meta: {text}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_metadata_surfaces_context_and_resource_usage() {
|
||||
fn turn_metadata_surfaces_goal_budget_only_while_goal_active() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let config = EngineConfig {
|
||||
model: "deepseek-v4-flash".to_string(),
|
||||
@@ -11620,6 +11642,8 @@ fn turn_metadata_surfaces_context_and_resource_usage() {
|
||||
..Default::default()
|
||||
};
|
||||
let (mut engine, _handle) = Engine::new(config, &Config::default());
|
||||
// Even with session usage recorded, the per-turn block must not surface
|
||||
// it: totals/cache figures are UI telemetry, not model steering signal.
|
||||
engine.session.total_usage.add(&Usage {
|
||||
input_tokens: 1_200,
|
||||
output_tokens: 300,
|
||||
@@ -11642,81 +11666,68 @@ fn turn_metadata_surfaces_context_and_resource_usage() {
|
||||
panic!("expected text metadata block");
|
||||
};
|
||||
|
||||
assert!(text.contains("Context pressure:"), "got: {text}");
|
||||
assert!(text.contains("tokens;"), "got: {text}");
|
||||
// The goal budget stays (model pacing), and only while the goal is active.
|
||||
assert!(
|
||||
text.contains("input tokens available"),
|
||||
"context headroom should be model-visible: {text}"
|
||||
text.contains("Active goal token budget: 2000"),
|
||||
"goal budget should be model-visible: {text}"
|
||||
);
|
||||
assert!(
|
||||
text.contains("Session token usage: 1500 total (1200 input, 300 output"),
|
||||
"session usage should be model-visible: {text}"
|
||||
);
|
||||
assert!(text.contains("cache hits 800"), "got: {text}");
|
||||
assert!(text.contains("cache writes 400"), "got: {text}");
|
||||
assert!(
|
||||
text.contains("Active goal resource usage:"),
|
||||
"active goal resource usage should be model-visible: {text}"
|
||||
);
|
||||
assert!(text.contains("50% budget"), "got: {text}");
|
||||
assert!(text.contains("10.0 tok/s"), "got: {text}");
|
||||
}
|
||||
// Usage/time deltas, rates, and continuation counts are telemetry.
|
||||
for telemetry in [
|
||||
"Session token usage:",
|
||||
"cache hits",
|
||||
"cache writes",
|
||||
"Active goal resource usage:",
|
||||
"tok/s",
|
||||
"continuations",
|
||||
"50% budget",
|
||||
] {
|
||||
assert!(
|
||||
!text.contains(telemetry),
|
||||
"{telemetry} leaked into turn_meta: {text}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_metadata_escalates_context_pressure_at_warning_threshold() {
|
||||
// Without an active goal the budget line must vanish entirely.
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let config = EngineConfig {
|
||||
model: "deepseek-v4-flash".to_string(),
|
||||
workspace: tmp.path().to_path_buf(),
|
||||
..Default::default()
|
||||
};
|
||||
let (mut engine, _handle) = Engine::new(config, &Config::default());
|
||||
|
||||
// Fabricate high context usage by stuffing the session with a large user message.
|
||||
let large = "x".repeat(900_000);
|
||||
engine.session.messages.push(Message {
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentBlock::Text {
|
||||
text: large,
|
||||
cache_control: None,
|
||||
}],
|
||||
});
|
||||
|
||||
let user_msg = engine.user_text_message_with_turn_metadata("wrap up".to_string());
|
||||
let last_block = user_msg.content.last().expect("turn metadata block");
|
||||
let ContentBlock::Text { text, .. } = last_block else {
|
||||
let (engine, _handle) = Engine::new(config, &Config::default());
|
||||
let user_msg = engine.user_text_message_with_turn_metadata("no goal".to_string());
|
||||
let ContentBlock::Text { text, .. } = user_msg.content.last().expect("turn metadata block")
|
||||
else {
|
||||
panic!("expected text metadata block");
|
||||
};
|
||||
assert!(
|
||||
!text.contains("Active goal token budget:"),
|
||||
"budget must not be emitted when no goal is active: {text}"
|
||||
);
|
||||
}
|
||||
|
||||
if text.contains("Context pressure:") {
|
||||
let usage_line = text
|
||||
.lines()
|
||||
.find(|line| line.starts_with("Context pressure:"))
|
||||
.expect("context pressure line");
|
||||
if usage_line.contains('%') {
|
||||
let percent = usage_line
|
||||
.split('(')
|
||||
.nth(1)
|
||||
.and_then(|rest| rest.split('%').next())
|
||||
.and_then(|value| value.trim().parse::<f64>().ok())
|
||||
.unwrap_or(0.0);
|
||||
if percent >= crate::tui::context_inspector::CONTEXT_WARNING_THRESHOLD_PERCENT {
|
||||
assert!(
|
||||
usage_line.contains("ESCALATED"),
|
||||
"expected escalation copy at >=85%: {usage_line}"
|
||||
);
|
||||
} else {
|
||||
assert!(
|
||||
!usage_line.contains("ESCALATED"),
|
||||
"below 85% should stay informational: {usage_line}"
|
||||
);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn context_pressure_message_emits_only_at_warning_and_critical_thresholds() {
|
||||
const WARNING: &str = "Context pressure: warning — ESCALATED: prefer /compact, narrow scope, or finish the current task";
|
||||
const CRITICAL: &str = "Context pressure: critical — CRITICAL: stop expanding scope; run /compact immediately or finish the current task";
|
||||
|
||||
assert_eq!(context_pressure_message(84.99), None);
|
||||
assert_eq!(context_pressure_message(85.0), Some(WARNING));
|
||||
assert_eq!(context_pressure_message(94.99), Some(WARNING));
|
||||
assert_eq!(context_pressure_message(95.0), Some(CRITICAL));
|
||||
assert_eq!(context_pressure_message(100.0), Some(CRITICAL));
|
||||
|
||||
// Threshold labels steer a decision without exposing a continuously
|
||||
// changing percentage, token count, or headroom value.
|
||||
for line in [WARNING, CRITICAL] {
|
||||
assert!(!line.contains('%'), "{line}");
|
||||
assert!(!line.contains("tokens"), "{line}");
|
||||
assert!(!line.contains("headroom"), "{line}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_turn_metadata_marks_non_authoritative_input() {
|
||||
fn runtime_turn_metadata_condenses_non_authoritative_provenance_to_one_line() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let config = EngineConfig {
|
||||
workspace: tmp.path().to_path_buf(),
|
||||
@@ -11732,12 +11743,18 @@ fn runtime_turn_metadata_marks_non_authoritative_input() {
|
||||
panic!("expected text metadata block");
|
||||
};
|
||||
|
||||
assert!(text.contains("Input provenance: assistant_generated"));
|
||||
assert!(text.contains("Input authority: non_authoritative"));
|
||||
// Reduced authority on a non-external turn is the sole signal: one
|
||||
// condensed line, not the former two-line provenance/authority pair.
|
||||
assert!(
|
||||
text.contains("Input provenance: assistant_generated (non-authoritative)"),
|
||||
"{text}"
|
||||
);
|
||||
assert!(!text.contains("Input authority:"), "{text}");
|
||||
assert!(!text.contains("Input provenance: external_user"), "{text}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_metadata_includes_auto_model_route() {
|
||||
fn turn_metadata_omits_route_and_reasoning_effort_telemetry() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let config = EngineConfig {
|
||||
workspace: tmp.path().to_path_buf(),
|
||||
@@ -11757,10 +11774,103 @@ fn turn_metadata_includes_auto_model_route() {
|
||||
panic!("expected text metadata block");
|
||||
};
|
||||
|
||||
assert!(text.contains("Current model: deepseek-v4-pro"));
|
||||
assert!(text.contains("Auto model route: deepseek-v4-pro"));
|
||||
assert!(text.contains("Auto reasoning effort: max"));
|
||||
// Model, auto-route, and auto-reasoning-effort lines were pure telemetry
|
||||
// and must never re-enter the per-turn block.
|
||||
assert!(!text.contains("Current model:"), "{text}");
|
||||
assert!(!text.contains("Auto model route:"), "{text}");
|
||||
assert!(!text.contains("Auto reasoning effort:"), "{text}");
|
||||
assert!(!text.contains("debug this regression"));
|
||||
assert!(
|
||||
text.starts_with(
|
||||
"<turn_meta>
|
||||
Current local date:"
|
||||
),
|
||||
"{text}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_metadata_is_byte_identical_across_identical_consecutive_turns() {
|
||||
// Diet acceptance (captains-log #18/#21/#22): two identical consecutive
|
||||
// turns must produce byte-identical `<turn_meta>` blocks. Pre-diet the
|
||||
// block carried session totals, context-pressure counts, and goal usage
|
||||
// rates that drifted between turns even with unchanged inputs; today the
|
||||
// block carries only facts that are stable across ordinary turns.
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let config = EngineConfig {
|
||||
model: "deepseek-v4-flash".to_string(),
|
||||
workspace: tmp.path().to_path_buf(),
|
||||
..Default::default()
|
||||
};
|
||||
let (mut engine, _handle) = Engine::new(config, &Config::default());
|
||||
|
||||
// Use explicit route limits so the fixture exercises the critical band
|
||||
// without depending on a model catalog entry or provider default.
|
||||
engine.session.messages.push(Message {
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentBlock::Text {
|
||||
text: "x".repeat(100_000),
|
||||
cache_control: None,
|
||||
}],
|
||||
});
|
||||
let prompt_context = NextTurnPromptContext::for_planned_turn(
|
||||
ApiProvider::Deepseek,
|
||||
"deepseek-v4-flash".to_string(),
|
||||
Some(codewhale_config::route::RouteLimits {
|
||||
context_tokens: Some(10_000),
|
||||
input_tokens: None,
|
||||
output_tokens: Some(512),
|
||||
}),
|
||||
AppMode::Agent,
|
||||
None,
|
||||
crate::tools::goal::GoalStatus::Active,
|
||||
None,
|
||||
false,
|
||||
None,
|
||||
);
|
||||
|
||||
let meta_of = |msg: &Message| -> String {
|
||||
let ContentBlock::Text { text, .. } = msg.content.last().expect("turn metadata block")
|
||||
else {
|
||||
panic!("expected text metadata block");
|
||||
};
|
||||
text.clone()
|
||||
};
|
||||
let message_for = |engine: &Engine| {
|
||||
engine.user_text_message_from_snapshot(
|
||||
"stable input".to_string(),
|
||||
&prompt_context.model,
|
||||
false,
|
||||
None,
|
||||
false,
|
||||
UserInputProvenance::ExternalUser,
|
||||
TurnMetadataSnapshot {
|
||||
prompt_context: &prompt_context,
|
||||
system_prompt: None,
|
||||
approval_mode: engine.session.approval_mode,
|
||||
working_set: &engine.session.working_set,
|
||||
policy_narrowing: None,
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
let first = message_for(&engine);
|
||||
let first_meta = meta_of(&first);
|
||||
assert!(
|
||||
first_meta.contains("Context pressure: critical"),
|
||||
"fixture must exercise the pressure line: {first_meta}"
|
||||
);
|
||||
|
||||
// Turn 2 builds with the first message already in the session, exactly as
|
||||
// a real turn sequence would; the block must not change.
|
||||
engine.session.add_message(first);
|
||||
let second = message_for(&engine);
|
||||
let second_meta = meta_of(&second);
|
||||
|
||||
assert_eq!(
|
||||
first_meta, second_meta,
|
||||
"turn_meta must be byte-identical across identical consecutive turns"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -12011,8 +12121,11 @@ fn external_user_wording_does_not_downgrade_standing_authority() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turn_metadata_includes_plan_mode_as_fact_only() {
|
||||
// #4780: turn_meta carries mode as a label, not the full mode doctrine.
|
||||
fn turn_metadata_leaves_mode_entirely_to_the_system_overlay() {
|
||||
// #4780 + turn-meta diet: mode doctrine ships once in the stable system
|
||||
// overlay, and the steady-state mode label was removed from turn_meta as
|
||||
// redundant with that overlay. Neither the label nor the doctrine may
|
||||
// re-enter the per-turn block.
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let config = EngineConfig {
|
||||
workspace: tmp.path().to_path_buf(),
|
||||
@@ -12033,7 +12146,7 @@ fn turn_metadata_includes_plan_mode_as_fact_only() {
|
||||
panic!("expected text metadata block");
|
||||
};
|
||||
|
||||
assert!(text.contains("Current mode: plan"), "got: {text}");
|
||||
assert!(!text.contains("Current mode:"), "got: {text}");
|
||||
assert!(
|
||||
!text.contains("Current mode policy"),
|
||||
"mode doctrine must not re-enter turn_meta: {text}"
|
||||
@@ -12050,7 +12163,8 @@ fn turn_metadata_includes_plan_mode_as_fact_only() {
|
||||
|
||||
#[test]
|
||||
fn turn_metadata_projects_permission_posture_as_fact_only() {
|
||||
// #4780: posture is a fact; question-discipline prose stays out of turn_meta.
|
||||
// #4780 + turn-meta diet: the active posture remains an actionable fact;
|
||||
// question-discipline prose stays out of turn_meta.
|
||||
use crate::tui::approval::ApprovalMode;
|
||||
|
||||
let cases = [
|
||||
@@ -12126,15 +12240,15 @@ fn turn_metadata_preserves_standing_full_access_for_subagent_handoff() {
|
||||
panic!("expected text turn metadata");
|
||||
};
|
||||
|
||||
// Act mode and the permission posture are independent: the child handoff
|
||||
// must keep the visible Act mode and the standing Full Access authority.
|
||||
assert!(text.contains("Current mode: agent"), "{text}");
|
||||
// A child handoff cannot grant new authority, but it retains the standing
|
||||
// posture and names its reduced provenance in one condensed line.
|
||||
assert!(!text.contains("Current mode:"), "{text}");
|
||||
assert!(
|
||||
text.contains("Current permission posture: Full Access"),
|
||||
"{text}"
|
||||
);
|
||||
assert!(
|
||||
text.contains("Input authority: non_authoritative"),
|
||||
text.contains("Input provenance: subagent_handoff (non-authoritative)"),
|
||||
"{text}"
|
||||
);
|
||||
}
|
||||
@@ -14247,9 +14361,11 @@ async fn background_completion_after_a_turn_is_delivered_once_on_the_next_turn()
|
||||
/// Providers cache on the longest common prefix of the request, so anything
|
||||
/// that rewrites an *already-sent* message — or the system prompt — between
|
||||
/// turns invalidates every cached token after it and silently raises cost.
|
||||
/// This is easy to regress because per-turn `<turn_meta>` legitimately varies
|
||||
/// (context pressure and session token totals change every turn); what makes
|
||||
/// that safe is that a message is frozen once it enters the session.
|
||||
/// The turn-meta diet removed the per-turn telemetry (session totals, pressure
|
||||
/// counts, goal rates) that used to make `<turn_meta>` drift every turn; it
|
||||
/// now varies only on genuinely new signal (date boundary, working-set
|
||||
/// changes, threshold crossings). Freezing a message once it enters the
|
||||
/// session keeps every earlier message byte-identical regardless.
|
||||
///
|
||||
/// The test pins both halves of that contract:
|
||||
/// 1. `<turn_meta>` is the *last* content block of a user message, so the
|
||||
|
||||
+28
-36
@@ -171,39 +171,30 @@ fn translation_target_language_for_tag(locale_tag: &str) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
/// Render a `## Environment` block listing the resolved locale tag,
|
||||
/// runtime version, host platform, login shell, and current working directory.
|
||||
/// Render a `## Environment` block listing the resolved locale tag and the
|
||||
/// actionable host facts that affect command syntax.
|
||||
///
|
||||
/// The block is appended to the workspace-static portion of the
|
||||
/// system prompt (after mode prompt + project context, before
|
||||
/// configured instructions / skills). `locale_tag` is resolved by the caller
|
||||
/// from `Settings` so this function stays I/O-free.
|
||||
/// The block is appended to the workspace-static portion of the system
|
||||
/// prompt (after mode prompt + project context, before configured
|
||||
/// instructions / skills). `locale_tag` is resolved by the caller from
|
||||
/// `Settings` so this function stays I/O-free.
|
||||
///
|
||||
/// `platform` and `shell` remain because they change how commands must be
|
||||
/// written and are stable for the life of the process. The release version was
|
||||
/// removed by the turn-meta diet: it is telemetry the model cannot act on and
|
||||
/// churned the otherwise-static prefix on every release. The live workspace
|
||||
/// path is delivered per-turn via `<turn_meta>` (see `turn_metadata_block`).
|
||||
fn render_environment_block(_workspace: &Path, locale_tag: &str) -> String {
|
||||
let codewhale_version = env!("CARGO_PKG_VERSION");
|
||||
let platform = std::env::consts::OS;
|
||||
let shell = crate::shell_dispatcher::global_dispatcher()
|
||||
.kind()
|
||||
.binary()
|
||||
.to_string();
|
||||
|
||||
// The workspace path (`pwd`) is intentionally delivered per-turn via the
|
||||
// `<turn_meta>` block (see `turn_metadata_block`) rather than embedded here.
|
||||
//
|
||||
// Rationale: when the workspace path changes between sessions (e.g. an
|
||||
// ephemeral per-session workspace), a volatile value inside the otherwise
|
||||
// static system prefix invalidates the inference server's prefix cache at
|
||||
// that exact point. The cache then only partially matches and the tail must
|
||||
// be re-prefilled from the divergence boundary. On backends that pair prefix
|
||||
// caching with speculative decoding, this partial re-prefill can perturb the
|
||||
// logits at the boundary enough to degrade structured tool-call emission
|
||||
// (the model regresses to bare text). Keeping the static system prefix
|
||||
// byte-identical across sessions lets the prefix cache be reused; the live
|
||||
// workspace path still reaches the model every turn through `turn_meta`.
|
||||
format!(
|
||||
"## Environment\n\
|
||||
\n\
|
||||
- lang: {locale_tag}\n\
|
||||
- codewhale_version: {codewhale_version}\n\
|
||||
- platform: {platform}\n\
|
||||
- shell: {shell}"
|
||||
)
|
||||
@@ -1359,7 +1350,8 @@ pub fn system_prompt_for_mode_with_context_skills_session_and_approval(
|
||||
// Permissions fragment: configured `instructions = [...]` files (#454).
|
||||
let permissions_body = instructions.and_then(render_instructions_block);
|
||||
|
||||
// Route fragment: active model / verbosity / translation posture.
|
||||
// Route fragment: verbosity / translation posture (the model id was
|
||||
// removed by the turn-meta diet — it is telemetry the model cannot act on).
|
||||
let route_body = render_route_fragment(&session_context);
|
||||
|
||||
// Token-budget / continuity fragment: prior-session handoff relay.
|
||||
@@ -1417,9 +1409,7 @@ fn render_route_fragment(session_context: &PromptSessionContext<'_>) -> String {
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or("default");
|
||||
format!(
|
||||
"model: {}\nverbosity: {}\ntranslation: {}",
|
||||
session_context.model_id.trim(),
|
||||
verbosity,
|
||||
"verbosity: {verbosity}\ntranslation: {}",
|
||||
if session_context.translation_enabled {
|
||||
"on"
|
||||
} else {
|
||||
@@ -2082,17 +2072,15 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_environment_block_lists_supplied_locale_and_workspace() {
|
||||
fn render_environment_block_keeps_actionable_host_facts_without_version() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
let block = render_environment_block(tmp.path(), "zh-Hans");
|
||||
assert!(block.starts_with("## Environment"));
|
||||
assert!(block.contains("- lang: zh-Hans"));
|
||||
assert!(block.contains(&format!(
|
||||
"- codewhale_version: {}",
|
||||
env!("CARGO_PKG_VERSION")
|
||||
)));
|
||||
// pwd is now delivered per-turn via `turn_meta`, not in the static block.
|
||||
// The workspace remains per-turn and the release version is telemetry;
|
||||
// platform and shell still steer valid command syntax.
|
||||
assert!(!block.contains("- pwd:"));
|
||||
assert!(!block.contains("- codewhale_version:"));
|
||||
assert!(block.contains("- platform:"));
|
||||
assert!(block.contains("- shell:"));
|
||||
}
|
||||
@@ -2435,7 +2423,9 @@ mod tests {
|
||||
));
|
||||
assert!(prompt.contains("## Environment"));
|
||||
assert!(prompt.contains("- lang: ja"));
|
||||
assert!(prompt.contains("- codewhale_version:"));
|
||||
assert!(!prompt.contains("- codewhale_version:"));
|
||||
assert!(prompt.contains("- platform:"));
|
||||
assert!(prompt.contains("- shell:"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2479,9 +2469,7 @@ mod tests {
|
||||
let user_block_at = prompt
|
||||
.find("<codewhale_user_constitution")
|
||||
.expect("user constitution block");
|
||||
let env_at = prompt
|
||||
.find("- codewhale_version:")
|
||||
.expect("rendered environment block");
|
||||
let env_at = prompt.find("- lang:").expect("rendered environment block");
|
||||
assert!(
|
||||
base_at < user_block_at && user_block_at < env_at,
|
||||
"user constitution should be its own layer after the base/project context and before volatile environment data"
|
||||
@@ -3881,7 +3869,11 @@ mod tests {
|
||||
assert!(flat.contains(crate::model_context::FragmentId::Workspace.marker()));
|
||||
assert!(flat.contains(crate::model_context::FragmentId::Route.marker()));
|
||||
assert!(flat.contains("## Environment"));
|
||||
assert!(flat.contains("model: deepseek-v4-pro"));
|
||||
assert!(
|
||||
flat.contains("verbosity: concise") && flat.contains("translation: off"),
|
||||
"route fragment keeps verbosity/translation but drops the model id"
|
||||
);
|
||||
assert!(!flat.contains("model: deepseek-v4-pro"));
|
||||
assert!(flat.contains("<session_goal>"));
|
||||
assert!(flat.contains("ship WorldState Blocks"));
|
||||
assert!(flat.contains("remember the cutover"));
|
||||
|
||||
@@ -53,20 +53,17 @@ const SHELL_COMPLETION_EVENT_SUFFIX: &str = "\n</codewhale:runtime_event>";
|
||||
|
||||
const SUBAGENT_HANDOFF_TURN_META: &str = concat!(
|
||||
"<turn_meta>\n",
|
||||
"Input provenance: subagent_handoff\n",
|
||||
"Input authority: non_authoritative\n",
|
||||
"Input provenance: subagent_handoff (non-authoritative)\n",
|
||||
"</turn_meta>",
|
||||
);
|
||||
const SHELL_COMPLETION_HANDOFF_TURN_META: &str = concat!(
|
||||
"<turn_meta>\n",
|
||||
"Input provenance: shell_completion\n",
|
||||
"Input authority: non_authoritative\n",
|
||||
"Input provenance: shell_completion (non-authoritative)\n",
|
||||
"</turn_meta>",
|
||||
);
|
||||
const RESTORED_CHECKPOINT_TURN_META: &str = concat!(
|
||||
"<turn_meta>\n",
|
||||
"Input provenance: subagent_handoff\n",
|
||||
"Input authority: non_authoritative\n",
|
||||
"Input provenance: subagent_handoff (non-authoritative)\n",
|
||||
"Restore projection: subagent_checkpoint_v1\n",
|
||||
"</turn_meta>",
|
||||
);
|
||||
@@ -258,6 +255,15 @@ fn is_subagent_handoff_turn_meta(text: &str) -> bool {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Current shape (turn-meta diet): a single condensed provenance line.
|
||||
if has_one_exact_metadata_line(
|
||||
body,
|
||||
"Input provenance:",
|
||||
"Input provenance: subagent_handoff (non-authoritative)",
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
// Legacy shape (pre-diet saved sessions): the two-line pair.
|
||||
has_one_exact_metadata_line(
|
||||
body,
|
||||
"Input provenance:",
|
||||
@@ -748,7 +754,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restore_projection_accepts_runtime_generated_rich_turn_metadata() {
|
||||
fn restore_projection_accepts_legacy_rich_turn_metadata() {
|
||||
let raw = Message {
|
||||
role: "user".to_string(),
|
||||
content: vec![
|
||||
|
||||
@@ -1079,7 +1079,6 @@ impl WorkingSet {
|
||||
|
||||
let mut lines: Vec<String> = Vec::new();
|
||||
lines.push("## Repo Working Set".to_string());
|
||||
lines.push(format!("Workspace: {}", workspace.display()));
|
||||
|
||||
if let Some(summary) = repo_summary {
|
||||
lines.push(summary);
|
||||
@@ -1771,6 +1770,7 @@ mod tests {
|
||||
let block = ws.summary_block(tmp.path()).expect("block");
|
||||
|
||||
assert!(block.contains("Repo Working Set"));
|
||||
assert!(!block.contains("Workspace:"));
|
||||
assert!(block.contains("Cargo.toml"));
|
||||
assert!(block.contains("src"));
|
||||
assert!(block.contains("src/lib.rs"));
|
||||
|
||||
@@ -5,43 +5,43 @@
|
||||
"fixture_id": "representative-v1",
|
||||
"stages": {
|
||||
"base": {
|
||||
"bytes": 11276,
|
||||
"identity_sha256": "97bd3dbb0c3abccc560e97547d6d94a51553fcc319f77f617b95cb1c15a0b07d"
|
||||
"bytes": 11232,
|
||||
"identity_sha256": "cfc75fc3e8faceddaec4e0611534245d83e36cac0c121b22be050866a1a0b9a7"
|
||||
},
|
||||
"goal": {
|
||||
"bytes": 14607,
|
||||
"bytes": 14563,
|
||||
"delta_bytes": 80,
|
||||
"identity_sha256": "4c46a340f126b3cc13bfd939ea643452b365b452051c0ed7ce30c44a432dd739"
|
||||
"identity_sha256": "26606dd13fe77413eaf8680d9663bf925658a57bbcbd8d5a882226f7e419089b"
|
||||
},
|
||||
"handoff": {
|
||||
"bytes": 14995,
|
||||
"bytes": 14951,
|
||||
"delta_bytes": 388,
|
||||
"identity_sha256": "72cb7f323828193f0515a9a68ce68fb1825d5bfa1617b03d03cfb2eead81d0dd"
|
||||
"identity_sha256": "bd2e996753c4611ad99431b2fc7774a091519ed4f616b2be386e757bc6cf1526"
|
||||
},
|
||||
"instructions": {
|
||||
"bytes": 11728,
|
||||
"bytes": 11684,
|
||||
"delta_bytes": 131,
|
||||
"identity_sha256": "39c3f2fb4f58a05a05afa9917a99dadd2002fd27c8a3342b0542d7cdb7d649de"
|
||||
"identity_sha256": "dc5fb6700cb5b96aad1bb2c2cb39299241fb62acd785fabd57ce4177ba6b9da6"
|
||||
},
|
||||
"memory": {
|
||||
"bytes": 14527,
|
||||
"bytes": 14483,
|
||||
"delta_bytes": 1649,
|
||||
"identity_sha256": "661944eb773653ef047e1d8d2d1512c5bc7be60fa8f33d224b439edc60ef0e1d"
|
||||
"identity_sha256": "95e89028c7d09988878376d4d836c90bc2c6c5f87147ce46738a59a74abc8e1f"
|
||||
},
|
||||
"project": {
|
||||
"bytes": 11597,
|
||||
"bytes": 11553,
|
||||
"delta_bytes": 321,
|
||||
"identity_sha256": "07a84f222831b4ae963a7bc84a2931a9f4e4a2227ec699260bba84dcb341001c"
|
||||
"identity_sha256": "435a780b13f567aa948a57eba201ad8cd154a7851cc72b44785d9d875c63e1bb"
|
||||
},
|
||||
"skill": {
|
||||
"bytes": 12878,
|
||||
"bytes": 12834,
|
||||
"delta_bytes": 1150,
|
||||
"identity_sha256": "0060208dc8e2c6034e0c1fc0d1b67a1d2d2487922128b145a3c3e3c1543ac857"
|
||||
"identity_sha256": "7913d1a133f8fbdc4edad68f561f1f81199b2eac318625f05548e9f521a4dff7"
|
||||
}
|
||||
},
|
||||
"system_prompt_blocks": 6,
|
||||
"total_bytes": 14995,
|
||||
"total_tokens_est": 3749
|
||||
"total_bytes": 14951,
|
||||
"total_tokens_est": 3738
|
||||
},
|
||||
"schema_version": 1,
|
||||
"skill_discovery": {
|
||||
@@ -62,22 +62,22 @@
|
||||
"mode_instructions_bytes": 802,
|
||||
"mode_instructions_tokens_est": 201,
|
||||
"system_prompt_blocks": 4,
|
||||
"system_prompt_bytes": 11276,
|
||||
"system_prompt_tokens_est": 2819
|
||||
"system_prompt_bytes": 11232,
|
||||
"system_prompt_tokens_est": 2808
|
||||
},
|
||||
"operate": {
|
||||
"mode_instructions_bytes": 1671,
|
||||
"mode_instructions_tokens_est": 418,
|
||||
"system_prompt_blocks": 4,
|
||||
"system_prompt_bytes": 12145,
|
||||
"system_prompt_tokens_est": 3037
|
||||
"system_prompt_bytes": 12101,
|
||||
"system_prompt_tokens_est": 3026
|
||||
},
|
||||
"plan": {
|
||||
"mode_instructions_bytes": 517,
|
||||
"mode_instructions_tokens_est": 130,
|
||||
"system_prompt_blocks": 4,
|
||||
"system_prompt_bytes": 10991,
|
||||
"system_prompt_tokens_est": 2748
|
||||
"system_prompt_bytes": 10947,
|
||||
"system_prompt_tokens_est": 2737
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user