killeros 2.0.18 → 2.0.19

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 CHANGED
@@ -4,6 +4,18 @@ All notable changes to KillerOS are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.0.19] - 2026-08-24
8
+
9
+ ### Changed
10
+
11
+ - Hardened JSON and caught-error boundaries, made goal and `/init` target states constructive, added exhaustive outcome handling, and replaced unsafe test doubles with checked adapters.
12
+ - Deprecated positional `executeHook` arguments in favor of object options; the compatibility adapter will be removed in the next major release.
13
+
14
+ ### Fixed
15
+
16
+ - Paused active goals during automatic compaction and resumed the exact paused revision once after compaction and turn settlement succeed.
17
+ - Restored active goals saved by v2.0.18 shutdown checkpoints that omitted their stopped clock timestamp.
18
+
7
19
  ## [2.0.18] - 2026-08-24
8
20
 
9
21
  ### Added
package/README.md CHANGED
@@ -34,7 +34,7 @@ Or from GitHub:
34
34
  pi install git:github.com/KyrosHendrix/pi-KillerOS
35
35
  ```
36
36
 
37
- Pin a release by appending its tag, for example `@v2.0.18`. Add `-l` to install only for the current project. Restart Pi after installing.
37
+ Pin a release by appending its tag, for example `@v2.0.19`. Add `-l` to install only for the current project. Restart Pi after installing.
38
38
 
39
39
  ## Commands
40
40
 
@@ -52,6 +52,10 @@ export function formatActivityMessage(message: ActivityMessage, theme: Theme): s
52
52
  detail = `using ${safeToolName(message.toolName)}`;
53
53
  }
54
54
  break;
55
+ default: {
56
+ const exhaustive: never = message;
57
+ return exhaustive;
58
+ }
55
59
  }
56
60
 
57
61
  return `${theme.fg("accent", verb)} ${theme.fg("dim", `(${theme.bold("esc")} to interrupt · ${detail})`)}`;
@@ -5,10 +5,9 @@ interface CodexFastState {
5
5
  listeners: Set<CodexFastStateListener>;
6
6
  }
7
7
 
8
- const GLOBAL_STATE_KEY = "__killerosCodexFastState";
9
- type GlobalWithCodexFastState = typeof globalThis & {
10
- [GLOBAL_STATE_KEY]?: unknown;
11
- };
8
+ declare global {
9
+ var __killerosCodexFastState: unknown;
10
+ }
12
11
 
13
12
  // Accepts process-global state only when it is safe to reuse across extension versions.
14
13
  function isCodexFastState(value: unknown): value is CodexFastState {
@@ -18,14 +17,13 @@ function isCodexFastState(value: unknown): value is CodexFastState {
18
17
  && [...value.listeners].every((listener) => typeof listener === "function");
19
18
  }
20
19
 
21
- const globalState = globalThis as GlobalWithCodexFastState;
22
- const savedState = globalState[GLOBAL_STATE_KEY];
20
+ const savedState = globalThis.__killerosCodexFastState;
23
21
  const state: CodexFastState = isCodexFastState(savedState) ? savedState : {
24
22
  enabled: typeof savedState === "object" && savedState !== null
25
23
  && "enabled" in savedState && savedState.enabled === true,
26
24
  listeners: new Set<CodexFastStateListener>(),
27
25
  };
28
- globalState[GLOBAL_STATE_KEY] = state;
26
+ globalThis.__killerosCodexFastState = state;
29
27
 
30
28
  export function isCodexFastEnabled(): boolean {
31
29
  return state.enabled;
@@ -234,9 +234,11 @@ export function registerSlashAutocomplete(
234
234
  };
235
235
  },
236
236
  applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
237
- const tagged = item as TaggedAutocompleteItem;
238
- if (!tagged.killerosCommand) return current.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
239
- usage.set(tagged.killerosCommand, (usage.get(tagged.killerosCommand) ?? 0) + 1);
237
+ const commandName = "killerosCommand" in item && typeof item.killerosCommand === "string"
238
+ ? item.killerosCommand
239
+ : undefined;
240
+ if (!commandName) return current.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
241
+ usage.set(commandName, (usage.get(commandName) ?? 0) + 1);
240
242
  const line = lines[cursorLine] ?? "";
241
243
  const beforeCursor = line.slice(0, cursorCol);
242
244
  const afterCursor = line.slice(cursorCol);
@@ -1,6 +1,14 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
2
  import { safeTerminalText } from "./safe-terminal-text.ts";
3
3
 
4
+ /** Checks a caught value for one exact string error code. */
5
+ export function hasErrorCode(error: unknown, code: string): boolean {
6
+ return typeof error === "object"
7
+ && error !== null
8
+ && "code" in error
9
+ && error.code === code;
10
+ }
11
+
4
12
  /** Converts unknown caught values into text suitable for user-facing errors. */
5
13
  export function errorMessage(error: unknown): string {
6
14
  return safeTerminalText(error instanceof Error ? error.message : String(error));
@@ -196,7 +196,7 @@ export function registerFooter(pi: ExtensionAPI, goalRuntime: GoalRuntime): void
196
196
  unsubscribeCodexFast = subscribeCodexFast(() => activeTui?.requestRender());
197
197
  const sessionStart = Date.now();
198
198
  currentModel = ctx.model;
199
- thinkingLevel = pi.getThinkingLevel() as ThinkingLevel;
199
+ thinkingLevel = pi.getThinkingLevel();
200
200
  const cwd = formatCwd(ctx.cwd);
201
201
 
202
202
  ctx.ui.setFooter((tui, theme, footerData) => {