pi-warden 0.40.2 → 0.48.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.
- package/CHANGELOG.md +96 -0
- package/README.md +23 -3
- package/dist/action-guard.d.ts +11 -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 +17 -4
- package/dist/extension.js +311 -80
- package/dist/extension.js.bak +263 -76
- package/dist/guard.d.ts +115 -1
- package/dist/guard.js +404 -11
- 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,13 @@ 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;
|
|
202
|
+
/**
|
|
203
|
+
* Real paths of host directories (`hostPaths()`). A write or edit in one is not held by the outside-project rule;
|
|
204
|
+
* every other check still applies.
|
|
205
|
+
*/
|
|
206
|
+
hostPaths?: readonly string[] | undefined;
|
|
190
207
|
}
|
|
191
208
|
export declare const higher: (a: Level, b: Level) => Level;
|
|
192
209
|
interface Rule {
|
|
@@ -196,6 +213,16 @@ interface Rule {
|
|
|
196
213
|
test: RegExp;
|
|
197
214
|
}
|
|
198
215
|
export declare const SHELL_RULES: Rule[];
|
|
216
|
+
/**
|
|
217
|
+
* The command family the large-output steer is remembered by: the head of the first segment that is not a `cd`, plus the
|
|
218
|
+
* subcommand for tools that have one (`git log`, `npm test`, `npm run build`). Undefined when no head can be read.
|
|
219
|
+
*/
|
|
220
|
+
export declare function commandFamily(command: string): string | undefined;
|
|
221
|
+
/**
|
|
222
|
+
* Whether a shell command has a segment whose effect is visible outside the working tree, decided in code before any
|
|
223
|
+
* request. Quoted data such as a commit message is blanked first, so a message that mentions `git push` is not a push.
|
|
224
|
+
*/
|
|
225
|
+
export declare function isVisibleCommand(command: string): boolean;
|
|
199
226
|
export interface ScannedCommand {
|
|
200
227
|
/** The command with data text blanked; what the pattern rules read. */
|
|
201
228
|
text: string;
|
|
@@ -207,12 +234,82 @@ export interface ScannedCommand {
|
|
|
207
234
|
* (`python3 - <<EOF`) are kept when the script calls out to a shell or process API.
|
|
208
235
|
*/
|
|
209
236
|
export declare function stripDataText(command: string): ScannedCommand;
|
|
237
|
+
/**
|
|
238
|
+
* Whether the exemption applies on this platform. It rests on birth time being a real creation time: macOS and Windows
|
|
239
|
+
* report one; Linux without `statx` reports ctime, which `mv` updates, so moved-in content would pass the tree walk.
|
|
240
|
+
* Elsewhere nothing is recorded and a recursive rm is classified as if no scratch existed.
|
|
241
|
+
*/
|
|
242
|
+
export declare function scratchPlatform(platform?: NodeJS.Platform): boolean;
|
|
243
|
+
/** What a recorded path was when it was created. A path whose current identity differs was replaced and is not scratch. */
|
|
244
|
+
export interface ScratchIdentity {
|
|
245
|
+
dev: number;
|
|
246
|
+
ino: number;
|
|
247
|
+
birthtimeMs: number;
|
|
248
|
+
}
|
|
249
|
+
/** Real paths the agent created under the temp directory in this session, with the identity each had when recorded. */
|
|
250
|
+
export type ScratchRecords = ReadonlyMap<string, ScratchIdentity>;
|
|
251
|
+
/** The identity of a path itself (a symlink is not followed); undefined when it does not exist. */
|
|
252
|
+
export declare function scratchIdentity(path: string): ScratchIdentity | undefined;
|
|
253
|
+
/** Drops records whose path is gone or now names a different file, so a later file at the same path is not scratch. */
|
|
254
|
+
export declare function pruneScratch(records: Map<string, ScratchIdentity>): void;
|
|
255
|
+
/** Real paths of the temp roots that exist: `os.tmpdir()`, `$TMPDIR`, `/tmp`, `/private/tmp`. */
|
|
256
|
+
export declare function tempRoots(): string[];
|
|
257
|
+
/**
|
|
258
|
+
* The real path of an absolute path: the realpath of its deepest existing ancestor plus the missing rest. Undefined when
|
|
259
|
+
* a component exists but cannot be resolved (a dangling symlink, a loop, no permission).
|
|
260
|
+
*/
|
|
261
|
+
export declare function realTarget(path: string): string | undefined;
|
|
262
|
+
/**
|
|
263
|
+
* Host paths: directories outside the project where the host lets its agent write, from `PI_WARDEN_HOST_PATHS` only (a
|
|
264
|
+
* `:`-separated list). Relative entries, empty entries, and a filesystem root are ignored. Each entry is kept as its real
|
|
265
|
+
* path, so a target is compared after `..` and symlinks are resolved on both sides.
|
|
266
|
+
*/
|
|
267
|
+
export declare const HOST_PATHS_ENV = "PI_WARDEN_HOST_PATHS";
|
|
268
|
+
export declare function hostPaths(env?: NodeJS.ProcessEnv): string[];
|
|
269
|
+
/** The temp root a real path lies strictly under; a temp root itself has none. */
|
|
270
|
+
export declare function tempRootOf(real: string, roots?: string[]): string | undefined;
|
|
271
|
+
/** How much of the target trees one rm segment may walk: entries seen, and the clock time it must finish by. */
|
|
272
|
+
export interface ScratchBudget {
|
|
273
|
+
entries: number;
|
|
274
|
+
deadline: number;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* True when every entry of the tree at `path`, the root included, has a birth time at or after `born`. Symlinks are
|
|
278
|
+
* checked as links and never followed. False when the root is missing, an entry has no birth time or cannot be read,
|
|
279
|
+
* or the walk runs past its budget: content moved in from elsewhere keeps its older birth time and must not pass.
|
|
280
|
+
*/
|
|
281
|
+
export declare function bornAfter(path: string, born: number, budget?: ScratchBudget): boolean;
|
|
282
|
+
/**
|
|
283
|
+
* Real paths under a temp root that this call may create and that do not exist yet: `mkdir` targets of a shell command,
|
|
284
|
+
* or a written file. Each path and its missing ancestors below the temp root are listed. Taken before the call runs, so
|
|
285
|
+
* a directory that already existed is never recorded as created.
|
|
286
|
+
*/
|
|
287
|
+
export declare function scratchCandidates(tool: string, input: Record<string, unknown>, cwd: string, platform?: NodeJS.Platform): string[];
|
|
288
|
+
/**
|
|
289
|
+
* Real paths the call created, read after it ran, each with its identity. A candidate from `scratchCandidates` counts
|
|
290
|
+
* when it now exists as itself (not through a symlink). For a shell command, absolute temp paths printed in its output
|
|
291
|
+
* also count, bounded: the first 20 such paths only, each must exist under a temp root as a real directory or file, and
|
|
292
|
+
* its birth time must fall in a later millisecond than `started` (`Date.now()` when the call began). Where the file
|
|
293
|
+
* system reports no birth time, a printed path counts only from a command that does nothing but run `mktemp` and print
|
|
294
|
+
* what it made (`mktempOnly`). `birthtime` reads a file's birth time in milliseconds, 0 when the file system has none.
|
|
295
|
+
* Nothing is recorded where `scratchPlatform` is false.
|
|
296
|
+
*/
|
|
297
|
+
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>;
|
|
298
|
+
/**
|
|
299
|
+
* How many paths a command that only makes temp files may print: each segment is a bare `mktemp`, an assignment
|
|
300
|
+
* `name=$(mktemp …)`, or an `echo` of names assigned that way. 0 for any other command.
|
|
301
|
+
*/
|
|
302
|
+
export declare function mktempOnly(command: string): number;
|
|
210
303
|
/** Compiled user rules and exempt ids; passed from the config so matchPatterns stays pure. */
|
|
211
304
|
export interface PatternOptions {
|
|
212
305
|
commandRules?: readonly CommandRule[];
|
|
213
306
|
commandDenyRules?: readonly CommandRule[];
|
|
214
307
|
exemptRules?: readonly string[];
|
|
215
308
|
pathRules?: readonly PathRule[];
|
|
309
|
+
/** Real paths the agent created under the temp directory in this session; a recursive rm of only these is not destructive. */
|
|
310
|
+
scratch?: ScratchRecords | undefined;
|
|
311
|
+
/** The platform `scratchPlatform` decides for; the running one when omitted. */
|
|
312
|
+
platform?: NodeJS.Platform | undefined;
|
|
216
313
|
}
|
|
217
314
|
/** Every id exemptRules can legitimately name: the built-in shell rules, the rm-classifier's derived ids, and the
|
|
218
315
|
* sensitive-path id. Unknown ids (a typo, or a rule that never existed) are inert; this list lets the surface be
|
|
@@ -262,6 +359,10 @@ export declare const questions: {
|
|
|
262
359
|
export declare const visibleQuestion: {
|
|
263
360
|
visible: import("pi-typesafe").NoulQuestion;
|
|
264
361
|
};
|
|
362
|
+
/** Bash only. Prevention before the call; the context saver still compresses whatever does print. Steers, never holds. */
|
|
363
|
+
export declare const largeOutputQuestion: {
|
|
364
|
+
large_output: import("pi-typesafe").NoulQuestion;
|
|
365
|
+
};
|
|
265
366
|
/** Asked only when the agent said something before the call; an empty plan cannot be contradicted. */
|
|
266
367
|
export declare const intentQuestion: {
|
|
267
368
|
intent_mismatch: import("pi-typesafe").NoulQuestion;
|
|
@@ -320,6 +421,8 @@ export declare function buildRequest(summary: ActionSummary, task: string | unde
|
|
|
320
421
|
rulesSource?: string | undefined;
|
|
321
422
|
violations?: readonly Violation[] | undefined;
|
|
322
423
|
floorHits?: string;
|
|
424
|
+
spine?: TaskSpine | undefined;
|
|
425
|
+
largeOutput?: boolean;
|
|
323
426
|
}): {
|
|
324
427
|
state: {
|
|
325
428
|
floor_hits?: string;
|
|
@@ -332,6 +435,10 @@ export declare function buildRequest(summary: ActionSummary, task: string | unde
|
|
|
332
435
|
path?: string;
|
|
333
436
|
}[];
|
|
334
437
|
plan?: string;
|
|
438
|
+
spine?: {
|
|
439
|
+
goal: string;
|
|
440
|
+
task_history: string[];
|
|
441
|
+
};
|
|
335
442
|
task: string;
|
|
336
443
|
action: Record<string, string | number | boolean>;
|
|
337
444
|
context: {
|
|
@@ -348,6 +455,7 @@ export declare function buildRequest(summary: ActionSummary, task: string | unde
|
|
|
348
455
|
slop_comments?: import("pi-typesafe").NoulQuestion;
|
|
349
456
|
slop_dead?: import("pi-typesafe").NoulQuestion;
|
|
350
457
|
slop_hedging?: import("pi-typesafe").NoulQuestion;
|
|
458
|
+
large_output?: import("pi-typesafe").NoulQuestion;
|
|
351
459
|
intent_mismatch?: import("pi-typesafe").NoulQuestion;
|
|
352
460
|
visible?: import("pi-typesafe").NoulQuestion;
|
|
353
461
|
irreversible: import("pi-typesafe").NoulQuestion;
|
|
@@ -371,6 +479,7 @@ export declare function buildRequest(summary: ActionSummary, task: string | unde
|
|
|
371
479
|
slop_comments?: import("pi-typesafe").NoulQuestion;
|
|
372
480
|
slop_dead?: import("pi-typesafe").NoulQuestion;
|
|
373
481
|
slop_hedging?: import("pi-typesafe").NoulQuestion;
|
|
482
|
+
large_output?: import("pi-typesafe").NoulQuestion;
|
|
374
483
|
intent_mismatch?: import("pi-typesafe").NoulQuestion;
|
|
375
484
|
visible?: import("pi-typesafe").NoulQuestion;
|
|
376
485
|
irreversible: import("pi-typesafe").NoulQuestion;
|
|
@@ -390,6 +499,7 @@ export declare function buildRequest(summary: ActionSummary, task: string | unde
|
|
|
390
499
|
slop_comments?: import("pi-typesafe").NoulQuestion;
|
|
391
500
|
slop_dead?: import("pi-typesafe").NoulQuestion;
|
|
392
501
|
slop_hedging?: import("pi-typesafe").NoulQuestion;
|
|
502
|
+
large_output?: import("pi-typesafe").NoulQuestion;
|
|
393
503
|
intent_mismatch?: import("pi-typesafe").NoulQuestion;
|
|
394
504
|
visible?: import("pi-typesafe").NoulQuestion;
|
|
395
505
|
irreversible: import("pi-typesafe").NoulQuestion;
|
|
@@ -414,6 +524,10 @@ export declare function intentSteer(verdict: Verdict): string;
|
|
|
414
524
|
export declare function shouldProceedMessage(verdict: Verdict): string;
|
|
415
525
|
/** What the agent reads after an unrelated change ran: the request it drifted from, the two acceptable moves, one line. */
|
|
416
526
|
export declare function offTaskSteer(verdict: Verdict): string;
|
|
527
|
+
/** What the agent reads the first time a command family is judged noisy: the family and one concrete way to print less. */
|
|
528
|
+
export declare function largeOutputSteer(verdict: Verdict): string;
|
|
529
|
+
/** The large-output steer for this verdict, once per command family per session; `steered` is the session's memory. */
|
|
530
|
+
export declare function largeOutputNotice(verdict: Verdict, steered: Set<string>): string | undefined;
|
|
417
531
|
/** Offline stand-in for the approval question when TypeSafe is not available. */
|
|
418
532
|
export declare function textApproves(task: string | undefined): boolean;
|
|
419
533
|
/**
|