@kodr/aiwrangler 0.0.0-stage → 0.1.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.
@@ -0,0 +1,522 @@
1
+ import {
2
+ effectiveTruth,
3
+ formatReport,
4
+ runCheck,
5
+ runPlanGate,
6
+ sectionsOfKind
7
+ } from "./chunk-2CIV4KGU.js";
8
+ import {
9
+ Jev
10
+ } from "./chunk-EDVWHCQV.js";
11
+ import {
12
+ Ledger,
13
+ activeThresholds,
14
+ applyRuntimeOverrides,
15
+ clearRuntimeOverrides,
16
+ expandPath,
17
+ findConfigFile,
18
+ flattenGates,
19
+ loadConfig,
20
+ loadCorpus,
21
+ readRuntimeOverridesFile,
22
+ writeRuntimeOverridesFile
23
+ } from "./chunk-EAQASAXL.js";
24
+
25
+ // src/osdialog.ts
26
+ import { spawn } from "child_process";
27
+ var DIALOG_TIMEOUT_MS = 3 * 60 * 1e3;
28
+ function hasLinuxGui() {
29
+ return Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY || process.env.MIR_SOCKET);
30
+ }
31
+ function runCommand(command, args, env) {
32
+ return new Promise((resolve) => {
33
+ const child = spawn(command, args, { env, stdio: ["ignore", "pipe", "pipe"] });
34
+ let stdout = "";
35
+ let settled = false;
36
+ const finish = (result) => {
37
+ if (settled) return;
38
+ settled = true;
39
+ clearTimeout(timer);
40
+ resolve(result);
41
+ };
42
+ const timer = setTimeout(() => {
43
+ child.kill("SIGKILL");
44
+ finish({ code: null, stdout, missing: false });
45
+ }, DIALOG_TIMEOUT_MS);
46
+ child.stdout.on("data", (chunk) => {
47
+ stdout += chunk.toString();
48
+ });
49
+ child.on("error", (err) => {
50
+ finish({ code: null, stdout, missing: err.code === "ENOENT" });
51
+ });
52
+ child.on("close", (code) => {
53
+ finish({ code, stdout, missing: false });
54
+ });
55
+ });
56
+ }
57
+ async function osascript(summary) {
58
+ const script = [
59
+ "on run argv",
60
+ "set prompt to item 1 of argv",
61
+ 'set r to display dialog prompt buttons {"Deny", "Approve"} default button "Deny" with title "AiWrangler" with icon caution',
62
+ "return button returned of r",
63
+ "end run"
64
+ ].join("\n");
65
+ const result = await runCommand("osascript", ["-e", script, "--", summary]);
66
+ if (result.missing) return "unavailable";
67
+ if (result.code === 0 && result.stdout.includes("Approve")) return "approve";
68
+ return "deny";
69
+ }
70
+ async function zenity(summary) {
71
+ const result = await runCommand("zenity", [
72
+ "--question",
73
+ "--title=AiWrangler",
74
+ "--ok-label=Approve",
75
+ "--cancel-label=Deny",
76
+ "--width=480",
77
+ "--text",
78
+ summary
79
+ ]);
80
+ if (result.missing) return "missing";
81
+ return result.code === 0 ? "approve" : "deny";
82
+ }
83
+ async function kdialog(summary) {
84
+ const result = await runCommand("kdialog", ["--title", "AiWrangler", "--yesno", summary]);
85
+ if (result.missing) return "missing";
86
+ return result.code === 0 ? "approve" : "deny";
87
+ }
88
+ async function linuxDialog(summary) {
89
+ if (!hasLinuxGui()) return "unavailable";
90
+ const zen = await zenity(summary);
91
+ if (zen !== "missing") return zen;
92
+ const kde = await kdialog(summary);
93
+ if (kde !== "missing") return kde;
94
+ return "unavailable";
95
+ }
96
+ async function windowsDialog(summary) {
97
+ const script = [
98
+ "Add-Type -AssemblyName System.Windows.Forms",
99
+ "$r = [System.Windows.Forms.MessageBox]::Show($env:AIWRANGLER_PROMPT, 'AiWrangler', 'YesNo', 'Warning', 'Button2')",
100
+ "if ($r -eq [System.Windows.Forms.DialogResult]::Yes) { exit 0 } else { exit 1 }"
101
+ ].join("\n");
102
+ const result = await runCommand("powershell.exe", ["-NoProfile", "-STA", "-Command", script], {
103
+ ...process.env,
104
+ AIWRANGLER_PROMPT: summary
105
+ });
106
+ if (result.missing) return "unavailable";
107
+ if (result.code === null) return "deny";
108
+ return result.code === 0 ? "approve" : "deny";
109
+ }
110
+ async function confirmOsDialog(summary) {
111
+ if (process.env.AIWRANGLER_OS_DIALOG === "off") return "unavailable";
112
+ const text = summary.length > 3500 ? summary.slice(0, 3500) + "\n\u2026" : summary;
113
+ if (process.platform === "darwin") return osascript(text);
114
+ if (process.platform === "win32") return windowsDialog(text);
115
+ if (process.platform === "linux" || process.platform === "freebsd" || process.platform === "openbsd") return linuxDialog(text);
116
+ return "unavailable";
117
+ }
118
+
119
+ // src/approve.ts
120
+ var OWNER_DECLINED = "aiwrangler: the owner declined. Nothing was recorded.";
121
+ var NO_APPROVAL_CHANNEL = "aiwrangler: no human approval channel is available. This client does not support MCP elicitation, and no desktop dialog could be shown. Nothing was recorded. The owner can run `aiwrangler ack` or `aiwrangler override` in a terminal and type yes.";
122
+ async function approveOwnerWrite(summary, channel = {}) {
123
+ if (channel.elicit) {
124
+ const outcome = await channel.elicit(summary);
125
+ if (outcome === "approve") return { ok: true, confirmation: "elicitation" };
126
+ if (outcome === "deny") return { ok: false, message: OWNER_DECLINED };
127
+ if (typeof outcome === "object") {
128
+ return { ok: false, message: `aiwrangler: elicitation failed (${outcome.error}). Nothing was recorded.` };
129
+ }
130
+ }
131
+ const dialog = channel.osDialog ?? confirmOsDialog;
132
+ const os = await dialog(summary);
133
+ if (os === "approve") return { ok: true, confirmation: "os-dialog" };
134
+ if (os === "deny") return { ok: false, message: "aiwrangler: the owner declined the desktop dialog. Nothing was recorded." };
135
+ return { ok: false, message: NO_APPROVAL_CHANNEL };
136
+ }
137
+
138
+ // src/core.ts
139
+ import { existsSync, writeFileSync } from "fs";
140
+ import { join } from "path";
141
+ var KIND_ORDER = ["spec", "rule", "divergence", "question", "door", "paper", "doc"];
142
+ function resolveGateDir(dir) {
143
+ return dir ?? process.env.AIWRANGLER_DIR ?? process.cwd();
144
+ }
145
+ function configReport(dir) {
146
+ const loaded = loadConfig(dir);
147
+ const corpus = loadCorpus(loaded.config, loaded.root);
148
+ const flags = flattenGates(loaded.config);
149
+ const thresholds = activeThresholds(loaded.config);
150
+ const counts = KIND_ORDER.map((kind) => `${kind}=${sectionsOfKind(corpus, kind).length}`);
151
+ const ruled = effectiveTruth(corpus);
152
+ const out = [
153
+ `config: ${loaded.path ?? "(defaults)"} [${loaded.source}]`,
154
+ `model: ${loaded.config.model} policy: ${loaded.config.activePolicy} (act>=${thresholds.actionThreshold}, review>=${thresholds.reviewThreshold})`,
155
+ `egress: semantic=${loaded.config.egress.semantic ? "ON (task/diff/spec text goes to api.typesafe.ai)" : "OFF (deterministic only)"}`,
156
+ "",
157
+ "gates:",
158
+ ...[...flags.entries()].map(([path, on]) => ` ${on ? "on " : "off"} ${path}`),
159
+ "",
160
+ `corpus: ${counts.join(" ")} ruled=${ruled.length} hash=${corpus.hash.slice(0, 12)}`,
161
+ ...corpus.sources.map((s) => ` ${s.path} (${s.hash.slice(0, 8)})`),
162
+ ...corpus.sections.length === 0 ? [" (no ground truth configured; point corpus paths in aiwrangler.config.json at your spec, rules, and rulings)"] : []
163
+ ];
164
+ if (loaded.config.egress.semantic && loaded.config.egress.warnOncePerSession) {
165
+ out.push("", "NOTE: the semantic gate sends task/diff/spec text to api.typesafe.ai. Set AIWRANGLER_EGRESS=off to disable.");
166
+ }
167
+ return out.join("\n");
168
+ }
169
+ function formatContextPack(pack) {
170
+ const out = [];
171
+ out.push("GROUND-TRUTH CONTEXT PACK (cite these IDs; honor them while implementing):");
172
+ if (pack.sections.length > 0) {
173
+ out.push("", "governing sections:");
174
+ for (const s of pack.sections) out.push(` [${s.id}] ${s.title}
175
+ ${s.text.replace(/\s+/g, " ").slice(0, 600)}`);
176
+ }
177
+ if (pack.rulings.length > 0) {
178
+ out.push("", "recorded rulings (these override the paper and spec):");
179
+ for (const r of pack.rulings) out.push(` [${r.id}] ${r.title}
180
+ ${(r.ruling ?? r.text).replace(/\s+/g, " ").slice(0, 400)}`);
181
+ }
182
+ if (pack.doors.length > 0) {
183
+ out.push("", "one-way doors / launch gates (never change without the owner):");
184
+ for (const d of pack.doors) out.push(` [${d.id}] ${d.title}
185
+ ${d.text.replace(/\s+/g, " ").slice(0, 400)}`);
186
+ }
187
+ if (pack.openItems.length > 0) {
188
+ out.push("", "open items in this task's area (do not pick a side; ask the user if it blocks you):");
189
+ for (const o of pack.openItems) out.push(` [${o.id}] ${o.title}
190
+ ${o.text.replace(/\s+/g, " ").slice(0, 400)}`);
191
+ }
192
+ return out.join("\n");
193
+ }
194
+ async function planGate(dir, input) {
195
+ if (!findConfigFile(dir) && !process.env.AIWRANGLER_CONFIG) {
196
+ return "aiwrangler: no aiwrangler.config.json found for this directory; plan gate inactive";
197
+ }
198
+ try {
199
+ const loaded = loadConfig(dir);
200
+ const corpus = loadCorpus(loaded.config, loaded.root);
201
+ const jev = new Jev(loaded.config, { cacheDir: loaded.root });
202
+ const report = await runPlanGate(loaded.config, jev, corpus, {
203
+ task: input.task,
204
+ ...input.plan ? { plan: input.plan } : {}
205
+ });
206
+ try {
207
+ new Ledger(loaded.config, loaded.root).append({
208
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
209
+ kind: "plan",
210
+ model: loaded.config.model,
211
+ corpusHash: corpus.hash,
212
+ action: report.action,
213
+ ...report.usage ? { usage: report.usage } : {},
214
+ findings: [],
215
+ ...report.verdicts.length > 0 ? { verdicts: report.verdicts.map((v) => ({ question: v.question, noul: v.noul })) } : {},
216
+ reasons: report.reasons,
217
+ ...input.sessionID !== void 0 ? { sessionID: input.sessionID } : {},
218
+ overridesActive: loaded.overrides !== null
219
+ });
220
+ } catch {
221
+ }
222
+ const out = [
223
+ `AIWRANGLER PLAN: ${report.action.toUpperCase()}${report.degraded ? " (degraded: " + report.degradedReason + ")" : ""}`
224
+ ];
225
+ if (report.verdicts.length > 0) {
226
+ out.push("", "verdicts:");
227
+ for (const v of report.verdicts) out.push(` ${v.question.padEnd(22)} ${v.noul.toFixed(2)} ${v.interpretation}${v.subject ? " [" + v.subject + "]" : ""} ${v.label}`);
228
+ }
229
+ if (report.reasons.length > 0) {
230
+ out.push("", "reasons:");
231
+ for (const r of report.reasons) out.push(` - ${r}`);
232
+ }
233
+ if (report.action === "clarify") out.push("", "Ask the user to resolve the flagged items before writing code.");
234
+ if (report.action === "human") out.push("", "STOP: this task touches a decided one-way door or flagged steering text; the owner must decide.");
235
+ out.push("", formatContextPack(report.contextPack));
236
+ return out.join("\n");
237
+ } catch (err) {
238
+ return `aiwrangler plan gate error: ${err instanceof Error ? err.message : String(err)}`;
239
+ }
240
+ }
241
+ async function checkGate(dir, input = {}) {
242
+ if (!findConfigFile(dir) && !process.env.AIWRANGLER_CONFIG) {
243
+ return "aiwrangler: no aiwrangler.config.json found for this directory; check gate inactive";
244
+ }
245
+ try {
246
+ const loaded = loadConfig(dir);
247
+ const corpus = loadCorpus(loaded.config, loaded.root);
248
+ const jev = new Jev(loaded.config, { cacheDir: loaded.root });
249
+ const report = await runCheck(loaded.config, loaded.root, corpus, jev, {
250
+ ...input.task ? { task: input.task } : {},
251
+ ...input.agentSummary ? { agentSummary: input.agentSummary } : {},
252
+ ...input.toolOutput ? { toolOutput: input.toolOutput } : {},
253
+ cargo: input.cargo || input.tests || false,
254
+ tests: input.tests || false,
255
+ ...input.sessionID !== void 0 ? { sessionID: input.sessionID } : {}
256
+ });
257
+ return formatReport(report);
258
+ } catch (err) {
259
+ return `aiwrangler check gate error: ${err instanceof Error ? err.message : String(err)}`;
260
+ }
261
+ }
262
+ function isAckWriteAttempt(input) {
263
+ return input.rules !== void 0 || input.note !== void 0 || input.diffHash !== void 0;
264
+ }
265
+ function isOverrideWriteAttempt(input) {
266
+ return input.set !== void 0 || input.clear === true || input.note !== void 0;
267
+ }
268
+ function latestDiffEntry(ledger) {
269
+ const entries = ledger.read().filter((e) => e.kind === "check" || e.kind === "plan");
270
+ return [...entries].reverse().find((e) => e.diffHash !== void 0);
271
+ }
272
+ function listPendingAck(dir) {
273
+ try {
274
+ const loaded = loadConfig(dir);
275
+ const ledger = new Ledger(loaded.config, loaded.root);
276
+ const latest = latestDiffEntry(ledger);
277
+ if (!latest) {
278
+ return "aiwrangler: no gate run recorded yet; run wrangler_check first";
279
+ }
280
+ const pending = (latest.findings ?? []).filter((f) => f.severity === "human" && !f.acknowledged);
281
+ const out = [`pending human review (check at ${latest.ts}, action: ${latest.action})`, `diffHash: ${latest.diffHash}`];
282
+ if (pending.length === 0) {
283
+ out.push("", "no pending human-severity findings on the latest recorded diff");
284
+ if (latest.action === "human") out.push("(the human verdict came from the semantic layer; re-run wrangler_check to re-confirm)");
285
+ } else {
286
+ const example = pending.map((f) => f.rule).slice(0, 8).join(",");
287
+ out.push("", "findings needing the owner's eyes:");
288
+ for (const f of pending) out.push(` ${f.rule} ${f.subject} [${f.severity}]`);
289
+ out.push(
290
+ "",
291
+ "the agent cannot answer the approval prompt. Call wrangler_ack with those rules to ask the owner in the editor,",
292
+ `or the owner can run \`aiwrangler ack --rules ${example}\` in a terminal.`,
293
+ "the ack binds to the diffHash above and is void if the diff changes.",
294
+ "block-severity findings can never be acknowledged; fix them instead."
295
+ );
296
+ }
297
+ return out.join("\n");
298
+ } catch (err) {
299
+ return `aiwrangler ack error: ${err instanceof Error ? err.message : String(err)}`;
300
+ }
301
+ }
302
+ function planAck(dir, input) {
303
+ try {
304
+ const loaded = loadConfig(dir);
305
+ const ledger = new Ledger(loaded.config, loaded.root);
306
+ const rules = input.rules.filter((r) => typeof r === "string" && r.length > 0);
307
+ if (rules.length === 0) return { ok: false, message: 'aiwrangler: rules must be a non-empty array (use ["*"] to ack all human findings)' };
308
+ let diffHash = input.diffHash;
309
+ if (!diffHash) {
310
+ diffHash = latestDiffEntry(ledger)?.diffHash;
311
+ if (!diffHash) return { ok: false, message: "aiwrangler: no recorded gate run to ack; run wrangler_check first (or pass diffHash explicitly)" };
312
+ }
313
+ if (!/^[0-9a-f]{16,64}$/.test(diffHash)) return { ok: false, message: "aiwrangler: invalid diffHash" };
314
+ const match = ledger.read().filter((e) => (e.kind === "check" || e.kind === "plan") && e.diffHash === diffHash).at(-1);
315
+ if (!match) return { ok: false, message: "aiwrangler: diffHash not found in ledger; run wrangler_check for that diff first" };
316
+ const note = input.note && input.note.trim() ? input.note.trim().slice(0, 500) : void 0;
317
+ const pending = (match.findings ?? []).filter((f) => f.severity === "human");
318
+ const summary = [
319
+ "AiWrangler: approve acknowledging these HUMAN REVIEW findings?",
320
+ `rules: ${rules.join(", ")}`,
321
+ `diffHash: ${diffHash}`,
322
+ ...note ? [`note: ${note}`] : [],
323
+ "findings on that diff:",
324
+ ...pending.length > 0 ? pending.slice(0, 20).map((f) => ` ${f.rule} ${f.subject} [${f.severity}]`) : [" (no human-severity rows stored on that run)"],
325
+ "The agent cannot answer this prompt. Approve only if you reviewed the diff.",
326
+ "Block-severity findings are never covered. A diff change voids the ack."
327
+ ].join("\n");
328
+ return { ok: true, rules: rules.slice(0, 50), diffHash, summary, ...note ? { note } : {} };
329
+ } catch (err) {
330
+ return { ok: false, message: `aiwrangler ack error: ${err instanceof Error ? err.message : String(err)}` };
331
+ }
332
+ }
333
+ function acknowledgeFindings(dir, input) {
334
+ try {
335
+ const planned = planAck(dir, input);
336
+ if (!planned.ok) return planned.message;
337
+ const loaded = loadConfig(dir);
338
+ const entry = {
339
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
340
+ kind: "ack",
341
+ diffHash: planned.diffHash,
342
+ rules: planned.rules,
343
+ ...planned.note ? { note: planned.note } : {},
344
+ by: input.by,
345
+ confirmation: input.confirmation
346
+ };
347
+ new Ledger(loaded.config, loaded.root).append(entry);
348
+ return [
349
+ `acknowledged: human-severity findings ${JSON.stringify(entry.rules)} for diff ${planned.diffHash.slice(0, 16)}`,
350
+ `recorded by ${input.by} (confirmation: ${input.confirmation})`,
351
+ "the next wrangler_check on this exact diff will pass them with an owner-acknowledged reason.",
352
+ "any diff change voids the ack; block-severity findings are never ackable."
353
+ ].join("\n");
354
+ } catch (err) {
355
+ return `aiwrangler ack error: ${err instanceof Error ? err.message : String(err)}`;
356
+ }
357
+ }
358
+ async function requestHumanAck(dir, input, channel) {
359
+ const planned = planAck(dir, input);
360
+ if (!planned.ok) return planned.message;
361
+ const approval = await approveOwnerWrite(planned.summary, channel);
362
+ if (!approval.ok) return approval.message;
363
+ return acknowledgeFindings(dir, {
364
+ rules: planned.rules,
365
+ diffHash: planned.diffHash,
366
+ confirmation: approval.confirmation,
367
+ by: channel.by,
368
+ ...planned.note ? { note: planned.note } : {}
369
+ });
370
+ }
371
+ function reportOverrides(dir) {
372
+ try {
373
+ const loaded = loadConfig(dir);
374
+ const file = readRuntimeOverridesFile(loaded.config, loaded.root);
375
+ if (!file) return "runtime overrides: none active (strict defaults in effect). The owner sets them with `aiwrangler override`.";
376
+ return [
377
+ "runtime overrides ACTIVE:",
378
+ JSON.stringify(file.overrides, null, 2),
379
+ `created: ${file.createdAt} expiresOn: ${file.expiresOn}${file.note ? ` note: ${file.note}` : ""}`,
380
+ "they auto-expire on the next PASS verdict; wrangler_config shows the effective state.",
381
+ "to change them, call wrangler_override with set or clear. That asks the owner; it does not apply the change by itself.",
382
+ "the owner can also run `aiwrangler override` in a terminal."
383
+ ].join("\n");
384
+ } catch (err) {
385
+ return `aiwrangler override error: ${err instanceof Error ? err.message : String(err)}`;
386
+ }
387
+ }
388
+ function planOverride(dir, input) {
389
+ try {
390
+ if (input.clear && input.set) return { ok: false, message: "aiwrangler: pass only one of set or clear; nothing was changed" };
391
+ const loaded = loadConfig(dir);
392
+ const note = input.note && input.note.trim() ? input.note.trim().slice(0, 300) : void 0;
393
+ if (input.clear) {
394
+ if (!readRuntimeOverridesFile(loaded.config, loaded.root)) {
395
+ return { ok: true, kind: "noop", message: "no runtime overrides were active" };
396
+ }
397
+ return {
398
+ ok: true,
399
+ kind: "clear",
400
+ summary: [
401
+ "AiWrangler: clear runtime overrides and restore the strict resting state?",
402
+ ...note ? [`note: ${note}`] : [],
403
+ "The agent cannot answer this prompt."
404
+ ].join("\n"),
405
+ ...note ? { note } : {}
406
+ };
407
+ }
408
+ if (!input.set) return { ok: false, message: "aiwrangler: override write needs set or clear" };
409
+ let appliedGates = [];
410
+ try {
411
+ appliedGates = applyRuntimeOverrides(loaded.config, input.set).appliedGates;
412
+ } catch (err) {
413
+ return { ok: false, message: `aiwrangler: invalid overrides: ${err instanceof Error ? err.message : String(err)}` };
414
+ }
415
+ return {
416
+ ok: true,
417
+ kind: "set",
418
+ set: input.set,
419
+ appliedGates,
420
+ summary: [
421
+ "AiWrangler: apply these runtime overrides? They expire on the next PASS.",
422
+ JSON.stringify(input.set, null, 2),
423
+ ...note ? [`note: ${note}`] : [],
424
+ "The agent cannot answer this prompt."
425
+ ].join("\n"),
426
+ ...note ? { note } : {}
427
+ };
428
+ } catch (err) {
429
+ return { ok: false, message: `aiwrangler override error: ${err instanceof Error ? err.message : String(err)}` };
430
+ }
431
+ }
432
+ function writeOverrides(dir, input) {
433
+ try {
434
+ const planned = planOverride(dir, input);
435
+ if (!planned.ok) return planned.message;
436
+ if (planned.kind === "noop") return planned.message;
437
+ const loaded = loadConfig(dir);
438
+ const ledger = new Ledger(loaded.config, loaded.root);
439
+ if (planned.kind === "clear") {
440
+ const existed = clearRuntimeOverrides(loaded.config, loaded.root);
441
+ if (existed) {
442
+ ledger.append({
443
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
444
+ kind: "override",
445
+ source: input.by,
446
+ changes: { cleared: "manual" },
447
+ confirmation: input.confirmation,
448
+ ...planned.note ? { note: planned.note } : {}
449
+ });
450
+ }
451
+ return existed ? `runtime overrides cleared by ${input.by} (confirmation: ${input.confirmation}); strict is the resting state again` : "no runtime overrides were active";
452
+ }
453
+ writeRuntimeOverridesFile(loaded.config, loaded.root, {
454
+ version: 1,
455
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
456
+ ...planned.note ? { note: planned.note } : {},
457
+ expiresOn: "pass",
458
+ overrides: planned.set
459
+ });
460
+ ledger.append({
461
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
462
+ kind: "override",
463
+ source: input.by,
464
+ changes: planned.set,
465
+ confirmation: input.confirmation,
466
+ ...planned.note ? { note: planned.note } : {}
467
+ });
468
+ const gateNote = planned.appliedGates.length > 0 ? `; gates applied: ${planned.appliedGates.join(", ")}` : "";
469
+ return `runtime overrides ACTIVE (expire on next PASS, audited, confirmation: ${input.confirmation})${gateNote}`;
470
+ } catch (err) {
471
+ return `aiwrangler override error: ${err instanceof Error ? err.message : String(err)}`;
472
+ }
473
+ }
474
+ async function requestHumanOverride(dir, input, channel) {
475
+ const planned = planOverride(dir, input);
476
+ if (!planned.ok) return planned.message;
477
+ if (planned.kind === "noop") return planned.message;
478
+ const approval = await approveOwnerWrite(planned.summary, channel);
479
+ if (!approval.ok) return approval.message;
480
+ return writeOverrides(dir, {
481
+ confirmation: approval.confirmation,
482
+ by: channel.by,
483
+ ...planned.kind === "clear" ? { clear: true } : { set: planned.set },
484
+ ...planned.note ? { note: planned.note } : {}
485
+ });
486
+ }
487
+ function loopResetGate(dir, by = "agent") {
488
+ try {
489
+ const loaded = loadConfig(dir);
490
+ const stateDir = expandPath(loaded.config.cache.dir, loaded.root);
491
+ const loopPath = join(stateDir, "loop-state.json");
492
+ if (!existsSync(loopPath)) return "no loop state to reset";
493
+ writeFileSync(loopPath, JSON.stringify({ cycles: [] }), "utf8");
494
+ new Ledger(loaded.config, loaded.root).append({
495
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
496
+ kind: "notice",
497
+ message: `loop state reset via ${by}`
498
+ });
499
+ return "circuit breaker reset; retry cycles cleared";
500
+ } catch (err) {
501
+ return `aiwrangler loop reset error: ${err instanceof Error ? err.message : String(err)}`;
502
+ }
503
+ }
504
+
505
+ export {
506
+ resolveGateDir,
507
+ configReport,
508
+ planGate,
509
+ checkGate,
510
+ isAckWriteAttempt,
511
+ isOverrideWriteAttempt,
512
+ listPendingAck,
513
+ planAck,
514
+ acknowledgeFindings,
515
+ requestHumanAck,
516
+ reportOverrides,
517
+ planOverride,
518
+ writeOverrides,
519
+ requestHumanOverride,
520
+ loopResetGate
521
+ };
522
+ //# sourceMappingURL=chunk-NQFO47CV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/osdialog.ts","../src/approve.ts","../src/core.ts"],"sourcesContent":["// Native desktop confirmation. The agent does not receive a token it can replay.\n// Headless sessions and AIWRANGLER_OS_DIALOG=off fail closed (no write).\nimport { spawn } from \"node:child_process\"\n\nexport type OsDialogResult = \"approve\" | \"deny\" | \"unavailable\"\n\nconst DIALOG_TIMEOUT_MS = 3 * 60 * 1000\n\nfunction hasLinuxGui(): boolean {\n return Boolean(process.env.DISPLAY || process.env.WAYLAND_DISPLAY || process.env.MIR_SOCKET)\n}\n\nfunction runCommand(\n command: string,\n args: string[],\n env?: NodeJS.ProcessEnv,\n): Promise<{ code: number | null; stdout: string; missing: boolean }> {\n return new Promise((resolve) => {\n const child = spawn(command, args, { env, stdio: [\"ignore\", \"pipe\", \"pipe\"] })\n let stdout = \"\"\n let settled = false\n const finish = (result: { code: number | null; stdout: string; missing: boolean }) => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n resolve(result)\n }\n const timer = setTimeout(() => {\n child.kill(\"SIGKILL\")\n finish({ code: null, stdout, missing: false })\n }, DIALOG_TIMEOUT_MS)\n child.stdout.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString()\n })\n child.on(\"error\", (err: NodeJS.ErrnoException) => {\n finish({ code: null, stdout, missing: err.code === \"ENOENT\" })\n })\n child.on(\"close\", (code) => {\n finish({ code, stdout, missing: false })\n })\n })\n}\n\nasync function osascript(summary: string): Promise<OsDialogResult> {\n const script = [\n \"on run argv\",\n \"set prompt to item 1 of argv\",\n 'set r to display dialog prompt buttons {\"Deny\", \"Approve\"} default button \"Deny\" with title \"AiWrangler\" with icon caution',\n \"return button returned of r\",\n \"end run\",\n ].join(\"\\n\")\n const result = await runCommand(\"osascript\", [\"-e\", script, \"--\", summary])\n if (result.missing) return \"unavailable\"\n if (result.code === 0 && result.stdout.includes(\"Approve\")) return \"approve\"\n return \"deny\"\n}\n\nasync function zenity(summary: string): Promise<OsDialogResult | \"missing\"> {\n const result = await runCommand(\"zenity\", [\n \"--question\",\n \"--title=AiWrangler\",\n \"--ok-label=Approve\",\n \"--cancel-label=Deny\",\n \"--width=480\",\n \"--text\",\n summary,\n ])\n if (result.missing) return \"missing\"\n return result.code === 0 ? \"approve\" : \"deny\"\n}\n\nasync function kdialog(summary: string): Promise<OsDialogResult | \"missing\"> {\n const result = await runCommand(\"kdialog\", [\"--title\", \"AiWrangler\", \"--yesno\", summary])\n if (result.missing) return \"missing\"\n return result.code === 0 ? \"approve\" : \"deny\"\n}\n\nasync function linuxDialog(summary: string): Promise<OsDialogResult> {\n if (!hasLinuxGui()) return \"unavailable\"\n const zen = await zenity(summary)\n if (zen !== \"missing\") return zen\n const kde = await kdialog(summary)\n if (kde !== \"missing\") return kde\n return \"unavailable\"\n}\n\nasync function windowsDialog(summary: string): Promise<OsDialogResult> {\n const script = [\n \"Add-Type -AssemblyName System.Windows.Forms\",\n \"$r = [System.Windows.Forms.MessageBox]::Show($env:AIWRANGLER_PROMPT, 'AiWrangler', 'YesNo', 'Warning', 'Button2')\",\n \"if ($r -eq [System.Windows.Forms.DialogResult]::Yes) { exit 0 } else { exit 1 }\",\n ].join(\"\\n\")\n const result = await runCommand(\"powershell.exe\", [\"-NoProfile\", \"-STA\", \"-Command\", script], {\n ...process.env,\n AIWRANGLER_PROMPT: summary,\n })\n if (result.missing) return \"unavailable\"\n if (result.code === null) return \"deny\"\n return result.code === 0 ? \"approve\" : \"deny\"\n}\n\n/** Ask the desktop user. Returns unavailable when no dialog can be shown. */\nexport async function confirmOsDialog(summary: string): Promise<OsDialogResult> {\n if (process.env.AIWRANGLER_OS_DIALOG === \"off\") return \"unavailable\"\n const text = summary.length > 3500 ? summary.slice(0, 3500) + \"\\n…\" : summary\n if (process.platform === \"darwin\") return osascript(text)\n if (process.platform === \"win32\") return windowsDialog(text)\n if (process.platform === \"linux\" || process.platform === \"freebsd\" || process.platform === \"openbsd\") return linuxDialog(text)\n return \"unavailable\"\n}\n","// Human approval for ack/override writes. Tool arguments are not an input here.\nimport type { HumanConfirmation } from \"./ledger.js\"\nimport { confirmOsDialog, type OsDialogResult } from \"./osdialog.js\"\n\nexport type ElicitOutcome = \"approve\" | \"deny\" | \"unsupported\" | { error: string }\n\nexport interface ApprovalChannel {\n elicit?: (summary: string) => Promise<ElicitOutcome>\n osDialog?: (summary: string) => Promise<OsDialogResult>\n}\n\nexport interface ApprovalGranted {\n ok: true\n confirmation: HumanConfirmation\n}\n\nexport interface ApprovalRefused {\n ok: false\n message: string\n}\n\nexport const OWNER_DECLINED = \"aiwrangler: the owner declined. Nothing was recorded.\"\n\nexport const NO_APPROVAL_CHANNEL =\n \"aiwrangler: no human approval channel is available. This client does not support MCP elicitation, and no desktop dialog could be shown. Nothing was recorded. The owner can run `aiwrangler ack` or `aiwrangler override` in a terminal and type yes.\"\n\n/**\n * Ask the human. MCP elicitation wins when the client supports it.\n * Otherwise try a native OS dialog. Never treat the absence of a channel as a yes.\n */\nexport async function approveOwnerWrite(summary: string, channel: ApprovalChannel = {}): Promise<ApprovalGranted | ApprovalRefused> {\n if (channel.elicit) {\n const outcome = await channel.elicit(summary)\n if (outcome === \"approve\") return { ok: true, confirmation: \"elicitation\" }\n if (outcome === \"deny\") return { ok: false, message: OWNER_DECLINED }\n if (typeof outcome === \"object\") {\n return { ok: false, message: `aiwrangler: elicitation failed (${outcome.error}). Nothing was recorded.` }\n }\n }\n const dialog = channel.osDialog ?? confirmOsDialog\n const os = await dialog(summary)\n if (os === \"approve\") return { ok: true, confirmation: \"os-dialog\" }\n if (os === \"deny\") return { ok: false, message: \"aiwrangler: the owner declined the desktop dialog. Nothing was recorded.\" }\n return { ok: false, message: NO_APPROVAL_CHANNEL }\n}\n","// Client-agnostic gate entry points. Ack and override writes go through\n// requestHumanAck / requestHumanOverride, which ask a human channel\n// (MCP elicitation, an OS dialog, or the CLI's TTY prompt). Tool arguments\n// never authorize a write.\nimport { loadConfig, findConfigFile, flattenGates, activeThresholds, expandPath } from \"./config.js\"\nimport { loadCorpus } from \"./groundtruth/corpus.js\"\nimport { sectionsOfKind, effectiveTruth } from \"./groundtruth/precedence.js\"\nimport { Jev } from \"./jev/client.js\"\nimport { runPlanGate } from \"./gates/plan/index.js\"\nimport { runCheck, formatReport } from \"./gates/check/run.js\"\nimport { approveOwnerWrite, type ApprovalChannel } from \"./approve.js\"\nimport { Ledger, type ApprovalHost, type GateLedgerEntry, type HumanConfirmation } from \"./ledger.js\"\nimport {\n readRuntimeOverridesFile,\n writeRuntimeOverridesFile,\n clearRuntimeOverrides,\n applyRuntimeOverrides,\n type RuntimeOverrides,\n} from \"./overrides.js\"\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\n\nconst KIND_ORDER = [\"spec\", \"rule\", \"divergence\", \"question\", \"door\", \"paper\", \"doc\"] as const\n\n/**\n * Resolve the directory a gate should operate on:\n * explicit argument > AIWRANGLER_DIR env > process cwd.\n */\nexport function resolveGateDir(dir?: string): string {\n return dir ?? process.env.AIWRANGLER_DIR ?? process.cwd()\n}\n\nexport function configReport(dir: string): string {\n const loaded = loadConfig(dir)\n const corpus = loadCorpus(loaded.config, loaded.root)\n const flags = flattenGates(loaded.config)\n const thresholds = activeThresholds(loaded.config)\n const counts = KIND_ORDER.map((kind) => `${kind}=${sectionsOfKind(corpus, kind).length}`)\n const ruled = effectiveTruth(corpus)\n const out = [\n `config: ${loaded.path ?? \"(defaults)\"} [${loaded.source}]`,\n `model: ${loaded.config.model} policy: ${loaded.config.activePolicy} (act>=${thresholds.actionThreshold}, review>=${thresholds.reviewThreshold})`,\n `egress: semantic=${loaded.config.egress.semantic ? \"ON (task/diff/spec text goes to api.typesafe.ai)\" : \"OFF (deterministic only)\"}`,\n \"\",\n \"gates:\",\n ...[...flags.entries()].map(([path, on]) => ` ${on ? \"on \" : \"off\"} ${path}`),\n \"\",\n `corpus: ${counts.join(\" \")} ruled=${ruled.length} hash=${corpus.hash.slice(0, 12)}`,\n ...corpus.sources.map((s) => ` ${s.path} (${s.hash.slice(0, 8)})`),\n ...(corpus.sections.length === 0 ? [\" (no ground truth configured; point corpus paths in aiwrangler.config.json at your spec, rules, and rulings)\"] : []),\n ]\n if (loaded.config.egress.semantic && loaded.config.egress.warnOncePerSession) {\n out.push(\"\", \"NOTE: the semantic gate sends task/diff/spec text to api.typesafe.ai. Set AIWRANGLER_EGRESS=off to disable.\")\n }\n return out.join(\"\\n\")\n}\n\nexport function formatContextPack(pack: {\n sections: { id: string; title: string; text: string }[]\n rulings: { id: string; title: string; text: string; ruling?: string }[]\n openItems: { id: string; title: string; text: string }[]\n doors: { id: string; title: string; text: string }[]\n}): string {\n const out: string[] = []\n out.push(\"GROUND-TRUTH CONTEXT PACK (cite these IDs; honor them while implementing):\")\n if (pack.sections.length > 0) {\n out.push(\"\", \"governing sections:\")\n for (const s of pack.sections) out.push(` [${s.id}] ${s.title}\\n ${s.text.replace(/\\s+/g, \" \").slice(0, 600)}`)\n }\n if (pack.rulings.length > 0) {\n out.push(\"\", \"recorded rulings (these override the paper and spec):\")\n for (const r of pack.rulings) out.push(` [${r.id}] ${r.title}\\n ${(r.ruling ?? r.text).replace(/\\s+/g, \" \").slice(0, 400)}`)\n }\n if (pack.doors.length > 0) {\n out.push(\"\", \"one-way doors / launch gates (never change without the owner):\")\n for (const d of pack.doors) out.push(` [${d.id}] ${d.title}\\n ${d.text.replace(/\\s+/g, \" \").slice(0, 400)}`)\n }\n if (pack.openItems.length > 0) {\n out.push(\"\", \"open items in this task's area (do not pick a side; ask the user if it blocks you):\")\n for (const o of pack.openItems) out.push(` [${o.id}] ${o.title}\\n ${o.text.replace(/\\s+/g, \" \").slice(0, 400)}`)\n }\n return out.join(\"\\n\")\n}\n\nexport async function planGate(\n dir: string,\n input: { task: string; plan?: string; sessionID?: string },\n): Promise<string> {\n if (!findConfigFile(dir) && !process.env.AIWRANGLER_CONFIG) {\n return \"aiwrangler: no aiwrangler.config.json found for this directory; plan gate inactive\"\n }\n try {\n const loaded = loadConfig(dir)\n const corpus = loadCorpus(loaded.config, loaded.root)\n const jev = new Jev(loaded.config, { cacheDir: loaded.root })\n const report = await runPlanGate(loaded.config, jev, corpus, {\n task: input.task,\n ...(input.plan ? { plan: input.plan } : {}),\n })\n try {\n new Ledger(loaded.config, loaded.root).append({\n ts: new Date().toISOString(),\n kind: \"plan\",\n model: loaded.config.model,\n corpusHash: corpus.hash,\n action: report.action,\n ...(report.usage ? { usage: report.usage } : {}),\n findings: [],\n ...(report.verdicts.length > 0 ? { verdicts: report.verdicts.map((v) => ({ question: v.question, noul: v.noul })) } : {}),\n reasons: report.reasons,\n ...(input.sessionID !== undefined ? { sessionID: input.sessionID } : {}),\n overridesActive: loaded.overrides !== null,\n })\n } catch {\n /* ledger append is best-effort; the gate result is authoritative */\n }\n const out = [\n `AIWRANGLER PLAN: ${report.action.toUpperCase()}${report.degraded ? \" (degraded: \" + report.degradedReason + \")\" : \"\"}`,\n ]\n if (report.verdicts.length > 0) {\n out.push(\"\", \"verdicts:\")\n for (const v of report.verdicts) out.push(` ${v.question.padEnd(22)} ${v.noul.toFixed(2)} ${v.interpretation}${v.subject ? \" [\" + v.subject + \"]\" : \"\"} ${v.label}`)\n }\n if (report.reasons.length > 0) {\n out.push(\"\", \"reasons:\")\n for (const r of report.reasons) out.push(` - ${r}`)\n }\n if (report.action === \"clarify\") out.push(\"\", \"Ask the user to resolve the flagged items before writing code.\")\n if (report.action === \"human\") out.push(\"\", \"STOP: this task touches a decided one-way door or flagged steering text; the owner must decide.\")\n out.push(\"\", formatContextPack(report.contextPack))\n return out.join(\"\\n\")\n } catch (err) {\n return `aiwrangler plan gate error: ${err instanceof Error ? err.message : String(err)}`\n }\n}\n\nexport async function checkGate(\n dir: string,\n input: {\n task?: string\n agentSummary?: string\n toolOutput?: string\n cargo?: boolean\n tests?: boolean\n sessionID?: string\n } = {},\n): Promise<string> {\n if (!findConfigFile(dir) && !process.env.AIWRANGLER_CONFIG) {\n return \"aiwrangler: no aiwrangler.config.json found for this directory; check gate inactive\"\n }\n try {\n const loaded = loadConfig(dir)\n const corpus = loadCorpus(loaded.config, loaded.root)\n const jev = new Jev(loaded.config, { cacheDir: loaded.root })\n const report = await runCheck(loaded.config, loaded.root, corpus, jev, {\n ...(input.task ? { task: input.task } : {}),\n ...(input.agentSummary ? { agentSummary: input.agentSummary } : {}),\n ...(input.toolOutput ? { toolOutput: input.toolOutput } : {}),\n cargo: input.cargo || input.tests || false,\n tests: input.tests || false,\n ...(input.sessionID !== undefined ? { sessionID: input.sessionID } : {}),\n })\n return formatReport(report)\n } catch (err) {\n return `aiwrangler check gate error: ${err instanceof Error ? err.message : String(err)}`\n }\n}\n\nexport function isAckWriteAttempt(input: {\n rules?: readonly string[] | undefined\n note?: string | undefined\n diffHash?: string | undefined\n}): boolean {\n return input.rules !== undefined || input.note !== undefined || input.diffHash !== undefined\n}\n\nexport function isOverrideWriteAttempt(input: {\n set?: unknown\n clear?: boolean | undefined\n note?: string | undefined\n}): boolean {\n return input.set !== undefined || input.clear === true || input.note !== undefined\n}\n\nfunction latestDiffEntry(ledger: Ledger): GateLedgerEntry | undefined {\n const entries = ledger.read().filter((e): e is GateLedgerEntry => e.kind === \"check\" || e.kind === \"plan\")\n return [...entries].reverse().find((e) => e.diffHash !== undefined)\n}\n\n/**\n * Read-only list of pending human-severity findings. Safe for MCP and\n * opencode: it never appends an ack.\n */\nexport function listPendingAck(dir: string): string {\n try {\n const loaded = loadConfig(dir)\n const ledger = new Ledger(loaded.config, loaded.root)\n const latest = latestDiffEntry(ledger)\n if (!latest) {\n return \"aiwrangler: no gate run recorded yet; run wrangler_check first\"\n }\n const pending = (latest.findings ?? []).filter((f) => f.severity === \"human\" && !f.acknowledged)\n const out = [`pending human review (check at ${latest.ts}, action: ${latest.action})`, `diffHash: ${latest.diffHash}`]\n if (pending.length === 0) {\n out.push(\"\", \"no pending human-severity findings on the latest recorded diff\")\n if (latest.action === \"human\") out.push(\"(the human verdict came from the semantic layer; re-run wrangler_check to re-confirm)\")\n } else {\n const example = pending\n .map((f) => f.rule)\n .slice(0, 8)\n .join(\",\")\n out.push(\"\", \"findings needing the owner's eyes:\")\n for (const f of pending) out.push(` ${f.rule} ${f.subject} [${f.severity}]`)\n out.push(\n \"\",\n \"the agent cannot answer the approval prompt. Call wrangler_ack with those rules to ask the owner in the editor,\",\n `or the owner can run \\`aiwrangler ack --rules ${example}\\` in a terminal.`,\n \"the ack binds to the diffHash above and is void if the diff changes.\",\n \"block-severity findings can never be acknowledged; fix them instead.\",\n )\n }\n return out.join(\"\\n\")\n } catch (err) {\n return `aiwrangler ack error: ${err instanceof Error ? err.message : String(err)}`\n }\n}\n\nexport interface PlannedAck {\n ok: true\n rules: string[]\n diffHash: string\n note?: string\n summary: string\n}\n\nexport type AckPlan = PlannedAck | { ok: false; message: string }\n\n/** Validate an ack and build the human-facing summary. Does not write. */\nexport function planAck(dir: string, input: { rules: string[]; note?: string; diffHash?: string }): AckPlan {\n try {\n const loaded = loadConfig(dir)\n const ledger = new Ledger(loaded.config, loaded.root)\n const rules = input.rules.filter((r) => typeof r === \"string\" && r.length > 0)\n if (rules.length === 0) return { ok: false, message: 'aiwrangler: rules must be a non-empty array (use [\"*\"] to ack all human findings)' }\n let diffHash = input.diffHash\n if (!diffHash) {\n diffHash = latestDiffEntry(ledger)?.diffHash\n if (!diffHash) return { ok: false, message: \"aiwrangler: no recorded gate run to ack; run wrangler_check first (or pass diffHash explicitly)\" }\n }\n if (!/^[0-9a-f]{16,64}$/.test(diffHash)) return { ok: false, message: \"aiwrangler: invalid diffHash\" }\n const match = ledger\n .read()\n .filter((e): e is GateLedgerEntry => (e.kind === \"check\" || e.kind === \"plan\") && e.diffHash === diffHash)\n .at(-1)\n if (!match) return { ok: false, message: \"aiwrangler: diffHash not found in ledger; run wrangler_check for that diff first\" }\n const note = input.note && input.note.trim() ? input.note.trim().slice(0, 500) : undefined\n const pending = (match.findings ?? []).filter((f) => f.severity === \"human\")\n const summary = [\n \"AiWrangler: approve acknowledging these HUMAN REVIEW findings?\",\n `rules: ${rules.join(\", \")}`,\n `diffHash: ${diffHash}`,\n ...(note ? [`note: ${note}`] : []),\n \"findings on that diff:\",\n ...(pending.length > 0 ? pending.slice(0, 20).map((f) => ` ${f.rule} ${f.subject} [${f.severity}]`) : [\" (no human-severity rows stored on that run)\"]),\n \"The agent cannot answer this prompt. Approve only if you reviewed the diff.\",\n \"Block-severity findings are never covered. A diff change voids the ack.\",\n ].join(\"\\n\")\n return { ok: true, rules: rules.slice(0, 50), diffHash, summary, ...(note ? { note } : {}) }\n } catch (err) {\n return { ok: false, message: `aiwrangler ack error: ${err instanceof Error ? err.message : String(err)}` }\n }\n}\n\n/**\n * Record an owner acknowledgment bound to a diff hash. `confirmation` comes\n * from a human channel (elicitation, OS dialog, or TTY), never from tool args.\n * Block-severity findings are never covered. A later diff change voids the ack.\n */\nexport function acknowledgeFindings(\n dir: string,\n input: { rules: string[]; note?: string; diffHash: string; confirmation: HumanConfirmation; by: ApprovalHost },\n): string {\n try {\n const planned = planAck(dir, input)\n if (!planned.ok) return planned.message\n const loaded = loadConfig(dir)\n const entry = {\n ts: new Date().toISOString(),\n kind: \"ack\" as const,\n diffHash: planned.diffHash,\n rules: planned.rules,\n ...(planned.note ? { note: planned.note } : {}),\n by: input.by,\n confirmation: input.confirmation,\n }\n new Ledger(loaded.config, loaded.root).append(entry)\n return [\n `acknowledged: human-severity findings ${JSON.stringify(entry.rules)} for diff ${planned.diffHash.slice(0, 16)}`,\n `recorded by ${input.by} (confirmation: ${input.confirmation})`,\n \"the next wrangler_check on this exact diff will pass them with an owner-acknowledged reason.\",\n \"any diff change voids the ack; block-severity findings are never ackable.\",\n ].join(\"\\n\")\n } catch (err) {\n return `aiwrangler ack error: ${err instanceof Error ? err.message : String(err)}`\n }\n}\n\n/** Validate, ask the human, then append the ack. Tool arguments alone never write. */\nexport async function requestHumanAck(\n dir: string,\n input: { rules: string[]; note?: string; diffHash?: string },\n channel: ApprovalChannel & { by: ApprovalHost },\n): Promise<string> {\n const planned = planAck(dir, input)\n if (!planned.ok) return planned.message\n const approval = await approveOwnerWrite(planned.summary, channel)\n if (!approval.ok) return approval.message\n return acknowledgeFindings(dir, {\n rules: planned.rules,\n diffHash: planned.diffHash,\n confirmation: approval.confirmation,\n by: channel.by,\n ...(planned.note ? { note: planned.note } : {}),\n })\n}\n\n/**\n * Read-only report of active runtime overrides. Safe for MCP and opencode.\n */\nexport function reportOverrides(dir: string): string {\n try {\n const loaded = loadConfig(dir)\n const file = readRuntimeOverridesFile(loaded.config, loaded.root)\n if (!file) return \"runtime overrides: none active (strict defaults in effect). The owner sets them with `aiwrangler override`.\"\n return [\n \"runtime overrides ACTIVE:\",\n JSON.stringify(file.overrides, null, 2),\n `created: ${file.createdAt} expiresOn: ${file.expiresOn}${file.note ? ` note: ${file.note}` : \"\"}`,\n \"they auto-expire on the next PASS verdict; wrangler_config shows the effective state.\",\n \"to change them, call wrangler_override with set or clear. That asks the owner; it does not apply the change by itself.\",\n \"the owner can also run `aiwrangler override` in a terminal.\",\n ].join(\"\\n\")\n } catch (err) {\n return `aiwrangler override error: ${err instanceof Error ? err.message : String(err)}`\n }\n}\n\nexport type OverridePlan =\n | { ok: false; message: string }\n | { ok: true; kind: \"noop\"; message: string }\n | { ok: true; kind: \"clear\"; note?: string; summary: string }\n | { ok: true; kind: \"set\"; set: RuntimeOverrides; note?: string; summary: string; appliedGates: string[] }\n\n/** Validate an override change and build the human-facing summary. Does not write. */\nexport function planOverride(dir: string, input: { set?: RuntimeOverrides; clear?: boolean; note?: string }): OverridePlan {\n try {\n if (input.clear && input.set) return { ok: false, message: \"aiwrangler: pass only one of set or clear; nothing was changed\" }\n const loaded = loadConfig(dir)\n const note = input.note && input.note.trim() ? input.note.trim().slice(0, 300) : undefined\n if (input.clear) {\n if (!readRuntimeOverridesFile(loaded.config, loaded.root)) {\n return { ok: true, kind: \"noop\", message: \"no runtime overrides were active\" }\n }\n return {\n ok: true,\n kind: \"clear\",\n summary: [\n \"AiWrangler: clear runtime overrides and restore the strict resting state?\",\n ...(note ? [`note: ${note}`] : []),\n \"The agent cannot answer this prompt.\",\n ].join(\"\\n\"),\n ...(note ? { note } : {}),\n }\n }\n if (!input.set) return { ok: false, message: \"aiwrangler: override write needs set or clear\" }\n let appliedGates: string[] = []\n try {\n appliedGates = applyRuntimeOverrides(loaded.config, input.set).appliedGates\n } catch (err) {\n return { ok: false, message: `aiwrangler: invalid overrides: ${err instanceof Error ? err.message : String(err)}` }\n }\n return {\n ok: true,\n kind: \"set\",\n set: input.set,\n appliedGates,\n summary: [\n \"AiWrangler: apply these runtime overrides? They expire on the next PASS.\",\n JSON.stringify(input.set, null, 2),\n ...(note ? [`note: ${note}`] : []),\n \"The agent cannot answer this prompt.\",\n ].join(\"\\n\"),\n ...(note ? { note } : {}),\n }\n } catch (err) {\n return { ok: false, message: `aiwrangler override error: ${err instanceof Error ? err.message : String(err)}` }\n }\n}\n\n/**\n * Set or clear runtime overrides after a human confirmation. Writes expire on\n * the next PASS. The ledger records the host and the confirmation method.\n */\nexport function writeOverrides(\n dir: string,\n input: { set?: RuntimeOverrides; clear?: boolean; note?: string; confirmation: HumanConfirmation; by: ApprovalHost },\n): string {\n try {\n const planned = planOverride(dir, input)\n if (!planned.ok) return planned.message\n if (planned.kind === \"noop\") return planned.message\n const loaded = loadConfig(dir)\n const ledger = new Ledger(loaded.config, loaded.root)\n if (planned.kind === \"clear\") {\n const existed = clearRuntimeOverrides(loaded.config, loaded.root)\n if (existed) {\n ledger.append({\n ts: new Date().toISOString(),\n kind: \"override\",\n source: input.by,\n changes: { cleared: \"manual\" },\n confirmation: input.confirmation,\n ...(planned.note ? { note: planned.note } : {}),\n })\n }\n return existed\n ? `runtime overrides cleared by ${input.by} (confirmation: ${input.confirmation}); strict is the resting state again`\n : \"no runtime overrides were active\"\n }\n writeRuntimeOverridesFile(loaded.config, loaded.root, {\n version: 1,\n createdAt: new Date().toISOString(),\n ...(planned.note ? { note: planned.note } : {}),\n expiresOn: \"pass\",\n overrides: planned.set,\n })\n ledger.append({\n ts: new Date().toISOString(),\n kind: \"override\",\n source: input.by,\n changes: planned.set as unknown as Record<string, unknown>,\n confirmation: input.confirmation,\n ...(planned.note ? { note: planned.note } : {}),\n })\n const gateNote = planned.appliedGates.length > 0 ? `; gates applied: ${planned.appliedGates.join(\", \")}` : \"\"\n return `runtime overrides ACTIVE (expire on next PASS, audited, confirmation: ${input.confirmation})${gateNote}`\n } catch (err) {\n return `aiwrangler override error: ${err instanceof Error ? err.message : String(err)}`\n }\n}\n\n/** Validate, ask the human, then write the override. Tool arguments alone never write. */\nexport async function requestHumanOverride(\n dir: string,\n input: { set?: RuntimeOverrides; clear?: boolean; note?: string },\n channel: ApprovalChannel & { by: ApprovalHost },\n): Promise<string> {\n const planned = planOverride(dir, input)\n if (!planned.ok) return planned.message\n if (planned.kind === \"noop\") return planned.message\n const approval = await approveOwnerWrite(planned.summary, channel)\n if (!approval.ok) return approval.message\n return writeOverrides(dir, {\n confirmation: approval.confirmation,\n by: channel.by,\n ...(planned.kind === \"clear\" ? { clear: true } : { set: planned.set }),\n ...(planned.note ? { note: planned.note } : {}),\n })\n}\n\n/**\n * Circuit-breaker reset (ported from the retired webui control panel).\n * Resets `.aiwrangler/loop-state.json` and audits a notice.\n */\nexport function loopResetGate(dir: string, by = \"agent\"): string {\n try {\n const loaded = loadConfig(dir)\n const stateDir = expandPath(loaded.config.cache.dir, loaded.root)\n const loopPath = join(stateDir, \"loop-state.json\")\n if (!existsSync(loopPath)) return \"no loop state to reset\"\n writeFileSync(loopPath, JSON.stringify({ cycles: [] }), \"utf8\")\n new Ledger(loaded.config, loaded.root).append({\n ts: new Date().toISOString(),\n kind: \"notice\",\n message: `loop state reset via ${by}`,\n })\n return \"circuit breaker reset; retry cycles cleared\"\n } catch (err) {\n return `aiwrangler loop reset error: ${err instanceof Error ? err.message : String(err)}`\n }\n}\n\n\n"],"mappings":"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","names":[]}