2026-03-07 15:39:25 +09:00
|
|
|
import { readFileSync, readdirSync } from "node:fs"
|
|
|
|
|
import { join } from "node:path"
|
2026-02-15 14:56:58 +09:00
|
|
|
|
2026-03-07 15:39:25 +09:00
|
|
|
import { getMessageDir, isSqliteBackend, normalizeSDKResponse } from "../../shared"
|
2026-02-15 14:56:58 +09:00
|
|
|
|
2026-03-07 15:39:25 +09:00
|
|
|
type SessionMessagesClient = {
|
|
|
|
|
session: {
|
|
|
|
|
messages: (input: { path: { id: string } }) => Promise<unknown>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isCompactionAgent(agent: unknown): boolean {
|
|
|
|
|
return typeof agent === "string" && agent.toLowerCase() === "compaction"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getLastAgentFromMessageDir(messageDir: string): string | null {
|
|
|
|
|
try {
|
2026-04-05 17:14:34 +09:00
|
|
|
const messages = readdirSync(messageDir)
|
2026-03-07 15:39:25 +09:00
|
|
|
.filter((fileName) => fileName.endsWith(".json"))
|
2026-04-05 17:14:34 +09:00
|
|
|
.map((fileName) => {
|
|
|
|
|
try {
|
|
|
|
|
const content = readFileSync(join(messageDir, fileName), "utf-8")
|
|
|
|
|
const parsed = JSON.parse(content) as { agent?: unknown; time?: { created?: unknown } }
|
|
|
|
|
return {
|
|
|
|
|
fileName,
|
|
|
|
|
agent: parsed.agent,
|
|
|
|
|
createdAt: typeof parsed.time?.created === "number" ? parsed.time.created : Number.NEGATIVE_INFINITY,
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
return null
|
2026-03-07 15:39:25 +09:00
|
|
|
}
|
2026-04-05 17:14:34 +09:00
|
|
|
})
|
|
|
|
|
.filter((message): message is { fileName: string; agent: unknown; createdAt: number } => message !== null)
|
|
|
|
|
.sort((left, right) => right.createdAt - left.createdAt || right.fileName.localeCompare(left.fileName))
|
|
|
|
|
|
|
|
|
|
for (const message of messages) {
|
|
|
|
|
if (typeof message.agent === "string" && !isCompactionAgent(message.agent)) {
|
|
|
|
|
return message.agent.toLowerCase()
|
2026-03-07 15:39:25 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
}
|
2026-02-15 14:56:58 +09:00
|
|
|
|
|
|
|
|
export async function getLastAgentFromSession(
|
|
|
|
|
sessionID: string,
|
2026-03-07 15:39:25 +09:00
|
|
|
client?: SessionMessagesClient
|
2026-02-15 14:56:58 +09:00
|
|
|
): Promise<string | null> {
|
|
|
|
|
if (isSqliteBackend() && client) {
|
2026-04-05 17:14:34 +09:00
|
|
|
try {
|
|
|
|
|
const response = await client.session.messages({ path: { id: sessionID } })
|
|
|
|
|
const messages = normalizeSDKResponse(response, [] as Array<{ id?: string; info?: { agent?: string; time?: { created?: number } } }>, {
|
|
|
|
|
preferResponseOnMissingData: true,
|
|
|
|
|
}).sort((left, right) => {
|
|
|
|
|
const leftTime = (left as { info?: { time?: { created?: number } } }).info?.time?.created ?? Number.NEGATIVE_INFINITY
|
|
|
|
|
const rightTime = (right as { info?: { time?: { created?: number } } }).info?.time?.created ?? Number.NEGATIVE_INFINITY
|
|
|
|
|
if (leftTime !== rightTime) {
|
|
|
|
|
return rightTime - leftTime
|
|
|
|
|
}
|
2026-03-07 15:39:25 +09:00
|
|
|
|
2026-04-05 17:14:34 +09:00
|
|
|
const leftId = typeof left.id === "string" ? left.id : ""
|
|
|
|
|
const rightId = typeof right.id === "string" ? right.id : ""
|
|
|
|
|
return rightId.localeCompare(leftId)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
for (const message of messages) {
|
|
|
|
|
const agent = message.info?.agent
|
|
|
|
|
if (typeof agent === "string" && !isCompactionAgent(agent)) {
|
|
|
|
|
return agent.toLowerCase()
|
|
|
|
|
}
|
2026-03-07 15:39:25 +09:00
|
|
|
}
|
2026-04-05 17:14:34 +09:00
|
|
|
} catch {
|
|
|
|
|
return null
|
2026-03-07 15:39:25 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null
|
2026-02-15 14:56:58 +09:00
|
|
|
}
|
2026-02-08 15:01:42 +09:00
|
|
|
|
2026-03-07 15:39:25 +09:00
|
|
|
const messageDir = getMessageDir(sessionID)
|
|
|
|
|
if (!messageDir) return null
|
|
|
|
|
|
|
|
|
|
return getLastAgentFromMessageDir(messageDir)
|
2026-02-08 15:01:42 +09:00
|
|
|
}
|