Files
hapi/shared/src/rpcMethods.ts
T
SSU-WEI HUANGandGitHub e6b9fd68e6 feat(pi): queue mid-turn messages by default; steer only via explicit per-message Steer button (#1480)
* feat(pi): queue mid-turn messages by default; steer only via explicit per-message Steer button

Pi (PyAgent) was the only flavor whose ordinary composer submission while
streaming bypassed the queue: the web resolved it to deliveryMode 'steer'
and the CLI dispatched a native steer into the running turn immediately,
with no waiting state. This makes Pi match Codex/Claude behavior (issue
#1466): mid-turn messages wait in the queue by default, and the operator
delivers one into the running turn with the new per-queued-message Steer
button.

- web: resolveMessageDeliveryMode now queues for every flavor; QueuedMessagesBar
  gains a Steer button (pi + thinking + remote-controlled + immediate rows)
  backed by a new useSteerQueuedMessage hook + api.steerMessage.
- hub: POST /sessions/:id/messages/:messageId/steer -> syncEngine.steerQueuedMessage
  (pi-only gate, remote-only, scheduled/absent/invoked rejection) -> RPC.
- cli: pi runner registers 'steer-queued-message'; a queued message is promoted
  into the active turn via the existing PiSteerDispatcher (target generation
  captured at promote time; turn-ended steers fall back to the prompt FIFO).
  Steers requested while the message is still preparing are deferred and
  promoted right after preparation completes.
- Removed the now-dead Alt+Enter / touch-hold queue gesture (its only purpose
  was opting out of the removed automatic steer).

Verified: bun typecheck; cli/hub/web/shared suites (env-dependent runner
integration + kimi wire-locator flakes reproduce on pristine upstream and
are unrelated to this diff).

* fix(pi): preserve queued messages on rejected steers and pin the steering generation

Addresses both Major findings from the HAPI Bot review of PR #1480.

- steerDispatcher: a deterministic native rejection (Pi responded error) now
  degrades the message to the ordinary prompt FIFO instead of emitting
  messages-consumed. A promoted queued message must not be lost just because
  the steer was rejected; the hub row stays queued until the FIFO delivers it.
  The indeterminate-timeout path keeps its fail-closed consume + escalate
  behavior (a duplicate delivery would be worse).
- runPi: the deferred-steer path now captures the streaming generation at RPC
  request time (Map<localId, generation>) instead of reading it after
  preparation completes, so a steer requested against turn G1 can never be
  injected into a turn G2 that started while the message was preparing — the
  dispatcher's generation-mismatch check degrades it to the FIFO.

Regression coverage: negative steer response preserves the entry via the FIFO
(no consume); generation rollover while preparing delivers as a normal prompt
at the next settle (no steer into the new turn).

Verified: bun typecheck; cli pi suites (48 tests), hub 1041, web 2301, shared
240 — all green; only the pre-existing environment-dependent runner
integration test fails locally (reproduces on pristine upstream).

* fix(pi): reject all scheduled steers and always clear deferred-steer bookkeeping

Addresses the two Minor findings from the HAPI Bot follow-up review.

- hub: steerQueuedMessage rejects every scheduled row — mature ones included —
  aligning the endpoint with the web UI (Steer is never offered on scheduled
  rows) and preserving scheduled-FIFO delivery semantics.
- cli: the deferred-steer bookkeeping map is now cleared in a finally on the
  preparation chain, covering the early exits (cancellation before/after
  attachment I/O, empty prepared message, preparation failure) that previously
  could leave a stale generation entry behind for the session lifetime.

Regression coverage: hub steer gate tests (mature scheduled row stays queued,
non-pi flavor rejected) and a runPi test proving cancellation wins over a
deferred steer (no steer/prompt/consume after preparation completes).

Verified: bun typecheck; hub 1043 pass, cli 2421 pass (only the pre-existing
environment-dependent runner integration suite fails locally), web 2301 and
shared 240 unchanged since their green runs.

* fix(web): reconcile stale queued rows when a steer returns invoked

Addresses the remaining Minor finding from the HAPI Bot follow-up review:
when the steer endpoint reports the message was already invoked and the
messages-consumed SSE was missed while the row was still queued, the hook
now marks the row consumed locally (mirroring useCancelQueuedMessage) so
the queued bar cannot keep a stale actionable row until the next sync.

Regression coverage: steer returning status 'invoked' reconciles the row
via markMessagesConsumed and shows no toast.

Verified: bun typecheck; web 2302 pass (hub/cli/shared unchanged since
their green runs).
2026-08-11 22:27:14 +08:00

54 lines
2.0 KiB
TypeScript

export const RPC_METHODS = {
Permission: 'permission',
Abort: 'abort',
Switch: 'switch',
SetSessionConfig: 'set-session-config',
KillSession: 'killSession',
HandoffLocal: 'handoff-local',
SpawnHappySession: 'spawn-happy-session',
StopSession: 'stop-session',
StopRunner: 'stop-runner',
ListMachineDirectory: 'list-directory',
PathExists: 'path-exists',
CursorChatStoreStatus: 'cursor-chat-store-status',
GitStatus: 'git-status',
GitDiffNumstat: 'git-diff-numstat',
GitDiffFile: 'git-diff-file',
ReadFile: 'readFile',
ReadGeneratedImage: 'readGeneratedImage',
WriteFile: 'writeFile',
ListDirectory: 'listDirectory',
StatFiles: 'statFiles',
GetDirectoryTree: 'getDirectoryTree',
UploadFile: 'uploadFile',
DeleteUpload: 'deleteUpload',
Ripgrep: 'ripgrep',
Difftastic: 'difftastic',
Bash: 'bash',
ListSlashCommands: 'listSlashCommands',
ListSkills: 'listSkills',
ListCodexModels: 'listCodexModels',
ListCodexSessions: 'listCodexSessions',
ArchiveCodexSession: 'archiveCodexSession',
ListCursorModels: 'listCursorModels',
ListPiModels: 'listPiModels',
ListPiSessions: 'listPiSessions',
ListOpencodeModels: 'listOpencodeModels',
ListOpencodeModelsForCwd: 'listOpencodeModelsForCwd',
ListGrokModelsForCwd: 'listGrokModelsForCwd',
ListGrokModels: 'listGrokModels',
ListGrokReasoningEffortOptions: 'listGrokReasoningEffortOptions',
ListCopilotModelsForCwd: 'listCopilotModelsForCwd',
ListCopilotModels: 'listCopilotModels',
ListOpencodeReasoningEffortOptions: 'listOpencodeReasoningEffortOptions',
ListAgyModels: 'listAgyModels',
/** Deliver one queued message into the active Pi turn (native steer). */
SteerQueuedMessage: 'steer-queued-message',
ForkConversation: 'fork-conversation',
RewindConversation: 'rewind-conversation',
} as const
export const RPC_TARGET_MISSING_ERROR_CODE = 'rpc_target_missing' as const
export type RpcMethod = typeof RPC_METHODS[keyof typeof RPC_METHODS]