Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/aipoch/open-science/llms.txt

Use this file to discover all available pages before exploring further.

The AcpRuntime class in src/main/acp/runtime.ts is the heart of Open Science. It owns the agent subprocess, manages one or more concurrent ACP sessions, streams typed events to the renderer as the agent works, intercepts tool-call permission requests, and coordinates the artifact and notebook MCP servers injected into every session. Everything the researcher sees in the chat transcript — messages, tool calls, plans, permission prompts, generated files — originates as an AcpRuntimeEvent emitted by this class.

Connection Lifecycle

1

connect()

Spawns the agent subprocess via spawnClaudeAgentAcp() and wraps its stdin/stdout in an acp.ndJsonStream. Sends an agent.initialize request advertising client capabilities (filesystem read/write, session config options, plans). Sets status to connecting, then connected on success or error on failure. A connectInFlight guard prevents duplicate concurrent connect calls.
await runtime.connect({ cwd: '/path/to/project' })
2

createSession()

Calls ensureConnected(), then builds a protocol session via connection.agent.buildSession(). At creation time it injects both MCP servers — the artifact server scoped to a freshly generated artifactSessionId, and the notebook server scoped to a notebookSessionId — using createMcpServers(). Also appends artifact and notebook guidance to the session’s system prompt via _meta.systemPrompt.append without touching user prompts.
const { sessionId } = await runtime.createSession({ cwd, projectName: 'my-project' })
3

sendPrompt()

Activates a new artifact run with activateArtifactRun(), which writes a current-run.json handoff file so the artifact MCP server knows which runId to attribute writes to. Streams updates via activeSession.nextUpdate() in a loop, converting each SessionNotification to a typed AcpRuntimeEvent. On stop, calls emitArtifactRunEvent() to publish any pending artifact files before emitting the final stop event.
await runtime.sendPrompt({ sessionId, text: 'Analyze the uploaded CSV and plot a histogram.' })
4

disconnect()

Cancels all pending permissions, disposes every active session, closes the ACP connection, and kills the agent subprocess. Emits a final closed status. Incrementing connectionGeneration ensures any in-flight connect promises from a previous generation are invalidated and discarded.
await runtime.disconnect()

ACP Connection States

The status field of AcpStateSnapshot is typed as AcpConnectionStatus (defined in src/shared/acp.ts). The renderer uses this to drive connection UI and gate prompt input.
StateDescription
idleRuntime is initialized but no agent subprocess has been started yet.
connectingSubprocess is being spawned and the agent.initialize handshake is in progress.
connectedThe protocol connection is live and the runtime is ready to create sessions and accept prompts.
errorThe connection attempt failed or the subprocess exited unexpectedly. AcpStateSnapshot.error carries the message.
closedThe connection was cleanly torn down by a disconnect() call or by the remote end closing the stream.

Runtime Event Kinds

Every activity the agent performs — or that the runtime itself generates — is published as an AcpRuntimeEvent with a kind field typed as AcpRuntimeEventKind. The renderer maps each kind to a distinct visual treatment in the transcript.
KindDescription
systemInternal runtime notifications: agent initialized, session created/resumed/deleted, process stderr output. Not produced by the agent model itself.
messageA text message from the agent (role: 'assistant') or the researcher (role: 'user').
thoughtThe agent’s internal reasoning text, shown collapsed in the transcript.
toolA tool call made by the agent, carrying toolKind, toolCallId, toolContent, and toolLocations for typed activity rendering.
planA structured plan the agent emits before beginning multi-step work.
permissionA permission request surfaced by the AcpPermissionBroker; the renderer renders an approval UI until the researcher responds.
artifactEmitted just before stop when the run produced files. Carries runId, artifactSessionId, artifactClaimId, and the list of ArtifactFile objects so the renderer can attach them to the finishing message.
errorA prompt failure, connection error, or artifact cleanup error.
stopThe final event of a prompt turn, carrying the stopReason and raw PromptResponse.
rawA pass-through for low-level protocol notifications that don’t map to a typed kind.
The in-memory event log is bounded to the most recent 500 events (MAX_EVENTS = 500). Older events are evicted as new ones arrive, keeping the renderer payload from growing unboundedly across long sessions.

State Snapshot

getSnapshot() returns an AcpStateSnapshot — the complete, serializable view of runtime state that is broadcast to the renderer on every meaningful change.
FieldTypeDescription
statusAcpConnectionStatusCurrent connection state (idle, connecting, connected, error, closed).
cwdstringThe resolved working directory of the most recently active session.
sessionIdstring?The session most recently targeted by a prompt or session switch.
sessionIdsstring[]All session IDs currently tracked by the runtime.
errorstring?Human-readable error message when status is error.
eventsAcpRuntimeEvent[]Bounded event log (last 500 events).
pendingPermissionsAcpPermissionRequest[]All permission requests awaiting a renderer response.
promptInFlightbooleantrue when any session has a prompt in progress.
promptInFlightSessionIdsstring[]IDs of sessions that currently have a prompt in flight.

Permission Broker

The AcpPermissionBroker (in src/main/acp/permission-broker.ts) sits between the ACP protocol’s client.session.requestPermission handler and the renderer’s approval UI. When the agent requests permission for a higher-risk tool call, the broker:
  1. Generates a unique requestId and wraps the raw ACP permission request into an AcpPermissionRequest.
  2. Stores the request in a pendingRequests map and emits it as a permission event so the renderer renders an approval card.
  3. Holds the underlying promise open until respondToPermission() is called from the renderer with an AcpPermissionResponse.
  4. Resolves the promise with outcome: 'selected' (approved) or outcome: 'cancelled' (denied/cancelled).
On disconnect(), cancelAll() cancels every pending request so the agent subprocess is never left waiting for a response that will never arrive. cancelForSession() targets only one session’s pending permissions, used when a single session is deleted while others remain active.

Attachment Handling

When a prompt includes file attachments (images, text, data files), createPromptContent() converts each UploadedAttachment into an appropriate ACP ContentBlock type:
  • Images (image/* MIME type) — read from disk and embedded as base64 { type: 'image', data, mimeType, uri } blocks so vision-capable agents receive actual pixel data.
  • Small text and JSON files (up to MAX_EMBEDDED_TEXT_UPLOAD_BYTES = 1 MiB) — embedded as { type: 'resource', resource: { uri, mimeType, text } } blocks for direct reading.
  • Large or binary files — passed as { type: 'resource_link', uri, name, mimeType, size } so the agent can decide how to fetch or reference them without inflating the prompt payload.
The MAX_EMBEDDED_TEXT_UPLOAD_BYTES threshold (1 MiB) keeps the ACP prompt payload manageable. Files that exceed this limit — even if they are plaintext — are sent as resource_link references rather than being inlined.

Build docs developers (and LLMs) love