{"_id":"@bump-sh/agent-conversation","_rev":"2-5c70549ab14465689997c31bcc8c8e0d","name":"@bump-sh/agent-conversation","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@bump-sh/agent-conversation","version":"0.1.0","keywords":["bump.sh","agent","mcp","chat","streaming","ndjson"],"license":"MIT","_id":"@bump-sh/agent-conversation@0.1.0","maintainers":[{"name":"paulrbr","email":"paul@bonaud.fr"},{"name":"scharrier","email":"sebastien@bump.sh"}],"homepage":"https://github.com/bump-sh/agent-client/tree/main/packages/agent-conversation#readme","bugs":{"url":"https://github.com/bump-sh/agent-client/issues"},"dist":{"shasum":"9fbd98d10b10b60b876be92efc9950d174f4a5f7","tarball":"https://registry.npmjs.org/@bump-sh/agent-conversation/-/agent-conversation-0.1.0.tgz","fileCount":8,"integrity":"sha512-CHWkc3nU340G17KOu30dLdViVOEwzpNXbtjDuu+ugjw920MVPNwtRD1kOM32YSR3Xe7PDfcFiMHL4kUgK/kMTw==","signatures":[{"sig":"MEUCIQDb0h8neGlh8FzHjl+oig4qpirFYPRgGT+Of/dYwPQh+AIgAnH6vY8lExODETTDURHqCkuC737rPzmQe/pmlLRSVgk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":48564},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"bb61054d3f5035080ddc9d6e2df8dfb9a11682d6","scripts":{"test":"vitest run","build":"tsup","check":"biome check src test","format":"biome format --write src test","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"scharrier","email":"sebastien@bump.sh"},"repository":{"url":"git+https://github.com/bump-sh/agent-client.git","type":"git","directory":"packages/agent-conversation"},"_npmVersion":"10.9.4","description":"Tiny, dependency-free client for streaming conversations with a Bump.sh agent API endpoint.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/agent-conversation_0.1.0_1786711359026_0.9434708360973438","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"_id":"@bump-sh/agent-conversation@0.2.0","bugs":{"url":"https://github.com/bump-sh/agent-client/issues"},"dist":{"shasum":"57478e337b031346b80f0e345c5546c136451df3","tarball":"https://registry.npmjs.org/@bump-sh/agent-conversation/-/agent-conversation-0.2.0.tgz","fileCount":8,"integrity":"sha512-rTyZBexbh6tJD39Abepl6ECUqEHpnZldSG/cYVtU6BNSgI5Kt5k0rNtb35UNnlMhXi8xwzPeZB6JbWHYkreleg==","signatures":[{"sig":"MEUCIF8WWUVA6c0ddQFzpuYjvEoKj4szxng02400M9pKSUpkAiEAnBhqFwdpZsUAzVfYTuXEf5S5FPwFeqC3A/ZAHQCQHf4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQD4w1I6sUJKaXMhwOibtoFyD05lk4D3LsMWguQ1Vs1R0QIhAN6BuQfrlNcVQjmRAy9CoLfsmXVK6v6xIiq4DJTODt8a"}],"unpackedSize":49681},"main":"./dist/index.cjs","name":"@bump-sh/agent-conversation","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"6dc8c559fb21c716f3d98afa7dc22c6114d7345d","license":"MIT","scripts":{"test":"vitest run","build":"tsup","check":"biome check src test","format":"biome format --write src test","test:watch":"vitest","prepublishOnly":"npm run build"},"version":"0.2.0","_npmUser":{"name":"paulrbr","email":"paul@bonaud.fr"},"homepage":"https://github.com/bump-sh/agent-client/tree/main/packages/agent-conversation#readme","keywords":["bump.sh","agent","mcp","chat","streaming","ndjson"],"repository":{"url":"git+https://github.com/bump-sh/agent-client.git","type":"git","directory":"packages/agent-conversation"},"_npmVersion":"11.12.1","description":"Tiny, dependency-free client for streaming conversations with a Bump.sh agent API endpoint.","directories":{},"maintainers":[{"name":"paulrbr","email":"paul@bonaud.fr"},{"name":"scharrier","email":"sebastien@bump.sh"}],"sideEffects":false,"_nodeVersion":"24.15.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/agent-conversation_0.2.0_1790760075012_0.9527173215818876"}}},"time":{"created":"2026-08-14T12:42:38.843Z","modified":"2026-09-30T09:21:15.593Z","0.1.0":"2026-08-14T12:42:39.157Z","0.2.0":"2026-09-30T09:21:15.123Z"},"bugs":{"url":"https://github.com/bump-sh/agent-client/issues"},"license":"MIT","homepage":"https://github.com/bump-sh/agent-client/tree/main/packages/agent-conversation#readme","keywords":["bump.sh","agent","mcp","chat","streaming","ndjson"],"repository":{"url":"git+https://github.com/bump-sh/agent-client.git","type":"git","directory":"packages/agent-conversation"},"description":"Tiny, dependency-free client for streaming conversations with a Bump.sh agent API endpoint.","maintainers":[{"name":"paulrbr","email":"paul@bonaud.fr"},{"name":"scharrier","email":"sebastien@bump.sh"}],"readme":"# @bump-sh/agent-conversation\n\nTiny, dependency-free client for streaming a conversation with a\n[Bump.sh](https://bump.sh) agent API endpoint. Drop it into any web app to let\nyour users chat with an agent scoped to your Bump.sh MCP server.\n\n- **Zero dependencies** — uses native `fetch` / `ReadableStream`.\n- **Stateful** — keeps the message history for you.\n- **Two styles, one call** — `await` the reply, or `for await` the stream.\n- **TypeScript** — fully typed events.\n\nBuilding a chat UI? [`@bump-sh/agent-widget`](../agent-widget) is a drop-in\nwidget built on this package.\n\n## Install\n\n```sh\nnpm install @bump-sh/agent-conversation\n```\n\nRequires a runtime with native `fetch` and `ReadableStream`: any modern\nbrowser, or Node.js ≥ 18.\n\n## Quickstart (the 5-minute path)\n\n```ts\nimport { Conversation } from \"@bump-sh/agent-conversation\"\n\nconst conversation = new Conversation({\n  endpoint: \"https://your-host/demo/weather/agent\",\n})\n\nconversation.on(\"text\", (delta) => {\n  document.querySelector(\"#reply\").textContent += delta\n})\n\nawait conversation.send(\"What's the weather in Paris?\")\n```\n\n`send()` resolves to the full assistant reply, so you can also just:\n\n```ts\nconst reply = await conversation.send(\"What's the weather in Paris?\")\nconsole.log(reply)\n```\n\n## Streaming every event (the control path)\n\nSame call — iterate it instead of awaiting:\n\n```ts\nfor await (const event of conversation.send(\"What's the weather in Paris?\")) {\n  if (event.type === \"text\") append(event.delta)\n  if (event.type === \"tool\") showToolActivity(event.names)\n  if (event.type === \"error\") showError(event.error)\n}\n```\n\n```ts\ntype AgentEvent =\n  | { type: \"text\"; delta: string }   // a chunk of the assistant reply\n  | { type: \"tool\"; names: string[] } // the agent started running these tools\n  | { type: \"error\"; error: Error }   // the turn failed (network or agent error)\n```\n\n## API\n\n### `new Conversation(options)`\n\n| option     | type                                          | description                                                        |\n| ---------- | --------------------------------------------- | ------------------------------------------------------------------ |\n| `endpoint` | `string` (required)                           | The agent endpoint URL to POST to.                                 |\n| `token`    | `string \\| () => string \\| Promise<string>`   | Bearer token → `Authorization`. A callback is re-evaluated per request, so short-lived tokens refresh. |\n| `config`   | `Record<string, string>` or a callback        | Agent configuration keys, sent as `Config-<Key>` request headers.  |\n| `headers`  | `Record<string, string>` or a callback        | Extra request headers, merged last.                                |\n| `allowedTools` | `string[]`                                | Focus the agent on a specific set of tools, sent as `allowed_tools` in the request body. Defaults to `[]` (no restriction). |\n| `fetch`    | `typeof fetch`                                | Custom fetch (SSR, testing). Defaults to `fetch`.                  |\n| `messages` | `Message[]`                                   | Seed the conversation history (`{ role: \"user\" \\| \"assistant\", content: string }`). |\n\n### `conversation.send(content, { signal? })`\n\nSends a user turn and streams the reply. Returns a `StreamResult` that is both:\n\n- **awaitable** → resolves to the assembled assistant reply, rejects on error;\n- **async-iterable** → yields `AgentEvent`s. Iteration never throws — errors\n  arrive as `{ type: \"error\" }` events, so your loop always runs to completion.\n\nTwo things to know about the `StreamResult`:\n\n- **The request is lazy.** Nothing is sent over the network until you `await`\n  the result or start iterating it. A bare `conversation.send(\"hi\")` with the\n  result discarded sends nothing.\n- **Consume it once, either way.** It wraps a single underlying stream:\n  awaiting *and* iterating (or iterating twice) drains it once — the second\n  consumer sees an empty stream. To both render deltas and get the full\n  reply, iterate and accumulate, or combine `on(\"text\", …)` with `await`.\n\nPass an `AbortSignal` to cancel a turn in flight:\n\n```ts\nconst controller = new AbortController()\nconst result = conversation.send(\"Summarize this very long document…\", {\n  signal: controller.signal,\n})\ncontroller.abort() // surfaces as an \"error\" event / rejection\n```\n\n### `conversation.on(event, handler)`\n\nSubscribe to stream events across all turns — the callback style, equivalent\nto iterating. Returns an unsubscribe function.\n\n| event       | handler payload    | fires                                                        |\n| ----------- | ------------------ | ------------------------------------------------------------ |\n| `\"text\"`    | `delta: string`    | For each chunk of the assistant reply.                       |\n| `\"tool\"`    | `names: string[]`  | When the agent starts running tools.                         |\n| `\"error\"`   | `error: Error`     | On a network or agent error.                                 |\n| `\"message\"` | `content: string`  | When a turn ends, with the full assistant reply (`\"\"` if the turn failed before any text). |\n| `\"done\"`    | —                  | When a turn ends, success or failure.                        |\n\n```ts\nconst off = conversation.on(\"tool\", (names) => console.log(\"running\", names))\noff() // stop listening\n```\n\n### `conversation.messages` / `conversation.reset()`\n\n`messages` is a read-only snapshot of the history (user and assistant turns) —\nthe user turn is recorded as soon as you call `send()`, the assistant turn when\nits reply finishes. `reset()` clears the history to start fresh.\n\n## Authentication\n\nPass a `token` — it's sent as `Authorization: Bearer <token>`:\n\n```ts\n// short-lived token, refreshed transparently on every turn\nnew Conversation({\n  endpoint,\n  token: async () => (await fetch(\"/agent-token\")).text(),\n})\n```\n\nThe token travels in the browser, so **mint a user-scoped, short-lived token\nserver-side** (a signed JWT your API verifies is ideal) — never ship a raw or\ntenant-wide API key to the page. In your workflow file, the token is available\nas `$current_user.token`.\n\n## Configuration & custom headers\n\nPass `config` to configure the agent: each key is sent as a `Config-<Key>`\nrequest header and is available in your workflow file as `$config.<key>`\n(`config: { locale: \"fr\" }` → `$config.locale`). Use `headers` for any other\nextra header. Both take a map, or a callback re-evaluated on every request\nfor values that change over time:\n\n```ts\nnew Conversation({\n  endpoint,\n  config: { locale: \"fr\" },\n  headers: () => ({ \"X-Request-Id\": crypto.randomUUID() }),\n})\n```\n\nKey matching is case-insensitive and treats `-` and `_` as equivalent:\n`config: { \"doc-id\": \"42\" }` can be read as `$config.doc_id`. Prefer\ndash-separated keys — some proxies drop headers with underscores.\n\nValues travel as raw HTTP header values, so keep them ASCII (identifiers,\nlocales, URLs) — encode anything richer yourself.\n\n## Error handling\n\nA failed turn (non-2xx response, network failure, agent error, abort)\nsurfaces three ways — pick the one matching how you consume the stream:\n\n- `await conversation.send(…)` **rejects** with the `Error`;\n- iterating yields an `{ type: \"error\", error }` event and the stream ends;\n- `on(\"error\", handler)` fires.\n\nAny text streamed before the failure is kept in `conversation.messages`, so\nthe history stays consistent with what the user saw.\n\n## Wire protocol\n\nThe client speaks the Bump.sh agent API protocol: it `POST`s\n`{ \"messages\": [{ \"role\", \"content\" }] }` as JSON — the full history, every\nturn — and reads an `application/x-ndjson` response, one JSON event per line:\n\n```\n{ \"type\": \"text\",  \"content\": \"It's sunny\" }\n{ \"type\": \"tool\",  \"names\": [\"get_weather\"] }\n{ \"type\": \"error\", \"content\": \"…\" }\n{ \"type\": \"done\" }\n```\n\n## Exports\n\n`Conversation`, `StreamResult`, and the types `AgentEvent`,\n`ConversationOptions`, `HeadersProvider`, `Message`, `Role`, `SendOptions`,\n`TokenProvider`. ESM and CJS builds are shipped.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}