You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I integrated billion-context-pi@0.1.58 (acp-kernel@0.0.55) with prime-agent@0.9.3 and validated the result against Prime's real extension loader and inline RLM runtime.
Prime's session model differs from the host assumptions that billion-context-pi normally sees. A working integration therefore needed a thin adapter, several session-boundary fixes in a small vendor fork, and one host-side fix in Prime's inline-child runtime.
This issue documents what was integrated and why. It is related to #269, but is not a duplicate: #269 covers models ignoring compression nudges; this report focuses on host adapters, custom-message turn boundaries, inherited child state, and extension-runner isolation.
Prime-specific runtime differences
Prime exposes persisted entries through SessionManager.getBranch(), while the plugin expects buildContextEntries().
Initial and forwarded RLM tasks are stored as custom_message entries with customType: "agent_message", not only as ordinary role: "user" messages.
Inline RLM children need the parent's existing compression blocks and refs, but must start with fresh nudge/cadence state.
Parent, child, sibling, and nested sessions can run in one process, so process-global nudge bookkeeping crosses session boundaries.
Prime created a child SessionManager, but its inline child previously reused the parent Agent's context transform and provider hooks.
sets delegate: false, because Prime already has native RLM delegation;
avoids installing a second universal proxy/context transform.
This keeps one ACP transformation layer and prevents two delegation systems from competing for the same session.
2. Pinned, reproducible vendor integration
The integration is based on upstream 0.1.58 with a small Prime-specific patch set. Prime resolves it as a local package instead of silently continuing to load an older pinned npm version.
This was necessary because a host adapter alone cannot change child state derivation or the host's extension-runner wiring, and modifying a transient global node_modules copy would be lost on the next install.
3. Shared Prime-aware turn boundary
The fork uses one boundary predicate for message projection, turn IDs, retry/outcome scope, and nudge accounting:
ordinary role: "user" messages begin a turn;
non-empty custom_message + customType: "agent_message" entries also begin a turn;
UI/control custom messages do not;
synthetic provider-throttle and delegate messages do not.
Without this, multiple real Prime turns can collapse to the session ID, and failed/no-op compression results from an earlier turn can incorrectly block a later one.
4. Per-session injection and runtime state
I replaced process-global nudge deduplication with a per-session injection ledger that records actual injections, not model responses.
Ordinary nudge budget: one injection per real turn.
Emergency nudge budget: bounded separately (up to three per real turn).
Session start/shutdown clears only that session's runtime state.
Auto-compress episode, dead-range, and throttle tracking are also session-scoped.
This prevents a child startup from clearing its parent, or a sibling's turn key from suppressing a valid injection.
5. Inherit structure; reset child-local cadence
When deriving a child state, the integration inherits:
compression blocks;
message refs;
statistics and absorbed records needed for recovery.
It does not inherit stale nudge baselines or tier cadence stamps. Existing child sidecars receive a one-time migration marker so resume does not reset healthy cadence repeatedly.
This fixes the observed case where a fresh child had a smaller context than the inherited parent baseline, produced negative “growth”, and remained blocked: T1 (cadence).
6. Bounded server-side auto-compression backstop
The local integration keeps nudges as the primary path but adds a bounded fallback:
trigger after three ignored over-threshold nudges, or at a hard threshold around 95%;
aim for roughly 80% context usage;
choose at most five viable ranges;
require at least 5,000 estimated net tokens saved;
cap automatic runs per session;
apply cooldown and range-fingerprint deduplication;
preserve reversible ACP blocks for decompress.
This follows the motivation of #269/#270 without treating repeated reminder injection as enforcement.
7. Isolated Prime inline-child extension runner
On the host side, Prime's inline RLM child now owns a child-specific extensionRunnerRef. The child's:
transformContext uses its own runner;
before_provider_request hook uses its own runner;
after_provider_response hook uses its own runner;
AgentSession is explicitly bound to that runner ref.
This host-side change is required because the plugin factory cannot override a child Agent that has already captured the parent's callbacks. A new child session file alone is not sufficient isolation.
Why these boundaries matter
Before the integration, I could see internal decisions reporting nudge=active while no new nudge reached the model. Fresh children could inherit a larger cadence baseline than their own context, remain cadence-blocked, and count an old turn's compression result as current. Reusing the parent's context callback also meant a child that looked separate on disk could still execute extension logic through the parent's runner.
The key design rule that fixed these cases was:
Compression structure may be inherited, but scheduling state, injection budgets, and extension runners belong to the concrete session runtime.
Validation
The installed integration passed:
TypeScript typecheck;
production build;
full vendor test suite: 610 tests total, 606 passed, 0 failed, 4 skipped;
Prime's real resource-loader smoke test with no extension diagnostics;
registration of compress, decompress, search_context, and acp_status, with no duplicate delegate tool;
parent/inline-child isolation tests (different runner refs and independent cadence/runtime state);
inherited-block + fresh-cadence tests;
old-child-sidecar one-time migration and resume tests;
repeated same-turn nudge suppression and next-turn reactivation tests;
ignored-nudge automatic compression plus reversible decompress tests.
Possible upstream seams
Would you be open to focused PRs for generic parts of this integration?
A host-supplied or shared isTurnBoundary(entry) / entry-normalization hook.
An explicit distinction between inherited structural state and child-local scheduling state.
Session-keyed injection/runtime bookkeeping throughout the extension.
A documented adapter contract for hosts whose child runtime is not created by the Pi coding agent.
I can split these into small changes so Prime-specific host code does not leak into the general plugin. This report intentionally omits local paths, session IDs, account details, and raw logs.
Summary
I integrated
billion-context-pi@0.1.58(acp-kernel@0.0.55) withprime-agent@0.9.3and validated the result against Prime's real extension loader and inline RLM runtime.Prime's session model differs from the host assumptions that
billion-context-pinormally sees. A working integration therefore needed a thin adapter, several session-boundary fixes in a small vendor fork, and one host-side fix in Prime's inline-child runtime.This issue documents what was integrated and why. It is related to #269, but is not a duplicate: #269 covers models ignoring compression nudges; this report focuses on host adapters, custom-message turn boundaries, inherited child state, and extension-runner isolation.
Prime-specific runtime differences
SessionManager.getBranch(), while the plugin expectsbuildContextEntries().custom_messageentries withcustomType: "agent_message", not only as ordinaryrole: "user"messages.SessionManager, but its inline child previously reused the parent Agent's context transform and provider hooks.Integration implemented
1. Thin Prime extension bridge
The Prime wrapper:
getBranch()tobuildContextEntries();delegate: false, because Prime already has native RLM delegation;This keeps one ACP transformation layer and prevents two delegation systems from competing for the same session.
2. Pinned, reproducible vendor integration
The integration is based on upstream
0.1.58with a small Prime-specific patch set. Prime resolves it as a local package instead of silently continuing to load an older pinned npm version.This was necessary because a host adapter alone cannot change child state derivation or the host's extension-runner wiring, and modifying a transient global
node_modulescopy would be lost on the next install.3. Shared Prime-aware turn boundary
The fork uses one boundary predicate for message projection, turn IDs, retry/outcome scope, and nudge accounting:
role: "user"messages begin a turn;custom_message+customType: "agent_message"entries also begin a turn;Without this, multiple real Prime turns can collapse to the session ID, and failed/no-op compression results from an earlier turn can incorrectly block a later one.
4. Per-session injection and runtime state
I replaced process-global nudge deduplication with a per-session injection ledger that records actual injections, not model responses.
This prevents a child startup from clearing its parent, or a sibling's turn key from suppressing a valid injection.
5. Inherit structure; reset child-local cadence
When deriving a child state, the integration inherits:
It does not inherit stale nudge baselines or tier cadence stamps. Existing child sidecars receive a one-time migration marker so resume does not reset healthy cadence repeatedly.
This fixes the observed case where a fresh child had a smaller context than the inherited parent baseline, produced negative “growth”, and remained
blocked: T1 (cadence).6. Bounded server-side auto-compression backstop
The local integration keeps nudges as the primary path but adds a bounded fallback:
decompress.This follows the motivation of #269/#270 without treating repeated reminder injection as enforcement.
7. Isolated Prime inline-child extension runner
On the host side, Prime's inline RLM child now owns a child-specific
extensionRunnerRef. The child's:transformContextuses its own runner;before_provider_requesthook uses its own runner;after_provider_responsehook uses its own runner;AgentSessionis explicitly bound to that runner ref.This host-side change is required because the plugin factory cannot override a child Agent that has already captured the parent's callbacks. A new child session file alone is not sufficient isolation.
Why these boundaries matter
Before the integration, I could see internal decisions reporting
nudge=activewhile no new nudge reached the model. Fresh children could inherit a larger cadence baseline than their own context, remain cadence-blocked, and count an old turn's compression result as current. Reusing the parent's context callback also meant a child that looked separate on disk could still execute extension logic through the parent's runner.The key design rule that fixed these cases was:
Validation
The installed integration passed:
compress,decompress,search_context, andacp_status, with no duplicate delegate tool;decompresstests.Possible upstream seams
Would you be open to focused PRs for generic parts of this integration?
isTurnBoundary(entry)/ entry-normalization hook.I can split these into small changes so Prime-specific host code does not leak into the general plugin. This report intentionally omits local paths, session IDs, account details, and raw logs.