//! Run from the workspace root: cargo run -p klbr-runtime --example workbench
//! No providers, credentials or real Discord effects are used.
use anyhow::Result;
use klbr_runtime::{
behavior::Release, protocol::SessionId, session::SessionRuntime, store::Store,
worker::WorkerConfig,
};
use std::{fs, path::PathBuf, time::Duration};
#[tokio::main]
async fn main() -> Result<()> {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.to_path_buf();
let state = tempfile::tempdir()?;
let release = Release::publish(&root.join("behavior"), &state.path().join("releases"))?;
let store = Store::open(state.path().join("runtime.sqlite"))?;
let session = SessionRuntime::start(
store,
SessionId::fresh(),
WorkerConfig::source_tree(&root),
release,
)
.await?;
session
.store
.scratchpad(
&session.id,
"i am preparing a small experiment, not sending this text".into(),
)
.await?;
let first = session
.execute(
"answer = 6 * 7\nprint('workbench output, not speech')\nanswer".into(),
Duration::from_secs(10),
)
.await?;
let second = session.execute("from klbr import local, hooks\nreceipt = await local.send(f'the answer is {answer}')\nreceipt".into(),Duration::from_secs(10)).await?;
// A hook-only source change; the variable `answer` stays in the workbench.
let draft = state.path().join("draft");
fs::create_dir_all(draft.join("klbr_hooks"))?;
fs::copy(
root.join("behavior/manifest.json"),
draft.join("manifest.json"),
)?;
fs::copy(
root.join("behavior/klbr_hooks/__init__.py"),
draft.join("klbr_hooks/__init__.py"),
)?;
let mut source = fs::read_to_string(root.join("behavior/klbr_hooks/defaults.py"))?;
source.push_str("\nasync def delivery(request, context):\n return DeliveryDecision.hold('testing a new guard')\n");
fs::write(draft.join("klbr_hooks/defaults.py"), source)?;
let candidate = Release::publish(&draft, &state.path().join("releases"))?;
session
.activate_hooks(session.hook_epoch().await, candidate)
.await?;
let held = session
.execute(
"assert answer == 42\n(await local.send('this stays held')).status".into(),
Duration::from_secs(10),
)
.await?;
let third = session.execute("from klbr import runtime\nawait runtime.wait(reason='waiting for an input')\nraise RuntimeError('must not run')".into(),Duration::from_secs(10)).await?;
for report in [first, second, held, third] {
println!("{}", serde_json::to_string_pretty(&report)?);
}
println!(
"durable history:\n{}",
serde_json::to_string_pretty(&session.store.history(&session.id, 0, 100).await?)?
);
session.shutdown().await;
Ok(())
}