Skip to content

Feature request: Host-provided background process management for long-running tools #600

@JMLX42

Description

@JMLX42

Problem

Currently, Wassette components can maintain state across tool calls (like memory-js), but they cannot run code between tool calls. This prevents use cases like:

  • Starting an HTTP server that listens while the LLM does other work
  • Running background workers that produce output over time
  • Any tool that needs to "stay alive" between invocations

Example scenario:

1. LLM calls: spawn("flask_server_code")  → returns process_id=42
2. LLM thinks, makes HTTP requests to localhost:8080
3. LLM calls: read-stdout(42) → expects server logs

This doesn't work today because the component is frozen between calls - the Flask server never actually runs.

Root Cause

Wassette uses wasmtime's async_support(true) for non-blocking host I/O, but doesn't use wasmtime's background task primitives (Accessor::spawn, StoreContextMut::spawn). The component only executes during a tool call, not between them.

Proposed Solution

Add host-provided imports for background process management. Components would call these host functions rather than trying to run code inside WASM:

interface process-manager {
    type process-id = u64;

    enum signal {
        terminate,  // SIGTERM
        kill,       // SIGKILL
    }

    variant process-state {
        running,
        exited(u32),
        signaled(signal),
    }

    variant process-error {
        invalid-code(string),
        not-found,
        not-running,
        resource-exhausted,
    }

    /// Start a background process (host spawns real thread/process)
    spawn: func(runtime: string, code: string) -> result<process-id, process-error>;

    /// Check process state
    state: func(id: process-id) -> result<process-state, process-error>;

    /// Send termination signal
    signal: func(id: process-id, sig: signal) -> result<_, process-error>;

    /// Read buffered stdout
    read-stdout: func(id: process-id) -> result<string, process-error>;

    /// Read buffered stderr
    read-stderr: func(id: process-id) -> result<string, process-error>;

    /// List all process IDs
    list: func() -> list<process-id>;
}

Key design points:

  • Host (Wassette) manages real OS processes/threads, not WASM execution
  • Components just hold process IDs and call host functions
  • Fits Wassette's deny-by-default security model (new capability to grant)
  • runtime parameter allows different interpreters (python, node, etc.)

Implementation Approaches

  1. Host-side OS processes: Wassette spawns actual subprocesses for each spawn() call
  2. Wasmtime Accessor::spawn: Use wasmtime's concurrent task API to run interpreters in background
  3. Hybrid: Lightweight tasks via wasmtime, heavy processes via OS

Use Cases

  • AI coding agents: Start a dev server, test it, then shut it down
  • Data pipelines: Run background ETL jobs while agent monitors progress
  • Interactive tools: REPLs, debuggers, or any stateful CLI tool

Questions for Maintainers

  1. Does this align with Wassette's goals?
  2. Would this be better as a core feature or a separate "process" capability?
  3. Any concerns about security model (sandboxing spawned processes)?
  4. Interest in a PR, or should this wait for WASI 0.3 async?

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions