Logging overhaul: per-task log files, daemon.log drill-down

Switch from jobkit-daemon crate to jobkit with daemon feature.
Wire up per-task log files for all daemon-spawned agent tasks.

Changes:
- Use jobkit::daemon:: instead of jobkit_daemon::
- All agent tasks get .log_dir() set to $data_dir/logs/
- Task log path shown in daemon status and TUI
- New CLI: poc-memory agent daemon log --task NAME
  Finds the task's log path from status or daemon.log, tails the file
- LLM backend selection logged to daemon.log via log_event
- Targeted agent job names include the target key for debuggability
- Logging architecture documented in doc/logging.md

Two-level logging, no duplication:
- daemon.log: lifecycle events with task log path for drill-down
- per-task logs: full agent output via ctx.log_line()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kent Overstreet 2026-03-19 11:17:07 -04:00
parent f2c2c02a22
commit 49f72cdac3
7 changed files with 192 additions and 54 deletions

View file

@ -187,10 +187,15 @@ pub(crate) fn call_haiku(agent: &str, prompt: &str) -> Result<String, String> {
/// Uses the direct API backend when api_base_url is configured,
/// otherwise falls back to claude CLI subprocess.
pub(crate) fn call_for_def(def: &super::defs::AgentDef, prompt: &str) -> Result<String, String> {
if crate::config::get().api_base_url.is_some() && !def.tools.is_empty() {
let config = crate::config::get();
if config.api_base_url.is_some() && !def.tools.is_empty() {
super::daemon::log_event_pub(&def.agent, "llm-backend",
&format!("API: {}", config.api_base_url.as_deref().unwrap_or("?")));
let log = |msg: &str| eprintln!("[{}] {}", def.agent, msg);
super::api::call_api_with_tools_sync(&def.agent, prompt, &log)
} else {
super::daemon::log_event_pub(&def.agent, "llm-backend",
&format!("claude -p (model={}, tools={})", def.model, def.tools.len()));
call_model_with_tools(&def.agent, &def.model, prompt, &def.tools)
}
}