Commit Graph
6 Commits
Author SHA1 Message Date
Kai 07dd648b5f feat: scenarios for curator/memo-inbox/pi-grok, deploy and backup tooling
Scenarios
- memo-inbox: mirrored by copying; the live directory was not moved or modified
  and the service was not restarted. All four tracked files match byte for byte
  (pi-diff.sh reports SAME). Marked deploy = "mirror" so deploy-scenario.sh
  refuses --apply: applying a mirror would invert the direction of truth and
  could change a service in daily use.
- curator: target configuration, not yet deployed. .pi/SYSTEM.md replaces pi's
  coding-assistant prompt; durable role text is in .pi/APPEND_SYSTEM.md;
  profile.toml is the single source of truth for the launch contract.
- pi-grok: registered only. It is genuinely a coding agent, so the isolation
  baseline does not apply in full.

Corrections to the documentation, found by testing rather than by reading
- AGENTS.override.md does NOT block parent-directory context files; it only
  shadows its own directory. Verified: with an override file in the workspace, a
  marker in /tmp/AGENTS.md still reached the system prompt. The only effective
  switch is --no-context-files, so durable role text must live in
  .pi/APPEND_SYSTEM.md, which is a system-prompt file and unaffected by -nc.
  Verified end state: no coding-assistant framing, no pi-docs block, own
  identity and role text present, no parent pollution, only own skills/tools.
- PI_CODING_AGENT_DIR isolates settings/models/auth/trust/extensions/skills/
  prompts/themes under the agent directory -- stronger than the --no-* flags
  because it also repoints credentials -- but does NOT cover ~/.agents/skills.
  Measured: find-skills, modsearch and summarize still leak. So it complements
  --no-skills rather than replacing it.
- --append-system-prompt accepts a file path, which pi-grok relies on.
- cwd is what anchors .pi discovery: a probe that forgot cwd silently lost
  .pi/SYSTEM.md and kept the coding-assistant persona.

Tooling (all dry-run by default; none of them restarts a service)
- pi-diff.sh: compares tracked config against the live install in both
  directions, with a key-redacted comparison for models.json
- deploy-scenario.sh: installs a workspace and renders profile.toml into
  .pi/launch.json, then checks that every referenced path exists
- deploy-runtime.sh: renders models.json from its template, refusing placeholder
  or missing keys. Verified byte-identical to the live file
- pi-backup.sh / pi-restore.sh: archives outside the repo, sha256 manifest
  verified before any restore, live paths preserved rather than overwritten

Fixed while testing: pi-backup.sh compared the destination against the repo root
literally, so a relative --dest ./backups wrote credential archives into the work
tree. Now canonicalised with realpath; ./backups, an absolute in-repo path and
./docs/../backups are all refused.
2026-08-26 23:17:12 -07:00
Kai 7b5e0b093d feat(shared): pi-guard-base, reusable isolation primitives for scenario extensions
Extracted from pi-workspaces/memo-inbox/.pi/extensions/memo-guard.ts, which has
enforced these patterns in production since 2026-07.

Exports:
- inside() / safeRealPath() / makePathResolver(): path containment that resolves
  symlinks before checking, so a link inside an allowed root cannot escape it
- registerRestrictedRead(): a path-restricted 'read' that shadows the built-in.
  Required rather than optional: pi emits the skills block only when a tool named
  'read' is active and skill bodies load through it, while the built-in 'read'
  accepts absolute paths and could reach the service's credential files
- installGuard(): the two capability layers, setActiveTools plus a tool_call
  block, re-asserted on resources_discover as well as session_start
- truncate(): byte-aware truncation ahead of pi's 50 KB / 2000 line caps
- registerBridgeTools() / fetchBridgeSpecs(): loopback HTTP bridge, with the
  baseUrl asserted to be loopback. Since registerTool accepts a plain JSON
  Schema object, the backend can own the schema instead of a drifting copy

Verified against a real pi process with zero model tokens
(shared/extensions/tests/run-guard-checks.sh, 14 assertions):
active tools are exactly the declared set, the read override wins with
source=cli, the skills section is present and contains only the scenario's own
skill, and reads of an outside file, a ../ traversal and an absolute path to
~/.config/curator/curator.env are all denied.

The deny-path fixture is named .txt and renamed to .env only inside the temp
work directory, because verify-no-secrets.sh correctly refused to track a file
called *.env.sample.
2026-08-26 22:57:52 -07:00
Kai 65d2f5988b feat(shared): long-lived Pi RPC client extracted from the memo-inbox gateway
Generalises PiRPC from pi-workspaces/memo-inbox/telegram-gateway/gateway.py,
which has run this pattern in production since 2026-07, and closes the four gaps
both existing scenarios shared:

- explicit minimal env, so provider and backend API keys never reach the node
  process (verified: 6 variables, an injected secret is withheld)
- start_new_session plus killpg on stop, so a stuck node tree cannot outlive the
  turn (verified: no orphan after stop)
- the loading-isolation flags are part of the launch contract instead of
  something each caller has to remember
- a per-turn deadline enforced with RPC abort rather than by killing the process

Retains the original's proven mechanics: strict newline-only JSONL framing,
correlation by id, agent_settled as terminal event, and receipts harvested from
tool_execution_end rather than from model prose.

PiLaunchConfig warns when skills are configured but no 'read' tool can be
active, which is exactly the condition that silently disabled Curator's SKILL.md.

Includes a zero-token smoke test: it drives a real pi process with get_state
only, so no model call is billed.
2026-08-26 22:55:21 -07:00
Kai 98635022d0 feat(runtime): track user-level Pi configuration with the provider key templated
Mirrors ~/.pi/agent/ as the authoritative copy. models.json becomes
models.json.template with ${ZENMUX_API_KEY} substituted; the real value stays
in secrets/zenmux.env, which is untracked and enforced by the pre-commit guard.

Excluded with rationale: auth.json, trust.json, models-store.json, sessions/,
herdr-agent-state.ts (installer-managed, overwritten on reinstall) and the
third-party skills under ~/.agents/skills.

Recorded during migration: the configured fallback model zenmux/x-ai/grok-4.6 is
absent from models.json, so pi falls back to an undeclared custom model id with
no context window, cost table or thinkingLevelMap. Fixing that is a behaviour
change and is deferred rather than folded into this zero-change migration.
2026-08-26 22:52:48 -07:00
Kai 734e63aa28 fix(verify-no-secrets): resolve repo root via git, allow scaffolding under secrets/
Two defects found by testing the guard against itself:

1. When invoked through the .git/hooks/pre-commit symlink, deriving the repo
   root from dirname(BASH_SOURCE)/.. resolved to .git/ instead of the work
   tree, so the hook scanned nothing and never blocked. Use
   'git rev-parse --show-toplevel' instead.

2. The blanket secrets/ rule rejected secrets/.gitkeep. Replaced with an
   explicit allowlist: .gitkeep, README.md, *.example, *.template.

Verified: a staged file containing a Telegram bot token now aborts the commit
and leaves HEAD unchanged.
2026-08-26 22:48:54 -07:00
Kai cbba8faabc docs: pi 0.84.3 runtime mechanics, isolation baseline, personality layering, gateway patterns
Establishes this repository as the authoritative source for Pi agent
configuration across scenarios, starting with the documentation layer.

Key verified findings (probe harness included, zero model tokens):

- The skills section of the system prompt is emitted only when an active tool
  named 'read' exists (system-prompt.js:59,113). Therefore --no-tools silently
  makes every SKILL.md unreachable and --skill a no-op.
- registerTool accepts a plain JSON Schema object, so tool definitions can be
  served from a backend instead of duplicated in TypeScript.
- An extension can shadow a built-in tool by name, which is how a dedicated
  agent gets a path-restricted 'read' while still satisfying the rule above.
- .pi/SYSTEM.md replaces pi's coding-assistant prompt, but the replacement
  branch contributes neither the tool list nor the guidelines.
- Without --no-skills/--no-extensions, user-global resources leak into every
  scenario; probed leak was find-skills, modsearch, summarize.

Measured effect of the full baseline: system prompt 2619 -> 960 characters,
coding-assistant framing and pi-docs paths removed, skill finally reachable.

Secrets are guarded by scripts/verify-no-secrets.sh, installed as a pre-commit
hook. Backups deliberately live outside the repository.
2026-08-26 22:47:53 -07:00