Custom adapter (Node)
Custom adapter (Node)
Build a custom adapter with @astropods/adapter-core
@astropods/adapter-core is the framework-agnostic core. Implement the AgentAdapter interface, hand it to serve(), and the bundled MessagingBridge handles the gRPC streaming loop, audio, feedback, reconnect, and shutdown.
Use this when there’s no first-party adapter for your framework (or no framework at all — you’re calling LLM APIs directly).
Install
Minimal adapter
serve(adapter) connects to localhost:9090 (or GRPC_SERVER_ADDR) and runs until the process exits.
Lifecycle
AgentAdapter
StreamHooks
Call these from inside stream() and streamAudio(). They translate directly into outbound AgentResponse messages on the gRPC stream.
StatusUpdate.status values: THINKING, SEARCHING, GENERATING, PROCESSING, ANALYZING, CUSTOM. Use CUSTOM with customMessage.
StreamOptions
The bridge passes this to every stream() / streamAudio() call.
AudioInput
Passed to streamAudio().
FeedbackEvent
Passed to onFeedback(). kind is a stable string discriminator so you don’t have to import proto types to switch on it.
FeedbackEvent.kind values
onFeedback() may return void or a Promise. The bridge does not await the result — so don’t block the stream on slow I/O. Push to a queue or trigger an async write and return.
serve(adapter, options?)
Thin wrapper around MessagingBridge that handles process lifecycle.
ServeOptions.serverAddress defaults to process.env.GRPC_SERVER_ADDR || 'localhost:9090'.
Worked example: a plain OpenAI agent
No framework — just calling OpenAI’s streaming chat API and forwarding tokens.
Rules of thumb
- Call exactly one of
onFinish()oronError()per request. Skipping either leaves the user staring at a half-rendered reply. - Catch your own exceptions inside
stream(). If you let the promise reject, the user sees nothing. - Don’t block in
onFeedback()— push to a queue or trigger async work and return. - Use
options.conversationId(notplatformContext.threadId) as your memory key. - Set up OTEL manually before calling
serve()if you want traces. Framework adapters auto-configure this; the raw core does not.
Re-exported types
logger is a Pino instance. Use it for adapter logs so output stays consistent with the bridge.
See Messaging SDK (Node) for the underlying proto shapes referenced by StreamHooks and PlatformContext.