@autohandai/agent-sdk
v1.0.3
Published
Autohand Code Agent SDK - CLI wrapper implementation
Downloads
409
Maintainers
Readme
Code Agent SDK for TypeScript
Autohand Code Agent SDK - CLI wrapper implementation for TypeScript.
Beta: this SDK is actively evolving while the Agent SDK APIs stabilize. Pin versions in production and review release notes before upgrading.
Overview
This SDK provides a TypeScript wrapper around the Autohand CLI binary, enabling programmatic access to Autohand's autonomous coding agent capabilities via JSON-RPC 2.0 protocol.
Architecture
User → TypeScript SDK (thin wrapper) → CLI Subprocess (existing binary) → Provider → HTTPThe SDK:
- Spawns the Autohand CLI as a subprocess
- Communicates via JSON-RPC 2.0 over stdin/stdout
- Provides an idiomatic TypeScript API
- Supports streaming events
Other Programming Languages (Beta)
The Agent SDK is available in multiple beta language packages. Use the same CLI-backed SDK model from another programming language:
- TypeScript - this package, with
Agent,Run, streaming, and JSON helpers. - Go - idiomatic Go package with
context.Context, typed events, and channel-based streaming. - Python - async Python package with
async forevent streams and typed Pydantic models. - Java - Java 21 records, sealed events, and virtual-thread-ready APIs.
- Swift - SwiftPM package with
Agent,Runner, async streams, tools, hooks, and permissions. - Rust - async Rust crate with Tokio, typed events, and stream-based runs.
- C++ - modern C++20 package with CMake targets and typed event callbacks.
- C# - .NET package with
IAsyncEnumerable,CancellationToken, andSystem.Text.Json.
Installation
npm install @autohandai/agent-sdkQuick Start
High-Level API
Use Agent for application code. It gives you an explicit run lifecycle while
keeping CLI subprocess and JSON-RPC details out of your app.
import { Agent } from '@autohandai/agent-sdk';
const agent = await Agent.create({
cwd: '.', // Optional: defaults to process.cwd()
instructions: 'Review code with Staff-level TypeScript judgement.',
permissionMode: 'interactive',
});
const run = await agent.send('Review this repository for release readiness');
for await (const event of run.stream()) {
if (event.type === 'message_update') {
process.stdout.write(event.delta);
}
}
const result = await run.wait();
console.log(result.text);
await agent.close();For simple one-shot tasks:
const result = await agent.run('Summarize the API surface');For JSON output:
type ReleaseRisk = {
summary: string;
risks: Array<{ title: string; severity: 'low' | 'medium' | 'high' }>;
};
const risk = await agent.runJson<ReleaseRisk>('Assess publish readiness', {
schemaName: 'ReleaseRisk',
schema: {
summary: 'string',
risks: [{ title: 'string', severity: 'low | medium | high' }],
},
validate: (value) => value as ReleaseRisk,
});Run CLI feature commands through the same streamed run lifecycle:
const research = await agent.deepResearch('Hermes self-evolving systems');
for await (const event of research.stream()) {
console.log(event.type);
}
await research.wait();
if (await agent.supportsCommand('/autoresearch')) {
await (await agent.autoresearch('Improve benchmark accuracy')).wait();
}
const measureScript = [
'set -euo pipefail',
'started="$(bun -e \'process.stdout.write(String(Date.now()))\')"',
'bun run test',
'finished="$(bun -e \'process.stdout.write(String(Date.now()))\')"',
'printf \'METRIC test_ms=%s\\n\' "$((finished - started))"',
].join('\n');
const started = await agent.startAutoresearch({
objective: 'Reduce test runtime',
metricName: 'test_ms',
metricUnit: 'ms',
direction: 'lower',
measureScript,
checksCommand: 'bun run typecheck && bun run lint',
maxIterations: 3,
filesInScope: ['src', 'tests'],
sampling: { minSamples: 3, maxSamples: 9, confidenceThreshold: 2 },
});
if (!started.success || !started.instruction) {
throw new Error(started.error ?? 'Autoresearch could not start.');
}
const experiment = await agent.send(started.instruction);
for await (const event of experiment.stream()) {
if (event.type === 'message_update') process.stdout.write(event.delta);
}
await experiment.wait();
const status = await agent.getAutoresearchStatus();
const history = await agent.getAutoresearchHistory();
const candidate = history.attempts.find((attempt) =>
attempt.replayable && attempt.materialization !== 'baseline'
);
if (candidate) {
await agent.replayAutoresearch({
attemptId: candidate.attemptId,
evaluator: 'original',
});
await agent.rescoreAutoresearch({ attemptId: candidate.attemptId });
await agent.pinAutoresearch({ attemptId: candidate.attemptId, pinned: true });
}
const pareto = await agent.getAutoresearchPareto();
await agent.pruneAutoresearch({ dryRun: true });
await agent.stopAutoresearch();agent.command('/name', args) supports any slash command reported by the
connected CLI. /deep-research and /autoresearch are available in the current
CLI. Use supportsCommand() when supporting older CLI versions. The typed
autoresearch methods use JSON-RPC and expose persisted state, adaptive benchmark
configuration, replayable history, rescoring, comparison, Pareto analysis,
pinning, retention previews, and typed lifecycle and ledger-operation events.
Pruning previews by default; pass { yes: true } only when artifact deletion is
intentional.
Read the replayable autoresearch guide for the metric
contract, clean-Git requirements, adaptive decisions, replay drift, and retention
safety. The complete runnable program is
examples/27-autoresearch-ledger.ts.
Low-Level API
import { AutohandSDK } from '@autohandai/agent-sdk';
const sdk = new AutohandSDK({
cwd: '.', // Optional: defaults to process.cwd()
debug: true,
});
await sdk.start();
// Send a prompt
await sdk.prompt({
message: 'Hello, Autohand!',
});
// Stream events
for await (const event of sdk.streamPrompt({
message: 'Analyze the codebase',
})) {
console.log(event);
}
await sdk.stop();Configuration
SDK Configuration
const sdk = new AutohandSDK({
cwd: '.', // Working directory. Omit to use process.cwd()
cliPath: '/path/to/cli', // Optional: custom CLI path
debug: true, // Enable debug logging
timeout: 30000, // Request timeout in ms
bare: false, // Optional minimal explicit runtime
idleLogout: false, // Keep long-running SDK sessions alive
features: {
slashGoal: true, // Enable typed persistent-goal RPC methods
},
});CLI Configuration
The SDK uses the CLI's configuration file (~/.autohand/config.json). You can configure providers there:
{
"provider": "openrouter",
"openrouter": {
"apiKey": "sk-or-...",
"model": "openrouter/auto"
}
}API Reference
AutohandSDK
Agent.create(options: AgentOptions): Promise<Agent>
Create and start a high-level agent session.
const agent = await Agent.create({
cwd: '.',
instructions: 'Prefer Bun commands and typed SDK APIs.',
});agent.send(input, options?): Promise<Run>
Create a run without waiting for it to finish.
const run = await agent.send('Add tests for permission decisions');
for await (const event of run.stream()) {
console.log(event.type);
}
const result = await run.wait();agent.run(input, options?): Promise<RunResult>
Run a prompt to completion.
const result = await agent.run('Summarize release risk');
console.log(result.text);agent.command(command, args?, options?): Promise<Run>
Execute a CLI slash command with normal SDK event streaming. Convenience
helpers are available for deepResearch(topic) and autoresearch(objective).
const run = await agent.command('/deep-research', 'TypeScript RPC reliability');
const result = await run.wait();agent.runJson<T>(input, options?): Promise<T>
Ask the agent for JSON, parse the final response, and optionally validate it.
Pass schema.parse from Zod or any (value: unknown) => T validator.
const result = await agent.runJson<{ files: string[] }>('List changed files', {
schema: { files: ['string'] },
validate: (value) => value as { files: string[] },
});run.json<T>(options?): Promise<T>
Parse a completed run result as JSON.
const run = await agent.send('Return {"ok": true}');
const data = await run.json<{ ok: boolean }>();constructor(config: SDKConfig)
Create a new SDK instance.
start(): Promise<void>
Start the CLI subprocess.
stop(): Promise<void>
Stop the CLI subprocess.
prompt(params: PromptParams): Promise<void>
Send a prompt to the agent.
await sdk.prompt({
message: 'Add a dark mode toggle',
context: {
files: ['src/settings.ts'],
},
thinkingLevel: 'normal',
});streamPrompt(params: PromptParams): AsyncGenerator<SDKEvent>
Send a prompt and stream events.
for await (const event of sdk.streamPrompt({ message: 'Hello' })) {
console.log(event);
}streamCommand(command, args?): AsyncGenerator<SDKEvent>
Execute a registered CLI slash command and stream its events. Use
supportedCommands() or supportsCommand('/command') for capability checks.
Persistent goal methods
The SDK exposes the CLI's typed goal RPC surface:
getGoal()createGoal(params)updateGoal(params)clearGoal()queueGoal(params)startQueuedGoal()listGoalTemplates()
Enable the CLI experiment when creating the SDK:
const agent = await Agent.create({
features: { slashGoal: true },
});
await agent.createGoal({
objective: 'Finish the SDK parity upgrade with passing validation',
tokenBudget: 20_000,
});abort(): Promise<void>
Abort the current operation.
getState(): Promise<GetStateResult>
Get the current agent state.
const state = await sdk.getState();
console.log(state.status); // 'idle' | 'processing' | 'waiting_permission'getMessages(params?: GetMessagesParams): Promise<GetMessagesResult>
Get conversation messages.
const messages = await sdk.getMessages({ limit: 10 });setSystemPrompt(promptOrPath: string): AutohandSDK
Replace the entire CLI system prompt before the session starts. The value can be
inline text or a file path, matching autohand --sys-prompt.
const sdk = new AutohandSDK({ cwd: '.' })
.setSystemPrompt('./SYSTEM_PROMPT.md');appendSystemPrompt(promptOrPath: string): AutohandSDK
Append instructions to the default CLI system prompt before the session starts. This is the recommended option for most SDK integrations.
const sdk = new AutohandSDK()
.appendSystemPrompt('Always run Bun checks before summarizing release readiness.');permissionResponse(params: PermissionResponseParams): Promise<void>
Respond to a permission request.
await sdk.permissionResponse({
requestId: 'req-123',
decision: 'allow_session',
});Prefer the ergonomic helpers for application code:
await sdk.allowPermission('req-123', 'session');
await sdk.denyPermission('req-456', 'once');
await sdk.suggestPermissionAlternative('req-789', 'Run bun run typecheck first');setPlanMode(enabled: boolean): Promise<void>
Enable or disable CLI-3 plan mode. Plan mode is separate from permission mode: it restricts the agent to read-only planning tools until the host disables plan mode or the plan is accepted by the CLI flow.
const sdk = new AutohandSDK({ planMode: true });
await sdk.start();
await sdk.disablePlanMode();
await sdk.enablePlanMode();events(): AsyncGenerator<SDKEvent>
Subscribe to all events.
for await (const event of sdk.events()) {
console.log(event);
}Event Types
The SDK emits the following events:
agent_start- Agent started a sessionagent_end- Agent ended a sessionturn_start- Turn startedturn_end- Turn endedmessage_start- Message generation startedmessage_update- Message content update (streaming)message_end- Message generation endedtool_start- Tool execution startedtool_update- Tool output update (streaming)tool_end- Tool execution endedpermission_request- Permission request from agentautoresearch- Autoresearch lifecycle or typed ledger-operation eventerror- Error occurred
turn_end includes provider-reported tokensUsed, tokensUsageStatus,
durationMs, and contextPercent when the CLI supplies them.
Examples
See the examples/ directory for more examples:
basic-usage.ts- Basic prompt usagestreaming.ts- Streaming eventspermission-handling.ts- Handling permission requests20-sdlc-discovery-plan.ts- Read-only SDLC discovery and planning21-sdlc-gated-implementation.ts- Plan first, execute after an explicit gate22-sdlc-release-readiness.ts- Release-readiness checks with event streaming23-system-prompts.ts- Replacing or appending the CLI system prompt24-high-level-agent.ts- Recommended Agent/Run API25-structured-json.ts- JSON output with optional validation26-runtime-error-to-pr.ts- Turn a captured runtime error into a repair pull request27-autoresearch-ledger.ts- Replayable autoresearch lifecycle and ledger analysis
See also SDLC workflows and the replayable autoresearch guide.
CLI Binaries
The SDK includes CLI binaries for all platforms:
autohand-macos-arm64(65MB)autohand-macos-x64(70MB)autohand-linux-arm64(101MB)autohand-linux-x64(108MB)autohand-windows-x64.exe(123MB)
The SDK automatically detects the correct binary for your platform. You can also specify a custom path:
const sdk = new AutohandSDK({
cliPath: '/path/to/custom/autohand',
});Development
# Install dependencies
npm install
# Build
npm run build
# Watch mode
npm run dev
# Type check
npm run typecheck
# Lint
npm run lint
# Test
npm run testArchitecture Details
Transport Layer
The transport layer handles subprocess spawning and stdin/stdout communication:
- Spawns CLI with
--mode rpc - Uses line reader for JSONL protocol
- Manages process lifecycle
- Handles errors and cleanup
RPC Client
The RPC client implements JSON-RPC 2.0:
- Sends requests over stdin
- Parses responses from stdout
- Handles notifications
- Manages request/response correlation
SDK API
The SDK API provides a high-level interface:
- Auto-start/stop management
- Event streaming
- Permission handling
- State management
Comparison with Library SDK
This is a CLI wrapper implementation. The previous library SDK (@autohandai/agent-sdk-typescript) was a direct library implementation with in-process provider integration.
Key differences:
- CLI wrapper: Spawns CLI subprocess, uses JSON-RPC
- Library: Direct provider integration, in-process
Trade-offs:
- ✅ Full CLI feature set
- ✅ Consistent with CLI behavior
- ✅ Single source of truth
- ❌ Larger package size (65-120MB)
- ❌ Subprocess overhead (~50-200ms)
License
Apache License 2.0
