From 2e41ed192d5cb6004445df6ac88fb21ddc72ac89 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 13 Aug 2026 22:33:20 +0200 Subject: [PATCH] Make public Research evidence request-owned --- epiphany-core/src/lib.rs | 2 + epiphany-core/src/runtime_spine.rs | 321 +++++++++++++----- .../src/bin/epiphany-openai-runtime.rs | 32 +- epiphany-openai-runtime/src/lib.rs | 161 +++++++-- 4 files changed, 392 insertions(+), 124 deletions(-) diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index 5b16b7a1..1ab3a65b 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -1115,9 +1115,11 @@ pub use runtime_spine::put_repo_frontier_verification_request; pub use runtime_spine::put_repo_frontier_work_proposal; pub use runtime_spine::put_runtime_reorient_worker_result; pub use runtime_spine::put_runtime_role_worker_result; +pub use runtime_spine::put_runtime_requested_public_source_intents; pub use runtime_spine::put_runtime_tool_execution_intent; pub use runtime_spine::put_runtime_tool_execution_receipt; pub use runtime_spine::runtime_authenticated_public_source_lookups_for_worker; +pub use runtime_spine::runtime_requested_public_source_refs_for_worker; #[cfg(test)] pub(crate) use runtime_spine::put_soul_verdict_receipt; pub use runtime_spine::put_substrate_gate_repo_access_grant_receipt; diff --git a/epiphany-core/src/runtime_spine.rs b/epiphany-core/src/runtime_spine.rs index 24c4794d..413158ea 100644 --- a/epiphany-core/src/runtime_spine.rs +++ b/epiphany-core/src/runtime_spine.rs @@ -1932,8 +1932,15 @@ pub fn put_runtime_tool_execution_intent( job_envelope.clone(), ]; let mut replacements = expected.clone(); - expected.extend(governed_source_authority.iter().cloned()); - replacements.extend(governed_source_authority); + for envelope in governed_source_authority { + let already_fenced = expected + .iter() + .any(|existing| existing.key == envelope.key && existing.r#type == envelope.r#type); + if !already_fenced { + expected.push(envelope.clone()); + replacements.push(envelope); + } + } if let Some(model_request_id) = intent.model_request_id.as_deref() { let model_binding = cache .get::(model_request_id)? @@ -3649,6 +3656,107 @@ fn validate_frontier_research_launch_carrier( Ok(()) } +pub fn put_runtime_requested_public_source_intents( + store_path: impl AsRef, + worker_job_id: &str, + created_at: &str, +) -> Result> { + validate_non_empty(worker_job_id, "requested public source worker job id")?; + chrono::DateTime::parse_from_rfc3339(created_at) + .map_err(|error| anyhow!("requested public source intent time is invalid: {error}"))?; + let store_path = store_path.as_ref(); + let mut cache = runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + let launch = cache + .get::(worker_job_id)? + .ok_or_else(|| anyhow!("requested public source worker has no launch request"))?; + let request = frontier_research_request_for_launch(&cache, &launch)? + .ok_or_else(|| anyhow!("requested public source worker has no typed Research request"))?; + let job = cache + .get::(worker_job_id)? + .ok_or_else(|| anyhow!("requested public source worker has no runtime job"))?; + drop(cache); + + let mut intents = Vec::new(); + for source_ref in request.public_source_refs { + let source = crate::ImmutableGithubSource::parse(&source_ref)?; + let intent_id = requested_public_source_intent_id(worker_job_id, &source_ref); + let call_id = format!("call-{intent_id}"); + let mut intent = EpiphanyToolInvocationIntent::new( + intent_id.clone(), + epiphany_tool_adapter::EPIPHANY_TOOL_RUNTIME_ADAPTER_ID, + "epiphany_public", + "github_file", + requested_public_source_arguments(&source), + "epiphany-runtime-requested-public-source", + format!( + "Typed Research request {} requires immutable source {}.", + request.request_id, source_ref + ), + created_at, + ); + intent.call_id = Some(call_id); + + let mut cache = runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + let existing_intent = cache.get::( + &tool_invocation_intent_key(&intent_id), + )?; + let existing_binding = cache.get::(&intent_id)?; + if let (Some(existing_intent), Some(existing_binding)) = + (&existing_intent, &existing_binding) + { + validate_requested_public_source_intent(&cache, worker_job_id, existing_intent)?; + if existing_intent.reason != intent.reason + || chrono::DateTime::parse_from_rfc3339(&existing_intent.created_at).is_err() + || existing_binding.intent_id != intent_id + || existing_binding.job_id != worker_job_id + || existing_binding.session_id != job.session_id + || existing_binding.model_request_id.is_some() + { + return Err(anyhow!( + "requested public source intent {intent_id:?} collides with foreign authority" + )); + } + let existing_intent = existing_intent.clone(); + drop(cache); + require_runtime_tool_execution_binding(store_path, &intent_id)?; + intents.push(existing_intent); + continue; + } + if existing_intent.is_some() || existing_binding.is_some() { + return Err(anyhow!( + "requested public source intent {intent_id:?} has a partial persisted family" + )); + } + drop(cache); + put_runtime_tool_execution_intent( + store_path, + &job.session_id, + worker_job_id, + &intent, + created_at, + )?; + intents.push(intent); + } + Ok(intents) +} + +pub fn runtime_requested_public_source_refs_for_worker( + store_path: impl AsRef, + worker_job_id: &str, +) -> Result> { + validate_non_empty(worker_job_id, "requested public source worker job id")?; + let mut cache = runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + let launch = cache + .get::(worker_job_id)? + .ok_or_else(|| anyhow!("requested public source worker has no launch request"))?; + Ok(frontier_research_request_for_launch(&cache, &launch)? + .map(|request| request.public_source_refs) + .unwrap_or_default()) +} + fn frontier_research_request_for_launch( cache: &CultCache, launch: &EpiphanyRuntimeWorkerLaunchRequest, @@ -3755,10 +3863,12 @@ fn authenticated_public_source_lookup_receipts_for_worker( .collect::>(); let mut lookups = Vec::new(); for binding in cache.get_all::()? { - let Some(model_request_id) = binding.model_request_id.as_deref() else { - continue; - }; - if !model_request_ids.contains(model_request_id) { + let model_owned = binding + .model_request_id + .as_deref() + .is_some_and(|model_request_id| model_request_ids.contains(model_request_id)); + let request_owned = binding.model_request_id.is_none() && binding.job_id == worker_job_id; + if !model_owned && !request_owned { continue; } let intent = cache @@ -3776,7 +3886,7 @@ fn authenticated_public_source_lookup_receipts_for_worker( .ok_or_else(|| anyhow!("public source tool intent has no terminal receipt"))?; let _ = validate_governed_source_tool_intent(&cache, &binding.job_id, &intent)?; if binding.intent_id != intent.intent_id - || intent.model_request_id.as_deref() != Some(model_request_id) + || intent.model_request_id != binding.model_request_id || receipt.intent_id != intent.intent_id || receipt.adapter != intent.adapter || receipt.server != intent.server @@ -3834,7 +3944,7 @@ fn authenticated_public_source_lookup_receipts_for_worker( content_sha256, byte_count, observed_at: receipt.completed_at, - contract: "Eyes admitted one bounded immutable public source only after authenticating its worker, model, tool, grant, provider identity, and content digest.".to_string(), + contract: "Eyes admitted one bounded immutable public source only after authenticating its worker, causal request or model execution, tool, grant, provider identity, and content digest.".to_string(), }); } lookups.sort_by(|left, right| left.receipt_id.cmp(&right.receipt_id)); @@ -3922,11 +4032,11 @@ fn validate_governed_source_tool_intent( ) }) { - return Err(anyhow!( - "governed source worker refuses a tool intent without model-call provenance" - )); + validate_requested_public_source_intent(cache, job_id, intent)?; + Some(job_id.to_string()) + } else { + None } - None } }; let Some(source_worker_job_id) = source_worker_job_id else { @@ -3996,6 +4106,62 @@ fn validate_governed_source_tool_intent( ]) } +fn requested_public_source_intent_id(worker_job_id: &str, source_ref: &str) -> String { + format!( + "requested-public-source-{:x}", + Sha256::digest(format!("{worker_job_id}:{source_ref}").as_bytes()) + ) +} + +fn requested_public_source_arguments(source: &crate::ImmutableGithubSource) -> String { + serde_json::json!({ + "owner": source.owner(), + "repository": source.repository_name(), + "revision": source.revision(), + "path": source.path(), + "maxBytes": 65536 + }) + .to_string() +} + +fn validate_requested_public_source_intent( + cache: &CultCache, + worker_job_id: &str, + intent: &EpiphanyToolInvocationIntent, +) -> Result<()> { + let launch = cache + .get::(worker_job_id)? + .ok_or_else(|| anyhow!("requested public source intent lost its worker launch"))?; + let request = frontier_research_request_for_launch(cache, &launch)? + .ok_or_else(|| anyhow!("requested public source intent has no typed Research request"))?; + let arguments: serde_json::Value = serde_json::from_str(&intent.arguments_json) + .context("requested public source intent arguments are invalid")?; + let source = crate::ImmutableGithubSource::from_components( + public_source_json_string(&arguments, "owner")?, + public_source_json_string(&arguments, "repository")?, + public_source_json_string(&arguments, "revision")?, + public_source_json_string(&arguments, "path")?, + )?; + let source_ref = source.to_string(); + let expected_id = requested_public_source_intent_id(worker_job_id, &source_ref); + let expected_call_id = format!("call-{expected_id}"); + if intent.intent_id != expected_id + || intent.adapter != epiphany_tool_adapter::EPIPHANY_TOOL_RUNTIME_ADAPTER_ID + || intent.server != "epiphany_public" + || intent.tool_name != "github_file" + || intent.arguments_json != requested_public_source_arguments(&source) + || intent.caller != "epiphany-runtime-requested-public-source" + || intent.call_id.as_deref() != Some(expected_call_id.as_str()) + || intent.model_request_id.is_some() + || !request.public_source_refs.contains(&source_ref) + { + return Err(anyhow!( + "governed source worker refuses a noncanonical request-owned public lookup" + )); + } + Ok(()) +} + pub fn runtime_worker_process_claims( store_path: impl AsRef, ) -> Result> { @@ -20031,58 +20197,62 @@ pub(crate) mod tests { before_missing_lookup ); - let mut model_request = EpiphanyModelRequest::new( - "request-public-tool", - "conversation-public-tool", - "openai-codex", - "gpt-test", - "Read one immutable public source.", + assert_eq!( + runtime_requested_public_source_refs_for_worker(&store, "worker-public-tool")?, + vec![source_ref] ); - model_request.source_worker_job_id = Some("worker-public-tool".to_string()); - let provider_request = EpiphanyOpenAiModelRequest::new( - "request-public-tool", - "conversation-public-tool", - "gpt-test", - "Read one immutable public source.", - ); - open_runtime_model_execution( + let requested = put_runtime_requested_public_source_intents( &store, - RuntimeSpineSessionOptions { - session_id: "model-session-public-tool".to_string(), - objective: "Read one immutable public source.".to_string(), - created_at: "2026-08-11T00:00:02Z".to_string(), - coordinator_note: "test".to_string(), - }, - RuntimeSpineJobOptions { - job_id: "model-job-public-tool".to_string(), - session_id: "model-session-public-tool".to_string(), - role: "openai-model-adapter".to_string(), - created_at: "2026-08-11T00:00:02Z".to_string(), - summary: "Bound public source model turn.".to_string(), - artifact_refs: Vec::new(), - }, - &model_request, - &provider_request, - "2026-08-11T00:00:02Z", + "worker-public-tool", + "2026-08-11T00:00:03Z", )?; - let exact = EpiphanyToolInvocationIntent::new( - "intent-public-tool-exact", - "epiphany-tools", + assert_eq!(requested.len(), 1); + let exact = &requested[0]; + assert_eq!(exact.model_request_id, None); + assert_eq!(exact.server, "epiphany_public"); + assert_eq!(exact.tool_name, "github_file"); + assert_eq!( + put_runtime_requested_public_source_intents( + &store, + "worker-public-tool", + "2026-08-11T00:00:05Z", + )?, + requested + ); + let exact_binding = + require_runtime_tool_execution_binding(&store, &exact.intent_id)?; + let unrequested_source = crate::ImmutableGithubSource::parse(concat!( + "github://openai/openai-openapi@", + "0123456789abcdef0123456789abcdef01234567/README.md" + ))?; + let unrequested_id = + requested_public_source_intent_id("worker-public-tool", &unrequested_source.to_string()); + let mut unrequested = EpiphanyToolInvocationIntent::new( + &unrequested_id, + epiphany_tool_adapter::EPIPHANY_TOOL_RUNTIME_ADAPTER_ID, "epiphany_public", "github_file", - r#"{"owner":"openai","repository":"openai-openapi","revision":"0123456789abcdef0123456789abcdef01234567","path":"openapi.yaml"}"#, - "model-runtime", - "Read immutable source.", + requested_public_source_arguments(&unrequested_source), + "epiphany-runtime-requested-public-source", + "Attempt an unrequested immutable source.", "2026-08-11T00:00:03Z", - ) - .with_model_call("call-public-tool-exact", "request-public-tool"); - put_runtime_tool_execution_intent( - &store, - "model-session-public-tool", - "model-job-public-tool", - &exact, - "2026-08-11T00:00:03Z", - )?; + ); + unrequested.call_id = Some(format!("call-{unrequested_id}")); + let before_unrequested = runtime_spine_backing_store(&store)?.pull_all()?; + assert!( + put_runtime_tool_execution_intent( + &store, + &exact_binding.session_id, + "worker-public-tool", + &unrequested, + "2026-08-11T00:00:03Z", + ) + .is_err() + ); + assert_eq!( + runtime_spine_backing_store(&store)?.pull_all()?, + before_unrequested + ); let content = "openapi: 3.1.0\n"; let content_sha256 = format!("{:x}", Sha256::digest(content.as_bytes())); let mut exact_receipt = EpiphanyToolInvocationReceipt::new( @@ -20104,7 +20274,7 @@ pub(crate) mod tests { "contentSha256": content_sha256, "byteCount": content.len(), "content": content, - "evidenceReceiptId": "eyes-source-intent-public-tool-exact" + "evidenceReceiptId": format!("eyes-source-{}", exact.intent_id) }) .to_string(), ); @@ -20116,7 +20286,7 @@ pub(crate) mod tests { assert_eq!(lookups.len(), 1); assert_eq!( lookups[0].receipt_id, - "eyes-source-intent-public-tool-exact" + format!("eyes-source-{}", exact.intent_id) ); assert_eq!(lookups[0].content_sha256, content_sha256); @@ -20150,43 +20320,16 @@ pub(crate) mod tests { .granted_operations .retain(|operation| operation != "publicSourceRead"); cache.put(grant_id, &grant)?; - let hostile = EpiphanyToolInvocationIntent::new( - "intent-public-tool-hostile", - "epiphany-tools", - "epiphany_public", - "github_file", - "{}", - "model-runtime", - "Attempt public read without operation authority.", - "2026-08-11T00:00:04Z", - ) - .with_model_call("call-public-tool-hostile", "request-public-tool"); let before = runtime_spine_backing_store(&store)?.pull_all()?; assert!( - put_runtime_tool_execution_intent( + put_runtime_requested_public_source_intents( &store, - "model-session-public-tool", - "model-job-public-tool", - &hostile, + "worker-public-tool", "2026-08-11T00:00:04Z", ) .is_err() ); assert_eq!(runtime_spine_backing_store(&store)?.pull_all()?, before); - let mut reloaded = runtime_spine_cache(&store)?; - reloaded.pull_all_backing_stores()?; - assert!( - reloaded - .get::(&hostile.intent_id)? - .is_none() - ); - assert!( - reloaded - .get::(&tool_invocation_intent_key( - &hostile.intent_id, - ))? - .is_none() - ); Ok(()) } diff --git a/epiphany-openai-runtime/src/bin/epiphany-openai-runtime.rs b/epiphany-openai-runtime/src/bin/epiphany-openai-runtime.rs index 8d52f1b2..583c2504 100644 --- a/epiphany-openai-runtime/src/bin/epiphany-openai-runtime.rs +++ b/epiphany-openai-runtime/src/bin/epiphany-openai-runtime.rs @@ -26,6 +26,7 @@ use epiphany_openai_runtime::EpiphanyOpenAiRuntimeOptions; use epiphany_openai_runtime::EpiphanyWorkerRuntimeOptions; use epiphany_openai_runtime::OPENAI_RUNTIME_ROLE; use epiphany_openai_runtime::assistant_text_from_model_events; +use epiphany_openai_runtime::append_requested_public_source_receipts; use epiphany_openai_runtime::build_tool_followup_model_request; use epiphany_openai_runtime::build_worker_model_request; use epiphany_openai_runtime::complete_worker_job_from_assistant_text; @@ -874,8 +875,36 @@ async fn run_worker_launch_with_tool_continuation( .clone() .context("run-worker --auto-tools requires --tool-adapter-bin")?; let launch_request = load_worker_launch_request(&options.store_path, &options.job_id)?; - let initial_request = + let mut initial_request = build_worker_model_request(&launch_request, &options.provider, &options.model)?; + let requested_public_source_intents = if launch_request + .repo_frontier_research_request_id + .is_some() + { + epiphany_core::put_runtime_requested_public_source_intents( + &options.store_path, + &options.job_id, + &now(), + )? + } else { + Vec::new() + }; + let mut requested_public_source_runs = Vec::new(); + for intent in &requested_public_source_intents { + requested_public_source_runs.push(run_tool_adapter( + &tool_adapter_bin, + &options.store_path, + options.mcp_config.as_ref(), + options.cwd.as_ref(), + options.resident_store.as_ref(), + &intent.intent_id, + )?); + } + append_requested_public_source_receipts( + &options.store_path, + &mut initial_request, + &requested_public_source_intents, + )?; let openai_options = EpiphanyOpenAiRuntimeOptions { store_path: options.store_path.clone(), codex_home: options.codex_home.clone(), @@ -994,6 +1023,7 @@ async fn run_worker_launch_with_tool_continuation( "nextSafeMove": worker_result.next_safe_move, "evidenceRefs": worker_result.evidence_refs, "artifactRefs": worker_result.artifact_refs, + "requestedPublicSourceRuns": requested_public_source_runs, "toolRounds": tool_rounds, })) } diff --git a/epiphany-openai-runtime/src/lib.rs b/epiphany-openai-runtime/src/lib.rs index 91f0859f..c17b24c8 100644 --- a/epiphany-openai-runtime/src/lib.rs +++ b/epiphany-openai-runtime/src/lib.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use std::path::Path; use std::path::PathBuf; @@ -320,6 +321,17 @@ pub async fn run_worker_launch( options: EpiphanyWorkerRuntimeOptions, ) -> Result { let launch_request = load_worker_launch_request(&options.store_path, &options.job_id)?; + if !epiphany_core::runtime_requested_public_source_refs_for_worker( + &options.store_path, + &options.job_id, + )? + .is_empty() + { + return Err(anyhow!( + "worker {:?} has request-owned public sources and must run through the governed automatic tool route", + options.job_id + )); + } let model_request = build_worker_model_request(&launch_request, &options.provider, &options.model)?; let openai_options = EpiphanyOpenAiRuntimeOptions { @@ -599,7 +611,7 @@ pub fn build_worker_model_request( if launch_request.binding_id == epiphany_core::EPIPHANY_VERIFICATION_ROLE_BINDING_ID { instructions.push_str("\n\nTool mandate: before returning `needs-evidence` because source files, artifact directories, command artifacts, commit diffs, Hands receipt bodies, or resident grant lifecycle are not inspectable, call the governed read-only tools available on this request. Use `mcp__epiphany_source__read_file` for cited source/artifact files, `mcp__epiphany_source__directory_inventory` for bounded workspace directory counts and bytes, `mcp__epiphany_source__git_show` for commit diffs, `mcp__epiphany_source__read_hands_receipt` for Hands patch/command/commit receipts, and `mcp__epiphany_state__resident_grant_lifecycle` for exact or bounded recent grant-owned lifecycle state. Directory totals are authoritative only when the tool reports `complete=true`. Grant launchability is authoritative only from the typed state projection, never artifact names or acknowledgement presence. If a tool fails, cite that failed tool result and the exact remaining blocker."); } else if launch_request.binding_id == epiphany_core::EPIPHANY_RESEARCH_ROLE_BINDING_ID { - instructions.push_str("\n\nTool mandate: Eyes must inspect current repository sources, immutable public GitHub source, or typed resident state before emitting evidence. Call the bounded governed tool appropriate to the claim. Public evidence uses `mcp__epiphany_public__github_file` with an exact 40-hex commit, repository, and path; put its exact sourceRef in filesInspected and its evidenceReceiptId in evidence, and preserve its contentSha256 in the finding. Never substitute a branch, tag, arbitrary URL, or model memory for immutable public evidence. Cite exact inspected paths/revisions or grant identities in filesInspected and evidence, and report a source gap if the required body cannot be observed. Directory totals are authoritative only when the inventory reports `complete=true`; grant launchability is authoritative only from the typed lifecycle projection."); + instructions.push_str("\n\nEvidence mandate: the runtime obtains every immutable public GitHub source named by the typed Research request before this model turn and supplies the exact tool calls and receipts in the input. Cite each requested sourceRef in filesInspected and its evidenceReceiptId in evidence, preserve its contentSha256 in the finding, and report a source gap if any supplied lookup failed. Use the remaining bounded tools only for additional repository or resident-state inspection appropriate to the claim. Never substitute a branch, tag, arbitrary URL, or model memory for immutable public evidence. Directory totals are authoritative only when the inventory reports `complete=true`; grant launchability is authoritative only from the typed lifecycle projection."); } else if launch_request.binding_id == epiphany_core::EPIPHANY_MODELING_ROLE_BINDING_ID { instructions.push_str("\n\nTool mandate: Modeling must inspect current repository sources or typed resident state before proposing repository anatomy. Call the bounded read-only file, directory-inventory, Git, or `mcp__epiphany_state__resident_grant_lifecycle` tool appropriate to the claim; cite exact inspected paths/revisions or grant identities in filesInspected and evidence, and emit regather-needed instead of inventing unobserved structure. Directory totals are authoritative only when the inventory reports `complete=true`; grant launchability is authoritative only from the typed lifecycle projection."); } @@ -626,9 +638,7 @@ pub fn build_worker_model_request( | epiphany_core::EPIPHANY_MODELING_ROLE_BINDING_ID | epiphany_core::EPIPHANY_VERIFICATION_ROLE_BINDING_ID ) { - request.tools = repository_source_tools( - launch_request.binding_id == epiphany_core::EPIPHANY_RESEARCH_ROLE_BINDING_ID, - ); + request.tools = repository_source_tools(); } Ok(request) } @@ -935,6 +945,111 @@ pub fn build_tool_followup_model_request( Ok(followup) } +pub fn append_requested_public_source_receipts( + store_path: impl AsRef, + request: &mut EpiphanyModelRequest, + intents: &[EpiphanyToolInvocationIntent], +) -> Result<()> { + let store_path = store_path.as_ref(); + let source_worker_job_id = request + .source_worker_job_id + .as_deref() + .ok_or_else(|| anyhow!("requested public source context has no source worker"))?; + let expected_sources = epiphany_core::runtime_requested_public_source_refs_for_worker( + store_path, + source_worker_job_id, + )? + .into_iter() + .collect::>(); + let mut observed_sources = BTreeSet::new(); + for intent in intents { + let arguments: serde_json::Value = serde_json::from_str(&intent.arguments_json) + .context("requested public source intent arguments are invalid")?; + let component = |name: &str| -> Result<&str> { + arguments + .get(name) + .and_then(serde_json::Value::as_str) + .ok_or_else(|| anyhow!("requested public source intent omitted {name:?}")) + }; + observed_sources.insert( + epiphany_core::ImmutableGithubSource::from_components( + component("owner")?, + component("repository")?, + component("revision")?, + component("path")?, + )? + .to_string(), + ); + } + if observed_sources != expected_sources || observed_sources.len() != intents.len() { + return Err(anyhow!( + "requested public source model context does not exactly cover its typed Research request" + )); + } + let mut cache = runtime_spine_cache(store_path)?; + cache.pull_all_backing_stores()?; + for intent in intents { + if intent.model_request_id.is_some() { + return Err(anyhow!( + "requested public source intent {:?} is model-owned", + intent.intent_id + )); + } + if intent.caller != "epiphany-runtime-requested-public-source" + || intent.server != "epiphany_public" + || intent.tool_name != "github_file" + { + return Err(anyhow!( + "requested public source intent {:?} is not the canonical public-source operation", + intent.intent_id + )); + } + let binding = + epiphany_core::require_runtime_tool_execution_binding(store_path, &intent.intent_id)?; + if binding.job_id != source_worker_job_id || binding.model_request_id.is_some() { + return Err(anyhow!( + "requested public source intent {:?} is not owned by worker {:?}", + intent.intent_id, + source_worker_job_id + )); + } + let call_id = intent + .call_id + .as_deref() + .ok_or_else(|| anyhow!("requested public source intent has no call id"))?; + let receipt = cache + .get::(&tool_invocation_receipt_key( + &intent.intent_id, + ))? + .ok_or_else(|| { + anyhow!( + "requested public source intent {:?} has no terminal receipt", + intent.intent_id + ) + })?; + if receipt.intent_id != intent.intent_id + || receipt.adapter != intent.adapter + || receipt.server != intent.server + || receipt.tool_name != intent.tool_name + { + return Err(anyhow!( + "requested public source intent {:?} has a foreign receipt", + intent.intent_id + )); + } + request.input.push(EpiphanyModelInputItem::ToolCall { + call_id: call_id.to_string(), + name: format!("mcp__{}__{}", intent.server, intent.tool_name), + arguments: intent.arguments_json.clone(), + }); + request.input.push(EpiphanyModelInputItem::ToolResult { + call_id: call_id.to_string(), + output: tool_receipt_output_for_model(intent, &receipt), + }); + } + Ok(()) +} + fn tool_receipt_output_for_model( intent: &EpiphanyToolInvocationIntent, receipt: &EpiphanyToolInvocationReceipt, @@ -1099,8 +1214,8 @@ pub fn openai_request_from_model_request( } } -fn repository_source_tools(include_public_source: bool) -> Vec { - let mut tools = vec![ +fn repository_source_tools() -> Vec { + vec![ EpiphanyModelToolDefinition { name: "mcp__epiphany_source__read_file".to_string(), description: "Read a bounded UTF-8 text slice from the current workspace for source-grounded Eyes, Modeling, or Soul work. Use only for repository sources and operator-safe artifacts in scope.".to_string(), @@ -1174,27 +1289,7 @@ fn repository_source_tools(include_public_source: bool) -> Vec EpiphanyModelInputItem { @@ -3608,16 +3703,14 @@ mod tests { research_launch.authority_scope = "epiphany.role.research".to_string(); let research_request = build_worker_model_request(&research_launch, DEFAULT_MODEL_PROVIDER, "gpt-5.4")?; - assert!( - research_request - .tools - .iter() - .any(|tool| tool.name == "mcp__epiphany_public__github_file") - ); + assert!(!research_request + .tools + .iter() + .any(|tool| tool.name == "mcp__epiphany_public__github_file")); assert!( research_request .instructions - .contains("exact 40-hex commit") + .contains("runtime obtains every immutable public GitHub source") ); Ok(()) }