Skip to content

Latest commit

 

History

History
223 lines (147 loc) · 15.8 KB

File metadata and controls

223 lines (147 loc) · 15.8 KB

Caplets Architecture

Caplets is a Code Mode and capability gateway for coding agents. It turns configured backends into Caplet handles, optional progressive wrapper tools, and optional direct MCP surfaces.

Source code is authoritative. This document summarizes the architecture, but implementation details in packages/core, public client details in packages/sdk, generated schemas, tests, and package entrypoints win when docs drift.

Runtime Layers

Configuration And Caplet Sources

The core config loader accepts user config, project config, and Markdown Caplet files. The schema source of truth is Zod in packages/core/src/config.ts, with generated JSON Schemas in schemas/.

Supported backend families are:

  • mcpServers
  • openapiEndpoints
  • googleDiscoveryApis
  • graphqlEndpoints
  • httpApis
  • cliTools
  • capletSets

Project sources override user/global sources. Source-aware inspection reports where each Caplet came from and warns when one Caplet shadows another.

Engine

packages/core/src/engine.ts owns the active config, backend managers, config reload behavior, and execution dispatch. Reload keeps the last known-good config if parsing or validation fails.

Backend managers provide a common shape for listing tools, searching tools, describing exact tools, calling tools, and checking readiness. MCP-backed Caplets additionally support resources, resource templates, prompts, and completion.

Exposure Policy And Projection

packages/core/src/exposure/policy.ts resolves one exposure value into three booleans:

  • codeMode
  • progressive
  • direct

The global default is code_mode. Per-Caplet config may choose direct, progressive, code_mode, direct_and_code_mode, or progressive_and_code_mode.

packages/core/src/exposure/projection.ts turns resolved discovery snapshots and attach manifests into the Caplets exposure projection: the adapter-neutral, registration-ready view of Code Mode handles, progressive tools, direct downstream operations, direct MCP surfaces, schemas, prompt arguments, resource metadata, route descriptors, hidden diagnostic breadcrumbs, and local/remote merge outcomes. MCP serving, native integrations, and attach/remote clients render this projection; they do not re-own exposure identity, namespace shadowing, registration facts, or hidden-Caplet policy.

The engine tags each projection with the config generation captured before discovery. Adapters publish only a projection that still matches the current generation, discard out-of-order discovery, and reject callbacks rendered from an older generation. Until initial or refreshed discovery resolves, Code Mode declarations and native execution allowlists fail closed rather than falling back to configured Caplet IDs.

MCP Server

packages/core/src/serve/session.ts registers the user-facing MCP surface.

  • Code Mode exposure registers one code_mode tool that runs TypeScript against generated caplets.<id> handles.
  • Progressive exposure registers one wrapper tool per Caplet.
  • Direct exposure registers discovered downstream MCP tools, resources, resource templates, and prompts.

The HTTP server in packages/core/src/serve/http.ts exposes one fixed topology at the Current Host Origin. GET / redirects to /dashboard; /.well-known/caplets links the disjoint /api, /mcp, and /dashboard namespaces. /api/openapi.json serves the OpenAPI contract, surviving v1 public operations live beneath /api/v1/*, and Admin resources live beneath /api/v2/admin/*.

Exact /mcp is the agent-facing Streamable HTTP MCP endpoint. It honors exposure policy, so a default code_mode server can expose only the code_mode tool to ordinary MCP clients.

/api/v1/attach/* is the Caplets runtime Attach API. Attached clients read /api/v1/attach/manifest, subscribe to /api/v1/attach/events, and invoke revision-scoped exports through /api/v1/attach/invoke before merging remote projections with local/project overlays. Protocol namespaces cannot be moved beneath a configured or reverse-proxy prefix.

Current Host Administration

packages/core/src/current-host/operations.ts is the Current Host administration Module. Its typed Interface accepts a trusted host-scoped Operator principal plus a semantic operation, then owns safe read models, catalog and Caplet administration, Pending Remote Login and Remote Client mutations, backend authentication, safe Vault administration, Operator activity, redaction, conditional generations, and actor-specific sessionEnded outcomes. HTTP and CLI Adapters do not own Host Storage orchestration.

One resource router mounts only under /api/v2/admin/*. Any Authorization header selects Remote Profile bearer validation exclusively; without one, a dashboard session cookie selects same-origin browser authentication and unsafe methods require the current X-Caplets-CSRF. Both modes produce the same trusted Operator principal for the route-local Zod/OpenAPI schemas and semantic handlers. Access Clients remain limited to MCP, Attach, Project Binding, and credential-owner self-revocation.

The public, cacheable /api/openapi.json describes canonical /api/v1/* and /api/v2/admin/* HTTP resources. Route-local Zod/OpenAPI definitions generate schemas/caplets-http.openapi.json, and the pinned generator writes the public HTTP client into the independent @caplets/sdk package.

The root @caplets/sdk entrypoint is the browser/Node Fetch client plus curated ordered and streaming Caplet Bundle helpers. @caplets/sdk/project-binding is the browser-safe caplets.project-binding.v1 session coordinator, while @caplets/sdk/project-binding/node computes marker-aware filesystem fingerprints. Callers create isolated clients with an explicit Current Host Origin and optional static or async authentication. The coordinator separately requires the exact /api/v1/attach/project-bindings/connect ws: or wss: URL.

Well-known discovery, MCP, dashboard login/session/logout routes, Raw Vault Reveal, and other browser-private routes are excluded from OpenAPI and the SDK. OpenAPI documents bearer and dashboard-session cookie alternatives only for /api/v2/admin/*; the generated SDK does not bootstrap browser sessions. Runtime discovery and invocation remain on Attach, and no v1 Admin Adapter exists. Remote init and add remain rejected as local filesystem operations.

Raw Vault Reveal is not a shared Admin operation. It remains a dashboard-only human confirmation path at /dashboard/api/private/vault-reveals, with same-origin session and CSRF checks, Cache-Control: no-store, and an ephemeral browser timer; bearer administration and generated clients cannot invoke it. Dashboard session cookies use Path=/; restore migrates an existing Path=/dashboard cookie, while a cookie scoped to a removed custom prefix requires a fresh login.

Caplets Daemon

packages/core/src/daemon/ owns the default per-user daemon lifecycle. caplets daemon install persists HTTP caplets serve configuration, explicit service environment variables, optional shell inheritance intent, user-only log paths, and native service descriptors under the daemon/default identity. Runtime lifecycle commands (start, restart, stop, status, logs, and uninstall) read that installed service state instead of accepting serve flags.

Top-level user serve config supplies optional HTTP defaults for foreground serve and daemon-managed serve. CLI flags and environment variables win over user config, and explicit daemon install settings win over later user-default changes. caplets daemon restart re-resolves user serve defaults for fields that were not explicit in the installed daemon config. Project config strips serve because repositories must not control a developer's local bind address, auth posture, or public origins.

The daemon uses the native per-user service manager for the host platform: launchd UserAgents on macOS, systemd --user services on Linux, and current-user Windows Scheduled Tasks on Windows. There is no detached-process fallback when a native manager is unavailable. Foreground caplets serve remains stdio/HTTP serving only.

Code Mode

Code Mode is implemented under packages/core/src/code-mode/.

The runtime generates TypeScript declarations from the current callable Caplets, statically checks the submitted script, runs it in the sandbox, bridges handle methods back to the native service, stores logs when configured, and returns JSON-serializable results with diagnostics.

Code Mode supports optional live sessions. A run without sessionId creates a fresh QuickJS heap and returns meta.sessionId; a run with a known live sessionId reuses that heap for adjacent calls. Unknown or expired session IDs are rejected before user code executes. Session heaps are runtime memory only and disappear on process restart or TTL eviction.

Recovery history is keyed by recoveryRef, which is returned in creation metadata when available. caplets.debug.readRecovery() reads redacted, bounded summaries for agents that already have that reference. A still-retained journal can also return the same reference when a known session ID was evicted by TTL, compatibility invalidation, or runtime restart while the journal remains readable. It is a setup-code reconstruction aid, not heap restoration, and unknown session IDs do not become recovery lookup paths.

Code Mode installs a browser-like, non-I/O platform surface as runtime globals for common JavaScript data shaping: base64 helpers, a minimal Buffer subset, structuredClone, URL and text encoding helpers, Web data containers such as Headers, Blob, File, FormData, streams, abort signals, Request/Response, timers, microtasks, and crypto randomness. These globals are intentionally omitted from generated Code Mode TypeScript declarations and tool prompts so the declaration payload stays focused on Caplet handles, debug helpers, and console.

Direct I/O remains routed through Caplet handles. fetch is intentionally unavailable, and Code Mode does not expose Node process, module loading, filesystem, child process, or direct network APIs.

The intended agent pattern is one compact script:

  1. choose handles
  2. inspect or check only when useful
  3. search for candidate operations
  4. describe exact operations when schemas are needed
  5. call tools
  6. filter, join, and summarize inside the script
  7. return compact decision-ready evidence

Native Service

packages/core/src/native/service.ts powers OpenCode and Pi. It uses the same engine and exposure policy as MCP serving, then exposes native tools with agent-specific prompt guidance.

caplets__code_mode is the native Code Mode entrypoint. caplets__<id> tools exist for progressive exposure. Direct native exposure registers operation-level tools named caplets__<id>__<operation>.

Remote Control

Remote control under packages/core/src/remote-control/ lets CLI and native integrations operate against a generic Current Host. Remote mode uses server-owned config, auth, and execution, with local/project overlays where supported.

Project Binding

Project Binding under packages/core/src/project-binding/ connects a local project root to a remote runtime. The foreground attach loop owns session state, heartbeat, reconnect behavior, sync preflight, and terminal recovery commands.

Native Project Binding lifecycle ordering lives in packages/core/src/native/project-binding-lifecycle.ts. The owner retains the last accepted local allowed-Caplet set, serializes and coalesces remote updates, makes cleanup the final mutation, and commits remote replacement only after the previous Adapter cleans up. Generic Current Host failures disconnect safely and permit a later registration attempt.

Current Host Binding Session records serialize heartbeat, end, expiry, prune, and shutdown mutations per record. Active socket work reauthorizes the durable Client ID at execution time, stages lease writes, and commits only after authorization, record generation, identity, and expiry remain current; terminal cleanup prevents stale or second-socket work from resurrecting a lease.

docs/project-binding.md is the living operational contract for Project Binding.

Backend Contracts

MCP

MCP-backed Caplets preserve downstream tool results and expose resources, templates, prompts, and completion when the downstream server supports them. Direct exposure can register those downstream surfaces directly.

OpenAPI, Google Discovery, GraphQL, And HTTP

OpenAPI, Google Discovery, GraphQL, and HTTP backends expose explicit operation/action tools. They do not synthesize MCP resources or prompts. HTTP-like backends enforce safe URL handling, bounded response bodies, timeouts, and redacted errors.

Google Discovery backends load local or remote Google Discovery documents, infer request base URLs from the document unless overridden, expose filtered Discovery methods as tools, and infer OAuth scopes from the exposed operation set. Google media downloads and oversized or binary HTTP-like responses are written as Caplets media artifacts under the configured artifact root instead of being forced inline.

HTTP-like backend results cross one internal Media contract. Small textual or JSON bodies use the inline variant. Non-inline results use local-artifact only when the host explicitly exposes its Caplets-managed artifact filesystem; remote boundaries use remote-reference, which carries an artifact URI and never filesystem path semantics. Backend managers produce this contract, while terminal, MCP, Attach, native, and browser Adapters own their local presentation.

Each configurable HTTP-like backend retains its configured maximum response size as a hard failure cap. The shared HTTP reader's default remains 1 MiB. GraphQL operation results use the same 1 MiB inline threshold and a separate 100 MiB artifact cap; GraphQL schema and introspection remain bounded-text control paths.

CLI Tools

CLI-backed Caplets expose curated actions only. Actions spawn declared commands and args without shell interpolation. Inputs are validated before spawn, and outputs are bounded.

Caplet Sets

Caplet sets expose another Caplets collection as a nested backend. This lets a team or repository share a Caplet catalog without flattening every child backend into the parent config.

Auth And Secrets

Auth supports none, bearer, headers, OAuth2, and OIDC where the backend family supports them. OAuth/OIDC state is stored outside config. Errors and diagnostics redact configured secrets.

Remote Profiles store generic Current Host credentials. caplets attach refreshes expired remote credentials before creating Binding Sessions and fails closed when refresh credentials are revoked.

Distribution And Deployment

The public CLI package is caplets. The native packages are @caplets/opencode and @caplets/pi.

The repo includes a source-build Dockerfile and three standalone Compose deployment descriptors for SQLite, convenient PostgreSQL, and hardened single-host PostgreSQL. Release workflows publish the service image and matching Compose files as GitHub Release assets.

CAPLETS_SERVER_URL, Remote Profile URLs, CLI URL arguments, native remote URLs, and SDK baseUrl are Current Host Origins: scheme, host, and optional port only. A reverse proxy must expose /.well-known/caplets, /api, exact /mcp, and /dashboard at the origin root; prefix-only hosting is unsupported. Deployment readiness probes use /api/v1/healthz.

Benchmark Architecture

packages/benchmarks owns deterministic and opt-in live benchmarks.

  • Deterministic benchmarks are stable, credential-free, and committed through docs/benchmarks/coding-agent.md.
  • Live benchmarks require local agents, credentials, selected models, and explicit CAPLETS_BENCH_LIVE=1.
  • Pi eval modes include Code Mode, progressive Code Mode, direct Code Mode, vanilla MCP, and Executor MCP competitors.

Benchmark output is product evidence, not runtime configuration truth. Runtime truth lives in packages/core.