Skip to main content

sim_lib_agent_runner_process/
effects.rs

1use crate::{ModelRequest, ModelResponse, ProcessRunner};
2use sim_kernel::{
3    CapabilityName, Cx, Datum, DatumStore, Effect, Expr, Ref, Result, Symbol, core_any_ref, effect,
4    value_from_ref,
5};
6
7/// Returns the capability gating subprocess execution by a [`ProcessRunner`].
8///
9/// Spawning `/bin/sh -c <command>` is an arbitrary host-exec operation, so the
10/// runner demands this capability before reaching the shell, mirroring the way
11/// sibling crates gate their host effects with an explicit `cx.require(...)`.
12pub fn host_process_capability() -> CapabilityName {
13    CapabilityName::new("host.process")
14}
15
16pub(super) fn resolve_process_effect<F>(
17    runner: &ProcessRunner,
18    cx: &mut Cx,
19    request: ModelRequest,
20    perform: F,
21) -> Result<ModelResponse>
22where
23    F: FnOnce(&ProcessRunner, ModelRequest) -> Result<ModelResponse>,
24{
25    // Refuse before spawning any shell: arbitrary host-process exec must rest on
26    // an explicit capability grant, not only on the kernel effect policy.
27    cx.require(&host_process_capability())?;
28    let effect = process_effect(runner, cx, &request)?;
29    let result = effect::resolve_effect(cx, effect, |cx, _effect| {
30        let response = perform(runner, request)?;
31        response_ref(cx, response)
32    })?;
33    response_from_ref(cx, &result)
34}
35
36fn process_effect(runner: &ProcessRunner, cx: &mut Cx, request: &ModelRequest) -> Result<Effect> {
37    let input = Datum::Node {
38        tag: Symbol::qualified("agent", "ProcessRunnerInput"),
39        fields: vec![
40            (Symbol::new("runner"), Datum::Symbol(runner.runner.clone())),
41            (Symbol::new("model"), Datum::String(runner.model.clone())),
42            (
43                Symbol::new("request"),
44                Datum::try_from(Expr::from(request.clone()))?,
45            ),
46        ],
47    };
48    let input = Ref::Content(cx.datum_store_mut().intern(input)?);
49    Effect::new(
50        effect::effect_host_process_kind(),
51        Ref::Symbol(runner.runner.clone()),
52        input,
53        core_any_ref(),
54        effect::effect_resume_op_key(),
55        effect::effect_abort_op_key(),
56    )
57    .with_replay_key(Some(Ref::Symbol(Symbol::qualified(
58        "agent",
59        "process-runner-v1",
60    ))))
61}
62
63fn response_ref(cx: &mut Cx, response: ModelResponse) -> Result<Ref> {
64    Ok(Ref::Content(
65        cx.datum_store_mut()
66            .intern(Datum::try_from(Expr::from(response))?)?,
67    ))
68}
69
70fn response_from_ref(cx: &mut Cx, reference: &Ref) -> Result<ModelResponse> {
71    ModelResponse::try_from(value_from_ref(cx, reference)?.object().as_expr(cx)?)
72}