killeros 3.0.1 → 3.0.2
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 +18 -0
- package/README.md +23 -3
- package/killeros/auto-compaction.ts +20 -9
- package/killeros/hooks.ts +24 -3
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ All notable changes to KillerOS are documented here. Releases use New Features f
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [3.0.2] - 2026-10-05
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Raised the minimum supported Pi version and locked Pi development packages to 1.0.2. Compatibility CI now tests the exact 1.0.2 minimum and latest stable 1.x; users on older Pi versions must upgrade.
|
|
12
|
+
- Documented Pi's native thinking-level sampling configuration and trusted-project MCP overrides without adding KillerOS commands or settings.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Added real Pi contract coverage for capacity-error recovery within one logical goal turn, with task receipts and completion sounds deferred until settlement.
|
|
17
|
+
- Verified that native OpenAI fast mode preserves thinking-level sampling overrides across API-key and ChatGPT subscription requests, extension reloads, and provider retries.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Kept POSIX process-group cleanup pending after a timed-out or cancelled hook's shell exits, preserving forced termination and reporting bounded uncertainty unless group exit is confirmed. Fixes #42.
|
|
22
|
+
- Preserved goal-request ownership through settlement so automatic compaction cannot restart completed or blocked goals as ordinary work. Active-goal recovery, later ordinary requests, and compaction error reporting remain supported. Fixes #43.
|
|
23
|
+
- Corrected the process-tree timeout fixture's marker argument and delayed descendant writes beyond cleanup. Added a real cleanup-disabled control that detects surviving descendants and checks explicit uncertainty. Fixes #44.
|
|
24
|
+
|
|
7
25
|
## [3.0.1] - 2026-10-02
|
|
8
26
|
|
|
9
27
|
### Changed
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ A TypeScript extension for the [Pi coding agent](https://github.com/earendil-wor
|
|
|
19
19
|
## Requirements
|
|
20
20
|
|
|
21
21
|
- Node.js 22.19.0+
|
|
22
|
-
- Pi 0.
|
|
22
|
+
- Pi 1.0.2 or later below 2.0.0
|
|
23
23
|
- An interactive TUI session for the custom header, editor, footer, and `question`
|
|
24
24
|
|
|
25
25
|
## Install
|
|
@@ -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 `@v3.0.
|
|
37
|
+
Pin a release by appending its tag, for example `@v3.0.2`. Add `-l` to install only for the current project. Restart Pi after installing.
|
|
38
38
|
|
|
39
39
|
## Commands
|
|
40
40
|
|
|
@@ -103,7 +103,7 @@ A direct quoted path-shaped file target binds silent file proof. For an extensio
|
|
|
103
103
|
/goal Fix `killeros/footer.ts`, verified by npm test
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
Bare quoted prose remains model-reported. KillerOS captures the file baseline at goal start and only completes when the file is created or changed. A normal response never continues a goal by itself: the agent must record `continue`, `complete`, or a blocker decision through `killeros_goal_update`. Repeated continuation reports and unavailable goal tools pause the goal. New goals pause after 20 turns without warning. An explicit `/goal resume` on an exhausted goal grants another 20 turns; compaction recovery never grants turns. Same-turn recovery requires an actual interruption; successful or skipped compaction cannot restart a normally stopped goal without a decision. Restored goals keep their persisted limit.
|
|
106
|
+
Bare quoted prose remains model-reported. KillerOS captures the file baseline at goal start and only completes when the file is created or changed. A normal response never continues a goal by itself: the agent must record `continue`, `complete`, or a blocker decision through `killeros_goal_update`. Repeated continuation reports and unavailable goal tools pause the goal. New goals pause after 20 turns without warning. An explicit `/goal resume` on an exhausted goal grants another 20 turns; compaction recovery never grants turns. Same-turn recovery requires an actual interruption; successful or skipped compaction cannot restart a normally stopped goal without a decision. A request that completes or blocks a goal remains goal-owned through settlement, so automatic compaction cannot restart it as ordinary work. Later ordinary requests still support compaction continuation. Restored goals keep their persisted limit.
|
|
107
107
|
|
|
108
108
|
Session replacement, reload, and committed tree navigation discard unfinished task receipts. Late receipt results do not write or notify through the old session context or attach to a different branch. Cancelled navigation preserves the pending receipt.
|
|
109
109
|
|
|
@@ -113,6 +113,24 @@ Failed Git metadata reads keep the footer's last successful file counts and mark
|
|
|
113
113
|
|
|
114
114
|
Completion sounds are off by default; change with `/notification` in TUI mode. The tab-title indicator requires a Nerd Font.
|
|
115
115
|
|
|
116
|
+
### Pi model and MCP settings
|
|
117
|
+
|
|
118
|
+
Use Pi's native `/thinking` selector for reasoning levels. Pi 1.0.2 can also apply different sampling parameters for each level through `samplingParamsByThinkingLevel` in `models.json`. For example, a model entry can contain:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"samplingParams": { "temperature": 1.0, "top_p": 0.95 },
|
|
123
|
+
"samplingParamsByThinkingLevel": {
|
|
124
|
+
"off": { "temperature": 0.7, "top_p": 0.8 },
|
|
125
|
+
"high": { "temperature": 0.6 }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Only configure parameters the endpoint accepts. These settings apply to `openai-completions`, `openai-responses`, and `azure-openai-responses`, not legacy `openai-codex`. Missing levels inherit model defaults; request-level sampling parameters take precedence. `/codex-fast` preserves sampling parameters when it adds the priority service tier. See Pi's [sampling configuration](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/models.md#configure-sampling-by-thinking-level) for the full file format and merge rules.
|
|
131
|
+
|
|
132
|
+
Use Pi's `/mcp` command and `.pi/mcp.json` to enable, disable, or change the exposure of a user-level MCP server for a trusted project without copying its credentials. See [project MCP overrides](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/mcp.md#configure-servers). KillerOS does not manage MCP configuration.
|
|
133
|
+
|
|
116
134
|
## Development
|
|
117
135
|
|
|
118
136
|
Strict TypeScript throughout. Tests run on Node's built-in test runner:
|
|
@@ -137,6 +155,8 @@ Releases go through CI on `main`; do not push version tags manually. The package
|
|
|
137
155
|
|
|
138
156
|
Pi extensions run with your user permissions. Review the source before installing globally. Hook commands run only for projects Pi marks as trusted; check `.pi/killeros-hooks.json` before enabling project trust. KillerOS accepts that configuration only as a regular, non-linked file no larger than 64 KiB in the project's real `.pi` directory.
|
|
139
157
|
|
|
158
|
+
On POSIX systems, timed-out or cancelled hooks receive `SIGTERM`, followed by `SIGKILL` if cleanup remains pending. Shell exit alone does not confirm that the original process group stopped. Cleanup uses a two-second window and reports uncertainty if group exit cannot be confirmed.
|
|
159
|
+
|
|
140
160
|
Handoff validation rejects recognized credential assignments in plain text, Markdown lists, bold labels, and inline-code labels before creating a destination session. This pattern check cannot detect every secret or personally identifying value. Rejected values are not included in the error notification.
|
|
141
161
|
|
|
142
162
|
## License
|
|
@@ -121,12 +121,14 @@ export function registerAutoCompaction(
|
|
|
121
121
|
const loadPreference = dependencies.loadPreference ?? (() => defaultPreference(settingsStore));
|
|
122
122
|
const getCompactionSettings = dependencies.getCompactionSettings ?? defaultCompactionSettings;
|
|
123
123
|
let request: AutoCompactionRequest | undefined;
|
|
124
|
+
let goalRequestOwned = false;
|
|
124
125
|
let armed = true;
|
|
125
126
|
let rearmOnNextLogicalRequest = false;
|
|
126
127
|
let settingsErrorReported = false;
|
|
127
128
|
|
|
128
129
|
const resetForLifecycle = (): void => {
|
|
129
130
|
request = undefined;
|
|
131
|
+
goalRequestOwned = false;
|
|
130
132
|
armed = true;
|
|
131
133
|
rearmOnNextLogicalRequest = false;
|
|
132
134
|
settingsErrorReported = false;
|
|
@@ -239,6 +241,11 @@ export function registerAutoCompaction(
|
|
|
239
241
|
}
|
|
240
242
|
};
|
|
241
243
|
|
|
244
|
+
pi.on("agent_start", (_event, ctx) => {
|
|
245
|
+
// Terminal decisions clear active status before turn_end, but still own this run.
|
|
246
|
+
goalRequestOwned = dependencies.goal?.isActive(ctx) === true;
|
|
247
|
+
});
|
|
248
|
+
|
|
242
249
|
pi.on("turn_end", (_event, ctx) => {
|
|
243
250
|
if (!supportedMode(ctx) || request) return;
|
|
244
251
|
|
|
@@ -277,7 +284,8 @@ export function registerAutoCompaction(
|
|
|
277
284
|
|
|
278
285
|
armed = false;
|
|
279
286
|
const token = Symbol();
|
|
280
|
-
const
|
|
287
|
+
const activeGoal = dependencies.goal?.isActive(ctx) === true;
|
|
288
|
+
const goal = goalRequestOwned || activeGoal;
|
|
281
289
|
request = {
|
|
282
290
|
phase: "compacting",
|
|
283
291
|
goal,
|
|
@@ -285,7 +293,7 @@ export function registerAutoCompaction(
|
|
|
285
293
|
turnSettled: false,
|
|
286
294
|
compactionCompleted: false,
|
|
287
295
|
};
|
|
288
|
-
if (
|
|
296
|
+
if (activeGoal && dependencies.goal) {
|
|
289
297
|
try {
|
|
290
298
|
dependencies.goal.onRequested();
|
|
291
299
|
} catch (error) {
|
|
@@ -299,26 +307,29 @@ export function registerAutoCompaction(
|
|
|
299
307
|
onComplete: () => {
|
|
300
308
|
if (request?.phase !== "compacting" || request.token !== token) return;
|
|
301
309
|
rearmOnNextLogicalRequest = preference.percentRemaining === 100;
|
|
302
|
-
if (goal
|
|
310
|
+
if (goal) {
|
|
303
311
|
request = undefined;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
312
|
+
if (activeGoal && dependencies.goal) {
|
|
313
|
+
try {
|
|
314
|
+
dependencies.goal.onCompleted(ctx);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
notifyFailure(ctx, error);
|
|
317
|
+
}
|
|
308
318
|
}
|
|
309
319
|
return;
|
|
310
320
|
}
|
|
311
321
|
request.compactionCompleted = true;
|
|
312
322
|
finalizeOrdinaryContinuation(ctx, token);
|
|
313
323
|
},
|
|
314
|
-
onError: (error) => finishRequestError(ctx, token,
|
|
324
|
+
onError: (error) => finishRequestError(ctx, token, activeGoal, error),
|
|
315
325
|
});
|
|
316
326
|
} catch (error) {
|
|
317
|
-
finishRequestError(ctx, token,
|
|
327
|
+
finishRequestError(ctx, token, activeGoal, error);
|
|
318
328
|
}
|
|
319
329
|
});
|
|
320
330
|
|
|
321
331
|
pi.on("agent_settled", (_event, ctx) => {
|
|
332
|
+
goalRequestOwned = false;
|
|
322
333
|
if (request?.phase === "continuation-dispatched") {
|
|
323
334
|
request = undefined;
|
|
324
335
|
notifyFailure(ctx, new Error("continuation was not accepted"));
|
package/killeros/hooks.ts
CHANGED
|
@@ -236,6 +236,17 @@ function terminateHookProcess(child: HookChildProcess, force: boolean): void {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
function hookProcessGroupExited(child: HookChildProcess): boolean {
|
|
240
|
+
if (!child.pid) return false;
|
|
241
|
+
try {
|
|
242
|
+
process.kill(-child.pid, 0);
|
|
243
|
+
return false;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
// Permission and other failures are uncertainty, not proof of exit.
|
|
246
|
+
return error instanceof Error && "code" in error && error.code === "ESRCH";
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
239
250
|
export function executeHook(options: ExecuteHookOptions): Promise<HookExecutionResult>;
|
|
240
251
|
/** @deprecated Use the object-argument form. The positional adapter will be removed in the next major release. */
|
|
241
252
|
export function executeHook(
|
|
@@ -297,6 +308,8 @@ export function executeHook(
|
|
|
297
308
|
let forceTimer: NodeJS.Timeout | undefined;
|
|
298
309
|
let settleTimer: NodeJS.Timeout | undefined;
|
|
299
310
|
let windowsCleanupPending = false;
|
|
311
|
+
let posixCleanupPending = false;
|
|
312
|
+
let shellClosed = false;
|
|
300
313
|
const finish = (code: number, exitUnconfirmed = false): void => {
|
|
301
314
|
if (completed) return;
|
|
302
315
|
completed = true;
|
|
@@ -334,10 +347,15 @@ export function executeHook(
|
|
|
334
347
|
}, 2_000);
|
|
335
348
|
return;
|
|
336
349
|
}
|
|
350
|
+
posixCleanupPending = process.platform !== "win32" && child.pid !== undefined;
|
|
337
351
|
forceTimer = setTimeout(() => {
|
|
338
352
|
if (completed) return;
|
|
339
353
|
terminateHookProcess(child, true);
|
|
340
|
-
|
|
354
|
+
if (completed) return;
|
|
355
|
+
settleTimer = setTimeout(() => {
|
|
356
|
+
const confirmed = posixCleanupPending && shellClosed && hookProcessGroupExited(child);
|
|
357
|
+
finish(terminationCode(), !confirmed);
|
|
358
|
+
}, 1_000);
|
|
341
359
|
}, 1_000);
|
|
342
360
|
terminateHookProcess(child, false);
|
|
343
361
|
};
|
|
@@ -349,10 +367,13 @@ export function executeHook(
|
|
|
349
367
|
if (child.stderr instanceof EventEmitter) child.stderr.on("error", captureStreamError);
|
|
350
368
|
child.on("error", (error) => {
|
|
351
369
|
appendBounded(stderr, error.message);
|
|
352
|
-
if (!windowsCleanupPending) finish(termination ? terminationCode() : 1);
|
|
370
|
+
if (!windowsCleanupPending && !posixCleanupPending) finish(termination ? terminationCode() : 1);
|
|
353
371
|
});
|
|
354
372
|
child.once("close", (code) => {
|
|
355
|
-
|
|
373
|
+
shellClosed = true;
|
|
374
|
+
if (!windowsCleanupPending && (!posixCleanupPending || hookProcessGroupExited(child))) {
|
|
375
|
+
finish(termination ? terminationCode() : code ?? 1);
|
|
376
|
+
}
|
|
356
377
|
});
|
|
357
378
|
if (completed) return;
|
|
358
379
|
const boundedTimeoutMs = Number.isNaN(timeoutMs)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "killeros",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "TUI, goals, and workflow automation for the Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -45,16 +45,16 @@
|
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@earendil-works/pi-ai": ">=0.
|
|
49
|
-
"@earendil-works/pi-coding-agent": ">=0.
|
|
50
|
-
"@earendil-works/pi-tui": ">=0.
|
|
48
|
+
"@earendil-works/pi-ai": ">=1.0.2 <2",
|
|
49
|
+
"@earendil-works/pi-coding-agent": ">=1.0.2 <2",
|
|
50
|
+
"@earendil-works/pi-tui": ">=1.0.2 <2",
|
|
51
51
|
"typebox": ">=1.1.38 <2"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@earendil-works/pi-ai": "1.0.
|
|
55
|
-
"@earendil-works/pi-coding-agent": "1.0.
|
|
56
|
-
"@earendil-works/pi-server": "1.0.
|
|
57
|
-
"@earendil-works/pi-tui": "1.0.
|
|
54
|
+
"@earendil-works/pi-ai": "1.0.2",
|
|
55
|
+
"@earendil-works/pi-coding-agent": "1.0.2",
|
|
56
|
+
"@earendil-works/pi-server": "1.0.2",
|
|
57
|
+
"@earendil-works/pi-tui": "1.0.2",
|
|
58
58
|
"@types/node": "24.12.4",
|
|
59
59
|
"eslint": "^10.9.1",
|
|
60
60
|
"typebox": "1.3.20",
|