@miller-tech/uap 2.1.7 → 2.11.1

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 (143) hide show
  1. package/README.md +7 -6
  2. package/config/capacity-policy.json +32 -0
  3. package/config/classify-thresholds.json +9 -0
  4. package/config/supervise-policy.json +13 -0
  5. package/dist/.tsbuildinfo +1 -1
  6. package/dist/bin/cli.js +87 -2
  7. package/dist/bin/cli.js.map +1 -1
  8. package/dist/capacity/policy.d.ts +67 -0
  9. package/dist/capacity/policy.d.ts.map +1 -0
  10. package/dist/capacity/policy.js +133 -0
  11. package/dist/capacity/policy.js.map +1 -0
  12. package/dist/capacity/probe.d.ts +23 -0
  13. package/dist/capacity/probe.d.ts.map +1 -0
  14. package/dist/capacity/probe.js +93 -0
  15. package/dist/capacity/probe.js.map +1 -0
  16. package/dist/classify/baseline.d.ts +24 -0
  17. package/dist/classify/baseline.d.ts.map +1 -0
  18. package/dist/classify/baseline.js +140 -0
  19. package/dist/classify/baseline.js.map +1 -0
  20. package/dist/classify/index.d.ts +30 -0
  21. package/dist/classify/index.d.ts.map +1 -0
  22. package/dist/classify/index.js +84 -0
  23. package/dist/classify/index.js.map +1 -0
  24. package/dist/classify/shadow.d.ts +34 -0
  25. package/dist/classify/shadow.d.ts.map +1 -0
  26. package/dist/classify/shadow.js +33 -0
  27. package/dist/classify/shadow.js.map +1 -0
  28. package/dist/classify/types.d.ts +64 -0
  29. package/dist/classify/types.d.ts.map +1 -0
  30. package/dist/classify/types.js +37 -0
  31. package/dist/classify/types.js.map +1 -0
  32. package/dist/cli/classify.d.ts +24 -0
  33. package/dist/cli/classify.d.ts.map +1 -0
  34. package/dist/cli/classify.js +94 -0
  35. package/dist/cli/classify.js.map +1 -0
  36. package/dist/cli/deliver.d.ts.map +1 -1
  37. package/dist/cli/deliver.js +26 -0
  38. package/dist/cli/deliver.js.map +1 -1
  39. package/dist/cli/doctor.d.ts +9 -0
  40. package/dist/cli/doctor.d.ts.map +1 -0
  41. package/dist/cli/doctor.js +49 -0
  42. package/dist/cli/doctor.js.map +1 -0
  43. package/dist/cli/review.d.ts +12 -0
  44. package/dist/cli/review.d.ts.map +1 -0
  45. package/dist/cli/review.js +118 -0
  46. package/dist/cli/review.js.map +1 -0
  47. package/dist/cli/supervise.d.ts +8 -0
  48. package/dist/cli/supervise.d.ts.map +1 -0
  49. package/dist/cli/supervise.js +76 -0
  50. package/dist/cli/supervise.js.map +1 -0
  51. package/dist/delivery/convergence-loop.d.ts +8 -0
  52. package/dist/delivery/convergence-loop.d.ts.map +1 -1
  53. package/dist/delivery/convergence-loop.js +1 -0
  54. package/dist/delivery/convergence-loop.js.map +1 -1
  55. package/dist/delivery/delivery-result.d.ts.map +1 -1
  56. package/dist/delivery/delivery-result.js +5 -0
  57. package/dist/delivery/delivery-result.js.map +1 -1
  58. package/dist/delivery/gate-evidence.d.ts +143 -0
  59. package/dist/delivery/gate-evidence.d.ts.map +1 -0
  60. package/dist/delivery/gate-evidence.js +239 -0
  61. package/dist/delivery/gate-evidence.js.map +1 -0
  62. package/dist/delivery/index.d.ts +1 -0
  63. package/dist/delivery/index.d.ts.map +1 -1
  64. package/dist/delivery/index.js +1 -0
  65. package/dist/delivery/index.js.map +1 -1
  66. package/dist/evals/routing-corpus.d.ts +35 -0
  67. package/dist/evals/routing-corpus.d.ts.map +1 -0
  68. package/dist/evals/routing-corpus.js +142 -0
  69. package/dist/evals/routing-corpus.js.map +1 -0
  70. package/dist/evals/routing-eval.d.ts +104 -0
  71. package/dist/evals/routing-eval.d.ts.map +1 -0
  72. package/dist/evals/routing-eval.js +231 -0
  73. package/dist/evals/routing-eval.js.map +1 -0
  74. package/dist/evals/tfidf.d.ts +35 -0
  75. package/dist/evals/tfidf.d.ts.map +1 -0
  76. package/dist/evals/tfidf.js +132 -0
  77. package/dist/evals/tfidf.js.map +1 -0
  78. package/dist/review/prepass.d.ts +88 -0
  79. package/dist/review/prepass.d.ts.map +1 -0
  80. package/dist/review/prepass.js +428 -0
  81. package/dist/review/prepass.js.map +1 -0
  82. package/dist/review/visual-captures.d.ts +60 -0
  83. package/dist/review/visual-captures.d.ts.map +1 -0
  84. package/dist/review/visual-captures.js +222 -0
  85. package/dist/review/visual-captures.js.map +1 -0
  86. package/dist/supervise/assess.d.ts +73 -0
  87. package/dist/supervise/assess.d.ts.map +1 -0
  88. package/dist/supervise/assess.js +215 -0
  89. package/dist/supervise/assess.js.map +1 -0
  90. package/dist/supervise/config.d.ts +13 -0
  91. package/dist/supervise/config.d.ts.map +1 -0
  92. package/dist/supervise/config.js +81 -0
  93. package/dist/supervise/config.js.map +1 -0
  94. package/dist/supervise/loop.d.ts +78 -0
  95. package/dist/supervise/loop.d.ts.map +1 -0
  96. package/dist/supervise/loop.js +311 -0
  97. package/dist/supervise/loop.js.map +1 -0
  98. package/dist/supervise/observe.d.ts +31 -0
  99. package/dist/supervise/observe.d.ts.map +1 -0
  100. package/dist/supervise/observe.js +172 -0
  101. package/dist/supervise/observe.js.map +1 -0
  102. package/dist/supervise/policy.d.ts +16 -0
  103. package/dist/supervise/policy.d.ts.map +1 -0
  104. package/dist/supervise/policy.js +147 -0
  105. package/dist/supervise/policy.js.map +1 -0
  106. package/dist/supervise/types.d.ts +109 -0
  107. package/dist/supervise/types.d.ts.map +1 -0
  108. package/dist/supervise/types.js +11 -0
  109. package/dist/supervise/types.js.map +1 -0
  110. package/docs/INDEX.md +15 -4
  111. package/docs/architecture/adr/0001-automode-risk-floor.md +75 -0
  112. package/docs/architecture/adr/0002-evidence-bound-ship.md +81 -0
  113. package/docs/guides/CAPACITY_POLICY.md +99 -0
  114. package/docs/performance/automode-risk-calibration.md +72 -0
  115. package/docs/performance/reliability-ladder.md +78 -0
  116. package/docs/plans/system1-uplift-2026-09-18.md +190 -0
  117. package/docs/reference/automode-risk.md +134 -0
  118. package/docs/reference/evidence-bound-ship.md +146 -0
  119. package/docs/reference/review-prepass.md +77 -0
  120. package/docs/reference/routing-evals.md +71 -0
  121. package/docs/reference/semantic-supervisor.md +140 -0
  122. package/docs/reference/system1-classifier.md +103 -0
  123. package/docs/reference/visual-captures.md +113 -0
  124. package/package.json +5 -3
  125. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  126. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-312.pyc +0 -0
  127. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-312.pyc +0 -0
  128. package/src/policies/enforcers/enforcement_self_protect.py +25 -2
  129. package/src/policies/enforcers/evidence_bound_ship.py +257 -0
  130. package/src/policies/enforcers/expert_review_required.py +120 -0
  131. package/src/policies/schemas/policies/evidence-bound-ship.md +106 -0
  132. package/src/policies/schemas/policies/visual-verification.md +8 -0
  133. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  134. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  135. package/tools/agents/scripts/anthropic_proxy.py +782 -19
  136. package/tools/agents/scripts/project_telemetry.py +52 -0
  137. package/tools/agents/scripts/tool_risk.py +789 -0
  138. package/tools/agents/scripts/tool_risk_calibrate.py +255 -0
  139. package/tools/agents/tests/fixtures/tool_risk_cases.json +124 -0
  140. package/tools/agents/tests/test_enforcer_escape_hatches.py +13 -0
  141. package/tools/agents/tests/test_gate_evidence.py +56 -0
  142. package/tools/agents/tests/test_tool_risk.py +521 -0
  143. package/tools/agents/tests/test_verbatim_compact.py +629 -0
@@ -0,0 +1,78 @@
1
+ import { type ClassifierLike } from './assess.js';
2
+ import type { Decision, Observation, SupervisorAction, SupervisorConfig, SupervisorState } from './types.js';
3
+ export declare const SUPERVISE_STATE_VERSION = 1;
4
+ /** Consecutive unanswered ESCALATEs before the watcher gives up. */
5
+ export declare const MAX_SUPPRESSED_ESCALATIONS = 20;
6
+ export interface SupervisorEvent {
7
+ v: 1;
8
+ ts: string;
9
+ runId: string;
10
+ kind: 'assessment' | 'action';
11
+ action: SupervisorAction;
12
+ reason: string;
13
+ /** True when the action was a repeat of the last one and side effects were skipped. */
14
+ suppressed?: boolean;
15
+ evidence: Record<string, unknown>;
16
+ /** Dimension name → value AND who asserted it (heuristic | classifier). */
17
+ dimensions: Record<string, {
18
+ value: boolean | number;
19
+ source: 'heuristic' | 'classifier';
20
+ }>;
21
+ }
22
+ export declare function superviseDir(projectRoot: string, runId: string): string;
23
+ export declare function supervisorStatePath(projectRoot: string, runId: string): string;
24
+ export declare function supervisorEventsPath(projectRoot: string, runId: string): string;
25
+ export declare function freshSupervisorState(runId: string, nowIso: string): SupervisorState;
26
+ /**
27
+ * Load persisted supervisor state, or a fresh one. A MISSING file is normal
28
+ * (first watch); a file that EXISTS but is unparseable or invalid is not —
29
+ * warn loudly, then recover fresh (fail closed means never silently dropping
30
+ * the retry/verify budgets a previous watcher already spent).
31
+ */
32
+ export declare function loadSupervisorState(projectRoot: string, runId: string, nowIso: string, warn?: (message: string) => void): SupervisorState;
33
+ /**
34
+ * Persist state atomically (tmp + rename), mode 0600 — the embedded
35
+ * lastDecision can carry log-tail evidence. Fail-soft (false) on ordinary IO
36
+ * errors; SupervisorError (path discipline) always propagates.
37
+ */
38
+ export declare function saveSupervisorState(projectRoot: string, state: SupervisorState): boolean;
39
+ /** Append one v-stamped event record (file created mode 0600). Fail-soft, except SupervisorError. */
40
+ export declare function appendSupervisorEvent(projectRoot: string, runId: string, event: SupervisorEvent): boolean;
41
+ /**
42
+ * The assessment cadence, PURE: a state change may trigger an assessment once
43
+ * the debounce has elapsed; a heartbeat forces one once the interval has.
44
+ */
45
+ export declare function shouldAssess(args: {
46
+ changed: boolean;
47
+ sinceLastMs: number;
48
+ debounceMs: number;
49
+ intervalMs: number;
50
+ }): boolean;
51
+ export interface RunSupervisorOpts {
52
+ projectRoot: string;
53
+ /** Run id or 'latest'. */
54
+ runId: string;
55
+ once?: boolean;
56
+ classifier?: ClassifierLike;
57
+ /** Injected config (tests); defaults to the packaged reviewed policy. */
58
+ config?: SupervisorConfig;
59
+ /** CLI --interval-ms override; clamped to >= debounceMs. */
60
+ intervalOverrideMs?: number;
61
+ now?: () => number;
62
+ sleep?: (ms: number) => Promise<void>;
63
+ /** Sink for ESCALATE/STOP warnings; defaults to console.warn. */
64
+ warn?: (message: string) => void;
65
+ /** Called after every cycle (CLI prints); not called for suppressed repeats. */
66
+ onDecision?: (decision: Decision, obs: Observation) => void;
67
+ /** Safety bound for tests; default unlimited. */
68
+ maxCycles?: number;
69
+ }
70
+ export interface SupervisorResult {
71
+ runId: string;
72
+ decision: Decision;
73
+ cycles: number;
74
+ statePath: string;
75
+ eventsPath: string;
76
+ }
77
+ export declare function runSupervisor(opts: RunSupervisorOpts): Promise<SupervisorResult>;
78
+ //# sourceMappingURL=loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/supervise/loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,311 @@
1
+ /**
2
+ * The supervisor loop: observe → assess → decide → persist, debounced and
3
+ * periodic.
4
+ *
5
+ * Assessments run AT MOST every debounceMs (state-change triggered) and AT
6
+ * LEAST every intervalMs (heartbeat). State persists per run at
7
+ * `.uap/supervise/<runId>/state.json` (atomic tmp+rename, mode 0600) and every
8
+ * cycle appends a v-stamped record to `events.jsonl` (mode 0600).
9
+ *
10
+ * Action side effects are deliberately minimal — the supervisor watches, it
11
+ * does not drive:
12
+ * STOP → requestStop(): the cooperative STOP file the deliver loop
13
+ * already polls. NEVER a signal, never a kill.
14
+ * RETRY → consumes one retry from the budget (recorded).
15
+ * ESCALATE → event + console warning only, once per streak; after
16
+ * MAX_SUPPRESSED_ESCALATIONS unanswered repeats the loop gives
17
+ * up with a final 'escalation unanswered' event.
18
+ * VERIFY → recorded; bounded by cfg.maxVerify (policy-side).
19
+ * FINISH/CONTINUE → recorded, nothing else.
20
+ *
21
+ * Path discipline: every directory the supervisor writes through is lstat'd
22
+ * and realpath-checked first — a symlinked `.uap/supervise/<runId>` or
23
+ * `.uap/deliver-runs/<runId>` (a planted redirect) throws SupervisorError
24
+ * instead of writing outside the project.
25
+ */
26
+ import { appendFileSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, writeFileSync, } from 'fs';
27
+ import { join, sep } from 'path';
28
+ import { deliverRunsDir, requestStop } from '../delivery/run-state.js';
29
+ import { assessAll } from './assess.js';
30
+ import { loadSupervisorConfig, SupervisorError } from './config.js';
31
+ import { collectObservation, runStateMtimeMs } from './observe.js';
32
+ import { decide, effectiveMaxMinutes } from './policy.js';
33
+ export const SUPERVISE_STATE_VERSION = 1;
34
+ /** Consecutive unanswered ESCALATEs before the watcher gives up. */
35
+ export const MAX_SUPPRESSED_ESCALATIONS = 20;
36
+ const TERMINAL_STATUSES = new Set(['delivered', 'failed', 'interrupted']);
37
+ export function superviseDir(projectRoot, runId) {
38
+ return join(projectRoot, '.uap', 'supervise', runId);
39
+ }
40
+ export function supervisorStatePath(projectRoot, runId) {
41
+ return join(superviseDir(projectRoot, runId), 'state.json');
42
+ }
43
+ export function supervisorEventsPath(projectRoot, runId) {
44
+ return join(superviseDir(projectRoot, runId), 'events.jsonl');
45
+ }
46
+ /**
47
+ * Refuse to write through anything but a plain directory INSIDE baseDir.
48
+ * lstat (not stat) so symlinks are seen as symlinks — including broken ones,
49
+ * which existsSync reports as missing. Throws SupervisorError.
50
+ */
51
+ function ensureContainedDir(baseDir, dir, label) {
52
+ for (const p of [baseDir, dir]) {
53
+ let st;
54
+ try {
55
+ st = lstatSync(p);
56
+ }
57
+ catch {
58
+ st = undefined; // absent — will be created below
59
+ }
60
+ if (st && (st.isSymbolicLink() || !st.isDirectory())) {
61
+ throw new SupervisorError(`${label}: refusing non-plain-directory path ${p}`);
62
+ }
63
+ }
64
+ mkdirSync(dir, { recursive: true });
65
+ const realBase = realpathSync(baseDir);
66
+ const realDir = realpathSync(dir);
67
+ if (realDir !== realBase && !realDir.startsWith(realBase + sep)) {
68
+ throw new SupervisorError(`${label}: ${dir} escapes ${baseDir}`);
69
+ }
70
+ }
71
+ function ensureSuperviseDir(projectRoot, runId) {
72
+ ensureContainedDir(join(projectRoot, '.uap', 'supervise'), superviseDir(projectRoot, runId), 'supervisor state');
73
+ }
74
+ function ensureDeliverRunDir(projectRoot, runId) {
75
+ ensureContainedDir(deliverRunsDir(projectRoot), join(deliverRunsDir(projectRoot), runId), 'deliver run');
76
+ }
77
+ export function freshSupervisorState(runId, nowIso) {
78
+ return {
79
+ version: SUPERVISE_STATE_VERSION,
80
+ runId,
81
+ startedAt: nowIso,
82
+ updatedAt: nowIso,
83
+ assessments: 0,
84
+ retries: 0,
85
+ verifyCount: 0,
86
+ suppressedEscalations: 0,
87
+ lastAction: null,
88
+ lastActionAt: null,
89
+ };
90
+ }
91
+ /**
92
+ * Load persisted supervisor state, or a fresh one. A MISSING file is normal
93
+ * (first watch); a file that EXISTS but is unparseable or invalid is not —
94
+ * warn loudly, then recover fresh (fail closed means never silently dropping
95
+ * the retry/verify budgets a previous watcher already spent).
96
+ */
97
+ export function loadSupervisorState(projectRoot, runId, nowIso, warn = () => { }) {
98
+ const path = supervisorStatePath(projectRoot, runId);
99
+ if (!existsSync(path))
100
+ return freshSupervisorState(runId, nowIso);
101
+ const fresh = () => {
102
+ warn(`supervisor: ${path} exists but is invalid — starting fresh (budgets reset); investigate the corrupt file`);
103
+ return freshSupervisorState(runId, nowIso);
104
+ };
105
+ let parsed;
106
+ try {
107
+ parsed = JSON.parse(readFileSync(path, 'utf-8'));
108
+ }
109
+ catch {
110
+ return fresh();
111
+ }
112
+ if (!parsed || parsed.version !== SUPERVISE_STATE_VERSION || typeof parsed.retries !== 'number') {
113
+ return fresh();
114
+ }
115
+ parsed.runId = runId; // directory name is the identity, never the payload
116
+ // Fields added after the first shipped state shape default, never reject.
117
+ if (typeof parsed.verifyCount !== 'number')
118
+ parsed.verifyCount = 0;
119
+ if (typeof parsed.suppressedEscalations !== 'number')
120
+ parsed.suppressedEscalations = 0;
121
+ return parsed;
122
+ }
123
+ /**
124
+ * Persist state atomically (tmp + rename), mode 0600 — the embedded
125
+ * lastDecision can carry log-tail evidence. Fail-soft (false) on ordinary IO
126
+ * errors; SupervisorError (path discipline) always propagates.
127
+ */
128
+ export function saveSupervisorState(projectRoot, state) {
129
+ try {
130
+ ensureSuperviseDir(projectRoot, state.runId);
131
+ const path = supervisorStatePath(projectRoot, state.runId);
132
+ const tmp = `${path}.tmp`;
133
+ writeFileSync(tmp, JSON.stringify(state, null, 2), { encoding: 'utf-8', mode: 0o600 });
134
+ renameSync(tmp, path);
135
+ return true;
136
+ }
137
+ catch (err) {
138
+ if (err instanceof SupervisorError)
139
+ throw err;
140
+ return false;
141
+ }
142
+ }
143
+ /** Append one v-stamped event record (file created mode 0600). Fail-soft, except SupervisorError. */
144
+ export function appendSupervisorEvent(projectRoot, runId, event) {
145
+ try {
146
+ ensureSuperviseDir(projectRoot, runId);
147
+ appendFileSync(supervisorEventsPath(projectRoot, runId), JSON.stringify(event) + '\n', {
148
+ encoding: 'utf-8',
149
+ mode: 0o600,
150
+ });
151
+ return true;
152
+ }
153
+ catch (err) {
154
+ if (err instanceof SupervisorError)
155
+ throw err;
156
+ return false;
157
+ }
158
+ }
159
+ /**
160
+ * The assessment cadence, PURE: a state change may trigger an assessment once
161
+ * the debounce has elapsed; a heartbeat forces one once the interval has.
162
+ */
163
+ export function shouldAssess(args) {
164
+ if (args.sinceLastMs >= args.intervalMs)
165
+ return true;
166
+ return args.changed && args.sinceLastMs >= args.debounceMs;
167
+ }
168
+ export async function runSupervisor(opts) {
169
+ const baseCfg = opts.config ?? loadSupervisorConfig();
170
+ const cfg = opts.intervalOverrideMs !== undefined
171
+ ? (() => {
172
+ if (!Number.isFinite(opts.intervalOverrideMs) || opts.intervalOverrideMs <= 0) {
173
+ throw new SupervisorError(`--interval-ms must be a positive number (got ${opts.intervalOverrideMs})`);
174
+ }
175
+ return { ...baseCfg, intervalMs: Math.max(baseCfg.debounceMs, Math.floor(opts.intervalOverrideMs)) };
176
+ })()
177
+ : baseCfg;
178
+ const now = opts.now ?? Date.now;
179
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
180
+ const warn = opts.warn ?? ((m) => console.warn(m));
181
+ const tickMs = Math.min(cfg.debounceMs, 1000);
182
+ // Resolve 'latest' and fail fast when there is nothing to watch.
183
+ const first = collectObservation(opts.projectRoot, opts.runId, now);
184
+ if (!first) {
185
+ throw new SupervisorError(`no deliver run '${opts.runId}' under ${opts.projectRoot}`);
186
+ }
187
+ const runId = first.runId;
188
+ const state = loadSupervisorState(opts.projectRoot, runId, new Date(now()).toISOString(), warn);
189
+ let lastAssessAt = 0;
190
+ let lastMtime = runStateMtimeMs(opts.projectRoot, runId);
191
+ let cycles = 0;
192
+ let decision;
193
+ let lastStatus = first.status;
194
+ const applyDecision = (d, dims) => {
195
+ const ts = new Date(now()).toISOString();
196
+ const firstOfStreak = d.action !== state.lastAction;
197
+ const suppressed = !firstOfStreak && (d.action === 'ESCALATE' || d.action === 'FINISH');
198
+ if (!suppressed) {
199
+ if (d.action === 'STOP') {
200
+ ensureDeliverRunDir(opts.projectRoot, runId);
201
+ requestStop(opts.projectRoot, runId);
202
+ if (firstOfStreak)
203
+ warn(`supervisor: STOP ${runId} — ${d.reason} (cooperative stop file written; no signals sent)`);
204
+ }
205
+ else if (d.action === 'RETRY') {
206
+ state.retries += 1;
207
+ }
208
+ else if (d.action === 'VERIFY') {
209
+ state.verifyCount += 1;
210
+ }
211
+ else if (d.action === 'ESCALATE') {
212
+ warn(`supervisor: ESCALATE ${runId} — ${d.reason}`);
213
+ }
214
+ }
215
+ // Budget bookkeeping: verifyCount resets when test evidence reappears;
216
+ // the unanswered-escalation streak counts consecutive suppressed repeats.
217
+ if (!dims.some((x) => x.name === 'verification-pending' && x.value === true)) {
218
+ state.verifyCount = 0;
219
+ }
220
+ if (suppressed && d.action === 'ESCALATE') {
221
+ state.suppressedEscalations += 1;
222
+ }
223
+ else {
224
+ state.suppressedEscalations = 0;
225
+ }
226
+ state.assessments += 1;
227
+ state.lastAction = d.action;
228
+ state.lastActionAt = ts;
229
+ state.lastDecision = d;
230
+ state.updatedAt = ts;
231
+ saveSupervisorState(opts.projectRoot, state);
232
+ const dimensions = {};
233
+ for (const dim of dims) {
234
+ dimensions[dim.name] = { value: dim.value, source: dim.source };
235
+ }
236
+ appendSupervisorEvent(opts.projectRoot, runId, {
237
+ v: 1,
238
+ ts,
239
+ runId,
240
+ kind: d.action === 'CONTINUE' ? 'assessment' : 'action',
241
+ action: d.action,
242
+ reason: d.reason,
243
+ ...(suppressed ? { suppressed: true } : {}),
244
+ evidence: d.evidence,
245
+ dimensions,
246
+ });
247
+ };
248
+ const cycle = () => {
249
+ const obs = collectObservation(opts.projectRoot, runId, now);
250
+ if (!obs)
251
+ throw new SupervisorError(`deliver run '${runId}' vanished mid-supervision`);
252
+ const dims = assessAll(obs, cfg, opts.classifier);
253
+ // An operator-raised deliver budget outranks the static policy number.
254
+ const policyCfg = {
255
+ ...cfg,
256
+ maxMinutes: effectiveMaxMinutes(cfg, opts.projectRoot),
257
+ };
258
+ const d = decide(obs, dims, state, policyCfg);
259
+ applyDecision(d, dims);
260
+ cycles += 1;
261
+ lastAssessAt = now();
262
+ lastStatus = obs.status;
263
+ opts.onDecision?.(d, obs);
264
+ return d;
265
+ };
266
+ for (;;) {
267
+ const mtime = runStateMtimeMs(opts.projectRoot, runId);
268
+ const changed = mtime !== lastMtime;
269
+ if (changed)
270
+ lastMtime = mtime;
271
+ const sinceLast = lastAssessAt === 0 ? Number.POSITIVE_INFINITY : now() - lastAssessAt;
272
+ if (shouldAssess({ changed, sinceLastMs: sinceLast, debounceMs: cfg.debounceMs, intervalMs: cfg.intervalMs })) {
273
+ decision = cycle();
274
+ if (opts.once)
275
+ break;
276
+ if (decision.action === 'STOP' || decision.action === 'FINISH')
277
+ break;
278
+ if (TERMINAL_STATUSES.has(lastStatus))
279
+ break;
280
+ if (state.suppressedEscalations >= MAX_SUPPRESSED_ESCALATIONS) {
281
+ // The escalation went unanswered for the whole bound — stop watching
282
+ // rather than filling the ledger forever. The final event says why.
283
+ appendSupervisorEvent(opts.projectRoot, runId, {
284
+ v: 1,
285
+ ts: new Date(now()).toISOString(),
286
+ runId,
287
+ kind: 'action',
288
+ action: 'ESCALATE',
289
+ reason: 'escalation unanswered',
290
+ evidence: { suppressedEscalations: state.suppressedEscalations },
291
+ dimensions: {},
292
+ });
293
+ warn(`supervisor: ESCALATE ${runId} unanswered after ${MAX_SUPPRESSED_ESCALATIONS} repeats — detaching watcher`);
294
+ break;
295
+ }
296
+ if (opts.maxCycles !== undefined && cycles >= opts.maxCycles)
297
+ break;
298
+ }
299
+ await sleep(tickMs);
300
+ }
301
+ if (!decision)
302
+ throw new SupervisorError('supervisor exited without assessing');
303
+ return {
304
+ runId,
305
+ decision,
306
+ cycles,
307
+ statePath: supervisorStatePath(opts.projectRoot, runId),
308
+ eventsPath: supervisorEventsPath(opts.projectRoot, runId),
309
+ };
310
+ }
311
+ //# sourceMappingURL=loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop.js","sourceRoot":"","sources":["../../src/supervise/loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { Observation } from './types.js';
2
+ /** Log tail budget — enough for error signatures, small enough to be safe. */
3
+ export declare const LOG_TAIL_BYTES = 8192;
4
+ /** `git diff --stat` cap; a monorepo diff can list thousands of files. */
5
+ export declare const DIFF_STAT_MAX_CHARS = 4000;
6
+ export declare const GIT_TIMEOUT_MS = 5000;
7
+ /**
8
+ * Read at most the last `maxBytes` of a file. Fail-soft: undefined on any
9
+ * error, and silently for anything that is not a plain regular file (a
10
+ * symlinked log or a FIFO must never be followed into a blocking read).
11
+ */
12
+ export declare function tailFile(path: string, maxBytes?: number): string | undefined;
13
+ /**
14
+ * Newest log plausibly belonging to a run. Run ids embed the launch stamp
15
+ * (`run-<YYYYMMDDTHHMMSS>-<hex>`) and the detached wrapper names its log
16
+ * `deliver-<YYYYMMDDTHHMMSS>.log` moments earlier, so the right log is the
17
+ * newest stamped at-or-before the run's stamp; absent any stamped match, the
18
+ * newest log by mtime. Undefined when there are no logs at all.
19
+ */
20
+ export declare function newestLogForRun(projectRoot: string, runId: string): string | undefined;
21
+ export declare function gitDirtyFileCount(projectRoot: string): number | undefined;
22
+ export declare function gitDiffStat(projectRoot: string): string | undefined;
23
+ /** mtime of the run's state.json — the change signal the debounce watches. */
24
+ export declare function runStateMtimeMs(projectRoot: string, runId: string): number;
25
+ /**
26
+ * Collect one bounded observation for a run. `runId` may be 'latest'
27
+ * (resolved by run-state). Returns null for invalid ids and unknown runs —
28
+ * the caller decides how loud to be. `now` is injectable for tests.
29
+ */
30
+ export declare function collectObservation(projectRoot: string, runId: string, now?: () => number): Observation | null;
31
+ //# sourceMappingURL=observe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../src/supervise/observe.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,8EAA8E;AAC9E,eAAO,MAAM,cAAc,OAAO,CAAC;AACnC,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,cAAc,OAAO,CAAC;AAcnC;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAuB,GAAG,MAAM,GAAG,SAAS,CAqB5F;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CA6BtF;AAiBD,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIzE;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGnE;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAM1E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,GAAG,GAAE,MAAM,MAAiB,GAC3B,WAAW,GAAG,IAAI,CAyBpB"}
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Observation collection — the ONLY impure step in the supervisor.
3
+ *
4
+ * Everything read here is bounded and fail-soft:
5
+ * - run state comes from run-state.ts's sanitizing loader (never trusted raw)
6
+ * - log tails are read with an fd seek of the last N bytes, never a whole-file
7
+ * readFileSync (a multi-GB mission log must not be slurped)
8
+ * - git runs via execFileSync ARG ARRAYS (no shell, no interpolation), piped
9
+ * stdio, 5s timeout — and any failure degrades to `undefined`, never throws
10
+ */
11
+ import { execFileSync } from 'child_process';
12
+ import { closeSync, fstatSync, lstatSync, openSync, readSync, readdirSync, statSync } from 'fs';
13
+ import { join } from 'path';
14
+ import { deliverRunsDir, isValidRunId, loadRunState } from '../delivery/run-state.js';
15
+ /** Log tail budget — enough for error signatures, small enough to be safe. */
16
+ export const LOG_TAIL_BYTES = 8192;
17
+ /** `git diff --stat` cap; a monorepo diff can list thousands of files. */
18
+ export const DIFF_STAT_MAX_CHARS = 4000;
19
+ export const GIT_TIMEOUT_MS = 5000;
20
+ /** Defeat fsmonitor/untracked-cache shims: observation must see the real tree. */
21
+ const GIT_ARGS_PREFIX = ['-c', 'core.fsmonitor=false', '-c', 'core.untrackedCache=false'];
22
+ /** True only for a plain regular file — symlinks/FIFOs/devices are skipped. */
23
+ function isRegularFile(path) {
24
+ try {
25
+ const st = lstatSync(path);
26
+ return !st.isSymbolicLink() && st.isFile();
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ /**
33
+ * Read at most the last `maxBytes` of a file. Fail-soft: undefined on any
34
+ * error, and silently for anything that is not a plain regular file (a
35
+ * symlinked log or a FIFO must never be followed into a blocking read).
36
+ */
37
+ export function tailFile(path, maxBytes = LOG_TAIL_BYTES) {
38
+ if (!isRegularFile(path))
39
+ return undefined;
40
+ let fd;
41
+ try {
42
+ fd = openSync(path, 'r');
43
+ const size = fstatSync(fd).size;
44
+ const start = Math.max(0, size - maxBytes);
45
+ const buf = Buffer.alloc(size - start);
46
+ readSync(fd, buf, 0, buf.length, start);
47
+ return buf.toString('utf-8');
48
+ }
49
+ catch {
50
+ return undefined;
51
+ }
52
+ finally {
53
+ if (fd !== undefined) {
54
+ try {
55
+ closeSync(fd);
56
+ }
57
+ catch {
58
+ /* already closed */
59
+ }
60
+ }
61
+ }
62
+ }
63
+ /**
64
+ * Newest log plausibly belonging to a run. Run ids embed the launch stamp
65
+ * (`run-<YYYYMMDDTHHMMSS>-<hex>`) and the detached wrapper names its log
66
+ * `deliver-<YYYYMMDDTHHMMSS>.log` moments earlier, so the right log is the
67
+ * newest stamped at-or-before the run's stamp; absent any stamped match, the
68
+ * newest log by mtime. Undefined when there are no logs at all.
69
+ */
70
+ export function newestLogForRun(projectRoot, runId) {
71
+ const dir = join(projectRoot, '.uap', 'deliver-logs');
72
+ let files;
73
+ try {
74
+ files = readdirSync(dir)
75
+ .filter((f) => f.endsWith('.log'))
76
+ .filter((f) => isRegularFile(join(dir, f)));
77
+ }
78
+ catch {
79
+ return undefined;
80
+ }
81
+ if (files.length === 0)
82
+ return undefined;
83
+ const runStamp = /^run-(\d{8}T\d{6})/.exec(runId)?.[1];
84
+ if (runStamp) {
85
+ const eligible = files
86
+ .map((f) => ({ f, stamp: /^deliver-(\d{8}T\d{6})\.log$/.exec(f)?.[1] }))
87
+ .filter((x) => x.stamp !== undefined && x.stamp <= runStamp)
88
+ .sort((a, b) => b.stamp.localeCompare(a.stamp));
89
+ if (eligible.length > 0)
90
+ return join(dir, eligible[0].f);
91
+ }
92
+ let best;
93
+ for (const f of files) {
94
+ try {
95
+ const mtime = statSync(join(dir, f)).mtimeMs;
96
+ if (!best || mtime > best.mtime)
97
+ best = { f, mtime };
98
+ }
99
+ catch {
100
+ /* vanished between readdir and stat */
101
+ }
102
+ }
103
+ return best ? join(dir, best.f) : undefined;
104
+ }
105
+ /** git, fail-soft: undefined on any error (not a repo, timeout, no binary). */
106
+ function git(projectRoot, args) {
107
+ try {
108
+ const out = execFileSync('git', [...GIT_ARGS_PREFIX, ...args], {
109
+ cwd: projectRoot,
110
+ timeout: GIT_TIMEOUT_MS,
111
+ stdio: ['ignore', 'pipe', 'pipe'],
112
+ maxBuffer: 1024 * 1024,
113
+ });
114
+ return out.toString('utf-8');
115
+ }
116
+ catch {
117
+ return undefined;
118
+ }
119
+ }
120
+ export function gitDirtyFileCount(projectRoot) {
121
+ const out = git(projectRoot, ['status', '--porcelain']);
122
+ if (out === undefined)
123
+ return undefined;
124
+ return out.split('\n').filter((l) => l.trim().length > 0).length;
125
+ }
126
+ export function gitDiffStat(projectRoot) {
127
+ const out = git(projectRoot, ['diff', '--stat']);
128
+ return out === undefined ? undefined : out.slice(0, DIFF_STAT_MAX_CHARS);
129
+ }
130
+ /** mtime of the run's state.json — the change signal the debounce watches. */
131
+ export function runStateMtimeMs(projectRoot, runId) {
132
+ try {
133
+ return statSync(join(deliverRunsDir(projectRoot), runId, 'state.json')).mtimeMs;
134
+ }
135
+ catch {
136
+ return 0;
137
+ }
138
+ }
139
+ /**
140
+ * Collect one bounded observation for a run. `runId` may be 'latest'
141
+ * (resolved by run-state). Returns null for invalid ids and unknown runs —
142
+ * the caller decides how loud to be. `now` is injectable for tests.
143
+ */
144
+ export function collectObservation(projectRoot, runId, now = Date.now) {
145
+ if (runId !== 'latest' && !isValidRunId(runId))
146
+ return null;
147
+ const state = loadRunState(projectRoot, runId);
148
+ if (!state)
149
+ return null;
150
+ const nowMs = now();
151
+ const created = Date.parse(state.createdAt);
152
+ const updated = Date.parse(state.updatedAt);
153
+ const elapsedMinutes = Number.isFinite(created) ? Math.max(0, (nowMs - created) / 60_000) : 0;
154
+ const minutesSinceUpdate = Number.isFinite(updated) ? Math.max(0, (nowMs - updated) / 60_000) : elapsedMinutes;
155
+ const cp = state.checkpoint;
156
+ const failures = cp?.history?.filter((h) => !h.passed).length ?? 0;
157
+ const logPath = newestLogForRun(projectRoot, state.runId);
158
+ return {
159
+ runId: state.runId,
160
+ status: state.status,
161
+ instruction: state.instruction,
162
+ elapsedMinutes,
163
+ minutesSinceUpdate,
164
+ turnsCompleted: cp?.turn ?? 0,
165
+ failures,
166
+ hasCheckpoint: cp !== undefined,
167
+ recentLogTail: (logPath && tailFile(logPath)) || '',
168
+ gitDirtyFiles: gitDirtyFileCount(projectRoot),
169
+ diffStat: gitDiffStat(projectRoot),
170
+ };
171
+ }
172
+ //# sourceMappingURL=observe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observe.js","sourceRoot":"","sources":["../../src/supervise/observe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Decision, DimensionAssessment, Observation, SupervisorConfig, SupervisorState } from './types.js';
2
+ /** The policy state it needs: retry/verify budgets + the oscillation guard's memory. */
3
+ export type PolicyState = Pick<SupervisorState, 'retries' | 'lastAction' | 'verifyCount'>;
4
+ /**
5
+ * The budget the STOP branch enforces: the LARGER of the supervisor policy
6
+ * and the deliver run's own budget (`UAP_DELIVER_MAX_MINUTES` /
7
+ * `.uap.json delivery.maxRunMinutes` / the calibrated default). An
8
+ * operator-raised deliver budget must not be overruled by the supervisor's
9
+ * static policy number; a LOWER operator budget makes the deliver loop stop
10
+ * itself first anyway, so taking the max never extends a mission past what
11
+ * its owner granted. Impure (reads env/config) — callers pass the result in
12
+ * via an adjusted cfg so decide() itself stays pure.
13
+ */
14
+ export declare function effectiveMaxMinutes(cfg: SupervisorConfig, projectRoot: string): number;
15
+ export declare function decide(obs: Observation, dims: DimensionAssessment[], state: PolicyState, cfg: SupervisorConfig): Decision;
16
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/supervise/policy.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,QAAQ,EAAE,mBAAmB,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAehH,wFAAwF;AACxF,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,YAAY,GAAG,aAAa,CAAC,CAAC;AAE1F;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAEtF;AAgBD,wBAAgB,MAAM,CACpB,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,mBAAmB,EAAE,EAC3B,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,gBAAgB,GACpB,QAAQ,CAiGV"}