Skip to content

feat(memory): Codex parity — register PreToolUse (Bash|apply_patch) and PreCompact, clamp the Codex envelope — release 0.28.0 (issue #95) - #152

Merged
zaxbysauce merged 2 commits into
mainfrom
fix/issue-95-codex-parity
Sep 10, 2026
Merged

feat(memory): Codex parity — register PreToolUse (Bash|apply_patch) and PreCompact, clamp the Codex envelope — release 0.28.0 (issue #95)#152
zaxbysauce merged 2 commits into
mainfrom
fix/issue-95-codex-parity

Conversation

@zaxbysauce

@zaxbysauce zaxbysauce commented Sep 10, 2026

Copy link
Copy Markdown
Owner

fix(memory): Codex parity — register PreToolUse and PreCompact, clamp the Codex envelope (0.28.0, issue #95)

Closes #95. Part of Workstream D (milestone "Workstream D — Right moment on every host").

What changed

  • Codex PreToolUse is registered in hooks.codex.json with the matcher Bash|apply_patch — written from a live tool_name dump (codex-cli 0.153.0, Windows, 2026-09-09, capture preserved in the issue comment): shell operations emit the hook tool name Bash (the model-facing tool is exec_command), file patches emit apply_patch, and Codex treats an all-alphanumeric/pipe matcher as EXACT alternation. MCP tools (mcp__<server>__<tool>) and write_stdin are deliberately OUT (query derivation is shell/file-patch based).
  • Codex PreCompact is registered, wired to the same shared precompact handler Claude uses. Upstream Codex drops additionalContext on PreCompact (decision control only, verified 2026-09-09 from codex-rs source at tag rust-v0.153.0 == main), so its functional payload on Codex is the delivery-ledger clear before compaction; post-compaction re-injection rides the registered SessionStart, which upstream fires with source=compact after every compaction.
  • PostCompact stays unregistered, deferred to [Workstream D] PR 2 of 8: Query-aware re-injection after compaction (and settle whether the PreCompact fence survives) #118 (upstream Codex PostCompact carries only trigger: manual|auto — no compact_summary; [Workstream D] PR 2 of 8: Query-aware re-injection after compaction (and settle whether the PreCompact fence survives) #118 owns the shared compaction handlers). Pinned by a new absence test so it cannot silently appear.
  • Codex envelope clamp (the issue's addendum "units trap"): hook envelopes on Codex are clamped to 8000 encoded chars ≈ 2000 tokens — 20% margin under upstream's DEFAULT_HOOK_OUTPUT_TOKEN_LIMIT = 2_500, above which Codex spills the text to a file and the model sees only a head/tail preview. The former 9000-char default sat within ~10% of the spill point. The cap binds even when an operator sets a larger ZMEM_CTX_BUDGET; Claude/ZCode are untouched.
  • Pins flipped, not deleted (issue requirement): the Codex PreToolUse absence pin, the SKILL parity needles, and the Codex PreCompact absence pin in test_recall_hook_fence.py are inverted with failure messages naming [Workstream D] PR 3 of 8: Codex parity — register PreToolUse and PreCompact/PostCompact after the hook failure is attributed #95 and the re-probe convention. Deployment docs now state the per-entry trusted-hash re-approval requirement (new hook entries are silently skipped until re-approved in the TUI /hooks review).
  • Release 0.28.0: all seven host-facing manifests + dated CHANGELOG section + regenerated release-manifest.json (release_gate.py --emit-manifest, 74 files, digest 775d9436).

Live tool_name dump (codex-cli 0.153.0, Windows 11, 2026-09-09 — as the issue requires)

Probe method: isolated CODEX_HOME (scratch dir, no contact with the user config), user-level capture hooks, codex exec --dangerously-bypass-hook-trust (documented flag for vetted automation), throwaway workdir. Captured PreToolUse payloads:

operation tool_name tool_input
shell command Bash {"command": "echo zmem95-shell-probe"}
file creation apply_patch {"command": "*** Begin Patch\n*** Add File: …probe.txt\n+hello-from-patch\n*** End Patch"}

Also captured: SessionStart (source: "startup", plus model/permission_mode), UserPromptSubmit (prompt), PostToolUse (mirrors tool names + tool_response). MCP decision: OUT — MCP tools emit mcp__<server>__<tool> (source-verified) and their JSON arguments don't fit the query derivation. A -c model_context_window=1500 run did not trigger live compaction, so compaction shapes were verified from source instead (the AC's compaction canary lane is exercised by the registered-path drive below; the force-/compact lane belongs to #118).

Acceptance criteria → evidence

AC Evidence
Canary passes on Codex (session start, user prompt, pre-tool, compaction) host_canary.py --host codex --self-test exit 0; pre-tool + precompact proven by the registered-path drive (matcher simulation + real launcher chain: PreToolUse fence carries the seeded row within the 8000-char cap; PreCompact drive clears the session ledger) — frozen checks 1-2, red pre-fix / green post-fix
Pinned absence test inverted, not deleted (message names #95) test_pretool_registered_on_zcode_claude_and_codex, test_codex_json_registers_precompact — frozen checks 3-4
SKILL timing matrix updated with probe date wired-state parity text + ceiling pin test_memory_skill_pins_wired_codex_parity_state (needles: `Bash
Explicit additional-context limit below the spill threshold + maximal-fence test CODEX_ENVELOPE_CAP_CHARS = 8000 clamp + clamp e2e (codex ≤8000 with ZMEM_CTX_BUDGET=50000; claude control unclamped) — frozen check 6
Launcher e2e with a Codex-shaped payload proves fence injection adapter section [7] + frozen check 2 leg 3
Surfacing-only contract (never deny, fail-open exit 0) no decision fields asserted on the driven envelope; garbage-stdin fail-open check green on both sides
Trust-gate docs SKILL.md "Reapprove hooks" bullet + CHANGELOG "Changed" entry (per-entry sha256, silent-skip consequence, bypass flag for vetted automation) — frozen check 8
Version bump 0.28.0 across 7 manifests + CHANGELOG + release-manifest.json, release_gate.py green — frozen check 9

Validation

  • 9/9 frozen acceptance checks GREEN post-fix (7 were RED at the red checkpoint on base a4e79c5; AMEND rows in the manifest document check-harness fixes with closed reasons)
  • node tests/test_codex_adapter.js: 94 passed, 0 failed
  • python -m unittest tests.test_pretool_inject tests.test_recall_hook_fence: 49 tests OK
  • node tests/test_launcher.js: 217 passed, 0 failed; tests.test_host_canary + tests.test_codex_manifest_contract: 25 tests OK
  • canaries --host codex|claude|zcode --self-test: all exit 0
  • python scripts/release_gate.py: green at 0.28.0

Deployment note

After upgrading, Codex users must re-approve the hook surface once (the two new entries carry new trusted hashes; untrusted entries are silently skipped). This is documented in SKILL.md and the CHANGELOG.

Merge status

MERGE_STATE: AWAITING_USER_APPROVAL — this PR is not merged by the agent; a separate, recorded human approval bound to the exact PR head SHA is required (issue-tracer Phase 5.1).

Waivers

None. No Full-Resolution Contract clauses waived. (The PostCompact half of the issue's registration item is dispositioned — not waived — as owned by open dependency #118, with an absence pin, SKILL pointer, and this disclosure; the compaction canary's force-/compact lane is likewise #118's scope item.)


Summary by cubic

Gives Codex parity with Claude and ZCode: Codex now fires PreToolUse and PreCompact hooks for pre-tool hazard recall and compaction ledger-clear, and the Codex envelope is clamped below upstream's output-spill limit. Fixes issue #95.

Changes

  • Registers Codex PreToolUse with matcher Bash|apply_patch, derived from a live tool-name dump (codex-cli 0.153.0); MCP tools and write_stdin stay excluded.
  • Registers Codex PreCompact through the same shared precompact handler Claude uses; since upstream Codex drops additionalContext on that event, the payload is the delivery-ledger clear before compaction, with post-compaction re-injection riding SessionStart.
  • Leaves PostCompact unregistered, deferred to issue [Workstream D] PR 2 of 8: Query-aware re-injection after compaction (and settle whether the PreCompact fence survives) #118, with an absence-pin test so it cannot silently reappear.
  • Clamps the Codex envelope to 8000 chars (~2000 tokens), a 20% margin under Codex's 2,500-token spill threshold; the cap binds even when ZMEM_CTX_BUDGET is larger, and an operator-set value above it warns on stderr before clamping. Claude/ZCode budgets are unchanged.
  • Reserves systemMessage's exact encoded size from the envelope budget before content is fitted; a systemMessage that alone cannot fit is dropped fail-open.
  • Flips former absence pins to registration assertions; bumps version to 0.28.0 across all manifests and the release manifest.

Deployment

  • Codex users must re-approve the hook surface once after upgrading: the two new entries carry new trusted hashes, and untrusted entries are silently skipped until re-approved in the TUI /hooks review.

Written for commit bb54467. Summary will update on new commits.

Review in cubic

…nd PreCompact, clamp the Codex envelope — release 0.28.0 (issue #95)

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 17 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread hooks/zmem-launch.js Outdated
Comment thread tests/test_recall_hook_fence.py

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

It is a versioned release whose correctness depends on cryptographic release-manifest hashes and on upstream Codex behavior claims (spill limit, matcher semantics, trust gate) that cannot be independently verified here, warranting human sign-off.

Pull request overview

This PR brings the Codex host to parity with Claude/ZCode by registering two previously-omitted coding-host hooks and adding a Codex-specific safety cap, then cutting release 0.28.0. It implements Workstream D-3 (issue #95): Codex now fires PreToolUse (pre-tool hazard recall) and PreCompact (delivery-ledger clear before compaction) through the already-wired pretool-recall/precompact launcher verbs, and clamps Codex hook envelopes below upstream's output-spill threshold so a full fence is never spilled to a file and hidden from the model. PostCompact is intentionally deferred to #118.

Changes:

  • Register Codex PreToolUse (matcher Bash|apply_patch, from a live tool-name dump) and PreCompact (shared precompact handler) in hooks/hooks.codex.json; MCP/write_stdin excluded.
  • Add a Codex-only envelope clamp CODEX_ENVELOPE_CAP_CHARS = 8000 in hooks/zmem-launch.js, applied via Math.min(resolveBudget, cap) so it binds even a large operator-set ZMEM_CTX_BUDGET; Claude/ZCode unaffected.
  • Flip the former absence pins to registration assertions, add new adapter/clamp tests, update docs, and bump the version to 0.28.0 across all manifests and the regenerated release manifest.
File summaries
File Description
hooks/hooks.codex.json Registers PreToolUse (matcher `Bash
hooks/zmem-launch.js Adds CODEX_ENVELOPE_CAP_CHARS=8000 and clamps the resolved budget for the codex host.
hooks/zmem-pretool-recall.sh Comment-only update reflecting the new Codex registration and matcher.
skills/memory/SKILL.md Rewrites the Codex parity/timing text to the wired state; documents the cap and the hook re-approval requirement.
tests/test_codex_adapter.js New sections [6] (clamp) and [7] (registered pre-tool/precompact drive) plus PostCompact-absence pin.
tests/test_pretool_inject.py Inverts the Codex PreToolUse absence pin; asserts matcher and PreCompact registration; updated SKILL needle test.
tests/test_recall_hook_fence.py Inverts the Codex PreCompact absence pin to a registration assertion.
release-manifest.json Updated file hashes (5 files) and version → 0.28.0; new digest.
README.md Documents the Codex 8000-char envelope cap for ZMEM_CTX_BUDGET.
CHANGELOG.md Adds the dated 0.28.0 section.
hermes-plugin/plugin.yaml, marketplace.json, .claude-plugin/plugin.json, .claude-plugin/marketplace.json, .codex-plugin/plugin.json, .zcode-plugin/plugin.json, .agents/plugins/marketplace.json Version bump 0.27.0 → 0.28.0.
Review details
  • Files reviewed: 17/17 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/test_codex_adapter.js Outdated
// clamp the codex envelope to 8000 encoded chars (~2000 tokens at the
// plugin's 4-chars/token estimator) EVEN when the operator sets a huge
// ZMEM_CTX_BUDGET — and the clamp must be codex-specific.
function giantEnvelopeEncoded(hostVar, hostValue) {
@zaxbysauce

Copy link
Copy Markdown
Owner Author

swarm-pr-review — PR #152 (fix/issue-95-codex-parity)

Structured review at head 28fbe21d (base a4e79c56), Profile B (Claude Code native subagents): 4 base explorer lanes covering all six review dimensions → independent reviewer validation → critic challenge on the highest-severity confirmed finding. 14 candidates generated, 0 CRITICAL/HIGH, 1 CONFIRMED MEDIUM after critic review, 5 CONFIRMED LOW/INFO (mostly advisory/test-fidelity), 3 PRE_EXISTING (out of PR scope), 4 DISPROVED.

Scope check: local diff independently matched the GitHub API stats (17 files, +402/-82) and the PR's own acceptance-criteria table was spot-checked against the diff; test-count claims in the PR body were independently re-run rather than trusted (node tests/test_codex_adapter.js: 94/0 ✅, python -m unittest tests.test_pretool_inject tests.test_recall_hook_fence: 49 OK ✅, node tests/test_launcher.js: 217/0 ✅, tests.test_host_canary+tests.test_codex_manifest_contract: 25 OK ✅, canaries all exit 0 ✅, release_gate.py: green at 0.28.0 ✅). The one unverifiable claim ("9/9 frozen acceptance checks GREEN") was checked and found to match this repo's established issue-tracer convention (session-local frozen-check artifacts referenced from committed tests, e.g. tests/test_host_canary.py:477, tests/test_trust_recall.py:303) — not a red flag.

🟠 MEDIUM — README Upgrade path doesn't surface the new Codex hook re-approval requirement, and existing detection can't catch the resulting silent no-op

README.md:486-515 (Upgrade section)

The PR correctly documents the new per-entry hash-trust requirement in CHANGELOG.md:39-52 and skills/memory/SKILL.md:1659-1668 ("an untrusted entry is silently skipped until re-approved"), but the README's ### Upgrade section — which CHANGELOG.md:9-11 itself names as the canonical user-facing upgrade doc — never mentions it. That alone would be a minor docs gap, except:

  • README.md:156-159 does describe Codex hook trust in general, but that text predates this PR (byte-identical at base a4e79c5) and describes the existing detection surface as reliable ("an untrusted install correctly reports hook-not-fired until you re-trust — that is the canary doing its job").
  • That claim is now stale: scripts/host_canary.py only ever fabricates a SessionStart payload (grep PreToolUse|PreCompact in that file returns nothing) — it never exercises the two new entries this PR ships.
  • Because this PR's hooks/hooks.codex.json diff is purely additive, SessionStart's own hash is unchanged and stays trusted, so the canary keeps reporting green while PreToolUse/PreCompact sit untrusted and silently skipped.
  • skills/memory/scripts/doctor.py:1322-1330 compounds this: repo_has_codex_hooks keys off <repo>/.codex/hooks.json, which is absent on a plugin-cache install, so a trusted project falls straight through to status="pass" / "Codex project trust is present" without ever inspecting per-entry state.

Net effect: a Codex user who upgrades via the documented path can end up with this PR's headline deliverable (pre-tool hazard recall before Bash/apply_patch) silently not running, with both automated health checks (host_canary.py, doctor.py) reporting green. Repo precedent (0.18.0 / issue #108, CHANGELOG.md:407-409) updated README in the same release for an equivalent hook-surface change, so this isn't a pre-existing pattern this PR merely continues — it's a regression in that convention.

Suggested fix: add an explicit "Codex users: re-approve hooks (/hooks review) after this upgrade" line to README's Upgrade section, and consider extending host_canary.py/doctor.py to actually probe PreToolUse/PreCompact trust state per-entry rather than only SessionStart, so a future silent-skip is caught automatically instead of only in docs.

(One caveat carried over from the critic pass, stated for transparency: this verdict is rendered against the trust model this PR itself asserts — per-entry SHA-256 over the raw hook entry. If Codex instead hashes the ${PLUGIN_ROOT}-expanded command, every entry would rehash on each version bump, SessionStart would go untrusted too, the canary would fire red already, and this would drop to LOW. That distinction couldn't be verified in-repo; if it turns out the PR's own hashing description is wrong, that's a separate, smaller documentation defect, not grounds to drop this finding.)


Advisory / low-severity confirmed findings (no action required to merge, worth a follow-up issue or a quick pass)

ID Severity Summary
sec-01 LOW The "Codex treats Bash|apply_patch as exact alternation" claim is verified only by a JS reimplementation of that assumption inside the test itself (tests/test_codex_adapter.js:487-502), not against real codex-rs source — self-consistency, not an external-drift guard.
C3 LOW Same root cause from the compatibility lane — the matcher-exactness model has no vendored/pinned upstream reference; if upstream semantics differ (e.g. true regex vs. hardcoded exact-split), only tool names containing Bash/apply_patch as a substring would be affected.
sec-02 INFO CODEX_ENVELOPE_CAP_CHARS = 8000 and its calibration against upstream's DEFAULT_HOOK_OUTPUT_TOKEN_LIMIT = 2_500 are documented only in prose comments, with no pinned constant or CI check to catch future upstream drift (fail-soft, not unsafe).
C4 INFO Minor 3-way doc drift: hooks/zmem-pretool-recall.sh:24-28's comment omits write_stdin from the excluded-tool list that SKILL.md, CHANGELOG.md, and the tests all mention. No behavioral effect.
TF-01 INFO No test feeds malformed/non-JSON stdin specifically through the new Codex PreToolUse/PreCompact registration (only well-formed payloads in tests/test_codex_adapter.js). Empirically verified fail-open still holds (printf 'not json' | node hooks/zmem-launch.js pretool-recall{}, rc=0), so this is a coverage gap, not a live defect.

Pre-existing, out of this PR's scope (surfaced for awareness only)

  • sec-03 / sibling to hooks/lib/zmem-recall-body.py:908-926: PreCompact's exclude-ID computation has no read-side carve-out (only the write side is guarded), predating this PR and untouched by its diff. Currently masked on Codex since upstream drops additionalContext on PreCompact anyway, so the PR doesn't newly expose it in a way that matters — but the same gap exists for Claude's PreCompact leg and may be worth its own issue.
  • RP-01: the new Codex PreToolUse registration adds measured ~1.4–2.2s warm latency per Bash/apply_patch call, but this reuses the exact code path and timeout (15s) already shipped for Claude's broader Edit|Write|MultiEdit|NotebookEdit|Bash matcher — not new exposure.
  • C2: README's "optional" language for repo-local .codex/hooks.json is accurate (confirmed against doctor.py's optional_codex vs required["codex_plugin"] split); the adjacent claim that "Claude/ZCode plugin surfaces are first-class now" (omitting Codex) is stale but predates this PR's diff.

Disproved candidates (listed for completeness, no action needed)

  • c1 — the claimed "SessionStart re-injection needs source=compact handling" was traced end-to-end and found to be a false comparison: zmem-session-start.sh injects unconditionally on every SessionStart using the delivery ledger's exclusion state (which PreCompact clears), with no source-based branching required.
  • c2 — the hypothesized fail-open gap in zmem-recall-body.py's early main() setup doesn't hold; every cited statement is individually exception-guarded.
  • TF-02 — canary "failure-looking" stderr lines only print inside actual failure branches; cannot appear during a passing run (and the file isn't touched by this PR).
  • RP-02 — the "new concurrency exposure" premise is false: Claude already registers the identical shared PreToolUse+PreCompact handler pair this PR extends to Codex, and ledger writes are atomic (tmp-file + os.replace).

Recommendation: Approve after addressing the MEDIUM finding (README upgrade-path documentation gap) — it's docs-only to fix but has a real, currently-undetectable functional consequence for upgrading Codex users. Everything else is advisory/pre-existing and doesn't block merge.

Review conducted per swarm-pr-review protocol (4 explorer lanes / 2 independent reviewer batches / 1 critic challenge, ~30 subagent-minutes, models: sonnet explorers+reviewers, opus critic). Full candidate ledger available on request.

… warn on operator clamp (review PRR-001..005)
@zaxbysauce

Copy link
Copy Markdown
Owner Author

Both findings addressed in bb54467 (pushed to the PR):

  1. P2 (systemMessage vs the 8,000-char cap) — confirmed real: translate() appended systemMessage after fitEnvelope(), so the assembled envelope could exceed the cap (reproduced at 8501 bytes; the sysMsg-only branch was entirely unclamped). Fix: systemMessage's exact encoded marginal size is now reserved from the budget before the content is fitted, so the assembled envelope always fits; a systemMessage that alone cannot fit is dropped (fail-open) rather than guaranteed to spill. Covered by three new tests in tests/test_codex_adapter.js §[6] (modest sysMsg preserved within cap; un-fittable sysMsg dropped; claude control unaffected), all proven discriminating against the pre-fix code. Severity note: the only current systemMessage producer is the ~175-char drift notice, so trigger probability today is ~zero — independent critic downgraded it Critical → MEDIUM, fixed regardless.

  2. P3 (stale class docstring) — updated to "claude.json (and codex.json since issue [Workstream D] PR 3 of 8: Codex parity — register PreToolUse and PreCompact/PostCompact after the hook failure is attributed #95)".

Also fixed in the same pass (from this PR's own swarm review): the codex clamp now warns on stderr when an explicitly-set ZMEM_CTX_BUDGET exceeds the cap (no warning on the 9000 host default), and a CJK/token-estimator margin caveat was added to the launcher comment, README, and SKILL.md.

Suites: node tests/test_codex_adapter.js 99/0; test_pretool_inject + test_recall_hook_fence + test_inject_kill_switch 76 OK; release_gate.py --verify-manifest fresh (digest 2024db35).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 7 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="hooks/zmem-launch.js">

<violation number="1" location="hooks/zmem-launch.js:566">
P2: When a payload contains only `systemMessage`, this reservation can still emit an envelope larger than `budget` because the base envelope bytes are not checked. Check the complete system-only envelope and fail open when it cannot fit.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread hooks/zmem-launch.js
Comment on lines +566 to +574
if (sysMsg) {
const sysBytes = encodedSize(_withSystemMessage(makeEnvelope(host, hookName, ""), sysMsg))
- encodedSize(makeEnvelope(host, hookName, ""));
if (sysBytes >= budget) {
sysMsg = null;
} else {
contentBudget = budget - sysBytes;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When a payload contains only systemMessage, this reservation can still emit an envelope larger than budget because the base envelope bytes are not checked. Check the complete system-only envelope and fail open when it cannot fit.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/zmem-launch.js, line 566:

<comment>When a payload contains only `systemMessage`, this reservation can still emit an envelope larger than `budget` because the base envelope bytes are not checked. Check the complete system-only envelope and fail open when it cannot fit.</comment>

<file context>
@@ -544,36 +544,63 @@ function fitEnvelope(host, hookName, content, budget) {
     const hasContent = !(content === undefined || content === null || content === "");
     if (!hasContent && !sysMsg) return {};
+    let contentBudget = budget;
+    if (sysMsg) {
+        const sysBytes = encodedSize(_withSystemMessage(makeEnvelope(host, hookName, ""), sysMsg))
+            - encodedSize(makeEnvelope(host, hookName, ""));
</file context>
Suggested change
if (sysMsg) {
const sysBytes = encodedSize(_withSystemMessage(makeEnvelope(host, hookName, ""), sysMsg))
- encodedSize(makeEnvelope(host, hookName, ""));
if (sysBytes >= budget) {
sysMsg = null;
} else {
contentBudget = budget - sysBytes;
}
}
if (sysMsg) {
const emptyEnvelope = makeEnvelope(host, hookName, "");
const emptyBytes = encodedSize(emptyEnvelope);
const withSys = _withSystemMessage(emptyEnvelope, sysMsg);
if (encodedSize(withSys) > budget) {
if (!hasContent) return {};
sysMsg = null;
} else {
const sysBytes = encodedSize(withSys) - emptyBytes;
contentBudget = budget - sysBytes;
}
}

@zaxbysauce
zaxbysauce merged commit c404f2f into main Sep 10, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Workstream D] PR 3 of 8: Codex parity — register PreToolUse and PreCompact/PostCompact after the hook failure is attributed

2 participants