Kai eaa3f6a8a1 feat(curator): tool extension, generated prompt check, two pi findings
curator-tools.ts registers no schema of its own: it fetches the specs from the
backend bridge, so contracts.py stays the single owner and there is no TypeScript
copy to drift. It refuses to activate without a bridge URL and token, because an
agent that silently loses its tools still answers -- from the model's memory of
what a media library might contain.

deploy-scenario.sh now vendors listed shared/extensions modules into
.pi/extensions/_shared/. A tracked extension importing from shared/ cannot
resolve that path once installed outside the repository, so the deployed tree has
to be self-contained; this overwrites rather than merges, keeping the repository
authoritative. common.sh gains toml_list, using tomllib rather than more awk
because an array can span lines or carry comments.

verify-generated.sh checks that generated regions in tracked prompts match the
backend that generates them, and is wired into the pre-commit hook. This is
needed because of finding 22 below: the tool list has to be copied into the
prompt, and a copy drifts silently.

Two findings recorded in docs/pi-runtime-notes.md, both measured:

  21. An extension that fails to import is silent -- exit 0, empty stderr, no
      tools. A missing --extension path exits 1 with a clear message, but a
      module that throws while loading reports nothing. The agent then invented a
      complete library listing with plausible episode counts, quality and size.
      A later identical run said it had no data instead, so the failure is both
      silent and inconsistent.

  22. --system-prompt suppresses the tool list. The customPrompt branch returns
      before toolsList and guidelines are built, so promptSnippet and
      promptGuidelines are inert. The tools stay callable over the provider API,
      so tool use becomes a coin flip: one run in four looked at the library and
      three said they had not been given any results.

SYSTEM.phase3.md is staged alongside the deployed SYSTEM.md rather than replacing
it: profile.toml still describes the phase-0 configuration that is actually
running, and the live service is untouched.
2026-08-28 00:35:04 -07:00

pi-agent-config

Authoritative configuration for every Pi agent on this host: the user-level Pi runtime, plus one directory per dedicated agent scenario.

The live locations (~/.pi/agent, ~/pi-workspaces/*) are runtime copies. Changes are made here and pushed out with scripts/deploy-*.sh, never the other way round — except for scenarios explicitly marked deploy = "mirror", which record what a running service already does.

Verified against pi 0.84.3. Re-run the probes after every pi update.

Scenarios

Scenario Purpose Service Status
curator Book / film / TV / music curation agent curator.service target config written, not yet deployed
memo-inbox Routes Telegram/WeChat messages to Calendar, Obsidian todo or journal pi-memo-telegram.service mirror of live config, zero behaviour change
pi-grok Interactive Grok 4.6 coding agent none (manual) registered only

Start here

Document Contents
docs/pi-runtime-notes.md pi 0.84.3 mechanics that the official docs understate, each verified against source or a live probe
docs/isolation-baseline.md The four isolation layers and the flag set every scenario should use
docs/personality-layering.md How to choose between SYSTEM.md, APPEND_SYSTEM.md, AGENTS.md, SKILL.md and the request
docs/gateway-patterns.md Ten patterns for hosting an agent behind a long-running service
docs/plans/ Active work plans

The finding that motivated this repository

pi emits the <available_skills> block only when a tool named read is active (dist/core/system-prompt.js:59,113). The Curator service ran with --no-tools --skill … for its entire lifetime, so:

  • its 64-line media policy never reached the model,
  • --skill was a no-op,
  • and both the README and the deployment runbook described a mechanism that was not happening.

Measured on the real workspace, before and after (evidence):

before after
system prompt 2548 chars 3539 chars
expert coding assistant framing present gone
pointer to pi's own documentation present gone
the 64-line media policy absent present
<available_skills> absent absent
workspace AGENTS.md loaded blocked
parent-directory AGENTS.md leaked blocked

The prompt got larger, and that is the fix rather than its cost: of the original 2548 characters roughly 1.9 KB was pi's coding-assistant scaffolding and a pointer telling the agent to read pi's documentation and follow its cross-references — noise for a media agent, and a ready-made escalation path for injected text — while none of it was Curator's own policy. All 3539 characters now are.

A mechanism demonstration with a stub SYSTEM.md is in docs/evidence/2026-08-27-isolation-probe.md; its 960-character figure measures the stub, not Curator.

Reproduce either with docs/evidence/probe-harness/ — zero model tokens, because a single RPC get_state starts the agent, fires session_start and exits without contacting the provider.

Layout

docs/            mechanics, baselines, plans, reproducible evidence
runtime/agent/   ~/.pi/agent — settings, models.json.template, extensions, prompts
shared/          cross-scenario code
  lib/py/pi_rpc.py            long-lived RPC client with rotation and budgets
  extensions/pi-guard-base.ts path containment, restricted read, capability guard
scenarios/<name>/
  profile.toml   single source of truth for the launch contract
  workspace/     what gets installed into the live workspace
  eval/          recorded golden transcripts
scripts/         diff, deploy, backup, restore, secret guard
secrets/         host-local, untracked

Usage

# What differs between this repository and the host?
scripts/pi-diff.sh
scripts/pi-diff.sh curator

# Push configuration out (dry run first; neither restarts a service)
scripts/deploy-runtime.sh
scripts/deploy-runtime.sh --apply
scripts/deploy-scenario.sh curator --apply

# Back up the live installation (archives land outside this repository)
scripts/pi-backup.sh
scripts/pi-restore.sh --from <backup dir>

# Verify the mechanics still hold after a pi upgrade
docs/evidence/probe-harness/collect-evidence.sh
shared/extensions/tests/run-guard-checks.sh
python3 shared/lib/py/tests/test_pi_rpc_smoke.py

Secrets

Nothing real ever enters this repository. models.json, auth.json, trust.json and every *.env are ignored; secrets/ accepts only .gitkeep, README.md, *.example and *.template.

scripts/verify-no-secrets.sh enforces this as a pre-commit hook. Install it in a fresh clone:

ln -sf ../../scripts/verify-no-secrets.sh .git/hooks/pre-commit
scripts/verify-no-secrets.sh --all

Backups contain plaintext credentials and are written outside the work tree; pi-backup.sh refuses any destination that resolves inside it.

First-time setup on a new host

git clone gitea-45:kai/pi-agent-config.git && cd pi-agent-config
ln -sf ../../scripts/verify-no-secrets.sh .git/hooks/pre-commit

cp secrets/zenmux.env.example secrets/zenmux.env
chmod 600 secrets/zenmux.env && $EDITOR secrets/zenmux.env

npm install -g @earendil-works/pi-coding-agent@0.84.3
scripts/deploy-runtime.sh --apply
scripts/deploy-scenario.sh <scenario> --apply
S
Description
定制不同用途的 Pi Agent
Readme
551 KiB
Languages
Python 86.4%
TypeScript 7.8%
Shell 5.8%