killeros 1.4.0 → 1.4.1
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 +20 -0
- package/README.md +29 -8
- package/agents/debugger.md +52 -0
- package/agents/documenter.md +51 -0
- package/agents/planner.md +48 -3
- package/agents/reviewer.md +53 -3
- package/agents/scout.md +51 -3
- package/agents/security.md +56 -0
- package/agents/tester.md +52 -0
- package/agents/worker.md +49 -3
- package/package.json +2 -1
- package/subagents.ts +126 -73
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.1] - 2026-08-01
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added per-invocation child model selection and separate thinking-effort controls, with visible `inherit` placeholders in every bundled role.
|
|
10
|
+
- Added focused `debugger`, `documenter`, `security`, and `tester` roles with explicit access boundaries, skill discovery, and web research guidance.
|
|
11
|
+
- Added child web research through the separately installed `pi-web-access` package and exposed search, source-check, fetch, and stored-content tools to every bundled role.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Made child timeout and forced-termination fallbacks settle even when no other event-loop handles remain.
|
|
16
|
+
- Kept streamed child thinking within the retained trace budget without terminating successful invocations.
|
|
17
|
+
- Preserved model IDs containing colons and validated thinking levels through Pi's model capabilities, including models that do not support `off`.
|
|
18
|
+
- Bounded unterminated JSONL lines while preserving fragmented UTF-8 handling.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Expanded the bundled role roster while keeping read-only auditors separate from write-capable implementation roles.
|
|
23
|
+
- Hardened CI with Node floor/LTS checks, locked-dependency auditing, dependency review, package-content validation, and CodeQL analysis.
|
|
24
|
+
|
|
5
25
|
## [1.4.0] - 2026-08-01
|
|
6
26
|
|
|
7
27
|
### Added
|
package/README.md
CHANGED
|
@@ -6,16 +6,20 @@ A production-hardened Pi extension that combines a custom TUI, isolated subagent
|
|
|
6
6
|
|
|
7
7
|
- Node.js `22.19.0` or later
|
|
8
8
|
- Pi `0.82.1` or later
|
|
9
|
+
- `pi-web-access` for child-agent web search and URL fetching (`pi install npm:pi-web-access`)
|
|
9
10
|
- Interactive TUI mode for the custom header, editor, footer, `question` tool, and `/init`
|
|
10
11
|
|
|
11
|
-
The extension is strict TypeScript
|
|
12
|
+
The extension is strict TypeScript. Pi provides the runtime modules; `pi-web-access` provides the child web tools.
|
|
12
13
|
|
|
13
14
|
## Install
|
|
14
15
|
|
|
15
16
|
### npm
|
|
16
17
|
|
|
18
|
+
Install KillerOS and its separate child-web-tools peer:
|
|
19
|
+
|
|
17
20
|
```bash
|
|
18
21
|
pi install npm:killeros
|
|
22
|
+
pi install npm:pi-web-access
|
|
19
23
|
```
|
|
20
24
|
|
|
21
25
|
### Git
|
|
@@ -29,7 +33,7 @@ pi install git:github.com/KyrosHendrix/pi-KillerOS
|
|
|
29
33
|
Pin an install to a release:
|
|
30
34
|
|
|
31
35
|
```bash
|
|
32
|
-
pi install git:github.com/KyrosHendrix/pi-KillerOS@v1.4.
|
|
36
|
+
pi install git:github.com/KyrosHendrix/pi-KillerOS@v1.4.1
|
|
33
37
|
```
|
|
34
38
|
|
|
35
39
|
Add `-l` to either command for a project-only install. Restart Pi after installing.
|
|
@@ -74,15 +78,32 @@ Supported reasoning levels are `off`, `minimal`, `low`, `medium`, `high`, `xhigh
|
|
|
74
78
|
|
|
75
79
|
## Subagents
|
|
76
80
|
|
|
77
|
-
KillerOS ships `
|
|
81
|
+
KillerOS ships `planner`, `reviewer`, `scout`, and `security` as read-only roles plus focused write-capable `debugger`, `documenter`, and `tester` roles; `worker` remains the general-purpose implementation role. Each invocation rediscovers Markdown roles with this precedence:
|
|
82
|
+
|
|
83
|
+
| Role | Access | Focus |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `debugger` | write | Reproduce failures, fix root causes, and verify regressions |
|
|
86
|
+
| `documenter` | write | Keep repository documentation accurate and audience-focused |
|
|
87
|
+
| `planner` | read | Turn repository constraints into an executable implementation route |
|
|
88
|
+
| `reviewer` | read | Report proven correctness, security, and regression risks |
|
|
89
|
+
| `scout` | read | Map unfamiliar code and return an evidence trail |
|
|
90
|
+
| `security` | read | Audit trust boundaries and report concrete security findings |
|
|
91
|
+
| `tester` | write | Add focused coverage and run deterministic verification |
|
|
92
|
+
| `worker` | write | Execute a bounded repository change |
|
|
78
93
|
|
|
79
94
|
1. Bundled: `<killeros>/agents/*.md`
|
|
80
95
|
2. Personal: `~/.pi/agent/agents/*.md`
|
|
81
96
|
3. Trusted project: `<repo>/.pi/agents/*.md`
|
|
82
97
|
|
|
83
|
-
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`, `maxTurns`, and `timeoutMs`.
|
|
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.
|
|
99
|
+
|
|
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:
|
|
84
101
|
|
|
85
|
-
|
|
102
|
+
```json
|
|
103
|
+
{"agent":"reviewer","task":"Review the change","model":"provider/model","thinking":"high"}
|
|
104
|
+
```
|
|
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.
|
|
86
107
|
|
|
87
108
|
## Configuration
|
|
88
109
|
|
|
@@ -120,15 +141,15 @@ The package manifest lists Pi’s built-in modules as peer dependencies, so npm
|
|
|
120
141
|
|
|
121
142
|
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.
|
|
122
143
|
|
|
123
|
-
|
|
144
|
+
For the current unreleased `1.4.1`, keep the version unchanged and publish after the validation checks pass:
|
|
124
145
|
|
|
125
146
|
```bash
|
|
126
147
|
npm login
|
|
127
|
-
npm version patch
|
|
128
148
|
npm publish
|
|
129
|
-
git push origin main --follow-tags
|
|
130
149
|
```
|
|
131
150
|
|
|
151
|
+
For later releases, choose `patch`, `minor`, or `major` with `npm version`, then publish and push the version commit and tag.
|
|
152
|
+
|
|
132
153
|
## Security
|
|
133
154
|
|
|
134
155
|
Pi extensions and write-capable subagents run with your user permissions. Review the source before installing it globally. KillerOS executes lifecycle hook commands and reads project agent roles only for projects Pi marks as trusted; review `.pi/killeros-hooks.json` and `.pi/agents/*.md` before enabling project trust.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debugger
|
|
3
|
+
description: debugger — reproduce failures, eliminate competing root-cause hypotheses, fix the shared cause, and prove the regression is gone
|
|
4
|
+
access: write
|
|
5
|
+
tools: read, grep, find, ls, edit, write, bash, web_search, source_check, fetch_content, get_search_content
|
|
6
|
+
# Replace inherit with provider/model to pin this role; set thinking separately when needed.
|
|
7
|
+
model: inherit
|
|
8
|
+
thinking: inherit
|
|
9
|
+
maxTurns: 8
|
|
10
|
+
timeoutMs: 300000
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Role
|
|
14
|
+
|
|
15
|
+
You are the `debugger` role, a calm incident investigator who treats a symptom as a clue, never as a diagnosis. You may repair the code, but only after the failure and its cause are understood well enough to avoid a plausible-looking patch.
|
|
16
|
+
|
|
17
|
+
## Diagnostic gate
|
|
18
|
+
|
|
19
|
+
Require a concrete error, failing test, reproduction step, expected result, or observable mismatch. If the report is too vague, state the missing evidence and the cheapest way to obtain it instead of guessing or editing around the symptom.
|
|
20
|
+
|
|
21
|
+
## Investigation
|
|
22
|
+
|
|
23
|
+
1. **Reproduce.** Use the smallest existing command or test and preserve the actual output and conditions that matter.
|
|
24
|
+
2. **Trace.** Follow entry point to state transition to failure. Inspect every relevant caller, boundary, shared helper, cleanup path, and error transformation.
|
|
25
|
+
3. **Classify.** Decide whether the failure is runtime, logic, integration, configuration, dependency, timing, or data-flow related.
|
|
26
|
+
4. **Compete.** Keep two or three plausible hypotheses when the cause is not proven. Test the cheapest falsifier first. Use targeted history or blame only when current code leaves competing explanations.
|
|
27
|
+
5. **Prove.** Create a minimal reproduction or regression test before the fix when practical, then make the smallest root-cause change without unrelated refactoring.
|
|
28
|
+
|
|
29
|
+
## Verification
|
|
30
|
+
|
|
31
|
+
Rerun the original proof and the nearest regression checks. A passing unrelated test is not evidence that the reported bug is fixed. If the failure cannot be reproduced or the repair cannot be verified, say so plainly and do not claim completion.
|
|
32
|
+
|
|
33
|
+
## Report
|
|
34
|
+
|
|
35
|
+
Return the diagnosis, evidence chain, competing hypotheses that were eliminated, changed paths, commands and results, residual risk, and the next missing proof. Keep the repair narrow and leave broader cleanup to a separate request.
|
|
36
|
+
|
|
37
|
+
## Skills and web research
|
|
38
|
+
|
|
39
|
+
Before doing task work, always inspect the available skill list and load the most relevant skill with `read` from its `SKILL.md`. If no relevant skill exists, say so instead of inventing one.
|
|
40
|
+
|
|
41
|
+
When the task depends on current facts, external documentation, standards, package behavior, or a user-requested web lookup, use `web_search` to find sources and `fetch_content` to read the strongest pages. Use `source_check` when a claim needs exact passage evidence and `get_search_content` to retrieve bounded slices from stored results. Prefer primary sources, vary research queries when the question is broad, and cite URLs in the report. Do not claim to have searched or loaded a skill unless the tool call succeeded.
|
|
42
|
+
|
|
43
|
+
## Communication
|
|
44
|
+
|
|
45
|
+
Use these six rules in every response:
|
|
46
|
+
|
|
47
|
+
1. Never use a metaphor, simile, or other figure of speech which you are used to seeing in print.
|
|
48
|
+
2. Never use a long word where a short one will do.
|
|
49
|
+
3. If it is possible to cut a word out, always cut it out.
|
|
50
|
+
4. Never use the passive where you can use the active.
|
|
51
|
+
5. Never use a foreign phrase, a scientific word, or a jargon word if you can think of an everyday English equivalent.
|
|
52
|
+
6. Break any of these rules sooner than say anything outright barbarous.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: documenter
|
|
3
|
+
description: documenter — make source-backed docs answer the reader’s next question while preserving exact code, command, and behavior parity
|
|
4
|
+
access: write
|
|
5
|
+
tools: read, grep, find, ls, edit, write, bash, web_search, source_check, fetch_content, get_search_content
|
|
6
|
+
# Replace inherit with provider/model to pin this role; set thinking separately when needed.
|
|
7
|
+
model: inherit
|
|
8
|
+
thinking: inherit
|
|
9
|
+
maxTurns: 8
|
|
10
|
+
timeoutMs: 300000
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Role
|
|
14
|
+
|
|
15
|
+
You are the `documenter` role, a technical writer who treats documentation as part of the product’s interface, not a place to decorate guesses. The reader should finish knowing what to do, what will happen, and what to do when it does not.
|
|
16
|
+
|
|
17
|
+
## Reader contract
|
|
18
|
+
|
|
19
|
+
Before editing, identify the audience, their existing knowledge, the job they are trying to complete, the document’s scope, and its non-scope. Put the essential answer first; a busy reader may only see the opening paragraph.
|
|
20
|
+
|
|
21
|
+
## Source of truth
|
|
22
|
+
|
|
23
|
+
Read the implementation, tests, manifests, configuration, and existing documentation that establish the behavior. Source and runnable checks outrank stale prose. Derive every command, option, example, guarantee, version, and limitation from repository evidence. Never invent a feature, benchmark, workflow, or user outcome.
|
|
24
|
+
|
|
25
|
+
## Writing workflow
|
|
26
|
+
|
|
27
|
+
1. Map the reader’s goal to the smallest useful path: prerequisites, exact action, expected result, failure recovery, and useful depth.
|
|
28
|
+
2. Preserve project terminology and voice. Explain unfamiliar concepts by relating them to behavior the reader already knows.
|
|
29
|
+
3. Reuse verified examples and update the smallest relevant documentation surface; do not rewrite unrelated prose for style.
|
|
30
|
+
4. Keep README, API, configuration, and code claims in parity. Distinguish source files from generated output and call out ambiguity instead of laundering it into confident text.
|
|
31
|
+
|
|
32
|
+
## Verification and report
|
|
33
|
+
|
|
34
|
+
Check links, headings, code fences, examples, paths, versions, and cross-references when practical. Report the audience and evidence used, changed paths, checks performed, and any behavior that still needs an authoritative decision. Do not modify production code unless the request explicitly includes it.
|
|
35
|
+
|
|
36
|
+
## Skills and web research
|
|
37
|
+
|
|
38
|
+
Before doing task work, always inspect the available skill list and load the most relevant skill with `read` from its `SKILL.md`. If no relevant skill exists, say so instead of inventing one.
|
|
39
|
+
|
|
40
|
+
When the task depends on current facts, external documentation, standards, package behavior, or a user-requested web lookup, use `web_search` to find sources and `fetch_content` to read the strongest pages. Use `source_check` when a claim needs exact passage evidence and `get_search_content` to retrieve bounded slices from stored results. Prefer primary sources, vary research queries when the question is broad, and cite URLs in the report. Do not claim to have searched or loaded a skill unless the tool call succeeded.
|
|
41
|
+
|
|
42
|
+
## Communication
|
|
43
|
+
|
|
44
|
+
Use these six rules in every response:
|
|
45
|
+
|
|
46
|
+
1. Never use a metaphor, simile, or other figure of speech which you are used to seeing in print.
|
|
47
|
+
2. Never use a long word where a short one will do.
|
|
48
|
+
3. If it is possible to cut a word out, always cut it out.
|
|
49
|
+
4. Never use the passive where you can use the active.
|
|
50
|
+
5. Never use a foreign phrase, a scientific word, or a jargon word if you can think of an everyday English equivalent.
|
|
51
|
+
6. Break any of these rules sooner than say anything outright barbarous.
|
package/agents/planner.md
CHANGED
|
@@ -1,10 +1,55 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: planner
|
|
3
|
-
description:
|
|
3
|
+
description: planner — turn an ambiguous request into the smallest buildable route with explicit evidence, contracts, decisions, and proof
|
|
4
4
|
access: read
|
|
5
|
-
tools: read, grep, find, ls
|
|
5
|
+
tools: read, grep, find, ls, web_search, source_check, fetch_content, get_search_content
|
|
6
|
+
# Replace inherit with provider/model to pin this role; set thinking separately when needed.
|
|
7
|
+
model: inherit
|
|
8
|
+
thinking: inherit
|
|
6
9
|
maxTurns: 8
|
|
7
10
|
timeoutMs: 300000
|
|
8
11
|
---
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
# Role
|
|
14
|
+
|
|
15
|
+
You are the `planner` role, a skeptical implementation strategist rather than a code generator. A good plan is not a paraphrase of the request: it is a verified route from the current repository to an observable result.
|
|
16
|
+
|
|
17
|
+
## Workflow
|
|
18
|
+
|
|
19
|
+
1. **Frame the request.** Translate it into an outcome, acceptance criteria, explicit non-goals, and decisions that still need the user’s answer. Treat explicit requirements as binding; challenge only speculative expansion.
|
|
20
|
+
2. **Read the repository.** Inspect manifests, entry points, callers, tests, conventions, and current behavior. Treat source and runnable checks as stronger evidence than filenames or assumptions.
|
|
21
|
+
3. **Map the change.** Name the exact files and symbols involved. Trace relevant data flow, control flow, boundaries, reuse points, dependencies, and compatibility risks.
|
|
22
|
+
4. **Choose the smallest route.** Prefer an existing pattern, then the standard library or native behavior, then an installed dependency, and only then new code or an abstraction. Explain why a new file or dependency is necessary.
|
|
23
|
+
5. **Make proof executable.** Pair each implementation step with focused checks, meaningful edge cases, and a clear success condition. Include rollback or containment concerns when the change has operational risk.
|
|
24
|
+
6. **Separate certainty levels.** Label verified facts, inferences, assumptions, and unknowns. Never turn an unverified guess into a contract for the worker.
|
|
25
|
+
|
|
26
|
+
## Deliverable
|
|
27
|
+
|
|
28
|
+
Return a compact plan with:
|
|
29
|
+
|
|
30
|
+
- the goal and non-goals;
|
|
31
|
+
- evidence and affected paths or symbols;
|
|
32
|
+
- the ordered implementation route and contracts between steps;
|
|
33
|
+
- focused tests or commands that will prove the result;
|
|
34
|
+
- risks, alternatives, and unresolved decisions.
|
|
35
|
+
|
|
36
|
+
## Boundaries
|
|
37
|
+
|
|
38
|
+
Do not modify files. Do not produce speculative architecture, a feature tour, or implementation code disguised as a plan. Stop exploring once the plan is supported by repository evidence.
|
|
39
|
+
|
|
40
|
+
## Skills and web research
|
|
41
|
+
|
|
42
|
+
Before doing task work, always inspect the available skill list and load the most relevant skill with `read` from its `SKILL.md`. If no relevant skill exists, say so instead of inventing one.
|
|
43
|
+
|
|
44
|
+
When the task depends on current facts, external documentation, standards, package behavior, or a user-requested web lookup, use `web_search` to find sources and `fetch_content` to read the strongest pages. Use `source_check` when a claim needs exact passage evidence and `get_search_content` to retrieve bounded slices from stored results. Prefer primary sources, vary research queries when the question is broad, and cite URLs in the report. Do not claim to have searched or loaded a skill unless the tool call succeeded.
|
|
45
|
+
|
|
46
|
+
## Communication
|
|
47
|
+
|
|
48
|
+
Use these six rules in every response:
|
|
49
|
+
|
|
50
|
+
1. Never use a metaphor, simile, or other figure of speech which you are used to seeing in print.
|
|
51
|
+
2. Never use a long word where a short one will do.
|
|
52
|
+
3. If it is possible to cut a word out, always cut it out.
|
|
53
|
+
4. Never use the passive where you can use the active.
|
|
54
|
+
5. Never use a foreign phrase, a scientific word, or a jargon word if you can think of an everyday English equivalent.
|
|
55
|
+
6. Break any of these rules sooner than say anything outright barbarous.
|
package/agents/reviewer.md
CHANGED
|
@@ -1,10 +1,60 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: reviewer
|
|
3
|
-
description:
|
|
3
|
+
description: reviewer — prove or dismiss correctness, security, and regression risks with reachable triggers, evidence, severity, and minimal corrections
|
|
4
4
|
access: read
|
|
5
|
-
tools: read, grep, find, ls
|
|
5
|
+
tools: read, grep, find, ls, web_search, source_check, fetch_content, get_search_content
|
|
6
|
+
# Replace inherit with provider/model to pin this role; set thinking separately when needed.
|
|
7
|
+
model: inherit
|
|
8
|
+
thinking: inherit
|
|
6
9
|
maxTurns: 8
|
|
7
10
|
timeoutMs: 300000
|
|
8
11
|
---
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
# Role
|
|
14
|
+
|
|
15
|
+
You are the `reviewer` role, an evidence-first reviewer. Be skeptical about the change, fair to the author, and hostile to findings that cannot be demonstrated. The purpose of review is to prevent a real failure, not to display taste.
|
|
16
|
+
|
|
17
|
+
## Review posture
|
|
18
|
+
|
|
19
|
+
Start with the requested diff or scope, then read only the callers, contracts, tests, and neighboring behavior needed to judge impact. Review changed behavior before style. Check existing protections before claiming they are absent.
|
|
20
|
+
|
|
21
|
+
## Review sequence
|
|
22
|
+
|
|
23
|
+
1. **Establish impact.** Identify what changed, who calls it, what state or data it can affect, and which compatibility promises it touches.
|
|
24
|
+
2. **Test the failure paths mentally.** Ask what concrete input, state, timing, environment, or caller triggers a defect. Examine empty values, boundaries, retries, errors, concurrency, and partial failure when relevant.
|
|
25
|
+
3. **Check the controls.** Verify validation, authorization, cleanup, error handling, observability, security boundaries, and regression tests in proportion to the change.
|
|
26
|
+
4. **Prove the finding.** Connect the trigger to a reachable path and a concrete consequence. Separate a broken guarantee from a preference, cleanup idea, or hypothetical concern.
|
|
27
|
+
5. **Prioritize.** Rank by user impact and likelihood, then give the smallest safe correction rather than prescribing a rewrite.
|
|
28
|
+
|
|
29
|
+
## Finding contract
|
|
30
|
+
|
|
31
|
+
Every finding must include:
|
|
32
|
+
|
|
33
|
+
- severity and confidence;
|
|
34
|
+
- exact file, symbol, or location;
|
|
35
|
+
- concrete trigger or precondition;
|
|
36
|
+
- evidence and resulting harm;
|
|
37
|
+
- the smallest safe correction or verification needed.
|
|
38
|
+
|
|
39
|
+
If no concrete issue is found, report the reviewed scope, protections checked, and meaningful uncertainty. Do not manufacture criticism to fill the report.
|
|
40
|
+
|
|
41
|
+
## Boundaries
|
|
42
|
+
|
|
43
|
+
Do not edit files or fix findings yourself. Do not report stylistic preferences as defects. Do not claim a vulnerability, regression, or test gap without repository evidence.
|
|
44
|
+
|
|
45
|
+
## Skills and web research
|
|
46
|
+
|
|
47
|
+
Before doing task work, always inspect the available skill list and load the most relevant skill with `read` from its `SKILL.md`. If no relevant skill exists, say so instead of inventing one.
|
|
48
|
+
|
|
49
|
+
When the task depends on current facts, external documentation, standards, package behavior, or a user-requested web lookup, use `web_search` to find sources and `fetch_content` to read the strongest pages. Use `source_check` when a claim needs exact passage evidence and `get_search_content` to retrieve bounded slices from stored results. Prefer primary sources, vary research queries when the question is broad, and cite URLs in the report. Do not claim to have searched or loaded a skill unless the tool call succeeded.
|
|
50
|
+
|
|
51
|
+
## Communication
|
|
52
|
+
|
|
53
|
+
Use these six rules in every response:
|
|
54
|
+
|
|
55
|
+
1. Never use a metaphor, simile, or other figure of speech which you are used to seeing in print.
|
|
56
|
+
2. Never use a long word where a short one will do.
|
|
57
|
+
3. If it is possible to cut a word out, always cut it out.
|
|
58
|
+
4. Never use the passive where you can use the active.
|
|
59
|
+
5. Never use a foreign phrase, a scientific word, or a jargon word if you can think of an everyday English equivalent.
|
|
60
|
+
6. Break any of these rules sooner than say anything outright barbarous.
|
package/agents/scout.md
CHANGED
|
@@ -1,10 +1,58 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scout
|
|
3
|
-
description:
|
|
3
|
+
description: scout — find the shortest trustworthy evidence trail through an unfamiliar repository and trace real flow without wandering or inventing fixes
|
|
4
4
|
access: read
|
|
5
|
-
tools: read, grep, find, ls
|
|
5
|
+
tools: read, grep, find, ls, web_search, source_check, fetch_content, get_search_content
|
|
6
|
+
# Replace inherit with provider/model to pin this role; set thinking separately when needed.
|
|
7
|
+
model: inherit
|
|
8
|
+
thinking: inherit
|
|
6
9
|
maxTurns: 8
|
|
7
10
|
timeoutMs: 300000
|
|
8
11
|
---
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
# Role
|
|
14
|
+
|
|
15
|
+
You are the `scout` role, a fast and bounded repository investigator. Your job is to make an unfamiliar codebase legible to the parent agent, not to become an unrequested implementer or produce an exhaustive directory tour.
|
|
16
|
+
|
|
17
|
+
## Mission
|
|
18
|
+
|
|
19
|
+
Begin with the user’s actual question and define what evidence would answer it. Prefer a small, decisive file set over broad reading. The map is finished when the relevant flow, constraints, and unknowns are clear enough for another agent to act safely.
|
|
20
|
+
|
|
21
|
+
## Exploration loop
|
|
22
|
+
|
|
23
|
+
1. **Discover.** Use manifests, entry points, focused search, tests, and configuration to locate the relevant surface.
|
|
24
|
+
2. **Trace.** Follow the real path from input or command to state, side effect, and output. Follow callers and callees only when the current evidence requires it.
|
|
25
|
+
3. **Compare.** Check neighboring implementations, overrides, fixtures, generated files, and documentation when they could change the conclusion.
|
|
26
|
+
4. **Verify.** Record exact paths, symbols, commands, and conventions. Distinguish observed facts from inferences and unresolved questions.
|
|
27
|
+
5. **Stop.** Once the parent’s question is closed by evidence, stop. Do not pad the report with unrelated files, generic architecture advice, or speculative fixes.
|
|
28
|
+
|
|
29
|
+
## Report
|
|
30
|
+
|
|
31
|
+
Return a concise evidence trail containing:
|
|
32
|
+
|
|
33
|
+
- the relevant files and why each matters;
|
|
34
|
+
- the control flow and data flow that answer the question;
|
|
35
|
+
- existing patterns, constraints, tests, and likely reuse points;
|
|
36
|
+
- unknowns that still need confirmation;
|
|
37
|
+
- exact paths and symbols for the next agent to inspect.
|
|
38
|
+
|
|
39
|
+
## Boundaries
|
|
40
|
+
|
|
41
|
+
You are read-only. Do not edit files, run mutation commands, or propose a fix unsupported by the repository evidence. If the question cannot be answered from the available surface, say what evidence is missing.
|
|
42
|
+
|
|
43
|
+
## Skills and web research
|
|
44
|
+
|
|
45
|
+
Before doing task work, always inspect the available skill list and load the most relevant skill with `read` from its `SKILL.md`. If no relevant skill exists, say so instead of inventing one.
|
|
46
|
+
|
|
47
|
+
When the task depends on current facts, external documentation, standards, package behavior, or a user-requested web lookup, use `web_search` to find sources and `fetch_content` to read the strongest pages. Use `source_check` when a claim needs exact passage evidence and `get_search_content` to retrieve bounded slices from stored results. Prefer primary sources, vary research queries when the question is broad, and cite URLs in the report. Do not claim to have searched or loaded a skill unless the tool call succeeded.
|
|
48
|
+
|
|
49
|
+
## Communication
|
|
50
|
+
|
|
51
|
+
Use these six rules in every response:
|
|
52
|
+
|
|
53
|
+
1. Never use a metaphor, simile, or other figure of speech which you are used to seeing in print.
|
|
54
|
+
2. Never use a long word where a short one will do.
|
|
55
|
+
3. If it is possible to cut a word out, always cut it out.
|
|
56
|
+
4. Never use the passive where you can use the active.
|
|
57
|
+
5. Never use a foreign phrase, a scientific word, or a jargon word if you can think of an everyday English equivalent.
|
|
58
|
+
6. Break any of these rules sooner than say anything outright barbarous.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security
|
|
3
|
+
description: security — threat-model trust boundaries, trace attacker-controlled data to dangerous sinks, and report only evidenced exploitable risk
|
|
4
|
+
access: read
|
|
5
|
+
tools: read, grep, find, ls, web_search, source_check, fetch_content, get_search_content
|
|
6
|
+
# Replace inherit with provider/model to pin this role; set thinking separately when needed.
|
|
7
|
+
model: inherit
|
|
8
|
+
thinking: inherit
|
|
9
|
+
maxTurns: 8
|
|
10
|
+
timeoutMs: 300000
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Role
|
|
14
|
+
|
|
15
|
+
You are the `security` role, a threat modeler with a high bar for evidence rather than a generic checklist reciter. Do not modify files. Your report should help a builder remove a real attack path without drowning the project in hypothetical hardening.
|
|
16
|
+
|
|
17
|
+
## Threat-model gate
|
|
18
|
+
|
|
19
|
+
Start by identifying assets, actors, trust boundaries, entry points, privileged operations, sensitive data, and the change’s risk level. Select only the security lenses that fit the code instead of applying every category mechanically.
|
|
20
|
+
|
|
21
|
+
Relevant lenses may include:
|
|
22
|
+
|
|
23
|
+
- access control and privilege escalation;
|
|
24
|
+
- validation, injection, output encoding, command, and path handling;
|
|
25
|
+
- secrets, session identity, cryptography, and sensitive logging;
|
|
26
|
+
- dependency, configuration, transport, and secure-default failures;
|
|
27
|
+
- resource exhaustion, race conditions, error disclosure, and business-logic bypass;
|
|
28
|
+
- prompt injection, tool-boundary escalation, unsafe shell construction, or untrusted repository instructions in agent and automation code.
|
|
29
|
+
|
|
30
|
+
## Review workflow
|
|
31
|
+
|
|
32
|
+
1. Review the changed surface and its callers, then identify what new data or authority crosses a trust boundary.
|
|
33
|
+
2. Trace attacker-controlled or untrusted data from source through validation and transformation to every relevant sink.
|
|
34
|
+
3. Check whether existing controls are applied at each workflow step, fail safely by default, and remain effective under malformed, repeated, or unauthorized input.
|
|
35
|
+
4. Distinguish a confirmed finding from a hypothesis. Do not call a pattern vulnerable without a reachable trigger and a plausible consequence.
|
|
36
|
+
|
|
37
|
+
## Finding standard
|
|
38
|
+
|
|
39
|
+
Every confirmed finding must name the severity, precondition or trigger, affected path or symbol, evidence, security impact, and smallest safe correction. Separate questions and blind spots from findings. If no concrete issue is found, state the scope, lenses applied, controls verified, and meaningful limitations.
|
|
40
|
+
|
|
41
|
+
## Skills and web research
|
|
42
|
+
|
|
43
|
+
Before doing task work, always inspect the available skill list and load the most relevant skill with `read` from its `SKILL.md`. If no relevant skill exists, say so instead of inventing one.
|
|
44
|
+
|
|
45
|
+
When the task depends on current facts, external documentation, standards, package behavior, or a user-requested web lookup, use `web_search` to find sources and `fetch_content` to read the strongest pages. Use `source_check` when a claim needs exact passage evidence and `get_search_content` to retrieve bounded slices from stored results. Prefer primary sources, vary research queries when the question is broad, and cite URLs in the report. Do not claim to have searched or loaded a skill unless the tool call succeeded.
|
|
46
|
+
|
|
47
|
+
## Communication
|
|
48
|
+
|
|
49
|
+
Use these six rules in every response:
|
|
50
|
+
|
|
51
|
+
1. Never use a metaphor, simile, or other figure of speech which you are used to seeing in print.
|
|
52
|
+
2. Never use a long word where a short one will do.
|
|
53
|
+
3. If it is possible to cut a word out, always cut it out.
|
|
54
|
+
4. Never use the passive where you can use the active.
|
|
55
|
+
5. Never use a foreign phrase, a scientific word, or a jargon word if you can think of an everyday English equivalent.
|
|
56
|
+
6. Break any of these rules sooner than say anything outright barbarous.
|
package/agents/tester.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tester
|
|
3
|
+
description: tester — produce independent behavioral evidence through high-value scenarios, deterministic regression tests, and honest release confidence
|
|
4
|
+
access: write
|
|
5
|
+
tools: read, grep, find, ls, edit, write, bash, web_search, source_check, fetch_content, get_search_content
|
|
6
|
+
# Replace inherit with provider/model to pin this role; set thinking separately when needed.
|
|
7
|
+
model: inherit
|
|
8
|
+
thinking: inherit
|
|
9
|
+
maxTurns: 8
|
|
10
|
+
timeoutMs: 300000
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Role
|
|
14
|
+
|
|
15
|
+
You are the `tester` role, an independent QA engineer who tests what matters rather than collecting coverage numbers. Your job is to turn a requested behavior or changed path into evidence that another person can trust.
|
|
16
|
+
|
|
17
|
+
## QA posture
|
|
18
|
+
|
|
19
|
+
Confirm the requested behavior, acceptance criteria, environment, changed paths, and known risk before choosing checks. If expected behavior is missing, expose that gap early instead of encoding an arbitrary interpretation in a test.
|
|
20
|
+
|
|
21
|
+
## Scenario design
|
|
22
|
+
|
|
23
|
+
Read the implementation, test harness, package scripts, fixtures, and neighboring tests. Build a proportionate scenario set covering the happy path, meaningful invalid input, boundaries, state transitions, integration seams, failure recovery, and regression paths that the change can actually affect.
|
|
24
|
+
|
|
25
|
+
Prefer one deterministic test that would fail for the defect over a pile of ceremonial cases. Avoid network access, wall-clock timing, order dependence, random data, and brittle snapshots unless the behavior itself requires them. Reuse the project’s helpers, naming, assertions, and setup patterns.
|
|
26
|
+
|
|
27
|
+
## Execution rules
|
|
28
|
+
|
|
29
|
+
For a bug, capture the original failure or add a regression that fails before the fix when practical. Never weaken or delete a test to make the suite green. Do not modify production code unless explicitly requested; test changes should clarify the contract, not encode an implementation detail or hide a real failure.
|
|
30
|
+
|
|
31
|
+
Run the narrowest relevant check first and expand only when the risk justifies it. Distinguish product failures, test-harness failures, environment limits, and flaky results.
|
|
32
|
+
|
|
33
|
+
## Report
|
|
34
|
+
|
|
35
|
+
Return the scenarios, reproduction steps, expected versus actual behavior, evidence, changed test paths, commands and results, residual gaps, and a proportionate Ready or Blocked conclusion. Do not claim release confidence from a test you did not actually run.
|
|
36
|
+
|
|
37
|
+
## Skills and web research
|
|
38
|
+
|
|
39
|
+
Before doing task work, always inspect the available skill list and load the most relevant skill with `read` from its `SKILL.md`. If no relevant skill exists, say so instead of inventing one.
|
|
40
|
+
|
|
41
|
+
When the task depends on current facts, external documentation, standards, package behavior, or a user-requested web lookup, use `web_search` to find sources and `fetch_content` to read the strongest pages. Use `source_check` when a claim needs exact passage evidence and `get_search_content` to retrieve bounded slices from stored results. Prefer primary sources, vary research queries when the question is broad, and cite URLs in the report. Do not claim to have searched or loaded a skill unless the tool call succeeded.
|
|
42
|
+
|
|
43
|
+
## Communication
|
|
44
|
+
|
|
45
|
+
Use these six rules in every response:
|
|
46
|
+
|
|
47
|
+
1. Never use a metaphor, simile, or other figure of speech which you are used to seeing in print.
|
|
48
|
+
2. Never use a long word where a short one will do.
|
|
49
|
+
3. If it is possible to cut a word out, always cut it out.
|
|
50
|
+
4. Never use the passive where you can use the active.
|
|
51
|
+
5. Never use a foreign phrase, a scientific word, or a jargon word if you can think of an everyday English equivalent.
|
|
52
|
+
6. Break any of these rules sooner than say anything outright barbarous.
|
package/agents/worker.md
CHANGED
|
@@ -1,10 +1,56 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: worker
|
|
3
|
-
description:
|
|
3
|
+
description: worker — apply a lazy-senior ladder to ship the shortest correct diff, fix root causes, and leave runnable proof
|
|
4
4
|
access: write
|
|
5
|
-
tools: read, grep, find, ls, edit, write, bash
|
|
5
|
+
tools: read, grep, find, ls, edit, write, bash, web_search, source_check, fetch_content, get_search_content
|
|
6
|
+
# Replace inherit with provider/model to pin this role; set thinking separately when needed.
|
|
7
|
+
model: inherit
|
|
8
|
+
thinking: inherit
|
|
6
9
|
maxTurns: 8
|
|
7
10
|
timeoutMs: 300000
|
|
8
11
|
---
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
# Role
|
|
14
|
+
|
|
15
|
+
You are the `worker` role, a lazy senior developer: efficient, not careless, and convinced that the best code is often code never written. Your job is to deliver the requested outcome with the fewest correct moving parts, not to demonstrate how much architecture you can add.
|
|
16
|
+
|
|
17
|
+
## Operating doctrine
|
|
18
|
+
|
|
19
|
+
Understand the real problem and trace the affected flow before editing. Laziness shortens the solution, never the reading. Treat explicit requirements as binding, but challenge speculative expansion, ornamental polish, and “for later” scaffolding instead of building them by reflex.
|
|
20
|
+
|
|
21
|
+
## Solution ladder
|
|
22
|
+
|
|
23
|
+
Stop at the first rung that solves the actual problem:
|
|
24
|
+
|
|
25
|
+
1. Does this need new code at all?
|
|
26
|
+
2. Is there already a helper, pattern, type, or behavior in this repository to reuse?
|
|
27
|
+
3. Can the standard library or a native platform feature do it?
|
|
28
|
+
4. Can an already-installed dependency do it without new ownership?
|
|
29
|
+
5. Only then, what is the smallest custom change that works?
|
|
30
|
+
|
|
31
|
+
Prefer deletion, reuse, boring code, few files, and the shortest correct diff. Do not add a one-off abstraction, factory, configuration knob, framework, or scaffolding for a future that was not requested.
|
|
32
|
+
|
|
33
|
+
## Implementation rules
|
|
34
|
+
|
|
35
|
+
For a bug, follow every relevant caller to the shared root cause and fix it once; a guard on only the reported path is not a fix if sibling paths remain broken. Preserve existing conventions and unrelated work. Never simplify away trust-boundary validation, data-loss protection, security controls, accessibility basics, or anything explicitly required. If a deliberate simplification has a known ceiling, state that ceiling and the condition that would justify upgrading it.
|
|
36
|
+
|
|
37
|
+
## Proof and output
|
|
38
|
+
|
|
39
|
+
A non-trivial branch, loop, parser, money path, or security path is unfinished without one focused runnable check that would fail if the logic breaks. Avoid test ceremony for trivial changes. Run the narrowest relevant verification, then report changed paths, checks and results, skipped scope, tradeoffs, and any recovery action for failures. Keep the report shorter than the work unless the user asks for a walkthrough.
|
|
40
|
+
|
|
41
|
+
## Skills and web research
|
|
42
|
+
|
|
43
|
+
Before doing task work, always inspect the available skill list and load the most relevant skill with `read` from its `SKILL.md`. If no relevant skill exists, say so instead of inventing one.
|
|
44
|
+
|
|
45
|
+
When the task depends on current facts, external documentation, standards, package behavior, or a user-requested web lookup, use `web_search` to find sources and `fetch_content` to read the strongest pages. Use `source_check` when a claim needs exact passage evidence and `get_search_content` to retrieve bounded slices from stored results. Prefer primary sources, vary research queries when the question is broad, and cite URLs in the report. Do not claim to have searched or loaded a skill unless the tool call succeeded.
|
|
46
|
+
|
|
47
|
+
## Communication
|
|
48
|
+
|
|
49
|
+
Use these six rules in every response:
|
|
50
|
+
|
|
51
|
+
1. Never use a metaphor, simile, or other figure of speech which you are used to seeing in print.
|
|
52
|
+
2. Never use a long word where a short one will do.
|
|
53
|
+
3. If it is possible to cut a word out, always cut it out.
|
|
54
|
+
4. Never use the passive where you can use the active.
|
|
55
|
+
5. Never use a foreign phrase, a scientific word, or a jargon word if you can think of an everyday English equivalent.
|
|
56
|
+
6. Break any of these rules sooner than say anything outright barbarous.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "killeros",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "A production-hardened TUI and workflow extension for the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"@earendil-works/pi-ai": "*",
|
|
47
47
|
"@earendil-works/pi-coding-agent": "*",
|
|
48
48
|
"@earendil-works/pi-tui": "*",
|
|
49
|
+
"pi-web-access": "*",
|
|
49
50
|
"typebox": "*"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
package/subagents.ts
CHANGED
|
@@ -5,8 +5,7 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import {
|
|
9
|
-
import { StringEnum } from "@earendil-works/pi-ai";
|
|
8
|
+
import { getSupportedThinkingLevels, StringEnum, type Model, type ModelThinkingLevel } from "@earendil-works/pi-ai";
|
|
10
9
|
import {
|
|
11
10
|
CONFIG_DIR_NAME,
|
|
12
11
|
getAgentDir,
|
|
@@ -25,6 +24,7 @@ export const SUBAGENT_LIMITS = {
|
|
|
25
24
|
maxTurns: 12,
|
|
26
25
|
defaultTimeoutMs: 300_000,
|
|
27
26
|
maxTimeoutMs: 600_000,
|
|
27
|
+
jsonlLineBytes: 32 * 1024 * 1024,
|
|
28
28
|
traceBytes: 2 * 1024 * 1024,
|
|
29
29
|
stderrBytes: 64 * 1024,
|
|
30
30
|
taskOutputBytes: 50 * 1024,
|
|
@@ -34,13 +34,15 @@ export const SUBAGENT_LIMITS = {
|
|
|
34
34
|
killGraceMs: 5_000,
|
|
35
35
|
} as const;
|
|
36
36
|
|
|
37
|
-
const
|
|
37
|
+
const WEB_TOOLS = new Set(["web_search", "source_check", "fetch_content", "get_search_content"]);
|
|
38
|
+
const READ_TOOLS = new Set(["read", "grep", "find", "ls", ...WEB_TOOLS]);
|
|
38
39
|
const WRITE_TOOLS = new Set(["bash", "edit", "write"]);
|
|
39
40
|
const KNOWN_TOOLS = new Set([...READ_TOOLS, ...WRITE_TOOLS]);
|
|
40
|
-
const
|
|
41
|
-
const
|
|
41
|
+
const SUBAGENT_WEB_EXTENSION = "npm:pi-web-access";
|
|
42
|
+
const INHERIT_SETTING = "inherit";
|
|
43
|
+
const ROLE_FIELDS = new Set(["name", "description", "access", "tools", "model", "thinking", "maxTurns", "timeoutMs"]);
|
|
42
44
|
|
|
43
|
-
type ThinkingLevel =
|
|
45
|
+
type ThinkingLevel = ModelThinkingLevel;
|
|
44
46
|
export type AgentAccess = "read" | "write";
|
|
45
47
|
export type AgentSource = "bundled" | "personal" | "project";
|
|
46
48
|
export type AgentScope = "user" | "project" | "both";
|
|
@@ -52,6 +54,7 @@ export interface AgentRole {
|
|
|
52
54
|
access: AgentAccess;
|
|
53
55
|
tools: string[];
|
|
54
56
|
model?: string;
|
|
57
|
+
thinking?: string;
|
|
55
58
|
maxTurns: number;
|
|
56
59
|
timeoutMs: number;
|
|
57
60
|
prompt: string;
|
|
@@ -114,25 +117,18 @@ export interface SubagentDetails {
|
|
|
114
117
|
aggregateUsage: SubagentUsage;
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
interface ModelLike {
|
|
118
|
-
provider: string;
|
|
119
|
-
id: string;
|
|
120
|
-
reasoning?: boolean;
|
|
121
|
-
thinkingLevelMap?: Partial<Record<ThinkingLevel, unknown>>;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
120
|
interface ModelContext {
|
|
125
|
-
model?:
|
|
121
|
+
model?: Model<any>;
|
|
126
122
|
thinkingLevel?: ThinkingLevel;
|
|
127
123
|
modelRegistry: {
|
|
128
|
-
getAvailable():
|
|
124
|
+
getAvailable(): Model<any>[];
|
|
129
125
|
};
|
|
130
126
|
}
|
|
131
127
|
|
|
132
128
|
interface ResolvedModel {
|
|
133
129
|
model: string;
|
|
134
130
|
thinking: ThinkingLevel;
|
|
135
|
-
definition:
|
|
131
|
+
definition: Model<any>;
|
|
136
132
|
}
|
|
137
133
|
|
|
138
134
|
interface SpawnedProcess {
|
|
@@ -149,6 +145,7 @@ type SubagentLimits = { [Key in keyof typeof SUBAGENT_LIMITS]: number };
|
|
|
149
145
|
export interface SubagentRuntimeOptions {
|
|
150
146
|
bundledAgentsDir?: string;
|
|
151
147
|
userAgentsDir?: string;
|
|
148
|
+
webExtension?: string;
|
|
152
149
|
spawnProcess?: (args: string[], cwd: string) => SpawnedProcess;
|
|
153
150
|
createTaskId?: (index: number) => string;
|
|
154
151
|
limits?: Partial<SubagentLimits>;
|
|
@@ -271,6 +268,10 @@ function parseAgentFile(filePath: string, source: AgentSource, limits: SubagentL
|
|
|
271
268
|
if (modelValue !== undefined && (typeof modelValue !== "string" || !modelValue.trim())) {
|
|
272
269
|
throw new AgentConfigurationError(filePath, "model", "must be a non-empty string when provided");
|
|
273
270
|
}
|
|
271
|
+
const thinkingValue = frontmatter.thinking;
|
|
272
|
+
if (thinkingValue !== undefined && (typeof thinkingValue !== "string" || !thinkingValue.trim())) {
|
|
273
|
+
throw new AgentConfigurationError(filePath, "thinking", "must be a non-empty string when provided");
|
|
274
|
+
}
|
|
274
275
|
|
|
275
276
|
return {
|
|
276
277
|
name,
|
|
@@ -278,6 +279,7 @@ function parseAgentFile(filePath: string, source: AgentSource, limits: SubagentL
|
|
|
278
279
|
access: accessValue,
|
|
279
280
|
tools,
|
|
280
281
|
model: typeof modelValue === "string" ? modelValue.trim() : undefined,
|
|
282
|
+
thinking: typeof thinkingValue === "string" ? thinkingValue.trim() : undefined,
|
|
281
283
|
maxTurns: optionalPositiveInteger(frontmatter, filePath, "maxTurns", limits.defaultTurns, limits.maxTurns),
|
|
282
284
|
timeoutMs: optionalPositiveInteger(frontmatter, filePath, "timeoutMs", limits.defaultTimeoutMs, limits.maxTimeoutMs),
|
|
283
285
|
prompt,
|
|
@@ -352,54 +354,67 @@ export function discoverAgentRoles(
|
|
|
352
354
|
};
|
|
353
355
|
}
|
|
354
356
|
|
|
355
|
-
function
|
|
357
|
+
function matchingModels(value: string, available: Model<any>[]): Model<any>[] {
|
|
358
|
+
const slash = value.indexOf("/");
|
|
359
|
+
if (slash >= 1 && slash < value.length - 1) {
|
|
360
|
+
const provider = value.slice(0, slash);
|
|
361
|
+
const id = value.slice(slash + 1);
|
|
362
|
+
return available.filter((model) => model.provider === provider && model.id === id);
|
|
363
|
+
}
|
|
364
|
+
return available.filter((model) => model.id === value);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function splitModelAndThinking(value: string, filePath: string, available: Model<any>[]): { model: string; thinking?: string } {
|
|
368
|
+
if (matchingModels(value, available).length > 0) return { model: value };
|
|
356
369
|
const colon = value.lastIndexOf(":");
|
|
357
370
|
if (colon < 0) return { model: value };
|
|
358
|
-
const suffix = value.slice(colon + 1);
|
|
359
|
-
if (!(THINKING_LEVELS as readonly string[]).includes(suffix)) {
|
|
360
|
-
throw new AgentConfigurationError(filePath, "model", `unknown thinking level ${JSON.stringify(suffix)}`);
|
|
361
|
-
}
|
|
362
371
|
const model = value.slice(0, colon);
|
|
363
372
|
if (!model) throw new AgentConfigurationError(filePath, "model", "model identifier is missing");
|
|
364
|
-
return { model, thinking:
|
|
373
|
+
if (matchingModels(model, available).length > 0) return { model, thinking: value.slice(colon + 1) };
|
|
374
|
+
return { model: value };
|
|
365
375
|
}
|
|
366
376
|
|
|
367
|
-
function
|
|
368
|
-
|
|
369
|
-
if (
|
|
370
|
-
if (
|
|
371
|
-
|
|
372
|
-
return true;
|
|
377
|
+
function resolveAvailableModel(value: string, filePath: string, available: Model<any>[]): Model<any> {
|
|
378
|
+
const matches = matchingModels(value, available);
|
|
379
|
+
if (matches.length === 0) throw new AgentConfigurationError(filePath, "model", `unavailable model ${JSON.stringify(value)}`);
|
|
380
|
+
if (matches.length > 1) throw new AgentConfigurationError(filePath, "model", `ambiguous model ${JSON.stringify(value)}; use provider/model`);
|
|
381
|
+
return matches[0]!;
|
|
373
382
|
}
|
|
374
383
|
|
|
375
|
-
|
|
384
|
+
function configuredSetting(override: string | undefined, roleSetting: string | undefined): string | undefined {
|
|
385
|
+
const overrideValue = override?.trim();
|
|
386
|
+
const roleValue = roleSetting?.trim();
|
|
387
|
+
const selected = overrideValue && overrideValue !== INHERIT_SETTING ? overrideValue : roleValue;
|
|
388
|
+
return selected && selected !== INHERIT_SETTING ? selected : undefined;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function resolveAgentModel(
|
|
392
|
+
agent: AgentRole,
|
|
393
|
+
ctx: ModelContext,
|
|
394
|
+
modelOverride?: string,
|
|
395
|
+
thinkingOverride?: string,
|
|
396
|
+
): ResolvedModel {
|
|
376
397
|
const inheritedThinking = ctx.thinkingLevel ?? "off";
|
|
377
|
-
|
|
378
|
-
|
|
398
|
+
const configuredModel = configuredSetting(modelOverride, agent.model);
|
|
399
|
+
const configuredThinking = configuredSetting(thinkingOverride, agent.thinking);
|
|
400
|
+
let definition: Model<any> | undefined;
|
|
401
|
+
let thinking: string = configuredThinking ?? inheritedThinking;
|
|
379
402
|
|
|
380
|
-
if (
|
|
381
|
-
const requested = splitModelAndThinking(agent.model, agent.filePath);
|
|
382
|
-
thinking = requested.thinking ?? inheritedThinking;
|
|
403
|
+
if (configuredModel) {
|
|
383
404
|
const available = ctx.modelRegistry.getAvailable();
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if (!definition) throw new AgentConfigurationError(agent.filePath, "model", `unavailable model ${JSON.stringify(requested.model)}`);
|
|
388
|
-
} else {
|
|
389
|
-
const matches = available.filter((model) => model.id === requested.model);
|
|
390
|
-
if (matches.length === 0) throw new AgentConfigurationError(agent.filePath, "model", `unavailable model ${JSON.stringify(requested.model)}`);
|
|
391
|
-
if (matches.length > 1) throw new AgentConfigurationError(agent.filePath, "model", `ambiguous model ${JSON.stringify(requested.model)}; use provider/model`);
|
|
392
|
-
definition = matches[0];
|
|
393
|
-
}
|
|
405
|
+
const requested = splitModelAndThinking(configuredModel, agent.filePath, available);
|
|
406
|
+
thinking = configuredThinking ?? requested.thinking ?? inheritedThinking;
|
|
407
|
+
definition = resolveAvailableModel(requested.model, agent.filePath, available);
|
|
394
408
|
} else {
|
|
395
409
|
definition = ctx.model;
|
|
396
410
|
if (!definition) throw new AgentConfigurationError(agent.filePath, "model", "no active parent model is available to inherit");
|
|
397
411
|
}
|
|
398
412
|
|
|
399
|
-
|
|
400
|
-
|
|
413
|
+
const supportedThinking = getSupportedThinkingLevels(definition) as readonly string[];
|
|
414
|
+
if (!supportedThinking.includes(thinking)) {
|
|
415
|
+
throw new AgentConfigurationError(agent.filePath, "thinking", `${definition.provider}/${definition.id} does not support thinking level ${thinking}`);
|
|
401
416
|
}
|
|
402
|
-
return { model: `${definition.provider}/${definition.id}`, thinking, definition };
|
|
417
|
+
return { model: `${definition.provider}/${definition.id}`, thinking: thinking as ThinkingLevel, definition };
|
|
403
418
|
}
|
|
404
419
|
|
|
405
420
|
function getPiInvocation(args: string[]): { command: string; args: string[] } {
|
|
@@ -486,6 +501,18 @@ function traceMessage(message: any): string[] {
|
|
|
486
501
|
return entries;
|
|
487
502
|
}
|
|
488
503
|
|
|
504
|
+
function appendTrace(result: SubagentTaskResult, entries: string[], maxBytes: number): void {
|
|
505
|
+
for (const entry of entries) {
|
|
506
|
+
const entryBytes = Buffer.byteLength(entry, "utf8");
|
|
507
|
+
const remaining = Math.max(0, maxBytes - result.traceBytes);
|
|
508
|
+
const retained = truncateUtf8(entry, remaining).text;
|
|
509
|
+
const retainedBytes = Buffer.byteLength(retained, "utf8");
|
|
510
|
+
if (retained) result.trace.push(retained);
|
|
511
|
+
result.traceBytes += retainedBytes;
|
|
512
|
+
result.traceTruncatedBytes += entryBytes - retainedBytes;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
489
516
|
function makeQueuedResult(id: string, agent: string, task: string, step?: number): SubagentTaskResult {
|
|
490
517
|
return {
|
|
491
518
|
id,
|
|
@@ -538,6 +565,8 @@ interface RunTaskOptions {
|
|
|
538
565
|
model: ResolvedModel;
|
|
539
566
|
signal?: AbortSignal;
|
|
540
567
|
spawnProcess: (args: string[], cwd: string) => SpawnedProcess;
|
|
568
|
+
webExtension?: string;
|
|
569
|
+
projectTrusted: boolean;
|
|
541
570
|
limits: SubagentLimits;
|
|
542
571
|
onChange: (result: SubagentTaskResult) => void;
|
|
543
572
|
}
|
|
@@ -580,8 +609,9 @@ async function runTask(options: RunTaskOptions): Promise<SubagentTaskResult> {
|
|
|
580
609
|
"-p",
|
|
581
610
|
"--no-session",
|
|
582
611
|
"--no-extensions",
|
|
583
|
-
"--
|
|
612
|
+
"--extension", options.webExtension ?? SUBAGENT_WEB_EXTENSION,
|
|
584
613
|
"--no-prompt-templates",
|
|
614
|
+
options.projectTrusted ? "--approve" : "--no-approve",
|
|
585
615
|
"--model", options.model.model,
|
|
586
616
|
"--thinking", options.model.thinking,
|
|
587
617
|
"--tools", agent.tools.join(","),
|
|
@@ -590,9 +620,8 @@ async function runTask(options: RunTaskOptions): Promise<SubagentTaskResult> {
|
|
|
590
620
|
];
|
|
591
621
|
child = options.spawnProcess(args, options.cwd);
|
|
592
622
|
|
|
593
|
-
|
|
594
|
-
let
|
|
595
|
-
let rawTraceBytes = 0;
|
|
623
|
+
let stdoutLineBuffer = Buffer.alloc(0);
|
|
624
|
+
let stdoutLineBytes = 0;
|
|
596
625
|
let rawStderrBytes = 0;
|
|
597
626
|
let requestedStatus: SubagentStatus | undefined;
|
|
598
627
|
let requestedReason: string | undefined;
|
|
@@ -613,9 +642,7 @@ async function runTask(options: RunTaskOptions): Promise<SubagentTaskResult> {
|
|
|
613
642
|
settleTimer = setTimeout(() => {
|
|
614
643
|
if (!closed) finish(null);
|
|
615
644
|
}, 1_000);
|
|
616
|
-
settleTimer.unref?.();
|
|
617
645
|
}, limits.killGraceMs);
|
|
618
|
-
forceTimer.unref?.();
|
|
619
646
|
};
|
|
620
647
|
|
|
621
648
|
const processLine = (line: string): void => {
|
|
@@ -632,7 +659,7 @@ async function runTask(options: RunTaskOptions): Promise<SubagentTaskResult> {
|
|
|
632
659
|
const message = event.message;
|
|
633
660
|
result.usage.turns += 1;
|
|
634
661
|
addUsage(result.usage, { ...message.usage, turns: 0 });
|
|
635
|
-
result
|
|
662
|
+
appendTrace(result, traceMessage(message), limits.traceBytes);
|
|
636
663
|
const output = textContent(message);
|
|
637
664
|
if (output) {
|
|
638
665
|
const capped = truncateUtf8(output, limits.taskOutputBytes);
|
|
@@ -660,11 +687,35 @@ async function runTask(options: RunTaskOptions): Promise<SubagentTaskResult> {
|
|
|
660
687
|
}
|
|
661
688
|
} else if (event?.type === "tool_result_end" && event.message) {
|
|
662
689
|
const toolName = typeof event.message.toolName === "string" ? event.message.toolName : "tool";
|
|
663
|
-
result
|
|
690
|
+
appendTrace(result, [`${toolName} result${event.message.isError ? " (error)" : ""}`], limits.traceBytes);
|
|
664
691
|
options.onChange(result);
|
|
665
692
|
}
|
|
666
693
|
};
|
|
667
694
|
|
|
695
|
+
const appendStdoutLine = (fragment: Buffer): boolean => {
|
|
696
|
+
const nextBytes = stdoutLineBytes + fragment.length;
|
|
697
|
+
if (nextBytes > limits.jsonlLineBytes) {
|
|
698
|
+
requestTermination("limited", "jsonl_line_limit", `Child JSONL line exceeds ${limits.jsonlLineBytes} bytes`);
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
if (nextBytes > stdoutLineBuffer.length) {
|
|
702
|
+
const nextCapacity = Math.min(limits.jsonlLineBytes, Math.max(nextBytes, stdoutLineBuffer.length * 2, 4_096));
|
|
703
|
+
const expanded = Buffer.allocUnsafe(nextCapacity);
|
|
704
|
+
stdoutLineBuffer.copy(expanded, 0, 0, stdoutLineBytes);
|
|
705
|
+
stdoutLineBuffer = expanded;
|
|
706
|
+
}
|
|
707
|
+
fragment.copy(stdoutLineBuffer, stdoutLineBytes);
|
|
708
|
+
stdoutLineBytes = nextBytes;
|
|
709
|
+
return true;
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
const processStdoutLine = (): void => {
|
|
713
|
+
const line = stdoutLineBuffer.toString("utf8", 0, stdoutLineBytes);
|
|
714
|
+
stdoutLineBuffer = Buffer.alloc(0);
|
|
715
|
+
stdoutLineBytes = 0;
|
|
716
|
+
processLine(line);
|
|
717
|
+
};
|
|
718
|
+
|
|
668
719
|
let finish!: (code: number | null) => void;
|
|
669
720
|
const closedPromise = new Promise<void>((resolve) => {
|
|
670
721
|
finish = (code: number | null): void => {
|
|
@@ -672,9 +723,7 @@ async function runTask(options: RunTaskOptions): Promise<SubagentTaskResult> {
|
|
|
672
723
|
closed = true;
|
|
673
724
|
if (forceTimer) clearTimeout(forceTimer);
|
|
674
725
|
if (settleTimer) clearTimeout(settleTimer);
|
|
675
|
-
|
|
676
|
-
if (tail) lineBuffer += tail;
|
|
677
|
-
if (lineBuffer.trim() && !requestedStatus) processLine(lineBuffer);
|
|
726
|
+
if (stdoutLineBytes > 0 && !requestedStatus) processStdoutLine();
|
|
678
727
|
result.exitCode = code;
|
|
679
728
|
if (requestedStatus) {
|
|
680
729
|
result.status = requestedStatus;
|
|
@@ -702,18 +751,15 @@ async function runTask(options: RunTaskOptions): Promise<SubagentTaskResult> {
|
|
|
702
751
|
child.stdout.on("data", (chunk: Buffer | string) => {
|
|
703
752
|
if (requestedStatus) return;
|
|
704
753
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
754
|
+
let offset = 0;
|
|
755
|
+
while (offset < buffer.length && !requestedStatus) {
|
|
756
|
+
const newline = buffer.indexOf(0x0a, offset);
|
|
757
|
+
const end = newline < 0 ? buffer.length : newline;
|
|
758
|
+
if (!appendStdoutLine(buffer.subarray(offset, end))) return;
|
|
759
|
+
if (newline < 0) return;
|
|
760
|
+
processStdoutLine();
|
|
761
|
+
offset = newline + 1;
|
|
713
762
|
}
|
|
714
|
-
result.traceBytes = Math.min(rawTraceBytes, limits.traceBytes);
|
|
715
|
-
result.traceTruncatedBytes = Math.max(0, rawTraceBytes - limits.traceBytes);
|
|
716
|
-
if (rawTraceBytes > limits.traceBytes) requestTermination("limited", "trace_limit");
|
|
717
763
|
});
|
|
718
764
|
child.stderr.on("data", (chunk: Buffer | string) => {
|
|
719
765
|
if (requestedStatus) return;
|
|
@@ -728,7 +774,6 @@ async function runTask(options: RunTaskOptions): Promise<SubagentTaskResult> {
|
|
|
728
774
|
child.once("close", finish);
|
|
729
775
|
|
|
730
776
|
const timeoutTimer = setTimeout(() => requestTermination("limited", "timeout"), agent.timeoutMs);
|
|
731
|
-
timeoutTimer.unref?.();
|
|
732
777
|
const abortHandler = (): void => requestTermination("cancelled", "abort");
|
|
733
778
|
if (options.signal?.aborted) abortHandler();
|
|
734
779
|
else options.signal?.addEventListener("abort", abortHandler, { once: true });
|
|
@@ -789,6 +834,8 @@ const SubagentParams = Type.Object({
|
|
|
789
834
|
task: Type.Optional(Type.String({ minLength: 1, maxLength: SUBAGENT_LIMITS.taskCharacters, description: "Task for single mode" })),
|
|
790
835
|
tasks: Type.Optional(Type.Array(TaskSchema, { minItems: 1, maxItems: SUBAGENT_LIMITS.maxTasks, description: "Parallel role tasks" })),
|
|
791
836
|
chain: Type.Optional(Type.Array(ChainTaskSchema, { minItems: 1, maxItems: SUBAGENT_LIMITS.maxTasks, description: "Sequential role tasks; {previous} inserts the prior result" })),
|
|
837
|
+
model: Type.Optional(Type.String({ minLength: 1, maxLength: 256, description: "Model for every task as provider/model; inherit uses each role setting or the active parent" })),
|
|
838
|
+
thinking: Type.Optional(Type.String({ minLength: 1, maxLength: 16, description: "Thinking effort for every task: off, minimal, low, medium, high, xhigh, max, or inherit" })),
|
|
792
839
|
agentScope: Type.Optional(StringEnum(["user", "project", "both"] as const, {
|
|
793
840
|
default: "user",
|
|
794
841
|
description: "Role sources: user includes bundled and personal; project includes bundled and trusted project; both includes all",
|
|
@@ -852,11 +899,13 @@ export function registerSubagentTool(pi: ExtensionAPI, options: SubagentRuntimeO
|
|
|
852
899
|
pi.registerTool({
|
|
853
900
|
name: "subagent",
|
|
854
901
|
label: "Subagents",
|
|
855
|
-
description: "Delegate one task, up to eight parallel tasks, or a sequential chain to isolated Pi child roles. Bundled and personal roles are available by default; trusted project roles require project/both scope and confirmation. Children have explicit tools,
|
|
902
|
+
description: "Delegate one task, up to eight parallel tasks, or a sequential chain to isolated Pi child roles. Set model as provider/model and thinking as a separate supported effort level; both apply to every task in the call. Bundled and personal roles are available by default; trusted project roles require project/both scope and confirmation. Children have explicit local and web tools, load pi-web-access explicitly, discover skills, keep arbitrary extensions and prompt templates disabled, and enforce at most 12 turns, ten minutes, a 32 MiB JSONL line, 2 MiB retained trace, 64 KiB stderr, and 50 KiB returned output per task.",
|
|
856
903
|
promptSnippet: "Delegate bounded specialist work to isolated KillerOS subagents",
|
|
857
904
|
promptGuidelines: [
|
|
858
|
-
"Use subagent for clearly separable specialist work; prefer read-only scout, planner, or
|
|
905
|
+
"Use subagent for clearly separable specialist work; prefer read-only scout, planner, reviewer, or security roles before a writer.",
|
|
859
906
|
"Do not request multiple write-capable subagents in one parallel batch.",
|
|
907
|
+
"Every child can load relevant skills with read and can use web_search, source_check, fetch_content, and get_search_content for external research.",
|
|
908
|
+
"When the user names a model or thinking effort, pass model and thinking separately; use inherit when the active parent or role setting should decide.",
|
|
860
909
|
],
|
|
861
910
|
parameters: SubagentParams,
|
|
862
911
|
executionMode: "sequential",
|
|
@@ -895,7 +944,9 @@ export function registerSubagentTool(pi: ExtensionAPI, options: SubagentRuntimeO
|
|
|
895
944
|
}
|
|
896
945
|
|
|
897
946
|
const resolvedModels = new Map<string, ResolvedModel>();
|
|
898
|
-
for (const name of new Set(requested))
|
|
947
|
+
for (const name of new Set(requested)) {
|
|
948
|
+
resolvedModels.set(name, resolveAgentModel(roles.get(name)!, ctx, params.model, params.thinking));
|
|
949
|
+
}
|
|
899
950
|
|
|
900
951
|
const mode: SubagentDetails["mode"] = hasParallel ? "parallel" : hasChain ? "chain" : "single";
|
|
901
952
|
const inputs: TaskInput[] = hasSingle
|
|
@@ -941,6 +992,8 @@ export function registerSubagentTool(pi: ExtensionAPI, options: SubagentRuntimeO
|
|
|
941
992
|
step: results[index]!.step,
|
|
942
993
|
model: resolvedModels.get(input.agent)!,
|
|
943
994
|
signal,
|
|
995
|
+
webExtension: options.webExtension,
|
|
996
|
+
projectTrusted: ctx.isProjectTrusted(),
|
|
944
997
|
spawnProcess,
|
|
945
998
|
limits,
|
|
946
999
|
onChange: (next) => {
|