fix: complete session persistence overhaul (Codex 5.2)
Some checks failed
Release Binaries / release (push) Has been cancelled

1. Implemented auto-selection of tasks in MultiXV2 to prevent empty initial state.
2. Added force-loading logic for task session messages with debouncing.
3. Updated session-actions to return full assistant text and immediately persist native messages.
4. Fixed caching logic in instance-shell2 to retain active task sessions in memory.
This commit is contained in:
Gemini AI
2025-12-27 20:36:43 +04:00
Unverified
parent 5022a23aeb
commit 1e991d9ebd
8 changed files with 235 additions and 20 deletions

View File

@@ -209,6 +209,51 @@ export function registerNativeSessionsRoutes(app: FastifyInstance, deps: NativeS
}
})
// Append messages to a session (client-side persistence)
app.post<{
Params: { workspaceId: string; sessionId: string }
Body: {
messages: Array<{
id?: string
role: "user" | "assistant" | "system" | "tool"
content?: string
createdAt?: number
updatedAt?: number
status?: "pending" | "streaming" | "completed" | "error"
}>
}
}>("/api/native/workspaces/:workspaceId/sessions/:sessionId/messages", async (request, reply) => {
const { workspaceId, sessionId } = request.params
const payload = request.body?.messages
if (!Array.isArray(payload)) {
reply.code(400)
return { error: "messages array is required" }
}
try {
const results: SessionMessage[] = []
for (const entry of payload) {
if (!entry || typeof entry.role !== "string") {
continue
}
const saved = await sessionManager.addMessage(workspaceId, sessionId, {
id: entry.id,
role: entry.role,
content: entry.content,
createdAt: entry.createdAt,
updatedAt: entry.updatedAt,
status: entry.status,
})
results.push(saved)
}
return { messages: results }
} catch (error) {
logger.error({ error }, "Failed to append messages")
reply.code(500)
return { error: "Failed to append messages" }
}
})
// Add a message (user prompt) and get streaming response
app.post<{
Params: { workspaceId: string; sessionId: string }

View File

@@ -27,6 +27,12 @@ export interface SessionMessage {
status?: "pending" | "streaming" | "completed" | "error"
}
type IncomingSessionMessage = Omit<SessionMessage, "id" | "sessionId" | "createdAt" | "updatedAt"> & {
id?: string
createdAt?: number
updatedAt?: number
}
export interface MessagePart {
type: "text" | "tool_call" | "tool_result" | "thinking" | "code"
content?: string
@@ -260,23 +266,29 @@ export class NativeSessionManager {
.filter((msg): msg is SessionMessage => msg !== undefined)
}
async addMessage(workspaceId: string, sessionId: string, message: Omit<SessionMessage, "id" | "sessionId" | "createdAt" | "updatedAt">): Promise<SessionMessage> {
async addMessage(workspaceId: string, sessionId: string, message: IncomingSessionMessage): Promise<SessionMessage> {
const store = await this.loadStore(workspaceId)
const session = store.sessions[sessionId]
if (!session) throw new Error(`Session not found: ${sessionId}`)
const now = Date.now()
const messageId = message.id ?? ulid()
const createdAt = typeof message.createdAt === "number" ? message.createdAt : now
const updatedAt = typeof message.updatedAt === "number" ? message.updatedAt : createdAt
const newMessage: SessionMessage = {
...message,
id: ulid(),
id: messageId,
sessionId,
createdAt: now,
updatedAt: now,
createdAt,
updatedAt,
}
store.messages[newMessage.id] = newMessage
session.messageIds.push(newMessage.id)
session.updatedAt = now
if (!session.messageIds.includes(newMessage.id)) {
session.messageIds.push(newMessage.id)
}
session.updatedAt = updatedAt
await this.saveStore(workspaceId)
return newMessage