killeros 1.5.4 → 1.5.5
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 +17 -0
- package/README.md +5 -5
- package/killeros/context-compaction.ts +64 -16
- package/killeros/runtime.ts +1 -1
- package/killeros/subagents.ts +1429 -1290
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to KillerOS are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.5.5] - 2026-08-05
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Added live background subagent progress with queued, running, and completed states plus per-child and batch usage.
|
|
12
|
+
- Added inline subagent roles for single, parallel, and chain spawns, bounded their tools to those active for the parent, and kept them scoped to one non-resumable spawn.
|
|
13
|
+
- Added `message` as a single-spawn task alias and visible fallback to `worker` for unknown named roles.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Replaced default deterministic context compaction with model-backed summaries at 40% remaining context, preserved manual focus prompts, and disclosed deterministic fallback after model retries fail.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Warned when a settled or failed background subagent handoff cannot reach the parent and pointed users to `list` and `collect` for recovery.
|
|
22
|
+
- Overrode vulnerable transitive `brace-expansion` and `undici` versions with patched releases in the development and CI install tree.
|
|
23
|
+
|
|
7
24
|
## [1.5.4] - 2026-08-04
|
|
8
25
|
|
|
9
26
|
### Added
|
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ Add `-l` to either command for a project-only install. Restart Pi after installi
|
|
|
45
45
|
- Coral Spark activity indicator with Claude-adjacent verbs that advance between agent runs and a quiet hidden-thinking label
|
|
46
46
|
- Framed multiline editor with Shift+Enter support
|
|
47
47
|
- Responsive footer with polished model/provider identity, plain-language context, and active goal state remaining; reasoning, Git branch, elapsed time, cost, and path cut down by available width
|
|
48
|
-
- Automatic
|
|
48
|
+
- Automatic model-backed context compaction at 40% remaining; active goals continue after the saved summary
|
|
49
49
|
- `/variants` selector and direct reasoning-level arguments
|
|
50
50
|
- Codex-style `/goal` for durable long-running objectives with pause, resume, edit, clear, automatic continuation, and explicit completion
|
|
51
51
|
- Pi-native `subagent` tool with named, inspectable child threads, Markdown roles, explicit read/write boundaries, parent controls, natural completion, and cancellation propagation
|
|
@@ -99,11 +99,11 @@ KillerOS ships `planner`, `reviewer`, `scout`, and `security` as read-only roles
|
|
|
99
99
|
|
|
100
100
|
The default `agentScope: "user"` uses bundled and personal roles. Use `"project"` or `"both"` to opt into trusted project roles; a selected project override requires interactive confirmation. Role frontmatter requires `name`, `description`, `access`, and an explicit `tools` list. Optional fields are `model`, `thinking`, and `timeoutMs`. Every bundled role shows `model: inherit` and `thinking: inherit` as editable placeholders. Replace them with an available `provider/model` and a separate thinking level when you want to pin a role; `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max` are checked against that model’s supported capabilities.
|
|
101
101
|
|
|
102
|
-
The tool supports a single `agent`
|
|
102
|
+
The tool supports a single `agent` plus `task` or its `message` alias, parallel `tasks`, or a sequential `chain` whose task text may include `{previous}`. `agent` may be a role name or an inline `{name, description, access, tools}` role for one spawn. Inline tools must be active for the parent, and inline role settings are not saved or resumable. Each task may set a `name`; names are unique within the parent session without regard to case and are passed to child Pi as `--name`. Read-only-only batches run concurrently, up to four at a time. Batches with write-capable roles are serialized in the shared worktree with one shared slot. Reader-only batches reject `writerConcurrency` because it does not apply. A call can also set `model` and `thinking` for every task, overriding role settings; use `inherit` to fall back to each role and then the active parent model.
|
|
103
103
|
|
|
104
104
|
| Action | Required fields | Allowed optional fields |
|
|
105
105
|
|---|---|---|
|
|
106
|
-
| omitted / `spawn` single | `agent`, `task` | `name`, `model`, `thinking`, `agentScope` |
|
|
106
|
+
| omitted / `spawn` single | `agent`, one of `task` or `message` | `name`, `model`, `thinking`, `agentScope` |
|
|
107
107
|
| omitted / `spawn` parallel | `tasks` | per-task `name`, `writerConcurrency`, `model`, `thinking`, `agentScope` |
|
|
108
108
|
| omitted / `spawn` chain | `chain` | per-task `name`, `model`, `thinking`, `agentScope` |
|
|
109
109
|
| `list` | none | none |
|
|
@@ -116,7 +116,7 @@ The tool supports a single `agent` + `task`, parallel `tasks`, or a sequential `
|
|
|
116
116
|
| `resume` | `threadId` | `task` |
|
|
117
117
|
| `close` | `threadId` | none |
|
|
118
118
|
|
|
119
|
-
The three spawn shapes cannot be mixed.
|
|
119
|
+
The three spawn shapes cannot be mixed. On a single spawn, `message` aliases `task`; supplying both is invalid. With `action: "steer"`, `message` remains required, and other lifecycle actions reject it. An unknown role name falls back to `worker` on a new spawn and reports that choice in the tool output. The `wait` action defaults to all queued or active children, waits up to 30 seconds by default, and never stops a child when it times out. The `resume` action keeps the same thread ID, name, session ID, and session directory and increments `attempt`; it requires the original named role and rejects inline roles. KillerOS rejects malformed requests before role discovery, project confirmation, thread creation, or child launch. The TUI shows a parallel or shared-pool schedule only after shape validation; malformed calls show `invalid request` instead of queued work. For example:
|
|
120
120
|
|
|
121
121
|
```json
|
|
122
122
|
{"agent":"reviewer","task":"Review the change","name":"auth-audit","model":"provider/model","thinking":"high"}
|
|
@@ -170,7 +170,7 @@ KillerOS activates its packaged `killeros` theme when a TUI session starts. Tool
|
|
|
170
170
|
|
|
171
171
|
KillerOS displays session costs in USD. The footer uses Pi's human-readable model name when available, keeps the provider visually secondary, and renders context as `percent left (tokens)` without a progress bar. When a goal exists, the footer adds its active time or terminal state; at narrow widths, context pressure and goal state take priority.
|
|
172
172
|
|
|
173
|
-
KillerOS checks context after each agent turn. At
|
|
173
|
+
KillerOS checks context after each agent turn. At 40% remaining, it starts Pi's model-backed compaction after the current run settles, so the active turn is not aborted. Manual `/compact` uses the same model path and keeps custom focus instructions. If model compaction is unavailable or exhausts its retries, KillerOS uses the disclosed deterministic fallback and warns that repeated compaction can reduce accuracy.
|
|
174
174
|
|
|
175
175
|
For trusted projects, KillerOS loads `AGENTS.local.md` after Pi's shared repository context. A one-line `@path` or `@~/path` file imports personal guidance from another location.
|
|
176
176
|
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
ExtensionContext,
|
|
4
4
|
SessionBeforeCompactEvent,
|
|
5
5
|
} from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { compact, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
6
7
|
import { pauseGoalAfterFailure } from "./goals.ts";
|
|
7
8
|
import type { CompactionRuntime, GoalRuntime } from "./runtime.ts";
|
|
8
9
|
|
|
@@ -21,7 +22,9 @@ const MAX_CONTEXT_ITEMS = 80;
|
|
|
21
22
|
const MAX_FILES = 40;
|
|
22
23
|
const MAX_PREVIOUS_SUMMARY = 6_000;
|
|
23
24
|
const MAX_CUSTOM_INSTRUCTIONS = 4_000;
|
|
24
|
-
const COMPACTION_STATE_TIMEOUT_MS = 60_000;
|
|
25
|
+
const COMPACTION_STATE_TIMEOUT_MS = 5 * 60_000;
|
|
26
|
+
const DETERMINISTIC_FALLBACK_PREFIX = "This summary was produced deterministically without model understanding. Verify its details before relying on it.";
|
|
27
|
+
const COMPACTION_ACCURACY_WARNING = "Long threads and multiple compactions can cause the model to be less accurate. Start a new thread when possible to keep threads small and targeted.";
|
|
25
28
|
|
|
26
29
|
type NotificationLevel = Parameters<ExtensionContext["ui"]["notify"]>[1];
|
|
27
30
|
|
|
@@ -231,6 +234,8 @@ function buildStructuredSummary(
|
|
|
231
234
|
}
|
|
232
235
|
|
|
233
236
|
return [
|
|
237
|
+
DETERMINISTIC_FALLBACK_PREFIX,
|
|
238
|
+
"",
|
|
234
239
|
"# KillerOS Compaction Summary",
|
|
235
240
|
"",
|
|
236
241
|
...(preparation.previousSummary?.trim()
|
|
@@ -256,6 +261,38 @@ function buildStructuredSummary(
|
|
|
256
261
|
].join("\n");
|
|
257
262
|
}
|
|
258
263
|
|
|
264
|
+
async function buildModelSummary(
|
|
265
|
+
event: SessionBeforeCompactEvent,
|
|
266
|
+
ctx: ExtensionContext,
|
|
267
|
+
) {
|
|
268
|
+
const model = ctx.model;
|
|
269
|
+
if (!model) throw new Error("No model is available for compaction");
|
|
270
|
+
|
|
271
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
272
|
+
if (!auth.ok) throw new Error(auth.error);
|
|
273
|
+
|
|
274
|
+
const provider = ctx.modelRegistry.getProvider(model.provider);
|
|
275
|
+
const streamFn = provider
|
|
276
|
+
? provider.streamSimple.bind(provider)
|
|
277
|
+
: undefined;
|
|
278
|
+
const retry = SettingsManager.create(ctx.cwd, undefined, {
|
|
279
|
+
projectTrusted: ctx.isProjectTrusted(),
|
|
280
|
+
}).getRetrySettings();
|
|
281
|
+
|
|
282
|
+
return compact(
|
|
283
|
+
event.preparation,
|
|
284
|
+
model,
|
|
285
|
+
auth.apiKey,
|
|
286
|
+
auth.headers,
|
|
287
|
+
event.customInstructions,
|
|
288
|
+
event.signal,
|
|
289
|
+
ctx.thinkingLevel,
|
|
290
|
+
streamFn,
|
|
291
|
+
auth.env,
|
|
292
|
+
retry,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
259
296
|
function exactPercentRemaining(ctx: ExtensionContext): number | null {
|
|
260
297
|
let usage: ReturnType<ExtensionContext["getContextUsage"]>;
|
|
261
298
|
try {
|
|
@@ -293,7 +330,7 @@ function isCurrentCompaction(runtime: CompactionRuntime, operationId: number): b
|
|
|
293
330
|
}
|
|
294
331
|
|
|
295
332
|
function armCompactionTimeout(runtime: CompactionRuntime, operationId: number): void {
|
|
296
|
-
// ponytail: recover stale state after
|
|
333
|
+
// ponytail: recover stale state after five minutes because Pi exposes no failed-compaction extension event; replace with that event if Pi adds one.
|
|
297
334
|
const timer = setTimeout(() => {
|
|
298
335
|
if (!isCurrentCompaction(runtime, operationId)) return;
|
|
299
336
|
const onFailure = compactionFailureHandlers.get(runtime);
|
|
@@ -519,7 +556,7 @@ export function registerContextCompaction(
|
|
|
519
556
|
requestAutomaticCompaction(pi, ctx, compactionRuntime, goalRuntime, percentRemaining);
|
|
520
557
|
});
|
|
521
558
|
|
|
522
|
-
pi.on("session_before_compact", (event, ctx) => {
|
|
559
|
+
pi.on("session_before_compact", async (event, ctx) => {
|
|
523
560
|
const expectedAutomaticHook = compactionRuntime.automaticCompactionAwaitingHook;
|
|
524
561
|
const sessionGeneration = compactionRuntime.sessionGeneration;
|
|
525
562
|
const operationId = markCompactionInFlight(
|
|
@@ -544,23 +581,34 @@ export function registerContextCompaction(
|
|
|
544
581
|
},
|
|
545
582
|
);
|
|
546
583
|
if (operationId === null) return { cancel: true };
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
:
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
584
|
+
|
|
585
|
+
try {
|
|
586
|
+
return { compaction: await buildModelSummary(event, ctx) };
|
|
587
|
+
} catch (error) {
|
|
588
|
+
if (event.signal.aborted) return { cancel: true };
|
|
589
|
+
notify(ctx, `Model compaction failed: ${errorMessage(error)}. Using the deterministic fallback.`, "warning");
|
|
590
|
+
const goalObjective = goalRuntime.state?.status === "active"
|
|
591
|
+
? goalRuntime.state.objective
|
|
592
|
+
: undefined;
|
|
593
|
+
const summary = buildStructuredSummary(event.preparation, goalObjective, event.customInstructions);
|
|
594
|
+
|
|
595
|
+
return {
|
|
596
|
+
compaction: {
|
|
597
|
+
summary,
|
|
598
|
+
firstKeptEntryId: event.preparation.firstKeptEntryId,
|
|
599
|
+
tokensBefore: event.preparation.tokensBefore,
|
|
600
|
+
details: { killerosDeterministicFallback: true },
|
|
601
|
+
},
|
|
602
|
+
};
|
|
603
|
+
}
|
|
559
604
|
});
|
|
560
605
|
|
|
561
|
-
pi.on("session_compact", () => {
|
|
606
|
+
pi.on("session_compact", (event, ctx) => {
|
|
562
607
|
resetCompactionState(compactionRuntime);
|
|
563
608
|
compactionFailureHandlers.delete(compactionRuntime);
|
|
564
609
|
compactionRuntime.lastCompactionAt = Date.now();
|
|
610
|
+
if (asRecord(event.compactionEntry.details)?.killerosDeterministicFallback === true) {
|
|
611
|
+
notify(ctx, COMPACTION_ACCURACY_WARNING, "warning");
|
|
612
|
+
}
|
|
565
613
|
});
|
|
566
614
|
}
|