@miller-tech/uap 1.26.6 → 1.28.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.
Files changed (41) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/bin/cli.js +20 -0
  3. package/dist/bin/cli.js.map +1 -1
  4. package/dist/cli/deliver.d.ts +21 -0
  5. package/dist/cli/deliver.d.ts.map +1 -0
  6. package/dist/cli/deliver.js +197 -0
  7. package/dist/cli/deliver.js.map +1 -0
  8. package/dist/delivery/applier.d.ts +50 -0
  9. package/dist/delivery/applier.d.ts.map +1 -0
  10. package/dist/delivery/applier.js +258 -0
  11. package/dist/delivery/applier.js.map +1 -0
  12. package/dist/delivery/convergence-loop.d.ts +143 -0
  13. package/dist/delivery/convergence-loop.d.ts.map +1 -0
  14. package/dist/delivery/convergence-loop.js +301 -0
  15. package/dist/delivery/convergence-loop.js.map +1 -0
  16. package/dist/delivery/critic.d.ts +35 -0
  17. package/dist/delivery/critic.d.ts.map +1 -0
  18. package/dist/delivery/critic.js +77 -0
  19. package/dist/delivery/critic.js.map +1 -0
  20. package/dist/delivery/explorer.d.ts +77 -0
  21. package/dist/delivery/explorer.d.ts.map +1 -0
  22. package/dist/delivery/explorer.js +166 -0
  23. package/dist/delivery/explorer.js.map +1 -0
  24. package/dist/delivery/index.d.ts +15 -0
  25. package/dist/delivery/index.d.ts.map +1 -0
  26. package/dist/delivery/index.js +15 -0
  27. package/dist/delivery/index.js.map +1 -0
  28. package/dist/delivery/judge.d.ts +33 -0
  29. package/dist/delivery/judge.d.ts.map +1 -0
  30. package/dist/delivery/judge.js +70 -0
  31. package/dist/delivery/judge.js.map +1 -0
  32. package/dist/delivery/verifier-ladder.d.ts +78 -0
  33. package/dist/delivery/verifier-ladder.d.ts.map +1 -0
  34. package/dist/delivery/verifier-ladder.js +213 -0
  35. package/dist/delivery/verifier-ladder.js.map +1 -0
  36. package/dist/models/openai-compat-client.d.ts +34 -0
  37. package/dist/models/openai-compat-client.d.ts.map +1 -0
  38. package/dist/models/openai-compat-client.js +82 -0
  39. package/dist/models/openai-compat-client.js.map +1 -0
  40. package/package.json +1 -1
  41. package/tools/agents/docker-compose.qdrant.yml +7 -1
@@ -0,0 +1,258 @@
1
+ /**
2
+ * File-block Applier
3
+ *
4
+ * Materializes model output into the project tree. Models emit complete
5
+ * files as fenced blocks:
6
+ *
7
+ * ```file:relative/path/from/root.ts
8
+ * <entire file content>
9
+ * ```
10
+ *
11
+ * Whole-file emission is deliberate: small models are far more reliable
12
+ * emitting complete files than unified diffs. Fences of 3+ backticks are
13
+ * supported so file contents containing ``` can be wrapped in ````.
14
+ */
15
+ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmdirSync, rmSync, writeFileSync, } from 'fs';
16
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'path';
17
+ /** Max bytes a single emitted file may have (guards runaway generations). */
18
+ const MAX_FILE_BYTES = 1_000_000;
19
+ const FILE_BLOCK_RE = /^(`{3,})file:([^\n]+)\n([\s\S]*?)^\1\s*$/gm;
20
+ /**
21
+ * Paths the model is never allowed to write. The harness executes project
22
+ * scripts during gate verification, so a model-supplied build/test/lint
23
+ * command in package.json, a git/husky hook, or a CI workflow would be
24
+ * arbitrary code execution against the host. These are blocked outright;
25
+ * delivering a change that legitimately needs them is out of scope for an
26
+ * autonomous loop and must go through a human.
27
+ */
28
+ const PROTECTED_SEGMENTS = new Set([
29
+ '.git',
30
+ '.husky',
31
+ '.github',
32
+ '.gitlab',
33
+ '.circleci',
34
+ 'node_modules',
35
+ ]);
36
+ const PROTECTED_BASENAMES = new Set([
37
+ 'package.json',
38
+ 'package-lock.json',
39
+ 'npm-shrinkwrap.json',
40
+ 'yarn.lock',
41
+ 'pnpm-lock.yaml',
42
+ '.npmrc',
43
+ '.yarnrc',
44
+ '.yarnrc.yml',
45
+ ]);
46
+ /** Extract file blocks from model output without writing anything. */
47
+ export function parseFileBlocks(output) {
48
+ const blocks = [];
49
+ for (const match of output.matchAll(FILE_BLOCK_RE)) {
50
+ const path = match[2].trim();
51
+ if (!path)
52
+ continue;
53
+ blocks.push({ path, content: match[3] });
54
+ }
55
+ return blocks;
56
+ }
57
+ /**
58
+ * Resolve `target`'s parent to its real (symlink-followed) location and
59
+ * confirm it stays inside the project root. Lexical checks alone let a
60
+ * pre-existing symlink inside the repo redirect a write outside it.
61
+ */
62
+ function realParentEscapes(target, realRoot) {
63
+ let dir = dirname(target);
64
+ // Walk up to the nearest existing ancestor (target/intermediate dirs may
65
+ // not exist yet) and realpath that.
66
+ while (!existsSync(dir)) {
67
+ const parent = dirname(dir);
68
+ if (parent === dir)
69
+ break;
70
+ dir = parent;
71
+ }
72
+ let realDir;
73
+ try {
74
+ realDir = realpathSync(dir);
75
+ }
76
+ catch {
77
+ return true;
78
+ }
79
+ const rel = relative(realRoot, realDir);
80
+ return rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel);
81
+ }
82
+ function validatePath(blockPath, projectRoot, realRoot) {
83
+ if (isAbsolute(blockPath))
84
+ return 'absolute paths are not allowed';
85
+ const target = resolve(projectRoot, blockPath);
86
+ const rel = relative(resolve(projectRoot), target);
87
+ if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
88
+ return 'path escapes the project root';
89
+ }
90
+ // Block protected segments anywhere in the path (case-insensitive), and
91
+ // protected basenames. Closes config/hook/CI-driven code execution.
92
+ const segments = rel.split(sep);
93
+ for (const seg of segments) {
94
+ if (PROTECTED_SEGMENTS.has(seg.toLowerCase())) {
95
+ return `writes into ${seg} are not allowed`;
96
+ }
97
+ }
98
+ const base = segments[segments.length - 1].toLowerCase();
99
+ if (PROTECTED_BASENAMES.has(base)) {
100
+ return `writes to ${base} are not allowed (would alter executed scripts)`;
101
+ }
102
+ // Reject writing through an existing symlink, and any symlinked ancestor.
103
+ try {
104
+ if (existsSync(target) && lstatSync(target).isSymbolicLink()) {
105
+ return 'target is a symlink';
106
+ }
107
+ }
108
+ catch {
109
+ return 'could not stat target';
110
+ }
111
+ if (realParentEscapes(target, realRoot)) {
112
+ return 'path resolves outside the project root via a symlink';
113
+ }
114
+ return null;
115
+ }
116
+ function realRootOf(projectRoot) {
117
+ try {
118
+ return realpathSync(resolve(projectRoot));
119
+ }
120
+ catch {
121
+ return resolve(projectRoot);
122
+ }
123
+ }
124
+ /**
125
+ * Default applier: parse file blocks and write them under projectRoot.
126
+ * Paths are validated against traversal and .git writes; oversized files
127
+ * are rejected rather than truncated.
128
+ */
129
+ export function applyFileBlocks(output, projectRoot) {
130
+ const blocks = parseFileBlocks(output);
131
+ if (blocks.length === 0) {
132
+ return {
133
+ filesWritten: [],
134
+ rejected: [],
135
+ error: 'No file blocks found in the output. Emit every created/modified file as a fenced block: ```file:relative/path … ```',
136
+ };
137
+ }
138
+ const realRoot = realRootOf(projectRoot);
139
+ const filesWritten = [];
140
+ const rejected = [];
141
+ for (const block of blocks) {
142
+ const invalid = validatePath(block.path, projectRoot, realRoot);
143
+ if (invalid) {
144
+ rejected.push({ path: block.path, reason: invalid });
145
+ continue;
146
+ }
147
+ if (Buffer.byteLength(block.content, 'utf-8') > MAX_FILE_BYTES) {
148
+ rejected.push({ path: block.path, reason: `file exceeds ${MAX_FILE_BYTES} bytes` });
149
+ continue;
150
+ }
151
+ const target = join(projectRoot, block.path);
152
+ mkdirSync(dirname(target), { recursive: true });
153
+ writeFileSync(target, block.content, 'utf-8');
154
+ filesWritten.push(block.path);
155
+ }
156
+ return { filesWritten, rejected };
157
+ }
158
+ /** Topmost directory segments that did not exist before applying `paths`. */
159
+ function newDirsFor(paths, projectRoot) {
160
+ const created = new Set();
161
+ for (const path of paths) {
162
+ const segments = path.split(sep);
163
+ let current = projectRoot;
164
+ for (let i = 0; i < segments.length - 1; i++) {
165
+ current = join(current, segments[i]);
166
+ if (!existsSync(current))
167
+ created.add(current);
168
+ }
169
+ }
170
+ // Deepest first so rmdir succeeds bottom-up
171
+ return [...created].sort((a, b) => b.length - a.length);
172
+ }
173
+ /**
174
+ * Apply file blocks with a snapshot of the prior state of every target, so
175
+ * the application can be undone. Used by the explorer to evaluate competing
176
+ * candidates against the same baseline tree without git machinery.
177
+ */
178
+ export function applyFileBlocksWithRollback(output, projectRoot) {
179
+ const blocks = parseFileBlocks(output);
180
+ const realRoot = realRootOf(projectRoot);
181
+ // Snapshot prior content (or absence) of every valid target, and the set
182
+ // of directories that do not yet exist, BEFORE writing anything.
183
+ const snapshots = new Map();
184
+ const validPaths = [];
185
+ for (const block of blocks) {
186
+ if (validatePath(block.path, projectRoot, realRoot))
187
+ continue;
188
+ const target = join(projectRoot, block.path);
189
+ if (!snapshots.has(block.path)) {
190
+ snapshots.set(block.path, existsSync(target) ? readFileSync(target, 'utf-8') : null);
191
+ validPaths.push(block.path);
192
+ }
193
+ }
194
+ const newDirs = newDirsFor(validPaths, projectRoot);
195
+ // Track what actually hit disk so an exception mid-apply still rolls back.
196
+ const written = [];
197
+ let restored = false;
198
+ const restore = () => {
199
+ if (restored)
200
+ return;
201
+ restored = true;
202
+ for (const path of written) {
203
+ const target = join(projectRoot, path);
204
+ const prior = snapshots.get(path);
205
+ if (prior === null || prior === undefined) {
206
+ rmSync(target, { force: true });
207
+ }
208
+ else {
209
+ writeFileSync(target, prior, 'utf-8');
210
+ }
211
+ }
212
+ for (const dir of newDirs) {
213
+ try {
214
+ rmdirSync(dir); // empty-only — throws (caught) if a surviving file remains
215
+ }
216
+ catch {
217
+ // Non-empty (a surviving file lives here) or already gone — leave it.
218
+ }
219
+ }
220
+ };
221
+ let result;
222
+ try {
223
+ // Re-implement the write loop here so `written` reflects partial progress.
224
+ if (blocks.length === 0) {
225
+ result = {
226
+ filesWritten: [],
227
+ rejected: [],
228
+ error: 'No file blocks found in the output. Emit every created/modified file as a fenced block: ```file:relative/path … ```',
229
+ };
230
+ }
231
+ else {
232
+ const rejected = [];
233
+ for (const block of blocks) {
234
+ const invalid = validatePath(block.path, projectRoot, realRoot);
235
+ if (invalid) {
236
+ rejected.push({ path: block.path, reason: invalid });
237
+ continue;
238
+ }
239
+ if (Buffer.byteLength(block.content, 'utf-8') > MAX_FILE_BYTES) {
240
+ rejected.push({ path: block.path, reason: `file exceeds ${MAX_FILE_BYTES} bytes` });
241
+ continue;
242
+ }
243
+ const target = join(projectRoot, block.path);
244
+ mkdirSync(dirname(target), { recursive: true });
245
+ writeFileSync(target, block.content, 'utf-8');
246
+ if (!written.includes(block.path))
247
+ written.push(block.path);
248
+ }
249
+ result = { filesWritten: [...written], rejected };
250
+ }
251
+ }
252
+ catch (err) {
253
+ restore();
254
+ throw err;
255
+ }
256
+ return { result, restore };
257
+ }
258
+ //# sourceMappingURL=applier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applier.js","sourceRoot":"","sources":["../../src/delivery/applier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Convergence Loop
3
+ *
4
+ * Drives an underlying model through execute → apply → verify → feedback
5
+ * iterations until the project's completion gates (verifier ladder) pass or
6
+ * the turn budget is exhausted.
7
+ *
8
+ * The loop owns pluggable seams so phases extend without breaking changes:
9
+ * - executor: how a prompt becomes model output
10
+ * - applier: how model output is materialized into the project tree
11
+ * - promptBuilder: how instruction/feedback/critique compose a prompt
12
+ * - ladderRunner: how gates are verified
13
+ * - explorer (Phase 2): best-of-N candidate exploration with judge tie-break
14
+ * - critic (Phase 3): structured repair plans replacing raw gate dumps
15
+ * - onIteration: per-turn control hook (Phase 5 escalation controllers)
16
+ */
17
+ import type { GateRung, LadderResult, LadderOptions } from './verifier-ladder.js';
18
+ import type { Applier } from './applier.js';
19
+ import type { StrategySeed } from './explorer.js';
20
+ import type { Judge } from './judge.js';
21
+ import type { Critic } from './critic.js';
22
+ export type LoopExecutor = (prompt: string) => Promise<string>;
23
+ /** Pluggable ladder runner — production uses runLadder, tests inject a stub. */
24
+ export type LadderRunner = (rungs: GateRung[], projectRoot: string, options?: LadderOptions) => LadderResult | Promise<LadderResult>;
25
+ export interface PromptContext {
26
+ instruction: string;
27
+ /** 1-based turn about to execute */
28
+ turn: number;
29
+ /** Model output from the previous turn (truncated) */
30
+ previousOutput?: string;
31
+ /** Gate feedback from the previous turn's ladder run */
32
+ feedback?: string;
33
+ /** Apply-stage error from the previous turn (e.g. no file blocks found) */
34
+ applyError?: string;
35
+ /** Files written by the previous turn */
36
+ previousFiles?: string[];
37
+ /** Structured repair steps from the critic (Phase 3) */
38
+ critique?: string[];
39
+ }
40
+ export type PromptBuilder = (context: PromptContext) => string;
41
+ export interface CandidateSummary {
42
+ id: string;
43
+ strategy: string;
44
+ passed: boolean;
45
+ score: number;
46
+ error?: string;
47
+ }
48
+ export interface IterationRecord {
49
+ /** Real 1-based loop turn (executor-error turns are recorded too) */
50
+ turn: number;
51
+ passed: boolean;
52
+ /** Fraction of gates passed this iteration (0 when the turn never reached verification) */
53
+ score: number;
54
+ gateResults: LadderResult['results'];
55
+ /** Files the applier wrote this turn */
56
+ filesApplied: string[];
57
+ /** Executor failure, if the model call itself errored */
58
+ executorError?: string;
59
+ /** Apply failure, if output could not be materialized */
60
+ applyError?: string;
61
+ /** Strategy seed of the committed candidate (explorer mode) */
62
+ strategy?: string;
63
+ /** All candidates evaluated this turn (explorer mode) */
64
+ candidates?: CandidateSummary[];
65
+ /** Judge rationale when a tie-break decided the winner (explorer mode) */
66
+ judgeRationale?: string;
67
+ durationMs: number;
68
+ }
69
+ export interface DeliveryResult {
70
+ success: boolean;
71
+ /** True when the baseline check found all gates already green (no turns ran) */
72
+ alreadyDelivered: boolean;
73
+ turns: number;
74
+ /** Highest gate score observed across iterations */
75
+ bestScore: number;
76
+ /** Turn that achieved bestScore (0 when no iterations reached verification) */
77
+ bestTurn: number;
78
+ history: IterationRecord[];
79
+ /** Feedback from the final ladder run (or apply/executor error context) */
80
+ finalFeedback: string;
81
+ /** Raw model output from the final turn */
82
+ finalOutput: string;
83
+ totalDurationMs: number;
84
+ }
85
+ export interface ExplorerSettings {
86
+ /** Candidates per turn (default 3) */
87
+ candidates?: number;
88
+ seeds?: StrategySeed[];
89
+ judge?: Judge;
90
+ }
91
+ export interface ConvergenceConfig {
92
+ /** Maximum execute→apply→verify iterations (default 5) */
93
+ maxTurns?: number;
94
+ /** Project whose gates define "delivered" */
95
+ projectRoot: string;
96
+ /** Override auto-detected gates (e.g. subset via CLI --gates) */
97
+ rungs?: GateRung[];
98
+ /** Ladder options forwarded to the runner */
99
+ ladderOptions?: LadderOptions;
100
+ /**
101
+ * Run the ladder once before turn 1 (default true). When the baseline is
102
+ * already green there is nothing to converge on — the loop returns
103
+ * alreadyDelivered without calling the model, preventing false-success
104
+ * outcomes from polluting adaptive routing.
105
+ */
106
+ baselineCheck?: boolean;
107
+ /** Max characters of prior model output included in retry prompts (default 3000) */
108
+ previousOutputChars?: number;
109
+ /** Best-of-N exploration per turn (Phase 2); omit for single-candidate turns */
110
+ explorer?: ExplorerSettings;
111
+ /** Structured critique of failed turns (Phase 3) */
112
+ critic?: Critic;
113
+ /**
114
+ * Called after every iteration. Return 'stop' to abort the loop early
115
+ * (Phase 5 escalation controllers hook in here).
116
+ */
117
+ onIteration?: (record: IterationRecord) => void | 'stop';
118
+ }
119
+ /** Default prompt strategy: lean contract + structured retry context. */
120
+ export declare const defaultPromptBuilder: PromptBuilder;
121
+ export declare class ConvergenceLoop {
122
+ private readonly config;
123
+ private readonly executor;
124
+ private readonly ladderRunner;
125
+ private readonly applier;
126
+ private readonly promptBuilder;
127
+ constructor(config: ConvergenceConfig, executor: LoopExecutor, seams?: {
128
+ ladderRunner?: LadderRunner;
129
+ applier?: Applier;
130
+ promptBuilder?: PromptBuilder;
131
+ });
132
+ /** Single-candidate turn: execute → apply → verify. */
133
+ private runSingleTurn;
134
+ /** Explorer turn: best-of-N candidates, commit the winner (Phase 2). */
135
+ private runExplorerTurn;
136
+ /**
137
+ * Run the loop for an instruction until all required gates pass or the
138
+ * turn budget is exhausted. Returns the full iteration history so callers
139
+ * can record outcomes and inspect convergence behavior.
140
+ */
141
+ deliver(instruction: string): Promise<DeliveryResult>;
142
+ }
143
+ //# sourceMappingURL=convergence-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convergence-loop.d.ts","sourceRoot":"","sources":["../../src/delivery/convergence-loop.ts"],"names":[],"mappings":"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"}