@zoowork-ai/sdk 0.6.0 → 0.7.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 CHANGED
@@ -3,6 +3,26 @@
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.7.0 — 2026-09-14
7
+
8
+ ### Added
9
+
10
+ - **Application-executed custom tools.** Agent resources can declare `custom_tools`; event
11
+ helpers expose requested calls; and the client can list pending calls, resolve a call, or
12
+ post typed `user.custom_tool_result` content.
13
+ - **Filtered cursor session listing.** `listSessionPage()` adds the channel, surface,
14
+ runtime-mode and archive-filtered cursor lane without changing the existing numeric-page
15
+ behavior of `listSessions()`. Session archive and delete operations are also exposed.
16
+
17
+ ### Changed
18
+
19
+ - **MCP configuration now covers runtime context and permissions.** Types include metadata
20
+ context, a server permission default, exact per-tool overrides, and trailing-prefix tool
21
+ policy selectors.
22
+ - **Channel contracts include direct DingTalk configuration and Feishu document capability
23
+ state.** Callers can inspect provider, sync, missing-scope and approval-state details from
24
+ returned capabilities.
25
+
6
26
  ## 0.6.0 — 2026-09-11
7
27
 
8
28
  ### Changed
package/README.md CHANGED
@@ -115,7 +115,7 @@ instead of silently returning an empty or apparently complete array.
115
115
  `run.finished` ends a turn; assistant text arrives on `agent.assistant`.
116
116
 
117
117
  ```ts
118
- import { assistantText, isRunFinished, runOutcome, toolCall } from '@zoowork-ai/sdk'
118
+ import { assistantText, customToolUse, isRunFinished, runOutcome, toolCall } from '@zoowork-ai/sdk'
119
119
 
120
120
  for await (const ev of zc.streamEvents(agent.agent_id, session.session_id)) {
121
121
  process.stdout.write(assistantText(ev)) // '' for every non-assistant event
@@ -201,10 +201,30 @@ const { exit_code, stdout } = await zc.exec(agent.agent_id, ['bash', '-lc', 'pwd
201
201
 
202
202
  ## Sessions, approvals, environments
203
203
 
204
- `listSessions`, `archiveSession` and `deleteSession` round out the session surface. There is no
204
+ `listSessions` keeps the legacy numeric-page contract. Use `listSessionPage` for the filtered
205
+ cursor lane: it starts with `sls1:0`, accepts channel/surface/runtime/archive filters, and returns
206
+ `next_cursor` plus a `list_cursor` on each row. Cursors are opaque and bound to the same filters.
207
+ `archiveSession` and `deleteSession` round out the session surface. There is no
205
208
  `patchSession`: the gateway does not proxy `PATCH` at all (405), so session `metadata` is fixed at
206
209
  creation time.
207
210
 
211
+ An application-executed tool is declared in `resource.custom_tools`. When
212
+ `customToolUse(ev)?.phase === 'requested'`, execute the named operation and call
213
+ `resolveCustomToolCall`; `listCustomToolCalls` recovers pending work after a restart. You may also
214
+ post a typed `user.custom_tool_result` event to the owning session. The run reports
215
+ `awaiting_approval` while paused, so use `pending_custom_tool_calls` to distinguish this wait from
216
+ a normal approval. These contracts are source-reviewed and need deployment verification.
217
+
218
+ ```ts
219
+ const call = customToolUse(ev)
220
+ if (call?.phase === 'requested') {
221
+ await zc.resolveCustomToolCall(agentId, call.callId, {
222
+ content: [{ type: 'json', value: { price: 42 } }],
223
+ resolvedBy: 'pricing-service',
224
+ })
225
+ }
226
+ ```
227
+
208
228
  `listApprovals` / `resolveApproval` expose the approvals resource — `decision` is one of
209
229
  `allow-once`, `allow-always`, `deny`. End-to-end approval and turn-budget behavior need
210
230
  separate verification on the deployment you use.
@@ -291,18 +311,19 @@ temporary Agent/Session, one potentially billable model turn and cleanup. JSON r
291
311
  in its printed private directory. The test never publishes. Normal `pnpm test` is offline
292
312
  and needs no key. See [E2E and recovery instructions](e2e/README.md) for scope and options.
293
313
 
294
- To publish the checked-out version, use npm normally:
314
+ Publishing runs through [`.github/workflows/release.yml`](.github/workflows/release.yml). Configure
315
+ the npm package's Trusted Publisher once with organization `SerendipityOneInc`, repository
316
+ `zoowork-sdk-typescript`, workflow `release.yml`, and direct publish permission. No npm token or
317
+ repeated `npm login` is needed after that.
295
318
 
296
- ```sh
297
- npm login
298
- npm publish
299
- ```
319
+ For each release, merge the intended version and changelog, then publish a GitHub Release whose
320
+ tag is exactly `v<package version>` — for example, `v0.7.0`. The workflow verifies that match,
321
+ runs the offline test and build gates, and publishes the public package with npm OIDC. A mismatched
322
+ tag fails before publication, and an existing npm version cannot be overwritten.
300
323
 
301
- The `prepack` hook rebuilds `dist` from source; `npm publish` then publishes that package with
302
- your npm account. It does not run E2E, read a staging key or require an E2E result directory.
303
- You choose when to test and publish, including on different machines. Check the version and
304
- changelog before publishing; existing npm versions cannot be overwritten. Use
305
- `npm publish --dry-run` to inspect the package without uploading it.
324
+ The release workflow does not run live E2E or read a staging key. Run `pnpm test:e2e` separately
325
+ before creating the GitHub Release when live verification is required. Use
326
+ `npm publish --dry-run` locally to inspect the package without uploading it.
306
327
 
307
328
  ## License
308
329
 
package/dist/client.d.ts CHANGED
@@ -123,6 +123,18 @@ export interface ModelInfo {
123
123
  api?: string;
124
124
  [k: string]: unknown;
125
125
  }
126
+ /** Approval behavior for one MCP server or one of its tools. */
127
+ export type McpToolPermission = 'always_ask' | 'always_allow';
128
+ /** Opt-in runtime coordinates sent only while a tool is executing, never during catalog discovery. */
129
+ export interface McpContextConfig {
130
+ /** Add the coordinates under `params._meta['ai.zooclaw/context']` on `tools/call`. */
131
+ meta?: boolean;
132
+ /** Add the coordinates as `x-zooclaw-*` headers on the tool call's HTTP requests. */
133
+ headers?: boolean;
134
+ }
135
+ export interface McpToolPermissionOverride {
136
+ permission: McpToolPermission;
137
+ }
126
138
  /**
127
139
  * One remote MCP server, declared as `resource.mcp[]` on create or update.
128
140
  *
@@ -161,8 +173,36 @@ export interface McpServerDeclaration {
161
173
  * There is no `auto` value.
162
174
  */
163
175
  exposure?: 'deferred' | 'direct';
176
+ /**
177
+ * Opt in to runtime coordinates for this server. Both switches default to false. The context
178
+ * can include agent/session/computer ids and, when available, run/turn/config/actor fields.
179
+ * Catalog discovery never receives it. Header delivery may be unavailable through proxies;
180
+ * `_meta` is the portable option.
181
+ */
182
+ context?: McpContextConfig;
183
+ /** Default approval behavior for every tool on this server. Omit for the existing default-allow behavior. */
184
+ permission?: McpToolPermission;
185
+ /**
186
+ * Per-tool approval overrides keyed by the server's original tool name, not its
187
+ * `mcp__<server>__<tool>` name. Keys are exact and cannot contain `*`; at most 64 entries.
188
+ */
189
+ tools?: Record<string, McpToolPermissionOverride>;
164
190
  [k: string]: unknown;
165
191
  }
192
+ /** One application-executed tool declared under {@link AgentResource.custom_tools}. */
193
+ export interface CustomToolDeclaration {
194
+ /** Unique within the Agent; 1–64 ASCII letters, numbers, `_` or `-`. Reserved runtime names are rejected. */
195
+ name: string;
196
+ /** Non-empty tool description, at most 4 KiB UTF-8. */
197
+ description: string;
198
+ /** JSON Schema for the input. Its top-level `type` must be `object`; at most 16 KiB serialized. */
199
+ input_schema: {
200
+ type: 'object';
201
+ [k: string]: unknown;
202
+ };
203
+ /** Result wait budget in milliseconds. Defaults to 600,000; maximum 86,400,000. */
204
+ timeoutMs?: number;
205
+ }
166
206
  /**
167
207
  * The system-prompt pin (`resource.system_prompt`).
168
208
  *
@@ -252,9 +292,21 @@ export interface AgentResource {
252
292
  version?: number | 'latest';
253
293
  }[];
254
294
  labels?: Record<string, string>;
295
+ /**
296
+ * Tool-surface and approval policy. Name selectors in `allow`, `deny`, `rules[].match.tool`,
297
+ * `afterRules[].match.tool`, and MCP names in `deferred.pinned` accept an exact name, `*`, or
298
+ * one trailing `prefix*`. Other wildcard forms match nothing. `alsoAllow` and
299
+ * `permissions` keys remain exact. Kept open for forward-compatible policy fields.
300
+ */
255
301
  tool_policy?: Record<string, unknown>;
256
302
  /** Remote MCP servers. Only unauthenticated ones work today — see {@link McpServerDeclaration}. */
257
303
  mcp?: McpServerDeclaration[];
304
+ /**
305
+ * Tools executed by your application. At most 32. A model call emits
306
+ * `agent.custom_tool_use`; return the result with `resolveCustomToolCall` or a
307
+ * `user.custom_tool_result` event. Source-reviewed; deployment availability is unverified.
308
+ */
309
+ custom_tools?: CustomToolDeclaration[];
258
310
  /**
259
311
  * System-prompt pin. Omitted on create means "the platform version active right now", pinned
260
312
  * from then on. REPLACE-ON-WRITE on PUT, like `tool_policy` — see {@link SystemPromptDeclaration}.
@@ -312,6 +364,27 @@ export interface AgentSkill {
312
364
  }[];
313
365
  [k: string]: unknown;
314
366
  }
367
+ /** Source-reviewed asynchronous capability-configuration state for one channel feature. */
368
+ export interface AgentChannelCapabilitySync {
369
+ state: 'pending' | 'applied' | 'retry' | 'error';
370
+ [k: string]: unknown;
371
+ }
372
+ export interface FeishuChannelProviderStatus {
373
+ state: 'ready' | 'degraded';
374
+ missing_scopes: string[];
375
+ approval_state?: 'pending_admin' | null;
376
+ [k: string]: unknown;
377
+ }
378
+ export interface FeishuDocumentsCapability {
379
+ permission_admin_enabled: boolean;
380
+ sync: AgentChannelCapabilitySync;
381
+ provider: FeishuChannelProviderStatus;
382
+ [k: string]: unknown;
383
+ }
384
+ export interface AgentChannelCapabilities {
385
+ feishu_documents?: FeishuDocumentsCapability | null;
386
+ [k: string]: unknown;
387
+ }
315
388
  /**
316
389
  * One platform account bound to an agent, as the channel service reports it.
317
390
  * `dm_policy` / `group_policy` are the reachability policies (`'open'` is the
@@ -328,10 +401,13 @@ export interface AgentChannel {
328
401
  health?: string;
329
402
  status?: string;
330
403
  status_code?: string | null;
404
+ /** Source-reviewed capability state. Omitted when the platform reports none. */
405
+ capabilities?: AgentChannelCapabilities | null;
331
406
  [k: string]: unknown;
332
407
  }
333
408
  /**
334
- * The chat platforms you can bind, staging-verified 2026-08-28.
409
+ * The chat platforms you can bind. Feishu, Slack, WeCom and WeChat were staging-verified
410
+ * 2026-08-28. Direct DingTalk support is source-reviewed, not deployment-verified here.
335
411
  *
336
412
  * Three of them have a server-driven QR flow ({@link GuidedSetupPlatform}); Slack does not,
337
413
  * and structurally cannot — a Slack app is created by a person and its tokens only ever exist
@@ -340,15 +416,15 @@ export interface AgentChannel {
340
416
  *
341
417
  * WeChat is the one platform that goes the other way: `'weixin'`/`'wechat'` on
342
418
  * {@link ZooworkClient.addChannel} answers `400 channel.weixin_setup_required`, so the QR flow
343
- * is its ONLY path. See {@link AddChannelPlatform}. Any name outside this type answers
344
- * `400 channel.invalid_request`.
419
+ * is its ONLY path. DingTalk uses `'dingtalk-connector'` and currently has a direct config path
420
+ * only on the public API; its product QR flow is not exposed here. See {@link AddChannelPlatform}.
345
421
  */
346
- export type ChannelPlatform = 'feishu' | 'slack' | 'wecom' | 'weixin';
422
+ export type ChannelPlatform = 'feishu' | 'slack' | 'wecom' | 'weixin' | 'dingtalk-connector';
347
423
  /**
348
424
  * The platforms {@link ZooworkClient.addChannel} accepts — every {@link ChannelPlatform}
349
425
  * except WeChat, which refuses explicit config and takes the QR flow only.
350
426
  */
351
- export type AddChannelPlatform = 'feishu' | 'slack' | 'wecom';
427
+ export type AddChannelPlatform = 'feishu' | 'slack' | 'wecom' | 'dingtalk-connector';
352
428
  /**
353
429
  * The platforms with a server-driven QR flow: {@link ZooworkClient.startChannelSetup} →
354
430
  * render the URI → poll. Slack is absent by design, not by omission.
@@ -397,7 +473,7 @@ export interface AddChannelInput {
397
473
  */
398
474
  account?: string;
399
475
  display_name?: string;
400
- /** Server default: `'open'`. `'pairing'` is rejected with `400 channel.pairing_unsupported`. */
476
+ /** Server default: `'open'`. DingTalk accepts only `'open'`; `'pairing'` is rejected everywhere. */
401
477
  dm_policy?: string;
402
478
  /** Server default: `'open'`. */
403
479
  group_policy?: string;
@@ -411,8 +487,11 @@ export interface AddChannelInput {
411
487
  * needs the app-level token as well as the bot token)
412
488
  * - `wecom` — `{ botId, secret }`, both required
413
489
  * - `feishu` — `{ appId, appSecret, domain }`, only when skipping the QR flow
490
+ * - `dingtalk-connector` — `{ clientId, clientSecret }`; no public guided QR route
414
491
  */
415
492
  config?: Record<string, unknown>;
493
+ /** Feishu only. Enable document-permission administration; defaults to false. */
494
+ permission_admin_enabled?: boolean;
416
495
  }
417
496
  export interface UpdateChannelInput {
418
497
  /** Which binding to touch — see {@link AddChannelInput.account}. Server default: `'default'`. */
@@ -420,6 +499,8 @@ export interface UpdateChannelInput {
420
499
  dm_policy?: string;
421
500
  group_policy?: string;
422
501
  enabled?: boolean;
502
+ /** Feishu only. Other platforms reject this field when it is present. */
503
+ permission_admin_enabled?: boolean;
423
504
  }
424
505
  /**
425
506
  * Body for {@link ZooworkClient.startChannelSetup}. Every field is optional, and each platform
@@ -462,6 +543,8 @@ export interface ChannelSetupInput {
462
543
  dm_policy?: string;
463
544
  /** Server default: `'open'`. Ignored by WeChat, which forces `'disabled'`. */
464
545
  group_policy?: string;
546
+ /** Feishu only. Enable document-permission administration; defaults to false. */
547
+ permission_admin_enabled?: boolean;
465
548
  }
466
549
  /** @deprecated Use {@link ChannelSetupInput}; this is the same shape under the old name. */
467
550
  export type FeishuSetupInput = ChannelSetupInput;
@@ -624,6 +707,8 @@ export interface SessionRecord {
624
707
  run_status?: string | null;
625
708
  /** Pending approval count on getSession; not included in every session projection. */
626
709
  pending_approvals?: number;
710
+ /** Pending application-executed custom-tool count on getSession. */
711
+ pending_custom_tool_calls?: number;
627
712
  /**
628
713
  * `running` on a `createSession` receipt, nullable on `getSession`, and absent from
629
714
  * `listSessions` rows. This is not the run outcome; read {@link SessionRecord.run_status}
@@ -632,12 +717,18 @@ export interface SessionRecord {
632
717
  status?: string | null;
633
718
  metadata?: Record<string, unknown>;
634
719
  archived?: boolean;
720
+ runtime_mode?: 'active' | 'preview' | 'authoring' | 'evaluation' | string;
721
+ config_version?: number;
635
722
  updated_at?: string;
723
+ /** Activity sort key used by filtered cursor listing. */
724
+ last_activity_at?: string;
725
+ /** Opaque per-row resume cursor returned only by {@link ZooworkClient.listSessionPage}. */
726
+ list_cursor?: string;
636
727
  /** Present only when the read asked for `history: true`; the most recent `limit` rows, in order. */
637
728
  history?: SessionHistoryEntry[];
638
729
  [k: string]: unknown;
639
730
  }
640
- /** Write-side events: user.message / user.interrupt / user.tool_confirmation / system.message */
731
+ /** Write-side events, including `user.custom_tool_result`; unsupported types are rejected by the API. */
641
732
  export interface OutboundEvent {
642
733
  type: string;
643
734
  content?: unknown;
@@ -655,6 +746,55 @@ export interface OutboundEvent {
655
746
  };
656
747
  [k: string]: unknown;
657
748
  }
749
+ export type CustomToolResultImageMimeType = 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp';
750
+ /** One content block returned by an application-executed custom tool. */
751
+ export type CustomToolResultContent = {
752
+ type: 'text';
753
+ text: string;
754
+ } | {
755
+ type: 'json';
756
+ value: unknown;
757
+ } | {
758
+ type: 'image';
759
+ source: {
760
+ type: 'base64';
761
+ media_type: CustomToolResultImageMimeType;
762
+ data: string;
763
+ };
764
+ } | {
765
+ type: 'image';
766
+ data: string;
767
+ mime_type: CustomToolResultImageMimeType;
768
+ };
769
+ /** Write-side result event for {@link ZooworkClient.postEvents}. */
770
+ export type CustomToolResultEvent = OutboundEvent & {
771
+ type: 'user.custom_tool_result';
772
+ content: CustomToolResultContent[];
773
+ is_error?: boolean;
774
+ idempotency_key?: string;
775
+ } & ({
776
+ custom_tool_use_id: string;
777
+ call_id?: never;
778
+ } | {
779
+ call_id: string;
780
+ custom_tool_use_id?: never;
781
+ });
782
+ /** Options for the filtered cursor lane. Filters are part of the cursor scope. */
783
+ export interface SessionListPageOptions {
784
+ /** Opaque cursor returned by this same filter scope. Omit to start at `sls1:0`. */
785
+ cursor?: string;
786
+ /** 1–100; server default 50. */
787
+ limit?: number;
788
+ excludeChannels?: string[];
789
+ includeSurfaces?: string[];
790
+ runtimeModes?: Array<'active' | 'preview' | 'authoring' | 'evaluation'>;
791
+ includeArchived?: boolean;
792
+ }
793
+ /** One filtered session page. `next_cursor` is null at the end. */
794
+ export interface SessionListPage {
795
+ sessions: SessionRecord[];
796
+ next_cursor: string | null;
797
+ }
658
798
  /** One `postEvents` receipt. An accepted event carries the full event object's fields too. */
659
799
  export interface PostEventReceipt {
660
800
  id?: string | null;
@@ -898,6 +1038,24 @@ export interface ApprovalRecord {
898
1038
  created_at?: string;
899
1039
  [k: string]: unknown;
900
1040
  }
1041
+ export type CustomToolCallStatus = 'pending' | 'completed' | 'timeout' | 'cancelled' | string;
1042
+ /** One application-executed custom-tool call. Unknown response fields are preserved. */
1043
+ export interface CustomToolCallRecord {
1044
+ call_id: string;
1045
+ session_id: string;
1046
+ tool_call_id: string;
1047
+ name: string;
1048
+ input: Record<string, unknown>;
1049
+ status: CustomToolCallStatus;
1050
+ requested_at: string;
1051
+ timeout_at?: string;
1052
+ resolved_by?: string;
1053
+ resolved_at?: string;
1054
+ is_error?: boolean;
1055
+ /** Resolve receipt: true when a pending call was signaled, false when it was already terminal. */
1056
+ signaled?: boolean;
1057
+ [k: string]: unknown;
1058
+ }
901
1059
  /** Artifact lifecycle. Only a `ready` row carries a resolvable `url`. */
902
1060
  export type ArtifactStatus = 'pending' | 'ready' | 'failed' | 'deleted' | string;
903
1061
  /**
@@ -1416,10 +1574,15 @@ export interface ZooworkClient {
1416
1574
  history?: boolean;
1417
1575
  limit?: number;
1418
1576
  }): Promise<SessionRecord>;
1419
- /** Newest first by `updated_at`, 50 per page, `page` is 1-based. Single page per call — there is no cursor. */
1577
+ /** Legacy newest-first numeric page lane: 50 per page, `page` is 1-based. */
1420
1578
  listSessions(agentId: string, opts?: {
1421
1579
  page?: number;
1422
1580
  }): Promise<SessionRecord[]>;
1581
+ /**
1582
+ * Filtered cursor lane. It is separate from {@link listSessions} so existing numeric-page
1583
+ * callers keep their contract. Cursors are opaque and valid only with the same filters.
1584
+ */
1585
+ listSessionPage(agentId: string, opts?: SessionListPageOptions): Promise<SessionListPage>;
1423
1586
  /**
1424
1587
  * Stamp `archived_at`. Afterwards writes are `409 session_archived` while reads keep working.
1425
1588
  * Interrupt an in-flight run first, or the archive races it.
@@ -1490,6 +1653,19 @@ export interface ZooworkClient {
1490
1653
  cursor?: string;
1491
1654
  signal?: AbortSignal;
1492
1655
  }): AsyncGenerator<SessionEvent>;
1656
+ /** Pending calls only. Any other status is rejected by the API. */
1657
+ listCustomToolCalls(agentId: string, opts?: {
1658
+ status?: 'pending';
1659
+ }): Promise<CustomToolCallRecord[]>;
1660
+ /**
1661
+ * Return one call's result. A pending call answers 202/signaled:true but stays pending until
1662
+ * the paused run consumes it; an already-terminal call answers 200/signaled:false.
1663
+ */
1664
+ resolveCustomToolCall(agentId: string, callId: string, input: {
1665
+ content: CustomToolResultContent[];
1666
+ isError?: boolean;
1667
+ resolvedBy?: string;
1668
+ }): Promise<CustomToolCallRecord>;
1493
1669
  /**
1494
1670
  * Tool calls parked on a human decision. `status` may ONLY be omitted or `'pending'` —
1495
1671
  * staging-verified 2026-08-07; any other value is rejected, so there is no way to list
package/dist/client.js CHANGED
@@ -543,6 +543,17 @@ export function createZooworkClient(cfg = {}) {
543
543
  const data = await json(`${sessions(agentId)}${query({ page: opts.page })}`);
544
544
  return data.sessions ?? [];
545
545
  },
546
+ listSessionPage: async (agentId, opts = {}) => {
547
+ const data = await json(`${sessions(agentId)}${query({
548
+ cursor: opts.cursor ?? 'sls1:0',
549
+ limit: opts.limit,
550
+ exclude_channels: opts.excludeChannels?.join(','),
551
+ include_surfaces: opts.includeSurfaces?.join(','),
552
+ runtime_modes: opts.runtimeModes?.join(','),
553
+ include_archived: opts.includeArchived === undefined ? undefined : String(opts.includeArchived),
554
+ })}`);
555
+ return { sessions: data.sessions ?? [], next_cursor: data.next_cursor ?? null };
556
+ },
546
557
  archiveSession: async (agentId, sessionId) => {
547
558
  const data = await json(`${sessions(agentId)}/${encodeURIComponent(sessionId)}/archive`, { method: 'POST' });
548
559
  return { ...data, archived: data.archived ?? false };
@@ -651,6 +662,14 @@ export function createZooworkClient(cfg = {}) {
651
662
  throw e;
652
663
  }
653
664
  },
665
+ listCustomToolCalls: async (agentId, opts = {}) => {
666
+ const data = await json(`${agents(agentId)}/custom_tool_calls${query({ status: opts.status })}`);
667
+ return data.custom_tool_calls ?? [];
668
+ },
669
+ resolveCustomToolCall: (agentId, callId, input) => json(`${agents(agentId)}/custom_tool_calls/${encodeURIComponent(callId)}/result`, {
670
+ method: 'POST',
671
+ body: JSON.stringify(input),
672
+ }),
654
673
  listApprovals: async (agentId, opts = {}) => {
655
674
  const data = await json(`${agents(agentId)}/approvals${query({ status: opts.status })}`);
656
675
  return data.approvals ?? [];
package/dist/events.d.ts CHANGED
@@ -17,10 +17,10 @@
17
17
  * and may add types within a version.
18
18
  */
19
19
  /** SESSION_EVENT_TYPES, mirrored from the API. */
20
- export declare const SESSION_EVENT_TYPES: readonly ["run.started", "run.finished", "chat.delta", "chat.final", "chat.aborted", "chat.error", "agent.lifecycle", "agent.assistant", "agent.thinking", "agent.tool", "agent.item", "agent.plan", "agent.approval", "agent.command_output", "agent.patch", "agent.compaction", "agent.error", "attachment.created", "message.outbound"];
20
+ export declare const SESSION_EVENT_TYPES: readonly ["run.started", "run.finished", "chat.delta", "chat.final", "chat.aborted", "chat.error", "agent.lifecycle", "agent.assistant", "agent.thinking", "agent.tool", "agent.item", "agent.plan", "agent.approval", "agent.custom_tool_use", "agent.command_output", "agent.patch", "agent.compaction", "agent.error", "attachment.created", "message.outbound"];
21
21
  export type SessionEventType = (typeof SESSION_EVENT_TYPES)[number];
22
22
  /** Your own inputs, echoed back in the unified event history. */
23
- export declare const PUBLIC_INPUT_EVENT_TYPES: readonly ["user.message", "user.interrupt", "user.tool_confirmation", "system.message"];
23
+ export declare const PUBLIC_INPUT_EVENT_TYPES: readonly ["user.message", "user.interrupt", "user.tool_confirmation", "user.custom_tool_result", "system.message"];
24
24
  export type PublicInputEventType = (typeof PUBLIC_INPUT_EVENT_TYPES)[number];
25
25
  /** A durable session event, normalized across the REST and SSE shapes. */
26
26
  export interface SessionEvent {
@@ -74,6 +74,20 @@ export interface ToolCall {
74
74
  isError?: boolean;
75
75
  resultPreview?: string;
76
76
  }
77
+ export interface CustomToolUse {
78
+ phase: 'requested' | 'resolved';
79
+ callId: string;
80
+ toolCallId?: string;
81
+ name?: string;
82
+ input?: Record<string, unknown>;
83
+ timeoutAt?: string;
84
+ outcome?: 'completed' | 'timeout' | 'cancelled';
85
+ isError?: boolean;
86
+ resolvedBy?: string;
87
+ resolutionChannel?: string;
88
+ }
89
+ /** Application-executed custom-tool activity; undefined for every other event type. */
90
+ export declare function customToolUse(e: SessionEvent): CustomToolUse | undefined;
77
91
  /**
78
92
  * Tool activity for an `agent.tool` event; undefined for every other type.
79
93
  *
package/dist/events.js CHANGED
@@ -31,6 +31,7 @@ export const SESSION_EVENT_TYPES = [
31
31
  'agent.item',
32
32
  'agent.plan',
33
33
  'agent.approval',
34
+ 'agent.custom_tool_use',
34
35
  'agent.command_output',
35
36
  'agent.patch',
36
37
  'agent.compaction',
@@ -43,6 +44,7 @@ export const PUBLIC_INPUT_EVENT_TYPES = [
43
44
  'user.message',
44
45
  'user.interrupt',
45
46
  'user.tool_confirmation',
47
+ 'user.custom_tool_result',
46
48
  'system.message',
47
49
  ];
48
50
  const isObj = (v) => !!v && typeof v === 'object';
@@ -124,6 +126,25 @@ export function thinkingText(e) {
124
126
  return '';
125
127
  return typeof e.payload.text === 'string' ? e.payload.text : '';
126
128
  }
129
+ /** Application-executed custom-tool activity; undefined for every other event type. */
130
+ export function customToolUse(e) {
131
+ if (e.eventType !== 'agent.custom_tool_use')
132
+ return undefined;
133
+ const p = e.payload;
134
+ const outcome = p.outcome === 'completed' || p.outcome === 'timeout' || p.outcome === 'cancelled' ? p.outcome : undefined;
135
+ return {
136
+ phase: p.phase === 'resolved' ? 'resolved' : 'requested',
137
+ callId: typeof p.callId === 'string' ? p.callId : '',
138
+ ...(typeof p.toolCallId === 'string' ? { toolCallId: p.toolCallId } : {}),
139
+ ...(typeof p.name === 'string' ? { name: p.name } : {}),
140
+ ...(isObj(p.input) ? { input: p.input } : {}),
141
+ ...(typeof p.timeoutAt === 'string' ? { timeoutAt: p.timeoutAt } : {}),
142
+ ...(outcome ? { outcome } : {}),
143
+ ...(typeof p.isError === 'boolean' ? { isError: p.isError } : {}),
144
+ ...(typeof p.resolvedBy === 'string' ? { resolvedBy: p.resolvedBy } : {}),
145
+ ...(typeof p.resolutionChannel === 'string' ? { resolutionChannel: p.resolutionChannel } : {}),
146
+ };
147
+ }
127
148
  /**
128
149
  * Tool activity for an `agent.tool` event; undefined for every other type.
129
150
  *
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { createZooworkClient, DEFAULT_BASE_URL, ZooworkError, type ZooworkClient, type ZooworkConfig, type ZooworkAuth, type Ownership, type ModelInfo, type AgentResource, type AgentRecord, type AgentListParams, type AgentPage, type AgentPagePromise, type AgentStatus, type AgentSkill, type AgentChannel, type ChannelPlatform, type AddChannelPlatform, type GuidedSetupPlatform, type AddChannelInput, type UpdateChannelInput, type ChannelSetupInput, type ChannelSetupSession, type ChannelPollResult, type FeishuSetupInput, type FeishuSetupSession, type FeishuPollResult, type McpServerDeclaration, type SkillRecord, type SkillVersionRecord, type SessionRecord, type SessionHistoryEntry, type SessionEvent, type SessionEventPage, type OutboundEvent, type PostEventReceipt, type ApprovalDecision, type ApprovalRecord, type ArtifactPage, type ArtifactRecord, type ArtifactStatus, type OutcomeConfig, type OutcomeEvaluator, type SystemPromptDeclaration, type SystemPromptInfo, type SystemPromptPreview, type SystemPromptPreviewInput, type SystemPromptUpgrade, type ScheduleSpec, type SchedulePayload, type ScheduleInput, type ScheduleUpdate, type ScheduleRecord, type ScheduleRun, type WakeResult, type ExecResult, type EnvironmentConfig, type EnvironmentResource, type EnvironmentRecord, type EnvironmentVersionRecord, } from './client.js';
2
- export { SESSION_EVENT_TYPES, type SessionEventType, PUBLIC_INPUT_EVENT_TYPES, type PublicInputEventType, normalizeEvent, isRunFinished, runOutcome, messageText, assistantText, thinkingText, toolCall, type ToolCall, } from './events.js';
1
+ export { createZooworkClient, DEFAULT_BASE_URL, ZooworkError, type ZooworkClient, type ZooworkConfig, type ZooworkAuth, type Ownership, type ModelInfo, type AgentResource, type AgentRecord, type AgentListParams, type AgentPage, type AgentPagePromise, type AgentStatus, type AgentSkill, type AgentChannel, type AgentChannelCapabilitySync, type AgentChannelCapabilities, type FeishuChannelProviderStatus, type FeishuDocumentsCapability, type ChannelPlatform, type AddChannelPlatform, type GuidedSetupPlatform, type AddChannelInput, type UpdateChannelInput, type ChannelSetupInput, type ChannelSetupSession, type ChannelPollResult, type FeishuSetupInput, type FeishuSetupSession, type FeishuPollResult, type McpContextConfig, type McpServerDeclaration, type McpToolPermission, type McpToolPermissionOverride, type CustomToolDeclaration, type CustomToolResultImageMimeType, type CustomToolResultContent, type CustomToolResultEvent, type SkillRecord, type SkillVersionRecord, type SessionRecord, type SessionListPageOptions, type SessionListPage, type SessionHistoryEntry, type SessionEvent, type SessionEventPage, type OutboundEvent, type PostEventReceipt, type ApprovalDecision, type ApprovalRecord, type CustomToolCallStatus, type CustomToolCallRecord, type ArtifactPage, type ArtifactRecord, type ArtifactStatus, type OutcomeConfig, type OutcomeEvaluator, type SystemPromptDeclaration, type SystemPromptInfo, type SystemPromptPreview, type SystemPromptPreviewInput, type SystemPromptUpgrade, type ScheduleSpec, type SchedulePayload, type ScheduleInput, type ScheduleUpdate, type ScheduleRecord, type ScheduleRun, type WakeResult, type ExecResult, type EnvironmentConfig, type EnvironmentResource, type EnvironmentRecord, type EnvironmentVersionRecord, } from './client.js';
2
+ export { SESSION_EVENT_TYPES, type SessionEventType, PUBLIC_INPUT_EVENT_TYPES, type PublicInputEventType, normalizeEvent, isRunFinished, runOutcome, messageText, assistantText, thinkingText, customToolUse, type CustomToolUse, toolCall, type ToolCall, } from './events.js';
3
3
  export { parseSSE, type SSEMessage } from './sse.js';
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { createZooworkClient, DEFAULT_BASE_URL, ZooworkError, } from './client.js';
2
- export { SESSION_EVENT_TYPES, PUBLIC_INPUT_EVENT_TYPES, normalizeEvent, isRunFinished, runOutcome, messageText, assistantText, thinkingText, toolCall, } from './events.js';
2
+ export { SESSION_EVENT_TYPES, PUBLIC_INPUT_EVENT_TYPES, normalizeEvent, isRunFinished, runOutcome, messageText, assistantText, thinkingText, customToolUse, toolCall, } from './events.js';
3
3
  export { parseSSE } from './sse.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zoowork-ai/sdk",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "TypeScript SDK for the ZooWork Managed Agents API (Developer Preview)",
5
5
  "keywords": [
6
6
  "zoowork",
@@ -39,12 +39,6 @@
39
39
  "publishConfig": {
40
40
  "access": "public"
41
41
  },
42
- "devDependencies": {
43
- "@types/node": "^22.20.1",
44
- "tsx": "^4.23.7",
45
- "typescript": "^5.6.0",
46
- "vitest": "^3.0.0"
47
- },
48
42
  "scripts": {
49
43
  "build": "tsc -p tsconfig.build.json",
50
44
  "typecheck": "tsc --noEmit",
@@ -55,6 +49,13 @@
55
49
  "test:e2e:offline": "node --import tsx --test --test-reporter=spec e2e/*.test.ts",
56
50
  "e2e:prepare": "node e2e/runner.ts prepare",
57
51
  "e2e:run": "node e2e/runner.ts run",
58
- "e2e:verify": "node e2e/runner.ts verify"
52
+ "e2e:verify": "node e2e/runner.ts verify",
53
+ "prepack": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && npm run build"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^22.20.1",
57
+ "tsx": "^4.23.7",
58
+ "typescript": "^5.6.0",
59
+ "vitest": "^3.0.0"
59
60
  }
60
- }
61
+ }