@chusky/sdk 0.1.1 → 0.2.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.
package/dist/widget.js ADDED
@@ -0,0 +1,137 @@
1
+ const HTMLElementBase = globalThis.HTMLElement ?? class {
2
+ };
3
+ /** A dependency-free chat element. The host owns auth and keeps the API key server-side. */
4
+ export class ChuskyChatElement extends HTMLElementBase {
5
+ messages = [];
6
+ busy = false;
7
+ conversationId;
8
+ connectedCallback() {
9
+ if (!this.shadowRoot)
10
+ this.attachShadow({ mode: "open" });
11
+ this.render();
12
+ }
13
+ options() {
14
+ return {
15
+ endpoint: this.getAttribute("endpoint") ?? "/api/chusky/chat",
16
+ title: this.getAttribute("title") ?? "Chat with us",
17
+ greeting: this.getAttribute("greeting") ?? "How can I help?",
18
+ accentColor: this.getAttribute("accent-color") ?? "#111111",
19
+ };
20
+ }
21
+ render() {
22
+ if (!this.shadowRoot)
23
+ return;
24
+ const options = this.options();
25
+ const messages = this.messages.length ? this.messages.map((message) => `
26
+ <div class="message ${message.role}" aria-label="${message.role === "user" ? "You" : "Chusky"}">${escapeHtml(message.text).replace(/\n/g, "<br />")}</div>`).join("") : `<p class="greeting">${escapeHtml(options.greeting ?? "How can I help?")}</p>`;
27
+ const accent = escapeHtml(options.accentColor ?? "#111111");
28
+ this.shadowRoot.innerHTML = `
29
+ <style>
30
+ :host { color: #1d1d1b; font: 14px/1.45 system-ui, sans-serif; }
31
+ .shell { width: min(360px, calc(100vw - 32px)); border: 1px solid #d9d9d2; border-radius: 16px; background: #fff; box-shadow: 0 16px 45px rgb(0 0 0 / 12%); overflow: hidden; }
32
+ header { padding: 14px 16px; color: #fff; background: ${accent}; }
33
+ header strong { font-size: 14px; }
34
+ .messages { display: grid; gap: 9px; max-height: 320px; overflow: auto; padding: 16px; background: #f7f7f4; }
35
+ .greeting { margin: 0; color: #696963; }
36
+ .message { max-width: 84%; padding: 9px 11px; border-radius: 11px; overflow-wrap: anywhere; }
37
+ .message.user { justify-self: end; color: #fff; background: ${accent}; }
38
+ .message.assistant { justify-self: start; border: 1px solid #deded7; background: #fff; }
39
+ form { display: flex; gap: 8px; padding: 12px; border-top: 1px solid #e3e3dd; background: #fff; }
40
+ textarea { flex: 1; min-height: 38px; max-height: 110px; resize: vertical; border: 1px solid #cfcfc7; border-radius: 9px; padding: 9px; font: inherit; }
41
+ button { align-self: end; border: 0; border-radius: 9px; padding: 10px 12px; color: #fff; background: ${accent}; cursor: pointer; }
42
+ button:disabled { cursor: wait; opacity: .55; }
43
+ textarea:focus-visible, button:focus-visible { outline: 2px solid #6a8cff; outline-offset: 2px; }
44
+ </style>
45
+ <section class="shell" aria-label="${escapeHtml(options.title ?? "Chat")}">
46
+ <header><strong>${escapeHtml(options.title ?? "Chat with us")}</strong></header>
47
+ <div class="messages" role="log" aria-live="polite">${messages}</div>
48
+ <form>
49
+ <textarea name="message" maxlength="4000" aria-label="Message" placeholder="Write a message…" required></textarea>
50
+ <button type="submit" ${this.busy ? "disabled" : ""}>${this.busy ? "…" : "Send"}</button>
51
+ </form>
52
+ </section>`;
53
+ this.shadowRoot.querySelector("form")?.addEventListener("submit", (event) => {
54
+ event.preventDefault();
55
+ const input = this.shadowRoot?.querySelector("textarea");
56
+ if (input?.value.trim())
57
+ void this.send(input.value.trim());
58
+ });
59
+ const log = this.shadowRoot.querySelector(".messages");
60
+ if (log)
61
+ log.scrollTop = log.scrollHeight;
62
+ }
63
+ async send(message) {
64
+ if (this.busy)
65
+ return;
66
+ this.busy = true;
67
+ this.messages.push({ role: "user", text: message });
68
+ this.messages.push({ role: "assistant", text: "" });
69
+ this.render();
70
+ try {
71
+ const response = await fetch(this.options().endpoint, {
72
+ method: "POST",
73
+ credentials: "include",
74
+ headers: { "Content-Type": "application/json", Accept: "application/x-ndjson, application/json" },
75
+ body: JSON.stringify({ message, conversationId: this.conversationId }),
76
+ });
77
+ if (!response.ok)
78
+ throw new Error("The assistant could not respond.");
79
+ await this.consume(response);
80
+ }
81
+ catch (error) {
82
+ this.messages[this.messages.length - 1] = { role: "assistant", text: error instanceof Error ? error.message : "The assistant could not respond." };
83
+ }
84
+ finally {
85
+ this.busy = false;
86
+ this.render();
87
+ }
88
+ }
89
+ async consume(response) {
90
+ const contentType = response.headers.get("content-type") ?? "";
91
+ if (!contentType.includes("ndjson") || !response.body) {
92
+ const body = await response.json().catch(async () => ({ text: await response.text() }));
93
+ if (body.conversationId)
94
+ this.conversationId = body.conversationId;
95
+ this.messages[this.messages.length - 1].text = body.text ?? body.message ?? "";
96
+ return;
97
+ }
98
+ const reader = response.body.getReader();
99
+ const decoder = new TextDecoder();
100
+ let pending = "";
101
+ while (true) {
102
+ const { value, done } = await reader.read();
103
+ pending += decoder.decode(value, { stream: !done });
104
+ const lines = pending.split("\n");
105
+ pending = lines.pop() ?? "";
106
+ for (const line of lines)
107
+ this.consumeEvent(line);
108
+ if (done)
109
+ break;
110
+ }
111
+ if (pending.trim())
112
+ this.consumeEvent(pending);
113
+ }
114
+ consumeEvent(line) {
115
+ if (!line.trim())
116
+ return;
117
+ const event = JSON.parse(line);
118
+ if (event.conversationId)
119
+ this.conversationId = event.conversationId;
120
+ if (event.type === "run.delta" || event.type === "message.delta")
121
+ this.messages[this.messages.length - 1].text += event.text ?? "";
122
+ if (event.type === "run.approval_required" || event.type === "approval_required")
123
+ this.messages[this.messages.length - 1].text += "\nThis action is waiting for approval.";
124
+ if (event.type === "error")
125
+ this.messages[this.messages.length - 1].text += event.error ?? event.message ?? "The assistant encountered an error.";
126
+ this.render();
127
+ }
128
+ }
129
+ export function defineChuskyChat(tagName = "chusky-chat") {
130
+ if (typeof customElements === "undefined" || customElements.get(tagName))
131
+ return;
132
+ customElements.define(tagName, ChuskyChatElement);
133
+ }
134
+ function escapeHtml(value) {
135
+ return value.replace(/[&<>\"']/g, (character) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", "\"": "&quot;", "'": "&#39;" })[character] ?? character);
136
+ }
137
+ //# sourceMappingURL=widget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"widget.js","sourceRoot":"","sources":["../src/widget.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,22 @@ scopes, rotate, and revoke project keys. They never accept or return
20
20
  Each verified account may have at most 10 active projects. Root-created projects
21
21
  remain ownerless operator records and are not visible through account routes.
22
22
 
23
+ Company projects attach to a Better Auth organization ID. Owners/admins can
24
+ create and manage project credentials, policy, and up to 20 agent profiles;
25
+ verified members can list those project resources. Default company scopes are
26
+ least-privilege and intentionally exclude `approvals:write`, so a project key
27
+ cannot approve its own external tool actions. Composio app/OAuth and trigger
28
+ endpoints remain the existing integration surface and retain the stable Chusky
29
+ end-user identity supplied in `X-Chusky-User-Id`.
30
+
31
+ Company telemetry is project-scoped rather than caller-scoped: keys with the
32
+ `company:read` scope can read status-only run summaries, bounded audit events,
33
+ and monthly completed-run/model-cost totals at `/v1/company/runs`,
34
+ `/v1/company/audit-events`, and `/v1/company/usage`. The authenticated
35
+ workspace dashboard exposes the same views only to organization owners/admins.
36
+ Run inputs/outputs and user/provider payloads are never copied into this shared
37
+ ledger. Durable run completion accounting is idempotent by project and run ID.
38
+
23
39
  ## Resources
24
40
 
25
41
  | Resource | Endpoint | Notes |
@@ -27,6 +43,8 @@ remain ownerless operator records and are not visible through account routes.
27
43
  | Threads | `POST /v1/threads`, `GET /v1/threads/:threadId` | Conversation/memory boundary for one explicit SDK end user. |
28
44
  | Projects | `GET/POST /v1/admin/projects`, `DELETE /v1/admin/projects/:id` | Root-key-only project provisioning and key revocation. |
29
45
  | Dashboard projects | `GET/POST /v1/account/projects`, `PATCH /v1/account/projects/:id`, `POST .../rotate-key`, `DELETE .../:id` | Better-Auth-cookie-only, verified-user project management. |
46
+ | Company policy | `GET/PUT /v1/account/projects/:id/policy` | Organization members may read; only owners/admins may change bounded tool grants and per-run budgets. |
47
+ | Company agents | `GET/POST/PATCH/DELETE /v1/account/projects/:id/agents` and `/v1/agents` | Dashboard management is role-checked; SDK routes require project `agents:read/write` scopes. Templates are listed at `GET /v1/agents/templates`. |
30
48
  | Runs | `POST /v1/threads/:threadId/runs` | Executes durable Chusky work. `wait` is bounded. |
31
49
  | Run stream | `POST /v1/threads/:threadId/runs/stream` | `application/x-ndjson`; emits typed run events. |
32
50
  | Runs | `GET /v1/threads/:threadId/runs/:runId`, `POST .../cancel` | Cancellation is request-specific; durable task results stay queryable. |
@@ -35,7 +53,16 @@ remain ownerless operator records and are not visible through account routes.
35
53
  | Files | `POST /v1/files`, `POST /v1/files/:fileId/complete`, `GET/DELETE /v1/files/:fileId` | Direct R2 upload URLs are short-lived; a `HEAD` verification must succeed before download; deletion is owner-scoped. |
36
54
  | Webhooks | `POST /v1/webhooks`, `GET /v1/webhooks`, `DELETE /v1/webhooks/:id` | HTTPS-only subscription; secret is encrypted at rest and returned only on creation. |
37
55
  | Delivery history | `GET /v1/webhooks/:id/deliveries` | Bounded, safe delivery status for operational diagnosis; delete disables future deliveries. |
56
+ | Trigger catalogue | `GET /v1/triggers/catalog/toolkits`, `GET /v1/triggers/catalog/toolkits/:toolkit` | Composio-backed, paginated trigger types for connected apps; the dashboard uses the same catalogue as Telegram. `POST /v1/triggers` can pin creation to a verified `connectedAccountId`. |
38
57
  | Observability | `GET /v1/audit-events`, `GET /v1/usage` | Bounded per-user audit trail and current usage snapshot. |
58
+ | Company telemetry | `GET /v1/company/runs`, `/v1/company/audit-events`, `/v1/company/usage`; dashboard `GET /v1/account/projects/:id/company/{runs,audit-events,usage}` | Requires `company:read` for project keys; dashboard reads require workspace owner/admin. Run summaries contain no prompt or output. |
59
+ | Calls | `GET/POST /v1/account/calls` | Lists redacted call metadata and creates an approval-gated outbound call request. SDK callers use `calls:read/write`; dashboard callers must be verified and Telegram-linked. |
60
+ | Voice | `GET /v1/account/voice-options`, `PATCH /v1/account/preferences` | Lists Flux and optional Bland catalogue entries and stores the account's live voice preference. Use `voice:read` for the catalogue and `account:write` for preferences. |
61
+ | Meetings | `GET/POST /v1/meetings`, `POST /v1/meetings/prepare`, `GET/PATCH /v1/meetings/profile`, `POST /v1/meetings/preparations/:id/join`, `GET /v1/meetings/:id`, `POST /v1/meetings/:id/leave`, `GET /v1/meetings/:id/context`, `DELETE /v1/meetings/contacts/:id` | Recall lifecycle for Zoom, Google Meet, Microsoft Teams, and Webex. SDK callers use `meetings:read/write`; meeting URLs and sealed calendar links are never returned by list endpoints. |
62
+ | Connected apps | `GET /v1/apps`, `POST /v1/apps/:toolkit/connect`, `GET /v1/apps/connections`, `DELETE /v1/apps/connections/:id` | Composio toolkit discovery, OAuth connection links, connected-account listing, and disconnect. Credentials remain server-side. |
63
+ | Native schedules | `GET/POST/DELETE /v1/reminders`, `GET/POST/DELETE /v1/jobs` | One-time reminders and recurring QStash schedules owned by the SDK user. |
64
+ | Memory and scratchpad | `GET/POST/DELETE /v1/memory`, `GET/PUT/DELETE /v1/scratchpad` | Explicit structured memory and temporary working notes; both are user-scoped. |
65
+ | Channel and device management | `GET/POST/PATCH/DELETE /v1/channels`, `GET/DELETE /v1/devices` | Link supported channels, control proactive delivery, and revoke CLI devices without exposing credentials. |
39
66
 
40
67
  ## Event stream
41
68
 
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: How the platform works
3
+ description: Understand request boundaries, durable execution, storage, and delivery.
4
+ ---
5
+
6
+ ## Request boundaries
7
+
8
+ Every SDK request is scoped by two values: the project key in `Authorization` and your application user ID in `X-Chusky-User-Id`. The user ID is the ownership boundary for threads, runs, files, approvals, tasks, artifacts, channel connections, and activity. Private credentials and internal tool implementations stay on the server.
9
+
10
+ The API is versioned under `/v1`. POST operations that create or change durable state accept `Idempotency-Key`; reuse the same key only when retrying the exact same operation. A mismatch returns a conflict instead of creating a duplicate.
11
+
12
+ ## Synchronous and durable runs
13
+
14
+ `runs.create()` waits for a terminal result by default. `wait: false` persists the run and creates a task before returning `202`. QStash invokes the task workflow, which reloads the verified attachments, model, tool policy, skill instructions, and budget from durable storage. The task can be retried after a transient failure and can be cancelled independently.
15
+
16
+ The run’s duration budget is a wall-clock limit across continuations. Tool-call and cost ceilings are checked inside the agent loop. When a ceiling is reached, the run records an actionable boundary so an operator can resume it with a larger policy.
17
+
18
+ ## Capability and approval flow
19
+
20
+ The server resolves the requested native and Composio tools, then applies allow and deny rules before sending the catalog to the model. `requireApproval` adds an explicit approval gate for selected tools, including tools that are not classified as risky by default. Risky tools still require approval automatically. Approval decisions are scoped to the user, expire, and bind to the arguments that the reviewer saw.
21
+
22
+ Skills are read from the trusted project `.chusky/skills` catalog. A skill’s `SKILL.md` is loaded as bounded instructions, and supporting files can be discovered through the skills API. Unknown or removed skills are ignored safely so a run can still complete with the remaining instructions.
23
+
24
+ ## Files and artifacts
25
+
26
+ Uploads go directly to Cloudflare R2 using a five-minute presigned URL. Chusky verifies the object’s size and content type before marking it available. Runs refer to file IDs; the server resolves those IDs to bytes or signed URLs and never accepts arbitrary object keys from the client.
27
+
28
+ Artifacts are outputs registered by the Daytona workspace. The artifact record is scoped to the owning user; the download endpoint checks ownership and returns verified bytes with a safe filename. This makes generated PDFs, documents, presentations, spreadsheets, images, videos, and ZIP files addressable after the original run has ended.
29
+
30
+ ## Webhooks and operational views
31
+
32
+ Terminal run events are written to the durable webhook outbox before delivery. The outbox leases work, retries interrupted deliveries, and exposes delivery records for inspection and retry. `activity`, `deliveries`, `usage`, and `channels` are read-only control-plane views suitable for an operator dashboard.
@@ -0,0 +1,41 @@
1
+ ---
2
+ title: Usage and budget limits
3
+ description: Keep model, tool, file, and workflow costs predictable.
4
+ ---
5
+
6
+ Budget policies can be set on each run and should also be constrained by the project and user policy in your application.
7
+
8
+ ```ts
9
+ const budget = {
10
+ maxToolCalls: 20,
11
+ maxCost: 0.25,
12
+ };
13
+ ```
14
+
15
+ The server enforces `maxToolCalls` and `maxCost` during the run. A tool-call ceiling produces a resumable boundary with an actionable message; a cost ceiling stops additional tool work. Durable runs also accept `budget.duration` values of `5m`, `30m`, `1h`, `3h`, `6h`, `3d`, or `1w`.
16
+
17
+ Recommended responses:
18
+
19
+ - `402 spend_limit` when a budget is exhausted
20
+ - `429 rate_limited` when request throughput is exceeded
21
+ - `409 policy_violation` when a run requests a disallowed capability
22
+
23
+ Expose usage through the usage endpoint and include bounded cost metadata in run and webhook records. Never trust a client-provided cost value.
24
+
25
+ For asynchronous work, combine the budget with `wait: false`; the QStash-backed task resumes from persisted state after a process restart. Treat the returned task as the source of truth for progress and retry state.
26
+
27
+ ## Inspecting a durable trace
28
+
29
+ Every synchronous SDK run is also checkpointed as a provider-neutral agent run.
30
+ Use the trace endpoint to inspect model turns, tool starts/results, approvals,
31
+ failures, and completion without loading the transcript into the normal run
32
+ response:
33
+
34
+ ```http
35
+ GET /v1/threads/{threadId}/runs/{runId}/trace
36
+ ```
37
+
38
+ Pass `?include_state=true` only when the checkpointed messages and tool results
39
+ are needed for debugging. The trace is owner-scoped, versioned for optimistic
40
+ concurrency, and retained independently from chat history. Worker handoffs use
41
+ the same trace model and expose their `runId` through the worker endpoints.
package/docs/calls.mdx ADDED
@@ -0,0 +1,58 @@
1
+ ---
2
+ title: Calls and live voice
3
+ description: Request approval-gated outbound calls and configure Chusky's live voices.
4
+ ---
5
+
6
+ ## Providers
7
+
8
+ Chusky supports outbound calls through the configured provider: Twilio for the
9
+ private live voice bridge, or Bland when Bland is enabled and fully configured.
10
+ The provider is selected by the Chusky deployment; application code does not
11
+ receive provider credentials.
12
+
13
+ ```ts
14
+ const voices = await chusky.account.voiceOptions();
15
+ console.log(voices.fluxVoices.map((voice) => voice.id), voices.blandVoices);
16
+ const current = await chusky.account.getPreferences();
17
+
18
+ await chusky.account.preferences({
19
+ liveVoice: { provider: "meetings", voice: "flux-haley-en" },
20
+ });
21
+ ```
22
+
23
+ `provider` may be `twilio`, `meetings`, or `bland`. Flux voice options include an
24
+ `id`, display `name`, and accent;
25
+ Bland selections use the `{ id, name }` object returned by `voiceOptions()`.
26
+ Set `voice: null` to restore the deployment default.
27
+
28
+ ## Requesting a call
29
+
30
+ Call creation creates the same exact, one-time approval used by Chusky's other
31
+ surfaces. It does not place the call until an authenticated owner approves it.
32
+
33
+ ```ts
34
+ const approval = await chusky.calls.request({
35
+ phoneNumber: "+14155550123",
36
+ purpose: "Confirm the test-drive appointment and answer final questions.",
37
+ profile: {
38
+ mode: "scheduling",
39
+ tone: "warm",
40
+ capabilities: ["memory_lookup", "schedule_lookup"],
41
+ },
42
+ }, { idempotencyKey: "test-drive-call-2026-09-15" });
43
+
44
+ if (approval.status === "pending") {
45
+ // Show the exact recipient, purpose, and profile to your authenticated owner.
46
+ await chusky.approvals.decide(approval.id, "approve", {
47
+ idempotencyKey: "approve-test-drive-call-2026-09-15",
48
+ });
49
+ }
50
+ ```
51
+
52
+ Use `chusky.calls.list()` to inspect safe call metadata. Phone numbers are not
53
+ used as identity keys, and call transcripts/audio are not exposed through this
54
+ resource.
55
+
56
+ The SDK endpoint requires the `calls:write` scope for requests and
57
+ `calls:read` for listing. On dashboard sessions, the Chusky account must be
58
+ verified and linked to its Telegram workspace.
@@ -0,0 +1,48 @@
1
+ ---
2
+ title: Capabilities, skills, and artifacts
3
+ description: Give runs the right tools and move generated work safely through the platform.
4
+ ---
5
+
6
+ ## Tool and skill discovery
7
+
8
+ Use `tools.list()` to discover the Composio and native capabilities available to a project. Use `skills.list()` to inspect the trusted `.chusky/skills` catalog and `skills.files()` or `skills.readFile()` when a skill references supporting scripts, templates, or assets.
9
+
10
+ ```ts
11
+ const tools = await chusky.tools.list({ query: "Gmail" });
12
+ const skills = await chusky.skills.list();
13
+
14
+ const run = await chusky.threads.runs(thread.id).create({
15
+ input: "Research the customer and prepare a report.",
16
+ tools: { allow: tools.data.slice(0, 5).map((tool) => tool.slug) },
17
+ skills: skills.data.filter((skill) => skill.name === "research").map((skill) => skill.name),
18
+ wait: false,
19
+ budget: { duration: "3h", maxToolCalls: 60, maxCost: 2 },
20
+ });
21
+ ```
22
+
23
+ Allow and deny rules are checked before the model sees the catalog and again immediately before execution. A missing connected account is surfaced as a connection requirement; it does not silently grant a credential.
24
+
25
+ ## Generated artifacts and video jobs
26
+
27
+ Artifacts are verified outputs produced in the Daytona workspace. List them, inspect metadata, and download the bytes through the SDK:
28
+
29
+ ```ts
30
+ const artifacts = await chusky.artifacts.list({ type: "report" });
31
+ const file = await chusky.artifacts.download(artifacts.data[0].id);
32
+ await writeFile("report.pdf", Buffer.from(file.bytes));
33
+ ```
34
+
35
+ Video generation is durable and independently cancellable. Submit a job with `videos.create()`, observe it with `videos.get()`, and deliver the completed artifact through the channel or your own storage policy.
36
+
37
+ ## Workers and channels
38
+
39
+ Workers are durable scheduled routines. `workers.create()` accepts a schedule, task input, budget, and optional channel target. Channels and activity provide the control-plane view needed for dashboards and operational history.
40
+
41
+ ## Voice and meetings
42
+
43
+ Voice calls and Recall meetings are first-class API resources rather than
44
+ provider-specific SDK forks. Use `account.voiceOptions()` and
45
+ `account.preferences()` for the deployment's Flux/Bland choices, `calls.request()`
46
+ for an approval-gated outbound call, and `meetings.prepare()` plus
47
+ `meetings.join()` for a meeting representative. See the [calls guide](/docs/calls)
48
+ and [meetings guide](/docs/meetings) for provider details and lifecycle handling.
@@ -0,0 +1,76 @@
1
+ ---
2
+ title: Company workspaces
3
+ description: Configure shared Chusky agent projects and run governed business workflows.
4
+ ---
5
+
6
+ Company workspaces use Better Auth organizations for membership and invitations. Workspace owners and admins create a company API project in the Chusky dashboard. Members can see project profiles and policy; only owners/admins can create or rotate keys or change policy.
7
+
8
+ OAuth and connected-account lifecycle stay with Composio. Chusky calls its existing Composio integration for toolkit discovery, OAuth consent URLs, account listing, tool execution, and triggers. A company backend connects an app using the same stable Chusky identity it will later use to run the agent:
9
+
10
+ ```ts
11
+ import { Chusky } from "@chusky/sdk";
12
+
13
+ const chusky = new Chusky({
14
+ apiKey: process.env.CHUSKY_API_KEY!,
15
+ userId: "acme-crm-service",
16
+ });
17
+
18
+ const apps = await chusky.account.apps();
19
+ const { url } = await chusky.account.connectApp("GMAIL");
20
+ // Send the URL to an authorized company admin to complete Composio consent.
21
+ ```
22
+
23
+ Use a stable server-side user identifier, not a browser-controlled header or a person's email address. Chusky's thread history, tasks, approvals, and Composio session are scoped to the API project plus that identity. For multi-user products, provide each authenticated end user with their own stable identifier.
24
+
25
+ ## Choose a specialist profile
26
+
27
+ Built-in templates cover Sales Development, Lead Research, Competitive Intelligence, Customer Support, Executive Assistant, Recruiting, and Marketing Operations. Templates include bounded instructions, an allowlist, and default per-run limits. Project policy and saved profiles can further restrict tool use. The server applies the strictest budget and always requires approval before the Composio execution wrappers run.
28
+
29
+ ```ts
30
+ const templates = await chusky.agents.templates();
31
+ const leadAgent = await chusky.agents.create({
32
+ template: "lead-research",
33
+ name: "Fintech lead scout",
34
+ instructions: "Target companies with 50+ employees. Cite the source for headcount and prepare outreach drafts for review.",
35
+ });
36
+ ```
37
+
38
+ User-supplied company criteria are instructions/data for the profile, not tool permissions. A caller cannot expand the project or profile grant in a run request.
39
+
40
+ ## Run an outcome
41
+
42
+ ```ts
43
+ const { thread, run } = await chusky.runs.create({
44
+ input: "Find fintech companies that match our ICP and prepare CRM-ready profiles and outreach drafts.",
45
+ agentId: leadAgent.id,
46
+ wait: false,
47
+ budget: { duration: "30m", maxToolCalls: 20, maxCost: 2 },
48
+ }, { idempotencyKey: "acme-lead-research-2026-09-15" });
49
+
50
+ const latest = await chusky.runs.get(thread.id, run.id);
51
+ ```
52
+
53
+ The run is durable and task-backed when `wait: false` is used with the configured Redis/QStash execution service. Poll run/task status or receive signed webhook events. A human approves pending external actions through the authenticated dashboard or a separately authorized application. Company-project keys have approval-read scope, not approval-write scope, by default; the MCP server also cannot approve its own actions.
54
+
55
+ ## Read company-wide outcomes
56
+
57
+ Run content and Composio sessions stay scoped to each stable caller identity. Workspace admins can still monitor shared execution status and spend without exposing prompts or outputs:
58
+
59
+ ```ts
60
+ const [runs, events, usage] = await Promise.all([
61
+ chusky.company.runs({ limit: 20 }),
62
+ chusky.company.audit(),
63
+ chusky.company.usage(),
64
+ ]);
65
+
66
+ console.log(runs.data.map(({ id, status, agentName }) => ({ id, status, agentName })));
67
+ console.log(usage.currentMonth.completedRuns, usage.currentMonth.costUsd);
68
+ ```
69
+
70
+ These reads require the project key's `company:read` scope. The dashboard shows the same project-scoped view to workspace owners/admins. Audit entries contain bounded action paths, status, request IDs, and timestamps—not request bodies. Successful durable completion is counted once per project/run even if a workflow completion is retried.
71
+
72
+ ## Integrate a company system
73
+
74
+ Use the SDK in a trusted backend, call the versioned REST API directly, or connect the Cloudflare Streamable HTTP MCP server at `/mcp`. MCP clients pass the scoped `chsk_` key and the same stable `X-Chusky-User-Id` header. See [`cloudflare/chusky-mcp`](../../cloudflare/chusky-mcp/README.md) for tools and deployment configuration.
75
+
76
+ Use existing Composio triggers or Chusky's scheduling facilities for event-driven and delayed work. Make the business instruction explicit—for example, draft a follow-up after three days—and keep sending gated by human approval. The standard agent profile does not itself create a prospect-facing recurring schedule.
@@ -0,0 +1,76 @@
1
+ ---
2
+ title: Embedded chat
3
+ description: Add a safe customer-facing Chusky chat without exposing project credentials.
4
+ ---
5
+
6
+ The widget is a browser UI only. It never talks to Chusky with a project key.
7
+ Your server owns the key, authenticates the visitor, chooses the stable Chusky
8
+ user ID, and proxies the stream.
9
+
10
+ ## Add the widget
11
+
12
+ ```html
13
+ <script type="module">
14
+ import { defineChuskyChat } from "@chusky/sdk/widget";
15
+ defineChuskyChat();
16
+ </script>
17
+ <chusky-chat
18
+ endpoint="/api/chusky/chat"
19
+ title="Acme assistant"
20
+ accent-color="#111111"
21
+ ></chusky-chat>
22
+ ```
23
+
24
+ The element renders an accessible launcher, message list, loading state, retry
25
+ state, and approval-pending notice. It accepts bounded NDJSON events with
26
+ `run.delta`, `run.completed`, `run.requires_approval`, and `error` types.
27
+
28
+ ## Server route contract
29
+
30
+ `POST /api/chusky/chat` receives:
31
+
32
+ ```json
33
+ { "message": "Prepare a sales brief", "conversationId": "optional-client-id" }
34
+ ```
35
+
36
+ The route should:
37
+
38
+ 1. Authenticate the visitor using your own session.
39
+ 2. Derive a stable, non-PII customer identifier.
40
+ 3. Create or recover a Chusky thread for that customer.
41
+ 4. Call `threads.runs.stream()` with the server-side project key.
42
+ 5. Forward only bounded run events; never forward raw tool arguments, provider
43
+ payloads, prompts, tokens, or private account details.
44
+
45
+ The route must reject missing sessions, cross-customer conversation IDs,
46
+ oversized messages, and cross-origin requests. Add your own rate limit and
47
+ CSRF/origin policy.
48
+
49
+ ## Example server flow
50
+
51
+ ```ts
52
+ const chusky = new Chusky({
53
+ apiKey: process.env.CHUSKY_API_KEY!,
54
+ userId: `customer_${session.accountId}`,
55
+ });
56
+
57
+ const thread = await chusky.threads.create({ source: "embedded-chat" });
58
+ const stream = chusky.threads.runs(thread.id).stream({ input: message });
59
+ // Convert the SDK events to NDJSON and return a streaming Response.
60
+ ```
61
+
62
+ For durable business work, use `runs.create({ wait: false })` instead of the
63
+ interactive stream and show the widget a status link or task progress.
64
+
65
+ ## Approval behavior
66
+
67
+ The widget cannot approve an external action. When Chusky emits
68
+ `run.requires_approval`, show a neutral waiting state and direct the customer
69
+ to the authenticated company console or your own approved operator workflow.
70
+
71
+ ## Domain and branding
72
+
73
+ Workspace branding is configured by an owner/admin in Organizations. Custom
74
+ domains require provider-side DNS/TLS routing; Chusky reports `pending_dns`
75
+ until the operator has completed that step. Branding changes do not grant API
76
+ access and do not change project scopes.
@@ -0,0 +1,23 @@
1
+ ---
2
+ title: Fallback models
3
+ description: Keep runs available when a model is unavailable or unsuitable.
4
+ ---
5
+
6
+ Fallbacks are an ordered list of models used only for retryable failures such as provider outages, rate limits, or unsupported input modalities.
7
+
8
+ ```ts
9
+ // Planned SDK shape
10
+ const chusky = new Chusky({
11
+ apiKey: process.env.CHUSKY_API_KEY!,
12
+ userId: "customer_123",
13
+ model: "openai/gpt-5.6-luna",
14
+ fallbackModels: [
15
+ "deepseek/deepseek-v4-flash",
16
+ "anthropic/claude-sonnet-4",
17
+ ],
18
+ });
19
+ ```
20
+
21
+ Fallbacks must not retry invalid input, denied permissions, expired approvals, or destructive tool calls. The final selected model should be recorded in the run and webhook metadata. A fallback must preserve the same conversation and idempotency context.
22
+
23
+ > **Status:** Planned public API. The current SDK supports a configured model and per-run model override; ordered fallback execution is not yet exposed.
package/docs/files.mdx CHANGED
@@ -19,6 +19,13 @@ await fetch(intent.uploadUrl, {
19
19
  });
20
20
 
21
21
  const verified = await chusky.files.complete(intent.id);
22
+
23
+ // For Node.js Buffers, Uint8Arrays, or browser Blobs:
24
+ const uploaded = await chusky.files.upload({
25
+ name: "logo.png",
26
+ contentType: "image/png",
27
+ bytes,
28
+ });
22
29
  ```
23
30
 
24
31
  Only verified files can be attached or downloaded. Upload URLs expire quickly; do not persist them. The backend enforces type, size, ownership, and content-type verification.
package/docs/index.mdx CHANGED
@@ -17,6 +17,9 @@ Install the SDK, create a thread, and stream the first response.
17
17
  - Authenticated chat products with streaming responses
18
18
  - Human-in-the-loop workflows for sending, deleting, publishing, or changing data
19
19
  - File-aware assistants using verified Cloudflare R2 uploads
20
+ - Company workspaces with specialist agent templates and project-scoped run policies
21
+ - Voice workflows through Twilio or Bland, with deployment-supported voice selection
22
+ - Recall meeting representatives for Zoom, Google Meet, Microsoft Teams, and Webex
20
23
 
21
24
  ## The execution model
22
25