@buoy-gg/agent-core 7.0.35 → 7.0.38
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/README.md +21 -14
- package/lib/commonjs/blocks/receipts.js +57 -1
- package/lib/commonjs/blocks/receipts.js.map +1 -1
- package/lib/commonjs/blocks/types.js +23 -2
- package/lib/commonjs/blocks/types.js.map +1 -1
- package/lib/commonjs/blocks/uiTool.js +423 -10
- package/lib/commonjs/blocks/uiTool.js.map +1 -1
- package/lib/commonjs/catalog/catalog.g.js +213 -48
- package/lib/commonjs/catalog/catalog.g.js.map +1 -1
- package/lib/commonjs/catalog/catalog.source.json +226 -48
- package/lib/commonjs/catalog/catalog.types.g.js +44 -0
- package/lib/commonjs/catalog/catalog.types.g.js.map +1 -0
- package/lib/commonjs/catalog/normalizeParams.js +333 -0
- package/lib/commonjs/catalog/normalizeParams.js.map +1 -0
- package/lib/commonjs/catalog/signature.js +68 -0
- package/lib/commonjs/catalog/signature.js.map +1 -0
- package/lib/commonjs/catalog/snapshotReads.js +13 -6
- package/lib/commonjs/catalog/snapshotReads.js.map +1 -1
- package/lib/commonjs/catalog/toProviderTools.js +44 -6
- package/lib/commonjs/catalog/toProviderTools.js.map +1 -1
- package/lib/commonjs/context/buildContextPack.js +367 -19
- package/lib/commonjs/context/buildContextPack.js.map +1 -1
- package/lib/commonjs/effects/digest.js +34 -0
- package/lib/commonjs/effects/digest.js.map +1 -0
- package/lib/commonjs/effects/ledger.js +98 -2
- package/lib/commonjs/effects/ledger.js.map +1 -1
- package/lib/commonjs/engine/askGate.js +287 -0
- package/lib/commonjs/engine/askGate.js.map +1 -0
- package/lib/commonjs/engine/effectFor.js +82 -1
- package/lib/commonjs/engine/effectFor.js.map +1 -1
- package/lib/commonjs/engine/evidence.js +113 -0
- package/lib/commonjs/engine/evidence.js.map +1 -0
- package/lib/commonjs/engine/historyBudget.js +363 -0
- package/lib/commonjs/engine/historyBudget.js.map +1 -0
- package/lib/commonjs/engine/retrieve.js +214 -0
- package/lib/commonjs/engine/retrieve.js.map +1 -0
- package/lib/commonjs/engine/runAgentTurn.js +1231 -120
- package/lib/commonjs/engine/runAgentTurn.js.map +1 -1
- package/lib/commonjs/engine/systemPrompt.js +103 -9
- package/lib/commonjs/engine/systemPrompt.js.map +1 -1
- package/lib/commonjs/engine/textToolCalls.js +276 -0
- package/lib/commonjs/engine/textToolCalls.js.map +1 -0
- package/lib/commonjs/engine/tokenCalibration.js +81 -0
- package/lib/commonjs/engine/tokenCalibration.js.map +1 -0
- package/lib/commonjs/engine/verify.js +300 -0
- package/lib/commonjs/engine/verify.js.map +1 -0
- package/lib/commonjs/index.js +74 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/policy/labels.js +19 -3
- package/lib/commonjs/policy/labels.js.map +1 -1
- package/lib/commonjs/policy/policy.js +6 -1
- package/lib/commonjs/policy/policy.js.map +1 -1
- package/lib/commonjs/policy/redact.js +42 -5
- package/lib/commonjs/policy/redact.js.map +1 -1
- package/lib/commonjs/providers/anthropic.js +340 -208
- package/lib/commonjs/providers/anthropic.js.map +1 -1
- package/lib/commonjs/providers/openai.js +237 -103
- package/lib/commonjs/providers/openai.js.map +1 -1
- package/lib/commonjs/providers/problem.js +98 -0
- package/lib/commonjs/providers/problem.js.map +1 -0
- package/lib/commonjs/providers/sse.js +170 -34
- package/lib/commonjs/providers/sse.js.map +1 -1
- package/lib/commonjs/providers/streamTimer.js +123 -0
- package/lib/commonjs/providers/streamTimer.js.map +1 -0
- package/lib/commonjs/providers/transport.js +123 -0
- package/lib/commonjs/providers/transport.js.map +1 -0
- package/lib/commonjs/providers/xhrStream.js +212 -0
- package/lib/commonjs/providers/xhrStream.js.map +1 -0
- package/lib/commonjs/session.js +242 -58
- package/lib/commonjs/session.js.map +1 -1
- package/lib/module/blocks/receipts.js +57 -1
- package/lib/module/blocks/receipts.js.map +1 -1
- package/lib/module/blocks/types.js +23 -2
- package/lib/module/blocks/types.js.map +1 -1
- package/lib/module/blocks/uiTool.js +421 -10
- package/lib/module/blocks/uiTool.js.map +1 -1
- package/lib/module/catalog/catalog.g.js +213 -48
- package/lib/module/catalog/catalog.g.js.map +1 -1
- package/lib/module/catalog/catalog.source.json +226 -48
- package/lib/module/catalog/catalog.types.g.js +40 -0
- package/lib/module/catalog/catalog.types.g.js.map +1 -0
- package/lib/module/catalog/normalizeParams.js +327 -0
- package/lib/module/catalog/normalizeParams.js.map +1 -0
- package/lib/module/catalog/signature.js +63 -0
- package/lib/module/catalog/signature.js.map +1 -0
- package/lib/module/catalog/snapshotReads.js +13 -6
- package/lib/module/catalog/snapshotReads.js.map +1 -1
- package/lib/module/catalog/toProviderTools.js +44 -6
- package/lib/module/catalog/toProviderTools.js.map +1 -1
- package/lib/module/context/buildContextPack.js +366 -19
- package/lib/module/context/buildContextPack.js.map +1 -1
- package/lib/module/effects/digest.js +29 -0
- package/lib/module/effects/digest.js.map +1 -0
- package/lib/module/effects/ledger.js +98 -2
- package/lib/module/effects/ledger.js.map +1 -1
- package/lib/module/engine/askGate.js +280 -0
- package/lib/module/engine/askGate.js.map +1 -0
- package/lib/module/engine/effectFor.js +83 -1
- package/lib/module/engine/effectFor.js.map +1 -1
- package/lib/module/engine/evidence.js +107 -0
- package/lib/module/engine/evidence.js.map +1 -0
- package/lib/module/engine/historyBudget.js +355 -0
- package/lib/module/engine/historyBudget.js.map +1 -0
- package/lib/module/engine/retrieve.js +210 -0
- package/lib/module/engine/retrieve.js.map +1 -0
- package/lib/module/engine/runAgentTurn.js +1227 -121
- package/lib/module/engine/runAgentTurn.js.map +1 -1
- package/lib/module/engine/systemPrompt.js +101 -9
- package/lib/module/engine/systemPrompt.js.map +1 -1
- package/lib/module/engine/textToolCalls.js +270 -0
- package/lib/module/engine/textToolCalls.js.map +1 -0
- package/lib/module/engine/tokenCalibration.js +75 -0
- package/lib/module/engine/tokenCalibration.js.map +1 -0
- package/lib/module/engine/verify.js +292 -0
- package/lib/module/engine/verify.js.map +1 -0
- package/lib/module/index.js +7 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/policy/labels.js +19 -3
- package/lib/module/policy/labels.js.map +1 -1
- package/lib/module/policy/policy.js +6 -1
- package/lib/module/policy/policy.js.map +1 -1
- package/lib/module/policy/redact.js +42 -5
- package/lib/module/policy/redact.js.map +1 -1
- package/lib/module/providers/anthropic.js +341 -209
- package/lib/module/providers/anthropic.js.map +1 -1
- package/lib/module/providers/openai.js +238 -104
- package/lib/module/providers/openai.js.map +1 -1
- package/lib/module/providers/problem.js +92 -0
- package/lib/module/providers/problem.js.map +1 -0
- package/lib/module/providers/sse.js +166 -34
- package/lib/module/providers/sse.js.map +1 -1
- package/lib/module/providers/streamTimer.js +118 -0
- package/lib/module/providers/streamTimer.js.map +1 -0
- package/lib/module/providers/transport.js +119 -0
- package/lib/module/providers/transport.js.map +1 -0
- package/lib/module/providers/xhrStream.js +207 -0
- package/lib/module/providers/xhrStream.js.map +1 -0
- package/lib/module/session.js +226 -60
- package/lib/module/session.js.map +1 -1
- package/lib/typescript/blocks/receipts.d.ts +5 -0
- package/lib/typescript/blocks/receipts.d.ts.map +1 -1
- package/lib/typescript/blocks/types.d.ts +23 -2
- package/lib/typescript/blocks/types.d.ts.map +1 -1
- package/lib/typescript/blocks/uiTool.d.ts +11 -2
- package/lib/typescript/blocks/uiTool.d.ts.map +1 -1
- package/lib/typescript/catalog/catalog.g.d.ts +2 -2
- package/lib/typescript/catalog/catalog.g.d.ts.map +1 -1
- package/lib/typescript/catalog/catalog.types.g.d.ts +40 -0
- package/lib/typescript/catalog/catalog.types.g.d.ts.map +1 -0
- package/lib/typescript/catalog/normalizeParams.d.ts +43 -0
- package/lib/typescript/catalog/normalizeParams.d.ts.map +1 -0
- package/lib/typescript/catalog/signature.d.ts +29 -0
- package/lib/typescript/catalog/signature.d.ts.map +1 -0
- package/lib/typescript/catalog/snapshotReads.d.ts.map +1 -1
- package/lib/typescript/catalog/toProviderTools.d.ts +28 -15
- package/lib/typescript/catalog/toProviderTools.d.ts.map +1 -1
- package/lib/typescript/context/buildContextPack.d.ts +24 -2
- package/lib/typescript/context/buildContextPack.d.ts.map +1 -1
- package/lib/typescript/effects/digest.d.ts +10 -0
- package/lib/typescript/effects/digest.d.ts.map +1 -0
- package/lib/typescript/effects/ledger.d.ts +73 -0
- package/lib/typescript/effects/ledger.d.ts.map +1 -1
- package/lib/typescript/engine/askGate.d.ts +29 -0
- package/lib/typescript/engine/askGate.d.ts.map +1 -0
- package/lib/typescript/engine/effectFor.d.ts +6 -1
- package/lib/typescript/engine/effectFor.d.ts.map +1 -1
- package/lib/typescript/engine/evidence.d.ts +67 -0
- package/lib/typescript/engine/evidence.d.ts.map +1 -0
- package/lib/typescript/engine/historyBudget.d.ts +115 -0
- package/lib/typescript/engine/historyBudget.d.ts.map +1 -0
- package/lib/typescript/engine/retrieve.d.ts +32 -0
- package/lib/typescript/engine/retrieve.d.ts.map +1 -0
- package/lib/typescript/engine/runAgentTurn.d.ts +179 -3
- package/lib/typescript/engine/runAgentTurn.d.ts.map +1 -1
- package/lib/typescript/engine/systemPrompt.d.ts +80 -0
- package/lib/typescript/engine/systemPrompt.d.ts.map +1 -1
- package/lib/typescript/engine/textToolCalls.d.ts +58 -0
- package/lib/typescript/engine/textToolCalls.d.ts.map +1 -0
- package/lib/typescript/engine/tokenCalibration.d.ts +46 -0
- package/lib/typescript/engine/tokenCalibration.d.ts.map +1 -0
- package/lib/typescript/engine/verify.d.ts +67 -0
- package/lib/typescript/engine/verify.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +7 -3
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/policy/labels.d.ts.map +1 -1
- package/lib/typescript/policy/policy.d.ts.map +1 -1
- package/lib/typescript/policy/redact.d.ts.map +1 -1
- package/lib/typescript/providers/anthropic.d.ts.map +1 -1
- package/lib/typescript/providers/openai.d.ts +0 -16
- package/lib/typescript/providers/openai.d.ts.map +1 -1
- package/lib/typescript/providers/problem.d.ts +50 -0
- package/lib/typescript/providers/problem.d.ts.map +1 -0
- package/lib/typescript/providers/sse.d.ts +86 -2
- package/lib/typescript/providers/sse.d.ts.map +1 -1
- package/lib/typescript/providers/streamTimer.d.ts +59 -0
- package/lib/typescript/providers/streamTimer.d.ts.map +1 -0
- package/lib/typescript/providers/transport.d.ts +48 -0
- package/lib/typescript/providers/transport.d.ts.map +1 -0
- package/lib/typescript/providers/types.d.ts +105 -3
- package/lib/typescript/providers/types.d.ts.map +1 -1
- package/lib/typescript/providers/xhrStream.d.ts +45 -0
- package/lib/typescript/providers/xhrStream.d.ts.map +1 -0
- package/lib/typescript/session.d.ts +55 -4
- package/lib/typescript/session.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +6 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/package.json +10 -10
- package/LICENSE +0 -58
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.SALVAGE_NOTE = void 0;
|
|
7
|
+
exports.couldBeToolCallEnvelope = couldBeToolCallEnvelope;
|
|
8
|
+
exports.parseTextToolCalls = parseTextToolCalls;
|
|
9
|
+
var _toProviderTools = require("../catalog/toProviderTools");
|
|
10
|
+
var _uiTool = require("../blocks/uiTool");
|
|
11
|
+
var _runId = require("../blocks/runId");
|
|
12
|
+
/**
|
|
13
|
+
* Recovering a tool call a model wrote as TEXT instead of calling.
|
|
14
|
+
*
|
|
15
|
+
* Every provider here is sent real tool definitions, and most models use them.
|
|
16
|
+
* Some don't — under a gateway, after a clarifying question, or just on the
|
|
17
|
+
* wrong roll, a model answers with the call spelled out as JSON:
|
|
18
|
+
*
|
|
19
|
+
* ```json
|
|
20
|
+
* {"calls":[{"tool":"buoy_zustand","action":"setState","params":{…}}],
|
|
21
|
+
* "reply":"Done — I added one Jigglypuff to your bag."}
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* Left alone this is the worst outcome the product has: the user said "yes,
|
|
25
|
+
* go ahead", nothing happened, and what they got back was a wall of JSON
|
|
26
|
+
* claiming it did. The same class of problem as the parameter aliases in
|
|
27
|
+
* normalizeParams — models reach for a convention they know, and meeting them
|
|
28
|
+
* costs one parse.
|
|
29
|
+
*
|
|
30
|
+
* Two halves, and the first is why this file exists at all:
|
|
31
|
+
*
|
|
32
|
+
* 1. {@link couldBeToolCallEnvelope} runs on every delta, because the decision
|
|
33
|
+
* has to be made BEFORE the text reaches the screen. Streamed text cannot
|
|
34
|
+
* be taken back. It withholds only what could still be an envelope and
|
|
35
|
+
* releases the moment it can't be.
|
|
36
|
+
* 2. {@link parseTextToolCalls} turns the finished blob into ordinary calls,
|
|
37
|
+
* which then go through validation, the policy, and approval exactly like a
|
|
38
|
+
* native one. Nothing here decides that a call is allowed to run — a
|
|
39
|
+
* recovered destructive call still stops at the same approval sheet.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
const id = (0, _runId.idFactory)("txt");
|
|
43
|
+
|
|
44
|
+
/** `{`, optionally inside a ```json fence. */
|
|
45
|
+
const OPENER = /^(?:```[a-zA-Z]*[ \t]*\r?\n?)?\{/;
|
|
46
|
+
/** A fence still arriving a backtick at a time: "`", "``", "```json\n". */
|
|
47
|
+
const PARTIAL_OPENER = /^`{1,3}[a-zA-Z]*\s*$/;
|
|
48
|
+
/**
|
|
49
|
+
* The keys an envelope names almost immediately.
|
|
50
|
+
*
|
|
51
|
+
* Deliberately excludes `name` and `action`: a response body the user asked to
|
|
52
|
+
* see routinely has a `name` field, and holding those back would make every
|
|
53
|
+
* JSON answer in the product stutter to catch a shape no model emits.
|
|
54
|
+
*/
|
|
55
|
+
const ENVELOPE_KEY = /"(calls|tool_calls|toolCalls|tool|function)"/;
|
|
56
|
+
/**
|
|
57
|
+
* How far past the opening brace to keep withholding before an envelope has to
|
|
58
|
+
* have named itself. Long enough for `{\n "calls": [` with any indentation,
|
|
59
|
+
* short enough that a JSON answer the user actually asked for barely stutters.
|
|
60
|
+
*/
|
|
61
|
+
const KEY_WINDOW = 80;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Could the text so far still turn out to be a tool-call envelope?
|
|
65
|
+
*
|
|
66
|
+
* False the instant it can't be, so an ordinary answer streams exactly as it
|
|
67
|
+
* did before — the window this holds is one opening brace plus a key name.
|
|
68
|
+
*/
|
|
69
|
+
function couldBeToolCallEnvelope(text) {
|
|
70
|
+
const t = text.trimStart();
|
|
71
|
+
if (t.length === 0) return true;
|
|
72
|
+
if (PARTIAL_OPENER.test(t)) return true;
|
|
73
|
+
const opener = OPENER.exec(t);
|
|
74
|
+
if (!opener) return false;
|
|
75
|
+
const body = t.slice(opener[0].length);
|
|
76
|
+
return body.length <= KEY_WINDOW || ENVELOPE_KEY.test(body);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The JSON object inside the text, fence and trailing prose removed — as up
|
|
81
|
+
* to two readings, tried in order.
|
|
82
|
+
*
|
|
83
|
+
* The first version cut at the FIRST closing fence. Seen live: an envelope
|
|
84
|
+
* whose `reply` carried its own ```json block — the bag line the user had
|
|
85
|
+
* asked for — was cut off inside that string, failed to parse, and the raw
|
|
86
|
+
* blob became the answer. So: (1) cut at the LAST fence, which is the
|
|
87
|
+
* envelope's own closer when the text is fenced; (2) ignore fences entirely
|
|
88
|
+
* and take everything up to the last "}". Whichever parses wins.
|
|
89
|
+
*/
|
|
90
|
+
function jsonBodies(text) {
|
|
91
|
+
const t = text.trim();
|
|
92
|
+
const unfenced = t.startsWith("```") ? t.replace(/^```[a-zA-Z]*[ \t]*\r?\n?/, "") : t;
|
|
93
|
+
const close = unfenced.lastIndexOf("```");
|
|
94
|
+
const candidates = [close >= 0 ? unfenced.slice(0, close) : unfenced, unfenced];
|
|
95
|
+
const out = [];
|
|
96
|
+
for (const raw of candidates) {
|
|
97
|
+
const c = raw.trim();
|
|
98
|
+
if (!c.startsWith("{")) continue;
|
|
99
|
+
const end = c.lastIndexOf("}");
|
|
100
|
+
if (end === -1) continue;
|
|
101
|
+
const body = c.slice(0, end + 1);
|
|
102
|
+
if (!out.includes(body)) out.push(body);
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* A last-resort parse for JSON a model got slightly wrong.
|
|
109
|
+
*
|
|
110
|
+
* The envelope that produced this file was not valid JSON. The model wrote
|
|
111
|
+
* `…"unitPrice":999}}}}],"reply":…` — four closing braces where five were
|
|
112
|
+
* needed, so the call object was never closed and `]` arrived while still
|
|
113
|
+
* inside it. Strict parsing rejects the whole thing, and rejecting it hands
|
|
114
|
+
* the user the blob and no action: exactly the failure this file exists to
|
|
115
|
+
* stop, and it survived the first version of it. A model that writes its call
|
|
116
|
+
* as prose is already off the rails; expecting it to balance twelve nested
|
|
117
|
+
* braces by hand is expecting the wrong thing.
|
|
118
|
+
*
|
|
119
|
+
* The repair is mechanical and changes no values: a container is auto-closed
|
|
120
|
+
* when a mismatched closer arrives, a stray closer with nothing open is
|
|
121
|
+
* dropped, and anything still open at the end is closed. Whatever comes out
|
|
122
|
+
* still has to READ as an envelope naming a tool this app has, and every call
|
|
123
|
+
* it yields goes through validation, the policy and approval. A repaired call
|
|
124
|
+
* is not a trusted one — it is only a legible one.
|
|
125
|
+
*/
|
|
126
|
+
function repairJson(text) {
|
|
127
|
+
const out = [];
|
|
128
|
+
const stack = [];
|
|
129
|
+
let inString = false;
|
|
130
|
+
let escaped = false;
|
|
131
|
+
for (const ch of text) {
|
|
132
|
+
if (inString) {
|
|
133
|
+
out.push(ch);
|
|
134
|
+
if (escaped) escaped = false;else if (ch === "\\") escaped = true;else if (ch === '"') inString = false;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (ch === '"') {
|
|
138
|
+
inString = true;
|
|
139
|
+
out.push(ch);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (ch === "{" || ch === "[") {
|
|
143
|
+
stack.push(ch);
|
|
144
|
+
out.push(ch);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (ch === "}" || ch === "]") {
|
|
148
|
+
const want = ch === "}" ? "{" : "[";
|
|
149
|
+
let i = stack.length - 1;
|
|
150
|
+
while (i >= 0 && stack[i] !== want) i--;
|
|
151
|
+
// Nothing this could be closing. Dropping it is the only repair that
|
|
152
|
+
// does not invent structure.
|
|
153
|
+
if (i < 0) continue;
|
|
154
|
+
while (stack.length - 1 > i) out.push(stack.pop() === "{" ? "}" : "]");
|
|
155
|
+
stack.pop();
|
|
156
|
+
out.push(ch);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
out.push(ch);
|
|
160
|
+
}
|
|
161
|
+
if (inString) out.push('"');
|
|
162
|
+
while (stack.length) out.push(stack.pop() === "{" ? "}" : "]");
|
|
163
|
+
return out.join("");
|
|
164
|
+
}
|
|
165
|
+
const rec = v => v && typeof v === "object" && !Array.isArray(v) ? v : undefined;
|
|
166
|
+
const str = v => typeof v === "string" && v.length > 0 ? v : undefined;
|
|
167
|
+
function tryParse(text) {
|
|
168
|
+
try {
|
|
169
|
+
return JSON.parse(text);
|
|
170
|
+
} catch {
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** `params` may arrive as an object or, in the OpenAI convention, as a string. */
|
|
176
|
+
function asParams(v) {
|
|
177
|
+
const direct = rec(v);
|
|
178
|
+
if (direct) return direct;
|
|
179
|
+
const s = str(v);
|
|
180
|
+
if (!s) return undefined;
|
|
181
|
+
try {
|
|
182
|
+
return rec(JSON.parse(s));
|
|
183
|
+
} catch {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Name it the way the provider would have, so the rest of the loop can't tell
|
|
190
|
+
* — or nothing, if it names no tool this app has.
|
|
191
|
+
*
|
|
192
|
+
* This is the guard that keeps ordinary JSON out. `{"name":"Bulbasaur", …}` is
|
|
193
|
+
* a Pokémon the user asked to see, not a call, and the only reliable way to
|
|
194
|
+
* tell is that no tool is called Bulbasaur.
|
|
195
|
+
*/
|
|
196
|
+
function providerName(name, catalog) {
|
|
197
|
+
if (name === _uiTool.UI_TOOL_NAME) return name;
|
|
198
|
+
if (catalog.some(t => t.toolId === name)) return (0, _toProviderTools.toProviderToolName)(name);
|
|
199
|
+
if (catalog.some(t => (0, _toProviderTools.toProviderToolName)(t.toolId) === name)) return name;
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
function toCall(entry, catalog) {
|
|
203
|
+
const e = rec(entry);
|
|
204
|
+
if (!e) return undefined;
|
|
205
|
+
// `function` is the OpenAI nesting; `input` is Anthropic's.
|
|
206
|
+
const fn = rec(e.function);
|
|
207
|
+
const inner = rec(e.input) ?? rec(e.arguments) ?? fn;
|
|
208
|
+
const named = str(e.tool) ?? str(e.name) ?? str(fn?.name) ?? str(e.toolName);
|
|
209
|
+
const name = named ? providerName(named, catalog) : undefined;
|
|
210
|
+
if (!name) return undefined;
|
|
211
|
+
const action = str(e.action) ?? str(inner?.action);
|
|
212
|
+
// Every real action is named. Requiring it keeps a JSON object that merely
|
|
213
|
+
// happens to share a word with a tool from being run as one.
|
|
214
|
+
if (!action && name !== _uiTool.UI_TOOL_NAME) return undefined;
|
|
215
|
+
const params = asParams(e.params) ?? asParams(inner?.params) ?? asParams(fn?.arguments) ?? asParams(e.arguments) ?? {};
|
|
216
|
+
return {
|
|
217
|
+
id: id(),
|
|
218
|
+
name,
|
|
219
|
+
input: {
|
|
220
|
+
...(action ? {
|
|
221
|
+
action
|
|
222
|
+
} : {}),
|
|
223
|
+
params
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Read a text envelope as tool calls, or nothing if that is not what it is.
|
|
229
|
+
*
|
|
230
|
+
* Deliberately strict about the SHAPE and loose about the spelling: it will
|
|
231
|
+
* not turn arbitrary JSON into calls (a model showing the user a response body
|
|
232
|
+
* must still be shown), but it accepts every naming convention a model might
|
|
233
|
+
* carry over — `tool`/`name`, `params`/`input`/`arguments`, one call or many.
|
|
234
|
+
*/
|
|
235
|
+
function parseTextToolCalls(text, catalog) {
|
|
236
|
+
const bodies = jsonBodies(text);
|
|
237
|
+
if (bodies.length === 0) return undefined;
|
|
238
|
+
// Strict first, on every reading; the repair only ever sees JSON that
|
|
239
|
+
// already failed every strict attempt.
|
|
240
|
+
let parsed;
|
|
241
|
+
for (const body of bodies) if ((parsed = tryParse(body)) !== undefined) break;
|
|
242
|
+
if (parsed === undefined) for (const body of bodies) if ((parsed = tryParse(repairJson(body))) !== undefined) break;
|
|
243
|
+
const env = rec(parsed);
|
|
244
|
+
if (!env) return undefined;
|
|
245
|
+
const list = env.calls ?? env.tool_calls ?? env.toolCalls;
|
|
246
|
+
const entries = Array.isArray(list) ? list : env.tool || env.name ? [env] : undefined;
|
|
247
|
+
if (!entries) return undefined;
|
|
248
|
+
const reply = str(env.reply) ?? str(env.text) ?? str(env.message) ?? "";
|
|
249
|
+
|
|
250
|
+
// An envelope with NO calls is the model wrapping a plain answer in the
|
|
251
|
+
// shape it uses for calls. Seen live: `{"calls":[],"reply":"Here is the
|
|
252
|
+
// full Poké Mart menu…"}` in a ```json fence, with a whole markdown table
|
|
253
|
+
// inside the string — and the screen showed the blob, because "nothing to
|
|
254
|
+
// salvage" used to mean "show the text as-is". There is nothing to run, but
|
|
255
|
+
// the reply is the answer and the wrapper is not. Show the words.
|
|
256
|
+
if (entries.length === 0) return reply ? {
|
|
257
|
+
calls: [],
|
|
258
|
+
reply
|
|
259
|
+
} : undefined;
|
|
260
|
+
const calls = entries.map(e => toCall(e, catalog)).filter(Boolean);
|
|
261
|
+
// Every entry has to read as a call. A partial parse would run half of what
|
|
262
|
+
// the model described and silently drop the rest, which is worse than
|
|
263
|
+
// running none of it.
|
|
264
|
+
if (calls.length === 0 || calls.length !== entries.length) return undefined;
|
|
265
|
+
return {
|
|
266
|
+
calls,
|
|
267
|
+
reply
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Appended to a recovered call's result, so the model corrects itself for the
|
|
273
|
+
* rest of the conversation instead of writing the next one as text too.
|
|
274
|
+
*/
|
|
275
|
+
const SALVAGE_NOTE = exports.SALVAGE_NOTE = "\n\n[You wrote this call as JSON in your reply instead of calling the tool. It was recovered and run this time. Call tools through the tool interface — text is shown to the user as-is, and a call written there does not run.]";
|
|
276
|
+
//# sourceMappingURL=textToolCalls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_toProviderTools","require","_uiTool","_runId","id","idFactory","OPENER","PARTIAL_OPENER","ENVELOPE_KEY","KEY_WINDOW","couldBeToolCallEnvelope","text","t","trimStart","length","test","opener","exec","body","slice","jsonBodies","trim","unfenced","startsWith","replace","close","lastIndexOf","candidates","out","raw","c","end","includes","push","repairJson","stack","inString","escaped","ch","want","i","pop","join","rec","v","Array","isArray","undefined","str","tryParse","JSON","parse","asParams","direct","s","providerName","name","catalog","UI_TOOL_NAME","some","toolId","toProviderToolName","toCall","entry","e","fn","function","inner","input","arguments","named","tool","toolName","action","params","parseTextToolCalls","bodies","parsed","env","list","calls","tool_calls","toolCalls","entries","reply","message","map","filter","Boolean","SALVAGE_NOTE","exports"],"sourceRoot":"../../../src","sources":["engine/textToolCalls.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.TokenCalibration = exports.DEFAULT_CHARS_PER_TOKEN = void 0;
|
|
7
|
+
exports.promptTokensOf = promptTokensOf;
|
|
8
|
+
/**
|
|
9
|
+
* How many characters make a token — measured, not assumed.
|
|
10
|
+
*
|
|
11
|
+
* Every budget in this engine is kept in characters because that is what a
|
|
12
|
+
* string has, but every LIMIT that matters is in tokens: the provider's
|
|
13
|
+
* context window, the reply room, the cost. The conversion was a constant
|
|
14
|
+
* (4) everywhere. English prose does run near 4; the tool results this agent
|
|
15
|
+
* actually sends are JSON, which tokenizes nearer 3 — so a request the budget
|
|
16
|
+
* called 100k tokens could be 130k, on a gateway whose window is 128k. The
|
|
17
|
+
* proactive budget was optimistic in exactly the sessions it exists for.
|
|
18
|
+
*
|
|
19
|
+
* The provider tells us the truth after every request: its usage report
|
|
20
|
+
* carries the prompt's token count, and the engine knows how many characters
|
|
21
|
+
* it sent. One division gives the ratio for THIS conversation on THIS
|
|
22
|
+
* tokenizer; an exponential average smooths a single odd request (a round
|
|
23
|
+
* that was all prose, or all ids). Strands' design 0008 does the same in
|
|
24
|
+
* reverse — a known token baseline plus an estimate of the delta — and its
|
|
25
|
+
* decision record says why: public limits should be in LLM-native units.
|
|
26
|
+
*
|
|
27
|
+
* Clamped, because a wrong sample must not be able to blow the budget wide
|
|
28
|
+
* open (a gateway that reports nonsense usage) or shrink it to nothing.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const DEFAULT_CHARS_PER_TOKEN = exports.DEFAULT_CHARS_PER_TOKEN = 4;
|
|
32
|
+
const MIN_CHARS_PER_TOKEN = 2.5;
|
|
33
|
+
const MAX_CHARS_PER_TOKEN = 5;
|
|
34
|
+
/** Weight of the newest sample once there is history. The first sample replaces the default outright. */
|
|
35
|
+
const EMA_ALPHA = 0.3;
|
|
36
|
+
/** Below this many prompt tokens a sample says more about rounding than about the tokenizer. */
|
|
37
|
+
const MIN_SAMPLE_TOKENS = 500;
|
|
38
|
+
class TokenCalibration {
|
|
39
|
+
ratio = DEFAULT_CHARS_PER_TOKEN;
|
|
40
|
+
samples = 0;
|
|
41
|
+
|
|
42
|
+
/** Characters per token, as currently believed. */
|
|
43
|
+
get charsPerToken() {
|
|
44
|
+
return this.ratio;
|
|
45
|
+
}
|
|
46
|
+
get sampleCount() {
|
|
47
|
+
return this.samples;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Fold in one request: what was sent, and what the provider counted. */
|
|
51
|
+
observe(requestChars, promptTokens) {
|
|
52
|
+
if (!(requestChars > 0) || !(promptTokens >= MIN_SAMPLE_TOKENS)) return;
|
|
53
|
+
const sample = Math.min(MAX_CHARS_PER_TOKEN, Math.max(MIN_CHARS_PER_TOKEN, requestChars / promptTokens));
|
|
54
|
+
this.ratio = this.samples === 0 ? sample : this.ratio + EMA_ALPHA * (sample - this.ratio);
|
|
55
|
+
this.samples += 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Characters a token budget allows, at the believed ratio. */
|
|
59
|
+
chars(tokens) {
|
|
60
|
+
return Math.floor(tokens * this.ratio);
|
|
61
|
+
}
|
|
62
|
+
reset() {
|
|
63
|
+
this.ratio = DEFAULT_CHARS_PER_TOKEN;
|
|
64
|
+
this.samples = 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The whole prompt's token count from a usage report.
|
|
70
|
+
*
|
|
71
|
+
* Anthropic reports the cached and freshly-written prefix SEPARATELY from
|
|
72
|
+
* `input`; OpenAI-shaped providers fold `cached_tokens` INTO `prompt_tokens`.
|
|
73
|
+
* See TokenUsage. Adding on the wrong protocol double-counts the cache and
|
|
74
|
+
* halves the believed ratio.
|
|
75
|
+
*/
|
|
76
|
+
exports.TokenCalibration = TokenCalibration;
|
|
77
|
+
function promptTokensOf(usage, protocol) {
|
|
78
|
+
if (protocol === "anthropic") return usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
|
|
79
|
+
return usage.input;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=tokenCalibration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["DEFAULT_CHARS_PER_TOKEN","exports","MIN_CHARS_PER_TOKEN","MAX_CHARS_PER_TOKEN","EMA_ALPHA","MIN_SAMPLE_TOKENS","TokenCalibration","ratio","samples","charsPerToken","sampleCount","observe","requestChars","promptTokens","sample","Math","min","max","chars","tokens","floor","reset","promptTokensOf","usage","protocol","input","cacheRead","cacheWrite"],"sourceRoot":"../../../src","sources":["engine/tokenCalibration.ts"],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGO,MAAMA,uBAAuB,GAAAC,OAAA,CAAAD,uBAAA,GAAG,CAAC;AACxC,MAAME,mBAAmB,GAAG,GAAG;AAC/B,MAAMC,mBAAmB,GAAG,CAAC;AAC7B;AACA,MAAMC,SAAS,GAAG,GAAG;AACrB;AACA,MAAMC,iBAAiB,GAAG,GAAG;AAEtB,MAAMC,gBAAgB,CAAC;EACpBC,KAAK,GAAGP,uBAAuB;EAC/BQ,OAAO,GAAG,CAAC;;EAEnB;EACA,IAAIC,aAAaA,CAAA,EAAW;IAC1B,OAAO,IAAI,CAACF,KAAK;EACnB;EAEA,IAAIG,WAAWA,CAAA,EAAW;IACxB,OAAO,IAAI,CAACF,OAAO;EACrB;;EAEA;EACAG,OAAOA,CAACC,YAAoB,EAAEC,YAAoB,EAAQ;IACxD,IAAI,EAAED,YAAY,GAAG,CAAC,CAAC,IAAI,EAAEC,YAAY,IAAIR,iBAAiB,CAAC,EAAE;IACjE,MAAMS,MAAM,GAAGC,IAAI,CAACC,GAAG,CAACb,mBAAmB,EAAEY,IAAI,CAACE,GAAG,CAACf,mBAAmB,EAAEU,YAAY,GAAGC,YAAY,CAAC,CAAC;IACxG,IAAI,CAACN,KAAK,GAAG,IAAI,CAACC,OAAO,KAAK,CAAC,GAAGM,MAAM,GAAG,IAAI,CAACP,KAAK,GAAGH,SAAS,IAAIU,MAAM,GAAG,IAAI,CAACP,KAAK,CAAC;IACzF,IAAI,CAACC,OAAO,IAAI,CAAC;EACnB;;EAEA;EACAU,KAAKA,CAACC,MAAc,EAAU;IAC5B,OAAOJ,IAAI,CAACK,KAAK,CAACD,MAAM,GAAG,IAAI,CAACZ,KAAK,CAAC;EACxC;EAEAc,KAAKA,CAAA,EAAS;IACZ,IAAI,CAACd,KAAK,GAAGP,uBAAuB;IACpC,IAAI,CAACQ,OAAO,GAAG,CAAC;EAClB;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAPAP,OAAA,CAAAK,gBAAA,GAAAA,gBAAA;AAQO,SAASgB,cAAcA,CAACC,KAAiB,EAAEC,QAAgC,EAAU;EAC1F,IAAIA,QAAQ,KAAK,WAAW,EAAE,OAAOD,KAAK,CAACE,KAAK,IAAIF,KAAK,CAACG,SAAS,IAAI,CAAC,CAAC,IAAIH,KAAK,CAACI,UAAU,IAAI,CAAC,CAAC;EACnG,OAAOJ,KAAK,CAACE,KAAK;AACpB","ignoreList":[]}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.VERIFIERS = void 0;
|
|
7
|
+
exports.atPath = atPath;
|
|
8
|
+
exports.isSubset = isSubset;
|
|
9
|
+
exports.verificationTrailer = verificationTrailer;
|
|
10
|
+
exports.verifyOutcome = verifyOutcome;
|
|
11
|
+
/**
|
|
12
|
+
* Outcome verification: after a write lands, check that what the user asked
|
|
13
|
+
* for actually happened — not just that the adapter said ok.
|
|
14
|
+
*
|
|
15
|
+
* "Install override → `{ok:true}`" was reported as done. But installed ≠ a
|
|
16
|
+
* request has used it ≠ the cart shows it, and each step can succeed while
|
|
17
|
+
* the outcome fails: the app had the old response cached, the screen kept
|
|
18
|
+
* saying In Stock, and the reply said "the item is now sold out". Strands'
|
|
19
|
+
* GoalLoop frames this as a completion criterion with bounded correction; the
|
|
20
|
+
* criterion borrowed here is DETERMINISTIC — a read-back the engine performs
|
|
21
|
+
* with the same tools the model has — not a second model judging the reply.
|
|
22
|
+
*
|
|
23
|
+
* Three outcomes, each honest about what it is:
|
|
24
|
+
*
|
|
25
|
+
* - `verified` — the read-back shows the requested state.
|
|
26
|
+
* - `unverified` — the write landed but the read-back cannot yet show the
|
|
27
|
+
* outcome (an override is installed but no request has
|
|
28
|
+
* matched it). Reported as exactly that, never as proof.
|
|
29
|
+
* - `failed` — the read-back contradicts the write. The model is told
|
|
30
|
+
* what was found and asked for ONE correction through the
|
|
31
|
+
* same policy and ledger path; never to repeat the write.
|
|
32
|
+
*
|
|
33
|
+
* Bounded by construction: each verifier makes a few reads, polls for at most
|
|
34
|
+
* `waitMs` (small, per action), and stops on the turn's abort signal. It never
|
|
35
|
+
* dispatches a write, and its reads are not stashed as evidence. Undefined
|
|
36
|
+
* means "no opinion" — nothing is appended, which is what every action
|
|
37
|
+
* without a verifier gets.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
const rec = v => v && typeof v === "object" && !Array.isArray(v) ? v : undefined;
|
|
41
|
+
function sleep(ms, signal) {
|
|
42
|
+
return new Promise(resolve => {
|
|
43
|
+
if (signal?.aborted) return resolve();
|
|
44
|
+
const t = setTimeout(done, ms);
|
|
45
|
+
function done() {
|
|
46
|
+
clearTimeout(t);
|
|
47
|
+
signal?.removeEventListener("abort", done);
|
|
48
|
+
resolve();
|
|
49
|
+
}
|
|
50
|
+
signal?.addEventListener("abort", done, {
|
|
51
|
+
once: true
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Poll `read` every `everyMs` until `until` says so or `waitMs` is spent. Returns the last read. */
|
|
57
|
+
async function poll(read, until, waitMs, everyMs, signal) {
|
|
58
|
+
const deadline = Date.now() + waitMs;
|
|
59
|
+
for (;;) {
|
|
60
|
+
const value = await read();
|
|
61
|
+
if (until(value)) return {
|
|
62
|
+
value,
|
|
63
|
+
satisfied: true
|
|
64
|
+
};
|
|
65
|
+
if (signal?.aborted || Date.now() + everyMs > deadline) return {
|
|
66
|
+
value,
|
|
67
|
+
satisfied: false
|
|
68
|
+
};
|
|
69
|
+
await sleep(everyMs, signal);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Every key in `want` present in `have` with an equal (deep) value. Arrays compare whole. */
|
|
74
|
+
function isSubset(want, have) {
|
|
75
|
+
if (want === null || typeof want !== "object" || Array.isArray(want)) return JSON.stringify(want) === JSON.stringify(have);
|
|
76
|
+
const h = rec(have);
|
|
77
|
+
if (!h) return false;
|
|
78
|
+
for (const [k, v] of Object.entries(want)) {
|
|
79
|
+
if (!(k in h)) return false;
|
|
80
|
+
if (!isSubset(v, h[k])) return false;
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The adapters' path grammar — dotted keys, `[0]`, and the id-addressed
|
|
87
|
+
* `[field=value]` the prompt recommends for list edits ("lines[lineId=seed-1].qty").
|
|
88
|
+
* A mirror of shared/sync/valuePath.ts, kept here because agent-core imports
|
|
89
|
+
* nothing. The first live run wrote exactly that form and this resolver, then
|
|
90
|
+
* dotted-only, reported a landed edit as "did not take".
|
|
91
|
+
*/
|
|
92
|
+
function atPath(root, path) {
|
|
93
|
+
const segs = path.replace(/^\/+/, "").match(/\[[^\]]*\]|[^.[\]/]+/g) ?? [];
|
|
94
|
+
let cur = root;
|
|
95
|
+
for (const seg of segs) {
|
|
96
|
+
if (cur === null || typeof cur !== "object") return undefined;
|
|
97
|
+
if (seg.startsWith("[")) {
|
|
98
|
+
const inner = seg.slice(1, -1).trim();
|
|
99
|
+
if (!Array.isArray(cur)) return undefined;
|
|
100
|
+
const eq = inner.indexOf("=");
|
|
101
|
+
if (eq < 0) {
|
|
102
|
+
cur = cur[Number(inner)];
|
|
103
|
+
} else {
|
|
104
|
+
const field = inner.slice(0, eq).trim();
|
|
105
|
+
const value = inner.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
|
|
106
|
+
cur = cur.find(el => el && typeof el === "object" && String(el[field]) === value);
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
cur = cur[seg];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return cur;
|
|
113
|
+
}
|
|
114
|
+
const brief = v => {
|
|
115
|
+
const s = JSON.stringify(v);
|
|
116
|
+
return s === undefined ? "undefined" : s.length > 80 ? `${s.slice(0, 77)}…` : s;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// ── the verifiers ────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
const storageSetItem = async ({
|
|
122
|
+
params,
|
|
123
|
+
dispatch
|
|
124
|
+
}) => {
|
|
125
|
+
if (typeof params.key !== "string") return undefined;
|
|
126
|
+
const back = await dispatch("storage", "async.getItem", {
|
|
127
|
+
key: params.key
|
|
128
|
+
});
|
|
129
|
+
const value = typeof back === "string" ? back : rec(back)?.value;
|
|
130
|
+
const wanted = typeof params.value === "string" ? params.value : JSON.stringify(params.value);
|
|
131
|
+
if (value === wanted) return {
|
|
132
|
+
status: "verified",
|
|
133
|
+
detail: `Read back: "${params.key}" now holds the written value.`
|
|
134
|
+
};
|
|
135
|
+
return {
|
|
136
|
+
status: "failed",
|
|
137
|
+
detail: `Read back after the write: "${params.key}" holds ${brief(value)}, not what was written. The write did not take.`
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
const zustandSetState = async ({
|
|
141
|
+
params,
|
|
142
|
+
dispatch,
|
|
143
|
+
after
|
|
144
|
+
}) => {
|
|
145
|
+
if (typeof params.storeName !== "string") return undefined;
|
|
146
|
+
const back = rec(after !== undefined ? after : await dispatch("zustand", "getStoreState", {
|
|
147
|
+
storeName: params.storeName
|
|
148
|
+
}));
|
|
149
|
+
if (!back || back.found === false) return {
|
|
150
|
+
status: "failed",
|
|
151
|
+
detail: `Read back: store "${params.storeName}" could not be read after the write.`
|
|
152
|
+
};
|
|
153
|
+
const current = back.currentState;
|
|
154
|
+
if (typeof params.path === "string") {
|
|
155
|
+
const got = atPath(current, params.path);
|
|
156
|
+
if (JSON.stringify(got) === JSON.stringify(params.value)) return {
|
|
157
|
+
status: "verified",
|
|
158
|
+
detail: `Read back: ${params.storeName}.${params.path} is now ${brief(got)}.`
|
|
159
|
+
};
|
|
160
|
+
return {
|
|
161
|
+
status: "failed",
|
|
162
|
+
detail: `Read back: ${params.storeName}.${params.path} is ${brief(got)}, not ${brief(params.value)}. The store did not take the write.`
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const want = rec(params.state);
|
|
166
|
+
if (!want) return undefined;
|
|
167
|
+
if (isSubset(want, current)) return {
|
|
168
|
+
status: "verified",
|
|
169
|
+
detail: `Read back: "${params.storeName}" now holds the written fields (${Object.keys(want).join(", ")}).`
|
|
170
|
+
};
|
|
171
|
+
const wrong = Object.keys(want).filter(k => !isSubset(want[k], rec(current)?.[k]));
|
|
172
|
+
return {
|
|
173
|
+
status: "failed",
|
|
174
|
+
detail: `Read back: "${params.storeName}" does not hold the written value for ${wrong.join(", ")}. The store did not take the write — read it before deciding what to do.`
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
const querySetQueryData = async ({
|
|
178
|
+
params,
|
|
179
|
+
dispatch,
|
|
180
|
+
after
|
|
181
|
+
}) => {
|
|
182
|
+
const key = params.queryHash !== undefined ? {
|
|
183
|
+
queryHash: params.queryHash
|
|
184
|
+
} : params.queryKey !== undefined ? {
|
|
185
|
+
queryKey: params.queryKey
|
|
186
|
+
} : undefined;
|
|
187
|
+
if (!key) return undefined;
|
|
188
|
+
const back = rec(after !== undefined ? after : await dispatch("query", "getQueryData", key));
|
|
189
|
+
if (!back || back.found === false) return {
|
|
190
|
+
status: "failed",
|
|
191
|
+
detail: "Read back: the query could not be read after the write."
|
|
192
|
+
};
|
|
193
|
+
const data = back.data;
|
|
194
|
+
if (typeof params.path === "string") {
|
|
195
|
+
const got = atPath(data, params.path);
|
|
196
|
+
if (JSON.stringify(got) === JSON.stringify(params.value)) return {
|
|
197
|
+
status: "verified",
|
|
198
|
+
detail: `Read back: the cached ${params.path} is now ${brief(got)}.`
|
|
199
|
+
};
|
|
200
|
+
return {
|
|
201
|
+
status: "failed",
|
|
202
|
+
detail: `Read back: the cached ${params.path} is ${brief(got)}, not ${brief(params.value)}. The cache did not take the write — the app may have refetched over it.`
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (params.data === undefined) return undefined;
|
|
206
|
+
if (isSubset(params.data, data)) return {
|
|
207
|
+
status: "verified",
|
|
208
|
+
detail: "Read back: the cache now holds the written data."
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
status: "failed",
|
|
212
|
+
detail: "Read back: the cache does not hold the written data — the app may have refetched over it, or the shape differed."
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
const navigate = async ({
|
|
216
|
+
params,
|
|
217
|
+
dispatch,
|
|
218
|
+
signal
|
|
219
|
+
}) => {
|
|
220
|
+
if (typeof params.path !== "string") return undefined;
|
|
221
|
+
const wanted = params.path.split("?")[0];
|
|
222
|
+
const {
|
|
223
|
+
value,
|
|
224
|
+
satisfied
|
|
225
|
+
} = await poll(async () => rec(await dispatch("route-events", "getCurrentRoute", {})), r => typeof r?.path === "string" && (r.path === wanted || r.path.split("?")[0] === wanted), 2_000, 250, signal);
|
|
226
|
+
if (satisfied) return {
|
|
227
|
+
status: "verified",
|
|
228
|
+
detail: `The app is now on ${wanted}.`
|
|
229
|
+
};
|
|
230
|
+
const at = typeof value?.path === "string" ? value.path : "an unknown route";
|
|
231
|
+
return {
|
|
232
|
+
status: "failed",
|
|
233
|
+
detail: `Two seconds after navigating, the app is on ${at}, not ${wanted}. Do not navigate again blindly — check the route exists (the sitemap) and whether something redirected.`
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
const upsertOverrideRule = async ({
|
|
237
|
+
params,
|
|
238
|
+
result,
|
|
239
|
+
dispatch,
|
|
240
|
+
signal
|
|
241
|
+
}) => {
|
|
242
|
+
const id = rec(rec(result)?.rule)?.id ?? rec(params.rule)?.id;
|
|
243
|
+
if (typeof id !== "string") return undefined;
|
|
244
|
+
const find = async () => {
|
|
245
|
+
const list = rec(await dispatch("network", "listOverrideRules", {}));
|
|
246
|
+
const rules = Array.isArray(list?.rules) ? list.rules : [];
|
|
247
|
+
return rules.map(rec).find(r => r?.id === id);
|
|
248
|
+
};
|
|
249
|
+
const {
|
|
250
|
+
value,
|
|
251
|
+
satisfied
|
|
252
|
+
} = await poll(find, r => typeof r?.hits === "number" && r.hits > 0, 3_000, 500, signal);
|
|
253
|
+
if (!value) return {
|
|
254
|
+
status: "failed",
|
|
255
|
+
detail: "The rule is not in the override list after the write. It did not install."
|
|
256
|
+
};
|
|
257
|
+
if (value.enabled === false) return {
|
|
258
|
+
status: "failed",
|
|
259
|
+
detail: "The rule is installed but disabled, so it will not fire."
|
|
260
|
+
};
|
|
261
|
+
if (satisfied) return {
|
|
262
|
+
status: "verified",
|
|
263
|
+
detail: `The rule has matched ${value.hits} request${value.hits === 1 ? "" : "s"} — the app has already received the overridden response.`
|
|
264
|
+
};
|
|
265
|
+
return {
|
|
266
|
+
status: "unverified",
|
|
267
|
+
detail: "The rule is installed, but no request has matched it yet — the app is still showing whatever it loaded before. A refetch or a visit to the screen that fetches this will exercise it; until then, do not say the screen shows the override."
|
|
268
|
+
};
|
|
269
|
+
};
|
|
270
|
+
const VERIFIERS = exports.VERIFIERS = {
|
|
271
|
+
"storage.async.setItem": storageSetItem,
|
|
272
|
+
"zustand.setState": zustandSetState,
|
|
273
|
+
"query.setQueryData": querySetQueryData,
|
|
274
|
+
"route-events.navigate": navigate,
|
|
275
|
+
"network.upsertOverrideRule": upsertOverrideRule
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/** Run the verifier for this action, if there is one. Never throws: a read that blows up is "no opinion". */
|
|
279
|
+
async function verifyOutcome(input) {
|
|
280
|
+
const verifier = VERIFIERS[`${input.toolId}.${input.action}`];
|
|
281
|
+
if (!verifier) return undefined;
|
|
282
|
+
try {
|
|
283
|
+
return await verifier(input);
|
|
284
|
+
} catch {
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** The trailer the model reads. Kept in the `[Buoy] ` trailer form the bench's parser splits on. */
|
|
290
|
+
function verificationTrailer(v) {
|
|
291
|
+
switch (v.status) {
|
|
292
|
+
case "verified":
|
|
293
|
+
return `\n\n[Buoy] Verified: ${v.detail}`;
|
|
294
|
+
case "unverified":
|
|
295
|
+
return `\n\n[Buoy] Unverified: ${v.detail}`;
|
|
296
|
+
case "failed":
|
|
297
|
+
return `\n\n[Buoy] Check FAILED: ${v.detail} You may correct this ONCE — read the current state first, then make a different, targeted change; never repeat the same write and never claim the outcome happened.`;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=verify.js.map
|