fix(core,tui): resolve stuck compacting status with terminal compaction events
This commit is contained in:
parent
674d08f9be
commit
4f4cb8cb36
10 changed files with 297 additions and 6 deletions
|
|
@ -3914,6 +3914,19 @@ export type EventSubscribeOutput =
|
||||||
readonly recent: string
|
readonly recent: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
readonly id: string
|
||||||
|
readonly metadata?: { readonly [x: string]: unknown }
|
||||||
|
readonly type: "session.next.compaction.failed"
|
||||||
|
readonly durable?: { readonly aggregateID: string; readonly seq: number; readonly version: number }
|
||||||
|
readonly location?: { readonly directory: string; readonly workspaceID?: string }
|
||||||
|
readonly data: {
|
||||||
|
readonly timestamp: number
|
||||||
|
readonly sessionID: string
|
||||||
|
readonly messageID: string
|
||||||
|
readonly reason: "auto" | "manual"
|
||||||
|
}
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
readonly id: string
|
readonly id: string
|
||||||
readonly metadata?: { readonly [x: string]: unknown }
|
readonly metadata?: { readonly [x: string]: unknown }
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,17 @@ const make = (dependencies: Dependencies) => {
|
||||||
Effect.catchTag("LLM.Error", () => Effect.succeed(false)),
|
Effect.catchTag("LLM.Error", () => Effect.succeed(false)),
|
||||||
)
|
)
|
||||||
const summary = chunks.join("")
|
const summary = chunks.join("")
|
||||||
if (!summarized || failed || !summary.trim()) return false
|
if (!summarized || failed || !summary.trim()) {
|
||||||
|
// Clients react to Started (for example by marking the session as running),
|
||||||
|
// so a failed summarization still needs a terminal event.
|
||||||
|
yield* dependencies.events.publish(SessionEvent.Compaction.Failed, {
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
messageID,
|
||||||
|
timestamp: yield* DateTime.now,
|
||||||
|
reason: input.reason,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
yield* dependencies.events.publish(SessionEvent.Compaction.Ended, {
|
yield* dependencies.events.publish(SessionEvent.Compaction.Ended, {
|
||||||
sessionID: input.sessionID,
|
sessionID: input.sessionID,
|
||||||
messageID,
|
messageID,
|
||||||
|
|
|
||||||
|
|
@ -389,6 +389,7 @@ export function update(adapter: Adapter, event: SessionEvent.Event) {
|
||||||
"session.next.retried": () => Effect.void,
|
"session.next.retried": () => Effect.void,
|
||||||
"session.next.compaction.started": () => Effect.void,
|
"session.next.compaction.started": () => Effect.void,
|
||||||
"session.next.compaction.delta": () => Effect.void,
|
"session.next.compaction.delta": () => Effect.void,
|
||||||
|
"session.next.compaction.failed": () => Effect.void,
|
||||||
"session.next.compaction.ended": (event) => {
|
"session.next.compaction.ended": (event) => {
|
||||||
return adapter.appendMessage(
|
return adapter.appendMessage(
|
||||||
SessionMessage.Compaction.make({
|
SessionMessage.Compaction.make({
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,12 @@ import { SessionV2 } from "@opencode-ai/core/session"
|
||||||
import { Project } from "@opencode-ai/core/project"
|
import { Project } from "@opencode-ai/core/project"
|
||||||
import { ProjectTable } from "@opencode-ai/core/project/sql"
|
import { ProjectTable } from "@opencode-ai/core/project/sql"
|
||||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||||
import { DateTime, Effect, Layer, Stream } from "effect"
|
import { DateTime, Effect, Fiber, Layer, Stream } from "effect"
|
||||||
import { asc, eq } from "drizzle-orm"
|
import { asc, eq } from "drizzle-orm"
|
||||||
import { testEffect } from "./lib/effect"
|
import { testEffect } from "./lib/effect"
|
||||||
|
|
||||||
let requests: LLMRequest[] = []
|
let requests: LLMRequest[] = []
|
||||||
|
let summaryText = "manual summary"
|
||||||
const model = Model.make({
|
const model = Model.make({
|
||||||
id: "summary-model",
|
id: "summary-model",
|
||||||
provider: "test",
|
provider: "test",
|
||||||
|
|
@ -33,7 +34,7 @@ const client = Layer.mock(LLMClient.Service)({
|
||||||
prepare: () => Effect.die("unused"),
|
prepare: () => Effect.die("unused"),
|
||||||
stream: (request: LLMRequest) => {
|
stream: (request: LLMRequest) => {
|
||||||
requests.push(request)
|
requests.push(request)
|
||||||
return Stream.make(LLMEvent.textDelta({ id: "summary", text: "manual summary" }))
|
return Stream.make(LLMEvent.textDelta({ id: "summary", text: summaryText }))
|
||||||
},
|
},
|
||||||
generate: () => Effect.die("unused"),
|
generate: () => Effect.die("unused"),
|
||||||
})
|
})
|
||||||
|
|
@ -66,9 +67,81 @@ test("compaction describes tool media without embedding base64", () => {
|
||||||
expect(serialized).not.toContain(base64)
|
expect(serialized).not.toContain(base64)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it.effect("manual compaction failure publishes a terminal failed event", () =>
|
||||||
|
Effect.scoped(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
requests = []
|
||||||
|
summaryText = ""
|
||||||
|
const db = (yield* Database.Service).db
|
||||||
|
const compaction = yield* SessionCompaction.Service
|
||||||
|
const store = yield* SessionStore.Service
|
||||||
|
const events = yield* EventV2.Service
|
||||||
|
const sessionID = SessionV2.ID.make("ses_failed_compaction")
|
||||||
|
yield* db
|
||||||
|
.insert(ProjectTable)
|
||||||
|
.values({ id: Project.ID.global, worktree: AbsolutePath.make("/project"), sandboxes: [] })
|
||||||
|
.onConflictDoNothing()
|
||||||
|
.run()
|
||||||
|
.pipe(Effect.orDie)
|
||||||
|
yield* db
|
||||||
|
.insert(SessionTable)
|
||||||
|
.values({
|
||||||
|
id: sessionID,
|
||||||
|
project_id: Project.ID.global,
|
||||||
|
slug: "failed-compaction",
|
||||||
|
directory: "/project",
|
||||||
|
title: "Failed compaction",
|
||||||
|
version: "test",
|
||||||
|
})
|
||||||
|
.run()
|
||||||
|
.pipe(Effect.orDie)
|
||||||
|
const failed = yield* events
|
||||||
|
.subscribe(SessionEvent.Compaction.Failed)
|
||||||
|
.pipe(Stream.take(1), Stream.runCollect, Effect.forkScoped)
|
||||||
|
yield* Effect.yieldNow
|
||||||
|
|
||||||
|
const session = yield* store
|
||||||
|
.get(sessionID)
|
||||||
|
.pipe(
|
||||||
|
Effect.flatMap((session) =>
|
||||||
|
session ? Effect.succeed(session) : Effect.die("failed compaction test session missing"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
yield* compaction.compactManual({
|
||||||
|
session,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
id: SessionMessage.ID.create(),
|
||||||
|
type: "user" as const,
|
||||||
|
text: "Summarization for this conversation will fail.",
|
||||||
|
time: { created: DateTime.makeUnsafe(0) },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
).toBe(false)
|
||||||
|
|
||||||
|
expect(Array.from(yield* Fiber.join(failed))[0]?.data).toMatchObject({ sessionID, reason: "manual" })
|
||||||
|
expect(yield* store.context(sessionID)).toEqual([])
|
||||||
|
// The failed event is live-only; only Started is durably recorded.
|
||||||
|
expect(
|
||||||
|
yield* db
|
||||||
|
.select({ type: EventTable.type })
|
||||||
|
.from(EventTable)
|
||||||
|
.where(eq(EventTable.aggregate_id, sessionID))
|
||||||
|
.orderBy(asc(EventTable.seq))
|
||||||
|
.all()
|
||||||
|
.pipe(Effect.orDie),
|
||||||
|
).toEqual([{ type: EventV2.versionedType(SessionEvent.Compaction.Started.type, 1) }])
|
||||||
|
summaryText = "manual summary"
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
it.effect("manual compaction summarizes short context instead of no-op", () =>
|
it.effect("manual compaction summarizes short context instead of no-op", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
requests = []
|
requests = []
|
||||||
|
summaryText = "manual summary"
|
||||||
const db = (yield* Database.Service).db
|
const db = (yield* Database.Service).db
|
||||||
const compaction = yield* SessionCompaction.Service
|
const compaction = yield* SessionCompaction.Service
|
||||||
const store = yield* SessionStore.Service
|
const store = yield* SessionStore.Service
|
||||||
|
|
|
||||||
|
|
@ -466,6 +466,18 @@ export namespace Compaction {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export type Ended = typeof Ended.Type
|
export type Ended = typeof Ended.Type
|
||||||
|
|
||||||
|
// Live-only: a failed compaction produces no durable message, but clients that
|
||||||
|
// reacted to Started need a terminal signal.
|
||||||
|
export const Failed = Event.define({
|
||||||
|
type: "session.next.compaction.failed",
|
||||||
|
schema: {
|
||||||
|
...Base,
|
||||||
|
messageID: SessionMessage.ID,
|
||||||
|
reason: Started.data.fields.reason,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export type Failed = typeof Failed.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace RevertEvent {
|
export namespace RevertEvent {
|
||||||
|
|
@ -549,6 +561,7 @@ export const Definitions = Event.inventory(
|
||||||
Compaction.Started,
|
Compaction.Started,
|
||||||
Compaction.Delta,
|
Compaction.Delta,
|
||||||
Compaction.Ended,
|
Compaction.Ended,
|
||||||
|
Compaction.Failed,
|
||||||
RevertEvent.Staged,
|
RevertEvent.Staged,
|
||||||
RevertEvent.Cleared,
|
RevertEvent.Cleared,
|
||||||
RevertEvent.Committed,
|
RevertEvent.Committed,
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ import { WorkspaceEvent } from "../src/workspace-event.js"
|
||||||
|
|
||||||
describe("public event manifest", () => {
|
describe("public event manifest", () => {
|
||||||
test("owns the complete public event surface", () => {
|
test("owns the complete public event surface", () => {
|
||||||
expect(EventManifest.ServerDefinitions.filter((definition) => definition.type !== "agent.updated").length).toBe(63)
|
expect(EventManifest.ServerDefinitions.filter((definition) => definition.type !== "agent.updated").length).toBe(66)
|
||||||
expect(EventManifest.ServerDefinitions.filter((definition) => definition.type === "agent.updated")).toEqual([
|
expect(EventManifest.ServerDefinitions.filter((definition) => definition.type === "agent.updated")).toEqual([
|
||||||
Agent.Event.Updated,
|
Agent.Event.Updated,
|
||||||
])
|
])
|
||||||
expect(EventManifest.Definitions.filter((definition) => definition.type !== "agent.updated").length).toBe(93)
|
expect(EventManifest.Definitions.filter((definition) => definition.type !== "agent.updated").length).toBe(97)
|
||||||
expect(EventManifest.Definitions.filter((definition) => definition.type === "agent.updated")).toEqual([
|
expect(EventManifest.Definitions.filter((definition) => definition.type === "agent.updated")).toEqual([
|
||||||
Agent.Event.Updated,
|
Agent.Event.Updated,
|
||||||
])
|
])
|
||||||
|
|
@ -29,7 +29,7 @@ describe("public event manifest", () => {
|
||||||
SessionV1.Event.Diff,
|
SessionV1.Event.Diff,
|
||||||
SessionV1.Event.Error,
|
SessionV1.Event.Error,
|
||||||
])
|
])
|
||||||
expect(Array.from(EventManifest.Latest.keys()).filter((type) => type !== "agent.updated").length).toBe(93)
|
expect(Array.from(EventManifest.Latest.keys()).filter((type) => type !== "agent.updated").length).toBe(97)
|
||||||
expect(EventManifest.Latest.get("agent.updated")).toBe(Agent.Event.Updated)
|
expect(EventManifest.Latest.get("agent.updated")).toBe(Agent.Event.Updated)
|
||||||
expect(Agent.Event.Updated.durable).toBeUndefined()
|
expect(Agent.Event.Updated.durable).toBeUndefined()
|
||||||
expect(EventManifest.Durable.has("agent.updated")).toBe(false)
|
expect(EventManifest.Durable.has("agent.updated")).toBe(false)
|
||||||
|
|
|
||||||
|
|
@ -399,6 +399,8 @@ import type {
|
||||||
V2SessionSwitchAgentResponses,
|
V2SessionSwitchAgentResponses,
|
||||||
V2SessionSwitchModelErrors,
|
V2SessionSwitchModelErrors,
|
||||||
V2SessionSwitchModelResponses,
|
V2SessionSwitchModelResponses,
|
||||||
|
V2SessionSyntheticErrors,
|
||||||
|
V2SessionSyntheticResponses,
|
||||||
V2SessionWaitErrors,
|
V2SessionWaitErrors,
|
||||||
V2SessionWaitResponses,
|
V2SessionWaitResponses,
|
||||||
V2ShellCreateErrors,
|
V2ShellCreateErrors,
|
||||||
|
|
@ -5816,6 +5818,47 @@ export class Session3 extends HeyApiClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add synthetic message
|
||||||
|
*
|
||||||
|
* Append a synthetic message to a session and resume execution.
|
||||||
|
*/
|
||||||
|
public synthetic<ThrowOnError extends boolean = false>(
|
||||||
|
parameters: {
|
||||||
|
sessionID: string
|
||||||
|
text?: string
|
||||||
|
description?: string
|
||||||
|
metadata?: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options?: Options<never, ThrowOnError>,
|
||||||
|
) {
|
||||||
|
const params = buildClientParams(
|
||||||
|
[parameters],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
args: [
|
||||||
|
{ in: "path", key: "sessionID" },
|
||||||
|
{ in: "body", key: "text" },
|
||||||
|
{ in: "body", key: "description" },
|
||||||
|
{ in: "body", key: "metadata" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
return (options?.client ?? this.client).post<V2SessionSyntheticResponses, V2SessionSyntheticErrors, ThrowOnError>({
|
||||||
|
url: "/api/session/{sessionID}/synthetic",
|
||||||
|
...options,
|
||||||
|
...params,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...options?.headers,
|
||||||
|
...params.headers,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compact session
|
* Compact session
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ export type Event =
|
||||||
| EventSessionNextCompactionStarted
|
| EventSessionNextCompactionStarted
|
||||||
| EventSessionNextCompactionDelta
|
| EventSessionNextCompactionDelta
|
||||||
| EventSessionNextCompactionEnded
|
| EventSessionNextCompactionEnded
|
||||||
|
| EventSessionNextCompactionFailed
|
||||||
| EventSessionNextRevertStaged
|
| EventSessionNextRevertStaged
|
||||||
| EventSessionNextRevertCleared
|
| EventSessionNextRevertCleared
|
||||||
| EventSessionNextRevertCommitted
|
| EventSessionNextRevertCommitted
|
||||||
|
|
@ -942,6 +943,9 @@ export type GlobalEvent = {
|
||||||
messageID: string
|
messageID: string
|
||||||
text: string
|
text: string
|
||||||
description?: string
|
description?: string
|
||||||
|
metadata?: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
|
|
@ -1229,6 +1233,16 @@ export type GlobalEvent = {
|
||||||
recent: string
|
recent: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
id: string
|
||||||
|
type: "session.next.compaction.failed"
|
||||||
|
properties: {
|
||||||
|
timestamp: number
|
||||||
|
sessionID: string
|
||||||
|
messageID: string
|
||||||
|
reason: "auto" | "manual"
|
||||||
|
}
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
id: string
|
id: string
|
||||||
type: "session.next.revert.staged"
|
type: "session.next.revert.staged"
|
||||||
|
|
@ -3032,6 +3046,7 @@ export type V2Event =
|
||||||
| SessionNextCompactionStarted
|
| SessionNextCompactionStarted
|
||||||
| SessionNextCompactionDelta
|
| SessionNextCompactionDelta
|
||||||
| SessionNextCompactionEnded
|
| SessionNextCompactionEnded
|
||||||
|
| SessionNextCompactionFailed
|
||||||
| SessionNextRevertStaged
|
| SessionNextRevertStaged
|
||||||
| SessionNextRevertCleared
|
| SessionNextRevertCleared
|
||||||
| SessionNextRevertCommitted
|
| SessionNextRevertCommitted
|
||||||
|
|
@ -3618,6 +3633,9 @@ export type SyncEventSessionNextSynthetic = {
|
||||||
messageID: string
|
messageID: string
|
||||||
text: string
|
text: string
|
||||||
description?: string
|
description?: string
|
||||||
|
metadata?: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4583,6 +4601,9 @@ export type SessionNextSynthetic = {
|
||||||
messageID: string
|
messageID: string
|
||||||
text: string
|
text: string
|
||||||
description?: string
|
description?: string
|
||||||
|
metadata?: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5664,6 +5685,26 @@ export type SessionNextCompactionDelta = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SessionNextCompactionFailed = {
|
||||||
|
id: string
|
||||||
|
metadata?: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
type: "session.next.compaction.failed"
|
||||||
|
durable?: {
|
||||||
|
aggregateID: string
|
||||||
|
seq: number
|
||||||
|
version: number
|
||||||
|
}
|
||||||
|
location?: LocationRef
|
||||||
|
data: {
|
||||||
|
timestamp: number
|
||||||
|
sessionID: string
|
||||||
|
messageID: string
|
||||||
|
reason: "auto" | "manual"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type MessagePartDelta = {
|
export type MessagePartDelta = {
|
||||||
id: string
|
id: string
|
||||||
metadata?: {
|
metadata?: {
|
||||||
|
|
@ -6806,6 +6847,9 @@ export type EventSessionNextSynthetic = {
|
||||||
messageID: string
|
messageID: string
|
||||||
text: string
|
text: string
|
||||||
description?: string
|
description?: string
|
||||||
|
metadata?: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7117,6 +7161,17 @@ export type EventSessionNextCompactionEnded = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EventSessionNextCompactionFailed = {
|
||||||
|
id: string
|
||||||
|
type: "session.next.compaction.failed"
|
||||||
|
properties: {
|
||||||
|
timestamp: number
|
||||||
|
sessionID: string
|
||||||
|
messageID: string
|
||||||
|
reason: "auto" | "manual"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type EventSessionNextRevertStaged = {
|
export type EventSessionNextRevertStaged = {
|
||||||
id: string
|
id: string
|
||||||
type: "session.next.revert.staged"
|
type: "session.next.revert.staged"
|
||||||
|
|
@ -12284,6 +12339,47 @@ export type V2SessionSkillResponses = {
|
||||||
|
|
||||||
export type V2SessionSkillResponse = V2SessionSkillResponses[keyof V2SessionSkillResponses]
|
export type V2SessionSkillResponse = V2SessionSkillResponses[keyof V2SessionSkillResponses]
|
||||||
|
|
||||||
|
export type V2SessionSyntheticData = {
|
||||||
|
body: {
|
||||||
|
text: string
|
||||||
|
description?: string
|
||||||
|
metadata?: {
|
||||||
|
[key: string]: unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path: {
|
||||||
|
sessionID: string
|
||||||
|
}
|
||||||
|
query?: never
|
||||||
|
url: "/api/session/{sessionID}/synthetic"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionSyntheticErrors = {
|
||||||
|
/**
|
||||||
|
* InvalidRequestError
|
||||||
|
*/
|
||||||
|
400: InvalidRequestError
|
||||||
|
/**
|
||||||
|
* UnauthorizedError
|
||||||
|
*/
|
||||||
|
401: UnauthorizedError
|
||||||
|
/**
|
||||||
|
* SessionNotFoundError
|
||||||
|
*/
|
||||||
|
404: SessionNotFoundError
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionSyntheticError = V2SessionSyntheticErrors[keyof V2SessionSyntheticErrors]
|
||||||
|
|
||||||
|
export type V2SessionSyntheticResponses = {
|
||||||
|
/**
|
||||||
|
* <No Content>
|
||||||
|
*/
|
||||||
|
204: void
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V2SessionSyntheticResponse = V2SessionSyntheticResponses[keyof V2SessionSyntheticResponses]
|
||||||
|
|
||||||
export type V2SessionCompactData = {
|
export type V2SessionCompactData = {
|
||||||
body?: never
|
body?: never
|
||||||
path: {
|
path: {
|
||||||
|
|
|
||||||
|
|
@ -535,7 +535,14 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
|
||||||
break
|
break
|
||||||
case "session.next.compaction.delta":
|
case "session.next.compaction.delta":
|
||||||
break
|
break
|
||||||
|
case "session.next.compaction.failed":
|
||||||
|
// Started marked the session running; without this a failed compaction
|
||||||
|
// leaves the session spinning forever. Mid-run auto compaction is
|
||||||
|
// followed by step events that set the status again.
|
||||||
|
setStore("session", "status", event.data.sessionID, "idle")
|
||||||
|
break
|
||||||
case "session.next.compaction.ended":
|
case "session.next.compaction.ended":
|
||||||
|
setStore("session", "status", event.data.sessionID, "idle")
|
||||||
message.update(event.data.sessionID, (draft, index) => {
|
message.update(event.data.sessionID, (draft, index) => {
|
||||||
message.append(draft, index, {
|
message.append(draft, index, {
|
||||||
id: event.data.messageID,
|
id: event.data.messageID,
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,41 @@ test("tracks session status from active sessions and execution events", async ()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await wait(() => data.session.status("session-failed") === "idle")
|
await wait(() => data.session.status("session-failed") === "idle")
|
||||||
|
|
||||||
|
emitEvent(events, {
|
||||||
|
id: "evt_compaction_started",
|
||||||
|
type: "session.next.compaction.started",
|
||||||
|
data: { sessionID: "session-compacting", messageID: "message-compaction", timestamp: 5, reason: "manual" },
|
||||||
|
})
|
||||||
|
await wait(() => data.session.status("session-compacting") === "running")
|
||||||
|
|
||||||
|
emitEvent(events, {
|
||||||
|
id: "evt_compaction_ended",
|
||||||
|
type: "session.next.compaction.ended",
|
||||||
|
data: {
|
||||||
|
sessionID: "session-compacting",
|
||||||
|
messageID: "message-compaction",
|
||||||
|
timestamp: 6,
|
||||||
|
reason: "manual",
|
||||||
|
text: "summary",
|
||||||
|
recent: "",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await wait(() => data.session.status("session-compacting") === "idle")
|
||||||
|
|
||||||
|
emitEvent(events, {
|
||||||
|
id: "evt_compaction_started_again",
|
||||||
|
type: "session.next.compaction.started",
|
||||||
|
data: { sessionID: "session-compacting", messageID: "message-compaction-2", timestamp: 7, reason: "manual" },
|
||||||
|
})
|
||||||
|
await wait(() => data.session.status("session-compacting") === "running")
|
||||||
|
|
||||||
|
emitEvent(events, {
|
||||||
|
id: "evt_compaction_failed",
|
||||||
|
type: "session.next.compaction.failed",
|
||||||
|
data: { sessionID: "session-compacting", messageID: "message-compaction-2", timestamp: 8, reason: "manual" },
|
||||||
|
})
|
||||||
|
await wait(() => data.session.status("session-compacting") === "idle")
|
||||||
} finally {
|
} finally {
|
||||||
app.renderer.destroy()
|
app.renderer.destroy()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue