mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-09-01 00:59:33 +00:00
Merge pull request #1196 from nearai/staging-promote/e74214dc-23104855330
chore: promote staging to staging-promote/97b11ffd-23104193988 (2026-03-15 06:18 UTC)
This commit is contained in:
+32
-9
@@ -855,19 +855,42 @@ impl Agent {
|
||||
};
|
||||
|
||||
if let Some(pending) = pending_auth {
|
||||
match &submission {
|
||||
Submission::UserInput { content } => {
|
||||
return self
|
||||
.process_auth_token(message, &pending, content, session, thread_id)
|
||||
.await;
|
||||
}
|
||||
_ => {
|
||||
// Any control submission (interrupt, undo, etc.) cancels auth mode
|
||||
if pending.is_expired() {
|
||||
// TTL exceeded — clear stale auth mode
|
||||
tracing::warn!(
|
||||
extension = %pending.extension_name,
|
||||
"Auth mode expired after TTL, clearing"
|
||||
);
|
||||
{
|
||||
let mut sess = session.lock().await;
|
||||
if let Some(thread) = sess.threads.get_mut(&thread_id) {
|
||||
thread.pending_auth = None;
|
||||
}
|
||||
// Fall through to normal handling
|
||||
}
|
||||
// If this was a user message (possibly a pasted token), return an
|
||||
// explicit error instead of forwarding it to the LLM/history.
|
||||
if matches!(submission, Submission::UserInput { .. }) {
|
||||
return Ok(Some(format!(
|
||||
"Authentication for **{}** expired. Please try again.",
|
||||
pending.extension_name
|
||||
)));
|
||||
}
|
||||
// Control submissions (interrupt, undo, etc.) fall through to normal handling
|
||||
} else {
|
||||
match &submission {
|
||||
Submission::UserInput { content } => {
|
||||
return self
|
||||
.process_auth_token(message, &pending, content, session, thread_id)
|
||||
.await;
|
||||
}
|
||||
_ => {
|
||||
// Any control submission (interrupt, undo, etc.) cancels auth mode
|
||||
let mut sess = session.lock().await;
|
||||
if let Some(thread) = sess.threads.get_mut(&thread_id) {
|
||||
thread.pending_auth = None;
|
||||
}
|
||||
// Fall through to normal handling
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
-9
@@ -12,7 +12,7 @@
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -135,6 +135,12 @@ pub enum ThreadState {
|
||||
|
||||
/// Pending auth token request.
|
||||
///
|
||||
/// Auth mode TTL — must stay in sync with
|
||||
/// `crate::cli::oauth_defaults::OAUTH_FLOW_EXPIRY` (5 minutes / 300 s).
|
||||
/// Defined separately to avoid a session→cli module dependency.
|
||||
const AUTH_MODE_TTL_SECS: i64 = 300;
|
||||
const AUTH_MODE_TTL: TimeDelta = TimeDelta::seconds(AUTH_MODE_TTL_SECS);
|
||||
|
||||
/// When `tool_auth` returns `awaiting_token`, the thread enters auth mode.
|
||||
/// The next user message is intercepted before entering the normal pipeline
|
||||
/// (no logging, no turn creation, no history) and routed directly to the
|
||||
@@ -143,6 +149,16 @@ pub enum ThreadState {
|
||||
pub struct PendingAuth {
|
||||
/// Extension name to authenticate.
|
||||
pub extension_name: String,
|
||||
/// When this auth mode was entered. Used for TTL expiry.
|
||||
#[serde(default = "Utc::now")]
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl PendingAuth {
|
||||
/// Returns `true` if this auth mode has exceeded the TTL.
|
||||
pub fn is_expired(&self) -> bool {
|
||||
Utc::now() - self.created_at > AUTH_MODE_TTL
|
||||
}
|
||||
}
|
||||
|
||||
/// Pending tool approval request stored on a thread.
|
||||
@@ -298,7 +314,10 @@ impl Thread {
|
||||
/// Enter auth mode: next user message will be routed directly to
|
||||
/// the credential store, bypassing the normal pipeline entirely.
|
||||
pub fn enter_auth_mode(&mut self, extension_name: String) {
|
||||
self.pending_auth = Some(PendingAuth { extension_name });
|
||||
self.pending_auth = Some(PendingAuth {
|
||||
extension_name,
|
||||
created_at: Utc::now(),
|
||||
});
|
||||
self.updated_at = Utc::now();
|
||||
}
|
||||
|
||||
@@ -687,15 +706,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_enter_auth_mode() {
|
||||
let before = Utc::now();
|
||||
let mut thread = Thread::new(Uuid::new_v4());
|
||||
assert!(thread.pending_auth.is_none());
|
||||
|
||||
thread.enter_auth_mode("telegram".to_string());
|
||||
assert!(thread.pending_auth.is_some());
|
||||
assert_eq!(
|
||||
thread.pending_auth.as_ref().unwrap().extension_name,
|
||||
"telegram"
|
||||
);
|
||||
let pending = thread.pending_auth.as_ref().unwrap();
|
||||
assert_eq!(pending.extension_name, "telegram");
|
||||
assert!(pending.created_at >= before);
|
||||
assert!(!pending.is_expired());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -705,8 +725,9 @@ mod tests {
|
||||
|
||||
let pending = thread.take_pending_auth();
|
||||
assert!(pending.is_some());
|
||||
assert_eq!(pending.unwrap().extension_name, "notion");
|
||||
|
||||
let pending = pending.unwrap();
|
||||
assert_eq!(pending.extension_name, "notion");
|
||||
assert!(!pending.is_expired());
|
||||
// Should be cleared after take
|
||||
assert!(thread.pending_auth.is_none());
|
||||
assert!(thread.take_pending_auth().is_none());
|
||||
@@ -720,10 +741,25 @@ mod tests {
|
||||
let json = serde_json::to_string(&thread).expect("should serialize");
|
||||
assert!(json.contains("pending_auth"));
|
||||
assert!(json.contains("openai"));
|
||||
assert!(json.contains("created_at"));
|
||||
|
||||
let restored: Thread = serde_json::from_str(&json).expect("should deserialize");
|
||||
assert!(restored.pending_auth.is_some());
|
||||
assert_eq!(restored.pending_auth.unwrap().extension_name, "openai");
|
||||
let pending = restored.pending_auth.unwrap();
|
||||
assert_eq!(pending.extension_name, "openai");
|
||||
assert!(!pending.is_expired());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pending_auth_expiry() {
|
||||
let mut pending = PendingAuth {
|
||||
extension_name: "test".to_string(),
|
||||
created_at: Utc::now(),
|
||||
};
|
||||
assert!(!pending.is_expired());
|
||||
// Backdate beyond the TTL
|
||||
pending.created_at = Utc::now() - AUTH_MODE_TTL - TimeDelta::seconds(1);
|
||||
assert!(pending.is_expired());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user