Merge remote-tracking branch 'origin/master' into web2-todo

# Conflicts:
#	packages/client/runtime/README.md
This commit is contained in:
Chinesezjc
2026-07-26 17:04:11 +08:00
872 changed files with 23265 additions and 3051 deletions

View File

@@ -92,9 +92,18 @@ export class FakeApiClient implements IApiClient {
onWorkspaceCreate: (payload: unknown) => Promise<RpcResponse<{ workspace: WorkspaceView; created: boolean }>> =
() => Promise.resolve(ok({ workspace: fakeWorkspace('fk-ws'), created: true }))
onWorkspaceRename: (payload: unknown) => Promise<RpcResponse<{ workspace: WorkspaceView }>> =
() => Promise.resolve(ok({ workspace: fakeWorkspace('fk-ws') }))
onWorkspaceInsertSessionBefore: (payload: unknown) => Promise<RpcResponse<{ workspace: WorkspaceView }>> =
() => Promise.resolve(ok({ workspace: fakeWorkspace('fk-ws') }))
readonly workspace: IApiClient['workspace'] = {
list: (payload: unknown) => this.record('workspace.list', payload, this.onWorkspaceList(payload)),
create: (payload: unknown) => this.record('workspace.create', payload, this.onWorkspaceCreate(payload)),
rename: (payload: unknown) => this.record('workspace.rename', payload, this.onWorkspaceRename(payload)),
insertSessionBefore: (payload: unknown) =>
this.record('workspace.insertSessionBefore', payload, this.onWorkspaceInsertSessionBefore(payload)),
}
/** When true, streams never fire onOpen (misbehaving-carrier material for the handshake timeout guard). */

View File

@@ -60,6 +60,34 @@ describe('frontend Session and Workspace intents', () => {
expect(workspaces.list.getSnapshot().intent).toBeUndefined()
})
it('echoes updateIntent into the list snapshot in the same tick (controlled-input contract)', async () => {
const api = new FakeApiClient()
const { sessions, workspaces } = services(api)
await ready(api, workspaces, sessions, [workspace('target')])
let notified = 0
sessions.list.subscribe(() => { notified += 1 })
// IME composition drives change events that a controlled textarea must see
// reflected before the handler returns; a microtask-deferred echo makes
// React roll the DOM back and the composition commits partial keystrokes.
sessions.updateIntent('你')
expect(sessions.list.getSnapshot().intent?.prompt).toBe('你')
expect(notified).toBeGreaterThan(0)
})
it('ignores updateIntent with no active Intent', async () => {
const api = new FakeApiClient()
const { sessions, workspaces } = services(api)
await ready(api, workspaces, sessions, [workspace('only', [sid('s-real')])], [
{ sessionId: sid('s-real'), updatedAt: 1, running: false },
])
sessions.open(sid('s-real'))
expect(sessions.list.getSnapshot().intent).toBeUndefined()
let notified = 0
sessions.list.subscribe(() => { notified += 1 })
sessions.updateIntent('dropped')
expect(notified).toBe(0)
})
it('materializes zero-state Workspace and Session intents and retains a rejected first prompt', async () => {
const api = new FakeApiClient()
const { sessions, workspaces } = services(api)