chore: generate

This commit is contained in:
opencode-agent[bot]
2026-07-24 02:50:38 +00:00
parent 6faadb332c
commit a46c9aee07
6 changed files with 164 additions and 91 deletions
@@ -124,7 +124,6 @@ describe("bootstrapDirectory", () => {
expect(store.status).toBe("complete") expect(store.status).toBe("complete")
expect(mcpReads).toEqual([]) expect(mcpReads).toEqual([])
}) })
}) })
describe("query keys", () => { describe("query keys", () => {
@@ -1,5 +1,10 @@
import { describe, expect, test } from "bun:test" import { describe, expect, test } from "bun:test"
import type { AgentListOutput, ModelDefaultOutput, ModelListOutput, ProviderListOutput } from "@opencode-ai/client/promise" import type {
AgentListOutput,
ModelDefaultOutput,
ModelListOutput,
ProviderListOutput,
} from "@opencode-ai/client/promise"
import { directoryKey, normalizeAgentList, normalizePermissionRequest, normalizeProviderList } from "./utils" import { directoryKey, normalizeAgentList, normalizePermissionRequest, normalizeProviderList } from "./utils"
describe("normalizeAgentList", () => { describe("normalizeAgentList", () => {
@@ -25,7 +25,12 @@ describe("v2 session reducer", () => {
input: { type: "user", delivery: "steer", data: { text: "hello" } }, input: { type: "user", delivery: "steer", data: { text: "hello" } },
}, },
}) })
apply({ ...base, id: "evt_promoted", type: "session.input.promoted", data: { sessionID: "ses_1", inputID: "msg_user" } }) apply({
...base,
id: "evt_promoted",
type: "session.input.promoted",
data: { sessionID: "ses_1", inputID: "msg_user" },
})
apply({ apply({
...base, ...base,
id: "evt_step", id: "evt_step",
@@ -136,12 +141,15 @@ describe("v2 session reducer", () => {
}) })
test("requests hydration when promotion admission was missed", () => { test("requests hydration when promotion admission was missed", () => {
const result = createV2SessionReducer().reduce([], event({ const result = createV2SessionReducer().reduce(
...base, [],
id: "evt_promoted", event({
type: "session.input.promoted", ...base,
data: { sessionID: "ses_1", inputID: "msg_user" }, id: "evt_promoted",
})) type: "session.input.promoted",
data: { sessionID: "ses_1", inputID: "msg_user" },
}),
)
expect(result).toMatchObject({ sessionID: "ses_1", missing: "msg_user", touched: [] }) expect(result).toMatchObject({ sessionID: "ses_1", missing: "msg_user", touched: [] })
}) })
@@ -103,40 +103,63 @@ export function createV2SessionReducer() {
time: { created: event.created }, time: { created: event.created },
}) })
case "session.shell.ended": case "session.shell.ended":
return updateMessage<Shell>(source, (item): item is Shell => item.type === "shell" && item.shellID === event.data.shell.id, (item) => ({ return updateMessage<Shell>(
...item, source,
status: event.data.shell.status, (item): item is Shell => item.type === "shell" && item.shellID === event.data.shell.id,
exit: event.data.shell.exit, (item) => ({
output: event.data.output, ...item,
time: { ...item.time, completed: event.created }, status: event.data.shell.status,
}), sessionID) exit: event.data.shell.exit,
output: event.data.output,
time: { ...item.time, completed: event.created },
}),
sessionID,
)
case "session.step.started": { case "session.step.started": {
const current = source.findLast((item): item is Assistant => item.type === "assistant" && !item.time.completed) const current = source.findLast((item): item is Assistant => item.type === "assistant" && !item.time.completed)
const completed = current && current.id !== event.data.assistantMessageID const completed =
? update(source, current.id, (item) => item.type === "assistant" ? { ...item, retry: undefined, time: { ...item.time, completed: event.created } } : item) current && current.id !== event.data.assistantMessageID
: [...source] ? 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) const existing = completed.find((item) => item.id === event.data.assistantMessageID)
if (existing?.type === "assistant") if (existing?.type === "assistant")
return result(update(completed, existing.id, (item) => item.type === "assistant" ? { return result(
...item, update(completed, existing.id, (item) =>
agent: event.data.agent, item.type === "assistant"
model: event.data.model, ? {
retry: undefined, ...item,
error: undefined, agent: event.data.agent,
finish: undefined, model: event.data.model,
snapshot: event.data.snapshot ? { ...item.snapshot, start: event.data.snapshot } : item.snapshot, retry: undefined,
time: { ...item.time, completed: undefined }, error: undefined,
} : item), current && current.id !== existing.id ? [current.id, existing.id] : [existing.id]) finish: undefined,
return result([...completed, { snapshot: event.data.snapshot ? { ...item.snapshot, start: event.data.snapshot } : item.snapshot,
id: event.data.assistantMessageID, time: { ...item.time, completed: undefined },
type: "assistant", }
metadata: event.metadata, : item,
agent: event.data.agent, ),
model: event.data.model, current && current.id !== existing.id ? [current.id, existing.id] : [existing.id],
content: [], )
snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined, return result(
time: { created: event.created }, [
}], current ? [current.id, event.data.assistantMessageID] : [event.data.assistantMessageID]) ...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": case "session.step.ended":
return updateAssistant(source, event.data.assistantMessageID, sessionID, (item) => ({ return updateAssistant(source, event.data.assistantMessageID, sessionID, (item) => ({
@@ -144,9 +167,10 @@ export function createV2SessionReducer() {
finish: event.data.finish, finish: event.data.finish,
cost: event.data.cost, cost: event.data.cost,
tokens: event.data.tokens, tokens: event.data.tokens,
snapshot: event.data.snapshot || event.data.files snapshot:
? { ...item.snapshot, end: event.data.snapshot, files: event.data.files } event.data.snapshot || event.data.files
: item.snapshot, ? { ...item.snapshot, end: event.data.snapshot, files: event.data.files }
: item.snapshot,
time: { ...item.time, completed: event.created }, time: { ...item.time, completed: event.created },
})) }))
case "session.step.failed": case "session.step.failed":
@@ -157,9 +181,10 @@ export function createV2SessionReducer() {
retry: undefined, retry: undefined,
cost: event.data.cost ?? item.cost, cost: event.data.cost ?? item.cost,
tokens: event.data.tokens ?? item.tokens, tokens: event.data.tokens ?? item.tokens,
snapshot: event.data.snapshot || event.data.files snapshot:
? { ...item.snapshot, end: event.data.snapshot, files: event.data.files } event.data.snapshot || event.data.files
: item.snapshot, ? { ...item.snapshot, end: event.data.snapshot, files: event.data.files }
: item.snapshot,
time: { ...item.time, completed: event.created }, time: { ...item.time, completed: event.created },
})) }))
case "session.text.started": case "session.text.started":
@@ -188,29 +213,46 @@ export function createV2SessionReducer() {
}), }),
})) }))
case "session.reasoning.delta": case "session.reasoning.delta":
return updateContent(source, event.data.assistantMessageID, sessionID, "reasoning", event.data.ordinal, (item) => ({ return updateContent(
...item, source,
text: item.text + event.data.delta, event.data.assistantMessageID,
})) sessionID,
"reasoning",
event.data.ordinal,
(item) => ({
...item,
text: item.text + event.data.delta,
}),
)
case "session.reasoning.ended": case "session.reasoning.ended":
return updateContent(source, event.data.assistantMessageID, sessionID, "reasoning", event.data.ordinal, (item) => ({ return updateContent(
...item, source,
text: event.data.text, event.data.assistantMessageID,
state: event.data.state ?? item.state, sessionID,
time: { created: item.time?.created ?? event.created, completed: event.created }, "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": case "session.tool.input.started":
return updateAssistant(source, event.data.assistantMessageID, sessionID, (item) => ({ return updateAssistant(source, event.data.assistantMessageID, sessionID, (item) => ({
...item, ...item,
content: item.content.some((content) => content.type === "tool" && content.id === event.data.callID) content: item.content.some((content) => content.type === "tool" && content.id === event.data.callID)
? item.content ? item.content
: [...item.content, { : [
type: "tool", ...item.content,
id: event.data.callID, {
name: event.data.name, type: "tool",
state: { status: "streaming", input: "" }, id: event.data.callID,
time: { created: event.created }, name: event.data.name,
}], state: { status: "streaming", input: "" },
time: { created: event.created },
},
],
})) }))
case "session.tool.input.delta": case "session.tool.input.delta":
return updateTool(source, event.data.assistantMessageID, event.data.callID, sessionID, (tool) => return updateTool(source, event.data.assistantMessageID, event.data.callID, sessionID, (tool) =>
@@ -295,12 +337,21 @@ export function createV2SessionReducer() {
time: { created: event.created }, time: { created: event.created },
}) })
case "session.compaction.delta": 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) => ({ return updateMessage<Extract<Compaction, { status: "running" }>>(
...item, source,
summary: item.summary + event.data.text, (item): item is Extract<Compaction, { status: "running" }> =>
}), sessionID) item.type === "compaction" && item.status === "running",
(item) => ({
...item,
summary: item.summary + event.data.text,
}),
sessionID,
)
case "session.compaction.ended": { 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) if (!current)
return append({ return append({
id: messageID(event.id), id: messageID(event.id),
@@ -312,16 +363,22 @@ export function createV2SessionReducer() {
recent: event.data.recent, recent: event.data.recent,
time: { created: event.created }, time: { created: event.created },
}) })
return result(update(source, current.id, () => ({ return result(
...current, update(source, current.id, () => ({
status: "completed", ...current,
reason: event.data.reason, status: "completed",
summary: event.data.text, reason: event.data.reason,
recent: event.data.recent, summary: event.data.text,
})), [current.id]) recent: event.data.recent,
})),
[current.id],
)
} }
case "session.compaction.failed": { 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" }> = { const failed: Extract<Compaction, { status: "failed" }> = {
id: current?.id ?? event.data.inputID ?? messageID(event.id), id: current?.id ?? event.data.inputID ?? messageID(event.id),
type: "compaction", type: "compaction",
@@ -332,7 +389,10 @@ export function createV2SessionReducer() {
time: current?.time ?? { created: event.created }, time: current?.time ?? { created: event.created },
} }
if (!current) return append(failed) if (!current) return append(failed)
return result(update(source, current.id, () => failed), [failed.id]) return result(
update(source, current.id, () => failed),
[failed.id],
)
} }
default: default:
return return
@@ -362,7 +422,7 @@ function update(
id: string, id: string,
apply: (item: SessionMessageInfo) => SessionMessageInfo, 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>( function updateMessage<T extends SessionMessageInfo>(
@@ -373,7 +433,11 @@ function updateMessage<T extends SessionMessageInfo>(
): V2SessionReduction { ): V2SessionReduction {
const current = source.findLast(matches) const current = source.findLast(matches)
if (!current) return { sessionID, messages: [...source], touched: [] } 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( function updateAssistant(
@@ -384,7 +448,7 @@ function updateAssistant(
): V2SessionReduction { ): V2SessionReduction {
return { return {
sessionID, 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] : [], touched: source.some((item) => item.id === id && item.type === "assistant") ? [id] : [],
} }
} }
@@ -395,7 +459,9 @@ function updateContent<T extends "text" | "reasoning">(
sessionID: string, sessionID: string,
type: T, type: T,
ordinal: number, 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) => { return updateAssistant(source, messageID, sessionID, (assistant) => {
let index = -1 let index = -1
@@ -414,11 +480,13 @@ function updateTool(
messageID: string, messageID: string,
callID: string, callID: string,
sessionID: 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) => ({ return updateAssistant(source, messageID, sessionID, (assistant) => ({
...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)),
})) }))
} }
+1 -6
View File
@@ -11,12 +11,7 @@ import type {
import { QueryClient } from "@tanstack/solid-query" import { QueryClient } from "@tanstack/solid-query"
import { canDisposeDirectory, pickDirectoriesToEvict } from "./global-sync/eviction" import { canDisposeDirectory, pickDirectoriesToEvict } from "./global-sync/eviction"
import { estimateRootSessionTotal, loadRootSessions } from "./global-sync/session-load" import { estimateRootSessionTotal, loadRootSessions } from "./global-sync/session-load"
import { import { loadActiveSessionsQuery, loadMcpQuery, loadMcpResourcesQuery, seedActiveSessionStatuses } from "./server-sync"
loadActiveSessionsQuery,
loadMcpQuery,
loadMcpResourcesQuery,
seedActiveSessionStatuses,
} from "./server-sync"
import { ServerScope } from "@/utils/server-scope" import { ServerScope } from "@/utils/server-scope"
import { createServerSession } from "./server-session" import { createServerSession } from "./server-session"
@@ -122,9 +122,7 @@ describe("normalizeSessionMessages", () => {
expect.objectContaining({ id: "msg_shell", role: "user" }), expect.objectContaining({ id: "msg_shell", role: "user" }),
expect.objectContaining({ id: "msg_shell:assistant", role: "assistant", parentID: "msg_shell" }), expect.objectContaining({ id: "msg_shell:assistant", role: "assistant", parentID: "msg_shell" }),
]) ])
expect(result.parts.get("msg_shell")).toEqual([ expect(result.parts.get("msg_shell")).toEqual([expect.objectContaining({ type: "text", text: "printf hello" })])
expect.objectContaining({ type: "text", text: "printf hello" }),
])
expect(result.parts.get("msg_shell:assistant")).toEqual([ expect(result.parts.get("msg_shell:assistant")).toEqual([
expect.objectContaining({ expect.objectContaining({
type: "tool", type: "tool",