summaryrefslogtreecommitdiffhomepage
path: root/packages/cli/src/http.test.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-22 00:08:21 +0900
committerAdam Malczewski <[email protected]>2026-06-22 00:08:21 +0900
commit7ff9f94c41a9870e124a50133cd74b42295ab9ac (patch)
tree3a3f09d843dc3263983fa44b384ecc3c1a32e750 /packages/cli/src/http.test.ts
parent037c136823a900e28864e4dd48e1dbe626e95dfb (diff)
downloaddispatch-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.ts23
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",
},
],
});