@inference-gateway/adk
v0.9.0
Published
Agent Development Kit (ADK) for the Inference Gateway, written in TypeScript.
Maintainers
Readme
⚠️ Early Stage: This project is in early bootstrap and the public API is not yet stable. Breaking changes are expected before 1.0. Pin a specific version in production and review
CHANGELOG.mdbefore upgrading.
Table of Contents
- Overview
- 🚀 Quick Start
- ✨ Key Features
- 📖 API Reference
- 🔧 Advanced Usage
- 🌐 A2A Ecosystem
- 📋 Requirements
- 📦 Container Image
- 📄 License
- 🤝 Contributing
- 📞 Support
- 🔗 Resources
Overview
The TypeScript ADK (Agent Development Kit) is a Node.js library that simplifies building Agent-to-Agent (A2A) protocol compatible agents. A2A enables seamless communication between AI agents, allowing them to collaborate, delegate tasks, and share capabilities across different systems and providers.
It is the TypeScript sibling of the Go ADK and the Rust ADK, and ships as the npm package @inference-gateway/adk. Patterns mirror the Go implementation where the languages allow - for example, BUILD_AGENT_NAME / BUILD_AGENT_DESCRIPTION / BUILD_AGENT_VERSION mirror the Go ADK's BuildAgentName / BuildAgentDescription / BuildAgentVersion LD-flag injection points.
What is A2A?
Agent-to-Agent (A2A) is a standardized protocol that enables AI agents to:
- Communicate with each other using a unified JSON-RPC interface
- Delegate tasks to specialized agents with specific capabilities
- Stream responses in real-time for better user experience
- Discover capabilities through standardized agent cards
🚀 Quick Start
Installation
pnpm add @inference-gateway/adk
# or
npm install @inference-gateway/adk
# or
yarn add @inference-gateway/adkRequires Node.js 24 LTS or newer. The package is ESM-only.
Hello, A2A
A minimal A2A agent that echoes every message it receives, plus a client that sends one message and waits for the task to complete. This is the smallest end-to-end usage of the ADK - the full runnable version with shutdown handling, message extraction, and dead-lettering lives in examples/minimal/.
server.ts - boot an A2A server with built-in message/send, tasks/get, and tasks/list handlers, plus an inline echo worker:
import {
createA2AServer,
createMessageSendHandler,
createTaskGetHandler,
createTaskListHandler,
InMemoryTaskStorage,
MESSAGE_SEND_METHOD,
TASK_GET_METHOD,
TASK_LIST_METHOD,
TASK_STATE,
transitionTask,
type AgentCard,
} from '@inference-gateway/adk';
const card: AgentCard = {
name: 'hello-agent',
description: 'Echoes every message it receives.',
version: '0.1.0',
protocolVersion: '0.3.0',
url: 'http://127.0.0.1:8080',
defaultInputModes: ['text/plain'],
defaultOutputModes: ['text/plain'],
capabilities: {
streaming: false,
pushNotifications: false,
stateTransitionHistory: false,
},
skills: [
{ id: 'echo', name: 'Echo', description: 'Echo input.', tags: ['echo'] },
],
};
const storage = new InMemoryTaskStorage();
const server = createA2AServer({ card });
server.registerMethod(
MESSAGE_SEND_METHOD,
createMessageSendHandler({ storage })
);
server.registerMethod(TASK_GET_METHOD, createTaskGetHandler({ storage }));
server.registerMethod(TASK_LIST_METHOD, createTaskListHandler({ storage }));
void runWorker();
await server.listen(8080, '127.0.0.1');
console.log('listening on http://127.0.0.1:8080');
async function runWorker(): Promise<void> {
while (true) {
const task = await storage.dequeue();
const running = transitionTask(task, TASK_STATE.IN_PROGRESS);
storage.updateActive(running);
storage.storeDeadLetter(transitionTask(running, TASK_STATE.COMPLETED));
}
}client.ts - send a message and poll tasks/get until the task reaches a terminal state:
import {
createA2AClient,
isTerminal,
type ManagedTaskState,
} from '@inference-gateway/adk';
const client = createA2AClient({ baseURL: 'http://127.0.0.1:8080' });
let task = await client.sendMessage({
message: {
messageId: crypto.randomUUID(),
role: 'ROLE_USER',
parts: [{ text: 'hello, agent' }],
},
});
while (!isTerminal(task.status.state as ManagedTaskState)) {
await new Promise((r) => setTimeout(r, 200));
task = await client.getTask(task.id);
}
console.log(task);Examples
Complete, runnable examples live under examples/:
examples/minimal/- A2A server + client with no LLM. Demonstrates the full task lifecycle, a graceful echo worker with dead-lettering, andA2AClientpolling. Mirrors the Go ADK'sexamples/minimal/.examples/streaming/- A2A server + client over SSE. Boots a server withcapabilities.streaming = true, registers a custommessage/streamexecutor that emits word-by-worddeltaevents, and consumes the SSE frames from a plainfetch-based client. Mirrors the Go ADK'sexamples/streaming/.examples/input-required/- Pause + client-driven resume. The server pauses a task to ask for a missing piece of information (INPUT_REQUIRED), and the client detects the pause, sends a follow-up on the samecontextId, and polls until completion. Mirrors the Go ADK'sexamples/input-required/.examples/ai-powered/- LLM-backed A2A agent with weather and time tools. WiresAgentBuilder+OpenAICompatibleLLMClientintoDefaultBackgroundTaskHandler, dispatches tool calls in a chat-completion loop, and answers natural-language prompts through any OpenAI-compatible provider routed via the Inference Gateway. Mirrors the Go ADK'sexamples/ai-powered/.
Each example ships its own README with setup instructions.
✨ Key Features
Core Capabilities
- 🤖 A2A Protocol Compliance - JSON-RPC 2.0 endpoint, agent-card discovery at
/.well-known/agent-card.json, and/healthliveness probe - 📬 Built-in Handlers - Drop-in
message/send,tasks/get, andtasks/listJSON-RPC handlers backed by anyTaskStorage - 🔌 Extensible JSON-RPC - Register custom methods on the per-server
MethodRegistry - 🔁 Task Lifecycle - Strict state machine (
SUBMITTED → WORKING → {INPUT_REQUIRED | COMPLETED | FAILED | CANCELLED}) withTaskTransitionErroron invalid transitions - 🗄️ Pluggable Storage - Small
TaskStorageinterface withInMemoryTaskStorageincluded; queue / active / dead-letter semantics out of the box - 🛰️ A2A Client -
A2AClientwithsendMessage,getTask,getAgentCard,getHealth, configurable timeout, retry with exponential backoff, and a typed error taxonomy - 📇 AgentCard Loading - Load from file or JSON with
${VAR}env-placeholder resolution, optional shallow overrides, and required-field validation - 🏷️ Build-Time Metadata - Inject
name/description/versionat bundle or runtime (mirrors the Go ADK'sBuildAgent*LD flags) - ☁️ CloudEvents v1.0 -
createCloudEventhelper for wrapping agent events in a spec-compliant envelope - 📡 SSE Streaming -
SSEStreamWriterfor emitting Server-Sent Events with a configurable heartbeat
Developer Experience
- 📦 ESM-only - Modern ES2024 bundle via
tsup, targeted at Node 24 LTS+ - 🛡️ Strict TypeScript -
verbatimModuleSyntax,noUncheckedIndexedAccess,exactOptionalPropertyTypes,isolatedModules - 📚 Generated A2A Types - Types generated from the canonical
inference-gateway/schemasat a pinned commit SHA, with a drift check enforced in CI - 🧪 Well Tested - Vitest suite covering the public surface; a dedicated drift test guards the generated A2A types
- 🪶 Minimal Dependencies - Only
hono+@hono/node-serverat runtime
Status & Roadmap
The TypeScript ADK currently focuses on the core A2A protocol surface: message/send, tasks/get, tasks/list, AgentCard discovery, the task lifecycle state machine, in-memory storage, the retrying client, CloudEvents, and SSE. Capabilities that exist in the Go ADK but are not yet implemented here include: LLM client / multi-provider chat completion, streaming task handlers, additional JSON-RPC methods (tasks/cancel, tasks/resubscribe, tasks/pushNotificationConfig/*, agent/getAuthenticatedExtendedCard), Redis-backed storage, file artifacts (filesystem & MinIO), OIDC/OAuth authentication, TLS configuration, push notifications, and OpenTelemetry-based observability. The TS ADK tracks the Go ADK as the long-term feature target - contributions toward parity are welcome.
📖 API Reference
Core Components
A2AServer / createA2AServer
The main HTTP server. Exposes the JSON-RPC endpoint at DEFAULT_JSONRPC_PATH (POST /), an AgentCard discovery endpoint at AGENT_CARD_PATH (/.well-known/agent-card.json), and a liveness probe at HEALTH_PATH (/health). The endpoint mount points and the agent-card Cache-Control header (DEFAULT_AGENT_CARD_CACHE_CONTROL) are configurable via A2AServerConfig.
const server = createA2AServer({ card });
await server.listen(8080, '127.0.0.1');
// ...
await server.close();A freshly constructed server already serves discovery and health - no method registration is required just to be reachable.
MethodRegistry and JSON-RPC dispatch
JSON-RPC methods are registered on a per-server MethodRegistry. Call server.registerMethod(name, handler) (or unregisterMethod / hasMethod / registeredMethods) to wire up methods. The lower-level helpers dispatch, createSuccessResponse, createErrorResponse, JSONRPCError, and JSONRPC_ERROR_CODES are exported as well - useful for custom transports or focused unit tests.
Built-in handlers
createMessageSendHandler({ storage })registers asMESSAGE_SEND_METHOD(message/send). It accepts a JSON-RPCmessage/sendrequest, creates aSUBMITTEDtask, enqueues it on the suppliedTaskStorage, and returns the wireTaskimmediately. Your worker code dequeues and progresses the task.createTaskGetHandler({ storage })registers asTASK_GET_METHOD(tasks/get). It looks up the requested task across active and dead-letter storage and returns whatever it finds.createTaskListHandler({ storage })registers asTASK_LIST_METHOD(tasks/list). It returns tasks filtered by optionalstate/contextId, paginated with an opaquecursorand alimitclamped tomaxLimit(default100). The response shape is{ tasks, nextCursor? };nextCursoris omitted on the final page. Pagination is stable under concurrent inserts and deletes because the cursor is keyset-encoded on(createdAt, id).
These handlers are pure adapters between the JSON-RPC surface and a TaskStorage - no business logic lives in them.
Task lifecycle
Tasks are managed by an explicit state machine. Use:
createTask(input)to construct a new managed tasktransitionTask(task, nextState, options?)to move it forward (throwsTaskTransitionErroron invalid transitions;options.messageattaches an agent reply tostatus.message)canTransition(from, to)to probe a transition without applying itisTerminal(state)/isPaused(state)for state-class predicatestoWireTask(task)to convert from the internalManagedTaskto the wire-formatTaskreturned over JSON-RPC- The
TASK_STATEconst for the canonical state literals
TaskStorage and InMemoryTaskStorage
TaskStorage is a queue-centric interface where tasks live in one of three locations:
- Queue - enqueued, waiting to be dequeued by a worker.
- Active - enqueued or in-flight (after
dequeue, before terminal). - Dead letter - terminal tasks (
COMPLETED/FAILED/CANCELLED) retained for audit and lookup.
The included InMemoryTaskStorage is suitable for tests, local development, and single-instance deployments. Key methods:
enqueue(task)/dequeue(signal?)- FIFO queue, with optionalAbortSignalfor cancellationupdateActive(task)- persist a state transition without re-enqueueingstoreDeadLetter(task)- move a terminal task out of the active mapgetTask(id)/listTasks(filter?)- read across active + dead-lettergetStats()- snapshot of storage health (counts by state, queue length, etc.)cleanupCompleted(),deleteContext(id)- cleanup helpers
To plug in a different backend (Redis, Postgres, S3-backed), implement TaskStorage and pass your implementation to the message-send and task-get handlers.
A2AClient / createA2AClient
A typed client for calling A2A servers:
const client = createA2AClient({ baseURL: 'http://localhost:8080' });
const task = await client.sendMessage({ message });
const refresh = await client.getTask(task.id, { historyLength: 10 });
const card = await client.getAgentCard();
const health = await client.getHealth();The client supports configurable per-attempt timeouts (DEFAULT_TIMEOUT_MS), exponential-backoff retries (withRetry, DEFAULT_RETRY_CONFIG, isRetryableError), custom headers, a pluggable fetch implementation, and a configurable User-Agent. Pass retry: false to disable retries entirely.
Errors are categorized for handling:
A2AHTTPError- non-2xx HTTP responseA2AJSONRPCError- JSON-RPC envelopeerrorfieldA2ATimeoutError- per-attempt timeout firedA2ANetworkError- DNS / connection / unexpected fetch failureA2AAbortError- caller-suppliedsignalaborted
All extend A2AClientError.
AgentCard loading
import {
loadAgentCardFromFile,
loadAgentCardFromJSON,
} from '@inference-gateway/adk';
const card = loadAgentCardFromFile('./agent.json', {
env: process.env,
overrides: { url: 'https://prod.example.com' },
});The loader runs a four-step pipeline:
- Parse JSON.
- Resolve
${VAR}placeholders againstoptions.env(defaults toprocess.env). A missing env var throwsAgentCardLoadErrorrather than silently substituting an empty string. - Shallow-merge
options.overridesover the resolved object (overrides win). - Validate the required-field subset (
name,description,version,protocolVersion,defaultInputModes,defaultOutputModes,capabilities,skills) - throwsAgentCardValidationError(with an optionalfieldhint) on failure. Optional fields are deliberately left loose.
loadAgentCardFromFile is synchronous by design - it uses readFileSync and is meant for boot-time configuration. Do not call it on the request path.
CloudEvents v1.0 envelope
import {
AGENT_EVENT_TYPE,
createCloudEvent,
DEFAULT_AGENT_EVENT_SOURCE,
} from '@inference-gateway/adk';
const evt = createCloudEvent({
type: AGENT_EVENT_TYPE.TASK_STATUS_CHANGED,
source: DEFAULT_AGENT_EVENT_SOURCE,
data: { taskId, state: 'TASK_STATE_COMPLETED' },
});AGENT_EVENT_TYPE is the canonical set of streaming event-type constants (DELTA, ITERATION_COMPLETED, TOOL_STARTED/COMPLETED/FAILED/RESULT, INPUT_REQUIRED, TASK_STATUS_CHANGED, TASK_INTERRUPTED, STREAM_FAILED) - identical to the Go ADK's Event* constants so a TS publisher and a Go consumer can interoperate without translation. Produces a CloudEvents v1.0-compliant envelope (CLOUDEVENTS_SPEC_VERSION = '1.0', served as CLOUDEVENTS_CONTENT_TYPE) - useful for forwarding agent events to event buses or webhook subscribers.
SSE streaming writer
SSEStreamWriter writes Server-Sent Events to a writable target, with a configurable heartbeat (DEFAULT_SSE_HEARTBEAT_MS) and the canonical SSE headers (SSE_HEADERS, SSE_CONTENT_TYPE). Useful for streaming task state transitions to long-lived HTTP clients.
Configuration
Most of the ADK is configured programmatically - via A2AServerConfig, A2AClientConfig, the handler option objects (MessageSendHandlerOptions, TaskGetHandlerOptions), and LoadAgentCardOptions. The only environment variables the library itself reads are the three build-metadata variables, plus the ${VAR} placeholders inside agent-card JSON.
Build-Time Agent Metadata
The ADK supports injecting agent name / description / version at build time, mirroring the Go ADK's BuildAgentName / BuildAgentDescription / BuildAgentVersion LD flags. Values are read from process.env once at first import and frozen into buildMetadata. An empty string means "not injected" - applyBuildMetadata(card) treats empty values as no-ops, so it is safe to call unconditionally.
| Variable | Default | Description |
| ------------------------- | --------- | ---------------------------------------------------------------------- |
| BUILD_AGENT_NAME | (empty) | Overrides card.name when non-empty (read once at module load) |
| BUILD_AGENT_DESCRIPTION | (empty) | Overrides card.description when non-empty (read once at module load) |
| BUILD_AGENT_VERSION | (empty) | Overrides card.version when non-empty (read once at module load) |
Two injection options:
Runtime - set
BUILD_AGENT_NAME(etc.) in the environment before the module is first imported.Bundle-time - use
tsup'sdefineoption to substitute at build time:// tsup.config.ts import { defineConfig } from 'tsup'; export default defineConfig({ entry: ['src/index.ts'], format: ['esm'], define: { 'process.env.BUILD_AGENT_NAME': JSON.stringify('weather-assistant'), 'process.env.BUILD_AGENT_VERSION': JSON.stringify('1.2.3'), }, });
Apply build metadata to a hand-written card:
import {
applyBuildMetadata,
createA2AServer,
type AgentCard,
} from '@inference-gateway/adk';
const baseCard: AgentCard = {
name: 'placeholder',
description: 'placeholder',
version: '0.0.0',
protocolVersion: '0.3.0',
url: 'http://127.0.0.1:8080',
defaultInputModes: ['text/plain'],
defaultOutputModes: ['text/plain'],
capabilities: {
streaming: false,
pushNotifications: false,
stateTransitionHistory: false,
},
skills: [],
};
const card = applyBuildMetadata(baseCard);
const server = createA2AServer({ card });See the Container Image section for containerized builds that wire these variables through.
Agent-card ${VAR} placeholders
Inside an agent-card JSON file passed to loadAgentCardFromFile / loadAgentCardFromJSON, any string of the form ${SOME_ENV_VAR} is resolved against options.env (defaulting to process.env) at load time. A missing variable throws AgentCardLoadError - there is no silent fallback. Example:
{
"name": "${A2A_AGENT_NAME}",
"description": "Production agent in ${ENVIRONMENT}",
"version": "0.1.0",
"protocolVersion": "0.3.0",
"url": "${A2A_AGENT_URL}",
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"capabilities": {
"streaming": false,
"pushNotifications": false,
"stateTransitionHistory": false
},
"skills": []
}For everything else - port, host, JSON-RPC path, agent-card cache-control, handler options, client retry / timeout / fetch - pass values directly to createA2AServer, createA2AClient, and the handler factories.
🔧 Advanced Usage
- Custom JSON-RPC methods - call
server.registerMethod(name, handler)with anyMethodHandlerto extend the server beyond the built-inmessage/send,tasks/get, andtasks/list. TheMethodContextpassed to handlers carries the JSON-RPC request id and anAbortSignaltied to the HTTP connection. - Custom task handlers - implement the
TaskHandler(background) orStreamableTaskHandler(streaming) interface to ship arbitrary agent logic. See Custom task handlers below. - Custom storage backends - implement the
TaskStorageinterface and pass your implementation intocreateMessageSendHandler({ storage }),createTaskGetHandler({ storage }), andcreateTaskListHandler({ storage }). Anything that satisfies the interface - Redis, Postgres, S3-backed - drops in. - Tuning client behavior -
A2AClientConfigexposestimeoutMs,retry(a partialRetryConfigorfalse),headers,fetch,userAgent, and overrides forjsonRpcPath/agentCardPath/healthPath. CallwithRetrydirectly when you want to apply the same retry policy outside the client. - Bundle-time metadata injection - use
tsup'sdefineoption to bakeBUILD_AGENT_NAME/_DESCRIPTION/_VERSIONinto the bundled output instead of relying on the runtime environment. See Build-Time Agent Metadata above. - CloudEvents forwarding - wrap your task-state transitions in
createCloudEventand POST them to a webhook or message bus for downstream subscribers, using the spec-compliantCLOUDEVENTS_CONTENT_TYPE.
Custom task handlers
TaskHandler and StreamableTaskHandler let you plug arbitrary agent logic into the server via A2AServerBuilder. Both mirror the Go ADK's server/task_handler.go interfaces; the TypeScript variant uses an AbortSignal on the context for cancellation instead of Go's context.Context.
import {
A2AServerBuilder,
AGENT_EVENT_TYPE,
BaseStreamableTaskHandler,
BaseTaskHandler,
TASK_STATE,
createCloudEvent,
transitionTask,
type AgentCard,
type CloudEvent,
type ManagedTask,
type Message,
type TaskHandlerContext,
} from '@inference-gateway/adk';
// Background handler - return the updated task once processing finishes.
class EchoTaskHandler extends BaseTaskHandler {
async handleTask(
_ctx: TaskHandlerContext,
task: ManagedTask,
_message: Message
): Promise<ManagedTask> {
let next = task;
if (next.state === TASK_STATE.PENDING) {
next = transitionTask(next, TASK_STATE.IN_PROGRESS);
}
return transitionTask(next, TASK_STATE.COMPLETED);
}
}
// Streaming handler - yield CloudEvents as the task progresses.
class EchoStreamHandler extends BaseStreamableTaskHandler {
async *handleStreamingTask(
ctx: TaskHandlerContext,
task: ManagedTask,
message: Message
): AsyncIterable<CloudEvent> {
if (ctx.signal.aborted) return;
yield createCloudEvent({
type: AGENT_EVENT_TYPE.DELTA,
subject: task.id,
data: {
messageId: crypto.randomUUID(),
role: 'ROLE_AGENT',
contextId: task.contextId,
taskId: task.id,
parts: [{ text: 'echo: ' + (message.parts[0]?.text ?? '') }],
},
});
}
}
const card: AgentCard = /* ... */;
const server = new A2AServerBuilder({})
.withAgentCard(card)
.withTaskHandler(new EchoTaskHandler())
// Or, for a streaming agent card (capabilities.streaming === true):
// .withStreamableTaskHandler(new EchoStreamHandler())
.build();
await server.listen(8080, '127.0.0.1');Handler contracts:
- Both interfaces receive a
TaskHandlerContextwhosesignalaborts when the originating request is cancelled (client disconnect, deadline, shutdown). Propagate it to LLM calls, tool dispatches, and fetches so cancellation actually unwinds. TaskHandler.handleTaskreturns the updatedManagedTask. UsetransitionTaskto advance the state machine; terminal states (COMPLETED/FAILED/CANCELLED) tell the worker the task is done.StreamableTaskHandler.handleStreamingTaskyields raw CloudEvents that the framework forwards to the SSE response verbatim. The pipeline emits the initialIN_PROGRESStask.status.changedframe before your handler runs and the terminal status frame after it returns, so you only need to yield the in-flight payload events (AGENT_EVENT_TYPE.DELTA,TOOL_*,ITERATION_COMPLETED, etc.).setAgent(agent)is called by the builder when anOpenAICompatibleAgenthas been registered viawithAgent(...)(now or later).BaseTaskHandler/BaseStreamableTaskHandlergive you freesetAgent/getAgentaccessors so concrete subclasses only need to implement thehandle*Taskmethod.
🌐 A2A Ecosystem
This ADK is the TypeScript implementation of the Agent-to-Agent (A2A) protocol within the Inference Gateway ecosystem.
Related Projects
- Inference Gateway - Unified API gateway for AI providers
- Go ADK - Go A2A Development Kit (the most feature-complete sibling)
- Rust ADK - Rust A2A Development Kit
- Go SDK - Go client library for Inference Gateway
- TypeScript SDK - TypeScript/JavaScript client library
- Python SDK - Python client library
- Rust SDK - Rust client library
- Schemas - Canonical A2A JSON Schemas (source of truth for generated types)
A2A Agents
- Awesome A2A - Curated list of A2A-compatible agents
- Browser Agent - Web browser automation and interaction agent
- Documentation Agent - Documentation generation and management agent
- Google Calendar Agent - Google Calendar integration agent
- n8n Agent - n8n workflow automation integration agent
📋 Requirements
- Node.js: 24 LTS or later
- pnpm: 10.0 or later (10.18.0 is pinned via
package.json#packageManager) - Dependencies: see
package.json- runtime depends only onhonoand@hono/node-server
📦 Container Image
A minimal multi-stage Dockerfile that produces an OCI-compliant container image for a TypeScript A2A agent built on top of this ADK. The same Dockerfile works with docker build, podman build, buildah, kaniko, or any other OCI-compatible build tool - there's nothing Docker-specific in it. Build-time agent metadata is injected via ARG → ENV, which the ADK reads on first import:
# --- Builder ---
FROM node:24-alpine AS builder
ARG AGENT_NAME="My A2A Agent"
ARG AGENT_DESCRIPTION="A custom A2A agent built with the TypeScript ADK"
ARG AGENT_VERSION="0.1.0"
WORKDIR /app
RUN corepack enable && corepack prepare [email protected] --activate
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
ENV BUILD_AGENT_NAME=${AGENT_NAME} \
BUILD_AGENT_DESCRIPTION=${AGENT_DESCRIPTION} \
BUILD_AGENT_VERSION=${AGENT_VERSION}
RUN pnpm build
# --- Runtime ---
FROM node:24-alpine
ARG AGENT_NAME
ARG AGENT_DESCRIPTION
ARG AGENT_VERSION
RUN addgroup -g 1001 -S a2a && adduser -u 1001 -S agent -G a2a
WORKDIR /home/agent
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
RUN chown -R agent:a2a /home/agent
USER agent
ENV BUILD_AGENT_NAME=${AGENT_NAME} \
BUILD_AGENT_DESCRIPTION=${AGENT_DESCRIPTION} \
BUILD_AGENT_VERSION=${AGENT_VERSION}
CMD ["node", "dist/index.js"]Build with custom metadata - docker build, podman build, etc. all take the same flags:
docker build \
--build-arg AGENT_NAME="Weather Assistant" \
--build-arg AGENT_DESCRIPTION="AI-powered weather forecasting agent" \
--build-arg AGENT_VERSION="0.1.1" \
-t my-a2a-agent .📄 License
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.
🤝 Contributing
Contributions are welcome - whether you're fixing bugs, adding features, improving documentation, or helping bring more of the Go ADK feature surface to TypeScript.
Please see the Contributing Guide for:
- 🚀 Getting Started - Prerequisites, Flox/manual dev environment setup
- 📋 Development Workflow - pnpm scripts, regenerating A2A types, the inner loop
- 🎯 Coding Guidelines - TypeScript strictness flags, style, and comment conventions
- 🛠️ Making Changes - Branch naming and Conventional Commit format
- 🧪 Testing Guidelines - Vitest layout, running a single test, the drift check
- 🔄 Continuous Integration - CI matrix and required status checks
- 🚢 Releases - How semantic-release computes the next version
- 🔄 Pull Request Process - Pre-submit checklist and review flow
Quick start for contributors:
# Fork the repo on GitHub, then:
git clone https://github.com/your-username/typescript-adk.git
cd typescript-adk
pnpm install
pnpm testFor questions or help getting started, please open a discussion or file an issue.
📞 Support
Issues & Questions
- Bug Reports: GitHub Issues
- Documentation: Official Docs
