fix(omo-ai): strip cursor-cli tool protocol frames from assistant messages - #7192
Closed
AceRothstein71 wants to merge 2 commits into
Closed
fix(omo-ai): strip cursor-cli tool protocol frames from assistant messages#7192AceRothstein71 wants to merge 2 commits into
AceRothstein71 wants to merge 2 commits into
Conversation
added 2 commits
August 24, 2026 18:51
…sages
The pinned senpi 2026.8.23 cursor-cli-oauth provider serializes every started
and completed Cursor tool_call event as a <cursor-cli-tool>{...}</cursor-cli-tool>
text delta, so provider-internal JSON landed in assistant prose and persisted
session history (code-yeongyu#7169). The upstream fix (senpi PR code-yeongyu#1107) is unreleased, so the
exact pin cannot be bumped yet.
Add the cursor-tool-frame-filter component: at the message_end boundary (which
senpi applies before session persistence) it strips frame spans from assistant
text blocks and drops blocks that held only frames. Frames are never converted
to host toolCall blocks because Cursor already executed those tools in its own
subprocess. Stripping skips fenced code blocks and requires a brace payload
(valid JSON or the upstream ...[truncated] shape), so prose and code examples
that merely resemble the protocol survive byte-identical.
Gated by omo-senpi-cursor-tool-frame-filter-disabled. Regression fixtures mirror
the exact renderToolFrame serialization of the pinned dist, including the
over-budget truncated shape.
Regenerated via plugin/scripts/build-extension.mjs to include the new cursor-tool-frame-filter component in the committed omo.js bundle.
Collaborator
|
Closing as superseded by upstream senpi#1107, which is included in the
This fallback should not be revived unchanged. It accepts any JSON object between tags rather than validating the Cursor protocol schema, so it can delete legitimate inline examples and corrupt adjacent non-frame text. Keep #7169 open only if the leak reproduces against the current Senpi pin. Reopen this PR only with such a reproduction and a schema-validating parser that preserves legitimate content and tool-call/result pairing. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds the
cursor-tool-frame-filtercomponent to the omo-senpi adapter. At themessage_endboundary it strips Cursor CLI tool protocol frames (<cursor-cli-tool>{...}</cursor-cli-tool>) from assistant text blocks and drops text blocks that held only frames, then returns the sanitized message as theMessageEndEventResult.messagereplacement. Also registers the component in the extension composition and rebuilds the committed extension bundle.Why
Issue #7169: with the
cursor-cli-oauthprovider selected, the pinned@code-yeongyu/senpi@2026.8.23engine serializes every started and completed Cursortool_callevent throughrenderToolFrame()into<cursor-cli-tool>JSON strings and routes them viapushTextDelta(), so provider-internal protocol lands in assistant prose and persists into session JSONL. The upstream fix (senpi PR #1107) is not yet released, so the exact pin cannot be bumped; this is the adapter-side boundary fix in the only repo-owned code that runs inside the senpi process.Design notes:
message_endreplacement via_replaceMessageInPlace(event.message, ...)beforesessionManager.appendMessage(event.message), so cleaned text is what gets stored and rendered (verified against the installed pinned dist).toolCallblocks, so Cursor-executed operations cannot run twice.{and either parses as JSON or carries the upstream over-budget...[truncated]suffix. Prose and code examples that merely resemble the protocol survive byte-identical.omo-senpi-cursor-tool-frame-filter-disabled.Relation to #7115: same class of bug (internal provider/host protocol surfacing as assistant-visible content in an oauth lane). That issue's fix crosses the Senpi dependency boundary and remains open; this fix uses the same lesson, sanitize at the provider boundary before content reaches assistant messages, applied at the seam this repo controls.
Verified
./filter/./index).bun test packages/omo-senpi/src/components/cursor-tool-frame-filter/). Fixtures byte-mirror the pinned distrenderToolFrame()output including the truncated over-budget shape; assertions cover frame removal, verbatim prose, byte-identical protocol-resembling code fences, empty-content collapse for frame-only turns, untouched user messages, and the disable flag.tsgo --noEmit -p packages/omo-senpi/tsconfig.jsonexit 0..omo/evidence/20260824-cursor-oauth-tool-leak/(local; the directory is gitignored by current repo policy), including the upstream leak source excerpt and the persistence-ordering proof lines from the installed senpi dist.test:senpichain cannot start on this machine because its first step (build:materialize-frontend) hits a pre-existing unfetchable submodule revision (shared-skills/upstreams/open-design); every downstream gate of that chain was executed individually instead.Risk
Low. The component is additive, flag-gated, and returns a replacement only when a frame was actually removed. Residual risk is a future senpi release changing the private frame format; the fixtures name the exact upstream file and function so drift is reviewable, and the component can be retired once the pin includes the upstream fix.
Fixes #7169
Summary by cubic
Removes Cursor CLI tool protocol frames from assistant messages to prevent internal
cursor-cli-oauthevents from leaking into prose and session history. Old:<cursor-cli-tool>{...}</cursor-cli-tool>strings appeared in assistant text; New: frames are stripped atmessage_endand frame-only blocks are dropped before persistence. Fixes #7169.cursor-tool-frame-filterinomo-senpi: sanitizes assistant text blocks, never converts frames to host tool calls, and ensures storage/rendering use the sanitized content.{and either parse as JSON or end with...[truncated].omo-senpi-cursor-tool-frame-filter-disabled.src/components/cursor-tool-frame-filter/filter.ts,src/components/cursor-tool-frame-filter/index.ts, and registration insrc/extension/component-list.ts; committed bundle updated underplugin/extensions/*.Written for commit 3a882c6. Summary will update on new commits.