From 565e931be5d55d5b1845fc4d3dab2b8423964f2a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 21 Jun 2026 17:13:40 +0100 Subject: [PATCH] Add Weksa interlingua speech receipts --- .../bin/epiphany-weksa-interlingua-smoke.rs | 89 ++++ epiphany-core/src/lib.rs | 13 + epiphany-core/src/persona_turn.rs | 9 +- epiphany-core/src/weksa_interlingua.rs | 381 ++++++++++++++++++ state/ledgers.msgpack | Bin 379243 -> 379824 bytes 5 files changed, 491 insertions(+), 1 deletion(-) create mode 100644 epiphany-core/src/bin/epiphany-weksa-interlingua-smoke.rs create mode 100644 epiphany-core/src/weksa_interlingua.rs diff --git a/epiphany-core/src/bin/epiphany-weksa-interlingua-smoke.rs b/epiphany-core/src/bin/epiphany-weksa-interlingua-smoke.rs new file mode 100644 index 00000000..c7d9caa6 --- /dev/null +++ b/epiphany-core/src/bin/epiphany-weksa-interlingua-smoke.rs @@ -0,0 +1,89 @@ +use anyhow::Result; +use epiphany_core::WEKSA_INTERLINGUA_PACKET_SCHEMA_VERSION; +use epiphany_core::WEKSA_TARGET_LOWERING_RECEIPT_SCHEMA_VERSION; +use epiphany_core::WEKSA_TARGET_LOWERING_REQUEST_SCHEMA_VERSION; +use epiphany_core::WeksaInterlinguaInput; +use epiphany_core::WeksaSpeakerContext; +use epiphany_core::build_weksa_interlingua_packet; +use epiphany_core::build_weksa_lowering_prompt; +use epiphany_core::build_weksa_target_lowering_request; +use epiphany_core::record_weksa_target_lowering_receipt; + +fn main() -> Result<()> { + let packet = build_weksa_interlingua_packet(WeksaInterlinguaInput { + packet_id: "weksa-packet-persona-smoke".to_string(), + source_interpreter_ref: "persona-interpreter:smoke-say".to_string(), + source_speech_audit_ref: "persona-speech-audit:smoke-ok".to_string(), + speaker: WeksaSpeakerContext { + persona_id: "epiphany.Persona".to_string(), + display_name: "Epiphany".to_string(), + source_surface: "eve://epiphany/persona".to_string(), + source_language: "en".to_string(), + utterance_state_ref: "state/agents.msgpack#Persona:utterance-state".to_string(), + }, + meaning: + "Tell the room that Epiphany can keep working inside its branch-local body, while publication and merge still need Bifrost receipts." + .to_string(), + speech_act: "status-reply".to_string(), + delivery_register: "warm-technical".to_string(), + target_audience: "local public Persona room".to_string(), + safety_notes: vec![ + "Do not claim upstream merge authority.".to_string(), + "Do not expose private worker thought or private Verse payloads.".to_string(), + ], + })?; + assert_eq!( + packet.schema_version, + WEKSA_INTERLINGUA_PACKET_SCHEMA_VERSION + ); + assert!(!packet.private_state_exposed); + assert!(packet.meaning.contains("Bifrost receipts")); + assert!(packet.contract.contains("interlingua cargo")); + + let request = build_weksa_target_lowering_request( + "weksa-lower-persona-smoke", + packet, + "es", + "warm-technical", + "discord", + )?; + assert_eq!( + request.schema_version, + WEKSA_TARGET_LOWERING_REQUEST_SCHEMA_VERSION + ); + assert!(request.model_required); + assert!(!request.private_state_exposed); + + let prompt = build_weksa_lowering_prompt(&request); + assert_contains(&prompt, "You are Weksa"); + assert_contains(&prompt, "Preserve meaning"); + assert_contains(&prompt, "Return only target-language text"); + assert_contains(&prompt, "Do not claim upstream merge authority"); + + let receipt = record_weksa_target_lowering_receipt( + &request, + "weksa-lowering-receipt-persona-smoke", + "Epiphany puede seguir trabajando dentro de su cuerpo de rama local, mientras que la publicacion y la fusion aun necesitan recibos de Bifrost.", + "deterministic-smoke-lowering", + )?; + assert_eq!( + receipt.schema_version, + WEKSA_TARGET_LOWERING_RECEIPT_SCHEMA_VERSION + ); + assert!(receipt.lowered_text.contains("Bifrost")); + assert!(receipt.transport_authority.contains("must publish")); + assert!(!receipt.private_state_exposed); + + println!( + "status=ok weksaInterlingua=packet loweringRequest=model-required loweringReceipt=recorded targetLanguage={} transportAuthority=none privateStateExposed=false", + receipt.target_language + ); + Ok(()) +} + +fn assert_contains(haystack: &str, needle: &str) { + assert!( + haystack.contains(needle), + "expected Weksa smoke artifact to contain `{needle}`" + ); +} diff --git a/epiphany-core/src/lib.rs b/epiphany-core/src/lib.rs index ea93a07b..997818a8 100644 --- a/epiphany-core/src/lib.rs +++ b/epiphany-core/src/lib.rs @@ -23,6 +23,7 @@ mod state_update; mod substrate_gate; mod surfaces; mod thread_state_store; +mod weksa_interlingua; pub use agent_launch::EPIPHANY_IMAGINATION_OWNER_ROLE; pub use agent_launch::EPIPHANY_IMAGINATION_ROLE_BINDING_ID; @@ -916,3 +917,15 @@ pub use thread_state_store::thread_state_cache; pub use thread_state_store::validate_thread_state_entry; pub use thread_state_store::write_thread_state; pub use thread_state_store::write_thread_state_entry; +pub use weksa_interlingua::WEKSA_INTERLINGUA_PACKET_SCHEMA_VERSION; +pub use weksa_interlingua::WEKSA_TARGET_LOWERING_RECEIPT_SCHEMA_VERSION; +pub use weksa_interlingua::WEKSA_TARGET_LOWERING_REQUEST_SCHEMA_VERSION; +pub use weksa_interlingua::WeksaInterlinguaInput; +pub use weksa_interlingua::WeksaInterlinguaPacket; +pub use weksa_interlingua::WeksaSpeakerContext; +pub use weksa_interlingua::WeksaTargetLoweringReceipt; +pub use weksa_interlingua::WeksaTargetLoweringRequest; +pub use weksa_interlingua::build_weksa_interlingua_packet; +pub use weksa_interlingua::build_weksa_lowering_prompt; +pub use weksa_interlingua::build_weksa_target_lowering_request; +pub use weksa_interlingua::record_weksa_target_lowering_receipt; diff --git a/epiphany-core/src/persona_turn.rs b/epiphany-core/src/persona_turn.rs index f6be7ce3..c3bf3968 100644 --- a/epiphany-core/src/persona_turn.rs +++ b/epiphany-core/src/persona_turn.rs @@ -228,11 +228,18 @@ Hard boundary: Allowed effect vocabulary: - STATE NOTE: bounded Persona memory, mood, need, social read, bond, value, goal, or agency pressure. -- SAY: public text candidate for an allowed channel. +- SAY: public utterance meaning candidate for an allowed channel; this becomes Weksa interlingua before target-language lowering or transport. - DRAFT: private candidate artifact when posting is blocked or needs review. - ROUTE: non-public action such as keep private, ask Self, or propose a bounded repo investigation. - DROP: no durable effect. +For any SAY block, preserve meaning separately from transport phrasing when possible: +- meaning: what the Persona intends to communicate +- speechAct: reply, status, invitation, correction, thanks, refusal, or other public act +- register: public delivery feel such as warm-technical, concise, playful, careful +- targetAudience: room or peer context +- safetyNotes: anything Weksa must preserve while lowering into the target language + Allowed channel ids: {channels} diff --git a/epiphany-core/src/weksa_interlingua.rs b/epiphany-core/src/weksa_interlingua.rs new file mode 100644 index 00000000..7e3a3a56 --- /dev/null +++ b/epiphany-core/src/weksa_interlingua.rs @@ -0,0 +1,381 @@ +use anyhow::Result; +use anyhow::anyhow; +use serde::Deserialize; +use serde::Serialize; + +pub const WEKSA_INTERLINGUA_PACKET_SCHEMA_VERSION: &str = "weksa.interlingua_packet.v0"; +pub const WEKSA_TARGET_LOWERING_REQUEST_SCHEMA_VERSION: &str = + "weksa.target_language_lowering_request.v0"; +pub const WEKSA_TARGET_LOWERING_RECEIPT_SCHEMA_VERSION: &str = + "weksa.target_language_lowering_receipt.v0"; + +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct WeksaSpeakerContext { + pub persona_id: String, + pub display_name: String, + pub source_surface: String, + pub source_language: String, + #[serde(default)] + pub utterance_state_ref: String, +} + +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct WeksaInterlinguaInput { + pub packet_id: String, + pub source_interpreter_ref: String, + #[serde(default)] + pub source_speech_audit_ref: String, + pub speaker: WeksaSpeakerContext, + pub meaning: String, + #[serde(default)] + pub speech_act: String, + #[serde(default)] + pub delivery_register: String, + #[serde(default)] + pub target_audience: String, + #[serde(default)] + pub safety_notes: Vec, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct WeksaInterlinguaPacket { + pub schema_version: String, + pub packet_id: String, + pub source_interpreter_ref: String, + pub source_speech_audit_ref: String, + pub speaker: WeksaSpeakerContext, + pub meaning: String, + pub speech_act: String, + pub delivery_register: String, + pub target_audience: String, + pub safety_notes: Vec, + pub private_state_exposed: bool, + pub contract: String, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct WeksaTargetLoweringRequest { + pub schema_version: String, + pub request_id: String, + pub packet: WeksaInterlinguaPacket, + pub target_language: String, + pub target_register: String, + pub delivery_surface: String, + pub model_required: bool, + pub private_state_exposed: bool, + pub contract: String, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct WeksaTargetLoweringReceipt { + pub schema_version: String, + pub receipt_id: String, + pub request_id: String, + pub packet_id: String, + pub target_language: String, + pub target_register: String, + pub delivery_surface: String, + pub lowered_text: String, + pub lowering_method: String, + pub transport_authority: String, + pub private_state_exposed: bool, + pub contract: String, +} + +pub fn build_weksa_interlingua_packet( + input: WeksaInterlinguaInput, +) -> Result { + require_text(&input.packet_id, "packet id")?; + require_text(&input.source_interpreter_ref, "source interpreter ref")?; + require_text(&input.speaker.persona_id, "speaker persona id")?; + require_text(&input.speaker.display_name, "speaker display name")?; + require_text(&input.speaker.source_surface, "speaker source surface")?; + require_text(&input.speaker.source_language, "speaker source language")?; + require_text(&input.meaning, "meaning")?; + + Ok(WeksaInterlinguaPacket { + schema_version: WEKSA_INTERLINGUA_PACKET_SCHEMA_VERSION.to_string(), + packet_id: input.packet_id, + source_interpreter_ref: input.source_interpreter_ref, + source_speech_audit_ref: input.source_speech_audit_ref, + speaker: input.speaker, + meaning: compact_public_text(&input.meaning, 1200), + speech_act: default_text(&input.speech_act, "say"), + delivery_register: default_text(&input.delivery_register, "persona-public"), + target_audience: default_text(&input.target_audience, "public-room"), + safety_notes: input + .safety_notes + .into_iter() + .filter_map(|note| { + let note = compact_public_text(¬e, 240); + (!note.is_empty()).then_some(note) + }) + .collect(), + private_state_exposed: false, + contract: "Mind/Interpreter supplied reviewed public meaning; Weksa owns meaning-to-language lowering; Bifrost or a mouth transport owns publication. This packet is interlingua cargo, not an English-only Discord string, not audio, and not durable memory authority.".to_string(), + }) +} + +pub fn build_weksa_target_lowering_request( + request_id: impl Into, + packet: WeksaInterlinguaPacket, + target_language: impl Into, + target_register: impl Into, + delivery_surface: impl Into, +) -> Result { + if packet.private_state_exposed { + return Err(anyhow!( + "Weksa lowering request refuses private-state-exposed packet" + )); + } + let request_id = request_id.into(); + let target_language = target_language.into(); + let target_register = target_register.into(); + let delivery_surface = delivery_surface.into(); + require_text(&request_id, "request id")?; + require_text(&target_language, "target language")?; + require_text(&target_register, "target register")?; + require_text(&delivery_surface, "delivery surface")?; + + Ok(WeksaTargetLoweringRequest { + schema_version: WEKSA_TARGET_LOWERING_REQUEST_SCHEMA_VERSION.to_string(), + request_id, + packet, + target_language, + target_register, + delivery_surface, + model_required: true, + private_state_exposed: false, + contract: "Lower this interlingua packet into the target language/register. The model may choose phrasing, but must preserve meaning, speaker dignity, safety notes, and transport boundaries. Publication still requires Bifrost or mouth-edge receipts.".to_string(), + }) +} + +pub fn build_weksa_lowering_prompt(request: &WeksaTargetLoweringRequest) -> String { + format!( + r#" +You are Weksa, the meaning-to-language lowering organ. + +You do not own Persona thought, durable memory, Discord posting, Bifrost publication, or audio playback. + +Lower the interlingua packet into the requested language and register. + +Hard boundary: +- Preserve meaning; do not invent new commitments, facts, permissions, receipts, or state. +- Preserve the speaker's dignity and public presentation. +- Respect safety notes. +- Return only target-language text, suitable for the delivery surface. +- If the request cannot be lowered safely, return a concise refusal in the target language. + +Target: +- language: {language} +- register: {register} +- delivery surface: {surface} + +Speaker: +- persona id: {persona_id} +- display name: {display_name} +- source surface: {source_surface} +- source language: {source_language} +- utterance state ref: {utterance_state_ref} + +Speech act: {speech_act} +Audience: {audience} +Safety notes: +{safety_notes} + +Interlingua meaning: +{meaning} +"#, + schema = WEKSA_TARGET_LOWERING_REQUEST_SCHEMA_VERSION, + language = request.target_language, + register = request.target_register, + surface = request.delivery_surface, + persona_id = request.packet.speaker.persona_id, + display_name = request.packet.speaker.display_name, + source_surface = request.packet.speaker.source_surface, + source_language = request.packet.speaker.source_language, + utterance_state_ref = default_ref(&request.packet.speaker.utterance_state_ref), + speech_act = request.packet.speech_act, + audience = request.packet.target_audience, + safety_notes = render_safety_notes(&request.packet.safety_notes), + meaning = request.packet.meaning, + ) +} + +pub fn record_weksa_target_lowering_receipt( + request: &WeksaTargetLoweringRequest, + receipt_id: impl Into, + lowered_text: impl Into, + lowering_method: impl Into, +) -> Result { + if request.private_state_exposed || request.packet.private_state_exposed { + return Err(anyhow!( + "Weksa lowering receipt refuses private-state-exposed request" + )); + } + let receipt_id = receipt_id.into(); + let lowered_text = lowered_text.into(); + let lowering_method = lowering_method.into(); + require_text(&receipt_id, "receipt id")?; + require_text(&lowered_text, "lowered text")?; + require_text(&lowering_method, "lowering method")?; + + Ok(WeksaTargetLoweringReceipt { + schema_version: WEKSA_TARGET_LOWERING_RECEIPT_SCHEMA_VERSION.to_string(), + receipt_id, + request_id: request.request_id.clone(), + packet_id: request.packet.packet_id.clone(), + target_language: request.target_language.clone(), + target_register: request.target_register.clone(), + delivery_surface: request.delivery_surface.clone(), + lowered_text: compact_public_text(&lowered_text, 1600), + lowering_method, + transport_authority: "none; Bifrost or a configured mouth transport must publish".to_string(), + private_state_exposed: false, + contract: "This receipt proves Weksa lowered reviewed interlingua meaning into target text. It is not a post, merge, publication, memory admission, or audio playback receipt.".to_string(), + }) +} + +fn require_text(value: &str, label: &str) -> Result<()> { + if value.trim().is_empty() { + Err(anyhow!("Weksa {label} is required")) + } else { + Ok(()) + } +} + +fn default_text(value: &str, fallback: &str) -> String { + if value.trim().is_empty() { + fallback.to_string() + } else { + compact_public_text(value, 160) + } +} + +fn default_ref(value: &str) -> &str { + if value.trim().is_empty() { + "none" + } else { + value + } +} + +fn render_safety_notes(notes: &[String]) -> String { + if notes.is_empty() { + return "- none".to_string(); + } + notes + .iter() + .map(|note| format!("- {}", compact_public_text(note, 240))) + .collect::>() + .join("\n") +} + +fn compact_public_text(value: &str, max_len: usize) -> String { + let mut compacted = value.split_whitespace().collect::>().join(" "); + if compacted.len() > max_len { + let keep = max_len.saturating_sub(3); + compacted.truncate(keep); + compacted.push_str("..."); + } + compacted +} + +#[cfg(test)] +mod tests { + use super::*; + + fn input() -> WeksaInterlinguaInput { + WeksaInterlinguaInput { + packet_id: "weksa-packet-1".to_string(), + source_interpreter_ref: "interpreter:persona-turn-1".to_string(), + source_speech_audit_ref: "speech-audit:ok".to_string(), + speaker: WeksaSpeakerContext { + persona_id: "epiphany.Persona".to_string(), + display_name: "Epiphany".to_string(), + source_surface: "eve://epiphany/persona".to_string(), + source_language: "en".to_string(), + utterance_state_ref: "agent-utterance-state:persona".to_string(), + }, + meaning: + "Tell the room that Epiphany can keep working, but publication still needs receipts." + .to_string(), + speech_act: "status-reply".to_string(), + delivery_register: "warm-technical".to_string(), + target_audience: "project-public-room".to_string(), + safety_notes: vec![ + "Do not claim merge or deployment authority.".to_string(), + "Do not expose private worker thought.".to_string(), + ], + } + } + + #[test] + fn weksa_interlingua_packet_preserves_meaning_and_authority_boundary() -> Result<()> { + let packet = build_weksa_interlingua_packet(input())?; + assert_eq!( + packet.schema_version, + WEKSA_INTERLINGUA_PACKET_SCHEMA_VERSION + ); + assert_eq!(packet.speech_act, "status-reply"); + assert!(packet.meaning.contains("publication still needs receipts")); + assert!( + packet + .contract + .contains("Weksa owns meaning-to-language lowering") + ); + assert!(!packet.private_state_exposed); + Ok(()) + } + + #[test] + fn weksa_lowering_request_and_receipt_do_not_publish() -> Result<()> { + let packet = build_weksa_interlingua_packet(input())?; + let request = build_weksa_target_lowering_request( + "weksa-lower-1", + packet, + "es", + "warm-technical", + "discord", + )?; + assert!(request.model_required); + let prompt = build_weksa_lowering_prompt(&request); + assert!(prompt.contains("You are Weksa")); + assert!(prompt.contains("Return only target-language text")); + assert!(prompt.contains("Do not claim merge or deployment authority")); + + let receipt = record_weksa_target_lowering_receipt( + &request, + "weksa-lowering-receipt-1", + "Epiphany puede seguir trabajando, pero la publicacion aun necesita recibos.", + "model-lowering-smoke", + )?; + assert_eq!( + receipt.schema_version, + WEKSA_TARGET_LOWERING_RECEIPT_SCHEMA_VERSION + ); + assert!(receipt.transport_authority.contains("must publish")); + assert!(!receipt.private_state_exposed); + Ok(()) + } + + #[test] + fn weksa_refuses_empty_meaning_and_private_lowering() -> Result<()> { + let mut empty_meaning = input(); + empty_meaning.meaning = " ".to_string(); + assert!(build_weksa_interlingua_packet(empty_meaning).is_err()); + + let mut packet = build_weksa_interlingua_packet(input())?; + packet.private_state_exposed = true; + assert!( + build_weksa_target_lowering_request("weksa-lower-2", packet, "en", "plain", "discord") + .is_err() + ); + Ok(()) + } +} diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 19114e2ee72ab1da872501b8c1a56c705a8dd6ba..37cf890bbada02ed992ff5f52e71a200f6f0cc57 100644 GIT binary patch delta 580 zcmYk3v2GJV5Qe!UZlD8&&>)S3BBkJx_yjJ36ch=HbVP}Spqt!{eJh`LH?wmw7DWmQ zBqSPyb*~YL0_6o#@Bp+t09En?%-V_UV)u4$XaD*CZ~uK?J8##{A1C*_`Ob^yA1?27 zet-M&Y#N%KQ8j^yq9hFvb=lL?d^2)_Ubx)@@93f zEU5&k2>OPDfJHH<1n+1V5R4V#+SoyZfUN5Y^c}-n;@~X8;of_2jrca%NFSY57(gao z)t0Y$EQTcE83^)#B&#v14G3yCCRS-+I%z0K*z)W~K9-^_myZiF`ddAsfM*KCYH&no zGcYTpb$GLbS!gM#M5_w#bD_m7{H|Y%SLC+JwTf(x#8{@)M9Z zUJobPIKs}BsAXZ~h7_HgYVSEh$aZ1mN~*I6%&>A^w3;J~B}_A1vv<-L@=7UGH}D}e J+{c18{{yS^)9(NP delta 53 zcmdlmUHtVV@eLA;+y;hLMix=W7+6nuY*uD0WMu4Yp32ldm5C9EK{PWEvuvNr#Cq)* E0MzRdUjP6A