killeros 2.0.19 → 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.
- package/CHANGELOG.md +12 -0
- package/Killeros.ts +3 -1
- package/README.md +5 -2
- package/killeros/auto-compaction.ts +40 -10
- package/killeros/footer.ts +2 -2
- package/killeros/goals.ts +22 -5
- package/killeros/handoff.ts +40 -10
- package/killeros/hooks.ts +1 -1
- package/killeros/personal-instructions.ts +2 -3
- package/killeros/question.ts +0 -1
- package/killeros/runtime.ts +4 -1
- package/killeros/shell-ui.ts +0 -1
- package/package.json +6 -3
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.20] - 2026-08-25
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Added a typescript-eslint type-checked lint gate (`npm run lint`) alongside `tsc --noEmit`, and cleaned up the dead imports and untyped boundaries it surfaced.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Treated Pi's exact `Nothing to compact (session too small)` rejection as an expected automatic-compaction skip: no failure notification, silent retry on the next eligible turn, and active goals resume through a dedicated skip recovery instead of pausing for `/goal resume`.
|
|
16
|
+
- Made `/handoff` resilient to an unreadable `killeros.json`: explicit `KillerosOptions` budgets bypass the file, while other summaries fall back to the default budget with a visible warning.
|
|
17
|
+
- Named `/handoff` output truncation instead of reporting "did not finish", raised the default summary budget to 8192 tokens for reasoning models, and made the budget configurable through `KillerosOptions` or the `killeros.json` `handoffMaxTokens` key.
|
|
18
|
+
|
|
7
19
|
## [2.0.19] - 2026-08-24
|
|
8
20
|
|
|
9
21
|
### Changed
|
package/Killeros.ts
CHANGED
|
@@ -30,6 +30,8 @@ export { buildInitEvidence, listInitEvidence, readInitEvidence } from "./killero
|
|
|
30
30
|
export { captureInitTargetBaseline, installInitAgentsFile, validateGeneratedGuidance, writeInitAgentsFile } from "./killeros/init-target.ts";
|
|
31
31
|
export interface KillerosOptions {
|
|
32
32
|
completionNotifications?: CompletionNotificationDependencies;
|
|
33
|
+
/** Output-token budget for /handoff summaries; invalid values fall back to killeros.json, then the default. */
|
|
34
|
+
handoffMaxTokens?: number;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export default function Killeros(pi: ExtensionAPI, options: KillerosOptions = {}): void {
|
|
@@ -41,7 +43,7 @@ export default function Killeros(pi: ExtensionAPI, options: KillerosOptions = {}
|
|
|
41
43
|
registerPersonalInstructions(pi, initRuntime);
|
|
42
44
|
registerQuestionTool(pi);
|
|
43
45
|
registerAliases(pi);
|
|
44
|
-
registerHandoff(pi, goalRuntime);
|
|
46
|
+
registerHandoff(pi, goalRuntime, options.handoffMaxTokens);
|
|
45
47
|
registerSlashAutocomplete(pi, commandResolver);
|
|
46
48
|
registerFooter(pi, goalRuntime);
|
|
47
49
|
registerVariants(pi);
|
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.
|
|
37
|
+
Pin a release by appending its tag, for example `@v2.0.20`. Add `-l` to install only for the current project. Restart Pi after installing.
|
|
38
38
|
|
|
39
39
|
## Commands
|
|
40
40
|
|
|
@@ -67,10 +67,13 @@ The packaged `killeros` theme activates on TUI start. Compaction triggers by def
|
|
|
67
67
|
"autoCompaction": {
|
|
68
68
|
"enabled": true,
|
|
69
69
|
"percentRemaining": 15
|
|
70
|
-
}
|
|
70
|
+
},
|
|
71
|
+
"handoffMaxTokens": 8192
|
|
71
72
|
}
|
|
72
73
|
```
|
|
73
74
|
|
|
75
|
+
`handoffMaxTokens` caps the `/handoff` summary output at 8192 tokens by default; raise it when long sessions truncate the summary.
|
|
76
|
+
|
|
74
77
|
Completion sounds are off by default; change with `/notification` in TUI mode. The tab-title indicator requires a Nerd Font.
|
|
75
78
|
|
|
76
79
|
## Development
|
|
@@ -12,6 +12,8 @@ import { createKillerosSettingsStore } from "./settings.ts";
|
|
|
12
12
|
export const DEFAULT_AUTO_COMPACTION_PERCENT_REMAINING = 15;
|
|
13
13
|
export const AUTO_COMPACTION_MESSAGE_TYPE = "killeros-auto-compaction";
|
|
14
14
|
export const AUTO_COMPACTION_MESSAGE = "Continue the interrupted task from the compacted context.";
|
|
15
|
+
/** Pi's exact rejection when a session has no eligible history to summarize; an expected skip, not a failure. */
|
|
16
|
+
export const SESSION_TOO_SMALL_COMPACTION_ERROR = "Nothing to compact (session too small)";
|
|
15
17
|
|
|
16
18
|
export interface AutoCompactionPreference {
|
|
17
19
|
enabled: boolean;
|
|
@@ -23,6 +25,8 @@ export interface AutoCompactionGoalHandlers {
|
|
|
23
25
|
onRequested(): void;
|
|
24
26
|
onCompleted(ctx: ExtensionContext): void;
|
|
25
27
|
onFailed(ctx: ExtensionContext, error: unknown): void;
|
|
28
|
+
/** Recovers the goal paused for a request Pi rejected as session-too-small, without claiming success. */
|
|
29
|
+
onSkipped(ctx: ExtensionContext): void;
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
export interface AutoCompactionDependencies {
|
|
@@ -72,6 +76,11 @@ function reserveTokens(settings: Pick<CompactionSettings, "reserveTokens">): num
|
|
|
72
76
|
: 0;
|
|
73
77
|
}
|
|
74
78
|
|
|
79
|
+
/** Matches only Pi's exact rejection text on a real Error; near-matches and stringified values stay failures. */
|
|
80
|
+
export function isSessionTooSmallCompactionError(error: unknown): boolean {
|
|
81
|
+
return error instanceof Error && error.message === SESSION_TOO_SMALL_COMPACTION_ERROR;
|
|
82
|
+
}
|
|
83
|
+
|
|
75
84
|
/** Triggers at the stricter of the user's percentage and Pi's token reserve. */
|
|
76
85
|
export function shouldTriggerAutoCompaction(
|
|
77
86
|
usage: Pick<ContextUsage, "tokens" | "contextWindow"> | undefined,
|
|
@@ -116,13 +125,7 @@ export function registerAutoCompaction(
|
|
|
116
125
|
ctx.ui.notify(`Automatic compaction failed: ${errorMessage(error)}`, "error");
|
|
117
126
|
};
|
|
118
127
|
|
|
119
|
-
const finishFailure = (
|
|
120
|
-
ctx: ExtensionContext,
|
|
121
|
-
token: symbol,
|
|
122
|
-
goal: boolean,
|
|
123
|
-
error: unknown,
|
|
124
|
-
): void => {
|
|
125
|
-
if (!request || request.token !== token) return;
|
|
128
|
+
const finishFailure = (ctx: ExtensionContext, goal: boolean, error: unknown): void => {
|
|
126
129
|
request = undefined;
|
|
127
130
|
if (goal && dependencies.goal) {
|
|
128
131
|
try {
|
|
@@ -135,6 +138,33 @@ export function registerAutoCompaction(
|
|
|
135
138
|
notifyFailure(ctx, error);
|
|
136
139
|
};
|
|
137
140
|
|
|
141
|
+
/** Ends a request with Pi's expected eligibility rejection: silent, rearmed, and never a failure. */
|
|
142
|
+
const finishSkip = (ctx: ExtensionContext, goal: boolean): void => {
|
|
143
|
+
request = undefined;
|
|
144
|
+
armed = true;
|
|
145
|
+
if (!goal) return;
|
|
146
|
+
try {
|
|
147
|
+
dependencies.goal?.onSkipped(ctx);
|
|
148
|
+
} catch (callbackError) {
|
|
149
|
+
notifyFailure(ctx, callbackError);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/** Single entry point for rejected requests: classifies before any sanitization, stale tokens stay inert. */
|
|
154
|
+
const finishRequestError = (
|
|
155
|
+
ctx: ExtensionContext,
|
|
156
|
+
token: symbol,
|
|
157
|
+
goal: boolean,
|
|
158
|
+
error: unknown,
|
|
159
|
+
): void => {
|
|
160
|
+
if (!request || request.token !== token) return;
|
|
161
|
+
if (isSessionTooSmallCompactionError(error)) {
|
|
162
|
+
finishSkip(ctx, goal);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
finishFailure(ctx, goal, error);
|
|
166
|
+
};
|
|
167
|
+
|
|
138
168
|
pi.on("turn_end", (_event, ctx) => {
|
|
139
169
|
if (!supportedMode(ctx) || request) return;
|
|
140
170
|
|
|
@@ -179,7 +209,7 @@ export function registerAutoCompaction(
|
|
|
179
209
|
try {
|
|
180
210
|
dependencies.goal.onRequested();
|
|
181
211
|
} catch (error) {
|
|
182
|
-
finishFailure(ctx,
|
|
212
|
+
finishFailure(ctx, true, error);
|
|
183
213
|
return;
|
|
184
214
|
}
|
|
185
215
|
}
|
|
@@ -207,10 +237,10 @@ export function registerAutoCompaction(
|
|
|
207
237
|
notifyFailure(ctx, error);
|
|
208
238
|
}
|
|
209
239
|
},
|
|
210
|
-
onError: (error) =>
|
|
240
|
+
onError: (error) => finishRequestError(ctx, token, goal, error),
|
|
211
241
|
});
|
|
212
242
|
} catch (error) {
|
|
213
|
-
|
|
243
|
+
finishRequestError(ctx, token, goal, error);
|
|
214
244
|
}
|
|
215
245
|
});
|
|
216
246
|
|
package/killeros/footer.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { type ExtensionAPI, type ExtensionContext, type Theme, type ThemeColor } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { truncateToWidth, visibleWidth, type TUI } from "@earendil-works/pi-tui";
|
|
3
3
|
import { isCodexFastEnabled, subscribeCodexFast } from "./codex-fast-state.ts";
|
|
4
4
|
import { formatCwd, formatTime, formatTokens, padRight } from "./display.ts";
|
|
5
5
|
import { goalElapsedMilliseconds } from "./goals.ts";
|
package/killeros/goals.ts
CHANGED
|
@@ -624,7 +624,7 @@ function scheduleGoalContinuation(
|
|
|
624
624
|
}
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
-
/** Resumes the revision paused by automatic compaction
|
|
627
|
+
/** Resumes the revision paused by automatic compaction once both host callbacks settle and Pi reports an outcome. */
|
|
628
628
|
function finalizeAutomaticCompaction(
|
|
629
629
|
pi: ExtensionAPI,
|
|
630
630
|
runtime: GoalRuntime,
|
|
@@ -632,7 +632,8 @@ function finalizeAutomaticCompaction(
|
|
|
632
632
|
ctx: ExtensionContext,
|
|
633
633
|
): void {
|
|
634
634
|
const recovery = runtime.automaticCompaction;
|
|
635
|
-
if (!recovery
|
|
635
|
+
if (!recovery || recovery.outcome === "pending" || !recovery.turnSettled) return;
|
|
636
|
+
const skipped = recovery.outcome === "skipped";
|
|
636
637
|
runtime.automaticCompaction = undefined;
|
|
637
638
|
if (runtime.state?.status !== "paused"
|
|
638
639
|
|| runtime.state.revision !== recovery.pausedRevision
|
|
@@ -641,7 +642,9 @@ function finalizeAutomaticCompaction(
|
|
|
641
642
|
transitionGoal(pi, runtime, "resume", "active", undefined, { resetBlockedAudit: true });
|
|
642
643
|
} catch (error) {
|
|
643
644
|
runtime.persistenceRetryNeeded = true;
|
|
644
|
-
reportError(ctx,
|
|
645
|
+
reportError(ctx, skipped
|
|
646
|
+
? "Automatic compaction was skipped, but the goal could not be resumed"
|
|
647
|
+
: "Automatic compaction succeeded, but the goal could not be resumed", error);
|
|
645
648
|
return;
|
|
646
649
|
}
|
|
647
650
|
runtime.continuationScheduled = false;
|
|
@@ -656,7 +659,19 @@ function completeAutomaticCompaction(
|
|
|
656
659
|
ctx: ExtensionContext,
|
|
657
660
|
): void {
|
|
658
661
|
if (!runtime.automaticCompaction) return;
|
|
659
|
-
runtime.automaticCompaction.
|
|
662
|
+
runtime.automaticCompaction.outcome = "completed";
|
|
663
|
+
finalizeAutomaticCompaction(pi, runtime, initState, ctx);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/** Records Pi's expected session-too-small rejection and resumes without claiming compaction succeeded. */
|
|
667
|
+
function skipAutomaticCompaction(
|
|
668
|
+
pi: ExtensionAPI,
|
|
669
|
+
runtime: GoalRuntime,
|
|
670
|
+
initState: InitRuntime,
|
|
671
|
+
ctx: ExtensionContext,
|
|
672
|
+
): void {
|
|
673
|
+
if (!runtime.automaticCompaction) return;
|
|
674
|
+
runtime.automaticCompaction.outcome = "skipped";
|
|
660
675
|
finalizeAutomaticCompaction(pi, runtime, initState, ctx);
|
|
661
676
|
}
|
|
662
677
|
|
|
@@ -1247,6 +1262,7 @@ export function registerGoalSettlement(
|
|
|
1247
1262
|
onRequested(): void;
|
|
1248
1263
|
onCompleted(ctx: ExtensionContext): void;
|
|
1249
1264
|
onFailed(ctx: ExtensionContext, error: unknown): void;
|
|
1265
|
+
onSkipped(ctx: ExtensionContext): void;
|
|
1250
1266
|
} {
|
|
1251
1267
|
pi.on("agent_settled", (_event, ctx) => {
|
|
1252
1268
|
const wasGoalTurn = runtime.goalTurnInFlight;
|
|
@@ -1333,7 +1349,7 @@ export function registerGoalSettlement(
|
|
|
1333
1349
|
const paused = transitionGoal(pi, runtime, "pause", "paused");
|
|
1334
1350
|
runtime.automaticCompaction = {
|
|
1335
1351
|
pausedRevision: paused.revision,
|
|
1336
|
-
|
|
1352
|
+
outcome: "pending",
|
|
1337
1353
|
turnSettled: false,
|
|
1338
1354
|
};
|
|
1339
1355
|
} catch (error) {
|
|
@@ -1355,5 +1371,6 @@ export function registerGoalSettlement(
|
|
|
1355
1371
|
},
|
|
1356
1372
|
onCompleted: (ctx: ExtensionContext): void => completeAutomaticCompaction(pi, runtime, initState, ctx),
|
|
1357
1373
|
onFailed: (ctx: ExtensionContext, error: unknown): void => failAutomaticCompaction(pi, runtime, ctx, error),
|
|
1374
|
+
onSkipped: (ctx: ExtensionContext): void => skipAutomaticCompaction(pi, runtime, initState, ctx),
|
|
1358
1375
|
};
|
|
1359
1376
|
}
|
package/killeros/handoff.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { contentText } from "@earendil-works/pi-ai";
|
|
2
2
|
import { BorderedLoader, convertToLlm, type ExtensionAPI, type ExtensionCommandContext, serializeConversation, sessionEntryToContextMessages } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { reportError } from "./errors.ts";
|
|
3
|
+
import { errorMessage, reportError } from "./errors.ts";
|
|
4
4
|
import type { GoalRuntime } from "./runtime.ts";
|
|
5
|
+
import { createKillerosSettingsStore, type KillerosSettings } from "./settings.ts";
|
|
5
6
|
import { safeTerminalText } from "./safe-terminal-text.ts";
|
|
6
7
|
|
|
7
8
|
const HANDOFF_UNAVAILABLE = "/handoff is not available while an agent or /goal is running.";
|
|
9
|
+
/** Output-token budget with headroom for reasoning traces plus all ten sections. */
|
|
10
|
+
export const DEFAULT_HANDOFF_MAX_TOKENS = 8_192;
|
|
8
11
|
const HANDOFF_SECTIONS = [
|
|
9
12
|
"Objective",
|
|
10
13
|
"Current state",
|
|
@@ -69,6 +72,20 @@ function sessionName(sourceName: string | undefined, focus: string, document: st
|
|
|
69
72
|
return `${shortBase || "Handoff"} · handoff`;
|
|
70
73
|
}
|
|
71
74
|
|
|
75
|
+
/** Accepts only positive integers. */
|
|
76
|
+
function isPositiveInt(value: unknown): value is number {
|
|
77
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function positiveIntOr(value: unknown, fallback: number): number {
|
|
81
|
+
return isPositiveInt(value) ? value : fallback;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Resolves the summary budget: explicit option first, then killeros.json, then the default. */
|
|
85
|
+
export function resolveHandoffMaxTokens(settings: Readonly<KillerosSettings>, override?: number): number {
|
|
86
|
+
return positiveIntOr(override, positiveIntOr(settings.handoffMaxTokens, DEFAULT_HANDOFF_MAX_TOKENS));
|
|
87
|
+
}
|
|
88
|
+
|
|
72
89
|
/** Checks that the model returned every section needed to continue safely. */
|
|
73
90
|
function hasRequiredHandoffContent(document: string, focus: string): boolean {
|
|
74
91
|
if (focus && !document.includes(focus)) return false;
|
|
@@ -82,16 +99,16 @@ function hasRequiredHandoffContent(document: string, focus: string): boolean {
|
|
|
82
99
|
});
|
|
83
100
|
}
|
|
84
101
|
|
|
85
|
-
/** Generates
|
|
86
|
-
async function generateHandoffSummary(
|
|
102
|
+
/** Generates a handoff summary; throws named errors for truncation and provider failures. */
|
|
103
|
+
export async function generateHandoffSummary(
|
|
87
104
|
ctx: ExtensionCommandContext,
|
|
88
105
|
conversation: string,
|
|
89
106
|
focus: string,
|
|
90
|
-
signal?: AbortSignal,
|
|
107
|
+
options: { maxTokens: number; signal?: AbortSignal },
|
|
91
108
|
): Promise<string> {
|
|
92
109
|
if (!ctx.model) throw new Error("No current model is available");
|
|
93
110
|
|
|
94
|
-
signal?.throwIfAborted();
|
|
111
|
+
options.signal?.throwIfAborted();
|
|
95
112
|
|
|
96
113
|
const response = await ctx.modelRegistry.complete(ctx.model, {
|
|
97
114
|
systemPrompt: HANDOFF_SYSTEM_PROMPT,
|
|
@@ -101,10 +118,13 @@ async function generateHandoffSummary(
|
|
|
101
118
|
timestamp: Date.now(),
|
|
102
119
|
}],
|
|
103
120
|
}, {
|
|
104
|
-
maxTokens:
|
|
105
|
-
signal,
|
|
121
|
+
maxTokens: options.maxTokens,
|
|
122
|
+
signal: options.signal,
|
|
106
123
|
});
|
|
107
124
|
if (response.stopReason === "error") throw new Error(response.errorMessage || "Handoff summary failed");
|
|
125
|
+
if (response.stopReason === "length") {
|
|
126
|
+
throw new Error(`The handoff summary exceeded its ${options.maxTokens}-token output budget. Shorten the source session or raise the handoff token budget.`);
|
|
127
|
+
}
|
|
108
128
|
if (response.stopReason !== "stop") throw new Error("The handoff summary did not finish");
|
|
109
129
|
|
|
110
130
|
const summary = safeTerminalText(contentText(response.content)).trim();
|
|
@@ -113,7 +133,7 @@ async function generateHandoffSummary(
|
|
|
113
133
|
}
|
|
114
134
|
|
|
115
135
|
/** Registers the idle-only command that summarizes context into a child session. */
|
|
116
|
-
export function registerHandoff(pi: ExtensionAPI, goalRuntime: GoalRuntime): void {
|
|
136
|
+
export function registerHandoff(pi: ExtensionAPI, goalRuntime: GoalRuntime, handoffMaxTokens?: number): void {
|
|
117
137
|
pi.registerCommand("handoff", {
|
|
118
138
|
description: "Create a fresh session with a continuation handoff",
|
|
119
139
|
handler: async (args, ctx) => {
|
|
@@ -136,6 +156,16 @@ export function registerHandoff(pi: ExtensionAPI, goalRuntime: GoalRuntime): voi
|
|
|
136
156
|
const conversation = serializeConversation(convertToLlm(messages));
|
|
137
157
|
if (!conversation.trim()) throw new Error("No usable session context is available");
|
|
138
158
|
focus = safeTerminalText(args).trim();
|
|
159
|
+
let maxTokens = handoffMaxTokens;
|
|
160
|
+
if (!isPositiveInt(maxTokens)) {
|
|
161
|
+
let settings: KillerosSettings = {};
|
|
162
|
+
try {
|
|
163
|
+
settings = createKillerosSettingsStore().load();
|
|
164
|
+
} catch (error) {
|
|
165
|
+
ctx.ui.notify(`killeros.json could not be read; using the default handoff budget: ${errorMessage(error)}`, "warning");
|
|
166
|
+
}
|
|
167
|
+
maxTokens = resolveHandoffMaxTokens(settings);
|
|
168
|
+
}
|
|
139
169
|
const generation = ctx.mode === "tui"
|
|
140
170
|
? await ctx.ui.custom<HandoffGenerationResult>((tui, theme, _keybindings, done) => {
|
|
141
171
|
const loader = new BorderedLoader(tui, theme, "Generating handoff...");
|
|
@@ -146,12 +176,12 @@ export function registerHandoff(pi: ExtensionAPI, goalRuntime: GoalRuntime): voi
|
|
|
146
176
|
done(result);
|
|
147
177
|
};
|
|
148
178
|
loader.onAbort = () => finish({ kind: "cancelled" });
|
|
149
|
-
generateHandoffSummary(ctx, conversation, focus, loader.signal)
|
|
179
|
+
generateHandoffSummary(ctx, conversation, focus, { maxTokens, signal: loader.signal })
|
|
150
180
|
.then((summary) => finish({ kind: "summary", summary }))
|
|
151
181
|
.catch((error: unknown) => finish({ kind: "error", error }));
|
|
152
182
|
return loader;
|
|
153
183
|
})
|
|
154
|
-
: { kind: "summary", summary: await generateHandoffSummary(ctx, conversation, focus) } as const;
|
|
184
|
+
: { kind: "summary", summary: await generateHandoffSummary(ctx, conversation, focus, { maxTokens }) } as const;
|
|
155
185
|
if (generation.kind === "cancelled") {
|
|
156
186
|
ctx.ui.notify("Handoff cancelled", "info");
|
|
157
187
|
return;
|
package/killeros/hooks.ts
CHANGED
|
@@ -261,7 +261,7 @@ export function executeHook(
|
|
|
261
261
|
if (signal?.aborted) {
|
|
262
262
|
return Promise.resolve({ code: 130, stdout: "", stderr: "", timedOut: false, cancelled: true, exitUnconfirmed: false });
|
|
263
263
|
}
|
|
264
|
-
let child;
|
|
264
|
+
let child: HookChildProcess;
|
|
265
265
|
try {
|
|
266
266
|
child = spawnProcess(command, {
|
|
267
267
|
cwd,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { closeSync,
|
|
1
|
+
import { closeSync, openSync, readSync } from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { StringDecoder } from "node:string_decoder";
|
|
5
|
-
import {
|
|
6
|
-
import { CONFIG_DIR_NAME, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
7
6
|
import type { InitRuntime } from "./runtime.ts";
|
|
8
7
|
|
|
9
8
|
const PERSONAL_INSTRUCTIONS_FILE = "AGENTS.local.md";
|
package/killeros/question.ts
CHANGED
package/killeros/runtime.ts
CHANGED
|
@@ -82,9 +82,12 @@ export type GoalState = GoalStateCommon & (
|
|
|
82
82
|
}
|
|
83
83
|
);
|
|
84
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
|
+
|
|
85
88
|
export interface AutomaticGoalCompaction {
|
|
86
89
|
pausedRevision: number;
|
|
87
|
-
|
|
90
|
+
outcome: AutomaticGoalCompactionOutcome;
|
|
88
91
|
turnSettled: boolean;
|
|
89
92
|
}
|
|
90
93
|
|
package/killeros/shell-ui.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "killeros",
|
|
3
|
-
"version": "2.0.
|
|
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",
|