feat(hololake): complete public runtime loop

This commit is contained in:
冰朔 2026-09-03 19:44:19 +08:00
commit 92cce27973
26 changed files with 2626 additions and 79 deletions

View file

@ -1,9 +1,13 @@
mod agent_executor;
mod model;
mod persona_runtime;
mod realtime_bridge;
mod storage;
use model::*;
use serde::Serialize;
use std::{fs, path::Path};
use tauri::AppHandle;
use tauri::{AppHandle, Manager, State};
use tauri_plugin_dialog::DialogExt;
const UPDATE_ENDPOINT: &str = "https://guanghulab.com/hololake/releases/latest.json";
@ -76,7 +80,11 @@ fn create_channel(app: AppHandle, name: String) -> Result<Channel, String> {
}
#[tauri::command]
fn submit_user_message(app: AppHandle, content: String) -> Result<MutationReceipt, String> {
fn submit_user_message(
app: AppHandle,
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
content: String,
) -> Result<MutationReceipt, String> {
let content = content.trim();
if content.is_empty() {
return Err("EMPTY_USER_MESSAGE".into());
@ -86,16 +94,17 @@ fn submit_user_message(app: AppHandle, content: String) -> Result<MutationReceip
}
let e = storage::event(SourceKind::UserMessage, "用户语言", content, "RECORDED");
storage::append_event(&app, &e)?;
realtime.broadcast_value(serde_json::json!({"type":"user_message","event":e}));
let waiting = storage::event(
SourceKind::SystemReceipt,
"系统回执",
"用户语言已按 USER_MESSAGE 保存;当前未绑定人格模型,不伪造人格回应。",
"用户语言已按 USER_MESSAGE 保存并实时发送到已连接的外部 AI系统不伪造人格回应。",
"WAITING",
);
receipt(
&app,
"WAITING_PERSONA",
"用户语言已记录,等待人格体接入",
"用户语言已记录并投递到实时桥",
None,
waiting,
)
@ -328,7 +337,7 @@ fn register_external_ai_bridge(
display_name: name.into(),
inbox_path: inbox.to_string_lossy().into_owned(),
outbox_path: outbox.to_string_lossy().into_owned(),
state: "LOCAL_EXPRESSION_ONLY_NO_EXECUTION_AUTHORITY".into(),
state: "GLP_REALTIME_REGISTERED_NO_EXECUTION_AUTHORITY".into(),
};
let mut all = storage::bridges(&app)?;
all.push(bridge.clone());
@ -346,12 +355,166 @@ fn register_external_ai_bridge(
Ok(bridge)
}
fn ensure_default_external_ai_bridge(app: &AppHandle) -> Result<(), String> {
let mut bridges = storage::bridges(app)?;
if bridges
.iter()
.any(|bridge| bridge.bridge_id == "HLP-BRIDGE-LOCAL-DEFAULT")
{
return Ok(());
}
let root = storage::root(app)?
.join("external-ai")
.join("HLP-BRIDGE-LOCAL-DEFAULT");
let inbox = root.join("inbox");
let outbox = root.join("outbox");
fs::create_dir_all(&inbox).map_err(|e| e.to_string())?;
fs::create_dir_all(&outbox).map_err(|e| e.to_string())?;
bridges.push(ExternalAiBridge {
bridge_id: "HLP-BRIDGE-LOCAL-DEFAULT".into(),
display_name: "本机编程 AI".into(),
inbox_path: inbox.to_string_lossy().into_owned(),
outbox_path: outbox.to_string_lossy().into_owned(),
state: "GLP_REALTIME_REGISTERED_NO_EXECUTION_AUTHORITY".into(),
});
storage::save_bridges(app, &bridges)
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct RuntimeOverview {
realtime: realtime_bridge::RealtimeBridgeStatus,
persona: persona_runtime::PersonaRuntimeSnapshot,
proposals: Vec<agent_executor::AgentProposal>,
}
#[tauri::command]
fn get_runtime_overview(
app: AppHandle,
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
) -> Result<RuntimeOverview, String> {
Ok(RuntimeOverview {
realtime: realtime.status(),
persona: persona_runtime::snapshot(&app)?,
proposals: agent_executor::list(&app)?,
})
}
#[tauri::command]
fn get_realtime_invitation(
app: AppHandle,
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
) -> Result<realtime_bridge::RealtimeInvitation, String> {
realtime_bridge::invitation(&app, &realtime)
}
#[tauri::command]
fn register_public_persona(
app: AppHandle,
display_name: String,
) -> Result<persona_runtime::PublicPersona, String> {
persona_runtime::register(&app, display_name)
}
#[tauri::command]
fn delete_trial_persona(
app: AppHandle,
persona_id: String,
exact_confirmation: String,
) -> Result<persona_runtime::PersonaRuntimeSnapshot, String> {
persona_runtime::delete_trial(&app, &persona_id, &exact_confirmation)
}
#[tauri::command]
fn compile_tcs_agent_proposal(
app: AppHandle,
request: agent_executor::TcsCompileRequest,
) -> Result<agent_executor::AgentProposal, String> {
agent_executor::queue(&app, request, "HOLOLAKE_LOCAL_UI".into(), None)
}
#[tauri::command]
fn approve_agent_proposal(
app: AppHandle,
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
proposal_id: String,
) -> Result<agent_executor::AgentReceipt, String> {
let receipt = agent_executor::approve(&app, &proposal_id)?;
realtime.broadcast_value(serde_json::json!({"type":"agent_receipt","receipt":receipt}));
Ok(receipt)
}
#[tauri::command]
fn reject_agent_proposal(
app: AppHandle,
realtime: State<'_, realtime_bridge::RealtimeBridgeState>,
proposal_id: String,
) -> Result<agent_executor::AgentProposal, String> {
let proposal = agent_executor::reject(&app, &proposal_id)?;
realtime
.broadcast_value(serde_json::json!({"type":"proposal_rejected","proposalId":proposal_id}));
Ok(proposal)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_updater::Builder::new().build())
.setup(|app| {
let diagnostic = storage::root(app.handle())
.map_err(std::io::Error::other)?
.join("startup-runtime-diagnostic.json");
storage::write_json(
&diagnostic,
&serde_json::json!({"state":"SETUP_ENTERED","recordedAt":storage::now()}),
)
.map_err(std::io::Error::other)?;
if std::env::var_os("HOLOLAKE_RUNTIME_TEST_ROOT").is_some() {
if let Ok(channel_name) = std::env::var("HOLOLAKE_TEST_CHANNEL_NAME") {
create_channel(app.handle().clone(), channel_name)
.map_err(std::io::Error::other)?;
}
if let Ok(display_name) = std::env::var("HOLOLAKE_TEST_PERSONA_NAME") {
persona_runtime::register(app.handle(), display_name)
.map_err(std::io::Error::other)?;
}
}
ensure_default_external_ai_bridge(app.handle()).map_err(std::io::Error::other)?;
let state = match realtime_bridge::start(app.handle().clone()) {
Ok(state) => state,
Err(error) => {
let _ = storage::write_json(
&diagnostic,
&serde_json::json!({"state":"LISTENER_FAILED","error":error.clone(),"recordedAt":storage::now()}),
);
return Err(std::io::Error::other(error).into());
}
};
storage::write_json(
&diagnostic,
&serde_json::json!({"state":"LISTENER_BOUND","status":state.status(),"recordedAt":storage::now()}),
)
.map_err(std::io::Error::other)?;
let invitation =
realtime_bridge::invitation(app.handle(), &state).map_err(std::io::Error::other)?;
storage::write_json(
&storage::root(app.handle())
.map_err(std::io::Error::other)?
.join("startup-runtime-receipt.json"),
&serde_json::json!({
"schema":"hololake.startup-runtime-receipt/v1",
"state":"GLP_LISTENING",
"protocol":invitation.protocol,
"endpoint":invitation.endpoint,
"recordedAt":storage::now()
}),
)
.map_err(std::io::Error::other)?;
app.manage(state);
Ok(())
})
.invoke_handler(tauri::generate_handler![
system_snapshot,
create_channel,
@ -362,7 +525,14 @@ pub fn run() {
delete_knowledge_document,
import_knowledge_folder,
export_knowledge_document,
register_external_ai_bridge
register_external_ai_bridge,
get_runtime_overview,
get_realtime_invitation,
register_public_persona,
delete_trial_persona,
compile_tcs_agent_proposal,
approve_agent_proposal,
reject_agent_proposal
])
.run(tauri::generate_context!())
.expect("HoloLake runtime failed")