Post-review iteration (29 Jun 2026). This drew detailed feedback — fluent builder, declarative registry, deny-by-default VFS, model-from-config, layout. It's folded into a proposal: defineAgent, reshaped (current vs proposed, side by side).
Update · AI SDK v7 is the base (GA 2026-06-25). This first pass was built on v6;
the base is now v7 (a breaking major). The framework shape is unchanged, but the implementation migrates:
mostly mechanical renames (system→instructions,
stepCountIs→isStepCount, experimental_output→output,
provider-registry types) via npx @ai-sdk/codemod v7, and the durable-runs and HITL primitives we
deferred now ship in-SDK. See the migration doc
(v7 update) for the base decision and the build-time gates.
3
phases, dependency-ordered
2
small agents, both transports
v6
AI SDK, pinned latest first
What we're building
The agent loop runs in Python today, and lawrence-api only proxies its stream. This first pass
moves just enough of the runtime to TypeScript to prove the shape: a defineAgent({ ... }) surface,
a slug registry, Langfuse prompt resolution, an initialContext hook for server-side context
assembly, structured output, and two transports off one runtime: single-shot run (JSON) and
streaming chat (SSE). The VFS gets a thin slice that reuses the existing matter-scoped adapters through a
generic-verb layer.
It is deliberately small. Two real-but-bounded agents validate the framework and the defineAgent
ergonomics; the harder capabilities (the matter-intake agent, non-matter scope, the full VFS catalogue) land
later on validated foundations rather than co-evolving with an unproven framework.
Prove the spine with two small agents, then the rest of “many Lawrences” builds on
something that already runs.
The two agents
One per transport. Both matter-scoped, so this pass needs no new scope machinery: the existing
matter auth chain and the existing matter-scoped VFS adapters carry the weight.
prompt-suggestions run · single-shot
Suggests prompts the lawyer could send Lawrence, from the matter's case context. Proves the
spine: registry, prompt resolution, the initialContext hook, and structured output, with no
tools and no streaming.
transportrun → JSON
contractstructured (generateObject)
contextinitialContext → getCaseContext
tools / vfsnone
lawrence-mini stream · chat
A stripped-down matter-scoped Lawrence. Proves the streaming transport, the tool loop, and
the VFS-as-tools surface. No citations, no full tool catalogue, stateless in this pass.
transportstream → SSE
contracttext (streamText)
vfs slicedocuments, files · read, list
loop boundstopWhen: stepCountIs(n)
The build, in three phases
Eleven units, dependency-ordered. Phase A is the spine, Phase B ships the first working
agent, Phase C adds streaming, the VFS slice, and the second agent.
phaseAfoundation
The framework spine
Everything the runtime needs before an agent can run, with no behaviour shipped to users.
U1Pin AI SDK to latest 6.0.x. Isolated bump off the lockfile-pinned 6.0.132 so framework code is written against current, not deprecated, APIs.
U2Provider registry. Resolve a model id to a direct lab provider; never the default Vercel gateway.
U3defineAgent + registry + types. The authoring surface and the slug lookup.
U4Langfuse prompt resolution. Fetch by name + label, compile variables, model can come from prompt config.
U5Context + initialContext hook. Resolve scope from auth, run the hook into render variables.
phaseBfirst agent
Single-shot, end to end
The run transport plus the first agent. After this phase a working agent exists.
U6Runtime run path + route. POST /agents/:slug/run, structured output via generateObject, built so an agentic-then-structured agent reuses it later via Output.
U7prompt-suggestions agent. CCO via initialContext reusing getCaseContext; structured suggestions.
phaseCstream + vfs
Streaming, the VFS slice, the second agent
The streaming transport, the generic-verb VFS layer, and the matter-scoped chat agent that uses both.
U8Runtime stream path + route. streamText + stopWhen, reusing the house stream-adapter; Anthropic cache discipline.
U9VFS thin slice. VfsConfig-gated read/list tools dispatching to existing matter-scoped adapters via the vfs-registry.
U10lawrence-mini agent. Streaming, matter-scoped, the thin VFS slice over documents and files.
U11Feature-flag gate. Server-side PostHog check on the endpoints, fail-closed; the legal-os UI flag reserved for later.
Phase A + B alone ships a working agent — the streaming surface is additive.
The decisions that shape it
The load-bearing calls, each settled with the team before the plan landed.
| Decision | Why |
Compose streamText / generateObject, not ToolLoopAgent | The loop already lives in streamText; v6's ToolLoopAgent wraps it without adding anything we need. Composing the core functions keeps control over the two transports, structured output, later MCP injection, and Langfuse spans. |
| Direct lab providers, never the gateway | A bare model string routes through the Vercel AI Gateway (US, Vercel-credentialed) by default. Resolution is a framework-internal seam: agent authors give a model id, the resolver maps it to a direct provider now and to a self-hosted LiteLLM-style proxy later, with no change to any agent definition. |
| Transport and contract are independent axes | Transport (run / stream), contract (text / object) and tools are orthogonal. Structured-without-tools uses generateObject; structured-with-tools (agentic context building, then a typed object) uses generateText/streamText + Output. Pass 1 exercises two combinations; matter-intake (agentic, then structured) drops in later without reworking the loop. |
| VFS slice reuses the matter-scoped adapters | The framework adds the VfsConfig + generic-verb tool surface and dispatches in-process to the existing vfs-registry adapters by matterId. No change to the matter-bound @lawhive/content-framework contract; the non-matter scope-climb is a separate follow-up. |
initialContext is an explicit seam | Python has no such hook (context assembly is inline in a 1600-line method). The TS hook returns an assembled context object; a render step maps it into Langfuse variables. The CCO fetch reuses the existing getCaseContext service, not a reimplementation. |
| Bump the AI SDK first, isolated and pinned | The bare caret plus a frozen dist-tag is why the lockfile sits at 6.0.132 (latest is 6.0.208). Bumping first lets the framework be written against the current streaming API, not the already-deprecated names. |
What this pass does not do
Deliberately deferred, so the spine lands clean.
non-matter scope & the scope-climb
matter-intake / matter-creation
full VFS namespace catalogue
presets / builder
create / edit verbs
citation processing
thread persistence
surface-based tool filtering
Python agent-gateway cutover
durable execution + durable streams
code execution (AgentCore)
token-budget loop control
Coexistence
The framework and agent definitions ship to production inert: registered, but nothing invokes them until
deliberately wired, and only agent-invoking UI is feature-flagged. The Python agent-gateway and the existing
/lawrence/chat route are untouched.
Top risks
| Risk | Severity | Mitigation |
| AI SDK streaming surface mid-rename | Medium | U1 bumps first; U8 builds against the current names (.stream, createUIMessageStreamResponse). |
ai bump blast radius | Medium | Other workspaces import ai (legal-os). U1 is isolated and gated on repo-wide pnpm check. |
| Anthropic cost without cache discipline | High | Keep per-turn variables out of the system prompt; reproduce cache breakpoints on the streaming agent. Verify cost against the Anthropic console, not Langfuse. |
ANTHROPIC_API_KEY env wiring | Medium | Doppler + Parameter Store + Flightcontrol; prod containers only get vars referenced in Flightcontrol config. |
Route collision on /agents/chat | Medium | The legacy proxy already owns that path. Namespace the new routes (/agents/:slug/run, /agents/:slug/chat). |
One line: build a thin defineAgent framework in lawrence-api on
the AI SDK v6 (compose the primitives, not ToolLoopAgent; direct lab providers, not
the Vercel gateway), prove it with two small matter-scoped agents across three phases, reuse the existing
matter-scoped VFS adapters, and defer the non-matter scope-climb. Phase A + B ships a working
agent; the framework sits inert until wired.