Commit Graph

7 Commits

Author SHA1 Message Date
can1357 df0bb6e31a feat: implemented provider wire codecs and optimized telemetry and parsing
- Added internal protobuf wire codecs, message builders, and protocol definitions for Cursor and Devin providers.
- Deferred loading of OTel SDK and OTLP exporters and added bounded caches to optimize startup and lookup performance.
- Added SQLite-backed parse caching for legacy extension source analysis and streaming file chunk parsing for changelogs.
- Added support for rendering context usage overflow above 100% in the status line component.
2026-08-20 07:54:06 +02:00
Diogo Soares Rodrigues a363f95009 fix(cursor): pair interrupted calls and gate advisor bridge approval
Two independent bugs found in review.

A stream that dies mid-turn takes the terminal-error path: `settleH2`
rejects when the transport closes without `turnEnded`, so the flush on
the success path never runs. `connect_scm` and native todo blocks are
stamped `kCursorExecResolved` at start, so `agent-loop.ts` synthesizes
no placeholder and only their completion frame pairs a result - the call
was left unpaired and its card animating, and `buildSessionContext`
strips a dangling call from every rebuilt transcript. The catch path now
closes open blocks and pairs those server-owned calls with an
interrupted result. Exec-settled MCP blocks are excluded: the dispatch
that ran them owns their result and `drainInFlightDispatches` awaits it,
so pairing here would duplicate against the same id.

Separately, the advisor bridge supplied no `getToolContext`.
`ExtensionToolWrapper` reads the approval mode, per-tool policies and
`autoApprove` only from that execute-time context, so every wrapped
advisor bridge tool resolved as `yolo` with empty policies - a
configured `ask` or `deny` on `edit`/`grep` did not apply to native
frames. Advisors now get the same `ToolContextStore` as the primary
bridge.

Both are covered against the real paths: the interrupted call through
the HTTP/2 fixture server (a helper-level test passes even with the
catch-path flush removed), and approval through real deny policies.

(cherry picked from commit 5ace682578af96708caf88db2d44b9077a1c0e74)
2026-07-30 01:42:28 +02:00
can1357 daf2680298 fix(cursor): bound the dispatch drain by the abort signal and settle todo calls when the host sync callback throws
Two defects flagged by review on the final head, both in the new drain/settle
machinery:

- drainInFlightDispatches looped on Promise.all unconditionally. Exec handlers
  have no cancellation contract (the bridge calls tool.execute with no signal),
  so a hung or long-running tool held the terminal error hostage after Ctrl+C.
  The drain now returns once the signal aborts; late results were discarded
  after agent_end regardless.

- A host todoSync callback that throws (session persistence on disk failure)
  skipped both the paired result and toolcall_end, stranding the live card and
  leaving the resolved block to be stripped from rebuilt transcripts. The
  callback is now caught and the call settles as a failure carrying the thrown
  message.

Both regression tests fail without their fix (the first by hanging the stream).
2026-07-26 15:41:27 +02:00
Diogo Soares Rodrigues 90b89bf3c4 fix(cursor): drain exec dispatches on transport error and preserve raw failure state
Two P2 findings on the exec-handler path.

1. The success path waits for in-flight exec dispatches before pushing
   done, but the catch path did not. When the HTTP/2 completion rejects
   while a handler decoded from the last chunk is still running, the
   Agent finalizes the synthesized call from the terminal error and
   clears its Cursor result buffer; the handler then lands its real
   result after agent_end and it is discarded at the next run - even
   though the tool may already have performed side effects. The barrier
   is now a shared helper and inFlightDispatches is hoisted out of the
   try so both exits drain it.

2. resolveExecHandler always synthesized "Tool produced no transcript
   result" with isError: false for the TResult-only handler form. A
   rejected or error protocol result therefore reached Cursor as a
   failure while the rebuilt transcript recorded the same call as
   successful. Every exec result is a proto oneof whose only non-failure
   variant is success, so describeExecResult() derives the state from
   the variant and reuses its own error/reason text - the same string
   the server received.
2026-07-26 09:29:13 -03:00
Diogo Soares Rodrigues ffc5ad50f7 test(cursor): release the gated exec handler even when the assertion fails
The gate asserted the stream was unsettled and only then resolved
handlerDone. A failing assertion skipped the resolve, so the consuming
`for await` waited on a handler nobody would unblock and the real
failure surfaced as a timeout. Moved to a finally.
2026-07-26 09:29:13 -03:00
Diogo Soares Rodrigues c0f1f69e16 fix(cursor): await in-flight dispatches before emitting done
The h2 data loop parses every frame in a chunk synchronously and starts
each handleServerMessage with `void`, so the socket keeps draining while
a handler runs. Nothing tracked those promises: after h2Completion the
provider went straight to `done`.

When an exec request, turnEnded and the stream close arrive in ONE chunk
- routine, since the server has no reason to split them - the transport
completes while the exec handler (and any onToolResult transformer) is
still resolving. The Agent drains its Cursor result buffer on the
terminal event, so the result is reserved after the snapshot and never
persisted, leaving the synthesized (already resolved) toolCall block
unpaired and stripped on replay.

Dispatches are tracked in a Set and awaited after h2Completion. Each
already swallows its own rejection, so this only waits.

The test drives a real h2 server whose final chunk carries readArgs +
turnEnded, and releases the handler only once the server flushed its
response and the handler is known to be running - no wall-clock delay.
Verified it fails 5/5 without the barrier and passes 5/5 with it. A
microtask drain in place of the yield does NOT discriminate: the
client end handler is IO, not a microtask.
2026-07-26 09:29:13 -03:00
roboomp 4a9eaf63ba fix(ai): deferred Cursor completion until protocol end
- Kept turnEnded as application-level state without settling the HTTP/2 request.
- Surfaced late CONNECT, gRPC trailer, request, and abort failures.
- Rejected streams that end before turnEnded and covered terminal lifecycle cases.

Fixes #5634
2026-07-16 01:20:05 +00:00