Skip to content

Commit f144124

Browse files
committed
More tests and cleanup
1 parent ae5dace commit f144124

File tree

20 files changed

+464
-128
lines changed

20 files changed

+464
-128
lines changed

crates/agent/src/agent/agent_config/definitions.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use serde::{
99
Serialize,
1010
};
1111

12-
use crate::agent::consts::BUILTIN_VIBER_AGENT_NAME;
12+
use super::types::ResourcePath;
13+
use crate::agent::consts::DEFAULT_AGENT_NAME;
1314
use crate::agent::tools::BuiltInToolName;
1415

1516
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
@@ -69,9 +70,10 @@ impl Config {
6970
}
7071
}
7172

72-
pub fn resources(&self) -> &Vec<String> {
73+
// pub fn resources(&self) -> &[impl AsRef<str>] {
74+
pub fn resources(&self) -> &[impl AsRef<str>] {
7375
match self {
74-
Config::V2025_08_22(a) => &a.resources,
76+
Config::V2025_08_22(a) => a.resources.as_slice(),
7577
}
7678
}
7779

@@ -150,7 +152,7 @@ pub struct AgentConfigV2025_08_22 {
150152
// context files
151153
/// Files to include in the agent's context
152154
#[serde(default)]
153-
pub resources: Vec<String>,
155+
pub resources: Vec<ResourcePath>,
154156

155157
// permissioning stuff
156158
/// List of tools the agent is explicitly allowed to use
@@ -162,9 +164,9 @@ impl Default for AgentConfigV2025_08_22 {
162164
fn default() -> Self {
163165
Self {
164166
schema: default_schema(),
165-
name: BUILTIN_VIBER_AGENT_NAME.to_string(),
167+
name: DEFAULT_AGENT_NAME.to_string(),
166168
description: Some("The default agent for Q CLI".to_string()),
167-
system_prompt: Some("You are Q, an expert programmer dedicated to becoming the greatest vibe-coding assistant in the world.".to_string()),
169+
system_prompt: None,
168170
tools: vec!["@builtin".to_string()],
169171
tool_settings: Default::default(),
170172
tool_aliases: Default::default(),
@@ -174,7 +176,16 @@ impl Default for AgentConfigV2025_08_22 {
174176
mcp_servers: Default::default(),
175177
use_legacy_mcp_json: false,
176178

177-
resources: Default::default(),
179+
resources: vec![
180+
"file://AmazonQ.md",
181+
"file://AGENTS.md",
182+
"file://README.md",
183+
"file://.amazonq/rules/**/*.md",
184+
]
185+
.into_iter()
186+
.map(Into::into)
187+
.collect::<Vec<_>>(),
188+
178189
allowed_tools: HashSet::from([BuiltInToolName::FsRead.to_string()]),
179190
}
180191
}

crates/agent/src/agent/agent_config/mod.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod definitions;
22
pub mod parse;
3+
pub mod types;
34

45
use std::collections::{
56
HashMap,
@@ -43,19 +44,19 @@ use crate::agent::util::error::{
4344
UtilError,
4445
};
4546

46-
/// Represents an agent config
47+
/// Represents an agent config.
4748
///
48-
/// Wraps [Config] along with some metadata
49+
/// Basically just wraps [Config] along with some metadata.
4950
#[derive(Debug, Clone)]
50-
pub struct AgentConfig {
51+
pub struct LoadedAgentConfig {
5152
/// Where the config was sourced from
5253
#[allow(dead_code)]
5354
source: ConfigSource,
5455
/// The actual config content
5556
config: Config,
5657
}
5758

58-
impl AgentConfig {
59+
impl LoadedAgentConfig {
5960
pub fn config(&self) -> &Config {
6061
&self.config
6162
}
@@ -84,7 +85,7 @@ impl AgentConfig {
8485
self.config.hooks()
8586
}
8687

87-
pub fn resources(&self) -> &Vec<String> {
88+
pub fn resources(&self) -> &[impl AsRef<str>] {
8889
self.config.resources()
8990
}
9091
}
@@ -103,7 +104,7 @@ pub enum ConfigSource {
103104
BuiltIn,
104105
}
105106

106-
impl Default for AgentConfig {
107+
impl Default for LoadedAgentConfig {
107108
fn default() -> Self {
108109
Self {
109110
source: ConfigSource::BuiltIn,
@@ -112,7 +113,7 @@ impl Default for AgentConfig {
112113
}
113114
}
114115

115-
impl AgentConfig {
116+
impl LoadedAgentConfig {
116117
pub fn system_prompt(&self) -> Option<&str> {
117118
self.config.system_prompt()
118119
}
@@ -136,7 +137,7 @@ impl From<UtilError> for AgentConfigError {
136137
}
137138
}
138139

139-
pub async fn load_agents() -> Result<(Vec<AgentConfig>, Vec<AgentConfigError>)> {
140+
pub async fn load_agents() -> Result<(Vec<LoadedAgentConfig>, Vec<AgentConfigError>)> {
140141
let mut agent_configs = Vec::new();
141142
let mut invalid_agents = Vec::new();
142143
match load_workspace_agents().await {
@@ -148,7 +149,7 @@ pub async fn load_agents() -> Result<(Vec<AgentConfig>, Vec<AgentConfigError>)>
148149
agent_configs.append(
149150
&mut valid
150151
.into_iter()
151-
.map(|(path, config)| AgentConfig {
152+
.map(|(path, config)| LoadedAgentConfig {
152153
source: ConfigSource::Workspace { path },
153154
config,
154155
})
@@ -169,7 +170,7 @@ pub async fn load_agents() -> Result<(Vec<AgentConfig>, Vec<AgentConfigError>)>
169170
agent_configs.append(
170171
&mut valid
171172
.into_iter()
172-
.map(|(path, config)| AgentConfig {
173+
.map(|(path, config)| LoadedAgentConfig {
173174
source: ConfigSource::Global { path },
174175
config,
175176
})
@@ -182,7 +183,7 @@ pub async fn load_agents() -> Result<(Vec<AgentConfig>, Vec<AgentConfigError>)>
182183
};
183184

184185
// Always include the default agent as a fallback.
185-
agent_configs.push(AgentConfig::default());
186+
agent_configs.push(LoadedAgentConfig::default());
186187

187188
info!(?agent_configs, "loaded agent config");
188189

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use std::borrow::Borrow;
2+
use std::ops::Deref;
3+
4+
use schemars::JsonSchema;
5+
use serde::{
6+
Deserialize,
7+
Serialize,
8+
};
9+
10+
#[derive(Debug, Clone, Serialize, Deserialize, Eq, Hash, PartialEq, JsonSchema)]
11+
pub struct ResourcePath(
12+
// You can extend this list via "|". e.g. r"^(file://|database://)"
13+
#[schemars(regex(pattern = r"^(file://)"))]
14+
String,
15+
);
16+
17+
impl Deref for ResourcePath {
18+
type Target = String;
19+
20+
fn deref(&self) -> &Self::Target {
21+
&self.0
22+
}
23+
}
24+
25+
impl AsRef<str> for ResourcePath {
26+
fn as_ref(&self) -> &str {
27+
self.0.as_str()
28+
}
29+
}
30+
31+
impl Borrow<str> for ResourcePath {
32+
fn borrow(&self) -> &str {
33+
self.0.as_str()
34+
}
35+
}
36+
37+
impl From<&str> for ResourcePath {
38+
fn from(value: &str) -> Self {
39+
Self(value.to_string())
40+
}
41+
}
42+
43+
impl From<String> for ResourcePath {
44+
fn from(value: String) -> Self {
45+
Self(value)
46+
}
47+
}

crates/agent/src/agent/agent_loop/mod.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod protocol;
33
pub mod types;
44

55
use std::pin::Pin;
6+
use std::sync::Arc;
67
use std::time::Instant;
78

89
use chrono::Utc;
@@ -236,6 +237,7 @@ impl AgentLoop {
236237
} else {
237238
// For successful streams with no tool uses, this always ends a user turn.
238239
loop_events.push(self.set_execution_state(LoopState::UserTurnEnded));
240+
self.loop_end_time = Some(Instant::now());
239241
loop_events.push(AgentLoopEventKind::UserTurnEnd(self.make_user_turn_metadata()));
240242
}
241243
} else {
@@ -268,9 +270,6 @@ impl AgentLoop {
268270
match self.execution_state {
269271
LoopState::Idle | LoopState::Errored | LoopState::PendingToolUseResults => {},
270272
LoopState::UserTurnEnded => {},
271-
// LoopState::UserTurnEnded => {
272-
// return Err(AgentLoopResponseError::AgentLoopExited);
273-
// },
274273
other => {
275274
error!(
276275
?other,
@@ -314,6 +313,7 @@ impl AgentLoop {
314313
self.stream_states.push(parse_state);
315314
}
316315

316+
self.loop_end_time = Some(Instant::now());
317317
let metadata = self.make_user_turn_metadata();
318318
buf.push(self.set_execution_state(LoopState::UserTurnEnded));
319319
buf.push(AgentLoopEventKind::UserTurnEnd(metadata.clone()));
@@ -642,16 +642,13 @@ impl AgentLoopHandle {
642642
self.loop_event_rx.recv().await
643643
}
644644

645-
pub async fn send_request<M: Model>(
645+
pub async fn send_request(
646646
&mut self,
647-
model: M,
647+
model: Arc<dyn Model>,
648648
args: SendRequestArgs,
649649
) -> Result<AgentLoopResponse, AgentLoopResponseError> {
650650
self.sender
651-
.send_recv(AgentLoopRequest::SendRequest {
652-
model: Box::new(model),
653-
args,
654-
})
651+
.send_recv(AgentLoopRequest::SendRequest { model, args })
655652
.await
656653
.unwrap_or(Err(AgentLoopResponseError::AgentLoopExited))
657654
}

crates/agent/src/agent/agent_loop/model.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ pub trait Model: std::fmt::Debug + Send + Sync + 'static {
2727
system_prompt: Option<String>,
2828
cancel_token: CancellationToken,
2929
) -> Pin<Box<dyn Stream<Item = Result<StreamEvent, StreamError>> + Send + 'static>>;
30+
31+
/// Dump serializable state required by the model implementation.
32+
///
33+
/// This is intended to provide the ability to save and restore state
34+
/// associated with an implementation, useful for restoring a previous conversation.
35+
fn state(&self) -> Option<serde_json::Value> {
36+
None
37+
}
3038
}
3139

3240
/// The supported backends

crates/agent/src/agent/agent_loop/protocol.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::sync::Arc;
12
use std::time::Duration;
23

34
use chrono::{
@@ -29,7 +30,7 @@ use super::{
2930
pub enum AgentLoopRequest {
3031
GetExecutionState,
3132
SendRequest {
32-
model: Box<dyn Model>,
33+
model: Arc<dyn Model>,
3334
args: SendRequestArgs,
3435
},
3536
/// Ends the agent loop
@@ -212,7 +213,7 @@ pub struct UserTurnMetadata {
212213
}
213214

214215
/// The reason why a user turn ended
215-
#[derive(Debug, Clone, Serialize, Deserialize)]
216+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
216217
pub enum EndReason {
217218
/// Loop ended before handling any requests
218219
DidNotRun,

crates/agent/src/agent/consts.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/// Name of the default agent.
2-
pub const BUILTIN_VIBER_AGENT_NAME: &str = "cli_default";
3-
pub const BUILTIN_PLANNER_AGENT_NAME: &str = "cli_planner";
2+
pub const DEFAULT_AGENT_NAME: &str = "q_cli_default";
43

54
pub const MAX_CONVERSATION_STATE_HISTORY_LEN: usize = 500;
65

0 commit comments

Comments
 (0)