test(session): cover legacy migration paths
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,31 +1,33 @@
|
||||
{"type":"session","version":0,"id":"4428b809-66d5-4ea2-9a03-89de742fcda1","createdAt":1785591986068,"cwd":"{{cwd}}","delegationDepth":0}
|
||||
{"type":"turn/start","seq":0,"time":1785591986072,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1785591986073,"data":{"content":[{"type":"text","text":"Call glob exactly once with pattern * and path tree. Then reply with exactly GLOB_SAMPLED and nothing else."}],"source":{"kind":"user"},"role":"user","id":"3d05fb76-4185-460b-9c6a-8c1b2495bc9f"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":2,"time":1785591986074,"data":{"title":"Call glob exactly once with","messageSeqs":[1],"source":{"kind":"fallback"}}}
|
||||
{"type":"agent/inbox/spliced","seq":0,"time":1785815911816,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Call glob exactly once with pattern * and path tree. Then reply with exactly GLOB_SAMPLED and nothing else."}],"source":{"kind":"user"},"role":"user","id":"b7f14c10-5c33-42b5-994e-60eed15a990e"}]}}
|
||||
{"type":"agent/inbox/spliced","seq":1,"time":1785815911817,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"turn/start","seq":2,"time":1785815911838,"data":{"turn":1}}
|
||||
{"type":"step/start","seq":3,"time":1785591986092,"data":{"turn":1,"step":1}}
|
||||
{"type":"request/header","seq":4,"time":1785591986093,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-pro"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"request/context","seq":5,"time":1785591986094,"data":{"provider":"deepseek-official","model":"deepseek-v4-pro"}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1785591987500,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"reasoning-chunks","seq0":7,"time0":1785591987500,"data":{"turn":1,"step":1,"index":0,"dt":[29,58,1,0,0,0,51,0,0,46,0,191,1,0,0,0,0,0,0,0,1,0,0,0,0,0,99],"texts":["The"," user"," wants"," me"," to"," call"," glob"," exactly"," once"," with"," pattern"," *"," and"," path"," tree",","," then"," reply"," with"," exactly"," \"","G","LOB","_S","AM","PL","ED","\"."]}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1785591988034,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"tool-call-chunks","seq0":36,"time0":1785591988035,"data":{"turn":1,"step":1,"index":1,"dt":[55,0,0,1,45,0,0,57,14,0,0,0,0,77,0,0,54],"id":"call_00_1cLZjkCW0vxVw0e3xVfh3430","name":"glob","args":["","{","\"","pattern","\"",": ","\"","*","\"",", ","\"","path","\"",": ","\"","tree","\"","}"]}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1785591988427,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to call glob exactly once with pattern * and path tree, then reply with exactly \"GLOB_SAMPLED\"."}}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1785591988427,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_1cLZjkCW0vxVw0e3xVfh3430","name":"glob","arguments":"{\"pattern\": \"*\", \"path\": \"tree\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1785591988427,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":1286,"outputTokens":87,"cacheReadTokens":0,"reasoningTokens":28}}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1785591988427,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":58,"time":1785591988430,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to call glob exactly once with pattern * and path tree, then reply with exactly \"GLOB_SAMPLED\"."},{"type":"tool-call","id":"call_00_1cLZjkCW0vxVw0e3xVfh3430","name":"glob","arguments":"{\"pattern\": \"*\", \"path\": \"tree\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-pro"},"id":"b74cbab2-c017-4e44-8c09-a7745d8b274a"},"usage":{"inputTokens":1286,"outputTokens":87,"cacheReadTokens":0,"reasoningTokens":28}},"sourceEventSeqs":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":59,"time":1785591988431,"data":{"turn":1,"step":1,"callId":"call_00_1cLZjkCW0vxVw0e3xVfh3430","name":"glob","arguments":"{\"pattern\": \"*\", \"path\": \"tree\"}"}}
|
||||
{"type":"tool/result","seq":60,"time":1785591988476,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_00_1cLZjkCW0vxVw0e3xVfh3430"},"content":[{"type":"tool-result","toolCallId":"call_00_1cLZjkCW0vxVw0e3xVfh3430","content":[{"type":"text","text":"tree/archive/a.ts\ntree/docs/guide.md\ntree/src/index.ts\ntree/test/spec.ts\n\n(Showing 4 of 8 paths, sampled across 4 of the 6 top-level entries this pattern matched instead of taken in modification-time order. Narrow path to inspect a specific subtree. The complete result could not be saved; narrow pattern or path to see more.)"}],"isError":false}],"role":"user","id":"10284f88-4890-49ed-9a17-56edbd6bfaa7"},"meta":{"shape":"paths","paths":["tree/archive/a.ts","tree/docs/guide.md","tree/src/index.ts","tree/test/spec.ts"],"truncated":true,"total":8}},"sourceEventSeqs":[59],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":61,"time":1785591988476,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":62,"time":1785591988482,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":63,"time":1785591989939,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"reasoning-chunks","seq0":64,"time0":1785591989939,"data":{"turn":1,"step":2,"index":0,"dt":[0,0,0,49,36,103,1,0,0,326,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,14],"texts":["The"," glob"," result"," shows"," it"," was"," sampled"," -"," ","4"," of"," ","8"," paths"," across"," ","4"," of"," ","6"," top","-level"," entries","."," I"," need"," to"," reply"," with"," exactly"," \"","G","LOB","_S","AM","PL","ED","\""," as"," instructed","."]}}
|
||||
{"type":"assistant/chunk","seq":105,"time":1785591990470,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"text-chunks","seq0":106,"time0":1785591990470,"data":{"turn":1,"step":2,"index":1,"dt":[0,0,0,48,0],"texts":["G","LOB","_S","AM","PL","ED"]}}
|
||||
{"type":"assistant/chunk","seq":112,"time":1785591990526,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The glob result shows it was sampled - 4 of 8 paths across 4 of 6 top-level entries. I need to reply with exactly \"GLOB_SAMPLED\" as instructed."}}}}
|
||||
{"type":"assistant/chunk","seq":113,"time":1785591990527,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"GLOB_SAMPLED"}}}}
|
||||
{"type":"assistant/chunk","seq":114,"time":1785591990527,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":188,"outputTokens":48,"cacheReadTokens":1280,"reasoningTokens":41}}}}
|
||||
{"type":"assistant/chunk","seq":115,"time":1785591990527,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":116,"time":1785591990527,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The glob result shows it was sampled - 4 of 8 paths across 4 of 6 top-level entries. I need to reply with exactly \"GLOB_SAMPLED\" as instructed."},{"type":"text","text":"GLOB_SAMPLED"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-pro"},"id":"dd3a9c28-43b2-4fdc-8089-1547309a71c0"},"usage":{"inputTokens":188,"outputTokens":48,"cacheReadTokens":1280,"reasoningTokens":41}},"sourceEventSeqs":[63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":117,"time":1785591990527,"data":{"turn":1,"step":2}}
|
||||
{"type":"turn/end","seq":118,"time":1785591990528,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
{"type":"user/message","seq":4,"time":1785815911838,"data":{"content":[{"type":"text","text":"Call glob exactly once with pattern * and path tree. Then reply with exactly GLOB_SAMPLED and nothing else."}],"source":{"kind":"user"},"role":"user","id":"b7f14c10-5c33-42b5-994e-60eed15a990e"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":5,"time":1785815911838,"data":{"title":"Call glob exactly once with","messageSeqs":[4],"source":{"kind":"fallback"}}}
|
||||
{"type":"request/header","seq":6,"time":1785815911840,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-pro"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"request/context","seq":7,"time":1785815911840,"data":{"provider":"deepseek-official","model":"deepseek-v4-pro"}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1785591987500,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"reasoning-chunks","seq0":9,"time0":1785591987529,"data":{"turn":1,"step":1,"index":0,"dt":[58,1,0,0,0,51,0,0,46,0,191,1,0,0,0,0,0,0,0,1,0,0,0,0,0,99,57],"texts":["The"," user"," wants"," me"," to"," call"," glob"," exactly"," once"," with"," pattern"," *"," and"," path"," tree",","," then"," reply"," with"," exactly"," \"","G","LOB","_S","AM","PL","ED","\"."]}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1785591988035,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"tool-call-chunks","seq0":38,"time0":1785591988090,"data":{"turn":1,"step":1,"index":1,"dt":[0,0,1,45,0,0,57,14,0,0,0,0,77,0,0,54,89],"id":"call_00_1cLZjkCW0vxVw0e3xVfh3430","name":"glob","args":["","{","\"","pattern","\"",": ","\"","*","\"",", ","\"","path","\"",": ","\"","tree","\"","}"]}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1785591988427,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to call glob exactly once with pattern * and path tree, then reply with exactly \"GLOB_SAMPLED\"."}}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1785591988427,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_1cLZjkCW0vxVw0e3xVfh3430","name":"glob","arguments":"{\"pattern\": \"*\", \"path\": \"tree\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1785591988427,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":1286,"outputTokens":87,"cacheReadTokens":0,"reasoningTokens":28}}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1785815911849,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":60,"time":1785815911849,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to call glob exactly once with pattern * and path tree, then reply with exactly \"GLOB_SAMPLED\"."},{"type":"tool-call","id":"call_00_1cLZjkCW0vxVw0e3xVfh3430","name":"glob","arguments":"{\"pattern\": \"*\", \"path\": \"tree\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-pro"},"id":"6aa63a52-8daf-41e1-84ff-6a991c1c65c2"},"usage":{"inputTokens":1286,"outputTokens":87,"cacheReadTokens":0,"reasoningTokens":28}},"sourceEventSeqs":[8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":61,"time":1785815911849,"data":{"turn":1,"step":1,"callId":"call_00_1cLZjkCW0vxVw0e3xVfh3430","name":"glob","arguments":"{\"pattern\": \"*\", \"path\": \"tree\"}"}}
|
||||
{"type":"tool/result","seq":62,"time":1785815911873,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_00_1cLZjkCW0vxVw0e3xVfh3430"},"content":[{"type":"tool-result","toolCallId":"call_00_1cLZjkCW0vxVw0e3xVfh3430","content":[{"type":"text","text":"tree/archive/a.ts\ntree/docs/guide.md\ntree/src/index.ts\ntree/test/spec.ts\n\n(Showing 4 of 8 paths, sampled across 4 of the 6 top-level entries this pattern matched instead of taken in modification-time order. Narrow path to inspect a specific subtree. The complete result could not be saved; narrow pattern or path to see more.)"}],"isError":false}],"role":"user","id":"1aa9a147-cf62-448c-a3a3-607afc166c8b"},"meta":{"shape":"paths","paths":["tree/archive/a.ts","tree/docs/guide.md","tree/src/index.ts","tree/test/spec.ts"],"truncated":true,"total":8}},"sourceEventSeqs":[61],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":63,"time":1785815911874,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":64,"time":1785815911886,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":65,"time":1785591989939,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"reasoning-chunks","seq0":66,"time0":1785591989939,"data":{"turn":1,"step":2,"index":0,"dt":[0,0,49,36,103,1,0,0,326,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,14,0],"texts":["The"," glob"," result"," shows"," it"," was"," sampled"," -"," ","4"," of"," ","8"," paths"," across"," ","4"," of"," ","6"," top","-level"," entries","."," I"," need"," to"," reply"," with"," exactly"," \"","G","LOB","_S","AM","PL","ED","\""," as"," instructed","."]}}
|
||||
{"type":"assistant/chunk","seq":107,"time":1785591990470,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"text-chunks","seq0":108,"time0":1785591990470,"data":{"turn":1,"step":2,"index":1,"dt":[0,0,48,0,8],"texts":["G","LOB","_S","AM","PL","ED"]}}
|
||||
{"type":"assistant/chunk","seq":114,"time":1785591990527,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The glob result shows it was sampled - 4 of 8 paths across 4 of 6 top-level entries. I need to reply with exactly \"GLOB_SAMPLED\" as instructed."}}}}
|
||||
{"type":"assistant/chunk","seq":115,"time":1785591990527,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"GLOB_SAMPLED"}}}}
|
||||
{"type":"assistant/chunk","seq":116,"time":1785591990527,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":188,"outputTokens":48,"cacheReadTokens":1280,"reasoningTokens":41}}}}
|
||||
{"type":"assistant/chunk","seq":117,"time":1785815911893,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":118,"time":1785815911894,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The glob result shows it was sampled - 4 of 8 paths across 4 of 6 top-level entries. I need to reply with exactly \"GLOB_SAMPLED\" as instructed."},{"type":"text","text":"GLOB_SAMPLED"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-pro"},"id":"2ceaed84-ac69-436e-82a2-f1bdacb8ca04"},"usage":{"inputTokens":188,"outputTokens":48,"cacheReadTokens":1280,"reasoningTokens":41}},"sourceEventSeqs":[65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":119,"time":1785815911894,"data":{"turn":1,"step":2}}
|
||||
{"type":"turn/end","seq":120,"time":1785815911894,"data":{"turn":1,"step":2,"reason":{"kind":"completed"}}}
|
||||
@@ -166,6 +166,35 @@ function preReactLoopLog(): SessionEvent[] {
|
||||
type: 'turn/end', seq: 17, time: 18,
|
||||
data: { turn: 5, reason: { kind: 'error', step: 1, message: 'old thrown value' } },
|
||||
},
|
||||
{
|
||||
type: 'turn/start', seq: 18, time: 19,
|
||||
data: { turn: 6, trigger: { kind: 'message', source: { kind: 'user' } } },
|
||||
},
|
||||
{
|
||||
type: 'turn/end', seq: 19, time: 20,
|
||||
data: {
|
||||
turn: 6,
|
||||
reason: {
|
||||
kind: 'error',
|
||||
step: 0,
|
||||
failure: {
|
||||
message: 'old detailed provider failure',
|
||||
code: 'RATE_LIMIT',
|
||||
status: 429,
|
||||
providerRetryAfterMs: 1000,
|
||||
requestId: 'request-1',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'turn/start', seq: 20, time: 21,
|
||||
data: { turn: 7, trigger: { kind: 'message', source: { kind: 'user' } } },
|
||||
},
|
||||
{
|
||||
type: 'turn/end', seq: 21, time: 22,
|
||||
data: { turn: 7, reason: { kind: 'error', step: 0, message: 'old coded error', code: 'CODED' } },
|
||||
},
|
||||
] as unknown as SessionEvent[]
|
||||
}
|
||||
|
||||
@@ -459,7 +488,9 @@ export function runCoordinatorContract(name: string, makeFixture: () => Promise<
|
||||
for (const snapshot of snapshots) {
|
||||
expect(snapshot.events.some(event => (event.type as string) === 'steering/message')).toBe(false)
|
||||
expect(snapshot.events.filter(event => event.type === 'turn/start').map(event => event.data))
|
||||
.toEqual([{ turn: 1 }, { turn: 2 }, { turn: 3 }, { turn: 4 }, { turn: 5 }])
|
||||
.toEqual([
|
||||
{ turn: 1 }, { turn: 2 }, { turn: 3 }, { turn: 4 }, { turn: 5 }, { turn: 6 }, { turn: 7 },
|
||||
])
|
||||
expect(snapshot.events.filter(event => event.type === 'turn/end').map(event => event.data)).toEqual([
|
||||
{ turn: 1, step: 1, reason: { kind: 'completed' } },
|
||||
{
|
||||
@@ -474,6 +505,25 @@ export function runCoordinatorContract(name: string, makeFixture: () => Promise<
|
||||
step: 1,
|
||||
reason: { kind: 'error', error: { message: 'old thrown value', code: 'UNKNOWN' } },
|
||||
},
|
||||
{
|
||||
turn: 6,
|
||||
step: 0,
|
||||
reason: {
|
||||
kind: 'error',
|
||||
error: {
|
||||
message: 'old detailed provider failure',
|
||||
code: 'RATE_LIMIT',
|
||||
status: 429,
|
||||
providerRetryAfterMs: 1000,
|
||||
requestId: 'request-1',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
turn: 7,
|
||||
step: 0,
|
||||
reason: { kind: 'error', error: { message: 'old coded error', code: 'CODED' } },
|
||||
},
|
||||
])
|
||||
|
||||
const resumed = new Session(id, snapshot.events, snapshot.meta)
|
||||
@@ -490,7 +540,29 @@ export function runCoordinatorContract(name: string, makeFixture: () => Promise<
|
||||
data: { id: legacySteering.data.message.id },
|
||||
})
|
||||
expect(suffix.events.filter(event => event.type === 'turn/end').map(event => event.data.step))
|
||||
.toEqual([1, 1, 0, 0, 1])
|
||||
.toEqual([1, 1, 0, 0, 1, 0, 0])
|
||||
|
||||
const flatId = SessionId('pre-react-loop-flat-steering')
|
||||
await ctx.sessionPersistence.create(meta(flatId, WORK))
|
||||
await ctx.sessionPersistence.append(flatId, [{
|
||||
type: 'steering/message',
|
||||
seq: 0,
|
||||
time: 1,
|
||||
data: {
|
||||
turn: 1,
|
||||
content: [{ type: 'text', text: 'flat steering' }],
|
||||
source: { kind: 'user' },
|
||||
},
|
||||
surfaceOp: 'append',
|
||||
} as unknown as SessionEvent])
|
||||
expect((await ctx.sessionPersistence.inspect(flatId)).events[0]).toMatchObject({
|
||||
type: 'user/message',
|
||||
data: {
|
||||
id: `legacy-message:${flatId}:0`,
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: 'flat steering' }],
|
||||
},
|
||||
})
|
||||
} finally {
|
||||
await fiber.dispose()
|
||||
await fix.cleanup()
|
||||
@@ -538,6 +610,13 @@ export function runCoordinatorContract(name: string, makeFixture: () => Promise<
|
||||
} as unknown as SessionEvent,
|
||||
message: 'malformed pre-react-loop steering/message',
|
||||
},
|
||||
{
|
||||
id: 'invalid-old-steering-data',
|
||||
event: {
|
||||
type: 'steering/message', seq: 0, time: 1, surfaceOp: 'append', data: null,
|
||||
} as unknown as SessionEvent,
|
||||
message: 'malformed pre-react-loop steering/message',
|
||||
},
|
||||
{
|
||||
id: 'invalid-old-turn-end',
|
||||
event: {
|
||||
@@ -546,6 +625,54 @@ export function runCoordinatorContract(name: string, makeFixture: () => Promise<
|
||||
} as unknown as SessionEvent,
|
||||
message: 'malformed pre-react-loop turn/end',
|
||||
},
|
||||
{
|
||||
id: 'invalid-old-turn-end-reason',
|
||||
event: {
|
||||
type: 'turn/end', seq: 0, time: 1,
|
||||
data: { turn: 1, reason: null },
|
||||
} as unknown as SessionEvent,
|
||||
message: 'malformed pre-react-loop turn/end',
|
||||
},
|
||||
{
|
||||
id: 'invalid-old-turn-end-aborted',
|
||||
event: {
|
||||
type: 'turn/end', seq: 0, time: 1,
|
||||
data: { turn: 1, reason: { kind: 'aborted', extra: true } },
|
||||
} as unknown as SessionEvent,
|
||||
message: 'malformed pre-react-loop turn/end',
|
||||
},
|
||||
{
|
||||
id: 'invalid-old-turn-end-disposed',
|
||||
event: {
|
||||
type: 'turn/end', seq: 0, time: 1,
|
||||
data: { turn: 1, reason: { kind: 'disposed', extra: true } },
|
||||
} as unknown as SessionEvent,
|
||||
message: 'malformed pre-react-loop turn/end',
|
||||
},
|
||||
{
|
||||
id: 'invalid-old-turn-end-error-step',
|
||||
event: {
|
||||
type: 'turn/end', seq: 0, time: 1,
|
||||
data: { turn: 1, reason: { kind: 'error', step: -1, message: 'bad step' } },
|
||||
} as unknown as SessionEvent,
|
||||
message: 'malformed pre-react-loop turn/end',
|
||||
},
|
||||
{
|
||||
id: 'invalid-old-turn-end-error-code',
|
||||
event: {
|
||||
type: 'turn/end', seq: 0, time: 1,
|
||||
data: { turn: 1, reason: { kind: 'error', step: 0, message: 'bad code', code: 1 } },
|
||||
} as unknown as SessionEvent,
|
||||
message: 'malformed pre-react-loop turn/end',
|
||||
},
|
||||
{
|
||||
id: 'invalid-old-turn-end-kind',
|
||||
event: {
|
||||
type: 'turn/end', seq: 0, time: 1,
|
||||
data: { turn: 1, reason: { kind: 'unknown' } },
|
||||
} as unknown as SessionEvent,
|
||||
message: 'malformed pre-react-loop turn/end',
|
||||
},
|
||||
]
|
||||
for (const malformed of malformedLegacy) {
|
||||
const malformedId = SessionId(malformed.id)
|
||||
@@ -579,6 +706,22 @@ export function runCoordinatorContract(name: string, makeFixture: () => Promise<
|
||||
} as unknown as SessionEvent])
|
||||
await expect(ctx.sessionPersistence.inspect(pluginId))
|
||||
.resolves.toMatchObject({ events: [{ type: 'plugin/test', data: null }] })
|
||||
await expect(ctx.sessionPersistence.readFrom(pluginId, 0))
|
||||
.resolves.toMatchObject({ events: [{ type: 'plugin/test', data: null }] })
|
||||
|
||||
for (const type of ['user/message', 'assistant/message'] as const) {
|
||||
const missingContentId = SessionId(`invalid-${type}-without-content`)
|
||||
await ctx.sessionPersistence.create(meta(missingContentId, WORK))
|
||||
await ctx.sessionPersistence.append(missingContentId, [{
|
||||
type,
|
||||
seq: 0,
|
||||
time: 1,
|
||||
surfaceOp: 'append',
|
||||
data: {},
|
||||
} as unknown as SessionEvent])
|
||||
await expect(ctx.sessionPersistence.readFrom(missingContentId, 0))
|
||||
.rejects.toThrow('lacks an identified message')
|
||||
}
|
||||
} finally {
|
||||
await fiber.dispose()
|
||||
await fix.cleanup()
|
||||
@@ -975,6 +1118,28 @@ export function runCoordinatorContract(name: string, makeFixture: () => Promise<
|
||||
}
|
||||
})
|
||||
|
||||
it('a live session whose complete seed matches loaded ownerless state claims it without appending', async () => {
|
||||
const fix = await makeFixture()
|
||||
const { ctx, fiber } = await freshCtx(fix)
|
||||
try {
|
||||
const id = SessionId('claim-exact')
|
||||
const completeSeed = [
|
||||
...oneTurnLog(),
|
||||
{ type: 'session/end-seed', seq: 6, time: 7, data: {} },
|
||||
] as SessionEvent[]
|
||||
await ctx.sessionPersistence.create(meta(id, WORK))
|
||||
await ctx.sessionPersistence.append(id, completeSeed)
|
||||
const { events } = await ctx.sessionPersistence.load(id)
|
||||
|
||||
const live = ctx.sessions.create(id, { seed: events, meta: { cwd: WORK } })
|
||||
await expect(ctx.sessions.flush(live)).resolves.toBe(true)
|
||||
expect((await ctx.sessionPersistence.load(id)).events).toEqual(events)
|
||||
} finally {
|
||||
await fiber.dispose()
|
||||
await fix.cleanup()
|
||||
}
|
||||
})
|
||||
|
||||
it('a live session whose seed matches the loaded prefix claims ownerless state and persists the suffix', async () => {
|
||||
const fix = await makeFixture()
|
||||
const { ctx, fiber } = await freshCtx(fix)
|
||||
|
||||
Reference in New Issue
Block a user