killeros 1.4.1 → 1.4.3

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
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to KillerOS are documented here.
4
4
 
5
+ ## [1.4.3] - 2026-08-01
6
+
7
+ ### Fixed
8
+
9
+ - Added child-runtime tool budgets for read-only roles, with a soft finalization nudge and hard blocking for read and web tools after 32 calls.
10
+ - Added bounded child report instructions and kept read-tool budgets cumulative across steering restarts.
11
+ - Lowered the default child quota to 250,000 tokens and $5, and exposed child tool-call counts in results.
12
+
13
+ ## [1.4.2] - 2026-08-01
14
+
15
+ ### Added
16
+
17
+ - Added named child threads with inspectable lifecycle state, Active/Done views, steering, interruption, collection, and closure controls.
18
+ - Added isolated child-process resource guards for wall time, JSONL lines, retained trace, stderr, output, token quota, cost quota, task count, and concurrency.
19
+
20
+ ### Changed
21
+
22
+ - Replaced routine child turn limits with natural completion plus named resource guards.
23
+ - Preserved partial traces and handoffs when a child fails, stops, or reaches a limit.
24
+
5
25
  ## [1.4.1] - 2026-08-01
6
26
 
7
27
  ### Added
package/README.md CHANGED
@@ -33,7 +33,7 @@ pi install git:github.com/KyrosHendrix/pi-KillerOS
33
33
  Pin an install to a release:
34
34
 
35
35
  ```bash
36
- pi install git:github.com/KyrosHendrix/pi-KillerOS@v1.4.1
36
+ pi install git:github.com/KyrosHendrix/pi-KillerOS@v1.4.3
37
37
  ```
38
38
 
39
39
  Add `-l` to either command for a project-only install. Restart Pi after installing.
@@ -47,7 +47,7 @@ Add `-l` to either command for a project-only install. Restart Pi after installi
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
48
  - `/variants` selector and direct reasoning-level arguments
49
49
  - Codex-style `/goal` for durable long-running objectives with pause, resume, edit, clear, automatic continuation, and explicit completion
50
- - Pi-native `subagent` tool with isolated child processes, Markdown roles, explicit read/write boundaries, parallel readers, one serialized writer, bounded output, and cancellation propagation
50
+ - Pi-native `subagent` tool with named, inspectable child threads, Markdown roles, explicit read/write boundaries, parent controls, bounded resources, and cancellation propagation
51
51
  - Claude Code-style `/init` that scans the repository and generates a concise root `AGENTS.md` without setup questions
52
52
  - `question` tool with filtering, proposal previews, keyboard selection, custom answers, history, cancellation, and resize-safe rendering
53
53
  - Mid-prompt slash completion with current Pi `0.82.1` commands, extensions, prompts, and skills
@@ -95,7 +95,7 @@ KillerOS ships `planner`, `reviewer`, `scout`, and `security` as read-only roles
95
95
  2. Personal: `~/.pi/agent/agents/*.md`
96
96
  3. Trusted project: `<repo>/.pi/agents/*.md`
97
97
 
98
- 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`, `maxTurns`, 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.
98
+ 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.
99
99
 
100
100
  The tool supports a single `agent` + `task`, parallel `tasks`, or a sequential `chain` whose task text may include `{previous}`. 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. For example:
101
101
 
@@ -103,7 +103,29 @@ The tool supports a single `agent` + `task`, parallel `tasks`, or a sequential `
103
103
  {"agent":"reviewer","task":"Review the change","model":"provider/model","thinking":"high"}
104
104
  ```
105
105
 
106
- Use the separate `model` and `thinking` fields for new configuration. The older `provider/model:thinking` model form remains accepted. Children run as ephemeral `pi --mode json -p --no-session` processes with explicit local tools plus `web_search`, `source_check`, `fetch_content`, and `get_search_content`. Each child explicitly loads `npm:pi-web-access`, discovers available skills, and keeps arbitrary extensions and prompt templates disabled; project-local skills load only when the parent project is trusted. Every bundled role is instructed to load the most relevant `SKILL.md` before work and to use web research when external evidence is needed. KillerOS allows at most eight tasks, four parallel readers, one serialized writer, 12 turns, ten minutes, a 32 MiB JSONL line, 2 MiB retained trace, 64 KiB stderr, and 50 KiB returned output per task. Esc cancellation terminates active children and escalates after five seconds.
106
+ Use the separate `model` and `thinking` fields for new configuration. The older `provider/model:thinking` model form remains accepted. Children run as ephemeral `pi --mode json -p --no-session` processes with explicit local tools plus `web_search`, `source_check`, `fetch_content`, and `get_search_content`. Each child explicitly loads `npm:pi-web-access` and the KillerOS child-budget hook, discovers available skills, and keeps arbitrary extensions and prompt templates disabled; project-local skills load only when the parent project is trusted. Every bundled role is instructed to load the most relevant `SKILL.md`, work in bounded passes, and report after its first useful evidence. KillerOS allows at most eight tasks, four parallel readers, ten minutes, a 32 MiB JSONL line, 2 MiB retained trace, 64 KiB stderr, 50 KiB returned output, 250,000 tokens, and $5 per child. Read-only children receive a 24-call soft and 32-call hard tool budget that blocks read and web tools after the hard limit; final reports remain allowed. Esc cancellation terminates active children and escalates after five seconds.
107
+
108
+ ### Thread lifecycle
109
+
110
+ Each delegated task creates a named child thread. Its contract records the parent ID, child ID, role, prompt, model, requested capability boundary, trace, usage, and result state. Roles define the child’s access and tools; they do not own lifecycle controls or grant new filesystem powers. The parent owns scope, waits, inspection, steering, collection, and closure.
111
+
112
+ Threads move through `queued`, `active`, `done`, `failed`, `stopped`, and `closed`. The parent renders separate **Active** and **Done** lists. Active threads show their name, task, model, usage, and direct controls. Done threads keep their handoff and trace available until the parent closes them.
113
+
114
+ The parent can inspect a thread’s prompt, role, model, tools, trace, usage, and handoff; steer an active thread with one bounded follow-up; interrupt one child or all active children; collect a concise handoff into parent context; and close a finished or stopped thread. An interrupt preserves the partial trace, states the reason, and reports the handoff as partial rather than successful.
115
+
116
+ A child completes naturally when it returns a final answer. Routine turn caps do not end useful work. Named resource guards still stop unsafe growth: wall time, output bytes, retained trace, stderr, quota, read-tool calls, task count, and concurrency. Read-tool budgets are enforced inside the child process, so the model receives a finalization nudge and a blocked-tool result instead of being killed while it is still researching. A guard reports its cause and returns any partial work clearly. Esc cancellation terminates active children and escalates after five seconds.
117
+
118
+ The replacement lifecycle has nine phases:
119
+
120
+ 1. **Dispatch:** create a named thread and store its contract before launch.
121
+ 2. **Track:** maintain lifecycle states and Active/Done visibility.
122
+ 3. **Inspect:** keep the trace in the child thread, not the parent context.
123
+ 4. **Steer:** append a bounded parent follow-up to an active thread.
124
+ 5. **Interrupt:** stop one or all active children while preserving partial work.
125
+ 6. **Collect:** return a concise handoff while retaining the expanded trace.
126
+ 7. **Bound:** apply named resource guards instead of a routine turn stop.
127
+ 8. **Close:** remove a finished or stopped thread from the workspace without deleting its result record.
128
+ 9. **Prove:** test identity, visibility, controls, natural completion, guards, partial handoffs, and closure.
107
129
 
108
130
  ## Configuration
109
131
 
@@ -141,7 +163,7 @@ The package manifest lists Pi’s built-in modules as peer dependencies, so npm
141
163
 
142
164
  The [`pi-package`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/packages.md) keyword makes a published npm release visible in Pi’s package catalog.
143
165
 
144
- For the current unreleased `1.4.1`, keep the version unchanged and publish after the validation checks pass:
166
+ For the current unreleased `1.4.3`, publish after the validation checks pass:
145
167
 
146
168
  ```bash
147
169
  npm login
@@ -6,7 +6,6 @@ tools: read, grep, find, ls, edit, write, bash, web_search, source_check, fetch_
6
6
  # Replace inherit with provider/model to pin this role; set thinking separately when needed.
7
7
  model: inherit
8
8
  thinking: inherit
9
- maxTurns: 8
10
9
  timeoutMs: 300000
11
10
  ---
12
11
 
@@ -6,7 +6,6 @@ tools: read, grep, find, ls, edit, write, bash, web_search, source_check, fetch_
6
6
  # Replace inherit with provider/model to pin this role; set thinking separately when needed.
7
7
  model: inherit
8
8
  thinking: inherit
9
- maxTurns: 8
10
9
  timeoutMs: 300000
11
10
  ---
12
11
 
package/agents/planner.md CHANGED
@@ -6,7 +6,6 @@ tools: read, grep, find, ls, web_search, source_check, fetch_content, get_search
6
6
  # Replace inherit with provider/model to pin this role; set thinking separately when needed.
7
7
  model: inherit
8
8
  thinking: inherit
9
- maxTurns: 8
10
9
  timeoutMs: 300000
11
10
  ---
12
11
 
@@ -6,7 +6,6 @@ tools: read, grep, find, ls, web_search, source_check, fetch_content, get_search
6
6
  # Replace inherit with provider/model to pin this role; set thinking separately when needed.
7
7
  model: inherit
8
8
  thinking: inherit
9
- maxTurns: 8
10
9
  timeoutMs: 300000
11
10
  ---
12
11
 
package/agents/scout.md CHANGED
@@ -6,7 +6,6 @@ tools: read, grep, find, ls, web_search, source_check, fetch_content, get_search
6
6
  # Replace inherit with provider/model to pin this role; set thinking separately when needed.
7
7
  model: inherit
8
8
  thinking: inherit
9
- maxTurns: 8
10
9
  timeoutMs: 300000
11
10
  ---
12
11
 
@@ -6,7 +6,6 @@ tools: read, grep, find, ls, web_search, source_check, fetch_content, get_search
6
6
  # Replace inherit with provider/model to pin this role; set thinking separately when needed.
7
7
  model: inherit
8
8
  thinking: inherit
9
- maxTurns: 8
10
9
  timeoutMs: 300000
11
10
  ---
12
11
 
package/agents/tester.md CHANGED
@@ -6,7 +6,6 @@ tools: read, grep, find, ls, edit, write, bash, web_search, source_check, fetch_
6
6
  # Replace inherit with provider/model to pin this role; set thinking separately when needed.
7
7
  model: inherit
8
8
  thinking: inherit
9
- maxTurns: 8
10
9
  timeoutMs: 300000
11
10
  ---
12
11
 
package/agents/worker.md CHANGED
@@ -6,7 +6,6 @@ tools: read, grep, find, ls, edit, write, bash, web_search, source_check, fetch_
6
6
  # Replace inherit with provider/model to pin this role; set thinking separately when needed.
7
7
  model: inherit
8
8
  thinking: inherit
9
- maxTurns: 8
10
9
  timeoutMs: 300000
11
10
  ---
12
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "killeros",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "A production-hardened TUI and workflow extension for the Pi coding agent.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -22,6 +22,10 @@
22
22
  "files": [
23
23
  "Killeros.ts",
24
24
  "subagents.ts",
25
+ "subagent-lifecycle.ts",
26
+ "subagent-process.ts",
27
+ "subagent-budget.ts",
28
+ "subagent-ui.ts",
25
29
  "agents/*.md",
26
30
  "themes/killeros.json",
27
31
  "README.md",
@@ -0,0 +1,65 @@
1
+ import type { ExtensionAPI, ToolCallEventResult } from "@earendil-works/pi-coding-agent";
2
+
3
+ export const CHILD_TOOL_BUDGET_ENV = "PI_KILLEROS_TOOL_BUDGET";
4
+
5
+ export interface ChildToolBudget {
6
+ soft?: number;
7
+ hard: number;
8
+ block: "*" | string[];
9
+ }
10
+
11
+ export function parseChildToolBudget(value: string | undefined): ChildToolBudget | undefined {
12
+ if (!value?.trim()) return undefined;
13
+ try {
14
+ const parsed = JSON.parse(value) as Record<string, unknown>;
15
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
16
+ const hard = parsed.hard;
17
+ const soft = parsed.soft;
18
+ const block = parsed.block;
19
+ if (typeof hard !== "number" || !Number.isSafeInteger(hard) || hard < 1) return undefined;
20
+ if (soft !== undefined && (typeof soft !== "number" || !Number.isSafeInteger(soft) || soft < 1 || soft > hard)) return undefined;
21
+ if (block !== "*" && (!Array.isArray(block) || block.length === 0 || block.some((tool) => typeof tool !== "string" || !tool.trim()))) return undefined;
22
+ return {
23
+ hard,
24
+ ...(soft === undefined ? {} : { soft }),
25
+ block: block === "*" ? "*" : [...new Set((block as string[]).map((tool) => tool.trim()))],
26
+ };
27
+ } catch {
28
+ return undefined;
29
+ }
30
+ }
31
+
32
+ function softNudge(budget: ChildToolBudget, calls: number): string {
33
+ return `Tool budget soft limit reached after ${calls} tool call${calls === 1 ? "" : "s"} (soft ${budget.soft}, hard ${budget.hard}). Stop starting new browsing/search work and finalize from the context you already have.`;
34
+ }
35
+
36
+ function shouldBlock(budget: ChildToolBudget, toolName: string, calls: number): boolean {
37
+ return calls > budget.hard && (budget.block === "*" || budget.block.includes(toolName));
38
+ }
39
+
40
+ export default function registerSubagentBudget(pi: ExtensionAPI): void {
41
+ const budget = parseChildToolBudget(process.env[CHILD_TOOL_BUDGET_ENV]);
42
+ if (!budget) return;
43
+ let calls = 0;
44
+ let nudged = false;
45
+ const sendUserMessage = (pi as unknown as {
46
+ sendUserMessage?: (content: string, options: { deliverAs: "steer" }) => unknown;
47
+ }).sendUserMessage;
48
+
49
+ pi.on("tool_call", (event): ToolCallEventResult | void => {
50
+ calls += 1;
51
+ if (!nudged && budget.soft !== undefined && calls >= budget.soft) {
52
+ nudged = true;
53
+ try {
54
+ sendUserMessage?.(softNudge(budget, calls), { deliverAs: "steer" });
55
+ } catch {
56
+ // The hard block below remains authoritative if steering is unavailable.
57
+ }
58
+ }
59
+ if (!shouldBlock(budget, event.toolName, calls)) return undefined;
60
+ return {
61
+ block: true,
62
+ reason: `Tool budget hard limit reached after ${calls} tool calls (hard ${budget.hard}). Finalize from the context you already have.`,
63
+ };
64
+ });
65
+ }