use std::collections::HashMap;
use std::io::{self, IsTerminal, Write};
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use anyhow::{bail, Context, Result};
use async_trait::async_trait;
use base64::Engine;
use clap::{CommandFactory, FromArgMatches, Parser, Subcommand, ValueEnum};
use supercode_harness::mcp::{cache_churn_notice, McpClient, McpServerHandle};
use supercode_harness::mcp_oauth;
use supercode_harness::memory::{
search_memory, show_memory, MemoryQuery, MemorySearchQuery, MEMORY_HARNESSES, MEMORY_SCHEMA,
};
use supercode_harness::modules::ModuleId;
use supercode_harness::reduce::{self, ReductionLog, ReductionPolicy};
use supercode_harness::session_title;
use supercode_harness::skills::{list_skills, SkillScope, SkillsQuery, SKILL_HARNESSES};
use supercode_harness::tools::{
minify_tool_schema, shell_sandbox_unenforceable, SandboxPolicy, ToolContext, ToolRegistry,
};
use supercode_harness::{
model_context_limit, AgentEvent, ApprovalPolicy, CachePlan, ChatMessage, ChatRequest, Config,
ConfigBuilder, DiscoveryQuery, HarnessHomes, HarnessId, OpenAiProvider, Provider, Role,
SchemaTier, SdkAgent, SdkRuntime, SessionFollower, SessionLocator, SessionStore,
ToolAdvertising, ToolSchema, DEFAULT_SYSTEM_PROMPT, UNKNOWN_MODEL_CONTEXT_FLOOR,
};
use supercode_interchange::session::{
looks_like_sqlite, opencode_sqlite_primary_id, opencode_sqlite_session_ids,
opencode_sqlite_store_stats, Session, SessionFormat, SessionSource,
};
use supercode_interchange::sidecar::SidecarWriter;
mod frontend_client;
mod hidden_input;
mod hooks;
mod mcp_import;
mod message;
mod notify;
mod open;
mod openrouter_login;
mod orchestrator_import;
mod picker;
mod repl_completer;
mod slash;
mod soft_steer;
mod spinner;
mod terminal_snapshot;
mod terminal_title;
mod tui;
mod ui;
mod userconfig;
use repl_completer::ReplHelper;
use slash::{resolve_slash_command, suggest_slash_command, SlashCmd};
use spinner::Spinner;
use std::sync::Arc;
use userconfig as uc;
const GIT_SHA: Option<&str> = option_env!("SC_GIT_SHA");
const BUILD_DATE: Option<&str> = option_env!("SC_BUILD_DATE");
fn long_version() -> String {
let version = env!("CARGO_PKG_VERSION");
match (GIT_SHA, BUILD_DATE) {
(Some(sha), Some(date)) => format!("{version} ({sha} {date})"),
(Some(sha), None) => format!("{version} ({sha})"),
(None, Some(date)) => format!("{version} ({date})"),
(None, None) => version.to_string(),
}
}
const AGENT_FLAGS: &[&str] = &[
"model",
"fallback_model",
"base_url",
"api_key",
"sandbox",
"approval",
"schema_tier",
"yes",
"dangerous",
"effort",
"system_prompt",
"system_prompt_file",
"append_system_prompt",
"add_dir",
"tools",
"disallow_tool",
"max_tokens",
"temperature",
"max_output_tokens",
"max_iterations",
"max_budget_usd",
"max_steps",
"no_project_context",
"no_cache_warnings",
"reduced",
"no_reduced",
"continue_",
"last",
"mcp_config",
"image",
"notify",
"no_notify",
"notify_threshold_secs",
"permission_profile",
];
const AGENT_VERBS: &[&str] = &["run", "chat", "serve", "acp", "resume", "handoff", "mcp"];
const VERB_AGENT_FLAGS: &[(&[&str], &[&str])] = &[
(&["doctor"], &["model", "base_url", "api_key"]),
(&["skills", "remove"], &["yes"]),
(&["profiles", "delete"], &["yes"]),
];
fn scope_agent_flags(mut command: clap::Command) -> clap::Command {
fn hide(command: clap::Command) -> clap::Command {
let names: Vec<String> = command
.get_subcommands()
.map(|sub| sub.get_name().to_string())
.collect();
let mut command = command.mut_args(|arg| {
let hidden = AGENT_FLAGS.contains(&arg.get_id().as_str());
arg.hide(hidden)
});
for name in names {
command = command.mut_subcommand(name, hide);
}
command
}
command.build();
let verbs: Vec<String> = command
.get_subcommands()
.map(|sub| sub.get_name().to_string())
.filter(|name| !AGENT_VERBS.contains(&name.as_str()))
.collect();
for verb in verbs {
command = command.mut_subcommand(verb, hide);
}
command
}
fn refuse_agent_flags(command: &clap::Command, matches: &clap::ArgMatches) -> Result<(), String> {
let Some((verb, mut leaf)) = matches.subcommand() else {
return Ok(());
};
if AGENT_VERBS.contains(&verb) {
return Ok(());
}
let mut path = vec![verb.to_string()];
let mut leaf_command = command.find_subcommand(verb);
while let Some((name, sub)) = leaf.subcommand() {
path.push(name.to_string());
leaf_command = leaf_command.and_then(|parent| parent.find_subcommand(name));
leaf = sub;
}
let own_flag = |id: &str| {
leaf_command.is_some_and(|leaf_command| {
leaf_command
.get_arguments()
.any(|arg| arg.get_id() == id && !arg.is_global_set())
})
};
let verb_reads = |id: &str| {
VERB_AGENT_FLAGS
.iter()
.any(|(verb_path, ids)| path == *verb_path && ids.contains(&id))
};
let given: Vec<String> = AGENT_FLAGS
.iter()
.filter(|id| !own_flag(id) && !verb_reads(id))
.filter(|id| {
[matches, leaf]
.iter()
.any(|found| found.value_source(id) == Some(clap::parser::ValueSource::CommandLine))
})
.map(|id| {
command
.get_arguments()
.find(|arg| arg.get_id() == *id)
.and_then(|arg| arg.get_long())
.map_or_else(|| id.to_string(), |long| format!("--{long}"))
})
.collect();
if given.is_empty() {
return Ok(());
}
Err(format!(
"`{}` does not run Volter Harness's agent, so {} would be ignored. These flags shape \
that agent only (`run`, `chat`, `resume`, `serve`, `acp`); another harness takes its \
own flags or configuration.",
path.join(" "),
given.join(", ")
))
}
fn workspace_command_index(raw: &[String]) -> Option<usize> {
let command = Cli::command();
let globals: Vec<_> = command
.get_arguments()
.filter(|argument| argument.is_global_set())
.collect();
let short_argument = |name: char| {
globals.iter().find(|argument| {
argument.get_short() == Some(name)
|| argument
.get_all_short_aliases()
.is_some_and(|aliases| aliases.contains(&name))
})
};
let mut index = 0;
while let Some(argument) = raw.get(index).map(String::as_str) {
if matches!(argument, "context" | "sessions" | "teams") {
return Some(index);
}
if argument == "--" || !argument.starts_with('-') || argument == "-" {
return None;
}
if argument == "--json" {
index += 1;
continue;
}
if let Some(long) = argument.strip_prefix("--") {
let (name, attached) = long
.split_once('=')
.map_or((long, false), |(name, _)| (name, true));
let Some(known) = globals.iter().find(|candidate| {
candidate.get_long() == Some(name)
|| candidate
.get_all_aliases()
.is_some_and(|aliases| aliases.contains(&name))
}) else {
return None;
};
index += 1;
if !attached && known.get_action().takes_values() {
index += 1;
}
continue;
}
let mut shorts = argument[1..].chars().peekable();
let mut consume_next = false;
while let Some(short) = shorts.next() {
let Some(known) = short_argument(short) else {
return None;
};
if known.get_action().takes_values() {
consume_next = shorts.peek().is_none();
break;
}
}
index += 1 + usize::from(consume_next);
}
None
}
fn effective_quiet(cli: &Cli) -> bool {
cli.quiet || spinner::env_quiet()
}
#[derive(Parser)]
#[command(
name = "supercode",
version,
about = "The glue tool for translating, rescuing, and reducing AI coding-agent sessions.",
long_about = "Volter Harness (the `supercode` command) — the superset glue tool between AI coding agents.\n\
Translate and continue real Claude Code, Codex, Gemini CLI, Grok, OpenCode, Pi,\n\
and native Volter Harness sessions,\n\
with reversible token reduction and rate-limit rescue across providers.",
after_help = "EXAMPLES:\n \
supercode run \"fix the failing test in src/lib.rs\"\n \
echo \"summarize this repo\" | supercode run\n \
supercode --sandbox workspace-write run \"add a README\"\n \
supercode -i diagram.png run \"implement what this sketch shows\"\n \
supercode run --output-format json \"list 3 refactors\" | jq .result\n \
supercode resume ~/.claude/projects/<id>.jsonl \"continue\"\n\n\
Set OPENROUTER_API_KEY (or pass --api-key) for the model-backed commands."
)]
struct Cli {
#[arg(long, global = true, value_name = "MODEL")]
model: Option<String>,
#[arg(long = "fallback-model", global = true, value_name = "MODEL")]
fallback_model: Vec<String>,
#[arg(long, global = true, value_name = "URL")]
base_url: Option<String>,
#[arg(long, global = true, value_name = "KEY")]
api_key: Option<String>,
#[arg(long, global = true, value_name = "DIR")]
cwd: Option<PathBuf>,
#[arg(long, global = true, value_name = "NAME")]
context: Option<String>,
#[arg(long, global = true, value_enum, value_name = "MODE")]
sandbox: Option<SandboxArg>,
#[arg(long, global = true, value_enum, value_name = "POLICY")]
approval: Option<ApprovalArg>,
#[arg(long, global = true, value_enum, value_name = "TIER")]
schema_tier: Option<SchemaTierArg>,
#[arg(long, short = 'y', global = true)]
yes: bool,
#[arg(long, global = true)]
dangerous: bool,
#[arg(long, global = true, value_name = "LEVEL")]
effort: Option<String>,
#[arg(long, global = true, value_name = "TEXT")]
system_prompt: Option<String>,
#[arg(long, global = true, value_name = "FILE")]
system_prompt_file: Option<PathBuf>,
#[arg(long, global = true, value_name = "TEXT")]
append_system_prompt: Option<String>,
#[arg(long = "add-dir", global = true, value_name = "DIR")]
add_dir: Vec<PathBuf>,
#[arg(long, global = true, value_name = "T1,T2", value_delimiter = ',')]
tools: Option<Vec<String>>,
#[arg(long = "disallow-tool", global = true, value_name = "TOOL")]
disallow_tool: Vec<String>,
#[arg(long, global = true, value_name = "N")]
max_tokens: Option<u32>,
#[arg(long, global = true, value_name = "T")]
temperature: Option<f32>,
#[arg(long, global = true, value_name = "N")]
max_output_tokens: Option<u64>,
#[arg(long, global = true, value_name = "N")]
max_iterations: Option<usize>,
#[arg(long, global = true, value_name = "USD")]
max_budget_usd: Option<f64>,
#[arg(long, global = true, value_name = "N")]
max_steps: Option<usize>,
#[arg(long, global = true)]
no_project_context: bool,
#[arg(long, global = true)]
no_cache_warnings: bool,
#[arg(long, short = 'q', global = true)]
quiet: bool,
#[arg(long, global = true)]
reduced: bool,
#[arg(long, global = true)]
no_reduced: bool,
#[arg(long = "continue", short = 'c', global = true)]
continue_: bool,
#[arg(long, global = true)]
last: bool,
#[arg(long = "mcp-config", global = true, value_name = "FILE")]
mcp_config: Vec<PathBuf>,
#[arg(long, short = 'i', global = true, value_name = "IMG")]
image: Vec<String>,
#[arg(long, global = true)]
trace: bool,
#[arg(long, global = true)]
notify: bool,
#[arg(long, global = true)]
no_notify: bool,
#[arg(long, global = true, value_name = "SECS")]
notify_threshold_secs: Option<u64>,
#[arg(long, global = true)]
bare: bool,
#[arg(long = "config", global = true, value_name = "KEY=VALUE")]
config_override: Vec<String>,
#[arg(long = "permission-profile", global = true, value_name = "NAME")]
permission_profile: Option<String>,
#[arg(long, global = true, value_name = "JSON|PATH")]
settings: Vec<String>,
#[arg(long, short = 'p', global = true, value_name = "NAME")]
profile: Option<String>,
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Run {
prompt: Option<String>,
#[arg(long, value_enum)]
output_format: Option<OutputFormat>,
#[arg(long, value_name = "SCHEMA.json")]
output_schema: Option<PathBuf>,
#[arg(long)]
show_prompt: bool,
},
Chat,
Acp {
#[arg(long, value_name = "URL")]
connect: Option<String>,
#[arg(
long,
value_name = "TOKEN",
requires = "connect",
conflicts_with = "credential_fd"
)]
token: Option<String>,
#[arg(
long,
value_name = "FD",
requires = "connect",
conflicts_with = "token"
)]
credential_fd: Option<i32>,
#[arg(long, value_name = "ID", requires = "connect")]
client_id: Option<String>,
},
Login {
#[arg(long, conflicts_with_all = ["provider", "oauth", "api_key", "api_key_stdin", "model", "base_url"])]
no_connect: bool,
#[arg(long)]
provider: bool,
#[arg(long, conflicts_with_all = ["api_key", "api_key_stdin", "base_url"])]
oauth: bool,
#[arg(long, requires = "oauth")]
headless: bool,
#[arg(long, value_name = "KEY")]
api_key: Option<String>,
#[arg(long, conflicts_with = "api_key")]
api_key_stdin: bool,
#[arg(long, value_name = "MODEL")]
model: Option<String>,
#[arg(long, value_name = "URL")]
base_url: Option<String>,
},
Logout {
#[arg(long)]
provider: bool,
},
#[command(disable_help_flag = true)]
Auth {
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
},
Doctor {
#[arg(long)]
json: bool,
#[arg(long)]
deep: bool,
},
Sessions {
#[command(subcommand)]
action: SessionAction,
},
Profiles {
#[command(subcommand)]
action: ProfileAction,
},
Orchestrator {
#[command(subcommand)]
action: OrchestratorAction,
},
#[command(disable_help_flag = true)]
Teams {
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
args: Vec<String>,
},
Ontology {
#[command(subcommand)]
action: OntologyAction,
},
Orchestration {
#[command(subcommand)]
action: OrchestrationAction,
},
#[command(args_conflicts_with_subcommands = true)]
Workflow {
#[command(subcommand)]
action: Option<WorkflowAction>,
#[arg(trailing_var_arg = true, allow_hyphen_values = true, hide = true)]
args: Vec<String>,
},
Skills {
#[command(subcommand)]
action: SkillsAction,
},
Channels {
#[command(subcommand)]
action: ChannelAction,
},
#[command(alias = "msg")]
Message {
#[command(subcommand)]
action: message::MessageAction,
},
Open(open::OpenArgs),
Close(open::CloseArgs),
Stop(open::StopArgs),
Routes {
#[command(subcommand)]
action: RouteAction,
},
Triggers {
#[command(subcommand)]
action: TriggerAction,
},
Memory {
#[command(subcommand)]
action: MemoryAction,
},
Jobs {
#[command(subcommand)]
action: JobAction,
},
Runs {
#[command(subcommand)]
action: RunAction,
},
Approvals {
#[command(subcommand)]
action: ApprovalsAction,
},
Checkpoint {
#[command(subcommand)]
action: CheckpointAction,
},
Model {
#[command(subcommand)]
action: ModelAction,
},
Provider {
#[command(subcommand)]
action: ProviderAction,
},
Mcp {
#[command(subcommand)]
action: McpAction,
},
#[command(name = "agent-package", alias = "package")]
AgentPackage {
#[command(subcommand)]
action: AgentPackageAction,
},
Serve {
#[arg(long, value_name = "HOST:PORT")]
bind: Option<String>,
#[arg(long, value_name = "TOKEN")]
token: Option<String>,
},
Completions {
shell: clap_complete::Shell,
},
Man,
Update {
#[arg(long)]
install: bool,
#[arg(long, value_enum, value_name = "CHANNEL", default_value_t = UpdateChannelArg::Stable)]
channel: UpdateChannelArg,
},
Config {
#[command(subcommand)]
action: ConfigAction,
},
Features {
#[command(subcommand)]
action: FeaturesAction,
},
#[command(alias = "follow")]
Watch {
file: PathBuf,
#[arg(long, value_name = "ID")]
opencode_session: Option<String>,
#[arg(long, value_name = "MILLISECONDS", default_value_t = 1000)]
poll_ms: u64,
#[arg(long, value_name = "COUNT")]
max_events: Option<u64>,
},
Discover {
#[arg(long, conflicts_with = "on")]
fleet: bool,
#[arg(long, value_name = "MACHINE")]
on: Option<String>,
#[arg(long, value_name = "PATH")]
workspace: Option<PathBuf>,
#[arg(long, value_name = "HARNESS")]
harness: Vec<String>,
#[arg(long, value_name = "PATH")]
claude_home: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
codex_home: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
pi_home: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
opencode_home: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
grok_home: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
gemini_home: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
goose_home: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
supercode_home: Option<PathBuf>,
#[arg(long, value_name = "TEXT")]
query: Option<String>,
#[arg(long, requires = "query")]
search_previews: bool,
#[arg(long, value_name = "CURSOR")]
cursor: Option<String>,
#[arg(long)]
page: bool,
#[arg(long, value_name = "COUNT")]
limit: Option<usize>,
#[arg(long)]
include_child_sessions: bool,
#[arg(long, value_name = "PATH")]
workspace_family: Option<PathBuf>,
#[arg(long, value_name = "MS")]
updated_after_ms: Option<u64>,
#[arg(long, value_name = "MS")]
updated_before_ms: Option<u64>,
},
#[command(alias = "harnesses")]
Harness {
#[command(subcommand)]
action: HarnessAction,
},
Resume {
file: Option<String>,
prompt: Option<String>,
#[arg(long, value_name = "HARNESS", requires = "file")]
harness: Option<String>,
#[arg(long)]
dry_run: bool,
#[arg(long, conflicts_with_all = ["prompt", "dry_run", "serve"])]
acp: bool,
#[arg(long, conflicts_with_all = ["prompt", "dry_run", "acp"])]
serve: bool,
#[arg(
long,
value_enum,
value_name = "CLIENT",
requires = "file",
conflicts_with_all = ["prompt", "dry_run", "acp", "serve"]
)]
frontend: Option<ResumeFrontendArg>,
#[arg(long, value_enum, value_name = "MODE", requires = "frontend")]
frontend_compatibility: Option<FrontendCompatibilityArg>,
#[arg(long, value_name = "HOST:PORT", requires = "serve")]
bind: Option<String>,
#[arg(long, value_name = "TOKEN", requires = "serve")]
token: Option<String>,
#[arg(long, value_name = "MILLISECONDS", requires = "serve")]
lease_ttl_ms: Option<u64>,
#[arg(long, value_name = "ID", requires = "file")]
opencode_session: Option<String>,
},
Inspect {
file: String,
#[arg(long)]
json: bool,
},
Audit {
dir: PathBuf,
#[arg(long, value_enum)]
format: Format,
#[arg(long)]
limit: Option<usize>,
#[arg(long)]
json: bool,
},
Convert {
file: String,
#[arg(long, value_enum)]
to: Format,
#[arg(short, long)]
out: Option<PathBuf>,
#[arg(long)]
session_id: Option<String>,
#[arg(long, value_name = "ID")]
opencode_session: Option<String>,
},
Handoff {
session: String,
#[arg(long, value_name = "ADDR|RANGE|PATH", value_delimiter = ',')]
keep: Vec<String>,
#[arg(long, value_name = "TEXT")]
objective: Option<String>,
#[arg(long, value_name = "K", default_value_t = 6)]
keep_last: usize,
#[arg(long)]
draft_objective: bool,
#[arg(long)]
json: bool,
},
}
fn harness_config_from(
fc: &uc::FileConfig,
trusted_extends: Option<&str>,
) -> supercode_harness::configfile::HarnessConfig {
let mut core = fc.core.clone();
core.model = fc.model.clone().or(core.model);
core.base_url = fc.base_url.clone().or(core.base_url);
core.effort = fc.effort.clone().or(core.effort);
core.temperature = fc.temperature.or(core.temperature);
core.max_tokens = fc.max_tokens.or(core.max_tokens);
core.project_context = fc.project_context.or(core.project_context);
core.system_prompt = fc.system_prompt.clone().or(core.system_prompt);
core.append_system_prompt = fc
.append_system_prompt
.clone()
.or(core.append_system_prompt);
core.api_key_cmd = fc.api_key_cmd.clone().or(core.api_key_cmd);
core.tools.schema_tier = fc.schema_tier.clone().or(core.tools.schema_tier);
let mut capabilities = fc.capabilities.clone();
if fc.sandbox.is_some() || fc.approval.is_some() {
let entry = capabilities.entry("permissions".to_string()).or_default();
if let Some(sandbox) = &fc.sandbox {
entry
.settings
.entry("sandbox".to_string())
.or_insert_with(|| serde_json::Value::String(sandbox.clone()));
}
if let Some(approval) = &fc.approval {
entry
.settings
.entry("approval".to_string())
.or_insert_with(|| serde_json::Value::String(approval.clone()));
}
}
supercode_harness::configfile::HarnessConfig {
schema: None,
schema_version: 1,
extends: Some(trusted_extends.unwrap_or("supercode-default").to_string()),
core,
capabilities,
experimental: fc
.experimental
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
}
}
fn resolve_layered_harness(cli: &Cli) -> Result<supercode_harness::configfile::Resolved> {
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let opts = uc::LoadOptions {
profile: cli.profile.clone(),
settings: cli.settings.clone(),
overrides: config_overrides(cli),
};
let (fc, trusted_extends) = if cli.bare {
(
uc::apply_run_layers(uc::FileConfig::default(), &opts)
.map_err(|e| anyhow::anyhow!("{e}"))?,
None,
)
} else {
uc::load_layered(&cwd, &opts).map_err(|e| anyhow::anyhow!("{e}"))?
};
let hc = harness_config_from(&fc, trusted_extends.as_deref());
supercode_harness::configfile::resolve_harness(
hc,
&supercode_harness::configfile::ResolveOptions::default(),
)
.map_err(|e| anyhow::anyhow!("config does not resolve: {e}"))
}
fn config_lock_path(cli: &Cli, explicit: Option<&Path>) -> PathBuf {
if let Some(p) = explicit {
return p.to_path_buf();
}
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let markers = uc::load(&cwd)
.core
.project_root_markers
.unwrap_or_else(|| vec![".git".to_string()]);
supercode_harness::project_root_for(&cwd, &markers)
.unwrap_or(cwd)
.join(supercode_harness::configfile::CONFIG_LOCK_FILENAME)
}
fn config_cmd(cli: &Cli, action: &ConfigAction) -> Result<()> {
match action {
ConfigAction::Show => {
let resolved = resolve_layered_harness(cli)?;
for w in &resolved.warnings {
eprintln!("{}", ui::paint(ui::WARN, format!("warning: {w}")));
}
println!("{}", serde_json::to_string_pretty(&resolved.harness)?);
Ok(())
}
ConfigAction::Lock { out } => {
let resolved = resolve_layered_harness(cli)?;
let lock = supercode_harness::configfile::ConfigLock::from_resolved(
&resolved,
env!("CARGO_PKG_VERSION"),
);
let path = config_lock_path(cli, out.as_deref());
std::fs::write(&path, format!("{}\n", lock.to_json()))
.with_context(|| format!("writing {}", path.display()))?;
eprintln!(
"{} {} ({} preset{}, supercode v{})",
ui::paint(ui::OK, "locked"),
ui::paint(ui::ACCENT, path.display().to_string()),
lock.preset_chain.len(),
if lock.preset_chain.len() == 1 {
""
} else {
"s"
},
lock.supercode_version,
);
Ok(())
}
ConfigAction::Check {
lock,
lock_file,
json,
} => {
let resolved = resolve_layered_harness(cli)?;
let mut drift: Vec<String> = Vec::new();
let mut lock_path: Option<PathBuf> = None;
if *lock {
let path = config_lock_path(cli, lock_file.as_deref());
let text = std::fs::read_to_string(&path).with_context(|| {
format!(
"reading {} (write one first with `supercode config lock`)",
path.display()
)
})?;
let locked = supercode_harness::configfile::ConfigLock::from_json(&text)
.with_context(|| format!("parsing {}", path.display()))?;
drift = locked.drift(&resolved, env!("CARGO_PKG_VERSION"));
lock_path = Some(path);
}
if *json {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"preset_chain": resolved.preset_chain,
"warnings": resolved.warnings,
"lock_file": lock_path.as_ref().map(|p| p.display().to_string()),
"drift": drift,
"ok": drift.is_empty(),
}))?
);
} else {
for w in &resolved.warnings {
println!("{}", ui::paint(ui::WARN, format!("warning: {w}")));
}
println!(
"preset chain: {}",
if resolved.preset_chain.is_empty() {
"(none)".to_string()
} else {
resolved.preset_chain.join(" -> ")
}
);
if let Some(path) = &lock_path {
if drift.is_empty() {
println!("{} matches {}", ui::paint(ui::OK, "lock:"), path.display());
} else {
println!("{} {}", ui::paint(ui::WARN, "drift vs"), path.display());
for d in &drift {
println!(" {d}");
}
}
}
}
if !drift.is_empty() {
anyhow::bail!(
"resolved config has drifted from the lockfile ({} keys)",
drift.len()
);
}
Ok(())
}
ConfigAction::Schema { write } => {
let text = supercode_harness::config_schema::config_schema_json();
if *write {
let path = PathBuf::from(supercode_harness::config_schema::CONFIG_SCHEMA_PATH);
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("creating {}", parent.display()))?;
}
std::fs::write(&path, &text)
.with_context(|| format!("writing {}", path.display()))?;
eprintln!("{} {}", ui::paint(ui::OK, "wrote"), path.display());
} else {
print!("{text}");
}
Ok(())
}
}
}
fn features_cmd(cli: &Cli, action: &FeaturesAction) -> Result<()> {
let FeaturesAction::List { json } = action;
let resolved = resolve_layered_harness(cli)?;
let states = supercode_harness::configfile::experimental_states(&resolved.harness);
for w in &resolved.warnings {
if w.contains("experimental") {
eprintln!("{}", ui::paint(ui::WARN, format!("warning: {w}")));
}
}
if *json {
println!("{}", serde_json::to_string_pretty(&states)?);
return Ok(());
}
println!("{}", render_feature_flags(&states));
Ok(())
}
fn live_feature_flags(
config: &Config,
) -> Vec<supercode_harness::configfile::ExperimentalFlagState> {
supercode_harness::configfile::EXPERIMENTAL_FLAGS
.iter()
.map(|f| {
let live = match f.name {
"module_registry" => Some(config.module_registry),
_ => None,
};
supercode_harness::configfile::ExperimentalFlagState {
name: f.name.to_string(),
stage: f.stage.label().to_string(),
summary: f.summary.to_string(),
default: f.stage.default_on(),
enabled: live.unwrap_or_else(|| f.stage.default_on()),
explicit: live.is_some_and(|v| v != f.stage.default_on()),
}
})
.collect()
}
fn render_feature_flags(states: &[supercode_harness::configfile::ExperimentalFlagState]) -> String {
let width = states.iter().map(|s| s.name.len()).max().unwrap_or(0);
let mut out = String::new();
for s in states {
let value = if s.enabled { "on" } else { "off" };
let source = if s.explicit { "set" } else { "default" };
out.push_str(&format!(
" {:<width$} {:<12} {:<3} ({source})\n {}\n",
s.name, s.stage, value, s.summary
));
}
if out.is_empty() {
out.push_str(" (this build defines no experimental flags)\n");
}
format!("experimental flags ([experimental] in your config):\n{out}")
}
#[derive(Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
enum UpdateChannelArg {
Stable,
Latest,
}
impl UpdateChannelArg {
fn label(self) -> &'static str {
match self {
UpdateChannelArg::Stable => "stable",
UpdateChannelArg::Latest => "latest",
}
}
}
#[derive(Subcommand)]
enum ConfigAction {
Show,
Lock {
#[arg(long, value_name = "FILE")]
out: Option<PathBuf>,
},
Check {
#[arg(long)]
lock: bool,
#[arg(long, value_name = "FILE")]
lock_file: Option<PathBuf>,
#[arg(long)]
json: bool,
},
Schema {
#[arg(long)]
write: bool,
},
}
#[derive(Subcommand)]
enum FeaturesAction {
#[command(alias = "ls")]
List {
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum McpAction {
Serve {
#[arg(long)]
sdk_readonly: bool,
},
Add {
name: String,
#[arg(long = "env", value_name = "KEY=VALUE")]
env: Vec<String>,
command: String,
#[arg(trailing_var_arg = true)]
args: Vec<String>,
},
#[command(alias = "ls")]
List,
#[command(alias = "rm")]
Remove { name: String },
Import {
#[arg(long, value_enum, default_value = "all")]
from: McpImportSource,
#[arg(long)]
dry_run: bool,
},
Login { name: String },
Logout { name: String },
}
#[derive(Subcommand)]
enum AgentPackageAction {
Inspect {
#[arg(long)]
resolve_resources: bool,
},
Resource {
contribution_id: String,
},
}
#[derive(Subcommand)]
enum HarnessAction {
Report,
Parity {
#[arg(long, default_value = "cc-parity")]
preset: String,
#[arg(long)]
json: bool,
},
List {
#[arg(long, value_name = "PATH")]
workspace: Option<PathBuf>,
#[arg(long, value_enum, default_value_t = HarnessProbeArg::Passive)]
probe: HarnessProbeArg,
#[arg(long, value_name = "HARNESS")]
harness: Vec<String>,
#[arg(long)]
sessions: bool,
#[arg(long)]
json: bool,
},
Settings {
harness: String,
#[arg(long)]
json: bool,
},
Configure {
harness: String,
#[arg(
long,
value_enum,
conflicts_with = "reset_cross_session_inbound",
required_unless_present = "reset_cross_session_inbound"
)]
cross_session_inbound: Option<ClaudeCrossSessionInboundArg>,
#[arg(long, conflicts_with = "cross_session_inbound")]
reset_cross_session_inbound: bool,
#[arg(long)]
json: bool,
},
Login {
harness: String,
#[arg(long)]
headless: bool,
#[arg(long)]
json: bool,
},
Drive {
harness: String,
#[arg(long, value_name = "MACHINE")]
on: Option<String>,
#[arg(long, value_name = "ID")]
session: Option<String>,
#[arg(long, value_name = "TEXT")]
prompt: Option<String>,
#[arg(long, value_name = "DIR")]
cwd: Option<PathBuf>,
},
Serve {
#[arg(long, value_name = "MILLISECONDS", default_value_t = 250)]
poll_ms: u64,
},
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum)]
enum HarnessProbeArg {
#[default]
Passive,
Handshake,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
enum ClaudeCrossSessionInboundArg {
Accept,
Hold,
Refuse,
}
impl From<ClaudeCrossSessionInboundArg> for supercode_harness::ClaudeCrossSessionInbound {
fn from(value: ClaudeCrossSessionInboundArg) -> Self {
match value {
ClaudeCrossSessionInboundArg::Accept => Self::Accept,
ClaudeCrossSessionInboundArg::Hold => Self::Hold,
ClaudeCrossSessionInboundArg::Refuse => Self::Refuse,
}
}
}
impl ClaudeCrossSessionInboundArg {
fn as_str(self) -> &'static str {
supercode_harness::ClaudeCrossSessionInbound::from(self).as_str()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
enum ResumeFrontendArg {
Codex,
Opencode,
Goose,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum)]
enum FrontendCompatibilityArg {
#[default]
TracedOnly,
SchemaExtended,
}
fn validate_frontend_compatibility(
frontend: Option<ResumeFrontendArg>,
compatibility: FrontendCompatibilityArg,
) -> Result<()> {
if frontend.is_some_and(|frontend| frontend != ResumeFrontendArg::Codex)
&& compatibility == FrontendCompatibilityArg::SchemaExtended
{
anyhow::bail!("schema-extended frontend compatibility is Codex-only");
}
Ok(())
}
impl HarnessProbeArg {
fn as_str(self) -> &'static str {
match self {
Self::Passive => "passive",
Self::Handshake => "handshake",
}
}
}
#[derive(Clone, Copy, ValueEnum)]
enum McpImportSource {
Claude,
Codex,
All,
}
#[derive(Subcommand)]
enum JobAction {
#[command(alias = "ls")]
List {
#[arg(long, value_name = "HARNESS")]
harness: Option<String>,
#[arg(long, value_name = "ID")]
session: Option<String>,
#[arg(long, value_name = "NAME")]
profile: Option<String>,
#[arg(long)]
json: bool,
},
Get {
#[arg(long, value_name = "HARNESS")]
harness: String,
id: String,
#[arg(long)]
json: bool,
},
Notepad {
#[arg(long, value_name = "HARNESS")]
harness: String,
id: String,
#[arg(long, value_name = "KEY")]
key: Option<String>,
#[arg(
long,
value_name = "VALUE",
requires = "key",
conflicts_with = "delete"
)]
set: Option<String>,
#[arg(long, requires = "key")]
delete: bool,
#[arg(long, value_name = "NAME")]
profile: Option<String>,
#[arg(long)]
json: bool,
},
Create {
#[arg(long, value_name = "HARNESS")]
harness: String,
#[command(flatten)]
definition: JobDefinitionArgs,
#[arg(long)]
json: bool,
},
Update {
#[arg(long, value_name = "HARNESS")]
harness: String,
id: String,
#[command(flatten)]
definition: JobDefinitionArgs,
#[arg(long)]
json: bool,
},
Pause {
#[arg(long, value_name = "HARNESS")]
harness: String,
id: String,
#[arg(long)]
json: bool,
},
Resume {
#[arg(long, value_name = "HARNESS")]
harness: String,
id: String,
#[arg(long)]
json: bool,
},
Run {
#[arg(long, value_name = "HARNESS")]
harness: String,
id: String,
#[arg(long)]
json: bool,
},
#[command(alias = "rm")]
Delete {
#[arg(long, value_name = "HARNESS")]
harness: String,
id: String,
#[arg(long)]
json: bool,
},
}
#[derive(clap::Args, Clone, Debug)]
struct JobDefinitionArgs {
#[arg(long, value_name = "NAME")]
name: Option<String>,
#[arg(long, value_name = "DURATION", conflicts_with_all = ["cron", "at"])]
every: Option<String>,
#[arg(long, value_name = "EXPR", conflicts_with_all = ["every", "at"])]
cron: Option<String>,
#[arg(long, value_name = "ISO", conflicts_with_all = ["every", "cron"])]
at: Option<String>,
#[arg(long, value_name = "TEXT")]
message: Option<String>,
#[arg(long = "system-event", value_name = "TEXT", conflicts_with = "message")]
system_event: Option<String>,
#[arg(long, value_name = "SHELL", conflicts_with_all = ["message", "system_event"])]
command: Option<String>,
#[arg(long, value_name = "PATH", conflicts_with_all = ["message", "system_event", "command"])]
script: Option<String>,
#[arg(long, value_name = "TARGET")]
session: Option<String>,
#[arg(long, value_name = "TARGET")]
deliver: Option<String>,
#[arg(long = "deliver-to", value_name = "CHAT")]
deliver_to: Option<String>,
#[arg(long, value_name = "NAME")]
profile: Option<String>,
#[arg(long = "skill", value_name = "SKILL")]
skills: Vec<String>,
#[arg(long = "clear-skills", conflicts_with = "skills")]
clear_skills: bool,
#[arg(long, value_name = "DIR")]
workdir: Option<String>,
#[arg(long = "pin-model", value_name = "MODEL")]
pin_model: Option<String>,
#[arg(long = "pin-provider", value_name = "PROVIDER")]
pin_provider: Option<String>,
#[arg(long, value_name = "N")]
repeat: Option<u32>,
}
#[derive(Subcommand)]
enum RunAction {
#[command(alias = "ls")]
List {
#[arg(long, value_name = "HARNESS")]
harness: Option<String>,
#[arg(long, value_name = "JOB")]
job: Option<String>,
#[arg(long, value_name = "N")]
limit: Option<usize>,
#[arg(long)]
json: bool,
},
Get {
#[arg(long, value_name = "HARNESS")]
harness: String,
id: String,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum SessionAction {
#[command(alias = "ls")]
List {
#[arg(long)]
json: bool,
#[arg(long)]
live: bool,
#[arg(long, value_name = "HARNESS", conflicts_with = "live")]
harness: Vec<String>,
#[arg(long, value_name = "NAME", conflicts_with = "live")]
profile: Option<String>,
},
Close {
runtime: String,
},
Archive {
session: String,
#[command(flatten)]
harness: SessionControlArgs,
},
#[command(alias = "rm")]
Delete {
session: String,
#[command(flatten)]
harness: SessionControlArgs,
},
Rename {
session: String,
new_name: String,
#[arg(long)]
title: bool,
},
New {
#[arg(long, value_name = "HARNESS")]
harness: String,
#[arg(long, value_name = "ID")]
session: Option<String>,
#[arg(long, value_name = "KEY")]
surface: Option<String>,
#[arg(long, value_name = "NAME")]
profile: Option<String>,
#[arg(long)]
json: bool,
},
Reset {
#[arg(long, value_name = "HARNESS")]
harness: String,
#[arg(long, value_name = "ID")]
session: Option<String>,
#[arg(long, value_name = "KEY")]
surface: Option<String>,
#[arg(long, value_name = "NAME")]
profile: Option<String>,
#[arg(long)]
json: bool,
},
Fork {
session: String,
#[arg(long)]
at: Option<usize>,
},
ShowReductions {
session: String,
#[arg(long)]
json: bool,
},
Export {
session: String,
#[arg(long)]
format: Option<String>,
#[arg(long)]
out: Option<PathBuf>,
},
Prune {
#[arg(long)]
retention_days: Option<u32>,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
enum ApprovalDecisionArg {
AllowOnce,
AllowAlways,
Deny,
}
impl From<ApprovalDecisionArg> for supercode_harness::ApprovalDecision {
fn from(value: ApprovalDecisionArg) -> Self {
match value {
ApprovalDecisionArg::AllowOnce => Self::AllowOnce,
ApprovalDecisionArg::AllowAlways => Self::AllowAlways,
ApprovalDecisionArg::Deny => Self::Deny,
}
}
}
#[derive(clap::Args, Clone, Debug)]
struct SessionControlArgs {
#[arg(long, value_name = "HARNESS")]
harness: Option<String>,
#[arg(long, value_name = "URL")]
base_url: Option<String>,
#[arg(long, value_name = "NAME")]
profile: Option<String>,
#[arg(long)]
json: bool,
}
#[derive(Subcommand)]
enum ApprovalsAction {
#[command(alias = "ls")]
List {
#[arg(long, value_name = "HARNESS")]
harness: Option<String>,
#[arg(long, value_name = "ID")]
session: Option<String>,
#[arg(long)]
json: bool,
},
Resolve {
#[arg(value_name = "ID")]
id: String,
#[arg(value_name = "DECISION")]
decision: ApprovalDecisionArg,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum MemoryAction {
Show {
#[arg(long, value_name = "HARNESS")]
harness: String,
#[arg(long, value_name = "PROFILE")]
profile: Option<String>,
#[arg(long, value_name = "SESSION")]
session: Option<String>,
#[arg(long)]
full: bool,
#[arg(long)]
json: bool,
},
Search {
#[arg(long, value_name = "HARNESS")]
harness: String,
#[arg(value_name = "QUERY")]
query: String,
#[arg(long, value_name = "PROFILE")]
profile: Option<String>,
#[arg(long)]
regex: bool,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum SkillsAction {
#[command(alias = "ls")]
List {
#[arg(long, value_name = "HARNESS")]
harness: Option<String>,
#[arg(long, value_name = "SCOPE")]
scope: Option<String>,
#[arg(long)]
json: bool,
},
Install {
#[arg(long, value_name = "HARNESS")]
harness: String,
#[arg(value_name = "SOURCE")]
source: String,
#[arg(long, value_name = "SCOPE", default_value = "user")]
scope: String,
#[arg(long, value_name = "NAME")]
name: Option<String>,
#[arg(long)]
json: bool,
},
#[command(alias = "uninstall")]
Remove {
#[arg(long, value_name = "HARNESS")]
harness: String,
#[arg(value_name = "NAME")]
name: String,
#[arg(long, value_name = "SCOPE", default_value = "user")]
scope: String,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum ProfileAction {
#[command(alias = "ls")]
List {
#[arg(long, value_name = "HARNESS")]
harness: Option<String>,
#[arg(long)]
json: bool,
},
Show {
#[arg(long, value_name = "HARNESS")]
harness: String,
name: String,
#[arg(long)]
json: bool,
},
Create {
#[arg(long, value_name = "HARNESS")]
harness: String,
name: String,
#[arg(long, value_name = "NAME")]
from: Option<String>,
#[arg(long, value_name = "DIR")]
workspace: Option<String>,
#[arg(long)]
json: bool,
},
#[command(alias = "rm")]
Delete {
#[arg(long, value_name = "HARNESS")]
harness: String,
name: String,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum OntologyAction {
Operations,
Schema {
#[arg(long)]
record: Option<String>,
#[arg(long)]
list: bool,
},
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum)]
enum OrchestrationFlavorArg {
#[default]
Orchestrator,
Hermes,
}
impl From<OrchestrationFlavorArg> for supercode_harness::orchestration_doors::HomeFlavor {
fn from(flavor: OrchestrationFlavorArg) -> Self {
match flavor {
OrchestrationFlavorArg::Orchestrator => Self::Orchestrator,
OrchestrationFlavorArg::Hermes => Self::Hermes,
}
}
}
#[derive(Clone, Copy, ValueEnum)]
enum SourceFlavorArg {
Native,
Orchestrator,
}
impl From<SourceFlavorArg> for supercode_harness::orchestration_doors::SourceFlavor {
fn from(value: SourceFlavorArg) -> Self {
match value {
SourceFlavorArg::Native => Self::Native,
SourceFlavorArg::Orchestrator => Self::Orchestrator,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
enum OrchestrationHarnessArg {
Hermes,
Openclaw,
}
impl From<OrchestrationHarnessArg>
for supercode_harness::orchestration_doors::OrchestrationHarness
{
fn from(harness: OrchestrationHarnessArg) -> Self {
match harness {
OrchestrationHarnessArg::Hermes => Self::Hermes,
OrchestrationHarnessArg::Openclaw => Self::Openclaw,
}
}
}
#[derive(Subcommand)]
enum WorkflowAction {
Load {
#[arg(long, value_enum)]
from: WorkflowHarnessArg,
home: PathBuf,
#[arg(long)]
json: bool,
},
#[command(external_subcommand)]
Board(Vec<String>),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
enum WorkflowHarnessArg {
Hermes,
}
impl From<WorkflowHarnessArg> for supercode_harness::workflow_doors::WorkflowHarness {
fn from(value: WorkflowHarnessArg) -> Self {
match value {
WorkflowHarnessArg::Hermes => Self::Hermes,
}
}
}
#[derive(Subcommand)]
enum OrchestrationAction {
Load {
root: PathBuf,
#[arg(long, value_enum, default_value_t = OrchestrationFlavorArg::Orchestrator)]
flavor: OrchestrationFlavorArg,
#[arg(long)]
json: bool,
},
Save {
root: PathBuf,
#[arg(long, value_name = "FILE")]
from_json: PathBuf,
},
Compile {
#[arg(long, value_enum)]
from: OrchestrationHarnessArg,
home: PathBuf,
#[arg(long)]
json: bool,
},
Decompile {
#[arg(long, value_enum)]
to: OrchestrationHarnessArg,
#[arg(long, value_name = "HOME")]
source: PathBuf,
#[arg(long, value_enum, default_value = "native")]
source_flavor: SourceFlavorArg,
#[arg(long, value_name = "DIR")]
dest: PathBuf,
#[arg(long, value_name = "FILE")]
from_json: PathBuf,
#[arg(long)]
json: bool,
},
Import {
#[arg(long, value_enum)]
from: OrchestrationHarnessArg,
home: PathBuf,
#[arg(long, value_name = "DIR")]
into: PathBuf,
#[arg(long)]
json: bool,
},
Export {
#[arg(long, value_enum)]
to: OrchestrationHarnessArg,
root: PathBuf,
#[arg(long, value_name = "DIR")]
dest: PathBuf,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum OrchestratorAction {
Setup {
#[arg(long, value_name = "DIR")]
root: Option<PathBuf>,
#[arg(long, value_name = "PROGRAM", default_value = "node")]
node: String,
#[arg(long, conflicts_with = "uninstall")]
install: bool,
#[arg(long)]
uninstall: bool,
#[arg(long)]
json: bool,
},
Start {
#[arg(long, value_name = "DIR")]
root: Option<PathBuf>,
#[arg(long, value_name = "PROGRAM", default_value = "node")]
node: String,
#[arg(long)]
json: bool,
},
Stop {
#[arg(long, value_name = "DIR")]
root: Option<PathBuf>,
#[arg(long)]
json: bool,
},
Status {
#[arg(long, value_name = "DIR")]
root: Option<PathBuf>,
#[arg(long)]
json: bool,
},
Pair {
#[command(subcommand)]
action: PairAction,
},
Access {
#[command(subcommand)]
action: AccessAction,
},
Import {
#[arg(long, value_name = "KIND")]
from: String,
#[arg(value_name = "ID")]
id: String,
#[arg(long, value_name = "DIR")]
root: Option<PathBuf>,
#[arg(long)]
json: bool,
},
}
#[derive(clap::Args, Clone)]
struct AccessArgs {
#[arg(long, value_name = "DIR")]
root: Option<PathBuf>,
#[arg(long, value_name = "NAME", default_value = "default")]
profile: String,
#[arg(long, value_name = "PROGRAM", default_value = "node")]
node: String,
#[arg(long)]
json: bool,
}
#[derive(Subcommand)]
enum PairAction {
Approve {
#[arg(value_name = "CODE")]
code: String,
#[command(flatten)]
args: AccessArgs,
},
#[command(alias = "ls")]
List {
#[command(flatten)]
args: AccessArgs,
},
}
#[derive(Subcommand)]
enum AccessAction {
Allow {
#[arg(value_name = "PLATFORM")]
platform: String,
#[arg(value_name = "USER_ID")]
user_id: String,
#[command(flatten)]
args: AccessArgs,
},
Revoke {
#[arg(value_name = "PLATFORM")]
platform: String,
#[arg(value_name = "USER_ID")]
user_id: String,
#[command(flatten)]
args: AccessArgs,
},
Admin {
#[command(subcommand)]
action: AdminAction,
},
}
#[derive(Subcommand)]
enum AdminAction {
Add {
#[arg(value_name = "PLATFORM")]
platform: String,
#[arg(value_name = "USER_ID")]
user_id: String,
#[command(flatten)]
args: AccessArgs,
},
Remove {
#[arg(value_name = "PLATFORM")]
platform: String,
#[arg(value_name = "USER_ID")]
user_id: String,
#[command(flatten)]
args: AccessArgs,
},
}
#[derive(Subcommand)]
enum TriggerAction {
#[command(alias = "ls")]
List {
#[arg(long, value_name = "HARNESS")]
harness: Option<String>,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum RouteAction {
#[command(alias = "ls")]
List {
#[arg(long, value_name = "HARNESS")]
harness: Option<String>,
#[arg(long, value_name = "NAME")]
profile: Option<String>,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum ChannelAction {
#[command(alias = "ls")]
List {
#[arg(long, value_name = "HARNESS")]
harness: Option<String>,
#[arg(long)]
json: bool,
},
Status {
#[arg(long, value_name = "HARNESS")]
harness: String,
name: String,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum CheckpointAction {
#[command(alias = "ls")]
List {
#[arg(long)]
json: bool,
},
Diff {
checkpoint: String,
#[arg(long)]
patch: bool,
},
Restore {
checkpoint: String,
},
}
#[derive(Subcommand)]
enum ModelAction {
#[command(alias = "ls")]
List {
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum ProviderAction {
#[command(alias = "ls")]
List {
#[arg(long)]
json: bool,
},
}
#[derive(Clone, Copy, ValueEnum)]
enum SandboxArg {
ReadOnly,
WorkspaceWrite,
DangerFullAccess,
}
impl From<SandboxArg> for SandboxPolicy {
fn from(s: SandboxArg) -> Self {
match s {
SandboxArg::ReadOnly => SandboxPolicy::ReadOnly,
SandboxArg::WorkspaceWrite => SandboxPolicy::WorkspaceWrite,
SandboxArg::DangerFullAccess => SandboxPolicy::DangerFullAccess,
}
}
}
#[derive(Clone, Copy, ValueEnum)]
enum ApprovalArg {
Never,
OnRequest,
Untrusted,
}
impl From<ApprovalArg> for ApprovalPolicy {
fn from(a: ApprovalArg) -> Self {
match a {
ApprovalArg::Never => ApprovalPolicy::Never,
ApprovalArg::OnRequest => ApprovalPolicy::OnRequest,
ApprovalArg::Untrusted => ApprovalPolicy::Untrusted,
}
}
}
#[derive(Clone, Copy, ValueEnum)]
enum SchemaTierArg {
Full,
Medium,
Minimal,
}
impl From<SchemaTierArg> for SchemaTier {
fn from(t: SchemaTierArg) -> Self {
match t {
SchemaTierArg::Full => SchemaTier::Full,
SchemaTierArg::Medium => SchemaTier::Medium,
SchemaTierArg::Minimal => SchemaTier::Minimal,
}
}
}
#[derive(Debug, Clone, Copy, ValueEnum, PartialEq)]
enum OutputFormat {
Text,
Json,
#[value(name = "stream-json")]
StreamJson,
Rpc,
}
#[derive(Clone, Copy, ValueEnum)]
enum Format {
ClaudeCode,
Codex,
#[value(name = "opencode")]
OpenCode,
Pi,
Grok,
Gemini,
Goose,
Hermes,
}
impl From<Format> for SessionFormat {
fn from(f: Format) -> Self {
match f {
Format::ClaudeCode => SessionFormat::ClaudeCode,
Format::Codex => SessionFormat::Codex,
Format::OpenCode => SessionFormat::OpenCode,
Format::Pi => SessionFormat::Pi,
Format::Grok => SessionFormat::Grok,
Format::Gemini => SessionFormat::Gemini,
Format::Goose => SessionFormat::Goose,
Format::Hermes => SessionFormat::Codex,
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
let raw: Vec<String> = std::env::args().skip(1).collect();
if raw.first().map(String::as_str) == Some("__teams-connector-service") {
return teams_connector_service_cmd(&raw[1..]);
}
let command_index = workspace_command_index(&raw);
let explicit_context = raw.iter().enumerate().find_map(|(index, arg)| {
arg.strip_prefix("--context=")
.map(str::to_owned)
.or_else(|| {
(arg == "--context")
.then(|| raw.get(index + 1).cloned())
.flatten()
})
});
let saved_context =
std::fs::read_to_string(supercode_harness::teams_home().join("workspaces/contexts.json"))
.ok()
.and_then(|text| serde_json::from_str::<serde_json::Value>(&text).ok())
.and_then(|value| {
value
.get("current")
.and_then(|current| current.as_str())
.map(str::to_owned)
});
let selected_context = explicit_context
.clone()
.or_else(|| std::env::var("SUPERCODE_CONTEXT").ok())
.or(saved_context);
let selected_remote = selected_context
.as_deref()
.is_some_and(|name| name != "local");
let contextual = match command_index.map(|index| (raw[index].as_str(), index)) {
Some(("context", _)) => true,
Some(("teams", _)) if explicit_context.is_some() => true,
Some(("sessions", index)) => {
let action = raw.get(index + 1).map(String::as_str);
let trailing = raw.get(index + 2..).unwrap_or_default();
let local_backend = trailing
.iter()
.any(|argument| matches!(argument.as_str(), "--live" | "--saved"));
let url_reference = trailing.iter().any(|argument| {
argument.starts_with("https://") || argument.starts_with("http://")
});
selected_remote
|| url_reference
|| (matches!(action, Some("list" | "ls"))
&& explicit_context.is_some()
&& !local_backend)
}
_ => false,
};
if contextual {
return workspace_cmd(&raw);
}
let mut cli = {
let long_version: &'static str = Box::leak(long_version().into_boxed_str());
let mut command = scope_agent_flags(Cli::command().long_version(long_version));
let matches = command.clone().get_matches();
if let Err(message) = refuse_agent_flags(&command, &matches) {
command
.error(clap::error::ErrorKind::ArgumentConflict, message)
.exit();
}
Cli::from_arg_matches(&matches).unwrap_or_else(|e| e.exit())
};
if cli.api_key.is_none() {
cli.api_key = std::env::var("SUPERCODE_SUPERVISED_API_KEY").ok();
}
if !cli.image.is_empty()
&& !matches!(cli.command, Command::Run { .. })
&& !effective_quiet(&cli)
{
eprintln!("warning: --image is only used by `run`; ignoring");
}
startup_update_check(&cli).await;
match &cli.command {
Command::Inspect { file, json } => inspect(&cli, file, *json),
Command::Audit {
dir,
format,
limit,
json,
} => spinner::with_spinner(effective_quiet(&cli), || {
audit(dir.clone(), *format, *limit, *json)
}),
Command::Convert {
file,
to,
out,
session_id,
opencode_session,
} => spinner::with_spinner(effective_quiet(&cli), || {
convert(
file,
*to,
out.as_deref(),
session_id.clone(),
opencode_session.clone(),
cli.cwd.as_deref(),
)
}),
Command::Run {
prompt,
output_format,
output_schema,
show_prompt,
} => {
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let fc = uc::load(&cwd);
let resolved_format = resolve_output_format(*output_format, &fc);
if resolved_format == OutputFormat::Rpc {
if prompt.is_some() {
eprintln!(
"warning: a prompt argument is ignored under --output-format rpc; \
submit turns via the RPC protocol on stdin instead"
);
}
return rpc_cmd(&cli, &fc).await;
}
let prompt = read_prompt(prompt.clone())?;
let hook_set = hooks::load_hook_set(&cwd);
let quiet = effective_quiet(&cli);
let env = hooks::session_env(&cwd, None);
hooks::fire_observational(&hook_set, hooks::HookEvent::SessionStart, &env, quiet);
let result = run_cmd(
&cli,
prompt,
&cli.image,
resolved_format,
output_schema.as_deref(),
*show_prompt,
)
.await;
hooks::fire_observational(&hook_set, hooks::HookEvent::SessionEnd, &env, quiet);
result
}
Command::Serve { bind, token } => serve_cmd(&cli, bind.clone(), token.clone()).await,
Command::Chat => {
maybe_onboard(&cli).await;
require_api_key(&cli)?;
let (mut agent, spinner) = build_agent(&cli, true, None)?;
let frontend_bridge = if tui::should_activate(agent.config()) {
Some(supercode_harness::server::FrontendRequestBridge::new())
} else {
None
};
let elicitation = frontend_bridge
.as_ref()
.map(supercode_harness::server::FrontendRequestBridge::elicitation_handler);
attach_mcp_with_elicitation(&cli, &mut agent, elicitation).await;
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let name = open_session(&cli, &mut agent, &cwd)?;
let model = effective_model(&cli);
let hook_set = hooks::load_hook_set(&cwd);
let quiet = effective_quiet(&cli);
let env = hooks::session_env(&cwd, Some(&name));
let notify_settings = effective_notify(&cli, &uc::load(&cwd));
hooks::fire_observational(&hook_set, hooks::HookEvent::SessionStart, &env, quiet);
let result = if let Some(frontend_bridge) = frontend_bridge {
let persist_name = name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named_with_frontend_bridge(
agent,
name.clone(),
supercode_harness::FrontendRuntimeMetadata::default(),
frontend_bridge,
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
let result = run_attachable_terminal(
runtime.clone(),
supercode_harness::LiveRuntimeSource {
harness: "supercode".into(),
session_id: name.clone(),
workspace: cwd.clone(),
},
)
.await;
runtime
.finalize_with(|agent| persist_session(agent, &name))
.await;
runtime.shutdown().await;
result
} else {
chat(
&mut agent,
Some(&name),
&model,
&spinner,
quiet,
¬ify_settings,
)
.await
};
hooks::fire_observational(&hook_set, hooks::HookEvent::SessionEnd, &env, quiet);
result
}
Command::Acp {
connect,
token,
credential_fd,
client_id,
} => {
acp_cmd(
&cli,
connect.as_deref(),
token.as_deref(),
*credential_fd,
client_id.as_deref(),
)
.await
}
Command::Login {
no_connect,
provider,
oauth,
headless,
api_key,
api_key_stdin,
model,
base_url,
} => {
let provider = *provider
|| *oauth
|| api_key.is_some()
|| *api_key_stdin
|| model.is_some()
|| base_url.is_some();
if !provider {
let mut args = vec!["auth".to_string(), "login".to_string()];
if *no_connect {
args.push("--no-connect".into());
}
return teams_cmd(&args);
}
let key = if *oauth {
Some(openrouter_login::authorize(*headless).await?)
} else {
api_key.clone()
};
login(
key,
*api_key_stdin,
model.clone(),
if *oauth {
Some(uc::DEFAULT_BASE_URL.to_owned())
} else {
base_url.clone()
},
)
.await
}
Command::Logout { provider: true } => {
uc::clear_api_key().context("forgetting saved provider credential")?;
eprintln!("Saved provider credential removed. Environment keys and provider-side authorization are unchanged.");
Ok(())
}
Command::Logout { provider: false } => teams_cmd(&["auth".into(), "logout".into()]),
Command::Auth { args } => {
let mut all = vec!["auth".to_string()];
all.extend(args.iter().cloned());
teams_cmd(&all)
}
Command::Doctor { json, deep } => doctor(&cli, *json, *deep).await,
Command::Profiles { action } => match action {
ProfileAction::List { harness, json } => profiles_list_cmd(harness.as_deref(), *json),
ProfileAction::Show {
harness,
name,
json,
} => profiles_show_cmd(harness, name, *json),
ProfileAction::Create {
harness,
name,
from,
workspace,
json,
} => profile_mutation_cmd(
"harness.v1.profiles.create",
profile_mutation_params(harness, name, from.as_deref(), workspace.as_deref()),
*json,
),
ProfileAction::Delete {
harness,
name,
json,
} => profiles_delete_cmd(&cli, harness, name, *json),
},
Command::Triggers { action } => match action {
TriggerAction::List { harness, json } => triggers_list_cmd(harness.as_deref(), *json),
},
Command::Routes { action } => match action {
RouteAction::List {
harness,
profile,
json,
} => routes_list_cmd(harness.as_deref(), profile.as_deref(), *json),
},
Command::Open(args) => open::run(&cli, args).await,
Command::Close(args) => open::close(args),
Command::Stop(args) => open::stop(args),
Command::Message { action } => {
let code = message::run(action).await?;
if code != 0 {
std::process::exit(code);
}
Ok(())
}
Command::Channels { action } => match action {
ChannelAction::List { harness, json } => channels_list_cmd(harness.as_deref(), *json),
ChannelAction::Status {
harness,
name,
json,
} => channels_status_cmd(harness, name, *json),
},
Command::Orchestrator { action } => orchestrator_cmd(action),
Command::Teams { args } => teams_cmd(args),
Command::Ontology { action } => ontology_cmd(action),
Command::Orchestration { action } => orchestration_cmd(action),
Command::Workflow { action, args } => match action {
Some(action) => workflow_cmd(action),
None => workflow_cmd(&WorkflowAction::Board(args.clone())),
},
Command::Jobs { action } => {
if cli.model.is_some() {
bail!(
"`--model` is Volter Harness's own model and does not reach a job; pin a job's model with `--pin-model`"
);
}
jobs_cmd(action)
}
Command::Runs { action } => runs_cmd(action),
Command::Sessions { action } => match action {
SessionAction::List {
live: true, json, ..
} => live_sessions_cmd(*json).await,
SessionAction::List {
json,
harness,
profile,
..
} if !harness.is_empty() || profile.is_some() => {
discovered_sessions_cmd(harness, profile.as_deref(), *json)
}
SessionAction::Close { runtime } => live_session_close_cmd(runtime).await,
SessionAction::Archive {
session,
harness:
SessionControlArgs {
harness: Some(harness),
base_url,
profile,
json,
},
} => {
session_control_cmd(
"harness.v1.sessions.archive",
harness,
session,
base_url.as_deref(),
profile.as_deref(),
*json,
)
.await
}
SessionAction::Delete {
session,
harness:
SessionControlArgs {
harness: Some(harness),
base_url,
profile,
json,
},
} => {
session_control_cmd(
"harness.v1.sessions.delete",
harness,
session,
base_url.as_deref(),
profile.as_deref(),
*json,
)
.await
}
SessionAction::New {
harness,
session,
surface,
profile,
json,
} => {
session_lifecycle_cmd(
"new",
harness,
session.as_deref(),
surface.as_deref(),
profile.as_deref(),
*json,
)
.await
}
SessionAction::Reset {
harness,
session,
surface,
profile,
json,
} => {
session_lifecycle_cmd(
"reset",
harness,
session.as_deref(),
surface.as_deref(),
profile.as_deref(),
*json,
)
.await
}
_ => sessions_cmd(&cli, action),
},
Command::Skills { action } => match action {
SkillsAction::List {
harness,
scope,
json,
} => skills_list_cmd(&cli, harness.as_deref(), scope.as_deref(), *json),
SkillsAction::Install {
harness,
source,
scope,
name,
json,
} => skills_install_cmd(&cli, harness, source, scope, name.as_deref(), *json),
SkillsAction::Remove {
harness,
name,
scope,
json,
} => skills_remove_cmd(&cli, harness, name, scope, *json),
},
Command::Memory { action } => match action {
MemoryAction::Show {
harness,
profile,
session,
full,
json,
} => memory_show_cmd(
&cli,
harness,
profile.as_deref(),
session.as_deref(),
*full,
*json,
),
MemoryAction::Search {
harness,
query,
profile,
regex,
json,
} => memory_search_cmd(&cli, harness, query, profile.as_deref(), *regex, *json),
},
Command::Approvals { action } => match action {
ApprovalsAction::List {
harness,
session,
json,
} => approvals_list_cmd(harness.as_deref(), session.as_deref(), *json),
ApprovalsAction::Resolve { id, decision, json } => {
approvals_resolve_cmd(id, *decision, *json).await
}
},
Command::Checkpoint { action } => checkpoint_cmd(&cli, action),
Command::Model { action } => model_cmd(action),
Command::Provider { action } => provider_cmd(action),
Command::Mcp { action } => mcp_cmd(&cli, action).await,
Command::AgentPackage { action } => agent_package_cmd(&cli, action),
Command::Completions { shell } => {
let mut cmd = Cli::command();
clap_complete::generate(*shell, &mut cmd, "supercode", &mut io::stdout());
Ok(())
}
Command::Man => {
clap_mangen::Man::new(Cli::command()).render(&mut io::stdout())?;
Ok(())
}
Command::Update { install, channel } => update_cmd(*install, *channel).await,
Command::Config { action } => config_cmd(&cli, action),
Command::Features { action } => features_cmd(&cli, action),
Command::Watch {
file,
opencode_session,
poll_ms,
max_events,
} => watch_cmd(file, opencode_session.as_deref(), *poll_ms, *max_events).await,
Command::Discover {
fleet,
on,
workspace,
harness,
claude_home,
codex_home,
pi_home,
opencode_home,
grok_home,
gemini_home,
goose_home,
supercode_home,
query,
search_previews,
cursor,
page,
limit,
include_child_sessions,
workspace_family,
updated_after_ms,
updated_before_ms,
} => {
validate_discovery_harnesses(harness)?;
if *fleet || on.is_some() {
return fleet_discover_cmd(
on.as_deref(),
query.as_deref(),
*limit,
workspace.as_deref(),
harness,
);
}
discover_cmd(
workspace.clone(),
harness,
DiscoveryHomeOverrides {
claude_code: claude_home.clone(),
codex: codex_home.clone(),
pi: pi_home.clone(),
opencode: opencode_home.clone(),
grok: grok_home.clone(),
gemini: gemini_home.clone(),
goose: goose_home.clone(),
supercode: supercode_home.clone(),
},
query.clone(),
*search_previews,
cursor.clone(),
*page,
*limit,
*include_child_sessions,
workspace_family.clone(),
*updated_after_ms,
*updated_before_ms,
)
}
Command::Harness { action } => harness_cmd(action).await,
Command::Resume {
file,
prompt,
harness,
dry_run,
acp,
serve,
frontend,
frontend_compatibility,
bind,
token,
lease_ttl_ms,
opencode_session,
} => {
if file.is_none() && !picker::available() {
anyhow::bail!(
"resume: no session file given, and no interactive terminal available for the picker \
— pass a `.jsonl` path, or run `supercode sessions list` to see saved sessions"
);
}
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let hook_set = hooks::load_hook_set(&cwd);
let quiet = effective_quiet(&cli);
let session_label = file
.as_ref()
.and_then(|f| Path::new(f).file_name())
.and_then(|n| n.to_str());
let env = hooks::session_env(&cwd, session_label);
hooks::fire_observational(&hook_set, hooks::HookEvent::SessionStart, &env, quiet);
let result = match file {
Some(file) => {
resume_cmd(
&cli,
file,
prompt.clone(),
harness.as_deref(),
ResumeRuntimeOptions {
dry_run: *dry_run,
acp: *acp,
serve: *serve,
frontend: *frontend,
frontend_compatibility: frontend_compatibility.unwrap_or_default(),
bind: bind.clone(),
token: token.clone(),
lease_ttl_ms: *lease_ttl_ms,
},
opencode_session.as_deref(),
)
.await
}
None => resume_picker_cmd(&cli, prompt.clone()).await,
};
hooks::fire_observational(&hook_set, hooks::HookEvent::SessionEnd, &env, quiet);
result
}
Command::Handoff {
session,
keep,
objective,
keep_last,
draft_objective,
json,
} => handoff_cmd(
&cli,
session,
keep,
objective.as_deref(),
*keep_last,
*draft_objective,
*json,
),
}
}
fn effective_reduced(cli: &Cli, fc: &uc::FileConfig, preset_enabled: bool) -> bool {
if cli.no_reduced {
false
} else if cli.reduced {
true
} else {
fc.reduce.unwrap_or(preset_enabled)
}
}
fn configured_reduction_policy(config: &Config) -> ReductionPolicy {
let mut policy = ReductionPolicy::default();
let settings = &config.reduction_policy;
if let Some(value) = settings.stale_reads {
policy.elide_stale_reads = value;
}
if let Some(value) = settings.diff_reads {
policy.diff_rereads = value;
}
if let Some(value) = settings.duplicates {
policy.deduplicate_outputs = value;
}
if let Some(value) = settings.tool_input_elision {
policy.elide_tool_inputs = value;
}
if let Some(value) = settings.supersede {
policy.supersede_enabled = value;
}
if let Some(value) = settings.normalize_output {
policy.normalize_terminal_output = value;
}
if let Some(value) = settings.image_redaction {
policy.redact_images = value;
}
if let Some(value) = settings.span_summaries {
policy.summarize_cleared_turns = value;
}
policy
}
fn effective_notify(cli: &Cli, fc: &uc::FileConfig) -> notify::NotifySettings {
let env_enabled = std::env::var("SUPERCODE_NOTIFY")
.ok()
.map(|v| v != "0" && !v.eq_ignore_ascii_case("false"));
let enabled = if cli.no_notify {
false
} else if cli.notify {
true
} else {
env_enabled.or(fc.notify).unwrap_or(false)
};
let threshold_secs = cli
.notify_threshold_secs
.or(fc.notify_threshold_secs)
.unwrap_or(notify::DEFAULT_THRESHOLD_SECS);
let email = fc.notify_email.as_ref().map(|e| notify::EmailConfig {
smtp_host: e.smtp_host.clone(),
smtp_port: e.smtp_port,
from: e.from.clone(),
to: e.to.clone(),
username: e.username.clone(),
});
notify::NotifySettings {
enabled,
threshold: std::time::Duration::from_secs(threshold_secs),
email,
hooks: hooks::HookSet::resolve(&fc.hooks),
quiet: effective_quiet(cli),
}
}
fn resolve_module_registry(
fc: &uc::FileConfig,
trusted_extends: Option<&str>,
) -> Result<Option<Config>> {
let flag = fc
.experimental
.get("module_registry")
.and_then(|v| v.as_bool());
if flag == Some(false) {
return Ok(None);
}
let names_modules =
flag == Some(true) || trusted_extends.is_some() || !fc.capabilities.is_empty();
if !names_modules {
return Ok(None);
}
let mut experimental = serde_json::Map::new();
for (k, v) in &fc.experimental {
experimental.insert(k.clone(), v.clone());
}
let mut direct_core = supercode_harness::configfile::CoreSection::default();
direct_core.tools.enabled = fc.core.tools.enabled.clone();
direct_core.skills.enabled = fc.core.skills.enabled;
let hc = supercode_harness::configfile::HarnessConfig {
schema: None,
schema_version: 1,
extends: Some(trusted_extends.unwrap_or("supercode-default").to_string()),
core: direct_core,
capabilities: fc.capabilities.clone(),
experimental,
};
let resolved = supercode_harness::configfile::resolve_harness(
hc,
&supercode_harness::configfile::ResolveOptions::default(),
)
.map_err(|e| {
anyhow::anyhow!(
"module_registry configuration is invalid: {e}; refusing to fall \
back to the wider legacy tool registry"
)
})?;
for warning in &resolved.warnings {
eprintln!("{}", ui::paint(ui::WARN, format!("warning: {warning}")));
}
Ok(Some(resolved.config))
}
fn build_config(cli: &Cli) -> Result<Config> {
let config = resolve_config(cli)?;
print_startup_banners(cli, &config);
Ok(config)
}
fn resolve_config(cli: &Cli) -> Result<Config> {
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let (fc, trusted_extends) = if cli.bare {
let opts = uc::LoadOptions {
profile: None,
settings: cli.settings.clone(),
overrides: config_overrides(cli),
};
if let Some(name) = &cli.profile {
anyhow::bail!(
"--profile {name} needs the user config it selects from, which --bare ignores"
);
}
(
uc::apply_run_layers(uc::FileConfig::default(), &opts)
.map_err(|e| anyhow::anyhow!("{e}"))?,
None,
)
} else {
uc::load_layered(
&cwd,
&uc::LoadOptions {
profile: cli.profile.clone(),
settings: cli.settings.clone(),
overrides: config_overrides(cli),
},
)
.map_err(|e| anyhow::anyhow!("{e}"))?
};
let mut registry_config = resolve_module_registry(&fc, trusted_extends.as_deref())?;
let explicit_preset = trusted_extends.is_some() && registry_config.is_some();
let preset_model = explicit_preset.then(|| registry_config.as_ref().unwrap().model.clone());
let preset_base_url =
explicit_preset.then(|| registry_config.as_ref().unwrap().base_url.clone());
let preset_sandbox = explicit_preset.then(|| registry_config.as_ref().unwrap().sandbox);
let preset_approval = explicit_preset.then(|| registry_config.as_ref().unwrap().approval);
let preset_project_context =
explicit_preset.then(|| registry_config.as_ref().unwrap().load_project_context);
let model_routing = registry_config
.as_ref()
.map(|c| c.model_routing.clone())
.unwrap_or_default();
let registry_fallback: Vec<String> = registry_config
.as_ref()
.map(|c| c.model_fallback.clone())
.unwrap_or_default();
let model = model_routing.resolve_alias(
cli.model
.clone()
.or(fc.model.clone())
.or(fc.core.model.clone())
.as_deref()
.unwrap_or_else(|| preset_model.as_deref().unwrap_or(uc::DEFAULT_MODEL)),
);
let cli_fallback_models: Vec<String> = cli
.fallback_model
.iter()
.map(|m| model_routing.resolve_alias(m))
.collect();
let base_url = cli
.base_url
.clone()
.or(fc
.base_url
.clone()
.or(fc.core.base_url.clone())
.map(|b| supercode_harness::configfile::expand_env_vars(&b)))
.unwrap_or_else(|| {
preset_base_url
.clone()
.unwrap_or_else(|| uc::DEFAULT_BASE_URL.into())
});
let mut b = registry_config
.take()
.map(ConfigBuilder::from_config)
.unwrap_or_else(Config::builder)
.model(model)
.base_url(base_url.clone());
if !cli_fallback_models.is_empty() {
let mut chain = registry_fallback.clone();
for m in &cli_fallback_models {
if !chain.iter().any(|existing| existing == m) {
chain.push(m.clone());
}
}
b = b.model_fallback(chain);
}
if let Some(env) = fc.core.api_key_env.clone().filter(|e| !e.is_empty()) {
b = b.api_key_env(env);
}
if let Some(key) = uc::resolve_api_key(cli.api_key.as_deref(), &base_url) {
b = b.api_key(key);
} else if let Some(cmd) = fc
.api_key_cmd
.clone()
.or(fc.core.api_key_cmd.clone())
.filter(|c| !c.is_empty())
{
b = b.api_key_cmd(cmd);
}
if let Some(cwd) = &cli.cwd {
b = b.cwd(cwd.clone());
}
for dir in &cli.add_dir {
b = b.add_dir(dir.clone());
}
if let Some(dirs) = &fc.core.additional_dirs {
for dir in dirs {
b = b.add_dir(supercode_harness::configfile::expand_env_vars(dir));
}
}
let interactive = io::stdin().is_terminal() && io::stderr().is_terminal();
let sandbox = if cli.dangerous {
SandboxPolicy::DangerFullAccess
} else {
cli.sandbox
.map(Into::into)
.or_else(|| fc.sandbox.as_deref().and_then(parse_sandbox))
.unwrap_or_else(|| preset_sandbox.unwrap_or(SandboxPolicy::WorkspaceWrite))
};
b = b.sandbox(sandbox);
let approval = if cli.dangerous || cli.yes {
ApprovalPolicy::Never
} else {
cli.approval
.map(Into::into)
.or_else(|| fc.approval.as_deref().and_then(parse_approval))
.unwrap_or_else(|| {
preset_approval.unwrap_or(if interactive {
ApprovalPolicy::OnRequest
} else {
ApprovalPolicy::Never
})
})
};
b = b.approval(approval);
if let Some(effort) = cli
.effort
.clone()
.or(fc.effort.clone())
.or(fc.core.effort.clone())
{
b = b.effort(effort);
}
if let Some(t) = cli.temperature.or(fc.temperature).or(fc.core.temperature) {
b = b.temperature(t);
}
if let Some(n) = cli.max_tokens.or(fc.max_tokens).or(fc.core.max_tokens) {
b = b.max_tokens(n);
}
if let Some(n) = cli.max_output_tokens.or(fc.core.max_total_output_tokens) {
b = b.max_total_output_tokens(n);
}
if let Some(usd) = cli.max_budget_usd.or(fc.core.max_budget_usd) {
b = b.max_budget_usd(usd);
}
if let Some(n) = cli.max_steps.or(fc.core.max_steps) {
b = b.max_steps(n);
}
if let (Some(input), Some(output)) =
(fc.core.price_input_per_mtok, fc.core.price_output_per_mtok)
{
b = b.model_price(input, output);
}
let mut system_prompt: Option<String> = fc
.system_prompt
.clone()
.or(fc.core.system_prompt.clone())
.as_deref()
.map(supercode_harness::configfile::expand_env_vars);
if let Some(file) = &cli.system_prompt_file {
let text = std::fs::read_to_string(file)
.with_context(|| format!("reading --system-prompt-file {}", file.display()))?;
system_prompt = Some(text);
}
if let Some(sp) = &cli.system_prompt {
system_prompt = Some(sp.clone());
}
if let Some(extra) = fc
.append_system_prompt
.clone()
.or(fc.core.append_system_prompt.clone())
.as_deref()
.map(supercode_harness::configfile::expand_env_vars)
{
let base = system_prompt
.clone()
.unwrap_or_else(|| DEFAULT_SYSTEM_PROMPT.to_string());
system_prompt = Some(format!("{base}\n\n{extra}"));
}
if let Some(extra) = &cli.append_system_prompt {
let base = system_prompt
.clone()
.unwrap_or_else(|| DEFAULT_SYSTEM_PROMPT.to_string());
system_prompt = Some(format!("{base}\n\n{extra}"));
}
if let Some(sp) = system_prompt {
b = b.system_prompt(sp);
}
let project_context = if cli.no_project_context {
false
} else {
fc.project_context
.or(fc.core.project_context)
.unwrap_or_else(|| preset_project_context.unwrap_or(true))
};
b = b.project_context(project_context);
let known = builtin_tool_names();
let check = |names: &[String], flag: &str| -> Result<()> {
for n in names {
if !known.iter().any(|k| k == n) {
anyhow::bail!(
"unknown tool `{n}` in {flag}. Available tools: {}",
known.join(", ")
);
}
}
Ok(())
};
if let Some(allow) = &cli.tools {
check(allow, "--tools")?;
}
check(&cli.disallow_tool, "--disallow-tool")?;
if let Some(allow) = &cli.tools {
for name in builtin_tool_names() {
if !allow.iter().any(|a| a == &name) {
b = b.disable_tool(name);
}
}
}
for name in &cli.disallow_tool {
b = b.disable_tool(name.clone());
}
if let Some(tier) = cli.schema_tier.map(Into::into).or_else(|| {
fc.schema_tier
.as_deref()
.or(fc.core.tools.schema_tier.as_deref())
.and_then(parse_schema_tier)
}) {
b = b.schema_tier(tier);
}
let cache_warnings_env = std::env::var("SUPERCODE_CACHE_WARNINGS")
.ok()
.map(|v| v != "0" && !v.eq_ignore_ascii_case("false"));
let cache_warnings = if cli.no_cache_warnings {
false
} else {
cache_warnings_env.or(fc.cache_warnings).unwrap_or(true)
};
b = b.cache_warnings(cache_warnings);
let mut config = b.build();
if fc.capabilities.contains_key("model_catalog") {
let mc = supercode_harness::model_catalog::resolve(&fc.capabilities, &config.model);
config.model = mc.model;
config.small_model = mc.small_model;
config.model_fallback = mc.fallback;
}
if let Some(cap) = fc.capabilities.get("permissions") {
let (deny, allow) = supercode_harness::configfile::permissions_rules_patterns(cap);
config.tool_deny_patterns = deny;
config.tool_allow_patterns = allow;
}
let core_hc = supercode_harness::configfile::HarnessConfig {
schema: None,
schema_version: 1,
extends: None,
core: fc.core.clone(),
capabilities: Default::default(),
experimental: Default::default(),
};
let mut core_profile = core_hc.to_config_profile();
core_profile.model = None;
core_profile.base_url = None;
core_profile.api_key_env = None;
core_profile.api_key_cmd = None;
core_profile.system_prompt = None;
core_profile.append_system_prompt = None;
core_profile.temperature = None;
core_profile.max_tokens = None;
core_profile.effort = None;
core_profile.project_context = None;
core_profile.additional_dirs = None;
core_profile.schema_tier = None;
core_profile.max_total_output_tokens = None;
config = ConfigBuilder::from_config(config)
.apply_profile(&core_profile)
.build();
if let Some(n) = cli.max_iterations {
if n == 0 {
anyhow::bail!("error: --max-iterations must be >= 1 (got 0)");
}
config = ConfigBuilder::from_config(config).max_iterations(n).build();
}
if let Some(dir) = &config.session_dir {
if let Ok(mut guard) = SESSION_DIR_OVERRIDE.lock() {
*guard = Some(PathBuf::from(dir));
}
}
if let Some(cap) = fc.capabilities.get("deferred_tools") {
if cap.enabled == Some(true) {
let core_list =
supercode_harness::configfile::deferred_tools_core(cap).unwrap_or_default();
config.tool_advertising = ToolAdvertising::Deferred { core: core_list };
}
}
if let Some(cap) = fc.capabilities.get("cache") {
if cap.enabled == Some(true) {
if let Some(plan) = supercode_harness::configfile::cache_plan_str(cap) {
config.cache_plan = match plan.as_str() {
"imported_prefix" | "imported-prefix" => CachePlan::ImportedPrefix,
_ => CachePlan::Off,
};
}
}
}
if let Some(cap) = fc.capabilities.get("tui") {
if cap.enabled == Some(true) {
config.tui_enabled = true;
}
if let Some(theme) = cap.settings.get("theme").and_then(|v| v.as_str()) {
config.tui_theme = theme.to_string();
}
if let Some(vim) = cap.settings.get("vim_mode").and_then(|v| v.as_bool()) {
config.tui_vim_mode = vim;
}
if let Some(keymap) = cap.settings.get("keymap").and_then(|v| v.as_object()) {
for (k, v) in keymap {
if let Some(s) = v.as_str() {
config.tui_keymap.insert(k.clone(), s.to_string());
}
}
}
}
if let Some(cap) = fc.capabilities.get("trust") {
if cap.enabled == Some(true) {
config.trust_enabled = true;
}
if let Some(default) = cap
.settings
.get("default")
.and_then(|v| v.as_str())
.and_then(supercode_harness::plugins::TrustDecision::parse)
{
config.trust_default = default;
}
}
if let Some(cap) = fc.capabilities.get("plugins") {
if cap.enabled == Some(true) {
config.plugins_enabled = true;
}
if let Some(dirs) = cap.settings.get("dirs").and_then(|v| v.as_array()) {
config.plugins_dirs = dirs
.iter()
.filter_map(|v| v.as_str())
.map(PathBuf::from)
.collect();
}
}
if effective_reduced(
cli,
&fc,
config.module_activation.is_active(ModuleId::Reduction),
) {
config.tool_advertising = ToolAdvertising::Deferred {
core: enabled_builtin_tool_names(&config),
};
config.cache_plan = CachePlan::ImportedPrefix;
}
if !matches!(approval, ApprovalPolicy::Never) {
config.approval_handler = Some(Box::new(prompt_for_approval));
}
if io::stdin().is_terminal() && io::stderr().is_terminal() {
config.trust_handler = Some(std::sync::Arc::new(InteractiveTrustPrompt));
}
let quiet_for_hooks = effective_quiet(cli);
let hook_set = hooks::HookSet::resolve(&fc.hooks);
let hooks_trusted = supercode_harness::trust::is_trusted(
&config,
supercode_harness::trust::TrustSurface::Hooks,
);
if !hook_set.is_empty() && !hooks_trusted {
eprintln!(
"[trust] {} lifecycle hook(s) were NOT installed: this workspace is not trusted \
(capabilities.trust)",
hook_set.configured_count()
);
}
if !hook_set.is_empty() && hooks_trusted {
if hook_set.pre_tool.is_some() {
let hs = hook_set.clone();
let quiet = quiet_for_hooks;
config.pre_tool_hook = Some(Box::new(move |name, args| {
let args_json = args.to_string();
let extra: [(&str, String); 2] = [
("SUPERCODE_HOOK_TOOL", name.to_string()),
(
"SUPERCODE_HOOK_TOOL_ARGS",
args_json.chars().take(8_000).collect::<String>(),
),
];
hooks::fire_pre_tool(&hs, &extra, quiet)
}));
}
if hook_set.post_tool.is_some() {
let hs = hook_set.clone();
let quiet = quiet_for_hooks;
config.post_tool_hook = Some(Box::new(move |name, output, is_error| {
let extra: [(&str, String); 3] = [
("SUPERCODE_HOOK_TOOL", name.to_string()),
("SUPERCODE_HOOK_TOOL_ERROR", is_error.to_string()),
("SUPERCODE_HOOK_TOOL_OUTPUT_LEN", output.len().to_string()),
];
hooks::fire_observational(&hs, hooks::HookEvent::PostTool, &extra, quiet);
}));
}
if hook_set.pre_compact.is_some()
|| hook_set.post_compact.is_some()
|| hook_set.subagent_start.is_some()
|| hook_set.subagent_stop.is_some()
{
let hs = hook_set.clone();
let quiet = quiet_for_hooks;
config.lifecycle_hook = Some(Box::new(move |event| {
use supercode_harness::LifecycleEvent as E;
let (kind, extra): (hooks::HookEvent, Vec<(&str, String)>) = match event {
E::PreCompact {
messages,
dropped,
manual,
} => (
hooks::HookEvent::PreCompact,
vec![
("SUPERCODE_HOOK_COMPACT_MESSAGES", messages.to_string()),
("SUPERCODE_HOOK_COMPACT_DROPPED", dropped.to_string()),
(
"SUPERCODE_HOOK_COMPACT_TRIGGER",
if *manual { "manual" } else { "auto" }.to_string(),
),
],
),
E::PostCompact { messages, dropped } => (
hooks::HookEvent::PostCompact,
vec![
("SUPERCODE_HOOK_COMPACT_MESSAGES", messages.to_string()),
("SUPERCODE_HOOK_COMPACT_DROPPED", dropped.to_string()),
],
),
E::SubagentStart { task } => (
hooks::HookEvent::SubagentStart,
vec![(
"SUPERCODE_HOOK_SUBAGENT_TASK",
task.chars().take(2_000).collect::<String>(),
)],
),
E::SubagentStop {
task,
is_error,
output_len,
} => (
hooks::HookEvent::SubagentStop,
vec![
(
"SUPERCODE_HOOK_SUBAGENT_TASK",
task.chars().take(2_000).collect::<String>(),
),
("SUPERCODE_HOOK_SUBAGENT_ERROR", is_error.to_string()),
("SUPERCODE_HOOK_SUBAGENT_OUTPUT_LEN", output_len.to_string()),
],
),
};
hooks::fire_observational(&hs, kind, &extra, quiet);
}));
}
if hook_set.stop.is_some() {
let hs = hook_set.clone();
let quiet = quiet_for_hooks;
config.stop_gate = Some(Box::new(move |content: &str| {
hooks::fire_stop(&hs, content, quiet)
}));
}
}
Ok(config)
}
fn print_startup_banners(cli: &Cli, config: &Config) {
let sandbox = config.sandbox;
let approval = config.approval;
if !effective_quiet(cli) {
let model_label = config
.model
.rsplit('/')
.next()
.unwrap_or(&config.model)
.to_string();
eprintln!();
eprintln!(
"{} {} {} {} {} {}",
ui::logo_mark(),
ui::bold(ui::ACCENT, "Volter Harness"),
ui::paint(ui::DIM, "·"),
ui::paint(ui::MUTED, model_label),
ui::paint(ui::DIM, "·"),
ui::paint(
ui::DIM,
format!("{} / {}", sandbox_label(sandbox), approval_label(approval))
),
);
eprintln!();
}
let enabled_shell_tools: Vec<&str> = ["bash", "shell"]
.into_iter()
.filter(|t| config.tool_enabled(t))
.collect();
if shell_sandbox_unenforceable(
sandbox,
std::env::consts::OS,
&enabled_shell_tools,
supercode_harness::landlock_available(),
) {
eprintln!(
"{}",
ui::paint(
ui::WARN,
format!(
"warning: sandbox `{}` cannot be enforced for shell tools on {} — \
`bash`/`shell` commands run unsandboxed (OS sandboxing is macOS \
seatbelt, or Linux Landlock on a Landlock-capable kernel; this \
host has neither available). File tools still honor the policy. \
Disable shell tools or run inside a container for hard isolation.",
sandbox_label(sandbox),
std::env::consts::OS
)
)
);
eprintln!();
}
}
fn unique_path(requested: &str) -> String {
let p = Path::new(requested);
if !p.exists() {
return requested.to_string();
}
let stem = p
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("transcript");
let ext = p.extension().and_then(|s| s.to_str());
let parent = p.parent();
let mut n = 1u32;
loop {
let name = match ext {
Some(e) => format!("{stem}-{n}.{e}"),
None => format!("{stem}-{n}"),
};
let candidate = match parent {
Some(dir) if !dir.as_os_str().is_empty() => dir.join(&name),
_ => PathBuf::from(&name),
};
if !candidate.exists() {
return candidate.to_string_lossy().into_owned();
}
n += 1;
}
}
fn sandbox_label(s: SandboxPolicy) -> &'static str {
match s {
SandboxPolicy::ReadOnly => "read-only",
SandboxPolicy::WorkspaceWrite => "workspace-write",
SandboxPolicy::DangerFullAccess => "DANGER-full-access",
}
}
fn approval_label(a: ApprovalPolicy) -> &'static str {
match a {
ApprovalPolicy::Never => "never",
ApprovalPolicy::OnRequest => "on-request",
ApprovalPolicy::Untrusted => "untrusted",
ApprovalPolicy::ModelRequested => "model-requested",
}
}
fn parse_sandbox(s: &str) -> Option<SandboxPolicy> {
match s.replace('_', "-").to_ascii_lowercase().as_str() {
"read-only" | "readonly" => Some(SandboxPolicy::ReadOnly),
"workspace-write" | "workspace" => Some(SandboxPolicy::WorkspaceWrite),
"danger-full-access" | "full" => Some(SandboxPolicy::DangerFullAccess),
_ => None,
}
}
fn parse_approval(s: &str) -> Option<ApprovalPolicy> {
match s.replace('_', "-").to_ascii_lowercase().as_str() {
"never" => Some(ApprovalPolicy::Never),
"on-request" | "onrequest" => Some(ApprovalPolicy::OnRequest),
"untrusted" => Some(ApprovalPolicy::Untrusted),
_ => None,
}
}
fn parse_schema_tier(s: &str) -> Option<SchemaTier> {
SchemaTier::parse(s)
}
fn resolve_base_url(cli: &Cli) -> String {
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let fc = uc::load(&cwd);
cli.base_url
.clone()
.or(fc.base_url)
.unwrap_or_else(|| uc::DEFAULT_BASE_URL.into())
}
fn require_api_key(cli: &Cli) -> Result<()> {
let base_url = resolve_base_url(cli);
if uc::resolve_api_key(cli.api_key.as_deref(), &base_url).is_some() {
return Ok(());
}
if !cli.bare {
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
if uc::load(&cwd).api_key_cmd.is_some_and(|c| !c.is_empty()) {
return Ok(());
}
}
anyhow::bail!(
"no API key found.\n \
Fix any one of:\n \
• run `supercode login --provider`\n \
• export OPENROUTER_API_KEY=sk-or-...\n \
• pass --api-key <KEY>"
)
}
fn builtin_tool_names() -> Vec<String> {
ToolRegistry::with_builtins()
.iter()
.map(|t| t.name().to_string())
.collect()
}
fn enabled_builtin_tool_names(config: &Config) -> Vec<String> {
ToolRegistry::from_config(config)
.iter()
.filter(|t| config.tool_enabled(t.name()))
.map(|t| t.name().to_string())
.collect()
}
fn serialized_bytes(msgs: &[ChatMessage]) -> u64 {
msgs.iter()
.map(|m| {
serde_json::to_string(m)
.map(|s| s.len() as u64)
.unwrap_or(0)
})
.sum()
}
fn commas(n: u64) -> String {
let digits = n.to_string();
let bytes = digits.as_bytes();
let mut out = String::with_capacity(bytes.len() + bytes.len() / 3);
for (i, b) in bytes.iter().enumerate() {
if i > 0 && (bytes.len() - i) % 3 == 0 {
out.push(',');
}
out.push(*b as char);
}
out
}
struct AutoTitleGate {
enabled: bool,
base_url: String,
api_key: Option<String>,
model: String,
}
impl AutoTitleGate {
fn capture(config: &Config) -> Self {
AutoTitleGate {
enabled: config.auto_title,
base_url: config.base_url.clone(),
api_key: config.api_key.clone(),
model: config
.small_model
.clone()
.unwrap_or_else(|| config.model.clone()),
}
}
}
struct SpanSummaryGate {
enabled: bool,
base_url: String,
api_key: Option<String>,
model: String,
extra_headers: HashMap<String, String>,
extra_body: serde_json::Map<String, serde_json::Value>,
}
impl SpanSummaryGate {
fn capture(config: &Config) -> Self {
let core_compaction_summarizes = config.compaction_summarize
&& config.compaction_enabled
&& (config.compact_after_messages.is_some()
|| config.compaction_reserve_tokens.is_some());
SpanSummaryGate {
enabled: config.reduction_policy.span_summaries.unwrap_or(false)
|| core_compaction_summarizes,
base_url: config.base_url.clone(),
api_key: config.api_key.clone(),
model: config
.small_model
.clone()
.unwrap_or_else(|| config.model.clone()),
extra_headers: config.extra_headers.clone(),
extra_body: config.extra_body.clone(),
}
}
}
struct CliSessionTitler {
provider: OpenAiProvider,
model: String,
}
impl session_title::SessionTitler for CliSessionTitler {
fn title(&self, prompt: &str) -> supercode_harness::Result<String> {
let mut req = ChatRequest::new(self.model.clone(), vec![ChatMessage::user(prompt)]);
req.max_tokens = Some(32); let handle = tokio::runtime::Handle::current();
let provider = &self.provider;
let (message, _usage) =
tokio::task::block_in_place(|| handle.block_on(provider.complete(&req, &|_| {})))?;
Ok(message.content.unwrap_or_default())
}
fn model_id(&self) -> &str {
&self.model
}
}
struct CliSpanSummarizer {
provider: OpenAiProvider,
model: String,
extra_body: serde_json::Map<String, serde_json::Value>,
}
impl reduce::summarize::SpanSummarizer for CliSpanSummarizer {
fn summarize(&self, span_text: &str) -> reduce::Result<String> {
let prompt = reduce::summarize::render_prompt(span_text);
let mut req = ChatRequest::new(self.model.clone(), vec![ChatMessage::user(prompt)]);
req.max_tokens = Some(256);
req.extra_body = self.extra_body.clone();
let handle = tokio::runtime::Handle::current();
let provider = &self.provider;
let (message, _usage) =
tokio::task::block_in_place(|| handle.block_on(provider.complete(&req, &|_| {})))
.map_err(|error| {
reduce::ReductionError::new(format!(
"span summarizer provider request failed: {error}"
))
})?;
Ok(message.content.unwrap_or_default())
}
fn model_id(&self) -> &str {
&self.model
}
}
fn maybe_install_auto_titler(agent: &mut SdkAgent, gate: &AutoTitleGate) {
if !gate.enabled {
return;
}
let Some(key) = gate.api_key.clone() else {
return;
};
let provider = OpenAiProvider::new(gate.base_url.clone(), key, HashMap::new());
agent.set_session_titler(CliSessionTitler {
provider,
model: gate.model.clone(),
});
}
fn maybe_install_span_summarizer(agent: &mut SdkAgent, gate: &SpanSummaryGate) {
if !gate.enabled {
return;
}
let Some(key) = gate.api_key.clone() else {
return;
};
let provider = OpenAiProvider::new(gate.base_url.clone(), key, gate.extra_headers.clone());
agent.set_span_summarizer(CliSpanSummarizer {
provider,
model: gate.model.clone(),
extra_body: gate.extra_body.clone(),
});
}
fn apply_auto_title(agent: &SdkAgent, name: &str) {
if !agent.session_persist() {
return;
}
let Some(title) = agent.auto_title() else {
return;
};
if let Ok(store) = session_store() {
let _ = store.set_title(name, &title);
}
}
fn build_agent(
cli: &Cli,
interactive: bool,
_prompt_hint: Option<&str>,
) -> Result<(SdkAgent, Arc<Spinner>)> {
let mut config = build_config(cli)?;
let (sink, spinner) = streaming_sink(interactive, effective_quiet(cli));
config.event_sink = Some(with_trace(sink, cli.trace));
let auto_title_gate = AutoTitleGate::capture(&config);
let span_summary_gate = SpanSummaryGate::capture(&config);
let mut agent = supercode_harness::create_agent(config)?;
maybe_install_auto_titler(&mut agent, &auto_title_gate);
maybe_install_span_summarizer(&mut agent, &span_summary_gate);
Ok((agent, spinner))
}
fn streaming_sink(_interactive: bool, quiet: bool) -> (supercode_harness::EventSink, Arc<Spinner>) {
use std::sync::Mutex;
let line_buf = Mutex::new(String::new());
let width = wrap_width();
let spinner = Arc::new(Spinner::new(quiet));
let sink_spinner = spinner.clone();
let flush_pending = move |buf: &mut String| {
if !buf.is_empty() {
let line = std::mem::take(buf);
for out in render_md(line.trim_end_matches('\r'), width) {
println!("{out}");
}
io::stdout().flush().ok();
}
};
let sink: supercode_harness::EventSink = Box::new(move |event: AgentEvent| {
sink_spinner.stop();
sink_spinner.clear_counter();
match event {
AgentEvent::TextDelta(t) => {
let mut buf = line_buf.lock().unwrap();
buf.push_str(&t);
while let Some(pos) = buf.find('\n') {
let line: String = buf.drain(..=pos).collect();
for out in render_md(line.trim_end_matches(['\n', '\r']), width) {
println!("{out}");
}
}
io::stdout().flush().ok();
sink_spinner.record_delta(&t);
sink_spinner.render_counter();
}
AgentEvent::TurnCompleted => {
let mut buf = line_buf.lock().unwrap();
flush_pending(&mut buf);
}
AgentEvent::ToolCallStarted {
name, arguments, ..
} => {
{
let mut buf = line_buf.lock().unwrap();
flush_pending(&mut buf);
}
if !quiet {
eprintln!(
"{} {} {}",
ui::paint(ui::ACCENT, "▸"),
ui::bold(ui::ACCENT, format!("{name:<10}")),
ui::paint(ui::MUTED, tool_arg_summary(&name, &arguments)),
);
}
}
AgentEvent::ToolCallCompleted {
is_error, output, ..
} => {
if !quiet {
let summary = first_line(output.trim(), 88);
if !summary.is_empty() {
eprintln!(
" {} {}",
if is_error {
ui::paint(ui::ERR, "✗")
} else {
ui::paint(ui::DIM, "└")
},
ui::paint(ui::DIM, summary)
);
}
}
sink_spinner.start("Thinking…");
}
AgentEvent::CacheWarning { message } if !quiet => {
eprintln!("{} {}", ui::paint(ui::WARN, "warning:"), message);
}
_ => {}
}
});
(sink, spinner)
}
fn trace_line(event: &AgentEvent) {
let ts = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis())
.unwrap_or(0);
match event {
AgentEvent::ToolCallStarted {
id,
name,
arguments,
} => {
eprintln!(
"[trace {ts}] tool_call id={id} name={name} arguments={}",
trace_truncate(arguments)
);
}
AgentEvent::ToolCallCompleted {
id,
name,
output,
is_error,
} => {
eprintln!(
"[trace {ts}] tool_result id={id} name={name} is_error={is_error} output={}",
trace_truncate(output)
);
}
AgentEvent::Usage(usage) => {
eprintln!(
"[trace {ts}] usage prompt_tokens={} completion_tokens={} total_tokens={}",
usage.prompt_tokens, usage.completion_tokens, usage.total_tokens
);
}
_ => {}
}
}
fn trace_truncate(s: &str) -> String {
const MAX_CHARS: usize = 4000;
let mut it = s.chars();
let head: String = it.by_ref().take(MAX_CHARS).collect();
if it.next().is_some() {
format!("{head}… ({} more chars)", s.chars().count() - MAX_CHARS)
} else {
head
}
}
fn with_trace(sink: supercode_harness::EventSink, trace: bool) -> supercode_harness::EventSink {
if !trace {
return sink;
}
Box::new(move |event: AgentEvent| {
trace_line(&event);
sink(event);
})
}
fn trace_only_sink() -> supercode_harness::EventSink {
Box::new(|event: AgentEvent| trace_line(&event))
}
fn stream_json_sink() -> supercode_harness::EventSink {
Box::new(|event: AgentEvent| {
println!("{}", event.to_json());
io::stdout().flush().ok();
})
}
async fn race_ctrl_c<F: std::future::Future>(
fut: F,
spinner: Option<&Spinner>,
) -> Option<F::Output> {
tokio::select! {
out = fut => Some(out),
_ = tokio::signal::ctrl_c() => {
if let Some(sp) = spinner {
sp.stop();
sp.clear_counter();
}
None
}
}
}
fn print_interrupt_notice(quiet: bool, detail: &str) {
if quiet {
return;
}
eprintln!();
eprintln!(
"{} {}",
ui::bold(ui::WARN, "^C"),
ui::paint(ui::DIM, format!("interrupted — {detail}"))
);
}
fn print_steer_queued_notice(quiet: bool, line: &str) {
if quiet {
return;
}
eprintln!();
eprintln!(
"{} {} {}",
ui::bold(ui::ACCENT, "»"),
ui::paint(ui::DIM, "queued — will steer after this turn:"),
line
);
}
async fn race_ctrl_c_with_steer<F: std::future::Future>(
fut: F,
spinner: Option<&Spinner>,
steer_enabled: bool,
quiet: bool,
) -> (Option<F::Output>, Option<String>) {
if !steer_enabled {
return (race_ctrl_c(fut, spinner).await, None);
}
let (stop, handle) = soft_steer::spawn();
race_turn_with_steer_handle(fut, spinner, quiet, stop, handle).await
}
async fn race_turn_with_steer_handle<F: std::future::Future>(
fut: F,
spinner: Option<&Spinner>,
quiet: bool,
stop: Arc<std::sync::atomic::AtomicBool>,
mut handle: tokio::task::JoinHandle<Option<String>>,
) -> (Option<F::Output>, Option<String>) {
tokio::pin!(fut);
let mut watch_live = true; let mut queued: Option<String> = None;
let outcome = loop {
tokio::select! {
out = &mut fut => break Some(out),
_ = tokio::signal::ctrl_c() => {
if let Some(sp) = spinner {
sp.stop();
sp.clear_counter();
}
break None;
}
joined = &mut handle, if watch_live => {
watch_live = false;
if let Ok(Some(line)) = joined {
if !line.is_empty() {
print_steer_queued_notice(quiet, &line);
queued = Some(line);
}
}
}
}
};
stop.store(true, std::sync::atomic::Ordering::Relaxed);
if watch_live {
if let Ok(Some(line)) = (&mut handle).await {
if !line.is_empty() {
print_steer_queued_notice(quiet, &line);
queued = Some(line);
}
}
}
(outcome, queued)
}
fn wrap_width() -> usize {
let cols = std::env::var("COLUMNS")
.ok()
.and_then(|c| c.trim().parse::<usize>().ok())
.unwrap_or(96);
cols.saturating_sub(2).clamp(40, 96)
}
fn render_md(line: &str, width: usize) -> Vec<String> {
let trimmed = line.trim_start();
if trimmed.starts_with('|') {
let cells: Vec<&str> = trimmed.trim_matches('|').split('|').collect();
let is_sep = cells.iter().all(|c| {
let c = c.trim();
!c.is_empty() && c.chars().all(|ch| ch == '-' || ch == ':')
});
if is_sep {
return vec![format!(" {}", ui::paint(ui::DIM, "─".repeat(48)))];
}
let rendered: Vec<String> = cells
.iter()
.map(|c| render_inline(truncate(c.trim(), 44).as_str()))
.collect();
return vec![format!(" {}", rendered.join(&ui::paint(ui::DIM, " │ ")))];
}
if let Some(rest) = trimmed.strip_prefix('#') {
let rest = rest.trim_start_matches('#').trim_start();
return vec![ui::bold(ui::ACCENT, render_inline(rest))];
}
for marker in ["- ", "* ", "• "] {
if let Some(rest) = trimmed.strip_prefix(marker) {
let first = format!("{} ", ui::paint(ui::ACCENT, "•"));
return wrap_words(styled_words(rest), width, &first, 2, " ");
}
}
if let Some((marker, rest)) = split_ordered(trimmed) {
let first = format!("{} ", ui::paint(ui::ACCENT, marker.trim_end()));
let cont = " ".repeat(marker.chars().count());
return wrap_words(
styled_words(rest),
width,
&first,
marker.chars().count(),
&cont,
);
}
wrap_words(styled_words(trimmed), width, "", 0, "")
}
fn split_ordered(s: &str) -> Option<(String, &str)> {
let digits: String = s.chars().take_while(|c| c.is_ascii_digit()).collect();
if digits.is_empty() {
return None;
}
let after = &s[digits.len()..];
for sep in [". ", ") "] {
if let Some(rest) = after.strip_prefix(sep) {
return Some((format!("{digits}{}", sep.trim_end()), rest));
}
}
None
}
fn styled_words(s: &str) -> Vec<(usize, String)> {
let mut words: Vec<(usize, String)> = Vec::new();
let mut cur = String::new();
let mut cur_w = 0usize;
let chars: Vec<char> = s.chars().collect();
let mut i = 0;
let flush = |cur: &mut String, cur_w: &mut usize, words: &mut Vec<(usize, String)>| {
if *cur_w > 0 {
words.push((*cur_w, std::mem::take(cur)));
*cur_w = 0;
}
};
while i < chars.len() {
if chars[i] == '`' {
if let Some(end) = (i + 1..chars.len()).find(|&j| chars[j] == '`') {
let inner: String = chars[i + 1..end].iter().collect();
cur.push_str(&ui::paint(ui::TEAL, &inner));
cur_w += inner.chars().count();
i = end + 1;
continue;
}
}
if i + 1 < chars.len() && chars[i] == '*' && chars[i + 1] == '*' {
if let Some(end) = find_pair(&chars, i + 2, "**") {
let inner: Vec<char> = chars[i + 2..end].to_vec();
let mut j = 0;
while j < inner.len() {
if inner[j] == ' ' {
flush(&mut cur, &mut cur_w, &mut words);
j += 1;
continue;
}
let start = j;
while j < inner.len() && inner[j] != ' ' {
j += 1;
}
let w: String = inner[start..j].iter().collect();
cur.push_str(&ui::bold_plain(&w));
cur_w += w.chars().count();
}
i = end + 2;
continue;
}
}
if chars[i] == ' ' {
flush(&mut cur, &mut cur_w, &mut words);
i += 1;
continue;
}
cur.push(chars[i]);
cur_w += 1;
i += 1;
}
flush(&mut cur, &mut cur_w, &mut words);
words
}
fn wrap_words(
words: Vec<(usize, String)>,
width: usize,
first: &str,
first_w: usize,
cont: &str,
) -> Vec<String> {
let mut lines: Vec<String> = Vec::new();
let mut cur = String::new();
let mut cur_w = 0usize; let cont_w = cont.chars().count();
let mut on_first = true;
let start_line = |cur: &mut String, cur_w: &mut usize, on_first: bool| {
if on_first {
cur.push_str(first);
*cur_w = first_w;
} else {
cur.push_str(cont);
*cur_w = cont_w;
}
};
start_line(&mut cur, &mut cur_w, on_first);
let mut has_word = false;
for (w, styled) in words {
let extra = if has_word { 1 + w } else { w };
if has_word && cur_w + extra > width {
lines.push(std::mem::take(&mut cur));
on_first = false;
start_line(&mut cur, &mut cur_w, on_first);
has_word = false;
}
if has_word {
cur.push(' ');
cur_w += 1;
}
cur.push_str(&styled);
cur_w += w;
has_word = true;
}
lines.push(cur);
lines
}
fn render_inline(s: &str) -> String {
let mut out = String::new();
let chars: Vec<char> = s.chars().collect();
let mut i = 0;
while i < chars.len() {
if i + 1 < chars.len() && chars[i] == '*' && chars[i + 1] == '*' {
if let Some(end) = find_pair(&chars, i + 2, "**") {
let inner: String = chars[i + 2..end].iter().collect();
out.push_str(&ui::bold_plain(&inner));
i = end + 2;
continue;
}
}
if chars[i] == '`' {
if let Some(end) = (i + 1..chars.len()).find(|&j| chars[j] == '`') {
let inner: String = chars[i + 1..end].iter().collect();
out.push_str(&ui::paint(ui::TEAL, &inner));
i = end + 1;
continue;
}
}
out.push(chars[i]);
i += 1;
}
out
}
fn find_pair(chars: &[char], from: usize, delim: &str) -> Option<usize> {
let d: Vec<char> = delim.chars().collect();
(from..chars.len().saturating_sub(1)).find(|&j| chars[j] == d[0] && chars[j + 1] == d[1])
}
fn tool_arg_summary(name: &str, args: &str) -> String {
let v: serde_json::Value = match serde_json::from_str(args) {
Ok(v) => v,
Err(_) => return first_line(args, 80),
};
let pick = |keys: &[&str]| -> Option<String> {
keys.iter()
.find_map(|k| v.get(*k).and_then(|x| x.as_str()))
.map(|s| s.to_string())
};
let summary = match name {
"bash" | "shell" => pick(&["command", "cmd"]),
"search" => pick(&["pattern", "query", "regex"]),
"glob" | "list_dir" => pick(&["pattern", "path", "dir"]),
_ => pick(&["file_path", "path", "file"]),
}
.or_else(|| pick(&["file_path", "path", "command", "pattern", "query"]))
.unwrap_or_else(|| first_line(args, 80));
first_line(&summary, 90)
}
struct InteractiveTrustPrompt;
impl supercode_harness::permissions::PermissionsApprovalHandler for InteractiveTrustPrompt {
fn ask(
&self,
req: &supercode_harness::permissions::ApprovalRequest,
) -> supercode_harness::permissions::ApprovalOutcome {
use supercode_harness::permissions::ApprovalOutcome;
let loading = req
.raw_args
.get("loading")
.and_then(|v| v.as_str())
.unwrap_or("project-local files");
eprint!(
"\n{} {} {}\n {}\n {} ",
ui::paint(ui::WARN, "?"),
ui::paint(ui::MUTED, "trust this workspace?"),
ui::bold(ui::WARN, req.subject.unwrap_or("(unknown)")),
ui::paint(ui::DIM, format!("about to load {loading}")),
ui::paint(ui::DIM, "[y = this run / a = always / N = no]"),
);
io::stderr().flush().ok();
let mut line = String::new();
if io::stdin().read_line(&mut line).is_err() {
return ApprovalOutcome::Deny;
}
match line.trim() {
"y" | "Y" | "yes" => ApprovalOutcome::Allow,
"a" | "A" | "always" => ApprovalOutcome::AllowForSession,
_ => ApprovalOutcome::Deny,
}
}
}
fn config_overrides(cli: &Cli) -> Vec<String> {
let mut out = cli.config_override.clone();
if let Some(name) = &cli.permission_profile {
out.push(format!("capabilities.permissions.profile={name}"));
}
out
}
fn prompt_for_approval(call: &supercode_harness::ToolCall) -> bool {
eprint!(
"\n{} {} {} {} ",
ui::paint(ui::WARN, "?"),
ui::paint(ui::MUTED, "approve"),
ui::bold(ui::WARN, &call.function.name),
ui::paint(
ui::DIM,
format!("{} [y/N]", first_line(&call.function.arguments, 70))
),
);
io::stderr().flush().ok();
let mut line = String::new();
if io::stdin().read_line(&mut line).is_err() {
return false;
}
matches!(line.trim(), "y" | "Y" | "yes")
}
static SESSION_DIR_OVERRIDE: std::sync::Mutex<Option<PathBuf>> = std::sync::Mutex::new(None);
fn session_store_dir() -> PathBuf {
SESSION_DIR_OVERRIDE
.lock()
.ok()
.and_then(|g| g.clone())
.unwrap_or_else(|| uc::config_home().join("sessions"))
}
fn session_store() -> Result<SessionStore> {
Ok(SessionStore::open(session_store_dir())?)
}
fn session_store_for_resume() -> SessionStore {
SessionStore::at(session_store_dir())
}
fn cwd_tag(cwd: &Path) -> String {
use std::hash::{Hash, Hasher};
let canon = std::fs::canonicalize(cwd).unwrap_or_else(|_| cwd.to_path_buf());
let mut h = std::collections::hash_map::DefaultHasher::new();
canon.hash(&mut h);
format!("{:016x}", h.finish())
}
const SESSION_ADJECTIVES: &[&str] = &[
"brave", "quiet", "swift", "calm", "bold", "sunny", "cozy", "clever", "gentle", "lucky",
"merry", "nimble", "proud", "quick", "silent", "spry", "stark", "vivid", "witty", "zesty",
"amber", "coral", "dusty", "eager", "fresh", "grand", "happy", "ivory", "jolly", "keen",
"lively", "misty", "noble", "olive", "plush", "rapid", "rosy", "salty", "tidy", "warm",
];
const SESSION_NOUNS: &[&str] = &[
"otter", "fox", "wolf", "hawk", "owl", "lynx", "bear", "deer", "crane", "finch", "heron",
"ibis", "koala", "lemur", "mole", "newt", "panda", "quail", "raven", "seal", "tiger", "viper",
"wren", "yak", "zebra", "beaver", "camel", "dingo", "eagle", "falcon", "gecko", "hare", "ibex",
"jackal", "kite", "llama", "moose", "puma", "robin", "stoat",
];
fn mint_session_name(tag: &str, configured: Option<&str>) -> String {
let existing: std::collections::HashSet<String> = session_store()
.map(|s| s.list().into_iter().map(|i| i.name).collect())
.unwrap_or_default();
match configured {
Some(name) => dedupe_session_name(name, &existing),
None => mint_session_name_avoiding(tag, &existing),
}
}
fn dedupe_session_name(base: &str, existing: &std::collections::HashSet<String>) -> String {
if !existing.contains(base) {
return base.to_string();
}
let mut n = 2u64;
loop {
let candidate = format!("{base}{n}");
if !existing.contains(&candidate) {
return candidate;
}
n += 1;
}
}
fn mint_session_name_avoiding(tag: &str, existing: &std::collections::HashSet<String>) -> String {
use std::hash::{Hash, Hasher};
let seed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
^ (std::process::id() as u128);
let word_index = |salt: u64, len: usize| -> usize {
let mut h = std::collections::hash_map::DefaultHasher::new();
(seed, salt).hash(&mut h);
(h.finish() as usize) % len
};
for attempt in 0..64u64 {
let adj = SESSION_ADJECTIVES[word_index(attempt * 2, SESSION_ADJECTIVES.len())];
let noun = SESSION_NOUNS[word_index(attempt * 2 + 1, SESSION_NOUNS.len())];
let candidate = format!("{tag}-{adj}-{noun}");
if !existing.contains(&candidate) {
return candidate;
}
}
let adj = SESSION_ADJECTIVES[word_index(0, SESSION_ADJECTIVES.len())];
let noun = SESSION_NOUNS[word_index(1, SESSION_NOUNS.len())];
let mut n = 2u64;
loop {
let candidate = format!("{tag}-{adj}-{noun}{n}");
if !existing.contains(&candidate) {
return candidate;
}
n += 1;
}
}
fn new_session_name(cwd: &Path, configured: Option<&str>) -> String {
mint_session_name(&cwd_tag(cwd), configured)
}
fn session_time(store: &SessionStore, name: &str) -> u64 {
if let Some(micros) = name.rsplit('-').next().and_then(|s| s.parse::<u128>().ok()) {
return (micros / 1_000_000) as u64;
}
store
.transcript_mtime(name)
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.as_secs())
.unwrap_or(0)
}
fn age_of(t: u64) -> String {
if t == 0 {
return "?".into();
}
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
let secs = now.saturating_sub(t);
if secs < 60 {
format!("{secs}s ago")
} else if secs < 3600 {
format!("{}m ago", secs / 60)
} else if secs < 86_400 {
format!("{}h ago", secs / 3600)
} else {
format!("{}d ago", secs / 86_400)
}
}
fn most_recent_session(cwd: Option<&Path>) -> Option<String> {
let store = session_store().ok()?;
let mut names: Vec<String> = store
.list()
.into_iter()
.filter(|s| !s.archived)
.map(|s| s.name)
.collect();
if let Some(cwd) = cwd {
let tag = cwd_tag(cwd);
names.retain(|n| n.starts_with(&tag));
}
names.into_iter().max_by_key(|n| session_time(&store, n))
}
fn resolve_session(store: &SessionStore, query: &str) -> Result<String> {
let names: Vec<String> = store.list().into_iter().map(|s| s.name).collect();
if names.iter().any(|n| n == query) {
return Ok(query.to_string());
}
let matches: Vec<&String> = names.iter().filter(|n| n.contains(query)).collect();
match matches.as_slice() {
[one] => Ok((*one).clone()),
[] => anyhow::bail!("no session matching `{query}`"),
many => anyhow::bail!("`{query}` is ambiguous ({} matches)", many.len()),
}
}
enum ResolvedInput {
Path(PathBuf),
Store(String),
}
fn resolve_input(input: &str) -> Result<ResolvedInput> {
let path = Path::new(input);
if path.exists() {
return Ok(ResolvedInput::Path(path.to_path_buf()));
}
let store = session_store()?;
let name = resolve_session(&store, input)?;
Ok(ResolvedInput::Store(name))
}
fn persisted_view(agent: &SdkAgent) -> Vec<ChatMessage> {
match agent.reduction_policy() {
Some(policy) => {
let (view, _) =
reduce::project_messages(&agent.history()[1..], policy, agent.reduction_log());
let mut out = Vec::with_capacity(view.len() + 1);
out.push(agent.history()[0].clone());
out.extend(view);
out
}
None => agent.history().to_vec(),
}
}
fn title_for_persistence(
store: &SessionStore,
name: &str,
msgs: &[ChatMessage],
preserve_existing: bool,
) -> String {
if preserve_existing {
if let Some(title) = store
.list()
.into_iter()
.find(|info| info.name == name && !info.title.is_empty())
.map(|info| info.title)
{
return title;
}
}
msgs.iter()
.find(|m| m.role == Role::User)
.and_then(|m| m.content.clone())
.map(|c| first_line(&c, 60))
.unwrap_or_else(|| "session".into())
}
fn persist_session(agent: &SdkAgent, name: &str) {
if !agent.session_persist() {
return;
}
let Ok(store) = session_store() else { return };
let was_reduced = agent.reduction_policy().is_none()
&& store
.list()
.into_iter()
.any(|session| session.name == name && session.reduced);
let msgs = persisted_view(agent);
let jsonl: String = msgs
.iter()
.filter_map(|m| serde_json::to_string(m).ok())
.collect::<Vec<_>>()
.join("\n");
let title = title_for_persistence(&store, name, &msgs, agent.reduction_policy().is_some());
let _ = store.save(name, &title, &jsonl);
if let Some(policy) = agent.reduction_policy() {
let mut log = agent.reduction_log().clone();
if let Ok(Some(sidecar_jsonl)) = store.load_sidecar(name) {
if let Ok(sidecar) = Session::from_sidecar_str(&sidecar_jsonl) {
log.attribution = Some(reduction_attribution(&log, &sidecar, Some(policy)));
}
}
let _ = store.save_reduction_log(name, &log);
let _ = store.set_reduction_stats(
name,
&title,
serialized_bytes(&agent.history()[1..]),
serialized_bytes(&msgs),
log.reductions.len() as u32,
);
} else {
if was_reduced {
if let Ok(Some(sidecar_jsonl)) = store.load_sidecar(name) {
if let Ok(sidecar) = Session::from_sidecar_str(&sidecar_jsonl) {
let _ = store.save_reduction_log(name, agent.reduction_log());
let _ = store.set_reduction_stats(
name,
&title,
serialized_bytes(&sidecar.messages),
serialized_bytes(&msgs),
agent.reduction_log().reductions.len() as u32,
);
}
}
}
}
let _ = agent.save_git_metadata(&store, name);
let _ = agent.save_usage_log(&store, name);
let _ = agent.save_turn_records(&store, name);
let _ = agent.save_goal(&store, name);
if let Some(manifest) = agent.claude_runtime_manifest() {
let _ = store.save_claude_runtime_manifest(name, manifest);
}
agent.checkpoint_session_journal(&store, name, msgs.len());
}
fn persist_full_view(agent: &SdkAgent, name: &str, view: &[ChatMessage]) {
if !agent.session_persist() {
return;
}
let Ok(store) = session_store() else { return };
let mut msgs = Vec::with_capacity(view.len() + 1);
msgs.push(agent.history()[0].clone());
msgs.extend_from_slice(view);
let jsonl: String = msgs
.iter()
.filter_map(|m| serde_json::to_string(m).ok())
.collect::<Vec<_>>()
.join("\n");
let title = title_for_persistence(&store, name, &msgs, true);
let _ = store.save(name, &title, &jsonl);
let _ = store.save_reduction_log(name, agent.reduction_log());
let _ = store.set_reduction_stats(
name,
&title,
serialized_bytes(&agent.history()[1..]),
serialized_bytes(&msgs),
agent.reduction_log().reductions.len() as u32,
);
}
fn open_session(cli: &Cli, agent: &mut SdkAgent, cwd: &Path) -> Result<String> {
arm_context_guard(agent);
let target = if cli.continue_ {
most_recent_session(Some(cwd))
} else if cli.last {
most_recent_session(None)
} else {
None
};
if let Some(name) = target {
let store = session_store()?;
upgrade_session_format(&store, &name, effective_quiet(cli));
let mut reduced_counts = None;
if !cli.no_reduced {
if let Some(sidecar_jsonl) = store.load_sidecar(&name)? {
let sidecar = Session::from_sidecar_str(&sidecar_jsonl)
.with_context(|| format!("parsing sidecar for `{name}`"))?;
let log = load_reduction_log_checked(&store, &name)?;
reduce::verify_log(&log, &sidecar).map_err(|e| {
anyhow::anyhow!("{e} — repair pointer: `sessions show-reductions {name}`")
})?;
let (handoff_note, saved_view_len) = load_saved_handoff_note(&store, &name)?;
let full_len = sidecar.messages.len();
agent.load_session(sidecar);
if let Some(note) = handoff_note {
agent.append_system_note(&format!("\n\n{note}"));
}
agent.set_recorder(SidecarWriter::open_append(&store.sidecar_path(&name))?);
let policy = configured_reduction_policy(agent.config());
agent.set_reduction_policy(policy);
agent.set_reduction_log(log);
reduced_counts = Some((
saved_view_len.unwrap_or_else(|| {
reduce::project_messages(
&agent.history()[1..],
agent.reduction_policy().expect("policy was just installed"),
agent.reduction_log(),
)
.0
.len()
+ 1 }),
full_len,
));
} else {
load_flat_store_transcript(&store, &name, agent)?;
}
} else if let Some(sidecar_jsonl) = store.load_sidecar(&name)? {
let sidecar = Session::from_sidecar_str(&sidecar_jsonl)
.with_context(|| format!("parsing sidecar for `{name}`"))?;
let mut log = load_reduction_log_checked(&store, &name)?;
reduce::verify_log(&log, &sidecar).map_err(|e| {
anyhow::anyhow!("{e} — repair pointer: `sessions show-reductions {name}`")
})?;
log.expanded.append(&mut log.reductions);
let (handoff_note, _) = load_saved_handoff_note(&store, &name)?;
agent.load_session(sidecar);
if let Some(note) = handoff_note {
agent.append_system_note(&format!("\n\n{note}"));
}
agent.set_recorder(SidecarWriter::open_append(&store.sidecar_path(&name))?);
agent.set_reduction_log(log);
} else {
load_flat_store_transcript(&store, &name, agent)?;
}
if let Some(manifest) = store.load_claude_runtime_manifest(&name)? {
agent.set_claude_runtime_manifest(manifest);
agent.restore_claude_project_agents()?;
agent.set_subagent_store(Arc::new(session_store()?), name.clone());
}
arm_session_state(agent, &store, &name, effective_quiet(cli));
agent.restore_goal(store.load_goal(&name)?);
if !effective_quiet(cli) {
if let Some((view_len, full_len)) = reduced_counts {
eprintln!(
"Continuing reduced session ({view_len}-message view; {full_len} full messages)."
);
} else {
eprintln!("Continuing session ({} messages).", agent.history().len());
}
}
Ok(name)
} else {
if (cli.continue_ || cli.last)
&& !matches!(cli.command, Command::Chat)
&& !effective_quiet(cli)
{
eprintln!("(no prior session found; starting fresh)");
}
let name = new_session_name(cwd, agent.session_name());
if let Ok(store) = session_store() {
arm_session_state(agent, &store, &name, effective_quiet(cli));
}
Ok(name)
}
}
fn upgrade_session_format(store: &SessionStore, name: &str, quiet: bool) {
match store.upgrade_in_place(name) {
Ok(Some(up)) => {
if !quiet && up.rewritten {
eprintln!(
"upgraded {} from format v{} to v{} ({} messages; original kept as {})",
short_id(name),
up.from_version,
up.to_version,
up.messages,
up.original
);
}
}
Ok(None) => {}
Err(e) => {
if !quiet {
eprintln!("note: format upgrade skipped for {}: {e}", short_id(name));
}
}
}
}
fn arm_session_state(agent: &mut SdkAgent, store: &SessionStore, name: &str, quiet: bool) {
if !agent.session_persist() {
return;
}
let report = agent.arm_session_journal(store, name);
if quiet {
return;
}
if report.recovered_messages > 0 {
eprintln!(
"recovered {} message(s) from {}'s journal (an earlier run ended mid-turn)",
report.recovered_messages,
short_id(name)
);
}
if report.restored_queue > 0 {
eprintln!("restored {} pending queued input(s)", report.restored_queue);
}
if report.restored_plan > 0 {
eprintln!("restored a {}-step plan", report.restored_plan);
}
}
fn arm_context_guard(agent: &mut SdkAgent) {
let model = agent.config().model.clone();
agent.set_context_limit(model_context_limit(&model).unwrap_or(UNKNOWN_MODEL_CONTEXT_FLOOR));
}
fn load_saved_handoff_note(
store: &SessionStore,
name: &str,
) -> Result<(Option<String>, Option<usize>)> {
let Some(saved) = store.load_if_present(name)? else {
return Ok((None, None));
};
parse_saved_handoff_note(name, &saved)
}
fn load_saved_handoff_note_from(
store: &SessionStore,
name: &str,
archived: bool,
) -> Result<(Option<String>, Option<usize>)> {
let Some(saved) = store.load_if_present_from(name, archived)? else {
return Ok((None, None));
};
parse_saved_handoff_note(name, &saved)
}
fn parse_saved_handoff_note(name: &str, saved: &str) -> Result<(Option<String>, Option<usize>)> {
let saved_view: Vec<ChatMessage> = saved
.lines()
.filter(|line| !line.trim().is_empty())
.map(serde_json::from_str)
.collect::<std::result::Result<_, _>>()
.with_context(|| format!("parsing stored working view for `{name}`"))?;
let len = saved_view.len();
let note = saved_view
.into_iter()
.find(|m| {
m.role == Role::System
&& m.content
.as_deref()
.is_some_and(|c| c.contains("=== HANDOFF ==="))
})
.and_then(|m| {
let content = m.content?;
let marker = content.find("=== HANDOFF ===")?;
Some(content[marker..].to_string())
});
Ok((note, Some(len)))
}
fn load_flat_store_transcript(
store: &SessionStore,
name: &str,
agent: &mut SdkAgent,
) -> Result<()> {
let jsonl = store.load(name)?;
let tmp = std::env::temp_dir().join(format!("sc-resume-{}.jsonl", std::process::id()));
std::fs::write(&tmp, jsonl)?;
let loaded = agent.load_transcript(&tmp);
let _ = std::fs::remove_file(&tmp);
loaded.map_err(Into::into)
}
fn sessions_cmd(cli: &Cli, action: &SessionAction) -> Result<()> {
let config = resolve_config(cli)?;
let store = session_store()?;
match action {
SessionAction::List { json, .. } => {
let sessions = store.index().entries;
if *json {
let metas = store.list();
let meta = |name: &str| {
metas
.iter()
.find(|m| m.name == name)
.cloned()
.unwrap_or_default()
};
let out: Vec<serde_json::Value> = sessions
.iter()
.map(|s| {
serde_json::json!({
"name": s.name,
"short_id": short_id(&s.name),
"title": s.title,
"archived": s.archived,
"reduced": meta(&s.name).reduced,
"tier": meta(&s.name).tier,
"full_bytes": meta(&s.name).full_bytes,
"view_bytes": meta(&s.name).view_bytes,
"stub_count": meta(&s.name).stub_count,
"escalations": meta(&s.name).escalations,
"age": age_of(s.time_secs),
"messages": s.messages,
"preview": s.preview,
})
})
.collect();
println!("{}", serde_json::to_string_pretty(&out)?);
return Ok(());
}
if sessions.is_empty() {
println!();
println!(
" {} {}",
ui::paint(ui::DIM, "○"),
ui::paint(
ui::MUTED,
"no saved sessions yet — run `supercode run \"…\"` to create one"
)
);
println!();
return Ok(());
}
println!();
println!("{}", ui::section(&format!("sessions ({})", sessions.len())));
for s in sessions {
let flag = if s.archived {
ui::paint(ui::DIM, " [archived]")
} else {
String::new()
};
println!(
" {} {} {}{}",
ui::paint(ui::ACCENT, format!("{:<20}", short_id(&s.name))),
ui::paint(ui::DIM, format!("{:>9}", age_of(s.time_secs))),
s.title,
flag
);
}
println!();
}
SessionAction::Archive { session, .. } => {
let name = resolve_session(&store, session)?;
store.archive(&name)?;
eprintln!("archived {}", short_id(&name));
}
SessionAction::Delete { session, .. } => {
let name = resolve_session(&store, session)?;
store.delete(&name)?;
eprintln!("deleted {}", short_id(&name));
}
SessionAction::Rename {
session,
new_name,
title,
} => {
let name = resolve_session(&store, session)?;
if *title {
store.set_title(&name, new_name)?;
eprintln!("titled {} \u{201c}{new_name}\u{201d}", short_id(&name));
} else {
store.rename(&name, new_name)?;
eprintln!("renamed {} \u{2192} {new_name}", short_id(&name));
}
}
SessionAction::ShowReductions { session, json } => show_reductions(&store, session, *json)?,
SessionAction::Fork { session, at } => {
let name = resolve_session(&store, session)?;
let tag = name.split('-').next().unwrap_or("fork");
let new_name = mint_session_name(tag, None);
let title = store
.list()
.into_iter()
.find(|s| s.name == name)
.map(|s| format!("fork of {}", s.title))
.unwrap_or_else(|| "fork".into());
let timestamp_ms = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as i64)
.unwrap_or(0);
store.fork(&name, &new_name, &title, *at, timestamp_ms)?;
eprintln!("forked {} → {}", short_id(&name), short_id(&new_name));
}
SessionAction::Export {
session,
format,
out,
} => {
let name = resolve_session(&store, session)?;
let jsonl = store.load(&name)?;
let messages: Vec<supercode_harness::ChatMessage> = jsonl
.lines()
.filter(|l| !l.trim().is_empty())
.map(serde_json::from_str)
.collect::<std::result::Result<_, _>>()
.context("parsing stored transcript")?;
let fmt_str = format
.clone()
.unwrap_or_else(|| match config.session_export_format {
supercode_harness::human_export::HumanExportFormat::Html => "html".to_string(),
supercode_harness::human_export::HumanExportFormat::Text => "text".to_string(),
});
let fmt = supercode_harness::human_export::HumanExportFormat::parse(&fmt_str)
.unwrap_or_else(|| {
eprintln!("unknown export format `{fmt_str}`; defaulting to text");
supercode_harness::human_export::HumanExportFormat::default()
});
let rendered =
supercode_harness::human_export::render_messages(&messages, Some(&name), None, fmt);
match out {
Some(path) => {
std::fs::write(path, &rendered)?;
eprintln!("exported {} → {}", short_id(&name), path.display());
}
None => {
print!("{rendered}");
}
}
}
SessionAction::Prune { retention_days } => {
let days = match retention_days.or(config.session_retention_days) {
Some(d) => d,
None => {
eprintln!(
"no retention window: pass --retention-days or set \
core.session.retention_days"
);
return Ok(());
}
};
let pruned = store.prune_expired(days, std::time::SystemTime::now())?;
if pruned.is_empty() {
eprintln!("nothing to prune (retention: {days}d)");
} else {
for name in &pruned {
eprintln!("pruned {}", short_id(name));
}
eprintln!("pruned {} archived session(s)", pruned.len());
}
}
SessionAction::Close { .. } | SessionAction::New { .. } | SessionAction::Reset { .. } => {
unreachable!("live session actions are dispatched asynchronously")
}
}
Ok(())
}
async fn session_control_cmd(
method: &str,
harness: &str,
session: &str,
base_url: Option<&str>,
profile: Option<&str>,
json: bool,
) -> Result<()> {
session_rpc_cmd(method, harness, session, None, base_url, profile, json).await
}
#[allow(clippy::too_many_arguments)]
async fn session_rpc_cmd(
method: &str,
harness: &str,
session: &str,
surface: Option<&str>,
base_url: Option<&str>,
profile: Option<&str>,
json: bool,
) -> Result<()> {
let mut params = serde_json::Map::new();
params.insert("harness".into(), harness.into());
params.insert("session".into(), session.into());
if let Some(surface) = surface {
params.insert("surface".into(), surface.into());
}
if let Some(base_url) = base_url {
params.insert("base_url".into(), base_url.into());
}
if let Some(profile) = profile {
params.insert("profile".into(), profile.into());
}
let mut service = supercode_harness::HarnessSessionService::new();
let response = service
.handle_async(serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": method,
"params": serde_json::Value::Object(params),
}))
.await;
if let Some(error) = response.get("error") {
anyhow::bail!(
"{}",
error
.get("message")
.and_then(serde_json::Value::as_str)
.unwrap_or("conversation request failed")
);
}
let result = response
.get("result")
.cloned()
.context("conversation response omitted result")?;
print_session_mutation(&result, json)
}
async fn session_lifecycle_cmd(
verb: &str,
harness: &str,
session: Option<&str>,
surface: Option<&str>,
profile: Option<&str>,
json: bool,
) -> Result<()> {
use supercode_harness::sessions_control::{SessionDoor, SessionVerb};
let session_verb = match verb {
"new" => SessionVerb::New,
_ => SessionVerb::Reset,
};
match supercode_harness::sessions_control::door(harness, session_verb) {
Ok(SessionDoor::Daemon) => {
session_rpc_cmd(
session_verb.method(),
harness,
session.unwrap_or_default(),
surface,
None,
profile,
json,
)
.await
}
_ => {
if surface.is_some() {
anyhow::bail!(
"`--surface` names an orchestrator BINDING; `{harness}` performs \
`sessions.{verb}` in a live driven session, so pass `--session <runtime>`"
);
}
let selector = session.filter(|value| !value.trim().is_empty()).context(
"`sessions {verb}` needs the live runtime carrying the conversation: \
`--session <id>` from `supercode sessions list --live`",
)?;
live_session_command_cmd(verb, harness, selector, json).await
}
}
}
async fn live_session_command_cmd(
verb: &str,
harness: &str,
selector: &str,
json: bool,
) -> Result<()> {
use supercode_harness::sessions_control::{SessionDoor, SessionVerb};
let session_verb = match verb {
"new" => SessionVerb::New,
_ => SessionVerb::Reset,
};
let command = match supercode_harness::sessions_control::door(harness, session_verb) {
Ok(SessionDoor::Live(command)) => command,
Ok(_) => anyhow::bail!(
"`{harness}` performs `sessions.{verb}` through a door that is not a live session; \
use `supercode sessions {verb} --harness {harness}` against its own store instead"
),
Err(error) => anyhow::bail!("{error}"),
};
let registry = supercode_harness::LocalRuntimeRegistry::new();
let entries = registry
.list(
&supercode_harness::RuntimeRegistryQuery {
include_live: true,
include_persisted: false,
..supercode_harness::RuntimeRegistryQuery::default()
},
&supercode_harness::RuntimeAuthorization::observer(),
)
.await?;
let mut matches = entries
.into_iter()
.filter(|entry| entry.id == selector || entry.source_session_id == selector);
let entry = matches.next().with_context(|| {
format!(
"no live {harness} runtime matches `{selector}`; run `supercode sessions list --live`"
)
})?;
if matches.next().is_some() {
anyhow::bail!(
"live runtime selector `{selector}` is ambiguous; pass the exact runtime id from \
`supercode sessions list --live`"
);
}
let client_id = supercode_harness::RuntimeClientId::parse(format!(
"cli-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis()
))?;
let runtime = registry
.attach(
&entry.id,
client_id,
supercode_harness::RuntimeAuthorization::owner(),
)
.await?;
runtime.take_control().await?;
let send = {
use supercode_harness::FrontendRuntime;
runtime.clone().send_input(command.to_string()).await
};
let detached = runtime.detach().await;
send?;
detached?;
let result = serde_json::json!({
"harness": harness,
"verb": verb,
"ran": format!("{harness} live session: {command}"),
"session": entry.source_session_id,
});
print_session_mutation(&result, json)
}
fn print_session_mutation(result: &serde_json::Value, json: bool) -> Result<()> {
if json {
println!("{}", serde_json::to_string_pretty(result)?);
return Ok(());
}
let cell = |pointer: &str| -> String {
result
.pointer(pointer)
.and_then(serde_json::Value::as_str)
.unwrap_or("-")
.to_string()
};
println!("ran: {}", cell("/ran"));
let flag = |key: &str| {
result
.get(key)
.and_then(serde_json::Value::as_bool)
.unwrap_or(false)
};
if flag("deleted") {
println!(
"deleted: {} conversation {}",
cell("/harness"),
cell("/session")
);
return Ok(());
}
if flag("archived") {
println!(
"archived: {} conversation {}",
cell("/harness"),
cell("/session")
);
}
match result.get("row") {
Some(serde_json::Value::Null) | None => println!(
"{} conversation {} (the harness's store reports no row yet)",
cell("/harness"),
cell("/session")
),
Some(row) => {
println!(
"{} conversation {}",
cell("/harness"),
row.pointer("/locator/session_id")
.and_then(serde_json::Value::as_str)
.unwrap_or_else(|| result
.get("session")
.and_then(serde_json::Value::as_str)
.unwrap_or("-"))
);
if let Some(title) = row.get("title").and_then(serde_json::Value::as_str) {
println!("title: {title}");
}
if let Some(cwd) = row.get("cwd").and_then(serde_json::Value::as_str) {
println!("cwd: {cwd}");
}
}
}
Ok(())
}
async fn live_sessions_cmd(json: bool) -> Result<()> {
let registry = supercode_harness::LocalRuntimeRegistry::new();
let query = supercode_harness::RuntimeRegistryQuery {
include_live: true,
include_persisted: false,
..supercode_harness::RuntimeRegistryQuery::default()
};
let entries = registry
.list(&query, &supercode_harness::RuntimeAuthorization::observer())
.await?;
if json {
println!("{}", serde_json::to_string_pretty(&entries)?);
return Ok(());
}
if entries.is_empty() {
println!("no live supercode runtimes");
return Ok(());
}
println!(
"{:<24} {:<24} {:<10} {:<18} {:<14} {:<13}",
"RUNTIME", "TMUX", "STATE", "MODEL", "SOURCE", "STARTED"
);
for entry in entries {
let tmux = match &entry.supervisor {
Some(supercode_harness::LiveRuntimeSupervisor::Tmux { session_name }) => {
session_name.as_str()
}
None => "-",
};
let started = entry
.started_at_ms
.map(|value| value.to_string())
.unwrap_or_else(|| "-".into());
println!(
"{:<24} {:<24} {:<10} {:<18} {:<14} {:<13}",
truncate_path(&entry.id, 24),
truncate_path(tmux, 24),
format!("{:?}", entry.state).to_ascii_lowercase(),
truncate_path(entry.model.as_deref().unwrap_or("-"), 18),
truncate_path(&entry.source_harness, 14),
truncate_path(&started, 13),
);
println!(" open: supercode open {}", entry.id);
if tmux != "-" {
println!(" terminal: tmux attach-session -t {tmux}");
}
println!(" close: supercode sessions close {}", entry.id);
}
Ok(())
}
async fn attach_runtime_client_cmd(
cli: &Cli,
selector: &str,
client: frontend_client::FrontendClientId,
observe: bool,
take_control: bool,
remote_instructions: bool,
) -> Result<()> {
let registry = supercode_harness::LocalRuntimeRegistry::new();
let query = supercode_harness::RuntimeRegistryQuery {
include_live: true,
include_persisted: false,
..supercode_harness::RuntimeRegistryQuery::default()
};
let entries = registry
.list(&query, &supercode_harness::RuntimeAuthorization::observer())
.await?;
let mut matches = entries
.into_iter()
.filter(|entry| entry.id == selector || entry.source_session_id == selector);
let entry = matches.next().with_context(|| {
format!("no live runtime matches `{selector}`; run `supercode sessions list --live`")
})?;
if matches.next().is_some() {
anyhow::bail!(
"live runtime selector `{selector}` is ambiguous; pass the exact runtime id from `supercode sessions list --live`"
);
}
let spec = client.spec();
let probe = spec.probe().await;
eprintln!(
"frontend availability: id={} installed={} compatible={} version={} detail={}",
probe.id.as_str(),
probe.installed,
probe.compatible,
probe.version.as_deref().unwrap_or("-"),
probe.detail
);
if !probe.compatible {
anyhow::bail!(
"frontend `{}` is unavailable: {}",
client.as_str(),
probe.detail
);
}
let client_id = supercode_harness::RuntimeClientId::parse(format!(
"ui-{}-{}-{}",
client.as_str(),
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis()
))?;
let credential_client_id = client_id.clone();
let runtime = registry
.attach(
&entry.id,
client_id,
if observe {
supercode_harness::RuntimeAuthorization::observer()
} else {
supercode_harness::RuntimeAuthorization::interactive()
},
)
.await?;
if take_control && client != frontend_client::FrontendClientId::Pi {
runtime.take_control().await?;
}
eprintln!(
"attached frontend={} transport={} runtime={} source={}:{}",
client.as_str(),
spec.transports[0].as_str(),
entry.id,
entry.source_harness,
entry.source_session_id
);
eprintln!(
"another terminal (observer): supercode open {} --observe",
entry.id
);
eprintln!(
"explicit takeover: supercode open {} --take-control",
entry.id
);
let embedded_config = if client == frontend_client::FrontendClientId::Embedded {
Some(resolve_config(cli)?)
} else {
None
};
let mut pi_credential = if client == frontend_client::FrontendClientId::Pi {
match mint_attach_credential(&entry, observe, Some(credential_client_id), false).await {
Ok(credential) => Some(credential),
Err(error) => {
let _ = runtime.detach().await;
return Err(error);
}
}
} else {
None
};
let mut remote_credential = if remote_instructions {
match mint_attach_credential(&entry, observe, None, true).await {
Ok(credential) => Some(credential),
Err(error) => {
if let Some(credential) = &mut pi_credential {
let _ = credential.revoke().await;
}
let _ = runtime.detach().await;
return Err(error);
}
}
} else {
None
};
let result = match client {
frontend_client::FrontendClientId::Embedded => {
let config = embedded_config.expect("embedded frontend resolved its config");
if tui::should_activate(&config) {
supercode_frontend_tui::app::run(runtime.clone())
.await
.map(|_| ())
.map_err(Into::into)
} else {
supercode_frontend_tui::app::run_line(runtime.clone())
.await
.map(|_| ())
.map_err(Into::into)
}
}
frontend_client::FrontendClientId::Codex => {
let cwd = entry
.source_workspace
.as_deref()
.unwrap_or_else(|| Path::new("."));
run_stock_codex_frontend(
runtime.clone(),
&entry.id,
cwd,
FrontendCompatibilityArg::TracedOnly,
effective_quiet(cli),
Some(runtime.clone()),
)
.await
.map(|_| ())
}
frontend_client::FrontendClientId::Opencode => {
let cwd = entry
.source_workspace
.as_deref()
.unwrap_or_else(|| Path::new("."));
run_stock_opencode_frontend(
runtime.clone(),
&entry.id,
cwd,
effective_quiet(cli),
Some(runtime.clone()),
)
.await
.map(|_| ())
}
frontend_client::FrontendClientId::Goose => {
let cwd = entry
.source_workspace
.as_deref()
.unwrap_or_else(|| Path::new("."));
run_stock_goose_frontend(
runtime.clone(),
cwd,
effective_quiet(cli),
Some(runtime.clone()),
)
.await
.map(|_| ())
}
frontend_client::FrontendClientId::Pi => {
let cwd = entry
.source_workspace
.as_deref()
.unwrap_or_else(|| Path::new("."));
run_pi_frontend(
cwd,
pi_credential
.as_ref()
.expect("Pi frontend minted its scoped credential"),
observe,
take_control,
effective_quiet(cli),
Some(runtime.clone()),
)
.await
}
};
let detach = if !runtime.is_disconnected() {
runtime.detach().await.map(|_| ())
} else {
Ok(())
};
let pi_revoke = if let Some(credential) = &mut pi_credential {
credential.revoke().await
} else {
Ok(())
};
let remote_revoke = if let Some(credential) = &mut remote_credential {
credential.revoke().await
} else {
Ok(())
};
detach?;
pi_revoke?;
remote_revoke?;
result
}
struct RemoteAttachCredential {
base_url: String,
bootstrap: String,
client_id: supercode_harness::RuntimeClientId,
root: EphemeralFrontendRoot,
revoked: bool,
}
impl RemoteAttachCredential {
fn base_url(&self) -> &str {
&self.base_url
}
fn client_id(&self) -> &supercode_harness::RuntimeClientId {
&self.client_id
}
fn credential_path(&self) -> PathBuf {
self.root.0.join("credential")
}
fn root(&self) -> &Path {
&self.root.0
}
async fn revoke(&mut self) -> Result<()> {
if self.revoked {
return Ok(());
}
let result =
revoke_remote_frontend_credential(&self.base_url, &self.bootstrap, &self.client_id)
.await;
self.revoked = result.is_ok();
self.bootstrap.clear();
std::fs::remove_dir_all(&self.root.0).ok();
result
}
}
struct RemoteAttachSecret(Vec<u8>);
impl Drop for RemoteAttachSecret {
fn drop(&mut self) {
self.0.fill(0);
}
}
async fn mint_attach_credential(
entry: &supercode_harness::RuntimeRegistryEntry,
observe: bool,
client_id: Option<supercode_harness::RuntimeClientId>,
print_remote_instructions: bool,
) -> Result<RemoteAttachCredential> {
if print_remote_instructions && !cfg!(unix) {
anyhow::bail!("remote attach credential delivery currently requires a Unix runtime host");
}
let record = supercode_harness::find_live_runtime(&entry.id)?
.with_context(|| format!("live runtime `{}` disappeared", entry.id))?;
let resolved = supercode_harness::resolve_live_runtime(&record.endpoint, &record.source)?;
let port = if print_remote_instructions {
let url = url::Url::parse(&resolved.base_url).context("parsing local runtime endpoint")?;
Some(
url.port_or_known_default()
.context("local runtime endpoint has no TCP port")?,
)
} else {
None
};
let client_id = match client_id {
Some(client_id) => client_id,
None => supercode_harness::RuntimeClientId::parse(format!(
"remote-{}-{:x}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos()
))?,
};
#[cfg(unix)]
let frontend_temp_root = PathBuf::from("/tmp");
#[cfg(not(unix))]
let frontend_temp_root = std::env::temp_dir();
let root = frontend_temp_root.join(format!(
"sc-remote-{}-{:x}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos()
));
create_private_frontend_dir(&root)?;
let root = EphemeralFrontendRoot(root);
let response = reqwest::Client::new()
.post(format!(
"{}/_supercode/frontend-credentials/mint",
resolved.base_url
))
.bearer_auth(&resolved.token)
.json(&serde_json::json!({
"clientId":client_id.as_str(),
"grant":if observe { "observer" } else { "interactive" }
}))
.send()
.await
.context("minting remote frontend credential")?;
if !response.status().is_success() {
anyhow::bail!(
"remote frontend credential mint returned {}",
response.status()
);
}
let secret = match response.bytes().await {
Ok(secret) => RemoteAttachSecret(secret.to_vec()),
Err(error) => {
let _ =
revoke_remote_frontend_credential(&resolved.base_url, &resolved.token, &client_id)
.await;
return Err(error).context("reading minted remote frontend credential");
}
};
if secret.0.len() != 64
|| !secret
.0
.iter()
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(byte))
{
let _ = revoke_remote_frontend_credential(&resolved.base_url, &resolved.token, &client_id)
.await;
anyhow::bail!("runtime returned a malformed remote frontend credential");
}
let credential_path = root.0.join("credential");
if let Err(error) = write_private_frontend_file(&credential_path, &secret.0) {
let _ = revoke_remote_frontend_credential(&resolved.base_url, &resolved.token, &client_id)
.await;
return Err(error).context("writing private remote frontend credential");
}
if let Some(port) = port {
eprintln!(
"remote attachment is opt-in and operator-created; Volter Harness will not open a tunnel"
);
eprintln!(
"1. in client terminal A: ssh -N -L 127.0.0.1:LOCAL_PORT:127.0.0.1:{port} USER@RUNTIME_HOST"
);
eprintln!(
"2. in client terminal B (bash/zsh): exec 3< <(ssh USER@RUNTIME_HOST 'cat {}')",
credential_path.display()
);
eprintln!(
"3. supercode open http://127.0.0.1:LOCAL_PORT --credential-fd 3 --client-id {}",
client_id.as_str()
);
eprintln!(
"credential lifecycle: keep this host attach running; its scoped {} grant is revoked and private file deleted when it exits",
if observe { "observer" } else { "interactive" }
);
}
Ok(RemoteAttachCredential {
base_url: resolved.base_url,
bootstrap: resolved.token,
client_id,
root,
revoked: false,
})
}
async fn revoke_remote_frontend_credential(
base_url: &str,
bootstrap: &str,
client_id: &supercode_harness::RuntimeClientId,
) -> Result<()> {
let response = reqwest::Client::new()
.post(format!("{base_url}/_supercode/frontend-credentials/revoke"))
.bearer_auth(bootstrap)
.json(&serde_json::json!({"clientId":client_id.as_str()}))
.send()
.await
.context("revoking remote frontend credential")?;
if !response.status().is_success() {
anyhow::bail!(
"remote frontend credential revoke returned {}",
response.status()
);
}
Ok(())
}
async fn live_session_close_cmd(runtime_id: &str) -> Result<()> {
let client_id = supercode_harness::RuntimeClientId::parse(format!(
"close-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis()
))?;
let runtime = supercode_harness::LocalRuntimeRegistry::new()
.attach(
runtime_id,
client_id,
supercode_harness::RuntimeAuthorization::owner(),
)
.await?;
runtime.close().await?;
println!("closed runtime {runtime_id}; persisted session data was retained");
Ok(())
}
fn short_id(name: &str) -> String {
name.rsplit('-').next().unwrap_or(name).to_string()
}
fn checkpoint_cmd(cli: &Cli, action: &CheckpointAction) -> Result<()> {
let config = resolve_config(cli)?;
if !config.checkpoint_enabled {
anyhow::bail!(
"`checkpoint` requires [capabilities.checkpoint] enabled = true in your project \
(.supercode.toml) or user config, or a preset that turns it on (e.g. \
`extends = \"cc-parity\"` or `extends = \"oc-parity\"`, both with \
[experimental] module_registry = true). See `supercode doctor` for where config \
is loaded from."
);
}
let cp = supercode_harness::checkpoint::observer_for_config(&config).ok_or_else(|| {
anyhow::anyhow!(
"checkpoint is enabled but its shadow store could not be opened (see the warning \
printed above)"
)
})?;
match action {
CheckpointAction::List { json } => {
let checkpoints = cp.list()?;
if *json {
let out: Vec<serde_json::Value> = checkpoints
.iter()
.map(|c| {
serde_json::json!({
"id": c.id,
"created_at_ms": c.created_at_ms,
"label": c.label,
"file_count": c.file_count,
})
})
.collect();
println!("{}", serde_json::to_string_pretty(&out)?);
} else if checkpoints.is_empty() {
println!("no checkpoints yet");
} else {
for c in &checkpoints {
println!(
"{} {:>3} file(s) {}",
short_checkpoint_id(&c.id),
c.file_count,
c.label
);
}
}
}
CheckpointAction::Diff { checkpoint, patch } => {
let id = resolve_checkpoint_id(&cp, checkpoint)?;
if *patch {
let text = cp.turn_patch(&id)?;
if text.is_empty() {
println!("(no changes this turn)");
} else {
print!("{text}");
}
} else {
let mut files = cp.turn_diff(&id)?;
files.sort();
if files.is_empty() {
println!("(no files touched this turn)");
} else {
for f in &files {
println!("{f}");
}
}
}
}
CheckpointAction::Restore { checkpoint } => {
let id = resolve_checkpoint_id(&cp, checkpoint)?;
let report = cp.restore(&id)?;
for f in &report.restored {
eprintln!("restored {f}");
}
for (f, reason) in &report.refused {
eprintln!("refused {f}: {reason}");
}
if !report.refused.is_empty() {
anyhow::bail!(
"{} of {} file(s) could not be restored — see the `refused` lines above",
report.refused.len(),
report.refused.len() + report.restored.len()
);
}
println!(
"restored {} file(s) to checkpoint {}",
report.restored.len(),
short_checkpoint_id(&id)
);
}
}
Ok(())
}
fn resolve_checkpoint_id(
cp: &supercode_harness::checkpoint::CheckpointObserver,
query: &str,
) -> Result<String> {
let all = cp.list()?;
if all.iter().any(|c| c.id == query) {
return Ok(query.to_string());
}
let matches: Vec<&supercode_harness::checkpoint::CheckpointMeta> =
all.iter().filter(|c| c.id.starts_with(query)).collect();
match matches.as_slice() {
[one] => Ok(one.id.clone()),
[] => anyhow::bail!("no checkpoint matching `{query}`"),
many => anyhow::bail!("`{query}` is ambiguous ({} matches)", many.len()),
}
}
fn short_checkpoint_id(id: &str) -> &str {
&id[..id.len().min(12)]
}
fn model_cmd(action: &ModelAction) -> Result<()> {
match action {
ModelAction::List { json } => {
let table = uc::alias_table();
if *json {
let out: Vec<serde_json::Value> = table
.iter()
.map(|(alias, slug)| serde_json::json!({"alias": alias, "slug": slug}))
.collect();
println!("{}", serde_json::to_string_pretty(&out)?);
return Ok(());
}
println!();
println!("{}", ui::section(&format!("models ({})", table.len())));
for (alias, slug) in table {
println!(
" {} {}",
ui::paint(ui::ACCENT, format!("{:<16}", alias)),
ui::paint(ui::MUTED, *slug)
);
}
println!();
Ok(())
}
}
}
fn model_picker_candidates() -> Vec<(String, picker::PickerItem)> {
uc::alias_table()
.iter()
.map(|(alias, slug)| ((*alias).to_string(), picker::PickerItem::new(*alias, *slug)))
.collect()
}
fn provider_of(slug: &str) -> &str {
slug.split('/').next().unwrap_or(slug)
}
fn provider_cmd(action: &ProviderAction) -> Result<()> {
match action {
ProviderAction::List { json } => {
let table = uc::alias_table();
let mut providers: Vec<(&str, Vec<&str>)> = Vec::new();
for (alias, slug) in table {
let p = provider_of(slug);
match providers.iter_mut().find(|(name, _)| *name == p) {
Some(entry) => entry.1.push(alias),
None => providers.push((p, vec![alias])),
}
}
if *json {
let out: Vec<serde_json::Value> = providers
.iter()
.map(|(name, aliases)| {
serde_json::json!({
"name": name,
"model_count": aliases.len(),
"aliases": aliases,
})
})
.collect();
println!("{}", serde_json::to_string_pretty(&out)?);
return Ok(());
}
println!();
println!(
"{}",
ui::section(&format!("providers ({})", providers.len()))
);
for (name, aliases) in &providers {
println!(
" {} {}",
ui::paint(ui::ACCENT, format!("{:<14}", name)),
ui::paint(ui::MUTED, aliases.join(", "))
);
}
println!();
Ok(())
}
}
}
fn pct_of(view: u64, full: u64) -> i64 {
if full == 0 {
0
} else {
((view as f64 / full as f64) * 100.0).round() as i64
}
}
const PROACTIVE_REDUCTION_TARGET_PERCENT: u64 = 35;
const PROACTIVE_REDUCTION_MIN_INPUT_TOKENS: u64 = 32_000;
fn proactive_reduction_target(baseline_input_tokens: u64, replay_message_tokens: u64) -> u64 {
if replay_message_tokens < PROACTIVE_REDUCTION_MIN_INPUT_TOKENS {
baseline_input_tokens
} else {
baseline_input_tokens
.saturating_mul(PROACTIVE_REDUCTION_TARGET_PERCENT)
.div_ceil(100)
}
}
fn reapply_only_policy() -> ReductionPolicy {
ReductionPolicy {
tool_output_trigger_bytes: usize::MAX,
elide_stale_reads: false,
redact_images: false,
clear_turns_older_than: None,
elide_tool_inputs: false,
diff_rereads: false,
duplicate_output_min_bytes: usize::MAX,
normalize_terminal_output: false,
supersede_enabled: false,
prune_errored_inputs: false,
summarize_cleared_turns: false,
..Default::default()
}
}
fn record_bytes(r: &reduce::Reduction, sidecar: &Session) -> usize {
match &r.kind {
reduce::ReductionKind::ToolOutputTruncated { original_bytes, .. } => *original_bytes,
reduce::ReductionKind::OutputNormalized { original_bytes, .. } => *original_bytes,
reduce::ReductionKind::FileReadDiffed { original_bytes, .. } => *original_bytes,
reduce::ReductionKind::TurnsCleared { first, last, .. } => sidecar
.messages
.get(*first..=*last)
.map(|slice| {
slice
.iter()
.map(|m| serde_json::to_string(m).map(|s| s.len()).unwrap_or(0))
.sum()
})
.unwrap_or(0),
reduce::ReductionKind::FileReadElided { .. } => sidecar
.messages
.get(r.ptr.addr.index)
.and_then(|m| m.content.as_deref())
.map(str::len)
.unwrap_or(0),
reduce::ReductionKind::ImageRedacted { part_index } => sidecar
.messages
.get(r.ptr.addr.index)
.and_then(|m| m.content_parts.as_ref())
.and_then(|parts| parts.get(*part_index))
.and_then(|p| serde_json::to_string(p).ok())
.map(|s| s.len())
.unwrap_or(0),
reduce::ReductionKind::ToolInputElided { original_bytes, .. } => *original_bytes,
reduce::ReductionKind::DuplicateOutput { original_bytes, .. } => *original_bytes,
reduce::ReductionKind::Superseded { original_bytes, .. } => *original_bytes,
}
}
fn load_reduction_log_checked(store: &SessionStore, name: &str) -> Result<ReductionLog> {
if let Some(log) = store.load_reduction_log(name)? {
return Ok(log);
}
let has_persisted_stubs = store
.load_if_present(name)?
.is_some_and(|view| view.contains(reduce::REDUCTION_SENTINEL));
if has_persisted_stubs {
anyhow::bail!(
"`{name}` has reduction stubs in its persisted working view, but its reduction log \
is missing — cannot safely interpret this reduced session; restore \
`{name}.reduction.json` or recreate the reduced session from its full sidecar/export"
);
}
Ok(ReductionLog::default())
}
fn load_reduction_log_checked_from(
store: &SessionStore,
name: &str,
archived: bool,
) -> Result<ReductionLog> {
if let Some(log) = store.load_reduction_log_from(name, archived)? {
return Ok(log);
}
let has_persisted_stubs = store
.load_if_present_from(name, archived)?
.is_some_and(|view| view.contains(reduce::REDUCTION_SENTINEL));
if has_persisted_stubs {
anyhow::bail!(
"`{name}` has reduction stubs in its persisted working view, but its reduction log \
is missing from the selected {} family — cannot safely interpret this reduced \
session",
if archived { "archived" } else { "active" }
);
}
Ok(ReductionLog::default())
}
fn show_reductions(store: &SessionStore, query: &str, json: bool) -> Result<()> {
let name = resolve_session(store, query)?;
let Some(sidecar_jsonl) = store.load_sidecar(&name)? else {
println!("not a reduced session — nothing to show");
return Ok(());
};
let sidecar = Session::from_sidecar_str(&sidecar_jsonl)
.with_context(|| format!("parsing sidecar for `{name}`"))?;
let log = load_reduction_log_checked(store, &name)?;
reduce::verify_log(&log, &sidecar).map_err(|e| {
anyhow::anyhow!("{e} — the session's transparency invariant is broken (C2 contract 3)")
})?;
if json {
println!("{}", serde_json::to_string_pretty(&log)?);
return Ok(());
}
let (view_msgs, _) = reduce::project(&sidecar, &reapply_only_policy(), &log);
let full_tok = supercode_runtime::estimate_view_tokens(&sidecar.messages);
let view_tok = supercode_runtime::estimate_view_tokens(&view_msgs);
let pct = pct_of(view_tok, full_tok);
println!();
println!(
"{}",
ui::section(&format!(
"reductions · {name} ({} records · view {} · full {} · {pct}%)",
log.reductions.len(),
supercode_runtime::fmt_approx_tokens(view_tok),
supercode_runtime::fmt_approx_tokens(full_tok),
))
);
println!();
for r in &log.reductions {
let kind_str = reduce::stub::Kind::from(&r.kind).as_str();
let summary = reduce::stub::parse(&r.placeholder)
.map(|(_, _, s)| s)
.unwrap_or_default();
let bytes = record_bytes(r, &sidecar);
println!(
" {} {:<14} {:>10} B {}",
ui::paint(ui::ACCENT, format!("{:<11}", r.id)),
kind_str,
commas(bytes as u64),
truncate(&summary, 60),
);
}
println!();
Ok(())
}
fn handoff_cmd(
cli: &Cli,
query: &str,
keep: &[String],
objective: Option<&str>,
keep_last: usize,
draft_objective: bool,
json: bool,
) -> Result<()> {
let config = resolve_config(cli)?;
if !config.handoff_enabled {
let controlling_gate =
if config.module_registry && !config.module_activation.is_active(ModuleId::Reduction) {
"`[capabilities.reduction] enabled = false`"
} else {
"`[capabilities.reduction] handoff = false`"
};
anyhow::bail!(
"handoff is disabled by {controlling_gate}; remove that narrowing override or set it \
to true"
);
}
let store = session_store()?;
let name = resolve_session(&store, query)?;
let Some(sidecar_jsonl) = store.load_sidecar(&name)? else {
anyhow::bail!(
"`{name}` has no sidecar recorded — handoff needs a reduced session; \
run `supercode resume --reduced` (or `chat --reduced`) on it first"
);
};
let sidecar = Session::from_sidecar_str(&sidecar_jsonl)
.with_context(|| format!("parsing sidecar for `{name}`"))?;
let prior_log = load_reduction_log_checked(&store, &name)?;
reduce::verify_log(&prior_log, &sidecar).map_err(|e| {
anyhow::anyhow!("{e} — the session's transparency invariant is broken (C2 contract 3)")
})?;
let result = reduce::handoff::build_handoff(
&sidecar.messages,
keep,
keep_last,
objective,
draft_objective,
None,
)
.context("building handoff projection")?;
let full_tok = supercode_runtime::estimate_view_tokens(&sidecar.messages);
let view_tok = supercode_runtime::estimate_view_tokens(&result.view);
let pct = pct_of(view_tok, full_tok);
store
.save_reduction_log(&name, &result.log)
.context("persisting the handoff reduction log")?;
let jsonl: String = result
.view
.iter()
.filter_map(|m| serde_json::to_string(m).ok())
.collect::<Vec<_>>()
.join("\n");
let title = store
.list()
.into_iter()
.find(|s| s.name == name)
.map(|s| s.title)
.filter(|t| !t.is_empty())
.unwrap_or_else(|| format!("{name} (handoff)"));
store
.save(&name, &title, &jsonl)
.context("persisting the handoff view")?;
let _ = store.set_reduction_stats(
&name,
&title,
serialized_bytes(&sidecar.messages),
serialized_bytes(&result.view),
result.log.reductions.len() as u32,
);
if json {
#[derive(serde::Serialize)]
struct HandoffJson<'a> {
session: &'a str,
kept_indices: &'a [usize],
gap_ids: &'a [String],
objective_source: &'static str,
objective: Option<&'a str>,
full_tokens: u64,
view_tokens: u64,
pct: i64,
reductions: usize,
}
let objective_source = match result.objective_source {
reduce::handoff::ObjectiveSource::UserSupplied => "user-supplied",
reduce::handoff::ObjectiveSource::Drafted => "drafted",
reduce::handoff::ObjectiveSource::Fallback => "fallback",
};
println!(
"{}",
serde_json::to_string_pretty(&HandoffJson {
session: &name,
kept_indices: &result.kept_indices,
gap_ids: &result.gap_ids,
objective_source,
objective: result.objective_text.as_deref(),
full_tokens: full_tok,
view_tokens: view_tok,
pct,
reductions: result.log.reductions.len(),
})?
);
return Ok(());
}
println!();
println!(
"{}",
ui::section(&format!(
"handoff · {name} ({} kept · {} spanning reduction(s) · {} → {} ({pct}%))",
result.kept_indices.len(),
result.gap_ids.len(),
supercode_runtime::fmt_approx_tokens(full_tok),
supercode_runtime::fmt_approx_tokens(view_tok),
))
);
let objective_note = match result.objective_source {
reduce::handoff::ObjectiveSource::UserSupplied => "objective: user-supplied",
reduce::handoff::ObjectiveSource::Drafted => "objective: drafted",
reduce::handoff::ObjectiveSource::Fallback => "objective: none (plain banner)",
};
println!(" {}", ui::paint(ui::DIM, objective_note));
println!(
" {}",
ui::paint(
ui::DIM,
format!("resume with: `supercode resume {name}` — exact named handoff, full sidecar")
)
);
println!();
Ok(())
}
const DEFAULT_UPDATE_CHECK_URL: &str = "https://registry.npmjs.org/@volter/supercode/latest";
const NPM_UPGRADE: &str = "npm install -g @volter/supercode@latest";
fn update_check_url(channel: UpdateChannelArg) -> String {
if let Ok(url) = std::env::var("SUPERCODE_UPDATE_CHECK_URL") {
if !url.is_empty() {
return url;
}
}
match channel {
UpdateChannelArg::Stable => DEFAULT_UPDATE_CHECK_URL.to_string(),
UpdateChannelArg::Latest => {
"https://registry.npmjs.org/-/package/@volter/supercode/dist-tags".to_string()
}
}
}
fn install_command(label: &str) -> Option<&'static str> {
match label {
"npm" => Some(NPM_UPGRADE),
"Homebrew" => Some("brew upgrade supercode"),
"cargo" => Some("cargo install supercode-cli"),
"from source" => Some("git pull && cargo install --path crates/cli"),
_ => None,
}
}
fn run_install_command(cmd: &str) -> Result<()> {
#[cfg(windows)]
let mut command = {
let mut c = std::process::Command::new("cmd");
c.arg("/C").arg(cmd);
c
};
#[cfg(not(windows))]
let mut command = {
let mut c = std::process::Command::new("sh");
c.arg("-c").arg(cmd);
c
};
let status = command
.status()
.with_context(|| format!("running `{cmd}`"))?;
if !status.success() {
anyhow::bail!("`{cmd}` failed with status {status}");
}
Ok(())
}
async fn startup_update_check(cli: &Cli) {
if cli.bare
|| effective_quiet(cli)
|| matches!(cli.command, Command::Update { .. })
|| !io::stderr().is_terminal()
{
return;
}
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let fc = uc::load(&cwd);
if fc.core.update_check != Some(true) {
return;
}
let url = update_check_url(UpdateChannelArg::Stable);
let Ok(body) = fetch_update_check_body(&url).await else {
return;
};
let Some(tag) = parse_release_tag(&body) else {
return;
};
if let VersionCheck::Newer(latest) = compare_versions(env!("CARGO_PKG_VERSION"), &tag) {
eprintln!(
"{} supercode v{latest} is available (you have v{}) — run `supercode update --install`",
ui::pill(ui::WARN, "UPDATE"),
env!("CARGO_PKG_VERSION"),
);
}
}
fn detect_install_method() -> (&'static str, &'static str) {
let exe = std::env::current_exe().ok();
let path = exe.as_deref().and_then(|p| p.to_str()).unwrap_or("");
if path.contains("node_modules") {
("npm", NPM_UPGRADE)
} else if path.contains("Cellar") || path.contains("/homebrew/") || path.contains("Homebrew") {
("Homebrew", "brew upgrade supercode")
} else if path.contains("/.cargo/") || path.contains("\\.cargo\\") {
("cargo", "cargo install supercode-cli")
} else if path.contains("/target/") {
("from source", "git pull && cargo install --path crates/cli")
} else {
(
"install script",
"download https://github.com/volter-ai/supercode/releases/latest, or run scripts/install.sh from a checkout",
)
}
}
fn parse_version(s: &str) -> Option<(u64, u64, u64)> {
let s = s.trim().trim_start_matches(['v', 'V']);
let mut parts = s.split('.');
let major = parts.next()?.parse().ok()?;
let minor = parts.next().map(|p| p.parse().unwrap_or(0)).unwrap_or(0);
let patch = parts
.next()
.map(|p| {
let digits: String = p.chars().take_while(|c| c.is_ascii_digit()).collect();
digits.parse().unwrap_or(0)
})
.unwrap_or(0);
Some((major, minor, patch))
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum VersionCheck {
Newer(String),
UpToDate,
Malformed(String),
}
fn compare_versions(current: &str, latest_tag: &str) -> VersionCheck {
match (parse_version(current), parse_version(latest_tag)) {
(Some(cur), Some(lat)) if lat > cur => {
VersionCheck::Newer(latest_tag.trim_start_matches(['v', 'V']).to_string())
}
(Some(_), Some(_)) => VersionCheck::UpToDate,
_ => VersionCheck::Malformed(latest_tag.to_string()),
}
}
fn parse_release_tag(body: &str) -> Option<String> {
let v: serde_json::Value = serde_json::from_str(body).ok()?;
let object = match &v {
serde_json::Value::Array(items) => items.first()?,
other => other,
};
if let Some(tag) = object.get("tag_name").or_else(|| object.get("version")) {
return tag.as_str().map(str::to_string);
}
object
.as_object()?
.values()
.filter_map(|tag| tag.as_str())
.filter(|tag| parse_version(tag).is_some())
.max_by_key(|tag| parse_version(tag))
.map(str::to_string)
}
async fn fetch_update_check_body(url: &str) -> Result<String, String> {
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(5))
.build()
.map_err(|e| format!("client init failed: {e}"))?;
let resp = client
.get(url)
.header(
reqwest::header::USER_AGENT,
format!("supercode-cli/{}", env!("CARGO_PKG_VERSION")),
)
.header(reqwest::header::ACCEPT, "application/json")
.send()
.await
.map_err(|e| format!("network error: {}", short_err(&e)))?;
let status = resp.status();
if !status.is_success() {
return Err(format!("HTTP {status}"));
}
resp.text()
.await
.map_err(|e| format!("read error: {}", short_err(&e)))
}
async fn update_cmd(install: bool, channel: UpdateChannelArg) -> Result<()> {
println!();
println!(
" {} {} {} {}",
ui::logo_mark(),
ui::bold(ui::ACCENT, "Volter Harness"),
ui::paint(ui::DIM, format!("v{}", env!("CARGO_PKG_VERSION"))),
ui::paint(ui::MUTED, format!("channel: {}", channel.label())),
);
println!();
let (recommended_label, recommended_cmd) = detect_install_method();
println!(
"{}",
ui::paint(ui::MUTED, " Upgrade with whichever you installed:")
);
let row = |label: &str, cmd: &str| {
let marker = if label == recommended_label {
format!(" {}", ui::paint(ui::OK, "← detected"))
} else {
String::new()
};
println!(
" {} {:<16} {}{}",
ui::paint(ui::ACCENT, "›"),
ui::paint(ui::MUTED, label),
ui::paint(ui::TEAL, cmd),
marker
);
};
row("npm", NPM_UPGRADE);
row(
"install script",
"download https://github.com/volter-ai/supercode/releases/latest, or run scripts/install.sh from a checkout",
);
row("Homebrew", "brew upgrade supercode");
row("cargo", "cargo install supercode-cli");
row("from source", "git pull && cargo install --path crates/cli");
println!();
let check_url = update_check_url(channel);
match fetch_update_check_body(&check_url).await {
Ok(body) => match parse_release_tag(&body) {
Some(tag) => match compare_versions(env!("CARGO_PKG_VERSION"), &tag) {
VersionCheck::Newer(latest) => {
println!(
" {} a newer version {} is available (you have v{})",
ui::pill(ui::WARN, "UPDATE"),
ui::bold(ui::ACCENT, format!("v{latest}")),
env!("CARGO_PKG_VERSION"),
);
println!(
" {} {}",
ui::paint(ui::MUTED, format!("{recommended_label} →")),
ui::paint(ui::TEAL, recommended_cmd)
);
}
VersionCheck::UpToDate => {
println!(
"{}",
ui::kv_status(
true,
"version",
ui::paint(
ui::OK,
format!("up to date (v{})", env!("CARGO_PKG_VERSION"))
)
)
);
}
VersionCheck::Malformed(raw) => {
println!(
"{}",
ui::paint(
ui::DIM,
format!(
" couldn't check for updates (unexpected release tag {raw:?}) \
— see upgrade commands above"
)
)
);
}
},
None => {
println!(
"{}",
ui::paint(
ui::DIM,
" couldn't check for updates (no release info found) — \
see upgrade commands above"
)
);
}
},
Err(msg) => {
println!(
"{}",
ui::paint(
ui::DIM,
format!(" couldn't check for updates ({msg}) — see upgrade commands above")
)
);
}
}
if install {
let Some(cmd) = install_command(recommended_label) else {
println!();
anyhow::bail!(
"no self-upgrade path for the detected install method ({recommended_label}): \
{recommended_cmd}"
);
};
println!();
println!(
" {} {}",
ui::pill(ui::ACCENT, "INSTALL"),
ui::paint(ui::TEAL, cmd)
);
run_install_command(cmd)?;
println!(
" {} upgraded via {recommended_label}; run `supercode --version` to confirm",
ui::paint(ui::OK, "✓")
);
}
println!();
Ok(())
}
async fn mcp_cmd(cli: &Cli, action: &McpAction) -> Result<()> {
match action {
McpAction::Serve { sdk_readonly } => {
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let mut registry = ToolRegistry::with_builtins();
if *sdk_readonly {
supercode_harness::mcp::register_sdk_tool(&mut registry);
}
let mut ctx = ToolContext::new(cwd);
ctx.sandbox = if cli.dangerous {
SandboxPolicy::DangerFullAccess
} else {
cli.sandbox
.map(Into::into)
.unwrap_or(SandboxPolicy::WorkspaceWrite)
};
supercode_harness::mcp::serve_stdio(®istry, &ctx).await?;
Ok(())
}
McpAction::Add {
name,
env,
command,
args,
} => {
let mut env_map = std::collections::BTreeMap::new();
for kv in env {
let (k, v) = kv
.split_once('=')
.ok_or_else(|| anyhow::anyhow!("--env `{kv}` is not `KEY=VALUE`"))?;
env_map.insert(k.to_string(), v.to_string());
}
let mut reg = uc::load_mcp();
reg.servers.insert(
name.clone(),
uc::McpServerDef {
command: Some(command.clone()),
args: args.clone(),
env: if env_map.is_empty() {
None
} else {
Some(env_map)
},
..Default::default()
},
);
uc::save_mcp(®)?;
eprintln!(
"added MCP server `{name}` → {} in {}",
command,
uc::mcp_file().display()
);
Ok(())
}
McpAction::List => {
let reg = uc::load_mcp();
if reg.servers.is_empty() {
println!("no MCP servers registered. Add one: supercode mcp add <name> <command> [args...]");
}
for (name, def) in ®.servers {
let env_suffix = match &def.env {
Some(e) if !e.is_empty() => {
let keys: Vec<&str> = e.keys().map(String::as_str).collect();
format!(" env: {}", keys.join(","))
}
_ => String::new(),
};
let addr = def
.command
.clone()
.or_else(|| def.url.clone())
.unwrap_or_else(|| "<unconfigured>".to_string());
let transport = def.transport.as_deref().unwrap_or("stdio");
println!(
"{name:<16} [{transport}] {} {}{env_suffix}",
addr,
def.args.join(" ")
);
}
Ok(())
}
McpAction::Remove { name } => {
let mut reg = uc::load_mcp();
if reg.servers.remove(name).is_some() {
uc::save_mcp(®)?;
eprintln!("removed MCP server `{name}`");
} else {
anyhow::bail!("no MCP server named `{name}`");
}
Ok(())
}
McpAction::Import { from, dry_run } => mcp_import_cmd(*from, *dry_run),
McpAction::Login { name } => mcp_login_cmd(name).await,
McpAction::Logout { name } => {
uc::remove_mcp_oauth_tokens(name)?;
eprintln!("removed stored OAuth tokens for `{name}` (if any)");
Ok(())
}
}
}
fn agent_package_cmd(cli: &Cli, action: &AgentPackageAction) -> Result<()> {
match action {
AgentPackageAction::Inspect { resolve_resources } => {
let workspace = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let canonical_workspace = std::fs::canonicalize(&workspace)
.with_context(|| format!("resolving workspace {}", workspace.display()))?;
let inspection = match supercode_harness::agent_package::load_workspace_agent_package(
&canonical_workspace,
)? {
Some(package) => {
let mut package_value = serde_json::to_value(&package)?;
if *resolve_resources {
let resources =
supercode_harness::agent_package::declared_resource_contribution_ids(
&package,
)
.into_iter()
.map(|contribution_id| {
(
contribution_id.to_string(),
agent_package_resource_state(&package, contribution_id),
)
})
.collect::<serde_json::Map<_, _>>();
package_value
.as_object_mut()
.expect("an agent package serializes as an object")
.insert(
"resources".to_string(),
serde_json::Value::Object(resources),
);
}
serde_json::json!({
"schema": "supercode/agent-package-inspection-v1",
"status": "ready",
"package": package_value,
})
}
None => serde_json::json!({
"schema": "supercode/agent-package-inspection-v1",
"status": "absent",
"workspace": canonical_workspace,
"expectedManifest": canonical_workspace
.join(supercode_harness::agent_package::AGENT_PACKAGE_MANIFEST),
}),
};
println!("{}", serde_json::to_string_pretty(&inspection)?);
Ok(())
}
AgentPackageAction::Resource { contribution_id } => {
let workspace = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let package =
supercode_harness::agent_package::load_workspace_agent_package(&workspace)?
.ok_or_else(|| {
anyhow::anyhow!("workspace has no Volter Harness agent package")
})?;
if !supercode_harness::agent_package::declared_resource_contribution_ids(&package)
.contains(&contribution_id.as_str())
{
anyhow::bail!(
"contribution `{contribution_id}` does not declare a storage resource"
);
}
println!(
"{}",
serde_json::to_string_pretty(&agent_package_resource_state(
&package,
contribution_id
))?
);
Ok(())
}
}
}
fn agent_package_resource_state(
package: &supercode_harness::agent_package::AgentPackage,
contribution_id: &str,
) -> serde_json::Value {
match supercode_harness::agent_package::read_contribution_resource(package, contribution_id) {
Ok(Some(resource)) => serde_json::json!({
"schema": "supercode/package-resource-state-v1",
"status": "ready",
"resource": resource,
}),
Ok(None) => serde_json::json!({
"schema": "supercode/package-resource-state-v1",
"status": "absent",
"contributionId": contribution_id,
}),
Err(_) => serde_json::json!({
"schema": "supercode/package-resource-state-v1",
"status": "error",
"contributionId": contribution_id,
"message": "The declared package resource could not be read.",
}),
}
}
async fn mcp_login_cmd(name: &str) -> Result<()> {
let reg = uc::load_mcp();
let def = reg.servers.get(name).ok_or_else(|| {
anyhow::anyhow!("no MCP server named `{name}` — `supercode mcp add`/edit mcp.json first")
})?;
let oauth = def.oauth.as_ref().ok_or_else(|| {
anyhow::anyhow!("`{name}` has no `oauth` block configured (mcp.json or [capabilities.mcp.servers.{name}.oauth])")
})?;
let endpoints = mcp_oauth::OAuthEndpoints {
device_authorization_endpoint: oauth.device_authorization_endpoint.clone(),
token_endpoint: oauth.token_endpoint.clone(),
client_id: oauth.client_id.clone(),
scope: oauth.scope.clone(),
};
let client = reqwest::Client::new();
let tokens = mcp_oauth::run_device_flow(&client, &endpoints, |auth| {
if let Some(complete) = &auth.verification_uri_complete {
eprintln!("mcp login `{name}`: open {complete} to authorize");
} else {
eprintln!(
"mcp login `{name}`: open {} and enter code {}",
auth.verification_uri, auth.user_code
);
}
eprintln!("waiting for authorization…");
})
.await
.map_err(|e| anyhow::anyhow!("mcp login `{name}` failed: {e}"))?;
uc::save_mcp_oauth_tokens(name, &tokens)?;
eprintln!(
"mcp login `{name}`: authorized, tokens stored in {}",
uc::mcp_oauth_file().display()
);
Ok(())
}
fn mcp_import_cmd(from: McpImportSource, dry_run: bool) -> Result<()> {
let Some(home) = mcp_import::home_dir() else {
println!("mcp import: $HOME is not set — nothing to scan");
return Ok(());
};
let report = match from {
McpImportSource::Claude => mcp_import::scan_claude(&home),
McpImportSource::Codex => mcp_import::scan_codex(&home),
McpImportSource::All => mcp_import::scan_all(&home),
};
for s in &report.skipped {
eprintln!(
"mcp: skipping `{}` from {} — {}",
s.name,
s.source.label(),
s.reason
);
}
if report.candidates.is_empty() {
println!("mcp import: no importable stdio MCP servers found (checked ~/.claude.json, ~/.claude/mcp.json, ~/.mcp.json, ~/.codex/config.toml)");
return Ok(());
}
for c in &report.candidates {
println!(
"found `{}` ({} {}) via {} [{}]",
c.name,
c.def.command.as_deref().unwrap_or(""),
c.def.args.join(" "),
c.source.label(),
c.origin
);
for note in &c.notes {
println!(" note: {note}");
}
}
let mut reg = uc::load_mcp();
let outcome = if dry_run {
let mut preview = reg.servers.clone();
mcp_import::apply(&report, &mut preview)
} else {
mcp_import::apply(&report, &mut reg.servers)
};
for name in &outcome.already_present {
eprintln!(
"mcp: `{name}` already exists in Volter Harness's MCP config — kept as-is (not overwritten)"
);
}
if outcome.imported.is_empty() {
println!("mcp import: nothing new to import (already up to date)");
return Ok(());
}
if dry_run {
println!(
"dry-run: would import {} server(s): {}",
outcome.imported.len(),
outcome.imported.join(", ")
);
return Ok(());
}
uc::save_mcp(®)?;
println!(
"imported {} server(s) into {}: {}",
outcome.imported.len(),
uc::mcp_file().display(),
outcome.imported.join(", ")
);
Ok(())
}
const PROMPT_INPUT_DEADLINE: std::time::Duration = std::time::Duration::from_secs(300);
#[cfg(unix)]
fn stdin_ready_within(deadline: std::time::Duration) -> io::Result<bool> {
let mut pfd = libc::pollfd {
fd: libc::STDIN_FILENO,
events: libc::POLLIN,
revents: 0,
};
let timeout_ms = i32::try_from(deadline.as_millis()).unwrap_or(i32::MAX);
let n = unsafe { libc::poll(std::ptr::addr_of_mut!(pfd), 1, timeout_ms) };
if n < 0 {
return Err(io::Error::last_os_error());
}
Ok(n > 0)
}
#[cfg(windows)]
fn stdin_ready_within(deadline: std::time::Duration) -> io::Result<bool> {
let _ = deadline;
Ok(true)
}
fn interpret_yes_no_read(result: &io::Result<usize>, line: &str) -> bool {
match result {
Ok(0) => false,
Ok(_) => !matches!(line.trim().to_lowercase().as_str(), "n" | "no"),
Err(_) => false,
}
}
fn prompt_yes_no(prompt: &str) -> bool {
eprint!("{prompt}");
let _ = io::stderr().flush();
if matches!(stdin_ready_within(PROMPT_INPUT_DEADLINE), Ok(false)) {
return false;
}
let mut line = String::new();
let result = io::stdin().read_line(&mut line);
interpret_yes_no_read(&result, &line)
}
fn should_onboard(config_exists: bool, stdin_tty: bool, stderr_tty: bool, quiet: bool) -> bool {
!config_exists && stdin_tty && stderr_tty && !quiet
}
fn pick_onboarding_model(yes: bool) -> String {
if yes {
return uc::DEFAULT_MODEL.to_string();
}
if picker::available() {
let candidates = model_picker_candidates();
let items: Vec<picker::PickerItem> =
candidates.iter().map(|(_, item)| item.clone()).collect();
if let Ok(Some(idx)) = picker::pick("pick a default model", &items) {
return uc::resolve_model_alias(&candidates[idx].0);
}
eprintln!(" (cancelled — using the default model)");
return uc::DEFAULT_MODEL.to_string();
}
eprint!(
" default model [{}] (alias or slug, blank for default): ",
uc::DEFAULT_MODEL
);
let _ = io::stderr().flush();
let mut line = String::new();
if io::stdin().read_line(&mut line).is_ok() {
let trimmed = line.trim();
if !trimmed.is_empty() {
return uc::resolve_model_alias(trimmed);
}
}
uc::DEFAULT_MODEL.to_string()
}
async fn maybe_onboard(cli: &Cli) {
if cli.bare {
return;
}
if !should_onboard(
uc::config_file().exists(),
io::stdin().is_terminal(),
io::stderr().is_terminal(),
effective_quiet(cli),
) {
return;
}
eprintln!();
eprintln!("{}", ui::bold(ui::ACCENT, "welcome to Volter Harness!"));
eprintln!(
" {}",
ui::paint(
ui::MUTED,
"first run detected — a short guided setup gets you to a working config. \
(this runs at most once; `supercode login --provider` / `supercode doctor` are always \
available on demand afterward.)"
)
);
if !(cli.yes || prompt_yes_no(" run the guided setup now? [Y/n] ")) {
if let Err(e) = uc::save_config(&uc::FileConfig::default()) {
eprintln!(" onboarding: failed to record setup decision: {e}");
}
eprintln!(" skipped — run `supercode login --provider` / `supercode doctor` anytime");
eprintln!();
return;
}
let base_url = resolve_base_url(cli);
let existing_key = uc::resolve_api_key(cli.api_key.as_deref(), &base_url);
let existing_source = uc::api_key_source(cli.api_key.as_deref(), &base_url);
let chosen_model = pick_onboarding_model(cli.yes);
let reuse = existing_key.is_some() && {
eprintln!(
" found an existing provider key via {}",
existing_source.unwrap_or("?")
);
cli.yes || prompt_yes_no(" use it instead of entering a new one? [Y/n] ")
};
if reuse {
let key = existing_key.expect("`reuse` is only true when `existing_key` is `Some`");
if existing_source != Some("credentials.toml") {
if let Err(e) = uc::save_api_key(&key) {
eprintln!(" onboarding: failed to save key: {e}");
}
}
let mut fc = uc::load(&std::env::current_dir().unwrap_or_default());
fc.model = Some(chosen_model);
match uc::save_config(&fc) {
Ok(()) => eprintln!(
" {} {}",
ui::mark(true),
ui::paint(ui::MUTED, "config saved")
),
Err(e) => eprintln!(" onboarding: failed to save config: {e}"),
}
eprintln!(" {}", ui::paint(ui::ACCENT, "› supercode run \"hello\""));
} else {
if let Err(e) = login(cli.api_key.clone(), false, Some(chosen_model), None).await {
eprintln!(" onboarding: login step failed: {e}");
}
}
eprintln!(
"{}",
ui::paint(
ui::MUTED,
" running doctor to confirm everything's wired up…"
)
);
if let Err(e) = doctor(cli, false, false).await {
eprintln!(" onboarding: doctor step failed: {e}");
}
eprintln!();
if !uc::config_file().exists() {
let _ = uc::save_config(&uc::FileConfig::default());
}
}
fn first_run_candidate_lines(report: &mcp_import::ScanReport) -> Vec<String> {
let mut lines = Vec::new();
for c in &report.candidates {
lines.push(format!(
" {} {} {}",
ui::paint(ui::TEAL, "›"),
c.name,
ui::paint(ui::MUTED, format!("(via {})", c.source.label())),
));
for note in &c.notes {
lines.push(format!(
" {}",
ui::paint(ui::MUTED, format!("note: {note}"))
));
}
}
lines
}
fn maybe_first_run_mcp_import(cli: &Cli) {
if uc::mcp_file().exists() {
return;
}
let Some(home) = mcp_import::home_dir() else {
return;
};
let report = mcp_import::scan_all(&home);
if report.candidates.is_empty() {
return;
}
let interactive = io::stdin().is_terminal() && io::stderr().is_terminal();
if !interactive || effective_quiet(cli) {
return;
}
eprintln!();
eprintln!(
"{}",
ui::paint(
ui::ACCENT,
"supercode: no MCP servers configured yet — found some in Claude Code/Codex:"
)
);
for line in first_run_candidate_lines(&report) {
eprintln!("{line}");
}
for s in &report.skipped {
eprintln!(
" mcp: skipping `{}` from {} — {}",
s.name,
s.source.label(),
s.reason
);
}
let proceed =
cli.yes || prompt_yes_no(" import these into Volter Harness's MCP config? [Y/n] ");
let mut reg = uc::load_mcp(); if proceed {
let outcome = mcp_import::apply(&report, &mut reg.servers);
match uc::save_mcp(®) {
Ok(()) => eprintln!(
" imported {} server(s) — see `supercode mcp list`",
outcome.imported.len()
),
Err(e) => eprintln!(" mcp: failed to save imported servers: {e}"),
}
} else {
if let Err(e) = uc::save_mcp(®) {
eprintln!(" mcp: failed to record import decision: {e}");
}
eprintln!(" skipped — run `supercode mcp import` anytime to import later");
}
}
fn mcp_module_on(agent: &SdkAgent) -> bool {
mcp_module_active(agent.config())
}
fn mcp_module_active(config: &Config) -> bool {
config.module_activation.is_active(ModuleId::McpClient)
}
async fn resolve_oauth_header(
name: &str,
oauth: &uc::McpOAuthServerConfig,
quiet: bool,
) -> Option<(String, String)> {
let endpoints = mcp_oauth::OAuthEndpoints {
device_authorization_endpoint: oauth.device_authorization_endpoint.clone(),
token_endpoint: oauth.token_endpoint.clone(),
client_id: oauth.client_id.clone(),
scope: oauth.scope.clone(),
};
let mut tokens = uc::load_mcp_oauth_tokens(name)?;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0);
if tokens.is_expired_at(now, 60) {
let Some(refresh) = tokens.refresh_token.clone() else {
if !quiet {
eprintln!(
"mcp: `{name}` OAuth token expired and no refresh token is stored — run `supercode mcp login {name}`"
);
}
return None;
};
let client = reqwest::Client::new();
match mcp_oauth::refresh_token(&client, &endpoints, &refresh).await {
Ok(mut refreshed) => {
if refreshed.refresh_token.is_none() {
refreshed.refresh_token = Some(refresh);
}
if let Err(e) = uc::save_mcp_oauth_tokens(name, &refreshed) {
if !quiet {
eprintln!("mcp: `{name}` failed to persist refreshed OAuth token: {e}");
}
}
tokens = refreshed;
}
Err(e) => {
if !quiet {
eprintln!("mcp: `{name}` OAuth refresh failed: {e} — run `supercode mcp login {name}`");
}
return None;
}
}
}
Some(mcp_oauth::bearer_header(&tokens))
}
async fn attach_mcp_with_elicitation(
cli: &Cli,
agent: &mut SdkAgent,
elicitation: Option<std::sync::Arc<dyn supercode_harness::mcp::McpElicitationHandler>>,
) {
let quiet = effective_quiet(cli);
let module_on = mcp_module_on(agent);
let mut servers = if cli.bare {
std::collections::BTreeMap::new()
} else {
maybe_first_run_mcp_import(cli);
uc::load_mcp().servers
};
for path in &cli.mcp_config {
match uc::read_mcp_file(path) {
Ok(extra) => servers.extend(extra.servers),
Err(e) => {
if !quiet {
eprintln!("mcp: failed to read {}: {e}", path.display());
}
}
}
}
if module_on && !cli.bare {
for (name, def) in mcp_servers_from_capabilities(cli) {
servers.insert(name, def);
}
}
let cache_plan_imported = agent.config().cache_plan == CachePlan::ImportedPrefix;
let cache_warnings = agent.config().cache_warnings;
let network_policy = agent.config().network_policy.clone();
for (name, def) in servers {
let transport = def.transport.as_deref().unwrap_or("stdio");
let client = match transport {
"stdio" => {
let Some(command) = def.command.as_deref() else {
if !quiet {
eprintln!("mcp: `{name}` has no `command` (stdio transport requires one)");
}
continue;
};
let args: Vec<&str> = def.args.iter().map(String::as_str).collect();
let env = def.env.clone().unwrap_or_default();
McpClient::connect(command, &args, &env).await
}
"http" | "sse" if !module_on => {
if !quiet {
eprintln!(
"mcp: `{name}` uses the `{transport}` transport, which needs \
[capabilities.mcp] enabled = true — skipped"
);
}
continue;
}
"http" | "sse" => {
let Some(url) = def.url.clone() else {
if !quiet {
eprintln!(
"mcp: `{name}` has no `url` ({transport} transport requires one)"
);
}
continue;
};
let mut headers = def.headers.clone().unwrap_or_default();
if let Some(oauth) = &def.oauth {
if let Some((k, v)) = resolve_oauth_header(&name, oauth, quiet).await {
headers.insert(k, v);
}
}
if transport == "http" {
McpClient::connect_http(&url, &headers, network_policy.as_ref()).await
} else {
McpClient::connect_sse(&url, &headers, network_policy.as_ref()).await
}
}
other => {
if !quiet {
eprintln!("mcp: `{name}` has an unknown transport `{other}` — skipped");
}
continue;
}
};
let mut client = match client {
Ok(c) => c,
Err(e) => {
if !quiet {
eprintln!("mcp: `{name}` connect failed: {e}");
}
continue;
}
};
if let Some(h) = &elicitation {
client.set_elicitation_handler(h.clone());
}
let handle = McpServerHandle::new(name.clone(), client);
match handle.tools().await {
Ok(tools) => {
let n = tools.len();
for t in tools {
agent.register_tool(t);
}
if !quiet {
eprintln!("mcp: loaded {n} tool(s) from `{name}`");
if cache_plan_imported && cache_warnings && n > 0 {
eprintln!("{}", cache_churn_notice(&name, n));
}
}
}
Err(e) => {
if !quiet {
eprintln!("mcp: `{name}` list_tools failed: {e}");
}
}
}
if !module_on {
continue; }
for t in handle.resource_tools() {
agent.register_tool(McpToolAdapter(t));
}
match handle.prompts().await {
Ok(prompts) => {
for (command_name, source) in prompts {
agent.register_mcp_prompt(command_name, source);
}
}
Err(e) => {
if !quiet {
eprintln!("mcp: `{name}` prompts/list failed: {e}");
}
}
}
if let Some(instructions) = handle.instructions().await {
agent.append_system_note(&format!("\n\n# MCP: {name} instructions\n{instructions}"));
}
}
}
async fn attach_mcp(cli: &Cli, agent: &mut SdkAgent) {
attach_mcp_with_elicitation(cli, agent, None).await
}
struct McpToolAdapter(Box<dyn supercode_harness::tools::Tool>);
#[async_trait]
impl supercode_harness::tools::Tool for McpToolAdapter {
fn name(&self) -> &str {
self.0.name()
}
fn description(&self) -> &str {
self.0.description()
}
fn parameters(&self) -> serde_json::Value {
self.0.parameters()
}
async fn execute(
&self,
args: serde_json::Value,
ctx: &ToolContext,
) -> supercode_harness::Result<String> {
self.0.execute(args, ctx).await
}
}
fn mcp_servers_from_capabilities(
cli: &Cli,
) -> std::collections::BTreeMap<String, uc::McpServerDef> {
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let fc = uc::load(&cwd);
let Some(cap) = fc.capabilities.get("mcp") else {
return Default::default();
};
let Some(servers) = cap.settings.get("servers") else {
return Default::default();
};
serde_json::from_value(servers.clone()).unwrap_or_default()
}
async fn run_cmd(
cli: &Cli,
prompt: String,
images: &[String],
output_format: OutputFormat,
output_schema: Option<&Path>,
show_prompt: bool,
) -> Result<()> {
maybe_onboard(cli).await;
require_api_key(cli)?;
let mut config = build_config(cli)?;
if let Some(schema_path) = output_schema {
let schema: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(schema_path)
.with_context(|| format!("reading {}", schema_path.display()))?,
)
.with_context(|| format!("parsing JSON Schema {}", schema_path.display()))?;
config.response_format = Some(serde_json::json!({
"type": "json_schema",
"json_schema": { "name": "output", "schema": schema, "strict": true }
}));
}
let json = output_format == OutputFormat::Json;
let stream_json = output_format == OutputFormat::StreamJson;
let spinner = match output_format {
OutputFormat::Text => {
let (sink, spinner) = streaming_sink(false, effective_quiet(cli));
config.event_sink = Some(with_trace(sink, cli.trace));
Some(spinner)
}
OutputFormat::Json => {
if cli.trace {
config.event_sink = Some(trace_only_sink());
}
None
}
OutputFormat::StreamJson => {
config.event_sink = Some(with_trace(stream_json_sink(), cli.trace));
None
}
OutputFormat::Rpc => {
unreachable!("Command::Run redirects to rpc_cmd before calling run_cmd")
}
};
let model_label = config.model.clone();
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let notify_settings = effective_notify(cli, &uc::load(&cwd));
let machine_format = json || stream_json;
let auto_title_gate = AutoTitleGate::capture(&config);
let span_summary_gate = SpanSummaryGate::capture(&config);
let mut agent = supercode_harness::create_agent(config)?;
maybe_install_auto_titler(&mut agent, &auto_title_gate);
maybe_install_span_summarizer(&mut agent, &span_summary_gate);
attach_mcp(cli, &mut agent).await;
let session_name = open_session(cli, &mut agent, &cwd)?;
hooks::fire_user_prompt_submit(
¬ify_settings.hooks,
&prompt,
&cwd,
Some(&session_name),
effective_quiet(cli),
);
if show_prompt {
let req = supercode_harness::show_model_input(&mut agent, &prompt).await;
println!("{}", serde_json::to_string_pretty(&req).unwrap_or_default());
return Ok(());
}
let turn_start = std::time::Instant::now();
let reply = if images.is_empty() {
if let Some(sp) = &spinner {
sp.reset_turn(); sp.start("Thinking…");
}
let outcome = race_ctrl_c(
supercode_harness::submit_agent(&mut agent, &prompt),
spinner.as_deref(),
)
.await;
if let Some(sp) = &spinner {
sp.stop();
sp.clear_counter(); }
let Some(result) = outcome else {
print_interrupt_notice(
effective_quiet(cli),
"turn cancelled; session left as it was before this turn",
);
std::process::exit(130);
};
result?
} else {
let urls = images
.iter()
.map(|s| image_to_url(s))
.collect::<Result<Vec<_>>>()?;
if let Some(sp) = &spinner {
sp.reset_turn(); sp.start("Thinking…");
}
let outcome = race_ctrl_c(
supercode_harness::submit_agent_with_images(&mut agent, &prompt, &urls),
spinner.as_deref(),
)
.await;
if let Some(sp) = &spinner {
sp.stop();
sp.clear_counter(); }
let Some(result) = outcome else {
print_interrupt_notice(
effective_quiet(cli),
"turn cancelled; session left as it was before this turn",
);
std::process::exit(130);
};
result?
};
persist_session(&agent, &session_name);
apply_auto_title(&agent, &session_name);
notify::maybe_fire(
¬ify_settings,
machine_format,
io::stderr().is_terminal(),
turn_start.elapsed(),
&model_label,
&reply,
);
if json {
println!("{}", supercode_harness::format_reply(&reply, true));
} else if stream_json {
println!("{}", serde_json::json!({"type": "result", "result": reply}));
io::stdout().flush().ok();
} else {
let _ = &reply;
println!();
println!();
}
Ok(())
}
fn resolve_output_format(cli_value: Option<OutputFormat>, fc: &uc::FileConfig) -> OutputFormat {
if let Some(v) = cli_value {
return v;
}
match fc.core.output.format.as_deref() {
Some("json") => OutputFormat::StreamJson,
_ => OutputFormat::Text,
}
}
fn require_server_capability(fc: &uc::FileConfig, config: &Config, surface: &str) -> Result<()> {
let directly_enabled = fc
.capabilities
.get("server")
.and_then(|c| c.enabled)
.unwrap_or(false);
let preset_enabled =
config.module_registry && config.module_activation.is_active(ModuleId::Server);
if !directly_enabled && !preset_enabled {
anyhow::bail!(
"`{surface}` requires [capabilities.server] enabled = true in your OWN \
(user/global) config — it can never be set from a project .supercode.toml \
(a listener is project-forbidden, COMPOSABLE-HARNESS-DESIGN.md §3.3 D-10). \
Add it to your user config (see `supercode doctor` for its path), or select \
a preset that turns it on (e.g. `extends = \"pi-core\"` with \
[experimental] module_registry = true)."
);
}
Ok(())
}
fn is_loopback_bind(bind: &str) -> bool {
let host = bind.rsplit_once(':').map(|(h, _)| h).unwrap_or(bind);
let host = host.trim_start_matches('[').trim_end_matches(']');
if host.eq_ignore_ascii_case("localhost") {
return true;
}
host.parse::<std::net::IpAddr>()
.map(|ip| ip.is_loopback())
.unwrap_or(false)
}
async fn rpc_cmd(cli: &Cli, fc: &uc::FileConfig) -> Result<()> {
maybe_onboard(cli).await;
require_api_key(cli)?;
let mut config = resolve_config(cli)?;
require_server_capability(fc, &config, "run --output-format rpc")?;
print_startup_banners(cli, &config);
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
config.approval_handler = None;
let span_summary_gate = SpanSummaryGate::capture(&config);
let mut agent = supercode_harness::create_agent(config)?;
maybe_install_span_summarizer(&mut agent, &span_summary_gate);
attach_mcp(cli, &mut agent).await;
let session_name = open_session(cli, &mut agent, &cwd)?;
let runtime_name = session_name.clone();
let engine = supercode_harness::server::RpcEngine::new_named(
agent,
runtime_name,
Some(Box::new(move |a: &SdkAgent| {
persist_session(a, &session_name);
apply_auto_title(a, &session_name);
})),
);
let stdin = tokio::io::BufReader::new(tokio::io::stdin());
let stdout = tokio::io::stdout();
supercode_harness::server::run_stdio(engine, stdin, stdout).await?;
Ok(())
}
struct GooseAcpBridge {
address: std::net::SocketAddr,
nonce: String,
task: Option<tokio::task::JoinHandle<Result<()>>>,
}
impl GooseAcpBridge {
async fn start(runtime: Arc<dyn supercode_harness::SdkRuntime>) -> Result<Self> {
use tokio::io::BufReader;
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.context("binding the private Goose ACP bridge")?;
let address = listener.local_addr()?;
let nonce = supercode_harness::server::generate_token();
let expected_nonce = nonce.clone();
let task = tokio::spawn(async move {
let (stream, peer) = tokio::time::timeout(Duration::from_secs(10), listener.accept())
.await
.context("stock Goose did not connect to its ACP bridge within ten seconds")??;
if !peer.ip().is_loopback() {
anyhow::bail!("Goose ACP bridge rejected non-loopback peer {peer}");
}
let (read, write) = tokio::io::split(stream);
let mut read = BufReader::new(read);
let received_nonce = read_goose_bridge_nonce(&mut read).await?;
if received_nonce != expected_nonce {
anyhow::bail!("Goose ACP bridge rejected an invalid child nonce");
}
let server = supercode_harness::acp_server::AcpServer::new_with_profile(
runtime,
supercode_harness::acp_server::AcpCompatibilityProfile::GooseAcd3c135,
)
.await?;
supercode_harness::acp_server::run_stdio(server, read, write).await?;
Ok(())
});
Ok(Self {
address,
nonce,
task: Some(task),
})
}
async fn finish(mut self) -> Result<()> {
let mut task = self.task.take().expect("Goose bridge task exists");
match tokio::time::timeout(Duration::from_secs(5), &mut task).await {
Ok(joined) => joined.context("joining the Goose ACP bridge task")?,
Err(_) => {
task.abort();
let _ = task.await;
anyhow::bail!("Goose ACP bridge did not close within five seconds");
}
}
}
}
impl Drop for GooseAcpBridge {
fn drop(&mut self) {
if let Some(task) = &self.task {
task.abort();
}
}
}
async fn read_goose_bridge_nonce<R>(reader: &mut R) -> Result<String>
where
R: tokio::io::AsyncBufRead + Unpin,
{
use tokio::io::AsyncBufReadExt;
const NONCE_BYTES: usize = 64;
let mut nonce = Vec::with_capacity(NONCE_BYTES);
loop {
let (chunk, consumed, complete) = {
let available = tokio::time::timeout(Duration::from_secs(5), reader.fill_buf())
.await
.context("Goose ACP bridge nonce handshake timed out")??;
if available.is_empty() {
anyhow::bail!("Goose ACP bridge child closed before authentication");
}
let newline = available.iter().position(|byte| *byte == b'\n');
let data_len = newline.unwrap_or(available.len());
(
available[..data_len].to_vec(),
data_len + usize::from(newline.is_some()),
newline.is_some(),
)
};
if nonce.len() + chunk.len() > NONCE_BYTES {
anyhow::bail!("Goose ACP bridge nonce exceeded {NONCE_BYTES} bytes");
}
nonce.extend_from_slice(&chunk);
reader.consume(consumed);
if complete {
break;
}
}
if nonce.len() != NONCE_BYTES || !nonce.iter().all(u8::is_ascii_hexdigit) {
anyhow::bail!("Goose ACP bridge child sent a malformed nonce");
}
String::from_utf8(nonce).context("Goose ACP bridge nonce was not UTF-8")
}
async fn proxy_goose_acp_child_from_environment() -> Result<bool> {
use tokio::io::AsyncWriteExt;
let address = std::env::var(frontend_client::GOOSE_BRIDGE_ADDRESS_ENV).ok();
let nonce = std::env::var(frontend_client::GOOSE_BRIDGE_NONCE_ENV).ok();
let (address, nonce) = match (address, nonce) {
(Some(address), Some(nonce)) => (address, nonce),
(None, None) => return Ok(false),
_ => anyhow::bail!("incomplete internal Goose ACP bridge environment"),
};
let address: std::net::SocketAddr = address
.parse()
.context("invalid internal Goose ACP bridge address")?;
if !address.ip().is_loopback() {
anyhow::bail!("internal Goose ACP bridge address must be loopback");
}
if nonce.len() != 64 || !nonce.bytes().all(|byte| byte.is_ascii_hexdigit()) {
anyhow::bail!("invalid internal Goose ACP bridge nonce");
}
let mut stream = tokio::time::timeout(
Duration::from_secs(5),
tokio::net::TcpStream::connect(address),
)
.await
.context("connecting the internal Goose ACP bridge timed out")??;
stream.write_all(nonce.as_bytes()).await?;
stream.write_all(b"\n").await?;
stream.flush().await?;
let (mut bridge_read, mut bridge_write) = tokio::io::split(stream);
let mut stdin = tokio::io::stdin();
let mut stdout = tokio::io::stdout();
let to_bridge = tokio::io::copy(&mut stdin, &mut bridge_write);
let to_client = tokio::io::copy(&mut bridge_read, &mut stdout);
let (_, _) = tokio::try_join!(to_bridge, to_client)?;
stdout.flush().await?;
Ok(true)
}
async fn acp_cmd(
cli: &Cli,
connect: Option<&str>,
token: Option<&str>,
credential_fd: Option<i32>,
client_id: Option<&str>,
) -> Result<()> {
if proxy_goose_acp_child_from_environment().await? {
return Ok(());
}
if let Some(base_url) = connect {
let fd_credential = credential_fd.map(read_runtime_credential_fd).transpose()?;
let token = token
.map(str::to_owned)
.or_else(|| fd_credential.map(|credential| credential.0))
.or_else(|| std::env::var("SUPERCODE_SERVER_TOKEN").ok())
.context(
"acp --connect requires --credential-fd, --token, or SUPERCODE_SERVER_TOKEN",
)?;
let client_id = client_id
.map(supercode_harness::RuntimeClientId::parse)
.transpose()?;
let runtime = if let Some(client_id) = client_id {
supercode_harness::acp_server::HttpAcpRuntime::connect_with_client_id(
base_url, token, client_id,
)
.await?
} else {
supercode_harness::acp_server::HttpAcpRuntime::connect(base_url, token).await?
};
let server = supercode_harness::acp_server::AcpServer::new(runtime).await?;
let stdin = tokio::io::BufReader::new(tokio::io::stdin());
let stdout = tokio::io::stdout();
supercode_harness::acp_server::run_stdio(server, stdin, stdout).await?;
return Ok(());
}
maybe_onboard(cli).await;
require_api_key(cli)?;
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let mut config = resolve_config(cli)?;
print_startup_banners(cli, &config);
config.approval_handler = None;
let span_summary_gate = SpanSummaryGate::capture(&config);
let mut agent = supercode_harness::create_agent(config)?;
maybe_install_span_summarizer(&mut agent, &span_summary_gate);
attach_mcp(cli, &mut agent).await;
let session_name = open_session(cli, &mut agent, &cwd)?;
let persist_name = session_name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named(
agent,
session_name,
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
let server = supercode_harness::acp_server::AcpServer::new(runtime.clone()).await?;
let stdin = tokio::io::BufReader::new(tokio::io::stdin());
let stdout = tokio::io::stdout();
let result = supercode_harness::acp_server::run_stdio(server, stdin, stdout).await;
runtime.shutdown().await;
result?;
Ok(())
}
struct AttachConnectOptions<'a> {
base_url: &'a str,
token: Option<&'a str>,
credential_fd: Option<i32>,
client_id: Option<&'a str>,
through_acp: bool,
session_id: Option<String>,
after_sequence: Option<u64>,
}
async fn attach_cmd(cli: &Cli, options: AttachConnectOptions<'_>) -> Result<()> {
let inherited_acp_fd = options
.through_acp
.then_some(options.credential_fd)
.flatten();
let fd_credential = if inherited_acp_fd.is_none() {
options
.credential_fd
.map(read_runtime_credential_fd)
.transpose()?
} else {
None
};
let token = if inherited_acp_fd.is_some() {
None
} else {
Some(
options
.token
.map(str::to_owned)
.or_else(|| fd_credential.map(|credential| credential.0))
.or_else(|| std::env::var("SUPERCODE_SERVER_TOKEN").ok())
.context("attach requires --credential-fd, --token, or SUPERCODE_SERVER_TOKEN")?,
)
};
let client_id = options
.client_id
.map(supercode_harness::RuntimeClientId::parse)
.transpose()?;
let config = resolve_config(cli)?;
if options.through_acp {
let executable = std::env::current_exe().context("resolving the supercode executable")?;
let mut env = std::collections::BTreeMap::new();
let arguments = if let Some(fd) = inherited_acp_fd {
let client_id = client_id
.as_ref()
.context("scoped ACP --credential-fd requires --client-id")?;
vec![
"acp".into(),
"--connect".into(),
options.base_url.into(),
"--credential-fd".into(),
fd.to_string(),
"--client-id".into(),
client_id.as_str().into(),
]
} else {
env.insert(
"SUPERCODE_SERVER_TOKEN".into(),
token.expect("non-FD ACP resolved its credential"),
);
vec!["acp".into(), "--connect".into(), options.base_url.into()]
};
let runtime = supercode_harness::AcpFrontendRuntime::connect(
supercode_harness::AcpFrontendConnectOptions {
launch: supercode_harness::RuntimeLaunch {
program: executable.to_string_lossy().into_owned(),
arguments,
env,
},
cwd: cli.cwd.clone(),
session_id: options.session_id,
after_sequence: options.after_sequence,
},
)
.await?;
let checkpoint_path = acp_frontend_checkpoint_path(
options.base_url,
runtime.session_id(),
options.client_id.unwrap_or("legacy-default"),
);
if options.after_sequence.is_none() {
if let Some(checkpoint) = load_acp_frontend_checkpoint(&checkpoint_path)? {
runtime
.restore_checkpoint(checkpoint)
.context("restoring ACP frontend checkpoint")?;
}
}
let frontend: Arc<dyn supercode_harness::SdkRuntime> = runtime.clone();
let frontend_result = if tui::should_activate(&config) {
supercode_frontend_tui::app::run(frontend).await
} else {
supercode_frontend_tui::app::run_line(frontend).await
};
let checkpoint_result =
save_acp_frontend_checkpoint(&checkpoint_path, &runtime.checkpoint());
let detach_result = runtime.detach().await;
frontend_result?;
checkpoint_result?;
detach_result?;
} else {
let token = token.expect("direct attach resolved its credential");
let runtime = if let Some(client_id) = client_id {
supercode_harness::HttpFrontendRuntime::connect_with_client_id(
options.base_url,
token,
client_id,
)
.await?
} else {
supercode_harness::HttpFrontendRuntime::connect(options.base_url, token).await?
};
if tui::should_activate(&config) {
supercode_frontend_tui::app::run(runtime.clone()).await?;
} else {
supercode_frontend_tui::app::run_line(runtime.clone()).await?;
}
if !runtime.is_disconnected() {
runtime.detach().await?;
}
}
Ok(())
}
fn acp_frontend_checkpoint_path(base_url: &str, session_id: &str, client_id: &str) -> PathBuf {
let mut hash = 0xcbf29ce484222325_u64;
for byte in base_url
.bytes()
.chain([0])
.chain(session_id.bytes())
.chain([0])
.chain(client_id.bytes())
{
hash ^= u64::from(byte);
hash = hash.wrapping_mul(0x100000001b3);
}
uc::config_home()
.join("frontend-acp")
.join(format!("{hash:016x}.json"))
}
fn load_acp_frontend_checkpoint(
path: &Path,
) -> Result<Option<supercode_harness::AcpFrontendCheckpoint>> {
match std::fs::read(path) {
Ok(bytes) => serde_json::from_slice(&bytes)
.with_context(|| format!("reading ACP frontend checkpoint {}", path.display()))
.map(Some),
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(None),
Err(error) => Err(error)
.with_context(|| format!("opening ACP frontend checkpoint {}", path.display())),
}
}
fn save_acp_frontend_checkpoint(
path: &Path,
checkpoint: &supercode_harness::AcpFrontendCheckpoint,
) -> Result<()> {
let mut checkpoint = checkpoint.clone();
if let Some(existing) = load_acp_frontend_checkpoint(path)? {
if existing.schema_version != checkpoint.schema_version
|| existing.session_id != checkpoint.session_id
{
anyhow::bail!(
"refusing to replace incompatible ACP frontend checkpoint {}",
path.display()
);
}
checkpoint.acknowledged_sequence = checkpoint
.acknowledged_sequence
.max(existing.acknowledged_sequence);
}
let parent = path.parent().context("ACP checkpoint path has no parent")?;
std::fs::create_dir_all(parent)
.with_context(|| format!("creating ACP checkpoint directory {}", parent.display()))?;
let temporary = path.with_extension(format!("json.tmp-{}", std::process::id()));
std::fs::write(&temporary, serde_json::to_vec_pretty(&checkpoint)?)
.with_context(|| format!("writing ACP frontend checkpoint {}", temporary.display()))?;
commit_acp_frontend_checkpoint(&temporary, path)
.with_context(|| format!("committing ACP frontend checkpoint {}", path.display()))?;
Ok(())
}
#[cfg(not(windows))]
fn commit_acp_frontend_checkpoint(temporary: &Path, destination: &Path) -> io::Result<()> {
std::fs::rename(temporary, destination)
}
#[cfg(windows)]
fn commit_acp_frontend_checkpoint(temporary: &Path, destination: &Path) -> io::Result<()> {
use std::os::windows::ffi::OsStrExt;
use windows_sys::Win32::Storage::FileSystem::{ReplaceFileW, REPLACEFILE_IGNORE_MERGE_ERRORS};
match std::fs::rename(temporary, destination) {
Ok(()) => return Ok(()),
Err(error) if error.kind() == io::ErrorKind::AlreadyExists => {}
Err(error) => return Err(error),
}
let wide = |path: &Path| {
path.as_os_str()
.encode_wide()
.chain(std::iter::once(0))
.collect::<Vec<_>>()
};
let destination = wide(destination);
let temporary = wide(temporary);
let replaced = unsafe {
ReplaceFileW(
destination.as_ptr(),
temporary.as_ptr(),
std::ptr::null(),
REPLACEFILE_IGNORE_MERGE_ERRORS,
std::ptr::null_mut(),
std::ptr::null_mut(),
)
};
if replaced == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
struct RuntimeCredentialInput(String);
#[cfg(unix)]
fn read_runtime_credential_fd(fd: i32) -> Result<RuntimeCredentialInput> {
use std::io::Read;
if fd < 0 {
anyhow::bail!("credential descriptor must be non-negative");
}
let file = std::fs::File::open(format!("/dev/fd/{fd}"))
.with_context(|| format!("opening inherited credential descriptor {fd}"))?;
let mut bytes = Vec::new();
file.take(4097)
.read_to_end(&mut bytes)
.with_context(|| format!("reading inherited credential descriptor {fd}"))?;
if bytes.len() > 4096 {
anyhow::bail!("runtime credential exceeds 4096-byte limit");
}
while matches!(bytes.last(), Some(b'\n' | b'\r')) {
bytes.pop();
}
if bytes.is_empty() || bytes.iter().any(|byte| byte.is_ascii_whitespace()) {
anyhow::bail!("runtime credential descriptor is empty or malformed");
}
let token = String::from_utf8(bytes).context("runtime credential is not UTF-8")?;
Ok(RuntimeCredentialInput(token))
}
#[cfg(not(unix))]
fn read_runtime_credential_fd(_fd: i32) -> Result<RuntimeCredentialInput> {
anyhow::bail!("--credential-fd is supported on Unix hosts")
}
async fn host_sdk_http_runtime(
runtime: Arc<supercode_harness::server::RpcEngine>,
bind: &str,
token: Arc<str>,
source: supercode_harness::LiveRuntimeSource,
lease_ttl_ms: Option<u64>,
) -> Result<()> {
let address = supercode_harness::server::run_http_authorized_with_lease_ttl(
runtime.clone(),
bind,
vec![supercode_harness::server::RuntimeHttpCredential::owner(
token.clone(),
)],
lease_ttl_ms.unwrap_or(supercode_harness::DEFAULT_RUNTIME_LEASE_TTL_MS),
)
.await
.with_context(|| format!("binding the SDK runtime to {bind}"))?;
let _registration = if address.ip().is_loopback() {
Some(supercode_harness::register_live_runtime_with_metadata(
runtime.session_id(),
source,
format!("http://{address}"),
token.to_string(),
live_runtime_metadata(&runtime)?,
)?)
} else {
None
};
eprintln!(
"supercode runtime {} listening on http://{address}",
runtime.session_id()
);
eprintln!(
"ACP attach: deliver the bearer out of band through SUPERCODE_SERVER_TOKEN, then run `supercode acp --connect http://{address}`"
);
tokio::select! {
_ = tokio::signal::ctrl_c() => {}
_ = runtime.wait_for_shutdown() => {}
}
runtime.shutdown().await;
Ok(())
}
struct EphemeralFrontendRoot(PathBuf);
impl Drop for EphemeralFrontendRoot {
fn drop(&mut self) {
std::fs::remove_dir_all(&self.0).ok();
}
}
async fn run_pi_frontend(
cwd: &Path,
credential: &RemoteAttachCredential,
observe: bool,
take_control: bool,
quiet: bool,
disconnect_monitor: Option<Arc<supercode_harness::HttpFrontendRuntime>>,
) -> Result<()> {
let client_root = credential.root().join("pi-client");
create_private_frontend_dir(&client_root)?;
for child in ["home", "tmp", "config", "data", "state", "cache"] {
create_private_frontend_dir(&client_root.join(child))?;
}
let permissions = if observe {
"observe"
} else {
"observe,interact,approve"
};
let mut child = frontend_client::FrontendClientId::Pi
.spec()
.pi_launch_command(frontend_client::PiLaunch {
cwd,
client_root: &client_root,
base_url: credential.base_url(),
client_id: credential.client_id().as_str(),
credential_path: &credential.credential_path(),
permissions,
take_control,
})
.map_err(anyhow::Error::msg)?;
let mut terminal = terminal_snapshot::TerminalSnapshot::capture()
.context("capturing terminal state before launching Pi frontend")?;
let status: Result<std::process::ExitStatus> = match child
.spawn()
.context("launching the separately packaged Pi frontend")
{
Ok(spawned) => wait_for_direct_frontend(spawned, disconnect_monitor).await,
Err(error) => Err(error),
};
let preference_writes = relative_file_inventory(&client_root)?;
if !quiet && !preference_writes.is_empty() {
eprintln!(
"Pi frontend isolated preference writes: {}",
preference_writes
.iter()
.map(|path| path.display().to_string())
.collect::<Vec<_>>()
.join(", ")
);
}
let status = match status {
Ok(status) if status.success() => {
terminal
.restore()
.context("restoring terminal state after Pi frontend exit")?;
status
}
Ok(status) => {
terminal
.restore_after_abnormal_exit()
.context("restoring terminal state after Pi frontend crash")?;
status
}
Err(error) => {
terminal
.restore_after_abnormal_exit()
.context("restoring terminal state after Pi frontend failure")?;
return Err(error);
}
};
if credential.credential_path().exists() {
anyhow::bail!("Pi frontend exited without consuming its scoped credential file");
}
if !status.success() {
anyhow::bail!("Pi frontend exited with {status}");
}
Ok(())
}
async fn run_stock_goose_frontend(
runtime: Arc<dyn supercode_harness::SdkRuntime>,
cwd: &Path,
quiet: bool,
disconnect_monitor: Option<Arc<supercode_harness::HttpFrontendRuntime>>,
) -> Result<Vec<PathBuf>> {
#[cfg(unix)]
let frontend_temp_root = PathBuf::from("/tmp");
#[cfg(not(unix))]
let frontend_temp_root = std::env::temp_dir();
let root = frontend_temp_root.join(format!(
"sc-goose-{}-{:x}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos()
));
create_private_frontend_dir(&root)?;
let _root_cleanup = EphemeralFrontendRoot(root.clone());
for child in ["home", "tmp", "config", "data", "state", "cache", "goose"] {
create_private_frontend_dir(&root.join(child))?;
}
let installed_tui =
frontend_client::pinned_goose_tui_for_materialization().map_err(anyhow::Error::msg)?;
let pinned_tui =
frontend_client::materialize_pinned_goose_tui(&installed_tui, &root.join("runtime"))
.map_err(anyhow::Error::msg)?;
let mut version_command = tokio::process::Command::new("node");
version_command.arg("--version").kill_on_drop(true);
frontend_client::isolate_stock_goose_environment(&mut version_command, &root);
let version = tokio::time::timeout(Duration::from_secs(5), version_command.output())
.await
.context("stock Goose version probe exceeded the five-second deadline")?
.with_context(|| {
format!(
"locating Node {} for pinned stock Goose {}",
frontend_client::GOOSE_NODE_VERSION,
frontend_client::GOOSE_TUI_VERSION,
)
})?;
let stdout = String::from_utf8_lossy(&version.stdout).trim().to_owned();
let stderr = String::from_utf8_lossy(&version.stderr).trim().to_owned();
let reported = if stdout.is_empty() { stderr } else { stdout };
if !version.status.success()
|| !(reported == frontend_client::GOOSE_NODE_VERSION
|| reported
.split_whitespace()
.any(|part| part.trim_start_matches('v') == frontend_client::GOOSE_NODE_VERSION))
{
anyhow::bail!(
"protocol_version_mismatch: Node {} is required for stock Goose {}, found `{}`",
frontend_client::GOOSE_NODE_VERSION,
frontend_client::GOOSE_TUI_VERSION,
if reported.is_empty() {
"unavailable"
} else {
&reported
}
);
}
let bridge = GooseAcpBridge::start(runtime).await?;
let executable = std::env::current_exe().context("resolving the Volter Harness executable")?;
let mut child = frontend_client::FrontendClientId::Goose
.spec()
.goose_launch_command(
cwd,
&root,
&executable,
&pinned_tui,
&bridge.address.to_string(),
&bridge.nonce,
)
.map_err(anyhow::Error::msg)?;
let mut terminal = terminal_snapshot::TerminalSnapshot::capture()
.context("capturing terminal state before launching frontend")?;
let mut bridge = Some(bridge);
let status: Result<std::process::ExitStatus> = match child.spawn().with_context(|| {
format!(
"launching pinned stock Goose {}",
frontend_client::GOOSE_TUI_VERSION
)
}) {
Ok(mut spawned) => {
match terminal_snapshot::ForegroundProcessGroup::transfer_to(spawned.id())
.context("giving the stock frontend foreground terminal ownership")
{
Ok(mut foreground) => {
let status = wait_for_goose_frontend(
spawned,
bridge.take().expect("Goose bridge exists"),
disconnect_monitor,
)
.await;
match foreground
.restore()
.context("returning foreground terminal ownership to Volter Harness")
{
Ok(()) => status,
Err(error) => Err(error),
}
}
Err(error) => reap_frontend_after_transfer_failure(&mut spawned, error).await,
}
}
Err(error) => Err(error),
};
drop(bridge);
let preference_writes = goose_preference_inventory(&root)?;
if !quiet && !preference_writes.is_empty() {
eprintln!(
"stock Goose isolated preference writes: {}",
preference_writes
.iter()
.map(|path| path.display().to_string())
.collect::<Vec<_>>()
.join(", ")
);
}
let status = match status {
Ok(status) if status.success() => {
terminal
.restore()
.context("restoring terminal state after frontend exit")?;
status
}
Ok(status) => {
terminal
.restore_after_abnormal_exit()
.context("restoring terminal state after frontend crash")?;
status
}
Err(error) => {
terminal
.restore_after_abnormal_exit()
.context("restoring terminal state after frontend failure")?;
return Err(error);
}
};
if !status.success() {
anyhow::bail!("stock Goose frontend exited with {status}");
}
Ok(preference_writes)
}
async fn run_stock_codex_frontend(
runtime: Arc<dyn supercode_harness::SdkRuntime>,
runtime_id: &str,
cwd: &Path,
compatibility: FrontendCompatibilityArg,
quiet: bool,
disconnect_monitor: Option<Arc<supercode_harness::HttpFrontendRuntime>>,
) -> Result<Vec<PathBuf>> {
#[cfg(unix)]
let frontend_temp_root = PathBuf::from("/tmp");
#[cfg(not(unix))]
let frontend_temp_root = std::env::temp_dir();
let root = frontend_temp_root.join(format!(
"sc-cx-{}-{:x}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos()
));
create_private_frontend_dir(&root)?;
let _root_cleanup = EphemeralFrontendRoot(root.clone());
let client_home = root.join("client-home");
create_private_frontend_dir(&client_home)?;
seed_stock_codex_frontend_config(&client_home)?;
let mut version_command = tokio::process::Command::new("codex");
version_command.arg("--version");
frontend_client::isolate_stock_codex_environment(&mut version_command, &client_home);
let version = match version_command.output().await {
Ok(version) => version,
Err(error) => return Err(error).context("locating the pinned stock Codex client"),
};
let reported = String::from_utf8_lossy(&version.stdout).trim().to_owned();
if !version.status.success()
|| reported != format!("codex-cli {}", supercode_codex_frontend::CODEX_CLI_VERSION)
{
anyhow::bail!(
"protocol_version_mismatch: stock Codex {} is required, found `{}`",
supercode_codex_frontend::CODEX_CLI_VERSION,
if reported.is_empty() {
"unavailable"
} else {
&reported
}
);
}
let mode = match compatibility {
FrontendCompatibilityArg::TracedOnly => {
supercode_codex_frontend::CodexCompatibilityMode::TracedOnly
}
FrontendCompatibilityArg::SchemaExtended => {
supercode_codex_frontend::CodexCompatibilityMode::SchemaExtended
}
};
let (endpoint, bootstrap) =
supercode_codex_frontend::CodexEndpoint::with_mode(runtime.clone(), runtime_id, cwd, mode)?;
let endpoint = endpoint.with_client_home(&client_home)?;
#[cfg(unix)]
let bootstrap_file = endpoint.write_bootstrap_file(&bootstrap, &root)?;
#[cfg(unix)]
let mut credential =
endpoint.issue_interactive_from_file(&bootstrap_file, "stock-codex-tui")?;
#[cfg(not(unix))]
let mut credential = endpoint.issue_interactive(&bootstrap, "stock-codex-tui")?;
let (handle, remote) = {
let handle = endpoint.bind_websocket(0).await?;
let address = match handle.kind() {
supercode_codex_frontend::CodexEndpointKind::WebSocket(address) => *address,
#[cfg(unix)]
supercode_codex_frontend::CodexEndpointKind::Unix(_) => unreachable!(),
};
(handle, format!("ws://{address}"))
};
let mut child = frontend_client::FrontendClientId::Codex
.spec()
.codex_launch_command(cwd, &client_home, &remote)
.map_err(anyhow::Error::msg)?;
let mut terminal = terminal_snapshot::TerminalSnapshot::capture()
.context("capturing terminal state before launching frontend")?;
let status: Result<std::process::ExitStatus> = match credential
.spawn_tokio_child(&mut child, frontend_client::CODEX_REMOTE_TOKEN_ENV)
.context("launching the pinned stock Codex frontend")
{
Ok(mut spawned) => {
match terminal_snapshot::ForegroundProcessGroup::transfer_to(spawned.id())
.context("giving the stock frontend foreground terminal ownership")
{
Ok(mut foreground) => {
let status = wait_for_stock_frontend(spawned, disconnect_monitor).await;
match foreground
.restore()
.context("returning foreground terminal ownership to Volter Harness")
{
Ok(()) => status,
Err(error) => Err(error),
}
}
Err(error) => reap_frontend_after_transfer_failure(&mut spawned, error).await,
}
}
Err(error) => Err(error),
};
handle.shutdown().await;
#[cfg(unix)]
let _ = endpoint
.revoke_frontend_from_file(&bootstrap_file, &mut credential)
.await;
#[cfg(not(unix))]
let _ = endpoint.revoke_frontend(&bootstrap, &mut credential).await;
let preference_writes = relative_file_inventory(&client_home)?;
if !quiet && !preference_writes.is_empty() {
eprintln!(
"stock Codex isolated preference writes: {}",
preference_writes
.iter()
.map(|path| path.display().to_string())
.collect::<Vec<_>>()
.join(", ")
);
}
let status = match status {
Ok(status) if status.success() => {
terminal
.restore()
.context("restoring terminal state after frontend exit")?;
status
}
Ok(status) => {
terminal
.restore_after_abnormal_exit()
.context("restoring terminal state after frontend crash")?;
status
}
Err(error) => {
terminal
.restore_after_abnormal_exit()
.context("restoring terminal state after frontend failure")?;
return Err(error);
}
};
if !status.success() {
anyhow::bail!("stock Codex frontend exited with {status}");
}
Ok(preference_writes)
}
async fn run_stock_opencode_frontend(
runtime: Arc<dyn supercode_harness::SdkRuntime>,
runtime_id: &str,
cwd: &Path,
quiet: bool,
disconnect_monitor: Option<Arc<supercode_harness::HttpFrontendRuntime>>,
) -> Result<Vec<PathBuf>> {
#[cfg(unix)]
let frontend_temp_root = PathBuf::from("/tmp");
#[cfg(not(unix))]
let frontend_temp_root = std::env::temp_dir();
let root = frontend_temp_root.join(format!(
"sc-oc-{}-{:x}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos()
));
create_private_frontend_dir(&root)?;
let _root_cleanup = EphemeralFrontendRoot(root.clone());
for child in [
"home",
"tmp",
"config",
"data",
"state",
"cache",
"opencode-config",
] {
create_private_frontend_dir(&root.join(child))?;
}
let mut version_command = tokio::process::Command::new("opencode");
version_command.arg("--version").kill_on_drop(true);
frontend_client::isolate_stock_opencode_environment(&mut version_command, &root);
let version = tokio::time::timeout(Duration::from_secs(5), version_command.output())
.await
.context("stock OpenCode version probe exceeded the five-second deadline")?
.context("locating the pinned stock OpenCode client")?;
let stdout = String::from_utf8_lossy(&version.stdout).trim().to_owned();
let stderr = String::from_utf8_lossy(&version.stderr).trim().to_owned();
let reported = if stdout.is_empty() { stderr } else { stdout };
if !version.status.success() || reported != supercode_opencode_frontend::OPENCODE_CLI_VERSION {
anyhow::bail!(
"protocol_version_mismatch: stock OpenCode {} is required, found `{}`",
supercode_opencode_frontend::OPENCODE_CLI_VERSION,
if reported.is_empty() {
"unavailable"
} else {
&reported
}
);
}
let (endpoint, credential) =
supercode_opencode_frontend::OpenCodeEndpoint::new(runtime, runtime_id, cwd)?;
let handle = endpoint.bind(0).await?;
let mut child = frontend_client::FrontendClientId::Opencode
.spec()
.opencode_launch_command(cwd, &root, &handle.url(), endpoint.session_id())
.map_err(anyhow::Error::msg)?;
let mut terminal = terminal_snapshot::TerminalSnapshot::capture()
.context("capturing terminal state before launching frontend")?;
let status: Result<std::process::ExitStatus> = match credential
.spawn_tokio_child(&mut child)
.context("launching the pinned stock OpenCode frontend")
{
Ok(mut spawned) => {
match terminal_snapshot::ForegroundProcessGroup::transfer_to(spawned.id())
.context("giving the stock frontend foreground terminal ownership")
{
Ok(mut foreground) => {
let status = wait_for_stock_frontend(spawned, disconnect_monitor).await;
match foreground
.restore()
.context("returning foreground terminal ownership to Volter Harness")
{
Ok(()) => status,
Err(error) => Err(error),
}
}
Err(error) => reap_frontend_after_transfer_failure(&mut spawned, error).await,
}
}
Err(error) => Err(error),
};
handle.shutdown().await;
let preference_writes = relative_file_inventory(&root)?;
if !quiet && !preference_writes.is_empty() {
eprintln!(
"stock OpenCode isolated preference writes: {}",
preference_writes
.iter()
.map(|path| path.display().to_string())
.collect::<Vec<_>>()
.join(", ")
);
}
let status = match status {
Ok(status) if status.success() => {
terminal
.restore()
.context("restoring terminal state after frontend exit")?;
status
}
Ok(status) => {
terminal
.restore_after_abnormal_exit()
.context("restoring terminal state after frontend crash")?;
status
}
Err(error) => {
terminal
.restore_after_abnormal_exit()
.context("restoring terminal state after frontend failure")?;
return Err(error);
}
};
if !status.success() {
anyhow::bail!("stock OpenCode frontend exited with {status}");
}
Ok(preference_writes)
}
async fn wait_for_stock_frontend(
child: tokio::process::Child,
disconnect_monitor: Option<Arc<supercode_harness::HttpFrontendRuntime>>,
) -> Result<std::process::ExitStatus> {
let monitor_disconnects = disconnect_monitor.is_some();
wait_for_stock_frontend_with(
child,
move || {
disconnect_monitor
.as_ref()
.is_some_and(|runtime| runtime.is_disconnected())
},
monitor_disconnects,
frontend_host_shutdown_signal(),
)
.await
}
async fn wait_for_direct_frontend(
mut child: tokio::process::Child,
disconnect_monitor: Option<Arc<supercode_harness::HttpFrontendRuntime>>,
) -> Result<std::process::ExitStatus> {
let monitor_disconnects = disconnect_monitor.is_some();
let shutdown = frontend_host_shutdown_signal();
tokio::pin!(shutdown);
let mut liveness = tokio::time::interval(std::time::Duration::from_millis(100));
loop {
tokio::select! {
status = child.wait() => return status.map_err(Into::into),
signal = &mut shutdown => {
child.start_kill().ok();
let _ = child.wait().await;
anyhow::bail!("frontend host received {signal}; SDK runtime remains alive");
}
_ = liveness.tick(), if monitor_disconnects => {
if disconnect_monitor
.as_ref()
.is_some_and(|runtime| runtime.is_disconnected())
{
child.start_kill().ok();
let _ = child.wait().await;
anyhow::bail!("frontend adapter disconnected; SDK runtime remains independently alive");
}
}
}
}
}
async fn wait_for_goose_frontend(
mut child: tokio::process::Child,
mut bridge: GooseAcpBridge,
disconnect_monitor: Option<Arc<supercode_harness::HttpFrontendRuntime>>,
) -> Result<std::process::ExitStatus> {
let monitor_disconnects = disconnect_monitor.is_some();
let mut liveness = tokio::time::interval(std::time::Duration::from_millis(100));
let shutdown = frontend_host_shutdown_signal();
tokio::pin!(shutdown);
loop {
tokio::select! {
biased;
status = child.wait() => {
let status = status?;
bridge.finish().await?;
return Ok(status);
}
joined = bridge.task.as_mut().expect("Goose bridge task exists") => {
bridge.task.take();
match joined.context("joining the Goose ACP bridge task")? {
Ok(()) => {
match tokio::time::timeout(Duration::from_millis(250), child.wait()).await {
Ok(status) => return Ok(status?),
Err(_) => {
kill_frontend_process_group(&mut child);
let _ = child.wait().await;
anyhow::bail!(
"Goose ACP bridge closed before the stock frontend exited"
);
}
}
}
Err(error) => {
kill_frontend_process_group(&mut child);
let _ = child.wait().await;
return Err(error.context(
"Goose ACP bridge failed before the stock frontend exited"
));
}
}
}
signal = &mut shutdown => {
kill_frontend_process_group(&mut child);
let _ = child.wait().await;
anyhow::bail!("frontend host received {signal}; SDK runtime remains alive");
}
_ = liveness.tick(), if monitor_disconnects => {
if disconnect_monitor
.as_ref()
.is_some_and(|runtime| runtime.is_disconnected())
{
kill_frontend_process_group(&mut child);
let _ = child.wait().await;
anyhow::bail!(
"frontend adapter disconnected; SDK runtime remains independently alive"
);
}
}
}
}
}
async fn reap_frontend_after_transfer_failure(
child: &mut tokio::process::Child,
error: anyhow::Error,
) -> Result<std::process::ExitStatus> {
kill_frontend_process_group(child);
let _ = child.wait().await;
Err(error)
}
async fn wait_for_stock_frontend_with<F, S>(
mut child: tokio::process::Child,
mut disconnected: F,
monitor_disconnects: bool,
shutdown: S,
) -> Result<std::process::ExitStatus>
where
F: FnMut() -> bool,
S: std::future::Future<Output = &'static str>,
{
tokio::pin!(shutdown);
let mut liveness = tokio::time::interval(std::time::Duration::from_millis(100));
loop {
tokio::select! {
status = child.wait() => return status.map_err(Into::into),
signal = &mut shutdown => {
kill_frontend_process_group(&mut child);
let _ = child.wait().await;
anyhow::bail!("frontend host received {signal}; SDK runtime remains alive");
}
_ = liveness.tick(), if monitor_disconnects => {
if disconnected() {
kill_frontend_process_group(&mut child);
let _ = child.wait().await;
anyhow::bail!("frontend adapter disconnected; SDK runtime remains independently alive");
}
}
}
}
}
fn kill_frontend_process_group(child: &mut tokio::process::Child) {
#[cfg(unix)]
if let Some(pid) = child.id() {
unsafe {
libc::kill(-(pid as libc::pid_t), libc::SIGKILL);
}
}
child.start_kill().ok();
}
async fn frontend_host_shutdown_signal() -> &'static str {
#[cfg(unix)]
{
use tokio::signal::unix::{signal, SignalKind};
let mut terminate = signal(SignalKind::terminate()).expect("install SIGTERM handler");
let mut hangup = signal(SignalKind::hangup()).expect("install SIGHUP handler");
tokio::select! {
_ = tokio::signal::ctrl_c() => "SIGINT",
_ = terminate.recv() => "SIGTERM",
_ = hangup.recv() => "SIGHUP",
}
}
#[cfg(not(unix))]
{
let _ = tokio::signal::ctrl_c().await;
"interrupt"
}
}
fn create_private_frontend_dir(path: &Path) -> Result<()> {
#[cfg(unix)]
{
use std::os::unix::fs::{DirBuilderExt, MetadataExt, PermissionsExt};
let mut builder = std::fs::DirBuilder::new();
builder.mode(0o700);
builder
.create(path)
.with_context(|| format!("creating private frontend directory `{}`", path.display()))?;
let metadata = std::fs::symlink_metadata(path)?;
if metadata.file_type().is_symlink()
|| !metadata.is_dir()
|| metadata.permissions().mode() & 0o777 != 0o700
|| metadata.uid() != unsafe { libc::geteuid() }
{
std::fs::remove_dir(path).ok();
anyhow::bail!("private frontend directory failed owner/mode verification");
}
}
#[cfg(windows)]
create_private_windows_directory(path)?;
#[cfg(not(any(unix, windows)))]
std::fs::create_dir(path)
.with_context(|| format!("creating private frontend directory `{}`", path.display()))?;
Ok(())
}
#[cfg(windows)]
struct WindowsSecurityDescriptor {
value: windows_sys::Win32::Security::PSECURITY_DESCRIPTOR,
user_sid: String,
}
#[cfg(windows)]
impl Drop for WindowsSecurityDescriptor {
fn drop(&mut self) {
unsafe {
windows_sys::Win32::Foundation::LocalFree(self.value);
}
}
}
#[cfg(windows)]
fn windows_wide(value: &std::ffi::OsStr) -> Vec<u16> {
use std::os::windows::ffi::OsStrExt;
value.encode_wide().chain(std::iter::once(0)).collect()
}
#[cfg(windows)]
fn current_windows_user_sid() -> std::io::Result<String> {
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE};
use windows_sys::Win32::Security::Authorization::ConvertSidToStringSidW;
use windows_sys::Win32::Security::{GetTokenInformation, TokenUser, TOKEN_QUERY, TOKEN_USER};
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
let mut token: HANDLE = std::ptr::null_mut();
if unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token) } == 0 {
return Err(std::io::Error::last_os_error());
}
let result = (|| {
let mut bytes = 0u32;
unsafe {
GetTokenInformation(token, TokenUser, std::ptr::null_mut(), 0, &mut bytes);
}
if bytes == 0 {
return Err(std::io::Error::last_os_error());
}
let mut buffer = vec![0u8; bytes as usize];
if unsafe {
GetTokenInformation(
token,
TokenUser,
buffer.as_mut_ptr().cast(),
bytes,
&mut bytes,
)
} == 0
{
return Err(std::io::Error::last_os_error());
}
let token_user = unsafe { &*(buffer.as_ptr().cast::<TOKEN_USER>()) };
let mut text = std::ptr::null_mut();
if unsafe { ConvertSidToStringSidW(token_user.User.Sid, &mut text) } == 0 {
return Err(std::io::Error::last_os_error());
}
let length = unsafe {
let mut length = 0usize;
while *text.add(length) != 0 {
length += 1;
}
length
};
let sid = String::from_utf16(unsafe { std::slice::from_raw_parts(text, length) })
.map_err(|error| std::io::Error::new(std::io::ErrorKind::InvalidData, error));
unsafe {
windows_sys::Win32::Foundation::LocalFree(text.cast());
}
sid
})();
unsafe {
CloseHandle(token);
}
result
}
#[cfg(windows)]
fn private_windows_security_descriptor() -> std::io::Result<WindowsSecurityDescriptor> {
use windows_sys::Win32::Security::Authorization::{
ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1,
};
let user_sid = current_windows_user_sid()?;
let sddl = format!("D:P(A;OICI;FA;;;SY)(A;OICI;FA;;;{user_sid})");
let sddl = windows_wide(std::ffi::OsStr::new(&sddl));
let mut value = std::ptr::null_mut();
if unsafe {
ConvertStringSecurityDescriptorToSecurityDescriptorW(
sddl.as_ptr(),
SDDL_REVISION_1,
&mut value,
std::ptr::null_mut(),
)
} == 0
{
return Err(std::io::Error::last_os_error());
}
Ok(WindowsSecurityDescriptor { value, user_sid })
}
#[cfg(windows)]
fn verify_private_windows_dacl(path: &Path, user_sid: &str) -> std::io::Result<()> {
use windows_sys::Win32::Foundation::LocalFree;
use windows_sys::Win32::Security::Authorization::{
ConvertStringSidToSidW, GetNamedSecurityInfoW, SE_FILE_OBJECT,
};
use windows_sys::Win32::Security::{
AclSizeInformation, EqualSid, GetAce, GetAclInformation, GetSecurityDescriptorControl,
GetSecurityDescriptorDacl, ACCESS_ALLOWED_ACE, ACL_SIZE_INFORMATION,
DACL_SECURITY_INFORMATION, INHERITED_ACE, SE_DACL_PROTECTED,
};
use windows_sys::Win32::Storage::FileSystem::FILE_ALL_ACCESS;
let path = windows_wide(path.as_os_str());
let mut descriptor = std::ptr::null_mut();
let status = unsafe {
GetNamedSecurityInfoW(
path.as_ptr(),
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION,
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut descriptor,
)
};
if status != 0 {
return Err(std::io::Error::from_raw_os_error(status as i32));
}
let user_sid = windows_wide(std::ffi::OsStr::new(user_sid));
let system_sid = windows_wide(std::ffi::OsStr::new("S-1-5-18"));
let mut user_sid_value = std::ptr::null_mut();
let mut system_sid_value = std::ptr::null_mut();
if unsafe { ConvertStringSidToSidW(user_sid.as_ptr(), &mut user_sid_value) } == 0
|| unsafe { ConvertStringSidToSidW(system_sid.as_ptr(), &mut system_sid_value) } == 0
{
let error = std::io::Error::last_os_error();
unsafe {
if !user_sid_value.is_null() {
LocalFree(user_sid_value.cast());
}
if !system_sid_value.is_null() {
LocalFree(system_sid_value.cast());
}
LocalFree(descriptor);
}
return Err(error);
}
let result = (|| {
let mut control = 0u16;
let mut revision = 0u32;
if unsafe { GetSecurityDescriptorControl(descriptor, &mut control, &mut revision) } == 0 {
return Err(std::io::Error::last_os_error());
}
if control & SE_DACL_PROTECTED == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"frontend DACL inherited access entries",
));
}
let mut present = 0;
let mut defaulted = 0;
let mut dacl = std::ptr::null_mut();
if unsafe { GetSecurityDescriptorDacl(descriptor, &mut present, &mut dacl, &mut defaulted) }
== 0
|| present == 0
|| defaulted != 0
|| dacl.is_null()
{
return Err(std::io::Error::last_os_error());
}
let mut info = ACL_SIZE_INFORMATION {
AceCount: 0,
AclBytesInUse: 0,
AclBytesFree: 0,
};
if unsafe {
GetAclInformation(
dacl,
std::ptr::addr_of_mut!(info).cast(),
std::mem::size_of::<ACL_SIZE_INFORMATION>() as u32,
AclSizeInformation,
)
} == 0
{
return Err(std::io::Error::last_os_error());
}
if info.AceCount != 2 {
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"frontend DACL is not restricted to the current user and SYSTEM",
));
}
let mut saw_user = false;
let mut saw_system = false;
for index in 0..info.AceCount {
let mut raw_ace = std::ptr::null_mut();
if unsafe { GetAce(dacl, index, &mut raw_ace) } == 0 {
return Err(std::io::Error::last_os_error());
}
let ace = unsafe { &*raw_ace.cast::<ACCESS_ALLOWED_ACE>() };
if ace.Header.AceType != 0
|| ace.Header.AceFlags & INHERITED_ACE as u8 != 0
|| ace.Mask != FILE_ALL_ACCESS
{
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"frontend DACL contains an unexpected access entry",
));
}
let sid = std::ptr::addr_of!(ace.SidStart).cast_mut().cast();
if unsafe { EqualSid(sid, user_sid_value) } != 0 && !saw_user {
saw_user = true;
} else if unsafe { EqualSid(sid, system_sid_value) } != 0 && !saw_system {
saw_system = true;
} else {
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"frontend DACL contains a duplicate or foreign SID",
));
}
}
if !saw_user || !saw_system {
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"frontend DACL omits the current user or SYSTEM",
));
}
Ok(())
})();
unsafe {
LocalFree(user_sid_value.cast());
LocalFree(system_sid_value.cast());
LocalFree(descriptor);
}
result
}
#[cfg(windows)]
fn create_private_windows_directory(path: &Path) -> std::io::Result<()> {
use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;
use windows_sys::Win32::Storage::FileSystem::CreateDirectoryW;
let descriptor = private_windows_security_descriptor()?;
let attributes = SECURITY_ATTRIBUTES {
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: descriptor.value,
bInheritHandle: 0,
};
let path_wide = windows_wide(path.as_os_str());
if unsafe { CreateDirectoryW(path_wide.as_ptr(), &attributes) } == 0 {
return Err(std::io::Error::last_os_error());
}
if let Err(error) = verify_private_windows_dacl(path, &descriptor.user_sid) {
std::fs::remove_dir(path).ok();
return Err(error);
}
Ok(())
}
fn write_private_frontend_file(path: &Path, contents: &[u8]) -> Result<()> {
use std::io::Write;
#[cfg(unix)]
let mut file = {
use std::os::unix::fs::OpenOptionsExt;
let mut options = std::fs::OpenOptions::new();
options
.write(true)
.create_new(true)
.mode(0o600)
.custom_flags(libc::O_NOFOLLOW | libc::O_CLOEXEC);
options.open(path)?
};
#[cfg(windows)]
let mut file = {
use std::os::windows::io::FromRawHandle;
use windows_sys::Win32::Foundation::{GENERIC_WRITE, INVALID_HANDLE_VALUE};
use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;
use windows_sys::Win32::Storage::FileSystem::{
CreateFileW, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,
};
let descriptor = private_windows_security_descriptor()?;
let attributes = SECURITY_ATTRIBUTES {
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: descriptor.value,
bInheritHandle: 0,
};
let path_wide = windows_wide(path.as_os_str());
let handle = unsafe {
CreateFileW(
path_wide.as_ptr(),
GENERIC_WRITE,
0,
&attributes,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
std::ptr::null_mut(),
)
};
if handle == INVALID_HANDLE_VALUE {
return Err(std::io::Error::last_os_error().into());
}
let file = unsafe { std::fs::File::from_raw_handle(handle) };
if let Err(error) = verify_private_windows_dacl(path, &descriptor.user_sid) {
drop(file);
std::fs::remove_file(path).ok();
return Err(error.into());
}
file
};
#[cfg(not(any(unix, windows)))]
let mut file = std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(path)?;
file.write_all(contents)?;
file.sync_all()?;
Ok(())
}
fn seed_stock_codex_frontend_config(client_home: &Path) -> Result<()> {
const CONFIG: &str = r#"model_provider = "supercode_frontend"
[model_providers.supercode_frontend]
name = "Volter Harness SDK runtime"
base_url = "http://127.0.0.1:1/unused"
wire_api = "responses"
requires_openai_auth = false
request_max_retries = 0
stream_max_retries = 0
"#;
let path = client_home.join("config.toml");
write_private_frontend_file(&path, CONFIG.as_bytes())
.with_context(|| format!("seeding isolated stock Codex config `{}`", path.display()))
}
fn relative_file_inventory(root: &Path) -> Result<Vec<PathBuf>> {
fn walk(base: &Path, directory: &Path, files: &mut Vec<PathBuf>) -> Result<()> {
for entry in std::fs::read_dir(directory)? {
let entry = entry?;
let metadata = std::fs::symlink_metadata(entry.path())?;
if metadata.file_type().is_symlink() {
files.push(entry.path().strip_prefix(base)?.to_path_buf());
} else if metadata.is_dir() {
walk(base, &entry.path(), files)?;
} else if metadata.is_file() {
files.push(entry.path().strip_prefix(base)?.to_path_buf());
}
}
Ok(())
}
let mut files = Vec::new();
walk(root, root, &mut files)?;
files.sort();
Ok(files)
}
fn goose_preference_inventory(root: &Path) -> Result<Vec<PathBuf>> {
let mut files = Vec::new();
for state_root in ["home", "tmp", "config", "data", "state", "cache", "goose"] {
files.extend(
relative_file_inventory(&root.join(state_root))?
.into_iter()
.map(|relative| PathBuf::from(state_root).join(relative)),
);
}
files.sort();
Ok(files)
}
async fn run_attachable_terminal(
runtime: Arc<supercode_harness::server::RpcEngine>,
source: supercode_harness::LiveRuntimeSource,
) -> Result<()> {
let token: Arc<str> = supercode_harness::server::generate_token().into();
let address =
supercode_harness::server::run_http(runtime.clone(), "127.0.0.1:0", token.clone())
.await
.context("binding the attachable terminal runtime")?;
let _registration = supercode_harness::register_live_runtime_with_metadata(
runtime.session_id(),
source,
format!("http://{address}"),
token.to_string(),
live_runtime_metadata(&runtime)?,
)?;
supercode_frontend_tui::app::run(runtime)
.await
.map(|_| ())
.map_err(Into::into)
}
fn running_tmux_session() -> Option<String> {
std::env::var_os("TMUX")?;
let output = std::process::Command::new("tmux")
.args(["display-message", "-p", "#S"])
.output()
.ok()?;
let name = String::from_utf8_lossy(&output.stdout).trim().to_string();
(output.status.success() && !name.is_empty()).then_some(name)
}
fn live_runtime_metadata(
runtime: &supercode_harness::server::RpcEngine,
) -> Result<supercode_harness::LiveRuntimeMetadata> {
let descriptor = runtime.frontend_descriptor();
Ok(supercode_harness::LiveRuntimeMetadata {
profile: descriptor.emulation_profile,
persistence_location: Some(session_store()?.session_path(runtime.session_id())?),
endpoint_capabilities: vec!["http".into(), "acp".into()],
supervisor: running_tmux_session()
.map(|session_name| supercode_harness::LiveRuntimeSupervisor::Tmux { session_name }),
})
}
async fn serve_cmd(cli: &Cli, bind_arg: Option<String>, token_arg: Option<String>) -> Result<()> {
maybe_onboard(cli).await;
require_api_key(cli)?;
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let fc = uc::load(&cwd);
let mut config = resolve_config(cli)?;
require_server_capability(&fc, &config, "serve")?;
print_startup_banners(cli, &config);
let cap = fc.capabilities.get("server").cloned().unwrap_or_default();
config.approval_handler = None;
let span_summary_gate = SpanSummaryGate::capture(&config);
let mut agent = supercode_harness::create_agent(config)?;
maybe_install_span_summarizer(&mut agent, &span_summary_gate);
let frontend_requests = supercode_harness::server::FrontendRequestBridge::new();
attach_mcp_with_elicitation(
cli,
&mut agent,
Some(frontend_requests.elicitation_handler()),
)
.await;
let session_name = open_session(cli, &mut agent, &cwd)?;
let bind = bind_arg
.or_else(|| supercode_harness::configfile::server_bind(&cap))
.unwrap_or_else(|| "127.0.0.1:0".to_string());
if !is_loopback_bind(&bind) {
eprintln!(
"\x1b[33mwarning: binding `{bind}` exposes the supercode RPC/HTTP server beyond \
localhost — anyone who can reach this host+port and knows/guesses the token can \
drive this agent (tool calls still run under the SAME permission/sandbox posture \
as a local user — see `serve`'s no-bypass guarantee — but a remote attacker can \
still spend your API budget and read/write anything your permission policy allows). \
Loopback (127.0.0.1) is the safe default; this is an explicit opt-in.\x1b[0m"
);
}
let token: Arc<str> = token_arg
.or_else(|| std::env::var("SUPERCODE_SERVER_TOKEN").ok())
.or_else(|| supercode_harness::configfile::server_token(&cap))
.unwrap_or_else(supercode_harness::server::generate_token)
.into();
let runtime_name = session_name.clone();
let engine = supercode_harness::server::RpcEngine::new_named_with_frontend_bridge(
agent,
runtime_name,
supercode_harness::FrontendRuntimeMetadata::default(),
frontend_requests,
Some(Box::new(move |a: &SdkAgent| {
persist_session(a, &session_name);
apply_auto_title(a, &session_name);
})),
);
let addr = supercode_harness::server::run_http(engine.clone(), &bind, token.clone())
.await
.with_context(|| format!("binding the server to {bind}"))?;
let _registration = if addr.ip().is_loopback() {
Some(supercode_harness::register_live_runtime_with_metadata(
engine.session_id(),
supercode_harness::LiveRuntimeSource {
harness: "supercode".into(),
session_id: engine.session_id().to_string(),
workspace: cwd.clone(),
},
format!("http://{addr}"),
token.to_string(),
live_runtime_metadata(&engine)?,
)?)
} else {
None
};
eprintln!("supercode server listening on http://{addr} (POST /rpc, GET /events)");
eprintln!(
"authentication: bearer required; deliver it out of band through SUPERCODE_SERVER_TOKEN (the value is not logged)"
);
tokio::select! {
_ = tokio::signal::ctrl_c() => {}
_ = engine.wait_for_shutdown() => {}
}
Ok(())
}
fn resume_picker_candidates(
index: &[supercode_harness::store::SessionIndexEntry],
cwd: &Path,
scope: PickerScope,
) -> Vec<(String, picker::PickerItem)> {
let tag = cwd_tag(cwd);
index
.iter()
.filter(|e| {
!e.archived && (matches!(scope, PickerScope::Everywhere) || e.name.starts_with(&tag))
})
.take(picker::MAX_ITEMS)
.map(|e| {
let label = format!("{:<20} {:>9}", short_id(&e.name), age_of(e.time_secs));
let preview = if e.preview.is_empty() {
format!("{} message(s)", e.messages)
} else {
format!("{} · {} message(s)", e.preview, e.messages)
};
(
e.name.clone(),
picker::PickerItem::new(label, e.title.clone()).with_preview(preview),
)
})
.collect()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PickerScope {
Cwd,
Everywhere,
}
async fn resume_picker_cmd(cli: &Cli, prompt: Option<String>) -> Result<()> {
maybe_onboard(cli).await;
require_api_key(cli)?;
let store = session_store()?;
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let index = store.index().entries;
let mut scope = PickerScope::Cwd;
let chosen = loop {
let candidates = resume_picker_candidates(&index, &cwd, scope);
if candidates.is_empty() {
if scope == PickerScope::Cwd && index.iter().any(|e| !e.archived) {
scope = PickerScope::Everywhere;
continue;
}
anyhow::bail!(
"no saved sessions to resume — run `supercode run \"…\"` or \
`supercode chat` to create one"
);
}
let items: Vec<picker::PickerItem> =
candidates.iter().map(|(_, item)| item.clone()).collect();
let widen = match scope {
PickerScope::Cwd => Some("sessions from every directory"),
PickerScope::Everywhere => None,
};
let header = match scope {
PickerScope::Cwd => "resume which session?",
PickerScope::Everywhere => "resume which session? (all directories)",
};
match picker::pick_searchable(header, &items, widen)? {
picker::PickOutcome::Selected(i) => break candidates[i].0.clone(),
picker::PickOutcome::Widen => {
scope = PickerScope::Everywhere;
continue;
}
picker::PickOutcome::Cancelled => {
eprintln!("(cancelled)");
return Ok(());
}
}
};
let name = resolve_session(&store, &chosen)?;
let quiet = effective_quiet(cli);
let (mut agent, spinner) = build_agent(cli, true, None)?;
let frontend_bridge = if prompt.is_none() && tui::should_activate(agent.config()) {
Some(supercode_harness::server::FrontendRequestBridge::new())
} else {
None
};
let elicitation = frontend_bridge
.as_ref()
.map(supercode_harness::server::FrontendRequestBridge::elicitation_handler);
attach_mcp_with_elicitation(cli, &mut agent, elicitation).await;
upgrade_session_format(&store, &name, quiet);
let jsonl = store.load(&name)?;
let tmp = std::env::temp_dir().join(format!("sc-resume-pick-{}.jsonl", std::process::id()));
std::fs::write(&tmp, jsonl)?;
agent.load_transcript(&tmp)?;
let _ = std::fs::remove_file(&tmp);
arm_context_guard(&mut agent);
arm_session_state(&mut agent, &store, &name, quiet);
if !quiet {
eprintln!(
"Resuming {} ({} messages).",
short_id(&name),
agent.history().len()
);
}
match prompt {
Some(p) => {
hooks::fire_user_prompt_submit(
&hooks::load_hook_set(&cwd),
&p,
&cwd,
Some(&name),
quiet,
);
spinner.reset_turn(); spinner.start("Thinking…");
let outcome = race_ctrl_c(
supercode_harness::submit_agent(&mut agent, &p),
Some(&spinner),
)
.await;
spinner.stop();
spinner.clear_counter(); let Some(result) = outcome else {
print_interrupt_notice(
quiet,
"turn cancelled; session left as it was before this turn",
);
std::process::exit(130);
};
result?;
persist_session(&agent, &name);
apply_auto_title(&agent, &name);
println!();
Ok(())
}
None => {
let model = effective_model(cli);
let notify_settings = effective_notify(cli, &uc::load(&cwd));
if let Some(frontend_bridge) = frontend_bridge {
let persist_name = name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named_with_frontend_bridge(
agent,
name.clone(),
supercode_harness::FrontendRuntimeMetadata::default(),
frontend_bridge,
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
let result = run_attachable_terminal(
runtime.clone(),
supercode_harness::LiveRuntimeSource {
harness: "supercode".into(),
session_id: name.clone(),
workspace: cwd.clone(),
},
)
.await;
runtime
.finalize_with(|agent| persist_session(agent, &name))
.await;
runtime.shutdown().await;
result
} else {
chat(
&mut agent,
Some(&name),
&model,
&spinner,
quiet,
¬ify_settings,
)
.await
}
}
}
}
async fn watch_cmd(
file: &Path,
opencode_session: Option<&str>,
poll_ms: u64,
max_events: Option<u64>,
) -> Result<()> {
if poll_ms == 0 {
anyhow::bail!("watch: --poll-ms must be greater than zero");
}
if max_events == Some(0) {
return Ok(());
}
let mut follower = SessionFollower::open(file, opencode_session)
.with_context(|| format!("opening session follower for {}", file.display()))?;
let mut emitted = 0_u64;
let delay = std::time::Duration::from_millis(poll_ms);
loop {
if let Some(event) = follower.poll()? {
println!("{}", event.to_json());
io::stdout().flush()?;
emitted += 1;
if max_events.is_some_and(|limit| emitted >= limit) {
return Ok(());
}
}
tokio::select! {
_ = tokio::time::sleep(delay) => {}
signal = tokio::signal::ctrl_c() => {
signal?;
return Ok(());
}
}
}
}
#[derive(Default)]
struct DiscoveryHomeOverrides {
claude_code: Option<PathBuf>,
codex: Option<PathBuf>,
pi: Option<PathBuf>,
opencode: Option<PathBuf>,
grok: Option<PathBuf>,
gemini: Option<PathBuf>,
goose: Option<PathBuf>,
supercode: Option<PathBuf>,
}
fn discovered_sessions_cmd(harnesses: &[String], profile: Option<&str>, json: bool) -> Result<()> {
let query = DiscoveryQuery {
harnesses: harnesses
.iter()
.map(|value| HarnessId::new(value.clone()))
.collect(),
profile: profile.map(str::to_string),
..DiscoveryQuery::default()
};
let sessions = supercode_harness::discover_session_page(&query)?.sessions;
if json {
println!("{}", serde_json::to_string_pretty(&sessions)?);
return Ok(());
}
if sessions.is_empty() {
let scope = if harnesses.is_empty() {
"any harness".to_string()
} else {
harnesses.join(", ")
};
println!();
println!(
" {} {}",
ui::paint(ui::DIM, "○"),
ui::paint(
ui::MUTED,
match profile {
Some(name) => format!("no {scope} conversation is routed through `{name}`"),
None => format!("no conversations discovered for {scope}"),
}
)
);
println!();
return Ok(());
}
println!();
println!(
"{}",
ui::section(&format!("conversations ({})", sessions.len()))
);
for session in &sessions {
let title = session
.title
.as_deref()
.map(str::trim)
.filter(|title| !title.is_empty())
.unwrap_or(&session.locator.session_id);
println!(
" {} {} {} {} {} {}",
ui::paint(
ui::ACCENT,
format!("{:<12}", session.locator.harness.as_str())
),
ui::paint(ui::DIM, format!("{:>9}", discovered_age(session))),
format!("{:<26}", surface_column(session)),
format!("{:<9}", trigger_column(session)),
ui::paint(
ui::MUTED,
format!("{:<10}", session.nouns.profile.as_deref().unwrap_or("—"))
),
title
);
}
println!();
Ok(())
}
fn approvals_list_cmd(harness: Option<&str>, session: Option<&str>, json: bool) -> Result<()> {
if let Some(harness) = harness {
if !supercode_harness::lists_approvals(harness) {
anyhow::bail!(
"approvals list: `{harness}` has no runtime door that carries an approval \
request (unsupported action) — known: {}",
supercode_harness::approval_harnesses().join(", ")
);
}
}
let query = supercode_harness::ApprovalsQuery {
harness: harness.map(str::to_string),
session: session.map(str::to_string),
};
let rows = supercode_harness::HarnessSessionService::new().approvals(&query);
if json {
println!("{}", serde_json::to_string_pretty(&rows)?);
return Ok(());
}
if rows.is_empty() {
println!();
println!(
" {} {}",
ui::paint(ui::DIM, "○"),
ui::paint(
ui::MUTED,
"no approval request is waiting on this process's runtimes"
)
);
println!(
" {}",
ui::paint(
ui::DIM,
"a request exists only while the turn it blocks is open, inside the process \
driving that runtime (`harness.v1.approvals.list` over `supercode harness serve`)",
)
);
println!();
return Ok(());
}
println!();
println!("{}", ui::section(&format!("approvals ({})", rows.len())));
for row in &rows {
println!(
" {} {} {} {} {} {}",
ui::paint(ui::ACCENT, format!("{:<12}", row.harness.as_str())),
ui::paint(ui::MUTED, format!("{:<9}", row.kind.as_str())),
format!("{:<9}", row.status.as_str()),
format!("{:<40}", truncate_path(&row.subject, 40)),
ui::paint(ui::MUTED, format!("{:>8}", approval_age(row.age_ms))),
ui::paint(ui::DIM, &row.id),
);
}
println!();
Ok(())
}
async fn approvals_resolve_cmd(id: &str, decision: ApprovalDecisionArg, json: bool) -> Result<()> {
let decision = supercode_harness::ApprovalDecision::from(decision);
let mut service = supercode_harness::HarnessSessionService::new();
let subject = service
.approvals(&supercode_harness::ApprovalsQuery::default())
.into_iter()
.find(|row| row.id == id)
.map(|row| row.subject);
let response = service
.handle_async(serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": "harness.v1.approvals.resolve",
"params": {"id": id, "decision": decision.as_str()},
}))
.await;
if let Some(error) = response.get("error") {
let message = error
.get("message")
.and_then(serde_json::Value::as_str)
.unwrap_or("approvals resolve failed");
anyhow::bail!("approvals resolve: {message}");
}
let result = &response["result"];
if json {
println!("{}", serde_json::to_string_pretty(result)?);
return Ok(());
}
println!();
println!(
" {} {}",
ui::paint(ui::ACCENT, "✓"),
subject.as_deref().unwrap_or(id),
);
println!(
" {} {} {} {}",
ui::paint(ui::MUTED, "decision"),
decision.as_str(),
ui::paint(ui::MUTED, "sent"),
result["option_id"].as_str().unwrap_or_default(),
);
println!(
" {}",
ui::paint(
ui::DIM,
"answered through the harness's own door \
(`harness.v1.runtimes.respond`)"
),
);
println!();
Ok(())
}
fn approval_age(age_ms: i64) -> String {
let seconds = age_ms.max(0) / 1_000;
if seconds < 60 {
format!("{seconds}s")
} else if seconds < 3_600 {
format!("{}m", seconds / 60)
} else if seconds < 86_400 {
format!("{}h", seconds / 3_600)
} else {
format!("{}d", seconds / 86_400)
}
}
fn skills_list_cmd(
cli: &Cli,
harness: Option<&str>,
scope: Option<&str>,
json: bool,
) -> Result<()> {
if let Some(harness) = harness {
if !SKILL_HARNESSES.contains(&harness) {
anyhow::bail!(
"skills list: `{harness}` has no skills root Volter Harness reads (unsupported action) \
— known: {}",
SKILL_HARNESSES.join(", ")
);
}
}
let scope = match scope {
Some(value) => Some(SkillScope::parse(value).ok_or_else(|| {
anyhow::anyhow!(
"skills list: `{value}` is not a scope — use managed, user, project, plugin, or bundled"
)
})?),
None => None,
};
let rows = list_skills(&SkillsQuery {
harness: harness.map(str::to_string),
scope,
cwd: cli.cwd.clone(),
..SkillsQuery::default()
});
if json {
println!("{}", serde_json::to_string_pretty(&rows)?);
return Ok(());
}
if rows.is_empty() {
println!();
println!(
" {} {}",
ui::paint(ui::DIM, "○"),
ui::paint(
ui::MUTED,
match harness {
Some(name) => format!("no skills installed for {name}"),
None => "no skills installed for any harness".to_string(),
}
)
);
println!();
return Ok(());
}
println!();
println!("{}", ui::section(&format!("skills ({})", rows.len())));
for row in &rows {
println!(
" {} {} {} {}",
ui::paint(ui::ACCENT, format!("{:<12}", row.harness.as_str())),
format!("{:<28}", row.name),
ui::paint(ui::MUTED, format!("{:<8}", row.scope.as_str())),
ui::paint(ui::DIM, row.location.display().to_string())
);
}
println!();
Ok(())
}
fn skills_rpc(method: &str, params: serde_json::Value) -> Result<serde_json::Value> {
let mut service = supercode_harness::HarnessSessionService::new();
let response = service.handle(serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": method,
"params": params,
}));
if let Some(error) = response.get("error") {
anyhow::bail!(
"{}",
error
.get("message")
.and_then(serde_json::Value::as_str)
.unwrap_or("skills request failed")
);
}
response
.get("result")
.cloned()
.context("skills response omitted result")
}
fn skills_write_scope(action: &str, value: &str) -> Result<&'static str> {
match value {
"user" => Ok("user"),
"project" => Ok("project"),
other => anyhow::bail!(
"skills {action}: `{other}` is not a scope Volter Harness may write — use user or project \
(managed, plugin and bundled roots belong to the harness itself)"
),
}
}
fn skill_cell<'a>(value: &'a serde_json::Value, pointer: &str) -> &'a str {
value
.pointer(pointer)
.and_then(serde_json::Value::as_str)
.unwrap_or("-")
}
fn skills_install_cmd(
cli: &Cli,
harness: &str,
source: &str,
scope: &str,
name: Option<&str>,
json: bool,
) -> Result<()> {
let scope = skills_write_scope("install", scope)?;
let result = skills_rpc(
"harness.v1.skills.install",
serde_json::json!({
"harness": harness,
"source": source,
"scope": scope,
"name": name,
"cwd": cli.cwd,
}),
)?;
if json {
println!("{}", serde_json::to_string_pretty(&result)?);
return Ok(());
}
println!("ran: {}", skill_cell(&result, "/ran"));
println!(
"installed: {} skill {} ({}) at {}",
skill_cell(&result, "/harness"),
skill_cell(&result, "/name"),
skill_cell(&result, "/skill/scope"),
skill_cell(&result, "/skill/location"),
);
Ok(())
}
fn skills_remove_cmd(cli: &Cli, harness: &str, name: &str, scope: &str, json: bool) -> Result<()> {
let scope = skills_write_scope("remove", scope)?;
let interactive = io::stdin().is_terminal() && io::stderr().is_terminal();
if !cli.yes && interactive {
let proceed = prompt_yes_no(&format!(
" remove {harness}'s {scope} skill `{name}`? [y/N] "
));
if !proceed {
anyhow::bail!("skills remove: cancelled");
}
}
let result = skills_rpc(
"harness.v1.skills.remove",
serde_json::json!({
"harness": harness,
"name": name,
"scope": scope,
"cwd": cli.cwd,
}),
)?;
if json {
println!("{}", serde_json::to_string_pretty(&result)?);
return Ok(());
}
println!("ran: {}", skill_cell(&result, "/ran"));
println!(
"removed: {} skill {}",
skill_cell(&result, "/harness"),
skill_cell(&result, "/name")
);
Ok(())
}
fn memory_show_cmd(
cli: &Cli,
harness: &str,
profile: Option<&str>,
session: Option<&str>,
full: bool,
json: bool,
) -> Result<()> {
guard_memory_harness(harness)?;
let documents = show_memory(&MemoryQuery {
harness: harness.to_string(),
profile: profile.map(str::to_string),
session: session.map(str::to_string),
full,
cwd: cli.cwd.clone(),
..MemoryQuery::default()
})
.map_err(memory_error)?;
if json {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"schema": MEMORY_SCHEMA,
"documents": documents,
}))?
);
return Ok(());
}
if documents.is_empty() {
println!("No memory documents found for {harness}.");
return Ok(());
}
println!(
"{:<10} {:<8} {:<14} {:<28} {:>8} {}",
"HARNESS", "SCOPE", "PROFILE", "NAME", "BYTES", "UPDATED"
);
for document in &documents {
println!(
"{:<10} {:<8} {:<14} {:<28} {:>8} {}",
document.harness,
document.scope.as_str(),
document.profile.as_deref().unwrap_or("-"),
document.name,
document.size,
document.updated_at.as_deref().unwrap_or("-"),
);
for line in &document.preview {
println!(" {line}");
}
if let Some(content) = document.content.as_deref() {
println!(" ---");
for line in content.lines() {
println!(" {line}");
}
} else if document.truncated {
println!(" …");
}
}
Ok(())
}
fn memory_search_cmd(
cli: &Cli,
harness: &str,
query: &str,
profile: Option<&str>,
regex: bool,
json: bool,
) -> Result<()> {
guard_memory_harness(harness)?;
let matches = search_memory(&MemorySearchQuery {
harness: harness.to_string(),
query: query.to_string(),
profile: profile.map(str::to_string),
regex,
cwd: cli.cwd.clone(),
..MemorySearchQuery::default()
})
.map_err(memory_error)?;
if json {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"schema": MEMORY_SCHEMA,
"matches": matches,
}))?
);
return Ok(());
}
if matches.is_empty() {
println!("No memory line in {harness} matches `{query}`.");
return Ok(());
}
for hit in &matches {
println!(
"{}:{}: {}",
hit.path.display(),
hit.line,
hit.excerpt.trim()
);
}
Ok(())
}
fn guard_memory_harness(harness: &str) -> Result<()> {
if MEMORY_HARNESSES.contains(&harness) {
return Ok(());
}
anyhow::bail!(
"memory: `{harness}` has no memory store Volter Harness reads (unsupported action) — known: {}",
MEMORY_HARNESSES.join(", ")
)
}
fn memory_error(error: supercode_harness::memory::MemoryError) -> anyhow::Error {
match error {
supercode_harness::memory::MemoryError::UnsupportedHarness { .. }
| supercode_harness::memory::MemoryError::SessionNotScoped { .. } => {
anyhow::anyhow!("memory: {error} (unsupported action)")
}
other => anyhow::anyhow!("memory: {other}"),
}
}
fn surface_column(session: &supercode_harness::SessionDescriptor) -> String {
let Some(surface) = session.nouns.surface.as_ref() else {
return "—".into();
};
let parts: Vec<&str> = [
surface.platform.as_deref(),
surface.kind.as_deref(),
surface.chat_id.as_deref(),
surface.thread_id.as_deref(),
]
.into_iter()
.flatten()
.collect();
if parts.is_empty() {
surface.key.clone().unwrap_or_else(|| "—".into())
} else {
parts.join(":")
}
}
fn trigger_column(session: &supercode_harness::SessionDescriptor) -> &'static str {
use supercode_harness::Trigger;
match session.nouns.trigger.unwrap_or(Trigger::Unknown) {
Trigger::Human => "human",
Trigger::Channel => "channel",
Trigger::Cron => "cron",
Trigger::Heartbeat => "heartbeat",
Trigger::Webhook => "webhook",
Trigger::Parent => "parent",
Trigger::Api => "api",
Trigger::Task => "task",
Trigger::Unknown => "unknown",
}
}
fn discovered_age(session: &supercode_harness::SessionDescriptor) -> String {
let Some(updated_at_ms) = session.updated_at_ms else {
return "?".into();
};
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|elapsed| elapsed.as_millis() as u64)
.unwrap_or(0);
let secs = now.saturating_sub(updated_at_ms) / 1000;
if secs < 60 {
format!("{secs}s ago")
} else if secs < 3600 {
format!("{}m ago", secs / 60)
} else if secs < 86_400 {
format!("{}h ago", secs / 3600)
} else {
format!("{}d ago", secs / 86_400)
}
}
const DISCOVERABLE_HARNESSES: [&str; 11] = [
HarnessId::CLAUDE_CODE,
HarnessId::CODEX,
HarnessId::PI,
HarnessId::OPENCODE,
HarnessId::GROK,
HarnessId::GEMINI,
HarnessId::GOOSE,
HarnessId::SUPERCODE,
HarnessId::OPENCLAW,
HarnessId::HERMES,
HarnessId::ORCHESTRATOR,
];
fn validate_discovery_harnesses(harnesses: &[String]) -> Result<()> {
for harness in harnesses {
if !DISCOVERABLE_HARNESSES.contains(&harness.as_str()) {
bail!(
"unknown harness `{harness}`; --harness accepts one of: {}",
DISCOVERABLE_HARNESSES.join(", ")
);
}
}
Ok(())
}
fn discover_cmd(
workspace: Option<PathBuf>,
harnesses: &[String],
overrides: DiscoveryHomeOverrides,
search: Option<String>,
search_previews: bool,
cursor: Option<String>,
page_output: bool,
limit: Option<usize>,
include_child_sessions: bool,
workspace_family: Option<PathBuf>,
updated_after_ms: Option<u64>,
updated_before_ms: Option<u64>,
) -> Result<()> {
let mut homes = HarnessHomes::default();
if let Some(path) = overrides.claude_code {
homes.claude_code = path;
}
if let Some(path) = overrides.codex {
homes.codex = path;
}
if let Some(path) = overrides.pi {
homes.pi = path;
}
if let Some(path) = overrides.opencode {
homes.opencode = path;
}
if let Some(path) = overrides.grok {
homes.grok = path;
}
if let Some(path) = overrides.gemini {
homes.gemini = path;
}
if let Some(path) = overrides.goose {
homes.goose = path;
}
if let Some(path) = overrides.supercode {
homes.supercode = path;
}
let search = search.map(|text| {
supercode_harness::mail_route::LiveSessions::read(&homes)
.resolve(&text)
.map(|session| session.address.session_id.clone())
.unwrap_or(text)
});
let query = DiscoveryQuery {
cursor,
workspace,
harnesses: harnesses
.iter()
.map(|value| HarnessId::new(value.clone()))
.collect(),
homes,
limit,
query: search,
search_previews,
include_topic_candidates: false,
include_child_sessions,
root_session_id: None,
workspace_family,
workspace_subtree: false,
updated_after_ms,
updated_before_ms,
profile: None,
};
let page = supercode_harness::discover_session_page(&query)?;
if page_output || query.cursor.is_some() {
println!("{}", serde_json::to_string_pretty(&page)?);
} else {
println!("{}", serde_json::to_string_pretty(&page.sessions)?);
}
Ok(())
}
fn jobs_rpc(method: &str, params: serde_json::Value) -> Result<serde_json::Value> {
let mut service = supercode_harness::HarnessSessionService::new();
let response = service.handle(serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": method,
"params": params,
}));
if let Some(error) = response.get("error") {
anyhow::bail!(
"{}",
error
.get("message")
.and_then(serde_json::Value::as_str)
.unwrap_or("scheduled-job request failed")
);
}
response
.get("result")
.cloned()
.context("scheduled-job response omitted result")
}
fn job_cell<'a>(job: &'a serde_json::Value, pointer: &str) -> &'a str {
job.pointer(pointer)
.and_then(serde_json::Value::as_str)
.unwrap_or("-")
}
fn job_delivery_cell(job: &serde_json::Value) -> String {
let field = |name: &str| {
job.pointer(&format!("/deliver/{name}"))
.and_then(serde_json::Value::as_str)
.filter(|value| !value.is_empty())
};
let mut cell = match (field("mode"), field("target")) {
(Some(mode), Some(target)) => format!("{mode} {target}"),
(Some(only), None) | (None, Some(only)) => only.to_string(),
(None, None) => "-".to_string(),
};
if let Some(chat) = field("chat_id").filter(|chat| !cell.ends_with(&format!(":{chat}"))) {
cell.push_str(&format!(" → {chat}"));
}
if let Some(thread) = field("thread_id").filter(|thread| !cell.ends_with(&format!(":{thread}")))
{
cell.push_str(&format!(":{thread}"));
}
cell
}
fn print_job_panel(job: &serde_json::Value) {
println!("{} job {}", job_cell(job, "/harness"), job_cell(job, "/id"));
println!("scope: {}", job_cell(job, "/scope"));
println!("schedule: {}", job_cell(job, "/schedule/display"));
println!(
"payload: {} · {}",
job_cell(job, "/payload/kind"),
job_cell(job, "/payload/text")
);
println!("deliver: {}", job_delivery_cell(job));
if let Some(account) = job
.pointer("/deliver/account")
.and_then(serde_json::Value::as_str)
{
println!("delivery account: {account}");
}
println!(
"state: {} (enabled: {})",
job_cell(job, "/state"),
job.get("enabled")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false)
);
println!(
"next run: {} · last run: {} · last status: {}",
job_cell(job, "/next_run_at"),
job_cell(job, "/last_run_at"),
job_cell(job, "/last_status")
);
if let Some(error) = job
.get("last_delivery_error")
.and_then(serde_json::Value::as_str)
{
println!("last delivery error: {error}");
}
if let Some(target) = job
.get("session_target")
.and_then(serde_json::Value::as_str)
{
println!("session target: {target}");
}
}
fn job_interval_minutes(every: &str) -> Result<f64> {
let every = every.trim().to_ascii_lowercase();
let (value, factor) = match every.chars().last() {
Some('m') => (&every[..every.len() - 1], 1.0),
Some('h') => (&every[..every.len() - 1], 60.0),
Some('d') => (&every[..every.len() - 1], 1440.0),
Some('s') => (&every[..every.len() - 1], 1.0 / 60.0),
_ => (every.as_str(), 1.0),
};
let parsed: f64 = value
.trim()
.parse()
.with_context(|| format!("`{every}` is not a duration like 10m, 2h, or 1d"))?;
Ok(parsed * factor)
}
fn job_mutation_params(
harness: &str,
id: Option<&str>,
definition: &JobDefinitionArgs,
) -> Result<serde_json::Value> {
if id.is_none() && definition.clear_skills {
bail!("`--clear-skills` edits an existing job; a new job simply names no `--skill`");
}
let mut params = serde_json::Map::new();
params.insert("harness".into(), harness.into());
if let Some(id) = id {
params.insert("id".into(), id.into());
}
if let Some(name) = &definition.name {
params.insert("name".into(), name.clone().into());
}
let schedule = if let Some(every) = &definition.every {
Some(serde_json::json!({
"kind": "interval",
"minutes": job_interval_minutes(every)?,
}))
} else if let Some(expr) = &definition.cron {
Some(serde_json::json!({"kind": "cron", "expr": expr}))
} else {
definition
.at
.as_ref()
.map(|at| serde_json::json!({"kind": "once", "run_at": at}))
};
if let Some(schedule) = schedule {
params.insert("schedule".into(), schedule);
}
let payload = [
("prompt", definition.message.as_ref()),
("system_event", definition.system_event.as_ref()),
("command", definition.command.as_ref()),
("script", definition.script.as_ref()),
]
.into_iter()
.find_map(|(kind, text)| text.map(|text| serde_json::json!({"kind": kind, "text": text})));
if let Some(payload) = payload {
params.insert("payload".into(), payload);
}
if let Some(session) = &definition.session {
params.insert("session_target".into(), session.clone().into());
}
if definition.deliver.is_some() || definition.deliver_to.is_some() {
params.insert(
"deliver".into(),
serde_json::json!({
"target": definition.deliver,
"chat_id": definition.deliver_to,
}),
);
}
if let Some(profile) = &definition.profile {
params.insert("profile".into(), profile.clone().into());
}
if definition.clear_skills || !definition.skills.is_empty() {
params.insert("skills".into(), definition.skills.clone().into());
}
for (key, value) in [
("workdir", &definition.workdir),
("model", &definition.pin_model),
("provider", &definition.pin_provider),
] {
if let Some(value) = value {
params.insert(key.into(), value.clone().into());
}
}
if let Some(repeat) = definition.repeat {
params.insert("repeat".into(), repeat.into());
}
Ok(serde_json::Value::Object(params))
}
fn job_mutation_cmd(method: &str, params: serde_json::Value, json: bool) -> Result<()> {
let result = jobs_rpc(method, params)?;
if json {
println!("{}", serde_json::to_string_pretty(&result)?);
return Ok(());
}
println!("ran: {}", job_cell(&result, "/ran"));
if result
.get("deleted")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false)
{
println!(
"deleted: {} job {}",
job_cell(&result, "/harness"),
job_cell(&result, "/id")
);
return Ok(());
}
match result.get("job") {
Some(job) => print_job_panel(job),
None => println!(
"{} job {} (no row returned)",
job_cell(&result, "/harness"),
job_cell(&result, "/id")
),
}
Ok(())
}
fn jobs_cmd(action: &JobAction) -> Result<()> {
match action {
JobAction::List {
harness,
session,
profile,
json,
} => {
let result = jobs_rpc(
"harness.v1.jobs.list",
serde_json::json!({
"harness": harness,
"session": session,
"profile": profile,
}),
)?;
if *json {
println!("{}", serde_json::to_string_pretty(&result)?);
return Ok(());
}
println!(
"{:<13} {:<24} {:<8} {:<22} {:<14} {:<22} {:<24} LAST_STATUS",
"HARNESS", "ID", "SCOPE", "SCHEDULE", "PAYLOAD", "NEXT_RUN", "DELIVERY"
);
for job in result
.get("jobs")
.and_then(serde_json::Value::as_array)
.into_iter()
.flatten()
{
println!(
"{:<13} {:<24} {:<8} {:<22} {:<14} {:<22} {:<24} {}",
job_cell(job, "/harness"),
truncate(job_cell(job, "/id"), 24),
job_cell(job, "/scope"),
truncate(job_cell(job, "/schedule/display"), 22),
job_cell(job, "/payload/kind"),
truncate(job_cell(job, "/next_run_at"), 22),
truncate(&job_delivery_cell(job), 24),
job_cell(job, "/last_status"),
);
}
for source in result
.get("sources")
.and_then(serde_json::Value::as_array)
.into_iter()
.flatten()
.filter(|source| source["state"] != "read" && source["state"] != "scanned")
{
println!(
" {}: {} at {}",
job_cell(source, "/harness"),
job_cell(source, "/state"),
job_cell(source, "/path"),
);
}
Ok(())
}
JobAction::Get { harness, id, json } => {
let result = jobs_rpc(
"harness.v1.jobs.get",
serde_json::json!({"harness": harness, "id": id}),
)?;
if *json {
println!("{}", serde_json::to_string_pretty(&result)?);
return Ok(());
}
let job = result
.get("job")
.cloned()
.unwrap_or(serde_json::Value::Null);
print_job_panel(&job);
println!(
"native record: {}",
serde_json::to_string(result.get("source").unwrap_or(&serde_json::Value::Null))?
);
Ok(())
}
JobAction::Notepad {
harness,
id,
key,
set,
delete,
profile,
json,
} => {
let mut params = serde_json::json!({"harness": harness, "id": id});
if let Some(key) = key {
params["key"] = key.clone().into();
}
if let Some(value) = set {
params["value"] = value.clone().into();
}
if let Some(profile) = profile {
params["profile"] = profile.clone().into();
}
let method = if set.is_some() {
"harness.v1.jobs.notepad_set"
} else if *delete {
"harness.v1.jobs.notepad_delete"
} else {
"harness.v1.jobs.notepad"
};
let result = jobs_rpc(method, params)?;
if *json {
println!("{}", serde_json::to_string_pretty(&result)?);
return Ok(());
}
if let Some(ran) = result.get("ran").and_then(serde_json::Value::as_str) {
println!("ran: {ran}");
}
let entries = result["entries"].as_array().cloned().unwrap_or_default();
if entries.is_empty() && set.is_none() && !*delete {
println!("(no notepad entries)");
}
for entry in entries {
println!(
"{} = {} ({})",
job_cell(&entry, "/key"),
job_cell(&entry, "/value"),
job_cell(&entry, "/updated_at")
);
}
Ok(())
}
JobAction::Create {
harness,
definition,
json,
} => job_mutation_cmd(
"harness.v1.jobs.create",
job_mutation_params(harness, None, definition)?,
*json,
),
JobAction::Update {
harness,
id,
definition,
json,
} => job_mutation_cmd(
"harness.v1.jobs.update",
job_mutation_params(harness, Some(id), definition)?,
*json,
),
JobAction::Pause { harness, id, json } => job_mutation_cmd(
"harness.v1.jobs.pause",
serde_json::json!({"harness": harness, "id": id}),
*json,
),
JobAction::Resume { harness, id, json } => job_mutation_cmd(
"harness.v1.jobs.resume",
serde_json::json!({"harness": harness, "id": id}),
*json,
),
JobAction::Run { harness, id, json } => job_mutation_cmd(
"harness.v1.jobs.run",
serde_json::json!({"harness": harness, "id": id}),
*json,
),
JobAction::Delete { harness, id, json } => job_mutation_cmd(
"harness.v1.jobs.delete",
serde_json::json!({"harness": harness, "id": id}),
*json,
),
}
}
fn runs_rpc(method: &str, params: serde_json::Value) -> Result<serde_json::Value> {
let mut service = supercode_harness::HarnessSessionService::new();
let response = service.handle(serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": method,
"params": params,
}));
if let Some(error) = response.get("error") {
anyhow::bail!(
"{}",
error
.get("message")
.and_then(serde_json::Value::as_str)
.unwrap_or("run-history request failed")
);
}
response
.get("result")
.cloned()
.context("run-history response omitted result")
}
fn run_cell<'a>(run: &'a serde_json::Value, pointer: &str) -> &'a str {
run.pointer(pointer)
.and_then(serde_json::Value::as_str)
.unwrap_or("-")
}
fn runs_cmd(action: &RunAction) -> Result<()> {
match action {
RunAction::List {
harness,
job,
limit,
json,
} => {
let result = runs_rpc(
"harness.v1.runs.list",
serde_json::json!({
"harness": harness,
"job": job,
"limit": limit,
}),
)?;
if *json {
println!("{}", serde_json::to_string_pretty(&result)?);
return Ok(());
}
println!(
"{:<13} {:<20} {:<10} {:<26} {:<26} {:<16} SESSION",
"HARNESS", "JOB", "STATUS", "STARTED", "FINISHED", "DELIVERY"
);
for run in result
.get("runs")
.and_then(serde_json::Value::as_array)
.into_iter()
.flatten()
{
println!(
"{:<13} {:<20} {:<10} {:<26} {:<26} {:<16} {}",
run_cell(run, "/harness"),
truncate(run_cell(run, "/job_id"), 20),
run_cell(run, "/status"),
truncate(run_cell(run, "/started_at"), 26),
truncate(run_cell(run, "/finished_at"), 26),
truncate(run_cell(run, "/delivery/state"), 16),
run_cell(run, "/session_id"),
);
}
for source in result
.get("sources")
.and_then(serde_json::Value::as_array)
.into_iter()
.flatten()
.filter(|source| source["state"] != "read")
{
println!(
" {}: {} at {}",
run_cell(source, "/harness"),
run_cell(source, "/state"),
run_cell(source, "/path"),
);
}
Ok(())
}
RunAction::Get { harness, id, json } => {
let result = runs_rpc(
"harness.v1.runs.get",
serde_json::json!({"harness": harness, "id": id}),
)?;
if *json {
println!("{}", serde_json::to_string_pretty(&result)?);
return Ok(());
}
let run = result
.get("run")
.cloned()
.unwrap_or(serde_json::Value::Null);
println!(
"{} run {}",
run_cell(&run, "/harness"),
run_cell(&run, "/id")
);
println!("job: {}", run_cell(&run, "/job_id"));
println!("status: {}", run_cell(&run, "/status"));
println!(
"claimed: {} · started: {} · finished: {}",
run_cell(&run, "/claimed_at"),
run_cell(&run, "/started_at"),
run_cell(&run, "/finished_at"),
);
println!("session: {}", run_cell(&run, "/session_id"));
if let Some(error) = run.get("error").and_then(serde_json::Value::as_str) {
println!("error: {error}");
}
match run.get("delivery") {
Some(delivery) if !delivery.is_null() => {
println!(
"delivery: {} → {}",
run_cell(delivery, "/state"),
run_cell(delivery, "/target"),
);
if let Some(attempts) = delivery
.pointer("/attempts")
.and_then(serde_json::Value::as_u64)
{
println!("delivery attempts: {attempts}");
}
if let Some(at) = delivery
.pointer("/delivered_at")
.and_then(serde_json::Value::as_str)
{
println!("delivered at: {at}");
}
if let Some(error) = delivery
.pointer("/last_error")
.and_then(serde_json::Value::as_str)
{
println!("delivery error: {error}");
}
}
_ => println!("delivery: none recorded for this fire"),
}
println!(
"native record: {}",
serde_json::to_string(result.get("source").unwrap_or(&serde_json::Value::Null))?
);
Ok(())
}
}
}
async fn harness_cmd(action: &HarnessAction) -> Result<()> {
match action {
HarnessAction::Report => {
println!(
"{}",
serde_json::to_string_pretty(&supercode_harness::harness_support_registry())?
);
Ok(())
}
HarnessAction::Parity { preset, json } => {
let report = supercode_harness::parity::report(preset).ok_or_else(|| {
anyhow::anyhow!(
"`{preset}` is not a parity preset (expected one of: {})",
supercode_harness::parity::preset_names().join(", ")
)
})?;
if *json {
println!("{}", serde_json::to_string_pretty(&report)?);
} else {
print!("{}", supercode_harness::parity::render(&report));
}
Ok(())
}
HarnessAction::List {
workspace,
probe,
harness,
sessions,
json,
} => harness_list_cmd(workspace.as_deref(), *probe, harness, *sessions, *json).await,
HarnessAction::Settings { harness, json } => harness_settings_cmd(harness, *json),
HarnessAction::Configure {
harness,
cross_session_inbound,
reset_cross_session_inbound,
json,
} => harness_configure_cmd(
harness,
*cross_session_inbound,
*reset_cross_session_inbound,
*json,
),
HarnessAction::Login {
harness,
headless,
json,
} => harness_login_cmd(harness, *headless, *json).await,
HarnessAction::Drive {
harness,
on,
session,
prompt,
cwd,
} => {
if let Some(machine) = on {
return fleet_drive_cmd(
machine,
harness,
session.as_deref(),
prompt.as_deref(),
cwd.as_deref(),
);
}
harness_drive_cmd(
harness,
session.as_deref(),
prompt.as_deref(),
cwd.as_deref(),
)
.await
}
HarnessAction::Serve { poll_ms } => harness_serve_cmd(*poll_ms).await,
}
}
fn fleet_entry() -> Result<PathBuf> {
let teams = supercode_harness::teams_entry()?;
let fleet = teams
.parent()
.map(|dir| dir.join("fleet.mjs"))
.ok_or_else(|| anyhow::anyhow!("teams entry `{}` has no directory", teams.display()))?;
if !fleet.is_file() {
anyhow::bail!(
"no fleet entry beside the teams entry (looked for `{}`)",
fleet.display()
);
}
Ok(fleet)
}
fn run_fleet(args: &[String]) -> Result<()> {
let entry = fleet_entry()?;
let node = teams_node_bin();
let mut command = std::process::Command::new(&node);
command.arg(&entry).args(args);
if args.first().map(String::as_str) == Some("launch") {
act_for_caller(&mut command);
}
run_node_cli(command, &node, &entry)
}
fn fleet_discover_cmd(
on: Option<&str>,
query: Option<&str>,
limit: Option<usize>,
workspace: Option<&Path>,
harnesses: &[String],
) -> Result<()> {
let mut params = serde_json::Map::new();
if let Some(query) = query {
params.insert("query".into(), serde_json::json!(query));
}
if let Some(limit) = limit {
params.insert("limit".into(), serde_json::json!(limit));
}
if let Some(workspace) = workspace {
params.insert("workspace".into(), serde_json::json!(workspace));
}
if !harnesses.is_empty() {
params.insert("harnesses".into(), serde_json::json!(harnesses));
}
let mut args: Vec<String> = Vec::new();
match on {
Some(machine) => {
args.push("rpc".into());
args.push("--on".into());
args.push(machine.to_string());
args.push("harness.v1.sessions.discover".into());
args.push(serde_json::Value::Object(params).to_string());
}
None => {
args.push("discover".into());
if let Some(query) = query {
args.push("--query".into());
args.push(query.to_string());
}
if let Some(limit) = limit {
args.push("--limit".into());
args.push(limit.to_string());
}
if let Some(workspace) = workspace {
args.push("--workspace".into());
args.push(workspace.display().to_string());
}
if !harnesses.is_empty() {
args.push(format!("--harness={}", harnesses.join(",")));
}
}
}
run_fleet(&args)
}
fn fleet_drive_cmd(
machine: &str,
harness: &str,
session: Option<&str>,
prompt: Option<&str>,
cwd: Option<&Path>,
) -> Result<()> {
if prompt.is_some() {
anyhow::bail!(
"`--prompt` is the local drive loop; across Machines, open the runtime with \
`harness drive --on {machine} {harness}` and send input with \
`supercode teams rpc --machine {machine} harness.v1.runtimes.send_input '{{…}}'`"
);
}
let cwd = match cwd {
Some(cwd) => cwd.to_path_buf(),
None => std::env::current_dir().context("resolve the current workspace")?,
};
let (method, params) = match session {
Some(id) => (
"harness.v1.runtimes.attach",
serde_json::json!({"harness": harness, "runtime_id": id, "cwd": cwd}),
),
None => (
"harness.v1.runtimes.start",
serde_json::json!({"harness": harness, "cwd": cwd}),
),
};
run_fleet(&[
"rpc".into(),
"--on".into(),
machine.to_string(),
method.to_string(),
params.to_string(),
])
}
async fn harness_login_cmd(harness: &str, headless: bool, json_output: bool) -> Result<()> {
let harness = supercode_harness::HarnessId::new(harness);
let cwd = std::env::current_dir().context("resolve the current workspace")?;
let plan = supercode_harness::harness_authentication_plan(
&harness,
if headless {
supercode_harness::HarnessAuthenticationEnvironment::Headless
} else {
supercode_harness::HarnessAuthenticationEnvironment::LocalBrowser
},
None,
&cwd,
)?;
if json_output {
println!("{}", serde_json::to_string_pretty(&plan)?);
return Ok(());
}
println!("{}", plan.instructions);
let status = tokio::process::Command::new(&plan.launch.program)
.args(&plan.launch.arguments)
.envs(&plan.launch.env)
.current_dir(&plan.launch.cwd)
.stdin(std::process::Stdio::inherit())
.stdout(std::process::Stdio::inherit())
.stderr(std::process::Stdio::inherit())
.status()
.await
.with_context(|| format!("launch {} sign-in", harness.as_str()))?;
if !status.success() {
anyhow::bail!("{} sign-in exited with {status}", harness.as_str());
}
let report = supercode_harness::inspect_harness_authentication(&harness).await;
match report.state {
supercode_harness::HarnessAuthenticationState::Authenticated => {
println!("{} sign-in verified by its native CLI.", harness.as_str());
Ok(())
}
_ => anyhow::bail!(
"{} completed its sign-in command, but its native status command did not verify an active sign-in",
harness.as_str()
),
}
}
fn print_harness_settings(
report: &supercode_harness::HarnessInteropSettingsReport,
json_output: bool,
) -> Result<()> {
if json_output {
println!("{}", serde_json::to_string_pretty(report)?);
return Ok(());
}
println!("{} interoperability settings", report.harness);
for control in &report.controls {
println!(
"{}: {} · {}",
control.label,
control.configured_value.as_deref().unwrap_or("default"),
control.source_path.display()
);
println!(" {}", control.effective_note);
}
for advisory in &report.advisories {
println!("Warning: {}", advisory.message);
println!("Recommendation: {}", advisory.recommendation.command);
}
Ok(())
}
fn profiles_list_cmd(harness: Option<&str>, json_output: bool) -> Result<()> {
let profiles = supercode_harness::list_profiles(&HarnessHomes::default(), harness)?;
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"schema": supercode_harness::PROFILES_SCHEMA,
"profiles": profiles,
}))?
);
return Ok(());
}
if profiles.is_empty() {
println!("No profiles found.");
return Ok(());
}
println!(
"{:<13} {:<20} {:<22} {:<8} {:<7} {:<9} WORKER",
"HARNESS", "NAME", "KIND", "DEFAULT", "ROUTES", "SESSIONS"
);
for row in &profiles {
println!(
"{:<13} {:<20} {:<22} {:<8} {:<7} {:<9} {}",
row.harness,
row.name,
row.kind.as_str(),
if row.default { "yes" } else { "-" },
count_cell(row.routes),
count_cell(row.sessions),
row.worker.as_deref().unwrap_or("-"),
);
}
Ok(())
}
fn ontology_cmd(action: &OntologyAction) -> Result<()> {
match action {
OntologyAction::Operations => {
let rows: Vec<serde_json::Value> = supercode_harness::SdkOperation::ALL
.iter()
.filter_map(|op| {
op.method().map(|method| {
serde_json::json!({ "action": op.action_name(), "method": method })
})
})
.collect();
println!("{}", serde_json::to_string_pretty(&rows)?);
Ok(())
}
OntologyAction::Schema { list: true, .. } => {
for name in supercode_harness::orchestration::RECORDS {
println!("{name}");
}
Ok(())
}
OntologyAction::Schema {
record: Some(name), ..
} => {
let schema = supercode_harness::orchestration::schema_for(name).ok_or_else(|| {
anyhow::anyhow!("no ontology record named `{name}`; `--list` names them")
})?;
println!("{}", serde_json::to_string_pretty(&schema)?);
Ok(())
}
OntologyAction::Schema { .. } => {
println!(
"{}",
serde_json::to_string_pretty(&supercode_harness::orchestration::schema())?
);
Ok(())
}
}
}
fn read_orchestration_json(path: &Path) -> Result<supercode_harness::orchestration::Orchestration> {
let text = if path == Path::new("-") {
let mut buffer = String::new();
std::io::Read::read_to_string(&mut std::io::stdin(), &mut buffer)?;
buffer
} else {
std::fs::read_to_string(path)
.with_context(|| format!("reading the orchestration from {}", path.display()))?
};
let mut value: serde_json::Value =
serde_json::from_str(&text).with_context(|| format!("`{}` is not JSON", path.display()))?;
if value.get("profiles").is_none() {
if let Some(inner) = value.get_mut("orchestration") {
value = inner.take();
}
}
serde_json::from_value(value)
.with_context(|| format!("`{}` is not an orchestration value", path.display()))
}
fn print_orchestration_read(read: &supercode_harness::orchestration_doors::OrchestrationRead) {
println!("orchestration {}", read.orchestration.root.display());
for (name, profile) in &read.orchestration.profiles {
println!(
" {name:<12} {} binding(s), {} job(s), {} channel(s)",
profile.bindings.len(),
profile.jobs.len(),
profile.channels.len()
);
}
println!(
" vault keys: {}",
if read.vault_keys.is_empty() {
"-".to_string()
} else {
read.vault_keys.join(", ")
}
);
}
fn workflow_cmd(action: &WorkflowAction) -> Result<()> {
match action {
WorkflowAction::Load { from, home, json } => {
let read = supercode_harness::workflow_doors::load((*from).into(), home)?;
if *json {
println!("{}", serde_json::to_string_pretty(&read)?);
} else {
for (slug, board) in &read.workflow.boards {
println!("board {slug}: {} task(s)", board.tasks.len());
for task in board.tasks.values() {
let lane = serde_json::to_value(task.lane)?;
println!(
" {} [{}] {} — {} attempt(s), {} review(s){}",
task.id,
lane.as_str().unwrap_or("?"),
task.title,
task.attempts.len(),
task.reviews.len(),
task.assignee
.as_deref()
.map(|a| format!(" → {a}"))
.unwrap_or_default()
);
}
}
if read.workflow.boards.is_empty() {
println!("no board under {}", read.workflow.root.display());
}
}
Ok(())
}
WorkflowAction::Board(args) => {
if let Some(at) = args.iter().position(|arg| arg == "--at") {
return workflow_at(args, at);
}
let entry = supercode_harness::daemon_entry()?;
let mut command = std::process::Command::new(teams_node_bin());
command.arg(&entry).arg("workflow").args(args);
if std::env::var_os("SUPERCODE_BIN").is_none() {
if let Ok(exe) = std::env::current_exe() {
command.env("SUPERCODE_BIN", exe);
}
}
let status = command.status().map_err(|error| {
anyhow::anyhow!("could not run `{} workflow`: {error}", entry.display())
})?;
std::process::exit(status.code().unwrap_or(1));
}
}
}
fn workflow_at(args: &[String], at: usize) -> Result<()> {
use supercode_harness::mailbox::{Envelope, MailAddress, MailKind, Mailbox, ReplyVia};
let board = args.get(at + 1).ok_or_else(|| {
anyhow::anyhow!("--at names the board's address (sc:<machine>:board:<name>)")
})?;
let address = MailAddress::parse(board).map_err(|_| {
anyhow::anyhow!("{board} is not a board address (sc:<machine>:board:<name>)")
})?;
if address.harness != "board" {
anyhow::bail!("{board} is not a board address (sc:<machine>:board:<name>)");
}
let verb: Vec<&String> = args
.iter()
.enumerate()
.filter(|(index, _)| *index != at && *index != at + 1)
.map(|(_, arg)| arg)
.collect();
let caller = supercode_harness::mail_route::resolve_caller(
&supercode_harness::HarnessHomes::default(),
&supercode_harness::mail_route::process_ancestry(),
)
.map_err(|message| anyhow::anyhow!(message))?;
let body = serde_json::json!({ "workflow": verb }).to_string();
let envelope = Envelope::new(
caller.address.clone(),
caller.name.clone(),
MailKind::Peer,
ReplyVia::Tool,
body,
)?;
let machine = supercode_harness::mailbox::local_machine_name();
if address.machine == machine {
Mailbox::open(&supercode_harness::mailbox::mail_root(), &address)?.deliver(&envelope)?;
} else {
let request = serde_json::json!({ "op": "file", "to": board, "envelope": envelope });
let answer = supercode_harness::mailbox::teams_mail(&address.machine, &request)
.map_err(|message| anyhow::anyhow!("filing for {board}: {message}"))?;
if answer
.get("code")
.and_then(serde_json::Value::as_i64)
.unwrap_or(1)
!= 0
{
anyhow::bail!("{board} did not take it: {answer}");
}
}
println!(
"filed for {board} as {}: {}. The board applies it at its next tick and answers by mail.",
caller.address,
verb.iter()
.map(|arg| arg.as_str())
.collect::<Vec<_>>()
.join(" ")
);
Ok(())
}
fn orchestration_cmd(action: &OrchestrationAction) -> Result<()> {
match action {
OrchestrationAction::Load { root, flavor, json } => {
let read = supercode_harness::orchestration_doors::load(root, (*flavor).into())?;
if *json {
println!("{}", serde_json::to_string_pretty(&read)?);
} else {
print_orchestration_read(&read);
}
Ok(())
}
OrchestrationAction::Save { root, from_json } => {
let orchestration = read_orchestration_json(from_json)?;
let saved = supercode_harness::orchestration_doors::save(
root,
orchestration,
Default::default(),
)?;
println!("wrote {}", saved.root.display());
Ok(())
}
OrchestrationAction::Compile { from, home, json } => {
let read = supercode_harness::orchestration_doors::compile((*from).into(), home)?;
if *json {
println!("{}", serde_json::to_string_pretty(&read)?);
} else {
print_orchestration_read(&read);
}
Ok(())
}
OrchestrationAction::Decompile {
to,
source,
source_flavor,
dest,
from_json,
json,
} => {
let orchestration = read_orchestration_json(from_json)?;
let report = supercode_harness::orchestration_doors::decompile(
(*to).into(),
orchestration,
source,
(*source_flavor).into(),
dest,
Default::default(),
)?;
print_world_decompiled(&report, dest, *json)
}
OrchestrationAction::Import {
from,
home,
into,
json,
} => {
let imported =
supercode_harness::orchestration_doors::import((*from).into(), home, into)?;
if *json {
println!("{}", serde_json::to_string_pretty(&imported)?);
return Ok(());
}
println!("wrote {}", imported.root.display());
print_orchestration_read(&supercode_harness::orchestration_doors::OrchestrationRead {
orchestration: imported.orchestration,
vault_keys: imported.vault_keys,
});
for rel in &imported.carried {
println!(" carried {rel}");
}
Ok(())
}
OrchestrationAction::Export {
to,
root,
dest,
json,
} => {
let report = supercode_harness::orchestration_doors::export((*to).into(), root, dest)?;
print_world_decompiled(&report, dest, *json)
}
}
}
fn print_world_decompiled(
report: &supercode_harness::orchestration_doors::OrchestrationDecompiled,
dest: &Path,
json: bool,
) -> Result<()> {
if json {
println!("{}", serde_json::to_string_pretty(report)?);
return Ok(());
}
println!("wrote {}", dest.display());
for artifact in &report.written {
let tier = match artifact.fidelity {
supercode_harness::Fidelity::ByteLossless => "byte_lossless",
supercode_harness::Fidelity::ValueLossless => "value_lossless",
supercode_harness::Fidelity::Semantic => "semantic",
};
println!(" {tier:<14} {}", artifact.path);
}
for refusal in &report.refused {
println!(" refused {}: {}", refusal.file, refusal.reason);
}
for note in &report.notes {
println!(" note {note}");
}
Ok(())
}
fn teams_node_bin() -> String {
std::env::var_os(supercode_harness::NODE_BIN_ENV)
.map(|value| value.to_string_lossy().trim().to_string())
.filter(|value| !value.is_empty())
.unwrap_or_else(|| "node".to_string())
}
fn teams_cmd(args: &[String]) -> Result<()> {
if args.first().map(String::as_str) == Some("machine") {
match args.get(1).map(String::as_str) {
Some("install") => return teams_service_install_cmd(),
Some("uninstall") => return teams_service_uninstall_cmd(),
_ => {}
}
}
let entry = teams_entry_or_install()?;
let node = teams_node_bin();
let mut command = std::process::Command::new(&node);
command
.arg(&entry)
.args(args)
.env("SUPERCODE_BIN", std::env::current_exe()?);
if matches!(
args.first().map(String::as_str),
Some("input" | "panes" | "suspend" | "resume")
) {
act_for_caller(&mut command);
}
run_node_cli(command, &node, &entry)
}
pub(crate) fn acting_caller() -> Option<String> {
if let Some(caller) = std::env::var("SUPERCODE_CALLER")
.ok()
.filter(|caller| !caller.is_empty())
{
return Some(caller);
}
supercode_harness::mail_route::resolve_caller(
&supercode_harness::HarnessHomes::default(),
&supercode_harness::mail_route::process_ancestry(),
)
.ok()
.map(|caller| caller.address.to_string())
}
pub(crate) fn act_for_caller(command: &mut std::process::Command) {
match acting_caller() {
Some(caller) => command.env("SUPERCODE_CALLER", caller),
None => command.env_remove("SUPERCODE_CALLER"),
};
}
fn run_node_cli(mut command: std::process::Command, node: &str, entry: &Path) -> Result<()> {
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
let error = command.exec();
anyhow::bail!("could not run `{node} {}`: {error}", entry.display());
}
#[cfg(not(unix))]
{
let status = command.status().map_err(|error| {
anyhow::anyhow!("could not run `{node} {}`: {error}", entry.display())
})?;
if status.success() {
return Ok(());
}
std::process::exit(status.code().unwrap_or(1));
}
}
fn teams_entry_or_install() -> Result<PathBuf> {
let missing = match supercode_harness::teams_entry() {
Ok(entry) => return Ok(entry),
Err(error @ supercode_harness::TeamsError::NoEntry { .. }) => error,
Err(error) => return Err(error.into()),
};
if !(io::stdin().is_terminal() && io::stderr().is_terminal()) {
return Err(missing.into());
}
let package = supercode_harness::TEAMS_PACKAGE;
eprint!("Volter sign-in and teams need the {package} npm package. Install it now? [Y/n] ");
io::stderr().flush().ok();
let mut answer = String::new();
io::stdin().read_line(&mut answer)?;
if answer.trim().to_ascii_lowercase().starts_with('n') {
return Err(missing.into());
}
let status = std::process::Command::new(supercode_harness::resolve_program("npm"))
.args([
"install",
"--global",
"--allow-scripts=@homebridge/node-pty-prebuilt-multiarch",
package,
])
.status()
.context("running npm (is Node.js installed?)")?;
if !status.success() {
anyhow::bail!("npm could not install {package}");
}
Ok(supercode_harness::teams_entry()?)
}
fn workspace_cmd(args: &[String]) -> Result<()> {
let entry = teams_entry_or_install()?;
let node = teams_node_bin();
let mut command = std::process::Command::new(&node);
command
.arg(&entry)
.args(args)
.env("SUPERCODE_WORKSPACE_ROOT", "1")
.env("SUPERCODE_BIN", std::env::current_exe()?);
run_node_cli(command, &node, &entry)
}
fn teams_connector_service_cmd(args: &[String]) -> Result<()> {
if args.len() != 5 {
anyhow::bail!("internal connector service usage: ACTION CONTEXT CWD SERVER_ID TEAM_ID");
}
let action = &args[0];
let context = &args[1];
let cwd = Path::new(&args[2]);
let server_id = &args[3];
let team_id = &args[4];
let teams_home = supercode_harness::teams_home();
let configured_home = std::env::var_os("SUPERCODE_HOME").map(PathBuf::from);
let supercode_home = configured_home
.as_deref()
.or_else(|| teams_home.parent())
.unwrap_or(&teams_home);
let entry = supercode_harness::teams_entry()?;
let node = teams_node_bin();
let label = supercode_harness::teams_connector_service_name(server_id, team_id, context);
let value = match action.as_str() {
"install" => {
let supercode_bin = supercode_harness::teams_connector_service_binary(
&teams_home,
&std::env::current_exe()?,
)?;
let unit = supercode_harness::teams_connector_service_unit(
&teams_home,
supercode_home,
&entry,
&node,
&supercode_bin,
context,
cwd,
server_id,
team_id,
)?;
let state = supercode_harness::install_teams_connector_service(&unit, &label)?;
let tools = supercode_harness::teams::register_message_tools(&supercode_bin);
serde_json::json!({"label":label,"kind":state.kind,"installed":state.installed,"detail":state.detail,"unit":unit.path,"message_tools":tools})
}
"uninstall" => {
let state = supercode_harness::uninstall_teams_connector_service(&teams_home, &label)?;
serde_json::json!({"label":label,"kind":state.kind,"installed":state.installed,"detail":state.detail})
}
"status" => {
let state = supercode_harness::teams_connector_service_status(&label);
serde_json::json!({"label":label,"kind":state.kind,"installed":state.installed,"pid":state.pid,"detail":state.detail})
}
_ => anyhow::bail!("unknown connector service action {action}"),
};
println!("{value}");
Ok(())
}
fn teams_service_install_cmd() -> Result<()> {
let home = supercode_harness::teams_home();
let entry = supercode_harness::teams_entry()?;
let node = teams_node_bin();
let (unit, service) = supercode_harness::install_teams_service(&home, &entry, &node)?;
println!();
println!("{}", ui::section(&format!("{} unit", unit.kind)));
println!(
" {}",
ui::paint(ui::MUTED, format!("written to {}", unit.path.display()))
);
println!();
for line in unit.text.lines() {
println!(" {line}");
}
println!();
println!(
" {} {}",
ui::paint(ui::ACCENT, "installed"),
ui::paint(ui::MUTED, format!("{} · {}", service.label, service.detail))
);
println!();
Ok(())
}
fn teams_service_uninstall_cmd() -> Result<()> {
let home = supercode_harness::teams_home();
let service = supercode_harness::uninstall_teams_service(&home)?;
println!();
println!("{}", ui::section(&format!("{} unit", service.kind)));
println!(
" {} {}",
ui::paint(ui::DIM, "removed"),
ui::paint(ui::MUTED, format!("{} · {}", service.label, service.detail))
);
println!();
Ok(())
}
fn orchestrator_cmd(action: &OrchestratorAction) -> Result<()> {
match action {
OrchestratorAction::Setup {
root,
node,
install,
uninstall,
json,
} => orchestrator_setup_cmd(root.clone(), node, *install, *uninstall, *json),
OrchestratorAction::Pair { action } => match action {
PairAction::Approve { code, args } => {
orchestrator_access_cmd(args, "pair-approve", std::slice::from_ref(code))
}
PairAction::List { args } => orchestrator_access_cmd(args, "pair-list", &[]),
},
OrchestratorAction::Access { action } => match action {
AccessAction::Allow {
platform,
user_id,
args,
} => orchestrator_access_cmd(args, "allow", &[platform.clone(), user_id.clone()]),
AccessAction::Revoke {
platform,
user_id,
args,
} => orchestrator_access_cmd(args, "revoke", &[platform.clone(), user_id.clone()]),
AccessAction::Admin { action } => match action {
AdminAction::Add {
platform,
user_id,
args,
} => {
orchestrator_access_cmd(args, "admin-add", &[platform.clone(), user_id.clone()])
}
AdminAction::Remove {
platform,
user_id,
args,
} => orchestrator_access_cmd(
args,
"admin-remove",
&[platform.clone(), user_id.clone()],
),
},
},
OrchestratorAction::Start { root, node, json } => {
orchestrator_start_cmd(root.clone(), node, *json)
}
OrchestratorAction::Stop { root, json } => orchestrator_stop_cmd(root.clone(), *json),
OrchestratorAction::Status { root, json } => orchestrator_status_cmd(root.clone(), *json),
OrchestratorAction::Import {
from,
id,
root,
json,
} => orchestrator_import_cmd(from, id, root.clone(), *json),
}
}
fn orchestrator_import_cmd(
from: &str,
id: &str,
root: Option<PathBuf>,
json_output: bool,
) -> Result<()> {
if from != "claude-session" {
anyhow::bail!(
"unknown import source `{from}` — `claude-session` is the only one Volter Harness compiles"
);
}
let root = orchestrator_root(root);
let homes = HarnessHomes::default();
let locator = supercode_harness::discover_sessions(&DiscoveryQuery {
harnesses: vec![HarnessId::from(HarnessId::CLAUDE_CODE)],
homes: homes.clone(),
..DiscoveryQuery::default()
})?
.into_iter()
.find(|descriptor| descriptor.locator.session_id == id)
.map(|descriptor| descriptor.locator)
.ok_or_else(|| {
anyhow::anyhow!(
"no Claude Code session `{id}` under {} — `supercode sessions list --harness \
claude-code` shows what is there",
homes.claude_code.display()
)
})?;
let session = supercode_harness::Session::load(locator.storage.path())
.with_context(|| format!("loading Claude session {id}"))?;
let manifest = supercode_harness::ClaudeRuntimeManifest::from_session(&session)?;
if manifest.active_crons.is_empty() && manifest.pending_wakeups.is_empty() {
anyhow::bail!(
"Claude Code session `{id}` carries no cron job and no pending wakeup — there is \
nothing to compile"
);
}
let outcome =
orchestrator_import::import_claude_session(&root, id, &manifest, current_unix_seconds())?;
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"root": root,
"source": {"kind": from, "id": id},
"store": outcome.path,
"added": outcome.added,
"replaced": outcome.replaced,
"untouched": outcome.untouched,
"fired": 0,
}))?
);
return Ok(());
}
println!();
println!("{}", ui::section("orchestrator import"));
println!(
" {} {}",
ui::paint(ui::MUTED, format!("{:<10}", "source")),
format!("claude-session {id}")
);
println!(
" {} {}",
ui::paint(ui::MUTED, format!("{:<10}", "store")),
outcome.path.display()
);
for id in &outcome.added {
println!(
" {} {id}",
ui::paint(ui::ACCENT, format!("{:<10}", "added"))
);
}
for id in &outcome.replaced {
println!(
" {} {id}",
ui::paint(ui::ACCENT, format!("{:<10}", "replaced"))
);
}
println!(
" {} {} other job(s) left untouched · nothing was fired",
ui::paint(ui::MUTED, format!("{:<10}", "kept")),
outcome.untouched
);
println!();
Ok(())
}
fn orchestrator_root(root: Option<PathBuf>) -> PathBuf {
root.unwrap_or_else(|| HarnessHomes::default().orchestrator)
}
fn orchestrator_profiles(root: &Path) -> Vec<String> {
supercode_harness::orchestrator_profile_dirs(root)
.into_iter()
.map(|(name, _)| name)
.collect()
}
fn orchestrator_setup_cmd(
root: Option<PathBuf>,
node: &str,
install: bool,
uninstall: bool,
json_output: bool,
) -> Result<()> {
let root = orchestrator_root(root);
if uninstall {
let service = supercode_harness::uninstall_service(&root)?;
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"root": root,
"uninstalled": true,
"service": service,
}))?
);
return Ok(());
}
println!();
println!("{}", ui::section(&format!("{} unit", service.kind)));
println!(
" {} {}",
ui::paint(ui::DIM, "removed"),
ui::paint(ui::MUTED, format!("{} · {}", service.label, service.detail))
);
println!();
return Ok(());
}
let entry = supercode_harness::daemon_entry()?;
let (unit, service) = if install {
let (unit, service) = supercode_harness::install_service(&root, &entry, node)?;
(unit, Some(service))
} else {
let unit = supercode_harness::service_unit(&root, &entry, node);
supercode_harness::write_unit(&unit)?;
(unit, None)
};
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"root": root,
"entry": entry,
"unit": unit,
"installed": install,
"service": service,
}))?
);
return Ok(());
}
println!();
println!("{}", ui::section(&format!("{} unit", unit.kind)));
println!(
" {}",
ui::paint(ui::MUTED, format!("written to {}", unit.path.display()))
);
println!();
for line in unit.text.lines() {
println!(" {line}");
}
println!();
match &service {
Some(state) => {
println!(
" {} {}",
ui::paint(ui::ACCENT, "installed"),
ui::paint(ui::MUTED, format!("{} · {}", state.label, state.detail))
);
}
None => {
println!(" {}", ui::paint(ui::MUTED, "not installed — run:"));
println!(" {}", unit.install_command);
}
}
println!();
Ok(())
}
fn orchestrator_access_cmd(args: &AccessArgs, verb: &str, rest: &[String]) -> Result<()> {
let root = orchestrator_root(args.root.clone());
let entry = supercode_harness::daemon_entry()?;
let mut command = std::process::Command::new(&args.node);
command.arg(&entry).arg("access").arg(verb);
for value in rest {
command.arg(value);
}
command
.arg("--root")
.arg(&root)
.arg("--profile")
.arg(&args.profile);
let output = command.output().map_err(|error| {
anyhow::anyhow!(
"could not run `{} {} access {verb}`: {error}",
args.node,
entry.display()
)
})?;
let stdout = String::from_utf8_lossy(&output.stdout);
let answer: serde_json::Value = stdout
.lines()
.rfind(|line| !line.trim().is_empty())
.and_then(|line| serde_json::from_str(line).ok())
.unwrap_or(serde_json::Value::Null);
if !output.status.success() {
let message = answer
.get("message")
.and_then(|m| m.as_str())
.map(str::to_string)
.unwrap_or_else(|| String::from_utf8_lossy(&output.stderr).trim().to_string());
anyhow::bail!("orchestrator {verb}: {message}");
}
if args.json {
println!("{}", serde_json::to_string_pretty(&answer)?);
return Ok(());
}
render_access(verb, &answer);
Ok(())
}
fn render_access(verb: &str, answer: &serde_json::Value) {
let list = |value: Option<&serde_json::Value>| -> String {
let Some(map) = value.and_then(|v| v.as_object()) else {
return "none".into();
};
if map.is_empty() {
return "none".into();
}
map.iter()
.map(|(platform, users)| {
let names: Vec<String> = users
.as_array()
.map(|a| {
a.iter()
.filter_map(|u| u.as_str().map(str::to_string))
.collect()
})
.unwrap_or_default();
format!("{platform}: {}", names.join(", "))
})
.collect::<Vec<_>>()
.join(" · ")
};
println!();
println!("{}", ui::section(&format!("orchestrator access ({verb})")));
println!(
" {} {}",
ui::paint(ui::MUTED, format!("{:<12}", "profile")),
answer
.get("profile")
.and_then(|p| p.as_str())
.unwrap_or("default")
);
println!(
" {} {}",
ui::paint(ui::MUTED, format!("{:<12}", "allowlist")),
list(answer.get("allowlist"))
);
println!(
" {} {}",
ui::paint(ui::MUTED, format!("{:<12}", "admins")),
list(answer.get("admins"))
);
let pending = answer
.get("pending")
.and_then(|p| p.as_array())
.cloned()
.unwrap_or_default();
if pending.is_empty() {
println!(
" {} none",
ui::paint(ui::MUTED, format!("{:<12}", "pending"))
);
}
for row in &pending {
let field = |key: &str| row.get(key).and_then(|v| v.as_str()).unwrap_or("");
let expired = row
.get("expired")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false);
println!(
" {} {} {} {}",
ui::paint(ui::MUTED, format!("{:<12}", "pending")),
if expired {
ui::paint(ui::DIM, field("code"))
} else {
ui::paint(ui::ACCENT, field("code"))
},
format_args!(
"{} {} (policy {})",
field("platform"),
field("user_id"),
field("policy")
),
ui::paint(
ui::MUTED,
if expired {
format!("expired {}", field("expires_at"))
} else {
format!("expires {}", field("expires_at"))
}
)
);
}
println!();
}
fn orchestrator_start_cmd(root: Option<PathBuf>, node: &str, json_output: bool) -> Result<()> {
let root = orchestrator_root(root);
if let Some(lease) = supercode_harness::live_lease(&root) {
anyhow::bail!(
"the orchestrator is already running for `{}` (pid {}, since {}) — \
`supercode orchestrator stop` first",
root.display(),
lease.pid,
lease.started_at
);
}
let entry = supercode_harness::daemon_entry()?;
let mut command = std::process::Command::new(node);
command
.arg(&entry)
.arg("--root")
.arg(&root)
.stdin(std::process::Stdio::null());
if let Ok(exe) = std::env::current_exe() {
command.env("SUPERCODE_BIN", exe);
}
let mut child = command
.spawn()
.map_err(|error| anyhow::anyhow!("could not run `{node} {}`: {error}", entry.display()))?;
let lease = wait_for_lease(&root, child.id()).unwrap_or(supercode_harness::Lease {
pid: child.id(),
started_at: supercode_interchange::sidecar::ms_to_rfc3339(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|since| since.as_millis() as i64)
.unwrap_or_default(),
),
root: root.clone(),
host: supercode_harness::orchestrator::this_host().0,
boot: supercode_harness::orchestrator::this_host().1,
});
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"root": root,
"entry": entry,
"lease": lease,
"lock": supercode_harness::lock_path(&root),
}))?
);
} else {
println!();
println!("{}", ui::section("orchestrator"));
println!(
" {} {}",
ui::paint(ui::ACCENT, "up"),
ui::paint(
ui::MUTED,
format!(
"pid {} · {} · lease {}",
lease.pid,
root.display(),
supercode_harness::lock_path(&root).display()
)
)
);
println!();
}
let status = child.wait();
if supercode_harness::read_lease(&root).is_some_and(|held| held.pid == lease.pid) {
supercode_harness::clear_lease(&root)?;
}
match status {
Ok(status) if status.success() => Ok(()),
Ok(status) => anyhow::bail!("the orchestrator daemon exited with {status}"),
Err(error) => anyhow::bail!("could not wait for the orchestrator daemon: {error}"),
}
}
fn wait_for_lease(root: &Path, pid: u32) -> Option<supercode_harness::Lease> {
for _ in 0..100 {
if let Some(lease) = supercode_harness::read_lease(root) {
if lease.pid == pid {
return Some(lease);
}
}
std::thread::sleep(std::time::Duration::from_millis(50));
}
None
}
fn orchestrator_stop_cmd(root: Option<PathBuf>, json_output: bool) -> Result<()> {
let root = orchestrator_root(root);
let lease = supercode_harness::orchestrator::stop(&root)?;
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"root": root,
"stopped": lease,
"signal": "SIGTERM",
}))?
);
return Ok(());
}
println!();
println!("{}", ui::section("orchestrator"));
println!(
" {} {}",
ui::paint(ui::DIM, "down"),
ui::paint(ui::MUTED, format!("SIGTERM to pid {}", lease.pid))
);
println!();
Ok(())
}
fn orchestrator_status_cmd(root: Option<PathBuf>, json_output: bool) -> Result<()> {
let root = orchestrator_root(root);
let lease = supercode_harness::read_lease(&root);
let live = lease
.as_ref()
.is_some_and(supercode_harness::Lease::is_live);
let profiles = orchestrator_profiles(&root);
let state = match (&lease, live) {
(Some(_), true) => "up",
(Some(_), false) => "down (stale lease)",
(None, _) => "down",
};
let service = supercode_harness::service_status(&root);
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"root": root,
"state": state,
"lease": lease,
"lock": supercode_harness::lock_path(&root),
"profiles": profiles,
"service": service,
}))?
);
return Ok(());
}
println!();
println!("{}", ui::section("orchestrator"));
println!(
" {} {}",
if live {
ui::paint(ui::ACCENT, format!("{state:<18}"))
} else {
ui::paint(ui::DIM, format!("{state:<18}"))
},
ui::paint(
ui::MUTED,
match &lease {
Some(lease) => format!("pid {} · since {}", lease.pid, lease.started_at),
None => format!(
"no lease at {}",
supercode_harness::lock_path(&root).display()
),
}
)
);
println!(
" {} {}",
ui::paint(ui::MUTED, format!("{:<18}", "profiles")),
if profiles.is_empty() {
format!("none under {}", root.display())
} else {
format!("{} ({})", profiles.len(), profiles.join(", "))
}
);
println!(
" {} {}",
ui::paint(ui::MUTED, format!("{:<18}", "service")),
if service.installed {
format!(
"{} · {} · {}{}",
service.kind,
service.label,
service.detail,
service
.pid
.map(|pid| format!(" (pid {pid})"))
.unwrap_or_default()
)
} else {
format!("not installed ({})", service.detail)
}
);
println!();
Ok(())
}
fn count_cell(value: Option<u64>) -> String {
value.map_or_else(|| "-".to_string(), |count| count.to_string())
}
fn flag_cell(value: Option<bool>) -> &'static str {
match value {
Some(true) => "yes",
Some(false) => "no",
None => "-",
}
}
fn triggers_list_cmd(harness: Option<&str>, json_output: bool) -> Result<()> {
let triggers = supercode_harness::list_triggers(&HarnessHomes::default(), harness)?;
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"schema": supercode_harness::TRIGGERS_SCHEMA,
"triggers": triggers,
}))?
);
return Ok(());
}
if triggers.is_empty() {
println!("No inbound triggers found.");
return Ok(());
}
println!(
"{:<13} {:<16} {:<14} {:<28} {:<8} {:<5} TARGET",
"HARNESS", "NAME", "KIND", "ROUTE", "ENABLED", "AUTH"
);
for row in &triggers {
let kind = serde_json::to_value(row.kind)
.ok()
.and_then(|v| v.as_str().map(str::to_string))
.unwrap_or_default();
let mut target: Vec<String> = Vec::new();
if let Some(a) = &row.target.action {
target.push(a.clone());
}
if let Some(p) = &row.target.profile {
target.push(format!("→ {p}"));
}
if let Some(d) = &row.deliver.target {
target.push(format!("deliver={d}"));
}
println!(
"{:<13} {:<16} {:<14} {:<28} {:<8} {:<5} {}",
row.harness,
row.name,
kind,
row.route,
if row.enabled { "yes" } else { "no" },
if row.authenticated { "yes" } else { "no" },
target.join(" ")
);
}
Ok(())
}
fn routes_list_cmd(harness: Option<&str>, profile: Option<&str>, json_output: bool) -> Result<()> {
let routes = supercode_harness::list_routes(&HarnessHomes::default(), harness, profile)?;
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"schema": supercode_harness::ROUTES_SCHEMA,
"routes": routes,
}))?
);
return Ok(());
}
if routes.is_empty() {
println!("No routes found.");
return Ok(());
}
println!("{:<13} {:<16} {:<5} MATCH", "HARNESS", "TARGET", "SPEC");
for row in &routes {
let m = &row.matcher;
let mut parts: Vec<String> = Vec::new();
if let Some(v) = &m.platform {
parts.push(v.clone());
}
if let Some(v) = &m.account {
parts.push(format!("account={v}"));
}
if let Some(v) = &m.guild {
parts.push(format!("guild={v}"));
}
if let Some(v) = &m.team {
parts.push(format!("team={v}"));
}
if let Some(v) = &m.chat_id {
parts.push(format!("{}={v}", m.peer_kind.as_deref().unwrap_or("chat")));
}
if let Some(v) = &m.thread_id {
parts.push(format!("thread={v}"));
}
if !m.roles.is_empty() {
parts.push(format!("roles={}", m.roles.join("|")));
}
let matcher = if row.default {
"(default)".to_string()
} else {
parts.join(" ")
};
println!(
"{:<10} {:<16} {:<5} {}",
row.harness, row.target, row.specificity, matcher
);
}
Ok(())
}
fn channels_list_cmd(harness: Option<&str>, json_output: bool) -> Result<()> {
let channels = supercode_harness::list_channels(&HarnessHomes::default(), harness)?;
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"schema": supercode_harness::CHANNELS_SCHEMA,
"channels": channels,
}))?
);
return Ok(());
}
if channels.is_empty() {
println!("No channels found.");
return Ok(());
}
println!(
"{:<13} {:<22} {:<14} {:<20} {:<8} {:<8} SESSIONS",
"HARNESS", "NAME", "KIND", "ACCOUNT", "ENABLED", "STATUS"
);
for row in &channels {
println!(
"{:<13} {:<22} {:<14} {:<20} {:<8} {:<8} {}",
row.harness,
row.name,
row.kind,
row.account.as_deref().unwrap_or("-"),
flag_cell(row.enabled),
row.status.as_str(),
count_cell(row.sessions),
);
}
Ok(())
}
fn channels_status_cmd(harness: &str, name: &str, json_output: bool) -> Result<()> {
let channel = supercode_harness::channel_status(&HarnessHomes::default(), harness, name)?;
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"schema": supercode_harness::CHANNELS_SCHEMA,
"channel": channel,
}))?
);
return Ok(());
}
println!("{} channel {}", channel.harness, channel.name);
println!(" kind: {}", channel.kind);
println!(
" account: {}",
channel.account.as_deref().unwrap_or("-")
);
println!(" enabled: {}", flag_cell(channel.enabled));
println!(
" configured: {}",
if channel.configured { "yes" } else { "no" }
);
println!(" status: {}", channel.status.as_str());
println!(" sessions: {}", count_cell(channel.sessions));
Ok(())
}
fn profiles_show_cmd(harness: &str, name: &str, json_output: bool) -> Result<()> {
let profile = supercode_harness::get_profile(&HarnessHomes::default(), harness, name)?;
if json_output {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"schema": supercode_harness::PROFILES_SCHEMA,
"profile": profile,
}))?
);
return Ok(());
}
println!("{} profile {}", profile.harness, profile.name);
println!(" kind: {}", profile.kind.as_str());
println!(
" home: {}",
profile
.home
.as_ref()
.map_or_else(|| "-".to_string(), |home| home.display().to_string())
);
println!(" default: {}", if profile.default { "yes" } else { "no" });
println!(" routes: {}", count_cell(profile.routes));
println!(" sessions: {}", count_cell(profile.sessions));
println!(" model: {}", profile.model.as_deref().unwrap_or("-"));
Ok(())
}
fn profile_mutation_params(
harness: &str,
name: &str,
from: Option<&str>,
workspace: Option<&str>,
) -> serde_json::Value {
let mut params = serde_json::Map::new();
params.insert("harness".into(), harness.into());
params.insert("name".into(), name.into());
if let Some(from) = from {
params.insert("from".into(), from.into());
}
if let Some(workspace) = workspace {
params.insert("workspace".into(), workspace.into());
}
serde_json::Value::Object(params)
}
fn profiles_rpc(method: &str, params: serde_json::Value) -> Result<serde_json::Value> {
let mut service = supercode_harness::HarnessSessionService::new();
let response = service.handle(serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": method,
"params": params,
}));
if let Some(error) = response.get("error") {
anyhow::bail!(
"{}",
error
.get("message")
.and_then(serde_json::Value::as_str)
.unwrap_or("profile request failed")
);
}
response
.get("result")
.cloned()
.context("profile response omitted result")
}
fn profile_cell<'a>(profile: &'a serde_json::Value, pointer: &str) -> &'a str {
profile
.pointer(pointer)
.and_then(serde_json::Value::as_str)
.unwrap_or("-")
}
fn profile_mutation_cmd(method: &str, params: serde_json::Value, json_output: bool) -> Result<()> {
let result = profiles_rpc(method, params)?;
if json_output {
println!("{}", serde_json::to_string_pretty(&result)?);
return Ok(());
}
println!("ran: {}", profile_cell(&result, "/ran"));
if result
.get("deleted")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false)
{
println!(
"deleted: {} profile {}",
profile_cell(&result, "/harness"),
profile_cell(&result, "/name")
);
return Ok(());
}
let profile = result
.get("profile")
.cloned()
.unwrap_or(serde_json::Value::Null);
println!(
"{} profile {}",
profile_cell(&profile, "/harness"),
profile_cell(&profile, "/name")
);
println!(" kind: {}", profile_cell(&profile, "/kind"));
println!(" home: {}", profile_cell(&profile, "/home"));
println!(
" default: {}",
if profile
.get("default")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false)
{
"yes"
} else {
"no"
}
);
println!(" model: {}", profile_cell(&profile, "/model"));
Ok(())
}
fn profiles_delete_cmd(cli: &Cli, harness: &str, name: &str, json_output: bool) -> Result<()> {
if !cli.yes {
let fate = if harness == HarnessId::ORCHESTRATOR {
"moves its home into `profiles/.trash/`"
} else {
"permanently removes it and everything in its home"
};
let interactive = io::stdin().is_terminal() && io::stderr().is_terminal();
if !interactive {
anyhow::bail!(
"`profiles delete` on the {harness} profile `{name}` {fate}; pass --yes to \
confirm without a terminal"
);
}
if !prompt_yes_no(&format!(
"delete the {harness} profile `{name}`? this {fate}. [y/N] "
)) {
eprintln!("cancelled");
return Ok(());
}
}
profile_mutation_cmd(
"harness.v1.profiles.delete",
profile_mutation_params(harness, name, None, None),
json_output,
)
}
fn harness_settings_cmd(harness: &str, json_output: bool) -> Result<()> {
let report =
supercode_harness::inspect_harness_interop_settings(&HarnessHomes::default(), harness)?;
print_harness_settings(&report, json_output)
}
fn harness_configure_cmd(
harness: &str,
value: Option<ClaudeCrossSessionInboundArg>,
reset: bool,
json_output: bool,
) -> Result<()> {
let homes = HarnessHomes::default();
let before = supercode_harness::inspect_harness_interop_settings(&homes, harness)?;
let change = supercode_harness::HarnessSettingChange {
key: supercode_harness::CLAUDE_CROSS_SESSION_INBOUND_KEY.into(),
value: if reset {
None
} else {
value.map(|value| value.as_str().to_string())
},
};
let report = supercode_harness::configure_harness_interop_settings(
&homes,
harness,
&[change],
Some(&before.revision),
)?;
print_harness_settings(&report, json_output)
}
async fn harness_attach_cmd(cli: &Cli, endpoint: &str, harness: &str, session: &str) -> Result<()> {
let endpoint = supercode_harness::LiveRuntimeEndpoint::parse(endpoint)?;
let workspace = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let receipt = supercode_harness::resolve_live_runtime(
&endpoint,
&supercode_harness::LiveRuntimeSource {
harness: harness.to_string(),
session_id: session.to_string(),
workspace,
},
)?;
let runtime =
supercode_harness::HttpFrontendRuntime::connect(receipt.base_url, receipt.token).await?;
let config = resolve_config(cli)?;
if tui::should_activate(&config) {
supercode_frontend_tui::app::run(runtime).await?;
} else {
supercode_frontend_tui::app::run_line(runtime).await?;
}
Ok(())
}
async fn harness_list_cmd(
workspace: Option<&Path>,
probe: HarnessProbeArg,
harnesses: &[String],
include_sessions: bool,
json_output: bool,
) -> Result<()> {
let mut service = supercode_harness::HarnessSessionService::new();
let response = service
.handle_async(serde_json::json!({
"jsonrpc": "2.0",
"id": 1,
"method": "harness.v1.harnesses.list",
"params": {
"workspace": workspace,
"probe": probe.as_str(),
"harnesses": harnesses,
"include_sessions": include_sessions,
},
}))
.await;
if let Some(error) = response.get("error") {
anyhow::bail!(
"harness list: {}",
error
.get("message")
.and_then(serde_json::Value::as_str)
.unwrap_or("inventory request failed")
);
}
let result = response
.get("result")
.context("harness list response omitted result")?;
if json_output {
println!("{}", serde_json::to_string_pretty(result)?);
return Ok(());
}
println!(
"{:<13} {:<10} {:<9} {:<11} {:<12} {:>8} {:<28} VERSION",
"HARNESS", "INSTALLED", "RUNNING", "AUTH", "RUNTIME", "SESSIONS", "GATEWAY"
);
for harness in result
.get("harnesses")
.and_then(serde_json::Value::as_array)
.into_iter()
.flatten()
{
let id = harness
.get("id")
.and_then(serde_json::Value::as_str)
.unwrap_or("?");
let installed = if harness
.get("installed")
.and_then(serde_json::Value::as_bool)
.unwrap_or(false)
{
"yes"
} else {
"no"
};
let auth = harness
.get("auth")
.and_then(serde_json::Value::as_str)
.unwrap_or("unknown");
let runtime = harness
.get("runtime")
.and_then(serde_json::Value::as_str)
.unwrap_or("unknown");
let sessions = harness
.pointer("/sessions/global")
.and_then(serde_json::Value::as_u64)
.map(|count| count.to_string())
.unwrap_or_else(|| "-".into());
let version = harness
.get("version")
.and_then(serde_json::Value::as_str)
.unwrap_or("-");
let running = if harness.get("running").is_some() {
"yes"
} else {
"-"
};
let gateway = harness
.get("gateway")
.map(|g| {
let state = g
.get("state")
.and_then(serde_json::Value::as_str)
.unwrap_or("unknown");
match g.get("endpoint").and_then(serde_json::Value::as_str) {
Some(endpoint) => format!("{state} {endpoint}"),
None => state.to_string(),
}
})
.unwrap_or_else(|| "-".to_string());
println!("{id:<13} {installed:<10} {running:<9} {auth:<11} {runtime:<12} {sessions:>8} {gateway:<28} {version}");
if let Some(evidence) = harness
.pointer("/running/evidence")
.and_then(serde_json::Value::as_str)
{
println!(" running: {evidence}");
}
if let Some(reason) = harness.get("reason").and_then(serde_json::Value::as_str) {
println!(" {reason}");
}
}
if probe == HarnessProbeArg::Passive {
println!("\nPassive inventory never opens a harness. Use `--probe handshake` to verify auth/readiness without sending a model prompt.");
}
Ok(())
}
async fn harness_drive_cmd(
harness: &str,
session: Option<&str>,
prompt: Option<&str>,
cwd: Option<&Path>,
) -> Result<()> {
use tokio::io::AsyncBufReadExt;
let cwd = match cwd {
Some(cwd) => cwd.to_path_buf(),
None => std::env::current_dir().context("resolve the current workspace")?,
};
let mut service = supercode_harness::HarnessSessionService::new();
let (method, params) = match session {
Some(id) => (
"harness.v1.runtimes.attach",
serde_json::json!({"harness": harness, "runtime_id": id, "cwd": cwd}),
),
None => (
"harness.v1.runtimes.start",
serde_json::json!({"harness": harness, "cwd": cwd}),
),
};
let response = service
.handle_async(serde_json::json!({
"jsonrpc": "2.0", "id": 1, "method": method, "params": params,
}))
.await;
if let Some(error) = response.get("error") {
let message = error
.get("message")
.and_then(serde_json::Value::as_str)
.unwrap_or("unknown error");
let hint = if session.is_some() && harness == "openclaw" {
"\nhint: an openclaw gateway names live sessions by its own key form (e.g. `agent:main:acp-bridge:<uuid>`), not the bare transcript uuid — the gateway's own session listing shows the exact key"
} else {
""
};
anyhow::bail!("could not open a {harness} session: {message}{hint}");
}
let connection = response
.pointer("/result/connection")
.and_then(serde_json::Value::as_str)
.context("runtime open returned no connection")?
.to_string();
let runtime_id = response
.pointer("/result/handle/runtime_id")
.and_then(serde_json::Value::as_str)
.unwrap_or("(unknown)")
.to_string();
eprintln!("✓ {harness} session {runtime_id} open — type a line and press Enter; Ctrl-D leaves (the harness keeps running only if it was already running)");
async fn drive_turn(
service: &mut supercode_harness::HarnessSessionService,
connection: String,
text: String,
) -> (Option<String>, String) {
let response = service
.handle_async(serde_json::json!({
"jsonrpc": "2.0", "id": 0, "method": "harness.v1.runtimes.send_input",
"params": {"connection": connection, "text": text},
}))
.await;
let failed = response
.get("error")
.and_then(|error| error.get("message"))
.and_then(serde_json::Value::as_str)
.map(str::to_string);
let mut reply = String::new();
let mut drained = Vec::new();
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(900);
'turn: while std::time::Instant::now() < deadline {
let batch = service.poll_runtimes().await;
if batch.is_empty() {
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
continue;
}
let mut finished = false;
for event in &batch {
match event
.pointer("/params/event/kind")
.and_then(serde_json::Value::as_str)
{
Some("supercode/acp_request_completed") | Some("transport_closed") => {
finished = true;
}
_ => {}
}
}
drained.extend(batch);
if finished {
break 'turn;
}
}
for event in drained {
let Some(kind) = event
.pointer("/params/event/kind")
.and_then(serde_json::Value::as_str)
else {
continue;
};
match kind {
"session/update" => {
if let Some(text) = event
.pointer("/params/event/payload/params/update")
.filter(|update| {
update
.get("sessionUpdate")
.and_then(serde_json::Value::as_str)
== Some("agent_message_chunk")
})
.and_then(|update| update.pointer("/content/text"))
.and_then(serde_json::Value::as_str)
{
reply.push_str(text);
}
}
"transport_stderr" | "supercode/acp_request_completed" => {}
other => {
if let Some(line) = event.pointer("/params/event/payload") {
eprintln!("[{other}] {line}");
}
}
}
}
(failed, reply)
}
if let Some(text) = prompt {
let (failed, reply) = drive_turn(&mut service, connection.clone(), text.to_string()).await;
if let Some(message) = failed {
anyhow::bail!("the {harness} turn failed: {message}");
}
println!("{reply}");
} else {
let mut lines = tokio::io::BufReader::new(tokio::io::stdin()).lines();
while let Some(line) = lines.next_line().await? {
let text = line.trim().to_string();
if text.is_empty() {
continue;
}
let (failed, reply) = drive_turn(&mut service, connection.clone(), text).await;
if let Some(message) = failed {
eprintln!("✗ the {harness} turn failed: {message}");
continue;
}
println!("{reply}");
}
}
service
.handle_async(serde_json::json!({
"jsonrpc": "2.0", "id": 99, "method": "harness.v1.runtimes.close",
"params": {"connection": connection},
}))
.await;
Ok(())
}
async fn harness_serve_cmd(poll_ms: u64) -> Result<()> {
use tokio::io::AsyncBufReadExt;
if poll_ms == 0 {
anyhow::bail!("harness serve: --poll-ms must be greater than zero");
}
let mut service = supercode_harness::HarnessSessionService::new();
let index_notifier = service.session_index_notifier();
let mut lines = tokio::io::BufReader::new(tokio::io::stdin()).lines();
let mut interval = tokio::time::interval(std::time::Duration::from_millis(poll_ms));
let mut index_reconcile = tokio::time::interval(std::time::Duration::from_secs(60));
let mut runtime_events = tokio::time::interval(std::time::Duration::from_millis(10));
runtime_events.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
let (discovery_tx, mut discovery_rx) = tokio::sync::mpsc::unbounded_channel();
let discoveries = std::sync::Arc::new(tokio::sync::Semaphore::new(DISCOVERIES_IN_FLIGHT));
let (opened_tx, mut opened_rx) = tokio::sync::mpsc::unbounded_channel();
let (detached_tx, mut detached_rx) = tokio::sync::mpsc::unbounded_channel();
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
interval.tick().await;
index_reconcile.tick().await;
loop {
tokio::select! {
line = lines.next_line() => {
let Some(line) = line? else {
break;
};
let request = match serde_json::from_str::<serde_json::Value>(&line) {
Ok(request) => request,
Err(error) => {
let response = serde_json::json!({
"jsonrpc": "2.0",
"id": null,
"error": {"code": -32700, "message": error.to_string()},
});
println!("{response}");
io::stdout().flush()?;
continue;
}
};
if request.get("method").and_then(serde_json::Value::as_str)
== Some("harness.v1.sessions.discover")
{
let discovery_tx = discovery_tx.clone();
let id = request
.get("id")
.cloned()
.unwrap_or(serde_json::Value::Null);
let Ok(permit) = std::sync::Arc::clone(&discoveries).try_acquire_owned() else {
let response = serde_json::json!({
"jsonrpc": "2.0",
"id": id,
"error": {
"code": -32000,
"message": format!(
"`harness.v1.sessions.discover` is busy: {DISCOVERIES_IN_FLIGHT} discoveries are already waiting on session stores that have not answered; retry when one of them finishes",
),
},
});
println!("{response}");
io::stdout().flush()?;
continue;
};
tokio::spawn(async move {
let discovery = tokio::task::spawn_blocking(move || {
let _permit = permit;
supercode_harness::HarnessSessionService::new().handle(request)
});
let response = match tokio::time::timeout(
supercode_harness::SESSION_DISCOVER_DEADLINE,
discovery,
)
.await
{
Ok(Ok(response)) => response,
Ok(Err(error)) => serde_json::json!({
"jsonrpc": "2.0",
"id": id,
"error": {"code": -32000, "message": error.to_string()},
}),
Err(_) => serde_json::json!({
"jsonrpc": "2.0",
"id": id,
"error": {
"code": -32000,
"message": format!(
"`harness.v1.sessions.discover` gave up after {}s: a session store did not answer",
supercode_harness::SESSION_DISCOVER_DEADLINE.as_secs()
),
},
}),
};
let _ = discovery_tx.send(response);
});
continue;
}
if let Some(open) = supercode_harness::HarnessSessionService::runtime_open(&request) {
let opened_tx = opened_tx.clone();
tokio::spawn(async move {
let _ = opened_tx.send(open.open().await);
});
continue;
}
if let Some(call) = service.detach_runtime(&request) {
let detached_tx = detached_tx.clone();
tokio::spawn(async move {
let _ = detached_tx.send(call.run().await);
});
continue;
}
if let Some(call) = service.detach(&request) {
let detached_tx = detached_tx.clone();
tokio::spawn(async move {
let _ = detached_tx.send(call.run().await);
});
continue;
}
let response = service.handle_async(request).await;
println!("{response}");
io::stdout().flush()?;
}
Some(response) = discovery_rx.recv() => {
println!("{response}");
io::stdout().flush()?;
}
Some(opened) = opened_rx.recv() => {
let response = service.finish_runtime_open(opened).await;
println!("{response}");
io::stdout().flush()?;
}
Some(answer) = detached_rx.recv() => {
let response = service.finish_detached(answer);
println!("{response}");
io::stdout().flush()?;
}
_ = runtime_events.tick() => {
let notifications = service.poll_runtimes().await;
if !notifications.is_empty() {
for notification in notifications {
println!("{notification}");
}
io::stdout().flush()?;
}
}
_ = interval.tick() => {
let mut notifications = service.poll();
notifications.extend(service.poll_session_runtime_states().await);
notifications.extend(service.poll_session_activities().await);
notifications.extend(service.poll_runtimes().await);
for notification in notifications {
println!("{notification}");
}
io::stdout().flush()?;
}
_ = index_notifier.notified() => {
for notification in service.poll_session_indexes() {
println!("{notification}");
}
io::stdout().flush()?;
}
_ = index_reconcile.tick() => {
for notification in service.poll_session_indexes() {
println!("{notification}");
}
io::stdout().flush()?;
}
}
}
io::stdout().flush()?;
service.kill_all_runtime_groups();
drop(service);
std::process::exit(0)
}
const DISCOVERIES_IN_FLIGHT: usize = 4;
#[derive(Debug, Clone, PartialEq, Eq)]
enum ResolvedResumeSession {
Path(PathBuf),
Store(StoredSessionLocator),
Catalog(SessionLocator),
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct StoredSessionLocator {
name: String,
archived: bool,
}
#[derive(Debug, Clone)]
struct StoreResumeState {
name: String,
handoff_note: Option<String>,
reduction_log: ReductionLog,
runtime_manifest: Option<supercode_harness::ClaudeRuntimeManifest>,
}
#[derive(Debug)]
struct LoadedResumeSession {
session: Session,
store: Option<StoreResumeState>,
}
fn stored_session_for_path(store: &SessionStore, path: &Path) -> Option<StoredSessionLocator> {
let canonical = std::fs::canonicalize(path).ok()?;
store.list().into_iter().find_map(|info| {
let candidate = store.session_path_for(&info.name, info.archived).ok()?;
(std::fs::canonicalize(candidate).ok()? == canonical).then_some(StoredSessionLocator {
name: info.name,
archived: info.archived,
})
})
}
fn stored_session_for_name(store: &SessionStore, query: &str) -> Result<StoredSessionLocator> {
let name = resolve_session(store, query)?;
let mut matching = store.list().into_iter().filter(|info| info.name == name);
let first = matching
.next()
.ok_or_else(|| anyhow::anyhow!("no session matching `{query}`"))?;
let chosen = matching.fold(first, |chosen, candidate| {
if chosen.archived && !candidate.archived {
candidate
} else {
chosen
}
});
Ok(StoredSessionLocator {
name,
archived: chosen.archived,
})
}
fn resolve_resume_session(
input: &str,
harness: Option<&str>,
homes: HarnessHomes,
store: &SessionStore,
) -> Result<ResolvedResumeSession> {
let path = PathBuf::from(input);
if path.exists() {
if let Some(name) = stored_session_for_path(store, &path) {
return Ok(ResolvedResumeSession::Store(name));
}
return Ok(ResolvedResumeSession::Path(path));
}
if matches!(harness, Some("supercode" | "native")) {
return stored_session_for_name(store, input).map(ResolvedResumeSession::Store);
}
let store_match = if harness.is_none() {
match stored_session_for_name(store, input) {
Ok(locator) => Some(locator),
Err(error) if error.to_string().starts_with("no session matching") => None,
Err(error) => return Err(error),
}
} else {
None
};
let query = DiscoveryQuery {
harnesses: harness
.map(|value| vec![HarnessId::new(value)])
.unwrap_or_else(|| {
[
HarnessId::CLAUDE_CODE,
HarnessId::CODEX,
HarnessId::GEMINI,
HarnessId::GROK,
HarnessId::GOOSE,
HarnessId::OPENCODE,
HarnessId::PI,
]
.into_iter()
.map(HarnessId::from)
.collect()
}),
homes,
..DiscoveryQuery::default()
};
let mut matches: Vec<_> = supercode_harness::discover_sessions(&query)?
.into_iter()
.filter(|descriptor| descriptor.locator.session_id == input)
.collect();
match (store_match, matches.len()) {
(Some(name), 0) => Ok(ResolvedResumeSession::Store(name)),
(Some(_), _) => anyhow::bail!(
"session id `{input}` is ambiguous between supercode's store and a native harness — \
retry with `--harness supercode` or the source harness name"
),
(None, 0) => {
if let Some(harness) = harness {
anyhow::bail!(
"no `{harness}` session has id `{input}` — check the id with \
`supercode discover --harness {harness}`, or pass an existing session path"
);
}
anyhow::bail!(
"no discovered Claude Code, Codex, Gemini CLI, Goose, OpenCode, Pi, Grok, or Volter Harness session has id \
`{input}` — check the id with `supercode discover`, or pass an existing session path"
);
}
(None, 1) => Ok(ResolvedResumeSession::Catalog(
matches.pop().expect("one match").locator,
)),
(None, _) => {
let mut owners: Vec<_> = matches
.iter()
.map(|descriptor| descriptor.locator.harness.as_str())
.collect();
owners.sort_unstable();
owners.dedup();
let owner_count = owners.len();
let owners = owners.join(", ");
if harness.is_some() || owner_count == 1 {
anyhow::bail!(
"session id `{input}` still matches multiple persisted sessions in `{owners}` \
— pass the exact session path"
);
}
anyhow::bail!(
"session id `{input}` is ambiguous across harnesses ({owners}) — retry with \
`supercode resume {input} --harness <HARNESS>`, or pass the exact session path"
);
}
}
}
fn load_resume_session(
input: &str,
harness: Option<&str>,
opencode_session: Option<&str>,
homes: HarnessHomes,
) -> Result<LoadedResumeSession> {
let store = session_store_for_resume();
load_resume_session_from_store(input, harness, opencode_session, homes, &store)
}
fn load_stored_subagents(
store: &SessionStore,
locator: &StoredSessionLocator,
) -> Result<Vec<Session>> {
store
.list_subagent_ids_from(&locator.name, locator.archived)?
.into_iter()
.map(|child_id| {
let raw = store
.load_subagent_transcript_from(&locator.name, &child_id, locator.archived)?
.ok_or_else(|| anyhow::anyhow!("stored subagent `{child_id}` disappeared"))?;
let child = Session::from_sidecar_str(&raw).with_context(|| {
format!(
"parsing stored subagent `{child_id}` for `{}`",
locator.name
)
})?;
let path =
store.subagent_transcript_path_for(&locator.name, &child_id, locator.archived)?;
fail_loudly_on_parse_loss(&child, &path, false)?;
Ok(child)
})
.collect()
}
fn load_resume_session_from_store(
input: &str,
harness: Option<&str>,
opencode_session: Option<&str>,
homes: HarnessHomes,
store: &SessionStore,
) -> Result<LoadedResumeSession> {
let resolved = resolve_resume_session(input, harness, homes, store)?;
let (session, store_state, parse_path) = match &resolved {
ResolvedResumeSession::Path(file) => {
let is_sqlite = looks_like_sqlite(file);
if opencode_session.is_some() && !is_sqlite {
anyhow::bail!(
"--opencode-session requires an OpenCode SQLite `opencode*.db` input; {} is not one",
file.display()
);
}
if is_sqlite {
let selected = opencode_session.map(str::to_string);
warn_on_opencode_multi_session(file, &selected, "resuming");
}
(
supercode_harness::load_session_path(file, opencode_session)
.with_context(|| format!("loading session {}", file.display()))?,
None,
Some(file.clone()),
)
}
ResolvedResumeSession::Store(locator) => {
let name = &locator.name;
if opencode_session.is_some() {
anyhow::bail!(
"--opencode-session cannot select inside supercode store session `{name}`"
);
}
let sidecar = store.load_sidecar_from(name, locator.archived)?;
let mut session = match sidecar.as_deref() {
Some(raw) => {
let session = Session::from_sidecar_str(raw)
.with_context(|| format!("parsing sidecar for `{name}`"))?;
let sidecar_path = store.sidecar_path_for(name, locator.archived)?;
fail_loudly_on_parse_loss(&session, &sidecar_path, false)?;
let log = load_reduction_log_checked_from(store, name, locator.archived)?;
reduce::verify_log(&log, &session).map_err(|error| {
anyhow::anyhow!(
"{error} — repair pointer: `sessions show-reductions {name}`"
)
})?;
session
}
None => {
parse_native_store_session(name, &store.load_from(name, locator.archived)?)?
}
};
session.subagents = load_stored_subagents(store, locator)?;
let reduction_log = if sidecar.is_some() {
load_reduction_log_checked_from(store, name, locator.archived)?
} else {
ReductionLog::default()
};
let handoff_note = if sidecar.is_some() {
load_saved_handoff_note_from(store, name, locator.archived)?.0
} else {
None
};
(
session,
Some(StoreResumeState {
name: name.clone(),
handoff_note,
reduction_log,
runtime_manifest: store
.load_claude_runtime_manifest_from(name, locator.archived)?,
}),
None,
)
}
ResolvedResumeSession::Catalog(locator) => {
if opencode_session.is_some() {
anyhow::bail!(
"--opencode-session selects a session inside an explicit OpenCode SQLite path; \
`{input}` is already a resolved session id"
);
}
(
supercode_harness::load_session(locator).with_context(|| {
format!(
"loading {} session {} from {}",
locator.harness.as_str(),
locator.session_id,
locator.storage.path().display()
)
})?,
None,
Some(locator.storage.path().to_path_buf()),
)
}
};
if let Some(path) = parse_path {
fail_loudly_on_parse_loss(&session, &path, false)?;
}
Ok(LoadedResumeSession {
session,
store: store_state,
})
}
fn current_unix_seconds() -> i64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|duration| duration.as_secs().min(i64::MAX as u64) as i64)
.unwrap_or(0)
}
#[derive(Clone)]
struct ResumeRuntimeOptions {
dry_run: bool,
acp: bool,
serve: bool,
frontend: Option<ResumeFrontendArg>,
frontend_compatibility: FrontendCompatibilityArg,
bind: Option<String>,
token: Option<String>,
lease_ttl_ms: Option<u64>,
}
async fn resume_cmd(
cli: &Cli,
input: &str,
prompt: Option<String>,
harness: Option<&str>,
runtime: ResumeRuntimeOptions,
opencode_session: Option<&str>,
) -> Result<()> {
let ResumeRuntimeOptions {
dry_run,
acp,
serve,
frontend,
frontend_compatibility,
bind: bind_arg,
token: token_arg,
lease_ttl_ms,
} = runtime;
validate_frontend_compatibility(frontend, frontend_compatibility)?;
let resolved_config = resolve_config(cli)?;
let loaded = load_resume_session(input, harness, opencode_session, HarnessHomes::default())?;
let session = loaded.session;
let store_resume = loaded.store;
let inherited_reduction_log = store_resume
.as_ref()
.map(|state| state.reduction_log.clone())
.unwrap_or_default();
let handoff_note = store_resume
.as_ref()
.and_then(|state| state.handoff_note.clone());
let source_harness = match session.meta.source {
supercode_harness::SessionSource::ClaudeCode => "claude-code",
supercode_harness::SessionSource::Codex => "codex",
supercode_harness::SessionSource::OpenCode => "opencode",
supercode_harness::SessionSource::Pi => "pi",
supercode_harness::SessionSource::Grok => "grok",
supercode_harness::SessionSource::Gemini => "gemini",
supercode_harness::SessionSource::Goose => "goose",
supercode_harness::SessionSource::OpenClaw => "openclaw",
supercode_harness::SessionSource::Hermes => "hermes",
supercode_harness::SessionSource::Native => "supercode",
};
let quiet = effective_quiet(cli);
let cwd_for_cfg = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let live_source = supercode_harness::LiveRuntimeSource {
harness: source_harness.to_string(),
session_id: store_resume
.as_ref()
.map(|state| state.name.clone())
.or_else(|| session.meta.session_id.clone())
.or_else(|| {
Path::new(input)
.file_stem()
.and_then(|value| value.to_str())
.map(str::to_owned)
})
.unwrap_or_else(|| input.to_string()),
workspace: session
.meta
.cwd
.clone()
.unwrap_or_else(|| cwd_for_cfg.clone()),
};
let (fc_for_resume, trusted_extends) = uc::load_with_trusted_extends(&cwd_for_cfg);
let frontend_metadata = supercode_harness::FrontendRuntimeMetadata {
source_harness: Some(source_harness.to_string()),
emulation_profile: trusted_extends,
};
if serve {
if let Some(record) = live_runtime_for_source(&live_source).await? {
anyhow::bail!(
"source session already has live runtime `{}`; open it with `supercode open {}` \
instead of starting a duplicate",
record.runtime_session_id,
record.runtime_session_id
);
}
}
let notify_settings = effective_notify(cli, &fc_for_resume);
print_startup_banners(cli, &resolved_config);
let mut config = resolved_config;
if acp || serve || frontend.is_some() {
config.approval_handler = None;
}
let http_host = if serve {
require_server_capability(&fc_for_resume, &config, "resume --serve")?;
let capability = fc_for_resume
.capabilities
.get("server")
.cloned()
.unwrap_or_default();
let bind = bind_arg
.or_else(|| supercode_harness::configfile::server_bind(&capability))
.unwrap_or_else(|| "127.0.0.1:0".to_string());
if !is_loopback_bind(&bind) {
eprintln!("warning: resume --serve is binding beyond localhost at `{bind}`");
}
let token: Arc<str> = token_arg
.or_else(|| std::env::var("SUPERCODE_SERVER_TOKEN").ok())
.or_else(|| supercode_harness::configfile::server_token(&capability))
.unwrap_or_else(supercode_harness::server::generate_token)
.into();
Some((bind, token, lease_ttl_ms))
} else {
None
};
let reduced = effective_reduced(
cli,
&fc_for_resume,
config.module_activation.is_active(ModuleId::Reduction),
);
if cli.cwd.is_none() {
if let Some(cwd) = &session.meta.cwd {
if cwd.is_dir() {
config.cwd = cwd.clone();
}
}
}
let runtime_cwd = config.cwd.clone();
let runtime_manifest = if let Some(manifest) = store_resume
.as_ref()
.and_then(|state| state.runtime_manifest.clone())
{
Some(manifest)
} else if session.meta.source == SessionSource::ClaudeCode {
Some(supercode_harness::ClaudeRuntimeManifest::from_session(
&session,
)?)
} else {
None
};
let claude_compat = if session.meta.source == SessionSource::ClaudeCode {
let claude_home = supercode_interchange::user_home()
.map(std::path::PathBuf::into_os_string)
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from("."))
.join(".claude");
Some(
supercode_harness::claude_compat::apply_resume_compatibility(
&mut config,
&claude_home,
)?,
)
} else {
None
};
if dry_run {
let report_cwd = config.cwd.clone();
let report_model = config.model.clone();
let custom_agents: Vec<_> = claude_compat
.as_ref()
.into_iter()
.flat_map(|snapshot| snapshot.project_agents.iter())
.map(|agent| {
serde_json::json!({
"name": agent.definition.name,
"path": agent.path,
"original_model": agent.original_model,
"resolved_model": agent.definition.model,
"original_tools": agent.original_tools,
"mapped_tools": agent.definition.tools,
})
})
.collect();
let runtime_report = runtime_manifest.as_ref().map(|manifest| {
let live_children = manifest
.background_children
.iter()
.filter(|child| {
matches!(
child.state,
supercode_harness::ClaudeBackgroundState::LaunchPending
| supercode_harness::ClaudeBackgroundState::Running
)
})
.count();
serde_json::json!({
"execution_state": "paused",
"posture": &manifest.posture,
"active_crons": &manifest.active_crons,
"pending_wakeups": &manifest.pending_wakeups,
"queue": {
"enqueued": manifest.queue.enqueued,
"dequeued": manifest.queue.dequeued,
"removed": manifest.queue.removed,
"unmatched_evidence_count": manifest.queue.pending.len(),
"reconstructable": true,
"will_replay": false,
},
"background_children": {
"total": manifest.background_children.len(),
"live": live_children,
"terminal": manifest.background_children.len() - live_children,
"reported_pending": manifest.reported_pending_background_children,
},
"residue_record_count": manifest.residue.len(),
})
});
let reduction_report = if reduced {
let core = enabled_builtin_tool_names(&config);
config.tool_advertising = ToolAdvertising::Deferred { core };
config.api_key = Some("supercode-dry-run-no-request".to_string());
let mut policy = configured_reduction_policy(&config);
reduce::prepare_read_freshness(&mut policy, &session.messages);
let full_message_tokens = supercode_runtime::estimate_view_tokens(&session.messages);
let mut agent = supercode_harness::create_agent(config)?;
if let Some(note) = handoff_note.as_deref() {
agent.append_system_note(&format!("\n\n{note}"));
}
if let Some(manifest) = runtime_manifest.clone() {
agent.set_claude_runtime_manifest(manifest);
}
agent.set_reduction_policy(policy.clone());
let system_msg = agent.history()[0].clone();
let tool_schemas = agent.tool_schemas();
let prompt_msg = prompt.as_ref().map(|p| ChatMessage::user(p.clone()));
let request_messages = |view: &[ChatMessage]| {
let mut candidate = Vec::with_capacity(view.len() + 2);
candidate.push(system_msg.clone());
candidate.extend_from_slice(view);
if let Some(prompt) = &prompt_msg {
candidate.push(prompt.clone());
}
candidate
};
let baseline_input_tokens = supercode_runtime::estimate_request_tokens(
&request_messages(&session.messages),
&tool_schemas,
);
let target_applied = full_message_tokens >= PROACTIVE_REDUCTION_MIN_INPUT_TOKENS;
let target_input_tokens =
proactive_reduction_target(baseline_input_tokens, full_message_tokens);
let context_limit =
model_context_limit(&report_model).unwrap_or(UNKNOWN_MODEL_CONTEXT_FLOOR);
let (view, log, _) = reduce::reduce_to_fit(
&session.messages,
&policy,
&inherited_reduction_log,
|view| {
let request = request_messages(view);
supercode_runtime::context_guard(&request, &tool_schemas, context_limit).0
&& (!target_applied
|| supercode_runtime::estimate_request_tokens(&request, &tool_schemas)
<= target_input_tokens)
},
);
let projected_input_tokens =
supercode_runtime::estimate_request_tokens(&request_messages(&view), &tool_schemas);
Some(serde_json::json!({
"full_message_tokens": full_message_tokens,
"view_message_tokens": supercode_runtime::estimate_view_tokens(&view),
"baseline_input_tokens": baseline_input_tokens,
"projected_input_tokens": projected_input_tokens,
"target_input_tokens": target_input_tokens,
"target_percent": PROACTIVE_REDUCTION_TARGET_PERCENT,
"target_applied": target_applied,
"target_met": !target_applied || projected_input_tokens <= target_input_tokens,
"stub_count": log.reductions.len(),
"reversible": true,
}))
} else {
None
};
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"source": format!("{:?}", session.meta.source),
"session_id": session.meta.session_id,
"cwd": report_cwd,
"target_model": report_model.clone(),
"target_context_limit": model_context_limit(&report_model)
.unwrap_or(UNKNOWN_MODEL_CONTEXT_FLOOR),
"replay_message_count": session.messages.len(),
"estimated_replay_tokens": supercode_runtime::estimate_view_tokens(&session.messages),
"attached_subagent_count": session.subagents.len(),
"custom_agents": custom_agents,
"claude_global_instructions_loaded": claude_compat
.as_ref()
.is_some_and(|snapshot| snapshot.global_instructions.is_some()),
"runtime": runtime_report,
"reduction": reduction_report,
"runtime_activation": "paused",
"source_log_modified": false,
"store_written": false,
"provider_request_sent": false,
}))?
);
return Ok(());
}
maybe_onboard(cli).await;
require_api_key(cli)?;
if !quiet {
eprintln!(
"Resuming {:?} session {} ({} messages).",
session.meta.source,
session.meta.session_id.as_deref().unwrap_or("?"),
session.messages.len()
);
}
if !quiet {
if let Some(manifest) = runtime_manifest.as_ref() {
let crons = manifest.active_crons.len();
let wakeups = manifest.pending_wakeups.len();
let queued = manifest.queue.pending.len();
if crons + wakeups + queued > 0 {
eprintln!(
"Claude runtime state carried and PAUSED: {crons} cron job(s), \
{wakeups} scheduled wakeup(s), {queued} queued prompt(s). None of them \
will execute here. To run them, compile them into the orchestrator: \
`supercode orchestrator import --from claude-session {}`.",
session.meta.session_id.as_deref().unwrap_or("<session-id>")
);
}
}
}
let (sink, spinner) = streaming_sink(prompt.is_none(), quiet);
config.event_sink = Some(with_trace(sink, cli.trace));
if !reduced {
let store = session_store()?;
let name = new_session_name(&config.cwd, config.session_name.as_deref());
store
.save_imported_subagents(&name, &session.subagents)
.with_context(|| format!("persisting imported subagents for `{name}`"))?;
if let Some(manifest) = &runtime_manifest {
store
.save_claude_runtime_manifest(&name, manifest)
.with_context(|| format!("persisting Claude runtime state for `{name}`"))?;
}
let recorder = SidecarWriter::create(&store.sidecar_path(&name), &session)?;
let mut agent = supercode_harness::resume_agent(config, session)?;
if let Some(note) = handoff_note.as_deref() {
agent.append_system_note(&format!("\n\n{note}"));
}
let mut inherited_log = inherited_reduction_log.clone();
inherited_log.expanded.append(&mut inherited_log.reductions);
agent.set_reduction_log(inherited_log);
agent.set_recorder(recorder);
if let Some(manifest) = runtime_manifest {
agent.set_claude_runtime_manifest(manifest);
}
arm_context_guard(&mut agent);
if store_resume.is_some() {
store
.save_reduction_log(&name, agent.reduction_log())
.with_context(|| format!("persisting inherited reduction log for `{name}`"))?;
}
arm_session_state(&mut agent, &store, &name, quiet);
persist_session(&agent, &name);
if acp {
let persist_name = name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named_with_frontend_metadata(
agent,
name,
frontend_metadata.clone(),
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
let server = supercode_harness::acp_server::AcpServer::new(runtime.clone()).await?;
let stdin = tokio::io::BufReader::new(tokio::io::stdin());
let stdout = tokio::io::stdout();
let result = supercode_harness::acp_server::run_stdio(server, stdin, stdout).await;
runtime.shutdown().await;
result?;
return Ok(());
}
if let Some((bind, token, lease_ttl_ms)) = http_host {
let persist_name = name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named_with_frontend_requests(
agent,
name,
frontend_metadata.clone(),
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
return host_sdk_http_runtime(runtime, &bind, token, live_source.clone(), lease_ttl_ms)
.await;
}
if let Some(frontend) = frontend {
let persist_name = name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named_with_frontend_requests(
agent,
name.clone(),
frontend_metadata.clone(),
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
let result = match frontend {
ResumeFrontendArg::Codex => {
run_stock_codex_frontend(
runtime.clone(),
runtime.session_id(),
&runtime_cwd,
frontend_compatibility,
quiet,
None,
)
.await
}
ResumeFrontendArg::Opencode => {
run_stock_opencode_frontend(
runtime.clone(),
runtime.session_id(),
&runtime_cwd,
quiet,
None,
)
.await
}
ResumeFrontendArg::Goose => {
run_stock_goose_frontend(runtime.clone(), &runtime_cwd, quiet, None).await
}
}
.map(|_| ());
runtime.shutdown().await;
runtime
.finalize_with(|agent| persist_session(agent, &name))
.await;
return result;
}
if prompt.is_none() && tui::should_activate(agent.config()) {
let persist_name = name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named_with_frontend_requests(
agent,
name.clone(),
frontend_metadata.clone(),
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
let result = run_attachable_terminal(runtime.clone(), live_source.clone()).await;
runtime
.finalize_with(|agent| persist_session(agent, &name))
.await;
runtime.shutdown().await;
return result;
}
let result = match prompt {
Some(p) => {
hooks::fire_user_prompt_submit(
¬ify_settings.hooks,
&p,
&cwd_for_cfg,
None,
quiet,
);
spinner.reset_turn(); spinner.start("Thinking…");
let turn_start = std::time::Instant::now(); let outcome = race_ctrl_c(
supercode_harness::submit_agent(&mut agent, &p),
Some(&spinner),
)
.await;
spinner.stop();
spinner.clear_counter(); let Some(result) = outcome else {
persist_session(&agent, &name);
print_interrupt_notice(
quiet,
"turn cancelled; continuation saved at its last completed step",
);
std::process::exit(130);
};
match result {
Ok(reply_text) => {
println!();
notify::maybe_fire(
¬ify_settings,
false,
io::stderr().is_terminal(),
turn_start.elapsed(),
&effective_model(cli),
&reply_text,
);
Ok(())
}
Err(error) => Err(error.into()),
}
}
None => {
let model = effective_model(cli);
chat(
&mut agent,
Some(&name),
&model,
&spinner,
quiet,
¬ify_settings,
)
.await
}
};
persist_session(&agent, &name);
return result;
}
let model = config.model.clone();
let context_limit = model_context_limit(&model).unwrap_or(UNKNOWN_MODEL_CONTEXT_FLOOR);
let mut policy = configured_reduction_policy(&config);
let full_msgs = session.messages.clone();
reduce::prepare_read_freshness(&mut policy, &full_msgs);
let full_tokens = supercode_runtime::estimate_view_tokens(&full_msgs);
let core = enabled_builtin_tool_names(&config);
let builtin_count = core.len();
config.tool_advertising = ToolAdvertising::Deferred { core };
let mcp_deferred_count = 0usize;
let deferred_tok: u64 = 0;
let session_source = session.meta.source;
let session_id_display = session.meta.session_id.clone();
let message_count = session.messages.len();
let title = session
.messages
.iter()
.find(|m| m.role == Role::User)
.and_then(|m| m.content.as_deref())
.map(|c| first_line(c, 60))
.unwrap_or_else(|| "resumed session".into());
let cwd_for_name = config.cwd.clone();
let auto_title_gate = AutoTitleGate::capture(&config);
let span_summary_gate = SpanSummaryGate::capture(&config);
let mut agent = supercode_harness::create_agent(config)?;
if let Some(note) = handoff_note.as_deref() {
agent.append_system_note(&format!("\n\n{note}"));
}
if let Some(manifest) = runtime_manifest.clone() {
agent.set_claude_runtime_manifest(manifest);
}
maybe_install_auto_titler(&mut agent, &auto_title_gate);
maybe_install_span_summarizer(&mut agent, &span_summary_gate);
agent.set_reduction_policy(policy.clone());
let system_msg = agent.history()[0].clone();
let tool_schemas = agent.tool_schemas();
let prompt_msg = prompt.as_ref().map(|p| ChatMessage::user(p.clone()));
let request_messages = |view: &[ChatMessage]| {
let mut candidate = Vec::with_capacity(view.len() + 2);
candidate.push(system_msg.clone());
candidate.extend_from_slice(view);
if let Some(pm) = &prompt_msg {
candidate.push(pm.clone());
}
candidate
};
let baseline_request_tokens =
supercode_runtime::estimate_request_tokens(&request_messages(&full_msgs), &tool_schemas);
let replay_message_tokens = supercode_runtime::estimate_view_tokens(&full_msgs);
let proactive_target_applied = replay_message_tokens >= PROACTIVE_REDUCTION_MIN_INPUT_TOKENS;
let target_request_tokens =
proactive_reduction_target(baseline_request_tokens, replay_message_tokens);
let meets_reduction_target = |view: &[ChatMessage]| {
let candidate = request_messages(view);
supercode_runtime::context_guard(&candidate, &tool_schemas, context_limit).0
&& (!proactive_target_applied
|| supercode_runtime::estimate_request_tokens(&candidate, &tool_schemas)
<= target_request_tokens)
};
let (mut view_msgs, mut initial_log, mut policy) = reduce::reduce_to_fit(
&full_msgs,
&policy,
&inherited_reduction_log,
meets_reduction_target,
);
if meets_reduction_target(&view_msgs)
&& initial_log
.reductions
.iter()
.any(|r| matches!(r.kind, reduce::ReductionKind::TurnsCleared { .. }))
{
if let Some(summary) =
agent.prepare_cleared_turns_summary(&full_msgs, &policy, &inherited_reduction_log)
{
let mut summarized_policy = policy.clone();
summarized_policy.cleared_turns_summary = Some(summary);
let (summarized_view, summarized_log) =
reduce::project_messages(&full_msgs, &summarized_policy, &inherited_reduction_log);
if meets_reduction_target(&summarized_view) {
view_msgs = summarized_view;
initial_log = summarized_log;
policy = summarized_policy;
}
}
}
let view_tokens = supercode_runtime::estimate_view_tokens(&view_msgs);
initial_log.attribution = Some(reduction_attribution(&initial_log, &session, Some(&policy)));
agent.set_reduction_policy(policy);
let mut projected_msgs = Vec::with_capacity(view_msgs.len() + 2);
projected_msgs.push(agent.history()[0].clone());
projected_msgs.extend(view_msgs.iter().cloned());
if let Some(p) = &prompt {
projected_msgs.push(ChatMessage::user(p.clone()));
}
let projected_tools = agent.tool_schemas();
let (fits, projected_request_tokens) =
supercode_runtime::context_guard(&projected_msgs, &projected_tools, context_limit);
let projected_input_tokens =
supercode_runtime::estimate_request_tokens(&projected_msgs, &projected_tools);
if !fits {
if !quiet {
print_context_guard_verdict(
&model,
baseline_request_tokens,
projected_request_tokens,
context_limit,
fits,
);
print_request_sent_status(false);
}
anyhow::bail!(
"cannot reduce below context limit: projected {} tokens + {} reserve exceeds model {} limit {}",
projected_request_tokens,
supercode_runtime::CONTEXT_RESPONSE_RESERVE_TOKENS,
model,
context_limit
);
}
let store = session_store()?;
let name = new_session_name(&cwd_for_name, agent.session_name());
let sidecar_path = store.sidecar_path(&name);
let full_bytes = serialized_bytes(&full_msgs);
let view_bytes = serialized_bytes(&view_msgs);
let stub_count = initial_log.reductions.len();
store
.save_imported_subagents(&name, &session.subagents)
.with_context(|| format!("persisting imported subagents for `{name}`"))?;
if let Some(manifest) = &runtime_manifest {
store
.save_claude_runtime_manifest(&name, manifest)
.with_context(|| format!("persisting Claude runtime state for `{name}`"))?;
}
let recorder = SidecarWriter::create(&sidecar_path, &session)?;
agent.load_session(session);
agent.set_recorder(recorder);
arm_session_state(&mut agent, &store, &name, quiet);
agent.set_context_limit(context_limit);
agent.set_reduction_log(initial_log.clone());
if !quiet {
print_reduced_banner(ReducedBanner {
source: session_source,
session_id: session_id_display.as_deref().unwrap_or("?"),
message_count,
builtin_count,
mcp_deferred_count,
deferred_tok,
full_tokens,
view_tokens,
baseline_request_tokens,
projected_input_tokens,
target_request_tokens,
target_met: projected_input_tokens <= target_request_tokens,
stub_count,
sidecar_path: &sidecar_path,
});
}
store.set_reduction_stats(&name, &title, full_bytes, view_bytes, stub_count as u32)?;
if !quiet {
print_context_guard_verdict(
&model,
full_tokens,
projected_request_tokens,
context_limit,
fits,
);
}
if acp {
persist_session(&agent, &name);
let persist_name = name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named_with_frontend_metadata(
agent,
name,
frontend_metadata.clone(),
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
let server = supercode_harness::acp_server::AcpServer::new(runtime.clone()).await?;
let stdin = tokio::io::BufReader::new(tokio::io::stdin());
let stdout = tokio::io::stdout();
let result = supercode_harness::acp_server::run_stdio(server, stdin, stdout).await;
runtime.shutdown().await;
result?;
return Ok(());
}
if let Some((bind, token, lease_ttl_ms)) = http_host {
let persist_name = name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named_with_frontend_requests(
agent,
name,
frontend_metadata,
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
return host_sdk_http_runtime(runtime, &bind, token, live_source.clone(), lease_ttl_ms)
.await;
}
if let Some(frontend) = frontend {
let persist_name = name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named_with_frontend_requests(
agent,
name.clone(),
frontend_metadata.clone(),
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
let result = match frontend {
ResumeFrontendArg::Codex => {
run_stock_codex_frontend(
runtime.clone(),
runtime.session_id(),
&runtime_cwd,
frontend_compatibility,
quiet,
None,
)
.await
}
ResumeFrontendArg::Opencode => {
run_stock_opencode_frontend(
runtime.clone(),
runtime.session_id(),
&runtime_cwd,
quiet,
None,
)
.await
}
ResumeFrontendArg::Goose => {
run_stock_goose_frontend(runtime.clone(), &runtime_cwd, quiet, None).await
}
}
.map(|_| ());
runtime.shutdown().await;
runtime
.finalize_with(|agent| {
persist_session(agent, &name);
if !quiet {
print_request_sent_status(agent.request_issued());
}
let final_log = agent.reduction_log().clone();
let final_policy = agent.reduction_policy().cloned().unwrap_or_default();
let (final_view, _) =
reduce::project_messages(&agent.history()[1..], &final_policy, &final_log);
let _ = store.save_reduction_log(&name, &final_log);
let _ = store.set_reduction_stats(
&name,
&title,
serialized_bytes(&agent.history()[1..]),
serialized_bytes(&final_view),
final_log.reductions.len() as u32,
);
if let Some(manifest) = agent.claude_runtime_manifest() {
let _ = store.save_claude_runtime_manifest(&name, manifest);
}
})
.await;
return result;
}
if prompt.is_none() && tui::should_activate(agent.config()) {
let persist_name = name.clone();
let runtime = supercode_harness::server::RpcEngine::new_named_with_frontend_requests(
agent,
name.clone(),
frontend_metadata,
Some(Box::new(move |agent: &SdkAgent| {
persist_session(agent, &persist_name);
apply_auto_title(agent, &persist_name);
})),
);
let result = run_attachable_terminal(runtime.clone(), live_source).await;
runtime
.finalize_with(|agent| {
persist_session(agent, &name);
if !quiet {
print_request_sent_status(agent.request_issued());
}
let final_log = agent.reduction_log().clone();
let final_policy = agent.reduction_policy().cloned().unwrap_or_default();
let (final_view, _) =
reduce::project_messages(&agent.history()[1..], &final_policy, &final_log);
let _ = store.save_reduction_log(&name, &final_log);
let _ = store.set_reduction_stats(
&name,
&title,
serialized_bytes(&agent.history()[1..]),
serialized_bytes(&final_view),
final_log.reductions.len() as u32,
);
if let Some(manifest) = agent.claude_runtime_manifest() {
let _ = store.save_claude_runtime_manifest(&name, manifest);
}
})
.await;
runtime.shutdown().await;
return result;
}
let result: Result<()> = match prompt {
Some(p) => {
hooks::fire_user_prompt_submit(¬ify_settings.hooks, &p, &cwd_for_cfg, None, quiet);
spinner.reset_turn(); spinner.start("Thinking…");
let turn_start = std::time::Instant::now(); let outcome = race_ctrl_c(
supercode_harness::submit_agent(&mut agent, &p),
Some(&spinner),
)
.await;
spinner.stop();
spinner.clear_counter(); let Some(send_result) = outcome else {
print_interrupt_notice(
quiet,
"turn cancelled; sidecar left at its last completed step",
);
std::process::exit(130);
};
match send_result {
Ok(reply_text) => {
println!();
apply_auto_title(&agent, &name);
notify::maybe_fire(
¬ify_settings,
false,
io::stderr().is_terminal(),
turn_start.elapsed(),
&model,
&reply_text,
);
Ok(())
}
Err(e) => Err(e.into()),
}
}
None => {
chat(
&mut agent,
Some(&name),
&model,
&spinner,
quiet,
¬ify_settings,
)
.await
}
};
if !quiet {
print_request_sent_status(agent.request_issued());
}
let mut final_log = agent.reduction_log().clone();
let final_policy = agent.reduction_policy().cloned().unwrap_or_default();
let (final_view, _) =
reduce::project_messages(&agent.history()[1..], &final_policy, &final_log);
if let Ok(sidecar_jsonl) = std::fs::read_to_string(&sidecar_path) {
if let Ok(final_sidecar) = Session::from_sidecar_str(&sidecar_jsonl) {
final_log.attribution = Some(reduction_attribution(
&final_log,
&final_sidecar,
Some(&final_policy),
));
}
}
let _ = store.save_reduction_log(&name, &final_log);
let _ = store.set_reduction_stats(
&name,
&title,
serialized_bytes(&agent.history()[1..]),
serialized_bytes(&final_view),
final_log.reductions.len() as u32,
);
if let Some(manifest) = agent.claude_runtime_manifest() {
let _ = store.save_claude_runtime_manifest(&name, manifest);
}
result
}
async fn live_runtime_for_source(
source: &supercode_harness::LiveRuntimeSource,
) -> Result<Option<supercode_harness::LiveRuntimeRecord>> {
let registry = supercode_harness::LocalRuntimeRegistry::new();
let query = supercode_harness::RuntimeRegistryQuery {
include_live: true,
include_persisted: false,
..supercode_harness::RuntimeRegistryQuery::default()
};
let _ = registry
.list(&query, &supercode_harness::RuntimeAuthorization::observer())
.await?;
let workspace =
std::fs::canonicalize(&source.workspace).unwrap_or_else(|_| source.workspace.clone());
Ok(supercode_harness::list_live_runtimes()?
.into_iter()
.find(|record| {
record.source.harness == source.harness
&& record.source.session_id == source.session_id
&& std::fs::canonicalize(&record.source.workspace)
.unwrap_or_else(|_| record.source.workspace.clone())
== workspace
}))
}
struct ReducedBanner<'a> {
source: SessionSource,
session_id: &'a str,
message_count: usize,
builtin_count: usize,
mcp_deferred_count: usize,
deferred_tok: u64,
full_tokens: u64,
view_tokens: u64,
baseline_request_tokens: u64,
projected_input_tokens: u64,
target_request_tokens: u64,
target_met: bool,
stub_count: usize,
sidecar_path: &'a Path,
}
fn print_reduced_banner(b: ReducedBanner<'_>) {
let source_label = match b.source {
SessionSource::ClaudeCode => "Claude Code",
SessionSource::Codex => "Codex",
SessionSource::OpenCode => "OpenCode",
SessionSource::Pi => "Pi",
SessionSource::Grok => "Grok",
SessionSource::Gemini => "Gemini",
SessionSource::Goose => "Goose",
SessionSource::OpenClaw => "OpenClaw",
SessionSource::Hermes => "Hermes",
SessionSource::Native => "Volter Harness",
};
let percent = if b.full_tokens == 0 {
0
} else {
((b.view_tokens as f64 / b.full_tokens as f64) * 100.0).round() as i64
};
eprintln!(
"{} {}",
ui::paint(ui::ACCENT, "↳"),
ui::paint(
ui::MUTED,
format!(
"imported {source_label} session {} ({} messages)",
b.session_id, b.message_count
)
),
);
eprintln!(
" {} {} builtin schemas · {} MCP schemas deferred (~{} schema tok/req avoided)",
ui::paint(ui::MUTED, "tools"),
b.builtin_count,
b.mcp_deferred_count,
commas(b.deferred_tok),
);
eprintln!(
" {} ~{} → {} ({percent}%) · {} stubs · full copy: {}",
ui::paint(ui::MUTED, "context"),
commas(b.full_tokens),
supercode_runtime::fmt_approx_tokens(b.view_tokens),
b.stub_count,
b.sidecar_path.display(),
);
let request_percent = pct_of(b.projected_input_tokens, b.baseline_request_tokens);
eprintln!(
" {} {} → {} input ({request_percent}%; target ≤{}% / {}: {})",
ui::paint(ui::MUTED, "request"),
supercode_runtime::fmt_approx_tokens(b.baseline_request_tokens),
supercode_runtime::fmt_approx_tokens(b.projected_input_tokens),
PROACTIVE_REDUCTION_TARGET_PERCENT,
supercode_runtime::fmt_approx_tokens(b.target_request_tokens),
if b.target_met { "met" } else { "missed" },
);
eprintln!(
" {}",
ui::paint(
ui::DIM,
"/status details · /expand <id> rehydrate a stub · export back anytime — always full-fidelity"
)
);
}
fn print_context_guard_verdict(
model: &str,
original_tokens: u64,
projected_tokens: u64,
context_limit: u64,
fits: bool,
) {
let verdict = if fits {
"within limit"
} else {
"OVER LIMIT — refusing to send"
};
eprintln!(
" {} model {} · limit {} tok · original ~{} tok · projected ~{} tok ({verdict})",
ui::paint(ui::MUTED, "guard"),
model,
commas(context_limit),
commas(original_tokens),
commas(projected_tokens),
);
}
fn print_request_sent_status(sent: bool) {
eprintln!(
" {} {}",
ui::paint(ui::MUTED, "request"),
if sent {
"sent"
} else {
"not sent (no live request was ever issued)"
},
);
}
async fn chat(
agent: &mut SdkAgent,
persist_name: Option<&str>,
initial_model_label: &str,
spinner: &Spinner,
quiet: bool,
notify_settings: ¬ify::NotifySettings,
) -> Result<()> {
use rustyline::error::ReadlineError;
use rustyline::history::DefaultHistory;
use rustyline::Editor;
let mut model_label = initial_model_label.to_string();
let mut rl: Option<Editor<ReplHelper, DefaultHistory>> = Some(Editor::new()?);
rl.as_mut()
.expect("editor initialized")
.set_helper(Some(ReplHelper::new()));
let history_file = uc::config_home().join("history.txt");
let _ = std::fs::create_dir_all(uc::config_home());
let _ = rl
.as_mut()
.expect("editor initialized")
.load_history(&history_file);
let title = terminal_title::TitleGuard::enter(quiet, &model_label);
if !quiet {
eprintln!(
"{}",
ui::paint(
ui::DIM,
" /help for commands · !cmd runs a shell command · end a line with \\ for multiline · Ctrl-D to exit"
)
);
eprintln!();
}
let mut pending: Vec<String> = Vec::new();
let mut titled_once = false;
let mut expanded: Vec<reduce::Reduction> = Vec::new();
let mut reduced_view: Vec<ChatMessage> = match agent.reduction_policy().cloned() {
Some(policy) => {
reduce::project_messages(&agent.history()[1..], &policy, agent.reduction_log()).0
}
None => Vec::new(),
};
let prompt = format!("{} ", ui::bold(ui::ACCENT, "›"));
let cont_prompt = format!("{} ", ui::paint(ui::DIM, "…"));
let mut consecutive_idle_interrupts = 0u32;
let steer_enabled =
soft_steer::capture_available(io::stdin().is_terminal(), io::stderr().is_terminal());
let mut queued_steer: Option<String> = None;
loop {
let readline_result: Result<String, ReadlineError> = match queued_steer.take() {
Some(line) => Ok(line),
None => rl.as_mut().expect("editor available").readline(&prompt),
};
match readline_result {
Ok(line) => {
consecutive_idle_interrupts = 0;
let mut full = line.trim_end().to_string();
while full.ends_with('\\') {
full.pop();
match rl
.as_mut()
.expect("editor available")
.readline(&cont_prompt)
{
Ok(more) => {
full.push('\n');
full.push_str(more.trim_end());
}
Err(_) => break,
}
}
let trimmed = full.trim().to_string();
if trimmed.is_empty() {
continue;
}
rl.as_mut()
.expect("editor available")
.add_history_entry(&trimmed)
.ok();
if trimmed.starts_with('/') {
let mut parts = trimmed.splitn(2, char::is_whitespace);
let verb = parts.next().unwrap_or("").to_string();
let rest = parts.next().unwrap_or("").trim().to_string();
match resolve_slash_command(&verb) {
Some(SlashCmd::Side) => {
if rest.is_empty() {
eprintln!("usage: /btw <question>");
continue;
}
spinner.start("Thinking…");
let outcome =
race_ctrl_c(agent.side_question(&rest), Some(spinner)).await;
spinner.stop();
match outcome {
Some(Ok(answer)) => println!("{answer}"),
Some(Err(e)) => eprintln!("side question failed: {e}"),
None => print_interrupt_notice(quiet, "side question cancelled"),
}
continue;
}
Some(SlashCmd::Review) => match agent.review_prompt(&rest) {
Some(prompt) => full = prompt,
None => {
eprintln!(
"no review template is configured for this harness (`[core.prompts] code-review`)"
);
continue;
}
},
_ => {
if handle_slash(
&full,
agent,
persist_name,
&mut model_label,
&mut pending,
&mut expanded,
&mut reduced_view,
)? {
break;
}
continue;
}
}
}
if let Some(shell_cmd) = trimmed.strip_prefix('!') {
let shell_cmd = shell_cmd.trim();
if shell_cmd.is_empty() {
if !quiet {
eprintln!(
"{}",
ui::paint(
ui::DIM,
" usage: !<command> — run a shell command, output only, \
nothing sent to the model"
)
);
}
} else {
run_shell_escape(shell_cmd).await;
}
continue;
}
let msg = if pending.is_empty() {
full
} else {
let ctx = pending.join("\n\n");
pending.clear();
format!("{ctx}\n\n{full}")
};
hooks::fire_user_prompt_submit(
¬ify_settings.hooks,
&msg,
&agent.config().cwd,
persist_name,
quiet,
);
spinner.reset_turn(); spinner.start("Thinking…");
title.set(&model_label, terminal_title::TitleState::Thinking); let turn_start = std::time::Instant::now(); let (outcome, steer) = race_ctrl_c_with_steer(
supercode_harness::submit_agent(agent, &msg),
Some(spinner),
steer_enabled,
quiet,
)
.await;
spinner.stop();
spinner.clear_counter(); title.set(&model_label, terminal_title::TitleState::Idle); let Some(send_result) = outcome else {
let detail = if steer.is_some() {
"turn cancelled, session intact — queued steer discarded"
} else {
"turn cancelled, session intact"
};
print_interrupt_notice(quiet, detail);
agent.note_abort("ctrl_c");
if let Some(name) = persist_name {
persist_session(agent, name);
}
continue;
};
queued_steer = steer;
let reply_text = send_result?;
println!();
notify::maybe_fire(
notify_settings,
false,
io::stderr().is_terminal(),
turn_start.elapsed(),
&model_label,
&reply_text,
);
if let Some(policy) = agent.reduction_policy().cloned() {
reduced_view = reduce::project_messages(
&agent.history()[1..],
&policy,
agent.reduction_log(),
)
.0;
}
if let Some(name) = persist_name {
persist_session(agent, name);
if !titled_once {
apply_auto_title(agent, name);
titled_once = true;
}
}
}
Err(ReadlineError::Interrupted) => {
consecutive_idle_interrupts += 1;
if consecutive_idle_interrupts >= 2 {
if !quiet {
eprintln!("bye.");
}
break;
}
if !quiet {
eprintln!("{}", ui::paint(ui::DIM, "(^C again to exit, or Ctrl-D)"));
}
continue;
}
Err(ReadlineError::Eof) => {
if !quiet {
eprintln!("bye.");
}
break;
}
Err(e) => return Err(e.into()),
}
}
let _ = rl
.as_mut()
.expect("editor available")
.save_history(&history_file);
Ok(())
}
async fn run_shell_escape(cmd: &str) {
match tokio::process::Command::new("sh")
.arg("-c")
.arg(cmd)
.status()
.await
{
Ok(status) if status.success() => {}
Ok(status) => {
eprintln!(
"{}",
ui::paint(
ui::WARN,
format!(
" (exit {})",
status
.code()
.map(|c| c.to_string())
.unwrap_or_else(|| "signal".to_string())
)
)
);
}
Err(e) => {
eprintln!(
"{}",
ui::paint(ui::WARN, format!(" failed to run `{cmd}`: {e}"))
);
}
}
}
fn handle_slash(
cmd: &str,
agent: &mut SdkAgent,
persist_name: Option<&str>,
model_label: &mut String,
pending: &mut Vec<String>,
expanded: &mut Vec<reduce::Reduction>,
reduced_view: &mut Vec<ChatMessage>,
) -> Result<bool> {
let mut parts = cmd.splitn(2, char::is_whitespace);
let name = parts.next().unwrap_or("");
let arg = parts.next().unwrap_or("").trim();
let Some(known) = resolve_slash_command(name) else {
match suggest_slash_command(name) {
Some(suggestion) => {
eprintln!("unknown command: {name} — did you mean {suggestion}?")
}
None => eprintln!("unknown command: {name} (try /help)"),
}
return Ok(false);
};
match known {
SlashCmd::Help => eprintln!("{}", slash::help_text()),
SlashCmd::Experimental => {
eprintln!(
"{}",
render_feature_flags(&live_feature_flags(agent.config()))
)
}
SlashCmd::Model => handle_model(model_label, agent, arg),
SlashCmd::Fast => handle_fast(agent, arg),
SlashCmd::Tokens => {
eprintln!(
"{} output tokens · {} turns",
agent.total_output_tokens(),
agent.turn_count()
)
}
SlashCmd::Add => {
if arg.is_empty() {
eprintln!("usage: /add <file>");
} else {
match std::fs::read_to_string(arg) {
Ok(text) => {
pending.push(format!("Contents of `{arg}`:\n```\n{text}\n```"));
eprintln!("staged {arg} for the next message");
}
Err(e) => eprintln!("can't read {arg}: {e}"),
}
}
}
SlashCmd::Save => {
let requested = if arg.is_empty() {
"transcript.jsonl"
} else {
arg
};
let path = unique_path(requested);
agent.save_transcript(&path)?;
if path == requested {
eprintln!("saved transcript to {path}");
} else {
eprintln!("saved transcript to {path} ({requested} already existed)");
}
}
SlashCmd::Clear => {
agent.rewind_to(1); if let Some(n) = persist_name {
persist_session(agent, n);
}
eprintln!("(conversation cleared)");
}
SlashCmd::Undo => {
let last_user = agent.history().iter().rposition(|m| m.role == Role::User);
if let Some(i) = last_user {
agent.rewind_to(i);
if let Some(n) = persist_name {
persist_session(agent, n);
}
eprintln!("(undid last exchange)");
} else {
eprintln!("nothing to undo");
}
}
SlashCmd::Rewind => handle_rewind(agent, persist_name, arg),
SlashCmd::Expand => handle_expand(agent, persist_name, arg, expanded, reduced_view),
SlashCmd::Reduce => handle_reduce(agent, persist_name, expanded, reduced_view),
SlashCmd::Compact => {
let focus = if arg.is_empty() { None } else { Some(arg) };
if agent.compact_now(focus) {
let usage = agent.context_usage();
if let Some(n) = persist_name {
persist_session(agent, n);
}
eprintln!("(compacted — {})", usage.summary_line());
} else {
eprintln!(
"nothing to compact (the conversation is already within the keep-window, \
or a reduction policy owns clearing this session)"
);
}
}
SlashCmd::Context => {
let usage = agent.context_usage();
eprintln!("{}", usage.summary_line());
if !usage.fits {
eprintln!(
" over the guard: the next request would be refused — /compact or /handoff"
);
}
}
SlashCmd::Handoff => {
if arg.is_empty() {
eprintln!("usage: /handoff <objective>");
} else {
let dropped = agent.new_context(arg, None);
if let Some(n) = persist_name {
persist_session(agent, n);
}
eprintln!(
"(handoff — {dropped} message(s) set aside; {})",
agent.context_usage().summary_line()
);
}
}
SlashCmd::Plan => handle_plan(agent, arg),
SlashCmd::Goal => {
if arg.is_empty() {
match agent.goal() {
Some(goal) => eprintln!("goal: {}", goal.objective),
None => eprintln!("no goal set (usage: /goal <objective>, /goal clear)"),
}
} else if arg == "clear" {
if agent.clear_goal() {
if let Some(n) = persist_name {
persist_session(agent, n);
}
eprintln!("(goal cleared)");
} else {
eprintln!("no goal set");
}
} else if agent.set_goal(arg) {
if let Some(n) = persist_name {
persist_session(agent, n);
}
eprintln!("(goal set — restated on every turn until cleared)");
} else {
eprintln!("goals are off for this session (`[capabilities.todos] goals = true`)");
}
}
SlashCmd::Effort => match arg {
"" => match agent.effort() {
Some(level) => eprintln!("effort: {level}"),
None => eprintln!("effort: off"),
},
"off" | "none" => {
agent.set_effort(None);
eprintln!("(extended thinking off)");
}
"low" | "medium" | "high" => {
agent.set_effort(Some(arg.to_string()));
eprintln!("(effort: {arg})");
}
other => eprintln!("unknown effort `{other}` (low | medium | high | off)"),
},
SlashCmd::Side | SlashCmd::Review => {
eprintln!("{name} is handled by the turn loop")
}
SlashCmd::Exit => return Ok(true),
}
Ok(false)
}
fn handle_plan(agent: &mut SdkAgent, arg: &str) {
let state = agent.plan_mode();
let want_on = match arg.trim().to_ascii_lowercase().as_str() {
"" => !state.is_active(),
"on" | "enter" => true,
"off" | "exit" => false,
other => {
eprintln!("usage: /plan [on|off] (got `{other}`)");
return;
}
};
if want_on {
state.enter(None);
eprintln!(
"plan mode ON — write and execution tools are refused; the model researches and \
proposes a plan. /plan off (or an approved exit_plan_mode) leaves it."
);
if !agent.config().permissions_enabled {
eprintln!(
" note: this session's permissions engine is off \
(`[capabilities.permissions] enabled = false`), so nothing enforces the \
read-only phase."
);
}
} else {
let plan = state.exit();
eprintln!("plan mode OFF — write and execution tools are available again.");
if !plan.trim().is_empty() {
eprintln!("plan so far:\n{plan}");
}
}
}
fn handle_model(model_label: &mut String, agent: &mut SdkAgent, arg: &str) {
if !arg.is_empty() {
let resolved = agent.config().model_routing.resolve_alias(arg.trim());
if let Some(refusal) = agent.config().model_routing.refusal(&resolved) {
eprintln!("model unchanged: {refusal}");
return;
}
apply_model_switch(model_label, agent, resolved);
return;
}
if !picker::available() {
eprintln!(
"model: {model_label} (not a terminal — pass --model to switch, \
or run interactively for the picker)"
);
return;
}
let candidates = model_picker_candidates();
let items: Vec<picker::PickerItem> = candidates.iter().map(|(_, item)| item.clone()).collect();
match picker::pick("switch model", &items) {
Ok(Some(idx)) => {
let resolved = agent
.config()
.model_routing
.resolve_alias(&candidates[idx].0);
apply_model_switch(model_label, agent, resolved);
}
Ok(None) => eprintln!("(cancelled — model unchanged: {model_label})"),
Err(e) => eprintln!("model picker failed ({e}) — model unchanged: {model_label}"),
}
}
fn handle_fast(agent: &mut SdkAgent, arg: &str) {
let configured = agent
.config()
.model_routing
.rules_for(agent.model())
.service_tier;
let current = agent
.config()
.service_tier
.clone()
.or_else(|| configured.clone());
let want_on = match arg.trim() {
"" => current.as_deref() != Some(PRIORITY_SERVICE_TIER),
"on" | "true" => true,
"off" | "false" => false,
other => {
eprintln!("usage: /fast [on|off] (unrecognized `{other}`)");
return;
}
};
let next = if want_on {
Some(PRIORITY_SERVICE_TIER.to_string())
} else {
configured
};
if next == current {
eprintln!(
"fast mode already {} (service tier: {})",
if want_on { "on" } else { "off" },
current.as_deref().unwrap_or("provider default")
);
return;
}
agent.set_service_tier(next.clone());
eprintln!(
"fast mode {} — service tier: {}",
if want_on { "ON" } else { "off" },
next.as_deref().unwrap_or("provider default")
);
eprintln!(
" note: changing the service tier changes the request's cache key, so the next \
request pays a full-price prompt-cache miss."
);
}
const PRIORITY_SERVICE_TIER: &str = "priority";
fn apply_model_switch(model_label: &mut String, agent: &mut SdkAgent, resolved: String) {
agent.switch_model(resolved.clone());
if agent.context_limit().is_some() {
let new_limit = model_context_limit(&resolved).unwrap_or(UNKNOWN_MODEL_CONTEXT_FLOOR);
agent.set_context_limit(new_limit);
}
*model_label = resolved;
eprintln!("model: {model_label}");
}
fn reduction_ordinal(id: &str) -> Option<u32> {
id.strip_prefix('r')?.get(..4)?.parse().ok()
}
fn working_view_tokens(agent: &SdkAgent, view: &[ChatMessage]) -> u64 {
let mut persisted = Vec::with_capacity(view.len() + 1);
persisted.push(agent.history()[0].clone());
persisted.extend_from_slice(view);
supercode_runtime::estimate_view_tokens(&persisted)
}
fn handle_rewind(agent: &mut SdkAgent, persist_name: Option<&str>, arg: &str) {
let points: Vec<usize> = agent
.history()
.iter()
.enumerate()
.filter(|(_, m)| m.role == Role::User)
.map(|(i, _)| i)
.collect();
if arg.is_empty() {
if points.is_empty() {
eprintln!("no rewind points yet (nothing has been said)");
return;
}
eprintln!("rewind points — `/rewind <n>` to go back, `/rewind undo` to reverse one:");
for (n, index) in points.iter().enumerate() {
let text = agent.history()[*index]
.content
.as_deref()
.unwrap_or("")
.lines()
.find(|l| !l.trim().is_empty())
.unwrap_or("")
.trim()
.chars()
.take(70)
.collect::<String>();
eprintln!(" {:>3} {text}", n + 1);
}
if agent.undoable_rewinds() > 0 {
eprintln!("({} rewind(s) can be undone)", agent.undoable_rewinds());
}
return;
}
if arg.eq_ignore_ascii_case("undo") {
if agent.undo_rewind() {
if let Some(n) = persist_name {
persist_session(agent, n);
}
eprintln!(
"(rewind undone — {} messages restored)",
agent.history().len()
);
} else {
eprintln!("no rewind to undo");
}
return;
}
let Ok(n) = arg.parse::<usize>() else {
eprintln!("usage: /rewind [<n>|undo] — `n` is a point number from `/rewind`");
return;
};
let Some(index) = n.checked_sub(1).and_then(|i| points.get(i).copied()) else {
eprintln!(
"no rewind point {n} (there {} {})",
if points.len() == 1 { "is" } else { "are" },
match points.len() {
0 => "none".to_string(),
1 => "1".to_string(),
k => format!("{k}"),
}
);
return;
};
let outcome = agent.rewind_conversation(index);
if outcome.removed == 0 {
eprintln!("(already at that point)");
return;
}
if let Some(n) = persist_name {
persist_session(agent, n);
}
let mut line = format!(
"(rewound to point {n} — {} message(s) set aside, `/rewind undo` restores them",
outcome.removed
);
if let Some(branch) = &outcome.preserved_branch {
line.push_str(&format!("; kept as branch `{branch}`"));
}
line.push(')');
eprintln!("{line}");
if let Some(observer) = supercode_harness::checkpoint::observer_for_config(agent.config()) {
match observer.list() {
Ok(list) => match list.get(n.saturating_sub(1)) {
Some(meta) => match observer.restore(&meta.id) {
Ok(report) => eprintln!(
"(working tree restored to that turn: {} file(s))",
report.restored.len()
),
Err(e) => eprintln!("(conversation rewound; file restore failed: {e})"),
},
None => eprintln!("(no file snapshot recorded for that turn)"),
},
Err(e) => eprintln!("(conversation rewound; checkpoints unreadable: {e})"),
}
}
}
fn handle_expand(
agent: &mut SdkAgent,
persist_name: Option<&str>,
arg: &str,
expanded: &mut Vec<reduce::Reduction>,
reduced_view: &mut Vec<ChatMessage>,
) {
let Some(name) = persist_name else {
eprintln!("/expand: no active session to persist against");
return;
};
if agent.reduction_policy().is_none() {
eprintln!("/expand: this session isn't in reduced mode");
return;
}
if arg.is_empty() {
eprintln!("usage: /expand <id|all|last>");
return;
}
let store = match session_store() {
Ok(s) => s,
Err(e) => {
eprintln!("/expand: {e}");
return;
}
};
let sidecar_jsonl = match store.load_sidecar(name) {
Ok(Some(s)) => s,
Ok(None) => {
eprintln!("/expand: no sidecar recorded for this session");
return;
}
Err(e) => {
eprintln!("/expand: {e}");
return;
}
};
let sidecar = match Session::from_sidecar_str(&sidecar_jsonl) {
Ok(s) => s,
Err(e) => {
eprintln!("/expand: corrupt sidecar: {e}");
return;
}
};
let log = agent.reduction_log().clone();
let before_bytes = serialized_bytes(reduced_view);
let before_tok = working_view_tokens(agent, reduced_view);
let ids: Vec<String> = if arg == "all" {
log.reductions.iter().map(|r| r.id.clone()).collect()
} else if arg == "last" {
match log
.reductions
.iter()
.max_by_key(|r| reduction_ordinal(&r.id))
{
Some(r) => vec![r.id.clone()],
None => {
eprintln!("/expand: no stubs to expand");
return;
}
}
} else {
vec![arg.to_string()]
};
if ids.is_empty() {
eprintln!("/expand: no stubs to expand");
return;
}
let mut cur_view = reduced_view.clone();
let mut cur_log = log.clone();
let mut newly_expanded: Vec<reduce::Reduction> = Vec::new();
for id in &ids {
let Some(record) = log.reductions.iter().find(|r| &r.id == id).cloned() else {
let valid: Vec<&str> = log.reductions.iter().map(|r| r.id.as_str()).collect();
eprintln!(
"/expand: no reduction with id `{id}` — valid ids: {}",
if valid.is_empty() {
"(none)".to_string()
} else {
valid.join(", ")
}
);
return; };
match reduce::invert_one(&cur_view, &cur_log, id, &sidecar) {
Ok((new_view, new_log)) => {
cur_view = new_view;
cur_log = new_log;
newly_expanded.push(record);
}
Err(e) => {
eprintln!("/expand: {e}");
return; }
}
}
agent.set_reduction_log(cur_log);
*reduced_view = cur_view.clone();
expanded.extend(newly_expanded);
persist_full_view(agent, name, &cur_view);
let after_bytes = serialized_bytes(&cur_view);
let after_tok = working_view_tokens(agent, &cur_view);
let delta_bytes = after_bytes.saturating_sub(before_bytes);
let delta_tok = after_tok.saturating_sub(before_tok);
let label = if ids.len() == 1 {
ids[0].clone()
} else {
format!("{} stub(s)", ids.len())
};
eprintln!(
"{} expanded {label} (+{} B, ~+{} tok → view now {})",
ui::paint(ui::ACCENT, "⤢"),
commas(delta_bytes),
commas(delta_tok),
supercode_runtime::fmt_approx_tokens(after_tok),
);
}
fn handle_reduce(
agent: &mut SdkAgent,
persist_name: Option<&str>,
expanded: &mut Vec<reduce::Reduction>,
reduced_view: &mut Vec<ChatMessage>,
) {
let Some(name) = persist_name else {
eprintln!("/reduce: no active session to persist against");
return;
};
let Some(policy) = agent.reduction_policy().cloned() else {
eprintln!("/reduce: this session isn't in reduced mode");
return;
};
let before_tok = working_view_tokens(agent, reduced_view);
let mut prior = agent.reduction_log().clone();
let mut restore = std::mem::take(expanded);
restore.append(&mut prior.expanded);
restore.sort_by_key(|r| {
(
matches!(r.kind, reduce::ReductionKind::TurnsCleared { .. }),
reduction_ordinal(&r.id),
)
});
restore.dedup_by(|a, b| a.id == b.id);
let restore_addrs: std::collections::HashSet<usize> = restore
.iter()
.filter(|r| !matches!(r.kind, reduce::ReductionKind::TurnsCleared { .. }))
.map(|r| r.ptr.addr.index)
.collect();
prior.reductions.retain(|r| {
matches!(r.kind, reduce::ReductionKind::TurnsCleared { .. })
|| !restore_addrs.contains(&r.ptr.addr.index)
});
prior.reductions.extend(restore);
let (final_view, final_log) = reduce::project_messages(&agent.history()[1..], &policy, &prior);
agent.set_reduction_log(final_log.clone());
*reduced_view = final_view.clone();
persist_full_view(agent, name, &final_view);
let after_tok = working_view_tokens(agent, &final_view);
eprintln!(
"{} re-reduced (~{} → {}, {} stubs)",
ui::paint(ui::ACCENT, "⤵"),
commas(before_tok),
supercode_runtime::fmt_approx_tokens(after_tok),
final_log.reductions.len(),
);
}
fn effective_model(cli: &Cli) -> String {
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let fc = uc::load(&cwd);
uc::resolve_model_alias(
cli.model
.clone()
.or(fc.model)
.as_deref()
.unwrap_or(uc::DEFAULT_MODEL),
)
}
async fn login(
api_key: Option<String>,
api_key_stdin: bool,
model: Option<String>,
base_url: Option<String>,
) -> Result<()> {
let key = match api_key {
Some(k) => k,
None if api_key_stdin => {
hidden_input::read_hidden_line().context("reading key from stdin")?
}
None => {
hidden_input::read_hidden_line_reporting(|status| {
let hint = match status {
hidden_input::HiddenReadStatus::Hidden => "input is hidden",
hidden_input::HiddenReadStatus::NotATty
| hidden_input::HiddenReadStatus::Unavailable => {
"input is NOT hidden here, take care"
}
};
eprint!("API key (OpenRouter by default; {hint}):\n> ");
io::stderr().flush().ok();
})
.context("reading key")?
}
};
if key.is_empty() {
anyhow::bail!("no key entered");
}
uc::save_api_key(&key).context("saving credentials")?;
let mut effective_base_url = base_url.clone();
if model.is_some() || base_url.is_some() {
let mut fc = uc::load(&std::env::current_dir().unwrap_or_default());
if let Some(m) = model {
fc.model = Some(uc::resolve_model_alias(&m));
}
if let Some(b) = base_url {
fc.base_url = Some(b);
}
uc::save_config(&fc).context("saving config")?;
}
if effective_base_url.is_none() {
effective_base_url = uc::load(&std::env::current_dir().unwrap_or_default()).base_url;
}
let effective_base_url = effective_base_url.unwrap_or_else(|| uc::DEFAULT_BASE_URL.into());
eprintln!();
eprintln!(
" {} {} {}",
ui::mark(true),
ui::paint(ui::MUTED, "saved key to"),
ui::paint(ui::DIM, uc::credentials_file().display().to_string())
);
eprintln!(
" {} {} {}",
ui::mark(true),
ui::paint(ui::MUTED, "config "),
ui::paint(ui::DIM, uc::config_file().display().to_string())
);
match check_provider_reachable(&effective_base_url, &key).await {
ReachStatus::Reachable(status) => eprintln!(
" {} {} {}",
ui::mark(true),
ui::paint(ui::MUTED, "provider "),
ui::paint(ui::OK, format!("reachable ({})", status))
),
ReachStatus::HttpError(status) => eprintln!(
" {} {} {}",
ui::mark(false),
ui::paint(ui::MUTED, "provider "),
ui::paint(
ui::ERR,
format!(
"HTTP {} — key saved, but the provider rejected it; check the key/base-url",
status
)
)
),
ReachStatus::Unreachable(msg) => eprintln!(
" {} {} {}",
ui::mark(false),
ui::paint(ui::MUTED, "provider "),
ui::paint(
ui::ERR,
format!(
"unreachable: {} — key saved; check your network/base-url",
msg
)
)
),
}
eprintln!(
" {} {}",
ui::paint(ui::ACCENT, "›"),
ui::paint(ui::TEAL, "supercode run \"hello\"")
);
eprintln!();
Ok(())
}
async fn doctor(cli: &Cli, json: bool, deep: bool) -> Result<()> {
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let fc = uc::load(&cwd);
let model = uc::resolve_model_alias(
cli.model
.clone()
.or(fc.model.clone())
.as_deref()
.unwrap_or(uc::DEFAULT_MODEL),
);
let base_url = cli
.base_url
.clone()
.or(fc.base_url.clone())
.unwrap_or_else(|| uc::DEFAULT_BASE_URL.into());
let cfg_exists = uc::config_file().exists();
let api_key_source = uc::api_key_source(cli.api_key.as_deref(), &base_url);
let resolved_key = uc::resolve_api_key(cli.api_key.as_deref(), &base_url);
let reach = match &resolved_key {
Some(key) => Some(check_provider_reachable(&base_url, key).await),
None => None,
};
let tier1 = tier_config(&api_key_source);
let tier2 = tier_catalog(&tier1, &reach);
let deep_outcome =
if deep && tier1.status == TierStatus::Pass && tier2.status == TierStatus::Pass {
match &resolved_key {
Some(key) => Some(run_deep_probe(&base_url, key, &model).await),
None => None,
}
} else {
None
};
let tier3 = tier_deep(deep, &tier1, &tier2, deep_outcome.as_ref());
let tiers = [&tier1, &tier2, &tier3];
let first_failing_tier = tiers
.iter()
.find(|t| t.status == TierStatus::Fail)
.map(|t| t.name);
if json {
let provider = reach.as_ref().map(|r| match r {
ReachStatus::Reachable(status) => serde_json::json!({
"reachable": true,
"http_status": status.as_u16(),
}),
ReachStatus::HttpError(status) => serde_json::json!({
"reachable": false,
"http_status": status.as_u16(),
}),
ReachStatus::Unreachable(msg) => serde_json::json!({
"reachable": false,
"error": msg,
}),
});
let out = serde_json::json!({
"version": env!("CARGO_PKG_VERSION"),
"config_home": uc::config_home().display().to_string(),
"config_file_found": cfg_exists,
"model": model,
"base_url": base_url,
"api_key_source": api_key_source,
"provider": provider,
"aliases": uc::alias_table()
.iter()
.map(|(alias, slug)| serde_json::json!({"alias": alias, "slug": slug}))
.collect::<Vec<_>>(),
"tiers": tiers.iter().map(|t| t.to_json()).collect::<Vec<_>>(),
"first_failing_tier": first_failing_tier,
"deep_probe_requested": deep,
});
println!("{}", serde_json::to_string_pretty(&out)?);
return Ok(());
}
println!();
println!("{}", ui::panel_top("system health"));
println!(
"{}",
ui::kv_info(
"version",
ui::paint(ui::DIM, format!("v{}", env!("CARGO_PKG_VERSION")))
)
);
println!(
"{}",
ui::kv_status(
true,
"config home",
ui::paint(ui::MUTED, uc::config_home().display().to_string())
)
);
println!(
"{}",
ui::kv_status(
true,
"config file",
ui::paint(
ui::MUTED,
if cfg_exists {
"found"
} else {
"none (using defaults)"
}
)
)
);
println!(
"{}",
ui::kv_status(true, "model", ui::paint(ui::TEAL, &model))
);
println!(
"{}",
ui::kv_status(true, "base url", ui::paint(ui::MUTED, &base_url))
);
match &api_key_source {
Some(src) => println!(
"{}",
ui::kv_status(
true,
"api key",
format!("from {}", ui::paint(ui::MUTED, src))
)
),
None => println!(
"{}",
ui::kv_status(
false,
"api key",
format!(
"{} {}",
ui::pill(ui::WARN, "MISSING"),
ui::paint(
ui::DIM,
"run `supercode login --provider` or set OPENROUTER_API_KEY"
)
)
)
),
}
match &reach {
Some(ReachStatus::Reachable(status)) => println!(
"{}",
ui::kv_status(
true,
"provider",
ui::paint(ui::OK, format!("reachable ({})", status))
)
),
Some(ReachStatus::HttpError(status)) => println!(
"{}",
ui::kv_status(
false,
"provider",
ui::paint(ui::ERR, format!("HTTP {}", status))
)
),
Some(ReachStatus::Unreachable(msg)) => println!(
"{}",
ui::kv_status(
false,
"provider",
ui::paint(ui::ERR, format!("unreachable: {}", msg))
)
),
None => {}
}
println!();
println!("{}", ui::section("diagnostic tiers"));
for t in tiers {
println!("{}", render_tier(t));
}
if let Some(name) = first_failing_tier {
let hint = tiers
.iter()
.find(|t| t.name == name)
.and_then(|t| t.hint.as_deref())
.unwrap_or("see the tier line above for details");
println!(
"{}",
ui::kv_blank(
"",
ui::paint(ui::ERR, format!("first failing tier: {name} — {hint}"))
)
);
}
println!();
let sample = ["opus", "sonnet", "gpt", "gemini", "flash", "llama"];
let table = uc::alias_table();
let aliases: Vec<String> = sample
.iter()
.filter_map(|a| {
table
.iter()
.find(|(name, _)| name == a)
.map(|(name, _)| ui::paint(ui::ACCENT, *name))
})
.collect();
println!(
"{}",
ui::kv_blank(
"aliases",
format!(
"{} {}",
aliases.join(ui::paint(ui::DIM, " · ").as_str()),
ui::paint(ui::DIM, "…")
)
)
);
println!("{}", ui::panel_bottom());
println!();
Ok(())
}
fn short_err(e: &reqwest::Error) -> String {
let s = e.to_string();
s.lines().next().unwrap_or("").chars().take(80).collect()
}
enum ReachStatus {
Reachable(reqwest::StatusCode),
HttpError(reqwest::StatusCode),
Unreachable(String),
}
async fn check_provider_reachable(base_url: &str, key: &str) -> ReachStatus {
let base = base_url.trim_end_matches('/');
let path = if base.contains("openrouter.ai") {
"key"
} else {
"models"
};
match reqwest::Client::new()
.get(format!("{base}/{path}"))
.bearer_auth(key)
.timeout(std::time::Duration::from_secs(10))
.send()
.await
{
Ok(r) if r.status().is_success() => ReachStatus::Reachable(r.status()),
Ok(r) => ReachStatus::HttpError(r.status()),
Err(e) => ReachStatus::Unreachable(short_err(&e)),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TierStatus {
Pass,
Fail,
Skip,
}
impl TierStatus {
fn as_str(self) -> &'static str {
match self {
TierStatus::Pass => "pass",
TierStatus::Fail => "fail",
TierStatus::Skip => "skip",
}
}
}
struct TierResult {
name: &'static str,
status: TierStatus,
detail: String,
hint: Option<String>,
}
impl TierResult {
fn to_json(&self) -> serde_json::Value {
serde_json::json!({
"name": self.name,
"status": self.status.as_str(),
"detail": self.detail,
"hint": self.hint,
})
}
}
fn render_tier(t: &TierResult) -> String {
let label = format!("tier · {}", t.name);
match t.status {
TierStatus::Pass => ui::kv_status(true, &label, ui::paint(ui::OK, &t.detail)),
TierStatus::Fail => ui::kv_status(false, &label, ui::paint(ui::ERR, &t.detail)),
TierStatus::Skip => ui::kv_info(&label, ui::paint(ui::DIM, &t.detail)),
}
}
fn tier_config(api_key_source: &Option<&str>) -> TierResult {
match api_key_source {
Some(src) => TierResult {
name: "config",
status: TierStatus::Pass,
detail: format!("key present (from {src})"),
hint: None,
},
None => TierResult {
name: "config",
status: TierStatus::Fail,
detail: "no api key configured".into(),
hint: Some("run `supercode login --provider` or set OPENROUTER_API_KEY".into()),
},
}
}
fn tier_catalog(tier1: &TierResult, reach: &Option<ReachStatus>) -> TierResult {
if tier1.status != TierStatus::Pass {
return TierResult {
name: "catalog",
status: TierStatus::Skip,
detail: "skipped — no api key to probe with".into(),
hint: Some("fix tier · config first".into()),
};
}
match reach {
Some(ReachStatus::Reachable(status)) => TierResult {
name: "catalog",
status: TierStatus::Pass,
detail: format!("reachable ({status})"),
hint: None,
},
Some(ReachStatus::HttpError(status)) => TierResult {
name: "catalog",
status: TierStatus::Fail,
detail: format!("HTTP {status}"),
hint: Some("provider rejected the key — check the key/base-url".into()),
},
Some(ReachStatus::Unreachable(msg)) => TierResult {
name: "catalog",
status: TierStatus::Fail,
detail: format!("unreachable: {msg}"),
hint: Some("check your network/base-url".into()),
},
None => TierResult {
name: "catalog",
status: TierStatus::Skip,
detail: "skipped — no reachability probe ran".into(),
hint: None,
},
}
}
enum DeepProbeOutcome {
Ok { tool_called: bool },
Err(String),
TimedOut,
}
const DEEP_PROBE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(20);
async fn run_deep_probe(base_url: &str, key: &str, model: &str) -> DeepProbeOutcome {
let provider = OpenAiProvider::new(base_url.to_string(), key.to_string(), HashMap::new());
let tool = ToolSchema::new(
"ping",
"Call this with no arguments to confirm the round-trip works.",
serde_json::json!({
"type": "object",
"properties": {},
"additionalProperties": false,
}),
);
let mut req = ChatRequest::new(
model.to_string(),
vec![ChatMessage::user(
"Call the `ping` tool now, with no arguments.",
)],
);
req.tools = vec![tool];
req.max_tokens = Some(64);
match tokio::time::timeout(DEEP_PROBE_TIMEOUT, provider.complete(&req, &|_delta| {})).await {
Ok(Ok((message, _usage))) => DeepProbeOutcome::Ok {
tool_called: message.tool_calls.as_ref().is_some_and(|tc| !tc.is_empty()),
},
Ok(Err(e)) => DeepProbeOutcome::Err(e.to_string().chars().take(120).collect()),
Err(_) => DeepProbeOutcome::TimedOut,
}
}
fn tier_deep(
requested: bool,
tier1: &TierResult,
tier2: &TierResult,
outcome: Option<&DeepProbeOutcome>,
) -> TierResult {
if !requested {
return TierResult {
name: "deep",
status: TierStatus::Skip,
detail: "not requested".into(),
hint: Some("pass --deep to run a chat/tool round-trip probe".into()),
};
}
if tier1.status != TierStatus::Pass || tier2.status != TierStatus::Pass {
return TierResult {
name: "deep",
status: TierStatus::Skip,
detail: "skipped — an earlier tier failed".into(),
hint: Some("fix tier · config / tier · catalog first".into()),
};
}
match outcome {
Some(DeepProbeOutcome::Ok { tool_called: true }) => TierResult {
name: "deep",
status: TierStatus::Pass,
detail: "chat + tool-call round-trip ok".into(),
hint: None,
},
Some(DeepProbeOutcome::Ok { tool_called: false }) => TierResult {
name: "deep",
status: TierStatus::Pass,
detail: "chat responded (no tool call observed)".into(),
hint: None,
},
Some(DeepProbeOutcome::Err(msg)) => TierResult {
name: "deep",
status: TierStatus::Fail,
detail: format!("probe failed: {msg}"),
hint: Some(
"model may not support tool calls, or the request was rejected — check the model/base-url"
.into(),
),
},
Some(DeepProbeOutcome::TimedOut) => TierResult {
name: "deep",
status: TierStatus::Fail,
detail: format!("timed out after {}s", DEEP_PROBE_TIMEOUT.as_secs()),
hint: Some("provider is slow/unresponsive to chat requests".into()),
},
None => TierResult {
name: "deep",
status: TierStatus::Skip,
detail: "skipped".into(),
hint: None,
},
}
}
fn audit(dir: PathBuf, format: Format, limit: Option<usize>, json: bool) -> Result<()> {
use supercode_harness::audit::{audit_dir, Corpus};
let corpus = match format {
Format::ClaudeCode => Corpus::ClaudeCode,
Format::Codex => Corpus::Codex,
Format::OpenCode => Corpus::OpenCode,
Format::Pi => Corpus::Pi,
Format::Grok => Corpus::Grok,
Format::Gemini => Corpus::Gemini,
Format::Goose => Corpus::Goose,
Format::Hermes => anyhow::bail!(
"hermes is a target of `convert --to hermes` (written through `hermes sessions import`); its sessions live in a store, not a transcript corpus to audit"
),
};
let report = audit_dir(&dir, corpus, limit);
if json {
println!("{}", serde_json::to_string_pretty(&report.to_json())?);
} else {
render_audit(&report.to_json());
}
Ok(())
}
fn render_audit(r: &serde_json::Value) {
let s = |v: &serde_json::Value, k: &str| {
v.get(k).and_then(|x| x.as_str()).unwrap_or("?").to_string()
};
let n = |v: &serde_json::Value, k: &str| v.get(k).and_then(|x| x.as_u64()).unwrap_or(0);
let cov_glyph = |c: &str| match c {
"normalized" => ui::paint(ui::OK, "✓"),
"retained" => ui::paint(ui::TEAL, "◆"),
"dropped" => ui::paint(ui::DIM, "◦"),
_ => ui::paint(ui::ERR, "✗"),
};
let corpus = s(r, "corpus");
let parse_errors = n(r, "parse_errors");
println!();
println!("{}", ui::panel_top(&format!("coverage audit · {corpus}")));
println!();
println!(
"{}",
ui::kv("files", ui::bold_plain(n(r, "files").to_string()))
);
println!(
"{}",
ui::kv("lines", ui::bold_plain(fmt_thousands(n(r, "lines"))))
);
let pe_val = if parse_errors == 0 {
ui::paint(ui::OK, "0")
} else {
ui::paint(ui::ERR, parse_errors.to_string())
};
println!("{}", ui::kv("parse errors", pe_val));
println!("{}", ui::panel_bottom());
println!();
if let Some(records) = r.get("records").and_then(|x| x.as_object()) {
let total = records.len();
println!("{}", ui::section(&format!("Records ({total} types)")));
let mut rows: Vec<(&String, &serde_json::Value)> = records.iter().collect();
rows.sort_by_key(|(_, v)| std::cmp::Reverse(n(v, "count")));
const RECORD_CAP: usize = 12;
let shown = rows.len().min(RECORD_CAP);
for (key, rec) in rows.iter().take(RECORD_CAP) {
let cov = s(rec, "coverage");
let count = n(rec, "count");
let fields = rec
.get("unmodeled_fields")
.and_then(|x| x.as_array())
.map(|a| a.len())
.unwrap_or(0);
let label = truncate(key, 38);
let dots = 40usize.saturating_sub(label.chars().count());
let count_str = fmt_thousands(count);
let pad = 9usize.saturating_sub(count_str.chars().count());
let extra = if fields > 0 {
ui::paint(ui::DIM, format!(" +{fields}"))
} else {
String::new()
};
println!(
" {} {} {} {}{}{}",
cov_glyph(&cov),
ui::paint(ui::MUTED, &label),
ui::paint(ui::DIM, ".".repeat(dots)),
" ".repeat(pad),
ui::bold_plain(count_str),
extra,
);
}
if total > shown {
println!(
" {} {}",
ui::paint(ui::DIM, " "),
ui::paint(
ui::DIM,
format!("… {} more — use --json for the full report", total - shown)
),
);
}
println!();
}
if let Some(blocks) = r.get("blocks").and_then(|x| x.as_object()) {
if !blocks.is_empty() {
let total = blocks.len();
println!(
"{}",
ui::section(&format!("Content blocks ({total} types)"))
);
let mut rows: Vec<(&String, &serde_json::Value)> = vec![];
for (tag, entries) in blocks {
if let Some(arr) = entries.as_array() {
for blk in arr {
rows.push((tag, blk));
}
}
}
rows.sort_by_key(|(_, v)| std::cmp::Reverse(n(v, "count")));
for (tag, blk) in &rows {
let cov = s(blk, "coverage");
let count = n(blk, "count");
let label = truncate(tag, 38);
let dots = 40usize.saturating_sub(label.chars().count());
let count_str = fmt_thousands(count);
let pad = 9usize.saturating_sub(count_str.chars().count());
println!(
" {} {} {} {}{}",
cov_glyph(&cov),
ui::paint(ui::MUTED, &label),
ui::paint(ui::DIM, ".".repeat(dots)),
" ".repeat(pad),
ui::bold_plain(count_str),
);
}
println!();
}
}
if let Some(tools) = r.get("tools").and_then(|x| x.as_object()) {
if !tools.is_empty() {
println!("{}", ui::section("Tools observed"));
let mut rows: Vec<(&String, u64)> = tools
.iter()
.map(|(k, v)| (k, v.as_u64().unwrap_or(0)))
.collect();
rows.sort_by_key(|&(_, c)| std::cmp::Reverse(c));
let max = rows.first().map(|(_, c)| *c).unwrap_or(1).max(1);
for (name, count) in rows.into_iter().take(8) {
let width = ((count as f64 / max as f64) * 28.0).round() as usize;
let bar = "█".repeat(width.max(1));
let label = format!("{:<16}", truncate(name, 16));
let count_str = format!("{:>8}", fmt_thousands(count));
println!(
" {} {} {}",
ui::paint(ui::MUTED, &label),
ui::paint(ui::DIM, count_str),
ui::paint(ui::ACCENT, bar),
);
}
println!();
}
}
let mut gaps: Vec<(String, u64)> = vec![];
if let Some(records) = r.get("records").and_then(|x| x.as_object()) {
for (key, rec) in records {
if s(rec, "coverage") == "unmodeled" {
gaps.push((key.clone(), n(rec, "count")));
}
}
}
if let Some(blocks) = r.get("blocks").and_then(|x| x.as_object()) {
for (tag, entries) in blocks {
if let Some(arr) = entries.as_array() {
for blk in arr {
if s(blk, "coverage") == "unmodeled" {
gaps.push((format!("content block `{tag}`"), n(blk, "count")));
}
}
}
}
}
if gaps.is_empty() {
println!(
" {} {}",
ui::paint(ui::OK, "✓"),
ui::paint(ui::MUTED, "every record type is modeled — no gaps")
);
} else {
println!("{}", ui::section("Gaps (not yet modeled)"));
gaps.sort_by_key(|g| std::cmp::Reverse(g.1));
for (key, count) in gaps {
println!(
" {} {} {}",
ui::paint(ui::ERR, "✗"),
ui::paint(ui::MUTED, format!("{key:<40}")),
ui::paint(ui::DIM, format!("{} occurrence(s)", fmt_thousands(count))),
);
}
}
println!();
}
fn truncate_path(s: &str, max: usize) -> String {
if s.chars().count() <= max {
return s.to_string();
}
let tail: String = s
.chars()
.rev()
.take(max.saturating_sub(1))
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect();
format!("…{tail}")
}
fn truncate(s: &str, max: usize) -> String {
if s.chars().count() <= max {
s.to_string()
} else {
let keep: String = s.chars().take(max.saturating_sub(1)).collect();
format!("{keep}…")
}
}
fn fmt_thousands(n: u64) -> String {
let s = n.to_string();
let bytes = s.as_bytes();
let mut out = String::new();
for (i, b) in bytes.iter().enumerate() {
if i > 0 && (bytes.len() - i) % 3 == 0 {
out.push(',');
}
out.push(*b as char);
}
out
}
struct ReducedExportStats {
stub_count: usize,
view_tok: u64,
full_tok: u64,
}
fn warn_on_zero_messages_from_nonempty_input(session: &Session, path: &Path) {
if !session.raw.is_empty() && session.messages.is_empty() {
eprintln!(
"{}",
ui::paint(
ui::WARN,
format!(
"warning: {} parsed to zero messages from a non-empty file — \
it may be corrupt or truncated; the output will be empty",
path.display()
)
)
);
}
}
fn warn_on_opencode_multi_session(path: &Path, opencode_session: &Option<String>, action: &str) {
if opencode_session.is_some() {
return;
}
let Ok(ids) = opencode_sqlite_session_ids(path) else {
return;
};
if ids.len() <= 1 {
return;
}
let chosen = opencode_sqlite_primary_id(path).ok();
eprintln!(
"warning: {} holds {} sessions; {action} {} (most-recently-updated) — \
pass --opencode-session <id> to pick another. All session ids: {}",
path.display(),
ids.len(),
chosen.as_deref().unwrap_or("<unknown>"),
ids.join(", ")
);
}
fn parse_loss_counts(session: &Session) -> (usize, usize) {
let subagent_errors: usize = session.subagents.iter().map(|s| s.parse_error_lines).sum();
let total_errors = session.parse_error_lines + subagent_errors;
let non_blank_lines = |raw: &[String]| raw.iter().filter(|l| !l.trim().is_empty()).count();
let total_lines = non_blank_lines(&session.raw)
+ session
.subagents
.iter()
.map(|s| non_blank_lines(&s.raw))
.sum::<usize>();
(total_errors, total_lines)
}
fn fail_loudly_on_parse_loss(
session: &Session,
path: &Path,
partial_output_written: bool,
) -> Result<()> {
let subagent_errors: usize = session.subagents.iter().map(|s| s.parse_error_lines).sum();
let (total_errors, total_lines) = parse_loss_counts(session);
if total_errors > 0 {
let subagent_note = if subagent_errors > 0 {
let subagents_display = path
.parent()
.zip(path.file_stem())
.map(|(dir, stem)| dir.join(stem).join("subagents").display().to_string())
.unwrap_or_else(|| format!("{}/subagents", path.display()));
format!(
" ({} of which came from {} subagent transcript(s) under {}/)",
subagent_errors,
session
.subagents
.iter()
.filter(|s| s.parse_error_lines > 0)
.count(),
subagents_display,
)
} else {
String::new()
};
let output_note = if partial_output_written {
format!(
"everything above reflects only the {} line(s) that DID parse, not the complete session",
total_lines.saturating_sub(total_errors)
)
} else if total_errors < total_lines {
"no provider call was made because the session was only partially loaded".to_string()
} else {
"no output was written because no input line parsed successfully".to_string()
};
eprintln!(
"{}",
ui::paint(
ui::ERR,
format!(
"error: {} — {} of {} line(s) failed to parse and were DROPPED{} \
(the file may be truncated or corrupted); {}",
path.display(),
total_errors,
total_lines,
subagent_note,
output_note,
)
)
);
if partial_output_written {
anyhow::bail!(
"{}: {total_errors} of {total_lines} line(s) failed to parse — refusing to report \
success on a partially-loaded session (PARITY-15){subagent_note}",
path.display(),
);
}
if total_errors < total_lines {
anyhow::bail!(
"{}: {total_errors} of {total_lines} line(s) failed to parse — refusing to \
continue a partially-loaded session; no provider call was made \
(PARITY-15){subagent_note}",
path.display(),
);
}
anyhow::bail!(
"{}: all {total_lines} line(s) failed to parse — no session data loaded and no \
output written (PARITY-15){subagent_note}",
path.display(),
);
}
Ok(())
}
fn fail_before_convert_if_all_lines_malformed(session: &Session, path: &Path) -> Result<()> {
let (total_errors, total_lines) = parse_loss_counts(session);
if total_errors > 0 && total_errors == total_lines {
return fail_loudly_on_parse_loss(session, path, false);
}
Ok(())
}
fn aliased_source_transcript(session: &Session, input: &Path, out: &Path) -> Option<PathBuf> {
let aliases =
|source: &Path| same_file::is_same_file(source, out).unwrap_or_else(|_| source == out);
if aliases(input) {
return Some(input.to_path_buf());
}
if session.meta.source != SessionSource::ClaudeCode {
return None;
}
let subagents_dir = input
.parent()
.zip(input.file_stem())
.map(|(dir, stem)| dir.join(stem).join("subagents"))?;
let entries = std::fs::read_dir(subagents_dir).ok()?;
entries
.flatten()
.map(|entry| entry.path())
.filter(|path| path.extension().and_then(|ext| ext.to_str()) == Some("jsonl"))
.find(|path| aliases(path))
}
fn fail_before_convert_if_parse_loss_would_overwrite_input(
session: &Session,
input: &Path,
out: Option<&Path>,
) -> Result<()> {
let (total_errors, total_lines) = parse_loss_counts(session);
let Some(out) = out else { return Ok(()) };
if total_errors == 0 {
return Ok(());
}
let Some(source) = aliased_source_transcript(session, input, out) else {
return Ok(());
};
let primary_input = source == input;
let source_description = if primary_input {
"the input file".to_string()
} else {
format!("source transcript `{}`", source.display())
};
eprintln!(
"{}",
ui::paint(
ui::ERR,
format!(
"error: {} — {} of {} line(s) failed to parse; `--out {}` identifies {}, so no \
output was written (use a distinct path or stdout to keep the recoverable \
partial conversion)",
input.display(),
total_errors,
total_lines,
out.display(),
source_description,
)
)
);
anyhow::bail!(
"{}: refusing to overwrite a parse-damaged source transcript with partial output \
(PARITY-15/{})",
input.display(),
if primary_input { "D41" } else { "D42" },
);
}
struct ClaudeSubagentExport {
parent_name: String,
session_id: Option<String>,
files: Vec<(String, PathBuf)>,
}
fn stored_claude_subagent_jsonl(child: &Session, session_id: Option<&str>) -> Result<String> {
if child.parse_error_lines > 0
|| child
.subagents
.iter()
.any(|subagent| subagent.parse_error_lines > 0)
{
anyhow::bail!(
"refusing stored Claude subagent export because the loaded session contains parse loss"
);
}
let has_appended_turns = child
.imported_message_count
.is_some_and(|imported| imported < child.messages.len());
if session_id.is_none() && !has_appended_turns {
Ok(child.raw_verbatim())
} else {
Ok(child.to_jsonl_spliced(SessionFormat::ClaudeCode, session_id)?)
}
}
fn prepare_claude_subagent_export(
store: &SessionStore,
parent_name: &str,
out: Option<&Path>,
source: SessionSource,
format: SessionFormat,
session_id: Option<&str>,
) -> Result<ClaudeSubagentExport> {
if source != SessionSource::ClaudeCode || format != SessionFormat::ClaudeCode {
return Ok(ClaudeSubagentExport {
parent_name: parent_name.to_string(),
session_id: session_id.map(str::to_string),
files: Vec::new(),
});
}
let mut children = Vec::new();
for child_id in store.list_subagent_ids(parent_name)? {
let native = store
.load_subagent_transcript(parent_name, &child_id)?
.ok_or_else(|| {
anyhow::anyhow!("subagent `{child_id}` disappeared while exporting `{parent_name}`")
})?;
let child = Session::from_sidecar_str(&native)
.with_context(|| format!("parsing stored subagent `{child_id}` for `{parent_name}`"))?;
if child.meta.source != SessionSource::ClaudeCode {
continue;
}
if !child.raw_is_verbatim {
anyhow::bail!(
"stored Claude subagent `{child_id}` for `{parent_name}` has no verbatim source bytes"
);
}
stored_claude_subagent_jsonl(&child, session_id)
.with_context(|| format!("exporting stored Claude subagent `{child_id}`"))?;
children.push(child_id);
}
if children.is_empty() {
return Ok(ClaudeSubagentExport {
parent_name: parent_name.to_string(),
session_id: session_id.map(str::to_string),
files: Vec::new(),
});
}
let output = out.ok_or_else(|| {
anyhow::anyhow!(
"Claude bundle export for `{parent_name}` has {} subagent transcript(s); use `--out <file>` so Volter Harness can recreate `<file-stem>/subagents/`",
children.len()
)
})?;
let stem = output.file_stem().ok_or_else(|| {
anyhow::anyhow!(
"cannot derive a Claude subagent directory from output path {}",
output.display()
)
})?;
let child_dir = output
.parent()
.unwrap_or_else(|| Path::new(""))
.join(stem)
.join("subagents");
let mut filenames = std::collections::BTreeSet::new();
let mut files = Vec::with_capacity(children.len());
for child_id in children {
let bare_id = child_id.strip_prefix("agent-").unwrap_or(&child_id);
if bare_id.is_empty() {
anyhow::bail!("stored Claude subagent id `{child_id}` has no filename-safe id body");
}
let filename = format!("agent-{bare_id}.jsonl");
if !filenames.insert(filename.clone()) {
anyhow::bail!(
"stored Claude subagent ids collide at output filename `{filename}` for `{parent_name}`"
);
}
files.push((child_id, child_dir.join(filename)));
}
Ok(ClaudeSubagentExport {
parent_name: parent_name.to_string(),
session_id: session_id.map(str::to_string),
files,
})
}
fn write_claude_subagent_export(store: &SessionStore, export: ClaudeSubagentExport) -> Result<()> {
for (child_id, path) in export.files {
let native = store
.load_subagent_transcript(&export.parent_name, &child_id)?
.ok_or_else(|| {
anyhow::anyhow!(
"subagent `{child_id}` disappeared while writing `{}`",
export.parent_name
)
})?;
let child = Session::from_sidecar_str(&native).with_context(|| {
format!(
"reloading stored subagent `{child_id}` for `{}`",
export.parent_name
)
})?;
let jsonl = stored_claude_subagent_jsonl(&child, export.session_id.as_deref())?;
let parent = path
.parent()
.ok_or_else(|| anyhow::anyhow!("subagent output has no parent: {}", path.display()))?;
std::fs::create_dir_all(parent)
.with_context(|| format!("creating Claude subagent directory {}", parent.display()))?;
std::fs::write(&path, jsonl)
.with_context(|| format!("writing Claude subagent transcript {}", path.display()))?;
}
Ok(())
}
fn convert(
input: &str,
to: Format,
out: Option<&Path>,
session_id: Option<String>,
opencode_session: Option<String>,
cwd: Option<&Path>,
) -> Result<()> {
let format: SessionFormat = to.into();
match resolve_input(input)? {
ResolvedInput::Path(path) => {
let mut session = if looks_like_sqlite(&path) && opencode_session.is_some() {
warn_on_opencode_multi_session(&path, &opencode_session, "converting");
Session::from_opencode_sqlite(&path, opencode_session.as_deref())
.with_context(|| format!("loading session {}", path.display()))?
} else {
if looks_like_sqlite(&path) {
warn_on_opencode_multi_session(&path, &opencode_session, "converting");
}
Session::load(&path)
.with_context(|| format!("loading session {}", path.display()))?
};
fail_before_convert_if_all_lines_malformed(&session, &path)?;
fail_before_convert_if_parse_loss_would_overwrite_input(&session, &path, out)?;
warn_on_zero_messages_from_nonempty_input(&session, &path);
if let Some(cwd) = cwd {
session.meta.cwd = Some(cwd.to_path_buf());
}
let had_session_id = session_id.is_some();
let had_cwd = cwd.is_some();
let verbatim = !had_session_id
&& !had_cwd
&& format.source() == session.meta.source
&& session.raw_is_verbatim;
if let Some(id) = session_id {
session.meta.session_id = Some(id);
}
if matches!(to, Format::Hermes) {
let imported =
supercode_harness::hermes_import::import_into_hermes(&session, None)?;
println!(
"imported into hermes as {} ({}): continue with `hermes --resume {}`",
imported.session_id,
imported.home.display(),
imported.session_id
);
return Ok(());
}
let jsonl = if verbatim {
session.raw_verbatim()
} else {
session.to_jsonl(format)?
};
let parity = if verbatim {
ConvertParity::Verbatim
} else if format.source() != session.meta.source {
ConvertParity::CrossFormat
} else if had_session_id {
ConvertParity::SessionIdRewritten
} else if had_cwd {
ConvertParity::SessionMetadataRewritten
} else {
ConvertParity::ReSerializedNonVerbatimRaw
};
let complete = session.parse_error_lines == 0
&& session
.subagents
.iter()
.all(|subagent| subagent.parse_error_lines == 0);
write_convert_output(
out,
&jsonl,
session.meta.source,
format,
session.messages.len(),
None,
ConvertStatus { parity, complete },
)?;
fail_loudly_on_parse_loss(&session, &path, true)
}
ResolvedInput::Store(name) => {
let store = session_store()?;
match store.load_sidecar(&name)? {
Some(sidecar_jsonl) => {
let sidecar = Session::from_sidecar_str(&sidecar_jsonl)
.with_context(|| format!("parsing sidecar for `{name}`"))?;
let log = load_reduction_log_checked(&store, &name)?;
reduce::verify_log(&log, &sidecar).map_err(|e| {
anyhow::anyhow!("{e} — repair pointer: `sessions show-reductions {name}`")
})?;
let (view_msgs, _) = reduce::project(&sidecar, &reapply_only_policy(), &log);
let full_tok = supercode_runtime::estimate_view_tokens(&sidecar.messages);
let view_tok = supercode_runtime::estimate_view_tokens(&view_msgs);
let jsonl = reduce::export_session_spliced_with_overrides(
&sidecar_jsonl,
format,
session_id.as_deref(),
cwd,
)
.context("exporting reduced session")?;
let parity = if format.source() != sidecar.meta.source {
ConvertParity::CrossFormat
} else if jsonl == sidecar.raw_verbatim() {
ConvertParity::Verbatim
} else if session_id.is_some()
&& sidecar
.to_jsonl_spliced(format, None)
.ok()
.is_some_and(|baseline| jsonl != baseline)
{
ConvertParity::SessionIdRewritten
} else {
ConvertParity::ReSerializedView
};
let child_export = prepare_claude_subagent_export(
&store,
&name,
out,
sidecar.meta.source,
format,
session_id.as_deref(),
)?;
write_convert_output(
out,
&jsonl,
sidecar.meta.source,
format,
sidecar.messages.len(),
Some(ReducedExportStats {
stub_count: log.reductions.len(),
view_tok,
full_tok,
}),
ConvertStatus {
parity,
complete: true,
},
)?;
write_claude_subagent_export(&store, child_export)
}
None => {
let saved = store.load(&name)?;
let mut session = parse_native_store_session(&name, &saved)?;
if let Some(cwd) = cwd {
session.meta.cwd = Some(cwd.to_path_buf());
}
if let Some(id) = session_id.clone() {
session.meta.session_id = Some(id);
}
let jsonl = session.to_jsonl(format)?;
write_convert_output(
out,
&jsonl,
session.meta.source,
format,
session.messages.len(),
None,
ConvertStatus {
parity: ConvertParity::ReSerializedView,
complete: true,
},
)
}
}
}
}
}
#[derive(Clone, Copy)]
enum ConvertParity {
Verbatim,
SessionIdRewritten,
SessionMetadataRewritten,
ReSerializedNonVerbatimRaw,
ReSerializedView,
CrossFormat,
}
#[derive(Clone, Copy)]
struct ConvertStatus {
parity: ConvertParity,
complete: bool,
}
fn write_convert_output(
out: Option<&Path>,
jsonl: &str,
source: SessionSource,
format: SessionFormat,
message_count: usize,
reduced: Option<ReducedExportStats>,
status: ConvertStatus,
) -> Result<()> {
match out {
Some(path) => {
std::fs::write(path, jsonl).with_context(|| format!("writing {}", path.display()))?;
let src = match source {
SessionSource::ClaudeCode => "Claude Code",
SessionSource::Codex => "Codex",
SessionSource::OpenCode => "OpenCode",
SessionSource::Pi => "Pi",
SessionSource::Grok => "Grok",
SessionSource::Gemini => "Gemini",
SessionSource::Goose => "Goose",
SessionSource::OpenClaw => "OpenClaw",
SessionSource::Hermes => "Hermes",
SessionSource::Native => "Volter Harness",
};
let dst = match format {
SessionFormat::ClaudeCode => "Claude Code",
SessionFormat::Codex => "Codex",
SessionFormat::OpenCode => "OpenCode",
SessionFormat::Pi => "Pi",
SessionFormat::Grok => "Grok",
SessionFormat::Gemini => "Gemini",
SessionFormat::Goose => "Goose",
};
let detail = match status.parity {
ConvertParity::Verbatim => format!(
"byte-identical (verbatim) · {message_count} messages · {}",
path.display()
),
ConvertParity::SessionIdRewritten => format!(
"{message_count} source messages · not byte-identical (session id rewritten) · {}",
path.display()
),
ConvertParity::SessionMetadataRewritten => format!(
"{message_count} source messages · not byte-identical (session metadata rewritten) · {}",
path.display()
),
ConvertParity::ReSerializedNonVerbatimRaw => format!(
"{message_count} source messages · re-serialized (source has no verbatim \
byte form to reproduce — an export document or a SQLite store) · {}",
path.display()
),
ConvertParity::ReSerializedView => format!(
"{message_count} source messages · reduced/store view, re-serialized · \
no byte-identity guarantee · {}",
path.display()
),
ConvertParity::CrossFormat => format!(
"{message_count} source messages · cross-format, not a parity guarantee · {}",
path.display()
),
};
let detail = if status.complete {
detail
} else {
format!("PARTIAL output from parseable records only · {detail}")
};
eprintln!(
"{} {} {} {} {}",
if status.complete {
ui::mark(true)
} else {
ui::pill(ui::WARN, "PARTIAL")
},
ui::paint(ui::MUTED, src),
ui::paint(ui::ACCENT, "→"),
ui::bold(ui::ACCENT, dst),
ui::paint(ui::DIM, detail),
);
if let Some(r) = reduced {
let hint = match format {
SessionFormat::ClaudeCode => "claude -r",
SessionFormat::Codex => "codex exec resume",
SessionFormat::OpenCode => "opencode",
SessionFormat::Pi => "pi --session",
SessionFormat::Grok => "grok",
SessionFormat::Gemini => "gemini --session-file",
SessionFormat::Goose => "goose session import",
};
eprintln!(
" {}",
ui::paint(
ui::DIM,
format!(
"fidelity: full — {} stubs rehydrated from sidecar (~{} → {}) · \
resume on your subscription: `{hint}`",
r.stub_count,
commas(r.view_tok),
supercode_runtime::fmt_approx_tokens(r.full_tok),
)
)
);
}
}
None => print!("{jsonl}"),
}
Ok(())
}
struct ReducedInspectInfo {
stub_count: usize,
view_tok: u64,
full_tok: u64,
pct: i64,
sidecar_path: PathBuf,
attribution: reduce::ReductionAttribution,
}
fn reduction_pass_enabled(kind: reduce::stub::Kind, policy: &ReductionPolicy) -> bool {
use reduce::stub::Kind;
match kind {
Kind::Duplicate => policy.deduplicate_outputs,
Kind::Superseded => policy.supersede_enabled,
Kind::OutputNormalized => policy.normalize_terminal_output,
Kind::ToolOutput => policy.tool_output_trigger_bytes != usize::MAX,
Kind::FileRead => policy.elide_stale_reads,
Kind::FileReadDiffed => policy.diff_rereads,
Kind::Image => policy.redact_images,
Kind::ToolInput => policy.elide_tool_inputs || policy.prune_errored_inputs,
Kind::TurnsCleared => policy.clear_turns_older_than.is_some(),
}
}
fn reduction_attribution(
log: &reduce::ReductionLog,
sidecar: &Session,
policy: Option<&ReductionPolicy>,
) -> reduce::ReductionAttribution {
use reduce::stub::Kind;
const PASS_ORDER: [Kind; 9] = [
Kind::Duplicate,
Kind::Superseded,
Kind::OutputNormalized,
Kind::ToolOutput,
Kind::FileRead,
Kind::FileReadDiffed,
Kind::Image,
Kind::ToolInput,
Kind::TurnsCleared,
];
let full_bytes = serialized_bytes(&sidecar.messages);
let full_tokens = supercode_runtime::estimate_view_tokens(&sidecar.messages);
let pre_final_log = policy
.filter(|_| {
log.reductions
.iter()
.any(|r| matches!(r.kind, reduce::ReductionKind::TurnsCleared { .. }))
})
.map(|policy| {
let mut before_clear = policy.clone();
before_clear.clear_turns_older_than = None;
reduce::project(sidecar, &before_clear, &ReductionLog::default()).1
})
.unwrap_or_else(|| log.clone());
let mut cumulative = ReductionLog {
read_log: log.read_log.clone(),
..Default::default()
};
let mut retained_bytes = full_bytes;
let mut retained_tokens = full_tokens;
let mut standalone_total_bytes = 0u64;
let mut standalone_total_tokens = 0u64;
let mut passes = Vec::with_capacity(PASS_ORDER.len());
for kind in PASS_ORDER {
let reductions = log
.reductions
.iter()
.filter(|reduction| Kind::from(&reduction.kind) == kind)
.cloned()
.collect::<Vec<_>>();
let applied_original_bytes = reductions
.iter()
.map(|reduction| record_bytes(reduction, sidecar) as u64)
.sum();
let candidates = if kind == Kind::TurnsCleared {
reductions.clone()
} else {
pre_final_log
.reductions
.iter()
.filter(|reduction| Kind::from(&reduction.kind) == kind)
.cloned()
.collect::<Vec<_>>()
};
let candidate_original_bytes = candidates
.iter()
.map(|reduction| record_bytes(reduction, sidecar) as u64)
.sum();
let suppressed_candidates = candidates
.iter()
.filter(|candidate| !reductions.iter().any(|applied| applied.id == candidate.id))
.collect::<Vec<_>>();
let suppressed_by_later_pass_bytes = suppressed_candidates
.iter()
.map(|reduction| record_bytes(reduction, sidecar) as u64)
.sum();
let standalone_log = ReductionLog {
reductions: candidates.clone(),
read_log: log.read_log.clone(),
..Default::default()
};
let standalone_view = reduce::project(sidecar, &reapply_only_policy(), &standalone_log).0;
let standalone_retained_bytes = serialized_bytes(&standalone_view);
let standalone_retained = supercode_runtime::estimate_view_tokens(&standalone_view);
let standalone_saved_bytes = full_bytes.saturating_sub(standalone_retained_bytes);
let standalone_saved_tokens = full_tokens.saturating_sub(standalone_retained);
standalone_total_bytes = standalone_total_bytes.saturating_add(standalone_saved_bytes);
standalone_total_tokens = standalone_total_tokens.saturating_add(standalone_saved_tokens);
cumulative.reductions.extend(reductions.iter().cloned());
let cumulative_view = reduce::project(sidecar, &reapply_only_policy(), &cumulative).0;
let next_retained_bytes = serialized_bytes(&cumulative_view);
let next_retained = supercode_runtime::estimate_view_tokens(&cumulative_view);
let marginal_saved_bytes = retained_bytes.saturating_sub(next_retained_bytes);
let marginal_saved_tokens = retained_tokens.saturating_sub(next_retained);
let suppressed_bytes = standalone_saved_bytes.saturating_sub(marginal_saved_bytes);
let suppressed_tokens = standalone_saved_tokens.saturating_sub(marginal_saved_tokens);
retained_bytes = next_retained_bytes;
retained_tokens = next_retained;
passes.push(reduce::ReductionPassAttribution {
kind: kind.as_str().to_string(),
enabled: policy
.map(|policy| reduction_pass_enabled(kind, policy))
.unwrap_or(!candidates.is_empty() || !reductions.is_empty()),
candidate_count: candidates.len(),
candidate_original_bytes,
applied_count: reductions.len(),
applied_original_bytes,
suppressed_by_later_pass_count: suppressed_candidates.len(),
suppressed_by_later_pass_bytes,
standalone_saved_bytes,
standalone_saved_tokens,
marginal_saved_bytes,
marginal_saved_tokens,
suppressed_bytes,
suppressed_tokens,
retained_bytes,
retained_tokens,
});
}
let view = reduce::project(sidecar, &reapply_only_policy(), log).0;
let view_bytes = serialized_bytes(&view);
let view_tokens = supercode_runtime::estimate_view_tokens(&view);
let aggregate_saved_bytes = full_bytes.saturating_sub(view_bytes);
let aggregate_saved_tokens = full_tokens.saturating_sub(view_tokens);
reduce::ReductionAttribution {
full_bytes,
view_bytes,
full_tokens,
view_tokens,
aggregate_saved_bytes,
aggregate_saved_tokens,
standalone_saved_bytes: standalone_total_bytes,
standalone_saved_tokens: standalone_total_tokens,
overlap_suppressed_bytes: standalone_total_bytes.saturating_sub(aggregate_saved_bytes),
overlap_suppressed_tokens: standalone_total_tokens.saturating_sub(aggregate_saved_tokens),
passes,
}
}
fn opencode_store_summary_json(path: &Path) -> Option<serde_json::Value> {
let stats = opencode_sqlite_store_stats(path).ok()?;
Some(serde_json::json!({
"path": path.display().to_string(),
"sessions": stats.sessions,
"messages": stats.messages,
"parts": stats.parts,
"todos": stats.todos,
}))
}
fn print_opencode_store_summary(store: &serde_json::Value) {
println!();
println!("{}", ui::panel_top("opencode store"));
println!();
println!(
"{}",
ui::kv(
"path",
ui::paint(ui::MUTED, store["path"].as_str().unwrap_or("?"))
)
);
println!(
"{}",
ui::kv("sessions", ui::bold_plain(store["sessions"].to_string()))
);
println!(
"{}",
ui::kv("messages", ui::bold_plain(store["messages"].to_string()))
);
println!(
"{}",
ui::kv("parts", ui::bold_plain(store["parts"].to_string()))
);
println!(
"{}",
ui::kv("todos", ui::bold_plain(store["todos"].to_string()))
);
println!("{}", ui::panel_bottom());
}
fn stored_message_records(session: &supercode_interchange::session::Session) -> Option<usize> {
use supercode_interchange::session::SessionSource;
if !matches!(
session.meta.source,
SessionSource::Pi | SessionSource::OpenClaw
) {
return None;
}
let stored = session
.raw
.iter()
.filter(|line| line.contains("\"type\":\"message\""))
.count();
(stored != session.messages.len()).then_some(stored)
}
fn session_summary_json(
session: &Session,
reduced: Option<&ReducedInspectInfo>,
) -> serde_json::Value {
let source = match session.meta.source {
SessionSource::ClaudeCode => "claude-code",
SessionSource::Codex => "codex",
SessionSource::OpenCode => "opencode",
SessionSource::Pi => "pi",
SessionSource::Grok => "grok",
SessionSource::Gemini => "gemini",
SessionSource::Goose => "goose",
SessionSource::OpenClaw => "openclaw",
SessionSource::Hermes => "hermes",
SessionSource::Native => "native",
};
let messages: Vec<serde_json::Value> = session
.messages
.iter()
.map(|m| {
serde_json::json!({
"role": format!("{:?}", m.role).to_lowercase(),
"content_preview": m.content.as_deref().map(|c| first_line(c, 120)),
"tool_calls": m.tool_calls().iter().map(|c| c.function.name.clone()).collect::<Vec<_>>(),
})
})
.collect();
let mut v = serde_json::json!({
"source": source,
"session_id": session.meta.session_id,
"model": session.meta.model,
"cwd": session.meta.cwd.as_deref().map(|p| p.display().to_string()),
"trigger": session.meta.trigger_or_default(),
"surface": session.meta.surface,
"profile": session.meta.profile,
"recurrence": session.meta.recurrence,
"cross_surface": session.meta.cross_surface,
"workspace": {
"kind": session.meta.workspace().0,
"value": session.meta.workspace().1,
},
"message_count": session.messages.len(),
"messages": messages,
});
if let Some(stored) = stored_message_records(session) {
v["stored_message_records"] = serde_json::json!(stored);
}
if let Some(r) = reduced {
v["reduced"] = serde_json::json!({
"stub_count": r.stub_count,
"view_tokens": r.view_tok,
"full_tokens": r.full_tok,
"pct": r.pct,
"sidecar_path": r.sidecar_path.display().to_string(),
"attribution": {
"full_bytes": r.attribution.full_bytes,
"view_bytes": r.attribution.view_bytes,
"full_tokens": r.attribution.full_tokens,
"view_tokens": r.attribution.view_tokens,
"aggregate_saved_bytes": r.attribution.aggregate_saved_bytes,
"aggregate_saved_tokens": r.attribution.aggregate_saved_tokens,
"standalone_saved_bytes": r.attribution.standalone_saved_bytes,
"standalone_saved_tokens": r.attribution.standalone_saved_tokens,
"overlap_suppressed_bytes": r.attribution.overlap_suppressed_bytes,
"overlap_suppressed_tokens": r.attribution.overlap_suppressed_tokens,
"aggregate_bytes_is_marginal_sum": r.attribution.passes.iter()
.map(|row| row.marginal_saved_bytes).sum::<u64>()
== r.attribution.aggregate_saved_bytes,
"aggregate_is_marginal_sum": r.attribution.passes.iter()
.map(|row| row.marginal_saved_tokens).sum::<u64>()
== r.attribution.aggregate_saved_tokens,
"passes": r.attribution.passes.iter().map(|row| serde_json::json!({
"kind": row.kind,
"enabled": row.enabled,
"candidate_count": row.candidate_count,
"candidate_original_bytes": row.candidate_original_bytes,
"applied_count": row.applied_count,
"applied_original_bytes": row.applied_original_bytes,
"suppressed_by_later_pass_count": row.suppressed_by_later_pass_count,
"suppressed_by_later_pass_bytes": row.suppressed_by_later_pass_bytes,
"standalone_saved_bytes": row.standalone_saved_bytes,
"standalone_saved_tokens": row.standalone_saved_tokens,
"marginal_saved_bytes": row.marginal_saved_bytes,
"marginal_saved_tokens": row.marginal_saved_tokens,
"suppressed_bytes": row.suppressed_bytes,
"suppressed_tokens": row.suppressed_tokens,
"retained_bytes": row.retained_bytes,
"retained_tokens": row.retained_tokens,
})).collect::<Vec<_>>(),
},
});
}
v
}
fn parse_native_store_session(name: &str, saved: &str) -> Result<Session> {
let messages: Vec<ChatMessage> = saved
.lines()
.filter(|line| !line.trim().is_empty())
.map(serde_json::from_str)
.collect::<std::result::Result<_, _>>()
.with_context(|| format!("parsing stored session `{name}`"))?;
let mut session = Session::from_native_messages(messages);
session.meta.session_id = Some(name.to_string());
Ok(session)
}
fn inspect(cli: &Cli, input: &str, json: bool) -> Result<()> {
match resolve_input(input)? {
ResolvedInput::Path(path) => {
let store = opencode_store_summary_json(&path);
let session = Session::load(&path)
.with_context(|| format!("loading session {}", path.display()))?;
if json {
let mut out = serde_json::json!({"session": session_summary_json(&session, None)});
if let Some(s) = store {
out["opencode_store"] = s;
}
println!("{}", serde_json::to_string_pretty(&out)?);
return fail_loudly_on_parse_loss(&session, &path, true);
}
if let Some(s) = &store {
print_opencode_store_summary(s);
}
render_inspect(&session, None)?;
fail_loudly_on_parse_loss(&session, &path, true)?;
}
ResolvedInput::Store(name) => {
let store = session_store()?;
match store.load_sidecar(&name)? {
Some(sidecar_jsonl) => {
let sidecar = Session::from_sidecar_str(&sidecar_jsonl)
.with_context(|| format!("parsing sidecar for `{name}`"))?;
let log = load_reduction_log_checked(&store, &name)?;
reduce::verify_log(&log, &sidecar).map_err(|e| {
anyhow::anyhow!("{e} — repair pointer: `sessions show-reductions {name}`")
})?;
let view_msgs: Vec<ChatMessage> = if let Some(saved) = store
.load_if_present(&name)
.with_context(|| format!("reading stored working view for `{name}`"))?
{
saved
.lines()
.filter(|line| !line.trim().is_empty())
.map(serde_json::from_str)
.collect::<std::result::Result<_, _>>()
.with_context(|| format!("parsing stored working view for `{name}`"))?
} else {
reduce::project(&sidecar, &reapply_only_policy(), &log).0
};
let full_tok = supercode_runtime::estimate_view_tokens(&sidecar.messages);
let view_tok = supercode_runtime::estimate_view_tokens(&view_msgs);
let pct = pct_of(view_tok, full_tok);
let mut display_session = sidecar.clone();
display_session.messages = view_msgs;
let reduced = ReducedInspectInfo {
stub_count: log.reductions.len(),
view_tok,
full_tok,
pct,
sidecar_path: store.sidecar_path(&name),
attribution: log
.attribution
.clone()
.unwrap_or_else(|| reduction_attribution(&log, &sidecar, None)),
};
if json {
let out = serde_json::json!({
"session": session_summary_json(&display_session, Some(&reduced))
});
println!("{}", serde_json::to_string_pretty(&out)?);
return Ok(());
}
render_inspect(&display_session, Some(reduced))?;
}
None => {
let saved = store.load(&name)?;
let session = parse_native_store_session(&name, &saved)?;
if json {
let out = serde_json::json!({
"session": session_summary_json(&session, None)
});
println!("{}", serde_json::to_string_pretty(&out)?);
return Ok(());
}
render_inspect(&session, None)?;
}
}
}
}
print_tool_schema_tier_stats(resolve_schema_tier(cli));
Ok(())
}
fn resolve_schema_tier(cli: &Cli) -> SchemaTier {
let cwd = cli
.cwd
.clone()
.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
let fc = uc::load(&cwd);
cli.schema_tier
.map(Into::into)
.or_else(|| fc.schema_tier.as_deref().and_then(parse_schema_tier))
.unwrap_or_default()
}
fn print_tool_schema_tier_stats(tier: SchemaTier) {
let registry = ToolRegistry::with_builtins();
let full_schemas: Vec<ToolSchema> = registry
.iter()
.map(|t| ToolSchema::new(t.name(), t.description(), t.parameters()))
.collect();
let tiered_schemas: Vec<ToolSchema> = full_schemas
.iter()
.map(|s| {
let (description, parameters) = minify_tool_schema(&s.description, &s.parameters, tier);
ToolSchema::new(s.name.clone(), description, parameters)
})
.collect();
let full_tok = supercode_runtime::estimate_tokens(
&serde_json::to_string(&full_schemas).unwrap_or_default(),
);
let tiered_tok = supercode_runtime::estimate_tokens(
&serde_json::to_string(&tiered_schemas).unwrap_or_default(),
);
let pct = pct_of(tiered_tok, full_tok);
println!();
println!("{}", ui::panel_top("tool schemas"));
println!();
println!("{}", ui::kv("tier", ui::paint(ui::TEAL, tier.as_str())));
println!(
"{}",
ui::kv(
"advertised",
format!(
"{} of {} ({}%)",
supercode_runtime::fmt_approx_tokens(tiered_tok),
supercode_runtime::fmt_approx_tokens(full_tok),
pct
)
)
);
println!("{}", ui::panel_bottom());
println!();
}
fn render_inspect(session: &Session, reduced: Option<ReducedInspectInfo>) -> Result<()> {
let source = match session.meta.source {
SessionSource::ClaudeCode => "Claude Code",
SessionSource::Codex => "Codex",
SessionSource::OpenCode => "OpenCode",
SessionSource::Pi => "Pi",
SessionSource::Grok => "Grok",
SessionSource::Gemini => "Gemini",
SessionSource::Goose => "Goose",
SessionSource::OpenClaw => "OpenClaw",
SessionSource::Hermes => "Hermes",
SessionSource::Native => "Volter Harness",
};
println!();
println!("{}", ui::panel_top("session"));
println!();
println!("{}", ui::kv("source", ui::bold(ui::ACCENT, source)));
println!(
"{}",
ui::kv(
"id",
ui::paint(ui::MUTED, session.meta.session_id.as_deref().unwrap_or("?"))
)
);
println!(
"{}",
ui::kv(
"model",
ui::paint(ui::TEAL, session.meta.model.as_deref().unwrap_or("?"))
)
);
let cwd = session
.meta
.cwd
.as_deref()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "?".into());
println!(
"{}",
ui::kv("cwd", ui::paint(ui::MUTED, truncate_path(&cwd, 62)))
);
if let Some(sp) = &session.meta.system_prompt {
println!(
"{}",
ui::kv(
"system",
ui::paint(ui::MUTED, format!("{} chars", sp.len()))
)
);
}
let message_line = match stored_message_records(session) {
Some(stored) => format!(
"{} on the active path (of {stored} stored message records)",
session.messages.len()
),
None => session.messages.len().to_string(),
};
println!("{}", ui::kv("messages", ui::bold_plain(message_line)));
if let Some(r) = &reduced {
println!(
"{}",
ui::kv(
"reduced",
format!(
"{} — {} stubs · view {} · full {} ({}%)",
ui::paint(ui::OK, "yes"),
r.stub_count,
supercode_runtime::fmt_approx_tokens(r.view_tok),
supercode_runtime::fmt_approx_tokens(r.full_tok),
r.pct,
)
)
);
for row in &r.attribution.passes {
println!(
"{}",
ui::kv(
&format!("· {}", row.kind),
ui::paint(
ui::MUTED,
format!(
"{} · {} candidates / {} applied / {} later-pass suppressed · {} retained B / {} tok · {} B / {} tok marginally saved · {} B / {} tok overlap suppressed",
if row.enabled { "enabled" } else { "disabled" },
row.candidate_count,
row.applied_count,
row.suppressed_by_later_pass_count,
commas(row.retained_bytes),
commas(row.retained_tokens),
commas(row.marginal_saved_bytes),
commas(row.marginal_saved_tokens),
commas(row.suppressed_bytes),
commas(row.suppressed_tokens),
)
)
)
);
}
println!(
"{}",
ui::kv(
"attribution",
ui::paint(
ui::MUTED,
format!(
"{} B / {} tok aggregate saved = marginal sums · {} B / {} tok standalone claims suppressed by overlap/order",
commas(r.attribution.aggregate_saved_bytes),
commas(r.attribution.aggregate_saved_tokens),
commas(r.attribution.overlap_suppressed_bytes),
commas(r.attribution.overlap_suppressed_tokens),
)
)
)
);
println!(
"{}",
ui::kv(
"sidecar",
ui::paint(ui::MUTED, r.sidecar_path.display().to_string())
)
);
}
println!("{}", ui::panel_bottom());
println!();
for (i, m) in session.messages.iter().enumerate() {
let role = format!("{:?}", m.role).to_lowercase();
let (role_color, label) = match role.as_str() {
"user" => (ui::SKY, "user"),
"assistant" => (ui::ACCENT, "asst"),
"tool" => (ui::WARN, "tool"),
"system" => (ui::DIM, "system"),
_ => (ui::MUTED, "?"),
};
let calls = m.tool_calls();
let tag = if !calls.is_empty() {
format!(
" {}",
ui::paint(ui::TEAL, format!("⚙ {}", calls[0].function.name))
)
} else {
String::new()
};
let reduced_tag = if reduced.is_some()
&& (m
.content
.as_deref()
.is_some_and(|c| c.contains(reduce::REDUCTION_SENTINEL))
|| calls
.iter()
.any(|c| c.function.arguments.contains(reduce::REDUCTION_SENTINEL)))
{
format!(" {}", ui::paint(ui::DIM, "⊟ reduced"))
} else {
String::new()
};
let preview = m
.content
.as_deref()
.map(|c| first_line(c, 64))
.unwrap_or_default();
println!(
" {} {} {}{}{}",
ui::paint(ui::DIM, format!("{i:>2}")),
ui::paint(role_color, format!("{label:<6}")),
preview,
tag,
reduced_tag,
);
}
println!();
Ok(())
}
fn read_prompt(prompt: Option<String>) -> Result<String> {
read_prompt_from(prompt, io::stdin())
}
fn read_prompt_from(prompt: Option<String>, mut reader: impl io::Read) -> Result<String> {
match prompt {
Some(p) if p != "-" => Ok(p),
_ => {
let mut buf = String::new();
reader
.read_to_string(&mut buf)
.context("reading prompt from stdin")?;
let trimmed = buf.trim();
if trimmed.is_empty() {
anyhow::bail!("no prompt given (pass an argument or pipe text on stdin)");
}
Ok(trimmed.to_string())
}
}
}
fn image_to_url(spec: &str) -> Result<String> {
if spec.starts_with("http://") || spec.starts_with("https://") || spec.starts_with("data:") {
return Ok(spec.to_string());
}
let bytes = std::fs::read(spec).with_context(|| format!("reading image {spec}"))?;
let mime = match Path::new(spec).extension().and_then(|e| e.to_str()) {
Some("png") => "image/png",
Some("jpg") | Some("jpeg") => "image/jpeg",
Some("gif") => "image/gif",
Some("webp") => "image/webp",
_ => "application/octet-stream",
};
let b64 = base64::engine::general_purpose::STANDARD.encode(&bytes);
Ok(format!("data:{mime};base64,{b64}"))
}
fn first_line(s: &str, max: usize) -> String {
let line = s.lines().next().unwrap_or("");
if line.chars().count() > max {
let truncated: String = line.chars().take(max).collect();
format!("{truncated}…")
} else {
line.to_string()
}
}