Skip to content

An acp runtime: launch a named ACP command with no model, so a deterministic program can run as an agent #1634

Description

@lex00

Context

Every runtime Fountain has is an LLM coding agent: claude, codex, gemini, opencode, each a module under apps/fountain/lib/fountain/runtimes/ implementing the Fountain.Runtimes behaviour and speaking ACP (ADR 0014). That is the right shape for the product, and it leaves out a class of work Fountain is otherwise perfect for: a deterministic program that wants a warm machine, a vault, a thread, a schedule, and a human who can read what happened.

The concrete consumer is chant, which is replacing its Temporal-backed ops runtime with Fountain. A chant op is a convergent verb (chant lifecycle apply --env prod, a converge tick, a drift diff) that should run on a schedule inside a persistent sandbox that holds the repo and the toolchain, with the run visible as a turn in a teammate's thread. chant will ship an ACP server (chant acp) so it looks like any other agent to Fountain. What it cannot do from outside is be launched: the runtime registry is a closed map, Fountain.Runtimes.Model expects a provider/model_id, and a turn resolves inference credentials it will never use.

Proposal

Add one runtime, acp, that launches a command the agent config names and speaks ACP to it over stdio exactly as the four existing runtimes do.

  • agents.runtime accepts "acp". A new field, runtime_command (string, argv or a shell line, resolved inside the sandbox), is required for this runtime and rejected for the others.
  • model is optional for this runtime. No inference credential is resolved, and no model.failed stage can occur.
  • prepare_sandbox/*, write_config/2, default_env/* and skills_root/0 are no-ops or minimal: the command owns its own config. Skills still mount, since a deterministic agent may read them.
  • Permission requests, session/cancel, tool-call blocks and stop reasons work unchanged, because they are ACP.
  • Metering: with CREDITS_ENABLED the turn is priced by sandbox time only. There is no token usage to record; turn.usage is null.
  • The console and fountain agents create expose the field. fountain acp (ADR 0015) needs no change.

The one product question is whether runtime_command is a free string or a catalogue entry. A free string is what a self-hoster wants and what the trusted-mode runner already implies. On the hosted service the command runs inside the sandbox under the same isolation as any agent, so it is no wider than a setup_script. I would start with the free string.

Acceptance criteria

  • runtime: "acp" with runtime_command creates an agent; the same value without runtime_command, or runtime_command on another runtime, is a 422 that names the field.
  • A conversation on such an agent runs the command inside the sandbox, streams tool_call / tool_call_update / agent_message_chunk blocks to the transcript and the SSE feed, and ends the turn on the ACP stop reason.
  • session/cancel via POST /api/conversations/:id/interrupt reaches the command and the turn ends interrupted.
  • session/request_permission from the command produces a permission_request block and honours the agent's policy, unchanged from the LLM runtimes.
  • No inference credential is looked up and no model is required; a turn on an account with no credentials succeeds.
  • The permission policy table in docs/concepts/permissions.md and the runtime field in docs/sdk.md gain a row for acp.
  • A conformance test runs a fixture ACP agent (a few lines of Node or Elixir) through the full turn on the test sandbox provider.

Dependencies and scope

No dependency on other issues. The consumer-side work is tracked in chant; that epic link will be added here once it exists. Out of scope: a catalogue of blessed commands, any change to the four LLM runtimes, and the durable-request change filed separately.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:acpAgent Client Protocol adapters, runtime integration and protocol behavior.area:sandboxSandbox providers, machine identity, capabilities and lifecycle.needs:decisionRequires a product, business or architecture decision before proceeding.type:featureAdds or improves product behavior.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions