Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ What else a session gives you:
- **Native goals where supported.** Opt in with `goals: true`, check
`Session.goal`, and observe native goal snapshots without a host continuation
loop ([contract](docs/spec/goals.md)).
- **Manual compaction:** Claude, Codex and Pi SDK/RPC expose optional
- **Manual compaction:** Claude, Codex, Pi SDK/RPC, Grok, Qoder, Cursor and OpenCode v2 expose optional
`session.compact()`. It waits for native completion; a timeout stops
waiting, not the operation. Read `runtime.manualCompaction` for the
declared policy ([contract](docs/spec/manual-compaction.md)).
Expand Down
24 changes: 21 additions & 3 deletions docs/runtimes/cursor.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ model and token usage; `run.steer(text)` adds input mid run; `run.cancel()`
stops it. Progress arrives through `send(…, { onDelta })` as updates, each a
record with a `type`; `run.stream()` offers a coarser message view of the
same run, including compaction statuses omitted from `onDelta` in SDK 1.2.0.
The native `Agent.summarize()` starts a manual summary run; OAR exposes no
manual-compaction control. An agent takes one run at a time: a second `send`
The native `Agent.summarize()` starts a manual summary run; OAR exposes it as
idle-only `compact()`. An agent takes one run at a time: a second `send`
while one runs is refused (`already has active run`, probed).

The credential is `CURSOR_API_KEY`, or, without it, the key
Expand All @@ -67,7 +67,7 @@ OAR reads out of it. Control calls are request/response record pairs.
| `@cursor/sdk` package | An optional peer dependency of `@botiverse/oar` that the host installs (`@cursor/sdk@1.2.0`) and hands over through `createCursorRuntime` ([installation](#installation-and-account-usage)); the agent runs in the host process. Cursor is not in `defaultRuntimes`; the `oar` CLI depends on the SDK and adds cursor itself ([CLI registry](../../packages/cli/src/runtimes.ts)). |
| Local agent | `Session.id` is the `agentId`; `SessionOptions.resume` reopens it with `Agent.resume`. |
| Agent state after open | One `cursor/agent_opened` frame with the `model` (and `effort`) the SDK holds. |
| Run | A turn: a prompt is one `send`; `run.wait()`'s answer is the `cursor/run_result` frame carrying `turn_ended`. |
| Run | A turn: a prompt is one `send`; `run.wait()`'s answer is the `cursor/run_result` frame carrying `turn_ended`. The SDK run id is `spanId` on results, `run.stream()` frames and `onDelta` frames; early callbacks retain their ingress time while waiting for `send` to return that id. |
| Updates | One frame per `onDelta` update, and one `cursor/stream/<type>` frame per `run.stream()` message ([compaction](#observation-children-and-history)); a subagent's updates arrive inside its `task` call and are attributed to it (`agentPath`, tier `attributed`). |
| Steer, queue, abort | `run.steer`; an adapter-held queue sent as the next run; `run.cancel`. |
| `Cursor.auth` | `login` is `Cursor.auth.login` (the URL relayed, the SDK polls), `logout` is `Cursor.auth.logout` (local only: the minted key is not revoked), `authStatus` is `Cursor.auth.status` ([login](#login), [logout](#logout)). |
Expand Down Expand Up @@ -282,6 +282,24 @@ counted once, through `onDelta`. No trigger or failure reason is reported.
still counts as completed compaction. Cancellation can end the run with only
a summary start observed; OAR adds no compaction end in that case.

Manual `compact()` binds its accepted response to the SDK's returned run id
before observing messages. Native `run_id` becomes `spanId`; `sessionId`
remains the root agent id. Children retain task-call `agentPath` attribution,
and a different native `agent_id` cannot supply root summary evidence. A root terminal summary and that run's result
are both required before queued input drains. A completed summary wins a late
run cancellation. An empty new agent is refused by the SDK. SDK 1.2.0 `summarize()` accepts no
options and passes empty send options internally, so `local.force` cannot
take over a restored active run for compact. An isolated
[storage probe](../../experiments/cursor-summarize-force-offline.ts) confirms
that refusal with and without an ignored force argument, without an executor
or network call. OAR returns the native refusal and does not expire that run.
Caller timeout or
signal stops waiting only. Native cancellation with no terminal summary returns
aborted using the run result, without inventing a summary event. A known run
end without a terminal summary is unconfirmed but releases the gate; lost
observation without a run end keeps it reserved. See the
[SDK 1.2.0 native sample and account-free regressions](../../experiments/manual-compaction-followup-2026-11-02.md#cursor-account-scope-and-usage).

Each view preserves its own internal order; the merged frames keep OAR's
reception order, with no global native order guaranteed between the views.
After `run.wait()` settles, OAR allows the iterator up to two seconds to
Expand Down
19 changes: 15 additions & 4 deletions docs/runtimes/grok.md
Original file line number Diff line number Diff line change
Expand Up @@ -666,10 +666,21 @@ The earlier foreground-style 1.0.25 recording (root 53321/355, child
(`live-contract/subagent`; [wire-shape test](../../tests/acp/acp-grok-wire-shapes.test.ts)).

Native [explicit compaction dispatch](https://github.com/xai-org/grok-build/blob/bc7f02e/crates/codegen/xai-grok-shell/src/agent/mvp_agent/acp_agent.rs#L2561-L2563)
exists through `x.ai/compact_conversation`; automatic compaction remains
native harness behavior. OAR has no typed compaction operation, and no ACP
frame reports one, so Grok sessions never carry `compaction_started` or
`compaction_ended` events.
exists on the wire as `_x.ai/compact_conversation`. OAR exposes idle-only
`compact()`: the dedicated response proves completion; `RequestError.data.kind`
`compact_failed` / `compact_cancelled` supplies failure / cancellation.
Method-not-found, invalid-parameter and missing-session errors are
`rejected / runtime_refused`, not compaction failures. A null answer is
unconfirmed; both release the gate. Transport loss without a native answer
remains unconfirmed and reserved. The correlated answer frame carries
`responseTo`; its publication releases both the gate and compacting status.
Only proven completion/failure/cancellation projects
`compaction_ended.requestId` for the OAR request it explicitly answers, without
inventing a start. The native `auto_compact_completed` notification also occurs
after a manual request, so neither trigger nor request ownership is inferred
from its name. Empty history returns a native successful no-op. Queued input
waits behind this operation; abort uses native `session/cancel`.
[1.0.70 controls and limits](../../experiments/manual-compaction-followup-2026-11-02.md).

### Tools, MCP, and permissions

Expand Down
22 changes: 22 additions & 0 deletions docs/runtimes/opencode.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ without an account
Native startup failures on v1 are under
[startup and compatibility](#startup-and-compatibility).

## Manual compaction

The v2 session exposes idle-only `compact()` through native `/compact`.
`session_info_update._meta["opencode/compaction"]` supplies status, messageId
and reason. OAR projects that native messageId as `compactionId` on both start
and end events, including automatic events. The manual control binds the first
manual start in its exclusive idle window and requires the same identity at
completion to prove compaction. The correlated prompt RPC answer
(`responseTo`) releases the gate and status together, after preceding markers.
Wrong, old and absent ids cannot establish completion; an answer without a
matching marker returns unconfirmed. A native RPC error is retained as a
frame and returns failed. Response usage follows the ordinary profile
projection when supplied; no missing counts are inferred. Ordinary `end_turn` does not prove success:
provider errors, invalid summary headings and empty history all produced
structured failed markers with ordinary success in 2.2.5. Cancellation supplies
`error.type: aborted`. Same-name configured commands did not override the
native compact path in the control.

V1 has no session member: a project command can replace `/compact`, and its
structural completion is unverified. The common v1/v2 Runtime remains
undeclared; inspect the opened session. [2.2.5 evidence and limitations](../../experiments/manual-compaction-followup-2026-11-02.md).

## Windows isolated-process cleanup

Disposing an active tool can stop OpenCode's shell before its tool and child
Expand Down
18 changes: 18 additions & 0 deletions docs/runtimes/qoder.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ Those replay checks used no real account or paid provider. The
[model-selection probe](../../experiments/qoder-native/model-selection-report.md)
uses an existing login for catalog reads and opens, without a model prompt.

## Manual compaction

`compact()` is idle-only and sends the native built-in `/compact` with the OAR
request id as its command UUID. Qoder 1.1.81 intercepts this built-in before
custom commands. OAR requires that command's started lifecycle, a root manual
`compact_boundary`, and its completed/cancelled lifecycle. Results carry no
input UUID, so an ordinary successful result cannot prove compaction.
Empty history ends without a boundary and returns `failed / not_compacted`.
A completed boundary remains `completed` if cancellation arrives later; the
ordinary result remains its own native fact (success in the measured race).
`queue()` is retained until the command settles. The compact request carries
`settledBy: "native_response"`; exact `command_uuid` equality attaches
`responseTo` to its terminal lifecycle. That frame releases the input gate
and compacting status together, even without an ordinary result. Earlier
turn and compaction events stay in place and do not release the operation. Native `system/status
compacting` projects the start without an inferred trigger.
[Controls and evidence](../../experiments/manual-compaction-followup-2026-11-02.md).

## Transport versions

[src] Qoder 1.1.81 explicitly rejects the old SDK worker with
Expand Down
6 changes: 5 additions & 1 deletion docs/spec/conversation.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ that caller gets `queued`, with any other merged or undelivered ids still
reported. An accepted input is never returned for automatic resubmission.

Every carried id must name an input in this session and agent whose latest
attempt is an accepted withdraw, with no later delivery. OAR checks the whole
attempt is an accepted withdraw with no later delivery, or whose current
owner is a queue accepted with `queued: "adapter"`. The latter permits the
adapter's recorded dispatch; a refused carry attempt does not take ownership
away from that held queue. Native queues, already dispatched inputs, foreign
lineage and a pending ownership change are ineligible. OAR checks the whole
set before publishing the prompt attempt and refuses it without invoking the
runtime if any id fails. The rejected request retains the caller's claim for
audit, but conversation and chat projections leave those original inputs alone.
Expand Down
Loading
Loading