quiver_agent/lib.rs
1//! Phase 6 — daily-task agent + learning loop.
2//!
3//! `run(cfg)` tails Claude Code session JSONL files, calls the recommender
4//! whenever a new user message arrives, writes a hint markdown for the
5//! current session, records the top-1 as a pending `agent_suggestion`, and
6//! marks suggestions accepted when the user actually invokes the suggested
7//! tool. `digest(cfg)` produces a markdown report for a sliding window.
8
9pub mod classify;
10pub mod digest;
11pub mod hint;
12pub mod recommend;
13pub mod tail;
14
15mod engine;
16
17use std::path::PathBuf;
18use std::sync::Arc;
19
20pub use classify::{ClassifiedTask, HaikuClassifier, NoopClassifier, TaskClassifier};
21pub use digest::digest;
22pub use engine::run;
23
24/// Runtime config for the agent loop.
25#[derive(Clone)]
26pub struct AgentConfig {
27 /// SQLite path. Default: `default_db_path()` (caller-provided).
28 pub db_path: PathBuf,
29 /// Root containing `<dir>/<session>.jsonl` files. Default: `~/.claude/projects`.
30 pub sessions_dir: PathBuf,
31 /// Where to write `<session>.md` hint files. Default: `~/.claude/hints`.
32 pub hints_dir: PathBuf,
33 /// How long after a suggestion a matching `tool_use` still counts as
34 /// "accepted". Default: 60 minutes.
35 pub acceptance_window_minutes: i64,
36 /// How often the engine recomputes `tool_scores` (seconds).
37 pub score_recompute_interval_secs: u64,
38 /// Number of recommendations to write into the hint file.
39 pub top_k: usize,
40 /// Optional task classifier. `None` ⇒ raw user text is sent to the
41 /// recommender verbatim (default). When set, every `UserText` event is
42 /// piped through the classifier first; non-task messages are dropped and
43 /// real tasks are rewritten into a focused query before embedding.
44 pub classifier: Option<Arc<dyn TaskClassifier>>,
45}
46
47impl AgentConfig {
48 pub fn new(db_path: PathBuf, sessions_dir: PathBuf, hints_dir: PathBuf) -> Self {
49 Self {
50 db_path,
51 sessions_dir,
52 hints_dir,
53 acceptance_window_minutes: 60,
54 score_recompute_interval_secs: 60,
55 top_k: 3,
56 classifier: None,
57 }
58 }
59
60 pub fn with_classifier(mut self, classifier: Arc<dyn TaskClassifier>) -> Self {
61 self.classifier = Some(classifier);
62 self
63 }
64}