@qoniai/qoni 0.5.2 → 0.7.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 +54 -0
- package/README.md +52 -6
- package/README.zh-CN.md +36 -6
- package/dist/index.cjs +305 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +101 -8
- package/dist/index.d.ts +101 -8
- package/dist/index.js +305 -147
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -230,6 +230,8 @@ var QoniEventTypes = {
|
|
|
230
230
|
Interaction: "interaction",
|
|
231
231
|
/** A live screenshot frame (opt in with `capture: { screenshots: true }`); image on `event.image`. */
|
|
232
232
|
Screenshot: "screenshot",
|
|
233
|
+
/** The browser viewer moved to a new page or needs a fresh attachment. Use `event.data.liveUrl` for the live preview. */
|
|
234
|
+
BrowserLiveUrlChanged: "browserLiveUrlChanged",
|
|
233
235
|
/** Terminal — the run finished; outcome + output in `event.data` (cost is on the `RunResult`). */
|
|
234
236
|
Done: "done",
|
|
235
237
|
// --- Web Search ---
|
|
@@ -360,7 +362,6 @@ var WIRE_TO_SEMANTIC = {
|
|
|
360
362
|
"run.recording_started": "progress",
|
|
361
363
|
"run.recording_paused": "progress",
|
|
362
364
|
"run.recording_finalized": "progress",
|
|
363
|
-
"run.browser_live_url_changed": "progress",
|
|
364
365
|
// side-channel
|
|
365
366
|
"run.feedback_submitted": "progress"
|
|
366
367
|
};
|
|
@@ -422,6 +423,18 @@ function normalizeRunEvent(wire, options = {}) {
|
|
|
422
423
|
return { ...base, type: "screenshot", data: sData, image, isTerminal: false };
|
|
423
424
|
}
|
|
424
425
|
}
|
|
426
|
+
if (wireType === "run.browser_live_url_changed") {
|
|
427
|
+
const liveUrl = asString(c.liveUrl);
|
|
428
|
+
if (!liveUrl) return null;
|
|
429
|
+
const data2 = {
|
|
430
|
+
liveUrl,
|
|
431
|
+
supportsLivePump: c.supportsLivePump === true,
|
|
432
|
+
forceViewerReload: c.forceViewerReload === true
|
|
433
|
+
};
|
|
434
|
+
const providerSessionId = asString(c.providerSessionId);
|
|
435
|
+
if (providerSessionId) data2.providerSessionId = providerSessionId;
|
|
436
|
+
return { ...base, type: "browserLiveUrlChanged", data: data2, isTerminal: false };
|
|
437
|
+
}
|
|
425
438
|
if (wireType === INTERACTION) {
|
|
426
439
|
const interaction = normalizeInteraction(payload);
|
|
427
440
|
if (interaction) {
|
|
@@ -477,6 +490,87 @@ var ActionKinds = {
|
|
|
477
490
|
};
|
|
478
491
|
var ACTION_KIND_VALUES = Object.values(ActionKinds);
|
|
479
492
|
|
|
493
|
+
// src/scopes.ts
|
|
494
|
+
var QoniScopes = {
|
|
495
|
+
GUMEM_SESSION_CREATE: "gumem.session:create",
|
|
496
|
+
GUMEM_MESSAGE_WRITE: "gumem.message:write",
|
|
497
|
+
GUMEM_MEMORY_READ: "gumem.memory:read",
|
|
498
|
+
GUMEM_MEMORY_WRITE: "gumem.memory:write",
|
|
499
|
+
GUMEM_MEMORY_DELETE: "gumem.memory:delete",
|
|
500
|
+
GUMEM_SEARCH_RUN: "gumem.search:run",
|
|
501
|
+
GUMEM_ADMIN_MANAGE: "gumem.admin:manage",
|
|
502
|
+
GUMEM_RESOURCE_WRITE: "gumem.resource:write",
|
|
503
|
+
GUMEM_ACTION_WRITE: "gumem.action:write",
|
|
504
|
+
GUMEM_ACTION_READ: "gumem.action:read",
|
|
505
|
+
GUMEM_PROFILE_READ: "gumem.profile:read",
|
|
506
|
+
// WebAgent products: exactly two verbs per product — `read` (observe) and
|
|
507
|
+
// `manage` (anything that changes execution state). See
|
|
508
|
+
// wa/docs/qoni-sdk-public-surface.md §3.
|
|
509
|
+
DO_ANYTHING_READ: "webagent.do_anything:read",
|
|
510
|
+
DO_ANYTHING_MANAGE: "webagent.do_anything:manage",
|
|
511
|
+
WEB_SEARCH_READ: "webagent.web_search:read",
|
|
512
|
+
WEB_SEARCH_MANAGE: "webagent.web_search:manage",
|
|
513
|
+
DEEP_RESEARCH_READ: "webagent.deep_research:read",
|
|
514
|
+
DEEP_RESEARCH_MANAGE: "webagent.deep_research:manage",
|
|
515
|
+
TRACK_READ: "webagent.track:read",
|
|
516
|
+
TRACK_MANAGE: "webagent.track:manage",
|
|
517
|
+
// Interactive site login (`InteractionHandle.openLogin()` on a `site_login`
|
|
518
|
+
// interaction). Not part of any product bundle: the AccessKey policy must
|
|
519
|
+
// allow it, and the delegation must request it explicitly.
|
|
520
|
+
SITE_LOGIN_REQUEST: "webagent.site_login:request",
|
|
521
|
+
// Persist that login into the browser profile (`OpenLoginResult.confirm()`).
|
|
522
|
+
SITE_LOGIN_CONFIRM: "webagent.site_login:confirm"
|
|
523
|
+
};
|
|
524
|
+
var QoniProductScopes = {
|
|
525
|
+
doAnything: [
|
|
526
|
+
QoniScopes.DO_ANYTHING_READ,
|
|
527
|
+
QoniScopes.DO_ANYTHING_MANAGE
|
|
528
|
+
],
|
|
529
|
+
webSearch: [
|
|
530
|
+
QoniScopes.WEB_SEARCH_READ,
|
|
531
|
+
QoniScopes.WEB_SEARCH_MANAGE
|
|
532
|
+
],
|
|
533
|
+
deepResearch: [
|
|
534
|
+
QoniScopes.DEEP_RESEARCH_READ,
|
|
535
|
+
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
536
|
+
],
|
|
537
|
+
track: [
|
|
538
|
+
QoniScopes.TRACK_READ,
|
|
539
|
+
QoniScopes.TRACK_MANAGE
|
|
540
|
+
]
|
|
541
|
+
};
|
|
542
|
+
var KNOWN_SCOPES = Object.values(QoniScopes);
|
|
543
|
+
var QoniScopeBundles = {
|
|
544
|
+
GUMEM_READONLY: [QoniScopes.GUMEM_MEMORY_READ, QoniScopes.GUMEM_PROFILE_READ],
|
|
545
|
+
GUMEM_SESSION_RECALL: [
|
|
546
|
+
QoniScopes.GUMEM_MEMORY_READ,
|
|
547
|
+
QoniScopes.GUMEM_MEMORY_WRITE,
|
|
548
|
+
QoniScopes.GUMEM_MESSAGE_WRITE
|
|
549
|
+
],
|
|
550
|
+
GUMEM_WRITE: [
|
|
551
|
+
QoniScopes.GUMEM_SESSION_CREATE,
|
|
552
|
+
QoniScopes.GUMEM_MESSAGE_WRITE,
|
|
553
|
+
QoniScopes.GUMEM_ACTION_WRITE
|
|
554
|
+
],
|
|
555
|
+
// One bundle per WebAgent product = [read, manage].
|
|
556
|
+
DO_ANYTHING: [
|
|
557
|
+
QoniScopes.DO_ANYTHING_READ,
|
|
558
|
+
QoniScopes.DO_ANYTHING_MANAGE
|
|
559
|
+
],
|
|
560
|
+
WEB_SEARCH: [
|
|
561
|
+
QoniScopes.WEB_SEARCH_READ,
|
|
562
|
+
QoniScopes.WEB_SEARCH_MANAGE
|
|
563
|
+
],
|
|
564
|
+
DEEP_RESEARCH: [
|
|
565
|
+
QoniScopes.DEEP_RESEARCH_READ,
|
|
566
|
+
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
567
|
+
],
|
|
568
|
+
TRACK: [
|
|
569
|
+
QoniScopes.TRACK_READ,
|
|
570
|
+
QoniScopes.TRACK_MANAGE
|
|
571
|
+
]
|
|
572
|
+
};
|
|
573
|
+
|
|
480
574
|
// src/interactions.ts
|
|
481
575
|
function asString2(value) {
|
|
482
576
|
return typeof value === "string" && value ? value : void 0;
|
|
@@ -575,6 +669,9 @@ function normalizeInteraction(payload) {
|
|
|
575
669
|
if (evidence) interaction.evidence = evidence;
|
|
576
670
|
return interaction;
|
|
577
671
|
}
|
|
672
|
+
function interactionActionScopes(action, productManageScope) {
|
|
673
|
+
return action.kind === ActionKinds.openLogin ? [QoniScopes.SITE_LOGIN_REQUEST] : [productManageScope];
|
|
674
|
+
}
|
|
578
675
|
var InteractionHandle = class {
|
|
579
676
|
constructor(interaction, post) {
|
|
580
677
|
this.post = post;
|
|
@@ -599,48 +696,122 @@ var InteractionHandle = class {
|
|
|
599
696
|
return this.interaction.actions.some((a) => a.kind === kind);
|
|
600
697
|
}
|
|
601
698
|
/** Answer a clarification ask. */
|
|
602
|
-
answer(text) {
|
|
603
|
-
|
|
699
|
+
async answer(text) {
|
|
700
|
+
await this.invoke(ActionKinds.answer, { response: text });
|
|
604
701
|
}
|
|
605
702
|
/** Skip / dismiss the interaction (proceed without acting on it). */
|
|
606
|
-
skip() {
|
|
607
|
-
|
|
703
|
+
async skip() {
|
|
704
|
+
await this.invoke(ActionKinds.skip);
|
|
608
705
|
}
|
|
609
706
|
/** Confirm a pending confirmation (approve the plan / destructive op). */
|
|
610
|
-
confirm() {
|
|
611
|
-
|
|
707
|
+
async confirm() {
|
|
708
|
+
await this.invoke(ActionKinds.confirm);
|
|
612
709
|
}
|
|
613
710
|
/** Reject a pending confirmation. */
|
|
614
|
-
reject() {
|
|
615
|
-
|
|
711
|
+
async reject() {
|
|
712
|
+
await this.invoke(ActionKinds.reject);
|
|
616
713
|
}
|
|
617
|
-
/**
|
|
618
|
-
|
|
619
|
-
|
|
714
|
+
/**
|
|
715
|
+
* Start an interactive login session for a `site_login` interaction. The
|
|
716
|
+
* backend needs the site to sign in to: pass `siteId`, or omit it when the
|
|
717
|
+
* interaction lists exactly one site. Show `liveUrl` to the user; when they
|
|
718
|
+
* finish, call `confirm()` on the result to save the login, and act on its
|
|
719
|
+
* outcome (see {@link OpenLoginResult.confirm}) before {@link confirmSignedIn}.
|
|
720
|
+
* The delegation must include `QoniScopes.SITE_LOGIN_REQUEST` and
|
|
721
|
+
* `QoniScopes.SITE_LOGIN_CONFIRM`; the AccessKey policy must allow both.
|
|
722
|
+
*/
|
|
723
|
+
async openLogin(options = {}) {
|
|
724
|
+
this.#declaredAction(ActionKinds.openLogin);
|
|
725
|
+
const body = { site_id: this.#loginSiteId(options.siteId) };
|
|
726
|
+
if (options.profileId !== void 0) {
|
|
727
|
+
if (typeof options.profileId !== "string" || !options.profileId) {
|
|
728
|
+
throw new QoniValidationError("openLogin profileId must be a non-empty string");
|
|
729
|
+
}
|
|
730
|
+
body.profile_id = options.profileId;
|
|
731
|
+
}
|
|
732
|
+
const response = await this.invoke(ActionKinds.openLogin, body);
|
|
733
|
+
const raw = isJsonObject(response) ? response : {};
|
|
734
|
+
const fields = camelizeRecord(raw);
|
|
735
|
+
const profileId = asString2(fields.profileId) ?? "";
|
|
736
|
+
const siteId = asString2(fields.siteId) ?? String(body.site_id);
|
|
737
|
+
return {
|
|
738
|
+
profileId,
|
|
739
|
+
siteId,
|
|
740
|
+
sessionId: asString2(fields.sessionId) ?? "",
|
|
741
|
+
liveUrl: asString2(fields.liveUrl) ?? "",
|
|
742
|
+
raw,
|
|
743
|
+
confirm: async (confirmOptions = {}) => {
|
|
744
|
+
if (!profileId) {
|
|
745
|
+
throw new QoniValidationError("openLogin response did not include a profileId to confirm");
|
|
746
|
+
}
|
|
747
|
+
const confirmBody = { site_id: siteId };
|
|
748
|
+
if (confirmOptions.displayLabel !== void 0) confirmBody.display_label = confirmOptions.displayLabel;
|
|
749
|
+
const response2 = await this.post(
|
|
750
|
+
{
|
|
751
|
+
kind: ActionKinds.openLogin,
|
|
752
|
+
label: "Confirm login",
|
|
753
|
+
method: "POST",
|
|
754
|
+
endpoint: `/profiles/${encodeURIComponent(profileId)}/confirm`
|
|
755
|
+
},
|
|
756
|
+
confirmBody,
|
|
757
|
+
{ requiredScopes: [QoniScopes.SITE_LOGIN_CONFIRM] }
|
|
758
|
+
);
|
|
759
|
+
const raw2 = isJsonObject(response2) ? response2 : {};
|
|
760
|
+
const fields2 = camelizeRecord(raw2);
|
|
761
|
+
const state = asString2(fields2.state) ?? "";
|
|
762
|
+
return {
|
|
763
|
+
profileId: asString2(fields2.profileId) ?? profileId,
|
|
764
|
+
active: state === "active",
|
|
765
|
+
state,
|
|
766
|
+
probeResult: asString2(fields2.probeResult) ?? "",
|
|
767
|
+
raw: raw2
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
};
|
|
620
771
|
}
|
|
621
772
|
/** Tell the agent the user has finished signing in. */
|
|
622
|
-
confirmSignedIn() {
|
|
623
|
-
|
|
773
|
+
async confirmSignedIn() {
|
|
774
|
+
await this.invoke(ActionKinds.confirmSignedIn);
|
|
624
775
|
}
|
|
625
776
|
/** Connect to the live browser for a take_control interaction. */
|
|
626
|
-
connectControl() {
|
|
627
|
-
|
|
777
|
+
async connectControl() {
|
|
778
|
+
await this.invoke(ActionKinds.connectControl);
|
|
628
779
|
}
|
|
629
780
|
/** Refresh the live control surface / live url. */
|
|
630
|
-
refreshControl() {
|
|
631
|
-
|
|
781
|
+
async refreshControl() {
|
|
782
|
+
await this.invoke(ActionKinds.refreshControl);
|
|
632
783
|
}
|
|
633
784
|
/** Hand control back to the agent. */
|
|
634
|
-
releaseControl() {
|
|
635
|
-
|
|
785
|
+
async releaseControl() {
|
|
786
|
+
await this.invoke(ActionKinds.releaseControl);
|
|
636
787
|
}
|
|
637
788
|
/** Retry a wait interaction now. */
|
|
638
|
-
retry() {
|
|
639
|
-
|
|
789
|
+
async retry() {
|
|
790
|
+
await this.invoke(ActionKinds.retry);
|
|
640
791
|
}
|
|
641
792
|
/** Switch the profile a wait/login interaction is bound to. */
|
|
642
|
-
switchProfile() {
|
|
643
|
-
|
|
793
|
+
async switchProfile() {
|
|
794
|
+
await this.invoke(ActionKinds.switchProfile);
|
|
795
|
+
}
|
|
796
|
+
/** Resolve the site for `openLogin`: explicit, or the only listed site. */
|
|
797
|
+
#loginSiteId(requested) {
|
|
798
|
+
const payload = this.interaction.payload;
|
|
799
|
+
const listed = Array.isArray(payload?.sites) ? payload.sites.map((site) => site.siteId).filter((id) => typeof id === "string" && id.length > 0) : [];
|
|
800
|
+
if (requested !== void 0) {
|
|
801
|
+
if (typeof requested !== "string" || !requested) {
|
|
802
|
+
throw new QoniValidationError("openLogin siteId must be a non-empty string");
|
|
803
|
+
}
|
|
804
|
+
if (listed.length > 0 && !listed.includes(requested)) {
|
|
805
|
+
throw new QoniValidationError(
|
|
806
|
+
`openLogin siteId "${requested}" is not one of the sites this interaction asks for: ${listed.join(", ")}`
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
return requested;
|
|
810
|
+
}
|
|
811
|
+
if (listed.length === 1) return listed[0];
|
|
812
|
+
throw new QoniValidationError(
|
|
813
|
+
listed.length === 0 ? "openLogin needs a siteId: this interaction does not list the site to sign in to" : `openLogin needs a siteId: choose one of ${listed.join(", ")}`
|
|
814
|
+
);
|
|
644
815
|
}
|
|
645
816
|
/**
|
|
646
817
|
* Invoke an action by kind with an optional body. `async` so a missing-action
|
|
@@ -648,6 +819,10 @@ var InteractionHandle = class {
|
|
|
648
819
|
* method contract uniformly promise-based.
|
|
649
820
|
*/
|
|
650
821
|
async invoke(kind, body) {
|
|
822
|
+
return this.post(this.#declaredAction(kind), body);
|
|
823
|
+
}
|
|
824
|
+
/** The declared action of `kind`; throws when the backend did not offer it. */
|
|
825
|
+
#declaredAction(kind) {
|
|
651
826
|
const action = this.interaction.actions.find((a) => a.kind === kind);
|
|
652
827
|
if (!action) {
|
|
653
828
|
const offered = this.interaction.actions.map((a) => a.kind).join(", ") || "none";
|
|
@@ -655,7 +830,7 @@ var InteractionHandle = class {
|
|
|
655
830
|
`interaction "${this.interaction.id}" (${this.interaction.type}) does not offer the "${kind}" action \u2014 the backend declared: ${offered}. Only call methods whose action the interaction currently offers.`
|
|
656
831
|
);
|
|
657
832
|
}
|
|
658
|
-
|
|
833
|
+
return action;
|
|
659
834
|
}
|
|
660
835
|
};
|
|
661
836
|
|
|
@@ -725,7 +900,7 @@ var RunHandle = class {
|
|
|
725
900
|
break settled;
|
|
726
901
|
}
|
|
727
902
|
}
|
|
728
|
-
const detail2 = await
|
|
903
|
+
const detail2 = await settleRead(() => this.ops.getDetail(signal), signal);
|
|
729
904
|
if (isTerminalRunStatus(normalizeRunStatus(this.id, detail2).status)) {
|
|
730
905
|
terminalDetail = detail2;
|
|
731
906
|
break;
|
|
@@ -739,10 +914,10 @@ var RunHandle = class {
|
|
|
739
914
|
sawCleanClose = true;
|
|
740
915
|
eventIdAtLastClose = lastEventId;
|
|
741
916
|
}
|
|
742
|
-
const detail = terminalDetail ?? await
|
|
917
|
+
const detail = terminalDetail ?? await settleRead(() => this.ops.getDetail(signal), signal);
|
|
743
918
|
this.#adoptSessionRef(detail);
|
|
744
919
|
const result = settleRunResult(this.id, detail, [], terminalDoneReason);
|
|
745
|
-
result.artifacts = this.ops.listArtifacts ? await
|
|
920
|
+
result.artifacts = this.ops.listArtifacts ? await settleRead(() => this.ops.listArtifacts(signal), signal) : [];
|
|
746
921
|
return result;
|
|
747
922
|
} catch (err) {
|
|
748
923
|
if (timedOut() && !opts.signal?.aborted) {
|
|
@@ -765,7 +940,7 @@ var RunHandle = class {
|
|
|
765
940
|
interactionHandle(interaction) {
|
|
766
941
|
return new InteractionHandle(
|
|
767
942
|
interaction,
|
|
768
|
-
(action, body) => this.ops.postAction(action, body)
|
|
943
|
+
(action, body, options) => this.ops.postAction(action, body, options)
|
|
769
944
|
);
|
|
770
945
|
}
|
|
771
946
|
/**
|
|
@@ -859,6 +1034,35 @@ function withTimeout(signal, timeoutMs) {
|
|
|
859
1034
|
}
|
|
860
1035
|
return { signal: controller.signal, timedOut: () => fired, dispose };
|
|
861
1036
|
}
|
|
1037
|
+
var SETTLE_READ_BACKOFF_MS = [250, 1e3];
|
|
1038
|
+
async function settleRead(read, signal) {
|
|
1039
|
+
for (let attempt = 0; ; attempt++) {
|
|
1040
|
+
try {
|
|
1041
|
+
return await waitWithSignal(read, signal);
|
|
1042
|
+
} catch (err) {
|
|
1043
|
+
const delay = SETTLE_READ_BACKOFF_MS[attempt];
|
|
1044
|
+
if (delay === void 0 || signal?.aborted || !(err instanceof QoniError) || !err.retryable) throw err;
|
|
1045
|
+
await sleepWithSignal(delay, signal);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
function sleepWithSignal(ms, signal) {
|
|
1050
|
+
return new Promise((resolve, reject) => {
|
|
1051
|
+
if (signal?.aborted) {
|
|
1052
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
const onAbort = () => {
|
|
1056
|
+
clearTimeout(timer);
|
|
1057
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
1058
|
+
};
|
|
1059
|
+
const timer = setTimeout(() => {
|
|
1060
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1061
|
+
resolve();
|
|
1062
|
+
}, ms);
|
|
1063
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
862
1066
|
function waitWithSignal(operation, signal) {
|
|
863
1067
|
if (!signal) return Promise.resolve().then(operation);
|
|
864
1068
|
return new Promise((resolve, reject) => {
|
|
@@ -956,17 +1160,15 @@ function createDeepResearchNamespace(transport) {
|
|
|
956
1160
|
signal: opts.signal,
|
|
957
1161
|
onReconnect: opts.onReconnect
|
|
958
1162
|
}),
|
|
959
|
-
postAction: async (action, body) =>
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
{
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
);
|
|
969
|
-
},
|
|
1163
|
+
postAction: async (action, body, options) => (await transport.webAgentJson(
|
|
1164
|
+
action.method || "POST",
|
|
1165
|
+
action.endpoint,
|
|
1166
|
+
token,
|
|
1167
|
+
{
|
|
1168
|
+
...body !== void 0 ? { body } : {},
|
|
1169
|
+
requiredScopes: options?.requiredScopes ?? interactionActionScopes(action, "webagent.deep_research:manage")
|
|
1170
|
+
}
|
|
1171
|
+
)).data,
|
|
970
1172
|
cancel: async () => asRecord((await api.cancel({ token, runId })).data),
|
|
971
1173
|
listArtifacts: async (signal) => {
|
|
972
1174
|
const res = await api.listArtifacts({ token, runId, signal });
|
|
@@ -1231,17 +1433,15 @@ function createDoAnythingNamespace(transport) {
|
|
|
1231
1433
|
// Skip screenshot rows in backfill when the caller did not opt in.
|
|
1232
1434
|
includeScreenshots: capture?.screenshots ? void 0 : false
|
|
1233
1435
|
}),
|
|
1234
|
-
postAction: async (action, body) =>
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
{
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
);
|
|
1244
|
-
},
|
|
1436
|
+
postAction: async (action, body, options) => (await transport.webAgentJson(
|
|
1437
|
+
action.method || "POST",
|
|
1438
|
+
action.endpoint,
|
|
1439
|
+
token,
|
|
1440
|
+
{
|
|
1441
|
+
...body !== void 0 ? { body } : {},
|
|
1442
|
+
requiredScopes: options?.requiredScopes ?? interactionActionScopes(action, "webagent.do_anything:manage")
|
|
1443
|
+
}
|
|
1444
|
+
)).data,
|
|
1245
1445
|
cancel: async (reason) => asRecord2(
|
|
1246
1446
|
(await api.cancel({
|
|
1247
1447
|
token,
|
|
@@ -1362,81 +1562,6 @@ function createQoniNamespace(_transport, delegateToken, completeDelegateToken) {
|
|
|
1362
1562
|
};
|
|
1363
1563
|
}
|
|
1364
1564
|
|
|
1365
|
-
// src/scopes.ts
|
|
1366
|
-
var QoniScopes = {
|
|
1367
|
-
GUMEM_SESSION_CREATE: "gumem.session:create",
|
|
1368
|
-
GUMEM_MESSAGE_WRITE: "gumem.message:write",
|
|
1369
|
-
GUMEM_MEMORY_READ: "gumem.memory:read",
|
|
1370
|
-
GUMEM_MEMORY_WRITE: "gumem.memory:write",
|
|
1371
|
-
GUMEM_MEMORY_DELETE: "gumem.memory:delete",
|
|
1372
|
-
GUMEM_SEARCH_RUN: "gumem.search:run",
|
|
1373
|
-
GUMEM_ADMIN_MANAGE: "gumem.admin:manage",
|
|
1374
|
-
GUMEM_RESOURCE_WRITE: "gumem.resource:write",
|
|
1375
|
-
GUMEM_ACTION_WRITE: "gumem.action:write",
|
|
1376
|
-
GUMEM_ACTION_READ: "gumem.action:read",
|
|
1377
|
-
GUMEM_PROFILE_READ: "gumem.profile:read",
|
|
1378
|
-
// WebAgent products: exactly two verbs per product — `read` (observe) and
|
|
1379
|
-
// `manage` (anything that changes execution state). See
|
|
1380
|
-
// wa/docs/qoni-sdk-public-surface.md §3.
|
|
1381
|
-
DO_ANYTHING_READ: "webagent.do_anything:read",
|
|
1382
|
-
DO_ANYTHING_MANAGE: "webagent.do_anything:manage",
|
|
1383
|
-
WEB_SEARCH_READ: "webagent.web_search:read",
|
|
1384
|
-
WEB_SEARCH_MANAGE: "webagent.web_search:manage",
|
|
1385
|
-
DEEP_RESEARCH_READ: "webagent.deep_research:read",
|
|
1386
|
-
DEEP_RESEARCH_MANAGE: "webagent.deep_research:manage",
|
|
1387
|
-
TRACK_READ: "webagent.track:read",
|
|
1388
|
-
TRACK_MANAGE: "webagent.track:manage"
|
|
1389
|
-
};
|
|
1390
|
-
var QoniProductScopes = {
|
|
1391
|
-
doAnything: [
|
|
1392
|
-
QoniScopes.DO_ANYTHING_READ,
|
|
1393
|
-
QoniScopes.DO_ANYTHING_MANAGE
|
|
1394
|
-
],
|
|
1395
|
-
webSearch: [
|
|
1396
|
-
QoniScopes.WEB_SEARCH_READ,
|
|
1397
|
-
QoniScopes.WEB_SEARCH_MANAGE
|
|
1398
|
-
],
|
|
1399
|
-
deepResearch: [
|
|
1400
|
-
QoniScopes.DEEP_RESEARCH_READ,
|
|
1401
|
-
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
1402
|
-
],
|
|
1403
|
-
track: [
|
|
1404
|
-
QoniScopes.TRACK_READ,
|
|
1405
|
-
QoniScopes.TRACK_MANAGE
|
|
1406
|
-
]
|
|
1407
|
-
};
|
|
1408
|
-
var KNOWN_SCOPES = Object.values(QoniScopes);
|
|
1409
|
-
var QoniScopeBundles = {
|
|
1410
|
-
GUMEM_READONLY: [QoniScopes.GUMEM_MEMORY_READ, QoniScopes.GUMEM_PROFILE_READ],
|
|
1411
|
-
GUMEM_SESSION_RECALL: [
|
|
1412
|
-
QoniScopes.GUMEM_MEMORY_READ,
|
|
1413
|
-
QoniScopes.GUMEM_MEMORY_WRITE,
|
|
1414
|
-
QoniScopes.GUMEM_MESSAGE_WRITE
|
|
1415
|
-
],
|
|
1416
|
-
GUMEM_WRITE: [
|
|
1417
|
-
QoniScopes.GUMEM_SESSION_CREATE,
|
|
1418
|
-
QoniScopes.GUMEM_MESSAGE_WRITE,
|
|
1419
|
-
QoniScopes.GUMEM_ACTION_WRITE
|
|
1420
|
-
],
|
|
1421
|
-
// One bundle per WebAgent product = [read, manage].
|
|
1422
|
-
DO_ANYTHING: [
|
|
1423
|
-
QoniScopes.DO_ANYTHING_READ,
|
|
1424
|
-
QoniScopes.DO_ANYTHING_MANAGE
|
|
1425
|
-
],
|
|
1426
|
-
WEB_SEARCH: [
|
|
1427
|
-
QoniScopes.WEB_SEARCH_READ,
|
|
1428
|
-
QoniScopes.WEB_SEARCH_MANAGE
|
|
1429
|
-
],
|
|
1430
|
-
DEEP_RESEARCH: [
|
|
1431
|
-
QoniScopes.DEEP_RESEARCH_READ,
|
|
1432
|
-
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
1433
|
-
],
|
|
1434
|
-
TRACK: [
|
|
1435
|
-
QoniScopes.TRACK_READ,
|
|
1436
|
-
QoniScopes.TRACK_MANAGE
|
|
1437
|
-
]
|
|
1438
|
-
};
|
|
1439
|
-
|
|
1440
1565
|
// src/genauth.ts
|
|
1441
1566
|
function createGenAuthNamespace(transport, options = {}) {
|
|
1442
1567
|
return {
|
|
@@ -1635,7 +1760,7 @@ var MonitorHandle = class {
|
|
|
1635
1760
|
id;
|
|
1636
1761
|
/** Refresh and return the monitor (normalized camelCase fields). */
|
|
1637
1762
|
async get() {
|
|
1638
|
-
return
|
|
1763
|
+
return normalizeMonitorRecord(await this.ops.get());
|
|
1639
1764
|
}
|
|
1640
1765
|
/**
|
|
1641
1766
|
* Pause the monitor — stops scheduled ticks until {@link resume}. The
|
|
@@ -1643,11 +1768,11 @@ var MonitorHandle = class {
|
|
|
1643
1768
|
* deleting.
|
|
1644
1769
|
*/
|
|
1645
1770
|
async pause() {
|
|
1646
|
-
return
|
|
1771
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "pause" }));
|
|
1647
1772
|
}
|
|
1648
1773
|
/** Resume a paused monitor. */
|
|
1649
1774
|
async resume() {
|
|
1650
|
-
return
|
|
1775
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "resume" }));
|
|
1651
1776
|
}
|
|
1652
1777
|
/**
|
|
1653
1778
|
* Change the monitor definition. Pass any subset of fields (at least one);
|
|
@@ -1665,11 +1790,11 @@ var MonitorHandle = class {
|
|
|
1665
1790
|
"track refine requires at least one field to change (schedule / targetUrls / extractionSchema / triggerDsl / stopConditionDsl / notifyChannel)"
|
|
1666
1791
|
);
|
|
1667
1792
|
}
|
|
1668
|
-
return
|
|
1793
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "refine", patch: body }));
|
|
1669
1794
|
}
|
|
1670
1795
|
/** Trigger an immediate tick outside the schedule. */
|
|
1671
1796
|
async runNow() {
|
|
1672
|
-
return
|
|
1797
|
+
return normalizeMonitorRecord(await this.ops.runNow());
|
|
1673
1798
|
}
|
|
1674
1799
|
/**
|
|
1675
1800
|
* Stream semantic monitor events. Monitors are resident, so the stream has
|
|
@@ -1690,7 +1815,7 @@ var MonitorHandle = class {
|
|
|
1690
1815
|
interactionHandle(interaction) {
|
|
1691
1816
|
return new InteractionHandle(
|
|
1692
1817
|
interaction,
|
|
1693
|
-
(action, body) => this.ops.postAction(action, body)
|
|
1818
|
+
(action, body, options) => this.ops.postAction(action, body, options)
|
|
1694
1819
|
);
|
|
1695
1820
|
}
|
|
1696
1821
|
/**
|
|
@@ -1795,17 +1920,15 @@ function createTrackNamespace(transport) {
|
|
|
1795
1920
|
update: async (input) => asRecord3((await api.updateMonitor({ token, monitorId, ...input })).data),
|
|
1796
1921
|
runNow: async () => asRecord3((await api.runNow({ token, monitorId })).data),
|
|
1797
1922
|
streamEvents: (opts) => api.events({ token, monitorId, lastEventId: opts.lastEventId, signal: opts.signal }),
|
|
1798
|
-
postAction: async (action, body) =>
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
{
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
);
|
|
1808
|
-
},
|
|
1923
|
+
postAction: async (action, body, options) => (await transport.webAgentJson(
|
|
1924
|
+
action.method || "POST",
|
|
1925
|
+
action.endpoint,
|
|
1926
|
+
token,
|
|
1927
|
+
{
|
|
1928
|
+
...body !== void 0 ? { body } : {},
|
|
1929
|
+
requiredScopes: options?.requiredScopes ?? interactionActionScopes(action, "webagent.track:manage")
|
|
1930
|
+
}
|
|
1931
|
+
)).data,
|
|
1809
1932
|
listRuns: async (opts) => asRecord3((await api.listRuns({ token, monitorId, ...opts })).data),
|
|
1810
1933
|
getRun: async (runId) => asRecord3((await api.getRun({ token, monitorId, runId })).data),
|
|
1811
1934
|
delete: async () => {
|
|
@@ -1820,10 +1943,12 @@ function createTrackNamespace(transport) {
|
|
|
1820
1943
|
if (!prompt || typeof prompt !== "string") {
|
|
1821
1944
|
throw new QoniValidationError("track.create requires a prompt string");
|
|
1822
1945
|
}
|
|
1946
|
+
const body = snakeifyKeys(rest);
|
|
1947
|
+
if (body.schedule !== void 0) body.schedule = normalizeSchedule(body.schedule);
|
|
1823
1948
|
const created = await api.createMonitor({
|
|
1824
1949
|
token,
|
|
1825
1950
|
intent: prompt,
|
|
1826
|
-
...
|
|
1951
|
+
...body
|
|
1827
1952
|
});
|
|
1828
1953
|
const monitorId = created.data.id || created.data.monitor_id;
|
|
1829
1954
|
if (!monitorId) {
|
|
@@ -1841,7 +1966,7 @@ function createTrackNamespace(transport) {
|
|
|
1841
1966
|
};
|
|
1842
1967
|
}
|
|
1843
1968
|
function normalizeMonitorRun(item) {
|
|
1844
|
-
const normalized =
|
|
1969
|
+
const normalized = normalizeMonitorRecord(item);
|
|
1845
1970
|
if (typeof normalized.id !== "string" || !normalized.id) {
|
|
1846
1971
|
const id = normalized.runId ?? normalized.snapshotId;
|
|
1847
1972
|
if (typeof id === "string" && id) normalized.id = id;
|
|
@@ -1851,11 +1976,44 @@ function normalizeMonitorRun(item) {
|
|
|
1851
1976
|
function normalizeTrackInterveneInput(input) {
|
|
1852
1977
|
return renameKeys4(input, { requestId: "request_id" });
|
|
1853
1978
|
}
|
|
1979
|
+
var USER_KEYED_FIELDS = /* @__PURE__ */ new Set(["extraction_schema", "baseline_extracted", "extracted"]);
|
|
1980
|
+
var USER_KEYED_DIFF_FIELDS = /* @__PURE__ */ new Set(["before", "after", "added", "removed", "sample_unchanged"]);
|
|
1981
|
+
function normalizeMonitorRecord(value) {
|
|
1982
|
+
const out = {};
|
|
1983
|
+
for (const [key, item] of Object.entries(value)) {
|
|
1984
|
+
if (USER_KEYED_FIELDS.has(key)) out[camelKey2(key)] = item;
|
|
1985
|
+
else if (key === "diffs" && Array.isArray(item)) out.diffs = item.map(normalizeDiff);
|
|
1986
|
+
else out[camelKey2(key)] = camelizeKeys(item);
|
|
1987
|
+
}
|
|
1988
|
+
return out;
|
|
1989
|
+
}
|
|
1990
|
+
function normalizeDiff(entry) {
|
|
1991
|
+
if (!isJsonObject(entry)) return camelizeKeys(entry);
|
|
1992
|
+
const out = {};
|
|
1993
|
+
for (const [key, item] of Object.entries(entry)) {
|
|
1994
|
+
out[camelKey2(key)] = USER_KEYED_DIFF_FIELDS.has(key) ? item : camelizeKeys(item);
|
|
1995
|
+
}
|
|
1996
|
+
return out;
|
|
1997
|
+
}
|
|
1998
|
+
function camelKey2(key) {
|
|
1999
|
+
return key.replace(/_([a-z0-9])/g, (_, ch) => ch.toUpperCase());
|
|
2000
|
+
}
|
|
1854
2001
|
function normalizeSchedule(schedule) {
|
|
2002
|
+
if (!isJsonObject(schedule)) return schedule;
|
|
1855
2003
|
if (schedule.kind === "interval") {
|
|
1856
|
-
|
|
2004
|
+
const { intervalSeconds, interval_seconds, ...rest } = schedule;
|
|
2005
|
+
const seconds = intervalSeconds ?? interval_seconds;
|
|
2006
|
+
if (typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) {
|
|
2007
|
+
throw new QoniValidationError(
|
|
2008
|
+
'track interval schedule needs a positive number: { kind: "interval", intervalSeconds }'
|
|
2009
|
+
);
|
|
2010
|
+
}
|
|
2011
|
+
return { ...rest, interval_seconds: seconds };
|
|
2012
|
+
}
|
|
2013
|
+
if (schedule.kind === "cron" && (typeof schedule.expr !== "string" || !schedule.expr.trim())) {
|
|
2014
|
+
throw new QoniValidationError('track cron schedule needs an expression: { kind: "cron", expr }');
|
|
1857
2015
|
}
|
|
1858
|
-
return
|
|
2016
|
+
return schedule;
|
|
1859
2017
|
}
|
|
1860
2018
|
function snakeifyKeys(value) {
|
|
1861
2019
|
const out = {};
|