pi-context-engineer
Context governor for Pi Fabric and Pi Fovea: taint-gates data flow, budgets selection, compresses results, offloads payloads, and records context cost.
Package details
Install pi-context-engineer from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-context-engineer- Package
pi-context-engineer- Version
0.6.0- Published
- Sep 7, 2026
- Downloads
- 644/mo · 525/wk
- Author
- p_yan
- License
- MIT
- Types
- extension, skill
- Size
- 1.4 MB
- Dependencies
- 1 dependency · 4 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
],
"skills": [
"./skills"
],
"providesTools": [
"ce_exec",
"ctx_read",
"ctx_summarize",
"ctx_remember",
"ctx_recall",
"ctx_forget",
"ctx_delegate",
"ctx_offload",
"ctx_status"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-context-engineer
A context governor for Pi, Pi Fabric, and optionally Pi Fovea.
The roles are deliberately separate:
- Fabric executes and orchestrates work without exposing intermediate values to Main.
- Fovea discovers the most relevant repository regions.
- Context Engineer decides what data is allowed to cross the boundary and records the cost.
Features
Static data-flow gate
fabric_exec programs are analyzed before execution. Tool-originated values are tracked through aliases, destructuring, object/array construction, method chains, callbacks, local helper functions, Promise.all, and function arguments.
The analyzer distinguishes:
RAW— direct or near-direct tool dataENCODED—String(raw),JSON.stringify(raw), templates, and similar representations that still contain the payloadUNKNOWN— an untrusted helper received tainted dataPROJECTED— scalar fields, counts, keys, and field projections (only scalar projections are inherently bounded)SELECTED— slices, filters, matches, and bounded Fovea results (only explicit bounds are safe)AGGREGATED— reductions and scalar summariesCOMPRESSED— context summariesOFFLOADED— disk handles
Scalar projections, some()/every()/includes(), one-item selectors such as find(), explicit slice(0, N), Fovea maxTokens, summaries, and offloads establish bounds. Numeric bounds survive aliases and local helper arguments, and common tool status fields such as ok, exitCode, and truncated are treated as scalars. map(), filter(), reduce(), Object.entries()/values(), trim(), and replace() can still retain the full source, so the analyzer marks them unbounded unless a later operation derives an explicit bound.
By default, an uncertain source-bearing return is classified as a warning but executes silently. The runtime boundary guard then keeps a small actual result or offloads a large one. Use ce_exec when you want the static diagnostic explicitly. This avoids blocking useful work because of a conservative static approximation. Set strict: true or blockUnboundedReturns: true for fail-closed preflight. Default preflight is advisory even for zero-source projections and estimated oversized returns. Fabric retains authoritative syntax/execution validation; only explicit strict/blocking settings turn CE diagnostics into pre-execution blocks. The number of internal calls is not the primary limit: a Fabric program may make many calls when its boundary result is controlled.
Runtime boundary guard
CE leaves all internal Fabric values intact: ordinary pi.* results, MCP results, captured Fovea/CE tools, and provider proxy details.result. Fabric owns its executor/transport limits. Explicit ctx_offload remains available when the program deliberately wants a handle.
At the model boundary CE budgets the sum of all text blocks, not just the first. Single-text handles store the exact text unchanged. Multi-text handles store { textBlocks: [{ index, text }, ...] }, preserving every original content-array index; use jsonPath: "$.textBlocks[0].text" or byte ranges to recover text. Media retain their positions, short independent notes get a bounded inline allowance, and rewritten slots share one recovery handle. A storage failure leaves the original result visible rather than discarding evidence.
Large final text results are offloaded; budgeted Fovea results that meet their requested size are left alone in auto mode. Artifact metadata alone cannot exempt an oversized payload. JSON previews prioritize short nested status/path/count fields over bulky siblings. Fabric's sectioned-YAML results retain small sections and source-line-labeled outline excerpts; previews are selections, not complete return values. summarize uses deterministic structural compression with the full original stored behind a recovery handle. Oversized errors also retain exact originals, not just selected diagnostics.
CE never rewrites grep patterns or other tool arguments. Fabric's catalog repairs handle argument/action naming near-misses; its entropy compiler optimizes tool surfaces. Neither requires CE to guess regex intent.
Recognized successful edit envelopes containing verbose diff/patch strings are compacted from 2 KB onward, even below the ordinary offload threshold. The acknowledgment stays visible and the complete original diff/patch remains retrievable. This only changes model-boundary text, never ordinary intermediate Pi values. Set compactEditResults: false to disable the lower edit threshold, or resultPolicy: "inline" to bypass successful-result rewriting altogether.
Runtime size advisories are disabled by default because bounded 4–8 KB results are usually intentional; set runtimeAdvisoryThreshold to opt in.
Fovea-aware selection
When Fovea is installed, use its captured tools inside Fabric:
const focus = await extensions.fovea_focus({
query: "authentication",
maxTokens: 500,
});
return extensions.ctx_summarize({
text: focus,
maxTokens: 300,
});
A Fovea call with maxTokens is classified as a budgeted SELECT operation. Context Engineer does not reproduce Fovea's code graph.
Context-effect registry (v0.5)
The analyzer consumes a small exported registry describing how calls affect context:
import { contextEffectFor } from "pi-context-engineer";
contextEffectFor("pi.read"); // { kind: "source" }
contextEffectFor("extensions.ctx_read"); // SELECT bounded by length bytes
contextEffectFor("extensions.ctx_summarize"); // COMPRESS bounded by maxTokens
contextEffectFor("extensions.ctx_offload"); // OFFLOAD
Unregistered calls under mcp.*, agents.*, workflow.*, state.*, memory.*, schema.*, mesh.*, council.*, rlm.*, fabric.*, and direct shell/web tools are conservatively classified as source effects; exact CE/Fovea entries override that fallback.
The registry remains descriptive, while the analyzer reports metrics.returnBound, metrics.returnProvenance, and an independent metrics.quantitativeDecision. Bounds distinguish exact values from proven upper bounds; the upper-bound algebra supports safe-integer +, -, *, Math.min, expression-level conditional joins, and Math.max when every operand is bounded. Conditional joins require both branches to be finite and use the larger branch bound; unsupported operations and control-flow assignment remain unknown. Quantitative policy defaults to 8192 bytes, 4000 tokens, and 8192 characters. Only bytes, tokens, and characters are directly comparable to those budgets; characters mean JavaScript UTF-16 code units (String.length), with no implicit byte/token conversion. Element and record bounds remain structural and are not used as context-size proofs. A within-budget proof additively clears only the legacy unbounded-return block; legacy-safe paths remain safe, while over-budget and not-comparable proofs remain blocked. Configure overrides in .pi/context-engineer.json with policy.maxBytes, policy.maxTokens, and policy.maxCharacters; each must be a non-negative safe integer no greater than 1,000,000,000. explainProgram(source) and formatProgramExplanation(...) expose the decision, budget, proven maximum, and result.
Addressable context store
Stored payloads include content hashes, provenance/source, content type, creation/access timestamps, estimated tokens, and expiry. Identical payloads deduplicate. ctx_read supports UTF-8 byte ranges, literal line queries, and focused JSON-path lookups such as $.results[0].name. Ranged results expose copyable offset / nextOffset; query mode formats at most 100 match windows by default (maxMatches, capped at 500), samples matchedLines, and preserves the exact totalMatches. The complete serialized result—not only its text field—is budgeted below the recursive-offload threshold. Handles expose structural previews (JSON keys/counts, or head/tail text/code) rather than dumping arbitrary payloads. At the model boundary, use ctx_read({ id, offset, length }); inside Fabric code, use extensions.ctx_read(...). Offload previews are compacted before their first model exposure. After exposure, CE leaves messages unchanged—including ctx_read output—so later calls preserve cacheable prefixes and the agent keeps its evidence. CE does not register a historical context rewrite hook. Pi's explicit or automatic session compaction remains responsible for shortening accumulated history; stored payloads remain re-readable subject to retention limits. By default, entries expire after one week and the store is capped at 500 MB; cleanup runs opportunistically during store activity.
Telemetry
The extension records sizes and strategies—not prompts or payloads—in .pi/context-store/context-events.jsonl. Metrics separate internalTokensProcessed, mainTokensPrevented, mainTokensInjected, and storeTokensWritten; the legacy savedTokens field aliases Main-context prevention for compatibility. Telemetry is scoped by extension runtime, host Pi session, or workspace lifetime; ctx_status defaults to a compact report for the selected scope (session by default), without repeated sibling scopes or per-event/per-strategy diagnostics. A fresh runtime can still request session/lifetime activity from earlier runtimes. Use ctx_status({ scope: "lifetime", detail: "full" }) for all three scopes and diagnostic breakdowns. Existing consumers of .runtime, .session, or .lifetime should request detail: "full"; compact callers use .summary.
Interactive commands:
/ce status current-session observed savings
/ce status --all all recorded workspace sessions
/ce trace recent policy events
/ce explain current architecture and policy
/ce settings effective project configuration
/ce clear clear telemetry
The numbers are approximate context-token estimates, not billing reconciliation or proof of agent effectiveness. Internal helper calls do not count as Main exposure; reading an existing handle does not count the same source as prevented again. Use actual model Usage (including nested calls and cache/cost fields) to evaluate billed savings. ctx_status exposes childCalls, observed childUsage, and childUsageComplete; incomplete/missing reports are not zero-cost work. Native standalone calls attach observed Usage on success and failure. Current Fabric capture omits nested tool Usage from native totals, so childUsageOutsideParent reports the additional observed usage separately; add it exactly once, not all childUsage again. The effectiveness harness performs this accounting from isolated-workspace telemetry.
Fabric example
const result = await pi.grep({ pattern: "TODO", path: "src" });
return (await extensions.ctx_summarize({
text: result,
mode: "code",
maxTokens: 300,
})).text; // one model-facing copy; structured details stay exact internally
Summarization modes
ctx_summarize accepts an inline text value or an offloaded id:
structural(default) — free deterministic JSON/text extraction.code— free deterministic code-aware extraction of imports, signatures, and head/tail windows.model— isolated no-tools semantic summarization; large inputs are chunked and reduced hierarchically.
Unknown modes reject at the registered Pi tool boundary. maxTokens is an approximate UTF-8 return budget, clamped to 64–4000. Model mode also accepts maxInputTokens (default 32000 approximate source tokens per child), maxChunks (default 16, maximum 64), maxCalls (default twice maxChunks, maximum 128), and a whole-operation timeoutSeconds (default 90, range 10–110). Binary reduction guarantees progress; no redundant final model call is made. Model-source data, including inline input, has an exact recovery handle. strategy: "direct" explicitly selects a prefix and reports complete, coveredBytes, totalBytes, and nextOffset; it is never an implicit fallback. Prefer deterministic code or structural unless semantic compression is needed.
Offloaded results include a copyable ctx_read({ id, offset, length }) example at the model boundary; inside Fabric code use extensions.ctx_read(...). Use query for literal matches or jsonPath for focused JSON values.
For large data, write it off-window instead of returning it directly:
const text = await pi.read({ path: "large.json" });
return (await extensions.ctx_offload({
key: "large-json",
source: "read",
data: text,
})).text;
Use the returned handle with ctx_read to retrieve a range, literal matches, or a focused JSON path later.
Tools
The registered tools are callable directly by the model and inside Fabric through extensions.*:
ctx_read— select a range, literal matches, or a JSON path from a stored handlectx_summarize— free structural/code compression or isolated no-tools model compressionctx_remember/ctx_recall/ctx_forget— persistent project facts with bounded recall, named upserts, and deletionctx_delegate— isolated child Pi withmaxTokens,maxTurns(default 8, range 1–32), a nested-safe deadline, bounded process-output buffers, and parent cancellation. Supported adapters receive real generation caps. Codex has no supported server output-cap field, so it uses an explicitly approximate streaming guard instead;generationBudgetreports this limitation and billed output may overshoot or be unknown on cancellation.childOutputTruncated: trueexposes provider-length truncation. Oversized returned text has an exact recovery handle. Pure summaries disable tools/project context and use a minimal prompt. Timeout/abort terminates process trees, escalating to SIGKILL on POSIX.ctx_offload— manual Write operationctx_status— current policy thresholds and observed savingsce_exec— explicit static preflight (PASS,WARN, orBLOCK)
For Fabric-native orchestration and recursive decomposition, call Fabric's agents.* APIs directly when available.
Prefer compact discovery before requesting complete schemas. For example, select only matching tool names/refs inside Fabric rather than returning the entire catalog:
const catalog = await tools.list({});
return catalog.filter(t => /blender/i.test(t.name + " " + t.description))
.slice(0, 10).map(t => ({ name: t.name, ref: t.ref }));
Then describe only the tools you will call. Context Engineer does not override Fabric's discovery API.
Configuration
Create .pi/context-engineer.json in a project when needed:
{
"enabled": true,
"strict": false,
"blockUnboundedReturns": false,
"maxReturnTokens": 4000,
"readOffloadThreshold": 16384,
"resultPolicy": "auto",
"errorCompactionThreshold": 4096,
"errorCompactionPreviewBytes": 4096,
"offloadPreviewBytes": 2048,
"runtimeAdvisoryThreshold": 0,
"compactEditResults": true,
"notifyOnStart": false,
"storeMaxBytes": 500000000,
"storeTtlMs": 604800000
}
Config reference
All options live in .pi/context-engineer.json (field names are exact CeConfig keys). Byte budgets are UTF-8 bytes; token estimates are ~bytes/4 and ASCII-biased (see note below).
| Option | Type | Default | Notes |
|---|---|---|---|
enabled |
boolean | true |
Set false to disable enforcement hooks. |
strict |
boolean | false |
Fail closed on statically unbounded source returns and soft warnings. |
blockUnboundedReturns |
boolean | false |
Block statically unbounded source returns before execution; strict: true implies blocking. |
maxReturnTokens |
number | 4000 |
Static advisory budget only; enforced by explicit strict/blocking modes. Maps to ~16 KB at ~4 chars/token (ASCII-biased). |
maxUnprocessedToolCalls |
number | legacy | Accepted for older configs; data-flow reduction and observed context cost are primary. |
quantitativePolicy |
object | {maxBytes: 8192, maxTokens: 4000, maxCharacters: 8192} |
Single policy source for static preflight and runtime budgets. |
policy |
object | alias | User-facing alias for quantitativePolicy; when both are set they are unified with policy as source. |
quantitativePolicy.maxBytes / policy.maxBytes |
number (bytes) | 8192 |
Static preflight budget; also unifies the runtime offload budget when readOffloadThreshold is unset (see resolveRuntimeByteBudget). Max 1,000,000,000. |
quantitativePolicy.maxTokens / policy.maxTokens |
number | 4000 |
Token budget for token-unit bounds. Max 1,000,000,000. |
quantitativePolicy.maxCharacters / policy.maxCharacters |
number | 8192 |
Character budget (UTF-16 code units). Max 1,000,000,000. |
readOffloadThreshold |
number (bytes) | 16384 |
Runtime auto-offload budget; wins over policy.maxBytes when set. Clamped to 256–1,000,000,000; clamps surface via ctx_status warnings. |
resultPolicy |
"auto" | "inline" | "offload" | "summarize" |
"auto" |
auto previews/offloads by threshold; inline preserves all results (including errors); offload forces handles for successful results; summarize uses threshold-based deterministic structural compression with exact recovery (no model call). |
errorCompactionThreshold |
number (bytes) | 4096 |
Model-boundary errors at/above this size are compacted (min 256). |
errorCompactionPreviewBytes |
number (bytes) | 4096 |
Visible budget retained by a compacted error, including the recovery recipe (256–64,000). |
offloadPreviewBytes |
number (bytes) | 2048 |
Structural first-use handle preview budget (256–4,096). |
runtimeAdvisoryThreshold |
number (bytes) | 0 |
Fabric boundary results at/above this size get a one-line advisory; 0 disables nudges. |
compactEditResults |
boolean | true |
Compact recognized verbose 2 KB+ successful edit envelopes (diff/patch) into an acknowledgment + recovery handle. Set false to keep them inline. |
notifyOnStart |
boolean | false |
Opt into the session-start toast. |
storeMaxBytes |
number (bytes) | 500000000 |
Transient store cap; cannot exceed 500 MB. Newest write is never evicted; over-budget writes evict oldest first (LRU) after TTL sweep. |
storeTtlMs |
number (ms) | 604800000 |
Transient entry TTL (1 week). Remembered facts use a separate persistent namespace (no TTL, 5 MB budget). |
compactStaleResults |
boolean | deprecated, ignored | Always effective false; already-exposed messages stay prefix-stable. Accepted so old configs still load. |
nestedResultThreshold |
number | deprecated, ignored | Always effective null; internal Fabric provider values are never rewritten. Accepted so old configs still load. |
Bad JSON in .pi/context-engineer.json does not throw: defaults load and a warning surfaces via ctx_status. Out-of-range numeric budgets are clamped (floored, min/max) with a ctx_status warning instead of failing startup.
Bytes vs tokens
Prefer byte budgets. Static preflight defaults to 8 KB (quantitativePolicy.maxBytes) while the runtime auto-offload default is 16 KB (readOffloadThreshold / DEFAULT_RUNTIME_OFFLOAD_BYTES) because static estimates are conservative upper bounds and runtime measures actual UTF-8 bytes. Token equivalents are heuristic: ~4 chars/token, so 16 KB ≈ 4,000 tokens for ASCII. Multibyte UTF-8 inflates bytes faster than the heuristic implies, and boundary messages report bytes with the ASCII-biased token estimate in parentheses. Invalid policy objects are ignored with a ctx_status warning; per-budget maximum is 1,000,000,000.
Selection and compression notes
ctx_read({ query })is literal and case-sensitive by default. Setregex: truefor a per-line JavaScript RegExp and/orignoreCase: true. Invalid regex and storage failures reject at the Pi tool boundary with structured error diagnostics; internal handlers use{ error, code, isError: true }. Storage uses explicitokstatus, so payloads and remembered facts beginningError:remain valid evidence. Regex/ignore-case scans are bounded and report scan truncation.- Hierarchical inputs exceeding
maxChunksfail before model calls, withsummary_input_budget_exceededand an exact recovery handle. Narrow the input or explicitly raise the budget; no first-chunk-only summary is substituted. Stored input reads stop after enough chunks to detect overflow. Structural/code stored summaries inspect a bounded 512 KB prefix and report truncation. Summaries are lossy navigation aids; recover original evidence when exact details matter.
Project configuration is re-read when the file's modification time changes, so tuning does not require an extension reload. /ce settings shows effective values, including defaults.
strictorblockUnboundedReturnsrestores fail-closed static enforcement.resultPolicyisauto(threshold-based previews),inline(never rewrite boundary results, including errors),offload(force addressable handles for successful results), orsummarize(threshold-based deterministic structural summaries with recovery handles; no child model call).ctx_readremains exempt from successful-result offloading to prevent recursive handles.errorCompactionThresholdanderrorCompactionPreviewBytesbound oversized model-facing errors, including the recovery recipe in the visible budget. Full original diagnostics are stored first; nested Fabric errors remain untouched.nestedResultThresholdis deprecated and ignored (effective valuenull): Fabric owns its internal transport limits. CE never substitutes handles for internal provider values.runtimeAdvisoryThreshold: 0disables repetitive size nudges; use a positive byte threshold to opt in.compactStaleResultsis deprecated and ignored (effective valuefalse, even in older configs that set it totrue). CE never shortens already-exposed messages; use Pi session compaction to reduce accumulated history.compactEditResults(defaulttrue) additionally offloads recognized successful 2 KB+ edit envelopes containing a verbose diff/patch. Set it tofalseto keep those results inline below the normal threshold.resultPolicy: "inline"still bypasses all successful-result rewriting.notifyOnStartcontrols the session-start toast, which is off by default.
Transient context-store entries default to one week (604800000 ms) and 500 MB (500000000 bytes). Remembered facts use a separate persistent store with no TTL and a 5 MB budget. Configured transient storage budgets cannot exceed the 500 MB cap. Payloads use a metadata index plus content-addressed private blobs; writes are atomic and cleanup removes expired, dangling, and over-budget entries.
Install in Pi
After installing from npm, Pi loads the extension and skill from the package manifest:
pi install npm:pi-context-engineer
Try it for one run without saving it to settings:
pi -e npm:pi-context-engineer
The package can also be loaded from a checkout:
pi --extension /path/to/pi-context-engineer/src/index.ts
Automated releases
Publishing is tag-driven through .github/workflows/publish.yml and uses npm trusted publishing (OIDC), so no npm token is stored in GitHub. Configure the repository once in the npm package settings under Trusted Publishers:
- Provider: GitHub Actions
- Owner:
p-yan-6908 - Repository:
pi-context-engineer - Workflow:
publish.yml
Then release a new version with:
npm version patch
git push origin main --follow-tags
The workflow verifies the vX.Y.Z tag matches package.json, runs the full test suite, and publishes to npm.
Verification
npm ci
npm test
The test suite includes more than 50 static data-flow cases plus live hook probes covering:
- aliases, destructuring, scalar result fields, numeric bounds passed through local helpers, and paging metadata
String/JSON.stringifyfalse reductions- unknown helper arguments
- callback projections and identity maps
Promise.all- bounded and unbounded Fovea calls
- nested Fabric provider proxies
- intermediate-result preservation, including large MCP/Fovea/CE provider proxies under every result policy
- automatic final offload, prefix-stable previews across retries and branches, legacy config compatibility, serialized
ctx_readenvelope caps, and bounded query work - runtime-first versus strict preflight, bounded delegation, and deterministic
ctx_summarizemodes - content deduplication, UTF-8 ranges, unchanged grep inputs (including valid ripgrep-only regex syntax), and lossless multi-text/error recovery
- generated adversarial transformations for aliases, destructuring, callbacks, async wrappers, computed access, loops, and Promise aggregates
- the explicit
src/context-effects.tsregistry used by the analyzer for source/select/compress/offload policy metadata, including current providers and PowerShell/process tools - JSON-path handle selection, structural previews, oversized error compaction, explicit result policies, and cross-runtime telemetry scopes
Run the generated adversarial suite directly with the normal npm test command. It fails closed when an unsafe transformation is classified as safe.
Benchmarking
The deterministic plumbing harness compares raw payload exposure with CE addressable storage, bounded selection, and hierarchical summarization. Its known-marker validators and mocked models test mechanics, not unchanged agent effectiveness:
npm run bench
It covers huge grep/JSON/build-log payloads, repeated reads, parallel provider-like results, Fovea-like source selection, large summaries, and nested-agent handoffs. The default one-warmup/three-iteration run reports median and p95 wall time to .tmp/context-benchmark.json; bench/result.schema.json defines retained release results with sourceCommit/runtime provenance and per-iteration samples. The v0.5.0 retained workload result is bench/results/v0.5.0.json; the frozen v0.4.0 result remains bench/results/v0.4.0.json for comparable baseline evidence.
The separate v0.5 quantitative-policy result is bench/results/v0.5.0-quantitative-policy.json; it emphasizes maintained legacy parity plus intentional symbolic-cap acceptance, not a misleading performance comparison. Metrics include Main-context exposure (not total token usage), Main input/output/injected tokens, child-model tokens, wall time, disk bytes, selected bytes retrieved, task correctness, quality-adjusted savings, and context efficiency.
Opt-in real runtime smoke tests launch the local pi CLI with CE and Fabric extensions. They require a configured model and are intentionally separate from CI's deterministic suite:
CE_RUN_E2E=1 PI_MODEL=openai-codex/gpt-5.6-luna npm run bench:e2e
Paired agent-effectiveness evaluation
Unlike the plumbing suite, this opt-in harness runs paired CE-on/off agents on isolated copies of multi-file tasks with hidden validators, late evidence, and stale-handle failures. It uses the shipped automatic offload policy, does not supply a retrieval needle, and reports task outcomes, actual observed Usage/cache/cost, latency, recovery calls, and retries. Unknown measurements remain explicitly partial/unavailable. A small fixture suite does not establish general non-inferiority.
npm run verify:effectiveness # offline fixture/scoring/accounting tests; also in npm test
CE_RUN_EFFECTIVENESS=1 PI_MODEL=openai-codex/gpt-5.6-luna npm run bench:effectiveness
Real runs require explicit opt-in and a configured model; they can incur provider usage. Reports default to .tmp/context-effectiveness.json. See bench/README.md for options and limitations.
License
MIT