fix(desktop-sdk): address adversarial review — type honesty, real tile coverage, docs
Independent second-pass review found three gaps: - focusedUsage is null | UsageStats, not Partial — ClientSessionState.usage is the full type (app/types.ts) and its only write site seeds the four required fields before merging (gateway-event.ts). The earlier Partial annotation traced the wrong type (SessionRuntimeInfo, an RPC payload). Comment now names the genuinely optional fields instead. - The tile-focus contract test never seeded $sessionTiles/$sessionStates, so it proved focusedStoredSessionId follows a tile but could not distinguish focusedSessionId/focusedUsage working from broken. Seed a bound runtime with distinct usage and assert both readout atoms move. - The two public host.state references (website docs + bundled skill reference) enumerated the old six atoms; plugin authors would never discover the new ones. Both lists updated. tsc/eslint/vitest green (4/4).
This commit is contained in:
committed by
Teknium
parent
a445a81279
commit
5fa092b481
@@ -59,7 +59,7 @@ describe('host.state focused-session atoms', () => {
|
||||
})
|
||||
|
||||
it('follows the interacted tile while the primary-only atom stays put', async () => {
|
||||
const { host, session } = await setup()
|
||||
const { host, session, states } = await setup()
|
||||
const tree = await import('@/components/pane-shell/tree/store')
|
||||
const model = await import('@/components/pane-shell/tree/model')
|
||||
const { registry } = await import('@/contrib/registry')
|
||||
@@ -85,9 +85,30 @@ describe('host.state focused-session atoms', () => {
|
||||
const primaryBefore = session.$activeSessionId.get()
|
||||
const primarySelection = session.$selectedStoredSessionId.get()
|
||||
|
||||
// Bind the tile to a live runtime with its own usage, so the readout
|
||||
// atoms (focusedSessionId / focusedUsage) — not just the navigation id —
|
||||
// are proven to follow the tile.
|
||||
const tileUsage = {
|
||||
calls: 3,
|
||||
input: 1200,
|
||||
output: 300,
|
||||
total: 1500,
|
||||
context_used: 42000,
|
||||
context_max: 200000,
|
||||
context_percent: 21,
|
||||
cost_usd: 0.0123
|
||||
}
|
||||
|
||||
states.$sessionTiles.set([{ storedSessionId: 'tile-a', runtimeId: 'runtime-tile-a' }])
|
||||
states.$sessionStates.set({
|
||||
'runtime-tile-a': { storedSessionId: 'tile-a', usage: tileUsage } as never
|
||||
})
|
||||
|
||||
// Focusing the tile zone moves the focused atoms onto the tile's session…
|
||||
tree.noteActiveTreeGroup('grp-side')
|
||||
expect(host.state.focusedStoredSessionId.get()).toBe('tile-a')
|
||||
expect(host.state.focusedSessionId.get()).toBe('runtime-tile-a')
|
||||
expect(host.state.focusedUsage.get()).toBe(tileUsage)
|
||||
// …while the primary-only atom a plugin used to rely on does not move.
|
||||
expect(host.state.activeSessionId.get()).toBe(primaryBefore)
|
||||
|
||||
|
||||
@@ -146,9 +146,9 @@ export const host = {
|
||||
/** Live usage snapshot of the focused session (`context_used` /
|
||||
* `context_max` / `context_percent`, token counts, `cost_usd`) —
|
||||
* streamed by the backend, no RPC needed. Null while unresolved.
|
||||
* Partial: the backend streams whichever fields changed, so every
|
||||
* field read needs a fallback. */
|
||||
focusedUsage: readonlyAtom<null | Partial<UsageStats>>($focusedUsage),
|
||||
* The UsageStats-optional fields (context_*, cost_usd) arrive as the
|
||||
* backend reports them, so read them with a fallback. */
|
||||
focusedUsage: readonlyAtom<null | UsageStats>($focusedUsage),
|
||||
/** Gateway socket state: 'idle' | 'connecting' | 'open' | …. Not turn-busy. */
|
||||
gateway: readonlyAtom<string>($gatewayState),
|
||||
/** Current main model slug. */
|
||||
|
||||
@@ -56,11 +56,16 @@ The ONLY import surface is `@hermes/plugin-sdk` (plus `react` /
|
||||
|
||||
- `host.state.*` — readonly reactive atoms: `activeSessionId`, `busy`,
|
||||
`awaitingResponse`, `busyBySession`, `cwd`, `gateway` (socket state, not
|
||||
turn-busy), `model`, `profile`, `viewport`.
|
||||
`busy` is true while the focused chat is working after a send (thinking
|
||||
and streaming). `awaitingResponse` is true until the first assistant
|
||||
payload. `busyBySession` maps runtime session id → mid-turn, for rosters
|
||||
that watch every session. Read with `.get()` in handlers,
|
||||
turn-busy), `model`, `profile`, `viewport`, plus the tile-aware focused
|
||||
session atoms: `focusedSessionId` (runtime id — key for `session.*` RPC),
|
||||
`focusedStoredSessionId` (durable id — navigation / list matching), and
|
||||
`focusedUsage` (live streamed `UsageStats` of the focused session, no RPC
|
||||
needed). `busy` is true while the focused chat is working after a send
|
||||
(thinking and streaming). `awaitingResponse` is true until the first
|
||||
assistant payload. `busyBySession` maps runtime session id → mid-turn,
|
||||
for rosters that watch every session. Prefer the focused atoms for any
|
||||
readout that should follow the user between tiles. Read with `.get()`
|
||||
in handlers,
|
||||
`useValue(atom)` in components.
|
||||
- `host.request(method, params)` — gateway JSON-RPC (sessions, config,
|
||||
skills, cron — everything the app uses).
|
||||
|
||||
@@ -378,6 +378,9 @@ host.state.activeSessionId // ReadableAtom<string | null>
|
||||
host.state.awaitingResponse // ReadableAtom<boolean> true until the first assistant payload
|
||||
host.state.busy // ReadableAtom<boolean> focused chat is working after a send
|
||||
host.state.busyBySession // ReadableAtom<Record<string, boolean>> runtime id → mid-turn
|
||||
host.state.focusedSessionId // ReadableAtom<string | null> (runtime id of the FOCUSED session — tile-aware; prefer for session.* RPC)
|
||||
host.state.focusedStoredSessionId // ReadableAtom<string | null> (durable id — navigation / session-list matching)
|
||||
host.state.focusedUsage // ReadableAtom<UsageStats | null> (live streamed usage of the focused session, no RPC needed)
|
||||
host.state.cwd // ReadableAtom<string>
|
||||
host.state.gateway // ReadableAtom<string> socket state ('idle' | 'connecting' | 'open' | …)
|
||||
host.state.model // ReadableAtom<string>
|
||||
|
||||
Reference in New Issue
Block a user