@miller-tech/uap 1.173.0 → 1.174.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.
@@ -0,0 +1,610 @@
1
+ /**
2
+ * Turn-end substance sweep for files written through `run_bash`.
3
+ *
4
+ * The stub guard covers `write_file` and `edit_file`. `run_bash` bypasses both:
5
+ * `cat > f <<'EOF' … EOF`, `sed -i` and `python -c "open(...).write(...)"` all
6
+ * land content without passing through a tool handler. That was recorded as a
7
+ * known limit when the guard shipped (PR #611) rather than papered over, and
8
+ * this closes it.
9
+ *
10
+ * WHY A SWEEP AND NOT A CHECK IN THE HANDLER
11
+ * The other run_bash protections work by SNAPSHOTTING a known set — protected
12
+ * tests, locked contracts, gate configs — and restoring whatever the command
13
+ * touched. That works because those sets are enumerable up front. The set of
14
+ * files a shell command might write is not, so there is nothing to snapshot by
15
+ * name. The sweep instead takes a baseline of the tree at the first command and
16
+ * compares at turn end, which is the only way to attribute an arbitrary write.
17
+ *
18
+ * THE COST MODEL IS INVERTED FROM THE WRITE PATH, AND THAT DRIVES EVERYTHING
19
+ * On the write path a false positive costs one retry: the write is refused and
20
+ * the model tries again. Here a false positive touches a file that already
21
+ * exists, so it costs WORK. Four consequences, each of which an earlier version
22
+ * of this file got wrong and each of which now has a test:
23
+ *
24
+ * 1. Absence of baseline CONTENT never implies absence of the FILE. Existence
25
+ * is recorded for every path with no read and no byte budget, so a file the
26
+ * content baseline skipped — capped, oversized, unreadable — is still known
27
+ * to have existed. Only a path absent from `known` may be treated as created
28
+ * this turn. Without that split, a file the baseline had skipped was DELETED
29
+ * when the shell shrank it, and on a tree past the cap files the shell never
30
+ * touched became deletion candidates.
31
+ * 2. Nothing the harness has SEEN is destroyed, and nothing leaves the project.
32
+ * Every removal and every revert preserves the current content under
33
+ * `.uap/bash-sweep-backup/` first, a failed preserve aborts the action, and
34
+ * every destination is proven to resolve inside that directory. The limit is
35
+ * worth stating precisely, because a broader claim would be false: a revert
36
+ * restores the last content the harness AUTHORISED, so if one shell command
37
+ * writes real code and a later command in the SAME turn hollows it, the
38
+ * intermediate version — which no tool handler ever saw — is not recovered.
39
+ * The rejected content is preserved; the unobserved good version is not. `relative()` used to be run through
40
+ * `.split('\\')`, which is correct on Windows and WRONG on POSIX where a
41
+ * backslash is a legal filename character: one directory entry named
42
+ * `..\..\..\..\x.js` produced a traversing key and the sweep wrote and
43
+ * RENAMED outside the project root. (Reproduced; test below. The harness
44
+ * already documents that the small model mangles paths, so this needed no
45
+ * adversary.)
46
+ * 3. An existing file is only reverted when the change made it STRICTLY WORSE —
47
+ * more empty bodies than the content we authorised. A formatter or codemod
48
+ * that rewrites an already-stubby file without emptying anything further is
49
+ * left alone, because reverting it would be both wrong and unexplainable
50
+ * ("your shell command wrote SKELETONS" about `prettier --write`).
51
+ * 4. Silence must mean "nothing changed", not "we could not look". Coverage
52
+ * gaps are reported — but ONLY for files that actually changed. Reporting
53
+ * every unreadable path made a single `logo.png` emit a coverage warning on
54
+ * every bash turn of every asset-bearing project, into a prompt slot that is
55
+ * head-truncated.
56
+ *
57
+ * WHAT COUNTS AS THE BASELINE
58
+ * Per file, the last content this harness knows it authorised: what a guarded
59
+ * tool wrote this turn if one did, otherwise the content at the first command.
60
+ * Using "was it written by a guarded tool" as a plain skip would leave a hole —
61
+ * write the real implementation with write_file, then hollow it out with sed,
62
+ * and the path is on the allow-list. Comparing against the authorised CONTENT
63
+ * closes it. A file CREATED this turn is judged on its own content regardless of
64
+ * authorisation, which closes the mirror hole: the shell writes a skeleton, then
65
+ * a single edit_file fills one body and the monotone-progress rule — measuring
66
+ * against the skeleton itself — stamps the rest as authorised.
67
+ *
68
+ * SCOPE, STATED PLAINLY
69
+ * This is a correctness guard against confused generation, not a security
70
+ * boundary. A model holding a shell can do anything a shell can do, including
71
+ * removing this check; run_bash is gated on kernel containment for that reason.
72
+ * The sweep judges SUBSTANCE only — a command that deletes a real file is a
73
+ * different failure and is not restored here. And a quarantine can leave the
74
+ * tree in a state neither the model nor the gates have seen: a `main.js` written
75
+ * by write_file may import a `player.js` the sweep removed. The gates see that
76
+ * and the note names the repair, which is the intended outcome, but it is a real
77
+ * intermediate state rather than a clean rollback.
78
+ */
79
+ import { readdirSync, statSync, fstatSync, writeFileSync, mkdirSync, renameSync, realpathSync, lstatSync, existsSync, openSync, readSync, closeSync, constants as fsConstants, } from 'fs';
80
+ import { join, relative, resolve, dirname, sep } from 'path';
81
+ import { detectStub, stubGuardDisabled } from './stub-detector.js';
82
+ import { CONTEXT_BUDGET_MARKER } from './context-budget.js';
83
+ /**
84
+ * Never walked at any depth: version control, dependencies, harness state,
85
+ * language caches and vendored third-party trees. `.git` and `node_modules` also
86
+ * hold code that would be judged as if the model had authored it — and the
87
+ * harness actively steers models to vendor dependencies locally when a CDN fetch
88
+ * fails, so `vendor`/`third_party` are the same case by another name.
89
+ */
90
+ const ALWAYS_SKIP = new Set([
91
+ '.git',
92
+ 'node_modules',
93
+ '.uap',
94
+ '.uap-deliver',
95
+ '.uap-backups',
96
+ '__pycache__',
97
+ '.pytest_cache',
98
+ '.mypy_cache',
99
+ '.ruff_cache',
100
+ '.tox',
101
+ '.venv',
102
+ 'venv',
103
+ '.cache',
104
+ '.next',
105
+ '.nuxt',
106
+ '.svelte-kit',
107
+ '.turbo',
108
+ 'vendor',
109
+ 'third_party',
110
+ // Every other tree-walk in this package excludes these; this one omitted them.
111
+ // A sweep rooted at a repo that follows the worktree workflow would otherwise
112
+ // walk N full checkouts, and `agents/` holds the memory/coordination SQLite
113
+ // stores and the vector index.
114
+ '.worktrees',
115
+ 'agents',
116
+ ]);
117
+ /**
118
+ * Build output — skipped only at the PROJECT ROOT. Matching these at any depth
119
+ * made `src/build/`, `app/out/` and `pkg/target/` permanent blind spots, and for
120
+ * a web mission the deliverable itself sometimes lives in `build/` or `out/`.
121
+ */
122
+ const ROOT_ONLY_SKIP = new Set(['dist', 'build', 'out', 'coverage', 'target']);
123
+ /**
124
+ * A delivery target is an application, not a monorepo, so these sit far above
125
+ * any real project — they exist so a mission pointed at something huge degrades
126
+ * predictably instead of reading gigabytes inside a turn boundary. Injectable so
127
+ * tests exercise the capped paths for real rather than setting a flag by hand.
128
+ */
129
+ export const DEFAULT_LIMITS = {
130
+ maxFiles: 3000,
131
+ maxTotalBytes: 16 * 1024 * 1024,
132
+ maxFileBytes: 200_000,
133
+ };
134
+ /** Where displaced content goes. Inside `.uap`, so the sweep never re-walks it. */
135
+ export const BACKUP_DIR = join('.uap', 'bash-sweep-backup');
136
+ /** Fresh on every call: a shared singleton lets one caller's mutation leak. */
137
+ function emptyOutcome() {
138
+ return { reverted: [], removed: [], uncovered: [], failed: [], note: '' };
139
+ }
140
+ /** A sweep that never fires — for callers with bash disabled. */
141
+ export function disabledSweep() {
142
+ return {
143
+ enabled: false,
144
+ projectRoot: '',
145
+ bashRan: false,
146
+ baselined: false,
147
+ swept: false,
148
+ authorised: new Map(),
149
+ baseline: new Map(),
150
+ known: new Set(),
151
+ statAt: new Map(),
152
+ truncated: false,
153
+ limits: DEFAULT_LIMITS,
154
+ };
155
+ }
156
+ /**
157
+ * Project-relative key for a walked path.
158
+ *
159
+ * Separator translation happens ONLY on Windows. Doing it unconditionally is the
160
+ * bug described in the header: on POSIX a backslash is a legal filename
161
+ * character, so translating it manufactures `..` segments out of a single
162
+ * directory entry.
163
+ */
164
+ function relKey(projectRoot, abs) {
165
+ const rel = relative(projectRoot, abs);
166
+ return sep === '\\' ? rel.split('\\').join('/') : rel;
167
+ }
168
+ /**
169
+ * Read a file as text, or null when it is binary, unreadable, or not a regular
170
+ * file by the time it is opened.
171
+ *
172
+ * Opened non-blocking and re-checked through `fstat` on the open descriptor: a
173
+ * background process left running by the command can swap a file for a FIFO
174
+ * between the directory read and this open, and a blocking open on a FIFO with
175
+ * no writer hangs forever — inside the turn boundary, which stops the deliver
176
+ * heartbeat and invites the lock's wedge-reclaim to kill a run that is merely
177
+ * blocked.
178
+ */
179
+ function readText(abs, maxBytes) {
180
+ let fd = -1;
181
+ try {
182
+ fd = openSync(abs, fsConstants.O_RDONLY | fsConstants.O_NONBLOCK);
183
+ const st = fstatSync(fd);
184
+ if (!st.isFile() || st.size > maxBytes)
185
+ return null;
186
+ const buf = Buffer.alloc(st.size);
187
+ let off = 0;
188
+ while (off < st.size) {
189
+ const n = readSync(fd, buf, off, st.size - off, off);
190
+ if (n <= 0)
191
+ break;
192
+ off += n;
193
+ }
194
+ // Binary sniff. The detector exempts a fixed list of extensions and scans
195
+ // everything else — right for a write path (the model writes source) and wrong
196
+ // for a whole-tree walk, where a .png/.wasm/.db under the read cap would be
197
+ // decoded as UTF-8, held in the baseline, and fed to a brace scanner.
198
+ if (buf.subarray(0, Math.min(off, 8192)).includes(0))
199
+ return null;
200
+ const text = buf.subarray(0, off).toString('utf-8');
201
+ // The decoded string is what gets written BACK on a revert, so a lossy decode
202
+ // would silently corrupt the file it is meant to restore. Latin-1 source, or a
203
+ // NUL past the 8 KB sniff window, round-trips through U+FFFD; a byte-length
204
+ // mismatch is the cheap, exact test for that. Treat it as out of scope.
205
+ if (Buffer.byteLength(text, 'utf-8') !== off)
206
+ return null;
207
+ return text;
208
+ }
209
+ catch {
210
+ return null;
211
+ }
212
+ finally {
213
+ if (fd >= 0) {
214
+ try {
215
+ closeSync(fd);
216
+ }
217
+ catch {
218
+ /* ignore */
219
+ }
220
+ }
221
+ }
222
+ }
223
+ /** Walk project files, calling `onFile` for each. Continues past unreadable dirs. */
224
+ function walk(projectRoot, onFile) {
225
+ const stack = [projectRoot];
226
+ while (stack.length > 0) {
227
+ const dir = stack.pop();
228
+ let entries;
229
+ try {
230
+ entries = readdirSync(dir, { withFileTypes: true });
231
+ }
232
+ catch {
233
+ continue; // unreadable directory — not a reason to fail the turn
234
+ }
235
+ const atRoot = dir === projectRoot;
236
+ for (const e of entries) {
237
+ if (e.isSymbolicLink())
238
+ continue; // never follow: a link can leave the root
239
+ const abs = join(dir, e.name);
240
+ if (e.isDirectory()) {
241
+ if (ALWAYS_SKIP.has(e.name))
242
+ continue;
243
+ if (atRoot && ROOT_ONLY_SKIP.has(e.name))
244
+ continue;
245
+ stack.push(abs);
246
+ continue;
247
+ }
248
+ if (!e.isFile())
249
+ continue;
250
+ let st;
251
+ try {
252
+ st = statSync(abs);
253
+ }
254
+ catch {
255
+ continue;
256
+ }
257
+ onFile(relKey(projectRoot, abs), abs, st.size, st.mtimeMs);
258
+ }
259
+ }
260
+ }
261
+ const ACTIVE_ROOTS = new Set();
262
+ /**
263
+ * Arm the sweep for a turn. Deliberately does NOT walk anything.
264
+ *
265
+ * The baseline is captured lazily, at the first command, so a bash-enabled
266
+ * session that does not actually shell out in a given turn pays nothing.
267
+ * Deferring is safe because of what `authorised` holds: anything that changed
268
+ * BEFORE the first command can only have come from a guarded tool, and those
269
+ * writes are recorded by content as they happen.
270
+ */
271
+ export function beginBashSweep(projectRoot, enabled, limits = DEFAULT_LIMITS) {
272
+ if (!enabled || stubGuardDisabled())
273
+ return disabledSweep();
274
+ // Two live sweeps over ONE tree would cross-attribute: A's baseline predates
275
+ // B's guarded writes, so A can revert or quarantine a file B legitimately
276
+ // created, and both would write the same backup paths. Nothing in the current
277
+ // wiring runs concurrent agentic executors on a shared root, but that is a
278
+ // decision in another file and this module is exported. Degrade to no sweep
279
+ // rather than trust it: a missed check is recoverable, a wrong revert is not.
280
+ if (ACTIVE_ROOTS.has(projectRoot))
281
+ return disabledSweep();
282
+ ACTIVE_ROOTS.add(projectRoot);
283
+ return {
284
+ enabled: true,
285
+ projectRoot,
286
+ bashRan: false,
287
+ baselined: false,
288
+ swept: false,
289
+ authorised: new Map(),
290
+ baseline: new Map(),
291
+ known: new Set(),
292
+ statAt: new Map(),
293
+ truncated: false,
294
+ limits,
295
+ };
296
+ }
297
+ /**
298
+ * Arm for a command: mark that the shell ran, and capture the baseline once.
299
+ *
300
+ * Called from the run_bash handler BEFORE the spawn — a command that times out
301
+ * or is killed may still have written files, and a sweep with nothing to compare
302
+ * against would be worse than none, reporting "checked" over the messiest case.
303
+ */
304
+ export function armSweepForCommand(sweep) {
305
+ if (!sweep.enabled)
306
+ return;
307
+ sweep.bashRan = true;
308
+ if (sweep.baselined)
309
+ return;
310
+ sweep.baselined = true;
311
+ let bytes = 0;
312
+ walk(sweep.projectRoot, (rel, abs, size, mtimeMs) => {
313
+ // EXISTENCE and STAT are recorded for every file, unconditionally. Existence
314
+ // stops a gap in the content baseline from being read as "this file is new";
315
+ // size is what lets a later change to an unbaselined file still be noticed,
316
+ // so coverage warnings name files that actually moved.
317
+ sweep.known.add(rel);
318
+ sweep.statAt.set(rel, { size, mtimeMs });
319
+ if (size > sweep.limits.maxFileBytes)
320
+ return; // handled via sizeAt, not a truncation
321
+ if (sweep.baseline.size >= sweep.limits.maxFiles || bytes + size > sweep.limits.maxTotalBytes) {
322
+ sweep.truncated = true;
323
+ return; // keep walking: existence still needs the rest of the tree
324
+ }
325
+ const text = readText(abs, sweep.limits.maxFileBytes);
326
+ if (text === null)
327
+ return; // binary — out of scope, and not a coverage gap
328
+ sweep.baseline.set(rel, text);
329
+ bytes += size;
330
+ });
331
+ }
332
+ /** Record what a guarded tool wrote, so a later shell write is distinguishable. */
333
+ export function recordAuthorisedWrite(sweep, rel, content) {
334
+ if (sweep.enabled)
335
+ sweep.authorised.set(rel, content);
336
+ }
337
+ /** Empty bodies in a verdict — the quantity the "strictly worse" test compares. */
338
+ function emptyCount(v) {
339
+ return Math.round(v.emptyRatio * v.callables);
340
+ }
341
+ /**
342
+ * Resolve the backup root and prove it is really inside the project.
343
+ *
344
+ * The walk never follows symlinks, so no path it yields can escape — but the
345
+ * backup root is a path this module CONSTRUCTS rather than discovers, and `.uap`
346
+ * is a directory a shell command can replace with a link. Measured before this
347
+ * check existed: `ln -s /elsewhere .uap` and the sweep's own backups landed
348
+ * outside the project root. Returns null when the location cannot be trusted,
349
+ * which disables every mutation rather than performing an unbacked-up one.
350
+ */
351
+ function resolveBackupRoot(projectRoot) {
352
+ try {
353
+ const holder = join(projectRoot, '.uap');
354
+ // Check the component we create THROUGH before creating anything, so a
355
+ // redirected root does not even leave an empty directory behind.
356
+ try {
357
+ if (lstatSync(holder).isSymbolicLink())
358
+ return null;
359
+ }
360
+ catch {
361
+ /* absent — mkdir below creates it inside the project */
362
+ }
363
+ const dest = join(projectRoot, BACKUP_DIR);
364
+ mkdirSync(dest, { recursive: true });
365
+ const realDest = realpathSync(dest);
366
+ const realRoot = realpathSync(projectRoot);
367
+ return realDest === realRoot || realDest.startsWith(realRoot + sep) ? realDest : null;
368
+ }
369
+ catch {
370
+ return null;
371
+ }
372
+ }
373
+ /**
374
+ * Destination inside the backup root for `rel`, or null if it would escape.
375
+ *
376
+ * Belt-and-braces now that `relKey` no longer manufactures `..` segments, but
377
+ * this is the check that actually contains the failure rather than the one that
378
+ * avoids provoking it — and it is two lines.
379
+ */
380
+ function backupDest(backupRoot, rel) {
381
+ const dest = resolve(backupRoot, rel);
382
+ return dest.startsWith(backupRoot + sep) ? dest : null;
383
+ }
384
+ /**
385
+ * A destination that will not clobber an earlier turn's backup.
386
+ *
387
+ * "Nothing is destroyed" has to hold ACROSS turns, and the retry loop is
388
+ * designed to re-attempt the same file — so the same `rel` being quarantined
389
+ * twice is the common case, not an edge one.
390
+ */
391
+ function freeDest(dest) {
392
+ if (!existsSync(dest))
393
+ return dest;
394
+ for (let i = 2; i < 1000; i++) {
395
+ const candidate = `${dest}.${i}`;
396
+ if (!existsSync(candidate))
397
+ return candidate;
398
+ }
399
+ return `${dest}.overflow`;
400
+ }
401
+ /** Copy content into the backup root. False means the caller must NOT mutate. */
402
+ function backup(backupRoot, rel, content) {
403
+ const dest = backupDest(backupRoot, rel);
404
+ if (dest === null)
405
+ return null;
406
+ try {
407
+ const target = freeDest(dest);
408
+ mkdirSync(dirname(target), { recursive: true });
409
+ writeFileSync(target, content, 'utf-8');
410
+ return target;
411
+ }
412
+ catch {
413
+ return null;
414
+ }
415
+ }
416
+ /**
417
+ * Compare the tree against the baseline and undo unattributed stub writes.
418
+ * Returns an empty outcome when bash never ran, so the common path is a boolean.
419
+ */
420
+ export function finishBashSweep(sweep) {
421
+ if (!sweep.enabled)
422
+ return emptyOutcome();
423
+ // Released here rather than by the caller: the executor's `finally` guarantees
424
+ // this runs, and a root left marked active would silently disable the guard for
425
+ // every later turn.
426
+ ACTIVE_ROOTS.delete(sweep.projectRoot);
427
+ if (!sweep.bashRan || sweep.swept || stubGuardDisabled())
428
+ return emptyOutcome();
429
+ sweep.swept = true;
430
+ const reverted = [];
431
+ const removed = [];
432
+ const uncovered = [];
433
+ const failed = [];
434
+ // Established once, before anything is touched. A backup location that cannot
435
+ // be proven inside the project means no mutation happens at all this turn.
436
+ const backupRoot = resolveBackupRoot(sweep.projectRoot);
437
+ walk(sweep.projectRoot, (rel, abs, size, mtimeMs) => {
438
+ const existedBefore = sweep.known.has(rel);
439
+ const before = sweep.statAt.get(rel);
440
+ const changedStat = before === undefined || before.size !== size || before.mtimeMs !== mtimeMs;
441
+ // Unmoved and not written by a guarded tool this turn: nothing to judge, and
442
+ // no reason to pay a full read. This is most of the tree on most turns.
443
+ if (!changedStat && !sweep.authorised.has(rel))
444
+ return;
445
+ // The last content this harness authorised: a guarded write this turn, else
446
+ // whatever was there when the first command ran.
447
+ const authorised = sweep.authorised.get(rel) ?? sweep.baseline.get(rel);
448
+ if (size > sweep.limits.maxFileBytes) {
449
+ // Too big to read now, so it can be neither judged nor reverted. Report it
450
+ // only if it demonstrably moved — otherwise every project with a large
451
+ // lockfile emits a coverage warning on every single bash turn.
452
+ if (existedBefore ? changedStat : true)
453
+ uncovered.push(rel);
454
+ return;
455
+ }
456
+ const current = readText(abs, sweep.limits.maxFileBytes);
457
+ if (current === null)
458
+ return; // binary or unreadable — out of scope entirely
459
+ if (existedBefore && authorised === undefined) {
460
+ // Known to have existed, but no content is held for it — the content
461
+ // baseline was capped. Report only a real change; guessing here is what
462
+ // deleted pre-existing files in the first version of this module.
463
+ uncovered.push(rel);
464
+ return;
465
+ }
466
+ if (!existedBefore) {
467
+ // Created this turn. Judged on its own content REGARDLESS of authorisation,
468
+ // and BEFORE the unchanged-since-authorised skip below — that skip is what
469
+ // made the laundering hole real: the shell writes a skeleton, one edit_file
470
+ // fills a single body (legitimately allowed, since monotone progress is
471
+ // measured against the skeleton itself), the file is stamped as authorised,
472
+ // and a content comparison then matches and skips it forever.
473
+ if (!detectStub(rel, current).isStub)
474
+ return;
475
+ // Rename IS the backup here — the file's own bytes move to the preserved
476
+ // location. Writing a copy first and then renaming over it, as this did
477
+ // originally, paid a full write that the rename immediately discarded and
478
+ // left a stray duplicate whenever the rename failed.
479
+ const dest = backupRoot === null ? null : backupDest(backupRoot, rel);
480
+ if (dest === null) {
481
+ failed.push(rel);
482
+ return;
483
+ }
484
+ try {
485
+ const target = freeDest(dest);
486
+ mkdirSync(dirname(target), { recursive: true });
487
+ renameSync(abs, target);
488
+ removed.push(rel);
489
+ }
490
+ catch {
491
+ failed.push(rel);
492
+ }
493
+ return;
494
+ }
495
+ if (authorised === current)
496
+ return; // untouched since we last saw it
497
+ // Pre-existing and changed. Act only when the change made it STRICTLY WORSE:
498
+ // a formatter or codemod that rewrites an already-stubby file without
499
+ // emptying anything further must survive.
500
+ const after = detectStub(rel, current);
501
+ if (!after.isStub)
502
+ return;
503
+ const beforeVerdict = detectStub(rel, authorised);
504
+ // A baseline the detector could not judge (over its scan cap, or an exempt
505
+ // extension) reports zero callables, which would make ANY later content with
506
+ // one empty body read as "strictly worse". No comparison, no action.
507
+ if (beforeVerdict.callables === 0)
508
+ return;
509
+ if (emptyCount(after) <= emptyCount(beforeVerdict))
510
+ return;
511
+ if (backupRoot === null || backup(backupRoot, rel, current) === null) {
512
+ failed.push(rel);
513
+ return;
514
+ }
515
+ try {
516
+ // Temp-then-rename, the pattern this subsystem already uses elsewhere: an
517
+ // in-place truncating write that dies mid-way leaves a mangled file whose
518
+ // good content existed only in a JS string the crash takes with it.
519
+ const tmp = `${abs}.uap-sweep-tmp`;
520
+ writeFileSync(tmp, authorised, 'utf-8');
521
+ renameSync(tmp, abs);
522
+ reverted.push(rel);
523
+ }
524
+ catch {
525
+ failed.push(rel);
526
+ }
527
+ });
528
+ return {
529
+ reverted,
530
+ removed,
531
+ uncovered,
532
+ failed,
533
+ note: buildNote({ reverted, removed, uncovered, failed }),
534
+ };
535
+ }
536
+ /**
537
+ * Filenames reach the model verbatim, and POSIX allows newlines and brackets in
538
+ * them — which would let a crafted name inject harness-shaped lines into a note
539
+ * that flows into the retry prompt, the acceptance judge and the operator log.
540
+ */
541
+ function safeName(rel) {
542
+ return rel.replace(/[\r\n\][]/g, '_').slice(0, 120);
543
+ }
544
+ function list(paths, max) {
545
+ const shown = paths.slice(0, max).map(safeName).join(', ');
546
+ return paths.length > max ? `${shown}, +${paths.length - max} more` : shown;
547
+ }
548
+ function buildNote(r) {
549
+ const parts = [];
550
+ const touched = [...r.removed, ...r.reverted];
551
+ if (touched.length > 0) {
552
+ const verb = r.removed.length > 0 && r.reverted.length > 0
553
+ ? 'removed/reverted'
554
+ : r.removed.length > 0
555
+ ? 'removed'
556
+ : 'reverted';
557
+ // The previous content IS preserved, but the location is deliberately not
558
+ // named: it lives under `.uap/`, the one directory read_file and list_dir
559
+ // refuse, and pointing an agent at a path the harness then hides is a
560
+ // harness bug this codebase has already had to fix once.
561
+ parts.push(`[blocked: ${touched.length} file(s) changed outside the write tools were SKELETONS — ` +
562
+ `an API surface with empty function bodies — and have been ${verb}: ${list(touched, 8)}. ` +
563
+ `Writing files through the shell does not bypass this check. Write the REAL ` +
564
+ `implementation with write_file — each function must contain the logic that makes ` +
565
+ `it work. If a file is deliberately a skeleton, give each body an explicit ` +
566
+ `throw new Error('TODO: <what>') instead of an empty block.]`);
567
+ }
568
+ if (r.failed.length > 0) {
569
+ // A guard that could not act must never look like a guard that found nothing.
570
+ parts.push(`[warning: the substance check could not act on ${r.failed.length} file(s) ` +
571
+ `(${list(r.failed, 4)}) — they were left as-is.]`);
572
+ }
573
+ if (r.uncovered.length > 0) {
574
+ // Gated on files that actually CHANGED, not on `truncated`. A capped baseline
575
+ // where nothing moved is complete coverage in every way the model cares
576
+ // about; emitting on the cap alone put a warning on every bash turn of every
577
+ // project with a big lockfile, in a prompt slot that is head-truncated.
578
+ // `truncated` remains on the sweep for callers and tests, as a fact about the
579
+ // baseline rather than a claim about coverage.
580
+ parts.push(`[note: ${r.uncovered.length} changed file(s) were too large to check this turn: ${list(r.uncovered, 4)}.]`);
581
+ }
582
+ return parts.join('\n');
583
+ }
584
+ /**
585
+ * Put the note FIRST — but never ahead of a protocol marker.
586
+ *
587
+ * The retry prompt includes the previous output through `truncateHead`, which
588
+ * keeps the leading 3000 characters, so a note appended after a long turn summary
589
+ * is exactly the part that gets cut. That argues for prepending.
590
+ *
591
+ * Against it: `decodeBudgetStop` only recognises the context-budget marker within
592
+ * the first 512 characters, and this note routinely exceeds that on its own. A
593
+ * budget-stopped turn that was also swept would therefore stop being recognised
594
+ * as budget-stopped — which silently disables the epic controller's rail-sizing
595
+ * split, on exactly the long shell-using sessions that blow the rail. So when the
596
+ * output carries that marker, the note goes after the marker's own line: the
597
+ * marker keeps position 0, and the note is still near the front.
598
+ */
599
+ export function prependSweepNote(output, outcome) {
600
+ if (!outcome.note)
601
+ return output;
602
+ if (output.startsWith(CONTEXT_BUDGET_MARKER)) {
603
+ const nl = output.indexOf('\n');
604
+ return nl === -1
605
+ ? `${output}\n${outcome.note}`
606
+ : `${output.slice(0, nl)}\n${outcome.note}${output.slice(nl)}`;
607
+ }
608
+ return `${outcome.note}\n${output}`;
609
+ }
610
+ //# sourceMappingURL=bash-sweep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bash-sweep.js","sourceRoot":"","sources":["../../src/delivery/bash-sweep.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.173.0",
3
+ "version": "1.174.0",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",