@osama.ansar/agent-loopguard 0.1.0 → 0.2.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/dist/index.d.ts CHANGED
@@ -42,24 +42,32 @@ interface GuardStep {
42
42
  /** When the step happened (epoch ms). Informational: recorded in history, not used by detectors. */
43
43
  timestamp?: number | undefined;
44
44
  }
45
- type StopReason = 'max-steps' | 'repeated-tool-call' | 'oscillation' | 'no-progress' | 'finish-reason' | 'budget' | 'custom';
45
+ type StopReason = 'max-steps' | 'tool-limit' | 'repeated-tool-call' | 'oscillation' | 'no-progress' | 'finish-reason' | 'budget' | 'custom';
46
46
  type BudgetMetric = 'totalTokens' | 'inputTokens' | 'outputTokens' | 'costUsd' | 'durationMs';
47
47
  /** Stable machine-readable codes. Safe to switch on; new codes are only added in minor versions. */
48
- type BuiltinStopCode = 'MAX_STEPS' | 'REPEATED_TOOL_CALL' | 'OSCILLATION' | 'NO_PROGRESS' | 'FINISH_TERMINAL' | 'FINISH_OTHER' | 'FINISH_OTHER_TEXT_ONLY' | 'BUDGET_TOTAL_TOKENS' | 'BUDGET_INPUT_TOKENS' | 'BUDGET_OUTPUT_TOKENS' | 'BUDGET_COST_USD' | 'BUDGET_DURATION_MS';
48
+ type BuiltinStopCode = 'MAX_STEPS' | 'TOOL_CALL_CAP' | 'REPEATED_TOOL_CALL' | 'OSCILLATION' | 'NO_PROGRESS' | 'FINISH_TERMINAL' | 'FINISH_OTHER' | 'FINISH_OTHER_TEXT_ONLY' | 'BUDGET_TOTAL_TOKENS' | 'BUDGET_INPUT_TOKENS' | 'BUDGET_OUTPUT_TOKENS' | 'BUDGET_COST_USD' | 'BUDGET_DURATION_MS';
49
49
  interface MaxStepsEvidence {
50
50
  steps: number;
51
51
  limit: number;
52
52
  }
53
+ interface ToolLimitEvidence {
54
+ tool: string;
55
+ /** Calls to the tool in this run, counting the one that went over. */
56
+ calls: number;
57
+ /** The tool's `maxCalls`. */
58
+ limit: number;
59
+ }
53
60
  interface RepeatedToolCallEvidence {
54
61
  tool: string;
55
62
  /** Canonical form of the arguments, truncated for display. */
56
63
  argsPreview: string;
57
64
  /** Length of the run of identical calls. */
58
65
  count: number;
66
+ /** The threshold that applied to this tool (the global one unless the tool's policy changes it). */
59
67
  threshold: number;
60
68
  /** Indexes (0-based) of the steps that contain the run; at most the last 10. */
61
69
  stepIndexes: number[];
62
- /** True when the rule was configured to require identical results too. */
70
+ /** True when identical results were required too (the global option, or a `polling` tool). */
63
71
  identicalResults: boolean;
64
72
  }
65
73
  interface OscillationEvidence {
@@ -70,6 +78,8 @@ interface OscillationEvidence {
70
78
  threshold: number;
71
79
  /** One cycle, as `tool(args)` labels. */
72
80
  cycle: string[];
81
+ /** The tool name of each call in the cycle (no arguments), in the same order as `cycle`. */
82
+ tools: string[];
73
83
  }
74
84
  interface NoProgressEvidence {
75
85
  /** Consecutive stalled steps with identical text. */
@@ -101,10 +111,18 @@ interface StopBase<R extends StopReason, C extends string, E> {
101
111
  detail: string;
102
112
  /** Structured, JSON-serialisable facts behind the decision. */
103
113
  evidence: E;
114
+ /**
115
+ * Plain-English text meant to be pasted into the model's next turn or tool result, present on
116
+ * every stop that is a fault (see `isFault`). It names the tool but never repeats arguments or
117
+ * results, so it is safe to send to the model. Absent on a normal `stop` finish and, for a
118
+ * `custom` stop, unless the detector (or `options.feedback`) supplies it. See `feedbackFor`.
119
+ */
120
+ feedback?: string;
104
121
  /** Non-fatal warnings raised in the same step, if any. */
105
122
  warnings?: GuardWarning[];
106
123
  }
107
124
  type MaxStepsVerdict = StopBase<'max-steps', 'MAX_STEPS', MaxStepsEvidence>;
125
+ type ToolLimitVerdict = StopBase<'tool-limit', 'TOOL_CALL_CAP', ToolLimitEvidence>;
108
126
  type RepeatedToolCallVerdict = StopBase<'repeated-tool-call', 'REPEATED_TOOL_CALL', RepeatedToolCallEvidence>;
109
127
  type OscillationVerdict = StopBase<'oscillation', 'OSCILLATION', OscillationEvidence>;
110
128
  type NoProgressVerdict = StopBase<'no-progress', 'NO_PROGRESS', NoProgressEvidence>;
@@ -112,7 +130,7 @@ type FinishReasonVerdict = StopBase<'finish-reason', 'FINISH_TERMINAL' | 'FINISH
112
130
  type BudgetVerdict = StopBase<'budget', 'BUDGET_TOTAL_TOKENS' | 'BUDGET_INPUT_TOKENS' | 'BUDGET_OUTPUT_TOKENS' | 'BUDGET_COST_USD' | 'BUDGET_DURATION_MS', BudgetEvidence>;
113
131
  /** Verdict produced by a user-supplied `custom` detector. */
114
132
  type CustomVerdict = StopBase<'custom', string, unknown>;
115
- type StopVerdict = MaxStepsVerdict | RepeatedToolCallVerdict | OscillationVerdict | NoProgressVerdict | FinishReasonVerdict | BudgetVerdict | CustomVerdict;
133
+ type StopVerdict = MaxStepsVerdict | ToolLimitVerdict | RepeatedToolCallVerdict | OscillationVerdict | NoProgressVerdict | FinishReasonVerdict | BudgetVerdict | CustomVerdict;
116
134
  interface GuardWarning {
117
135
  code: 'BUDGET_WARNING';
118
136
  metric: BudgetMetric;
@@ -182,6 +200,8 @@ interface CustomStop {
182
200
  detail?: string;
183
201
  /** Anything JSON-serialisable that explains the decision. */
184
202
  evidence?: unknown;
203
+ /** Text for the model, becomes `verdict.feedback`. Default: none. Must not contain secrets. */
204
+ feedback?: string;
185
205
  }
186
206
  /**
187
207
  * User-supplied detector. Receives the full history (including `step`, last) and may return a
@@ -191,6 +211,35 @@ interface CustomStop {
191
211
  type CustomDetector = (history: readonly GuardStep[], step: GuardStep) => CustomStop | {
192
212
  stop: false;
193
213
  } | undefined;
214
+ /**
215
+ * Rules for one tool, keyed by tool name in `GuardOptions.tools`. Every field is optional. See the
216
+ * README ("Per-tool policies") for how they combine with the global detectors.
217
+ */
218
+ interface ToolPolicy {
219
+ /**
220
+ * Identical arguments are legitimate for this tool as long as the results keep changing (a job
221
+ * status poll, a paginated read). The repeated-call rule then applies only when the results are
222
+ * identical too, after `maxIdenticalResults` identical outcomes in a row (default: the tool's
223
+ * `repeatThreshold`, else the global threshold). `true` is the same as `{}`.
224
+ */
225
+ polling?: boolean | {
226
+ maxIdenticalResults?: number;
227
+ };
228
+ /**
229
+ * At most this many calls to the tool in one run (integer >= 0; `0` forbids the tool). The call
230
+ * after the limit stops the run with `TOOL_CALL_CAP` (and `guard.preflight` denies it before it runs).
231
+ */
232
+ maxCalls?: number;
233
+ /** Stop after this many identical calls in a row to this tool. Integer >= 2. Overrides the global threshold. */
234
+ repeatThreshold?: number;
235
+ /**
236
+ * Leave the tool out of repeat, oscillation and no-progress detection and out of `maxCalls`
237
+ * (for bookkeeping tools such as `think` or `todo`). Takes precedence over the other fields.
238
+ */
239
+ ignore?: boolean;
240
+ }
241
+ /** Where a piece of feedback is going: after a step was recorded, or in place of a call that was not run. */
242
+ type FeedbackPhase = 'check' | 'preflight';
194
243
  interface GuardOptions {
195
244
  /** Stop after this many steps. Integer >= 1. Default 50. `false` disables. */
196
245
  maxSteps?: number | false;
@@ -204,8 +253,18 @@ interface GuardOptions {
204
253
  finishReason?: FinishReasonOptions | false;
205
254
  /** Default: no limits. */
206
255
  budget?: BudgetOptions | false;
256
+ /** Per-tool rules by tool name; tools that are not listed follow the global options. */
257
+ tools?: Readonly<Record<string, ToolPolicy>>;
207
258
  /** Extra detectors, run after the built-in ones. */
208
259
  custom?: CustomDetector | readonly CustomDetector[];
260
+ /**
261
+ * Replace the model-facing text of a fault verdict (and of a `preflight` denial). Return a string
262
+ * to use it, or `undefined` to keep the default (`feedbackFor`). Errors it throws propagate out of
263
+ * `guard.check` / `guard.preflight`. The text is yours to keep free of secrets.
264
+ */
265
+ feedback?: (verdict: StopVerdict, info: {
266
+ phase: FeedbackPhase;
267
+ }) => string | undefined;
209
268
  /** Clock used for wall-clock duration budgets. Default `Date.now`. */
210
269
  now?: () => number;
211
270
  }
@@ -229,6 +288,7 @@ interface GuardSummary {
229
288
  remaining: number;
230
289
  } | null;
231
290
  repeatedToolCall: {
291
+ /** The global threshold; tools with their own `repeatThreshold` or `polling` policy can differ. */
232
292
  threshold: number;
233
293
  currentRun: {
234
294
  tool: string;
@@ -255,18 +315,175 @@ interface GuardSummary {
255
315
  limits: Partial<Record<BudgetMetric, number>>;
256
316
  used: Record<BudgetMetric, number>;
257
317
  } | null;
318
+ /** Calls so far against each tool's `maxCalls`. Present only when at least one tool has a cap. */
319
+ toolLimits?: Array<{
320
+ tool: string;
321
+ limit: number;
322
+ calls: number;
323
+ }>;
258
324
  };
259
325
  /** The verdict returned by the most recent `check`, or `null` before the first one. */
260
326
  lastVerdict: Verdict | null;
261
327
  }
328
+ /** A tool call that has not run yet: all `preflight` needs is the name and the arguments. */
329
+ interface PreflightCall {
330
+ name: string;
331
+ args?: unknown;
332
+ }
333
+ /** The call may go ahead (as far as the rules `preflight` can evaluate are concerned). */
334
+ interface PreflightAllow {
335
+ allow: true;
336
+ /** Budget warnings that apply right now, if any. */
337
+ warnings?: GuardWarning[];
338
+ }
339
+ interface DenyBase<R extends StopReason, C extends string, E> {
340
+ allow: false;
341
+ /** Same vocabulary as `StopVerdict.reason`. */
342
+ reason: R;
343
+ /** Same vocabulary as `StopVerdict.code`. */
344
+ code: C;
345
+ /** One developer-facing sentence. May contain argument previews, like `StopVerdict.detail`. */
346
+ detail: string;
347
+ /** Structured, JSON-serialisable facts behind the decision. */
348
+ evidence: E;
349
+ /** Text to give the model in place of the tool's result. Never repeats arguments or results. */
350
+ feedback: string;
351
+ }
352
+ /**
353
+ * The call should not run. Only the rules that can be judged before a call runs appear here:
354
+ * `max-steps`, `budget`, `tool-limit`, `repeated-tool-call` and `oscillation`.
355
+ */
356
+ type PreflightDeny = DenyBase<'max-steps', 'MAX_STEPS', MaxStepsEvidence> | DenyBase<'budget', BudgetVerdict['code'], BudgetEvidence> | DenyBase<'tool-limit', 'TOOL_CALL_CAP', ToolLimitEvidence> | DenyBase<'repeated-tool-call', 'REPEATED_TOOL_CALL', RepeatedToolCallEvidence> | DenyBase<'oscillation', 'OSCILLATION', OscillationEvidence>;
357
+ type Preflight = PreflightAllow | PreflightDeny;
358
+ /** The stop verdicts a `preflight` denial is made from. */
359
+ type PreflightStopVerdict = MaxStepsVerdict | BudgetVerdict | ToolLimitVerdict | RepeatedToolCallVerdict | OscillationVerdict;
360
+ /** The current snapshot format. A guard only reads snapshots of the version it knows. */
361
+ type GuardSnapshotVersion = 1;
362
+ /**
363
+ * One short fingerprint per group of options, so a snapshot can tell whether it is being restored
364
+ * under the options it was taken with. The options themselves are not stored.
365
+ */
366
+ interface GuardOptionsFingerprint {
367
+ maxSteps: string;
368
+ repeatedToolCall: string;
369
+ oscillation: string;
370
+ noProgress: string;
371
+ finishReason: string;
372
+ budget: string;
373
+ tools: string;
374
+ /** Covers the number of custom detectors, not what they do. */
375
+ custom: string;
376
+ }
377
+ /** The run of identical calls that `repeatedToolCall` is currently counting. */
378
+ interface RepeatedToolCallSnapshot {
379
+ /** Hash identifying the repeated call (or call and result); `null` when there is no run. */
380
+ key: string | null;
381
+ tool: string;
382
+ /** Truncated canonical arguments (at most 80 characters), kept so evidence reads the same after a restore. */
383
+ preview: string;
384
+ length: number;
385
+ /** Indexes of the newest steps in the run (at most 10). */
386
+ steps: number[];
387
+ longest: number;
388
+ }
389
+ /** The newest calls the oscillation rule compares against, as hashes plus short labels. */
390
+ interface OscillationSnapshot {
391
+ sigs: string[];
392
+ /** `tool(argument preview)` for each call in `sigs`. */
393
+ labels: string[];
394
+ /** Tool name of each call in `sigs`. */
395
+ names: string[];
396
+ matches: number[];
397
+ current: {
398
+ period: number;
399
+ repeats: number;
400
+ } | null;
401
+ }
402
+ interface NoProgressSnapshot {
403
+ /** Hashes of the most recent distinct tool outcomes, oldest first. */
404
+ seen: string[];
405
+ streak: number;
406
+ /** Hash of the text the current streak shares; `null` when there is no streak. */
407
+ textKey: string | null;
408
+ /** Truncated text of the streak (at most 80 characters). */
409
+ textPreview: string;
410
+ }
411
+ /**
412
+ * Everything a guard needs to carry on a run in another process, as plain JSON-safe data.
413
+ *
414
+ * Contains: counters, running totals, elapsed wall-clock time (only when a duration budget exists),
415
+ * hashes of recent tool calls and outcomes, tool names, and a few truncated previews (at most 80
416
+ * characters each) of recent arguments and text, so that evidence and messages read the same after a
417
+ * restore. Does not contain: raw arguments, results, errors, model text, the step history, the
418
+ * options, or the last verdict. `snapshot({ redact: true })` also replaces the previews.
419
+ * Hashes are not cryptographic: a short, guessable value could be recovered by trying candidates.
420
+ */
421
+ interface GuardSnapshot {
422
+ version: GuardSnapshotVersion;
423
+ steps: number;
424
+ toolCalls: number;
425
+ failedToolCalls: number;
426
+ totals: GuardTotals;
427
+ /** Milliseconds of wall-clock time elapsed when the snapshot was taken; `null` without a duration budget. */
428
+ wallClockMs: number | null;
429
+ options: GuardOptionsFingerprint;
430
+ /** `null` for a detector that is disabled. */
431
+ detectors: {
432
+ repeatedToolCall: RepeatedToolCallSnapshot | null;
433
+ oscillation: OscillationSnapshot | null;
434
+ noProgress: NoProgressSnapshot | null;
435
+ finishReason: {
436
+ lastFinishReason: string | null;
437
+ } | null;
438
+ /** Calls so far for each tool with a `maxCalls`, in configuration order; `null` when no tool has one. */
439
+ toolLimits: Array<{
440
+ tool: string;
441
+ calls: number;
442
+ }> | null;
443
+ };
444
+ }
445
+ interface SnapshotOptions {
446
+ /**
447
+ * Replace the argument and text previews in the snapshot with `[redacted]`. Stop decisions stay
448
+ * identical after a restore, but evidence and messages that quote those previews will say
449
+ * `[redacted]` for calls made before the snapshot. Default `false`.
450
+ */
451
+ redact?: boolean;
452
+ }
453
+ interface RestoreOptions {
454
+ /**
455
+ * Allow `maxSteps` and `budget` to differ from the options the snapshot was taken with, for
456
+ * example to give a run that hit its limit more room. Every other option must still match.
457
+ * Default `false`.
458
+ */
459
+ allowLimitChanges?: boolean;
460
+ }
262
461
  interface Guard {
263
462
  /** Record a step and decide whether the run should continue. Never throws on odd step data. */
264
463
  check(step: GuardStep): Verdict;
464
+ /**
465
+ * Would this call (or these parallel calls) trip a rule if it ran now? Records nothing and does
466
+ * not change the guard: call it as often as you like, then still `check` the finished step.
467
+ * Evaluates only the rules that need no results, text, finish reason or usage; see the README
468
+ * ("Preflight") for the exact list. Never throws on odd call data.
469
+ */
470
+ preflight(call: PreflightCall | readonly PreflightCall[]): Preflight;
265
471
  /** Forget everything and start a fresh run (options are kept). */
266
472
  reset(): void;
473
+ /**
474
+ * Plain, JSON-safe state for carrying the run into another process; restore it with
475
+ * `createGuard.fromJSON`. Does not change the guard. Because it is called `toJSON`,
476
+ * `JSON.stringify(guard)` gives the same data. See `GuardSnapshot` for what it holds.
477
+ */
478
+ toJSON(): GuardSnapshot;
479
+ /** Same as `toJSON`, with options. */
480
+ snapshot(options?: SnapshotOptions): GuardSnapshot;
267
481
  /** Counts, totals and per-detector state. Contains nothing time-dependent. */
268
482
  summary(): GuardSummary;
269
- /** Frozen shallow copy of the steps recorded so far. */
483
+ /**
484
+ * Frozen shallow copy of the steps recorded so far by this guard object. A guard restored from a
485
+ * snapshot starts with an empty history: the raw steps are not part of a snapshot.
486
+ */
270
487
  readonly history: readonly GuardStep[];
271
488
  }
272
489
 
@@ -276,19 +493,52 @@ declare const DEFAULT_MAX_STEPS = 50;
276
493
  * Create a guard. Feed it one step at a time with `check`; it answers `{ stop: false }` or a
277
494
  * typed verdict saying why the run should end.
278
495
  *
279
- * Detectors run in this order and the first stop wins: finish reason, budget, max steps,
280
- * repeated tool call, oscillation, no progress, then custom detectors. All of them see every step
281
- * even after one has stopped, so their state stays consistent.
496
+ * Detectors run in this order and the first stop wins: finish reason, budget, max steps, tool
497
+ * limit, repeated tool call, oscillation, no progress, then custom detectors. All of them see
498
+ * every step even after one has stopped, so their state stays consistent.
499
+ *
500
+ * Tools with `ignore: true` in `options.tools` are removed from the calls the tool-limit,
501
+ * repeated-call, oscillation and no-progress detectors see.
282
502
  *
283
503
  * Invalid options throw a `TypeError` here, never later from `check`.
504
+ *
505
+ * `createGuard.fromJSON(snapshot, options)` (also exported as `restoreGuard`) continues a run from
506
+ * `guard.toJSON()`.
284
507
  */
285
508
  declare function createGuard(options?: GuardOptions): Guard;
509
+ declare namespace createGuard {
510
+ var fromJSON: typeof restoreGuard;
511
+ }
512
+ /**
513
+ * Rebuild a guard from a snapshot (`guard.toJSON()`), so that it goes on exactly where the original
514
+ * stopped: given the same steps, it gives the same verdicts.
515
+ *
516
+ * Pass the same `options` the snapshot was taken with. They are not stored (functions cannot
517
+ * be), only fingerprinted, so a mismatch throws a `TypeError` naming the option groups that differ
518
+ * instead of silently behaving differently. `restoreOptions.allowLimitChanges` relaxes that for
519
+ * `maxSteps` and `budget` only. `now`, `feedback` and the code of custom detectors are not checked.
520
+ *
521
+ * Anything wrong with the snapshot (unknown version, wrong shape, hostile values) throws a
522
+ * `TypeError` here, never later from `check`. `snapshot` may be the result of `JSON.parse`.
523
+ */
524
+ declare function restoreGuard(snapshot: unknown, options?: GuardOptions, restoreOptions?: RestoreOptions): Guard;
286
525
 
287
526
  /** True for a stop verdict that is not just the model finishing normally (`finishReason: 'stop'`). */
288
527
  declare function isFault(verdict: Verdict): verdict is StopVerdict;
289
528
  /** Turn a verdict into readable text for logs and error messages. Never throws. */
290
529
  declare function explain(verdict: Verdict): string;
291
530
 
531
+ /**
532
+ * The default model-facing text for a stop verdict, or `undefined` when there is none: a normal
533
+ * `stop` finish (not a fault), and a `custom` verdict that did not bring its own `feedback`.
534
+ *
535
+ * `phase` is `'check'` (the default) for a verdict returned by `guard.check`, where the step has
536
+ * already happened, and `'preflight'` for a call that `guard.preflight` refused to let run. The
537
+ * text is deterministic: the same verdict always produces the same string.
538
+ */
539
+ declare function feedbackFor(verdict: PreflightStopVerdict, phase?: FeedbackPhase): string;
540
+ declare function feedbackFor(verdict: StopVerdict, phase?: FeedbackPhase): string | undefined;
541
+
292
542
  /**
293
543
  * Canonicalisation: turn any JavaScript value into a deterministic string so that two
294
544
  * "equal" tool-call arguments compare equal regardless of key order, and so that nothing
@@ -438,4 +688,4 @@ interface GuardedSteps<S extends GuardStep> extends AsyncIterable<S> {
438
688
  */
439
689
  declare function withGuard<S extends GuardStep>(source: AsyncIterable<S> | Iterable<S>, guard: Guard): GuardedSteps<S>;
440
690
 
441
- export { type AbortSignalLike, type AiSdkStepLike, type AiSdkToolCallLike, type AiSdkToolResultLike, type BudgetEvidence, type BudgetLimit, type BudgetMetric, type BudgetOptions, type BudgetVerdict, type BuiltinStopCode, type ContinueVerdict, type CustomDetector, type CustomStop, type CustomVerdict, DEFAULT_MAX_STEPS, type FinishReasonEvidence, type FinishReasonOptions, type FinishReasonVerdict, type Guard, type GuardOptions, type GuardStep, type GuardSummary, type GuardToolCall, type GuardTotals, type GuardUsage, type GuardWarning, type GuardedSteps, type LoopResult, type LoopStep, type MaxStepsEvidence, type MaxStepsVerdict, type NoProgressEvidence, type NoProgressOptions, type NoProgressVerdict, type OscillationEvidence, type OscillationOptions, type OscillationVerdict, type OtherFinishPolicy, type RepeatedToolCallEvidence, type RepeatedToolCallOptions, type RepeatedToolCallVerdict, type RunAgentLoopOptions, type StopReason, type StopVerdict, type ToStopWhenOptions, type Verdict, canonicalize, createGuard, explain, isFault, runAgentLoop, stableHash, toGuardStep, toStopWhen, withGuard };
691
+ export { type AbortSignalLike, type AiSdkStepLike, type AiSdkToolCallLike, type AiSdkToolResultLike, type BudgetEvidence, type BudgetLimit, type BudgetMetric, type BudgetOptions, type BudgetVerdict, type BuiltinStopCode, type ContinueVerdict, type CustomDetector, type CustomStop, type CustomVerdict, DEFAULT_MAX_STEPS, type FeedbackPhase, type FinishReasonEvidence, type FinishReasonOptions, type FinishReasonVerdict, type Guard, type GuardOptions, type GuardOptionsFingerprint, type GuardSnapshot, type GuardSnapshotVersion, type GuardStep, type GuardSummary, type GuardToolCall, type GuardTotals, type GuardUsage, type GuardWarning, type GuardedSteps, type LoopResult, type LoopStep, type MaxStepsEvidence, type MaxStepsVerdict, type NoProgressEvidence, type NoProgressOptions, type NoProgressSnapshot, type NoProgressVerdict, type OscillationEvidence, type OscillationOptions, type OscillationSnapshot, type OscillationVerdict, type OtherFinishPolicy, type Preflight, type PreflightAllow, type PreflightCall, type PreflightDeny, type PreflightStopVerdict, type RepeatedToolCallEvidence, type RepeatedToolCallOptions, type RepeatedToolCallSnapshot, type RepeatedToolCallVerdict, type RestoreOptions, type RunAgentLoopOptions, type SnapshotOptions, type StopReason, type StopVerdict, type ToStopWhenOptions, type ToolLimitEvidence, type ToolLimitVerdict, type ToolPolicy, type Verdict, canonicalize, createGuard, explain, feedbackFor, isFault, restoreGuard, runAgentLoop, stableHash, toGuardStep, toStopWhen, withGuard };