Skip to content

fix(provider-acp): declare manual compaction support for omp - #2293

Merged
SawyerHood merged 5 commits into
get-bb:mainfrom
bradhallett:fix/acp-compaction-409
Sep 2, 2026
Merged

fix(provider-acp): declare manual compaction support for omp#2293
SawyerHood merged 5 commits into
get-bb:mainfrom
bradhallett:fix/acp-compaction-409

Conversation

@bradhallett

Copy link
Copy Markdown
Contributor

What was wrong

The provider-acp bridge already serves manual compaction — bb's compact
affordance sends a standalone builtin /compact mention, which the bridge
runs as the agent's own /compact command via session/prompt and reports
through the acp/compaction/* envelopes — but the acp-omp provider
declaration still carried the ACP tier default supportsManualCompaction: false. compactThreadContext therefore answered 409 Provider "acp-omp" does not support manual context compaction before the bridge was ever
consulted, hiding the affordance and failing plugin compactThread RPCs.
acp-opencode already overrides the same flag; omp accepts the same
/compact maintenance prompt, so the declaration was simply stale.

What changed

  • plugins/provider-acp/server.ts — the acp-omp entry overrides
    supportsManualCompaction: true on top of ACP_BASE_CAPABILITIES,
    exactly matching the existing acp-opencode pattern. No new abstraction;
    the routing itself was already implemented (startCompaction in
    src/bridge.ts). Cursor, Grok Build, and Hermes Agent stay false.
  • apps/server/test/public/public-thread-compaction.test.ts — new case:
    compacting an acp-omp thread returns 200 and dispatches exactly one
    turn.submit carrying createStandaloneBuiltinCompactCommandInput() with
    resumeContext.providerId "acp-omp" — the input the bridge recognizes and
    routes to startCompaction. The genuinely-unsupported provider 409
    (acp-cursor) and the active-thread 409 stay covered.
  • apps/server/test/services/plugins/first-party-provider-plugins.test.ts
    the acp-omp expectation-table entry tracks the flipped capability.
  • No wire, protocol, CLI, or doc surface changes.

How you verified

  • Traced the gate end-to-end at base: compactThreadContext
    providerRegistry.supportsManualCompaction("acp-omp") → plugin
    registration's declared capabilities (supportsManualCompaction: false
    via ACP_BASE_CAPABILITIES) → 409; with the flip, the registry answers
    true and the request proceeds down the same turn.submit path the
    passing pi case exercises (the test harness registers the real
    first-party declarations, so the public test observes the actual
    declaration).
  • Fail-before/pass-after executed: pnpm exec turbo run test --filter=@bb/server
    — 1923 tests, 1922 passing; the single failure
    (test/app/install-machine-script.test.ts) reproduces identically on a
    pristine main checkout at fff3ae8 (environmental, pre-existing).
    Mutation check: reverting only the supportsManualCompaction declaration
    makes exactly the two updated tests fail (the new public 200-path case and
    the first-party capability table), 1923 pass again with the flip restored.
  • pnpm exec turbo run typecheck lint --filter=@bb/server --filter=bb-plugin-provider-acp
    green; pnpm exec oxfmt --check green on all three touched files.
  • Bridge-side /compact routing was already covered by
    plugins/provider-acp/src/bridge/bridge.test.ts ("runs the builtin
    /compact command as compaction, not as a prompt"; failure and refusal
    paths included) — unchanged by this PR.

Fixes #2290

AGENT GENERATED

@bradhallett

Copy link
Copy Markdown
Contributor Author

Live production evidence that the bridge compaction path works end-to-end for acp-omp — gathered today (2026-08-22) on a real thread, independent of this PR's tests.

A local auto-compaction plugin (context-compact) queues /compact for busy threads instead of calling the direct RPC. On thread thr_4tm7g4ie78 (provider acp-omp, OMP 17.4.1) the queued command drained at the turn boundary and executed a real compaction through the provider:

14:31:39Z  plugin log: auto-compact thr_4tm7g4ie78: 73111 tok (7%) → queue
           (idle acp-omp threads in the same sweep: "provider acp-omp has no
            manual compaction; skipping its threads" — the 409 gate)
14:36:50Z  turn/completed (prior turn ends)
14:36:50Z  client/turn/requested: "/compact" (builtin command mention)
14:36:50Z  turn/started → item/started contextCompaction → turn/input/accepted
14:38:17Z  thread/compacted → turn/completed

Sequence numbers on the thread's event log: 19648 (prior turn/completed), 19649–19652 (the /compact turn: requested/started/contextCompaction/input-accepted), 19656 (thread/compacted), 19657 (turn/completed). Provider session id 01a027f5-2537-7000-98be-c1612765f2f8 is stable across all four.

Two conclusions:

  1. The turn-input compaction path already works for acp-omp in production — the same path this PR's new public-route test exercises (createStandaloneBuiltinCompactCommandInput → bridge → session/prompt "/compact"). The compaction above completed successfully at 14:38:17Z.
  2. The 409 is purely the declaration-level gate (supportsManualCompaction: false in ACP_BASE_CAPABILITIES) blocking the idle/direct compactThread RPC. Busy threads are servable today only because the queue path never consults that gate.

So the declaration flip in this PR doesn't enable an untested path — it un-gates the direct RPC onto a path production already exercises successfully.

AGENT GENERATED

@bradhallett

Copy link
Copy Markdown
Contributor Author

Rework addendum (reply to review of #2293)

Finding 1 (blocking — obsolete server.ts hunk): Addressed by the rebase. The branch now sits on current main (42658f987); the conflict in plugins/provider-acp/server.ts was resolved in main's favor and the one-line capability is re-expressed where #2325 put it: supportsManualCompaction: true on the acp-omp entry of plugins/provider-acp/src/known-agents.ts, exactly as the review's §6 step 1 specifies. The first-party capability table test and the acp-omp 200-path public-route case are ported onto main's layout (rebased commit 6287a8ec0).

Finding 2 (major — false thread/compacted on omp): Addressed in the same PR (new commit f8620a8ea), following the pi/#1721 precedent the review pointed at. While the compaction maintenance prompt is in flight (activePromptKind === "compaction" only), the bridge accumulates agent_message_chunk text; when the prompt ends with end_turn the completed text is classified:

  • "Compaction failed: Nothing to compact (session too small)" / "Compaction failed: Already compacted" (the exact live strings from §5b) → new skipped compaction outcome → translator emits provider.warning with category: "compaction-skipped" plus a completed turn boundary and no thread/compacted — identical treatment to pi's delta translation.
  • Any other "Compaction failed: …" text → turn fails with the agent's own message; no thread/compacted, no success banner.
  • Anything else → unchanged completed + thread/compacted.

Detection is scoped narrowly to the compaction turn and never inspects ordinary agent traffic; the message strings are matched verbatim from the report. Covered by three new tests (two bridge-level against the fake ACP agent — which gained a FAKE_ACP_COMPACT_AGENT_MESSAGE knob to replay omp's message-then-end_turn behavior — and one delta-translation unit test), each verified fail-before/pass-after by mutation:

  • early-return {status:'completed'} before the failure check → both bridge tests fail, including the exact false thread/compacted the report predicted;
  • disabling the skipped→warning mapping → the translation test fails expecting the provider/warning row;
  • reverting the known-agents.ts declaration → the public 200-path test fails with the 409 body and the capability table fails expected false to be true.

Finding 3 (minor — bridge behavior untested): The two bridge-level tests close this gap.

Finding 4 (minor — unrelated reformatting): Dropped during the rebase, but the repo-pinned oxfmt 0.64.0 re-introduces both hunks on main's current copy of the file (main's own blob fails oxfmt --check), so they are kept to stay format-clean; they are pure formatter output, no semantic change.

Finding 5/6 (ok): No wire/daemon/CLI changes; the skipped status is a bridge-internal envelope value (the wire still only carries existing delta kinds), so HOST_DAEMON_PROTOCOL_VERSION stays untouched.

Gates: @bb/provider-bridge-acp + bb-plugin-provider-acp test/typecheck/lint 8/8 green (258 + 76 tests); @bb/server typecheck/lint green, full suite 2005/2006 — the single failure (install-machine-script.test.ts, Abort trap: 6 in a nohup'd host-daemon join) reproduces identically with these changes stashed on clean main; scoped test/public/ + test/services/plugins/ 793/793.

AGENT GENERATED

@bradhallett
bradhallett force-pushed the fix/acp-compaction-409 branch 2 times, most recently from f8620a8 to 64dce8d Compare August 26, 2026 02:42
@bradhallett

bradhallett commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Rebase + SDK-version fix (CI failure root-caused)

The last push failed CI with check-npm-version-guard: the compaction-honesty change alters dist/provider-bridge-acp.js, and @get-bb/plugin-sdk@0.4.16 was already published, so the guard demanded a version bump (node scripts/bump-plugin-sdk.mjs --patch) — the same requirement packages/domain/src/plugin-sdk-version.ts + packages/plugin-sdk/package.json exist to enforce.

Done, plus a refresh onto current main (31d66d9d4 — the rework base 42658f987 had drifted):

  • Rebased via cherry-pick; both rework commit messages preserved, one new commit bumps @get-bb/plugin-sdk 0.4.22 → 0.4.23 using the repo script (both files moved together).
  • Feature-content parity vs the previous head verified marker-for-marker (declaration, skipped outcome, failure/skip classification, fake-agent knob) before running gates.

Gates:

  • node packages/plugin-sdk/scripts/check-npm-version-guard.mjsPASS (0.4.23 not on npm yet).
  • turbo run test typecheck lint --filter=@bb/provider-bridge-acp --filter=bb-plugin-provider-acp --force — 8/8 green, 291 tests (17 files).
  • turbo run test --filter=@bb/server --force — 2048/2049; the single failure (install-machine-script.test.ts, per-server data-dir default) is the documented pre-existing environmental one (reproduces on pristine main in this class of environment).
  • oxfmt --check clean on all touched files.

@SawyerHood — re-review when you get a chance; the two rework commits are unchanged in content, only rebased, plus the mechanical version bump.

@bradhallett

bradhallett commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Hardened: compaction classification no longer pins exact prose

Follow-up to the review thread: the no-op detection matched omp's failure strings verbatim, so an upstream reword of the Compaction failed: opener would have made a failed or no-op compaction indistinguishable from a shrunk context (false thread/compacted). omp's phrasing is TUI presentation code rather than a contract — asked for a structured signal in can1357/oh-my-pi#9786 — so until one exists, classification is now semantic:

  • failure detection: /\bcompaction failed\b/i anywhere in the streamed message (case-insensitive) — a failure sentence can no longer masquerade as success regardless of position, case, or surrounding text
  • no-op detection: /\b(?:nothing to compact|already compacted)\b/i — a reworded, lowercased reason still completes as a skip with the agent's text surfaced verbatim
  • every other Compaction failed: … message stays a failed turn

Two new bridge tests cover the drift cases (reworded no-op prose → skip; failure sentence preceded by other streamed text → failed), each mutation-verified: reverting no-op matching alone fails the first, reverting to the old prefix-only check fails the second with a false completed.

Gates: bridge 293/293 (17 files), typecheck, lint, oxfmt, check-npm-version-guard PASS (0.4.23 still unpublished).

@bradhallett

Copy link
Copy Markdown
Contributor Author

Refreshed onto current main — main took 0.4.23 (mention aliases) and 0.4.24 (lost connection reason) while this branch carried its own bridge dist changes, so the branch re-bases its SDK claim to 0.4.25. No code changes beyond the merge: the classifier and drift tests are untouched, and main's new ACP fixes (inbound-dispatch-after-kill #2594, topology marker race #2571, intentional-stop write race #2552) merged clean.

Gates: bridge 304/304 (17 files — includes the 11 tests main added), typecheck, lint, oxfmt, check-npm-version-guard PASS (0.4.25 unpublished).

@bradhallett

Copy link
Copy Markdown
Contributor Author

Refreshed onto current main (SDK 0.4.27 → branch claims 0.4.28). This merge also adopts main's two newer conventions where they collided with this branch's regions: the comment-free style from #2624 (the classifier's rationale lives in the PR description) and main's independently-landed supportsManualCompaction: true on the omp entry (duplicate removed; the compaction turn/classifier themselves are not on main). Gates: bridge 305/305, plugin 76/76, typecheck, lint, oxfmt, version-guard PASS.

@bradhallett

Copy link
Copy Markdown
Contributor Author

CI status on head 9a026a4: pre-existing load-marginal tests, not this diff

The Tests (packages) job has failed 4× in a row on this head, each time in a different package this PR doesn't touch, always as a timeout, and each preceded by a runner-wide 30s ACP-native model discovery … timed out warning from the bridge suite (that discovery test still passes — it's a victim of the same CPU starvation, not a cause).

Run Failing test Budget
33185878477 bb-plugin-provider-pi catalog.test.ts › "re-reads model scope after the catalog child restarts" 5000ms
33186437294 @bb/host-workspace › "does not overflow the call stack merging a large subdirectory" (hook) 10000ms
33187231762 pi catalog.test.ts (same test) 5000ms

The diff is exonerated

Running the exact CI shard at full turbo parallelism on a pristine origin/main checkout reproduces the identical failure set:

pnpm exec turbo run test --continue \
  --filter='!@bb/server' --filter='!@bb/app' --filter='!@bb/integration-tests' --force

@bb/host-workspace "Hook timed out in 10000ms" (2), bb-plugin-provider-pi (catalog child restart), bb-plugin-tasks, @bb/plugin-build — same set as on this branch. Every one of these tests passes when its package runs alone (verified locally: bridge 305/305, provider-acp 76/76, the pi catalog test in isolation ×2, server 2072/2073 with the known env failure). Main's and #2626's green packages-job logs contain zero discovery-timeout lines; this branch's slightly longer bridge suite appears to be what tips the marginal scheduling over on the CI runner.

Filed separately with the deterministic main-branch repro and fix options: #2653.

Ask

Fork PRs can't re-run their own checks (Must have admin rights to Repository). If a maintainer re-runs the packages job — or #2653 lands a budget/concurrency fix — this should go green. All local gates on this head pass (typecheck, lint, oxfmt, version guard, full suites above).

@bradhallett

Copy link
Copy Markdown
Contributor Author

cc @SawyerHood — evidence above that the four red Tests (packages) runs on this head are pre-existing load-marginal tests (deterministic repro on pristine main: #2653). All local gates pass; a re-run of the packages job is the only thing between this and green. Re-review welcome when you get a chance.

@bradhallett

Copy link
Copy Markdown
Contributor Author

CI is green on this head after #2655 landed the --concurrency=4 turbo cap (#2653 confirmed + closed). All checks pass; mergeable state CLEAN. Ready for re-review.

@bradhallett

Copy link
Copy Markdown
Contributor Author

Refreshed against main: absorbed the #2701 SDK bump via merge + re-bump to 0.4.30 (merge 03dde13a2, conflicts were the two version files only). Classifier and tests untouched; bridge 305/305, typecheck/lint/version-guard green locally; CI green on the new head.

bradhallett and others added 5 commits September 2, 2026 18:45
The provider-acp bridge already serves manual compaction by sending the
agent's own /compact command as a session/prompt maintenance turn, but
the acp-omp declaration kept the tier default supportsManualCompaction:
false, so compactThreadContext answered 409 before the bridge was ever
consulted. Override the capability per agent, matching acp-opencode.

The public compaction suite gains the acp-omp dispatch case (200 with
the standalone builtin /compact turn submitted for the acp provider),
and the first-party capability table tracks the flip.
omp resolves every consumed builtin slash command with end_turn, including
a /compact that printed "Compaction failed: ..." as an ordinary agent
message, so an end_turn compaction prompt is only a shrunk context when
the agent did not spend the turn reporting a failure (get-bb#2290 review).

While the compaction maintenance prompt is in flight the bridge now keeps
the streamed agent message text. On end_turn it classifies the completed
text: the two known no-op messages ("Nothing to compact (session too
small)", "Already compacted" — the same strings pi prints, get-bb#1721) settle
the compaction as skipped, which the translator reports as a
compaction-skipped warning plus a clean turn boundary with no
thread/compacted; any other "Compaction failed:" text fails the turn with
the agent's reason. Detection only runs for the compaction prompt, never
for ordinary agent traffic.
The compaction-honesty change alters dist/provider-bridge-acp.js, so the
published package content differs from 0.4.22; check-npm-version-guard
requires a new version before CI passes.
…act prose

omp's failure phrasing is TUI presentation code, not a contract (asked
for a structured signal in can1357/oh-my-pi#9786). Match the failure
opener and the no-op reasons case-insensitively anywhere in the streamed
message: a reworded reason stays a skip, a failure sentence preceded by
other text still fails the turn, and no reword can report a compacted
context that never compacted.
@SawyerHood
SawyerHood force-pushed the fix/acp-compaction-409 branch from 03dde13 to eece92f Compare September 2, 2026 18:53
@bb-slop-cop

bb-slop-cop Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

🚨 SLOP COP 🚨 · review

I am SlopCop. I am reviewing this pull request now.

I will check security, code quality, performance, architecture, and the main user path.

@SawyerHood
SawyerHood merged commit e8bae14 into get-bb:main Sep 2, 2026
22 of 23 checks passed
agentMessage: string,
): Record<string, unknown> {
const text = agentMessage.trim();
if (!COMPACTION_FAILURE_PATTERN.test(text)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚨 slopcop/review — The OMP text rule changes compaction results for every ACP agent.

compactionOutcomeForEndTurn also runs for OpenCode and custom ACP providers. Successful text that mentions "compaction failed" now produces a failed turn.

An unsupported failure phrase can still produce a false thread/compacted event. Put this rule behind an OMP dialect hook.

Keep the generic end_turn result unchanged. Add a regression test for generic ACP message text.

parsed.data.update,
);
if (chunk.success) {
session.compactionAgentMessage +=

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚨 slopcop/review — The bridge keeps all compaction message text without a size limit.

Each message chunk makes this string larger. finishCompaction does not clear the string.

A verbose ACP agent can keep a large string for the session. The repeated copies and final scans also increase CPU use.

Use a fixed-size diagnostic buffer or a small classifier state. Clear the stored text after compaction.

Add a test for the size limit and the cleanup.

@bb-slop-cop bb-slop-cop Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚨 SLOP COP 🚨 · review

This pull request lets users request manual context compaction for OMP. It also checks OMP messages before it reports compaction success.

I found two problems.

  1. Major: The OMP text rule runs for every ACP agent. OpenCode and custom ACP providers can now receive incorrect compaction results.
  2. Medium: The bridge keeps all compaction message text without a size limit. The text also remains after compaction completes.

The first issue needs an OMP dialect hook. Generic ACP agents must keep the existing end_turn behavior.

The second issue needs a size limit and cleanup in finishCompaction.

I found no authentication, permission, command, path, network, dependency, or secret problem.

The Pi provider has similar warning output. However, Pi receives a structured error field, so a shared text parser would not help.

Local checks passed:

  • The ACP bridge passed 305 tests.
  • The ACP provider plugin passed 81 tests.
  • The two server files passed 7 tests.
  • Type checks passed for the bridge, provider plugin, and server.
  • All GitHub checks passed.

I could not run a real OMP browser test because this machine does not have the omp command. The server route and fake bridge tests passed.

I left this review as a comment. I did not approve the pull request or request changes.

ymichael added a commit that referenced this pull request Sep 2, 2026
## Human comments

## What was wrong

The ACP bridge classified every successful manual-compaction response by
looking for OMP-specific phrases such as `compaction failed`. That
policy leaked across dialects, so a generic ACP or OpenCode response
describing an earlier failed attempt could be reported as a failed
compaction even when the agent's final result was successful.

## What changed

- Move end-turn compaction classification into the ACP dialect contract.
- Apply the existing failure/no-op prose handling only to OMP.
- Let generic ACP and OpenCode keep the completed result reported by
their successful end turn.
- Add regressions for generic ACP and OpenCode while preserving OMP
failed and skipped outcomes.
- No wire contract or host-daemon protocol change is required.

## How you verified

- `pnpm exec turbo run test typecheck --filter=@bb/provider-bridge-acp
--force` (307 tests passed)
- `pnpm exec turbo run test --filter=@bb/server --force --
"test/public/public-thread-compaction.test.ts"` (4 tests passed)
- `pnpm exec turbo run build:types --filter=@get-bb/plugin-sdk --force`
- `pnpm exec turbo run test --filter=@bb/plugin-api-map --force` (75
tests passed; public SDK inventory unchanged)
- `pnpm exec oxfmt --check
packages/provider-bridge-acp/src/bridge/bridge.ts
packages/provider-bridge-acp/src/bridge/bridge.test.ts
packages/provider-bridge-acp/src/dialect.ts`
- `git diff --check origin/main...HEAD`

Follow-up to #2293.

> AGENT GENERATED
@bradhallett

Copy link
Copy Markdown
Contributor Author

Follow-up for the two review findings on this merged commit: #3013. Status: the Major finding (compaction text rule applying to all ACP agents) was already fixed on main by #2957; #3013 closes the Medium finding — compaction prose accumulation is now gated to the omp dialect, clamped at 64 KiB head-preserved, and cleared in finishCompaction, with mutation-checked regression tests for the cap and the cleanup.

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.

compactThread returns 409 for acp-omp although the provider-acp bridge already implements /compact

2 participants