Add the gpt6 agent profile kind

lithos ships `metadata.agent.profile = "gpt6"` on the GPT-6 Astra row.
Fabro runs it on the GPT-5.6 harness: the same Codex core tool set,
memory filenames, question tool, and command timeout. The shared
`uses_codex_core_tools` predicate replaces the `== Gpt56` checks so the
two kinds cannot drift apart.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-09-09 22:37:28 -06:00
parent f2695b96c7
commit 7d5f33ab16
No known key found for this signature in database
6 changed files with 28 additions and 13 deletions

View file

@ -544,7 +544,7 @@ async fn run_with_args_and_client_and_catalog_styled(
&model,
Arc::clone(&catalog),
);
let profile_builder = if profile_kind == AgentProfileKind::Gpt56 {
let profile_builder = if profile_kind.uses_codex_core_tools() {
profile_builder
} else {
profile_builder.with_web_fetch_summarizer(Some(build_summarizer(

View file

@ -139,9 +139,10 @@ impl NativeToolOptions {
AgentProfileKind::Kimi => 60_000,
// Codex's `shell_command` documents a 10s default, which is
// already fabro's, so GPT-5.6 budgets against the same number.
AgentProfileKind::OpenAi | AgentProfileKind::Gemini | AgentProfileKind::Gpt56 => {
defaults.default_command_timeout_ms
}
AgentProfileKind::OpenAi
| AgentProfileKind::Gemini
| AgentProfileKind::Gpt56
| AgentProfileKind::Gpt6 => defaults.default_command_timeout_ms,
};
Self {
default_command_timeout_ms,

View file

@ -34,7 +34,7 @@ pub async fn discover_memory(
AgentProfileKind::Anthropic | AgentProfileKind::Claude5 => {
vec!["AGENTS.md", "CLAUDE.md"]
}
AgentProfileKind::OpenAi | AgentProfileKind::Gpt56 => {
AgentProfileKind::OpenAi | AgentProfileKind::Gpt56 | AgentProfileKind::Gpt6 => {
vec!["AGENTS.md", ".codex/instructions.md"]
}
AgentProfileKind::Gemini => vec!["AGENTS.md", "GEMINI.md"],

View file

@ -104,7 +104,7 @@ impl AgentProfileBuilder {
/// `web_fetch` discard it instead of retaining an unused LLM client.
#[must_use]
pub fn with_web_fetch_summarizer(mut self, summarizer: Option<WebFetchSummarizer>) -> Self {
if self.profile_kind != AgentProfileKind::Gpt56 {
if !self.profile_kind.uses_codex_core_tools() {
self.summarizer = summarizer;
}
self
@ -115,7 +115,7 @@ impl AgentProfileBuilder {
let model = self.model.as_str();
let deps = ProfileDeps {
options: self.native_tool_options.clone(),
summarizer: if self.profile_kind == AgentProfileKind::Gpt56 {
summarizer: if self.profile_kind.uses_codex_core_tools() {
None
} else {
self.summarizer.clone()
@ -147,7 +147,7 @@ impl AgentProfileBuilder {
.with_provider_id(self.provider_id.clone())
.with_catalog(Arc::clone(&self.catalog)),
),
AgentProfileKind::Gpt56 => Box::new(
AgentProfileKind::Gpt56 | AgentProfileKind::Gpt6 => Box::new(
Gpt56Profile::with_native_tools(model, &deps)
.with_route(self.provider_id.clone(), Arc::clone(&self.catalog)),
),

View file

@ -194,8 +194,8 @@ pub fn is_question_tool(name: &str) -> bool {
pub fn register_question_tools(profile_kind: AgentProfileKind, registry: &mut ToolRegistry) {
match profile_kind {
// Codex names this tool `request_user_input` for GPT-5.6 too.
AgentProfileKind::OpenAi | AgentProfileKind::Gpt56 => {
// Codex names this tool `request_user_input` for GPT-5.6 and GPT-6 too.
AgentProfileKind::OpenAi | AgentProfileKind::Gpt56 | AgentProfileKind::Gpt6 => {
registry.register(make_openai_question_tool());
}
// Kimi Code names this tool `AskUserQuestion` with the same

View file

@ -1,8 +1,8 @@
//! Agent profile vocabulary shared by the catalog policy and the agent.
//! Agent profile vocabulary shared by the catalog and the agent.
//!
//! The catalog records which profile a model should run under in its
//! `metadata.fabro.agent_profile` entry. This enum is the Rust spelling of
//! that value.
//! `metadata.agent.profile` entry, a namespace lithos-llm ships and Pebble
//! reads too. This enum is the Rust spelling of that value.
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString, IntoStaticStr, VariantArray};
@ -47,6 +47,9 @@ pub enum AgentProfileKind {
/// per provider, so other models on the `openai` provider keep
/// [`Self::OpenAi`].
Gpt56,
/// GPT-6 models (Astra), which Codex drives with the same narrow tool
/// contract as GPT-5.6. Fabro runs them on the GPT-5.6 harness.
Gpt6,
}
impl AgentProfileKind {
@ -54,6 +57,14 @@ impl AgentProfileKind {
pub fn as_str(self) -> &'static str {
self.into()
}
/// Whether the profile runs Codex's narrow core tool set (a shell, a file
/// editor, and `update_plan`) instead of Fabro's dedicated read,
/// discovery, and fetch tools.
#[must_use]
pub fn uses_codex_core_tools(self) -> bool {
matches!(self, Self::Gpt56 | Self::Gpt6)
}
}
#[cfg(test)]
@ -76,6 +87,9 @@ mod tests {
fn claude5_and_gpt56_use_their_catalog_spellings() {
assert_eq!(AgentProfileKind::Claude5.as_str(), "claude-5");
assert_eq!(AgentProfileKind::Gpt56.as_str(), "gpt56");
assert_eq!(AgentProfileKind::Gpt6.as_str(), "gpt6");
assert!(AgentProfileKind::Gpt6.uses_codex_core_tools());
assert!(!AgentProfileKind::OpenAi.uses_codex_core_tools());
assert_eq!(AgentProfileKind::OpenAi.as_str(), "openai");
}
}