fix(session-manager): convert blocking sync I/O to async for improved concurrency

Convert session-manager storage layer from synchronous blocking I/O (readdirSync, readFileSync) to non-blocking async I/O (readdir, readFile from fs/promises). This fixes hanging issues in session_search and other tools caused by blocking filesystem operations.

Changes:
- storage.ts: getAllSessions, readSessionMessages, getSessionInfo now async
- utils.ts: Updated utility functions to be async-compatible
- tools.ts: Added await calls for async storage functions
- storage.test.ts, utils.test.ts: Updated tests with async/await patterns

This resolves the session_search tool hang issue and improves overall responsiveness.

🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
YeonGyu-Kim
2025-12-28 14:33:47 +09:00
parent 6d6102f1ff
commit 49f3be5a1f
5 changed files with 269 additions and 128 deletions
+66 -24
View File
@@ -1,21 +1,39 @@
import { describe, test, expect } from "bun:test"
import { formatSessionList, formatSessionMessages, formatSessionInfo, formatSearchResults, filterSessionsByDate, searchInSession } from "./utils"
import {
formatSessionList,
formatSessionMessages,
formatSessionInfo,
formatSearchResults,
filterSessionsByDate,
searchInSession,
} from "./utils"
import type { SessionInfo, SessionMessage, SearchResult } from "./types"
describe("session-manager utils", () => {
test("formatSessionList handles empty array", () => {
const result = formatSessionList([])
test("formatSessionList handles empty array", async () => {
// #given
const sessions: string[] = []
// #when
const result = await formatSessionList(sessions)
// #then
expect(result).toContain("No sessions found")
})
test("formatSessionMessages handles empty array", () => {
const result = formatSessionMessages([])
// #given
const messages: SessionMessage[] = []
// #when
const result = formatSessionMessages(messages)
// #then
expect(result).toContain("No messages")
})
test("formatSessionMessages includes message content", () => {
// #given
const messages: SessionMessage[] = [
{
id: "msg_001",
@@ -24,14 +42,17 @@ describe("session-manager utils", () => {
parts: [{ id: "prt_001", type: "text", text: "Hello world" }],
},
]
// #when
const result = formatSessionMessages(messages)
// #then
expect(result).toContain("user")
expect(result).toContain("Hello world")
})
test("formatSessionMessages includes todos when requested", () => {
// #given
const messages: SessionMessage[] = [
{
id: "msg_001",
@@ -40,20 +61,22 @@ describe("session-manager utils", () => {
parts: [{ id: "prt_001", type: "text", text: "Test" }],
},
]
const todos = [
{ id: "1", content: "Task 1", status: "completed" as const },
{ id: "2", content: "Task 2", status: "pending" as const },
]
// #when
const result = formatSessionMessages(messages, true, todos)
// #then
expect(result).toContain("Todos")
expect(result).toContain("Task 1")
expect(result).toContain("Task 2")
})
test("formatSessionInfo includes all metadata", () => {
// #given
const info: SessionInfo = {
id: "ses_test123",
message_count: 42,
@@ -65,9 +88,11 @@ describe("session-manager utils", () => {
todos: [{ id: "1", content: "Test", status: "pending" }],
transcript_entries: 123,
}
// #when
const result = formatSessionInfo(info)
// #then
expect(result).toContain("ses_test123")
expect(result).toContain("42")
expect(result).toContain("build, oracle")
@@ -75,12 +100,18 @@ describe("session-manager utils", () => {
})
test("formatSearchResults handles empty array", () => {
const result = formatSearchResults([])
// #given
const results: SearchResult[] = []
// #when
const result = formatSearchResults(results)
// #then
expect(result).toContain("No matches")
})
test("formatSearchResults formats matches correctly", () => {
// #given
const results: SearchResult[] = [
{
session_id: "ses_test123",
@@ -91,9 +122,11 @@ describe("session-manager utils", () => {
timestamp: Date.now(),
},
]
// #when
const result = formatSearchResults(results)
// #then
expect(result).toContain("Found 1 matches")
expect(result).toContain("ses_test123")
expect(result).toContain("msg_001")
@@ -101,17 +134,26 @@ describe("session-manager utils", () => {
expect(result).toContain("Matches: 3")
})
test("filterSessionsByDate filters correctly", () => {
test("filterSessionsByDate filters correctly", async () => {
// #given
const sessionIDs = ["ses_001", "ses_002", "ses_003"]
const result = filterSessionsByDate(sessionIDs)
// #when
const result = await filterSessionsByDate(sessionIDs)
// #then
expect(Array.isArray(result)).toBe(true)
})
test("searchInSession finds matches case-insensitively", () => {
const results = searchInSession("ses_nonexistent", "test", false)
test("searchInSession finds matches case-insensitively", async () => {
// #given
const sessionID = "ses_nonexistent"
const query = "test"
// #when
const results = await searchInSession(sessionID, query, false)
// #then
expect(Array.isArray(results)).toBe(true)
expect(results.length).toBe(0)
})