@inetafrica/open-claudia 1.20.0 → 1.20.1

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/bot.js CHANGED
@@ -651,6 +651,20 @@ function getLastProjectSession(userId, projectName) {
651
651
  return sessions.length > 0 ? sessions[0] : null;
652
652
  }
653
653
 
654
+ // Guard against cross-user session-id leakage from migration glitches:
655
+ // confirm sessionId is recorded under this userId in sessions.json
656
+ // before we hand it to `claude --resume`.
657
+ function userOwnsClaudeSession(userId, sessionId) {
658
+ if (!sessionId) return false;
659
+ const all = loadSessions();
660
+ const bucket = all[normalizeCanonicalUserId(userId)];
661
+ if (!bucket) return false;
662
+ for (const list of Object.values(bucket)) {
663
+ if (Array.isArray(list) && list.some((s) => s && s.id === sessionId)) return true;
664
+ }
665
+ return false;
666
+ }
667
+
654
668
  function isAuthorized(msg) {
655
669
  const chatId = String(msg.chat.id);
656
670
  if (CHAT_IDS.includes(chatId)) return true;
@@ -1348,12 +1362,12 @@ if (PROJECT_TRANSCRIPTS) {
1348
1362
 
1349
1363
  function transcriptProjectInfo(state = currentState()) {
1350
1364
  if (!state.currentSession) return null;
1351
- return projectTranscripts.pointer(state.currentSession.dir, state.currentSession.name);
1365
+ return projectTranscripts.pointer(state.currentSession.dir, state.currentSession.name, state.userId);
1352
1366
  }
1353
1367
 
1354
1368
  function transcriptPointerNote(state = currentState()) {
1355
1369
  if (!state.currentSession) return "";
1356
- return projectTranscripts.buildPointerNote(state.currentSession.dir, state.currentSession.name);
1370
+ return projectTranscripts.buildPointerNote(state.currentSession.dir, state.currentSession.name, state.userId);
1357
1371
  }
1358
1372
 
1359
1373
  function appendProjectTranscript(role, text, metadata = {}, state = currentState()) {
@@ -1378,7 +1392,7 @@ function appendProjectTranscript(role, text, metadata = {}, state = currentState
1378
1392
 
1379
1393
  function promptWithTranscriptPointer(prompt, state = currentState()) {
1380
1394
  if (!state.currentSession) return prompt;
1381
- return projectTranscripts.withPointer(prompt, state.currentSession.dir, state.currentSession.name);
1395
+ return projectTranscripts.withPointer(prompt, state.currentSession.dir, state.currentSession.name, state.userId);
1382
1396
  }
1383
1397
 
1384
1398
  function stripTranscriptPointerForStorage(prompt) {
@@ -1728,7 +1742,14 @@ function buildClaudeArgs(prompt, opts = {}) {
1728
1742
  if (transcriptInfo) args.push("--add-dir", transcriptInfo.transcriptsDir);
1729
1743
  if (opts.resumeSessionId) args.push("--resume", opts.resumeSessionId);
1730
1744
  else if (opts.continueSession) args.push("--continue");
1731
- else if (state.lastSessionId && !opts.fresh) args.push("--resume", state.lastSessionId);
1745
+ else if (state.lastSessionId && !opts.fresh) {
1746
+ if (userOwnsClaudeSession(state.userId, state.lastSessionId)) {
1747
+ args.push("--resume", state.lastSessionId);
1748
+ } else {
1749
+ console.warn(`[session-guard] dropping stale lastSessionId=${state.lastSessionId.slice(0, 8)} for ${state.userId}; not present in sessions.json — starting fresh`);
1750
+ state.lastSessionId = null;
1751
+ }
1752
+ }
1732
1753
  if (settings.model) args.push("--model", settings.model);
1733
1754
  if (settings.effort) args.push("--effort", settings.effort);
1734
1755
  if (settings.budget) args.push("--max-budget-usd", String(settings.budget));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "1.20.0",
3
+ "version": "1.20.1",
4
4
  "description": "Your always-on AI coding assistant — Claude Code, Cursor Agent, and OpenAI Codex via Telegram",
5
5
  "main": "bot.js",
6
6
  "bin": {
@@ -21,8 +21,10 @@ function safeName(value) {
21
21
  return String(value || "project").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "project";
22
22
  }
23
23
 
24
- function projectHash(projectPath) {
25
- return crypto.createHash("sha256").update(normalizeProjectPath(projectPath)).digest("hex").slice(0, 24);
24
+ function projectHash(projectPath, userId) {
25
+ const normalized = normalizeProjectPath(projectPath);
26
+ const seed = userId ? `${String(userId)} ${normalized}` : normalized;
27
+ return crypto.createHash("sha256").update(seed).digest("hex").slice(0, 24);
26
28
  }
27
29
 
28
30
  function mkdirp(dir) {
@@ -39,13 +41,14 @@ class ProjectTranscripts {
39
41
  this.redact = redact;
40
42
  }
41
43
 
42
- projectInfo(projectPath, projectName) {
44
+ projectInfo(projectPath, projectName, userId) {
43
45
  const normalizedPath = normalizeProjectPath(projectPath);
44
- const hash = projectHash(normalizedPath);
46
+ const hash = projectHash(normalizedPath, userId);
45
47
  const name = projectName || path.basename(normalizedPath) || "project";
46
48
  const baseName = `${hash}-${safeName(name)}`;
47
49
  return {
48
50
  hash,
51
+ userId: userId || null,
49
52
  projectName: name,
50
53
  projectPath: normalizedPath,
51
54
  transcriptPath: path.join(this.transcriptsDir, `${baseName}.jsonl`),
@@ -54,13 +57,13 @@ class ProjectTranscripts {
54
57
  };
55
58
  }
56
59
 
57
- pointer(projectPath, projectName) {
60
+ pointer(projectPath, projectName, userId) {
58
61
  if (!this.enabled || !projectPath) return null;
59
- return this.projectInfo(projectPath, projectName);
62
+ return this.projectInfo(projectPath, projectName, userId);
60
63
  }
61
64
 
62
- buildPointerNote(projectPath, projectName) {
63
- const info = this.pointer(projectPath, projectName);
65
+ buildPointerNote(projectPath, projectName, userId) {
66
+ const info = this.pointer(projectPath, projectName, userId);
64
67
  if (!info) return "";
65
68
  return [
66
69
  "## Project Transcript Memory",
@@ -71,8 +74,8 @@ class ProjectTranscripts {
71
74
  ].join("\n");
72
75
  }
73
76
 
74
- withPointer(prompt, projectPath, projectName) {
75
- const note = this.buildPointerNote(projectPath, projectName);
77
+ withPointer(prompt, projectPath, projectName, userId) {
78
+ const note = this.buildPointerNote(projectPath, projectName, userId);
76
79
  if (!note) return prompt;
77
80
  return `${note}\n\nCurrent user request:\n${prompt}`;
78
81
  }
@@ -96,13 +99,14 @@ class ProjectTranscripts {
96
99
 
97
100
  append(entry) {
98
101
  if (!this.enabled || !entry || !entry.projectPath) return null;
99
- const info = this.projectInfo(entry.projectPath, entry.projectName);
102
+ const canonicalUserId = entry.userId || entry.canonicalUserId || null;
103
+ const info = this.projectInfo(entry.projectPath, entry.projectName, canonicalUserId);
100
104
  mkdirp(this.transcriptsDir);
101
105
  const textData = this.truncateText(entry.text);
102
106
  const now = new Date().toISOString();
103
107
  const record = {
104
108
  timestamp: entry.timestamp || now,
105
- canonicalUserId: entry.userId || entry.canonicalUserId || null,
109
+ canonicalUserId,
106
110
  chat: entry.chat || null,
107
111
  project: { name: info.projectName, path: info.projectPath, hash: info.hash },
108
112
  backend: entry.backend || null,
@@ -118,6 +122,7 @@ class ProjectTranscripts {
118
122
  fs.appendFileSync(info.transcriptPath, JSON.stringify(record) + "\n", { mode: 0o600 });
119
123
  const metadata = {
120
124
  projectHash: info.hash,
125
+ canonicalUserId,
121
126
  projectName: info.projectName,
122
127
  projectPath: info.projectPath,
123
128
  transcriptPath: info.transcriptPath,