diff options
| author | Adam Malczewski <[email protected]> | 2026-06-22 00:08:21 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-22 00:08:21 +0900 |
| commit | 7ff9f94c41a9870e124a50133cd74b42295ab9ac (patch) | |
| tree | 3a3f09d843dc3263983fa44b384ecc3c1a32e750 /packages/cli/src/http.test.ts | |
| parent | 037c136823a900e28864e4dd48e1dbe626e95dfb (diff) | |
| download | dispatch-7ff9f94c41a9870e124a50133cd74b42295ab9ac.tar.gz dispatch-7ff9f94c41a9870e124a50133cd74b42295ab9ac.zip | |
feat: conversation lifecycle status (active/idle/closed) for tab persistence
Implement roadmap item 9: tab persistence across devices.
Wire (0.10.0):
- Add ConversationStatus type (active | idle | closed)
- Add status field to ConversationMeta
Transport-contract (0.14.0):
- Add conversation.statusChanged WS message to WsServerMessage union
- Re-export ConversationStatus
Conversation-store:
- Track status in ConversationMetaRow (default: idle)
- getConversationStatus / setConversationStatus methods
- listConversations accepts { status: ConversationStatus[] } filter
- Old meta rows without status default to idle on read
Session-orchestrator:
- conversationStatusChanged hook descriptor
- Emit on transitions: idle→active (turn start), active→idle (turn settle),
→closed (closeConversation)
- Persist status to store as fire-and-forget side effect
- Declare hook in manifest contributes.hooks
Transport-ws:
- Subscribe to conversationStatusChanged hook
- Broadcast conversation.statusChanged WS message to all clients
Transport-http:
- GET /conversations?status=active,idle filter (parseStatusFilter pure helper)
- POST /conversations/:id/close now sets status to closed
CLI:
- dispatch list defaults to active,idle (excludes closed)
- --status <state> flag to filter by single status
- --all flag to include closed
FE handoff: frontend-conversation-lifecycle-handoff.md
Diffstat (limited to 'packages/cli/src/http.test.ts')
| -rw-r--r-- | packages/cli/src/http.test.ts | 23 |
1 files changed, 19 insertions, 4 deletions
diff --git a/packages/cli/src/http.test.ts b/packages/cli/src/http.test.ts index 013492e..3e7befe 100644 --- a/packages/cli/src/http.test.ts +++ b/packages/cli/src/http.test.ts @@ -251,7 +251,9 @@ describe("fetchConversations", () => { it("requests GET /conversations with no query when query omitted", async () => { let calledUrl: string | undefined; const list: ConversationListResponse = { - conversations: [{ id: "abcdef1234567890", title: "first", createdAt: 1, lastActivityAt: 2 }], + conversations: [ + { id: "abcdef1234567890", title: "first", createdAt: 1, lastActivityAt: 2, status: "idle" }, + ], }; const fakeFetch = (async (url: string | URL | Request): Promise<Response> => { calledUrl = String(url); @@ -277,7 +279,7 @@ describe("fetchConversations", () => { { fetchImpl: fakeFetch }, { server: "http://localhost:24203", query: "abc def" }, ); - expect(calledUrl).toBe("http://localhost:24203/conversations?q=abc%20def"); + expect(calledUrl).toBe("http://localhost:24203/conversations?q=abc+def"); }); it("throws on non-OK status", async () => { @@ -400,7 +402,13 @@ describe("resolveConversationId", () => { it("returns the full id on a single match", async () => { const fetchImpl = listFetch({ conversations: [ - { id: "abcdef1234567890abcdef1234567890", title: "only", createdAt: 1, lastActivityAt: 2 }, + { + id: "abcdef1234567890abcdef1234567890", + title: "only", + createdAt: 1, + lastActivityAt: 2, + status: "idle", + }, ], }); const result = await resolveConversationId( @@ -426,12 +434,19 @@ describe("resolveConversationId", () => { it("returns an error object listing matches on multiple matches", async () => { const fetchImpl = listFetch({ conversations: [ - { id: "abcdef1234567890aaaaaaaaaaaaaaaa", title: "first", createdAt: 1, lastActivityAt: 2 }, + { + id: "abcdef1234567890aaaaaaaaaaaaaaaa", + title: "first", + createdAt: 1, + lastActivityAt: 2, + status: "idle", + }, { id: "abcdef1234567890bbbbbbbbbbbbbbbb", title: "second", createdAt: 1, lastActivityAt: 3, + status: "idle", }, ], }); |
