Files
pi-agent-config/scenarios/curator/profile.toml
T
Kai b5a29b05e1 docs: mark phase 3 complete, record the skills and caching findings
Two findings that changed the plan rather than confirming it:

  23. Skills require a tool literally named `read`. Curator's tools are all
      domain-specific, so every --skill argument was discarded in silence. The
      planned split into curator-core / video-arr / books-ingest was inert before
      it was written; the policy stays in APPEND_SYSTEM.md. memo-inbox is
      unaffected because it registers a restricted `read` override, which is why
      the earlier note generalised wrongly from it.

  24. A long-lived session is worth far more than the startup it saves: 99.97% of
      input read from cache on a continuing conversation against 0% on a new one.
      That is what makes the generated tool list necessary rather than merely
      tidy -- anything varying at the front of the prompt destroys it -- and it
      makes rotation a cost to be bounded rather than applied eagerly.

profile.toml now describes the phase-3 configuration that is actually deployed,
including that the empty `skills` list is a finding and not an oversight.
pi_rpc gains --system-prompt support and no longer guesses whether a `read` tool
will exist; extension_registers_read has to be stated.

harness-layering.md records what transfers from a widely-shared account of
building a personal coding harness on pi, and what does not. The layering frame
holds and the cache-hit figure was the useful part. Its central recommendation --
installing third-party packages -- is disqualifying for an unattended agent
holding tracker credentials, and its discipline layer (AGENTS.md) is precisely
what we block, because it is discovered from every parent directory.
2026-08-28 01:10:58 -07:00

133 lines
6.1 KiB
TOML

# curator — Pi scenario profile
#
# This file describes the configuration that is DEPLOYED.
#
# Current state: plan phase 3. The agent has five read/propose tools served over a
# loopback bridge, and one long-lived pi process per Telegram chat.
#
# The enforcement point is PiLaunchConfig in
# pi-agent-config/shared/lib/py/pi_rpc.py, built by curator/pi_session.py. The
# flags below are asserted by
# test_pi_isolation_flags_match_the_deployed_contract, so this file and the code
# cannot drift silently.
[scenario]
name = "curator"
description = "Personal book / film / TV / music curation agent for the Curator service."
workspace = "/home/claw/pi-workspaces/curator"
session_dir = "/home/claw/.local/share/pi-curator/sessions"
service = "curator.service"
# Application code lives in a separate repository and is intentionally outside
# the agent's workspace.
backend = "/home/claw/codex-workspace/server-management/deploy/curator"
deploy = "managed"
[model]
provider = "zenmux"
primary = "openai/gpt-5.6-luna"
fallback = "x-ai/grok-4.6"
[model.thinking]
# Split by role. The conversation turn has to reason about tool results, so it
# keeps "high". Intent classification and extraction produce JSON for a parser and
# run at the front of every message, where they were the dominant p50 contributor.
conversation = "high"
structured = "medium"
[session]
# One long-lived `pi --mode rpc` process per Telegram chat, keyed by a uuid5 of
# the chat id, plus one shared toolless process for structured tasks. A stable
# prompt prefix across turns is what makes provider prompt caching effective;
# a process per message paid for the whole prompt every time.
strategy = "long-lived-rpc"
rotate_after_prompts = 24
rotate_after_messages = 60
[isolation]
# Enforced in curator/pi_agent.py::_isolation_args. Verified effect is recorded
# in docs/evidence/2026-08-27-curator-phase0-prompt.md.
no_tools = false # the agent has tools now
no_builtin_tools = true # bash / edit / write stay unreachable, extension
# tools stay reachable. An explicit `--tools`
# allowlist is deliberately NOT used: it filters the
# registry and would stop the extension registering
# anything at all.
no_extensions = true # ...except the one named under [resources]
no_skills = true
no_prompt_templates = true
no_themes = true
no_context_files = true # the ONLY switch that stops parent-dir AGENTS.md;
# AGENTS.override.md does not (verified)
approve = true # required to load .pi/SYSTEM.md
[personality]
# Both are system-prompt files, so --no-context-files does not affect them.
#
# SYSTEM.md contains a GENERATED tool list. It has to: pi omits its own tool list
# when --system-prompt is used, because the customPrompt branch returns before
# `toolsList` is assembled, so promptSnippet and promptGuidelines never reach the
# model. Measured effect of not having it: the agent called a tool in one run out
# of four and answered from memory in the other three.
# Regenerate with scripts/verify-generated.sh --fix.
system_prompt = ".pi/SYSTEM.md" # conversation, with tools
# A separate prompt for the toolless structured turns. Handing them the
# tool-bearing prompt would tell the model it can query the library when it
# cannot.
structured_system_prompt = ".pi/SYSTEM.structured.md"
append_system_prompt = ".pi/APPEND_SYSTEM.md" # durable domain policy
context_files = [] # deliberately none
[resources]
extensions = [".pi/extensions/curator-tools.ts"]
# Vendored into .pi/extensions/_shared/ by deploy-scenario.sh, because a tracked
# extension cannot resolve an import from shared/ once installed outside the repo.
shared_extensions = ["pi-guard-base.ts"]
# Deliberately empty, and it is not an oversight. pi emits the skills section only
# when a tool named `read` is active; Curator's tools are all domain-specific, so
# every --skill argument would be discarded in silence. Measured: with tools
# [query_library, lookup_online, counts] the prompt contained no skills section
# and no skill names, with and without --system-prompt. The media policy lives in
# APPEND_SYSTEM.md, which is unconditional.
skills = []
[tools]
# Served by the backend at /tools from curator/contracts.py, so the tool the model
# sees and the endpoint that answers it are the same object. Listed here for
# review only; this file is not the source.
allow = ["query_library", "lookup_online", "book_reviews", "counts", "propose_write"]
[budget]
# One deadline per user turn, enforced with the RPC abort command rather than by
# killing the process, so the session survives a slow answer and the next message
# does not pay to start up. The old per-invocation timeout did not compose: a
# single message could run interpret + query + answer and take three times the
# configured value.
turn_deadline_seconds = 180
startup_timeout_seconds = 60
[env]
# Explicit allowlist, enforced in pi_agent.ENV_ALLOWLIST. Notably absent: every
# CURATOR_* credential, including the Telegram bot token and the Radarr, Sonarr,
# Plex and Tavily keys. The provider key is read by pi itself from
# ~/.pi/agent/models.json and does not travel through the environment.
minimal = true
allowlist = [
"PATH", "HOME", "LANG", "LC_ALL", "LC_CTYPE", "TZ",
"NODE_OPTIONS", "SSL_CERT_FILE", "SSL_CERT_DIR", "NO_PROXY", "no_proxy",
]
# The bridge URL and a per-conversation token, generated at start and passed only
# through the child's environment. The token grants access to this service's read
# tools and to propose_write, which the policy engine still adjudicates; it grants
# nothing else and outlives nothing.
extra = ["CURATOR_BRIDGE_URL", "CURATOR_BRIDGE_TOKEN"]
[bridge]
# Ephemeral loopback port, chosen by the kernel. Not configurable and not
# predictable; the agent's only route to the library.
bind = "127.0.0.1"
port = 0
auth = "per-conversation token, compared with compare_digest"
[secrets]
env_file = "/home/claw/.config/curator/curator.env"