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    ///
248    /// The secret comes from the operating system's CSPRNG. A clock-derived
249    /// seed would make capability ids guessable to a peer that can estimate the
250    /// start time, which defeats unforgeability.
251    pub fn new() -> Self {
252        let mut seed = [0u8; 32];
253        getrandom::fill(&mut seed).expect("the operating system CSPRNG must be available");
254        Self::with_secret(seed)
255    }
256
257    /// Create an agent with a fixed secret (tests, deterministic replays).
258    pub fn with_secret(secret: [u8; 32]) -> Self {
259        let kernel = Kernel {
260            secret,
261            providers: BTreeMap::new(),
262            caps: BTreeMap::new(),
263            meters: BTreeMap::new(),
264            promises: BTreeMap::new(),
265            lanes: BTreeMap::new(),
266            channels: BTreeMap::new(),
267            jobs: BTreeMap::new(),
268            queue: VecDeque::new(),
269            events: Vec::new(),
270            round_trips: 0,
271            next_promise: 0,
272            next_job: 0,
273            next_lane: 0,
274            next_channel: 0,
275            next_serial: 0,
276            next_provider: 0,
277            now_ms: 0,
278            latency_ms: 1,
279            transfers: 0,
280        };
281        let cell: KernelCell = Rc::new(RefCell::new(kernel));
282        let control: Rc<dyn CapControl> = Rc::new(HandleControl::new(Rc::clone(&cell)));
283        Agent { cell, control }
284    }
285
286    /// The shared cell, for transports and adapters that need direct access.
287    pub fn cell(&self) -> &KernelCell {
288        &self.cell
289    }
290
291    /// Register a provider and return its id.
292    pub fn register(&self, provider: Box<dyn Provider>) -> ProviderId {
293        let mut kernel = self.cell.borrow_mut();
294        let id = ProviderId(kernel.next_provider);
295        kernel.next_provider += 1;
296        kernel.providers.insert(id, provider);
297        id
298    }
299
300    /// Configure the simulated provider latency (milliseconds per batch).
301    pub fn set_latency(&self, latency_ms: u64) {
302        self.cell.borrow_mut().latency_ms = latency_ms;
303    }
304
305    /// Mint a first-generation capability.
306    pub fn mint(&self, provider: ProviderId, object: u64, attrs: CapAttrs) -> Result<CapHandle> {
307        attrs.budget.check()?;
308        if attrs.methods.is_empty() {
309            return Err(Error::InvalidArgument("capability grants no methods"));
310        }
311        let mut kernel = self.cell.borrow_mut();
312        if !kernel.providers.contains_key(&provider) {
313            return Err(Error::InvalidArgument("unknown provider"));
314        }
315        let serial = kernel.next_serial;
316        kernel.next_serial += 1;
317        let id = CapId::derive(&kernel.secret, serial, None);
318        let record = CapRecord::new(id, provider, object, attrs.clone());
319        kernel.meters.insert(id, BudgetMeter::new(attrs.budget));
320        kernel.caps.insert(id, record);
321        kernel.events.push(Event::CapMinted { cap: id, provider });
322        Ok(CapHandle::new(Rc::clone(&self.control), id))
323    }
324
325    /// Attenuate a capability. Refuses any widening.
326    pub fn attenuate(&self, parent: &CapHandle, attrs: CapAttrs) -> Result<CapHandle> {
327        let parent_id = parent.id();
328        let mut kernel = self.cell.borrow_mut();
329        let record = kernel
330            .caps
331            .get(&parent_id)
332            .ok_or(Error::UnknownCapability(u128::from_be_bytes(pad128(
333                parent_id,
334            ))))?
335            .clone();
336        if record.revoked {
337            return Err(Error::Revoked(u128::from_be_bytes(pad128(parent_id))));
338        }
339        attrs.budget.check()?;
340        let attrs = record.attrs.attenuate(attrs)?;
341        let serial = kernel.next_serial;
342        kernel.next_serial += 1;
343        let id = CapId::derive(&kernel.secret, serial, Some(&parent_id));
344        let mut child = CapRecord::new(id, record.provider, record.object, attrs.clone());
345        child.parent = Some(parent_id);
346        kernel.meters.insert(id, BudgetMeter::new(attrs.budget));
347        kernel.caps.insert(id, child);
348        kernel.events.push(Event::CapMinted {
349            cap: id,
350            provider: record.provider,
351        });
352        Ok(CapHandle::new(Rc::clone(&self.control), id))
353    }
354
355    /// Import a capability id without owning a reference.
356    ///
357    /// This is how a peer handle is reconstructed from the wire: the id must
358    /// already exist in this kernel, otherwise the first call fails with
359    /// `rpc/unknown-capability`. Importing never proves authority; the registry
360    /// does.
361    pub fn import(&self, id: CapId) -> CapHandle {
362        CapHandle::borrowed(Rc::clone(&self.control), id)
363    }
364
365    /// Revoke a capability and every capability derived from it.
366    pub fn revoke(&self, cap: &CapHandle) {
367        let mut kernel = self.cell.borrow_mut();
368        let mut doomed = BTreeSet::new();
369        let mut stack = vec![cap.id()];
370        while let Some(id) = stack.pop() {
371            if !doomed.insert(id) {
372                continue;
373            }
374            for (child, record) in &kernel.caps {
375                if record.parent == Some(id) {
376                    stack.push(*child);
377                }
378            }
379        }
380        for id in doomed {
381            if let Some(record) = kernel.caps.get_mut(&id) {
382                record.revoked = true;
383            }
384            kernel.events.push(Event::CapRevoked(id));
385        }
386    }
387
388    /// Create a lane.
389    pub fn create_lane(&self, name: impl Into<String>, budget: Budget) -> Result<LaneId> {
390        budget.check()?;
391        let mut kernel = self.cell.borrow_mut();
392        let id = LaneId(kernel.next_lane);
393        kernel.next_lane += 1;
394        kernel.lanes.insert(id, Lane::new(id, name, budget));
395        kernel.events.push(Event::LaneCreated(id));
396        Ok(id)
397    }
398
399    /// Attenuate a lane's budget.
400    pub fn attenuate_lane(&self, lane: LaneId, budget: Budget) -> Result<()> {
401        budget.check()?;
402        let mut kernel = self.cell.borrow_mut();
403        let record = kernel
404            .lanes
405            .get_mut(&lane)
406            .ok_or(Error::UnknownLane(lane.0))?;
407        record.attenuate(budget)
408    }
409
410    /// Destroy a lane and drop its queued work.
411    ///
412    /// Queued jobs are removed from the dispatch queue and their promises are
413    /// rejected, so a destroyed lane cannot keep spending budget or produce side
414    /// effects after the fact.
415    pub fn destroy_lane(&self, lane: LaneId) -> Result<()> {
416        let mut kernel = self.cell.borrow_mut();
417        {
418            let record = kernel
419                .lanes
420                .get_mut(&lane)
421                .ok_or(Error::UnknownLane(lane.0))?;
422            record.destroy();
423        }
424        let doomed: Vec<u64> = kernel
425            .jobs
426            .iter()
427            .filter(|(_, job)| job.lane == lane && job.state == JobState::Queued)
428            .map(|(id, _)| *id)
429            .collect();
430        kernel.queue.retain(|id| !doomed.contains(id));
431        for job_id in doomed {
432            kernel.fail_job(job_id, Error::UnknownLane(lane.0));
433        }
434        kernel.events.push(Event::LaneDestroyed(lane));
435        Ok(())
436    }
437
438    /// Create a channel.
439    pub fn create_channel(
440        &self,
441        name: impl Into<String>,
442        direction: Direction,
443    ) -> Result<ChannelId> {
444        let mut kernel = self.cell.borrow_mut();
445        let id = ChannelId(kernel.next_channel);
446        kernel.next_channel += 1;
447        kernel
448            .channels
449            .insert(id, Channel::new(id, name, direction));
450        Ok(id)
451    }
452
453    /// Subscribe a lane to a channel.
454    pub fn subscribe(&self, lane: LaneId, channel: ChannelId) -> Result<()> {
455        let mut kernel = self.cell.borrow_mut();
456        let lane_record = kernel.lanes.get(&lane).ok_or(Error::UnknownLane(lane.0))?;
457        if lane_record.closed {
458            return Err(Error::UnknownLane(lane.0));
459        }
460        let record = kernel
461            .channels
462            .get_mut(&channel)
463            .ok_or(Error::UnknownChannel(channel.0))?;
464        record.subscribers.insert(lane.0);
465        if let Some(lane) = kernel.lanes.get_mut(&lane) {
466            lane.channels.insert(channel.0);
467        }
468        Ok(())
469    }
470
471    /// Send a message, transferring capabilities with it.
472    pub fn send(
473        &self,
474        lane: LaneId,
475        channel: ChannelId,
476        payload: Value,
477        caps: &[CapHandle],
478    ) -> Result<u64> {
479        let mut kernel = self.cell.borrow_mut();
480        let lane_record = kernel.lanes.get(&lane).ok_or(Error::UnknownLane(lane.0))?;
481        if lane_record.closed {
482            return Err(Error::UnknownLane(lane.0));
483        }
484        let mut cap_ids = Vec::with_capacity(caps.len());
485        for cap in caps {
486            let record = kernel
487                .caps
488                .get(&cap.id())
489                .ok_or(Error::InvalidArgument("unknown capability"))?;
490            if record.revoked {
491                return Err(Error::Revoked(u128::from_be_bytes(pad128(cap.id()))));
492            }
493            // A channel shares (borrows) the capability: the id travels with
494            // the message and the sender keeps its handle. No reference is
495            // created, so a channel cannot keep a capability alive after its
496            // holder drops it (see `channel.rs`).
497            cap_ids.push(cap.id());
498        }
499        let record = kernel
500            .channels
501            .get_mut(&channel)
502            .ok_or(Error::UnknownChannel(channel.0))?;
503        let message = record.send(lane, payload, cap_ids.clone())?;
504        kernel.events.push(Event::ChannelSend {
505            channel,
506            sequence: message.sequence,
507        });
508        for id in cap_ids {
509            kernel.transfers += 1;
510            kernel
511                .events
512                .push(Event::CapTransferred { cap: id, channel });
513        }
514        Ok(message.sequence)
515    }
516
517    /// Receive the next message on a channel.
518    pub fn recv(&self, channel: ChannelId) -> Result<Option<Message>> {
519        let mut kernel = self.cell.borrow_mut();
520        let record = kernel
521            .channels
522            .get_mut(&channel)
523            .ok_or(Error::UnknownChannel(channel.0))?;
524        let message = record.recv()?;
525        if let Some(message) = &message {
526            kernel.events.push(Event::ChannelRecv {
527                channel,
528                sequence: message.sequence,
529            });
530        }
531        Ok(message)
532    }
533
534    /// Issue a call. The returned promise is pending until the kernel runs.
535    pub fn call(
536        &self,
537        lane: LaneId,
538        cap: &CapHandle,
539        method: &str,
540        args: Vec<Arg>,
541    ) -> Result<PromiseHandle> {
542        let mut kernel = self.cell.borrow_mut();
543        let cap_id = cap.id();
544        let record = kernel
545            .caps
546            .get(&cap_id)
547            .ok_or(Error::UnknownCapability(u128::from_be_bytes(pad128(
548                cap_id,
549            ))))?
550            .clone();
551        if record.revoked {
552            return Err(Error::Revoked(u128::from_be_bytes(pad128(cap_id))));
553        }
554        let (lane_closed, lane_capacity, lane_budget) = {
555            let lane_record = kernel.lanes.get(&lane).ok_or(Error::UnknownLane(lane.0))?;
556            (
557                lane_record.closed,
558                lane_record.has_capacity(),
559                lane_record.budget,
560            )
561        };
562        if lane_closed {
563            return Err(Error::UnknownLane(lane.0));
564        }
565        if !lane_capacity {
566            return Err(Error::BudgetExceeded("lane call"));
567        }
568        if !record.attrs.grants(method) {
569            return Err(Error::MethodNotGranted {
570                method: method.to_string(),
571            });
572        }
573        let provider = kernel
574            .providers
575            .get(&record.provider)
576            .ok_or(Error::InvalidArgument("provider disappeared"))?;
577        if !provider.has_method(method) {
578            return Err(Error::MethodNotGranted {
579                method: method.to_string(),
580            });
581        }
582        if record.attrs.read_only && provider.mutating(method) {
583            return Err(Error::ReadOnly);
584        }
585        // Path scope is enforced here, before the provider sees the call.
586        if let Some(scope) = &record.attrs.path_scope {
587            for arg in &args {
588                if let Arg::Value(Value::Path(path)) = arg {
589                    if !crate::cap::is_path_prefix(scope, path) {
590                        return Err(Error::PathNotGranted(path.clone()));
591                    }
592                }
593            }
594        }
595        // Tool policy is enforced here too.
596        if let Some(policy) = &record.attrs.tool_policy {
597            if !policy.allowed.contains(method) {
598                return Err(Error::ToolPolicy("tool not allowed"));
599            }
600            // Count every argument shape, recursively: a nested list or map
601            // must not slip past the tool's payload ceiling.
602            let bytes: u64 = args.iter().map(Arg::wire_size).sum();
603            if bytes > policy.max_arg_bytes {
604                return Err(Error::ToolPolicy("argument too large"));
605            }
606        }
607        let call_bytes: u64 = args.iter().map(|arg| arg.wire_size()).sum();
608        let effective = record.attrs.budget.intersect(&lane_budget);
609        if call_bytes > effective.max_bytes {
610            return Err(Error::BudgetExceeded("byte"));
611        }
612        let meter = kernel
613            .meters
614            .get_mut(&cap_id)
615            .ok_or(Error::InvalidArgument("capability has no meter"))?;
616        meter.charge(call_bytes)?;
617
618        let promise_id = PromiseId(kernel.next_promise);
619        kernel.next_promise += 1;
620        kernel
621            .promises
622            .insert(promise_id, PromiseRecord::new(promise_id, lane.0));
623        let job_id = kernel.next_job;
624        kernel.next_job += 1;
625        let deps: Vec<PromiseId> = args.iter().filter_map(|arg| arg.promise()).collect();
626        for dep in &deps {
627            if !kernel.promises.contains_key(dep) {
628                return Err(Error::UnknownPromise(dep.0));
629            }
630            if let Some(record) = kernel.promises.get_mut(dep) {
631                record.waiters += 1;
632            }
633        }
634        let call = Call {
635            method: method.to_string(),
636            args,
637            path_scope: record.attrs.path_scope.clone(),
638            redact: record.attrs.redact,
639            read_only: record.attrs.read_only,
640            tool_allows_process: record
641                .attrs
642                .tool_policy
643                .as_ref()
644                .map(|policy| policy.allow_process)
645                .unwrap_or(false),
646            budget: effective,
647        };
648        let job = Job {
649            id: job_id,
650            lane,
651            cap: cap_id,
652            call,
653            deps,
654            promise: promise_id,
655            state: JobState::Queued,
656        };
657        kernel.jobs.insert(job_id, job);
658        kernel.queue.push_back(job_id);
659        if let Some(lane_record) = kernel.lanes.get_mut(&lane) {
660            lane_record.pending += 1;
661        }
662        kernel.events.push(Event::CallQueued {
663            job: job_id,
664            lane,
665            cap: cap_id,
666        });
667        // Budgets are enforced again at dispatch with the effective grant.
668        Ok(PromiseHandle {
669            agent: self.clone(),
670            id: promise_id,
671        })
672    }
673
674    /// Run until no job is queued. Returns the number of provider round-trips.
675    pub fn run(&self) -> u64 {
676        loop {
677            let batch = {
678                let mut kernel = self.cell.borrow_mut();
679                if kernel.queue.is_empty() {
680                    return kernel.round_trips;
681                }
682                match kernel.next_batch() {
683                    Ok(batch) => batch,
684                    Err(stuck) => {
685                        // Only the jobs that cannot be ordered are failed; work
686                        // for other providers stays queued.
687                        for job_id in stuck {
688                            kernel.fail_job(job_id, Error::PipelinedCycle);
689                        }
690                        continue;
691                    }
692                }
693            };
694            self.dispatch(batch);
695        }
696    }
697
698    /// Round-trips observed so far.
699    pub fn round_trips(&self) -> u64 {
700        self.cell.borrow().round_trips
701    }
702
703    /// Await a promise, running the kernel until it settles.
704    pub fn await_promise(&self, promise: &PromiseHandle) -> Result<Value> {
705        loop {
706            {
707                let kernel = self.cell.borrow();
708                let record = kernel
709                    .promises
710                    .get(&promise.id)
711                    .ok_or(Error::UnknownPromise(promise.id.0))?;
712                if record.state.is_settled() {
713                    return record.value();
714                }
715            }
716            if self.cell.borrow().queue.is_empty() {
717                return Err(Error::UnresolvablePromise(promise.id.0));
718            }
719            self.run();
720        }
721    }
722
723    /// Resolve a pending promise manually (used by peers that answer out of band).
724    pub fn resolve(&self, promise: &PromiseHandle, value: Value) -> Result<()> {
725        let mut kernel = self.cell.borrow_mut();
726        let record = kernel
727            .promises
728            .get_mut(&promise.id)
729            .ok_or(Error::UnknownPromise(promise.id.0))?;
730        record.resolve(value)?;
731        kernel.events.push(Event::PromiseResolved(promise.id));
732        Ok(())
733    }
734
735    /// Cancel a promise.
736    ///
737    /// A job that has not been dispatched yet is removed from the queue, so a
738    /// cancellation really prevents the provider call (and its side effects). A
739    /// job already in flight cannot be stopped; its result is discarded.
740    pub fn cancel(&self, promise: &PromiseHandle) -> Result<()> {
741        let mut kernel = self.cell.borrow_mut();
742        {
743            let record = kernel
744                .promises
745                .get_mut(&promise.id)
746                .ok_or(Error::UnknownPromise(promise.id.0))?;
747            record.cancel()?;
748        }
749        let queued: Option<u64> = kernel
750            .jobs
751            .iter()
752            .find(|(_, job)| job.promise == promise.id && job.state == JobState::Queued)
753            .map(|(id, _)| *id);
754        if let Some(job_id) = queued {
755            kernel.queue.retain(|id| *id != job_id);
756            let lane = kernel.jobs.get(&job_id).map(|job| job.lane);
757            if let Some(job) = kernel.jobs.get_mut(&job_id) {
758                job.state = JobState::Done;
759            }
760            if let Some(lane) = lane {
761                if let Some(lane_record) = kernel.lanes.get_mut(&lane) {
762                    lane_record.complete();
763                }
764            }
765        }
766        Ok(())
767    }
768
769    /// Drain drop notifications recorded by one provider.
770    pub fn take_drops(&self, provider: ProviderId) -> Vec<(u64, CapId)> {
771        self.cell
772            .borrow_mut()
773            .providers
774            .get_mut(&provider)
775            .map(|provider| provider.take_drop_notifications())
776            .unwrap_or_default()
777    }
778
779    /// The event log.
780    pub fn events(&self) -> Vec<Event> {
781        self.cell.borrow().events.clone()
782    }
783
784    /// Lane statistics.
785    pub fn lane(&self, lane: LaneId) -> Result<Lane> {
786        self.cell
787            .borrow()
788            .lanes
789            .get(&lane)
790            .cloned()
791            .ok_or(Error::UnknownLane(lane.0))
792    }
793
794    /// Channel statistics.
795    pub fn channel(&self, channel: ChannelId) -> Result<Channel> {
796        self.cell
797            .borrow()
798            .channels
799            .get(&channel)
800            .cloned()
801            .ok_or(Error::UnknownChannel(channel.0))
802    }
803
804    /// Capability attributes, for tests and policy tooling.
805    pub fn cap_attrs(&self, cap: &CapHandle) -> Result<CapAttrs> {
806        self.cell
807            .borrow()
808            .caps
809            .get(&cap.id())
810            .map(|record| record.attrs.clone())
811            .ok_or(Error::UnknownCapability(u128::from_be_bytes(pad128(
812                cap.id(),
813            ))))
814    }
815
816    fn dispatch(&self, batch: Vec<u64>) {
817        // No kernel borrow is held while a provider runs: a provider may hold an
818        // `Agent` and call back into the kernel, and a re-entrant borrow would
819        // panic (or, with `try_borrow_mut`, silently drop a refcount update).
820        // Each phase takes a short borrow and returns owned data.
821        let (provider_id, provider) = {
822            let mut kernel = self.cell.borrow_mut();
823            kernel.round_trips += 1;
824            kernel.now_ms = kernel.now_ms.saturating_add(kernel.latency_ms);
825            let Some(first) = batch.first().and_then(|id| kernel.jobs.get(id)) else {
826                return;
827            };
828            let provider_id = kernel
829                .caps
830                .get(&first.cap)
831                .map(|record| record.provider)
832                .unwrap_or(ProviderId(u64::MAX));
833            let lanes: BTreeSet<u64> = batch
834                .iter()
835                .filter_map(|id| kernel.jobs.get(id).map(|job| job.lane.0))
836                .collect();
837            kernel.events.push(Event::BatchDispatched {
838                provider: provider_id,
839                jobs: batch.len(),
840                lanes,
841            });
842            let provider = kernel.providers.remove(&provider_id);
843            (provider_id, provider)
844        };
845        let Some(mut provider) = provider else {
846            let mut kernel = self.cell.borrow_mut();
847            for job_id in batch {
848                kernel.fail_job(
849                    job_id,
850                    Error::Provider {
851                        code: "rpc/provider".to_string(),
852                    },
853                );
854            }
855            return;
856        };
857
858        for job_id in &batch {
859            // Phase 1: decide, under a read borrow.
860            let prepared = {
861                let kernel = self.cell.borrow();
862                let Some(job) = kernel.jobs.get(job_id).cloned() else {
863                    continue;
864                };
865                if job.state == JobState::Done {
866                    continue;
867                }
868                let mut args = Vec::with_capacity(job.call.args.len());
869                let mut failure = None;
870                for arg in &job.call.args {
871                    match arg {
872                        Arg::Promise(id) => match kernel.promises.get(id) {
873                            Some(record) if record.state.is_settled() => match record.value() {
874                                Ok(value) => args.push(Arg::Value(value)),
875                                Err(error) => {
876                                    failure = Some(error);
877                                    break;
878                                }
879                            },
880                            Some(_) => {
881                                failure = Some(Error::UnresolvablePromise(id.0));
882                                break;
883                            }
884                            None => {
885                                failure = Some(Error::UnknownPromise(id.0));
886                                break;
887                            }
888                        },
889                        other => args.push(other.clone()),
890                    }
891                }
892                // Authority is re-checked at dispatch: a capability revoked
893                // after the call was queued must not reach the provider.
894                let record = kernel.caps.get(&job.cap).cloned();
895                let failure = match &record {
896                    None => Some(Error::UnknownCapability(0)),
897                    Some(record) if record.revoked => {
898                        Some(Error::Revoked(u128::from_be_bytes(pad128(record.id))))
899                    }
900                    Some(_) => failure,
901                };
902                Some((
903                    job,
904                    args,
905                    failure,
906                    record.map(|record| record.object).unwrap_or(0),
907                ))
908            };
909            let Some((job, args, failure, object)) = prepared else {
910                continue;
911            };
912
913            // Phase 2: run provider code with no borrow held.
914            let outcome = match failure {
915                Some(error) => Err(error),
916                None => {
917                    let call = Call {
918                        args,
919                        ..job.call.clone()
920                    };
921                    provider.invoke(object, &call).map(Response::into_value)
922                }
923            };
924
925            // Phase 3: settle under a write borrow.
926            let mut kernel = self.cell.borrow_mut();
927            match &outcome {
928                Ok(value) => {
929                    if let Some(record) = kernel.promises.get_mut(&job.promise) {
930                        let _ = record.resolve(value.clone());
931                    }
932                    kernel.events.push(Event::PromiseResolved(job.promise));
933                }
934                Err(error) => {
935                    if let Some(record) = kernel.promises.get_mut(&job.promise) {
936                        let _ = record.reject(error.clone());
937                    }
938                    kernel.events.push(Event::PromiseRejected {
939                        promise: job.promise,
940                        code: error.code().to_string(),
941                    });
942                }
943            }
944            if let Some(record) = kernel.jobs.get_mut(job_id) {
945                record.state = JobState::Done;
946            }
947            if let Some(lane_record) = kernel.lanes.get_mut(&job.lane) {
948                lane_record.complete();
949            }
950            kernel.events.push(Event::JobCompleted {
951                job: *job_id,
952                lane: job.lane,
953            });
954        }
955
956        self.cell
957            .borrow_mut()
958            .providers
959            .insert(provider_id, provider);
960    }
961}
962
963impl Kernel {
964    /// Build the next dispatch batch: all queued jobs for the head job's
965    /// provider, ordered so promise producers precede consumers.
966    fn next_batch(&mut self) -> std::result::Result<Vec<u64>, Vec<u64>> {
967        let Some(head) = self.queue.front().copied() else {
968            return Ok(Vec::new());
969        };
970        let Some(head_job) = self.jobs.get(&head) else {
971            self.queue.pop_front();
972            return Ok(Vec::new());
973        };
974        let Some(provider) = self.caps.get(&head_job.cap).map(|record| record.provider) else {
975            // The capability vanished; fail this job and let the caller retry.
976            self.queue.pop_front();
977            return Err(vec![head]);
978        };
979        let mut candidates: Vec<u64> = self
980            .queue
981            .iter()
982            .copied()
983            .filter(|id| {
984                self.jobs
985                    .get(id)
986                    .and_then(|job| self.caps.get(&job.cap))
987                    .map(|record| record.provider == provider)
988                    .unwrap_or(false)
989            })
990            .collect();
991        // Producers first: order by promise dependencies inside the batch.
992        let mut ordered: Vec<u64> = Vec::new();
993        let mut pending = candidates.clone();
994        let mut progress = true;
995        while progress && !pending.is_empty() {
996            progress = false;
997            let mut index = 0;
998            while index < pending.len() {
999                let id = pending[index];
1000                let job = self.jobs.get(&id).cloned();
1001                let ready = match &job {
1002                    Some(job) => job.deps.iter().all(|dep| {
1003                        // Resolved already, or produced by an ordered job.
1004                        match self.promises.get(dep) {
1005                            Some(record) if record.state.is_settled() => true,
1006                            _ => ordered
1007                                .iter()
1008                                .any(|other| self.jobs.get(other).map(|j| j.promise) == Some(*dep)),
1009                        }
1010                    }),
1011                    None => true,
1012                };
1013                if ready {
1014                    ordered.push(id);
1015                    pending.remove(index);
1016                    progress = true;
1017                } else {
1018                    index += 1;
1019                }
1020            }
1021        }
1022        if !pending.is_empty() {
1023            // Unorderable: the remaining jobs form (or depend on) a cycle.
1024            for id in &pending {
1025                self.queue.retain(|queued| queued != id);
1026            }
1027            return Err(pending);
1028        }
1029        candidates.clear();
1030        self.queue.retain(|id| !ordered.contains(id));
1031        Ok(ordered)
1032    }
1033
1034    fn fail_job(&mut self, job_id: u64, error: Error) {
1035        if let Some(job) = self.jobs.get_mut(&job_id) {
1036            job.state = JobState::Done;
1037            let promise = job.promise;
1038            let lane = job.lane;
1039            let code = error.code().to_string();
1040            if let Some(record) = self.promises.get_mut(&promise) {
1041                let _ = record.reject(error);
1042            }
1043            if let Some(lane_record) = self.lanes.get_mut(&lane) {
1044                lane_record.complete();
1045            }
1046            self.events.push(Event::PromiseRejected { promise, code });
1047            self.events.push(Event::JobCompleted { job: job_id, lane });
1048        }
1049    }
1050}
1051
1052fn pad128(id: CapId) -> [u8; 16] {
1053    id.0
1054}
1055
1056/// A handle to a pending or settled result.
1057#[derive(Clone)]
1058pub struct PromiseHandle {
1059    agent: Agent,
1060    id: PromiseId,
1061}
1062
1063impl PromiseHandle {
1064    /// Promise id.
1065    pub fn id(&self) -> PromiseId {
1066        self.id
1067    }
1068
1069    /// The owning agent.
1070    pub fn agent(&self) -> &Agent {
1071        &self.agent
1072    }
1073
1074    /// Await the value, running the kernel as needed.
1075    pub fn value(&self) -> Result<Value> {
1076        self.agent.await_promise(self)
1077    }
1078
1079    /// The settled value without running the kernel.
1080    pub fn peek(&self) -> Result<Value> {
1081        self.agent
1082            .cell
1083            .borrow()
1084            .promises
1085            .get(&self.id)
1086            .ok_or(Error::UnknownPromise(self.id.0))?
1087            .value()
1088    }
1089
1090    /// Whether the promise has settled.
1091    pub fn is_settled(&self) -> bool {
1092        self.agent
1093            .cell
1094            .borrow()
1095            .promises
1096            .get(&self.id)
1097            .map(|record| record.state.is_settled())
1098            .unwrap_or(false)
1099    }
1100}
1101
1102impl std::fmt::Debug for PromiseHandle {
1103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1104        f.debug_struct("PromiseHandle")
1105            .field("id", &self.id)
1106            .field("settled", &self.is_settled())
1107            .finish()
1108    }
1109}
1110
1111/// Tool policy helper re-exported for callers building attenuated tools.
1112pub fn tool_policy_for(methods: &[&str]) -> ToolPolicy {
1113    ToolPolicy::allow(methods)
1114}
1115
1116/// Promise state accessor for tests and diagnostics.
1117pub fn promise_state(agent: &Agent, id: PromiseId) -> Option<PromiseState> {
1118    agent
1119        .cell
1120        .borrow()
1121        .promises
1122        .get(&id)
1123        .map(|record| record.state.clone())
1124}