@zoowork-ai/sdk 0.4.1 → 0.5.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,52 @@
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.5.0 — 2026-08-28
7
+
8
+ ### Added
9
+
10
+ - **The QR flow now covers WeCom and WeChat, not just Feishu** — gateway PR #3512 shipped
11
+ `/channels/{wecom,weixin}/{setup,poll,setup-cancel}`, and 0.4.x had no way to call them.
12
+ Four platform-taking methods replace the four Feishu-only ones:
13
+ `startChannelSetup(agentId, platform, input?)`, `pollChannelSetup`, `cancelChannelSetup`,
14
+ `waitForChannelSetup`. New types `ChannelSetupInput`, `ChannelSetupSession`,
15
+ `ChannelPollResult`, `GuidedSetupPlatform` (`'feishu' | 'wecom' | 'weixin'`) and
16
+ `AddChannelPlatform`.
17
+ - **`startFeishuSetup` / `pollFeishuSetup` / `cancelFeishuSetup` / `waitForFeishuSetup` still
18
+ work** — they now delegate to the platform-taking versions. `FeishuSetupInput` and
19
+ `FeishuPollResult` are aliases of the new names; `FeishuSetupSession` narrows
20
+ `ChannelSetupSession` to the one platform that always answers `verification_uri_complete`.
21
+ Only the message text of a thrown timeout/abort changed (it names `waitForChannelSetup` and
22
+ the platform); `status` and `type` are unchanged.
23
+
24
+ ### Documentation
25
+
26
+ - **`ChannelPlatform` gains `'weixin'`, and WeChat is no longer described as unbindable.**
27
+ 0.3.2–0.4.2 said WeChat "answers `400 channel.weixin_setup_required`, naming a QR flow this
28
+ API does not expose". The flow exists now; that error is a signpost to it, not a dead end.
29
+ `addChannel` still refuses WeChat, so `AddChannelPlatform` is the type that lists what it
30
+ takes.
31
+ - **Per-platform shapes, staging-verified 2026-08-28** (`notes/probes/channels-guided-probe.mts`):
32
+ Feishu answers `verification_uri_complete` + `poll_interval: 5`, `expires_in: 600`; WeCom and
33
+ WeChat answer `qrcode_url` with no `poll_interval` and `expires_in: 300`; WeChat's
34
+ `qrcode_url` may be an inline `data:image/…` payload rather than a URL. WeChat reads only
35
+ `dm_policy` and only `'open'`/`'disabled'` — `'allowlist'` is
36
+ `400 channel.allowlist_unsupported` — pins the account to `'default'`, and ignores anything
37
+ else in the body. Cancelled sessions 404 per platform:
38
+ `channel.{feishu,wecom,weixin}_session_not_found`.
39
+
40
+ ## 0.4.2 — 2026-08-25
41
+
42
+ ### Documentation
43
+
44
+ - **`addChannel` is idempotent, not an upsert — 0.3.4 said the opposite.** Re-posting an
45
+ identical body for the same `platform` + `account` replays the binding you already have and
46
+ answers `201` again; that same pair with a **different** `config` answers
47
+ `409 channel.conflict`. Rotating credentials therefore means `removeChannel` and then a
48
+ fresh `addChannel` — a plain re-add fails. The earlier note came from re-posting the same
49
+ body, where a replay cannot be told apart from an overwrite; the conflict path was measured
50
+ on staging 2026-08-25. No signature or behaviour change.
51
+
6
52
  ## 0.4.1 — 2026-08-25
7
53
 
8
54
  ### Internal
package/dist/client.d.ts CHANGED
@@ -259,22 +259,47 @@ export interface AgentChannel {
259
259
  [k: string]: unknown;
260
260
  }
261
261
  /**
262
- * The chat platforms you can bind, staging-verified 2026-08-25.
262
+ * The chat platforms you can bind, staging-verified 2026-08-28.
263
263
  *
264
- * Only `'feishu'` has a server-driven QR flow here, and the two reasons the others lack one
265
- * are different. Slack structurally cannot have one — a Slack app is created by a person, and
266
- * its tokens only ever exist in that person's browser so `addChannel` with `botToken` +
267
- * `appToken` is its permanent path. WeCom's flow exists in the product but is not exposed on
268
- * this API yet, so today it also binds through `addChannel`.
264
+ * Three of them have a server-driven QR flow ({@link GuidedSetupPlatform}); Slack does not,
265
+ * and structurally cannot — a Slack app is created by a person and its tokens only ever exist
266
+ * in that person's browser, so {@link AddChannelInput} with `botToken` + `appToken` is its
267
+ * permanent path.
269
268
  *
270
- * WeChat (`'weixin'`/`'wechat'`) is absent because it cannot be bound here at all: it answers
271
- * `400 channel.weixin_setup_required`, naming a QR flow this API does not expose. Any other
272
- * name answers `400 channel.invalid_request`.
269
+ * WeChat is the one platform that goes the other way: `'weixin'`/`'wechat'` on
270
+ * {@link ZooworkClient.addChannel} answers `400 channel.weixin_setup_required`, so the QR flow
271
+ * is its ONLY path. See {@link AddChannelPlatform}. Any name outside this type answers
272
+ * `400 channel.invalid_request`.
273
273
  */
274
- export type ChannelPlatform = 'feishu' | 'slack' | 'wecom';
274
+ export type ChannelPlatform = 'feishu' | 'slack' | 'wecom' | 'weixin';
275
+ /**
276
+ * The platforms {@link ZooworkClient.addChannel} accepts — every {@link ChannelPlatform}
277
+ * except WeChat, which refuses explicit config and takes the QR flow only.
278
+ */
279
+ export type AddChannelPlatform = 'feishu' | 'slack' | 'wecom';
280
+ /**
281
+ * The platforms with a server-driven QR flow: {@link ZooworkClient.startChannelSetup} →
282
+ * render the URI → poll. Slack is absent by design, not by omission.
283
+ *
284
+ * The three differ in what the setup answer carries and in what the body may say
285
+ * (staging-verified 2026-08-28):
286
+ *
287
+ * - `feishu` — answers `verification_uri_complete` and a `poll_interval`; takes `brand`,
288
+ * `account`, `dm_policy`, `group_policy`; `expires_in: 600`.
289
+ * - `wecom` — answers `qrcode_url` and NO `poll_interval` (you pick the cadence); takes
290
+ * `account`, `dm_policy`, `group_policy`; `expires_in: 300`.
291
+ * - `weixin` — answers `qrcode_url`, which may be a URL *or* an inline `data:image/…` payload;
292
+ * takes `dm_policy` only, `'open'` or `'disabled'` (the account is pinned to `'default'` and
293
+ * the group policy to `'disabled'` server-side); `expires_in: 300`.
294
+ */
295
+ export type GuidedSetupPlatform = 'feishu' | 'wecom' | 'weixin';
275
296
  export interface AddChannelInput {
276
- /** See {@link ChannelPlatform}. Typed loosely so a newly supported platform needs no SDK release. */
277
- platform: ChannelPlatform | (string & {});
297
+ /**
298
+ * See {@link AddChannelPlatform}. Typed loosely so a newly supported platform needs no SDK
299
+ * release. `'weixin'`/`'wechat'` is refused here with `400 channel.weixin_setup_required` —
300
+ * use {@link ZooworkClient.startChannelSetup} instead.
301
+ */
302
+ platform: AddChannelPlatform | (string & {});
278
303
  /**
279
304
  * Names this binding. It is part of the record's identity, not a setting: `updateChannel`
280
305
  * and `removeChannel` find a binding by `platform` + `account`, and nothing renames one
@@ -324,8 +349,14 @@ export interface UpdateChannelInput {
324
349
  group_policy?: string;
325
350
  enabled?: boolean;
326
351
  }
327
- export interface FeishuSetupInput {
328
- /** `'feishu'` (default) or `'lark'` the international brand of the same platform. */
352
+ /**
353
+ * Body for {@link ZooworkClient.startChannelSetup}. Every field is optional, and each platform
354
+ * reads a different subset — see {@link GuidedSetupPlatform}. A field a platform does not read
355
+ * is ignored rather than rejected: `weixin` accepts an `account` in the body and still binds
356
+ * `'default'` (staging-verified 2026-08-28).
357
+ */
358
+ export interface ChannelSetupInput {
359
+ /** Feishu only: `'feishu'` (default) or `'lark'` — the international brand of the same platform. */
329
360
  brand?: 'feishu' | 'lark';
330
361
  /**
331
362
  * Names this binding. It is part of the record's identity, not a setting: `updateChannel`
@@ -351,37 +382,60 @@ export interface FeishuSetupInput {
351
382
  * works as-is: it matches the pattern and is unique per agent by construction.
352
383
  */
353
384
  account?: string;
354
- /** Server default: `'open'`. */
385
+ /**
386
+ * Server default: `'open'`. WeChat takes only `'open'` or `'disabled'` — `'allowlist'` is
387
+ * `400 channel.allowlist_unsupported` there, and `'pairing'` is
388
+ * `400 channel.pairing_unsupported` on every platform.
389
+ */
355
390
  dm_policy?: string;
356
- /** Server default: `'open'`. */
391
+ /** Server default: `'open'`. Ignored by WeChat, which forces `'disabled'`. */
357
392
  group_policy?: string;
358
393
  }
394
+ /** @deprecated Use {@link ChannelSetupInput}; this is the same shape under the old name. */
395
+ export type FeishuSetupInput = ChannelSetupInput;
359
396
  /**
360
- * A running Feishu QR registration. Render `verification_uri_complete` to the person
361
- * doing the binding (typically as a QR code), then poll with `pollFeishuSetup` /
362
- * `waitForFeishuSetup` until it leaves `pending`. The session expires after
363
- * `expires_in` seconds.
397
+ * A running QR registration. Show the person doing the binding whichever URI the platform
398
+ * answered `verification_uri_complete` for Feishu, `qrcode_url` for WeCom and WeChat, so
399
+ * `session.verification_uri_complete ?? session.qrcode_url` is the value to render then poll
400
+ * with {@link ZooworkClient.pollChannelSetup} / {@link ZooworkClient.waitForChannelSetup} until
401
+ * it leaves `pending`. The session expires after `expires_in` seconds.
402
+ *
403
+ * Two of the three are QR-only: WeCom's `qrcode_url` is a URL you encode yourself, and WeChat's
404
+ * may be a URL *or* an inline `data:image/…` payload you render directly, so check the prefix
405
+ * before you feed it to a QR encoder.
364
406
  */
365
- export interface FeishuSetupSession {
407
+ export interface ChannelSetupSession {
366
408
  session_id: string;
367
- verification_uri_complete: string;
409
+ /** Feishu only — the URI to encode into a QR code. */
410
+ verification_uri_complete?: string;
411
+ /** WeCom and WeChat — a URL to encode, or (WeChat) an inline `data:image/…` image. */
412
+ qrcode_url?: string;
368
413
  expires_in: number;
369
- /** Suggested seconds between polls; the server may omit it. */
414
+ /** Suggested seconds between polls. Feishu sends it; WeCom and WeChat never do. */
370
415
  poll_interval?: number | null;
371
416
  [k: string]: unknown;
372
417
  }
373
418
  /**
374
- * One poll of a Feishu setup session. The gateway's own vocabulary for `status` is
375
- * `pending | success | expired | denied | error`; treat anything unknown as
376
- * still-in-flight rather than throwing.
419
+ * A running Feishu QR registration {@link ChannelSetupSession} narrowed to the one platform
420
+ * that always answers `verification_uri_complete`.
421
+ */
422
+ export interface FeishuSetupSession extends ChannelSetupSession {
423
+ verification_uri_complete: string;
424
+ }
425
+ /**
426
+ * One poll of a setup session. The gateway's own vocabulary for `status` is
427
+ * `pending | success | expired | denied | error` (`denied` is Feishu-only); treat anything
428
+ * unknown as still-in-flight rather than throwing.
377
429
  */
378
- export interface FeishuPollResult {
430
+ export interface ChannelPollResult {
379
431
  status: string;
380
432
  channel_configured?: boolean;
381
433
  message?: string | null;
382
434
  poll_interval?: number | null;
383
435
  [k: string]: unknown;
384
436
  }
437
+ /** @deprecated Use {@link ChannelPollResult}; this is the same shape under the old name. */
438
+ export type FeishuPollResult = ChannelPollResult;
385
439
  export interface AgentRecord {
386
440
  agent_id: string;
387
441
  computer_id?: string;
@@ -1053,11 +1107,15 @@ export interface ZooworkClient {
1053
1107
  /**
1054
1108
  * Bind a channel from explicit platform config (the non-QR path) — `config` carries the
1055
1109
  * platform's own credential keys. Answers the created channel (HTTP 201). This is the ONLY
1056
- * path for Slack and WeCom; Feishu also has the QR flow. See {@link ChannelPlatform} for what
1057
- * binds and what does not.
1110
+ * path for Slack, an alternative to the QR flow for Feishu and WeCom, and refused outright
1111
+ * for WeChat (`400 channel.weixin_setup_required`). See {@link AddChannelPlatform}.
1058
1112
  *
1059
- * **It is an upsert, not a create.** Binding the same `platform` + `account` twice answers
1060
- * `201` again and overwrites the first binding rather than conflicting.
1113
+ * **It is idempotent, not an upsert.** Re-posting an identical body for the same
1114
+ * `platform` + `account` answers `201` again and replays the binding you already have — it
1115
+ * neither creates a second channel nor overwrites the first. That same pair with a DIFFERENT
1116
+ * `config` answers `409 channel.conflict`, so rotating credentials means
1117
+ * {@link removeChannel} and then a fresh `addChannel`; a plain re-add fails
1118
+ * (staging-verified 2026-08-25).
1061
1119
  *
1062
1120
  * ⚠️ **201 means STORED, not WORKING.** Credentials are not validated at bind time: a channel
1063
1121
  * created from deliberately bogus credentials still answered 201 with `health: 'unknown'`,
@@ -1086,52 +1144,70 @@ export interface ZooworkClient {
1086
1144
  account?: string;
1087
1145
  }): Promise<void>;
1088
1146
  /**
1089
- * Start the Feishu/Lark QR registration. YOU own the UI: render
1090
- * `verification_uri_complete` (usually as a QR code) and drive the poll loop —
1091
- * `waitForFeishuSetup` does the loop part for you.
1147
+ * Start a QR registration on one of the three guided platforms. YOU own the UI: render the
1148
+ * URI the session answers (`verification_uri_complete` for Feishu, `qrcode_url` for WeCom and
1149
+ * WeChat) and drive the poll loop — {@link waitForChannelSetup} does the loop part for you.
1150
+ *
1151
+ * What each platform reads from `input`, and what it answers, is in
1152
+ * {@link GuidedSetupPlatform}; the short version is that only Feishu takes `brand`, only
1153
+ * Feishu and WeCom take `account`, and only Feishu sends back a `poll_interval`.
1092
1154
  *
1093
- * Observed defaults: `expires_in: 600`, `poll_interval: 5`. `brand` picks the real host —
1094
- * `'feishu'` answers an `open.feishu.cn` URI, `'lark'` an `open.larksuite.com` one, so the
1095
- * brand has to match the workspace the person will approve it in.
1155
+ * For Feishu, `brand` picks the real host — `'feishu'` answers an `open.feishu.cn` URI,
1156
+ * `'lark'` an `open.larksuite.com` one so it has to match the workspace the person will
1157
+ * approve it in.
1096
1158
  *
1097
- * Pick `account` before you show the QR. Approving the scan registers a NEW app in that
1098
- * Feishu workspace, and only then does the binding get written — so a name clash surfaces as
1159
+ * Pick `account` before you show the QR. On Feishu, approving the scan registers a NEW app in
1160
+ * that workspace, and only then does the binding get written — so a name clash surfaces as
1099
1161
  * `409 channel.conflict` AFTER someone has scanned, with the freshly registered app left
1100
1162
  * behind in their workspace. Retrying under the same name repeats both. See
1101
1163
  * {@link AddChannelInput.account} for how names are scoped.
1102
1164
  */
1103
- startFeishuSetup(agentId: string, input?: FeishuSetupInput): Promise<FeishuSetupSession>;
1165
+ startChannelSetup(agentId: string, platform: GuidedSetupPlatform, input?: ChannelSetupInput): Promise<ChannelSetupSession>;
1104
1166
  /**
1105
- * One poll of a setup session. `status: 'pending'` means keep going; a cancelled or expired
1106
- * session answers `404 channel.feishu_session_not_found` rather than a terminal status, so
1107
- * a hand-rolled loop must treat that 404 as an end condition, not as a transport error.
1167
+ * One poll of a setup session. `status: 'pending'` means keep going; a cancelled session
1168
+ * answers `404 channel.{platform}_session_not_found` rather than a terminal status, so a
1169
+ * hand-rolled loop must treat that 404 as an end condition, not as a transport error.
1108
1170
  */
1109
- pollFeishuSetup(agentId: string, sessionId: string): Promise<FeishuPollResult>;
1110
- /** Abandon a setup session. Afterwards polling it answers `404 channel.feishu_session_not_found`. */
1111
- cancelFeishuSetup(agentId: string, sessionId: string): Promise<void>;
1171
+ pollChannelSetup(agentId: string, platform: GuidedSetupPlatform, sessionId: string): Promise<ChannelPollResult>;
1172
+ /** Abandon a setup session. Afterwards polling it answers `404 channel.{platform}_session_not_found`. */
1173
+ cancelChannelSetup(agentId: string, platform: GuidedSetupPlatform, sessionId: string): Promise<void>;
1112
1174
  /**
1113
- * Poll a Feishu setup session until it leaves `pending`, then hand back that terminal poll.
1175
+ * Poll a setup session until it leaves `pending`, then hand back that terminal poll.
1114
1176
  * A status the server reports in the body — `success` / `expired` / `denied` / `error` — is
1115
1177
  * RETURNED, not thrown: "the person rejected it" is an outcome, not an exception.
1116
1178
  *
1117
1179
  * But a session can also stop existing, and then polling answers
1118
- * `404 channel.feishu_session_not_found`, which surfaces here as a thrown
1119
- * {@link ZooworkError} carrying that `type`. Confirmed for a cancelled session
1120
- * (staging 2026-08-25); whether a session that simply runs past `expires_in` reports
1121
- * `status: 'expired'` in a 200 or disappears into this 404 was NOT observed — handle both.
1180
+ * `404 channel.{platform}_session_not_found`, which surfaces here as a thrown
1181
+ * {@link ZooworkError} carrying that `type`. Confirmed for a cancelled session on all three
1182
+ * platforms (staging 2026-08-28); whether a session that simply runs past `expires_in`
1183
+ * reports `status: 'expired'` in a 200 or disappears into this 404 was NOT observed — handle
1184
+ * both.
1122
1185
  *
1123
- * Pacing follows the server's `poll_interval` when present (observed default 5s; the local
1124
- * fallback matches). The default budget is 600s, which is also the observed `expires_in` —
1125
- * pass the session's own value when you have it. On timeout it throws `status: 408` /
1126
- * `type: 'timeout'`; on abort, `status: 0` / `type: 'aborted'` both synthesized locally,
1127
- * and every in-flight poll is bounded the way {@link waitUntilRunning} bounds its polls.
1128
- * `onPoll` fires after every poll, terminal one included, for progress UI.
1186
+ * Pacing follows the server's `poll_interval` when present (Feishu sends 5s; WeCom and WeChat
1187
+ * send none and fall back to the same 5s). The default budget is 600s, which matches Feishu's
1188
+ * `expires_in` but is twice WeCom's and WeChat's 300s — pass the session's own value when you
1189
+ * have it. On timeout it throws `status: 408` / `type: 'timeout'`; on abort, `status: 0` /
1190
+ * `type: 'aborted'` — both synthesized locally, and every in-flight poll is bounded the way
1191
+ * {@link waitUntilRunning} bounds its polls. `onPoll` fires after every poll, terminal one
1192
+ * included, for progress UI.
1129
1193
  */
1194
+ waitForChannelSetup(agentId: string, platform: GuidedSetupPlatform, sessionId: string, opts?: {
1195
+ timeoutMs?: number;
1196
+ signal?: AbortSignal;
1197
+ onPoll?: (poll: ChannelPollResult) => void;
1198
+ }): Promise<ChannelPollResult>;
1199
+ /** Feishu-only spelling of {@link startChannelSetup}, kept for callers written against 0.3.x–0.4.x. */
1200
+ startFeishuSetup(agentId: string, input?: ChannelSetupInput): Promise<FeishuSetupSession>;
1201
+ /** Feishu-only spelling of {@link pollChannelSetup}. */
1202
+ pollFeishuSetup(agentId: string, sessionId: string): Promise<ChannelPollResult>;
1203
+ /** Feishu-only spelling of {@link cancelChannelSetup}. */
1204
+ cancelFeishuSetup(agentId: string, sessionId: string): Promise<void>;
1205
+ /** Feishu-only spelling of {@link waitForChannelSetup}. */
1130
1206
  waitForFeishuSetup(agentId: string, sessionId: string, opts?: {
1131
1207
  timeoutMs?: number;
1132
1208
  signal?: AbortSignal;
1133
- onPoll?: (poll: FeishuPollResult) => void;
1134
- }): Promise<FeishuPollResult>;
1209
+ onPoll?: (poll: ChannelPollResult) => void;
1210
+ }): Promise<ChannelPollResult>;
1135
1211
  /**
1136
1212
  * The agent's system-prompt pin and the rendered template in effect. Staging-verified
1137
1213
  * 2026-08-14 — a fresh agent answers a real `declaration` (`{source:'platform',version:1}`),
package/dist/client.js CHANGED
@@ -336,19 +336,20 @@ export function createZooworkClient(cfg = {}) {
336
336
  body: JSON.stringify({ account: opts.account ?? 'default' }),
337
337
  });
338
338
  },
339
- startFeishuSetup: (agentId, input = {}) => json(`${agents(agentId)}/channels/feishu/setup`, { method: 'POST', body: JSON.stringify(input) }),
340
- pollFeishuSetup: (agentId, sessionId) => json(`${agents(agentId)}/channels/feishu/poll${query({ session_id: sessionId })}`),
341
- cancelFeishuSetup: async (agentId, sessionId) => {
342
- await json(`${agents(agentId)}/channels/feishu/setup/cancel${query({ session_id: sessionId })}`, {
343
- method: 'POST',
344
- });
339
+ startChannelSetup: (agentId, platform, input = {}) => json(`${agents(agentId)}/channels/${encodeURIComponent(platform)}/setup`, {
340
+ method: 'POST',
341
+ body: JSON.stringify(input),
342
+ }),
343
+ pollChannelSetup: (agentId, platform, sessionId) => json(`${agents(agentId)}/channels/${encodeURIComponent(platform)}/poll${query({ session_id: sessionId })}`),
344
+ cancelChannelSetup: async (agentId, platform, sessionId) => {
345
+ await json(`${agents(agentId)}/channels/${encodeURIComponent(platform)}/setup/cancel${query({ session_id: sessionId })}`, { method: 'POST' });
345
346
  },
346
- waitForFeishuSetup: async (agentId, sessionId, opts = {}) => {
347
+ waitForChannelSetup: async (agentId, platform, sessionId, opts = {}) => {
347
348
  const timeoutMs = opts.timeoutMs ?? 600_000;
348
349
  const deadline = Date.now() + timeoutMs;
349
350
  let lastStatus = 'unknown';
350
- const abortedError = () => new ZooworkError(0, `waitForFeishuSetup(${agentId}, ${sessionId}) aborted`, 'aborted');
351
- const timeoutError = () => new ZooworkError(408, `Feishu setup session ${sessionId} still '${lastStatus}' after ${timeoutMs}ms — ` +
351
+ const abortedError = () => new ZooworkError(0, `waitForChannelSetup(${agentId}, ${platform}, ${sessionId}) aborted`, 'aborted');
352
+ const timeoutError = () => new ZooworkError(408, `${platform} setup session ${sessionId} still '${lastStatus}' after ${timeoutMs}ms — ` +
352
353
  'the QR may simply not have been scanned yet; the session itself expires server-side', 'timeout');
353
354
  for (;;) {
354
355
  if (opts.signal?.aborted)
@@ -364,7 +365,7 @@ export function createZooworkClient(cfg = {}) {
364
365
  const budget = setTimeout(cancelPoll, remaining);
365
366
  let result;
366
367
  try {
367
- result = await json(`${agents(agentId)}/channels/feishu/poll${query({ session_id: sessionId })}`, { signal: poll.signal });
368
+ result = await json(`${agents(agentId)}/channels/${encodeURIComponent(platform)}/poll${query({ session_id: sessionId })}`, { signal: poll.signal });
368
369
  }
369
370
  catch (e) {
370
371
  if (poll.signal.aborted)
@@ -378,7 +379,7 @@ export function createZooworkClient(cfg = {}) {
378
379
  opts.onPoll?.(result);
379
380
  lastStatus = result.status ?? 'unknown';
380
381
  // Only a literal 'pending' keeps the loop alive… except that an UNKNOWN status is
381
- // treated as still-in-flight too (see FeishuPollResult): a new intermediate state on
382
+ // treated as still-in-flight too (see ChannelPollResult): a new intermediate state on
382
383
  // the server should stretch the wait, not end it with a fake terminal result.
383
384
  const terminal = ['success', 'expired', 'denied', 'error'].includes(lastStatus);
384
385
  if (terminal)
@@ -391,6 +392,12 @@ export function createZooworkClient(cfg = {}) {
391
392
  await sleep(intervalMs, opts.signal);
392
393
  }
393
394
  },
395
+ // The Feishu-only spellings, kept for callers written against 0.3.x–0.4.x. `startFeishuSetup`
396
+ // narrows the return type only — Feishu always answers `verification_uri_complete`.
397
+ startFeishuSetup: (agentId, input = {}) => client.startChannelSetup(agentId, 'feishu', input),
398
+ pollFeishuSetup: (agentId, sessionId) => client.pollChannelSetup(agentId, 'feishu', sessionId),
399
+ cancelFeishuSetup: (agentId, sessionId) => client.cancelChannelSetup(agentId, 'feishu', sessionId),
400
+ waitForFeishuSetup: (agentId, sessionId, opts = {}) => client.waitForChannelSetup(agentId, 'feishu', sessionId, opts),
394
401
  uploadSkill: (zip, opts) => {
395
402
  const form = skillForm(zip, opts);
396
403
  form.append('scope', opts.scope);
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 AgentStatus, type AgentSkill, type AgentChannel, type ChannelPlatform, type AddChannelInput, type UpdateChannelInput, type FeishuSetupInput, type FeishuSetupSession, type FeishuPollResult, type McpServerDeclaration, type SkillRecord, 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';
1
+ export { createZooworkClient, DEFAULT_BASE_URL, ZooworkError, type ZooworkClient, type ZooworkConfig, type ZooworkAuth, type Ownership, type ModelInfo, type AgentResource, type AgentRecord, 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 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
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';
3
3
  export { parseSSE, type SSEMessage } from './sse.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zoowork-ai/sdk",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "TypeScript SDK for the ZooWork Managed Agents API (Developer Preview)",
5
5
  "keywords": [
6
6
  "zoowork",