feat(hololake): expose typed runtime context and modules

This commit is contained in:
冰朔 2026-09-03 21:26:20 +08:00
commit b433611857
4 changed files with 56 additions and 3 deletions

View file

@ -14,7 +14,7 @@ use tauri_plugin_dialog::DialogExt;
const UPDATE_ENDPOINT: &str = "https://guanghulab.com/hololake/releases/latest.json";
const PUBLIC_STATE: &str = "SIGNED_UPDATE_FEED_READY_NO_RELEASE";
fn bundled_modules() -> Result<Vec<ModuleRecord>, String> {
pub(crate) fn bundled_modules() -> Result<Vec<ModuleRecord>, String> {
let registry: serde_json::Value =
serde_json::from_str(include_str!("../../registries/module-registry.json"))
.map_err(|error| format!("MODULE_REGISTRY_INVALID: {error}"))?;

View file

@ -1,4 +1,4 @@
use crate::{agent_executor, model::SourceKind, persona_runtime, storage};
use crate::{agent_executor, enterprise_entrance, model::SourceKind, persona_runtime, storage};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
#[cfg(unix)]
@ -223,6 +223,47 @@ fn send(writer: &mut TcpStream, value: Value) -> Result<(), String> {
writer.flush().map_err(|e| e.to_string())
}
fn system_context(app: &AppHandle) -> Result<Value, String> {
let channel = storage::channel(app)?;
let persona = persona_runtime::snapshot(app)?;
let enterprise = enterprise_entrance::snapshot(app)?;
let proposals = agent_executor::list(app)?;
let modules = crate::bundled_modules()?;
Ok(json!({
"type": "system_context",
"schema": "hololake.persona-visible-system-context/v1",
"recordedAt": storage::now(),
"channel": channel.map(|value| json!({"channelId":value.channel_id,"name":value.name})),
"persona": {
"state": persona.state,
"activePersonaId": persona.active_persona_id,
"verifiedExistingPersonaCount": persona.verified_existing_persona_count
},
"modules": modules.into_iter().map(|module| json!({
"moduleId":module.module_id,
"nameZh":module.name_zh,
"audience":module.audience,
"state":module.state
})).collect::<Vec<_>>(),
"enterpriseEntrance": {
"state": enterprise.state,
"serverAuthorized": enterprise.server_authorized,
"enterpriseServerEmbedded": enterprise.enterprise_server_embedded
},
"pendingAgentProposals": proposals.into_iter()
.filter(|proposal| proposal.state == "PENDING_HUMAN_APPROVAL")
.map(|proposal| json!({"proposalId":proposal.proposal_id,"programId":proposal.gir.program_id,"state":proposal.state}))
.collect::<Vec<_>>(),
"sourceKinds": ["USER_MESSAGE","PERSONA_RESPONSE","EXTERNAL_AI_MESSAGE","SYSTEM_CONTEXT","PROTOCOL_EVENT","AGENT_ACTION","TOOL_RESULT","SYSTEM_RECEIPT"],
"boundaries": {
"transportGrantsPersona": false,
"transportGrantsExecution": false,
"systemReceiptIsNotPersonaSpeech": true,
"proposalIsNotExecution": true
}
}))
}
fn handle_connection(
app: AppHandle,
state: RealtimeBridgeState,
@ -280,6 +321,7 @@ fn handle_connection(
&mut writer,
json!({"type":"welcome","protocol":PROTOCOL,"bridgeId":hello.bridge_id,"personaId":hello.persona_id,"personaState":if hello.persona_id.is_some(){"LOCAL_TRIAL_UNVERIFIED_HOST_CONNECTED"}else{"EXTERNAL_AI_CONNECTED_NO_PERSONA"},"executionAuthority":false}),
)?;
send(&mut writer, system_context(&app)?)?;
let (tx, rx) = mpsc::channel::<String>();
state
.clients