authorgravatar for 71498452+StiensWout@users.noreply.github.comWout Stiens <71498452+StiensWout@users.noreply.github.com> 2026-10-13 19:39:18+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-13 12:39:18-05:00
log9a9fd2e3ed613275b0c1fdf356def2230480c47e
treefaf458147aab98b59146f5cf57f69dce41f8864c
parent446be6933f4ecc207eed94db361ff40a88c3964f
signature Signed by PGP key B5690EEEBB952194

fix(pi): queue turn-start prompts when native work is running (#18639)


11 files changed, 79 insertions(+), 22 deletions(-)

apps/server/src/orchestration-v2/testkit/fixtures/message_steering/pi_output.ts+1-1
...@@ -36,7 +36,7 @@ export function assertPiMessageSteeringOutput(...@@ -36,7 +36,7 @@ export function assertPiMessageSteeringOutput(
36 assert.deepEqual(36 assert.deepEqual(
37 prompts.map((frame) => [frameField(frame, "message"), frameField(frame, "streamingBehavior")]),37 prompts.map((frame) => [frameField(frame, "message"), frameField(frame, "streamingBehavior")]),
38 [38 [
39 [MESSAGE_STEERING_INITIAL_PROMPT, undefined],39 [MESSAGE_STEERING_INITIAL_PROMPT, "steer"],
40 [MESSAGE_STEERING_STEER_PROMPT, "steer"],40 [MESSAGE_STEERING_STEER_PROMPT, "steer"],
41 ],41 ],
42 );42 );
apps/server/src/orchestration-v2/testkit/fixtures/message_steering/pi_transcript.ndjson+1-1
...@@ -12,7 +12,7 @@...@@ -12,7 +12,7 @@
12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: message_steering","id":"t3-5"}}12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: message_steering","id":"t3-5"}}
13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: message_steering"}}13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: message_steering"}}
14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}
15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: steering fixture initial response"}}15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: steering fixture initial response","streamingBehavior":"steer"}}
16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
apps/server/src/orchestration-v2/testkit/fixtures/multi_turn/pi_transcript.ndjson+2-2
...@@ -12,7 +12,7 @@...@@ -12,7 +12,7 @@
12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: multi_turn","id":"t3-5"}}12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: multi_turn","id":"t3-5"}}
13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: multi_turn"}}13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: multi_turn"}}
14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}
15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: first fixture turn complete"}}15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: first fixture turn complete","streamingBehavior":"steer"}}
16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
...@@ -41,7 +41,7 @@...@@ -41,7 +41,7 @@
41{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-7","type":"response","command":"get_entries","success":true,"data":{"entries":[{"type":"model_change","id":"5e37f535","parentId":"f3bce6ef","timestamp":"2026-01-01T00:00:00.437Z","provider":"openrouter","modelId":"deepseek/deepseek-v4-flash"},{"type":"session_info","id":"16d089a9","parentId":"5e37f535","timestamp":"2026-01-01T00:00:00.440Z","name":"Replay fixture: multi_turn"},{"type":"message","id":"ea6f5949","parentId":"16d089a9","timestamp":"2026-01-01T00:00:00.448Z","message":{"role":"system","content":"","sections":{"preamble":"<system-prompt-section>","tools":"<system-prompt-section>","rules":"<system-prompt-section>","docs":"<system-prompt-section>","cwd":"<system-prompt-section>"},"timestamp":1790898591750,"toolsAdded":[{"name":"read","description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","parameters":{"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"Path to the file to read (relative or absolute)"},"offset":{"type":"number","description":"Line number to start reading from (1-indexed)"},"limit":{"type":"number","description":"Maximum number of lines to read"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"bash","description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","parameters":{"type":"object","required":["command"],"properties":{"command":{"type":"string","description":"Shell command to execute"},"timeout":{"type":"number","description":"Timeout in seconds (optional, no default timeout)"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"edit","description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","parameters":{"type":"object","required":["path","edits"],"properties":{"path":{"type":"string","description":"Path to the file to edit (relative or absolute)"},"edits":{"type":"array","items":{"type":"object","required":["oldText","newText"],"properties":{"oldText":{"type":"string","description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call."},"newText":{"type":"string","description":"Replacement text for this targeted edit."}}},"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead."}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"write","description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","parameters":{"type":"object","required":["path","content"],"properties":{"path":{"type":"string","description":"Path to the file to write (relative or absolute)"},"content":{"type":"string","description":"Content to write to the file"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}}]}},{"type":"message","id":"ff895990","parentId":"ea6f5949","timestamp":"2026-01-01T00:00:00.448Z","message":{"role":"user","content":[{"type":"text","text":"Respond with exactly: first fixture turn complete"}],"timestamp":1767225600444}},{"type":"message","id":"5c0d3865","parentId":"ff895990","timestamp":"2026-01-01T00:00:01.723Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to respond with exactly: \"first fixture turn complete\"","thinkingSignature":"<signature>"},{"type":"text","text":"first fixture turn complete"}],"api":"openai-completions","provider":"openrouter","model":"deepseek/deepseek-v4-flash","usage":{"input":1787,"output":21,"cacheRead":0,"cacheWrite":0,"reasoning":18,"totalTokens":1808,"cost":{"input":0.00007480382,"output":0.0000017581199999999999,"cacheRead":0,"cacheWrite":0,"total":0.00007656194}},"stopReason":"stop","timestamp":1767225600465,"responseId":"gen-1790898592-RtbvUBJEB0AHaUCPMqrg","rawStopReason":"stop","thinkingLevel":"high"}}],"leafId":"5c0d3865"}}}41{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-7","type":"response","command":"get_entries","success":true,"data":{"entries":[{"type":"model_change","id":"5e37f535","parentId":"f3bce6ef","timestamp":"2026-01-01T00:00:00.437Z","provider":"openrouter","modelId":"deepseek/deepseek-v4-flash"},{"type":"session_info","id":"16d089a9","parentId":"5e37f535","timestamp":"2026-01-01T00:00:00.440Z","name":"Replay fixture: multi_turn"},{"type":"message","id":"ea6f5949","parentId":"16d089a9","timestamp":"2026-01-01T00:00:00.448Z","message":{"role":"system","content":"","sections":{"preamble":"<system-prompt-section>","tools":"<system-prompt-section>","rules":"<system-prompt-section>","docs":"<system-prompt-section>","cwd":"<system-prompt-section>"},"timestamp":1790898591750,"toolsAdded":[{"name":"read","description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","parameters":{"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"Path to the file to read (relative or absolute)"},"offset":{"type":"number","description":"Line number to start reading from (1-indexed)"},"limit":{"type":"number","description":"Maximum number of lines to read"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"bash","description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","parameters":{"type":"object","required":["command"],"properties":{"command":{"type":"string","description":"Shell command to execute"},"timeout":{"type":"number","description":"Timeout in seconds (optional, no default timeout)"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"edit","description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","parameters":{"type":"object","required":["path","edits"],"properties":{"path":{"type":"string","description":"Path to the file to edit (relative or absolute)"},"edits":{"type":"array","items":{"type":"object","required":["oldText","newText"],"properties":{"oldText":{"type":"string","description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call."},"newText":{"type":"string","description":"Replacement text for this targeted edit."}}},"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead."}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"write","description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","parameters":{"type":"object","required":["path","content"],"properties":{"path":{"type":"string","description":"Path to the file to write (relative or absolute)"},"content":{"type":"string","description":"Content to write to the file"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}}]}},{"type":"message","id":"ff895990","parentId":"ea6f5949","timestamp":"2026-01-01T00:00:00.448Z","message":{"role":"user","content":[{"type":"text","text":"Respond with exactly: first fixture turn complete"}],"timestamp":1767225600444}},{"type":"message","id":"5c0d3865","parentId":"ff895990","timestamp":"2026-01-01T00:00:01.723Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to respond with exactly: \"first fixture turn complete\"","thinkingSignature":"<signature>"},{"type":"text","text":"first fixture turn complete"}],"api":"openai-completions","provider":"openrouter","model":"deepseek/deepseek-v4-flash","usage":{"input":1787,"output":21,"cacheRead":0,"cacheWrite":0,"reasoning":18,"totalTokens":1808,"cost":{"input":0.00007480382,"output":0.0000017581199999999999,"cacheRead":0,"cacheWrite":0,"total":0.00007656194}},"stopReason":"stop","timestamp":1767225600465,"responseId":"gen-1790898592-RtbvUBJEB0AHaUCPMqrg","rawStopReason":"stop","thinkingLevel":"high"}}],"leafId":"5c0d3865"}}}
42{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-8"}}42{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-8"}}
43{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-8","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":1,"assistantMessages":1,"toolCalls":0,"toolResults":0,"totalMessages":3,"tokens":{"input":1787,"output":21,"cacheRead":0,"cacheWrite":0,"total":1808},"cost":0.00007656194,"contextUsage":{"tokens":1808,"contextWindow":1048576,"percent":0.17242431640625}}}}43{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-8","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":1,"assistantMessages":1,"toolCalls":0,"toolResults":0,"totalMessages":3,"tokens":{"input":1787,"output":21,"cacheRead":0,"cacheWrite":0,"total":1808},"cost":0.00007656194,"contextUsage":{"tokens":1808,"contextWindow":1048576,"percent":0.17242431640625}}}}
44{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: second fixture turn complete"}}44{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: second fixture turn complete","streamingBehavior":"steer"}}
45{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}45{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
46{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}46{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
47{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}47{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
apps/server/src/orchestration-v2/testkit/fixtures/pi_compaction/pi_transcript.ndjson+4-4
...@@ -12,7 +12,7 @@...@@ -12,7 +12,7 @@
12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: pi_compaction","id":"t3-5"}}12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: pi_compaction","id":"t3-5"}}
13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: pi_compaction"}}13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: pi_compaction"}}
14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}
15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Remember the opaque marker compaction-marker-4Z7Q for later. Respond with exactly: compaction marker stored"}}15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Remember the opaque marker compaction-marker-4Z7Q for later. Respond with exactly: compaction marker stored","streamingBehavior":"steer"}}
16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
...@@ -56,7 +56,7 @@...@@ -56,7 +56,7 @@
56{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-11","type":"response","command":"get_entries","success":true,"data":{"entries":[],"leafId":"9923aaa9"}}}56{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-11","type":"response","command":"get_entries","success":true,"data":{"entries":[],"leafId":"9923aaa9"}}}
57{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-12"}}57{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-12"}}
58{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-12","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":1,"assistantMessages":1,"toolCalls":0,"toolResults":0,"totalMessages":3,"tokens":{"input":1803,"output":36,"cacheRead":0,"cacheWrite":0,"total":1839},"cost":0.0000784875,"contextUsage":{"tokens":1839,"contextWindow":1048576,"percent":0.17538070678710938}}}}58{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-12","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":1,"assistantMessages":1,"toolCalls":0,"toolResults":0,"totalMessages":3,"tokens":{"input":1803,"output":36,"cacheRead":0,"cacheWrite":0,"total":1839},"cost":0.0000784875,"contextUsage":{"tokens":1839,"contextWindow":1048576,"percent":0.17538070678710938}}}}
59{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: second compaction fixture turn"}}59{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: second compaction fixture turn","streamingBehavior":"steer"}}
60{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}60{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
61{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}61{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
62{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}62{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
...@@ -82,7 +82,7 @@...@@ -82,7 +82,7 @@
82{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-14","type":"response","command":"get_entries","success":true,"data":{"entries":[{"type":"message","id":"de423e78","parentId":"9923aaa9","timestamp":"2026-01-01T00:00:02.437Z","message":{"role":"user","content":[{"type":"text","text":"Respond with exactly: second compaction fixture turn"}],"timestamp":1767225602435}},{"type":"message","id":"70c802be","parentId":"de423e78","timestamp":"2026-01-01T00:00:03.192Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to respond with exactly \"second compaction fixture turn\".","thinkingSignature":"<signature>"},{"type":"text","text":"second compaction fixture turn"}],"api":"openai-completions","provider":"openrouter","model":"deepseek/deepseek-v4-flash","usage":{"input":58,"output":20,"cacheRead":1792,"cacheWrite":0,"reasoning":19,"totalTokens":1870,"cost":{"input":0.00000242788,"output":0.0000016744,"cacheRead":0.000015002623999999998,"cacheWrite":0,"total":0.000019104903999999998}},"stopReason":"stop","timestamp":1767225602438,"responseId":"gen-1790898593-ijLmDH6S1P8UCX8xelRy","rawStopReason":"stop","thinkingLevel":"high"}}],"leafId":"70c802be"}}}82{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-14","type":"response","command":"get_entries","success":true,"data":{"entries":[{"type":"message","id":"de423e78","parentId":"9923aaa9","timestamp":"2026-01-01T00:00:02.437Z","message":{"role":"user","content":[{"type":"text","text":"Respond with exactly: second compaction fixture turn"}],"timestamp":1767225602435}},{"type":"message","id":"70c802be","parentId":"de423e78","timestamp":"2026-01-01T00:00:03.192Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to respond with exactly \"second compaction fixture turn\".","thinkingSignature":"<signature>"},{"type":"text","text":"second compaction fixture turn"}],"api":"openai-completions","provider":"openrouter","model":"deepseek/deepseek-v4-flash","usage":{"input":58,"output":20,"cacheRead":1792,"cacheWrite":0,"reasoning":19,"totalTokens":1870,"cost":{"input":0.00000242788,"output":0.0000016744,"cacheRead":0.000015002623999999998,"cacheWrite":0,"total":0.000019104903999999998}},"stopReason":"stop","timestamp":1767225602438,"responseId":"gen-1790898593-ijLmDH6S1P8UCX8xelRy","rawStopReason":"stop","thinkingLevel":"high"}}],"leafId":"70c802be"}}}
83{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-15"}}83{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-15"}}
84{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-15","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":2,"assistantMessages":2,"toolCalls":0,"toolResults":0,"totalMessages":5,"tokens":{"input":1861,"output":56,"cacheRead":1792,"cacheWrite":0,"total":3709},"cost":0.00009759240400000001,"contextUsage":{"tokens":1870,"contextWindow":1048576,"percent":0.17833709716796875}}}}84{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-15","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":2,"assistantMessages":2,"toolCalls":0,"toolResults":0,"totalMessages":5,"tokens":{"input":1861,"output":56,"cacheRead":1792,"cacheWrite":0,"total":3709},"cost":0.00009759240400000001,"contextUsage":{"tokens":1870,"contextWindow":1048576,"percent":0.17833709716796875}}}}
85{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: third compaction fixture turn"}}85{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: third compaction fixture turn","streamingBehavior":"steer"}}
86{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}86{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
87{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}87{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
88{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}88{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
...@@ -120,7 +120,7 @@...@@ -120,7 +120,7 @@
120{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-21","type":"response","command":"get_entries","success":true,"data":{"entries":[{"type":"compaction","id":"f5058450","parentId":"705e1e5c","timestamp":"2026-01-01T00:00:34.035Z","summary":"## Goal\nThe user asked the assistant to remember the opaque marker \"compaction-marker-4Z7Q\" for later use in the conversation.\n\n## Constraints & Preferences\n- The assistant was required to respond with exactly \"compaction marker stored\" (which it did).\n\n## Progress\n### Done\n- [x] Acknowledged and stored the marker \"compaction-marker-4Z7Q\"\n- [x] Responded with exactly \"compaction marker stored\"\n\n### In Progress\n- (none)\n\n### Blocked\n- (none)\n\n## Key Decisions\n- **Remember the marker**: The value \"compaction-marker-4Z7Q\" is to be retained as the opaque marker for later reference.\n\n## Next Steps\n1. When the user later references \"the marker\" or asks to use it, recall that the marker value is \"compaction-marker-4Z7Q\".\n2. Use this marker in whatever context the user requests (e.g., compaction operations).\n\n## Critical Context\n- Opaque marker value: **compaction-marker-4Z7Q**\n\n---\n\n**Turn Context (split turn):**\n\n# Conversation Summary\n\n## Original Request\nThe user asked the assistant to \"Respond with exactly: second compaction fixture turn\" — i.e., to reply using only that exact phrase.\n\n## Progress So Far\n- The user issued a single directive requiring a verbatim response of the phrase \"second compaction fixture turn.\"\n- No additional work, decisions, or content has been provided in this segment.\n\n## Context Needed to Continue\n- The required exact response text is: **second compaction fixture turn**\n- The user's instruction is to output nothing beyond that exact phrase.","firstKeptEntryId":"70c802be","tokensBefore":1901,"details":{"readFiles":[],"modifiedFiles":[]},"usage":{"input":0,"output":1003,"cacheRead":693,"cacheWrite":0,"reasoning":660,"totalTokens":1696,"cost":{"input":0,"output":0.00008397116,"cacheRead":0.000005801795999999999,"cacheWrite":0,"total":0.000089772956}},"fromHook":false,"systemMessage":{"role":"system","content":"","sections":{"preamble":"<system-prompt-section>","tools":"<system-prompt-section>","rules":"<system-prompt-section>","docs":"<system-prompt-section>","cwd":"<system-prompt-section>"},"toolsAdded":[{"name":"read","description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","parameters":{"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"Path to the file to read (relative or absolute)"},"offset":{"type":"number","description":"Line number to start reading from (1-indexed)"},"limit":{"type":"number","description":"Maximum number of lines to read"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"bash","description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","parameters":{"type":"object","required":["command"],"properties":{"command":{"type":"string","description":"Shell command to execute"},"timeout":{"type":"number","description":"Timeout in seconds (optional, no default timeout)"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"edit","description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","parameters":{"type":"object","required":["path","edits"],"properties":{"path":{"type":"string","description":"Path to the file to edit (relative or absolute)"},"edits":{"type":"array","items":{"type":"object","required":["oldText","newText"],"properties":{"oldText":{"type":"string","description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call."},"newText":{"type":"string","description":"Replacement text for this targeted edit."}}},"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead."}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"write","description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","parameters":{"type":"object","required":["path","content"],"properties":{"path":{"type":"string","description":"Path to the file to write (relative or absolute)"},"content":{"type":"string","description":"Content to write to the file"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}}],"timestamp":1790898625260}}],"leafId":"f5058450"}}}120{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-21","type":"response","command":"get_entries","success":true,"data":{"entries":[{"type":"compaction","id":"f5058450","parentId":"705e1e5c","timestamp":"2026-01-01T00:00:34.035Z","summary":"## Goal\nThe user asked the assistant to remember the opaque marker \"compaction-marker-4Z7Q\" for later use in the conversation.\n\n## Constraints & Preferences\n- The assistant was required to respond with exactly \"compaction marker stored\" (which it did).\n\n## Progress\n### Done\n- [x] Acknowledged and stored the marker \"compaction-marker-4Z7Q\"\n- [x] Responded with exactly \"compaction marker stored\"\n\n### In Progress\n- (none)\n\n### Blocked\n- (none)\n\n## Key Decisions\n- **Remember the marker**: The value \"compaction-marker-4Z7Q\" is to be retained as the opaque marker for later reference.\n\n## Next Steps\n1. When the user later references \"the marker\" or asks to use it, recall that the marker value is \"compaction-marker-4Z7Q\".\n2. Use this marker in whatever context the user requests (e.g., compaction operations).\n\n## Critical Context\n- Opaque marker value: **compaction-marker-4Z7Q**\n\n---\n\n**Turn Context (split turn):**\n\n# Conversation Summary\n\n## Original Request\nThe user asked the assistant to \"Respond with exactly: second compaction fixture turn\" — i.e., to reply using only that exact phrase.\n\n## Progress So Far\n- The user issued a single directive requiring a verbatim response of the phrase \"second compaction fixture turn.\"\n- No additional work, decisions, or content has been provided in this segment.\n\n## Context Needed to Continue\n- The required exact response text is: **second compaction fixture turn**\n- The user's instruction is to output nothing beyond that exact phrase.","firstKeptEntryId":"70c802be","tokensBefore":1901,"details":{"readFiles":[],"modifiedFiles":[]},"usage":{"input":0,"output":1003,"cacheRead":693,"cacheWrite":0,"reasoning":660,"totalTokens":1696,"cost":{"input":0,"output":0.00008397116,"cacheRead":0.000005801795999999999,"cacheWrite":0,"total":0.000089772956}},"fromHook":false,"systemMessage":{"role":"system","content":"","sections":{"preamble":"<system-prompt-section>","tools":"<system-prompt-section>","rules":"<system-prompt-section>","docs":"<system-prompt-section>","cwd":"<system-prompt-section>"},"toolsAdded":[{"name":"read","description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","parameters":{"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"Path to the file to read (relative or absolute)"},"offset":{"type":"number","description":"Line number to start reading from (1-indexed)"},"limit":{"type":"number","description":"Maximum number of lines to read"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"bash","description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","parameters":{"type":"object","required":["command"],"properties":{"command":{"type":"string","description":"Shell command to execute"},"timeout":{"type":"number","description":"Timeout in seconds (optional, no default timeout)"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"edit","description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","parameters":{"type":"object","required":["path","edits"],"properties":{"path":{"type":"string","description":"Path to the file to edit (relative or absolute)"},"edits":{"type":"array","items":{"type":"object","required":["oldText","newText"],"properties":{"oldText":{"type":"string","description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call."},"newText":{"type":"string","description":"Replacement text for this targeted edit."}}},"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead."}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"write","description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","parameters":{"type":"object","required":["path","content"],"properties":{"path":{"type":"string","description":"Path to the file to write (relative or absolute)"},"content":{"type":"string","description":"Content to write to the file"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}}],"timestamp":1790898625260}}],"leafId":"f5058450"}}}
121{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-22"}}121{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-22"}}
122{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-22","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":3,"assistantMessages":3,"toolCalls":0,"toolResults":0,"totalMessages":7,"tokens":{"input":1950,"output":1079,"cacheRead":4277,"cacheWrite":0,"total":7306},"cost":0.000207767924,"contextUsage":{"tokens":null,"contextWindow":1048576,"percent":null}}}}122{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-22","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":3,"assistantMessages":3,"toolCalls":0,"toolResults":0,"totalMessages":7,"tokens":{"input":1950,"output":1079,"cacheRead":4277,"cacheWrite":0,"total":7306},"cost":0.000207767924,"contextUsage":{"tokens":null,"contextWindow":1048576,"percent":null}}}}
123{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Return the opaque marker I asked you to remember. Respond with only the marker."}}123{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Return the opaque marker I asked you to remember. Respond with only the marker.","streamingBehavior":"steer"}}
124{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}124{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
125{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}125{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
126{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}126{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
apps/server/src/orchestration-v2/testkit/fixtures/provider_thread_resume/pi_transcript.ndjson+2-2
...@@ -12,7 +12,7 @@...@@ -12,7 +12,7 @@
12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: provider_thread_resume","id":"t3-5"}}12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: provider_thread_resume","id":"t3-5"}}
13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: provider_thread_resume"}}13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: provider_thread_resume"}}
14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}
15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: provider thread resume fixture first turn complete"}}15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: provider thread resume fixture first turn complete","streamingBehavior":"steer"}}
16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
...@@ -74,7 +74,7 @@...@@ -74,7 +74,7 @@
74{"type":"expect_outbound","label":"set_session_name@p2","frame":{"type":"set_session_name","name":"Replay fixture: provider_thread_resume","id":"t3-6"}}74{"type":"expect_outbound","label":"set_session_name@p2","frame":{"type":"set_session_name","name":"Replay fixture: provider_thread_resume","id":"t3-6"}}
75{"type":"emit_inbound","label":"session_info_changed@p2","frame":{"type":"session_info_changed","name":"Replay fixture: provider_thread_resume"}}75{"type":"emit_inbound","label":"session_info_changed@p2","frame":{"type":"session_info_changed","name":"Replay fixture: provider_thread_resume"}}
76{"type":"emit_inbound","label":"response:set_session_name@p2","frame":{"id":"t3-6","type":"response","command":"set_session_name","success":true}}76{"type":"emit_inbound","label":"response:set_session_name@p2","frame":{"id":"t3-6","type":"response","command":"set_session_name","success":true}}
77{"type":"expect_outbound","label":"prompt@p2","frame":{"type":"prompt","message":"Using the conversation history available in this resumed thread, first repeat the exact final answer you gave in the previous turn. Then on a new line write exactly: provider thread resume fixture second turn complete"}}77{"type":"expect_outbound","label":"prompt@p2","frame":{"type":"prompt","message":"Using the conversation history available in this resumed thread, first repeat the exact final answer you gave in the previous turn. Then on a new line write exactly: provider thread resume fixture second turn complete","streamingBehavior":"steer"}}
78{"type":"emit_inbound","label":"response:prompt@p2","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}78{"type":"emit_inbound","label":"response:prompt@p2","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
79{"type":"emit_inbound","label":"agent_start@p2","frame":{"type":"agent_start"}}79{"type":"emit_inbound","label":"agent_start@p2","frame":{"type":"agent_start"}}
80{"type":"emit_inbound","label":"turn_start@p2","frame":{"type":"turn_start"}}80{"type":"emit_inbound","label":"turn_start@p2","frame":{"type":"turn_start"}}
apps/server/src/orchestration-v2/testkit/fixtures/simple/pi_transcript.ndjson+1-1
...@@ -12,7 +12,7 @@...@@ -12,7 +12,7 @@
12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: simple","id":"t3-5"}}12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: simple","id":"t3-5"}}
13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: simple"}}13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: simple"}}
14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}
15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with the following text: fixture simple ok"}}15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with the following text: fixture simple ok","streamingBehavior":"steer"}}
16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
apps/server/src/orchestration-v2/testkit/fixtures/thread_rollback/pi_transcript.ndjson+3-3
...@@ -12,7 +12,7 @@...@@ -12,7 +12,7 @@
12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: thread_rollback","id":"t3-5"}}12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: thread_rollback","id":"t3-5"}}
13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: thread_rollback"}}13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: thread_rollback"}}
14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}
15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: rollback fixture first turn complete"}}15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: rollback fixture first turn complete","streamingBehavior":"steer"}}
16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
...@@ -52,7 +52,7 @@...@@ -52,7 +52,7 @@
52{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-7","type":"response","command":"get_entries","success":true,"data":{"entries":[{"type":"model_change","id":"cc15a6b4","parentId":"00c31b78","timestamp":"2026-01-01T00:00:00.344Z","provider":"openrouter","modelId":"deepseek/deepseek-v4-flash"},{"type":"session_info","id":"2c1dcf96","parentId":"cc15a6b4","timestamp":"2026-01-01T00:00:00.349Z","name":"Replay fixture: thread_rollback"},{"type":"message","id":"aca5528d","parentId":"2c1dcf96","timestamp":"2026-01-01T00:00:00.357Z","message":{"role":"system","content":"","sections":{"preamble":"<system-prompt-section>","tools":"<system-prompt-section>","rules":"<system-prompt-section>","docs":"<system-prompt-section>","cwd":"<system-prompt-section>"},"timestamp":1790898591831,"toolsAdded":[{"name":"read","description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","parameters":{"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"Path to the file to read (relative or absolute)"},"offset":{"type":"number","description":"Line number to start reading from (1-indexed)"},"limit":{"type":"number","description":"Maximum number of lines to read"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"bash","description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","parameters":{"type":"object","required":["command"],"properties":{"command":{"type":"string","description":"Shell command to execute"},"timeout":{"type":"number","description":"Timeout in seconds (optional, no default timeout)"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"edit","description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","parameters":{"type":"object","required":["path","edits"],"properties":{"path":{"type":"string","description":"Path to the file to edit (relative or absolute)"},"edits":{"type":"array","items":{"type":"object","required":["oldText","newText"],"properties":{"oldText":{"type":"string","description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call."},"newText":{"type":"string","description":"Replacement text for this targeted edit."}}},"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead."}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"write","description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","parameters":{"type":"object","required":["path","content"],"properties":{"path":{"type":"string","description":"Path to the file to write (relative or absolute)"},"content":{"type":"string","description":"Content to write to the file"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}}]}},{"type":"message","id":"70559bb2","parentId":"aca5528d","timestamp":"2026-01-01T00:00:00.357Z","message":{"role":"user","content":[{"type":"text","text":"Respond with exactly: rollback fixture first turn complete"}],"timestamp":1767225600354}},{"type":"message","id":"c906b4e2","parentId":"70559bb2","timestamp":"2026-01-01T00:00:02.034Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user is asking me to respond with exactly: \"rollback fixture first turn complete\"\n\nLet me check if there are any rollback-related fixtures or tests in the workspace first, as this seems like a test instruction.","thinkingSignature":"<signature>"},{"type":"text","text":"rollback fixture first turn complete"}],"api":"openai-completions","provider":"openrouter","model":"deepseek/deepseek-v4-flash","usage":{"input":1791,"output":52,"cacheRead":0,"cacheWrite":0,"reasoning":54,"totalTokens":1843,"cost":{"input":0.00007497126,"output":0.00000435344,"cacheRead":0,"cacheWrite":0,"total":0.0000793247}},"stopReason":"stop","timestamp":1767225600369,"responseId":"gen-1790898592-a7T4i6GUUneRyLxy0gtp","rawStopReason":"stop","thinkingLevel":"high"}}],"leafId":"c906b4e2"}}}52{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-7","type":"response","command":"get_entries","success":true,"data":{"entries":[{"type":"model_change","id":"cc15a6b4","parentId":"00c31b78","timestamp":"2026-01-01T00:00:00.344Z","provider":"openrouter","modelId":"deepseek/deepseek-v4-flash"},{"type":"session_info","id":"2c1dcf96","parentId":"cc15a6b4","timestamp":"2026-01-01T00:00:00.349Z","name":"Replay fixture: thread_rollback"},{"type":"message","id":"aca5528d","parentId":"2c1dcf96","timestamp":"2026-01-01T00:00:00.357Z","message":{"role":"system","content":"","sections":{"preamble":"<system-prompt-section>","tools":"<system-prompt-section>","rules":"<system-prompt-section>","docs":"<system-prompt-section>","cwd":"<system-prompt-section>"},"timestamp":1790898591831,"toolsAdded":[{"name":"read","description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","parameters":{"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"Path to the file to read (relative or absolute)"},"offset":{"type":"number","description":"Line number to start reading from (1-indexed)"},"limit":{"type":"number","description":"Maximum number of lines to read"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"bash","description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","parameters":{"type":"object","required":["command"],"properties":{"command":{"type":"string","description":"Shell command to execute"},"timeout":{"type":"number","description":"Timeout in seconds (optional, no default timeout)"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"edit","description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","parameters":{"type":"object","required":["path","edits"],"properties":{"path":{"type":"string","description":"Path to the file to edit (relative or absolute)"},"edits":{"type":"array","items":{"type":"object","required":["oldText","newText"],"properties":{"oldText":{"type":"string","description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call."},"newText":{"type":"string","description":"Replacement text for this targeted edit."}}},"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead."}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"write","description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","parameters":{"type":"object","required":["path","content"],"properties":{"path":{"type":"string","description":"Path to the file to write (relative or absolute)"},"content":{"type":"string","description":"Content to write to the file"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}}]}},{"type":"message","id":"70559bb2","parentId":"aca5528d","timestamp":"2026-01-01T00:00:00.357Z","message":{"role":"user","content":[{"type":"text","text":"Respond with exactly: rollback fixture first turn complete"}],"timestamp":1767225600354}},{"type":"message","id":"c906b4e2","parentId":"70559bb2","timestamp":"2026-01-01T00:00:02.034Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user is asking me to respond with exactly: \"rollback fixture first turn complete\"\n\nLet me check if there are any rollback-related fixtures or tests in the workspace first, as this seems like a test instruction.","thinkingSignature":"<signature>"},{"type":"text","text":"rollback fixture first turn complete"}],"api":"openai-completions","provider":"openrouter","model":"deepseek/deepseek-v4-flash","usage":{"input":1791,"output":52,"cacheRead":0,"cacheWrite":0,"reasoning":54,"totalTokens":1843,"cost":{"input":0.00007497126,"output":0.00000435344,"cacheRead":0,"cacheWrite":0,"total":0.0000793247}},"stopReason":"stop","timestamp":1767225600369,"responseId":"gen-1790898592-a7T4i6GUUneRyLxy0gtp","rawStopReason":"stop","thinkingLevel":"high"}}],"leafId":"c906b4e2"}}}
53{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-8"}}53{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-8"}}
54{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-8","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":1,"assistantMessages":1,"toolCalls":0,"toolResults":0,"totalMessages":3,"tokens":{"input":1791,"output":52,"cacheRead":0,"cacheWrite":0,"total":1843},"cost":0.0000793247,"contextUsage":{"tokens":1843,"contextWindow":1048576,"percent":0.17576217651367188}}}}54{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-8","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":1,"assistantMessages":1,"toolCalls":0,"toolResults":0,"totalMessages":3,"tokens":{"input":1791,"output":52,"cacheRead":0,"cacheWrite":0,"total":1843},"cost":0.0000793247,"contextUsage":{"tokens":1843,"contextWindow":1048576,"percent":0.17576217651367188}}}}
55{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: rollback fixture second turn complete"}}55{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: rollback fixture second turn complete","streamingBehavior":"steer"}}
56{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}56{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
57{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}57{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
58{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}58{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
...@@ -100,7 +100,7 @@...@@ -100,7 +100,7 @@
100{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: thread_rollback","id":"t3-19"}}100{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: thread_rollback","id":"t3-19"}}
101{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: thread_rollback"}}101{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: thread_rollback"}}
102{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-19","type":"response","command":"set_session_name","success":true}}102{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-19","type":"response","command":"set_session_name","success":true}}
103{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Repeat the conversation verbatim."}}103{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Repeat the conversation verbatim.","streamingBehavior":"steer"}}
104{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}104{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
105{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}105{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
106{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}106{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
apps/server/src/orchestration-v2/testkit/fixtures/thread_rollback_after_stop/pi_transcript.ndjson+4-4
...@@ -12,7 +12,7 @@...@@ -12,7 +12,7 @@
12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: thread_rollback_after_stop","id":"t3-5"}}12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: thread_rollback_after_stop","id":"t3-5"}}
13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: thread_rollback_after_stop"}}13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: thread_rollback_after_stop"}}
14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}
15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: rollback fixture first turn complete"}}15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Respond with exactly: rollback fixture first turn complete","streamingBehavior":"steer"}}
16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
...@@ -42,7 +42,7 @@...@@ -42,7 +42,7 @@
42{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-7","type":"response","command":"get_entries","success":true,"data":{"entries":[{"type":"model_change","id":"7aecf566","parentId":"10e2239d","timestamp":"2026-01-01T00:00:00.359Z","provider":"openrouter","modelId":"deepseek/deepseek-v4-flash"},{"type":"session_info","id":"5e1135b5","parentId":"7aecf566","timestamp":"2026-01-01T00:00:00.363Z","name":"Replay fixture: thread_rollback_after_stop"},{"type":"message","id":"66a686a0","parentId":"5e1135b5","timestamp":"2026-01-01T00:00:00.371Z","message":{"role":"system","content":"","sections":{"preamble":"<system-prompt-section>","tools":"<system-prompt-section>","rules":"<system-prompt-section>","docs":"<system-prompt-section>","cwd":"<system-prompt-section>"},"timestamp":1790898591965,"toolsAdded":[{"name":"read","description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","parameters":{"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"Path to the file to read (relative or absolute)"},"offset":{"type":"number","description":"Line number to start reading from (1-indexed)"},"limit":{"type":"number","description":"Maximum number of lines to read"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"bash","description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","parameters":{"type":"object","required":["command"],"properties":{"command":{"type":"string","description":"Shell command to execute"},"timeout":{"type":"number","description":"Timeout in seconds (optional, no default timeout)"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"edit","description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","parameters":{"type":"object","required":["path","edits"],"properties":{"path":{"type":"string","description":"Path to the file to edit (relative or absolute)"},"edits":{"type":"array","items":{"type":"object","required":["oldText","newText"],"properties":{"oldText":{"type":"string","description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call."},"newText":{"type":"string","description":"Replacement text for this targeted edit."}}},"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead."}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"write","description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","parameters":{"type":"object","required":["path","content"],"properties":{"path":{"type":"string","description":"Path to the file to write (relative or absolute)"},"content":{"type":"string","description":"Content to write to the file"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}}]}},{"type":"message","id":"0d296cbc","parentId":"66a686a0","timestamp":"2026-01-01T00:00:00.372Z","message":{"role":"user","content":[{"type":"text","text":"Respond with exactly: rollback fixture first turn complete"}],"timestamp":1767225600369}},{"type":"message","id":"b184b5a8","parentId":"0d296cbc","timestamp":"2026-01-01T00:00:01.622Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to respond with exactly \"rollback fixture first turn complete\".","thinkingSignature":"<signature>"},{"type":"text","text":"rollback fixture first turn complete"}],"api":"openai-completions","provider":"openrouter","model":"deepseek/deepseek-v4-flash","usage":{"input":1795,"output":24,"cacheRead":0,"cacheWrite":0,"reasoning":21,"totalTokens":1819,"cost":{"input":0.00007513869999999999,"output":0.00000200928,"cacheRead":0,"cacheWrite":0,"total":0.00007714798}},"stopReason":"stop","timestamp":1767225600386,"responseId":"gen-1790898592-vpyqfefcvhCaD41XnfZc","rawStopReason":"stop","thinkingLevel":"high"}}],"leafId":"b184b5a8"}}}42{"type":"emit_inbound","label":"response:get_entries","frame":{"id":"t3-7","type":"response","command":"get_entries","success":true,"data":{"entries":[{"type":"model_change","id":"7aecf566","parentId":"10e2239d","timestamp":"2026-01-01T00:00:00.359Z","provider":"openrouter","modelId":"deepseek/deepseek-v4-flash"},{"type":"session_info","id":"5e1135b5","parentId":"7aecf566","timestamp":"2026-01-01T00:00:00.363Z","name":"Replay fixture: thread_rollback_after_stop"},{"type":"message","id":"66a686a0","parentId":"5e1135b5","timestamp":"2026-01-01T00:00:00.371Z","message":{"role":"system","content":"","sections":{"preamble":"<system-prompt-section>","tools":"<system-prompt-section>","rules":"<system-prompt-section>","docs":"<system-prompt-section>","cwd":"<system-prompt-section>"},"timestamp":1790898591965,"toolsAdded":[{"name":"read","description":"Read the contents of a file. Supports text files and images (jpg, png, gif, webp, bmp). Images are sent as attachments. For text files, output is truncated to 2000 lines or 50KB (whichever is hit first). Use offset/limit for large files. When you need the full file, continue with offset until complete.","parameters":{"type":"object","required":["path"],"properties":{"path":{"type":"string","description":"Path to the file to read (relative or absolute)"},"offset":{"type":"number","description":"Line number to start reading from (1-indexed)"},"limit":{"type":"number","description":"Maximum number of lines to read"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"bash","description":"Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last 2000 lines or 50KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.","parameters":{"type":"object","required":["command"],"properties":{"command":{"type":"string","description":"Shell command to execute"},"timeout":{"type":"number","description":"Timeout in seconds (optional, no default timeout)"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"edit","description":"Edit a single file using exact text replacement. Every edits[].oldText must match a unique, non-overlapping region of the original file. If two changes affect the same block or nearby lines, merge them into one edit instead of emitting overlapping edits. Do not include large unchanged regions just to connect distant changes.","parameters":{"type":"object","required":["path","edits"],"properties":{"path":{"type":"string","description":"Path to the file to edit (relative or absolute)"},"edits":{"type":"array","items":{"type":"object","required":["oldText","newText"],"properties":{"oldText":{"type":"string","description":"Exact text for one targeted replacement. It must be unique in the original file and must not overlap with any other edits[].oldText in the same call."},"newText":{"type":"string","description":"Replacement text for this targeted edit."}}},"description":"One or more targeted replacements. Each edit is matched against the original file, not incrementally. Do not include overlapping or nested edits. If two changes touch the same block or nearby lines, merge them into one edit instead."}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}},{"name":"write","description":"Write content to a file. Creates the file if it doesn't exist, overwrites if it does. Automatically creates parent directories.","parameters":{"type":"object","required":["path","content"],"properties":{"path":{"type":"string","description":"Path to the file to write (relative or absolute)"},"content":{"type":"string","description":"Content to write to the file"}}},"constrainedSampling":{"type":"json_schema","strict":"prefer"}}]}},{"type":"message","id":"0d296cbc","parentId":"66a686a0","timestamp":"2026-01-01T00:00:00.372Z","message":{"role":"user","content":[{"type":"text","text":"Respond with exactly: rollback fixture first turn complete"}],"timestamp":1767225600369}},{"type":"message","id":"b184b5a8","parentId":"0d296cbc","timestamp":"2026-01-01T00:00:01.622Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to respond with exactly \"rollback fixture first turn complete\".","thinkingSignature":"<signature>"},{"type":"text","text":"rollback fixture first turn complete"}],"api":"openai-completions","provider":"openrouter","model":"deepseek/deepseek-v4-flash","usage":{"input":1795,"output":24,"cacheRead":0,"cacheWrite":0,"reasoning":21,"totalTokens":1819,"cost":{"input":0.00007513869999999999,"output":0.00000200928,"cacheRead":0,"cacheWrite":0,"total":0.00007714798}},"stopReason":"stop","timestamp":1767225600386,"responseId":"gen-1790898592-vpyqfefcvhCaD41XnfZc","rawStopReason":"stop","thinkingLevel":"high"}}],"leafId":"b184b5a8"}}}
43{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-8"}}43{"type":"expect_outbound","label":"get_session_stats","frame":{"type":"get_session_stats","id":"t3-8"}}
44{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-8","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":1,"assistantMessages":1,"toolCalls":0,"toolResults":0,"totalMessages":3,"tokens":{"input":1795,"output":24,"cacheRead":0,"cacheWrite":0,"total":1819},"cost":0.00007714798,"contextUsage":{"tokens":1819,"contextWindow":1048576,"percent":0.17347335815429688}}}}44{"type":"emit_inbound","label":"response:get_session_stats","frame":{"id":"t3-8","type":"response","command":"get_session_stats","success":true,"data":{"sessionFile":"/pi-sessions/session-1.jsonl","sessionId":"00000000-0000-4000-8000-000000000002","userMessages":1,"assistantMessages":1,"toolCalls":0,"toolResults":0,"totalMessages":3,"tokens":{"input":1795,"output":24,"cacheRead":0,"cacheWrite":0,"total":1819},"cost":0.00007714798,"contextUsage":{"tokens":1819,"contextWindow":1048576,"percent":0.17347335815429688}}}}
45{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Run this exact local command: `node -e \"console.log('interrupt fixture tool started'); setTimeout(() => {}, 30000)\"`. Do not answer until it completes, then respond exactly: interrupt fixture should not finish naturally."}}45{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Run this exact local command: `node -e \"console.log('interrupt fixture tool started'); setTimeout(() => {}, 30000)\"`. Do not answer until it completes, then respond exactly: interrupt fixture should not finish naturally.","streamingBehavior":"steer"}}
46{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}46{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
47{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}47{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
48{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}48{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
...@@ -164,7 +164,7 @@...@@ -164,7 +164,7 @@
164{"type":"expect_outbound","label":"set_session_name@p2","frame":{"type":"set_session_name","name":"Replay fixture: thread_rollback_after_stop","id":"t3-6"}}164{"type":"expect_outbound","label":"set_session_name@p2","frame":{"type":"set_session_name","name":"Replay fixture: thread_rollback_after_stop","id":"t3-6"}}
165{"type":"emit_inbound","label":"session_info_changed@p2","frame":{"type":"session_info_changed","name":"Replay fixture: thread_rollback_after_stop"}}165{"type":"emit_inbound","label":"session_info_changed@p2","frame":{"type":"session_info_changed","name":"Replay fixture: thread_rollback_after_stop"}}
166{"type":"emit_inbound","label":"response:set_session_name@p2","frame":{"id":"t3-6","type":"response","command":"set_session_name","success":true}}166{"type":"emit_inbound","label":"response:set_session_name@p2","frame":{"id":"t3-6","type":"response","command":"set_session_name","success":true}}
167{"type":"expect_outbound","label":"prompt@p2","frame":{"type":"prompt","message":"Respond with exactly: rollback fixture second turn complete"}}167{"type":"expect_outbound","label":"prompt@p2","frame":{"type":"prompt","message":"Respond with exactly: rollback fixture second turn complete","streamingBehavior":"steer"}}
168{"type":"emit_inbound","label":"response:prompt@p2","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}168{"type":"emit_inbound","label":"response:prompt@p2","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
169{"type":"emit_inbound","label":"agent_start@p2","frame":{"type":"agent_start"}}169{"type":"emit_inbound","label":"agent_start@p2","frame":{"type":"agent_start"}}
170{"type":"emit_inbound","label":"turn_start@p2","frame":{"type":"turn_start"}}170{"type":"emit_inbound","label":"turn_start@p2","frame":{"type":"turn_start"}}
...@@ -223,7 +223,7 @@...@@ -223,7 +223,7 @@
223{"type":"expect_outbound","label":"set_session_name@p2","frame":{"type":"set_session_name","name":"Replay fixture: thread_rollback_after_stop","id":"t3-17"}}223{"type":"expect_outbound","label":"set_session_name@p2","frame":{"type":"set_session_name","name":"Replay fixture: thread_rollback_after_stop","id":"t3-17"}}
224{"type":"emit_inbound","label":"session_info_changed@p2","frame":{"type":"session_info_changed","name":"Replay fixture: thread_rollback_after_stop"}}224{"type":"emit_inbound","label":"session_info_changed@p2","frame":{"type":"session_info_changed","name":"Replay fixture: thread_rollback_after_stop"}}
225{"type":"emit_inbound","label":"response:set_session_name@p2","frame":{"id":"t3-17","type":"response","command":"set_session_name","success":true}}225{"type":"emit_inbound","label":"response:set_session_name@p2","frame":{"id":"t3-17","type":"response","command":"set_session_name","success":true}}
226{"type":"expect_outbound","label":"prompt@p2","frame":{"type":"prompt","message":"Repeat the conversation verbatim."}}226{"type":"expect_outbound","label":"prompt@p2","frame":{"type":"prompt","message":"Repeat the conversation verbatim.","streamingBehavior":"steer"}}
227{"type":"emit_inbound","label":"response:prompt@p2","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}227{"type":"emit_inbound","label":"response:prompt@p2","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
228{"type":"emit_inbound","label":"agent_start@p2","frame":{"type":"agent_start"}}228{"type":"emit_inbound","label":"agent_start@p2","frame":{"type":"agent_start"}}
229{"type":"emit_inbound","label":"turn_start@p2","frame":{"type":"turn_start"}}229{"type":"emit_inbound","label":"turn_start@p2","frame":{"type":"turn_start"}}
apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_mid_tool/pi_transcript.ndjson+1-1
...@@ -12,7 +12,7 @@...@@ -12,7 +12,7 @@
12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: turn_interrupt_mid_tool","id":"t3-5"}}12{"type":"expect_outbound","label":"set_session_name","frame":{"type":"set_session_name","name":"Replay fixture: turn_interrupt_mid_tool","id":"t3-5"}}
13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: turn_interrupt_mid_tool"}}13{"type":"emit_inbound","label":"session_info_changed","frame":{"type":"session_info_changed","name":"Replay fixture: turn_interrupt_mid_tool"}}
14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}14{"type":"emit_inbound","label":"response:set_session_name","frame":{"id":"t3-5","type":"response","command":"set_session_name","success":true}}
15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Run this exact local command: `node -e \"console.log('interrupt fixture tool started'); setTimeout(() => {}, 30000)\"`. Do not answer until it completes, then respond exactly: interrupt fixture should not finish naturally."}}15{"type":"expect_outbound","label":"prompt","frame":{"type":"prompt","message":"Run this exact local command: `node -e \"console.log('interrupt fixture tool started'); setTimeout(() => {}, 30000)\"`. Do not answer until it completes, then respond exactly: interrupt fixture should not finish naturally.","streamingBehavior":"steer"}}
16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}16{"type":"emit_inbound","label":"response:prompt","frame":{"type":"response","command":"prompt","success":true,"data":{"disposition":"started"}}}
17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}17{"type":"emit_inbound","label":"agent_start","frame":{"type":"agent_start"}}
18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}18{"type":"emit_inbound","label":"turn_start","frame":{"type":"turn_start"}}
packages/provider-pi/src/server/adapter.test.ts+57-2
...@@ -90,6 +90,8 @@ interface FakePi {...@@ -90,6 +90,8 @@ interface FakePi {
90 readonly vetoNextSwitch: () => void;90 readonly vetoNextSwitch: () => void;
91 /** Fields overriding the recorded idle state in the next `get_state` acks, in order. */91 /** Fields overriding the recorded idle state in the next `get_state` acks, in order. */
92 readonly queueState: (data: Record<string, unknown>) => void;92 readonly queueState: (data: Record<string, unknown>) => void;
93 /** Change Pi's streaming state before its agent_start event reaches T3. */
94 readonly setStreaming: (streaming: boolean) => void;
93 /** Hold the next `get_state` response until the test resolves it. */95 /** Hold the next `get_state` response until the test resolves it. */
94 readonly deferNextState: () => void;96 readonly deferNextState: () => void;
95 /** Resolve the held `get_state` request. */97 /** Resolve the held `get_state` request. */
...@@ -154,6 +156,7 @@ const makeFakePi: Effect.Effect<FakePi> = Effect.gen(function* () {...@@ -154,6 +156,7 @@ const makeFakePi: Effect.Effect<FakePi> = Effect.gen(function* () {
154 let sessionFile = FAKE_SESSION_FILE;156 let sessionFile = FAKE_SESSION_FILE;
155 let sessionGeneration = 0;157 let sessionGeneration = 0;
156 let models: ReadonlyArray<unknown> = [];158 let models: ReadonlyArray<unknown> = [];
159 let isStreaming: boolean | null = null;
157 let stdinBuffer = "";160 let stdinBuffer = "";
158161
159 const emit = (record: PiRpcRecord, chunkBytes?: number) =>162 const emit = (record: PiRpcRecord, chunkBytes?: number) =>
...@@ -166,7 +169,8 @@ const makeFakePi: Effect.Effect<FakePi> = Effect.gen(function* () {...@@ -166,7 +169,8 @@ const makeFakePi: Effect.Effect<FakePi> = Effect.gen(function* () {
166 });169 });
167170
168 const respondTo = (record: PiRpcRecord): PiRpcRecord | null => {171 const respondTo = (record: PiRpcRecord): PiRpcRecord | null => {
169 if (typeof record["id"] !== "string") return null;172 if (typeof record["id"] !== "string" && !(record["type"] === "prompt" && isStreaming !== null))
173 return null;
170 const base = {174 const base = {
171 type: "response",175 type: "response",
172 id: record["id"],176 id: record["id"],
...@@ -181,7 +185,24 @@ const makeFakePi: Effect.Effect<FakePi> = Effect.gen(function* () {...@@ -181,7 +185,24 @@ const makeFakePi: Effect.Effect<FakePi> = Effect.gen(function* () {
181 }185 }
182 // Queued data overrides fields of the recorded idle state, so a test186 // Queued data overrides fields of the recorded idle state, so a test
183 // that only cares about the session file still gets a real shape.187 // that only cares about the session file still gets a real shape.
184 return { ...base, data: { ...recordedIdleState(sessionFile), ...stateQueue.shift() } };188 return {
189 ...base,
190 data: {
191 ...recordedIdleState(sessionFile),
192 isStreaming: isStreaming ?? false,
193 ...stateQueue.shift(),
194 },
195 };
196 case "prompt":
197 if (isStreaming && record["streamingBehavior"] === undefined) {
198 return {
199 ...base,
200 success: false,
201 error:
202 "Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.",
203 };
204 }
205 return base;
185 case "get_available_models":206 case "get_available_models":
186 return { ...base, data: { models } };207 return { ...base, data: { models } };
187 case "new_session": {208 case "new_session": {
...@@ -312,6 +333,9 @@ const makeFakePi: Effect.Effect<FakePi> = Effect.gen(function* () {...@@ -312,6 +333,9 @@ const makeFakePi: Effect.Effect<FakePi> = Effect.gen(function* () {
312 vetoSwitch = true;333 vetoSwitch = true;
313 },334 },
314 queueState: (data) => stateQueue.push(data),335 queueState: (data) => stateQueue.push(data),
336 setStreaming: (streaming) => {
337 isStreaming = streaming;
338 },
315 queueStats: (data) => statsQueue.push(data),339 queueStats: (data) => statsQueue.push(data),
316 closeStdout: Queue.end(stdout),340 closeStdout: Queue.end(stdout),
317 lastSpawn: () => lastSpawn,341 lastSpawn: () => lastSpawn,
...@@ -488,6 +512,37 @@ const expectModelFailure = (errorMessage: string) =>...@@ -488,6 +512,37 @@ const expectModelFailure = (errorMessage: string) =>
488 }).pipe(Effect.scoped, Effect.provide(layerTest));512 }).pipe(Effect.scoped, Effect.provide(layerTest));
489513
490describe("PiAdapterV2", () => {514describe("PiAdapterV2", () => {
515 it.effect.each([false, true])(
516 "completes a resumed user turn before native wake events arrive, Pi streaming=%s",
517 (streaming) =>
518 Effect.gen(function* () {
519 const fake = yield* makeFakePi;
520 const { runtime, takeEvent } = yield* openRuntime(fake);
521 const providerThread = yield* runtime.ensureThread({
522 threadId: THREAD_ID,
523 modelSelection: modelSelection("default"),
524 runtimePolicy,
525 });
526 fake.setStreaming(streaming);
527 yield* runtime.resumeThread({ providerThread });
528 yield* startTurn(runtime, providerThread);
529 yield* fake.takeRequest("prompt");
530 yield* fake.emit({ type: "agent_start" });
531 yield* fake.emit({
532 type: "message_end",
533 message: {
534 role: "assistant",
535 content: [{ type: "text", text: "Resumed prompt completed" }],
536 stopReason: "stop",
537 },
538 });
539 fake.queueState({ isStreaming: false });
540 yield* fake.emit({ type: "agent_settled" });
541 const terminal = yield* takeEvent((event) => event.type === "turn.terminal");
542 assert.isTrue(terminal.type === "turn.terminal" && terminal.status === "completed");
543 }).pipe(Effect.scoped, Effect.provide(layerTest)),
544 );
545
491 it.effect.each([false, true])(546 it.effect.each([false, true])(
492 "serializes rollback fork hooks with native wake ownership, wake=%s",547 "serializes rollback fork hooks with native wake ownership, wake=%s",
493 (nativeWake) =>548 (nativeWake) =>
packages/provider-pi/src/server/adapter.ts+3-1
...@@ -2697,10 +2697,12 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* (...@@ -2697,10 +2697,12 @@ export const makePiAdapterV2 = Effect.fn("makePiAdapterV2")(function* (
2697 kind: "turn_start",2697 kind: "turn_start",
2698 });2698 });
2699 } else if (payload !== null) {2699 } else if (payload !== null) {
2700 // Pi may already be streaming before its wake reaches T3.
2701 // Let Pi atomically queue the prompt or start an idle run.
2700 yield* connection.send({2702 yield* connection.send({
2701 type: "prompt",2703 type: "prompt",
2702 message: payload.message,2704 message: payload.message,
2703 ...(wake === null ? {} : { streamingBehavior: "steer" }),2705 streamingBehavior: "steer",
2704 ...(payload.images.length === 0 ? {} : { images: payload.images }),2706 ...(payload.images.length === 0 ? {} : { images: payload.images }),
2705 });2707 });
2706 pendingPromptResponses.push({2708 pendingPromptResponses.push({