@inceptionstack/roundhouse 0.5.16 → 0.5.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/gateway/topic-command.ts +21 -10
package/package.json
CHANGED
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* /topic main — return to default session
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readFileSync, writeFileSync, mkdirSync, readdirSync } from "node:fs";
|
|
13
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync, renameSync } from "node:fs";
|
|
14
14
|
import { join } from "node:path";
|
|
15
|
+
import { randomBytes } from "node:crypto";
|
|
15
16
|
import { ROUNDHOUSE_DIR } from "../config";
|
|
16
17
|
|
|
17
18
|
const TOPICS_FILE = join(ROUNDHOUSE_DIR, "active-topics.json");
|
|
@@ -28,7 +29,9 @@ try {
|
|
|
28
29
|
function persistTopics(): void {
|
|
29
30
|
try {
|
|
30
31
|
mkdirSync(ROUNDHOUSE_DIR, { recursive: true });
|
|
31
|
-
|
|
32
|
+
const tmp = TOPICS_FILE + "." + randomBytes(4).toString("hex");
|
|
33
|
+
writeFileSync(tmp, JSON.stringify(Object.fromEntries(activeTopics)));
|
|
34
|
+
renameSync(tmp, TOPICS_FILE);
|
|
32
35
|
} catch (e) { console.error("[roundhouse] failed to persist topics:", e); }
|
|
33
36
|
}
|
|
34
37
|
|
|
@@ -38,12 +41,12 @@ export function getActiveTopic(chatId: string): string | undefined {
|
|
|
38
41
|
return (topic && topic !== "main") ? topic : undefined;
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
/** Apply topic override to a resolved agent thread ID. */
|
|
44
|
+
/** Apply topic override to a resolved agent thread ID. Scoped per chat. */
|
|
42
45
|
export function applyTopicOverride(agentThreadId: string, thread: { id?: string }): string {
|
|
43
46
|
if (agentThreadId !== "main") return agentThreadId;
|
|
44
47
|
const chatId = thread.id?.split(":")[1] ?? thread.id ?? "";
|
|
45
48
|
const topic = getActiveTopic(String(chatId));
|
|
46
|
-
return topic ? `topic:${topic}` : agentThreadId;
|
|
49
|
+
return topic ? `topic:${chatId}:${topic}` : agentThreadId;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
/** Set the active topic for a chat. */
|
|
@@ -56,14 +59,16 @@ export function setActiveTopic(chatId: string, topic: string): void {
|
|
|
56
59
|
persistTopics();
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
/** Get all known topics from memory-state directory. */
|
|
60
|
-
export function listTopics(): string[] {
|
|
62
|
+
/** Get all known topics for a specific chat from memory-state directory. */
|
|
63
|
+
export function listTopics(chatId: string): string[] {
|
|
61
64
|
const stateDir = join(ROUNDHOUSE_DIR, "memory-state");
|
|
65
|
+
// Files are named topic_c<chatId>_c<topicName>.json (threadIdToDir encoding)
|
|
66
|
+
const prefix = `topic_c${chatId}_c`;
|
|
62
67
|
try {
|
|
63
68
|
return readdirSync(stateDir)
|
|
64
|
-
.filter(f => f.startsWith(
|
|
65
|
-
.map(f => f.
|
|
66
|
-
.map(f => f.replace(/__/g, "_")); // reverse threadIdToDir encoding
|
|
69
|
+
.filter(f => f.startsWith(prefix) && f.endsWith(".json"))
|
|
70
|
+
.map(f => f.slice(prefix.length).replace(/\.json$/, ""))
|
|
71
|
+
.map(f => f.replace(/__/g, "_")); // reverse threadIdToDir underscore encoding
|
|
67
72
|
} catch {
|
|
68
73
|
return [];
|
|
69
74
|
}
|
|
@@ -81,6 +86,12 @@ export async function handleTopic(ctx: TopicCommandContext): Promise<void> {
|
|
|
81
86
|
// Extract chat ID from thread (for private: "telegram:<chatId>")
|
|
82
87
|
const chatId = thread.id?.split(":")[1] ?? thread.id;
|
|
83
88
|
|
|
89
|
+
// /topic only works in private chats (groups use forum topics instead)
|
|
90
|
+
if (chatId && chatId.startsWith("-")) {
|
|
91
|
+
await postWithFallback(thread, "⚠️ /topic only works in private chats. Use Telegram forum topics for groups.");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
84
95
|
// Parse the topic name from the command
|
|
85
96
|
const match = text.match(/^\/topic(?:@\S+)?\s+(.+)/i);
|
|
86
97
|
const topicName = match?.[1]?.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -88,7 +99,7 @@ export async function handleTopic(ctx: TopicCommandContext): Promise<void> {
|
|
|
88
99
|
if (!topicName) {
|
|
89
100
|
// Show current topic + known topics
|
|
90
101
|
const current = getActiveTopic(chatId) ?? "main (default)";
|
|
91
|
-
const known = listTopics();
|
|
102
|
+
const known = listTopics(chatId);
|
|
92
103
|
let msg = `📂 Current topic: \`${current}\`\n\n`;
|
|
93
104
|
if (known.length > 0) {
|
|
94
105
|
msg += `Known topics: ${known.map(t => `\`${t}\``).join(", ")}\n\n`;
|