Skip to content

feat: add per-session sandbox workspace for microservice mode - #49

Merged
schplitt merged 6 commits into
mainfrom
feat/sandbox-namespace
Jul 23, 2026
Merged

feat: add per-session sandbox workspace for microservice mode#49
schplitt merged 6 commits into
mainfrom
feat/sandbox-namespace

Conversation

@schplitt

Copy link
Copy Markdown
Owner

Summary

Adds a sandbox global inside codemode for microservice (server) mode: an in-memory shell + virtual filesystem for wrangling data fetched from the Cumulocity API. It mirrors Flue's SandboxApi plus an mc8yp-only clear(), and is backed by a swappable adapter (first backend: just-bash, core shell only — jq/awk/grep/sed/sort/sqlite3; no network, no host filesystem — it never reaches Cumulocity).

Built on top of the codemode-namespaces work (already merged via #47); this PR is only the sandbox diff.

Behaviour

  • Server-only. One sandbox per MCP session, keyed by ctx.sessionId, so files persist across codemode calls within a session. CLI mode does not expose it — local agent harnesses bring their own file I/O.
  • Eviction, in-memory only (never touches disk):
    • 15-minute idle TTL per session, reset on every use (unref'd timer) — the guarantee.
    • Clean-close: client DELETE /mcp → transport info.delete(id) → sandbox dropped immediately, via an InfoSessionManager wrapper wired in src/index.ts.
  • Discovery. Surfaces as a peer entry in codemode.describe() and describe("sandbox") returns its typed interface; a purpose note in the tool description + code-mode-guide prompt frames it as the workspace for saving files / processing data.
  • Swappable seam. SandboxAdapter is kept identical to Flue's SandboxApi; clear() and lifecycle live one layer up, so the backend can be replaced without touching agent-facing code.

Notable details

  • Bumps @iso4/sandbox to 0.3.1 (fixes Uint8Array sandbox→host transfer). Date is now rejected by the wire codec, so stat().mtime is exposed as epoch milliseconds.
  • just-bash (+@tmcp/session-manager) added as deps and kept external in the bundle (just-bash lazy-loads WASM runtimes for its optional commands); session-eviction.ts is imported only by the server entrypoint, so @tmcp/session-manager never enters the CLI bundle.
  • just-bash runs with cwd: '/' so relative paths resolve identically in the fs methods and the shell.

Tests

  • test/sandbox-adapter.test.ts — adapter (fs round-trip, shell pipelines, jq, fs↔shell path alignment, isolation) + per-session store (15-min eviction, timer reset-on-use, per-session eviction, clean-close DELETE hook).
  • test/excute.test.ts — real-sandbox integration: absent in CLI, absent server-without-session, present in server, persistence across calls, session isolation, clear(), binary round-trip, mtime as number, describe gating.

Validation: pnpm test:run (all pass), pnpm lint (0 errors), pnpm typecheck (clean), pnpm build (all variants).

Docs: AGENTS.md and README.md updated.

Expose a `sandbox` global inside codemode in microservice (server) mode: an
in-memory shell + virtual filesystem (just-bash, core shell only —
jq/awk/grep/sed/sort/sqlite; no network, no host FS) for wrangling data fetched
from the API. Mirrors Flue's SandboxApi plus an mc8yp-only clear().

- Server-only: one sandbox per MCP session (keyed by ctx.sessionId); files
  persist across codemode calls. CLI does not expose it — local harnesses bring
  their own file I/O.
- Eviction: 15-min idle TTL (reset on use) plus clean-close on client DELETE via
  an InfoSessionManager wrapper. In-memory only, never touches disk.
- Swappable adapter seam (SandboxAdapter = Flue shape); just-bash is the first
  backend, kept external in the bundle (lazy WASM optional runtimes).
- Discovery: appears in codemode.describe() as a peer surface; a purpose note in
  the tool description + prompt frames it as the workspace for saving files and
  processing data.
- Bump @iso4/sandbox to 0.3.1 (fixes Uint8Array sandbox->host; Date is now
  rejected by the wire codec, so stat().mtime is exposed as epoch ms).

Tests: adapter unit tests, per-session store (TTL / reset-on-use / isolation /
clean-close), and execute integration (present/absent/persist/clear/describe).
@schplitt
schplitt force-pushed the feat/sandbox-namespace branch from c67d543 to b5af46a Compare July 23, 2026 06:51
schplitt added 5 commits July 23, 2026 08:56
Both were dead weight from the interfaces work — neither is imported anywhere
in src, tests, config, or scripts. Removing the devDependency and the dangling
catalog entries.
@schplitt
schplitt merged commit c8f1fbe into main Jul 23, 2026
1 check passed
@schplitt
schplitt deleted the feat/sandbox-namespace branch July 23, 2026 07:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant