@miller-tech/uap 1.172.15 → 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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/delivery/agentic-executor.d.ts +2 -1
- package/dist/delivery/agentic-executor.d.ts.map +1 -1
- package/dist/delivery/agentic-executor.js +102 -4
- package/dist/delivery/agentic-executor.js.map +1 -1
- package/dist/delivery/applier.d.ts.map +1 -1
- package/dist/delivery/applier.js +53 -0
- package/dist/delivery/applier.js.map +1 -1
- package/dist/delivery/bash-sweep.d.ts +188 -0
- package/dist/delivery/bash-sweep.d.ts.map +1 -0
- package/dist/delivery/bash-sweep.js +610 -0
- package/dist/delivery/bash-sweep.js.map +1 -0
- package/dist/delivery/contract-extractor.d.ts +16 -0
- package/dist/delivery/contract-extractor.d.ts.map +1 -1
- package/dist/delivery/contract-extractor.js +136 -3
- package/dist/delivery/contract-extractor.js.map +1 -1
- package/dist/delivery/mission-acceptance.d.ts.map +1 -1
- package/dist/delivery/mission-acceptance.js +48 -3
- package/dist/delivery/mission-acceptance.js.map +1 -1
- package/dist/delivery/stub-detector.d.ts +150 -0
- package/dist/delivery/stub-detector.d.ts.map +1 -0
- package/dist/delivery/stub-detector.js +474 -0
- package/dist/delivery/stub-detector.js.map +1 -0
- package/dist/delivery/visual-gate.d.ts +58 -0
- package/dist/delivery/visual-gate.d.ts.map +1 -1
- package/dist/delivery/visual-gate.js +87 -3
- package/dist/delivery/visual-gate.js.map +1 -1
- package/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
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
|
+
export interface SweepLimits {
|
|
80
|
+
/** Max files whose CONTENT is baselined. Existence is never capped. */
|
|
81
|
+
maxFiles: number;
|
|
82
|
+
/** Max total bytes of baselined content. */
|
|
83
|
+
maxTotalBytes: number;
|
|
84
|
+
/** Per-file read ceiling; matches the detector's own scan cap. */
|
|
85
|
+
maxFileBytes: number;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* A delivery target is an application, not a monorepo, so these sit far above
|
|
89
|
+
* any real project — they exist so a mission pointed at something huge degrades
|
|
90
|
+
* predictably instead of reading gigabytes inside a turn boundary. Injectable so
|
|
91
|
+
* tests exercise the capped paths for real rather than setting a flag by hand.
|
|
92
|
+
*/
|
|
93
|
+
export declare const DEFAULT_LIMITS: SweepLimits;
|
|
94
|
+
/** Where displaced content goes. Inside `.uap`, so the sweep never re-walks it. */
|
|
95
|
+
export declare const BACKUP_DIR: string;
|
|
96
|
+
export interface BashSweep {
|
|
97
|
+
/** False when bash is disabled for the session: no baseline, no sweep. */
|
|
98
|
+
readonly enabled: boolean;
|
|
99
|
+
/** Root of both walks. */
|
|
100
|
+
readonly projectRoot: string;
|
|
101
|
+
/** Set true once a command has run — the sweep is a no-op until then. */
|
|
102
|
+
bashRan: boolean;
|
|
103
|
+
/** Whether the baseline walk has happened (it is lazy). */
|
|
104
|
+
baselined: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Whether the sweep has already run for this turn. The wrapper calls
|
|
107
|
+
* finishBashSweep on the success path and again from `finally`; making the
|
|
108
|
+
* second call a no-op here — rather than relying on the caller to track it —
|
|
109
|
+
* keeps the double walk out of every bash turn no matter who calls.
|
|
110
|
+
*/
|
|
111
|
+
swept: boolean;
|
|
112
|
+
/** Project-relative path -> content a guarded tool wrote this turn. */
|
|
113
|
+
readonly authorised: Map<string, string>;
|
|
114
|
+
/** Project-relative path -> content at the first command. May be partial. */
|
|
115
|
+
readonly baseline: Map<string, string>;
|
|
116
|
+
/** EVERY path that existed at the first command. Never capped — see doc. */
|
|
117
|
+
readonly known: Set<string>;
|
|
118
|
+
/**
|
|
119
|
+
* Size and mtime of every known path. Two jobs: an unbaselined file's CHANGE
|
|
120
|
+
* stays visible (so coverage warnings name files that actually moved), and the
|
|
121
|
+
* sweep can skip re-reading a file that demonstrably did not move — otherwise
|
|
122
|
+
* it re-reads the entire content baseline on a turn where the shell touched
|
|
123
|
+
* one file.
|
|
124
|
+
*/
|
|
125
|
+
readonly statAt: Map<string, {
|
|
126
|
+
size: number;
|
|
127
|
+
mtimeMs: number;
|
|
128
|
+
}>;
|
|
129
|
+
/** True when the file-count/byte budget cut the content baseline short. */
|
|
130
|
+
truncated: boolean;
|
|
131
|
+
readonly limits: SweepLimits;
|
|
132
|
+
}
|
|
133
|
+
export interface SweepOutcome {
|
|
134
|
+
/** Paths reverted to their authorised content (backed up first). */
|
|
135
|
+
reverted: string[];
|
|
136
|
+
/** Paths moved into the backup — they did not exist before the turn. */
|
|
137
|
+
removed: string[];
|
|
138
|
+
/** Paths that CHANGED but could not be attributed; left untouched. */
|
|
139
|
+
uncovered: string[];
|
|
140
|
+
/** Paths the sweep decided to act on but could not. */
|
|
141
|
+
failed: string[];
|
|
142
|
+
/** Model-facing note, '' when there is nothing to say. */
|
|
143
|
+
note: string;
|
|
144
|
+
}
|
|
145
|
+
/** A sweep that never fires — for callers with bash disabled. */
|
|
146
|
+
export declare function disabledSweep(): BashSweep;
|
|
147
|
+
/**
|
|
148
|
+
* Arm the sweep for a turn. Deliberately does NOT walk anything.
|
|
149
|
+
*
|
|
150
|
+
* The baseline is captured lazily, at the first command, so a bash-enabled
|
|
151
|
+
* session that does not actually shell out in a given turn pays nothing.
|
|
152
|
+
* Deferring is safe because of what `authorised` holds: anything that changed
|
|
153
|
+
* BEFORE the first command can only have come from a guarded tool, and those
|
|
154
|
+
* writes are recorded by content as they happen.
|
|
155
|
+
*/
|
|
156
|
+
export declare function beginBashSweep(projectRoot: string, enabled: boolean, limits?: SweepLimits): BashSweep;
|
|
157
|
+
/**
|
|
158
|
+
* Arm for a command: mark that the shell ran, and capture the baseline once.
|
|
159
|
+
*
|
|
160
|
+
* Called from the run_bash handler BEFORE the spawn — a command that times out
|
|
161
|
+
* or is killed may still have written files, and a sweep with nothing to compare
|
|
162
|
+
* against would be worse than none, reporting "checked" over the messiest case.
|
|
163
|
+
*/
|
|
164
|
+
export declare function armSweepForCommand(sweep: BashSweep): void;
|
|
165
|
+
/** Record what a guarded tool wrote, so a later shell write is distinguishable. */
|
|
166
|
+
export declare function recordAuthorisedWrite(sweep: BashSweep, rel: string, content: string): void;
|
|
167
|
+
/**
|
|
168
|
+
* Compare the tree against the baseline and undo unattributed stub writes.
|
|
169
|
+
* Returns an empty outcome when bash never ran, so the common path is a boolean.
|
|
170
|
+
*/
|
|
171
|
+
export declare function finishBashSweep(sweep: BashSweep): SweepOutcome;
|
|
172
|
+
/**
|
|
173
|
+
* Put the note FIRST — but never ahead of a protocol marker.
|
|
174
|
+
*
|
|
175
|
+
* The retry prompt includes the previous output through `truncateHead`, which
|
|
176
|
+
* keeps the leading 3000 characters, so a note appended after a long turn summary
|
|
177
|
+
* is exactly the part that gets cut. That argues for prepending.
|
|
178
|
+
*
|
|
179
|
+
* Against it: `decodeBudgetStop` only recognises the context-budget marker within
|
|
180
|
+
* the first 512 characters, and this note routinely exceeds that on its own. A
|
|
181
|
+
* budget-stopped turn that was also swept would therefore stop being recognised
|
|
182
|
+
* as budget-stopped — which silently disables the epic controller's rail-sizing
|
|
183
|
+
* split, on exactly the long shell-using sessions that blow the rail. So when the
|
|
184
|
+
* output carries that marker, the note goes after the marker's own line: the
|
|
185
|
+
* marker keeps position 0, and the note is still near the front.
|
|
186
|
+
*/
|
|
187
|
+
export declare function prependSweepNote(output: string, outcome: SweepOutcome): string;
|
|
188
|
+
//# sourceMappingURL=bash-sweep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-sweep.d.ts","sourceRoot":"","sources":["../../src/delivery/bash-sweep.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AA+DH,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,WAI5B,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,UAAU,QAAoC,CAAC;AAE5D,MAAM,WAAW,SAAS;IACxB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,0BAA0B;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,yEAAyE;IACzE,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,KAAK,EAAE,OAAO,CAAC;IACf,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChE,2EAA2E;IAC3E,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,oEAAoE;IACpE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,wEAAwE;IACxE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sEAAsE;IACtE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,uDAAuD;IACvD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;CACd;AAOD,iEAAiE;AACjE,wBAAgB,aAAa,IAAI,SAAS,CAczC;AAsGD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,OAAO,EAChB,MAAM,GAAE,WAA4B,GACnC,SAAS,CAuBX;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAuBzD;AAED,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1F;AA+ED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,CAgH9D;AA+DD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CAS9E"}
|