mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-15 23:32:46 +00:00
Let pebble discover memory and skills, and name the reuse rules
Steps 5 and 8 of .ai/plans/pebble-absorbs-embedder-concerns.md, pinning pebble 49da137. Agent stages and `fabro exec` ask pebble for the profile's instruction files from the repository root down to the working directory (`MemoryDiscovery::from_git_root`), which fabro lacked: it read the working directory alone. Skill directories are pebble's to resolve too: the user's skills directory, then `.fabro/skills` and `skills` under the repository root. Prompt stages keep reading the working directory alone, through the same discovery and loader, so `agent_memory.rs` keeps only that call; the filename table is pebble's now. A retained thread's export comes from `export_for_reuse`, which closes the session and hands back an export whose cursor is already past the close, in place of export, shutdown, and a cursor advance by hand. Ask Fabro resumes a stored record with `resume_after`, the rule it applied under the older name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
64c578d044
commit
d4e483925c
7 changed files with 101 additions and 145 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
|
@ -2063,7 +2063,7 @@ dependencies = [
|
|||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2190,7 +2190,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4345,7 +4345,7 @@ dependencies = [
|
|||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.62.2",
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5291,7 +5291,7 @@ version = "0.50.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5856,7 +5856,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pebble-agent"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/lithoscomputer/pebble?rev=6cdb30a874010582106bbde80ac91ae000dc13d8#6cdb30a874010582106bbde80ac91ae000dc13d8"
|
||||
source = "git+https://github.com/lithoscomputer/pebble?rev=49da13798c753eb312b765596840bdc200b819d7#49da13798c753eb312b765596840bdc200b819d7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures-util",
|
||||
|
|
@ -5873,7 +5873,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pebble-coding-agent"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/lithoscomputer/pebble?rev=6cdb30a874010582106bbde80ac91ae000dc13d8#6cdb30a874010582106bbde80ac91ae000dc13d8"
|
||||
source = "git+https://github.com/lithoscomputer/pebble?rev=49da13798c753eb312b765596840bdc200b819d7#49da13798c753eb312b765596840bdc200b819d7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures-util",
|
||||
|
|
@ -6243,7 +6243,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6720,7 +6720,7 @@ dependencies = [
|
|||
"errno 0.3.14",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6779,7 +6779,7 @@ dependencies = [
|
|||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -7423,7 +7423,7 @@ version = "1.4.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||
dependencies = [
|
||||
"errno 0.3.14",
|
||||
"errno 0.2.8",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
|
@ -7988,7 +7988,7 @@ dependencies = [
|
|||
"getrandom 0.4.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -8023,7 +8023,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -9090,7 +9090,7 @@ version = "0.1.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ futures-util = "0.3"
|
|||
# the merge commit once it lands. Pebble pins the same lithos-llm rev as
|
||||
# fabro, and its lockfile policy is that every shared crate resolves to the
|
||||
# version lithos-llm locks.
|
||||
pebble-agent = { git = "https://github.com/lithoscomputer/pebble", rev = "6cdb30a874010582106bbde80ac91ae000dc13d8" }
|
||||
pebble-coding-agent = { git = "https://github.com/lithoscomputer/pebble", rev = "6cdb30a874010582106bbde80ac91ae000dc13d8", features = ["mcp"] }
|
||||
pebble-agent = { git = "https://github.com/lithoscomputer/pebble", rev = "49da13798c753eb312b765596840bdc200b819d7" }
|
||||
pebble-coding-agent = { git = "https://github.com/lithoscomputer/pebble", rev = "49da13798c753eb312b765596840bdc200b819d7", features = ["mcp"] }
|
||||
sandbox-driver = { git = "https://github.com/lithoscomputer/sandbox-driver", rev = "23062b6ad62ff4665cbbcb7dce037ec9c4c34318" }
|
||||
sandbox-driver-protocol = { git = "https://github.com/lithoscomputer/sandbox-driver", rev = "23062b6ad62ff4665cbbcb7dce037ec9c4c34318" }
|
||||
sandbox-driver-host = { git = "https://github.com/lithoscomputer/sandbox-driver", rev = "23062b6ad62ff4665cbbcb7dce037ec9c4c34318" }
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use std::sync::{Arc, Mutex};
|
|||
|
||||
use anyhow::{Context as _, Result as AnyResult};
|
||||
use async_trait::async_trait;
|
||||
use fabro_llm::catalog::agent_profile;
|
||||
use fabro_llm::credentials::CredentialProvider;
|
||||
use fabro_llm::gateway::{GatewayAdapter, GatewayError, GatewayTransport};
|
||||
use fabro_llm::lithos_catalog::{Catalog, CatalogProvider};
|
||||
|
|
@ -21,13 +20,12 @@ use fabro_mcp::config::McpServerSettings;
|
|||
use fabro_mcp::pebble::pebble_servers;
|
||||
use fabro_sandbox::{RunSandbox, SecretRedactor, local_sandbox};
|
||||
use fabro_static::EnvVars;
|
||||
use fabro_types::PermissionLevel;
|
||||
use fabro_types::settings::cli::OutputFormat as SettingsOutputFormat;
|
||||
use fabro_types::settings::run::ResolvedMcpEntry;
|
||||
use fabro_types::{AgentProfileKind, PermissionLevel};
|
||||
use fabro_util::exit::{self, ErrorExt, ExitClass};
|
||||
use fabro_util::home::Home;
|
||||
use fabro_util::terminal::Styles;
|
||||
use fabro_workflow::agent_memory;
|
||||
use fabro_workflow::web_search::{SearchBackend, SearchSecrets};
|
||||
use lithos_llm::catalog::ProviderId;
|
||||
use pebble_agent::{ToolCallRequest, ToolSystemError};
|
||||
|
|
@ -38,7 +36,9 @@ use pebble_coding_agent::subagents::SubagentOptions;
|
|||
use pebble_coding_agent::tools::{
|
||||
ApprovalDecision, PermissionLevelPolicy, PermissionMiddleware, ToolApprovalService,
|
||||
};
|
||||
use pebble_coding_agent::{CodingAgent, CodingAgentOptions, ShutdownReason};
|
||||
use pebble_coding_agent::{
|
||||
CodingAgent, CodingAgentOptions, MemoryDiscovery, ShutdownReason, SkillDiscovery,
|
||||
};
|
||||
use tokio::io::{AsyncWriteExt, stdout};
|
||||
use tokio::signal;
|
||||
use tokio::task::spawn_blocking;
|
||||
|
|
@ -609,23 +609,20 @@ async fn run_session(
|
|||
PermissionMiddleware::new(Arc::new(PermissionLevelPolicy::new(permissions)))
|
||||
.with_approval(approval);
|
||||
|
||||
// The profile's own instruction files from the repository root down, and
|
||||
// fabro's skill directories: pebble knows the files and does the walk.
|
||||
let mut options = CodingAgentOptions::default()
|
||||
.with_memory_files(agent_memory::memory_paths(
|
||||
sandbox.working_directory(),
|
||||
agent_profile(&catalog, provider_id.as_str(), Some(&model))
|
||||
.unwrap_or(AgentProfileKind::OpenAi),
|
||||
))
|
||||
.with_memory_discovery(MemoryDiscovery::from_git_root())
|
||||
.with_recorded_permission_level(permissions);
|
||||
if let Some(skills_dir) = &args.skills_dir {
|
||||
options = options.with_skill_dirs([skills_dir.clone()]);
|
||||
} else {
|
||||
let root = sandbox.working_directory().trim_end_matches('/');
|
||||
options = options.with_skill_dirs([
|
||||
Home::from_env().skills_dir().to_string_lossy().into_owned(),
|
||||
format!("{root}/.fabro/skills"),
|
||||
format!("{root}/skills"),
|
||||
]);
|
||||
}
|
||||
options = match &args.skills_dir {
|
||||
Some(skills_dir) => options.with_skill_dirs([skills_dir.clone()]),
|
||||
None => options.with_skill_discovery(
|
||||
SkillDiscovery::new()
|
||||
.search(Home::from_env().skills_dir().to_string_lossy().into_owned())
|
||||
.search_under_git_root(".fabro/skills")
|
||||
.search_under_git_root("skills"),
|
||||
),
|
||||
};
|
||||
|
||||
let environment: Arc<dyn Environment> = Arc::clone(&sandbox) as Arc<dyn Environment>;
|
||||
let mut builder = CodingAgent::builder(client, environment)
|
||||
|
|
|
|||
|
|
@ -771,7 +771,7 @@ async fn build_agent(
|
|||
Some(stored) => {
|
||||
let mut record = stored.record;
|
||||
if let Ok(Some(last_seq)) = run_store.last_event_seq().await {
|
||||
record.advance_event_cursor(u64::from(last_seq));
|
||||
record.resume_after(u64::from(last_seq));
|
||||
}
|
||||
CodingAgent::resume(
|
||||
llm_result.client,
|
||||
|
|
|
|||
|
|
@ -1,92 +1,53 @@
|
|||
//! Project memory files for agent and prompt stages.
|
||||
//! Project memory for prompt stages.
|
||||
//!
|
||||
//! Pebble loads memory from explicit paths and looks in no conventional
|
||||
//! location. Fabro supplies the convention: each coding harness reads the
|
||||
//! instruction files its vendor's own agent reads, found in the sandbox
|
||||
//! working directory. The loading itself — the 32 KB budget, duplicate text
|
||||
//! skipped, the file that crosses the budget cut and marked — is pebble's
|
||||
//! [`ProjectMemory`], the same loader an agent stage runs over
|
||||
//! `with_memory_files`, so a prompt stage reads by the agent's rules.
|
||||
//! Agent stages ask pebble to discover the profile's instruction files from
|
||||
//! the repository root down (`MemoryDiscovery::from_git_root`). A prompt
|
||||
//! stage reads the working directory alone, as it always has, through the
|
||||
//! same discovery and the same loader, so the two agree on which files a
|
||||
//! harness reads and how much of them fits.
|
||||
|
||||
use fabro_sandbox::RunSandbox;
|
||||
use fabro_types::AgentProfileKind;
|
||||
use pebble_coding_agent::environment::Environment;
|
||||
use pebble_coding_agent::{InterruptReason, ProjectMemory};
|
||||
use pebble_coding_agent::{InterruptReason, MemoryDiscovery, ProjectMemory};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
/// The instruction filenames a harness reads, in load order.
|
||||
#[must_use]
|
||||
pub fn memory_filenames(profile_kind: AgentProfileKind) -> &'static [&'static str] {
|
||||
// `AgentProfileKind` is non-exhaustive: a profile pebble adds later reads
|
||||
// the shared AGENTS.md until fabro says otherwise.
|
||||
match profile_kind {
|
||||
AgentProfileKind::Anthropic | AgentProfileKind::Claude5 => &["AGENTS.md", "CLAUDE.md"],
|
||||
AgentProfileKind::OpenAi | AgentProfileKind::Gpt56 | AgentProfileKind::Gpt6 => {
|
||||
&["AGENTS.md", ".codex/instructions.md"]
|
||||
}
|
||||
AgentProfileKind::Gemini => &["AGENTS.md", "GEMINI.md"],
|
||||
// Kimi Code reads only AGENTS.md; it has no vendor-specific
|
||||
// instruction filename of its own.
|
||||
AgentProfileKind::Kimi | _ => &["AGENTS.md"],
|
||||
}
|
||||
}
|
||||
|
||||
/// The candidate memory paths for a harness working in `working_dir`.
|
||||
///
|
||||
/// Missing and empty files are skipped by the loader, so every candidate can
|
||||
/// be named without checking the sandbox first.
|
||||
#[must_use]
|
||||
pub fn memory_paths(working_dir: &str, profile_kind: AgentProfileKind) -> Vec<String> {
|
||||
let root = working_dir.trim_end_matches('/');
|
||||
memory_filenames(profile_kind)
|
||||
.iter()
|
||||
.map(|filename| format!("{root}/{filename}"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The memory text a prompt stage inlines into its system prompt: every
|
||||
/// candidate file's contents, loaded by pebble's [`ProjectMemory`] rules.
|
||||
/// The memory text a prompt stage inlines into its system prompt: the
|
||||
/// profile's instruction files in the sandbox working directory, loaded by
|
||||
/// pebble's [`ProjectMemory`] rules.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::Cancelled`] when `cancel` fires around a read.
|
||||
pub async fn load_memory_text(
|
||||
sandbox: &RunSandbox,
|
||||
working_dir: &str,
|
||||
profile_kind: AgentProfileKind,
|
||||
cancel: &CancellationToken,
|
||||
) -> Result<Option<String>, Error> {
|
||||
let paths = memory_paths(working_dir, profile_kind);
|
||||
let memory = ProjectMemory::load(sandbox as &dyn Environment, &paths, cancel)
|
||||
let environment: &dyn Environment = sandbox;
|
||||
let paths = MemoryDiscovery::working_directory()
|
||||
.resolve(environment, profile_kind, cancel)
|
||||
.await
|
||||
.map_err(|error| match error {
|
||||
pebble_coding_agent::Error::Interrupted(InterruptReason::Cancelled) => Error::Cancelled,
|
||||
other => Error::handler_with_source("Failed to load project memory", other),
|
||||
})?;
|
||||
.map_err(cancelled_or_handler)?;
|
||||
let memory = ProjectMemory::load(environment, &paths, cancel)
|
||||
.await
|
||||
.map_err(cancelled_or_handler)?;
|
||||
Ok((!memory.is_empty()).then(|| memory.text()))
|
||||
}
|
||||
|
||||
fn cancelled_or_handler(error: pebble_coding_agent::Error) -> Error {
|
||||
match error {
|
||||
pebble_coding_agent::Error::Interrupted(InterruptReason::Cancelled) => Error::Cancelled,
|
||||
other => Error::handler_with_source("Failed to load project memory", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn candidate_paths_follow_the_profile() {
|
||||
assert_eq!(memory_paths("/work/", AgentProfileKind::Claude5), vec![
|
||||
"/work/AGENTS.md".to_string(),
|
||||
"/work/CLAUDE.md".to_string()
|
||||
]);
|
||||
assert_eq!(memory_paths("/work", AgentProfileKind::Gpt56), vec![
|
||||
"/work/AGENTS.md".to_string(),
|
||||
"/work/.codex/instructions.md".to_string()
|
||||
]);
|
||||
assert_eq!(memory_paths("/work", AgentProfileKind::Kimi), vec![
|
||||
"/work/AGENTS.md".to_string()
|
||||
]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn memory_text_dedupes_and_skips_missing_files() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
|
@ -102,7 +63,6 @@ mod tests {
|
|||
|
||||
let text = load_memory_text(
|
||||
&sandbox,
|
||||
sandbox.working_directory(),
|
||||
AgentProfileKind::Anthropic,
|
||||
&CancellationToken::new(),
|
||||
)
|
||||
|
|
@ -110,14 +70,17 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
assert_eq!(text.as_deref(), Some("shared"));
|
||||
let none = load_memory_text(
|
||||
let gemini = load_memory_text(
|
||||
&sandbox,
|
||||
sandbox.working_directory(),
|
||||
AgentProfileKind::Gemini,
|
||||
&CancellationToken::new(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(none.as_deref(), Some("shared"));
|
||||
assert_eq!(
|
||||
gemini.as_deref(),
|
||||
Some("shared"),
|
||||
"AGENTS.md is every harness's"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ use pebble_coding_agent::subagents::SubagentOptions;
|
|||
use pebble_coding_agent::tools::{RegisteredTool, ToolEnvProvider};
|
||||
use pebble_coding_agent::{
|
||||
CodingAgent, CodingAgentBuilder, CodingAgentControlHandle, CodingAgentExport,
|
||||
CodingAgentOptions, CodingInput, InterruptReason, ShutdownReason, SteeringLease,
|
||||
SteeringMessage, SteeringOutcome,
|
||||
CodingAgentOptions, CodingInput, InterruptReason, MemoryDiscovery, ShutdownReason,
|
||||
SkillDiscovery, SteeringLease, SteeringMessage, SteeringOutcome,
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
|
|
@ -58,7 +58,6 @@ use super::controls::{
|
|||
use super::fabro_tools::register_fabro_run_tools;
|
||||
use super::fallback::{self, FallbackPlan, LlmRoute};
|
||||
use super::routing::{self, ProviderContext};
|
||||
use crate::agent_memory;
|
||||
use crate::context::WorkflowContext;
|
||||
use crate::context::keys::Fidelity;
|
||||
use crate::error::Error;
|
||||
|
|
@ -591,33 +590,31 @@ impl PebbleBackend {
|
|||
build_llm_client(&self.catalog, Arc::clone(&self.source)).await
|
||||
}
|
||||
|
||||
fn skill_dirs(&self, sandbox: &RunSandbox) -> Vec<String> {
|
||||
if let Some(dirs) = &self.skill_dirs {
|
||||
return dirs.clone();
|
||||
/// Where a stage's skills come from: the directories the backend was
|
||||
/// given, else fabro's convention — the user's skills directory, then
|
||||
/// `.fabro/skills` and `skills` under the repository root — which pebble
|
||||
/// resolves and searches.
|
||||
fn skill_options(&self, options: CodingAgentOptions) -> CodingAgentOptions {
|
||||
match &self.skill_dirs {
|
||||
Some(dirs) => options.with_skill_dirs(dirs.clone()),
|
||||
None => options.with_skill_discovery(
|
||||
SkillDiscovery::new()
|
||||
.search(Home::from_env().skills_dir().to_string_lossy().into_owned())
|
||||
.search_under_git_root(".fabro/skills")
|
||||
.search_under_git_root("skills"),
|
||||
),
|
||||
}
|
||||
let root = sandbox.working_directory().trim_end_matches('/');
|
||||
let mut dirs = vec![Home::from_env().skills_dir().to_string_lossy().into_owned()];
|
||||
dirs.push(format!("{root}/.fabro/skills"));
|
||||
dirs.push(format!("{root}/skills"));
|
||||
dirs
|
||||
}
|
||||
|
||||
fn agent_options(
|
||||
&self,
|
||||
node: &Node,
|
||||
profile_kind: AgentProfileKind,
|
||||
controls: EffectiveRequestControls,
|
||||
sandbox: &RunSandbox,
|
||||
) -> CodingAgentOptions {
|
||||
CodingAgentOptions::default()
|
||||
fn agent_options(&self, node: &Node, controls: EffectiveRequestControls) -> CodingAgentOptions {
|
||||
// The profile's own instruction files, from the repository root down
|
||||
// to the working directory: pebble knows the files and does the walk.
|
||||
let options = CodingAgentOptions::default()
|
||||
.with_reasoning_effort(controls.reasoning_effort)
|
||||
.with_speed(controls.speed)
|
||||
.with_max_tokens(node_max_output_tokens(node).map(i64::from))
|
||||
.with_memory_files(agent_memory::memory_paths(
|
||||
sandbox.working_directory(),
|
||||
profile_kind,
|
||||
))
|
||||
.with_skill_dirs(self.skill_dirs(sandbox))
|
||||
.with_memory_discovery(MemoryDiscovery::from_git_root());
|
||||
self.skill_options(options)
|
||||
.with_recorded_permission_level(PermissionLevel::Full)
|
||||
.with_context_compaction(true)
|
||||
.with_compaction_threshold_percent(COMPACTION_THRESHOLD_PERCENT)
|
||||
|
|
@ -649,12 +646,7 @@ impl PebbleBackend {
|
|||
.tools(self.stage_tools())
|
||||
.mcp_servers(pebble_servers(&self.mcp_servers))
|
||||
.permission_level(PermissionLevel::Full)
|
||||
.options(self.agent_options(
|
||||
node,
|
||||
provider.profile_kind,
|
||||
route.controls,
|
||||
bindings.sandbox,
|
||||
))
|
||||
.options(self.agent_options(node, route.controls))
|
||||
.fallback_routes(plan.pebble_routes(max_tokens))
|
||||
.event_sink(Arc::new(WorkflowEventSink {
|
||||
emitter: Arc::clone(bindings.emitter),
|
||||
|
|
@ -1244,17 +1236,23 @@ impl CodergenBackend for PebbleBackend {
|
|||
.with_reported_cost(live.total_cost);
|
||||
|
||||
live.release_lease();
|
||||
let mut export = reuse_key.as_ref().map(|_| live.agent.export());
|
||||
if let Err(error) = live.agent.shutdown(ShutdownReason::Completed).await {
|
||||
tracing::debug!(error = %error, "agent session did not shut down cleanly");
|
||||
}
|
||||
if let (Some(key), Some(export)) = (reuse_key, export.as_mut()) {
|
||||
// The close is in the log now; the successor numbers past it.
|
||||
export.advance_event_cursor(live.agent.committed_event_seq());
|
||||
self.store_thread(key, CachedThread {
|
||||
export: export.clone(),
|
||||
fallback_plan: fallback_plan.clone(),
|
||||
});
|
||||
match reuse_key {
|
||||
// The thread's successor continues from an export whose cursor is
|
||||
// already past this session's close.
|
||||
Some(key) => match live.agent.export_for_reuse(ShutdownReason::Completed).await {
|
||||
Ok(export) => self.store_thread(key, CachedThread {
|
||||
export,
|
||||
fallback_plan: fallback_plan.clone(),
|
||||
}),
|
||||
Err(error) => {
|
||||
tracing::debug!(error = %error, "agent session did not shut down cleanly");
|
||||
}
|
||||
},
|
||||
None => {
|
||||
if let Err(error) = live.agent.shutdown(ShutdownReason::Completed).await {
|
||||
tracing::debug!(error = %error, "agent session did not shut down cleanly");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CodergenResult::Text {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ impl Handler for PromptHandler {
|
|||
|
||||
// 1b. Discover project docs for system prompt when project_memory is enabled
|
||||
let system_prompt = if node.project_memory() {
|
||||
let working_dir = services.run.sandbox.working_directory();
|
||||
let profile_kind = routing::resolve_node_provider_context(
|
||||
services.run.catalog.as_ref(),
|
||||
&services.run.provider_id,
|
||||
|
|
@ -77,7 +76,6 @@ impl Handler for PromptHandler {
|
|||
.profile_kind;
|
||||
agent_memory::load_memory_text(
|
||||
&services.run.sandbox,
|
||||
working_dir,
|
||||
profile_kind,
|
||||
&services.run.cancel_token(),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue