Threads: add Slack/Discord thread sessions

This commit is contained in:
Shadow
2026-01-07 09:02:20 -06:00
committed by Peter Steinberger
parent 422477499c
commit 7e5cef29a0
17 changed files with 670 additions and 27 deletions

View File

@@ -0,0 +1,82 @@
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
import type { ClawdbotConfig } from "../../config/config.js";
import { saveSessionStore } from "../../config/sessions.js";
import { initSessionState } from "./session.js";
describe("initSessionState thread forking", () => {
it("forks a new session from the parent session file", async () => {
const root = await fs.mkdtemp(
path.join(os.tmpdir(), "clawdbot-thread-session-"),
);
const sessionsDir = path.join(root, "sessions");
await fs.mkdir(sessionsDir, { recursive: true });
const parentSessionId = "parent-session";
const parentSessionFile = path.join(sessionsDir, "parent.jsonl");
const header = {
type: "session",
version: 3,
id: parentSessionId,
timestamp: new Date().toISOString(),
cwd: process.cwd(),
};
const message = {
type: "message",
id: "m1",
parentId: null,
timestamp: new Date().toISOString(),
message: { role: "user", content: "Parent prompt" },
};
await fs.writeFile(
parentSessionFile,
`${JSON.stringify(header)}\n${JSON.stringify(message)}\n`,
"utf-8",
);
const storePath = path.join(root, "sessions.json");
const parentSessionKey = "slack:channel:C1";
await saveSessionStore(storePath, {
[parentSessionKey]: {
sessionId: parentSessionId,
sessionFile: parentSessionFile,
updatedAt: Date.now(),
},
});
const cfg = {
session: { store: storePath },
} as ClawdbotConfig;
const threadSessionKey = "slack:thread:C1:123";
const threadLabel = "Slack thread #general: starter";
const result = await initSessionState({
ctx: {
Body: "Thread reply",
SessionKey: threadSessionKey,
ParentSessionKey: parentSessionKey,
ThreadLabel: threadLabel,
},
cfg,
commandAuthorized: true,
});
expect(result.sessionKey).toBe(threadSessionKey);
expect(result.sessionEntry.sessionId).not.toBe(parentSessionId);
expect(result.sessionEntry.sessionFile).toBeTruthy();
expect(result.sessionEntry.displayName).toBe(threadLabel);
const newSessionFile = result.sessionEntry.sessionFile!;
const [headerLine] = (await fs.readFile(newSessionFile, "utf-8"))
.split(/\r?\n/)
.filter((line) => line.trim().length > 0);
const parsedHeader = JSON.parse(headerLine) as {
parentSession?: string;
};
expect(parsedHeader.parentSession).toBe(parentSessionFile);
});
});