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

@@ -707,6 +707,7 @@ export function createBridgeHandlers(ctx: BridgeHandlersContext) {
for (const candidate of resolveSessionTranscriptCandidates(
sessionId,
storePath,
entry?.sessionFile,
)) {
if (!fs.existsSync(candidate)) continue;
try {
@@ -773,6 +774,7 @@ export function createBridgeHandlers(ctx: BridgeHandlersContext) {
const filePath = resolveSessionTranscriptCandidates(
sessionId,
storePath,
entry?.sessionFile,
).find((candidate) => fs.existsSync(candidate));
if (!filePath) {
return {
@@ -843,7 +845,7 @@ export function createBridgeHandlers(ctx: BridgeHandlersContext) {
const sessionId = entry?.sessionId;
const rawMessages =
sessionId && storePath
? readSessionMessages(sessionId, storePath)
? readSessionMessages(sessionId, storePath, entry?.sessionFile)
: [];
const max = typeof limit === "number" ? limit : 200;
const sliced =

View File

@@ -46,7 +46,9 @@ export const chatHandlers: GatewayRequestHandlers = {
const { cfg, storePath, entry } = loadSessionEntry(sessionKey);
const sessionId = entry?.sessionId;
const rawMessages =
sessionId && storePath ? readSessionMessages(sessionId, storePath) : [];
sessionId && storePath
? readSessionMessages(sessionId, storePath, entry?.sessionFile)
: [];
const hardMax = 1000;
const defaultLimit = 200;
const requested = typeof limit === "number" ? limit : defaultLimit;

View File

@@ -485,6 +485,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
for (const candidate of resolveSessionTranscriptCandidates(
sessionId,
storePath,
entry?.sessionFile,
target.agentId,
)) {
if (!fs.existsSync(candidate)) continue;
@@ -559,6 +560,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
const filePath = resolveSessionTranscriptCandidates(
sessionId,
storePath,
entry?.sessionFile,
target.agentId,
).find((candidate) => fs.existsSync(candidate));
if (!filePath) {

View File

@@ -327,6 +327,67 @@ describe("gateway server chat", () => {
await server.close();
});
test("chat.history prefers sessionFile when set", async () => {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-gw-"));
testState.sessionStorePath = path.join(dir, "sessions.json");
const forkedPath = path.join(dir, "sess-forked.jsonl");
await fs.writeFile(
forkedPath,
JSON.stringify({
message: {
role: "user",
content: [{ type: "text", text: "from-fork" }],
timestamp: Date.now(),
},
}),
"utf-8",
);
await fs.writeFile(
path.join(dir, "sess-main.jsonl"),
JSON.stringify({
message: {
role: "user",
content: [{ type: "text", text: "from-default" }],
timestamp: Date.now(),
},
}),
"utf-8",
);
await fs.writeFile(
testState.sessionStorePath,
JSON.stringify(
{
main: {
sessionId: "sess-main",
sessionFile: forkedPath,
updatedAt: Date.now(),
},
},
null,
2,
),
"utf-8",
);
const { server, ws } = await startServerWithClient();
await connectOk(ws);
const res = await rpcReq<{ messages?: unknown[] }>(ws, "chat.history", {
sessionKey: "main",
});
expect(res.ok).toBe(true);
const messages = res.payload?.messages ?? [];
expect(messages.length).toBe(1);
const first = messages[0] as { content?: { text?: string }[] };
expect(first.content?.[0]?.text).toBe("from-fork");
ws.close();
await server.close();
});
test("chat.history defaults thinking to low for reasoning-capable models", async () => {
piSdkMock.enabled = true;
piSdkMock.models = [

View File

@@ -74,8 +74,13 @@ export type SessionsPatchResult = {
export function readSessionMessages(
sessionId: string,
storePath: string | undefined,
sessionFile?: string,
): unknown[] {
const candidates = resolveSessionTranscriptCandidates(sessionId, storePath);
const candidates = resolveSessionTranscriptCandidates(
sessionId,
storePath,
sessionFile,
);
const filePath = candidates.find((p) => fs.existsSync(p));
if (!filePath) return [];
@@ -99,9 +104,11 @@ export function readSessionMessages(
export function resolveSessionTranscriptCandidates(
sessionId: string,
storePath: string | undefined,
sessionFile?: string,
agentId?: string,
): string[] {
const candidates: string[] = [];
if (sessionFile) candidates.push(sessionFile);
if (storePath) {
const dir = path.dirname(storePath);
candidates.push(path.join(dir, `${sessionId}.jsonl`));