feat: inject memory summary into resident prompt
This commit is contained in:
@@ -22,7 +22,10 @@ pub use error::{LintError, LintWarning, MemoryError};
|
||||
pub use extract::ExtractPointerPayload;
|
||||
pub use lint_common::{RecordLintError, Slug, is_valid_slug};
|
||||
pub use linter::{LintReport, Linter};
|
||||
pub use resident::{ResidentKnowledgeEntry, collect_resident_knowledge, list_knowledge_slugs};
|
||||
pub use resident::{
|
||||
ResidentKnowledgeEntry, collect_resident_knowledge, collect_resident_summary,
|
||||
list_knowledge_slugs,
|
||||
};
|
||||
pub use scope::deny_write_rules;
|
||||
pub use usage::{
|
||||
UsageEvent, UsageEventKind, UsageRecordSnapshot, UsageReport, UsageReportRecord, UsageSource,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
//! Workspace knowledge enumeration helpers.
|
||||
//! Workspace memory resident-enumeration helpers.
|
||||
//!
|
||||
//! Two surfaces, both walking `<workspace>/.insomnia/knowledge/*.md`:
|
||||
//! Surfaces used by the Pod system-prompt assembler:
|
||||
//!
|
||||
//! - [`collect_resident_knowledge`] — resident-injection candidates
|
||||
//! (`model_invokation: true`) returned as `(slug, description)` pairs
|
||||
//! for the Pod system-prompt assembler.
|
||||
//! (`model_invokation: true`) returned as `(slug, description)` pairs.
|
||||
//! - [`collect_resident_summary`] — the body of
|
||||
//! `<workspace>/.insomnia/memory/summary.md` when it parses as a summary
|
||||
//! record and has non-empty body.
|
||||
//! - [`list_knowledge_slugs`] — every slug whose file parses, regardless
|
||||
//! of `model_invokation`. Used by the Pod IPC layer to answer TUI `#`
|
||||
//! completion (`model_invokation` is a resident-injection flag, not a
|
||||
@@ -14,7 +16,7 @@
|
||||
//! enforces shape on write, so a malformed file here means external
|
||||
//! tampering and we'd rather degrade than panic.
|
||||
|
||||
use crate::schema::{KnowledgeFrontmatter, split_frontmatter};
|
||||
use crate::schema::{KnowledgeFrontmatter, SummaryFrontmatter, split_frontmatter};
|
||||
use crate::workspace::WorkspaceLayout;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@@ -40,6 +42,21 @@ pub fn collect_resident_knowledge(layout: &WorkspaceLayout) -> Vec<ResidentKnowl
|
||||
out
|
||||
}
|
||||
|
||||
/// Read `<workspace>/.insomnia/memory/summary.md` for resident prompt
|
||||
/// injection. Returns only the markdown body (frontmatter stripped), and
|
||||
/// degrades to `None` for missing, unreadable, malformed, or empty records.
|
||||
pub fn collect_resident_summary(layout: &WorkspaceLayout) -> Option<String> {
|
||||
let raw = std::fs::read_to_string(layout.summary_path()).ok()?;
|
||||
let (yaml, body) = split_frontmatter(&raw).ok()?;
|
||||
let _fm: SummaryFrontmatter = serde_yaml::from_str(yaml).ok()?;
|
||||
let body = body.trim_matches(&['\n', '\r'][..]);
|
||||
if body.trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(body.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Walk `<workspace>/knowledge/*.md` and return every slug whose
|
||||
/// frontmatter parses, sorted ascending. Does not filter on
|
||||
/// `model_invokation`. A missing `knowledge/` directory yields an empty
|
||||
@@ -97,6 +114,12 @@ mod tests {
|
||||
Utc::now().to_rfc3339()
|
||||
}
|
||||
|
||||
fn write_summary(dir: &Path, body: &str) {
|
||||
let path = dir.join(".insomnia/memory/summary.md");
|
||||
let content = format!("---\nupdated_at: {n}\n---\n{body}", n = now());
|
||||
std::fs::write(path, content).unwrap();
|
||||
}
|
||||
|
||||
fn write_knowledge(
|
||||
dir: &Path,
|
||||
slug: &str,
|
||||
@@ -116,10 +139,48 @@ mod tests {
|
||||
fn setup() -> (TempDir, WorkspaceLayout) {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".insomnia/knowledge")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".insomnia/memory")).unwrap();
|
||||
let layout = WorkspaceLayout::new(dir.path().to_path_buf());
|
||||
(dir, layout)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_summary_returns_none() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let layout = WorkspaceLayout::new(dir.path().to_path_buf());
|
||||
assert!(collect_resident_summary(&layout).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summary_returns_body_without_frontmatter() {
|
||||
let (dir, layout) = setup();
|
||||
write_summary(dir.path(), "remember this\n");
|
||||
|
||||
let got = collect_resident_summary(&layout).unwrap();
|
||||
assert_eq!(got, "remember this");
|
||||
assert!(!got.contains("updated_at"));
|
||||
assert!(!got.contains("---"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_summary_returns_none() {
|
||||
let (dir, layout) = setup();
|
||||
std::fs::write(
|
||||
dir.path().join(".insomnia/memory/summary.md"),
|
||||
"---\nthis is not yaml: : :\n---\nbody\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(collect_resident_summary(&layout).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_summary_body_returns_none() {
|
||||
let (dir, layout) = setup();
|
||||
write_summary(dir.path(), " \n");
|
||||
assert!(collect_resident_summary(&layout).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_knowledge_dir_returns_empty() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user