@getunblocked/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +211 -0
  3. package/dist/agents.d.ts +301 -0
  4. package/dist/agents.d.ts.map +1 -0
  5. package/dist/agents.js +262 -0
  6. package/dist/agents.js.map +1 -0
  7. package/dist/client.d.ts +39 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +76 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/errors.d.ts +70 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +96 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/http.d.ts +16 -0
  16. package/dist/http.d.ts.map +1 -0
  17. package/dist/http.js +222 -0
  18. package/dist/http.js.map +1 -0
  19. package/dist/index.d.ts +10 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +7 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/timing.d.ts +9 -0
  24. package/dist/timing.d.ts.map +1 -0
  25. package/dist/timing.js +86 -0
  26. package/dist/timing.js.map +1 -0
  27. package/dist/types.d.ts +273 -0
  28. package/dist/types.d.ts.map +1 -0
  29. package/dist/types.js +2 -0
  30. package/dist/types.js.map +1 -0
  31. package/dist/version.d.ts +14 -0
  32. package/dist/version.d.ts.map +1 -0
  33. package/dist/version.js +22 -0
  34. package/dist/version.js.map +1 -0
  35. package/docs/README.md +28 -0
  36. package/docs/agent-frameworks.md +84 -0
  37. package/docs/api-design.md +53 -0
  38. package/docs/authentication.md +71 -0
  39. package/docs/configuration.md +67 -0
  40. package/docs/context.md +131 -0
  41. package/docs/contributing.md +51 -0
  42. package/docs/errors-and-retries.md +135 -0
  43. package/docs/getting-started.md +62 -0
  44. package/docs/low-level-requests.md +72 -0
  45. package/docs/pagination.md +29 -0
  46. package/docs/release.md +73 -0
  47. package/docs/runtime-and-packaging.md +57 -0
  48. package/docs/typescript.md +102 -0
  49. package/examples/context-research-tool.ts +10 -0
  50. package/package.json +51 -0
@@ -0,0 +1,14 @@
1
+ /**
2
+ * SDK version. Keep in sync with the `version` field in package.json when
3
+ * cutting a release; it is sent on the User-Agent header for telemetry.
4
+ */
5
+ export declare const VERSION = "0.1.0";
6
+ /**
7
+ * Builds the User-Agent value sent on every request, e.g.
8
+ * `@getunblocked/sdk/0.1.0 node/20.11.0`. The runtime suffix is best-effort.
9
+ *
10
+ * Browsers treat User-Agent as a forbidden header name and silently drop it,
11
+ * which is harmless: the backend only logs the value for debugging.
12
+ */
13
+ export declare function getUserAgent(): string;
14
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B;;;;;;GAMG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAQrC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * SDK version. Keep in sync with the `version` field in package.json when
3
+ * cutting a release; it is sent on the User-Agent header for telemetry.
4
+ */
5
+ export const VERSION = "0.1.0";
6
+ /**
7
+ * Builds the User-Agent value sent on every request, e.g.
8
+ * `@getunblocked/sdk/0.1.0 node/20.11.0`. The runtime suffix is best-effort.
9
+ *
10
+ * Browsers treat User-Agent as a forbidden header name and silently drop it,
11
+ * which is harmless: the backend only logs the value for debugging.
12
+ */
13
+ export function getUserAgent() {
14
+ const parts = [`@getunblocked/sdk/${VERSION}`];
15
+ const nodeVersion = globalThis.process?.versions
16
+ ?.node;
17
+ if (nodeVersion) {
18
+ parts.push(`node/${nodeVersion}`);
19
+ }
20
+ return parts.join(" ");
21
+ }
22
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B;;;;;;GAMG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,KAAK,GAAG,CAAC,qBAAqB,OAAO,EAAE,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAI,UAA6D,CAAC,OAAO,EAAE,QAAQ;QAClG,EAAE,IAAI,CAAC;IACT,IAAI,WAAW,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,QAAQ,WAAW,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC"}
package/docs/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Documentation
2
+
3
+ This directory contains the SDK documentation for people installing, integrating, and maintaining `@getunblocked/sdk`.
4
+
5
+ ## Start Here
6
+
7
+ - [Getting Started](./getting-started.md) - install the package and make the first API calls.
8
+ - [Authentication](./authentication.md) - use Personal Access Tokens, Team Access Tokens, environment variables, and token providers.
9
+ - [Configuration](./configuration.md) - configure base URLs, fetch, headers, timeouts, and retries.
10
+
11
+ ## API Guides
12
+
13
+ - [Context](./context.md) - research, search, query, and fetch context from the connected engine.
14
+ - [Pagination](./pagination.md) - parse Link headers manually when using low-level list requests.
15
+ - [Low-Level Requests](./low-level-requests.md) - call API paths that do not yet have first-class helpers.
16
+
17
+ ## Integration Guides
18
+
19
+ - [Agent Frameworks](./agent-frameworks.md) - adapt the SDK to TypeScript agent tool systems.
20
+ - [Runtime and Packaging](./runtime-and-packaging.md) - supported runtimes and package publishing behavior.
21
+ - [TypeScript Guide](./typescript.md) - exported types and recommended typing patterns.
22
+
23
+ ## Operations
24
+
25
+ - [Errors and Retries](./errors-and-retries.md) - handle API errors, timeouts, and retry behavior.
26
+ - [Contributing](./contributing.md) - local development workflow for this SDK.
27
+ - [Release Guide](./release.md) - versioning and publishing checklist.
28
+ - [API Design Notes](./api-design.md) - rationale behind the public interface.
@@ -0,0 +1,84 @@
1
+ # Agent Frameworks
2
+
3
+ The SDK is designed to be adapted into TypeScript-first agent frameworks without taking a runtime dependency on those frameworks.
4
+
5
+ ## Built-In Tool Helpers
6
+
7
+ ```ts
8
+ import {
9
+ createUnblockedTools,
10
+ unblockedToolSchemas,
11
+ } from "@getunblocked/sdk";
12
+
13
+ const tools = createUnblockedTools({
14
+ apiKey: process.env.UNBLOCKED_API_TOKEN,
15
+ });
16
+
17
+ export const research = {
18
+ ...unblockedToolSchemas.research,
19
+ execute: tools.research,
20
+ };
21
+ ```
22
+
23
+ The same pattern works for frameworks that expect a schema plus an `execute` function. If a framework uses a different naming convention, wrap the `tools.research` function in that framework's tool registration API.
24
+
25
+ ## Available Tool Functions
26
+
27
+ `createUnblockedTools` returns one function per context method, and `unblockedToolSchemas` exposes a matching JSON-schema descriptor for each:
28
+
29
+ | Function | Purpose |
30
+ | --- | --- |
31
+ | `research` | Unified multi-source research that synthesizes an answer with references. |
32
+ | `searchCode` | Search the connected codebase. |
33
+ | `searchPrs` | Search pull requests. |
34
+ | `searchIssues` | Search issues. |
35
+ | `searchDocumentation` | Search connected documentation. |
36
+ | `searchMessages` | Search connected messages and conversations. |
37
+ | `queryIssues` | Query issues scoped by projects and/or user. |
38
+ | `queryPrs` | Query pull requests scoped by projects and/or user. |
39
+ | `getUrls` | Fetch and summarize the content of specific URLs. |
40
+ | `getRules` | Get coding rules and conventions for a repository. |
41
+
42
+ `research` resolves to a `ContextResearchResponse` (`summary`, `sources`, `effort`). Search, query, and get functions resolve to responses carrying a `sources` array.
43
+
44
+ ```ts
45
+ const research = await tools.research({
46
+ query: "Where is rate limiting implemented?",
47
+ effort: "high",
48
+ });
49
+
50
+ console.log(research.summary);
51
+ for (const source of research.sources) {
52
+ console.log(source.content, source.url);
53
+ }
54
+
55
+ const code = await tools.searchCode({ query: "rate limiter middleware" });
56
+ console.log(code.sources.map((source) => source.content));
57
+ ```
58
+
59
+ ## Custom Tool Names
60
+
61
+ Frameworks often prefer short action names. Keep the SDK call hidden inside a small adapter.
62
+
63
+ ```ts
64
+ export const searchCodebaseKnowledge = {
65
+ description: "Ask Unblocked about the connected codebase.",
66
+ parameters: unblockedToolSchemas.research.parameters,
67
+ execute: tools.research,
68
+ };
69
+ ```
70
+
71
+ ## Recommended Agent Instructions
72
+
73
+ When exposing `research` to an agent, give the agent guidance about when to call it:
74
+
75
+ ```md
76
+ Use research when you need repository, docs, issue, pull request, or team-context information that is not present in the current prompt or local files.
77
+ Prefer specific questions. Include relevant file paths, symbol names, error messages, or URLs when available.
78
+ ```
79
+
80
+ ## Example Module
81
+
82
+ See [`../examples/context-research-tool.ts`](../examples/context-research-tool.ts).
83
+
84
+ For a complete local Flue demo that shows implementation preflight and bug triage with organizational memory, see [`../examples/flue-bug-triage`](../examples/flue-bug-triage/README.md). For a deployable Eve GitHub-App-webhook agent that makes Unblocked context a required PR-triage step, see [`../examples/eve-pr-triage`](../examples/eve-pr-triage/README.md).
@@ -0,0 +1,53 @@
1
+ # API Design Notes
2
+
3
+ The SDK intentionally exposes two layers:
4
+
5
+ 1. Resource clients for normal application and agent-framework use:
6
+ `unblocked.context` and its nested `search`, `query`, and `get` groups.
7
+ 2. A low-level `unblocked.request()` escape hatch for early adopters who need a
8
+ new endpoint before a first-class wrapper exists.
9
+
10
+ ## Principles
11
+
12
+ - Keep the primary client small and branded. `new Unblocked()` is the recommended
13
+ entry point, while `UnblockedClient` remains available for callers who prefer a
14
+ descriptive class name.
15
+ - Model the public API's resources directly, but hide fetch details, bearer-token
16
+ headers, retry timing, response parsing, and link-header parsing.
17
+ - Prefer object parameters for request bodies and option bags so future fields can
18
+ be added without positional-argument churn.
19
+ - Make invalid calls harder to write. Context inputs require the fields the API
20
+ needs, such as `query` on research and search or `urls` on `get.urls`.
21
+ - Keep retries conservative. Only replay idempotent methods by default.
22
+ - Keep package boundaries explicit through `exports`, generated declarations, and
23
+ a narrow `files` allowlist.
24
+ - Avoid runtime dependencies so the package works in Node, edge runtimes, and
25
+ TypeScript agent harnesses.
26
+
27
+ ## Chosen Surface
28
+
29
+ ```ts
30
+ import Unblocked from "@getunblocked/sdk";
31
+
32
+ const unblocked = new Unblocked({
33
+ apiKey: process.env.UNBLOCKED_API_TOKEN,
34
+ timeoutMs: 30_000,
35
+ maxRetries: 2,
36
+ });
37
+
38
+ const code = await unblocked.context.search.code({
39
+ query: "rate limiting",
40
+ });
41
+
42
+ const research = await unblocked.context.research({
43
+ query: "How does auth work?",
44
+ });
45
+ ```
46
+
47
+ ## Considered Alternatives
48
+
49
+ The design pass considered a single `invoke("tool_name", input)` interface, a
50
+ pure OpenAPI-generated REST client, and framework-specific adapters for Flue or
51
+ Eve. The selected surface keeps the common path readable, avoids coupling to a
52
+ framework that may change quickly, and still leaves an escape hatch for advanced
53
+ callers.
@@ -0,0 +1,71 @@
1
+ # Authentication
2
+
3
+ The SDK uses the same authentication model as the Unblocked Public API. Each request includes:
4
+
5
+ ```http
6
+ Authorization: Bearer <token>
7
+ ```
8
+
9
+ ## Token Types
10
+
11
+ Unblocked supports two API token styles:
12
+
13
+ - Personal Access Tokens for user-scoped automation, testing, and development workflows.
14
+ - Team Access Tokens for organization-wide automation.
15
+
16
+ Use the narrowest token that can perform the job. For agent workflows, prefer data-source-scoped tokens when possible.
17
+
18
+ ## Passing a Token
19
+
20
+ ```ts
21
+ import Unblocked from "@getunblocked/sdk";
22
+
23
+ const unblocked = new Unblocked({
24
+ apiKey: "unb_...",
25
+ });
26
+ ```
27
+
28
+ `token` is an alias for `apiKey`:
29
+
30
+ ```ts
31
+ const unblocked = new Unblocked({
32
+ token: "unb_...",
33
+ });
34
+ ```
35
+
36
+ ## Environment Variables
37
+
38
+ If no token is passed, the client reads environment variables in this order:
39
+
40
+ 1. `UNBLOCKED_API_TOKEN`
41
+ 2. `UNBLOCKED_API_KEY`
42
+
43
+ ```bash
44
+ export UNBLOCKED_API_TOKEN="unb_..."
45
+ ```
46
+
47
+ ```ts
48
+ const unblocked = new Unblocked();
49
+ ```
50
+
51
+ ## Async Token Providers
52
+
53
+ Use a token provider when the token comes from a secrets manager or short-lived credential service.
54
+
55
+ ```ts
56
+ const unblocked = new Unblocked({
57
+ apiKey: async () => {
58
+ return await getTokenFromSecretManager();
59
+ },
60
+ });
61
+ ```
62
+
63
+ The provider is called before each request, so it can refresh or rotate credentials.
64
+
65
+ ## Security Notes
66
+
67
+ - Do not commit tokens to source control.
68
+ - Do not expose team tokens in browser code.
69
+ - Prefer server-side or agent-runtime usage for SDK calls.
70
+ - Rotate tokens when they are no longer needed.
71
+ - Scope tokens to the minimum data sources required by the workflow.
@@ -0,0 +1,67 @@
1
+ # Configuration
2
+
3
+ Create a client with `UnblockedClientOptions`.
4
+
5
+ ```ts
6
+ import Unblocked from "@getunblocked/sdk";
7
+
8
+ const unblocked = new Unblocked({
9
+ apiKey: process.env.UNBLOCKED_API_TOKEN,
10
+ baseUrl: "https://getunblocked.com/api",
11
+ timeoutMs: 30_000,
12
+ maxRetries: 2,
13
+ retryDelayMs: 500,
14
+ headers: {
15
+ "User-Agent": "my-agent/1.0",
16
+ },
17
+ fetch: globalThis.fetch,
18
+ });
19
+ ```
20
+
21
+ ## Options
22
+
23
+ `apiKey`:
24
+ API token string or async token provider. If omitted, the SDK reads `UNBLOCKED_API_TOKEN` and then `UNBLOCKED_API_KEY`.
25
+
26
+ `token`:
27
+ Alias for `apiKey`.
28
+
29
+ `baseUrl`:
30
+ Base URL for the public API. Defaults to `https://getunblocked.com/api`.
31
+
32
+ `fetch`:
33
+ Fetch implementation. Defaults to `globalThis.fetch`.
34
+
35
+ `headers`:
36
+ Headers included on every request. The SDK still controls `Authorization`, `Accept`, and `Content-Type` for JSON requests. The SDK also sets a default `User-Agent` (e.g. `@getunblocked/sdk/0.1.0 node/20.x`) that you can override here. Browsers ignore custom `User-Agent` values.
37
+
38
+ `timeoutMs`:
39
+ Maximum time for each HTTP request before the request is aborted. Defaults to `60000` (60 seconds).
40
+
41
+ `maxRetries`:
42
+ Maximum number of retry attempts for retriable requests. Defaults to `2`.
43
+
44
+ `retryDelayMs`:
45
+ Base delay used for exponential backoff. Defaults to `500`.
46
+
47
+ `dangerouslyAllowBrowser`:
48
+ By default the client throws if constructed in a browser-like environment, to keep your API token out of client-side bundles. Set to `true` only if you understand the risk (e.g. you proxy the API or use a restricted token).
49
+
50
+ ## Per-Request Options
51
+
52
+ Most methods accept request options as the final argument.
53
+
54
+ ```ts
55
+ await unblocked.context.research(
56
+ {
57
+ query: "How does auth work?",
58
+ },
59
+ {
60
+ timeoutMs: 10_000,
61
+ maxRetries: 1,
62
+ signal: abortController.signal,
63
+ },
64
+ );
65
+ ```
66
+
67
+ Per-request options override client-level options for that call.
@@ -0,0 +1,131 @@
1
+ # Context
2
+
3
+ The `client.context` resource is the SDK's interface to Unblocked's context engine. Every context method sends a `POST` to a `/tools/context/*` endpoint (under the `https://getunblocked.com/api` base URL) and resolves to a response carrying `sources`.
4
+
5
+ ## Response Shape
6
+
7
+ Search, query, and get methods return a response with a `sources` array. `research` additionally returns a `summary` and the `effort` that was used.
8
+
9
+ ```ts
10
+ interface ContextSearchSource {
11
+ content: string;
12
+ title?: string;
13
+ url?: string;
14
+ sourceType?: "code" | "pull_request" | "issue" | "messages" | "documentation" | "rules";
15
+ provider?: string;
16
+ }
17
+
18
+ interface ContextSearchResponse {
19
+ toolCallId?: string;
20
+ sources: ContextSearchSource[];
21
+ }
22
+
23
+ interface ContextResearchResponse {
24
+ toolCallId?: string;
25
+ summary: string;
26
+ sources: ContextSearchSource[];
27
+ effort: "low" | "medium" | "high";
28
+ }
29
+ ```
30
+
31
+ `ContextQueryResponse` and `ContextGetResponse` have the same shape as `ContextSearchResponse` (`{ toolCallId?, sources }`).
32
+
33
+ ```ts
34
+ const result = await unblocked.context.research({
35
+ query: "How does the user authentication system work?",
36
+ });
37
+
38
+ console.log(result.summary);
39
+ for (const source of result.sources) {
40
+ console.log(source.content);
41
+ if (source.url) {
42
+ console.log(source.url);
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Common Request Fields
48
+
49
+ Every request accepts optional `toolCallId` and `sessionId` strings that correlate a call with its originating tool call and group related context calls. They are always optional.
50
+
51
+ ## Effort Levels
52
+
53
+ Only `research` accepts an optional `effort` that trades breadth for latency:
54
+
55
+ - `low` - quick lookup, fastest and narrowest.
56
+ - `medium` - normal, balanced depth (the server default when `effort` is omitted).
57
+ - `high` - broad cross-system research, slowest and most thorough.
58
+
59
+ ```ts
60
+ import type { ContextSearchEffort } from "@getunblocked/sdk";
61
+
62
+ const effort: ContextSearchEffort = "high";
63
+ ```
64
+
65
+ ## Research
66
+
67
+ `client.context.research(request, options?)` runs unified multi-source research and synthesizes an answer with references.
68
+
69
+ ```ts
70
+ await unblocked.context.research({
71
+ query: "How does the user authentication system work?",
72
+ instruction: "Focus on token refresh.",
73
+ effort: "high",
74
+ });
75
+ ```
76
+
77
+ Request fields: `query` (required), `instruction` (optional), `effort` (optional), plus optional `toolCallId` and `sessionId`. Returns `ContextResearchResponse` (`summary`, `sources`, `effort`).
78
+
79
+ ## Search
80
+
81
+ `client.context.search.*` searches a single source. Each method takes `{ query, instruction? }` (plus optional `toolCallId`/`sessionId`) and returns `ContextSearchResponse`.
82
+
83
+ ```ts
84
+ await unblocked.context.search.code({ query: "rate limiting" });
85
+ await unblocked.context.search.prs({ query: "auth refactor" });
86
+ await unblocked.context.search.issues({ query: "logout after workspace switch" });
87
+ await unblocked.context.search.documentation({ query: "session lifecycle" });
88
+ await unblocked.context.search.messages({ query: "incident 2024-05" });
89
+ ```
90
+
91
+ | Method | Endpoint |
92
+ | --- | --- |
93
+ | `search.code` | `/tools/context/search/code` |
94
+ | `search.prs` | `/tools/context/search/prs` |
95
+ | `search.documentation` | `/tools/context/search/documentation` |
96
+ | `search.issues` | `/tools/context/search/issues` |
97
+ | `search.messages` | `/tools/context/search/messages` |
98
+
99
+ ## Query
100
+
101
+ `client.context.query.*` queries issues or pull requests, optionally scoped by `projects` and/or `user_name`. Each method takes `{ query, projects?, user_name? }` (plus optional `toolCallId`/`sessionId`) and returns `ContextQueryResponse`.
102
+
103
+ ```ts
104
+ await unblocked.context.query.issues({ query: "open bugs", projects: ["web"], user_name: "alice" });
105
+ await unblocked.context.query.prs({ query: "recent auth changes", projects: ["api"] });
106
+ ```
107
+
108
+ ## Get
109
+
110
+ `client.context.get.*` fetches specific resources.
111
+
112
+ ```ts
113
+ await unblocked.context.get.urls({ urls: ["https://example.com/docs"] });
114
+ await unblocked.context.get.rules({ repoName: "acme/web", task: "add endpoint", language: "typescript", paths: ["src/auth"] });
115
+ ```
116
+
117
+ - `get.urls` takes `{ urls }` (required) plus optional `toolCallId`/`sessionId`, and summarizes the content of specific URLs. Returns `ContextGetResponse`.
118
+ - `get.rules` takes `{ repoName }` (required) plus optional `ruleId`, `task`, `language`, `paths`, `toolCallId`, and `sessionId`, and returns the coding rules and conventions for a repository. Returns `ContextGetResponse`.
119
+
120
+ ## Request Options
121
+
122
+ Every method accepts an optional second `RequestOptions` argument for per-call `headers`, `signal`, `timeoutMs`, `maxRetries`, and `retryDelayMs`; because context methods use `POST`, retry options are accepted but do not trigger retries.
123
+
124
+ ```ts
125
+ await unblocked.context.research(
126
+ { query: "How does auth work?" },
127
+ { timeoutMs: 90_000 },
128
+ );
129
+ ```
130
+
131
+ Context methods use `POST` and are never retried. See [Errors and Retries](./errors-and-retries.md).
@@ -0,0 +1,51 @@
1
+ # Contributing
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ npm install
7
+ ```
8
+
9
+ ## Build
10
+
11
+ ```bash
12
+ npm run build
13
+ ```
14
+
15
+ ## Test
16
+
17
+ ```bash
18
+ npm test
19
+ ```
20
+
21
+ The test command builds the package and runs Node's built-in test runner against the emitted `dist` files.
22
+
23
+ ## Package Dry Run
24
+
25
+ ```bash
26
+ npm pack --dry-run
27
+ ```
28
+
29
+ If your local npm cache has permission issues, use a temporary cache:
30
+
31
+ ```bash
32
+ npm --cache /private/tmp/unblocked-typescript-sdk-npm-cache pack --dry-run
33
+ ```
34
+
35
+ ## Development Guidelines
36
+
37
+ - Keep runtime dependencies at zero unless there is a strong reason to add one.
38
+ - Prefer resource methods for stable public API endpoints.
39
+ - Use `request` and `requestRaw` as escape hatches, not as the primary documented path.
40
+ - Add or update tests for request construction, auth headers, retries, errors, and type exports.
41
+ - Keep docs in `docs/` updated when changing public behavior.
42
+ - Do not commit generated package tarballs.
43
+
44
+ ## Source Layout
45
+
46
+ - `src/client.ts` - public resource clients.
47
+ - `src/http.ts` - fetch transport, retries, timeouts, and pagination parsing.
48
+ - `src/types.ts` - exported public types.
49
+ - `src/errors.ts` - exported error classes.
50
+ - `src/agents.ts` - framework-neutral agent tool helpers.
51
+ - `tests/` - tests against built `dist` output.
@@ -0,0 +1,135 @@
1
+ # Errors and Retries
2
+
3
+ ## API Errors
4
+
5
+ Non-2xx responses throw `UnblockedApiError`.
6
+
7
+ ```ts
8
+ import { UnblockedApiError } from "@getunblocked/sdk";
9
+
10
+ try {
11
+ await unblocked.context.research({ query: "How does auth work?" });
12
+ } catch (error) {
13
+ if (error instanceof UnblockedApiError) {
14
+ console.error(error.status);
15
+ console.error(error.statusText);
16
+ console.error(error.body);
17
+ console.error(error.requestId); // X-Request-ID, useful for support
18
+ }
19
+ }
20
+ ```
21
+
22
+ `UnblockedApiError` includes:
23
+
24
+ - `status`
25
+ - `statusText`
26
+ - `url`
27
+ - `method`
28
+ - `body` — the parsed `ApiError` body (`status`, optional `title`, `detail`, `url`, `urlLabel`, `cause`)
29
+ - `headers`
30
+ - `requestId` — the `X-Request-ID` response header, when present
31
+
32
+ The error `message` includes the `title`/`detail` from the response body when the API provides them.
33
+
34
+ ## Error Subclasses
35
+
36
+ The thrown error is a status-specific subclass of `UnblockedApiError`, so you can match on the case you care about:
37
+
38
+ | Status | Class |
39
+ | --- | --- |
40
+ | 400 | `UnblockedBadRequestError` |
41
+ | 401 | `UnblockedAuthenticationError` |
42
+ | 403 | `UnblockedPermissionDeniedError` |
43
+ | 404 | `UnblockedNotFoundError` |
44
+ | 429 | `UnblockedRateLimitError` |
45
+ | 5xx | `UnblockedServerError` |
46
+
47
+ Any other non-2xx status throws the base `UnblockedApiError`.
48
+
49
+ ```ts
50
+ import { UnblockedRateLimitError } from "@getunblocked/sdk";
51
+
52
+ try {
53
+ await unblocked.context.research({ query: "How does auth work?" });
54
+ } catch (error) {
55
+ if (error instanceof UnblockedRateLimitError) {
56
+ // Rate limit exceeded — back off and retry later.
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Timeouts
62
+
63
+ HTTP request timeouts throw `UnblockedTimeoutError`.
64
+
65
+ ```ts
66
+ import { UnblockedTimeoutError } from "@getunblocked/sdk";
67
+
68
+ try {
69
+ await unblocked.context.research(
70
+ { query: "How does auth work?" },
71
+ { timeoutMs: 30_000 },
72
+ );
73
+ } catch (error) {
74
+ if (error instanceof UnblockedTimeoutError) {
75
+ console.error(error.message);
76
+ }
77
+ }
78
+ ```
79
+
80
+ ## Retry Behavior
81
+
82
+ The SDK retries transient network failures and these response statuses:
83
+
84
+ - `408`
85
+ - `429`
86
+ - `500`
87
+ - `502`
88
+ - `503`
89
+ - `504`
90
+
91
+ Only `GET`, `PUT`, and `DELETE` are retried by default. `POST` and `PATCH` are never retried because they are not generally safe to replay. All `client.context.*` methods use `POST`, so `maxRetries` has no effect on context calls.
92
+
93
+ The default retry settings are:
94
+
95
+ ```ts
96
+ {
97
+ maxRetries: 2,
98
+ retryDelayMs: 500,
99
+ }
100
+ ```
101
+
102
+ `Retry-After` response headers are respected when present.
103
+
104
+ ## Override Retries
105
+
106
+ ```ts
107
+ const unblocked = new Unblocked({
108
+ apiKey: process.env.UNBLOCKED_API_TOKEN,
109
+ maxRetries: 0,
110
+ });
111
+ ```
112
+
113
+ Per request:
114
+
115
+ ```ts
116
+ await unblocked.request("GET", "/some/idempotent/path", {
117
+ maxRetries: 1,
118
+ retryDelayMs: 250,
119
+ });
120
+ ```
121
+
122
+ Context methods use `POST` and are never retried. Passing `maxRetries` to `client.context.*` only records an option the retry loop will not use; retry options affect idempotent low-level `GET`, `PUT`, and `DELETE` requests through `request`/`requestRaw`.
123
+
124
+ ## Abort Signals
125
+
126
+ ```ts
127
+ const controller = new AbortController();
128
+
129
+ setTimeout(() => controller.abort(), 5_000);
130
+
131
+ await unblocked.context.research(
132
+ { query: "How does auth work?" },
133
+ { signal: controller.signal },
134
+ );
135
+ ```