From c0ddbe9cdb2287253617f8ea2a4b96830718f358 Mon Sep 17 00:00:00 2001 From: Cody De Arkland Date: Tue, 26 May 2026 09:46:53 -0700 Subject: [PATCH] Rephrase agent advisory and gate by CLI version The previous advisory text was flagged as prompt-injection by Claude and other agents that scan for directive framing. Reports from users showed the warning firing on every other Railway command in agent sessions. Two changes here: 1. Rephrase the message to be neutral status, not a directive aimed at the agent. Drops "Ask the user if they would like this agent to run ..." and the `-y` non-interactive flag from the suggested command. The new message states a fact and offers a remediation; the agent decides what to do with it. 2. Replace the 24-hour per-command throttle with a per-CLI-version gate. The advisory now fires once per installed CLI version instead of every 24 hours per command. Upgrading the CLI re-arms it exactly once. State file (`~/.railway/agent-state.json`) gains `advisory.lastShownCliVersion`. The old `advisories` map is dropped; existing state files deserialize cleanly (extra fields ignored) and will see the advisory at most one more time after upgrade. `RAILWAY_AGENT_ADVISORY=0` still disables the advisory entirely, and `railway setup agent` still suppresses it permanently via `record_setup_complete`. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/util/agent_advisory.rs | 42 ++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/src/util/agent_advisory.rs b/src/util/agent_advisory.rs index f6218b172..eb6b50277 100644 --- a/src/util/agent_advisory.rs +++ b/src/util/agent_advisory.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, fs::File, io::Read, path::PathBuf}; +use std::{fs::File, io::Read, path::PathBuf}; use anyhow::{Context, Result}; use chrono::{DateTime, Utc}; @@ -8,7 +8,6 @@ use serde::{Deserialize, Serialize}; use crate::{telemetry, util}; const STATE_VERSION: u32 = 1; -const ADVISORY_INTERVAL_HOURS: i64 = 24; const DISABLE_ENV: &str = "RAILWAY_AGENT_ADVISORY"; const FORCE_ENV: &str = "RAILWAY_AGENT"; @@ -18,7 +17,7 @@ struct AgentState { #[serde(default)] setup: SetupState, #[serde(default)] - advisories: BTreeMap, + advisory: AdvisoryState, } #[derive(Debug, Default, Serialize, Deserialize)] @@ -31,6 +30,7 @@ struct SetupState { #[derive(Debug, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct AdvisoryState { + last_shown_cli_version: Option, last_shown_at: Option>, } @@ -156,15 +156,14 @@ fn should_skip_for_args(raw_args: &[String]) -> bool { }) } -fn advisory_is_recent(state: &AgentState, command: &str) -> bool { +// Suppress the advisory if we've already shown it for the currently running +// CLI version. Upgrading the CLI re-arms the advisory exactly once. +fn advisory_already_shown_for_current_cli(state: &AgentState) -> bool { state - .advisories - .get(command) - .and_then(|advisory| advisory.last_shown_at) - .map(|last_shown_at| { - Utc::now() - last_shown_at < chrono::Duration::hours(ADVISORY_INTERVAL_HOURS) - }) - .unwrap_or(false) + .advisory + .last_shown_cli_version + .as_deref() + .is_some_and(|shown| shown == env!("CARGO_PKG_VERSION")) } pub async fn maybe_show(raw_args: &[String], command: Option<&str>) { @@ -180,25 +179,20 @@ pub async fn maybe_show(raw_args: &[String], command: Option<&str>) { } let mut state = read_state(); - if agent_setup_is_current(&state) || advisory_is_recent(&state, command) { + if agent_setup_is_current(&state) || advisory_already_shown_for_current_cli(&state) { return; } eprintln!( - "\n{}\n\n{}\n\n{}", - "IMPORTANT: Railway agent tooling not detected.".yellow().bold(), - "Ask the user if they would like this agent to run `railway setup agent -y` to configure the Railway agent tooling (agent skills and MCP configurations)." - .yellow(), - "Your agent will work much more effectively in Railway with these installed.".yellow() - .yellow() + "\n{}\n{}", + "Railway agent tooling (skills + MCP) isn't installed.".yellow(), + "Run `railway setup agent` to configure it.".dimmed(), ); - state.advisories.insert( - command.to_string(), - AdvisoryState { - last_shown_at: Some(Utc::now()), - }, - ); + state.advisory = AdvisoryState { + last_shown_cli_version: Some(env!("CARGO_PKG_VERSION").to_string()), + last_shown_at: Some(Utc::now()), + }; let _ = write_state(&state);