|
|
@@ -103,40 +103,63 @@ export function createV2SessionReducer() {
|
|
|
time: { created: event.created },
|
|
|
})
|
|
|
case "session.shell.ended":
|
|
|
- return updateMessage<Shell>(source, (item): item is Shell => item.type === "shell" && item.shellID === event.data.shell.id, (item) => ({
|
|
|
- ...item,
|
|
|
- status: event.data.shell.status,
|
|
|
- exit: event.data.shell.exit,
|
|
|
- output: event.data.output,
|
|
|
- time: { ...item.time, completed: event.created },
|
|
|
- }), sessionID)
|
|
|
+ return updateMessage<Shell>(
|
|
|
+ source,
|
|
|
+ (item): item is Shell => item.type === "shell" && item.shellID === event.data.shell.id,
|
|
|
+ (item) => ({
|
|
|
+ ...item,
|
|
|
+ status: event.data.shell.status,
|
|
|
+ exit: event.data.shell.exit,
|
|
|
+ output: event.data.output,
|
|
|
+ time: { ...item.time, completed: event.created },
|
|
|
+ }),
|
|
|
+ sessionID,
|
|
|
+ )
|
|
|
case "session.step.started": {
|
|
|
const current = source.findLast((item): item is Assistant => item.type === "assistant" && !item.time.completed)
|
|
|
- const completed = current && current.id !== event.data.assistantMessageID
|
|
|
- ? update(source, current.id, (item) => item.type === "assistant" ? { ...item, retry: undefined, time: { ...item.time, completed: event.created } } : item)
|
|
|
- : [...source]
|
|
|
+ const completed =
|
|
|
+ current && current.id !== event.data.assistantMessageID
|
|
|
+ ? update(source, current.id, (item) =>
|
|
|
+ item.type === "assistant"
|
|
|
+ ? { ...item, retry: undefined, time: { ...item.time, completed: event.created } }
|
|
|
+ : item,
|
|
|
+ )
|
|
|
+ : [...source]
|
|
|
const existing = completed.find((item) => item.id === event.data.assistantMessageID)
|
|
|
if (existing?.type === "assistant")
|
|
|
- return result(update(completed, existing.id, (item) => item.type === "assistant" ? {
|
|
|
- ...item,
|
|
|
- agent: event.data.agent,
|
|
|
- model: event.data.model,
|
|
|
- retry: undefined,
|
|
|
- error: undefined,
|
|
|
- finish: undefined,
|
|
|
- snapshot: event.data.snapshot ? { ...item.snapshot, start: event.data.snapshot } : item.snapshot,
|
|
|
- time: { ...item.time, completed: undefined },
|
|
|
- } : item), current && current.id !== existing.id ? [current.id, existing.id] : [existing.id])
|
|
|
- return result([...completed, {
|
|
|
- id: event.data.assistantMessageID,
|
|
|
- type: "assistant",
|
|
|
- metadata: event.metadata,
|
|
|
- agent: event.data.agent,
|
|
|
- model: event.data.model,
|
|
|
- content: [],
|
|
|
- snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined,
|
|
|
- time: { created: event.created },
|
|
|
- }], current ? [current.id, event.data.assistantMessageID] : [event.data.assistantMessageID])
|
|
|
+ return result(
|
|
|
+ update(completed, existing.id, (item) =>
|
|
|
+ item.type === "assistant"
|
|
|
+ ? {
|
|
|
+ ...item,
|
|
|
+ agent: event.data.agent,
|
|
|
+ model: event.data.model,
|
|
|
+ retry: undefined,
|
|
|
+ error: undefined,
|
|
|
+ finish: undefined,
|
|
|
+ snapshot: event.data.snapshot ? { ...item.snapshot, start: event.data.snapshot } : item.snapshot,
|
|
|
+ time: { ...item.time, completed: undefined },
|
|
|
+ }
|
|
|
+ : item,
|
|
|
+ ),
|
|
|
+ current && current.id !== existing.id ? [current.id, existing.id] : [existing.id],
|
|
|
+ )
|
|
|
+ return result(
|
|
|
+ [
|
|
|
+ ...completed,
|
|
|
+ {
|
|
|
+ id: event.data.assistantMessageID,
|
|
|
+ type: "assistant",
|
|
|
+ metadata: event.metadata,
|
|
|
+ agent: event.data.agent,
|
|
|
+ model: event.data.model,
|
|
|
+ content: [],
|
|
|
+ snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined,
|
|
|
+ time: { created: event.created },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ current ? [current.id, event.data.assistantMessageID] : [event.data.assistantMessageID],
|
|
|
+ )
|
|
|
}
|
|
|
case "session.step.ended":
|
|
|
return updateAssistant(source, event.data.assistantMessageID, sessionID, (item) => ({
|
|
|
@@ -144,9 +167,10 @@ export function createV2SessionReducer() {
|
|
|
finish: event.data.finish,
|
|
|
cost: event.data.cost,
|
|
|
tokens: event.data.tokens,
|
|
|
- snapshot: event.data.snapshot || event.data.files
|
|
|
- ? { ...item.snapshot, end: event.data.snapshot, files: event.data.files }
|
|
|
- : item.snapshot,
|
|
|
+ snapshot:
|
|
|
+ event.data.snapshot || event.data.files
|
|
|
+ ? { ...item.snapshot, end: event.data.snapshot, files: event.data.files }
|
|
|
+ : item.snapshot,
|
|
|
time: { ...item.time, completed: event.created },
|
|
|
}))
|
|
|
case "session.step.failed":
|
|
|
@@ -157,9 +181,10 @@ export function createV2SessionReducer() {
|
|
|
retry: undefined,
|
|
|
cost: event.data.cost ?? item.cost,
|
|
|
tokens: event.data.tokens ?? item.tokens,
|
|
|
- snapshot: event.data.snapshot || event.data.files
|
|
|
- ? { ...item.snapshot, end: event.data.snapshot, files: event.data.files }
|
|
|
- : item.snapshot,
|
|
|
+ snapshot:
|
|
|
+ event.data.snapshot || event.data.files
|
|
|
+ ? { ...item.snapshot, end: event.data.snapshot, files: event.data.files }
|
|
|
+ : item.snapshot,
|
|
|
time: { ...item.time, completed: event.created },
|
|
|
}))
|
|
|
case "session.text.started":
|
|
|
@@ -188,29 +213,46 @@ export function createV2SessionReducer() {
|
|
|
}),
|
|
|
}))
|
|
|
case "session.reasoning.delta":
|
|
|
- return updateContent(source, event.data.assistantMessageID, sessionID, "reasoning", event.data.ordinal, (item) => ({
|
|
|
- ...item,
|
|
|
- text: item.text + event.data.delta,
|
|
|
- }))
|
|
|
+ return updateContent(
|
|
|
+ source,
|
|
|
+ event.data.assistantMessageID,
|
|
|
+ sessionID,
|
|
|
+ "reasoning",
|
|
|
+ event.data.ordinal,
|
|
|
+ (item) => ({
|
|
|
+ ...item,
|
|
|
+ text: item.text + event.data.delta,
|
|
|
+ }),
|
|
|
+ )
|
|
|
case "session.reasoning.ended":
|
|
|
- return updateContent(source, event.data.assistantMessageID, sessionID, "reasoning", event.data.ordinal, (item) => ({
|
|
|
- ...item,
|
|
|
- text: event.data.text,
|
|
|
- state: event.data.state ?? item.state,
|
|
|
- time: { created: item.time?.created ?? event.created, completed: event.created },
|
|
|
- }))
|
|
|
+ return updateContent(
|
|
|
+ source,
|
|
|
+ event.data.assistantMessageID,
|
|
|
+ sessionID,
|
|
|
+ "reasoning",
|
|
|
+ event.data.ordinal,
|
|
|
+ (item) => ({
|
|
|
+ ...item,
|
|
|
+ text: event.data.text,
|
|
|
+ state: event.data.state ?? item.state,
|
|
|
+ time: { created: item.time?.created ?? event.created, completed: event.created },
|
|
|
+ }),
|
|
|
+ )
|
|
|
case "session.tool.input.started":
|
|
|
return updateAssistant(source, event.data.assistantMessageID, sessionID, (item) => ({
|
|
|
...item,
|
|
|
content: item.content.some((content) => content.type === "tool" && content.id === event.data.callID)
|
|
|
? item.content
|
|
|
- : [...item.content, {
|
|
|
- type: "tool",
|
|
|
- id: event.data.callID,
|
|
|
- name: event.data.name,
|
|
|
- state: { status: "streaming", input: "" },
|
|
|
- time: { created: event.created },
|
|
|
- }],
|
|
|
+ : [
|
|
|
+ ...item.content,
|
|
|
+ {
|
|
|
+ type: "tool",
|
|
|
+ id: event.data.callID,
|
|
|
+ name: event.data.name,
|
|
|
+ state: { status: "streaming", input: "" },
|
|
|
+ time: { created: event.created },
|
|
|
+ },
|
|
|
+ ],
|
|
|
}))
|
|
|
case "session.tool.input.delta":
|
|
|
return updateTool(source, event.data.assistantMessageID, event.data.callID, sessionID, (tool) =>
|
|
|
@@ -295,12 +337,21 @@ export function createV2SessionReducer() {
|
|
|
time: { created: event.created },
|
|
|
})
|
|
|
case "session.compaction.delta":
|
|
|
- return updateMessage<Extract<Compaction, { status: "running" }>>(source, (item): item is Extract<Compaction, { status: "running" }> => item.type === "compaction" && item.status === "running", (item) => ({
|
|
|
- ...item,
|
|
|
- summary: item.summary + event.data.text,
|
|
|
- }), sessionID)
|
|
|
+ return updateMessage<Extract<Compaction, { status: "running" }>>(
|
|
|
+ source,
|
|
|
+ (item): item is Extract<Compaction, { status: "running" }> =>
|
|
|
+ item.type === "compaction" && item.status === "running",
|
|
|
+ (item) => ({
|
|
|
+ ...item,
|
|
|
+ summary: item.summary + event.data.text,
|
|
|
+ }),
|
|
|
+ sessionID,
|
|
|
+ )
|
|
|
case "session.compaction.ended": {
|
|
|
- const current = source.findLast((item): item is Extract<Compaction, { status: "running" }> => item.type === "compaction" && item.status === "running")
|
|
|
+ const current = source.findLast(
|
|
|
+ (item): item is Extract<Compaction, { status: "running" }> =>
|
|
|
+ item.type === "compaction" && item.status === "running",
|
|
|
+ )
|
|
|
if (!current)
|
|
|
return append({
|
|
|
id: messageID(event.id),
|
|
|
@@ -312,16 +363,22 @@ export function createV2SessionReducer() {
|
|
|
recent: event.data.recent,
|
|
|
time: { created: event.created },
|
|
|
})
|
|
|
- return result(update(source, current.id, () => ({
|
|
|
- ...current,
|
|
|
- status: "completed",
|
|
|
- reason: event.data.reason,
|
|
|
- summary: event.data.text,
|
|
|
- recent: event.data.recent,
|
|
|
- })), [current.id])
|
|
|
+ return result(
|
|
|
+ update(source, current.id, () => ({
|
|
|
+ ...current,
|
|
|
+ status: "completed",
|
|
|
+ reason: event.data.reason,
|
|
|
+ summary: event.data.text,
|
|
|
+ recent: event.data.recent,
|
|
|
+ })),
|
|
|
+ [current.id],
|
|
|
+ )
|
|
|
}
|
|
|
case "session.compaction.failed": {
|
|
|
- const current = source.findLast((item): item is Extract<Compaction, { status: "running" }> => item.type === "compaction" && item.status === "running")
|
|
|
+ const current = source.findLast(
|
|
|
+ (item): item is Extract<Compaction, { status: "running" }> =>
|
|
|
+ item.type === "compaction" && item.status === "running",
|
|
|
+ )
|
|
|
const failed: Extract<Compaction, { status: "failed" }> = {
|
|
|
id: current?.id ?? event.data.inputID ?? messageID(event.id),
|
|
|
type: "compaction",
|
|
|
@@ -332,7 +389,10 @@ export function createV2SessionReducer() {
|
|
|
time: current?.time ?? { created: event.created },
|
|
|
}
|
|
|
if (!current) return append(failed)
|
|
|
- return result(update(source, current.id, () => failed), [failed.id])
|
|
|
+ return result(
|
|
|
+ update(source, current.id, () => failed),
|
|
|
+ [failed.id],
|
|
|
+ )
|
|
|
}
|
|
|
default:
|
|
|
return
|
|
|
@@ -362,7 +422,7 @@ function update(
|
|
|
id: string,
|
|
|
apply: (item: SessionMessageInfo) => SessionMessageInfo,
|
|
|
) {
|
|
|
- return source.map((item) => item.id === id ? apply(item) : item)
|
|
|
+ return source.map((item) => (item.id === id ? apply(item) : item))
|
|
|
}
|
|
|
|
|
|
function updateMessage<T extends SessionMessageInfo>(
|
|
|
@@ -373,7 +433,11 @@ function updateMessage<T extends SessionMessageInfo>(
|
|
|
): V2SessionReduction {
|
|
|
const current = source.findLast(matches)
|
|
|
if (!current) return { sessionID, messages: [...source], touched: [] }
|
|
|
- return { sessionID, messages: update(source, current.id, (item) => matches(item) ? apply(item) : item), touched: [current.id] }
|
|
|
+ return {
|
|
|
+ sessionID,
|
|
|
+ messages: update(source, current.id, (item) => (matches(item) ? apply(item) : item)),
|
|
|
+ touched: [current.id],
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function updateAssistant(
|
|
|
@@ -384,7 +448,7 @@ function updateAssistant(
|
|
|
): V2SessionReduction {
|
|
|
return {
|
|
|
sessionID,
|
|
|
- messages: update(source, id, (item) => item.type === "assistant" ? apply(item) : item),
|
|
|
+ messages: update(source, id, (item) => (item.type === "assistant" ? apply(item) : item)),
|
|
|
touched: source.some((item) => item.id === id && item.type === "assistant") ? [id] : [],
|
|
|
}
|
|
|
}
|
|
|
@@ -395,7 +459,9 @@ function updateContent<T extends "text" | "reasoning">(
|
|
|
sessionID: string,
|
|
|
type: T,
|
|
|
ordinal: number,
|
|
|
- apply: (item: Extract<Assistant["content"][number], { type: T }>) => Extract<Assistant["content"][number], { type: T }>,
|
|
|
+ apply: (
|
|
|
+ item: Extract<Assistant["content"][number], { type: T }>,
|
|
|
+ ) => Extract<Assistant["content"][number], { type: T }>,
|
|
|
) {
|
|
|
return updateAssistant(source, messageID, sessionID, (assistant) => {
|
|
|
let index = -1
|
|
|
@@ -414,11 +480,13 @@ function updateTool(
|
|
|
messageID: string,
|
|
|
callID: string,
|
|
|
sessionID: string,
|
|
|
- apply: (item: Extract<Assistant["content"][number], { type: "tool" }>) => Extract<Assistant["content"][number], { type: "tool" }>,
|
|
|
+ apply: (
|
|
|
+ item: Extract<Assistant["content"][number], { type: "tool" }>,
|
|
|
+ ) => Extract<Assistant["content"][number], { type: "tool" }>,
|
|
|
) {
|
|
|
return updateAssistant(source, messageID, sessionID, (assistant) => ({
|
|
|
...assistant,
|
|
|
- content: assistant.content.map((item) => item.type === "tool" && item.id === callID ? apply(item) : item),
|
|
|
+ content: assistant.content.map((item) => (item.type === "tool" && item.id === callID ? apply(item) : item)),
|
|
|
}))
|
|
|
}
|
|
|
|