@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.js
CHANGED
|
@@ -490,6 +490,87 @@ var ActionKinds = {
|
|
|
490
490
|
};
|
|
491
491
|
var ACTION_KIND_VALUES = Object.values(ActionKinds);
|
|
492
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
|
+
|
|
493
574
|
// src/interactions.ts
|
|
494
575
|
function asString2(value) {
|
|
495
576
|
return typeof value === "string" && value ? value : void 0;
|
|
@@ -588,6 +669,9 @@ function normalizeInteraction(payload) {
|
|
|
588
669
|
if (evidence) interaction.evidence = evidence;
|
|
589
670
|
return interaction;
|
|
590
671
|
}
|
|
672
|
+
function interactionActionScopes(action, productManageScope) {
|
|
673
|
+
return action.kind === ActionKinds.openLogin ? [QoniScopes.SITE_LOGIN_REQUEST] : [productManageScope];
|
|
674
|
+
}
|
|
591
675
|
var InteractionHandle = class {
|
|
592
676
|
constructor(interaction, post) {
|
|
593
677
|
this.post = post;
|
|
@@ -612,48 +696,122 @@ var InteractionHandle = class {
|
|
|
612
696
|
return this.interaction.actions.some((a) => a.kind === kind);
|
|
613
697
|
}
|
|
614
698
|
/** Answer a clarification ask. */
|
|
615
|
-
answer(text) {
|
|
616
|
-
|
|
699
|
+
async answer(text) {
|
|
700
|
+
await this.invoke(ActionKinds.answer, { response: text });
|
|
617
701
|
}
|
|
618
702
|
/** Skip / dismiss the interaction (proceed without acting on it). */
|
|
619
|
-
skip() {
|
|
620
|
-
|
|
703
|
+
async skip() {
|
|
704
|
+
await this.invoke(ActionKinds.skip);
|
|
621
705
|
}
|
|
622
706
|
/** Confirm a pending confirmation (approve the plan / destructive op). */
|
|
623
|
-
confirm() {
|
|
624
|
-
|
|
707
|
+
async confirm() {
|
|
708
|
+
await this.invoke(ActionKinds.confirm);
|
|
625
709
|
}
|
|
626
710
|
/** Reject a pending confirmation. */
|
|
627
|
-
reject() {
|
|
628
|
-
|
|
711
|
+
async reject() {
|
|
712
|
+
await this.invoke(ActionKinds.reject);
|
|
629
713
|
}
|
|
630
|
-
/**
|
|
631
|
-
|
|
632
|
-
|
|
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
|
+
};
|
|
633
771
|
}
|
|
634
772
|
/** Tell the agent the user has finished signing in. */
|
|
635
|
-
confirmSignedIn() {
|
|
636
|
-
|
|
773
|
+
async confirmSignedIn() {
|
|
774
|
+
await this.invoke(ActionKinds.confirmSignedIn);
|
|
637
775
|
}
|
|
638
776
|
/** Connect to the live browser for a take_control interaction. */
|
|
639
|
-
connectControl() {
|
|
640
|
-
|
|
777
|
+
async connectControl() {
|
|
778
|
+
await this.invoke(ActionKinds.connectControl);
|
|
641
779
|
}
|
|
642
780
|
/** Refresh the live control surface / live url. */
|
|
643
|
-
refreshControl() {
|
|
644
|
-
|
|
781
|
+
async refreshControl() {
|
|
782
|
+
await this.invoke(ActionKinds.refreshControl);
|
|
645
783
|
}
|
|
646
784
|
/** Hand control back to the agent. */
|
|
647
|
-
releaseControl() {
|
|
648
|
-
|
|
785
|
+
async releaseControl() {
|
|
786
|
+
await this.invoke(ActionKinds.releaseControl);
|
|
649
787
|
}
|
|
650
788
|
/** Retry a wait interaction now. */
|
|
651
|
-
retry() {
|
|
652
|
-
|
|
789
|
+
async retry() {
|
|
790
|
+
await this.invoke(ActionKinds.retry);
|
|
653
791
|
}
|
|
654
792
|
/** Switch the profile a wait/login interaction is bound to. */
|
|
655
|
-
switchProfile() {
|
|
656
|
-
|
|
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
|
+
);
|
|
657
815
|
}
|
|
658
816
|
/**
|
|
659
817
|
* Invoke an action by kind with an optional body. `async` so a missing-action
|
|
@@ -661,6 +819,10 @@ var InteractionHandle = class {
|
|
|
661
819
|
* method contract uniformly promise-based.
|
|
662
820
|
*/
|
|
663
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) {
|
|
664
826
|
const action = this.interaction.actions.find((a) => a.kind === kind);
|
|
665
827
|
if (!action) {
|
|
666
828
|
const offered = this.interaction.actions.map((a) => a.kind).join(", ") || "none";
|
|
@@ -668,7 +830,7 @@ var InteractionHandle = class {
|
|
|
668
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.`
|
|
669
831
|
);
|
|
670
832
|
}
|
|
671
|
-
|
|
833
|
+
return action;
|
|
672
834
|
}
|
|
673
835
|
};
|
|
674
836
|
|
|
@@ -738,7 +900,7 @@ var RunHandle = class {
|
|
|
738
900
|
break settled;
|
|
739
901
|
}
|
|
740
902
|
}
|
|
741
|
-
const detail2 = await
|
|
903
|
+
const detail2 = await settleRead(() => this.ops.getDetail(signal), signal);
|
|
742
904
|
if (isTerminalRunStatus(normalizeRunStatus(this.id, detail2).status)) {
|
|
743
905
|
terminalDetail = detail2;
|
|
744
906
|
break;
|
|
@@ -752,10 +914,10 @@ var RunHandle = class {
|
|
|
752
914
|
sawCleanClose = true;
|
|
753
915
|
eventIdAtLastClose = lastEventId;
|
|
754
916
|
}
|
|
755
|
-
const detail = terminalDetail ?? await
|
|
917
|
+
const detail = terminalDetail ?? await settleRead(() => this.ops.getDetail(signal), signal);
|
|
756
918
|
this.#adoptSessionRef(detail);
|
|
757
919
|
const result = settleRunResult(this.id, detail, [], terminalDoneReason);
|
|
758
|
-
result.artifacts = this.ops.listArtifacts ? await
|
|
920
|
+
result.artifacts = this.ops.listArtifacts ? await settleRead(() => this.ops.listArtifacts(signal), signal) : [];
|
|
759
921
|
return result;
|
|
760
922
|
} catch (err) {
|
|
761
923
|
if (timedOut() && !opts.signal?.aborted) {
|
|
@@ -778,7 +940,7 @@ var RunHandle = class {
|
|
|
778
940
|
interactionHandle(interaction) {
|
|
779
941
|
return new InteractionHandle(
|
|
780
942
|
interaction,
|
|
781
|
-
(action, body) => this.ops.postAction(action, body)
|
|
943
|
+
(action, body, options) => this.ops.postAction(action, body, options)
|
|
782
944
|
);
|
|
783
945
|
}
|
|
784
946
|
/**
|
|
@@ -872,6 +1034,35 @@ function withTimeout(signal, timeoutMs) {
|
|
|
872
1034
|
}
|
|
873
1035
|
return { signal: controller.signal, timedOut: () => fired, dispose };
|
|
874
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
|
+
}
|
|
875
1066
|
function waitWithSignal(operation, signal) {
|
|
876
1067
|
if (!signal) return Promise.resolve().then(operation);
|
|
877
1068
|
return new Promise((resolve, reject) => {
|
|
@@ -969,17 +1160,15 @@ function createDeepResearchNamespace(transport) {
|
|
|
969
1160
|
signal: opts.signal,
|
|
970
1161
|
onReconnect: opts.onReconnect
|
|
971
1162
|
}),
|
|
972
|
-
postAction: async (action, body) =>
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
{
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
);
|
|
982
|
-
},
|
|
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,
|
|
983
1172
|
cancel: async () => asRecord((await api.cancel({ token, runId })).data),
|
|
984
1173
|
listArtifacts: async (signal) => {
|
|
985
1174
|
const res = await api.listArtifacts({ token, runId, signal });
|
|
@@ -1244,17 +1433,15 @@ function createDoAnythingNamespace(transport) {
|
|
|
1244
1433
|
// Skip screenshot rows in backfill when the caller did not opt in.
|
|
1245
1434
|
includeScreenshots: capture?.screenshots ? void 0 : false
|
|
1246
1435
|
}),
|
|
1247
|
-
postAction: async (action, body) =>
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
{
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
);
|
|
1257
|
-
},
|
|
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,
|
|
1258
1445
|
cancel: async (reason) => asRecord2(
|
|
1259
1446
|
(await api.cancel({
|
|
1260
1447
|
token,
|
|
@@ -1375,81 +1562,6 @@ function createQoniNamespace(_transport, delegateToken, completeDelegateToken) {
|
|
|
1375
1562
|
};
|
|
1376
1563
|
}
|
|
1377
1564
|
|
|
1378
|
-
// src/scopes.ts
|
|
1379
|
-
var QoniScopes = {
|
|
1380
|
-
GUMEM_SESSION_CREATE: "gumem.session:create",
|
|
1381
|
-
GUMEM_MESSAGE_WRITE: "gumem.message:write",
|
|
1382
|
-
GUMEM_MEMORY_READ: "gumem.memory:read",
|
|
1383
|
-
GUMEM_MEMORY_WRITE: "gumem.memory:write",
|
|
1384
|
-
GUMEM_MEMORY_DELETE: "gumem.memory:delete",
|
|
1385
|
-
GUMEM_SEARCH_RUN: "gumem.search:run",
|
|
1386
|
-
GUMEM_ADMIN_MANAGE: "gumem.admin:manage",
|
|
1387
|
-
GUMEM_RESOURCE_WRITE: "gumem.resource:write",
|
|
1388
|
-
GUMEM_ACTION_WRITE: "gumem.action:write",
|
|
1389
|
-
GUMEM_ACTION_READ: "gumem.action:read",
|
|
1390
|
-
GUMEM_PROFILE_READ: "gumem.profile:read",
|
|
1391
|
-
// WebAgent products: exactly two verbs per product — `read` (observe) and
|
|
1392
|
-
// `manage` (anything that changes execution state). See
|
|
1393
|
-
// wa/docs/qoni-sdk-public-surface.md §3.
|
|
1394
|
-
DO_ANYTHING_READ: "webagent.do_anything:read",
|
|
1395
|
-
DO_ANYTHING_MANAGE: "webagent.do_anything:manage",
|
|
1396
|
-
WEB_SEARCH_READ: "webagent.web_search:read",
|
|
1397
|
-
WEB_SEARCH_MANAGE: "webagent.web_search:manage",
|
|
1398
|
-
DEEP_RESEARCH_READ: "webagent.deep_research:read",
|
|
1399
|
-
DEEP_RESEARCH_MANAGE: "webagent.deep_research:manage",
|
|
1400
|
-
TRACK_READ: "webagent.track:read",
|
|
1401
|
-
TRACK_MANAGE: "webagent.track:manage"
|
|
1402
|
-
};
|
|
1403
|
-
var QoniProductScopes = {
|
|
1404
|
-
doAnything: [
|
|
1405
|
-
QoniScopes.DO_ANYTHING_READ,
|
|
1406
|
-
QoniScopes.DO_ANYTHING_MANAGE
|
|
1407
|
-
],
|
|
1408
|
-
webSearch: [
|
|
1409
|
-
QoniScopes.WEB_SEARCH_READ,
|
|
1410
|
-
QoniScopes.WEB_SEARCH_MANAGE
|
|
1411
|
-
],
|
|
1412
|
-
deepResearch: [
|
|
1413
|
-
QoniScopes.DEEP_RESEARCH_READ,
|
|
1414
|
-
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
1415
|
-
],
|
|
1416
|
-
track: [
|
|
1417
|
-
QoniScopes.TRACK_READ,
|
|
1418
|
-
QoniScopes.TRACK_MANAGE
|
|
1419
|
-
]
|
|
1420
|
-
};
|
|
1421
|
-
var KNOWN_SCOPES = Object.values(QoniScopes);
|
|
1422
|
-
var QoniScopeBundles = {
|
|
1423
|
-
GUMEM_READONLY: [QoniScopes.GUMEM_MEMORY_READ, QoniScopes.GUMEM_PROFILE_READ],
|
|
1424
|
-
GUMEM_SESSION_RECALL: [
|
|
1425
|
-
QoniScopes.GUMEM_MEMORY_READ,
|
|
1426
|
-
QoniScopes.GUMEM_MEMORY_WRITE,
|
|
1427
|
-
QoniScopes.GUMEM_MESSAGE_WRITE
|
|
1428
|
-
],
|
|
1429
|
-
GUMEM_WRITE: [
|
|
1430
|
-
QoniScopes.GUMEM_SESSION_CREATE,
|
|
1431
|
-
QoniScopes.GUMEM_MESSAGE_WRITE,
|
|
1432
|
-
QoniScopes.GUMEM_ACTION_WRITE
|
|
1433
|
-
],
|
|
1434
|
-
// One bundle per WebAgent product = [read, manage].
|
|
1435
|
-
DO_ANYTHING: [
|
|
1436
|
-
QoniScopes.DO_ANYTHING_READ,
|
|
1437
|
-
QoniScopes.DO_ANYTHING_MANAGE
|
|
1438
|
-
],
|
|
1439
|
-
WEB_SEARCH: [
|
|
1440
|
-
QoniScopes.WEB_SEARCH_READ,
|
|
1441
|
-
QoniScopes.WEB_SEARCH_MANAGE
|
|
1442
|
-
],
|
|
1443
|
-
DEEP_RESEARCH: [
|
|
1444
|
-
QoniScopes.DEEP_RESEARCH_READ,
|
|
1445
|
-
QoniScopes.DEEP_RESEARCH_MANAGE
|
|
1446
|
-
],
|
|
1447
|
-
TRACK: [
|
|
1448
|
-
QoniScopes.TRACK_READ,
|
|
1449
|
-
QoniScopes.TRACK_MANAGE
|
|
1450
|
-
]
|
|
1451
|
-
};
|
|
1452
|
-
|
|
1453
1565
|
// src/genauth.ts
|
|
1454
1566
|
function createGenAuthNamespace(transport, options = {}) {
|
|
1455
1567
|
return {
|
|
@@ -1648,7 +1760,7 @@ var MonitorHandle = class {
|
|
|
1648
1760
|
id;
|
|
1649
1761
|
/** Refresh and return the monitor (normalized camelCase fields). */
|
|
1650
1762
|
async get() {
|
|
1651
|
-
return
|
|
1763
|
+
return normalizeMonitorRecord(await this.ops.get());
|
|
1652
1764
|
}
|
|
1653
1765
|
/**
|
|
1654
1766
|
* Pause the monitor — stops scheduled ticks until {@link resume}. The
|
|
@@ -1656,11 +1768,11 @@ var MonitorHandle = class {
|
|
|
1656
1768
|
* deleting.
|
|
1657
1769
|
*/
|
|
1658
1770
|
async pause() {
|
|
1659
|
-
return
|
|
1771
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "pause" }));
|
|
1660
1772
|
}
|
|
1661
1773
|
/** Resume a paused monitor. */
|
|
1662
1774
|
async resume() {
|
|
1663
|
-
return
|
|
1775
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "resume" }));
|
|
1664
1776
|
}
|
|
1665
1777
|
/**
|
|
1666
1778
|
* Change the monitor definition. Pass any subset of fields (at least one);
|
|
@@ -1678,11 +1790,11 @@ var MonitorHandle = class {
|
|
|
1678
1790
|
"track refine requires at least one field to change (schedule / targetUrls / extractionSchema / triggerDsl / stopConditionDsl / notifyChannel)"
|
|
1679
1791
|
);
|
|
1680
1792
|
}
|
|
1681
|
-
return
|
|
1793
|
+
return normalizeMonitorRecord(await this.ops.update({ action: "refine", patch: body }));
|
|
1682
1794
|
}
|
|
1683
1795
|
/** Trigger an immediate tick outside the schedule. */
|
|
1684
1796
|
async runNow() {
|
|
1685
|
-
return
|
|
1797
|
+
return normalizeMonitorRecord(await this.ops.runNow());
|
|
1686
1798
|
}
|
|
1687
1799
|
/**
|
|
1688
1800
|
* Stream semantic monitor events. Monitors are resident, so the stream has
|
|
@@ -1703,7 +1815,7 @@ var MonitorHandle = class {
|
|
|
1703
1815
|
interactionHandle(interaction) {
|
|
1704
1816
|
return new InteractionHandle(
|
|
1705
1817
|
interaction,
|
|
1706
|
-
(action, body) => this.ops.postAction(action, body)
|
|
1818
|
+
(action, body, options) => this.ops.postAction(action, body, options)
|
|
1707
1819
|
);
|
|
1708
1820
|
}
|
|
1709
1821
|
/**
|
|
@@ -1808,17 +1920,15 @@ function createTrackNamespace(transport) {
|
|
|
1808
1920
|
update: async (input) => asRecord3((await api.updateMonitor({ token, monitorId, ...input })).data),
|
|
1809
1921
|
runNow: async () => asRecord3((await api.runNow({ token, monitorId })).data),
|
|
1810
1922
|
streamEvents: (opts) => api.events({ token, monitorId, lastEventId: opts.lastEventId, signal: opts.signal }),
|
|
1811
|
-
postAction: async (action, body) =>
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
{
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
);
|
|
1821
|
-
},
|
|
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,
|
|
1822
1932
|
listRuns: async (opts) => asRecord3((await api.listRuns({ token, monitorId, ...opts })).data),
|
|
1823
1933
|
getRun: async (runId) => asRecord3((await api.getRun({ token, monitorId, runId })).data),
|
|
1824
1934
|
delete: async () => {
|
|
@@ -1833,10 +1943,12 @@ function createTrackNamespace(transport) {
|
|
|
1833
1943
|
if (!prompt || typeof prompt !== "string") {
|
|
1834
1944
|
throw new QoniValidationError("track.create requires a prompt string");
|
|
1835
1945
|
}
|
|
1946
|
+
const body = snakeifyKeys(rest);
|
|
1947
|
+
if (body.schedule !== void 0) body.schedule = normalizeSchedule(body.schedule);
|
|
1836
1948
|
const created = await api.createMonitor({
|
|
1837
1949
|
token,
|
|
1838
1950
|
intent: prompt,
|
|
1839
|
-
...
|
|
1951
|
+
...body
|
|
1840
1952
|
});
|
|
1841
1953
|
const monitorId = created.data.id || created.data.monitor_id;
|
|
1842
1954
|
if (!monitorId) {
|
|
@@ -1854,7 +1966,7 @@ function createTrackNamespace(transport) {
|
|
|
1854
1966
|
};
|
|
1855
1967
|
}
|
|
1856
1968
|
function normalizeMonitorRun(item) {
|
|
1857
|
-
const normalized =
|
|
1969
|
+
const normalized = normalizeMonitorRecord(item);
|
|
1858
1970
|
if (typeof normalized.id !== "string" || !normalized.id) {
|
|
1859
1971
|
const id = normalized.runId ?? normalized.snapshotId;
|
|
1860
1972
|
if (typeof id === "string" && id) normalized.id = id;
|
|
@@ -1864,11 +1976,44 @@ function normalizeMonitorRun(item) {
|
|
|
1864
1976
|
function normalizeTrackInterveneInput(input) {
|
|
1865
1977
|
return renameKeys4(input, { requestId: "request_id" });
|
|
1866
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
|
+
}
|
|
1867
2001
|
function normalizeSchedule(schedule) {
|
|
2002
|
+
if (!isJsonObject(schedule)) return schedule;
|
|
1868
2003
|
if (schedule.kind === "interval") {
|
|
1869
|
-
|
|
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 }');
|
|
1870
2015
|
}
|
|
1871
|
-
return
|
|
2016
|
+
return schedule;
|
|
1872
2017
|
}
|
|
1873
2018
|
function snakeifyKeys(value) {
|
|
1874
2019
|
const out = {};
|