@qoniai/qoni 0.6.0 → 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 +45 -0
- package/README.md +44 -5
- package/README.zh-CN.md +32 -5
- package/dist/index.cjs +291 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -7
- package/dist/index.d.ts +87 -7
- package/dist/index.js +291 -146
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -538,6 +538,87 @@ var ActionKinds = {
|
|
|
538
538
|
};
|
|
539
539
|
var ACTION_KIND_VALUES = Object.values(ActionKinds);
|
|
540
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
|
+
|
|
541
622
|
// src/interactions.ts
|
|
542
623
|
function asString2(value) {
|
|
543
624
|
return typeof value === "string" && value ? value : void 0;
|
|
@@ -636,6 +717,9 @@ function normalizeInteraction(payload) {
|
|
|
636
717
|
if (evidence) interaction.evidence = evidence;
|
|
637
718
|
return interaction;
|
|
638
719
|
}
|
|
720
|
+
function interactionActionScopes(action, productManageScope) {
|
|
721
|
+
return action.kind === ActionKinds.openLogin ? [QoniScopes.SITE_LOGIN_REQUEST] : [productManageScope];
|
|
722
|
+
}
|
|
639
723
|
var InteractionHandle = class {
|
|
640
724
|
constructor(interaction, post) {
|
|
641
725
|
this.post = post;
|
|
@@ -660,48 +744,122 @@ var InteractionHandle = class {
|
|
|
660
744
|
return this.interaction.actions.some((a) => a.kind === kind);
|
|
661
745
|
}
|
|
662
746
|
/** Answer a clarification ask. */
|
|
663
|
-
answer(text) {
|
|
664
|
-
|
|
747
|
+
async answer(text) {
|
|
748
|
+
await this.invoke(ActionKinds.answer, { response: text });
|
|
665
749
|
}
|
|
666
750
|
/** Skip / dismiss the interaction (proceed without acting on it). */
|
|
667
|
-
skip() {
|
|
668
|
-
|
|
751
|
+
async skip() {
|
|
752
|
+
await this.invoke(ActionKinds.skip);
|
|
669
753
|
}
|
|
670
754
|
/** Confirm a pending confirmation (approve the plan / destructive op). */
|
|
671
|
-
confirm() {
|
|
672
|
-
|
|
755
|
+
async confirm() {
|
|
756
|
+
await this.invoke(ActionKinds.confirm);
|
|
673
757
|
}
|
|
674
758
|
/** Reject a pending confirmation. */
|
|
675
|
-
reject() {
|
|
676
|
-
|
|
759
|
+
async reject() {
|
|
760
|
+
await this.invoke(ActionKinds.reject);
|
|
677
761
|
}
|
|
678
|
-
/**
|
|
679
|
-
|
|
680
|
-
|
|
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
|
+
};
|
|
681
819
|
}
|
|
682
820
|
/** Tell the agent the user has finished signing in. */
|
|
683
|
-
confirmSignedIn() {
|
|
684
|
-
|
|
821
|
+
async confirmSignedIn() {
|
|
822
|
+
await this.invoke(ActionKinds.confirmSignedIn);
|
|
685
823
|
}
|
|
686
824
|
/** Connect to the live browser for a take_control interaction. */
|
|
687
|
-
connectControl() {
|
|
688
|
-
|
|
825
|
+
async connectControl() {
|
|
826
|
+
await this.invoke(ActionKinds.connectControl);
|
|
689
827
|
}
|
|
690
828
|
/** Refresh the live control surface / live url. */
|
|
691
|
-
refreshControl() {
|
|
692
|
-
|
|
829
|
+
async refreshControl() {
|
|
830
|
+
await this.invoke(ActionKinds.refreshControl);
|
|
693
831
|
}
|
|
694
832
|
/** Hand control back to the agent. */
|
|
695
|
-
releaseControl() {
|
|
696
|
-
|
|
833
|
+
async releaseControl() {
|
|
834
|
+
await this.invoke(ActionKinds.releaseControl);
|
|
697
835
|
}
|
|
698
836
|
/** Retry a wait interaction now. */
|
|
699
|
-
retry() {
|
|
700
|
-
|
|
837
|
+
async retry() {
|
|
838
|
+
await this.invoke(ActionKinds.retry);
|
|
701
839
|
}
|
|
702
840
|
/** Switch the profile a wait/login interaction is bound to. */
|
|
703
|
-
switchProfile() {
|
|
704
|
-
|
|
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
|
+
);
|
|
705
863
|
}
|
|
706
864
|
/**
|
|
707
865
|
* Invoke an action by kind with an optional body. `async` so a missing-action
|
|
@@ -709,6 +867,10 @@ var InteractionHandle = class {
|
|
|
709
867
|
* method contract uniformly promise-based.
|
|
710
868
|
*/
|
|
711
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) {
|
|
712
874
|
const action = this.interaction.actions.find((a) => a.kind === kind);
|
|
713
875
|
if (!action) {
|
|
714
876
|
const offered = this.interaction.actions.map((a) => a.kind).join(", ") || "none";
|
|
@@ -716,7 +878,7 @@ var InteractionHandle = class {
|
|
|
716
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.`
|
|
717
879
|
);
|
|
718
880
|
}
|
|
719
|
-
|
|
881
|
+
return action;
|
|
720
882
|
}
|
|
721
883
|
};
|
|
722
884
|
|
|
@@ -786,7 +948,7 @@ var RunHandle = class {
|
|
|
786
948
|
break settled;
|
|
787
949
|
}
|
|
788
950
|
}
|
|
789
|
-
const detail2 = await
|
|
951
|
+
const detail2 = await settleRead(() => this.ops.getDetail(signal), signal);
|
|
790
952
|
if (isTerminalRunStatus(normalizeRunStatus(this.id, detail2).status)) {
|
|
791
953
|
terminalDetail = detail2;
|
|
792
954
|
break;
|
|
@@ -800,10 +962,10 @@ var RunHandle = class {
|
|
|
800
962
|
sawCleanClose = true;
|
|
801
963
|
eventIdAtLastClose = lastEventId;
|
|
802
964
|
}
|
|
803
|
-
const detail = terminalDetail ?? await
|
|
965
|
+
const detail = terminalDetail ?? await settleRead(() => this.ops.getDetail(signal), signal);
|
|
804
966
|
this.#adoptSessionRef(detail);
|
|
805
967
|
const result = settleRunResult(this.id, detail, [], terminalDoneReason);
|
|
806
|
-
result.artifacts = this.ops.listArtifacts ? await
|
|
968
|
+
result.artifacts = this.ops.listArtifacts ? await settleRead(() => this.ops.listArtifacts(signal), signal) : [];
|
|
807
969
|
return result;
|
|
808
970
|
} catch (err) {
|
|
809
971
|
if (timedOut() && !opts.signal?.aborted) {
|
|
@@ -826,7 +988,7 @@ var RunHandle = class {
|
|
|
826
988
|
interactionHandle(interaction) {
|
|
827
989
|
return new InteractionHandle(
|
|
828
990
|
interaction,
|
|
829
|
-
(action, body) => this.ops.postAction(action, body)
|
|
991
|
+
(action, body, options) => this.ops.postAction(action, body, options)
|
|
830
992
|
);
|
|
831
993
|
}
|
|
832
994
|
/**
|
|
@@ -920,6 +1082,35 @@ function withTimeout(signal, timeoutMs) {
|
|
|
920
1082
|
}
|
|
921
1083
|
return { signal: controller.signal, timedOut: () => fired, dispose };
|
|
922
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
|
+
}
|
|
923
1114
|
function waitWithSignal(operation, signal) {
|
|
924
1115
|
if (!signal) return Promise.resolve().then(operation);
|
|
925
1116
|
return new Promise((resolve, reject) => {
|
|
@@ -1017,17 +1208,15 @@ function createDeepResearchNamespace(transport) {
|
|
|
1017
1208
|
signal: opts.signal,
|
|
1018
1209
|
onReconnect: opts.onReconnect
|
|
1019
1210
|
}),
|
|
1020
|
-
postAction: async (action, body) =>
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
{
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
);
|
|
1030
|
-
},
|
|
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,
|
|
1031
1220
|
cancel: async () => asRecord((await api.cancel({ token, runId })).data),
|
|
1032
1221
|
listArtifacts: async (signal) => {
|
|
1033
1222
|
const res = await api.listArtifacts({ token, runId, signal });
|
|
@@ -1292,17 +1481,15 @@ function createDoAnythingNamespace(transport) {
|
|
|
1292
1481
|
// Skip screenshot rows in backfill when the caller did not opt in.
|
|
1293
1482
|
includeScreenshots: capture?.screenshots ? void 0 : false
|
|
1294
1483
|
}),
|
|
1295
|
-
postAction: async (action, body) =>
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
{
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
);
|
|
1305
|
-
},
|
|
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,
|
|
1306
1493
|
cancel: async (reason) => asRecord2(
|
|
1307
1494
|
(await api.cancel({
|
|
1308
1495
|
token,
|
|
@@ -1423,81 +1610,6 @@ function createQoniNamespace(_transport, delegateToken, completeDelegateToken) {
|
|
|
1423
1610
|
};
|
|
1424
1611
|
}
|
|
1425
1612
|
|
|
1426
|
-
// src/scopes.ts
|
|
1427
|
-
var QoniScopes = {
|
|
1428
|
-
GUMEM_SESSION_CREATE: "gumem.session:create",
|
|
1429
|
-
GUMEM_MESSAGE_WRITE: "gumem.message:write",
|
|
1430
|
-
GUMEM_MEMORY_READ: "gumem.memory:read",
|
|
1431
|
-
GUMEM_MEMORY_WRITE: "gumem.memory:write",
|
|
1432
|
-
GUMEM_MEMORY_DELETE: "gumem.memory:delete",
|
|
1433
|
-
GUMEM_SEARCH_RUN: "gumem.search:run",
|
|
1434
|
-
GUMEM_ADMIN_MANAGE: "gumem.admin:manage",
|
|
1435
|
-
GUMEM_RESOURCE_WRITE: "gumem.resource:write",
|
|
1436
|
-
GUMEM_ACTION_WRITE: "gumem.action:write",
|
|
1437
|
-
GUMEM_ACTION_READ: "gumem.action:read",
|
|
1438
|
-
GUMEM_PROFILE_READ: "gumem.profile:read",
|
|
1439
|
-
// WebAgent products: exactly two verbs per product — `read` (observe) and
|
|
1440
|
-
// `manage` (anything that changes execution state). See
|
|
1441
|
-
// wa/docs/qoni-sdk-public-surface.md §3.
|
|
1442
|
-
DO_ANYTHING_READ: "webagent.do_anything:read",
|
|
1443
|
-
DO_ANYTHING_MANAGE: "webagent.do_anything:manage",
|
|
1444
|
-
WEB_SEARCH_READ: "webagent.web_search:read",
|
|
1445
|
-
WEB_SEARCH_MANAGE: "webagent.web_search:manage",
|
|
1446
|
-
DEEP_RESEARCH_READ: "webagent.deep_research:read",
|
|
1447
|
-
DEEP_RESEARCH_MANAGE: "webagent.deep_research:manage",
|
|
1448
|
-
TRACK_READ: "webagent.track:read",
|
|
1449
|
-
TRACK_MANAGE: "webagent.track:manage"
|
|
1450
|
-
};
|
|
1451
|
-
var QoniProductScopes = {
|
|
1452
|
-
doAnything: [
|
|
1453
|
-
QoniScopes.DO_ANYTHING_READ,
|
|
1454
|
-
QoniScopes.DO_ANYTHING_MANAGE
|
|
1455
|
-
],
|
|
1456
|
-
webSearch: [
|
|
1457
|
-
QoniScopes.WEB_SEARCH_READ,
|
|
1458
|
-
QoniScopes.WEB_SEARCH_MANAGE
|
|
1459
|
-
],
|
|
1460
|
-
deepResearch: [
|
|
1461
|
-
QoniScopes.DEEP_RESEARCH_READ,
|
|
1462
|
-
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
1463
|
-
],
|
|
1464
|
-
track: [
|
|
1465
|
-
QoniScopes.TRACK_READ,
|
|
1466
|
-
QoniScopes.TRACK_MANAGE
|
|
1467
|
-
]
|
|
1468
|
-
};
|
|
1469
|
-
var KNOWN_SCOPES = Object.values(QoniScopes);
|
|
1470
|
-
var QoniScopeBundles = {
|
|
1471
|
-
GUMEM_READONLY: [QoniScopes.GUMEM_MEMORY_READ, QoniScopes.GUMEM_PROFILE_READ],
|
|
1472
|
-
GUMEM_SESSION_RECALL: [
|
|
1473
|
-
QoniScopes.GUMEM_MEMORY_READ,
|
|
1474
|
-
QoniScopes.GUMEM_MEMORY_WRITE,
|
|
1475
|
-
QoniScopes.GUMEM_MESSAGE_WRITE
|
|
1476
|
-
],
|
|
1477
|
-
GUMEM_WRITE: [
|
|
1478
|
-
QoniScopes.GUMEM_SESSION_CREATE,
|
|
1479
|
-
QoniScopes.GUMEM_MESSAGE_WRITE,
|
|
1480
|
-
QoniScopes.GUMEM_ACTION_WRITE
|
|
1481
|
-
],
|
|
1482
|
-
// One bundle per WebAgent product = [read, manage].
|
|
1483
|
-
DO_ANYTHING: [
|
|
1484
|
-
QoniScopes.DO_ANYTHING_READ,
|
|
1485
|
-
QoniScopes.DO_ANYTHING_MANAGE
|
|
1486
|
-
],
|
|
1487
|
-
WEB_SEARCH: [
|
|
1488
|
-
QoniScopes.WEB_SEARCH_READ,
|
|
1489
|
-
QoniScopes.WEB_SEARCH_MANAGE
|
|
1490
|
-
],
|
|
1491
|
-
DEEP_RESEARCH: [
|
|
1492
|
-
QoniScopes.DEEP_RESEARCH_READ,
|
|
1493
|
-
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
1494
|
-
],
|
|
1495
|
-
TRACK: [
|
|
1496
|
-
QoniScopes.TRACK_READ,
|
|
1497
|
-
QoniScopes.TRACK_MANAGE
|
|
1498
|
-
]
|
|
1499
|
-
};
|
|
1500
|
-
|
|
1501
1613
|
// src/genauth.ts
|
|
1502
1614
|
function createGenAuthNamespace(transport, options = {}) {
|
|
1503
1615
|
return {
|
|
@@ -1696,7 +1808,7 @@ var MonitorHandle = class {
|
|
|
1696
1808
|
id;
|
|
1697
1809
|
/** Refresh and return the monitor (normalized camelCase fields). */
|
|
1698
1810
|
async get() {
|
|
1699
|
-
return
|
|
1811
|
+
return normalizeMonitorRecord(await this.ops.get());
|
|
1700
1812
|
}
|
|
1701
1813
|
/**
|
|
1702
1814
|
* Pause the monitor — stops scheduled ticks until {@link resume}. The
|
|
@@ -1704,11 +1816,11 @@ var MonitorHandle = class {
|
|
|
1704
1816
|
* deleting.
|
|
1705
1817
|
*/
|
|
1706
1818
|
async pause() {
|
|
1707
|
-
return
|
|
1819
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "pause" }));
|
|
1708
1820
|
}
|
|
1709
1821
|
/** Resume a paused monitor. */
|
|
1710
1822
|
async resume() {
|
|
1711
|
-
return
|
|
1823
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "resume" }));
|
|
1712
1824
|
}
|
|
1713
1825
|
/**
|
|
1714
1826
|
* Change the monitor definition. Pass any subset of fields (at least one);
|
|
@@ -1726,11 +1838,11 @@ var MonitorHandle = class {
|
|
|
1726
1838
|
"track refine requires at least one field to change (schedule / targetUrls / extractionSchema / triggerDsl / stopConditionDsl / notifyChannel)"
|
|
1727
1839
|
);
|
|
1728
1840
|
}
|
|
1729
|
-
return
|
|
1841
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "refine", patch: body }));
|
|
1730
1842
|
}
|
|
1731
1843
|
/** Trigger an immediate tick outside the schedule. */
|
|
1732
1844
|
async runNow() {
|
|
1733
|
-
return
|
|
1845
|
+
return normalizeMonitorRecord(await this.ops.runNow());
|
|
1734
1846
|
}
|
|
1735
1847
|
/**
|
|
1736
1848
|
* Stream semantic monitor events. Monitors are resident, so the stream has
|
|
@@ -1751,7 +1863,7 @@ var MonitorHandle = class {
|
|
|
1751
1863
|
interactionHandle(interaction) {
|
|
1752
1864
|
return new InteractionHandle(
|
|
1753
1865
|
interaction,
|
|
1754
|
-
(action, body) => this.ops.postAction(action, body)
|
|
1866
|
+
(action, body, options) => this.ops.postAction(action, body, options)
|
|
1755
1867
|
);
|
|
1756
1868
|
}
|
|
1757
1869
|
/**
|
|
@@ -1856,17 +1968,15 @@ function createTrackNamespace(transport) {
|
|
|
1856
1968
|
update: async (input) => asRecord3((await api.updateMonitor({ token, monitorId, ...input })).data),
|
|
1857
1969
|
runNow: async () => asRecord3((await api.runNow({ token, monitorId })).data),
|
|
1858
1970
|
streamEvents: (opts) => api.events({ token, monitorId, lastEventId: opts.lastEventId, signal: opts.signal }),
|
|
1859
|
-
postAction: async (action, body) =>
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
{
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
);
|
|
1869
|
-
},
|
|
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,
|
|
1870
1980
|
listRuns: async (opts) => asRecord3((await api.listRuns({ token, monitorId, ...opts })).data),
|
|
1871
1981
|
getRun: async (runId) => asRecord3((await api.getRun({ token, monitorId, runId })).data),
|
|
1872
1982
|
delete: async () => {
|
|
@@ -1881,10 +1991,12 @@ function createTrackNamespace(transport) {
|
|
|
1881
1991
|
if (!prompt || typeof prompt !== "string") {
|
|
1882
1992
|
throw new QoniValidationError("track.create requires a prompt string");
|
|
1883
1993
|
}
|
|
1994
|
+
const body = snakeifyKeys(rest);
|
|
1995
|
+
if (body.schedule !== void 0) body.schedule = normalizeSchedule(body.schedule);
|
|
1884
1996
|
const created = await api.createMonitor({
|
|
1885
1997
|
token,
|
|
1886
1998
|
intent: prompt,
|
|
1887
|
-
...
|
|
1999
|
+
...body
|
|
1888
2000
|
});
|
|
1889
2001
|
const monitorId = created.data.id || created.data.monitor_id;
|
|
1890
2002
|
if (!monitorId) {
|
|
@@ -1902,7 +2014,7 @@ function createTrackNamespace(transport) {
|
|
|
1902
2014
|
};
|
|
1903
2015
|
}
|
|
1904
2016
|
function normalizeMonitorRun(item) {
|
|
1905
|
-
const normalized =
|
|
2017
|
+
const normalized = normalizeMonitorRecord(item);
|
|
1906
2018
|
if (typeof normalized.id !== "string" || !normalized.id) {
|
|
1907
2019
|
const id = normalized.runId ?? normalized.snapshotId;
|
|
1908
2020
|
if (typeof id === "string" && id) normalized.id = id;
|
|
@@ -1912,11 +2024,44 @@ function normalizeMonitorRun(item) {
|
|
|
1912
2024
|
function normalizeTrackInterveneInput(input) {
|
|
1913
2025
|
return renameKeys4(input, { requestId: "request_id" });
|
|
1914
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
|
+
}
|
|
1915
2049
|
function normalizeSchedule(schedule) {
|
|
2050
|
+
if (!isJsonObject(schedule)) return schedule;
|
|
1916
2051
|
if (schedule.kind === "interval") {
|
|
1917
|
-
|
|
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 }');
|
|
1918
2063
|
}
|
|
1919
|
-
return
|
|
2064
|
+
return schedule;
|
|
1920
2065
|
}
|
|
1921
2066
|
function snakeifyKeys(value) {
|
|
1922
2067
|
const out = {};
|