{"_id":"@deepaksankhyan91/open-agent-sdk","name":"@deepaksankhyan91/open-agent-sdk","dist-tags":{"next":"0.2.1-next.0","latest":"0.2.1-next.0"},"versions":{"0.2.1-next.0":{"name":"@deepaksankhyan91/open-agent-sdk","version":"0.2.1-next.0","description":"Provider-neutral, type-safe agent SDK for TypeScript","keywords":["ai","agent","anthropic","openai","sdk","typescript"],"homepage":"https://github.com/gitdeepaks/custom-agent-sdk#readme","bugs":{"url":"https://github.com/gitdeepaks/custom-agent-sdk/issues"},"repository":{"type":"git","url":"git+https://github.com/gitdeepaks/custom-agent-sdk.git"},"author":{"name":"Deepak Sankhyan"},"module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./openai":{"types":"./dist/providers/openai/index.d.ts","import":"./dist/providers/openai/index.js"},"./anthropic":{"types":"./dist/providers/anthropic/index.d.ts","import":"./dist/providers/anthropic/index.js"}},"type":"module","sideEffects":false,"scripts":{"clean":"rm -rf dist coverage","build":"bun ./scripts/build.ts","format":"biome format --write .","format:check":"biome format .","lint":"biome lint .","test":"bun test","test:coverage":"bun test --coverage --coverage-reporter=text --coverage-reporter=lcov && bun ./scripts/coverage-gate.ts","test:performance":"bun ./scripts/performance-gate.ts","test:live":"bun ./scripts/live-provider-smoke.ts","typecheck":"tsc --noEmit","verify:artifacts":"bun ./scripts/verify-package.ts","verify:package":"bun ./scripts/package-smoke.ts","check":"bun run format:check && bun run lint && bun run typecheck && bun run test","check:release":"bun run check && bun run test:coverage && bun run test:performance && bun run build && bun run verify:artifacts && bun run verify:package","prepack":"bun run check && bun run build && bun run verify:artifacts"},"devDependencies":{"@biomejs/biome":"2.5.6","@types/bun":"1.3.14","typescript":"7.0.2"},"engines":{"bun":">=1.2.0"},"packageManager":"bun@1.3.14","publishConfig":{"access":"public","provenance":true},"license":"MIT","dependencies":{"@opentelemetry/api":"^1.9.1"},"gitHead":"dbaf97aa9327a8c8213e8d321b6baae5d723af36","_id":"@deepaksankhyan91/open-agent-sdk@0.2.1-next.0","_nodeVersion":"24.11.1","_npmVersion":"11.14.1","dist":{"integrity":"sha512-8rbd6x6BETWKDLVEEfdJ1li5K7GSkXBPpI+SpyGJpICCTm0wIG9XRmLIlbiMqDalP27doPmTlIAIXWfvwzlHuQ==","shasum":"44bc1236d112d2025842ee144eaaee796e70e041","tarball":"https://registry.npmjs.org/@deepaksankhyan91/open-agent-sdk/-/open-agent-sdk-0.2.1-next.0.tgz","fileCount":45,"unpackedSize":499426,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDlsn9kQBynsrZhFHoQN6f2FIBADyORpMPUCnVaGfxMegIhAKg0XoZ1zYliy980aAjqbOh6KM3McSugDUHjUq2CdhqV"}]},"_npmUser":{"name":"deepaksankhyan","email":"deepaksankhyan92@outlook.com"},"directories":{},"maintainers":[{"name":"deepaksankhyan","email":"deepaksankhyan92@outlook.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/open-agent-sdk_0.2.1-next.0_1785670499626_0.14267240304310236"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-02T11:34:59.465Z","0.2.1-next.0":"2026-08-02T11:34:59.755Z","modified":"2026-08-02T11:34:59.972Z"},"maintainers":[{"name":"deepaksankhyan","email":"deepaksankhyan92@outlook.com"}],"description":"Provider-neutral, type-safe agent SDK for TypeScript","homepage":"https://github.com/gitdeepaks/custom-agent-sdk#readme","keywords":["ai","agent","anthropic","openai","sdk","typescript"],"repository":{"type":"git","url":"git+https://github.com/gitdeepaks/custom-agent-sdk.git"},"author":{"name":"Deepak Sankhyan"},"bugs":{"url":"https://github.com/gitdeepaks/custom-agent-sdk/issues"},"license":"MIT","readme":"# Open Agent SDK\n\nA provider-neutral, type-safe agent SDK for TypeScript, designed around the ergonomic core of the Vercel AI SDK while keeping providers and application concerns separate.\n\n## Status\n\nThis repository includes a provider-neutral core plus first-party OpenAI Responses API and Anthropic Messages API adapters. It supports text and structured generation, multipart messages, native streaming, runtime-validated tools, bounded tool loops, retries, cancellation, normalized provider errors and usage, and a reusable `Agent` API.\n\n## Requirements\n\n- Bun 1.2 or newer\n- TypeScript 7\n\n## Install\n\n```bash\nbun add @deepaksankhyan91/open-agent-sdk\n```\n\nThe package can also be installed with npm:\n\n```bash\nnpm install @deepaksankhyan91/open-agent-sdk\n```\n\n## Environment Variables\n\nCreate a local environment file from the committed template:\n\n```bash\ncp .env.example .env\n```\n\nBun automatically loads `.env`; no `dotenv` dependency is required. Set only the credentials needed by the provider adapter used by your application:\n\n```env\n# Used by an OpenAI adapter\nOPENAI_API_KEY=sk-...\n\n# Used by an Anthropic adapter\nANTHROPIC_API_KEY=sk-ant-...\n```\n\n| Variable             | Required           | Purpose                                                         |\n| -------------------- | ------------------ | --------------------------------------------------------------- |\n| `OPENAI_API_KEY`     | For OpenAI only    | Authenticates requests made by an OpenAI provider adapter.      |\n| `ANTHROPIC_API_KEY`  | For Anthropic only | Authenticates requests made by an Anthropic provider adapter.   |\n| `OPENAI_BASE_URL`    | No                 | Overrides the OpenAI endpoint when supported by the adapter.    |\n| `ANTHROPIC_BASE_URL` | No                 | Overrides the Anthropic endpoint when supported by the adapter. |\n\nThe core `@deepaksankhyan91/open-agent-sdk` package does not read these variables. Credentials belong to the application or provider package that constructs the `LanguageModel`. Do not use `OPENAI_API_KEY || ANTHROPIC_API_KEY`; select a provider explicitly and validate its corresponding key. Never commit `.env` or real credentials. The repository ignores `.env` while retaining `.env.example` as documentation.\n\n## Quick Start\n\nCreate a provider explicitly and pass its protocol-v1 model to the core:\n\n```ts\nimport { Agent, defineSchema, tool } from \"@deepaksankhyan91/open-agent-sdk\";\nimport { createOpenAI } from \"@deepaksankhyan91/open-agent-sdk/openai\";\n\nconst apiKey = Bun.env.OPENAI_API_KEY;\nif (!apiKey) throw new Error(\"OPENAI_API_KEY is required\");\n\nconst model = createOpenAI({ apiKey }).languageModel(\"your-model-id\");\n\nconst weather = tool({\n  name: \"weather\",\n  description: \"Get the weather for a city\",\n  inputSchema: defineSchema({\n    jsonSchema: {\n      type: \"object\",\n      properties: { city: { type: \"string\" } },\n      required: [\"city\"],\n      additionalProperties: false,\n    },\n    parse(value) {\n      if (typeof value !== \"object\" || value === null || !(\"city\" in value)) {\n        throw new Error(\"Expected an object with city\");\n      }\n      const city = value.city;\n      if (typeof city !== \"string\") throw new Error(\"city must be a string\");\n      return { city };\n    },\n  }),\n  async execute({ city }, { abortSignal }) {\n    const response = await fetch(\n      `https://example.com/weather?city=${encodeURIComponent(city)}`,\n      {\n        signal: abortSignal,\n      },\n    );\n    return response.json();\n  },\n});\n\nconst agent = new Agent({\n  model,\n  instructions: \"You are a concise weather assistant.\",\n  tools: { weather },\n  maxSteps: 5,\n});\n\nconst result = await agent.run({ prompt: \"What is the weather in Delhi?\" });\nconsole.log(result.text);\n```\n\nThe same agent can be created with the fluent builder API:\n\n```ts\nconst agent = Agent.builder(model)\n  .setInstructions(\"You are an expert weather agent.\")\n  .tool(weather)\n  .build();\n```\n\n## Streaming\n\n```ts\nconst stream = agent.stream({ prompt: \"Explain the forecast\" });\n\nfor await (const delta of stream.textStream) {\n  await Bun.write(Bun.stdout, delta);\n}\n\nconst finalResult = await stream.result;\n```\n\n`fullStream` is the canonical bounded event stream. `textStream` is a text-only view over the same session. They are intentionally alternative views: consume one, not both. This avoids `ReadableStream.tee()` buffering while preserving provider backpressure. Await `result` after consuming the selected stream.\n\n## Structured Output\n\n`generateObject()` supports object, array, enum, and arbitrary JSON modes. OpenAI and Anthropic receive native structured-output constraints, and the core validates every final value before returning typed data:\n\n```ts\nconst profile = await generateObject({\n  model,\n  prompt: \"Generate a user profile\",\n  schema: profileSchema,\n});\n\nconsole.log(profile.object);\n```\n\nUse `mode: \"array\"` with an element schema, `mode: \"enum\"` with a literal `values` list, or `mode: \"json\"` for any `JsonValue`. An optional `repair` callback runs at most once after validation fails, and its result is fully revalidated.\n\n`streamObject()` exposes demand-driven JSON snapshots through `partialObjectStream`. These snapshots are typed as `JsonValue` because incomplete data has not passed the final schema. Only `(await stream.result).object` is returned as the schema-inferred type. `Agent.runObject()` and `Agent.streamObject()` provide the same behavior with agent settings and tool loops.\n\nNative and fallback models emit the same ordered SDK protocol:\n\n```text\nstep-start -> text-start -> text-delta* -> text-end\n           -> tool-call* -> finish -> step-finish\n```\n\nMalformed provider ordering, duplicate or missing finish events, and events after finish produce a `StreamProtocolError`. Cancelling either consumer aborts the provider request and active tools.\n\nRetries and timeouts are configured consistently for generation and streaming:\n\n```ts\nconst stream = streamText({\n  model,\n  prompt: \"Explain the forecast\",\n  retry: {\n    maxRetries: 2,\n    initialDelayMs: 100,\n    maxDelayMs: 5_000,\n    onRetry: ({ attempt, error }) => console.warn(attempt, error.code),\n  },\n  timeouts: {\n    requestMs: 60_000,\n    firstChunkMs: 15_000,\n    chunkMs: 15_000,\n    toolMs: 30_000,\n  },\n});\n```\n\nOnly classified transient failures are retried. Once provider output is externally visible, a stream is never replayed. Errors expose stable codes, retry metadata, serializable `toJSON()` output, and `partialResult` when generation has already produced text, usage, messages, or completed steps.\n\n## Tool And Agent Policies\n\nTools can validate outputs, require approval, and override the default timeout:\n\n```ts\nconst removeFile = tool({\n  name: \"removeFile\",\n  description: \"Remove a file within the configured workspace\",\n  inputSchema: pathSchema,\n  outputSchema: resultSchema,\n  needsApproval: true,\n  timeoutMs: 10_000,\n  async execute(input, { abortSignal, runId, idempotencyKey }) {\n    return removeWorkspaceFile(input, { abortSignal, runId, idempotencyKey });\n  },\n});\n\nconst result = await generateText({\n  model,\n  prompt: \"Remove the obsolete build output\",\n  tools: { removeFile },\n  maxSteps: 5,\n  toolExecution: {\n    mode: \"parallel\",\n    maxConcurrency: 4,\n    errorMode: \"fail-fast\",\n  },\n  requestToolApproval: async (request) =>\n    approvalQueue.waitForDecision(request),\n});\n```\n\nApproval handlers return `approved`, `denied`, `user-approval`, or `not-applicable`. The SDK awaits the decision without replaying prior work. If no handler resolves a required approval, generation fails with `ToolApprovalRequiredError` before the sensitive tool executes.\n\nAgent loops support composable stop conditions, per-step preparation, token and cost budgets, context preparation, and lifecycle callbacks:\n\n```ts\nconst result = await generateText({\n  model,\n  prompt: \"Investigate and summarize\",\n  tools: { search, finalAnswer },\n  maxSteps: 10,\n  stopWhen: [hasToolCall(\"finalAnswer\"), tokenBudgetExceeded(20_000)],\n  prepareStep: ({ stepNumber }) => ({\n    activeTools: stepNumber === 1 ? [\"search\"] : [\"search\", \"finalAnswer\"],\n  }),\n  budget: { tokens: { maxTotalTokens: 20_000 } },\n  contextManager: {\n    prepareMessages: ({ messages }) => pruneForModelContext(messages),\n  },\n  callbacks: {\n    onToolExecutionEnd: ({ toolCall, outcome }) => {\n      auditToolOutcome(toolCall.toolCallId, outcome);\n    },\n  },\n});\n```\n\n`prepareMessages` changes only the provider request view; canonical run history remains intact. Tool execution defaults to bounded parallelism with four workers. Results retain model call order, and completed sibling outputs are included in partial error metadata when another tool fails.\n\n## Middleware And Telemetry\n\nWrap any protocol-v1 model with provider-neutral middleware. The first item is outermost, request defaults never overwrite explicit values, cache keys hash all request settings including headers, and stream middleware remains pull-based:\n\n```ts\nconst productionModel = wrapLanguageModel({\n  model,\n  middleware: [\n    loggingMiddleware({ logger }),\n    defaultSettingsMiddleware({ temperature: 0.2 }),\n    cacheMiddleware({ cache: new MemoryLanguageModelCache(), ttlMs: 60_000 }),\n    retryMiddleware({ maxRetries: 2 }),\n  ],\n});\n```\n\n`loggingMiddleware` excludes prompts and responses by default. Set `recordInputs` or `recordOutputs` only when the application has an appropriate retention policy, and provide `redact` before recording sensitive content. Cache entries contain complete model responses independently of logging and telemetry privacy settings; production applications should provide a tenant-scoped, encrypted `LanguageModelCache` when responses are sensitive.\n\nOpenTelemetry instrumentation is opt-in on generation calls or `AgentSettings`:\n\n```ts\nconst result = await generateText({\n  model: productionModel,\n  prompt: \"Summarize the incident\",\n  telemetry: {\n    enabled: true,\n    recordInputs: false,\n    recordOutputs: false,\n  },\n});\n```\n\nWhen omitted or configured with `enabled: false`, telemetry does not obtain a tracer or meter and adds no stream wrappers. Enabled telemetry records GenAI model spans and token metrics plus run, retry, first-chunk, chunk-interval, tool-duration, and configured cost-budget metrics. Prompts, model outputs, tool data, headers, provider options, and runtime context are not recorded by default. Headers are never recorded.\n\n## Safety And Type Guarantees\n\n- Model and tool boundaries accept `unknown`, never `any`.\n- Tool inputs are validated at runtime before typed execution.\n- Configured tool output schemas are validated before results reach a model.\n- Tool registries are snapshotted, own-property-safe, and reject malformed or duplicate registrations.\n- Sensitive tools support awaited approval decisions and deterministic idempotency keys.\n- Parallel tool execution is bounded and can return failures to the model when configured.\n- Stop conditions and token/cost budgets prevent starting unusable tool or model work.\n- Prompt input is an exclusive union: provide `prompt` or `messages`, never both.\n- Tool loops are bounded with `maxSteps`.\n- Abort signals flow through model and tool calls.\n- Request, first-chunk, per-chunk, and tool timeouts use `TimeoutError`.\n- Public failures use stable errors including `AbortError`, `NetworkError`, `StreamProtocolError`, and `ToolError`.\n- Provider responses and stream events are runtime validated before use.\n- Retries use capped exponential backoff, jitter, transient-failure classification, and `Retry-After` metadata.\n- The source contains no type assertions or unchecked JSON casts.\n\n## Commands\n\n```bash\nbun test\nbun run typecheck\nbun run build\nbun run verify:package\n```\n\nContributor setup and release requirements are documented in\n[`CONTRIBUTING.md`](./CONTRIBUTING.md).\n\n## Provider Contract\n\n`LanguageModel` uses the versioned `v1` provider protocol. The core owns orchestration and runtime boundary validation; adapters own authentication, wire-format validation, provider error normalization, and SSE decoding. Keeping that boundary narrow prevents vendor types and credentials from leaking into agents.\n\nProvider adapters implement `Provider` and create models through `languageModel(modelId, settings?)`. Each provider owns its API key, endpoint, request validation, and typed model settings. The core intentionally never reads `OPENAI_API_KEY` or `ANTHROPIC_API_KEY`.\n\n```ts\nimport { createAnthropic } from \"@deepaksankhyan91/open-agent-sdk/anthropic\";\n\nconst apiKey = Bun.env.ANTHROPIC_API_KEY;\nif (!apiKey) throw new Error(\"ANTHROPIC_API_KEY is required\");\n\nconst model = createAnthropic({ apiKey }).languageModel(\"your-model-id\");\n```\n\n## Project Structure\n\n```text\nsrc/\n├── index.ts                    # Stable public package entry point\n├── core/\n│   ├── index.ts                # Core export boundary\n│   ├── agent/agent.ts          # Reusable Agent facade\n│   ├── errors/errors.ts        # Stable SDK and tool errors\n│   ├── generation/\n│   │   ├── generate-text.ts    # Generation and tool loop\n│   │   └── stream-text.ts      # Provider-native streaming\n│   ├── middleware/middleware.ts # Model middleware and built-ins\n│   ├── model/types.ts          # Provider protocol and messages\n│   ├── provider/provider.ts    # Provider factory contract\n│   ├── telemetry/telemetry.ts   # OpenTelemetry GenAI instrumentation\n│   └── tools/tool.ts           # Schemas and typed tools\n└── providers/\n    ├── openai/                 # OpenAI Responses API adapter\n    └── anthropic/              # Anthropic Messages API adapter\ntest/\n├── provider-contract.ts        # Shared adapter contract suite\n└── phase2-providers.test.ts    # Provider wire fixtures\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-77de76e23a224e6be327533c8c188997"}