unbrowse 2.1.3 → 2.1.4
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/dist/index.js +604 -73
- package/package.json +1 -1
- package/runtime-src/execution/index.ts +91 -12
- package/runtime-src/extraction/index.ts +131 -1
- package/runtime-src/intent-match.ts +2 -2
- package/runtime-src/orchestrator/index.ts +390 -64
package/dist/index.js
CHANGED
|
@@ -6863,7 +6863,7 @@ function classifyRows(rows, intent) {
|
|
|
6863
6863
|
return matching.length >= 1 ? { verdict: "pass", reason: "channel_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
|
|
6864
6864
|
}
|
|
6865
6865
|
if (/\b(search|find|lookup)\b/.test(lower)) {
|
|
6866
|
-
const matching = objects.filter((row) => hasAnyPath(row, ["name", "title", "songName", "resource_name"]) && hasAnyPath(row, ["id", "url", "link", "href", "resource_id"]) && hasAnyPath(row, ["description", "summary", "metadata", "stats", "author", "uploader", "createdAt", "updatedAt"]));
|
|
6866
|
+
const matching = objects.filter((row) => hasAnyPath(row, ["name", "title", "songName", "resource_name"]) && hasAnyPath(row, ["id", "url", "link", "href", "resource_id", "citation", "case_number"]) && hasAnyPath(row, ["description", "summary", "metadata", "stats", "author", "uploader", "createdAt", "updatedAt", "court", "decision_date", "coram", "catchword"]));
|
|
6867
6867
|
return matching.length >= 1 ? { verdict: "pass", reason: "search_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
|
|
6868
6868
|
}
|
|
6869
6869
|
return { verdict: "skip", reason: "unclassified_array" };
|
|
@@ -7282,6 +7282,14 @@ function normalizeStructureForIntent(structure, intent) {
|
|
|
7282
7282
|
const objectRows = structure.data.filter((row) => !!row && typeof row === "object" && !Array.isArray(row));
|
|
7283
7283
|
if (objectRows.length === 0)
|
|
7284
7284
|
return structure;
|
|
7285
|
+
const normalizedLawNet = normalizeLawNetSearchRows(objectRows);
|
|
7286
|
+
if (normalizedLawNet.length >= 1) {
|
|
7287
|
+
return {
|
|
7288
|
+
...structure,
|
|
7289
|
+
data: normalizedLawNet,
|
|
7290
|
+
element_count: normalizedLawNet.length
|
|
7291
|
+
};
|
|
7292
|
+
}
|
|
7285
7293
|
const pruned = pruneRowsForIntent(objectRows, intent);
|
|
7286
7294
|
if (pruned.length >= 1 && pruned.length < objectRows.length) {
|
|
7287
7295
|
return {
|
|
@@ -7292,6 +7300,87 @@ function normalizeStructureForIntent(structure, intent) {
|
|
|
7292
7300
|
}
|
|
7293
7301
|
return structure;
|
|
7294
7302
|
}
|
|
7303
|
+
function parseLawNetCitation(title) {
|
|
7304
|
+
const match = title.match(/^(.*?)\s*-\s*(\[[^\]]+\].+)$/);
|
|
7305
|
+
if (!match)
|
|
7306
|
+
return {};
|
|
7307
|
+
const case_name = cleanText(match[1] ?? "");
|
|
7308
|
+
const citation = cleanText(match[2] ?? "");
|
|
7309
|
+
return {
|
|
7310
|
+
...case_name ? { case_name } : {},
|
|
7311
|
+
...citation ? { citation } : {}
|
|
7312
|
+
};
|
|
7313
|
+
}
|
|
7314
|
+
function parseLawNetLabeledField(text, label) {
|
|
7315
|
+
const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7316
|
+
const match = text.match(new RegExp(`${escaped}\\s*:\\s*([\\s\\S]*?)(?=\\s+(?:Court|Corams?|Decision Date|Case Number|Catchword)\\s*:|$)`, "i"));
|
|
7317
|
+
const value = cleanText(match?.[1] ?? "");
|
|
7318
|
+
return value || undefined;
|
|
7319
|
+
}
|
|
7320
|
+
function isLikelyLawNetSearchShell(rows) {
|
|
7321
|
+
return rows.some((row) => {
|
|
7322
|
+
if (row.title === "Search Results")
|
|
7323
|
+
return true;
|
|
7324
|
+
const keys = Object.keys(row);
|
|
7325
|
+
if (!keys.some((key) => /^heading_\d+$/.test(key)))
|
|
7326
|
+
return false;
|
|
7327
|
+
return Object.values(row).some((value) => typeof value === "string" && (/Results returned:/i.test(value) || /\bCourt\s*:/.test(value) || /\[\d{4}\]/.test(value)));
|
|
7328
|
+
});
|
|
7329
|
+
}
|
|
7330
|
+
function parseLawNetCaseRow(text) {
|
|
7331
|
+
const cleaned = cleanText(text.replace(/\u00a0/g, " "));
|
|
7332
|
+
if (!cleaned)
|
|
7333
|
+
return null;
|
|
7334
|
+
if (cleaned === "Search Results" || /^Results returned:/i.test(cleaned) || /^(Catchword|Category|Courts|Coram|Jurisdiction|Years|Title \[A to Z\]|Title \[Z to A\]|Date \[latest first\])$/i.test(cleaned) || /^Please enter the no\. of words before and after\./i.test(cleaned)) {
|
|
7335
|
+
return null;
|
|
7336
|
+
}
|
|
7337
|
+
if (!/\[\d{4}\]/.test(cleaned))
|
|
7338
|
+
return null;
|
|
7339
|
+
const marker = cleaned.search(/\s+(?:Court|Corams?|Decision Date|Case Number|Catchword)\s*:/i);
|
|
7340
|
+
const title = cleanText(marker >= 0 ? cleaned.slice(0, marker) : cleaned);
|
|
7341
|
+
if (!title || !/\[\d{4}\]/.test(title) || title.length < 12)
|
|
7342
|
+
return null;
|
|
7343
|
+
const row = {
|
|
7344
|
+
title,
|
|
7345
|
+
...parseLawNetCitation(title)
|
|
7346
|
+
};
|
|
7347
|
+
const court = parseLawNetLabeledField(cleaned, "Court");
|
|
7348
|
+
const coram = parseLawNetLabeledField(cleaned, "Corams") ?? parseLawNetLabeledField(cleaned, "Coram");
|
|
7349
|
+
const decision_date = parseLawNetLabeledField(cleaned, "Decision Date");
|
|
7350
|
+
const case_number = parseLawNetLabeledField(cleaned, "Case Number");
|
|
7351
|
+
const catchword = parseLawNetLabeledField(cleaned, "Catchword");
|
|
7352
|
+
if (court)
|
|
7353
|
+
row.court = court;
|
|
7354
|
+
if (coram)
|
|
7355
|
+
row.coram = coram;
|
|
7356
|
+
if (decision_date)
|
|
7357
|
+
row.decision_date = decision_date;
|
|
7358
|
+
if (case_number)
|
|
7359
|
+
row.case_number = case_number;
|
|
7360
|
+
if (catchword)
|
|
7361
|
+
row.catchword = catchword;
|
|
7362
|
+
row.raw_text = cleaned;
|
|
7363
|
+
return row;
|
|
7364
|
+
}
|
|
7365
|
+
function normalizeLawNetSearchRows(rows) {
|
|
7366
|
+
if (rows.length === 0 || !isLikelyLawNetSearchShell(rows))
|
|
7367
|
+
return [];
|
|
7368
|
+
const bestByTitle = new Map;
|
|
7369
|
+
for (const row of rows) {
|
|
7370
|
+
for (const value of Object.values(row)) {
|
|
7371
|
+
if (typeof value !== "string")
|
|
7372
|
+
continue;
|
|
7373
|
+
const parsed = parseLawNetCaseRow(value);
|
|
7374
|
+
if (!parsed)
|
|
7375
|
+
continue;
|
|
7376
|
+
const existing = bestByTitle.get(parsed.title);
|
|
7377
|
+
if (!existing || Object.keys(parsed).length > Object.keys(existing).length) {
|
|
7378
|
+
bestByTitle.set(parsed.title, parsed);
|
|
7379
|
+
}
|
|
7380
|
+
}
|
|
7381
|
+
}
|
|
7382
|
+
return [...bestByTitle.values()];
|
|
7383
|
+
}
|
|
7295
7384
|
function normalizeGitHubPath(href) {
|
|
7296
7385
|
if (!href)
|
|
7297
7386
|
return null;
|
|
@@ -8090,6 +8179,31 @@ function scoreFieldRichness(structure) {
|
|
|
8090
8179
|
return 8;
|
|
8091
8180
|
return 0;
|
|
8092
8181
|
}
|
|
8182
|
+
function scoreCaseRowElements(structure) {
|
|
8183
|
+
if (structure.type !== "repeated-elements" || !Array.isArray(structure.data))
|
|
8184
|
+
return 0;
|
|
8185
|
+
const items = structure.data;
|
|
8186
|
+
if (items.length < 2)
|
|
8187
|
+
return 0;
|
|
8188
|
+
const caseLike = items.filter((item) => typeof item.title === "string" && (typeof item.case_name === "string" || typeof item.citation === "string" || typeof item.case_number === "string" || typeof item.court === "string")).length;
|
|
8189
|
+
if (caseLike >= Math.min(3, items.length))
|
|
8190
|
+
return 220;
|
|
8191
|
+
if (caseLike >= 2)
|
|
8192
|
+
return 140;
|
|
8193
|
+
return 0;
|
|
8194
|
+
}
|
|
8195
|
+
function scoreSearchShellNoise(structure) {
|
|
8196
|
+
if (structure.type !== "key-value" || !structure.data || typeof structure.data !== "object")
|
|
8197
|
+
return 0;
|
|
8198
|
+
const record = structure.data;
|
|
8199
|
+
const title = typeof record.title === "string" ? record.title : "";
|
|
8200
|
+
const description = typeof record.description === "string" ? record.description : "";
|
|
8201
|
+
const headingCount = Object.keys(record).filter((key) => /^heading_\d+$/.test(key)).length;
|
|
8202
|
+
if (/^search results$/i.test(title) && /results returned:/i.test(description) && headingCount >= 6) {
|
|
8203
|
+
return -260;
|
|
8204
|
+
}
|
|
8205
|
+
return 0;
|
|
8206
|
+
}
|
|
8093
8207
|
function buildReplaySelector($el) {
|
|
8094
8208
|
const tag = $el.get(0)?.tagName;
|
|
8095
8209
|
if (!tag)
|
|
@@ -8173,7 +8287,7 @@ function extractFromDOM(html, intent) {
|
|
|
8173
8287
|
const intentWords = intent.toLowerCase().split(/\s+/).filter(Boolean);
|
|
8174
8288
|
const scored = structures.map((s) => ({
|
|
8175
8289
|
structure: s,
|
|
8176
|
-
score: scoreRelevance(s, intentWords) + scoreSemanticFit(s, intent) + scoreSparseLinkList(s) + scoreFieldRichness(s)
|
|
8290
|
+
score: scoreRelevance(s, intentWords) + scoreSemanticFit(s, intent) + scoreSparseLinkList(s) + scoreFieldRichness(s) + scoreCaseRowElements(s) + scoreSearchShellNoise(s)
|
|
8177
8291
|
}));
|
|
8178
8292
|
scored.sort((a, b) => b.score - a.score);
|
|
8179
8293
|
const passing = scored.filter((candidate) => assessIntentResult(candidate.structure.data, intent).verdict === "pass");
|
|
@@ -10645,6 +10759,12 @@ function stampTrace(trace) {
|
|
|
10645
10759
|
trace.trace_version = TRACE_VERSION;
|
|
10646
10760
|
return trace;
|
|
10647
10761
|
}
|
|
10762
|
+
function canUseTriggerIntercept(endpoint) {
|
|
10763
|
+
return !!endpoint.trigger_url && (endpoint.method === "GET" || endpoint.idempotency === "safe");
|
|
10764
|
+
}
|
|
10765
|
+
function resolveTriggerInterceptTargetUrl(url, structuredReplayUrl, hasStructuredReplay) {
|
|
10766
|
+
return hasStructuredReplay ? structuredReplayUrl : url;
|
|
10767
|
+
}
|
|
10648
10768
|
function mapHeaders(headers) {
|
|
10649
10769
|
return Object.entries(headers ?? {}).map(([name, value]) => ({ name, value: String(value) }));
|
|
10650
10770
|
}
|
|
@@ -12717,6 +12837,31 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
12717
12837
|
}
|
|
12718
12838
|
} catch {}
|
|
12719
12839
|
}
|
|
12840
|
+
const reloadAuthMaterial = async () => {
|
|
12841
|
+
cookies.length = 0;
|
|
12842
|
+
for (const key of Object.keys(authHeaders))
|
|
12843
|
+
delete authHeaders[key];
|
|
12844
|
+
authSourceMeta = null;
|
|
12845
|
+
try {
|
|
12846
|
+
const epBundle = await getStoredAuthBundle(epDomain);
|
|
12847
|
+
if (epBundle) {
|
|
12848
|
+
cookies.push(...epBundle.cookies);
|
|
12849
|
+
Object.assign(authHeaders, epBundle.headers);
|
|
12850
|
+
authSourceMeta = epBundle.source_meta ?? authSourceMeta;
|
|
12851
|
+
}
|
|
12852
|
+
} catch {}
|
|
12853
|
+
if (registrableDomain && registrableDomain !== epDomain) {
|
|
12854
|
+
try {
|
|
12855
|
+
const regBundle = await getStoredAuthBundle(registrableDomain);
|
|
12856
|
+
if (regBundle) {
|
|
12857
|
+
cookies.push(...regBundle.cookies.filter((cookie) => !cookies.some((existing) => existing.name === cookie.name && existing.domain === cookie.domain)));
|
|
12858
|
+
if (Object.keys(authHeaders).length === 0)
|
|
12859
|
+
Object.assign(authHeaders, regBundle.headers);
|
|
12860
|
+
authSourceMeta = regBundle.source_meta ?? authSourceMeta;
|
|
12861
|
+
}
|
|
12862
|
+
} catch {}
|
|
12863
|
+
}
|
|
12864
|
+
};
|
|
12720
12865
|
log("exec", `endpoint ${endpoint.endpoint_id}: cookies=${cookies.length} authHeaders=${Object.keys(authHeaders).length} hasAuth=${cookies.length > 0 || Object.keys(authHeaders).length > 0}`);
|
|
12721
12866
|
let mergedParams = mergeContextTemplateParams(params, endpoint.url_template, options?.contextUrl);
|
|
12722
12867
|
if (endpoint.path_params && typeof endpoint.path_params === "object") {
|
|
@@ -12821,6 +12966,7 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
12821
12966
|
}
|
|
12822
12967
|
const structuredReplayUrl = isSafe ? deriveStructuredDataReplayUrl(url) : url;
|
|
12823
12968
|
const hasStructuredReplay = structuredReplayUrl !== url;
|
|
12969
|
+
const triggerInterceptTargetUrl = resolveTriggerInterceptTargetUrl(url, structuredReplayUrl, hasStructuredReplay);
|
|
12824
12970
|
const serverFetch = async () => {
|
|
12825
12971
|
const defaultAccept = !endpoint.dom_extraction && !endpoint.headers_template?.["accept"] ? { accept: "application/json" } : {};
|
|
12826
12972
|
const headers = {
|
|
@@ -12906,6 +13052,17 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
12906
13052
|
}
|
|
12907
13053
|
return { data: last.data, status: last.status, trace_id: nanoid5(), network_events: networkEvents.length > 0 ? networkEvents : [last.event] };
|
|
12908
13054
|
};
|
|
13055
|
+
const serverFetchWithAuthRefresh = async () => {
|
|
13056
|
+
let current = await serverFetch();
|
|
13057
|
+
if (current.status !== 401 && current.status !== 403)
|
|
13058
|
+
return current;
|
|
13059
|
+
const refreshedEndpoint = await refreshAuthFromBrowser(epDomain).catch(() => false);
|
|
13060
|
+
const refreshedRegistrable = !refreshedEndpoint && registrableDomain && registrableDomain !== epDomain ? await refreshAuthFromBrowser(registrableDomain).catch(() => false) : false;
|
|
13061
|
+
if (!refreshedEndpoint && !refreshedRegistrable)
|
|
13062
|
+
return current;
|
|
13063
|
+
await reloadAuthMaterial();
|
|
13064
|
+
return serverFetch();
|
|
13065
|
+
};
|
|
12909
13066
|
const browserCall = () => executeInBrowser(url, endpoint.method, endpoint.headers_template ?? {}, body, authHeaders, cookies);
|
|
12910
13067
|
let result;
|
|
12911
13068
|
const hasAuth = cookies.length > 0 || Object.keys(authHeaders).length > 0;
|
|
@@ -12922,11 +13079,11 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
12922
13079
|
let strategy;
|
|
12923
13080
|
const endpointStrategy = endpoint.exec_strategy;
|
|
12924
13081
|
if (hasStructuredReplay) {
|
|
12925
|
-
result = await
|
|
13082
|
+
result = await serverFetchWithAuthRefresh();
|
|
12926
13083
|
if (result.status >= 200 && result.status < 400 && !shouldFallbackToBrowserReplay(result.data, endpoint, options?.intent ?? skill.intent_signature, options?.contextUrl)) {
|
|
12927
13084
|
strategy = "server";
|
|
12928
|
-
} else if (endpoint
|
|
12929
|
-
result = await triggerAndIntercept(endpoint.trigger_url,
|
|
13085
|
+
} else if (canUseTriggerIntercept(endpoint)) {
|
|
13086
|
+
result = await triggerAndIntercept(endpoint.trigger_url, triggerInterceptTargetUrl, cookies, authHeaders, {
|
|
12930
13087
|
authSource: authSourceMeta
|
|
12931
13088
|
});
|
|
12932
13089
|
strategy = "trigger-intercept";
|
|
@@ -12935,16 +13092,33 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
12935
13092
|
strategy = "browser";
|
|
12936
13093
|
}
|
|
12937
13094
|
} else if (endpointStrategy === "server") {
|
|
12938
|
-
result = await
|
|
12939
|
-
if (
|
|
13095
|
+
result = await serverFetchWithAuthRefresh();
|
|
13096
|
+
if (result.status >= 200 && result.status < 400) {
|
|
13097
|
+
if (shouldFallbackToBrowserReplay(result.data, endpoint, options?.intent ?? skill.intent_signature, options?.contextUrl)) {
|
|
13098
|
+
if (canUseTriggerIntercept(endpoint)) {
|
|
13099
|
+
result = await triggerAndIntercept(endpoint.trigger_url, triggerInterceptTargetUrl, cookies, authHeaders, {
|
|
13100
|
+
authSource: authSourceMeta
|
|
13101
|
+
});
|
|
13102
|
+
strategy = "trigger-intercept";
|
|
13103
|
+
} else {
|
|
13104
|
+
result = await withRetry(browserCall, (r) => isRetryableStatus(r.status));
|
|
13105
|
+
strategy = "browser";
|
|
13106
|
+
}
|
|
13107
|
+
} else {
|
|
13108
|
+
strategy = "server";
|
|
13109
|
+
}
|
|
13110
|
+
} else if (canUseTriggerIntercept(endpoint)) {
|
|
13111
|
+
result = await triggerAndIntercept(endpoint.trigger_url, triggerInterceptTargetUrl, cookies, authHeaders, {
|
|
13112
|
+
authSource: authSourceMeta
|
|
13113
|
+
});
|
|
13114
|
+
strategy = "trigger-intercept";
|
|
13115
|
+
} else {
|
|
12940
13116
|
result = await withRetry(browserCall, (r) => isRetryableStatus(r.status));
|
|
12941
13117
|
strategy = "browser";
|
|
12942
|
-
} else {
|
|
12943
|
-
strategy = "server";
|
|
12944
13118
|
}
|
|
12945
|
-
} else if (endpointStrategy === "trigger-intercept" && endpoint
|
|
13119
|
+
} else if (endpointStrategy === "trigger-intercept" && canUseTriggerIntercept(endpoint)) {
|
|
12946
13120
|
log("exec", `using learned strategy trigger-intercept via ${endpoint.trigger_url}`);
|
|
12947
|
-
result = await triggerAndIntercept(endpoint.trigger_url,
|
|
13121
|
+
result = await triggerAndIntercept(endpoint.trigger_url, triggerInterceptTargetUrl, cookies, authHeaders, {
|
|
12948
13122
|
authSource: authSourceMeta
|
|
12949
13123
|
});
|
|
12950
13124
|
strategy = "trigger-intercept";
|
|
@@ -12965,7 +13139,7 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
12965
13139
|
}
|
|
12966
13140
|
} else {
|
|
12967
13141
|
try {
|
|
12968
|
-
result = await
|
|
13142
|
+
result = await serverFetchWithAuthRefresh();
|
|
12969
13143
|
if (result.status >= 200 && result.status < 400) {
|
|
12970
13144
|
if (shouldFallbackToBrowserReplay(result.data, endpoint, options?.intent ?? skill.intent_signature, options?.contextUrl)) {
|
|
12971
13145
|
result = await withRetry(browserCall, (r) => isRetryableStatus(r.status));
|
|
@@ -12975,8 +13149,8 @@ async function executeEndpoint(skill, endpoint, params = {}, projection, options
|
|
|
12975
13149
|
}
|
|
12976
13150
|
} else {
|
|
12977
13151
|
log("exec", `server fetch returned ${result.status}, falling back`);
|
|
12978
|
-
if (endpoint
|
|
12979
|
-
result = await triggerAndIntercept(endpoint.trigger_url,
|
|
13152
|
+
if (canUseTriggerIntercept(endpoint)) {
|
|
13153
|
+
result = await triggerAndIntercept(endpoint.trigger_url, triggerInterceptTargetUrl, cookies, authHeaders, {
|
|
12980
13154
|
authSource: authSourceMeta
|
|
12981
13155
|
});
|
|
12982
13156
|
strategy = "trigger-intercept";
|
|
@@ -13956,7 +14130,7 @@ function queuePassiveSkillPublish(skill, options = {}) {
|
|
|
13956
14130
|
|
|
13957
14131
|
// ../../src/orchestrator/index.ts
|
|
13958
14132
|
import { nanoid as nanoid6 } from "nanoid";
|
|
13959
|
-
import { existsSync as existsSync8, writeFileSync as writeFileSync5, readFileSync as readFileSync5, mkdirSync as mkdirSync6 } from "node:fs";
|
|
14133
|
+
import { existsSync as existsSync8, writeFileSync as writeFileSync5, readFileSync as readFileSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync4 } from "node:fs";
|
|
13960
14134
|
import { dirname as dirname2, join as join8 } from "node:path";
|
|
13961
14135
|
import { createHash as createHash2 } from "node:crypto";
|
|
13962
14136
|
var CONFIDENCE_THRESHOLD = 0.3;
|
|
@@ -14058,15 +14232,109 @@ function writeSkillSnapshot(cacheKey, skill) {
|
|
|
14058
14232
|
return;
|
|
14059
14233
|
}
|
|
14060
14234
|
}
|
|
14235
|
+
function hasSearchBindings(endpoint) {
|
|
14236
|
+
const haystack = JSON.stringify({
|
|
14237
|
+
url: endpoint.url_template,
|
|
14238
|
+
query: endpoint.query ?? {},
|
|
14239
|
+
body_params: endpoint.body_params ?? {},
|
|
14240
|
+
body: endpoint.body ?? {},
|
|
14241
|
+
semantic: endpoint.semantic ?? {}
|
|
14242
|
+
}).toLowerCase();
|
|
14243
|
+
return /(basicsearchkey|query|keyword|search|lookup|find|term)/.test(haystack);
|
|
14244
|
+
}
|
|
14245
|
+
function scoreSkillSnapshot(skill) {
|
|
14246
|
+
let score = 0;
|
|
14247
|
+
for (const endpoint of skill.endpoints) {
|
|
14248
|
+
const active = endpoint.verification_status !== "disabled";
|
|
14249
|
+
if (active)
|
|
14250
|
+
score += 20;
|
|
14251
|
+
if (endpoint.dom_extraction || endpoint.response_schema)
|
|
14252
|
+
score += 10;
|
|
14253
|
+
if (hasSearchBindings(endpoint))
|
|
14254
|
+
score += 40;
|
|
14255
|
+
if (endpoint.method === "POST")
|
|
14256
|
+
score += 6;
|
|
14257
|
+
if (/\/result-page\b/i.test(endpoint.url_template))
|
|
14258
|
+
score += 12;
|
|
14259
|
+
if (/captured page artifact/i.test(endpoint.description ?? ""))
|
|
14260
|
+
score -= 18;
|
|
14261
|
+
}
|
|
14262
|
+
return score + skill.endpoints.length;
|
|
14263
|
+
}
|
|
14264
|
+
function pickPreferredSkillSnapshot(primary, candidates) {
|
|
14265
|
+
let best = primary;
|
|
14266
|
+
let bestScore = scoreSkillSnapshot(primary);
|
|
14267
|
+
for (const candidate of candidates) {
|
|
14268
|
+
if (candidate.skill_id !== primary.skill_id)
|
|
14269
|
+
continue;
|
|
14270
|
+
const candidateScore = scoreSkillSnapshot(candidate);
|
|
14271
|
+
if (candidateScore > bestScore) {
|
|
14272
|
+
best = candidate;
|
|
14273
|
+
bestScore = candidateScore;
|
|
14274
|
+
}
|
|
14275
|
+
}
|
|
14276
|
+
return best;
|
|
14277
|
+
}
|
|
14061
14278
|
function readSkillSnapshot(path4) {
|
|
14062
14279
|
if (!path4 || !existsSync8(path4))
|
|
14063
14280
|
return;
|
|
14064
14281
|
try {
|
|
14065
|
-
|
|
14282
|
+
const primary = JSON.parse(readFileSync5(path4, "utf-8"));
|
|
14283
|
+
if (!existsSync8(SKILL_SNAPSHOT_DIR))
|
|
14284
|
+
return primary;
|
|
14285
|
+
const siblingSnapshots = [];
|
|
14286
|
+
for (const entry of readdirSync4(SKILL_SNAPSHOT_DIR)) {
|
|
14287
|
+
if (!entry.endsWith(".json"))
|
|
14288
|
+
continue;
|
|
14289
|
+
const candidatePath = join8(SKILL_SNAPSHOT_DIR, entry);
|
|
14290
|
+
if (candidatePath === path4)
|
|
14291
|
+
continue;
|
|
14292
|
+
try {
|
|
14293
|
+
const candidate = JSON.parse(readFileSync5(candidatePath, "utf-8"));
|
|
14294
|
+
if (candidate.skill_id === primary.skill_id)
|
|
14295
|
+
siblingSnapshots.push(candidate);
|
|
14296
|
+
} catch {}
|
|
14297
|
+
}
|
|
14298
|
+
return pickPreferredSkillSnapshot(primary, siblingSnapshots);
|
|
14066
14299
|
} catch {
|
|
14067
14300
|
return;
|
|
14068
14301
|
}
|
|
14069
14302
|
}
|
|
14303
|
+
function findBestLocalDomainSnapshot(requestedDomain, intent, contextUrl) {
|
|
14304
|
+
if (!existsSync8(SKILL_SNAPSHOT_DIR))
|
|
14305
|
+
return;
|
|
14306
|
+
const targetDomain = getRegistrableDomain(requestedDomain);
|
|
14307
|
+
const bestBySkill = new Map;
|
|
14308
|
+
for (const entry of readdirSync4(SKILL_SNAPSHOT_DIR)) {
|
|
14309
|
+
if (!entry.endsWith(".json"))
|
|
14310
|
+
continue;
|
|
14311
|
+
try {
|
|
14312
|
+
const candidate = JSON.parse(readFileSync5(join8(SKILL_SNAPSHOT_DIR, entry), "utf-8"));
|
|
14313
|
+
if (getRegistrableDomain(candidate.domain) !== targetDomain)
|
|
14314
|
+
continue;
|
|
14315
|
+
const existing = bestBySkill.get(candidate.skill_id);
|
|
14316
|
+
bestBySkill.set(candidate.skill_id, existing ? pickPreferredSkillSnapshot(existing, [candidate]) : candidate);
|
|
14317
|
+
} catch {}
|
|
14318
|
+
}
|
|
14319
|
+
let best;
|
|
14320
|
+
let bestScore = Number.NEGATIVE_INFINITY;
|
|
14321
|
+
for (const candidate of bestBySkill.values()) {
|
|
14322
|
+
if (!hasUsableEndpoints(candidate))
|
|
14323
|
+
continue;
|
|
14324
|
+
if (!isCachedSkillRelevantForIntent(candidate, intent, contextUrl))
|
|
14325
|
+
continue;
|
|
14326
|
+
if (!marketplaceSkillMatchesContext(candidate, intent, contextUrl))
|
|
14327
|
+
continue;
|
|
14328
|
+
const ranked = rankEndpoints(candidate.endpoints, intent, candidate.domain, contextUrl);
|
|
14329
|
+
const topScore = ranked[0]?.score ?? Number.NEGATIVE_INFINITY;
|
|
14330
|
+
const composite = topScore + scoreSkillSnapshot(candidate);
|
|
14331
|
+
if (composite > bestScore) {
|
|
14332
|
+
best = candidate;
|
|
14333
|
+
bestScore = composite;
|
|
14334
|
+
}
|
|
14335
|
+
}
|
|
14336
|
+
return best;
|
|
14337
|
+
}
|
|
14070
14338
|
function isIpv4Hostname(hostname2) {
|
|
14071
14339
|
return /^\d{1,3}(?:\.\d{1,3}){3}$/.test(hostname2);
|
|
14072
14340
|
}
|
|
@@ -14272,6 +14540,11 @@ function isCachedSkillRelevantForIntent(skill, intent, contextUrl) {
|
|
|
14272
14540
|
if (top && isEducationCatalogIntent(intent) && isRootContextUrl(contextUrl) && /captured page artifact/i.test(top.endpoint.description ?? "") && top.endpoint.url_template === contextUrl) {
|
|
14273
14541
|
return false;
|
|
14274
14542
|
}
|
|
14543
|
+
if (isSearchIntent) {
|
|
14544
|
+
const hasStructuredSearchEndpoint = resolvedSkill.endpoints.some((endpoint) => endpointHasSearchBindings(endpoint) && (!!endpoint.dom_extraction || !!endpoint.response_schema) && endpointMatchesContextOrigin(endpoint, contextUrl));
|
|
14545
|
+
if (hasStructuredSearchEndpoint)
|
|
14546
|
+
return true;
|
|
14547
|
+
}
|
|
14275
14548
|
return (top?.score ?? Number.NEGATIVE_INFINITY) >= 0;
|
|
14276
14549
|
}
|
|
14277
14550
|
function assessLocalExecutionResult(endpoint, result, intent, trace) {
|
|
@@ -14280,7 +14553,26 @@ function assessLocalExecutionResult(endpoint, result, intent, trace) {
|
|
|
14280
14553
|
return semanticAssessment;
|
|
14281
14554
|
if (endpoint.response_schema?.type !== "array")
|
|
14282
14555
|
return semanticAssessment;
|
|
14283
|
-
if (Array.isArray(result)
|
|
14556
|
+
if (Array.isArray(result)) {
|
|
14557
|
+
if (result.length === 0)
|
|
14558
|
+
return { verdict: "fail", reason: "search_empty_results" };
|
|
14559
|
+
const rows = result.filter((row) => !!row && typeof row === "object");
|
|
14560
|
+
if (rows.length === 0)
|
|
14561
|
+
return semanticAssessment;
|
|
14562
|
+
const authBounce = rows.some((row) => {
|
|
14563
|
+
const title2 = String(row.title ?? row.name ?? "").trim().toLowerCase();
|
|
14564
|
+
const description2 = String(row.description ?? row.summary ?? "").trim().toLowerCase();
|
|
14565
|
+
const link2 = String(row.link ?? row.url ?? "").trim().toLowerCase();
|
|
14566
|
+
return /^(about|home|welcome)\b/.test(title2) || /\b(login|log in|sign in|password)\b/.test(`${title2} ${description2}`) || /\/(?:about|home|login)\b/.test(link2);
|
|
14567
|
+
});
|
|
14568
|
+
if (authBounce)
|
|
14569
|
+
return { verdict: "fail", reason: "search_auth_or_homepage_bounce" };
|
|
14570
|
+
const hasStructuredRows = rows.some((row) => typeof row.title === "string" || typeof row.name === "string" || typeof row.case_name === "string" || typeof row.citation === "string");
|
|
14571
|
+
if (hasStructuredRows)
|
|
14572
|
+
return { verdict: "pass", reason: "search_result_rows" };
|
|
14573
|
+
return semanticAssessment;
|
|
14574
|
+
}
|
|
14575
|
+
if (result == null || typeof result !== "object")
|
|
14284
14576
|
return semanticAssessment;
|
|
14285
14577
|
const record = result;
|
|
14286
14578
|
const title = String(record.title ?? "").trim().toLowerCase();
|
|
@@ -14516,9 +14808,216 @@ function inferDefaultParam(paramName, intent) {
|
|
|
14516
14808
|
}
|
|
14517
14809
|
return;
|
|
14518
14810
|
}
|
|
14811
|
+
var SEARCH_INTENT_STOPWORDS = new Set([
|
|
14812
|
+
"a",
|
|
14813
|
+
"an",
|
|
14814
|
+
"and",
|
|
14815
|
+
"are",
|
|
14816
|
+
"at",
|
|
14817
|
+
"be",
|
|
14818
|
+
"boss",
|
|
14819
|
+
"but",
|
|
14820
|
+
"by",
|
|
14821
|
+
"do",
|
|
14822
|
+
"doing",
|
|
14823
|
+
"fact",
|
|
14824
|
+
"for",
|
|
14825
|
+
"from",
|
|
14826
|
+
"get",
|
|
14827
|
+
"going",
|
|
14828
|
+
"had",
|
|
14829
|
+
"has",
|
|
14830
|
+
"have",
|
|
14831
|
+
"i",
|
|
14832
|
+
"if",
|
|
14833
|
+
"im",
|
|
14834
|
+
"in",
|
|
14835
|
+
"into",
|
|
14836
|
+
"is",
|
|
14837
|
+
"it",
|
|
14838
|
+
"its",
|
|
14839
|
+
"just",
|
|
14840
|
+
"let",
|
|
14841
|
+
"like",
|
|
14842
|
+
"me",
|
|
14843
|
+
"my",
|
|
14844
|
+
"now",
|
|
14845
|
+
"of",
|
|
14846
|
+
"on",
|
|
14847
|
+
"or",
|
|
14848
|
+
"our",
|
|
14849
|
+
"s",
|
|
14850
|
+
"says",
|
|
14851
|
+
"search",
|
|
14852
|
+
"should",
|
|
14853
|
+
"show",
|
|
14854
|
+
"so",
|
|
14855
|
+
"take",
|
|
14856
|
+
"taking",
|
|
14857
|
+
"tell",
|
|
14858
|
+
"that",
|
|
14859
|
+
"the",
|
|
14860
|
+
"their",
|
|
14861
|
+
"them",
|
|
14862
|
+
"there",
|
|
14863
|
+
"these",
|
|
14864
|
+
"they",
|
|
14865
|
+
"this",
|
|
14866
|
+
"thoroughly",
|
|
14867
|
+
"to",
|
|
14868
|
+
"up",
|
|
14869
|
+
"us",
|
|
14870
|
+
"was",
|
|
14871
|
+
"we",
|
|
14872
|
+
"were",
|
|
14873
|
+
"what",
|
|
14874
|
+
"where",
|
|
14875
|
+
"which",
|
|
14876
|
+
"who",
|
|
14877
|
+
"why",
|
|
14878
|
+
"with",
|
|
14879
|
+
"would",
|
|
14880
|
+
"you",
|
|
14881
|
+
"your"
|
|
14882
|
+
]);
|
|
14883
|
+
var SEARCH_DIRECTIVE_PREFIX = /^(search\s+for|search|find\s+me|find|look\s+for|looking\s+for|show\s+me|show|get\s+me|get|browse|discover|shop\s+for|buy)\s+/i;
|
|
14884
|
+
var SEARCH_TRAILING_SITE_HINT = /\s+(on|at|from|in|via)\s+\S+$/i;
|
|
14885
|
+
var SEARCH_INSTRUCTION_NOISE = /\b(do not|don't|dont|tell me|let me know|extremely thoroughly|thoroughly|random cases|for the sake of it|if there is no such|if none exists|if no such)\b/i;
|
|
14886
|
+
function isLikelySearchParam(urlTemplate, param) {
|
|
14887
|
+
const lowerParam = param.toLowerCase();
|
|
14888
|
+
if (/(^q$|^k$|basicsearchkey|basic_search_key|query|keyword|keywords|search|lookup|find|term|phrase|querystr|query_string)/.test(lowerParam)) {
|
|
14889
|
+
return true;
|
|
14890
|
+
}
|
|
14891
|
+
try {
|
|
14892
|
+
const parsed = new URL(urlTemplate.replace(/\{[^}]+\}/g, "x"));
|
|
14893
|
+
for (const [key, value] of parsed.searchParams.entries()) {
|
|
14894
|
+
if (key === param || value === "x") {
|
|
14895
|
+
if (/(^q$|^k$|query|keyword|keywords|search|lookup|find|term|phrase|querystr|query_string)/.test(key.toLowerCase())) {
|
|
14896
|
+
return true;
|
|
14897
|
+
}
|
|
14898
|
+
}
|
|
14899
|
+
}
|
|
14900
|
+
} catch {}
|
|
14901
|
+
return false;
|
|
14902
|
+
}
|
|
14903
|
+
function collectSearchBindingKeys(endpoint) {
|
|
14904
|
+
const keys = new Set;
|
|
14905
|
+
for (const key of Object.keys(endpoint.body_params ?? {})) {
|
|
14906
|
+
if (isLikelySearchParam(endpoint.url_template, key))
|
|
14907
|
+
keys.add(key);
|
|
14908
|
+
}
|
|
14909
|
+
for (const key of Object.keys(endpoint.query ?? {})) {
|
|
14910
|
+
if (isLikelySearchParam(endpoint.url_template, key))
|
|
14911
|
+
keys.add(key);
|
|
14912
|
+
}
|
|
14913
|
+
for (const [rawKey, bindingKey] of Object.entries(extractTemplateQueryBindings(endpoint.url_template))) {
|
|
14914
|
+
if (isLikelySearchParam(endpoint.url_template, rawKey) || isLikelySearchParam(endpoint.url_template, bindingKey)) {
|
|
14915
|
+
keys.add(bindingKey);
|
|
14916
|
+
}
|
|
14917
|
+
}
|
|
14918
|
+
for (const match of endpoint.url_template.matchAll(/\{([^}]+)\}/g)) {
|
|
14919
|
+
const key = match[1];
|
|
14920
|
+
if (isLikelySearchParam(endpoint.url_template, key))
|
|
14921
|
+
keys.add(key);
|
|
14922
|
+
}
|
|
14923
|
+
return [...keys];
|
|
14924
|
+
}
|
|
14925
|
+
function stripSearchIntentBoilerplate(intent) {
|
|
14926
|
+
return intent.trim().replace(SEARCH_DIRECTIVE_PREFIX, "").replace(SEARCH_TRAILING_SITE_HINT, "").replace(/\s+/g, " ").trim();
|
|
14927
|
+
}
|
|
14928
|
+
function extractLiteralSearchTermsFromIntent(intent) {
|
|
14929
|
+
const stripped = stripSearchIntentBoilerplate(intent);
|
|
14930
|
+
if (!stripped)
|
|
14931
|
+
return null;
|
|
14932
|
+
const clauses = stripped.split(/(?<=[.!?])\s+|\n+/).map((clause) => clause.trim()).filter(Boolean);
|
|
14933
|
+
if (clauses.length <= 1)
|
|
14934
|
+
return stripped;
|
|
14935
|
+
const scored = clauses.map((clause, index) => {
|
|
14936
|
+
const tokens = clause.toLowerCase().replace(/[^a-z0-9\-/]+/g, " ").split(/\s+/).filter((token) => token.length >= 3 && !SEARCH_INTENT_STOPWORDS.has(token));
|
|
14937
|
+
let score = Math.min(tokens.length, 12);
|
|
14938
|
+
if (/["“”']/.test(clause))
|
|
14939
|
+
score += 4;
|
|
14940
|
+
if (/[()]/.test(clause))
|
|
14941
|
+
score += 2;
|
|
14942
|
+
if (/\d/.test(clause))
|
|
14943
|
+
score += 2;
|
|
14944
|
+
if (SEARCH_INSTRUCTION_NOISE.test(clause))
|
|
14945
|
+
score -= 8;
|
|
14946
|
+
return { clause, index, score };
|
|
14947
|
+
});
|
|
14948
|
+
const selected = scored.filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.index - b.index).slice(0, 2).sort((a, b) => a.index - b.index).map((entry) => entry.clause.replace(/\s+/g, " ").trim());
|
|
14949
|
+
const joined = (selected.length > 0 ? selected.join(" ") : stripped).trim();
|
|
14950
|
+
return joined || null;
|
|
14951
|
+
}
|
|
14952
|
+
function inferSearchParamOverrides(endpoint, intent, explicitParams = {}) {
|
|
14953
|
+
if (!/\b(search|find|lookup|browse|discover)\b/i.test(intent))
|
|
14954
|
+
return {};
|
|
14955
|
+
const keys = collectSearchBindingKeys(endpoint);
|
|
14956
|
+
if (keys.length === 0)
|
|
14957
|
+
return {};
|
|
14958
|
+
const selectedTerms = selectSearchTermsForExecution(intent);
|
|
14959
|
+
if (!selectedTerms)
|
|
14960
|
+
return {};
|
|
14961
|
+
const overrides = {};
|
|
14962
|
+
for (const key of keys) {
|
|
14963
|
+
if (explicitParams[key] != null && explicitParams[key] !== "")
|
|
14964
|
+
continue;
|
|
14965
|
+
overrides[key] = selectedTerms;
|
|
14966
|
+
}
|
|
14967
|
+
return overrides;
|
|
14968
|
+
}
|
|
14969
|
+
function selectSearchTermsForExecution(intent) {
|
|
14970
|
+
const literal = extractLiteralSearchTermsFromIntent(intent);
|
|
14971
|
+
const condensed = extractSearchTermsFromIntent(intent);
|
|
14972
|
+
if (!literal)
|
|
14973
|
+
return condensed;
|
|
14974
|
+
if (!condensed || condensed === literal)
|
|
14975
|
+
return literal;
|
|
14976
|
+
const wordCount = literal.split(/\s+/).filter(Boolean).length;
|
|
14977
|
+
const hasQuotedPhrase = /["“”]/.test(literal);
|
|
14978
|
+
const hasSentencePunctuation = /[.!?]/.test(literal);
|
|
14979
|
+
const tooLongForSingleField = literal.length > 180 || wordCount > 24;
|
|
14980
|
+
if (hasQuotedPhrase && !tooLongForSingleField)
|
|
14981
|
+
return literal;
|
|
14982
|
+
if (!hasSentencePunctuation && !tooLongForSingleField)
|
|
14983
|
+
return literal;
|
|
14984
|
+
return condensed;
|
|
14985
|
+
}
|
|
14986
|
+
function condenseSearchIntent(intent) {
|
|
14987
|
+
const wantsSearchAction = /\b(search|find|lookup|look\s+for|browse|discover)\b/i.test(intent);
|
|
14988
|
+
const priorityPattern = /\b(high|court|appeal|leave|adduce|evidence|assessment|damages?|tranche|tranches|started|late|stage|hearing|trial|mediation|case|cases)\b/;
|
|
14989
|
+
const tokens = intent.toLowerCase().replace(/[^a-z0-9\][\-/]+/g, " ").split(/\s+/).map((token) => token.trim()).filter((token) => token.length >= 3 && !SEARCH_INTENT_STOPWORDS.has(token));
|
|
14990
|
+
const scored = new Map;
|
|
14991
|
+
tokens.forEach((token, index) => {
|
|
14992
|
+
let score = 0;
|
|
14993
|
+
if (priorityPattern.test(token))
|
|
14994
|
+
score += 10;
|
|
14995
|
+
if (token.length >= 8)
|
|
14996
|
+
score += 2;
|
|
14997
|
+
if (index < 12)
|
|
14998
|
+
score += 1;
|
|
14999
|
+
const existing = scored.get(token);
|
|
15000
|
+
if (!existing || score > existing.score) {
|
|
15001
|
+
scored.set(token, { token, index, score });
|
|
15002
|
+
}
|
|
15003
|
+
});
|
|
15004
|
+
const budget = wantsSearchAction ? 13 : 14;
|
|
15005
|
+
const selected = Array.from(scored.values()).sort((a, b) => b.score - a.score || a.index - b.index).slice(0, budget).sort((a, b) => a.index - b.index).map((entry) => entry.token);
|
|
15006
|
+
if (selected.length === 0)
|
|
15007
|
+
return null;
|
|
15008
|
+
if (wantsSearchAction && selected[0] !== "search") {
|
|
15009
|
+
selected.unshift("search");
|
|
15010
|
+
}
|
|
15011
|
+
return selected.join(" ");
|
|
15012
|
+
}
|
|
14519
15013
|
function extractSearchTermsFromIntent(intent) {
|
|
14520
|
-
let terms = intent
|
|
14521
|
-
|
|
15014
|
+
let terms = stripSearchIntentBoilerplate(intent).toLowerCase();
|
|
15015
|
+
if (!terms)
|
|
15016
|
+
return null;
|
|
15017
|
+
const words = terms.split(/\s+/).filter(Boolean);
|
|
15018
|
+
if (terms.length > 160 || words.length > 20 || /[.!?]/.test(terms)) {
|
|
15019
|
+
return condenseSearchIntent(terms);
|
|
15020
|
+
}
|
|
14522
15021
|
if (/\b(from|to|between|before|after|near|in\s+\w+,?\s+\w+|under\s+\$|over\s+\$|cheaper\s+than|more\s+than)\b/i.test(terms)) {
|
|
14523
15022
|
return null;
|
|
14524
15023
|
}
|
|
@@ -14529,9 +15028,11 @@ async function inferParamsFromIntent(urlTemplate, intent, unboundParams, endpoin
|
|
|
14529
15028
|
return {};
|
|
14530
15029
|
if (unboundParams.length === 1) {
|
|
14531
15030
|
const param = unboundParams[0];
|
|
14532
|
-
|
|
14533
|
-
|
|
14534
|
-
|
|
15031
|
+
if (isLikelySearchParam(urlTemplate, param, endpointDescription)) {
|
|
15032
|
+
const searchTerms = selectSearchTermsForExecution(intent);
|
|
15033
|
+
if (searchTerms) {
|
|
15034
|
+
return { [param]: searchTerms };
|
|
15035
|
+
}
|
|
14535
15036
|
}
|
|
14536
15037
|
}
|
|
14537
15038
|
const system = `You extract URL query/path parameter values from a user's natural-language intent.
|
|
@@ -14954,6 +15455,9 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
14954
15455
|
search_candidates: [],
|
|
14955
15456
|
autoexec_attempts: []
|
|
14956
15457
|
};
|
|
15458
|
+
const queryIntent = extractSearchTermsFromIntent(intent) ?? intent;
|
|
15459
|
+
if (queryIntent !== intent)
|
|
15460
|
+
decisionTrace.query_intent = queryIntent;
|
|
14957
15461
|
const DEFAULT_CAPTURE_MS = 22000;
|
|
14958
15462
|
const DEFAULT_CAPTURE_TOKENS = 30000;
|
|
14959
15463
|
const CHARS_PER_TOKEN = 4;
|
|
@@ -15000,7 +15504,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15000
15504
|
return timing;
|
|
15001
15505
|
}
|
|
15002
15506
|
async function buildDeferralWithAutoExec(skill, source, extraFields) {
|
|
15003
|
-
if (
|
|
15507
|
+
if (queryIntent && queryIntent.trim().length > 0) {
|
|
15004
15508
|
try {
|
|
15005
15509
|
const autoResult = await tryAutoExecute(skill, source);
|
|
15006
15510
|
if (autoResult) {
|
|
@@ -15021,13 +15525,13 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15021
15525
|
};
|
|
15022
15526
|
}
|
|
15023
15527
|
function buildDeferral(skill, source, extraFields) {
|
|
15024
|
-
const resolvedSkill = withContextReplayEndpoint(skill,
|
|
15528
|
+
const resolvedSkill = withContextReplayEndpoint(skill, queryIntent, context?.url);
|
|
15025
15529
|
const chunk = getSkillChunk(resolvedSkill, {
|
|
15026
|
-
intent,
|
|
15530
|
+
intent: queryIntent,
|
|
15027
15531
|
known_bindings: knownBindingsFromInputs(params, context?.url),
|
|
15028
15532
|
max_operations: 8
|
|
15029
15533
|
});
|
|
15030
|
-
const epRanked = rankEndpoints(resolvedSkill.endpoints,
|
|
15534
|
+
const epRanked = rankEndpoints(resolvedSkill.endpoints, queryIntent, resolvedSkill.domain, context?.url);
|
|
15031
15535
|
const deferTrace = {
|
|
15032
15536
|
trace_id: nanoid6(),
|
|
15033
15537
|
skill_id: resolvedSkill.skill_id,
|
|
@@ -15093,9 +15597,9 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15093
15597
|
function canAutoExecuteEndpoint(endpoint) {
|
|
15094
15598
|
const endpointParams = resolveEndpointTemplateBindings(endpoint, resolvedParams, context?.url);
|
|
15095
15599
|
const missing = missingTemplateParams(endpoint, endpointParams);
|
|
15096
|
-
const unresolvedBySync = missing.filter((name) => inferDefaultParam(name,
|
|
15600
|
+
const unresolvedBySync = missing.filter((name) => inferDefaultParam(name, queryIntent) === undefined);
|
|
15097
15601
|
if (unresolvedBySync.length > 0) {
|
|
15098
|
-
if (!
|
|
15602
|
+
if (!queryIntent || queryIntent.trim().length === 0)
|
|
15099
15603
|
return false;
|
|
15100
15604
|
if (unresolvedBySync.length > 4)
|
|
15101
15605
|
return false;
|
|
@@ -15118,10 +15622,10 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15118
15622
|
return merged;
|
|
15119
15623
|
})();
|
|
15120
15624
|
async function tryAutoExecute(skill, source) {
|
|
15121
|
-
let epRanked = rankEndpoints(skill.endpoints,
|
|
15625
|
+
let epRanked = rankEndpoints(skill.endpoints, queryIntent, skill.domain, context?.url);
|
|
15122
15626
|
const originalRanked = epRanked;
|
|
15123
15627
|
const chunk = getSkillChunk(skill, {
|
|
15124
|
-
intent,
|
|
15628
|
+
intent: queryIntent,
|
|
15125
15629
|
known_bindings: knownBindingsFromInputs(resolvedParams, context?.url),
|
|
15126
15630
|
max_operations: 8
|
|
15127
15631
|
});
|
|
@@ -15144,8 +15648,8 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15144
15648
|
url: ranked.endpoint.url_template,
|
|
15145
15649
|
dom_extraction: !!ranked.endpoint.dom_extraction
|
|
15146
15650
|
}));
|
|
15147
|
-
if (epRanked.length >= 2 &&
|
|
15148
|
-
const intentTokens = new Set(
|
|
15651
|
+
if (epRanked.length >= 2 && queryIntent) {
|
|
15652
|
+
const intentTokens = new Set(queryIntent.toLowerCase().replace(/[^a-z0-9]+/g, " ").split(/\s+/).filter((w) => w.length > 2));
|
|
15149
15653
|
epRanked = epRanked.map((r) => {
|
|
15150
15654
|
let schemaBonus = 0;
|
|
15151
15655
|
const schema = r.endpoint.response_schema;
|
|
@@ -15186,11 +15690,11 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15186
15690
|
if (missing.length === 0)
|
|
15187
15691
|
readinessBonus += 40;
|
|
15188
15692
|
else {
|
|
15189
|
-
const syncSatisfiable = missing.filter((name) => inferDefaultParam(name,
|
|
15693
|
+
const syncSatisfiable = missing.filter((name) => inferDefaultParam(name, queryIntent) !== undefined);
|
|
15190
15694
|
const remaining = missing.length - syncSatisfiable.length;
|
|
15191
15695
|
if (remaining === 0) {
|
|
15192
15696
|
readinessBonus += 8;
|
|
15193
|
-
} else if (
|
|
15697
|
+
} else if (queryIntent && remaining <= 4) {
|
|
15194
15698
|
readinessBonus += 4 - remaining * 5;
|
|
15195
15699
|
} else {
|
|
15196
15700
|
readinessBonus -= missing.length * 25;
|
|
@@ -15219,12 +15723,12 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15219
15723
|
return { ...r, score: r.score + readinessBonus };
|
|
15220
15724
|
});
|
|
15221
15725
|
epRanked.sort((a, b) => b.score - a.score);
|
|
15222
|
-
epRanked = prioritizeIntentMatchedApis(epRanked,
|
|
15726
|
+
epRanked = prioritizeIntentMatchedApis(epRanked, queryIntent, context?.url);
|
|
15223
15727
|
const ready = epRanked.filter((r) => canAutoExecuteEndpoint(r.endpoint));
|
|
15224
15728
|
const tryList = ready.length > 0 ? [...ready, ...epRanked.filter((r) => !canAutoExecuteEndpoint(r.endpoint))] : epRanked;
|
|
15225
15729
|
const MAX_TRIES = Math.min(tryList.length, 5);
|
|
15226
|
-
const deterministicStructuredSearchLeader = /\b(search|find|lookup|browse|discover)\b/i.test(
|
|
15227
|
-
const agentOrder = !agentChoseEndpoint && tryList.length > 1 && !deterministicStructuredSearchLeader ? await agentSelectEndpoint(
|
|
15730
|
+
const deterministicStructuredSearchLeader = /\b(search|find|lookup|browse|discover)\b/i.test(queryIntent) && !!epRanked[0] && endpointHasSearchBindings(epRanked[0].endpoint) && (!!epRanked[0].endpoint.dom_extraction || !!epRanked[0].endpoint.response_schema);
|
|
15731
|
+
const agentOrder = !agentChoseEndpoint && tryList.length > 1 && !deterministicStructuredSearchLeader ? await agentSelectEndpoint(queryIntent, skill, tryList.slice(0, MAX_TRIES), context?.url) : null;
|
|
15228
15732
|
const orderedTryList = agentOrder ? [
|
|
15229
15733
|
...agentOrder.map((endpointId) => tryList.find((r) => r.endpoint.endpoint_id === endpointId)).filter((r) => !!r),
|
|
15230
15734
|
...tryList.filter((r) => !agentOrder.includes(r.endpoint.endpoint_id))
|
|
@@ -15236,28 +15740,42 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15236
15740
|
console.log(`[auto-exec] trying #${i + 1}: ${candidate.endpoint.endpoint_id} score=${candidate.score.toFixed(1)}`);
|
|
15237
15741
|
try {
|
|
15238
15742
|
const endpointParams = mergeContextTemplateParams(resolvedParams, candidate.endpoint.url_template, context?.url);
|
|
15743
|
+
const templateDefaults = {
|
|
15744
|
+
...candidate.endpoint.path_params ?? {},
|
|
15745
|
+
...candidate.endpoint.body_params ?? {}
|
|
15746
|
+
};
|
|
15747
|
+
const searchOverrides = inferSearchParamOverrides(candidate.endpoint, intent, params);
|
|
15239
15748
|
const inferredOptionalParams = {};
|
|
15240
|
-
const inferredType = inferDefaultParam("type",
|
|
15749
|
+
const inferredType = inferDefaultParam("type", queryIntent);
|
|
15241
15750
|
if (inferredType !== undefined && endpointParams.type == null && /\/(search|lookup|find)\b/i.test(candidate.endpoint.url_template)) {
|
|
15242
15751
|
inferredOptionalParams.type = inferredType;
|
|
15243
15752
|
}
|
|
15244
|
-
const syncInferred = Object.fromEntries([...candidate.endpoint.url_template.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]).filter((name) => endpointParams[name] == null || endpointParams[name] === "").map((name) => [name, inferDefaultParam(name,
|
|
15245
|
-
const allBound = {
|
|
15753
|
+
const syncInferred = Object.fromEntries([...candidate.endpoint.url_template.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]).filter((name) => endpointParams[name] == null || endpointParams[name] === "").map((name) => [name, inferDefaultParam(name, queryIntent)]).filter((entry) => entry[1] !== undefined));
|
|
15754
|
+
const allBound = {
|
|
15755
|
+
...templateDefaults,
|
|
15756
|
+
...endpointParams,
|
|
15757
|
+
...syncInferred,
|
|
15758
|
+
...searchOverrides,
|
|
15759
|
+
...inferredOptionalParams
|
|
15760
|
+
};
|
|
15246
15761
|
const stillUnbound = [...candidate.endpoint.url_template.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]).filter((name) => allBound[name] == null || allBound[name] === "");
|
|
15247
15762
|
let llmInferred = {};
|
|
15248
|
-
if (stillUnbound.length > 0 &&
|
|
15249
|
-
llmInferred = await inferParamsFromIntent(candidate.endpoint.url_template,
|
|
15763
|
+
if (stillUnbound.length > 0 && queryIntent) {
|
|
15764
|
+
llmInferred = await inferParamsFromIntent(candidate.endpoint.url_template, queryIntent, stillUnbound, candidate.endpoint.description);
|
|
15250
15765
|
}
|
|
15251
15766
|
const execOut = await executeSkill(skill, {
|
|
15767
|
+
...templateDefaults,
|
|
15252
15768
|
...endpointParams,
|
|
15253
15769
|
...syncInferred,
|
|
15770
|
+
...searchOverrides,
|
|
15254
15771
|
...llmInferred,
|
|
15255
15772
|
...inferredOptionalParams,
|
|
15256
|
-
endpoint_id: candidate.endpoint.endpoint_id
|
|
15257
|
-
|
|
15773
|
+
endpoint_id: candidate.endpoint.endpoint_id,
|
|
15774
|
+
...queryIntent !== intent ? { intent: queryIntent } : {}
|
|
15775
|
+
}, projection, { ...options, intent: queryIntent, contextUrl: context?.url });
|
|
15258
15776
|
timing.execute_ms = Date.now() - te02;
|
|
15259
15777
|
if (execOut.trace.success) {
|
|
15260
|
-
const localAssessment = assessLocalExecutionResult(candidate.endpoint, execOut.result,
|
|
15778
|
+
const localAssessment = assessLocalExecutionResult(candidate.endpoint, execOut.result, queryIntent, execOut.trace);
|
|
15261
15779
|
if (localAssessment.verdict === "fail") {
|
|
15262
15780
|
decisionTrace.autoexec_attempts.push({
|
|
15263
15781
|
endpoint_id: candidate.endpoint.endpoint_id,
|
|
@@ -15352,7 +15870,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15352
15870
|
if (!forceCapture && !agentChoseEndpoint) {
|
|
15353
15871
|
const cachedResult = routeResultCache.get(cacheKey);
|
|
15354
15872
|
if (cachedResult) {
|
|
15355
|
-
if (!shouldReuseRouteResultSnapshot(cachedResult,
|
|
15873
|
+
if (!shouldReuseRouteResultSnapshot(cachedResult, queryIntent, context?.url)) {
|
|
15356
15874
|
routeResultCache.delete(cacheKey);
|
|
15357
15875
|
} else {
|
|
15358
15876
|
timing.cache_hit = true;
|
|
@@ -15379,7 +15897,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15379
15897
|
continue;
|
|
15380
15898
|
}
|
|
15381
15899
|
const skill = readSkillSnapshot(cached.localSkillPath) ?? await getSkillWithTimeout(cached.skillId, clientScope);
|
|
15382
|
-
if (!skill || !isCachedSkillRelevantForIntent(skill,
|
|
15900
|
+
if (!skill || !isCachedSkillRelevantForIntent(skill, queryIntent, context?.url)) {
|
|
15383
15901
|
skillRouteCache.delete(scopedKey);
|
|
15384
15902
|
persistRouteCache();
|
|
15385
15903
|
continue;
|
|
@@ -15391,7 +15909,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15391
15909
|
skill
|
|
15392
15910
|
});
|
|
15393
15911
|
}
|
|
15394
|
-
const bestCached = chooseBestRouteCacheCandidate(routeCacheCandidates,
|
|
15912
|
+
const bestCached = chooseBestRouteCacheCandidate(routeCacheCandidates, queryIntent, context?.url);
|
|
15395
15913
|
if (bestCached) {
|
|
15396
15914
|
if (bestCached.scopedKey !== cacheKey) {
|
|
15397
15915
|
promoteLearnedSkill(clientScope, resolveCacheKey, bestCached.skill, bestCached.entry.endpointId, context?.url);
|
|
@@ -15412,7 +15930,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15412
15930
|
const domainCached = domainKey ? domainSkillCache.get(domainKey) : null;
|
|
15413
15931
|
if (domainCached && Date.now() - domainCached.ts < 7 * 24 * 60 * 60000) {
|
|
15414
15932
|
const skill = readSkillSnapshot(domainCached.localSkillPath) ?? await getSkill2(domainCached.skillId, clientScope);
|
|
15415
|
-
if (skill && isCachedSkillRelevantForIntent(skill,
|
|
15933
|
+
if (skill && isCachedSkillRelevantForIntent(skill, queryIntent, context?.url)) {
|
|
15416
15934
|
console.log(`[domain-cache] hit for ${domainKey} → skill ${skill.skill_id.slice(0, 15)}`);
|
|
15417
15935
|
const result2 = await buildDeferralWithAutoExec(skill, "marketplace");
|
|
15418
15936
|
if (shouldFallbackToLiveCaptureAfterAutoexecFailure(result2.autoexecFailedAll, context?.url)) {
|
|
@@ -15424,9 +15942,22 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15424
15942
|
return result2.orchestratorResult;
|
|
15425
15943
|
}
|
|
15426
15944
|
} else if (skill) {
|
|
15427
|
-
const ranked = rankEndpoints(skill.endpoints,
|
|
15945
|
+
const ranked = rankEndpoints(skill.endpoints, queryIntent, skill.domain, context?.url);
|
|
15428
15946
|
const top = ranked[0];
|
|
15429
|
-
console.log(`[domain-cache] skip ${domainKey}: no relevant endpoint for "${
|
|
15947
|
+
console.log(`[domain-cache] skip ${domainKey}: no relevant endpoint for "${queryIntent}"` + (top ? ` (${top.endpoint.endpoint_id} score=${top.score.toFixed(1)})` : ""));
|
|
15948
|
+
}
|
|
15949
|
+
}
|
|
15950
|
+
const localDomainSkill = findBestLocalDomainSnapshot(requestedDomain, queryIntent, context?.url);
|
|
15951
|
+
if (localDomainSkill) {
|
|
15952
|
+
console.log(`[local-snapshot] hit for ${requestedDomain} → skill ${localDomainSkill.skill_id.slice(0, 15)}`);
|
|
15953
|
+
const result2 = await buildDeferralWithAutoExec(localDomainSkill, "marketplace");
|
|
15954
|
+
if (shouldFallbackToLiveCaptureAfterAutoexecFailure(result2.autoexecFailedAll, context?.url)) {
|
|
15955
|
+
console.log(`[local-snapshot] stale skill for ${requestedDomain}; retrying via live capture`);
|
|
15956
|
+
} else {
|
|
15957
|
+
timing.cache_hit = true;
|
|
15958
|
+
result2.orchestratorResult.timing.cache_hit = true;
|
|
15959
|
+
promoteLearnedSkill(clientScope, cacheKey, localDomainSkill, result2.orchestratorResult.trace.endpoint_id, context?.url);
|
|
15960
|
+
return result2.orchestratorResult;
|
|
15430
15961
|
}
|
|
15431
15962
|
}
|
|
15432
15963
|
}
|
|
@@ -15451,7 +15982,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15451
15982
|
skill
|
|
15452
15983
|
});
|
|
15453
15984
|
}
|
|
15454
|
-
const cached = chooseBestRouteCacheCandidate(routeCacheCandidates,
|
|
15985
|
+
const cached = chooseBestRouteCacheCandidate(routeCacheCandidates, queryIntent, context?.url);
|
|
15455
15986
|
if (cached) {
|
|
15456
15987
|
if (cached.scopedKey !== cacheKey) {
|
|
15457
15988
|
promoteLearnedSkill(clientScope, resolveCacheKey, cached.skill, cached.entry.endpointId, context?.url);
|
|
@@ -15460,9 +15991,9 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15460
15991
|
if (skill) {
|
|
15461
15992
|
const te02 = Date.now();
|
|
15462
15993
|
try {
|
|
15463
|
-
const execOut = await executeSkill(skill, { ...params, endpoint_id: params.endpoint_id ?? cached.entry.endpointId }, projection, { ...options, intent, contextUrl: context?.url });
|
|
15994
|
+
const execOut = await executeSkill(skill, { ...params, endpoint_id: params.endpoint_id ?? cached.entry.endpointId, ...queryIntent !== intent ? { intent: queryIntent } : {} }, projection, { ...options, intent: queryIntent, contextUrl: context?.url });
|
|
15464
15995
|
timing.execute_ms = Date.now() - te02;
|
|
15465
|
-
if (execOut.trace.success && isAcceptableIntentResult(execOut.result,
|
|
15996
|
+
if (execOut.trace.success && isAcceptableIntentResult(execOut.result, queryIntent)) {
|
|
15466
15997
|
timing.cache_hit = true;
|
|
15467
15998
|
promoteResultSnapshot(cacheKey, skill, params.endpoint_id ?? cached.entry.endpointId, execOut.result, execOut.trace, execOut.response_schema, execOut.extraction_hints);
|
|
15468
15999
|
return {
|
|
@@ -15484,7 +16015,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15484
16015
|
}
|
|
15485
16016
|
if (!forceCapture) {
|
|
15486
16017
|
const ts0 = Date.now();
|
|
15487
|
-
const { domain_results: domainResults, global_results: globalResults } = await searchIntentResolve(
|
|
16018
|
+
const { domain_results: domainResults, global_results: globalResults } = await searchIntentResolve(queryIntent, requestedDomain ?? undefined, MARKETPLACE_DOMAIN_SEARCH_K, MARKETPLACE_GLOBAL_SEARCH_K).catch(() => ({
|
|
15488
16019
|
domain_results: [],
|
|
15489
16020
|
global_results: [],
|
|
15490
16021
|
skipped_global: false
|
|
@@ -15523,9 +16054,9 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15523
16054
|
continue;
|
|
15524
16055
|
if (!hasUsableEndpoints(skill))
|
|
15525
16056
|
continue;
|
|
15526
|
-
if (!isCachedSkillRelevantForIntent(skill,
|
|
16057
|
+
if (!isCachedSkillRelevantForIntent(skill, queryIntent, context?.url))
|
|
15527
16058
|
continue;
|
|
15528
|
-
if (!marketplaceSkillMatchesContext(skill,
|
|
16059
|
+
if (!marketplaceSkillMatchesContext(skill, queryIntent, context?.url))
|
|
15529
16060
|
continue;
|
|
15530
16061
|
if (targetRegDomain && getRegistrableDomain(skill.domain) !== targetRegDomain)
|
|
15531
16062
|
continue;
|
|
@@ -15548,7 +16079,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15548
16079
|
const winner = await Promise.any(viable.map((candidate, i) => Promise.race([
|
|
15549
16080
|
executeSkill(candidate.skill, params, projection, {
|
|
15550
16081
|
...options,
|
|
15551
|
-
intent,
|
|
16082
|
+
intent: queryIntent,
|
|
15552
16083
|
contextUrl: context?.url
|
|
15553
16084
|
}).then((execOut) => {
|
|
15554
16085
|
if (!execOut.trace.success) {
|
|
@@ -15597,12 +16128,12 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15597
16128
|
const captureDomain = new URL(context.url).hostname;
|
|
15598
16129
|
const domainHit = !forceCapture ? capturedDomainCache.get(cacheKey) : undefined;
|
|
15599
16130
|
if (domainHit && Date.now() < domainHit.expires) {
|
|
15600
|
-
if (!isCachedSkillRelevantForIntent(domainHit.skill,
|
|
16131
|
+
if (!isCachedSkillRelevantForIntent(domainHit.skill, queryIntent, context?.url)) {
|
|
15601
16132
|
capturedDomainCache.delete(cacheKey);
|
|
15602
16133
|
} else {
|
|
15603
16134
|
if (agentChoseEndpoint) {
|
|
15604
|
-
const execOut = await executeSkill(domainHit.skill, { ...params, endpoint_id: params.endpoint_id ?? domainHit.endpointId }, projection, { ...options, intent, contextUrl: context?.url });
|
|
15605
|
-
if (execOut.trace.success && isAcceptableIntentResult(execOut.result,
|
|
16135
|
+
const execOut = await executeSkill(domainHit.skill, { ...params, endpoint_id: params.endpoint_id ?? domainHit.endpointId, ...queryIntent !== intent ? { intent: queryIntent } : {} }, projection, { ...options, intent: queryIntent, contextUrl: context?.url });
|
|
16136
|
+
if (execOut.trace.success && isAcceptableIntentResult(execOut.result, queryIntent)) {
|
|
15606
16137
|
promoteResultSnapshot(cacheKey, domainHit.skill, params.endpoint_id ?? domainHit.endpointId, execOut.result, execOut.trace, execOut.response_schema, execOut.extraction_hints);
|
|
15607
16138
|
return {
|
|
15608
16139
|
result: execOut.result,
|
|
@@ -15723,18 +16254,18 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15723
16254
|
if (!directDomCaptureResult)
|
|
15724
16255
|
learned_skill = undefined;
|
|
15725
16256
|
}
|
|
15726
|
-
if (learned_skill && learnedSkillUsable && !isCachedSkillRelevantForIntent(learned_skill,
|
|
15727
|
-
const resolvedSkill = withContextReplayEndpoint(learned_skill,
|
|
15728
|
-
const ranked = rankEndpoints(resolvedSkill.endpoints,
|
|
16257
|
+
if (learned_skill && learnedSkillUsable && !isCachedSkillRelevantForIntent(learned_skill, queryIntent, context?.url)) {
|
|
16258
|
+
const resolvedSkill = withContextReplayEndpoint(learned_skill, queryIntent, context?.url);
|
|
16259
|
+
const ranked = rankEndpoints(resolvedSkill.endpoints, queryIntent, resolvedSkill.domain, context?.url);
|
|
15729
16260
|
const rejectedTrace = {
|
|
15730
16261
|
...trace,
|
|
15731
16262
|
success: false,
|
|
15732
|
-
error: `No relevant endpoint discovered for "${
|
|
16263
|
+
error: `No relevant endpoint discovered for "${queryIntent}"`
|
|
15733
16264
|
};
|
|
15734
|
-
console.warn(`[capture] dropping learned skill with no relevant endpoints for "${
|
|
16265
|
+
console.warn(`[capture] dropping learned skill with no relevant endpoints for "${queryIntent}"`);
|
|
15735
16266
|
return {
|
|
15736
16267
|
result: {
|
|
15737
|
-
error: `No relevant endpoint discovered for "${
|
|
16268
|
+
error: `No relevant endpoint discovered for "${queryIntent}"`,
|
|
15738
16269
|
discovered_endpoints: ranked.slice(0, 3).map((candidate) => ({
|
|
15739
16270
|
endpoint_id: candidate.endpoint.endpoint_id,
|
|
15740
16271
|
score: Math.round(candidate.score * 10) / 10,
|
|
@@ -15787,7 +16318,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15787
16318
|
};
|
|
15788
16319
|
}
|
|
15789
16320
|
const hasNonDomApiEndpoints = !!learned_skill?.endpoints?.some((ep) => !ep.dom_extraction && ep.method !== "WS");
|
|
15790
|
-
const hasBetterStructuredSearchEndpoint = learned_skill ? skillHasBetterStructuredSearchEndpoint(learned_skill, trace.endpoint_id,
|
|
16321
|
+
const hasBetterStructuredSearchEndpoint = learned_skill ? skillHasBetterStructuredSearchEndpoint(learned_skill, trace.endpoint_id, queryIntent, context?.url) : false;
|
|
15791
16322
|
const isDirectDomResult = directDomCaptureResult;
|
|
15792
16323
|
const directExtractionSource = isDirectDomResult && result && typeof result === "object" ? result._extraction?.source : undefined;
|
|
15793
16324
|
if (isDirectDomResult && (directExtractionSource === "html-embedded" && !hasBetterStructuredSearchEndpoint || !hasNonDomApiEndpoints)) {
|
|
@@ -15823,13 +16354,13 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15823
16354
|
const te1 = Date.now();
|
|
15824
16355
|
const execOut = await executeSkill(learned_skill, params, projection, {
|
|
15825
16356
|
...options,
|
|
15826
|
-
intent,
|
|
16357
|
+
intent: queryIntent,
|
|
15827
16358
|
contextUrl: context?.url
|
|
15828
16359
|
});
|
|
15829
16360
|
timing.execute_ms += Date.now() - te1;
|
|
15830
16361
|
if (execOut.trace.success)
|
|
15831
16362
|
promoteLearnedSkill(clientScope, cacheKey, learned_skill, execOut.trace.endpoint_id, context?.url);
|
|
15832
|
-
if (execOut.trace.success && isAcceptableIntentResult(execOut.result,
|
|
16363
|
+
if (execOut.trace.success && isAcceptableIntentResult(execOut.result, queryIntent)) {
|
|
15833
16364
|
queuePassivePublishIfExecuted(learned_skill, {
|
|
15834
16365
|
result: execOut.result,
|
|
15835
16366
|
trace: execOut.trace,
|
|
@@ -15840,7 +16371,7 @@ async function resolveAndExecute(intent, params = {}, context, projection, optio
|
|
|
15840
16371
|
extraction_hints: execOut.extraction_hints
|
|
15841
16372
|
}, parityBaseline);
|
|
15842
16373
|
}
|
|
15843
|
-
if (execOut.trace.success && isAcceptableIntentResult(execOut.result,
|
|
16374
|
+
if (execOut.trace.success && isAcceptableIntentResult(execOut.result, queryIntent)) {
|
|
15844
16375
|
promoteResultSnapshot(cacheKey, learned_skill, execOut.trace.endpoint_id, execOut.result, execOut.trace, execOut.response_schema, execOut.extraction_hints);
|
|
15845
16376
|
}
|
|
15846
16377
|
return {
|
|
@@ -16007,7 +16538,7 @@ var ROUTE_LIMITS = {
|
|
|
16007
16538
|
|
|
16008
16539
|
// ../../src/session-logs.ts
|
|
16009
16540
|
init_domain();
|
|
16010
|
-
import { existsSync as existsSync9, readFileSync as readFileSync6, readdirSync as
|
|
16541
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, readdirSync as readdirSync5, statSync } from "node:fs";
|
|
16011
16542
|
import { join as join9 } from "node:path";
|
|
16012
16543
|
function normalizeDomainVariants(domain) {
|
|
16013
16544
|
const trimmed = domain.trim().toLowerCase();
|
|
@@ -16086,7 +16617,7 @@ function listRecentSessionsForDomain(tracesDir, domain, limit = 10) {
|
|
|
16086
16617
|
if (variants.length === 0)
|
|
16087
16618
|
return [];
|
|
16088
16619
|
const entries = [];
|
|
16089
|
-
for (const name of
|
|
16620
|
+
for (const name of readdirSync5(tracesDir)) {
|
|
16090
16621
|
if (!name.endsWith(".json"))
|
|
16091
16622
|
continue;
|
|
16092
16623
|
const filePath = join9(tracesDir, name);
|