chore(web): drop plan RPC residue and stale carriers after merge

Sweep the silently auto-merged leftovers of the superseded #587 wire layer:
the PlanModeState interface and planMode/setPlanMode entries in the sessions
API and rpc map, the fixture plan RPC spec, the composer.controls README
paragraph and spec assertions, the old-format plan-mode snapshot, and the
two Agent Notes written against the RPC design (to be rewritten for the
projection reattachment). ChatView/PendingCard keep the approval-only
pending rendering; QuestionComposer improvements ride along unchanged.
This commit is contained in:
imccyu
2026-07-28 20:28:43 +08:00
parent ae55771511
commit 2ecc89a186
14 changed files with 2 additions and 386 deletions

View File

@@ -48,59 +48,6 @@ describe('createFixtureApi', () => {
expect(response.result.value.items[1]?.parentSessionId).toBe('fx-alpha') // lineage material
})
it('queues fixture plan selections, commits them at prompt boundaries, and preserves pending state on cancel', async () => {
const client = new FixtureApiClient()
expect((await client.sessions.planMode({ sessionId: sid('fx-alpha') })).result).toEqual({
ok: true, value: { active: false },
})
expect((await client.sessions.setPlanMode({ sessionId: sid('fx-alpha'), active: true })).result).toEqual({
ok: true, value: { active: false, pending: true },
})
expect((await client.sessions.setPlanMode({ sessionId: sid('fx-alpha'), active: true })).result).toEqual({
ok: true, value: { active: false, pending: true },
})
expect((await client.sessions.cancel({ sessionId: sid('fx-alpha') })).result).toEqual({
ok: true, value: { accepted: true },
})
expect((await client.sessions.planMode({ sessionId: sid('fx-alpha') })).result).toEqual({
ok: true, value: { active: false, pending: true },
})
await client.sessions.prompt({
sessionId: sid('fx-alpha'), mode: 'queue', content: [{ type: 'text', text: 'commit plan' }],
})
expect((await client.sessions.planMode({ sessionId: sid('fx-alpha') })).result).toEqual({
ok: true, value: { active: true },
})
expect((await client.sessions.setPlanMode({ sessionId: sid('fx-alpha'), active: false })).result).toEqual({
ok: true, value: { active: true, pending: false },
})
expect((await client.sessions.planMode({ sessionId: sid('missing') })).result).toMatchObject({
ok: false, error: { code: 'session-not-found' },
})
expect((await client.sessions.setPlanMode({ sessionId: sid('missing'), active: true })).result).toMatchObject({
ok: false, error: { code: 'session-not-found' },
})
const created = await client.sessions.create({})
if (!created.result.ok) throw new Error('create failed')
const createdId = created.result.value.sessionId
await client.sessions.setPlanMode({ sessionId: createdId, active: true })
expect((await client.sessions.setPlanMode({ sessionId: createdId, active: false })).result).toEqual({
ok: true, value: { active: false },
})
// Selecting the committed target clears pending immediately, and repeating
// that selection remains a no-op without a redundant plan/mode event.
expect((await client.sessions.setPlanMode({ sessionId: createdId, active: false })).result).toEqual({
ok: true, value: { active: false },
})
await client.sessions.prompt({
sessionId: createdId, mode: 'queue', content: [{ type: 'text', text: 'remain default' }],
})
expect((await client.sessions.planMode({ sessionId: createdId })).result).toEqual({
ok: true, value: { active: false },
})
})
it('pages history backwards on message-boundary cuts with seq-contiguous stitching', async () => {
const api = createFixtureApi()
const tail = await api.sessions.history(req({ sessionId: sid('fx-alpha'), maxMessages: 10 }))

View File

@@ -18,10 +18,6 @@ Per-session UI state for selection and the active view lives in the declared cha
The composer bar declares session-scoped single seats for `'conversation.input.plan'` and `'conversation.input.model'`, plus list slots for overlay, dock, left, and right input extensions. InputBar renders the model seat immediately before its pending indicator and send/stop button. Feature packages own each control and its state; ui-conversation supplies placement, the `locked` owner prop, and the standard slot shares. The resident no-session shell uses `DisabledInputBar` and therefore dispatches no session-scoped control seats.
The default composer's bottom row exposes the session-scoped `'conversation.composer.controls'` list slot to the left of the primary action. Mode and policy features contribute controls through that slot; whole-composer takeovers such as questions remain selector-routed entries of the separate `'conversation.composer'` chain. Pending questions render only through that takeover and are omitted from chat-flow placeholders, while approvals remain visible until their own Web response surface exists.
Prompt submission has a short local admission phase distinct from model generation. The composer clears the draft, prevents a duplicate send, and waits while the session settles the latest mode selection and the Host accepts the prompt. Admission success releases that lock immediately; the independently streamed running state then keeps Stop available for the model turn. Admission failure restores the submitted draft only when the user has not supplied replacement text and surfaces through the ordinary prompt-error strip.
`src/client/` is organized for the future package split: `contract/` is the sole inter-domain shared face (`slots.ts` slot declarations + composed slot props including the tool-row contract, `views.ts` shared primitives, `tool-call-model.ts`); the `skeleton/`, `chat/`, and `toolviews/` (sample registrants) domain directories import contract files and never each other; `apply.ts` is the only assembly point allowed to import all three domains. The `/client` export surface is the contract only — `apply`/`inject`, the two service classes, and the `contract/` type families; implementation components (skeleton, chat rows) and the store factory stay internal and reach the page exclusively through apply's slot registrations (tests take them via the `./src/*` subpath).
## Model Experience

View File

@@ -91,7 +91,6 @@ describe('apply wiring', () => {
// Declaring is claiming: the chat entry's registration put the hole on
// the ledger with the contract's kind/scope.
expect(b.slots.spec('conversation.chat.toolview')).toEqual({ kind: 'keyed', scope: 'session' })
expect(b.slots.spec('conversation.composer.controls')).toEqual({ kind: 'list', scope: 'session' })
})
it('occupies the slots + the ring; session entries share one store handle', async () => {
@@ -133,7 +132,6 @@ describe('apply wiring', () => {
expect(b.slots.entries('conversation.view')).toHaveLength(0)
expect(b.slots.entries('conversation.chat.toolview')).toHaveLength(0)
expect(b.slots.spec('conversation.chat.toolview')).toBeUndefined()
expect(b.slots.spec('conversation.composer.controls')).toBeUndefined()
expect(b.slots.entries('details')).toHaveLength(0)
expect(b.ctx.get('conversation')).toBeUndefined()
})