@qoniai/qoni 0.8.0 → 0.9.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 +102 -2
- package/README.md +119 -72
- package/README.zh-CN.md +73 -44
- package/dist/index.cjs +355 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +163 -42
- package/dist/index.d.ts +163 -42
- package/dist/index.js +355 -58
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/skills/qoni-sdk/SKILL.md +42 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qoniai/qoni",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Unified Node.js SDK for Qoni Agent delegation, GUMem memory, and WebAgent automation.",
|
|
5
5
|
"homepage": "https://github.com/QONIAI/qoni-sdk-node#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"gen:event-types": "node scripts/gen-event-types.mjs",
|
|
47
47
|
"typecheck": "tsc --noEmit",
|
|
48
48
|
"test": "pnpm build && pnpm check:brand && pnpm test:mock && pnpm test:package",
|
|
49
|
-
"test:mock": "vitest run --coverage=false tests/qoni.test.ts tests/semantic-surface.test.ts tests/open-source-readiness.test.ts tests/complete-coverage.test.ts tests/event-catalog.test.ts tests/interactions.test.ts tests/contract-surface.test.ts tests/sse-abort.test.ts tests/do-anything-evidence.test.ts tests/query-matrix.test.ts tests/live-evidence.test.ts tests/site-login-settle-track.test.ts",
|
|
49
|
+
"test:mock": "vitest run --coverage=false tests/qoni.test.ts tests/semantic-surface.test.ts tests/open-source-readiness.test.ts tests/complete-coverage.test.ts tests/event-catalog.test.ts tests/interactions.test.ts tests/contract-surface.test.ts tests/sse-abort.test.ts tests/do-anything-evidence.test.ts tests/query-matrix.test.ts tests/live-evidence.test.ts tests/site-login-settle-track.test.ts tests/abstract-interactions.test.ts",
|
|
50
50
|
"test:e2e": "QONI_LIVE_E2E=1 vitest run --coverage=false --fileParallelism=false --maxWorkers=1 --testTimeout=120000 tests/live-e2e/*.test.ts",
|
|
51
51
|
"test:e2e:do-anything": "QONI_LIVE_E2E=1 vitest run --coverage=false --fileParallelism=false --maxWorkers=1 --testTimeout=120000 tests/live-e2e/do-anything.test.ts",
|
|
52
52
|
"test:e2e:do-anything:release": "QONI_LIVE_E2E=1 QONI_LIVE_ALLOW_ENV_CONSTRAINTS=0 vitest run --coverage=false --fileParallelism=false --maxWorkers=1 --testTimeout=120000 tests/live-e2e/do-anything.test.ts",
|
package/skills/qoni-sdk/SKILL.md
CHANGED
|
@@ -66,12 +66,12 @@ const run = await qoni.doAnything.run({
|
|
|
66
66
|
});
|
|
67
67
|
const result = await run.wait({
|
|
68
68
|
onScreenshot: (img, i) => saveImage(`step-${i}.jpg`, img.bytes),
|
|
69
|
-
|
|
69
|
+
onInteraction: (handle) => handleInteraction(handle), // see "Do Anything Interactions"
|
|
70
70
|
});
|
|
71
71
|
console.log(result.output);
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
For explicit authorization, the Qoni Console/BFF owns GenAuth login and grant completion before it redirects back to the business `redirectUri`. The redirect only carries one-time callback fields
|
|
74
|
+
For explicit authorization, the Qoni Console/BFF owns GenAuth login and grant completion before it redirects back to the business `redirectUri`. The redirect only carries one-time callback fields: `code`, `grant_state`, and your business `state` when you supplied one. It does not carry `grantId`; load the `grantId` you saved by `grant_state`. Never design an API that exposes a delegation token to browser code.
|
|
75
75
|
|
|
76
76
|
```ts
|
|
77
77
|
const grant = await qoni.delegateToken({
|
|
@@ -82,12 +82,17 @@ const grant = await qoni.delegateToken({
|
|
|
82
82
|
scopes: [QoniScopes.GUMEM_MEMORY_READ],
|
|
83
83
|
});
|
|
84
84
|
|
|
85
|
+
// The callback carries code, state (your business state) and grant_state, but no grantId.
|
|
86
|
+
await savePendingGrant(grant.data.grantState, { grantId: grant.data.grantId, businessState: "business-csrf-state" });
|
|
85
87
|
redirectUserTo(grant.data.authorizationUrl);
|
|
86
88
|
|
|
89
|
+
// Callback handler: look up by grant_state, check the business state, then exchange.
|
|
90
|
+
const pending = await loadPendingGrant(callback.grant_state);
|
|
91
|
+
if (!pending || callback.state !== pending.businessState) throw new Error("Invalid delegation callback");
|
|
87
92
|
const completed = await qoni.completeDelegateToken({
|
|
88
|
-
grantId,
|
|
89
|
-
code,
|
|
90
|
-
state,
|
|
93
|
+
grantId: pending.grantId,
|
|
94
|
+
code: callback.code,
|
|
95
|
+
state: callback.grant_state,
|
|
91
96
|
});
|
|
92
97
|
```
|
|
93
98
|
|
|
@@ -148,7 +153,7 @@ const run = await qoni.doAnything.run({
|
|
|
148
153
|
run.id;
|
|
149
154
|
run.sessionRef; // pass back to run({ session })
|
|
150
155
|
await run.status(); // refresh current state
|
|
151
|
-
|
|
156
|
+
run.interactionHandle(event.data); // wrap an interaction event; answer via its methods
|
|
152
157
|
await run.cancel(reason?); // idempotent — safe on a finished run
|
|
153
158
|
|
|
154
159
|
const result = await run.wait({
|
|
@@ -156,7 +161,7 @@ const result = await run.wait({
|
|
|
156
161
|
onEvent: (e) => console.log(e.type),
|
|
157
162
|
onWireEvent: (e) => appendRawEvidence(e), // every SSE frame before semantic filtering
|
|
158
163
|
onReconnect: (info) => logReconnect(info.attempt, info.lastEventId),
|
|
159
|
-
|
|
164
|
+
onInteraction: (handle) => handleInteraction(handle), // typed InteractionHandle
|
|
160
165
|
onScreenshot: (img, i) => saveImage(`step-${i}.jpg`, img.bytes),
|
|
161
166
|
});
|
|
162
167
|
// result: { runId, status: "succeeded"|"failed"|"canceled", output?, artifacts,
|
|
@@ -210,18 +215,38 @@ for await (const event of run.events()) {
|
|
|
210
215
|
|
|
211
216
|
`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.
|
|
212
217
|
|
|
213
|
-
## Do Anything
|
|
218
|
+
## Do Anything Interactions (human handoff)
|
|
214
219
|
|
|
215
|
-
|
|
220
|
+
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.
|
|
216
221
|
|
|
217
|
-
-
|
|
218
|
-
-
|
|
219
|
-
-
|
|
222
|
+
- 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
|
+
- Interactions arrive one at a time; the run resumes once the current one is handled.
|
|
224
|
+
- `site_login`: `openLogin()` → show `liveUrl` → after the user signs in, `confirmSignedIn()` (it saves the login once, then the agent rechecks). Do NOT call the deprecated `login.confirm()` first. A `QoniError` coded `site_login.save_failed` or `site_login.session_replaced` is retryable: call `confirmSignedIn()` again. Delegate `webagent.site_login:request` and `webagent.site_login:confirm`.
|
|
225
|
+
- `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.
|
|
227
|
+
- `confirmation`: `confirm()` or `reject()` after showing `payload.summary`.
|
|
228
|
+
- Only call methods `handle.can(kind)` reports; an undeclared action throws.
|
|
220
229
|
|
|
221
230
|
```ts
|
|
222
231
|
const result = await run.wait({
|
|
223
|
-
|
|
224
|
-
|
|
232
|
+
onInteraction: async (handle) => {
|
|
233
|
+
const request = handle.interaction;
|
|
234
|
+
if (request.status !== "pending" || handled.has(request.id)) return;
|
|
235
|
+
handled.add(request.id);
|
|
236
|
+
switch (request.type) {
|
|
237
|
+
case "site_login": {
|
|
238
|
+
const login = await handle.openLogin();
|
|
239
|
+
await showToUser(login.liveUrl); // resolves when the user says "I'm signed in"
|
|
240
|
+
await handle.confirmSignedIn();
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case "ask_user":
|
|
244
|
+
await handle.answer(await askUser(request.payload));
|
|
245
|
+
break;
|
|
246
|
+
case "confirmation":
|
|
247
|
+
await handle.confirm();
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
225
250
|
},
|
|
226
251
|
});
|
|
227
252
|
```
|
|
@@ -243,12 +268,9 @@ pending → running → (running ⇄ awaiting_input)* → succeeded | failed | c
|
|
|
243
268
|
`terminal_reason` / `is_task_successful`).
|
|
244
269
|
|
|
245
270
|
**Client action is event-driven, not status-driven.** The only thing that needs
|
|
246
|
-
you is
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
(e.g. `waiting_reason=confirmation_required` during internal pacing) is
|
|
250
|
-
expected — do NOT poll or call `respond`. Only call `respond` to answer an
|
|
251
|
-
actual `inputRequest`.
|
|
271
|
+
you is an `interaction` event (see Do Anything Interactions). A run flapping
|
|
272
|
+
`running ⇄ awaiting_input` (e.g. `waiting_reason=confirmation_required` during
|
|
273
|
+
internal pacing) is expected — do NOT poll; act only on pending interactions.
|
|
252
274
|
|
|
253
275
|
### Parent & sub-tasks
|
|
254
276
|
|