A TypeScript toolkit for running local coding agents through one ACP-oriented host API.
@tutti-os/agent-acp-kit lets a host application detect, launch, stream, cancel, and resume local coding agents through a stable TypeScript facade.
It is built for apps that want to support Codex, Claude Code, and ACP-compatible agents such as Gemini CLI, Cursor Agent, GitHub Copilot CLI, Factory Droid, fast-agent, OpenCode, Qwen Code, Kimi CLI, Kilo, Mistral Vibe, and Trae CLI without scattering provider-specific process, transport, MCP, skill, model, and event parsing logic throughout the app.
This is an embeddable host SDK. It is not a replacement for ACP clients such as acpx, and it is not a single-provider ACP adapter binary such as codex-acp.
Local coding agents do not all expose the same interface:
- Codex is CLI-first and can stream JSONL from
codex exec --json. - Claude Code is CLI-first and streams
stream-jsonoutput. - ACP-compatible agents speak JSON-RPC session protocols and can be discovered through the ACP Registry.
- Host apps still need their own messages, sessions, tool permissions, replay, canvas state, billing, and product semantics.
This package sits in the middle. It owns local agent execution. Your application owns product behavior.
ACP-compatible agents in the wider ecosystem include:
| Agent | Typical ACP command |
|---|---|
| Gemini CLI | gemini --acp |
| Cursor Agent | cursor-agent acp |
| GitHub Copilot CLI | copilot --acp --stdio |
| Factory Droid | droid exec --output-format acp |
| fast-agent | uvx fast-agent-mcp acp |
| OpenCode | opencode acp |
| Qwen Code | qwen --acp --experimental-skills |
| Kimi CLI | kimi acp |
| Kilo | kilo acp |
| Mistral Vibe | vibe-acp |
| Trae CLI | traecli acp serve |
This table is for ecosystem orientation. Commands can vary by agent version, so use the registry as the source of truth. Built-in provider support in this package is listed below, and hosts can use createGenericAcpProvider() or a custom provider plugin when they already have an ACP command to launch.
For the latest installable agent list, see:
npm install @tutti-os/agent-acp-kitOther package managers work too:
pnpm add @tutti-os/agent-acp-kit
yarn add @tutti-os/agent-acp-kit
bun add @tutti-os/agent-acp-kitRequirements:
- Node.js 22 or newer.
- ESM runtime.
- Installed provider CLIs for the providers you want to use.
For a regular local host, the application owns the workspace directory and
passes that path directly as cwd.
import {
createClaudeProvider,
createCodexProvider,
createLocalAgentRuntime,
} from "@tutti-os/agent-acp-kit";
const runtime = createLocalAgentRuntime({
providers: [createCodexProvider(), createClaudeProvider()],
});
const detections = await runtime.detect();
console.log(
detections.map((item) => ({
provider: item.provider,
supported: item.supported,
authState: item.authState,
models: item.models,
reason: item.reason,
})),
);
for await (const event of runtime.run({
runId: crypto.randomUUID(),
provider: "codex",
cwd: "/path/to/workspace",
prompt: "Inspect this project and summarize the architecture.",
model: "codex:gpt-5.4",
})) {
if (event.type === "text_delta") {
process.stdout.write(event.text);
}
if (event.type === "tool_call") {
console.log("tool started", event.name, event.input);
}
if (event.type === "done") {
console.log("run finished", event.status);
}
}| Area | Included |
|---|---|
| Runtime facade | detect(), run(), cancel(), listProviders() |
| Providers | Codex, Claude Code, ACP presets, generic ACP, fake test provider |
| Process runtime | command resolution, stdin prompt delivery, timeout, cancel, stderr tail, redaction |
| Transports | JSONL, plain stdout, ACP JSON-RPC |
| MCP delivery | normalized stdio/http MCP server config passed into provider launch plans |
| Skills | materialized files, prompt injection, project-instruction style delivery, cleanup |
| Events | normalized AgentEvent discriminated union |
| Testing | fake provider, fake ACP peer, fixtures, conformance helpers |
| Tutti integration | auto CLI-backed/standalone agent catalog, target-scoped composer options, dynamic skill context, browser-safe contracts |
Tutti apps keep platform integration behind @tutti-os/agent-acp-kit/tutti and keep actual Agent execution in an app-owned local runtime:
import {
createDefaultLocalAgentRuntime,
} from "@tutti-os/agent-acp-kit";
import {
loadTuttiAgentSkillContext,
} from "@tutti-os/agent-acp-kit/tutti";
const runtime = createDefaultLocalAgentRuntime();
const projectCwd = await resolveAppLocalProjectCwd(projectId);
const detectContext = { cwd: projectCwd };
const agents = await runtime.detect(detectContext);
const agent =
agents.find(
(item) =>
item.isDefault && item.supported,
) ??
agents.find((item) => item.supported);
if (!agent) {
throw new Error("No local Agent is currently available.");
}
if (!agent.agentTargetId) throw new Error("Selected Agent has no target identity.");
const skills = await loadTuttiAgentSkillContext({
agentTargetId: agent.agentTargetId,
agentSessionId: runId,
cwd: projectCwd,
detectContext,
});
for await (const event of runtime.run({
agentTargetId: agent.agentTargetId,
runId,
provider: agent.provider,
cwd: projectCwd,
prompt,
skillManifest: skills.skillManifest,
})) {
// Persist or stream normalized Agent events in the App.
}The host owns both cwd and environment policy. Pass the app-local run or
project directory explicitly; the kit does not derive a business workspace
root from TUTTI_WORKSPACE_ROOT or another host path alias. When env is
omitted, runtime detection and execution inherit the host process environment.
When env is present, its values overlay that ambient environment. The kit
does not apply an application-specific allowlist or remove arbitrary host
variables; it only performs provider/runtime operational normalization such as
removing a nested Claude session marker and extending PATH. A host that must
exclude product secrets is responsible for constructing an appropriate process
environment before invoking the runtime.
There is no app-facing mode switch. runtime.detect() is the single discovery
API. When TUTTI_CLI is configured it uses agent list plus target-scoped
composer JSON and preserves every exact Agent Target, including multiple
targets backed by one runtime provider. Without TUTTI_CLI it performs direct
Provider plugin detection. runtime.run() accepts the selected
agentTargetId and applies its latest target-scoped model and reasoning
defaults internally before launching the app-owned local Provider process.
Composer permission defaults are UI presentation state and do not override an
autonomous Workspace App run. Permission comes only from an explicit
AgentRunInput.permission selection or the SDK autonomous default described
below.
loadTuttiAgentCatalog() and loadTuttiAgentComposerOptions() remain available
as low-level compatibility APIs, but Workspace Apps should not compose them
themselves.
The integration negotiates both daemon generations during rollout. It prefers
the agent-ID contract and falls back to the old provider contract only when the
CLI reports that agent list is unsupported. The kit resolves provider runtime
metadata internally from the selected target. Legacy fallback requires an exact
target ID from the old catalog and fails closed when multiple targets share a
provider; ordinary CLI failures never trigger fallback.
Apps that wrap a Provider plugin can preserve that customization:
const runtime = createDefaultLocalAgentRuntime({
providers: customProviderPlugins,
});Hosts can pass the same detectContext object to runtime detection and skill
helpers. App-owned Tutti children use projectTuttiCliChildProcess at their
child boundary and pass returned output through
redactTuttiCliChildProcessText before logging or returning errors.
Apps do not construct daemon URLs or CLI argv, read catalog tokens, pass app IDs,
or derive agent IDs from provider names. They persist and round-trip the exact
agentTargetId returned by the live catalog. Provider IDs are open runtime
metadata rather than the public selection identity. Claude Code runtime metadata
is canonicalized to claude-code; legacy claude remains input-only.
Frontend code can import DTO types and guards without Node dependencies:
import {
isTuttiAgentCatalog,
type TuttiAgentCatalog,
} from "@tutti-os/agent-acp-kit/tutti/contracts";| Provider | Status | Transport | Notes |
|---|---|---|---|
| Codex | Supported | codex exec --json JSONL |
Dynamic model discovery via codex debug models; per-run CODEX_HOME with copied auth and sanitized config; same-provider resume via codex exec resume --json <session> - |
Claude Code (claude-code) |
Supported | claude -p --output-format stream-json |
Canonical provider ID is claude-code; legacy claude input is accepted internally; supports fallback model hints, custom model pass-through, and same-provider resume via --resume <session> |
Tutti Agent (tutti-agent) |
Supported | tutti-agent exec --json JSONL |
First-party canonical provider; runs use a temporary TUTTI_AGENT_HOME derived from the VM-local source Home; authentication is probed with tutti-agent login status; no Nexight runtime alias |
| Devin for Terminal | Experimental | ACP JSON-RPC | Shared generic ACP transport; command override DEVIN_ACP_BIN |
| Hermes | Experimental | ACP JSON-RPC | Shared generic ACP transport; command override HERMES_ACP_BIN |
| Kimi | Experimental | ACP JSON-RPC | Shared generic ACP transport; command override KIMI_ACP_BIN |
| Kiro | Experimental | ACP JSON-RPC | Shared generic ACP transport; command override KIRO_ACP_BIN |
| Kilo | Experimental | ACP JSON-RPC | Shared generic ACP transport; command override KILO_ACP_BIN |
| Mistral Vibe | Experimental | ACP JSON-RPC | Shared generic ACP transport; command override VIBE_ACP_BIN |
| Cursor Agent | Experimental | ACP JSON-RPC | Shared generic ACP transport with run-scoped selected skills; command override CURSOR_ACP_BIN |
| Gemini CLI | Experimental | ACP JSON-RPC | Shared generic ACP transport; command override GEMINI_ACP_BIN |
| OpenCode | Experimental | ACP JSON-RPC | Shared generic ACP transport with run-scoped selected skills; command override OPENCODE_ACP_BIN |
| Qoder CLI | Experimental | ACP JSON-RPC | Shared generic ACP transport; command override QODER_ACP_BIN |
| Qwen Code | Experimental | ACP JSON-RPC | Shared generic ACP transport; command override QWEN_ACP_BIN |
| Generic ACP | Experimental | ACP JSON-RPC | Bring your own ACP agent command |
| Fake | Test helper | In-memory async events | For host tests and conformance checks |
Built-in real local providers do not impose a provider-level concurrency cap. Hosts can still enforce stricter queueing, cancellation, or watchdog policies around runtime.run() when a product surface needs serialized execution.
Local Codex runs always use a run-scoped temporary CODEX_HOME. The temporary
root comes from run env TMPDIR, TEMP, or TMP, then process env, then the
OS default. The provider shares auth.json, sessions/, and plugins/cache/
with the requested CODEX_HOME or the user's default ~/.codex so token
refreshes, native resume, and plugin assets stay durable across runs; plugin
cache exposure is best-effort and does not block a run. It copies isolated
config files such as config.json, config.toml, and instructions.md,
preserves compatible config.toml settings such as custom model providers and
base_url, removes Codex config values known to break current CLI parsing,
disables Codex native multi-agent for single-process run lifecycle safety, and
overlays any run-scoped MCP server config.
Codex and Tutti Agent keep one .agent-acp-kit-codex-root marker in the
application working directory after the first successful preparation. The
marker is project metadata used by Codex root discovery. It is deliberately
not created and removed per run: concurrent runs share it, avoiding remote
filesystem churn and preventing one finishing run from removing another
run's root marker.
Treat this package as the local-agent execution layer, not as your application orchestrator.
Your host should own:
- User, session, run, and message persistence.
- Assistant message anchor creation.
- Runtime policy, such as trusted local mode, default provider, default model, and tool allowlists.
- Domain tools and MCP server creation.
- Mapping
AgentEventinto your app stream, websocket, or replay protocol. - Billing, job queues, media storage, canvas writes, and product state.
- Cross-provider resume or handoff semantics.
This package should own:
- Provider detection and capability reporting.
- Provider-specific command args, env, MCP config delivery, and model normalization.
- Process supervision and transport handling.
- Provider output parsing into
AgentEvent. - Cleanup of per-run temporary files it creates.
Keep the host adapter thin:
const mcpServers = [
{
name: "app-tools",
type: "stdio" as const,
command: process.execPath,
args: ["/absolute/path/to/app-tools-mcp.js"],
env: {
APP_TOOL_TOKEN: runScopedToken,
APP_DAEMON_URL: "http://127.0.0.1:3001",
},
},
];
for await (const event of runtime.run({
runId,
provider: selectedProvider,
cwd: workspaceDir,
prompt: userPrompt,
systemPrompt,
history,
model,
mcpServers,
skillManifest,
extraAllowedDirs: [workspaceDir],
env: providerEnv,
resume: resumeContext,
})) {
await projectAgentEventToHostStream(event);
}AgentEvent is a TypeScript discriminated union. Narrow on event.type and TypeScript will expose the fields for that event variant.
if (event.type === "tool_result" && event.status === "failed") {
console.error(event.error);
}Common event types:
| Event | Meaning |
|---|---|
status |
Lifecycle progress such as detecting, spawning, running, warning |
thinking_delta |
Incremental reasoning or thinking text when a provider exposes it |
text_delta |
Assistant text |
tool_call |
Normalized tool start |
tool_result |
Normalized tool completion or failure |
stderr |
Redacted stderr text |
error |
Runtime or provider error |
done |
Terminal event with completed, failed, or canceled |
For MCP tool events, name is the normalized short tool name. Providers that
surface a server namespace may also include rawName and mcpServerName, so
hosts can distinguish same-named tools exposed by different MCP servers while
keeping backward-compatible short-name routing.
Hosts should persist enough event data for replay and should treat done as the terminal source of truth for a run. Individual error events are diagnostics, not terminal status by themselves.
Apps can ask the runtime for secret-free preparation and execution timings by
setting metadata.timingDiagnostics on a run:
for await (const event of runtime.run({
runId,
provider: "codex",
cwd,
prompt,
metadata: { timingDiagnostics: true },
})) {
if (event.type === "status" && event.diagnostic?.kind === "timing") {
appLogger.info("agent_kit_timing", event.diagnostic);
continue;
}
// Project normal provider events into the UI.
}Timing diagnostics separate runtime preparation (process_env,
tutti_run_context, and provider_plan) from provider execution
(transport_started, provider_first_event, provider_first_text,
provider_first_tool, and provider_done). Failed preparation and provider
stream stages carry an outcome without including the error message. Each
diagnostic contains stage and total elapsed milliseconds only; it never
contains prompts, paths, environment values, MCP credentials, or provider
output. Hosts should persist these events with their own run id and should not
render them as user-visible agent status messages.
Codex reconnect progress such as Reconnecting... 2/5 (request timed out) is a transient provider retry state. The Codex provider maps those JSONL messages to status events with status: "warning" so hosts can show progress without ending the run.
Use runtime.detect() to get Agent Targets, provider installation/support
status, and authoritative model options.
const modelOptions = await runtime.detect();Tutti agent catalogs are cached per CLI/workspace and composer options are
cached per CLI/workspace/exact Agent Target for the lifetime of the runtime.
Changing from the workspace root to an app-local project cwd therefore does
not repeat provider/model discovery or temporarily invalidate the selected Agent Target;
standalone no-argument detection is cached per Provider. After the catalog,
credentials, models, or local CLI installation changes, call
runtime.detect({ refresh: true }) to refresh it.
Provider behavior differs:
- Codex: attempts dynamic discovery with
codex debug models, then falls back to bundled or package model hints. - Claude Code: returns fallback hints such as
sonnet,opus,haiku, and known full ids, then adds configured custom ids from the Claude settings file when present. Custom model ids can be passed through. - ACP providers: attempt model discovery through ACP session lifecycle when the peer supports it.
Provider plugin diagnostics remain internal to standalone projection. The
public result intentionally contains only provider identity, supported,
authState, models/default model, and an optional display-only reason.
Hosts should not hardcode Codex or Claude model lists above this package. If a UI needs additional custom models, keep that UI behavior in the host and pass the chosen id into AgentRunInput.model.
Runs accept an optional provider-neutral permission selection. Pass both the semantic returned by the composer and its provider mode id when available:
permission: {
modeId: selectedMode.id,
semantic: selectedMode.semantic,
}The SDK maps this policy to each provider. Workspace App runs default to
full-access when the host omits permission: Codex uses its unrestricted
sandbox mode, Claude uses --dangerously-skip-permissions, and ACP requests select a
recognized permissive option when the peer offers one (otherwise the request
is cancelled).
An App can pass an explicit narrower semantic for a run. For ACP, every
non-full-access semantic cancels permission requests because the protocol
adapter cannot safely infer a tool's risk from a provider-specific option id.
The permissionConfig.defaultValue returned by a Tutti composer is the UI's
initial selection, not an explicit App run selection. Hosts that intentionally
offer a permission picker must pass the user's explicit choice in
AgentRunInput.permission; the automatic Tutti runtime integration never
promotes the composer default into execution policy.
For Codex runs, pass the VM session user's existing Codex home explicitly:
for await (const event of runtime.run({
runId,
provider: "codex",
cwd: workspaceDir,
prompt,
env: {
...process.env,
CODEX_HOME: "/home/tsh-runtime/.codex",
},
mcpServers,
})) {
// Project AgentEvent into the host protocol.
}The value in CODEX_HOME is the source home owned by the VM session user. The
kit creates a temporary per-run Codex home under TMPDIR, links auth.json and
shared session/plugin state from the source home, copies and sanitizes
config.toml, and overlays the run's MCP servers. It never treats an App
runtime directory as the source login home.
Hosts can expose an install action for supported local providers through one public function:
import { installAgentProvider } from "@tutti-os/agent-acp-kit";
const result = await installAgentProvider("codex");Supported install targets are codex and claude. The function installs and
checks the official provider CLIs only:
- Codex install:
npm install -g @openai/codex - Claude Code install:
npm install -g @anthropic-ai/claude-code
ACP adapter packages such as codex-acp and claude-agent-acp are not required
for these built-in providers. The install status still reports whether legacy
adapter binaries are present as compatibility metadata, but adapter presence no
longer affects availability, command selection, or post-install success.
The result is structured instead of throwing raw shell output. Failed installs
include a failureReason such as install_command_failed,
install_timed_out, or post_install_probe_failed. A successful install can
still return auth_required in after.availability; hosts should then prompt
the user to log in with the provider CLI.
Install, detection, and runtime launch envs include common local binary
directories such as /opt/homebrew/bin, /usr/local/bin, ~/.local/bin, npm
configured prefix bins, and npm's global prefix bin when it can be resolved.
This package does not define product tools. It accepts mcpServers and converts them into the provider's expected format.
const mcpServers = [
{
name: "app-tools",
type: "stdio" as const,
command: "node",
args: ["/absolute/path/to/app-tools-mcp.js"],
env: { APP_TOOL_TOKEN: runScopedToken },
toolTimeoutMs: 30 * 60_000,
startupTimeoutMs: 2 * 60_000,
// Optional override: "auto" | "prompt" | "writes" | "approve".
// Run-scoped application MCP servers default to "approve".
defaultToolsApprovalMode: "approve" as const,
},
];Timeouts are normalized by provider. Codex writes startup_timeout_sec and
tool_timeout_sec into its per-run config. Claude Code writes per-server
timeout for tool calls. Generic ACP providers receive only standard ACP MCP
server fields because the ACP MCP server schema does not define timeout fields.
Codex also writes default_tools_approval_mode; it defaults to approve so
trusted run-scoped application MCP tools can execute in non-interactive runs.
Hosts can explicitly choose auto, prompt, or writes for less-trusted MCP
servers. Claude full-access runs use --dangerously-skip-permissions.
Codex and Claude MCP delivery uses those provider-native, run-scoped config
paths. Generic ACP providers receive normalized MCP servers through ACP.
Keep tool tokens run-scoped and short-lived. Do not pass broad application secrets or database credentials directly to agent processes.
skillManifest supports three delivery modes:
materialized-files: writes selected skills into the run-scoped native provider home when the provider supports it (CODEX_HOME/skillsfor Codex andTUTTI_AGENT_HOME/skillsfor Tutti Agent), or into a provider-owned temporary run root for Claude Code and Generic ACP providers, then references them in the prompt. Cursor, OpenCode, and the other ACP presets use this Generic ACP layout without replacing their authenticationHOME.prompt-injection: injects skill content into the provider prompt.project-instructions: injects instruction-style skill content.
The package handles delivery and cleanup for runtime and adapter execution. A
host that calls a provider's low-level buildLaunchPlan() directly owns the
resulting launch-plan artifact lifetime. The host remains the source of truth
for skill selection, permission, and storage.
Codex-compatible provider homes, Claude Code invocation artifacts, and Generic
ACP selected-skill roots are run-scoped. Their parent directory
comes from run env TMPDIR, TEMP, or TMP, falling back to the process env
and then the operating-system temp directory. Consequently VM callers can set
TMPDIR to an App runtime directory while non-VM callers need no special
configuration. Selected skills never mutate the source or global provider
home, or the project working directory. Generic ACP child-process TMPDIR,
TEMP, and TMP point at an isolated tmp/ below the same run root so
provider-generated logs and scratch files are reclaimed with the run. Claude
Code's generated MCP config
is kept in the same temporary run root. Generic ACP MCP definitions are
delivered through ACP session/new and are not copied into generated files. For
provider-owned roots,
materializedPath is treated as an input hint only; the provider rewrites the
effective path to <run-home>/skills/<safe-slug>.
Hosts can also pass skill manifests produced by external commands. Tutti workspace apps can use the Tutti subpath helper to load dynamic CLI skills, then decide how to merge Tutti's recommended system prompt with the app-owned prompt:
import { loadTuttiAgentSkillContext } from "@tutti-os/agent-acp-kit/tutti";
const tuttiContext = await loadTuttiAgentSkillContext({
provider,
agentSessionId: runId,
cwd,
commandEnvNames: ["MY_APP_TUTTI_CLI"],
});
const systemPrompt = [
appSystemPrompt,
tuttiContext.recommendedSystemPrompt?.content,
]
.filter(Boolean)
.join("\n\n");
for await (const event of runtime.run({
runId,
provider,
cwd,
prompt,
systemPrompt,
skillManifest: tuttiContext.skillManifest,
})) {
await projectAgentEventToHostStream(event);
}Use runtime.cancel(runId) or abort the signal passed into runtime.run().
const controller = new AbortController();
const stream = runtime.run({ ...input, signal: controller.signal });
controller.abort();
await runtime.cancel(input.runId);Resume is conservative by design:
- Same-provider resume may pass
providerSessionIdorresumeTokenwhen the provider supports it. - If no provider resume metadata exists, pass
resume: { mode: "fresh" }. - Cross-provider resume should be host-level handoff: rebuild prompt, history, and context, then start a fresh provider run.
Hosts should still pass durable history on every run. Native resume is an optimization for the same provider, not the only source of continuity:
const sameProvider = previousRun?.provider === selectedProvider;
const providerResumeId =
previousRun?.providerSessionId ?? previousRun?.resumeToken;
const resume =
sameProvider && providerResumeId
? {
mode: "provider" as const,
providerSessionId: previousRun?.providerSessionId,
resumeToken: previousRun?.resumeToken,
}
: { mode: "fresh" as const };
for await (const event of runtime.run({
runId,
provider: selectedProvider,
cwd,
prompt,
history: durableMessages,
resume,
})) {
if (event.type === "done") {
await saveRunResumeMetadata(runId, {
providerSessionId: event.sessionId,
resumeToken: event.resumeToken,
});
}
}Main export:
import {
createLocalAgentRuntime,
createCodexProvider,
createClaudeProvider,
createDefaultLocalAgentProviderPlugins,
createGenericAcpProvider,
installAgentProvider,
type AgentEvent,
type AgentRunInput,
} from "@tutti-os/agent-acp-kit";Runtime control plane export:
import {
createRuntimeControlPlane,
inferRuntimeKind,
} from "@tutti-os/agent-acp-kit/runtime-control-plane";Testing export:
import {
assertProviderConformance,
createFakeAcpPeer,
createFakeProvider,
} from "@tutti-os/agent-acp-kit/testing";pnpm install
pnpm typecheck
pnpm test
pnpm build
pnpm pack:checkUse .github/workflows/npm-package-release.yml for package releases.
Stable latest releases:
- Open and merge a PR that bumps
package.jsonto the target stable version. - Run the workflow from
mainwithversion_bump=current,dist_tag=latest, anddry_run=truefirst. - Re-run with
dry_run=falseto publish the currentpackage.jsonversion and push tagv<version>.
Beta and other prerelease packages:
- Run the workflow from a feature or release branch.
- Use
dist_tag=betaand either a prerelease bump such asprepatch,preminor,premajor, orprerelease, orversion_bump=customwith a prerelease version such as0.3.0-beta.0. - The workflow commits the prerelease version bump back to the triggering branch, publishes the package, and pushes tag
v<version>.
The workflow never commits directly to main. Non-latest releases must use prerelease semver versions, and latest releases must use stable semver versions.
Local agents execute user-trusted CLIs on the local machine. Only enable this package in trusted local mode.
Recommended host policy:
- Use run-scoped tool tokens with TTL and explicit revoke.
- Do not pass Supabase, database, or cloud provider tokens directly to agents.
- Redact stdout and stderr before persistence.
- Clean per-run temporary directories.
- Limit MCP tool allowlists per run.
- Gate dangerous provider flags behind trusted local mode.
- Persist terminal events durably so cancellation or failure cannot be overwritten by late process output.
@tutti-os/agent-acp-kit is licensed under the Apache License 2.0.
- Stabilize the public
AgentRunInputandAgentEventcontracts. - Expand provider conformance tests for ACP lifecycle edge cases.
- Add more provider-specific adapters where shared ACP behavior is not enough.
- Add first-class examples for desktop apps and local web apps.
- Add repository-level
CONTRIBUTING.mdandSECURITY.mdbefore broader external contribution.
