fix(rpc): classic --mode rpc applies set_client_info capabilities

Merged
CO
15h ago
+289-9

Fixes #4006
Part of sisyphuslabs/omo-desktop-app#4042

Problem

In classic --mode rpc (one client over stdio), set_client_info capabilities were accepted (success: true) and then ignored. The handler assigned the declared set only inside the shared-host clientInfo branch, and a classic handler has no clientInfo. A client that declared question therefore still got one select per question; the #4002 real-CLI QA run found this. It contradicts docs/rpc.md "Client information".

Change (connection-handler.ts only)

  • Classic set_client_info takes effect. On a connection without clientInfo, a set_client_info that carries capabilities replaces the handler's set. This happens after the senpi#3811 role check, so a refused declaration changes nothing.
  • Width-only calls keep the set. A set_client_info without capabilities changes nothing, and the launch-time SENPI_RPC_CLIENT_CAPABILITIES stays the initial value.
  • extension_events follows the declaration. When extension_events comes or goes, the handler re-subscribes extension events. The existing subscription code moved into subscribeExtensionEvents unchanged, and rebindSession calls it as before.
  • Shared hosts are untouched. That branch keeps exactly what it did. Per-connection shared-host behaviour is #4000's, which is stacked on #3814; this PR is independent of it and based on main.
  • Size: the handler grows by 14 net lines (+23/−9). The logic needs the handler's closure state (the set, the session and the event output), so it stays in the file as two small closures rather than a new module.

Evidence (exact head 6a04f92d04 on main 4dd6f3c135, Mini)

RED on main, with test/suite/regressions/issue-4006-classic-set-client-info-capabilities.test.ts and the source unchanged:

FAIL classic --mode rpc over stdio > a client that declares question gets the structured question frame   expected 'select' to be 'question'
FAIL classic --mode rpc over stdio > a width-only set_client_info keeps the declared question            expected 'select' to be 'question'
FAIL classic handler extension_events > declaring extension_events subscribes, and declaring without it unsubscribes   expected +0 to be 1
FAIL classic handler extension_events > the launch capabilities are the initial set, which a declaration then replaces   expected 1 to be +0
Tests  4 failed | 1 passed (5)

The test has two layers:

  • The two question cases spawn the real CLI in --mode rpc over stdio, with an extension that asks a question.
  • The extension_events cases run a classic handler on the faux harness: subscribe, resize (still subscribed), withdraw (unsubscribed), launch default replaced, and a role-refused declaration that changes nothing.

GREEN at 6a04f92d04 with the workspace rebuilt:

  • The new file plus the RPC capability/attach/host suites present on main: 18 files, 107 tests pass.
  • Every rpc-* test under test/ and test/suite/, plus the rpc, issue-3811, issue-2951 and issue-4006 regressions, with CI=1:
Test Files  240 passed | 1 skipped (241)
     Tests  1464 passed | 9 skipped (1473)
  • tsc --noEmit -p tsconfig.json: exit 0.
  • biome check on the changed files: clean.
  • node scripts/check-pr-changelog.mjs --base 4dd6f3c135: changelog-gate: PASS.

Coordination

#4000 also edits the set_client_info case and the extension-event subscription in connection-handler.ts. Whichever lands second rebases. The two changes agree: in #4000 a classic handler keeps no clientInfo, so this branch is still the classic path there.


Summary by cubic

Fixes #4006, part of sisyphuslabs/omo-desktop-app#4042. In classic --mode rpc, a client's set_client_info capabilities were accepted but ignored, so a client declaring question still got single select dialogs. The classic handler now applies the declaration after the connection-role check, cancels pending structured questions when question is withdrawn, and re-subscribes extension events when extension_events comes or goes.

  • Extracted subscribeExtensionEvents and added replaceClientCapabilities for the classic path; the shared-host branch is unchanged.
  • A set_client_info without capabilities keeps the set; SENPI_RPC_CLIENT_CAPABILITIES is only the initial value.
  • Withdrawing question settles each pending structured question once as question_resolved with outcome cancelled, matching shared hosts.
  • Added regression tests in test/suite/regressions/issue-4006-classic-set-client-info-capabilities.test.ts and documented the behavior in docs/rpc.md.