Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Deeper rationale: [docs/why.md](docs/why.md) · companion book: [agenticoding.ai
- **Spawn** — run research or implementation in a clean child context so the parent stays focused
- **Model Groups** — manage durable project/global model pools with `/model-groups`; route `spawn` by an exact group name, with `#group` autocomplete showing model/thinking details
- **Notebook** — task-scoped named pages for facts and decisions; survives handoff, dies with the conversation (`/new`) — no forever-memory rot
- **Handoff** — deliberate clean restart with a task brief when the job changes or context turns to noise
- **Handoff** — deliberate clean restart with a task prompt when the job changes or context turns to noise
- **Topic** — same problem → prefer spawn; new problem → prefer handoff (human-set topics win)
- **Readonly** — explore and plan without writing the tree (`/readonly`, Ctrl+Shift+R, or `--readonly`); macOS/Linux can OS-sandbox bash, Windows is classifier-only
- **Visibility** — status bar shows context pressure, notebook count, topic, and readonly; warning at high usage
Expand Down Expand Up @@ -77,8 +77,8 @@ The agent set a topic, spawned research, saved decisions, delegated implementati
| | |
|---|---|
| **Spawn** | Subtask in a clean child context. Parent orchestrates; siblings run in parallel. Children inherit active registered parent tools executable in the child session — MCP/extension tools such as ChunkHound — plus child-local notebook tools. Children cannot spawn grandchildren or handoff. Omit `group` to inherit the parent model/thinking. An unknown group reports fallback to the parent. A known group randomly selects among configured/authenticated usable entries and fails before child creation if none are usable. The selected entry supplies the model and, when configured, overrides explicit/inherited thinking before Pi clamps it; the final selected public model runs in the child-owned runtime. |
| **Notebook** | Named pages coupled to this conversation/task. Carries grounding across handoff; cleared on `/new`. Not a long-lived memory store — lifetime matches the work, so it cannot go stale across unrelated sessions. |
| **Handoff** | Write a brief, compact, resume clean. Notebook holds reusable grounding for this task; the brief holds only remaining situational context. |
| **Notebook** | Named pages coupled to this conversation/task. Carries memory across handoff; cleared on `/new`. Not a long-lived memory store — lifetime matches the work, so it cannot go stale across unrelated sessions. |
| **Handoff** | Write a prompt, compact, resume clean. Notebook holds reusable memory for this task; the prompt holds only remaining situational context. |
| **Readonly** | Blocks write/edit and guards bash while researching. Spawn inherits the posture. **macOS/Linux:** bash can run under OS sandbox (`sandbox-exec` / `bwrap`) — syscall-level write denial outside temp. **Windows:** no OS sandbox — **best-effort command classifier only** (interpreters and clever pipes can bypass). A coding guardrail on every OS — not a hardened security boundary. |

**Commands:** `/handoff` · `/notebook` · `/notebook <topic>` · `/readonly` · `Ctrl+Shift+R` · `--readonly`
Expand All @@ -90,7 +90,7 @@ The agent set a topic, spawned research, saved decisions, delegated implementati
| Platform auto-compaction | Runtime (late threshold) | Blunt lossy summary |
| `/compact` or `/clear` | User (timing + steer) | Lossy summarizer pass / paste |
| Forever “memory” stores | Background / RAG | Accumulates, goes stale, needs invalidation |
| **pi-agenticoding** | **Agent** | **Task-scoped notebook + handoff brief** |
| **pi-agenticoding** | **Agent** | **Task-scoped notebook + handoff prompt** |

## Learn more

Expand Down
14 changes: 14 additions & 0 deletions audit-ci.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@
"$schema": "https://github.com/IBM/audit-ci/raw/main/docs/schema.json",
"moderate": true,
"allowlist": [
// GHSA-f38q-mgvj-vph7: protobufjs DoS via infinite loop in .proto parsing.
// Suppressed by advisory ID; the config-invariant test separately rejects
// any vulnerable protobufjs outside the exact Pi 0.82.0 development path.
{ "GHSA-f38q-mgvj-vph7": { "active": true, "expiry": "2026-09-01", "notes": "protobufjs 7.6.1 only via @earendil-works/pi-ai > @google/genai in the exact Pi 0.82.0 development graph" } },
// GHSA-3jxr-9vmj-r5cp: brace-expansion DoS via exponential-time expansion
// of consecutive non-expanding {} groups. In pi-coding-agent's transitive
// dependency tree (minimatch > brace-expansion). Not a direct dependency;
// no user-controlled input reaches this code path.
{ "GHSA-3jxr-9vmj-r5cp": { "active": true, "expiry": "2026-09-01", "notes": "brace-expansion <5.0.7 via @earendil-works/pi-coding-agent > minimatch; not a direct dependency" } },
// GHSA-j3f2-48v5-ccww: protobufjs Denial of Service via infinite loop in
// .proto option parsing. Same transitive path as the existing protobufjs
// advisory (pi-ai > @google/genai > protobufjs) but a distinct advisory.
{ "GHSA-j3f2-48v5-ccww": { "active": true, "expiry": "2026-09-01", "notes": "protobufjs <=7.6.4 via @earendil-works/pi-ai > @google/genai; second protobufjs advisory on same path" } },
// npm's audit report exposes this shrinkwrapped advisory as the module path
// `brace-expansion`; a config invariant separately rejects any vulnerable
// occurrence outside Pi coding agent's exact 0.82.0 shrinkwrapped path.
{ "GHSA-mh99-v99m-4gvg|brace-expansion": { "active": true, "expiry": "2026-09-01", "notes": "brace-expansion 5.0.7 only in @earendil-works/pi-coding-agent's shrinkwrap via minimatch; consumer overrides cannot replace it, so remove when Pi publishes brace-expansion 5.0.8+" } }

]
}
4 changes: 2 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ interface AgenticodingState {

**Notebook** — Agent-curated named pages **scoped to the current conversation/task**, not a long-lived memory product. Stored as session custom entries so pages survive handoff and resume of the same work stream; `/new` (fresh session) clears them with the conversation. That coupling avoids the stale-entry / invalidation problem of forever-memory systems. Active topic (`notebook_topic_set` or `/notebook <topic>`) frames spawn-vs-handoff preference; human-set topics are authoritative. Topic clears after a successful handoff.

**Handoff** — Requires a real brief and a meaningful context load (rejects empty briefs, very small sessions, or missing usage). Notebook bodies are not inlined into the brief; the next context in this work stream fetches pages by name. Under readonly, handoff is blocked unless the user runs `/handoff` or crosses an eligible human topic boundary; readonly can resume after compaction. Compaction replaces the prior transcript with the brief: the next turns see a small context again (quality), and providers start a new input prefix for billing/cache (the dropped history is no longer in that prefix). Spawn runs children in separate context so their token use does not permanently inflate the parent. This extension does not configure provider cache TTLs or breakpoints.
**Handoff** — Requires a real prompt and a meaningful context load (rejects empty prompts, very small sessions, or missing usage). Notebook bodies are not inlined into the prompt; the next context in this work stream fetches pages by name. Under readonly, handoff is blocked unless the user runs `/handoff` or crosses an eligible human topic boundary; readonly can resume after compaction. Compaction replaces the prior transcript with the prompt: the next turns see a small context again (quality), and providers start a new input prefix for billing/cache (the dropped history is no longer in that prefix). Spawn runs children in separate context so their token use does not permanently inflate the parent. This extension does not configure provider cache TTLs or breakpoints.

**Readonly** — Session-persisted research posture. Toggle via `/readonly`, Ctrl+Shift+R, or `--readonly`. Skills/prompts may set `readonly: true` in frontmatter to defer-enable when invoked. Write/edit always blocked at the tool boundary. Bash uses a two-layer guard:

Expand All @@ -68,7 +68,7 @@ Coding-agent guardrail on every OS — not a hardened security boundary. Stronge
| `spawn/` | Child sessions and live TUI rendering |
| `model-groups/` | Persistence, boot validation, CRUD TUI/autocomplete, and spawn routing |
| `notebook/` | Page store, tools, topic, rehydration |
| `handoff/` | Eligibility, brief, compaction bridge |
| `handoff/` | Eligibility, prompt, compaction bridge |
| `readonly-*.ts` / `os-sandbox.ts` | Readonly posture, bash policy, sandbox |
| `watchdog.ts` / `tui.ts` / `state.ts` | Pressure advisories, status UI, shared state |

Expand Down
8 changes: 4 additions & 4 deletions docs/why.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,21 @@ All three manage context **around** the model. The agent stays a passive recipie
| Move | Primitive | Prevents |
|---|---|---|
| **Isolate** | Spawn | Noisy subtasks polluting the parent |
| **Ground** | Notebook | Losing reusable knowledge across deliberate cuts *in the same task* |
| **Remember** | Notebook | Losing reusable knowledge across deliberate cuts *in the same task* |
| **Compact** | Handoff | Waiting on `/compact`, late auto-summarize, or one mixed summary blob |
| **Guard** | Readonly | Accidental edits during research and planning (write/edit blocked everywhere; bash OS-sandboxed on macOS/Linux — **Windows is classifier-only, not syscall-level**) |

### Notebook is not “memory
### Notebook is task-scoped shared memory

Standard agent memory systems try to be **long-lived**: they accumulate facts across days and projects, then rot. Stale entries, conflicting truths, and cache invalidation become the product.

The notebook is deliberately the opposite. It is **coupled to the conversation/task**:

- Pages carry grounding across **handoff** and resume of *this* work stream
- Pages carry memory across **handoff** and resume of *this* work stream
- **`/new` (or a new session) clears everything** with the conversation
- Nothing is shared into the next unrelated job unless the agent writes it again on purpose

So the agent can keep facts, decisions, constraints, and expensive findings **without** building a forever store that needs invalidation. Handoff still splits concerns: the notebook holds reusable grounding *for this task*; the brief holds only remaining situational context. That beats one summary blob that mixes both — and beats external memory that outlives the work and goes stale.
So the agent can keep facts, decisions, constraints, and expensive findings **without** building a forever store that needs invalidation. Handoff still splits concerns: the notebook holds reusable memory *for this task*; the prompt holds only remaining situational context. That beats one summary blob that mixes both — and beats external memory that outlives the work and goes stale.

## Awareness, not autopilot

Expand Down
6 changes: 3 additions & 3 deletions handoff/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* /handoff command for the agenticoding extension.
*
* Collects a user direction, asks the LLM to complete the picture in a
* handoff brief, and lets the handoff tool perform the actual compaction.
* handoff prompt, and lets the handoff tool perform the actual compaction.
*/

import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
Expand All @@ -18,7 +18,7 @@ import { STATUS_KEY_HANDOFF } from "../tui.js";
export function registerHandoffCommand(pi: ExtensionAPI, state: AgenticodingState): void {
pi.registerCommand("handoff", {
description:
"Ask the LLM to draft a handoff brief that completes the picture from " +
"Ask the LLM to draft a handoff prompt that completes the picture from " +
"your direction, then perform the handoff automatically.",

handler: async (args, ctx) => {
Expand Down Expand Up @@ -63,7 +63,7 @@ export function registerHandoffCommand(pi: ExtensionAPI, state: AgenticodingStat
: "\n\nA real handoff is required in the current session. Do not continue normal work instead.";

pi.sendUserMessage(
`Handoff direction: ${direction}\n\nPrepare a handoff in the current session now. First, save any durable reusable knowledge that aligns with the direction above to the notebook: findings worth keeping, constraints discovered, decisions made, or other grounding future contexts will need. Then draft a concise but sufficiently detailed handoff brief capturing only the remaining situational context: current state, blockers, unresolved questions, failed paths worth avoiding, and next steps. The next context will read the notebook on demand, so do not duplicate notebook content in the brief. Use any structure that makes the next work unambiguous. Reference notebook pages by name when relevant.${readonlyNotice}`,
`Handoff direction: ${direction}\n\nPrepare a handoff in the current session now. First, save any durable reusable knowledge that aligns with the direction above to the notebook: findings worth keeping, constraints discovered, decisions made, or other durable memory needed by future contexts. Then draft a concise but sufficiently detailed handoff prompt capturing only the remaining situational context: current state, blockers, unresolved questions, failed paths worth avoiding, and next steps. The next context will read the notebook on demand, so do not duplicate notebook content in the prompt. Use any structure that makes the next work unambiguous. Reference notebook pages by name when relevant.${readonlyNotice}`,
ctx.isIdle() ? undefined : { deliverAs: "followUp" },
);
},
Expand Down
2 changes: 1 addition & 1 deletion handoff/compact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function registerHandoffCompaction(pi: ExtensionAPI, state: AgenticodingS
// pendingHandoff — cleared here (the compaction hook consumed the queued task)
// pendingRequestedHandoff — kept; cleared later by completeHandoff in tool.ts
// (on success) or preserved for retry (on error).
// Read readonlyEnabled at the cut so the brief reflects a toggle made after
// Read readonlyEnabled at the cut so the prompt reflects a toggle made after
// the handoff tool was called but before Pi consumes the queued task.
const task = buildEnrichedTask(pending.task, {
resumeReadonlyAfterHandoff: state.readonlyEnabled,
Expand Down
8 changes: 4 additions & 4 deletions handoff/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ import {
/**
* Build the enriched task that becomes the compaction summary.
*
* Shape: handoff primer + original task.
* Shape: handoff prompt + original task.
*/
export function buildEnrichedTask(task: string, options?: { resumeReadonlyAfterHandoff?: boolean }): string {
const parts: string[] = [
"## Handoff — Continue Previous Work",
"",
"You are continuing a previous agent's work in a clean context. Use the available knowledge correctly:",
"- Notebook pages hold durable grounding knowledge; fetch them with `notebook_read`",
"- This handoff brief holds the distilled next task and immediate situational context",
"- Notebook pages hold durable memory; fetch them with `notebook_read`",
"- This handoff prompt holds the distilled next task and immediate situational context",
"- Use `notebook_index` to scan available pages when needed",
"- Use `spawn` to delegate isolated subtasks to child agents",
"- Build on notebook grounding and this brief rather than reconstructing old context",
"- Build on notebook memory and this prompt rather than reconstructing old context",
];

if (options?.resumeReadonlyAfterHandoff) {
Expand Down
Loading
Loading