Skip to content

Releases: DaniAkash/agent-toolkit

agent-mcp-manager-v0.0.3

26 Jun 13:15
8bf2bc8

Choose a tag to compare

Pre-release

Closes #61. Codex's ~/.codex/config.toml accepts streamable HTTP MCP servers directly per the official OpenAI Codex MCP docs. The v0.0.2 catalog marked codex as stdio-only, which forced every codex link through an npx -y mcp-remote <url> stdio bridge. That fails silently when Node is not on the user's machine. This release surfaces the http path so the shim is no longer needed.

What changes

Surface Before (0.0.2) After (0.0.3)
codex.supportedTransports ['stdio'] ['stdio', 'http']
link({ agent: 'codex', spec: { transport: 'http', url } }) throws UnsupportedTransportError succeeds; writes [mcp_servers.<name>] with url (and http_headers when spec.headers is set)
link({ agent: 'codex', spec: { transport: 'sse', url } }) throws still throws (codex's TOML parser has no sse schema)
transportHint('codex', ...) for sse failures "Codex only accepts stdio MCP servers..." "Codex accepts stdio and streamable-HTTP MCP servers; SSE is not parsed. If the URL actually serves streamable HTTP, re-add with transport: \"http\". If SSE-only, wrap with npx -y mcp-remote <url> as stdio."

Existing stdio entries written by 0.0.2 keep working identically. No migration needed for callers who only use stdio specs.

On-disk shape (new http path)

[mcp_servers.figma]
url = "https://mcp.figma.com/mcp"

[mcp_servers.figma.http_headers]
"X-Figma-Region" = "us-east-1"

Registering a remote MCP for Codex

Pass an http spec directly. No mcp-remote shim needed:

await mgr.add({
  name: 'figma',
  spec: {
    transport: 'http',
    url: 'https://mcp.figma.com/mcp',
    headers: { 'X-Figma-Region': 'us-east-1' },
  },
})
await mgr.link({ serverName: 'figma', agent: 'codex' })

Out of scope for this release

  • bearer_token_env_var and env_http_headers. Codex's TOML schema accepts both, but McpHttpSpec has no env-var-name field today. Hand-edit the TOML if you need env-sourced bearer tokens.
  • startup_timeout_sec / tool_timeout_sec. Codex defaults (10s / 60s) apply.
  • env on codex stdio entries. Not emitted in 0.0.2 either (matches upstream docker/mcp-gateway's codex_handler.go).
  • claude-desktop stays stdio-only. Anthropic's local config parser still validates stdio-shape only for local servers.

Downstream

Consumers whose install flow gates on surface.supportedTransports.includes('http') (BrowserOS's apps/server/src/lib/mcp-manager/service.ts:planFor is the reference case) now hit the http branch for codex automatically. The npx mcp-remote runtime dependency disappears for codex users.

Links

agent-mcp-manager-v0.0.2

19 Jun 21:07
0349e4a

Choose a tag to compare

Pre-release

Closes two production bug reports surfaced while installing remote MCP servers (BrowserOS) across coding agents. Both are rooted in the same gap: upstream docker/mcp-gateway writes one stdio entry per client by Go-type, so it never modelled per-agent transport capability. This library had generalised to "write any transport per caller spec" without adding the per-agent gate, which let it produce shapes some agents reject.

What changes by agent

Agent What changes
claude-desktop link() with an http/sse spec now throws UnsupportedTransportError at the API boundary instead of writing a {url, headers} block Claude Desktop will silently skip on next launch with "not a valid MCP server configuration." stdio specs unchanged.
codex Same typed enforcement, for parity. Codex was already stdio-only via the TOML emitter; the rejection is now a typed error you can catch in a fan-out loop.
claude-code system (~/.claude.json) Unchanged. Still accepts stdio, sse, and http with no type tag.
claude-code project (.mcp.json) Now stdio-only AND injects type: "stdio", matching upstream Docker's set: .mcpServers[$NAME] = $JSON+{"type":"stdio"}. Newer Claude Code releases require this tag; the library was silently producing entries those releases would skip.
vscode Was hardcoding type: "stdio" on every entry (wrong for http/sse). Now writes type: matching spec.transport. Stdio callers see no change.
cursor, gemini, zed Unchanged.

What changes library-wide

  • UnsupportedTransportError (new typed error). Includes agent, transport, details.supported, and an mcp-remote shim hint. Exported from the package root.
  • McpTransport (new public type, just 'stdio' | 'sse' | 'http').
  • link({ allowOverwrite: true }) (new opt-in). Bypasses the foreign-entry guard, takes ownership of an existing on-disk entry, and records it in the manifest. Default false. Does NOT bypass the transport-capability check.
  • getCatalogEntry and resolveAgentSurface newly exported. Use resolveAgentSurface(id, scope).supportedTransports to pre-filter agents by transport capability before linking; it honours scope-specific overrides (Claude Code project scope is stdio-only, system is not).

Recovering from ForeignEntryError

If link() throws Cannot replace a user-edited entry. Please remove <name> from this agent's config manually and try again, it means the on-disk config already has an entry under that name the workspace manifest did not write. Two options:

// Take ownership and rewrite with your spec:
await mgr.link({
  serverName: 'BrowserOS',
  agent: 'claude-code',
  allowOverwrite: true,
})

// Or remove manually and retry without the flag.

Registering a remote MCP server in a stdio-only agent

claude-desktop, codex, and claude-code project scope only accept stdio entries. Wrap the URL with mcp-remote:

await mgr.add({
  name: 'browseros',
  spec: {
    transport: 'stdio',
    command: 'npx',
    args: [
      '-y',
      'mcp-remote',
      'https://browseros.example.com/mcp',
      '--header',
      `Authorization: Bearer ${process.env.BROWSEROS_TOKEN}`,
    ],
  },
})

for (const a of (await detectInstalledAgents()).filter((a) => a.installed)) {
  await mgr.link({ serverName: 'browseros', agent: a.id })
}

The library does not auto-shim through mcp-remote in this release. Auto-shim is on the v0.2 roadmap so the manifest stays a faithful record of intent.

Pre-filter pattern (scope-aware)

import { resolveAgentSurface, type AgentScope, type McpTransport } from 'agent-mcp-manager'

const scope: AgentScope = 'system'
const supports = (id, t: McpTransport) =>
  resolveAgentSurface(id, scope).supportedTransports.includes(t)

for (const a of (await detectInstalledAgents()).filter((a) => a.installed)) {
  if (!supports(a.id, 'http')) continue
  await mgr.link({ serverName: 'github', agent: a.id })
}

Upstream Docker conformance

Now matches the per-agent on-disk shape upstream docker/mcp-gateway writes for every agent in our catalog. Three known deltas remain by design or are deferred:

  • VS Code: we write three transports; upstream writes one. Intentional (VS Code accepts all three when tagged correctly).
  • Zed spread order (defaults vs caller-supplied): deferred to v0.2.
  • Missing agents (Cline, Continue.dev, Goose, LM Studio, OpenCode, Sema4, Crush, Kiro): planned for v0.2.

Out of scope for 0.0.2 (planned)

  • Auto-shim http/sse via mcp-remote for stdio-only agents.
  • Case-insensitive name matching (manifest schema change).
  • adopt() primitive that preserves the on-disk shape verbatim instead of rewriting.

Links

ai-sdk-microsandbox-v0.0.1

18 Jun 12:23
10e70f4

Choose a tag to compare

Pre-release

First public release of ai-sdk-microsandbox.

A Vercel AI SDK v7 HarnessV1SandboxProvider backed by microsandbox. Drop-in alternative to @ai-sdk/sandbox-vercel at the sandbox: slot of HarnessAgent, running coding agents in local microVM isolation instead of Vercel-hosted sandboxes. The bridge, the agent CLI, and any files the agent touches stay on-host. Cross-process resume works via an on-disk snapshot cache.

Install

npm install ai-sdk-microsandbox @ai-sdk/harness @ai-sdk/harness-codex ai microsandbox

Quickstart

import { HarnessAgent } from '@ai-sdk/harness/agent'
import { createCodex } from '@ai-sdk/harness-codex'
import { createMicrosandbox } from 'ai-sdk-microsandbox'

const agent = new HarnessAgent({
  harness: createCodex({
    auth: { openai: { apiKey: process.env.OPENAI_API_KEY } },
  }),
  sandbox: createMicrosandbox({
    image: 'node:22-bookworm-slim',
    cpus: 1,
    memory: 1024,
    workdir: '/root',
    ports: [{ host: 4000, guest: 4000 }],
    bootstrapPreCommands: [
      'apt-get update -qq && apt-get install -y --no-install-recommends ca-certificates >/dev/null && update-ca-certificates -f >/dev/null',
      'corepack enable pnpm',
    ],
  }),
})

const session = await agent.createSession()
try {
  const result = await agent.generate({
    session,
    prompt: 'Use bash to create /root/hi.txt containing "hello".',
  })
  console.log(result.text)
} finally {
  await session.destroy()
}

Requirements

  • Linux with KVM enabled, or macOS on Apple Silicon
  • Run microsandbox setup once on the host

Verified end-to-end

This release was validated against real Codex turns through real OpenAI on a clean machine:

  • Unit suite: 216 / 216 pass
  • Integration suite (real microVM): 23 / 23 pass
  • E2E suite (real Codex + real OpenAI): 15 / 15 pass

Covers: text generation, streaming, bash tool through the bridge, file I/O round-trips, multi-turn context preservation, distinct sandboxes per session, cross-process resume via session.detach() + agent.createSession({ resumeFrom }), abort cancellation, and the configuration matrix (workdir, env, cpus, memory, network policy).

What's in the box

  • createMicrosandbox(settings) provider with both create-mode (fresh microVM per session) and wrap-mode (caller-owned sandbox)
  • Filesystem-level snapshot cache keyed on a stable identity hash so the bootstrap recipe runs once per identity across processes
  • bootstrapPreCommands setting for image-prep steps (e.g. corepack enable pnpm, apt-get install ca-certificates) that get captured into the snapshot
  • resumeSession implementation that dispatches between handle.connect() (running) and handle.start() (stopped) so resume works for both detach() and stop() payloads
  • Network policy translation (allow-all / deny-all / custom with allowedHosts, allowedCIDRs, deniedCIDRs) into microsandbox's NetworkPolicyBuilder at create-time
  • Configurable cache root via AI_SDK_MICROSANDBOX_CACHE_DIR; defaults to the OS-conventional cache directory

Status and limitations

  • Alpha. The exported API may shift before 1.0.0.
  • Runtime network policy updates are unavailable; policy is sealed at create-time.
  • Concurrent multi-session usage on one provider is bounded by the host-port mapping (two forks cannot bind the same host port simultaneously).
  • Snapshot pruning is manual today; remove the cache directory to reset.
  • The chosen workdir must already exist in the image, or be created via bootstrapPreCommands.

Links

acpx-ai-harness-v0.0.1

17 Jun 08:43
931629d

Choose a tag to compare

Pre-release

First public release of acpx-ai-harness: a Vercel AI SDK v7 HarnessV1 adapter built on the acpx runtime. Brings any ACP-protocol agent (Claude Code, Codex, Gemini, Copilot, Cursor) into the AI SDK Harnesses ecosystem alongside @ai-sdk/harness-claude-code, @ai-sdk/harness-codex, and @ai-sdk/harness-pi.

Experimental. Both this package and the upstream @ai-sdk/harness API are pre-1.0 and subject to breaking changes.

Install

npm install acpx-ai-harness @ai-sdk/harness ai acpx

@ai-sdk/harness, ai, and acpx are declared as peer dependencies. A sandbox provider with port exposure is also required (see Sandbox setup below).

Quick start

import { HarnessAgent } from '@ai-sdk/harness/agent'
import { createVercelSandbox } from '@ai-sdk/sandbox-vercel'
import { createAcpxHarness } from 'acpx-ai-harness'

const harness = createAcpxHarness({
  agent: 'codex',
  auth: { openai_api_key: process.env.OPENAI_API_KEY! },
})

const agent = new HarnessAgent({
  harness,
  sandbox: createVercelSandbox({ runtime: 'node22', ports: [4001] }),
})

const session = await agent.createSession()
try {
  const result = await agent.stream({ session, prompt: 'Refactor user.ts to use Result<T, E>' })
  for await (const part of result.fullStream) {
    if (part.type === 'text-delta') process.stdout.write(part.delta)
  }
} finally {
  await session.destroy()
}

What's in 0.0.1

Agents

The harness's bootstrap recipe pre-warms the ACP wrapper binary acpx invokes for each known agent. Vercel sandbox keys snapshots by the recipe hash, so the install cost is paid once and amortised across sessions.

agent setting Bootstrap pre-warm settings.auth key
'codex' (default) npx --yes @agentclientprotocol/codex-acp --version openai_api_key
'claude' npx --yes @agentclientprotocol/claude-agent-acp --version anthropic_api_key
'gemini' npm install -g @google/gemini-cli gemini_api_key
any other id (skipped, bring your own install) (varies, see acpx config docs)

Lifecycle

acpxHarness implements the full HarnessV1Session surface except doCompact (acpx auto-compacts internally; manual compaction has no API and the method throws HarnessCapabilityUnsupportedError). The harness picks between two recovery rungs when createSession({ resumeFrom }) or createSession({ continueFrom }) is called:

  1. ATTACH when the saved bridge coords are still live (same sandbox id, reachable WebSocket). The host reconnects to the running bridge and replays buffered events past the saved cursor.
  2. RERUN if the bridge is gone (sandbox cycled, port lost). A fresh bridge spawns and the acpx session reloads from disk via sessionKey. For continueFrom, a Continue. nudge is sent so the agent picks up where it left off.
const state = await session.detach()                 // park, bridge stays alive
const resumed = await agent.createSession({
  sessionId: session.sessionId,
  resumeFrom: state,
})
const stopState = await session.stop()               // bridge exits, state captured for resume

Permission modes

Harness mode acpx mode Behaviour
allow-all (default) approve-all Every tool call auto-approved.
allow-edits approve-all acpx's approve-all already covers the edits bucket.
allow-reads approve-reads Reads / searches auto-approved; edits and shell raise an approval request via tool-approval-request.

The host receives tool-approval-request stream parts for any tool the agent wants to call, and responds via HarnessV1PromptControl.submitToolApproval({ approvalId, approved, reason? }).

Built-in tools

The harness advertises the seven harness common-tool entries (read, write, edit, bash, grep, glob, webSearch) so cross-harness consumers can identify them. Per-agent native tool names (Claude's Bash, Codex's shell, Gemini's run_shell_command) are normalised to the common names on the wire; the original native name flows along on tool-call.nativeName.

Sandbox setup

acpx-ai-harness is a bridge-backed adapter: it spawns a Node.js process inside the sandbox that drives the ACP agent and serves a WebSocket to the host. The sandbox provider must support port exposure for the host to reach that WebSocket.

Provider Works? Notes
@ai-sdk/sandbox-vercel Yes Cloud sandbox, supports port exposure + snapshots. Requires VERCEL_TOKEN, VERCEL_TEAM_ID, VERCEL_PROJECT_ID.
@ai-sdk/sandbox-just-bash No Local sandbox, doesn't expose ports. Bridge-backed adapters reject it at start. Useful for non-bridge harnesses.

Other providers conforming to HarnessV1SandboxProvider work as long as their network sandbox session implements getPortUrl({ port, protocol: 'ws' }).

Auth

Per acpx config docs, standard provider env vars like OPENAI_API_KEY reach child processes but do not drive acpx's auth gate. Credentials must flow through acpx's own auth channels:

  • settings.auth writes ~/.acpx/config.json inside the sandbox per session.
  • The same map is exported as ACPX_AUTH_<METHOD_ID> env vars on the bridge spawn so acpx's runtime sees them on first ensureSession.

Both channels are populated per session, never via the bootstrap recipe, so credentials never end up in a Vercel sandbox snapshot.

The harness defaults authPolicy: 'fail' when auth is non-empty so a missing or invalid key surfaces immediately rather than as a downstream ACP error. Override with settings.authPolicy: 'skip' for legacy "let the adapter handle it" behaviour.

Verified end-to-end

generate() and stream() were exercised live against a real Vercel sandbox + a real codex turn before the release was cut: 2 pass / 0 fail / ~73 seconds cold start. Asserts in the smoke suite cover non-empty result.text and the presence of text-delta + finish parts on fullStream.

Current limitations

  • Host AI SDK tools are not yet forwarded to the agent. Tools passed via HarnessAgent({ tools }) emit an unsupported-tool CallWarning on stream-start and are otherwise ignored. acpx's runtime only accepts wire-protocol MCP servers (stdio / http / sse); a host-side MCP server inside the bridge needs to land before this works end to end. Stdio / http / sse MCP servers passed via the start frame's mcpServers field flow through verbatim today.
  • No manual compaction. doCompact() throws HarnessCapabilityUnsupportedError. acpx delegates compaction to the underlying agent.
  • @ai-sdk/sandbox-just-bash is rejected because just-bash cannot expose ports.

Links

agent-mcp-manager-v0.0.1

17 Jun 08:46
8875e79

Choose a tag to compare

Pre-release

First public release of agent-mcp-manager: a TypeScript library for programmatically registering Model Context Protocol servers across AI coding agents (Claude Code, Claude Desktop, Cursor, VS Code, Codex, Gemini CLI, Zed). Built for hosts (IDE plugins, internal tools, enterprise onboarding flows, custom installers) that need to wire MCP servers into a user's agent configs without shelling out to a per-agent CLI.

Alpha software. The public API may change between minor versions without notice until 1.0.0. Pin exact versions; expect rough edges.

Install

npm install agent-mcp-manager

Quick start

import { createMcpManager, detectInstalledAgents } from 'agent-mcp-manager'

const mgr = createMcpManager()

await mgr.add({
  name: 'filesystem',
  spec: {
    transport: 'stdio',
    command: 'npx',
    args: ['-y', '@modelcontextprotocol/server-filesystem', process.cwd()],
  },
})

const agents = await detectInstalledAgents()
for (const a of agents.filter((a) => a.installed)) {
  await mgr.link({ serverName: 'filesystem', agent: a.id })
}

// Later, full teardown across every linked agent:
await mgr.remove({ serverName: 'filesystem' })

Mental model

Two layers, clear split of responsibility:

  • Workspace (yours): a directory you own with a manifest.json recording every server you've added, its spec, when, and which agents you've linked it to. add() writes here; listServers() reads it.
  • Agent MCP configs (the user's): ~/.claude.json, ~/.cursor/mcp.json, ~/.codex/config.toml, etc. link() injects entries into them; unlink() removes them; listLinks() reports the ones the manifest knows about. Foreign keys at those paths are never touched.

The manifest is authoritative for intent and metadata; the on-disk config files are authoritative for current state. rescan() cross-checks the two and reports verified / broken / unmanaged entries.

What's in 0.0.1

Seven primitives

add({ name, spec })
link({ serverName, agent, configPath? })
unlink({ serverName, agent, configPath? })
remove({ serverName, unlinkFirst? })
listServers({ scanUnmanaged? })
listLinks({ agents?, serverNames?, scanUnmanaged? })
rescan({ mode?: 'merge' | 'replace' })

Detection helpers

detectInstalledAgents()
listSupportedAgents()
isAgentSupported(agent)
resolveAgentMcpConfigPath(agent, scope?, projectRoot?)

Supported agents

Agent System config (macOS) Emitter Project file
claude-code ~/.claude.json JSON mcpServers .mcp.json
claude-desktop ~/Library/Application Support/Claude/claude_desktop_config.json JSONC mcpServers n/a
cursor ~/.cursor/mcp.json JSON mcpServers .cursor/mcp.json
vscode ~/Library/Application Support/Code/User/mcp.json JSON servers (injects type: stdio) .vscode/mcp.json
gemini ~/.gemini/settings.json JSON mcpServers n/a
codex ~/.codex/config.toml TOML mcp_servers n/a
zed ~/.config/zed/settings.json JSON context_servers (injects source: custom, enabled: true) n/a

More agents (Cline, Continue.dev, OpenCode, Goose, Crush, LMStudio, Kiro, Sema4) are planned for v0.2.

Transports

  • stdio is fully supported.
  • sse and http types are exported and the JSON emitters write them correctly, but v0.1 tests skip non-stdio link flows. Treat as preview. Codex stays stdio-only because its upstream TOML config is stdio-only.

How this relates to docker/mcp-gateway

agent-mcp-manager derives its agent catalog from docker/mcp-gateway's pkg/client/config.yml (MIT-licensed). The per-OS paths, install-check heuristics, and config-file shapes mirror the upstream entries. Key differences:

  • TypeScript-native emitters instead of yq expressions. JSONC clients use jsonc-parser; Codex uses @iarna/toml. JSONC comment preservation is built in.
  • General-purpose, not Docker-specific. Docker's CLI plugin writes a single fixed MCP_DOCKER entry pointing at its gateway. This library writes arbitrary MCP server entries from caller-supplied specs.
  • Manifest-backed: a manifest.json records which entries the library wrote so unlink() can refuse to clobber user-owned keys.

See THIRD_PARTY_NOTICES.md for upstream attribution.

Errors

Every error subclasses McpManagerError:

Error When
AgentNotSupportedError Unknown agent id
ServerNotFoundError Operation on a server name absent from the manifest
ForeignEntryError unlink sees an entry the manifest didn't write
InvalidServerSpecError Spec fails validation (e.g. http transport with no url)
UnresolvedConfigPathError OS or env vars do not yield a valid config path

Secrets

The manifest stores the server spec verbatim, including any env / headers values you pass. If your spec carries a token, the manifest holds it in plaintext at ${workspaceDir}/manifest.json. v0.2 will add optional OS-keyring indirection via keytar. For v0.1: keep your workspace dir scoped to a single user and treat manifest.json like any other secret-bearing config file.

Library, not a CLI

If you want to register a single MCP server in one of your editors interactively, use that editor's own command (claude mcp add …, the Cursor settings UI, etc.) or docker mcp client connect. This package is a programmatic API for embedders.

Links

acpx-ai-provider-v0.0.6

21 May 09:13
ffb5440

Choose a tag to compare

Pre-release

First combined release after v0.0.4 — four feature PRs land in one version since v0.0.5 was bumped but never tagged or published.

Features

  • onPermissionRequest callback on AcpxProviderSettings (#17). Hosts can intercept per-call permission requests (write, shell, delete, …) with their own UI instead of relying on the up-front permissionMode. Returning undefined falls through to the existing mode resolver — zero behavior change for consumers that don't opt in. Driven by the BrowserOS chat flow that wanted inline approve/deny CTA cards.

    createAcpxProvider({
      agent: 'codex',
      permissionMode: 'approve-reads', // fallback for unhandled cases
      onPermissionRequest: async (req) => {
        const decision = await myUi.prompt({
          title: req.raw.toolCall.title,
          kind: req.inferredKind, // 'edit' | 'shell' | 'delete' | …
        })
        return decision // undefined falls through to the mode resolver
      },
    })
  • AcpxProvider.getModels() helper (#19). Typed access to the agent's advertised model list without reaching into the untyped runtime.getStatus().details bag. Returns { currentModelId, availableModelIds } for agents that advertise models (claude, codex), or undefined for those that don't (gemini, custom adapters).

    const provider = createAcpxProvider({ agent: 'claude-code', cwd })
    const models = await provider.getModels()
    // → { currentModelId: 'claude-opus-4-7',
    //     availableModelIds: ['claude-haiku-4-5', 'claude-sonnet-4-6', 'claude-opus-4-7'] }
  • sessionOptions forwarded into runtime.ensureSession (#23). Set per-session systemPrompt / model / allowedTools / maxTurns on a fresh ACP session without bypassing the runtime. System prompts are applied at session/new time; changing them later requires a distinct sessionKey (calling provider.close() keeps the persistent record by default and won't help here).

    createAcpxProvider({
      agent: 'claude-code',
      sessionOptions: {
        systemPrompt: 'You are an expert Rust reviewer. Be terse.',
        // or { append: 'When you finish, also propose tests.' }
      },
    })
  • fullStream now emits an error part on failed turns (#32, #34). When an ACP turn ends with a failed result (model rejected, agent internal error, network blip, auth expired), the stream now emits { type: 'error', error: AcpxError } immediately before the terminal finish part. Previously the only signal was finishReason: "error" on finish plus diagnostic data stashed on providerMetadata.acpx — invisible to a streamText consumer iterating fullStream, which saw a completely silent empty assistant turn. The new error part carries the agent's actual code / message and the underlying AcpRuntimeTurnResultError as cause.

Behavior change — doGenerate / generateText throw on failed turns

The new error stream part is treated as throwable by doGenerate's accumulator (already the contract for ACP-thrown errors). On a failed-turn result, doGenerate and AI SDK's generateText now reject with an AcpxError instead of resolving with { finishReason: "error", providerMetadata: { acpx: { errorCode, errorMessage } } }. Aligns the three failure paths (thrown ACP error, mid-stream error event, failed result) on a single contract.

  • Consumers that read providerMetadata.acpx.{errorCode,errorMessage} on finish keep working — that field is preserved verbatim on the finish part for the streaming path.
  • streamText callers that iterate fullStream (or use the onError hook) automatically receive the new diagnostic. No code change required — they just stop seeing silent empty turns.

Compatibility

  • Peer dependency acpx is now >=0.8.0 (upstream contract changes that ship the new permission callback, session-options threading, and getStatus().models shape).
  • Public API: three new settings (onPermissionRequest, sessionOptions) and one new method (getModels()); no removals or renames.
  • Re-exports added: AcpPermissionRequest, AcpPermissionDecision, SessionAgentOptions, SystemPromptOption, AcpRuntimeSessionModels, AcpRuntimeStatus.

Internal

  • EventTranslator.errorPartIfFailed(result) new helper sitting alongside finish(). createTranslatingStream calls it between flush() and finish().
  • Real-codex e2e contract test for the permission callback round-trip (gated behind SMOKE_AGENTS=codex).
  • Unit + integration coverage across all four PRs uses the existing MockAcpRuntime harness; full provider suite is 186 pass / 0 fail.

⚠️ Alpha software

Both this package and its underlying runtime (acpx) are pre-1.0. Public APIs may change in any minor release. Pin exact versions. Bug reports + design feedback welcome on DaniAkash/acpx.

Full Changelog: acpx-ai-provider-v0.0.4...acpx-ai-provider-v0.0.6

acp-probe-v0.0.2

21 May 09:13
c45b968

Choose a tag to compare

acp-probe-v0.0.2 Pre-release
Pre-release

Features

  • New result.modelConfig field — the setable model list (#31, #33). ACP describes an agent's models in two different protocol surfaces, and they don't always agree. session/new.models.availableModels[] is the declarative list (best for display); configOptions[id=model].options[] is the contract setConfigOption('model', X) will accept. On codex-acp 0.12.0 the two are entirely disjoint — 24 compound <model>/<effort> ids in availableModels vs 6 bare model names in configOptions[model] — and downstream pickers built on availableModels silently fail when setConfigOption rejects the value (the next prompt finishes with finishReason: "error" and no error frame). The new derived pointer makes the setable list a one-hop typed lookup:

    const result = await probeAgent({ command: 'npx @zed-industries/codex-acp@^0.12.0' })
    
    if (result.modelConfig) {
      for (const id of result.modelConfig.values) {
        // Every id here is a valid setConfigOption('model', X) input.
      }
    }

    modelConfig is null for agents that don't expose configOptions[id=model] at all (e.g. gemini-cli, where setConfigOption itself returns -32601 method not found). Mirrors the existing result.reasoning pattern.

Documentation

  • New "Picking the right model list" section in the package README with three worked code examples (display browser / mutable picker / rich mutable picker) and a quick-reference table. The "Result shape" overview also lists modelConfig with a one-line description.

Internal

  • New EventTranslator-style deriveModelConfig(configOptions) helper next to the existing deriveReasoning in _internal/normalize.ts.
  • Unit, integration, and real-agent e2e coverage added across all three fixture agents (claude / codex / gemini). The codex e2e additionally asserts the bare-id invariant — no /-suffixed values in modelConfig.values against the live agent.

Compatibility

  • Purely additive. result.models is unchanged (still byte-faithful to availableModels[]); result.configOptions is unchanged. No existing consumer breaks.
  • ModelConfigInfo.configId is typed as the string literal 'model' rather than string, so consumers that destructure it get type-safety matching the docs.

⚠️ Alpha software

The public API may change between minor versions until 1.0.0. Pin exact versions. Bug reports + design feedback welcome — open an issue on DaniAkash/acpx.

Full Changelog: acp-probe-v0.0.1...acp-probe-v0.0.2

acp-probe-v0.0.1

14 May 16:19
1ce12e2

Choose a tag to compare

acp-probe-v0.0.1 Pre-release
Pre-release

First public release. acp-probe discovers what an Agent Client Protocol agent advertises — models, modes, reasoning effort, prompt capabilities (image / audio / embedded context), MCP transports, session controls, auth methods — via a single typed probeAgent({ command }) call.

Built for hosts (IDE plugins, desktop apps, internal tools) that need to render settings UIs and pickers for any ACP-compatible agent, built-in or custom, without sending a real prompt and without consuming any LLM tokens. The probe runs the ACP initialize + session/new handshake, optionally tests session/set_config_option to detect agents like gemini-cli that don't implement it, then tears the agent down. See the package README for the full API.

⚠️ Alpha software

The public API may change between minor versions until 1.0.0. Pin exact versions. Bug reports + design feedback welcome — open an issue on DaniAkash/acpx.

agent-skills-manager-v0.0.1

12 May 05:52
88c44d1

Choose a tag to compare

Pre-release

First public release. agent-skills-manager is a programmatic workspace + agent-link manager for skills that follow the agentskills.io specification.

Built for hosts (IDE plugins, desktop apps, internal tools) that need to manage agent skills on a user's behalf without shelling out to a CLI. See the package README for the full API.

⚠️ Alpha software

The public API may change between minor versions until 1.0.0. Pin exact versions. Bug reports + design feedback welcome — open an issue on DaniAkash/acpx.

acpx-ai-provider-v0.0.4

12 May 05:53

Choose a tag to compare

Pre-release

Fixes

  • provider.prepare() no longer consumes session freshness (#25, #26). Calling provider.prepare() before the first streamText used to flip the internal freshness flag, so the first doStream would run in continuation mode and silently drop all but the latest user message from a multi-turn messages array. Hosts using prepare() as an early-failure sync point combined with seeded conversation history now get the full transcript flattened onto the wire as expected. Behavior on subsequent turns (continuation mode → only latest user message) is unchanged.

Internal

  • Freshness check-and-set is now atomic inside markSessionKeyUsed, so two concurrent doStream calls on the same sessionKey can no longer both observe isFresh: true and duplicate seeded context to the agent.
  • New integration tests under test/integration/prepare-freshness.test.ts cover prepare() + multi-turn, second-turn continuation, and the concurrent-doStream race.

Compatibility

  • No public API additions or removals. AcpxProvider.markSessionKeyUsed(sessionKey) exists on the class but is internal — external consumers should keep going through provider.languageModel(...) and streamText/generateText.

Full Changelog: v0.0.3...acpx-ai-provider-v0.0.4