sitelooper 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +625 -0
- package/bin/sitelooper.js +6 -0
- package/dist/agent/llm.js +460 -0
- package/dist/agent/llm.js.map +1 -0
- package/dist/agent/loop.js +870 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/prompt.js +40 -0
- package/dist/agent/prompt.js.map +1 -0
- package/dist/agent/report.js +545 -0
- package/dist/agent/report.js.map +1 -0
- package/dist/agent/tools.js +1147 -0
- package/dist/agent/tools.js.map +1 -0
- package/dist/cli.js +1692 -0
- package/dist/cli.js.map +1 -0
- package/dist/daemon/browser.js +218 -0
- package/dist/daemon/browser.js.map +1 -0
- package/dist/daemon/codegen.js +241 -0
- package/dist/daemon/codegen.js.map +1 -0
- package/dist/daemon/dialogs.js +57 -0
- package/dist/daemon/dialogs.js.map +1 -0
- package/dist/daemon/diff.js +198 -0
- package/dist/daemon/diff.js.map +1 -0
- package/dist/daemon/fingerprint.js +98 -0
- package/dist/daemon/fingerprint.js.map +1 -0
- package/dist/daemon/inputs.js +134 -0
- package/dist/daemon/inputs.js.map +1 -0
- package/dist/daemon/recorder.js +1232 -0
- package/dist/daemon/recorder.js.map +1 -0
- package/dist/daemon/refs.js +194 -0
- package/dist/daemon/refs.js.map +1 -0
- package/dist/daemon/server.js +1724 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/state.js +239 -0
- package/dist/daemon/state.js.map +1 -0
- package/dist/doctor.js +90 -0
- package/dist/doctor.js.map +1 -0
- package/dist/shared/paths.js +80 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/shared/protocol.js +28 -0
- package/dist/shared/protocol.js.map +1 -0
- package/dist/shared/secrets.js +92 -0
- package/dist/shared/secrets.js.map +1 -0
- package/dist/shared/text.js +39 -0
- package/dist/shared/text.js.map +1 -0
- package/dist/skills/compile.js +1420 -0
- package/dist/skills/compile.js.map +1 -0
- package/dist/skills/components.js +456 -0
- package/dist/skills/components.js.map +1 -0
- package/dist/skills/flow.js +1041 -0
- package/dist/skills/flow.js.map +1 -0
- package/dist/skills/learn.js +406 -0
- package/dist/skills/learn.js.map +1 -0
- package/dist/skills/ledger.js +304 -0
- package/dist/skills/ledger.js.map +1 -0
- package/dist/skills/relabel.js +206 -0
- package/dist/skills/relabel.js.map +1 -0
- package/dist/skills/repair.js +570 -0
- package/dist/skills/repair.js.map +1 -0
- package/dist/skills/replay.js +1281 -0
- package/dist/skills/replay.js.map +1 -0
- package/dist/skills/store.js +147 -0
- package/dist/skills/store.js.map +1 -0
- package/dist/spec/check.js +428 -0
- package/dist/spec/check.js.map +1 -0
- package/dist/spec/diagnostics.js +58 -0
- package/dist/spec/diagnostics.js.map +1 -0
- package/dist/spec/emit.js +2084 -0
- package/dist/spec/emit.js.map +1 -0
- package/dist/spec/index.js +62 -0
- package/dist/spec/index.js.map +1 -0
- package/dist/spec/ir.js +216 -0
- package/dist/spec/ir.js.map +1 -0
- package/dist/spec/lift.js +162 -0
- package/dist/spec/lift.js.map +1 -0
- package/dist/spec/locators.js +270 -0
- package/dist/spec/locators.js.map +1 -0
- package/dist/spec/lower.js +124 -0
- package/dist/spec/lower.js.map +1 -0
- package/dist/spec/repair.js +657 -0
- package/dist/spec/repair.js.map +1 -0
- package/dist/spec/rerecord.js +169 -0
- package/dist/spec/rerecord.js.map +1 -0
- package/dist/spec/rethread.js +120 -0
- package/dist/spec/rethread.js.map +1 -0
- package/package.json +50 -0
- package/skills/sitelooper/SKILL.md +228 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The RunLedger: one registry for everything a RUN made, as opposed to what
|
|
3
|
+
* the app provides.
|
|
4
|
+
*
|
|
5
|
+
* Seven defects in six cloud takes were the same bug wearing different
|
|
6
|
+
* clothes — a value or a procedure from the recording run surviving into a
|
|
7
|
+
* replay: a read pinned to row 1 publishing a seed record's ref; a runid
|
|
8
|
+
* baked into a locator's hasText; a dashboard uid embedded 62 times in skill
|
|
9
|
+
* templates; a three-character record id below a minting floor. Each was
|
|
10
|
+
* fixed where it was found, and the next take produced a fresh instance,
|
|
11
|
+
* because the rule they all violate had no home: recognition was
|
|
12
|
+
* re-implemented per call site (three copies of the same length gate), and
|
|
13
|
+
* relied on text matching against whatever the current instruction happened
|
|
14
|
+
* to mention.
|
|
15
|
+
*
|
|
16
|
+
* This is that home. A value enters ONCE, with a Binding saying how a later
|
|
17
|
+
* run re-derives its own, and every producer asks the ledger rather than
|
|
18
|
+
* guessing. See PLAN-provenance.md.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Identifier-like: specific enough to be a REFERENCE rather than a word the
|
|
22
|
+
* app happens to use. A route word ("tickets", "dashboards") is a common
|
|
23
|
+
* lowercase noun; a minted id carries a digit, a separator, or the length of
|
|
24
|
+
* a generated uid.
|
|
25
|
+
*
|
|
26
|
+
* This is the ONE copy. It previously existed three times with two different
|
|
27
|
+
* length floors, which is why repair-desk's "t15" was banked by one caller
|
|
28
|
+
* and left literal by another.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* A url part that is an identifier by POSITION, whatever its characters.
|
|
32
|
+
*
|
|
33
|
+
* identifierLike reads the value's shape, and a shape gate has a floor: odoo's
|
|
34
|
+
* database ids are short integers (`#id=44`), invisible to it — and through
|
|
35
|
+
* it, to the ledger, the leak guards, flow minting and compile slotting all at
|
|
36
|
+
* once. fwod27 is the bill: the recording's contact id 44 rode into a flow
|
|
37
|
+
* instruction as prose ("res.partner id 44"), no guard saw it, and both
|
|
38
|
+
* replays navigated to the recording's deleted record and halted at step 2.
|
|
39
|
+
*
|
|
40
|
+
* The url's own vocabulary settles what a shape cannot: a query/hash param
|
|
41
|
+
* NAMED id holds a record id. Only all-digit values qualify — a param named
|
|
42
|
+
* `id` carrying a word is some app's routing, not a record number.
|
|
43
|
+
*
|
|
44
|
+
* Exactly `id`, not `.*_id`. The wider match was speculation beyond the
|
|
45
|
+
* evidence (fwod27's leak was `#id=44`), and fwod29 showed what it costs:
|
|
46
|
+
* odoo's `menu_id=181` — a routing constant every run shares — banked as a
|
|
47
|
+
* run-made record id and flunked verify-artifacts' navigation check on a
|
|
48
|
+
* clean 8/8 sweep. An app calls its record pointer `id`; a `<thing>_id`
|
|
49
|
+
* param in a url is that thing's ADDRESS in the app's chrome.
|
|
50
|
+
*/
|
|
51
|
+
export function idPositionPart(part) {
|
|
52
|
+
return part.label === 'q.id' && /^\d{1,10}$/.test(part.value);
|
|
53
|
+
}
|
|
54
|
+
export function identifierLike(value) {
|
|
55
|
+
if (value.length < MIN_ID_LEN)
|
|
56
|
+
return false;
|
|
57
|
+
// No minted id contains whitespace. Without this the `length >= 12` clause
|
|
58
|
+
// — which exists for digitless uids like "afwfbbc2of6rkf" — swallows
|
|
59
|
+
// ordinary prose: fwrd23l reported the app's validation heading "Ticket is
|
|
60
|
+
// not ready" as a value, it was banked as an identifier, and the export gate
|
|
61
|
+
// then refused a clean 37-minute run because a text locator legitimately
|
|
62
|
+
// matched the app's own error message.
|
|
63
|
+
if (/\s/.test(value))
|
|
64
|
+
return false;
|
|
65
|
+
// A hyphenated pair of words is a SLUG, not a minted id: grafana's
|
|
66
|
+
// "bench-service-health", atelyr's "project-manager". Those are route
|
|
67
|
+
// segments every run shares, and banking them made the export gate refuse a
|
|
68
|
+
// whole recording. A separator only makes a reference when a digit comes
|
|
69
|
+
// with it ("RD-1015", "fwrd24l-n1"); a digitless opaque token still
|
|
70
|
+
// qualifies on length alone, which is what grafana's "cfwcsdxqdjabkf" needs.
|
|
71
|
+
if (/[-_]/.test(value))
|
|
72
|
+
return /\d/.test(value);
|
|
73
|
+
return /\d/.test(value) || value.length >= 12;
|
|
74
|
+
}
|
|
75
|
+
/** Three, not four: repair-desk's record ids are "t15". */
|
|
76
|
+
const MIN_ID_LEN = 3;
|
|
77
|
+
const MAX_VALUE_LEN = 200;
|
|
78
|
+
/**
|
|
79
|
+
* A binding's stable identity, so a compiled param can name the ORIGIN of its
|
|
80
|
+
* value ("var:runid", "url:01-open:p1") and a later run resolve its own from
|
|
81
|
+
* the same origin. This is what lets a value cross an instruction boundary: a
|
|
82
|
+
* skill whose template never mentions the runid can still bind it, because the
|
|
83
|
+
* param points at where the value comes from rather than at a word to match.
|
|
84
|
+
*/
|
|
85
|
+
export function bindingKey(b) {
|
|
86
|
+
switch (b.from) {
|
|
87
|
+
case 'var':
|
|
88
|
+
return `var:${b.name}`;
|
|
89
|
+
case 'input':
|
|
90
|
+
return 'input';
|
|
91
|
+
case 'url':
|
|
92
|
+
return `url:${b.step}:${b.label}`;
|
|
93
|
+
case 'output':
|
|
94
|
+
return `output:${b.step}:${b.name}${b.path ? `#${b.path}` : ''}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export class RunLedger {
|
|
98
|
+
entries = [];
|
|
99
|
+
/** Values already banked, so first appearance wins. */
|
|
100
|
+
seen = new Set();
|
|
101
|
+
instruction = 0;
|
|
102
|
+
step = 0;
|
|
103
|
+
/** Advance the cursor used to stamp `firstSeen`. */
|
|
104
|
+
beginInstruction(index) {
|
|
105
|
+
this.instruction = index;
|
|
106
|
+
this.step = 0;
|
|
107
|
+
}
|
|
108
|
+
beginStep(index) {
|
|
109
|
+
this.step = index;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Bank a value with its provenance. Returns the entry, or null when the
|
|
113
|
+
* value is unusable (too short, too long, already known). First appearance
|
|
114
|
+
* wins: the step that MINTED a value owns it, so later steps reference it
|
|
115
|
+
* rather than re-minting a duplicate.
|
|
116
|
+
*/
|
|
117
|
+
add(value, binding, opts = {}) {
|
|
118
|
+
const v = String(value ?? '').trim();
|
|
119
|
+
// The length floor guards against banking junk from shape-guessing
|
|
120
|
+
// callers. A VOUCHED value has positional evidence instead (a `q.id` url
|
|
121
|
+
// part — see idPositionPart), and odoo's two-digit record ids are exactly
|
|
122
|
+
// what the floor was silently discarding: fwod27's contact id 44 never
|
|
123
|
+
// banked, so no guard downstream could see it leak.
|
|
124
|
+
if ((v.length < MIN_ID_LEN && !opts.vouched) || !v.length || v.length > MAX_VALUE_LEN || this.seen.has(v))
|
|
125
|
+
return null;
|
|
126
|
+
const entry = {
|
|
127
|
+
value: v,
|
|
128
|
+
binding,
|
|
129
|
+
kind: opts.kind ?? (identifierLike(v) ? 'identifier' : 'text'),
|
|
130
|
+
known: opts.known ?? binding.from === 'var',
|
|
131
|
+
firstSeen: { instruction: this.instruction, step: this.step },
|
|
132
|
+
};
|
|
133
|
+
this.seen.add(v);
|
|
134
|
+
this.entries.push(entry);
|
|
135
|
+
return entry;
|
|
136
|
+
}
|
|
137
|
+
/** Bank the identifier-like parts of a url the run just landed on. */
|
|
138
|
+
addUrlIds(url, step, parts) {
|
|
139
|
+
const out = [];
|
|
140
|
+
for (const part of parts) {
|
|
141
|
+
if (!identifierLike(part.value) && !idPositionPart(part))
|
|
142
|
+
continue;
|
|
143
|
+
// A pure-digit QUERY param the app does not call `id` is routing
|
|
144
|
+
// vocabulary, not a record: fwod29 banked odoo's `action=315` and
|
|
145
|
+
// `action=126` (window-action numbers, identical on every run) and the
|
|
146
|
+
// navigation check flagged 16 "leaks" on a clean sweep. A digit run in
|
|
147
|
+
// a PATH position (`/tickets/315`) still banks — there the position is
|
|
148
|
+
// the app saying "this is the record".
|
|
149
|
+
if (/^\d+$/.test(part.value) && part.label.startsWith('q.') && !idPositionPart(part))
|
|
150
|
+
continue;
|
|
151
|
+
const entry = this.add(part.value, { from: 'url', step, label: part.label }, { kind: 'identifier', known: true, vouched: idPositionPart(part) });
|
|
152
|
+
if (entry)
|
|
153
|
+
out.push(entry);
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
/** Every entry, oldest first. */
|
|
158
|
+
all() {
|
|
159
|
+
return [...this.entries];
|
|
160
|
+
}
|
|
161
|
+
has(value) {
|
|
162
|
+
return this.seen.has(String(value ?? '').trim());
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* THE predicate. Every producer that needs to know "does this string carry
|
|
166
|
+
* something this run made?" asks here — compile's slot discovery, the flow
|
|
167
|
+
* exporter's referencizing, the recorder's identity hints, the scanner.
|
|
168
|
+
*
|
|
169
|
+
* Longest first, so a value nested inside a longer one (a runid inside a
|
|
170
|
+
* part name) does not shadow the more specific match.
|
|
171
|
+
*/
|
|
172
|
+
runValuesIn(text) {
|
|
173
|
+
const s = String(text ?? '');
|
|
174
|
+
if (!s)
|
|
175
|
+
return [];
|
|
176
|
+
return this.entries.filter((e) => occursAsToken(s, e.value)).sort((a, b) => b.value.length - a.value.length);
|
|
177
|
+
}
|
|
178
|
+
/** The values themselves, for callers that only need strings (identity hints). */
|
|
179
|
+
values(opts = {}) {
|
|
180
|
+
return this.entries.filter((e) => (opts.known === undefined ? true : e.known === opts.known)).map((e) => e.value);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Whole-token occurrence. Underscores bind — `o_form_view_group` is ONE
|
|
185
|
+
* identifier, so a value "form" must not match its middle (fwod5 shipped
|
|
186
|
+
* exactly that corruption) — while hyphens do not, since a runid prefix in
|
|
187
|
+
* "x7-bench-dashboard" is a reference worth threading.
|
|
188
|
+
*/
|
|
189
|
+
export function occursAsToken(text, value) {
|
|
190
|
+
if (!value)
|
|
191
|
+
return false;
|
|
192
|
+
return new RegExp(`(?<![A-Za-z0-9_])${escapeRe(value)}(?![A-Za-z0-9_])`).test(text);
|
|
193
|
+
}
|
|
194
|
+
export function escapeRe(s) {
|
|
195
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Fields whose CONTRACT is to hold the recording run's value: a param's
|
|
199
|
+
* example, the provenance of the recording, a flow step's record of what it
|
|
200
|
+
* observed. They are documentation, never replayed, and exempting them is
|
|
201
|
+
* what keeps the scanner's output all signal.
|
|
202
|
+
*
|
|
203
|
+
* Deliberately NOT exempt, though both hold recorded text: `reportTemplate`
|
|
204
|
+
* (a tier-A replay synthesises its report from it, so an unslotted value is
|
|
205
|
+
* published as this run's answer) and `expect.urlPattern` (a replay checks
|
|
206
|
+
* the live url against it).
|
|
207
|
+
*/
|
|
208
|
+
const EXEMPT = [/(^|\.)provenance(\.|$)/, /(^|\.)params\.[^.]+\.example$/, /(^|\.)derived\.[^.]+\.example$/, /(^|\.)recorded(\.|$)/, /(^|\.)stats(\.|$)/];
|
|
209
|
+
function exempt(path) {
|
|
210
|
+
return EXEMPT.some((re) => re.test(path));
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Walk anything JSON-shaped and report every run value that survived
|
|
214
|
+
* unslotted. This is an INSTRUMENT before it is a guard: what it actually
|
|
215
|
+
* measures is ledger coverage, since a converter given a complete slot set is
|
|
216
|
+
* a total function. Every one of the seven known defects was a recognition
|
|
217
|
+
* gap, not a conversion error, and each cost a two-hour sweep plus a reading
|
|
218
|
+
* of the drift files to find. This finds them in milliseconds.
|
|
219
|
+
*
|
|
220
|
+
* It can only report values it knows about, so it can never prove absence —
|
|
221
|
+
* only ever "here are more".
|
|
222
|
+
*/
|
|
223
|
+
export function scanForLeaks(artifact, ledger, where = '') {
|
|
224
|
+
const out = [];
|
|
225
|
+
const walk = (node, path) => {
|
|
226
|
+
if (exempt(path))
|
|
227
|
+
return;
|
|
228
|
+
if (typeof node === 'string') {
|
|
229
|
+
for (const entry of ledger.runValuesIn(node)) {
|
|
230
|
+
out.push({ where: path, value: entry.value, binding: entry.binding, kind: entry.kind, context: node.slice(0, 160) });
|
|
231
|
+
}
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (Array.isArray(node)) {
|
|
235
|
+
node.forEach((v, i) => walk(v, `${path}[${i}]`));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (node && typeof node === 'object') {
|
|
239
|
+
for (const [k, v] of Object.entries(node))
|
|
240
|
+
walk(v, path ? `${path}.${k}` : k);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
walk(artifact, where);
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
/** One line per leak, for a warning or a thrown error. */
|
|
247
|
+
export function describeLeaks(leaks) {
|
|
248
|
+
return leaks
|
|
249
|
+
.map((l) => ` ${l.where}: ${JSON.stringify(l.value)} (${l.binding.from}) in ${JSON.stringify(l.context)}`)
|
|
250
|
+
.join('\n');
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Which leaks are fatal.
|
|
254
|
+
*
|
|
255
|
+
* A leak in a LOCATOR or a precondition acts on the wrong record silently:
|
|
256
|
+
* the step resolves, the run continues, and nothing in the output says the
|
|
257
|
+
* procedure moved. That is the failure this whole plan exists to stop, and it
|
|
258
|
+
* is worth refusing an export over.
|
|
259
|
+
*
|
|
260
|
+
* Everywhere else the leak announces itself. A stale `expect.urlPattern`
|
|
261
|
+
* fails its assertion loudly; a stale `reportTemplate` is caught at replay by
|
|
262
|
+
* synthesizeReport, which refuses to publish a value this run did not
|
|
263
|
+
* observe. Those stay warnings — blocking an export on a defect that is
|
|
264
|
+
* already contained would only teach people to pass a --force flag.
|
|
265
|
+
*/
|
|
266
|
+
export function fatal(leak) {
|
|
267
|
+
// Only an IDENTIFIER. A run also reports text it merely observed — an error
|
|
268
|
+
// heading, a status word — and a locator matching the app's own copy is
|
|
269
|
+
// doing its job. Refusing an export over one of those trains people to
|
|
270
|
+
// force past the gate, which costs more than the leak it caught.
|
|
271
|
+
if (leak.kind !== 'identifier')
|
|
272
|
+
return false;
|
|
273
|
+
// ...and only in a LOCATOR, which is the silent case: the step resolves,
|
|
274
|
+
// the run continues, and nothing says the procedure moved record.
|
|
275
|
+
//
|
|
276
|
+
// A precondition is loud. A stale urlPattern or requireText makes the skill
|
|
277
|
+
// REFUSE — softUrlMatch may generalise it, requireText gates identity, and
|
|
278
|
+
// either way the step falls to recovery and says so. Grafana's dashboards
|
|
279
|
+
// put a minted uid in almost every precondition, so treating those as fatal
|
|
280
|
+
// refused whole recordings for a defect that announces itself.
|
|
281
|
+
//
|
|
282
|
+
// A NAVIGATION TARGET was fatal here for one release cycle and is not any
|
|
283
|
+
// more. The reasoning was sound -- fwgr11 went to
|
|
284
|
+
// `/d/<run-1-uid>/{{runid}}-bench-dashboard` and a url is a locator for a
|
|
285
|
+
// page -- but deciding it needs a judgement that cannot be made from one
|
|
286
|
+
// run. fwod19 refused a clean 6/6 recording over
|
|
287
|
+
//
|
|
288
|
+
// args.url: "123" in "http://127.0.0.1:8069/web#action=123&cids=1&menu_id=81"
|
|
289
|
+
//
|
|
290
|
+
// where 123 is Odoo's Discuss MENU id, present in the first post-login
|
|
291
|
+
// navigation and identical on every run. identifierLike("123") is true, so
|
|
292
|
+
// the ledger banked a permanent app constant as a record this run made, and
|
|
293
|
+
// the whole export died. No record-time discriminator survives contact with
|
|
294
|
+
// it: the navigation that reveals 123 is a click, and the step before it is
|
|
295
|
+
// the login fill, so neither "before the first mutation" nor "before the
|
|
296
|
+
// run typed anything" separates it from a real minted uid.
|
|
297
|
+
//
|
|
298
|
+
// So the rule moved to bench/verify-artifacts.mjs, where a false positive
|
|
299
|
+
// costs a look instead of a run. A gate may only enforce what a single run
|
|
300
|
+
// can actually establish; see PLAN-evidence-over-shape.md, which makes the
|
|
301
|
+
// deferred version -- run 1 proposes, run 2 decides -- stage 1.
|
|
302
|
+
return /(^|\.)locators(\.|\[)/.test(leak.where);
|
|
303
|
+
}
|
|
304
|
+
//# sourceMappingURL=ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/skills/ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
export function relabelCases(entries) {
|
|
2
|
+
const out = [];
|
|
3
|
+
let index = 0;
|
|
4
|
+
let instruction = '';
|
|
5
|
+
for (const e of entries) {
|
|
6
|
+
if (e.k === 'instruction') {
|
|
7
|
+
index++;
|
|
8
|
+
instruction = e.text ?? '';
|
|
9
|
+
}
|
|
10
|
+
else if (e.k === 'report' && e.status === 'success' && Object.keys(e.values ?? {}).length) {
|
|
11
|
+
out.push({ index, instruction, values: { ...e.values }, ...(e.skill ? { skill: e.skill } : {}) });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
const RENAME_TOOL = {
|
|
17
|
+
name: 'rename_values',
|
|
18
|
+
description: 'Submit the value renames for this session. Rename ONLY names that mislead or say nothing ' +
|
|
19
|
+
'(selector fragments like h1, role_radiogroup_aria_che, generic value/value_2, or a name that ' +
|
|
20
|
+
'contradicts what the value is). Leave every adequate name alone — churn costs more than it buys.',
|
|
21
|
+
parameters: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
required: ['renames'],
|
|
24
|
+
properties: {
|
|
25
|
+
renames: {
|
|
26
|
+
type: 'array',
|
|
27
|
+
items: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
required: ['i', 'from', 'to'],
|
|
30
|
+
properties: {
|
|
31
|
+
i: { type: 'number', description: 'The instruction number the value belongs to.' },
|
|
32
|
+
from: { type: 'string', description: 'The current name, exactly as listed.' },
|
|
33
|
+
to: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: 'The name a person would use: order_reference, unit_price, customer_name. snake_case.',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
function relabelMessages(cases) {
|
|
44
|
+
const lines = cases.map((c) => `instruction ${c.index}: ${JSON.stringify(c.instruction)}\n values: ${JSON.stringify(c.values)}`);
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
role: 'system',
|
|
48
|
+
content: 'A browser-automation session just finished recording. Each instruction below reported values read ' +
|
|
49
|
+
'off the page, keyed by name. Later runs of this procedure address these values BY NAME, so a name ' +
|
|
50
|
+
'must say what the value IS to someone who cannot see the page: a value the app minted as an order ' +
|
|
51
|
+
'reference must be called order_reference, not h1 (the selector it was read from) and not value_2. ' +
|
|
52
|
+
'You see the whole session, so use hindsight: if a later instruction uses a value, name it the way ' +
|
|
53
|
+
'that instruction speaks about it. Call rename_values exactly once with every rename worth making — ' +
|
|
54
|
+
'and no others.',
|
|
55
|
+
},
|
|
56
|
+
{ role: 'user', content: lines.join('\n\n') },
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
// snake_case is what the prompt asks for, but models also name values in
|
|
60
|
+
// camelCase and those names WORK — fwrd35's reports use totalPrice, partB.
|
|
61
|
+
// The validator's job is mechanical safety, not style: it must not refuse
|
|
62
|
+
// `partB -> partBName` while partB itself stands. Letters either case.
|
|
63
|
+
const NAME_RE = /^[a-zA-Z][a-zA-Z0-9_]{0,31}$/;
|
|
64
|
+
/**
|
|
65
|
+
* Validate a raw rename list into a plan. Anything mechanically unsafe is
|
|
66
|
+
* dropped, not repaired: a rename of a name that does not exist, to a name
|
|
67
|
+
* already taken in the same report, or to something that is not a
|
|
68
|
+
* snake_case word. Returns the plan and what was dropped (for the log).
|
|
69
|
+
*/
|
|
70
|
+
export function validateRelabelPlan(raw, cases) {
|
|
71
|
+
const plan = new Map();
|
|
72
|
+
const dropped = [];
|
|
73
|
+
const byIndex = new Map(cases.map((c) => [c.index, c]));
|
|
74
|
+
const list = Array.isArray(raw?.renames) ? raw.renames : [];
|
|
75
|
+
for (const r of list) {
|
|
76
|
+
const { i, from, to } = (r ?? {});
|
|
77
|
+
const c = typeof i === 'number' ? byIndex.get(i) : undefined;
|
|
78
|
+
const oldName = typeof from === 'string' ? from : '';
|
|
79
|
+
const newName = typeof to === 'string' ? to.trim() : '';
|
|
80
|
+
const have = c
|
|
81
|
+
? new Set([...Object.keys(c.values), ...Object.values(plan.get(c.index) ?? {})])
|
|
82
|
+
: new Set();
|
|
83
|
+
if (!c || !(oldName in c.values))
|
|
84
|
+
dropped.push(`i${String(i)}.${oldName}: no such value`);
|
|
85
|
+
else if (!NAME_RE.test(newName))
|
|
86
|
+
dropped.push(`i${c.index}.${oldName} -> ${JSON.stringify(newName)}: not a snake_case name`);
|
|
87
|
+
else if (newName === oldName)
|
|
88
|
+
continue;
|
|
89
|
+
else if (have.has(newName))
|
|
90
|
+
dropped.push(`i${c.index}.${oldName} -> ${newName}: name already taken`);
|
|
91
|
+
else {
|
|
92
|
+
const m = plan.get(c.index) ?? {};
|
|
93
|
+
m[oldName] = newName;
|
|
94
|
+
plan.set(c.index, m);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { plan, dropped };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Ask the model for a plan. One call; a refusal or malformed answer is an
|
|
101
|
+
* empty plan. Pass a timeout signal: this runs inside the daemon's `stop`,
|
|
102
|
+
* and a slow model must cost the caller a bounded wait, never the export —
|
|
103
|
+
* fwod26's stop blew the CLI's 20s budget on exactly this call and the sweep
|
|
104
|
+
* skipped both replays believing the flow was never saved.
|
|
105
|
+
*/
|
|
106
|
+
export async function requestRelabelPlan(provider, cases, opts = {}) {
|
|
107
|
+
if (!cases.length)
|
|
108
|
+
return { plan: new Map(), dropped: [] };
|
|
109
|
+
// effort low: the plan is one mechanical tool call, and at default effort a
|
|
110
|
+
// reasoning model can deliberate past the caller's whole timebox on a large
|
|
111
|
+
// session — fwgr19-n1 aborted at 100s with the request still pending, on
|
|
112
|
+
// the same model/endpoint where low effort answers in seconds (see
|
|
113
|
+
// CompleteOptions.effort for the measurement).
|
|
114
|
+
const done = await provider.complete(relabelMessages(cases), [RENAME_TOOL], { signal: opts.signal, effort: 'low' });
|
|
115
|
+
const call = (done.toolCalls ?? []).find((t) => t.name === 'rename_values');
|
|
116
|
+
return validateRelabelPlan(call?.args, cases);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Rename report value keys in the recorded entries, in place. Key order is
|
|
120
|
+
* preserved so the rewritten script diffs cleanly. Returns renames applied.
|
|
121
|
+
*
|
|
122
|
+
* Each renamed report also gets a `relabel` field recording old -> new — the
|
|
123
|
+
* durable trace, like `namingAsk`. The daemon runs detached with its stderr
|
|
124
|
+
* ignored, so fwod26's first live relabel was a black box: nothing anywhere
|
|
125
|
+
* said whether the pass ran, renamed, or failed. script.jsonl is the one
|
|
126
|
+
* artifact every run publishes; what the pass did belongs in it.
|
|
127
|
+
*/
|
|
128
|
+
export function applyRelabelToEntries(entries, plan) {
|
|
129
|
+
let applied = 0;
|
|
130
|
+
let index = 0;
|
|
131
|
+
for (const e of entries) {
|
|
132
|
+
if (e.k === 'instruction')
|
|
133
|
+
index++;
|
|
134
|
+
else if (e.k === 'report' && plan.has(index)) {
|
|
135
|
+
const renames = plan.get(index);
|
|
136
|
+
const values = {};
|
|
137
|
+
const done = {};
|
|
138
|
+
for (const [k, v] of Object.entries(e.values ?? {})) {
|
|
139
|
+
const nk = renames[k] ?? k;
|
|
140
|
+
if (nk !== k) {
|
|
141
|
+
applied++;
|
|
142
|
+
done[k] = nk;
|
|
143
|
+
}
|
|
144
|
+
values[nk] = v;
|
|
145
|
+
}
|
|
146
|
+
e.values = values;
|
|
147
|
+
if (Object.keys(done).length)
|
|
148
|
+
e.relabel = done;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return applied;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Rename one session's compiled skills to agree with the renamed entries:
|
|
155
|
+
* read labels, reportTemplate keys, and `output:i<n>:<name>` param bindings.
|
|
156
|
+
* `skillIndex` says which instruction each skill (or segment chain) belongs
|
|
157
|
+
* to. Mutates; returns the ids of skills actually changed, for persisting.
|
|
158
|
+
*/
|
|
159
|
+
export function applyRelabelToSkills(skills, plan, skillIndex) {
|
|
160
|
+
const changed = new Set();
|
|
161
|
+
for (const skill of skills) {
|
|
162
|
+
const index = skillIndex.get(skill.id);
|
|
163
|
+
const renames = index !== undefined ? plan.get(index) : undefined;
|
|
164
|
+
if (renames) {
|
|
165
|
+
const renameSteps = (steps) => {
|
|
166
|
+
for (const s of steps) {
|
|
167
|
+
if (s.label && renames[s.label]) {
|
|
168
|
+
s.label = renames[s.label];
|
|
169
|
+
changed.add(skill.id);
|
|
170
|
+
}
|
|
171
|
+
if (s.body)
|
|
172
|
+
renameSteps(s.body);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
renameSteps(skill.steps);
|
|
176
|
+
if (skill.reportTemplate?.values) {
|
|
177
|
+
const values = {};
|
|
178
|
+
let touched = false;
|
|
179
|
+
for (const [k, v] of Object.entries(skill.reportTemplate.values)) {
|
|
180
|
+
const nk = renames[k] ?? k;
|
|
181
|
+
if (nk !== k)
|
|
182
|
+
touched = true;
|
|
183
|
+
values[nk] = v;
|
|
184
|
+
}
|
|
185
|
+
if (touched) {
|
|
186
|
+
skill.reportTemplate.values = values;
|
|
187
|
+
changed.add(skill.id);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Bindings can point at ANY earlier instruction's output, so every skill
|
|
192
|
+
// is checked against the whole plan, not just its own instruction's part.
|
|
193
|
+
for (const param of Object.values(skill.params ?? {})) {
|
|
194
|
+
const m = param.binding?.match(/^output:i(\d+):([^#]+)(#.*)?$/);
|
|
195
|
+
if (!m)
|
|
196
|
+
continue;
|
|
197
|
+
const to = plan.get(Number(m[1]))?.[m[2]];
|
|
198
|
+
if (to) {
|
|
199
|
+
param.binding = `output:i${m[1]}:${to}${m[3] ?? ''}`;
|
|
200
|
+
changed.add(skill.id);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return [...changed];
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=relabel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relabel.js","sourceRoot":"","sources":["../../src/skills/relabel.ts"],"names":[],"mappings":"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"}
|