Skip to content

[OMP] cursor-agent: managed-dispatch signature check ignores Cursor AgentRunRequest, refusing every turn #880

Closed

Description

@Zireael

Summary

On OMP 18.18.0 with @cortexkit/pi-magic-context 0.49.40, every turn using the cursor-agent provider is refused at provider dispatch:

Magic Context could not safely prepare this turn; send your message again. stage=provider dispatch elapsed=219ms recovery=no managed result; refused (provider request does not match this pass's managed dispatch (missing current receipt signature))

The session still executes tool calls, but every assistant message is committed as aborted with empty content. The result is a model that appears to make only tool calls and never produces a reply.

This is not context exhaustion or compaction pressure. At the failing turn the session was at 51.0% usage (120,363 / 236,000 tokens); earlier failures were at 7.9%, 16.0%, and 19.0%. The historian/compaction trigger was not firing, and Magic Context was injecting its managed m[0]/m[1] prefix normally.

Environment

  • @cortexkit/pi-magic-context: 0.49.40 installed
  • @cortexkit/pi-magic-context: 0.49.42 (current npm latest) inspected; same code path, no Cursor support
  • OMP: omp/18.18.0
  • Provider/API/model: cursor / cursor-agent / cursor/claude-opus-5-5
  • OS: Windows x64
  • Harness: OMP with the Pi plugin and the dispatch fence enabled
  • Session: 01a1b834-81d4-7222-be40-d509d88f1679 (~/.omp/agent/sessions/--D--Coding-Anki-German-Plus--/2026-11-07T22-50-11-284Z_01a1b834-81d4-7222-be40-d509d88f1679.jsonl)

Reproduction

  1. Start an OMP session whose active model uses the cursor-agent API (for example cursor/claude-opus-5-5).
  2. Send any ordinary message in a repository where Magic Context injects m[0]/m[1].
  3. Observe the provider-dispatch refusal above.

Five consecutive turns in the linked session failed this way at:

  • 2026-11-07T22:55:53.453Z
  • 2026-11-07T23:51:47.519Z
  • 2026-11-07T23:53:48.146Z
  • 2026-11-08T06:57:39.076Z
  • 2026-11-08T07:59:38.124Z

Evidence

The session contains 353 tool_execution_start events (read 154, bash 124, write 56, grep 17, ctx_memory 2) but only five assistant messages, all with:

  • stopReason: "aborted"
  • content: []
  • errorMessage: "Interrupted by user" or "Request was aborted"

There is no successful assistant text message in the session.

The last refusal is especially clear:

  • 2026-11-08T07:59:38.124Z: CONTEXT REFUSED ... missing current receipt signature
  • 2026-11-08T07:59:49.619Z through 08:00:03.446Z: five tool calls that directly match the new prompt (git log, the handover document, bd state, git diff, and a config read)

So the request reached Cursor and the model responded with tools, but the assistant turn was aborted before any text was committed.

A synthetic check against the installed piHasManagedSignature() produces:

cursor_payload_has_signature false
messages_payload_has_signature true

Root cause

Magic Context's managed-dispatch verifier assumes an OpenAI/Anthropic/Google-style request body. piHasManagedSignature() in dist/index.js (0.49.40 line 36856) checks only:

  • body.messages
  • body.input
  • Google contents

OMP's Cursor provider instead builds an AgentRunRequest whose history is in conversationState.rootPromptMessagesJson and turns, with the active message in action:

AgentRunRequest {
  conversationState: {
    rootPromptMessagesJson: Uint8Array[],
    turns: Uint8Array[],
    ...
  },
  action: { case: "userMessageAction", value: { userMessage: ... } },
  ...
}

See OMP packages/ai/src/providers/cursor.ts (buildGrpcRequestForWireMode, around lines 5510–5684).

Because there is no top-level messages or input, piHasManagedSignature() returns false for every Cursor request. The managed signature is present in the prepared Pi messages, but the verifier cannot see it in the Cursor wire object.

piConversationFields() has the same gap: it handles messages, input, or Google contents, and returns undefined for the Cursor shape. If the signature check were bypassed, the next check would fail with provider conversation identity could not be established.

This is distinct from #714 (Codex Responses content mismatch) and #686 (closed leading-system injection issue). The exact searches for cursor-agent and AgentRunRequest currently return no issues.

Why this looks like "tool calls only"

The refusal function calls ctx.abort() but does not throw or replace the provider payload. The before_provider_request handler then returns undefined, and OMP's emitBeforeProviderRequest() keeps the original payload when a handler returns nothing. The Cursor request is still sent, the model emits tool calls, and OMP executes them. The assistant message is marked aborted and its content is empty.

That means the refusal is a false positive that burns tokens and executes tools while suppressing the reply. This should be fixed independently of the Cursor payload support.

Suggested fix

  1. Add a Cursor adapter to piHasManagedSignature() and piConversationFields():
    • decode conversationState.rootPromptMessagesJson (JSON blobs) and conversationState.turns
    • include the active action.value.userMessage where applicable
    • search the decoded messages for the managed signature
  2. Until that adapter exists, explicitly skip the managed-dispatch fence for ctx.model.api === "cursor-agent" with a one-time diagnostic, instead of refusing every request.
  3. Make the refusal path actually block provider dispatch. A failed managed-dispatch check must not continue to send the request and execute tools.
  4. Add a regression test with a representative Cursor AgentRunRequest payload.

Workaround

There is no official workaround in 0.49.40/0.49.42. A local skip of the dispatch fence for cursor-agent is the minimal unblock; a non-Cursor model avoids this particular failure but is not a fix.

Activity

  1. magic-alfonso commented on Nov 8, 2026

    @magic-alfonso

    Thanks for the detailed report and the session timeline. We're checking both parts against the plugin and Oh My Pi's source now: the managed-dispatch check not understanding Cursor's AgentRunRequest shape, and a refusal that still lets the request through to the provider. The second is the more serious one, since a refused turn should never reach the model or run tools. We'll follow up here once both are verified and fixed.

  2. Zireael commented on Nov 8, 2026

    @Zireael
    ContributorAuthor

    Opened draft PR #881 for the Cursor wire-shape adapter.

    Summary of the proposed fix:

    • Hashes the prepared managed m[0]/m[1] messages into a receipt and matches those content hashes against conversationState.rootPromptMessagesJson blob IDs.
    • Derives retry identity from the root blob IDs plus the active user text, ignoring Cursor's random messageId.
    • Explicitly skips the structural row comparison for cursor-agent with a diagnostic until its root serializer is modeled. The managed receipt and conversation-identity checks remain enforced.

    Validation in the PR: 50 focused tests pass, plugin typecheck passes, Biome passes, and the receipt hashes were verified against a real OMP 18.18.0 buildGrpcRequest Cursor payload.

    This PR intentionally does not change the separate refusal-path behavior where ctx.abort() is called but OMP can still send the original payload; that part should remain tracked here. The PR is draft because this issue does not yet have design-approved.

  3. magic-alfonso commented on Nov 8, 2026

    @magic-alfonso

    Fixed in 0.49.43, thanks again for the detailed report and the session timeline.

    • Refused turns are now actually stopped. You were right that a refusal still let the request through: Pi and Oh My Pi catch errors from the request hook and keep the original payload, and aborting the session raced with dispatch. When Magic Context refuses a turn now, it hands the host a request that fails before anything is sent. The model isn't called, no tools run, the refusal message is shown, and the next turn works normally. This applies to every refusal on Pi and Oh My Pi, not just Cursor.
    • Cursor turns are recognized. The dispatch check now reads Cursor's AgentRunRequest, including the content-addressed history in rootPromptMessagesJson, by matching Magic Context's managed messages against Oh My Pi's own root hashes. Cursor turns are checked like any other provider rather than skipped. We tested against Oh My Pi's real Cursor request builder for 18.13.2, 18.18.0 and 18.18.1, including the <rules> entry 18.18.0 adds.
    • If a provider's request format isn't recognized, the refusal now names the provider API and the shape, so the next one is easy to report.

    Update the Pi package (@cortexkit/[email protected]) and restart Oh My Pi. No database migration is needed. If a Cursor turn is still refused, please post the refusal line; it now says why.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions