You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by as...@apache.org on 2026/08/31 09:51:51 UTC

(maka) branch main updated: refactor(runtime): persist durable Tool Result projections (#4287)

This is an automated email from the ASF dual-hosted git repository.

Astro-Han pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/maka.git


The following commit(s) were added to refs/heads/main by this push:
     new bd951aa881 refactor(runtime): persist durable Tool Result projections (#4287)
bd951aa881 is described below

commit bd951aa8819c5c03e39d2878f6866ef5aa53a075
Author: AstroHan <le...@outlook.com>
AuthorDate: Mon Aug 31 17:51:46 2026 +0800

    refactor(runtime): persist durable Tool Result projections (#4287)
    
    * feat(core): define durable tool result projections
    
    Generated-by: Codex
    
    * feat(runtime): add durable projection codec
    
    Generated-by: Codex
    
    * feat(runtime): commit projections atomically at T2
    
    Generated-by: Codex
    
    * refactor(runtime): replay effective tool result projections
    
    Generated-by: Codex
    
    * feat(runtime-host): persist projected artifacts and recoveries
    
    Generated-by: Codex
    
    * refactor(runtime): remove transient Tool Result authority
    
    Generated-by: Codex
    
    * fix(storage): reuse projected image artifacts
    
    Generated-by: Codex
    
    * fix(runtime): remap projected artifacts on copy
    
    Generated-by: Codex
    
    * refactor(runtime): remove synthetic projection return
    
    Generated-by: Codex
    
    * refactor(runtime): privatize synthetic result writer
    
    Generated-by: Codex
    
    * fix(runtime): validate projected images before persistence
    
    Generated-by: Codex
    
    * fix(runtime): reject unowned projection copies
    
    Generated-by: Codex
    
    * refactor(runtime): remove retired conversation copy owner
    
    Generated-by: Codex
    
    * test(runtime): verify durable projection owner boundaries
    
    Generated-by: Codex
    
    * refactor(runtime): keep projection helper types private
    
    Generated-by: Codex
    
    * test(runtime): target durable projection T2 rollback
    
    Generated-by: Codex
    
    * refactor(runtime): trim projection artifact input
    
    Generated-by: Codex
    
    * test(runtime): produce restart projection through T2
    
    Generated-by: Codex
    
    * style(runtime): format session manager tests
    
    Generated-by: Codex
    
    * fix(runtime): make projection admission side-effect free
    
    Generated-by: Codex
    
    * test(runtime): replay persisted image after restart
    
    Generated-by: Codex
    
    * test(runtime-host): cover projection artifact restart
    
    Generated-by: Codex
    
    * refactor(runtime): preserve projection content fidelity
    
    Generated-by: Codex
    
    * fix(storage): protect projection artifacts
    
    Generated-by: Codex
    
    * fix(runtime): admit tool calls before publication
    
    Generated-by: Codex
    
    * fix(runtime): bound projection copies before allocation
    
    Generated-by: Codex
    
    * test(runtime-host): consolidate projection lifecycle coverage
    
    Generated-by: Codex
    
    * test(runtime): lock T1 failure side effects
    
    Generated-by: Codex
    
    * refactor(core): unify artifact source policy
    
    Generated-by: Codex
    
    * fix(runtime): make tool result projectors total
    
    Generated-by: Codex
---
 .../workbar/tools/artifacts/artifact-visibility.ts |  22 +-
 packages/core/package.json                         |   1 +
 packages/core/src/__tests__/artifacts.test.ts      |  20 +
 .../durable-tool-result-projection.test.ts         | 160 ++++
 packages/core/src/__tests__/runtime-event.test.ts  |  34 +
 packages/core/src/artifacts.ts                     |  48 +-
 .../core/src/durable-tool-result-projection.ts     | 189 ++++
 packages/core/src/events.ts                        |   3 +
 packages/core/src/runtime-event.ts                 |  41 +-
 packages/core/src/session-name.ts                  |   1 -
 .../__tests__/client-capability-recovery.test.ts   |   7 +
 .../__tests__/execution-model-composition.test.ts  | 197 +++-
 .../session-revision-two-client-uds.test.ts        |  92 +-
 .../src/server/artifact-coordinator.ts             |   8 +-
 .../src/server/client-capability-recovery.ts       |  17 +-
 .../src/server/execution-model-composition.ts      |  10 +
 packages/runtime/package.json                      |   1 +
 .../runtime/src/__tests__/ai-sdk-backend.test.ts   | 107 ++-
 .../src/__tests__/computer-use-model-loop.test.ts  |   1 +
 .../computer-use-provider-protocol.test.ts         |   6 +
 .../durable-tool-result-projection.test.ts         | 315 +++++++
 .../__tests__/execution-boundary-test-helpers.ts   |  36 +-
 .../__tests__/overflow-reactive-recovery.test.ts   |   2 +-
 .../__tests__/pre-dispatch-refusal-ledger.test.ts  |   4 +-
 .../src/__tests__/recovery-resolver.test.ts        |  82 ++
 .../runtime/src/__tests__/session-manager.test.ts  | 995 +--------------------
 .../tool-runtime-durable-boundary.test.ts          | 207 ++++-
 .../tool-runtime-sandbox-boundary.test.ts          |  16 +-
 .../src/__tests__/tool-runtime-settlement.test.ts  | 114 +--
 .../__tests__/tool-runtime-sqlite-boundary.test.ts | 147 ++-
 packages/runtime/src/ai-sdk-backend.ts             | 143 ++-
 packages/runtime/src/conversation-copy.ts          |  20 +
 .../runtime/src/durable-tool-result-projection.ts  | 478 ++++++++++
 packages/runtime/src/model-history.ts              |  61 +-
 packages/runtime/src/recovery-resolver.ts          |  32 +-
 .../runtime/src/session-event-runtime-mapper.ts    |  32 +-
 packages/runtime/src/session-manager.ts            | 206 -----
 packages/runtime/src/tool-runtime.ts               | 271 ++++--
 .../src/__tests__/artifact-attachments.test.ts     |  86 +-
 .../src/__tests__/sqlite-runtime-store.test.ts     |  70 +-
 packages/storage/src/artifact-attachments.ts       | 100 ++-
 packages/storage/src/artifact-stores.ts            |   2 +
 packages/storage/src/sqlite-runtime-store.ts       |   7 +
 43 files changed, 2811 insertions(+), 1580 deletions(-)

diff --git a/apps/desktop/src/renderer/features/workbar/tools/artifacts/artifact-visibility.ts b/apps/desktop/src/renderer/features/workbar/tools/artifacts/artifact-visibility.ts
index cc35bb97c5..cd7bb4c89f 100644
--- a/apps/desktop/src/renderer/features/workbar/tools/artifacts/artifact-visibility.ts
+++ b/apps/desktop/src/renderer/features/workbar/tools/artifacts/artifact-visibility.ts
@@ -17,28 +17,10 @@
  * under the License.
  */
 
-import type { ArtifactDescriptor, ArtifactSource } from '@maka/core/artifacts';
-
-const USER_VISIBLE_ARTIFACT_SOURCES = {
-  tool_result: false,
-  tool_result_archive: false,
-  synthesis_cache_block: false,
-  history_compact_block: false,
-  history_compact_source: false,
-  provider_request_capture: false,
-  session_effect: false,
-  subagent_writeback: true,
-  deep_research: true,
-  user_upload: false,
-  export: true,
-  snapshot: true,
-  fixture: true,
-} satisfies Record<ArtifactSource, boolean>;
+import { isArtifactUserVisible, type ArtifactDescriptor } from '@maka/core/artifacts';
 
 export function filterUserVisibleArtifacts(
   records: readonly ArtifactDescriptor[],
 ): ArtifactDescriptor[] {
-  return records.filter(
-    (record) => record.source === undefined || USER_VISIBLE_ARTIFACT_SOURCES[record.source],
-  );
+  return records.filter(isArtifactUserVisible);
 }
diff --git a/packages/core/package.json b/packages/core/package.json
index 7c0dfeec11..b9752a2fbc 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -7,6 +7,7 @@
   "sideEffects": false,
   "private": true,
   "exports": {
+    "./durable-tool-result-projection": "./dist/durable-tool-result-projection.js",
     "./canonical-runtime-event": "./dist/canonical-runtime-event.js",
     "./runtime-boundary": "./dist/runtime-boundary.js",
     "./runtime-event": "./dist/runtime-event.js",
diff --git a/packages/core/src/__tests__/artifacts.test.ts b/packages/core/src/__tests__/artifacts.test.ts
index 0fc3499a5f..53f5d4b2fb 100644
--- a/packages/core/src/__tests__/artifacts.test.ts
+++ b/packages/core/src/__tests__/artifacts.test.ts
@@ -23,6 +23,8 @@ import {
   ARTIFACT_ENTITY_ID_MAX_CHARS,
   ARTIFACT_TURN_KEY_MAX_CHARS,
   canUserDeleteArtifact,
+  isArtifactSharedSessionReadable,
+  isArtifactUserVisible,
   isArtifactTurnKey,
   isCanonicalArtifactEntityId,
 } from '../artifacts.js';
@@ -61,3 +63,21 @@ describe('Artifact user-delete policy', () => {
     assert.equal(canUserDeleteArtifact({ source: undefined }), true);
   });
 });
+
+describe('Artifact source policy', () => {
+  test('keeps projection artifacts internal, durable, and readable in shared sessions', () => {
+    const projection = { source: 'tool_result_projection' as const };
+
+    assert.equal(canUserDeleteArtifact(projection), false);
+    assert.equal(isArtifactUserVisible(projection), false);
+    assert.equal(isArtifactSharedSessionReadable(projection), true);
+  });
+
+  test('preserves unattributed artifact defaults', () => {
+    const unattributed = { source: undefined };
+
+    assert.equal(canUserDeleteArtifact(unattributed), true);
+    assert.equal(isArtifactUserVisible(unattributed), true);
+    assert.equal(isArtifactSharedSessionReadable(unattributed), false);
+  });
+});
diff --git a/packages/core/src/__tests__/durable-tool-result-projection.test.ts b/packages/core/src/__tests__/durable-tool-result-projection.test.ts
new file mode 100644
index 0000000000..9726ccbaa9
--- /dev/null
+++ b/packages/core/src/__tests__/durable-tool-result-projection.test.ts
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import assert from 'node:assert/strict';
+import { describe, it } from 'node:test';
+
+import {
+  decodeDurableToolResultProjection,
+  DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES,
+  DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_DEPTH,
+  DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_NODES,
+} from '../durable-tool-result-projection.js';
+
+describe('durable Tool Result projection', () => {
+  it('accepts only the closed current-version schema', () => {
+    const projection = { version: 1, kind: 'text', text: 'safe' } as const;
+
+    assert.deepEqual(decodeDurableToolResultProjection(projection), projection);
+    assert.throws(
+      () => decodeDurableToolResultProjection({ ...projection, version: 2 }),
+      /Invalid durable Tool Result projection/,
+    );
+    assert.throws(
+      () => decodeDurableToolResultProjection({ ...projection, providerOptions: {} }),
+      /Invalid durable Tool Result projection/,
+    );
+    assert.throws(
+      () =>
+        decodeDurableToolResultProjection({
+          version: 1,
+          kind: 'content',
+          parts: [],
+        }),
+      /Invalid durable Tool Result projection/,
+    );
+  });
+
+  it('rejects projections whose serialized JSON exceeds the durable byte bound', () => {
+    assert.throws(
+      () =>
+        decodeDurableToolResultProjection({
+          version: 1,
+          kind: 'text',
+          text: 'x'.repeat(DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES),
+        }),
+      /Invalid durable Tool Result projection/,
+    );
+  });
+
+  it('accepts only content-addressed references owned by the same Session protocol', () => {
+    assert.deepEqual(
+      decodeDurableToolResultProjection({
+        version: 1,
+        kind: 'content',
+        parts: [
+          {
+            kind: 'artifact',
+            mediaType: 'image/png',
+            ref: { kind: 'session_context', sessionId: 'session-1', refId: 'sha256-image' },
+          },
+        ],
+      }).kind,
+      'content',
+    );
+    assert.throws(
+      () =>
+        decodeDurableToolResultProjection({
+          version: 1,
+          kind: 'content',
+          parts: [
+            {
+              kind: 'artifact',
+              mediaType: 'image/png',
+              ref: { kind: 'external_file', absolutePath: '/private/image.png' },
+            },
+          ],
+        }),
+      /Invalid durable Tool Result projection/,
+    );
+    assert.throws(
+      () =>
+        decodeDurableToolResultProjection({
+          version: 1,
+          kind: 'content',
+          parts: [
+            {
+              kind: 'artifact',
+              mediaType: 'image/png',
+              ref: {
+                kind: 'session_file',
+                sessionId: 'session-1',
+                relativePath: 'arbitrary/path.png',
+              },
+            },
+          ],
+        }),
+      /Invalid durable Tool Result projection/,
+    );
+  });
+
+  it('admits only canonical safe image media types', () => {
+    const projection = (mediaType: string) => ({
+      version: 1,
+      kind: 'content',
+      parts: [
+        {
+          kind: 'artifact',
+          mediaType,
+          ref: { kind: 'session_file', sessionId: 'session-1', relativePath: 'artifact-1' },
+        },
+      ],
+    });
+
+    assert.equal(decodeDurableToolResultProjection(projection('image/png')).kind, 'content');
+    assert.throws(
+      () => decodeDurableToolResultProjection(projection('image/png; token=sk-secret')),
+      /Invalid durable Tool Result projection/,
+    );
+    assert.throws(
+      () => decodeDurableToolResultProjection(projection('image/svg+xml')),
+      /Invalid durable Tool Result projection/,
+    );
+  });
+
+  it('applies the same bounded JSON budget to persisted projections', () => {
+    let tooDeep: unknown = 'leaf';
+    for (let depth = 0; depth <= DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_DEPTH; depth += 1) {
+      tooDeep = [tooDeep];
+    }
+    assert.throws(
+      () => decodeDurableToolResultProjection({ version: 1, kind: 'json', value: tooDeep }),
+      /Invalid durable Tool Result projection/,
+    );
+    assert.throws(
+      () =>
+        decodeDurableToolResultProjection({
+          version: 1,
+          kind: 'json',
+          value: Array.from({ length: DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_NODES }, () => null),
+        }),
+      /Invalid durable Tool Result projection/,
+    );
+  });
+});
diff --git a/packages/core/src/__tests__/runtime-event.test.ts b/packages/core/src/__tests__/runtime-event.test.ts
index b0e13b3ba7..31bd12c9b1 100644
--- a/packages/core/src/__tests__/runtime-event.test.ts
+++ b/packages/core/src/__tests__/runtime-event.test.ts
@@ -498,6 +498,40 @@ describe('RuntimeEvent content variants', () => {
   });
 });
 
+test('rejects a Tool Result projection that references another Session artifact', () => {
+  assert.throws(
+    () =>
+      decodeRuntimeEvent(
+        baseEvent({
+          role: 'tool',
+          author: 'tool',
+          content: {
+            kind: 'function_response',
+            id: 'call-1',
+            name: 'Read',
+            result: { kind: 'image' },
+            modelProjection: {
+              version: 1,
+              kind: 'content',
+              parts: [
+                {
+                  kind: 'artifact',
+                  mediaType: 'image/png',
+                  ref: {
+                    kind: 'session_context',
+                    sessionId: 'another-session',
+                    refId: 'image-1',
+                  },
+                },
+              ],
+            },
+          },
+        }),
+      ),
+    /Invalid RuntimeEvent schema/,
+  );
+});
+
 describe('RuntimeEvent actions', () => {
   test('binds the managed mutation digest to its canonical execution semantics', () => {
     const canonicalProfile = JSON.stringify({
diff --git a/packages/core/src/artifacts.ts b/packages/core/src/artifacts.ts
index 02acd38dad..0e203c9f92 100644
--- a/packages/core/src/artifacts.ts
+++ b/packages/core/src/artifacts.ts
@@ -88,6 +88,7 @@ export function resolveArtifactImagePreview(
 
 export const ARTIFACT_SOURCES = [
   'tool_result',
+  'tool_result_projection',
   'tool_result_archive',
   'synthesis_cache_block',
   'history_compact_block',
@@ -152,24 +153,39 @@ export interface ArtifactRecord extends ArtifactDescriptor {
   deepResearchRole?: import('./deep-research-run.js').DeepResearchArtifactRole;
 }
 
-const ARTIFACT_USER_DELETE_ALLOWED_BY_SOURCE = {
-  tool_result: true,
-  tool_result_archive: false,
-  synthesis_cache_block: true,
-  history_compact_block: true,
-  history_compact_source: true,
-  provider_request_capture: true,
-  subagent_writeback: false,
-  deep_research: false,
-  user_upload: true,
-  export: true,
-  snapshot: true,
-  session_effect: false,
-  fixture: true,
-} as const satisfies Record<ArtifactSource, boolean>;
+interface ArtifactSourcePolicy {
+  readonly userDeletable: boolean;
+  readonly userVisible: boolean;
+  readonly sharedReadable: boolean;
+}
+
+const ARTIFACT_SOURCE_POLICIES = {
+  tool_result: { userDeletable: true, userVisible: false, sharedReadable: true },
+  tool_result_projection: { userDeletable: false, userVisible: false, sharedReadable: true },
+  tool_result_archive: { userDeletable: false, userVisible: false, sharedReadable: false },
+  synthesis_cache_block: { userDeletable: true, userVisible: false, sharedReadable: false },
+  history_compact_block: { userDeletable: true, userVisible: false, sharedReadable: false },
+  history_compact_source: { userDeletable: true, userVisible: false, sharedReadable: false },
+  provider_request_capture: { userDeletable: true, userVisible: false, sharedReadable: false },
+  subagent_writeback: { userDeletable: false, userVisible: true, sharedReadable: false },
+  deep_research: { userDeletable: false, userVisible: true, sharedReadable: false },
+  user_upload: { userDeletable: true, userVisible: false, sharedReadable: true },
+  export: { userDeletable: true, userVisible: true, sharedReadable: false },
+  snapshot: { userDeletable: true, userVisible: true, sharedReadable: false },
+  session_effect: { userDeletable: false, userVisible: false, sharedReadable: false },
+  fixture: { userDeletable: true, userVisible: true, sharedReadable: false },
+} as const satisfies Record<ArtifactSource, ArtifactSourcePolicy>;
 
 export function canUserDeleteArtifact(record: Pick<ArtifactRecord, 'source'>): boolean {
-  return record.source === undefined || ARTIFACT_USER_DELETE_ALLOWED_BY_SOURCE[record.source];
+  return record.source === undefined || ARTIFACT_SOURCE_POLICIES[record.source].userDeletable;
+}
+
+export function isArtifactUserVisible(record: Pick<ArtifactRecord, 'source'>): boolean {
+  return record.source === undefined || ARTIFACT_SOURCE_POLICIES[record.source].userVisible;
+}
+
+export function isArtifactSharedSessionReadable(record: Pick<ArtifactRecord, 'source'>): boolean {
+  return record.source !== undefined && ARTIFACT_SOURCE_POLICIES[record.source].sharedReadable;
 }
 
 export type ArtifactChangedReason = 'created' | 'deleted' | 'purged';
diff --git a/packages/core/src/durable-tool-result-projection.ts b/packages/core/src/durable-tool-result-projection.ts
new file mode 100644
index 0000000000..3a5a397597
--- /dev/null
+++ b/packages/core/src/durable-tool-result-projection.ts
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { isCanonicalStorageRef, type StorageRef } from './events.js';
+import { isCanonicalArtifactEntityId, normalizeArtifactImagePreviewMime } from './artifacts.js';
+import { hasExactShape, isRecord } from './record-schema.js';
+import { serializedByteLength } from './serialized-byte-length.js';
+
+export const DURABLE_TOOL_RESULT_PROJECTION_VERSION = 1 as const;
+export const DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES = 256 * 1024;
+export const DURABLE_TOOL_RESULT_PROJECTION_MAX_PARTS = 64;
+export const DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_DEPTH = 32;
+export const DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_NODES = 20_000;
+export const DURABLE_TOOL_RESULT_PROJECTION_FAILURE_MESSAGE =
+  'The tool completed, but its model-visible result could not be projected safely.';
+
+export type DurableProjectionJson =
+  | null
+  | boolean
+  | number
+  | string
+  | DurableProjectionJson[]
+  | { [key: string]: DurableProjectionJson };
+
+export type DurableProjectionArtifactRef = Extract<
+  StorageRef,
+  { kind: 'session_context' | 'session_file' }
+>;
+
+export type DurableToolResultProjectionPart =
+  | { kind: 'text'; text: string }
+  | {
+      kind: 'artifact';
+      mediaType: string;
+      ref: DurableProjectionArtifactRef;
+    };
+
+export type DurableToolResultProjection =
+  | { version: 1; kind: 'text'; text: string; isError?: true }
+  | { version: 1; kind: 'json'; value: DurableProjectionJson; isError?: true }
+  | { version: 1; kind: 'content'; parts: DurableToolResultProjectionPart[] }
+  | { version: 1; kind: 'execution_denied'; reason?: string }
+  | {
+      version: 1;
+      kind: 'failure';
+      reason: 'projection_failed';
+      message: typeof DURABLE_TOOL_RESULT_PROJECTION_FAILURE_MESSAGE;
+    };
+
+export const DURABLE_TOOL_RESULT_PROJECTION_FAILURE: DurableToolResultProjection = Object.freeze({
+  version: DURABLE_TOOL_RESULT_PROJECTION_VERSION,
+  kind: 'failure',
+  reason: 'projection_failed',
+  message: DURABLE_TOOL_RESULT_PROJECTION_FAILURE_MESSAGE,
+});
+
+export function decodeDurableToolResultProjection(value: unknown): DurableToolResultProjection {
+  if (
+    !isDurableToolResultProjection(value) ||
+    serializedByteLength(value, DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES) >
+      DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES
+  ) {
+    throw new Error('Invalid durable Tool Result projection');
+  }
+  return value;
+}
+
+function isDurableToolResultProjection(value: unknown): value is DurableToolResultProjection {
+  if (!isRecord(value) || value.version !== DURABLE_TOOL_RESULT_PROJECTION_VERSION) return false;
+  switch (value.kind) {
+    case 'text':
+      return (
+        hasExactShape(value, {
+          required: ['version', 'kind', 'text'],
+          allowed: new Set(['version', 'kind', 'text', 'isError']),
+        }) &&
+        typeof value.text === 'string' &&
+        (value.isError === undefined || value.isError === true)
+      );
+    case 'json':
+      return (
+        hasExactShape(value, {
+          required: ['version', 'kind', 'value'],
+          allowed: new Set(['version', 'kind', 'value', 'isError']),
+        }) &&
+        isDurableProjectionJson(value.value, { nodes: 0 }, 0) &&
+        (value.isError === undefined || value.isError === true)
+      );
+    case 'content':
+      return (
+        hasExactShape(value, {
+          required: ['version', 'kind', 'parts'],
+          allowed: new Set(['version', 'kind', 'parts']),
+        }) &&
+        Array.isArray(value.parts) &&
+        value.parts.length > 0 &&
+        value.parts.length <= DURABLE_TOOL_RESULT_PROJECTION_MAX_PARTS &&
+        value.parts.every(isProjectionPart)
+      );
+    case 'execution_denied':
+      return (
+        hasExactShape(value, {
+          required: ['version', 'kind'],
+          allowed: new Set(['version', 'kind', 'reason']),
+        }) &&
+        (value.reason === undefined || typeof value.reason === 'string')
+      );
+    case 'failure':
+      return (
+        hasExactShape(value, {
+          required: ['version', 'kind', 'reason', 'message'],
+          allowed: new Set(['version', 'kind', 'reason', 'message']),
+        }) &&
+        value.reason === 'projection_failed' &&
+        value.message === DURABLE_TOOL_RESULT_PROJECTION_FAILURE_MESSAGE
+      );
+    default:
+      return false;
+  }
+}
+
+function isProjectionPart(value: unknown): value is DurableToolResultProjectionPart {
+  if (!isRecord(value)) return false;
+  if (value.kind === 'text') {
+    return (
+      hasExactShape(value, {
+        required: ['kind', 'text'],
+        allowed: new Set(['kind', 'text']),
+      }) && typeof value.text === 'string'
+    );
+  }
+  return (
+    value.kind === 'artifact' &&
+    hasExactShape(value, {
+      required: ['kind', 'mediaType', 'ref'],
+      allowed: new Set(['kind', 'mediaType', 'ref']),
+    }) &&
+    typeof value.mediaType === 'string' &&
+    normalizeArtifactImagePreviewMime(value.mediaType) === value.mediaType &&
+    isCanonicalStorageRef(value.ref) &&
+    (value.ref.kind === 'session_context' ||
+      (value.ref.kind === 'session_file' && isCanonicalArtifactEntityId(value.ref.relativePath)))
+  );
+}
+
+function isDurableProjectionJson(
+  value: unknown,
+  state: { nodes: number },
+  depth: number,
+): value is DurableProjectionJson {
+  state.nodes += 1;
+  if (
+    state.nodes > DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_NODES ||
+    depth > DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_DEPTH
+  ) {
+    return false;
+  }
+  if (
+    value === null ||
+    typeof value === 'string' ||
+    typeof value === 'boolean' ||
+    (typeof value === 'number' && Number.isFinite(value))
+  ) {
+    return true;
+  }
+  if (Array.isArray(value)) {
+    return value.every((item) => isDurableProjectionJson(item, state, depth + 1));
+  }
+  return (
+    isRecord(value) &&
+    Object.values(value).every((item) => isDurableProjectionJson(item, state, depth + 1))
+  );
+}
diff --git a/packages/core/src/events.ts b/packages/core/src/events.ts
index 386b5c598b..c5ccd3b0ff 100644
--- a/packages/core/src/events.ts
+++ b/packages/core/src/events.ts
@@ -48,6 +48,7 @@ import type {
 export { SHELL_RUN_SOURCE_TOOL_CALL_ID_MAX_BYTES } from './shell-run.js';
 import { type TokenUsageFields } from './usage-record-schema.js';
 import { defineObjectShape, hasExactShape, isRecord } from './record-schema.js';
+import type { DurableToolResultProjection } from './durable-tool-result-projection.js';
 
 export const TOOL_OUTPUT_STREAMS = ['stdout', 'stderr'] as const;
 export const TOOL_OUTPUT_DELTA_MAX_CHARS = 8192;
@@ -698,6 +699,8 @@ export interface ToolResultEvent extends BaseEvent, ToolActivityIdentity {
   providerExecuted?: boolean;
   /** Raw provider result retained for provider-native replay; never rendered directly. */
   providerOutput?: unknown;
+  /** Provider-neutral model-visible output computed before durable publication. */
+  modelProjection?: DurableToolResultProjection;
   /** The transport omitted durable result content; consumers must not treat the placeholder as authoritative. */
   contentOmitted?: true;
   isError: boolean;
diff --git a/packages/core/src/runtime-event.ts b/packages/core/src/runtime-event.ts
index 644f3c89fe..8dbe8e31e9 100644
--- a/packages/core/src/runtime-event.ts
+++ b/packages/core/src/runtime-event.ts
@@ -63,6 +63,10 @@ import {
   isRuntimeEventWorkspaceFactEnvelope,
   type RuntimeEventWorkspaceFactEnvelope,
 } from './workspace-version-authority.js';
+import {
+  decodeDurableToolResultProjection,
+  type DurableToolResultProjection,
+} from './durable-tool-result-projection.js';
 
 // ============================================================================
 // Role / Author / Status
@@ -188,6 +192,8 @@ export interface RuntimeEventFunctionResponseContent {
   providerExecuted?: boolean;
   /** Raw provider result retained for provider-native replay; never rendered directly. */
   providerOutput?: unknown;
+  /** Frozen provider-neutral content consumed by every model-history projection. */
+  modelProjection?: DurableToolResultProjection;
 }
 
 export interface RuntimeEventErrorContent {
@@ -249,6 +255,8 @@ export type ToolBoundaryProtocol = typeof TOOL_BOUNDARY_PROTOCOL_V1;
  */
 export interface RuntimeEventToolDispatch {
   protocol: ToolBoundaryProtocol;
+  /** New writes require this exact durable Tool Result projection protocol. */
+  resultProjectionVersion?: 1;
   operationId: string;
   providerToolCallId: string;
   toolName: string;
@@ -548,7 +556,7 @@ const FUNCTION_CALL_CONTENT_SHAPE = defineObjectShape<RuntimeEventFunctionCallCo
 );
 const FUNCTION_RESPONSE_CONTENT_SHAPE = defineObjectShape<RuntimeEventFunctionResponseContent>()(
   ['kind', 'id', 'name', 'result'],
-  ['isError', 'providerExecuted', 'providerOutput'],
+  ['isError', 'providerExecuted', 'providerOutput', 'modelProjection'],
 );
 const ERROR_CONTENT_SHAPE = defineObjectShape<RuntimeEventErrorContent>()(
   ['kind', 'message'],
@@ -601,7 +609,7 @@ const RUNTIME_TOOL_DISPATCH_SHAPE = defineObjectShape<RuntimeEventToolDispatch>(
     'canonicalArgsHash',
     'recoveryMode',
   ],
-  ['managedMutation'],
+  ['managedMutation', 'resultProjectionVersion'],
 );
 const RUNTIME_MANAGED_WORKSPACE_MUTATION_SHAPE =
   defineObjectShape<RuntimeEventManagedWorkspaceMutationV2>()(
@@ -712,6 +720,7 @@ export function decodeRuntimeEvent(value: unknown): RuntimeEvent {
     !isOptionalMember(value.modelVisibility, RUNTIME_EVENT_MODEL_VISIBILITIES) ||
     (value.status !== undefined && !isRuntimeEventStatus(value.status)) ||
     (value.content !== undefined && !isRuntimeEventContent(value.content)) ||
+    !hasOwnedModelProjection(value.content, value.sessionId) ||
     (value.actions !== undefined && !isRuntimeEventActions(value.actions)) ||
     (value.refs !== undefined && !isRuntimeEventRefs(value.refs))
   ) {
@@ -733,6 +742,20 @@ export function decodeRuntimeEvent(value: unknown): RuntimeEvent {
   return value as unknown as RuntimeEvent;
 }
 
+function hasOwnedModelProjection(content: unknown, sessionId: string): boolean {
+  if (!isRecord(content) || content.kind !== 'function_response') return true;
+  const projection = content.modelProjection;
+  if (!isRecord(projection) || projection.kind !== 'content' || !Array.isArray(projection.parts)) {
+    return true;
+  }
+  return projection.parts.every(
+    (part) =>
+      !isRecord(part) ||
+      part.kind !== 'artifact' ||
+      (isRecord(part.ref) && part.ref.sessionId === sessionId),
+  );
+}
+
 function isRuntimeEventContent(value: unknown): value is RuntimeEventContent {
   if (!isRecord(value)) return false;
   switch (value.kind) {
@@ -777,7 +800,9 @@ function isRuntimeEventContent(value: unknown): value is RuntimeEventContent {
         typeof value.name === 'string' &&
         Object.hasOwn(value, 'result') &&
         (value.isError === undefined || typeof value.isError === 'boolean') &&
-        (value.providerExecuted === undefined || typeof value.providerExecuted === 'boolean')
+        (value.providerExecuted === undefined || typeof value.providerExecuted === 'boolean') &&
+        (value.modelProjection === undefined ||
+          decodesDurableToolResultProjection(value.modelProjection))
       );
     case 'error':
       return (
@@ -792,6 +817,15 @@ function isRuntimeEventContent(value: unknown): value is RuntimeEventContent {
   }
 }
 
+function decodesDurableToolResultProjection(value: unknown): boolean {
+  try {
+    decodeDurableToolResultProjection(value);
+    return true;
+  } catch {
+    return false;
+  }
+}
+
 function isTurnOrigin(value: unknown): value is TurnOrigin {
   return decodeTurnOrigin(value) !== undefined;
 }
@@ -896,6 +930,7 @@ function isRuntimeToolDispatch(value: unknown): value is RuntimeEventToolDispatc
     isRecord(value) &&
     hasExactShape(value, RUNTIME_TOOL_DISPATCH_SHAPE) &&
     value.protocol === TOOL_BOUNDARY_PROTOCOL_V1 &&
+    (value.resultProjectionVersion === undefined || value.resultProjectionVersion === 1) &&
     typeof value.operationId === 'string' &&
     typeof value.providerToolCallId === 'string' &&
     typeof value.toolName === 'string' &&
diff --git a/packages/core/src/session-name.ts b/packages/core/src/session-name.ts
index 7b670b6e66..993e426d16 100644
--- a/packages/core/src/session-name.ts
+++ b/packages/core/src/session-name.ts
@@ -31,7 +31,6 @@
  * module so every write path can call it:
  *   - `sessions:create`  IPC → runtime.create → store.create
  *   - `sessions:rename`  IPC → runtime.renameSession → store.rename
- *   - `sessions:branchFromTurn`  IPC → runtime.branchFromTurn → store.create
  *
  * Pipeline (applied in order):
  *   1. **Runtime type guard**: `typeof input !== 'string'` → typed
diff --git a/packages/runtime-host/src/__tests__/client-capability-recovery.test.ts b/packages/runtime-host/src/__tests__/client-capability-recovery.test.ts
index badfba53e2..19874cce2c 100644
--- a/packages/runtime-host/src/__tests__/client-capability-recovery.test.ts
+++ b/packages/runtime-host/src/__tests__/client-capability-recovery.test.ts
@@ -60,6 +60,12 @@ test('successor recovery durably settles dispatched Client Capabilities as outco
           },
         },
         isError: true,
+        modelProjection: {
+          version: 1,
+          kind: 'text',
+          text: 'Error: outcome_unknown: the Host restarted after dispatching this Client Capability call. The client-side effect may have happened; do not retry it automatically.',
+          isError: true,
+        },
       },
       refs: {
         operationId: 'capability-operation',
@@ -132,6 +138,7 @@ async function prepare(
         toolName: 'client_tool',
         canonicalArgsHash,
         recoveryMode,
+        resultProjectionVersion: 1,
       },
     },
     refs: { operationId, toolCallId: providerToolCallId },
diff --git a/packages/runtime-host/src/__tests__/execution-model-composition.test.ts b/packages/runtime-host/src/__tests__/execution-model-composition.test.ts
index e7a74b52a6..698da29dab 100644
--- a/packages/runtime-host/src/__tests__/execution-model-composition.test.ts
+++ b/packages/runtime-host/src/__tests__/execution-model-composition.test.ts
@@ -573,6 +573,178 @@ test('backend creation admits an enabled model a snapshot never listed', async (
   await backend.dispose();
 });
 
+test('Host reopens one projected image from its ArtifactStore authority', async () => {
+  const base = await mkdtemp(join(tmpdir(), 'maka-host-projection-image-'));
+  const capability = await resolveStorageRoot({
+    path: join(base, 'interactive'),
+    kind: 'interactive',
+  });
+  const runtimePath = join(base, 'runtime.sqlite');
+  const pngBytes = Buffer.from(
+    'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==',
+    'base64',
+  );
+  const sessionId = 'backend-creation-session';
+  const runId = 'projection-image-run';
+  const turnId = 'projection-image-turn';
+  const head: RuntimeEvent = {
+    id: 'projection-image-head',
+    invocationId: runId,
+    runId,
+    sessionId,
+    turnId,
+    ts: 1,
+    partial: false,
+    role: 'user',
+    author: 'user',
+    content: { kind: 'text', text: 'Return the projected image.' },
+  };
+  let owner = await tryAcquireInteractiveRootOwner(capability);
+  assert.ok(owner);
+  if (!owner) return;
+  const provider = await startProvider();
+  provider.configureProjectionImageFlow('ProjectedImage');
+  const assertProjectedImage = (body: Record<string, unknown> | undefined) => {
+    assert.ok(body);
+    assert.doesNotMatch(JSON.stringify(body), /raw execution fact/u);
+    assert.deepEqual(JSON.parse(latestToolResultText(body) ?? 'null'), [
+      {
+        type: 'file',
+        mediaType: 'image/png',
+        data: { type: 'data', data: pngBytes.toString('base64') },
+      },
+    ]);
+  };
+  let backend: Awaited<ReturnType<typeof createHostAiSdkBackend>> | undefined;
+  let artifacts: Awaited<ReturnType<typeof openInteractiveArtifactStoreForWrite>> | undefined;
+  let runtime = createSqliteRuntimeStore(runtimePath);
+  try {
+    artifacts = await openInteractiveArtifactStoreForWrite(owner.lease);
+    await artifacts.recover();
+    await runtime.appendRuntimeEvent(sessionId, runId, head);
+    backend = await createHostAiSdkBackend(
+      backendCreationFixture({
+        abortSignal: new AbortController().signal,
+        resolveExecutionConnection: async () =>
+          readyExecutionConnection(provider.baseUrl, { vision: true }),
+        readPricing: async () => ({ revision: 0, overrides: [] }),
+        executionBoundary: createBypassExecutionBoundary(0),
+        tools: [
+          {
+            name: 'ProjectedImage',
+            description: 'Return one inline image.',
+            parameters: z.object({}),
+            recoveryMode: 'replay_safe',
+            impl: async () => ({ private: 'raw execution fact' }),
+            toModelOutput: () => ({
+              type: 'content',
+              value: [
+                {
+                  type: 'file',
+                  data: { type: 'data', data: pngBytes.toString('base64') },
+                  mediaType: 'image/png',
+                },
+              ],
+            }),
+          },
+        ],
+        artifacts,
+        loadTurnRuntimeEvents: () => runtime.readImmutableRuntimeEvents(sessionId, runId),
+        runtimeCommitSink: runtime,
+      }),
+    );
+    for await (const _event of backend.send({
+      invocationId: runId,
+      runId,
+      turnId,
+      headAnchorRuntimeEvent: head,
+      text: 'Return the projected image.',
+      context: [],
+      runtimeContext: [head],
+    })) {
+      // Drain the complete live tool step.
+    }
+    const liveRequests = provider.requests.filter((request) => request.body.stream === true);
+    assert.equal(liveRequests.length, 2);
+    assertProjectedImage(liveRequests[1]?.body);
+
+    const nextRunId = 'projection-image-next-run';
+    const nextText = 'Continue in the same process.';
+    const nextHead: RuntimeEvent = {
+      id: 'projection-image-next-head',
+      invocationId: nextRunId,
+      runId: nextRunId,
+      sessionId,
+      turnId: 'projection-image-next-turn',
+      ts: 2,
+      partial: false,
+      role: 'user',
+      author: 'user',
+      content: { kind: 'text', text: nextText },
+    };
+    await runtime.appendRuntimeEvent(sessionId, nextRunId, nextHead);
+    const nextTurnContext = [...(await runtime.readRuntimeEvents(sessionId, runId)), nextHead];
+    for await (const _event of backend.send({
+      invocationId: nextRunId,
+      runId: nextRunId,
+      turnId: nextHead.turnId,
+      headAnchorRuntimeEvent: nextHead,
+      text: nextText,
+      context: [],
+      runtimeContext: nextTurnContext,
+    })) {
+      // Drain the next Turn built from the same committed projection.
+    }
+    const nextTurnRequests = provider.requests.filter((request) => request.body.stream === true);
+    assert.equal(nextTurnRequests.length, 3);
+    assertProjectedImage(nextTurnRequests[2]?.body);
+
+    await backend.dispose();
+    backend = undefined;
+    artifacts.close();
+    artifacts = undefined;
+    runtime.close();
+    await owner.close();
+
+    owner = await tryAcquireInteractiveRootOwner(capability);
+    assert.ok(owner);
+    if (!owner) return;
+    artifacts = await openInteractiveArtifactStoreForWrite(owner.lease);
+    await artifacts.recover();
+    runtime = createSqliteRuntimeStore(runtimePath);
+    const recoveredEvents = await runtime.readRuntimeEvents(sessionId, runId);
+    backend = await createHostAiSdkBackend(
+      backendCreationFixture({
+        abortSignal: new AbortController().signal,
+        resolveExecutionConnection: async () =>
+          readyExecutionConnection(provider.baseUrl, { vision: true }),
+        readPricing: async () => ({ revision: 0, overrides: [] }),
+        artifacts,
+      }),
+    );
+    for await (const _event of backend.send({
+      invocationId: 'projection-image-replay-invocation',
+      runId: 'projection-image-replay-run',
+      turnId: 'projection-image-replay-turn',
+      text: 'Continue after restart.',
+      context: [],
+      runtimeContext: recoveredEvents,
+    })) {
+      // Drain the replay request built from the reopened authorities.
+    }
+    const streamRequests = provider.requests.filter((request) => request.body.stream === true);
+    assert.equal(streamRequests.length, 4);
+    assertProjectedImage(streamRequests[3]?.body);
+  } finally {
+    await backend?.dispose();
+    artifacts?.close();
+    runtime.close();
+    await owner?.close();
+    await provider.close();
+    await rm(base, { recursive: true, force: true });
+  }
+});
+
 test('provider dispatch fails closed when the Run Composition commit fails', async () => {
   const provider = await startProvider();
   let commits = 0;
@@ -3558,6 +3730,7 @@ function backendCreationFixture(input: {
   recordModelCallAttempt?: BackendFactoryContext['recordModelCallAttempt'];
   createFetchTransport?: HostAiSdkBackendInput['createFetchTransport'];
   createRunComposer?: HostAiSdkBackendInput['createRunComposer'];
+  artifacts?: HostAiSdkBackendInput['artifacts'];
 }): HostAiSdkBackendInput {
   const runtimePolicy =
     input.runtimePolicy ??
@@ -3631,7 +3804,7 @@ function backendCreationFixture(input: {
     runtimePolicy,
     ...(input.oauthCredentials ? { oauthCredentials: input.oauthCredentials } : {}),
     createRunComposer,
-    artifacts: {},
+    artifacts: input.artifacts ?? {},
     executionArtifacts: {
       recordToolArtifacts: async () => undefined,
       toolResultArchive: createToolResultArchiveCapability({
@@ -3667,6 +3840,7 @@ function readyExecutionConnection(
   customization: {
     readonly requestHeaders?: Readonly<Record<string, string>>;
     readonly requestBodyOverlay?: Readonly<Record<string, unknown>>;
+    readonly vision?: boolean;
   } = {},
 ) {
   return {
@@ -3682,7 +3856,11 @@ function readyExecutionConnection(
       models: [
         {
           id: MODEL_ID,
-          capabilities: { chat: true, functionCalling: true },
+          capabilities: {
+            chat: true,
+            functionCalling: true,
+            ...(customization.vision !== undefined ? { vision: customization.vision } : {}),
+          },
           contextWindow: 8_192,
           maxOutputTokens: 1_024,
         },
@@ -3959,6 +4137,7 @@ type ProviderFlow =
       readonly groupId: string;
       readonly toolName: string;
     }
+  | { readonly kind: 'projection_image'; readonly toolName: string }
   | { readonly kind: 'child_agent' }
   | {
       readonly kind: 'implementation_child_agent';
@@ -3972,6 +4151,7 @@ async function startProvider(): Promise<{
   readonly requests: ProviderRequest[];
   configureManagedBashFlow(sandboxPaths?: ManagedSandboxPaths): void;
   configureClientCapability(input: { groupId: string; toolName: string }): void;
+  configureProjectionImageFlow(toolName: string): void;
   configureChildAgentFlow(): void;
   configureImplementationChildAgentFlow(): void;
   configureAgentGraphFlow(): void;
@@ -4001,6 +4181,10 @@ async function startProvider(): Promise<{
       if (flow.kind !== 'default') throw new Error('Provider flow is already configured');
       flow = { kind: 'client_capability', ...input };
     },
+    configureProjectionImageFlow: (toolName) => {
+      if (flow.kind !== 'default') throw new Error('Provider flow is already configured');
+      flow = { kind: 'projection_image', toolName };
+    },
     configureChildAgentFlow: () => {
       if (flow.kind !== 'default') throw new Error('Provider flow is already configured');
       flow = { kind: 'child_agent' };
@@ -4077,6 +4261,15 @@ async function handleProviderRequest(
     return;
   }
   const streamRequestIndex = requests.filter((candidate) => candidate.body.stream === true).length;
+  if (flow.kind === 'projection_image' && streamRequestIndex === 1) {
+    assert.ok(toolNames(body).includes(flow.toolName));
+    respondProviderToolCall(response, streamRequestIndex, flow.toolName, {});
+    return;
+  }
+  if (flow.kind === 'projection_image') {
+    respondProviderText(response, RESPONSE_TEXT);
+    return;
+  }
   if (flow.kind === 'managed_bash' && streamRequestIndex === 1) {
     assert.ok(toolNames(body).includes('Bash'));
     respondProviderToolCall(response, streamRequestIndex, 'Bash', {
diff --git a/packages/runtime-host/src/__tests__/session-revision-two-client-uds.test.ts b/packages/runtime-host/src/__tests__/session-revision-two-client-uds.test.ts
index a77e117d70..6c09508c64 100644
--- a/packages/runtime-host/src/__tests__/session-revision-two-client-uds.test.ts
+++ b/packages/runtime-host/src/__tests__/session-revision-two-client-uds.test.ts
@@ -363,7 +363,7 @@ async function verifyConcurrentRevisionAuthority(
     });
     assert.equal(artifactPage.kind, 'page');
     if (artifactPage.kind !== 'page') assert.fail('Branch Artifact query must return a page');
-    assert.equal(artifactPage.artifacts.length, 2);
+    assert.equal(artifactPage.artifacts.length, 3);
     assert.notEqual(artifactPage.artifacts[0]?.id, 'source-artifact');
     const taskPage = await tui.request('task.ledger.query', {
       kind: 'list_start',
@@ -881,6 +881,17 @@ async function seedSource(
       source: 'user_upload',
       now: 1,
     });
+    const projectionArtifact = await artifacts.create({
+      id: 'source-projection-artifact',
+      sessionId: source.id,
+      turnId: 'turn-1',
+      name: 'projection.png',
+      kind: 'file',
+      content: new Uint8Array([0x89, 0x50, 0x4e, 0x47]),
+      mimeType: 'image/png',
+      source: 'tool_result',
+      now: 2,
+    });
     await artifacts.create({
       id: 'legacy-child-artifact',
       sessionId: source.id,
@@ -981,6 +992,53 @@ async function seedSource(
         author: 'agent',
         content: { kind: 'text', text: 'first response' },
       }),
+      runtimeEvent(source.id, 'run-turn-1', 'invocation-turn-1', 'turn-1', {
+        id: 'projection-call',
+        ts: 2.1,
+        role: 'model',
+        author: 'agent',
+        content: {
+          kind: 'function_call',
+          id: 'projection-tool-call',
+          name: 'Read',
+          args: { path: 'projection.png' },
+        },
+      }),
+      runtimeEvent(source.id, 'run-turn-1', 'invocation-turn-1', 'turn-1', {
+        id: 'projection-result',
+        ts: 2.2,
+        role: 'tool',
+        author: 'tool',
+        content: {
+          kind: 'function_response',
+          id: 'projection-tool-call',
+          name: 'Read',
+          result: {
+            kind: 'image',
+            mimeType: 'image/png',
+            ref: {
+              kind: 'session_file',
+              sessionId: source.id,
+              relativePath: projectionArtifact.id,
+            },
+          },
+          modelProjection: {
+            version: 1,
+            kind: 'content',
+            parts: [
+              {
+                kind: 'artifact',
+                mediaType: 'image/png',
+                ref: {
+                  kind: 'session_file',
+                  sessionId: source.id,
+                  relativePath: projectionArtifact.id,
+                },
+              },
+            ],
+          },
+        },
+      }),
       runtimeEvent(source.id, 'run-turn-1', 'invocation-turn-1', 'turn-1', {
         id: 'terminal-1',
         ts: 2.5,
@@ -1552,7 +1610,7 @@ async function verifyDurableBranch(
       });
     };
     const messages = await execution.sessionStore.readMessagesSnapshot(branchSessionId);
-    assert.equal(messages.length, 3);
+    assert.equal(messages.length, 5);
     const user = messages.find((message) => message.type === 'user');
     assert.ok(user?.attachments?.[0]);
     const ref = user?.attachments?.[0]?.ref;
@@ -1562,7 +1620,8 @@ async function verifyDurableBranch(
     // The user-upload attachment ref carries the source artifact id; the copy
     // must rewrite it to a fresh target artifact id, never leave the source id.
     assert.notEqual(ref.relativePath, 'source-artifact');
-    assert.equal((await artifacts.listPage(branchSessionId, { offset: 0, limit: 10 })).total, 2);
+    const branchArtifacts = await artifacts.listPage(branchSessionId, { offset: 0, limit: 10 });
+    assert.equal(branchArtifacts.total, 3);
     assert.deepEqual(await artifacts.readTextInSession(branchSessionId, ref.relativePath), {
       ok: true,
       text: 'retained bytes',
@@ -1579,6 +1638,33 @@ async function verifyDurableBranch(
     assert.ok(copiedChild);
     assert.ok(copiedParent);
     assert.equal(copiedChild.parentRunId, copiedParent.runId);
+    const copiedProjectionResult = (
+      await execution.runtimeEventStore.readRuntimeEvents(branchSessionId, copiedParent.runId)
+    ).find((event) => event.content?.kind === 'function_response');
+    assert.equal(copiedProjectionResult?.content?.kind, 'function_response');
+    if (copiedProjectionResult?.content?.kind !== 'function_response') {
+      assert.fail('Copied branch must retain the durable Tool Result projection');
+    }
+    const copiedProjection = copiedProjectionResult.content.modelProjection;
+    assert.equal(copiedProjection?.kind, 'content');
+    if (copiedProjection?.kind !== 'content') {
+      assert.fail('Copied Tool Result must retain artifact projection content');
+    }
+    const copiedProjectionPart = copiedProjection.parts[0];
+    assert.equal(copiedProjectionPart?.kind, 'artifact');
+    if (copiedProjectionPart?.kind !== 'artifact') {
+      assert.fail('Copied Tool Result must retain its projected artifact');
+    }
+    assert.equal(copiedProjectionPart.ref.kind, 'session_file');
+    if (copiedProjectionPart.ref.kind !== 'session_file') {
+      assert.fail('Copied Tool Result artifact must remain Session-backed');
+    }
+    assert.equal(copiedProjectionPart.ref.sessionId, branchSessionId);
+    const copiedProjectionArtifact = branchArtifacts.records.find(
+      (record) => record.name === 'projection.png',
+    );
+    assert.ok(copiedProjectionArtifact);
+    assert.equal(copiedProjectionPart.ref.relativePath, copiedProjectionArtifact.id);
     assert.equal((await artifacts.listPage('revision-target', { offset: 0, limit: 10 })).total, 0);
     assert.deepEqual(await tasks.list('revision-target'), []);
     assert.deepEqual(await execution.agentRunStore.listSessionRuns('revision-target'), []);
diff --git a/packages/runtime-host/src/server/artifact-coordinator.ts b/packages/runtime-host/src/server/artifact-coordinator.ts
index 195d5fb40c..3a278deb99 100644
--- a/packages/runtime-host/src/server/artifact-coordinator.ts
+++ b/packages/runtime-host/src/server/artifact-coordinator.ts
@@ -20,7 +20,7 @@
 import { createHash } from 'node:crypto';
 import { attachmentKindFromMimeType } from '@maka/core/attachments';
 import type { AttachmentRef } from '@maka/core/events';
-import type { ArtifactRecord } from '@maka/core/artifacts';
+import { isArtifactSharedSessionReadable, type ArtifactRecord } from '@maka/core/artifacts';
 import {
   authenticateInteractiveArtifactStoreWriter,
   sanitizeArtifactName,
@@ -51,8 +51,6 @@ import { ConnectionBoundChunkUploads } from './connection-bound-chunk-uploads.js
 const MAX_ACTIVE_ARTIFACT_UPLOADS = 16;
 const MAX_STAGED_ARTIFACT_UPLOAD_BYTES = 128 * 1024 * 1024;
 const ARTIFACT_UPLOAD_TTL_MS = 5 * 60 * 1000;
-const SHARED_ARTIFACT_SOURCES = new Set(['user_upload', 'tool_result']);
-
 interface ArtifactUploadMetadata {
   readonly attachmentKind: AttachmentRef['kind'];
   readonly name: string;
@@ -443,9 +441,7 @@ export class HostArtifactCoordinator {
     );
     if (!grant) return;
     const entry = await this.#store.getInSession(input.sessionId, input.artifactId);
-    return entry.record?.status === 'live' &&
-      entry.record.source !== undefined &&
-      SHARED_ARTIFACT_SOURCES.has(entry.record.source)
+    return entry.record?.status === 'live' && isArtifactSharedSessionReadable(entry.record)
       ? grant.grantId
       : undefined;
   }
diff --git a/packages/runtime-host/src/server/client-capability-recovery.ts b/packages/runtime-host/src/server/client-capability-recovery.ts
index 1ad74e8402..43aec6f104 100644
--- a/packages/runtime-host/src/server/client-capability-recovery.ts
+++ b/packages/runtime-host/src/server/client-capability-recovery.ts
@@ -19,6 +19,7 @@
 
 import type { ToolResultContent } from '@maka/core/events';
 import type { RuntimeEvent } from '@maka/core/runtime-event';
+import { compatibilityToolResultProjection } from '@maka/runtime/durable-tool-result-projection';
 import type { ExecutionRuntimeEventWriter } from '@maka/storage/execution-stores';
 
 const OUTCOME_UNKNOWN_TEXT =
@@ -43,6 +44,14 @@ export async function recoverClientCapabilityOutcomes(
           retrySafe: false,
         },
       } as const satisfies ToolResultContent;
+      const responseContent = {
+        kind: 'function_response' as const,
+        id: operation.providerToolCallId,
+        name: operation.toolName,
+        result,
+        isError: true as const,
+      };
+      const modelProjection = compatibilityToolResultProjection(responseContent, sessionId);
       const runtimeEvent: RuntimeEvent = {
         id: `${operation.operationId}_response`,
         invocationId: operation.invocationId,
@@ -53,13 +62,7 @@ export async function recoverClientCapabilityOutcomes(
         partial: false,
         role: 'tool',
         author: 'tool',
-        content: {
-          kind: 'function_response',
-          id: operation.providerToolCallId,
-          name: operation.toolName,
-          result,
-          isError: true,
-        },
+        content: { ...responseContent, ...(modelProjection ? { modelProjection } : {}) },
         refs: {
           operationId: operation.operationId,
           toolCallId: operation.providerToolCallId,
diff --git a/packages/runtime-host/src/server/execution-model-composition.ts b/packages/runtime-host/src/server/execution-model-composition.ts
index 454aa2338a..3408e5cf50 100644
--- a/packages/runtime-host/src/server/execution-model-composition.ts
+++ b/packages/runtime-host/src/server/execution-model-composition.ts
@@ -48,6 +48,7 @@ import { type BackendFactoryContext } from '@maka/runtime/session-manager';
 import { type RuntimeCommitSink } from '@maka/runtime/runtime-commit-sink';
 import {
   createAttachmentByteReader,
+  createReadImageSnapshotPlanner,
   persistProviderRequestCaptureArtifact,
   type InteractiveArtifactStoreWriter,
 } from '@maka/storage/artifact-stores';
@@ -332,6 +333,7 @@ export async function createHostAiSdkBackend(input: HostAiSdkBackendInput): Prom
         );
       }
     : undefined;
+  const planProjectionImage = createReadImageSnapshotPlanner(input.artifacts);
 
   try {
     return new HostAiSdkBackend(
@@ -398,6 +400,14 @@ export async function createHostAiSdkBackend(input: HostAiSdkBackendInput): Prom
             ? { readImageSnapshotsUnavailable: true }
             : {}),
         }),
+        prepareDurableProjectionArtifact: ({ turnId, bytes, mediaType }) =>
+          planProjectionImage({
+            sessionId: input.context.sessionId,
+            turnId,
+            name: 'Tool Result image',
+            bytes,
+            mimeType: mediaType,
+          }),
         recordToolArtifacts: input.executionArtifacts.recordToolArtifacts,
         toolResultArchive: input.executionArtifacts.toolResultArchive,
         ...(!input.context.tools &&
diff --git a/packages/runtime/package.json b/packages/runtime/package.json
index 35b4ad5e48..fd2c28d77e 100644
--- a/packages/runtime/package.json
+++ b/packages/runtime/package.json
@@ -13,6 +13,7 @@
     "./shell-tools": "./dist/shell-tools.js",
     "./shell-run-manager": "./dist/shell-run-manager.js",
     "./deep-research-tools": "./dist/deep-research-tools.js",
+    "./durable-tool-result-projection": "./dist/durable-tool-result-projection.js",
     "./tool-artifacts": "./dist/tool-artifacts.js",
     "./model-factory": "./dist/model-factory.js",
     "./model-runtime": "./dist/model-runtime.js",
diff --git a/packages/runtime/src/__tests__/ai-sdk-backend.test.ts b/packages/runtime/src/__tests__/ai-sdk-backend.test.ts
index 8ac41d9d43..50a956ba7b 100644
--- a/packages/runtime/src/__tests__/ai-sdk-backend.test.ts
+++ b/packages/runtime/src/__tests__/ai-sdk-backend.test.ts
@@ -20,7 +20,7 @@
 import assert from 'node:assert/strict';
 import { Buffer } from 'node:buffer';
 import { createHash } from 'node:crypto';
-import { resolve } from 'node:path';
+import { join, resolve } from 'node:path';
 import { describe, test } from 'node:test';
 import type { ModelMessage, ModelStreamResult } from '../model-protocol.js';
 import { MockLanguageModelV4, simulateReadableStream } from 'ai/test';
@@ -341,6 +341,78 @@ describe('AiSdkBackend ApplyPatch routing', () => {
     await assertApplyPatchHistoryDowngraded(targetConnection, targetConnection.defaultModel!);
   });
 
+  test('preserves a durable projection failure when apply_patch history is downgraded', async () => {
+    const model = completionModel();
+    const backend = createTestAiSdkBackend({
+      sessionId: 'session-1',
+      header: header(),
+      appendMessage: async () => {},
+      connection: connection(),
+      apiKey: 'sk-test',
+      modelId: 'mock-model-id',
+      modelFactory: () => model,
+      tools: [nativeApplyPatchTool()],
+      newId: idGenerator(),
+      now: monotonicClock(),
+    });
+
+    await drain(
+      backend.send({
+        turnId: 'turn-current',
+        text: 'continue',
+        context: [],
+        runtimeContext: [
+          runtimeEvent({
+            id: 'rt-call',
+            turnId: 'turn-previous',
+            role: 'model',
+            author: 'agent',
+            content: {
+              kind: 'function_call',
+              id: 'call-1',
+              name: 'apply_patch',
+              args: [
+                '*** Begin Patch',
+                '*** Update File: file.txt',
+                '@@',
+                '-before',
+                '+after',
+                '*** End Patch',
+              ].join('\n'),
+            },
+          }),
+          runtimeEvent({
+            id: 'rt-result',
+            turnId: 'turn-previous',
+            role: 'tool',
+            author: 'tool',
+            content: {
+              kind: 'function_response',
+              id: 'call-1',
+              name: 'apply_patch',
+              result: { status: 'completed', output: 'Applied 1 file operation.' },
+              modelProjection: {
+                version: 1,
+                kind: 'failure',
+                reason: 'projection_failed',
+                message:
+                  'The tool completed, but its model-visible result could not be projected safely.',
+              },
+            },
+          }),
+        ],
+      }),
+    );
+
+    const replayText = (compactPrompt(model) as Array<{ content: any[] }>)
+      .flatMap((message) => message.content)
+      .filter((part) => part.type === 'text')
+      .map((part) => part.text)
+      .join('\n');
+    assert.match(replayText, /could not be projected safely/);
+    assert.doesNotMatch(replayText, /ApplyPatch completed/);
+  });
+
   test('preserves a multi-file ApplyPatch fact when structured replay cannot represent it', async () => {
     const model = completionModel();
     const backend = createTestAiSdkBackend({
@@ -3710,6 +3782,7 @@ describe('AiSdkBackend model history', () => {
       'base64',
     );
     let calls = 0;
+    let artifactReads = 0;
     const anchor = runtimeTextEvent({
       id: 'runtime-user',
       turnId: 'turn-1',
@@ -3785,7 +3858,11 @@ describe('AiSdkBackend model history', () => {
         },
       ],
       supportsVision: true,
-      readAttachmentBytes: async () => ({ ok: true, bytes: pngBytes }),
+      maxProviderImageRequestBytes: pngBytes.byteLength,
+      readAttachmentBytes: async () => {
+        artifactReads += 1;
+        return { ok: true, bytes: pngBytes };
+      },
       loadTurnRuntimeEvents: async () => ledger,
       newId: idGenerator(),
       now: monotonicClock(),
@@ -3806,6 +3883,7 @@ describe('AiSdkBackend model history', () => {
     assert.ok(
       result.value.some((part: any) => part.type === 'file' && part.mediaType === 'image/png'),
     );
+    assert.equal(artifactReads, 1);
   });
 
   test('reloads durable multi-tool settlement before terminal continuation', async () => {
@@ -6907,7 +6985,7 @@ describe('AiSdkBackend error surfaces', () => {
     assert.equal(text.endsWith('…'), true);
   });
 
-  test('writeSyntheticToolResult never persists raw secret-shaped errors', async () => {
+  test('tool settlement never persists raw secret-shaped synthetic errors', async () => {
     const messages: ToolResultMessage[] = [];
     const events: SessionEvent[] = [];
     const backend = createTestAiSdkBackend({
@@ -6925,19 +7003,18 @@ describe('AiSdkBackend error surfaces', () => {
       now: () => 1,
     });
 
-    await turnScope(backend, 'turn-1').toolRuntime.writeSyntheticToolResult(
-      'tool-1',
-      'turn-1',
-      'failed with api_key=sk-live-secret-token-value',
-      {
-        push: (event) => {
-          events.push(event);
-        },
-        pushAndWaitUntilConsumed: async (event) => {
-          events.push(event);
-        },
+    const tool: MakaTool = {
+      name: 'FailingTool',
+      description: 'fails with a provider secret',
+      parameters: {},
+      impl: async () => {
+        throw new Error('failed with api_key=sk-live-secret-token-value');
       },
-    );
+    };
+
+    await runtimeExecute(backend, tool, 'turn-1', {
+      push: (event) => events.push(event),
+    })({}, { toolCallId: 'tool-1', abortSignal: new AbortController().signal });
 
     assert.equal(JSON.stringify(messages).includes('sk-live-secret-token-value'), false);
     assert.equal(JSON.stringify(events).includes('sk-live-secret-token-value'), false);
diff --git a/packages/runtime/src/__tests__/computer-use-model-loop.test.ts b/packages/runtime/src/__tests__/computer-use-model-loop.test.ts
index 2e071cad89..3ba7e3c99c 100644
--- a/packages/runtime/src/__tests__/computer-use-model-loop.test.ts
+++ b/packages/runtime/src/__tests__/computer-use-model-loop.test.ts
@@ -394,6 +394,7 @@ function createRuntime(input: {
     modelId: 'mock-computer-model',
     modelFactory: () => input.model,
     tools: [input.computerTool],
+    testProjectionArtifacts: true,
     ...(input.durable ? { loadTurnRuntimeEvents: input.durable.loadTurnRuntimeEvents } : {}),
     newId: idGenerator(),
     now: monotonicClock(),
diff --git a/packages/runtime/src/__tests__/computer-use-provider-protocol.test.ts b/packages/runtime/src/__tests__/computer-use-provider-protocol.test.ts
index cbac5ac106..e928514689 100644
--- a/packages/runtime/src/__tests__/computer-use-provider-protocol.test.ts
+++ b/packages/runtime/src/__tests__/computer-use-provider-protocol.test.ts
@@ -150,6 +150,7 @@ describe('Anthropic-compatible Computer Use product loops', () => {
         provider.expectedWireOutputLimit,
       );
       const runtime = createTestAiSdkBackend({
+        testProjectionArtifacts: true,
         sessionId,
         header: header(provider.providerType, provider.modelId),
         appendMessage: async () => {},
@@ -275,6 +276,7 @@ describe('Anthropic-compatible Computer Use product loops', () => {
       });
       const events: SessionEvent[] = [];
       const runtime = createTestAiSdkBackend({
+        testProjectionArtifacts: true,
         sessionId,
         header: header(provider.providerType, provider.modelId),
         appendMessage: async () => {},
@@ -378,6 +380,7 @@ describe('OpenAI-compatible product loops', () => {
         'openai-chat',
       );
       const runtime = createTestAiSdkBackend({
+        testProjectionArtifacts: true,
         sessionId,
         header: header(provider.providerType, provider.modelId),
         appendMessage: async () => {},
@@ -494,6 +497,7 @@ describe('OpenAI-compatible product loops', () => {
       now: monotonicClock(),
     });
     const runtime = createTestAiSdkBackend({
+      testProjectionArtifacts: true,
       sessionId,
       header: header('kimi-coding-plan', 'k3'),
       appendMessage: async () => {},
@@ -576,6 +580,7 @@ describe('OpenAI-compatible product loops', () => {
     );
     const createRuntime = () =>
       createTestAiSdkBackend({
+        testProjectionArtifacts: true,
         sessionId,
         header: header('kimi-coding-plan', 'k3'),
         appendMessage: async (message) => {
@@ -695,6 +700,7 @@ describe('OpenAI-compatible product loops', () => {
     );
     const events: SessionEvent[] = [];
     const runtime = createTestAiSdkBackend({
+      testProjectionArtifacts: true,
       sessionId,
       header: header('kimi-coding-plan', 'k3'),
       appendMessage: async () => {},
diff --git a/packages/runtime/src/__tests__/durable-tool-result-projection.test.ts b/packages/runtime/src/__tests__/durable-tool-result-projection.test.ts
new file mode 100644
index 0000000000..836797f49d
--- /dev/null
+++ b/packages/runtime/src/__tests__/durable-tool-result-projection.test.ts
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import assert from 'node:assert/strict';
+import { describe, it } from 'node:test';
+import { MAX_READ_IMAGE_BYTES } from '@maka/core/attachments';
+import { DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES } from '@maka/core/durable-tool-result-projection';
+import { serializedByteLength } from '@maka/core/serialized-byte-length';
+
+import {
+  decodeEffectiveToolResultProjection,
+  encodeDefaultDurableToolResultOutput,
+  encodeDurableToolResultOutput,
+  encodeDurableToolResultOutputWithArtifacts,
+} from '../durable-tool-result-projection.js';
+
+describe('durable Tool Result projection codec', () => {
+  it('preserves arbitrary text and JSON content faithfully', () => {
+    assert.deepEqual(
+      encodeDurableToolResultOutput(
+        { type: 'text', value: 'Authorization: Bearer sk-live-secret-token-value' },
+        'session-1',
+      ),
+      {
+        version: 1,
+        kind: 'text',
+        text: 'Authorization: Bearer sk-live-secret-token-value',
+      },
+    );
+    assert.deepEqual(
+      encodeDurableToolResultOutput(
+        {
+          type: 'json',
+          value: { password: 'correct-horse-battery-staple', keep: 'visible' },
+        },
+        'session-1',
+      ),
+      {
+        version: 1,
+        kind: 'json',
+        value: { password: 'correct-horse-battery-staple', keep: 'visible' },
+      },
+    );
+  });
+
+  it('never persists inline binary or provider options', () => {
+    const projection = encodeDurableToolResultOutput(
+      {
+        type: 'content',
+        value: [
+          { type: 'text', text: 'visible', providerOptions: { provider: { opaque: true } } },
+          {
+            type: 'file',
+            data: { type: 'data', data: 'unbounded-base64-payload' },
+            mediaType: 'image/png',
+            providerOptions: { provider: { opaque: true } },
+          },
+        ],
+      },
+      'session-1',
+    );
+
+    assert.doesNotMatch(JSON.stringify(projection), /unbounded-base64-payload|providerOptions/);
+    assert.deepEqual(projection, {
+      version: 1,
+      kind: 'content',
+      parts: [
+        { kind: 'text', text: 'visible' },
+        {
+          kind: 'text',
+          text: '[Binary tool output omitted from the durable model projection; repeat the tool call if it is still needed.]',
+        },
+      ],
+    });
+  });
+
+  it('maps opaque and oversized JSON to the same deterministic failure sentinel', () => {
+    const opaque = encodeDurableToolResultOutput(
+      { type: 'json', value: new URL('https://example.test') as never },
+      'session-1',
+    );
+    const oversized = encodeDurableToolResultOutput(
+      { type: 'json', value: { body: 'x'.repeat(300_000) } },
+      'session-1',
+    );
+
+    assert.deepEqual(opaque, oversized);
+    assert.deepEqual(opaque, {
+      version: 1,
+      kind: 'failure',
+      reason: 'projection_failed',
+      message: 'The tool completed, but its model-visible result could not be projected safely.',
+    });
+  });
+
+  it('fails deterministically instead of silently dropping excess content parts', () => {
+    const projection = encodeDurableToolResultOutput(
+      {
+        type: 'content',
+        value: Array.from({ length: 65 }, (_, index) => ({
+          type: 'text' as const,
+          text: `part-${index}`,
+        })),
+      },
+      'session-1',
+    );
+
+    assert.equal(projection.kind, 'failure');
+  });
+
+  it('validates every inline image before persisting any projection artifact', async () => {
+    let writes = 0;
+    const projection = await encodeDurableToolResultOutputWithArtifacts(
+      {
+        type: 'content',
+        value: [
+          {
+            type: 'file',
+            data: { type: 'data', data: Buffer.from('valid').toString('base64') },
+            mediaType: 'image/png',
+          },
+          {
+            type: 'file',
+            data: { type: 'data', data: 'not-canonical-base64' },
+            mediaType: 'image/png',
+          },
+        ],
+      },
+      'session-1',
+      artifactPlanner(() => {
+        writes += 1;
+      }),
+    );
+
+    assert.equal(projection.kind, 'failure');
+    assert.equal(writes, 0);
+  });
+
+  it('rejects an oversized ArrayBuffer before copying its bytes', async () => {
+    class ObservableArrayBuffer extends ArrayBuffer {
+      copies = 0;
+
+      override slice(begin?: number, end?: number): ArrayBuffer {
+        this.copies += 1;
+        return super.slice(begin, end);
+      }
+    }
+    const data = new ObservableArrayBuffer(MAX_READ_IMAGE_BYTES + 1);
+    const projection = await encodeDurableToolResultOutputWithArtifacts(
+      {
+        type: 'content',
+        value: [
+          {
+            type: 'file',
+            data: { type: 'data', data },
+            mediaType: 'image/png',
+          },
+        ],
+      },
+      'session-1',
+      () => {
+        throw new Error('oversized bytes must not reach artifact planning');
+      },
+    );
+
+    assert.equal(projection.kind, 'failure');
+    assert.equal(data.copies, 0);
+  });
+
+  it('validates the complete projection before persisting any artifact', async () => {
+    let writes = 0;
+    const projection = await encodeDurableToolResultOutputWithArtifacts(
+      {
+        type: 'content',
+        value: [
+          ...Array.from({ length: 64 }, (_, index) => ({
+            type: 'text' as const,
+            text: `part-${index}`,
+          })),
+          {
+            type: 'file',
+            data: { type: 'data' as const, data: Buffer.from('valid').toString('base64') },
+            mediaType: 'image/png',
+          },
+        ],
+      },
+      'session-1',
+      artifactPlanner(() => {
+        writes += 1;
+      }),
+    );
+
+    assert.equal(projection.kind, 'failure');
+    assert.equal(writes, 0);
+  });
+
+  it('uses the exact planned artifact ref as the projection size authority', async () => {
+    const ref = {
+      kind: 'session_file' as const,
+      sessionId: 'session-1',
+      relativePath: 'artifact-1',
+    };
+    const emptyProjection = {
+      version: 1 as const,
+      kind: 'content' as const,
+      parts: [
+        { kind: 'text' as const, text: '' },
+        { kind: 'artifact' as const, mediaType: 'image/png', ref },
+      ],
+    };
+    const textLength =
+      DURABLE_TOOL_RESULT_PROJECTION_MAX_BYTES - serializedByteLength(emptyProjection);
+    let writes = 0;
+    const projection = await encodeDurableToolResultOutputWithArtifacts(
+      {
+        type: 'content',
+        value: [
+          { type: 'text', text: 'x'.repeat(textLength) },
+          {
+            type: 'file',
+            data: { type: 'data', data: Buffer.from('valid').toString('base64') },
+            mediaType: 'image/png',
+          },
+        ],
+      },
+      'session-1',
+      () => ({
+        ref,
+        persist: async () => {
+          writes += 1;
+        },
+      }),
+    );
+
+    assert.equal(projection.kind, 'content');
+    assert.equal(writes, 1);
+  });
+
+  it('rejects unsafe image metadata before persisting it', async () => {
+    let writes = 0;
+    const projection = await encodeDurableToolResultOutputWithArtifacts(
+      {
+        type: 'content',
+        value: [
+          {
+            type: 'file',
+            data: { type: 'data', data: Buffer.from('valid').toString('base64') },
+            mediaType: 'image/png; token=sk-secret',
+          },
+        ],
+      },
+      'session-1',
+      artifactPlanner(() => {
+        writes += 1;
+      }),
+    );
+
+    assert.equal(projection.kind, 'failure');
+    assert.equal(writes, 0);
+  });
+
+  it('validates default image refs through the same closed schema', () => {
+    const legacyImage = {
+      kind: 'image',
+      mimeType: 'image/png',
+      ref: {
+        kind: 'session_file',
+        sessionId: 'session-1',
+        relativePath: 'legacy/path.png',
+      },
+    } as const;
+    assert.equal(encodeDefaultDurableToolResultOutput(legacyImage, 'session-1').kind, 'failure');
+    assert.deepEqual(
+      decodeEffectiveToolResultProjection(
+        {
+          kind: 'function_response',
+          id: 'legacy-image-1',
+          name: 'Read',
+          result: legacyImage,
+        },
+        'session-1',
+      ),
+      { kind: 'legacy_output', output: legacyImage },
+    );
+  });
+});
+
+function artifactPlanner(onPersist: () => void) {
+  let nextId = 0;
+  return () => {
+    const relativePath = `artifact-${++nextId}`;
+    return {
+      ref: { kind: 'session_file' as const, sessionId: 'session-1', relativePath },
+      persist: async () => {
+        onPersist();
+      },
+    };
+  };
+}
diff --git a/packages/runtime/src/__tests__/execution-boundary-test-helpers.ts b/packages/runtime/src/__tests__/execution-boundary-test-helpers.ts
index 65900de151..a40de02125 100644
--- a/packages/runtime/src/__tests__/execution-boundary-test-helpers.ts
+++ b/packages/runtime/src/__tests__/execution-boundary-test-helpers.ts
@@ -31,12 +31,44 @@ export const readExternalExecutionBoundary: AiSdkBackendInput['readExecutionBoun
   createExternalExecutionBoundary();
 
 type TestAiSdkBackendInput = Omit<AiSdkBackendInput, 'readExecutionBoundary'> &
-  Partial<Pick<AiSdkBackendInput, 'readExecutionBoundary'>>;
+  Partial<Pick<AiSdkBackendInput, 'readExecutionBoundary'>> & {
+    testProjectionArtifacts?: boolean;
+  };
 
 export function createTestAiSdkBackend(input: TestAiSdkBackendInput): AiSdkBackend {
+  const { testProjectionArtifacts, ...backendInput } = input;
+  const artifacts = new Map<string, Uint8Array>();
+  let nextArtifactId = 0;
   return new AiSdkBackend({
     readExecutionBoundary: readExternalExecutionBoundary,
-    ...input,
+    ...backendInput,
+    ...(testProjectionArtifacts
+      ? {
+          prepareDurableProjectionArtifact: ({ bytes }: { bytes: Uint8Array }) => {
+            const relativePath = `artifact-${++nextArtifactId}`;
+            const accepted = bytes.slice();
+            return {
+              ref: {
+                kind: 'session_file' as const,
+                sessionId: input.sessionId,
+                relativePath,
+              },
+              persist: async () => {
+                artifacts.set(relativePath, accepted);
+              },
+            };
+          },
+          readAttachmentBytes:
+            input.readAttachmentBytes ??
+            (async (ref) => {
+              const bytes =
+                ref.kind === 'session_file' ? artifacts.get(ref.relativePath) : undefined;
+              return bytes
+                ? { ok: true as const, bytes: bytes.slice() }
+                : { ok: false as const, reason: 'not_found' as const };
+            }),
+        }
+      : {}),
   });
 }
 
diff --git a/packages/runtime/src/__tests__/overflow-reactive-recovery.test.ts b/packages/runtime/src/__tests__/overflow-reactive-recovery.test.ts
index 510795351b..b726de0dd3 100644
--- a/packages/runtime/src/__tests__/overflow-reactive-recovery.test.ts
+++ b/packages/runtime/src/__tests__/overflow-reactive-recovery.test.ts
@@ -574,7 +574,7 @@ function buildReactiveFixture(options: ReactiveFixtureOptions): ReactiveFixture
               ref: {
                 kind: 'session_file' as const,
                 sessionId: 'session-1',
-                relativePath: 'live.png',
+                relativePath: 'live_image',
               },
             };
           }
diff --git a/packages/runtime/src/__tests__/pre-dispatch-refusal-ledger.test.ts b/packages/runtime/src/__tests__/pre-dispatch-refusal-ledger.test.ts
index a85749edf0..884d8647ea 100644
--- a/packages/runtime/src/__tests__/pre-dispatch-refusal-ledger.test.ts
+++ b/packages/runtime/src/__tests__/pre-dispatch-refusal-ledger.test.ts
@@ -372,7 +372,7 @@ test('client-capability refusal carries actionable bypass metadata', async () =>
 test('arguments the schema rejects leave a matched call/response pair on the generic lane', async () => {
   const h = harness();
 
-  const { result } = await settle(h, swarmTool, {
+  const settlement = await settle(h, swarmTool, {
     items: [
       { item_id: 'a', task: 'one', subagent_id: 'reviewer' },
       { item_id: 'b', task: 'two', subagent_id: 'reviewer' },
@@ -383,7 +383,7 @@ test('arguments the schema rejects leave a matched call/response pair on the gen
 
   // The refusal still reaches the model, unchanged.
   assert.match(
-    (result as { error?: string }).error ?? '',
+    (settlement.result as { error?: string }).error ?? '',
     /Tool "exclusive_batch" arguments failed validation/,
   );
 
diff --git a/packages/runtime/src/__tests__/recovery-resolver.test.ts b/packages/runtime/src/__tests__/recovery-resolver.test.ts
index dd975fcd74..7fd2c4c5a8 100644
--- a/packages/runtime/src/__tests__/recovery-resolver.test.ts
+++ b/packages/runtime/src/__tests__/recovery-resolver.test.ts
@@ -18,9 +18,13 @@
  */
 
 import assert from 'node:assert/strict';
+import { mkdtemp, rm } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import { join } from 'node:path';
 import { describe, it } from 'node:test';
 import { canonicalToolArgsHash } from '@maka/core/tool-args-identity';
 import type { RuntimeEvent } from '@maka/core/runtime-event';
+import { createSqliteRuntimeStore } from '@maka/storage/sqlite-runtime-store';
 import {
   buildInterruptedCodeModeOutcomeCommits,
   resolveRuntimeRecovery,
@@ -139,6 +143,80 @@ describe('RecoveryResolver', () => {
         },
       },
     );
+    assert.deepEqual(
+      commits[0]?.runtimeEvent.content?.kind === 'function_response'
+        ? commits[0].runtimeEvent.content.modelProjection
+        : undefined,
+      {
+        version: 1,
+        kind: 'json',
+        value: {
+          kind: 'json',
+          value: {
+            kind: 'code_mode',
+            status: 'interrupted',
+            message: 'Code Mode execution was interrupted by runtime recovery.',
+          },
+        },
+        isError: true,
+      },
+    );
+  });
+
+  it('commits a projected recovery outcome through the projection-aware SQLite T2 gate', async () => {
+    const root = await mkdtemp(join(tmpdir(), 'maka-recovery-projection-'));
+    const store = createSqliteRuntimeStore(join(root, 'runtime.sqlite'));
+    try {
+      const call = event({
+        id: 'outer-call',
+        role: 'model',
+        author: 'agent',
+        origin: 'provider',
+        modelVisibility: 'visible',
+        content: { kind: 'function_call', id: 'exec-1', name: 'exec', args: { code: 'work()' } },
+        refs: { operationId: 'outer-op', toolCallId: 'exec-1' },
+      });
+      const dispatch = dispatchFor({
+        id: 'outer-dispatch',
+        operationId: 'outer-op',
+        toolCallId: 'exec-1',
+        toolName: 'exec',
+        args: { code: 'work()' },
+        resultProjectionVersion: 1,
+      });
+      await store.commitToolPrepared({
+        operationId: 'outer-op',
+        journalEventId: 'outer-op_prepared',
+        runtimeEvent: call,
+        dispatchRuntimeEvent: dispatch,
+        providerToolCallId: 'exec-1',
+        toolName: 'exec',
+        canonicalArgsHash: canonicalToolArgsHash('exec', { code: 'work()' }),
+        recoveryMode: 'never_auto_retry',
+        committedAt: 10,
+      });
+
+      const [commit] = buildInterruptedCodeModeOutcomeCommits(
+        await store.readImmutableRuntimeEvents('session-1', 'run-1'),
+        50,
+        'code_mode',
+      );
+      assert.ok(commit);
+      await store.commitToolOutcome(commit);
+
+      assert.equal((await store.readToolOperation('outer-op'))?.currentState, 'outcome_committed');
+      const response = (await store.readImmutableRuntimeEvents('session-1', 'run-1')).at(-1);
+      assert.equal(response?.content?.kind, 'function_response');
+      assert.equal(
+        response?.content?.kind === 'function_response'
+          ? response.content.modelProjection?.version
+          : undefined,
+        1,
+      );
+    } finally {
+      store.close();
+      await rm(root, { recursive: true, force: true });
+    }
   });
 
   it('does not infer Code Mode recovery from a custom direct exec name', () => {
@@ -399,6 +477,7 @@ function dispatchFor(input: {
   modelVisibility?: 'visible' | 'hidden';
   parentOperationId?: string;
   parentToolCallId?: string;
+  resultProjectionVersion?: 1;
 }): RuntimeEvent {
   return event({
     id: input.id,
@@ -412,6 +491,9 @@ function dispatchFor(input: {
         toolName: input.toolName,
         canonicalArgsHash: canonicalToolArgsHash(input.toolName, input.args),
         recoveryMode: 'never_auto_retry',
+        ...(input.resultProjectionVersion !== undefined
+          ? { resultProjectionVersion: input.resultProjectionVersion }
+          : {}),
       },
     },
     refs: {
diff --git a/packages/runtime/src/__tests__/session-manager.test.ts b/packages/runtime/src/__tests__/session-manager.test.ts
index 880a44922e..b1b9bb7e12 100644
--- a/packages/runtime/src/__tests__/session-manager.test.ts
+++ b/packages/runtime/src/__tests__/session-manager.test.ts
@@ -28,7 +28,6 @@ import {
 } from '@maka/core/sandbox-boundary';
 import { createWorkspaceWritePermissionProfile } from '@maka/core/permission-profile';
 import { DEEP_RESEARCH_SESSION_LABEL } from '@maka/core/deep-research';
-import { SIDE_CONVERSATION_SESSION_LABEL } from '@maka/core/side-conversation';
 import { RUNTIME_CONTINUATION_AUTHORITY_V1 } from '@maka/core/runtime-event-store';
 import { deriveTurnRecords } from '@maka/core/session';
 import { isSessionInlineRun } from '@maka/core/agent-run';
@@ -48,7 +47,7 @@ import type {
   UserMessageInput,
 } from '@maka/core/runtime-inputs';
 import type { ExecutionBoundary } from '@maka/core/sandbox-boundary';
-import type { SessionEvent, ShellRunSnapshotResult } from '@maka/core/events';
+import type { SessionEvent } from '@maka/core/events';
 import type {
   AgentGraphIntentClaim,
   AgentGraphIntentClaimStore,
@@ -8632,277 +8631,6 @@ describe('SessionManager permission mode updates', () => {
     expect(regenUser?.type === 'user' ? regenUser.text : undefined).toBe('aborted turn text');
   });
 
-  test('branchFromTurn copies through the RuntimeEvent-primary message boundary', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const manager = makeManagerForReadCutover(store, runStore);
-    const session = await manager.createSession(makeInput({ name: 'Parent' }));
-    await seedRuntimeReadTurn({
-      store,
-      runStore,
-      sessionId: session.id,
-      turnId: 'source',
-      runId: 'source-run',
-      userText: 'runtime branch context',
-      assistantText: 'runtime branch answer',
-      legacyIdPrefix: 'legacy',
-    });
-    store.failNextReadMessagesFor.set(session.id, 1);
-
-    const child = await manager.branchFromTurn(session.id, {
-      sourceTurnId: 'source',
-      name: 'Child',
-    });
-
-    const childMessages = await store.readMessages(child.id);
-    expect(childMessages[0]).toMatchObject({
-      type: 'user',
-      turnId: 'source',
-      text: 'runtime branch context',
-    });
-    expect(childMessages[1]).toMatchObject({
-      type: 'assistant',
-      turnId: 'source',
-      text: 'runtime branch answer',
-    });
-    expect(childMessages[2]).toMatchObject({ type: 'system_note', kind: 'session_start' });
-    expect(childMessages.some((message) => message.type === 'turn_state')).toBe(false);
-
-    const runtimeMessages = await manager.getMessages(child.id);
-    expect(runtimeMessages[0]).toMatchObject({
-      type: 'user',
-      turnId: 'source',
-      text: 'runtime branch context',
-    });
-    expect(runtimeMessages[1]).toMatchObject({
-      type: 'assistant',
-      turnId: 'source',
-      text: 'runtime branch answer',
-    });
-  });
-
-  test('conversation copies materialize requestless hosted permission history', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const backends = new BackendRegistry();
-    const backendInstances: TestBackend[] = [];
-    backends.register('ai-sdk', (ctx) => {
-      const backend = new TestBackend(ctx);
-      backendInstances.push(backend);
-      return backend;
-    });
-    const session = await store.create(makeInput({ name: 'Parent' }));
-    const header = makeRunHeader({
-      sessionId: session.id,
-      runId: 'source-run',
-      turnId: 'source',
-      status: 'completed',
-      createdAt: 100,
-      updatedAt: 130,
-      completedAt: 130,
-    });
-    await seedCanonicalPermissionRun(runStore, header, false);
-    const sourcePermissionEvents = await runStore.readRuntimeEvents(session.id, header.runId);
-    expect(sourcePermissionEvents.some((event) => event.actions?.permissionRequest)).toBe(false);
-    expect(sourcePermissionEvents.some((event) => event.content?.kind === 'function_call')).toBe(
-      false,
-    );
-    await seedRuntimeRun(
-      runStore,
-      makeRunHeader({
-        sessionId: session.id,
-        runId: 'later-run',
-        turnId: 'later',
-        status: 'completed',
-        createdAt: 140,
-        updatedAt: 141,
-        completedAt: 141,
-      }),
-      [
-        runtimeEvent({
-          id: 'later-user',
-          sessionId: session.id,
-          runId: 'later-run',
-          turnId: 'later',
-          ts: 140,
-          role: 'user',
-          author: 'user',
-          content: { kind: 'text', text: 'replace this turn' },
-        }),
-        runtimeEvent({
-          id: 'later-terminal',
-          sessionId: session.id,
-          runId: 'later-run',
-          turnId: 'later',
-          ts: 141,
-          status: 'completed',
-          actions: { endInvocation: true },
-        }),
-      ],
-    );
-    let sourceInteractionAvailable = true;
-    const manager = new SessionManager({
-      store,
-      runStore,
-      runtimeEventStore: runStore,
-      backends,
-      newId: nextId(),
-      now: nextNow(6_755),
-      interactionAuthority: testInteractionAuthority(),
-      canonicalPermissionOutcomes: {
-        readPermissionOutcome: async () =>
-          sourceInteractionAvailable ? canonicalPermissionRecord(header) : undefined,
-      },
-    });
-
-    const child = await manager.branchFromTurn(session.id, {
-      sourceTurnId: header.turnId,
-      name: 'Child',
-    });
-    const revision = await manager.reviseBeforeTurn(session.id, { sourceTurnId: 'later' });
-    sourceInteractionAvailable = false;
-
-    const [childSourceRun] = await runStore.listSessionRuns(child.id);
-    const childSourceEvents = childSourceRun
-      ? await runStore.readRuntimeEvents(child.id, childSourceRun.runId)
-      : [];
-    expect(
-      childSourceEvents.find((event) => event.actions?.permissionDecision)?.actions
-        ?.permissionDecision,
-    ).toMatchObject({
-      requestId: 'request-canonical',
-      toolName: 'Write',
-      decision: 'deny',
-    });
-    expect(childSourceEvents.some((event) => event.actions?.permissionRequest)).toBe(false);
-    expect(childSourceEvents.some((event) => event.actions?.permissionAnswerAccepted)).toBe(false);
-
-    for (const copiedSession of [child, revision]) {
-      expect(
-        (await manager.getMessages(copiedSession.id)).find(
-          (message) => message.type === 'permission_decision',
-        ),
-      ).toMatchObject({
-        type: 'permission_decision',
-        id: 'request-canonical',
-        turnId: header.turnId,
-        toolUseId: 'tool-canonical',
-        toolName: 'Write',
-        decision: 'deny',
-        reviewer: 'user',
-      });
-    }
-
-    await drain(manager.sendMessage(child.id, { turnId: 'child-next', text: 'continue' }));
-    expect(
-      backendInstances
-        .find((backend) => backend.sessionId === child.id)
-        ?.sendInputs[0]?.context.find((message) => message.type === 'permission_decision'),
-    ).toMatchObject({
-      id: 'request-canonical',
-      toolUseId: 'tool-canonical',
-      toolName: 'Write',
-      decision: 'deny',
-    });
-  });
-
-  test('branch child next turn receives cloned RuntimeEvent context', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const backends = new BackendRegistry();
-    const backendInstances: TestBackend[] = [];
-    backends.register('ai-sdk', (ctx) => {
-      const backend = new TestBackend(ctx);
-      backendInstances.push(backend);
-      return backend;
-    });
-    const manager = new SessionManager({
-      store,
-      runStore,
-      runtimeEventStore: runStore,
-      backends,
-      newId: nextId(),
-      now: nextNow(6_870),
-    });
-    const session = await manager.createSession(makeInput({ name: 'Parent' }));
-
-    await drain(manager.sendMessage(session.id, { turnId: 'source', text: 'branch seed' }));
-    const child = await manager.branchFromTurn(session.id, {
-      sourceTurnId: 'source',
-      name: 'Child',
-    });
-    await store.appendMessage(child.id, {
-      type: 'assistant',
-      id: 'child-cache-only',
-      turnId: 'cache-only',
-      ts: 6_999,
-      text: 'cache-only child context',
-      modelId: 'fake-model',
-    });
-
-    await drain(manager.sendMessage(child.id, { turnId: 'child-next', text: 'child follow-up' }));
-
-    const childInput = backendInstances[1]?.sendInputs[0];
-    if (!childInput) throw new Error('child backend input was not recorded');
-    expect(
-      childInput.context.some(
-        (message) =>
-          message.type === 'user' && message.turnId === 'source' && message.text === 'branch seed',
-      ),
-    ).toBe(true);
-    expect(
-      childInput.context.some(
-        (message) => message.type === 'assistant' && message.id === 'child-cache-only',
-      ),
-    ).toBe(false);
-    expect(childInput.runtimeContext?.map((event) => event.turnId)).toEqual([
-      'source',
-      'source',
-      'source',
-    ]);
-    expect(childInput.runtimeContext?.[0]?.sessionId).toBe(child.id);
-  });
-
-  test('branchFromTurn removes an incomplete target when Runtime ledger copy fails', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore({ failRuntimeEventAppendAfter: 5 });
-    const manager = makeManagerForReadCutover(store, runStore);
-    const session = await manager.createSession(makeInput({ name: 'Parent' }));
-    await seedRuntimeReadTurn({
-      store,
-      runStore,
-      sessionId: session.id,
-      turnId: 'source',
-      runId: 'source-run',
-      userText: 'runtime branch context',
-      assistantText: 'runtime branch answer',
-      legacyIdPrefix: 'legacy',
-    });
-
-    let branchError: unknown;
-    try {
-      await manager.branchFromTurn(session.id, { sourceTurnId: 'source', name: 'Child' });
-    } catch (error) {
-      branchError = error;
-    }
-    expect(branchError instanceof Error ? branchError.message : String(branchError)).toContain(
-      'runtime event append failed',
-    );
-
-    const child = (await store.list()).find((summary) => summary.parentSessionId === session.id);
-    expect(child).toBeUndefined();
-    const childRuns = await runStore.listSessionRuns('session-2');
-    for (const run of childRuns) {
-      const runtimeEvents = await runStore.readRuntimeEvents('session-2', run.runId);
-      const hasTerminalFact = runtimeEvents.some(isTerminalRuntimeEvent);
-      expect(
-        run.status === 'completed' || run.status === 'failed' || run.status === 'cancelled'
-          ? hasTerminalFact
-          : true,
-      ).toBe(true);
-    }
-  });
-
   test('multi-run RuntimeEvent projection preserves retry regenerate and branch lineage on turns', async () => {
     const store = new MemorySessionStore();
     const runStore = new MemoryAgentRunStore();
@@ -11737,727 +11465,6 @@ describe('SessionManager permission mode updates', () => {
     const [turn] = await store.listTurns(session.id);
     expect(turn?.errorClass).toBe('app_restarted');
   });
-
-  test('branchFromTurn marks side conversations without changing ordinary branches', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const backends = new BackendRegistry();
-    backends.register(
-      'ai-sdk',
-      (ctx) => new EventBackend(ctx, [{ type: 'complete', stopReason: 'end_turn' }]),
-    );
-    const manager = new SessionManager({
-      store,
-      runStore,
-      runtimeEventStore: runStore,
-      backends,
-      newId: nextId(),
-      now: nextNow(15_500),
-    });
-    const session = await manager.createSession(makeInput({ name: 'Parent', labels: ['kept'] }));
-    await drain(manager.sendMessage(session.id, { turnId: 'source', text: 'context' }));
-
-    const ordinary = await manager.branchFromTurn(session.id, {
-      sourceTurnId: 'source',
-      name: 'Ordinary',
-    });
-    const side = await manager.branchFromTurn(session.id, {
-      sourceTurnId: 'source',
-      name: 'Side',
-      sideConversation: true,
-    });
-
-    expect(ordinary.labels).toEqual(['kept']);
-    expect(side.labels).toEqual(['kept', SIDE_CONVERSATION_SESSION_LABEL]);
-  });
-
-  test('conversation copies inherit the authoritative execution boundary', async () => {
-    const store = new AtomicBoundaryMemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const backends = new BackendRegistry();
-    backends.register(
-      'ai-sdk',
-      (ctx) => new EventBackend(ctx, [{ type: 'complete', stopReason: 'end_turn' }]),
-    );
-    const manager = new SessionManager({
-      store,
-      runStore,
-      runtimeEventStore: runStore,
-      backends,
-      newId: nextId(),
-      now: nextNow(15_050),
-    });
-    const boundaries: ExecutionBoundary[] = [
-      {
-        kind: 'managed',
-        revision: 4,
-        profile: applySandboxBoundaryExpansion(createWorkspaceWritePermissionProfile(), {
-          filesystem: {
-            entries: [{ path: '/approved/release.txt', access: 'write', scope: 'exact' }],
-          },
-        }),
-      },
-      { kind: 'external', revision: 3 },
-    ];
-
-    for (const [index, boundary] of boundaries.entries()) {
-      const source = await manager.createSession(makeInput({ name: `Source ${index}` }));
-      await drain(manager.sendMessage(source.id, { turnId: 'first', text: 'keep' }));
-      await drain(manager.sendMessage(source.id, { turnId: 'second', text: 'replace' }));
-      store.forceBoundary(source.id, boundary);
-
-      const branch = await manager.branchFromTurn(source.id, { sourceTurnId: 'first' });
-      const revision = await manager.reviseBeforeTurn(source.id, { sourceTurnId: 'second' });
-
-      for (const copy of [branch, revision]) {
-        expect(await store.readExecutionBoundary(copy.id)).toEqual({
-          ...boundary,
-          revision: 0,
-        });
-      }
-    }
-  });
-
-  test('branch and revision sessions rewrite tool operation authority facts', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const backends = new BackendRegistry();
-    const manager = new SessionManager({
-      store,
-      runStore,
-      runtimeEventStore: runStore,
-      backends,
-      newId: nextId(),
-      now: nextNow(15_100),
-    });
-    const session = await manager.createSession(makeInput({ name: 'Authority source' }));
-    const header = await store.readHeader(session.id);
-    await seedRuntimeRun(
-      runStore,
-      makeRunHeader({
-        sessionId: session.id,
-        runId: 'authority-source-run',
-        turnId: 'authority-source-turn',
-        status: 'completed',
-        cwd: header.cwd,
-        createdAt: 1,
-        updatedAt: 3,
-        completedAt: 3,
-      }),
-      [
-        runtimeEvent({
-          id: 'authority-source-user',
-          sessionId: session.id,
-          invocationId: 'authority-source-invocation',
-          runId: 'authority-source-run',
-          turnId: 'authority-source-turn',
-          ts: 1,
-          role: 'user',
-          author: 'user',
-          content: { kind: 'text', text: 'authority-bearing history' },
-        }),
-        runtimeEvent({
-          id: 'authority-source-dispatch',
-          sessionId: session.id,
-          invocationId: 'authority-source-invocation',
-          runId: 'authority-source-run',
-          turnId: 'authority-source-turn',
-          ts: 2,
-          role: 'system',
-          author: 'system',
-          actions: {
-            toolDispatch: {
-              protocol: 't1_after_preflight_v1',
-              operationId: 'authority-operation',
-              providerToolCallId: 'authority-call',
-              toolName: 'Write',
-              canonicalArgsHash: `sha256:${'a'.repeat(64)}`,
-              recoveryMode: 'reconcile',
-            },
-          },
-          refs: { toolCallId: 'authority-call', operationId: 'authority-operation' },
-        }),
-        runtimeEvent({
-          id: 'authority-source-terminal',
-          sessionId: session.id,
-          invocationId: 'authority-source-invocation',
-          runId: 'authority-source-run',
-          turnId: 'authority-source-turn',
-          ts: 3,
-          status: 'completed',
-          actions: { endInvocation: true },
-        }),
-      ],
-    );
-    await seedRuntimeRun(
-      runStore,
-      makeRunHeader({
-        sessionId: session.id,
-        runId: 'authority-later-run',
-        turnId: 'authority-later-turn',
-        status: 'completed',
-        cwd: header.cwd,
-        createdAt: 4,
-        updatedAt: 5,
-        completedAt: 5,
-      }),
-      [
-        runtimeEvent({
-          id: 'authority-later-user',
-          sessionId: session.id,
-          invocationId: 'authority-later-invocation',
-          runId: 'authority-later-run',
-          turnId: 'authority-later-turn',
-          ts: 4,
-          role: 'user',
-          author: 'user',
-          content: { kind: 'text', text: 'later boundary' },
-        }),
-        runtimeEvent({
-          id: 'authority-later-terminal',
-          sessionId: session.id,
-          invocationId: 'authority-later-invocation',
-          runId: 'authority-later-run',
-          turnId: 'authority-later-turn',
-          ts: 5,
-          status: 'completed',
-          actions: { endInvocation: true },
-        }),
-      ],
-    );
-
-    const copies = [
-      await manager.branchFromTurn(session.id, {
-        sourceTurnId: 'authority-source-turn',
-        name: 'Tool branch',
-      }),
-      await manager.branchBeforeTurn(session.id, {
-        sourceTurnId: 'authority-later-turn',
-        name: 'Tool branch before later turn',
-      }),
-      await manager.reviseBeforeTurn(session.id, {
-        sourceTurnId: 'authority-later-turn',
-      }),
-    ];
-    for (const copy of copies) {
-      const [targetRun] = await runStore.listSessionRuns(copy.id);
-      assert.ok(targetRun);
-      const targetEvents = await runStore.readRuntimeEvents(copy.id, targetRun.runId);
-      const dispatch = targetEvents.find((event) => event.actions?.toolDispatch);
-      const targetOperationId = dispatch?.actions?.toolDispatch?.operationId;
-      assert.ok(targetOperationId);
-      assert.notEqual(targetOperationId, 'authority-operation');
-      assert.equal(dispatch?.refs?.operationId, targetOperationId);
-      assert.equal(targetRun.sessionId, copy.id);
-    }
-  });
-
-  test('fails before creating branch or revision sessions for child continuation authority', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const manager = new SessionManager({
-      store,
-      runStore,
-      runtimeEventStore: runStore,
-      backends: new BackendRegistry(),
-      newId: nextId(),
-      now: nextNow(15_150),
-    });
-    const session = await manager.createSession(makeInput({ name: 'Legacy continuation' }));
-    const header = await store.readHeader(session.id);
-    await seedRuntimeRun(
-      runStore,
-      makeRunHeader({
-        sessionId: session.id,
-        runId: 'continuation-parent-run',
-        turnId: 'continuation-parent-turn',
-        status: 'completed',
-        cwd: header.cwd,
-        createdAt: 1,
-        updatedAt: 2,
-        completedAt: 2,
-      }),
-      [
-        runtimeEvent({
-          id: 'continuation-parent-user',
-          sessionId: session.id,
-          invocationId: 'continuation-parent-invocation',
-          runId: 'continuation-parent-run',
-          turnId: 'continuation-parent-turn',
-          ts: 1,
-          role: 'user',
-          author: 'user',
-          content: { kind: 'text', text: 'retain parent and child closure' },
-        }),
-        runtimeEvent({
-          id: 'continuation-parent-terminal',
-          sessionId: session.id,
-          invocationId: 'continuation-parent-invocation',
-          runId: 'continuation-parent-run',
-          turnId: 'continuation-parent-turn',
-          ts: 2,
-          status: 'completed',
-          actions: { endInvocation: true },
-        }),
-      ],
-    );
-    await seedRuntimeRun(
-      runStore,
-      makeRunHeader({
-        sessionId: session.id,
-        runId: 'legacy-continuation-run',
-        turnId: 'legacy-child-turn',
-        parentRunId: 'continuation-parent-run',
-        agentId: 'child-agent',
-        status: 'completed',
-        cwd: header.cwd,
-        continuationSource: {
-          sourceInvocationId: 'legacy-source-invocation',
-          sourceRunId: 'legacy-source-run',
-          sourceTurnId: 'legacy-source-turn',
-          sourceRuntimeEventHighWater: 1,
-        },
-        createdAt: 3,
-        updatedAt: 4,
-        completedAt: 4,
-      }),
-      [
-        runtimeEvent({
-          id: 'legacy-continuation-user',
-          sessionId: session.id,
-          invocationId: 'legacy-continuation-run',
-          runId: 'legacy-continuation-run',
-          turnId: 'legacy-child-turn',
-          ts: 3,
-          role: 'user',
-          author: 'user',
-          content: { kind: 'text', text: 'legacy continuation history' },
-        }),
-        runtimeEvent({
-          id: 'legacy-continuation-terminal',
-          sessionId: session.id,
-          invocationId: 'legacy-continuation-run',
-          runId: 'legacy-continuation-run',
-          turnId: 'legacy-child-turn',
-          ts: 4,
-          status: 'completed',
-          actions: { endInvocation: true },
-        }),
-      ],
-    );
-    await seedRuntimeRun(
-      runStore,
-      makeRunHeader({
-        sessionId: session.id,
-        runId: 'continuation-later-run',
-        turnId: 'continuation-later-turn',
-        status: 'completed',
-        cwd: header.cwd,
-        createdAt: 5,
-        updatedAt: 6,
-        completedAt: 6,
-      }),
-      [
-        runtimeEvent({
-          id: 'continuation-later-user',
-          sessionId: session.id,
-          invocationId: 'continuation-later-invocation',
-          runId: 'continuation-later-run',
-          turnId: 'continuation-later-turn',
-          ts: 5,
-          role: 'user',
-          author: 'user',
-          content: { kind: 'text', text: 'later boundary' },
-        }),
-        runtimeEvent({
-          id: 'continuation-later-terminal',
-          sessionId: session.id,
-          invocationId: 'continuation-later-invocation',
-          runId: 'continuation-later-run',
-          turnId: 'continuation-later-turn',
-          ts: 6,
-          status: 'completed',
-          actions: { endInvocation: true },
-        }),
-      ],
-    );
-
-    const attempts = [
-      () =>
-        manager.branchFromTurn(session.id, {
-          sourceTurnId: 'continuation-parent-turn',
-          name: 'Blocked child continuation branch',
-        }),
-      () =>
-        manager.branchBeforeTurn(session.id, {
-          sourceTurnId: 'continuation-later-turn',
-          name: 'Blocked child continuation branch before',
-        }),
-      () =>
-        manager.reviseBeforeTurn(session.id, {
-          sourceTurnId: 'continuation-later-turn',
-        }),
-    ];
-    for (const attempt of attempts) {
-      await expectRejects(attempt(), /typed identity rewriting/i);
-    }
-    expect((await store.list()).map((candidate) => candidate.id)).toEqual([session.id]);
-  });
-
-  test('hydrates an inherited running ShellRun with its source-session owner', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const backends = new BackendRegistry();
-    const ref = 'maka://runtime/background-tasks/pty-parent';
-    const sourceSnapshot: ShellRunSnapshotResult = {
-      kind: 'shell_run',
-      ref,
-      mode: 'pty',
-      status: 'running',
-      cwd: '/tmp/workspace',
-      cmd: 'interactive',
-      startedAt: 1,
-      updatedAt: 2,
-      revision: 2,
-      output: {
-        mode: 'pty',
-        screen: 'ready',
-        scrollback: '',
-        cols: 80,
-        rows: 24,
-        cursor: { x: 5, y: 0, visible: true },
-        alternateScreen: false,
-        truncated: false,
-        redacted: false,
-      },
-    };
-    let ownerAvailable = true;
-    const shellRuns = {
-      async listSessionUpdates() {
-        return [];
-      },
-      async getSessionUpdate() {
-        return undefined;
-      },
-      async inspectResource(sessionId: string, candidateRef: string) {
-        if (ownerAvailable && candidateRef === ref && sessionId === 'session-1')
-          return sourceSnapshot;
-        const error = new Error('missing') as NodeJS.ErrnoException;
-        error.code = 'ENOENT';
-        throw error;
-      },
-    } as unknown as ShellRunProcessManager;
-    const manager = new SessionManager({
-      store,
-      runStore,
-      runtimeEventStore: runStore,
-      backends,
-      shellRuns,
-      newId: nextId(),
-      now: nextNow(15_250),
-    });
-    const parent = await manager.createSession(makeInput({ name: 'Parent' }));
-    await seedRuntimeRun(
-      runStore,
-      makeRunHeader({
-        sessionId: parent.id,
-        runId: 'source-run',
-        turnId: 'source',
-        status: 'completed',
-        createdAt: 1,
-        updatedAt: 4,
-        completedAt: 4,
-      }),
-      [
-        runtimeEvent({
-          id: 'user-1',
-          sessionId: parent.id,
-          runId: 'source-run',
-          turnId: 'source',
-          ts: 1,
-          role: 'user',
-          author: 'user',
-          content: { kind: 'text', text: 'start' },
-        }),
-        runtimeEvent({
-          id: 'call-1',
-          sessionId: parent.id,
-          runId: 'source-run',
-          turnId: 'source',
-          ts: 2,
-          role: 'model',
-          author: 'agent',
-          content: {
-            kind: 'function_call',
-            id: 'bash-1',
-            name: 'Bash',
-            args: { command: 'interactive', run_in_background: true, pty: true },
-          },
-          refs: { toolCallId: 'bash-1' },
-        }),
-        runtimeEvent({
-          id: 'result-1',
-          sessionId: parent.id,
-          runId: 'source-run',
-          turnId: 'source',
-          ts: 3,
-          role: 'tool',
-          author: 'tool',
-          content: {
-            kind: 'function_response',
-            id: 'bash-1',
-            name: 'Bash',
-            result: {
-              kind: 'shell_run',
-              ref,
-              mode: 'pty',
-              status: 'running',
-              cwd: '/tmp/workspace',
-              cmd: 'interactive',
-              startedAt: 1,
-              updatedAt: 1,
-              revision: 1,
-            },
-            isError: false,
-          },
-          refs: { toolCallId: 'bash-1' },
-        }),
-        runtimeEvent({
-          id: 'complete-1',
-          sessionId: parent.id,
-          runId: 'source-run',
-          turnId: 'source',
-          ts: 4,
-          role: 'system',
-          author: 'system',
-          status: 'completed',
-          actions: { endInvocation: true },
-        }),
-      ],
-    );
-    await seedRuntimeRun(
-      runStore,
-      makeRunHeader({
-        sessionId: parent.id,
-        runId: 'later-run',
-        turnId: 'later',
-        status: 'completed',
-        createdAt: 5,
-        updatedAt: 6,
-        completedAt: 6,
-      }),
-      [
-        runtimeEvent({
-          id: 'later-user',
-          sessionId: parent.id,
-          runId: 'later-run',
-          turnId: 'later',
-          ts: 5,
-          role: 'user',
-          author: 'user',
-          content: { kind: 'text', text: 'later' },
-        }),
-        runtimeEvent({
-          id: 'later-complete',
-          sessionId: parent.id,
-          runId: 'later-run',
-          turnId: 'later',
-          ts: 6,
-          role: 'system',
-          author: 'system',
-          status: 'completed',
-          actions: { endInvocation: true },
-        }),
-      ],
-    );
-    const child = await manager.branchFromTurn(parent.id, {
-      sourceTurnId: 'source',
-      name: 'Child',
-    });
-    const revision = await manager.reviseBeforeTurn(parent.id, { sourceTurnId: 'later' });
-
-    const updates = await manager.listShellRunUpdates(child.id);
-
-    expect(updates).toHaveLength(1);
-    expect(updates[0]?.sessionId).toBe(child.id);
-    expect(updates[0]?.ownership).toEqual({
-      kind: 'source_owned',
-      sourceSessionId: parent.id,
-      ownerSessionId: parent.id,
-    });
-    expect(updates[0]?.sourceToolCallId).toBe('bash-1');
-    expect(updates[0]?.result).toEqual(sourceSnapshot);
-    expect(await manager.getShellRunUpdate(child.id, sourceSnapshot.ref)).toEqual(updates[0]);
-    expect(
-      await manager.getShellRunUpdate(child.id, 'maka://runtime/background-tasks/missing-shell'),
-    ).toBeNull();
-
-    const revisionUpdates = await manager.listShellRunUpdates(revision.id);
-    expect(revisionUpdates).toHaveLength(1);
-    expect(revisionUpdates[0]?.ownership).toEqual({
-      kind: 'source_owned',
-      sourceSessionId: parent.id,
-      ownerSessionId: parent.id,
-    });
-    expect(revisionUpdates[0]?.result).toEqual(sourceSnapshot);
-
-    ownerAvailable = false;
-    await store.remove(parent.id);
-    const danglingUpdates = await manager.listShellRunUpdates(child.id);
-    expect(danglingUpdates).toHaveLength(1);
-    expect(danglingUpdates[0]?.sessionId).toBe(child.id);
-    expect(danglingUpdates[0]?.ownership).toEqual({
-      kind: 'source_unavailable',
-      sourceSessionId: parent.id,
-    });
-    expect(danglingUpdates[0]?.result.status).toBe('running');
-    expect(danglingUpdates[0]?.result.output).toBe(undefined);
-    expect(await manager.getShellRunUpdate(child.id, sourceSnapshot.ref)).toEqual(
-      danglingUpdates[0],
-    );
-  });
-
-  test('reviseBeforeTurn creates an in-conversation version without ordinary branch lineage', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const backends = new BackendRegistry();
-    backends.register(
-      'ai-sdk',
-      (ctx) => new EventBackend(ctx, [{ type: 'complete', stopReason: 'end_turn' }]),
-    );
-    const manager = new SessionManager({
-      store,
-      runStore,
-      runtimeEventStore: runStore,
-      backends,
-      newId: nextId(),
-      now: nextNow(16_700),
-    });
-    const session = await manager.createSession(
-      makeInput({
-        name: 'Conversation',
-        projectId: 'project-1',
-        collaborationMode: 'plan',
-        orchestrationMode: 'swarm',
-      }),
-    );
-    await manager.setFlagged(session.id, true);
-    await drain(manager.sendMessage(session.id, { turnId: 'first', text: 'keep me' }));
-    await drain(manager.sendMessage(session.id, { turnId: 'second', text: 'replace me' }));
-
-    const version2 = await manager.reviseBeforeTurn(session.id, { sourceTurnId: 'second' });
-
-    expect(version2.name).toBe('Conversation');
-    expect(version2.projectId).toBe('project-1');
-    expect(version2.isFlagged).toBe(true);
-    expect(version2.collaborationMode).toBe('plan');
-    expect(version2.orchestrationMode).toBe('swarm');
-    expect(version2.parentSessionId).toBeUndefined();
-    expect(version2.branchOfTurnId).toBeUndefined();
-    expect(version2.revisionRootSessionId).toBe(session.id);
-    expect(version2.revisionParentSessionId).toBe(session.id);
-    expect(version2.revisionOfTurnId).toBe('second');
-    expect(version2.revisionIndex).toBe(2);
-    expect(version2.revisionState).toBe('preparing');
-    expect((await store.readHeader(version2.id)).collaborationMode).toBe('plan');
-    expect((await store.readHeader(version2.id)).orchestrationMode).toBe('swarm');
-    await drain(
-      manager.sendMessage(
-        version2.id,
-        { turnId: 'edited-second', text: 'replacement' },
-        {
-          onRunStarted: async () => {
-            await manager.commitRevisionVersion(version2.id);
-          },
-        },
-      ),
-    );
-    expect((await store.readHeader(version2.id)).revisionState).toBe('committed');
-    const messages = await store.readMessages(version2.id);
-    expect(messages.some((message) => (message as { turnId?: string }).turnId === 'first')).toBe(
-      true,
-    );
-    expect(messages.some((message) => (message as { turnId?: string }).turnId === 'second')).toBe(
-      false,
-    );
-
-    const version3 = await manager.reviseBeforeTurn(version2.id, { sourceTurnId: 'first' });
-    expect(version3.projectId).toBe('project-1');
-    expect(version3.revisionRootSessionId).toBe(session.id);
-    expect(version3.revisionParentSessionId).toBe(version2.id);
-    expect(version3.revisionIndex).toBe(3);
-    expect(version3.revisionState).toBe('preparing');
-    expect(version3.parentSessionId).toBeUndefined();
-  });
-
-  test('startup recovery removes empty preparing revisions and commits admitted edits', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const backends = new BackendRegistry();
-    backends.register(
-      'ai-sdk',
-      (ctx) => new EventBackend(ctx, [{ type: 'complete', stopReason: 'end_turn' }]),
-    );
-    const manager = new SessionManager({
-      store,
-      runStore,
-      runtimeEventStore: runStore,
-      backends,
-      newId: nextId(),
-      now: nextNow(16_900),
-    });
-    const root = await manager.createSession(makeInput({ name: 'Recovery root' }));
-    await drain(manager.sendMessage(root.id, { turnId: 'first', text: 'original' }));
-
-    const empty = await manager.reviseBeforeTurn(root.id, { sourceTurnId: 'first' });
-    expect(empty.revisionState).toBe('preparing');
-    await manager.recoverInterruptedSessions();
-    let removedError: unknown;
-    try {
-      await store.readHeader(empty.id);
-    } catch (error) {
-      removedError = error;
-    }
-    expect(removedError instanceof Error ? removedError.message : String(removedError)).toContain(
-      'Unknown session',
-    );
-
-    const admitted = await manager.reviseBeforeTurn(root.id, { sourceTurnId: 'first' });
-    await drain(manager.sendMessage(admitted.id, { turnId: 'edited', text: 'edited prompt' }));
-    expect((await store.readHeader(admitted.id)).revisionState).toBe('preparing');
-    await manager.recoverInterruptedSessions();
-    expect((await store.readHeader(admitted.id)).revisionState).toBe('committed');
-  });
-
-  test('branchBeforeTurn rejects an unknown turn', async () => {
-    const store = new MemorySessionStore();
-    const runStore = new MemoryAgentRunStore();
-    const backends = new BackendRegistry();
-    backends.register(
-      'ai-sdk',
-      (ctx) => new EventBackend(ctx, [{ type: 'complete', stopReason: 'end_turn' }]),
-    );
-    const manager = new SessionManager({
-      store,
-      runStore,
-      runtimeEventStore: runStore,
-      backends,
-      newId: nextId(),
-      now: nextNow(16_800),
-    });
-    const session = await manager.createSession(makeInput({ name: 'Parent' }));
-    await drain(manager.sendMessage(session.id, { turnId: 'only', text: 'the one prompt' }));
-
-    let branchError: unknown;
-    try {
-      await manager.branchBeforeTurn(session.id, { sourceTurnId: 'nope' });
-    } catch (error) {
-      branchError = error;
-    }
-    expect(branchError instanceof Error ? branchError.message : String(branchError)).toContain(
-      'Cannot branch before unknown turn',
-    );
-  });
 });
 
 async function drainAll(iterable: AsyncIterable<SessionEvent>): Promise<SessionEvent[]> {
diff --git a/packages/runtime/src/__tests__/tool-runtime-durable-boundary.test.ts b/packages/runtime/src/__tests__/tool-runtime-durable-boundary.test.ts
index e6a56f7c66..c6f52e935f 100644
--- a/packages/runtime/src/__tests__/tool-runtime-durable-boundary.test.ts
+++ b/packages/runtime/src/__tests__/tool-runtime-durable-boundary.test.ts
@@ -24,6 +24,7 @@ import type { LlmConnection } from '@maka/core/llm-connections';
 import type { SessionEvent } from '@maka/core/events';
 import type { SessionHeader, StoredMessage } from '@maka/core/session';
 import { ToolOutcomeUnknownError } from '@maka/core/events';
+import type { RuntimeEvent } from '@maka/core/runtime-event';
 import type {
   RuntimeCommitSink,
   ToolOutcomeCommit,
@@ -59,17 +60,11 @@ describe('ToolRuntime durable boundary', () => {
     );
 
     assert.equal(implementationCalls, 0);
-    assert.equal(
-      harness.events.some((event) => event.type === 'tool_result'),
-      false,
-    );
-    assert.equal(
-      harness.messages.some((message) => message.type === 'tool_result'),
-      false,
-    );
+    assert.deepEqual(harness.events, []);
+    assert.deepEqual(harness.messages, []);
   });
 
-  it('does not invoke a tool when another local dispatcher already owns its operation', async () => {
+  it('publishes no call side effects when another dispatcher owns the operation', async () => {
     let implementationCalls = 0;
     const harness = makeHarness({
       commitToolPrepared: async () => ({ created: false, runtimeEventSeq: 1 }),
@@ -89,14 +84,8 @@ describe('ToolRuntime durable boundary', () => {
     );
 
     assert.equal(implementationCalls, 0);
-    assert.deepEqual(
-      harness.events.map((event) => event.type),
-      ['tool_start'],
-    );
-    assert.deepEqual(
-      harness.messages.map((message) => message.type),
-      ['tool_call'],
-    );
+    assert.deepEqual(harness.events, []);
+    assert.deepEqual(harness.messages, []);
   });
 
   it('refuses durable tool execution when the turn carries no run id', async () => {
@@ -194,6 +183,10 @@ describe('ToolRuntime durable boundary', () => {
       prepared[0]?.dispatchRuntimeEvent.actions?.toolDispatch?.protocol,
       't1_after_preflight_v1',
     );
+    assert.equal(
+      prepared[0]?.dispatchRuntimeEvent.actions?.toolDispatch?.resultProjectionVersion,
+      1,
+    );
     assert.equal(prepared[0]?.dispatchRuntimeEvent.content, undefined);
     assert.equal(outcomes[0]?.runtimeEvent.content?.kind, 'function_response');
     assert.equal(prepared[0]?.operationId, outcomes[0]?.operationId);
@@ -202,6 +195,162 @@ describe('ToolRuntime durable boundary', () => {
     assert.equal(outcomes[0]?.runtimeEvent.refs?.operationId, prepared[0]?.operationId);
   });
 
+  it('commits the completed outcome with its model projection in T2', async () => {
+    const order: string[] = [];
+    const outcomes: ToolOutcomeCommit[] = [];
+    const harness = makeHarness({
+      commitToolPrepared: async () => ({ created: true, runtimeEventSeq: 1 }),
+      commitToolOutcome: async (input) => {
+        outcomes.push(input);
+        order.push('t2');
+        return { created: true, runtimeEventSeq: 2 };
+      },
+    });
+    const projectedTool = tool(() => ({ private: 'raw execution fact' }));
+    projectedTool.toModelOutput = () => {
+      order.push('project');
+      return { type: 'text', value: 'bounded model fact' };
+    };
+
+    await harness.execute(projectedTool);
+
+    assert.deepEqual(order, ['project', 't2']);
+    const response = outcomes[0]?.runtimeEvent.content;
+    assert.deepEqual(
+      response?.kind === 'function_response' ? response.modelProjection : undefined,
+      {
+        version: 1,
+        kind: 'text',
+        text: 'bounded model fact',
+      },
+    );
+  });
+
+  it('commits one deterministic fallback when projection fails', async () => {
+    let implementationCalls = 0;
+    const outcomes: ToolOutcomeCommit[] = [];
+    const harness = makeHarness({
+      commitToolPrepared: async () => ({ created: true, runtimeEventSeq: 1 }),
+      commitToolOutcome: async (input) => {
+        outcomes.push(input);
+        return { created: true, runtimeEventSeq: 2 };
+      },
+    });
+    const unprojectableTool = tool(() => {
+      implementationCalls += 1;
+      return { private: 'completed execution fact' };
+    });
+    unprojectableTool.toModelOutput = () => {
+      throw new Error('projection implementation failed');
+    };
+
+    assert.deepEqual(await harness.execute(unprojectableTool), {
+      private: 'completed execution fact',
+    });
+
+    assert.equal(implementationCalls, 1);
+    assert.equal(outcomes.length, 1);
+    const response = outcomes[0]?.runtimeEvent.content;
+    assert.deepEqual(
+      response?.kind === 'function_response' ? response.modelProjection : undefined,
+      {
+        version: 1,
+        kind: 'failure',
+        reason: 'projection_failed',
+        message: 'The tool completed, but its model-visible result could not be projected safely.',
+      },
+    );
+  });
+
+  it('commits fallback instead of awaiting an asynchronous projector', {
+    timeout: 1_000,
+  }, async () => {
+    const outcomes: ToolOutcomeCommit[] = [];
+    const harness = makeHarness({
+      commitToolPrepared: async () => ({ created: true, runtimeEventSeq: 1 }),
+      commitToolOutcome: async (input) => {
+        outcomes.push(input);
+        return { created: true, runtimeEventSeq: 2 };
+      },
+    });
+    const invalidTool = tool(() => ({ private: 'completed execution fact' }));
+    invalidTool.toModelOutput = (() =>
+      new Promise<never>(() => undefined)) as unknown as NonNullable<MakaTool['toModelOutput']>;
+
+    await harness.execute(invalidTool);
+
+    const response = outcomes[0]?.runtimeEvent.content;
+    assert.deepEqual(
+      response?.kind === 'function_response' ? response.modelProjection : undefined,
+      {
+        version: 1,
+        kind: 'failure',
+        reason: 'projection_failed',
+        message: 'The tool completed, but its model-visible result could not be projected safely.',
+      },
+    );
+  });
+
+  it('persists inline image output as a Session artifact before committing T2', async () => {
+    const order: string[] = [];
+    const outcomes: ToolOutcomeCommit[] = [];
+    const artifactRef = {
+      kind: 'session_file' as const,
+      sessionId: 'session-1',
+      relativePath: 'artifact-1',
+    };
+    const harness = makeHarness(
+      {
+        commitToolPrepared: async () => ({ created: true, runtimeEventSeq: 1 }),
+        commitToolOutcome: async (input) => {
+          outcomes.push(input);
+          order.push('t2');
+          return { created: true, runtimeEventSeq: 2 };
+        },
+      },
+      undefined,
+      'run-1',
+      {
+        prepareDurableProjectionArtifact: (input) => {
+          assert.equal(input.turnId, 'turn-1');
+          assert.equal(input.mediaType, 'image/png');
+          assert.deepEqual([...input.bytes], [137, 80, 78, 71]);
+          return {
+            ref: artifactRef,
+            persist: async () => {
+              order.push('artifact');
+            },
+          };
+        },
+      },
+    );
+    const imageTool = tool(() => ({ private: 'raw execution fact' }));
+    imageTool.toModelOutput = () => ({
+      type: 'content',
+      value: [
+        {
+          type: 'file',
+          data: { type: 'data', data: Buffer.from([137, 80, 78, 71]).toString('base64') },
+          mediaType: 'image/png',
+        },
+      ],
+    });
+
+    await harness.execute(imageTool);
+
+    assert.deepEqual(order, ['artifact', 't2']);
+    const response = outcomes[0]?.runtimeEvent.content;
+    assert.deepEqual(
+      response?.kind === 'function_response' ? response.modelProjection : undefined,
+      {
+        version: 1,
+        kind: 'content',
+        parts: [{ kind: 'artifact', mediaType: 'image/png', ref: artifactRef }],
+      },
+    );
+    assert.doesNotMatch(JSON.stringify(response), /iVBORw/);
+  });
+
   it('adopts an owner-committed managed successor without invoking generic T2', async () => {
     const order: string[] = [];
     const prepared: ToolPreparedCommit[] = [];
@@ -1627,6 +1776,9 @@ function managedOutcomeEvent(
   isError: boolean,
   options: {
     durationMs?: number;
+    modelProjection?: NonNullable<
+      Extract<RuntimeEvent['content'], { kind: 'function_response' }>['modelProjection']
+    >;
     origin?: 'provider' | 'code_mode';
     modelVisibility?: 'visible' | 'hidden';
     toolCallId?: string;
@@ -1653,6 +1805,7 @@ function managedOutcomeEvent(
       name: 'Write',
       result,
       ...(isError ? { isError: true } : {}),
+      modelProjection: options.modelProjection ?? managedModelProjection(result),
     },
     refs: {
       operationId,
@@ -1664,6 +1817,26 @@ function managedOutcomeEvent(
   };
 }
 
+function managedModelProjection(
+  result: unknown,
+): NonNullable<Extract<RuntimeEvent['content'], { kind: 'function_response' }>['modelProjection']> {
+  const raw =
+    result &&
+    typeof result === 'object' &&
+    !Array.isArray(result) &&
+    (result as { kind?: unknown }).kind === 'json'
+      ? (result as { value: unknown }).value
+      : result;
+  const providerError =
+    raw && typeof raw === 'object' && !Array.isArray(raw)
+      ? (raw as { error?: unknown }).error
+      : undefined;
+  if (typeof providerError === 'string') {
+    return { version: 1, kind: 'text', text: `Error: ${providerError}`, isError: true };
+  }
+  return { version: 1, kind: 'json', value: raw as never };
+}
+
 function managedMutationDispatch() {
   return {
     protocol: 'managed_mutation_v2' as const,
diff --git a/packages/runtime/src/__tests__/tool-runtime-sandbox-boundary.test.ts b/packages/runtime/src/__tests__/tool-runtime-sandbox-boundary.test.ts
index df190f08c7..e38ca4ebd9 100644
--- a/packages/runtime/src/__tests__/tool-runtime-sandbox-boundary.test.ts
+++ b/packages/runtime/src/__tests__/tool-runtime-sandbox-boundary.test.ts
@@ -1006,6 +1006,7 @@ describe('ToolRuntime session sandbox boundary', () => {
       getPermissionPauseTarget: () => null,
     });
 
+    const events: SessionEvent[] = [];
     const settlement = await runtime.settleToolCall({
       tool: buildRequestSandboxBoundaryTool() as unknown as MakaTool,
       turnId: 'turn-1',
@@ -1016,14 +1017,19 @@ describe('ToolRuntime session sandbox boundary', () => {
       },
       abortSignal: new AbortController().signal,
       eventSink: {
-        push: () => {},
-        pushAndWaitUntilConsumed: async () => {},
+        push: (event) => events.push(event),
+        pushAndWaitUntilConsumed: async (event) => {
+          events.push(event);
+        },
       },
     });
 
-    assert.deepEqual(settlement.modelOutput, {
-      type: 'error-text',
-      value: `Error: ${SANDBOX_BOUNDARY_UNAVAILABLE}`,
+    assert.equal(settlement.providerError, SANDBOX_BOUNDARY_UNAVAILABLE);
+    assert.deepEqual(events.find((event) => event.type === 'tool_result')?.modelProjection, {
+      version: 1,
+      kind: 'text',
+      text: `Error: ${SANDBOX_BOUNDARY_UNAVAILABLE}`,
+      isError: true,
     });
   });
 });
diff --git a/packages/runtime/src/__tests__/tool-runtime-settlement.test.ts b/packages/runtime/src/__tests__/tool-runtime-settlement.test.ts
index 12a34a65c6..8130a76531 100644
--- a/packages/runtime/src/__tests__/tool-runtime-settlement.test.ts
+++ b/packages/runtime/src/__tests__/tool-runtime-settlement.test.ts
@@ -25,6 +25,7 @@ import {
   createGenesisExecutionBoundary,
 } from '@maka/core/sandbox-boundary';
 import { type LlmConnection } from '@maka/core/llm-connections';
+import type { SessionEvent } from '@maka/core/events';
 import { type SessionHeader } from '@maka/core/session';
 import { buildForegroundBashTool, buildManagedBashTool } from '../shell-tools.js';
 import { ToolRuntime, type MakaTool, type ToolRuntimeInput } from '../tool-runtime.js';
@@ -78,8 +79,9 @@ describe('ToolRuntime settlement', () => {
     assert.deepEqual(allowed.result, { ok: true });
   });
 
-  it('keeps the durable Bash command while omitting it from the live model output', async () => {
+  it('keeps the durable Bash command while omitting it from the durable projection', async () => {
     const runtime = makeRuntime();
+    const events: SessionEvent[] = [];
     const bash = buildForegroundBashTool({
       description: 'shell',
       execute: async () => ({
@@ -98,14 +100,17 @@ describe('ToolRuntime settlement', () => {
       input: { command: 'printf durable-command' },
       abortSignal: new AbortController().signal,
       eventSink: {
-        push: () => {},
-        pushAndWaitUntilConsumed: async () => {},
+        push: (event) => events.push(event),
+        pushAndWaitUntilConsumed: async (event) => {
+          events.push(event);
+        },
       },
     });
 
     assert.equal((settlement.result as { cmd?: unknown }).cmd, 'printf durable-command');
-    assert.deepEqual(settlement.modelOutput, {
-      type: 'json',
+    assert.deepEqual(settledProjection(events), {
+      version: 1,
+      kind: 'json',
       value: {
         kind: 'terminal',
         cwd: '/workspace/repo',
@@ -194,6 +199,7 @@ describe('ToolRuntime settlement', () => {
 
     for (const [index, terminal] of terminalResults.entries()) {
       const runtime = makeRuntime();
+      const events: SessionEvent[] = [];
       const bash = buildManagedBashTool({
         runForegroundBash: async () => terminal,
         runBackgroundBash: async () => {
@@ -208,20 +214,27 @@ describe('ToolRuntime settlement', () => {
         input: { command: terminal.cmd, boundary_intent: 'current' },
         abortSignal: new AbortController().signal,
         eventSink: {
-          push: () => {},
-          pushAndWaitUntilConsumed: async () => {},
+          push: (event) => events.push(event),
+          pushAndWaitUntilConsumed: async (event) => {
+            events.push(event);
+          },
         },
       });
       const { cmd: _cmd, ...projected } = terminal;
 
       assert.deepEqual(settlement.result, terminal);
-      assert.deepEqual(settlement.modelOutput, { type: 'json', value: projected });
+      assert.deepEqual(settledProjection(events), {
+        version: 1,
+        kind: 'json',
+        value: projected,
+      });
       assert.equal(terminalResults[index]?.cmd, terminal.cmd);
     }
   });
 
   it('preserves live provider error mapping', async () => {
     const runtime = makeRuntime();
+    const events: SessionEvent[] = [];
     const result = {
       error: 'internal detail',
       text: 'tool text',
@@ -242,19 +255,27 @@ describe('ToolRuntime settlement', () => {
       input: {},
       abortSignal: new AbortController().signal,
       eventSink: {
-        push: () => {},
-        pushAndWaitUntilConsumed: async () => {},
+        push: (event) => events.push(event),
+        pushAndWaitUntilConsumed: async (event) => {
+          events.push(event);
+        },
       },
     });
 
     assert.deepEqual(settlement, {
       result,
-      modelOutput: { type: 'error-text', value: 'Error: safe model detail' },
+      providerError: 'safe model detail',
+    });
+    assert.deepEqual(settledProjection(events), {
+      version: 1,
+      kind: 'text',
+      text: 'Error: safe model detail',
+      isError: true,
     });
   });
 
   it('records apply_patch failures without changing their provider output shape', async () => {
-    const events: Array<{ type: string; isError?: boolean }> = [];
+    const events: SessionEvent[] = [];
     const runtime = makeRuntime();
     const settlement = await runtime.settleToolCall({
       tool: {
@@ -279,8 +300,10 @@ describe('ToolRuntime settlement', () => {
       events.some((event) => event.type === 'tool_result' && event.isError === true),
       true,
     );
-    assert.deepEqual(settlement.modelOutput, {
-      type: 'json',
+    assert.deepEqual(settlement.providerError, 'dispatch failed');
+    assert.deepEqual(settledProjection(events), {
+      version: 1,
+      kind: 'json',
       value: { status: 'failed', output: 'dispatch failed' },
     });
   });
@@ -288,8 +311,8 @@ describe('ToolRuntime settlement', () => {
   it('falls back from provider text to the raw error message', async () => {
     const runtime = makeRuntime();
     for (const [result, expected] of [
-      [{ error: 'internal detail', text: 'tool text' }, 'Error: tool text'],
-      [{ error: 'internal detail' }, 'Error: internal detail'],
+      [{ error: 'internal detail', text: 'tool text' }, 'tool text'],
+      [{ error: 'internal detail' }, 'internal detail'],
     ] as const) {
       const settlement = await runtime.settleToolCall({
         tool: tool(() => result),
@@ -304,13 +327,13 @@ describe('ToolRuntime settlement', () => {
         },
       });
 
-      assert.deepEqual(settlement.modelOutput, { type: 'error-text', value: expected });
+      assert.equal(settlement.providerError, expected);
     }
   });
 
   it('keeps structured durable failures on the live success arm', async () => {
     const runtime = makeRuntime();
-    const events: Array<{ type: string; isError?: boolean }> = [];
+    const events: SessionEvent[] = [];
     const result = {
       kind: 'subagent',
       agentName: 'Reviewer',
@@ -340,39 +363,18 @@ describe('ToolRuntime settlement', () => {
       events.some((event) => event.type === 'tool_result' && event.isError === true),
       true,
     );
-    assert.deepEqual(settlement.modelOutput, { type: 'json', value: result });
-  });
-
-  it('uses the runtime model-output materializer for default tool results', async () => {
-    const result = { kind: 'image', ref: 'artifact-1' };
-    const runtime = makeRuntime({
-      materializeDefaultToolResultOutput: async ({ toolCallId, output }) => {
-        assert.equal(toolCallId, 'call-1');
-        assert.equal(output, result);
-        return { type: 'text', value: 'materialized image' };
-      },
-    });
-
-    const settlement = await runtime.settleToolCall({
-      tool: tool(() => result),
-      turnId: 'turn-1',
-      stepId: 'step-1',
-      toolCallId: 'call-1',
-      input: {},
-      abortSignal: new AbortController().signal,
-      eventSink: {
-        push: () => {},
-        pushAndWaitUntilConsumed: async () => {},
-      },
+    assert.deepEqual(settledProjection(events), {
+      version: 1,
+      kind: 'json',
+      value: result,
     });
-
-    assert.deepEqual(settlement.modelOutput, { type: 'text', value: 'materialized image' });
   });
 
   it('rejects direct-only nested tools before permission argument projection or implementation', async () => {
     let permissionProjectionCalls = 0;
     let implementationCalls = 0;
     const runtime = makeRuntime();
+    const events: SessionEvent[] = [];
 
     const settlement = await runtime.settleToolCall({
       tool: {
@@ -391,8 +393,10 @@ describe('ToolRuntime settlement', () => {
       input: {},
       abortSignal: new AbortController().signal,
       eventSink: {
-        push: () => {},
-        pushAndWaitUntilConsumed: async () => {},
+        push: (event) => events.push(event),
+        pushAndWaitUntilConsumed: async (event) => {
+          events.push(event);
+        },
       },
       origin: 'code_mode',
       parentToolCallId: 'exec-1',
@@ -400,9 +404,12 @@ describe('ToolRuntime settlement', () => {
 
     assert.equal(permissionProjectionCalls, 0);
     assert.equal(implementationCalls, 0);
-    assert.deepEqual(settlement.modelOutput, {
-      type: 'error-text',
-      value: 'Error: Tool Read is direct-only and cannot run inside exec.',
+    assert.equal(settlement.providerError, 'Tool Read is direct-only and cannot run inside exec.');
+    assert.deepEqual(settledProjection(events), {
+      version: 1,
+      kind: 'text',
+      text: 'Error: Tool Read is direct-only and cannot run inside exec.',
+      isError: true,
     });
   });
 
@@ -527,10 +534,7 @@ describe('ToolRuntime settlement', () => {
 
 function makeRuntime(
   overrides: Partial<
-    Pick<
-      ToolRuntimeInput,
-      'materializeDefaultToolResultOutput' | 'readExecutionBoundary' | 'spawnChildSession' | 'runId'
-    >
+    Pick<ToolRuntimeInput, 'readExecutionBoundary' | 'spawnChildSession' | 'runId'>
   > = {},
 ): ToolRuntime {
   return createTestToolRuntime({
@@ -546,6 +550,10 @@ function makeRuntime(
   });
 }
 
+function settledProjection(events: readonly SessionEvent[]) {
+  return events.find((event) => event.type === 'tool_result')?.modelProjection;
+}
+
 function tool(impl: MakaTool['impl']): MakaTool {
   return {
     name: 'Read',
diff --git a/packages/runtime/src/__tests__/tool-runtime-sqlite-boundary.test.ts b/packages/runtime/src/__tests__/tool-runtime-sqlite-boundary.test.ts
index 43a9c952f3..e378e8bd86 100644
--- a/packages/runtime/src/__tests__/tool-runtime-sqlite-boundary.test.ts
+++ b/packages/runtime/src/__tests__/tool-runtime-sqlite-boundary.test.ts
@@ -19,6 +19,7 @@
 
 import { createTestToolRuntime } from './execution-boundary-test-helpers.js';
 import assert from 'node:assert/strict';
+import { Buffer } from 'node:buffer';
 import { mkdtemp, rm } from 'node:fs/promises';
 import { tmpdir } from 'node:os';
 import { join } from 'node:path';
@@ -26,9 +27,12 @@ import { describe, it } from 'node:test';
 import { createGenesisExecutionBoundary } from '@maka/core/sandbox-boundary';
 import { type LlmConnection } from '@maka/core/llm-connections';
 import { type SessionEvent } from '@maka/core/events';
-import { type SessionHeader } from '@maka/core/session';
+import { type SessionHeader, type StoredMessage } from '@maka/core/session';
 import type { McpToolBinding } from '@maka/core/mcp';
-import { createSqliteRuntimeStore } from '@maka/storage/sqlite-runtime-store';
+import {
+  createSqliteRuntimeStore,
+  type SqliteRuntimeStoreFailpoint,
+} from '@maka/storage/sqlite-runtime-store';
 import {
   createSessionEventMapMemory,
   mapSessionEventToRuntimeEvent,
@@ -471,6 +475,16 @@ describe('ToolRuntime with real SQLite boundary', () => {
       );
       assert.equal((await store.readImmutableRuntimeEvents('session-1', 'run-1')).length, 3);
 
+      const response = events.find((event) => event.content?.kind === 'function_response');
+      const durableProjection =
+        response?.content?.kind === 'function_response'
+          ? response.content.modelProjection
+          : undefined;
+      assert.deepEqual(durableProjection, {
+        version: 1,
+        kind: 'json',
+        value: { ok: true, text: 'contents' },
+      });
       const context = invocationContext();
       const memory = createSessionEventMapMemory();
       const durableEvents = published.filter(
@@ -482,10 +496,13 @@ describe('ToolRuntime with real SQLite boundary', () => {
       );
       assert.deepEqual(
         mappedEvents,
-        events.filter(
-          (event) =>
-            event.content?.kind === 'function_call' || event.content?.kind === 'function_response',
-        ),
+        events
+          .filter(
+            (event) =>
+              event.content?.kind === 'function_call' ||
+              event.content?.kind === 'function_response',
+          )
+          .map((event) => JSON.parse(JSON.stringify(event))),
       );
 
       assert.equal((await store.readRuntimeEvents('session-1', 'run-1')).length, 3);
@@ -496,6 +513,113 @@ describe('ToolRuntime with real SQLite boundary', () => {
     }
   });
 
+  it('does not repeat tool or projection side effects after an atomic T2 failure', async () => {
+    const root = await mkdtemp(join(tmpdir(), 'maka-tool-sqlite-t2-retry-'));
+    let runtimeEventInsertions = 0;
+    let failT2 = true;
+    const store = createSqliteRuntimeStore(join(root, 'runtime.sqlite'), {
+      failpoint: (point) => {
+        if (
+          point === ('after_runtime_event_insert' satisfies SqliteRuntimeStoreFailpoint) &&
+          failT2 &&
+          ++runtimeEventInsertions === 2
+        ) {
+          throw new Error(`sqlite runtime failpoint: ${point}`);
+        }
+      },
+    });
+    try {
+      let implementationCalls = 0;
+      let artifactWrites = 0;
+      const appendedMessages: StoredMessage[] = [];
+      const runtime = createTestToolRuntime({
+        sessionId: 'session-1',
+        header: header(),
+        connection: connection(),
+        modelId: 'model-1',
+        appendMessage: async (message) => {
+          appendedMessages.push(message);
+        },
+        newId: nextId(),
+        now: nextNow(),
+        getPermissionPauseTarget: () => null,
+        runId: 'run-1',
+        invocationId: 'invocation-1',
+        runtimeCommitSink: store,
+        prepareDurableProjectionArtifact: () => {
+          return {
+            ref: {
+              kind: 'session_file',
+              sessionId: 'session-1',
+              relativePath: 'projection-artifact',
+            },
+            persist: async () => {
+              artifactWrites += 1;
+            },
+          };
+        },
+      });
+      const imageTool: MakaTool = {
+        name: 'Read',
+        description: 'read',
+        parameters: {},
+        recoveryMode: 'replay_safe',
+        impl: async () => {
+          implementationCalls += 1;
+          return { private: 'completed execution fact' };
+        },
+        toModelOutput: () => ({
+          type: 'content',
+          value: [
+            {
+              type: 'file',
+              data: { type: 'data', data: Buffer.from([137, 80, 78, 71]).toString('base64') },
+              mediaType: 'image/png',
+            },
+          ],
+        }),
+      };
+      const published: SessionEvent[] = [];
+      const settle = () =>
+        runtime.settleToolCall({
+          tool: imageTool,
+          turnId: 'turn-1',
+          toolCallId: 'provider-call-1',
+          input: {},
+          abortSignal: new AbortController().signal,
+          eventSink: {
+            push: (event) => published.push(event),
+            pushAndWaitUntilConsumed: async (event) => {
+              published.push(event);
+            },
+          },
+        });
+
+      await assert.rejects(settle(), /sqlite runtime failpoint: after_runtime_event_insert/u);
+      assert.equal(implementationCalls, 1);
+      assert.equal(artifactWrites, 1);
+      failT2 = false;
+      await assert.rejects(settle(), /duplicate_event_id/u);
+
+      assert.equal(implementationCalls, 1);
+      assert.equal(artifactWrites, 1);
+      assert.equal(published.filter((event) => event.type === 'tool_result').length, 0);
+      assert.equal(published.filter((event) => event.type === 'tool_start').length, 1);
+      assert.equal(appendedMessages.filter((message) => message.type === 'tool_call').length, 1);
+      const events = await store.readRuntimeEvents('session-1', 'run-1');
+      assert.deepEqual(
+        events.map((event) => event.content?.kind),
+        ['function_call', undefined],
+      );
+      const operationId = events[0]?.refs?.operationId;
+      assert.ok(operationId);
+      assert.equal((await store.readToolOperation(operationId))?.currentState, 'prepared');
+    } finally {
+      store.close();
+      await rm(root, { recursive: true, force: true });
+    }
+  });
+
   it('persists the same normalized error event that the Runtime flow later observes', async () => {
     const root = await mkdtemp(join(tmpdir(), 'maka-tool-sqlite-error-'));
     const store = createSqliteRuntimeStore(join(root, 'runtime.sqlite'));
@@ -545,10 +669,13 @@ describe('ToolRuntime with real SQLite boundary', () => {
       const events = await store.readRuntimeEvents('session-1', 'run-1');
       assert.deepEqual(
         mappedEvents,
-        events.filter(
-          (event) =>
-            event.content?.kind === 'function_call' || event.content?.kind === 'function_response',
-        ),
+        events
+          .filter(
+            (event) =>
+              event.content?.kind === 'function_call' ||
+              event.content?.kind === 'function_response',
+          )
+          .map((event) => JSON.parse(JSON.stringify(event))),
       );
       assert.equal(events.length, 3);
       assert.equal(events[2]?.content?.kind, 'function_response');
diff --git a/packages/runtime/src/ai-sdk-backend.ts b/packages/runtime/src/ai-sdk-backend.ts
index 2364129d91..b51269200d 100644
--- a/packages/runtime/src/ai-sdk-backend.ts
+++ b/packages/runtime/src/ai-sdk-backend.ts
@@ -194,6 +194,8 @@ import {
 } from './ai-sdk-compaction.js';
 import type { AiSdkCompactionCapabilities } from './ai-sdk-compaction-contract.js';
 import type { ToolArtifactRecorder } from './tool-artifacts.js';
+import { durableProjectionToToolResultOutput } from './durable-tool-result-projection.js';
+import type { DurableToolResultProjection } from '@maka/core/durable-tool-result-projection';
 import { openAiChatReasoningFieldFromProviderOptions } from './openai-chat-reasoning-transport.js';
 import { RunTrace, type RunTraceRecorder } from './run-trace.js';
 import { SandboxCommandError } from './sandbox/errors.js';
@@ -801,6 +803,8 @@ export interface AiSdkBackendInput extends AiSdkCompactionCapabilities {
    * Caller wires this to the session ArtifactStore; runtime never imports storage.
    */
   readAttachmentBytes?: AttachmentByteReader;
+  /** Host-owned exact ref plan for inline images, persisted only after projection validation. */
+  prepareDurableProjectionArtifact?: ToolRuntimeInput['prepareDurableProjectionArtifact'];
   /**
    * Whether the selected model accepts image input. Only explicit true sends
    * image parts; false/unknown stay as text refs with a fallback note.
@@ -859,6 +863,37 @@ function nativeApplyPatchFailureOutput(output: ToolResultOutput): ToolResultOutp
   };
 }
 
+function durableApplyPatchReplayFactText(
+  input: unknown,
+  projection: DurableToolResultProjection,
+  isError: boolean,
+): string | null {
+  if (projection.kind === 'json') {
+    const fact = applyPatchReplayFactText(input, projection, isError);
+    if (fact) return fact;
+  }
+  const output = durableProjectionToToolResultOutput(projection);
+  switch (output.type) {
+    case 'text':
+    case 'error-text':
+      return output.value;
+    case 'json':
+    case 'error-json':
+      return JSON.stringify(output.value);
+    case 'content': {
+      const text = output.value
+        .filter((part): part is Extract<typeof part, { type: 'text' }> => part.type === 'text')
+        .map((part) => part.text)
+        .join('\n');
+      return text || null;
+    }
+    case 'execution-denied':
+      return output.reason
+        ? `ApplyPatch execution denied: ${output.reason}`
+        : 'ApplyPatch execution denied.';
+  }
+}
+
 /**
  * One Code Mode cell runs at a time on a backend, with one allowed to wait.
  * Widening either needs evidence that concurrent cells are wanted; none exists
@@ -1106,7 +1141,7 @@ export class AiSdkBackend implements AgentBackend {
       createProviderRequestTracker: (trackerInput) =>
         this.createProviderRequestTracker(trackerInput),
       materializeRuntimeReplayPlan: (plan, imageBudget, checkpoint) =>
-        this.materializeRuntimeReplayPlan(plan, imageBudget, undefined, checkpoint),
+        this.materializeRuntimeReplayPlan(plan, imageBudget, checkpoint),
       canReplayProviderNative: (plan) => this.canReplayProviderNative(plan),
     });
     if (
@@ -1323,8 +1358,7 @@ export class AiSdkBackend implements AgentBackend {
       ...(identity.runId ? { runId: identity.runId } : {}),
       orchestrationMode: identity.orchestrationMode,
       ...(identity.invocationId ? { invocationId: identity.invocationId } : {}),
-      materializeDefaultToolResultOutput: ({ toolCallId, output }) =>
-        this.materializeToolResultOutput(identity.scope().imageBudget, output, false, toolCallId),
+      prepareDurableProjectionArtifact: input.prepareDurableProjectionArtifact,
       spawnChildSession: input.spawnChildSession,
       listChildAgents: input.listChildAgents,
       readChildAgentOutput: input.readChildAgentOutput,
@@ -1854,7 +1888,6 @@ export class AiSdkBackend implements AgentBackend {
                   content: currentUserContent,
                 } as ModelMessage,
               ];
-        const settledModelOutputs = new Map<string, ToolResultOutput>();
         const loadDurableTurnEvents = async (): Promise<RuntimeEvent[]> => {
           const loadTurnRuntimeEvents = this.input.loadTurnRuntimeEvents;
           if (!loadTurnRuntimeEvents) {
@@ -1906,7 +1939,6 @@ export class AiSdkBackend implements AgentBackend {
           const currentTurnMessages = await this.materializeRuntimeReplayPlan(
             replayPlan,
             scope.imageBudget,
-            settledModelOutputs,
             projectionCheckpoint,
           );
           return projectionCheckpoint
@@ -2675,13 +2707,6 @@ export class AiSdkBackend implements AgentBackend {
               }
             }
             await queue.waitUntilConsumedThroughCurrent();
-            for (let index = 0; index < returnedToolCalls.length; index += 1) {
-              const toolCall = returnedToolCalls[index];
-              const settlement = settlements[index];
-              if (toolCall && settlement) {
-                settledModelOutputs.set(toolCall.toolCallId, settlement.modelOutput);
-              }
-            }
 
             const continuationWillRun =
               (maxSteps === undefined || runtimeSteps < maxSteps) &&
@@ -3104,7 +3129,7 @@ export class AiSdkBackend implements AgentBackend {
           const tool = snapshot.get(name);
           if (!tool) throw new Error(`Tool "${name}" is not active or nestable in this cell`);
           const parsedInput = await validateCodeModeToolInput(tool, input);
-          const settlement = await scope.toolRuntime.settleToolCallRaw({
+          const settlement = await scope.toolRuntime.settleToolCall({
             tool,
             turnId: context.turnId,
             toolCallId: `${context.toolCallId}:nested:${this.newId()}`,
@@ -3615,7 +3640,6 @@ export class AiSdkBackend implements AgentBackend {
         messages: await this.materializeRuntimeReplayPlan(
           plan,
           scope.imageBudget,
-          undefined,
           projectedHistoryCompactCheckpoint,
         ),
         gate: 'runtime_replay_text_only',
@@ -3639,7 +3663,6 @@ export class AiSdkBackend implements AgentBackend {
             ? await this.materializeRuntimeReplayPlan(
                 degradedPlan,
                 scope.imageBudget,
-                undefined,
                 projectedHistoryCompactCheckpoint,
               )
             : await materializeReplayFallback(),
@@ -3658,7 +3681,6 @@ export class AiSdkBackend implements AgentBackend {
       messages: await this.materializeRuntimeReplayPlan(
         plan,
         scope.imageBudget,
-        undefined,
         projectedHistoryCompactCheckpoint,
       ),
       gate: 'runtime_replay_provider_native',
@@ -3730,7 +3752,6 @@ export class AiSdkBackend implements AgentBackend {
   private async materializeRuntimeReplayPlan(
     plan: RuntimeEventModelReplayPlan,
     budget: ProviderImageBudget,
-    settledModelOutputs?: ReadonlyMap<string, ToolResultOutput>,
     historyCompactCheckpoint?: HistoryCompactCheckpoint,
   ): Promise<ModelMessage[]> {
     type ToolCallItem = Extract<RuntimeEventModelReplayItem, { kind: 'tool_call' }>;
@@ -3854,14 +3875,18 @@ export class AiSdkBackend implements AgentBackend {
       result: ToolResultItem,
       toolName: string,
     ): Promise<ToolResultOutput> => {
-      const output =
-        settledModelOutputs?.get(result.toolCallId) ??
-        (await this.materializeToolResultOutput(
-          budget,
-          result.output,
-          result.isError,
-          `runtime-event:${result.eventId}:tool-result`,
-        ));
+      const output = result.modelProjection
+        ? await this.materializeDurableToolResultProjection(
+            budget,
+            result.modelProjection,
+            `runtime-event:${result.eventId}:tool-result`,
+          )
+        : await this.materializeToolResultOutput(
+            budget,
+            result.output,
+            result.isError,
+            `runtime-event:${result.eventId}:tool-result`,
+          );
       if (toolName !== 'apply_patch') return output;
       return result.isError ? nativeApplyPatchFailureOutput(output) : output;
     };
@@ -4053,11 +4078,13 @@ export class AiSdkBackend implements AgentBackend {
             break;
           }
           downgradedApplyPatchCalls.delete(item.toolCallId);
-          const replayFact = applyPatchReplayFactText(
-            downgradedCall.input,
-            item.output,
-            item.isError,
-          );
+          const replayFact = item.modelProjection
+            ? durableApplyPatchReplayFactText(
+                downgradedCall.input,
+                item.modelProjection,
+                item.isError,
+              )
+            : applyPatchReplayFactText(downgradedCall.input, item.output, item.isError);
           if (!replayFact) break;
           if (downgradedCall.stepId) {
             const stepFacts = replayFactsByStep.get(downgradedCall.stepId) ?? [];
@@ -4413,6 +4440,62 @@ export class AiSdkBackend implements AgentBackend {
     };
   }
 
+  private async materializeDurableToolResultProjection(
+    budget: ProviderImageBudget,
+    projection: DurableToolResultProjection,
+    decisionKey: string,
+  ): Promise<ToolResultOutput> {
+    if (projection.kind !== 'content') return durableProjectionToToolResultOutput(projection);
+    const value: Extract<ToolResultOutput, { type: 'content' }>['value'] = [];
+    for (const [index, part] of projection.parts.entries()) {
+      if (part.kind === 'text') {
+        value.push({ type: 'text', text: part.text });
+        continue;
+      }
+      if (this.input.supportsVision !== true) {
+        value.push({
+          type: 'text',
+          text: 'Image was read, but the selected model does not support image input.',
+        });
+        continue;
+      }
+      if (!this.input.readAttachmentBytes) {
+        value.push({
+          type: 'text',
+          text: 'Image was read, but its stored bytes are unavailable.',
+        });
+        continue;
+      }
+      let read: Awaited<ReturnType<AttachmentByteReader>>;
+      try {
+        read = await this.input.readAttachmentBytes(part.ref);
+      } catch {
+        value.push({
+          type: 'text',
+          text: 'Image could not be loaded from artifact storage: read_failed.',
+        });
+        continue;
+      }
+      if (!read.ok) {
+        value.push({
+          type: 'text',
+          text: `Image could not be loaded from artifact storage: ${read.reason}.`,
+        });
+        continue;
+      }
+      if (!this.chargeImageBudget(budget, read.bytes.length, `${decisionKey}:artifact:${index}`)) {
+        value.push({ type: 'text', text: PROVIDER_IMAGE_BUDGET_EXCEEDED_MESSAGE });
+        continue;
+      }
+      value.push({
+        type: 'file',
+        data: { type: 'data', data: Buffer.from(read.bytes).toString('base64') },
+        mediaType: part.mediaType,
+      });
+    }
+    return { type: 'content', value };
+  }
+
   private async buildCurrentUserContent(
     budget: ProviderImageBudget,
     text: string,
diff --git a/packages/runtime/src/conversation-copy.ts b/packages/runtime/src/conversation-copy.ts
index 6a4054f81c..41d3c72618 100644
--- a/packages/runtime/src/conversation-copy.ts
+++ b/packages/runtime/src/conversation-copy.ts
@@ -58,6 +58,7 @@ import {
   isArchivedToolResultPlaceholder,
   type ArchivedToolResultPlaceholder,
 } from './tool-result-archive.js';
+import { rewriteDurableToolResultProjectionArtifactRefs } from './durable-tool-result-projection.js';
 
 export interface ConversationCopySlice {
   readonly messages: readonly StoredMessage[];
@@ -1161,6 +1162,14 @@ function rewriteRuntimeEventReferences(
         ? {
             ...event.content,
             result: rewriteRuntimeToolResult(event.content.result, references),
+            ...(event.content.modelProjection
+              ? {
+                  modelProjection: rewriteDurableToolResultProjectionArtifactRefs(
+                    event.content.modelProjection,
+                    (ref) => rewriteProjectionArtifactRef(ref, references),
+                  ),
+                }
+              : {}),
           }
         : event.content;
   const refs = event.refs
@@ -1588,6 +1597,17 @@ function rewriteStorageRef(
   };
 }
 
+function rewriteProjectionArtifactRef(
+  ref: Extract<StorageRef, { kind: 'session_context' | 'session_file' }>,
+  references: ConversationCopyArtifactReferenceMap,
+): Extract<StorageRef, { kind: 'session_context' | 'session_file' }> {
+  const rewritten = rewriteStorageRef(ref, references);
+  if (rewritten.kind !== 'session_context' && rewritten.kind !== 'session_file') {
+    throw new Error('Conversation copy produced an invalid projection Artifact reference');
+  }
+  return rewritten;
+}
+
 function rewriteArchivedToolResult(
   value: ArchivedToolResultPlaceholder,
   references: ConversationCopyMessageReferenceMap,
diff --git a/packages/runtime/src/durable-tool-result-projection.ts b/packages/runtime/src/durable-tool-result-projection.ts
new file mode 100644
index 0000000000..f81e4d5f82
--- /dev/null
+++ b/packages/runtime/src/durable-tool-result-projection.ts
@@ -0,0 +1,478 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import {
+  decodeDurableToolResultProjection,
+  DURABLE_TOOL_RESULT_PROJECTION_FAILURE,
+  DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_DEPTH,
+  DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_NODES,
+  DURABLE_TOOL_RESULT_PROJECTION_MAX_PARTS,
+  DURABLE_TOOL_RESULT_PROJECTION_VERSION,
+  type DurableProjectionArtifactRef,
+  type DurableProjectionJson,
+  type DurableToolResultProjection,
+  type DurableToolResultProjectionPart,
+} from '@maka/core/durable-tool-result-projection';
+import { MAX_READ_IMAGE_BYTES } from '@maka/core/attachments';
+import {
+  isCanonicalArtifactEntityId,
+  normalizeArtifactImagePreviewMime,
+} from '@maka/core/artifacts';
+import { isCanonicalStorageRef } from '@maka/core/events';
+import type { ToolResultContent } from '@maka/core/events';
+import type { RuntimeEventFunctionResponseContent } from '@maka/core/runtime-event';
+import { decodeCanonicalShellToolResultContent } from '@maka/core/shell-run-result';
+import { markPersisted } from '@maka/core/persisted-value';
+import { decodePersistedToolResultContent } from '@maka/core/tool-result-record-schema';
+
+import type { ToolResultOutput } from './model-protocol.js';
+import { toolResultOutput } from './tool-result-output.js';
+import { withToolResultArchiveResourceRef } from './tool-result-archive.js';
+import { projectBashToolResultForModel } from './bash-model-output.js';
+import { projectFileWriteToolResultForModel } from './file-tool-model-output.js';
+
+const OMITTED_BINARY_TEXT =
+  '[Binary tool output omitted from the durable model projection; repeat the tool call if it is still needed.]';
+
+/**
+ * The only new-write codec from Runtime's tool-output contract into the
+ * provider-neutral durable projection protocol. It is total by construction:
+ * invalid, unsafe, or oversized output becomes one stable failure sentinel.
+ */
+export function encodeDurableToolResultOutput(
+  output: ToolResultOutput,
+  sessionId: string,
+): DurableToolResultProjection {
+  try {
+    const projection = encodeOutput(output, sessionId);
+    return decodeDurableToolResultProjection(projection);
+  } catch {
+    return DURABLE_TOOL_RESULT_PROJECTION_FAILURE;
+  }
+}
+
+interface DurableProjectionArtifactPlan {
+  ref: Extract<DurableProjectionArtifactRef, { kind: 'session_file' }>;
+  persist(): Promise<void>;
+}
+
+type DurableProjectionArtifactPlanner = (input: {
+  bytes: Uint8Array;
+  mediaType: string;
+}) => DurableProjectionArtifactPlan;
+
+export function encodeDurableToolResultOutputWithArtifacts(
+  output: ToolResultOutput,
+  sessionId: string,
+  planArtifact: DurableProjectionArtifactPlanner | undefined,
+): DurableToolResultProjection | PromiseLike<DurableToolResultProjection> {
+  if (!planArtifact || output.type !== 'content' || !hasInlineImage(output)) {
+    return encodeDurableToolResultOutput(output, sessionId);
+  }
+  return (async () => {
+    try {
+      const prepared = prepareContentProjection(output, sessionId, planArtifact);
+      const persisted = new Set<string>();
+      for (const artifact of prepared.artifacts) {
+        if (persisted.has(artifact.ref.relativePath)) continue;
+        await artifact.persist();
+        persisted.add(artifact.ref.relativePath);
+      }
+      return prepared.projection;
+    } catch {
+      return DURABLE_TOOL_RESULT_PROJECTION_FAILURE;
+    }
+  })();
+}
+
+export function encodeDefaultDurableToolResultOutput(
+  result: unknown,
+  sessionId: string,
+): DurableToolResultProjection {
+  const image = sessionImageResult(result, sessionId);
+  if (image) {
+    try {
+      const mediaType = normalizeArtifactImagePreviewMime(image.mimeType);
+      if (!mediaType) throw new Error('Image has an unsafe media type');
+      return decodeDurableToolResultProjection({
+        version: DURABLE_TOOL_RESULT_PROJECTION_VERSION,
+        kind: 'content',
+        parts: [{ kind: 'artifact', mediaType, ref: image.ref }],
+      });
+    } catch {
+      return DURABLE_TOOL_RESULT_PROJECTION_FAILURE;
+    }
+  }
+  return encodeDurableToolResultOutput(
+    typeof result === 'string'
+      ? { type: 'text', value: result }
+      : { type: 'json', value: result as never },
+    sessionId,
+  );
+}
+
+export function durableProjectionToToolResultOutput(
+  projection: DurableToolResultProjection,
+): ToolResultOutput {
+  switch (projection.kind) {
+    case 'text':
+      return projection.isError
+        ? { type: 'error-text', value: projection.text }
+        : { type: 'text', value: projection.text };
+    case 'json':
+      return projection.isError
+        ? { type: 'error-json', value: projection.value }
+        : { type: 'json', value: projection.value };
+    case 'content':
+      return {
+        type: 'content',
+        value: projection.parts.map((part) =>
+          part.kind === 'text'
+            ? { type: 'text' as const, text: part.text }
+            : {
+                type: 'text' as const,
+                text: `[Artifact ${JSON.stringify(
+                  part.ref.kind === 'session_context' ? part.ref.refId : part.ref.relativePath,
+                )} (${part.mediaType}) is stored in this Session.]`,
+              },
+        ),
+      };
+    case 'execution_denied':
+      return {
+        type: 'execution-denied',
+        ...(projection.reason !== undefined ? { reason: projection.reason } : {}),
+      };
+    case 'failure':
+      return { type: 'error-text', value: projection.message };
+  }
+}
+
+export function rewriteDurableToolResultProjectionArtifactRefs(
+  projection: DurableToolResultProjection,
+  rewrite: (ref: DurableProjectionArtifactRef) => DurableProjectionArtifactRef,
+): DurableToolResultProjection {
+  if (projection.kind !== 'content') return projection;
+  return {
+    ...projection,
+    parts: projection.parts.map((part) =>
+      part.kind === 'artifact' ? { ...part, ref: rewrite(part.ref) } : part,
+    ),
+  };
+}
+
+type EffectiveToolResultProjection =
+  | {
+      kind: 'projection';
+      projection: DurableToolResultProjection;
+      legacyOutput: unknown;
+    }
+  | { kind: 'provider_native'; output: unknown }
+  | { kind: 'legacy_output'; output: unknown }
+  | { kind: 'invalid_legacy'; message: string };
+
+/** The single compatibility boundary for both current and legacy response events. */
+export function decodeEffectiveToolResultProjection(
+  content: RuntimeEventFunctionResponseContent,
+  sessionId: string,
+): EffectiveToolResultProjection {
+  if (content.providerExecuted && content.providerOutput !== undefined) {
+    return { kind: 'provider_native', output: content.providerOutput };
+  }
+  if (content.modelProjection !== undefined) {
+    try {
+      return {
+        kind: 'projection',
+        projection: decodeDurableToolResultProjection(content.modelProjection),
+        legacyOutput: content.result,
+      };
+    } catch {
+      return {
+        kind: 'projection',
+        projection: DURABLE_TOOL_RESULT_PROJECTION_FAILURE,
+        legacyOutput: content.result,
+      };
+    }
+  }
+
+  let output = withToolResultArchiveResourceRef(content.result);
+  if (isRetiredExploreAgentResult(output)) {
+    try {
+      output = decodePersistedToolResultContent(markPersisted<ToolResultContent>(output));
+    } catch {
+      return {
+        kind: 'invalid_legacy',
+        message: 'function_response contains an invalid retired tool result',
+      };
+    }
+  }
+  const shellResult = decodeCanonicalShellToolResultContent(output);
+  if (shellResult.state === 'invalid') {
+    return {
+      kind: 'invalid_legacy',
+      message: 'function_response contains an invalid shell tool result',
+    };
+  }
+  if (shellResult.state === 'valid') output = shellResult.content;
+  output =
+    content.name === 'Bash'
+      ? projectBashToolResultForModel(output)
+      : projectFileWriteToolResultForModel(content.name, output);
+  const projection =
+    content.isError === true
+      ? encodeDurableToolResultOutput(compatibilityErrorOutput(output), sessionId)
+      : encodeDefaultDurableToolResultOutput(output, sessionId);
+  if (projection.kind === 'failure' && isLegacyPathImageResult(output, sessionId)) {
+    return { kind: 'legacy_output', output };
+  }
+  return {
+    kind: 'projection',
+    projection,
+    legacyOutput: output,
+  };
+}
+
+function compatibilityErrorOutput(output: unknown): ToolResultOutput {
+  return output !== null &&
+    typeof output === 'object' &&
+    !Array.isArray(output) &&
+    (output as { kind?: unknown }).kind === 'text' &&
+    typeof (output as { text?: unknown }).text === 'string'
+    ? { type: 'error-text', value: new Error((output as { text: string }).text).toString() }
+    : toolResultOutput(output, true);
+}
+
+export function compatibilityToolResultProjection(
+  content: RuntimeEventFunctionResponseContent,
+  sessionId: string,
+): DurableToolResultProjection | undefined {
+  const effective = decodeEffectiveToolResultProjection(content, sessionId);
+  if (effective.kind === 'provider_native') return undefined;
+  if (effective.kind === 'legacy_output') return DURABLE_TOOL_RESULT_PROJECTION_FAILURE;
+  return effective.kind === 'projection'
+    ? effective.projection
+    : DURABLE_TOOL_RESULT_PROJECTION_FAILURE;
+}
+
+function encodeOutput(output: ToolResultOutput, sessionId: string): DurableToolResultProjection {
+  switch (output.type) {
+    case 'text':
+    case 'error-text':
+      return {
+        version: DURABLE_TOOL_RESULT_PROJECTION_VERSION,
+        kind: 'text',
+        text: output.value,
+        ...(output.type === 'error-text' ? { isError: true as const } : {}),
+      };
+    case 'json':
+    case 'error-json':
+      return {
+        version: DURABLE_TOOL_RESULT_PROJECTION_VERSION,
+        kind: 'json',
+        value: sanitizeJson(output.value),
+        ...(output.type === 'error-json' ? { isError: true as const } : {}),
+      };
+    case 'execution-denied':
+      return {
+        version: DURABLE_TOOL_RESULT_PROJECTION_VERSION,
+        kind: 'execution_denied',
+        ...(output.reason !== undefined ? { reason: output.reason } : {}),
+      };
+    case 'content': {
+      return prepareContentProjection(output, sessionId).projection;
+    }
+  }
+}
+
+function prepareContentProjection(
+  output: Extract<ToolResultOutput, { type: 'content' }>,
+  sessionId: string,
+  planArtifact?: DurableProjectionArtifactPlanner,
+): {
+  projection: DurableToolResultProjection;
+  artifacts: DurableProjectionArtifactPlan[];
+} {
+  if (output.value.length > DURABLE_TOOL_RESULT_PROJECTION_MAX_PARTS) {
+    throw new Error('Tool Result content exceeds the durable part limit');
+  }
+  const parts: DurableToolResultProjectionPart[] = [];
+  const artifacts: DurableProjectionArtifactPlan[] = [];
+  for (const part of output.value) {
+    if (part.type === 'text') {
+      parts.push({ kind: 'text', text: part.text });
+      continue;
+    }
+    if (part.type !== 'file') continue;
+    const ref = readSessionArtifactRef(part, sessionId);
+    if (ref) {
+      const mediaType = normalizeArtifactImagePreviewMime(part.mediaType);
+      if (!mediaType) throw new Error('Artifact has an unsafe media type');
+      parts.push({ kind: 'artifact', mediaType, ref });
+      continue;
+    }
+    if (
+      planArtifact &&
+      part.data.type === 'data' &&
+      part.mediaType.toLowerCase().startsWith('image/')
+    ) {
+      const mediaType = normalizeArtifactImagePreviewMime(part.mediaType);
+      if (!mediaType) throw new Error('Inline image has an unsafe media type');
+      const artifact = planArtifact({
+        bytes: decodeBoundedImageData(part.data.data),
+        mediaType,
+      });
+      artifacts.push(artifact);
+      parts.push({ kind: 'artifact', mediaType, ref: artifact.ref });
+      continue;
+    }
+    parts.push({ kind: 'text', text: OMITTED_BINARY_TEXT });
+  }
+  if (parts.length === 0) parts.push({ kind: 'text', text: 'Tool completed with no content.' });
+  return {
+    projection: decodeDurableToolResultProjection({
+      version: DURABLE_TOOL_RESULT_PROJECTION_VERSION,
+      kind: 'content',
+      parts,
+    }),
+    artifacts,
+  };
+}
+
+function readSessionArtifactRef(
+  part: Extract<ToolResultOutput, { type: 'content' }>['value'][number],
+  sessionId: string,
+) {
+  if (part.type !== 'file') return undefined;
+  const data = part.data as unknown;
+  if (!data || typeof data !== 'object' || Array.isArray(data)) return undefined;
+  const ref = (data as { ref?: unknown }).ref;
+  return isCanonicalStorageRef(ref) &&
+    (ref.kind === 'session_context' || ref.kind === 'session_file') &&
+    ref.sessionId === sessionId
+    ? ref
+    : undefined;
+}
+
+function hasInlineImage(output: Extract<ToolResultOutput, { type: 'content' }>): boolean {
+  return output.value.some(
+    (part) =>
+      part.type === 'file' &&
+      part.data.type === 'data' &&
+      part.mediaType.toLowerCase().startsWith('image/'),
+  );
+}
+
+function decodeBoundedImageData(data: unknown): Uint8Array {
+  let bytes: Uint8Array;
+  if (typeof data === 'string') {
+    if (data.length > Math.ceil((MAX_READ_IMAGE_BYTES * 4) / 3) + 4) {
+      throw new Error('Inline image exceeds the artifact byte limit');
+    }
+    const decoded = Buffer.from(data, 'base64');
+    if (decoded.toString('base64') !== data)
+      throw new Error('Inline image is not canonical base64');
+    bytes = decoded;
+  } else if (data instanceof ArrayBuffer) {
+    if (data.byteLength > MAX_READ_IMAGE_BYTES) {
+      throw new Error('Inline image exceeds the artifact byte limit');
+    }
+    bytes = new Uint8Array(data.slice(0));
+  } else if (ArrayBuffer.isView(data)) {
+    if (data.byteLength > MAX_READ_IMAGE_BYTES) {
+      throw new Error('Inline image exceeds the artifact byte limit');
+    }
+    bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength).slice();
+  } else {
+    throw new Error('Inline image data is not representable');
+  }
+  if (bytes.byteLength > MAX_READ_IMAGE_BYTES) {
+    throw new Error('Inline image exceeds the artifact byte limit');
+  }
+  return bytes;
+}
+
+function sessionImageResult(result: unknown, sessionId: string) {
+  if (!result || typeof result !== 'object' || Array.isArray(result)) return undefined;
+  const image = result as { kind?: unknown; mimeType?: unknown; ref?: unknown };
+  return image.kind === 'image' &&
+    typeof image.mimeType === 'string' &&
+    image.mimeType.length > 0 &&
+    isCanonicalStorageRef(image.ref) &&
+    (image.ref.kind === 'session_context' || image.ref.kind === 'session_file') &&
+    image.ref.sessionId === sessionId
+    ? { mimeType: image.mimeType, ref: image.ref }
+    : undefined;
+}
+
+function isLegacyPathImageResult(result: unknown, sessionId: string): boolean {
+  const image = sessionImageResult(result, sessionId);
+  return image?.ref.kind === 'session_file' && !isCanonicalArtifactEntityId(image.ref.relativePath);
+}
+
+function sanitizeJson(value: unknown): DurableProjectionJson {
+  const state = { nodes: 0 };
+  return sanitizeJsonValue(value, state, 0);
+}
+
+function sanitizeJsonValue(
+  value: unknown,
+  state: { nodes: number },
+  depth: number,
+): DurableProjectionJson {
+  state.nodes += 1;
+  if (
+    state.nodes > DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_NODES ||
+    depth > DURABLE_TOOL_RESULT_PROJECTION_MAX_JSON_DEPTH
+  ) {
+    throw new Error('JSON exceeds limit');
+  }
+  if (value === null) return null;
+  if (typeof value === 'string') return value;
+  if (typeof value === 'boolean') return value;
+  if (typeof value === 'number' && Number.isFinite(value)) return value;
+  if (Array.isArray(value)) {
+    return value.map((item) => sanitizeJsonValue(item, state, depth + 1));
+  }
+  if (!value || typeof value !== 'object') throw new Error('JSON is not representable');
+  const prototype = Object.getPrototypeOf(value);
+  if (prototype !== Object.prototype && prototype !== null) {
+    throw new Error('JSON contains an opaque object');
+  }
+  if (typeof (value as { toJSON?: unknown }).toJSON === 'function') {
+    throw new Error('JSON contains an opaque serializer');
+  }
+  const result: Record<string, DurableProjectionJson> = {};
+  for (const [key, item] of Object.entries(value)) {
+    Object.defineProperty(result, key, {
+      enumerable: true,
+      configurable: true,
+      writable: true,
+      value: sanitizeJsonValue(item, state, depth + 1),
+    });
+  }
+  return result;
+}
+
+function isRetiredExploreAgentResult(value: unknown): boolean {
+  return (
+    value !== null &&
+    typeof value === 'object' &&
+    !Array.isArray(value) &&
+    (value as { kind?: unknown }).kind === 'explore_agent'
+  );
+}
diff --git a/packages/runtime/src/model-history.ts b/packages/runtime/src/model-history.ts
index 53332e4343..1a74cde6c6 100644
--- a/packages/runtime/src/model-history.ts
+++ b/packages/runtime/src/model-history.ts
@@ -61,16 +61,11 @@ import {
   type RuntimeEventContent,
   type RuntimeEventRole,
 } from '@maka/core/runtime-event';
-import { withToolResultArchiveResourceRef } from './tool-result-archive.js';
 import { formatAttachmentResourceRef } from '@maka/core/attachments';
-import { decodeCanonicalShellToolResultContent } from '@maka/core/shell-run-result';
-import { markPersisted } from '@maka/core/persisted-value';
-import { decodePersistedToolResultContent } from '@maka/core/tool-result-record-schema';
-import type { ToolResultContent } from '@maka/core/events';
 import type { AttachmentRef, QuoteRef } from '@maka/core/events';
 import type { ModelMessage, UserContent, UserModelMessage } from './model-protocol.js';
-import { projectBashToolResultForModel } from './bash-model-output.js';
-import { projectFileWriteToolResultForModel } from './file-tool-model-output.js';
+import { decodeEffectiveToolResultProjection } from './durable-tool-result-projection.js';
+import type { DurableToolResultProjection } from '@maka/core/durable-tool-result-projection';
 
 export const PROVIDER_REPLAY_PROJECTION_VERSION = 1;
 
@@ -178,6 +173,7 @@ export type RuntimeEventModelReplayItem =
       toolName: string;
       output: unknown;
       isError: boolean;
+      modelProjection?: DurableToolResultProjection;
       providerExecuted?: boolean;
       eventId: string;
       ts: number;
@@ -610,47 +606,21 @@ export function buildRuntimeEventModelReplayPlan(
           );
           continue;
         }
-        const usesProviderOutput =
-          event.content.providerExecuted && event.content.providerOutput !== undefined;
-        let invalidResultMessage: string | undefined;
-        let normalizedResult: unknown = usesProviderOutput
-          ? event.content.providerOutput
-          : withToolResultArchiveResourceRef(event.content.result);
-        if (!usesProviderOutput && isRetiredExploreAgentResult(normalizedResult)) {
-          try {
-            normalizedResult = decodePersistedToolResultContent(
-              markPersisted<ToolResultContent>(normalizedResult),
-            );
-          } catch {
-            invalidResultMessage = 'function_response contains an invalid retired tool result';
-          }
-        }
-        if (!invalidResultMessage) {
-          const shellResult = decodeCanonicalShellToolResultContent(normalizedResult);
-          if (shellResult.state === 'invalid') {
-            invalidResultMessage = 'function_response contains an invalid shell tool result';
-          } else if (shellResult.state === 'valid') {
-            normalizedResult = shellResult.content;
-          }
-        }
-        if (!invalidResultMessage && event.content.name === 'Bash') {
-          normalizedResult = projectBashToolResultForModel(normalizedResult);
-        } else if (!invalidResultMessage) {
-          normalizedResult = projectFileWriteToolResultForModel(
-            event.content.name,
-            normalizedResult,
-          );
-        }
-        if (invalidResultMessage) {
+        const effective = decodeEffectiveToolResultProjection(event.content, event.sessionId);
+        if (effective.kind === 'invalid_legacy') {
           const call = callsById.get(event.content.id);
           if (call) {
             const callIndex = items.indexOf(call.item);
             if (callIndex >= 0) items.splice(callIndex, 1);
             callsById.delete(event.content.id);
           }
-          diagnostics.push(diagnostic(event, 'unsupported_content', invalidResultMessage));
+          diagnostics.push(diagnostic(event, 'unsupported_content', effective.message));
           continue;
         }
+        const normalizedResult =
+          effective.kind === 'provider_native' || effective.kind === 'legacy_output'
+            ? effective.output
+            : effective.legacyOutput;
         const call = callsById.get(event.content.id);
         if (!call) {
           diagnostics.push(
@@ -683,6 +653,11 @@ export function buildRuntimeEventModelReplayPlan(
           toolCallId: event.content.id,
           toolName: event.content.name,
           output: normalizedResult,
+          ...(effective.kind === 'projection'
+            ? {
+                modelProjection: effective.projection,
+              }
+            : {}),
           isError: event.content.isError === true,
           ...(event.content.providerExecuted !== undefined
             ? { providerExecuted: event.content.providerExecuted }
@@ -749,12 +724,6 @@ export function buildRuntimeEventModelReplayPlan(
   };
 }
 
-function isRetiredExploreAgentResult(value: unknown): boolean {
-  return (
-    typeof value === 'object' && value !== null && 'kind' in value && value.kind === 'explore_agent'
-  );
-}
-
 /**
  * Convert projected RuntimeEvent history into the current AI SDK text-only
  * message shape. Tool/function and thinking entries are intentionally skipped.
diff --git a/packages/runtime/src/recovery-resolver.ts b/packages/runtime/src/recovery-resolver.ts
index 925944c8c0..aac21f7c78 100644
--- a/packages/runtime/src/recovery-resolver.ts
+++ b/packages/runtime/src/recovery-resolver.ts
@@ -30,6 +30,7 @@ import {
 import { interpretScannedToolRecovery } from '@maka/core/tool-recovery-bundle';
 import type { ToolOutcomeCommit } from './runtime-commit-sink.js';
 import type { ToolMode } from '@maka/core/tool-mode';
+import { compatibilityToolResultProjection } from './durable-tool-result-projection.js';
 
 export type ToolRecoveryDecisionStatus =
   | 'completed'
@@ -151,6 +152,22 @@ export function buildInterruptedCodeModeOutcomeCommits(
     ) {
       return [];
     }
+    const result = {
+      kind: 'json' as const,
+      value: {
+        kind: 'code_mode' as const,
+        status: 'interrupted' as const,
+        message: 'Code Mode execution was interrupted by runtime recovery.',
+      },
+    };
+    const responseContent = {
+      kind: 'function_response' as const,
+      id: call.id,
+      name: call.name,
+      result,
+      isError: true as const,
+    };
+    const modelProjection = compatibilityToolResultProjection(responseContent, callEvent.sessionId);
     const runtimeEvent: RuntimeEvent = {
       id: `${decision.operationId}_response`,
       invocationId: callEvent.invocationId,
@@ -163,20 +180,7 @@ export function buildInterruptedCodeModeOutcomeCommits(
       author: 'tool',
       origin: 'provider',
       modelVisibility: 'visible',
-      content: {
-        kind: 'function_response',
-        id: call.id,
-        name: call.name,
-        result: {
-          kind: 'json',
-          value: {
-            kind: 'code_mode',
-            status: 'interrupted',
-            message: 'Code Mode execution was interrupted by runtime recovery.',
-          },
-        },
-        isError: true,
-      },
+      content: { ...responseContent, ...(modelProjection ? { modelProjection } : {}) },
       refs: { operationId: decision.operationId, toolCallId: call.id },
     };
     return [
diff --git a/packages/runtime/src/session-event-runtime-mapper.ts b/packages/runtime/src/session-event-runtime-mapper.ts
index 3044d110f9..63e251c84e 100644
--- a/packages/runtime/src/session-event-runtime-mapper.ts
+++ b/packages/runtime/src/session-event-runtime-mapper.ts
@@ -34,6 +34,7 @@ import {
 import type { RuntimeEvent, RuntimeEventStatus } from '@maka/core/runtime-event';
 
 import type { BackendSessionEvent } from '@maka/core/backend-types';
+import { compatibilityToolResultProjection } from './durable-tool-result-projection.js';
 
 export interface RuntimeEventMapContext {
   readonly sessionId: string;
@@ -332,6 +333,24 @@ function mapBackendSessionEvent(
       };
     case 'tool_result': {
       const name = memory.toolNameByUseId.get(event.toolUseId) ?? '';
+      const content = {
+        kind: 'function_response' as const,
+        id: event.toolUseId,
+        name,
+        result: event.content,
+        ...(event.isError ? { isError: true as const } : {}),
+        ...(event.providerExecuted !== undefined
+          ? { providerExecuted: event.providerExecuted }
+          : {}),
+        ...(event.providerExecuted && event.providerOutput !== undefined
+          ? { providerOutput: structuredClone(event.providerOutput) }
+          : {}),
+      };
+      const modelProjection =
+        event.modelProjection ??
+        (event.operationId === undefined
+          ? compatibilityToolResultProjection(content, ctx.sessionId)
+          : undefined);
       const ev: RuntimeEvent = {
         ...base,
         role: 'tool',
@@ -339,17 +358,8 @@ function mapBackendSessionEvent(
         ...(event.origin !== undefined ? { origin: event.origin } : {}),
         ...(event.modelVisibility !== undefined ? { modelVisibility: event.modelVisibility } : {}),
         content: {
-          kind: 'function_response',
-          id: event.toolUseId,
-          name,
-          result: event.content,
-          ...(event.isError ? { isError: true } : {}),
-          ...(event.providerExecuted !== undefined
-            ? { providerExecuted: event.providerExecuted }
-            : {}),
-          ...(event.providerExecuted && event.providerOutput !== undefined
-            ? { providerOutput: structuredClone(event.providerOutput) }
-            : {}),
+          ...content,
+          ...(modelProjection ? { modelProjection } : {}),
         },
         refs: {
           toolCallId: event.toolUseId,
diff --git a/packages/runtime/src/session-manager.ts b/packages/runtime/src/session-manager.ts
index 96591f9d73..b8497a9df0 100644
--- a/packages/runtime/src/session-manager.ts
+++ b/packages/runtime/src/session-manager.ts
@@ -61,9 +61,7 @@ import type {
 } from '@maka/core/session';
 import type {
   CreateSessionInput,
-  BranchFromTurnInput,
   RegenerateTurnInput,
-  ReviseBeforeTurnInput,
   UserMessageInput,
   SessionListFilter,
 } from '@maka/core/runtime-inputs';
@@ -91,7 +89,6 @@ import {
 } from '@maka/core/plan';
 import { DEFAULT_SESSION_NAME } from '@maka/core/session-name';
 import { DEEP_RESEARCH_SESSION_LABEL, isDeepResearchSession } from '@maka/core/deep-research';
-import { SIDE_CONVERSATION_SESSION_LABEL } from '@maka/core/side-conversation';
 import {
   SUBAGENT_SESSION_RUNTIME_SCHEMA_VERSION,
   SUBAGENT_SESSION_SPAWN_SCHEMA_VERSION,
@@ -144,12 +141,6 @@ import {
   type RuntimeReadModelSessionView,
 } from './runtime-read-model.js';
 import { inspectAgentRunReadModel, type AgentRunInspectModel } from './agent-run-inspect.js';
-import {
-  cloneConversationRuntimeLedger as cloneConversationLedger,
-  createConversationCopySlice,
-  prepareConversationRuntimeLedgerCopy,
-  type ConversationRuntimeLedgerCopyPlan,
-} from './conversation-copy.js';
 import { firstRuntimeRepairRunId, RuntimeLedgerRepair } from './runtime-ledger-repair.js';
 import {
   buildRecoveredTerminalRuntimeEvent,
@@ -3875,13 +3866,6 @@ export class SessionManager {
     };
   }
 
-  async branchFromTurn(sessionId: string, input: BranchFromTurnInput): Promise<SessionSummary> {
-    const sourceView = await this.getSessionView(sessionId);
-    const slice = createConversationCopySlice(sourceView.messages, input.sourceTurnId, 'through');
-    if (!slice) throw new Error(`Cannot branch from unknown turn ${input.sourceTurnId}`);
-    return this.createBranchSession(sessionId, sourceView, [...slice.messages], input);
-  }
-
   /** Canonical, repaired source view for a Host-owned cross-Session copy. */
   async readConversationCopySnapshot(sessionId: string): Promise<RuntimeReadModelSessionView> {
     const readMessagesSnapshot = this.deps.store.readMessagesSnapshot;
@@ -3900,148 +3884,6 @@ export class SessionManager {
     };
   }
 
-  async branchBeforeTurn(sessionId: string, input: BranchFromTurnInput): Promise<SessionSummary> {
-    const sourceView = await this.getSessionView(sessionId);
-    const slice = createConversationCopySlice(sourceView.messages, input.sourceTurnId, 'before');
-    if (!slice) throw new Error(`Cannot branch before unknown turn ${input.sourceTurnId}`);
-    return this.createBranchSession(sessionId, sourceView, [...slice.messages], input);
-  }
-
-  /**
-   * Create a non-destructive edit-and-resend version. Unlike branchBeforeTurn,
-   * this is not a new sidebar conversation: revision lineage lets hosts fold
-   * every version into one conversation slot while keeping old transcripts.
-   */
-  async reviseBeforeTurn(sessionId: string, input: ReviseBeforeTurnInput): Promise<SessionSummary> {
-    const sourceView = await this.getSessionView(sessionId);
-    const slice = createConversationCopySlice(sourceView.messages, input.sourceTurnId, 'before');
-    if (!slice) throw new Error(`Cannot revise before unknown turn ${input.sourceTurnId}`);
-    return this.createRevisionSession(sessionId, sourceView, [...slice.messages], input);
-  }
-
-  private async createRevisionSession(
-    sessionId: string,
-    sourceView: RuntimeReadModelSessionView,
-    copied: StoredMessage[],
-    input: ReviseBeforeTurnInput,
-  ): Promise<SessionSummary> {
-    const plan = await this.prepareConversationRuntimeLedgerClone(sessionId, sourceView, copied);
-    const [header, boundary] = await Promise.all([
-      this.deps.store.readHeader(sessionId),
-      this.deps.store.readExecutionBoundary(sessionId),
-    ]);
-    const revisionRootSessionId = header.revisionRootSessionId ?? sessionId;
-    const family = (await this.deps.store.list()).filter(
-      (candidate) =>
-        candidate.id === revisionRootSessionId ||
-        candidate.revisionRootSessionId === revisionRootSessionId,
-    );
-    const revisionIndex =
-      Math.max(1, ...family.map((candidate) => candidate.revisionIndex ?? 1)) + 1;
-    const next = await this.deps.store.create(
-      {
-        cwd: header.cwd,
-        ...(header.projectId !== undefined ? { projectId: header.projectId } : {}),
-        ...(header.llmConnectionId === undefined
-          ? {}
-          : { llmConnectionId: header.llmConnectionId }),
-        llmConnectionSlug: header.llmConnectionSlug,
-        model: header.model,
-        thinkingLevel: header.thinkingLevel,
-        permissionMode: header.permissionMode,
-        collaborationMode: header.collaborationMode,
-        orchestrationMode: header.orchestrationMode ?? 'default',
-        name: header.name,
-        labels: header.labels,
-        // A revision of a real branch remains in that branch's conversation
-        // slot; revision lineage itself must not create a branch banner.
-        parentSessionId: header.parentSessionId,
-        branchOfTurnId: header.branchOfTurnId,
-        revisionRootSessionId,
-        revisionParentSessionId: sessionId,
-        revisionOfTurnId: input.sourceTurnId,
-        revisionIndex,
-        revisionState: 'preparing',
-        status: 'active',
-      },
-      boundary,
-    );
-    try {
-      const rewritten = await this.cloneConversationRuntimeLedger(next.id, copied, plan);
-      if (rewritten.length > 0) await this.deps.store.appendMessages(next.id, [...rewritten]);
-      await this.deps.store.appendMessage(next.id, {
-        type: 'system_note',
-        id: this.deps.newId(),
-        ts: this.deps.now(),
-        kind: 'session_start',
-        data: {
-          revisionRootSessionId,
-          revisionParentSessionId: sessionId,
-          revisionOfTurnId: input.sourceTurnId,
-          revisionIndex,
-          revisionState: 'preparing',
-        },
-      });
-      await this.deps.store.updateHeader(next.id, {
-        isFlagged: header.isFlagged,
-        titleIsManual: header.titleIsManual,
-      });
-      return headerToSummary(await this.deps.store.readHeader(next.id));
-    } catch (error) {
-      return this.rollbackLegacyConversationCopy(next.id, error);
-    }
-  }
-
-  private async createBranchSession(
-    sessionId: string,
-    sourceView: RuntimeReadModelSessionView,
-    copied: StoredMessage[],
-    input: BranchFromTurnInput,
-  ): Promise<SessionSummary> {
-    const plan = await this.prepareConversationRuntimeLedgerClone(sessionId, sourceView, copied);
-    const [header, boundary] = await Promise.all([
-      this.deps.store.readHeader(sessionId),
-      this.deps.store.readExecutionBoundary(sessionId),
-    ]);
-    const next = await this.deps.store.create(
-      {
-        cwd: header.cwd,
-        ...(header.projectId !== undefined ? { projectId: header.projectId } : {}),
-        ...(header.llmConnectionId === undefined
-          ? {}
-          : { llmConnectionId: header.llmConnectionId }),
-        llmConnectionSlug: header.llmConnectionSlug,
-        model: header.model,
-        thinkingLevel: header.thinkingLevel,
-        permissionMode: header.permissionMode,
-        collaborationMode: header.collaborationMode,
-        orchestrationMode: header.orchestrationMode ?? 'default',
-        name: input.name ?? `${header.name} · 分支`,
-        labels: input.sideConversation
-          ? [...new Set([...header.labels, SIDE_CONVERSATION_SESSION_LABEL])]
-          : header.labels,
-        parentSessionId: sessionId,
-        branchOfTurnId: input.sourceTurnId,
-        status: 'active',
-      },
-      boundary,
-    );
-    try {
-      const rewritten = await this.cloneConversationRuntimeLedger(next.id, copied, plan);
-      if (rewritten.length > 0) await this.deps.store.appendMessages(next.id, [...rewritten]);
-      await this.deps.store.appendMessage(next.id, {
-        type: 'system_note',
-        id: this.deps.newId(),
-        ts: this.deps.now(),
-        kind: 'session_start',
-        data: { parentSessionId: sessionId, branchOfTurnId: input.sourceTurnId },
-      });
-      return headerToSummary(await this.deps.store.readHeader(next.id));
-    } catch (error) {
-      return this.rollbackLegacyConversationCopy(next.id, error);
-    }
-  }
-
   async respondToSandboxBoundary(
     sessionId: string,
     response: SandboxBoundaryResponse,
@@ -4471,54 +4313,6 @@ export class SessionManager {
     this.preparedTranscriptLedgers.add(sessionId);
   }
 
-  private async prepareConversationRuntimeLedgerClone(
-    sourceSessionId: string,
-    sourceView: RuntimeReadModelSessionView,
-    copiedMessages: readonly StoredMessage[],
-  ): Promise<ConversationRuntimeLedgerCopyPlan | undefined> {
-    if (!this.deps.runStore || !this.deps.runtimeEventStore) return undefined;
-    return prepareConversationRuntimeLedgerCopy({
-      sourceSessionId,
-      sourceEvents: sourceView.events,
-      copiedMessages,
-      runStore: this.deps.runStore,
-      runtimeEventStore: this.deps.runtimeEventStore,
-    });
-  }
-
-  private async cloneConversationRuntimeLedger(
-    childSessionId: string,
-    copiedMessages: readonly StoredMessage[],
-    plan: ConversationRuntimeLedgerCopyPlan | undefined,
-  ): Promise<readonly StoredMessage[]> {
-    if (!plan || !this.deps.runStore || !this.deps.runtimeEventStore) return copiedMessages;
-    const copied = await cloneConversationLedger({
-      plan,
-      copiedMessages,
-      referenceMap: {
-        mode: 'preserve_external',
-        sourceSessionId: plan.sourceSessionId,
-        targetSessionId: childSessionId,
-      },
-      runStore: this.deps.runStore,
-      runtimeEventStore: this.deps.runtimeEventStore,
-      newId: this.deps.newId,
-    });
-    return copied.copiedMessages;
-  }
-
-  private async rollbackLegacyConversationCopy(sessionId: string, error: unknown): Promise<never> {
-    try {
-      await this.deps.store.remove(sessionId);
-    } catch (cleanupError) {
-      throw new AggregateError(
-        [error, cleanupError],
-        `Conversation copy ${sessionId} failed and could not be removed`,
-      );
-    }
-    throw error;
-  }
-
   /**
    * Closes only the two provably pre-provider crash windows owned by B2:
    * claim-only and target-Run-created-without-start. A repaired claim never
diff --git a/packages/runtime/src/tool-runtime.ts b/packages/runtime/src/tool-runtime.ts
index a5d612072a..66d2797313 100644
--- a/packages/runtime/src/tool-runtime.ts
+++ b/packages/runtime/src/tool-runtime.ts
@@ -82,8 +82,18 @@ import { stableHash } from './request-shape.js';
 import { classifyError } from './provider-error-classification.js';
 import type { RunTraceLike } from './run-trace.js';
 import { AwaitRegistry } from './await-registry.js';
-import { jsonValue } from './tool-result-output.js';
 import type { ToolResultOutput } from './model-protocol.js';
+import {
+  compatibilityToolResultProjection,
+  encodeDefaultDurableToolResultOutput,
+  encodeDurableToolResultOutput,
+  encodeDurableToolResultOutputWithArtifacts,
+} from './durable-tool-result-projection.js';
+import {
+  DURABLE_TOOL_RESULT_PROJECTION_FAILURE,
+  type DurableProjectionArtifactRef,
+  type DurableToolResultProjection,
+} from '@maka/core/durable-tool-result-projection';
 import {
   buildToolOperationId,
   canonicalToolArgsHash,
@@ -137,11 +147,6 @@ export interface DurableSessionEventSink {
 }
 
 export interface ToolSettlement {
-  result: unknown;
-  modelOutput: ToolResultOutput;
-}
-
-export interface RawToolSettlement {
   result: unknown;
   providerError?: string;
 }
@@ -194,12 +199,12 @@ export interface MakaTool<P = any, R = unknown> {
    * settlement instead of detaching, so late side effects cannot outlive `exec`.
    */
   impl: (args: P, ctx: MakaToolContext) => Promise<R> | R;
-  /** Optional provider-visible content mapping, used for screenshot image parts. */
+  /** Optional synchronous provider-visible content mapping, used for screenshot image parts. */
   toModelOutput?: (options: {
     toolCallId: string;
     input: unknown;
     output: unknown;
-  }) => ToolResultOutput | PromiseLike<ToolResultOutput>;
+  }) => ToolResultOutput;
 }
 
 export interface MakaToolContext {
@@ -349,10 +354,14 @@ export interface ToolRuntimeInput {
   runId?: string;
   orchestrationMode?: OrchestrationMode;
   invocationId?: string;
-  materializeDefaultToolResultOutput?: (options: {
-    toolCallId: string;
-    output: unknown;
-  }) => ToolResultOutput | PromiseLike<ToolResultOutput>;
+  prepareDurableProjectionArtifact?: (input: {
+    turnId: string;
+    bytes: Uint8Array;
+    mediaType: string;
+  }) => {
+    ref: Extract<DurableProjectionArtifactRef, { kind: 'session_file' }>;
+    persist(): Promise<void>;
+  };
   spawnChildSession?: (input: {
     parentRunId: string;
     parentTurnId: string;
@@ -404,6 +413,7 @@ interface RuntimeManagedMutationOperationValue<T> {
     readonly content: ToolResultContent;
     readonly isError: boolean;
     readonly durationMs: number;
+    readonly modelProjection: DurableToolResultProjection;
   };
 }
 
@@ -416,6 +426,7 @@ export interface RuntimeManagedMutationOperationProof {
   readonly content: ToolResultContent;
   readonly isError: boolean;
   readonly durationMs: number;
+  readonly modelProjection: DurableToolResultProjection;
 }
 
 export type RuntimeManagedMutationSettlement =
@@ -446,12 +457,14 @@ interface DurableToolAttempt {
   commitOutcome(
     result: unknown,
     isError: boolean,
+    modelProjection: DurableToolResultProjection,
     durationMs?: number,
   ): Promise<{ id: string; operationId: string; ts: number }>;
   adoptCommittedOutcome(
     event: RuntimeEvent,
     result: ToolResultContent,
     isError: boolean,
+    modelProjection: DurableToolResultProjection,
     durationMs: number,
   ): { id: string; operationId: string; ts: number };
 }
@@ -709,38 +722,6 @@ export class ToolRuntime {
    * provider-facing error output; durable runtime commit failures still reject.
    */
   async settleToolCall(call: ResolvedMakaToolCall): Promise<ToolSettlement> {
-    const settlement = await this.settleToolCallRaw(call);
-    const modelOutput = settlement.providerError
-      ? call.tool.providerTool?.kind === 'openai-apply-patch'
-        ? {
-            type: 'json' as const,
-            value: { status: 'failed' as const, output: settlement.providerError },
-          }
-        : { type: 'error-text' as const, value: new Error(settlement.providerError).toString() }
-      : call.tool.toModelOutput
-        ? await call.tool.toModelOutput({
-            toolCallId: call.toolCallId,
-            input: call.input,
-            output: settlement.result,
-          })
-        : this.input.materializeDefaultToolResultOutput
-          ? await this.input.materializeDefaultToolResultOutput({
-              toolCallId: call.toolCallId,
-              output: settlement.result,
-            })
-          : typeof settlement.result === 'string'
-            ? { type: 'text' as const, value: settlement.result }
-            : { type: 'json' as const, value: jsonValue(settlement.result) };
-    return { result: settlement.result, modelOutput };
-  }
-
-  /**
-   * Settle a tool without producing provider-visible output. Runtime-owned
-   * nested calls use this path because their result is consumed by Code Mode,
-   * not sent as a provider tool-result part; materializing it could spend
-   * turn-scoped provider resources such as the image budget.
-   */
-  async settleToolCallRaw(call: ResolvedMakaToolCall): Promise<RawToolSettlement> {
     const settlement = this.performToolSettlement(call);
     // Tracked so endTurn can wait out unwinds already in flight (#2253):
     // their T2 outcomes must land before the stop path settles the run's
@@ -753,7 +734,7 @@ export class ToolRuntime {
     return settlement;
   }
 
-  private async performToolSettlement(call: ResolvedMakaToolCall): Promise<RawToolSettlement> {
+  private async performToolSettlement(call: ResolvedMakaToolCall): Promise<ToolSettlement> {
     const result = await this.executeTool(
       call.tool,
       call.turnId,
@@ -774,6 +755,55 @@ export class ToolRuntime {
     return { result, ...(providerError ? { providerError } : {}) };
   }
 
+  private projectToolResult(
+    tool: MakaTool,
+    turnId: string,
+    toolCallId: string,
+    input: unknown,
+    result: unknown,
+  ): DurableToolResultProjection | PromiseLike<DurableToolResultProjection> {
+    try {
+      const providerError = providerToolErrorMessage(result);
+      if (providerError) {
+        return encodeDurableToolResultOutput(
+          tool.providerTool?.kind === 'openai-apply-patch'
+            ? {
+                type: 'json' as const,
+                value: { status: 'failed' as const, output: providerError },
+              }
+            : { type: 'error-text' as const, value: new Error(providerError).toString() },
+          this.input.sessionId,
+        );
+      }
+      if (!tool.toModelOutput) {
+        return encodeDefaultDurableToolResultOutput(result, this.input.sessionId);
+      }
+      const output = tool.toModelOutput({ toolCallId, input, output: result });
+      // Projection is deliberately synchronous and total at the tool boundary.
+      // Fail closed for untyped/plugin implementations that violate the
+      // contract so a completed effect can never be stranded before T2.
+      if (isPromiseLike<ToolResultOutput>(output)) {
+        return DURABLE_TOOL_RESULT_PROJECTION_FAILURE;
+      }
+      const encode = (resolved: ToolResultOutput) =>
+        encodeDurableToolResultOutputWithArtifacts(
+          resolved,
+          this.input.sessionId,
+          this.input.prepareDurableProjectionArtifact
+            ? ({ bytes, mediaType }) =>
+                this.input.prepareDurableProjectionArtifact!({
+                  turnId,
+                  bytes,
+                  mediaType,
+                })
+            : undefined,
+        );
+      return encode(output);
+    } catch {
+      return DURABLE_TOOL_RESULT_PROJECTION_FAILURE;
+    }
+  }
+
   /**
    * Install the per-step tool-availability gating used at the execute boundary.
    * The backend recomputes the active snapshot before each step; the guard in
@@ -891,9 +921,10 @@ export class ToolRuntime {
     this.lastFailedToolCallBoundaryDetails = boundaryDetails;
   }
 
-  async writeSyntheticToolResult(
+  private async writeSyntheticToolResult(
     toolUseId: string,
     turnId: string,
+    toolName: string,
     text: string,
     queue: DurableSessionEventSink,
     sandboxDenial?: SandboxDenialSignal,
@@ -924,7 +955,18 @@ export class ToolRuntime {
     // guards, where no attempt exists and no identity is owed.
     const durableAttempt =
       attempt ?? this.durableToolAttempts.get(durableAttemptKey(turnId, toolUseId));
-    const durableOutcome = await durableAttempt?.commitOutcome(content, true);
+    const modelProjection =
+      compatibilityToolResultProjection(
+        {
+          kind: 'function_response',
+          id: toolUseId,
+          name: toolName,
+          result: content,
+          isError: true,
+        },
+        this.input.sessionId,
+      ) ?? DURABLE_TOOL_RESULT_PROJECTION_FAILURE;
+    const durableOutcome = await durableAttempt?.commitOutcome(content, true, modelProjection);
     const msg: ToolResultMessage = {
       type: 'tool_result',
       id: this.input.newId(),
@@ -945,6 +987,7 @@ export class ToolRuntime {
       ...(durableOutcome ? { operationId: durableOutcome.operationId } : {}),
       isError: true,
       content,
+      modelProjection,
       ...activityIdentity,
     } satisfies ToolResultEvent);
   }
@@ -1111,20 +1154,52 @@ export class ToolRuntime {
       ...(tool.displayName ? { displayName: tool.displayName } : {}),
       ...(stepId !== undefined ? { stepId } : {}),
     };
-    let pushedCallEvent: ToolStartEvent | undefined;
-    const pushCallEvent = (lane: 'dispatch' | 'preflight'): ToolStartEvent => {
-      // Idempotent by construction: one call, one call event, whichever lane
-      // asks for it first. A second ask cannot mint a second id.
-      if (pushedCallEvent) return pushedCallEvent;
+    let callEvent: ToolStartEvent | undefined;
+    const buildCallEvent = (lane: 'dispatch' | 'preflight'): ToolStartEvent => {
+      if (callEvent) return callEvent;
       const operationId = lane === 'dispatch' ? dispatchOperationId : undefined;
-      const event: ToolStartEvent = {
+      callEvent = {
         ...callEventFacts,
         id: operationId ? `${operationId}_call` : this.input.newId(),
         ...(operationId ? { operationId } : {}),
       };
+      return callEvent;
+    };
+    let callEventPublished = false;
+    const publishCallEvent = (event: ToolStartEvent): void => {
+      if (callEventPublished) return;
       queue.push(event);
-      pushedCallEvent = event;
-      return event;
+      callEventPublished = true;
+    };
+    const callMsg: ToolCallMessage = {
+      type: 'tool_call',
+      id: toolUseId,
+      turnId,
+      ts: now,
+      toolName: tool.name,
+      ...activityIdentity,
+      ...(tool.activityKind ? { activityKind: tool.activityKind } : {}),
+      ...(tool.displayName ? { displayName: tool.displayName } : {}),
+      args: structuredClone(persistedArgs),
+      ...(ctx.providerOptions !== undefined
+        ? { providerOptions: structuredClone(ctx.providerOptions) }
+        : {}),
+      // Persist the same step id the tool_start event carries so the UI
+      // timeline and post-restart backfill can pair this call with its step.
+      ...(stepId !== undefined ? { stepId } : {}),
+    };
+    let callMessageAppended = false;
+    const appendCallMessage = async (): Promise<void> => {
+      if (callMessageAppended) return;
+      await this.input.appendMessage(callMsg);
+      callMessageAppended = true;
+    };
+    const emitToolStartedTrace = (): void => {
+      trace?.emit('tool', 'tool_started', 'Tool execution started', {
+        toolUseId,
+        toolName: tool.name,
+        ...(tool.categoryHint !== undefined ? { categoryHint: tool.categoryHint } : {}),
+      });
     };
     /**
      * One pre-dispatch refusal: the call fact on the generic lane, then the
@@ -1135,10 +1210,13 @@ export class ToolRuntime {
       text: string,
       sandboxFailure?: Extract<ToolResultContent, { kind: 'text' }>['sandboxFailure'],
     ): Promise<void> => {
-      pushCallEvent('preflight');
+      await appendCallMessage();
+      publishCallEvent(buildCallEvent('preflight'));
+      emitToolStartedTrace();
       await this.writeSyntheticToolResult(
         toolUseId,
         turnId,
+        tool.name,
         text,
         queue,
         undefined,
@@ -1147,29 +1225,6 @@ export class ToolRuntime {
         activityIdentity,
       );
     };
-    const callMsg: ToolCallMessage = {
-      type: 'tool_call',
-      id: toolUseId,
-      turnId,
-      ts: now,
-      toolName: tool.name,
-      ...activityIdentity,
-      ...(tool.activityKind ? { activityKind: tool.activityKind } : {}),
-      ...(tool.displayName ? { displayName: tool.displayName } : {}),
-      args: structuredClone(persistedArgs),
-      ...(ctx.providerOptions !== undefined
-        ? { providerOptions: structuredClone(ctx.providerOptions) }
-        : {}),
-      // Persist the same step id the tool_start event carries so the UI
-      // timeline and post-restart backfill can pair this call with its step.
-      ...(stepId !== undefined ? { stepId } : {}),
-    };
-    await this.input.appendMessage(callMsg);
-    trace?.emit('tool', 'tool_started', 'Tool execution started', {
-      toolUseId,
-      toolName: tool.name,
-      ...(tool.categoryHint !== undefined ? { categoryHint: tool.categoryHint } : {}),
-    });
     if (admissionFailure) {
       const boundaryKind = boundaryAuthorityAttempt ? ('invalid' as const) : undefined;
       if (boundaryKind) {
@@ -1405,7 +1460,7 @@ export class ToolRuntime {
     try {
       durableAttempt = await this.prepareDurableToolAttempt({
         tool,
-        startEvent: pushCallEvent('dispatch'),
+        startEvent: buildCallEvent('dispatch'),
         persistedArgs,
         modelFacingArgs,
         abortSignal: ctx.abortSignal,
@@ -1420,6 +1475,9 @@ export class ToolRuntime {
       await disposeManagedMutationAdmission(managedMutationAdmission);
       throw error;
     }
+    await appendCallMessage();
+    publishCallEvent(buildCallEvent('dispatch'));
+    emitToolStartedTrace();
     if (durableAttempt) {
       this.durableToolAttempts.set(durableAttemptKey(turnId, toolUseId), durableAttempt);
     }
@@ -1468,7 +1526,7 @@ export class ToolRuntime {
             toolCallId: toolUseId,
             // The id the call event actually carries, not the candidate: by here
             // `prepareDurableToolAttempt` has pushed it on the dispatch lane.
-            ...(pushedCallEvent?.operationId ? { operationId: pushedCallEvent.operationId } : {}),
+            ...(callEvent?.operationId ? { operationId: callEvent.operationId } : {}),
             abortSignal: ctx.abortSignal,
             emitOutput: output.emit,
             emitProgress: (current, total) => {
@@ -1549,10 +1607,13 @@ export class ToolRuntime {
           const content = immutableSnapshot
             ? Object.freeze(coerceResultContent(result))
             : coerceResultContent(result);
+          const projected = this.projectToolResult(tool, turnId, toolUseId, executionArgs, result);
+          const modelProjection = isPromiseLike(projected) ? await projected : projected;
           const outcome = {
             content,
             isError: deriveToolResultStatus(content, result) !== 'success',
             durationMs: this.input.now() - startedAt,
+            modelProjection,
           };
           const value = {
             result,
@@ -1593,6 +1654,7 @@ export class ToolRuntime {
                   content: value.outcome.content,
                   isError: value.outcome.isError,
                   durationMs: value.outcome.durationMs,
+                  modelProjection: value.outcome.modelProjection,
                 };
               } finally {
                 if (operationLifecycle.state === 'running') {
@@ -1664,7 +1726,7 @@ export class ToolRuntime {
         }
         const { result, outcome } = settledExecution.value;
         output.flush();
-        const { content, durationMs } = outcome;
+        const { content, durationMs, modelProjection } = outcome;
         // Keep the full provider-facing terminal classification. `isError` is
         // sufficient for the durable response envelope, but it intentionally
         // collapses `aborted` into an error bit and therefore cannot drive live
@@ -1681,12 +1743,14 @@ export class ToolRuntime {
             settledExecution.durableOutcome,
             content,
             outcome.isError,
+            modelProjection,
             durationMs,
           );
         } else {
           durableOutcome = await durableAttempt?.commitOutcome(
             content,
             outcome.isError,
+            modelProjection,
             durationMs,
           );
         }
@@ -1732,6 +1796,7 @@ export class ToolRuntime {
           ...(durableOutcome ? { operationId: durableOutcome.operationId } : {}),
           isError: toolResultStatus !== 'success',
           content,
+          modelProjection,
           durationMs,
           ...activityIdentity,
         } satisfies ToolResultEvent);
@@ -1846,9 +1911,19 @@ export class ToolRuntime {
           );
         }
         const durationMs = Math.max(0, this.input.now() - startedAt);
+        const terminalResult = this.errorReturn(terminalFailure.message);
+        const projected = this.projectToolResult(
+          tool,
+          turnId,
+          toolUseId,
+          executionArgs,
+          terminalResult,
+        );
+        const modelProjection = isPromiseLike(projected) ? await projected : projected;
         const durableOutcome = await durableAttempt?.commitOutcome(
           terminalFailure.content,
           true,
+          modelProjection,
           durationMs,
         );
         const resultMsg: ToolResultMessage = {
@@ -1872,6 +1947,7 @@ export class ToolRuntime {
           ...(durableOutcome ? { operationId: durableOutcome.operationId } : {}),
           isError: true,
           content: terminalFailure.content,
+          modelProjection,
           durationMs,
           ...activityIdentity,
         } satisfies ToolResultEvent);
@@ -1902,7 +1978,7 @@ export class ToolRuntime {
           errorClass,
           ...(sandboxError ? { sandbox: sandboxError } : {}),
         });
-        return this.errorReturn(terminalFailure.message);
+        return terminalResult;
       }
       const msg =
         err instanceof ToolResultLimitError
@@ -1915,6 +1991,7 @@ export class ToolRuntime {
       await this.writeSyntheticToolResult(
         toolUseId,
         turnId,
+        tool.name,
         msg,
         queue,
         sandboxDenialSignalFromError(err),
@@ -2049,6 +2126,7 @@ export class ToolRuntime {
       actions: {
         toolDispatch: {
           protocol: TOOL_BOUNDARY_PROTOCOL_V1,
+          resultProjectionVersion: 1,
           operationId,
           providerToolCallId: input.startEvent.toolUseId,
           toolName: input.tool.name,
@@ -2109,6 +2187,7 @@ export class ToolRuntime {
     const buildResponseEvent = (
       result: unknown,
       isError: boolean,
+      modelProjection: DurableToolResultProjection,
       durationMs: number | undefined,
       ts: number,
     ): RuntimeEvent => ({
@@ -2129,6 +2208,7 @@ export class ToolRuntime {
         name: input.tool.name,
         result,
         ...(isError ? { isError: true } : {}),
+        modelProjection,
       },
       refs: {
         operationId,
@@ -2146,9 +2226,15 @@ export class ToolRuntime {
     return {
       operationId,
       responseEventId: `${operationId}_response`,
-      commitOutcome: async (result, isError, durationMs) => {
+      commitOutcome: async (result, isError, modelProjection, durationMs) => {
         if (committedOutcome) return committedOutcome;
-        const responseEvent = buildResponseEvent(result, isError, durationMs, this.input.now());
+        const responseEvent = buildResponseEvent(
+          result,
+          isError,
+          modelProjection,
+          durationMs,
+          this.input.now(),
+        );
         try {
           await sink.commitToolOutcome({
             operationId,
@@ -2169,9 +2255,9 @@ export class ToolRuntime {
         );
         return committedOutcome;
       },
-      adoptCommittedOutcome: (event, result, isError, durationMs) => {
+      adoptCommittedOutcome: (event, result, isError, modelProjection, durationMs) => {
         if (committedOutcome) return committedOutcome;
-        const expected = buildResponseEvent(result, isError, durationMs, event.ts);
+        const expected = buildResponseEvent(result, isError, modelProjection, durationMs, event.ts);
         if (!Number.isFinite(event.ts) || !isDeepStrictEqual(event, expected)) {
           throw new RuntimeCommitBoundaryError(
             'T2',
@@ -3249,6 +3335,7 @@ function normalizeManagedMutationSettlement(
   const expectedError = kind === 'operation_failed_no_effect_committed';
   if (
     response?.kind !== 'function_response' ||
+    response.modelProjection === undefined ||
     (expectedError ? response.isError !== true : response.isError === true)
   ) {
     throw new Error('Managed no-effect settlement has the wrong durable outcome state');
@@ -3257,6 +3344,7 @@ function normalizeManagedMutationSettlement(
   const outcome = Object.freeze({
     content,
     isError: expectedError,
+    modelProjection: response.modelProjection,
     durationMs:
       typeof durableOutcome.actions?.stateDelta?.durationMs === 'number'
         ? durableOutcome.actions.stateDelta.durationMs
@@ -3709,6 +3797,15 @@ function providerToolErrorMessage(output: unknown): string | undefined {
   return record.error;
 }
 
+function isPromiseLike<T>(value: T | PromiseLike<T>): value is PromiseLike<T> {
+  return (
+    typeof value === 'object' &&
+    value !== null &&
+    'then' in value &&
+    typeof (value as { then?: unknown }).then === 'function'
+  );
+}
+
 function summarizeArgs(toolName: string, args: unknown): string {
   const projected =
     toolName === 'WebSearch'
diff --git a/packages/storage/src/__tests__/artifact-attachments.test.ts b/packages/storage/src/__tests__/artifact-attachments.test.ts
index a84c3366e9..ddc63dd389 100644
--- a/packages/storage/src/__tests__/artifact-attachments.test.ts
+++ b/packages/storage/src/__tests__/artifact-attachments.test.ts
@@ -28,9 +28,13 @@ import { type StorageRef } from '@maka/core/events';
 import {
   createArtifactAttachmentResourceReader,
   createAttachmentByteReader,
+  createReadImageSnapshotPlanner,
   createReadImageSnapshotter,
 } from '../artifact-attachments.js';
-import { createSqliteArtifactStore as createArtifactStore } from '../artifact-store.js';
+import {
+  createSqliteArtifactStoreWriteAuthority,
+  type ArtifactAuthorityStore,
+} from '../artifact-store.js';
 
 const png = Uint8Array.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
 
@@ -220,6 +224,76 @@ describe('artifact attachment authority', () => {
       assert.deepEqual(await store.list('session-1'), []);
     });
   });
+
+  test('snapshotter reuses one content-addressed artifact for the same turn image', async () => {
+    await withStore(async (store) => {
+      const snapshot = createReadImageSnapshotter(store);
+      const input = {
+        sessionId: 'session-1',
+        turnId: 'turn-1',
+        name: 'Tool Result image',
+        bytes: Uint8Array.from([1, 2, 3]),
+        mimeType: 'image/png',
+      };
+
+      const first = await snapshot(input);
+      const repeated = await snapshot(input);
+
+      assert.deepEqual(repeated, first);
+      assert.equal((await store.list('session-1')).length, 1);
+    });
+  });
+
+  test('keeps a durable projection image live when a user requests deletion', async () => {
+    await withStore(async (store) => {
+      const ref = await createReadImageSnapshotter(store)({
+        sessionId: 'session-1',
+        turnId: 'turn-1',
+        name: 'Tool Result image',
+        bytes: png,
+        mimeType: 'image/png',
+      });
+
+      assert.deepEqual(await store.deleteUserArtifactInSession('session-1', ref.relativePath), {
+        kind: 'protected',
+      });
+      assert.deepEqual(await store.readBinary(ref.relativePath), {
+        ok: true,
+        base64: Buffer.from(png).toString('base64'),
+        mimeType: 'image/png',
+      });
+    });
+  });
+
+  test('planner derives the final ref without publishing before commit', async () => {
+    await withStore(async (store) => {
+      const bytes = png.slice();
+      const input = {
+        sessionId: 'session-1',
+        turnId: 'turn-1',
+        name: 'Tool Result image',
+        bytes,
+        mimeType: 'image/png',
+      };
+      const plan = createReadImageSnapshotPlanner(store)(input);
+
+      assert.deepEqual(await store.list('session-1'), []);
+      bytes[0] = 0;
+      input.name = 'mutated after prepare';
+      await Promise.all([plan.persist(), plan.persist()]);
+      const published = await store.list('session-1');
+      assert.deepEqual(
+        published.map((artifact) => artifact.id),
+        [plan.ref.relativePath],
+      );
+      assert.equal(published[0]?.name, 'Tool Result image');
+      assert.deepEqual(await store.readBinary(plan.ref.relativePath), {
+        ok: true,
+        base64: Buffer.from(png).toString('base64'),
+        mimeType: 'image/png',
+      });
+    });
+  });
 });
 
 function sessionFileRef(relativePath: string, sessionId = 'session-1'): StorageRef {
@@ -230,15 +304,15 @@ function sessionContextRef(refId: string, sessionId = 'session-1'): StorageRef {
   return { kind: 'session_context', sessionId, refId };
 }
 
-async function withStore(
-  run: (store: ReturnType<typeof createArtifactStore>) => Promise<void>,
-): Promise<void> {
+async function withStore(run: (store: ArtifactAuthorityStore) => Promise<void>): Promise<void> {
   const root = await mkdtemp(join(tmpdir(), 'maka-artifact-attachment-'));
-  const store = createArtifactStore(root);
+  const authority = createSqliteArtifactStoreWriteAuthority(root);
   try {
+    await authority.recover();
+    const { store } = authority;
     await run(store);
   } finally {
-    store.close?.();
+    authority.close();
     await rm(root, { recursive: true, force: true });
   }
 }
diff --git a/packages/storage/src/__tests__/sqlite-runtime-store.test.ts b/packages/storage/src/__tests__/sqlite-runtime-store.test.ts
index be1d955a10..3fa3060403 100644
--- a/packages/storage/src/__tests__/sqlite-runtime-store.test.ts
+++ b/packages/storage/src/__tests__/sqlite-runtime-store.test.ts
@@ -420,8 +420,16 @@ describe('SqliteRuntimeStore', () => {
 
   it('commits function_response, outcome journal fact, and projection atomically in T2', async () => {
     await withStore(async (store) => {
-      await commitPrepared(store);
-      const outcome = functionResponseEvent();
+      await commitPrepared(store, { resultProjectionVersion: 1 });
+      const outcome = functionResponseEvent({
+        content: {
+          kind: 'function_response',
+          id: 'provider-call-1',
+          name: 'Read',
+          result: 'private execution contents',
+          modelProjection: { version: 1, kind: 'text', text: 'bounded model contents' },
+        },
+      });
 
       const result = await store.commitToolOutcome({
         operationId: 'operation-1',
@@ -434,7 +442,19 @@ describe('SqliteRuntimeStore', () => {
       assert.equal(result.runtimeEventSeq, 3);
       assert.deepEqual(await store.readRuntimeEvents('session-1', 'run-1'), [
         functionCallEvent(),
-        toolDispatchEvent(),
+        toolDispatchEvent({
+          actions: {
+            toolDispatch: {
+              protocol: 't1_after_preflight_v1',
+              operationId: 'operation-1',
+              providerToolCallId: 'provider-call-1',
+              toolName: 'Read',
+              canonicalArgsHash: READ_ARGS_HASH,
+              recoveryMode: 'replay_safe',
+              resultProjectionVersion: 1,
+            },
+          },
+        }),
         outcome,
       ]);
       assert.equal((await store.readImmutableRuntimeEvents('session-1', 'run-1')).length, 3);
@@ -461,6 +481,32 @@ describe('SqliteRuntimeStore', () => {
     });
   });
 
+  it('keeps projected T2 prepared when its atomic model projection is missing', async () => {
+    await withStore(async (store) => {
+      await commitPrepared(store, { resultProjectionVersion: 1 });
+
+      await assert.rejects(
+        store.commitToolOutcome({
+          operationId: 'operation-1',
+          journalEventId: 'operation-1_outcome',
+          runtimeEvent: functionResponseEvent(),
+          committedAt: 20,
+        }),
+        /requires its durable model projection/,
+      );
+
+      assert.deepEqual(
+        (await store.readRuntimeEvents('session-1', 'run-1')).map((event) => event.id),
+        ['call-event-1', 'dispatch-event-1'],
+      );
+      assert.equal((await store.readToolOperation('operation-1'))?.currentState, 'prepared');
+      assert.deepEqual(
+        (await store.readToolJournal('operation-1')).map((event) => event.state),
+        ['prepared'],
+      );
+    });
+  });
+
   it('rolls back T2 without hiding the previously committed prepared boundary', async () => {
     await withStore(async (store, _dbPath, setFailpoint) => {
       await commitPrepared(store);
@@ -2069,12 +2115,26 @@ function toolDispatchEvent(overrides: Partial<RuntimeEvent> = {}): RuntimeEvent
   };
 }
 
-function commitPrepared(store: Store) {
+function commitPrepared(store: Store, options: { resultProjectionVersion?: 1 } = {}) {
   return store.commitToolPrepared({
     operationId: 'operation-1',
     journalEventId: 'operation-1_prepared',
     runtimeEvent: functionCallEvent(),
-    dispatchRuntimeEvent: toolDispatchEvent(),
+    dispatchRuntimeEvent: toolDispatchEvent({
+      actions: {
+        toolDispatch: {
+          protocol: 't1_after_preflight_v1',
+          operationId: 'operation-1',
+          providerToolCallId: 'provider-call-1',
+          toolName: 'Read',
+          canonicalArgsHash: READ_ARGS_HASH,
+          recoveryMode: 'replay_safe',
+          ...(options.resultProjectionVersion !== undefined
+            ? { resultProjectionVersion: options.resultProjectionVersion }
+            : {}),
+        },
+      },
+    }),
     providerToolCallId: 'provider-call-1',
     toolName: 'Read',
     canonicalArgsHash: READ_ARGS_HASH,
diff --git a/packages/storage/src/artifact-attachments.ts b/packages/storage/src/artifact-attachments.ts
index c274c0c628..d281fe8206 100644
--- a/packages/storage/src/artifact-attachments.ts
+++ b/packages/storage/src/artifact-attachments.ts
@@ -23,6 +23,12 @@ import {
   READ_IMAGE_TOO_LARGE_MESSAGE,
   type AttachmentByteReader,
 } from '@maka/core/attachments';
+import {
+  isArtifactTurnKey,
+  isCanonicalArtifactEntityId,
+  normalizeArtifactImagePreviewMime,
+} from '@maka/core/artifacts';
+import { createHash } from 'node:crypto';
 import { type StorageRef, type ToolResultContent } from '@maka/core/events';
 import type { ReadImageSnapshotReader } from '@maka/core/context-offload';
 import type {
@@ -30,6 +36,7 @@ import type {
   ArtifactStore,
   DurableArtifactAttachmentReader,
 } from './artifact-store.js';
+import { sanitizeArtifactName } from './artifact-store.js';
 
 export interface ArtifactAttachmentResourceReader {
   readAttachmentResource(
@@ -119,30 +126,87 @@ export function createAttachmentByteReader(input: {
   };
 }
 
-export function createReadImageSnapshotter(artifactStore: Pick<ArtifactStore, 'create'>) {
-  return async (input: {
-    sessionId: string;
-    turnId: string;
-    name: string;
-    bytes: Uint8Array;
-    mimeType: string;
-  }): Promise<Extract<StorageRef, { kind: 'session_file' }>> => {
+interface ReadImageSnapshotInput {
+  sessionId: string;
+  turnId: string;
+  name: string;
+  bytes: Uint8Array;
+  mimeType: string;
+}
+
+export interface ReadImageSnapshotPlan {
+  ref: Extract<StorageRef, { kind: 'session_file' }>;
+  persist(): Promise<void>;
+}
+
+export function createReadImageSnapshotPlanner(artifactStore: Pick<ArtifactStore, 'create'>) {
+  return (input: ReadImageSnapshotInput): ReadImageSnapshotPlan => {
     if (input.bytes.byteLength > MAX_READ_IMAGE_BYTES) {
       throw new Error(READ_IMAGE_TOO_LARGE_MESSAGE);
     }
-    const artifact = await artifactStore.create({
+    if (normalizeArtifactImagePreviewMime(input.mimeType) !== input.mimeType) {
+      throw new Error('Image media type is not canonical or safe');
+    }
+    if (!isCanonicalArtifactEntityId(input.sessionId)) {
+      throw new Error('Image Session id is not canonical');
+    }
+    if (!isArtifactTurnKey(input.turnId)) {
+      throw new Error('Image turn id is not canonical');
+    }
+    const accepted = Object.freeze({
       sessionId: input.sessionId,
       turnId: input.turnId,
-      name: input.name,
-      kind: 'image',
-      content: input.bytes,
+      name: sanitizeArtifactName(input.name),
+      bytes: input.bytes.slice(),
       mimeType: input.mimeType,
-      source: 'tool_result',
     });
-    return {
-      kind: 'session_file',
-      sessionId: input.sessionId,
-      relativePath: artifact.id,
-    };
+    const id = `image_${createHash('sha256')
+      .update(accepted.sessionId, 'utf8')
+      .update('\0', 'utf8')
+      .update(accepted.turnId, 'utf8')
+      .update('\0', 'utf8')
+      .update(accepted.name, 'utf8')
+      .update('\0', 'utf8')
+      .update(accepted.mimeType, 'utf8')
+      .update('\0', 'utf8')
+      .update(accepted.bytes)
+      .digest('hex')}`;
+    let publication: Promise<void> | undefined;
+    const ref = Object.freeze({
+      kind: 'session_file' as const,
+      sessionId: accepted.sessionId,
+      relativePath: id,
+    });
+    return Object.freeze({
+      ref,
+      persist() {
+        publication ??= artifactStore
+          .create({
+            id,
+            sessionId: accepted.sessionId,
+            turnId: accepted.turnId,
+            name: accepted.name,
+            kind: 'image',
+            content: accepted.bytes,
+            mimeType: accepted.mimeType,
+            source: 'tool_result_projection',
+          })
+          .then((artifact) => {
+            if (artifact.id !== id) throw new Error('Artifact publication changed its planned id');
+          });
+        return publication;
+      },
+    });
+  };
+}
+
+export function createReadImageSnapshotter(artifactStore: Pick<ArtifactStore, 'create'>) {
+  const planSnapshot = createReadImageSnapshotPlanner(artifactStore);
+  return async (
+    input: ReadImageSnapshotInput,
+  ): Promise<Extract<StorageRef, { kind: 'session_file' }>> => {
+    const plan = planSnapshot(input);
+    await plan.persist();
+    return plan.ref;
   };
 }
diff --git a/packages/storage/src/artifact-stores.ts b/packages/storage/src/artifact-stores.ts
index 99a5e1f232..9fbfa807da 100644
--- a/packages/storage/src/artifact-stores.ts
+++ b/packages/storage/src/artifact-stores.ts
@@ -41,8 +41,10 @@ import {
 export {
   createArtifactAttachmentResourceReader,
   createAttachmentByteReader,
+  createReadImageSnapshotPlanner,
   createReadImageSnapshotter,
   type ArtifactAttachmentResourceReader,
+  type ReadImageSnapshotPlan,
 } from './artifact-attachments.js';
 export { persistProviderRequestCaptureArtifact } from './provider-request-capture-artifact.js';
 
diff --git a/packages/storage/src/sqlite-runtime-store.ts b/packages/storage/src/sqlite-runtime-store.ts
index c060f26833..c2c459e46c 100644
--- a/packages/storage/src/sqlite-runtime-store.ts
+++ b/packages/storage/src/sqlite-runtime-store.ts
@@ -2799,6 +2799,13 @@ export class SqliteRuntimeStore
     const dispatchEvent = dispatchJson
       ? decodeRuntimeEvent(JSON.parse(dispatchJson) as unknown)
       : undefined;
+    if (
+      dispatchEvent?.actions?.toolDispatch?.resultProjectionVersion === 1 &&
+      input.runtimeEvent.content?.kind === 'function_response' &&
+      input.runtimeEvent.content.modelProjection === undefined
+    ) {
+      throw new Error('Projected Tool Result T2 requires its durable model projection');
+    }
     if (dispatchEvent?.actions?.toolDispatch?.managedMutation) {
       const reservation = this.db
         .prepare(`


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org