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
3 changes: 2 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,7 @@ use crate::routes::{
},
dreaming::__path_dreaming_review_queue,
events::__path_events_ingest,
evidence::{__path_evidence_check, __path_note_evidence},
evidence::{__path_evidence_check, __path_evidence_pack, __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 @@ -89,6 +89,7 @@ pub const SCALAR_DOCS_PATH: &str = "/docs";
events_ingest,
note_evidence,
evidence_check,
evidence_pack,
docs_put,
docs_get,
docs_delete,
Expand Down
36 changes: 35 additions & 1 deletion apps/elf-api/src/routes/evidence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@ use serde::Deserialize;
use crate::routes::{self, ApiError, AppState, HeaderMap, Json, Path, RequestContext, State, Uuid};
use elf_service::{
NoteFetchRequest,
evidence::{ClaimEvidence, EvidenceCheckRequest, EvidenceCheckResponse, NoteEvidenceResponse},
evidence::{
ClaimEvidence, EvidenceCheckRequest, EvidenceCheckResponse, EvidencePackRequest,
EvidencePackResponse, NoteEvidenceResponse,
},
};

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub(super) struct PackBody {
note_ids: Vec<Uuid>,
max_source_bytes: Option<usize>,
}

#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
pub(super) struct CheckBody {
Expand Down Expand Up @@ -59,3 +69,27 @@ pub(super) async fn evidence_check(
.await?,
))
}

#[utoipa::path(post, path = "/v2/evidence/pack", tag = "notes", request_body = Value, responses((status = 200, description = "Derived notes and bounded original context with native citations.", body = Value)))]
pub(super) async fn evidence_pack(
State(state): State<AppState>,
headers: HeaderMap,
Json(body): Json<PackBody>,
) -> Result<Json<EvidencePackResponse>, ApiError> {
let ctx = RequestContext::from_headers(&headers)?;
let read_profile = routes::required_read_profile(&headers)?;

Ok(Json(
state
.service
.evidence_pack(EvidencePackRequest {
tenant_id: ctx.tenant_id,
project_id: ctx.project_id,
agent_id: ctx.agent_id,
read_profile,
note_ids: body.note_ids,
max_source_bytes: body.max_source_bytes,
})
.await?,
))
}
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/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))
.route(
Expand Down
4 changes: 2 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,8 @@ pub(in crate::app::server) use self::{
entity_memory_get_schema, recall_debug_panel_schema,
},
notes::{
evidence_check_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, 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
11 changes: 11 additions & 0 deletions apps/elf-mcp/src/app/server/schemas/notes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ pub(in crate::app::server) fn evidence_check_schema() -> Arc<JsonObject> {
"type":"object","additionalProperties":false,"required":["text","state","citations"],"properties":{
"text":{"type":"string"},"state":{"type":"string","enum":["supported","absent","unknown","conflict"]},
"citations":{"type":"array","maxItems":8,"items":{"oneOf":[
{"type":"object","additionalProperties":false,"required":["kind","reference"],"properties":{"kind":{"const":"reference"},"reference":{"type":"string","maxLength":192}}},
{"type":"object","additionalProperties":false,"required":["kind","note_id","quote"],"properties":{"kind":{"const":"note"},"note_id":{"type":"string"},"quote":{"type":"string"}}},
{"type":"object","additionalProperties":false,"required":["kind","doc_id","content_hash","quote"],"properties":{"kind":{"const":"source"},"doc_id":{"type":"string"},"content_hash":{"type":"string"},"quote":{"type":"string"}}}
]}}
Expand All @@ -105,6 +106,16 @@ pub(in crate::app::server) fn evidence_check_schema() -> Arc<JsonObject> {
})).expect("Valid evidence check schema"))
}

pub(in crate::app::server) fn evidence_pack_schema() -> Arc<JsonObject> {
Arc::new(serde_json::from_value(serde_json::json!({
"type":"object", "additionalProperties":false, "required":["note_ids"],
"properties":{
"note_ids":{"type":"array","minItems":1,"maxItems":12,"items":{"type":"string","format":"uuid"}},
"max_source_bytes":{"type":"integer","minimum":1,"maximum":32_768}
}
})).expect("Valid evidence pack schema"))
}

fn notes_structured_entity_schema() -> Value {
serde_json::json!({
"type": "object",
Expand Down
10 changes: 8 additions & 2 deletions apps/elf-mcp/src/app/server/tests/tool_definitions/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ use std::collections::HashMap;

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

const ALL_TOOL_DEFINITIONS: [ToolDefinition; 40] = [
const ALL_TOOL_DEFINITIONS: [ToolDefinition; 41] = [
ToolDefinition::new(
"elf_notes_evidence",
HttpMethod::Get,
"/v2/notes/{note_id}/evidence",
"Read a derived note with bounded, hash-verified original context. Use the original subject and time to assess support; a valid source hash does not prove a claim. Does not modify sources.",
),
ToolDefinition::new(
"elf_evidence_pack",
HttpMethod::Post,
"/v2/evidence/pack",
"Resolve 1-12 retrieved note IDs into deduplicated original context with native citation references. Source timestamps are record times, not inferred fact validity. Incomplete context does not prove absence.",
),
ToolDefinition::new(
"elf_evidence_check",
HttpMethod::Post,
Expand All @@ -25,7 +31,7 @@ const ALL_TOOL_DEFINITIONS: [ToolDefinition; 40] = [
"elf_graph_query",
HttpMethod::Post,
"/v2/graph/query",
"Query graph entities and relations by structured criteria.",
"Query graph entities and relations by structured criteria. To inspect original context, pass selected evidence_note_ids to elf_evidence_pack; a graph edge does not certify semantic truth.",
),
ToolDefinition::new(
"elf_graph_report",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ fn registers_all_tools() {
"elf_notes_get",
"elf_notes_evidence",
"elf_evidence_check",
"elf_evidence_pack",
"elf_notes_patch",
"elf_notes_delete",
"elf_notes_publish",
Expand Down
2 changes: 1 addition & 1 deletion apps/elf-mcp/src/app/server/tools/core/ingest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl ElfMcp {

#[rmcp::tool(
name = "elf_graph_query",
description = "Query graph entities and relations by structured criteria.",
description = "Query graph entities and relations by structured criteria. To inspect original context, pass selected evidence_note_ids to elf_evidence_pack; a graph edge does not certify semantic truth.",
input_schema = graph_query_schema()
)]
async fn elf_graph_query(&self, params: JsonObject) -> Result<CallToolResult, ErrorData> {
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,8 +7,8 @@ use rmcp::{
use crate::app::server::{
ElfMcp, HttpMethod,
schemas::{
evidence_check_schema, notes_get_schema, notes_list_schema, notes_patch_schema,
notes_publish_schema, notes_unpublish_schema,
evidence_check_schema, evidence_pack_schema, notes_get_schema, notes_list_schema,
notes_patch_schema, notes_publish_schema, notes_unpublish_schema,
},
support,
};
Expand Down Expand Up @@ -56,6 +56,11 @@ impl ElfMcp {
.await
}

#[rmcp::tool(name = "elf_evidence_pack", description = "Resolve 1-12 retrieved note IDs into deduplicated original context with native citation references. Source timestamps are record times, not inferred fact validity. Incomplete context does not prove absence.", input_schema = evidence_pack_schema())]
async fn elf_evidence_pack(&self, params: JsonObject) -> Result<CallToolResult, ErrorData> {
self.forward(HttpMethod::Post, "/v2/evidence/pack", params, None).await
}

#[rmcp::tool(name = "elf_evidence_check", description = "Check each claim citation against currently readable notes or hash-guarded original quotes. This verifies source bindings, not semantic entailment.", input_schema = evidence_check_schema())]
async fn elf_evidence_check(&self, params: JsonObject) -> Result<CallToolResult, ErrorData> {
self.forward(HttpMethod::Post, "/v2/evidence/check", params, None).await
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"prior_paid_usd": 7.906963426,
"new_paid_usd": 0.19228509,
"cumulative_paid_usd": 8.099248516000022,
"conservative_exposure_usd": 9.183048916000008,
"ceiling_usd": 10,
"unsettled_reservations": [
{
"ordinal": 1577,
"reserved_usd": 0.5691777,
"status": "provider_failed"
},
{
"ordinal": 4501,
"reserved_usd": 0.5146227,
"status": "provider_failed"
}
]
}
Loading
Loading