pub struct Kernel { /* private fields */ }Expand description
The Kernel (核) — executes kaish code.
This is the primary interface for running kaish commands. It owns all the runtime state: variables, tools, VFS, jobs, and persistence.
Implementations§
Source§impl Kernel
impl Kernel
Sourcepub fn new(config: KernelConfig) -> Result<Self>
pub fn new(config: KernelConfig) -> Result<Self>
Create a new kernel with the given configuration.
Sourcepub fn with_backend(
backend: Arc<dyn KernelBackend>,
config: KernelConfig,
configure_vfs: impl FnOnce(&mut VfsRouter),
configure_tools: impl FnOnce(&mut ToolRegistry),
) -> Result<Self>
pub fn with_backend( backend: Arc<dyn KernelBackend>, config: KernelConfig, configure_vfs: impl FnOnce(&mut VfsRouter), configure_tools: impl FnOnce(&mut ToolRegistry), ) -> Result<Self>
Create a kernel with a custom backend and /v/* virtual path support.
This is the constructor for embedding kaish in other systems that provide their own storage backend (e.g., CRDT-backed storage in kaijutsu).
A VirtualOverlayBackend routes paths automatically:
/v/*→ Internal VFS (JobFs at/v/jobs, MemoryFs at/v/blobs)- Everything else → Your custom backend
The optional configure_vfs closure lets you add additional virtual mounts
(e.g., /v/docs for CRDT blocks) after the built-in mounts are set up.
Note: The config’s vfs_mode is ignored — all non-/v/* path routing
is handled by your custom backend. The config is only used for name, cwd,
skip_validation, and interactive.
§Example
// Simple: default /v/* mounts only
let kernel = Kernel::with_backend(backend, config, |_| {}, |_| {})?;
// With custom mounts
let kernel = Kernel::with_backend(backend, config, |vfs| {
vfs.mount_arc("/v/docs", docs_fs);
vfs.mount_arc("/v/g", git_fs);
}, |_| {})?;
// With custom tools
let kernel = Kernel::with_backend(backend, config, |_| {}, |tools| {
tools.register(MyCustomTool::new());
})?;Sourcepub fn into_arc(self) -> Arc<Self>
pub fn into_arc(self) -> Arc<Self>
Wrap this Kernel in an Arc and initialize its self-reference.
This enables the Kernel to hand out Arc<dyn CommandDispatcher> references
to child contexts, allowing builtins like timeout to dispatch inner
commands through the full resolution chain (user tools → builtins →
.kai scripts → external commands).
Sourcepub async fn fork(&self) -> Arc<Self>
pub async fn fork(&self) -> Arc<Self>
Fork a subsidiary kernel for concurrent execution.
The fork is a fully-functional Kernel that:
- Snapshots per-session state from the parent: scope (COW — cheap), user-defined tools, cwd, aliases, ignore config, etc. Mutations on the fork do NOT propagate back to the parent — matching bash subshell / background-job semantics.
- Shares read-mostly resources with the parent via
Arc: the tool registry, the VFS router, and the job manager. A job registered by the fork is visible to the parent’sjobsbuiltin, and the fork sees the same VFS mounts. - Owns its own
stderr_receiver,cancel_token, andexecute_lock. It is never the TTY owner, sointeractiveisfalseandterminal_stateisNone.
The returned Arc has its self_weak populated (via into_arc), so
nested dispatch through ctx.dispatcher (e.g. the timeout builtin)
routes through the fork itself, not the parent — which is essential
for concurrency safety.
Use this for detached background concurrency where the fork should
survive parent cancellation: the & background-job operator and any
other “fire and forget” worker. The fork gets a fresh, independent
cancellation token.
For foreground concurrency (scatter workers, concurrent pipeline
stages, $(...) cmdsubs) where parent timeout/cancel must cascade
into the fork’s external children, use Self::fork_attached.
Sourcepub async fn fork_attached(&self) -> Arc<Self>
pub async fn fork_attached(&self) -> Arc<Self>
Fork attached to the parent’s cancellation.
Same as Self::fork but the fork’s cancel_token is a child of
the parent’s. When the parent cancels (request timeout, embedder
Kernel::cancel, etc.), the fork’s token also cancels, which in
turn kills any external children spawned in the fork via the
wait_or_kill / SIGTERM-grace-SIGKILL path.
Sourcepub async fn fork_for_background(
&self,
cancel: CancellationToken,
job_id: JobId,
) -> Arc<Self>
pub async fn fork_for_background( &self, cancel: CancellationToken, job_id: JobId, ) -> Arc<Self>
Fork for a background job, stamping the job id so external commands
spawned anywhere beneath it record their process groups on that job
(for kill -<sig> %N). The caller owns cancel so it can also drive
JobManager::cancel.
Sourcepub fn dispatcher(&self) -> Option<Arc<dyn CommandDispatcher>>
pub fn dispatcher(&self) -> Option<Arc<dyn CommandDispatcher>>
Get an Arc<dyn CommandDispatcher> to this Kernel, if wrapped via into_arc().
Returns None if the Kernel was not wrapped, or if all strong references
have been dropped (the Weak can no longer upgrade).
Sourcepub fn set_trash_backend(&mut self, backend: Option<Arc<dyn TrashBackend>>)
pub fn set_trash_backend(&mut self, backend: Option<Arc<dyn TrashBackend>>)
Replace or remove the trash backend used by rm and kaish-trash.
The kernel installs the OS trash (SystemTrash) automatically when
built with the os-integration feature. Embedders and tests can swap
in a custom crate::trash::TrashBackend, or pass None to remove
it — with trash enabled but no backend present, rm fails loud
rather than falling through to permanent delete.
Sourcepub fn cancel(&self)
pub fn cancel(&self)
Cancel the current execution.
This cancels the current cancellation token, causing any execution
loop to exit at the next checkpoint with exit code 130 (SIGINT).
A fresh token is installed for the next execute() call.
Sourcepub fn is_cancelled(&self) -> bool
pub fn is_cancelled(&self) -> bool
Check if the current execution has been cancelled.
Sourcepub async fn execute(&self, input: &str) -> Result<ExecResult>
pub async fn execute(&self, input: &str) -> Result<ExecResult>
Execute kaish source code with default options.
Equivalent to execute_with_options(input, ExecuteOptions::default()).
Returns the result of the last statement executed.
Sourcepub async fn execute_with_options(
&self,
input: &str,
opts: ExecuteOptions,
) -> Result<ExecResult>
pub async fn execute_with_options( &self, input: &str, opts: ExecuteOptions, ) -> Result<ExecResult>
Execute with per-call options. The primary entry point for embedders that don’t need per-statement output streaming.
opts carries timeout, transient vars overlay, optional cwd override,
and optional embedder-owned cancellation token. See ExecuteOptions
for semantics. For streaming, use Self::execute_with_options_streaming.
Cancellation: if opts.cancel_token is Some, it is raced
against the kernel’s internal token. Either firing cancels and kills
external children. The embedder’s token is read-only — kernel
timeouts do NOT propagate into it. Distinguish via the returned
code: 124 = timeout, 130 = cancellation.
Timeout: opts.timeout overrides KernelConfig::request_timeout.
Some(Duration::ZERO) returns 124 immediately without spawning.
Concurrent callers on the same Kernel serialize on the kernel-wide
execute lock. For true parallelism, call Kernel::fork (detached)
or Kernel::fork_attached (cancellation cascades from this kernel).
Sourcepub async fn execute_with_options_streaming(
&self,
input: &str,
opts: ExecuteOptions,
on_output: &mut (dyn FnMut(&ExecResult) + Send),
) -> Result<ExecResult>
pub async fn execute_with_options_streaming( &self, input: &str, opts: ExecuteOptions, on_output: &mut (dyn FnMut(&ExecResult) + Send), ) -> Result<ExecResult>
Same as Self::execute_with_options but with a per-statement output
callback. The callback fires after each top-level statement so the
embedder (REPL, MCP streaming) can flush output incrementally.
Sourcepub async fn execute_with_vars(
&self,
input: &str,
vars: HashMap<String, Value>,
) -> Result<ExecResult>
👎Deprecated: use Kernel::execute_with_options with ExecuteOptions::with_vars
pub async fn execute_with_vars( &self, input: &str, vars: HashMap<String, Value>, ) -> Result<ExecResult>
use Kernel::execute_with_options with ExecuteOptions::with_vars
Execute kaish source code with a transient overlay of exported variables.
Deprecated thin wrapper over Self::execute_with_options. New code
should use that method directly:
execute_with_options(input, ExecuteOptions::new().with_vars(vars)).
Sourcepub async fn execute_streaming(
&self,
input: &str,
on_output: &mut (dyn FnMut(&ExecResult) + Send),
) -> Result<ExecResult>
👎Deprecated: use Kernel::execute_with_options_streaming
pub async fn execute_streaming( &self, input: &str, on_output: &mut (dyn FnMut(&ExecResult) + Send), ) -> Result<ExecResult>
use Kernel::execute_with_options_streaming
Execute kaish source code with a per-statement callback.
Deprecated thin wrapper. New code should use
Self::execute_with_options_streaming.
Sourcepub async fn set_positional(
&self,
script_name: impl Into<String>,
args: Vec<String>,
)
pub async fn set_positional( &self, script_name: impl Into<String>, args: Vec<String>, )
Set positional parameters ($0 script name and $1-$9 args).
Sourcepub async fn exported_vars(&self) -> Vec<(String, Value)>
pub async fn exported_vars(&self) -> Vec<(String, Value)>
List exported variables (name, value), sorted by name. These are the
vars a child process would see (see dispatch’s hermetic env build).
Sourcepub async fn try_set_cwd(&self, path: PathBuf) -> bool
pub async fn try_set_cwd(&self, path: PathBuf) -> bool
Set the working directory only if path resolves to a directory in the
kernel’s backend — the same namespace cd validates against. Unlike a
raw host-FS is_dir() check, this correctly accepts virtual mounts
(/v/docs, in-memory scratch, …) and rejects real paths that have since
disappeared. Returns whether the cwd was changed.
Sourcepub async fn last_result(&self) -> ExecResult
pub async fn last_result(&self) -> ExecResult
Get the last result ($?).
Sourcepub async fn has_function(&self, name: &str) -> bool
pub async fn has_function(&self, name: &str) -> bool
Check if a user-defined function exists.
Sourcepub fn tool_schemas(&self) -> Vec<ToolSchema>
pub fn tool_schemas(&self) -> Vec<ToolSchema>
Get available tool schemas.
Sourcepub fn jobs(&self) -> Arc<JobManager>
pub fn jobs(&self) -> Arc<JobManager>
Get job manager.
Trait Implementations§
Source§impl CommandDispatcher for Kernel
impl CommandDispatcher for Kernel
Source§fn dispatch<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
cmd: &'life1 Command,
ctx: &'life2 mut ExecContext,
) -> Pin<Box<dyn Future<Output = Result<ExecResult>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn dispatch<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
cmd: &'life1 Command,
ctx: &'life2 mut ExecContext,
) -> Pin<Box<dyn Future<Output = Result<ExecResult>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Dispatch a command through the Kernel’s full resolution chain.
This is the single path for all command execution when called from the pipeline runner. It provides the full dispatch chain: user tools → builtins → .kai scripts → external commands → backend tools.
Source§fn eval_expr<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
expr: &'life1 Expr,
_ctx: &'life2 ExecContext,
) -> Pin<Box<dyn Future<Output = Result<Value>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn eval_expr<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
expr: &'life1 Expr,
_ctx: &'life2 ExecContext,
) -> Pin<Box<dyn Future<Output = Result<Value>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Evaluate an expression through the kernel’s async chain, including
command substitution. Delegates to eval_expr_async, which snapshots
the kernel’s scope/cwd and restores them after any $(...) runs, so
only command output escapes. The ctx is unused here because the
kernel evaluates against its own session state (a fork carries the
pipeline stage’s snapshot); var refs resolve against that scope.
Source§fn fork<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Arc<dyn CommandDispatcher>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn fork<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Arc<dyn CommandDispatcher>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Produce a forked dispatcher with independent mutable state (detached).
Calls the inherent Kernel::fork method (note the UFCS to avoid
recursing into the trait method we’re defining) and coerces the
returned Arc<Kernel> to Arc<dyn CommandDispatcher>.
Source§fn fork_attached<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Arc<dyn CommandDispatcher>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn fork_attached<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Arc<dyn CommandDispatcher>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Produce a forked dispatcher with cancellation cascading from this kernel.