@konneal/engine 0.2.32 → 0.2.34
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/apicalls.d.ts +72 -0
- package/dist/ask-BDD5TFNC.js +12 -0
- package/dist/{chunk-JGKSUSF5.js → chunk-4GJGBGJK.js} +3 -3
- package/dist/{chunk-F66NYFUK.js → chunk-DADOI4Q6.js} +44 -8
- package/dist/{chunk-TDKNQPA2.js → chunk-MHA2OFWE.js} +305 -11
- package/dist/{chunk-S7NLWPNX.js → chunk-MNNE7OIM.js} +4 -4
- package/dist/{chunk-2RKYO3OC.js → chunk-R2V3X6SQ.js} +1 -1
- package/dist/{chunk-OMXAE27N.js → chunk-V46XM2GU.js} +4 -0
- package/dist/config.d.ts +1 -0
- package/dist/context.d.ts +37 -0
- package/dist/drafts.d.ts +5 -0
- package/dist/env.d.ts +2 -0
- package/dist/quota.d.ts +1 -0
- package/dist/requestScope.d.ts +10 -2
- package/dist/search-EADF54TY.js +12 -0
- package/dist/worker_mcp/src/index.js +2 -2
- package/dist/worker_public/src/config.js +1 -1
- package/dist/worker_public/src/index.js +12 -9
- package/dist/worker_public/src/requestScope.js +2 -2
- package/docs/API.md +83 -0
- package/package.json +1 -1
- package/workers/worker_public/migrations/0015_ask_retries.sql +4 -0
- package/workers/worker_public/src/apicalls.ts +447 -0
- package/workers/worker_public/src/ask.ts +57 -7
- package/workers/worker_public/src/config.ts +4 -0
- package/workers/worker_public/src/context.ts +89 -4
- package/workers/worker_public/src/drafts.ts +10 -0
- package/workers/worker_public/src/env.ts +5 -1
- package/workers/worker_public/src/index.ts +4 -1
- package/workers/worker_public/src/ports/cloudflare/adapters.ts +8 -0
- package/workers/worker_public/src/quota.ts +3 -3
- package/workers/worker_public/src/requestScope.ts +9 -3
- package/dist/ask-43C2WZZO.js +0 -12
- package/dist/search-NHTT5DDI.js +0 -12
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type DeclaredContext } from "./context.ts";
|
|
2
|
+
export interface ApiCall {
|
|
3
|
+
method: "POST" | "PUT" | "PATCH" | "DELETE" | "GET";
|
|
4
|
+
path: string;
|
|
5
|
+
body?: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface ApiCallDraft {
|
|
8
|
+
kind: "draft";
|
|
9
|
+
act: "api_call";
|
|
10
|
+
version: 1;
|
|
11
|
+
title: string;
|
|
12
|
+
prepared_at: string;
|
|
13
|
+
/** ALWAYS true — the card's tap (or the standing grant, for the
|
|
14
|
+
* preference family) is the only authority that executes. */
|
|
15
|
+
requires_confirmation: true;
|
|
16
|
+
call: ApiCall;
|
|
17
|
+
notes?: string[];
|
|
18
|
+
}
|
|
19
|
+
export type ApiCallRefusalReason = "sign_in_required" | "machine_read_only" | "no_matching_act" | "entity_unresolved" | "operation_unknown" | "operation_never_offered" | "body_untraced" | "pick_failed";
|
|
20
|
+
export type ApiCallVerdict = {
|
|
21
|
+
status: "draft";
|
|
22
|
+
draft: ApiCallDraft;
|
|
23
|
+
answer: string;
|
|
24
|
+
} | {
|
|
25
|
+
status: "refused";
|
|
26
|
+
reason: ApiCallRefusalReason;
|
|
27
|
+
answer: string;
|
|
28
|
+
};
|
|
29
|
+
/** A target the assistant never proposes: the never-offer prefixes match
|
|
30
|
+
* the operation's declared (templated) path. */
|
|
31
|
+
export declare function neverOffered(path: string): boolean;
|
|
32
|
+
export interface ApiOp {
|
|
33
|
+
operation_id: string;
|
|
34
|
+
method: string;
|
|
35
|
+
path: string;
|
|
36
|
+
act_class: string | null;
|
|
37
|
+
summary: string | null;
|
|
38
|
+
tag: string | null;
|
|
39
|
+
}
|
|
40
|
+
/** Parse the declared context's route against the profile's entity-route
|
|
41
|
+
* patterns (`/a/b/:id` segments; the LAST parameter captures the entity
|
|
42
|
+
* id). Returns the store + id the entity write addresses, or null — a
|
|
43
|
+
* route the table does not cover is an honest unresolved, never a guess. */
|
|
44
|
+
export declare function resolveEntityRoute(route: string | undefined): {
|
|
45
|
+
store: string;
|
|
46
|
+
id: string;
|
|
47
|
+
} | null;
|
|
48
|
+
export type ApiCallIntent = "machine_act" | "preference";
|
|
49
|
+
/** Which api_call family the ask aims at, or null. The machine facet
|
|
50
|
+
* decides the machine family (no facet = no machine act, whatever the
|
|
51
|
+
* verbs); the preference family needs no facet. */
|
|
52
|
+
export declare function detectApiCallIntent(query: string, declared: DeclaredContext | null): ApiCallIntent | null;
|
|
53
|
+
/** The body guard: every string value traces to the user's own words
|
|
54
|
+
* (the never-invents doctrine, drafts.ts's primitive). Scalars the user
|
|
55
|
+
* toggles (booleans, numbers) pass — they state no fact. */
|
|
56
|
+
export declare function bodyTraces(body: unknown, userTurns: string[]): boolean;
|
|
57
|
+
export interface ApiCallPrepareOpts {
|
|
58
|
+
intent: ApiCallIntent;
|
|
59
|
+
query: string;
|
|
60
|
+
history: Array<{
|
|
61
|
+
role: string;
|
|
62
|
+
content: string;
|
|
63
|
+
}>;
|
|
64
|
+
member: {
|
|
65
|
+
sub: string;
|
|
66
|
+
} | null;
|
|
67
|
+
declared: DeclaredContext | null;
|
|
68
|
+
/** the intent-mapping model (the profile's pick — the operations
|
|
69
|
+
* deployment runs the stronger reasoning tier here) */
|
|
70
|
+
model: string;
|
|
71
|
+
}
|
|
72
|
+
export declare function prepareApiCall(env: any, opts: ApiCallPrepareOpts): Promise<ApiCallVerdict>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
handleAsk
|
|
3
|
+
} from "./chunk-MHA2OFWE.js";
|
|
4
|
+
import "./chunk-DADOI4Q6.js";
|
|
5
|
+
import "./chunk-R2V3X6SQ.js";
|
|
6
|
+
import "./chunk-A3QHHUN5.js";
|
|
7
|
+
import "./chunk-4GJGBGJK.js";
|
|
8
|
+
import "./chunk-V46XM2GU.js";
|
|
9
|
+
import "./chunk-3FYJM7LH.js";
|
|
10
|
+
export {
|
|
11
|
+
handleAsk
|
|
12
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DATASETS,
|
|
3
3
|
datasetAllowed
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-V46XM2GU.js";
|
|
5
5
|
import {
|
|
6
6
|
P
|
|
7
7
|
} from "./chunk-3FYJM7LH.js";
|
|
@@ -20,13 +20,13 @@ function standardKeysFrom(body) {
|
|
|
20
20
|
function entitlementScope(keys) {
|
|
21
21
|
return licenseDeclared() ? keys : null;
|
|
22
22
|
}
|
|
23
|
-
function resolveRequestScope(body, member) {
|
|
23
|
+
function resolveRequestScope(body, member, opts) {
|
|
24
24
|
const allIds = DATASETS().map((d) => d.id);
|
|
25
25
|
const requested = Array.isArray(body?.datasets) ? body.datasets.filter((x) => typeof x === "string" && allIds.includes(x)) : null;
|
|
26
26
|
if (requested !== null && requested.length === 0) return { error: "empty-datasets" };
|
|
27
27
|
const permittedIds = allIds.filter((id) => {
|
|
28
28
|
const d = DATASETS().find((x) => x.id === id);
|
|
29
|
-
return d.session ? datasetAllowed(d, member) : true;
|
|
29
|
+
return d.session ? datasetAllowed(d, member) || !!opts?.keyWithEntitlements : true;
|
|
30
30
|
});
|
|
31
31
|
const scopeIds = (requested ?? permittedIds).filter((id) => permittedIds.includes(id));
|
|
32
32
|
const corpora = /* @__PURE__ */ new Set();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bubbleConfirmPage,
|
|
3
3
|
isAllowedBubbleOrigin
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-R2V3X6SQ.js";
|
|
5
5
|
import {
|
|
6
6
|
DATASETS,
|
|
7
7
|
LIMITS,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
processExpansion,
|
|
13
13
|
sha256Hex,
|
|
14
14
|
today
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-V46XM2GU.js";
|
|
16
16
|
import {
|
|
17
17
|
P
|
|
18
18
|
} from "./chunk-3FYJM7LH.js";
|
|
@@ -21952,6 +21952,27 @@ function refCodec() {
|
|
|
21952
21952
|
|
|
21953
21953
|
// workers/worker_public/src/context.ts
|
|
21954
21954
|
var NO_CONTEXT = { kind: "none", scoped_to: null };
|
|
21955
|
+
var MACHINE_STATE_MAX = 60;
|
|
21956
|
+
var MACHINE_ACT_MAX = 24;
|
|
21957
|
+
var MACHINE_FIELD_MAX = 60;
|
|
21958
|
+
function parseMachine(v) {
|
|
21959
|
+
if (!v || typeof v !== "object") return void 0;
|
|
21960
|
+
const state = typeof v.state === "string" ? v.state.trim().slice(0, MACHINE_STATE_MAX) : "";
|
|
21961
|
+
if (!state) return void 0;
|
|
21962
|
+
const acts = [];
|
|
21963
|
+
for (const a of Array.isArray(v.acts) ? v.acts.slice(0, MACHINE_ACT_MAX) : []) {
|
|
21964
|
+
const action = typeof a?.action === "string" ? a.action.trim().slice(0, MACHINE_FIELD_MAX) : "";
|
|
21965
|
+
const to = typeof a?.to === "string" ? a.to.trim().slice(0, MACHINE_FIELD_MAX) : "";
|
|
21966
|
+
if (!action || !to) continue;
|
|
21967
|
+
const guard = typeof a?.guard === "string" && a.guard.trim() ? a.guard.trim().slice(0, MACHINE_FIELD_MAX) : void 0;
|
|
21968
|
+
acts.push({ action, to, ...guard ? { guard } : {} });
|
|
21969
|
+
}
|
|
21970
|
+
return { state, acts };
|
|
21971
|
+
}
|
|
21972
|
+
function machineOffers(machine, action) {
|
|
21973
|
+
if (!machine) return false;
|
|
21974
|
+
return machine.acts.some((a) => a.action === action);
|
|
21975
|
+
}
|
|
21955
21976
|
function parseContext(body) {
|
|
21956
21977
|
const c = body?.context;
|
|
21957
21978
|
if (!c || typeof c !== "object") return null;
|
|
@@ -21960,7 +21981,8 @@ function parseContext(body) {
|
|
|
21960
21981
|
const route = typeof c.route === "string" && c.route.trim() ? c.route.trim().slice(0, 200) : void 0;
|
|
21961
21982
|
const doc = typeof c.doc === "string" && c.doc.trim() ? c.doc.trim().slice(0, 80) : void 0;
|
|
21962
21983
|
const edition = typeof c.edition === "string" && /^\d{4}$/.test(c.edition.trim()) ? c.edition.trim() : void 0;
|
|
21963
|
-
|
|
21984
|
+
const machine = c.kind === "entity" ? parseMachine(c.machine) : void 0;
|
|
21985
|
+
return { kind: c.kind, label, ...route ? { route } : {}, ...doc ? { doc } : {}, ...edition ? { edition } : {}, ...machine ? { machine } : {} };
|
|
21964
21986
|
}
|
|
21965
21987
|
function parseDocRef(doc, edition) {
|
|
21966
21988
|
return refCodec().parse(doc, edition);
|
|
@@ -21987,7 +22009,11 @@ function appliedContext(declared, scope, note, live) {
|
|
|
21987
22009
|
label: declared.label,
|
|
21988
22010
|
scoped_to: scope ? scope.label : null,
|
|
21989
22011
|
...note ? { note } : {},
|
|
21990
|
-
...live ? { live } : {}
|
|
22012
|
+
...live ? { live } : {},
|
|
22013
|
+
// the affordance echo (TODO.ai-platform/08): the machine's state +
|
|
22014
|
+
// the offered-act COUNT (the acts themselves are the platform's
|
|
22015
|
+
// vocabulary, echoed back only bounded on the draft path)
|
|
22016
|
+
...declared.machine ? { machine: { state: declared.machine.state, offered: declared.machine.acts.length } } : {}
|
|
21991
22017
|
};
|
|
21992
22018
|
}
|
|
21993
22019
|
function parseAppliedContext(v) {
|
|
@@ -22003,7 +22029,8 @@ function parseAppliedContext(v) {
|
|
|
22003
22029
|
standard: v.model.standard.slice(0, 40),
|
|
22004
22030
|
...typeof v.model.clause === "string" && v.model.clause.trim() ? { clause: v.model.clause.slice(0, 120) } : {}
|
|
22005
22031
|
} : void 0;
|
|
22006
|
-
|
|
22032
|
+
const machine = v.machine && typeof v.machine === "object" && typeof v.machine.state === "string" && typeof v.machine.offered === "number" ? { state: v.machine.state.slice(0, MACHINE_STATE_MAX), offered: Math.min(Math.max(0, Math.floor(v.machine.offered)), MACHINE_ACT_MAX) } : void 0;
|
|
22033
|
+
return { kind: v.kind, ...label ? { label } : {}, scoped_to: scoped, ...note ? { note } : {}, ...live ? { live } : {}, ...model ? { model } : {}, ...machine ? { machine } : {} };
|
|
22007
22034
|
}
|
|
22008
22035
|
function contextNote(declared, scope) {
|
|
22009
22036
|
if (!declared) return void 0;
|
|
@@ -22014,7 +22041,8 @@ function contextNote(declared, scope) {
|
|
|
22014
22041
|
return `Context note: the user is viewing ${declared.label || "a page"}${declared.route ? ` (${declared.route})` : ""} in the ${P().publisher.product_name} platform. The passages come from the general corpus; frame procedural guidance for that page when relevant.`;
|
|
22015
22042
|
}
|
|
22016
22043
|
if (declared.kind === "entity") {
|
|
22017
|
-
|
|
22044
|
+
const machineSentence = declared.machine ? declared.machine.acts.length ? ` The entity's lifecycle machine is at ${declared.machine.state}; the acts it offers this user's role there are exactly: ${declared.machine.acts.map((a) => a.action).join(", ")}. Never propose an act outside that set.` : ` The entity's lifecycle machine is at ${declared.machine.state}, and it offers this user's role NO acts there \u2014 the user can read but not act; say so when they ask for an act, and propose nothing.` : "";
|
|
22045
|
+
return scope ? `Context note: the user is asking about ${declared.label || "an entity"} \u2014 the passages are scoped to ${scope.label}, the publication that governs it. You do NOT have the entity's own data; answer what the publication requires and say when the question needs the record itself.${machineSentence}` : `Context note: the user is asking about ${declared.label || "an entity"}. You do NOT have the entity's own data; answer from the corpus passages and say when the question needs the record itself.${machineSentence}`;
|
|
22018
22046
|
}
|
|
22019
22047
|
return scope ? `Context note: the user scoped this question to ${scope.label} \u2014 the passages come from that publication. If they cannot answer the question, say so instead of drawing on other documents.` : `Context note: the user named ${declared.label || declared.doc || "a document"} as context, but it is not in the indexed corpus \u2014 answer from the general corpus and say the document was not found.`;
|
|
22020
22048
|
}
|
|
@@ -22153,6 +22181,9 @@ function cfVectorIndex(index) {
|
|
|
22153
22181
|
}
|
|
22154
22182
|
};
|
|
22155
22183
|
}
|
|
22184
|
+
function cfStore(db) {
|
|
22185
|
+
return db;
|
|
22186
|
+
}
|
|
22156
22187
|
|
|
22157
22188
|
// workers/worker_public/src/env.ts
|
|
22158
22189
|
function portModelRunner(env) {
|
|
@@ -22162,6 +22193,9 @@ function portIndex(env, which = "public") {
|
|
|
22162
22193
|
const b = which === "public" ? env.VECTORIZE : which === "primmel" ? env.EXP_PRIMMEL : which === "composed" ? env.EXP_COMPOSED : which === "plain" ? env.EXP_PLAIN : which === "adoc" ? env.EXP_ADC : which === "mko" ? env.EXP_MKO : which === "pflat" ? env.EXP_PFLAT : env.GLOSSARY;
|
|
22163
22194
|
return cfVectorIndex(b);
|
|
22164
22195
|
}
|
|
22196
|
+
function portStore(env) {
|
|
22197
|
+
return cfStore(env.DB);
|
|
22198
|
+
}
|
|
22165
22199
|
function hasLane(env, which) {
|
|
22166
22200
|
switch (which) {
|
|
22167
22201
|
case "glossary":
|
|
@@ -24373,8 +24407,8 @@ function telemetry(env, ctx, tier, route, model, ok, answerChars, queryHash, lan
|
|
|
24373
24407
|
ctx.waitUntil(
|
|
24374
24408
|
env.DB.batch([
|
|
24375
24409
|
env.DB.prepare(
|
|
24376
|
-
"INSERT INTO queries (ts, day, tier, route, model, ok, answer_chars, query_hash, lang, cache, duration_ms, key_id) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12)"
|
|
24377
|
-
).bind((/* @__PURE__ */ new Date()).toISOString(), day, tier, route, model, ok ? 1 : 0, answerChars, queryHash, lang ?? null, cache ?? null, meta?.durationMs ?? null, meta?.keyId ?? null),
|
|
24410
|
+
"INSERT INTO queries (ts, day, tier, route, model, ok, answer_chars, query_hash, lang, cache, duration_ms, key_id, retries) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13)"
|
|
24411
|
+
).bind((/* @__PURE__ */ new Date()).toISOString(), day, tier, route, model, ok ? 1 : 0, answerChars, queryHash, lang ?? null, cache ?? null, meta?.durationMs ?? null, meta?.keyId ?? null, meta?.retries ?? 0),
|
|
24378
24412
|
env.DB.prepare(
|
|
24379
24413
|
"INSERT INTO spend (day, tier, model, requests) VALUES (?1,?2,?3,1) ON CONFLICT(day, tier, model) DO UPDATE SET requests = requests + 1"
|
|
24380
24414
|
).bind(day, tier, model ?? "none")
|
|
@@ -24426,6 +24460,7 @@ export {
|
|
|
24426
24460
|
ftsMatchQuery,
|
|
24427
24461
|
refCodec,
|
|
24428
24462
|
NO_CONTEXT,
|
|
24463
|
+
machineOffers,
|
|
24429
24464
|
parseContext,
|
|
24430
24465
|
namedDocumentIn,
|
|
24431
24466
|
resolveDocScope,
|
|
@@ -24434,6 +24469,7 @@ export {
|
|
|
24434
24469
|
contextNote,
|
|
24435
24470
|
syntheticUnderstanding,
|
|
24436
24471
|
portModelRunner,
|
|
24472
|
+
portStore,
|
|
24437
24473
|
promptVars,
|
|
24438
24474
|
fill,
|
|
24439
24475
|
retrievalQuery,
|
|
@@ -14,9 +14,11 @@ import {
|
|
|
14
14
|
identityNote,
|
|
15
15
|
listwiseRerank,
|
|
16
16
|
liveDataConfig,
|
|
17
|
+
machineOffers,
|
|
17
18
|
namedDocumentIn,
|
|
18
19
|
parseContext,
|
|
19
20
|
portModelRunner,
|
|
21
|
+
portStore,
|
|
20
22
|
rawSessionToken,
|
|
21
23
|
refCodec,
|
|
22
24
|
resolveDocScope,
|
|
@@ -28,14 +30,14 @@ import {
|
|
|
28
30
|
syntheticUnderstanding,
|
|
29
31
|
telemetry,
|
|
30
32
|
understandQuery
|
|
31
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-DADOI4Q6.js";
|
|
32
34
|
import {
|
|
33
35
|
corsHeaders,
|
|
34
36
|
err,
|
|
35
37
|
json,
|
|
36
38
|
readJson,
|
|
37
39
|
validateQuery
|
|
38
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-R2V3X6SQ.js";
|
|
39
41
|
import {
|
|
40
42
|
canonicalRefusal,
|
|
41
43
|
refusalAnswer
|
|
@@ -43,8 +45,9 @@ import {
|
|
|
43
45
|
import {
|
|
44
46
|
entitlementScope,
|
|
45
47
|
requestSalt,
|
|
46
|
-
resolveRequestScope
|
|
47
|
-
|
|
48
|
+
resolveRequestScope,
|
|
49
|
+
standardKeysFrom
|
|
50
|
+
} from "./chunk-4GJGBGJK.js";
|
|
48
51
|
import {
|
|
49
52
|
LIMITS,
|
|
50
53
|
MODELS,
|
|
@@ -54,7 +57,7 @@ import {
|
|
|
54
57
|
requestEffort,
|
|
55
58
|
roleModel,
|
|
56
59
|
sha256Hex
|
|
57
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-V46XM2GU.js";
|
|
58
61
|
import {
|
|
59
62
|
P
|
|
60
63
|
} from "./chunk-3FYJM7LH.js";
|
|
@@ -1196,6 +1199,11 @@ ${transcript}` }
|
|
|
1196
1199
|
}
|
|
1197
1200
|
var norm = (s) => s.toLowerCase().replace(/[^a-z0-9-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
1198
1201
|
var docNorm = (s) => norm(s).replace(/\s+/g, "");
|
|
1202
|
+
function valueTracesToUser(value, userTurns) {
|
|
1203
|
+
if (typeof value !== "string" || !value.trim()) return false;
|
|
1204
|
+
const haystack = norm(userTurns.join("\n"));
|
|
1205
|
+
return haystack.includes(norm(value));
|
|
1206
|
+
}
|
|
1199
1207
|
var DROP_REASON = "not stated in your own words";
|
|
1200
1208
|
function traceabilityGuard(extraction, userTurns) {
|
|
1201
1209
|
const haystack = norm(userTurns.join("\n"));
|
|
@@ -1385,6 +1393,263 @@ The draft opens in the real application form with every field editable \u2014 re
|
|
|
1385
1393
|
};
|
|
1386
1394
|
}
|
|
1387
1395
|
|
|
1396
|
+
// workers/worker_public/src/apicalls.ts
|
|
1397
|
+
function actsProfile() {
|
|
1398
|
+
const acts = P().publisher?.acts;
|
|
1399
|
+
return acts && typeof acts === "object" ? acts : null;
|
|
1400
|
+
}
|
|
1401
|
+
function neverOffered(path) {
|
|
1402
|
+
const prefixes = actsProfile()?.never_offer ?? [];
|
|
1403
|
+
return prefixes.some((p) => typeof p === "string" && path.startsWith(p));
|
|
1404
|
+
}
|
|
1405
|
+
async function operationById(store, id) {
|
|
1406
|
+
try {
|
|
1407
|
+
return await store.prepare("SELECT operation_id, method, path, act_class, summary, tag FROM api_ops WHERE operation_id = ?1").bind(id).first();
|
|
1408
|
+
} catch {
|
|
1409
|
+
return null;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
async function operationByMethodPath(store, method, path) {
|
|
1413
|
+
try {
|
|
1414
|
+
return await store.prepare("SELECT operation_id, method, path, act_class, summary, tag FROM api_ops WHERE method = ?1 AND path = ?2").bind(method.toLowerCase(), path).first();
|
|
1415
|
+
} catch {
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
async function preferenceOps(store) {
|
|
1420
|
+
try {
|
|
1421
|
+
const rows = await store.prepare("SELECT operation_id, method, path, act_class, summary, tag FROM api_ops WHERE act_class = 'preference' ORDER BY operation_id LIMIT 32").bind().all();
|
|
1422
|
+
return rows.results ?? [];
|
|
1423
|
+
} catch {
|
|
1424
|
+
return [];
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
function resolveEntityRoute(route) {
|
|
1428
|
+
const patterns = actsProfile()?.entity_routes ?? [];
|
|
1429
|
+
if (!route) return null;
|
|
1430
|
+
const segs = route.split("?")[0].split("/").filter(Boolean);
|
|
1431
|
+
for (const p of patterns) {
|
|
1432
|
+
const psegs = String(p.pattern ?? "").split("/").filter(Boolean);
|
|
1433
|
+
if (psegs.length !== segs.length) continue;
|
|
1434
|
+
let id = null;
|
|
1435
|
+
let ok = true;
|
|
1436
|
+
for (let i = 0; i < psegs.length; i++) {
|
|
1437
|
+
const ps = psegs[i];
|
|
1438
|
+
if (ps.startsWith(":")) {
|
|
1439
|
+
id = decodeURIComponent(segs[i]);
|
|
1440
|
+
} else if (ps !== segs[i]) {
|
|
1441
|
+
ok = false;
|
|
1442
|
+
break;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
if (ok && id && id.length <= 120 && /^[A-Za-z0-9._~-]+$/.test(id)) return { store: String(p.store), id };
|
|
1446
|
+
}
|
|
1447
|
+
return null;
|
|
1448
|
+
}
|
|
1449
|
+
var MACHINE_INTENT_RE = /\b(accept|reject|submit|resubmit|withdraw|suspend|reinstate|renew|revise|transfer|issue|register|log|record|dispatch|complete|sign|approve|verify)\b[\s\S]{0,40}\b(this|that|the|it)\b|\b(this|that|the|it)\b[\s\S]{0,40}\b(accept|reject|submit|resubmit|withdraw|suspend|reinstate|renew|revise|transfer|issue|register|log|dispatch|complete|sign|approve|verify)\b/i;
|
|
1450
|
+
var PREFERENCE_INTENT_RE = /\b(mute|unmute|unsubscribe|subscribe|notification prefs?|notification rules?|email prefs?|email preferences|saved? filters?|save (?:this|the|that) filter|notify me|stop notifying|inbox)\b/i;
|
|
1451
|
+
var QUESTION_OPENER_RE = /^\s*(?:what|how|why|where|when|which|who|whose|explain|describe|define|is|are|does|do)\b/i;
|
|
1452
|
+
function detectApiCallIntent(query, declared) {
|
|
1453
|
+
if (QUESTION_OPENER_RE.test(query)) return null;
|
|
1454
|
+
if (declared?.kind === "entity" && declared.machine && MACHINE_INTENT_RE.test(query)) return "machine_act";
|
|
1455
|
+
if (PREFERENCE_INTENT_RE.test(query)) return "preference";
|
|
1456
|
+
return null;
|
|
1457
|
+
}
|
|
1458
|
+
function pickPrompt(kind, candidates) {
|
|
1459
|
+
if (kind === "machine_act") {
|
|
1460
|
+
return {
|
|
1461
|
+
system: `You map the user's request to one machine act from the OFFERED list, or to none.
|
|
1462
|
+
|
|
1463
|
+
Rules:
|
|
1464
|
+
- Output ONLY a JSON object \u2014 no prose, no code fence: {"target": "<action id or null>", "title": "<what the act does, plain words>"}.
|
|
1465
|
+
- The target must be one of the offered action ids, copied exactly, or null when the request matches none.
|
|
1466
|
+
- The title names what the act DOES for the user ("Accept the application"), never the wire (no method, no path, no "/api/").
|
|
1467
|
+
- Never invent an act that is not listed.
|
|
1468
|
+
|
|
1469
|
+
Offered acts (action id \u2014 lands the entity on):
|
|
1470
|
+
${candidates.join("\n")}`,
|
|
1471
|
+
max: 300
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
return {
|
|
1475
|
+
system: `You map the user's request to one operation from the PREFERENCE list (the user's own settings), or to none.
|
|
1476
|
+
|
|
1477
|
+
Rules:
|
|
1478
|
+
- Output ONLY a JSON object \u2014 no prose, no code fence: {"target": "<operation id or null>", "title": "<what the act does, plain words>", "body": {...}}.
|
|
1479
|
+
- The target must be one of the listed operation ids, copied exactly, or null when the request matches none.
|
|
1480
|
+
- "body" carries only values the user stated in their own words; omit it when the request names no values.
|
|
1481
|
+
- The title names what the act DOES for the user ("Save the filter", "Mute these emails"), never the wire (no method, no path, no "/api/").
|
|
1482
|
+
- Never invent an operation that is not listed.
|
|
1483
|
+
|
|
1484
|
+
Preference operations (id \u2014 method path \u2014 summary):
|
|
1485
|
+
${candidates.join("\n")}`,
|
|
1486
|
+
max: 800
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
async function pickTarget(runner, model, kind, candidates, userTurns) {
|
|
1490
|
+
const { system, max } = pickPrompt(kind, candidates);
|
|
1491
|
+
const transcript = userTurns.map((t, i) => `${i + 1}. ${t}`).join("\n").slice(0, 8e3);
|
|
1492
|
+
try {
|
|
1493
|
+
const res = await runner.run({
|
|
1494
|
+
model,
|
|
1495
|
+
messages: [
|
|
1496
|
+
{ role: "system", content: system },
|
|
1497
|
+
{ role: "user", content: `The user's messages, oldest first:
|
|
1498
|
+
${transcript}` }
|
|
1499
|
+
],
|
|
1500
|
+
maxTokens: max,
|
|
1501
|
+
effort: "low",
|
|
1502
|
+
temperature: 0.1
|
|
1503
|
+
});
|
|
1504
|
+
const text = res.text;
|
|
1505
|
+
if (typeof text !== "string") return null;
|
|
1506
|
+
const start = text.indexOf("{");
|
|
1507
|
+
const end = text.lastIndexOf("}");
|
|
1508
|
+
if (start < 0 || end <= start) return null;
|
|
1509
|
+
const parsed = JSON.parse(text.slice(start, end + 1));
|
|
1510
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1511
|
+
return {
|
|
1512
|
+
target: typeof parsed.target === "string" && parsed.target.trim() ? parsed.target.trim().slice(0, 120) : null,
|
|
1513
|
+
title: typeof parsed.title === "string" && parsed.title.trim() ? parsed.title.trim().slice(0, 120) : void 0,
|
|
1514
|
+
body: parsed.body && typeof parsed.body === "object" ? parsed.body : void 0
|
|
1515
|
+
};
|
|
1516
|
+
} catch {
|
|
1517
|
+
return null;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
function honestTitle(proposed, fallback) {
|
|
1521
|
+
const t = (proposed ?? "").trim();
|
|
1522
|
+
if (!t) return fallback;
|
|
1523
|
+
if (/\b(GET|POST|PUT|PATCH|DELETE)\b/.test(t) || t.includes("/api/") || t.includes("{")) return fallback;
|
|
1524
|
+
return t.slice(0, 120);
|
|
1525
|
+
}
|
|
1526
|
+
function bodyTraces(body, userTurns) {
|
|
1527
|
+
if (body == null) return true;
|
|
1528
|
+
if (typeof body === "boolean" || typeof body === "number") return true;
|
|
1529
|
+
if (typeof body === "string") return valueTracesToUser(body, userTurns);
|
|
1530
|
+
if (Array.isArray(body)) return body.every((v) => bodyTraces(v, userTurns));
|
|
1531
|
+
if (typeof body === "object") return Object.values(body).every((v) => bodyTraces(v, userTurns));
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1534
|
+
function refusal2(reason, answer) {
|
|
1535
|
+
return { status: "refused", reason, answer };
|
|
1536
|
+
}
|
|
1537
|
+
async function prepareApiCall(env, opts) {
|
|
1538
|
+
if (!opts.member) {
|
|
1539
|
+
return refusal2(
|
|
1540
|
+
"sign_in_required",
|
|
1541
|
+
"Preparing an act starts from your own account \u2014 sign in and ask again. The draft would still be yours alone: the card asks for your confirmation, and only your own tap executes it \u2014 I never hold a write credential."
|
|
1542
|
+
);
|
|
1543
|
+
}
|
|
1544
|
+
const acts = actsProfile();
|
|
1545
|
+
const store = portStore(env);
|
|
1546
|
+
const runner = portModelRunner(env);
|
|
1547
|
+
const userTurns = [...opts.history.filter((h) => h.role === "user").map((h) => h.content), opts.query];
|
|
1548
|
+
if (opts.intent === "machine_act") {
|
|
1549
|
+
const machine = opts.declared?.machine;
|
|
1550
|
+
const label = opts.declared?.label || "this record";
|
|
1551
|
+
if (!machine) {
|
|
1552
|
+
return refusal2("no_matching_act", "This page did not declare the record's lifecycle state, so I cannot propose an act on it honestly. Open the record itself and ask again, or act from the platform's own controls \u2014 they always show exactly what your role may do.");
|
|
1553
|
+
}
|
|
1554
|
+
if (machine.acts.length === 0) {
|
|
1555
|
+
return refusal2(
|
|
1556
|
+
"machine_read_only",
|
|
1557
|
+
`The record's lifecycle is at ${machine.state}, and the machine offers your role no acts there \u2014 you can read this record but not act on it. Nothing was drafted. If that looks wrong, an officer of your organization can check the role your account carries.`
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
const entity = resolveEntityRoute(opts.declared?.route);
|
|
1561
|
+
if (!entity) {
|
|
1562
|
+
return refusal2("entity_unresolved", "I cannot tell which record the act would land on from this page, so nothing was drafted. The platform's own controls on the record always act on the right one.");
|
|
1563
|
+
}
|
|
1564
|
+
const candidates2 = machine.acts.map((a) => `${a.action} \u2014 lands on ${a.to}${a.guard ? ` (requires ${a.guard})` : ""}`);
|
|
1565
|
+
const pick2 = await pickTarget(runner, opts.model, "machine_act", candidates2, userTurns);
|
|
1566
|
+
if (!pick2) {
|
|
1567
|
+
return refusal2("pick_failed", "I could not map your request to an act reliably just now \u2014 nothing was drafted. Ask again in a moment, or act from the platform's own controls.");
|
|
1568
|
+
}
|
|
1569
|
+
if (!pick2.target || !machineOffers(machine, pick2.target)) {
|
|
1570
|
+
const offered = machine.acts.map((a) => a.action).join(", ");
|
|
1571
|
+
return refusal2(
|
|
1572
|
+
"no_matching_act",
|
|
1573
|
+
`None of the acts the machine offers your role at ${machine.state} matches that request \u2014 the offered set is exactly: ${offered}. I never propose an act outside it. Nothing was drafted.`
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
const act = machine.acts.find((a) => a.action === pick2.target);
|
|
1577
|
+
const write = acts?.entity_write;
|
|
1578
|
+
if (!write?.method || !write?.path) {
|
|
1579
|
+
return refusal2("operation_unknown", "This deployment has not wired the entity write operation, so I cannot prepare the act here.");
|
|
1580
|
+
}
|
|
1581
|
+
const op2 = await operationByMethodPath(store, write.method, write.path);
|
|
1582
|
+
if (!op2) {
|
|
1583
|
+
return refusal2("operation_unknown", "The act's operation is not in this deployment's API directory, so I cannot prepare it honestly \u2014 nothing was drafted.");
|
|
1584
|
+
}
|
|
1585
|
+
if (neverOffered(op2.path)) {
|
|
1586
|
+
return refusal2("operation_never_offered", "That act is not one the assistant may propose \u2014 nothing was drafted.");
|
|
1587
|
+
}
|
|
1588
|
+
const path = write.path.replace("{store}", entity.store).replace("{id}", entity.id);
|
|
1589
|
+
const title2 = honestTitle(pick2.title, `${act.action} \u2014 ${label}`.slice(0, 120));
|
|
1590
|
+
const notes = [
|
|
1591
|
+
`The machine act ${act.action} moves the record from ${machine.state} to ${act.to}; the platform re-judges the act against the machine and your role when you confirm.`,
|
|
1592
|
+
...act.guard ? [`This act declares the ${act.guard} input \u2014 the confirmation card collects it; the draft carries none.`] : []
|
|
1593
|
+
];
|
|
1594
|
+
const draft2 = {
|
|
1595
|
+
kind: "draft",
|
|
1596
|
+
act: "api_call",
|
|
1597
|
+
version: 1,
|
|
1598
|
+
title: title2,
|
|
1599
|
+
prepared_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1600
|
+
requires_confirmation: true,
|
|
1601
|
+
call: { method: write.method, path, body: { status: act.to } },
|
|
1602
|
+
notes
|
|
1603
|
+
};
|
|
1604
|
+
const answer2 = `I've prepared the act "${title2}" for ${label}: the machine act ${act.action}, from ${machine.state} to ${act.to}.
|
|
1605
|
+
|
|
1606
|
+
The confirmation card carries it \u2014 a record act, so your own confirmation signs it and the platform's gates judge the write as you. I never hold a write credential, and I never propose an act the machine does not offer your role.`;
|
|
1607
|
+
return { status: "draft", draft: draft2, answer: answer2 };
|
|
1608
|
+
}
|
|
1609
|
+
const family = (await preferenceOps(store)).filter((o) => !neverOffered(o.path));
|
|
1610
|
+
if (!family.length) {
|
|
1611
|
+
return refusal2("operation_unknown", "This deployment's API directory lists no preference operations, so I cannot prepare that here \u2014 nothing was drafted.");
|
|
1612
|
+
}
|
|
1613
|
+
const candidates = family.map((o) => `${o.operation_id} \u2014 ${o.method.toUpperCase()} ${o.path} \u2014 ${o.summary ?? ""}`.slice(0, 300));
|
|
1614
|
+
const pick = await pickTarget(runner, opts.model, "preference", candidates, userTurns);
|
|
1615
|
+
if (!pick) {
|
|
1616
|
+
return refusal2("pick_failed", "I could not map your request to a preference act reliably just now \u2014 nothing was drafted. Ask again in a moment, or change the setting on your account page directly.");
|
|
1617
|
+
}
|
|
1618
|
+
if (!pick.target) {
|
|
1619
|
+
return refusal2("no_matching_act", "None of the preference acts I may propose matches that request \u2014 nothing was drafted. Your account's settings page carries the full set.");
|
|
1620
|
+
}
|
|
1621
|
+
const op = await operationById(store, pick.target);
|
|
1622
|
+
if (!op || op.act_class !== "preference") {
|
|
1623
|
+
return refusal2("operation_unknown", "I cannot match that request to a preference act the platform declares \u2014 nothing was drafted.");
|
|
1624
|
+
}
|
|
1625
|
+
if (neverOffered(op.path)) {
|
|
1626
|
+
return refusal2("operation_never_offered", "That act is not one the assistant may propose \u2014 delegation and sign-in acts stay in your own hands on the settings page. Nothing was drafted.");
|
|
1627
|
+
}
|
|
1628
|
+
if (pick.body !== void 0 && !bodyTraces(pick.body, userTurns)) {
|
|
1629
|
+
return refusal2(
|
|
1630
|
+
"body_untraced",
|
|
1631
|
+
"I can't prepare that change because the values I would set are not ones you stated \u2014 I never invent settings. Tell me exactly what to set, in your own words, and I'll prepare the draft."
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
const title = honestTitle(pick.title, (op.summary ?? op.operation_id).slice(0, 120));
|
|
1635
|
+
const draft = {
|
|
1636
|
+
kind: "draft",
|
|
1637
|
+
act: "api_call",
|
|
1638
|
+
version: 1,
|
|
1639
|
+
title,
|
|
1640
|
+
prepared_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1641
|
+
requires_confirmation: true,
|
|
1642
|
+
call: { method: op.method.toUpperCase(), path: op.path, ...pick.body !== void 0 ? { body: pick.body } : {} },
|
|
1643
|
+
notes: [
|
|
1644
|
+
"A preference act: with your standing grant the card executes it straight away; without one, your own tap on the card confirms it. The grant is yours on the settings page \u2014 granted or revoked by your hand, never by a message."
|
|
1645
|
+
]
|
|
1646
|
+
};
|
|
1647
|
+
const answer = `I've prepared the act "${title}" \u2014 a preference act on your own settings.
|
|
1648
|
+
|
|
1649
|
+
With your standing grant it executes under the grant's authority; without one, the card asks once and your tap confirms it. The platform re-checks the act against its own specification either way, and the grant never covers record acts.`;
|
|
1650
|
+
return { status: "draft", draft, answer };
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1388
1653
|
// workers/worker_public/src/memories.ts
|
|
1389
1654
|
var MAX_FILES = 10;
|
|
1390
1655
|
var MAX_CONTENT = 8e3;
|
|
@@ -1612,7 +1877,7 @@ async function* sseTokens(stream) {
|
|
|
1612
1877
|
}
|
|
1613
1878
|
async function handleAsk(env, ctx, req, tier, key) {
|
|
1614
1879
|
const tStart = Date.now();
|
|
1615
|
-
const telemetryMeta = () => ({ durationMs: Date.now() - tStart, keyId: key?.id ?? null });
|
|
1880
|
+
const telemetryMeta = () => ({ durationMs: Date.now() - tStart, keyId: key?.id ?? null, retries: generateRetries });
|
|
1616
1881
|
const stageTiming = {};
|
|
1617
1882
|
let generateRetries = 0;
|
|
1618
1883
|
const readAs = () => understanding ? {
|
|
@@ -1629,6 +1894,7 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
1629
1894
|
if (!q) return err(400, "invalid_input", `query is required (1-${LIMITS.maxInputChars} chars)`);
|
|
1630
1895
|
const declaredCtx = parseContext(body);
|
|
1631
1896
|
const draftAct = P().publisher.features?.drafts ? detectDraftIntent(q.query) : null;
|
|
1897
|
+
const apiCallIntent = !draftAct && P().publisher.features?.api_call_drafts ? detectApiCallIntent(q.query, declaredCtx) : null;
|
|
1632
1898
|
const member = tier === "member" ? await sessionFrom(req, env) : null;
|
|
1633
1899
|
const effort = requestEffort(env, member, body?.effort);
|
|
1634
1900
|
const limit = tier === "key" ? key.day_limit : tier === "member" || member ? num(env, "MEMBER_DAY_ASK", 300) : num(env, "ANON_DAY_ASK", 20);
|
|
@@ -1649,14 +1915,15 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
1649
1915
|
cookie: req.headers.get("cookie") ?? "",
|
|
1650
1916
|
authorization: req.headers.get("authorization") ?? ""
|
|
1651
1917
|
};
|
|
1652
|
-
const
|
|
1918
|
+
const keyLicensed = tier === "key" && standardKeysFrom(body).size > 0;
|
|
1919
|
+
const scope = resolveRequestScope(body, member, { keyWithEntitlements: keyLicensed });
|
|
1653
1920
|
if ("error" in scope) return err(400, "invalid_input", "datasets: at least one dataset must stay enabled");
|
|
1654
1921
|
const { corpora, narrowed, isoOn } = scope;
|
|
1655
1922
|
const standardKeys = entitlementScope(scope.standardKeys);
|
|
1656
1923
|
const [memNote, memoryUsed] = member && scope.memoryIds.length ? await memoryNote(env, member.sub, scope.memoryIds) : [null, []];
|
|
1657
1924
|
const requestSaltStr = requestSalt(scope, memoryUsed);
|
|
1658
1925
|
const salt = requestSaltStr ? `${requestSaltStr}|effort:${effort}` : `effort:${effort}`;
|
|
1659
|
-
const federate = member && service && isoOn ? (q2) => retrieveInternal(service, fedAuth, q2) : void 0;
|
|
1926
|
+
const federate = (member || keyLicensed) && service && isoOn ? (q2) => retrieveInternal(service, fedAuth, q2) : void 0;
|
|
1660
1927
|
const ns = tier === "key" ? `k:${key.id}` : member ? `m:${member.sub}` : "anon";
|
|
1661
1928
|
const model = member ? MODELS.member : MODELS.anon;
|
|
1662
1929
|
const prev = typeof body?.prev === "string" ? body.prev.slice(0, 800) : void 0;
|
|
@@ -1673,7 +1940,7 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
1673
1940
|
let retrieved;
|
|
1674
1941
|
const fresh = freshRequested(body);
|
|
1675
1942
|
const gen = await corpusGen(env.CACHE);
|
|
1676
|
-
const cached = fresh || contextual || declaredCtx || draftAct || userImage ? null : await cacheGet(env, gen, ns, q.query, q.lang, salt);
|
|
1943
|
+
const cached = fresh || contextual || declaredCtx || draftAct || apiCallIntent || userImage ? null : await cacheGet(env, gen, ns, q.query, q.lang, salt);
|
|
1677
1944
|
const wantsStream = body?.stream === true || tier === "anon" && body?.stream !== false;
|
|
1678
1945
|
if (cached) {
|
|
1679
1946
|
telemetry(env, ctx, tier, "ask", null, true, (cached.value.answer ?? "").length, cached.value.query_hash, q.lang, "exact", telemetryMeta());
|
|
@@ -1697,7 +1964,7 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
1697
1964
|
}
|
|
1698
1965
|
let understanding = null;
|
|
1699
1966
|
const nodeScoped = !!modelNodeRefIn(q.query) || !!modelNodeRefIn(declaredCtx?.label);
|
|
1700
|
-
if (!cached && !nodeScoped && !contextual && !declaredCtx && !draftAct && !q.lang && !userImage && !fresh) {
|
|
1967
|
+
if (!cached && !nodeScoped && !contextual && !declaredCtx && !draftAct && !apiCallIntent && !q.lang && !userImage && !fresh) {
|
|
1701
1968
|
const wv0 = await warmEmbed ?? null;
|
|
1702
1969
|
if (wv0) {
|
|
1703
1970
|
const sc0 = await semanticCacheGet(env, gen, wv0, salt);
|
|
@@ -1795,7 +2062,7 @@ async function handleAsk(env, ctx, req, tier, key) {
|
|
|
1795
2062
|
console.log("understand:", understanding?.intent ?? "null", understanding?.doc_number ? `doc#${understanding.doc_number}${understanding.edition ? "@" + understanding.edition : ""}` : "nodoc", "|", q.query.slice(0, 50));
|
|
1796
2063
|
const graphDocNumbers = await graphExpand(env, understanding);
|
|
1797
2064
|
const eNote = await editionNote(env, understanding);
|
|
1798
|
-
if (understanding?.intent !== "conversational" && !nodeScoped && !contextual && !declaredCtx && !draftAct && !userImage && !fresh) {
|
|
2065
|
+
if (understanding?.intent !== "conversational" && !nodeScoped && !contextual && !declaredCtx && !draftAct && !apiCallIntent && !userImage && !fresh) {
|
|
1799
2066
|
const warmVec = await warmEmbed ?? null;
|
|
1800
2067
|
if (warmVec) {
|
|
1801
2068
|
const sc = await semanticCacheGet(env, gen, warmVec, salt);
|
|
@@ -1900,6 +2167,33 @@ ${summary}` }] : [],
|
|
|
1900
2167
|
}
|
|
1901
2168
|
return json({ answer: verdict.answer, citations: citations2, model, query_hash: queryHash2, follow_ups: [], context_applied: draftCtxApplied, read: readAs(), ...draftPayload ? { draft: draftPayload } : {}, quota });
|
|
1902
2169
|
}
|
|
2170
|
+
if (apiCallIntent) {
|
|
2171
|
+
const callCtxApplied = declaredCtx ? appliedContext(declaredCtx, null) : NO_CONTEXT;
|
|
2172
|
+
const queryHash2 = await sha256Hex(q.query);
|
|
2173
|
+
const verdict = await prepareApiCall(env, {
|
|
2174
|
+
intent: apiCallIntent,
|
|
2175
|
+
query: q.query,
|
|
2176
|
+
history: keptHistory,
|
|
2177
|
+
member,
|
|
2178
|
+
declared: declaredCtx,
|
|
2179
|
+
model: roleModel(env, "acts")
|
|
2180
|
+
});
|
|
2181
|
+
console.log("api_call draft:", apiCallIntent, "\u2192", verdict.status === "draft" ? `draft (${verdict.draft.call.method} ${verdict.draft.call.path})` : `refused (${verdict.reason})`);
|
|
2182
|
+
const draftPayload = verdict.status === "draft" ? verdict.draft : void 0;
|
|
2183
|
+
telemetry(env, ctx, tier, "ask", model, true, verdict.answer.length, queryHash2, q.lang, void 0, telemetryMeta());
|
|
2184
|
+
if (wantsStream) {
|
|
2185
|
+
return sseResponse(
|
|
2186
|
+
[
|
|
2187
|
+
...readAs() ? [{ type: "read", read: readAs() }] : [],
|
|
2188
|
+
{ type: "citations", citations: [], context_applied: callCtxApplied, ...draftPayload ? { draft: draftPayload } : {}, quota },
|
|
2189
|
+
{ type: "token", v: verdict.answer },
|
|
2190
|
+
{ type: "done", model, query_hash: queryHash2, context_applied: callCtxApplied, read: readAs() }
|
|
2191
|
+
],
|
|
2192
|
+
corsHeaders(req)
|
|
2193
|
+
);
|
|
2194
|
+
}
|
|
2195
|
+
return json({ answer: verdict.answer, citations: [], model, query_hash: queryHash2, follow_ups: [], context_applied: callCtxApplied, read: readAs(), ...draftPayload ? { draft: draftPayload } : {}, quota });
|
|
2196
|
+
}
|
|
1903
2197
|
let liveRecords;
|
|
1904
2198
|
let accountNote;
|
|
1905
2199
|
const modelDocHint = named ?? docScope ?? namedDocumentIn(q.query);
|