Skip to main content

talon_core/query/
input.rs

1//! Query tool input types.
2
3use serde::{Deserialize, Serialize};
4
5use crate::constants::{CHANGES_DEFAULT_LIMIT, DEFAULT_LIMIT};
6use crate::contracts::PositiveCount;
7use crate::search::input::WhereClause;
8
9/// Output format for the recall command.
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(rename_all = "kebab-case")]
12pub enum RecallFormat {
13    /// Structured JSON (default).
14    #[default]
15    Json,
16    /// Prompt-XML block ready for agent context injection.
17    PromptXml,
18}
19
20/// Read request.
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
22#[serde(rename_all = "camelCase")]
23pub struct ReadInput {
24    /// Single path to read.
25    pub path: Option<String>,
26    /// Include raw content.
27    #[serde(default)]
28    pub raw: bool,
29    /// First line to include.
30    #[serde(default)]
31    pub from_line: Option<PositiveCount>,
32    /// Maximum lines to include.
33    #[serde(default)]
34    pub max_lines: Option<PositiveCount>,
35}
36
37/// Context recall request.
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39#[serde(rename_all = "camelCase")]
40pub struct RecallInput {
41    /// The current user message to recall context for.
42    pub message: String,
43    /// Prior conversation turns (last N user/assistant messages) fed to expansion.
44    #[serde(default)]
45    pub prior_messages: Vec<String>,
46    /// Token budget for the response payload (default 2000).
47    #[serde(default = "default_recall_budget")]
48    pub budget_tokens: u32,
49    /// Vault paths to exclude from all retrieval sections.
50    #[serde(default)]
51    pub exclude: Vec<String>,
52    /// Scope names to include (additive).
53    #[serde(default)]
54    pub scope: Vec<String>,
55    /// Scope names to search exclusively.
56    #[serde(default)]
57    pub scope_only: Vec<String>,
58    /// Include every configured scope, overriding `default = false`.
59    #[serde(default)]
60    pub scope_all: bool,
61    /// Output format.
62    #[serde(default)]
63    pub format: RecallFormat,
64    /// Link graph traversal depth for `linked_context` (1-3, default 1).
65    #[serde(default = "default_recall_depth")]
66    pub depth: u8,
67    /// Minimum `evidence_score` threshold; below this, return `skipped=true` (default 0.0).
68    #[serde(default)]
69    pub min_confidence: f64,
70    /// Skip expansion and rerank (fast lexical-only path).
71    #[serde(default)]
72    pub fast: bool,
73    /// Include recall stage diagnostics in JSON output.
74    #[serde(default)]
75    pub diagnostics: bool,
76    /// Optional wall-clock deadline for hook-style recall orchestration.
77    #[serde(default, skip_serializing_if = "Option::is_none")]
78    pub deadline_ms: Option<u64>,
79}
80
81const fn default_recall_budget() -> u32 {
82    2000
83}
84
85const fn default_recall_depth() -> u8 {
86    1
87}
88
89impl Default for RecallInput {
90    fn default() -> Self {
91        Self {
92            message: String::new(),
93            prior_messages: Vec::new(),
94            budget_tokens: default_recall_budget(),
95            exclude: Vec::new(),
96            scope: Vec::new(),
97            scope_only: Vec::new(),
98            scope_all: false,
99            format: RecallFormat::Json,
100            depth: default_recall_depth(),
101            min_confidence: 0.0,
102            fast: false,
103            diagnostics: false,
104            deadline_ms: None,
105        }
106    }
107}
108
109/// Frontmatter query request.
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
111#[serde(rename_all = "camelCase")]
112pub struct MetaInput {
113    /// Frontmatter `--where` filters (AND-composed).
114    #[serde(default)]
115    pub where_: Vec<WhereClause>,
116    /// Filter results indexed since this timestamp.
117    #[serde(default)]
118    pub since: Option<String>,
119    /// Scope names to include.
120    #[serde(default)]
121    pub scope: Vec<String>,
122    /// Scope names to search exclusively.
123    #[serde(default)]
124    pub scope_only: Vec<String>,
125    /// Include every configured scope, overriding `default = false`.
126    #[serde(default)]
127    pub scope_all: bool,
128    /// Frontmatter fields to select (comma-separated).
129    #[serde(default)]
130    pub select: Vec<String>,
131    /// Emit tag counts.
132    #[serde(default)]
133    pub tag_counts: bool,
134    /// Reverse-source index: return files listed in a path's `sources:` frontmatter.
135    #[serde(default)]
136    pub sources: Option<String>,
137    /// Result limit.
138    #[serde(default)]
139    pub limit: PositiveCount,
140}
141
142impl Default for MetaInput {
143    fn default() -> Self {
144        Self {
145            where_: Vec::new(),
146            since: None,
147            scope: Vec::new(),
148            scope_only: Vec::new(),
149            scope_all: false,
150            select: Vec::new(),
151            tag_counts: false,
152            sources: None,
153            limit: PositiveCount::from_const(DEFAULT_LIMIT),
154        }
155    }
156}
157
158/// Change feed request.
159#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
160#[serde(rename_all = "camelCase")]
161pub struct ChangesInput {
162    /// Return changes since this timestamp.
163    pub since: String,
164    /// Scope names to include.
165    #[serde(default)]
166    pub scope: Vec<String>,
167    /// Scope names to search exclusively.
168    #[serde(default)]
169    pub scope_only: Vec<String>,
170    /// Include every configured scope, overriding `default = false`.
171    #[serde(default)]
172    pub scope_all: bool,
173    /// Result limit.
174    #[serde(default = "default_changes_limit")]
175    pub limit: PositiveCount,
176}
177
178const fn default_changes_limit() -> PositiveCount {
179    PositiveCount::from_const(CHANGES_DEFAULT_LIMIT)
180}