@miller-tech/uap 1.220.8 → 1.220.10
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 +63 -0
- package/dist/delivery/agentic-executor.d.ts.map +1 -1
- package/dist/delivery/agentic-executor.js +166 -18
- package/dist/delivery/agentic-executor.js.map +1 -1
- package/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/src/policies/enforcers/rtk_wrap.py +41 -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
|
@@ -553,16 +553,79 @@ export interface ReadCache {
|
|
|
553
553
|
* parsed back out of the key — a key is now lossy (it may carry an offset
|
|
554
554
|
* suffix), and a path recovered by string-slicing a key is how the grounding
|
|
555
555
|
* builder would silently start reading files that do not exist.
|
|
556
|
+
*
|
|
557
|
+
* `offset` is the 1-based line the model last read this window from. It is
|
|
558
|
+
* carried for the same reason `path` is: the key is lossy, and the grounding
|
|
559
|
+
* builder needs to know WHICH part of a long file the model was working in.
|
|
560
|
+
* Without it the builder can only clip from byte 0 — see
|
|
561
|
+
* buildForcedRoundGrounding.
|
|
556
562
|
*/
|
|
557
563
|
seen: Map<string, {
|
|
558
564
|
mtimeMs: number;
|
|
559
565
|
round: number;
|
|
560
566
|
path?: string;
|
|
567
|
+
offset?: number;
|
|
568
|
+
seq?: number;
|
|
561
569
|
}>;
|
|
570
|
+
/**
|
|
571
|
+
* Monotonic counter stamped onto each entry as `seq`.
|
|
572
|
+
*
|
|
573
|
+
* `round` cannot order these on its own: one round issues many tool calls
|
|
574
|
+
* and they all share its number, and an entry updated in place keeps its
|
|
575
|
+
* original Map position, so insertion order stops being chronological the
|
|
576
|
+
* first time a window is re-read. Both cases sent the grounding anchor to
|
|
577
|
+
* the WRONG window — measured, both landed back on the head clip this
|
|
578
|
+
* whole mechanism exists to avoid.
|
|
579
|
+
*/
|
|
580
|
+
nextSeq?: number;
|
|
562
581
|
}
|
|
563
582
|
/** Bounds for forced-round grounding — see buildForcedRoundGrounding. */
|
|
564
583
|
export declare const FORCED_GROUNDING_MAX_FILES = 3;
|
|
565
584
|
export declare const FORCED_GROUNDING_MAX_BYTES = 24000;
|
|
585
|
+
/**
|
|
586
|
+
* Below this, a per-file share buys so few lines that the block is noise —
|
|
587
|
+
* a mid-word fragment carrying a confident line-range label. Skip the file
|
|
588
|
+
* instead; grounding two files well beats grounding three badly.
|
|
589
|
+
*/
|
|
590
|
+
export declare const FORCED_GROUNDING_MIN_WINDOW_BYTES = 400;
|
|
591
|
+
/**
|
|
592
|
+
* Clip `body` to `maxBytes`, keeping the region around `anchorLine` rather than
|
|
593
|
+
* the head of the file.
|
|
594
|
+
*
|
|
595
|
+
* A head-anchored clip is what made the forced round useless on any file longer
|
|
596
|
+
* than the budget: the model pages toward the region it needs, the read cap
|
|
597
|
+
* strips its read tools, and the grounding then hands it bytes 0..budget —
|
|
598
|
+
* which is the one part of the file it had already moved past. Measured
|
|
599
|
+
* 2026-08-25 on a 952-line, 38 019-byte Rust file: the 24 000-byte head clip
|
|
600
|
+
* ended at line 615, the region under review was 725-732, and the run reported
|
|
601
|
+
* "the file is truncated before line 725; I cannot verify" ten times over forty
|
|
602
|
+
* minutes before giving up.
|
|
603
|
+
*
|
|
604
|
+
* Grows forward from the anchor FIRST (the model reads downward), then spends
|
|
605
|
+
* whatever is left backward toward the head for lead-in context.
|
|
606
|
+
*
|
|
607
|
+
* The order is load-bearing. An earlier version reserved the lead-in before
|
|
608
|
+
* growing forward, which charged the budget for lines above the anchor before
|
|
609
|
+
* it had paid for the anchor itself: on a tight budget the window stopped
|
|
610
|
+
* short and the anchor line was not in its own window — 88% of clipped results
|
|
611
|
+
* in a fuzz sweep. The block then carried a confident "centred on line N"
|
|
612
|
+
* label for an N it did not contain, which is worse than the bare
|
|
613
|
+
* "(truncated)" it replaced. Growing from the anchor makes its presence an
|
|
614
|
+
* invariant whenever any line fits at all.
|
|
615
|
+
*
|
|
616
|
+
* `anchor` is the CLAMPED line the window is actually built around. Callers
|
|
617
|
+
* must label with this, not with the raw request — a past-EOF offset otherwise
|
|
618
|
+
* renders as "your last read at line 5000" on a 952-line file.
|
|
619
|
+
*/
|
|
620
|
+
export declare function clipAroundAnchor(body: string, maxBytes: number, anchorLine?: number): {
|
|
621
|
+
text: string;
|
|
622
|
+
from: number;
|
|
623
|
+
to: number;
|
|
624
|
+
total: number;
|
|
625
|
+
anchor: number;
|
|
626
|
+
clipped: boolean;
|
|
627
|
+
partialLine: boolean;
|
|
628
|
+
};
|
|
566
629
|
/**
|
|
567
630
|
* Build the grounding block for a forced-write round.
|
|
568
631
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentic-executor.d.ts","sourceRoot":"","sources":["../../src/delivery/agentic-executor.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"agentic-executor.d.ts","sourceRoot":"","sources":["../../src/delivery/agentic-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1101,6 +1101,110 @@ function restoreProtected(snap) {
|
|
|
1101
1101
|
/** Bounds for forced-round grounding — see buildForcedRoundGrounding. */
|
|
1102
1102
|
export const FORCED_GROUNDING_MAX_FILES = 3;
|
|
1103
1103
|
export const FORCED_GROUNDING_MAX_BYTES = 24_000;
|
|
1104
|
+
/**
|
|
1105
|
+
* Below this, a per-file share buys so few lines that the block is noise —
|
|
1106
|
+
* a mid-word fragment carrying a confident line-range label. Skip the file
|
|
1107
|
+
* instead; grounding two files well beats grounding three badly.
|
|
1108
|
+
*/
|
|
1109
|
+
export const FORCED_GROUNDING_MIN_WINDOW_BYTES = 400;
|
|
1110
|
+
/** Recency order for read-cache entries — see ReadCache.nextSeq. */
|
|
1111
|
+
function readEntryRank(m) {
|
|
1112
|
+
return [m.seq ?? 0, m.round];
|
|
1113
|
+
}
|
|
1114
|
+
function isMoreRecent(a, b) {
|
|
1115
|
+
const [as, ar] = readEntryRank(a);
|
|
1116
|
+
const [bs, br] = readEntryRank(b);
|
|
1117
|
+
// `>=` on the tie-break, not `>`: entries hand-built without a seq (tests,
|
|
1118
|
+
// and any caller that fills the map directly) fall back to round alone, and
|
|
1119
|
+
// within one round the LATER call is the one the model made last.
|
|
1120
|
+
return as !== bs ? as > bs : ar >= br;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Clip `body` to `maxBytes`, keeping the region around `anchorLine` rather than
|
|
1124
|
+
* the head of the file.
|
|
1125
|
+
*
|
|
1126
|
+
* A head-anchored clip is what made the forced round useless on any file longer
|
|
1127
|
+
* than the budget: the model pages toward the region it needs, the read cap
|
|
1128
|
+
* strips its read tools, and the grounding then hands it bytes 0..budget —
|
|
1129
|
+
* which is the one part of the file it had already moved past. Measured
|
|
1130
|
+
* 2026-08-25 on a 952-line, 38 019-byte Rust file: the 24 000-byte head clip
|
|
1131
|
+
* ended at line 615, the region under review was 725-732, and the run reported
|
|
1132
|
+
* "the file is truncated before line 725; I cannot verify" ten times over forty
|
|
1133
|
+
* minutes before giving up.
|
|
1134
|
+
*
|
|
1135
|
+
* Grows forward from the anchor FIRST (the model reads downward), then spends
|
|
1136
|
+
* whatever is left backward toward the head for lead-in context.
|
|
1137
|
+
*
|
|
1138
|
+
* The order is load-bearing. An earlier version reserved the lead-in before
|
|
1139
|
+
* growing forward, which charged the budget for lines above the anchor before
|
|
1140
|
+
* it had paid for the anchor itself: on a tight budget the window stopped
|
|
1141
|
+
* short and the anchor line was not in its own window — 88% of clipped results
|
|
1142
|
+
* in a fuzz sweep. The block then carried a confident "centred on line N"
|
|
1143
|
+
* label for an N it did not contain, which is worse than the bare
|
|
1144
|
+
* "(truncated)" it replaced. Growing from the anchor makes its presence an
|
|
1145
|
+
* invariant whenever any line fits at all.
|
|
1146
|
+
*
|
|
1147
|
+
* `anchor` is the CLAMPED line the window is actually built around. Callers
|
|
1148
|
+
* must label with this, not with the raw request — a past-EOF offset otherwise
|
|
1149
|
+
* renders as "your last read at line 5000" on a 952-line file.
|
|
1150
|
+
*/
|
|
1151
|
+
export function clipAroundAnchor(body, maxBytes, anchorLine = 1) {
|
|
1152
|
+
const lines = body.split('\n');
|
|
1153
|
+
// A newline-terminated file — which is nearly every source file — splits to a
|
|
1154
|
+
// phantom trailing ''. Counting it inflates `total` and lets `to` name a line
|
|
1155
|
+
// that does not exist, desyncing these numbers from the ones read_file shows
|
|
1156
|
+
// the model. The whole point of the label is that the two agree.
|
|
1157
|
+
if (lines.length > 1 && lines[lines.length - 1] === '')
|
|
1158
|
+
lines.pop();
|
|
1159
|
+
const total = lines.length;
|
|
1160
|
+
const requested = Number.isFinite(anchorLine) ? Math.trunc(anchorLine) : 1;
|
|
1161
|
+
const anchorIdx = Math.max(0, Math.min(total - 1, requested - 1));
|
|
1162
|
+
const anchor = anchorIdx + 1;
|
|
1163
|
+
if (body.length <= maxBytes) {
|
|
1164
|
+
return { text: body, from: 1, to: total, total, anchor, clipped: false, partialLine: false };
|
|
1165
|
+
}
|
|
1166
|
+
let end = anchorIdx; // exclusive
|
|
1167
|
+
let used = 0;
|
|
1168
|
+
while (end < total) {
|
|
1169
|
+
const cost = lines[end].length + 1;
|
|
1170
|
+
if (used + cost > maxBytes)
|
|
1171
|
+
break;
|
|
1172
|
+
used += cost;
|
|
1173
|
+
end += 1;
|
|
1174
|
+
}
|
|
1175
|
+
// The anchor's own line is wider than the entire budget. Emit its head —
|
|
1176
|
+
// that line, not a neighbour, is the one the model was looking at.
|
|
1177
|
+
if (end === anchorIdx) {
|
|
1178
|
+
return {
|
|
1179
|
+
text: lines[anchorIdx].slice(0, maxBytes),
|
|
1180
|
+
from: anchor,
|
|
1181
|
+
to: anchor,
|
|
1182
|
+
total,
|
|
1183
|
+
anchor,
|
|
1184
|
+
clipped: true,
|
|
1185
|
+
partialLine: true,
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
let start = anchorIdx;
|
|
1189
|
+
while (start > 0) {
|
|
1190
|
+
const cost = lines[start - 1].length + 1;
|
|
1191
|
+
if (used + cost > maxBytes)
|
|
1192
|
+
break;
|
|
1193
|
+
used += cost;
|
|
1194
|
+
start -= 1;
|
|
1195
|
+
}
|
|
1196
|
+
return {
|
|
1197
|
+
text: lines.slice(start, end).join('\n'),
|
|
1198
|
+
from: start + 1,
|
|
1199
|
+
to: end,
|
|
1200
|
+
total,
|
|
1201
|
+
anchor,
|
|
1202
|
+
// Reaching here means the whole body did not fit, so the window is a clip
|
|
1203
|
+
// by construction — full coverage is unreachable on this path.
|
|
1204
|
+
clipped: true,
|
|
1205
|
+
partialLine: false,
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1104
1208
|
/**
|
|
1105
1209
|
* Build the grounding block for a forced-write round.
|
|
1106
1210
|
*
|
|
@@ -1129,21 +1233,34 @@ export function buildForcedRoundGrounding(cache, projectRoot, readFile = (p) =>
|
|
|
1129
1233
|
if (!key.startsWith('read_file:'))
|
|
1130
1234
|
continue;
|
|
1131
1235
|
// Entries carry their path; keys may also carry a window offset, and
|
|
1132
|
-
// several windows of one file must ground it ONCE, not three times.
|
|
1236
|
+
// several windows of one file must ground it ONCE, not three times. The
|
|
1237
|
+
// window that wins is the most recent one — that is where the model was
|
|
1238
|
+
// working when its read tools were taken away, so that is where the clip
|
|
1239
|
+
// must be anchored if the file does not fit whole. Recency is `seq`, not
|
|
1240
|
+
// `round`: see ReadCache.nextSeq for why round alone picks the wrong one.
|
|
1133
1241
|
const rel = meta.path ?? key.slice('read_file:'.length);
|
|
1134
1242
|
const prior = byPath.get(rel);
|
|
1135
|
-
if (!prior || meta
|
|
1136
|
-
byPath.set(rel, { round: meta.round });
|
|
1243
|
+
if (!prior || isMoreRecent(meta, prior)) {
|
|
1244
|
+
byPath.set(rel, { round: meta.round, offset: meta.offset ?? 1, seq: meta.seq });
|
|
1245
|
+
}
|
|
1137
1246
|
}
|
|
1138
1247
|
const recent = [...byPath.entries()]
|
|
1139
|
-
.sort((a, b) => b[1].round - a[1].round)
|
|
1248
|
+
.sort((a, b) => (b[1].seq ?? 0) - (a[1].seq ?? 0) || b[1].round - a[1].round)
|
|
1140
1249
|
.slice(0, FORCED_GROUNDING_MAX_FILES);
|
|
1141
1250
|
if (recent.length === 0)
|
|
1142
1251
|
return undefined;
|
|
1143
1252
|
const blocks = [];
|
|
1144
1253
|
let budget = FORCED_GROUNDING_MAX_BYTES;
|
|
1145
|
-
|
|
1146
|
-
|
|
1254
|
+
let filesLeft = recent.length;
|
|
1255
|
+
for (const [rel, meta] of recent) {
|
|
1256
|
+
// Even share, with whatever earlier files left unspent rolling forward.
|
|
1257
|
+
// A greedy first-come split let file 1 take the whole budget and handed
|
|
1258
|
+
// file 2 a mid-word fragment under a confident line-range label — worse
|
|
1259
|
+
// than omitting it, because the label asserts the fragment is the region
|
|
1260
|
+
// the model needs.
|
|
1261
|
+
const share = Math.floor(budget / filesLeft);
|
|
1262
|
+
filesLeft -= 1;
|
|
1263
|
+
if (share < FORCED_GROUNDING_MIN_WINDOW_BYTES)
|
|
1147
1264
|
break;
|
|
1148
1265
|
let body;
|
|
1149
1266
|
try {
|
|
@@ -1152,9 +1269,25 @@ export function buildForcedRoundGrounding(cache, projectRoot, readFile = (p) =>
|
|
|
1152
1269
|
catch {
|
|
1153
1270
|
continue; // deleted or unreadable since it was read — skip, never throw
|
|
1154
1271
|
}
|
|
1155
|
-
const
|
|
1156
|
-
budget -=
|
|
1157
|
-
|
|
1272
|
+
const win = clipAroundAnchor(body, share, meta.offset);
|
|
1273
|
+
budget -= win.text.length;
|
|
1274
|
+
// Naming the exact range is not cosmetic. A block labelled only
|
|
1275
|
+
// "(truncated)" is indistinguishable from a corrupt read, and the model's
|
|
1276
|
+
// response to that was to declare the whole file unverifiable and stop.
|
|
1277
|
+
// Told which lines it holds, it can act on them and name what is missing.
|
|
1278
|
+
// The anchor named is the CLAMPED one the window was actually built on.
|
|
1279
|
+
let label = '(current content)';
|
|
1280
|
+
if (win.partialLine) {
|
|
1281
|
+
label =
|
|
1282
|
+
`(current content, first ${win.text.length} chars of line ${win.anchor} ` +
|
|
1283
|
+
`of ${win.total} — truncated, that line is wider than the budget)`;
|
|
1284
|
+
}
|
|
1285
|
+
else if (win.clipped) {
|
|
1286
|
+
label =
|
|
1287
|
+
`(current content, lines ${win.from}-${win.to} of ${win.total} — truncated, ` +
|
|
1288
|
+
`centred on your last read at line ${win.anchor})`;
|
|
1289
|
+
}
|
|
1290
|
+
blocks.push(`--- ${rel} ${label} ---\n${win.text}`);
|
|
1158
1291
|
}
|
|
1159
1292
|
if (blocks.length === 0)
|
|
1160
1293
|
return undefined;
|
|
@@ -1163,7 +1296,10 @@ export function buildForcedRoundGrounding(cache, projectRoot, readFile = (p) =>
|
|
|
1163
1296
|
`${blocks.join('\n\n')}\n\n` +
|
|
1164
1297
|
'Base your edit on exactly this text. For edit_file, copy old_string ' +
|
|
1165
1298
|
'verbatim from above and make it long enough to be unique. Do not rewrite a ' +
|
|
1166
|
-
'whole file when a targeted edit will do.'
|
|
1299
|
+
'whole file when a targeted edit will do. Where a block is marked truncated ' +
|
|
1300
|
+
'it still shows the region you were last reading: act on what is shown and ' +
|
|
1301
|
+
'state which line range you would need next — do not report the whole file ' +
|
|
1302
|
+
'as unreadable.');
|
|
1167
1303
|
}
|
|
1168
1304
|
/**
|
|
1169
1305
|
* Read a positive-integer round threshold from the environment.
|
|
@@ -1202,7 +1338,7 @@ export function readCountEnv(name, fallback) {
|
|
|
1202
1338
|
return n;
|
|
1203
1339
|
}
|
|
1204
1340
|
export function newReadCache() {
|
|
1205
|
-
return { seen: new Map() };
|
|
1341
|
+
return { seen: new Map(), nextSeq: 1 };
|
|
1206
1342
|
}
|
|
1207
1343
|
/** mtime of a path, or null when it does not exist / is unreadable. */
|
|
1208
1344
|
function mtimeOf(p) {
|
|
@@ -1228,20 +1364,32 @@ export function repeatReadNote(cache, projectRoot, name, args, round) {
|
|
|
1228
1364
|
// repeat — same path, same window, unchanged on disk — earns the note.
|
|
1229
1365
|
// Both windowed tools, not just read_file: list_dir pages too now, and a
|
|
1230
1366
|
// directory's second page is no more a re-read than a file's.
|
|
1231
|
-
const
|
|
1232
|
-
?
|
|
1233
|
-
:
|
|
1367
|
+
const offsetLine = Number.isFinite(Number(args.offset))
|
|
1368
|
+
? Math.max(1, Math.trunc(Number(args.offset)))
|
|
1369
|
+
: 1;
|
|
1370
|
+
const offsetKey = Number.isFinite(Number(args.offset)) ? `@${offsetLine}` : '';
|
|
1234
1371
|
const key = `${name}:${args.path}${offsetKey}`;
|
|
1235
1372
|
const abs = resolve(projectRoot, args.path);
|
|
1236
1373
|
const mtime = mtimeOf(abs);
|
|
1237
1374
|
const prior = cache.seen.get(key);
|
|
1238
|
-
|
|
1239
|
-
|
|
1375
|
+
const seq = cache.nextSeq ?? 1;
|
|
1376
|
+
cache.nextSeq = seq + 1;
|
|
1377
|
+
const repeated = Boolean(prior && mtime !== null && prior.mtimeMs === mtime);
|
|
1378
|
+
// Record the read BEFORE the repeat check returns. The early return used to
|
|
1379
|
+
// skip this, so a window re-read seven times kept the round of its FIRST
|
|
1380
|
+
// read and lost the recency race to a one-off head read — leaving the
|
|
1381
|
+
// grounding anchored on the head, which is the read-forever profile this
|
|
1382
|
+
// whole mechanism exists to break. A re-read is still a read; it is in fact
|
|
1383
|
+
// the strongest signal of where the model is stuck.
|
|
1384
|
+
const priorRound = prior?.round ?? round;
|
|
1385
|
+
if (mtime !== null) {
|
|
1386
|
+
cache.seen.set(key, { mtimeMs: mtime, round, path: args.path, offset: offsetLine, seq });
|
|
1387
|
+
}
|
|
1388
|
+
if (repeated) {
|
|
1389
|
+
return (`[NOTE: unchanged since you read ${args.path} at round ${priorRound}. ` +
|
|
1240
1390
|
'The content follows anyway — but re-reading it tells you nothing new, so act on it: ' +
|
|
1241
1391
|
'make the edit, or call finish.]');
|
|
1242
1392
|
}
|
|
1243
|
-
if (mtime !== null)
|
|
1244
|
-
cache.seen.set(key, { mtimeMs: mtime, round, path: args.path });
|
|
1245
1393
|
return null;
|
|
1246
1394
|
}
|
|
1247
1395
|
export function runTool(projectRoot, name, args, bashTimeoutMs, protectedFiles, protectGateConfigs, allowBash, contractFiles = EMPTY_SET,
|