@qoniai/qoni 0.4.0 → 0.4.2

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
@@ -6,8 +6,65 @@ This project follows semantic versioning. Before `1.0.0`, minor versions may
6
6
  include SDK API changes, while patch versions are reserved for backward
7
7
  compatible fixes.
8
8
 
9
+ ## 0.4.2 - 2026-09-09
10
+
11
+ ### Fixed
12
+
13
+ - Require authoritative run details when settling `wait()`: preserve detail
14
+ errors and reject unconfirmed terminal states instead of inventing success.
15
+ - Reuse terminal details read after a clean stream close and resume from every
16
+ wire event cursor, including frames filtered from semantic events.
17
+ - Propagate wait cancellation to detail and artifact-list GETs. Bound complete
18
+ JSON, NDJSON, binary, and HTTP error body reads by the request timeout, cancel
19
+ stalled readers, and discard late responses without starting replacement runs.
20
+ - Clean up wait timers and SSE abort listeners. Cancel idle SSE readers directly
21
+ and release their locks without waiting indefinitely for source cancellation.
22
+ - Retry HTTP 408 stream failures within the configured reconnect budget and stop
23
+ delivering buffered events after cancellation.
24
+
25
+ ### Added
26
+
27
+ - Optional abort signals on product run-detail and artifact-list wire reads;
28
+ existing calls without a signal remain compatible.
29
+
9
30
  ## Unreleased
10
31
 
32
+ ### Breaking
33
+
34
+ - Rename the public SDK constructor from its previous longer name to `Qoni`.
35
+ The previous constructor export has been removed.
36
+
37
+ ### Changed
38
+
39
+ - The default Qoni host is now `https://dashboard.qoni.ai` (was
40
+ `https://anima.eazo.ai/dashboard`). Callers that never passed an explicit `host`
41
+ reach a different control plane after upgrading. Note that the new default has
42
+ no `/dashboard` path segment — pass `host` explicitly for private or local
43
+ deployments.
44
+ - Service resolution no longer fetches the runtime-config document to answer a
45
+ Qoni base URL. It never read the result for that service, but it did require
46
+ the fetch to succeed, so an unavailable discovery endpoint failed every Qoni
47
+ call. Discovery is still fetched lazily for `genauth` / `gumem` / `webagent`,
48
+ which do read it.
49
+ - Signed Qoni requests always stay on the configured control-plane host. Service
50
+ resolution no longer follows `eakBaseUrl` from the runtime config for the
51
+ Qoni service, because AK/SK signatures are scoped to that endpoint and a
52
+ cross-origin runtime URL can strip `Authorization` on redirect or disclose
53
+ credentials to an unintended origin.
54
+ - `unstableRequest()` without a `token` now signs the call with AK/SK instead of
55
+ sending it unauthenticated. Caller-supplied `authorization`, `content-type`,
56
+ `date` and `x-authing-*` headers are dropped so they cannot corrupt the
57
+ signature.
58
+
59
+ ### Fixed
60
+
61
+ - The previous default host was unreachable for SDK API calls. A client created
62
+ as `new Qoni({ accessKey, secretKey })` now reaches the live control plane.
63
+ - Abandoning an event iterator no longer leaks its connection. Leaving
64
+ `for await (const event of run.events())` through a `break` now cancels the
65
+ response body before releasing the reader lock, returning the socket to the
66
+ connection pool.
67
+
11
68
  ### Added
12
69
 
13
70
  - `RunHandle.events()` and `wait()` accept `onWireEvent` and `onReconnect`
@@ -23,8 +80,8 @@ compatible fixes.
23
80
  ### Breaking
24
81
 
25
82
  - Rename the package from its previous product name to `@qoniai/qoni`.
26
- - Rename the primary constructor and all branded exports to `QoniAgentKit`,
27
- `Qoni`, and the corresponding `Qoni*` types, constants, and error classes.
83
+ - Rename the primary constructor and all branded exports to `Qoni` and the
84
+ corresponding `Qoni*` types, constants, and error classes.
28
85
  - Rename the product namespace to `client.qoni` and environment variables to
29
86
  `QONI_*`. The current server wire contract remains unchanged: SDK gateway
30
87
  routes stay under `/api/v3/eak/*`, delegation tokens retain
@@ -207,7 +264,7 @@ Initial public release of `@qoniai/qoni`.
207
264
 
208
265
  ### What ships
209
266
 
210
- - Server-side `QoniAgentKit` constructor for Qoni Agent delegation and product
267
+ - Server-side `Qoni` constructor for Qoni Agent delegation and product
211
268
  runtime calls.
212
269
  - Runtime service discovery from the Qoni Console gateway through
213
270
  `GET /api/v3/eak/runtime-config`.
@@ -226,7 +283,7 @@ Initial public release of `@qoniai/qoni`.
226
283
 
227
284
  ### Compatibility
228
285
 
229
- - `Qoni` remains available as a short constructor alias.
286
+ - `Qoni` is the public SDK constructor.
230
287
  - `delegateAgent` and `completeDelegateAgent` remain available as deprecated
231
288
  aliases for older integrations, but new integrations should use
232
289
  `delegateToken`.
package/README.md CHANGED
@@ -19,7 +19,7 @@ Agents that perform real work need more than a backend API key. They need a user
19
19
 
20
20
  Qoni keeps that model small:
21
21
 
22
- - One SDK entry: `new QoniAgentKit({ accessKey, secretKey })`.
22
+ - One SDK entry: `new Qoni({ accessKey, secretKey })`.
23
23
  - One discovery path: `host` can override the Qoni Console/SDK gateway for private or local deployments, and the SDK reads downstream runtime URLs from `/api/v3/eak/runtime-config`.
24
24
  - One delegation entry: call `delegateToken`; silent mode returns `data.token`, while interactive mode returns an authorization URL and later completes on your server.
25
25
  - One management path: call `genauth.users.*`, and the SDK exchanges AK/SK for a GenAuth management token before calling GenAuth v3 users APIs.
@@ -59,8 +59,8 @@ The package ships both ESM and CommonJS builds with TypeScript types, so either
59
59
  import style works:
60
60
 
61
61
  ```ts
62
- import { QoniAgentKit } from "@qoniai/qoni"; // ESM / TypeScript
63
- const { QoniAgentKit } = require("@qoniai/qoni"); // CommonJS
62
+ import { Qoni } from "@qoniai/qoni"; // ESM / TypeScript
63
+ const { Qoni } = require("@qoniai/qoni"); // CommonJS
64
64
  ```
65
65
 
66
66
  ## AI Skill
@@ -87,9 +87,9 @@ Start with a server-side SDK instance. AK/SK stays on your trusted server for bo
87
87
 
88
88
  ```ts
89
89
  import fs from "node:fs";
90
- import { QoniAgentKit } from "@qoniai/qoni";
90
+ import { Qoni } from "@qoniai/qoni";
91
91
 
92
- const qoni = new QoniAgentKit({
92
+ const qoni = new Qoni({
93
93
  accessKey: process.env.QONI_ACCESS_KEY!,
94
94
  secretKey: process.env.QONI_SECRET_KEY!,
95
95
  });
@@ -472,7 +472,7 @@ const sameMonitor = await qoni.track.attach(monitor.id, { token });
472
472
  ### GenAuth User Context and Management
473
473
 
474
474
  ```ts
475
- const client = new QoniAgentKit({
475
+ const client = new Qoni({
476
476
  accessKey: process.env.QONI_ACCESS_KEY!,
477
477
  secretKey: process.env.QONI_SECRET_KEY!,
478
478
  });
@@ -499,7 +499,7 @@ const created = await client.genauth.users.create({
499
499
  ### Client
500
500
 
501
501
  ```ts
502
- const qoni = new QoniAgentKit({
502
+ const qoni = new Qoni({
503
503
  accessKey: process.env.QONI_ACCESS_KEY!,
504
504
  secretKey: process.env.QONI_SECRET_KEY!,
505
505
  timeoutMs: 30_000,
@@ -512,7 +512,7 @@ const qoni = new QoniAgentKit({
512
512
  | `secretKey` | `string` | Yes | Qoni secret key from Qoni Console. |
513
513
  | `host` | `string` | No | Optional Qoni Console/SDK gateway override for private or local deployments. |
514
514
  | `fetch` | `typeof fetch` | No | Custom transport implementation. |
515
- | `timeoutMs` | `number` | No | Per-request timeout (connect + response headers). Defaults to `30000`. |
515
+ | `timeoutMs` | `number` | No | Per-request timeout (connect + headers + body for one-shot requests; headers only for successful SSE connections). Defaults to `30000`. |
516
516
 
517
517
  > **`timeoutMs` does not bound a long-running run.** It applies to individual
518
518
  > one-shot HTTP requests (delegation, `status()`, a single POST). The live
@@ -524,12 +524,6 @@ const qoni = new QoniAgentKit({
524
524
  > `wait()` timeout does not stop the run; it keeps executing server-side and
525
525
  > you can `attach()` to it again.
526
526
 
527
- `Qoni` is also exported as a short alias:
528
-
529
- ```ts
530
- import { Qoni, QoniAgentKit } from "@qoniai/qoni";
531
- ```
532
-
533
527
  ### Namespaces
534
528
 
535
529
  | Namespace | Methods |
@@ -556,6 +550,26 @@ await run.cancel(reason?) // idempotent
556
550
 
557
551
  `await run.wait(...)` settles to a `RunResult`:
558
552
 
553
+ The run-detail response must confirm a terminal state. Detail read errors propagate
554
+ unchanged; `wait()` never substitutes an SSE completion payload for the authoritative
555
+ result. A nonterminal or incomplete detail rejects with a retryable `upstream.failed`
556
+ error; use `status()` or re-attach to recover.
557
+
558
+ `timeoutMs` and `signal` stop waiting for detail/artifact reads and promises returned
559
+ by `onEvent`, `onWireEvent`, `onInteraction`, and `onScreenshot`. They cannot preempt
560
+ synchronous JavaScript or stop work already started inside a callback. `onReconnect`
561
+ is synchronous telemetry: its return value is not awaited and returned promise
562
+ rejections are not handled. Do not pass an async callback there; synchronous throws
563
+ propagate. Reconnect backoff remains abortable and subject to the wait deadline.
564
+
565
+ Detail/artifact-list GETs receive the wait signal: aborting or timing out a wait aborts
566
+ the request and cancels its body reader. The client's per-request timer also covers
567
+ complete JSON/NDJSON and binary body reads, including HTTP error bodies. A late response
568
+ from a custom fetch that ignores abort has its body canceled and cannot resume settlement
569
+ or start subsequent run requests. Custom transports must honor `RequestInit.signal` to
570
+ stop their own in-flight network work. Stopping a wait neither cancels the server-side
571
+ run nor creates a replacement task; use `cancel()` explicitly to stop the existing run.
572
+
559
573
  ```ts
560
574
  interface RunResult {
561
575
  runId: string;
package/README.zh-CN.md CHANGED
@@ -14,7 +14,7 @@ AK/SK 必须只保存在可信服务端,不能下发到浏览器、移动端
14
14
 
15
15
  Qoni 把 Agent 授权模型收敛成几个稳定边界:
16
16
 
17
- - 一个 SDK 入口:`new QoniAgentKit({ accessKey, secretKey })`。
17
+ - 一个 SDK 入口:`new Qoni({ accessKey, secretKey })`。
18
18
  - 一条发现路径:`host` 可在私有化或本地部署中覆盖 Qoni Console/SDK 网关,SDK 通过 `/api/v3/eak/runtime-config` 获取 GenAuth、GUMem、WebAgent 等运行时地址。
19
19
  - 一个授权入口:调用 `delegateToken`;silent 模式直接返回 `data.token`,interactive 模式返回授权 URL,之后由业务服务端完成兑换。
20
20
  - 一条管理面路径:调用 `genauth.users.*`,SDK 会先用 AK/SK 换取 GenAuth management token,再调用 GenAuth v3 users API。
@@ -52,8 +52,8 @@ yarn add @qoniai/qoni
52
52
  包同时发布 ESM 和 CommonJS 两套构建(都带 TypeScript 类型),两种引入方式都可用:
53
53
 
54
54
  ```ts
55
- import { QoniAgentKit } from "@qoniai/qoni"; // ESM / TypeScript
56
- const { QoniAgentKit } = require("@qoniai/qoni"); // CommonJS
55
+ import { Qoni } from "@qoniai/qoni"; // ESM / TypeScript
56
+ const { Qoni } = require("@qoniai/qoni"); // CommonJS
57
57
  ```
58
58
 
59
59
  ## AI Skill
@@ -80,9 +80,9 @@ npm 包中包含 `skills/` 目录,但推荐仍使用上面的 GitHub 仓库安
80
80
 
81
81
  ```ts
82
82
  import fs from "node:fs";
83
- import { QoniAgentKit } from "@qoniai/qoni";
83
+ import { Qoni } from "@qoniai/qoni";
84
84
 
85
- const qoni = new QoniAgentKit({
85
+ const qoni = new Qoni({
86
86
  accessKey: process.env.QONI_ACCESS_KEY!,
87
87
  secretKey: process.env.QONI_SECRET_KEY!,
88
88
  });
@@ -442,7 +442,7 @@ const sameMonitor = await qoni.track.attach(monitor.id, { token });
442
442
  ### GenAuth 用户上下文与用户管理
443
443
 
444
444
  ```ts
445
- const client = new QoniAgentKit({
445
+ const client = new Qoni({
446
446
  accessKey: process.env.QONI_ACCESS_KEY!,
447
447
  secretKey: process.env.QONI_SECRET_KEY!,
448
448
  });
@@ -469,7 +469,7 @@ const created = await client.genauth.users.create({
469
469
  ### Client
470
470
 
471
471
  ```ts
472
- const qoni = new QoniAgentKit({
472
+ const qoni = new Qoni({
473
473
  accessKey: process.env.QONI_ACCESS_KEY!,
474
474
  secretKey: process.env.QONI_SECRET_KEY!,
475
475
  timeoutMs: 30_000,
@@ -482,16 +482,10 @@ const qoni = new QoniAgentKit({
482
482
  | `secretKey` | `string` | 是 | Qoni Console 创建的 secret key。 |
483
483
  | `host` | `string` | 否 | 私有化或本地部署时可选的 Qoni Console/SDK 网关覆盖地址。 |
484
484
  | `fetch` | `typeof fetch` | 否 | 自定义传输实现。 |
485
- | `timeoutMs` | `number` | 否 | 单次请求超时(连接 + 响应头),默认 `30000`。 |
485
+ | `timeoutMs` | `number` | 否 | 单次请求超时(一次性请求覆盖连接、响应头及响应体;成功建立的 SSE 仅覆盖到响应头),默认 `30000`。 |
486
486
 
487
487
  > **`timeoutMs` 不会限制长任务。** 它只作用于单次一次性 HTTP 请求(委托、`status()`、单个 POST)。事件流(`events()` / `wait()`)**不受它约束**——流的响应头一到,这个单次计时器就被清掉了,所以一个跑一小时的 Deep Research 在默认 30s 客户端超时下也能正常流式。要限制等待时长用 `wait({ timeoutMs })`;不传则 `wait()` 一直等到终态。`wait()` 超时**不会停止 run**——它在服务端继续跑,可以再 `attach()` 回去。
488
488
 
489
- 兼容导出:
490
-
491
- ```ts
492
- import { Qoni, QoniAgentKit } from "@qoniai/qoni";
493
- ```
494
-
495
489
  ### 命名空间
496
490
 
497
491
  | 命名空间 | 方法 |
@@ -518,6 +512,19 @@ await run.cancel(reason?) // 幂等
518
512
 
519
513
  `await run.wait(...)` 结算成一个 `RunResult`:
520
514
 
515
+ 必须由 run-detail 响应确认终态。详情读取错误原样抛出,`wait()` 不会用 SSE 完成事件替代权威结果。
516
+ 详情仍为非终态或缺少终态信息时,抛出可重试的 `upstream.failed`;可通过 `status()` 或重新 `attach()` 恢复。
517
+
518
+ `timeoutMs` 与 `signal` 会结束详情、交付物读取及 `onEvent`、`onWireEvent`、`onInteraction`、
519
+ `onScreenshot` 返回的 Promise 的等待,但不能抢占同步 JavaScript,也不能停止回调内部已启动的工作。
520
+ `onReconnect` 是同步遥测通知:返回值不被等待,返回的 Promise 的 rejection 也不被处理,因此不要传 async
521
+ 回调;同步抛错会向上传播。重连退避等待仍可被 signal 或 wait 超时中断。
522
+
523
+ 详情/交付物列表 GET 会接收 wait signal:wait 中止或超时时会中止请求、取消并释放响应体 reader。
524
+ 客户端单次请求计时器也覆盖完整 JSON/NDJSON、二进制响应体及 HTTP 错误体读取。即使自定义 fetch 忽略
525
+ abort,SDK 也会取消其迟到响应体,不恢复结算或启动后续 run 请求;自定义传输须遵守 `RequestInit.signal`
526
+ 才能停止其内部网络工作。停止 wait 既不会取消服务端 run,也不会创建替代任务;需要停止原 run 时须显式调用 `cancel()`。
527
+
521
528
  ```ts
522
529
  interface RunResult {
523
530
  runId: string;