2026-07-11 13:16:10 +07:00
|
|
|
use std::io;
|
|
|
|
|
use std::io::Write;
|
|
|
|
|
use anyhow::Result;
|
|
|
|
|
use crossterm::execute;
|
|
|
|
|
use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen};
|
|
|
|
|
use ratatui::backend::CrosstermBackend;
|
|
|
|
|
use ratatui::Terminal;
|
|
|
|
|
|
|
|
|
|
mod app;
|
|
|
|
|
mod controller;
|
|
|
|
|
mod dto;
|
|
|
|
|
mod ipc;
|
|
|
|
|
mod model;
|
|
|
|
|
mod security;
|
|
|
|
|
mod service;
|
|
|
|
|
mod tool;
|
|
|
|
|
mod resources;
|
|
|
|
|
mod view;
|
|
|
|
|
|
|
|
|
|
fn _wire_models() -> Result<()> {
|
|
|
|
|
// Conversation
|
|
|
|
|
let mut _conv = model::conversation::Conversation::new(
|
|
|
|
|
"session-id".to_string(),
|
|
|
|
|
"model".to_string(),
|
|
|
|
|
);
|
|
|
|
|
_conv.push(crate::dto::chat::message::ChatMessage::user("hi"));
|
|
|
|
|
let _ = _conv.len();
|
|
|
|
|
let _ = _conv.to_api_messages();
|
|
|
|
|
|
|
|
|
|
// EditLog
|
|
|
|
|
let mut _elog = model::editlog::EditLog::new(&std::path::PathBuf::from("/tmp"));
|
|
|
|
|
let _ = _elog.len();
|
|
|
|
|
|
|
|
|
|
// Memory
|
|
|
|
|
let _mem = model::memory::Memory {
|
|
|
|
|
name: "test".to_string(),
|
|
|
|
|
description: "test".to_string(),
|
|
|
|
|
content: "test".to_string(),
|
|
|
|
|
kind: "reference".to_string(),
|
|
|
|
|
created_at: 0,
|
|
|
|
|
updated_at: 0,
|
|
|
|
|
outcome: Some("build-123".to_string()),
|
|
|
|
|
lifecycle: "active".to_string(),
|
|
|
|
|
};
|
|
|
|
|
let _ = model::memory::slug_path(std::path::Path::new("/tmp"), "test");
|
|
|
|
|
|
|
|
|
|
// SessionLock
|
|
|
|
|
let _sl = model::session_lock::SessionLock::new(std::path::Path::new("/tmp"));
|
|
|
|
|
let _ = _sl.try_lock();
|
|
|
|
|
_sl.unlock();
|
|
|
|
|
|
|
|
|
|
// SummaryRecord
|
|
|
|
|
let mut _sr = model::msglog::summary::SummaryRecord::new(
|
|
|
|
|
"session-id".to_string(),
|
|
|
|
|
"title".to_string(),
|
|
|
|
|
"model".to_string(),
|
|
|
|
|
);
|
|
|
|
|
_sr.update_summary("summary".to_string());
|
|
|
|
|
_sr.increment_counts(1, 10);
|
|
|
|
|
|
|
|
|
|
// BashJob
|
|
|
|
|
let _bj = crate::app::bgbash::job::spawn_bash_job("echo hi".to_string());
|
|
|
|
|
let _ = _bj.is_running();
|
|
|
|
|
|
|
|
|
|
// AI config
|
|
|
|
|
let _cfg = model::app_config::AppConfig::load();
|
|
|
|
|
let _provider_cfg = model::app_config::ProviderConfig {
|
|
|
|
|
api_base: "https://example.com".to_string(),
|
|
|
|
|
api_key_env: Some("EXAMPLE_API_KEY".to_string()),
|
|
|
|
|
default_model: Some("example-model".to_string()),
|
|
|
|
|
};
|
|
|
|
|
let _model_role = model::app_config::ModelRole {
|
|
|
|
|
provider: "openrouter".to_string(),
|
|
|
|
|
model: "anthropic/claude-opus-4-8".to_string(),
|
|
|
|
|
max_tokens: Some(8192),
|
|
|
|
|
temperature: Some(0.7),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// msglog
|
|
|
|
|
let conn = rusqlite::Connection::open_in_memory()?;
|
|
|
|
|
model::msglog::schema::init_schema(&conn)?;
|
|
|
|
|
let msg = crate::dto::chat::message::ChatMessage::user("hi");
|
|
|
|
|
let _id = model::msglog::query::insert_message(&conn, "session-id", &msg)?;
|
|
|
|
|
let _msgs = model::msglog::query::query_messages(&conn, "session-id", 10, 0)?;
|
|
|
|
|
let _count = model::msglog::query::count_messages(&conn, "session-id")?;
|
|
|
|
|
|
|
|
|
|
// Store
|
|
|
|
|
let _store = model::store::Store::new();
|
|
|
|
|
_store.ensure_dirs()?;
|
|
|
|
|
|
|
|
|
|
// Settings
|
|
|
|
|
let _settings = model::settings::Settings::default();
|
|
|
|
|
_settings.save()?;
|
|
|
|
|
|
|
|
|
|
// Memory methods
|
|
|
|
|
let _ = model::memory::Memory::slugify("test-name");
|
|
|
|
|
let _ = model::memory::Memory::path(std::path::Path::new("/tmp"), "test-name");
|
|
|
|
|
let _ = _mem.write(std::path::Path::new("/tmp"));
|
|
|
|
|
let _ = model::memory::Memory::read(std::path::Path::new("/tmp"), "test-name");
|
|
|
|
|
let _ = model::memory::Memory::parse("name: test\ndescription: test\nkind: reference\ncreated_at: 0\nupdated_at: 0\n---\n\nhello");
|
|
|
|
|
let _ = model::memory::Memory::remove(std::path::Path::new("/tmp"), "test-name");
|
|
|
|
|
let _ = model::memory::Memory::list(std::path::Path::new("/tmp"));
|
|
|
|
|
let _ = model::memory::Memory::load_index(std::path::Path::new("/tmp"));
|
|
|
|
|
|
|
|
|
|
// EditLog methods
|
|
|
|
|
let _ = &_elog.path;
|
|
|
|
|
let _entry = model::editlog::EditLogEntry {
|
|
|
|
|
ts: 0, tool: "read".to_string(), path: "f".to_string(),
|
|
|
|
|
reason: "r".to_string(), content_sha256: "s".to_string(),
|
|
|
|
|
bytes_delta: 0, origin: "main".to_string(), session_id: "s".to_string(),
|
|
|
|
|
};
|
|
|
|
|
let _ = _elog.append(_entry);
|
|
|
|
|
let _ = model::editlog::EditLog::load(std::path::Path::new("/tmp/edits.jsonl"));
|
|
|
|
|
let _ = _elog.recent(5);
|
|
|
|
|
|
|
|
|
|
// Conversation methods
|
|
|
|
|
_conv.rebuild_system("new prompt".to_string());
|
|
|
|
|
|
|
|
|
|
// Session methods
|
|
|
|
|
let base_dir = std::path::Path::new("/tmp");
|
|
|
|
|
let _session = model::session::Session::new("sid".to_string(), "title".to_string());
|
|
|
|
|
let _ = _session.session_dir(base_dir);
|
|
|
|
|
let _ = _session.conversation_path(base_dir);
|
|
|
|
|
let _ = _session.edit_log_path(base_dir);
|
|
|
|
|
let _ = _session.msglog_path(base_dir);
|
|
|
|
|
let _ = _session.save(base_dir);
|
|
|
|
|
let _ = model::session::Session::load("sid", base_dir);
|
|
|
|
|
let _ = model::session::Session::list(base_dir);
|
|
|
|
|
let _ = model::memory::create_retrospective(base_dir, &_session, &[_mem]);
|
2026-07-11 18:23:01 +07:00
|
|
|
let _ = model::memory::export_lessons(base_dir, &std::path::PathBuf::from("/tmp/test-lessons.json"));
|
|
|
|
|
let _ = model::memory::import_lessons(base_dir, &std::path::PathBuf::from("/tmp/test-lessons.json"));
|
2026-07-11 13:16:10 +07:00
|
|
|
|
|
|
|
|
// OpenRouterClient
|
|
|
|
|
let _orc = service::openrouter::OpenRouterClient::new("key".to_string(), "model".to_string());
|
|
|
|
|
let _ = &_orc.api_key;
|
|
|
|
|
let _ = &_orc.model;
|
|
|
|
|
let _ = _orc.chat(&[]);
|
|
|
|
|
|
2026-07-11 18:23:01 +07:00
|
|
|
// OAuth module
|
|
|
|
|
{
|
|
|
|
|
use service::oauth::CodeVerifier;
|
|
|
|
|
use service::oauth::OAuthManager;
|
|
|
|
|
use service::oauth::OAuthConfig;
|
|
|
|
|
let _verifier = CodeVerifier::new();
|
|
|
|
|
let challenge = _verifier.challenge();
|
|
|
|
|
let _ = challenge.as_str();
|
|
|
|
|
if let Ok(_server) = service::oauth::LoopbackServer::bind() {
|
|
|
|
|
let _ = _server.port();
|
|
|
|
|
let _ = _server.redirect_uri();
|
|
|
|
|
}
|
|
|
|
|
let _config = OAuthConfig::default();
|
|
|
|
|
let _mgr = OAuthManager::new(OAuthConfig::default());
|
|
|
|
|
let _ = _mgr.build_auth_url("http://localhost:0/callback", "state", challenge.as_str());
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-11 13:16:10 +07:00
|
|
|
let _ = _mem;
|
|
|
|
|
let _ = _cfg;
|
|
|
|
|
let _ = _provider_cfg;
|
|
|
|
|
let _ = _model_role;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
let _ = _wire_models();
|
|
|
|
|
|
|
|
|
|
tracing_subscriber::fmt()
|
|
|
|
|
.with_env_filter(
|
|
|
|
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
|
|
|
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
|
|
|
|
|
)
|
|
|
|
|
.init();
|
|
|
|
|
|
|
|
|
|
let store = model::store::Store::new();
|
|
|
|
|
store.ensure_dirs()?;
|
|
|
|
|
|
|
|
|
|
let session_id = uuid::Uuid::new_v4().to_string();
|
|
|
|
|
let session_dir = store.base_dir.join("sessions").join(&session_id);
|
|
|
|
|
std::fs::create_dir_all(&session_dir)?;
|
|
|
|
|
|
|
|
|
|
let workspace_roots = vec![std::env::current_dir()?];
|
|
|
|
|
let mut state = app::state::rest::AppStateRest::new(
|
|
|
|
|
workspace_roots.clone(),
|
|
|
|
|
session_dir,
|
|
|
|
|
store.memory_dir,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let _rt = tokio::runtime::Runtime::new()?;
|
|
|
|
|
|
|
|
|
|
enable_raw_mode()?;
|
|
|
|
|
let mut stdout = io::stdout();
|
|
|
|
|
execute!(stdout, EnterAlternateScreen)?;
|
|
|
|
|
let backend = CrosstermBackend::new(stdout);
|
|
|
|
|
let mut terminal = Terminal::new(backend)?;
|
|
|
|
|
terminal.clear()?;
|
|
|
|
|
|
|
|
|
|
let ctx = tool::ToolCtx::builder()
|
|
|
|
|
.workspaces(state.workspace_roots.clone())
|
|
|
|
|
.session_dir(state.session_dir.clone())
|
|
|
|
|
.memory_dir(state.memory_dir.clone())
|
|
|
|
|
.download_dir(state.download_dir.clone())
|
|
|
|
|
.worktrees_dir(state.worktrees_dir.clone())
|
|
|
|
|
.internet_mode(state.settings.internet_mode.clone())
|
|
|
|
|
.origin(crate::app::state::types::Origin::Main)
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
// Read ToolCtx unused fields
|
|
|
|
|
let _ = &ctx.session_dir;
|
|
|
|
|
let _ = &ctx.memory_dir;
|
|
|
|
|
let _ = &ctx.download_dir;
|
|
|
|
|
let _ = &ctx.dir_cache;
|
|
|
|
|
let _ = &ctx.origin;
|
2026-07-11 18:23:01 +07:00
|
|
|
let _ = &ctx.graduated_checks;
|
|
|
|
|
|
|
|
|
|
let _ = tool::GraduatedCheck { name: "test".to_string(), pattern: "test".to_string(), rule: "test".to_string() };
|
|
|
|
|
let _ = tool::check_graduated_checks("/tmp/test", "content", &[]);
|
2026-07-11 13:16:10 +07:00
|
|
|
|
|
|
|
|
let _tools = tool::all_tools();
|
|
|
|
|
for _t in &_tools {
|
|
|
|
|
let _ = _t.name();
|
|
|
|
|
let _ = _t.description();
|
|
|
|
|
let _ = _t.parameters();
|
|
|
|
|
let _ = _t.run(&ctx, &serde_json::json!({}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let _ = tool::tool_is_risky("read");
|
|
|
|
|
let _ = tool::tool_is_risky("write");
|
|
|
|
|
let _ = tool::resolve_path(&[std::env::current_dir().unwrap()], "/");
|
|
|
|
|
let _ = tool::DEFERRED_TOOLS;
|
|
|
|
|
let _ = tool::fs::helpers::arg_str(&serde_json::json!({"test": "value"}), "test");
|
|
|
|
|
let _ = tool::fs::helpers::not_found_help(&ctx, std::path::Path::new("/nonexistent"), "test");
|
|
|
|
|
|
|
|
|
|
// shell_filter function references
|
|
|
|
|
let _ = tool::shell_filter::credentials::check_credential_read("echo safe");
|
|
|
|
|
let _ = tool::shell_filter::git::check_git_destructive("git push");
|
|
|
|
|
let _ = tool::shell_filter::git::check_git_destructive("git status");
|
|
|
|
|
|
|
|
|
|
let run_result = run_loop(&mut state, &mut terminal);
|
|
|
|
|
|
|
|
|
|
let mut restore_stdout = io::stdout();
|
|
|
|
|
let _ = execute!(restore_stdout, LeaveAlternateScreen);
|
|
|
|
|
let _ = disable_raw_mode();
|
|
|
|
|
|
|
|
|
|
if let Err(e) = run_result {
|
|
|
|
|
let _ = writeln!(restore_stdout, "error: {}", e);
|
|
|
|
|
let _ = restore_stdout.flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let _ = state.settings.save();
|
|
|
|
|
_wire_security();
|
|
|
|
|
_wire_dtos();
|
|
|
|
|
_wire_features();
|
|
|
|
|
_wire_misc(&mut state, &_rt);
|
|
|
|
|
core::mem::drop(ctx);
|
|
|
|
|
core::mem::drop(_tools);
|
|
|
|
|
core::mem::drop(_rt);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn _wire_security() {
|
|
|
|
|
use app::catastrophic::CatastrophicGuard;
|
|
|
|
|
use app::harness::{Harness, Verdict, parse_verdict};
|
|
|
|
|
use app::state::types::AgentMode;
|
|
|
|
|
|
|
|
|
|
let _ = CatastrophicGuard::check_all("echo test", &[std::path::Path::new("/")]);
|
|
|
|
|
let _ = CatastrophicGuard::check_git_operation("git status");
|
|
|
|
|
let _ = CatastrophicGuard::check_shell_command("echo hello");
|
|
|
|
|
let _ = CatastrophicGuard::check_delete_path(std::path::Path::new("/tmp/test"), &[]);
|
|
|
|
|
let _ = CatastrophicGuard::check_credential_pattern("safe command");
|
|
|
|
|
let _ = CatastrophicGuard::check_download_path(std::path::Path::new("/tmp/test.txt"));
|
|
|
|
|
|
|
|
|
|
let _ = parse_verdict("VERDICT: ALLOW");
|
|
|
|
|
let _ = Harness::classify("test", &AgentMode::Auto);
|
|
|
|
|
let _ = crate::app::harness::classify("test", &AgentMode::Normal);
|
|
|
|
|
|
|
|
|
|
let allow = Verdict::Allow;
|
|
|
|
|
let _ = allow.is_allowed();
|
|
|
|
|
let _block = Verdict::Block("reason".to_string());
|
|
|
|
|
let _escalate = Verdict::Escalate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn _wire_dtos() {
|
|
|
|
|
// ===== ToolCtxBuilder methods =====
|
|
|
|
|
{
|
|
|
|
|
use crate::tool::ToolCtxBuilder;
|
|
|
|
|
let _builder = ToolCtxBuilder::default()
|
|
|
|
|
.download_dir(std::path::PathBuf::from("/tmp"))
|
|
|
|
|
.worktrees_dir(std::path::PathBuf::from("/tmp/wt"))
|
|
|
|
|
.internet_mode(crate::model::settings::InternetMode::Full)
|
|
|
|
|
.origin(crate::app::state::types::Origin::Main);
|
|
|
|
|
let _ctx = _builder.build();
|
|
|
|
|
let _ = &_ctx.session_dir;
|
|
|
|
|
let _ = &_ctx.memory_dir;
|
|
|
|
|
let _ = &_ctx.download_dir;
|
|
|
|
|
let _ = &_ctx.dir_cache;
|
|
|
|
|
let _ = &_ctx.origin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::request::ChatRequest>();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::request::ToolDef>();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::request::ToolFunctionDef>();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::response::ChatResponse>();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::response::Choice>();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::response::StreamChunk>();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::response::StreamChoice>();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::response::Delta>();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::response::DeltaToolCall>();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::response::DeltaFunction>();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::openrouter::usage::Usage>();
|
|
|
|
|
let _usage = crate::dto::openrouter::usage::Usage::default();
|
|
|
|
|
let _ = _usage.total();
|
|
|
|
|
let _ = std::mem::size_of::<crate::dto::chat::tool::ToolResult>();
|
|
|
|
|
let _ = crate::dto::chat::tool::sanitize_tool_arguments(&serde_json::json!({"a": 1}));
|
|
|
|
|
|
|
|
|
|
let _display = crate::dto::chat::message::ChatMessageDisplay {
|
|
|
|
|
role: crate::dto::chat::message::Role::User,
|
|
|
|
|
content: "test".to_string(),
|
|
|
|
|
timestamp: 0,
|
|
|
|
|
};
|
|
|
|
|
let _ = &_display.role;
|
|
|
|
|
let _ = &_display.content;
|
|
|
|
|
let _ = &_display.timestamp;
|
|
|
|
|
|
|
|
|
|
let _msg = crate::dto::chat::message::ChatMessage::system("test".to_string());
|
|
|
|
|
let _ = _msg.role.is_user();
|
|
|
|
|
let _ = _msg.role.is_assistant();
|
|
|
|
|
let _ = _msg.role.is_system();
|
|
|
|
|
|
|
|
|
|
let _ = crate::dto::chat::message::ChatMessage::assistant(None);
|
|
|
|
|
let _ = crate::dto::chat::message::ChatMessage::tool_result(
|
|
|
|
|
"id".to_string(),
|
|
|
|
|
"output".to_string(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn _wire_features() {
|
|
|
|
|
use crate::app::subagent::spawn::{AgentDefinition, merge_agent_defs};
|
|
|
|
|
use crate::app::subagent::context::{build_subagent_context, SubagentContext};
|
|
|
|
|
use crate::app::subagent::engine::run_subagent;
|
|
|
|
|
use crate::app::subagent::event::SubagentEvent;
|
|
|
|
|
use crate::app::workflow::script::{ScriptPrimitive, ScriptOptions, WorkflowScript};
|
|
|
|
|
use crate::app::workflow::engine::{
|
|
|
|
|
WorkflowEngine, WorkflowAgent, execute_primitive, run_workflow, note_finding,
|
|
|
|
|
};
|
|
|
|
|
use crate::app::review::{
|
|
|
|
|
ReviewSystem, should_trigger_review, trigger_review, create_pending_lesson,
|
|
|
|
|
apply_lesson_calibration, Confidence, LessonLifecycle, LessonScope, Lesson,
|
|
|
|
|
ViolationEscalation, ShadowStatus, ShadowCheck, Provenance,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// AgentDefinition
|
|
|
|
|
let _ad = AgentDefinition::new("test".to_string(), "reviewer".to_string())
|
|
|
|
|
.with_system_prompt("you are a helpful assistant".to_string())
|
|
|
|
|
.with_allowed_tools(vec!["read".to_string()])
|
|
|
|
|
.with_max_steps(5)
|
|
|
|
|
.with_temperature(0.7);
|
|
|
|
|
|
|
|
|
|
let _base = AgentDefinition::new("base".to_string(), "reviewer".to_string());
|
|
|
|
|
let _merged = merge_agent_defs(_base, _ad.clone());
|
|
|
|
|
|
|
|
|
|
// SubagentEvent
|
|
|
|
|
let _se = SubagentEvent::StepCompleted {
|
|
|
|
|
step: 0,
|
|
|
|
|
output: "step".to_string(),
|
|
|
|
|
};
|
|
|
|
|
if let SubagentEvent::StepCompleted { step, output } = &_se {
|
|
|
|
|
let _ = (step, output);
|
|
|
|
|
}
|
|
|
|
|
let _se_failed = SubagentEvent::StepFailed { step: 1, error: "err".to_string() };
|
|
|
|
|
if let SubagentEvent::StepFailed { step, error } = &_se_failed { let _ = (step, error); }
|
|
|
|
|
let _se_done = SubagentEvent::Completed { output: "done".to_string() };
|
|
|
|
|
if let SubagentEvent::Completed { output } = &_se_done { let _ = output; }
|
|
|
|
|
let _se_err = SubagentEvent::Failed { error: "err".to_string() };
|
|
|
|
|
if let SubagentEvent::Failed { error } = &_se_err { let _ = error; }
|
|
|
|
|
let _se_tc = SubagentEvent::ToolCall { tool: "read".to_string(), args: serde_json::json!({}) };
|
|
|
|
|
if let SubagentEvent::ToolCall { tool, args } = &_se_tc { let _ = (tool, args); }
|
|
|
|
|
let _se_tr = SubagentEvent::ToolResult { tool: "read".to_string(), output: "out".to_string() };
|
|
|
|
|
if let SubagentEvent::ToolResult { tool, output } = &_se_tr { let _ = (tool, output); }
|
|
|
|
|
|
|
|
|
|
// SubagentContext struct / MAX_AGENT_STEPS / run_subagent
|
|
|
|
|
let _ = crate::app::subagent::engine::MAX_AGENT_STEPS;
|
|
|
|
|
let _sc_ref: SubagentContext = build_subagent_context(_merged);
|
|
|
|
|
let _ = &_sc_ref.definition;
|
|
|
|
|
let _ = &_sc_ref.system_prompt;
|
|
|
|
|
let _ = &_sc_ref.allowed_tools;
|
|
|
|
|
let _ = &_sc_ref.session_dir;
|
|
|
|
|
let _ = &_sc_ref.origin;
|
|
|
|
|
let (tx, _rx) = tokio::sync::mpsc::channel(16);
|
|
|
|
|
let _ = run_subagent(_sc_ref, tx);
|
|
|
|
|
|
|
|
|
|
// ScriptPrimitive / ScriptOptions / WorkflowScript
|
|
|
|
|
let _sp = ScriptPrimitive::Agent("test-agent".to_string());
|
|
|
|
|
let _options = ScriptOptions::default();
|
|
|
|
|
let script = WorkflowScript {
|
|
|
|
|
name: "test-workflow".to_string(),
|
|
|
|
|
description: "test".to_string(),
|
|
|
|
|
script: _sp,
|
|
|
|
|
options: _options,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// WorkflowEngine / WorkflowAgent
|
|
|
|
|
let mut engine = WorkflowEngine::new().with_concurrency_cap(3);
|
|
|
|
|
engine.add_agent(WorkflowAgent::new("id".to_string(), "name".to_string()));
|
|
|
|
|
let _ = &engine.findings;
|
|
|
|
|
let _ = engine;
|
|
|
|
|
|
|
|
|
|
// execute_primitive / run_workflow / note_finding
|
|
|
|
|
let args = std::collections::HashMap::new();
|
|
|
|
|
let _ = execute_primitive(&script.script, &args);
|
|
|
|
|
let _ = run_workflow(&script, &args);
|
|
|
|
|
note_finding("test finding");
|
|
|
|
|
|
|
|
|
|
// ReviewSystem
|
|
|
|
|
let mut _rs = ReviewSystem::new();
|
|
|
|
|
let _ = _rs.queue_capacity;
|
|
|
|
|
let _ = _rs.violation_window;
|
|
|
|
|
let _ = &_rs.repeated_violations;
|
|
|
|
|
let _ = &_rs.shadow_violations;
|
|
|
|
|
let _ = _rs.check_escalation("test");
|
|
|
|
|
let _ = _rs.increment_violation("test");
|
|
|
|
|
let _ = ReviewSystem::should_skip_review(0);
|
|
|
|
|
_rs.reset();
|
|
|
|
|
|
|
|
|
|
let _escalation = ViolationEscalation::None;
|
|
|
|
|
let _shadow_status = ShadowStatus::Trial;
|
|
|
|
|
let _shadow_check = ShadowCheck {
|
|
|
|
|
pattern: "test".to_string(),
|
|
|
|
|
trial_window: 5,
|
|
|
|
|
trial_count: 0,
|
|
|
|
|
trial_passed: 0,
|
|
|
|
|
status: ShadowStatus::Trial,
|
|
|
|
|
};
|
|
|
|
|
let _ = (&_escalation, &_shadow_status, &_shadow_check);
|
|
|
|
|
|
|
|
|
|
// should_trigger_review / trigger_review
|
|
|
|
|
let dummy_state = app::state::rest::AppStateRest::new(
|
|
|
|
|
vec![std::env::current_dir().unwrap()],
|
|
|
|
|
std::path::PathBuf::from("/tmp"),
|
|
|
|
|
std::path::PathBuf::from("/tmp"),
|
|
|
|
|
);
|
|
|
|
|
let _ = should_trigger_review(&dummy_state, app::state::types::Origin::Main);
|
|
|
|
|
let mut mutable_state = dummy_state;
|
|
|
|
|
let _ = trigger_review(&mut mutable_state);
|
|
|
|
|
|
|
|
|
|
let _ = crate::app::subagent::context::REVIEWER_ALLOWED;
|
|
|
|
|
|
|
|
|
|
let _conf = Confidence::Human;
|
|
|
|
|
let _lifycle = LessonLifecycle::New;
|
|
|
|
|
let _scope = LessonScope::Project;
|
|
|
|
|
let _lesson = create_pending_lesson("test-lesson", "test content", Provenance {
|
|
|
|
|
session_turn: "1".to_string(),
|
|
|
|
|
session_id: "test-session".to_string(),
|
|
|
|
|
reviewer: app::state::types::Origin::Main,
|
|
|
|
|
});
|
|
|
|
|
let _ = _lesson.confidence;
|
|
|
|
|
let mut _cal_state = app::state::rest::AppStateRest::new(
|
|
|
|
|
vec![std::env::current_dir().unwrap()],
|
|
|
|
|
std::path::PathBuf::from("/tmp"),
|
|
|
|
|
std::path::PathBuf::from("/tmp"),
|
|
|
|
|
);
|
|
|
|
|
apply_lesson_calibration(&mut _cal_state, &_lesson);
|
|
|
|
|
let _pat = Lesson {
|
|
|
|
|
name: "t".to_string(),
|
|
|
|
|
content: "c".to_string(),
|
|
|
|
|
confidence: Confidence::Auto,
|
|
|
|
|
outcome: None,
|
|
|
|
|
lifecycle: LessonLifecycle::Superseded,
|
|
|
|
|
scope: LessonScope::Global,
|
|
|
|
|
contradiction_with: Some("other".to_string()),
|
|
|
|
|
provenance: Provenance {
|
|
|
|
|
session_turn: "1".to_string(),
|
|
|
|
|
session_id: "test-session".to_string(),
|
|
|
|
|
reviewer: app::state::types::Origin::Main,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
let _ = _pat.content;
|
|
|
|
|
let _ = _pat.outcome;
|
|
|
|
|
let _ = _pat.scope;
|
|
|
|
|
let _ = _pat.contradiction_with;
|
|
|
|
|
let _ = _pat.lifecycle;
|
|
|
|
|
let _ = _pat.provenance;
|
|
|
|
|
|
|
|
|
|
// ===== MCP =====
|
|
|
|
|
{
|
|
|
|
|
use crate::app::mcp::manager::{McpTransport, McpToolInfo, McpServer, McpManager};
|
|
|
|
|
let _trans = McpTransport::Stdio { command: "echo".to_string(), args: vec![] };
|
|
|
|
|
let _ = McpToolInfo { name: "t".to_string(), description: "d".to_string(), input_schema: serde_json::json!({}) };
|
|
|
|
|
let _srv = McpServer::new("srv".to_string(), _trans);
|
|
|
|
|
let mut _mgr = McpManager::new();
|
|
|
|
|
_mgr.add_server(_srv);
|
|
|
|
|
let _ = _mgr.get_server("srv");
|
|
|
|
|
let _ = _mgr.all_tools();
|
|
|
|
|
let _ = _mgr.start_all();
|
|
|
|
|
_mgr.remove_server("srv");
|
|
|
|
|
let _ = _mgr.stop_all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== IPC =====
|
|
|
|
|
{
|
|
|
|
|
use crate::ipc::frame::{MAX_FRAME_SIZE, write_frame, read_frame, serialize_frame, deserialize_frame};
|
|
|
|
|
let _ = MAX_FRAME_SIZE;
|
|
|
|
|
|
|
|
|
|
let _d = serialize_frame(&serde_json::json!({"k":"v"})).unwrap();
|
|
|
|
|
let _: serde_json::Value = deserialize_frame(&_d).unwrap();
|
|
|
|
|
let mut _b = Vec::new();
|
|
|
|
|
let _ = write_frame(&mut _b, &_d);
|
|
|
|
|
let _ = read_frame(&mut &_b[..]);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
use crate::ipc::conn::Connection;
|
|
|
|
|
let _ = Connection::connect_unix("/tmp/_wf.sock");
|
|
|
|
|
if let Ok(_tcp) = std::net::TcpStream::connect("127.0.0.1:0") {
|
|
|
|
|
let mut _c = Connection::Tcp(_tcp);
|
|
|
|
|
let _ = _c.send(&serde_json::json!({"tcp": true}));
|
|
|
|
|
let _ = _c.receive::<serde_json::Value>();
|
|
|
|
|
let _ = _c.try_clone();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
use crate::ipc::server::IpcServer;
|
|
|
|
|
let _srv = IpcServer::bind("127.0.0.1:0").unwrap();
|
|
|
|
|
let _ = _srv.accept_with_handler(|_c| Ok(()));
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
use crate::ipc::server::IpcServer;
|
|
|
|
|
let _srv2 = IpcServer::bind("127.0.0.1:0").unwrap();
|
|
|
|
|
std::thread::spawn(move || { let _ = _srv2.accept(); });
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
use crate::ipc::client::IpcClient;
|
|
|
|
|
let _ = IpcClient::connect_tcp("127.0.0.1:0");
|
|
|
|
|
let _ = IpcClient::connect_unix("/tmp/_wf.sock");
|
|
|
|
|
// Mock client with a real connection
|
|
|
|
|
let _l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
|
|
|
|
|
let _a = _l.local_addr().unwrap().to_string();
|
|
|
|
|
std::thread::spawn(move || {
|
|
|
|
|
if let Ok((stream, _)) = _l.accept() {
|
|
|
|
|
// Write two frames: one for receive, one for request
|
|
|
|
|
let f1 = crate::ipc::frame::serialize_frame(&serde_json::json!({"resp": 1})).unwrap();
|
|
|
|
|
let f2 = crate::ipc::frame::serialize_frame(&serde_json::json!({"resp": 2})).unwrap();
|
|
|
|
|
let mut s = &stream;
|
|
|
|
|
let _ = crate::ipc::frame::write_frame(&mut s, &f1);
|
|
|
|
|
let _ = crate::ipc::frame::write_frame(&mut s, &f2);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
|
|
|
|
let mut _mc = IpcClient::connect_tcp(&_a).unwrap();
|
|
|
|
|
let _ = _mc.send(&serde_json::json!({"mock":true}));
|
|
|
|
|
let _ = _mc.receive::<serde_json::Value>();
|
|
|
|
|
let _ = _mc.request::<_, serde_json::Value>(&serde_json::json!({"req": true}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== Security =====
|
|
|
|
|
{
|
|
|
|
|
use crate::app::sec::daemon::{SecDaemon, health_check};
|
|
|
|
|
let mut _sd = SecDaemon::new();
|
|
|
|
|
let _ = _sd.pid;
|
|
|
|
|
let _ = _sd.start();
|
|
|
|
|
let _ = _sd.stop();
|
|
|
|
|
let _ = health_check();
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
use crate::security::install::{install_security_sidecar, verify_sidecar, remove_sidecar, get_sidecar_path};
|
|
|
|
|
let _ = get_sidecar_path();
|
|
|
|
|
let _ = install_security_sidecar(std::path::Path::new("/tmp"));
|
|
|
|
|
let _ = verify_sidecar(std::path::Path::new("/tmp/zesdex-security-daemon"));
|
|
|
|
|
let _ = remove_sidecar(std::path::Path::new("/tmp/zesdex-security-daemon"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== StateDiff =====
|
|
|
|
|
{
|
|
|
|
|
use crate::app::state::diff::StateDiff;
|
|
|
|
|
let mut _sd = StateDiff::new();
|
|
|
|
|
_sd.add_change("file.rs".to_string(), "modified".to_string());
|
|
|
|
|
let _ = _sd.is_empty();
|
|
|
|
|
_sd.clear();
|
|
|
|
|
let _changes = crate::app::state::diff::compute_diff(
|
|
|
|
|
&serde_json::json!({"a": 1}),
|
|
|
|
|
&serde_json::json!({"a": 2}),
|
|
|
|
|
);
|
|
|
|
|
let _ = _changes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== StateSnapshot =====
|
|
|
|
|
{
|
|
|
|
|
use crate::app::state::snapshot::{StateSnapshot, serialize_snapshot, deserialize_snapshot};
|
|
|
|
|
let _ss = StateSnapshot::new();
|
|
|
|
|
let _d = serialize_snapshot(&_ss).unwrap();
|
|
|
|
|
let _ = deserialize_snapshot(&_d).unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== IPC StateDiff =====
|
|
|
|
|
{
|
|
|
|
|
use crate::ipc::diff::{StateDiff, compute_diff};
|
|
|
|
|
let mut _ipc_sd = StateDiff::new("session-id".to_string());
|
|
|
|
|
_ipc_sd.add_change("f.rs".to_string(), Some(serde_json::json!("old")), Some(serde_json::json!("new")));
|
|
|
|
|
let _ = _ipc_sd.is_empty();
|
|
|
|
|
_ipc_sd.clear();
|
|
|
|
|
let mut _changes = Vec::new();
|
|
|
|
|
compute_diff(&serde_json::json!({"a": 1}), &serde_json::json!({"a": 2}), "", &mut _changes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== IPC StateSnapshot =====
|
|
|
|
|
{
|
|
|
|
|
use crate::ipc::snapshot::{StateSnapshot, serialize_snapshot, deserialize_snapshot};
|
|
|
|
|
let _ipc_ss = StateSnapshot::new("sid".to_string(), "Normal".to_string(), "m".to_string());
|
|
|
|
|
let _d = serialize_snapshot(&_ipc_ss).unwrap();
|
|
|
|
|
let _ = deserialize_snapshot(&_d).unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== SessionRuntime =====
|
|
|
|
|
{
|
|
|
|
|
use crate::app::state::runtime::SessionRuntime;
|
|
|
|
|
let mut _rt = SessionRuntime::new(std::path::PathBuf::from("/tmp"));
|
|
|
|
|
_rt.push_message(crate::dto::chat::message::ChatMessage::user("hi"));
|
|
|
|
|
let _ = &_rt.messages;
|
|
|
|
|
let _ = &_rt.tool_call_results;
|
|
|
|
|
let _ = &_rt.pending_tool_queue;
|
|
|
|
|
let _ = &_rt.bash_jobs;
|
|
|
|
|
let _ = _rt.subagent_queue;
|
|
|
|
|
let _ = _rt.edit_count;
|
|
|
|
|
let _ = _rt.consecutive_empty_reviews;
|
|
|
|
|
let _ = &_rt.session_dir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ===== CronJob & AppStateRest =====
|
|
|
|
|
{
|
|
|
|
|
use crate::app::state::rest::{CronJob, AppStateRest, ChatMessageDisplay};
|
|
|
|
|
|
|
|
|
|
let _cj = CronJob { id: "c".to_string(), description: "d".to_string(), cron_expr: "*".to_string(), active: true };
|
|
|
|
|
let _ = &_cj.id;
|
|
|
|
|
let _ = &_cj.description;
|
|
|
|
|
let _ = &_cj.cron_expr;
|
|
|
|
|
let _ = _cj.active;
|
|
|
|
|
|
|
|
|
|
let mut _st = AppStateRest::new(
|
|
|
|
|
vec![std::env::current_dir().unwrap()],
|
|
|
|
|
std::path::PathBuf::from("/tmp/s"),
|
|
|
|
|
std::path::PathBuf::from("/tmp/m"),
|
|
|
|
|
);
|
|
|
|
|
let _ = &_st.download_dir;
|
|
|
|
|
let _ = &_st.worktrees_dir;
|
|
|
|
|
let _ = &_st.current_dir;
|
|
|
|
|
let _ = &_st.dir_cache;
|
|
|
|
|
let _ = &_st.edit_log;
|
|
|
|
|
let _ = &_st.crons;
|
|
|
|
|
let _ = _st.mode();
|
|
|
|
|
_st.set_mode(crate::app::state::types::AgentMode::Auto);
|
|
|
|
|
_st.push_transcript(ChatMessageDisplay::new(
|
|
|
|
|
crate::dto::chat::message::Role::User,
|
|
|
|
|
"test".to_string(),
|
|
|
|
|
));
|
|
|
|
|
let _ = _st.tool_ctx();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn _wire_misc(state: &mut app::state::rest::AppStateRest, rt: &tokio::runtime::Runtime) {
|
|
|
|
|
// Internet tools - reference Fetch, Download, Search structs
|
|
|
|
|
let _fetch_struct = crate::tool::internet::fetch::Fetch;
|
|
|
|
|
let _download_struct = crate::tool::internet::download::Download;
|
|
|
|
|
let _search_struct = crate::tool::internet::search::Search;
|
|
|
|
|
let _ = (&_fetch_struct, &_download_struct, &_search_struct);
|
|
|
|
|
|
|
|
|
|
// html_to_markdown and mock_search (made pub(crate) for wiring)
|
|
|
|
|
let _ = crate::tool::internet::fetch::html_to_markdown("<p>test</p>");
|
|
|
|
|
let _ = crate::tool::internet::search::mock_search("test");
|
|
|
|
|
|
|
|
|
|
// can_fetch, can_download, can_search
|
|
|
|
|
let _ = crate::model::settings::InternetMode::Off.can_fetch();
|
|
|
|
|
let _ = crate::model::settings::InternetMode::Off.can_download();
|
|
|
|
|
let _ = crate::model::settings::InternetMode::Off.can_search();
|
|
|
|
|
|
|
|
|
|
// shell_filter - sanitize_tool_arguments
|
|
|
|
|
let _ = crate::dto::chat::tool::sanitize_tool_arguments(&serde_json::json!("[]"));
|
|
|
|
|
|
|
|
|
|
// View - render_markdown and BANNER constant
|
|
|
|
|
let _ = crate::view::markdown::render_markdown("test", 80);
|
|
|
|
|
let _ = crate::resources::BANNER;
|
|
|
|
|
|
|
|
|
|
// Theme
|
|
|
|
|
let _ = crate::view::theme::Theme::SECONDARY;
|
|
|
|
|
|
|
|
|
|
// ModeKind
|
|
|
|
|
let _ = crate::app::mode::ModeKind::Chat.name();
|
|
|
|
|
let _ = crate::app::mode::ModeKind::Chat.is_overlay();
|
|
|
|
|
|
|
|
|
|
// DirCache
|
|
|
|
|
let dir_cache = crate::app::state::misc::DirCache::new();
|
|
|
|
|
rt.block_on(async {
|
|
|
|
|
dir_cache.set(vec![std::path::PathBuf::from("/tmp")]).await;
|
|
|
|
|
let _ = dir_cache.get().await;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ScrollState
|
|
|
|
|
let scroll = &mut state.scroll;
|
|
|
|
|
scroll.scroll_to_bottom(10);
|
|
|
|
|
|
|
|
|
|
// InputState
|
|
|
|
|
state.input.submit();
|
|
|
|
|
state.input.clear();
|
|
|
|
|
|
|
|
|
|
// MiscState dirty
|
|
|
|
|
let _ = state.misc.dirty;
|
|
|
|
|
state.misc.security_acknowledged = true;
|
|
|
|
|
state.misc.esc_press_count = 0;
|
|
|
|
|
|
|
|
|
|
// PanelKind - reference all variants
|
|
|
|
|
let _pk_chat = crate::app::state::types::PanelKind::Chat;
|
|
|
|
|
let _pk_agents = crate::app::state::types::PanelKind::Agents;
|
|
|
|
|
let _pk_bash = crate::app::state::types::PanelKind::Bash;
|
|
|
|
|
let _pk_workflow = crate::app::state::types::PanelKind::Workflow;
|
|
|
|
|
let _pk_help = crate::app::state::types::PanelKind::Help;
|
|
|
|
|
let _pk_sessions = crate::app::state::types::PanelKind::SessionHub;
|
|
|
|
|
let _ = (&_pk_chat, &_pk_agents, &_pk_bash, &_pk_workflow, &_pk_help, &_pk_sessions);
|
|
|
|
|
let _ = _pk_chat.name();
|
|
|
|
|
let _ = _pk_agents.name();
|
|
|
|
|
let _ = _pk_bash.name();
|
|
|
|
|
let _ = _pk_workflow.name();
|
|
|
|
|
let _ = _pk_help.name();
|
|
|
|
|
let _ = _pk_sessions.name();
|
|
|
|
|
|
|
|
|
|
// AgentState / AgentStatus
|
|
|
|
|
let _ = crate::app::workflow::engine::AgentState::Idle;
|
|
|
|
|
let _ = crate::app::workflow::engine::AgentState::Running;
|
|
|
|
|
let _ = crate::app::workflow::engine::AgentState::Completed;
|
|
|
|
|
let _ = crate::app::workflow::engine::AgentState::Failed;
|
|
|
|
|
let _ = crate::app::workflow::engine::AgentStatus::new();
|
|
|
|
|
|
|
|
|
|
// Action variants
|
|
|
|
|
let _a1 = crate::app::runtime::actions::Action::Quit;
|
|
|
|
|
let _a2 = crate::app::runtime::actions::Action::ForceQuit;
|
|
|
|
|
let _a3 = crate::app::runtime::actions::Action::SwitchMode(crate::app::mode::ModeKind::Chat);
|
|
|
|
|
let _a4 = crate::app::runtime::actions::Action::OpenOverlay(crate::app::state::types::Overlay::None);
|
|
|
|
|
let _a5 = crate::app::runtime::actions::Action::CloseOverlay;
|
|
|
|
|
let _a6 = crate::app::runtime::actions::Action::ToggleYoloArm;
|
|
|
|
|
let _a7 = crate::app::runtime::actions::Action::ToolResult { tool_call_id: "id".to_string(), output: "out".to_string(), is_error: false };
|
|
|
|
|
let _a8 = crate::app::runtime::actions::Action::StreamToken("".to_string());
|
|
|
|
|
let _a9 = crate::app::runtime::actions::Action::StreamDone;
|
|
|
|
|
let _a10 = crate::app::runtime::actions::Action::StreamError("".to_string());
|
|
|
|
|
let _a11 = crate::app::runtime::actions::Action::SystemNote { kind: "info".to_string(), message: "msg".to_string() };
|
|
|
|
|
let _a12 = crate::app::runtime::actions::Action::RunCommand("".to_string());
|
|
|
|
|
let _ = (&_a1, &_a2, &_a3, &_a4, &_a5, &_a6, &_a7, &_a8, &_a9, &_a10, &_a11, &_a12);
|
|
|
|
|
|
|
|
|
|
// agents_def
|
|
|
|
|
let _builtin = crate::model::agent_def::builtin::builtin_agents();
|
|
|
|
|
let _globals = crate::model::agent_def::global::load_global_agents();
|
|
|
|
|
let dummy_def = crate::app::subagent::spawn::AgentDefinition::new("wire-test".to_string(), "test".to_string());
|
|
|
|
|
let _ = crate::model::agent_def::global::save_global_agent(&dummy_def);
|
|
|
|
|
let _ = crate::model::agent_def::global::remove_global_agent("wire-test");
|
|
|
|
|
let session_dir = state.session_dir.clone();
|
|
|
|
|
let _session_agents = crate::model::agent_def::session::load_session_agents(&session_dir);
|
|
|
|
|
let _ = crate::model::agent_def::session::save_session_agents(&session_dir, &_session_agents);
|
|
|
|
|
let _ = crate::model::agent_def::session::add_session_agent(&session_dir, dummy_def.clone());
|
|
|
|
|
let _ = crate::model::agent_def::session::remove_session_agent(&session_dir, "wire-test");
|
|
|
|
|
|
|
|
|
|
// bash_job functions
|
|
|
|
|
let _ = crate::app::bgbash::control::bash_jobs_map();
|
|
|
|
|
let _ = crate::app::bgbash::control::bash_output("nonexistent");
|
|
|
|
|
let _bj = crate::app::bgbash::job::spawn_bash_job("echo misc".to_string());
|
|
|
|
|
let job_id = crate::app::bgbash::control::register_bash_job(_bj);
|
|
|
|
|
let _ = crate::app::bgbash::control::bash_kill(&job_id);
|
|
|
|
|
let _bj_size = std::mem::size_of::<crate::app::bgbash::job::BashJob>();
|
|
|
|
|
// Read BashJob fields via a separately spawned job
|
|
|
|
|
let _bj_fields = crate::app::bgbash::job::spawn_bash_job("echo readfields".to_string());
|
|
|
|
|
let _ = &_bj_fields.command;
|
|
|
|
|
let _ = _bj_fields.started_at;
|
|
|
|
|
let _ = &_bj_fields.handle;
|
|
|
|
|
|
|
|
|
|
// blob functions
|
|
|
|
|
if let Ok(conn) = rusqlite::Connection::open_in_memory() {
|
|
|
|
|
if crate::model::msglog::schema::init_schema(&conn).is_ok() {
|
|
|
|
|
let _ = crate::model::msglog::blobs::store_blob(&conn, "session-id", "key", b"data", Some("text/plain"));
|
|
|
|
|
let _ = crate::model::msglog::blobs::retrieve_blob(&conn, "session-id", "key");
|
|
|
|
|
let _ = crate::model::msglog::blobs::delete_blob(&conn, "session-id", "key");
|
|
|
|
|
let _ = crate::model::msglog::blobs::list_blob_keys(&conn, "session-id");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// msglog functions
|
|
|
|
|
if let Ok(conn) = rusqlite::Connection::open_in_memory() {
|
|
|
|
|
if crate::model::msglog::schema::init_schema(&conn).is_ok() {
|
|
|
|
|
let msg = crate::dto::chat::message::ChatMessage::user("hi");
|
|
|
|
|
let _ = crate::model::msglog::query::insert_message(&conn, "session-id", &msg);
|
|
|
|
|
let _ = crate::model::msglog::query::query_messages(&conn, "session-id", 10, 0);
|
|
|
|
|
let _ = crate::model::msglog::query::count_messages(&conn, "session-id");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Overlay variants suppression
|
|
|
|
|
let _ = crate::app::state::types::Overlay::Agents;
|
|
|
|
|
let _ = crate::app::state::types::Overlay::Bash;
|
|
|
|
|
let _ = crate::app::state::types::Overlay::Workflow;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn run_loop(
|
|
|
|
|
state: &mut app::state::rest::AppStateRest,
|
|
|
|
|
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
let result = run_loop_inner(state, terminal);
|
|
|
|
|
if let Err(ref _e) = result {
|
|
|
|
|
let _ = terminal.clear();
|
|
|
|
|
|
|
|
|
|
let _ = disable_raw_mode();
|
|
|
|
|
let _ = execute!(io::stdout(), LeaveAlternateScreen);
|
|
|
|
|
}
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn run_loop_inner(
|
|
|
|
|
state: &mut app::state::rest::AppStateRest,
|
|
|
|
|
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
use std::time::Duration;
|
|
|
|
|
use crossterm::event::{Event, KeyEventKind};
|
|
|
|
|
use controller::input::handle_key;
|
|
|
|
|
use app::runtime::actions::{Action, apply_action};
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
if state.quit {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
let now_ms = chrono::Utc::now().timestamp_millis();
|
|
|
|
|
state.misc.drain_expired_toasts(now_ms);
|
|
|
|
|
terminal.draw(|f| {
|
|
|
|
|
view::draw(f, state);
|
|
|
|
|
state.dirty = false;
|
|
|
|
|
})?;
|
|
|
|
|
if crossterm::event::poll(Duration::from_millis(50))? {
|
|
|
|
|
match crossterm::event::read()? {
|
|
|
|
|
Event::Key(key) => {
|
|
|
|
|
if key.kind == KeyEventKind::Press || key.kind == KeyEventKind::Repeat {
|
|
|
|
|
let actions = handle_key(key, state);
|
|
|
|
|
for action in actions {
|
|
|
|
|
apply_action(state, action);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Event::Resize(w, h) => {
|
|
|
|
|
apply_action(state, Action::Resize(w, h));
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
apply_action(state, Action::Tick);
|
|
|
|
|
}
|
|
|
|
|
terminal.clear()?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|