Synced from monorepo
Changes: - Classify clipboard delivery confidence - Add durable session update append - Scope the xAI session bearer to first-party memory embedding endpoints - Persist subagent outputs to disk and bound long-lived agent state - Add MiniSweAgent:bash for mini-swe-agent parity - Revert taking local sessions off the persistent shell - Contextual tip recommending grok wrap on SSH sessions - Voice STT bearer from model BYOK env_key/api_key - Define exact website policies for sandbox - Gate unsafe shell environments - Shared pin hoist; single require_sha gate for marketplace plugins - Server-signed is-managed claim (closes sidecar-removal downgrade) - Optional require_sha pin for remote plugin installs - Show session title and last exchange in the exit resume hint - Gate shell output redirects - Warn when fail_closed is present but not a boolean - Add canonical text editing core (ratatui-textarea) - Keep execution state out of goal scratch - Add acknowledged persistence primitives - Inherit child network restrictions in sandbox - Fail closed when hook matchers fail to recompile - Add MCP setup preferences for plugin MCPs - Gate sourced shell scripts - Gate file-typed project hooks - grok wrap: restore terminal modes on child death - Harden owner-only permissions on auth and MCP credentials - Create crash dump files with owner-only permissions - Write the agent_id cache owner-only (0600) - SessionMetrics mode skips Mixpanel profile sync - Dashboard: slim live-tail peek - Yank full queued prompt text, not (+N lines) - Defeat clock-rollback on the signed managed-config cache - Stop early session/cancel from overtaking the prompt and wedging the turn slot - Self-heal a diverged agent entrypoint on startup - Add matched inference expectations in test-support - Add AuthSingleFlight cancel/successor gap tests - Remove consumer from external OTEL allowlist and pin scrub coverage - Enable /copy in minimal mode - Surface capacity and API-key detail on 429 errors - Single-flight interactive auth - Fix PageUp/PageDown skipping lines behind sticky prompt header
This commit is contained in:
parent
8adf9013a0
commit
98c3b2438a
225 changed files with 18836 additions and 7156 deletions
|
|
@ -915,6 +915,10 @@ pub struct AppView {
|
|||
/// first evaluation at a stable agent-view draw (regardless of outcome),
|
||||
/// so later resizes can never re-trigger the tip within this run.
|
||||
pub small_screen_tip_evaluated: bool,
|
||||
/// One-shot gate for the SSH `grok wrap` tip: set after the first
|
||||
/// evaluation at a stable agent-view draw (the environment gates are
|
||||
/// process-constant, so one evaluation decides the run).
|
||||
pub ssh_wrap_tip_evaluated: bool,
|
||||
/// Focus-scoped, opportunistically-polled clipboard-image tip state: poll
|
||||
/// throttle, changeCount delta-detection, fire cooldown, and changeCount
|
||||
/// dedup (macOS-only at the probe layer).
|
||||
|
|
@ -955,6 +959,9 @@ pub struct AppView {
|
|||
pub auth_code_input: String,
|
||||
/// Monotonically increasing sequence number for auth requests.
|
||||
pub next_auth_request_seq: u64,
|
||||
/// Abort handle for the in-flight `PollAuthUrl` task (with its request_seq).
|
||||
/// Aborted alongside the Authenticate task in single-flight re-login.
|
||||
pub auth_url_poll_handle: Option<(u64, tokio::task::AbortHandle)>,
|
||||
/// Every session/chat/worktree/prompt action deferred behind startup gates.
|
||||
pub deferred_startup: crate::app::session_startup::DeferredStartupActions,
|
||||
/// Whether deferred welcome-screen login should force OAuth.
|
||||
|
|
@ -1275,6 +1282,7 @@ impl AppView {
|
|||
tip_seen_counts: Default::default(),
|
||||
last_known_terminal_rows: 0,
|
||||
small_screen_tip_evaluated: false,
|
||||
ssh_wrap_tip_evaluated: false,
|
||||
clipboard_focus_tip: Default::default(),
|
||||
new_session_worktree_mode: WorktreeMode::Never,
|
||||
fork_worktree_mode: WorktreeMode::Ask,
|
||||
|
|
@ -1289,6 +1297,7 @@ impl AppView {
|
|||
auth_start_mode: AuthMode::Pending,
|
||||
auth_code_input: String::new(),
|
||||
next_auth_request_seq: 1,
|
||||
auth_url_poll_handle: None,
|
||||
deferred_startup: Default::default(),
|
||||
auth_use_oauth: false,
|
||||
auth_clipboard_copied: false,
|
||||
|
|
@ -1647,6 +1656,16 @@ impl AppView {
|
|||
None
|
||||
}
|
||||
}
|
||||
/// The active agent's view, when an agent tab is focused.
|
||||
///
|
||||
/// Always the root agent, even when a subagent view is focused within the
|
||||
/// tab; for subagent-aware resolution use `dispatch::ctx::get_active_agent`.
|
||||
pub fn active_agent(&self) -> Option<&AgentView> {
|
||||
match self.active_view {
|
||||
ActiveView::Agent(id) => self.agents.get(&id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Session ID of the active agent, if one exists and has an established session.
|
||||
pub fn active_session_id(&self) -> Option<&str> {
|
||||
match self.active_view {
|
||||
|
|
@ -3741,6 +3760,7 @@ impl AppView {
|
|||
}
|
||||
}
|
||||
self.maybe_trigger_small_screen_tip();
|
||||
self.maybe_trigger_ssh_wrap_tip();
|
||||
let compact = self.appearance.prompt.compact;
|
||||
let (header_pad_left, header_pad_right, header_pad_top) = {
|
||||
let layout_cfg = &self.appearance.scrollback.layout;
|
||||
|
|
@ -4076,6 +4096,11 @@ impl AppView {
|
|||
d.overlay_prev_hit.set(header.and_then(|c| c.prev_rect));
|
||||
d.overlay_next_hit.set(header.and_then(|c| c.next_rect));
|
||||
}
|
||||
if let Some(d) = self.dashboard.as_mut()
|
||||
&& d.peek_viewport.is_some()
|
||||
{
|
||||
d.restore_peek_viewport(agents);
|
||||
}
|
||||
if let Some(agent) = agents.get_mut(&id) {
|
||||
let announcement_banner_h =
|
||||
crate::views::announcements::session_banner_height(
|
||||
|
|
@ -4425,6 +4450,59 @@ impl AppView {
|
|||
self.small_screen_tip_evaluated = true;
|
||||
super::dispatch::show_small_screen_tip(self);
|
||||
}
|
||||
/// One-shot SSH `grok wrap` tip trigger, run at the top of every `draw`
|
||||
/// right after [`Self::maybe_trigger_small_screen_tip`]. The welcome
|
||||
/// screen has no ephemeral-tip row, so the first stable agent-view draw
|
||||
/// is the earliest surface that can paint a session-load tip. Reads the
|
||||
/// live environment (cached statics) and delegates to the injectable
|
||||
/// inner so tests never depend on the host's SSH shape.
|
||||
pub(crate) fn maybe_trigger_ssh_wrap_tip(&mut self) {
|
||||
if self.ssh_wrap_tip_evaluated {
|
||||
return;
|
||||
}
|
||||
static ENV_RECOMMENDS_WRAP: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
|
||||
let env_recommends_wrap = *ENV_RECOMMENDS_WRAP.get_or_init(|| {
|
||||
let ctx = crate::terminal::terminal_context();
|
||||
crate::diagnostics::ssh_wrap_hint(
|
||||
ctx.is_ssh,
|
||||
crate::clipboard::osc52_sink_active(),
|
||||
ctx.is_official_vscode_remote,
|
||||
)
|
||||
.is_some()
|
||||
});
|
||||
self.maybe_trigger_ssh_wrap_tip_inner(env_recommends_wrap);
|
||||
}
|
||||
/// Inner trigger with the environment verdict injected
|
||||
/// (`diagnostics::ssh_wrap_hint` on the live path). Same
|
||||
/// defer-vs-consume rules as the small-screen trigger above, with two
|
||||
/// deltas: the environment gates are process-constant, so a failing
|
||||
/// verdict consumes the one-shot; and a busy tip slot defers instead of
|
||||
/// replacing — both session-load tips can qualify on the same first
|
||||
/// draw, and replacing would burn the other tip's once-per-session show,
|
||||
/// while this one loses nothing by waiting for a later draw.
|
||||
pub(crate) fn maybe_trigger_ssh_wrap_tip_inner(&mut self, env_recommends_wrap: bool) {
|
||||
if self.ssh_wrap_tip_evaluated {
|
||||
return;
|
||||
}
|
||||
let ActiveView::Agent(id) = self.active_view else {
|
||||
return;
|
||||
};
|
||||
let Some(agent) = self.agents.get(&id) else {
|
||||
return;
|
||||
};
|
||||
if agent.terminal_size_stale || agent.last_terminal_size == (0, 0) {
|
||||
return;
|
||||
}
|
||||
if !env_recommends_wrap {
|
||||
self.ssh_wrap_tip_evaluated = true;
|
||||
return;
|
||||
}
|
||||
if !agent.ephemeral_tip_can_render() || agent.ephemeral_tip.is_active() {
|
||||
return;
|
||||
}
|
||||
self.ssh_wrap_tip_evaluated = true;
|
||||
super::dispatch::show_ssh_wrap_tip(self);
|
||||
}
|
||||
/// Whether the clipboard-image tip may poll right now — the single in-window
|
||||
/// gate. Outside it the poll touches the pasteboard ZERO times: contextual
|
||||
/// hints on, the probe supported (macOS), past the fire cooldown, the
|
||||
|
|
@ -5110,6 +5188,7 @@ pub(crate) mod tests {
|
|||
tip_seen_counts: Default::default(),
|
||||
last_known_terminal_rows: 0,
|
||||
small_screen_tip_evaluated: false,
|
||||
ssh_wrap_tip_evaluated: false,
|
||||
clipboard_focus_tip: Default::default(),
|
||||
new_session_worktree_mode: WorktreeMode::Never,
|
||||
fork_worktree_mode: WorktreeMode::Ask,
|
||||
|
|
@ -5124,6 +5203,7 @@ pub(crate) mod tests {
|
|||
auth_start_mode: AuthMode::Pending,
|
||||
auth_code_input: String::new(),
|
||||
next_auth_request_seq: 1,
|
||||
auth_url_poll_handle: None,
|
||||
deferred_startup: Default::default(),
|
||||
auth_use_oauth: false,
|
||||
auth_clipboard_copied: false,
|
||||
|
|
@ -5240,7 +5320,7 @@ pub(crate) mod tests {
|
|||
voice_state: VoiceState::Idle,
|
||||
}
|
||||
}
|
||||
fn test_app_with_agent() -> AppView {
|
||||
pub(crate) fn test_app_with_agent() -> AppView {
|
||||
let mut app = test_app();
|
||||
let id = super::super::agent::AgentId(0);
|
||||
let mut agent = AgentView::new(
|
||||
|
|
|
|||
Loading…
Reference in a new issue