pi-warden 0.40.2 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/dist/action-guard.d.ts +9 -2
- package/dist/action-guard.js +1 -1
- package/dist/backend.d.ts +4 -0
- package/dist/backend.js +4 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +15 -1
- package/dist/conscience.d.ts +9 -3
- package/dist/conscience.js +13 -4
- package/dist/extension.d.ts +8 -1
- package/dist/extension.js +238 -67
- package/dist/guard.d.ts +98 -1
- package/dist/guard.js +345 -10
- package/dist/holds.d.ts +2 -0
- package/dist/holds.js +5 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/judge-cooldown.d.ts +44 -0
- package/dist/judge-cooldown.js +92 -0
- package/dist/panel.d.ts +5 -2
- package/dist/panel.js +5 -2
- package/dist/redact.d.ts +5 -3
- package/dist/redact.js +17 -4
- package/dist/rules.d.ts +2 -0
- package/dist/rules.js +23 -0
- package/dist/shape.d.ts +46 -0
- package/dist/shape.js +76 -1
- package/dist/trace-file.d.ts +50 -0
- package/dist/trace-file.js +141 -0
- package/dist/trace.d.ts +12 -1
- package/dist/trace.js +3 -3
- package/package.json +1 -1
package/dist/guard.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { Stats } from "node:fs";
|
|
1
2
|
import type { IntegrationErrorCode, Judge, Questions } from "pi-typesafe";
|
|
2
|
-
import type { ActionGuardConfig, ArmingRule, CommandRule, PathRule, RulesConfig, SecurityConfig, SlopGuardConfig } from "./config.js";
|
|
3
|
+
import type { ActionGuardConfig, ArmingRule, CommandRule, LargeOutputConfig, PathRule, RulesConfig, SecurityConfig, SlopGuardConfig } from "./config.js";
|
|
4
|
+
import type { TaskSpine } from "./shape.js";
|
|
3
5
|
import type { RulesSourceConfig } from "./rules.js";
|
|
4
6
|
export type Level = "allow" | "warn" | "confirm" | "deny";
|
|
5
7
|
export type Severity = "destructive" | "risky" | "sensitive" | "deny";
|
|
@@ -63,6 +65,8 @@ export interface ActionInput {
|
|
|
63
65
|
task?: string | undefined;
|
|
64
66
|
/** Prior conversation clarifies scope, but never grants approval for a held action. */
|
|
65
67
|
context?: readonly TaskMessage[] | undefined;
|
|
68
|
+
/** The task spine: the thread's goal and earlier user turns, so follow-ups are judged with the goal they belong to. Scope context only; it never authorizes. */
|
|
69
|
+
spine?: TaskSpine | undefined;
|
|
66
70
|
/** The agent's own words in the message that makes this call (or its latest text under this prompt). Explains the step; never authorizes it. */
|
|
67
71
|
plan?: string | undefined;
|
|
68
72
|
}
|
|
@@ -109,6 +113,8 @@ export interface Judgment {
|
|
|
109
113
|
visible?: number;
|
|
110
114
|
/** P(action is safe to proceed without asking). Inverted: low = hold. */
|
|
111
115
|
shouldProceed?: number;
|
|
116
|
+
/** P(the command prints far more than the agent needs); bash only, never holds. */
|
|
117
|
+
largeOutput?: number;
|
|
112
118
|
model: string;
|
|
113
119
|
elapsedMs: number;
|
|
114
120
|
}
|
|
@@ -156,6 +162,8 @@ export interface Verdict {
|
|
|
156
162
|
offTaskTraceOnly?: boolean;
|
|
157
163
|
/** Index of the trace-only off-task diagnostic; later reasons append, and any prepend must adjust this index. */
|
|
158
164
|
offTaskTraceOnlyReasonIndex?: number;
|
|
165
|
+
/** Command family of a bash call at or above the large-output threshold; the agent is steered once per family per session. */
|
|
166
|
+
largeOutputFamily?: string;
|
|
159
167
|
/** Answers to the caller's own `questions`: P(yes) for a noul, the picked option for a choice, the level for a score. */
|
|
160
168
|
extra?: Record<string, number | string>;
|
|
161
169
|
/** Safe TypeSafe error message when the judge could not answer. */
|
|
@@ -170,6 +178,8 @@ export interface EvaluateOptions {
|
|
|
170
178
|
signal?: AbortSignal | undefined;
|
|
171
179
|
/** Adds quality questions for write/edit content to the same request. */
|
|
172
180
|
slop?: SlopGuardConfig | undefined;
|
|
181
|
+
/** Adds the large-output question to a bash request. */
|
|
182
|
+
largeOutput?: LargeOutputConfig | undefined;
|
|
173
183
|
security?: SecurityConfig | undefined;
|
|
174
184
|
/**
|
|
175
185
|
* The rules guard's switch. The rules the active config resolves ride every judged action request, so
|
|
@@ -187,6 +197,8 @@ export interface EvaluateOptions {
|
|
|
187
197
|
* How a candidate question is measured on recorded sessions before it earns an acting rule (scripts/calibrate-action.mjs).
|
|
188
198
|
*/
|
|
189
199
|
questions?: Questions | undefined;
|
|
200
|
+
/** Real paths the agent created under the temp directory in this session (`PatternOptions.scratch`). */
|
|
201
|
+
scratch?: ScratchRecords | undefined;
|
|
190
202
|
}
|
|
191
203
|
export declare const higher: (a: Level, b: Level) => Level;
|
|
192
204
|
interface Rule {
|
|
@@ -196,6 +208,11 @@ interface Rule {
|
|
|
196
208
|
test: RegExp;
|
|
197
209
|
}
|
|
198
210
|
export declare const SHELL_RULES: Rule[];
|
|
211
|
+
/**
|
|
212
|
+
* The command family the large-output steer is remembered by: the head of the first segment that is not a `cd`, plus the
|
|
213
|
+
* subcommand for tools that have one (`git log`, `npm test`, `npm run build`). Undefined when no head can be read.
|
|
214
|
+
*/
|
|
215
|
+
export declare function commandFamily(command: string): string | undefined;
|
|
199
216
|
export interface ScannedCommand {
|
|
200
217
|
/** The command with data text blanked; what the pattern rules read. */
|
|
201
218
|
text: string;
|
|
@@ -207,12 +224,75 @@ export interface ScannedCommand {
|
|
|
207
224
|
* (`python3 - <<EOF`) are kept when the script calls out to a shell or process API.
|
|
208
225
|
*/
|
|
209
226
|
export declare function stripDataText(command: string): ScannedCommand;
|
|
227
|
+
/**
|
|
228
|
+
* Whether the exemption applies on this platform. It rests on birth time being a real creation time: macOS and Windows
|
|
229
|
+
* report one; Linux without `statx` reports ctime, which `mv` updates, so moved-in content would pass the tree walk.
|
|
230
|
+
* Elsewhere nothing is recorded and a recursive rm is classified as if no scratch existed.
|
|
231
|
+
*/
|
|
232
|
+
export declare function scratchPlatform(platform?: NodeJS.Platform): boolean;
|
|
233
|
+
/** What a recorded path was when it was created. A path whose current identity differs was replaced and is not scratch. */
|
|
234
|
+
export interface ScratchIdentity {
|
|
235
|
+
dev: number;
|
|
236
|
+
ino: number;
|
|
237
|
+
birthtimeMs: number;
|
|
238
|
+
}
|
|
239
|
+
/** Real paths the agent created under the temp directory in this session, with the identity each had when recorded. */
|
|
240
|
+
export type ScratchRecords = ReadonlyMap<string, ScratchIdentity>;
|
|
241
|
+
/** The identity of a path itself (a symlink is not followed); undefined when it does not exist. */
|
|
242
|
+
export declare function scratchIdentity(path: string): ScratchIdentity | undefined;
|
|
243
|
+
/** Drops records whose path is gone or now names a different file, so a later file at the same path is not scratch. */
|
|
244
|
+
export declare function pruneScratch(records: Map<string, ScratchIdentity>): void;
|
|
245
|
+
/** Real paths of the temp roots that exist: `os.tmpdir()`, `$TMPDIR`, `/tmp`, `/private/tmp`. */
|
|
246
|
+
export declare function tempRoots(): string[];
|
|
247
|
+
/**
|
|
248
|
+
* The real path of an absolute path: the realpath of its deepest existing ancestor plus the missing rest. Undefined when
|
|
249
|
+
* a component exists but cannot be resolved (a dangling symlink, a loop, no permission).
|
|
250
|
+
*/
|
|
251
|
+
export declare function realTarget(path: string): string | undefined;
|
|
252
|
+
/** The temp root a real path lies strictly under; a temp root itself has none. */
|
|
253
|
+
export declare function tempRootOf(real: string, roots?: string[]): string | undefined;
|
|
254
|
+
/** How much of the target trees one rm segment may walk: entries seen, and the clock time it must finish by. */
|
|
255
|
+
export interface ScratchBudget {
|
|
256
|
+
entries: number;
|
|
257
|
+
deadline: number;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* True when every entry of the tree at `path`, the root included, has a birth time at or after `born`. Symlinks are
|
|
261
|
+
* checked as links and never followed. False when the root is missing, an entry has no birth time or cannot be read,
|
|
262
|
+
* or the walk runs past its budget: content moved in from elsewhere keeps its older birth time and must not pass.
|
|
263
|
+
*/
|
|
264
|
+
export declare function bornAfter(path: string, born: number, budget?: ScratchBudget): boolean;
|
|
265
|
+
/**
|
|
266
|
+
* Real paths under a temp root that this call may create and that do not exist yet: `mkdir` targets of a shell command,
|
|
267
|
+
* or a written file. Each path and its missing ancestors below the temp root are listed. Taken before the call runs, so
|
|
268
|
+
* a directory that already existed is never recorded as created.
|
|
269
|
+
*/
|
|
270
|
+
export declare function scratchCandidates(tool: string, input: Record<string, unknown>, cwd: string, platform?: NodeJS.Platform): string[];
|
|
271
|
+
/**
|
|
272
|
+
* Real paths the call created, read after it ran, each with its identity. A candidate from `scratchCandidates` counts
|
|
273
|
+
* when it now exists as itself (not through a symlink). For a shell command, absolute temp paths printed in its output
|
|
274
|
+
* also count, bounded: the first 20 such paths only, each must exist under a temp root as a real directory or file, and
|
|
275
|
+
* its birth time must fall in a later millisecond than `started` (`Date.now()` when the call began). Where the file
|
|
276
|
+
* system reports no birth time, a printed path counts only from a command that does nothing but run `mktemp` and print
|
|
277
|
+
* what it made (`mktempOnly`). `birthtime` reads a file's birth time in milliseconds, 0 when the file system has none.
|
|
278
|
+
* Nothing is recorded where `scratchPlatform` is false.
|
|
279
|
+
*/
|
|
280
|
+
export declare function createdScratch(tool: string, input: Record<string, unknown>, output: string, started: number, candidates: readonly string[], birthtime?: (stats: Stats) => number, platform?: NodeJS.Platform): Map<string, ScratchIdentity>;
|
|
281
|
+
/**
|
|
282
|
+
* How many paths a command that only makes temp files may print: each segment is a bare `mktemp`, an assignment
|
|
283
|
+
* `name=$(mktemp …)`, or an `echo` of names assigned that way. 0 for any other command.
|
|
284
|
+
*/
|
|
285
|
+
export declare function mktempOnly(command: string): number;
|
|
210
286
|
/** Compiled user rules and exempt ids; passed from the config so matchPatterns stays pure. */
|
|
211
287
|
export interface PatternOptions {
|
|
212
288
|
commandRules?: readonly CommandRule[];
|
|
213
289
|
commandDenyRules?: readonly CommandRule[];
|
|
214
290
|
exemptRules?: readonly string[];
|
|
215
291
|
pathRules?: readonly PathRule[];
|
|
292
|
+
/** Real paths the agent created under the temp directory in this session; a recursive rm of only these is not destructive. */
|
|
293
|
+
scratch?: ScratchRecords | undefined;
|
|
294
|
+
/** The platform `scratchPlatform` decides for; the running one when omitted. */
|
|
295
|
+
platform?: NodeJS.Platform | undefined;
|
|
216
296
|
}
|
|
217
297
|
/** Every id exemptRules can legitimately name: the built-in shell rules, the rm-classifier's derived ids, and the
|
|
218
298
|
* sensitive-path id. Unknown ids (a typo, or a rule that never existed) are inert; this list lets the surface be
|
|
@@ -262,6 +342,10 @@ export declare const questions: {
|
|
|
262
342
|
export declare const visibleQuestion: {
|
|
263
343
|
visible: import("pi-typesafe").NoulQuestion;
|
|
264
344
|
};
|
|
345
|
+
/** Bash only. Prevention before the call; the context saver still compresses whatever does print. Steers, never holds. */
|
|
346
|
+
export declare const largeOutputQuestion: {
|
|
347
|
+
large_output: import("pi-typesafe").NoulQuestion;
|
|
348
|
+
};
|
|
265
349
|
/** Asked only when the agent said something before the call; an empty plan cannot be contradicted. */
|
|
266
350
|
export declare const intentQuestion: {
|
|
267
351
|
intent_mismatch: import("pi-typesafe").NoulQuestion;
|
|
@@ -320,6 +404,8 @@ export declare function buildRequest(summary: ActionSummary, task: string | unde
|
|
|
320
404
|
rulesSource?: string | undefined;
|
|
321
405
|
violations?: readonly Violation[] | undefined;
|
|
322
406
|
floorHits?: string;
|
|
407
|
+
spine?: TaskSpine | undefined;
|
|
408
|
+
largeOutput?: boolean;
|
|
323
409
|
}): {
|
|
324
410
|
state: {
|
|
325
411
|
floor_hits?: string;
|
|
@@ -332,6 +418,10 @@ export declare function buildRequest(summary: ActionSummary, task: string | unde
|
|
|
332
418
|
path?: string;
|
|
333
419
|
}[];
|
|
334
420
|
plan?: string;
|
|
421
|
+
spine?: {
|
|
422
|
+
goal: string;
|
|
423
|
+
task_history: string[];
|
|
424
|
+
};
|
|
335
425
|
task: string;
|
|
336
426
|
action: Record<string, string | number | boolean>;
|
|
337
427
|
context: {
|
|
@@ -348,6 +438,7 @@ export declare function buildRequest(summary: ActionSummary, task: string | unde
|
|
|
348
438
|
slop_comments?: import("pi-typesafe").NoulQuestion;
|
|
349
439
|
slop_dead?: import("pi-typesafe").NoulQuestion;
|
|
350
440
|
slop_hedging?: import("pi-typesafe").NoulQuestion;
|
|
441
|
+
large_output?: import("pi-typesafe").NoulQuestion;
|
|
351
442
|
intent_mismatch?: import("pi-typesafe").NoulQuestion;
|
|
352
443
|
visible?: import("pi-typesafe").NoulQuestion;
|
|
353
444
|
irreversible: import("pi-typesafe").NoulQuestion;
|
|
@@ -371,6 +462,7 @@ export declare function buildRequest(summary: ActionSummary, task: string | unde
|
|
|
371
462
|
slop_comments?: import("pi-typesafe").NoulQuestion;
|
|
372
463
|
slop_dead?: import("pi-typesafe").NoulQuestion;
|
|
373
464
|
slop_hedging?: import("pi-typesafe").NoulQuestion;
|
|
465
|
+
large_output?: import("pi-typesafe").NoulQuestion;
|
|
374
466
|
intent_mismatch?: import("pi-typesafe").NoulQuestion;
|
|
375
467
|
visible?: import("pi-typesafe").NoulQuestion;
|
|
376
468
|
irreversible: import("pi-typesafe").NoulQuestion;
|
|
@@ -390,6 +482,7 @@ export declare function buildRequest(summary: ActionSummary, task: string | unde
|
|
|
390
482
|
slop_comments?: import("pi-typesafe").NoulQuestion;
|
|
391
483
|
slop_dead?: import("pi-typesafe").NoulQuestion;
|
|
392
484
|
slop_hedging?: import("pi-typesafe").NoulQuestion;
|
|
485
|
+
large_output?: import("pi-typesafe").NoulQuestion;
|
|
393
486
|
intent_mismatch?: import("pi-typesafe").NoulQuestion;
|
|
394
487
|
visible?: import("pi-typesafe").NoulQuestion;
|
|
395
488
|
irreversible: import("pi-typesafe").NoulQuestion;
|
|
@@ -414,6 +507,10 @@ export declare function intentSteer(verdict: Verdict): string;
|
|
|
414
507
|
export declare function shouldProceedMessage(verdict: Verdict): string;
|
|
415
508
|
/** What the agent reads after an unrelated change ran: the request it drifted from, the two acceptable moves, one line. */
|
|
416
509
|
export declare function offTaskSteer(verdict: Verdict): string;
|
|
510
|
+
/** What the agent reads the first time a command family is judged noisy: the family and one concrete way to print less. */
|
|
511
|
+
export declare function largeOutputSteer(verdict: Verdict): string;
|
|
512
|
+
/** The large-output steer for this verdict, once per command family per session; `steered` is the session's memory. */
|
|
513
|
+
export declare function largeOutputNotice(verdict: Verdict, steered: Set<string>): string | undefined;
|
|
417
514
|
/** Offline stand-in for the approval question when TypeSafe is not available. */
|
|
418
515
|
export declare function textApproves(task: string | undefined): boolean;
|
|
419
516
|
/**
|