@acpjs/core
v0.8.0
Published
acpjs AcpHost: agent process lifecycle, ACP connection, event normalization/numbering, fs/terminal defaults, permission routing, restart and StorageAdapter scheduling.
Readme
@acpjs/core
Node AcpHost runtime. Spawns ACP agent subprocesses over the official SDK, normalizes protocol notifications into numbered @acpjs/protocol events, replays the log for late subscribers, routes permissions, provides default fs, opt-in terminal handling, crash recovery, and StorageAdapter scheduling.
Install
pnpm add @acpjs/coreESM-only, node >= 24. Runtime deps: @acpjs/protocol, @agentclientprotocol/sdk, zod.
Usage
import { createAcpHost } from '@acpjs/core'
const host = createAcpHost({ restart: 'on-crash' })
const agent = await host.spawnAgent({
id: 'my-agent',
command: 'npx',
args: ['some-acp-agent'],
})
const { sessionId } = await host.createSession(agent.agentId, {
cwd: process.cwd(),
mcpServers: [],
additionalDirectories: [],
})
const unsubscribe = host.subscribe(sessionId, 0, (event) =>
console.log(event.seq, event.type),
)
const result = await host.prompt(sessionId, [{ type: 'text', text: 'hello' }])
unsubscribe()
await host.closeSession(sessionId)
await host.dispose()Exports
createAcpHost(options?): AcpHost(theAcpHostclass is also exported)createHostEndpoint(host): EnvelopeEndpoint— wraps a host as the HostClientTransport endpoint for@acpjs/client.AcpError— carries a host-boundarycodefromACPJS_ERROR_CODES(acpjs/*).resolveHostOptions(options),resolveAgentDefinition(definition)— config pipeline; validation failures throwAcpError(acpjs/config-invalid); resolved products are frozen.deriveClientCapabilities(fs, terminal)— reports only the methods a handler implements.normalizeSessionUpdate(update): NormalizedUpdate— maps the 13SessionUpdatevariants to event type/payload/extensions; unmodeled →unrecognized-update.createMemoryStorage(),createJsonlStorage(file)— built-inStorageAdapters.createDefaultFsHandler(),createDefaultTerminalHandler().- Types:
HostOptions,ResolvedHostOptions,AgentDefinition,ResolvedAgentDefinition,FsHandler,TerminalHandler,RestartBackoff,AgentSnapshot,SessionSnapshot,CreateOrLoadSessionParams,ResumeSessionParams,CreateSessionResult,SessionConfigValue,PromptResult,EventSubscriber,StorageAdapter,SessionMeta,NormalizedUpdate.
AcpHost methods
- Agents:
spawnAgent(definition),getAgent(agentId),getAgents(),disposeAgent(agentId) - Auth:
authenticate(agentId, methodId)sends theauthenticateRPC;logout(agentId)gated onauth.logoutcapability (elseacpjs/capability-unsupported). Sends RPC only — does not pick method, store credentials, or track login state. - Sessions:
createSession(agentId, { cwd, mcpServers, additionalDirectories }),loadSession(...),resumeSession(...),listSessions(agentId, { cursor?, cwd? }),deleteSession(agentId, sessionId),prompt(sessionId, ContentBlock[]),cancel(sessionId),closeSession(sessionId),setMode(sessionId, modeId),setConfigOption(sessionId, configId, value),getSession(sessionId),getSessions() - Events:
subscribe(sessionId | undefined, fromSeq, callback)— passundefinedfor the host stream (agent/session/permission projections + diagnostics). - Permissions:
respondPermission(requestId, outcome) - Recovery:
restoreSessions()— rebuildsdisconnectedsessions from storage. dispose(),disposeAgent(agentId)(idempotent; sessions →disconnected, agent removed from registry, emitsagent-removed).
HostOptions
| Field | Default | Notes |
| ---------------- | ---------------------------------------------- | -------------------------------------------------------------------------- |
| restart | 'never' | 'on-crash' restarts only on a crashed exit. |
| restartLimit | 3 | Max consecutive restarts; ready resets the counter. |
| restartBackoff | { initialMs: 1000, factor: 2, maxMs: 30000 } | Exponential backoff. |
| storage | in-memory | StorageAdapter. |
| fs | built-in Node fs | Replaced wholesale when injected; drives the initialize capability report. |
| terminal | disabled | Requires a complete handler with cleanupSession. |
| killTimeoutMs | 5000 | dispose graceful-shutdown timeout; SIGKILL after. |
Frozen once constructed; rebuild the host to change it.
Snapshots
AgentSnapshot:{ agentId, status, restartCount, reason?, exit?, capabilities?, authMethods? }.authMethods= advertised methods frominitialize(read this to pick amethodIdforauthenticate).capabilities.auth.logoutgateslogout.SessionSnapshot:{ sessionId, status, agentId?, cwd, mcpServers?, additionalDirectories, agentDefinitionId?, title?, updatedAt? }.
Host stream diagnostics (code values)
agent/spawn, agent/spawn-failed, agent/initialized, agent/initialize-failed, agent/exit, agent/process-error, agent/stderr, agent/restart-scheduled, agent/restart-suppressed, agent/restart-exhausted, agent/kill, session/recovery-skipped, session/load-failed, storage/write-failed, event/unserializable, subscriber/error. Diagnostics never participate in SessionState reduction. agent/spawn records env key names only, never values.
Key semantics
- agentId / requestId:
agent-<n>/perm-<n>, monotonic per host lifetime, never reused. - cwd:
AgentDefinition.cwddefaults to host process cwd; absolutized viapath.resolve. - prompt protocol errors:
promptrejects on agent JSON-RPC errors (does not fabricate aStopReason); envelope callers receiveacpjs/agent-errorwith the original error indata. - normalization key-omission:
nulloptional fields are omitted, exceptsession_info_updatetitle/updatedAt(clear) andtool_call(_update)rawInput/rawOutput(passthrough). Top-level_meta→extensions._meta; other unknown fields →extensions.<key>. - capability gating:
session/list|resumechecksessionCapabilities.<x> != null;loadSessionchecks the top-level boolean;set_mode/set_config_optioncheck whether modes/configOptions were ever seen. Local close/delete always available; remote close/delete is best-effort and resolves on the local tombstone (does not wait for ACP ACK). - load/resume staging: unknown load/resume is invisible until the RPC succeeds.
loadbuffers replayed updates, then emitssession-reset+ replay + config + active;resumerejects replayed history. - restart pre-ready failures: during a restart cycle, spawn/initialize failures keep consuming restart budget; the first (non-cyclic) failure is not retried.
- storage: event writes are queued; failures emit
storage/write-failed(not retried). Close/delete tombstones are strict commits — if unwritable, the API rejects.restoreSessionsskips closed/deleted and non-clone-safe events; restored sessions aredisconnected. - terminal ownership: the host records the owning
sessionIdperterminalIdand rejects cross-session terminal ops withacpjs/invalid-params. - protocol version:
initializeresponseprotocolVersion !== PROTOCOL_VERSION→ process killed, judgedinitialize-failed(no downgrade). - unserializable payloads: events failing structured clone are rejected →
event/unserializablediagnostic.
