killeros 2.0.18 → 2.0.20

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.
@@ -38,31 +38,66 @@ export interface GoalFileVerification {
38
38
  baseline: GoalFileBaseline;
39
39
  }
40
40
 
41
- export interface GoalState {
41
+ export interface GoalStateCommon {
42
42
  version: 1;
43
43
  revision: number;
44
44
  objective: string;
45
- status: GoalStatus;
46
45
  createdAt: number;
47
46
  updatedAt: number;
48
47
  activeMilliseconds: number;
49
- activeStartedAt?: number;
50
48
  turns: number;
51
49
  blockedAuditStartTurn: number;
52
50
  baselineTokens: number;
53
- result?: string;
54
- resumeAfterManualCompaction?: true;
55
- blockerAudit?: GoalBlockerAudit;
56
51
  verification?: GoalFileVerification;
57
52
  }
58
53
 
54
+ export type GoalState = GoalStateCommon & (
55
+ | {
56
+ status: "active";
57
+ activeStartedAt: number;
58
+ result?: string;
59
+ blockerAudit?: GoalBlockerAudit;
60
+ resumeAfterManualCompaction?: never;
61
+ }
62
+ | {
63
+ status: "paused";
64
+ activeStartedAt?: never;
65
+ result?: string;
66
+ blockerAudit?: GoalBlockerAudit;
67
+ resumeAfterManualCompaction?: true;
68
+ }
69
+ | {
70
+ status: "blocked";
71
+ activeStartedAt?: never;
72
+ result: string;
73
+ blockerAudit?: GoalBlockerAudit;
74
+ resumeAfterManualCompaction?: never;
75
+ }
76
+ | {
77
+ status: "complete";
78
+ activeStartedAt?: never;
79
+ result: string;
80
+ blockerAudit?: never;
81
+ resumeAfterManualCompaction?: never;
82
+ }
83
+ );
84
+
85
+ /** Pi request outcomes for goal recovery: awaiting result, compacted, or rejected as session-too-small. */
86
+ export type AutomaticGoalCompactionOutcome = "pending" | "completed" | "skipped";
87
+
88
+ export interface AutomaticGoalCompaction {
89
+ pausedRevision: number;
90
+ outcome: AutomaticGoalCompactionOutcome;
91
+ turnSettled: boolean;
92
+ }
93
+
59
94
  export interface GoalRuntime {
60
95
  state?: GoalState;
61
96
  continuationScheduled: boolean;
62
97
  continuationHeld: boolean;
63
98
  goalTurnInFlight: boolean;
64
99
  agentEndObserved: boolean;
65
- automaticCompaction?: "pending";
100
+ automaticCompaction?: AutomaticGoalCompaction;
66
101
  persistenceRetryNeeded: boolean;
67
102
  lastStopReason?: string;
68
103
  lastError?: string;
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
5
+ import { hasErrorCode } from "./errors.ts";
5
6
 
6
7
  export type KillerosSettings = Record<string, unknown>;
7
8
 
@@ -10,15 +11,17 @@ export interface KillerosSettingsStore {
10
11
  update(patch: Readonly<Record<string, unknown>>): void;
11
12
  }
12
13
 
14
+ function isSettings(value: unknown): value is KillerosSettings {
15
+ return typeof value === "object" && value !== null && !Array.isArray(value);
16
+ }
17
+
13
18
  function readStoredSettings(settingsPath: string): KillerosSettings {
14
19
  try {
15
20
  const parsed: unknown = JSON.parse(readFileSync(settingsPath, "utf8"));
16
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
17
- throw new Error("KillerOS settings must contain a JSON object");
18
- }
19
- return parsed as KillerosSettings;
21
+ if (!isSettings(parsed)) throw new Error("KillerOS settings must contain a JSON object");
22
+ return parsed;
20
23
  } catch (error) {
21
- if ((error as NodeJS.ErrnoException).code === "ENOENT") return {};
24
+ if (hasErrorCode(error, "ENOENT")) return {};
22
25
  throw error;
23
26
  }
24
27
  }
@@ -2,7 +2,6 @@ import { execFile } from "node:child_process";
2
2
  import { readFileSync } from "node:fs";
3
3
  import {
4
4
  CustomEditor,
5
- VERSION,
6
5
  type ExtensionAPI,
7
6
  type ExtensionContext,
8
7
  type KeybindingsManager,
@@ -25,14 +24,19 @@ import {
25
24
  } from "./commands.ts";
26
25
  import { reportError } from "./errors.ts";
27
26
  import { formatModel } from "./footer.ts";
28
- import { LEVEL_COLORS, type ThinkingLevel } from "./variants.ts";
27
+ import { LEVEL_COLORS } from "./variants.ts";
29
28
 
30
29
  const COMPACT_HEADER_MAX_WIDTH = 52;
31
30
 
32
31
  function readPackageVersion(path: string | URL): string | undefined {
33
32
  try {
34
- const value = JSON.parse(readFileSync(path, "utf8")) as { version?: unknown };
35
- return typeof value.version === "string" ? value.version : undefined;
33
+ const value: unknown = JSON.parse(readFileSync(path, "utf8"));
34
+ return typeof value === "object"
35
+ && value !== null
36
+ && "version" in value
37
+ && typeof value.version === "string"
38
+ ? value.version
39
+ : undefined;
36
40
  } catch {
37
41
  return undefined;
38
42
  }
@@ -93,7 +97,11 @@ function shuffledDeck(values: readonly string[]): string[] {
93
97
  const deck = [...values];
94
98
  for (let index = deck.length - 1; index > 0; index -= 1) {
95
99
  const swapIndex = Math.floor(Math.random() * (index + 1));
96
- [deck[index], deck[swapIndex]] = [deck[swapIndex]!, deck[index]!];
100
+ const current = deck[index];
101
+ const swap = deck[swapIndex];
102
+ if (current === undefined || swap === undefined) continue;
103
+ deck[index] = swap;
104
+ deck[swapIndex] = current;
97
105
  }
98
106
  return deck;
99
107
  }
@@ -152,7 +160,7 @@ class PiStartupHeader {
152
160
  const innerWidth = panelWidth - 4;
153
161
  const version = KILLEROS_VERSION ? theme.fg("dim", ` (v${KILLEROS_VERSION})`) : "";
154
162
  const identity = `${theme.fg("dim", "›")} ${theme.fg("text", theme.bold("KillerOS"))}${version}`;
155
- const thinkingLevel = this.pi.getThinkingLevel() as ThinkingLevel;
163
+ const thinkingLevel = this.pi.getThinkingLevel();
156
164
  const reasoning = this.ctx.model?.reasoning === false
157
165
  ? theme.fg("thinkingOff", "no reasoning")
158
166
  : theme.fg(LEVEL_COLORS[thinkingLevel], thinkingLevel);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "killeros",
3
- "version": "2.0.18",
3
+ "version": "2.0.20",
4
4
  "description": "TUI, goals, and workflow automation for the Pi coding agent",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -30,7 +30,8 @@
30
30
  "node": ">=22.19.0"
31
31
  },
32
32
  "scripts": {
33
- "check": "tsc --noEmit",
33
+ "check": "tsc --noEmit && eslint .",
34
+ "lint": "eslint .",
34
35
  "test": "node --test --experimental-strip-types test/*.test.*"
35
36
  },
36
37
  "pi": {
@@ -52,8 +53,10 @@
52
53
  "@earendil-works/pi-coding-agent": "0.84.3",
53
54
  "@earendil-works/pi-tui": "0.84.3",
54
55
  "@types/node": "24.12.4",
56
+ "eslint": "^10.9.1",
55
57
  "typebox": "1.1.38",
56
- "typescript": "5.9.3"
58
+ "typescript": "5.9.3",
59
+ "typescript-eslint": "^8.68.0"
57
60
  },
58
61
  "overrides": {
59
62
  "brace-expansion": "5.0.9",