https://github.com/synadia-ai/synadia-agents
SDKs and reference implementations for the Synadia Agent Protocol for NATS
https://github.com/synadia-ai/synadia-agents
agent-protocol ai ai-agents claude-code dspy hermes-agent llm multi-agent multi-agent-system nats nats-io open-agents openclaw pi-coding-agent python sdk typescript
Last synced: 2 days ago
JSON representation
SDKs and reference implementations for the Synadia Agent Protocol for NATS
- Host: GitHub
- URL: https://github.com/synadia-ai/synadia-agents
- Owner: synadia-ai
- License: apache-2.0
- Created: 2026-04-22T16:18:14.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-07-14T14:24:07.000Z (8 days ago)
- Last Synced: 2026-07-14T16:24:11.506Z (8 days ago)
- Topics: agent-protocol, ai, ai-agents, claude-code, dspy, hermes-agent, llm, multi-agent, multi-agent-system, nats, nats-io, open-agents, openclaw, pi-coding-agent, python, sdk, typescript
- Language: TypeScript
- Homepage:
- Size: 3.31 MB
- Stars: 63
- Watchers: 5
- Forks: 6
- Open Issues: 3
-
Metadata Files:
- Readme: README-DEV.md
- License: LICENSE
Awesome Lists containing this project
README
# Local development
Quick reference for working on the TypeScript packages in this repo
without publishing to npm. The Python side is documented in
[`client-sdk/python/CLAUDE.md`](client-sdk/python/CLAUDE.md).
The TypeScript SDK ships as **two npm packages** that always release in
lockstep:
| Path | Package | Role |
| --- | --- | --- |
| `client-sdk/typescript/` | `@synadia-ai/agents` | Caller side — discover, prompt, stream. Most consumers want only this. |
| `agent-sdk/typescript/` | `@synadia-ai/agent-service` | Host side — `AgentService`, `ReferenceAgent`, server-side wire helpers. Depends on the caller package. |
Inside the monorepo every consumer (`agents/*`, `examples/*`) refers to
both packages via `file:` links in its `package.json`. Bun **copies**
those links at install time rather than symlinking, so an edit to
either SDK is invisible to a consumer until that consumer's install is
refreshed against a freshly built `dist/`.
The same applies inside `agent-sdk/typescript/` itself: it depends on
`@synadia-ai/agents` via `file:`, so its own `node_modules` carries a
copy of the caller package that needs caller's `dist/` to be present
for the host's compiled `dist/index.cjs` to load.
The recipes below all account for this.
## Building the SDKs
Build caller first, then host (host depends on caller's `dist/`):
```sh
(cd client-sdk/typescript && bun run build)
(cd agent-sdk/typescript && bun install && bun run build)
```
The extra `bun install` in `agent-sdk/typescript` re-copies the
freshly-built caller `dist/` into `agent-sdk/typescript/node_modules/@synadia-ai/agents/`,
which is the path host's compiled output resolves at runtime.
Skip the build commands above if neither SDK has changed since the
last build — but if you're unsure, rebuilding is cheap (~1s each).
## Running the examples
Each example has a `start` script that runs the source directly with
`bun run`. After building the SDKs, refresh the example's install so
its copy of each SDK reflects the latest `dist/`, then run:
```sh
cd examples/pi-headless # or examples/claude-code-headless / examples/dspy
bun install
bun run start
```
`bun install` is what copies the freshly-built SDKs into the example's
`node_modules`. Without it the example silently runs against whatever
`dist/` was current at the previous install.
For the browser test client `examples/agent-web-ui` only the caller
SDK matters (it doesn't host an agent), but the install dance is the
same.
## Running the SDK-side examples
Both SDKs ship runnable example scripts next to the package source —
useful as smoke targets while iterating on the SDK or as starting
shapes for new agents:
| Path | What it does |
| --- | --- |
| `client-sdk/typescript/examples/01-discover.ts` … `05-liveness.ts` | Caller-side demos against a running agent. |
| `client-sdk/typescript/examples/_run-reference-agent.ts` | Spec-compliant `ReferenceAgent` to point the caller demos at. |
| `agent-sdk/typescript/examples/01-echo.ts` | Minimal `AgentService` echo agent. |
Each script supports `$NATS_CONTEXT`, `$NATS_URL`, or falls back to
`nats://127.0.0.1:4222`. Run with:
```sh
bun client-sdk/typescript/examples/_run-reference-agent.ts
bun agent-sdk/typescript/examples/01-echo.ts
```
## Installing extension-style agent plugins locally (PI, OpenClaw, Claude Code)
`agents/pi/`, `agents/openclaw/`, and `agents/claude-code/` are
extension/plugin packages that get loaded by their host application
(`pi`, `openclaw`, the Claude Code MCP runtime). When the host loads
the extension it follows the `file:` link in the extension's
`package.json` back to the SDK source — so both SDKs need a current
`dist/` when the extension is installed.
Other agent packages in `agents/`, including `agents/flue/`,
`agents/opencode/`, and `agents/codex/`, run as
sidecars or wrappers rather than host-loaded extensions; follow their
per-agent READMEs for local startup.
```sh
# Build the SDKs, then install the extension into its host application.
(cd client-sdk/typescript && bun run build)
(cd agent-sdk/typescript && bun install && bun run build)
# Pi:
pi install $(pwd)/agents/pi
pi
# OpenClaw:
openclaw install $(pwd)/agents/openclaw
# Claude Code (MCP plugin):
# follow the install steps in agents/claude-code/README.md
```
If the host complains with a path like
`Cannot find module '.../agent-sdk/typescript/node_modules/@synadia-ai/agents/dist/index.cjs'`,
the missing `dist/` is in `agent-sdk/typescript/node_modules/@synadia-ai/agents/` —
re-run the SDK build sequence above (the `bun install` step in
`agent-sdk/typescript` is what populates that path).
## Editing one SDK without rebuilding the other
When iterating on caller-side code only:
```sh
(cd client-sdk/typescript && bun run build)
(cd agent-sdk/typescript && bun install) # refresh nested caller copy
# then refresh the consumer's install (cd to consumer dir, bun install)
```
When iterating on host-side code only:
```sh
(cd agent-sdk/typescript && bun run build)
# then refresh the consumer's install
```
## Running the test suites
The TS test suites use vitest's `resolve.alias` to resolve
`@synadia-ai/agents` and `@synadia-ai/agent-service` directly to source.
That bypasses `dist/` for tests, so the suites run fine without
building first:
```sh
(cd client-sdk/typescript && bun run check) # typecheck + lint + format + tests
(cd agent-sdk/typescript && bun run check)
```
`bun run check` will pull in the sibling SDK's source via path aliases.
On a fresh clone install both packages first so the transitive
`@nats-io/*` deps are available to both checkouts:
```sh
(cd client-sdk/typescript && bun install)
(cd agent-sdk/typescript && bun install)
```
CI runs the same shape — see
[`.github/workflows/client-sdk-typescript.yml`](.github/workflows/client-sdk-typescript.yml)
and [`.github/workflows/agent-sdk-typescript.yml`](.github/workflows/agent-sdk-typescript.yml).
## Releasing the SDKs
`main` keeps `file:` links between consumers and the SDK packages so
contributors editing the SDK see their changes live in the
agents/examples without any flip step. That's also why a fresh `npm
publish` of any consumer would ship `file:` refs that break for npm
users — published tarballs need `^semver` instead. The
[`devtools/devmode.sh`](devtools/devmode.sh) script bridges the two
states.
```sh
./devtools/devmode.sh status # what's currently flipped where
./devtools/devmode.sh off # flip every tracked consumer to ^semver
./devtools/devmode.sh on # flip back to file: (the default state)
./devtools/devmode.sh check-release # exit 0 iff every dep is at its SDK's ^semver
```
The script discovers consumers automatically — every `package.json`
under `examples/`, `agents/`, `client-sdk/`, and `agent-sdk/` that
depends on a tracked SDK gets flipped. Names listed in
`devtools/.devmodeignore` are skipped (currently just `dspy`, which
lives on `file:` permanently).
### The release ladder (one cycle)
Order matters: caller `@synadia-ai/agents` first because the host SDK
declares `^0.4.x` against it; agent harnesses and headless examples
follow once both SDKs are on npm. Each `npm publish` is a separate
user-approval gate — read the dry-run output before pulling the
trigger.
```sh
# 1. Pre-flight: confirm versions, identity, and tarball shape.
git status # tree must be clean
jq -r '.version' client-sdk/typescript/package.json
jq -r '.version' agent-sdk/typescript/package.json
npm whoami # the @synadia-ai publish identity
# 2. Build dist/ artifacts fresh.
(cd client-sdk/typescript && bun install && bun run build)
(cd agent-sdk/typescript && bun install && bun run build)
# 3. Flip to release mode.
./devtools/devmode.sh off
# 4. Publish caller, then host. Inspect each dry-run before publishing.
(cd client-sdk/typescript && npm publish --dry-run && npm publish)
(cd agent-sdk/typescript && npm publish --dry-run && npm publish)
# 5. Publish each consumer that needs to ship.
# Bundled (agents/openclaw, agents/pi) — `bun install` first so
# bundleDependencies can copy the SDKs into the tarball.
(cd agents/openclaw && bun install && npm publish --dry-run && npm publish)
(cd agents/pi && bun install && npm publish --dry-run && npm publish)
# Plain plugin packages with Bun TypeScript entrypoints.
(cd agents/opencode && bun install && npm publish --dry-run && npm publish)
(cd agents/codex && bun install && npm publish --dry-run && npm publish)
# Plain (examples/pi-headless, examples/claude-code-headless) — the
# `prepack` hook builds dist/ on its own.
(cd examples/pi-headless && npm publish --dry-run && npm publish)
(cd examples/claude-code-headless && npm publish --dry-run && npm publish)
# 6. Flip back to dev mode and commit any non-empty diff.
./devtools/devmode.sh on
git status
```
### Gotchas the script accounts for (so you don't trip over them)
- **`agent-sdk/typescript`'s self-dep on caller.** Discovery scans
`agent-sdk/` and `client-sdk/` in addition to `examples/` and
`agents/`. Without that, the host SDK would publish with a `file:`
ref to caller, which breaks every npm consumer of the host.
- **`bun install --silent` can spin on `agents/openclaw`.** Its
`peerDependencies: { openclaw: "" }` (empty version range) sends bun
into a 100%-CPU walk. Each per-consumer `bun install` is wrapped in
`timeout 60` (override with `BUN_INSTALL_TIMEOUT=…`); the script
prints a `⏱ timed out` line and continues.
- **`^semver` `bun install` failures pre-publish are normal.** Before
the SDK pair is on npm, `devmode.sh off` flips the deps but the
follow-on `bun install` can't resolve `^0.4.0` against an empty
registry. The script treats those as best-effort; the package.json
flips themselves succeed and that's what `npm publish` reads.
## Troubleshooting
| Symptom | Likely cause | Fix |
| --- | --- | --- |
| `Cannot find module '@synadia-ai/agent-service'` at startup | Consumer's install is older than the host SDK split | `bun install` in the consumer dir |
| `Cannot find module '.../agent-sdk/typescript/node_modules/@synadia-ai/agents/dist/index.cjs'` | Caller's `dist/` not present in agent-sdk's nested install | `(cd client-sdk/typescript && bun run build) && (cd agent-sdk/typescript && bun install)` |
| Edits to SDK source aren't reflected when running an example or extension | Consumer's `node_modules` carries a stale copy | Rebuild the SDK(s) and re-`bun install` in the consumer |
| `Failed to resolve entry for package "@synadia-ai/agents"` from vitest | Stale CI-style install without sibling SDK source | `bun install` in the sibling SDK directory |
| `./devtools/devmode.sh off` hangs on `agents/openclaw` | bun's empty-string peer-dep walk | The script auto-times-out at 60 s; kill manually if you ran an older version |
| `./devtools/devmode.sh off` reports `bun install` failures with `404` / `No version matching ^x.y.z` | Pre-publish — the SDKs aren't on npm yet | Expected; the package.json flips succeeded. Run again after `npm publish` to refresh lockfiles. |
## Why not workspaces?
A Bun workspace would symlink the `file:` packages and remove most of
the rebuild/reinstall dance, at the cost of a non-trivial restructure
(root `package.json`, repo-wide `bun.lock`, and a publish workflow that
correctly handles workspace deps). The current layout keeps each
package self-contained and publishable on its own.