Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa846000e7 |
@@ -252,6 +252,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -307,7 +307,11 @@ async fn report_activity(
|
|||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
Json(body): Json<ReportActivityRequest>,
|
Json(body): Json<ReportActivityRequest>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
match s.manager.report_activity(id, body.state, body.detail).await {
|
match s
|
||||||
|
.manager
|
||||||
|
.report_activity(id, body.state, body.detail, body.session_id)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(()) => StatusCode::NO_CONTENT.into_response(),
|
Ok(()) => StatusCode::NO_CONTENT.into_response(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let info = e.to_error_info();
|
let info = e.to_error_info();
|
||||||
|
|||||||
@@ -478,6 +478,7 @@ impl SessionManager {
|
|||||||
// fresh tree has nothing in the way anyway.
|
// fresh tree has nothing in the way anyway.
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = self.inner.sidecar.write(&session) {
|
if let Err(e) = self.inner.sidecar.write(&session) {
|
||||||
@@ -683,6 +684,7 @@ impl SessionManager {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let fifo_path = self.inner.fifo_dir.join(format!("{id}.fifo"));
|
let fifo_path = self.inner.fifo_dir.join(format!("{id}.fifo"));
|
||||||
@@ -881,11 +883,18 @@ impl SessionManager {
|
|||||||
/// A report that changes the effective state restarts `activity_since`
|
/// A report that changes the effective state restarts `activity_since`
|
||||||
/// (#104's clock); re-reporting an unchanged state only extends the TTL
|
/// (#104's clock); re-reporting an unchanged state only extends the TTL
|
||||||
/// and stays silent on the event stream at steady state.
|
/// and stays silent on the event stream at steady state.
|
||||||
|
///
|
||||||
|
/// `agent_session_id` (#161) is the agent's own conversation id, parsed
|
||||||
|
/// by the caller from the hook payload's `session_id` field. It is
|
||||||
|
/// written once — the first report that carries one wins — and the
|
||||||
|
/// sidecar is only rewritten on that transition, never on every report,
|
||||||
|
/// since an agent's session id never changes mid-session.
|
||||||
pub async fn report_activity(
|
pub async fn report_activity(
|
||||||
&self,
|
&self,
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
state: ActivityState,
|
state: ActivityState,
|
||||||
detail: Option<String>,
|
detail: Option<String>,
|
||||||
|
agent_session_id: Option<String>,
|
||||||
) -> Result<(), SessionError> {
|
) -> Result<(), SessionError> {
|
||||||
let updated = {
|
let updated = {
|
||||||
let mut sessions = self.inner.sessions.lock().await;
|
let mut sessions = self.inner.sessions.lock().await;
|
||||||
@@ -901,7 +910,16 @@ impl SessionManager {
|
|||||||
state,
|
state,
|
||||||
self.inner.report_ttl,
|
self.inner.report_ttl,
|
||||||
));
|
));
|
||||||
changed.then(|| rec.session.clone())
|
let newly_identified =
|
||||||
|
rec.session.agent_session_id.is_none() && agent_session_id.is_some();
|
||||||
|
if newly_identified {
|
||||||
|
rec.session.agent_session_id = agent_session_id;
|
||||||
|
self.inner
|
||||||
|
.sidecar
|
||||||
|
.write(&rec.session)
|
||||||
|
.map_err(SessionError::Sidecar)?;
|
||||||
|
}
|
||||||
|
(changed || newly_identified).then(|| rec.session.clone())
|
||||||
};
|
};
|
||||||
if let Some(session) = updated {
|
if let Some(session) = updated {
|
||||||
let _ = self
|
let _ = self
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
struct Fixture {
|
struct Fixture {
|
||||||
manager: SessionManager,
|
manager: SessionManager,
|
||||||
|
sidecar: SidecarStore,
|
||||||
_tmux: TmuxFixture,
|
_tmux: TmuxFixture,
|
||||||
_state_dir: TempDir,
|
_state_dir: TempDir,
|
||||||
}
|
}
|
||||||
@@ -25,15 +26,17 @@ struct Fixture {
|
|||||||
async fn fixture(ttl: Duration) -> Fixture {
|
async fn fixture(ttl: Duration) -> Fixture {
|
||||||
let tmux = TmuxFixture::new();
|
let tmux = TmuxFixture::new();
|
||||||
let state_dir = TempDir::new().unwrap();
|
let state_dir = TempDir::new().unwrap();
|
||||||
|
let sidecar = SidecarStore::new(state_dir.path().join("sessions"));
|
||||||
let manager = SessionManager::new(
|
let manager = SessionManager::new(
|
||||||
Arc::new(tmux.tmux.clone()),
|
Arc::new(tmux.tmux.clone()),
|
||||||
SidecarStore::new(state_dir.path().join("sessions")),
|
sidecar.clone(),
|
||||||
WorktreeManager::new(None),
|
WorktreeManager::new(None),
|
||||||
state_dir.path().join("fifos"),
|
state_dir.path().join("fifos"),
|
||||||
)
|
)
|
||||||
.with_activity_report_ttl(ttl);
|
.with_activity_report_ttl(ttl);
|
||||||
Fixture {
|
Fixture {
|
||||||
manager,
|
manager,
|
||||||
|
sidecar,
|
||||||
_tmux: tmux,
|
_tmux: tmux,
|
||||||
_state_dir: state_dir,
|
_state_dir: state_dir,
|
||||||
}
|
}
|
||||||
@@ -67,7 +70,7 @@ async fn report_on_unknown_session_is_not_found() {
|
|||||||
let fx = fixture(Duration::from_secs(300)).await;
|
let fx = fixture(Duration::from_secs(300)).await;
|
||||||
let err = fx
|
let err = fx
|
||||||
.manager
|
.manager
|
||||||
.report_activity(Uuid::new_v4(), ActivityState::Idle, None)
|
.report_activity(Uuid::new_v4(), ActivityState::Idle, None, None)
|
||||||
.await
|
.await
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
assert!(matches!(err, maestro_daemon::SessionError::NotFound(_)));
|
assert!(matches!(err, maestro_daemon::SessionError::NotFound(_)));
|
||||||
@@ -83,6 +86,7 @@ async fn report_sets_error_state_and_detail() {
|
|||||||
id,
|
id,
|
||||||
ActivityState::Error,
|
ActivityState::Error,
|
||||||
Some("cargo build failed: link error".into()),
|
Some("cargo build failed: link error".into()),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -101,7 +105,7 @@ async fn replacing_a_report_replaces_both_state_and_detail() {
|
|||||||
let id = spawn_sleeping(&fx.manager).await;
|
let id = spawn_sleeping(&fx.manager).await;
|
||||||
|
|
||||||
fx.manager
|
fx.manager
|
||||||
.report_activity(id, ActivityState::Error, Some("first failure".into()))
|
.report_activity(id, ActivityState::Error, Some("first failure".into()), None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
fx.manager
|
fx.manager
|
||||||
@@ -109,6 +113,7 @@ async fn replacing_a_report_replaces_both_state_and_detail() {
|
|||||||
id,
|
id,
|
||||||
ActivityState::AwaitingInput,
|
ActivityState::AwaitingInput,
|
||||||
Some("needs approval".into()),
|
Some("needs approval".into()),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -125,7 +130,7 @@ async fn detail_is_capped_server_side() {
|
|||||||
|
|
||||||
let long = "x".repeat(10_000);
|
let long = "x".repeat(10_000);
|
||||||
fx.manager
|
fx.manager
|
||||||
.report_activity(id, ActivityState::Error, Some(long))
|
.report_activity(id, ActivityState::Error, Some(long), None)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -144,7 +149,12 @@ async fn reported_state_survives_watcher_ticks_until_the_ttl_then_inference_resu
|
|||||||
fx.manager.start_liveness_watcher(Duration::from_millis(50));
|
fx.manager.start_liveness_watcher(Duration::from_millis(50));
|
||||||
|
|
||||||
fx.manager
|
fx.manager
|
||||||
.report_activity(id, ActivityState::Error, Some("agent died mid-run".into()))
|
.report_activity(
|
||||||
|
id,
|
||||||
|
ActivityState::Error,
|
||||||
|
Some("agent died mid-run".into()),
|
||||||
|
None,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -162,3 +172,55 @@ async fn reported_state_survives_watcher_ticks_until_the_ttl_then_inference_resu
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn a_reported_session_id_is_recorded_on_the_session() {
|
||||||
|
let fx = fixture(Duration::from_secs(300)).await;
|
||||||
|
let id = spawn_sleeping(&fx.manager).await;
|
||||||
|
|
||||||
|
fx.manager
|
||||||
|
.report_activity(id, ActivityState::Active, None, Some("conv-abc123".into()))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let s = observed(&fx.manager, id).await;
|
||||||
|
assert_eq!(s.agent_session_id.as_deref(), Some("conv-abc123"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// #161: a session id does not change mid-session, so once set it is never
|
||||||
|
/// overwritten by a later report — even one that names a different id.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn a_reported_session_id_is_written_once_and_never_replaced() {
|
||||||
|
let fx = fixture(Duration::from_secs(300)).await;
|
||||||
|
let id = spawn_sleeping(&fx.manager).await;
|
||||||
|
|
||||||
|
fx.manager
|
||||||
|
.report_activity(id, ActivityState::Active, None, Some("conv-first".into()))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
fx.manager
|
||||||
|
.report_activity(id, ActivityState::Active, None, Some("conv-second".into()))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let s = observed(&fx.manager, id).await;
|
||||||
|
assert_eq!(s.agent_session_id.as_deref(), Some("conv-first"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn a_reported_session_id_survives_recovery_from_the_sidecar() {
|
||||||
|
let fx = fixture(Duration::from_secs(300)).await;
|
||||||
|
let id = spawn_sleeping(&fx.manager).await;
|
||||||
|
|
||||||
|
fx.manager
|
||||||
|
.report_activity(id, ActivityState::Active, None, Some("conv-abc123".into()))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stored = fx
|
||||||
|
.sidecar
|
||||||
|
.read(id)
|
||||||
|
.expect("sidecar read")
|
||||||
|
.expect("sidecar present");
|
||||||
|
assert_eq!(stored.agent_session_id.as_deref(), Some("conv-abc123"));
|
||||||
|
}
|
||||||
|
|||||||
@@ -526,6 +526,33 @@ async fn post_activity_reports_error_with_detail() {
|
|||||||
assert_eq!(s.activity_detail.as_deref(), Some("cargo build failed"));
|
assert_eq!(s.activity_detail.as_deref(), Some("cargo build failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// #161: a reported `session_id` lands on the session as `agent_session_id`.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn post_activity_records_the_reported_session_id() {
|
||||||
|
let h = spawn("naps-laptop").await;
|
||||||
|
let id = spawn_via_manager(&h).await;
|
||||||
|
|
||||||
|
let resp = reqwest::Client::new()
|
||||||
|
.post(url(h.addr, &format!("/api/sessions/{id}/activity")))
|
||||||
|
.json(&serde_json::json!({
|
||||||
|
"state": "Active",
|
||||||
|
"session_id": "conv-abc123"
|
||||||
|
}))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(resp.status(), reqwest::StatusCode::NO_CONTENT);
|
||||||
|
|
||||||
|
let sessions: Vec<Session> = reqwest::get(url(h.addr, "/api/sessions"))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.json()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let s = sessions.iter().find(|s| s.id == id).unwrap();
|
||||||
|
assert_eq!(s.agent_session_id.as_deref(), Some("conv-abc123"));
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn post_activity_to_unknown_session_returns_404() {
|
async fn post_activity_to_unknown_session_returns_404() {
|
||||||
let h = spawn("naps-laptop").await;
|
let h = spawn("naps-laptop").await;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ fn sample(id: Uuid) -> Session {
|
|||||||
current_cwd: Some(PathBuf::from("/tmp/work/crates")),
|
current_cwd: Some(PathBuf::from("/tmp/work/crates")),
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: Some("conv-abc123".into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,15 +84,23 @@ if [ "$state" = "Prompt" ]; then
|
|||||||
>/dev/null 2>&1 || true
|
>/dev/null 2>&1 || true
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
body="{\"state\":\"$state\"}"
|
fields="\"state\":\"$state\""
|
||||||
# StopFailure payloads carry the outcome in "error". Only a plain enum
|
# StopFailure payloads carry the outcome in "error". Only a plain enum
|
||||||
# spelling is reported as the detail — anything else (quotes, backslashes,
|
# spelling is reported as the detail — anything else (quotes, backslashes,
|
||||||
# prose) would risk breaking the JSON body, and the spelling already names
|
# prose) would risk breaking the JSON body, and the spelling already names
|
||||||
# the failure.
|
# the failure.
|
||||||
detail=$(printf '%s' "$payload" | sed -n 's/.*"error"[[:space:]]*:[[:space:]]*"\([a-z_]*\)".*/\1/p')
|
detail=$(printf '%s' "$payload" | sed -n 's/.*"error"[[:space:]]*:[[:space:]]*"\([a-z_]*\)".*/\1/p')
|
||||||
if [ -n "$detail" ]; then
|
if [ -n "$detail" ]; then
|
||||||
body="{\"state\":\"$state\",\"detail\":\"$detail\"}"
|
fields="$fields,\"detail\":\"$detail\""
|
||||||
fi
|
fi
|
||||||
|
# Every Claude Code hook payload carries its own conversation id (#161).
|
||||||
|
# Constrained to a safe charset for the same reason as "detail" above: it is
|
||||||
|
# spliced into the JSON body unescaped.
|
||||||
|
session_id=$(printf '%s' "$payload" | sed -n 's/.*"session_id"[[:space:]]*:[[:space:]]*"\([A-Za-z0-9_-]*\)".*/\1/p')
|
||||||
|
if [ -n "$session_id" ]; then
|
||||||
|
fields="$fields,\"session_id\":\"$session_id\""
|
||||||
|
fi
|
||||||
|
body="{$fields}"
|
||||||
curl -fsS --connect-timeout 1 --max-time 2 \
|
curl -fsS --connect-timeout 1 --max-time 2 \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-d "$body" \
|
-d "$body" \
|
||||||
|
|||||||
@@ -128,6 +128,41 @@ fn script_reports_the_error_spelling_as_detail() {
|
|||||||
assert!(request.contains(r#""detail":"rate_limit""#), "{request}");
|
assert!(request.contains(r#""detail":"rate_limit""#), "{request}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// #161: the payload's own `session_id` — the agent's conversation id — is
|
||||||
|
/// parsed out and forwarded in the activity body, so the daemon can persist
|
||||||
|
/// it on the session.
|
||||||
|
#[test]
|
||||||
|
fn script_forwards_the_payloads_session_id_in_the_activity_body() {
|
||||||
|
let request = capture_request(|url| {
|
||||||
|
sh(
|
||||||
|
HOOK_SCRIPT,
|
||||||
|
&["Active"],
|
||||||
|
&owned_envs(&session_envs(&url)),
|
||||||
|
br#"{"session_id":"conv-abc123","hook_event_name":"PreToolUse"}"#,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
assert!(request.contains(r#""state":"Active""#), "{request}");
|
||||||
|
assert!(
|
||||||
|
request.contains(r#""session_id":"conv-abc123""#),
|
||||||
|
"{request}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A payload without a `session_id` (or one with an unexpected shape) must
|
||||||
|
/// never produce a broken body — no `session_id` field is posted.
|
||||||
|
#[test]
|
||||||
|
fn script_posts_no_session_id_when_the_payload_has_none() {
|
||||||
|
let request = capture_request(|url| {
|
||||||
|
sh(
|
||||||
|
HOOK_SCRIPT,
|
||||||
|
&["AwaitingInput"],
|
||||||
|
&owned_envs(&session_envs(&url)),
|
||||||
|
br#"{"hook_event_name":"Stop","stop_hook_active":false}"#,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
assert!(!request.contains("session_id"), "{request}");
|
||||||
|
}
|
||||||
|
|
||||||
/// An `error` value that is not a plain enum spelling (or a payload without
|
/// An `error` value that is not a plain enum spelling (or a payload without
|
||||||
/// one at all) must never produce a broken body — no detail is posted.
|
/// one at all) must never produce a broken body — no detail is posted.
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,6 +116,13 @@ pub struct Session {
|
|||||||
/// sorts a never-focused session after every session that has one.
|
/// sorts a never-focused session after every session that has one.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub last_focused_at: Option<DateTime<Utc>>,
|
pub last_focused_at: Option<DateTime<Utc>>,
|
||||||
|
/// The conversation id the agent running in this session reported for
|
||||||
|
/// itself (#161), parsed from a hook's `session_id` field. Written once,
|
||||||
|
/// from the first activity report that carries one, and left alone after
|
||||||
|
/// — an agent's own session id does not change mid-session. `None` for
|
||||||
|
/// sessions that never run an agent (plain shells) or predate the field.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub agent_session_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default for [`Session::last_activity_at`] when an older payload omits it.
|
/// Default for [`Session::last_activity_at`] when an older payload omits it.
|
||||||
@@ -184,6 +191,11 @@ pub struct ReportActivityRequest {
|
|||||||
/// The daemon caps its length server-side.
|
/// The daemon caps its length server-side.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub detail: Option<String>,
|
pub detail: Option<String>,
|
||||||
|
/// The agent's own conversation id (#161), forwarded from the hook
|
||||||
|
/// payload's `session_id` field. `None` for hook events that don't carry
|
||||||
|
/// one, or hooks predating this field.
|
||||||
|
#[serde(default)]
|
||||||
|
pub session_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reject an unknown [`ActivityState`] spelling instead of degrading it to
|
/// Reject an unknown [`ActivityState`] spelling instead of degrading it to
|
||||||
@@ -862,6 +874,7 @@ mod tests {
|
|||||||
current_cwd: Some(PathBuf::from("/home/naps62/projects/maestro/crates")),
|
current_cwd: Some(PathBuf::from("/home/naps62/projects/maestro/crates")),
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1031,6 +1044,7 @@ mod tests {
|
|||||||
let full = ReportActivityRequest {
|
let full = ReportActivityRequest {
|
||||||
state: ActivityState::AwaitingInput,
|
state: ActivityState::AwaitingInput,
|
||||||
detail: Some("waiting on permission".into()),
|
detail: Some("waiting on permission".into()),
|
||||||
|
session_id: Some("conv-abc123".into()),
|
||||||
};
|
};
|
||||||
assert_eq!(full, round_trip(&full));
|
assert_eq!(full, round_trip(&full));
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ pub fn session(id: Uuid, cwd: &str, command: &str) -> Session {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1898,6 +1898,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
});
|
});
|
||||||
@@ -2079,6 +2080,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
app.dispatch(Action::SidebarNext);
|
app.dispatch(Action::SidebarNext);
|
||||||
@@ -2106,6 +2108,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3214,6 +3217,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: focused,
|
last_focused_at: focused,
|
||||||
|
agent_session_id: None,
|
||||||
};
|
};
|
||||||
let older = Utc::now() - chrono::Duration::seconds(60);
|
let older = Utc::now() - chrono::Duration::seconds(60);
|
||||||
app.catalog.apply_event(CatalogEvent::Snapshot {
|
app.catalog.apply_event(CatalogEvent::Snapshot {
|
||||||
@@ -3492,6 +3496,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
app.dispatch(Action::KillHighlighted);
|
app.dispatch(Action::KillHighlighted);
|
||||||
@@ -3527,6 +3532,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -809,6 +809,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -500,6 +500,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1266,6 +1267,7 @@ mod tests {
|
|||||||
current_cwd: None,
|
current_cwd: None,
|
||||||
blocked_work: None,
|
blocked_work: None,
|
||||||
last_focused_at: None,
|
last_focused_at: None,
|
||||||
|
agent_session_id: None,
|
||||||
};
|
};
|
||||||
let sb = Sidebar {
|
let sb = Sidebar {
|
||||||
sessions: vec![s],
|
sessions: vec![s],
|
||||||
|
|||||||
Reference in New Issue
Block a user