Skip to content

feat(integrations): add provider-independent Pi/OMP extension - #2657

Open
gurkanguray wants to merge 39 commits into
headroomlabs-ai:mainfrom
gurkanguray:feat/pi-extension-headroom
Open

feat(integrations): add provider-independent Pi/OMP extension#2657
gurkanguray wants to merge 39 commits into
headroomlabs-ai:mainfrom
gurkanguray:feat/pi-extension-headroom

Conversation

@gurkanguray

@gurkanguray gurkanguray commented Jul 29, 2026

Copy link
Copy Markdown

Description

Add one provider-independent Headroom extension for Pi and Oh My Pi. The extension prepares eligible large tool results asynchronously, substitutes only validated cached output in the copied model-facing context, and preserves the raw host transcript.

This is a local-install fleet path: one loopback proxy plus headroom-pi. It coexists with the existing Anthropic inference-proxy wrapper. Pi wrap does not rewrite inference URLs. OMP wrap still owns models.yml; durable OMP setup never touches that file.

Closes #2655

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Performance improvement
  • Code refactoring (no functional changes)

Changes Made

  • Added headroom-pi at plugins/pi (same layout as OpenClaw/OpenCode). One package loads in Pi and OMP.
  • Background tool-result preparation with bounded concurrency, backoff, cancellation, and fail-open behavior.
  • Copy-on-write substitution that keeps message order, tool identity, protected/recent results, and raw session history.
  • /v1/compress now sends config.mode: "ccr" so prepare/retrieve matches the current Headroom CCR API (review 4921293436).
  • Typed compression outcomes: accepted / {status:"skipped",reason:"noop"} / {status:"rejected",reason}. No-ops are skipped, not rejected. lastError clears after a later accept.
  • Session footer is Headroom saved N tokens this session, persisted next to the Pi session file across /reload and /resume. /new resets it.
  • Durable global-only lifecycle: headroom init -g pi / headroom init -g omp pins the exact unpublished-safe release version after npm publish. headroom wrap pi sets HEADROOM_PI_BASE_URL only.
  • Ownership-aware install/remove and config live in headroom/providers/pi_extension.py. There is no providers/pi/ inference rewrite.
  • /metrics scrape failures return 500 with # scrape_error ... and headroom_metrics_scrape_errors_total. headroom doctor probes /metrics and the native pin.
  • Release gate publishes headroom-pi before PyPI. CI matrix: Pi 0.80.10 / 0.82.1 / 0.84.1, OMP 17.1.8. Workflow still uses uv sync --frozen --extra proxy.
  • Operator docs: plugins/pi/README.md, wiki/cli.md, wiki/pi-omp-runbook.md.

Testing

  • Unit tests pass (pytest) — focused Python suites below.
  • Linting / typecheck on changed Python — focused suites only; not a full-tree ruff/mypy rerun after the main merge.
  • TypeScript unit tests pass (npm test).
  • TypeScript type checking passes (npm run typecheck).
  • New tests added for new functionality.
  • Manual testing performed against a real local Headroom proxy.

Test Output

# plugins/pi
npm test
Test Files  9 passed (9)
Tests       78 passed (78)

npm run typecheck
> tsc --noEmit

# live CCR contract against loopback proxy :8787
npm run test:live
Test Files  1 passed (1)
Tests       3 passed (3)

# focused Python after merging origin/main
pytest tests/test_cli/test_init_pi_omp_lifecycle.py \
       tests/test_cli/test_wrap_pi.py \
       tests/test_cli/test_wrap_omp.py \
       tests/test_cli_doctor.py \
       tests/test_providers/test_pi_extension.py \
       tests/test_install/test_health.py \
       tests/test_release_workflows.py
219 passed

python3 scripts/verify-versions.py
All versions aligned at 0.35.0

Real Behavior Proof

  • Environment: macOS/Darwin arm64, Node v24.12.0, checkout Headroom 0.35.0-dev after merge with origin/main, loopback proxy on :8787.
  • Exact command / steps: HEADROOM_PI_BASE_URL=http://127.0.0.1:8787 npm run test:live in plugins/pi. Payload includes config.mode: "ccr". Both live cases from the review (validates real compression and retrieval... and fails open while offline and resumes...) pass.
  • Observed result: prepared entry is present, CCR hashes retrieve as substrings of the original tool result, offline fail-open then recovery substitution works.
  • Earlier packed-host evidence (pre-plugins/pi move): Pi 0.80.10 / 0.82.1 and OMP 17.1.8 loaded the packed package; raw history unchanged; model switch reused the prepared result. That matrix was not re-run after the path rename + main merge.
  • Not tested: published-registry headroom init -g (headroom-pi is still npm 404); real external model accounts; non-loopback Headroom; upstream fork workflows (need maintainer approval).

Runtime Rollout Safety

  • Rollout-managed feature(s): none. This is a local opt-in extension, not a hosted rollout flag.
  • Minimum rollout channel: N/A.
  • Stable/default behavior changed: no. Unwrapped Pi/OMP sessions are unchanged. Wrapped/durable hosts fail open if the loopback proxy is down.
  • Kill switch / disable path: headroom init -g remove pi and/or headroom init -g remove omp. Or stop the local proxy.
  • Unsafe override required: no.
  • Qualification impact: none beyond local Pi/OMP extension users.
  • Rollback path: install the previous released headroom-ai and rerun headroom init -g pi / headroom init -g omp so the pin matches that CLI. Do not install latest.

Review Readiness

  • I have performed a self-review
  • This PR is ready for human review

Maintainer gates still pending: core-maintainer approval of #2655, permission to run fork workflows, and first headroom-pi npm publish before durable init -g can pin a released version.

Checklist

  • My code follows the project's style guidelines.
  • I have performed a self-review of my code.
  • I have commented code where the safety invariant or host compatibility is not obvious.
  • I have made corresponding changes to the documentation.
  • My changes generate no new warnings.
  • I have added tests that prove the feature works and fails open safely.
  • New and existing extension tests pass locally with my changes.
  • I did not edit CHANGELOG.md — the PR title supplies the release-please entry.

Screenshots (if applicable)

N/A — terminal extension and host lifecycle change.

Additional Notes

  • Review 4921293436 is addressed in plugins/pi/src/bridge.ts: buildCompressPayload sends config: { compress_user_messages: false, mode: "ccr" }. The workflow still uses uv sync --frozen --extra proxy.
  • Pi wrap sets HEADROOM_PI_BASE_URL only. Package install/ownership is headroom/providers/pi_extension.py, not a providers/pi/ inference analog.
  • Compression requests contain one synthetic tool call/result pair with empty arguments. They exclude user prompts, assistant prose, system prompts, provider payloads, and credentials.
  • Do not close [FEATURE] Add a provider-independent Headroom context extension for Pi and OMP #2655 until merge + headroom-pi publish.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

PR governance

This PR follows the template and is marked ready for human review.

@github-actions github-actions Bot added the status: needs author action Pull request body or readiness checklist still needs author updates label Jul 29, 2026
@gurkanguray
gurkanguray force-pushed the feat/pi-extension-headroom branch from 0e7a2a9 to 15330a2 Compare July 29, 2026 17:03
@github-actions github-actions Bot removed the status: needs author action Pull request body or readiness checklist still needs author updates label Jul 29, 2026
@gurkanguray
gurkanguray marked this pull request as ready for review July 29, 2026 17:28
@github-actions github-actions Bot added the status: needs author action Pull request body or readiness checklist still needs author updates label Jul 29, 2026
@gurkanguray
gurkanguray marked this pull request as draft July 30, 2026 06:43
@gurkanguray
gurkanguray marked this pull request as ready for review July 30, 2026 06:43
@github-actions github-actions Bot added status: ready for review Pull request body is complete and the author marked it ready for human review and removed status: needs author action Pull request body or readiness checklist still needs author updates labels Jul 30, 2026

@JerrettDavis JerrettDavis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the Pi/OMP extension end-to-end after the latest updates. The context transformation path stays copy-on-write, retrieval is hash/substring guarded, protected/recent/error tool results remain fail-open, and the runtime/client/cache boundaries look appropriately conservative for a host extension.

I pushed one maintainer cleanup commit (chore(pi-extension): fix whitespace) to clear the local git diff --check failure. No behavior changes in that commit.

Local validation:

  • npm test in integrations/pi-extension -> 69 passed
  • npm run typecheck in integrations/pi-extension -> passed
  • npm run build in integrations/pi-extension -> passed
  • npm audit --omit=dev --json in integrations/pi-extension -> 0 vulnerabilities
  • npm pack --dry-run --json -> package includes only the intended runtime/docs files
  • git diff --check upstream/main...HEAD -> passed after the whitespace cleanup

GitHub is still showing only the PR governance checks queued/blocked on the fork workflow side, so this still needs the normal maintainer merge-gate handling.

@gurkanguray

Copy link
Copy Markdown
Author

Thanks for the review

Comment thread integrations/pi-extension/src/policy.ts Outdated
) {
return cachedCandidate;
}
if (CCR_MARKER_RE.test(originalText)) return undefined;
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions github-actions Bot added status: ci failing Required or reported CI checks are failing and removed status: ready for review Pull request body is complete and the author marked it ready for human review labels Aug 3, 2026
@Rishang

Rishang commented Aug 9, 2026

Copy link
Copy Markdown

+1

JerrettDavis and others added 5 commits August 12, 2026 16:04
Prepare eligible tool results asynchronously and substitute only validated
cached context without rewriting provider endpoints. Preserve raw transcripts,
exact CCR retrieval, protected results, and fail-open behavior.
@JerrettDavis
JerrettDavis force-pushed the feat/pi-extension-headroom branch from 08ea9fd to 36f7815 Compare August 12, 2026 21:05

@JerrettDavis JerrettDavis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved after the CI-only repair at 36f7815. The previously reviewed feature tree is byte-identical to the approved head outside .github/workflows/pi-extension.yml; the workflow now installs --extra proxy, which supplies FastAPI before starting headroom proxy. The initial commit message was reworded only to wrap its overlong body line for commitlint.

Verification: git diff 08ea9fdfc..36f78154 -- :!.github/workflows/pi-extension.yml is empty, and all rewritten commit-message lines are within the repository’s 200-character limit.

@github-actions github-actions Bot added status: ready for review Pull request body is complete and the author marked it ready for human review and removed status: ci failing Required or reported CI checks are failing labels Aug 12, 2026

@JerrettDavis JerrettDavis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow/commitlint repair is correct, but the newly enabled live matrix now exposes a real product-contract blocker in both supported Pi versions.

npm run test:live fails identically for Pi 0.80.10 and 0.82.1:

  • validates real compression and retrieval before model-independent substitution: no prepared entry is found (entry is undefined at e2e/live-contract.test.ts:121).
  • fails open while offline and resumes prepared substitution after recovery: waitFor times out at line 190 after ~60 seconds.

This is no longer an installation/infrastructure failure—the canonical proxy starts successfully with [proxy] installed, and the hot-path test passes. Please reconcile the extension’s prepare/retrieve assumptions with the current Headroom CCR API, then prove both live cases pass for both matrix versions. Keep the workflow’s uv sync --frozen --extra proxy fix.

Ship the HEADROOM ASCII banner and point pi.image at a live branch URL
until the file exists on main.
@github-actions github-actions Bot added status: ready for review Pull request body is complete and the author marked it ready for human review and removed status: ready for review Pull request body is complete and the author marked it ready for human review labels Aug 13, 2026
@gurkanguray

Copy link
Copy Markdown
Author

@JerrettDavis Live contract should be fixed — /v1/compress now sends config.mode: "ccr". Local test:live is 3/3. Please re-run the Pi matrix.

Keep agent task notes out of the published branch.
@github-actions github-actions Bot added status: ready for review Pull request body is complete and the author marked it ready for human review and removed status: ready for review Pull request body is complete and the author marked it ready for human review labels Aug 14, 2026

@JerrettDavis JerrettDavis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prior live-contract blocker is resolved on the latest source. I re-ran the extension unit suite (78 tests), typecheck, build, package dry-run, a real proxy-backed live contract (3 tests), and the focused Python lifecycle/release suites (122 tests); all passed. I also audited the native-host transaction/rollback and exact-artifact release gates.

I made one small release-metadata cleanup on the branch: the npm banner now uses the durable upstream main URL instead of the contributor feature branch. The package gates remain green after that change. This is ready from my review.

@github-actions github-actions Bot added the status: ready for review Pull request body is complete and the author marked it ready for human review label Aug 14, 2026
@dio16

dio16 commented Aug 15, 2026

Copy link
Copy Markdown

Design warning: strict-prefix provider cache destruction via per-request outbound rewriting

Context: I maintain an OMP setup that used the third-party omp-headroom plugin (DarkPhilosophy/omp-headroom). Last week its provider cache hit rate collapsed from ~98% to ~40% overnight. Root cause, fully traced with per-request usage data and a live provider test, applies directly to any extension that rewrites the model-facing conversation prefix between requests.

The failure mechanism (in omp-headroom)

Its "session archive" transform re-ran on every before_provider_request:

  • createSessionCompaction computes a cut = body.length − live_messages, then checks for an existing archive marker only inside body[cut:] (the last N messages). A marker inserted on a previous request sits near the head of the body (inside the prefix), so it is never detected → the transform re-fires every request.
  • The marker hash is sha256(full messages list) → since the list grows, every request produces a new hash and rewrites the entire history region of the outbound payload.
  • Observed signature: cache_read stuck at ~37k tokens (system prompt only), input growing monotonically with context (~33M input tokens over 492 requests). The provider's per-request usage showed the history never hitting cache again.
  • Live proof of provider semantics: two identical-prefix requests hit ~98.7%; changing one token anywhere in the prefix collapsed the hit to 0% (strict prefix caching). Full details: Session archive re-fires every request, busting provider prefix cache (cache hit 95% -> 47%) DarkPhilosophy/omp-headroom#2

Why this is relevant to this PR

The new provider-independent extension promises to "substitute only previously prepared results in the model-facing context, and preserve the host session transcript byte-for-byte" — that is the right shape, and it avoids the whole-history rewrite bug by construction. The remaining risk is narrower but still real:

  1. Byte-stability of substituted content across requests. Any transform whose output for a given message depends on global context (message count, session state, token budget, adaptive ratio) will rotate the prefix and destroy caching on strict-prefix providers (DeepSeek-family, and others with prompt-cache discounting). omp-headroom's proxy-side compressor was deterministic (content-hash cached); the instability came entirely from the archive layer recomputing per request. Make byte-stability an explicit invariant + test (same input messages ⇒ identical outbound payload region, regardless of appended turns).
  2. One-shot substitution. Once a tool result is replaced with a prepared/cached form, never re-decide it. Any "re-archive with a new hash" pattern reintroduces the bug.
  3. Recommend a regression test that replays a real session transcript twice (with appended turns) and asserts the outbound payload prefix is byte-identical between the two replays. That single test would have caught the omp-headroom bug immediately.

I'd be happy to provide the full measured dataset (per-request cache_read curves for healthy vs broken windows) if useful for the PR's test design.

@chopratejas chopratejas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holding this one out of the current merge batch — not on quality, but because there is an open bug filed directly against this branch: #3056, "Pi extension double-compresses RTK-truncated tool results."

The reporter ran this PR end to end and measured the failure: 262,369 chars → 12,000 via RTK → 10,698 via Headroom. The candidate policy in plugins/pi/src/policy.ts treats an already-truncated toolResult as compressible because the pruning-metadata check doesn't look at the RTK markers. Compressing a compaction is not just wasted work — it degrades content the upstream optimizer already decided to keep.

Concretely, the metadata check needs to recognise both shapes the reporter observed:

details.rtkCompaction.truncated
details.metadata.rtkCompaction.truncated

They also specified the regression test they'd want, which is worth taking as written.

Everything else here looks ready, so I'd rather land it once with the guard than land it and immediately follow with a fix. Once #3056's case is covered and there's a test pinning it, this is good to go from my side.

Separately: CI on this branch has only run 2 checks, which is well short of the ~35 a full run produces. Worth pushing an empty commit to re-trigger so the merge decision rests on a complete run.

@github-actions github-actions Bot added status: has conflicts Pull request has merge conflicts with the base branch and removed status: ready for review Pull request body is complete and the author marked it ready for human review labels Aug 17, 2026
@github-actions github-actions Bot added status: ready for review Pull request body is complete and the author marked it ready for human review and removed status: has conflicts Pull request has merge conflicts with the base branch labels Aug 17, 2026
@gurkanguray

Copy link
Copy Markdown
Author

Thanks @chopratejas #3056 is fixed and covers both RTK metadata paths, with regression tests. Post-sync validation also passed with 91 plugin tests, 276 Python tests, build/typecheck, audit, and the full Pi/OMP matrix. About CI, GitHub still shows only two governance checks after my commits. I see Expected — Waiting for status to be reported on pending checks; it seems you need to approve or run them

@gurkanguray

Copy link
Copy Markdown
Author

Hi @dio16, can you please provide the dataset? Also, I've been using this pr for a while and haven't faced this issue yet. I need to observe this more closely by reproducing it locally

@dio16

dio16 commented Aug 17, 2026

Copy link
Copy Markdown

Hi @gurkanguray — here's the dataset from the omp-headroom investigation (referenced in DarkPhilosophy/omp-headroom#2). Full per-request curves + README: https://gist.github.com/dio16/e1a9b1b5bc1eba21d1a3ef8c9e44c16e

Same binary and provider/model across both windows (opencode-go / deepseek-v4-flash, messages API, loopback proxy :8787, Windows 11).

Broken window — session 01a003cc, 2026-08-15, 214 requests (archive stats: 212 archives for 214 requests, charsBefore 228 MB):

  • cache_read pinned at 37,248 tokens (= system prompt) for 93.5% of requests; p50 = p90 = 37,248. Never grows with the conversation.
  • input grows monotonically ~70k → ~165k; 18.7M uncached input tokens; effective hit 31.7%.
  • Representative rows (input / cache_read): req 5 12,989 / 37,120, req 100 82,210 / 37,248, req 150 116,835 / 37,248, req 210 147,275 / 37,248.

Healthy window — 08-02..08-08, 1,082 deepseek-v4-flash requests:

  • cache_read grows with the conversation (08-04 session: 8,448 → 311,552; avg first quarter 121,459 → last quarter 279,332), input stays small. Per-session hit 96.3–99.3%; window total 99.1%.

Post-fix A/B (fresh process, session_archive: false): req1 35,243 / 640 → req2 55 / 35,840 — full history served from cache. Also 12-turn session 01a004f3: after the cold request, every turn is input < 200 with cache_read ≈ 35,712–35,840.

Strict-prefix sensitivity (session 01a0052c): fresh 33,316 / 2,176 → prefix variant 27,895 / 7,936 → stable prefix 160 / 35,712.

Why you likely haven't seen it with this PR: the rewrite lived in omp-headroom's archive layer, which recomputed a new sha256(prefix) marker on every request and replaced the whole history region — exactly the whole-history rewrite the copy-on-write, one-shot design here avoids by construction. That's why I framed it as a byte-stability invariant + regression-test suggestion rather than a bug in this PR.

Reproduction recipe: deepseek-v4-flash on messages, then per request rewrite one byte anywhere in the history region of the outbound payload (e.g. re-hash-and-replace the prefix, as createSessionCompaction did). Observable signature: after the first request, cache_read drops to system-prompt size and stays flat while input grows by ~full-history each turn. That exact signature is in broken-window-01a003cc.csv; it doubles as a fast pass/fail check for any future transform.

@gurkanguray

Copy link
Copy Markdown
Author

Thanks for sharing the dataset @dio16 . The failure came from omp-headroom’s per-request whole-history rewrite, while this PR substitutes individual prepared results copy-on-write; I haven’t reproduced the same behavior here. Based on current evidence, no PR change is needed, but the signature is useful for future regression testing.

@github-actions github-actions Bot added status: has conflicts Pull request has merge conflicts with the base branch and removed status: ready for review Pull request body is complete and the author marked it ready for human review labels Aug 20, 2026

@JerrettDavis JerrettDavis left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed and refreshed the current head at eb5e1ba. The RTK guard now recognizes both documented metadata paths (including mixed direct/nested values) and keeps malformed/non-truncated metadata eligible, so it avoids double-compressing an upstream-truncated result without broad false positives. I merged current main, resolved release metadata by keeping the 0.36.0 fleet and adding headroom-pi at 0.36.0, and aligned the Pi package/lock versions. A Windows-only mypy defect in the POSIX fcntl branch was fixed with a typing-only Any cast matching the existing msvcrt pattern.

Validation: 91 extension tests, TypeScript typecheck, 53 passed/1 skipped Pi lifecycle Python tests, production npm audit (0 vulnerabilities), version alignment, Ruff, and full commit hooks including mypy all pass. No remaining source blocker; fork CI may still require workflow authorization.

@github-actions github-actions Bot added status: ready for review Pull request body is complete and the author marked it ready for human review and removed status: has conflicts Pull request has merge conflicts with the base branch labels Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: ready for review Pull request body is complete and the author marked it ready for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Add a provider-independent Headroom context extension for Pi and OMP

7 participants