@runtypelabs/sdk 9.2.1 → 9.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,3441 @@
1
+ # @runtypelabs/sdk
2
+
3
+ ## 9.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 95e48e6: Address the /review findings on the legacy agent-loop retirement (#7824). No
8
+ behavior changes beyond three consolidated error bodies and the deletion of code
9
+ the retirement left unreachable; the rest is documentation and message text that
10
+ still described the deleted engine as a live destination.
11
+ - **One parity-fold refusal for three hosts.** `/v1/dispatch`,
12
+ `/v1/agents/:id/execute` and the Product API answered the same defect with
13
+ three different bodies, one of which told the author to "dispatch it via POST
14
+ /v1/dispatch (which routes folded definitions to the legacy engine)" — a dead
15
+ end now that lane returns a 400 of its own. All three share
16
+ `parityFoldRefusal()`, so the response is `400
17
+ RUNTIME_AGENT_PARITY_FOLD_UNSUPPORTED` with the fold classes as evidence.
18
+ - **Remediation text no longer prescribes the retired opt-out.** The agent-save
19
+ advisory, the admission reason strings, the runtime-package gate messages, the
20
+ `dispatch` request-schema descriptions (public spec + SDK cores + MCP tool
21
+ inputs), the client SDK JSDoc, two knowledge fragments and two Fern docs pages
22
+ now describe the terminal answer and append `LEGACY_LANE_NO_BRIDGE_REMEDY`.
23
+ - **Internal census truthfulness.** Three log lines said "internal execution runs
24
+ on the legacy engine" while the census already reported `refused`; they now
25
+ agree.
26
+ - **Deletion residue removed:** the unsatisfiable local-inference guard in
27
+ `/v1/dispatch` and its `LOCAL_INFERENCE_UNAVAILABLE` export,
28
+ `lib/virtual-agent-summary.ts`, and five knip-verified dead modules
29
+ (`scripts/test-loop-standalone.ts`, which imported the deleted agent executor
30
+ and was therefore broken, `services/webhook-doc-crawler.ts`,
31
+ `lib/agent-memory/cf-types-compat.ts`, `lib/constants/record-limits.ts`,
32
+ `lib/email/index.ts`).
33
+
34
+ ## 9.3.0
35
+
36
+ ### Minor Changes
37
+
38
+ - 6bfcfa0: Durable-by-default agent turns (ADR 0020): the `runtype_managed` agentType is deleted and durability becomes the default property of every agent turn, decided once at admission by the pure `turn-durability-policy` (outs: parity fold, Flagship `enable-durable-agent-turns` rule, per-agent `config.durability.forced` pin). The session DO adopts the `SessionEventStore` ledger with post-engine filters before journaling and single relay-owned SSE `id:` lines, gains a bounded recovery engine (incident record, budgets, sealed terminal reasons, recover-on-any-wake, sub-turn continuation with orphaned-tool repair), a 7-day parked-turn TTL with a resume-vs-expiry CAS, and two-phase fenced claim/start concurrency (`reject` / `supersede` / `queue` + `coalesce`, admission `idempotencyKey`) with every rejectable check ahead of the daily-quota reservation. Harness knobs move to `config.durability { watchLeaseMs, maxBudgetMs, forced }`.
39
+
40
+ ### Patch Changes
41
+
42
+ - 20f2a28: Deprecate the `options.useRuntimePackage: false` legacy execution-lane opt-out on `/v1/dispatch`. The legacy agent engine is being retired: an agent dispatch that still sends `false` runs on it for one more release and its response now carries RFC 9745 `Deprecation` and `X-API-Deprecation-Warning` headers; a later release rejects `false`. The value is deprecated, not the field: omitting it selects the runtime lane, and `true` remains the strict opt-in that fails loudly instead of folding to legacy. The OpenAPI spec, SDK types and MCP `dispatch` tool input describe the new contract, every API remediation that still names the opt-out calls it a deprecated bridge, and the dashboard's agent test chat no longer sends `false` (it was forcing every dashboard agent test onto the legacy engine).
43
+ - 9ac677d: Carry the Anthropic adaptive-thinking effort and the Gemini thinking level all the way to the model request.
44
+
45
+ `buildReasoningOptions` mapped a flat `ReasoningConfig` onto `anthropic: { budgetTokens }` and `google: { thinkingBudget, includeThoughts }` only, while `buildReasoningProviderOptions` reads `effort` in the Anthropic adaptive branch and `thinkingLevel` in the Google branch. On Claude 4.6 and later, including the Claude 5 family that left the model with no working reasoning dial at all: adaptive thinking discards `budgetTokens`, and the effort it does accept was never mapped. Gemini 3 level mode had the same shape.
46
+
47
+ `ReasoningConfig` gains `effort` and `thinkingLevel` (plain strings, because the accepted rungs are per-model catalog data that already exceeds the fixed `reasoningEffort` union), the agent config and FPO agent schemas accept them, the execution contract and the model-gateway wire schema carry them, and both prompt executors map them into their own provider bucket. Neither is cross-filled from `reasoningEffort`, which is the OpenAI ladder. The api and runtime mappings move together and are pinned by three new cases in the shared prompt-executor parity corpus.
48
+
49
+ - 9ac677d: Expose per-model reasoning knobs on the model-configuration surfaces, and warn at save time when a reasoning config asks for a knob the model does not accept.
50
+
51
+ `GET /v1/model-configs` and `GET /v1/model-configs/grouped` now carry the catalog-sourced `reasoningCapability.options` (`toggle` / `effort` ladder / `budgetTokens` range), `anthropicThinkingMode` and `builtInReasoning`, so a client can render the dials a model actually has instead of a per-provider guess. The committed OpenAPI spec and the generated SDK types are regenerated for the new fields.
52
+
53
+ The model-advisory pass gains one non-blocking code, `REASONING_KNOB_UNSUPPORTED` (warning, never error): an effort level outside the model's published ladder, a `budgetTokens` on an Anthropic model the executor drives in adaptive thinking mode (which carries no token budget and drops the value before the request), or a thinking budget outside the model's published range. The effort check reads only the carrier the executor actually reads for that provider, so a level stranded on a field the request never carries (a Gemini row that stored its thinking level under the OpenAI effort field) is no longer reported as unsupported. It stays silent whenever the catalog makes no claim about the model, on the boolean `reasoning: true` form, on an explicit `enabled: false`, and on disabled steps.
54
+
55
+ - e1111dc: Retire dispatch-transient `secrets` on agent dispatches.
56
+
57
+ An agent dispatch that carries a NON-EMPTY request-scoped `secrets` map is now
58
+ answered with a terminal 400 `RUNTIME_AGENT_TRANSIENT_SECRETS_UNSUPPORTED`.
59
+ Previously only an explicit `options.useRuntimePackage: true` was refused; a
60
+ caller who never set the option was folded onto the legacy agent engine so the
61
+ field could keep working. An empty map is still accepted and does nothing, and
62
+ the deprecated `options.useRuntimePackage: false` opt-out still routes the run
63
+ to the legacy engine, which honors the map for single-turn agents until that
64
+ engine is removed. Dispatch-transient secrets are retired
65
+ platform-wide (D-080, amended 2026-08-27), the census counted zero occurrences
66
+ in 30 days on both the fold line and the refusal line, and the fold was routing
67
+ callers onto an engine being deleted to honor a contract being deleted. Store
68
+ the value as a managed secret and reference it as `{{secret:NAME}}`.
69
+
70
+ The remediation is terminal: it no longer prescribes the deprecated
71
+ `options.useRuntimePackage: false` bridge. The wire field stays on the dispatch
72
+ schema for compatibility, with its description (and the regenerated OpenAPI
73
+ spec, SDK types and MCP `dispatch` input schema) rewritten to say retired. Flow
74
+ dispatches are unchanged: they have always accepted and ignored the field.
75
+
76
+ - 6bfcfa0: Add the `runtype_managed` agent execution mode (slice 0: mode plumbing). A new `agentType` value classifies agents onto a durable self-hosted session harness (staging-only, flag-gated via the env-pinned `enable-runtype-managed` Flagship gate plus the fail-closed `runtype-managed-agents` Schematic flag). This slice lands the classification, the `agents.runtype_managed_config` column and route schemas, and loud rejections at every execution surface so the mode can never silently degrade into the in-request loop; the session DO harness and entry-surface wiring land in follow-up slices. Design: `docs/features/planning/2026-07-31-runtype-managed-harness.md`.
77
+
78
+ ## 9.2.1
79
+
80
+ ### Patch Changes
81
+
82
+ - a370a11: Surface the model credit promo (ADR 0021) everywhere models are listed, not only in the dashboard picker.
83
+ - Every model-listing route (`GET /v1/model-configs`, `/v1/model-configs/grouped`, `/v1/model-configs/available`) now stamps `creditPromoMultiplier` (e.g. `0.1`) and a plain-language `creditPromoLabel` (e.g. `"90% off"`, `null` when undiscounted) on each row, computed server-side from the same eligibility rule the billing seam uses and gated on platform-key custody. Routed families (`runtype:deepseek-v4-flash`) resolve through the routing table to their Workers AI primary, so a discounted route is advertised even when the id says nothing about Workers AI. MCP (`list_available_models`, `list_model_configs`, `list_model_configs_grouped`), the SDK and the CLI all read the same fields.
84
+ - Dashboard: Settings → Models rows and the Add Model dialog show the callout next to the model name; the Add Model dialog gains a "Discounted" filter that appears only while a promo is active. The model picker prefers the server label and falls back to the shared predicate.
85
+
86
+ ## 9.2.0
87
+
88
+ ### Minor Changes
89
+
90
+ - d129b45: Add bounded Flow loops and durable asynchronous execution handles with pollable status results.
91
+ - 005b795: First-party MCP catalog tools now cross the dashboard assistant's dispatch as name-only `runtype-mcp` catalog refs that the api hydrates from its own copy of the catalog, instead of full browser-supplied manifests. `clientTools[]` accepts `{ name, catalog: 'runtype-mcp' }` beside the existing `InlineClientTool` shape on `POST /v1/dispatch` and `POST /v1/agents/:id/execute`; an unknown ref is a 400, never a silent drop. Because the catalog no longer crosses the browser, the per-string `parametersSchema` cap returns from a provisional 8KB to 2KB (runtypelabs/core#7617).
92
+
93
+ No entry is exempt from the byte caps. The catalog clears 2KB on its own merits after the description rewrite (longest string 1894 bytes), and the host proxy collapses first-party tools to refs before dispatch, so a browser bundle loaded before this ships keeps working rather than sending manifests the new cap rejects.
94
+
95
+ `@runtypelabs/sdk` gains `CatalogClientToolRef` / `ClientToolEntry` / `isCatalogClientToolRef`, and `dispatch()` forwards a ref instead of throwing on its absent `parametersSchema`.
96
+
97
+ MCP tool and parameter descriptions were rewritten to be terse and factual; the flow step `config` description dropped from 4.4KB to under 2KB, with its uncovered caveats (send-email `html`, vector-search qualifiers, `loop`, recordFilter operators) moved into the `generate-flow` build instructions.
98
+
99
+ - 235751c: Report exact raw Runtype-funded provider and hosted-tool cost separately from blended execution cost.
100
+ - dd54ba0: Allow detached Flow dispatches to run with 30-minute Flow and step budgets, and expose those controls on MCP and Code Mode `run_flow` helpers.
101
+ - b2ac8a0: Harden durable asynchronous execution authorization, encrypted storage, native Claude Managed status polling, and SDK surface parity.
102
+ - dae3030: Add the agent key request contract and MCP tools. `@runtypelabs/shared` owns the
103
+ wire contract and the canonical `runtype.api-key/v1` key-file format that the
104
+ CLI writer and dashboard download both emit. The MCP server gains
105
+ `request_api_key`, `claim_api_key`, and `cancel_api_key_request`: filing a
106
+ request generates its own PKCE verifier and raises the approval page through URL
107
+ elicitation where the client supports it, and redemption defaults to storing the
108
+ minted key as a Runtype secret so only a `{{secret:KEY}}` reference reaches the
109
+ conversation. The secret-intake URL-elicitation helpers are generalized into a
110
+ reusable handoff helper that both flows now share, with unchanged secret-intake
111
+ behavior. The SDK gains the matching request/get/claim/cancel methods.
112
+ - c5a5baf: Exported runtime artifacts now DECLARE their host dependencies instead of being rejected for them (D-116).
113
+
114
+ `GET /v1/flows/{id}/export-runtime` no longer answers `422 DURABLE_FLOW_EXPORT_UNSUPPORTED` for a flow carrying a durable-class step (`wait-until` / `crawl`). That code is retired. Both export responses — flow and agent — now carry an additive `hostDependencies` array naming the runtime seams the artifact needs a host to wire before it can run everything it describes:
115
+ - `durable-pause-host` (`ExecuteFlowOptions.onDurablePause`, with `ExecuteAgentOptions.onDurablePause` as the agent-lane sibling) for durable-class steps anywhere in the artifact's executable closure;
116
+ - `background-run-coordinator` (`RuntimeToolExecutorDeps.backgroundRunCoordinator`) for a detached-capable `tools.subagentConfig` pool or a detached inline subagent tool.
117
+
118
+ Each entry carries the seam name, the reason, and the sites that caused it with full closure provenance (`ownerKind` / `ownerId` / `ownerName`), so a site living in an embedded agent, a capability, or an inline tool's backing definition is findable. The array is always present and empty when nothing needs wiring. The shape is a discriminated union on `kind` declared once in `@runtypelabs/shared` (`runtimeHostDependencySchema`); consumers should render `interface` and `reason` for a `kind` they do not recognise rather than failing.
119
+
120
+ The Workers-for-Platforms artifact lane is unchanged: a durable-class step is still a permanent `durable_steps_hosted_only` ineligibility reason there, because Cloudflare Workflows cannot live in a WfP dispatch namespace and that host is not the customer's to wire.
121
+
122
+ ### Patch Changes
123
+
124
+ - b216c6a: Follow-ups to the D-107 durable-flow export rejection (#7634).
125
+
126
+ Each entry in the `422` `durableSteps` array now carries provenance — `ownerKind` (`flow` / `agent` / `capability` / `inline-tool`) plus `ownerId` and `ownerName` where they exist — and the rejection message names the carrier. The check covers a flow's whole executable closure, so a bare `stepId` routinely named a step that appears nowhere in the flow the caller is editing.
127
+
128
+ The closure walk also got three correctness fixes: a nested flow's own embedded agent registry is swept (previously only the root flow's was, so an agent reachable only through a flow-tool backing flow was never inspected); an agent capability's `tool` payload is descended like any other inline runtime tool (previously only `flow` and `subAgent` were); and the fixed ten-hop closure recursion bound — which failed OPEN, exporting a durable step found past it — is replaced by a worklist with an identity visited-set that terminates without ever declining to look.
129
+
130
+ `MAX_CONDITIONAL_NESTING_DEPTH` is now exported from `@runtypelabs/shared`, so the export gate imports the validator's own cap instead of restating it, and `isDurableStepType` replaces the last two hand-restated `wait-until` / `crawl` literal pairs.
131
+
132
+ - edefa3c: Enforce single-config delegated external Agent evals and correlate their synthetic step and tool traces with the public runtime execution ID.
133
+ - f95b02e: A messaging human takeover now lapses and the agent resumes after the surface's `interventionMode.takeoverIdleTimeoutMinutes` (default 24 hours, `0` disables) with no operator send. The lapse is evaluated at the dispatch gate on the next inbound, applied as a compare-and-set on the observed `takeoverAt`, and logged as `conversation_takeover_lapsed`. Closing or reopening a conversation (`PUT /v1/messaging/conversations/{id}` status changes, `closeConversation`, the `/new` reset) now clears the takeover so a reopened thread never inherits a stale mute. Every `agent_mode` writer lives in `apps/api/src/services/messaging-takeover.ts`.
134
+ - f5250b6: Make saved external-agent authentication credentials write-only on agent
135
+ management responses. Create, update, detail, and agent-card refresh responses
136
+ now return `<redacted>` instead of plaintext, while safe same-auth-type PUT
137
+ round trips preserve the sealed credential and cross-host repoints still require
138
+ the caller to resend the real credential. Clients that copy an agent read into
139
+ a create request must now inject the original credential from their secret
140
+ manager instead of treating the read response as a credential export.
141
+ - e305a29: Key a sandbox's injected egress state by execution owner instead of holding it
142
+ DO-level, so concurrent code-execution steps sharing one sandbox id can no
143
+ longer clobber or prematurely clear each other's credentials.
144
+
145
+ `reuseSandboxId` is an author-supplied string, and a constant collapses every
146
+ concurrent execution of a flow, for one organization, onto one sandbox id. The
147
+ injected API token, credential-proxy rules and `networkAccess` posture were
148
+ plain instance fields on `CloudflareSandboxDO`, so the last writer won: a step
149
+ could run under another step's egress posture and another user's token, and a
150
+ sibling's cleanup cleared credentials a live step was still using.
151
+
152
+ Each execution now holds one grant keyed by its containment owner id and
153
+ releases only its own on the way out. A grant that asks for a different API
154
+ token or a different `networkAccess` than a live sibling is refused and its
155
+ step fails with an actionable error, since a shared container has no honest
156
+ merge for a container-wide posture; an omitted dimension resolves against the
157
+ ambient layer rather than inheriting a sibling's, so an execution that asked
158
+ for the fail-closed default never gains a neighbour's open egress. Every
159
+ execution takes a grant, including one that injects nothing — it has a posture
160
+ too, and leaving it uncounted would let it run under a sibling's broader
161
+ egress. Identical
162
+ credential-proxy rules compose — phantom tokens are persisted per secret, so
163
+ concurrent runs of one flow legitimately present the same phantom, and only a
164
+ phantom resolving to a different rule is refused. Grants carry their
165
+ execution's containment deadline, so an abandoned step stops refusing siblings
166
+ once its own budget lapses. The long-lived callers whose credentials
167
+ deliberately outlive one call (`deployWithPreview`, the agent tool lane's
168
+ `setNetworkAccess`) keep an ambient layer that a scoped release now restores
169
+ rather than wipes.
170
+
171
+ - 2783291: Finished the Slack OAuth install server-side, so a headless caller no longer needs the dashboard popup to complete setup. `POST /v1/oauth/slack/start` now accepts an optional `surfaceId`, validates that the surface exists and belongs to the caller before minting the encrypted state blob (400 on a malformed id, 404 on a missing or foreign one), and carries the id across the redirect; the callback then stamps the claim result onto that surface's inbound config (`integrationId`, `teamId`, `teamName`, `appId`, `botUserId`) and activates it — the same write the dashboard wizard performs client-side today. Stamping is idempotent, re-checks ownership at callback time, and never fails the OAuth: a surface deleted mid-handshake is logged and the install still completes, and a failed claim leaves the surface untouched. State blobs minted before the field existed keep parsing, and `SlackOAuthStartRequest` in the TypeScript SDK carries the new optional field. Separately, `POST /v1/integrations/slack/install` now enforces the `INTEGRATIONS:WRITE` API-key scope that every sibling integrations mutation route already required; it was missing.
172
+
173
+ ## 9.1.1
174
+
175
+ ### Patch Changes
176
+
177
+ - 7aea953: Follow-ups to the #6049 §D10 wire fixes: the AG-UI translator synthesizes a `TOOL_CALL_START` for complete-only tool frames (the MCP discovery connection pseudo-tool) so strict AG-UI clients no longer see an orphan `TOOL_CALL_END`; the AG-UI runtime tee redacts the discovery pseudo-tool's raw connection-failure text on end-user streams; the SDK's `agent_tool_complete` event carries the frame's `error` field so complete-only failures stay diagnosable; the terminal error `code`/`details.errorName` invariant is folded into one `terminalErrorFields` helper.
178
+ - 9c9eb47: Close the #6049 §D5 agent-lifecycle re-audit (D-111..D-115).
179
+ - `execution_start` on the resume leg of a paused agent run now carries `resumed: true` (D-113). Consumers keep run-level state across a start that carries the flag; the SDK forwards it on `AgentStartEvent.resumed` and `executeWithLocalTools` fires `onAgentStart` once per session instead of once per leg.
180
+ - Terminal `execution_error.code` is a stable vocabulary on the runtime flow lane too (D-115): a generic throw reports `EXECUTION_FAILED` with the class name on `details.errorName` instead of leaking `Error` / `TypeError` as the code. `StepTimeoutError` and the `AbortError` / `TimeoutError` pair still pass through by name; structured codes keep precedence. `FlowLifecycleComplete.error.details` carries the same observability host-side.
181
+ - Pins for the ratified pause sequence (D-111), the agent-kind shape on external / Claude Managed lanes (D-112), and the abort terminal (D-114).
182
+
183
+ ## 9.1.0
184
+
185
+ ### Minor Changes
186
+
187
+ - ccdc5f6: Expose the first-class record/conversation `ownerId` on every client surface (#3395)
188
+ - **CLI**: `records list`, `records create`, and `records export` take `--owner-id`; `records update` takes `--owner-id` plus `--clear-owner` (an explicit `null`, which clears the stored owner — omitting both leaves it untouched). `conversations list` takes `--owner-id`. `records get`/`update` render the owner, and the CSV export gains an `ownerId` column.
189
+ - **SDK**: `CreateRecordRequest.ownerId` and `CreateConversationRequest.ownerId` / `UpdateConversationRequest.ownerId` accept `string | null`, so `records.update()` and `conversations.update()` can clear an owner.
190
+ - **MCP**: `create_record` / `create_conversation` accept `owner_id`; `update_record` / `update_conversation` accept a nullable `owner_id` where `null` clears the owner.
191
+ - **Code Mode MCP**: the `listRecords` / `createRecord` / `updateRecord` / `listConversations` / `createConversation` / `updateConversation` method docs describe `ownerId` and its null-clears semantics.
192
+
193
+ - ccdc5f6: Promote conversation/record `ownerId` to a dedicated indexed column (#3395)
194
+ - Adds `records.owner_id` (B-tree indexed) via migration `0148`, backfilling every existing row whose `metadata.ownerId` is a non-empty string.
195
+ - Converges every write seam onto the column — conversations create/update, records create/update (`POST/PUT /v1/records`), and dispatch `resolveRecord`. One precedence rule (`resolveRecordOwner`): a top-level `ownerId` wins over a nested `metadata.ownerId`, a legacy string nested value is promoted into the column, and the nested key is never persisted again. Update requests accept `ownerId: null` to clear the owner; omitting the field leaves it untouched.
196
+ - Conversation and record responses source the first-class `ownerId` from the column (`GET /v1/records` list/detail and the CSV export now return it) and strip a residual nested copy from the echoed metadata when the column is set.
197
+ - Extends the `?ownerId=` filter to `GET /v1/records` (and the CSV export), backed by plain index equality instead of JSONB extraction. The record-filter DSL (`recordFilter` on `retrieve-record` / `update-record` / `list-records` steps, schedule `record_filter`, `POST /v1/records/preview-filter`) treats `ownerId` as a top-level column on both execution lanes.
198
+ - Adds the `{{_record.ownerId}}` system variable (#7549) on both execution lanes, sourced from the column; the key is omitted (never `undefined`) when a record has no owner.
199
+
200
+ Behavior note: records written after deploy carry the owner only in the column, so `{{_record.metadata.ownerId}}` no longer resolves for them. Use `{{_record.ownerId}}` instead.
201
+
202
+ ### Patch Changes
203
+
204
+ - b4c1858: `/v1/dispatch` agent loops now run on the `@runtypelabs/runtime` lane BY DEFAULT (legacy-engine retirement program, D-109 M1).
205
+
206
+ `options.useRuntimePackage` becomes an opt-OUT: an eligible agent dispatch that omits the field selects the runtime lane instead of the legacy engine. An explicit `false` still selects legacy verbatim, and every existing fold — `claude_managed` / no agent input, the parity-fold classes, and the unchanged request/definition support gates (`authoredDefinitionSupportIssue`) — still routes ineligible definitions to legacy.
207
+
208
+ Compatibility: a request that reaches the runtime lane through the new DEFAULT and then hits a support gate degrades to a legacy execution rather than returning HTTP 400. An explicit `useRuntimePackage: true` keeps rejecting exactly as before. Local agent inference no longer requires the explicit opt-in (it is refused only for an explicit opt-out).
209
+
210
+ - cf8dd74: Expose per-unit execution-engine attribution on the batch/eval read surfaces (#6106). `FlowStepResultSchema` (flow/record/batch step results), the eval results per-step rows, the eval run-scores per-case rows, and the schedules batch/run per-record results gain a nullable `executionEngine` ('runtime' | 'legacy'; null for rows written before attribution shipped or units that never executed), mapped verbatim from the persisted column. `GET /v1/batch/status/{id}` gains `executionEngineSummary` ('runtime' | 'legacy' | 'mixed' | null), derived at read time by aggregating the batch's per-record values — never inferred from IDs, sources, or models. SDK types regenerate from the updated spec.
211
+ - 901ee20: Return the per-execution metrics `record_results` already stores from `GET /v1/records/{id}/results`, and render them on a conversation turn.
212
+
213
+ `record_results` persists `total_tokens`, `model_used`, `execution_time_ms` and `estimated_cost` on every row the executor writes, but the handler's projection selected nine columns and none of them were these. The product Activity view is the only consumer that reads a conversation's turns, so a turn rendered with no latency, cost, tokens or model while an agent run beside it in the same merged list showed all four. Nothing failed and nothing logged: the fields were simply never on the wire, and a response schema that did not declare them agreed with the omission.
214
+
215
+ The projection and `RecordResultSchema` now carry all four, so they reach the OpenAPI spec and the generated TypeScript, Python, Ruby and Java SDK types. `estimatedCost` is declared as a string because `estimated_cost` is a `decimal(10,6)` column that reaches the wire unparsed, matching how `analytics.ts` already returns it.
216
+
217
+ The Activity turn pane renders them through the same `MetricCard` the run pane uses, so the two item kinds no longer read as different depths of detail. A turn carrying none of the four renders no metric block rather than four empty tiles. A new api-side contract test asserts set equality between the schema's fields and the query's projection, so neither half can drift from the other again.
218
+
219
+ - 86212a3: Make runs and conversations navigable instead of something you jump between.
220
+
221
+ Agent editor: closing the Test Agent sheet now routes to the run it produced,
222
+ detected by row identity rather than timestamps so it never depends on
223
+ client/server clock agreement, and never hijacks navigation when no run was
224
+ dispatched. The active tab and selected run round-trip through the URL
225
+ (`?tab=runs&run=…`), so a run can be linked, opened in a new tab, and reached
226
+ with the Back button. The Runs tab carries a run count and a failure dot, the
227
+ runs list gets keyboard triage, the per-run rail became a duration waterfall
228
+ instead of a second flat copy of the tool calls, and a stale run id renders a
229
+ recovery state rather than an endless skeleton.
230
+
231
+ Products: conversations are no longer reachable only from a hover-revealed icon
232
+ on a canvas node. A product-level Activity view lists runs and conversations
233
+ across every surface with a surface filter, surface nodes carry an activity and
234
+ failure signal, and the product header's stats link into Activity pre-filtered.
235
+
236
+ API: `agent_executions` gains a nullable `conversation_id`, exposed on the
237
+ executions list and detail responses, so consumers can group a multi-turn
238
+ thread's runs without a per-run log query. It is the run's own thread, distinct
239
+ from the existing `parent_conversation_id` (subagent lineage), and stays null on
240
+ stateless surfaces (webhook, schedule, eval, one-shot API) rather than being
241
+ synthesized from the execution id.
242
+
243
+ - 840934b: Force managed external-agent skill orchestration through Runtime at every whole-agent production entrance, including dispatch, internal jobs, webhook, messaging, client chat, voice, evals, nested agents, and surface overrides. Managed execution now fails closed rather than degrading to direct A2A delegation when Runtime is unavailable.
244
+
245
+ ## 9.0.0
246
+
247
+ ### Major Changes
248
+
249
+ - b01026e: Repair foreign-key drift across environments, separate asset storage identity from canonical ownership without breaking rollback Workers, and preserve organization-owned tools and skills when their creating user is deleted.
250
+
251
+ ### Patch Changes
252
+
253
+ - 7a5cf94: Persist the post-approval assistant turn on `POST /v1/client/resume`
254
+
255
+ A client-tool (WebMCP) pause is a terminal success, so the pausing
256
+ `/v1/client/chat` turn wrote its assistant message with the gated calls still in
257
+ `state: 'call'` and finished, while the resume that carried the approved tool
258
+ outputs wrote nothing at all. Everything the model produced after the approval
259
+ only reached the conversation record when a later chat turn happened to re-send
260
+ the browser's own copy of it, so a conversation whose last turn was an approval
261
+ lost its final answer outright.
262
+
263
+ Resume now claims the conversation's active-turn slot when it is free (never
264
+ taking it back from a newer chat turn the visitor started instead of approving),
265
+ assembles the leg's assistant text from its own stream, resolves the
266
+ stored `toolInvocations` from the submitted `toolOutputs`, and persists both
267
+ under the same compare-and-swap guard — on completion, on a re-pause, and on the
268
+ buffered (`streamResponse: false`) path. A client's later echo of that message
269
+ is deduplicated, and the request accepts an optional `assistantMessageId` so a
270
+ client that mints the id locally gets exact id dedupe.
271
+
272
+ ## 8.0.3
273
+
274
+ ### Patch Changes
275
+
276
+ - e154387: Surface managed Telegram bot creation failures instead of spinning silently. The status endpoint now runs a KV-cached manager-webhook health probe once a request has been pending for over two minutes and returns a `diagnostic: 'manager_webhook_unhealthy'` hint (new optional enum field in the SDK types); the verdict is derived from the delivery-blocking verify checks only, so a stale Telegram `last_error_date` cannot flip it. The Create New Bot wizard shows a slow-progress notice after 90 seconds, a misconfiguration notice when the diagnostic fires, and offers a one-click fallback to manual bot connection. Manual installs for a surface now cancel that surface's abandoned managed-bot requests, retrying after a poll timeout cancels the timed-out request, and initiate logs a traceable success line.
277
+
278
+ ## 8.0.2
279
+
280
+ ### Patch Changes
281
+
282
+ - 3b4f69c: Normalize package authorship and copyright metadata.
283
+
284
+ The `author` field reads `Runtype` rather than `Runtype Labs` across every
285
+ workspace package that set it, and the copyright holder in each license file is
286
+ now `Runtype, Inc` — replacing `Travrse Labs, Inc` in the CLI and the four Fern
287
+ SDKs, and the `Runtype Labs` that the new `flue-otel` license was written with.
288
+
289
+ Metadata only: no source, dependency, or behavior change.
290
+
291
+ ## 8.0.1
292
+
293
+ ### Patch Changes
294
+
295
+ - 047a57b: Serve `finalOutput` on the agent-executions list by default, with a `view=compact` opt-out.
296
+
297
+ `GET /v1/agents/{id}/executions` now accepts the shared `view=full|compact`
298
+ collection-representation parameter and defaults to `full`, which carries each
299
+ run's `finalOutput`. The route also serves the `?executionId=` single-row
300
+ lookup, so callers reading a run's result off it previously got `stopReason`
301
+ and `totalTokens` with no output and read the intact row as data loss.
302
+ `view=compact` restores the historical lightweight projection (and applies the
303
+ standard 100-character string preview). The per-iteration blobs
304
+ (`iterationDetails`, `loopConfig`) stay detail-endpoint-only in both views.
305
+
306
+ The MCP `list_agent_executions` tool gains the same `view` parameter and
307
+ defaults to `compact`, matching every other MCP list tool; `view` is threaded
308
+ through both `RuntypeClient` implementations and the Code Mode executor so an
309
+ explicit `full` actually reaches the API. The dashboard runs list asks for
310
+ `compact` explicitly since it renders only row titles and status.
311
+
312
+ ## 8.0.0
313
+
314
+ ### Major Changes
315
+
316
+ - 93387f9: Add the expand-first organization model-configuration foundation: canonical organization-owned storage, creator-safe lifecycle, rollback-compatible reads and runtime settings, stale legacy-write containment, and nullable ownership/provenance fields in model-config responses. The nullable `userId` response contract is a breaking schema change required before canonical organization rows can be activated.
317
+ - 8847973: Remove the dormant `@runtypelabs/guardrails` library and the entire skill-spector scanning feature (the `@runtypelabs/skill-spector` package, the `apps/skill-spector` scanner worker, and the API/dashboard wiring) from the codebase. The advisory `POST /v1/skills/scan` endpoint and the `enableSkillScanner` profile-features flag are removed; the TypeScript SDK drops `skills.scan()` and the `SkillScan*` verdict types; the dashboard drops the skill "Security scan" affordance.
318
+
319
+ ### Minor Changes
320
+
321
+ - 7d284c9: Activate organization-owned model configurations with conflict-preserving migration, shared teammate CRUD, fail-closed runtime resolution, creator-independent lifecycle, transactional audit events, rollback aliases, and a dashboard recovery state for conflicting legacy settings.
322
+ - 3ea4dd2: Migrate organization Amazon Bedrock credentials to shared organization connections with explicit BYOK custody across REST, TypeScript SDK, MCP, and Code Mode; deterministic structured-credential runtime resolution; lifecycle preservation; audit attribution; and storage-only mixed-version aliases that cannot silently select execution custody.
323
+ - 59e5efa: Move organization OpenAI-compatible credentials and typed endpoint metadata to shared organization connections with explicit BYOK authority, exact model discovery and sync, creator-independent lifecycle, rollback-safe endpoint mirroring, and consistent REST, SDK, MCP, Code Mode, dashboard, runtime, status, telemetry, and audit behavior.
324
+ - 60e39b1: Migrate organization Mixlayer model credentials to shared organization connections with explicit BYOK or platform-managed custody across REST, TypeScript SDK, MCP, Code Mode, and the dashboard; deterministic runtime and status resolution; lifecycle preservation; audit attribution; and canonical mixlayer policy identity for legacy Modelsocket-compatible execution.
325
+ - 553da9d: Migrate organization Tinfoil model credentials to shared organization connections with explicit BYOK custody across REST, TypeScript SDK, MCP, and Code Mode; deterministic runtime and status resolution; lifecycle preservation; audit attribution; and storage-only mixed-version aliases that cannot silently select execution custody.
326
+ - a2c4ec7: Migrate organization Together.ai credentials to shared organization connections with explicit BYOK custody across REST, TypeScript SDK, MCP, Code Mode, and the dashboard; deterministic runtime and status resolution; creator-independent lifecycle; transactional audit attribution; and guarded dedicated-endpoint routing that cannot redirect the organization credential outside Together.ai.
327
+ - 6671abd: Move Vercel AI Gateway credentials to organization-owned connections with explicit shared BYOK or Platform authority, durable actor audit, creator-independent lifecycle, typed custom gateway settings, and consistent SDK, MCP, Code Mode, dashboard, runtime, and billing attribution.
328
+ - f3aef36: Migrate Vertex AI and Vertex AI (Claude) credentials to independent organization-owned connections with explicit BYOK custody, creator-independent lifecycle, transactional actor audit, and provider-scoped service-account resolution across REST, SDK, MCP, Code Mode, dashboard, legacy execution, and hosted runtime.
329
+
330
+ ### Patch Changes
331
+
332
+ - af59a67: Agent-lane consumers scope terminal semantics to their own execution, so a relayed flow-as-tool child no longer supplies the run's terminal, final text, or response body.
333
+
334
+ A flow tool relays its nested flow onto the parent agent's stream, and that child always completes before the parent's `tool_complete`. Consumers reading "this run's terminal / final text / reported error" now filter by the owning `executionId` (`ownExecutionFrames` / `isOwnExecutionFrame`). Fixes an HTTP 500 on non-streaming `/v1/agents/{id}/execute` (`tool_complete after terminal execution_complete`), a voice turn that failed closed on a nested `success: false`, a duplicate AG-UI terminal callback, a Product API capability returning a nested flow step's payload as its `data`/`message`, and an SDK `onAgentComplete(success:false)` firing mid-run for a nested failure the agent recovered from.
335
+
336
+ - c29f1ce: Prepare Linear and GitHub built-in integrations for organization-owned credential authority with previous-Worker guards, owner-native credential preservation, and forward-compatible runtime/status resolution.
337
+ - 4776fa0: Retain external OTLP spans, span events, and LogRecords in the append-only
338
+ customer Logs tier on both the standalone ingest worker and API fallback lane.
339
+ The projection resolves tenant attribution before retention, applies the
340
+ agent's logging and PII-redaction policies, scrubs credential-shaped content,
341
+ and records bounded-row truncation explicitly. Logs and trace readers collapse
342
+ at-least-once retries by kind-specific identities and rebuild the exported span
343
+ parent/child tree without double-counting envelope metrics. OTLP LogRecords are
344
+ now decoded for protobuf and JSON exports; metrics remain acknowledged without
345
+ storage. JSON `/v1/executions/ingest` runs remain a compatibility path and now
346
+ return an explicit trace-availability reason instead of looking like a
347
+ successful empty trace. MCP execution-id inputs clarify that they accept the
348
+ runtime `execution_id`, not the `aex_...` database row ID.
349
+
350
+ ## 7.3.1
351
+
352
+ ### Patch Changes
353
+
354
+ - 5df5995: Migrate organization Braintrust tracing to organization-owned observability connections with shared control/runtime resolution, bound secret custody, legacy promotion, lifecycle resilience, and actor-attributed audit events.
355
+ - 1419c72: chore: update model configs from models.dev/Vercel/Mixlayer APIs
356
+
357
+ Adds 3 runtime models (`gemini-3.7-flash`, `google/gemini-3.7-flash`,
358
+ `alibaba/qwen3.8-2.4t-a95b`); no models were removed and no pricing changed.
359
+
360
+ Also swaps the account default in `DEFAULT_MODELS_FOR_NEW_ACCOUNTS` from
361
+ `gemini-3.6-flash` to `gemini-3.7-flash` in the fast Gemini flash-tier slot:
362
+ same 1M context, same reasoning/tool-use/vision/audio-input capability tags,
363
+ same google→vercel routing, at half the blended cost (0.00225 vs 0.0045 per
364
+ 1k tokens). This moves `DEFAULT_MODEL_ID`, which is the OpenAPI `model`
365
+ default on the prompt-create/update routes, so the spec and SDK types are
366
+ regenerated. `EVAL_JUDGE_MODEL` and `PRODUCT_GENERATOR_MODEL` are
367
+ intentionally untouched.
368
+
369
+ - 948ceed: Migrate organization Anthropic credentials to shared organization connections with explicit custody policy, deterministic runtime resolution, lifecycle preservation, audit attribution, and mixed-version compatibility.
370
+ - c56a67e: Migrate organization Google model credentials to shared organization connections with explicit custody policy across REST, TypeScript SDK, MCP, and Code Mode; deterministic runtime and status resolution; lifecycle preservation; audit attribution; and storage-only mixed-version aliases that cannot silently select execution custody.
371
+ - 212bd10: Migrate organization OpenAI credentials to shared organization connections with explicit custody policy, deterministic runtime resolution, audit attribution, legacy model-config compatibility, and a provider-agnostic mixed-version guard for later provider activations.
372
+ - 5ae8a9c: Migrate organization xAI model credentials to shared organization connections with explicit custody policy across REST, TypeScript SDK, MCP, and Code Mode; deterministic runtime and status resolution; lifecycle preservation; audit attribution; and storage-only mixed-version aliases that cannot silently select execution custody.
373
+
374
+ ## 7.3.0
375
+
376
+ ### Minor Changes
377
+
378
+ - d1ba834: Add a uniform `view=compact` representation to collection endpoints, cap long string previews at 100 characters, default MCP list tools to compact responses, and fix agent type filtering.
379
+
380
+ ## 7.2.1
381
+
382
+ ### Patch Changes
383
+
384
+ - 083846c: External agents can speak Runtype's unified stream (`protocol: 'runtype-stream'`).
385
+
386
+ `agents.externalConfig` gains one additive field, `protocol`. Absent and `'a2a'`
387
+ both mean today's A2A JSON-RPC behavior byte-for-byte — the column is JSONB, so
388
+ no migration and no backfill. `'runtype-stream'` means the endpoint answers
389
+ `POST {endpoint}` with a `text/event-stream` in the 35-event unified vocabulary,
390
+ the same wire Runtype's own engines emit. `framework` also gains `'flue'`, a
391
+ reporting hint that nothing branches on.
392
+
393
+ **This fixes a live correctness bug, not just a feature.** Everything that runs
394
+ an external agent through `executeAgentInternal` — plain agent batches,
395
+ schedules, messaging channels, webhook surfaces — got `{ success, responseText,
396
+ error }` and wrote no `tool_executions` rows, so anything reading a run's trace
397
+ saw an EMPTY one. Every trace grader (`called_tool`, `tool_order`,
398
+ `max_tool_calls`, `used_no_tools`) therefore failed in a way that looked like the
399
+ agent misbehaving. On the new protocol the structure survives the boundary and
400
+ the rows are written.
401
+ - `ExternalAgentProxy.executeUnifiedStream` is a fourth method on the existing
402
+ proxy, so all outbound external-agent I/O keeps one home: the same
403
+ `UrlValidationService` (SSRF on a customer-supplied endpoint), the same
404
+ `buildAuthHeaders` (exactly one place materializes the sealed credential), the
405
+ same `timeoutMs` / `retryCount` fields. Retries cover the CONNECT phase only —
406
+ once a frame has been forwarded the request is not replayable.
407
+ - The request body is the full `messages` history rather than the A2A lane's
408
+ last message as one text part, so adopting the protocol also fixes that
409
+ truncation. It is deliberately the dispatch body `examples/flue-persona`
410
+ already accepts, which makes that example the executable reference
411
+ implementation of the server side.
412
+ - `executeExternalAgentUnified` (`agent-executor.ts`) folds the terminal frame
413
+ into the usual result shape; `InternalAgentExecutionParams` gains an optional
414
+ `stream`, and the external branch tees the caller's sink with
415
+ `createAgentToolSSETap`. The tap needed no changes — it already reads the
416
+ native unified tool vocabulary — which is what makes tool rows, and therefore
417
+ batch trace grading, start working.
418
+ - A terminal frame's `totalCost` is returned but never persisted as Runtype
419
+ cost. It is the customer's assertion about their own provider spend, and
420
+ `aggregateExecutionCost` has no provenance predicate yet.
421
+
422
+ **Validation is a trust boundary here.** The stream is customer-authored input
423
+ on the path that writes DB rows, so every frame goes through
424
+ `unifiedSSEEventSchema` (non-conforming frames are dropped, as the AG-UI edge
425
+ does) and the run goes through the shared structural validator
426
+ (`packages/shared/src/utils/unified-sse-conformance.ts`) — no private copy, so
427
+ producer and consumer are held to identical rules. A structural violation (a
428
+ `tool_complete` for a call that never started, a second `execution_start`, a
429
+ frame after the terminal, a hole in `seq`) is never forwarded and fails the
430
+ dispatch, and event/byte/frame caps plus the configured timeout bound every
431
+ hostile shape. That validator now also exposes `createConformanceTracker`, an
432
+ incremental form for exactly this consumer; the whole-array
433
+ `findConformanceViolations` is implemented in terms of it so the two can never
434
+ disagree.
435
+
436
+ Not included, and unchanged: the synchronous `/eval/run` path still rejects a
437
+ non-`runtype` agent target with `AGENT_TYPE_RUN_UNSUPPORTED` (its message is
438
+ narrowed to name the surfaces that do work). Direct dispatch for unified
439
+ external agents there is the next phase.
440
+
441
+ ## 7.2.0
442
+
443
+ ### Minor Changes
444
+
445
+ - 6ae01b5: Canonicalize the "Managed in code" provenance indicator across the dashboard and expose flow provenance through the API.
446
+
447
+ One shared dashboard module now owns the badge, the save-overwrite confirmation dialog, and the predicate, which delegates to `isManagedByCodeSource` in `@runtypelabs/shared`. This fixes terraform-managed tools, which previously showed no badge and skipped the overwrite warning because the tools surface hardcoded a check against the `sdk` source.
448
+
449
+ Flows gain provenance end to end: `lastModifiedSource` is now returned on the flow list and detail responses (and by create/update), and the flow editor renders the badge and gates Save behind the confirmation dialog, matching agents. The flows index badges code-managed rows.
450
+
451
+ ## 7.1.0
452
+
453
+ ### Minor Changes
454
+
455
+ - 27d0b95: Add an additive optional `stepErrorCount` to `execution_complete`: the count of top-level steps that completed carrying a non-null `error`, **including continue-on-error swallows**.
456
+
457
+ It is a **sibling** of the failure counters, never a replacement. `stepErrorCount` is deliberately the LARGER set: a step that swallowed its error under the default `errorHandling: 'continue'` reports `success: true` and still carries the reason on its top-level `error`, so it contributes to `stepErrorCount` but not to `failedSteps` or to the flow's `success` verdict. Collapsing the two onto one predicate would either redefine `success` (counting swallows as failures) or undercount errors.
458
+ - Emitted on `execution_complete` (unified SSE) by both the API-owned lane and the `@runtypelabs/runtime` FLOW lane, and carried inside the terminal event of the buffered JSON dispatch body.
459
+ - Persisted with the execution's `flow_complete` telemetry event, so a run's swallowed-error count is queryable after the fact.
460
+ - Threaded through the runtime's `RuntimeFlowProgress` and the persisted resume state, so a paused/resumed flow keeps an accurate count across legs.
461
+ - Optional and additive: absent (not `undefined`) when a producer has no counter, so existing clients and parsers are unaffected.
462
+
463
+ Documented boundary: the two dispatch-seam swallows (`generate-embedding`, `vector-search`) carry their reason on `metadata.error` with no top-level `error`, so they land in neither counter — `stepErrorCount` counts exactly what `step_complete.error` puts on the wire.
464
+
465
+ ### Patch Changes
466
+
467
+ - 8d85ce9: Forward the A2A conversation handles on an `await` pause, so a paused external-agent run stays correlatable off the buffered response and the SDK callback.
468
+
469
+ Follow-up to #6845 (#6103 §B3), where the external-agent `input-required` pause moved from `approval_start` onto the `await` rail. Two consumers still only read the handles off `approval_start`:
470
+ - **API** — the buffered (`streamResponse: false`) dispatch response forwarded `externalAgent` only from an `approval_start` terminal, so a non-streaming A2A pause returned `status: 'paused'` with no `externalAgent` at all. The `await` terminal's context is now forwarded the same way. Resume was never affected (it routes off the persisted checkpoint), but the response shape regressed for exactly the lane that PR retargeted.
471
+ - **SDK** — the unified `await` → `agent_await` mapping dropped `awaitReason`, `elicitation`, and `externalAgent`, while the same adapter already forwards `externalAgent` on `execution_complete`. An `onAgentPaused` consumer could not tell an A2A elicitation from a client-tool wait, nor see the `contextId` / `taskId` to resume the conversation with. `AgentPausedEvent` gains the three optional fields, typed against the canonical wire schemas: a new exported `AgentElicitation` (and `AgentElicitationRequest`) restates `unifiedElicitationSchema` in full — both `mode` values, the required `message`, plus `requestedSchema`, `url`, `serverName`, `pauseCount`, and multi-request `requests` — and `externalAgent` reuses the existing exported `ExternalAgentContext`. Previously these fields were reachable only as `unknown` through an index signature, so consumers had to cast to render a form, follow a `url`-mode auth prompt, or preserve the pause-count binding.
472
+
473
+ - cdff95a: A2A runtime parity for the external-agent client (#6103 §B3).
474
+
475
+ **Runtime** — "speaking A2A" is now a first-class feature of the runtime package. A new `engine/a2a/` module owns the wire protocol for both A2A clients: 0.3/1.0 negotiation off `cachedAgentCard.protocolVersion` (method names, `a2a-version` header, roles, part oneofs, `TASK_STATE` normalization), endpoint preference for the cached card, and immediate-reply result normalization (a `{ result: { message } }` reply is a completed task carrying the message text, not an empty output). The external-agent client (`external-agent.ts`) and the A2A tool path (`runA2A`) now share it, so the two clients agree on the wire. Transient send failures retry (`retryCount ?? 3`, 100ms ×2, no retry on abort / URL-validation / JSON-RPC error), and per-agent `timeoutMs` bounds each attempt while the engine wall clock still bounds the whole run.
476
+
477
+ The external-agent `input-required` / `auth-required` pause is now a resumable await instead of an `approval_start`. It emits the unified `await` frame with `awaitReason: 'a2a_input_required'` / `'a2a_auth_required'`, the peer's question on the existing elicitation payload, and a new additive optional `externalAgent: { contextId, taskId }` field (wire shape reused from `approval_start`/`execution_complete`). A resumable external-mode checkpoint (`kind: 'external-agent'`, keyed at pinned iteration 0, overwrite-on-re-pause) is persisted through the same store as loop checkpoints; a resume re-enters external mode and sends the user's reply as the next message on the checkpoint's `contextId`. No checkpoint is written when the peer assigned no `contextId`. The new `external_input_required` stop reason maps to the paused status so usage is not double-counted. `awaitReason` remains UX context only — nothing branches on it; the tool/skill approval fold is unchanged.
478
+
479
+ **API** — the runtime export pipeline now carries per-agent `timeoutMs`/`retryCount` through to the runtime lane, and the paused-status mapping understands the new stop reason.
480
+
481
+ **Shared / SDK** — the additive `await.externalAgent` field is added to the unified SSE schema (open optional field) and regenerated into the TypeScript SDK types and OpenAPI spec.
482
+
483
+ ## 7.0.0
484
+
485
+ ### Major Changes
486
+
487
+ - dda50f3: Retire the legacy eval override channels. `overrides` is the only spelling the wire accepts: `stepOverrides` (with its `'*'` key), `claudeManagedOverride`, `advisorOverride`, and the four scalar surface-eval `*Override` fields no longer parse.
488
+
489
+ **Breaking**, and the major lands on `@runtypelabs/sdk` alone because that is the only package anything resolves by range: it declared `stepOverrides` and `claudeManagedOverride` as accepted input on `runVirtualEval` and no longer does. The API takes a minor — its wire contract breaks too, but the package is private, so its version is changelog and deploy metadata rather than a contract a dependent resolves. What tells an API caller about the break is this entry and the 400 itself, not a version number they never read.
490
+
491
+ A request still using a retired spelling gets a 400 naming the `overrides.*` field that replaced it. That rejection is the point rather than a side effect — the eval request schemas are `.passthrough()`, so merely undeclaring a field would leave it silently ignored, and an ignored override is recorded and scored exactly like an applied one. Failing loudly is what keeps an unmigrated caller from being graded as a baseline it never asked for.
492
+
493
+ Migration is a rename in every case:
494
+
495
+ | Retired | Replacement |
496
+ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
497
+ | `stepOverrides['*']` | `overrides.agent` or `overrides.flow`, whichever matches the target |
498
+ | `stepOverrides[stepId]` | `overrides.steps[stepId]` |
499
+ | `claudeManagedOverride` | `overrides.claudeManaged` |
500
+ | `advisorOverride` | `overrides.advisor` |
501
+ | `temperatureOverride` / `maxTokensOverride` / `responseFormatOverride` / `reasoningOverride` | `overrides.flow.temperature` / `.maxTokens` / `.responseFormat` / `.reasoning` |
502
+
503
+ Splitting `'*'` into `agent` / `flow` is what makes the target-illegal case a 400 instead of a drop: the one key used to mean "the agent under test" on one target kind and "every prompt step" on the other, so it could never be checked.
504
+
505
+ `options.modelOverride` is unaffected — it is a dispatch knob, not an eval override channel, and keeps its existing lowest precedence beneath the caller's own `overrides.flow`.
506
+
507
+ In-flight batches are unaffected and the deploy needs no coordination. The plan expected this deletion to require a versioned Durable Object drain or a no-batches deploy window, which holds only while "what a caller may send" and "what persisted state may contain" are the same type. They are now separate: `evalOverridesInputFromExecutionConfig` folds the state shape for internal execution metadata and pre-carrier `batchState`, and a request cannot reach it. `'*'` therefore survives inside execution metadata on purpose — it is still how the flow engine spells "every prompt step", which no wire change touches.
508
+
509
+ A data migration (`0130_backfill_eval_config_canonical_overrides`) rewrites the historical `batch_executions.eval_config` audit snapshots into the same canonical shape, so one spelling remains in the system. It cannot change what any run does: nothing executes from that column — the batch Durable Object restores `evalConfig` from its own state, never from Postgres — and every read of it is display or reporting. The Runs-list reader now understands both shapes, so it labels runs correctly whether or not the migration has run.
510
+
511
+ ### Minor Changes
512
+
513
+ - a634b53: Eval overrides now travel on one canonical `overrides` request field
514
+
515
+ `overrides` previously carried only the per-sub-agent targets, which had no
516
+ legacy channel and no first-party producer. It now carries every channel —
517
+ `agent` / `flow` / `steps` / `claudeManaged` / `advisor` beside `subagents` /
518
+ `subagentDefaults` — and the dashboard, the SDK, the MCP tool inputs and the
519
+ OpenAPI examples all emit it.
520
+
521
+ Four new submit-time 400s replace what were silently-scored no-ops: setting
522
+ both the canonical and the legacy spelling of one channel, `overrides.agent` on
523
+ a flow target (or `overrides.flow` on an agent target), and an `overrides.steps`
524
+ key naming no step of the target. The unreachable-`agentId` rejection now
525
+ applies on every eval lane, `/eval/stream` included.
526
+
527
+ The legacy `stepOverrides` (with its `'*'` key), `claudeManagedOverride` and
528
+ `advisorOverride` fields still parse and still resolve identically; they are
529
+ deleted in a later phase.
530
+
531
+ ### Patch Changes
532
+
533
+ - 836fc82: Phase 0 of the Persona visitor conversation-history contract. Everything here is additive: `flow` on init, `flowId` on summaries and query parameters, and the stored `metadata.flow_id` key all stay as compatibility aliases through the rolling-deploy window.
534
+
535
+ **Durable handles.** `POST /v1/client/init` now returns `conversationId` from every successful branch (fresh mint, legacy `sessionId` replay, `conversationId` resume) plus a canonical top-level `targetId` from the chat-capable ones. A widget persists the conversation, not the short-lived session. `targetId` is not the identity function on what the caller configured: an agent-only client token resolves its agent to that agent's primary flow before storing, so a widget filtering by the configured agent id would have got an empty history and, on delete-all, would have deleted nothing while reporting success. The four target shapes are pinned against the record's stored `flow_id`.
536
+
537
+ **`targetId` filter symmetry.** Both `GET` and `DELETE /v1/client/conversations` take it; a target-scoped UI can no longer clear conversations it never showed the visitor. Supplying `targetId` and `flowId` with different values is a 400 rather than a guess.
538
+
539
+ **Display projections.** A stored message's `content` is the model channel, which for anything richer than a plain text turn is not what the person read. Messages now carry an optional `displayContent` alongside it, plus `displayAvailable` on the read path; execution reads only `content`. Absent and empty-string projections are distinct states. Without a projection, stored content stands in only when it is plainly render-safe, so opaque model-only structure is withheld rather than handed to a renderer.
540
+
541
+ A terminal assistant turn is the hard case: its visible text is only computable in the browser after the stream closes, and if the visitor leaves right then no later request exists to carry it. `PATCH /v1/client/conversations/{id}/display-projections` is that channel. Projection-only (it cannot create a message or move content, role, ordering, timestamps, count, ownership, or title), all-or-nothing per batch, idempotent, bound to the session's own conversation, and bounded for a `keepalive` request. The write compare-and-swaps on `conversationRevision`: unlike the two chat-path writers, this route is not serialized by the active-turn CAS, so an unguarded rewrite of the transcript array could delete a turn persisted between its read and its write. A re-sent chat turn repairs a lost finalization.
542
+
543
+ **`conversationRevision`.** An opaque equality-only change token on init, detail, and the finalization response. It moves on every transcript mutation including a projection change, which deliberately leaves `updatedAt` alone so a closing tab cannot reorder a history list.
544
+
545
+ **Authoritative continuation.** `/v1/client/chat` executes the stored transcript merged with genuinely new message ids instead of the client's own array. A second device's turns can no longer be dropped by a stale or truncated local view; stored content wins for ids the record already holds.
546
+
547
+ **Identity acknowledgement (privacy-critical).** Identity admission is gated per owner, and with the gate off a supplied proof was accepted and ignored, so a request asking for verified cross-device scope ran silently at browser scope. A widget could not tell the difference, which is unacceptable for a verified erase. Every visitor grant now reports `identityStatus` (`not_provided` | `admitted` | `ignored`) and every history response carries `X-History-Identity-Status`. Init still succeeds with `ignored` so chat degrades gracefully; a history route handed such a proof now fails 503 `identity_proof_not_admitted` before any visitor-scoped read or mutation runs, pinned by DB-mutation counts on every destructive route. A rejected proof stays a 401 and is never reported as `ignored`.
548
+
549
+ **Previews.** Summaries carry a plain-text `preview` of the newest visitor-visible message, bounded to 140 Unicode code points, derived from the display projection and never falling through to opaque content.
550
+
551
+ Also pins the zero-message first-page-load ownership claim Persona's bootstrap depends on, and adds a generated wire fixture (`apps/api/tests/fixtures/client-conversation-history-wire.json`) for the Persona repo to pin its message mapper against.
552
+
553
+ ## 6.6.3
554
+
555
+ ### Patch Changes
556
+
557
+ - f59e62b: Add per-visitor conversation history to the public client surface, so an embedded Persona widget can show a visitor their own past conversations with no backend on the host site.
558
+
559
+ History-capable clients opt in with `visitorHistory: true` on `POST /v1/client/init`. The API then mints (or resumes) a durable anonymous visitor for that browser and returns a `visitor` grant alongside the session. The raw visitor secret is returned only when minted, is stored only as a SHA-256 hash, and is namespaced per client token so a secret from one site is inert on another. Legacy clients that do not opt in keep the previous response shape and do not create discarded visitors.
560
+
561
+ New session-authed, visitor-scoped routes:
562
+ - `GET /v1/client/conversations` (cursor-paginated, optional `flowId` filter)
563
+ - `GET /v1/client/conversations/{id}`
564
+ - `DELETE /v1/client/conversations/{id}`
565
+ - `DELETE /v1/client/conversations`
566
+ - `POST /v1/client/visitor/reset`
567
+
568
+ Each requires both a live `sessionId` and the `X-Visitor-Token` secret: a bare client token is public and site-wide, and a bare `sessionId` is a low-entropy TypeID, so neither alone can read history. A conversation the caller does not own answers 404 rather than 403, so the routes cannot be used to enumerate other visitors' conversations.
569
+
570
+ `POST /v1/client/init` also accepts a visitor-authorized `conversationId`, mutually exclusive with legacy `sessionId`, so a conversation selected from server history can be resumed without a browser-local conversation-to-session map. Out-of-scope conversations answer 404.
571
+
572
+ Conversations can be titled server-side from their opening exchange, replacing the timestamp placeholder, so embedders no longer need to keep local message snippets just to render a history list. This ships dark behind `enable-conversation-titles`: titling sends the opening exchange to a model from a different provider than the surface's agent, and there is no per-surface way to decline that yet, so it stays off in production until the `conversationTitles` config lands (`docs/features/planning/2026-08-08-conversation-title-configuration.md`). The flag doubles as an operator kill switch for a platform-run model call on a public surface.
573
+
574
+ `POST /v1/client/init` also accepts an optional `identityProof`. When the Identity Exchange gate is on, it is verified once against the token owner's registered integrations and projected to a durable `eu_*`, which binds the browser's visitor to that person. An invalid proof is rejected 401 and nothing is bound; an absent proof leaves the session anonymous, unchanged.
575
+
576
+ Binding an end user is the anonymous-to-identified merge: the visitor's existing conversations come along (no rows move — ownership is read through the link). Cross-device reads require a fresh admitted identity proof on that request; a stored visitor binding alone never widens authorization beyond the exact browser. Sibling resolution is scoped to the client token, so one identity on two sites remains two histories, and an unbound visitor resolves to itself alone rather than unioning on a null end user. A second person signing in on a browser that is already bound gets a fresh visitor instead of inheriting the first person's history.
577
+
578
+ Visitor ownership is tracked on a new `records.client_visitor_id` column and a new `client_visitors` table, deliberately separate from the `end_users` (`eu_*`) tenancy plane: an `eu_*` is an admitted, verified identity that doubles as an authorization key for end-user-private record reads, so an anonymous browser-held secret must not mint into it. Keeping the planes separate also leaves the verified `identityProof` path untouched.
579
+
580
+ - 308c44c: Stop every consumer surface from claiming execution-count caps that no longer exist (remove-execution-caps Slice 3).
581
+
582
+ Slice 2 removed execution-count caps from enforcement. This makes the dashboard, the REST responses, and the docs agree with that: a card-on-file account is never bounded by execution count, and the only remaining bound is the code-owned protective daily ceiling for accounts with no payment method, framed as "add a payment method to remove", never "upgrade your plan".
583
+ - `ExecutionStats` gains `executionCapReason` (`'no_card_ceiling' | null`), the single signal a surface may branch on. The neutral fallbacks (rate-limit block, spend-cap block, DO fail-open, failed display read) no longer report a ceiling-shaped limit.
584
+ - `GET /usage-limits` and `/usage-limits/daily` expose `executionCapReason` and always key the payload under `daily`; `inSlowMode` / `isOverage` / `overageCount` / `periodType` stay on the wire but are documented as inert.
585
+ - `GET /v1/billing/status` gains `usage.executionsToday` (`executionsThisMonth` is a deprecated alias carrying the same daily count); `isApproachingLimits` / `isOverLimits` are documented as always `false`.
586
+ - Dashboard: usage banners render only for a no-card account, the slow-mode modal is deleted (it had no producer), and the dev billing tester models the carded/no-card matrix instead of retired plan pools.
587
+ - CLI: `runtype billing status` and `runtype auth whoami` read `executionsToday` instead of `executionsUsed` / `executionsLimit`, which the API has never returned, so both printed "Executions: 0" against a live API. They now show a plain count (no invented denominator) and say "unavailable" when the usage read failed.
588
+
589
+ ## 6.6.2
590
+
591
+ ### Patch Changes
592
+
593
+ - 941f500: Warn at authoring time when an approval-requiring agent is bound to an
594
+ unattended surface (slice 1 of the unanswerable-approval-surfaces plan; no
595
+ execution behavior change). Adds the shared surface-attendance registry (an
596
+ `approvalAnswerable` trait on `SurfaceTraits` with polarity-explicit helpers
597
+ `isApprovalAnswerableSurfaceType` / `isApprovalUnanswerableSurfaceType`) and a
598
+ new advisory code `APPROVAL_UNANSWERABLE_ON_SURFACE` (warning severity, never
599
+ blocking) emitted from the product validator, the surface-item add/update
600
+ response envelopes (REST and, via passthrough, MCP `add_surface_item` — the
601
+ SDK response types gain an optional `warnings` array), and the agent-save
602
+ `_warnings` when an update arms approval on an agent already bound to
603
+ unattended surfaces. The dashboard channel and surface config panels show the
604
+ same warning inline. The webhook DO and messaging trigger now read their
605
+ (unchanged, `false`) attendance fact from the shared registry instead of
606
+ omitting it per call site.
607
+
608
+ ## 6.6.1
609
+
610
+ ### Patch Changes
611
+
612
+ - 5ad8bac: Add a runnable end-user usage acceptance lab, align client-token SDK types with agent-scoped surface tokens, support explicit local-test hosted identity, make magic-link OAuth continuation work across email-opened tabs, and make empty-ledger/dashboard deep-link paths testable end to end.
613
+ - 58f5a8a: Remove surfaces as a billing dimension entirely.
614
+
615
+ Surfaces (the deployed-channel objects on products: chat, api, mcp, slack,
616
+ etc.) no longer count against any plan limit or appear in any billing surface:
617
+ - Surface create / environment-change no longer consults the Schematic
618
+ `production-surfaces` / `development-surfaces` entitlement flags; surfaces are
619
+ unlimited on every plan.
620
+ - Surface counts are no longer synced to Schematic as `production_surface_count`
621
+ / `development_surface_count` company traits.
622
+ - `BillingConfig` drops the `productionSurfaceLimit` / `developmentSurfaceLimit`
623
+ fields; `/v1/billing/status` drops the surface limit + usage fields and the
624
+ surface component of `isApproachingLimits` / `isOverLimits`.
625
+ - The 402 `PRODUCTION_SURFACE_LIMIT` / `DEVELOPMENT_SURFACE_LIMIT` responses are
626
+ removed from the surface create / update / ensure routes (and their OpenAPI
627
+ contracts), so the surface-limit error handling in the dashboard is dead code.
628
+
629
+ Surfaces remain fully functional as a product concept; only the billing and
630
+ entitlement machinery is removed. The Schematic-side flags/features/traits
631
+ become unused and can be deleted from the Schematic dashboard separately.
632
+
633
+ - e12b556: Add an org-scoped, non-admin read endpoint `GET /v1/executions/{executionId}/journal`
634
+ that returns the durable `flow_execution_events` journal rows for an execution the
635
+ caller owns, redacted and in replay order (API-key/Clerk authenticated, ownership-scoped,
636
+ `FLOWS:READ`/`AGENTS:READ` scoped for API keys, 404 on unowned/missing ids, `truncated`
637
+ flag when the 1000-event cap is hit). This is a strictly-narrower read surface than the
638
+ admin trace bundle and is what the runtime-parity B-17 gate reads from the normal
639
+ `api.runtype-staging.com` surface.
640
+ - e23af15: Add a dedicated `ScheduleEntitlementErrorSchema` OpenAPI component for the
641
+ schedule 402 responses (create/update/resume). The runtime bodies already
642
+ return `message`, `code`, and `upgradeUrl`, but the routes referenced the
643
+ generic `ErrorSchema` (which declares only `error` + `details`), so
644
+ generated SDK types hid the upgrade path. The new schema extends
645
+ `ErrorSchema` with the three fields and is registered as
646
+ `#/components/schemas/ScheduleEntitlementError`. Regenerates the OpenAPI
647
+ spec and all SDK cores (TS/Python/PHP/Ruby/Java).
648
+
649
+ Addresses Greptile P1 on PR #6490.
650
+
651
+ ## 6.6.0
652
+
653
+ ### Minor Changes
654
+
655
+ - bcf78f5: Add typed record collections end to end (Records→Collections plan §5.1/§5.2). The SDK gains an augmentable `RecordCollections` map, a `CollectionMeta<S>` helper, and `client.records.from(slug)` — a typed scope whose `.list/.get/.create/.update` pin the record `type` to the slug and type `metadata` for that collection (`.get`/`.update` guard against cross-collection ids). A new authed `GET /v1/collections/types.d.ts` endpoint hand-emits a `.d.ts` (one interface per schematized collection plus a `declare module '@runtypelabs/sdk'` augmentation, with `// skipped:` markers for rows whose stored schema no longer parses) from the constrained schema dialect. `runtype records typegen [-o <file>]` fetches it (commit and diff-check the output in CI to catch collection-schema drift). The typegen endpoint is fanned out across surfaces: the `get_collection_types` MCP tool (standard + code-mode `getCollectionTypegen`) and the CLI.
656
+
657
+ ## 6.5.1
658
+
659
+ ### Patch Changes
660
+
661
+ - 22ab25a: Widen the SSE `agent_tool_start` toolType union to include `data_connection` and `search`, matching what both engines already emit on the wire.
662
+
663
+ ## 6.5.0
664
+
665
+ ### Minor Changes
666
+
667
+ - 9dca3bc: Add the agent state events to every wire layer (agent state IR, slice 2).
668
+
669
+ State change is now expressible on the wire as RFC-6902 JSON Patch over an open JSON
670
+ document, in all three declared contracts: the internal engine IR gains
671
+ `agent_state_snapshot` / `agent_state_delta`, the public unified vocabulary grows from
672
+ 33 to 35 events with `state_snapshot` / `state_delta`, and the AG-UI union gains
673
+ `STATE_SNAPSHOT` / `STATE_DELTA` under the spec's own field names. Both emitters
674
+ (`AgentEventEmitter`, `UnifiedEventEmitter`) can produce them, the API edge translator
675
+ projects internal onto unified, and the AG-UI translator projects unified onto the
676
+ protocol events.
677
+
678
+ The JSON-Patch shape moves to `@runtypelabs/shared` (`json-patch.ts`) so all three
679
+ schemas type against one definition; `packages/runtime/src/engine/state-channel.ts`
680
+ re-exports it and keeps the apply/diff behavior.
681
+
682
+ State events are deliberately block-neutral: unlike media, approval, and tool frames
683
+ they do not seal an open text channel, because state is rendered out-of-band and a
684
+ mid-turn write must not split the assistant's message. Both producers agree, pinned by
685
+ the runtime/unified parity gate.
686
+
687
+ The generated SDK types and Python SDK follow the OpenAPI spec, so `ExecutionStreamEvent`
688
+ now includes both new variants.
689
+
690
+ Still inert: nothing seeds a state channel and no run emits these yet. The reserved
691
+ state-write tool, seeding, and exposing state to the model land in the next increment.
692
+
693
+ - e76a310: Add feature-gated product usage analytics grouped by durable end-user identity. UsageTracker Durable Objects now retain exact, idempotent daily execution and platform-spend rollups for 93 days; the authenticated analytics API verifies product and tenant ownership, hydrates customer-facing identity labels, and reports attributed versus unattributed coverage.
694
+
695
+ Expose the breakdown through the TypeScript SDK and a lazy-loaded Usage settings subview with product, tenant, period, and sort filters, top-user metrics, unattributed-usage guidance, and formula-safe CSV export.
696
+
697
+ ### Patch Changes
698
+
699
+ - eab2128: Pin first-party dispatch helpers to the response mode selected by their JSON or streaming transport.
700
+ - 25412dc: Run flow-definition record batches and isolated step tests through the hosted
701
+ runtime, fail closed when a definition needs unsupported continuation behavior,
702
+ and submit large record sets to the batch processor instead of returning a
703
+ scheduled handle without enqueuing work. Preserve the prior temporal snapshot
704
+ during isolated step replays while stamping a fresh execution-start timestamp,
705
+ retain record execution history, and pin scheduled batches to the admitted
706
+ definition.
707
+ - 7ab031f: Authorize Product surface keys with immutable capability target IDs across API, MCP, A2A, AG-UI, and webhook execution paths.
708
+ - eab2128: Document and normalize buffered continuation responses across dispatch,
709
+ saved-agent, and client routes, preserving failures and chained approvals across
710
+ legacy and runtime continuations.
711
+
712
+ ## 6.4.0
713
+
714
+ ### Minor Changes
715
+
716
+ - c576866: Add operator-authored `sandbox.setupCommands` plus a `sandbox.secretEnv` map on the
717
+ agent Definition (env var name → managed secret KEY). Setup runs once after network
718
+ policy enforcement and before the model's first sandbox verb. The API resolves
719
+ credential values only for setup and delivers them out-of-band over the
720
+ `cloudflareSandboxExecWithSecretEnv` RPC, so they never appear in command strings,
721
+ model-facing tool schemas, logs, or spill files.
722
+
723
+ Model-authored `bash` always uses the plain credential-less RPC, even when its
724
+ command references a configured variable. A run-scoped memo plus trusted Durable
725
+ Object claim/completion state keeps setup once-only across parallel and sequential
726
+ tool calls. A setup-definition fingerprint versions the sandbox identity, so
727
+ configuration changes get a fresh machine rather than reinjecting credentials
728
+ into an existing model-tainted one. Failed or interrupted setup remains retryable.
729
+
730
+ Credentialed setup must remain operator-authored, foreground, and self-contained:
731
+ the secret supervisor freezes pre-existing processes and reaps every descendant on
732
+ return. Prefer `credentialProxies` (phantom tokens) when the target protocol
733
+ supports them.
734
+
735
+ - 9e19bc0: Surface inbound webhook health in the API and dashboard.
736
+
737
+ Rejected inbound webhooks were invisible: a Slack surface whose signing secret no longer matches returns 401 before any execution starts, so nothing reached the logs and the channel simply looked idle. The API has recorded per-surface delivery and failure history in `product_surfaces.webhook_health` all along, but never served it.
738
+
739
+ Surface responses now carry a `webhookHealth` projection (`lastDeliveryAt` plus the most recent rejections), and the dashboard keeps it refreshed while the product editor is open: the channel node shows a red "Rejecting events" dot, and the channel edit panel explains the specific failure and names the field to fix (for a Slack signature mismatch, the Signing Secret rather than the similar-looking Client Secret). Derivation and copy are single-sourced in `@runtypelabs/shared`.
740
+
741
+ ### Patch Changes
742
+
743
+ - ad943b9: Align public dispatch request types and clients with the canonical API schema.
744
+ - 15f3980: Document and type buffered dispatch responses, and honor `streamResponse: false` on the runtime-agent lane.
745
+ - 41a2650: Redact protected tool parameter values from debug streams, customer logs, approvals, and persisted tool executions while preserving protected-field attribution.
746
+ - 050c834: Retire legacy dispatch-scoped `{{secrets.key}}` values for hosted FLOW
747
+ execution. Hosted flows continue accepting the `secrets` field for wire
748
+ compatibility, but the pipeline strips it before either runtime or legacy
749
+ execution; use managed `{{secret:NAME}}` references for flow credentials.
750
+ - 17382d8: Retire the per-request legacy engine override for flow dispatches while
751
+ continuing to accept the compatibility field for existing clients and agent
752
+ runtime selection.
753
+ - 17382d8: Run eligible buffered JSON flow dispatches through `@runtypelabs/runtime`, using
754
+ the same structured runtime completion and unified events as the streaming path.
755
+ Explicit `useRuntimePackage: false` and runtime-ineligible requests continue to
756
+ use the legacy engine.
757
+
758
+ ## 6.3.6
759
+
760
+ ### Patch Changes
761
+
762
+ - 19e0d21: Remove the remaining backtracking ambiguity from the artifact-reference
763
+ `savedToPattern` in `extractArtifactReferencesFromMessages`, closing CodeQL
764
+ alert #320 (`js/polynomial-redos`).
765
+
766
+ An earlier fix stopped the capture's _first_ character from accepting
767
+ whitespace, which removed the overlap with the preceding `\s+`. It left a second
768
+ overlap in place: the capture body `[^—\]\n]*?` accepts tabs and spaces, and so
769
+ did the `\s+—` alternative in the trailing group, so the two still shared split
770
+ points across a whitespace run.
771
+
772
+ The trailing group now lists its terminators bare (`—`, `]`, `\n`, `$`). Because
773
+ the capture class excludes exactly those terminators, the two are disjoint,
774
+ exactly one parse exists at each position, and the polynomial behavior is gone
775
+ structurally rather than merely bounded.
776
+
777
+ Behavior is unchanged: dropping `\s+` only moves whitespace that sits before a
778
+ `—` from the separator into the capture, and the capture is already `.trim()`ed
779
+ at the use site. Verified equivalent across 16 input shapes, and pinned by two
780
+ new tests covering whitespace around the separator and a 200k-character
781
+ whitespace run.
782
+
783
+ ## 6.3.5
784
+
785
+ ### Patch Changes
786
+
787
+ - 6784100: Eliminate ReDoS on externally-influenced inputs
788
+
789
+ Five regexes that ran over third-party or tenant-authored text combined an
790
+ unbounded/lazy class with an overlapping quantifier, making them quadratic on a
791
+ hostile input. Each is now linear, with behavior preserved:
792
+ - **skill-spector** hostname extraction stripped delimiters with a single `/g`
793
+ alternation of two anchored branches (the global scan retried the `$` branch at
794
+ every position). Split into two separately anchored, non-global replaces, plus a
795
+ token-length cap — the manifest under scan is hostile by design.
796
+ - **sdk** artifact-reference extraction let `\s+` overlap a whitespace-accepting
797
+ lazy class; the capture's first character now excludes whitespace.
798
+ - **api** `store-vector` unwrapped `config.vectorsSource` with a regex whose `\s*`
799
+ overlapped `[^}]+?`. It now calls the shared, regex-free
800
+ `unwrapVectorSourceTemplate`, which also converges the api with the runtime's
801
+ previously private twin.
802
+ - **shared** `Link` header parsing widened `[^>]*` to `[^<>]*` so an unterminated
803
+ `<` fails at the next `<`.
804
+ - **model-execution** parameter-rejection classification now bounds the scanned
805
+ provider error body before matching, covering all five patterns.
806
+
807
+ - d3bb1ed: Apply security overrides for dev/build tooling (batch 1), resolving 29 Dependabot
808
+ alerts. Adds bounded `overrides` entries for `@tootallnate/once`, `ajv`,
809
+ `minimatch`, `path-to-regexp`, `smol-toml`, `rollup`, `@babel/core`, `js-yaml`,
810
+ `brace-expansion`, `undici` (<6 only) and `sharp`, and bumps the
811
+ `vercel-functions-hello` example's `vercel` devDependency 44 -> 58 so
812
+ `@vercel/fun` stops pinning `tar@6.2.1` (which no `tar` override could clear).
813
+
814
+ No runtime source changed; this is a lockfile/root-config change. Notable
815
+ resolution shifts: `sharp` 0.34.5 -> 0.35.2 (prebuilt binary set swap, verified
816
+ against the marketing Next.js build), `rollup` 4.57.0 -> 4.62.0, and `tar@6.2.1`
817
+ removed from the tree entirely. `undici@7.28.0` is deliberately untouched for
818
+ miniflare / wrangler / `@cloudflare/vitest-pool-workers`.
819
+
820
+ - 13b718f: Apply security overrides for runtime-reachable dependencies (batch 2). Adds bounded
821
+ `overrides` entries for `fast-uri`, `form-data` (3.x and 4.x), `lodash`, `uuid` (13.x),
822
+ `basic-ftp`, `defu`, `flatted`, `body-parser` (2.x), `js-yaml` (4.x),
823
+ `brace-expansion` (5.x), `ip-address` (10.x) and `dompurify`.
824
+
825
+ Unlike batch 1 these are reachable from shipped code, not just dev/build tooling:
826
+ `fast-uri` sits under `ajv` in the API worker's MCP/schema path, `form-data` and
827
+ `uuid` are pulled in by the API, runtime and sandbox dependency trees, `js-yaml` 4.x
828
+ reaches `@runtypelabs/cli`, and `brace-expansion` 5.x reaches
829
+ `@runtypelabs/model-execution`. The `dompurify` entry drops the vulnerable 3.3.3 copy
830
+ that `posthog-js` pulled in and consolidates on 3.4.12, the version
831
+ `@runtypelabs/persona` already ships in the same tree.
832
+
833
+ No runtime source changed; this is a lockfile/root-config change. Every override
834
+ carries a deliberate upper bound, since a bare `>=X` override resolves to the newest
835
+ match across all majors. Verified: no downgrades anywhere in the lockfile.
836
+
837
+ ## 6.3.4
838
+
839
+ ### Patch Changes
840
+
841
+ - 400c1f1: Tighten the batch results header and surface failed steps at the batch level
842
+ - Batch and scheduled-run results responses gain a `summary.recordsWithFailedSteps` count. It is batch-wide rather than page-scoped and ignores the status filter, so it stays correct on a paginated batch. The batch's own `failedRecords` cannot express this: a record whose flow ran to the end counts as completed even when a step inside it failed.
843
+ - The results sheet header collapses from three rows to two. Identity (title, copyable batch ID, duration, and the new "N with failed steps" signal) shares the title row with refresh and close, so the close button no longer drifts down beside the filters; the segmented status filter and record lookup get their own row.
844
+ - The record lookup is now an `ExpandingSearch` that costs an icon until used, instead of a permanently open text input.
845
+ - Dropped the title count badge when the segmented control is present, since its "All" segment already states the count.
846
+ - The batch sheet no longer routes through `ExecutionFilterBar`, which removes a "Clear" button that was always lit: the sheet seeds a `from` date that the batch endpoint never sends, so the bar read as actively filtered when nothing was applied.
847
+
848
+ - 400c1f1: Batch results screen redesign: honest per-record status and less chrome
849
+ - Batch and scheduled-run results endpoints now include a per-record step rollup (`stepCount`, `failedStepCount`, `durationMs`) so the results list can answer "did it work end to end, and how fast" without per-record fetches.
850
+ - Batch result rows are titled by record name (never "Unknown") and show duration plus a step summary; a completed record with failed steps renders as "Completed with errors" (amber) instead of a clean green "Completed", with the failed count spelled out in text.
851
+ - The batch results sheet gets a segmented status filter with counts (All / Completed / Failed / Running) driven by the batch summary, a copyable batch ID chip instead of an ID embedded in the title, a "Duration" label once the batch finishes, no $0.00 cost display, and a pagination footer only when there is something to page.
852
+ - The record detail panel leads with the run output, auto-expands failed steps with the error inline, quiets successful steps to icon-only status, and drops badges that repeated the header.
853
+
854
+ - 93a3c5b: Hosted Google OAuth connect flow with automatic token refresh. Google Workspace moves off the paste-a-token path onto `organization_integrations`: a "Connect Google" popup flow (`POST /v1/oauth/google/start` + public callback) captures tokens server-side, and a new token broker transparently refreshes access tokens (5-minute skew, optimistic version-gated writes) for every Google tool call. `invalid_grant` surfaces as a "Needs reconnection" badge instead of silent hourly credential death. BYO OAuth client supported per connect, with the deployment's authorized redirect URI shown as a copy field in both BYO panels (served by `GET /v1/oauth/google/config` so it is always the exact string the authorize call sends); the Runtype-provided platform client additionally ships behind the `enable-google-platform-oauth-client` Flagship gate (fail-closed off in production, on in staging/dev) on top of its `GOOGLE_OAUTH_CLIENT_ID`/`GOOGLE_OAUTH_CLIENT_SECRET` env requirement, so BYO is the only path until that rollout completes. The legacy pasted-token path is removed in the same change (a census found no stored `google-workspace` credentials in any environment): `POST`/`DELETE /v1/integrations/google/credentials` now reject with a pointer to the connect flow, and Google tool failures report their real cause instead of degrading into a generic "not configured" error.
855
+
856
+ ## 6.3.3
857
+
858
+ ### Patch Changes
859
+
860
+ - 844b742: Slack setup: replace the broken manifest deep link with a copy-and-paste flow. Slack's redesigned create-app modal silently ignores the `manifest_json` URL parameter, so `createAppUrl` now points at the plain app-creation page and the setup wizard has users copy the manifest and paste it into Slack's "From a manifest" option.
861
+
862
+ ## 6.3.2
863
+
864
+ ### Patch Changes
865
+
866
+ - f0ec44b: Realign the outer agent loop to terminate on natural model yield. The loop now
867
+ continues only for mechanical reasons: the turn was cut off by the inner tool
868
+ budget (`max_tool_calls`), the tool surface expanded that turn (skill load or
869
+ `tool_search` discovery), or the turn died on a provider error (retry path,
870
+ bounded by the consecutive-error abort). A turn the model ends itself
871
+ (`end_turn`) terminates the loop with `stopReason: 'complete'` regardless of
872
+ output length, tool count, or wording, so conversational agents with
873
+ `maxTurns > 1` no longer re-run tools and answer themselves after asking the
874
+ user a question. `maxTurns` is now a pure safety cap. Removes the
875
+ `"Continue working on the task."` nudge on terminating turns and the
876
+ four-signal `detectAutoComplete` text heuristics (the text-only signal
877
+ survives only as the fallback for providers that report no stop reason).
878
+ Breaking-internal: `@runtypelabs/runtime` no longer exports
879
+ `detectAutoComplete` / `AutoCompleteSignal`; use `decideLoopContinuation` and
880
+ its types instead.
881
+
882
+ ## 6.3.1
883
+
884
+ ### Patch Changes
885
+
886
+ - d547ab3: Fix client-chat dispatch of a locked-down (tenancy-strategy) agent failing every admitted turn: the `client_conversation` record is created at session init, before any identity proof exists, so it was un-stamped and the execution's scoped record read excluded it ("Record ... not found or not accessible"). The pipeline now claims the session-authorized conversation record into the resolved tenant/end-user scope pre-engine (idempotent claim-or-match), and rejects a cross-scope conversation with a 403 `conversation_scope_mismatch` instead of ever widening the read.
887
+ - c065bb9: Restore debug-tier cost and token fidelity dropped by the unified SSE cutover: the edge translator now forwards the flow-level `totalTokensUsed` scalar onto `execution_complete` and per-tool `toolCost` onto flow-family `tool_complete`. FilteredStream still strips both from user-facing streams; debug/SDK streams see them again, fixing the dashboard live execution timeline's per-tool cost display.
888
+
889
+ ## 6.3.0
890
+
891
+ ### Minor Changes
892
+
893
+ - 20737ee: Make the 33-event `ExecutionStreamEvent` vocabulary the platform's only public
894
+ execution stream. Remove request-, Persona-version-, and Flagship-based wire
895
+ format negotiation; translate API-owned engine frames unconditionally; expose
896
+ unified events in non-streaming JSON responses; and remove the public
897
+ `FlowSSEEvent` OpenAPI component.
898
+
899
+ Update the TypeScript and Python SDKs, dashboard, examples, smoke tests, and
900
+ internal tools to consume unified streams. The TypeScript SDK keeps its stable
901
+ flow and agent callback presentation shapes by translating the unified wire
902
+ client-side, while obsolete stream-format helper calls are source-compatible
903
+ no-ops. Preserve external-agent resume handles and flow completion counts on
904
+ the unified contract.
905
+
906
+ ### Patch Changes
907
+
908
+ - 401acbd: Authored `toolMocks` on manual eval cases: a hand-written case can now carry its own `toolMocks` array without ever having been captured from a real run — `POST /eval/ensure`, `defineEval`, and the MCP eval-case tools all accept it. On a suite with `recordedToolMode: 'continue'`, `toolMocks` presence (captured or authored) is what triggers serving recorded results instead of dispatching the real tool, so an eval case can now get a `toolType: 'local'` tool past its first call (which otherwise pauses the run forever) without a captured checkpoint. `checkpoint` stays capture-only — it is execution-state (where a run was forked from), not something an author writes by hand, and `/eval/ensure` continues to reject it. Authored-mocks replay keeps the same agent (virtual-flow) target restriction as captured checkpoint cases; a flow-target suite skips the case as a gradeable failure instead of running it. The suite-run batch path (`POST /eval/suites/:id/run` above the sync case ceiling) now rejects a continue-mode case carrying `toolMocks` (`REPLAY_MODE_CASES_UNSUPPORTED_ON_BATCH`) instead of silently running it with mocks unserved; the code-mode MCP spec overlay and SDK output validation (a mock must include an explicit `output` key) round out the surface. A `ToolMock`'s `output` field is now correctly marked required in the generated OpenAPI spec and TS SDK types (a bare `z.unknown()` had rendered it as optional despite the server always requiring it, letting a caller construct an output-less mock the server then rejected with an opaque error).
909
+ - cb8cb9e: Per-case eval graders: eval cases now carry an optional `graders` array that REPLACES the suite-level graders for that case (null ⇒ use the suite graders). `POST /eval/ensure` decomposes divergent per-case `expect` arrays into a suite default plus per-case overrides instead of rejecting them with a 400; both run paths (sync `/eval/run` and the batch scoring pass) score each case against its own effective grader array, and `graderIndex` now indexes into that per-case array. The suite CRUD case schemas, `/eval/ensure` pull round-trip, and the MCP eval-case tool inputs all carry the field.
910
+ - 7f77e9f: Surface responses now embed the canonical public endpoint URL. The API computes `endpoint` server-side (via its own base-URL authority) on surface list, get, create, and update responses, and on surfaces embedded in the product GET: the URL for MCP, API, A2A, AG-UI, webhook, and chat surfaces, or null for channel-based types (Slack, email, schedule). The dashboard's snippet generators, endpoint listings, and onboarding steps now prefer `surface.endpoint` over client-side URL construction, which survives only as a fallback for cached payloads that predate the field.
911
+
912
+ ## 6.2.1
913
+
914
+ ### Patch Changes
915
+
916
+ - 68a503e: Add `orgName` to the `GET /v1/users/profile` response: the display name of the caller's active organization, resolved from the Clerk-synced organizations table. Null for personal accounts. Lets API-key and SDK consumers resolve their org identity without a Clerk session.
917
+
918
+ ## 6.2.0
919
+
920
+ ### Minor Changes
921
+
922
+ - b967727: Remove the dead product-generation sessions surface: the `/v1/products/generation/sessions` CRUD endpoints (create, list, get, update, delete, append-event) and their `product_generation_sessions` / `product_generation_session_events` tables. This surface was orphaned legacy from the March 2026 three-agent generator design and had zero consumers (dashboard, worker, CLI, SDK, or API callers); both tables held zero rows in staging and production. A drop migration removes the tables; the OpenAPI spec and generated TypeScript/Python SDK types no longer expose the routes.
923
+
924
+ ### Patch Changes
925
+
926
+ - 2c8cf42: Secrets gain a first-class `docsUrl` ("where to get this key"). New nullable `docs_url` column on secrets with https-only validation (`secretDocsUrlSchema`) at every write path (REST create/update with null-clears, intake submit, MCP `create_secret`/`update_secret` via `docs_url`), plus a hand-curated provider-docs registry (`SECRET_PROVIDER_DOCS`, 15 providers) mapping well-known key names to official console pages. Intake manifest items resolve `docsUrl` server-side with binding > secret row > registry precedence, each candidate gated by the new `isSafeDocsUrl` guard; the dashboard gates all "Get key" anchors on the same guard (hardening for pre-gate rows) and manual mode shows a registry-driven "Get key" button as the user types a known key name. FPO `setupInstructions.docsUrl` validation tightened to https-only.
927
+ - 2c8cf42: Secret intake sources accept an optional `description` so MCP agents can hand users a self-explanatory configuration link. `secretRequirementSourceSchema` gains the field; `buildConfigurationUrl` carries it (with `key`) into the intake screen query string; focused (`source=secret`) manifest items fall back to the caller-supplied description when the secret does not exist yet; the dashboard renders it on focused cards and forwards it from the URL. The MCP `get_secret_intake_manifest` / `submit_secret_intake` source and `create_secret.description` now guide agents to write descriptions that state the key's general, account-wide purpose and where to obtain the value. Secret values never travel in URLs.
928
+
929
+ ## 6.1.4
930
+
931
+ ### Patch Changes
932
+
933
+ - 843b702: Remove the `enable-agent-skills` Flagship rollout flag now that Agent Skills is fully enabled in production. The feature is unconditionally on: the admin/control-plane gate, the legacy-flow and resume-reattach skill-loading gates, the `propose_skill` execution guard, and the dashboard Skills nav/route gating are all removed. `GET /v1/users/profile` still returns `features.enableAgentSkills`, now hardcoded to `true` for wire compatibility with deployed SPA bundles and the published SDK.
934
+
935
+ ## 6.1.3
936
+
937
+ ### Patch Changes
938
+
939
+ - 1acada7: Accept inline `config.flow` flow tools at the dispatch boundary when the caller opts into the runtime execution lane (`options.useRuntimePackage: true`). The dispatch validator previously rejected any flow tool without a `flowId`/`toolId` reference, and the tools normalizer silently stripped `config.flow` — together 400-ing the rt-seam staging smoke templates and rolling back the staging deploy. Inline flows remain rejected (clean 400 with an actionable message) on the legacy lane, which cannot execute them. `flowName`-only references are accepted on the runtime lane (resolved `flowName ?? flowId` by the runtime tool executor); the SDK's mirrored `FlowToolConfig`/`RuntimeFlowToolConfig` types gained the same `flow`/`flowName` fields.
940
+ - db2cf06: Make monetary usage delivery idempotent, normalize live billing webhooks and customer notifications, and rebuild Billing and Usage around explicit collection, credit, analytics, and enforcement sources.
941
+
942
+ ## 6.1.2
943
+
944
+ ### Patch Changes
945
+
946
+ - 158223e: Launch Runtown as a disposable, standalone, staging-only Cloudflare app for navigating a real Runtype product as a third-person 3D town. A Flagship-gated dashboard command opens the experiment, which signs in with Clerk, calls the user API directly, adapts Pica's first-run tour to the product architecture, and deep-links back to canonical dashboard editors. Runtown has no production Worker, route, release dispatch, or production-access path while the experiment is being evaluated.
947
+
948
+ Add account-and-product-scoped Durable Object multiplayer with anonymous travelers, authoritative movement guards, short-lived tickets, and opt-in HRTF nearby dialogue cues that never transmit questions, generated answers, transcripts, or generated audio. Preserve the richer architecture characters, grounded streamed narration, enterable buildings, live execution traffic, quests, configuration health, and spatial voice while isolating all game code and deployment lifecycle from the dashboard.
949
+
950
+ - 3abba4d: Preserve debug step costs and inline Claude Managed agent IDs in unified SSE streams while migrating smoke coverage to the default vocabulary.
951
+
952
+ ## 6.1.1
953
+
954
+ ### Patch Changes
955
+
956
+ - 40cb003: Generated OpenAPI types for the flow export-runtime response now declare the
957
+ ADR 0014 fields (`agents` Embedded Agent Registry and `dependencyRefs`), so
958
+ typed SDK consumers get first-class access instead of reaching through
959
+ untyped extras.
960
+
961
+ ## 6.1.0
962
+
963
+ ### Minor Changes
964
+
965
+ - cfacf11: feat: record collections on every non-REST surface
966
+
967
+ Fans the `/v1/collections` REST API (Records→Collections Phase 1) out to the
968
+ remaining surfaces, closing the Phase 1 acceptance loop — create → infer →
969
+ warn → validate-existing → enforce → 422 works identically via REST, SDK, MCP,
970
+ and CLI:
971
+ - **MCP** (`@runtypelabs/mcp` + the api-internal client): seven tools —
972
+ `list_collections`, `get_collection`, `create_collection`,
973
+ `update_collection`, `delete_collection`, `infer_collection_schema`,
974
+ `validate_collection_records` — with
975
+ read-only/destructive annotations, next-step hints, and collections-domain
976
+ error recovery hints.
977
+ - **Code Mode MCP**: matching `runtype.listCollections(...)` /
978
+ `getCollection` / `createCollection` / `updateCollection` /
979
+ `deleteCollection` / `inferCollectionSchema` / `validateExistingRecords`
980
+ executor methods with overlay docs and examples.
981
+ - **SDK**: `client.collections` endpoint class
982
+ (list/get/create/update/delete/inferSchema/validateExisting) with response
983
+ types derived from the generated OpenAPI spec.
984
+ - **CLI**: `runtype collections list|get|create|update|delete|infer|validate`,
985
+ including `--schema-file` round-tripping and the infer → update adoption
986
+ flow.
987
+ - **Docs**: new user-guide page "Defining a schema" plus a collections section
988
+ in the MCP server integration guide.
989
+
990
+ The dashboard assistant deliberately excludes the new tools
991
+ (`phase2:collections`) until the Collections UI ships.
992
+
993
+ - cd1133f: Add a Slack "Add to Slack" OAuth install flow. The Slack setup wizard now
994
+ captures the bot token server-side via a real OAuth v2 handshake instead of
995
+ asking the user to hand-copy the App ID, install the app, and paste the
996
+ `xoxb-` bot token. The user pastes only the three App Credentials (Client ID,
997
+ Client Secret, Signing Secret) from one Slack page and clicks "Add to Slack".
998
+ - New authenticated `POST /v1/oauth/slack/start` returns the Slack authorize
999
+ URL, storing the client + signing secret in a short-lived (10 min),
1000
+ one-time-use, AES-256-GCM-encrypted KV state blob.
1001
+ - New public `GET /v1/oauth/slack/callback` exchanges the code, claims the
1002
+ pending integration, and posts non-secret metadata back to the dashboard
1003
+ opener. The bot token never transits the browser.
1004
+ - The generated Slack app manifest now includes `oauth_config.redirect_urls`.
1005
+ - New `integrations.startSlackOAuth` SDK method.
1006
+
1007
+ ### Patch Changes
1008
+
1009
+ - c26556c: Add record collections: customer-defined data models for Records. New `record_collections` entity (slug = record type by convention) owning an optional constrained-dialect JSON Schema, with `/v1/collections` CRUD, append-only schema version history, additive-vs-breaking evolution gating, infer-schema-from-data proposals, and validate-existing dry runs. Validation mode defaults to `off`, so registering a collection changes no existing behavior; write-seam enforcement lands in a follow-up.
1010
+ - 2c315c5: Enforce collection schema validation at every record write seam (Records→Collections Phase 1 PR 2). When a record's type matches a collection whose validationMode is `warn` or `enforce` with a non-null schema, the write's metadata is validated: `enforce` rejects with a 422 `schema_validation_failed` envelope carrying stable field-level codes (`REQUIRED_FIELD_MISSING`, `INVALID_TYPE`, `INVALID_ENUM_VALUE`, …); `warn` proceeds, surfaces `schemaWarnings` on the response, and stamps the new nullable `records.schema_valid` column. Covered seams: POST/PUT /v1/records, bulk-edit (post-merge), CSV import (per-row), the upsert-record/update-record context steps (step errorHandling contract), the builtin record agent tools, the dispatch record-create path, and the conversations routes. `recordFilter` gains a `schemaValid` top-level pseudo-field (`isTrue`/`isFalse`/`isSet`/`isNotSet`) so filters can target nonconforming or unevaluated rows. Unregistered types see zero behavior change.
1011
+ - 644e057: Generate the Slack app manifest API-side so it always carries absolute URLs. The Slack setup wizard built the manifest in the browser from the SPA's API base, which is the relative `/api` proxy path on staging/preview, so the manifest ended up with relative URLs (`/api/v1/messaging/webhooks/slack/...`) that Slack rejects (`Input must match regex pattern: ^https?:\/\/`). A new authed endpoint `POST /v1/integrations/slack/manifest` builds the manifest from the API's true public origin; its OAuth redirect URL now reuses the same helper as the OAuth start route so the manifest's `redirect_urls` and the handshake's `redirect_uri` can never drift. Also fixes two Slack schema issues (empty `suggested_prompts`, unsanitized bot `display_name`), makes the manual webhook URL shown for surfaces absolute, and derives the OAuth-callback postMessage origin from the manifest so completion no longer breaks on relative API bases.
1012
+
1013
+ ## 6.0.2
1014
+
1015
+ ### Patch Changes
1016
+
1017
+ - a948051: `POST /v1/client/resume` now accepts an optional refreshed `clientTools[]` + `clientToolsFingerprint` snapshot (same diff-only / send-once protocol as `/v1/client/chat`, including the 409 `client_tools_resend_required` handshake). The refreshed set is re-validated and re-gated against the surface's `behavior.webmcp` policy, then replaces the run's persisted clientTools so page tools registered after a mid-run navigation become callable on the next model turn — letting a WebMCP browser agent complete cross-page tasks (search → navigate → read product → add to cart) in a single user turn. Omitting both fields keeps the existing frozen-at-dispatch behavior.
1018
+
1019
+ ## 6.0.1
1020
+
1021
+ ### Patch Changes
1022
+
1023
+ - 4b4bbe9: Fix and prune the `paginate-api` flow step.
1024
+ - **`link_header` pagination now actually advances pages.** Previously the loop rebuilt the base URL every iteration and discarded the `rel="next"` URL parsed from the response `Link` header, so it either looped on page one or never followed the next link. The next URL is now threaded through the loop and fetched directly, so link-header APIs (e.g. GitHub) paginate correctly.
1025
+ - **Removed the unused `oauth2` auth type.** The `oauth2` value is dropped from the `authType` enum and the `oauth2TokenUrl` / `oauth2ClientId` / `oauth2ClientSecret` / `oauth2Scopes` fields are removed from `authConfig`. The executor never implemented OAuth2 (it only logged a warning), and zero stored flows use it.
1026
+ - **Removed the unused `entityIdPath` field.** It was never read by the executor.
1027
+ - Fixed the hidden `api-data-sync` dashboard template, which used a non-schema `pagination: { type, pageSize, maxPages }` shape; it now uses the schema fields (`paginationType`, `pageSize`, `maxPages`).
1028
+
1029
+ - 9a3dd1a: Narrow the `send-event` flow step's `provider` to PostHog only. The step never shipped a Google Analytics, Amplitude, or Segment implementation (those enum values threw at dispatch), so `provider` now accepts `'posthog'` alone; a flow that stored a removed value fails validation with an actionable message pointing at the API Call step for other analytics services. The SDK's `SendEventStepConfig.provider` type is narrowed to `'posthog'` to match. The PostHog capture request now goes through the executor's fetch seam, adding SSRF protection; that seam also defaults the Runtype User-Agent for every shared HTTP step (fetch-url, paginate-api, api-call, wait-until, send-event).
1030
+ - aab02e1: Remove dead flow-step config surface that was accepted by validation but never read by either execution engine. Each field below was verified unused in the api and runtime executors with zero stored production usage; removing them makes the schemas honest about what the platform actually does. A flow that stored one of these fields keeps running — the value was already ignored — it simply no longer appears in the schema, SDK types, or generated artifacts.
1031
+ - **`update-record.updatesTemplate`** — removed from the schema and the runtime executor branch that implemented it. Use `updates` (per-value `{{template}}` substitution) instead. Dropping it keeps update targets' field names statically knowable. A config still carrying the field now fails loudly rather than silently applying an empty patch: the shared normalizer (which runs on the API execution and validation paths) throws a `NormalizationError`, and the runtime executor (which bypasses normalization) throws its own equivalent guard.
1032
+ - **`generate-embedding` inline vector store** — removed the `vectorStore` sub-config and the `storeInRecord` flag. This step only produces an embedding vector into `outputVariable`; persisting it is the separate `store-vector` step's job (the inline store was never wired to an executor). The false "stores the embedding in a vector DB" doc comment is corrected.
1033
+ - **`pinecone` vector backend** — dropped from the `vector-search` and `store-vector` provider/destination enums (plus the now-unreachable `pineconeConfig` sub-configs and dashboard affordances). Pinecone was never implemented — it only ever threw "not yet implemented". The `vector-search` executor now throws an actionable error on an unknown provider instead of silently falling through to the pgvector path.
1034
+ - **`execute-agent.variables`** — removed from the schema and normalizer; nothing read it.
1035
+ - **`upsert-record.metadataMapping`** — removed from the schema and normalizer; nothing read it.
1036
+ - **`api-call.requestTemplate`** — removed from the schema and normalizer; it was dropped at the api-call → fetch-url seam.
1037
+ - **`retrieve-record.fields`** — removed from the schema (UI-only, never read).
1038
+ - **`fetch-url` `responseType: 'xml'`** — removed from the enum, the executor branch, and the SDK type. It only ever aliased `text` (there was never XML parsing). The schema stays strict so new authoring is rejected, but because the normalizer runs on the API execution path a legacy stored flow keeps working: `normalizeFetchUrlConfig` remaps a stored `'xml'` to `'text'` before the enum check (and the runtime executor's default branch already returns text), so immutable `flow_versions` still execute.
1039
+
1040
+ The removed SDK builder fields (`GenerateEmbeddingVectorStoreConfig`, `StoreVectorPineconeConfig`, and the corresponding `flow-builder` config properties) and the shared type aliases are dropped in the same change.
1041
+
1042
+ - ebb401b: Sibling-parity fixes for two flow step types (additive, no behavior change for existing flows):
1043
+ - **tool-call `timeout`**: the config field existed but was passed as an ignored `_timeout` parameter and never enforced. It is now enforced via `Promise.race` inside `executeToolById`. A timeout fails the step respecting its `onError` contract (throws for `fail`/`retry`, returns the continue output for `continue`). Enforcement applies only when `timeout` is set, so flows that omit it keep their prior unbounded behavior.
1044
+ - **send-stream `outputVariable` / `errorHandling` / `defaultValue`**: send-stream previously accepted only `{ message }`. It now supports an optional `outputVariable` (defaults to `"message"`, so the flows that omit it are unaffected) plus the standard `errorHandling` / `defaultValue` continue-on-error fields shared by sibling steps. The streaming orchestrator resolves the effective output key so a custom output variable still streams. The SDK `SendStreamStepConfig` exposes the new fields; the dashboard step-card UI for `outputVariable` is deferred to a follow-up (its closest sibling, send-event, also omits it).
1045
+
1046
+ ## 6.0.0
1047
+
1048
+ ### Major Changes
1049
+
1050
+ - 51ab2d1: Remove the `fetch-github` flow step from validation, execution, SDKs, generated schemas, and dashboard surfaces. The step was never surfaced in the UI, ignored half of its own config fields, and depended on an external repomix ingestor service. Use `api-call` for single GitHub API requests, `transform-data` with a container sandbox (`daytona` / `runtype-sandbox`) to clone and work on repositories, or the GitHub MCP server for agent-driven GitHub work.
1051
+ - a27eea9: Remove the non-functional `send-text` flow step from validation, SDKs, generated schemas, and dashboard surfaces.
1052
+
1053
+ ### Minor Changes
1054
+
1055
+ - 5f08625: feat: product and surface setup readiness
1056
+
1057
+ Adds a net-new readiness primitive that answers "what's left to make this product (or surface) live?" with structured, headless-first next steps. A pure `@runtypelabs/shared` core composes pending secrets with each surface's remaining install steps, and every step names the best available way to satisfy it — an MCP tool to call now, a REST call from a coding harness, or a dashboard deep link when a browser is genuinely required (OAuth, DNS, keypair).
1058
+ - `GET /v1/products/{id}/setup` and `GET /v1/products/{id}/surfaces/{surfaceId}/setup` (public API).
1059
+ - `get_product_setup` and `get_surface_setup` read-only MCP tools, plus code-mode `getProductSetup` / `getSurfaceSetup`.
1060
+
1061
+ ### Patch Changes
1062
+
1063
+ - 5cff576: Enforce full-workspace ESLint coverage for published TypeScript packages.
1064
+ - 9ab9197: Persist flow descriptions and use the generated flow request contract across the SDK and dashboard.
1065
+
1066
+ ## 5.15.0
1067
+
1068
+ ### Minor Changes
1069
+
1070
+ - 6aac451: feat: add a multi-header auth type (`type: 'headers'`) for custom MCP servers. Servers can now send an arbitrary non-empty record of legal HTTP headers, with `{{secret:NAME}}` references resolved through the scoped secret store and registered with the per-execution leak guard. Support spans API discovery and saved-server CRUD, runtime discovery and dispatch, FPO/agent/SDK contracts, and runtime export. The dashboard exposes the uncommon configuration through a progressively disclosed **Advanced authentication** editor while keeping stored values encrypted and opaque until the user supplies a complete replacement set.
1071
+
1072
+ ## 5.14.3
1073
+
1074
+ ### Patch Changes
1075
+
1076
+ - 29ce53f: Finish hiding pre-GA Runtype Apps from public SDK/docs/CLI surfaces and keep app-only client sessions fail-closed behind the feature flag.
1077
+ - 5d828d9: Use Schematic-accepted platform spend as the canonical billing-month spend display across Billing and Usage.
1078
+
1079
+ ## 5.14.2
1080
+
1081
+ ### Patch Changes
1082
+
1083
+ - b00dea7: Hide pre-GA Runtype Apps surfaces from public tool catalogs, docs, schemas, skills indexes, SDK generation, and unauthenticated CLI help; keep runtime/tool access fail-closed behind the feature flag.
1084
+
1085
+ ## 5.14.1
1086
+
1087
+ ### Patch Changes
1088
+
1089
+ - 0d4b1b8: Default Usage analytics to the current billing month and add spend analytics period presets.
1090
+
1091
+ ## 5.14.0
1092
+
1093
+ ### Minor Changes
1094
+
1095
+ - facb5d9: Add Massive Web Render as a web-scraping provider alongside Firecrawl. Includes the `builtin:massive` agent tool, a `massive` fetch method on the fetch-url flow step (format, geotargeting, delay, and cache options), a `PLATFORM_MASSIVE_KEY` platform key with BYOK support via Settings > Integrations, cost tracking (`massive:render`), and SDK/dashboard surfaces. The whole feature ships behind the `enable-massive-web-render` Flagship flag (fail-closed in production): tool execution, the fetch-url step branch, built-in tool listings, and the dashboard surfaces (via `features.enableMassiveWebRender` on the user profile) all gate on it.
1096
+
1097
+ ## 5.13.0
1098
+
1099
+ ### Minor Changes
1100
+
1101
+ - 89dbb72: FPO expressiveness: evals, skills, and Claude Managed agents are now first-class in the Full Product Object schema.
1102
+ - New optional top-level `evals[]` section (suites with graders, cases, recorded-tool replay config) targeting a capability; assembly materializes suites + cases with `origin: 'fpo'`, and ensure-fpo converges suite-level fields while preserving user-added (non-FPO-origin) cases.
1103
+ - New optional top-level `skills[]` section (content, trust level, capability refs, inline tools, `bindTo` agent capabilities); materialized via the skill service and bound to agent-backed capabilities. `mcpServers` is rejected at the schema per the v1 write invariant.
1104
+ - `agentClaudeManagedConfigSchema` widened to the canonical surface: `setupMode` ('create' | 'connect'), `mcpServers`, `customTools` (materialized into `agent_capabilities` with FPO-local capability ids rewritten to real product capability ids), and `anthropicAgentVersion` coerced to number (legacy string input still accepted). ensure-fpo no longer rejects inline `claude_managed` / `external` agents.
1105
+ - `pull-fpo` now reconstructs `secrets` (declarations from product secret bindings), `schedules`, `evals` (shape-compatible cases only, with skip warnings for captured checkpoint/toolMock cases), `skills` (via agent skill bindings), and Claude Managed agent configs. Records remain unreconstructed (account-scoped, not product-attributable) with an explicit warning.
1106
+ - Mirror-drift fixes: public FPO types gain the `ag-ui` / `hosted-page` / `chrome_extension` surface types; FPO flow steps accept top-level `when`; the flat agent schema's error handling gains `message` fallbacks + `triggers` and its runtime-tool type enum gains `advisor` / `search`, matching v2.
1107
+ - `computeFpoContentHash` covers the new sections with absent-key omission, so hashes of existing FPOs without `evals`/`skills` are unchanged (all three lockstep copies updated).
1108
+
1109
+ ## 5.12.1
1110
+
1111
+ ### Patch Changes
1112
+
1113
+ - b2badd2: Retire the `usage-based-pricing` feature flag now that the rollout is complete across all environments. The trial-expiry and auto-topup-failed spend-gate branches, the card-entry slow-mode 429 messaging (`/settings/billing/portal` CTA), the `X-Runtype-Billing-Status` advisory header, and the billing lifecycle emails are now unconditional. The `features.usageBasedPricing` field is removed from `GET /v1/users/profile`, and the dashboard renders the card-centric billing copy unconditionally.
1114
+
1115
+ ## 5.12.0
1116
+
1117
+ ### Minor Changes
1118
+
1119
+ - 9a527cd: Evals E6: feedback-triggered eval proposals and judge calibration.
1120
+
1121
+ Real end-user feedback now feeds the eval proposal queue automatically: a thumbs-down, NPS of 6 or below, or CSAT of 2 or below on a Flow conversation proposes a regression case forked just before the poorly rated reply (a left comment pre-fills the expected answer), and a thumbs-up or NPS of 9 or above proposes a golden case whose endorsed reply becomes the expectation. The on-write hook is non-blocking, dedups per conversation moment, targets the Flow's default (or only) suite, and writes through the governed E5 proposal seam so the per-suite daily cap and review-required governance apply unchanged.
1122
+
1123
+ Judge calibration lands the humanVerdict loop: POST /eval/scores/{scoreId}/review records a lightweight 👍/👎 review of an AI-grader verdict (run-results outcomes now carry `scoreId` and `humanVerdict`), and the suite detail exposes `judgeAgreement`, surfaced on the suite page as a plain "Agrees with you N of M times" trust display. Parity: SDK `evals.reviewScore` + `PersistedGraderOutcome`, MCP `review_eval_score`, code-mode `reviewEvalScore`, and dashboard 👍/👎 buttons with a tune-your-criteria hint on disagreement.
1124
+
1125
+ ### Patch Changes
1126
+
1127
+ - 2821ddb: Evals E5: eval case proposal queue, definition-seeded generation, and the coverage meter. New `eval_case_proposals` table plus REST endpoints (`GET /v1/eval/suites/{id}/proposals`, accept/reject, `POST /v1/eval/suites/{id}/generate-cases`, `GET /v1/eval/suites/{id}/coverage`). Machine-generated cases never enter a suite directly: generation fans the target definition out over an explicit diversity matrix (category × persona), filters candidates for gradeability, and writes proposals that require an affirmative accept (verbatim or edited, with provenance backlink and audit trail — same governance stance as skill proposals). The coverage meter reports which target tools and instruction clauses the suite's cases and graders already exercise (structural tool inventory + a cached, definition-hash-invalidated LLM clause pass), with per-gap targeted generation. Ships with the suite-page review queue and coverage UI, SDK methods (`suites.listProposals/acceptProposal/rejectProposal/generateCases/getCoverage`), and MCP tools (`list_eval_proposals`, `resolve_eval_proposal`, `generate_eval_cases`, `get_eval_coverage`).
1128
+ - ec895c9: Prompt-caching auto-demotion loop plus two caching-ops levers.
1129
+ - **Auto-demotion**: workloads that persistently pay the Anthropic 1.25× cache-write premium without landing reads (≥20 stamped requests in a 24h window with a <5% read rate) are automatically suppressed from stamping for 24h via a KV entry consulted upstream of `computeCachePlan`. Counters are folded inline at the usage-emit site (no cron), fail-open everywhere, keyed by tenant + agent/flow@version, with a structured `prompt-cache demotion` log charted on the Grafana prompt-cache dashboard. `CACHE_AUTO_DEMOTION_DISABLED='true'` is the ops kill switch.
1130
+ - **Gateway cache-pinning kill switch**: `CF_GATEWAY_CACHE_PINNING_DISABLED='true'` restores Vercel-gateway Anthropic failover for cache-bearing requests without turning off caching.
1131
+ - **Per-batch canary opt-out**: `options.skipCacheWarm` on batch submission skips the serial prompt-cache warm canary and fans out immediately (REST, SDK `BatchOptions`, MCP `submit_batch`).
1132
+
1133
+ ## 5.11.0
1134
+
1135
+ ### Minor Changes
1136
+
1137
+ - 8f998e7: Add optional offset pagination to `GET /v1/client-tokens` (`limit` 1-200 + `offset`), mirroring the `/client-tokens/:id/conversations` sibling. When `limit` is provided the response includes a `pagination` object (`limit`, `offset`, `hasMore`); when omitted the full list and legacy envelope are returned unchanged, so existing callers keep working. The listing order gains a deterministic `id` tiebreak. The params are threaded through the SDK `clientTokens.list()`, the MCP `list_client_tokens` tool, and the Code Mode `listClientTokens(params)` method.
1138
+ - 9a47ef1: Prompt caching slice 3 — determinism guards + implicit-provider hygiene + escape hatch.
1139
+ - **Determinism (prompt-cache hit rate):** freeze `_execution.timestamp` / `_now` once per execution instead of per loop iteration (both the api agent loop and the runtime port), so temporal system-prompt bytes stay identical across turns and don't bust the cache prefix. Weighted provider selection (`selectProviderByWeight`) is now sticky per conversation — a deterministic hash of `conversationId ?? executionId` replaces `Math.random()`, so a multi-provider family never flips providers mid-conversation onto a cold cache.
1140
+ - **Implicit / routing-key providers:** OpenAI-family routes now carry a platform-derived `promptCacheKey` (`providerOptions.openai.promptCacheKey`) sharded per conversation — a cross-tenant routing-namespace hygiene fix and a hit-rate improvement; scope keys are derived from platform-owned identifiers only, never caller-supplied on platform keys. DeepSeek's `prompt_cache_hit_tokens` / `prompt_cache_miss_tokens` usage shape is now normalized by `normalizeCacheUsage`. No new family's billing discount is flipped on (hygiene only).
1141
+ - **Escape hatch:** `options.cache: false` on dispatch / execute-agent (and the `.strict()` MCP tool mirrors) disables prompt caching per request, overriding the rollout flag — for eval/batch fan-outs and workloads that must never share provider cache state.
1142
+ - New byte-prefix parity corpus (`prompt-cache-parity/`) driven by both prompt-executor ports pins the actual product outcome (hit rate): turn N is a strict byte-prefix of turn N+1, boundary sets are identical across executors, and no stray timestamp bytes appear when the template has no temporal references.
1143
+
1144
+ ### Patch Changes
1145
+
1146
+ - e6b97e1: Regenerated OpenAPI types for the hosted end-user identity endpoints (`/v1/end-user-auth/*`): request/response schemas and the `rt_eu_`-prefixed access-token description.
1147
+ - e88000e: Trace→eval capture UI (E3 UI increment): "Add to eval" from a log trace, fork picker, replayability badge, and the next-step divergence diff.
1148
+ - **Add to eval** — an agent execution's log detail panel gains an "Add to eval" action that forks the run into a `saved_from_run` test case. The dialog picks an agent-target eval (or creates the agent's default one), lets the user choose which recorded action becomes the graded next step (fork picker), and reports whether the case is fully replayable after saving.
1149
+ - **Capture preview endpoint** — new `GET /v1/eval/executions/{executionId}/capture-preview` dry-runs the capture (same ownership-scoped reader and message math as the write path) and returns the recorded actions plus the fork index each maps to, so the fork picker never re-derives the seed math client-side.
1150
+ - **Replayability badge** — captured cases in the suite view show a "Replayable" / "Partial replay" badge derived from whether any recorded tool output was truncated at capture.
1151
+ - **Divergence diff** — the run-results page shows, for a captured case run in next-step mode, the recorded next action beside the new run's next action ("used to call `lookup_account`, now calls `issue_refund`"), before the grader chips. This is backed by a new persisted `eval_case_scores.next_step_tool_calls` column (a migration) written by the next-step run path and read back through `GET /v1/eval/runs/{runId}/scores`; the `NextStepToolCall` shape is added to `@runtypelabs/shared` and surfaced on the SDK's `EvalRunScores` cases.
1152
+
1153
+ - 3ac490c: Evals E4 — recorded-tool replay ("continue past the first action"). A new
1154
+ `RecordedToolExecutor` (runtime) serves a captured case's `toolMocks` by tool
1155
+ name and never performs I/O, so a replayed checkpoint case can run past its
1156
+ first action to grade what the agent does after a tool result comes back. The
1157
+ match/policy/error-shaping logic lives in one shared pure core
1158
+ (`resolveRecordedToolOutcome`) consumed by both the runtime executor and the
1159
+ api eval-run chokepoint, pinned by a cross-import parity suite.
1160
+
1161
+ Adds a per-suite replay config on `eval_suites`: `recordedToolMode`
1162
+ (`next_step` default | `continue`) and `recordedToolUnmatchedPolicy` (`fail`
1163
+ default | `stub`), surfaced through the suite create/update/ensure REST routes,
1164
+ the SDK `defineEval`, and a dashboard "Replay mode" control. Editing a mock's
1165
+ `output` turns any recorded case into a counterfactual for free. The
1166
+ recorded-tool config is included in the eval content hash only when non-default,
1167
+ so existing (default) suites keep their hashes.
1168
+
1169
+ - d7780b7: Prompt caching slice 5 — read-only observability + author advisory + breakpoint-budget tail.
1170
+
1171
+ `@runtypelabs/shared`: add the non-blocking `CACHE_VOLATILE_SYSTEM_PROMPT` flow-validation advisory (a `recommendation`) — a prompt step whose system prompt embeds a per-run temporal variable (`{{_now}}`, `{{_execution.*}}`, `{{_schedule.*}}`) re-renders its cacheable prefix every run, so automatic prompt caching can't land a cross-request hit; the advisory names the offending roots and suggests moving them into the user prompt. Its volatile-root rule mirrors the cache planner in `@runtypelabs/model-execution` (duplicated because `shared` cannot import that package). Adds a nine-turn byte-prefix parity fixture exercising the new reserve breakpoint.
1172
+
1173
+ `@runtypelabs/model-execution`: add the reserve breakpoint (budget #4) to Anthropic-family cache stamping — when a within-turn tool loop grows the gap between the system boundary and the moving last-message boundary past 15 content blocks, one intermediate history breakpoint is added at the block-count midpoint so neither gap exceeds Anthropic's cache lookback. Total stays within the 4-breakpoint budget.
1174
+
1175
+ `@runtypelabs/api`: surface prompt-cache read/write token counts (`cacheReadTokens` / `cacheWriteTokens`, from `flow_step_results`) in the `GET /flows/{id}/step-results` response, and add a debug-only `cache` block (`{ strategy, boundaries, prefixTokens, read, write }`) to prompt `step_complete` telemetry under `debugMode`.
1176
+
1177
+ `@runtypelabs/dashboard-spa`: render cache read/write token rows in the step-detail view.
1178
+
1179
+ `@runtypelabs/sdk`: regenerated OpenAPI types for the new step-result fields.
1180
+
1181
+ ## 5.10.0
1182
+
1183
+ ### Minor Changes
1184
+
1185
+ - 36d84c1: feat(evals): criteria decomposition for AI graders (E2 slice 3). New `POST /eval/graders/decompose` splits one plain-language AI-grader criterion carrying several obligations into focused, independently judgeable sub-checks; each accepted sub-check becomes a plain `kind: 'ai'` grader row, indistinguishable at scoring time from a hand-typed one. SDK: `Runtype.evals.decomposeCriteria(criteria)`. The decomposer is a versioned platform prompt (`@runtypelabs/shared` `eval-criteria-decomposition.ts`) sharing the judge flow's model pair and metering path; a failed decomposition surfaces as a visible error, never a silent no-op.
1186
+ - e769fbf: Add trace→eval capture (evals Extension 2, increment 1): `POST /eval/suites/:id/cases/from-execution` forks a real agent run into a test case — it freezes the run's conversation history up to a fork point, attaches every recorded tool result as an editable mock, and saves the case with origin `saved_from_run` (the first writer for that origin). Capture is a copy, never a replay: the endpoint reads persisted rows and writes a case row; it executes nothing. The next-step run mode and the recorded-tool executor are later increments.
1187
+
1188
+ New additive JSONB keys inside `eval_cases.input` (no migration): `checkpoint` (`{ sourceExecutionId?, sourceConversationId?, forkMessageIndex }`) and `toolMocks` (`Array<{ toolName, input?, output, isError?, sourceToolExecutionId?, truncated? }>`), with `caseCheckpointSchema` / `toolMockSchema` in `@runtypelabs/shared`. A mock captured from a failed call is stamped `isError: true` so a future replay serves it as a failure. The SDK gains `Runtype.evals.suites.addCaseFromExecution(...)`.
1189
+
1190
+ Per a tool-output fidelity audit, capture sources the full-fidelity persisted `tool_executions.output_result` (uncapped) rather than the R2 log-trace tree (which truncates tool outputs at emit), runs its own secret scrub over the reconstructed history + mocks (`input_messages` is raw at rest), and caps each mock output — a truncated capture marks the case non-fully-replayable via the response's `replayable` flag.
1191
+
1192
+ The captured case shape (`checkpoint`/`toolMocks` and tool-call/tool-result message turns) round-trips through `addCases`/`updateCase` without a validation error. A captured case is a next-step replay case; because that run mode does not exist yet, the synchronous run path skips such a case with a clear gradeable failure instead of feeding its frozen history to dispatch (the batch path already rejects `messages`-input cases). The endpoint is also exposed through the Code-Mode MCP as `captureEvalCaseFromExecution`.
1193
+
1194
+ ### Patch Changes
1195
+
1196
+ - 5d07273: Consolidate the duplicated content-hash primitives (`isPlainObject`, canonical `normalizeValue`, and the SHA-256 hex encoder) used by `products.ensure`, `tools.ensure`, `skills.ensure`, `surfaces.ensure`, and `products.ensureFpo` into a single internal `content-hash.ts` module. No behavior change — content hashes for all `*.ensure()` surfaces are byte-identical before and after. `flows-ensure.ts` and `evals-ensure.ts` keep their own local normalizers (they intentionally preserve `null` values, unlike the canonical normalizer) but now share the same `isPlainObject` and hex-encoding helpers.
1197
+
1198
+ ## 5.9.0
1199
+
1200
+ ### Minor Changes
1201
+
1202
+ - e85c67e: Custom judge flows for AI graders (E2, `judgeFlowId`): an AI grader can now name one of the evaluating account's own flows as its judge instead of the platform judge prompt, via `judge(criteria, { judgeFlowId })` in the SDK or `judgeFlowId` on the grader config. The flow is executed through the normal flow pipeline in the customer's own account (so judge spend bills them and is metered once, by the pipeline) under a fixed contract: it receives the four platform judge variables (`criteria`, `caseName`, `expected`, `transcript`) as inputs, and its final output must be the platform verdict JSON (`reasoning` / `verdict` / `evidence`), parsed by the same validator as the platform judge so the verdict contract never varies by judge implementation. Contract validation is run-time: a miswired judge flow (missing, inaccessible, failing, or returning malformed output) resolves to an `insufficient_evidence` outcome with a visible grader error naming the problem, so it can neither fail nor silently pass cases. `judgePromptVersion` is not stamped into these scores' grader snapshots (the platform prompt did not produce them); the snapshot's `judgeFlowId` is the provenance.
1203
+ - f5ad9c0: Trajectory judge core (E1): the AI grader now evaluates the whole run, not just the final output. The judge is the new Runtype-owned "Runtype Eval Judge" system flow (config as code in `@runtypelabs/shared`, converged per environment via `scripts/ensure-eval-judge-flow.ts` like the Flow Generator): the builder's criteria is embedded as a variable inside a hand-crafted eval-expert system prompt, graded by `gemini-3.5-flash` with a cross-provider `nemotron-3-ultra-550b-a55b` fallback, and rendered in-process by the grader executor so judge spend bills the evaluating customer. The judge receives an indexed transcript of the run's steps and tool calls (with inputs/outputs, truncated and capped) and returns a binary verdict (`pass` / `fail` / `insufficient_evidence`) with critique-before-verdict reasoning and clickable evidence citations, instead of a 1-5 score. `insufficient_evidence` renders as a warning and never fails a case. Existing `threshold`/`.atLeast()` SDK semantics are preserved by mapping pass/fail to 5/1 internally, and the judge-prompt version is stamped into each score's `grader_snapshot` so historical scores stay interpretable. The batch scoring path now threads tool inputs/outputs from `tool_executions` into the grading trace, `eval_case_scores` gains `verdict`/`evidence` columns, and the run-results page shows insufficient-evidence badges plus evidence chips that deep-link into the case's trace section.
1204
+
1205
+ ### Patch Changes
1206
+
1207
+ - ae6f98e: Degrade gracefully when R2 SQL fails on the logs read path. When the historical (R2 SQL) leg of a merged GET /v1/logs or /v1/logs/stats query fails (e.g. the Iceberg manifest scan limit, error 40020), the request now serves hot-tier (recent) entries and counts with a new optional `degraded: true` field on the response data instead of failing the whole request with a 500. Degraded stats responses are never cached. The dashboard logs page shows a "historical logs temporarily unavailable" banner when the flag is set.
1208
+
1209
+ ## 5.8.1
1210
+
1211
+ ### Patch Changes
1212
+
1213
+ - 9087295: Add abuse limits to the Runtype Apps records data plane (`/v1/client/records`).
1214
+ - **Per-app record cap**: creates are blocked once an app holds 10,000 records
1215
+ across all its namespaces (counted by the per-app storage-type prefix within
1216
+ the owner scope), returning `403` with a hint. This bounds unmetered writes
1217
+ from the anonymous, freely-mintable data plane into the owner's `records`
1218
+ table.
1219
+ - **Write rate limiting** (`POST`/`PUT`/`DELETE`): a native Cloudflare Workers
1220
+ rate-limit binding (`RATE_LIMIT_APP_DATA`) keyed per app (the real backstop)
1221
+ and per session, 100 writes / 10s. Rejections return `429` with `Retry-After`.
1222
+ Fails open (no limiting) when the binding is unconfigured so the data plane
1223
+ never breaks.
1224
+ - Documents both limits in the deploying-apps guide and the `build-runtype-app`
1225
+ skill; adds the `429` response to the write routes in the OpenAPI spec.
1226
+
1227
+ - 7007359: Add optional optimistic concurrency to `PUT /v1/client/records/:id` (the Runtype
1228
+ Apps data plane) so concurrent anonymous sessions stop silently clobbering each
1229
+ other. The flagship use case for apps (collaborative surfaces like a shared retro
1230
+ board) is exactly concurrent writers, and PUT is a full metadata replace, so the
1231
+ default last-write-wins behavior loses updates.
1232
+ - New optional request field `expectedUpdatedAt` (the `updatedAt` from the
1233
+ caller's last read). When provided, the update is a compare-and-set: the
1234
+ equality is folded into the UPDATE's `WHERE` clause (closing the TOCTOU race),
1235
+ with a fresh pre-check for a friendly error. On a mismatch the API returns
1236
+ `409` with the **current** record in the body (`{ error, hint, record }`) so
1237
+ the client can rebase and retry.
1238
+ - The comparison is millisecond-precision-robust: the `records.updated_at`
1239
+ Postgres `timestamp` stores microseconds, but the token the caller holds is a
1240
+ millisecond ISO string (`Date.toISOString()`), so both the JS pre-check and the
1241
+ SQL guard (`date_trunc('milliseconds', updated_at)`) compare on the
1242
+ ms-truncated value.
1243
+ - Omitting `expectedUpdatedAt` preserves the existing last-write-wins behavior
1244
+ exactly — fully backward compatible.
1245
+
1246
+ Also updates the `build-runtype-app` agent skill and the deploying-apps developer
1247
+ guide with the new field.
1248
+
1249
+ - ee51039: feat(evals): "Save as test case" moment (Beginner-First Evals §6.1)
1250
+
1251
+ After a test run completes in the RunFlowSheet Test tab, a "Save as test case"
1252
+ button captures the run's input and output into a pre-filled, editable eval
1253
+ case dialog, targeting the flow's default eval suite (auto-created on first
1254
+ save, named "<Flow name> eval"). Saved cases are tagged `origin: 'saved_from_run'`.
1255
+ - `POST /v1/eval/suites` and `POST /v1/eval/suites/{id}/cases` accept an
1256
+ optional, server-validated `origin` field on each case (`manual` |
1257
+ `saved_from_run` | `generated` | `imported` | `fpo`), previously hardcoded
1258
+ to `'manual'` on every write path.
1259
+ - Adds the missing `createEvalSuite` method to the dashboard's browser API
1260
+ client and an `ensureDefaultEvalSuiteForFlow` helper (find-or-create the
1261
+ flow's default suite).
1262
+ - `EvalCaseDialog` gains `initialValues`/`origin` props so the save-as-test-case
1263
+ flow can reuse its existing input/expected shaping instead of a new form.
1264
+
1265
+ - db60b0c: Score-over-time chart on the eval suite page, a Score column in the eval comparison view for suite-linked runs, and a server-side `evalSuiteId` filter on `GET /v1/eval/batches` (the suite page's run history no longer truncates to a client-side slice of the org-wide runs list). `GET /v1/eval/group/{groupId}` batches now include `evalSuiteId` and `suiteScore`.
1266
+ - 40874d8: Add pagination to the Evals page's Runs tab and to the suite detail page's
1267
+ runs list. Both previously fetched a single `limit: 100` page and rendered it
1268
+ with client-side search only, so an active org's older runs (or a suite past
1269
+ its first 100 org-wide runs) were silently invisible with no way to page.
1270
+ `GET /v1/eval/batches` is offset-based, so the pagination footer paginates by
1271
+ offset (50/page on the Runs tab, 25/page on the suite page). The endpoint now
1272
+ returns `hasMore` (matching the convention already used by `/v1/flows`,
1273
+ `/v1/client/conversations`, `/v1/schedules`, …) instead of the client
1274
+ inferring it from page fullness. Also drops the dead cursor plumbing in
1275
+ `useEvalBatches` / `listEvalBatches` (the API never returned
1276
+ `cursor`/`nextCursor`/`prevCursor`), and fixes two follow-on bugs found in
1277
+ review: the suite detail page's pagination footer stayed hidden when a
1278
+ full-page offset landed on an empty page (stranding the user with no way
1279
+ back), and running a suite from the Evals tab while `runsOffset` was
1280
+ non-zero refetched at the stale offset instead of surfacing the new run on
1281
+ page 1.
1282
+ - 41b296a: Add the Runtype Apps records data plane (`/v1/client/records`). A deployed
1283
+ app's browser JS can now persist data using its origin-locked client token,
1284
+ governed entirely by the manifest's `data[]` namespace grants:
1285
+ - `GET /v1/client/records?namespace=…` (cursor-paginated list), `POST /v1/client/records`,
1286
+ `GET/PUT/DELETE /v1/client/records/:id` — all session-authed with the session
1287
+ from `POST /v1/client/init`.
1288
+ - Authorization is manifest-driven and evaluated per request: the session's
1289
+ client token resolves to its app, and the requested namespace must appear in
1290
+ the **active** version's `manifest.data[]`. Mutating verbs require
1291
+ `read-write`; an un-granted namespace is never accessible (out-of-namespace
1292
+ records 404 to avoid leaking existence).
1293
+ - Rows live in the existing `records` table (`type` = namespace) under the app
1294
+ owner's tenant, stamped with server-controlled `appId` / `versionId` /
1295
+ `clientSessionId` provenance (reserved `_app` metadata key, stripped from
1296
+ caller input and never echoed). Metadata is capped at 64 KB.
1297
+ - `POST /v1/client/init` now mints a session for data-only app tokens (a
1298
+ manifest that declares record namespaces but no flows/agents). The init
1299
+ response carries an `app` object instead of `flow` for these sessions; `flow`
1300
+ is now optional.
1301
+
1302
+ Also updates the `build-runtype-app` agent skill and the deploying-apps
1303
+ developer/user guides with the data API and an end-to-end example.
1304
+
1305
+ ## 5.8.0
1306
+
1307
+ ### Minor Changes
1308
+
1309
+ - 4c17349: Eval score persistence (Beginner-First Evals Phase 1, PR-A): grader scores now have a durable home.
1310
+ - **Saved-suite runs of `POST /v1/eval/run` persist by default**: the run gets a `batchExecutions` anchor row (`executionType='eval'`, `evalSuiteId` set) and one `eval_case_scores` row per (case, grader) — the data source for run history, baselines, and score-over-time. Pass `virtual: true` to keep a run ephemeral; inline `definition` runs stay ephemeral always. The response (and SDK `RunEvalResult`) gains `runId`.
1311
+ - **Suite-linked batch runs are scored at completion**: `completeBatch()` now grades a run with `batchExecutions.evalSuiteId` set from its persisted step results — including trace-grader coverage rebuilt from `flowStepResults` + `tool_executions` (closing the deferred batch-trace gap). Idempotent and non-fatal; dormant until a producer links batches to suites.
1312
+ - **New read endpoint `GET /v1/eval/runs/{batchId}/scores`** returning per-case grader outcomes in the same shape as the sync run response.
1313
+ - **Four new AI grader presets**: Stays on task, Same language, Refuses when it should, No personal data (mirrored in the SDK `judges.*` builders).
1314
+ - CLI `runtype eval run` prints the persisted run id.
1315
+
1316
+ - f89cb41: Eval suite CRUD + suite runs + Evals index restructure (Beginner-First Evals Phase 1, PR-B).
1317
+ - **New `/v1/eval/suites` REST family**: create/list/get/update/delete suites, add/edit/delete test cases, and `POST /v1/eval/suites/{id}/run`. List/get include case counts and each suite's latest run + score. Suites within the synchronous case limit run inline and return the scored result; larger suites queue a durable run whose grader scoring fires in `completeBatch()` (activating the previously dormant batch scoring pass) via inline records stamped with `_evalCaseId`.
1318
+ - **Two-writers provenance**: Definition edits (name, description, graders) from the dashboard/API stamp `lastModifiedSource` and a graders change invalidates the config-as-code `contentHash` so the next `ensure` re-converges; case mutations deliberately leave provenance untouched (cases are server-authoritative data and survive unchanged-probe deploys).
1319
+ - **`GET /v1/eval/batches`** gains an `origin` filter (`suite` | `adhoc`) and returns `evalSuiteId`, `requestSource`, and a `suiteScore` summary per run.
1320
+ - **Dashboard `/evals` restructure**: the index is now Evals (suites) as the primary tab — name, target, case count, latest score, last run, Run/Delete actions, and a "Managed in code" badge for ensure-converged suites — with Runs as a secondary tab carrying an origin badge + filter, a Score column for suite runs, and "Ad-hoc comparison" labeling for suite-less runs. "Compare configs" is an action on run groups.
1321
+ - **SDK**: `client.evals.suites.*` namespace (create/list/get/update/delete/run/addCases/updateCase/deleteCase) plus a generic `patch` helper on `RuntypeClient`.
1322
+ - **Code-Mode MCP**: `createEvalSuite`, `listEvalSuites`, `getEvalSuite`, `updateEvalSuite`, `deleteEvalSuite`, `runEvalSuite`, `addEvalCases`, `updateEvalCase`, `deleteEvalCase`.
1323
+
1324
+ - fa6d18a: feat(flow-steps): add `get-record` and `list-records` step types, deprecate `retrieve-record`
1325
+
1326
+ `retrieve-record` derives its return shape (single object vs. array) from the lookup method, which is unintuitive and a common source of `{{var.field}}` resolving to `undefined`. Cardinality is now a property of the step **type**: `get-record` always returns a single record object (fails if no match), `list-records` always returns an array (newest-first, `limit` default 50 / max 1000, `onEmpty: 'succeed' | 'fail'`).
1327
+ - **Engine (api + runtime)**: `retrieve-record` is resolved to its successor by a single pure alias (`resolveRetrieveRecordAlias`) at the normalizer seam, so both legacy executors are removed and saved / published / inline flows execute through the new `get-record` / `list-records` executors with behavior preserved (query mode → `list-records` with `limit: 1000` + `onEmpty: 'fail'`; id mode → `get-record`). SSE step events emit the resolved type for aliased steps.
1328
+ - **Validation**: `validate_flow` emits a non-blocking `DEPRECATED_STEP_TYPE` warning for `retrieve-record` naming the successor its config resolves to, and the array/object-access warning code was renamed `RETRIEVE_RECORD_ARRAY_OBJECT_ACCESS` → `LIST_RECORDS_OBJECT_ACCESS`.
1329
+ - **MCP**: `create_flow` / `update_flow` / `validate_flow` step-config guidance now documents both new types and marks `retrieve-record` as deprecated.
1330
+ - **Client SDK**: `FlowBuilder`/`RuntypeFlowBuilder` gain `getRecord()` and `listRecords()` methods (plus `GetRecordStepConfig` / `ListRecordsStepConfig` types); `retrieveRecord()` is marked `@deprecated` but continues to work unchanged.
1331
+ - **react-flow**: `get-record` and `list-records` are added to `FlowStepType` with display labels ("Get record" / "List records").
1332
+
1333
+ `retrieve-record` is still accepted everywhere for backward compatibility — this is additive, no breaking changes.
1334
+
1335
+ ### Patch Changes
1336
+
1337
+ - 1422e92: Add fire-and-forget dispatch for Claude Managed agents via the RFC 7240
1338
+ `Prefer: respond-async` request header. On the durable lane (a saved agent
1339
+ invoked with a `conversationId`), the turn runs headlessly in the session DO
1340
+ with full durability (seq log + `session_turns` row + watchdog alarm) and the
1341
+ request returns `202 { executionId, status, conversationId }` plus a
1342
+ `Preference-Applied: respond-async` header instead of streaming. Clients
1343
+ reconnect via `GET /agents/:id/executions/:executionId/events` or poll
1344
+ `GET /agents/:id/runs`. Requesting async on the in-request/virtual lane (no
1345
+ durable owner to outlive the request) returns `400 ASYNC_REQUIRES_DURABLE_LANE`.
1346
+ Honored on both `/v1/agents/:id/execute` and saved-agent `/v1/dispatch`. The
1347
+ 202 response + `AsyncDispatchHandle` schema are documented in the OpenAPI spec
1348
+ and flow through to the generated TypeScript and Python SDKs.
1349
+ - d0df6a8: Durable Claude Managed sessions: two reliability fixes for the session DO.
1350
+ 1. Watchdog re-arm on settle: `runTurn`'s finally now mirrors `alarm()`'s
1351
+ guard — it re-arms the watchdog when any durable `running` turn row remains
1352
+ (an orphan stranded by a mid-turn DO eviction) instead of unconditionally
1353
+ disarming. Previously a new turn settling inside the watchdog window
1354
+ deleted the alarm out from under the orphaned turn, stranding it `running`
1355
+ forever with no interrupted run row and no terminal frame.
1356
+ 2. Sub-frame-granular reconnect cursor: the legacy-to-unified translation is
1357
+ 1-to-many (one durable log row can fan out into several unified events), and
1358
+ every sub-frame previously carried the same row seq as its SSE `id:`, so a
1359
+ mid-row disconnect skipped the rest of the row on reconnect (worst case the
1360
+ terminal `execution_complete`). Intermediate sub-frames now carry a
1361
+ composite `<rowSeq>.<subIndex>` id and the row's final sub-frame keeps the
1362
+ plain row seq, so a reconnect with the last seen id (passed verbatim as
1363
+ `?after=`) resumes with exactly the not-yet-applied sub-frames. Plain-seq
1364
+ cursors keep their existing semantics; the id stays opaque to Persona.
1365
+
1366
+ - de60a30: End-user credential registration (multi-tenancy V1, inc 5) — connect leg. Adds
1367
+ `POST /v1/end-user-integrations/{provider}/connect`: an end user attaches their
1368
+ own third-party account (GitHub first) so a Runtype agent can act on their behalf
1369
+ later.
1370
+ - **Auth model:** builder-authenticated (api-key / clerk, the same credential
1371
+ that consumes the resulting secret via builder-mediated dispatch). The
1372
+ end-user identity comes ONLY from a verified Identity-Exchange proof
1373
+ (`verifyExchangeProof`), never a body-asserted id. Registration admits
1374
+ `verified` end-user assurance ONLY.
1375
+ - **Flow:** verify the proof against the builder-owner's configured identity
1376
+ integrations → project a durable `eu_*` (Option 1: `integration_id = NULL`, so
1377
+ the stored secret reconciles with the builder-mediated dispatch path) → bind a
1378
+ single-use, KV-backed `state` → return the connector's authorize URL.
1379
+ - **Connector seam:** a new `OAuthConnector` registry (`services/end-user-oauth`)
1380
+ with GitHub as the first provider (GitHub was previously PAT/App-paste only —
1381
+ this is the first real OAuth-redirect connector). Adding a provider is a
1382
+ registry entry, not a new route.
1383
+ - Orchestration + security invariants live in
1384
+ `services/end-user-oauth/connect-service.ts` (thin route adapter over it),
1385
+ unit-tested with the verifier injected.
1386
+
1387
+ Connect leg only — the OAuth callback (token exchange → `eu_*`-scoped secret) and
1388
+ GitHub OAuth App credential provisioning (add `GITHUB_OAUTH_CLIENT_ID` /
1389
+ `GITHUB_OAUTH_CLIENT_SECRET` to `wrangler.toml` `required`) land in the follow-up.
1390
+ Until provisioned, the connector reports "not configured" and connect returns 501.
1391
+
1392
+ - 9736850: Fix non-streaming agent execution masking an approval pause. When a Quick-Agent's
1393
+ tool call tripped the approval gate (`config.tools.approval.require`), the flow
1394
+ paused but the non-streaming (`streamResponse: false`) `POST /v1/agents/{id}/execute`
1395
+ and `POST /v1/dispatch` agent paths branched only on `execResult.success` (which
1396
+ is `true` on a pause), so they persisted the `agent_executions` row as `completed`
1397
+ (stopReason `complete`, `completedAt` stamped) and returned a bare success
1398
+ envelope — a JSON caller could never discover or resolve the pending approval.
1399
+
1400
+ Both paths now mirror the streaming path's paused persistence and the dispatch
1401
+ JSON payload's paused status: the row is persisted as `paused` (stopReason
1402
+ `paused`, no `completedAt`) and the response carries `status: 'paused'` with a
1403
+ `pausedReason` object. `pausedReason` threads the approval discriminators
1404
+ (`awaitReason: 'approval_required'` + `approvalId`) so a caller can resolve the
1405
+ gate via `POST /v1/agents/{id}/approve`. The wire shape is single-sourced in
1406
+ `buildPausedReason` so the several producers can't drift.
1407
+
1408
+ `@runtypelabs/sdk` (packages/client): `AgentExecuteResponse` gains `'paused'` on
1409
+ `stopReason` plus optional `status` and `pausedReason` fields, and
1410
+ `AgentsEndpoint.runTask()` now stops the session loop on a `paused` result
1411
+ instead of re-sending.
1412
+
1413
+ Verified live on staging.
1414
+
1415
+ - 8f13a37: Add policy-gated PII redaction at the log/telemetry chokepoints. A new
1416
+ `resolvePiiRedactionPolicy` resolver (mirroring `resolveLoggingPolicy`, default
1417
+ `off`) is threaded from agent/flow, surface, and product config through the
1418
+ execution engine into the two customer-data chokepoints: `prepareCustomerTailData`
1419
+ (customer tail logs → dashboard, R2, hot-tier DO, live tail, Sentry/Grafana) and
1420
+ `ExecutionTelemetryService.sendBatch` (R2 telemetry pipelines). When the resolved
1421
+ policy is `redact`, customer-supplied (UGC) fields are masked via the shared PII
1422
+ primitive; system fields are never touched. Opt-in and flag-free — no behavior
1423
+ change unless a config layer sets `piiRedaction: 'redact'`.
1424
+
1425
+ The agent `piiRedaction` config is a first-class field on every write surface:
1426
+ the SDK agent content-hash (`@runtypelabs/sdk`) includes it so an agent that only
1427
+ changes its redaction policy produces a new content hash, and the MCP
1428
+ `create_agent` / `update_agent` tools (`@runtypelabs/mcp`) accept `piiRedaction`
1429
+ (and the previously-missing `loggingPolicy`) so it round-trips through the agent
1430
+ tool surface.
1431
+
1432
+ - 9002f51: Security: agent sandbox container egress now fails closed. The sandbox DO's outbound enforcement treats an unset in-memory network-access profile as `off` for raw pass-through egress (previously it fell through to unrestricted egress, so a background process that outlived a DO eviction escaped the operator's `essentials`/`none` profile). The credentialed proxy paths (per-execution api token and phantom-token credential-proxy rules) remain reachable under the unset default because their state is equally in-memory and dies with the same eviction; an explicit `off`/allowlist profile still gates all paths. The container executor forwards an explicit `on` and deploy pins its historical open posture explicitly. The flow validator's `SANDBOX_NETWORK_ACCESS_DISABLED` warning now also fires for `runtype-sandbox`/`cloudflare-sandbox` transform steps that call `fetch()` without `networkAccess`. The inert `requireApprovalForBash` sandbox config knob is removed from the public schema until its approval wiring exists (use the generic `tools.approval` config to gate `bash` by name); the `open` egress profile's flag-only gating and the org-shared `conversationId` container keying are documented as accepted risks.
1433
+ - 4520c60: Fix the subagent HITL resume model's three design gaps (runtime). (1) Exactly-once pre-gate side effects: resume is a full child re-run (the Anthropic session is archived on pause), so a non-idempotent child tool call made before the gated one used to re-execute on approve-resume (child does `create_ticket` then gated `send_email` → two tickets). `runSubagent` now records every child tool result in a `SubagentReplayJournal` that rides out on `ApprovalRequiredError.replayJournal`; passed back on resume via `ApprovalConfig.subagentReplayJournal`, matching re-issued calls (same tool name + same canonical parameters) are answered from the journal instead of executing again. (2) Boundary-scoped, parameter-bound approval grants: the gate previously matched `approvedTools` by bare tool name, so approving the child's `send_email` pre-approved the parent's and every sibling subagent's same-named tool for the whole execution — and a child re-run could re-issue the approved tool with different parameters than the human reviewed. Grants now resolve against the approval boundary (`ApprovalRequiredError.approvalScope`, e.g. `subagent:delegate_email`) and the recommended one-shot resume grant is the param-bound `ApprovalRequiredError.approvalKey`, which re-prompts when the re-issued call's parameters drift from what the human saw; bare-name grants keep working for the top-level agent's own tools but no longer bleed into children (bare-name denials stay effective at every boundary, the fail-closed direction). (3) Parent-visible approval correlation: `agent_approval_start.toolCallId` for a subagent pause is the muted child's tool_use id, which never appeared on the parent stream — the `subagent` attribution block now carries `parentToolCallId`, the subagent tool call's id whose `agent_tool_start` the client already saw (additive optional field on the shared SSE schemas and the regenerated OpenAPI spec / SDK types). Also: `ExecuteAgentOptions.onApprovalRequired` hands `executeAgent` consumers the full pause object (the SSE frame intentionally omits the resume-critical `approvalKey` / `approvalScope` / `replayJournal`), and the skill capability-load gate resolves grants through the same boundary-scoped + param-bound matcher so the pause's `approvalKey` satisfies it on resume and a top-level skill grant does not bleed into a subagent child's same-named skill load.
1434
+ - 49d599f: Fix chat surfaces created via REST/MCP silently disabling WebMCP: surface write paths (REST create/update and the config-as-code ensure service) now inject the `type` discriminant into the persisted behavior JSONB (callers passing legacy `config` or a `behavior` without `type` previously stored it verbatim), the canonical surface content hash normalizes behavior with the injected discriminant on both the shared and SDK copies so ensure converges without phantom diffs, and `readSurfaceWebMCPPolicy` accepts the surface row's `type` column as the authoritative discriminant so legacy rows written without one still resolve their WebMCP policy.
1435
+ - 9cbdba8: Carry per-request tenancy identity (`tenant` / `endUser`, multi-tenancy V1) through
1436
+ the MCP and Code-Mode execution surfaces so multi-tenant dispatch/execute reaches
1437
+ parity with the REST routes + generated SDKs:
1438
+ - MCP `dispatch` tool gains optional `tenant` / `end_user` inputs (snake_case,
1439
+ mapped to camelCase on the wire); the `.strict()` schema previously rejected them.
1440
+ - MCP `execute_agent` tool gains the same optional `tenant` / `end_user` inputs,
1441
+ threaded through both `RuntypeClient.executeAgent` implementations (the
1442
+ hand-written HTTP client and the api-internal client), whose closed body objects
1443
+ previously dropped them.
1444
+ - Code-Mode MCP `executeAgent` becomes a pass-through like `dispatch` (its closed
1445
+ destructure dropped `tenant`/`endUser`; it also now sends the
1446
+ `streamResponse: false` key the execute route actually reads, instead of the
1447
+ ignored `stream: false`), and the overlay docs show tenant/endUser on
1448
+ `executeAgent` and `dispatch`.
1449
+ - TS SDK `CreateSecretRequest` is now derived from the generated OpenAPI types
1450
+ instead of a drifted hand-written interface, picking up `productTenantId` /
1451
+ `endUserId`.
1452
+
1453
+ Omit `tenant` / `endUser` for the unchanged org-level default.
1454
+
1455
+ - b82d8f1: Usage-based-pricing slice 1b-ii: propagate the platform-key spend-block
1456
+ `blockReason` discriminant (`credit_exhausted | trial_expired |
1457
+ auto_topup_failed`) onto the public wire and render differentiated dashboard
1458
+ paywall copy keyed off it.
1459
+ - Wire: `blockReason` is now an optional field on the `execution_error`
1460
+ (unified) and `flow_error` (legacy) SSE event schemas, forwarded through both
1461
+ legacy→unified mapper branches, every `flow_error` / `dispatch_error` emitter,
1462
+ the REST error envelope, and the buffered non-streaming payload. Regenerated
1463
+ OpenAPI + Python SDK + client types follow.
1464
+ - Dashboard: a shared `getSpendBlockCopy` helper maps the reason to card-centric
1465
+ copy (out of credit / trial ended / payment failed), consumed by the streaming
1466
+ execution-error surface, the REST 402 handler, and the usage banner. All spend
1467
+ and slow-mode CTAs route to the credit-card entry page
1468
+ (`/settings/billing/portal`); hardcoded "Upgrade to Startup" / "Upgrade to
1469
+ Team" strings are replaced with "Add a payment method".
1470
+
1471
+ Additive and behavior-neutral: `blockReason` is only populated on an already
1472
+ flag-gated (`usage-based-pricing`) spend block, and the paywall copy falls open
1473
+ to generic "add a payment method" copy for an absent or unrecognized reason.
1474
+
1475
+ - edae98b: Usage-based pricing slice 4 (dashboard): expose the `usage-based-pricing` flag to the SPA as `features.usageBasedPricing` on `GET /v1/users/profile`, and gate the card-centric billing surfaces on it. With the flag on, the SlowModeModal CTA and the BYOK card copy become "Add a payment method" (routing to the credit-card entry page), and the usage alert banner differentiates the force-upgrade reasons (out of credit vs trial ended vs payment failed) by mirroring the API spend gate's subscription/trial precedence, including a new trial-expired alert. Flag off (the production default) renders today's UI byte-identically; the flip per cohort is the launch lever.
1476
+
1477
+ ## 5.7.0
1478
+
1479
+ ### Minor Changes
1480
+
1481
+ - fd2e18e: Durable Claude Managed sessions (slice 4a): durable run record + deliverables persistence.
1482
+
1483
+ The Postgres half of the durable-sessions §8 Q1 split — the raw SSE event log stays in DO-SQLite; Postgres carries only a per-turn summary plus deliverable pointers.
1484
+ - New tables `managed_agent_runs` (one row per managed turn, written at settle with status / final text / token usage / cost / the seq ceiling) and `managed_agent_outputs` (one pointer row per swept `/mnt/session/outputs/` file; the bytes already live in asset storage). Run rows are idempotent on `execution_id`; deliverable rows dedupe on `(run_id, anthropic_file_id)`.
1485
+ - `ClaudeManagedSessionDO` writes both at turn settle (success or failure) in the same `finally` as the lease — best-effort, so a persistence failure never fails the run. The shared `onSessionOutput` sweep callback gains an `onDeliverableSwept` hook the DO uses to collect deliverable pointers during the turn.
1486
+ - New read routes `GET /v1/agents/{id}/runs?conversationId=` and `GET /v1/agents/{id}/runs/{runId}` (agent ownership + AGENTS:READ, the same trust level as the events reconnect route) — the queryable execution record and "deliverables tab" backbone managed turns lacked.
1487
+
1488
+ Adds `managed-agent-run-store.ts` mirroring the session store. No webhook or headless behavior yet (slices 4b/4c).
1489
+
1490
+ ## 5.6.0
1491
+
1492
+ ### Minor Changes
1493
+
1494
+ - 78e6f0e: feat(evals): grader severity (`gate`/`soft`) + functional `--strict`, and inline `evals:` on `flows.ensure` (code-colocated evals increment 5)
1495
+ - **Severity.** Every grader now carries an optional `severity: 'gate' | 'soft'` (absent ⇒ `gate`, the historical hard-gate behavior). A `soft` grader miss is tracked-but-not-failing — reported per-outcome but it does not fail the case unless the run is strict. `casePassed(outcomes, { strict })` in `@runtypelabs/shared` owns the aggregation; `EvalScoringService.scoreRun` threads a `strict` flag and stamps each outcome's severity.
1496
+ - **`runtype eval --strict` is now functional** (previously a stub): it sends `strict: true` to `POST /v1/eval/run`, and the server-computed severity-aware `passed` drives the exit code (a soft miss alone exits `0` without `--strict`, `1` with it; a gate miss always exits `1`). The CLI also surfaces soft misses on passing cases as warnings.
1497
+ - **SDK chainable severity handles.** Grader builders (`contains(...)`, `judge(...)`, `calledTool(...)`, …) return a `Gradeable` with `.gate()` / `.soft()` handles (and `.atLeast(n)` on AI graders to set the 1-5 judge cutoff). The handles are non-enumerable, so a grader still serializes byte-identically to its plain wire shape and the content hash of an un-annotated grader is unchanged.
1498
+ - **Inline `evals:` on `flows.ensure`.** `defineFlow({ ..., evals: [...] })` attaches eval suites that converge with the flow; an inline eval that omits `target` defaults to the enclosing flow. This is SDK-orchestrated and honors the eval-endpoints-only rule — the flow converges through `/flows/ensure` (its `{ name, steps }` wire shape and content hash are unchanged; `evals` is never sent there) and each inline suite converges through the existing `/eval/ensure`. `Runtype.flows.ensure(...)` returns the per-suite converge outcomes under `evals`.
1499
+
1500
+ ## 5.5.0
1501
+
1502
+ ### Minor Changes
1503
+
1504
+ - edf1724: Code-colocated evals increment 4: trace graders — deterministic, free assertions
1505
+ over a run's execution trace (which tools/steps ran, in what order, whether it
1506
+ completed, what it cost), not just its final output text.
1507
+
1508
+ New `CheckGrader` kinds scored by the pure `runCheck` engine: `called_tool`
1509
+ (with optional `input` / `output` / `isError` / `times` filters),
1510
+ `not_called_tool`, `used_no_tools`, `max_tool_calls`, `tool_order` (ordered
1511
+ subsequence), `ran_step`, `step_order`, `completed`, and `cost`. `GradingTarget`
1512
+ gains an optional `trace` ({ toolCalls, steps, completed, costUsd }); trace
1513
+ checks fail gracefully with a clear reason when no trace was captured.
1514
+
1515
+ The synchronous `POST /v1/eval/run` path now populates `trace` per case: steps
1516
+ and cost come from the `FlowExecutionResult`, and tool calls are captured
1517
+ in-memory from the run's SSE tool events (the same mechanism the virtual-agent
1518
+ tool-persistence tap uses), so trace graders work end-to-end without persisting
1519
+ anything. Batch scoring (`/eval/submit`) is unchanged and still does not wire
1520
+ trace graders — they are fully exercisable via `/eval/run`.
1521
+
1522
+ The SDK gains trace-grader builders: `calledTool(name, opts?)`,
1523
+ `notCalledTool(name)`, `usedNoTools()`, `maxToolCalls(max)`, `toolOrder(tools)`,
1524
+ `ranStep(name)`, `stepOrder(steps)`, `completed()`, and `cost(maxUsd)`.
1525
+ `defineEval` now accepts these kinds (previously rejected as "not available
1526
+ yet").
1527
+
1528
+ ## 5.4.0
1529
+
1530
+ ### Minor Changes
1531
+
1532
+ - 0cd1d82: Add `defineEval` — the pure authoring layer for code-colocated evals. Define the evals for a flow or agent right next to its `defineFlow` / `flows.ensure` definition, using grader builders (`contains`, `regex`, `jsonField`, `length`, `latency`, `noError`, `matchesExpected`, `judge`, and the `judges.*` presets) that emit the canonical `GraderConfig` wire shapes. `defineEval` validates and normalizes a definition (target + cases + per-case graders, with suite-level `graders` merged into each case) into an environment-portable `EvalDefinition`, and `computeEvalContentHash` provides the hash-first basis for future `evals.ensure` convergence. Pure and local (no I/O), mirroring the `defineFlow` precedent. The converge endpoint, `runtype eval` CLI, and trace/severity grader extensions land in follow-up increments (see `docs/features/planning/2026-06-24-code-colocated-evals.md`).
1533
+ - a7b06ce: Code-colocated evals increment 3: the `runtype eval run` CI gate plus the
1534
+ synchronous run+score endpoint behind it.
1535
+
1536
+ New `POST /v1/eval/run` runs every case of an eval suite (a saved suite by id, or
1537
+ an inline `--virtual` definition) against its target flow/saved-agent, grades the
1538
+ outputs with the suite graders via `EvalScoringService`, and returns the suite
1539
+ score + per-case grader outcomes in one response. Synchronous and ephemeral — no
1540
+ batch is created and no `eval_case_scores` are persisted (AI-grader spend is
1541
+ still metered); bounded by a per-run case ceiling, with a per-case timeout.
1542
+ `claude_managed` agents and inline/virtual agent targets are rejected with an
1543
+ actionable error.
1544
+
1545
+ The SDK gains `client.evals.runSuite({ suiteId } | { definition })` (and the
1546
+ `runEvalSuite` helper) returning the typed `RunEvalResult`.
1547
+
1548
+ The CLI gains `runtype eval run [idOrDirPrefix]`: it discovers `**/*.eval.ts`,
1549
+ loads each `defineEval(...)` default export via jiti, `ensure`s the suite (or runs
1550
+ it inline with `--virtual`), runs it, and maps the result to eve-style exit codes
1551
+ (`0` pass / `1` gate failure / `2` config error). Supports `--junit <path>`,
1552
+ `--url <api>`, `--cwd <dir>`, and a `--strict` stub (grader severity lands in a
1553
+ later increment).
1554
+
1555
+ - 17f7b6b: Add the code-colocated eval converge surface (increment 2): `POST /v1/eval/ensure`
1556
+ - `GET /v1/eval/pull`, the `client.evals.ensure(def)` / `client.evals.pull(name)`
1557
+ SDK methods, and the canonical `computeEvalContentHash` in `@runtypelabs/shared`.
1558
+
1559
+ `evals.ensure` is the deploy-time, non-executing converge for `defineEval` suites
1560
+ — the eval sibling of `flows.ensure`. It is hash-first (the steady state is one
1561
+ tiny probe), upserts an eval suite + replaces its cases, and returns
1562
+ `unchanged | created | updated | definitionRequired` with the server-computed
1563
+ content hash. Suite identity is the suite name (defaults to `flow:<name>` /
1564
+ `agent:<name>`) + account scope; the target flow/agent is resolved by name and
1565
+ fails loudly on a missing or ambiguous match. Every write stamps
1566
+ `last_modified_source = 'sdk'` for the deferred external-edit conflict guard.
1567
+
1568
+ Lean v2 scope: hash-first idempotency only. Per-case divergent graders, dry-run /
1569
+ plan, 409 conflict codes, and version snapshots are deferred to later increments;
1570
+ `virtual: true` suites are rejected (ephemeral, nothing durable to converge).
1571
+ Adds `EVALS:*` / `EVALS:READ` / `EVALS:WRITE` API-key scopes.
1572
+
1573
+ ## 5.3.1
1574
+
1575
+ ### Patch Changes
1576
+
1577
+ - 2260366: Switch the flow generator and product generator to GLM 5.2 (`glm-5.2`).
1578
+ - The flow generator's `FLOW_GENERATION_MODEL_ID` now points at `glm-5.2`.
1579
+ - The product generator is now a **single fixed model**: the `product-generator-model` Flagship flag (and its allowlist, profile plumbing, and dashboard-side resolution) has been removed entirely. `@runtypelabs/shared` now exports a single `PRODUCT_GENERATOR_MODEL = 'glm-5.2'` constant used by both the converged agent definition and the dashboard generate route. The `features.productGeneratorModel` field is dropped from `GET /v1/users/profile`.
1580
+
1581
+ ## 5.3.0
1582
+
1583
+ ### Minor Changes
1584
+
1585
+ - 83d57ad: Add the `_tenant` system variable and the `ResolvedTenancyContext` spine (multi-tenancy V1, increment 1)
1586
+
1587
+ Dispatch and `agents/:id/execute` now accept an optional top-level `tenant: { id, ... }`
1588
+ object, exposed in templates as `{{_tenant.*}}` — the Product Tenant scope (the
1589
+ builder's customer _inside_ a Product), nested under the Builder Org (`_user`) and
1590
+ above the End User (`_endUser`). It is threaded exactly like `_endUser`: additive,
1591
+ caller-asserted, and keyed to nothing yet (the trust boundary, identity projection,
1592
+ and scoped memory/records/secrets land in later increments).
1593
+
1594
+ Internally, `runExecutionPipeline` now computes a `ResolvedTenancyContext` once at the
1595
+ execution seam (a thin, pure projection of the caller + dispatch identity) and attaches
1596
+ it to `executionMetadata.resolvedTenancy` as the forward-stated dependency root later
1597
+ increments read scope from. Every existing execution behaves identically.
1598
+
1599
+ - 9ed5154: Multi-tenancy V1 (increment 4): Tenancy Strategy + lock-down gate (ADR 0012).
1600
+ A resource can now declare a `tenancyStrategy` on its Definition (an archetype
1601
+ preset — `internal` / `tenant-isolated` / `end-user-isolated` — expanding into
1602
+ two orthogonal axes, data scope × credential authority, plus a per-scope
1603
+ assurance floor). At the execution-pipeline seam the strategy is expanded
1604
+ (populating `dataScope` / `credentialAuthority` on `ResolvedTenancyContext`) and
1605
+ a **lock-down gate** runs **pre-engine**: a locked-down resource is rejected
1606
+ (403) when the resolved tenancy lacks a required scope or sits below the declared
1607
+ assurance floor — never silently run against the global scope. This is the first
1608
+ consumer of increment 3's graded `assurance`, so a web-embedded `client-token`
1609
+ dispatch of an `end-user-isolated` agent correctly **fails closed** (graded
1610
+ `anonymous`) until an Identity Exchange can raise it to `verified`.
1611
+
1612
+ Scope is the **agent** resource (authorable via the agent create/update/ensure
1613
+ config, the FPO inline-agent config, and the SDK — content-hashed so `ensure`
1614
+ tracks it). Enforcement is at the pipeline seam (single-turn agent dispatch /
1615
+ `:id/execute` / product-chat / client-token). The multi-turn loop executor and
1616
+ the background schedule/eval executor — neither of which resolves per-execution
1617
+ tenancy yet — apply a **fail-closed** backstop: a locked-down agent is refused
1618
+ there rather than run global. Resources with no strategy behave identically to
1619
+ today (the headline invariant). Flow-resource strategies, the config-bearing
1620
+ tenant overlay, elicitation surfaces, full loop/background enforcement, and
1621
+ ResumeState scope persistence are forward-stated follow-ups (the scoped-data
1622
+ increment).
1623
+
1624
+ ## 5.2.3
1625
+
1626
+ ### Patch Changes
1627
+
1628
+ - 346f947: Run workspace type-checking on the TypeScript 7 native compiler (`tsgo`, from `@typescript/native-preview`). The per-package `typecheck` / `type-check` scripts now invoke `tsgo --noEmit` instead of `tsc --noEmit` (~5x faster across the workspace). This is a type-check-only change: `typescript` stays pinned at `^6.0.3` for builds, lint, and codegen — whose compiler-API consumers (tsup `.d.ts` emit, typescript-eslint, ts-morph) are not stable on the 7.x API until ~7.1 — so no package's runtime behavior or emitted types change. The `typecheck:native` benchmark harness is updated to stay version-agnostic (it derives the `tsc` baseline for `--compare`), `@typescript/native-preview` is bumped to the latest tracked build, and the orphaned `@runtypelabs/system-product-generator` package's stray `typescript@^5.9.3` pin is aligned to `^6.0.3`.
1629
+
1630
+ ## 5.2.2
1631
+
1632
+ ### Patch Changes
1633
+
1634
+ - c09b0af: Agent Sandbox (Phase 1): give a hosted agent a Linux computer via five `builtin`
1635
+ tools — `bash`, `read_file`, `write_file`, `edit_file`, `expose_port` — injected
1636
+ at dispatch when `agent.config.sandbox.enabled` and the `enable-agent-sandbox`
1637
+ Flagship flag is on. The agent acts as if it is already inside the machine; the
1638
+ container is provisioned lazily on the first sandbox tool call. Phase 1 ships the
1639
+ `runtype-sandbox` provider with `ephemeral` persistence behind the flag.
1640
+
1641
+ The egress posture defaults to a curated, versioned, single-sourced `essentials`
1642
+ network profile (language registries, OS mirrors, GitHub) enforced at the single
1643
+ DO egress proxy — bounded, not open. The runtime is untouched: the tools route
1644
+ through the existing injected `ToolExecutor`, and all
1645
+ provisioning/network/billing lives API-side (ADR 0010). `bash`/`read_file`/etc.
1646
+ do not appear in tool discovery (injected, not catalog-selected).
1647
+
1648
+ ## 5.2.1
1649
+
1650
+ ### Patch Changes
1651
+
1652
+ - 9a77df1: Subagent HITL approval propagation (runtime Claude Managed path). When a Claude Managed subagent's child agent calls a tool that requires approval, the runtime now pauses for a human instead of swallowing the request into an opaque subagent tool failure. The Claude Managed bridge no longer converts a child tool's `ApprovalRequiredError` into an `is_error` result — it re-raises it; `runSubagent` forwards the parent's approval policy (`require` / `approvedTools` / `deniedTools`) into the child and enriches the propagating error with subagent attribution, so the parent agent loop emits a single `agent_approval_start`. The `agent_approval_start` / `approval_start` SSE events gain an optional `subagent` attribution object (`toolName`, `agentName`) so an approval UI can render "subagent X wants to run tool Y". On resume the parent re-dispatches the subagent with the now-approved tool, which proceeds. Attribution is UX context only — no code branches on it. (api virtual-flow / Claude Managed subagent paths are tracked as a gated follow-up.)
1653
+
1654
+ ## 5.2.0
1655
+
1656
+ ### Minor Changes
1657
+
1658
+ - 049bcf9: Surface nested flow-as-tool attribution on the unified SSE stream via `text_start.parentToolCallId` and `reasoning_start.parentToolCallId`.
1659
+
1660
+ When a flow runs as a tool, the server enriches its streamed prompt text and reasoning with the parent model tool-call id (`toolContext.toolId`). The unified (Persona 4.0) translator previously dropped that enrichment, so the nested flow's text and thinking were flattened into the parent execution's channels with no way to attribute them. The unified `text_start` and `reasoning_start` events now carry an optional `parentToolCallId` (matching `tool_start.toolCallId`): a consumer maps a block's `id` to its `parentToolCallId` and routes the nested streamed text/thinking into the parent tool's row instead of the top-level assistant message. Additive and optional — top-level (non-nested) text/reasoning omits the field.
1661
+
1662
+ ## 5.1.0
1663
+
1664
+ ### Minor Changes
1665
+
1666
+ - 82d897c: Two follow-ups to the unified SSE cutover:
1667
+
1668
+ **Single public SSE schema name.** Rename the OpenAPI component `UnifiedSSEEvent`
1669
+ → `ExecutionStreamEvent` (and the SDK's canonical `UnifiedStreamEvent` type →
1670
+ `ExecutionStreamEvent`), so the published spec / Fern API reference / SDK types
1671
+ expose a single, neutrally-named SSE event union with no "unified vs legacy"
1672
+ framing. The internal Zod source keeps its `unified*` names; `DispatchEvent` /
1673
+ `AgentStreamEvent` remain deprecated aliases of the renamed type, and the
1674
+ legacy `FlowSSEEvent` component stays registered (no route reference) so the
1675
+ SDK's FlowBuilder convenience types keep resolving.
1676
+
1677
+ **Per-step token usage in the unified stream.** Add an optional `tokensUsed`
1678
+ field to the unified `step_complete` event and forward it through the edge
1679
+ translator, restoring the per-step token count on surfaces that moved to the
1680
+ unified vocabulary (e.g. the dashboard flow-builder test-step result).
1681
+
1682
+ ### Patch Changes
1683
+
1684
+ - 97a3431: Normalize remaining dashboard dispatch SSE consumers and flow-tool execution streams for the unified SSE default.
1685
+
1686
+ ## 5.0.0
1687
+
1688
+ ### Major Changes
1689
+
1690
+ - 44779c1: Remove phantom prompt-execution surfaces that targeted server routes which never existed (`POST /prompts/{id}/run` and `POST /prompts/{id}/run-on-record`): drops `prompts.run()` / `PromptRunner` / `PromptRunOptions` and `PromptsEndpoint.runOnRecord()`. These methods always 404'd; use `dispatch()` to execute a prompt step instead.
1691
+
1692
+ ## 4.21.0
1693
+
1694
+ ### Minor Changes
1695
+
1696
+ - b43088f: Make the unified SSE event vocabulary the default for streaming responses.
1697
+
1698
+ Every execution stream (`/v1/dispatch`, `/v1/agents/{id}/execute`, the client
1699
+ chat/resume routes, and the dispatch resume/approve routes) now emits the
1700
+ unified 33-event vocabulary by default. The OpenAPI spec and generated SDK types
1701
+ document the unified `UnifiedSSEEvent` union only; the legacy `AgentSSEEvent` /
1702
+ `DispatchSSEEvent` components are removed (`FlowSSEEvent` is retained solely for
1703
+ the not-yet-ported eval stream).
1704
+
1705
+ The wire vocabulary is resolved per request by `resolveSseEventFormat`:
1706
+ 1. an explicit `?events=unified` / `?events=legacy` query param wins;
1707
+ 2. otherwise the `X-Persona-Version` header negotiates (v4.0+ → unified, an older
1708
+ announced version → legacy, so deployed pre-4.0 widgets are protected);
1709
+ 3. otherwise the new `default-sse-event-format` Cloudflare Flagship flag (enum
1710
+ `unified` | `legacy`, code default `unified`) decides — targetable by
1711
+ environment, organization, user, or surface so a cohort can be rolled back to
1712
+ the legacy vocabulary during the surgical rollout.
1713
+
1714
+ The engine continues to emit legacy frames internally; they are translated to
1715
+ the unified vocabulary at the stream edge.
1716
+
1717
+ ### Patch Changes
1718
+
1719
+ - 4bef8db: Port the Python SDK (`runtype-sdk`) to consume the unified SSE event vocabulary
1720
+ as its only wire format. Every streaming request opts into `?events=unified` and
1721
+ the SDK parses the unified 33-event vocabulary natively (no legacy parsers, no
1722
+ translation layer). The hand-written event models, the SSE dispatch, the
1723
+ local-tool pause/resume collector, and the `FlowResult` / `FlowSummary`
1724
+ accumulation are all rewritten to the unified events.
1725
+
1726
+ This is a breaking change to the Python SDK's streaming surface (`runtype-sdk`
1727
+ 4.0.0): the legacy `agent_*` / `flow_*` event models are replaced by the unified
1728
+ models (`UnifiedEvent` union), `StreamCallbacks` exposes one callback per unified
1729
+ event type (and is the single surface for flow + agent, with `AgentStreamCallbacks`
1730
+ as an alias), and `FlowSummary.execution_time` becomes `duration_ms`. The npm
1731
+ `@runtypelabs/sdk` (TypeScript SDK) is unchanged; this entry exists to record the
1732
+ Python SDK change in the release pipeline.
1733
+
1734
+ - 2b788c7: Port the TS SDK's hand-written SSE runtime parsers to the unified 33-event
1735
+ vocabulary. The dispatch and agent execution streams (`/dispatch`,
1736
+ `/dispatch/resume`, `/agents/{id}/execute`, `/agents/{id}/resume`) now request
1737
+ `?events=unified` and reverse-translate the unified frames back to the SDK's
1738
+ existing legacy event shapes, so the public `StreamCallbacks` /
1739
+ `AgentStreamCallbacks` surface is unchanged. The translators are tolerant of a
1740
+ legacy stream too (pass-through), keeping the SDK safe during the server-side
1741
+ rollout. Still-legacy producers (`/prompts/{id}/run`, the eval stream,
1742
+ `/tools/{id}/execute`) are untouched.
1743
+
1744
+ ## 4.20.0
1745
+
1746
+ ### Minor Changes
1747
+
1748
+ - 3fdf801: FPO config-as-code PR3 — `pull-fpo` + `prune`, closing the whole-FPO round-trip.
1749
+ - **`GET /v1/products/pull-fpo?name=…`** (SDK `products.pullFpo(name)`) reconstructs
1750
+ a self-contained Full Product Object from the live product graph — the absorb-drift
1751
+ direction of `ensure-fpo`. It composes the existing per-entity pulls (product,
1752
+ backing flows/agents, surfaces) and mints deterministic FPO-local ids, so feeding
1753
+ the result back into `ensure-fpo` converges to `unchanged`. records/schedules/secrets
1754
+ are not reconstructed and `tools` is emitted empty (tool refs are portable
1755
+ `tool:<name>`); per-pull caveats are surfaced in `warnings`.
1756
+ - **`prune` (opt-in)** on `POST /v1/products/ensure-fpo` (SDK `ensureFpo(fpo, { prune })`):
1757
+ after the converge passes, removes product-scoped `product_capabilities` / `product_surfaces`
1758
+ (and their `product_surface_items`) absent from the FPO. Account-scoped flows/agents/tools/records
1759
+ are NEVER touched; `createPolicy: 'skip'` entities are kept. Adds a `pruned` per-entity
1760
+ result; with `dryRun`, would-be removals are reported as `pruned` plan rows without deleting.
1761
+
1762
+ ## 4.19.2
1763
+
1764
+ ### Patch Changes
1765
+
1766
+ - d5b9f87: Normalize client identity + version advertisement across all Runtype SDKs/clients.
1767
+
1768
+ Every first-party client now sends a consistent `X-Runtype-Client: <kind>` header
1769
+ plus a `runtype-<kind>/<version>` `User-Agent`, so the API can attribute traffic by
1770
+ client and version (`detectActorSource` in `audit-log.ts`):
1771
+ - **JS/TS SDK (`@runtypelabs/sdk`)**: previously sent no attribution at all — now
1772
+ defaults to `X-Runtype-Client: sdk` + `User-Agent: runtype-sdk/<version> (typescript)`
1773
+ (version inlined at build via tsup `define`). Caller-supplied headers still override,
1774
+ so the CLI's `runtype-cli/<version>` wrapping is unaffected.
1775
+ - **Python SDK**: now sends `X-Runtype-Client: sdk` + `User-Agent: runtype-sdk/<version> (python)`
1776
+ on both the ergonomic client and the generated `.api` surface. The version is resolved
1777
+ from installed distribution metadata via `importlib.metadata` (single source: `pyproject.toml`).
1778
+ - **MCP (`@runtypelabs/mcp`)**: now advertises `X-Runtype-Client: mcp` +
1779
+ `User-Agent: runtype-mcp/<version>` (the headers were previously gated on a client
1780
+ kind that no call site ever set, so the MCP server sent no attribution at all).
1781
+ - **Code Mode MCP**: now versions its proxy `User-Agent` (`Runtype-CodeMode/<version>`),
1782
+ keeping the exact prefix casing the Analytics Engine dashboard filters on.
1783
+
1784
+ The `runtype-sdk/` prefix is shared by both SDKs (it's what the API parses); the
1785
+ TypeScript vs Python distinction rides in the standard User-Agent comment
1786
+ (`(typescript)` / `(python)`). The CLI already advertised identity + version and is
1787
+ unchanged.
1788
+
1789
+ - 6d5ba51: Harden WebMCP page-tool handling against indirect prompt injection (Chrome WebMCP security guidance / OWASP LLM01):
1790
+ - **Spotlight untrusted tool output.** Output from `origin: 'webmcp'` page tools (and any tool a page flags with the new `untrustedContentHint`) is now wrapped in a randomized-nonce delimiter envelope before the model reads it, so the model treats contaminated third-party data as data, not instructions. Verbatim output is unchanged in ResumeState, `{{toolName_result}}` variables, and the SSE stream.
1791
+ - **Cap untrusted output size** at 64KB (truncated with a visible marker) to prevent context/token-exhaustion via an unbounded page-tool result.
1792
+ - **Cap `clientTools[]` descriptions at 2KB** to close the malicious-manifest / tool-poisoning vector (hidden instructions in an oversized description).
1793
+ - **Add `untrustedContentHint`** to the inline client-tool wire schema so a page can mark an `sdk`-origin tool's output as untrusted.
1794
+ - Strengthen the dashboard assistant's safety prompt to recognize the spotlight envelope and treat tool names/descriptions as untrusted data.
1795
+
1796
+ ## 4.19.1
1797
+
1798
+ ### Patch Changes
1799
+
1800
+ - cd7b402: Refresh default models for new accounts. The global default model is now
1801
+ `kimi-k2.6` (a reasoning + tool-use + vision model already trusted as the
1802
+ product-generator default) instead of the tagless 9B `qwen/qwen3.5-9b`. Bumped
1803
+ flagship tiers to the current generation and dropped the superseded versions:
1804
+ `gpt-5.4` → `gpt-5.5`, `claude-opus-4-6` → `claude-opus-4-8`, `qwen/qwen3.5-9b`
1805
+ → `qwen/qwen3.6-27b`, `qwen3.5-plus` → `qwen3.6-plus`, and added
1806
+ `gemini-3.5-flash` and `nemotron-3-ultra-550b-a55b`. Dropped the grok tiers and
1807
+ `nemotron-3-120b-a12b` from the auto-enabled set. Cheaper tiers with no newer
1808
+ equivalent (`gpt-5.4-mini`, `gpt-5.4-nano`, `gemini-3-1-flash-lite`,
1809
+ `gemini-3.1-pro`, `claude-sonnet-4-6`) are unchanged.
1810
+
1811
+ Kept in sync with the new default: the `prompts.model` column default (migration
1812
+ `0077`), the agent-facing model-selection guidance in `knowledge-fragments.ts`,
1813
+ and the regenerated OpenAPI spec / SDK types (the `create_prompt` / `update_prompt`
1814
+ `model` default).
1815
+
1816
+ ## 4.19.0
1817
+
1818
+ ### Minor Changes
1819
+
1820
+ - 9efb803: Marathon playbooks: opt-in `fallbackOnEmpty` to recover from empty (text-less)
1821
+ model output.
1822
+
1823
+ Set `fallbackOnEmpty: true` on a milestone (or at the playbook top level as a
1824
+ default) to make that milestone's `fallbackModels` chain also fire when the model
1825
+ finishes successfully but returns no visible text — the "thinking model spends its
1826
+ whole budget reasoning and answers nothing" failure — instead of only on errors.
1827
+ It attaches the execution engine's existing `empty-output` fallback trigger to the
1828
+ agent `errorHandling` the marathon writes. Off by default (never automatic) and
1829
+ only effective when `fallbackModels` is configured; the playbook loader warns when
1830
+ it is enabled without a fallback chain. No API change.
1831
+
1832
+ - 9efb803: Marathon: support per-session `maxTokens` and `temperature` overrides for the
1833
+ **primary** model, settable from the CLI (`--max-tokens` / `--temperature`) and
1834
+ in playbooks (per-milestone fields plus top-level defaults).
1835
+ - API: `POST /v1/agents/:id/execute` now accepts request-level `temperature` and
1836
+ `maxTokens`, merged into the agent config before `buildVirtualFlow` (mirrors the
1837
+ existing `model` / `systemPrompt` overrides). Out-of-range values are ignored.
1838
+ - SDK: `RunTaskOptions`, `AgentExecuteRequest`, and `WorkflowMilestoneConfig` gain
1839
+ optional `temperature` / `maxTokens`; the marathon runner threads them into each
1840
+ session's execute request.
1841
+ - CLI: resolves both per phase with precedence per-milestone playbook value >
1842
+ global CLI flag > playbook top-level default.
1843
+
1844
+ Previously only a fallback model could carry a `maxTokens`, so a "thinking"
1845
+ primary model that spent its budget reasoning could return no tool call and stall
1846
+ the run with nothing to show.
1847
+
1848
+ ### Patch Changes
1849
+
1850
+ - e96edad: feat(products): ensure-fpo PR2 — converge surfaces, records, schedules, secrets, and capability-as-tool composition
1851
+
1852
+ Extends `POST /v1/products/ensure-fpo` (and SDK `products.ensureFpo()`) beyond the PR1 product → capability-backing graph to converge the rest of the Full Product Object:
1853
+ - **Surfaces + surface items**: each `fpo.surfaces[]` converges via the product-scoped surface-ensure service (behavior `{ type, ...config }`), and route → `product_surface_items` join rows reconcile against the converged capabilities. Integration-backed types (slack/telegram/discord) reserve a pending integration once, on first create only (idempotent; `inbound` stored raw and excluded from the content hash).
1854
+ - **Records**: `fpo.records[]` reconcile by tenant `(type, name)` with merged metadata (mirrors the assembler's `onConflictDoUpdate`).
1855
+ - **Schedules**: `fpo.schedules[]` reconcile by `(owner, backing flow/agent, triggerType)` with update-in-place (never orphan — an orphaned schedule keeps firing). A within-FPO duplicate `(capabilityId, triggerType)` guard keeps the identity unambiguous.
1856
+ - **Secrets**: tool `auth.setupRequired` secret bindings + product-level `fpo.secrets[]` materialize via the (now shared) assembler helpers. The route additionally requires `SECRETS:WRITE` — but only when the FPO actually carries secrets, so secret-free callers keep working with `PRODUCTS:WRITE` alone.
1857
+ - **Capability-as-tool composition**: `capability:<id>` toolIds and `capabilityToolRefs[]` now materialize as flow-backed tools (converged by name via tool-ensure) with the agent ref rewritten to the portable `tool:<name>` form before the agent converges. The PR1 rejection guard is removed. Backing converges in two passes (flows then agents) so a composition target resolves regardless of FPO order.
1858
+
1859
+ The per-entity report (`entities[]`) gains `tool` / `surface` / `record` / `schedule` kinds. Still NON-atomic and self-healing on re-run; whole-FPO fast-probe, `pull-fpo`, and `prune` remain deferred to PR3.
1860
+
1861
+ ## 4.18.0
1862
+
1863
+ ### Minor Changes
1864
+
1865
+ - 266280e: Add whole-FPO config-as-code converge: `POST /v1/products/ensure-fpo` + `products.ensureFpo()`.
1866
+
1867
+ Converges an entire Full Product Object (the nested product graph) in one shot by fanning out to the
1868
+ existing per-entity ensure services in dependency order (product → capability backing flows/agents →
1869
+ capability link rows), threading FPO-local refs to real identities and returning a per-entity report.
1870
+ NON-atomic by design: a per-entity failure is reported (not thrown) and a re-run self-heals.
1871
+
1872
+ This first release converges product + capability backing flows/agents + capability links. Surfaces,
1873
+ records, schedules, `fpo.tools` secret bindings, capability-as-tool composition, the whole-FPO
1874
+ hash-only fast-probe, `pull-fpo`, and opt-in `prune` are deliberately deferred to follow-ups (such
1875
+ capabilities are reported as `failed`/`skipped` in the per-entity report). Adds the canonical
1876
+ `computeFpoContentHash` to `@runtypelabs/shared` (with an inlined SDK copy pinned by a shared parity
1877
+ corpus).
1878
+
1879
+ ## 4.17.1
1880
+
1881
+ ### Patch Changes
1882
+
1883
+ - 408e8f4: Enable conditional revalidation (instant-lists part 4) for the Agents list, the
1884
+ root deferred from part 3. The canonical first page of `GET /agents` now supports
1885
+ HTTP conditional GET: it returns a weak `ETag` and `304 Not Modified` when the
1886
+ caller's `If-None-Match` still matches, skipping the page query. The dashboard
1887
+ sends the prior ETag when warming/revalidating the Agents list, so a navigation or
1888
+ reload where nothing changed costs a single `304` with no body and keeps the warm
1889
+ seed on screen.
1890
+
1891
+ The agents list `lastRunAt` is a correlated `MAX(agent_executions.created_at)` per
1892
+ agent rather than an `agents` column, so the change token folds that cross-table
1893
+ max in (via a new additive `freshnessExpressions` option on the shared
1894
+ `evaluateListConditional` helper). This means a new agent run busts the cache and
1895
+ the "Last run" cell never goes stale behind a `304` — a deliberate trade that
1896
+ lowers the `304` hit rate for actively-running accounts in exchange for
1897
+ correctness. See `docs/features/planning/2026-06-14-list-conditional-revalidation.md`.
1898
+
1899
+ - fee4239: Extend conditional revalidation (instant-lists part 3) to the Records, Tools, and
1900
+ Schedules lists. The canonical first page of `GET /records`, `GET /tools`, and
1901
+ `GET /schedules` now supports HTTP conditional GET: each returns a weak `ETag`
1902
+ (`COUNT` + `MAX(updated_at, ...)`) and `304 Not Modified` when the caller's
1903
+ `If-None-Match` still matches, skipping the page query entirely. The dashboard
1904
+ sends the prior ETag when warming/revalidating these lists, so a navigation or
1905
+ reload where nothing changed costs a single `304` with no body and keeps the warm
1906
+ seed on screen. Schedules folds `next_run_at`/`last_run_at` into the freshness MAX
1907
+ (the scheduler writes them without bumping `updated_at`); records/tools need no
1908
+ extra freshness columns. The conditional path is gated to the truly unfiltered
1909
+ canonical page on both sides — any filter/search/cursor or non-default page size
1910
+ falls back to a plain GET.
1911
+
1912
+ The Agents list is intentionally deferred to a follow-up: its `lastRunAt` is a
1913
+ correlated subquery over the executions table rather than an `agents` column, so a
1914
+ token over `agents` alone would 304 after a new run and leave "Last run" stale.
1915
+ See `docs/features/planning/2026-06-14-list-conditional-revalidation.md`.
1916
+
1917
+ ## 4.17.0
1918
+
1919
+ ### Minor Changes
1920
+
1921
+ - 83e5921: Conditional revalidation for the Flows list (instant-lists part 2). The canonical
1922
+ first page of `GET /flows` now supports HTTP conditional GET: it returns a weak
1923
+ `ETag` (`COUNT` + `MAX(updated_at)`) and `304 Not Modified` when the caller's
1924
+ `If-None-Match` still matches, skipping the page query entirely. The dashboard
1925
+ sends the prior ETag when warming/revalidating the Flows list, so a navigation or
1926
+ reload where nothing changed costs a single `304` with no body and keeps the warm
1927
+ seed on screen. Adds `RuntypeClient.getConditional()` to the SDK. Other primary
1928
+ lists and detail-shell seeding are fast-follows (see
1929
+ `docs/features/planning/2026-06-14-list-conditional-revalidation.md`).
1930
+
1931
+ ## 4.16.0
1932
+
1933
+ ### Minor Changes
1934
+
1935
+ - 46624dc: Add config-as-code `ensure` for skills (admin/control plane). New `POST /v1/skills/ensure` (idempotent hash-first converge: create the skill or append a new version when the canonical manifest hash differs; `release: 'publish'` publishes the converged version) and `GET /v1/skills/pull` (canonical definition + provenance, the absorb-drift direction). Identity is name + account scope; the content hash (`computeSkillContentHash`) covers the manifest with the frontmatter `name` excluded. Ensure is the admin-plane `create_skill` path — API scopes only (`SKILLS:WRITE`/`SKILLS:*`), no review queue, no proposal row — distinct from the deployed-agent `propose_skill` data plane. Adds `configHash` / `lastModifiedSource` / `lastSdkSyncAt` provenance columns on `skills`, a `skill_ensure_locks` scoped-name serialization table, and the SDK surface `defineSkill` + `skills.ensure()` / `skills.pull()`.
1936
+ - 845d81b: Add config-as-code `ensure` / `pull` for surfaces. New product-scoped endpoints
1937
+ `POST /v1/products/{id}/surfaces/ensure` and `GET /v1/products/{id}/surfaces/pull`
1938
+ idempotently converge a repo-defined surface onto a product (identity is
1939
+ productId + name) using the shared hash-first ensure protocol — hash-only probe,
1940
+ `definitionRequired` miss, 422 content-hash-mismatch, 409 conflict, and `dryRun`
1941
+ planning. Surfaces have no version snapshots (release `none`), so there is no
1942
+ publish option. The SDK gains `Runtype.surfaces.ensure(productId, def)` /
1943
+ `Runtype.surfaces.pull(productId, name)` and `defineSurface`. Credentials in
1944
+ `inbound` / `outbound` are sealed before write and the content hash is computed
1945
+ over the plaintext form so probes stay stable across pulls.
1946
+ - 45aaaa7: Add cross-session "Always allow" persisted tool-approval grants. A remembered tool
1947
+ approval now persists server-side, keyed to (owner, agent, end-user) with an
1948
+ account-level fallback, so the human-in-the-loop prompt is skipped automatically on
1949
+ future dispatches for that tool. Enabled per-agent via `approval.choices.alwaysAllow`
1950
+ (default off) and revocable from the dashboard. Skill-load approvals
1951
+ (`toolType === 'skill'`) are excluded, and a grant only short-circuits the approval
1952
+ prompt — tool resolution, ownership scoping, and secret access are unaffected
1953
+ (approval ≠ authorization).
1954
+ - 3396ef1: Tighten context-step `errorHandling` validation to object-only (errorHandling v2, leg 4)
1955
+
1956
+ Flow create / update / ensure / validate now reject the legacy bare-string
1957
+ `errorHandling` shorthand (`'fail' | 'continue' | 'default'`) on context steps,
1958
+ along with the deprecated `'default'` onError alias. Clients must send the object
1959
+ form `{ onError: 'fail' | 'continue' | 'fallback' }` (which the dashboard, SDK,
1960
+ templates, and AI generators have emitted since the leg-2 convergence).
1961
+
1962
+ This is a deliberate, low-risk breaking change to the flow-authoring contract:
1963
+ 30-day production telemetry (`Flow ErrorHandling Written`) showed inbound
1964
+ string-form context writes came 100% from one internal smoke-test account, with
1965
+ zero organic customer traffic.
1966
+
1967
+ The SDK (`@runtypelabs/sdk`) context-step builders now type `errorHandling` as the
1968
+ object form only (`ContextErrorHandling`), so the now-rejected bare-string form is
1969
+ caught at compile time instead of surfacing as a runtime 400. Prompt-step builders
1970
+ still accept the string shorthand.
1971
+
1972
+ The runtime is unchanged: the replay normalizer still tolerates the string and
1973
+ `'default'` forms, so immutable historical flow versions keep executing, and
1974
+ `/dispatch` (which does not run this validator) is unaffected. Prompt-step
1975
+ `errorHandling` uses its own richer schema and is out of scope.
1976
+
1977
+ - f9b537d: Add Agent Skill scan verdicts (warnings vs errors).
1978
+
1979
+ Introduces a deterministic two-tier verdict layer over SkillSpector findings: a
1980
+ `warning` when a skill _appears_ malicious (low–medium confidence) and an
1981
+ `error` when it is high-confidence malicious or matches an always-block category
1982
+ (credential exfiltration, prompt injection, MCP tool poisoning, data
1983
+ exfiltration, privilege escalation).
1984
+ - `@runtypelabs/shared`: `deriveSkillScanVerdict()` + the confidence × severity
1985
+ matrix, hard-block categories, and the scanner-Worker RPC contract types
1986
+ (`SkillScanRequest` / `SkillScanResponse`). Pure and single-sourced so the API,
1987
+ dashboard, and scanner Worker share identical thresholds.
1988
+ - New `runtype-skill-spector` Worker app wraps `@runtypelabs/skill-spector` in
1989
+ its own bundle (off the API's bundle budget) and exposes a `scanSkill` RPC.
1990
+ - `@runtypelabs/api`: `scanSkillManifest()` client that serializes a manifest to
1991
+ its canonical SKILL.md, calls the scanner over the optional
1992
+ `SKILL_SPECTOR_SCAN` binding, and fails closed when the scanner is
1993
+ unavailable. Includes a `dev:bun` in-process stub for parity.
1994
+ - `@runtypelabs/api`: `POST /v1/skills/scan` ("scan before save", no persist/
1995
+ gate) + the `scanSkillManifest` client over the optional `SKILL_SPECTOR_SCAN`
1996
+ binding (staging-bound; fails closed when absent). Dev:bun in-process stub.
1997
+ - `@runtypelabs/sdk`: `skills.scan(markdown)` returning the verdict.
1998
+ - `@runtypelabs/dashboard-spa`: a "Security scan" affordance + verdict banner
1999
+ (green clean / amber suspicious / red blocked, with a findings list) in the
2000
+ skill editor.
2001
+
2002
+ Publish/bind gating and scan persistence remain sequenced follow-ups, and the
2003
+ production scanner binding is added once the production scanner Worker is
2004
+ deployed (worker-first ordering).
2005
+
2006
+ ## 4.15.0
2007
+
2008
+ ### Minor Changes
2009
+
2010
+ - 0dcd669: Add config-as-code `ensure`/`pull` for products. `POST /v1/products/ensure` idempotently converges a repo-defined product (create-or-update by name + account scope, hash-first probe protocol) and `GET /v1/products/pull` returns the canonical definition + provenance (the absorb-drift direction). The SDK gains `Runtype.products.ensure`/`Runtype.products.pull` plus `defineProduct`, mirroring the tool ensure surface.
2011
+
2012
+ Scope (v1): the converge covers the top-level product record only — `description`, `icon`, and the `spec` (ProductSpec: identity / stack / internal docs). The canonical content hash (`computeProductContentHash` in `@runtypelabs/shared`) excludes `name` (identity) and `canvas` (architecture-viewer UI layout state). Nested capabilities/surfaces/tools/records/schedules are not converged. Like tools, products have no version snapshots, so there is no publish option.
2013
+
2014
+ Adds `config_hash` / `last_modified_source` / `last_sdk_sync_at` provenance columns to `products` and a `product_ensure_locks` table for scoped-name serialization (migration `0071`).
2015
+
2016
+ ## 4.14.0
2017
+
2018
+ ### Minor Changes
2019
+
2020
+ - fec3688: feat(api): per-turn systemPrompt + clientTools on POST /v1/agents/:id/execute
2021
+
2022
+ Unblocks config-as-code migration (ADR 0003) for agents whose defining state is
2023
+ per-turn dynamic — the Dev Mode and Dashboard assistants rebuild their system
2024
+ prompt every turn (embedding the flow being edited / page context) and the
2025
+ Dashboard assistant's entire tool surface arrives as browser `clientTools[]`.
2026
+ They can now be referenced by saved-agent id instead of dispatched fully inline.
2027
+ - **`systemPrompt` override** (#4398): optional request-level override, applied to
2028
+ the loaded agent config before `buildVirtualFlow` (covers both the loop and
2029
+ non-loop paths), mirroring the existing `model` / `reasoning` overrides.
2030
+ Request-scoped, never persisted. Trusted-server-proxy-only.
2031
+ - **`clientTools` passthrough** (#4399): optional request-level WebMCP / SDK local
2032
+ tools, admitted through the SAME seam dispatch uses and threaded via
2033
+ `executionMetadata.clientTools` (api prompt-executor merge, precedence
2034
+ `saved < runtimeTools < clientTools`; stays off the runtime package).
2035
+ - **Shared admission seam**: extracted `admitApiKeyClientTools` in
2036
+ `runtime-tools-utils.ts`. `POST /v1/dispatch` is refactored onto it
2037
+ (behavior-preserving), pinned by a `.claude/review/touchpoints.yaml` row.
2038
+ - **SDK parity**: `AgentExecuteRequest` (`@runtypelabs/sdk`) gains `systemPrompt`,
2039
+ `clientTools`, and `clientToolsPolicy`, mirroring its existing `model` /
2040
+ `reasoning` overrides and the `DispatchRequest` contract.
2041
+
2042
+ Also adds `maxTokens` to the `FpoAgentRuntimeConfig` public type snapshot (the v2
2043
+ inline-agent `config` mirror), with a new `FpoAgentRuntimeConfig ↔
2044
+ agentRuntimeConfigSchema` parity guard so the hand-maintained mirror can no longer
2045
+ drift silently. Docs: `guide-runtime-tools.mdx` updated — `clientTools[]` /
2046
+ `clientToolsPolicy` are now accepted on both `/v1/dispatch` and
2047
+ `/v1/agents/:id/execute`.
2048
+
2049
+ - 26c393c: feat(agents): make `maxTokens` a savable agent config field
2050
+
2051
+ `maxTokens` is now part of the canonical agent runtime config surface
2052
+ (`agentRuntimeConfigSchema`), so it can be set via `createAgent`, `updateAgent`,
2053
+ and the config-as-code `ensure` protocol (`defineAgent({ maxTokens })`), persisted
2054
+ on the agent row, and included in the content hash. Previously it was an unknown
2055
+ key, silently stripped on write — agents that need a fixed output budget (e.g. the
2056
+ Product Generator pinning `24000` for a "thinking" default model) could not save it.
2057
+
2058
+ The execution wiring already consumed `config.maxTokens` (mapped onto the virtual
2059
+ flow's prompt step, defaulting to `16384` when unset), so saving it is all that was
2060
+ required for it to take effect. No per-request route override is added — a fixed
2061
+ token budget is a static property of the agent definition.
2062
+
2063
+ ## 4.13.1
2064
+
2065
+ ### Patch Changes
2066
+
2067
+ - 8bffeb2: Internal, tenant-scoped asset offload for inbound conversation media.
2068
+
2069
+ Large inline media (base64 images / file attachments) on inbound conversation
2070
+ message writes can now be offloaded to internal, tenant-scoped R2 storage and
2071
+ replaced with a small `asset_ref` reference in the persisted JSONB, then
2072
+ rehydrated back to bytes at model-execution time. Internal assets are resolved
2073
+ **server-side only**, gated on the authenticated caller's tenant — they are never
2074
+ exposed via a public or signed URL.
2075
+ - `AssetStorageService` gains a `visibility: 'internal'` mode and a
2076
+ tenant-checked `getInternal()` getter; `assets.public_url` is now nullable.
2077
+ - New authenticated, tenant-scoped serve route `GET /v1/internal-assets/{assetId}`.
2078
+ - Offload-on-write is gated behind the `enable-asset-offload` flag (off in
2079
+ production by default). Rehydration is always-on and a no-op when no references
2080
+ are present, so toggling the flag never strands data.
2081
+ - Shared offload/rehydrate primitives live in `@runtypelabs/model-execution`
2082
+ (`offloadInlineMediaInMessages`, `rehydrateAssetReferences`,
2083
+ `AssetReferenceContent`) and are wired into both the api and runtime prompt
2084
+ executors from the single shared implementation (no api↔runtime drift).
2085
+
2086
+ ## 4.13.0
2087
+
2088
+ ### Minor Changes
2089
+
2090
+ - 04db6c7: Generalize config-as-code name-based resolution from tools to **agents and
2091
+ flows**: reference a saved agent or flow from a portable `defineAgent` /
2092
+ `defineFlow` definition by name with the new `agent:<name>` and `flow:<name>`
2093
+ forms — the agent/flow analogue of the shipped `tool:<name>`.
2094
+
2095
+ Three reference sites are covered:
2096
+ - an `execute-agent` flow step's `config.agentId` → `agent:<name>`
2097
+ - a saved subagent runtime tool's `config.agentId` → `agent:<name>`
2098
+ - a flow-as-tool runtime tool's `config.flowId` → `flow:<name>`
2099
+
2100
+ The name is canonical (hashed and persisted), and resolves to a concrete row at
2101
+ execute time, every dispatch — ownership-scoped, oldest-wins on duplicate names —
2102
+ so the same definition converges to the same content hash across environments
2103
+ even though the underlying agent/flow ids differ. All three surfaces already
2104
+ loaded the **live row** by id at execution (no published-version indirection), so
2105
+ name resolution loads the same live row.
2106
+
2107
+ Highlights:
2108
+ - Reserved `agent:` / `flow:` reference prefixes with
2109
+ `isNamedAgentRef` / `parseNamedAgentRef` / `formatNamedAgentRef` (and the flow
2110
+ trio) in `@runtypelabs/shared`. They coexist with the `{{flow:X}}` template
2111
+ **variable** namespace, which is a separate grammar (only meaningful inside
2112
+ `{{…}}`).
2113
+ - **Ensure/validation time** (including `dryRun`): an `agent:<name>` / `flow:<name>`
2114
+ that resolves to zero rows (missing) or more than one (ambiguous) is a hard
2115
+ `400` per reference path (`AGENT_NOT_FOUND` / `AGENT_NAME_AMBIGUOUS` /
2116
+ `FLOW_NOT_FOUND` / `FLOW_NAME_AMBIGUOUS`) via the flow-validator account-scoped
2117
+ pass.
2118
+ - **Execution time**: names resolve oldest-wins with a structured warn for
2119
+ missing/ambiguous; a turn is never failed over reference quality. Wired into the
2120
+ execute-agent step handler, subagent dispatch, and the flow-as-tool executor.
2121
+ - Raw `agent_…` / `flow_…` ids stay rejected on the ensure surface. This also
2122
+ **closes a pre-existing portability hole**: the agent and flow ensure scans now
2123
+ cover saved-subagent / flow-as-tool runtime-tool refs and an `execute-agent`
2124
+ step's top-level `config.agentId`, none of which were scanned before.
2125
+ - `pull` reverse-maps raw `agent_…` / `flow_…` references to their name forms so a
2126
+ dashboard-built definition round-trips; references that can't safely round-trip
2127
+ (deleted, or name-shadowed by an older same-named row) are left as raw ids and
2128
+ reported in the existing `warnings` field.
2129
+
2130
+ Note: agents and flows have no "active/callable" gate (unlike tools), so
2131
+ resolution is over all rows in scope and ambiguity is simply "≥2 rows share the
2132
+ name". See `docs/features/planning/2026-06-13-ensure-name-based-agent-flow-resolution.md`.
2133
+
2134
+ - 7ea7ece: Layer 3 of the variable-namespace re-architecture: a prompt dispatched to the model with a surviving `{{...}}` token is now an observable signal, not just a buried server log.
2135
+ - A new optional `unresolvedVariables: string[]` field on the `step_complete` SSE event (canonical Zod in `sse-event-schemas.ts`, flowing to the OpenAPI spec + generated TS/Python SDK types). Prompt steps populate it (and telemetry) when template substitution left any reference unresolved — sourced from the engine's `missingVariables`, which already includes unknown-namespace tokens. Non-fatal: the step still runs.
2136
+ - The api prompt-executor's previously warn-only "missing variables" path now threads the unresolved names onto the wire + telemetry, and logs a clearer message.
2137
+ - The portable runtime engine emits the same field on its own `step_complete` (threaded through `PromptStepResult` → `StepLifecycleComplete` → the flow-stream emitter) and warns to the console — api↔runtime parity, not just the api.
2138
+
2139
+ This is the execute-time catch-all that surfaces the original `{{flow:topic}}` foot-gun on the first dispatch regardless of static coverage.
2140
+
2141
+ ### Patch Changes
2142
+
2143
+ - 3a162cc: Marathon ledger unification (increment 3): gate provider-native compaction prediction on the masked send view.
2144
+
2145
+ `buildNativeCompactionEvent` previously decided whether to announce a provider-native (Anthropic-side) compaction by comparing `breakdown.estimatedInputTokens` — the **true pre-mask** conversation size — against the auto-compact threshold. But provider-native compaction is performed by Anthropic, which only ever sees the **masked send view** (hot-tail offload has already shrunk old tool outputs before the request leaves the SDK). Gating on the larger pre-mask size made the SDK emit `start`/`complete` compaction lifecycle events for compactions the provider never performed.
2146
+
2147
+ The predictor now gates on `breakdown.sendEstimatedInputTokens ?? breakdown.estimatedInputTokens` (the send view's size, falling back to the true size when no masking reduction happened — the two are equal then). The event payload still reports the true pre-mask numbers (`estimatedTokens` / `beforeTokens`) for honest accounting display.
2148
+
2149
+ SDK-only; no wire-contract or behavior change for `summary_fallback` compaction. (Anchoring the provider-native summary in the durable ledger and fixing the latent compaction-summary leak in `stream-helpers.ts` are deferred to a follow-up increment — see the planning doc.)
2150
+
2151
+ - ee6f248: Harden record metadata key normalization (P0.6). Keys that normalize to a reserved prototype name (`constructor`, `prototype`, `__proto__`) or to an empty string are now rejected at every write seam (create, update, bulk-edit, CSV import) with a `METADATA_RESERVED_KEY` 400 (CSV reports the offending row and continues). Collision renames that were previously silent are now surfaced as `warnings: [{ code: 'KEY_RENAMED', from, to }]` on the create/update/bulk-edit/CSV responses. Also fixes a latent false-collision bug where the collision check traversed `Object.prototype` (now built on a null-prototype accumulator). The SDK's `records.create()` / `records.update()` now return `RecordWriteResponse` and `BulkEditResponse` carries an optional `warnings` field so typed consumers can read the rename notices.
2152
+
2153
+ ## 4.12.0
2154
+
2155
+ ### Minor Changes
2156
+
2157
+ - d895f91: Add name-based tool resolution to the config-as-code (`ensure`) surface:
2158
+ reference a saved tool from a portable `defineAgent` / `defineFlow` definition by
2159
+ its name with the new `tool:<name>` form (e.g.
2160
+ `toolIds: ['builtin:web-search', 'tool:My Scraper']`).
2161
+
2162
+ The name is canonical: it is what gets hashed and persisted, and it resolves to a
2163
+ concrete tool row at execute time, every dispatch (ownership-scoped, oldest-wins
2164
+ on duplicate names) — so the same definition converges to the same content hash
2165
+ across environments even though the underlying tool ids differ. Raw `tool_…` ids
2166
+ stay rejected on the ensure surface (they are environment-bound); the rejection
2167
+ message now points at `tool:<name>`.
2168
+
2169
+ Highlights:
2170
+ - `tool:` is a reserved tool-ref prefix; `separateToolIds` gains a `namedToolIds`
2171
+ bucket. Helpers `isNamedToolRef` / `parseNamedToolRef` / `formatNamedToolRef`
2172
+ live in `@runtypelabs/shared`.
2173
+ - **Ensure/validation time** (including `dryRun`): a `tool:<name>` that resolves
2174
+ to zero tools (missing) or more than one (ambiguous) is a hard `400` per
2175
+ reference path — the breakage surfaces at converge time in CI, not as a silent
2176
+ dispatch-time drop. Agent path in `agent-ensure-service`; flow path via the
2177
+ flow-validator account-scoped pass (which also fixes a pre-existing gap: the
2178
+ flow ensure scan now covers a `tool-call` step's top-level `config.toolId`).
2179
+ - **Execution time**: names resolve oldest-wins with a structured warn for
2180
+ missing/ambiguous; a turn is never failed over reference quality. Wired into
2181
+ the prompt-executor, code-step tool resolver, and tool-call executor.
2182
+ - `requiresApproval` accepts the `tool:<name>` form (matching the saved tool's
2183
+ name; the decision is still made on tool name + parameters only).
2184
+ - `pull` reverse-maps raw `tool_…` references to `tool:<name>` so a
2185
+ dashboard-built definition round-trips; references that can't safely round-trip
2186
+ (deleted, or name-shadowed by an older same-named tool) are left as raw ids and
2187
+ reported in a new `warnings` field on the pull response.
2188
+
2189
+ Note: named resolution is ownership-scoped (org-shared via `ownershipFilter`),
2190
+ intentionally broader than the legacy by-id custom-tool load in the
2191
+ prompt-executor (`eq(userId)`) — see the planning doc's open question #3. The
2192
+ by-id load's narrower scoping is left unchanged here to avoid a broad
2193
+ behavior change.
2194
+
2195
+ - 2392c5e: Hide the Agents Subdomains surface (`/v1/agents-subdomains`) from the published
2196
+ schema surface until GA. The routes stay mounted and runtime-gated by the
2197
+ `enable-agents-subdomains` Flagship flag, but they are now excluded from the
2198
+ public OpenAPI spec, the generated TypeScript SDK types, the Python SDK, and
2199
+ Fern docs.
2200
+
2201
+ This adds a build-time, declarative hidden-surface list
2202
+ (`HIDDEN_PUBLIC_PATH_PREFIXES` in `apps/api/src/lib/openapi/registry.ts`) that
2203
+ the OpenAPI generator applies only to the PUBLIC document (the same code path as
2204
+ `INTERNAL_TAG`, but keyed by path prefix so a whole feature can be hidden with
2205
+ one entry). The internal/admin spec still records the surface. Because the
2206
+ generator is deterministic and CI-drift-gated, hiding cannot depend on the
2207
+ runtime flag; this is a static exclusion that leaves runtime behavior unchanged.
2208
+
2209
+ Two customer-facing surfaces that the build-time spec exclusion can't reach are
2210
+ also gated at runtime by the same flag:
2211
+ - `GET /v1/api-keys/options` (which feeds the dashboard API-key scope picker) now
2212
+ omits the `AGENTS_SUBDOMAINS:*` / `:READ` / `:WRITE` scopes when the flag is off
2213
+ for the caller, so the feature isn't advertised there.
2214
+ - OAuth-minted MCP keys no longer include `AGENTS_SUBDOMAINS:*` until GA.
2215
+
2216
+ To un-hide the surface when it's ready, remove its entry from
2217
+ `HIDDEN_PUBLIC_PATH_PREFIXES`, regenerate the spec + SDKs, re-add
2218
+ `AGENTS_SUBDOMAINS:*` to `MCP_OAUTH_PERMISSIONS`, and drop the
2219
+ `AGENTS_SUBDOMAINS` entry from `KNOWN_EXCLUDED` in
2220
+ `tests/mcp-oauth-minted-scope.test.ts`. (The `/v1/api-keys/options` picker
2221
+ un-hides automatically once the flag is on.)
2222
+
2223
+ - 4cfef88: Marathon ledger unification (increment 1): route SDK hot-tail tool-output offloads through one artifact store.
2224
+ - SDK: new `offloadRecorder` option on `RunTaskOptions` (and exported `RunTaskOffloadRecorder` type). When supplied, `offloadToolResult` delegates persistence to the recorder and splices its reference into the send view instead of writing to the unledgered `.runtype/marathons/<slug>/tool-outputs/` fallback. The SDK still owns the offload decision (its inline threshold); the recorder owns storage. Declining (returning `undefined`/throwing) falls back to the built-in store, so non-marathon and browser consumers are unchanged.
2225
+ - CLI: supplies a recorder backed by the existing content-addressed ledger store (`offloadToolOutput`), so the 500–100k char band becomes a first-class ledger citizen (content-addressed `art_<sha256>`, hashed, `read_offloaded_output`-resolvable, deduped, on-timeline) instead of a separate unledgered store.
2226
+ - CLI: fix the per-offload `loadTreeLog` reload — `recordOffloadedArtifact` now appends through a shared head accessor (`getOrLoadTreeLogSync`) so artifact entries land on-chain at the live head instead of as sibling stubs off a stale, disk-reloaded head, and offload cost is O(1) rather than O(tree-log size).
2227
+
2228
+ - 04dc52c: Plan-tiered preview TTL and preview expiry extension.
2229
+
2230
+ `publish_page` preview pages now expire based on the account's plan instead of a uniform 7 days: the new `htmlPreviewTtlSeconds` billing entitlement (Schematic feature "HTML Preview TTL Seconds") is resolved at publish time, with -1 meaning permanent (no expiry) and a fail-closed 7-day default when unset or unreachable. The TTL is snapshotted at upload, so plan changes never retroactively expire existing URLs.
2231
+
2232
+ New `POST /v1/assets/:assetId/extend` endpoint re-ups a preview page's expiry to the caller's current plan TTL at the same URL — including previews that have already expired (expired pages keep their stored content; only serving is blocked). Exposed through the Runtype MCP server as the `extend_asset_expiry` tool, gated by the new `ASSETS:WRITE` API-key scope.
2233
+
2234
+ Callers can also choose a custom lifetime: `publish_page` accepts an optional `expiresInSeconds` parameter and the extend endpoint an optional `{ expiresInSeconds }` body (`expires_in_seconds` on the MCP tool). The plan TTL is a hard ceiling — shorter lifetimes are honored, longer requests are clamped, and permanence can never be requested (only granted by a permanent-plan entitlement).
2235
+
2236
+ - 6f6dd80: Extend config-as-code `ensure`/`pull` to the **tools** entity. Adds `POST /v1/tools/ensure` (idempotent, hash-first create-or-update by name + ownership) and `GET /v1/tools/pull` (round-trippable canonical definition + provenance), built on the shared `ensure-protocol` decision core used by agents and flows.
2237
+ - **SDK**: `defineTool`, `Runtype.tools.ensure(def, opts)`, and `Runtype.tools.pull(name)` with `expectNoChanges` drift gating and `onConflict` handling. The inlined content hash is parity-pinned against `@runtypelabs/shared` via a shared fixture corpus.
2238
+ - **Shared**: `computeToolContentHash` / `normalizeToolDefinition` over `{ toolType, description, parametersSchema, config }` (name is identity, excluded from the hash).
2239
+ - **Provenance**: `ensure` writes stamp `lastModifiedSource: 'sdk'` on the tool. Both dashboards render a "Managed in code" badge and a save-warning dialog when editing a code-managed tool.
2240
+
2241
+ Tools have no version snapshots, so (unlike agents/flows) there is no `release: 'publish'` option and no `versionId` in the response.
2242
+
2243
+ ### Patch Changes
2244
+
2245
+ - cf9eae0: Add the Runtype Apps index page to the dashboard (SPA): a Tier-1 list view to create apps, manage details (name, description, visibility), suspend/resume, delete, and browse version history with activate/rollback. The nav entry and route are gated behind the `enable-runtype-apps` flag via a new `features.enableRuntypeApps` field on `GET /v1/users/profile` (fail-closed in production), so a flag-off account never sees Apps. Regenerated OpenAPI spec and SDK types follow the new profile field.
2246
+ - 6c475fe: Close record-tool surface-parity gaps (P0.7): expose already-existing `/v1/records` API capabilities on the client surfaces that lacked them.
2247
+ - MCP: add the `delete_record` tool (input schema, schema-map registration, destructive tool definition, execution handler, and `deleteRecord` on the `RuntypeClient` interface + both implementers).
2248
+ - Code-Mode MCP: add `deleteRecord(id)` → `DELETE /v1/records/:id` to the executor and code-spec overlay.
2249
+ - SDK: add `records.getStepResults(id, params?)` → `GET /v1/records/:id/step-results` and `records.getCosts(id)` → `GET /v1/records/:id/costs` to `RecordsEndpoint`.
2250
+ - CLI: add `records results <id>` and `records costs <id>` subcommands routed through the SDK.
2251
+
2252
+ ## 4.11.0
2253
+
2254
+ ### Minor Changes
2255
+
2256
+ - e7a7c22: Default marathon workflow adopts the stall-policy and write-rule primitives (Phase 2). The default workflow now ships `stallPolicy: { nudgeAfter: 1, escalateModelAfter: 2, stopAfter: 3 }`, so a session that ends without any tool calls gets a corrective nudge on the very next session, a second actionless session signals model escalation (consumed when a fallback model is configured), and the run stalls on the third as before. The research and planning guards become trace-aware: once the plan file is written, product writes unlock in the same session instead of being blocked until the phase advances between sessions, confined by the same write-target rules execution enforces (modify allowlist, creation `outputRoot`), now shared via one helper. Creation tasks are also told about the `outputRoot` confinement in the planning instructions and execution tool guidance instead of discovering it through blocked writes.
2257
+ - e7a7c22: Add workflow stall policy and richer marathon playbooks. WorkflowDefinition gains an optional `stallPolicy` (`nudgeAfter` / `escalateModelAfter` / `stopAfter`) keyed on the consecutive-empty-sessions counter, so narration-only sessions trigger a recovery nudge, a model-escalation signal (`state.stallEscalationRequested`), or a configurable stall threshold instead of silently dying after 3 empty sessions. Playbooks can now declare per-milestone `recovery` messages (triggered on the same stall counter), `transitionSummary` strings, and a top-level `stallPolicy`; policy blocks also compile into model-facing tool guidance so enforced constraints (write globs, outputRoot, plan-first) are stated to the model instead of silently blocking it. The CLI consumes the escalation signal by restarting the marathon on the milestone's next `fallbackModels` entry, and surfaces playbook authoring warnings (e.g. no milestone sets `canAcceptCompletion: true`) at load time. Default (non-playbook) marathon behavior is unchanged.
2258
+ - e7a7c22: TypeScript playbooks. Marathon playbooks can now be `.ts`/`.mts` modules, loaded at runtime via jiti (no Node version or transpile-step requirements) from the same search paths as YAML/JSON. Every workflow config behavior slot (instructions, tool guidance, completion criteria, recovery, transition summaries, intercepts, force-end-turn, accept-completion, classify/bootstrap/candidate-block) now accepts a plain function in addition to inline data and hook references, so TypeScript playbooks express custom logic directly without registry ceremony; `definePlaybook(...)` is exported from the SDK as optional type-inference sugar. A module playbook's default export is either a config object or a factory receiving the injected `{ registerWorkflowHook }` API for named-hook registration. Load failures, missing default exports, and non-config exports fail with actionable errors naming the playbook path.
2259
+ - e7a7c22: Workflow hook registry and the default marathon workflow as data (Phase 3). The SDK gains a named hook registry (`registerWorkflowHook` with a reserved `builtin:` namespace, slot-kind validation, and actionable load-time errors) and an environment-free `compileWorkflowConfig` that turns declarative workflow configs into `WorkflowDefinition`s, with glob matching injected via deps. The default workflow's behaviors (classification, bootstrap discovery, per-phase instructions/guards/recovery/completion heuristics) are now registered as `builtin:*` hooks and composed by an exported `defaultWorkflowConfig` compiled through the same function — so playbooks and the default share one compile path and a user playbook can reuse or override any slice of the default by hook name. The CLI playbook loader delegates compilation to the SDK and adds: hook references in every behavior slot (`instructions: builtin:research-instructions`, `completionCriteria: { type: acme:my-completion }`), per-milestone `intercept`/`forceEndTurn` slots, and a `plugins:` field that loads JS modules (confined to the playbook's directory) which register custom hooks via an injected `registerWorkflowHook` API. `loadPlaybook` is now async. Compiled output for the default workflow is behavior-identical (pinned by the existing workflow test suite).
2260
+
2261
+ ### Patch Changes
2262
+
2263
+ - 713e743: Add the `chrome_extension` product surface type: a downloadable Manifest V3 Chrome extension that embeds the product's agent (vendored Persona widget) in the browser side panel with packaged browser tools (read page, fill forms, navigate tabs) executed through the native WebMCP client-tool loop. The dashboard assembles the ZIP client-side, pins a stable extension ID via the manifest `key` field, and converges the surface's client token allowlist and `behavior.webmcp` policy on download. The WebMCP surface-policy gate now admits `chrome_extension` behaviors alongside `chat`.
2264
+
2265
+ ## 4.10.0
2266
+
2267
+ ### Minor Changes
2268
+
2269
+ - ad2dd30: Agent config-as-code: `POST /v1/agents/ensure` (non-executing, hash-first idempotent converge with dryRun planning, onConflict/release policies, and 409/422 conflict detection), `GET /v1/agents/pull` (canonical definition + provenance), SDK `defineAgent` + `client.agents.ensure/pull`, shared `computeAgentContentHash` canonical serializer, and `configHash`/`lastModifiedSource` provenance columns on agents (all non-ensure write paths now stamp their source).
2270
+ - 90a4707: Enrich `GET /v1/skill-proposals` listings with the underlying skill + proposed
2271
+ version manifest. Each item now carries two additive, optional fields —
2272
+ `skill: { id, name, description, status } | null` and
2273
+ `version: { id, versionNumber, manifest: { frontmatter, runtype, body } } | null`
2274
+ — so the review queue can show reviewers WHAT they are approving. A skill body is
2275
+ instructions injected into another agent's context, and the `runtype` block
2276
+ carries the proposed capability bindings (flows, agents, tools, MCP servers,
2277
+ inline tools) — the most security-relevant part of an approval decision — so
2278
+ blind approval defeated review-by-default governance. Both fields are `null` only when the underlying
2279
+ skill/version row was deleted out from under the proposal (the listing degrades
2280
+ gracefully via a LEFT JOIN rather than 500-ing), and are entirely absent on
2281
+ older API servers — consumers must tolerate them being `undefined`. The SDK's
2282
+ `SkillProposal` interface gains the matching optional `skill?` / `version?`
2283
+ fields.
2284
+ - 7027149: Flow config-as-code: `POST /v1/flows/ensure` (non-executing, hash-first idempotent converge with dryRun planning, onConflict/release policies, and 409/422 conflict detection) and `GET /v1/flows/pull` (canonical definition + provenance), built on a generic ensure decision core shared with the agent ensure service. SDK gains `defineFlow` + `client.flows.ensure/pull`; `flows.upsert()` is unchanged and documented as the execute-flavored sibling (save-and-run) of the deploy-time ensure verb. The dashboards' flow integration-code generators add an "Ensure Flow (config-as-code)" example (defineFlow + flows.ensure snippet and the `/v1/flows/ensure` HTTP request) alongside the existing upsert example, whose copy now distinguishes save-and-run from converge. Adds the `flow_ensure_locks` scoped-name serialization table and a flow content-hash parity fixture corpus shared by the SDK and `@runtypelabs/shared`.
2285
+ - f07ceb8: Add marathon context-ledger artifact plumbing, derive tool-result context views without mutating saved history, and enable session context recall by default.
2286
+ - 7d398a2: Mid-session hard abort for marathon (Esc Esc). The SDK's `runTask` and `executeWithLocalTools` accept an `abortSignal` that cancels the in-flight SSE stream and ends the run gracefully (cost and tokens preserved, state persisted, status `paused`/resumable). In the marathon TUI, pressing Esc twice while the agent works interrupts the turn, restores any queued steering messages and composer draft into the checkpoint input, and lands at a "stopped" checkpoint where Enter exits or new instructions continue the task.
2287
+ - 92c6ae3: Marathon context ledger: window-base compaction and persistence fixes.
2288
+
2289
+ SDK: compaction now records a send-window base (`RunTaskState.contextWindowBaseIndex`). After a compaction, sessions send `[stored summary, messages since base, continuation]` and only re-compact when that window itself re-crosses the threshold — previously every post-threshold session re-summarized the whole task and dropped all recent raw turns from the send. The persist path appends exactly the new continuation message per session, fixing duplicated (and fidelity-degraded) history once replay trimming made the send view shorter than the source. Send-view derivation no longer deep-clones the full history per send, and hot-tail offload skips rewriting files that already exist. Offload reference markers now live in a shared `offload-markers` module with locale-pinned size formatting.
2290
+
2291
+ CLI: tree-log delta checkpoints no longer re-embed compaction summary text on every save (summary ledger entries are the durable copy; materialization and `/tree` navigation rebuild them from the chain). Repeated offloads of identical output record one artifact entry. `read_offloaded_output` verifies content against the recorded sha256 and warns on mismatch. Tools rebuilt after a `/tools` or sandbox checkpoint change keep their offload wrapping. The unused artifact GC stub (which would have deleted still-readable legacy offload files) was removed.
2292
+
2293
+ - 518018a: Replace Helicone with [models.dev](https://models.dev) as the build-time source of model metadata. The auto-generated catalog now carries real `contextLength`, `maxOutputTokens`, modalities, capability flags, knowledge cutoff, release date, and cached-token pricing — replacing the previous name-pattern estimates. Vercel AI Gateway stays authoritative for pricing.
2294
+
2295
+ New optional `ModelConfig` fields (all surfaced via `@runtypelabs/shared`): `knowledgeCutoff`, `releaseDate`, `inputModalities`, `outputModalities`, `supportsVision`, `supportsToolUse`, `supportsReasoning`, `inputCacheReadCostPer1kTokens`, `inputCacheWriteCostPer1kTokens`. These populate only on the verbose lazy-loaded catalog (`apps/api/src/lib/model-catalog/generated-metadata.ts`, a separate Wrangler module); the hot-path `generated-configs.ts` tuple format is unchanged so the main Workers bundle does not grow.
2296
+
2297
+ The `pnpm update-models` script also gains a disappearance classifier that aborts when a `DEFAULT_MODELS_FOR_NEW_ACCOUNTS` entry vanishes from upstream without being in `RETIRED_MODELS` / `DEPRECATED_MODELS`, preventing silent breakage of new-account onboarding.
2298
+
2299
+ Tag vocabulary is normalized on `'tool-use'` for tool-calling capability (matching Vercel AI Gateway passthrough tags and the model-advisory validator), and the dashboard "tools" capability filter now matches it. The `'playground'` tag (previously emitted from Helicone's `show_in_playground`) is no longer generated — no consumer read it.
2300
+
2301
+ - 6ecce93: Runtype Apps increment 1, PR 3: agent + developer deploy surfaces. Adds six MCP tools (`create_app`, `deploy_app_version`, `activate_app_version`, `list_apps`, `get_app`, `delete_app`) on both MCP servers (standard + Code Mode `runtype.*` methods), an SDK `client.apps` endpoint (incl. raw-zip `uploadVersion` and JSON `uploadVersionFiles`), a `runtype apps` CLI command family (`create`, `deploy <dir>`, `list`, `versions`, `activate`, `delete`), a JSON `{ files, filesBase64 }` upload variant on `POST /v1/apps/:id/versions` (the API zips server-side for surfaces that cannot stream binary bodies), and the `build-runtype-app` agent skill.
2302
+ - c7bad37: Cursor pagination for the Agent Skills list. `GET /v1/skills` now accepts `cursor`, `limit`, `direction`, and `includeCount` query params and returns the standard `pagination` envelope alongside `data` (additive; the status filter still works with cursors). The TypeScript SDK adds `skills.listPage()` for the full `{ data, pagination }` envelope while `skills.list()` keeps returning the plain array, and the dashboard Skills tab pages through skills with the standard cursor pagination footer.
2303
+ - 6e53e7f: Use typed model capability flags instead of tag/name heuristics. `GET /v1/model-configs/grouped` now returns `supportsVision`, `supportsToolUse`, and `supportsReasoning` per group (derived from the typed ModelConfig flags populated by the models.dev pipeline, with capability tags as fallback for tag-only sources, OR-merged across a group's provider entries). The dashboard add-model capability filters and the settings page tools/reasoning filters read these flags (tags remain a fallback), replacing the hand-coded provider/model-name heuristics. The model-advisory `MODEL_CAPABILITY_MISMATCH` checks also accept the typed flags.
2304
+
2305
+ ### Patch Changes
2306
+
2307
+ - df3a66d: Dashboard provenance guardrail for config-as-code agents: agents whose live config was last converged from a code definition (`lastModifiedSource: 'sdk'`, stamped only by `POST /v1/agents/ensure`) now show a "Managed in code" badge in the agent editor and edit slide-over, and saving dashboard edits to such an agent asks for confirmation first (the next deploy may overwrite the edit). Also documents the existing `lastModifiedSource` and `configHash` response fields in the agent detail/mutation OpenAPI schemas (they were already on the wire via the row spread).
2308
+ - 2bb37c4: Skill proposal review enrichment: `GET /v1/skill-proposals` now resolves the manifest's capability IDs (flows, agents, tools) to tenant-scoped names via `resolvedCapabilityNames`, and resolves the proposing execution to the authoring agent via `proposingAgent`. IDs that do not resolve inside the reviewer's account are surfaced with a null name as a review signal instead of being dropped. The dashboard proposals queue renders the named capability grants inline (with a "Not found in this account" flag for unresolved IDs) and shows the proposing agent's name in place of the raw execution typeid.
2309
+ - 4e3999f: Dashboard SPA build-time cuts and hygiene: skip the duplicate shared build during Workers Builds installs (SKIP_PREPARE=1), env-gate tsup .d.ts generation (TSUP_SKIP_DTS=1, set by the SPA build:cf scripts), disable Vite's gzip-size report pass, switch shiki to the fine-grained core bundle with the JavaScript regex engine and an explicit grammar set in both dashboards (~274 fewer emitted chunks, no oniguruma wasm), restore the SPA ESLint baseline to the legacy dashboard's effective rule set, wire up the SPA vitest suite, and make the SDK's stream-utils import style consistent to silence Rollup's mixed static/dynamic import warning.
2310
+
2311
+ ## 4.9.0
2312
+
2313
+ ### Minor Changes
2314
+
2315
+ - 96ec88a: Mid-run steering queue for marathon (Pi-style): press Enter while the agent is working to open a composer and queue a steering message. Queued steers end the in-flight session at the next local-tool pause and are delivered at the start of the next session; Tab toggles delivery to "after all work" (follow-up). `runTask` gains additive `getQueuedUserMessages` / `hasQueuedUserMessages` options.
2316
+
2317
+ ## 4.8.1
2318
+
2319
+ ### Patch Changes
2320
+
2321
+ - 15b4a2f: Add a native Granola integration. Connect Granola from Settings > Integrations by pasting a `grn_` API key (verified live against Granola's public API before storage). Agents, flows, and transform-data code steps gain five read-only `builtin:granola:*` tools (list/get meeting notes, transcript, folders, and bounded client-side search). Ships a "New Granola Meeting Notes" flow template that pairs with a schedule to poll for new or updated notes using the `{{_schedule.lastRunAt}}` cursor with overlap. SDK types regenerated for the new `/v1/integrations/granola/install` endpoint.
2322
+
2323
+ Granola credential resolution is explicit-only: each `builtin:granola:*` tool instance must name its credential in `toolConfigs[toolId]` (`GranolaToolConfig`) as either `apiKey` (a `{{secret:NAME}}` reference to a secret holding a `grn_` API key, enabling per-tenant keys; literal keys are rejected) or `integrationId` (pin one connected workspace, the explicit way to use the account's connected Granola). There is no silent fallback to the org's workspace, so adding a Granola tool can never unintentionally expose the whole org's meeting corpus.
2324
+
2325
+ - 698edda: Regenerated OpenAPI types: the GET /v1/logs/stats response schema now documents the existing cachedAt field (string when served from the 30s stats cache, null for fresh responses).
2326
+
2327
+ ## 4.8.0
2328
+
2329
+ ### Minor Changes
2330
+
2331
+ - 8aeda2c: Add internal builder docs to the FPO/template model
2332
+
2333
+ Templates and products can now carry non-user-facing builder documentation that
2334
+ captures intent: why a template is shaped the way it is, design choices, best
2335
+ practices, and implementation notes.
2336
+ - New shared `internalDocsSchema` (`content` markdown body, `tags` for indexing,
2337
+ and a forward-compatible `resources` array seeding a broader template
2338
+ knowledge/resource system). Added to the FPO `product` object and the
2339
+ FPO-template top level.
2340
+ - Persisted on `products.spec.internalDocs` and accepted by the products
2341
+ create/update routes (`null` clears it).
2342
+ - Editable from the product edit panel in the architecture viewer. The field is
2343
+ intentionally excluded from the agent-facing public type snapshots, so it is
2344
+ not exposed in end-user surfaces by default.
2345
+
2346
+ ### Patch Changes
2347
+
2348
+ - 982da6c: Agent-supplied approval reasons. When a tool call requires approval, the engine now injects a reserved optional `_approvalReason` parameter into the model-visible tool schema so the agent can justify the call. The value is stripped before the tool executes and surfaced to the approver as `reason` on `agent_approval_start` / `step_await` events, in Slack/Telegram approval prompts, and in the dashboard approval card. Enabled by default for approval-gated tools; opt out with `tools.approval.requestReason: false`. Tools that already define `_approvalReason` in their own schema are left untouched.
2349
+
2350
+ ## 4.7.1
2351
+
2352
+ ### Patch Changes
2353
+
2354
+ - 2c12987: Close six SSE contract-field gaps surfaced by a new drift audit (same class as `step_complete.stopReason`): fields emitted on the public wire (and read by consumers) that the canonical SSE schema omitted, so the public OpenAPI components and generated SDK types under-represented the stream. All additions are optional, so existing frames still validate.
2355
+ - `step_start.outputVariable` — the step output-variable name the dashboard reads to unwrap the `{ [outputVariable]: <rendered> }` envelope at render time.
2356
+ - `flow_complete.flowName` — emitted by the API raw-write paths; now declared for wire-superset parity with `flow_start`.
2357
+ - `agent_error` / `agent_reflection` / `agent_skill_loaded` / `agent_skill_proposed` `.timestamp` — the runtime `AgentEventEmitter` injects an ISO timestamp on these events; reconciled into the shared schema per the runtime↔shared wire-contract rule.
2358
+
2359
+ Regenerates the public/internal OpenAPI spec and the generated TS + Python SDK types. Adds a `contract-field-drift-hunter` agent that audits the API surface for this drift class.
2360
+
2361
+ ## 4.7.0
2362
+
2363
+ ### Minor Changes
2364
+
2365
+ - 5d3eada: Harden flows Dev Mode SDK code generation against drift by deriving parser and completions from SDK metadata and adding SDK builder coverage for all generated step methods.
2366
+ - 6730e12: Complete the public SSE/OpenAPI contract for Persona Runtype streams (#3975):
2367
+ - Add the optional `stopReason` field (`end_turn` | `max_tool_calls` | `length` | `content_filter` | `error` | `unknown`) to the canonical `step_complete` SSE schema, so the public `FlowSSEEvent` component and generated SDK types document the model finish reason that prompt steps already emit on the wire.
2368
+ - Re-export stable public stream-event aliases (`DispatchEvent`, `FlowStreamEvent`, `AgentStreamEvent`, `StreamEventOf`) from the SDK barrel, tied to the generated OpenAPI components so they drift only with the public spec.
2369
+ - Add public OpenAPI coverage for the remaining browser client-token runtime endpoints — `POST /v1/client/init`, `POST /v1/client/resume` (SSE), and `POST /v1/client/feedback` — reusing the runtime validation schemas so the docs and implementation cannot drift.
2370
+
2371
+ ### Patch Changes
2372
+
2373
+ - 6efd928: Fix a race in `/client/chat` interrupt handling where a slower, older request could reclaim the active turn slot from a newer interrupting turn. `markClientChatTurnStarted` now claims the slot with a compare-and-swap on the turn's arrival timestamp (captured at request entry), so a stale request whose write lands late can no longer restore its turn id and defeat the interrupt.
2374
+
2375
+ ## 4.6.1
2376
+
2377
+ ### Patch Changes
2378
+
2379
+ - cf15960: Improve dashboard responsiveness with persisted safe query caching, lazy analytics loading, optimistic list actions, and batched product editor context loading.
2380
+
2381
+ ## 4.6.0
2382
+
2383
+ ### Minor Changes
2384
+
2385
+ - e7eefe0: Agent memory: thread `endUser` identity through the dispatch / agent-execute / SDK surface so templates like `{{_endUser.id}}` resolve for per-end-user memory sharding (distinct from `_user`, the Runtype account holder). `endUser` is exposed as `{{_endUser.*}}` in template variables on both the agent and flow execution paths, and is carried into the memory profile resolution so multi-tenant SaaS callers can shard long-term memory per end-user. Also persists agent `config.memory` on create/update and adds the agent-editor memory config state plumbing.
2386
+
2387
+ ## 4.5.0
2388
+
2389
+ ### Minor Changes
2390
+
2391
+ - 55eb091: Add agent self-service coverage for OpenAI-compatible (BYOK) model endpoints across MCP, the SDK, and Code Mode. Agents can now connect a custom OpenAI-compatible endpoint, discover its models, and register them without the dashboard.
2392
+ - **MCP**: new `create_provider_key`, `list_provider_keys`, `update_provider_key`, `delete_provider_key`, `discover_provider_models`, and `sync_provider_models` tools, plus `create_model_config` extended to accept `provider: "generic-openai"` with a `base_url` and custom-model pricing/display fields (and normalizing the bare endpoint model id to the routed `generic-openai:` form the API expects). Mirrored across both MCP servers (`RuntypeApiClient` and `RuntypeInternalClient`) and Code Mode (`runtype.createProviderKey`, `discoverProviderModels`, `syncProviderModels`, etc.).
2393
+ - **SDK**: new `client.providerKeys` endpoint (`create`, `list`, `update`, `delete`, `discoverModels`, `discoverModelsForKey`, `syncModels`); `CreateModelConfigRequest`/`CreateProviderKeyRequest`/`UpdateProviderKeyRequest` extended for `generic-openai`.
2394
+ - These wrap existing BYOK-gated REST routes, so callers without the entitlement receive `402 BYOK_NOT_AVAILABLE`. Documentation updated for connecting an OpenAI-compatible endpoint via the API, MCP, and SDK.
2395
+
2396
+ ### Patch Changes
2397
+
2398
+ - 3f65083: Derive the `provider` enum on `CreateProviderKeyRequest` and `CreateModelConfigRequest` from the generated OpenAPI types instead of hand-maintaining the unions. This fixes a real drift: the hand-written unions accepted `'huggingface'` (which the API rejects) and omitted valid providers such as `vertex`, `vertex-anthropic`, `bedrock`, `tinfoil`, `braintrust`, `weaviate`, `vectorize`, `browser-rendering`, plus `model`/`runtime`/`mock`/`modelsocket` for model configs. Type-only change; no runtime behavior change.
2399
+
2400
+ ## 4.4.0
2401
+
2402
+ ### Minor Changes
2403
+
2404
+ - 59fd1b6: Remove the no-op `parallelCalls` tool-config flag (and the unused
2405
+ `parallelToolCalls` field on `ToolsConfig`).
2406
+
2407
+ `parallelCalls` was accepted and plumbed through the dispatch schema, SDK,
2408
+ MCP tool inputs, FPO schema, OpenAPI spec, config-merge, and runtime export,
2409
+ but it was never applied at the provider level — no executor ever passed a
2410
+ parallel-tool-use option to the model. Parallel tool calls therefore always
2411
+ used (and continue to use) each provider's default, which is parallel-enabled
2412
+ for current Claude/OpenAI/etc. models. Removing the dead config makes the
2413
+ surface honest: it no longer implies you can toggle a behavior that the
2414
+ platform never actually controlled.
2415
+
2416
+ Backward compatible at runtime: request schemas strip unknown keys, so an
2417
+ older client that still sends `parallelCalls` is accepted unchanged (the key
2418
+ is ignored, exactly as before). The only change is type-level — the field is
2419
+ gone from the SDK and API types.
2420
+
2421
+ ## 4.3.0
2422
+
2423
+ ### Minor Changes
2424
+
2425
+ - e133753: Add the optional `clientToolsPolicy` field to the SDK's `DispatchRequest` type.
2426
+
2427
+ Follow-up to allowing WebMCP `clientTools[]` on `/dispatch`: typed
2428
+ `@runtypelabs/sdk` callers can now pass `clientToolsPolicy.allowlist` to
2429
+ self-restrict which `origin: 'webmcp'` client tools are admitted, matching the
2430
+ shared `DispatchRequestSchema`.
2431
+
2432
+ ### Patch Changes
2433
+
2434
+ - 26401de: WebMCP: make parallel calls to the same local tool individually addressable on resume (core#3878, follow-on to #3870).
2435
+
2436
+ When a single model turn calls the same local tool more than once in parallel (the common "add A and B to my cart" case), the `flow_await` / `agent_await` / `step_await` events now carry a unique per-call `toolCallId` (the provider `tool_use` id). The `/resume` `toolOutputs` map can be keyed by that `toolCallId` so a client can resolve every pending call of one paused execution in a single resume, including multiple calls to the same tool. Keying by tool name still works (legacy contract; collapses same-tool parallel calls onto one slot). Resolution remains tolerant: outputs can arrive in any order across one or more `/resume` requests, and missing siblings re-pause.
2437
+
2438
+ ## 4.2.0
2439
+
2440
+ ### Minor Changes
2441
+
2442
+ - 52091ac: Bump `@daytonaio/sdk` to `^0.184.0` and let the Daytona deploy path tag sandboxes with labels + opt into server-side reaping.
2443
+ - **`@runtypelabs/sandbox`:** upgraded `@daytonaio/sdk` (`^0.27.1` → `^0.184.0`). Existing call sites (`create`/`get`/`delete`/`codeRun`/`executeCommand`/`getPreviewLink`/`setAutostopInterval`) remain compatible; we don't use the now-async-iterator `list()`. `DaytonaDeployOptions` gains optional `labels`, `autoStopInterval`, and `autoDeleteInterval`. Labels are applied at sandbox creation (new sandboxes only); `autoStopInterval` defaults to the existing 120 minutes; `autoDeleteInterval` (via `setAutoDeleteInterval`) is opt-in so callers can have leaked sandboxes self-reap.
2444
+ - **`@runtypelabs/api`:** `POST /api/tools/sandbox/deploy` now accepts optional `labels`, `autoStopInterval`, and `autoDeleteInterval` and forwards them to the Daytona executor. Default behavior is unchanged when they're omitted. The executor guards both intervals with `Number.isFinite` so a non-number from the untyped request body falls back to the default instead of silently leaving the sandbox with no auto-stop.
2445
+ - **`@runtypelabs/sdk`:** `DeploySandboxRequest` gains the same three optional fields so SDK/CLI callers can pass them in lockstep with the REST route.
2446
+
2447
+ This lets CI tag its Daytona sandboxes (e.g. `source: 'runtype-ci'`) for traceability and gives them a short auto-stop + immediate auto-delete so a killed run can't leak a live sandbox.
2448
+
2449
+ - 0aa81ed: `runWithLocalTools` now resolves parallel local tool calls in a single pause/resume cycle (core#3870 follow-up).
2450
+
2451
+ When a model turn requests several local (client-side / WebMCP) tool calls at once — e.g. "search for X **and** add SKU Y" — the server emits a `flow_await`/`step_await` per call and its `/resume` accepts every output in one request. The SDK previously processed only the last paused tool per cycle (last-writer-wins), so N parallel calls took N pause/resume round-trips. Both `RuntypeClient.runWithLocalTools` and the FlowBuilder's `runWithLocalTools` now collect the whole batch (keyed by tool name, merging `flow_await` + `step_await`), execute every handler concurrently, and resume once with all `toolOutputs`. Single-tool turns are unchanged; the merge of partial `flow_await`/`step_await` context is preserved.
2452
+
2453
+ ## 4.1.0
2454
+
2455
+ ### Minor Changes
2456
+
2457
+ - 88d04d6: Add `FlowBuilder.validate()` to the SDK (#10, slice 3 of the end-to-end
2458
+ type-safety initiative — the SDK-facing consumer of the shared flow validator).
2459
+
2460
+ `new FlowBuilder()…validate(client)` (and the bound `runtype.flow(name)…validate()`
2461
+ / `Runtype.flows.virtual()…validate()`) POSTs the prospective flow to the public
2462
+ validation endpoint (`POST /v1/public/flows/validate`) WITHOUT creating it, and
2463
+ returns the same `errors` / `warnings` / `recommendations` envelope the API,
2464
+ dashboard, and MCP `validate_flow` tool use. Structural issues, the upsert-record
2465
+ JSON foot-gun, undeclared-variable warnings, and sub-optimal model selections now
2466
+ surface at author time rather than at dispatch time. Authentication is optional;
2467
+ an authenticated client additionally runs account-scoped checks (referenced
2468
+ tools / flows / agents must exist + be owned), reported via `result.context`.
2469
+
2470
+ Implemented as the API-backed option (slice-3 option a), so the SDK stays
2471
+ **zero-dependency**: `validate()` reuses the client's existing `post<T>` transport
2472
+ (no new HTTP plumbing). The return type (`FlowValidationResult`) and `FlowValidationIssue`
2473
+ are **spec-derived** from `components['schemas']` in the generated OpenAPI types, so
2474
+ they cannot drift from the API contract. New public exports: `FlowValidationResult`,
2475
+ `FlowValidationIssue`, `FlowValidationClient`.
2476
+
2477
+ The `@runtypelabs/shared` bump is the regenerated `generated-sdk-reference.ts`
2478
+ (the SDK reference auto-extracts the public API surface, so the two new `validate`
2479
+ methods land in the MCP `runtype://types/sdk-reference` resource).
2480
+
2481
+ - ac87cbe: Type `DispatchRequest.flow.steps` instead of `Array<any>`.
2482
+
2483
+ `flow.steps` is now `FlowStepDefinition[]`, a step shape derived from the
2484
+ OpenAPI spec's `POST /v1/flows` request body, so its `type` discriminant is the
2485
+ canonical `FlowStepType` union (the 26 `FLOW_STEP_TYPES` from
2486
+ `@runtypelabs/shared`) rather than `any`. A malformed step — an unknown `type`,
2487
+ or a non-object element — is now a compile error in `dispatch()` payloads and
2488
+ the FlowBuilders. Two new public types are exported: `FlowStepType` and
2489
+ `FlowStepDefinition`.
2490
+
2491
+ Because the type is spec-derived, it cannot drift from the API:
2492
+ `pnpm generate:types` regenerates the underlying SDK types from the committed
2493
+ spec and `generate:types:check` fails CI on drift. A new compile-time guard
2494
+ (`flow-step-definition.type-assert.ts`, type-checked but not shipped) pins the
2495
+ "malformed step is a compile error" contract so a future regression toward
2496
+ `any` breaks the build.
2497
+
2498
+ Step `config` is still `unknown` at this layer (the API keeps step config loose
2499
+ at the schema layer and validates it imperatively for rich, field-level agent
2500
+ feedback). Compile-time per-type `config` typing — and typing the dispatch
2501
+ request body directly in the spec — is the remaining follow-up (#12(a) of the
2502
+ end-to-end type-safety initiative).
2503
+
2504
+ Dashboard (`patch`): three call sites that assemble dispatch payloads from
2505
+ dynamically-typed flow data (`product-generator-agent.ts`, `ai-flow-assistant-flow.ts`,
2506
+ `useStandaloneDispatchExecution.ts`) were updated to assert the SDK's narrowed
2507
+ step shape at the wire boundary — type-only adjustments, no behavior change.
2508
+
2509
+ Classified **minor**, not major: the runtime/wire contract is byte-identical
2510
+ (this is a compile-time-only tightening), `any` was an eslint-disabled escape
2511
+ hatch rather than a real contract, every valid step payload still compiles, and
2512
+ the primary consumers (`FlowBuilder` / `RuntypeFlowBuilder`) produce conforming
2513
+ steps. Only step literals that were already invalid per the API contract (a
2514
+ non-canonical `type`, a non-object element) become compile errors — the
2515
+ intended safety improvement.
2516
+
2517
+ ### Patch Changes
2518
+
2519
+ - 2711b97: Fix `flows.create()` step type: `CreateFlowRequest.flowSteps[].type` now uses the
2520
+ spec-derived `FlowStepType` (the full canonical `FLOW_STEP_TYPES` set) instead of a
2521
+ stale hand-written 5-value literal (`'prompt' | 'context' | 'condition' | 'output' |
2522
+ 'email'`, 4 of which were never real step types). Real step types like
2523
+ `'transform-data'` and `'conditional'` are now accepted, and the type is pinned by a
2524
+ compile-time assertion so it cannot drift from the API spec again.
2525
+ - 84ab7da: Consolidate the three `validate()` implementations (`FlowBuilder`,
2526
+ `ClientFlowBuilder`, `RuntypeFlowBuilder`) onto a single shared
2527
+ `validateInlineFlow()` helper. The saved-flow-by-id guard, the
2528
+ `POST /public/flows/validate` path, and the `{ name, steps }` body shape were
2529
+ duplicated verbatim across `flow-builder.ts` and `flows-namespace.ts`; they now
2530
+ share one implementation, so the validation contract can no longer drift between
2531
+ the two surfaces. Behavior is identical — error messages are byte-for-byte the
2532
+ same (each caller passes its own surface-specific remediation hint). Internal
2533
+ refactor only; no public API change.
2534
+
2535
+ ## 4.0.4
2536
+
2537
+ ### Patch Changes
2538
+
2539
+ - 2e3a776: Begin the converge-then-tighten path toward strict validation of steps nested
2540
+ inside `conditional` branches (the last unmigrated step type, #6 capstone).
2541
+
2542
+ Leg 1 (non-breaking, measure-first):
2543
+ - `setVariableConfigSchema` now accepts `value: null`, aligning the schema with
2544
+ the runtime normalizer and executor (which already permit it). Clearing a
2545
+ variable (`value: null`) is a legitimate idiom, commonly the else-branch of a
2546
+ conditional. The SDK's `SetVariableStepConfig.value` type is widened to match.
2547
+ - New read-only `Flow NestedStep Shape Written` PostHog event counts legacy
2548
+ markers (snake_case configs, `set-variable value:null`, legacy `true_steps` /
2549
+ `false_steps` branch keys, lenient envelopes, unknown types) on steps nested
2550
+ inside conditional branches per flow write. This is the decay watch that gates
2551
+ the eventual strict recursive-union tighten — no behavior change to what is
2552
+ persisted or validated.
2553
+
2554
+ See `docs/features/planning/2026-06-04-conditional-nested-step-strict-validation.md`.
2555
+
2556
+ ## 4.0.3
2557
+
2558
+ ### Patch Changes
2559
+
2560
+ - 8f11ab5: Normalize the generic REST request-validation error envelope. Every `.openapi()`
2561
+ route now returns the same coded `{ code, message, path, suggestion? }` detail
2562
+ shape (stable Runtype codes like `MISSING_REQUIRED_FIELD`, `INVALID_ENUM`,
2563
+ `OUT_OF_RANGE`) instead of raw Zod issues, so REST/SDK callers get the same
2564
+ enriched, machine-readable feedback that MCP previously had to reconstruct via
2565
+ `enrichErrorForAgent`. The four `defaultHook` sites (shared `createOpenAPIApp()`
2566
+ factory plus the hand-rolled product-api / quick-start / product-chat mappers)
2567
+ now route through a single shared `validationErrorResponse` helper. The MCP
2568
+ client now forwards the new `code` and `suggestion` fields through
2569
+ `enrichErrorForAgent`, so MCP-surface agents receive the same coded feedback.
2570
+ - edf2b2a: Register the OpenAPI error envelope and four other high-duplication schemas as
2571
+ shared `#/components/schemas/*` components, so routes emit one-line `$ref`s
2572
+ instead of inlining the full object at every use site:
2573
+ - `Error` — the 4xx/5xx error envelope (~1,291 copies → 1 definition)
2574
+ - `Pagination` — the cursor envelope on every paginated list response (12×)
2575
+ - `FlowValidationResult` / `FlowValidationIssue` — the flow-validation envelope
2576
+ and its issue item (the issue appeared 36× = 12 results × 3 arrays)
2577
+ - `RecordFilterCondition` — the record-filter `field/op/value` clause (12×)
2578
+
2579
+ Net: the committed spec artifacts shrink ~98k lines and the generated SDK types
2580
+ shrink ~14k lines, exposing reusable `components['schemas']['*']` types.
2581
+ Representation-only: runtime request/response bodies and documented shapes are
2582
+ byte-identical (verified zero semantic diffs across all 1,773 public + 2,004
2583
+ internal resolved schemas).
2584
+
2585
+ ## 4.0.2
2586
+
2587
+ ### Patch Changes
2588
+
2589
+ - d4ca2dd: Correct `createExternalTool` JSDoc to stop documenting `{{_internal.auth_token}}`, `{{_internal.user_id}}`, and `{{_internal.org_id}}` as available auth variables. These `_internal.*` variables are never populated at runtime and are actively blocked by the execution engine; the docs now point users to secret references (`{{secret:NAME}}`) for credentials.
2590
+
2591
+ ## 4.0.1
2592
+
2593
+ ### Patch Changes
2594
+
2595
+ - a7cb0d9: Add a `slow` fallback trigger: a per-model-call total wall-clock duration cap that fails a slow-but-not-stalled model call over to its configured fallback model. A model that streams continuously but slowly (e.g. ~24 tok/s) never trips the existing TTFT/idle stall watchdogs (an idle timer resets on every chunk), so a step can now opt in with `errorHandling.triggers: [{ type: 'slow', afterMs }]` (suggested default 45s) plus a `model` fallback.
2596
+
2597
+ The cap aborts-and-falls-over **only while the partial work is safely discardable**, gated two ways: it disarms permanently the moment a tool chunk passes (a side effect may have fired — re-running could double it), and, in a streaming-to-client dispatch, the moment a delta is flushed live (falling over would double-emit). In a non-streaming (`streamResponse: false`) dispatch the buffered partial is discardable, so the cap can fire mid-generation — the case it exists for. A duration-cap abort skips the same-model retry and routes straight to the configured fallback (a slow model is likely slow again).
2598
+
2599
+ `withModelCallResilience` gains optional `maxDurationMs` / `streamingToClient` / `isToolChunk` (off by default — fully opt-in, zero change to existing calls) and a `duration_cap` `ModelStreamStallError` phase. The runtime honors the trigger too, but with no Layer-2 fallback chain a cap abort surfaces as a clean `StepExecutionError`. The flow validator rejects a `slow` trigger with a non-positive `afterMs` (`SLOW_TRIGGER_INVALID_AFTER_MS`), and the error-handling editor preserves a saved slow trigger's `afterMs` across save.
2600
+
2601
+ ## 4.0.0
2602
+
2603
+ ### Major Changes
2604
+
2605
+ - 55d5817: SDK: alias four more entity types to the generated OpenAPI types, and remove a dead endpoint.
2606
+
2607
+ **Breaking (types only — runtime behavior unchanged):** `Surface`, `ScheduleRun`, `ProviderApiKey`, and `BuiltInTool` now re-export from the committed OpenAPI spec instead of hand-mirrored interfaces, so they reflect what the API actually returns:
2608
+ - `ProviderApiKey.id` is now `string` (was `number`); `provider` widens from a literal union to `string`; `keyPreview`/`lastUsedAt` become `| null`; gains optional `settings`.
2609
+ - `BuiltInTool.category` widens from a literal union to `string`; gains optional `modelCompatibility`; `parametersSchema` is an opaque object.
2610
+ - `ScheduleRun` gains `totalRecords`/`processedRecords`/`failedRecords` and drops its `[key: string]: unknown` index signature.
2611
+ - `Surface.inbound` is now a typed (Slack) object rather than `Record<string, unknown>`.
2612
+
2613
+ Consumers reading dropped/renamed fields were already getting `undefined` at runtime; the types now match the wire.
2614
+
2615
+ **Breaking:** removed `tools.getExecutions()` and the exported `ToolExecution` type. They called `GET /tools/{id}/executions`, which has **no route handler** and 404s in production. The never-mounted dashboard `ToolExecutionHistory` component and its API test (which asserted the nonexistent route) are removed alongside.
2616
+
2617
+ - 89b107f: feat(sdk): alias Flow/FlowStep/Tool from the OpenAPI spec; fix flow + tool response-schema drift
2618
+
2619
+ Continues the OpenAPI codegen swap (after #3645/#3665). Three coupled changes that all touch `entities.ts` + the regenerated spec + SDK `types.ts`:
2620
+ - **Flow phantom fix (API spec accuracy):** the flow `GET`/`LIST`/`POST` response schemas advertised a `status` field the handlers stopped returning (removed in #3404), and the shared detail schema carried a `draftVersionId` that only the `PUT` handler returns. Removed `status` everywhere and split a dedicated `UpdatedFlowResponseSchema` (`FlowDetailSchema.extend({ draftVersionId })`) for the update route so `GET`/`POST` no longer promise it.
2621
+ - **Tool schema split:** `GET /v1/tools/{id}` never attaches `validation` (only create/update/convert do, when custom-tool code produces warnings). Split `ToolDetailSchema` (base, GET) from `ToolWithValidationSchema` (create/update/convert).
2622
+ - **SDK aliases:** `Flow`, `FlowStep`, and `Tool` are now `paths[...]`-derived from the generated spec instead of hand-written interfaces. Added a `FlowListItem` alias for the lighter `GET /v1/flows` list item and retyped `flows.list()` to `PaginationResponse<FlowListItem>` (it was incorrectly typed as the detail shape). `FlowListItem` is named to avoid colliding with the flow-execution `FlowSummary` already exported by `flow-builder.ts`.
2623
+
2624
+ This is a **major** bump for `@runtypelabs/sdk` because the generated shapes are looser/more accurate than the old interfaces: the GET flow detail no longer carries `status`/`description`, flow steps have optional `name`/`order`/`enabled`/`config`, the tool GET shape drops `validation`, and tool `config`/`parametersSchema` are typed as the JSONB records the API actually returns. Dashboard and CLI consumers are updated in lockstep.
2625
+
2626
+ - e1a4368: Alias the SDK `RuntypeRecord` type to the generated OpenAPI contract (PR3 of the
2627
+ OpenAPI codegen swap, following #3665 / #3670).
2628
+
2629
+ `RuntypeRecord` is now derived from `GET /v1/records/{id}` and a new
2630
+ `RecordListItem` type from `GET /v1/records`, replacing the hand-written
2631
+ interface that had drifted from what the API actually returns:
2632
+ - **`availableFields` is list-only.** It is computed per-row from metadata keys
2633
+ and returned only when `includeFields=true`, so it now lives on
2634
+ `RecordListItem` (returned by `records.list()` / `records.getExample()`), not
2635
+ on the GET/create/update `RuntypeRecord`. Mirrors the `FlowListItem` split.
2636
+ - **`metadataSchema` typing corrected.** The hand-written `keyMapping` /
2637
+ `keyTypes` / `sizeKb` / `keyCount` / `updatedAt` fields never existed on the
2638
+ wire — the trigger-computed JSONB summary is returned verbatim with snake_case
2639
+ keys. It is now typed as `{ keys: string[] }` (the only field consumers read)
2640
+ plus a catchall for the remaining computed fields.
2641
+ - **`metadataLabels`** tightened from `Record<string, unknown>` to
2642
+ `Record<string, string>` (its real shape).
2643
+
2644
+ API-side, the records list response schema now documents `availableFields` via a
2645
+ `RecordListItemSchema`, and `RecordSchema` accurately describes the
2646
+ `metadataSchema` / `metadataLabels` shapes.
2647
+
2648
+ ### Minor Changes
2649
+
2650
+ - a0b30f3: Add `Runtype.skills.*` — a hand-written SDK resource for the Agent Skills admin/control plane. Covers `create`, `get`, `list`, `update` (append draft version), `delete`, `listVersions`, `publishVersion`, `import`, `bind`, `unbind`, `listBindings`, plus a nested `skills.proposals` review queue (`list`, `approve`, `reject`). The deployed-agent `propose_skill` data plane is intentionally not exposed. Also adds generic `put`/`delete` request helpers to `RuntypeClient` (the skills surface uses real `PUT`/`DELETE` verbs).
2651
+
2652
+ ### Patch Changes
2653
+
2654
+ - c3536a2: Upgrade A2A (Agent-to-Agent) protocol support to spec v1.0.
2655
+
2656
+ **Surface (we expose agents as A2A): 1.0 only.** The agent card now advertises
2657
+ `supportedInterfaces[]` + `securitySchemes`, the JSON-RPC endpoint uses
2658
+ PascalCase methods (`SendMessage`/`SendStreamingMessage`/`GetTask`/`CancelTask`/
2659
+ `SubscribeToTask`, plus the new `ListTasks`), Parts use the unified 1.0 oneof
2660
+ (`text`/`raw`/`url`/`data`, `mediaType`, no `kind`), task states are
2661
+ SCREAMING*SNAKE (`TASK_STATE*\*`), streaming events are `StreamResponse`
2662
+ wrappers (`statusUpdate`/`artifactUpdate`, no `final`), and errors use the
2663
+ `google.rpc.Status`form. Wire encoding is centralized in a new`apps/api/src/lib/a2a-wire/v10.ts` serializer. (No production A2A surface had
2664
+ ever been invoked, so the hard cut breaks no existing consumer.)
2665
+
2666
+ **Import / federation (we consume external A2A agents): supports both 0.3 and
2667
+ 1.0.** External agent cards are parsed against a dual-version schema; an
2668
+ interface selector prefers a 1.0 JSON-RPC interface and falls back to the 0.3
2669
+ top-level `url`. The external-agent proxy and the `runA2A` runtime tool emit
2670
+ the envelope matching the negotiated version (`A2AToolConfig.protocolVersion`,
2671
+ default 0.3).
2672
+
2673
+ - 8fefe6f: Agent Skills (admin plane): expose the `enable-agent-skills` flag to clients via `GET /v1/users/profile` (`features.enableAgentSkills`) so the dashboard can gate its Skills nav, and add a dedicated `GET /v1/skills/{id}/versions` endpoint for listing a skill's version history. Regenerated the public/internal OpenAPI artifacts and the SDK's generated types.
2674
+ - aa97266: Add an `empty-output` fallback trigger and terminal `message` fallback to prompt/agent error handling. Fallbacks can now fire when a model finishes successfully but returns no visible text (the "thinking" model reasoning-spiral failure), not just on error. `errorHandling.triggers` is an array (`error` | `empty-output`, defaulting to `[{type:'error'}]`), and a `{type:'message', message}` fallback yields a graceful author-controlled reply when retries/model swaps still come back empty. The product generator agent opts in (retry → reliable non-thinking model → synthesized message) so Kimi K2.6 empty answers no longer surface as blank chat bubbles.
2675
+
2676
+ The error-handling editor (used by both flow prompt steps and now the agent editor sidebar) exposes the new options: toggles for which conditions run the fallback chain (errors / empty replies) and a "Fixed message" fallback type. Agents gain a dedicated "Error handling" section that reuses the same modal.
2677
+
2678
+ The SDK's inlined error-handling types gain `MessageFallback`, the `triggers` field on `PromptErrorHandling`, and `FallbackTrigger`; the `FallbacksExhaustedEvent` type is corrected to match the wire event (`fallback_exhausted`, optional `finalError`, new `reason`).
2679
+
2680
+ - 42979fc: Add a `product-generator-model` Flagship string flag that selects which model the dashboard's product generator dispatches with (`kimi-k2.6` default, `claude-sonnet-4-6`, `gpt-5.5`, `claude-opus-4-8`). The flag is evaluated per user/org by the API and surfaced via `GET /v1/users/profile` as `features.productGeneratorModel`; the dashboard resolves it before dispatch and falls back to `kimi-k2.6` on any lookup failure.
2681
+ - af80a8a: Add Agent Skills — versioned, loadable context bundles (Anthropic SKILL.md +
2682
+ a `runtype:` capability extension) that deployed Runtype agents load on demand.
2683
+ - Two-plane governance: admin/control authoring (REST/SDK/MCP, scopes-only) vs
2684
+ deployed-agent self-authoring via `propose_skill` (review-by-default, with a
2685
+ human-set, agent-unreachable auto-publish opt-out scoped to the proposing
2686
+ agent).
2687
+ - Runtime: `skill:<slug>` virtual tools, stateless `runSkill` (body-as-result +
2688
+ capability activation through the existing tool-discovery channel), the
2689
+ capability-load approval gate reusing `ApprovalRequiredError`, and the
2690
+ `agent_skill_loaded` / `agent_skill_proposed` SSE events.
2691
+ - v1 capability scope: `flowIds`/`agentIds`/`toolIds`/`inlineTools`
2692
+ (`mcpServers` rejected at write, still parsed losslessly on import).
2693
+
2694
+ ## 3.0.0
2695
+
2696
+ ### Major Changes
2697
+
2698
+ - 53bfe49: Generate SDK types from the committed OpenAPI 3.2 spec via `openapi-typescript`, replacing hand-mirrored types. Adds `pnpm generate:types` (+ `:check` drift gate, wired into CI).
2699
+
2700
+ **Breaking (types only — runtime behavior unchanged):** several public types now reflect what the API actually returns, so over-declared fields are removed and some become optional:
2701
+ - `StreamEvent` and its members derive from the spec's `FlowSSEEvent` union — fields the API may omit (e.g. `StepCompleteEvent.name`, `executionTime`) are now optional, and the deprecated fallback event aliases (`FallbackFailEvent`, `FallbackSuccessEvent`, `FallbacksInitiatedEvent`) map onto the canonical discriminants (`fallback_exhausted`, `fallback_complete`, `fallback_start`).
2702
+ - Entity types re-export from the spec: `Prompt` (drops `isStreamed`/`tools`/`inputVariables`), `UserProfile` (drops `firstName`/`lastName`/`preferences.defaultModel`), `IntegrationTool` (`parametersSchema` → `parameters`), plus `Secret`, `Conversation`, `ConversationListItem`, `AgentVersionDetail`, `FlowVersionDetail`, `Integration`, `ModelUsageDetail`, `Schedule`.
2703
+
2704
+ Consumers reading removed fields were already getting `undefined` at runtime (the API never returned them); the types now match reality.
2705
+
2706
+ ## 2.2.0
2707
+
2708
+ ### Minor Changes
2709
+
2710
+ - f676234: Add typed SDK endpoint classes for resources the client previously lacked: `secrets`, `schedules` (CRUD + pause/resume/run-now + runs/stats), `surfaces`, `conversations`, `logs` (query/stats/trace), `agentVersions`, `flowVersions`, `integrations`, and `billing`. Each is exposed as a typed property on `RuntypeClient`, bringing the SDK closer to parity with the API surface used by the CLI.
2711
+
2712
+ ### Patch Changes
2713
+
2714
+ - 75c7cbd: CLI consistency polish: standardize output and pagination flags, bump Node engine
2715
+ - Unify the `--json` flag description to `Output as JSON` across commands and the
2716
+ root program (semantically distinct variants on `tail`, `agents task`, and
2717
+ `persona init` are intentionally preserved).
2718
+ - Standardize pagination flags on list commands whose API supports cursor
2719
+ pagination: `--limit <n>` + `--cursor <cursor>`, with an actionable
2720
+ "fetch the next page with `--cursor …`" hint when more results exist.
2721
+ - Added to: `flows`, `prompts`, `records`, `agents`, `schedules`,
2722
+ `conversations`, `tools`, `products`.
2723
+ - `--limit` has no forced default — when omitted, the API's own page size
2724
+ applies (avoids silently shrinking existing default result counts).
2725
+ - `conversations`/`tools` previously printed a dead-end "pass ?limit and
2726
+ ?cursor" hint without exposing the flags; they now work.
2727
+ - Add shared `buildListParams` / `printNextCursorHint` helpers in `lib/output.ts`
2728
+ (normalizing the `nextCursor`/`cursor` and `hasMore`/`hasNextPage` response
2729
+ variants) and adopt them in `surfaces` and `logs`, replacing hand-rolled query
2730
+ builders and inline hints (also fixes a `surfaces` hint that rendered a trailing
2731
+ space when no cursor was present).
2732
+ - Bump `engines.node` from `>=18.0.0` to `>=22.0.0` for `@runtypelabs/cli`,
2733
+ `@runtypelabs/sdk`, `@runtypelabs/ink-components`, and
2734
+ `@runtypelabs/terminal-animations`. Node 18 is EOL; 22 is the current LTS. (The
2735
+ repo root requires `>=24`, but these are published/consumer-installed packages,
2736
+ so the floor is held at the current LTS rather than the dev-toolchain version.)
2737
+
2738
+ - 3b357cc: Route all CLI API access through the SDK client instead of a hand-rolled raw HTTP client. Every command now constructs a configured `@runtypelabs/sdk` client via `createCliClient`, using typed endpoint methods (e.g. `client.flows.list()`) where available and the SDK's generic methods elsewhere. This makes the SDK the CLI's single source of truth for the API surface, so the CLI stops drifting behind the API, and a new `runtype/cli-use-sdk` lint rule prevents reintroducing a parallel raw client. The CLI client disables request timeouts by default for long-running executions via the SDK's new `timeout: null` support. Also fixes two latent bugs surfaced during the migration: `flows run --no-stream` now correctly requests a non-streamed response (`options.streamResponse`), and `flows get` displays the step count from the actual `flowSteps`/`stepCount` response fields.
2739
+
2740
+ ## 2.1.1
2741
+
2742
+ ### Patch Changes
2743
+
2744
+ - 3b6779b: Migrate WebMCP polyfill from navigator.modelContext to document.modelContext per Chrome 150 spec change (WebML CG Issue 173 / PR #184)
2745
+
2746
+ ## 2.1.0
2747
+
2748
+ ### Minor Changes
2749
+
2750
+ - 7d37b2f: Add `dispatch.clientTools[]` — a per-dispatch wire field for client-executed
2751
+ tools (WebMCP page tools and SDK locals).
2752
+ - Server validates the envelope (`webmcp:` prefix is server-applied,
2753
+ ~64KB budget, duplicate / regex / shape checks) and merges into the
2754
+ prompt-step tool set with precedence `saved < runtimeTools < clientTools`.
2755
+ - SDK extends `runWithLocalTools` with `{ scope?: 'turn' | 'session' }`.
2756
+ Default `'session'` is back-compat. `'turn'` auto-builds
2757
+ `dispatch.clientTools[]` from the handler map.
2758
+ - Surface model gains an opt-in `chat.webmcp.{enabled, allowlist,
2759
+ requireConfirmFor}` block. Default off; dashboard UI is Phase 4.
2760
+
2761
+ Complementary to in-engine `resolveTools`
2762
+ (`packages/runtime/src/engine/agent-engine.ts:687`): `resolveTools` is
2763
+ per-iteration inside one agent loop; `clientTools[]` is the wire-side
2764
+ counterpart that arrives per-dispatch.
2765
+
2766
+ ## 2.0.1
2767
+
2768
+ ### Patch Changes
2769
+
2770
+ - 89dc476: Preserve reasoning content (Anthropic extended-thinking signatures, Gemini thought signatures) across the platform-key proxy round-trip. Previously, when the runtime ran multi-round tool loops over `keys.mode: "platform"`, the assistant's round-1 `ReasoningPart` was silently dropped on round 2's POST body — breaking Anthropic extended thinking because the model requires its signed thinking block to ride alongside subsequent `tool_use` content. Now:
2771
+ - `StreamChunk.reasoningProviderOptions` carries the signature on the `reason_complete` SSE chunk. The capture lives in a single shared helper (`buildReasonCompleteChunk` in `stream-helpers.ts`) used by `processStandardStreamEvent` and the four hand-written executors (cloudflare-gateway, mixlayer, togetherai, workers-ai).
2772
+ - `MessageContent` gains a `reasoning` variant at every schema site: model-execution, runtime, shared dispatch, shared feedback-types, shared runtime-proxy (newly extracted), the api dispatch / schedules / client routes (consolidated onto the shared `MessageContentSchema`), the MCP `execute_agent` tool input, the public `DispatchChatMessage` agent-facing type snapshot, and the runtime's exported `PromptMessage` interface.
2773
+ - The runtime-proxy schema moved from `apps/api/src/routes/runtime-proxy.ts` to `@runtypelabs/shared/api-schemas/runtime-proxy.ts` so the cross-package parity test can validate proxy ↔ runtime ↔ dispatch in one place.
2774
+ - `PlatformKeyProxyLanguageModel`'s `translateNonSystemMessage` and `translateChunkToV2Parts` now emit reasoning parts in both directions, with provider signatures verbatim.
2775
+ - `buildMessagesArray` reconstructs assistant messages with reasoning **before** tool-call (Anthropic ordering rule); `applyTemplateToContent` skips template substitution on reasoning text (would invalidate the signed payload); `SecretLeakGuard.scrubValue` is applied to reasoning text to prevent secrets in chain-of-thought leaking through the proxy.
2776
+ - Tool-call and tool-result `providerOptions` (Anthropic `cache_control`, Gemini `thought_signature`) now round-trip through the proxy schema.
2777
+ - The dispatch route's pre-validation message filter (`hasNonEmptyMessageContent`) accepts `reasoning` (and `file`) variants so legitimate multi-modal turns are no longer silently dropped before Zod validation runs.
2778
+
2779
+ ## 2.0.0
2780
+
2781
+ ### Major Changes
2782
+
2783
+ - dc704fb: Remove the unused `status` lifecycle field from Flows and Agents (Option A from the status-field planning doc). The field gated nothing at runtime — dispatch always ran the live config row — and conflated lifecycle and draft/publish concerns. Dropped the `flows.status` and `agents.status` columns (and their now-redundant indexes), removed `status` from the REST list/create/update routes, the SDK `Flow` type, the MCP create/update agent + flow tools and list filters, the FPO agent schema, and the dashboard status pill/selectors.
2784
+
2785
+ Breaking: API responses for flows/agents no longer include `status`; the create/update endpoints and MCP tools no longer accept it; `Flow.status` is removed from `@runtypelabs/sdk`. Surface status, product status, and execution/run status are unaffected. Draft vs. published is now owned entirely by the versioning system.
2786
+
2787
+ ### Minor Changes
2788
+
2789
+ - fed76a8: Add a first-class `ownerId` field to conversations and dispatch records. `POST`/`PUT /v1/conversations` and dispatch `record` inputs accept a top-level `ownerId` (stored as `metadata.ownerId`, overriding any nested value); `GET /v1/conversations?ownerId=<id>` filters the list server-side by owner; and conversation responses echo `ownerId` as a top-level field. The SDK's `DispatchRequest.record` gains an `ownerId` field. Enables multi-tenant apps to scope conversations per end-user under a single shared API key.
2790
+ - 102e70b: CSV record upload now supports a constant type. `POST /records/upload-csv` accepts a new optional `typeValue` form field; when set, every imported record receives that type and the header lookup for `typeColumn` is skipped. The dashboard upload dialog gains a "From column" / "Constant" toggle on the Type field, defaulting to "Constant" when no type-like column is auto-detected. The SDK (`records.uploadCsv`) and dashboard API client (`uploadRecordsCsv`) gain a matching optional `typeValue` parameter; `typeValue` takes precedence when both `typeColumn` and `typeValue` are supplied.
2791
+ - 95df762: Make flow step IDs stable across saves and let "Test step" replay against unsaved edits. Previously every `PUT /v1/flows/:id` regenerated all step IDs, which broke the dashboard "Test step" modal (stale id → 404) and orphaned prior `flow_step_results`. Step IDs supplied by the client are now preserved; the server only mints an ID when none is provided. Duplicate IDs within a flow are rejected. `POST /v1/dispatch/test-step` accepts an optional inline `step` config so the dashboard can iterate on unsaved edits without saving the flow first.
2792
+
2793
+ ### Patch Changes
2794
+
2795
+ - cd6d918: Address code-review findings on the Claude Managed eval adapter:
2796
+ - Surface eval streaming path now passes `billing` to `adapter.runOnVariant` so platform-key token spend is recorded in Schematic instead of running free.
2797
+ - Eval variant agents are now created with `PLATFORM_ANTHROPIC_KEY` when available so the daily orphan sweeper can reach them regardless of which user spawned the eval. The resolved key is persisted on the `EvalVariantHandle` so archive paths (including the batch DO's terminal cleanup) succeed even when the underlying agent row was deleted mid-batch.
2798
+ - SDK `EvalEndpoint.runVirtualEval` type signature now exposes `agentId` and per-config `claudeManagedOverride` so SDK callers can target Claude Managed agents without casting.
2799
+ - `/eval/stream` route now uses the shared `loadAndGateClaudeManagedAgent` helper instead of re-inlining the six-gate check.
2800
+ - The `claudeManagedOverride` Zod schema in product-surface-eval is extracted to a single constant shared between the realtime and batch request schemas.
2801
+ - `CLAUDE_MANAGED_SYNTHETIC_STEP_ID` is now imported from `agent-internal-executor` in `virtual-eval-executor` and `product-surface-eval` instead of being redeclared / hardcoded.
2802
+
2803
+ ## 1.24.2
2804
+
2805
+ ### Patch Changes
2806
+
2807
+ - bade11f: External A2A agent enhancements: per-call `skillId` override, A2A 0.3.0 `contextId` threading, and caller-supplied `metadata` merge.
2808
+
2809
+ `ExecuteAgentOptions.externalAgent?: { skillId?; contextId?; metadata? }` — pass a per-call skill override (wins over the cached agent card resolution), a conversation handle to resume an existing A2A conversation, and arbitrary metadata that merges into `params.metadata` alongside the reserved `skill` / `system` keys.
2810
+
2811
+ `AgentCompleteEvent.externalAgent?: { contextId?; taskId? }` and `AgentApprovalStartEvent.externalAgent?: { contextId?; taskId? }` are new optional fields on the wire that surface the upstream-assigned conversation handle and task id. Callers persist `contextId` and pass it back on the next `executeAgent` call to resume the same conversation. Mirrored in `@runtypelabs/shared`'s `sse-parser` types in lockstep so Persona clients can type-check the new fields. Fully additive — the api emitter leaves the field unset and existing parsers ignore unknown fields.
2812
+
2813
+ `runExternalAgentTurn` and `runExternalAgentTurnStreaming` accept an optional 6th `invokeOptions` parameter; `RunExternalAgentTurnResult` gains an optional `contextId?: string` on both `'completed'` and `'paused'` variants.
2814
+
2815
+ `@runtypelabs/sdk` — the SDK's local copies of `AgentApprovalStartEvent` and `AgentCompleteEvent` in `endpoints.ts` gain the same optional `externalAgent` field so callers using `agents.executeWithCallbacks` / `agents.executeWithLocalTools` can access the conversation handle type-safely without casting.
2816
+
2817
+ - 6fbe4ed: Consolidate the `{ contextId?: string; taskId?: string }` shape — previously declared inline 6 times across `@runtypelabs/shared`, `@runtypelabs/runtime`, and `@runtypelabs/sdk` — into a named `ExternalAgentContext` interface. `shared` is the canonical source; the runtime re-exports it; the SDK keeps a structurally identical local copy to preserve its zero-production-dep posture. New type-level parity test pins the consolidated shape across all three surfaces and both variants of `RunExternalAgentTurnResult` via `expectTypeOf`. No wire-format change; the shape is bit-for-bit identical.
2818
+
2819
+ ## 1.24.1
2820
+
2821
+ ### Patch Changes
2822
+
2823
+ - d659976: Fix persisted flow protocol 422 detection — check statusCode instead of message string
2824
+ - 0442e36: Harden persisted flow 422 detection to handle both error types, add unit tests
2825
+
2826
+ ## 1.24.0
2827
+
2828
+ ### Minor Changes
2829
+
2830
+ - 761dd74: Add persisted flow protocol (APQ-style) for zero-overhead upsert execution
2831
+
2832
+ SDK now sends only a flow name + content hash on upsert dispatch. If the API recognizes the hash, it executes immediately with no comparison or write overhead. On first deploy or code change, the API responds with FLOW_DEFINITION_REQUIRED and the SDK retries once with the full definition. This eliminates sending the full flow definition over the wire on every execution for the common case (steady-state deployment).
2833
+
2834
+ ## 1.23.0
2835
+
2836
+ ### Minor Changes
2837
+
2838
+ - 96b2f28: Bulk Edit Records dialog now uses the same record-filter chip builder as the records list, schedules, and per-step record queries. The filter picks fields from the live record schema (with type-aware operators, sample values, and nested AND/OR groups) instead of the hand-rolled equals/contains/etc. select trio.
2839
+
2840
+ The `POST /v1/records/bulk-edit` endpoint, the `bulk_edit_records` MCP tool, and the Code-Mode `bulkEditRecords` spec all accept an optional `recordFilter` (the record-filter DSL) on the request body. The legacy `conditions` shape is still supported for backward compatibility.
2841
+
2842
+ - 5b7e3fe: Add Phase 2 runtime export API endpoints: `GET /v1/flows/:id/export-runtime` and `GET /v1/agents/:id/export-runtime`. These endpoints return fully-resolved, self-contained JSON definitions that the `@runtypelabs/runtime` package can consume at boot with no live database queries needed at execution time.
2843
+
2844
+ Key features:
2845
+ - Flow steps inlined in execution order
2846
+ - Agent capabilities resolved recursively (up to 3 levels of nested sub-agents)
2847
+ - MCP server credentials sanitized — raw tokens replaced with secret-name references, `{{secret:NAME}}` references preserved
2848
+ - External agent credentials unsealed before export
2849
+ - Built-in tool IDs resolved to inline `InlineRuntimeTool` definitions
2850
+ - DB-backed tool TypeIDs fetched and inlined as `InlineToolDefinition` entries
2851
+
2852
+ ## 1.22.0
2853
+
2854
+ ### Minor Changes
2855
+
2856
+ - de7f600: Add computed `keyStatus` enum to model API responses. The server now resolves per-user key availability at response time, returning `"platform"` (works out of the box), `"custom"` (user brought their own key), or `"needs_setup"` (user action required). This replaces the static `supportsCustomKey: boolean` which was always `true` and provided no useful signal to agents or consumers.
2857
+
2858
+ ## 1.21.3
2859
+
2860
+ ### Patch Changes
2861
+
2862
+ - 569e7d5: Rename model field `requiresApiKey` to `supportsCustomKey` to clarify that models are available by default via platform keys and custom API keys are optional (BYOK). Rename embedding model field `requiresApiKey` string to `customKeyProvider` for consistency.
2863
+
2864
+ ## 1.21.2
2865
+
2866
+ ### Patch Changes
2867
+
2868
+ - b3a580e: Consolidate SDK builder and product surface skill config types around canonical shared contracts.
2869
+
2870
+ ## 1.21.1
2871
+
2872
+ ### Patch Changes
2873
+
2874
+ - c5e0c1d: Consolidate entity types into single source of truth in packages/shared
2875
+ - Define canonical API response entity types (Agent, Surface, SurfaceItem, SurfaceKey, Product, Capability, ApiKey, UserModelConfig) in packages/shared/src/types/entities.ts
2876
+ - Update packages/shared/src/types/client-types.ts with comprehensive Flow, FlowStep, RuntypeRecord types matching actual API response shapes
2877
+ - Update dashboard product types to re-export from @runtypelabs/shared instead of maintaining independent definitions
2878
+ - Remove dead packages/types package (zero imports across codebase, stale column references)
2879
+ - Remove dead convertKeysToCamelCase/convertKeysToSnakeCase code from SDK transform.ts (pass-through since API uses native camelCase)
2880
+ - Use SurfaceBehavior discriminated union from shared for Surface.behavior typing
2881
+
2882
+ ## 1.21.0
2883
+
2884
+ ### Minor Changes
2885
+
2886
+ - 5190fd0: Add type-driven SDK code generation metadata registry that prevents silent drift between SDK config interfaces and the dashboard code generator
2887
+
2888
+ ## 1.20.0
2889
+
2890
+ ### Minor Changes
2891
+
2892
+ - f777029: Linear-style chip+popover record filter builder (`RecordFilterChips`), unified across the schedules page and the `retrieve-record` flow step card. Same `RecordFilter` wire shape — drop-in for the existing builder.
2893
+
2894
+ Each chip is split into independent segments (`field | op | value | X`) with per-segment popovers. The value popover for set-membership / equality on string fields now renders a contextual list of distinct field values with per-value match counts (Linear-style), backed by a new `GET /v1/records/field-values?type=&field=` endpoint. Free-text fallback preserved via "Use 'X'" search row.
2895
+
2896
+ **Schedule UI unification**: dropped the three-way `Record Target` mode selector (`All records of a type` / `Specific record IDs` / `Records matching a filter`). The chip filter is now the single way to target records, since:
2897
+ - `id`, `name`, `createdAt`, `updatedAt` are now reachable as top-level columns in the filter compiler — `id is one of [...]` covers what `Specific record IDs` used to do.
2898
+ - A filter with type only and no chips covers what `All records of a type` used to do.
2899
+
2900
+ A "Paste IDs" button drops a pre-filled `id is one of [...]` chip for power users with a list to paste; the value picker also bulk-adds when you paste a comma- or newline-separated list into its search input. Old saved schedules still load (and their `recordType` / `recordIds` are normalized into a chip filter on edit) — the executor's resolver still understands all three legacy fields, so existing schedules keep running unchanged.
2901
+
2902
+ **`retrieve-record` step now accepts a `recordFilter`**: in query mode, the step card renders the same `RecordFilterChips` UI, replacing the `where type is X / and name contains Y` two-row form. The new `config.recordFilter` is validated via `recordFilterSchema`, normalized through the shared normalizer, and AND-ed into the executor's where clause (with `{{template}}` substitution applied to chip values before compilation). Legacy `recordType` / `recordName` keep working — old saved steps load by normalizing into chips, and the executor still falls back to the legacy fields when no `recordFilter` is set.
2903
+
2904
+ **`update-record` step gets the same treatment**: a new "Find record" radio at the top of the step card lets users target by context (`_record`, the existing default), by ID, or by chip filter. When a filter is set, the executor compiles it with templates substituted and takes the first match (ordered by `updatedAt desc`). Filter validation is enforced at validate-time via a new `updateRecordConfigSchema`. Same backward-compatibility story: legacy `recordType` / `recordName` keep working until the user edits.
2905
+
2906
+ **MCP discoverability**: the `create-flow` / `update-flow` / `validate-flow` MCP tool descriptions now document `recordFilter` as a valid `config` shape for `retrieve-record` and `update-record`, including the operator allowlist and reachable top-level columns. New `RecordFilter`, `RecordFilterCondition`, `RecordFilterGroup`, `RecordFilterOperator`, `RetrieveRecordConfig`, and `UpdateRecordConfig` types are added to `runtype://types/flow-steps` so agent clients see the shape directly. `flow-step-type-metadata.ts` config-hints lines now mention `recordFilter` for both steps. The runtime passthrough already worked (config is forwarded as an opaque object); this slice makes it discoverable.
2907
+
2908
+ Also fix two bugs surfaced by the new UI:
2909
+ - **`/v1/records/preview-filter` 500 on `is one of` / `is not one of`**: drizzle's `sql` template expanded `${array}` as a row constructor `($1, $2)`, so `= ANY(${list})` compiled to `text = ANY(($1, $2))` and Postgres rejected it as `text = record`. Switched both `in` and `notIn` to the `IN (${list})` form, which compiles to the correct `text IN ($1, $2)`. Pinned with a regression test.
2910
+ - **Trailing comma vanished while typing in the multi-value input**: every keystroke re-rendered from the parsed array, eating the comma. Now holds the raw typed text in local state and keeps the parsed array in sync on every keystroke so Apply still reads a fresh value.
2911
+
2912
+ ## 1.19.1
2913
+
2914
+ ### Patch Changes
2915
+
2916
+ - 7712f0d: Expose flowTimeoutMs/stepTimeoutMs in dispatch options, add timeout abort smoke test, and fix partial token accounting on aborted prompt steps
2917
+
2918
+ ## 1.19.0
2919
+
2920
+ ### Minor Changes
2921
+
2922
+ - e6d59df: Add per-step `when` predicate for conditional step execution. Any step can carry
2923
+ a JS expression evaluated at runtime; if falsy the step is skipped with its
2924
+ outputVariable set to null and a `step_skip` SSE event emitted. Default-skip-on-
2925
+ throw; opt into fail-loud via `errorHandling.onError: 'fail'`.
2926
+
2927
+ ## 1.18.1
2928
+
2929
+ ### Patch Changes
2930
+
2931
+ - 778a8be: Fix flow tools failing when referenced via saved tool ID instead of direct flowId
2932
+
2933
+ When a saved tool with `toolType: 'flow'` was added as an agent capability, the dashboard and API built the runtime tool config with `config.toolId` (the saved tool's database ID) instead of `config.flowId` (the ID of the flow to execute). This caused validation to fail with "Flow tool missing flowId".
2934
+ - Dashboard now resolves saved tool config to extract the actual `flowId`
2935
+ - API executor resolves `config.toolId` → saved tool → `flowId` at execution time
2936
+ - Validation accepts either `flowId` or `toolId` for flow tools
2937
+ - Added end-to-end smoke test for flow-as-tool on agents
2938
+
2939
+ ## 1.18.0
2940
+
2941
+ ### Minor Changes
2942
+
2943
+ - 7070cfb: Remove `chunk` from the SDK's `step_delta` event shape, the
2944
+ `StepChunkEvent` alias, and the `onStepChunk` callback. Customers migrate
2945
+ to `text` and `onStepDelta`. Bruno API examples and the dashboard's
2946
+ generated SDK code now use the new field/callback names. The shared
2947
+ flow-execution streaming hook drops its unrelated `data.content` legacy
2948
+ reader. Runtime: `StepDeltaEventData` adds optional `partId? /
2949
+ messageId? / toolId?` fields to match the api's canonical
2950
+ `StepDeltaData` per the api-parity-for-ports rule.
2951
+
2952
+ ### Patch Changes
2953
+
2954
+ - e886078: Add first-class `agent_media` SSE events for multimodal tool output, aligned with AI SDK standards. Media types use AI SDK v3/v4 content-part shapes (`media`, `image-url`, `file-url`) and tools wire up `toModelOutput` so models receive native multimodal content. Messaging surfaces (Telegram, Slack) collect and deliver media natively via channel adapters.
2955
+ - e6fcbe6: Add media visibility control for agent_media SSE events
2956
+
2957
+ Two-layer visibility system: MCP `annotations.audience` on content parts (spec compliance) and tool-level `mediaVisibility` config via `toolConfigs`. Tools can now return media to the model for reasoning without it being shown to the user. Default behavior unchanged — all existing tools continue to show media to both audiences.
2958
+
2959
+ - 94261ac: Restore browser tools and add dedicated Browser category with namespaced tool IDs
2960
+ - Unhide all 16 browser tools (7 quick-action + 9 session) that were hidden pending staging validation
2961
+ - Add `BROWSER` category to separate browser tools from generic web scraping
2962
+ - Rename tool IDs from `browser-*` to `browser:*` namespace format (e.g. `builtin:browser:screenshot`)
2963
+ - Add alias support for backward compatibility with legacy `browser-*` IDs
2964
+ - Add Browser ToolGroupCard in the dashboard tools sheet
2965
+
2966
+ - 91db7b9: Add typed agent approval stream events and callbacks to the TypeScript SDK.
2967
+
2968
+ ## 1.17.1
2969
+
2970
+ ### Patch Changes
2971
+
2972
+ - 28f126c: Thread AI SDK `finishReason` through the model-execution stream into dispatch `step_complete` SSE events as a new `stopReason` field, and — for parity — onto `agent_turn_complete` on both the API agent-loop path (`/agents/:id/execute`, `/dispatch`, `/product-chat`, `/mcp-capability`) and the self-hosted runtime. Clients can now distinguish a model invocation that ended naturally (`end_turn`) from one that was capped mid-loop by `stopWhen` (`max_tool_calls`), plus `length`, `content_filter`, `error`, and `unknown`. Fixes the empty-chat-bubble symptom in the dashboard when a virtual-agent tool call hits `maxToolCalls: 1`: the follow-up model invocation was cut off silently and the UI had no way to explain why.
2973
+
2974
+ `@runtypelabs/runtime` mirrors the feature end-to-end: `PromptStepResult.stopReason` exposes the per-step value, `FlowEngineResult.lastStopReason` surfaces the last-prompt-step reason of a flow run, and `AgentTurnCompleteEventData.stopReason` lands on the `agent_turn_complete` SSE event so self-hosted Persona clients see the same signal as the hosted API. `StepStopReason` and `mapFinishReasonToStopReason` are now re-exported from the runtime barrel so external consumers can type-annotate the new field.
2975
+
2976
+ `@runtypelabs/sdk` gains `stopReason` on its `AgentTurnCompleteEvent` interface — the public SDK surface was missing the field even though the wire carried it.
2977
+
2978
+ **Bug fixes pulled in alongside the feature**:
2979
+ - `collectUsageWithTimeout` (and the three inline variants in `cloudflare-gateway-executor`, `workers-ai-executor`, `mixlayer-executor`) no longer discard an already-resolved `finishReason` when the usage race times out. Previously `Promise.all` rejected the whole await when the usage arm rejected, losing `finishReason: 'tool-calls'` that had arrived at t=0 — re-creating the empty-bubble symptom for providers with slow usage accounting. Switched to `Promise.allSettled` so the two arms resolve independently.
2980
+
2981
+ **Python SDK** (`packages/python-sdk`, versioned separately): `StepCompleteEvent` gains an optional `stop_reason` field with `AliasChoices('stop_reason', 'stopReason')`, plus a new `StepStopReason` Literal type. Full agent-loop event modeling (`agent_turn_complete`, etc.) in the Python SDK is out of scope — it doesn't model agent events today.
2982
+
2983
+ ## 1.17.0
2984
+
2985
+ ### Minor Changes
2986
+
2987
+ - 060278a: Add Subagent tool authoring UI and SDK builder methods.
2988
+
2989
+ Dashboard: `subagent` is now selectable in the Create Tool dialog. The tool
2990
+ detail page exposes an agent picker (from `/v1/agents`), an allowed-tools
2991
+ multiselect drawn from the selected agent's configured tools, an output
2992
+ format radio (`text` / `json` / `last_message`), and optional `maxTurns`,
2993
+ `timeoutMs`, `taskTemplate`, and `inheritMessages` controls. Tools list
2994
+ renders a Bot icon + "Subagent" badge for the new type.
2995
+
2996
+ SDK: `FlowBuilder` gains `.withSubagentTool(name, opts)` (attach a subagent
2997
+ runtime tool to the most recent prompt step) and `.withSubagents(opts)`
2998
+ (enable agent-driven dynamic spawning via the synthesized `spawn_subagent`
2999
+ tool). `Tool`, `ToolConfig`, `RuntimeTool`, and `ToolsConfig` types are
3000
+ extended with `'subagent'` and the new `SubagentToolConfig` /
3001
+ `RuntimeSubagentToolConfig` / `AgentSubagentConfig` shapes.
3002
+
3003
+ The API trust boundary (tool-pool intersection, leak guard propagation,
3004
+ recursion depth, per-run spawn ceiling) is unchanged — this PR is a
3005
+ UX-only veneer over the already-shipped API engine.
3006
+
3007
+ ## 1.16.0
3008
+
3009
+ ### Minor Changes
3010
+
3011
+ - 23b42c8: Add sampling config parameters (topP, topK, frequencyPenalty, presencePenalty, seed) for model execution
3012
+
3013
+ Adds end-to-end support for five new sampling parameters across the platform:
3014
+ - API: ExecutionOptions, all model executors, prompt executor, agent executor, dispatch/flow-steps validation
3015
+ - Dashboard: New SamplingParameterControl component, model selector, prompt card, agent editor
3016
+ - SDK: Prompt step builders, prompt run options, eval overrides, agent execute request, and shared dispatch/eval override types
3017
+ - All params default to undefined (use model default) with no fallback values
3018
+ - Qwen 3.5 open-weight models routed through Mixlayer still apply their specialized defaults when these fields are unset, but any explicitly configured sampling value takes precedence over those Qwen fallbacks
3019
+
3020
+ ## 1.15.3
3021
+
3022
+ ### Patch Changes
3023
+
3024
+ - 60dbf93: Improve `deploy_sandbox` on the Cloudflare Sandbox provider: auto-create parent directories when writing nested `files` entries, reject reserved port 3000 up front with a clear message, default to port 8080, and return a `stage` field plus captured stdout/stderr in `output` on failure for easier debugging.
3025
+
3026
+ ## 1.15.2
3027
+
3028
+ ### Patch Changes
3029
+
3030
+ - 64dfdb5: Replace stale model references in user-facing examples, JSDoc, templates, and builtin-tool compatibility lists. After the tranche-2 model curation, several example and documentation sites still referenced models that had been filtered from the generated catalog (`gpt-3.5-turbo`, `gpt-4-turbo`, `o1`, `o1-mini`, `claude-3-5-sonnet`, `claude-3-opus`, `gemini-1.5-flash`, `gemini-2.0-flash`, `gemini-pro`, `grok-3`, `grok-3-mini`). Users copying those examples would receive model-not-found errors.
3031
+
3032
+ Touched:
3033
+ - `apps/dashboard/lib/constants/vector-flow-templates.ts` — user-facing vector flow template (`gpt-3.5-turbo` → `gpt-5-nano`)
3034
+ - `apps/dashboard/lib/api-request-generator.ts` — dashboard-generated API example payloads (eval comparison pair → `claude-sonnet-4-6`, `gemini-3-flash`)
3035
+ - `apps/dashboard/lib/sdk-completions.ts` — SDK autocompletion options for the `model` field (OpenAI + Anthropic + Google current)
3036
+ - `apps/dashboard/lib/sdk-code-generator.ts` — SDK eval-comparison example (`gpt-5.4`, `claude-opus-4-6`, `gemini-3.1-pro`)
3037
+ - `packages/mcp/src/core/tools.ts` — `run_prompt` / `execute_agent` MCP tool schema descriptions (example model triplet in the `model` arg description)
3038
+ - `packages/client/src/runtype.ts`, `evals-namespace.ts`, `eval-builder.ts` — five JSDoc `@example` blocks that showed `compareModels` usage with `claude-3-opus` / `gpt-4o` / `gemini-pro`
3039
+ - `packages/shared/src/product-generation/knowledge-fragments.ts` — rewrote the xAI Live Search compatibility paragraph to drop grok-3 advice (grok-3 is no longer in the catalog)
3040
+ - `packages/shared/src/builtin-tools-registry.ts` — `modelCompatibility` lists for DALL-E, OpenAI web search, Anthropic web search/fetch, and xAI Live Search: removed filtered-out models and added current ones
3041
+
3042
+ No runtime behavior changes — this is all documentation, example code, and compatibility-list hygiene.
3043
+
3044
+ ## 1.15.1
3045
+
3046
+ ### Patch Changes
3047
+
3048
+ - ca82595: Fix Cloudflare Sandbox deploy/cleanup routes overflowing the SDK's 63-char sandbox ID limit
3049
+
3050
+ The `@cloudflare/sandbox` SDK's `sanitizeSandboxId()` throws a `SecurityError` when the ID exceeds 63 chars (DNS subdomain limit). The prior scoping format — `${user.orgId || user.userId}_cf-sandbox-${uuid}` — produced ~76-char IDs with Clerk-style org/user IDs, so every call to `/tools/sandbox/cf-sandbox/deploy` threw before reaching the inner try/catch and returned a generic 500 `Failed to deploy Cloudflare Sandbox`. Replace the direct prefix with a 12-hex-char SHA-256 tenant hash so scoped IDs land under 50 chars, and lowercase the result to avoid the SDK's case-insensitivity warning on preview hostnames. The SDK client now appends `details` to error messages when the API returns a `{ error, details }` shape, so future failures like this aren't hidden behind the top-level label.
3051
+
3052
+ ## 1.15.0
3053
+
3054
+ ### Minor Changes
3055
+
3056
+ - 2820927: Replace the `cloudflare-shell` sandbox provider with `cloudflare-sandbox`, backed by `@cloudflare/sandbox`.
3057
+ - `sandboxProvider` on transform-data steps and custom runtime tools accepts `'cloudflare-sandbox'` instead of `'cloudflare-shell'`.
3058
+ - New `CloudflareSandboxDO` container-backed Durable Object replaces `ShellSandboxDO`. Each sandbox is a full Linux computer (Node 22, Python 3.12, pnpm, tsx, uv, git) with snapshot-backed persistence, preview URLs, and Active-CPU pricing.
3059
+ - New routes: `POST /tools/sandbox/cf-sandbox/deploy` and `DELETE /tools/sandbox/cf-sandbox/:sandboxId`. The old `cf-shell` routes are removed.
3060
+ - SDK: `deployCfShell` / `cleanupCfShell` renamed to `deployCfSandbox` / `cleanupCfSandbox`. `DeployCfShellRequest` / `DeployCfShellResponse` renamed to `DeployCfSandboxRequest` / `DeployCfSandboxResponse`.
3061
+ - CLI: `createDeployCfShellLocalTool` / `cleanupCfShellWorkers` renamed to `createDeployCfSandboxLocalTool` / `cleanupCfSandboxes`. Python is now a supported language for Cloudflare Sandbox.
3062
+ - Infrastructure: wrangler migration `v7` drops `ShellSandboxDO` and adds `CloudflareSandboxDO`. `SHELL_STORAGE` R2 binding renamed to `SANDBOX_STORAGE`. Requires Workers Paid + Containers add-on on the Cloudflare account.
3063
+
3064
+ Breaking change: any caller passing `sandboxProvider: 'cloudflare-shell'` must switch to `'cloudflare-sandbox'`. There was no production usage of the old provider, so the impact is limited to internal testing.
3065
+
3066
+ ## 1.14.1
3067
+
3068
+ ### Patch Changes
3069
+
3070
+ - 799f8db: Extend `BuiltInTool` category union for commerce and artifact tools; type `IntegrationToolsCombobox` for minimal tool rows used by built-in groupings.
3071
+
3072
+ ## 1.14.0
3073
+
3074
+ ### Minor Changes
3075
+
3076
+ - 040c7e8: Add body template support for external tools. External tool configs now accept an optional `body` field with `{{variable}}` interpolation, enabling control over the exact JSON body structure sent to APIs. When absent, behavior is unchanged (remaining parameters auto-serialized).
3077
+
3078
+ ## 1.13.3
3079
+
3080
+ ### Patch Changes
3081
+
3082
+ - e10c4d4: Fix marathon tasks completing prematurely by preventing auto-waive from forcing plan-written and best-candidate-verified gates open, and routing stopReason=complete through workflow canAcceptCompletion gates.
3083
+
3084
+ ## 1.13.2
3085
+
3086
+ ### Patch Changes
3087
+
3088
+ - 7f727ab: feat: add record management built-in tools for agents
3089
+
3090
+ Adds 5 new built-in tools (runtype_record_upsert, runtype_record_batch_upsert, runtype_record_get, runtype_record_list, runtype_record_delete) that enable agents to create, read, update, and delete Runtype records during execution. Tools appear as a grouped "Record Management" section in the tool picker with a select-all checkbox.
3091
+
3092
+ - 19b9c08: fix(sdk): refine marathon task agent workflow phase handling and resume copy
3093
+
3094
+ Align `TASK_COMPLETE` checks with the workflow phase at session start when the phase transitions mid-turn. Treat non-default execute phases as execution-like for local tool trace bookkeeping. Distinguish forced-compaction and continuation guardrails for in-progress (saved) tasks versus completed tasks.
3095
+
3096
+ ## 1.13.1
3097
+
3098
+ ### Patch Changes
3099
+
3100
+ - d73361a: Disable source maps in published packages to reduce npm package size
3101
+
3102
+ ## 1.13.0
3103
+
3104
+ ### Minor Changes
3105
+
3106
+ - 2de3530: Add `deployCfShell` and `cleanupCfShell` methods to the Tools endpoint
3107
+
3108
+ ## 1.12.0
3109
+
3110
+ ### Minor Changes
3111
+
3112
+ - 62c2f83: Add Cloudflare Shell as a new sandbox provider for Marathon CLI
3113
+
3114
+ Introduces `cloudflare-shell` as an alternative to Daytona for agent code execution in Marathon. Uses `@cloudflare/worker-bundler` for runtime TypeScript compilation and npm dependency resolution, and `@cloudflare/shell` for virtual filesystem persistence backed by SQLite + R2.
3115
+
3116
+ Key features:
3117
+ - JavaScript and TypeScript execution with npm dependencies
3118
+ - Virtual filesystem persistence via `@cloudflare/shell` Workspace
3119
+ - Deploy-with-preview via persistent Dynamic Workers
3120
+ - No API key required (uses existing Cloudflare Workers infrastructure)
3121
+ - ~1-5ms startup vs ~100-500ms for Daytona
3122
+
3123
+ Usage: `runtype task "your task" --sandbox cloudflare-shell`
3124
+
3125
+ ## 1.11.0
3126
+
3127
+ ### Minor Changes
3128
+
3129
+ - cc9ff7a: Add Orthogonal-backed platform tools with hierarchical `platform:orthogonal:company:tool` ID format, catalog generation, managed execution, and spend tracking via DO + Analytics Engine.
3130
+
3131
+ ## 1.10.2
3132
+
3133
+ ### Patch Changes
3134
+
3135
+ - 9e3fee1: Fix marathon session reliability: resolve completion deadlocks (verification auto-waive, premature TASK_COMPLETE), fix write loops and stall detection, retain accumulated cost across checkpoint restarts, retry server-side network errors in session loop, and add 'o' key to open files from Files tab or open agent in dashboard
3136
+
3137
+ ## 1.10.1
3138
+
3139
+ ### Patch Changes
3140
+
3141
+ - ce76184: Fix the SDK package entrypoints to emit stable ESM and CommonJS bundles for workspace consumers.
3142
+
3143
+ ## 1.10.0
3144
+
3145
+ ### Minor Changes
3146
+
3147
+ - 190adf8: Add agent fallback-model and playbook policy support across the API, CLI, and SDK.
3148
+
3149
+ Improve marathon task execution with safer verification handling, creation-task output constraints, and better recovery when verification is repeatedly blocked.
3150
+
3151
+ ## 1.9.2
3152
+
3153
+ ### Patch Changes
3154
+
3155
+ - bd08d42: Wire streamed tool input through the API, SDK, and Marathon CLI, keep local tools active through client-side execution, and enable Anthropic eager input streaming behind a feature flag.
3156
+
3157
+ ## 1.9.1
3158
+
3159
+ ### Patch Changes
3160
+
3161
+ - 8494865: Wire streamed tool input through the API, SDK, and Marathon CLI, keep local tools active through client-side execution, and enable Anthropic eager input streaming behind a feature flag.
3162
+ - f49d2d6: Improve Marathon build-and-serve workflow routing for prompts that ask the agent to create a runnable web app, serve it, and verify routes.
3163
+
3164
+ ## 1.9.0
3165
+
3166
+ ### Minor Changes
3167
+
3168
+ - 152019b: Add a first-class Cloudflare Browser Rendering `crawl` flow step with synchronous execution, settings-based credentials, per-page platform billing, and editor/SDK authoring support.
3169
+
3170
+ ### Patch Changes
3171
+
3172
+ - b6d2ceb: Fix Marathon history replay so local tool call/result pairs stay valid when continuation history is trimmed or resumed. This prevents Anthropic and Claude runs from failing on orphaned `tool_result` messages during Marathon continuation.
3173
+
3174
+ ## 1.8.2
3175
+
3176
+ ### Patch Changes
3177
+
3178
+ - c7229a9: Make marathon web research tasks honor the existing `external` workflow variant end-to-end. External prompts now steer agents toward built-in web tools, block repo discovery during research, and stop injecting repo-editing guidance into session context for website-focused runs.
3179
+ - c7229a9: Make marathon external research tasks write their final findings to a workspace markdown file before completion. External runs now default that artifact path to `<task-slug>.md`, require `write_file` to that path, and do not accept `TASK_COMPLETE` until the markdown report has been saved.
3180
+
3181
+ ## 1.8.1
3182
+
3183
+ ### Patch Changes
3184
+
3185
+ - 4ec90b5: Fix `--compact-threshold` parsing to remove ambiguous bare-ratio format. The flag now accepts only two formats: percent (e.g. `--compact-threshold 80%`) and absolute token count (e.g. `--compact-threshold 120000`). Bare decimal values like `0.8` are no longer interpreted as ratios. Also ensure provider-native compaction lifecycle cleanup fires even when session execution throws, preventing a stuck compaction indicator in the TUI.
3186
+ - 4ec90b5: Add provider-aware marathon context management with model-budget-aware compaction thresholds, compaction lifecycle telemetry, structured compact summaries, CLI controls for compaction strategy and instructions, and tool-output guardrails that surface or offload oversized local tool results.
3187
+
3188
+ ## 1.8.0
3189
+
3190
+ ### Minor Changes
3191
+
3192
+ - 564d33c: Add --tools flag to marathon command for enabling built-in tools (exa, firecrawl, dalle, web search, etc.) during agent runs
3193
+
3194
+ ## 1.7.3
3195
+
3196
+ ### Patch Changes
3197
+
3198
+ - f9b1333: Switch SDK build from tsc (CommonJS) to tsup (dual ESM + CJS) to fix named export errors when consumed by ESM packages like the CLI on Node.js v24+
3199
+
3200
+ ## 1.7.2
3201
+
3202
+ ### Patch Changes
3203
+
3204
+ - cb689ac: Improve marathon safety and verification by checkpointing original files before writes, adding rollback and guarded verification tools, and requiring successful verification before completion.
3205
+ - cb689ac: Fix marathon resume and execution-state handling so resumed or restarted runs keep their workflow phase, advance cleanly from planning into execution, avoid reusing the wrong local-tool results across resume cycles, and stop reporting errored runs as completed.
3206
+ - cb689ac: Tighten marathon execution target tracking by trimming candidate paths, ignoring scratch-file writes as targets, blocking planning/execution writes to undeclared files, sanitizing poisoned saved marathon state on resume, and surfacing clearer rate-limit details when marathon hits a 429.
3207
+ - cb689ac: Harden marathon repo-editing runs by requiring stronger UX-task research, preserving existing functionality during execution, preferring tool-trace memory over freeform narration, and blocking completion until the edited target is re-read after writes.
3208
+ - cb689ac: Fix marathon execution loops by only advancing after a successful write to the exact plan path, ignoring `.runtype` artifact candidates, and blocking redundant execution-phase discovery until the current target read fails.
3209
+
3210
+ ## 1.7.1
3211
+
3212
+ ### Patch Changes
3213
+
3214
+ - 458711d: refactor: remove modelsocket provider, consolidate onto mixlayer
3215
+
3216
+ Removes the redundant `modelsocket` provider and consolidates all model routing onto `mixlayer`. Both providers were backed by the same Mixlayer infrastructure (`https://models.mixlayer.ai`) and shared the same API key. This simplifies the codebase and removes the `modelsocket` npm dependency.
3217
+
3218
+ Key changes:
3219
+ - Rewrites `/chat` endpoint from proprietary WebSocket SDK to AI SDK `streamText()`
3220
+ - Adds backward compatibility: stored `modelsocket` references transparently resolve to `mixlayer`
3221
+ - Includes database migration to convert existing `modelsocket` records to `mixlayer`
3222
+ - Removes `modelsocket-executor.ts` and related test files
3223
+ - Updates all dashboard UI, routing, and config files
3224
+ - Keeps `modelsocketKey` field name and `MODELSOCKET_*` env vars for infrastructure compatibility
3225
+
3226
+ ## 1.7.0
3227
+
3228
+ ### Minor Changes
3229
+
3230
+ - 3531abb: Make Cloudflare Worker the default sandbox provider for transform-data steps, soft-deprecating QuickJS as legacy
3231
+
3232
+ ## 1.6.0
3233
+
3234
+ ### Minor Changes
3235
+
3236
+ - 57ca1e0: Add Cloudflare Worker-Loader sandbox provider for transform-data steps
3237
+ - Add cloudflare-worker as third sandbox provider option
3238
+ - Full async/await support with V8 isolate sandboxing
3239
+ - Helper function injection and console output capture
3240
+ - Network isolation and configurable timeout
3241
+ - Zero cost (in-process, no external API calls)
3242
+ - Runtime-aware AI code generation: magic button and flow generator now produce code matching the selected runtime's constraints (sync-only for QuickJS, helpers.\* namespace for Cloudflare Worker, fetch/npm for Daytona)
3243
+
3244
+ ## 1.5.1
3245
+
3246
+ ### Patch Changes
3247
+
3248
+ - 077b0ff: Internal maintenance: update comments and rename imports for clarity
3249
+
3250
+ ## 1.5.0
3251
+
3252
+ ### Minor Changes
3253
+
3254
+ - a96ce5d: Rename `maxIterations` to `maxTurns` in agent loop configuration and SSE event types to align with industry conventions (OpenAI, Anthropic, Vercel AI SDK). The `stopReason` value `max_iterations` is now `max_turns`.
3255
+
3256
+ ### Patch Changes
3257
+
3258
+ - 8505d96: Add 'timeout' to agent loop stop reason types for wall-clock timeout guard
3259
+
3260
+ ## 1.4.0
3261
+
3262
+ ### Minor Changes
3263
+
3264
+ - ed4d85a: Unify SSE event naming across the platform
3265
+ - Rename `step_chunk` to `step_delta` (aligns with Anthropic/OpenAI/Vercel convention)
3266
+ - Rename `flow_paused` to `flow_await`, `step_waiting_local` to `step_await`, `agent_paused` to `agent_await`
3267
+ - Consolidate fallback events: merge `fallbacks_initiated` into `fallback_start`, unify `fallback_success`/`fallback_fail` into `fallback_complete`, rename `fallbacks_exhausted` to `fallback_exhausted`
3268
+ - Add structured TypeScript interfaces for tool events (`ToolStartEventData`, `ToolCompleteEventData`, `ToolErrorEventData`)
3269
+ - Formalize `tool_error` as a first-class event type with `emitToolError()` method
3270
+
3271
+ All renames use dual-emit for backwards compatibility: the API sends both the new and legacy event names during the transition period. SDK consumers accept both names.
3272
+
3273
+ ## 1.3.0
3274
+
3275
+ ### Minor Changes
3276
+
3277
+ - ffab49e: Add marathon continuation support: RunTaskContinuation type, previousMessages/continuationMessage/compact options, costByModel tracking, and compact summary for resume
3278
+
3279
+ ## 1.2.1
3280
+
3281
+ ### Patch Changes
3282
+
3283
+ - 0e5a64a: Add `markdownIfAvailable` support for fetch-url steps so SDK consumers can explicitly control markdown-first text fetching behavior.
3284
+
3285
+ ## 1.2.0
3286
+
3287
+ ### Minor Changes
3288
+
3289
+ - b0bada9: Add `end_turn` stop reason to distinguish "turn ended" from "task completed." Non-loop agents now return `end_turn` instead of `complete`, enabling multi-session marathon execution to continue across sessions. SDK includes client-side stop-phrase detection as a fallback for non-loop agents.
3290
+
3291
+ ## 1.1.0
3292
+
3293
+ ### Minor Changes
3294
+
3295
+ - ebe3c6b: Add `agents.runTask()` method for multi-session long-task agent execution with automatic state management, streaming support, cost tracking, and optional dashboard progress sync via records.
3296
+
3297
+ ## 1.0.2
3298
+
3299
+ ### Patch Changes
3300
+
3301
+ - aa97d84: Remove `embedCode` from `CreateClientTokenResponse`. The API no longer returns embed snippets (dashboard uses persona's generateCodeSnippet instead). Aligns SDK types with API.
3302
+
3303
+ ## 1.0.1
3304
+
3305
+ ### Patch Changes
3306
+
3307
+ - de81f99: Remove unused `promptId`, `contextTemplateId`, `prompt`, and `contextTemplate` fields from the `FlowStep` type. These fields were never functional at runtime and have been removed from the API.
3308
+
3309
+ ## 1.0.0
3310
+
3311
+ ### Major Changes
3312
+
3313
+ - 065de96: BREAKING: Normalize SSE stream events for consistency.
3314
+
3315
+ ## Changes
3316
+
3317
+ ### 1. Standardize on `result` field
3318
+
3319
+ Previously, prompt steps used `result` while context steps used `output`. Now all step types consistently use `result`.
3320
+ - Removed `output` field from `StepCompleteEvent` type
3321
+ - Removed fallback logic (`event.result ?? event.output`) from stream handlers
3322
+
3323
+ ### 2. Rename `executionType` to `stepType`
3324
+
3325
+ Renamed the field from `executionType` to `stepType` to better align with industry conventions (Zapier, n8n, LangGraph) and clarify that it describes the type of step, not how it executes.
3326
+
3327
+ ### 3. Expand `stepType` values
3328
+
3329
+ Previously, `executionType` was limited to `'prompt' | 'context'`. Now `stepType` provides the specific step type like `'prompt'`, `'transform-data'`, `'api-call'`, `'send-email'`, etc.
3330
+
3331
+ This is more useful for:
3332
+ - Filtering specific step types
3333
+ - Building step-type-specific UI
3334
+ - Consumer control logic
3335
+ - Future agent execution types
3336
+
3337
+ ### 4. User-facing visibility
3338
+
3339
+ `stepType` is now visible in user-facing mode (debugMode: false) for `step_chunk`, `step_complete`, and `step_error` events, allowing consumers to use it for control logic.
3340
+
3341
+ ## Migration
3342
+ 1. Change `event.output` to `event.result`
3343
+ 2. Change `event.executionType` to `event.stepType`
3344
+ 3. If checking `stepType === 'context'`, update to check for specific types or use `stepType !== 'prompt'`
3345
+
3346
+ ## 0.5.0
3347
+
3348
+ ### Minor Changes
3349
+
3350
+ - 0140d17: Add `withInputs()` method to FlowBuilder for top-level input variables
3351
+
3352
+ You can now pass input variables directly accessible as `{{varName}}` in templates without needing the `_record.metadata` prefix:
3353
+
3354
+ ```typescript
3355
+ const result = await new FlowBuilder()
3356
+ .useExistingFlow('flow_abc123')
3357
+ .withInputs({
3358
+ customerName: 'Acme Corp',
3359
+ topic: 'Q4 sales',
3360
+ })
3361
+ .run(apiClient, { streamResponse: true })
3362
+ ```
3363
+
3364
+ The `inputs` field is also available directly on `DispatchRequest` for API calls.
3365
+
3366
+ ## 0.4.0
3367
+
3368
+ ### Minor Changes
3369
+
3370
+ - cf6fcdc: Add strongly-typed JSON types to replace `any` usage across the SDK. This includes:
3371
+ - `JsonValue`, `JsonObject`, `JsonPrimitive`, `JsonArray` types for type-safe JSON handling
3372
+ - `Metadata`, `StepOutput`, `ToolArgs`, `ToolResult` semantic type aliases
3373
+ - Type guards: `isJsonValue()`, `isJsonObject()`, `isJsonPrimitive()`, `isJsonArray()`
3374
+ - Updated all interfaces to use proper types instead of `any`:
3375
+ - `FlowStep.config` now uses `JsonObject`
3376
+ - `RuntypeRecord.metadata` now uses `Metadata`
3377
+ - `ModelConfig.settings` now uses `JsonObject`
3378
+ - Tool-related types (`parametersSchema`, `inputParameters`, `outputResult`) now use `JSONSchema`, `JsonObject`, `JsonValue`
3379
+ - Request/response types updated throughout
3380
+
3381
+ These changes improve TypeScript type safety while maintaining backward compatibility - existing code that passes plain objects will continue to work.
3382
+
3383
+ ## 0.3.0
3384
+
3385
+ ### Minor Changes
3386
+
3387
+ - 9ec83bd: Add ReasoningConfig type for fine-grained control over AI reasoning/extended thinking
3388
+ - New `ReasoningConfig` interface supports GPT-5, Claude 4, and Gemini 2.5 reasoning options
3389
+ - `reasoning` field on prompt steps now accepts `boolean | ReasoningConfig`
3390
+ - GPT-5 users can configure `reasoningEffort` and `reasoningSummary` for streaming
3391
+ - Claude users can set `budgetTokens` for extended thinking
3392
+ - Google users can configure `thinkingBudget` and `includeThoughts`
3393
+
3394
+ Example usage:
3395
+
3396
+ ```typescript
3397
+ .prompt({
3398
+ name: 'Analyze',
3399
+ model: 'gpt-5',
3400
+ userPrompt: 'Analyze this data',
3401
+ reasoning: {
3402
+ enabled: true,
3403
+ reasoningEffort: 'high',
3404
+ reasoningSummary: 'detailed'
3405
+ }
3406
+ })
3407
+ ```
3408
+
3409
+ - a63f268: Migrate SDK to native camelCase API convention
3410
+ - Transform functions (`transformRequest`, `transformResponse`, `transformQueryParams`) are now pass-through since the API uses native camelCase
3411
+ - Added TypeScript utility types for compile-time case conversion: `ToCamelCase`, `ToSnakeCase`, `SnakeToCamelString`, `CamelToSnakeString`
3412
+ - No changes to the SDK's external API surface - existing code continues to work
3413
+
3414
+ ## 0.2.1
3415
+
3416
+ ### Patch Changes
3417
+
3418
+ - 8506aa1: Remove private @runtypelabs/shared dependency from all public packages by inlining types and constants. This fixes npm installation errors where users couldn't install packages due to the unpublished internal shared package dependency.
3419
+
3420
+ ## 0.2.0
3421
+
3422
+ ### Minor Changes
3423
+
3424
+ - Remove `includeDistance` option from `VectorSearchStepConfig` interface. Distance/similarity scores are now always included in vector search results when available.
3425
+ - 06ac26d: Add SDK support for error handling fallbacks and client token management
3426
+
3427
+ TypeScript SDK (@runtypelabs/sdk):
3428
+ - Add error handling types to step configs (PromptErrorHandling, ContextErrorHandling)
3429
+ - Add support for fallback chains with retry and model fallbacks
3430
+ - Add fallback SSE event types (fallbacks_initiated, fallback_start, fallback_success, fallback_fail, fallbacks_exhausted)
3431
+ - Add ClientTokensEndpoint for client token CRUD operations
3432
+ - Re-export error handling and client token types from shared lib
3433
+
3434
+ MCP Server (@runtypelabs/mcp-server):
3435
+ - Add client token management tools: list_client_tokens, get_client_token, create_client_token, update_client_token, delete_client_token, regenerate_client_token
3436
+
3437
+ ## 0.1.3
3438
+
3439
+ ### Patch Changes
3440
+
3441
+ - 2eb58c6: Add official npm package badge to README