Pi harness via Jaah Pi extension

Task 9947 · Queued · Coder · created · updated

Original request (verbatim): "Close the gap between Claude, Codex, Antigravity and Pi. More universal code, less provider specific" Settled inputs: PRD (Task 9932) and Architecture (Task 9933) of this epic. Approach (architecture §1): lifecycle events run in Jaah-owned code in front of every session (ACP: `jaah-acp-host`, `apps/jaahd/acp/host.py`; terminal: node daemon + vendor CLI events); tool events run in-band through ONE neutral dispatcher `tools/hook_bridge_core.py::dispatch`. Adapters contribute only declarations (event names, tool-name map, placement literals, error text patterns, question prefix), never a copy of the logic. Requirement R1 (verbatim): "Each of these mechanisms has exactly ONE provider-neutral implementation home that every provider uses; an adapter contributes only vendor literals and capability declarations, never its own copy of the logic: (a) hook/guard rendering & chaining, (b) hook-context injection into the model, (c) charter placement as system/built-in instructions, (d) session-start rule list (guard brief), (e) read-once guard reset signal, (f) prompt-submit and session-end lifecycle events, (g) StopFailure / API-error self-heal, (h) project-memory hooks, (i) token/cost (spend) reading, (j) usage-window (quota) reading, (k) the operator question / structured interview channel." R5 (verbatim): "Availability: every closed capability is ON by default on the dev control plane (`dev.jaah.ai`) — no new switch that ships OFF; if a switch is unavoidable it defaults ON and is wired in the real deployment." Closing-test naming contract (architecture §4): every degradation key this child closes gets a test named `test_closes_<key with - as _>` (e.g. `test_closes_no_hook_context`); C9 refuses to remove a key without it. Do NOT remove catalogue degradation keys in this child — the close-out child removes them; this child delivers the behaviour and the closing test. ## Scope (this child) Architecture child C4 — closes Pi `charter-in-first-prompt`, `question-by-command`, Pi project-memory hooks. Builds on C1 (S1 `dispatch`, S3 fields, generic charter-file write) and C2 (`HostLifecycle`). Mechanisms (a)/(b) via dispatch, (c), (e), (k), (h). - New `apps/jaahd/agents/pi_extension/jaah-harness.ts`, a thin shim with no logic beyond translation. Shipped in the packaged tree beside `pi_rpc.py`, root-owned and not runner-writable (also into the MicroVM toolchain share as `pi_rpc.py` is), loaded by `pi_rpc.PI_FLAGS += ("--extension", <path>)`; `--no-extensions` stays (worktree `.pi/` extensions stay off). - Events: `tool_call` → `dispatch PreToolUse` (deny → `{block: true, reason}`); `tool_result` → `dispatch PostToolUse`, context appended to the tool result (mid-turn, in-band); compaction → `dispatch PreCompact`. - S4 ask tool: calls `ctx.ui.select(title, options)`; pi-acp turns that into `session/request_permission` with `toolCall.toolCallId = "pi-ui-<id>"` and `choice-<n>` options (`pi-acp/dist/index.js:1238`, `:1377-1400`). Several questions in one call are asked as sequential selects; each answer returns as the tool result. - Lifecycle events are the host's: Pi row `host_lifecycle=("SessionStart","UserPromptSubmit","Stop","SessionEnd")` (C2's generic `HostLifecycle` runs them — the Pi memory SessionStart/Stop rows and the guard brief reach Pi only through it, and the SessionStart/guard-brief output must land in Pi's charter file `system-prompt.md`; add that wiring here if C2's generic join does not already cover file placement). - Charter: `system_prompt_file_env="JAAH_PI_SYSTEM_PROMPT"`; the host writes `<run dir>/system-prompt.md` before `session/new` (C1's generic code); `pi_rpc` adds `--append-system-prompt <file>`. - Row: `ask_tool` = the extension tool's name, `question_call_prefix="pi-ui-"`, `project_memory_hooks=True`; `inherits_harness_hooks` stays True (now true, matching `fleet_launch.py:441-444`). PRD AC10 (verbatim): "a Task with Autonomous OFF on an Antigravity terminal session and on Pi asks through the structured question card (console shows the question with options, answering returns it to the same session), not a shell command/prose." AC6 (verbatim): "run a Task on an Antigravity account (ACP) and a Pi account; open its session transcript in Audit > Sessions; observe the charter delivered as system/built-in instructions (not as the first user message), the project's guard rule list present at session start, and a hook-context note (e.g. context-architecture check after an edit) visible in the model context." (Live checks run in the close-out child.) Credentials & reach: - C1–C7 unit and replay tests, AC1–AC4 | nothing beyond the worktree (`./dc check full`) | verified (worktree) - AC6/AC9/AC10 Pi | dev account `t7281-pi-key` (Pi API key; row present, currently Disabled, and its enable toggle is a live button on the Accounts page, so C9's verifier enables it as a console action); the Anthropic key behind it: roster `me/jaah/system/anthropic_api_key`, `credential_get` ok | verified (probe: Accounts page row read in browser, toggle present) Out of scope (PRD, verbatim — do NOT touch): - MUST NOT retire, rewrite or change the runtime selection of the legacy terminal runtime; Claude `oauth` stays `legacy_only` (ADR in `docs/adrs/README.md`, ACP Claude entry). - MUST NOT remove or reword any "stay published" degradation (list above), including Pi `no-subagents` and `acp-runtime-only`. - MUST NOT build Pi subagents or a Jaah-side subagent lane. - MUST NOT add Jaah-side workarounds for vendor-intrinsic DeepSeek/Codex gaps (PDF conversion, model remap changes, throttling avoidance). - MUST NOT do live-verification work for Antigravity `not-yet-observed`, nor touch `home-is-account-dir` or `microvm-signed-in-legacy-runtime`. - MUST NOT change `tools/provider_branches.toml` patterns/allowlist scope beyond what removing branches requires (gate tightening was not selected), and set no line-count target. - MUST NOT add a new provider or retire one; no change to login/sign-in flows, model-tier resolution, or the plan gate (`non_claude_engines`). Stay-published keys (must stay byte-identical): DeepSeek `document-blocks-unsupported`, `images-flash-only`, `prompt-cache-controls-ignored`, `beta-features-ignored`, `web-search-extra-calls`, `claude-model-names-remapped`, `model-quality-differs`; Codex `device-code-login-workspace-gated`, `subscription-concurrency-throttling`; Pi `no-subagents`, `acp-runtime-only`. I1-escaped (stay, unchanged): Antigravity `no-spend-reading`; Pi and DeepSeek `no-quota-reading`. Done means: - Replay fixtures (`apps/jaahd/tests/acp_replay/test_replay_pi.py` or beside it) show: the charter NOT in the first prompt and `session/new` never sent before the file is written (`test_closes_charter_in_first_prompt`, Pi); a guard deny blocking a Pi tool call; a question round-trip through `pi-ui-*` (`test_closes_question_by_command`); a PostToolUse context in the tool result; `test_closes_project_memory_hooks` (Pi) — a replay showing the memory SessionStart and Stop rows fire exactly once each through `HostLifecycle`. - A Pi replay test shows the charter file `system-prompt.md` holds the SessionStart output including the guard brief (AC6 guard rule list at session start). - A test pins the extension path as packaged/root-owned (not under the worktree) and `--no-extensions` still present in `PI_FLAGS`. - Disjointness test green; `python3 tools/check_provider_branches.py` exits 0; quick gate green.

Activity

  • Draft
  • Ready