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 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 non-destructive context compaction at 30% remaining with a structured handoff; active goals continue after the saved summary
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` + `task`, parallel `tasks`, or a sequential `chain` whose task text may include `{previous}`. 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.
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. The `message` field is only valid with `action: "steer"`, and lifecycle actions reject spawn fields. 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`. 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:
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 30% remaining, it starts Pi compaction after the current run settles, so the active turn is not aborted. Manual `/compact` remains available, and the compaction summary keeps the goal, progress, decisions, next steps, and changed files.
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 60s because Pi exposes no failed-compaction extension event; replace with that event if Pi adds one.
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
- const goalObjective = goalRuntime.state?.status === "active"
548
- ? goalRuntime.state.objective
549
- : undefined;
550
- const summary = buildStructuredSummary(event.preparation, goalObjective, event.customInstructions);
551
-
552
- return {
553
- compaction: {
554
- summary,
555
- firstKeptEntryId: event.preparation.firstKeptEntryId,
556
- tokensBefore: event.preparation.tokensBefore,
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
  }
@@ -81,6 +81,6 @@ export function createCompactionRuntime(): CompactionRuntime {
81
81
  automaticCompactionPending: false,
82
82
  compactionOperationId: 0,
83
83
  sessionGeneration: 0,
84
- thresholdPercent: 30,
84
+ thresholdPercent: 40,
85
85
  };
86
86
  }