@ssheleg/agent-stack 0.8.0 → 0.9.0
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 +54 -0
- package/README.md +6 -3
- package/package.json +1 -1
- package/plugins/agent-stack/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-stack/skills/agent-harness/SKILL.md +11 -9
- package/plugins/agent-stack/skills/agent-harness/references/layers.md +4 -0
- package/plugins/agent-stack/skills/agent-harness/references/pi-sdk.md +318 -0
- package/plugins/agent-stack/skills/agent-harness/references/pi.md +241 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.0] — 2026-08-15
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`agent-harness/references/pi.md` and `pi-sdk.md` — the harness doctrine as a worked
|
|
8
|
+
implementation.** Every other reference in that skill states a rule; **Pi** is small enough
|
|
9
|
+
to read and complete enough to have made each of those decisions in public. So each section
|
|
10
|
+
says what Pi does and then **which rule it is an instance of** — the second half is the
|
|
11
|
+
point, and where Pi disagrees with this pack, that is said rather than smoothed over.
|
|
12
|
+
|
|
13
|
+
Read from `pi.dev/docs/latest` on 2026-08-15: sixteen doc pages plus the package source.
|
|
14
|
+
All sixteen verified reachable (`200`); the only non-resolving URLs in either file are the
|
|
15
|
+
two deliberate placeholders inside custom-provider examples.
|
|
16
|
+
|
|
17
|
+
- **`pi.md`** — the four ways to run it; **sessions as a JSONL tree** (8-hex `id`, `parentId`,
|
|
18
|
+
version 3, `BranchSummaryEntry`) matched to *time travel and forking* in
|
|
19
|
+
`agent-orchestrator/references/runtime.md`; **compaction with the real numbers**
|
|
20
|
+
(`contextTokens > contextWindow - reserveTokens`, defaults 16,384 and 20,000, tool results
|
|
21
|
+
truncated to 2,000 chars while summarizing) matched to the compaction ladder, with what the
|
|
22
|
+
ladder adds that Pi leaves to you; settings precedence that **merges rather than replaces**;
|
|
23
|
+
skills, prompt templates and packages; and the trust model.
|
|
24
|
+
|
|
25
|
+
Its sharpest section is **the deliberate absence of a sandbox**, quoted: *"prompt injection
|
|
26
|
+
from repository files … is expected local-agent risk and cannot be reliably prevented by
|
|
27
|
+
pi."* That is `layers.md`'s delegation thesis stated by the project itself — and for an
|
|
28
|
+
audit it changes the finding, because "no permission model" here is a delegation, not a
|
|
29
|
+
defect. Three containerization patterns are compared by **where credentials end up**, which
|
|
30
|
+
is the question that actually decides between them.
|
|
31
|
+
|
|
32
|
+
- **`pi-sdk.md`** — `createAgentSession()`, `ModelRuntime`, `SessionManager`, `defineTool()`,
|
|
33
|
+
`DefaultResourceLoader`; the RPC protocol with its command groups, its full event lifecycle
|
|
34
|
+
and the **`\n`-only JSONL framing warning**; JSON mode's delta-only records and why;
|
|
35
|
+
the `ExtensionAPI` surface; and **the eight seams** — `tool_call` (can block),
|
|
36
|
+
`tool_result` (a middleware chain), `context`, `before_agent_start`, the three provider
|
|
37
|
+
hooks, and the compaction pair — each matched to the doctrine it lets you implement.
|
|
38
|
+
`tool_call` blocking is called out as the single most important one for an audit: it is
|
|
39
|
+
where a per-tool, per-caller policy can actually live.
|
|
40
|
+
|
|
41
|
+
- Noted with its evidence and its limit: **Pi discovers skills from `~/.agents/skills/`**,
|
|
42
|
+
which on this machine is the ssheleg hub — 72 entries, every family skill carrying the
|
|
43
|
+
`name` and `description` front matter Pi requires. Stated as a fact about the path and the
|
|
44
|
+
front matter, **not** as an observed load: Pi is not installed here. The reference also
|
|
45
|
+
names Pi's documented divergence from the Agent Skills standard (a skill name may differ
|
|
46
|
+
from its directory) and warns that `make-skill`'s validator enforces the strict rule.
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
- `references/layers.md` points at the two new files as the worked example of the kernel
|
|
51
|
+
layer it describes abstractly.
|
|
52
|
+
- `agent-harness`'s description gains the embedding triggers (`agent SDK`, `embed an agent`,
|
|
53
|
+
`Pi harness`, `встроить агента`) — 807/1024, inside the family's 970 working budget. The
|
|
54
|
+
repository's own front-matter gate caught a first draft at 1066 and refused the write.
|
|
55
|
+
|
|
56
|
+
|
|
3
57
|
## [0.8.0] — 2026-08-14
|
|
4
58
|
|
|
5
59
|
### Added
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Part of the [ssheleg skill family](https://github.com/ssheleg/sshlg-skills).
|
|
|
11
11
|
|
|
12
12
|
Four skills — `agent-orchestrator` for wiring the loop, `agent-evals` for proving it
|
|
13
13
|
behaves, `agent-interop` for everything it talks to outside its own process,
|
|
14
|
-
`agent-harness` for what it is **told** — and
|
|
14
|
+
`agent-harness` for what it is **told** — and eighteen references they load on demand,
|
|
15
15
|
plus one scanner.
|
|
16
16
|
|
|
17
17
|
**The orchestrator** (`SKILL.md`) — what the agent reads first:
|
|
@@ -61,13 +61,16 @@ ACP, AGNTCY, AP2, Agent Skills — so an agent stops guessing.
|
|
|
61
61
|
model, and the one where most agent bugs actually live: *the biggest performance
|
|
62
62
|
improvements often come from clearly explaining tool usage in the system prompt*, and *even
|
|
63
63
|
small refinements to tool descriptions can yield dramatic improvements*. Before adding a
|
|
64
|
-
retry or a sub-agent, it asks four questions about the text.
|
|
64
|
+
retry or a sub-agent, it asks four questions about the text. Seven references —
|
|
65
65
|
`system-prompt.md` (right altitude, enumerated vocabulary, and the three things reasoning
|
|
66
66
|
models changed — starting with **do not add chain-of-thought**), `tools.md` (the
|
|
67
67
|
agent–computer interface, with a worked before/after and poka-yoke), `techniques.md`
|
|
68
68
|
(fifteen techniques, a verdict each **for production** rather than a benchmark),
|
|
69
69
|
`layers.md` (which layer you are building at, and why permission boundaries are usually the
|
|
70
|
-
environment's job), `audit.md` (seven tracks, evidence tiers, a plan instead of a score)
|
|
70
|
+
environment's job), `audit.md` (seven tracks, evidence tiers, a plan instead of a score) —
|
|
71
|
+
plus **`pi.md` and `pi-sdk.md`, the doctrine as a worked implementation**: Pi read end to
|
|
72
|
+
end, each mechanism matched to the rule it instantiates, its divergences named, and the
|
|
73
|
+
eight extension seams where a permission gate or a context rewrite can actually live.
|
|
71
74
|
|
|
72
75
|
It runs in both directions: **building a harness and auditing somebody else's are one
|
|
73
76
|
checklist read forwards and backwards.** `scripts/audit_agent.py` is the mechanical half —
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ssheleg/agent-stack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Production patterns for AI agent orchestrators \u2014 tool-calling loops, multi-stage pipelines with checkpoints, LLM provider routing with fallback, four-layer memory with confidence decay \u2014 plus the wallet side of reselling LLM access. This package is the installer CLI.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agent-stack": "bin/agent-stack.js"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "agent-stack",
|
|
3
3
|
"displayName": "Agent Stack",
|
|
4
4
|
"description": "Two skills: agent-orchestrator \u2014 tool-calling loops, multi-stage pipelines with checkpoints, provider routing with fallback, four-layer memory, context engineering, plus the wallet side of reselling LLM access; and agent-evals \u2014 run/trace/thread evals, judges, and fixtures grown from production.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.9.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ssheleg",
|
|
8
8
|
"url": "https://x.com/sshlg93"
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
name: agent-harness
|
|
3
3
|
description: >-
|
|
4
4
|
Use when the question is what the agent is TOLD rather than how its loop is wired — writing
|
|
5
|
-
or fixing a system prompt, shaping tools so the model
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
description", "
|
|
11
|
-
"
|
|
12
|
-
"системный промпт", "агент не вызывает тул", "аудит агента", "
|
|
13
|
-
the loop's plumbing, its evals, or
|
|
5
|
+
or fixing a system prompt, shaping tools so the model picks the right one, deciding whether
|
|
6
|
+
a job wants a workflow or an agent, or choosing between ReAct, reflection and voting. Also
|
|
7
|
+
auditing an agent system somebody else built: tracks, evidence tiers and a prioritized plan
|
|
8
|
+
instead of a score, plus a scanner. Carries Pi as a worked kernel implementation — SDK, RPC
|
|
9
|
+
and extension seams — for embedding or extending a harness. Triggers - "system prompt",
|
|
10
|
+
"tool description", "agent picks the wrong tool", "agent loops forever", "prompt
|
|
11
|
+
engineering", "ReAct", "workflow or agent", "audit this agent", "embed an agent", "agent
|
|
12
|
+
SDK", "Pi harness", "системный промпт", "агент не вызывает тул", "аудит агента", "встроить
|
|
13
|
+
агента". Not for the loop's plumbing, its evals, or its protocols — those are siblings.
|
|
14
14
|
---
|
|
15
15
|
|
|
16
16
|
# Agent harness — what the agent is told, and how to audit what someone else told theirs
|
|
@@ -95,6 +95,8 @@ and `test/validate.py` fails the build on a reference that does not say when it
|
|
|
95
95
|
| [`references/techniques.md`](references/techniques.md) | you are choosing between **ReAct, reflection, voting, planning** and the rest — every entry carries a verdict for production, not a benchmark score |
|
|
96
96
|
| [`references/layers.md`](references/layers.md) | deciding **what your harness owns** — kernel, workbench and product layers, and why permission boundaries are usually somebody else's job |
|
|
97
97
|
| [`references/audit.md`](references/audit.md) | reviewing **an agent system you did not build** — seven tracks, evidence tiers, and a prioritized plan |
|
|
98
|
+
| [`references/pi.md`](references/pi.md) | you want the doctrine above as a **worked implementation** — Pi's sessions, compaction, config, skills, trust and containerization, each matched to the rule it implements, and the places it deliberately disagrees |
|
|
99
|
+
| [`references/pi-sdk.md`](references/pi-sdk.md) | you are **embedding or extending** a harness — the SDK, the RPC protocol, JSON mode, and the eight extension seams where a permission gate, a context rewrite or a cost hook can actually live |
|
|
98
100
|
|
|
99
101
|
**`scripts/audit_agent.py`** — the mechanical half of the audit. It finds what is visible
|
|
100
102
|
without understanding intent (an unbounded loop, a tool with no description, a swallowed
|
|
@@ -62,6 +62,10 @@ surrounds it. Read as a weakness, it looks like a missing feature. Read as archi
|
|
|
62
62
|
is a clear statement: *a harness that also claims to be a sandbox is claiming a guarantee it
|
|
63
63
|
cannot keep*, because it runs in the same process as the code it would be confining.
|
|
64
64
|
|
|
65
|
+
**Worked out in full in `pi.md` and `pi-sdk.md`** — the kernel layer read end to end, with
|
|
66
|
+
each mechanism matched to the rule it implements and its divergences named. Use it as the
|
|
67
|
+
concrete example when this file's abstractions need a body.
|
|
68
|
+
|
|
65
69
|
Two consequences worth stating plainly:
|
|
66
70
|
|
|
67
71
|
- **A harness advertising "safe tool execution" without an OS-level or container boundary is
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# Building on Pi — SDK, RPC, and the extension seams
|
|
2
|
+
|
|
3
|
+
**Load this when:** embedding an agent in your own process, driving one from another
|
|
4
|
+
language, or extending a harness — and you want a real API surface rather than a sketch.
|
|
5
|
+
|
|
6
|
+
**Spec pinned:** `@earendil-works/pi-coding-agent`, `pi.dev/docs/latest` (sdk, rpc, json, extensions, custom-provider) · read 2026-08-15
|
|
7
|
+
|
|
8
|
+
Read `pi.md` first for the harness itself. This file is the programmable half, and its
|
|
9
|
+
value is the **seams**: the eight or so places a real harness lets you intervene, each
|
|
10
|
+
matched to the rule it lets you implement.
|
|
11
|
+
|
|
12
|
+
## Contents
|
|
13
|
+
|
|
14
|
+
- Choosing a way in
|
|
15
|
+
- The SDK
|
|
16
|
+
- Custom tools
|
|
17
|
+
- RPC: driving it from any language
|
|
18
|
+
- JSON mode, and why it differs
|
|
19
|
+
- The extension API
|
|
20
|
+
- The seams that matter
|
|
21
|
+
- Custom providers
|
|
22
|
+
- Traps
|
|
23
|
+
|
|
24
|
+
## Choosing a way in
|
|
25
|
+
|
|
26
|
+
| You want | Use | Because |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| your process owns the loop, in Node | **SDK** | direct objects, no serialization |
|
|
29
|
+
| to drive an agent from Python, Go, a UI | **RPC** | JSONL over stdin/stdout, bidirectional |
|
|
30
|
+
| to consume a run's events, one shot | **JSON mode** | delta-only stream, linear in size |
|
|
31
|
+
| a scripted answer | **print** | `pi -p` |
|
|
32
|
+
| to change behaviour rather than call it | **an extension** | it runs inside the loop |
|
|
33
|
+
|
|
34
|
+
**The distinction people get wrong:** RPC and JSON both emit events, but only RPC accepts
|
|
35
|
+
commands. If you need to steer mid-run, it is RPC.
|
|
36
|
+
|
|
37
|
+
## The SDK
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install @earendil-works/pi-coding-agent
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
import { createAgentSession, ModelRuntime, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
45
|
+
|
|
46
|
+
const modelRuntime = await ModelRuntime.create();
|
|
47
|
+
const { session } = await createAgentSession({
|
|
48
|
+
sessionManager: SessionManager.inMemory(),
|
|
49
|
+
modelRuntime,
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**`AgentSession`** — `prompt(text, options?)`, `steer(text)` and `followUp(text)` to queue
|
|
54
|
+
during streaming, `subscribe(listener)`, `setModel()`, `setThinkingLevel()`, `compact()`,
|
|
55
|
+
`abort()`, `dispose()`.
|
|
56
|
+
|
|
57
|
+
**Events** arrive structured:
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
session.subscribe((event) => {
|
|
61
|
+
if (event.type === "message_update" &&
|
|
62
|
+
event.assistantMessageEvent.type === "text_delta") {
|
|
63
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**`SessionManager`** factories decide persistence: `inMemory()`, `create(cwd)`,
|
|
69
|
+
`continueRecent(cwd)`, `open(filePath)`. **`AgentSessionRuntime`** handles replacement —
|
|
70
|
+
`newSession()`, `switchSession(path)`, `fork(entryId)`.
|
|
71
|
+
|
|
72
|
+
**`createAgentSession()` options** worth knowing: `model` (from `@earendil-works/pi-ai`),
|
|
73
|
+
`thinkingLevel` (`"off"` … `"max"`), `tools` (names to enable), `cwd`, `agentDir`
|
|
74
|
+
(defaults `~/.pi/agent`), `resourceLoader`, `settingsManager`.
|
|
75
|
+
|
|
76
|
+
**`ModelRuntime`** carries credentials and availability:
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
const modelRuntime = await ModelRuntime.create({
|
|
80
|
+
allowModelNetwork: true,
|
|
81
|
+
modelRefreshTimeoutMs: 15_000,
|
|
82
|
+
});
|
|
83
|
+
await modelRuntime.setRuntimeApiKey("anthropic", "sk-key");
|
|
84
|
+
const available = await modelRuntime.getAvailable();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Resolution: runtime overrides → `auth.json` → environment.
|
|
88
|
+
|
|
89
|
+
**`DefaultResourceLoader`** discovers extensions, skills and prompts, and is where you
|
|
90
|
+
override the system prompt for an embedded agent:
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
const loader = new DefaultResourceLoader({
|
|
94
|
+
cwd: process.cwd(),
|
|
95
|
+
additionalExtensionPaths: ["/path/to/extension.ts"],
|
|
96
|
+
systemPromptOverride: () => "Custom system prompt",
|
|
97
|
+
});
|
|
98
|
+
await loader.reload();
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**`allowModelNetwork` and `systemPromptOverride` are the two options an embedded agent
|
|
102
|
+
almost always needs** — the first because a server should not discover models at runtime
|
|
103
|
+
unless you meant it, the second because the default prompt is a coding agent's and yours
|
|
104
|
+
probably is not.
|
|
105
|
+
|
|
106
|
+
## Custom tools
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
const myTool = defineTool({
|
|
110
|
+
name: "my_tool",
|
|
111
|
+
description: "Does something useful",
|
|
112
|
+
parameters: Type.Object({ input: Type.String() }),
|
|
113
|
+
execute: async (_id, params) => ({
|
|
114
|
+
content: [{ type: "text", text: `Result: ${params.input}` }],
|
|
115
|
+
details: {},
|
|
116
|
+
}),
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Passed as `customTools: [myTool]`. Built-ins: `read`, `bash`, `edit`, `write`, `grep`,
|
|
121
|
+
`find`, `ls`.
|
|
122
|
+
|
|
123
|
+
**`details` is not decoration.** It is how a tool result carries structured state into the
|
|
124
|
+
session, and Pi's own guidance is to rebuild in-memory state after a restart by walking
|
|
125
|
+
`ctx.sessionManager.getBranch()` and reading it. That is **structured note-taking**
|
|
126
|
+
(`techniques.md`) with a durable home.
|
|
127
|
+
|
|
128
|
+
Write the `description` to `tools.md`'s standard — this is the same field, and the same
|
|
129
|
+
leverage.
|
|
130
|
+
|
|
131
|
+
## RPC: driving it from any language
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
pi --mode rpc [--provider … --model … --name … --no-session --session-dir …]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
JSON Lines over stdin/stdout: **commands** in, **responses** (`type: "response"`)
|
|
138
|
+
acknowledging them, **events** streaming asynchronously.
|
|
139
|
+
|
|
140
|
+
> **Framing warning, quoted because it bites in exactly one language at a time:** *"Split
|
|
141
|
+
> records on `\n` only; accept optional `\r\n` input by stripping a trailing `\r`."* Some
|
|
142
|
+
> standard line readers split on Unicode separators too, and a model that emits one inside
|
|
143
|
+
> a string will then desynchronize your parser.
|
|
144
|
+
|
|
145
|
+
**Commands**, by group:
|
|
146
|
+
|
|
147
|
+
| Group | Commands |
|
|
148
|
+
|---|---|
|
|
149
|
+
| Prompting | `prompt`, `steer` (delivered after the current tool), `follow_up`, `abort` |
|
|
150
|
+
| State | `get_state`, `get_messages`, `set_model`, `cycle_model`, `set_thinking_level`, `set_steering_mode`, `set_follow_up_mode` |
|
|
151
|
+
| Sessions | `new_session`, `switch_session`, `fork`, `clone`, `get_session_stats`, `export_html`, `set_session_name` |
|
|
152
|
+
| Execution | `bash`, `compact`, `set_auto_compaction`, `set_auto_retry` |
|
|
153
|
+
| Introspection | `get_available_models`, `get_commands`, `get_fork_messages`, `get_entries`, `get_tree` |
|
|
154
|
+
|
|
155
|
+
**Event lifecycle**, in order:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
agent_start → turn_start → message_start → message_update* → message_end
|
|
159
|
+
→ tool_execution_start → tool_execution_update* → tool_execution_end
|
|
160
|
+
→ turn_end → agent_end → agent_settled
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Plus `queue_update`, `compaction_start/end`, `auto_retry_start/end`,
|
|
164
|
+
`bash_execution_update` (correlated by the command's `id`), and `extension_error`.
|
|
165
|
+
|
|
166
|
+
**`agent_settled` is the one to wait on, not `agent_end`** — it means no further auto-retry
|
|
167
|
+
is queued. A client that treats `agent_end` as final will occasionally act on a run that is
|
|
168
|
+
about to continue.
|
|
169
|
+
|
|
170
|
+
Message shapes are stable and worth matching: `UserMessage`, `AssistantMessage` (with
|
|
171
|
+
`model`, `usage`, `stopReason`), `ToolResultMessage` (`toolCallId`, `toolName`, `isError`),
|
|
172
|
+
`BashExecutionMessage` (`command`, `output`, `exitCode`, `cancelled`).
|
|
173
|
+
|
|
174
|
+
**Extensions can ask the user something over RPC**, which is the part most integrations
|
|
175
|
+
forget. `extension_ui_request` events carry `select`, `confirm`, `input`, `editor` and
|
|
176
|
+
expect an `extension_ui_response` with the matching `id`; `notify`, `setStatus`, `setWidget`
|
|
177
|
+
are fire-and-forget.
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{"type": "extension_ui_request", "id": "uuid-1", "method": "select", "title": "Choose", "options": ["A", "B"]}
|
|
181
|
+
{"type": "extension_ui_response", "id": "uuid-1", "value": "A"}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**A client that ignores these hangs the agent** whenever an extension asks a question. Not
|
|
185
|
+
implementing them is a decision; not knowing about them is an outage.
|
|
186
|
+
|
|
187
|
+
## JSON mode, and why it differs
|
|
188
|
+
|
|
189
|
+
`pi --mode json "…"` streams the same lifecycle, opening with a header:
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{"type":"session","version":3,"id":"uuid","timestamp":"...","cwd":"/path"}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**`message_update` records are delta-only** — they omit the cumulative `message` field and
|
|
196
|
+
`assistantMessageEvent.partial` *"to keep stream size linear."* Consumers assemble text from
|
|
197
|
+
`contentIndex` and `delta`.
|
|
198
|
+
|
|
199
|
+
That is a deliberate trade: RPC gives you snapshots you can resync from, JSON gives you a
|
|
200
|
+
stream that does not grow quadratically. **Pick JSON for pipelines, RPC for UIs.**
|
|
201
|
+
|
|
202
|
+
## The extension API
|
|
203
|
+
|
|
204
|
+
Auto-discovered from `~/.pi/agent/extensions/*.ts` (global), `.pi/extensions/*.ts`
|
|
205
|
+
(project, after trust) or an `extensions` array in settings. A file, a directory with
|
|
206
|
+
`index.ts`, or a package with its own `node_modules`.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
210
|
+
|
|
211
|
+
export default function (pi: ExtensionAPI) {
|
|
212
|
+
// may be async — do initialization here, not resource startup
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Registration surface:**
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
pi.registerTool({ name, label, description, promptSnippet, promptGuidelines,
|
|
220
|
+
parameters, prepareArguments?, execute, renderCall?, renderResult? })
|
|
221
|
+
pi.registerCommand(name, { description, getArgumentCompletions?, handler })
|
|
222
|
+
pi.registerProvider(name, config) / pi.unregisterProvider(name)
|
|
223
|
+
pi.registerMessageRenderer / registerEntryRenderer / registerMarkdownTransformer
|
|
224
|
+
pi.registerShortcut(shortcut, options) / pi.registerFlag(name, options)
|
|
225
|
+
pi.on(eventName, handler) / pi.events.on|emit
|
|
226
|
+
pi.getActiveTools() / getAllTools() / setActiveTools(names)
|
|
227
|
+
pi.setModel(model) / getThinkingLevel() / setThinkingLevel(level)
|
|
228
|
+
pi.sendMessage / sendUserMessage / appendEntry / setSessionName / setLabel
|
|
229
|
+
pi.exec(command, args, options?)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**`promptSnippet` and `promptGuidelines` deserve attention**: a tool contributes not only a
|
|
233
|
+
schema but a line to the system prompt and a set of guidelines. That is `system-prompt.md`'s
|
|
234
|
+
*"tool policy belongs in the prompt"* built into the registration call, which is the right
|
|
235
|
+
place for it — the policy cannot drift from the tool because they are declared together.
|
|
236
|
+
|
|
237
|
+
**Context (`ctx`)** in every handler: `ui`, `mode` (`"tui" | "rpc" | "json" | "print"`),
|
|
238
|
+
`hasUI`, `cwd`, `isProjectTrusted()`, `sessionManager`, `modelRegistry`, `model`,
|
|
239
|
+
`thinkingLevel`, `signal`, `isIdle()`, `abort()`, `getContextUsage()`, `compact(options)`,
|
|
240
|
+
`getSystemPrompt()`. Commands additionally get `newSession()`, `fork()`, `navigateTree()`,
|
|
241
|
+
`switchSession()`, `waitForIdle()`, `reload()`.
|
|
242
|
+
|
|
243
|
+
**`ctx.mode` and `ctx.hasUI` are how an extension stays honest** across surfaces: an
|
|
244
|
+
extension that calls `ctx.ui.confirm()` unconditionally works in the TUI and hangs in a
|
|
245
|
+
pipeline unless the client implements the UI sub-protocol.
|
|
246
|
+
|
|
247
|
+
## The seams that matter
|
|
248
|
+
|
|
249
|
+
Pi exposes ~30 events. These are the ones that let you implement doctrine this pack
|
|
250
|
+
otherwise only describes:
|
|
251
|
+
|
|
252
|
+
| Event | What it lets you do | Implements |
|
|
253
|
+
|---|---|---|
|
|
254
|
+
| **`tool_call`** — *can block* | refuse a call before it runs, per caller, per argument | the per-hop permission gate of `agent-orchestrator/references/governance.md`; track 6 of `audit.md` |
|
|
255
|
+
| **`tool_result`** — *middleware chain* | rewrite, truncate or annotate a result; handlers see the previous handler's output | tool-output offload and token efficiency (`tools.md`) |
|
|
256
|
+
| **`context`** | modify messages **before** the provider call | the compaction ladder's upper rungs, and typed carryover |
|
|
257
|
+
| **`before_agent_start`** | inject a message, modify the system prompt | capability-aware prompt assembly (`system-prompt.md`) |
|
|
258
|
+
| **`before_provider_headers` / `before_provider_request` / `after_provider_response`** | mutate headers, inspect or replace the payload, handle the response | provider routing, proxying and cost attribution |
|
|
259
|
+
| **`session_before_compact` / `session_compact`** | decide what survives | *preserve decisions and open questions, not the discussion* |
|
|
260
|
+
| **`input`** — *can intercept* | rewrite or absorb a user message | routing before the loop |
|
|
261
|
+
| **`resources_discover`** | add skill/prompt/theme paths at runtime | dynamic capability |
|
|
262
|
+
| **`project_trust`** | act on the trust decision | the delegation boundary of `layers.md` |
|
|
263
|
+
|
|
264
|
+
**`tool_call` blocking is the single most important one for an audit.** It is where a
|
|
265
|
+
per-tool, per-caller policy can actually live in a Pi-based system — so its absence is a
|
|
266
|
+
finding, and its presence is where you read the policy.
|
|
267
|
+
|
|
268
|
+
**Lifecycle discipline**, from Pi's own guidance and worth generalizing: start background
|
|
269
|
+
resources in `session_start` and clean up in `session_shutdown`, **never from the factory**;
|
|
270
|
+
after `/new`, `/resume` or `/fork` a fresh context arrives and **stale `ctx` objects must not
|
|
271
|
+
be reused**; and tools that mutate files should use `withFileMutationQueue()` to avoid
|
|
272
|
+
racing the built-ins.
|
|
273
|
+
|
|
274
|
+
## Custom providers
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// route an existing provider through a proxy — baseUrl and/or headers only,
|
|
278
|
+
// and the existing model list is preserved
|
|
279
|
+
pi.registerProvider("anthropic", { baseUrl: "https://proxy.example.com" });
|
|
280
|
+
|
|
281
|
+
// or a whole new one
|
|
282
|
+
pi.registerProvider("my-llm", {
|
|
283
|
+
baseUrl: "https://api.my-llm.com/v1",
|
|
284
|
+
apiKey: "$MY_LLM_API_KEY",
|
|
285
|
+
api: "openai-completions",
|
|
286
|
+
models: [{
|
|
287
|
+
id: "my-llm-large", name: "My LLM Large", reasoning: true,
|
|
288
|
+
input: ["text", "image"],
|
|
289
|
+
cost: { input: 3.0, output: 15.0, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
290
|
+
contextWindow: 200000, maxTokens: 16384,
|
|
291
|
+
}],
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
For a non-standard API, implement `streamSimple`, pushing an `AssistantMessageEventStream`:
|
|
296
|
+
start → content (text, thinking blocks, tool calls) → done or error, updating usage and cost.
|
|
297
|
+
|
|
298
|
+
**The `cost` block is the hook for everything in
|
|
299
|
+
`agent-orchestrator/references/llm-proxy-billing.md`.** A provider that declares its per-token
|
|
300
|
+
cost makes attribution arithmetic rather than estimation — and a custom provider that omits
|
|
301
|
+
it silently makes every downstream number a guess.
|
|
302
|
+
|
|
303
|
+
Auth supports API keys with env interpolation, and OAuth with refresh, browser and
|
|
304
|
+
device-code flows.
|
|
305
|
+
|
|
306
|
+
## Traps
|
|
307
|
+
|
|
308
|
+
- **Waiting on `agent_end` instead of `agent_settled`**, and acting on a run that continues.
|
|
309
|
+
- **Ignoring `extension_ui_request`** in a non-TUI client, and hanging the first time an
|
|
310
|
+
extension asks a question.
|
|
311
|
+
- **Splitting JSONL on anything but `\n`.** The docs warn about it; the failure is rare,
|
|
312
|
+
data-dependent and looks like corruption.
|
|
313
|
+
- **Reusing a `ctx` after a session replacement.** It points at the old session.
|
|
314
|
+
- **Starting background work in the extension factory** rather than `session_start`, so it
|
|
315
|
+
outlives the session and doubles on reload.
|
|
316
|
+
- **Registering a custom provider with no `cost`**, then trusting the spend numbers.
|
|
317
|
+
- **Assuming an extension is a boundary.** It runs in the Pi process, with the Pi process's
|
|
318
|
+
permissions — see `pi.md` → *trust*.
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Pi — a harness you can read, and what each of its parts implements
|
|
2
|
+
|
|
3
|
+
**Load this when:** you want a **worked example** of the harness doctrine, are choosing a
|
|
4
|
+
kernel to build on, or are auditing a system built on Pi.
|
|
5
|
+
|
|
6
|
+
**Spec pinned:** Pi (`@earendil-works/pi-coding-agent`, MIT, Earendil Inc.), `pi.dev/docs/latest` · read 2026-08-15
|
|
7
|
+
|
|
8
|
+
**Why this file exists.** Everything else in this skill states a rule; Pi is small enough
|
|
9
|
+
to read and complete enough to have made every one of those decisions in public. So each
|
|
10
|
+
section below says **what Pi does** and then **which rule it is an instance of** — the
|
|
11
|
+
value is in the second half. Where Pi disagrees with the doctrine, that is said too.
|
|
12
|
+
|
|
13
|
+
This is not a substitute for `pi.dev`. It moves faster than this file; the stamp above is
|
|
14
|
+
the honest boundary.
|
|
15
|
+
|
|
16
|
+
## Contents
|
|
17
|
+
|
|
18
|
+
- What Pi is, and the stance underneath it
|
|
19
|
+
- Four ways to run it
|
|
20
|
+
- Sessions are a tree, not a log
|
|
21
|
+
- Compaction, with the actual numbers
|
|
22
|
+
- Configuration and precedence
|
|
23
|
+
- Skills, prompts and packages
|
|
24
|
+
- Trust, and the deliberate absence of a sandbox
|
|
25
|
+
- Containerization — three patterns, three threat models
|
|
26
|
+
- Providers and credentials
|
|
27
|
+
- Where Pi and this pack's doctrine differ
|
|
28
|
+
- Traps
|
|
29
|
+
|
|
30
|
+
## What Pi is, and the stance underneath it
|
|
31
|
+
|
|
32
|
+
*"A minimal agent harness."* Its stated position is **primitives, not features**: it ships
|
|
33
|
+
`read`, `write`, `edit`, `bash` and a loop, and deliberately omits sub-agents and plan mode,
|
|
34
|
+
expecting you to build them as extensions.
|
|
35
|
+
|
|
36
|
+
**This is the kernel layer of `layers.md`, made concrete.** The omissions are the argument:
|
|
37
|
+
a kernel that shipped a plan mode would have chosen your planning shape for you. When
|
|
38
|
+
comparing Pi against a workbench, remember the comparison is across layers and will not
|
|
39
|
+
converge.
|
|
40
|
+
|
|
41
|
+
## Four ways to run it
|
|
42
|
+
|
|
43
|
+
| Mode | Invocation | For |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| **Interactive TUI** | `pi` | a human at a terminal |
|
|
46
|
+
| **Print** | `pi -p "…"` | one shot, text out |
|
|
47
|
+
| **JSON** | `pi --mode json "…"` | events as JSON lines, for another tool's UI |
|
|
48
|
+
| **RPC** | `pi --mode rpc` | a long-lived subprocess you drive both ways |
|
|
49
|
+
| **Embedded** | the SDK | your process owns the loop |
|
|
50
|
+
|
|
51
|
+
**One agent, five front doors.** That separation — a core that does not know which surface
|
|
52
|
+
is attached — is the same shape `agent-orchestrator` describes when it insists the loop
|
|
53
|
+
must not know which provider answered. Details of the last three: `pi-sdk.md`.
|
|
54
|
+
|
|
55
|
+
## Sessions are a tree, not a log
|
|
56
|
+
|
|
57
|
+
Sessions persist to `~/.pi/agent/sessions/` as **JSONL, one entry per line**, each carrying
|
|
58
|
+
an 8-character hex `id` and a `parentId`. The current position is a leaf; context is built
|
|
59
|
+
by walking leaf→root.
|
|
60
|
+
|
|
61
|
+
| Entry type | Holds |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `session` | the header: `version` (currently **3**), `id`, `timestamp`, `cwd` |
|
|
64
|
+
| `SessionMessageEntry` | a message with its role and content |
|
|
65
|
+
| `ModelChangeEntry` / `ThinkingLevelChangeEntry` | mid-conversation switches, recorded rather than implied |
|
|
66
|
+
| `CompactionEntry` | a summary, with an optional `retainedTail` |
|
|
67
|
+
| `BranchSummaryEntry` | what an abandoned branch was about |
|
|
68
|
+
| `CustomEntry` / `CustomMessageEntry` | extension data — the second participates in context, the first does not |
|
|
69
|
+
|
|
70
|
+
Commands: `/tree` navigates within one file, `/fork` starts a new session from an earlier
|
|
71
|
+
prompt, `/clone` duplicates the active branch, `/export` writes HTML, `/share` uploads a
|
|
72
|
+
private gist. Flags: `pi -c` continues, `pi -r` browses, `--no-session` keeps nothing.
|
|
73
|
+
|
|
74
|
+
**This implements `agent-orchestrator/references/runtime.md` → *time travel and forking*.**
|
|
75
|
+
That file argues you must be able to fork a past checkpoint and debug **through the real
|
|
76
|
+
loop** rather than a reconstruction. A parent-pointer tree is what makes that cheap: no
|
|
77
|
+
copy, no replay, and the abandoned branch leaves a `BranchSummaryEntry` behind so the
|
|
78
|
+
context is not simply lost.
|
|
79
|
+
|
|
80
|
+
**Two design details worth stealing.** Model and thinking-level changes are *entries*, so a
|
|
81
|
+
session explains its own cost curve. And the version field is honest about migration —
|
|
82
|
+
v1 was linear, v2 introduced the tree, v3 unified role naming.
|
|
83
|
+
|
|
84
|
+
## Compaction, with the actual numbers
|
|
85
|
+
|
|
86
|
+
Auto-compaction fires when `contextTokens > contextWindow - reserveTokens`.
|
|
87
|
+
|
|
88
|
+
| Setting | Default | Meaning |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `reserveTokens` | 16,384 | held back for the response |
|
|
91
|
+
| `keepRecentTokens` | 20,000 | recent tail never summarized |
|
|
92
|
+
|
|
93
|
+
Preserved messages run from `firstKeptEntryId` onward and are sent alongside the summary.
|
|
94
|
+
`/compact [instructions]` runs it manually and the instructions steer the summary. Setting
|
|
95
|
+
`"enabled": false` disables the automatic path while leaving the manual one.
|
|
96
|
+
|
|
97
|
+
**Two caveats stated in the docs and worth carrying:** tool results are **truncated to 2,000
|
|
98
|
+
characters** while summarizing, and a turn larger than `keepRecentTokens` produces two
|
|
99
|
+
summaries that are then merged.
|
|
100
|
+
|
|
101
|
+
**This is the ladder from `agent-orchestrator/references/context-engineering.md` with one
|
|
102
|
+
rung.** Pi reserves, keeps a tail, and summarizes the rest. What that file adds and Pi
|
|
103
|
+
leaves to you: clearing old tool results *before* paying a summarizer, offloading a large
|
|
104
|
+
tool result to a file and keeping the path, and **typed carryover** — the observation that a
|
|
105
|
+
summarizer keeps the discussion and drops the state. Pi's `BranchSummaryEntry` and
|
|
106
|
+
`retainedTail` are the seams to hang that on.
|
|
107
|
+
|
|
108
|
+
## Configuration and precedence
|
|
109
|
+
|
|
110
|
+
| File | Scope |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `~/.pi/agent/settings.json` | global |
|
|
113
|
+
| `.pi/settings.json` | project — **overrides global, merging nested objects** |
|
|
114
|
+
|
|
115
|
+
Keys cluster into model and thinking (`defaultProvider`, `defaultModel`,
|
|
116
|
+
`defaultThinkingLevel`, `thinkingBudgets`), UI, network and retry (`retry` with `enabled`,
|
|
117
|
+
`maxRetries`, `baseDelayMs`; `httpProxy`, `transport`, timeouts), content handling
|
|
118
|
+
(`shellPath`, `npmCommand`, `defaultTools`), and resources (`packages`, `extensions`,
|
|
119
|
+
`skills`, `prompts`, `themes`).
|
|
120
|
+
|
|
121
|
+
**Merge, not replace, is the part that matters.** A project that wants one different model
|
|
122
|
+
should not have to restate the whole file — and a harness that replaced wholesale would
|
|
123
|
+
make every project config a copy that drifts.
|
|
124
|
+
|
|
125
|
+
## Skills, prompts and packages
|
|
126
|
+
|
|
127
|
+
**Pi implements the Agent Skills standard**, with progressive disclosure: at startup it
|
|
128
|
+
scans skill locations and takes only `name` and `description` into the system prompt as XML;
|
|
129
|
+
the full `SKILL.md` loads when a task matches.
|
|
130
|
+
|
|
131
|
+
It discovers skills from `~/.pi/agent/skills/`, **`~/.agents/skills/`**, `.pi/skills/` and
|
|
132
|
+
`.agents/skills/` (project paths only after the project is trusted), from packages, from a
|
|
133
|
+
`skills` array in settings, and from `--skill <path>`.
|
|
134
|
+
|
|
135
|
+
> **Concretely relevant here: `~/.agents/skills/` is the ssheleg hub.** On the machine this
|
|
136
|
+
> file was written on, that directory holds 72 entries including every family skill, each
|
|
137
|
+
> with the `name` and `description` front matter Pi requires — so the family is already in a
|
|
138
|
+
> directory Pi reads. **Not verified by running Pi**, which is not installed here; this is a
|
|
139
|
+
> statement about the path and the front matter, not an observation of a load.
|
|
140
|
+
|
|
141
|
+
**Pi documents one deliberate divergence from the standard:** it allows a skill's `name` to
|
|
142
|
+
differ from its directory, calling that rule *"suboptimal for shared skill directories used
|
|
143
|
+
across multiple agent harnesses."* Which is exactly what `~/.agents/skills/` is. Note the
|
|
144
|
+
asymmetry before relying on it — `make-skill`'s validator enforces the strict rule, so a
|
|
145
|
+
skill built to Pi's leniency fails the family gate.
|
|
146
|
+
|
|
147
|
+
**Prompt templates** are Markdown in `~/.pi/agent/prompts/*.md`; the filename becomes the
|
|
148
|
+
command (`review.md` → `/review`). Front matter takes `description` and `argument-hint`
|
|
149
|
+
(`<required>`, `[optional]`). Arguments substitute as `$1`, `$@` / `$ARGUMENTS`,
|
|
150
|
+
`${1:-default}`, `${@:N}` and `${@:N:L}`. Discovery is **not recursive**.
|
|
151
|
+
|
|
152
|
+
**Packages** bundle extensions, skills, prompts and themes over npm or git, declared under a
|
|
153
|
+
`pi` key in `package.json` or by convention (`extensions/`, `skills/`, `prompts/`,
|
|
154
|
+
`themes/`). Installed with `pi install npm:@foo/bar@1.0.0`, `git:…`, an https URL, or a
|
|
155
|
+
path; `-l` writes to project settings for a team. Resource lists take globs with `!`
|
|
156
|
+
exclusions, `[]` for none, `+path` / `-path` to force.
|
|
157
|
+
|
|
158
|
+
## Trust, and the deliberate absence of a sandbox
|
|
159
|
+
|
|
160
|
+
Pi *"runs with the permissions of the user account that starts it"* and treats files that
|
|
161
|
+
user can write as inside the same trust boundary.
|
|
162
|
+
|
|
163
|
+
**Project trust** is asked for when a repository carries `.pi/settings.json`, local
|
|
164
|
+
extensions, skills, prompts or themes, a `.pi/SYSTEM.md` or `.pi/APPEND_SYSTEM.md`, or
|
|
165
|
+
project agent skills in ancestor directories. Decisions persist in `~/.pi/agent/trust.json`.
|
|
166
|
+
|
|
167
|
+
What it buys, in the docs' own words: it *"prevents a repository from silently changing pi's
|
|
168
|
+
settings or extensions before you approve it"* — and explicitly **does not** protect against
|
|
169
|
+
untrusted code, prompts, or model output.
|
|
170
|
+
|
|
171
|
+
**There is no built-in sandbox, on purpose.** The stated reasons: a partial in-process
|
|
172
|
+
sandbox creates false assumptions, real isolation needs an OS or container boundary, and Pi
|
|
173
|
+
is meant to invoke project toolchains with full local access. And the sentence worth
|
|
174
|
+
quoting to anyone who claims otherwise about any harness:
|
|
175
|
+
|
|
176
|
+
> *"prompt injection from repository files, comments, documentation, context files, or build
|
|
177
|
+
> output is expected local-agent risk and cannot be reliably prevented by pi."*
|
|
178
|
+
|
|
179
|
+
**This is `layers.md` → *what a harness should delegate*, stated by the project itself.** A
|
|
180
|
+
harness that also claimed to be a sandbox would be claiming a guarantee it cannot keep from
|
|
181
|
+
inside the same process. **For an audit this changes the finding**: "no permission model" is
|
|
182
|
+
not a defect here, it is a delegation — so audit what surrounds the process (`audit.md`,
|
|
183
|
+
track 6).
|
|
184
|
+
|
|
185
|
+
## Containerization — three patterns, three threat models
|
|
186
|
+
|
|
187
|
+
| Pattern | Isolates | Credentials live |
|
|
188
|
+
|---|---|---|
|
|
189
|
+
| **Gondolin extension** | built-in tools and `!` commands, in a micro-VM | on the host — auth never enters the boundary |
|
|
190
|
+
| **Plain Docker** | the whole Pi process | **inside the container** |
|
|
191
|
+
| **OpenShell** | filesystem, process, network, credentials by policy | per policy; local or remote gateway |
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
docker run --rm -it -e ANTHROPIC_API_KEY -v "$PWD:/workspace" pi-sandbox
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**The distinction that decides which you want: extensions execute wherever the Pi process
|
|
198
|
+
runs.** Host-side Pi routing tools into a micro-VM keeps auth local and isolates execution;
|
|
199
|
+
containerized Pi needs the key inside the boundary. Mounting `/root/.pi/agent` as a named
|
|
200
|
+
volume keeps settings isolated — mounting your host directory *"exposes host auth and
|
|
201
|
+
session files to the container"*, which is the opposite of the intent.
|
|
202
|
+
|
|
203
|
+
## Providers and credentials
|
|
204
|
+
|
|
205
|
+
Two paths: **subscription OAuth** via `/login` (ChatGPT Plus/Pro, Claude Pro/Max, GitHub
|
|
206
|
+
Copilot, xAI, OpenRouter, Radius) with refresh handled, and **API keys**. 30+ providers.
|
|
207
|
+
|
|
208
|
+
Resolution order — **CLI `--api-key` → `auth.json` → environment variable → custom provider
|
|
209
|
+
keys in `models.json`**. `auth.json` is written `0600` and takes priority over the
|
|
210
|
+
environment, which is the ordering you want: an explicit file beats an inherited variable.
|
|
211
|
+
|
|
212
|
+
Keys support literals, `$ENV_VAR` interpolation, and shell commands
|
|
213
|
+
(`!security find-generic-password …`) — so a key can live in a system keychain rather than a
|
|
214
|
+
file. Registering a custom provider is `pi-sdk.md`.
|
|
215
|
+
|
|
216
|
+
## Where Pi and this pack's doctrine differ
|
|
217
|
+
|
|
218
|
+
Named rather than smoothed over:
|
|
219
|
+
|
|
220
|
+
- **No iteration guard is documented as a first-class setting.** `agent-orchestrator` treats
|
|
221
|
+
a bounded loop as non-negotiable. Pi has `auto_retry` and abort; a max-iteration ceiling
|
|
222
|
+
is yours to add. Check this first when auditing a Pi-based system.
|
|
223
|
+
- **No sub-agents.** Deliberate. `agent-orchestrator`'s sub-agent protocol and
|
|
224
|
+
`techniques.md`'s "distilled summary, never a transcript" are things you build here.
|
|
225
|
+
- **Skill naming leniency** contradicts the standard `make-skill` enforces (above).
|
|
226
|
+
- **Compaction is one rung**, not the ladder.
|
|
227
|
+
|
|
228
|
+
None of these is a defect in a kernel. They are the difference between a harness and a
|
|
229
|
+
platform, and they are the work you are signing up for.
|
|
230
|
+
|
|
231
|
+
## Traps
|
|
232
|
+
|
|
233
|
+
- **Reading the omissions as gaps.** They are the layer boundary. If you need all of them
|
|
234
|
+
filled, you wanted a workbench.
|
|
235
|
+
- **Assuming trust means safety.** It means the repository did not silently change your
|
|
236
|
+
configuration. Nothing more, and the docs say so.
|
|
237
|
+
- **Mounting your host `~/.pi/agent` into a container** and calling the result isolated.
|
|
238
|
+
- **Building on skill-name leniency** and then failing a stricter harness's validator.
|
|
239
|
+
- **Forgetting `keepRecentTokens` against a large turn.** One turn bigger than the budget
|
|
240
|
+
becomes two summaries merged — surprising if you are diffing summaries.
|
|
241
|
+
- **Treating the docs here as current.** Check the stamp; Pi moves.
|