Skip to content
This repository was archived by the owner on Oct 20, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/elf-api/src/routes/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ use crate::routes::{
},
dreaming::__path_dreaming_review_queue,
events::__path_events_ingest,
evidence::{__path_evidence_check, __path_evidence_pack, __path_note_evidence},
evidence::{
__path_evidence_check, __path_evidence_pack, __path_evidence_recall, __path_note_evidence,
},
graph::{
__path_admin_graph_predicate_alias_add, __path_admin_graph_predicate_aliases_list,
__path_admin_graph_predicate_patch, __path_admin_graph_predicates_list, __path_graph_query,
Expand Down Expand Up @@ -90,6 +92,7 @@ pub const SCALAR_DOCS_PATH: &str = "/docs";
note_evidence,
evidence_check,
evidence_pack,
evidence_recall,
docs_put,
docs_get,
docs_delete,
Expand Down
46 changes: 45 additions & 1 deletion apps/elf-api/src/routes/evidence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use elf_service::{
NoteFetchRequest,
evidence::{
ClaimEvidence, EvidenceCheckRequest, EvidenceCheckResponse, EvidencePackRequest,
EvidencePackResponse, NoteEvidenceResponse,
EvidencePackResponse, EvidenceRecallRequest, EvidenceRecallResponse, NoteEvidenceResponse,
},
};

Expand All @@ -22,6 +22,13 @@ pub(super) struct CheckBody {
claims: Vec<ClaimEvidence>,
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub(super) struct RecallBody {
subjects: Vec<String>,
as_of: Option<String>,
}

#[utoipa::path(get, path = "/v2/notes/{note_id}/evidence", tag = "notes", params(("note_id" = Uuid, Path, description = "Note ID.")), responses((status = 200, description = "Derived note and bounded verified original context.", body = Value)))]
pub(super) async fn note_evidence(
State(state): State<AppState>,
Expand Down Expand Up @@ -93,3 +100,40 @@ pub(super) async fn evidence_pack(
.await?,
))
}

#[utoipa::path(post, path = "/v2/evidence/recall", tag = "notes", request_body = Value, responses((status = 200, description = "Source-diverse lexical and graph evidence candidates.", body = Value)))]
pub(super) async fn evidence_recall(
State(state): State<AppState>,
headers: HeaderMap,
Json(body): Json<RecallBody>,
) -> Result<Json<EvidenceRecallResponse>, ApiError> {
let ctx = RequestContext::from_headers(&headers)?;
let read_profile = routes::required_read_profile(&headers)?;

Ok(Json(
state
.service
.evidence_recall(EvidenceRecallRequest {
tenant_id: ctx.tenant_id,
project_id: ctx.project_id,
agent_id: ctx.agent_id,
read_profile,
subjects: body.subjects,
as_of: routes::parse_optional_rfc3339(body.as_of.as_ref(), "as_of")?,
})
.await?,
))
}

#[cfg(test)]
mod tests {
use crate::routes::evidence::RecallBody;

#[test]
fn recall_time_is_optional_at_the_http_boundary() {
let body: RecallBody = serde_json::from_value(serde_json::json!({"subjects":["Cedar"]}))
.expect("Omitted optional time");

assert!(body.as_of.is_none());
}
}
1 change: 1 addition & 0 deletions apps/elf-api/src/routes/route_builder/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub(super) fn public_api_router() -> Router<AppState> {
.route("/v2/graph/query", routing::post(routes::graph::graph_query))
.route("/v2/graph/report", routing::post(routes::graph::graph_report))
.route("/v2/notes/{note_id}/evidence", routing::get(routes::evidence::note_evidence))
.route("/v2/evidence/recall", routing::post(routes::evidence::evidence_recall))
.route("/v2/evidence/pack", routing::post(routes::evidence::evidence_pack))
.route("/v2/evidence/check", routing::post(routes::evidence::evidence_check))
.route("/v2/notes", routing::get(routes::notes::notes_list))
Expand Down
5 changes: 3 additions & 2 deletions apps/elf-mcp/src/app/server/schemas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ pub(in crate::app::server) use self::{
entity_memory_get_schema, recall_debug_panel_schema,
},
notes::{
evidence_check_schema, evidence_pack_schema, notes_get_schema, notes_ingest_schema,
notes_list_schema, notes_patch_schema, notes_publish_schema, notes_unpublish_schema,
evidence_check_schema, evidence_pack_schema, evidence_recall_schema, notes_get_schema,
notes_ingest_schema, notes_list_schema, notes_patch_schema, notes_publish_schema,
notes_unpublish_schema,
},
search::{
searches_create_schema, searches_get_schema, searches_notes_schema,
Expand Down
10 changes: 10 additions & 0 deletions apps/elf-mcp/src/app/server/schemas/notes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ pub(in crate::app::server) fn evidence_pack_schema() -> Arc<JsonObject> {
})).expect("Valid evidence pack schema"))
}

pub(in crate::app::server) fn evidence_recall_schema() -> Arc<JsonObject> {
Arc::new(serde_json::from_value(serde_json::json!({
"type": "object", "additionalProperties": false, "required": ["subjects"],
"properties": {
"subjects": {"type":"array", "minItems":1, "maxItems":4, "items":{"type":"string", "minLength":2, "maxLength":128}},
"as_of": {"type":["string", "null"], "format":"date-time"}
}
})).expect("Valid evidence recall schema"))
}

fn notes_structured_entity_schema() -> Value {
serde_json::json!({
"type": "object",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ use std::collections::HashMap;

use crate::app::server::HttpMethod;

const ALL_TOOL_DEFINITIONS: [ToolDefinition; 41] = [
const ALL_TOOL_DEFINITIONS: [ToolDefinition; 42] = [
ToolDefinition::new(
"elf_evidence_recall",
HttpMethod::Post,
"/v2/evidence/recall",
"Supplement incomplete recall with 1-4 literal subject names or identifiers. Retrieve original text independently of extraction, plus source-diverse note and incoming/outgoing graph evidence across distinct entity candidates without merging them. Supply as_of for historical questions; compare states effective then, retain late reports, and do not apply later decisions backward. Match and citation validity do not prove truth.",
),
ToolDefinition::new(
"elf_notes_evidence",
HttpMethod::Get,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ fn registers_all_tools() {
"elf_notes_evidence",
"elf_evidence_check",
"elf_evidence_pack",
"elf_evidence_recall",
"elf_notes_patch",
"elf_notes_delete",
"elf_notes_publish",
Expand Down
9 changes: 7 additions & 2 deletions apps/elf-mcp/src/app/server/tools/core/notes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ use rmcp::{
use crate::app::server::{
ElfMcp, HttpMethod,
schemas::{
evidence_check_schema, evidence_pack_schema, notes_get_schema, notes_list_schema,
notes_patch_schema, notes_publish_schema, notes_unpublish_schema,
evidence_check_schema, evidence_pack_schema, evidence_recall_schema, notes_get_schema,
notes_list_schema, notes_patch_schema, notes_publish_schema, notes_unpublish_schema,
},
support,
};

#[rmcp::tool_router(router = core_notes_tool_router, vis = "pub(in crate::app::server)")]
impl ElfMcp {
#[rmcp::tool(name = "elf_evidence_recall", description = "Supplement incomplete recall with 1-4 literal subject names or identifiers. Retrieve original text independently of extraction, plus source-diverse note and incoming/outgoing graph evidence across distinct entity candidates without merging them. Supply as_of for historical questions; compare states effective then, retain late reports, and do not apply later decisions backward. Match and citation validity do not prove truth.", input_schema = evidence_recall_schema())]
async fn elf_evidence_recall(&self, params: JsonObject) -> Result<CallToolResult, ErrorData> {
self.forward(HttpMethod::Post, "/v2/evidence/recall", params, None).await
}

#[rmcp::tool(
name = "elf_notes_list",
description = "List notes in a tenant and project with optional filters.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"prior_paid_usd": 8.099248516,
"new_paid_usd": 0.403978656,
"initial_trial_paid_usd": 0.199928712,
"final_incremental_paid_usd": 0.20404994399999998,
"cumulative_paid_usd": 8.50322717200002,
"conservative_exposure_usd": 9.587027572000006,
"ceiling_usd": 10,
"unique_paid_requests": 67,
"unsettled_reservations": [
{
"ordinal": 1577,
"reserved_usd": 0.5691777,
"status": "provider_failed"
},
{
"ordinal": 4501,
"reserved_usd": 0.5146227,
"status": "provider_failed"
}
]
}
Loading
Loading