ソースを参照

test(ci): exercise Messages defaults in runtime smokes

Tianyi Cui 1 週間 前
コミット
ece35a1a38

+ 2 - 2
.github/workflows/build-exe-for-python-sdk.yml

@@ -410,7 +410,7 @@ jobs:
                    || github.event.pull_request.user.login == 'dependabot[bot]'))
         env:
           DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY_EXTERNAL }}
-          DEEPSEEK_BASE_URL: https://api.deepseek.com
+          DEEPSEEK_BASE_URL: https://api.deepseek.com/anthropic
         run: |
           set -euo pipefail
           blackbox_root="$(python -c 'import tempfile; print(tempfile.mkdtemp(prefix="dsh-sdk-blackbox-live-"))')"
@@ -431,7 +431,7 @@ jobs:
         shell: pwsh
         env:
           DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY_EXTERNAL }}
-          DEEPSEEK_BASE_URL: https://api.deepseek.com
+          DEEPSEEK_BASE_URL: https://api.deepseek.com/anthropic
         run: |
           $blackboxRoot = (& python -c 'import tempfile; print(tempfile.mkdtemp(prefix="dsh-sdk-blackbox-live-"))').Trim()
           Remove-Item Env:PYTHONPATH -ErrorAction SilentlyContinue

+ 2 - 2
.github/workflows/e2e.yml

@@ -3,7 +3,7 @@ name: E2E (real DeepSeek API)
 # Real-API end-to-end suite (`pnpm run test:e2e`). Unlike ci.yml this job
 # consumes the DEEPSEEK_API_KEY_EXTERNAL secret (mapped to the env var
 # DEEPSEEK_API_KEY the tests read) and hits the external API at
-# https://api.deepseek.com (DEEPSEEK_BASE_URL is pinned to it explicitly so a
+# https://api.deepseek.com/anthropic (DEEPSEEK_BASE_URL is pinned explicitly so a
 # stray repo-root .env can't redirect the run).
 #
 # pull_request IS a trigger, but GitHub withholds repo secrets from BOTH forked
@@ -117,7 +117,7 @@ jobs:
       - name: E2E tests (real DeepSeek API)
         env:
           DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY_EXTERNAL }}
-          DEEPSEEK_BASE_URL: https://api.deepseek.com
+          DEEPSEEK_BASE_URL: https://api.deepseek.com/anthropic
           DSH_E2E_MAX_WORKERS: 4
           DSH_EXAMPLE_MODE: lib
         run: pnpm run test:e2e

+ 1 - 1
apps/web/tests/expected/onboarding-deepseek-config/default-models.expected.md

@@ -33,7 +33,7 @@
       - group:
         - text: 自定义设置 API 地址
         - textbox "API 地址":
-          - /placeholder: https://api.deepseek.com
+          - /placeholder: https://api.deepseek.com/anthropic
         - text: 请填写与当前连接配置兼容的 API 地址。
         - region "模型目录":
           - text: 模型目录 正在使用适配器默认模型

+ 1 - 1
apps/web/tests/expected/onboarding-deepseek-config/models.expected.md

@@ -33,7 +33,7 @@
       - group:
         - text: 自定义设置 API 地址
         - textbox "API 地址":
-          - /placeholder: https://api.deepseek.com
+          - /placeholder: https://api.deepseek.com/anthropic
         - text: 请填写与当前连接配置兼容的 API 地址。
         - region "模型目录":
           - text: 模型目录 已自定义模型目录

+ 5 - 4
packages/llm/llm-deepseek/tests/adapter.e2e.ts

@@ -111,6 +111,7 @@ async function harness(model: string, config: Partial<Config> = {}) {
   await ctx.plugin(E2eAttachmentStore)
   await ctx.plugin(LlmDeepSeek, {
     protocol: 'chat-completions',
+    baseURL: LlmDeepSeek.PUBLIC_BASE_URL,
     ...model === VISION ? { models: [{ id: VISION, inputModalities: ['text', 'image'] }] } : {},
     ...config,
   })
@@ -154,7 +155,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('llm-deepseek e2e (real API)', ()
     contexts.push(ctx)
     await ctx.plugin(LlmRuntime)
     await ctx.plugin(LocalAttachments)
-    await ctx.plugin(LlmDeepSeek, { protocol: 'chat-completions', maxTokens: 4096 })
+    await ctx.plugin(LlmDeepSeek, { protocol: 'chat-completions', baseURL: LlmDeepSeek.PUBLIC_BASE_URL, maxTokens: 4096 })
     const model = 'deepseek-flash'
     await expect(ctx.llm.resolveModelInfo('deepseek-official', model)).resolves.toMatchObject({
       inputModalities: ['text', 'image'], systemPromptUpdate: 'in-history',
@@ -181,7 +182,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('llm-deepseek e2e (real API)', ()
   it.skipIf(!VISION_E2E_ENABLED)('uses the built-in official route to upload, reference, and delete one image', async () => {
     const key = process.env.DEEPSEEK_API_KEY
     if (key === undefined) throw new Error('e2e ran without DEEPSEEK_API_KEY')
-    const baseURL = process.env.DEEPSEEK_BASE_URL ?? LlmDeepSeek.PUBLIC_BASE_URL
+    const baseURL = LlmDeepSeek.PUBLIC_BASE_URL
     const ctx = await harness(VISION, { baseURL })
     await ctx.plugin(E2eAttachmentStore)
     const attachments = ctx.attachments as E2eAttachmentStore
@@ -233,7 +234,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('llm-deepseek e2e (real API)', ()
     await ctx.plugin(DeepSeekLlmApiExtensionRegistry)
     await ctx.plugin(SessionLogDeepSeek, { enabled: true })
     await ctx.plugin(PluginPackageInventoryDeepSeek)
-    await ctx.plugin(LlmDeepSeek, { protocol: 'chat-completions', thinking: 'disabled' })
+    await ctx.plugin(LlmDeepSeek, { protocol: 'chat-completions', baseURL: LlmDeepSeek.PUBLIC_BASE_URL, thinking: 'disabled' })
     const session = ctx.sessions.create(SessionId('real-extension-fields'))
     session.append('turn/start', { turn: 1 })
 
@@ -263,7 +264,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('llm-deepseek e2e (real API)', ()
       contexts.push(ctx)
       await ctx.plugin(LlmRuntime)
       await ctx.plugin(LocalCredentialProvider, { path: join(dir, '.credentials.yaml'), watch: false })
-      await ctx.plugin(LlmDeepSeek, { protocol: 'chat-completions' })
+      await ctx.plugin(LlmDeepSeek, { protocol: 'chat-completions', baseURL: LlmDeepSeek.PUBLIC_BASE_URL })
 
       const result = await assemble(ctx, {
         model: FLASH,

+ 1 - 1
packages/llm/llm-pi-ai/tests/adapter.e2e.ts

@@ -24,7 +24,7 @@ async function harness(_model: string, config: Partial<PiAiProviderProfile> = {}
     providers: {
       deepseek: {
         ...process.env.DEEPSEEK_API_KEY === undefined ? {} : { apiKey: process.env.DEEPSEEK_API_KEY },
-        ...process.env.DEEPSEEK_BASE_URL === undefined ? {} : { baseURL: process.env.DEEPSEEK_BASE_URL },
+        baseURL: LlmDeepSeek.PUBLIC_BASE_URL,
         ...config,
       },
     },

+ 39 - 21
python/sdk/tests/test_smoke_model.py

@@ -3,6 +3,7 @@ from __future__ import annotations
 import json
 import runpy
 import subprocess
+import urllib.request
 from pathlib import Path
 from types import SimpleNamespace
 
@@ -170,59 +171,76 @@ def test_live_smoke_rejects_unrelated_tool_without_exact_receipt(
 def test_child_prompt_precedes_runtime_context(prompt_name: str, expected: str) -> None:
     chunks = SMOKE["completion_chunks"]({
         "messages": [
-            {"role": "user", "content": SMOKE[prompt_name]},
-            {"role": "user", "content": "Current runtime context"},
+            {"role": "user", "content": [
+                {"type": "text", "text": SMOKE[prompt_name]},
+                {"type": "text", "text": "Current runtime context"},
+            ]},
         ],
     })
 
     assert any(
-        choice.get("delta", {}).get("content") == expected
+        chunk.get("delta", {}).get("text") == expected
         for chunk in chunks
-        for choice in chunk.get("choices", [])
     )
 
 
 def test_mcp_smoke_requests_the_discovered_tool() -> None:
     chunks = SMOKE["completion_chunks"]({
-        "messages": [{"role": "user", "content": SMOKE["MCP_PROMPT"]}],
-        "tools": [{"type": "function", "function": {"name": "mcp__fixture__add"}}],
+        "messages": [{"role": "user", "content": [{"type": "text", "text": SMOKE["MCP_PROMPT"]}]}],
+        "tools": [{"name": "mcp__fixture__add", "input_schema": {"type": "object"}}],
     })
 
     calls = [
-        call
+        chunk["content_block"]
         for chunk in chunks
-        for choice in chunk.get("choices", [])
-        for call in choice.get("delta", {}).get("tool_calls", [])
+        if chunk.get("type") == "content_block_start"
     ]
-    assert calls[0]["function"] == {
-        "name": "mcp__fixture__add",
-        "arguments": '{"a": 19, "b": 23}',
-    }
+    assert calls == [{"type": "tool_use", "id": "mcp-add", "name": "mcp__fixture__add", "input": {}}]
+    arguments = next(chunk["delta"]["partial_json"] for chunk in chunks if chunk.get("type") == "content_block_delta")
+    assert json.loads(arguments) == {"a": 19, "b": 23}
 
 
 def test_mcp_smoke_accepts_the_external_server_result() -> None:
     chunks = SMOKE["completion_chunks"]({
         "messages": [
-            {"role": "user", "content": SMOKE["MCP_PROMPT"]},
+            {"role": "user", "content": [{"type": "text", "text": SMOKE["MCP_PROMPT"]}]},
             {
                 "role": "assistant",
-                "tool_calls": [{
-                    "id": "mcp-add",
-                    "type": "function",
-                    "function": {"name": "mcp__fixture__add", "arguments": '{}'},
+                "content": [{
+                    "type": "tool_use", "id": "mcp-add", "name": "mcp__fixture__add", "input": {},
                 }],
             },
-            {"role": "tool", "tool_call_id": "mcp-add", "content": "42"},
+            {"role": "user", "content": [
+                {"type": "tool_result", "tool_use_id": "mcp-add", "content": [{"type": "text", "text": "42"}]},
+            ]},
         ],
     })
 
     assert any(
-        choice.get("delta", {}).get("content") == SMOKE["MCP_TEXT"]
+        chunk.get("delta", {}).get("text") == SMOKE["MCP_TEXT"]
         for chunk in chunks
-        for choice in chunk.get("choices", [])
     )
 
 
+def test_mock_model_serves_native_messages_events() -> None:
+    with SMOKE["MockModel"]() as model:
+        body = {"model": "smoke-model", "stream": True, "messages": [{
+            "role": "user", "content": [{"type": "text", "text": "hello"}],
+        }]}
+        request = urllib.request.Request(
+            model.url + "/v1/messages", data=json.dumps(body).encode(),
+            headers={"content-type": "application/json"},
+        )
+        with urllib.request.urlopen(request) as response:
+            stream = response.read().decode()
+        frames = [frame.splitlines() for frame in stream.strip().split("\n\n")]
+        events = [json.loads(frame[1].removeprefix("data: ")) for frame in frames]
+        assert all(frame[0] == f"event: {event['type']}" for frame, event in zip(frames, events))
+        assert events[0]["type"] == "message_start"
+        assert events[-1] == {"type": "message_stop"}
+        assert next(event["delta"]["text"] for event in events if event["type"] == "content_block_delta") == SMOKE["EXPECTED_TEXT"]
+
+
 def test_advanced_snapshot_normalizes_catalog_child_creation_time() -> None:
     value = {
         "type": "subagent/catalog",

+ 73 - 47
scripts/smoke-python-runtime.py

@@ -307,6 +307,9 @@ class MockModelHandler(BaseHTTPRequestHandler):
     requests: list[dict[str, object]] = []
 
     def do_POST(self) -> None:
+        if self.path != "/v1/messages":
+            self.send_error(404)
+            return
         content_length = int(self.headers.get("content-length", "0"))
         body = json.loads(self.rfile.read(content_length))
         self.requests.append(body)
@@ -315,8 +318,7 @@ class MockModelHandler(BaseHTTPRequestHandler):
         self.end_headers()
         chunks = completion_chunks(body)
         for chunk in chunks:
-            self.wfile.write(f"data: {json.dumps(chunk)}\n\n".encode())
-        self.wfile.write(b"data: [DONE]\n\n")
+            self.wfile.write(f"event: {chunk['type']}\ndata: {json.dumps(chunk)}\n\n".encode())
         self.wfile.flush()
 
     def log_message(self, _format: str, *_args: object) -> None:
@@ -333,9 +335,14 @@ def completion_chunks(body: dict[str, object]) -> list[dict[str, object]]:
     if not isinstance(latest, dict):
         raise AssertionError(f"model request has an invalid latest message: {body}")
 
-    if latest.get("role") == "tool":
-        call_id, tool_name = latest_tool_call(messages)
-        tool_text = message_text(latest.get("content"))
+    tool_results = [
+        block for block in latest.get("content", [])
+        if isinstance(block, dict) and block.get("type") == "tool_result"
+    ]
+    if tool_results:
+        result = tool_results[-1]
+        call_id, tool_name = latest_tool_call(messages, result.get("tool_use_id"))
+        tool_text = message_text(result.get("content"))
         mcp = mcp_tool_followup(call_id, tool_name, tool_text)
         if mcp is not None:
             return mcp
@@ -360,9 +367,11 @@ def completion_chunks(body: dict[str, object]) -> list[dict[str, object]]:
         raise AssertionError(f"unexpected tool follow-up: {tool_name}")
 
     user_prompts = [
-        message_text(message.get("content"))
+        block["text"]
         for message in reversed(messages)
         if isinstance(message, dict) and message.get("role") == "user"
+        for block in message.get("content", [])
+        if isinstance(block, dict) and block.get("type") == "text"
     ]
     minimal_prompt = next((prompt for prompt in user_prompts if prompt == MINIMAL_PROMPT), None)
     # The minimal composition's assembled system prompt, advertised tool schemas, and
@@ -459,7 +468,7 @@ def completion_chunks(body: dict[str, object]) -> list[dict[str, object]]:
             {"a": 19, "b": 23},
         )
     if prompt == PROFILE_PLUGIN_PROMPT:
-        system_text = "\n".join(
+        system_text = message_text(body.get("system")) + "\n" + "\n".join(
             message_text(message.get("content"))
             for message in messages
             if isinstance(message, dict) and message.get("role") == "system"
@@ -654,64 +663,67 @@ def advanced_tool_followup(
 
 
 def text_chunks(text: str) -> list[dict[str, object]]:
-    """Build a complete streaming text response."""
+    """Build a complete Messages text response."""
     return [
-        {"choices": [{"delta": {"role": "assistant", "content": None, "reasoning_content": ""}}]},
-        {"choices": [{"delta": {"content": text}}]},
-        {
-            "choices": [{"delta": {"content": ""}, "finish_reason": "stop"}],
-            "usage": {"prompt_tokens": 3, "completion_tokens": 3},
-        },
+        message_start(),
+        {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}},
+        {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": text}},
+        {"type": "content_block_stop", "index": 0},
+        {"type": "message_delta", "delta": {"stop_reason": "end_turn"}, "usage": {"output_tokens": 3}},
+        {"type": "message_stop"},
     ]
 
 
 def tool_call_chunks(call_id: str, name: str, arguments: dict[str, object]) -> list[dict[str, object]]:
-    """Build a complete streaming function-call response."""
+    """Build a complete Messages tool-use response."""
     return [
-        {"choices": [{"delta": {"role": "assistant", "content": None, "reasoning_content": ""}}]},
+        message_start(),
         {
-            "choices": [{
-                "delta": {
-                    "tool_calls": [{
-                        "index": 0,
-                        "id": call_id,
-                        "type": "function",
-                        "function": {"name": name, "arguments": json.dumps(arguments)},
-                    }],
-                },
-            }],
+            "type": "content_block_start", "index": 0,
+            "content_block": {"type": "tool_use", "id": call_id, "name": name, "input": {}},
         },
         {
-            "choices": [{"delta": {"content": ""}, "finish_reason": "tool_calls"}],
-            "usage": {"prompt_tokens": 3, "completion_tokens": 3},
+            "type": "content_block_delta", "index": 0,
+            "delta": {"type": "input_json_delta", "partial_json": json.dumps(arguments)},
         },
+        {"type": "content_block_stop", "index": 0},
+        {"type": "message_delta", "delta": {"stop_reason": "tool_use"}, "usage": {"output_tokens": 3}},
+        {"type": "message_stop"},
     ]
 
 
-def latest_tool_call(messages: list[object]) -> tuple[str, str]:
+def message_start() -> dict[str, object]:
+    """Start a Messages response with deterministic token usage."""
+    return {
+        "type": "message_start",
+        "message": {"id": "msg_smoke", "model": "smoke-model", "usage": {"input_tokens": 3, "output_tokens": 0}},
+    }
+
+
+def latest_tool_call(messages: list[object], result_id: object) -> tuple[str, str]:
     """Find the assistant call id and name paired with the latest tool result."""
     for message in reversed(messages[:-1]):
         if not isinstance(message, dict):
             continue
-        calls = message.get("tool_calls")
+        calls = message.get("content")
         if not isinstance(calls, list):
             continue
         for call in reversed(calls):
             if not isinstance(call, dict):
                 continue
-            function = call.get("function")
             call_id = call.get("id")
             if (
                 isinstance(call_id, str)
-                and isinstance(function, dict)
-                and isinstance(function.get("name"), str)
+                and call_id == result_id
+                and call.get("type") == "tool_use"
+                and isinstance(call.get("name"), str)
             ):
-                return call_id, function["name"]
+                return call_id, call["name"]
     raise AssertionError(f"tool result has no preceding assistant tool call: {messages}")
 
 
 def message_text(content: object) -> str:
-    """Read OpenAI text content in either string or block-list form."""
+    """Read Messages text content in either string or block-list form."""
     if isinstance(content, str):
         return content
     if isinstance(content, list):
@@ -732,9 +744,8 @@ def advertised_tool_names(body: dict[str, object]) -> set[str]:
     for tool in tools:
         if not isinstance(tool, dict):
             continue
-        function = tool.get("function")
-        if isinstance(function, dict) and isinstance(function.get("name"), str):
-            names.add(function["name"])
+        if isinstance(tool.get("name"), str):
+            names.add(tool["name"])
     return names
 
 
@@ -1878,13 +1889,18 @@ def build_in_history_snapshot_files(
     request_prompts = []
     for index, request in enumerate(requests):
         messages = request["messages"]
-        assert messages[0]["role"] == "system" and message_text(messages[0]["content"]) == prompts[0]
+        assert message_text(request.get("system")) == prompts[0]
         assert request["tools"] == requests[0]["tools"], "prompt update changed tool schemas"
         positions = [position for position, message in enumerate(messages) if message["role"] == "system"]
-        texts = [message_text(messages[position]["content"]) for position in positions]
+        texts = [message_text(request["system"]), *[
+            message_text(messages[position]["content"]) for position in positions
+        ]]
         assert texts == (prompts[:1] if index == 0 else prompts), texts
         if index > 0:
-            assert messages[positions[1] - 1]["role"] == "tool", messages
+            previous = messages[positions[0] - 1]
+            assert previous["role"] == "user" and any(
+                block.get("type") == "tool_result" for block in previous["content"]
+            ), messages
         request_prompts.append(texts)
     evidence = {
         "requestSystemPrompts": request_prompts,
@@ -1914,6 +1930,7 @@ def build_minimal_snapshot_files(
         if not isinstance(messages, list):
             raise AssertionError(f"minimal model request has no messages: {body}")
         snapshot.append({
+            "system": minimal_snapshot_text(body.get("system"), cwd),
             "tools": minimal_snapshot_text(body.get("tools"), cwd),
             "messages": [
                 minimal_snapshot_message(message, cwd)
@@ -1928,22 +1945,30 @@ def minimal_snapshot_message(message: object, cwd: Path) -> dict[str, object]:
     if not isinstance(message, dict):
         raise AssertionError(f"minimal model request has an invalid message: {message}")
     role = message.get("role")
-    if role in ("system", "user"):
+    if role == "system":
         return {"role": role, "text": minimal_snapshot_text(message_text(message.get("content")), cwd)}
+    if role == "user":
+        content = []
+        for block in message.get("content", []):
+            if block.get("type") == "tool_result":
+                content.append({"type": "tool_result", "tool_use_id": block.get("tool_use_id"), "content": "{{tool-result}}"})
+            elif block.get("type") == "text":
+                content.append(minimal_snapshot_text(block, cwd))
+            else:
+                raise AssertionError(f"minimal user message has unexpected content: {block}")
+        return {"role": role, "content": content}
     if role == "assistant":
-        calls = message.get("tool_calls")
+        calls = message.get("content")
         if not isinstance(calls, list):
             raise AssertionError(f"minimal assistant message has no tool calls: {message}")
         return {
             "role": role,
             "toolCalls": [
-                {"id": call.get("id"), "name": (call.get("function") or {}).get("name")}
+                {"id": call.get("id"), "name": call.get("name")}
                 for call in calls
-                if isinstance(call, dict)
+                if isinstance(call, dict) and call.get("type") == "tool_use"
             ],
         }
-    if role == "tool":
-        return {"role": role, "toolCallId": message.get("tool_call_id"), "text": "{{tool-result}}"}
     raise AssertionError(f"minimal model request has an unexpected message role: {message}")
 
 
@@ -2061,6 +2086,7 @@ def build_restart_snapshot_files(
     request_value = [
         {
             "model": request.get("model"),
+            "system": "{{system}}" if request.get("system") else None,
             "messages": restart_request_messages(request),
             "toolNames": sorted(advertised_tool_names(request)),
         }

ファイルの差分が大きいため隠しています
+ 623 - 19
scripts/snapshots/python-sdk-single-exe/advanced/result.json


+ 1 - 1
scripts/snapshots/python-sdk-single-exe/advanced/session.1.v3.jsonl

@@ -14,6 +14,6 @@
 {"type":"request/context","data":{"provider":"deepseek-official","model":"smoke-model","contextWindow":1000000}}
 {"type":"session/title","data":{"title":"Reply with exactly DIRECT_CHILD_OK and","messageSeqs":[9],"source":{"kind":"fallback"}}}
 {"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{child-1}}","sessionFormatVersion":3,"throughSeq":13}}
-{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":0,"dt":[],"texts":["DIRECT_CHILD_OK"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DIRECT_CHILD_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
+{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model","replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"text"}]}},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":0,"dt":[],"texts":["DIRECT_CHILD_OK"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DIRECT_CHILD_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"},"replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"text"}]}}}]},"surfaceOp":"append"}
 {"type":"step/end","data":{"turn":1,"step":1}}
 {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}

+ 1 - 1
scripts/snapshots/python-sdk-single-exe/advanced/session.2.v3.jsonl

@@ -14,6 +14,6 @@
 {"type":"request/context","data":{"provider":"deepseek-official","model":"smoke-model","contextWindow":1000000}}
 {"type":"session/title","data":{"title":"Reply with exactly WORKFLOW_CHILD_OK and","messageSeqs":[9],"source":{"kind":"fallback"}}}
 {"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{child-2}}","sessionFormatVersion":3,"throughSeq":13}}
-{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":0,"dt":[],"texts":["WORKFLOW_CHILD_OK"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"WORKFLOW_CHILD_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
+{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model","replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"text"}]}},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":0,"dt":[],"texts":["WORKFLOW_CHILD_OK"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"WORKFLOW_CHILD_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"},"replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"text"}]}}}]},"surfaceOp":"append"}
 {"type":"step/end","data":{"turn":1,"step":1}}
 {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}

ファイルの差分が大きいため隠しています
+ 0 - 0
scripts/snapshots/python-sdk-single-exe/advanced/session.v3.jsonl


+ 84 - 72
scripts/snapshots/python-sdk-single-exe/minimal/model-visible.json

@@ -1,67 +1,65 @@
 [
   {
+    "system": "You are a helpful software engineer assistant.",
     "tools": [
       {
-        "type": "function",
-        "function": {
-          "name": "bash",
-          "description": "Run commands in a bash shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* Network access depends on the task environment. Prefer configured mirrors/proxies when they are available.\n* State is persistent across command calls and discussions with the user.\n* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.",
-          "parameters": {
-            "type": "object",
-            "properties": {
-              "command": {
-                "type": "string",
-                "description": "The bash command to run. Relative path is preferred in the command."
-              }
-            },
-            "required": [
-              "command"
-            ]
-          }
+        "name": "bash",
+        "description": "Run commands in a bash shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* Network access depends on the task environment. Prefer configured mirrors/proxies when they are available.\n* State is persistent across command calls and discussions with the user.\n* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.",
+        "input_schema": {
+          "type": "object",
+          "properties": {
+            "command": {
+              "type": "string",
+              "description": "The bash command to run. Relative path is preferred in the command."
+            }
+          },
+          "required": [
+            "command"
+          ]
         }
       }
     ],
     "messages": [
-      {
-        "role": "system",
-        "text": "You are a helpful software engineer assistant."
-      },
       {
         "role": "user",
-        "text": "Exercise the packaged minimal agent's persistent shell."
+        "content": [
+          {
+            "type": "text",
+            "text": "Exercise the packaged minimal agent's persistent shell."
+          }
+        ]
       }
     ]
   },
   {
+    "system": "You are a helpful software engineer assistant.",
     "tools": [
       {
-        "type": "function",
-        "function": {
-          "name": "bash",
-          "description": "Run commands in a bash shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* Network access depends on the task environment. Prefer configured mirrors/proxies when they are available.\n* State is persistent across command calls and discussions with the user.\n* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.",
-          "parameters": {
-            "type": "object",
-            "properties": {
-              "command": {
-                "type": "string",
-                "description": "The bash command to run. Relative path is preferred in the command."
-              }
-            },
-            "required": [
-              "command"
-            ]
-          }
+        "name": "bash",
+        "description": "Run commands in a bash shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* Network access depends on the task environment. Prefer configured mirrors/proxies when they are available.\n* State is persistent across command calls and discussions with the user.\n* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.",
+        "input_schema": {
+          "type": "object",
+          "properties": {
+            "command": {
+              "type": "string",
+              "description": "The bash command to run. Relative path is preferred in the command."
+            }
+          },
+          "required": [
+            "command"
+          ]
         }
       }
     ],
     "messages": [
-      {
-        "role": "system",
-        "text": "You are a helpful software engineer assistant."
-      },
       {
         "role": "user",
-        "text": "Exercise the packaged minimal agent's persistent shell."
+        "content": [
+          {
+            "type": "text",
+            "text": "Exercise the packaged minimal agent's persistent shell."
+          }
+        ]
       },
       {
         "role": "assistant",
@@ -73,42 +71,46 @@
         ]
       },
       {
-        "role": "tool",
-        "toolCallId": "minimal-bash-1",
-        "text": "{{tool-result}}"
+        "role": "user",
+        "content": [
+          {
+            "type": "tool_result",
+            "tool_use_id": "minimal-bash-1",
+            "content": "{{tool-result}}"
+          }
+        ]
       }
     ]
   },
   {
+    "system": "You are a helpful software engineer assistant.",
     "tools": [
       {
-        "type": "function",
-        "function": {
-          "name": "bash",
-          "description": "Run commands in a bash shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* Network access depends on the task environment. Prefer configured mirrors/proxies when they are available.\n* State is persistent across command calls and discussions with the user.\n* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.",
-          "parameters": {
-            "type": "object",
-            "properties": {
-              "command": {
-                "type": "string",
-                "description": "The bash command to run. Relative path is preferred in the command."
-              }
-            },
-            "required": [
-              "command"
-            ]
-          }
+        "name": "bash",
+        "description": "Run commands in a bash shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* Network access depends on the task environment. Prefer configured mirrors/proxies when they are available.\n* State is persistent across command calls and discussions with the user.\n* To inspect a particular line range of a file, e.g. lines 10-25, try 'sed -n 10,25p /path/to/the/file'.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'sleep 10 &' or start a server in the background.",
+        "input_schema": {
+          "type": "object",
+          "properties": {
+            "command": {
+              "type": "string",
+              "description": "The bash command to run. Relative path is preferred in the command."
+            }
+          },
+          "required": [
+            "command"
+          ]
         }
       }
     ],
     "messages": [
-      {
-        "role": "system",
-        "text": "You are a helpful software engineer assistant."
-      },
       {
         "role": "user",
-        "text": "Exercise the packaged minimal agent's persistent shell."
+        "content": [
+          {
+            "type": "text",
+            "text": "Exercise the packaged minimal agent's persistent shell."
+          }
+        ]
       },
       {
         "role": "assistant",
@@ -120,9 +122,14 @@
         ]
       },
       {
-        "role": "tool",
-        "toolCallId": "minimal-bash-1",
-        "text": "{{tool-result}}"
+        "role": "user",
+        "content": [
+          {
+            "type": "tool_result",
+            "tool_use_id": "minimal-bash-1",
+            "content": "{{tool-result}}"
+          }
+        ]
       },
       {
         "role": "assistant",
@@ -134,9 +141,14 @@
         ]
       },
       {
-        "role": "tool",
-        "toolCallId": "minimal-bash-2",
-        "text": "{{tool-result}}"
+        "role": "user",
+        "content": [
+          {
+            "type": "tool_result",
+            "tool_use_id": "minimal-bash-2",
+            "content": "{{tool-result}}"
+          }
+        ]
       }
     ]
   }

+ 84 - 72
scripts/snapshots/python-sdk-single-exe/minimal/win-x64/model-visible.json

@@ -1,67 +1,65 @@
 [
   {
+    "system": "You are a helpful software engineer assistant.",
     "tools": [
       {
-        "type": "function",
-        "function": {
-          "name": "pwsh",
-          "description": "Run commands in a PowerShell shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* You don't have access to the internet via this tool.\n* State is persistent across command calls and discussions with the user.\n* Use native Windows paths (C:\\...) and $env:NAME variables; this is PowerShell, not bash.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.",
-          "parameters": {
-            "type": "object",
-            "properties": {
-              "command": {
-                "type": "string",
-                "description": "The PowerShell command to run. Relative path is preferred in the command."
-              }
-            },
-            "required": [
-              "command"
-            ]
-          }
+        "name": "pwsh",
+        "description": "Run commands in a PowerShell shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* You don't have access to the internet via this tool.\n* State is persistent across command calls and discussions with the user.\n* Use native Windows paths (C:\\...) and $env:NAME variables; this is PowerShell, not bash.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.",
+        "input_schema": {
+          "type": "object",
+          "properties": {
+            "command": {
+              "type": "string",
+              "description": "The PowerShell command to run. Relative path is preferred in the command."
+            }
+          },
+          "required": [
+            "command"
+          ]
         }
       }
     ],
     "messages": [
-      {
-        "role": "system",
-        "text": "You are a helpful software engineer assistant."
-      },
       {
         "role": "user",
-        "text": "Exercise the packaged minimal agent's persistent shell."
+        "content": [
+          {
+            "type": "text",
+            "text": "Exercise the packaged minimal agent's persistent shell."
+          }
+        ]
       }
     ]
   },
   {
+    "system": "You are a helpful software engineer assistant.",
     "tools": [
       {
-        "type": "function",
-        "function": {
-          "name": "pwsh",
-          "description": "Run commands in a PowerShell shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* You don't have access to the internet via this tool.\n* State is persistent across command calls and discussions with the user.\n* Use native Windows paths (C:\\...) and $env:NAME variables; this is PowerShell, not bash.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.",
-          "parameters": {
-            "type": "object",
-            "properties": {
-              "command": {
-                "type": "string",
-                "description": "The PowerShell command to run. Relative path is preferred in the command."
-              }
-            },
-            "required": [
-              "command"
-            ]
-          }
+        "name": "pwsh",
+        "description": "Run commands in a PowerShell shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* You don't have access to the internet via this tool.\n* State is persistent across command calls and discussions with the user.\n* Use native Windows paths (C:\\...) and $env:NAME variables; this is PowerShell, not bash.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.",
+        "input_schema": {
+          "type": "object",
+          "properties": {
+            "command": {
+              "type": "string",
+              "description": "The PowerShell command to run. Relative path is preferred in the command."
+            }
+          },
+          "required": [
+            "command"
+          ]
         }
       }
     ],
     "messages": [
-      {
-        "role": "system",
-        "text": "You are a helpful software engineer assistant."
-      },
       {
         "role": "user",
-        "text": "Exercise the packaged minimal agent's persistent shell."
+        "content": [
+          {
+            "type": "text",
+            "text": "Exercise the packaged minimal agent's persistent shell."
+          }
+        ]
       },
       {
         "role": "assistant",
@@ -73,42 +71,46 @@
         ]
       },
       {
-        "role": "tool",
-        "toolCallId": "minimal-bash-1",
-        "text": "{{tool-result}}"
+        "role": "user",
+        "content": [
+          {
+            "type": "tool_result",
+            "tool_use_id": "minimal-bash-1",
+            "content": "{{tool-result}}"
+          }
+        ]
       }
     ]
   },
   {
+    "system": "You are a helpful software engineer assistant.",
     "tools": [
       {
-        "type": "function",
-        "function": {
-          "name": "pwsh",
-          "description": "Run commands in a PowerShell shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* You don't have access to the internet via this tool.\n* State is persistent across command calls and discussions with the user.\n* Use native Windows paths (C:\\...) and $env:NAME variables; this is PowerShell, not bash.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.",
-          "parameters": {
-            "type": "object",
-            "properties": {
-              "command": {
-                "type": "string",
-                "description": "The PowerShell command to run. Relative path is preferred in the command."
-              }
-            },
-            "required": [
-              "command"
-            ]
-          }
+        "name": "pwsh",
+        "description": "Run commands in a PowerShell shell\n* When invoking this tool, the contents of the \"command\" parameter does NOT need to be XML-escaped.\n* You don't have access to the internet via this tool.\n* State is persistent across command calls and discussions with the user.\n* Use native Windows paths (C:\\...) and $env:NAME variables; this is PowerShell, not bash.\n* Please avoid commands that may produce a very large amount of output.\n* Please run long lived commands in the background, e.g. 'Start-Job' or start a server with Start-Process.",
+        "input_schema": {
+          "type": "object",
+          "properties": {
+            "command": {
+              "type": "string",
+              "description": "The PowerShell command to run. Relative path is preferred in the command."
+            }
+          },
+          "required": [
+            "command"
+          ]
         }
       }
     ],
     "messages": [
-      {
-        "role": "system",
-        "text": "You are a helpful software engineer assistant."
-      },
       {
         "role": "user",
-        "text": "Exercise the packaged minimal agent's persistent shell."
+        "content": [
+          {
+            "type": "text",
+            "text": "Exercise the packaged minimal agent's persistent shell."
+          }
+        ]
       },
       {
         "role": "assistant",
@@ -120,9 +122,14 @@
         ]
       },
       {
-        "role": "tool",
-        "toolCallId": "minimal-bash-1",
-        "text": "{{tool-result}}"
+        "role": "user",
+        "content": [
+          {
+            "type": "tool_result",
+            "tool_use_id": "minimal-bash-1",
+            "content": "{{tool-result}}"
+          }
+        ]
       },
       {
         "role": "assistant",
@@ -134,9 +141,14 @@
         ]
       },
       {
-        "role": "tool",
-        "toolCallId": "minimal-bash-2",
-        "text": "{{tool-result}}"
+        "role": "user",
+        "content": [
+          {
+            "type": "tool_result",
+            "tool_use_id": "minimal-bash-2",
+            "content": "{{tool-result}}"
+          }
+        ]
       }
     ]
   }

+ 22 - 18
scripts/snapshots/python-sdk-single-exe/restart/requests.json

@@ -1,18 +1,20 @@
 [
   {
     "model": "smoke-model",
+    "system": "{{system}}",
     "messages": [
-      {
-        "role": "system",
-        "content": "{{system}}"
-      },
-      {
-        "role": "user",
-        "content": "Complete the first isolated Python SDK process turn."
-      },
       {
         "role": "user",
-        "content": "Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."
+        "content": [
+          {
+            "type": "text",
+            "text": "Complete the first isolated Python SDK process turn."
+          },
+          {
+            "type": "text",
+            "text": "Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."
+          }
+        ]
       }
     ],
     "toolNames": [
@@ -33,18 +35,20 @@
   },
   {
     "model": "smoke-model",
+    "system": "{{system}}",
     "messages": [
-      {
-        "role": "system",
-        "content": "{{system}}"
-      },
-      {
-        "role": "user",
-        "content": "Complete the second isolated Python SDK process turn."
-      },
       {
         "role": "user",
-        "content": "Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."
+        "content": [
+          {
+            "type": "text",
+            "text": "Complete the second isolated Python SDK process turn."
+          },
+          {
+            "type": "text",
+            "text": "Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."
+          }
+        ]
       }
     ],
     "toolNames": [

+ 1 - 1
scripts/snapshots/python-sdk-single-exe/restart/session.1.v3.jsonl

@@ -13,6 +13,6 @@
 {"type":"request/context","data":{"provider":"deepseek-official","model":"smoke-model","contextWindow":1000000}}
 {"type":"session/title","data":{"title":"Complete the first isolated Python","messageSeqs":[8],"source":{"kind":"fallback"}}}
 {"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{session-1}}","sessionFormatVersion":3,"throughSeq":12}}
-{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"PROCESS_ONE_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":0,"dt":[],"texts":["PROCESS_ONE_OK"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"PROCESS_ONE_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
+{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"PROCESS_ONE_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model","replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"text"}]}},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":0,"dt":[],"texts":["PROCESS_ONE_OK"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"PROCESS_ONE_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"},"replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"text"}]}}}]},"surfaceOp":"append"}
 {"type":"step/end","data":{"turn":1,"step":1}}
 {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}

+ 1 - 1
scripts/snapshots/python-sdk-single-exe/restart/session.2.v3.jsonl

@@ -13,6 +13,6 @@
 {"type":"request/context","data":{"provider":"deepseek-official","model":"smoke-model","contextWindow":1000000}}
 {"type":"session/title","data":{"title":"Complete the second isolated Python","messageSeqs":[8],"source":{"kind":"fallback"}}}
 {"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{session-2}}","sessionFormatVersion":3,"throughSeq":12}}
-{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"PROCESS_TWO_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":0,"dt":[],"texts":["PROCESS_TWO_OK"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"PROCESS_TWO_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
+{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"PROCESS_TWO_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model","replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"text"}]}},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":0,"dt":[],"texts":["PROCESS_TWO_OK"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"PROCESS_TWO_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"},"replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"text"}]}}}]},"surfaceOp":"append"}
 {"type":"step/end","data":{"turn":1,"step":1}}
 {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません