pi-warden 0.26.0 → 0.28.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/README.md +56 -176
- package/dist/config.d.ts +18 -0
- package/dist/config.js +48 -0
- package/dist/extension.d.ts +1 -1
- package/dist/extension.js +148 -41
- package/dist/extension.js.bak +148 -41
- package/dist/guard.d.ts +5 -0
- package/dist/guard.js +5 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +4 -3
- package/dist/learning.d.ts +51 -5
- package/dist/learning.js +219 -29
- package/dist/output.d.ts +31 -1
- package/dist/output.js +80 -1
- package/dist/panel.d.ts +29 -0
- package/dist/panel.js +257 -10
- package/dist/saver.d.ts +2 -0
- package/dist/saver.js +4 -0
- package/dist/shape.js +2 -1
- package/dist/trace.d.ts +2 -0
- package/dist/widget.d.ts +96 -0
- package/dist/widget.js +259 -1
- package/package.json +1 -1
package/dist/panel.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Key, matchesKey, truncateToWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
2
|
+
import { applyUserOverrides, defaultConfig, readUserConfig, writeUserConfig } from "./config.js";
|
|
2
3
|
import { LEVEL_COLOR, parseVerdictLine, renderSegment } from "./widget.js";
|
|
3
4
|
/**
|
|
4
5
|
* Sidebar listing the trace newest-first, live-updating while open. It opens without taking keyboard input: the editor
|
|
@@ -16,6 +17,8 @@ export class TracePanel {
|
|
|
16
17
|
focused = false;
|
|
17
18
|
scroll = 0;
|
|
18
19
|
viewport = 20;
|
|
20
|
+
/** Compact mode: show 1-3 lines per entry. Toggle with `d` while focused. */
|
|
21
|
+
compact = true;
|
|
19
22
|
unsubscribe;
|
|
20
23
|
constructor(trace, theme, actions, requestRender, title = "pi-warden trace") {
|
|
21
24
|
this.trace = trace;
|
|
@@ -31,10 +34,13 @@ export class TracePanel {
|
|
|
31
34
|
/** Rendering is cheap and derived from the trace each time; nothing is cached. */
|
|
32
35
|
invalidate() { }
|
|
33
36
|
lines(width) {
|
|
34
|
-
const { theme } = this;
|
|
37
|
+
const { theme, compact } = this;
|
|
35
38
|
const entries = this.trace.entries();
|
|
36
39
|
const out = [];
|
|
37
|
-
const
|
|
40
|
+
const modeLabel = compact ? "compact" : "detailed";
|
|
41
|
+
const keys = this.focused
|
|
42
|
+
? `↑↓ PgUp PgDn scroll · d ${modeLabel} · c clear · esc back · q close`
|
|
43
|
+
: `click for keys · wheel scrolls · /warden trace closes`;
|
|
38
44
|
out.push(theme.bold(theme.fg("accent", `${this.title}`)) + theme.fg("muted", ` · ${entries.length} event${entries.length === 1 ? "" : "s"}`));
|
|
39
45
|
out.push(theme.fg("muted", keys));
|
|
40
46
|
out.push(theme.fg("borderMuted", "─".repeat(Math.max(0, width))));
|
|
@@ -43,8 +49,6 @@ export class TracePanel {
|
|
|
43
49
|
out.push(line);
|
|
44
50
|
return out.map(line => truncateToWidth(line, width, ""));
|
|
45
51
|
}
|
|
46
|
-
// Left rail: time and guard label. The verdict leads the right column as a bold chip, so a scan down the pane reads
|
|
47
|
-
// the decisions first and the evidence after.
|
|
48
52
|
const RAIL = 16;
|
|
49
53
|
for (let index = entries.length - 1; index >= 0; index--) {
|
|
50
54
|
const entry = entries[index];
|
|
@@ -56,12 +60,20 @@ export class TracePanel {
|
|
|
56
60
|
const rendered = body.map((segment, n) => renderSegment(segment, n === 0, theme)).join(theme.fg("dim", " · "));
|
|
57
61
|
const wrapped = wrapTextWithAnsi(rendered, Math.max(10, width - indent));
|
|
58
62
|
out.push(head + chip + (wrapped[0] ?? ""));
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
if (!compact) {
|
|
64
|
+
for (const continuation of wrapped.slice(1))
|
|
65
|
+
out.push(" ".repeat(indent) + continuation);
|
|
66
|
+
for (const detail of entry.details) {
|
|
67
|
+
const detailLines = wrapTextWithAnsi(detail, Math.max(10, width - RAIL - 2));
|
|
68
|
+
for (const [n, line] of detailLines.entries())
|
|
69
|
+
out.push(" ".repeat(RAIL) + theme.fg("muted", n === 0 ? "· " : " ") + theme.fg("dim", line));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (entry.details.length > 0) {
|
|
73
|
+
// Compact: show only the first detail line, truncated
|
|
74
|
+
const firstDetail = entry.details[0];
|
|
75
|
+
const truncated = firstDetail.length > width - indent - 4 ? firstDetail.slice(0, width - indent - 4) + "…" : firstDetail;
|
|
76
|
+
out.push(" ".repeat(indent) + theme.fg("dim", truncated));
|
|
65
77
|
}
|
|
66
78
|
out.push("");
|
|
67
79
|
}
|
|
@@ -101,6 +113,11 @@ export class TracePanel {
|
|
|
101
113
|
this.requestRender();
|
|
102
114
|
return;
|
|
103
115
|
}
|
|
116
|
+
if (data === "d") {
|
|
117
|
+
this.compact = !this.compact;
|
|
118
|
+
this.requestRender();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
104
121
|
if (matchesKey(data, Key.up) || data === "k")
|
|
105
122
|
this.scroll = Math.max(0, this.scroll - 1);
|
|
106
123
|
else if (matchesKey(data, Key.down) || data === "j")
|
|
@@ -153,3 +170,233 @@ export function openTracePanel(ui, trace, options = {}) {
|
|
|
153
170
|
});
|
|
154
171
|
return { closed, close: () => close() };
|
|
155
172
|
}
|
|
173
|
+
function flattenConfig(obj, prefix = "") {
|
|
174
|
+
const entries = [];
|
|
175
|
+
const skip = new Set(["action", "stuck", "done", "slop", "security", "rules", "context", "runaway", "notify", "subagent"]);
|
|
176
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
177
|
+
if (skip.has(key) && !prefix)
|
|
178
|
+
continue;
|
|
179
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
180
|
+
if (typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
|
|
181
|
+
entries.push({ path, label: key, level: prefix ? prefix.split(".").length : 0, value, type: typeof value, editable: true });
|
|
182
|
+
}
|
|
183
|
+
else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
184
|
+
entries.push({ path, label: key, level: prefix ? prefix.split(".").length : 0, value: "", type: "string", editable: false });
|
|
185
|
+
entries.push(...flattenConfig(value, path));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return entries;
|
|
189
|
+
}
|
|
190
|
+
export class ConfigPanel {
|
|
191
|
+
config;
|
|
192
|
+
theme;
|
|
193
|
+
actions;
|
|
194
|
+
requestRender;
|
|
195
|
+
focused = false;
|
|
196
|
+
cursor = 0;
|
|
197
|
+
scroll = 0;
|
|
198
|
+
viewport = 20;
|
|
199
|
+
editing = false;
|
|
200
|
+
editBuffer = "";
|
|
201
|
+
edits = new Map();
|
|
202
|
+
entries;
|
|
203
|
+
constructor(config, theme, actions, requestRender) {
|
|
204
|
+
this.config = config;
|
|
205
|
+
this.theme = theme;
|
|
206
|
+
this.actions = actions;
|
|
207
|
+
this.requestRender = requestRender;
|
|
208
|
+
this.entries = flattenConfig(config);
|
|
209
|
+
}
|
|
210
|
+
invalidate() { }
|
|
211
|
+
lines(width) {
|
|
212
|
+
const { theme, entries, cursor, scroll, editing, editBuffer, edits } = this;
|
|
213
|
+
const out = [];
|
|
214
|
+
const modeLabel = editing ? "editing" : "browsing";
|
|
215
|
+
const keys = this.focused
|
|
216
|
+
? `↑↓ navigate · enter toggle/edit · esc ${editing ? "cancel" : "back"} · s save · ${modeLabel}`
|
|
217
|
+
: `click for keys · /warden config closes`;
|
|
218
|
+
out.push(theme.bold(theme.fg("accent", "pi-warden config")) + theme.fg("muted", ` · ${entries.length} keys`));
|
|
219
|
+
out.push(theme.fg("muted", keys));
|
|
220
|
+
out.push(theme.fg("borderMuted", "─".repeat(Math.max(0, width))));
|
|
221
|
+
for (let index = 0; index < entries.length; index++) {
|
|
222
|
+
const entry = entries[index];
|
|
223
|
+
const isSelected = index === cursor;
|
|
224
|
+
const indent = " ".repeat(entry.level);
|
|
225
|
+
const changed = edits.has(entry.path);
|
|
226
|
+
const label = entry.label + (entry.editable ? "" : "/");
|
|
227
|
+
if (!entry.editable) {
|
|
228
|
+
out.push(indent + theme.fg("accent", theme.bold(label)));
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
let valueStr;
|
|
232
|
+
if (isSelected && editing && entry.type !== "boolean") {
|
|
233
|
+
valueStr = editBuffer + "\u2588";
|
|
234
|
+
}
|
|
235
|
+
else if (entry.type === "boolean") {
|
|
236
|
+
valueStr = entry.value ? "●" : "○";
|
|
237
|
+
}
|
|
238
|
+
else if (entry.type === "number") {
|
|
239
|
+
valueStr = String(entry.value);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
valueStr = typeof entry.value === "string" && entry.value.length > 40 ? entry.value.slice(0, 37) + "…" : String(entry.value);
|
|
243
|
+
}
|
|
244
|
+
const changedMark = changed ? theme.fg("warning", " *") : "";
|
|
245
|
+
const line = `${indent}${isSelected ? "> " : " "}${theme.fg("muted", label)} ${theme.fg("text", valueStr)}${changedMark}`;
|
|
246
|
+
if (isSelected) {
|
|
247
|
+
out.push(theme.bold(line));
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
out.push(line);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return out.map(line => truncateToWidth(line, width, ""));
|
|
254
|
+
}
|
|
255
|
+
render(width) {
|
|
256
|
+
const border = theme_fg(this.theme, "muted", "│ ");
|
|
257
|
+
const inner = Math.max(10, width - 2);
|
|
258
|
+
const all = this.lines(inner);
|
|
259
|
+
const maxScroll = Math.max(0, all.length - this.viewport);
|
|
260
|
+
if (this.scroll > maxScroll)
|
|
261
|
+
this.scroll = maxScroll;
|
|
262
|
+
const visible = all.slice(this.scroll, this.scroll + this.viewport);
|
|
263
|
+
if (all.length > this.viewport) {
|
|
264
|
+
const last = visible.length - 1;
|
|
265
|
+
const below = all.length - this.scroll - this.viewport;
|
|
266
|
+
const above = this.scroll;
|
|
267
|
+
const label = below > 0 ? `… ${below} more below${above ? ` · ${above} above` : ""}` : `… end${above ? ` · ${above} above` : ""}`;
|
|
268
|
+
visible[last] = truncateToWidth(theme_fg(this.theme, "dim", label), inner, "");
|
|
269
|
+
}
|
|
270
|
+
while (visible.length < this.viewport)
|
|
271
|
+
visible.push("");
|
|
272
|
+
return visible.map(line => border + line);
|
|
273
|
+
}
|
|
274
|
+
setViewport(rows) { this.viewport = Math.max(5, rows); }
|
|
275
|
+
handleInput(data) {
|
|
276
|
+
const { entries, editing } = this;
|
|
277
|
+
if (data === "q" || matchesKey(data, Key.ctrl("c"))) {
|
|
278
|
+
this.actions.close();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (matchesKey(data, Key.escape)) {
|
|
282
|
+
if (editing) {
|
|
283
|
+
this.editing = false;
|
|
284
|
+
this.requestRender();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
this.actions.unfocus();
|
|
288
|
+
this.requestRender();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (editing) {
|
|
292
|
+
const entry = entries[this.cursor];
|
|
293
|
+
if (matchesKey(data, Key.return)) {
|
|
294
|
+
this.edits.set(entry.path, this.parseValue(this.editBuffer, entry.type));
|
|
295
|
+
(entries[this.cursor]).value = this.parseValue(this.editBuffer, entry.type);
|
|
296
|
+
this.editing = false;
|
|
297
|
+
this.requestRender();
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (matchesKey(data, Key.backspace)) {
|
|
301
|
+
this.editBuffer = this.editBuffer.slice(0, -1);
|
|
302
|
+
this.requestRender();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (data.length === 1 && data >= " ") {
|
|
306
|
+
this.editBuffer += data;
|
|
307
|
+
this.requestRender();
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (matchesKey(data, Key.up) || data === "k")
|
|
313
|
+
this.cursor = Math.max(0, this.cursor - 1);
|
|
314
|
+
else if (matchesKey(data, Key.down) || data === "j")
|
|
315
|
+
this.cursor = Math.min(entries.length - 1, this.cursor + 1);
|
|
316
|
+
else if (matchesKey(data, Key.pageUp))
|
|
317
|
+
this.cursor = Math.max(0, this.cursor - this.viewport);
|
|
318
|
+
else if (matchesKey(data, Key.pageDown))
|
|
319
|
+
this.cursor = Math.min(entries.length - 1, this.cursor + this.viewport);
|
|
320
|
+
else if (matchesKey(data, Key.home))
|
|
321
|
+
this.cursor = 0;
|
|
322
|
+
else if (matchesKey(data, Key.end))
|
|
323
|
+
this.cursor = entries.length - 1;
|
|
324
|
+
else if (data === "enter" || matchesKey(data, Key.return)) {
|
|
325
|
+
const entry = entries[this.cursor];
|
|
326
|
+
if (entry.type === "boolean") {
|
|
327
|
+
this.edits.set(entry.path, !entry.value);
|
|
328
|
+
entry.value = !entry.value;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
this.editing = true;
|
|
332
|
+
this.editBuffer = String(entry.value);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
else if (data === "s") {
|
|
336
|
+
this.save();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
else
|
|
340
|
+
return;
|
|
341
|
+
// Keep cursor visible
|
|
342
|
+
if (this.cursor < this.scroll)
|
|
343
|
+
this.scroll = this.cursor;
|
|
344
|
+
else if (this.cursor >= this.scroll + this.viewport)
|
|
345
|
+
this.scroll = this.cursor - this.viewport + 1;
|
|
346
|
+
this.requestRender();
|
|
347
|
+
}
|
|
348
|
+
handleMouse(event) {
|
|
349
|
+
if (event.type === "wheel") {
|
|
350
|
+
this.scroll = Math.max(0, this.scroll + (event.wheelDelta ?? 0) * 3);
|
|
351
|
+
return { handled: true, render: true };
|
|
352
|
+
}
|
|
353
|
+
if (event.type === "press" && event.button === "left")
|
|
354
|
+
return { handled: true, focus: true, render: true };
|
|
355
|
+
return undefined;
|
|
356
|
+
}
|
|
357
|
+
parseValue(raw, type) {
|
|
358
|
+
if (type === "boolean")
|
|
359
|
+
return raw === "true" || raw === "1";
|
|
360
|
+
if (type === "number") {
|
|
361
|
+
const n = Number(raw);
|
|
362
|
+
return Number.isFinite(n) ? n : raw;
|
|
363
|
+
}
|
|
364
|
+
return raw;
|
|
365
|
+
}
|
|
366
|
+
save() {
|
|
367
|
+
if (this.edits.size === 0)
|
|
368
|
+
return;
|
|
369
|
+
const raw = readUserConfig();
|
|
370
|
+
const obj = (typeof raw === "object" && raw !== null && !Array.isArray(raw)) ? raw : {};
|
|
371
|
+
for (const [path, value] of this.edits) {
|
|
372
|
+
const keys = path.split(".");
|
|
373
|
+
let current = obj;
|
|
374
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
375
|
+
const k = keys[i];
|
|
376
|
+
if (typeof current[k] !== "object" || current[k] === null)
|
|
377
|
+
current[k] = {};
|
|
378
|
+
current = current[k];
|
|
379
|
+
}
|
|
380
|
+
current[keys.at(-1)] = value;
|
|
381
|
+
}
|
|
382
|
+
writeUserConfig(obj);
|
|
383
|
+
this.edits.clear();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
export function openConfigPanel(ui, config, options = {}) {
|
|
387
|
+
let close = () => { };
|
|
388
|
+
let unfocus = () => { };
|
|
389
|
+
const closed = ui.custom((tui, theme, _keybindings, done) => {
|
|
390
|
+
close = () => done();
|
|
391
|
+
const panel = new ConfigPanel(config, theme, { close, unfocus: () => unfocus() }, () => tui.requestRender());
|
|
392
|
+
const rows = tui.terminal?.rows;
|
|
393
|
+
if (typeof rows === "number")
|
|
394
|
+
panel.setViewport(rows - 2);
|
|
395
|
+
return panel;
|
|
396
|
+
}, {
|
|
397
|
+
overlay: true,
|
|
398
|
+
overlayOptions: { anchor: "right-center", width: options.width ?? "40%", minWidth: 44, maxHeight: "100%", nonCapturing: true },
|
|
399
|
+
onHandle: (handle) => { unfocus = () => handle.unfocus(); },
|
|
400
|
+
});
|
|
401
|
+
return { closed, close: () => close() };
|
|
402
|
+
}
|
package/dist/saver.d.ts
CHANGED
|
@@ -48,6 +48,8 @@ export declare class ContextLedger {
|
|
|
48
48
|
/** Counts the first access to a compressed output; later accesses and duplicate-only copies are not new recalls. */
|
|
49
49
|
noteAccess(text: string, kind?: RecallKind): string | undefined;
|
|
50
50
|
snapshot(): ContextLedgerSnapshot;
|
|
51
|
+
/** Paths to temp files for cleanup at session start. Internal only — paths never leave the machine. */
|
|
52
|
+
storedPaths(): string[];
|
|
51
53
|
reset(): void;
|
|
52
54
|
}
|
|
53
55
|
/** One line for /warden status. Says when there is nothing to report instead of printing zeros. */
|
package/dist/saver.js
CHANGED
|
@@ -69,6 +69,10 @@ export class ContextLedger {
|
|
|
69
69
|
snapshot() {
|
|
70
70
|
return { large: this.large, compressed: this.compressed, duplicates: this.duplicates, bytesSaved: this.bytesSaved, turns: this.turns, tokenTurnsSaved: this.tokenTurnsSaved, recalls: this.recalls, recallsFull: this.recallsFull };
|
|
71
71
|
}
|
|
72
|
+
/** Paths to temp files for cleanup at session start. Internal only — paths never leave the machine. */
|
|
73
|
+
storedPaths() {
|
|
74
|
+
return [...this.stored.keys()];
|
|
75
|
+
}
|
|
72
76
|
reset() {
|
|
73
77
|
this.large = 0;
|
|
74
78
|
this.compressed = 0;
|
package/dist/shape.js
CHANGED
|
@@ -40,7 +40,8 @@ export function completeConfig(loaded) {
|
|
|
40
40
|
runaway: section("runaway", { ...off, repeats: Number.MAX_SAFE_INTEGER, thinkingRepeats: Number.MAX_SAFE_INTEGER, minChars: Number.MAX_SAFE_INTEGER, recover: false }),
|
|
41
41
|
notify: section("notify", { ...off, cooldownMs: 0, command: [] }),
|
|
42
42
|
subagent: section("subagent", { ...off, wake: false, threshold: 1, cooldownMs: 0 }),
|
|
43
|
-
widget: section("widget", { ...off, placement: "aboveEditor", shortcut: "", panelWidth: "40%", action: "", stuck: "", done: "", prose: "", security: "", context: "", runaway: "", rules: "", subagent: "" }),
|
|
43
|
+
widget: section("widget", { ...off, placement: "aboveEditor", barMode: "live", shortcut: "", panelWidth: "40%", action: "", stuck: "", done: "", prose: "", security: "", context: "", runaway: "", rules: "", subagent: "" }),
|
|
44
|
+
learning: section("learning", { adaptiveThresholds: true, patternAnalysis: true, minHoldsForAdaptive: 20, adaptationRate: 0.1, retentionDays: 365 }),
|
|
44
45
|
};
|
|
45
46
|
if (typeof config.slop.prose !== "object" || config.slop.prose === null) {
|
|
46
47
|
missing.push("slop.prose");
|
package/dist/trace.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface TraceEntry {
|
|
|
12
12
|
line: string;
|
|
13
13
|
/** Redacted details: what was inspected, what Jev answered, what the agent was told. */
|
|
14
14
|
details: string[];
|
|
15
|
+
/** Raw tokens for re-rendering in live mode with sentence templates. */
|
|
16
|
+
tokens?: Record<string, string | undefined> | undefined;
|
|
15
17
|
}
|
|
16
18
|
/** Session memory of guard decisions; the widget shows the latest line per guard, the panel shows the history. */
|
|
17
19
|
export declare class Trace {
|
package/dist/widget.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { RulesVerdict } from "./rules.js";
|
|
|
5
5
|
import type { RunawayVerdict } from "./runaway.js";
|
|
6
6
|
import type { StuckVerdict } from "./stuck.js";
|
|
7
7
|
export type WidgetPlacement = "aboveEditor" | "belowEditor";
|
|
8
|
+
export type WidgetBarMode = "stack" | "live";
|
|
8
9
|
/** Palette shared by the status line and the trace sidebar: one place decides what a verdict looks like. */
|
|
9
10
|
export interface ThemeLike {
|
|
10
11
|
fg(color: string, text: string): string;
|
|
@@ -14,6 +15,7 @@ export declare const LEVEL_COLOR: Record<string, string>;
|
|
|
14
15
|
export interface WidgetConfig {
|
|
15
16
|
enabled: boolean;
|
|
16
17
|
placement: WidgetPlacement;
|
|
18
|
+
barMode: WidgetBarMode;
|
|
17
19
|
/** Keyboard shortcut that toggles the trace sidebar; empty string disables it. */
|
|
18
20
|
shortcut: string;
|
|
19
21
|
/** Sidebar width: a percentage string such as "40%" or a column count. */
|
|
@@ -40,6 +42,100 @@ export declare const DEFAULT_TEMPLATES: {
|
|
|
40
42
|
readonly rules: "warden · rules · {tool} {path} · {asked} rules · {violations} · {status}";
|
|
41
43
|
readonly subagent: "warden · subagent · {agent} · {kind} · {wake} · {status}";
|
|
42
44
|
};
|
|
45
|
+
/** Sentence-style templates: natural language, no " · " separators between semantic groups. */
|
|
46
|
+
export declare const SENTENCE_TEMPLATES: {
|
|
47
|
+
readonly action: {
|
|
48
|
+
readonly allow_default: "warden allowed {tool} action — {scope}";
|
|
49
|
+
readonly allow_safe: "warden allowed {tool} action — low risk";
|
|
50
|
+
readonly allow_read_only: "warden allowed {tool} action — read-only operation";
|
|
51
|
+
readonly allow_with_context: "warden allowed {tool} action — {scope}, irreversibility {irreversible}";
|
|
52
|
+
readonly allow_off_task: "warden allowed {tool} action — off-task but below threshold ({offTask})";
|
|
53
|
+
readonly allow_intent_mismatch: "warden allowed {tool} action — plan mismatch ({intent}) but not blocking";
|
|
54
|
+
readonly allow_slop: "warden allowed {tool} action — slop detected ({slop})";
|
|
55
|
+
readonly allow_patterns: "warden allowed {tool} action — patterns matched ({patterns})";
|
|
56
|
+
readonly allow_approved: "warden allowed {tool} action — approved by user";
|
|
57
|
+
readonly allow_typesafe_error: "warden allowed {tool} action — TypeSafe unavailable, pattern checks only";
|
|
58
|
+
readonly warn_off_task: "warden warned about {tool} action — off-task risk ({offTask})";
|
|
59
|
+
readonly warn_irreversible: "warden warned about {tool} action — high irreversibility ({irreversible})";
|
|
60
|
+
readonly warn_intent: "warden warned about {tool} action — differs from stated plan";
|
|
61
|
+
readonly warn_slop: "warden warned about {tool} action — code slop detected ({slop})";
|
|
62
|
+
readonly warn_patterns: "warden warned about {tool} action — security patterns matched ({patterns})";
|
|
63
|
+
readonly warn_visible: "warden warned about {tool} action — visible outside working tree";
|
|
64
|
+
readonly hold_irreversible: "warden held {tool} action — irreversibility {irreversible} exceeds threshold";
|
|
65
|
+
readonly hold_dangerous: "warden held {tool} action — destructive pattern detected ({patterns})";
|
|
66
|
+
readonly hold_deny: "warden blocked {tool} action — deny rule matched ({patterns})";
|
|
67
|
+
readonly default: "warden {level}ed {tool} action";
|
|
68
|
+
};
|
|
69
|
+
readonly stuck: {
|
|
70
|
+
readonly stuck_repeat: "warden detected stuck loop — {failures} failures, same strategy ({sameStrategy})";
|
|
71
|
+
readonly stuck_churn: "warden detected stuck loop — {failures} failures, churning approaches";
|
|
72
|
+
readonly stuck_no_progress: "warden detected stuck loop — {failures} failures, no progress ({progress})";
|
|
73
|
+
readonly stuck_repeat_successful: "warden detected repeated success — {failures} identical results, consider if the task is done";
|
|
74
|
+
readonly stuck_typesafe_error: "warden stuck check unavailable — TypeSafe error";
|
|
75
|
+
readonly stuck_recovering: "warden detected stuck loop — {failures} failures, now recovering";
|
|
76
|
+
readonly ok: "warden checked stuck — no loop detected ({failures} failures, approach change {approachChange})";
|
|
77
|
+
readonly default: "warden {status} — {failures} failures";
|
|
78
|
+
};
|
|
79
|
+
readonly done: {
|
|
80
|
+
readonly unverified: "warden flagged done claim — {checksPassed}/{checks} checks passed, {changes} changes, not verified";
|
|
81
|
+
readonly false_claim: "warden flagged false done claim — {checksPassed}/{checks} checks failed";
|
|
82
|
+
readonly ok: "warden confirmed done — {checksPassed}/{checks} checks passed, {changes} changes";
|
|
83
|
+
readonly ok_all_passed: "warden confirmed done — all {checks} checks passed";
|
|
84
|
+
readonly partial: "warden flagged done — {checksPassed}/{checks} checks passed, needs attention";
|
|
85
|
+
readonly no_checks: "warden flagged done claim — no verification checks run";
|
|
86
|
+
readonly typesafe_error: "warden done check unavailable — TypeSafe error";
|
|
87
|
+
readonly unverified_nudged: "warden flagged done claim — agent asked to verify ({checksPassed}/{checks} passed)";
|
|
88
|
+
readonly false_claim_nudged: "warden flagged false claim — agent asked to recheck";
|
|
89
|
+
readonly default: "warden done check — {outcome}";
|
|
90
|
+
};
|
|
91
|
+
readonly prose: {
|
|
92
|
+
readonly nudged: "warden nudged prose — {status} in recent replies";
|
|
93
|
+
readonly wordy: "warden flagged prose — wordy ({wordy})";
|
|
94
|
+
readonly cliches: "warden flagged prose — clichés detected ({cliches})";
|
|
95
|
+
readonly jargon: "warden flagged prose — jargon detected ({jargon})";
|
|
96
|
+
readonly multiple: "warden flagged prose — {status}";
|
|
97
|
+
readonly ok: "warden checked prose — all clear";
|
|
98
|
+
readonly typesafe_error: "warden prose check unavailable — TypeSafe error";
|
|
99
|
+
readonly default: "warden prose — {status}";
|
|
100
|
+
};
|
|
101
|
+
readonly security: {
|
|
102
|
+
readonly injection: "warden flagged security — injection risk in {tool} output";
|
|
103
|
+
readonly exfiltration: "warden flagged security — possible credentials in {tool} output";
|
|
104
|
+
readonly both: "warden flagged security — injection and exfiltration risk in {tool} output";
|
|
105
|
+
readonly suspicious: "warden flagged security — suspicious content in {tool} output";
|
|
106
|
+
readonly ok: "warden checked {tool} output — no security issues";
|
|
107
|
+
readonly default: "warden security — {tool}";
|
|
108
|
+
};
|
|
109
|
+
readonly context: {
|
|
110
|
+
readonly compressed: "warden compressed {tool} output — kept {retention}, saved {bytesSaved} bytes";
|
|
111
|
+
readonly recalled: "warden recalled full {tool} output — agent needed the original";
|
|
112
|
+
readonly kept: "warden kept {tool} output — {retention}";
|
|
113
|
+
readonly large: "warden compressed {tool} output — {bytesSaved} bytes saved";
|
|
114
|
+
readonly default: "warden context — {tool}, {retention}";
|
|
115
|
+
};
|
|
116
|
+
readonly runaway: {
|
|
117
|
+
readonly stopped: "warden stopped runaway {kind} — repeated {count}× ({chars} chars)";
|
|
118
|
+
readonly stopped_recovering: "warden stopped runaway {kind} — repeated {count}×, agent recovering";
|
|
119
|
+
readonly detected: "warden detected runaway {kind} — {count}× repetition";
|
|
120
|
+
readonly default: "warden runaway — {kind}";
|
|
121
|
+
};
|
|
122
|
+
readonly rules: {
|
|
123
|
+
readonly violations: "warden flagged rules — {violations} in {tool} {path}";
|
|
124
|
+
readonly violation_single: "warden flagged rule — {violations}";
|
|
125
|
+
readonly none: "warden checked rules — no violations in {tool} {path}";
|
|
126
|
+
readonly skipped: "warden rules — skipped ({reasons})";
|
|
127
|
+
readonly typesafe_error: "warden rules check unavailable — TypeSafe error";
|
|
128
|
+
readonly default: "warden rules — {tool}";
|
|
129
|
+
};
|
|
130
|
+
readonly subagent: {
|
|
131
|
+
readonly wake: "warden woke for subagent report — {agent}, {kind}";
|
|
132
|
+
readonly silent: "warden ignored subagent report — {agent}, {kind}";
|
|
133
|
+
readonly appended: "warden appended subagent report — {agent}";
|
|
134
|
+
readonly default: "warden subagent — {agent}";
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
/** Pick the best sentence template for a verdict. Falls back to the data-style template. */
|
|
138
|
+
export declare function pickSentenceTemplate(guard: string, tokens: Tokens): string;
|
|
43
139
|
export declare function defaultWidgetConfig(): WidgetConfig;
|
|
44
140
|
export type Tokens = Record<string, string | undefined>;
|
|
45
141
|
/**
|