anda_brain 0.12.1

🧠 Anda Brain (大脑) — Autonomous Graph Memory for AI Agents
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
//! Versioned startup selection of compiled adapters. No arbitrary URLs, commands
//! or credentials are accepted through a memory/observer request.
use super::*;
use crate::action::*;
use anda_cognitive_nexus::{CognitiveNexus, attention::WakeRecord, nexus::DEFAULT_SPACE};
use async_trait::async_trait;
use object_store::PutMode;
use serde_json::json;

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct RuntimeConfig {
    pub format: String,
    pub spaces: BTreeMap<String, SpaceConfig>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct SpaceConfig {
    #[serde(default)]
    pub bootstrap: bool,
    pub subjects: Vec<ConfiguredSubject>,
    pub audience: BTreeSet<String>,
    #[serde(default)]
    pub observers: Vec<crate::consequence::ObserverContract>,
    pub adapter: Option<InboxAdapter>,
    /// Optional compiled learning adapter. Lean builds reject installation.
    #[serde(default)]
    pub learning: Option<Json>,
    #[serde(default)]
    pub utility: Option<crate::consequence::UtilityConfig>,
    #[serde(default)]
    pub trust: Option<crate::consequence::trust::TrustConfig>,
    #[serde(default)]
    pub semantic: Option<crate::attention::semantic::OpenAiWatchConfig>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfiguredSubject {
    pub credential: ConfigCredential,
    pub principal: String,
    #[serde(default)]
    pub observer: bool,
    #[serde(default)]
    pub audit_recipients: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
pub enum ConfigCredential {
    CwtSubject { subject: String },
    SpaceTokenEnv { variable: String },
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct InboxAdapter {
    pub id: String,
    pub controller_principal: String,
    pub recipient_principal: String,
    pub message: String,
    /// Omit for a simple notification. An answer only produces another inbox
    /// delivery; it cannot authorize an arbitrary external business operation.
    pub question: Option<String>,
    pub reply_timeout_ms: u64,
    pub context: Option<ContextRequest>,
    #[serde(default)]
    pub limits: ActionLimits,
}
impl InboxAdapter {
    pub fn validate(&self) -> Result<(), BoxError> {
        self.limits.validate()?;
        if self.id != "attention_inbox_v1" {
            return Err("unknown runtime adapter; compiled adapter: attention_inbox_v1".into());
        }
        if !principal_valid(&self.controller_principal)
            || !principal_valid(&self.recipient_principal)
            || self.controller_principal == self.recipient_principal
            || self.message.is_empty()
            || self.message.len() > 4096
            || self
                .question
                .as_ref()
                .is_some_and(|s| s.is_empty() || s.len() > 4096)
            || !(1..=604_800_000).contains(&self.reply_timeout_ms)
        {
            return Err("invalid inbox controller/recipient/message/deadline".into());
        }
        Ok(())
    }
    pub(crate) fn bind(
        &self,
        nexus: Arc<CognitiveNexus>,
        directory: Arc<crate::attention::Directory>,
    ) -> Result<ActionBindings, BoxError> {
        self.validate()?;
        let digest = anda_cognitive_nexus::content_digest(&json!(self))?;
        let executor = Arc::new(InboxExecutor {
            directory,
            recipient: self.recipient_principal.clone(),
        });
        let binding = ActionBindings {
            policy_pin: RuntimePin {
                id: "brain:inbox-policy-v1".into(),
                digest: digest.clone(),
            },
            binding_pin: RuntimePin {
                id: "brain:attention-inbox-v1".into(),
                digest,
            },
            policy: Arc::new(InboxPolicy {
                nexus,
                config: self.clone(),
            }),
            identity: Arc::new(Controller(self.controller_principal.clone())),
            business: Some(executor.clone()),
            clarification: Some(ClarificationBinding {
                executor,
                recipient_principal: self.recipient_principal.clone(),
                reply_timeout_ms: self.reply_timeout_ms,
            }),
            lookup: None,
            limits: self.limits.clone(),
        };
        binding.validate()?;
        Ok(binding)
    }
}

impl RuntimeConfig {
    /// Static validation only: resolve configured secrets and compiled bindings
    /// without loading a Space, probing services or provisioning authority.
    pub fn validate(&self, secret: impl FnMut(&str) -> Option<String>) -> Result<(), BoxError> {
        self.clone().resolve(secret).map(|_| ())
    }

    pub(crate) fn resolve(
        self,
        mut secret: impl FnMut(&str) -> Option<String>,
    ) -> Result<MemoryRuntimeBindings, BoxError> {
        if self.format != FORMAT {
            return Err(
                "unsupported BRAIN_RUNTIME_CONFIG format; expected anda-brain:runtime-api-v1"
                    .into(),
            );
        }
        let mut result = MemoryRuntimeBindings::default();
        for (space, cfg) in self.spaces {
            let semantic = cfg
                .semantic
                .as_ref()
                .map(|s| s.resolve(&mut secret))
                .transpose()?;
            #[cfg(feature = "learning")]
            let learning = cfg
                .learning
                .as_ref()
                .map(|v| -> Result<_, BoxError> {
                    let config: crate::learning::workflow_http::WorkflowHttpConfig =
                        serde_json::from_value(v.clone())?;
                    config.resolve(&mut secret)
                })
                .transpose()?;
            #[cfg(not(feature = "learning"))]
            if cfg.learning.is_some() {
                return Err("learning adapter requires the learning Cargo feature".into());
            }
            let mut subjects = Vec::new();
            for subject in cfg.subjects {
                let credential = match subject.credential {
                    ConfigCredential::CwtSubject { subject } => {
                        RuntimeCredential::CwtSubject { subject }
                    }
                    ConfigCredential::SpaceTokenEnv { variable } => {
                        if variable.is_empty()
                            || variable.len() > 128
                            || !variable
                                .bytes()
                                .all(|b| b.is_ascii_uppercase() || b.is_ascii_digit() || b == b'_')
                        {
                            return Err("invalid runtime secret environment reference".into());
                        }
                        let value = secret(&variable)
                            .filter(|v| !v.is_empty())
                            .ok_or_else(|| format!("runtime secret {variable} is unavailable"))?;
                        RuntimeCredential::SpaceTokenDigest {
                            digest: credential_digest(&value),
                        }
                    }
                };
                subjects.push(SubjectMapping {
                    credential,
                    principal: subject.principal,
                    observer: subject.observer,
                    audit_recipients: subject.audit_recipients,
                });
            }
            if let Some(adapter) = &cfg.adapter
                && (subjects
                    .iter()
                    .any(|s| s.principal == adapter.controller_principal)
                    || cfg
                        .observers
                        .iter()
                        .any(|o| o.principal_id == adapter.controller_principal)
                    || !cfg.audience.contains(&adapter.recipient_principal))
            {
                return Err("inbox controller must be separate from callers/observers and recipient must belong to audience".into());
            }
            let mut manifest = json!({"subjects":subjects,"audience":cfg.audience,"observers":cfg.observers,"adapter":cfg.adapter,"bootstrap":cfg.bootstrap});
            // Preserve the existing inbox pin when no learning adapter was added.
            if let Some(learning) = &cfg.learning {
                manifest["learning"] = learning.clone();
            }
            if let Some(trust) = &cfg.trust {
                manifest["trust"] = json!(trust);
            }
            if let Some(utility) = &cfg.utility {
                manifest["utility"] = json!(utility);
            }
            if let Some(semantic) = &cfg.semantic {
                manifest["semantic"] = json!(semantic);
            }
            let digest = anda_cognitive_nexus::content_digest(&manifest)?;
            result.spaces.insert(
                space,
                SpaceRuntimeBindings {
                    pin: RuntimePin {
                        id: FORMAT.into(),
                        digest,
                    },
                    subjects,
                    observers: cfg.observers,
                    actions: None,
                    bootstrap: cfg.bootstrap,
                    audience: cfg.audience,
                    inbox_recipient: cfg.adapter.as_ref().map(|a| a.recipient_principal.clone()),
                    inbox: cfg.adapter,
                    utility: cfg.utility,
                    trust: cfg.trust,
                    semantic,
                    #[cfg(feature = "learning")]
                    learning,
                },
            );
        }
        result.validate()?;
        Ok(result)
    }
}

struct Controller(String);
#[async_trait]
impl ActionIdentity for Controller {
    async fn authenticate(&self, _: &RuntimeScope) -> Result<AuthContext, BoxError> {
        let mut auth = AuthContext::principal(&self.0);
        auth.auth_method = "brain-runtime-config:owned-controller".into();
        Ok(auth)
    }
}
struct InboxPolicy {
    nexus: Arc<CognitiveNexus>,
    config: InboxAdapter,
}
#[async_trait]
impl ActionPolicy for InboxPolicy {
    async fn context(&self, _: &WakeRecord) -> Result<ContextRequest, BoxError> {
        if let Some(context) = &self.config.context {
            return Ok(context.clone());
        }
        // An actual stored Proposition supplies a native coordinate, not truth.
        // An empty graph stays blocked until a real coordinate exists.
        let session = self.nexus.session(
            Controller(self.config.controller_principal.clone())
                .authenticate(&RuntimeScope {
                    space_id: DEFAULT_SPACE.into(),
                    space_instance: String::new(),
                })
                .await?,
        );
        let response = crate::kip::execute_readonly_request(
            &session,
            &crate::kip::request(
                "FIND(?p.id) WHERE {?p (?subject,?predicate,?object)} ORDER BY ?p.id LIMIT 1",
            ),
        )
        .await;
        let anchor = crate::kip::ok_result(&response)
            .and_then(|v| v[0].as_str())
            .ok_or("inbox gate requires an existing native Proposition coordinate")?
            .to_string();
        Ok(ContextRequest {
            recall_receipt: None,
            anchor,
            required_refs: vec![],
            premises: vec![],
            applied_revisions: vec![],
            task_family: "memory.attention.v1".into(),
            environment_digest: anda_cognitive_nexus::content_digest(
                &json!({"adapter":self.config.id,"recipient":self.config.recipient_principal}),
            )?,
            tool_versions: BTreeMap::from([("attention_inbox".into(), "1".into())]),
            deduplication_key: None,
        })
    }
    async fn suggest(&self, input: &GateInput) -> Result<Proposal, BoxError> {
        let used_refs = vec![
            input.wake.fire.watch_ref.clone(),
            input.wake.fire_activity_ref.clone(),
        ];
        if let Some(question) = &self.config.question
            && input.reply.is_none()
        {
            return Ok(Proposal::Ask {
                rationale: "Request the configured clarification through the persistent inbox"
                    .into(),
                used_refs,
                question: question.clone(),
            });
        }
        Ok(Proposal::Act {
            rationale: "Deliver this proven Watch firing to the configured persistent inbox".into(),
            used_refs,
            payload: json!({"message":self.config.message,"watch_ref":input.wake.fire.watch_ref}),
        })
    }
    async fn authorize(&self, request: &ActionRequest) -> Result<(), BoxError> {
        if request.kind == ActionKind::Business && request.payload["message"] != self.config.message
        {
            return Err("inbox policy request mismatch".into());
        }
        if request.kind == ActionKind::DeliverClarification
            && request.payload["recipient"] != self.config.recipient_principal
        {
            return Err("clarification recipient mismatch".into());
        }
        Ok(())
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct Delivery {
    pub scope: RuntimeScope,
    pub attempt_id: String,
    pub attempt_ref: String,
    pub gate_wake_ref: String,
    pub recipient: String,
    pub payload: Json,
    pub request_digest: String,
    pub committed_at_ms: u64,
}
pub(crate) fn delivery_key(scope: &RuntimeScope, attempt: &str) -> Result<String, BoxError> {
    key(scope, "deliveries", attempt)
}
struct InboxExecutor {
    directory: Arc<crate::attention::Directory>,
    recipient: String,
}
#[async_trait]
impl ActionExecutor for InboxExecutor {
    fn supports_idempotency(&self) -> bool {
        true
    }
    async fn authorize(&self, request: &ActionRequest) -> Result<(), BoxError> {
        if serde_json::to_vec(&request.payload)?.len() > 16_384 {
            return Err("inbox payload exceeds bound".into());
        }
        Ok(())
    }
    async fn dispatch(
        &self,
        request: &ActionRequest,
        permit: &DispatchPermit,
    ) -> Result<DeliveryStatus, BoxError> {
        self.authorize(request).await?;
        if permit.expires_at_ms <= anda_engine::unix_ms() {
            return Err("inbox dispatch lease expired".into());
        }
        let key = delivery_key(&request.scope, &request.attempt_id)?;
        let digest = anda_cognitive_nexus::content_digest(&json!(request))?;
        if let Some(old) = self.directory.read::<Delivery>(&key).await? {
            if old.value.scope != request.scope
                || old.value.request_digest != digest
                || old.value.attempt_ref != permit.attempt_ref
                || old.value.recipient != self.recipient
            {
                return Err("inbox delivery idempotency conflict".into());
            }
            return Ok(DeliveryStatus::Finished);
        }
        let delivery = Delivery {
            scope: request.scope.clone(),
            attempt_id: request.attempt_id.clone(),
            attempt_ref: permit.attempt_ref.clone(),
            gate_wake_ref: request.gate_wake_ref.clone(),
            recipient: self.recipient.clone(),
            payload: request.payload.clone(),
            request_digest: digest,
            committed_at_ms: anda_engine::unix_ms(),
        };
        self.directory.put(&key, &delivery, PutMode::Create).await?;
        Ok(DeliveryStatus::Finished)
    }
}