feat(mesh): '!ai' over mesh runs the assistant's shared tool loop

Mesh AssistQuery answered with a bare LLM call — no tools, no actions.
The CallerScope::Mesh variant was designed for this wiring ('the variant
exists so the shape is right when a future plan wires mesh callers into
the shared loop'); this is that plan. A trusted/allowlisted asker's prompt
now runs assistant::chat with CallerScope::Mesh { authorized } — the
operator's persisted grants cap what the model may touch (never wider),
and writes suspend on the node's own confirm gate. The reply is capped
for airtime as before, with a brevity instruction for mesh turns.

Wiring follows the blob_store pattern: RpcHandler::set_mesh_service (now
&Arc<Self>) forward-propagates an Arc<RpcHandler> into the mesh state's
new assistant_handler slot; absent (early boot) falls back to the legacy
bare-LLM answer.

Test: mesh_caller_authority_is_capped_at_operator_grants.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-07 16:54:12 -04:00
co-authored by Claude
parent 34085e30a2
commit 4361a5cba7
4 changed files with 81 additions and 5 deletions
+6 -1
View File
@@ -200,13 +200,18 @@ impl RpcHandler {
}
/// Set the mesh service (called after identity is loaded).
pub async fn set_mesh_service(&self, service: crate::mesh::MeshService) {
pub async fn set_mesh_service(self: &Arc<Self>, service: crate::mesh::MeshService) {
// If the blob store is already initialised, propagate it into the
// freshly-started mesh state so the listener can persist inline
// attachments. Mirrors `set_blob_store`'s forward-propagation.
if let Some(store) = self.blob_store.read().await.as_ref().cloned() {
*service.shared_state().blob_store.write().await = Some(store);
}
// Wire the mesh `!ai` path into the assistant's shared tool loop: a
// trusted mesh peer's question runs the same tool registry with the
// operator's persisted grants, and writes still suspend on this
// node's confirm gate. Same forward-propagation pattern as above.
*service.shared_state().assistant_handler.write().await = Some(Arc::clone(self));
*self.mesh_service.write().await = Some(service);
}