@qoniai/qoni 0.9.0 → 0.9.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 +203 -0
- package/README.md +548 -76
- package/README.zh-CN.md +443 -53
- package/dist/index.cjs +15082 -699
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25826 -243
- package/dist/index.d.ts +25826 -243
- package/dist/index.js +15077 -698
- package/dist/index.js.map +1 -1
- package/docs/authing-sdk-LICENSE.txt +21 -0
- package/docs/delivery-examples/README.md +44 -0
- package/docs/delivery-examples/README.zh-CN.md +44 -0
- package/docs/delivery-examples/agents.ts +26 -0
- package/docs/delivery-examples/failure-handling.ts +21 -0
- package/docs/delivery-examples/hosted-login.ts +40 -0
- package/docs/delivery-examples/interactions.ts +30 -0
- package/docs/delivery-examples/local-consumer.mjs +159 -0
- package/docs/delivery-examples/m2-r2/README.md +20 -0
- package/docs/delivery-examples/m2-r2/README.zh-CN.md +20 -0
- package/docs/delivery-examples/m2-r2/local-consumer.mjs +53 -0
- package/docs/delivery-examples/m2-r2/memory-outcome.ts +22 -0
- package/docs/delivery-examples/m2-r2/package.json +1 -0
- package/docs/delivery-examples/m2-r2/tsconfig.json +1 -0
- package/docs/delivery-examples/package.json +5 -0
- package/docs/delivery-examples/profile-memory.ts +30 -0
- package/docs/delivery-examples/tsconfig.json +4 -0
- package/docs/genauth-communication.md +1410 -0
- package/docs/genauth-management-api.md +597 -0
- package/docs/genauth-management.md +285 -0
- package/docs/ordinary-task-profile.md +18 -0
- package/docs/quickstart-v5-contract.md +39 -0
- package/package.json +17 -7
- package/skills/qoni-sdk/SKILL.md +177 -7
package/skills/qoni-sdk/SKILL.md
CHANGED
|
@@ -1,21 +1,60 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: qoni-sdk
|
|
3
|
-
description: Use when building, testing, debugging, or documenting Node.js integrations with @qoniai/qoni, the Qoni constructor, AK/SK credentials, delegateToken, GenAuth currentUser/userInfo/introspection/
|
|
3
|
+
description: Use when building, testing, debugging, or documenting Node.js integrations with @qoniai/qoni, the Qoni constructor, AK/SK credentials, delegateToken, GenAuth hosted sign-up/sign-in (genauth.auth.createLoginUrl/exchangeLoginCode), currentUser/userInfo/introspection/Authing v3 management, Qoni runtime-config discovery, GUMem, WebAgent, Web Search, Do Anything, Deep Research, Track, OBO product token exchange, or local .genauth.localhost SDK flows.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Qoni SDK
|
|
7
7
|
|
|
8
|
-
> **
|
|
8
|
+
> **Released semantic baseline: `@qoniai/qoni` ≥ 0.4.0.** Unreleased additions
|
|
9
|
+
> require the exact candidate archive described below, not merely a version check.
|
|
10
|
+
> This guide documents the semantic-layer
|
|
9
11
|
> surface: `run()`/`create()` return handles (`RunHandle` / `MonitorHandle`),
|
|
10
12
|
> events are normalized (`event.type`, camelCase `data`, decoded screenshots),
|
|
11
|
-
> and
|
|
13
|
+
> and supported 1:1 wire methods live under `qoni.<product>.api.*`. If the consumer
|
|
12
14
|
> resolves an older version from npm, install this repo's local build instead
|
|
13
15
|
> (`npm install <path-to-qoni-sdk-node>` or a `pnpm pack` tarball) — otherwise
|
|
14
16
|
> the code below will not match the installed SDK.
|
|
15
17
|
|
|
18
|
+
## 内部 Pi 宿主 task facade(Unreleased、本地独立构建)
|
|
19
|
+
|
|
20
|
+
现有 Pi 宿主可显式使用 `createTaskClient({ productHosts: { gumem }, runId, tokenProvider, fetch?, timeoutMs? })`。
|
|
21
|
+
只允许 `task.memory.createSession({ signal? })` 和 `task.memory.addMessages({ messages, signal? })`;
|
|
22
|
+
调用方不能指定 user/session/token/header/path/scope 或使用 recall/admin/delegation/raw。
|
|
23
|
+
Session 固定为 `qoni-run-<runId>`;run ID 原样保留、最多 55 个 Unicode 字符,拒绝 slash/backslash;
|
|
24
|
+
run 与消息 ID 拒绝 C0/C1 控制字符和 DEL。消息只含稳定非空 id、GUM 字符串 role(非空)、
|
|
25
|
+
字符串 content;role/content 必须是完整 Unicode,合法 pair、空白、换行和空 content 原样保留。
|
|
26
|
+
全批同步校验通过即绑定,同一客户端已提交过的 ID(包括尚未发出的批次)内容/role 不可变。
|
|
27
|
+
provider 拒绝或 HTTP 发出前超时仍保留绑定,原内容可显式重试;坏批次不绑定任何 ID。
|
|
28
|
+
应用层 Quickstart 不直调记忆/Profile;不要把这个内部入口接成模型的任意 HTTP 工具。
|
|
29
|
+
|
|
30
|
+
`tokenProvider(product, requiredScopes, runId, signal)` 每次请求查询当前许可,返回
|
|
31
|
+
`{ accessToken, expiresAt }`(Unix 毫秒,未来有限值);没有 AK/SK 初始化或 token 长期缓存。
|
|
32
|
+
provider/HTTP/body 共享总 timeout 与 abort;host 只用可信配置,HTTPS 默认,loopback HTTP 仅隔离测试。
|
|
33
|
+
不跟随重定向、不自动重试 POST。固定有序批次内部生成稳定 X-Request-ID,复用 usage outbox 去重;
|
|
34
|
+
不宣称重叠/并发批次完整 exactly-once。成功只返回 Session 字段或 request_id/message_count,
|
|
35
|
+
错误保留 typed Qoni 类别/code/status,去掉 provider/HTTP 私密原始正文和 cause。
|
|
36
|
+
|
|
37
|
+
显式选择 task facade 新归档;不得覆盖 current_task 的 r2 冻结联调包。G6/WA 可信许可来源、GUM
|
|
38
|
+
run/session 资源端限制、Pi 私有游标/恢复及真实 Pi/E2B/离线验收仍待后继实施;方法收窄不是沙箱隔离。
|
|
39
|
+
|
|
40
|
+
## Candidate examples and limits
|
|
41
|
+
|
|
42
|
+
The hosted-login/current_task/forms/Profile/memory/agents additions below require an explicitly selected unreleased candidate package and matching backend; package version 0.9.0 alone is insufficient. Use the exact tarball and public DTS. See `docs/delivery-examples/README.md` and `README.zh-CN.md` for typed examples.
|
|
43
|
+
|
|
44
|
+
- `gumem.recallContext()` returns `QoniResponse<MemoryContext>`: read `.data`. `doAnything.runWithMemory()` returns `{run,memory}` directly and explicitly opts in; ordinary `run()` never recalls automatically. Facts/summary sources may be unavailable; stored history URLs are not verified fact provenance.
|
|
45
|
+
- `doAnything.profile.read/update` returns `QoniResponse<OrdinaryTaskProfile>`. Preserve original runId/token and original user/App authority; read `.data.values/missing/writable_fields/field_types`. Only the documented ordinary field allowlist is supported, excluding phone/email verification, card/payment data and sensitive/encrypted fields. A plain form does not save Profile.
|
|
46
|
+
- `genauth.agents.create/get/bind` uses the original persisted request/idempotencyKey and provisioningId. `ready:false` is not success. Bind only a ready receipt using the current human accessToken; never use a browser-supplied owner ID. Backend business validation remains incomplete and this does not establish external mailbox-provider support. Do not invent an `unknown` provisioning state; inspect the actual state/errorCode and preserve uncertainty on errors.
|
|
47
|
+
- JSON POSTs are not automatically retried. `retryable` is not replay permission; 202/pending/unknown does not mean completed. Read the original resource/operation where supported. Do not replace an unknown write with a new key, card or Task.
|
|
48
|
+
- After consent narrowing/revocation, stop scheduling affected calls and respect denial. Never silently broaden the original grant, swap identity/App, or invent `genauth.consents.revoke()`. Stopping local waiting is not proof of stopping server work.
|
|
49
|
+
- Longer duration settings do not establish one-hour credential/browser/provider continuity. Confirmation cards do not establish forced approval or sensitive-value isolation across open browser/bash/harness paths. Keep these full-business limits explicit.
|
|
50
|
+
|
|
51
|
+
### Corrected memory-run errors
|
|
52
|
+
|
|
53
|
+
In the corrected `runWithMemory` candidate only, `QoniError.phase` is `recall`, `task_permission`, or `task_submit`; `submissionUnknown` is false before an actual Task fetch or on an explicit rejection. `QoniTaskSubmissionUnknownError` has code `task.submit_unknown`, phase `task_submit`, submissionUnknown=true, retryable=false, fixed message and no body/cause. Never replay automatically. HTTP 4xx responses and explicit business rejections inside a successful HTTP response retain their original typed error, code, status and retryable flag; actual HTTP 5xx responses after dispatch are submission unknown and nonretryable. Once fetch is invoked, even connection refusal is conservatively unknown; this does not prove that the server received or created a Task and never permits an automatic retry. Old candidates and ordinary run APIs may lack these additive markers; a generic retryable timeout is still not write replay permission. The unified local candidate includes this corrected contract; use the fixed package/DTS. Earlier separate archives remain historical inputs.
|
|
54
|
+
|
|
16
55
|
## Core Contract
|
|
17
56
|
|
|
18
|
-
Use `@qoniai/qoni` from trusted server-side Node.js code. Keep AK/SK on the server and create one reusable `Qoni`. For silent runtime product calls, resolve a real GenAuth user id from the credential-bound userpool, call `delegateToken`, then pass `delegation.data.token` explicitly to every product capability call. For interactive runtime authorization, call `delegateToken({ mode: "interactive", redirectUri, state, agent, scopes })` from the server without exposing AK/SK; redirect the user to `authorizationUrl`, then handle the business callback server-side with `completeDelegateToken({ grantId, code, state })` to receive the token. For GenAuth
|
|
57
|
+
Use `@qoniai/qoni` from trusted server-side Node.js code. Keep AK/SK on the server and create one reusable `Qoni`. For silent runtime product calls, resolve a real GenAuth user id from the credential-bound userpool, call `delegateToken`, then pass `delegation.data.token` explicitly to every product capability call. For interactive runtime authorization, call `delegateToken({ mode: "interactive", redirectUri, state, agent, scopes })` from the server without exposing AK/SK; redirect the user to `authorizationUrl`, then handle the business callback server-side with `completeDelegateToken({ grantId, code, state })` to receive the token. For GenAuth management, use `qoni.genauth` domain namespaces (such as `users`, `roles`, `groups`, `organizations`, `applications`) or the Authing-name migration entry `qoni.genauth.management`; the SDK exchanges AK/SK for a standard GenAuth management token internally and does not require `QONI_USER_ID`.
|
|
19
58
|
|
|
20
59
|
```ts
|
|
21
60
|
import { Qoni, QoniScopes } from "@qoniai/qoni";
|
|
@@ -128,6 +167,15 @@ await qoni.genauth.users.create({
|
|
|
128
167
|
});
|
|
129
168
|
```
|
|
130
169
|
|
|
170
|
+
## Hosted Sign-Up And Sign-In (Unreleased)
|
|
171
|
+
|
|
172
|
+
For end-user sign-up/sign-in, call `qoni.genauth.auth.createLoginUrl({ redirectUri, state, screen? })` on the server, keep `state` in your own session, and redirect the browser to `url`. In the callback, compare `state` yourself before calling `qoni.genauth.auth.exchangeLoginCode({ code, redirectUri })`; the SDK never validates `state`. The AccessKey must be bound to a registered business app whose redirect URIs contain `redirectUri` exactly.
|
|
173
|
+
|
|
174
|
+
- Do not pass `clientId`, client secrets, issuer or token URLs: undeclared fields are rejected before HTTP. Secrets, PKCE verifier and nonce stay in GenAuth.
|
|
175
|
+
- `expiresAt` is fixed at creation (600 seconds) and is not extended by reopening, MFA or new sessions. Each URL completes once and each code exchanges once; on any exchange failure or unknown outcome, start a new `createLoginUrl`. The SDK does not retry.
|
|
176
|
+
- `user.isNewUser` comes from the actual registration in this sign-in (true only when the account was registered in the same hosted-page window that completes it); never derive it from timestamps or `screen`. `error=access_denied` on the callback means start again.
|
|
177
|
+
- GA business errors are HTTP 200 envelopes (for example `eak.hosted_login.code_invalid`, `eak.credential.binding_mismatch`, `eak.hosted_login.unavailable` when the server is not configured) and surface as typed `QoniError`s.
|
|
178
|
+
|
|
131
179
|
Use `delegateToken` output for runtime capability namespaces:
|
|
132
180
|
|
|
133
181
|
- `gumem.createSession`, `gumem.addMessages`, `gumem.recall`, `gumem.uploadResource`, `gumem.actions.*`
|
|
@@ -215,18 +263,124 @@ for await (const event of run.events()) {
|
|
|
215
263
|
|
|
216
264
|
`qoni.doAnything.api.events({ token, sessionId, runId, signal?, lastEventId?, onlyTopLevel? })` yields raw `{ id?, event, data }` wire events: the type is a raw string on `event.event` (e.g. `"run.status_changed"`), the payload is `event.data.data` (snake_case), and `event.data.task_id` identifies the run. `run.status_changed` payloads carry the new status in `.to` (NOT `.status`). Terminal detection is `event.event === "run.completed"`. Wire names are internal and may evolve — prefer the high-level `run.events()` + `event.type` instead.
|
|
217
265
|
|
|
266
|
+
## Track and deprecated snapshot reads
|
|
267
|
+
|
|
268
|
+
Use `track.create({ token, prompt, schedule, title? })` with an explicit
|
|
269
|
+
`{ kind: "interval", intervalSeconds: N }` (integer 600–2592000 seconds) or
|
|
270
|
+
`{ kind: "daily", at: "HH:MM", tz: "Area/City" }`. Daily starts 0–30 minutes
|
|
271
|
+
before the target. Prompt maps to instructions; include URLs there. No default
|
|
272
|
+
schedule is inferred, and arbitrary cron is rejected with a daily alternative.
|
|
273
|
+
|
|
274
|
+
The historical `MonitorHandle` name remains. `get()` normalizes the real Track
|
|
275
|
+
record: known schedule shapes convert without write-bound/Intl revalidation;
|
|
276
|
+
unknown/incomplete wire schedules stay verbatim. Input validation remains strict.
|
|
277
|
+
`pause()`/`resume()` PATCH status; pause keeps any started check running.
|
|
278
|
+
`refine()` accepts title/schedule only and cannot change instructions.
|
|
279
|
+
`runNow()` returns `{ trackId, sessionId, runId }` (202 acceptance); it can
|
|
280
|
+
surface 409 task_in_progress. `runs({ limit?, offset? })` slices the newest 50
|
|
281
|
+
checks locally; `run(id)` searches that exact window and reports its range on
|
|
282
|
+
not_found. Keep real status/outcome/headline/answer/failure, never invent
|
|
283
|
+
extracted/diffs/snapshotId. `delete()` soft-deletes without cancelling a running task.
|
|
284
|
+
|
|
285
|
+
Create starts a first_look immediately. Capture the original initial lastTaskId,
|
|
286
|
+
wait for that exact check to terminate, then call runNow once; never retry 409
|
|
287
|
+
implicitly or substitute another check. Checks keep `running/done/failed/expired/
|
|
288
|
+
canceled`; success is done, distinct from RunResult.status succeeded. A missing
|
|
289
|
+
check in the recent window is local not_found without an HTTP status; a missing
|
|
290
|
+
Track is backend 404 track_not_found.
|
|
291
|
+
|
|
292
|
+
The example uses the reusable qoni client and delegated token from Core Contract:
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
import type { MonitorSchedule } from "@qoniai/qoni";
|
|
296
|
+
|
|
297
|
+
const monitor = await qoni.track.create({
|
|
298
|
+
token,
|
|
299
|
+
prompt: "Watch https://example.com/policy and report changes.",
|
|
300
|
+
title: "Policy changes",
|
|
301
|
+
schedule: { kind: "interval", intervalSeconds: 3600 },
|
|
302
|
+
}); // create immediately starts a first_look check
|
|
303
|
+
const initial = await monitor.get();
|
|
304
|
+
const firstRunId = initial.lastTaskId; // capture this original ID once
|
|
305
|
+
if (typeof firstRunId !== "string" || !firstRunId) throw new Error("Missing initial lastTaskId");
|
|
306
|
+
const sameMonitor = await qoni.track.attach(monitor.id, { token });
|
|
307
|
+
|
|
308
|
+
async function waitForCheck(runId: string) {
|
|
309
|
+
const deadline = Date.now() + 300_000;
|
|
310
|
+
do {
|
|
311
|
+
const check = await monitor.run(runId); // exact ID in the recent 50 checks
|
|
312
|
+
if (["done", "failed", "expired", "canceled"].includes(String(check.status))) {
|
|
313
|
+
if (check.status !== "done") throw new Error(`Check ${runId} ${String(check.status)}: ${String(check.failure ?? "")}`);
|
|
314
|
+
return check;
|
|
315
|
+
}
|
|
316
|
+
await new Promise(resolve => setTimeout(resolve, Math.min(1000, Math.max(0, deadline - Date.now()))));
|
|
317
|
+
} while (Date.now() < deadline);
|
|
318
|
+
throw new Error(`Check ${runId} timed out`);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
await monitor.pause(); // pause future scheduling; the first look continues
|
|
323
|
+
await waitForCheck(firstRunId);
|
|
324
|
+
await monitor.refine({ schedule: initial.schedule as MonitorSchedule }); // our known, validated input roundtrips
|
|
325
|
+
await monitor.refine({ schedule: { kind: "daily", at: "09:00", tz: "Asia/Singapore" } });
|
|
326
|
+
const task = await monitor.runNow(); // one explicit manual check while scheduling is paused
|
|
327
|
+
const check = await waitForCheck(task.runId); // 202 acceptance is not completion
|
|
328
|
+
const checks = await monitor.runs({ limit: 10, offset: 0 });
|
|
329
|
+
await monitor.resume();
|
|
330
|
+
} finally {
|
|
331
|
+
await monitor.delete(); // soft-delete; any in-flight check continues
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Wire API mapping (all supported routes use `/track/tracks`, not `/monitors`):
|
|
336
|
+
|
|
337
|
+
| Method | Contract |
|
|
338
|
+
| --- | --- |
|
|
339
|
+
| createMonitor / getMonitor | POST / GET Track; create uses instructions/title and wire schedule every_seconds or daily_at/tz. |
|
|
340
|
+
| updateMonitor | PATCH title/instructions/schedule/status; raw instructions is supported, high-level refine is stricter. |
|
|
341
|
+
| runNow / deleteMonitor | POST run_now returns track_id/session_id/run_id; DELETE returns empty 204. |
|
|
342
|
+
| listRuns / getRun | GET checks, local pagination/exact lookup within the recent 50. |
|
|
343
|
+
| api.events / api.intervene / MonitorHandle.events / interactionHandle | Deprecated; local QoniUnsupportedError, zero HTTP and zero token exchange. No Track scope authorizes DoAnything actions. |
|
|
344
|
+
|
|
345
|
+
Legacy targetUrls/extractionSchema/triggerDsl/stopConditionDsl/notifyChannel
|
|
346
|
+
(including snake_case) and url are unsupported local errors, never folded into
|
|
347
|
+
instructions. Cron and legacy event/option types remain exported, deprecated.
|
|
348
|
+
Invalid or missing input schedules raise QoniValidationError before HTTP.
|
|
349
|
+
Optional undefined keys keep their original omission behavior; defined
|
|
350
|
+
unsupported values (including null) are still local errors. Interval tz, when
|
|
351
|
+
specified, is an invalid schedule and raises QoniValidationError.
|
|
352
|
+
|
|
353
|
+
`doAnything.api.readArtifacts` and DoAnythingSnapshot/SnapshotImage/SnapshotAction
|
|
354
|
+
are deprecated: no backend snapshot route/source exists. The method raises
|
|
355
|
+
local QoniUnsupportedError (`unsupported.operation`), zero HTTP. Read actual
|
|
356
|
+
files with `qoni.doAnything.artifacts({ token, runId })` / api.listArtifacts and download via
|
|
357
|
+
artifact.content()/api.artifactDownloadUrl. For images, enable
|
|
358
|
+
capture.screenshots and consume screenshot events. A text artifact is not a
|
|
359
|
+
snapshot. Deprecated unsupported negatives never count as positive capability coverage.
|
|
360
|
+
|
|
218
361
|
## Do Anything Interactions (human handoff)
|
|
219
362
|
|
|
220
363
|
When a run needs the user it pauses and emits one `interaction` event per request (`event.data` is a typed `Interaction`; `wait({ onInteraction })` hands you an `InteractionHandle`). Interactions are typed by what the user must do, never by business: `site_login`, `take_control`, `ask_user`, `confirmation`, plus the system `wait`. `switch (handle.interaction.type)` narrows `payload` per branch.
|
|
221
364
|
|
|
222
365
|
- Act only on `status === "pending"` and dedupe by `interaction.id`: creation, status changes and reconnect replays all call back again with the same `id` and `type`. Updates with real content replace the card (an `active` take-over carries a new `liveUrl`); only empty settling shells keep the original `payload`.
|
|
223
|
-
-
|
|
224
|
-
- `site_login`: `openLogin()` → show `liveUrl` → after the user signs in, `confirmSignedIn()
|
|
366
|
+
- Follow each interaction's declared actions and current state. A pending input question may coexist with a browser-control round. Releasing a control round — SDK/OBO pending or Console active — never answers a login/credential ask; in current_task both are `site_login` cards that end only through `confirmSignedIn()` after reliable save, a declared explicit `skip()`, or expiry. Only legacy `secure_input` asks receive a recheck reply on Console active release, which proves neither sign-in nor persistence. Clarification and confirmation are never answered automatically.
|
|
367
|
+
- `site_login`: `openLogin()` → show `liveUrl` → after the user signs in, `confirmSignedIn()`. Do NOT call the deprecated `login.confirm()` first. For the unreleased `current_task` policy, require exact same-run/original-L declared open and confirm routes plus explicit response mode; bind L and C (`sessionId === raw.control_interaction_id`, not `raw.browser_session_id`). Open uses the existing task browser with SITE_LOGIN_REQUEST + BROWSER_USE_TAKE_CONTROL. Confirm uses SITE_LOGIN_CONFIRM + BROWSER_USE_TAKE_CONTROL + DO_ANYTHING_MANAGE, sending only one `{login_session_id:C}` request for reliable save/release/reply, without Profile confirm or another recheck. `login.confirm()` shares it and returns `state: saved, active: false, probeResult: agent_pending`; never treat persistence as verified sign-in. A successful open is required first. Invalid mode/L/C fails closed; 409, including `no_pending_login`, remains an error without fetching/replacing IDs or automatic reopen. Save failure, timeout/response loss or `site_login.invalid_response` allows an explicit retry of the original L/C; only backend exact saved history acknowledges lost success. Keep existing shared-slot/replacement guards. Legacy `/profiles/login` and Profile confirm retain single scopes, result semantics and recheck behavior. Obtain explicit user consent for all needed scopes after AccessKey policy preparation; manage cannot replace login/control permission.
|
|
368
|
+
- `fill_form` (Unreleased): render `payload.fields` and call `submit(values)` only when declared. Exact field names, option values, finite numbers with safe integers and ordinary address objects `{line1,line2?,city?,region?,postalCode?,country?}` are preserved. Required text/address.line1 is nonempty after trimming (original text is preserved); `0` and optional empty text are valid. Sensitive/payment-card/Profile-write fields are unavailable; credentials use browser sign-in/control. This requires the matching W4 backend and does not claim W6/provider acceptance. In this unreleased phase, structured Pi questions/forms use the main agent; subagents reject structured parameters before browser/network/card calls.
|
|
225
369
|
- `ask_user`: `answer(value)` with the type `payload.answerType` names (`text` string, `number`, `boolean`, `single_choice` option value, `multiple_choice` array of option values), or `skip()`. The wire's `clarification` arrives as `ask_user`; never switch on `"clarification"`.
|
|
226
|
-
- `take_control`: `connectControl()` only when `handle.can("connect_control")`; it resolves to `{ liveUrl, expiresAt }` (show that URL, else `payload.liveUrl`) → `releaseControl()`. `refreshControl()` returns a fresh URL.
|
|
370
|
+
- `take_control`: `connectControl()` only when `handle.can("connect_control")`; it resolves to `{ liveUrl, expiresAt }` (show that URL, else `payload.liveUrl`) → `releaseControl()`. `refreshControl()` returns a fresh URL. Explicitly delegate `QoniScopes.BROWSER_USE_TAKE_CONTROL` (`webagent.browser_use:take_control`) for all three methods; product `manage` and `products` bundles exclude it. Prepare the AccessKey policy and obtain user consent before creating the run whose token this handler uses.
|
|
227
371
|
- `confirmation`: `confirm()` or `reject()` after showing `payload.summary`.
|
|
228
372
|
- Only call methods `handle.can(kind)` reports; an undeclared action throws.
|
|
229
373
|
|
|
374
|
+
Browser-control migration is an unreleased breaking change. Prepare GenAuth registration and Console's browser-control/site-login policies, update only the needed AccessKey permissions, and obtain new user consent for the explicitly requested scopes. Do not expand an existing user grant through policy edits or token renewal. The old Web Agent release endpoint expects `manage`, the new SDK requests `take_control`, and the new Web Agent rejects `manage` as a substitute: use a maintenance window to finish or drain existing control tasks, then coordinate the Web Agent + SDK cutover and verify connect, refresh, release and missing-scope rejection. Do not restore legacy control aliases or add a `manage` bypass. The new Console lists permissions not covered by the selected policies separately, including legacy, unknown and partial policy entries. They are preserved by default; removing them requires selecting “Remove these permissions when saving”. Inspect the saved allowlist; preserved legacy scopes do not grant independent browser control. Deployment and publication still require a separate Owner instruction; this skill does not authorize them.
|
|
375
|
+
|
|
376
|
+
For a task that may initiate human control, create it with `taskScopes: [QoniScopes.BROWSER_USE_TAKE_CONTROL]` in `doAnything.run()` (or `api.createRun()` / `runWithMemory()`). Add specific site-login scopes only when required and already authorized. Default task creation requests manage only; delegation scope alone does not enlarge the task's original authority. `taskScopes` is SDK-only, requires known concrete Web Agent scopes, and is not supported by npm 0.9.0. Direct product tokens cannot gain authority through it.
|
|
377
|
+
|
|
378
|
+
For raw `doAnything.api.intervene()` control calls, pass `interactionId` (wire alias `interaction_id`) captured for that round. Refresh/release require it; a first user-initiated take may omit it only without an active marker, while reconnecting to an active round requires its exact ID. New take/refresh responses contain `data.interaction_id`; use the exported `DoAnythingControlResponse` type and save that field. Missing or stale IDs reach the server and return 409; do not fetch the current ID and retry, and do not rebind an old `ControlSession` to a new card. Invalidate the stale UI and wait for a separate user action on a new interaction. Card methods already carry their declared path ID. `requestId` is for input questions, not control rounds. Conflicting aliases or explicit non-string/empty IDs fail SDK validation before HTTP; omission is forwarded for server enforcement.
|
|
379
|
+
|
|
380
|
+
Raw input answer/skip calls must target the current persisted pending request and its declared action; stale or undeclared actions return 409. Use the declared confirmation/sign-in methods, not ordinary answers or skips as substitutes. Card connect returns pending; an OBO/API-key caller cannot claim an active Console connection through `surface`. Releasing a pending round leaves the pending login/credential ask unresolved. A connect-only login card cannot substitute for the declared current_task open/confirm flow above; actual page verification still belongs to the agent and deployment remains pending.
|
|
381
|
+
|
|
382
|
+
The unreleased recall mapping sends `metadataFilters` inside `recall_config.metadata_filters`. Matching aliases merge; conflicts, null/non-scalar/non-finite values and normalized system keys (`project_id`, `group_id`, `user_id`, `thread_id`, `session_id`, `scope`, `expired_at`, `validity`) throw `QoniValidationError` before HTTP. Other config keys pass through unchanged. This is SDK-side validation, not a backend isolation fix or cross-task recall acceptance.
|
|
383
|
+
|
|
230
384
|
```ts
|
|
231
385
|
const result = await run.wait({
|
|
232
386
|
onInteraction: async (handle) => {
|
|
@@ -344,3 +498,19 @@ For skill or docs changes that teach integration, also validate from a fresh con
|
|
|
344
498
|
5. Run the consumer project's typecheck and test command.
|
|
345
499
|
|
|
346
500
|
Prefer mocked fetch-based tests for normal verification. Use real GenAuth/GUMem/WebAgent services only for explicit smoke tests, and never hard-code real secrets in repository files.
|
|
501
|
+
|
|
502
|
+
## Authing v3 management additions
|
|
503
|
+
|
|
504
|
+
The SDK provides 591 typed JSON management methods in 35 namespaces, aligned with the official Authing Node SDK 4.0.2 (496 JSON methods) plus GenAuth extensions and compatibility names. Read `docs/genauth-management.md` and `docs/genauth-management-api.md` for exact methods, aliases, inputs and deployment-dependent rules. Preserve the Qoni management-token exchange; do not use a delegated runtime token for management calls.
|
|
505
|
+
|
|
506
|
+
- Use `genauth.users.iterate` for paginated user processing (limit 1-50); pass cancellation in the optional second argument.
|
|
507
|
+
- Roles and resources belong to an existing permission namespace. Resource action names use `resourceCode:action`; newer permission deployments require `apiIdentifier` for API resources.
|
|
508
|
+
- `customFields.set` requires `dataType` for both create and update. `customFields.list` defaults the subject to `USER`.
|
|
509
|
+
- HTTP-200 business errors throw typed Qoni errors, including numeric Authing API codes as strings. Do not replay writes after network or business failures.
|
|
510
|
+
- Respect employment rules: resign a user only when departure is intended. The SDK never automatically resigns users to make deletion succeed.
|
|
511
|
+
- Filtered user queries depend on the GenAuth search index. A successful `get` or batch lookup does not prove that a newly created user is searchable. Keep search acceptance failures visible and diagnose the server index separately.
|
|
512
|
+
- Run `pnpm test:e2e:genauth` using an isolated test userpool; cleanup failures fail the suite. Generated contract coverage does not imply live acceptance of every operation.
|
|
513
|
+
|
|
514
|
+
### Agent Mail and Agent Phone (unreleased extension)
|
|
515
|
+
|
|
516
|
+
Use `qoni.genauth.agentMail` and `qoni.genauth.agentPhone`. Read `docs/genauth-communication.md` and `docs/genauth-communication-api.md` before using these APIs. Runtime requires native GenAuth credentials through `genauthCommunicationCredentials`; administrator calls reuse Qoni management exchange. Preserve stable idempotency keys, pass current revisions, and inspect original operations after ambiguous results. Never treat an unknown purchase/send outcome as safe to repeat. Phone numbers, mailboxes, and communication personas are distinct from login email/phone and delegation agentId.
|