Update the Pi Harness Target
SkillAI & modelsAudit and update Sesori's Pi Agent Harness target while finding protocol capabilities, event changes, and simplifications worth integrating. Use when refreshing the Pi runtime pin or reviewing a new Pi release.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Update the Pi Harness Target skill
What this skill tells your AI
The instructions your AI receives, as published by sesori-ai/sesori_apps_monorepo in .agents/skills/update-pi-harness/SKILL.md and read by ahel’s review.
This repository skill lives under .agents/skills/ so the agent harness can
load it alongside the other repository skills.
Refresh the Sesori Pi plugin against the latest stable release of
earendil-works/pi, but treat a target
bump as a protocol audit rather than a version-only edit. The outcome should
identify useful upstream capabilities, distinguish externally observable RPC
behavior from extension-only behavior, and propose the smallest Sesori changes
that improve or simplify the integration.
This skill is intentionally Pi-only. Use the repository's multi-harness
workflow for a multi-harness refresh: open
.opencode/skills/update-backend-runtimes/SKILL.md with the file-reading
mechanism available to the agent, resolving the path from the repository root,
before proceeding. If that path is absent, stop and report the missing handoff
rather than assuming this Pi-only workflow covers other backends.
Normal release-check policy
Target refreshes use normal release checks: the official stable release, verified hashes for all managed archives, isolated current-host install/version/ protocol smoke checks, and focused package tests/analyzer. Do not require source-to-binary attestations, reproducible builds, or a multi-platform live probe matrix. Missing optional provenance evidence is a reporting limitation, not a blocker or a reason to ask for an exception on each update. Keep the compatibility minimum unchanged unless the user explicitly approves raising it.
Scope and invariants
Relevant production files normally include:
bridge/sesori_plugin_pi/lib/src/runtime/pi_runtime_manifest.dartbridge/sesori_plugin_pi/lib/src/api/models/pi_event.dartbridge/sesori_plugin_pi/lib/src/api/models/pi_assistant_delta.dartbridge/sesori_plugin_pi/lib/src/api/models/pi_rpc_frame.dartbridge/sesori_plugin_pi/lib/src/models/pi_rpc_command.dartbridge/sesori_plugin_pi/lib/src/repositories/pi_session_process_repository.dartbridge/sesori_plugin_pi/lib/src/services/pi_event_dispatcher.dartbridge/sesori_plugin_pi/lib/src/services/pi_session_service.dartbridge/sesori_plugin_pi/test/pi_runtime_manifest_test.dartbridge/sesori_plugin_pi/test/pi_plugin_descriptor_test.dart
Preserve these invariants unless the user explicitly approves a separate compatibility change:
- Keep
PiRuntimeManifest.minPathVersionbyte-for-byte unchanged. - Keep Pi's complete package-directory archive layout. Unix archives contain a
pi/package tree; Windows archives are a package tree withpi.exe. - Keep strict JSONL framing, request-ID correlation, delta-only message updates,
and
message_endas final message authority. - Preserve the user's Pi profile, credential sources, and runtime settings in the production launch. The safe probe is deliberately credential-free. Never put secrets, prompts, transcripts, user/project/local filesystem paths, or raw provider errors in reports or commits. Public upstream repository paths and source links may be cited for auditability.
- Never hand-edit generated Dart files.
Phase 1 — Establish the candidate
-
Read the current target and minimum from
PiRuntimeManifest, its tests, and the registered plugin list. Verify the Pi descriptor/registry seam inbridge/app/lib/src/runtime/plugin_registry.dart, including the descriptor registration and manifest wiring. Record both values before touching anything. -
Query the latest GitHub release, not an arbitrary
maincommit:gh api repos/earendil-works/pi/releases/latest --jq \ '{tag: .tag_name, prerelease: .prerelease, draft: .draft, published_at: .published_at, assets: [.assets[] | {name, digest, size}]}'Ignore drafts and prereleases. The target is the stable
vX.Y.Zrelease tag. Do not silently select an unreleased commit because it contains a promising feature. -
Require exactly these six managed assets, with non-null GitHub
sha256:digests:pi-darwin-arm64.tar.gzpi-darwin-x64.tar.gzpi-linux-arm64.tar.gzpi-linux-x64.tar.gzpi-windows-arm64.zippi-windows-x64.zip
Download
SHA256SUMSwhen present and confirm every line agrees with the GitHub asset digest. Do not guess mappings, accept a missing digest, flatten an archive, or invoke Pi's installer scripts. -
Resolve both the current and candidate release tags to full immutable commit SHAs (
oldCommitShaandnewCommitSha) and record them before diffing or downloading. Re-resolve the public tags immediately before approval and require exact equality; if a tag moved, disappeared, or is ambiguous, stop and restart the audit. Inspect release-workflow and packaging changes as part of the source audit. Provenance attestations or reproducible-build evidence may be recorded when already available, but are not required for a normal target refresh. Do not start a separate rebuild/provenance project or block pinning because that evidence is absent. Report checksum integrity separately from source-to-binary provenance; a checksum or smoke test does not prove the latter.
Phase 2 — Audit the release diff in aggregate
Compare the release commits directly before reading individual commits:
Resolve and retain the full commit SHAs recorded in Phase 1. Re-check that both public tags still resolve to those exact commits before using any diff:
old_commit_sha="<recorded full 40-hex old-release commit>"
new_commit_sha="<recorded full 40-hex new-release commit>"
resolved_old_sha="$(gh api repos/earendil-works/pi/commits/v<old> --jq .sha)" || {
echo "ERROR: could not resolve v<old>; restart the audit" >&2
exit 1
}
if [ "$resolved_old_sha" != "$old_commit_sha" ]; then
echo "ERROR: v<old> no longer resolves to $old_commit_sha; restart the audit" >&2
exit 1
fi
resolved_new_sha="$(gh api repos/earendil-works/pi/commits/v<new> --jq .sha)" || {
echo "ERROR: could not resolve v<new>; restart the audit" >&2
exit 1
}
if [ "$resolved_new_sha" != "$new_commit_sha" ]; then
echo "ERROR: v<new> no longer resolves to $new_commit_sha; restart the audit" >&2
exit 1
fi
gh api "repos/earendil-works/pi/compare/${old_commit_sha}...${new_commit_sha}"
Do not substitute mutable tag names for the recorded SHAs after this check.
Treat the compare response as a bounded API result, not proof of completeness.
Check its status, total_commits, returned commit count, returned file count,
Link/pagination metadata, and any API truncation indication. Use
gh api --paginate where pagination is advertised and aggregate the pages. If
those checks cannot prove that the changed-file list is complete (GitHub can
cap compare-file results), do not classify protocol changes from it; use a
local tag-diff fallback instead:
tmp_repo="$(mktemp -d)"
git clone --filter=blob:none --no-checkout --quiet \
https://github.com/earendil-works/pi.git "$tmp_repo/pi"
git -C "$tmp_repo/pi" fetch --quiet --no-tags origin \
refs/tags/v<old>:refs/tags/v<old> refs/tags/v<new>:refs/tags/v<new>
resolved_old_sha="$(git -C "$tmp_repo/pi" rev-parse "refs/tags/v<old>^{commit}")" || {
echo "ERROR: local v<old> tag cannot be resolved; restart the audit" >&2
exit 1
}
if [ "$resolved_old_sha" != "$old_commit_sha" ]; then
echo "ERROR: local v<old> tag moved from $old_commit_sha; restart the audit" >&2
exit 1
fi
resolved_new_sha="$(git -C "$tmp_repo/pi" rev-parse "refs/tags/v<new>^{commit}")" || {
echo "ERROR: local v<new> tag cannot be resolved; restart the audit" >&2
exit 1
}
if [ "$resolved_new_sha" != "$new_commit_sha" ]; then
echo "ERROR: local v<new> tag moved from $new_commit_sha; restart the audit" >&2
exit 1
fi
git -C "$tmp_repo/pi" diff --name-status "$old_commit_sha" "$new_commit_sha"
git -C "$tmp_repo/pi" diff --stat "$old_commit_sha" "$new_commit_sha"
git -C "$tmp_repo/pi" diff --no-ext-diff --no-textconv "$old_commit_sha" "$new_commit_sha" -- \
> "$tmp_repo/pi-release.patch"
# Keep this temporary tree until the complete patch has been consumed.
Inspect and summarize the patch for every changed path, including release
workflows, archive builders, package metadata, and launch wrappers; a
packages/-only view is supplemental, never the completeness check. Consume
pi-release.patch with the available file-reading or analysis mechanism before
cleanup, and do not install an EXIT trap that removes it prematurely. If the
inspection is interrupted, retain the temporary tree until the patch has been
read or the failure has been recorded. Only after that inspection, run
rm -rf "$tmp_repo" as the explicit cleanup step. The fallback must include
the complete tag diff (or a separately
paginated per-commit inventory) before a capability is marked absent. Prefer a
derived summary of commit titles, changed paths, additions/deletions, and
selected patches over dumping the full JSON response or patch into the
conversation. A complete
commit-by-commit deep read is optional; when the range is large, a lightweight
scout/delegate may inventory every commit title and changed area, while the
main review uses the aggregate tag diff for evidence. Do not let an agent edit
the Sesori worktree or create another worktree.
For any nontrivial subagent reasoning or synthesis, check the available model
registry first and prefer the exact registered model
openai-codex/gpt-5.6-luna with maximum thinking effort when the caller's
agent interface can select it. If that model or effort selector is unavailable,
use the caller's current model/effort or another available selector and record
that fallback; do not make an otherwise executable audit depend on an
unavailable model. Keep lightweight inventory work separate from smart review
work.
Search the aggregate diff, release changelogs, and source at both tags for:
- top-level JSONL/RPC event names and field changes;
agent_start,agent_end,agent_settled,turn_start,turn_end, and tool execution lifecycle semantics;extension_ui_request,ui_prompt_start,ui_prompt_end, and other extension/UI events;session_compact_failed, compaction/retry ordering, queue behavior, and session-file/history changes;- RPC commands/responses, exact field casing, acceptance versus completion, and new cancellation/queue controls;
message_updatedelta shape, tool-call metadata, message persistence, image support, and new tool names;- model/provider/auth discovery, launch flags, bundled runtime behavior, and package/archive changes.
Inspect source, not only release notes. For each candidate capability, record:
- the upstream tag/commit and exact source path;
- the wire shape and field types/casing;
- ordering and completion semantics;
- whether it is emitted by
session.subscribe()/toJsonEvent()in RPC mode, by the extension event bus only, or through a separate request frame; and - the smallest concrete Sesori flow that would benefit.
Do not confuse extension events with RPC events
An event being accepted by pi.on(...) does not mean an external RPC client
can observe it. Verify that it belongs to the serialized AgentSessionEvent
union and that rpc-mode.ts forwards it. For example, ui_prompt_start and
ui_prompt_end may describe blocking ctx.ui.* waits and may be extension-bus
only; in RPC mode the observable equivalent can instead be an
extension_ui_request. State this distinction explicitly rather than adding a
parser for an event the wire never emits.
Likewise, agent_end is a low-level loop boundary and may be followed by retry,
compaction, or queued work. Use agent_settled as the final user-visible
completion signal unless current source proves otherwise. Treat
turn_start/turn_end as per-turn boundaries and tool execution events as
per-tool boundaries.
Classify findings:
- Adopt now: directly observable, stable, and fixes a demonstrated Sesori limitation or removes existing workaround code.
- Probe first: promising but dependent on authenticated behavior, a new package entrypoint, platform-specific assets, or an untested ordering.
- Track only: extension-only, interactive-TUI-only, provider-specific, or unrelated to the current bridge contract.
- Reject: speculative defenses or compatibility machinery without a real caller and meaningful damage.
Choose a compatibility shape deliberately
For a real version difference, first try one tolerant implementation: preserve unknown frames, make newly introduced transport fields optional where an older Pi can legitimately omit them, and degrade to the existing behavior. Do not add version branches merely because an API technically permits them.
If the semantics are genuinely incompatible and the compatibility code is large
or hard to reason about, explicitly evaluate a narrow shared interface with two
Pi-version-specific implementations. Select the implementation only from a
validated runtime version (the managed manifest or a validated PATH
probe); Pi has no handshake, so never infer a version from an event or silently
assume the binary behind PATH. Keep both implementations inside the Pi plugin
and do not leak Pi-specific concepts into bridge/app/ or client contracts.
For every retained compatibility branch, comment the exact older Pi behavior it
preserves and the first minimum Pi version that no longer needs the branch. Read
the current product version from bridge/app/pubspec.yaml immediately before
writing the marker, and replace both YYYY-MM-DD and vX.Y.Z with concrete
values; never commit the placeholders or a stale package version. Use this
marker directly above the retained field/branch:
// COMPATIBILITY YYYY-MM-DD (vX.Y.Z): Pi <= <old> omits <behavior>; remove this
// fallback when PiRuntimeManifest.minPathVersion is raised to <new>.
When a single tolerant path is sufficient, prefer it over the two-implementation interface; if neither path has a concrete caller and meaningful damage, reject both as speculative machinery.
Phase 3 — Run a safe current-host probe
Verify the downloaded SHA-256 of all six managed archives as opaque bytes;
checksum validation does not require extracting or launching them. Run the
installation and live probe below only for the archive matching the current
host OS and architecture. Other platforms do not need installation or launch
probes to approve a target refresh; report them as untested, not validated by
the host result. Record the tested asset and digest, host OS/architecture,
disposable boundary, production extraction/placement, exact --version, and
JSONL/RPC get_state results. Treat downloaded archives as untrusted executables
even after verifying their official digests:
-
Establish the current-host disposable boundary before handling archive contents. Use a native OS sandbox or suitable VM/container/restricted OS account with no sensitive mounts and blocked outbound network access. A temporary
HOME, Pi directories, or allowlisted environment does not sandbox a process running as the maintainer's account. If this boundary is unavailable, do not inspect, extract, or execute the host archive; report the current-host probe as blocked. -
Transfer the matching official archive as opaque bytes into that boundary, or download it there. Verify its SHA-256 before parsing it. Keep every other archive opaque unless an optional platform-specific check is warranted.
-
Inspect and extract the current-host archive inside the same disposable boundary without changing the repository. Run the production extraction and package-placement path (
bridge/sesori_bridge_foundation/lib/src/archive_extractor.dartandbridge/sesori_plugin_runtime/lib/src/provisioning/runtime_install_service.dart) on a target-appropriate host; do not substitute a generic extractor for this validation. Use its hardened policy to reject absolute, traversal, and escaping symlink/hardlink targets before writing; confirm the package tree and platform-specific entrypoint remain intact. This repository rejects all symlinks after extraction, not only links that escape. If the production path cannot run in the host sandbox, report the current-host probe as blocked. Resolve that entrypoint to an absolute path and invoke that path for every check; never invoke a bare PATH-installedpior copy the executable away from its package files. If a temporary PATH wrapper is unavoidable, use only a symlink to the original entrypoint, assert its resolved real path stays inside the extraction root, and launch with the original package root intact. -
Create an empty temporary project cwd and isolated temporary
HOME, Pi data/session roots, and host-specific config/cache roots. Explicitly setPI_CODING_AGENT_DIRandPI_CODING_AGENT_SESSION_DIRto temporary directories (or unset them while proving the defaults also resolve inside the probe root); do not inherit the user's values. On Windows also isolateUSERPROFILE,APPDATA, andLOCALAPPDATAas applicable. Do not read or mutate the user's normal profile. -
Construct an explicit allowlisted child environment rather than inheriting the caller's environment. Include only a minimal known-safe
PATH(or equivalent runtime path),HOME/host temp roots, the two Pi directory variables,PI_SKIP_VERSION_CHECK=1, locale settings, and required Windows system variables. Omit API keys,GH_TOKEN, cloud credentials,SSH_AUTH_SOCK, credential-helper settings, and other unrelated secrets. The probe must remain unauthenticated; use a separately authorized and explicitly approved procedure if authenticated behavior needs testing. -
Run the separate current-host
--versionprocess with a bounded 10-second timeout and the same process-group/Job Object cleanup guarantee. If it hangs or exits unexpectedly, terminate its entire process tree before reporting failure; do not let it block the RPC probe or filesystem cleanup. Verify that its output identifies exactly the candidate release, rather than merely returning success. Preserve the normal environment policy in the production launch design. -
Launch the extracted current-host entrypoint with exactly these arguments:
--mode rpc --no-session --approveApply the allowlisted environment using host-appropriate APIs (
env -ior an explicit environment map on POSIX; an explicitProcessStartInfoenvironment/PowerShell map on Windows), and keep the empty temporary cwd as the process working directory. Do not use a PATH-installed binary. -
Write this exact newline-delimited request to the child stdin:
{"id":"probe-1","type":"get_state"}Within a bounded 10-second read timeout, parse stdout records until the matching response and assert
id == "probe-1",type == "response",command == "get_state",success == true, anddatais an object. Reject malformed JSON, an unexpected matching response, or a timeout; unrelated event records may be drained but must not be mistaken for the response. Put the version check, RPC assertions, and any focused probes inside atry/finally. On success or rejection, close stdin, wait up to 2 seconds, then terminate the entire candidate process tree before removing anything. On POSIX, terminate the process group/session with SIGTERM and then SIGKILL only if needed. On Windows, assign the candidate and its descendants to a Job Object configured to terminate members on close, or explicitly enumerate and terminate every descendant (re-enumerating until none remain);Stop-ProcessorTerminateProcessalone are not recursive and are insufficient. Use the same bounded wait. Do not kill only the parent and assume bundled child processes exited. Tear down the sandbox and remove the temporary archive, extraction root, profile roots, project cwd, and probe logs only after child cleanup completes. -
If the release changes a command/event surface that Sesori may adopt, run a focused additional probe for that surface. Stop on a startup, framing, response-correlation, package-layout, isolation, or required-surface regression. Do not pin a candidate based on a version output alone.
A skipped or failed required current-host probe blocks the target refresh. Missing install/launch results for other platforms do not block it. Optional platform checks may be added for a concrete release change or regression; report any observed failure rather than treating it as a passing host limitation. All six archive digests remain required; source-to-binary provenance is optional. When approved, update the complete release consistently; never combine a new shared target URL with old asset digests.
Keep probe output redacted and bounded. Do not retain raw frames, credentials, transcripts, prompts, user/project/local filesystem paths, or provider/account identifiers. Public upstream repository paths may remain in the audit record.
Phase 4 — Report and approval gate
Before editing the runtime target or production protocol code, report:
- current target and candidate stable release/date;
- unchanged PATH minimum;
- all six asset names, sizes, and verified SHA-256 values;
- any optional provenance evidence, clearly distinguished from checksum integrity;
- aggregate diff size and the high/medium findings with source links;
- which findings are truly visible on JSONL/RPC stdout;
- current-host OS/architecture, production extraction/placement, exact version, and RPC results; list other platforms as untested unless optionally checked;
- proposed Sesori edits, explicitly separating version-only changes from capability changes, simplifications, and follow-up work;
- the compatibility strategy: tolerant single path versus a justified version-selected interface with two implementations, including each branch's retirement/minimum version.
An explicit request to update the target approves the version-only bump; do not ask for that approval again or request an exception for optional provenance. Ask before adding capabilities or raising the minimum. A release audit is not permission to expand scope or implement unverified upstream behavior. If the user approves a higher minimum Pi version, inspect the compatibility branches, tests, and docs that only support versions below the new floor and delete the obsolete code rather than carrying it forward. The skill-only documentation PR may proceed when explicitly requested, while the runtime/capability PR remains behind this gate.
Phase 5 — Implement only after approval
Enter this phase only after all six archive digests are verified, the current-host sandboxed production extraction/placement and live version/RPC probe pass, and the user approves the scope. Source-to-binary provenance is optional and does not block the refresh. Other platforms do not require installation or launch probes. Report their untested status without blocking the complete six-asset target update.
For an approved target refresh:
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 119
- Forks
- 8
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
update-pi-harness- Source
- github.com/sesori-ai/sesori_apps_monorepo