Skip to content

feat(delegate): label sessions and show estimated token savings - #150

Merged
Brooooooklyn merged 6 commits into
mainfrom
codex/delegate-token-efficiency
Sep 15, 2026
Merged

Brooooooklyn merged 6 commits into
mainfrom
codex/delegate-token-efficiency

Conversation

@Brooooooklyn

@Brooooooklyn Brooooooklyn commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Delegate sessions currently look like ordinary agent sessions, and local model usage does not tell users whether the worker reduced the text returned to their coding agent. Small status checks can also expand compact evidence into longer handoffs.

Add a Delegate badge and a per-session savings estimate to the list, plus a detail card showing evidence tokens, handoff tokens and their ratio. Positive results display tokens saved, negative results display extra tokens, and incomplete or unsupported runs show an unavailable estimate.

The estimate uses the fixed o200k_base vocabulary through Hugging Face's native Node tokenizers@0.23.2 binding: successful tool-result text deduplicated across all marked invocations in the session, minus every completed handoff. Tool errors, local reasoning and cache usage are excluded. Caller invocation, verification and transcript rereads are not observable here, so this is evidence compression rather than measured total coding-agent or account savings.

Implementation:

  • Persist a session-ID-bound metadata entry before each delegate invocation, outside the model context.
  • Calculate metrics from the active branch's full ancestry, preserving pre-compaction evidence while excluding abandoned branches, inherited fork work and ordinary-agent resumes.
  • Bundle the offline vocabulary with pinned regeneration dependencies, provenance and license. Encode asynchronously in bounded batches; a tokenizer failure keeps the Delegate badge with an unavailable estimate. Desktop packaging includes the vocabulary and the macOS arm64 native binding.
  • Index the result in SQLite schema revision 6 and serve it through the list/detail APIs. Detail uses one fresh file snapshot and withholds estimates for partial records.
  • Keep the compact-output and conditional-query prompt changes, caller guidance to consume final handoffs once, and CLI documentation.

Older sessions without delegate metadata remain unclassified; titles are never used to guess their origin. Existing blocked-handoff metadata can identify older, unforked delegates, but does not establish their savings. Forks need a matching delegate boundary for their own session ID. The index rebuild does not rewrite source session files.

Validation:

  • All 755 dashboard and desktop staging tests passed, including native tokenization, calculation, ingestion, APIs and UI. Coverage includes resumed-evidence deduplication, compaction, legacy forks, literal special-token strings, multilingual text, long inputs, concurrent encoding and tokenizer failures.
  • The native tokenizer matched the previous encoder's token IDs on 1,064 samples, including saved delegate output and deterministic multilingual cases.
  • Regenerated the bundled vocabulary with identical SHA-256 hashes. Verified the package file list includes the vocabulary and license, and a freshly staged desktop runtime loads its own native binding and returns the expected counts.
  • Full repository typecheck and lint, dashboard production build, changed-source formatting and whitespace checks passed.
  • 486 agent, delegate CLI and savings tests passed after updating the agent startup fixture to supply the delegate session context.
  • Browser-checked the actual list and detail components with representative positive, negative, zero and incomplete data.
  • A real read-only PR feat(delegate): label sessions and show estimated token savings #150 invocation using the worktree CLI and default local qwen3.8-27b-mxfp4-mlx model completed, persisted exactly one metadata boundary, and produced a compact JSON handoff. The measurement correctly reported 78 evidence tokens → 79 handoff tokens: 1 extra token.
  • Post-change total caller savings still require paired workflow measurements.

Note

Low Risk
Changes are mostly additive (metadata, index column, UI) with disposable DB rebuild on schema bump; delegate behavior and metrics are covered by tests and do not alter auth or payment paths.

Overview
Delegate sessions are now identifiable in the dashboard, with an estimated evidence-compression metric (unique successful tool-result text vs. final handoff, o200k_base), shown as a Delegate badge and token savings on the session list and a breakdown card on session detail.

Each delegate run writes a persisted mlx-delegate-session boundary (session id, not model context) so metrics ignore ordinary resumes, forked parents, and abandoned branches; ingestion stores the result in SQLite schema v6 (sessions.delegation), while detail API recomputes from a single file snapshot and marks partial writes as incomplete.

Worker and caller guidance is tightened: delegate system prompts favor compact handoffs, query budgets, and conditional gh steps; parent-agent instructions say to read the final handoff once instead of polling transcripts; CLI docs describe mlx delegate and how to interpret dashboard savings limits.

Reviewed by Cursor Bugbot for commit f5e93e7. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: d2059e50-595f-480e-a2a5-9b957ddfe941

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Sep 14, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_6434c36c-b420-4bdb-bd57-ca6682684a23)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-15T03:37:45.182858Z bb1f1a8 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 62d3bd1f8e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/cli.md Outdated
@cursor

cursor Bot commented Sep 14, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_6aeb1714-4468-4c67-bdf8-f07b9e5e44f5)

@Brooooooklyn Brooooooklyn changed the title fix(delegate): keep handoffs compact and avoid transcript polling feat(delegate): label sessions and show estimated token savings Sep 14, 2026
@cursor

cursor Bot commented Sep 15, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_259e9b13-5fa6-4220-a17d-a204018610c0)

@cursor

cursor Bot commented Sep 15, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_ca5cf7a7-a980-4a3d-b32c-31cc60759df9)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5e93e7441

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/dashboard/src/ingest/delegation.ts Outdated
Comment thread packages/dashboard/src/ingest/delegation.ts Outdated
@cursor

cursor Bot commented Sep 15, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_e324c507-f968-46d0-a8c9-6e56afdbbba1)

@cursor

cursor Bot commented Sep 15, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_0f23b0f0-de8b-452d-9435-3c93b560c82b)

@Brooooooklyn
Brooooooklyn merged commit 2caca3a into main Sep 15, 2026
8 checks passed
@Brooooooklyn
Brooooooklyn deleted the codex/delegate-token-efficiency branch September 15, 2026 04:11
Brooooooklyn added a commit that referenced this pull request Sep 15, 2026
`lfm2_paged_budget_forced_warm_continue_parity` reddened `main` on three
consecutive runs (#148, #149, #150) with one signature: the warm paged continue
and the flat carried-state oracle agreed for 23 bytes and then split at the
first content word after `<think>` ("keep" vs "what"), `first_diff_byte=Some(23)`
identical on every run, while the same commit passes on a developer host.

The failing run's own log rules out the carry path and the fixture: `turn1` and
`FLAT turn1` are both `"</think> Ok"`, and `cached_tokens` (23) and `num_tokens`
(32) match on both sides. What split is the first *free-form* decision inside the
thinking span — exactly the kind of near-tie the accepted ~1-ULP paged-vs-flat
kernel-stack class flips (`models/lfm2/attention.rs`, plus this file's own
"deliberately NOT asserted" note for long generations). Byte parity was being
asserted over a coin flip.

Fix: run turn 2 budget-forced like turn 1, so both arms commit the same forced
`</think>` first and the compared tokens are answer-region ones. Byte parity
stays fatal; nothing is weakened.

Also asserted now, because they were missing: the arms' turn-1 outputs are equal
(turn 1 is cold, so a mismatch is a fixture lottery and the message says so
instead of sending a reader into the continue path), the reuse lengths are equal,
and the committed token counts are equal (the qwen3 sibling already asserts that
one).

Verified: gate passes locally with both arms byte-identical (`[PASS] … warm
cached=23, tokens=32`); the count invariant was confirmed to fire by temporarily
shortening the flat oracle's turn 2 — "the arms committed different token counts
(warm=32 flat=31)".
Brooooooklyn added a commit that referenced this pull request Sep 15, 2026
…ns (#152)

## The problem, corrected

`main` failed `e2e lfm2` on three consecutive runs (#148, #149, #150),
and it is **not** a random flake — it is host-dependent determinism:

```
[warm-cont] turn1:             num_tokens=2 finish=length raw_text="</think> Ok"
[warm-cont] FLAT turn1:        num_tokens=2 finish=length raw_text="</think> Ok"     ← arms identical before the boundary
[warm-cont] turn2 WARM:        num_tokens=32 cached_tokens=23 finish=length
[warm-cont] turn2 FLAT oracle: num_tokens=32 cached_tokens=23 finish=length          ← same reuse, same length
WARM raw_text="<think> Okay, let's see. The user said to keep going from where I left off, but …"
FLAT raw_text="<think> Okay, let's see what the user is asking here. They said, \"Now …"
```

`first_diff_byte=Some(23)` is identical on all three red runs, and the
same commit passes locally. So the carry path is fine, the arms are
comparable, and what split is the first **free-form** decision inside
the thinking span — the near-tie that this file already documents as
flippable by the accepted ~1-ULP paged-vs-flat kernel-stack class
(`models/lfm2/attention.rs`, and the *"Deliberately NOT asserted:
byte-equality of the answer texts"* note on the memory-probe test). Byte
parity was being asserted over a coin flip.

I first tried downgrading that assertion to a diagnostic; an adversarial
review rightly rejected it — *"a stale or incorrect conv tensor can
preserve turn-1 output, reuse 23 tokens, and generate 32 incorrect
tokens"*, so invariants cannot replace the bite. This is the reviewer's
own recommended alternative instead.

## The change (one file)

- **Turn 2 is now budget-forced, like turn 1.** Both arms therefore
commit the same forced `</think>` first and the compared tokens are
answer-region ones, not a sentence-opening word choice. The boundary
under test (turn-1/turn-2 carry, `ContinuedLivePrefix`, 23-token prefix)
is untouched.
- **Byte parity stays fatal** — the assertion is unchanged in strength.
- **Three invariants added**, which the fixture was missing: the arms'
turn-1 outputs must be equal (turn 1 is cold, so a mismatch is a fixture
lottery, and the message says so instead of sending a reader into the
continue path), the reuse lengths must be equal, and the committed token
counts must be equal (the qwen3 sibling already asserts that one).

## Verification

| Check | Result |
| --- | --- |
| Targeted test locally | `[PASS] … (warm cached=23, tokens=32)`, both
arms byte-identical |
| Whole gate file locally | 9 passed, 0 failed |
| New count invariant actually fires | confirmed by temporarily
shortening the flat oracle's turn 2 → `the arms committed different
token counts (warm=32 flat=31)` |
| `cargo fmt --all` | touched only this file |
| **On the affected (CI) host** | CI dispatched on this branch: [run
34939712245](https://github.com/mlx-node/mlx-node/actions/runs/34939712245)
— result will be posted here |

## What is still not proven

The compared region is now answer-region, but a near-tie there is still
possible in principle, so this is a **fixture pin validated on the
affected host**, not a proof that the split was harmless rounding. The
durable instrument is the one `models/qwen3/model.rs` uses for its
mixed-step gate: logits at turn-2's first forward within
`test_support::bf16_scaled_tolerance`, plus `top - second > 2 ×
tolerance`. That needs a logits readout a test can reach — `ChatResult`
carries none and `Lfm2Inner` is crate-private — so it is a follow-up,
not something to fake with a looser text comparison here.

This clears the `model-test` gate that has been skipping `Publish` on
`main`.
Brooooooklyn added a commit that referenced this pull request Sep 15, 2026
…igning (#153)

## What failed

The v0.0.14 desktop release ([run
34937845040](https://github.com/mlx-node/mlx-node/actions/runs/34937845040))
failed in `Sign, notarize, publish` → `Verify bundle
(pre-notarization)`:

```
[2/5] every Mach-O carries a Team ID
  scanned  18 Mach-O files
[3/5] no build paths baked into load commands
  FAIL     leaks a build path: Contents/Resources/app/node_modules/tokenizers/tokenizers.darwin-arm64.node
           /Users/runner/work/tokenizers/tokenizers/bindings/node/target/aarch64-apple-darwin/release/deps/libnode.dylib
BUNDLE GATE: FAIL
```

## Why, and why it is our fixup to make

That path is the binary's **`LC_ID_DYLIB`** — its own install name,
written by the publisher's CI:

```
LC_ID_DYLIB  /Users/runner/work/tokenizers/tokenizers/bindings/node/target/aarch64-apple-darwin/release/deps/libnode.dylib
LC_LOAD_DYLIB /usr/lib/libc++.1.dylib
LC_LOAD_DYLIB /usr/lib/libiconv.2.dylib
LC_LOAD_DYLIB /usr/lib/libSystem.B.dylib
```

`tokenizers` arrived with the delegate token counter (#150) and is
loaded lazily by `packages/dashboard/src/ingest/tokenizer.ts`. This is
the same defect `mlx-core` and `@napi-rs/keyring` ship with, and
`package.ts` already rewrites both with `install_name_tool -id
@rpath/<file>` on the staged copy, before packager/codesign. Rewriting
the install name is therefore the established remedy here — not
excluding the package (the token counter is a live feature) and not
carving it out of the gate (the gate is what caught this).

`stage-app.ts` already ships exactly one slice of the package — its copy
filter keeps only `tokenizers.darwin-arm64.node` — so a single rewrite
covers the bundle.

## The change (one file)

```ts
// The dashboard's delegate token counter imports `tokenizers` lazily, and
// stage-app ships exactly one slice of it. Its upstream LC_ID_DYLIB carries the
// publisher's CI path …, which is the same defect mlx-core and keyring arrive
// with, and step [3/5] of verify-bundle refuses it. Rewrite the staged copy.
const tokenizersBinary = 'tokenizers.darwin-arm64.node';
run('install_name_tool', ['-id', `@rpath/${tokenizersBinary}`, join(STAGE_APP, 'node_modules', 'tokenizers', tokenizersBinary)]);
```

## Verification

| Check | Result |
| --- | --- |
| Step [3/5]'s own logic on the real binary, before the rewrite |
`["/Users/runner/work/tokenizers/tokenizers/bindings/node/target/aarch64-apple-darwin/release/deps/libnode.dylib"]`
|
| Same check after the rewrite | `[]` |
| `process.dlopen` of the rewritten copy | loads |
| `yarn typecheck` | clean |
| `verify-bundle.test.ts` + `stage-app.test.ts` | 64/64 |

I discarded an earlier attempt that carved third-party prebuilds out of
step [3/5]; the real defect turned out to be fixable in the bundle,
which keeps the gate absolute.

## Re-releasing needs your call

The workflow checks out `inputs.dry_run ? github.ref :
refs/tags/<release_tag>`, so:

- **Validate this fix** — dispatch with `dry_run: true` on `main` (or
this branch): it builds and signs, and step [3/5] should pass.
- **Publish v0.0.14** — not possible from the current tag: `v0.0.14`'s
tree does not contain this fix. Either move the tag onto the fixed
commit, or cut `0.0.15` from `main`.

My recommendation is `0.0.15`: the number `0.0.14` already exists (npm
may hold it), and re-pointing a tag after a publish makes one version
mean two different trees.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Packaging-only Mach-O install-name rewrite on the staged app tree;
mirrors existing mlx-core/keyring fixups with no auth or data-path
changes.
> 
> **Overview**
> Fixes desktop release **verify-bundle** step **[3/5]** failing on
`tokenizers.darwin-arm64.node`, which ships with the publisher’s CI path
in **`LC_ID_DYLIB`** (same issue already handled for **mlx-core** and
**keyring**).
> 
> **`package.ts`** now runs **`install_name_tool -id
@rpath/tokenizers.darwin-arm64.node`** on the staged copy under
`node_modules/tokenizers` before packager/codesign, so the delegate
token counter’s lazy **`tokenizers`** import can stay in the bundle
without weakening the gate.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
6b24c95. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
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.

1 participant