thepopebot 1.2.76-beta.10 → 1.2.76-beta.12

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.
Files changed (59) hide show
  1. package/api/index.js +52 -28
  2. package/bin/cli.js +22 -54
  3. package/drizzle/0022_organic_apocalypse.sql +16 -0
  4. package/drizzle/0023_needy_ender_wiggin.sql +1 -0
  5. package/drizzle/meta/0021_snapshot.json +639 -0
  6. package/drizzle/meta/0022_snapshot.json +743 -0
  7. package/drizzle/meta/0023_snapshot.json +750 -0
  8. package/drizzle/meta/_journal.json +14 -0
  9. package/lib/CLAUDE.md +2 -2
  10. package/lib/actions.js +9 -1
  11. package/lib/ai/index.js +19 -9
  12. package/lib/ai/scope.js +26 -0
  13. package/lib/ai/sdk-adapters/claude-code.js +20 -9
  14. package/lib/ai/system-prompt.js +23 -5
  15. package/lib/ai/tools.js +22 -5
  16. package/lib/ai/workspace-setup.js +1 -31
  17. package/lib/channels/CLAUDE.md +2 -2
  18. package/lib/channels/base.js +6 -2
  19. package/lib/channels/commands/index.js +42 -0
  20. package/lib/channels/commands/session.js +53 -0
  21. package/lib/channels/commands/verify.js +18 -0
  22. package/lib/channels/telegram.js +9 -29
  23. package/lib/chat/actions.js +48 -61
  24. package/lib/chat/api.js +39 -1
  25. package/lib/chat/components/chat.js +20 -3
  26. package/lib/chat/components/chat.jsx +23 -2
  27. package/lib/chat/components/index.js +1 -1
  28. package/lib/chat/components/profile-page.js +182 -4
  29. package/lib/chat/components/profile-page.jsx +196 -1
  30. package/lib/chat/components/scope-picker.js +21 -0
  31. package/lib/chat/components/scope-picker.jsx +27 -0
  32. package/lib/chat/components/settings-secrets-page.js +1 -95
  33. package/lib/chat/components/settings-secrets-page.jsx +0 -116
  34. package/lib/chat/telegram-profile.js +33 -0
  35. package/lib/code/actions.js +23 -4
  36. package/lib/config.js +0 -5
  37. package/lib/db/code-workspaces.js +2 -1
  38. package/lib/db/config.js +0 -1
  39. package/lib/db/schema.js +23 -1
  40. package/lib/db/user-channels.js +129 -0
  41. package/lib/tools/create-agent-job.js +3 -0
  42. package/lib/tools/docker.js +14 -3
  43. package/lib/tools/telegram.js +0 -9
  44. package/lib/utils/render-md.js +43 -17
  45. package/package.json +2 -1
  46. package/setup/lib/targets.mjs +0 -2
  47. package/setup/lib/telegram.mjs +0 -1
  48. package/setup/setup-telegram.mjs +5 -36
  49. package/templates/.env.example +0 -7
  50. package/templates/.gitignore.template +0 -2
  51. package/templates/CLAUDE.md.template +24 -1
  52. package/templates/agent-job/SYSTEM.md +12 -5
  53. package/templates/docker-compose.yml +1 -0
  54. package/templates/skills/CLAUDE.md.template +16 -23
  55. package/templates/skills/{library/agent-job-secrets → agent-job-secrets}/SKILL.md +2 -2
  56. package/setup/lib/telegram-verify.mjs +0 -63
  57. package/templates/skills/active/.gitkeep +0 -0
  58. /package/templates/skills/{library/agent-job-secrets → agent-job-secrets}/agent-job-secrets.js +0 -0
  59. /package/templates/skills/{library/playwright-cli → playwright-cli}/SKILL.md +0 -0
package/api/index.js CHANGED
@@ -1,8 +1,10 @@
1
- import { createHash, timingSafeEqual } from 'crypto';
1
+ import { createHash, timingSafeEqual, randomUUID } from 'crypto';
2
2
  import { createAgentJob } from '../lib/tools/create-agent-job.js';
3
3
  import { setWebhook } from '../lib/tools/telegram.js';
4
4
  import { getAgentJobStatus, fetchAgentJobLog } from '../lib/tools/github.js';
5
5
  import { getTelegramAdapter } from '../lib/channels/index.js';
6
+ import { dispatchCommand, dispatchPreAuthCommand } from '../lib/channels/commands/index.js';
7
+ import { getByChannelChatId, setActiveThread } from '../lib/db/user-channels.js';
6
8
  import { chat, chatStream, summarizeAgentJob } from '../lib/ai/index.js';
7
9
  import { createNotification } from '../lib/db/notifications.js';
8
10
  import { loadTriggers } from '../lib/triggers.js';
@@ -209,43 +211,65 @@ async function handleTelegramWebhook(request) {
209
211
  }
210
212
 
211
213
  /**
212
- * Process a normalized message through the AI layer with channel UX.
213
- * Message persistence is handled centrally by the AI layer.
214
- *
215
- * Uses chatStream() for progressive tool-call rendering when the adapter
216
- * supports it (Telegram: sends each tool call as a message, reacts on completion).
217
- * Falls back to chat() for adapters without streamChatResponse.
214
+ * Resolve the incoming channel message to a user, dispatch any slash command,
215
+ * and otherwise stream the message through the AI layer using the user's
216
+ * active session.
218
217
  */
219
218
  async function processChannelMessage(adapter, normalized) {
220
- await adapter.acknowledge(normalized.metadata);
221
- const stopIndicator = adapter.startProcessingIndicator(normalized.metadata);
219
+ const { channel, channelChatId, metadata } = normalized;
220
+ const binding = getByChannelChatId(channel, channelChatId);
221
+
222
+ // Unbound chat → only /verify is accepted; everything else is silently ignored.
223
+ if (!binding || !binding.verifiedAt) {
224
+ const result = await dispatchPreAuthCommand(normalized, { channel, channelChatId });
225
+ if (result?.handled) {
226
+ await adapter.sendResponse(channelChatId, result.reply, metadata);
227
+ }
228
+ return;
229
+ }
230
+
231
+ const ctx = { channel, channelChatId, userId: binding.userId };
232
+
233
+ // Post-auth slash commands short-circuit the AI path.
234
+ const cmd = await dispatchCommand(normalized, ctx);
235
+ if (cmd?.handled) {
236
+ await adapter.sendResponse(channelChatId, cmd.reply, metadata);
237
+ return;
238
+ }
239
+
240
+ await adapter.acknowledge(metadata);
241
+ const stopIndicator = adapter.startProcessingIndicator(metadata);
222
242
 
223
243
  try {
244
+ let threadId = binding.activeThreadId;
245
+ if (!threadId) {
246
+ threadId = randomUUID();
247
+ setActiveThread(binding.userId, channel, threadId);
248
+ }
249
+
250
+ const envRepo = process.env.GH_OWNER && process.env.GH_REPO
251
+ ? `${process.env.GH_OWNER}/${process.env.GH_REPO}`
252
+ : '';
253
+ const streamOptions = {
254
+ userId: binding.userId,
255
+ chatTitle: 'Telegram',
256
+ repo: envRepo,
257
+ branch: 'main',
258
+ codeMode: false,
259
+ codeModeType: 'code',
260
+ };
261
+
224
262
  if (adapter.streamChatResponse) {
225
- const chunks = chatStream(
226
- normalized.threadId,
227
- normalized.text,
228
- normalized.attachments,
229
- { userId: 'telegram', chatTitle: 'Telegram' }
230
- );
231
- await adapter.streamChatResponse(normalized.metadata.chatId, chunks);
263
+ const chunks = chatStream(threadId, normalized.text, normalized.attachments, streamOptions);
264
+ await adapter.streamChatResponse(channelChatId, chunks);
232
265
  } else {
233
- const response = await chat(
234
- normalized.threadId,
235
- normalized.text,
236
- normalized.attachments,
237
- { userId: 'telegram', chatTitle: 'Telegram' }
238
- );
239
- await adapter.sendResponse(normalized.threadId, response, normalized.metadata);
266
+ const response = await chat(threadId, normalized.text, normalized.attachments, streamOptions);
267
+ await adapter.sendResponse(channelChatId, response, metadata);
240
268
  }
241
269
  } catch (err) {
242
270
  console.error('Failed to process message with AI:', err);
243
271
  await adapter
244
- .sendResponse(
245
- normalized.threadId,
246
- 'Sorry, I encountered an error processing your message.',
247
- normalized.metadata
248
- )
272
+ .sendResponse(channelChatId, 'Sorry, I encountered an error processing your message.', metadata)
249
273
  .catch(() => {});
250
274
  } finally {
251
275
  stopIndicator();
package/bin/cli.js CHANGED
@@ -86,12 +86,12 @@ function getTemplateFiles(templatesDir) {
86
86
  return files;
87
87
  }
88
88
 
89
- async function init() {
89
+ async function init(options = {}) {
90
90
  let cwd = process.cwd();
91
91
  const packageDir = path.join(__dirname, '..');
92
92
  const templatesDir = path.join(packageDir, 'templates');
93
- const noManaged = args.includes('--no-managed');
94
- const noInstall = args.includes('--no-install');
93
+ const noManaged = options.noManaged ?? args.includes('--no-managed');
94
+ const noInstall = options.noInstall ?? args.includes('--no-install');
95
95
 
96
96
  // Guard: warn if the directory is not empty (unless it's an existing thepopebot project)
97
97
  const entries = fs.readdirSync(cwd);
@@ -277,58 +277,23 @@ async function init() {
277
277
  }
278
278
  }
279
279
 
280
- // Create default skill activation symlinks
281
- const defaultSkills = [];
282
- const activeDir = path.join(cwd, 'skills', 'active');
283
- fs.mkdirSync(activeDir, { recursive: true });
284
- for (const skill of defaultSkills) {
285
- const symlink = path.join(activeDir, skill);
286
- if (!fs.existsSync(symlink)) {
287
- createDirLink(`../library/${skill}`, symlink);
288
- console.log(` Created skills/active/${skill} ../library/${skill}`);
280
+ // Create agent skill bridge symlinks (point to ../skills)
281
+ const skillBridges = [
282
+ { dir: '.pi', name: 'Pi' },
283
+ { dir: '.claude', name: 'Claude' },
284
+ { dir: '.codex', name: 'Codex' },
285
+ { dir: '.gemini', name: 'Gemini' },
286
+ { dir: '.kimi', name: 'Kimi' },
287
+ ];
288
+ for (const { dir, name } of skillBridges) {
289
+ const link = path.join(cwd, dir, 'skills');
290
+ if (!fs.existsSync(link)) {
291
+ fs.mkdirSync(path.dirname(link), { recursive: true });
292
+ createDirLink('../skills', link);
293
+ console.log(` Created ${dir}/skills → ../skills`);
289
294
  }
290
295
  }
291
296
 
292
- // Create .pi/skills → ../skills/active symlink
293
- const piSkillsLink = path.join(cwd, '.pi', 'skills');
294
- if (!fs.existsSync(piSkillsLink)) {
295
- fs.mkdirSync(path.dirname(piSkillsLink), { recursive: true });
296
- createDirLink('../skills/active', piSkillsLink);
297
- console.log(' Created .pi/skills → ../skills/active');
298
- }
299
-
300
- // Create .claude/skills → ../skills/active symlink
301
- const claudeSkillsLink = path.join(cwd, '.claude', 'skills');
302
- if (!fs.existsSync(claudeSkillsLink)) {
303
- fs.mkdirSync(path.dirname(claudeSkillsLink), { recursive: true });
304
- createDirLink('../skills/active', claudeSkillsLink);
305
- console.log(' Created .claude/skills → ../skills/active');
306
- }
307
-
308
- // Create .codex/skills → ../skills/active symlink
309
- const codexSkillsLink = path.join(cwd, '.codex', 'skills');
310
- if (!fs.existsSync(codexSkillsLink)) {
311
- fs.mkdirSync(path.dirname(codexSkillsLink), { recursive: true });
312
- createDirLink('../skills/active', codexSkillsLink);
313
- console.log(' Created .codex/skills → ../skills/active');
314
- }
315
-
316
- // Create .gemini/skills → ../skills/active symlink
317
- const geminiSkillsLink = path.join(cwd, '.gemini', 'skills');
318
- if (!fs.existsSync(geminiSkillsLink)) {
319
- fs.mkdirSync(path.dirname(geminiSkillsLink), { recursive: true });
320
- createDirLink('../skills/active', geminiSkillsLink);
321
- console.log(' Created .gemini/skills → ../skills/active');
322
- }
323
-
324
- // Create .kimi/skills → ../skills/active symlink
325
- const kimiSkillsLink = path.join(cwd, '.kimi', 'skills');
326
- if (!fs.existsSync(kimiSkillsLink)) {
327
- fs.mkdirSync(path.dirname(kimiSkillsLink), { recursive: true });
328
- createDirLink('../skills/active', kimiSkillsLink);
329
- console.log(' Created .kimi/skills → ../skills/active');
330
- }
331
-
332
297
 
333
298
  // Report backed-up files
334
299
  if (backedUp.length > 0) {
@@ -681,6 +646,9 @@ const PROTECTED_PATHS = [
681
646
  'package.json',
682
647
  'docker-compose.custom.yml',
683
648
  '.claude/',
649
+ '.codex/',
650
+ '.gemini/',
651
+ '.kimi/',
684
652
  '.pi/',
685
653
  'skills/',
686
654
  'node_modules/',
@@ -774,10 +742,10 @@ async function resetAll() {
774
742
 
775
743
  console.log(`\n Moved ${filesToMove.length} file(s) to .backups/${ts}/`);
776
744
 
777
- // Run init to rebuild from templates
745
+ // Run init to rebuild from templates (call directly to use the same package version)
778
746
  console.log('\n Running init to rebuild project...\n');
779
747
  try {
780
- execSync('npx thepopebot init --no-install', { stdio: 'inherit', cwd });
748
+ await init({ noInstall: true });
781
749
  } catch {
782
750
  console.error('\n Init failed. Your backup is at .backups/' + ts + '/\n');
783
751
  process.exit(1);
@@ -0,0 +1,16 @@
1
+ CREATE TABLE `user_channels` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `user_id` text NOT NULL,
4
+ `channel` text NOT NULL,
5
+ `channel_chat_id` text,
6
+ `code` text,
7
+ `code_expires_at` integer,
8
+ `verified_at` integer,
9
+ `active_thread_id` text,
10
+ `created_at` integer NOT NULL,
11
+ `updated_at` integer NOT NULL
12
+ );
13
+ --> statement-breakpoint
14
+ CREATE UNIQUE INDEX `user_channels_user_channel_unique` ON `user_channels` (`user_id`,`channel`);--> statement-breakpoint
15
+ CREATE UNIQUE INDEX `user_channels_channel_chat_id_unique` ON `user_channels` (`channel`,`channel_chat_id`);--> statement-breakpoint
16
+ CREATE INDEX `user_channels_code_lookup` ON `user_channels` (`code`);
@@ -0,0 +1 @@
1
+ ALTER TABLE `code_workspaces` ADD `scope` text;