PAL — a durable, capability-safe runtime sketch for supervised AI agents.
This repository is a bounded local prototype, not a language release. It shows the smallest runnable Rust design that captures four runtime properties:
- Durable append-only task journal — events persist as local JSONL (
Journal). - Typed effect / capability boundary — external-style effects require an explicit grant (
CapabilitySet+Effect). - Deterministic replay — the same journal always rebuilds the same
TaskSnapshot. - Human-approval pause / resume — a task can enter
AwaitingApproval, survive process restart (via the journal), and continue with a recorded decision.
No network calls, credentials, remote services, or publish pipeline are involved.
cargo test
cargo fmt| Path | Role |
|---|---|
src/types.rs |
Task ids, status, journal events, snapshots |
src/journal.rs |
Append-only JSONL persistence + replay fold |
src/capability.rs |
Capability, Effect, authorize gate |
src/runtime.rs |
Start / step / effect / approve / finish |
tests/prototype.rs |
Journaling/replay, denied capability, approval |
use pal::{
ApprovalDecision, Capability, CapabilitySet, Effect, Journal, Runtime, TaskId,
};
let journal = Journal::open("pal.jsonl")?;
let caps = CapabilitySet::with([Capability::Log]);
let rt = Runtime::new(journal, caps);
let tid = TaskId::new("demo-1");
rt.start_task(tid.clone(), "demo")?;
rt.complete_step(&tid, "think", "planned work")?;
// Denied unless Capability::HttpFetch is granted:
// rt.attempt_effect(&tid, Effect::HttpFetch { url: "...".into() })?;
rt.request_approval(&tid, "ship the change")?;
rt.resume_with_approval(
&tid,
ApprovalDecision {
approved: true,
note: "lgtm".into(),
},
)?;
rt.finish(&tid, "done")?;
let snap = rt.replay(&tid)?;- Effects are stubs. Allowed effects are recorded, not executed against the OS or network.
HttpFetchnever dials;WriteLocalnever writes a real file. This keeps the prototype offline and safe. - Capabilities are session-scoped. Grants live on
Runtimeconstruction, not as a full policy language or ACL hierarchy. - Journal is JSONL. One event per line, append-only, best-effort
sync_allafter each write. SQLite would also fit; JSONL keeps dependencies minimal. - Replay is pure fold.
TaskSnapshot::applyis the single reducer. No wall-clock or RNG is consulted during rebuild. - Approval is a stub. There is no UI or multi-party workflow—only durable state transitions and a decision record.
- No real language surface (parser, AST, interpreter) — only a runtime kernel.
- No multi-agent scheduling, timers, or distributed consensus.
- No cryptographic integrity / signed journals.
- No concurrent writers; single-process assumed.
- No real I/O adapters (filesystem, HTTP, shell).
- No credential store, sandbox, or OS-level confinement.
- Not published to crates.io (
publish = false).
cargo testCovered scenarios:
- journaling + independent deterministic replay
- denied capability (
HttpFetchwithout grant) - approval pause blocks progress; resume continues the same task
- approval rejection finishes as
Denied
MIT (prototype; not a product release).