codai-sdk 0.2.0 → 0.3.1

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.
package/README.md CHANGED
@@ -5,12 +5,14 @@
5
5
 
6
6
  Official TypeScript SDK for the [codai](https://codai.ro) AI gateway — a single
7
7
  OpenAI-compatible endpoint with smart routing, sessions, server-side agents,
8
- streaming, embeddings, audio, and feedback.
8
+ streaming, embeddings, audio, outcome-billed tasks, shared sessions, devices,
9
+ hosts, orgs and account — **full parity with the public gateway OpenAPI**.
9
10
 
10
11
  - **Zero dependencies** — uses the platform `fetch`.
11
12
  - Works in **Node 18+** and modern edge runtimes.
12
13
  - **OpenAI-compatible** chat surface with codai extensions.
13
- - Fully typed.
14
+ - Fully typed — request/response types are **generated from the OpenAPI spec**.
15
+ - Every `operationId` of the gateway has exactly one method (parity-tested).
14
16
 
15
17
  ```bash
16
18
  npm install codai-sdk
@@ -35,6 +37,38 @@ console.log(res.content);
35
37
  console.log(res.routedTo); // which upstream model actually served
36
38
  ```
37
39
 
40
+ ## Resource groups
41
+
42
+ Everything hangs off the `Codai` client. The 0.2.x top-level methods (`chat()`,
43
+ `chatStream()`, `embeddings()`, `models()`, `feedback()`, `mintToken()`,
44
+ `agents.run()`, `audio.*`) still work; the resource groups below are the full
45
+ surface.
46
+
47
+ | Group | Methods | Gateway paths |
48
+ | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
49
+ | `chat.completions` | `create`, `stream` | `POST /v1/chat/completions` |
50
+ | `messages` | `create`, `stream` | `POST /v1/messages` (Anthropic wire) |
51
+ | `responses` | `create`, `stream` | `POST /v1/responses` (OpenAI Responses wire) |
52
+ | `embeddings` | `create` | `POST /v1/embeddings` |
53
+ | `audio` | `transcribe`, `transcribeDetailed`, `speech`, `speechDetailed` | `POST /v1/audio/transcriptions`, `POST /v1/audio/speech` |
54
+ | `tokens` | `create` | `POST /v1/tokens` |
55
+ | `models` | `list` | `GET /v1/models` |
56
+ | `health` | `get`, `ready`, `status` | `GET /health`, `GET /health/ready`, `GET /status` |
57
+ | `agents` | `run`; `runs.create`, `runs.get`, `runs.steps`, `runs.stats`, `runs.cancel`, `runs.stream` | `/v1/agents/run`, `/v1/agents/runs/*` |
58
+ | `tools` | `search`, `fetch` | `POST /v1/tools/search`, `POST /v1/tools/fetch` |
59
+ | `tasks` | `list`, `pending`, `stats`, `get`, `confirm` | `/v1/tasks/*` |
60
+ | `sessions` | `create`, `list`, `get`, `update`, `delete`, `dispatch`, `stream`; `events.list/append`; `controls.list/submit/markApplied`; `lease.acquire/renew/release`; `shares.list/create/delete/sharedWithMe` | `/v1/sessions/*` |
61
+ | `devices` | `list`, `update`, `delete`, `dispatchInbox` | `/v1/devices/*` |
62
+ | `hosts` | `list`, `exec`, `postResult`, `stream` | `/v1/hosts/*` |
63
+ | `orgs` | `create`, `list`; `members.list/add/remove` | `/v1/orgs/*` |
64
+ | `account` | `get`, `update` | `GET/PATCH /v1/account` |
65
+ | `receipt` | `get` | `GET /v1/receipt` |
66
+ | `feedback` | `submit` | `POST /v1/feedback` |
67
+ | `phoneModels` | `list` | `GET /v1/phone/models` |
68
+
69
+ Every method takes an optional last argument `{ ext, signal }` where `ext` is a
70
+ typed bag of `X-Codai-*` extension headers (see below).
71
+
38
72
  ## Streaming
39
73
 
40
74
  ```ts
@@ -62,6 +96,30 @@ const { requestId, usage, routedTo, toolCalls } = await stream.final;
62
96
  if (requestId) await codai.feedback(requestId, 1);
63
97
  ```
64
98
 
99
+ `chat.completions.stream()` is the same stream; `stream.chunks()` yields the raw
100
+ `chat.completion.chunk` objects.
101
+
102
+ ## Anthropic Messages and OpenAI Responses wires
103
+
104
+ ```ts
105
+ // Anthropic Messages shape — text blocks, tool_use, streaming events
106
+ const msg = await codai.messages.create({
107
+ messages: [{ role: 'user', content: 'Salut!' }],
108
+ max_tokens: 256,
109
+ });
110
+ msg.text; // concatenated text blocks
111
+
112
+ for await (const ev of codai.messages.stream({ messages, max_tokens: 256 })) {
113
+ if (ev.type === 'content_block_delta') {
114
+ /* ev.delta */
115
+ }
116
+ }
117
+
118
+ // OpenAI Responses shape
119
+ const r = await codai.responses.create({ input: 'Say hi.' });
120
+ r.outputText;
121
+ ```
122
+
65
123
  ## Server-side agent
66
124
 
67
125
  Run a plan-and-execute loop on the gateway — the heavy lifting (planning, tool
@@ -76,6 +134,75 @@ const run = await codai.agents.run({
76
134
  console.log(run.result);
77
135
  ```
78
136
 
137
+ Async runs are persisted and can be polled, streamed and cancelled:
138
+
139
+ ```ts
140
+ const { id } = await codai.agents.runs.create({ task: '…', client_request_id: 'ci-8812' });
141
+ for await (const ev of codai.agents.runs.stream(id)) {
142
+ if (ev.event === 'step') console.log(ev.data.kind, ev.data.summary);
143
+ if (ev.event === 'done') console.log(ev.data.status, ev.data.result);
144
+ }
145
+ const run = await codai.agents.runs.get(id);
146
+ ```
147
+
148
+ ## Tools, tasks and receipts
149
+
150
+ ```ts
151
+ const hits = await codai.tools.search({ query: 'BNR EUR RON' });
152
+ const page = await codai.tools.fetch({ url: 'https://bnr.ro', format: 'markdown' });
153
+
154
+ const { tasks, next_cursor } = await codai.tasks.list({ outcome: 'unconfirmed', limit: 20 });
155
+ await codai.tasks.confirm(tasks[0].id, { outcome: 'confirmed' });
156
+
157
+ const receipt = await codai.receipt.get({ sessionId: 'my-project' });
158
+ receipt.cost_usd;
159
+ ```
160
+
161
+ ## Shared sessions, devices and hosts
162
+
163
+ Shared-sessions and hosts calls need a device UUID (`x-codai-device`). Set it once
164
+ on the client:
165
+
166
+ ```ts
167
+ const codai = new Codai({
168
+ apiKey,
169
+ device: '6f1c2b3a-4d5e-4f60-8a9b-0c1d2e3f4a5b',
170
+ deviceName: 'ci-runner',
171
+ devicePlatform: 'cli',
172
+ });
173
+
174
+ const session = await codai.sessions.create({ session_key: 'desktop-main', title: 'Refactor' });
175
+ await codai.sessions.lease.acquire(session.id);
176
+ await codai.sessions.events.append(session.id, {
177
+ events: [{ kind: 'user', payload: { text: 'hello' }, client_event_id: 'e1' }],
178
+ });
179
+
180
+ for await (const frame of codai.sessions.stream(session.id, { after: 0 })) {
181
+ // frame.event ∈ 'event' | 'control' | 'lease' | 'presence'
182
+ }
183
+
184
+ await codai.sessions.dispatch(session.id, { device_id: phoneId, text: 'continue on the phone' });
185
+
186
+ // Host relay: run one op on a connected desktop
187
+ const hosts = await codai.hosts.list();
188
+ const out = await codai.hosts.exec(hosts[0].device_id, 'shell', { cmd: 'git status' });
189
+ ```
190
+
191
+ ## Orgs and account
192
+
193
+ ```ts
194
+ const org = await codai.orgs.create('Acme Robotics');
195
+ await codai.orgs.members.add(org.id, { email: 'ana@example.com', role: 'admin' });
196
+ await codai.sessions.shares.create(session.id, {
197
+ principal_type: 'org',
198
+ principal_id: org.id,
199
+ role: 'editor',
200
+ });
201
+
202
+ const me = await codai.account.get();
203
+ await codai.account.update({ training_opt_out: true });
204
+ ```
205
+
79
206
  ## Feedback
80
207
 
81
208
  ```ts
@@ -114,6 +241,9 @@ const codai = new Codai({
114
241
  apiKey: process.env.CODAI_API_KEY!,
115
242
  baseUrl: 'https://ai.codai.ro', // default
116
243
  sessionId: 'my-project', // enables session memory + stickiness
244
+ device: '<uuid>', // shared sessions / hosts (x-codai-device)
245
+ client: 'my-app/1.2.0', // x-codai-client surface tag
246
+ defaults: { effort: 'medium' }, // any other X-Codai-* defaults
117
247
  timeoutMs: 120_000,
118
248
  maxRetries: 2,
119
249
  });
@@ -121,7 +251,8 @@ const codai = new Codai({
121
251
 
122
252
  ## codai extensions
123
253
 
124
- The chat surface is OpenAI-compatible, with a few opt-in extensions:
254
+ The chat surface is OpenAI-compatible, with opt-in extensions. On `chat()` the
255
+ 0.2.x option names still work:
125
256
 
126
257
  | Option | Description |
127
258
  | ----------------- | ----------------------------------------------------------------------- |
@@ -130,6 +261,35 @@ The chat surface is OpenAI-compatible, with a few opt-in extensions:
130
261
  | `compact: "auto"` | Server-side context compaction. |
131
262
  | `bestOf` | Best-of-N sampling override (`0` disables, `3` forces). |
132
263
 
264
+ Every method also accepts `{ ext }` — a typed `CodaiRequestExtensions` bag that
265
+ covers all 41 documented `X-Codai-*` request headers (`effort`, `thinking`,
266
+ `thinkingBudget`, `cache`, `noTask`, `taskId`, `incognito`, `noRecall`,
267
+ `provenOnly`, `repo`, `agentId`, `disableSubagents`, `mode`, `serverTools`,
268
+ `orchestrate`, `cascade`, `bestOf`, `reflect`, `stepVerify`, `plan`, `consensus`,
269
+ `compact`, `retrieval`, `heuristics`, `identity`, `playbook`, `debug`, `device`,
270
+ `shareToken`, …):
271
+
272
+ ```ts
273
+ await codai.chat.completions.create(
274
+ { messages, model: 'codai' },
275
+ { ext: { effort: 'high', thinking: true, thinkingBudget: 8192, taskId: 'task_42' } },
276
+ );
277
+ ```
278
+
279
+ ## Generated types
280
+
281
+ `paths`, `components` and `operations` from the gateway OpenAPI are exported, and
282
+ every resource re-exports friendly aliases (`ChatCompletionRequest`, `Task`,
283
+ `Session`, `AccountView`, …):
284
+
285
+ ```ts
286
+ import type { components, Task, SessionStreamEvent } from 'codai-sdk';
287
+ type Receipt = components['schemas']['AccountReceipt'];
288
+ ```
289
+
290
+ Regenerate after a spec change with `pnpm gen`; `operations.test.ts` fails when
291
+ the spec and the client drift.
292
+
133
293
  ## Migrating from the OpenAI SDK
134
294
 
135
295
  The chat payload is OpenAI-shaped, so migration is mostly swapping the client:
@@ -149,11 +309,28 @@ try {
149
309
  await codai.chat({ messages: [{ role: 'user', content: 'hi' }] });
150
310
  } catch (err) {
151
311
  if (err instanceof CodaiError) {
152
- console.error(err.status, err.message, err.body);
312
+ console.error(err.status, err.code, err.requestId, err.retryAfter, err.body);
153
313
  }
154
314
  }
155
315
  ```
156
316
 
317
+ `code` is the stable gateway error code (`invalid_api_key`, `rate_limit_exceeded`,
318
+ `quota_exceeded`, `lease_held`, `host_offline`, …); `retryAfter` is the
319
+ `Retry-After` value in seconds on 429s.
320
+
321
+ ## Migrating from 0.2.x
322
+
323
+ Nothing breaks: every 0.2.x method keeps its signature. What changed underneath:
324
+
325
+ - `chat`, `embeddings`, `models` and `feedback` are now **callable resource
326
+ groups** — `codai.chat({...})` still works and `codai.chat.completions.create({...})`
327
+ is the same call with the raw OpenAI body.
328
+ - `ChatResult` gained `eventId`, `toolCalls`, `headers`; `ChatStreamResult` gained
329
+ `finishReason`, `headers`, `usage.cachedTokens`.
330
+ - `CodaiError` gained `code`, `requestId`, `retryAfter`.
331
+ - Streams are no longer subject to `timeoutMs` and are never retried.
332
+ - New client options: `device`, `deviceName`, `devicePlatform`, `client`, `defaults`, `fetch`.
333
+
157
334
  ## License
158
335
 
159
336
  [MIT](./LICENSE) © codai