@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.cjs
CHANGED
|
@@ -278,6 +278,8 @@ var QoniEventTypes = {
|
|
|
278
278
|
Interaction: "interaction",
|
|
279
279
|
/** A live screenshot frame (opt in with `capture: { screenshots: true }`); image on `event.image`. */
|
|
280
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",
|
|
281
283
|
/** Terminal — the run finished; outcome + output in `event.data` (cost is on the `RunResult`). */
|
|
282
284
|
Done: "done",
|
|
283
285
|
// --- Web Search ---
|
|
@@ -408,7 +410,6 @@ var WIRE_TO_SEMANTIC = {
|
|
|
408
410
|
"run.recording_started": "progress",
|
|
409
411
|
"run.recording_paused": "progress",
|
|
410
412
|
"run.recording_finalized": "progress",
|
|
411
|
-
"run.browser_live_url_changed": "progress",
|
|
412
413
|
// side-channel
|
|
413
414
|
"run.feedback_submitted": "progress"
|
|
414
415
|
};
|
|
@@ -470,6 +471,18 @@ function normalizeRunEvent(wire, options = {}) {
|
|
|
470
471
|
return { ...base, type: "screenshot", data: sData, image, isTerminal: false };
|
|
471
472
|
}
|
|
472
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
|
+
}
|
|
473
486
|
if (wireType === INTERACTION) {
|
|
474
487
|
const interaction = normalizeInteraction(payload);
|
|
475
488
|
if (interaction) {
|
|
@@ -525,6 +538,87 @@ var ActionKinds = {
|
|
|
525
538
|
};
|
|
526
539
|
var ACTION_KIND_VALUES = Object.values(ActionKinds);
|
|
527
540
|
|
|
541
|
+
// src/scopes.ts
|
|
542
|
+
var QoniScopes = {
|
|
543
|
+
GUMEM_SESSION_CREATE: "gumem.session:create",
|
|
544
|
+
GUMEM_MESSAGE_WRITE: "gumem.message:write",
|
|
545
|
+
GUMEM_MEMORY_READ: "gumem.memory:read",
|
|
546
|
+
GUMEM_MEMORY_WRITE: "gumem.memory:write",
|
|
547
|
+
GUMEM_MEMORY_DELETE: "gumem.memory:delete",
|
|
548
|
+
GUMEM_SEARCH_RUN: "gumem.search:run",
|
|
549
|
+
GUMEM_ADMIN_MANAGE: "gumem.admin:manage",
|
|
550
|
+
GUMEM_RESOURCE_WRITE: "gumem.resource:write",
|
|
551
|
+
GUMEM_ACTION_WRITE: "gumem.action:write",
|
|
552
|
+
GUMEM_ACTION_READ: "gumem.action:read",
|
|
553
|
+
GUMEM_PROFILE_READ: "gumem.profile:read",
|
|
554
|
+
// WebAgent products: exactly two verbs per product — `read` (observe) and
|
|
555
|
+
// `manage` (anything that changes execution state). See
|
|
556
|
+
// wa/docs/qoni-sdk-public-surface.md §3.
|
|
557
|
+
DO_ANYTHING_READ: "webagent.do_anything:read",
|
|
558
|
+
DO_ANYTHING_MANAGE: "webagent.do_anything:manage",
|
|
559
|
+
WEB_SEARCH_READ: "webagent.web_search:read",
|
|
560
|
+
WEB_SEARCH_MANAGE: "webagent.web_search:manage",
|
|
561
|
+
DEEP_RESEARCH_READ: "webagent.deep_research:read",
|
|
562
|
+
DEEP_RESEARCH_MANAGE: "webagent.deep_research:manage",
|
|
563
|
+
TRACK_READ: "webagent.track:read",
|
|
564
|
+
TRACK_MANAGE: "webagent.track:manage",
|
|
565
|
+
// Interactive site login (`InteractionHandle.openLogin()` on a `site_login`
|
|
566
|
+
// interaction). Not part of any product bundle: the AccessKey policy must
|
|
567
|
+
// allow it, and the delegation must request it explicitly.
|
|
568
|
+
SITE_LOGIN_REQUEST: "webagent.site_login:request",
|
|
569
|
+
// Persist that login into the browser profile (`OpenLoginResult.confirm()`).
|
|
570
|
+
SITE_LOGIN_CONFIRM: "webagent.site_login:confirm"
|
|
571
|
+
};
|
|
572
|
+
var QoniProductScopes = {
|
|
573
|
+
doAnything: [
|
|
574
|
+
QoniScopes.DO_ANYTHING_READ,
|
|
575
|
+
QoniScopes.DO_ANYTHING_MANAGE
|
|
576
|
+
],
|
|
577
|
+
webSearch: [
|
|
578
|
+
QoniScopes.WEB_SEARCH_READ,
|
|
579
|
+
QoniScopes.WEB_SEARCH_MANAGE
|
|
580
|
+
],
|
|
581
|
+
deepResearch: [
|
|
582
|
+
QoniScopes.DEEP_RESEARCH_READ,
|
|
583
|
+
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
584
|
+
],
|
|
585
|
+
track: [
|
|
586
|
+
QoniScopes.TRACK_READ,
|
|
587
|
+
QoniScopes.TRACK_MANAGE
|
|
588
|
+
]
|
|
589
|
+
};
|
|
590
|
+
var KNOWN_SCOPES = Object.values(QoniScopes);
|
|
591
|
+
var QoniScopeBundles = {
|
|
592
|
+
GUMEM_READONLY: [QoniScopes.GUMEM_MEMORY_READ, QoniScopes.GUMEM_PROFILE_READ],
|
|
593
|
+
GUMEM_SESSION_RECALL: [
|
|
594
|
+
QoniScopes.GUMEM_MEMORY_READ,
|
|
595
|
+
QoniScopes.GUMEM_MEMORY_WRITE,
|
|
596
|
+
QoniScopes.GUMEM_MESSAGE_WRITE
|
|
597
|
+
],
|
|
598
|
+
GUMEM_WRITE: [
|
|
599
|
+
QoniScopes.GUMEM_SESSION_CREATE,
|
|
600
|
+
QoniScopes.GUMEM_MESSAGE_WRITE,
|
|
601
|
+
QoniScopes.GUMEM_ACTION_WRITE
|
|
602
|
+
],
|
|
603
|
+
// One bundle per WebAgent product = [read, manage].
|
|
604
|
+
DO_ANYTHING: [
|
|
605
|
+
QoniScopes.DO_ANYTHING_READ,
|
|
606
|
+
QoniScopes.DO_ANYTHING_MANAGE
|
|
607
|
+
],
|
|
608
|
+
WEB_SEARCH: [
|
|
609
|
+
QoniScopes.WEB_SEARCH_READ,
|
|
610
|
+
QoniScopes.WEB_SEARCH_MANAGE
|
|
611
|
+
],
|
|
612
|
+
DEEP_RESEARCH: [
|
|
613
|
+
QoniScopes.DEEP_RESEARCH_READ,
|
|
614
|
+
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
615
|
+
],
|
|
616
|
+
TRACK: [
|
|
617
|
+
QoniScopes.TRACK_READ,
|
|
618
|
+
QoniScopes.TRACK_MANAGE
|
|
619
|
+
]
|
|
620
|
+
};
|
|
621
|
+
|
|
528
622
|
// src/interactions.ts
|
|
529
623
|
function asString2(value) {
|
|
530
624
|
return typeof value === "string" && value ? value : void 0;
|
|
@@ -623,6 +717,9 @@ function normalizeInteraction(payload) {
|
|
|
623
717
|
if (evidence) interaction.evidence = evidence;
|
|
624
718
|
return interaction;
|
|
625
719
|
}
|
|
720
|
+
function interactionActionScopes(action, productManageScope) {
|
|
721
|
+
return action.kind === ActionKinds.openLogin ? [QoniScopes.SITE_LOGIN_REQUEST] : [productManageScope];
|
|
722
|
+
}
|
|
626
723
|
var InteractionHandle = class {
|
|
627
724
|
constructor(interaction, post) {
|
|
628
725
|
this.post = post;
|
|
@@ -647,48 +744,122 @@ var InteractionHandle = class {
|
|
|
647
744
|
return this.interaction.actions.some((a) => a.kind === kind);
|
|
648
745
|
}
|
|
649
746
|
/** Answer a clarification ask. */
|
|
650
|
-
answer(text) {
|
|
651
|
-
|
|
747
|
+
async answer(text) {
|
|
748
|
+
await this.invoke(ActionKinds.answer, { response: text });
|
|
652
749
|
}
|
|
653
750
|
/** Skip / dismiss the interaction (proceed without acting on it). */
|
|
654
|
-
skip() {
|
|
655
|
-
|
|
751
|
+
async skip() {
|
|
752
|
+
await this.invoke(ActionKinds.skip);
|
|
656
753
|
}
|
|
657
754
|
/** Confirm a pending confirmation (approve the plan / destructive op). */
|
|
658
|
-
confirm() {
|
|
659
|
-
|
|
755
|
+
async confirm() {
|
|
756
|
+
await this.invoke(ActionKinds.confirm);
|
|
660
757
|
}
|
|
661
758
|
/** Reject a pending confirmation. */
|
|
662
|
-
reject() {
|
|
663
|
-
|
|
759
|
+
async reject() {
|
|
760
|
+
await this.invoke(ActionKinds.reject);
|
|
664
761
|
}
|
|
665
|
-
/**
|
|
666
|
-
|
|
667
|
-
|
|
762
|
+
/**
|
|
763
|
+
* Start an interactive login session for a `site_login` interaction. The
|
|
764
|
+
* backend needs the site to sign in to: pass `siteId`, or omit it when the
|
|
765
|
+
* interaction lists exactly one site. Show `liveUrl` to the user; when they
|
|
766
|
+
* finish, call `confirm()` on the result to save the login, and act on its
|
|
767
|
+
* outcome (see {@link OpenLoginResult.confirm}) before {@link confirmSignedIn}.
|
|
768
|
+
* The delegation must include `QoniScopes.SITE_LOGIN_REQUEST` and
|
|
769
|
+
* `QoniScopes.SITE_LOGIN_CONFIRM`; the AccessKey policy must allow both.
|
|
770
|
+
*/
|
|
771
|
+
async openLogin(options = {}) {
|
|
772
|
+
this.#declaredAction(ActionKinds.openLogin);
|
|
773
|
+
const body = { site_id: this.#loginSiteId(options.siteId) };
|
|
774
|
+
if (options.profileId !== void 0) {
|
|
775
|
+
if (typeof options.profileId !== "string" || !options.profileId) {
|
|
776
|
+
throw new QoniValidationError("openLogin profileId must be a non-empty string");
|
|
777
|
+
}
|
|
778
|
+
body.profile_id = options.profileId;
|
|
779
|
+
}
|
|
780
|
+
const response = await this.invoke(ActionKinds.openLogin, body);
|
|
781
|
+
const raw = isJsonObject(response) ? response : {};
|
|
782
|
+
const fields = camelizeRecord(raw);
|
|
783
|
+
const profileId = asString2(fields.profileId) ?? "";
|
|
784
|
+
const siteId = asString2(fields.siteId) ?? String(body.site_id);
|
|
785
|
+
return {
|
|
786
|
+
profileId,
|
|
787
|
+
siteId,
|
|
788
|
+
sessionId: asString2(fields.sessionId) ?? "",
|
|
789
|
+
liveUrl: asString2(fields.liveUrl) ?? "",
|
|
790
|
+
raw,
|
|
791
|
+
confirm: async (confirmOptions = {}) => {
|
|
792
|
+
if (!profileId) {
|
|
793
|
+
throw new QoniValidationError("openLogin response did not include a profileId to confirm");
|
|
794
|
+
}
|
|
795
|
+
const confirmBody = { site_id: siteId };
|
|
796
|
+
if (confirmOptions.displayLabel !== void 0) confirmBody.display_label = confirmOptions.displayLabel;
|
|
797
|
+
const response2 = await this.post(
|
|
798
|
+
{
|
|
799
|
+
kind: ActionKinds.openLogin,
|
|
800
|
+
label: "Confirm login",
|
|
801
|
+
method: "POST",
|
|
802
|
+
endpoint: `/profiles/${encodeURIComponent(profileId)}/confirm`
|
|
803
|
+
},
|
|
804
|
+
confirmBody,
|
|
805
|
+
{ requiredScopes: [QoniScopes.SITE_LOGIN_CONFIRM] }
|
|
806
|
+
);
|
|
807
|
+
const raw2 = isJsonObject(response2) ? response2 : {};
|
|
808
|
+
const fields2 = camelizeRecord(raw2);
|
|
809
|
+
const state = asString2(fields2.state) ?? "";
|
|
810
|
+
return {
|
|
811
|
+
profileId: asString2(fields2.profileId) ?? profileId,
|
|
812
|
+
active: state === "active",
|
|
813
|
+
state,
|
|
814
|
+
probeResult: asString2(fields2.probeResult) ?? "",
|
|
815
|
+
raw: raw2
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
};
|
|
668
819
|
}
|
|
669
820
|
/** Tell the agent the user has finished signing in. */
|
|
670
|
-
confirmSignedIn() {
|
|
671
|
-
|
|
821
|
+
async confirmSignedIn() {
|
|
822
|
+
await this.invoke(ActionKinds.confirmSignedIn);
|
|
672
823
|
}
|
|
673
824
|
/** Connect to the live browser for a take_control interaction. */
|
|
674
|
-
connectControl() {
|
|
675
|
-
|
|
825
|
+
async connectControl() {
|
|
826
|
+
await this.invoke(ActionKinds.connectControl);
|
|
676
827
|
}
|
|
677
828
|
/** Refresh the live control surface / live url. */
|
|
678
|
-
refreshControl() {
|
|
679
|
-
|
|
829
|
+
async refreshControl() {
|
|
830
|
+
await this.invoke(ActionKinds.refreshControl);
|
|
680
831
|
}
|
|
681
832
|
/** Hand control back to the agent. */
|
|
682
|
-
releaseControl() {
|
|
683
|
-
|
|
833
|
+
async releaseControl() {
|
|
834
|
+
await this.invoke(ActionKinds.releaseControl);
|
|
684
835
|
}
|
|
685
836
|
/** Retry a wait interaction now. */
|
|
686
|
-
retry() {
|
|
687
|
-
|
|
837
|
+
async retry() {
|
|
838
|
+
await this.invoke(ActionKinds.retry);
|
|
688
839
|
}
|
|
689
840
|
/** Switch the profile a wait/login interaction is bound to. */
|
|
690
|
-
switchProfile() {
|
|
691
|
-
|
|
841
|
+
async switchProfile() {
|
|
842
|
+
await this.invoke(ActionKinds.switchProfile);
|
|
843
|
+
}
|
|
844
|
+
/** Resolve the site for `openLogin`: explicit, or the only listed site. */
|
|
845
|
+
#loginSiteId(requested) {
|
|
846
|
+
const payload = this.interaction.payload;
|
|
847
|
+
const listed = Array.isArray(payload?.sites) ? payload.sites.map((site) => site.siteId).filter((id) => typeof id === "string" && id.length > 0) : [];
|
|
848
|
+
if (requested !== void 0) {
|
|
849
|
+
if (typeof requested !== "string" || !requested) {
|
|
850
|
+
throw new QoniValidationError("openLogin siteId must be a non-empty string");
|
|
851
|
+
}
|
|
852
|
+
if (listed.length > 0 && !listed.includes(requested)) {
|
|
853
|
+
throw new QoniValidationError(
|
|
854
|
+
`openLogin siteId "${requested}" is not one of the sites this interaction asks for: ${listed.join(", ")}`
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
return requested;
|
|
858
|
+
}
|
|
859
|
+
if (listed.length === 1) return listed[0];
|
|
860
|
+
throw new QoniValidationError(
|
|
861
|
+
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(", ")}`
|
|
862
|
+
);
|
|
692
863
|
}
|
|
693
864
|
/**
|
|
694
865
|
* Invoke an action by kind with an optional body. `async` so a missing-action
|
|
@@ -696,6 +867,10 @@ var InteractionHandle = class {
|
|
|
696
867
|
* method contract uniformly promise-based.
|
|
697
868
|
*/
|
|
698
869
|
async invoke(kind, body) {
|
|
870
|
+
return this.post(this.#declaredAction(kind), body);
|
|
871
|
+
}
|
|
872
|
+
/** The declared action of `kind`; throws when the backend did not offer it. */
|
|
873
|
+
#declaredAction(kind) {
|
|
699
874
|
const action = this.interaction.actions.find((a) => a.kind === kind);
|
|
700
875
|
if (!action) {
|
|
701
876
|
const offered = this.interaction.actions.map((a) => a.kind).join(", ") || "none";
|
|
@@ -703,7 +878,7 @@ var InteractionHandle = class {
|
|
|
703
878
|
`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.`
|
|
704
879
|
);
|
|
705
880
|
}
|
|
706
|
-
|
|
881
|
+
return action;
|
|
707
882
|
}
|
|
708
883
|
};
|
|
709
884
|
|
|
@@ -773,7 +948,7 @@ var RunHandle = class {
|
|
|
773
948
|
break settled;
|
|
774
949
|
}
|
|
775
950
|
}
|
|
776
|
-
const detail2 = await
|
|
951
|
+
const detail2 = await settleRead(() => this.ops.getDetail(signal), signal);
|
|
777
952
|
if (isTerminalRunStatus(normalizeRunStatus(this.id, detail2).status)) {
|
|
778
953
|
terminalDetail = detail2;
|
|
779
954
|
break;
|
|
@@ -787,10 +962,10 @@ var RunHandle = class {
|
|
|
787
962
|
sawCleanClose = true;
|
|
788
963
|
eventIdAtLastClose = lastEventId;
|
|
789
964
|
}
|
|
790
|
-
const detail = terminalDetail ?? await
|
|
965
|
+
const detail = terminalDetail ?? await settleRead(() => this.ops.getDetail(signal), signal);
|
|
791
966
|
this.#adoptSessionRef(detail);
|
|
792
967
|
const result = settleRunResult(this.id, detail, [], terminalDoneReason);
|
|
793
|
-
result.artifacts = this.ops.listArtifacts ? await
|
|
968
|
+
result.artifacts = this.ops.listArtifacts ? await settleRead(() => this.ops.listArtifacts(signal), signal) : [];
|
|
794
969
|
return result;
|
|
795
970
|
} catch (err) {
|
|
796
971
|
if (timedOut() && !opts.signal?.aborted) {
|
|
@@ -813,7 +988,7 @@ var RunHandle = class {
|
|
|
813
988
|
interactionHandle(interaction) {
|
|
814
989
|
return new InteractionHandle(
|
|
815
990
|
interaction,
|
|
816
|
-
(action, body) => this.ops.postAction(action, body)
|
|
991
|
+
(action, body, options) => this.ops.postAction(action, body, options)
|
|
817
992
|
);
|
|
818
993
|
}
|
|
819
994
|
/**
|
|
@@ -907,6 +1082,35 @@ function withTimeout(signal, timeoutMs) {
|
|
|
907
1082
|
}
|
|
908
1083
|
return { signal: controller.signal, timedOut: () => fired, dispose };
|
|
909
1084
|
}
|
|
1085
|
+
var SETTLE_READ_BACKOFF_MS = [250, 1e3];
|
|
1086
|
+
async function settleRead(read, signal) {
|
|
1087
|
+
for (let attempt = 0; ; attempt++) {
|
|
1088
|
+
try {
|
|
1089
|
+
return await waitWithSignal(read, signal);
|
|
1090
|
+
} catch (err) {
|
|
1091
|
+
const delay = SETTLE_READ_BACKOFF_MS[attempt];
|
|
1092
|
+
if (delay === void 0 || signal?.aborted || !(err instanceof QoniError) || !err.retryable) throw err;
|
|
1093
|
+
await sleepWithSignal(delay, signal);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
function sleepWithSignal(ms, signal) {
|
|
1098
|
+
return new Promise((resolve, reject) => {
|
|
1099
|
+
if (signal?.aborted) {
|
|
1100
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
const onAbort = () => {
|
|
1104
|
+
clearTimeout(timer);
|
|
1105
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
1106
|
+
};
|
|
1107
|
+
const timer = setTimeout(() => {
|
|
1108
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1109
|
+
resolve();
|
|
1110
|
+
}, ms);
|
|
1111
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
910
1114
|
function waitWithSignal(operation, signal) {
|
|
911
1115
|
if (!signal) return Promise.resolve().then(operation);
|
|
912
1116
|
return new Promise((resolve, reject) => {
|
|
@@ -1004,17 +1208,15 @@ function createDeepResearchNamespace(transport) {
|
|
|
1004
1208
|
signal: opts.signal,
|
|
1005
1209
|
onReconnect: opts.onReconnect
|
|
1006
1210
|
}),
|
|
1007
|
-
postAction: async (action, body) =>
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
{
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
);
|
|
1017
|
-
},
|
|
1211
|
+
postAction: async (action, body, options) => (await transport.webAgentJson(
|
|
1212
|
+
action.method || "POST",
|
|
1213
|
+
action.endpoint,
|
|
1214
|
+
token,
|
|
1215
|
+
{
|
|
1216
|
+
...body !== void 0 ? { body } : {},
|
|
1217
|
+
requiredScopes: options?.requiredScopes ?? interactionActionScopes(action, "webagent.deep_research:manage")
|
|
1218
|
+
}
|
|
1219
|
+
)).data,
|
|
1018
1220
|
cancel: async () => asRecord((await api.cancel({ token, runId })).data),
|
|
1019
1221
|
listArtifacts: async (signal) => {
|
|
1020
1222
|
const res = await api.listArtifacts({ token, runId, signal });
|
|
@@ -1279,17 +1481,15 @@ function createDoAnythingNamespace(transport) {
|
|
|
1279
1481
|
// Skip screenshot rows in backfill when the caller did not opt in.
|
|
1280
1482
|
includeScreenshots: capture?.screenshots ? void 0 : false
|
|
1281
1483
|
}),
|
|
1282
|
-
postAction: async (action, body) =>
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
{
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
);
|
|
1292
|
-
},
|
|
1484
|
+
postAction: async (action, body, options) => (await transport.webAgentJson(
|
|
1485
|
+
action.method || "POST",
|
|
1486
|
+
action.endpoint,
|
|
1487
|
+
token,
|
|
1488
|
+
{
|
|
1489
|
+
...body !== void 0 ? { body } : {},
|
|
1490
|
+
requiredScopes: options?.requiredScopes ?? interactionActionScopes(action, "webagent.do_anything:manage")
|
|
1491
|
+
}
|
|
1492
|
+
)).data,
|
|
1293
1493
|
cancel: async (reason) => asRecord2(
|
|
1294
1494
|
(await api.cancel({
|
|
1295
1495
|
token,
|
|
@@ -1410,81 +1610,6 @@ function createQoniNamespace(_transport, delegateToken, completeDelegateToken) {
|
|
|
1410
1610
|
};
|
|
1411
1611
|
}
|
|
1412
1612
|
|
|
1413
|
-
// src/scopes.ts
|
|
1414
|
-
var QoniScopes = {
|
|
1415
|
-
GUMEM_SESSION_CREATE: "gumem.session:create",
|
|
1416
|
-
GUMEM_MESSAGE_WRITE: "gumem.message:write",
|
|
1417
|
-
GUMEM_MEMORY_READ: "gumem.memory:read",
|
|
1418
|
-
GUMEM_MEMORY_WRITE: "gumem.memory:write",
|
|
1419
|
-
GUMEM_MEMORY_DELETE: "gumem.memory:delete",
|
|
1420
|
-
GUMEM_SEARCH_RUN: "gumem.search:run",
|
|
1421
|
-
GUMEM_ADMIN_MANAGE: "gumem.admin:manage",
|
|
1422
|
-
GUMEM_RESOURCE_WRITE: "gumem.resource:write",
|
|
1423
|
-
GUMEM_ACTION_WRITE: "gumem.action:write",
|
|
1424
|
-
GUMEM_ACTION_READ: "gumem.action:read",
|
|
1425
|
-
GUMEM_PROFILE_READ: "gumem.profile:read",
|
|
1426
|
-
// WebAgent products: exactly two verbs per product — `read` (observe) and
|
|
1427
|
-
// `manage` (anything that changes execution state). See
|
|
1428
|
-
// wa/docs/qoni-sdk-public-surface.md §3.
|
|
1429
|
-
DO_ANYTHING_READ: "webagent.do_anything:read",
|
|
1430
|
-
DO_ANYTHING_MANAGE: "webagent.do_anything:manage",
|
|
1431
|
-
WEB_SEARCH_READ: "webagent.web_search:read",
|
|
1432
|
-
WEB_SEARCH_MANAGE: "webagent.web_search:manage",
|
|
1433
|
-
DEEP_RESEARCH_READ: "webagent.deep_research:read",
|
|
1434
|
-
DEEP_RESEARCH_MANAGE: "webagent.deep_research:manage",
|
|
1435
|
-
TRACK_READ: "webagent.track:read",
|
|
1436
|
-
TRACK_MANAGE: "webagent.track:manage"
|
|
1437
|
-
};
|
|
1438
|
-
var QoniProductScopes = {
|
|
1439
|
-
doAnything: [
|
|
1440
|
-
QoniScopes.DO_ANYTHING_READ,
|
|
1441
|
-
QoniScopes.DO_ANYTHING_MANAGE
|
|
1442
|
-
],
|
|
1443
|
-
webSearch: [
|
|
1444
|
-
QoniScopes.WEB_SEARCH_READ,
|
|
1445
|
-
QoniScopes.WEB_SEARCH_MANAGE
|
|
1446
|
-
],
|
|
1447
|
-
deepResearch: [
|
|
1448
|
-
QoniScopes.DEEP_RESEARCH_READ,
|
|
1449
|
-
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
1450
|
-
],
|
|
1451
|
-
track: [
|
|
1452
|
-
QoniScopes.TRACK_READ,
|
|
1453
|
-
QoniScopes.TRACK_MANAGE
|
|
1454
|
-
]
|
|
1455
|
-
};
|
|
1456
|
-
var KNOWN_SCOPES = Object.values(QoniScopes);
|
|
1457
|
-
var QoniScopeBundles = {
|
|
1458
|
-
GUMEM_READONLY: [QoniScopes.GUMEM_MEMORY_READ, QoniScopes.GUMEM_PROFILE_READ],
|
|
1459
|
-
GUMEM_SESSION_RECALL: [
|
|
1460
|
-
QoniScopes.GUMEM_MEMORY_READ,
|
|
1461
|
-
QoniScopes.GUMEM_MEMORY_WRITE,
|
|
1462
|
-
QoniScopes.GUMEM_MESSAGE_WRITE
|
|
1463
|
-
],
|
|
1464
|
-
GUMEM_WRITE: [
|
|
1465
|
-
QoniScopes.GUMEM_SESSION_CREATE,
|
|
1466
|
-
QoniScopes.GUMEM_MESSAGE_WRITE,
|
|
1467
|
-
QoniScopes.GUMEM_ACTION_WRITE
|
|
1468
|
-
],
|
|
1469
|
-
// One bundle per WebAgent product = [read, manage].
|
|
1470
|
-
DO_ANYTHING: [
|
|
1471
|
-
QoniScopes.DO_ANYTHING_READ,
|
|
1472
|
-
QoniScopes.DO_ANYTHING_MANAGE
|
|
1473
|
-
],
|
|
1474
|
-
WEB_SEARCH: [
|
|
1475
|
-
QoniScopes.WEB_SEARCH_READ,
|
|
1476
|
-
QoniScopes.WEB_SEARCH_MANAGE
|
|
1477
|
-
],
|
|
1478
|
-
DEEP_RESEARCH: [
|
|
1479
|
-
QoniScopes.DEEP_RESEARCH_READ,
|
|
1480
|
-
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
1481
|
-
],
|
|
1482
|
-
TRACK: [
|
|
1483
|
-
QoniScopes.TRACK_READ,
|
|
1484
|
-
QoniScopes.TRACK_MANAGE
|
|
1485
|
-
]
|
|
1486
|
-
};
|
|
1487
|
-
|
|
1488
1613
|
// src/genauth.ts
|
|
1489
1614
|
function createGenAuthNamespace(transport, options = {}) {
|
|
1490
1615
|
return {
|
|
@@ -1683,7 +1808,7 @@ var MonitorHandle = class {
|
|
|
1683
1808
|
id;
|
|
1684
1809
|
/** Refresh and return the monitor (normalized camelCase fields). */
|
|
1685
1810
|
async get() {
|
|
1686
|
-
return
|
|
1811
|
+
return normalizeMonitorRecord(await this.ops.get());
|
|
1687
1812
|
}
|
|
1688
1813
|
/**
|
|
1689
1814
|
* Pause the monitor — stops scheduled ticks until {@link resume}. The
|
|
@@ -1691,11 +1816,11 @@ var MonitorHandle = class {
|
|
|
1691
1816
|
* deleting.
|
|
1692
1817
|
*/
|
|
1693
1818
|
async pause() {
|
|
1694
|
-
return
|
|
1819
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "pause" }));
|
|
1695
1820
|
}
|
|
1696
1821
|
/** Resume a paused monitor. */
|
|
1697
1822
|
async resume() {
|
|
1698
|
-
return
|
|
1823
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "resume" }));
|
|
1699
1824
|
}
|
|
1700
1825
|
/**
|
|
1701
1826
|
* Change the monitor definition. Pass any subset of fields (at least one);
|
|
@@ -1713,11 +1838,11 @@ var MonitorHandle = class {
|
|
|
1713
1838
|
"track refine requires at least one field to change (schedule / targetUrls / extractionSchema / triggerDsl / stopConditionDsl / notifyChannel)"
|
|
1714
1839
|
);
|
|
1715
1840
|
}
|
|
1716
|
-
return
|
|
1841
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "refine", patch: body }));
|
|
1717
1842
|
}
|
|
1718
1843
|
/** Trigger an immediate tick outside the schedule. */
|
|
1719
1844
|
async runNow() {
|
|
1720
|
-
return
|
|
1845
|
+
return normalizeMonitorRecord(await this.ops.runNow());
|
|
1721
1846
|
}
|
|
1722
1847
|
/**
|
|
1723
1848
|
* Stream semantic monitor events. Monitors are resident, so the stream has
|
|
@@ -1738,7 +1863,7 @@ var MonitorHandle = class {
|
|
|
1738
1863
|
interactionHandle(interaction) {
|
|
1739
1864
|
return new InteractionHandle(
|
|
1740
1865
|
interaction,
|
|
1741
|
-
(action, body) => this.ops.postAction(action, body)
|
|
1866
|
+
(action, body, options) => this.ops.postAction(action, body, options)
|
|
1742
1867
|
);
|
|
1743
1868
|
}
|
|
1744
1869
|
/**
|
|
@@ -1843,17 +1968,15 @@ function createTrackNamespace(transport) {
|
|
|
1843
1968
|
update: async (input) => asRecord3((await api.updateMonitor({ token, monitorId, ...input })).data),
|
|
1844
1969
|
runNow: async () => asRecord3((await api.runNow({ token, monitorId })).data),
|
|
1845
1970
|
streamEvents: (opts) => api.events({ token, monitorId, lastEventId: opts.lastEventId, signal: opts.signal }),
|
|
1846
|
-
postAction: async (action, body) =>
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
{
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
);
|
|
1856
|
-
},
|
|
1971
|
+
postAction: async (action, body, options) => (await transport.webAgentJson(
|
|
1972
|
+
action.method || "POST",
|
|
1973
|
+
action.endpoint,
|
|
1974
|
+
token,
|
|
1975
|
+
{
|
|
1976
|
+
...body !== void 0 ? { body } : {},
|
|
1977
|
+
requiredScopes: options?.requiredScopes ?? interactionActionScopes(action, "webagent.track:manage")
|
|
1978
|
+
}
|
|
1979
|
+
)).data,
|
|
1857
1980
|
listRuns: async (opts) => asRecord3((await api.listRuns({ token, monitorId, ...opts })).data),
|
|
1858
1981
|
getRun: async (runId) => asRecord3((await api.getRun({ token, monitorId, runId })).data),
|
|
1859
1982
|
delete: async () => {
|
|
@@ -1868,10 +1991,12 @@ function createTrackNamespace(transport) {
|
|
|
1868
1991
|
if (!prompt || typeof prompt !== "string") {
|
|
1869
1992
|
throw new QoniValidationError("track.create requires a prompt string");
|
|
1870
1993
|
}
|
|
1994
|
+
const body = snakeifyKeys(rest);
|
|
1995
|
+
if (body.schedule !== void 0) body.schedule = normalizeSchedule(body.schedule);
|
|
1871
1996
|
const created = await api.createMonitor({
|
|
1872
1997
|
token,
|
|
1873
1998
|
intent: prompt,
|
|
1874
|
-
...
|
|
1999
|
+
...body
|
|
1875
2000
|
});
|
|
1876
2001
|
const monitorId = created.data.id || created.data.monitor_id;
|
|
1877
2002
|
if (!monitorId) {
|
|
@@ -1889,7 +2014,7 @@ function createTrackNamespace(transport) {
|
|
|
1889
2014
|
};
|
|
1890
2015
|
}
|
|
1891
2016
|
function normalizeMonitorRun(item) {
|
|
1892
|
-
const normalized =
|
|
2017
|
+
const normalized = normalizeMonitorRecord(item);
|
|
1893
2018
|
if (typeof normalized.id !== "string" || !normalized.id) {
|
|
1894
2019
|
const id = normalized.runId ?? normalized.snapshotId;
|
|
1895
2020
|
if (typeof id === "string" && id) normalized.id = id;
|
|
@@ -1899,11 +2024,44 @@ function normalizeMonitorRun(item) {
|
|
|
1899
2024
|
function normalizeTrackInterveneInput(input) {
|
|
1900
2025
|
return renameKeys4(input, { requestId: "request_id" });
|
|
1901
2026
|
}
|
|
2027
|
+
var USER_KEYED_FIELDS = /* @__PURE__ */ new Set(["extraction_schema", "baseline_extracted", "extracted"]);
|
|
2028
|
+
var USER_KEYED_DIFF_FIELDS = /* @__PURE__ */ new Set(["before", "after", "added", "removed", "sample_unchanged"]);
|
|
2029
|
+
function normalizeMonitorRecord(value) {
|
|
2030
|
+
const out = {};
|
|
2031
|
+
for (const [key, item] of Object.entries(value)) {
|
|
2032
|
+
if (USER_KEYED_FIELDS.has(key)) out[camelKey2(key)] = item;
|
|
2033
|
+
else if (key === "diffs" && Array.isArray(item)) out.diffs = item.map(normalizeDiff);
|
|
2034
|
+
else out[camelKey2(key)] = camelizeKeys(item);
|
|
2035
|
+
}
|
|
2036
|
+
return out;
|
|
2037
|
+
}
|
|
2038
|
+
function normalizeDiff(entry) {
|
|
2039
|
+
if (!isJsonObject(entry)) return camelizeKeys(entry);
|
|
2040
|
+
const out = {};
|
|
2041
|
+
for (const [key, item] of Object.entries(entry)) {
|
|
2042
|
+
out[camelKey2(key)] = USER_KEYED_DIFF_FIELDS.has(key) ? item : camelizeKeys(item);
|
|
2043
|
+
}
|
|
2044
|
+
return out;
|
|
2045
|
+
}
|
|
2046
|
+
function camelKey2(key) {
|
|
2047
|
+
return key.replace(/_([a-z0-9])/g, (_, ch) => ch.toUpperCase());
|
|
2048
|
+
}
|
|
1902
2049
|
function normalizeSchedule(schedule) {
|
|
2050
|
+
if (!isJsonObject(schedule)) return schedule;
|
|
1903
2051
|
if (schedule.kind === "interval") {
|
|
1904
|
-
|
|
2052
|
+
const { intervalSeconds, interval_seconds, ...rest } = schedule;
|
|
2053
|
+
const seconds = intervalSeconds ?? interval_seconds;
|
|
2054
|
+
if (typeof seconds !== "number" || !Number.isFinite(seconds) || seconds <= 0) {
|
|
2055
|
+
throw new QoniValidationError(
|
|
2056
|
+
'track interval schedule needs a positive number: { kind: "interval", intervalSeconds }'
|
|
2057
|
+
);
|
|
2058
|
+
}
|
|
2059
|
+
return { ...rest, interval_seconds: seconds };
|
|
2060
|
+
}
|
|
2061
|
+
if (schedule.kind === "cron" && (typeof schedule.expr !== "string" || !schedule.expr.trim())) {
|
|
2062
|
+
throw new QoniValidationError('track cron schedule needs an expression: { kind: "cron", expr }');
|
|
1905
2063
|
}
|
|
1906
|
-
return
|
|
2064
|
+
return schedule;
|
|
1907
2065
|
}
|
|
1908
2066
|
function snakeifyKeys(value) {
|
|
1909
2067
|
const out = {};
|