feat(frontend): surface agent web sur WebSocket (cellule agent) (#13)

Lot F4 du chantier server/client mode : le client web expose une cellule
agent branchée sur le PTY WebSocket, en face de agent.launch (B6).

- wsLiveClient.ts : launchAgent sur le transport WebSocket.
- streamGateways.ts : gateway agent alignée sur le contrat serveur B6.
- WebAgentCell.tsx : cellule agent web, câblée dans WebWorkspace et index.
- Tests : agentGateway.test.ts, WebApp.test.tsx.

Validé : frontend 749 tests verts, contrat B6↔F4 aligné (aucun écart de
frame), desktop non régressé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 23:17:24 +02:00
parent 6391d1c75a
commit 5254f16095
7 changed files with 359 additions and 32 deletions

View File

@ -105,6 +105,21 @@ describe("WebApp pairing routing", () => {
expect(snapshot.textContent).toContain("idle");
});
it("opens a live agent cell for a work-state agent (F4 affordance)", async () => {
const session = new WebSession({ baseUrl: "https://h", fetchImpl: okFetch, store: memStore() });
session.markPaired();
renderWebApp(session, await seededGateways());
fireEvent.click(await screen.findByText("Demo"));
await screen.findByTestId("web-workstate");
// The per-agent "Ouvrir" affordance mounts the reusable TerminalView cell,
// wired to the DI agent gateway (the CLI runs server-side).
expect(screen.queryByTestId("web-agent-cell")).toBeNull();
fireEvent.click(screen.getByRole("button", { name: "Ouvrir" }));
expect(await screen.findByTestId("web-agent-cell")).toBeTruthy();
});
it("returns to pairing when the session reports unauthorized (401)", async () => {
const session = new WebSession({ baseUrl: "https://h", fetchImpl: okFetch, store: memStore() });
session.markPaired();