@miller-tech/uap 1.187.8 → 1.188.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/cli/deliver.d.ts.map +1 -1
- package/dist/cli/deliver.js +26 -0
- package/dist/cli/deliver.js.map +1 -1
- package/dist/delivery/scope-notice.d.ts +65 -0
- package/dist/delivery/scope-notice.d.ts.map +1 -0
- package/dist/delivery/scope-notice.js +134 -0
- package/dist/delivery/scope-notice.js.map +1 -0
- 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,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Warn when a mission's instruction names files it cannot reach.
|
|
3
|
+
*
|
|
4
|
+
* THE FAILURE THIS CATCHES
|
|
5
|
+
* `safePath` refuses any edit that resolves outside the project root, which is
|
|
6
|
+
* correct and stays. But refusing one path does not stop the model wanting that
|
|
7
|
+
* file — and its next move is the damaging one. Observed live on 2026-08-09 in
|
|
8
|
+
* `cognition-engine`:
|
|
9
|
+
*
|
|
10
|
+
* projectRoot: <repo>/src/rust-pg-ext (the crate)
|
|
11
|
+
* instruction: "... **src/sql/setup.sql** lines 22-29: replace SQL md5 hash
|
|
12
|
+
* functions with Rust XXH64 versions ..."
|
|
13
|
+
*
|
|
14
|
+
* That file is `<repo>/src/sql/setup.sql` — 62KB, outside the crate. The model
|
|
15
|
+
* tried `../sql/setup.sql`, was refused, and then CREATED a 1.5KB stub at
|
|
16
|
+
* `<crate>/src/sql/setup.sql`, which safePath allows because it is inside the
|
|
17
|
+
* root. The guard deflected the model into writing a plausible file in the
|
|
18
|
+
* wrong place: untracked, unreferenced, and easy to mistake for the real edit.
|
|
19
|
+
*
|
|
20
|
+
* The mission was mis-scoped — it should have run at the repo root — and that
|
|
21
|
+
* was knowable before the first turn, from the instruction and the filesystem
|
|
22
|
+
* alone. Saying so up front turns a silent wrong-place write into a fact the
|
|
23
|
+
* model and the operator both get for free.
|
|
24
|
+
*
|
|
25
|
+
* DELIBERATELY ADVISORY. A path in an instruction can be illustrative, a file
|
|
26
|
+
* yet to be created, or prose that merely looks like a path, so refusing the
|
|
27
|
+
* run on this signal would block legitimate work. It only ever reports paths
|
|
28
|
+
* that are ABSENT under the project root and PRESENT above it — the one
|
|
29
|
+
* combination that cannot be explained by "the model will create it".
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* The enclosing repository root ABOVE `projectRoot`, or null.
|
|
33
|
+
*
|
|
34
|
+
* Null when the project root is itself the repo root — there is nothing above
|
|
35
|
+
* to be out of scope of — and null when no repository encloses it at all.
|
|
36
|
+
* `.git` is a directory in a normal clone and a FILE in a worktree, so both
|
|
37
|
+
* count.
|
|
38
|
+
*/
|
|
39
|
+
export declare function findRepoRoot(projectRoot: string, exists?: (p: string) => boolean): string | null;
|
|
40
|
+
/** A path the instruction asks for that this run cannot reach. */
|
|
41
|
+
export interface UnreachablePath {
|
|
42
|
+
/** The path as written in the instruction. */
|
|
43
|
+
mentioned: string;
|
|
44
|
+
/** Where it actually is, relative to the search root. */
|
|
45
|
+
foundAt: string;
|
|
46
|
+
}
|
|
47
|
+
/** Candidate paths mentioned in free text, de-duplicated, in first-seen order. */
|
|
48
|
+
export declare function mentionedPaths(instruction: string): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Paths the instruction names that are missing under `projectRoot` but present
|
|
51
|
+
* under `searchRoot` (normally the repo root above it).
|
|
52
|
+
*
|
|
53
|
+
* Returns [] when searchRoot is not an ancestor of projectRoot — there is no
|
|
54
|
+
* "above" to look in, so there is nothing this can be confident about.
|
|
55
|
+
*/
|
|
56
|
+
export declare function unreachablePaths(instruction: string, projectRoot: string, searchRoot: string, exists?: (p: string) => boolean): UnreachablePath[];
|
|
57
|
+
/**
|
|
58
|
+
* The prompt/console notice, or null when there is nothing to report.
|
|
59
|
+
*
|
|
60
|
+
* Names the substitute-file failure explicitly. Telling a model only "you
|
|
61
|
+
* cannot reach it" leaves creating a same-named file inside the root as the
|
|
62
|
+
* obvious next move — which is the exact damage observed.
|
|
63
|
+
*/
|
|
64
|
+
export declare function formatScopeNotice(unreachable: readonly UnreachablePath[], projectRoot: string, searchRoot: string): string | null;
|
|
65
|
+
//# sourceMappingURL=scope-notice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-notice.d.ts","sourceRoot":"","sources":["../../src/delivery/scope-notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,MAAM,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAoB,GAC1C,MAAM,GAAG,IAAI,CASf;AAED,kEAAkE;AAClE,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAqBD,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAc5D;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAoB,GAC1C,eAAe,EAAE,CAYnB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,SAAS,eAAe,EAAE,EACvC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAaf"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Warn when a mission's instruction names files it cannot reach.
|
|
3
|
+
*
|
|
4
|
+
* THE FAILURE THIS CATCHES
|
|
5
|
+
* `safePath` refuses any edit that resolves outside the project root, which is
|
|
6
|
+
* correct and stays. But refusing one path does not stop the model wanting that
|
|
7
|
+
* file — and its next move is the damaging one. Observed live on 2026-08-09 in
|
|
8
|
+
* `cognition-engine`:
|
|
9
|
+
*
|
|
10
|
+
* projectRoot: <repo>/src/rust-pg-ext (the crate)
|
|
11
|
+
* instruction: "... **src/sql/setup.sql** lines 22-29: replace SQL md5 hash
|
|
12
|
+
* functions with Rust XXH64 versions ..."
|
|
13
|
+
*
|
|
14
|
+
* That file is `<repo>/src/sql/setup.sql` — 62KB, outside the crate. The model
|
|
15
|
+
* tried `../sql/setup.sql`, was refused, and then CREATED a 1.5KB stub at
|
|
16
|
+
* `<crate>/src/sql/setup.sql`, which safePath allows because it is inside the
|
|
17
|
+
* root. The guard deflected the model into writing a plausible file in the
|
|
18
|
+
* wrong place: untracked, unreferenced, and easy to mistake for the real edit.
|
|
19
|
+
*
|
|
20
|
+
* The mission was mis-scoped — it should have run at the repo root — and that
|
|
21
|
+
* was knowable before the first turn, from the instruction and the filesystem
|
|
22
|
+
* alone. Saying so up front turns a silent wrong-place write into a fact the
|
|
23
|
+
* model and the operator both get for free.
|
|
24
|
+
*
|
|
25
|
+
* DELIBERATELY ADVISORY. A path in an instruction can be illustrative, a file
|
|
26
|
+
* yet to be created, or prose that merely looks like a path, so refusing the
|
|
27
|
+
* run on this signal would block legitimate work. It only ever reports paths
|
|
28
|
+
* that are ABSENT under the project root and PRESENT above it — the one
|
|
29
|
+
* combination that cannot be explained by "the model will create it".
|
|
30
|
+
*/
|
|
31
|
+
import { existsSync } from 'fs';
|
|
32
|
+
import { isAbsolute, join, relative, resolve } from 'path';
|
|
33
|
+
/**
|
|
34
|
+
* The enclosing repository root ABOVE `projectRoot`, or null.
|
|
35
|
+
*
|
|
36
|
+
* Null when the project root is itself the repo root — there is nothing above
|
|
37
|
+
* to be out of scope of — and null when no repository encloses it at all.
|
|
38
|
+
* `.git` is a directory in a normal clone and a FILE in a worktree, so both
|
|
39
|
+
* count.
|
|
40
|
+
*/
|
|
41
|
+
export function findRepoRoot(projectRoot, exists = existsSync) {
|
|
42
|
+
let dir = resolve(projectRoot);
|
|
43
|
+
for (let hops = 0; hops < 64; hops++) {
|
|
44
|
+
const parent = resolve(dir, '..');
|
|
45
|
+
if (parent === dir)
|
|
46
|
+
return null; // hit the filesystem root
|
|
47
|
+
dir = parent;
|
|
48
|
+
if (exists(join(dir, '.git')))
|
|
49
|
+
return dir;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* File-like tokens: a path with a real extension. Requiring an extension is
|
|
55
|
+
* what keeps prose out — "src/sql" or "the setup" are not candidates, and a
|
|
56
|
+
* bare word never is. Markdown emphasis and surrounding punctuation are
|
|
57
|
+
* stripped by the caller before this matches.
|
|
58
|
+
*/
|
|
59
|
+
const PATH_TOKEN = /[\w.-]+(?:\/[\w.-]+)*\.[a-z]{1,6}\b/gi;
|
|
60
|
+
/**
|
|
61
|
+
* Extensions worth checking. An open-ended match turns ordinary sentences
|
|
62
|
+
* ("v1.2", "e.g.", "node.js") into candidate paths; a fixed list keeps the
|
|
63
|
+
* signal specific to source trees.
|
|
64
|
+
*/
|
|
65
|
+
const SOURCE_EXT = new Set([
|
|
66
|
+
'ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs', 'rs', 'py', 'go', 'rb', 'java', 'kt',
|
|
67
|
+
'c', 'h', 'cc', 'cpp', 'hpp', 'cs', 'php', 'swift', 'sql', 'sh', 'bash',
|
|
68
|
+
'toml', 'yaml', 'yml', 'json', 'md', 'css', 'scss', 'html', 'jinja',
|
|
69
|
+
]);
|
|
70
|
+
/** Candidate paths mentioned in free text, de-duplicated, in first-seen order. */
|
|
71
|
+
export function mentionedPaths(instruction) {
|
|
72
|
+
const out = [];
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
for (const raw of instruction.match(PATH_TOKEN) ?? []) {
|
|
75
|
+
// Strip markdown emphasis and trailing punctuation the token may have eaten.
|
|
76
|
+
const tok = raw.replace(/^[*_`]+/, '').replace(/[*_`.,;:)\]]+$/, '');
|
|
77
|
+
if (!tok.includes('/'))
|
|
78
|
+
continue; // a bare filename is too weak a signal
|
|
79
|
+
const ext = tok.split('.').pop()?.toLowerCase() ?? '';
|
|
80
|
+
if (!SOURCE_EXT.has(ext))
|
|
81
|
+
continue;
|
|
82
|
+
if (seen.has(tok))
|
|
83
|
+
continue;
|
|
84
|
+
seen.add(tok);
|
|
85
|
+
out.push(tok);
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Paths the instruction names that are missing under `projectRoot` but present
|
|
91
|
+
* under `searchRoot` (normally the repo root above it).
|
|
92
|
+
*
|
|
93
|
+
* Returns [] when searchRoot is not an ancestor of projectRoot — there is no
|
|
94
|
+
* "above" to look in, so there is nothing this can be confident about.
|
|
95
|
+
*/
|
|
96
|
+
export function unreachablePaths(instruction, projectRoot, searchRoot, exists = existsSync) {
|
|
97
|
+
const rel = relative(resolve(searchRoot), resolve(projectRoot));
|
|
98
|
+
if (rel === '' || rel.startsWith('..') || isAbsolute(rel))
|
|
99
|
+
return [];
|
|
100
|
+
const found = [];
|
|
101
|
+
for (const p of mentionedPaths(instruction)) {
|
|
102
|
+
if (isAbsolute(p))
|
|
103
|
+
continue; // an absolute path is not a scope mistake
|
|
104
|
+
if (exists(join(projectRoot, p)))
|
|
105
|
+
continue; // reachable — nothing to say
|
|
106
|
+
if (!exists(join(searchRoot, p)))
|
|
107
|
+
continue; // absent everywhere: to be created
|
|
108
|
+
found.push({ mentioned: p, foundAt: join(relative(projectRoot, searchRoot), p) });
|
|
109
|
+
}
|
|
110
|
+
return found;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The prompt/console notice, or null when there is nothing to report.
|
|
114
|
+
*
|
|
115
|
+
* Names the substitute-file failure explicitly. Telling a model only "you
|
|
116
|
+
* cannot reach it" leaves creating a same-named file inside the root as the
|
|
117
|
+
* obvious next move — which is the exact damage observed.
|
|
118
|
+
*/
|
|
119
|
+
export function formatScopeNotice(unreachable, projectRoot, searchRoot) {
|
|
120
|
+
if (unreachable.length === 0)
|
|
121
|
+
return null;
|
|
122
|
+
const lines = unreachable.map((u) => ` - ${u.mentioned} (actually at ${u.foundAt})`);
|
|
123
|
+
return [
|
|
124
|
+
'SCOPE NOTICE — this task names files that are OUTSIDE the project root you are running in.',
|
|
125
|
+
`Project root: ${projectRoot}`,
|
|
126
|
+
lines.join('\n'),
|
|
127
|
+
'',
|
|
128
|
+
'You cannot edit them: every path is resolved inside the project root, so `../` is refused.',
|
|
129
|
+
'Do NOT create a same-named file inside the project root as a substitute — it would be a new,',
|
|
130
|
+
'unreferenced file, not the edit that was asked for. Complete the parts that ARE in scope, and',
|
|
131
|
+
`report the rest as out of scope. If they are essential, the run belongs at ${searchRoot}.`,
|
|
132
|
+
].join('\n');
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=scope-notice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-notice.js","sourceRoot":"","sources":["../../src/delivery/scope-notice.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|