@chusky/sdk 1.6.4 → 1.7.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +275 -371
  3. package/cookbook/README.md +52 -0
  4. package/cookbook/a2a-delegation.ts +33 -0
  5. package/cookbook/approvals.ts +35 -0
  6. package/cookbook/company-agent.ts +47 -0
  7. package/cookbook/connect-business-app.ts +28 -0
  8. package/cookbook/context-and-department-handoff.ts +48 -0
  9. package/cookbook/files-and-image-attachment.ts +40 -0
  10. package/cookbook/mission.ts +59 -0
  11. package/cookbook/native-tool-health-check.ts +39 -0
  12. package/cookbook/run-and-wait.ts +30 -0
  13. package/cookbook/scheduled-follow-up.ts +40 -0
  14. package/cookbook/streaming.ts +43 -0
  15. package/cookbook/tsconfig.json +13 -0
  16. package/cookbook/webhooks.ts +27 -0
  17. package/cookbook/workflow-composer.ts +49 -0
  18. package/dist/client.d.ts +10 -10
  19. package/dist/client.d.ts.map +1 -1
  20. package/dist/client.js +5 -2
  21. package/dist/client.js.map +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/types.d.ts +47 -0
  25. package/dist/types.d.ts.map +1 -1
  26. package/docs/api-contract.md +11 -10
  27. package/docs/architecture.mdx +1 -1
  28. package/docs/company-workspaces.mdx +2 -2
  29. package/docs/concepts.mdx +1 -1
  30. package/docs/embedded-chat.mdx +3 -3
  31. package/docs/errors.mdx +1 -1
  32. package/docs/production.mdx +1 -1
  33. package/docs/quickstart.mdx +2 -2
  34. package/docs/releases.mdx +8 -15
  35. package/docs/security.mdx +1 -1
  36. package/docs/triggers.mdx +64 -0
  37. package/docs.json +1 -1
  38. package/examples/README.md +1 -1
  39. package/examples/_client.ts +2 -0
  40. package/openapi.yaml +41 -8
  41. package/package.json +5 -5
package/README.md CHANGED
@@ -2,477 +2,381 @@
2
2
 
3
3
  The official TypeScript client for the Chusky Developer API.
4
4
 
5
- Chusky gives applications a persistent, tool-using agent that can stream
6
- responses, run durable work, use connected business applications, pause for
7
- human approval, produce artifacts, and continue after restarts. The SDK is the
8
- server-side boundary for those capabilities; it does not expose Redis,
9
- Composio credentials, or Chusky's internal `CHUCK_*` tool implementations.
5
+ Use Chusky when your application needs an agent that can reason over context,
6
+ use connected business tools, stream progress, pause for a human decision, and
7
+ continue durable work after a request or process ends.
8
+
9
+ The SDK is designed for trusted server applications. It keeps connected-app
10
+ credentials out of browsers and mobile clients.
10
11
 
11
12
  ## Install
12
13
 
13
- ```bash
14
+ ~~~bash
14
15
  npm install @chusky/sdk
15
- ```
16
+ ~~~
16
17
 
17
- Requirements: Node.js 18 or newer.
18
+ Node.js 18 or newer is required.
18
19
 
19
- ## Five-minute quickstart
20
+ ## Quickstart
20
21
 
21
- Create a project-scoped API key in the Chusky dashboard under **Developer API**
22
- or provision one from a trusted operator environment. Then keep it on your
23
- server:
22
+ Create an API key in the Chusky dashboard, then configure it in your server
23
+ environment:
24
24
 
25
- ```env
25
+ ~~~env
26
26
  CHUSKY_API_KEY=chsk_your_project_key
27
- CHUSKY_BASE_URL=https://api.chusky.ai
28
- ```
27
+ ~~~
29
28
 
30
- Never put `CHUSKY_API_KEY` in browser JavaScript, a mobile binary, a public
31
- repository, or client-side environment variables.
29
+ The SDK automatically uses the hosted Chusky API, so `baseUrl` is optional.
30
+ Pass `baseUrl` only when targeting a staging or self-hosted API. Never expose
31
+ `CHUSKY_API_KEY` in browser code, mobile apps, public repositories, or prompts.
32
32
 
33
- ```ts
33
+ ~~~ts
34
34
  import { Chusky } from "@chusky/sdk";
35
35
 
36
36
  const chusky = new Chusky({
37
37
  apiKey: process.env.CHUSKY_API_KEY!,
38
- baseUrl: process.env.CHUSKY_BASE_URL,
39
- // Use your application's stable user or tenant identity. Do not use a
40
- // secret, email address, or the root operator identity here.
38
+ // Stable ID owned by your application. Do not use a secret.
41
39
  userId: "customer_123",
42
40
  });
43
41
 
44
42
  const { thread, run } = await chusky.runs.create(
45
- { input: "Prepare a concise renewal brief.", wait: false },
46
- { idempotencyKey: "renewal-brief-customer-123-2026-09-21" },
43
+ {
44
+ input: "Prepare a concise renewal brief from the available account context.",
45
+ wait: false,
46
+ budget: { duration: "5m", maxToolCalls: 20, maxCost: 1 },
47
+ },
48
+ { idempotencyKey: "renewal-brief-customer-123-v1" },
47
49
  );
48
50
 
49
51
  const completed = await chusky.runs.wait(thread.id, run.id, {
50
52
  timeoutMs: 120_000,
51
53
  });
52
54
 
53
- console.log(completed.status);
54
- console.log(completed.output ?? "The run did not produce text output.");
55
-
56
- // Generated image bytes are kept in the owner's private image store. Runs
57
- // include metadata only; refresh a short-lived download URL when needed.
58
- for (const image of completed.images ?? []) {
59
- const download = await chusky.images.get(image.id);
60
- console.log(download.contentType, download.downloadUrl, download.expiresAt);
55
+ if (completed.status === "failed") {
56
+ throw new Error(completed.error?.message ?? "Chusky run failed.");
61
57
  }
62
- ```
63
-
64
- Project-scoped API keys need the `images:read` scope to call `images.get()`.
65
- Treat its signed URL as a temporary secret and do not persist or publicly log it.
66
-
67
- For a reliability operation, `chusky.tools.list({ source: "native" })` and
68
- `chusky.tools.get(slug)` return the native JSON input schema. The SDK helper
69
- starts one durable run restricted to the selected operation; it does not call
70
- the native dispatcher outside the normal owner policy and approval path:
71
-
72
- ```ts
73
- const { thread, run } = await chusky.tools.run({
74
- tool: "CHUCK_ARTIFACT_QA",
75
- arguments: { path: "artifacts/quarterly-report.pdf", type: "pdf" },
76
- }, { idempotencyKey: "qa-quarterly-report-v1" });
77
- const result = await chusky.runs.wait(thread.id, run.id);
78
- ```
79
-
80
- The same helper supports `CHUCK_TOOL_PREFLIGHT`, `CHUCK_INTEGRATION_HEALTH`,
81
- `CHUCK_FILE_BRIDGE`, `CHUCK_MEDIA_BRIDGE`, and `CHUCK_TOOL_RECOVERY`. For image
82
- transfer, upload first and pass the returned owner-scoped file ID; only verified
83
- JPEG, PNG, or WebP images are accepted. Both bridges keep their normal approval
84
- gates, and approval decisions still belong to the human-facing workflow.
85
-
86
- ```ts
87
- const image = await chusky.files.upload({ name: "launch.png", contentType: "image/png", data: imageBytes });
88
- const { thread, run } = await chusky.tools.run({
89
- tool: "CHUCK_MEDIA_BRIDGE",
90
- arguments: { source: "current", toolSlug: "SOCIAL_POST", arguments: { caption: "Launch" } },
91
- attachments: [image.id],
92
- });
93
- ```
94
-
95
- `userId` is an application-owned identity boundary. Chusky uses it to isolate
96
- threads, runs, memories, approvals, files, tasks, reminders, connected
97
- accounts, and durable work. Use the same stable value whenever that user
98
- returns.
99
-
100
- ## Examples
101
-
102
- The [`examples/`](examples/) directory contains complete TypeScript examples
103
- that can be adapted directly into a server application:
104
-
105
- | Example | Shows |
106
- | --- | --- |
107
- | [`quickstart.ts`](examples/quickstart.ts) | Create a durable run and wait for completion |
108
- | [`streaming.ts`](examples/streaming.ts) | Stream response deltas and handle approval events |
109
- | [`company-agent.ts`](examples/company-agent.ts) | Use an agent template, policy, budget, and idempotency |
110
- | [`mission.ts`](examples/mission.ts) | Run multi-step work with proof, evidence, and verification |
111
- | [`approvals.ts`](examples/approvals.ts) | Present and decide a pending human approval |
112
- | [`context-and-departments.ts`](examples/context-and-departments.ts) | Save shared context and create a typed department handoff |
113
- | [`files.ts`](examples/files.ts) | Upload bytes through a short-lived storage intent |
114
- | [`webhooks.ts`](examples/webhooks.ts) | Register a delivery endpoint and inspect deliveries |
115
-
116
- Run an example from the SDK repository with `tsx`:
117
-
118
- ```bash
119
- CHUSKY_API_KEY=chsk_... npx tsx examples/quickstart.ts
120
- ```
121
-
122
- PowerShell:
123
58
 
124
- ```powershell
125
- $env:CHUSKY_API_KEY = "chsk_..."
126
- npx tsx examples/quickstart.ts
127
- ```
128
-
129
- Examples make real API requests. Use a development project key and a test
130
- identity when trying them.
59
+ console.log(completed.output ?? "The run completed without text output.");
60
+ ~~~
131
61
 
132
62
  ## The execution model
133
63
 
134
- ```text
64
+ ~~~text
135
65
  Your server
136
66
  ↓
137
67
  @chusky/sdk
138
68
  ↓ authenticated /v1 API
139
69
  Chusky runtime
140
70
  ↓
141
- agent loop → native tools / Composio / durable workflows
71
+ agent loop → native tools / connected apps / durable work
142
72
  ↓
143
- business result, artifact, webhook, or approval
144
- ```
73
+ text, approval, artifact, webhook, or verified business result
74
+ ~~~
145
75
 
146
- There are three useful execution modes:
76
+ Choose the execution style that matches the job:
147
77
 
148
- 1. **Synchronous** — set `wait: true` when the result should return in the
149
- request lifecycle and the work is short.
150
- 2. **Durable** — set `wait: false` to receive a task-backed run immediately,
151
- then use `runs.get()`, `runs.wait()`, `runs.events()`, `tasks.get()`, or a
152
- webhook to observe it.
153
- 3. **Streaming** — use `threads.runs(threadId).stream()` for incremental text
154
- and approval events. Streaming is a delivery channel, not the source of
155
- truth; persisted run state remains available through `get()` and `events()`.
78
+ | Use case | SDK entry point |
79
+ | --- | --- |
80
+ | One bounded request | <code>chusky.runs.create()</code> and <code>chusky.runs.wait()</code> |
81
+ | Conversation with incremental output | <code>chusky.threads.create()</code> and <code>chusky.threads.runs(threadId).stream()</code> |
82
+ | Work that may pause or retry | <code>runs.get()</code>, <code>runs.events()</code>, <code>runs.resume()</code>, and <code>tasks</code> |
83
+ | Multi-step process with checkpoints and proof | <code>chusky.missions</code> |
84
+ | Delegated agent-to-agent work | <code>chusky.a2a</code> |
85
+ | Recurring or scheduled work | <code>chusky.reminders</code> and <code>chusky.jobs</code> |
156
86
 
157
- ### Agent-to-agent tasks
87
+ Persist the returned threadId, runId, taskId, or missionId. Those identifiers
88
+ let your application reconnect after a timeout or restart without creating
89
+ duplicate work.
158
90
 
159
- The SDK also exposes the standards-shaped A2A boundary. Discover the remote
160
- Agent Card, submit a durable task, stream or subscribe to updates, and attach
161
- an encrypted callback for long-running work:
91
+ ## Stable identity
162
92
 
163
- ```ts
164
- const card = await chusky.a2a.card();
165
- const task = await chusky.a2a.send("Prepare a verified launch brief.", {
166
- idempotencyKey: "a2a-launch-brief-2026-09-22",
167
- });
93
+ userId is the isolation boundary for SDK state. Chusky uses it to scope
94
+ threads, runs, memory, files, artifacts, approvals, tasks, and connected
95
+ accounts.
168
96
 
169
- // For image tasks, upload with chusky.files.upload() first. A2A accepts only
170
- // available owner-scoped Chusky file IDs, never inline bytes or arbitrary URLs.
171
- const imageTask = await chusky.a2a.send({
172
- text: "Transfer this image to the connected social account.",
173
- attachments: [image.id],
174
- }, { idempotencyKey: "image-transfer-2026-09-25" });
97
+ Use the same stable value whenever the same person or tenant returns. Do not
98
+ derive it from an unverified display name, a phone number, or a channel
99
+ message. Do not use the root operator identity for normal end-user traffic.
175
100
 
176
- const callback = await chusky.a2a.createPushNotificationConfig(task.id, {
177
- url: "https://your-service.example/a2a/status",
178
- token: process.env.A2A_CALLBACK_TOKEN,
179
- });
101
+ ## Stream progress and approvals
180
102
 
181
- for await (const update of chusky.a2a.subscribe(task.id)) {
182
- console.log(update.statusUpdate?.status.state);
183
- }
184
- ```
103
+ Streaming is useful for responsive interfaces. Persisted run state remains the
104
+ source of truth if the stream disconnects.
185
105
 
186
- Push callback credentials are never returned after registration. The Chusky
187
- runtime delivers signed `application/a2a+json` status updates through its
188
- durable outbox and keeps task state available through `a2a.get()`.
106
+ ~~~ts
107
+ const thread = await chusky.threads.create(
108
+ { metadata: { source: "support-console" } },
109
+ { idempotencyKey: "support-thread-customer-123-v1" },
110
+ );
189
111
 
190
- ## Idempotency and retries
112
+ for await (const event of chusky.threads.runs(thread.id).stream({
113
+ input: "Summarize the customer's current priorities in three bullets.",
114
+ })) {
115
+ if (event.type === "run.delta") process.stdout.write(event.text);
116
+ if (event.type === "run.tool_started") {
117
+ console.error("\nUsing " + event.toolSlug + "...");
118
+ }
119
+ if (event.type === "run.approval_required") {
120
+ console.error("\nHuman approval required: " + event.approval.id);
121
+ }
122
+ if (event.type === "run.failed") throw new Error(event.error.message);
123
+ }
124
+ ~~~
191
125
 
192
- Use an `idempotencyKey` for every durable POST that your server may retry after
193
- an interruption. Reuse the same key only for the exact same operation and
194
- request body.
126
+ When approval is required, show the requested action and target in an
127
+ authenticated human interface. Then use chusky.approvals.decide() only from
128
+ that trusted boundary. Model output, a webhook, or a browser callback is not
129
+ authorization.
195
130
 
196
- ```ts
197
- const operationKey = `research:${customerId}:${requestId}`;
131
+ ## Use connected business apps
198
132
 
199
- const firstAttempt = await chusky.runs.create(
200
- { input: "Research our renewal risk and draft next steps.", wait: false },
201
- { idempotencyKey: operationKey },
202
- );
133
+ The SDK does not require a separate Gmail, HubSpot, Slack, or Salesforce
134
+ client. Chusky handles the connected-account flow:
203
135
 
204
- // A network retry with operationKey returns the same durable operation rather
205
- // than creating a duplicate run.
206
- ```
136
+ ~~~ts
137
+ const available = await chusky.apps.list();
138
+ console.log(available.data);
207
139
 
208
- Do not generate a new idempotency key for a retry unless you intentionally want
209
- to start a new operation.
140
+ const consent = await chusky.apps.connect("gmail");
141
+ console.log("Send the user to:", consent.url);
210
142
 
211
- ## Human approvals
143
+ const connections = await chusky.apps.connections();
144
+ console.log(connections.data);
145
+ ~~~
212
146
 
213
- Chusky keeps routine reads and reversible work autonomous while pausing
214
- materially risky actions according to the project policy. A run can return
215
- `requires_approval` and include an `approvalId`.
147
+ After the user finishes consent, retry the original operation with the same
148
+ stable identity. If a connection, approval, or human answer is missing, pause
149
+ the existing run or mission and resume it rather than starting a replacement.
216
150
 
217
- Your application should show the action, target, and relevant context to an
218
- authenticated human, then call `approvals.decide()`. Never auto-approve from a
219
- browser callback or from model output.
151
+ ## Files, images, and artifacts
220
152
 
221
- ```ts
222
- const approvals = await chusky.approvals.list();
223
- const pending = approvals.data.find((item) => item.status === "pending");
153
+ Upload input files through the SDK and pass the returned file ID to the run:
224
154
 
225
- if (pending) {
226
- // Render pending.request and the bounded action details in your own UI.
227
- const decision = await chusky.approvals.decide(
228
- pending.id,
229
- "approve",
230
- { idempotencyKey: `approval:${pending.id}:approve` },
231
- );
232
- console.log("Approval handled", decision);
233
- }
234
- ```
235
-
236
- The exact approval boundary is enforced server-side. The SDK is not a way to
237
- bypass it.
238
-
239
- ## Agent templates and company workflows
240
-
241
- Use a built-in specialist template or create a governed agent profile for a
242
- company workflow. Policies, allowed tools, budgets, and approvals are applied
243
- by Chusky before execution.
244
-
245
- ```ts
246
- const templates = await chusky.agents.templates();
247
- console.log(templates.data.map((template) => template.slug));
248
-
249
- const agent = await chusky.agents.create({
250
- template: "lead-research",
251
- name: "Fintech lead scout",
252
- instructions: "Return sourced, deduplicated company profiles.",
253
- policy: {
254
- tools: {
255
- allow: ["crm.read", "web.search", "email.draft"],
256
- requireApproval: ["email.send", "crm.write"],
257
- },
258
- budget: { duration: "30m", maxToolCalls: 80, maxCost: 8 },
259
- },
155
+ ~~~ts
156
+ import { readFile } from "node:fs/promises";
157
+
158
+ const imageBytes = new Uint8Array(await readFile("launch.png"));
159
+ const image = await chusky.files.upload({
160
+ name: "launch.png",
161
+ contentType: "image/png",
162
+ data: imageBytes,
260
163
  });
261
164
 
262
165
  const { thread, run } = await chusky.runs.create(
263
166
  {
264
- agentId: agent.id,
265
- input: "Find qualified fintech leads with more than 50 employees.",
167
+ input: "Use this image in the prepared social post, then wait for approval.",
168
+ attachments: [image.id],
266
169
  wait: false,
267
170
  },
268
- { idempotencyKey: "acme-fintech-leads-2026-09-21" },
171
+ { idempotencyKey: "launch-post-image-customer-123-v1" },
269
172
  );
270
173
 
271
- console.log(`Run ${run.id} started in thread ${thread.id}`);
272
- ```
174
+ console.log(thread.id, run.id, image.id);
175
+ ~~~
273
176
 
274
- Composio owns OAuth, connected accounts, token refresh, and external tool
275
- execution. Chusky owns the agent profile, policy, orchestration, approvals,
276
- durability, and result delivery.
177
+ Use chusky.images.get(imageId) for a fresh, short-lived download URL for a
178
+ generated image. Use chusky.artifacts.get() and chusky.artifacts.download() for
179
+ generated documents and other artifacts.
180
+
181
+ Do not send raw bytes, credentials, or arbitrary public URLs inside a prompt
182
+ and assume a provider received them. Verify the file belongs to the current
183
+ identity and confirm the provider receipt before claiming an external post or
184
+ message succeeded.
277
185
 
278
186
  ## Durable missions
279
187
 
280
- Use missions when the work has multiple steps, dependencies, budgets, evidence,
281
- waits, or a definition of done. The mission API supports pause, resume,
282
- repair, cancellation, provider-event continuation, replanning, proof, and
283
- verification.
284
-
285
- ```ts
286
- const mission = await chusky.missions.create({
287
- title: "Qualified fintech leads",
288
- objective: "Find 20 fintech companies matching our ICP.",
289
- definitionOfDone: "Every lead has a source, qualification reason, and CRM-ready payload.",
290
- verificationMode: "strict",
291
- requiredEvidence: ["source URL", "qualification assertion", "deduplication check"],
292
- steps: [
293
- { id: "research", title: "Research companies", objective: "Collect source-backed facts." },
294
- { id: "qualify", title: "Qualify leads", objective: "Apply the ICP and remove duplicates.", dependsOn: ["research"] },
295
- { id: "prepare", title: "Prepare CRM payload", objective: "Create an approval-ready import.", dependsOn: ["qualify"] },
296
- ],
297
- maxDurationSeconds: 3 * 60 * 60,
298
- maxSteps: 30,
299
- maxToolCalls: 100,
300
- maxCost: 15,
301
- }, { idempotencyKey: "acme-lead-mission-2026-09-21" });
188
+ Use a mission when a job has multiple steps, dependencies, budgets, waits, or a
189
+ definition of done:
190
+
191
+ ~~~ts
192
+ const mission = await chusky.missions.create(
193
+ {
194
+ title: "Turn a qualified inquiry into a confirmed order",
195
+ objective: "Research the buyer, answer questions, and prepare an offer.",
196
+ definitionOfDone:
197
+ "The CRM record is updated, the offer is prepared, and no purchase is made without approval.",
198
+ verificationMode: "strict",
199
+ requiredEvidence: ["CRM record", "buyer requirements", "approval-ready offer"],
200
+ steps: [
201
+ { id: "research", title: "Research buyer", objective: "Collect verified facts." },
202
+ { id: "qualify", title: "Qualify opportunity", objective: "Check fit and budget.", dependsOn: ["research"] },
203
+ { id: "offer", title: "Prepare offer", objective: "Draft the bounded offer.", dependsOn: ["qualify"] },
204
+ ],
205
+ maxDurationSeconds: 3 * 60 * 60,
206
+ maxSteps: 30,
207
+ maxToolCalls: 100,
208
+ maxCost: 15,
209
+ },
210
+ { idempotencyKey: "buyer-inquiry-customer-123-v1" },
211
+ );
302
212
 
303
213
  const proof = await chusky.missions.proof(mission.id);
304
214
  console.log(proof.status, proof.nextAction, proof.verification);
305
- ```
306
-
307
- Treat `proof()` and `verify()` as the external completion record. Do not claim
308
- that a mission completed because a model produced a plausible paragraph; use
309
- the recorded steps, evidence, and verification state.
310
-
311
- ## Shared context, departments, and outcomes
312
-
313
- The operating layer lets applications preserve useful, sensitivity-aware
314
- context and hand work between specialized departments.
315
-
316
- ```ts
317
- await chusky.context.save({
318
- scope: "customer",
319
- scopeId: "customer_123",
320
- kind: "preference",
321
- key: "renewal_window",
322
- value: "Customer prefers renewal discussions in October.",
323
- source: "crm",
324
- confidence: 0.9,
325
- sensitivity: "normal",
326
- });
215
+ ~~~
327
216
 
328
- const salesContext = await chusky.context.list({
329
- scope: "customer",
330
- scopeId: "customer_123",
331
- purpose: "renewal",
332
- });
217
+ Missions support pause(), resume(), cancel(), repair(), replan(), evidence(),
218
+ and verify(). Use proof and fresh provider readbacks as the completion record;
219
+ a plausible model paragraph is not proof that a business action happened.
333
220
 
334
- const packet = await chusky.departments.handoff("customer-success", {
335
- objective: "Prepare a renewal risk review for the account team.",
336
- inputs: { customerId: "customer_123" },
337
- constraints: ["Use verified CRM facts only."],
338
- evidenceRequired: ["account health source", "open risk owner"],
339
- approvalBoundary: "Draft only; do not contact the customer.",
340
- });
221
+ ## Agent-to-agent work
341
222
 
342
- console.log(packet.id, packet.status, salesContext.data.length);
343
- ```
223
+ The SDK includes a typed A2A client for durable delegated tasks:
344
224
 
345
- ## Agent-to-agent (A2A)
346
-
347
- The SDK includes a typed client for Chusky's standards-shaped A2A 1.0
348
- boundary. It uses the same project API key and stable user identity as the
349
- rest of the SDK, so delegated work stays owner-scoped and durable.
350
-
351
- ```ts
225
+ ~~~ts
352
226
  const card = await chusky.a2a.card();
353
- console.log(card.protocolVersion, card.skills?.map((skill) => skill.id));
227
+ console.log(card.skills);
354
228
 
355
229
  const task = await chusky.a2a.send("Prepare a verified launch brief.", {
356
- idempotencyKey: "launch-brief-2026-09-22",
230
+ idempotencyKey: "a2a-launch-brief-v1",
357
231
  });
358
232
 
359
233
  const current = await chusky.a2a.get(task.id);
360
- const page = await chusky.a2a.list(undefined, 20);
361
- console.log(current.status.state, page.tasks.length);
234
+ console.log(current.status.state);
235
+ ~~~
362
236
 
363
- if (current.status.state !== "TASK_STATE_COMPLETED") {
364
- await chusky.a2a.cancel(current.id);
365
- }
366
- ```
367
-
368
- Use `a2a.card()` for discovery, `a2a.send()` for a durable delegated task,
369
- `a2a.get()` or `a2a.list()` for status, and `a2a.cancel()` for cancellation.
370
- The SDK sends A2A JSON-RPC over the authenticated `/a2a/rpc` boundary and
371
- does not expose private prompts, credentials, or unscoped tenant data. Image
372
- references use Chusky's `data.chuskyFileIds` message-part extension and are
373
- validated against the caller's available image files before the durable task
374
- is queued.
375
-
376
- ## Files and artifacts
377
-
378
- File uploads use a short-lived storage URL. The SDK also exposes artifact
379
- metadata and verified downloads for files generated by Chusky.
380
-
381
- ```ts
382
- const body = new TextEncoder().encode("customer_id,renewal_date\n123,2026-10-01\n");
383
- const upload = await chusky.files.create({
384
- name: "renewals.csv",
385
- contentType: "text/csv",
386
- size: body.byteLength,
387
- }, { idempotencyKey: "upload-renewals-2026-09-21" });
388
-
389
- const response = await fetch(upload.uploadUrl, {
390
- method: "PUT",
391
- headers: { "Content-Type": "text/csv" },
392
- body,
393
- });
394
- if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
237
+ Use a2a.stream(), a2a.subscribe(), or
238
+ a2a.createPushNotificationConfig() for long-running delegated work.
239
+ Attachments must be owner-scoped Chusky file IDs, not inline bytes or arbitrary
240
+ URLs.
395
241
 
396
- const file = await chusky.files.complete(upload.id);
397
- console.log(file.id, file.status);
398
- ```
242
+ ## Idempotency, retries, and errors
399
243
 
400
- ## Webhooks
244
+ Use an idempotencyKey for every durable write that your server might retry.
245
+ Reuse the same key only for the same operation and request body.
401
246
 
402
- Register a server endpoint for durable delivery notifications and make the
403
- handler idempotent by recording the delivery ID before applying the event.
247
+ ~~~ts
248
+ const key = "research:" + customerId + ":" + requestId;
404
249
 
405
- ```ts
406
- const webhook = await chusky.webhooks.create(
407
- "https://app.example.com/api/chusky/events",
408
- { idempotencyKey: "webhook-register-events-v1" },
250
+ const first = await chusky.runs.create(
251
+ { input: "Research renewal risk and draft next steps.", wait: false },
252
+ { idempotencyKey: key },
409
253
  );
410
254
 
411
- const deliveries = await chusky.webhooks.deliveries(webhook.id);
412
- console.log(deliveries.data.map((delivery) => delivery.status));
413
- ```
255
+ // Retrying with key returns the same durable operation rather than a duplicate.
256
+ ~~~
257
+
258
+ Catch the typed errors when you need specific recovery:
259
+
260
+ ~~~ts
261
+ import {
262
+ ChuskyAuthenticationError,
263
+ ChuskyRateLimitError,
264
+ } from "@chusky/sdk";
265
+
266
+ try {
267
+ await chusky.usage.get();
268
+ } catch (error) {
269
+ if (error instanceof ChuskyRateLimitError) {
270
+ console.log("Retry after:", error.retryAfter);
271
+ } else if (error instanceof ChuskyAuthenticationError) {
272
+ console.log("Check the API key and user identity.");
273
+ }
274
+ throw error;
275
+ }
276
+ ~~~
414
277
 
415
- ## Operator provisioning
278
+ If a request times out, read the run or task by ID before retrying. A lost HTTP
279
+ response does not prove that durable work failed.
416
280
 
417
- `createChuskyAdmin()` is for a trusted operator service only. It uses the root
418
- project key to provision scoped project keys and must never be shipped to an
419
- end-user application.
281
+ ## Resource map
420
282
 
421
- ```ts
422
- import { createChuskyAdmin } from "@chusky/sdk";
283
+ | Resource | What it helps you build |
284
+ | --- | --- |
285
+ | threads, runs, tasks | Conversations, durable execution, and recovery |
286
+ | agents, tools, skills | Governed agent profiles and available capabilities |
287
+ | apps, mcp, channels, devices | Connected accounts, external MCP servers, delivery, and device access |
288
+ | missions, workflows, outcomes, departments | Multi-step business processes and handoffs |
289
+ | files, images, artifacts, videos | Inputs and generated outputs |
290
+ | context, memory, scratchpad | Explicit operating context and temporary notes |
291
+ | meetings, calls | Meeting preparation, joining, and voice operations |
292
+ | reminders, jobs, webhooks, audit, usage | Scheduled work, delivery, traceability, and usage |
293
+ | autonomy, operator, company | Readiness, reliability, business queues, and company telemetry |
294
+
295
+ The public package also exports the dependency-free
296
+ @chusky/sdk/widget entry point for a browser chat element. The browser widget
297
+ talks to your own server endpoint; your server keeps the API key private.
298
+
299
+ ~~~ts
300
+ import { defineChuskyChat } from "@chusky/sdk/widget";
301
+
302
+ defineChuskyChat();
303
+ ~~~
304
+
305
+ ~~~html
306
+ <chusky-chat
307
+ endpoint="/api/chusky/chat"
308
+ title="Talk to our assistant"
309
+ greeting="How can we help?"
310
+ ></chusky-chat>
311
+ ~~~
423
312
 
424
- const admin = createChuskyAdmin({
425
- apiKey: process.env.CHUSKY_PROJECT_KEY!,
426
- baseUrl: process.env.CHUSKY_BASE_URL,
427
- });
313
+ ## Examples
428
314
 
429
- const project = await admin.projects.create({
430
- name: "Acme production",
431
- scopes: ["runs:create", "runs:read", "missions:read", "missions:create"],
432
- });
315
+ The examples directory contains runnable TypeScript examples:
433
316
 
434
- console.log(project.key); // Store once. It is not returned by list().
435
- ```
317
+ | Example | Demonstrates |
318
+ | --- | --- |
319
+ | [quickstart.ts](examples/quickstart.ts) | Start a durable run and wait for completion |
320
+ | [streaming.ts](examples/streaming.ts) | Stream deltas, tool activity, and approval events |
321
+ | [company-agent.ts](examples/company-agent.ts) | Create a governed company agent |
322
+ | [mission.ts](examples/mission.ts) | Define steps, evidence, and mission proof |
323
+ | [approvals.ts](examples/approvals.ts) | Display and decide a pending approval |
324
+ | [context-and-departments.ts](examples/context-and-departments.ts) | Save context and create a department handoff |
325
+ | [files.ts](examples/files.ts) | Upload bytes through the SDK |
326
+ | [webhooks.ts](examples/webhooks.ts) | Register a delivery endpoint and inspect deliveries |
327
+
328
+ Run an example from this directory with an API key created in the Chusky
329
+ dashboard:
330
+
331
+ ~~~bash
332
+ CHUSKY_API_KEY=chsk_... npx tsx examples/quickstart.ts
333
+ ~~~
436
334
 
437
- ## Resource map
335
+ PowerShell:
438
336
 
439
- | Resource | Use it for |
440
- | --- | --- |
441
- | `threads`, `runs` | Conversations and durable agent execution |
442
- | `agents`, `company` | Governed profiles and company telemetry |
443
- | `tasks`, `approvals` | Recovery and human decisions |
444
- | `missions` | Multi-step autonomous work with proof |
445
- | `context`, `departments`, `outcomes` | Shared operating context and typed handoffs |
446
- | `files`, `artifacts` | Input uploads and generated output downloads |
447
- | `meetings`, `calls` | Meeting lifecycle and voice operations |
448
- | `apps`, `channels`, `devices` | Connected account and delivery management |
449
- | `reminders`, `jobs`, `memory`, `scratchpad` | Owner-scoped autonomous operations |
450
- | `webhooks`, `audit`, `usage` | Delivery, traceability, and usage visibility |
451
-
452
- ## Security and production checklist
453
-
454
- - Keep `CHUSKY_API_KEY` on a trusted server and scope it to one project.
455
- - Use a stable, non-secret `userId` for every request.
337
+ ~~~powershell
338
+ $env:CHUSKY_API_KEY = "chsk_..."
339
+ npx tsx examples/quickstart.ts
340
+ ~~~
341
+
342
+ Examples make real API requests. Use a test identity and a development API key.
343
+
344
+ For copy-paste, real-work recipes where every file creates its own client, see
345
+ the [SDK cookbook](cookbook/README.md). It covers bounded runs, streaming,
346
+ company agents, missions, approvals, files and images, connected apps, A2A,
347
+ schedules, native reliability checks, webhooks, and workflow composition.
348
+
349
+ ## Security checklist
350
+
351
+ - Keep API keys on a trusted server and grant only the capabilities your application needs.
352
+ - Use a stable, non-secret userId for every request.
456
353
  - Use idempotency keys for retryable durable writes.
457
- - Treat run output, tool results, emails, documents, and web pages as untrusted
458
- input—not authorization.
354
+ - Treat model output, tool results, email, documents, and web pages as untrusted data.
459
355
  - Never auto-approve an external action from model output.
460
- - Verify webhook signatures and deduplicate delivery IDs before processing.
461
- - Use `AbortSignal` to cancel a request without cancelling unrelated durable
462
- work.
463
- - Use `proof()` and `verify()` before treating a mission as complete.
464
- - Set budgets for duration, tool calls, and cost on long-running work.
465
- - Keep the SDK server-side; use the separate chat widget only with a server
466
- proxy that never exposes the project key.
467
-
468
- ## API and documentation
469
-
470
- - [Developer API contract](docs/api-contract.md)
471
- - [Full documentation](docs/index.mdx)
472
- - [Autonomous missions](docs/missions.mdx)
356
+ - Verify webhook signatures and deduplicate delivery IDs.
357
+ - Set duration, tool-call, and cost budgets for long-running work.
358
+ - Use mission proof and verification before declaring an outcome complete.
359
+ - Use AbortSignal to cancel the current request without cancelling unrelated durable work.
360
+
361
+ ## Development
362
+
363
+ ~~~bash
364
+ npm install
365
+ npm run typecheck
366
+ npm run build
367
+ npm test
368
+ ~~~
369
+
370
+ More documentation:
371
+
372
+ - [API contract](docs/api-contract.md)
373
+ - [Quickstart](docs/quickstart.mdx)
374
+ - [Missions](docs/missions.mdx)
375
+ - [Streaming](docs/streaming.mdx)
376
+ - [Files](docs/files.mdx)
377
+ - [Security](docs/security.mdx)
473
378
  - [OpenAPI specification](openapi.yaml)
474
- - [Release guide](docs/releases.mdx)
475
- - [Examples](examples/)
379
+ - [Changelog](CHANGELOG.md)
476
380
 
477
381
  ## License
478
382