You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make each ModelAdapter stream call represent exactly one provider step. Maka, not the AI SDK, should own the agent loop, tool execution, durable settlement, steering, compaction, and continuation.
This is a boundary correction, not a provider rewrite. Maka should continue using ai and @ai-sdk/* for request lowering, HTTP transport, SSE parsing, and provider-specific response normalization.
Current ownership
ModelAdapter.startStream() currently calls AI SDK streamText() with:
executable tools;
stopWhen for a bounded or unbounded multi-step loop;
prepareStep for steering, compaction, active-tool changes, and message shaping.
The SDK therefore owns the loop between provider requests and invokes Maka's wrapped tool executors from inside that loop. Maka has had to adapt its durable runtime semantics around SDK-local step and attempt state. Examples include:
attemptStepBase rebasing after a retry;
remaining-step-budget forwarding across startStream() retries;
steering injection through prepareStep;
guarding against transport retry after a tool may have changed external state;
accepting multiple SDK chunk names across migration windows.
The tool implementation itself already belongs to ToolRuntime. The current extra ownership in streamText() does not provide a capability that Maka lacks. It hides provider-step boundaries that Maka needs for persistence and recovery.
Required boundary
ModelAdapter should expose one Maka-owned operation with this meaning:
one request projection in
one provider step stream out
no local tool execution
no automatic continuation
The exact exported names can follow the existing code, but the ownership must be visible in the type. Step budgets, prepareStep, and stopAfterStep do not belong on the adapter call.
AI SDK types must stay behind ModelAdapter. Runtime code consumes Maka-owned messages, stream events, usage, finish reasons, request metadata, and typed failures.
Execution flow
Runtime projects durable history and current input
|
v
apply steering / context shaping / active-tool policy
|
v
ModelAdapter sends one provider request
|
v
Runtime persists streamed assistant facts
|
v
Runtime settles returned tool calls through ToolRuntime
|
v
reload durable projected history
|
+--> continue with another ModelAdapter call when required
|
+--> stop when the model produced a terminal assistant response
Tools passed to the SDK are schemas only. They do not carry SDK-owned execute callbacks. Every returned tool call crosses the existing ToolRuntime permission, admission, journal, artifact, and output boundaries before continuation.
The context-shaping code currently driven by prepareStep becomes ordinary runtime logic between adapter calls. This change should preserve its existing behavior before any policy is redesigned.
Retry and interruption semantics
The adapter must not hide a multi-step retry loop.
A provider retry is a new explicit adapter call owned by the Runtime. Main-agent adapter calls set maxRetries: 0 so the AI SDK never hides an additional attempt.
No retry may repeat a provider step after a tool call could have produced an external side effect.
Abort stops the current provider request. It does not discard already durable assistant or tool facts.
A provider response that contains tool calls ends the adapter call. Tool settlement and the decision to continue happen outside the adapter.
A durably recorded tool call without a matching tool result is indeterminate after interruption. Recovery blocks and never automatically re-executes it.
Auxiliary calls such as compaction may continue using their own narrow one-request methods. They do not share agent-loop state.
ModelAdapter does not distinguish a hidden “pre-output” retry: once dispatch is attempted, retry ownership stays with the Runtime because absence of visible output does not prove that the provider neither accepted nor billed the request.
Migration plan
Define the one-step ModelAdapter contract and characterize current streaming output with tests.
Run streamText() in one-step, zero-retry mode and remove SDK-owned tool execution.
Move the loop into the existing runtime/backend path without introducing a second loop implementation.
Move each prepareStep responsibility to an explicit between-step stage.
Preserve provider request capture, signed thinking, usage normalization, tool-call repair, abort behavior, and context-overflow handling.
Remove the old loop controls and the compatibility code that no longer has an owner.
Each slice must leave one production path. Do not keep the SDK-owned loop as a fallback.
Delivery slices
Land the change as three sequential flat PRs. Each PR starts from the latest main after the previous PR merges; do not stack them.
1. Contain the AI SDK protocol in ModelAdapter
Scope
Define Maka-owned model messages, tool definitions, stream events, completion metadata, usage, finish reasons, request metadata, and typed failures.
Keep raw AI SDK chunks, provider metadata, retry wrappers, and provider-specific normalization inside ModelAdapter.
Move runtime, history projection, and compaction consumers away from direct ai type imports.
Characterize text, signed thinking, tool calls, usage, finish reasons, errors, request capture, and provider-matrix behavior before changing loop ownership.
Exit condition
No AI SDK type crosses the adapter boundary.
The existing SDK-owned multi-step loop and executable-tool behavior remain unchanged for this slice, with exactly one production path.
This slice does not move tool execution or continuation ownership.
2. Expose direct ToolRuntime settlement
Scope
Replace the SDK-shaped wrapToolExecute() entry with one direct ToolRuntime operation that accepts a resolved Maka tool call and performs admission, permission, durable preparation, execution, outcome commit, artifact recording, telemetry, and model-output materialization.
Make the current AI SDK execute callback a thin caller of that operation; it must not own policy or state.
Preserve synchronous per-step admission, current multi-tool concurrency, permission waits, durable identities, recovery modes, plan handoff behavior, and tool-result error semantics.
Move ToolRuntime tests to the direct entry and retain a contract test that prevents the backend from reimplementing settlement.
Exit condition
Tool execution and settlement have one implementation owner in ToolRuntime.
The SDK still triggers the call in this slice, but the SDK-shaped wrapper no longer owns or duplicates behavior.
Agent-loop ownership remains unchanged.
3. Move the agent loop above ModelAdapter
Scope
Switch ModelAdapter to one provider step per call, pass schema-only tools, and set maxRetries: 0 for main-agent calls.
Implement the only agent loop in the existing AiSdkBackend.send() path: load the durable projection, project the request, call the adapter, persist assistant and tool-call facts, settle all returned tool calls through ToolRuntime, reload durable history, and decide whether to continue.
Preserve current multi-tool concurrency and wait for every tool outcome to become durable before the next provider request.
Give both parent and child runs a durable current-run reader. Represent mid-turn compaction eligibility as a separate capability instead of inferring it from reader availability.
Move steering, active-tool selection, pruning, compaction, final capacity checks, provider request capture, step limits, explicit network retry, overflow recovery, abort, and after-step stop to explicit Runtime stages.
Remove prepareStep, executable SDK tools, SDK loop stopWhen, attemptStepBase, remaining-budget forwarding, stopAfterStep, and compatibility code that no longer has an owner.
Validate the two replay-equivalence fixtures plus focused permission, steering, compaction, retry, abort, and interrupted-execution tests.
Exit condition
All acceptance criteria below pass on the single Runtime-owned production path.
Reverting this PR restores the pre-switch loop while leaving the independently useful adapter and ToolRuntime seams intact.
Acceptance criteria
One ModelAdapter call completes after one provider step, including a response that requests tools.
The SDK never invokes a Maka tool implementation.
Tool calls execute once through ToolRuntime, retain their durable identities, and settle before continuation.
Projected history is reloaded before every continuation request.
Steering, deferred-tool activation, active pruning, compaction, permission, abort, context overflow, and transport failure retain their current externally visible behavior.
Provider request capture and normalized usage remain complete for each step.
No AI SDK type escapes the adapter boundary.
Tests cover text-only completion, one and multiple tool calls, tool failure, permission wait, steering, compaction, retry, abort, and interrupted execution.
A dangling durable tool call remains indeterminate on recovery and is never automatically replayed.
Explicitly not included
replacing AI SDK provider integrations;
implementing Anthropic or OpenAI wire protocols;
provider-native Tool Search;
Programmatic Tool Calling or a local code mode;
redesigning compaction, permissions, steering, or retry policy;
These do not block #1381 and are not part of delivery slice 1. Audit them one at a time after the main-agent model protocol is contained; do not turn the audit into a blanket removal of the ai dependency.
Should the history-compaction summarizer accept a Maka-owned request shape while keeping its narrow generateText() integration local?
Should session-title generation expose a Maka-owned model-call contract instead of an AI SDK LanguageModel type?
Should the auto-approval reviewer isolate its narrow model call behind an explicit adapter-local contract?
Are jsonSchema / zodSchema imports in builtin and MCP tool construction only local schema implementation details, or does any AI SDK type cross their ownership boundary?
Only open follow-up work where a concrete ownership or type leak exists. Schema helpers that remain local implementation details need no refactor.
Resolved decisions
All main-agent provider-call retries are explicit Runtime attempts. The adapter sets maxRetries: 0; auxiliary calls keep narrow independent retry policies.
Steering, active-tool selection, pruning, and pre-request compaction run during explicit request projection. Decisions that depend on tool outcomes run only after durable tool settlement. The final capacity verdict and provider request capture sit immediately around the adapter call.
Replay equivalence uses two minimal fixtures: a text-only terminal step with signed thinking and usage; and a multi-tool step covering success and failure, durable settlement, history reload, and terminal continuation. Permission wait, steering, compaction, retry, abort, and interruption remain focused tests rather than one monolithic fixture.
Dangling tool calls block recovery as indeterminate and are never automatically replayed.
中文版本
概述
把 ModelAdapter 的基本单位收窄成一次 provider step。Agent loop、工具执行、持久化结算、steering、compaction 和 continuation 都由 Maka 自己负责,不再交给 AI SDK 的 streamText() 内部循环。
Summary
Make each
ModelAdapterstream call represent exactly one provider step. Maka, not the AI SDK, should own the agent loop, tool execution, durable settlement, steering, compaction, and continuation.This is a boundary correction, not a provider rewrite. Maka should continue using
aiand@ai-sdk/*for request lowering, HTTP transport, SSE parsing, and provider-specific response normalization.Current ownership
ModelAdapter.startStream()currently calls AI SDKstreamText()with:stopWhenfor a bounded or unbounded multi-step loop;prepareStepfor steering, compaction, active-tool changes, and message shaping.The SDK therefore owns the loop between provider requests and invokes Maka's wrapped tool executors from inside that loop. Maka has had to adapt its durable runtime semantics around SDK-local step and attempt state. Examples include:
attemptStepBaserebasing after a retry;startStream()retries;prepareStep;The tool implementation itself already belongs to
ToolRuntime. The current extra ownership instreamText()does not provide a capability that Maka lacks. It hides provider-step boundaries that Maka needs for persistence and recovery.Required boundary
ModelAdaptershould expose one Maka-owned operation with this meaning:An illustrative shape:
The exact exported names can follow the existing code, but the ownership must be visible in the type. Step budgets,
prepareStep, andstopAfterStepdo not belong on the adapter call.AI SDK types must stay behind
ModelAdapter. Runtime code consumes Maka-owned messages, stream events, usage, finish reasons, request metadata, and typed failures.Execution flow
Tools passed to the SDK are schemas only. They do not carry SDK-owned
executecallbacks. Every returned tool call crosses the existingToolRuntimepermission, admission, journal, artifact, and output boundaries before continuation.The context-shaping code currently driven by
prepareStepbecomes ordinary runtime logic between adapter calls. This change should preserve its existing behavior before any policy is redesigned.Retry and interruption semantics
maxRetries: 0so the AI SDK never hides an additional attempt.ModelAdapterdoes not distinguish a hidden “pre-output” retry: once dispatch is attempted, retry ownership stays with the Runtime because absence of visible output does not prove that the provider neither accepted nor billed the request.Migration plan
ModelAdaptercontract and characterize current streaming output with tests.streamText()in one-step, zero-retry mode and remove SDK-owned tool execution.prepareStepresponsibility to an explicit between-step stage.Each slice must leave one production path. Do not keep the SDK-owned loop as a fallback.
Delivery slices
Land the change as three sequential flat PRs. Each PR starts from the latest
mainafter the previous PR merges; do not stack them.1. Contain the AI SDK protocol in
ModelAdapterScope
ModelAdapter.aitype imports.Exit condition
2. Expose direct
ToolRuntimesettlementScope
wrapToolExecute()entry with one direct ToolRuntime operation that accepts a resolved Maka tool call and performs admission, permission, durable preparation, execution, outcome commit, artifact recording, telemetry, and model-output materialization.executecallback a thin caller of that operation; it must not own policy or state.Exit condition
ToolRuntime.3. Move the agent loop above
ModelAdapterScope
ModelAdapterto one provider step per call, pass schema-only tools, and setmaxRetries: 0for main-agent calls.AiSdkBackend.send()path: load the durable projection, project the request, call the adapter, persist assistant and tool-call facts, settle all returned tool calls throughToolRuntime, reload durable history, and decide whether to continue.prepareStep, executable SDK tools, SDK loopstopWhen,attemptStepBase, remaining-budget forwarding,stopAfterStep, and compatibility code that no longer has an owner.Exit condition
Acceptance criteria
ModelAdaptercall completes after one provider step, including a response that requests tools.ToolRuntime, retain their durable identities, and settle before continuation.Explicitly not included
Follow-up questions
These do not block #1381 and are not part of delivery slice 1. Audit them one at a time after the main-agent model protocol is contained; do not turn the audit into a blanket removal of the
aidependency.generateText()integration local?LanguageModeltype?jsonSchema/zodSchemaimports in builtin and MCP tool construction only local schema implementation details, or does any AI SDK type cross their ownership boundary?Only open follow-up work where a concrete ownership or type leak exists. Schema helpers that remain local implementation details need no refactor.
Resolved decisions
maxRetries: 0; auxiliary calls keep narrow independent retry policies.中文版本
概述
把
ModelAdapter的基本单位收窄成一次 provider step。Agent loop、工具执行、持久化结算、steering、compaction 和 continuation 都由 Maka 自己负责,不再交给 AI SDK 的streamText()内部循环。这不是重写 provider。
ai和@ai-sdk/*继续负责请求转换、HTTP、SSE 解析和各家响应格式适配。现有职责为什么不对
当前
ModelAdapter.startStream()会把下面这些一起交给streamText():execute的工具;stopWhen;prepareStep。因此,两次 provider 请求之间何时执行工具、何时继续,实际由 SDK 控制。Maka 为了把 durable runtime 套在外面,已经出现不少补丁式状态:
attemptStepBase;prepareStep注入 steering;工具执行本身早已归
ToolRuntime。SDK 内部 loop 没有补上 Maka 缺失的能力,反而把 Maka 需要持久化和恢复的 provider-step 边界藏起来了。目标边界
ModelAdapter只保留一个清楚的含义:示意接口:
最终命名可以就近改现有接口,但 ownership 必须体现在类型里。step budget、
prepareStep、stopAfterStep不再属于 adapter 参数。AI SDK 类型不能穿过
ModelAdapter。Runtime 只看到 Maka 自己定义的 message、stream event、usage、finish reason、request metadata 和 typed failure。执行流程
传给 SDK 的工具只有 schema,不再带由 SDK 调用的
execute。模型返回的每个 tool call 必须经过现有 ToolRuntime 的 permission、admission、journal、artifact 和 output 边界,结算完成后才能继续。今天挂在
prepareStep上的 context shaping 会变成两次 adapter 调用之间的普通 runtime 逻辑。第一阶段只搬 ownership,不顺手重写策略。Retry 与中断
maxRetries: 0,AI SDK 不得隐藏额外 attempt;ModelAdapter不区分隐藏的“尚无可见输出”重试:一旦尝试 dispatch,retry ownership 就留在 Runtime,因为没有可见输出不能证明 provider 没有接收请求或计费。迁移步骤
ModelAdapter契约,用测试锁住当前 streaming 输出。streamText()只执行一步、关闭内部重试,并移除 SDK-owned tool execution。prepareStep的每项职责逐个移到显式的 between-step 阶段。每个 slice 都必须只留下一个生产路径,不保留 SDK loop fallback。
交付切片
按三个依次合入的 flat PR 交付。前一个 PR 合并后,下一个 PR 从最新
main开始,不做 stacked PR。1. 把 AI SDK 协议收进
ModelAdapter范围
ModelAdapter内。ai类型。完成出口
2. 暴露直接的
ToolRuntimesettlement范围
wrapToolExecute();admission、permission、durable preparation、execution、outcome commit、artifact、telemetry 和 model-output materialization 都由它完成。executecallback 只薄调用这个操作,不拥有 policy 或 state。完成出口
ToolRuntime一个实现 owner。3. 把 agent loop 移到
ModelAdapter之上范围
ModelAdapter每次调用只完成一个 provider step,工具只传 schema,主 agent 调用固定maxRetries: 0。AiSdkBackend.send()路径实现唯一 agent loop:读取 durable projection、投影请求、调用 adapter、持久化 assistant 与 tool-call facts、通过ToolRuntime结算全部工具、重新读取 durable history,再决定 continuation。prepareStep、executable SDK tools、SDK loopstopWhen、attemptStepBase、remaining-budget forwarding、stopAfterStep和失去 owner 的兼容代码。完成出口
验收条件
ModelAdapter调用只完成一个 provider step;模型请求工具时,本次调用也必须结束。ToolRuntime执行一次,保留 durable identity,并在 continuation 前完成结算。明确不做
Follow-up 问题
这些问题不阻塞 #1381,也不属于交付切片 1。主 agent model protocol 收口后再逐项审计,不把审计扩大成清除全部
ai依赖。generateText()integration 保留在本地?LanguageModel类型?jsonSchema/zodSchemaimport 是否只是本地 schema 实现细节,还是有 AI SDK 类型越过了其 ownership boundary?只有发现具体 ownership 或 type leak 时才开启 follow-up;保持局部实现细节的 schema helper 不需要重构。
已定决策
maxRetries: 0;compaction 等辅助调用保留各自独立的窄 retry policy。