Synced from monorepo

Synced from monorepo

Changes:
- Report invalid MCP server config instead of failing startup
- Keep completed terminal output when the gateway connection is lost
- Show a duration-only detail view for single-task task output
- Don't let a stale registry turn counter hide local sessions
- Raise the file-descriptor soft limit on Linux and log effective limits at startup
- Stop aborting when HTTP client construction fails
- Make session thread and runtime spawn failures recoverable
- Fix main-prompt paste parity in the question freeform input
- Fire SessionEnd hooks on /exit and headless quit
- Embed the deployment-config signing public key
- Repaint paste-chip background on inline panel inputs
- Security: prevent acceptEdits from auto-approving agent writes into the always-trusted global hook root
- Fix stacked "Worked for" markers so parks render as status and turns close with exactly one marker
- Parse hooks from config files
- Add a remote kill-switch for managed-config signature verification
- Security: fix workspace file-reference resolution bypassing workspace filesystem confinement

Source-Revision: d02693a856a54f1030695b36b91d276e96b30b23
This commit is contained in:
grokkybara[bot] 2026-07-25 18:44:42 +00:00
commit 47348d13ec
138 changed files with 7283 additions and 5796 deletions

View file

@ -35,6 +35,17 @@ use crate::session::{SessionCommand, SessionHandle};
/// not yet exited.
const FLUSH_POLL: Duration = Duration::from_millis(50);
/// Default bound on a process-exit session flush ([`AgentActivity::flush_all_sessions`]):
/// leader auto-update shutdown and the in-process agent's `/exit` / headless-quit
/// path both use it, so one wedged actor delays exit by the same amount everywhere.
/// Sessions are normally idle by then and the flush completes in milliseconds.
///
/// Known gap: a `SessionEnd` hook configured with a longer `timeout` than this
/// is still cut off at the grace. Aligning the two needs the hook registry's
/// configured timeouts at flush time, which this layer does not see — tracked as
/// a follow-up rather than hardcoding a larger bound for every exit.
pub const SESSION_FLUSH_GRACE: Duration = Duration::from_secs(10);
/// Per-session slice of state shared with the session actor (the same `Arc`s
/// the actor mutates — see the matching `SessionHandle` fields).
struct SessionActivityEntry {
@ -131,9 +142,13 @@ impl AgentActivity {
/// with a fresh actor gets its own signal), all against one deadline —
/// `grace` bounds the **total** shutdown delay.
///
/// Call **before** cancelling the leader's root token so session state
/// is durable before the `LocalSet` drop aborts remaining tasks. Actors
/// that miss the grace are logged and abandoned.
/// Callers: the leader's auto-update / `RelaunchForUpdate` shutdown, and
/// the in-process agent worker on `/exit` / headless quit. In the leader
/// case, call **before** cancelling the root token; in the in-process case,
/// **after** the cancel that ends the worker's run loop but before its
/// `LocalSet` drops — either way, session state must be durable before the
/// drop aborts remaining tasks. Actors that miss the grace are logged and
/// abandoned.
pub async fn flush_all_sessions(&self, grace: Duration) {
let deadline = tokio::time::Instant::now() + grace;
// Every distinct channel signaled so far (id kept for logging).
@ -148,7 +163,7 @@ impl AgentActivity {
.collect();
for (id, tx) in snapshot {
if !signaled.iter().any(|(_, s)| s.same_channel(&tx)) {
tracing::info!(session_id = %id, "leader shutdown: flushing session");
tracing::info!(session_id = %id, "shutdown: flushing session");
let _ = tx.send(SessionCommand::Shutdown);
signaled.push((id, tx));
}
@ -162,7 +177,7 @@ impl AgentActivity {
if !tx.is_closed() {
tracing::warn!(
session_id = %id,
"leader shutdown: session actor did not exit within grace; proceeding"
"shutdown: session actor did not exit within grace; proceeding"
);
}
}