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