Skip to main content

active_rpc/
kernel.rs

1//! The kernel: one agent's capability table, promise graph, lanes, channels and
2//! dispatch loop.
3//!
4//! The kernel is a deterministic state machine. Nothing here does I/O, timing or
5//! threading: providers are ordinary objects, the clock is a counter, and the
6//! dispatch loop is driven by [`Agent::run`]. That makes capability semantics,
7//! attenuation and pipelining observable and reproducible in tests — and it
8//! means a transport is a thin adapter over the same machine (ADR-0002 in this
9//! crate).
10//!
11//! # Dispatch and round-trips
12//!
13//! A *batch* is every queued job for one provider, ordered so that a job whose
14//! promise dependencies are produced in the same batch runs after its producer.
15//! One batch is one provider round-trip. A client that waits for each result and
16//! then issues the next call produces one batch per call; a client that passes
17//! promises as arguments produces one batch for the whole chain. That difference
18//! is what the pipelining tests measure.
19
20use std::cell::RefCell;
21use std::collections::{BTreeMap, BTreeSet, VecDeque};
22use std::rc::Rc;
23
24use crate::budget::{Budget, BudgetMeter};
25use crate::cap::{CapAttrs, CapControl, CapHandle, CapId, CapRecord, ProviderId, ToolPolicy};
26use crate::channel::{Channel, ChannelId, Direction, Message};
27use crate::error::{Error, Result};
28use crate::lane::{Lane, LaneId};
29use crate::promise::{PromiseId, PromiseRecord, PromiseState};
30use crate::value::{Arg, Call, Response, Value};
31
32/// A provider of capabilities: a module, a tool, a peer.
33pub trait Provider {
34    /// Stable adapter name.
35    fn name(&self) -> &'static str;
36    /// Methods this provider answers (introspection).
37    fn methods(&self) -> Vec<String>;
38    /// Whether this provider answers `method`.
39    fn has_method(&self, method: &str) -> bool {
40        self.methods().iter().any(|name| name == method)
41    }
42    /// Whether `method` mutates provider state (refused on read-only caps).
43    fn mutating(&self, method: &str) -> bool;
44    /// Invoke a method on `object`.
45    fn invoke(&mut self, object: u64, call: &Call) -> Result<Response>;
46    /// Called when the last capability referencing `object` is dropped.
47    fn on_drop(&mut self, _object: u64, _cap: CapId) {}
48
49    /// Drain provider-side drop notifications recorded by [`Provider::on_drop`].
50    ///
51    /// The kernel owns providers, so tests and operators read the notifications
52    /// through [`Agent::take_drops`].
53    fn take_drop_notifications(&mut self) -> Vec<(u64, CapId)> {
54        Vec::new()
55    }
56}
57
58/// Observable kernel events. Tests and operators assert on these.
59#[derive(Debug, Clone, PartialEq)]
60pub enum Event {
61    /// A capability was minted.
62    CapMinted {
63        /// New capability.
64        cap: CapId,
65        /// Owning provider.
66        provider: ProviderId,
67    },
68    /// A capability handle gained a reference.
69    CapRetained(CapId),
70    /// A capability handle dropped a reference.
71    CapReleased(CapId),
72    /// The last reference to a capability went away; the provider was notified.
73    CapDropped(CapId),
74    /// A capability was revoked.
75    CapRevoked(CapId),
76    /// A lane was created.
77    LaneCreated(LaneId),
78    /// A lane was destroyed.
79    LaneDestroyed(LaneId),
80    /// A call was queued.
81    CallQueued {
82        /// Job id.
83        job: u64,
84        /// Issuing lane.
85        lane: LaneId,
86        /// Target capability.
87        cap: CapId,
88    },
89    /// A batch of jobs was dispatched to a provider (one round-trip).
90    BatchDispatched {
91        /// Provider.
92        provider: ProviderId,
93        /// Number of jobs in the batch.
94        jobs: usize,
95        /// Lanes represented in the batch.
96        lanes: BTreeSet<u64>,
97    },
98    /// A job completed.
99    JobCompleted {
100        /// Job id.
101        job: u64,
102        /// Issuing lane.
103        lane: LaneId,
104    },
105    /// A promise settled.
106    PromiseResolved(PromiseId),
107    /// A promise was rejected.
108    PromiseRejected {
109        /// Promise id.
110        promise: PromiseId,
111        /// Stable code.
112        code: String,
113    },
114    /// A message was sent on a channel.
115    ChannelSend {
116        /// Channel.
117        channel: ChannelId,
118        /// Sequence number.
119        sequence: u64,
120    },
121    /// A message was received from a channel.
122    ChannelRecv {
123        /// Channel.
124        channel: ChannelId,
125        /// Sequence number.
126        sequence: u64,
127    },
128    /// A capability was transferred through a channel.
129    CapTransferred {
130        /// Capability.
131        cap: CapId,
132        /// Channel.
133        channel: ChannelId,
134    },
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138enum JobState {
139    Queued,
140    Done,
141}
142
143#[derive(Debug, Clone)]
144struct Job {
145    #[allow(dead_code)]
146    id: u64,
147    lane: LaneId,
148    cap: CapId,
149    call: Call,
150    deps: Vec<PromiseId>,
151    promise: PromiseId,
152    state: JobState,
153}
154
155pub struct Kernel {
156    secret: [u8; 32],
157    providers: BTreeMap<ProviderId, Box<dyn Provider>>,
158    caps: BTreeMap<CapId, CapRecord>,
159    meters: BTreeMap<CapId, BudgetMeter>,
160    promises: BTreeMap<PromiseId, PromiseRecord>,
161    lanes: BTreeMap<LaneId, Lane>,
162    channels: BTreeMap<ChannelId, Channel>,
163    jobs: BTreeMap<u64, Job>,
164    queue: VecDeque<u64>,
165    events: Vec<Event>,
166    round_trips: u64,
167    next_promise: u64,
168    next_job: u64,
169    next_lane: u64,
170    next_channel: u64,
171    next_serial: u64,
172    next_provider: u64,
173    now_ms: u64,
174    latency_ms: u64,
175    transfers: u64,
176}
177
178/// Control half of a capability handle: reference counting and provider
179/// notification. Keeping it separate from the kernel cell lets handles hold an
180/// `Rc<dyn CapControl>` without exposing the kernel itself.
181pub(crate) struct HandleControl {
182    cell: KernelCell,
183}
184
185impl HandleControl {
186    fn new(cell: KernelCell) -> Self {
187        Self { cell }
188    }
189}
190
191impl CapControl for HandleControl {
192    fn retain(&self, id: CapId) {
193        if let Ok(mut kernel) = self.cell.try_borrow_mut() {
194            if let Some(record) = kernel.caps.get_mut(&id) {
195                record.refs = record.refs.saturating_add(1);
196                kernel.events.push(Event::CapRetained(id));
197            }
198        }
199    }
200
201    fn release(&self, id: CapId) {
202        let dropped = {
203            let Ok(mut kernel) = self.cell.try_borrow_mut() else {
204                return;
205            };
206            let Some(record) = kernel.caps.get_mut(&id) else {
207                return;
208            };
209            record.refs = record.refs.saturating_sub(1);
210            let last = record.refs == 0;
211            kernel.events.push(Event::CapReleased(id));
212            last
213        };
214        if !dropped {
215            return;
216        }
217        let mut kernel = self.cell.borrow_mut();
218        let Some(record) = kernel.caps.remove(&id) else {
219            return;
220        };
221        kernel.meters.remove(&id);
222        kernel.events.push(Event::CapDropped(id));
223        if let Some(provider) = kernel.providers.get_mut(&record.provider) {
224            provider.on_drop(record.object, id);
225        }
226    }
227}
228
229/// Shared kernel cell.
230pub type KernelCell = Rc<RefCell<Kernel>>;
231
232/// One agent: an owner of lanes, channels, capabilities and promises.
233#[derive(Clone)]
234pub struct Agent {
235    cell: KernelCell,
236    control: Rc<dyn CapControl>,
237}
238
239impl Default for Agent {
240    fn default() -> Self {
241        Self::new()
242    }
243}
244
245impl Agent {
246    /// Create an agent kernel with an unpredictable capability secret.
247    pub fn new() -> Self {
248        let mut seed = [0u8; 32];
249        let nanos = std::time::SystemTime::now()
250            .duration_since(std::time::UNIX_EPOCH)
251            .map(|duration| duration.as_nanos())
252            .unwrap_or(0);
253        let counter = NEXT_AGENT.with(|value| {
254            let mut value = value.borrow_mut();
255            *value += 1;
256            *value
257        });
258        let mut hasher = blake3::Hasher::new();
259        hasher.update(b"active-rpc/agent");
260        hasher.update(&nanos.to_le_bytes());
261        hasher.update(&counter.to_le_bytes());
262        seed.copy_from_slice(&hasher.finalize().as_bytes()[..32]);
263        Self::with_secret(seed)
264    }
265
266    /// Create an agent with a fixed secret (tests, deterministic replays).
267    pub fn with_secret(secret: [u8; 32]) -> Self {
268        let kernel = Kernel {
269            secret,
270            providers: BTreeMap::new(),
271            caps: BTreeMap::new(),
272            meters: BTreeMap::new(),
273            promises: BTreeMap::new(),
274            lanes: BTreeMap::new(),
275            channels: BTreeMap::new(),
276            jobs: BTreeMap::new(),
277            queue: VecDeque::new(),
278            events: Vec::new(),
279            round_trips: 0,
280            next_promise: 0,
281            next_job: 0,
282            next_lane: 0,
283            next_channel: 0,
284            next_serial: 0,
285            next_provider: 0,
286            now_ms: 0,
287            latency_ms: 1,
288            transfers: 0,
289        };
290        let cell: KernelCell = Rc::new(RefCell::new(kernel));
291        let control: Rc<dyn CapControl> = Rc::new(HandleControl::new(Rc::clone(&cell)));
292        Agent { cell, control }
293    }
294
295    /// The shared cell, for transports and adapters that need direct access.
296    pub fn cell(&self) -> &KernelCell {
297        &self.cell
298    }
299
300    /// Register a provider and return its id.
301    pub fn register(&self, provider: Box<dyn Provider>) -> ProviderId {
302        let mut kernel = self.cell.borrow_mut();
303        let id = ProviderId(kernel.next_provider);
304        kernel.next_provider += 1;
305        kernel.providers.insert(id, provider);
306        id
307    }
308
309    /// Configure the simulated provider latency (milliseconds per batch).
310    pub fn set_latency(&self, latency_ms: u64) {
311        self.cell.borrow_mut().latency_ms = latency_ms;
312    }
313
314    /// Mint a first-generation capability.
315    pub fn mint(&self, provider: ProviderId, object: u64, attrs: CapAttrs) -> Result<CapHandle> {
316        attrs.budget.check()?;
317        if attrs.methods.is_empty() {
318            return Err(Error::InvalidArgument("capability grants no methods"));
319        }
320        let mut kernel = self.cell.borrow_mut();
321        if !kernel.providers.contains_key(&provider) {
322            return Err(Error::InvalidArgument("unknown provider"));
323        }
324        let serial = kernel.next_serial;
325        kernel.next_serial += 1;
326        let id = CapId::derive(&kernel.secret, serial, None);
327        let record = CapRecord::new(id, provider, object, attrs.clone());
328        kernel.meters.insert(id, BudgetMeter::new(attrs.budget));
329        kernel.caps.insert(id, record);
330        kernel.events.push(Event::CapMinted { cap: id, provider });
331        Ok(CapHandle::new(Rc::clone(&self.control), id))
332    }
333
334    /// Attenuate a capability. Refuses any widening.
335    pub fn attenuate(&self, parent: &CapHandle, attrs: CapAttrs) -> Result<CapHandle> {
336        let parent_id = parent.id();
337        let mut kernel = self.cell.borrow_mut();
338        let record = kernel
339            .caps
340            .get(&parent_id)
341            .ok_or(Error::UnknownCapability(u128::from_be_bytes(pad128(
342                parent_id,
343            ))))?
344            .clone();
345        if record.revoked {
346            return Err(Error::Revoked(u128::from_be_bytes(pad128(parent_id))));
347        }
348        let attrs = record.attrs.attenuate(attrs)?;
349        let serial = kernel.next_serial;
350        kernel.next_serial += 1;
351        let id = CapId::derive(&kernel.secret, serial, Some(&parent_id));
352        let mut child = CapRecord::new(id, record.provider, record.object, attrs.clone());
353        child.parent = Some(parent_id);
354        kernel.meters.insert(id, BudgetMeter::new(attrs.budget));
355        kernel.caps.insert(id, child);
356        kernel.events.push(Event::CapMinted {
357            cap: id,
358            provider: record.provider,
359        });
360        Ok(CapHandle::new(Rc::clone(&self.control), id))
361    }
362
363    /// Import a capability id without owning a reference.
364    ///
365    /// This is how a peer handle is reconstructed from the wire: the id must
366    /// already exist in this kernel, otherwise the first call fails with
367    /// `rpc/unknown-capability`. Importing never proves authority; the registry
368    /// does.
369    pub fn import(&self, id: CapId) -> CapHandle {
370        CapHandle::borrowed(Rc::clone(&self.control), id)
371    }
372
373    /// Revoke a capability and every capability derived from it.
374    pub fn revoke(&self, cap: &CapHandle) {
375        let mut kernel = self.cell.borrow_mut();
376        let mut doomed = BTreeSet::new();
377        let mut stack = vec![cap.id()];
378        while let Some(id) = stack.pop() {
379            if !doomed.insert(id) {
380                continue;
381            }
382            for (child, record) in &kernel.caps {
383                if record.parent == Some(id) {
384                    stack.push(*child);
385                }
386            }
387        }
388        for id in doomed {
389            if let Some(record) = kernel.caps.get_mut(&id) {
390                record.revoked = true;
391            }
392            kernel.events.push(Event::CapRevoked(id));
393        }
394    }
395
396    /// Create a lane.
397    pub fn create_lane(&self, name: impl Into<String>, budget: Budget) -> Result<LaneId> {
398        budget.check()?;
399        let mut kernel = self.cell.borrow_mut();
400        let id = LaneId(kernel.next_lane);
401        kernel.next_lane += 1;
402        kernel.lanes.insert(id, Lane::new(id, name, budget));
403        kernel.events.push(Event::LaneCreated(id));
404        Ok(id)
405    }
406
407    /// Attenuate a lane's budget.
408    pub fn attenuate_lane(&self, lane: LaneId, budget: Budget) -> Result<()> {
409        let mut kernel = self.cell.borrow_mut();
410        let record = kernel
411            .lanes
412            .get_mut(&lane)
413            .ok_or(Error::UnknownLane(lane.0))?;
414        record.attenuate(budget)
415    }
416
417    /// Destroy a lane and drop its queued work.
418    pub fn destroy_lane(&self, lane: LaneId) -> Result<()> {
419        let mut kernel = self.cell.borrow_mut();
420        let record = kernel
421            .lanes
422            .get_mut(&lane)
423            .ok_or(Error::UnknownLane(lane.0))?;
424        record.destroy();
425        kernel.events.push(Event::LaneDestroyed(lane));
426        Ok(())
427    }
428
429    /// Create a channel.
430    pub fn create_channel(
431        &self,
432        name: impl Into<String>,
433        direction: Direction,
434    ) -> Result<ChannelId> {
435        let mut kernel = self.cell.borrow_mut();
436        let id = ChannelId(kernel.next_channel);
437        kernel.next_channel += 1;
438        kernel
439            .channels
440            .insert(id, Channel::new(id, name, direction));
441        Ok(id)
442    }
443
444    /// Subscribe a lane to a channel.
445    pub fn subscribe(&self, lane: LaneId, channel: ChannelId) -> Result<()> {
446        let mut kernel = self.cell.borrow_mut();
447        if !kernel.lanes.contains_key(&lane) {
448            return Err(Error::UnknownLane(lane.0));
449        }
450        let record = kernel
451            .channels
452            .get_mut(&channel)
453            .ok_or(Error::UnknownChannel(channel.0))?;
454        record.subscribers.insert(lane.0);
455        if let Some(lane) = kernel.lanes.get_mut(&lane) {
456            lane.channels.insert(channel.0);
457        }
458        Ok(())
459    }
460
461    /// Send a message, transferring capabilities with it.
462    pub fn send(
463        &self,
464        lane: LaneId,
465        channel: ChannelId,
466        payload: Value,
467        caps: &[CapHandle],
468    ) -> Result<u64> {
469        let mut kernel = self.cell.borrow_mut();
470        if !kernel.lanes.contains_key(&lane) {
471            return Err(Error::UnknownLane(lane.0));
472        }
473        let mut cap_ids = Vec::with_capacity(caps.len());
474        for cap in caps {
475            let record = kernel
476                .caps
477                .get(&cap.id())
478                .ok_or(Error::InvalidArgument("unknown capability"))?;
479            if record.revoked {
480                return Err(Error::Revoked(u128::from_be_bytes(pad128(cap.id()))));
481            }
482            // A transfer moves the capability: the sender's reference is
483            // consumed by the message, which keeps the capability alive for
484            // the receiver.
485            cap_ids.push(cap.id());
486        }
487        let record = kernel
488            .channels
489            .get_mut(&channel)
490            .ok_or(Error::UnknownChannel(channel.0))?;
491        let message = record.send(lane, payload, cap_ids.clone())?;
492        kernel.events.push(Event::ChannelSend {
493            channel,
494            sequence: message.sequence,
495        });
496        for id in cap_ids {
497            if let Some(record) = kernel.caps.get_mut(&id) {
498                record.refs = record.refs.saturating_add(1);
499            }
500            kernel.transfers += 1;
501            kernel
502                .events
503                .push(Event::CapTransferred { cap: id, channel });
504        }
505        Ok(message.sequence)
506    }
507
508    /// Receive the next message on a channel.
509    pub fn recv(&self, channel: ChannelId) -> Result<Option<Message>> {
510        let mut kernel = self.cell.borrow_mut();
511        let record = kernel
512            .channels
513            .get_mut(&channel)
514            .ok_or(Error::UnknownChannel(channel.0))?;
515        let message = record.recv()?;
516        if let Some(message) = &message {
517            kernel.events.push(Event::ChannelRecv {
518                channel,
519                sequence: message.sequence,
520            });
521        }
522        Ok(message)
523    }
524
525    /// Issue a call. The returned promise is pending until the kernel runs.
526    pub fn call(
527        &self,
528        lane: LaneId,
529        cap: &CapHandle,
530        method: &str,
531        args: Vec<Arg>,
532    ) -> Result<PromiseHandle> {
533        let mut kernel = self.cell.borrow_mut();
534        let cap_id = cap.id();
535        let record = kernel
536            .caps
537            .get(&cap_id)
538            .ok_or(Error::UnknownCapability(u128::from_be_bytes(pad128(
539                cap_id,
540            ))))?
541            .clone();
542        if record.revoked {
543            return Err(Error::Revoked(u128::from_be_bytes(pad128(cap_id))));
544        }
545        let (lane_closed, lane_capacity, lane_budget) = {
546            let lane_record = kernel.lanes.get(&lane).ok_or(Error::UnknownLane(lane.0))?;
547            (
548                lane_record.closed,
549                lane_record.has_capacity(),
550                lane_record.budget,
551            )
552        };
553        if lane_closed {
554            return Err(Error::UnknownLane(lane.0));
555        }
556        if !lane_capacity {
557            return Err(Error::BudgetExceeded("lane call"));
558        }
559        if !record.attrs.grants(method) {
560            return Err(Error::MethodNotGranted {
561                method: method.to_string(),
562            });
563        }
564        let provider = kernel
565            .providers
566            .get(&record.provider)
567            .ok_or(Error::InvalidArgument("provider disappeared"))?;
568        if !provider.has_method(method) {
569            return Err(Error::MethodNotGranted {
570                method: method.to_string(),
571            });
572        }
573        if record.attrs.read_only && provider.mutating(method) {
574            return Err(Error::ReadOnly);
575        }
576        // Path scope is enforced here, before the provider sees the call.
577        if let Some(scope) = &record.attrs.path_scope {
578            for arg in &args {
579                if let Arg::Value(Value::Path(path)) = arg {
580                    if !crate::cap::is_path_prefix(scope, path) {
581                        return Err(Error::PathNotGranted(path.clone()));
582                    }
583                }
584            }
585        }
586        // Tool policy is enforced here too.
587        if let Some(policy) = &record.attrs.tool_policy {
588            if !policy.allowed.contains(method) {
589                return Err(Error::ToolPolicy("tool not allowed"));
590            }
591            let bytes: u64 = args
592                .iter()
593                .map(|arg| match arg {
594                    Arg::Value(Value::Text(text)) => text.len() as u64,
595                    Arg::Value(Value::Module(bytes)) => bytes.len() as u64,
596                    _ => 0,
597                })
598                .sum();
599            if bytes > policy.max_arg_bytes {
600                return Err(Error::ToolPolicy("argument too large"));
601            }
602        }
603        let meter = kernel
604            .meters
605            .get_mut(&cap_id)
606            .ok_or(Error::InvalidArgument("capability has no meter"))?;
607        meter.charge(0)?;
608        let effective = record.attrs.budget.intersect(&lane_budget);
609
610        let promise_id = PromiseId(kernel.next_promise);
611        kernel.next_promise += 1;
612        kernel
613            .promises
614            .insert(promise_id, PromiseRecord::new(promise_id, lane.0));
615        let job_id = kernel.next_job;
616        kernel.next_job += 1;
617        let deps: Vec<PromiseId> = args.iter().filter_map(|arg| arg.promise()).collect();
618        for dep in &deps {
619            if !kernel.promises.contains_key(dep) {
620                return Err(Error::UnknownPromise(dep.0));
621            }
622            if let Some(record) = kernel.promises.get_mut(dep) {
623                record.waiters += 1;
624            }
625        }
626        let call = Call {
627            method: method.to_string(),
628            args,
629            path_scope: record.attrs.path_scope.clone(),
630            redact: record.attrs.redact,
631            read_only: record.attrs.read_only,
632            tool_allows_process: record
633                .attrs
634                .tool_policy
635                .as_ref()
636                .map(|policy| policy.allow_process)
637                .unwrap_or(false),
638            budget: effective,
639        };
640        let job = Job {
641            id: job_id,
642            lane,
643            cap: cap_id,
644            call,
645            deps,
646            promise: promise_id,
647            state: JobState::Queued,
648        };
649        kernel.jobs.insert(job_id, job);
650        kernel.queue.push_back(job_id);
651        if let Some(lane_record) = kernel.lanes.get_mut(&lane) {
652            lane_record.pending += 1;
653        }
654        kernel.events.push(Event::CallQueued {
655            job: job_id,
656            lane,
657            cap: cap_id,
658        });
659        // Budgets are enforced again at dispatch with the effective grant.
660        let _ = effective;
661        Ok(PromiseHandle {
662            agent: self.clone(),
663            id: promise_id,
664        })
665    }
666
667    /// Run until no job is queued. Returns the number of provider round-trips.
668    pub fn run(&self) -> u64 {
669        loop {
670            let batch = {
671                let mut kernel = self.cell.borrow_mut();
672                if kernel.queue.is_empty() {
673                    return kernel.round_trips;
674                }
675                match kernel.next_batch() {
676                    Ok(batch) => batch,
677                    Err(_) => {
678                        // Unorderable jobs (a pipelined cycle): fail them all.
679                        let jobs: Vec<u64> = kernel.queue.drain(..).collect();
680                        for job_id in jobs {
681                            kernel.fail_job(job_id, "rpc/pipelined-cycle");
682                        }
683                        return kernel.round_trips;
684                    }
685                }
686            };
687            self.dispatch(batch);
688        }
689    }
690
691    /// Round-trips observed so far.
692    pub fn round_trips(&self) -> u64 {
693        self.cell.borrow().round_trips
694    }
695
696    /// Await a promise, running the kernel until it settles.
697    pub fn await_promise(&self, promise: &PromiseHandle) -> Result<Value> {
698        loop {
699            {
700                let kernel = self.cell.borrow();
701                let record = kernel
702                    .promises
703                    .get(&promise.id)
704                    .ok_or(Error::UnknownPromise(promise.id.0))?;
705                if record.state.is_settled() {
706                    return record.value();
707                }
708            }
709            if self.cell.borrow().queue.is_empty() {
710                return Err(Error::UnresolvablePromise(promise.id.0));
711            }
712            self.run();
713        }
714    }
715
716    /// Resolve a pending promise manually (used by peers that answer out of band).
717    pub fn resolve(&self, promise: &PromiseHandle, value: Value) -> Result<()> {
718        let mut kernel = self.cell.borrow_mut();
719        let record = kernel
720            .promises
721            .get_mut(&promise.id)
722            .ok_or(Error::UnknownPromise(promise.id.0))?;
723        record.resolve(value)?;
724        kernel.events.push(Event::PromiseResolved(promise.id));
725        Ok(())
726    }
727
728    /// Cancel a promise.
729    pub fn cancel(&self, promise: &PromiseHandle) -> Result<()> {
730        let mut kernel = self.cell.borrow_mut();
731        let record = kernel
732            .promises
733            .get_mut(&promise.id)
734            .ok_or(Error::UnknownPromise(promise.id.0))?;
735        record.cancel()
736    }
737
738    /// Drain drop notifications recorded by one provider.
739    pub fn take_drops(&self, provider: ProviderId) -> Vec<(u64, CapId)> {
740        self.cell
741            .borrow_mut()
742            .providers
743            .get_mut(&provider)
744            .map(|provider| provider.take_drop_notifications())
745            .unwrap_or_default()
746    }
747
748    /// The event log.
749    pub fn events(&self) -> Vec<Event> {
750        self.cell.borrow().events.clone()
751    }
752
753    /// Lane statistics.
754    pub fn lane(&self, lane: LaneId) -> Result<Lane> {
755        self.cell
756            .borrow()
757            .lanes
758            .get(&lane)
759            .cloned()
760            .ok_or(Error::UnknownLane(lane.0))
761    }
762
763    /// Channel statistics.
764    pub fn channel(&self, channel: ChannelId) -> Result<Channel> {
765        self.cell
766            .borrow()
767            .channels
768            .get(&channel)
769            .cloned()
770            .ok_or(Error::UnknownChannel(channel.0))
771    }
772
773    /// Capability attributes, for tests and policy tooling.
774    pub fn cap_attrs(&self, cap: &CapHandle) -> Result<CapAttrs> {
775        self.cell
776            .borrow()
777            .caps
778            .get(&cap.id())
779            .map(|record| record.attrs.clone())
780            .ok_or(Error::UnknownCapability(u128::from_be_bytes(pad128(
781                cap.id(),
782            ))))
783    }
784
785    fn dispatch(&self, batch: Vec<u64>) {
786        let mut kernel = self.cell.borrow_mut();
787        kernel.round_trips += 1;
788        kernel.now_ms = kernel.now_ms.saturating_add(kernel.latency_ms);
789        let Some(first) = batch.first().and_then(|id| kernel.jobs.get(id)) else {
790            return;
791        };
792        let provider_id = kernel
793            .caps
794            .get(&first.cap)
795            .map(|record| record.provider)
796            .unwrap_or(ProviderId(u64::MAX));
797        let lanes: BTreeSet<u64> = batch
798            .iter()
799            .filter_map(|id| kernel.jobs.get(id).map(|job| job.lane.0))
800            .collect();
801        kernel.events.push(Event::BatchDispatched {
802            provider: provider_id,
803            jobs: batch.len(),
804            lanes,
805        });
806        // Take the provider out so the borrow of the kernel ends before
807        // invoking user code.
808        let Some(mut provider) = kernel.providers.remove(&provider_id) else {
809            for job_id in batch {
810                kernel.fail_job(job_id, "rpc/provider");
811            }
812            return;
813        };
814        let mut results: Vec<(u64, Result<Value>, LaneId, PromiseId)> = Vec::new();
815        for job_id in &batch {
816            let Some(job) = kernel.jobs.get(job_id).cloned() else {
817                continue;
818            };
819            if job.state == JobState::Done {
820                continue;
821            }
822            // Resolve promise arguments from the settled promise graph (a
823            // dependency produced earlier in this batch is already settled).
824            let mut args = Vec::with_capacity(job.call.args.len());
825            let mut failure = None;
826            for arg in &job.call.args {
827                match arg {
828                    Arg::Promise(id) => match kernel.promises.get(id) {
829                        Some(record) if record.state.is_settled() => match record.value() {
830                            Ok(value) => args.push(Arg::Value(value)),
831                            Err(error) => {
832                                failure = Some(error);
833                                break;
834                            }
835                        },
836                        Some(_) => {
837                            failure = Some(Error::UnresolvablePromise(id.0));
838                            break;
839                        }
840                        None => {
841                            failure = Some(Error::UnknownPromise(id.0));
842                            break;
843                        }
844                    },
845                    other => args.push(other.clone()),
846                }
847            }
848            let outcome = match failure {
849                Some(error) => Err(error),
850                None => {
851                    let call = Call {
852                        args,
853                        ..job.call.clone()
854                    };
855                    let object = kernel
856                        .caps
857                        .get(&job.cap)
858                        .map(|record| record.object)
859                        .unwrap_or(0);
860                    provider.invoke(object, &call).map(Response::into_value)
861                }
862            };
863            // Settle the promise now so later jobs in this batch can use it.
864            match &outcome {
865                Ok(value) => {
866                    if let Some(record) = kernel.promises.get_mut(&job.promise) {
867                        let _ = record.resolve(value.clone());
868                    }
869                    kernel.events.push(Event::PromiseResolved(job.promise));
870                }
871                Err(error) => {
872                    if let Some(record) = kernel.promises.get_mut(&job.promise) {
873                        let _ = record.reject(error.clone());
874                    }
875                    kernel.events.push(Event::PromiseRejected {
876                        promise: job.promise,
877                        code: error.code().to_string(),
878                    });
879                }
880            }
881            results.push((*job_id, outcome, job.lane, job.promise));
882        }
883        kernel.providers.insert(provider_id, provider);
884        for (job_id, outcome, lane, _promise) in results {
885            if let Some(job) = kernel.jobs.get_mut(&job_id) {
886                job.state = JobState::Done;
887            }
888            if let Some(lane_record) = kernel.lanes.get_mut(&lane) {
889                lane_record.complete();
890            }
891            kernel
892                .events
893                .push(Event::JobCompleted { job: job_id, lane });
894            if outcome.is_err() {
895                // Keep the job record so callers can inspect it; the promise
896                // carries the failure code.
897            }
898        }
899    }
900}
901
902impl Kernel {
903    /// Build the next dispatch batch: all queued jobs for the head job's
904    /// provider, ordered so promise producers precede consumers.
905    fn next_batch(&mut self) -> Result<Vec<u64>> {
906        let Some(head) = self.queue.front().copied() else {
907            return Ok(Vec::new());
908        };
909        let Some(head_job) = self.jobs.get(&head) else {
910            self.queue.pop_front();
911            return Ok(Vec::new());
912        };
913        let provider = self
914            .caps
915            .get(&head_job.cap)
916            .map(|record| record.provider)
917            .ok_or(Error::InvalidArgument("job capability disappeared"))?;
918        let mut candidates: Vec<u64> = self
919            .queue
920            .iter()
921            .copied()
922            .filter(|id| {
923                self.jobs
924                    .get(id)
925                    .and_then(|job| self.caps.get(&job.cap))
926                    .map(|record| record.provider == provider)
927                    .unwrap_or(false)
928            })
929            .collect();
930        // Producers first: order by promise dependencies inside the batch.
931        let mut ordered: Vec<u64> = Vec::new();
932        let mut pending = candidates.clone();
933        let mut progress = true;
934        while progress && !pending.is_empty() {
935            progress = false;
936            let mut index = 0;
937            while index < pending.len() {
938                let id = pending[index];
939                let job = self.jobs.get(&id).cloned();
940                let ready = match &job {
941                    Some(job) => job.deps.iter().all(|dep| {
942                        // Resolved already, or produced by an ordered job.
943                        match self.promises.get(dep) {
944                            Some(record) if record.state.is_settled() => true,
945                            _ => ordered
946                                .iter()
947                                .any(|other| self.jobs.get(other).map(|j| j.promise) == Some(*dep)),
948                        }
949                    }),
950                    None => true,
951                };
952                if ready {
953                    ordered.push(id);
954                    pending.remove(index);
955                    progress = true;
956                } else {
957                    index += 1;
958                }
959            }
960        }
961        if !pending.is_empty() {
962            return Err(Error::PipelinedCycle);
963        }
964        candidates.clear();
965        self.queue.retain(|id| !ordered.contains(id));
966        Ok(ordered)
967    }
968
969    fn fail_job(&mut self, job_id: u64, code: &str) {
970        if let Some(job) = self.jobs.get_mut(&job_id) {
971            job.state = JobState::Done;
972            let promise = job.promise;
973            let lane = job.lane;
974            if let Some(record) = self.promises.get_mut(&promise) {
975                let _ = record.reject(Error::Provider {
976                    code: code.to_string(),
977                });
978            }
979            if let Some(lane_record) = self.lanes.get_mut(&lane) {
980                lane_record.complete();
981            }
982            self.events.push(Event::PromiseRejected {
983                promise,
984                code: code.to_string(),
985            });
986            self.events.push(Event::JobCompleted { job: job_id, lane });
987        }
988    }
989}
990
991thread_local! {
992    static NEXT_AGENT: RefCell<u64> = const { RefCell::new(0) };
993}
994
995fn pad128(id: CapId) -> [u8; 16] {
996    id.0
997}
998
999/// A handle to a pending or settled result.
1000#[derive(Clone)]
1001pub struct PromiseHandle {
1002    agent: Agent,
1003    id: PromiseId,
1004}
1005
1006impl PromiseHandle {
1007    /// Promise id.
1008    pub fn id(&self) -> PromiseId {
1009        self.id
1010    }
1011
1012    /// The owning agent.
1013    pub fn agent(&self) -> &Agent {
1014        &self.agent
1015    }
1016
1017    /// Await the value, running the kernel as needed.
1018    pub fn value(&self) -> Result<Value> {
1019        self.agent.await_promise(self)
1020    }
1021
1022    /// The settled value without running the kernel.
1023    pub fn peek(&self) -> Result<Value> {
1024        self.agent
1025            .cell
1026            .borrow()
1027            .promises
1028            .get(&self.id)
1029            .ok_or(Error::UnknownPromise(self.id.0))?
1030            .value()
1031    }
1032
1033    /// Whether the promise has settled.
1034    pub fn is_settled(&self) -> bool {
1035        self.agent
1036            .cell
1037            .borrow()
1038            .promises
1039            .get(&self.id)
1040            .map(|record| record.state.is_settled())
1041            .unwrap_or(false)
1042    }
1043}
1044
1045impl std::fmt::Debug for PromiseHandle {
1046    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1047        f.debug_struct("PromiseHandle")
1048            .field("id", &self.id)
1049            .field("settled", &self.is_settled())
1050            .finish()
1051    }
1052}
1053
1054/// Tool policy helper re-exported for callers building attenuated tools.
1055pub fn tool_policy_for(methods: &[&str]) -> ToolPolicy {
1056    ToolPolicy::allow(methods)
1057}
1058
1059/// Promise state accessor for tests and diagnostics.
1060pub fn promise_state(agent: &Agent, id: PromiseId) -> Option<PromiseState> {
1061    agent
1062        .cell
1063        .borrow()
1064        .promises
1065        .get(&id)
1066        .map(|record| record.state.clone())
1067}