Wave D: native AgentEvent wire + client session rewrite (SDK purged)

Server: ui-stream.ts is verbatim pi AgentEvent passthrough (SSE data
lines + [DONE]); only app-level events are the loop-level error and
turn-settled terminal; onError carries raw provider text (owner ruling).

Client: useChat + the AI-SDK transport replaced by components/session-core.ts
(SessionModel: 409 identity-retry, optimistic c_ appends, absorbed()
adoption, needsContinuation, shared consume for POST + reattach) and the
session.ts hook; the golden-master grouping canon moved client-side into
core/view.ts (projectView); scanners read the native record (phaseAt3Lh,
danglingQuestionOf, graph toolParts, plan-blocks, runner, extraction).

Coverage: the native message is the unit (fork 3) with the call+result
inseparability bond rule; view-line-less messages (markers, error
results) are invisible to coverage and never checkpointed.

Deletes: chat-transport, projection, ui-message, their tests (ported to
tests/session-core.test.ts), and the ai + @ai-sdk/react dependencies —
the Vercel SDK is fully out of the tree. Wire pins in chat-flow rewritten
onto the AgentEvent dialect (eventsOf parser).

Gates: tsc clean, 994/994 tests, eslint clean, build green; live smoke
on PORT=3004 (verbatim wire → native record).
This commit is contained in:
amosblomqvist
2026-09-26 01:52:47 +03:00
parent 7c0e8d9a50
commit f3003d357a
44 changed files with 2299 additions and 2205 deletions
+5 -5
View File
@@ -10,11 +10,11 @@
*/
import assert from "node:assert";
import test from "node:test";
import type { UIMessage } from "ai";
import type { LhViewMessage } from "../src/core/view.ts";
import { phaseAt3, phaseTimeline, type Phase3 } from "../src/core/phases.ts";
/** Text-only user message. */
function user(text: string): UIMessage {
function user(text: string): LhViewMessage {
return { id: `u_${text}`, role: "user", parts: [{ type: "text", text }] };
}
@@ -23,7 +23,7 @@ function assistantTool(
name: string,
state: "input-available" | "output-available" | "output-error",
output?: unknown,
): UIMessage {
): LhViewMessage {
return {
id: `a_${name}_${state}_${Math.random().toString(36).slice(2, 6)}`,
role: "assistant",
@@ -37,7 +37,7 @@ const PLAN_OK = { ok: true, tree: { nodes: [{ id: "n1", title: "N" }], edges: []
/** The verdict button's user message (the custom-message scheme): its own
* message carrying ONLY the data-verdict marker part — no human-authored
* text; the model-facing wording is toLlmMessages' (native-messages.ts). */
function verdict(accepted: boolean): UIMessage {
function verdict(accepted: boolean): LhViewMessage {
return {
id: `u_verdict_${Math.random().toString(36).slice(2, 6)}`,
role: "user",
@@ -48,7 +48,7 @@ function verdict(accepted: boolean): UIMessage {
}
/** The accept button's message. */
function planAccepted(): UIMessage {
function planAccepted(): LhViewMessage {
return verdict(true);
}