@czottmann/pi-automode 1.8.2 → 1.9.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/README.md CHANGED
@@ -88,12 +88,17 @@ This is project-local and should not be committed. Shared project `.pi/automode.
88
88
 
89
89
  Set a global default classifier model in `~/.pi/agent/automode.json`; override it per project in `.pi/automode.local.json`.
90
90
 
91
+ `classifierReasoningLevel` optionally requests `low`, `medium`, `high`, `xhigh`, or `max` reasoning for both classifier stages. If the key is absent, pi-automode sends no reasoning preference and leaves the choice to the server. Pi AI clamps unsupported values to the nearest level supported by the selected model; a non-reasoning model resolves to `off`. `low` matches Codex Auto Review's reasoning effort and is the practical default when an explicit value is needed. Higher levels can consume the existing 512/1200-token stage limits before producing visible output, which causes the classifier to fail closed.
92
+
93
+ The setting follows the normal scalar precedence: global, then project-local, then `PI_AUTOMODE_SETTINGS_JSON`. Shared project `.pi/automode.json` cannot set it. Omitting the key at a higher-precedence scope does not clear a lower-precedence value.
94
+
91
95
  Example:
92
96
 
93
97
  ```json
94
98
  {
95
99
  "autoMode": {
96
100
  "classifierModel": "provider/model-id",
101
+ "classifierReasoningLevel": "low",
97
102
  "maxUserTranscriptTokens": 4000,
98
103
  "maxToolTranscriptTokens": 4000,
99
104
  "environment": [
@@ -253,6 +253,10 @@ The classifier model is selected in this order:
253
253
 
254
254
  `/automode model provider/model-id` and the interactive model picker save `autoMode.classifierModel` to `~/.pi/agent/automode.json`. Project-local `.pi/automode.local.json` can still override that global choice.
255
255
 
256
+ `autoMode.classifierReasoningLevel` can request `low`, `medium`, `high`, `xhigh`, or `max` reasoning for both classifier stages. When the key is absent, classifier calls use the raw completion path and omit a reasoning preference so the server can choose its default. When it is set, classifier calls use Pi AI's normalized completion path. Pi AI clamps the request to the nearest level supported by the model; non-reasoning models resolve to `off`, remain on the normalized path, and receive no reasoning preference.
257
+
258
+ Reasoning does not raise the stage token limits. A high level can consume the fast stage's 512 tokens or the detailed stage's 1200 tokens before producing valid visible output. Truncation still fails closed. `low` is the practical explicit setting and matches Codex Auto Review.
259
+
256
260
  The extension asks Pi's model registry for API credentials. If the model cannot be found or credentials are unavailable, classification returns a blocking decision:
257
261
 
258
262
  ```text
@@ -55,6 +55,21 @@ One per tool-call decision. Every allow and every block goes through exactly one
55
55
  | `outcome` | `allow` or `block` |
56
56
  | `reason` | the reason string (classifier reason, or the deterministic/permission reason) |
57
57
  | `classifierModel` | the configured classifier model, when relevant |
58
+ | `reasoning` | classifier reasoning mode and requested/effective level; see below |
59
+
60
+ The reasoning field records either server-default mode:
61
+
62
+ ```json
63
+ {"mode":"server-default"}
64
+ ```
65
+
66
+ or an explicit request after model-level clamping:
67
+
68
+ ```json
69
+ {"mode":"explicit","requestedLevel":"max","effectiveLevel":"xhigh"}
70
+ ```
71
+
72
+ Classifier-routed decisions contain the effective level once the configured model resolves, even when `classifierIo` is off or authentication then fails. If the configured model itself cannot be resolved, an explicit entry records `requestedLevel` without `effectiveLevel` because no model-supported level exists. A local permission, deterministic, or read-only decision does not run the classifier and likewise may omit `effectiveLevel`. In `server-default` mode, the concrete server-selected level is not observable and is not inferred.
58
73
 
59
74
  ### `message` (classifier usage)
60
75
 
@@ -78,6 +93,7 @@ Written only for classifier-routed actions, and only when `classifierIo: true`.
78
93
  | `ts` | ISO timestamp |
79
94
  | `decisionId` | matches the `decision` entry for the same call |
80
95
  | `model` | classifier model used, e.g. `anthropic/claude-haiku-4` |
96
+ | `reasoning` | `server-default`, or the explicit requested and effective model-supported level |
81
97
  | `prompt.system` | the full system policy with `environment`/`allow`/`soft_deny`/`hard_deny` rules interpolated |
82
98
  | `prompt.context` | the shared context message: loaded project instructions + classifier transcript + action |
83
99
  | `prompt.fastInstruction` | the exact one-token filter instruction |
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "autoMode": {
3
+ "classifierReasoningLevel": "low",
3
4
  "environment": [
4
5
  "$defaults",
5
6
  "Source control: github.example.com/acme-corp and all repos under it",
@@ -1,5 +1,9 @@
1
1
  import { createHash } from "node:crypto";
2
- import { complete } from "@earendil-works/pi-ai";
2
+ import { clampThinkingLevel } from "@earendil-works/pi-ai";
3
+ import {
4
+ complete,
5
+ completeSimple,
6
+ } from "@earendil-works/pi-ai/compat";
3
7
  import type {
4
8
  AssistantMessage,
5
9
  Model,
@@ -17,7 +21,11 @@ import type {
17
21
  ClassificationDecision,
18
22
  ClassifyAction,
19
23
  ClassifierIoAttempt,
24
+ ClassifierReasoning,
25
+ ClassifierReasoningLevel,
26
+ ClassifierReasoningLog,
20
27
  ClassifyResult,
28
+ EffectiveClassifierReasoningLevel,
21
29
  EffectiveConfig,
22
30
  } from "./types.ts";
23
31
 
@@ -40,13 +48,28 @@ export function buildClassifierPrompt(config: EffectiveConfig): string {
40
48
  );
41
49
  }
42
50
 
51
+ type ClassifierResolution = {
52
+ reasoning: ClassifierReasoningLog;
53
+ classifier?: {
54
+ model: Model<any>;
55
+ apiKey?: string;
56
+ headers?: Record<string, string>;
57
+ };
58
+ completionPlan?: ClassifierCompletionPlan;
59
+ };
60
+
61
+ export function classifierReasoningForConfig(
62
+ requestedLevel: ClassifierReasoningLevel | undefined,
63
+ ): ClassifierReasoningLog {
64
+ return requestedLevel === undefined
65
+ ? { mode: "server-default" }
66
+ : { mode: "explicit", requestedLevel };
67
+ }
68
+
43
69
  async function resolveClassifier(
44
70
  ctx: ExtensionContext,
45
71
  config: EffectiveConfig,
46
- ): Promise<
47
- | { model: Model<any>; apiKey?: string; headers?: Record<string, string> }
48
- | undefined
49
- > {
72
+ ): Promise<ClassifierResolution> {
50
73
  const configured = config.classifierModel;
51
74
  const model = configured
52
75
  ? (() => {
@@ -56,10 +79,27 @@ async function resolveClassifier(
56
79
  : undefined;
57
80
  })()
58
81
  : ctx.model;
59
- if (!model) return undefined;
82
+ if (!model) {
83
+ return {
84
+ reasoning: classifierReasoningForConfig(config.classifierReasoningLevel),
85
+ };
86
+ }
87
+
88
+ const completionPlan = createClassifierCompletionPlan(
89
+ model,
90
+ config.classifierReasoningLevel,
91
+ );
60
92
  const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
61
- if (!auth.ok) return undefined;
62
- return { model, apiKey: auth.apiKey, headers: auth.headers };
93
+ if (!auth.ok) return { reasoning: completionPlan.reasoning };
94
+ return {
95
+ reasoning: completionPlan.reasoning,
96
+ classifier: {
97
+ model,
98
+ apiKey: auth.apiKey,
99
+ headers: auth.headers,
100
+ },
101
+ completionPlan,
102
+ };
63
103
  }
64
104
 
65
105
  export type ClassifierCompletionFn = (
@@ -71,6 +111,7 @@ export type ClassifierCompletionFn = (
71
111
  signal?: AbortSignal;
72
112
  maxTokens: number;
73
113
  temperature?: number;
114
+ reasoning?: Exclude<EffectiveClassifierReasoningLevel, "off">;
74
115
  sessionId?: string;
75
116
  cacheRetention?: "none" | "short" | "long";
76
117
  },
@@ -80,6 +121,7 @@ export type RetryOptions = {
80
121
  maxAttempts?: number;
81
122
  maxTokens?: number;
82
123
  temperature?: number;
124
+ reasoningLevel?: Exclude<EffectiveClassifierReasoningLevel, "off">;
83
125
  sessionId?: string;
84
126
  cacheRetention?: "none" | "short" | "long";
85
127
  stage?: "fast" | "detailed";
@@ -91,9 +133,46 @@ const FAST_CLASSIFIER_MAX_TOKENS = 512;
91
133
 
92
134
  export type StagedClassifierOptions = {
93
135
  sessionId: string;
136
+ reasoningLevel?: Exclude<EffectiveClassifierReasoningLevel, "off">;
94
137
  onAttempt?: (attempt: ClassifierIoAttempt) => void;
95
138
  };
96
139
 
140
+ export type ClassifierCompletionPlan = {
141
+ completeFn: ClassifierCompletionFn;
142
+ reasoning: ClassifierReasoning;
143
+ reasoningLevel?: Exclude<EffectiveClassifierReasoningLevel, "off">;
144
+ };
145
+
146
+ /** Select the raw or normalized Pi AI completion path and record the effective level. */
147
+ export function createClassifierCompletionPlan(
148
+ model: Model<any>,
149
+ requestedLevel: ClassifierReasoningLevel | undefined,
150
+ rawComplete: ClassifierCompletionFn = complete,
151
+ simpleComplete: ClassifierCompletionFn = completeSimple,
152
+ ): ClassifierCompletionPlan {
153
+ if (requestedLevel === undefined) {
154
+ return {
155
+ completeFn: rawComplete,
156
+ reasoning: { mode: "server-default" },
157
+ };
158
+ }
159
+
160
+ const effectiveLevel = clampThinkingLevel(model, requestedLevel);
161
+ const reasoning: ClassifierReasoning = {
162
+ mode: "explicit",
163
+ requestedLevel,
164
+ effectiveLevel,
165
+ };
166
+ if (effectiveLevel === "off") {
167
+ return { completeFn: simpleComplete, reasoning };
168
+ }
169
+ return {
170
+ completeFn: simpleComplete,
171
+ reasoning,
172
+ reasoningLevel: effectiveLevel,
173
+ };
174
+ }
175
+
97
176
  /** Concatenate all text blocks of an assistant message into a single string. */
98
177
  function extractAssistantText(message: AssistantMessage, trim = true): string {
99
178
  const text = message.content
@@ -250,6 +329,9 @@ export async function classifyWithRetry(
250
329
  signal,
251
330
  maxTokens,
252
331
  ...(temperature === undefined ? {} : { temperature }),
332
+ ...(options.reasoningLevel === undefined
333
+ ? {}
334
+ : { reasoning: options.reasoningLevel }),
253
335
  sessionId: options.sessionId,
254
336
  cacheRetention: options.cacheRetention,
255
337
  },
@@ -317,6 +399,9 @@ export async function classifyInStages(
317
399
  // Reasoning and OpenAI-compatible models may consume hidden reasoning,
318
400
  // control, and EOS tokens before emitting the required visible digit.
319
401
  maxTokens: FAST_CLASSIFIER_MAX_TOKENS,
402
+ ...(options.reasoningLevel === undefined
403
+ ? {}
404
+ : { reasoning: options.reasoningLevel }),
320
405
  sessionId: options.sessionId,
321
406
  cacheRetention: "short",
322
407
  },
@@ -380,6 +465,7 @@ export async function classifyInStages(
380
465
  stage: "detailed",
381
466
  sessionId: options.sessionId,
382
467
  cacheRetention: "short",
468
+ reasoningLevel: options.reasoningLevel,
383
469
  onAttempt: options.onAttempt,
384
470
  },
385
471
  );
@@ -398,14 +484,17 @@ export const defaultClassifyAction: ClassifyAction = async (
398
484
  action,
399
485
  loadedContext,
400
486
  ): Promise<ClassifyResult> => {
401
- const classifier = await resolveClassifier(ctx, config);
402
- if (!classifier) {
487
+ const resolution = await resolveClassifier(ctx, config);
488
+ if (!resolution.classifier || !resolution.completionPlan) {
403
489
  return {
404
490
  decision: "block",
405
491
  tier: "none",
406
492
  reason: "No classifier model/API key available; auto mode fails closed.",
493
+ reasoning: resolution.reasoning,
407
494
  };
408
495
  }
496
+ const classifier = resolution.classifier;
497
+ const completionPlan = resolution.completionPlan;
409
498
 
410
499
  const systemPrompt = buildClassifierPrompt(config);
411
500
  const transcript = buildClassifierTranscript(ctx, {
@@ -426,20 +515,23 @@ export const defaultClassifyAction: ClassifyAction = async (
426
515
  const attempts: ClassifierIoAttempt[] = [];
427
516
  const started = Date.now();
428
517
  const decision = await classifyInStages(
429
- complete,
518
+ completionPlan.completeFn,
430
519
  classifier,
431
520
  { systemPrompt, contextMessage },
432
521
  ctx.signal,
433
522
  {
434
523
  sessionId: classifierCacheSessionId(ctx),
524
+ reasoningLevel: completionPlan.reasoningLevel,
435
525
  onAttempt: (attempt) => attempts.push(attempt),
436
526
  },
437
527
  );
438
528
 
439
529
  return {
440
530
  ...decision,
531
+ reasoning: completionPlan.reasoning,
441
532
  io: {
442
533
  model: formatModelSpec(classifier.model),
534
+ reasoning: completionPlan.reasoning,
443
535
  prompt: {
444
536
  system: systemPrompt,
445
537
  context: contextText,
@@ -16,6 +16,7 @@ import {
16
16
  import { parseToolPattern } from "./permissions.ts";
17
17
  import type {
18
18
  AutoModeSettings,
19
+ ClassifierReasoningLevel,
19
20
  ConfigLoadResult,
20
21
  EffectiveConfig,
21
22
  LoadedSettingsFile,
@@ -97,6 +98,7 @@ export function validateSettingsFile(
97
98
  const knownAutoMode = new Set([
98
99
  "enabled",
99
100
  "classifierModel",
101
+ "classifierReasoningLevel",
100
102
  "maxUserTranscriptTokens",
101
103
  "maxToolTranscriptTokens",
102
104
  "environment",
@@ -126,6 +128,14 @@ export function validateSettingsFile(
126
128
  `${source}: autoMode.classifierModel must be a provider/model string`,
127
129
  );
128
130
  }
131
+ if (
132
+ hasOwn(autoMode, "classifierReasoningLevel") &&
133
+ !isClassifierReasoningLevel(autoMode.classifierReasoningLevel)
134
+ ) {
135
+ diagnostics.push(
136
+ `${source}: autoMode.classifierReasoningLevel must be one of low, medium, high, xhigh, max`,
137
+ );
138
+ }
129
139
  for (
130
140
  const key of [
131
141
  "maxUserTranscriptTokens",
@@ -273,6 +283,21 @@ function mergeLog(
273
283
  };
274
284
  }
275
285
 
286
+ const CLASSIFIER_REASONING_LEVELS = new Set<ClassifierReasoningLevel>([
287
+ "low",
288
+ "medium",
289
+ "high",
290
+ "xhigh",
291
+ "max",
292
+ ]);
293
+
294
+ export function isClassifierReasoningLevel(
295
+ value: unknown,
296
+ ): value is ClassifierReasoningLevel {
297
+ return typeof value === "string" &&
298
+ CLASSIFIER_REASONING_LEVELS.has(value as ClassifierReasoningLevel);
299
+ }
300
+
276
301
  function validTranscriptBudget(value: unknown): value is number {
277
302
  return Number.isInteger(value) && Number(value) >= 32;
278
303
  }
@@ -286,6 +311,11 @@ function applyAutoModeScalars(
286
311
  ...base,
287
312
  enabled: settings.enabled ?? base.enabled,
288
313
  classifierModel: settings.classifierModel ?? base.classifierModel,
314
+ classifierReasoningLevel: isClassifierReasoningLevel(
315
+ settings.classifierReasoningLevel,
316
+ )
317
+ ? settings.classifierReasoningLevel
318
+ : base.classifierReasoningLevel,
289
319
  maxUserTranscriptTokens: validTranscriptBudget(
290
320
  settings.maxUserTranscriptTokens,
291
321
  )
@@ -155,6 +155,7 @@ export const CLASSIFIER_FAST_INSTRUCTION =
155
155
  export const CLASSIFIER_DETAILED_INSTRUCTION =
156
156
  `Return only JSON exactly matching:
157
157
  {"decision":"allow"|"block","tier":"hard_deny"|"soft_deny"|"allow"|"explicit_intent"|"none","reason":"brief concrete reason"}
158
+ Do not use Markdown, code fences, prose, or any wrapper. The first character must be { and the last character must be }.
158
159
  Valid decision/tier combinations:
159
160
  - allow: allow, explicit_intent, or none
160
161
  - block: hard_deny, soft_deny, or none
@@ -3,7 +3,10 @@ import type {
3
3
  ExtensionCommandContext,
4
4
  ExtensionContext,
5
5
  } from "@earendil-works/pi-coding-agent";
6
- import { defaultClassifyAction } from "./classifier.ts";
6
+ import {
7
+ classifierReasoningForConfig,
8
+ defaultClassifyAction,
9
+ } from "./classifier.ts";
7
10
  import {
8
11
  AUTO_MODE_GUIDANCE,
9
12
  DEFAULT_ALLOW,
@@ -39,6 +42,7 @@ import {
39
42
  import { loadedContextFromSystemPromptOptions } from "./transcript.ts";
40
43
  import type {
41
44
  AutoModeState,
45
+ ClassifierReasoningLog,
42
46
  ClassifyAction,
43
47
  ClassifyResult,
44
48
  ConfigLoadResult,
@@ -61,10 +65,13 @@ type LogCtx = {
61
65
  logger: Logger;
62
66
  decisionId: string;
63
67
  classifierModel?: string;
68
+ reasoning: ClassifierReasoningLog;
64
69
  };
65
70
 
66
71
  /** Append ccusage-compatible usage and optional classifier I/O entries. */
67
72
  function logClassifierIo(decision: ClassifyResult, log: LogCtx): void {
73
+ if (decision.reasoning) log.reasoning = decision.reasoning;
74
+ if (decision.io) log.reasoning = decision.io.reasoning;
68
75
  if (!log.logger.enabled || !decision.io) return;
69
76
 
70
77
  for (const attempt of decision.io.attempts) {
@@ -87,6 +94,7 @@ function logClassifierIo(decision: ClassifyResult, log: LogCtx): void {
87
94
  ts: new Date().toISOString(),
88
95
  decisionId: log.decisionId,
89
96
  model: decision.io.model,
97
+ reasoning: decision.io.reasoning,
90
98
  prompt: decision.io.prompt,
91
99
  attempts: decision.io.attempts,
92
100
  durationMs: decision.io.durationMs,
@@ -170,6 +178,7 @@ export function createPiAutomode(options: PiAutomodeOptions = {}) {
170
178
  outcome: "block",
171
179
  reason: denial.reason,
172
180
  classifierModel: logCtx.classifierModel,
181
+ reasoning: logCtx.reasoning,
173
182
  });
174
183
  }
175
184
  if (ctx.hasUI) {
@@ -206,6 +215,7 @@ export function createPiAutomode(options: PiAutomodeOptions = {}) {
206
215
  outcome: "allow",
207
216
  reason,
208
217
  classifierModel: logCtx.classifierModel,
218
+ reasoning: logCtx.reasoning,
209
219
  });
210
220
  }
211
221
  return undefined;
@@ -251,6 +261,7 @@ export function createPiAutomode(options: PiAutomodeOptions = {}) {
251
261
  }),
252
262
  decisionId: newDecisionId(),
253
263
  classifierModel: cfg.classifierModel,
264
+ reasoning: classifierReasoningForConfig(cfg.classifierReasoningLevel),
254
265
  };
255
266
 
256
267
  for (const pattern of cfg.permissionDeny) {
@@ -4,6 +4,7 @@ import { basename, dirname, extname, join } from "node:path";
4
4
  import type {
5
5
  ClassifierIo,
6
6
  ClassifierIoAttempt,
7
+ ClassifierReasoningLog,
7
8
  ClassificationDecision,
8
9
  DecisionKind,
9
10
  } from "./types.ts";
@@ -21,6 +22,7 @@ export type DecisionLogEntry = {
21
22
  outcome: "allow" | "block";
22
23
  reason: string;
23
24
  classifierModel?: string;
25
+ reasoning: ClassifierReasoningLog;
24
26
  };
25
27
 
26
28
  /** The classifier prompt, raw responses, and parsed decision for one action. */
@@ -29,6 +31,7 @@ export type ClassifierLogEntry = {
29
31
  ts: string;
30
32
  decisionId: string;
31
33
  model: string;
34
+ reasoning: ClassifierIo["reasoning"];
32
35
  prompt: ClassifierIo["prompt"];
33
36
  attempts: ClassifierIoAttempt[];
34
37
  durationMs: number;
@@ -30,6 +30,7 @@ export function statusText(
30
30
  return [
31
31
  `enabled: ${(state.enabledOverride ?? config.enabled) ? "yes" : "no"}`,
32
32
  `classifier: ${config.classifierModel ?? "current session model"}`,
33
+ `classifier reasoning: ${config.classifierReasoningLevel ?? "server default"}`,
33
34
  `checked actions: ${state.checkedActions}`,
34
35
  `blocked actions: ${state.blockedActions}`,
35
36
  `classifier allowed: ${state.classifierAllowed}`,
@@ -1,6 +1,34 @@
1
1
  import type { AssistantMessage } from "@earendil-works/pi-ai";
2
2
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
3
3
 
4
+ export type ClassifierReasoningLevel =
5
+ | "low"
6
+ | "medium"
7
+ | "high"
8
+ | "xhigh"
9
+ | "max";
10
+
11
+ export type EffectiveClassifierReasoningLevel =
12
+ | "off"
13
+ | "minimal"
14
+ | ClassifierReasoningLevel;
15
+
16
+ export type ClassifierReasoning =
17
+ | { mode: "server-default" }
18
+ | {
19
+ mode: "explicit";
20
+ requestedLevel: ClassifierReasoningLevel;
21
+ effectiveLevel: EffectiveClassifierReasoningLevel;
22
+ };
23
+
24
+ export type ClassifierReasoningLog =
25
+ | ClassifierReasoning
26
+ | {
27
+ mode: "explicit";
28
+ requestedLevel: ClassifierReasoningLevel;
29
+ effectiveLevel?: undefined;
30
+ };
31
+
4
32
  /** Observability log configuration. Off by default. */
5
33
  export type LogConfig = {
6
34
  enabled: boolean;
@@ -11,6 +39,7 @@ export type LogConfig = {
11
39
  export type AutoModeSettings = {
12
40
  enabled?: boolean;
13
41
  classifierModel?: string;
42
+ classifierReasoningLevel?: ClassifierReasoningLevel;
14
43
  maxUserTranscriptTokens?: number;
15
44
  maxToolTranscriptTokens?: number;
16
45
  environment?: unknown;
@@ -46,6 +75,7 @@ export type ToolPattern = {
46
75
  export type EffectiveConfig = {
47
76
  enabled: boolean;
48
77
  classifierModel?: string;
78
+ classifierReasoningLevel?: ClassifierReasoningLevel;
49
79
  maxUserTranscriptTokens: number;
50
80
  maxToolTranscriptTokens: number;
51
81
  environment: string[];
@@ -111,6 +141,7 @@ export type ClassifierIoAttempt = {
111
141
  /** Full classifier I/O for an action, surfaced for optional observability logging. */
112
142
  export type ClassifierIo = {
113
143
  model: string;
144
+ reasoning: ClassifierReasoning;
114
145
  prompt: {
115
146
  system: string;
116
147
  context: string;
@@ -121,8 +152,11 @@ export type ClassifierIo = {
121
152
  durationMs: number;
122
153
  };
123
154
 
124
- /** Classification decision plus the I/O that produced it (when available). */
125
- export type ClassifyResult = ClassificationDecision & { io?: ClassifierIo };
155
+ /** Classification decision plus resolved reasoning and the I/O that produced it (when available). */
156
+ export type ClassifyResult = ClassificationDecision & {
157
+ reasoning?: ClassifierReasoningLog;
158
+ io?: ClassifierIo;
159
+ };
126
160
 
127
161
  export type SettingsSources = {
128
162
  globalSettings?: SettingsFile[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@czottmann/pi-automode",
3
- "version": "1.8.2",
3
+ "version": "1.9.0",
4
4
  "description": "Claude Code-style auto mode guardrail for pi.",
5
5
  "repository": {
6
6
  "url": "https://github.com/czottmann/pi-automode"
@@ -41,9 +41,9 @@
41
41
  "@earendil-works/pi-tui": "*"
42
42
  },
43
43
  "devDependencies": {
44
- "@earendil-works/pi-ai": "^0.79.3",
45
- "@earendil-works/pi-coding-agent": "^0.79.3",
46
- "@earendil-works/pi-tui": "^0.79.3",
44
+ "@earendil-works/pi-ai": "^0.81.1",
45
+ "@earendil-works/pi-coding-agent": "^0.81.1",
46
+ "@earendil-works/pi-tui": "^0.81.1",
47
47
  "@types/node": "^24.0.0",
48
48
  "tsx": "^4.22.4",
49
49
  "typescript": "^5.8.0"