@qoniai/qoni 0.5.1 → 0.6.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
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 - 2026-09-23
4
+
5
+ ### Added
6
+
7
+ - Deliver `browserLiveUrlChanged` on semantic run streams when the browser
8
+ viewer URL changes. Applications can follow the active tab instead of
9
+ remaining on the initial blank page. The event includes the live viewer URL
10
+ and refresh hint, without exposing the private CDP URL in `event.data`.
11
+
12
+ ## 0.5.2 - 2026-09-23
13
+
14
+ ### Fixed
15
+
16
+ - Keep permission errors concise instead of appending every known scope.
17
+ - Explain how to enable a permission policy when an AccessKey has no allowed
18
+ scopes. Preserve the backend error code and request ID for diagnosis.
19
+
3
20
  ## 0.5.1 - 2026-09-23
4
21
 
5
22
  ### Fixed
package/README.md CHANGED
@@ -687,6 +687,7 @@ that type:
687
687
  // "message" { text, role }
688
688
  // "interaction" the typed Interaction object (HITL) — see Interactions
689
689
  // "screenshot" { pageUrl?, step? } + the decoded image on event.image
690
+ // "browserLiveUrlChanged" { liveUrl, supportsLivePump, forceViewerReload, providerSessionId? }
690
691
  // "done" { output, succeeded, terminalReason }
691
692
  // Common to every event: runId, at (ISO 8601), isTerminal (true only for "done"),
692
693
  // raw (the original wire event — escape hatch).
@@ -696,11 +697,17 @@ Each product handle is typed to its own subset:
696
697
 
697
698
  | Product | `event.type` set |
698
699
  |---|---|
699
- | Do Anything | core: `progress` / `message` / `interaction` / `screenshot` / `done` |
700
+ | Do Anything | core: `progress` / `message` / `interaction` / `screenshot` / `browserLiveUrlChanged` / `done` |
700
701
  | Web Search | core + `resultsReady` |
701
702
  | Deep Research | core + `phase` / `sectionReady` (the outline gate arrives as an `interaction`) |
702
703
  | Track (`MonitorHandle`) | core + `monitorCreated` / `triggered` / `checkCompleted` |
703
704
 
705
+ For a live browser preview, listen for `browserLiveUrlChanged` and show
706
+ `event.data.liveUrl` in your viewer. A run can switch browser tabs after its
707
+ first URL arrives, so update the viewer on later events too. Remount an
708
+ existing viewer when `forceViewerReload` is true. The URL grants access to the
709
+ browser view; keep it out of public logs.
710
+
704
711
  Dropped connections reconnect automatically with `Last-Event-ID` catch-up; the iterator ends at the run's terminal (`done`) event.
705
712
  For complete audit/observability capture, `onWireEvent` receives every original
706
713
  SSE frame before semantic normalization or filtering; `onReconnect` receives
@@ -727,7 +734,7 @@ All error classes are exported and can be matched with `instanceof`:
727
734
  | --- | --- | --- |
728
735
  | `QoniValidationError` | Local input check failed before any request (bad scope string, silent delegation missing a user). | Fix the call — the message states the correct form. |
729
736
  | `QoniAuthError` | AK/SK signing rejected by Qoni (bad/rotated credentials). | Check `accessKey` / `secretKey`. |
730
- | `QoniPermissionDeniedError` | The delegation token lacks a required scope (403). | Re-`delegateToken` with the missing scope. |
737
+ | `QoniPermissionDeniedError` | The AccessKey or delegation token lacks a required scope (403). | Check the AccessKey permission policy and request the missing scope with `delegateToken`. |
731
738
  | `QoniTokenExpiredError` | The delegation / product token expired. | Mint a fresh token and retry. |
732
739
  | `QoniDelegationRequiredError` | A product call was made without `token`, or with a raw token the gateway won't accept directly. | Pass `delegation.data.token` to the product call. |
733
740
  | `QoniRateLimitError` | Rate limited (429). | Back off and retry (`retryable === true`). |
@@ -777,6 +784,7 @@ Common first-run errors:
777
784
  | --- | --- | --- |
778
785
  | `agent must be a string` | The online delegation API currently expects `agent` to be a string id. | Pass `agent: "memory-agent"` rather than an object. |
779
786
  | `eak.delegation.user_not_bound` | The `userId` is not in the GenAuth userpool bound to this Qoni credential. | Use a real user id from `currentUser` or from the same bound userpool. |
787
+ | `eak.delegation.no_allowed_scopes` | The AccessKey has no permission scopes enabled. | In Qoni Console, open its Permission scope settings and enable the policy required by this call. |
780
788
  | `eak.genauth.userpool_binding_missing` | The AK/SK is not bound to a GenAuth userpool, so `genauth.users.*` cannot obtain a management token. | Bind the Qoni credential to the target GenAuth userpool before calling user management APIs. |
781
789
  | `eak.genauth.userpool_owner_missing` | The bound GenAuth userpool has no resolvable owner user for management-token signing. | Fix the GenAuth userpool owner data or binding. |
782
790
  | `eak.token_exchange.upstream_failed` with `unauthorized_client` or `grant_type is not enabled` | Delegation succeeded, but the managed delegation app is missing token-exchange grant support or has drifted from the tenant binding. | Repair the managed delegation app binding before calling GUMem or WebAgent product capabilities. |
package/README.zh-CN.md CHANGED
@@ -620,6 +620,7 @@ type QoniResponse<T> = {
620
620
  // "message" { text, role }
621
621
  // "interaction" 带类型的 Interaction 对象(HITL)—— 见「交互」一节
622
622
  // "screenshot" { pageUrl?, step? } + 解码后的图在 event.image
623
+ // "browserLiveUrlChanged" { liveUrl, supportsLivePump, forceViewerReload, providerSessionId? }
623
624
  // "done" { output, succeeded, terminalReason }
624
625
  // 公共字段:runId、at(ISO 8601)、isTerminal(仅 "done" 为 true)、raw(原始 wire 事件,逃生舱)。
625
626
  ```
@@ -628,11 +629,13 @@ type QoniResponse<T> = {
628
629
 
629
630
  | 产品 | `event.type` 集合 |
630
631
  |---|---|
631
- | Do Anything | 核心:`progress` / `message` / `interaction` / `screenshot` / `done` |
632
+ | Do Anything | 核心:`progress` / `message` / `interaction` / `screenshot` / `browserLiveUrlChanged` / `done` |
632
633
  | Web Search | 核心 + `resultsReady` |
633
634
  | Deep Research | 核心 + `phase` / `sectionReady`(大纲审批走 `interaction`) |
634
635
  | Track(`MonitorHandle`) | 核心 + `monitorCreated` / `triggered` / `checkCompleted` |
635
636
 
637
+ 展示实时浏览器时,监听 `browserLiveUrlChanged`,用 `event.data.liveUrl` 更新预览地址。任务执行期间可能切换标签页,因此不能只使用第一次收到的地址。`forceViewerReload` 为 true 时重载已有预览。live URL 可访问浏览器画面,不要写入公开日志。
638
+
636
639
  断线会带 `Last-Event-ID` 自动重连续传;迭代器在终态事件后自动结束。
637
640
  需要完整审计/可观测性证据时,`onWireEvent` 会在语义归一化和过滤之前收到每一个原始 SSE 帧;
638
641
  `onReconnect` 会收到重试次数和最后事件 id。应用逻辑继续使用稳定的 `RunEvent`,原始回调只用于证据与诊断。
@@ -657,7 +660,7 @@ type QoniEvent<T = unknown> = {
657
660
  | --- | --- | --- |
658
661
  | `QoniValidationError` | 请求发出前的本地校验失败(scope 字符串格式错、silent 委托缺 user)。 | 按消息修正调用——消息里给出正确写法。 |
659
662
  | `QoniAuthError` | AK/SK 签名被 Qoni 拒绝(凭据错误/已轮换)。 | 检查 `accessKey` / `secretKey`。 |
660
- | `QoniPermissionDeniedError` | delegation token 缺所需 scope(403)。 | 带上缺失 scope 重新 `delegateToken`。 |
663
+ | `QoniPermissionDeniedError` | AccessKey 或 delegation token 缺少所需 scope(403)。 | 检查 AccessKey 权限策略,并在 `delegateToken` 中申请所需 scope。 |
661
664
  | `QoniTokenExpiredError` | delegation / 产品 token 过期。 | 重新 mint token 再试。 |
662
665
  | `QoniDelegationRequiredError` | 产品调用没带 `token`,或带的是网关不接受的裸 token。 | 把 `delegation.data.token` 传给产品调用。 |
663
666
  | `QoniRateLimitError` | 被限流(429)。 | 退避后重试(`retryable === true`)。 |
@@ -706,6 +709,7 @@ try {
706
709
  | --- | --- | --- |
707
710
  | `agent must be a string` | 当前线上 delegation API 期望 `agent` 是字符串 ID。 | 使用 `agent: "memory-agent"`,不要传对象。 |
708
711
  | `eak.delegation.user_not_bound` | `userId` 不属于该 Qoni credential 绑定的 GenAuth userpool。 | 使用 `currentUser` 返回的真实用户 ID,或确认用户来自同一个绑定 userpool。 |
712
+ | `eak.delegation.no_allowed_scopes` | AccessKey 没有启用任何权限 scope。 | 在 Qoni Console 的 Permission scope 设置中启用本次调用所需的策略。 |
709
713
  | `eak.genauth.userpool_binding_missing` | AK/SK 没有绑定 GenAuth userpool,`genauth.users.*` 无法换取 management token。 | 先把 Qoni credential 绑定到目标 GenAuth userpool。 |
710
714
  | `eak.genauth.userpool_owner_missing` | 绑定的 GenAuth userpool 找不到可用于签发 management token 的 owner 用户。 | 修复 GenAuth userpool owner 数据或绑定。 |
711
715
  | `eak.token_exchange.upstream_failed` 且包含 `unauthorized_client` 或 `grant_type is not enabled` | Delegation 已成功,但 managed delegation app 缺少 token-exchange grant,或租户绑定发生漂移。 | 先修复 managed delegation app 绑定,再调用 GUMem 或 WebAgent 产品能力。 |
package/dist/index.cjs CHANGED
@@ -104,81 +104,6 @@ function buildSignedHeaders(credentials, method, pathname, payload) {
104
104
  return headers;
105
105
  }
106
106
 
107
- // src/scopes.ts
108
- var QoniScopes = {
109
- GUMEM_SESSION_CREATE: "gumem.session:create",
110
- GUMEM_MESSAGE_WRITE: "gumem.message:write",
111
- GUMEM_MEMORY_READ: "gumem.memory:read",
112
- GUMEM_MEMORY_WRITE: "gumem.memory:write",
113
- GUMEM_MEMORY_DELETE: "gumem.memory:delete",
114
- GUMEM_SEARCH_RUN: "gumem.search:run",
115
- GUMEM_ADMIN_MANAGE: "gumem.admin:manage",
116
- GUMEM_RESOURCE_WRITE: "gumem.resource:write",
117
- GUMEM_ACTION_WRITE: "gumem.action:write",
118
- GUMEM_ACTION_READ: "gumem.action:read",
119
- GUMEM_PROFILE_READ: "gumem.profile:read",
120
- // WebAgent products: exactly two verbs per product — `read` (observe) and
121
- // `manage` (anything that changes execution state). See
122
- // wa/docs/qoni-sdk-public-surface.md §3.
123
- DO_ANYTHING_READ: "webagent.do_anything:read",
124
- DO_ANYTHING_MANAGE: "webagent.do_anything:manage",
125
- WEB_SEARCH_READ: "webagent.web_search:read",
126
- WEB_SEARCH_MANAGE: "webagent.web_search:manage",
127
- DEEP_RESEARCH_READ: "webagent.deep_research:read",
128
- DEEP_RESEARCH_MANAGE: "webagent.deep_research:manage",
129
- TRACK_READ: "webagent.track:read",
130
- TRACK_MANAGE: "webagent.track:manage"
131
- };
132
- var QoniProductScopes = {
133
- doAnything: [
134
- QoniScopes.DO_ANYTHING_READ,
135
- QoniScopes.DO_ANYTHING_MANAGE
136
- ],
137
- webSearch: [
138
- QoniScopes.WEB_SEARCH_READ,
139
- QoniScopes.WEB_SEARCH_MANAGE
140
- ],
141
- deepResearch: [
142
- QoniScopes.DEEP_RESEARCH_READ,
143
- QoniScopes.DEEP_RESEARCH_MANAGE
144
- ],
145
- track: [
146
- QoniScopes.TRACK_READ,
147
- QoniScopes.TRACK_MANAGE
148
- ]
149
- };
150
- var KNOWN_SCOPES = Object.values(QoniScopes);
151
- var QoniScopeBundles = {
152
- GUMEM_READONLY: [QoniScopes.GUMEM_MEMORY_READ, QoniScopes.GUMEM_PROFILE_READ],
153
- GUMEM_SESSION_RECALL: [
154
- QoniScopes.GUMEM_MEMORY_READ,
155
- QoniScopes.GUMEM_MEMORY_WRITE,
156
- QoniScopes.GUMEM_MESSAGE_WRITE
157
- ],
158
- GUMEM_WRITE: [
159
- QoniScopes.GUMEM_SESSION_CREATE,
160
- QoniScopes.GUMEM_MESSAGE_WRITE,
161
- QoniScopes.GUMEM_ACTION_WRITE
162
- ],
163
- // One bundle per WebAgent product = [read, manage].
164
- DO_ANYTHING: [
165
- QoniScopes.DO_ANYTHING_READ,
166
- QoniScopes.DO_ANYTHING_MANAGE
167
- ],
168
- WEB_SEARCH: [
169
- QoniScopes.WEB_SEARCH_READ,
170
- QoniScopes.WEB_SEARCH_MANAGE
171
- ],
172
- DEEP_RESEARCH: [
173
- QoniScopes.DEEP_RESEARCH_READ,
174
- QoniScopes.DEEP_RESEARCH_MANAGE
175
- ],
176
- TRACK: [
177
- QoniScopes.TRACK_READ,
178
- QoniScopes.TRACK_MANAGE
179
- ]
180
- };
181
-
182
107
  // src/types.ts
183
108
  function isJsonObject(value) {
184
109
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -262,7 +187,7 @@ function errorFromPayload(status, payload, fallbackMessage) {
262
187
  const source = detailObject || (isJsonObject(payload) ? payload : {});
263
188
  const code = stringField(source, "code") || stringField(source, "apiCode") || statusCodeToCode(status);
264
189
  const rawMessage = stringField(source, "message") || stringField(source, "detail") || fallbackMessage;
265
- const message = appendOnboardingHint(
190
+ const message = code === "eak.delegation.no_allowed_scopes" ? "This AccessKey has no permission scopes. In Qoni Console, open its Permission scope settings, enable the policy needed for this call, then retry." : appendOnboardingHint(
266
191
  appendDeniedScopes(appendUpstreamDescription(rawMessage, source), source),
267
192
  code
268
193
  );
@@ -279,7 +204,7 @@ function errorFromPayload(status, payload, fallbackMessage) {
279
204
  if (isDelegationRequired(code)) return new QoniDelegationRequiredError(message, options);
280
205
  if (status === 401) return new QoniAuthError(message, options);
281
206
  if (status === 403 || isPermissionDenied(code)) {
282
- return new QoniPermissionDeniedError(appendKnownScopes(message), options);
207
+ return new QoniPermissionDeniedError(message, options);
283
208
  }
284
209
  if (status === 400 || status === 422) return new QoniValidationError(message, options);
285
210
  if (status === 429) return new QoniRateLimitError(message, options);
@@ -314,17 +239,14 @@ function appendUpstreamDescription(message, source) {
314
239
  }
315
240
  var ONBOARDING_HINTS = {
316
241
  "eak.delegation.user_not_bound": "Hint: the userId is not in the GenAuth userpool bound to this credential. Resolve a real user with qoni.resolveAnyBoundUser() (demo/smoke) or qoni.currentUser({ accessToken }).",
317
- "eak.genauth.userpool_binding_missing": "Hint: bind the Qoni credential to a GenAuth userpool before calling genauth.users.*."
242
+ "eak.genauth.userpool_binding_missing": "Hint: bind the Qoni credential to a GenAuth userpool before calling genauth.users.*.",
243
+ "eak.delegation.scope_not_allowed": "Hint: enable the denied scopes in this AccessKey's Permission scope settings in Qoni Console."
318
244
  };
319
245
  function appendOnboardingHint(message, code) {
320
246
  const hint = ONBOARDING_HINTS[code];
321
247
  if (!hint || message.includes(hint)) return message;
322
248
  return `${message} ${hint}`;
323
249
  }
324
- function appendKnownScopes(message) {
325
- if (message.includes("Known scopes:")) return message;
326
- return `${message} (Known scopes: ${KNOWN_SCOPES.join(", ")})`;
327
- }
328
250
  function statusCodeToCode(status) {
329
251
  if (status === 401) return "auth.failed";
330
252
  if (status === 403) return "permission_denied";
@@ -356,6 +278,8 @@ var QoniEventTypes = {
356
278
  Interaction: "interaction",
357
279
  /** A live screenshot frame (opt in with `capture: { screenshots: true }`); image on `event.image`. */
358
280
  Screenshot: "screenshot",
281
+ /** The browser viewer moved to a new page or needs a fresh attachment. Use `event.data.liveUrl` for the live preview. */
282
+ BrowserLiveUrlChanged: "browserLiveUrlChanged",
359
283
  /** Terminal — the run finished; outcome + output in `event.data` (cost is on the `RunResult`). */
360
284
  Done: "done",
361
285
  // --- Web Search ---
@@ -486,7 +410,6 @@ var WIRE_TO_SEMANTIC = {
486
410
  "run.recording_started": "progress",
487
411
  "run.recording_paused": "progress",
488
412
  "run.recording_finalized": "progress",
489
- "run.browser_live_url_changed": "progress",
490
413
  // side-channel
491
414
  "run.feedback_submitted": "progress"
492
415
  };
@@ -548,6 +471,18 @@ function normalizeRunEvent(wire, options = {}) {
548
471
  return { ...base, type: "screenshot", data: sData, image, isTerminal: false };
549
472
  }
550
473
  }
474
+ if (wireType === "run.browser_live_url_changed") {
475
+ const liveUrl = asString(c.liveUrl);
476
+ if (!liveUrl) return null;
477
+ const data2 = {
478
+ liveUrl,
479
+ supportsLivePump: c.supportsLivePump === true,
480
+ forceViewerReload: c.forceViewerReload === true
481
+ };
482
+ const providerSessionId = asString(c.providerSessionId);
483
+ if (providerSessionId) data2.providerSessionId = providerSessionId;
484
+ return { ...base, type: "browserLiveUrlChanged", data: data2, isTerminal: false };
485
+ }
551
486
  if (wireType === INTERACTION) {
552
487
  const interaction = normalizeInteraction(payload);
553
488
  if (interaction) {
@@ -1488,6 +1423,81 @@ function createQoniNamespace(_transport, delegateToken, completeDelegateToken) {
1488
1423
  };
1489
1424
  }
1490
1425
 
1426
+ // src/scopes.ts
1427
+ var QoniScopes = {
1428
+ GUMEM_SESSION_CREATE: "gumem.session:create",
1429
+ GUMEM_MESSAGE_WRITE: "gumem.message:write",
1430
+ GUMEM_MEMORY_READ: "gumem.memory:read",
1431
+ GUMEM_MEMORY_WRITE: "gumem.memory:write",
1432
+ GUMEM_MEMORY_DELETE: "gumem.memory:delete",
1433
+ GUMEM_SEARCH_RUN: "gumem.search:run",
1434
+ GUMEM_ADMIN_MANAGE: "gumem.admin:manage",
1435
+ GUMEM_RESOURCE_WRITE: "gumem.resource:write",
1436
+ GUMEM_ACTION_WRITE: "gumem.action:write",
1437
+ GUMEM_ACTION_READ: "gumem.action:read",
1438
+ GUMEM_PROFILE_READ: "gumem.profile:read",
1439
+ // WebAgent products: exactly two verbs per product — `read` (observe) and
1440
+ // `manage` (anything that changes execution state). See
1441
+ // wa/docs/qoni-sdk-public-surface.md §3.
1442
+ DO_ANYTHING_READ: "webagent.do_anything:read",
1443
+ DO_ANYTHING_MANAGE: "webagent.do_anything:manage",
1444
+ WEB_SEARCH_READ: "webagent.web_search:read",
1445
+ WEB_SEARCH_MANAGE: "webagent.web_search:manage",
1446
+ DEEP_RESEARCH_READ: "webagent.deep_research:read",
1447
+ DEEP_RESEARCH_MANAGE: "webagent.deep_research:manage",
1448
+ TRACK_READ: "webagent.track:read",
1449
+ TRACK_MANAGE: "webagent.track:manage"
1450
+ };
1451
+ var QoniProductScopes = {
1452
+ doAnything: [
1453
+ QoniScopes.DO_ANYTHING_READ,
1454
+ QoniScopes.DO_ANYTHING_MANAGE
1455
+ ],
1456
+ webSearch: [
1457
+ QoniScopes.WEB_SEARCH_READ,
1458
+ QoniScopes.WEB_SEARCH_MANAGE
1459
+ ],
1460
+ deepResearch: [
1461
+ QoniScopes.DEEP_RESEARCH_READ,
1462
+ QoniScopes.DEEP_RESEARCH_MANAGE
1463
+ ],
1464
+ track: [
1465
+ QoniScopes.TRACK_READ,
1466
+ QoniScopes.TRACK_MANAGE
1467
+ ]
1468
+ };
1469
+ var KNOWN_SCOPES = Object.values(QoniScopes);
1470
+ var QoniScopeBundles = {
1471
+ GUMEM_READONLY: [QoniScopes.GUMEM_MEMORY_READ, QoniScopes.GUMEM_PROFILE_READ],
1472
+ GUMEM_SESSION_RECALL: [
1473
+ QoniScopes.GUMEM_MEMORY_READ,
1474
+ QoniScopes.GUMEM_MEMORY_WRITE,
1475
+ QoniScopes.GUMEM_MESSAGE_WRITE
1476
+ ],
1477
+ GUMEM_WRITE: [
1478
+ QoniScopes.GUMEM_SESSION_CREATE,
1479
+ QoniScopes.GUMEM_MESSAGE_WRITE,
1480
+ QoniScopes.GUMEM_ACTION_WRITE
1481
+ ],
1482
+ // One bundle per WebAgent product = [read, manage].
1483
+ DO_ANYTHING: [
1484
+ QoniScopes.DO_ANYTHING_READ,
1485
+ QoniScopes.DO_ANYTHING_MANAGE
1486
+ ],
1487
+ WEB_SEARCH: [
1488
+ QoniScopes.WEB_SEARCH_READ,
1489
+ QoniScopes.WEB_SEARCH_MANAGE
1490
+ ],
1491
+ DEEP_RESEARCH: [
1492
+ QoniScopes.DEEP_RESEARCH_READ,
1493
+ QoniScopes.DEEP_RESEARCH_MANAGE
1494
+ ],
1495
+ TRACK: [
1496
+ QoniScopes.TRACK_READ,
1497
+ QoniScopes.TRACK_MANAGE
1498
+ ]
1499
+ };
1500
+
1491
1501
  // src/genauth.ts
1492
1502
  function createGenAuthNamespace(transport, options = {}) {
1493
1503
  return {