@zoowork-ai/sdk 0.8.0 → 0.10.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/CHANGELOG.md +54 -2
- package/README.md +140 -44
- package/dist/client.d.ts +46 -20
- package/dist/client.js +14 -7
- package/dist/developer-api.d.ts +200 -0
- package/dist/developer-api.js +42 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/secret-redaction.d.ts +2 -0
- package/dist/secret-redaction.js +10 -0
- package/dist/sse.js +39 -29
- package/dist/webhooks.d.ts +430 -0
- package/dist/webhooks.js +410 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,59 @@
|
|
|
3
3
|
All notable changes to `@zoowork-ai/sdk` (formerly `@zooclaw-agents/sdk`). Dates are the
|
|
4
4
|
day the behaviour was verified, not the day it was written.
|
|
5
5
|
|
|
6
|
+
## 0.10.0 — 2026-10-03
|
|
7
|
+
|
|
8
|
+
- Add Files, read-only Agent Database, Usage, Run Output, action paging/detail and Agent webhook management helpers.
|
|
9
|
+
- Add typed MCP confirmation, active Session creation and configuration concurrency metadata.
|
|
10
|
+
- Align onboarding and capability examples with Platform Project keys.
|
|
11
|
+
|
|
12
|
+
### Known service and documentation limitations
|
|
13
|
+
|
|
14
|
+
Verified against production on 2026-10-03; these are not resolved by this SDK release:
|
|
15
|
+
|
|
16
|
+
- Workspace Files read/write APIs return HTTP 502 with both Platform and legacy keys ([ecap #4006](https://github.com/SerendipityOneInc/ecap-workspace/issues/4006)).
|
|
17
|
+
- Legacy keys cannot update or remove visible global Skill assignments on their own Agents; Platform keys succeed ([ecap #4007](https://github.com/SerendipityOneInc/ecap-workspace/issues/4007)).
|
|
18
|
+
- Published Authentication URLs redirect to Quickstart, including the AI Markdown entry ([docs #56](https://github.com/SerendipityOneInc/zoowork-agents-docs/issues/56)).
|
|
19
|
+
- Agent Database reads depend on deployment support. The production viewer was disabled, so its successful read path has not been validated.
|
|
20
|
+
|
|
21
|
+
## 0.9.0 — 2026-10-01
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **Webhook receiving.** `verifyWebhookSignature` and `unwrapWebhook` check a delivery's Standard
|
|
26
|
+
Webhooks signature over the raw body and hand back the envelope; `signWebhook` is exported so a
|
|
27
|
+
receiver can build a delivery for its own tests. The HMAC is WebCrypto, so the zero-runtime-
|
|
28
|
+
dependency guarantee holds and the functions are `async`. Headers can come from a fetch
|
|
29
|
+
`Headers`, a record in any casing, or Node's `IncomingHttpHeaders`. Failures throw
|
|
30
|
+
`ZooworkWebhookError` with a machine-readable `code` and a message that carries no secret,
|
|
31
|
+
signature or body.
|
|
32
|
+
- **Rotation without a deploy.** `ZOOWORK_WEBHOOK_SECRET` accepts several secrets separated by
|
|
33
|
+
whitespace or commas, and a delivery signed under any of them verifies — the sender double-signs
|
|
34
|
+
for the length of the window. The Python SDK reads the variable identically, so one deployment's
|
|
35
|
+
configuration serves both. A `secret` passed explicitly is never split.
|
|
36
|
+
- **The webhook event vocabulary.** `WEBHOOK_EVENT_TYPES` lists the sixteen types Engine
|
|
37
|
+
delivers today, with a `data` shape per type behind `WebhookEventFor<T>` and
|
|
38
|
+
`knownWebhookEvent()`. `WEBHOOK_SCHEDULE_CONFIG_EVENT_TYPES` declares the five schedule
|
|
39
|
+
configuration events for schedule configuration changes. A type the release does not know
|
|
40
|
+
passes through with its raw `type` and `data` instead of throwing, so a receiver stays
|
|
41
|
+
forward compatible.
|
|
42
|
+
- Fixed cross-language signing vectors in `src/__vectors__/webhook-vectors.json`, copied verbatim
|
|
43
|
+
from Engine's `packages/webhook-signing` and asserted three ways (an independent WebCrypto
|
|
44
|
+
computation, the recorded JSON, and this SDK). They are the compatibility contract with the
|
|
45
|
+
official `standardwebhooks` libraries; adding a vector is fine, editing one is not.
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- `streamEvents()` cancels and releases its response reader when a consumer exits early,
|
|
50
|
+
including a loop that stops at `run.finished`. This prevents an open SSE connection from
|
|
51
|
+
keeping a Node.js consumer alive after its turn has completed.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- Local E2E checks accept Platform Project keys. Production checks require an explicit URL
|
|
56
|
+
and confirmation, and record their scope separately from staging checks.
|
|
57
|
+
- Live smoke tests select only selectable catalog models.
|
|
58
|
+
|
|
6
59
|
## 0.8.0 — 2026-09-20
|
|
7
60
|
|
|
8
61
|
### Added
|
|
@@ -179,8 +232,7 @@ No runtime change — comments only.
|
|
|
179
232
|
- `createZooclawClient` → `createZooworkClient`; `ZooclawClient`, `ZooclawError`,
|
|
180
233
|
`ZooclawAuth`, `ZooclawConfig` → `Zoowork*`.
|
|
181
234
|
- `ZOOCLAW_API_KEY` / `ZOOCLAW_BASE_URL` → `ZOOWORK_API_KEY` / `ZOOWORK_BASE_URL`.
|
|
182
|
-
- Server-side identifiers
|
|
183
|
-
environment names the API returns (e.g. `zooclaw-tts`) are whatever the server says.
|
|
235
|
+
- Server-side identifiers and resource names remain opaque.
|
|
184
236
|
|
|
185
237
|
## 0.3.4 — 2026-08-25
|
|
186
238
|
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npm install @zoowork-ai/sdk
|
|
|
10
10
|
|
|
11
11
|
## Quickstart
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Create a Project API key in [ZooWork Platform](https://platform.zoowork.ai). The secret is shown once. Save it as `ZOOWORK_API_KEY` on your server. Agent and Session access is scoped to the key's Project. Initialize Organization billing and bind owner credentials; sign in and rebind when the API requests it.
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
16
|
import { createZooworkClient } from '@zoowork-ai/sdk'
|
|
@@ -66,12 +66,6 @@ separately.
|
|
|
66
66
|
|
|
67
67
|
An explicit option always beats the environment variable.
|
|
68
68
|
|
|
69
|
-
> **Finding the agent you built in the app.** The first path segment of a ZooWork chat URL
|
|
70
|
-
> (`/chat/<32-hex>/sessions/…`) is a *workspace* id, not an `agt_…`. Resolve it with
|
|
71
|
-
> `zc.listAgents({ labels: { workspace_id: '<32-hex>' } })`; use the pagination patterns
|
|
72
|
-
> below to read one page or traverse every match. Scope is `owner_uid AND org_id` — an agent a *colleague*
|
|
73
|
-
> created in your org is fetchable by id but will not appear in your list.
|
|
74
|
-
|
|
75
69
|
> **Wait on `status.desired_state`, never on `status.actual_state`.**
|
|
76
70
|
> `actual_state` reports chat-channel connectivity. An API-only agent has no channels,
|
|
77
71
|
> but its projection depends on the channel-status capability: a GET can report `active`
|
|
@@ -153,30 +147,9 @@ must authenticate the user and authorize the session; `actor.ref` does neither.
|
|
|
153
147
|
isolate the shared agent's sandbox files or erase a session's previous context. Omit `actor`
|
|
154
148
|
to use the owner; IM sessions reject it. See the field's SDK comment for input constraints.
|
|
155
149
|
|
|
156
|
-
##
|
|
157
|
-
|
|
158
|
-
A skill is a zip. One upload creates the skill *and* its first version; `putAgentSkill` attaches it.
|
|
159
|
-
|
|
160
|
-
```ts
|
|
161
|
-
import { readFile } from 'node:fs/promises'
|
|
162
|
-
|
|
163
|
-
const skill = await zc.uploadSkill(await readFile('market-research.zip'), { scope: 'org' })
|
|
164
|
-
await zc.putAgentSkill(agent.agent_id, skill.skill_id)
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
The zip's single top-level directory must be named exactly like the `name` in its `SKILL.md`
|
|
168
|
-
frontmatter — `market-research/SKILL.md` declaring `name: market-research`. A mismatch is a 400,
|
|
169
|
-
and it is the first one nearly everyone gets. `scope` is `org` or `personal`; the preinstalled
|
|
170
|
-
`global` skills are listable but not installable with an API key, so this is the only way to
|
|
171
|
-
control what a skill says. `uploadSkillVersion` publishes an update, and agents that installed it
|
|
172
|
-
unpinned follow along without another `putAgentSkill`.
|
|
150
|
+
## Agent Skills
|
|
173
151
|
|
|
174
|
-
`
|
|
175
|
-
`latest_version` and `status` of the `SkillRecord` returned by `uploadSkill`. This return
|
|
176
|
-
contract is source-reviewed, not a new live recording. On initial create, put the description
|
|
177
|
-
in the zip's frontmatter: the gateway drops the `description` option. Version uploads can
|
|
178
|
-
use that override. A successful create retried under the same name can return `409 skill_exists`;
|
|
179
|
-
read back first. Version uploads deduplicate identical content for the same skill, not HTTP keys.
|
|
152
|
+
New Agents receive global Skills by default. Use `listAgentSkills(agentId)` to inspect attached Skills. At create time, `resource.skills` accepts a catalog name or `skill_id`; `include_global_skills: false` or an explicit empty list opts out of automatic Skills. Root Skill uploads are unavailable to Project keys. Keep source changes in your application or the Agent workspace through the Files API.
|
|
180
153
|
|
|
181
154
|
## Schedules, wake and exec
|
|
182
155
|
|
|
@@ -247,20 +220,7 @@ Approval response fields are source-reviewed, not end-to-end verified: read `req
|
|
|
247
220
|
`allowed_decisions` and optional timeout/resolution fields defensively. `signaled: true` means
|
|
248
221
|
the resolution was accepted; a returned `status: 'pending'` is not completed execution.
|
|
249
222
|
|
|
250
|
-
|
|
251
|
-
`getEnvironmentVersion` and `archiveEnvironment` manage prebuilt sandbox images (apt/npm/pip
|
|
252
|
-
packages, files, a build script, and an outbound allowlist). Two facts worth having before you
|
|
253
|
-
start: an agent's Environment **freezes on its first sandbox creation** — after that every change
|
|
254
|
-
is `409 environment_locked`, and stopping the agent does not clear it — and sandbox networking
|
|
255
|
-
defaults to unrestricted unless the Environment declares `networking: { type: 'limited' }`.
|
|
256
|
-
|
|
257
|
-
Build polling must have a deadline and handle `partial_ready`: some resource classes can be
|
|
258
|
-
ready while others are building or failed. `getEnvironmentVersion(id, version, { resourceClass:
|
|
259
|
-
'starter' })` selects one class; omitting the option keeps the aggregate read. Re-read the
|
|
260
|
-
aggregate before concluding the build is fully ready. These details are source-reviewed.
|
|
261
|
-
|
|
262
|
-
Channel callers must not use `allow_from` as an access-control list: the public gateway ignores
|
|
263
|
-
it. Use supported `dm_policy` settings.
|
|
223
|
+
Platform uses its managed Environment. Root Environment administration and Channel binding return `404 service_api.not_found` for Project keys. An exported method does not expand a key's permissions.
|
|
264
224
|
|
|
265
225
|
## Artifacts and the system prompt
|
|
266
226
|
|
|
@@ -285,6 +245,106 @@ activation on its own — moving it is one explicit call, `upgradeSystemPrompt`,
|
|
|
285
245
|
the agent's current `config_version` as a CAS (`409 config_version_changed` on a stale one)
|
|
286
246
|
and answers the new pin plus the version bump it cost.
|
|
287
247
|
|
|
248
|
+
## Receiving webhooks
|
|
249
|
+
|
|
250
|
+
Verification needs the **raw request bytes**. `express.json()` and most framework body parsers
|
|
251
|
+
consume them, and a `JSON.parse` → `JSON.stringify` round trip does not reproduce them byte for
|
|
252
|
+
byte, so read the body before anything else touches it. The example requires your
|
|
253
|
+
application to supply `acceptOnce`, an atomic durable insert plus work-item enqueue, before it
|
|
254
|
+
returns 204. A worker processes that item separately:
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
import { createServer } from 'node:http'
|
|
258
|
+
import { knownWebhookEvent, unwrapWebhook, ZooworkWebhookError } from '@zoowork-ai/sdk'
|
|
259
|
+
|
|
260
|
+
createServer(async (req, res) => {
|
|
261
|
+
const chunks: Buffer[] = []
|
|
262
|
+
for await (const chunk of req) chunks.push(chunk as Buffer)
|
|
263
|
+
const rawBody = Buffer.concat(chunks)
|
|
264
|
+
|
|
265
|
+
let event
|
|
266
|
+
try {
|
|
267
|
+
// Defaults to ZOOWORK_WEBHOOK_SECRET, or pass `secret: [newest, previous]` explicitly.
|
|
268
|
+
event = await unwrapWebhook({ headers: req.headers, rawBody })
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (error instanceof ZooworkWebhookError) {
|
|
271
|
+
console.warn('rejected webhook', error.code) // never carries the secret, signature or body
|
|
272
|
+
res.writeHead(400).end()
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
throw error
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Validate identity, then atomically persist the event and a durable work item.
|
|
279
|
+
// Implement acceptOnce with a unique webhook-id; a duplicate is successful too.
|
|
280
|
+
if (event.id !== req.headers['webhook-id']) { res.writeHead(400).end(); return }
|
|
281
|
+
try { await acceptOnce(event.id, event) } catch { res.writeHead(503).end(); return }
|
|
282
|
+
res.writeHead(204).end()
|
|
283
|
+
|
|
284
|
+
// Dispatch this switch in the durable worker.
|
|
285
|
+
const known = knownWebhookEvent(event)
|
|
286
|
+
if (!known) return // keep unknown events accepted without side effects
|
|
287
|
+
switch (known.type) {
|
|
288
|
+
case 'run.finished':
|
|
289
|
+
console.log(known.data.run_id, known.data.status)
|
|
290
|
+
break
|
|
291
|
+
case 'approval.requested':
|
|
292
|
+
console.log(known.data.approval_id, known.data.tool_name)
|
|
293
|
+
break
|
|
294
|
+
}
|
|
295
|
+
}).listen(3000)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
`verifyWebhookSignature` is the same check without the parse, for a handler that wants the bytes.
|
|
299
|
+
Both are `async`: the HMAC is WebCrypto (`crypto.subtle`), which is how this SDK verifies
|
|
300
|
+
signatures with no runtime dependency and still runs in Workers, Deno and the browser.
|
|
301
|
+
|
|
302
|
+
### Rotating the secret without a deploy
|
|
303
|
+
|
|
304
|
+
`ZOOWORK_WEBHOOK_SECRET` accepts **several secrets**, separated by whitespace or commas. During a
|
|
305
|
+
rotation window the sender signs under every active key, so a receiver that lists both accepts
|
|
306
|
+
whichever one signed a given delivery:
|
|
307
|
+
|
|
308
|
+
```sh
|
|
309
|
+
ZOOWORK_WEBHOOK_SECRET="whsec_<new> whsec_<previous>" # commas work too
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Splitting is unambiguous because a secret is `whsec_` plus standard base64 of 32 bytes — no comma,
|
|
313
|
+
no whitespace. The Python SDK reads the variable the same way, so one deployment's configuration
|
|
314
|
+
serves both. A `secret` you pass explicitly is never split: a string is one secret, and several go
|
|
315
|
+
in an array.
|
|
316
|
+
|
|
317
|
+
Four more things worth knowing:
|
|
318
|
+
|
|
319
|
+
- **The clock window is checked against the `webhook-timestamp` header, not the envelope's
|
|
320
|
+
`created_at`.** A retry of an old event carries a fresh timestamp and verifies; `created_at`
|
|
321
|
+
still says when the fact happened. The default tolerance is ±300s.
|
|
322
|
+
- **A body over 16 KiB is refused before it is hashed** — the sender's own envelope ceiling.
|
|
323
|
+
- **Unknown event types are normal.** New types ship within a schema version, and a receiver that
|
|
324
|
+
fails one only makes the sender retry it and then dead-letter it. Acknowledge and ignore.
|
|
325
|
+
- **A repeated `webhook-id`, `webhook-timestamp` or `webhook-signature` header is rejected**, not
|
|
326
|
+
resolved to one of its values: choosing would be a guess about which send arrived. The Python
|
|
327
|
+
SDK rejects it too.
|
|
328
|
+
- **`unwrapWebhook` checks six fields and no more** — `object`, `id`, `type`, `schema_version`,
|
|
329
|
+
`created_at`, `data` — so a field or event type a later API release adds is not a reason to
|
|
330
|
+
drop a delivery. `schema_version` must be a number with an **integer value**: `1` and `1.0` both
|
|
331
|
+
pass, since JSON has one numeric type and they are the same number, while `1.5` is rejected with
|
|
332
|
+
`invalid_payload`. The Python SDK reaches the same verdict on the same envelope.
|
|
333
|
+
|
|
334
|
+
`ZooworkWebhookError.code` is the contract to match on — `invalid_secret`, `body_too_large`,
|
|
335
|
+
`missing_header`, `invalid_header`, `timestamp_out_of_window`, `signature_mismatch`,
|
|
336
|
+
`invalid_payload` — and those strings are identical in the Python SDK. The class shape is not: here
|
|
337
|
+
it extends `Error`, while the Python SDK makes it a subclass of its own `ZooworkError` carrying a
|
|
338
|
+
400. Match on `code`, and do not port `instanceof` checks between the two.
|
|
339
|
+
|
|
340
|
+
You do not have to use this SDK to verify. ZooWork emits plain
|
|
341
|
+
[Standard Webhooks](https://github.com/standard-webhooks/standard-webhooks), so the official
|
|
342
|
+
library for any language works with the same `whsec_` secret and the same raw body — npm
|
|
343
|
+
[`standardwebhooks`](https://www.npmjs.com/package/standardwebhooks), PyPI
|
|
344
|
+
[`standardwebhooks`](https://pypi.org/project/standardwebhooks/), or another of the ports. The
|
|
345
|
+
fixed vectors in [`src/__vectors__/webhook-vectors.json`](src/__vectors__/webhook-vectors.json)
|
|
346
|
+
are copied verbatim from Engine and are byte-compatible with all of them.
|
|
347
|
+
|
|
288
348
|
## Two helpers
|
|
289
349
|
|
|
290
350
|
```ts
|
|
@@ -342,3 +402,39 @@ before creating the GitHub Release when live verification is required. Use
|
|
|
342
402
|
## License
|
|
343
403
|
|
|
344
404
|
MIT
|
|
405
|
+
|
|
406
|
+
## Developer API helpers
|
|
407
|
+
|
|
408
|
+
These helpers require an SDK release that includes them. Check the installed declarations before using an example; use the documented HTTP endpoint if the installed release lacks the method.
|
|
409
|
+
|
|
410
|
+
```ts
|
|
411
|
+
const files = await zc.getWorkspaceFile(agentId, '/workspace')
|
|
412
|
+
await zc.writeWorkspaceFile(agentId, '/workspace/input.txt', 'hello')
|
|
413
|
+
const bytes = await zc.getWorkspaceFileContent(agentId, '/workspace/result.bin')
|
|
414
|
+
const database = await zc.getAgentDatabase(agentId) // reading never provisions it
|
|
415
|
+
const rows = await zc.getAgentDatabaseRows(agentId, 'results', { limit: 100, offset: 0 })
|
|
416
|
+
const usage = await zc.getUsage({ range: '7d', view: 'both' })
|
|
417
|
+
const endpoint = await zc.createAgentWebhook(agentId, {
|
|
418
|
+
url: 'https://receiver.example/webhook', event_types: ['run.finished'],
|
|
419
|
+
}, 'register-hook-v1')
|
|
420
|
+
// Save signing_secret securely when present; an idempotent replay can return null.
|
|
421
|
+
const hooks = await zc.listAgentWebhooks(agentId) // hooks.webhooks
|
|
422
|
+
const output = await zc.getRunOutput(agentId, sessionId, runId)
|
|
423
|
+
const approvals = await zc.listApprovalPage(agentId, { sessionId })
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
File reads derive `owner_uid` and `org_id` from the Agent projection. Raw file content is
|
|
427
|
+
`Uint8Array`; text writes are not binary uploads. Database inspection is read-only. Usage stays
|
|
428
|
+
within the current key scope. Page helpers retain `next_cursor` and `has_more`; replay cursors
|
|
429
|
+
verbatim. `getApproval` and `getCustomToolCall` read terminal as well as pending actions.
|
|
430
|
+
|
|
431
|
+
Agent webhook methods include get/update/delete, `rotateAgentWebhookSecret`, `testAgentWebhook`,
|
|
432
|
+
`getAgentWebhookEvent`, delivery list/detail and single/batch redelivery. Pass a stable idempotency
|
|
433
|
+
key to create, rotation, test and redelivery. A 202 receipt confirms queuing; query delivery
|
|
434
|
+
records to learn the result. SDK writes do not retry automatically.
|
|
435
|
+
|
|
436
|
+
`createSession` accepts `runtime_mode: 'active'` to pin the current active configuration at
|
|
437
|
+
creation. Omit it to resolve active configuration on later turns. `idle_compaction` preserves
|
|
438
|
+
false, null and omission. MCP tool overrides accept `requireConfirmation?: boolean`.
|
|
439
|
+
`updateAgent` accepts `expected_config_version`; a stale version returns
|
|
440
|
+
`409 active_config_changed`. Ownership-only changes do not increment the configuration version.
|
package/dist/client.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ZooWork Managed Agents SDK — core client. Developer Preview.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* authority, so it is SERVER-SIDE ONLY: never ship it in a browser or mobile bundle.
|
|
4
|
+
* Create a Project API key at https://platform.zoowork.ai. Keep it server-side: never ship it in a browser or mobile bundle.
|
|
6
5
|
*
|
|
7
6
|
* IDs are opaque strings and unknown response fields must be ignored — both are
|
|
8
7
|
* forward-compatibility rules, not suggestions. Errors surface an `error.type`; match on
|
|
9
8
|
* that, never on the message text.
|
|
10
9
|
*/
|
|
10
|
+
import { type DeveloperApi } from './developer-api.js';
|
|
11
11
|
import { type SessionEvent } from './events.js';
|
|
12
12
|
/**
|
|
13
13
|
* The production API base URL — the default. You should not need to set this:
|
|
@@ -22,9 +22,9 @@ export type ZooworkAuth = {
|
|
|
22
22
|
};
|
|
23
23
|
export interface ZooworkConfig {
|
|
24
24
|
/**
|
|
25
|
-
* Your API key (`
|
|
25
|
+
* Your Platform Project API key (`zwp_live_...`). Defaults to `ZOOWORK_API_KEY`.
|
|
26
26
|
*
|
|
27
|
-
* Server-side only:
|
|
27
|
+
* Server-side only: access is scoped to the selected Project. Authorize application users separately.
|
|
28
28
|
*/
|
|
29
29
|
apiKey?: string;
|
|
30
30
|
/**
|
|
@@ -115,6 +115,8 @@ export declare class ZooworkError extends Error {
|
|
|
115
115
|
export interface Ownership {
|
|
116
116
|
owner_uid: string;
|
|
117
117
|
org_id: string;
|
|
118
|
+
project_id?: string | null;
|
|
119
|
+
visibility?: 'private' | 'project';
|
|
118
120
|
}
|
|
119
121
|
export interface ModelInfo {
|
|
120
122
|
model: string;
|
|
@@ -143,6 +145,9 @@ export interface McpContextConfig {
|
|
|
143
145
|
}
|
|
144
146
|
export interface McpToolPermissionOverride {
|
|
145
147
|
permission: McpToolPermission;
|
|
148
|
+
/** True requires permission: 'always_ask' and a decision for each call, even with a persistent grant.
|
|
149
|
+
* Only allow-once or deny is offered. False/omission retains permission policy behavior. */
|
|
150
|
+
requireConfirmation?: boolean;
|
|
146
151
|
}
|
|
147
152
|
/**
|
|
148
153
|
* One remote MCP server, declared as `resource.mcp[]` on create or update.
|
|
@@ -298,10 +303,15 @@ export interface AgentResource {
|
|
|
298
303
|
seed_policy?: string;
|
|
299
304
|
}[];
|
|
300
305
|
};
|
|
301
|
-
skills?: {
|
|
306
|
+
skills?: (({
|
|
302
307
|
skill_id: string;
|
|
308
|
+
name?: string;
|
|
309
|
+
} | {
|
|
310
|
+
name: string;
|
|
311
|
+
skill_id?: string;
|
|
312
|
+
}) & {
|
|
303
313
|
version?: number | 'latest';
|
|
304
|
-
}[];
|
|
314
|
+
})[];
|
|
305
315
|
/**
|
|
306
316
|
* Defaults to true. False disables automatic global Skills without removing explicitly
|
|
307
317
|
* listed Skills. An explicit empty `skills` array also opts out.
|
|
@@ -655,6 +665,7 @@ export interface AgentRecord {
|
|
|
655
665
|
environment_locked_at?: string | null;
|
|
656
666
|
status?: AgentStatus;
|
|
657
667
|
ownership?: Ownership;
|
|
668
|
+
sandbox_resource_class?: string;
|
|
658
669
|
[k: string]: unknown;
|
|
659
670
|
}
|
|
660
671
|
/**
|
|
@@ -725,6 +736,10 @@ export interface SessionRecord {
|
|
|
725
736
|
pending_approvals?: number;
|
|
726
737
|
/** Pending application-executed custom-tool count on getSession. */
|
|
727
738
|
pending_custom_tool_calls?: number;
|
|
739
|
+
pending_approval_ids?: string[];
|
|
740
|
+
pending_approval_ids_complete?: boolean;
|
|
741
|
+
pending_custom_tool_call_ids?: string[];
|
|
742
|
+
pending_custom_tool_call_ids_complete?: boolean;
|
|
728
743
|
/**
|
|
729
744
|
* `running` on a `createSession` receipt, nullable on `getSession`, and absent from
|
|
730
745
|
* `listSessions` rows. This is not the run outcome; read {@link SessionRecord.run_status}
|
|
@@ -735,6 +750,8 @@ export interface SessionRecord {
|
|
|
735
750
|
archived?: boolean;
|
|
736
751
|
/** Present on filtered pages only when `includeDeleted` was requested. */
|
|
737
752
|
deleted?: boolean;
|
|
753
|
+
idle_compaction?: boolean | null;
|
|
754
|
+
pinned_config_version?: number | null;
|
|
738
755
|
runtime_mode?: 'active' | 'preview' | 'authoring' | 'evaluation' | string;
|
|
739
756
|
config_version?: number;
|
|
740
757
|
updated_at?: string;
|
|
@@ -940,12 +957,13 @@ export interface ScheduleUpdate {
|
|
|
940
957
|
* - `scheduleId` is the FULLY-QUALIFIED name `cron/{computer_id}/{agent_id}/{schedule_id}`, not
|
|
941
958
|
* the id you chose. Your id is `name`.
|
|
942
959
|
*
|
|
943
|
-
*
|
|
944
|
-
* `schedule_name`; `GET /schedules/{id}` answers the camelCase projection below; `GET /schedules`
|
|
960
|
+
* Create receipts include the public `schedule_id` and snake_case `schedule_name`; `GET /schedules/{id}` answers the camelCase projection below; `GET /schedules`
|
|
945
961
|
* answers the raw Temporal describe (`spec` / `state` / `memo` / `next_action_times`) with the
|
|
946
962
|
* camelCase projection merged on top. Read defensively and match on what you find.
|
|
947
963
|
*/
|
|
948
964
|
export interface ScheduleRecord {
|
|
965
|
+
/** Public opaque id, also present on create receipts. */
|
|
966
|
+
schedule_id?: string;
|
|
949
967
|
/** FULLY-QUALIFIED: `cron/{computer_id}/{agent_id}/{schedule_id}`. Not the id you passed in. */
|
|
950
968
|
scheduleId?: string;
|
|
951
969
|
/** The `schedule_id` you chose. This is the one you pass back to get/update/delete. */
|
|
@@ -1016,6 +1034,9 @@ export interface ScheduleRecord {
|
|
|
1016
1034
|
export interface ScheduleRun {
|
|
1017
1035
|
/** Which projection this row came from. Decides which of the field groups below is populated. */
|
|
1018
1036
|
source?: 'temporal' | 'run_projection' | string;
|
|
1037
|
+
linked_by?: string;
|
|
1038
|
+
run_id?: string;
|
|
1039
|
+
trigger?: string;
|
|
1019
1040
|
/** Present only when a session can be associated with this fire. */
|
|
1020
1041
|
session_id?: string;
|
|
1021
1042
|
/** Un-qualified `schedule_id`. */
|
|
@@ -1043,6 +1064,9 @@ export type ApprovalDecision = 'allow-once' | 'allow-always' | 'deny';
|
|
|
1043
1064
|
* completion of the tool or run. Verify the resulting state separately.
|
|
1044
1065
|
*/
|
|
1045
1066
|
export interface ApprovalRecord {
|
|
1067
|
+
run_id?: string;
|
|
1068
|
+
tool_call_id?: string;
|
|
1069
|
+
resolution_channel?: string;
|
|
1046
1070
|
approval_id?: string;
|
|
1047
1071
|
session_id?: string;
|
|
1048
1072
|
tool_name?: string;
|
|
@@ -1063,6 +1087,8 @@ export interface ApprovalRecord {
|
|
|
1063
1087
|
export type CustomToolCallStatus = 'pending' | 'completed' | 'timeout' | 'cancelled' | string;
|
|
1064
1088
|
/** One application-executed custom-tool call. Unknown response fields are preserved. */
|
|
1065
1089
|
export interface CustomToolCallRecord {
|
|
1090
|
+
run_id?: string;
|
|
1091
|
+
resolution_channel?: string;
|
|
1066
1092
|
call_id: string;
|
|
1067
1093
|
session_id: string;
|
|
1068
1094
|
tool_call_id: string;
|
|
@@ -1302,26 +1328,21 @@ export interface WakeResult {
|
|
|
1302
1328
|
triggered: boolean;
|
|
1303
1329
|
}
|
|
1304
1330
|
export type { SessionEvent } from './events.js';
|
|
1305
|
-
export interface ZooworkClient {
|
|
1331
|
+
export interface ZooworkClient extends DeveloperApi {
|
|
1306
1332
|
listModels(): Promise<ModelInfo[]>;
|
|
1307
1333
|
/**
|
|
1308
1334
|
* Create an agent. The gateway derives ownership from your API key and always skips the
|
|
1309
1335
|
* interactive onboarding interview — the agent answers your first message directly.
|
|
1310
|
-
*
|
|
1311
|
-
* through the gateway it is overwritten and can be omitted.
|
|
1336
|
+
* Ownership is derived from the key. Omit caller-supplied ownership.
|
|
1312
1337
|
*/
|
|
1313
1338
|
createAgent(input: {
|
|
1314
1339
|
resource: AgentResource;
|
|
1315
1340
|
ownership?: Ownership;
|
|
1316
1341
|
}, idempotencyKey?: string): Promise<AgentRecord>;
|
|
1317
1342
|
/**
|
|
1318
|
-
* List
|
|
1319
|
-
*
|
|
1320
|
-
*
|
|
1321
|
-
* segment of a ZooWork chat URL) to its agent. Page size is fixed at 100 by the engine.
|
|
1322
|
-
*
|
|
1323
|
-
* Note the scope is `owner_uid AND org_id`: an agent a colleague created in your org is
|
|
1324
|
-
* fetchable by `getAgent` but will not appear here.
|
|
1343
|
+
* List Agents within the key's Project and applicable owner visibility. Labels filter
|
|
1344
|
+
* declared labels, and page size is 100. Reads by id still recheck authorization;
|
|
1345
|
+
* a colleague's Agent is not automatically accessible.
|
|
1325
1346
|
*
|
|
1326
1347
|
* `await listAgents()` returns one {@link AgentPage}; read `.data` for its agents and
|
|
1327
1348
|
* `.next_page` / `.hasNextPage()` for continuation. `for await (const agent of listAgents())`
|
|
@@ -1329,7 +1350,8 @@ export interface ZooworkClient {
|
|
|
1329
1350
|
*/
|
|
1330
1351
|
listAgents(opts?: AgentListParams): AgentPagePromise;
|
|
1331
1352
|
getAgent(agentId: string): Promise<AgentRecord>;
|
|
1332
|
-
/** PUT declared sections
|
|
1353
|
+
/** PUT declared sections. Ownership-only changes do not bump config_version.
|
|
1354
|
+
* Supply expected_config_version for an atomic version check; a mismatch returns 409 active_config_changed. */
|
|
1333
1355
|
updateAgent(agentId: string, sections: Record<string, unknown>): Promise<AgentRecord>;
|
|
1334
1356
|
/**
|
|
1335
1357
|
* Soft delete: the agent stops resolving on the API, but this is not a resource purge.
|
|
@@ -1591,6 +1613,8 @@ export interface ZooworkClient {
|
|
|
1591
1613
|
createSession(agentId: string, input: {
|
|
1592
1614
|
initial_events?: OutboundEvent[];
|
|
1593
1615
|
metadata?: Record<string, unknown>;
|
|
1616
|
+
runtime_mode?: 'active';
|
|
1617
|
+
idle_compaction?: boolean | null;
|
|
1594
1618
|
}, idempotencyKey?: string): Promise<SessionRecord>;
|
|
1595
1619
|
getSession(agentId: string, sessionId: string, opts?: {
|
|
1596
1620
|
history?: boolean;
|
|
@@ -1678,6 +1702,7 @@ export interface ZooworkClient {
|
|
|
1678
1702
|
/** Pending calls only. Any other status is rejected by the API. */
|
|
1679
1703
|
listCustomToolCalls(agentId: string, opts?: {
|
|
1680
1704
|
status?: 'pending';
|
|
1705
|
+
sessionId?: string;
|
|
1681
1706
|
}): Promise<CustomToolCallRecord[]>;
|
|
1682
1707
|
/**
|
|
1683
1708
|
* Return one call's result. A pending call answers 202/signaled:true but stays pending until
|
|
@@ -1700,6 +1725,7 @@ export interface ZooworkClient {
|
|
|
1700
1725
|
*/
|
|
1701
1726
|
listApprovals(agentId: string, opts?: {
|
|
1702
1727
|
status?: 'pending';
|
|
1728
|
+
sessionId?: string;
|
|
1703
1729
|
}): Promise<ApprovalRecord[]>;
|
|
1704
1730
|
/** Resolve one approval. `decision` is exactly one of allow-once / allow-always / deny. */
|
|
1705
1731
|
resolveApproval(agentId: string, approvalId: string, input: {
|
|
@@ -1857,7 +1883,7 @@ export interface ZooworkClient {
|
|
|
1857
1883
|
* Create a client.
|
|
1858
1884
|
*
|
|
1859
1885
|
* ```ts
|
|
1860
|
-
* const zc = createZooworkClient({ apiKey: '
|
|
1886
|
+
* const zc = createZooworkClient({ apiKey: 'zwp_live_...' })
|
|
1861
1887
|
* const zc = createZooworkClient() // reads ZOOWORK_API_KEY
|
|
1862
1888
|
* ```
|
|
1863
1889
|
*
|
package/dist/client.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ZooWork Managed Agents SDK — core client. Developer Preview.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* authority, so it is SERVER-SIDE ONLY: never ship it in a browser or mobile bundle.
|
|
4
|
+
* Create a Project API key at https://platform.zoowork.ai. Keep it server-side: never ship it in a browser or mobile bundle.
|
|
6
5
|
*
|
|
7
6
|
* IDs are opaque strings and unknown response fields must be ignored — both are
|
|
8
7
|
* forward-compatibility rules, not suggestions. Errors surface an `error.type`; match on
|
|
9
8
|
* that, never on the message text.
|
|
10
9
|
*/
|
|
10
|
+
import { createDeveloperApi } from './developer-api.js';
|
|
11
11
|
import { parseSSE, isObj } from './sse.js';
|
|
12
12
|
import { normalizeEvent } from './events.js';
|
|
13
13
|
/**
|
|
@@ -143,7 +143,7 @@ function httpError(res, text) {
|
|
|
143
143
|
* Create a client.
|
|
144
144
|
*
|
|
145
145
|
* ```ts
|
|
146
|
-
* const zc = createZooworkClient({ apiKey: '
|
|
146
|
+
* const zc = createZooworkClient({ apiKey: 'zwp_live_...' })
|
|
147
147
|
* const zc = createZooworkClient() // reads ZOOWORK_API_KEY
|
|
148
148
|
* ```
|
|
149
149
|
*
|
|
@@ -165,7 +165,7 @@ export function createZooworkClient(cfg = {}) {
|
|
|
165
165
|
})());
|
|
166
166
|
if (!auth) {
|
|
167
167
|
throw new Error('No ZooWork API key. Pass createZooworkClient({ apiKey }) or set ZOOWORK_API_KEY. ' +
|
|
168
|
-
'
|
|
168
|
+
'Create a Project API key at https://platform.zoowork.ai.');
|
|
169
169
|
}
|
|
170
170
|
const bearer = 'serviceToken' in auth ? auth.serviceToken : auth.apiKey;
|
|
171
171
|
/**
|
|
@@ -282,13 +282,20 @@ export function createZooworkClient(cfg = {}) {
|
|
|
282
282
|
const own = projection.ownership;
|
|
283
283
|
if (!own?.owner_uid || !own.org_id) {
|
|
284
284
|
throw new ZooworkError(500, `agent ${agentId} projection carries no ownership — cannot derive the owner_uid/org_id ` +
|
|
285
|
-
'selectors the artifact routes require', 'ownership_unavailable');
|
|
285
|
+
'selectors the file and artifact routes require', 'ownership_unavailable');
|
|
286
286
|
}
|
|
287
287
|
const sel = { owner_uid: own.owner_uid, org_id: own.org_id };
|
|
288
288
|
ownershipByAgent.set(agentId, sel);
|
|
289
289
|
return sel;
|
|
290
290
|
};
|
|
291
|
+
const bytes = async (path) => {
|
|
292
|
+
const res = await doFetch(`${base}${path}`, { headers: { Authorization: `Bearer ${bearer}` } });
|
|
293
|
+
if (!res.ok)
|
|
294
|
+
throw httpError(res, await res.text());
|
|
295
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
296
|
+
};
|
|
291
297
|
const client = {
|
|
298
|
+
...createDeveloperApi(json, bytes, artifactSelectors),
|
|
292
299
|
listModels: async () => {
|
|
293
300
|
const data = await json('/models');
|
|
294
301
|
return Array.isArray(data) ? data : (data.models ?? []);
|
|
@@ -668,7 +675,7 @@ export function createZooworkClient(cfg = {}) {
|
|
|
668
675
|
}
|
|
669
676
|
},
|
|
670
677
|
listCustomToolCalls: async (agentId, opts = {}) => {
|
|
671
|
-
const data = await json(`${agents(agentId)}/custom_tool_calls${query({ status: opts.status })}`);
|
|
678
|
+
const data = await json(`${agents(agentId)}/custom_tool_calls${query({ status: opts.status, session_id: opts.sessionId })}`);
|
|
672
679
|
return data.custom_tool_calls ?? [];
|
|
673
680
|
},
|
|
674
681
|
resolveCustomToolCall: (agentId, callId, input) => json(`${agents(agentId)}/custom_tool_calls/${encodeURIComponent(callId)}/result`, {
|
|
@@ -676,7 +683,7 @@ export function createZooworkClient(cfg = {}) {
|
|
|
676
683
|
body: JSON.stringify(input),
|
|
677
684
|
}),
|
|
678
685
|
listApprovals: async (agentId, opts = {}) => {
|
|
679
|
-
const data = await json(`${agents(agentId)}/approvals${query({ status: opts.status })}`);
|
|
686
|
+
const data = await json(`${agents(agentId)}/approvals${query({ status: opts.status, session_id: opts.sessionId })}`);
|
|
680
687
|
return data.approvals ?? [];
|
|
681
688
|
},
|
|
682
689
|
resolveApproval: (agentId, approvalId, input) => json(`${agents(agentId)}/approvals/${encodeURIComponent(approvalId)}/resolve`, {
|