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

@@ -1,5 +1,11 @@
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import {
CURRENT_SESSION_VERSION,
SessionManager,
} from "@mariozechner/pi-coding-agent";
import type { ClawdbotConfig } from "../../config/config.js";
import {
buildGroupDisplayName,
@@ -9,6 +15,7 @@ import {
loadSessionStore,
resolveAgentIdFromSessionKey,
resolveGroupSessionKey,
resolveSessionFilePath,
resolveSessionKey,
resolveStorePath,
type SessionEntry,
@@ -36,6 +43,45 @@ export type SessionInitResult = {
triggerBodyNormalized: string;
};
function forkSessionFromParent(params: {
parentEntry: SessionEntry;
}): { sessionId: string; sessionFile: string } | null {
const parentSessionFile = resolveSessionFilePath(
params.parentEntry.sessionId,
params.parentEntry,
);
if (!parentSessionFile || !fs.existsSync(parentSessionFile)) return null;
try {
const manager = SessionManager.open(parentSessionFile);
const leafId = manager.getLeafId();
if (leafId) {
const sessionFile =
manager.createBranchedSession(leafId) ?? manager.getSessionFile();
const sessionId = manager.getSessionId();
if (sessionFile && sessionId) return { sessionId, sessionFile };
}
const sessionId = crypto.randomUUID();
const timestamp = new Date().toISOString();
const fileTimestamp = timestamp.replace(/[:.]/g, "-");
const sessionFile = path.join(
manager.getSessionDir(),
`${fileTimestamp}_${sessionId}.jsonl`,
);
const header = {
type: "session",
version: CURRENT_SESSION_VERSION,
id: sessionId,
timestamp,
cwd: manager.getCwd(),
parentSession: parentSessionFile,
};
fs.writeFileSync(sessionFile, `${JSON.stringify(header)}\n`, "utf-8");
return { sessionId, sessionFile };
} catch {
return null;
}
}
export async function initSessionState(params: {
ctx: MsgContext;
cfg: ClawdbotConfig;
@@ -189,6 +235,26 @@ export async function initSessionState(params: {
} else if (!sessionEntry.chatType) {
sessionEntry.chatType = "direct";
}
const threadLabel = ctx.ThreadLabel?.trim();
if (threadLabel) {
sessionEntry.displayName = threadLabel;
}
const parentSessionKey = ctx.ParentSessionKey?.trim();
if (
isNewSession &&
parentSessionKey &&
parentSessionKey !== sessionKey &&
sessionStore[parentSessionKey]
) {
const forked = forkSessionFromParent({
parentEntry: sessionStore[parentSessionKey],
});
if (forked) {
sessionId = forked.sessionId;
sessionEntry.sessionId = forked.sessionId;
sessionEntry.sessionFile = forked.sessionFile;
}
}
sessionStore[sessionKey] = sessionEntry;
await saveSessionStore(storePath, sessionStore);