ql-agent-sdk 0.0.0-stage → 1.0.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/README.md +246 -2
- package/dist/index.cjs +516 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +416 -0
- package/dist/index.d.ts +416 -0
- package/dist/index.js +493 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -4
package/README.md
CHANGED
|
@@ -1,3 +1,247 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ql-agent-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript SDK for the **Quanton Labs** agent platform API (v1). Integrates your application with the 8 specialized agents, capabilities, workflows, and background tasks. Zero runtime dependencies — built on native `fetch` and Web Streams.
|
|
4
|
+
|
|
5
|
+
> Base URL defaults to `https://app.quantonlabs.com`. Override with `baseUrl` or the `QL_BASE_URL` env var.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install ql-agent-sdk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add ql-agent-sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires **Node.js 18+** (native `fetch`). Works in Node, Bun, Deno, and browser/edge runtimes that provide `fetch` and Web Streams.
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { QLClient } from "ql-agent-sdk";
|
|
21
|
+
|
|
22
|
+
const ql = new QLClient({ apiKey: process.env.QL_API_KEY! });
|
|
23
|
+
|
|
24
|
+
// Discover agents
|
|
25
|
+
const { agents } = await ql.agents.list();
|
|
26
|
+
console.log(agents.map((a) => a.id));
|
|
27
|
+
|
|
28
|
+
// Chat with an agent (non-streaming)
|
|
29
|
+
const reply = await ql.agents.chat({
|
|
30
|
+
agentId: "governing",
|
|
31
|
+
messages: [{ role: "user", content: "Summarize this quarter's revenue." }],
|
|
32
|
+
});
|
|
33
|
+
console.log(reply.content);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Configuration
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
new QLClient({
|
|
40
|
+
apiKey, // required — your Bearer API key (never logged/exposed)
|
|
41
|
+
baseUrl, // optional — defaults to QL_BASE_URL or https://app.quantonlabs.com
|
|
42
|
+
fetch: customFetch, // optional — override for tests/proxies/custom runtimes
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`apiKey` is held in a private field and is never returned by the client, included in errors, or logged.
|
|
47
|
+
|
|
48
|
+
## API reference
|
|
49
|
+
|
|
50
|
+
### Workspace
|
|
51
|
+
```ts
|
|
52
|
+
const ws = await ql.workspace.get(); // -> { _id, name, plan, createdAt, updatedAt }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Agents
|
|
56
|
+
```ts
|
|
57
|
+
const { agents } = await ql.agents.list(); // AgentListResult
|
|
58
|
+
const detail = await ql.agents.get("finance"); // AgentDetail (agent + capabilities)
|
|
59
|
+
const caps = await ql.agents.capabilities("finance"); // CapabilitiesResult
|
|
60
|
+
|
|
61
|
+
// Run a capability ("feature"). The result is a discriminated union:
|
|
62
|
+
const { result } = await ql.agents.runCapability("finance", {
|
|
63
|
+
capId: "invoice",
|
|
64
|
+
params: { data: "Revenue | 12000\nCosts | 4000" },
|
|
65
|
+
});
|
|
66
|
+
if (result.type === "html") render(result.html);
|
|
67
|
+
else if (result.type === "json") use(result.data);
|
|
68
|
+
else if (result.type === "text") print(result.content);
|
|
69
|
+
// result.type === "file" -> { url, filename, mime }
|
|
70
|
+
|
|
71
|
+
// Chat (non-streaming)
|
|
72
|
+
const out = await ql.agents.chat({ agentId: "marketing", messages: [...] }); // ChatResult
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Streaming chat (SSE)
|
|
76
|
+
`agents.chatStream` returns an **async iterable** of events. Stop it with an `AbortController`.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { QLClient } from "ql-agent-sdk";
|
|
80
|
+
|
|
81
|
+
const ql = new QLClient({ apiKey: process.env.QL_API_KEY! });
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
|
|
84
|
+
// e.g. abort after 30s or on user action
|
|
85
|
+
const timer = setTimeout(() => controller.abort(), 30_000);
|
|
86
|
+
|
|
87
|
+
let full = "";
|
|
88
|
+
try {
|
|
89
|
+
for await (const ev of ql.agents.chatStream(
|
|
90
|
+
{ agentId: "governing", messages: [{ role: "user", content: "Hello" }] },
|
|
91
|
+
{ signal: controller.signal },
|
|
92
|
+
)) {
|
|
93
|
+
if (ev.type === "delta") {
|
|
94
|
+
process.stdout.write(ev.content ?? ""); // incremental text
|
|
95
|
+
full += ev.content ?? "";
|
|
96
|
+
} else if (ev.type === "done") {
|
|
97
|
+
full = ev.content; // final accumulated text
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch (err) {
|
|
101
|
+
if ((err as any)?.code === "aborted") console.log("stopped by user");
|
|
102
|
+
} finally {
|
|
103
|
+
clearTimeout(timer);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The stream is robust to chunk boundaries, CRLF/LF endings, multiple events per chunk, a final event without a trailing newline, `[DONE]`, and malformed JSON (skipped).
|
|
108
|
+
|
|
109
|
+
### History / pagination
|
|
110
|
+
`agents.history` returns newest-first pages. Walk them with the `nextBefore` cursor until it is `null`.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
const ids: string[] = [];
|
|
114
|
+
let before: string | undefined;
|
|
115
|
+
do {
|
|
116
|
+
const page = await ql.agents.history("finance", { limit: 50, before });
|
|
117
|
+
for (const m of page.items) ids.push(m.id);
|
|
118
|
+
before = page.nextBefore ?? undefined; // null when exhausted
|
|
119
|
+
} while (before);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Workflows
|
|
123
|
+
```ts
|
|
124
|
+
const start = await ql.workflows.run("6adf929d6f87c9ab86f32c19", { input: { a: 1 } }); // 202 -> { runId, status: "running" }
|
|
125
|
+
const run = await ql.workflows.getRun(start.runId); // poll -> WorkflowRun (status/output/error/durationMs/...)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Tasks (background)
|
|
129
|
+
```ts
|
|
130
|
+
const created = await ql.tasks.create({ agentId: "finance", title: "Monthly report", priority: "high" }); // 201 -> { taskId, status }
|
|
131
|
+
const handle = ql.tasks.handle(created.taskId);
|
|
132
|
+
|
|
133
|
+
// One-shot poll
|
|
134
|
+
const snapshot = await handle.poll();
|
|
135
|
+
|
|
136
|
+
// Wait until terminal (completed/failed/cancelled) with backoff + timeout + cancellation
|
|
137
|
+
const done = await handle.wait({
|
|
138
|
+
intervalMs: 2000, // initial delay
|
|
139
|
+
maxIntervalMs: 15000, // backoff cap
|
|
140
|
+
timeoutMs: 5 * 60_000, // throw QLError(code="timeout") if exceeded
|
|
141
|
+
signal: controller.signal, // throw QLError(code="aborted") if aborted
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
> **Task execution limitation:** `tasks.create` only **enqueues** a task. It may remain in the `queued` state until an existing worker/execution mechanism on the backend processes it — creating a task via this SDK does **not** trigger execution. `wait()` simply polls until the task reaches a terminal status (or you time out / abort).
|
|
146
|
+
|
|
147
|
+
## Errors
|
|
148
|
+
|
|
149
|
+
All failures throw a typed `QLError`:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { QLClient, QLError } from "ql-agent-sdk";
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
await ql.agents.chat({ ... });
|
|
156
|
+
} catch (err) {
|
|
157
|
+
if (err instanceof QLError) {
|
|
158
|
+
err.status; // HTTP status (401/403/404/409/400/500) when from an HTTP response
|
|
159
|
+
err.code; // stable code: unauthorized | forbidden | not_found | conflict |
|
|
160
|
+
// validation_error | internal_error | network | aborted | timeout
|
|
161
|
+
err.message; // human-readable (never includes the API key)
|
|
162
|
+
err.isAuthError / err.isAborted / err.isTimeout / err.isNetworkError
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
| HTTP status | `code` | Meaning |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| 401 | `unauthorized` | missing/invalid/revoked API key |
|
|
170
|
+
| 403 | `forbidden` | key lacks the required scope |
|
|
171
|
+
| 404 | `not_found` | resource not found in this workspace |
|
|
172
|
+
| 409 | `conflict` | conflict (e.g. duplicate) |
|
|
173
|
+
| 400 | `validation_error` | bad request / invalid params |
|
|
174
|
+
| 5xx | `internal_error` | server error |
|
|
175
|
+
|
|
176
|
+
Transport-level failures map to `network` (no HTTP response), `aborted` (caller aborted), and `timeout` (`wait()` exceeded its budget).
|
|
177
|
+
|
|
178
|
+
## TypeScript
|
|
179
|
+
|
|
180
|
+
The package ships full type declarations (`dist/index.d.ts` / `.d.cts`). All request/response types are exported:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import type {
|
|
184
|
+
QLClientOptions, ChatInput, ChatResult, ChatStreamEvent,
|
|
185
|
+
AgentSummary, CapabilityInfo, CapabilityResult, HistoryPage,
|
|
186
|
+
WorkflowRun, Task, TaskCreateInput, WaitOptions, QLError,
|
|
187
|
+
} from "ql-agent-sdk";
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Using it in a Next.js App Router (server-side)
|
|
191
|
+
|
|
192
|
+
In a **Route Handler** (`app/api/.../route.ts`) or a server Component — the SDK is safe to use on the server:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
// app/api/agent/route.ts
|
|
196
|
+
import { QLClient, QLError } from "ql-agent-sdk";
|
|
197
|
+
|
|
198
|
+
export const runtime = "nodejs"; // uses native fetch + Web Streams
|
|
199
|
+
|
|
200
|
+
const ql = new QLClient({ apiKey: process.env.QL_API_KEY! });
|
|
201
|
+
|
|
202
|
+
export async function POST(req: Request) {
|
|
203
|
+
try {
|
|
204
|
+
const { prompt } = await req.json();
|
|
205
|
+
const res = await ql.agents.chat({
|
|
206
|
+
agentId: "governing",
|
|
207
|
+
messages: [{ role: "user", content: prompt }],
|
|
208
|
+
});
|
|
209
|
+
return Response.json({ ok: true, content: res.content });
|
|
210
|
+
} catch (err) {
|
|
211
|
+
if (err instanceof QLError) {
|
|
212
|
+
return Response.json({ ok: false, code: err.code, message: err.message }, { status: err.status ?? 500 });
|
|
213
|
+
}
|
|
214
|
+
throw err;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
For **streaming** to the browser from a Route Handler, forward the SDK's events as an SSE `ReadableStream` (see the streaming example above for consuming them).
|
|
220
|
+
|
|
221
|
+
## Development
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
npm install
|
|
225
|
+
npm run typecheck # tsc --noEmit
|
|
226
|
+
npm run lint # eslint (typescript-eslint)
|
|
227
|
+
npm test # vitest (unit: fetch, errors, SSE, polling, timeout, cancellation, pagination)
|
|
228
|
+
npm run build # tsup -> dist (ESM + CJS + .d.ts)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Publishing
|
|
232
|
+
|
|
233
|
+
This package is **not** published. To publish when ready (requires an npm account/org):
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
npm login
|
|
237
|
+
# from packages/sdk:
|
|
238
|
+
npm run build
|
|
239
|
+
npm publish --access public # or --access restricted for a scoped/owned package
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
To install from a local tarball instead of publishing:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
cd packages/sdk && npm pack # -> ql-agent-sdk-0.1.0.tgz
|
|
246
|
+
npm install /absolute/path/to/ql-agent-sdk-0.1.0.tgz
|
|
247
|
+
```
|