Skip to content

Support per-session/per-agent instances with persistent Store lifetime #615

@JMLX42

Description

@JMLX42

Summary

Currently, Wassette creates a fresh Store for each tool call, which means all WebAssembly linear memory state is reset between calls. This proposal requests an optional mode where a Store can persist across multiple calls within a session or agent context.

Motivation

1. Consistent Multi-Turn MCP Tool Calls

Many MCP tools benefit from maintaining state across a conversation. For example:

  • A database query tool that keeps a connection pool
  • A stateful interpreter (Python REPL, shell session)
  • A tool that accumulates context or learns from previous calls

Currently, each call_tool invocation starts fresh, making it impossible for the WASM component to maintain any in-memory state between calls from the same agent.

2. Per-Instance MCP Resources

MCP's resources capability allows servers to expose data that clients can read. Resources often represent stateful entities:

  • File handles or cursors
  • Session-specific data
  • Cached computation results

With per-call Store isolation, resource state cannot persist, limiting the expressiveness of WASM-based MCP servers.

3. Efficient Resource Utilization

Some components perform expensive initialization (loading models, parsing large configs, establishing connections). Re-doing this on every call is wasteful when multiple calls will be made within the same agent session.

Current Architecture

Wassette already optimizes compilation via InstancePre:

pub struct ComponentInstance {
    component: Arc<Component>,
    instance_pre: Arc<InstancePre<WassetteWasiState<WasiState>>>,
    // ...
}

But in execute_component_call(), a fresh Store is created per call:

let mut store = Store::new(self.runtime.as_ref(), state);
let instance = component.instance_pre.instantiate_async(&mut store).await?;
func.call_async(&mut store, &argument_vals, &mut results).await?;
// Store dropped here - state lost

Proposed Solution

Add an optional session/agent-scoped instance mode:

  1. Session Registry: Maintain a map of (session_id, component_id) -> (Store, Instance)
  2. Lifecycle Hooks:
    • create_session(session_id) - instantiate components for a session
    • destroy_session(session_id) - clean up Stores
    • Or automatic cleanup via TTL/LRU
  3. Execution Mode: New method like execute_component_call_in_session(session_id, ...) that reuses the existing Store

This would be opt-in, preserving the current stateless behavior as the default for security-sensitive use cases.

Alternatives Considered

  • External state via WASI APIs: Components could use wasi:keyvalue or similar, but this externalizes state management and adds complexity for simple in-memory use cases.
  • Component-managed serialization: Components could serialize/deserialize state on each call, but this is inefficient and burdensome for component authors.

Use Case Example

An agentic runtime instantiates multiple agents, each with their own MCP server connections. With per-session instances:

  • Agent A's calls to a "notes" tool accumulate in Agent A's memory
  • Agent B gets a separate instance with its own state
  • Component compilation remains shared across all agents

Disclosure: This issue was drafted with assistance from Claude (AI).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrustPull requests that update rust code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions